aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2023-01-25 18:31:31 +0000
committerJosh Rahm <joshuarahm@gmail.com>2023-01-25 18:31:31 +0000
commit9243becbedbb6a1592208051f8fa2b090dcc5e7d (patch)
tree607c2a862ec3f4399b8766383f6f8e04c4aa43b4
parent9e40b6e9e1bc67f2d856adb837ee64dd0e25b717 (diff)
parent3c48d3c83fc21dbc0841f9210f04bdb073d73cd1 (diff)
downloadrneovim-9243becbedbb6a1592208051f8fa2b090dcc5e7d.tar.gz
rneovim-9243becbedbb6a1592208051f8fa2b090dcc5e7d.tar.bz2
rneovim-9243becbedbb6a1592208051f8fa2b090dcc5e7d.zip
Merge remote-tracking branch 'upstream/master' into usermarksusermarks
-rw-r--r--.builds/freebsd.yml45
-rw-r--r--.builds/openbsd.yml49
-rw-r--r--.cirrus.yml29
-rw-r--r--.clang-tidy42
-rw-r--r--.editorconfig3
-rw-r--r--.flake82
-rw-r--r--.git-blame-ignore-revs22
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.yml53
-rw-r--r--.github/ISSUE_TEMPLATE/feature_request.yml13
-rw-r--r--.github/ISSUE_TEMPLATE/lsp_bug_report.yml34
-rw-r--r--.github/SECURITY.md2
-rw-r--r--.github/actions/cache/action.yml22
-rw-r--r--.github/labeler.yml6
-rwxr-xr-x.github/scripts/install_deps_ubuntu.sh19
-rw-r--r--.github/scripts/remove-reviewers.js4
-rw-r--r--.github/scripts/reviews.js42
-rw-r--r--.github/scripts/unstale.js19
-rw-r--r--.github/workflows/add-reviewers.yml (renamed from .github/workflows/reviews.yml)3
-rw-r--r--.github/workflows/api-docs.yml10
-rw-r--r--.github/workflows/backport.yml2
-rw-r--r--.github/workflows/ci.yml221
-rw-r--r--.github/workflows/codeql.yml (renamed from .github/workflows/codeql-analysis.yml)6
-rw-r--r--.github/workflows/coverity.yml (renamed from .github/workflows/coverity-scan.yml)2
-rw-r--r--.github/workflows/env.ps17
-rwxr-xr-x.github/workflows/env.sh4
-rw-r--r--.github/workflows/labeler.yml1
-rw-r--r--.github/workflows/lintcommit.yml (renamed from .github/workflows/commitlint.yml)6
-rw-r--r--.github/workflows/news.yml31
-rw-r--r--.github/workflows/release.yml83
-rw-r--r--.github/workflows/remove-reviewers.yml (renamed from .github/workflows/remove-reviewers-on-draft.yml)1
-rw-r--r--.github/workflows/stale.yml42
-rw-r--r--.github/workflows/vim-patches.yml7
-rw-r--r--.gitignore8
-rw-r--r--.luacheckrc9
-rw-r--r--.mailmap5
-rw-r--r--CMakeLists.txt463
-rw-r--r--CMakePresets.json113
-rw-r--r--CONTRIBUTING.md164
-rw-r--r--LICENSE.txt7
-rw-r--r--MAINTAIN.md123
-rw-r--r--Makefile27
-rw-r--r--README.md29
-rwxr-xr-xci/before_cache.sh2
-rwxr-xr-xci/before_script.sh1
-rw-r--r--ci/build.ps1146
-rw-r--r--ci/common/build.sh7
-rw-r--r--ci/common/suite.sh4
-rw-r--r--ci/common/test.sh6
-rwxr-xr-xci/run_tests.sh10
-rw-r--r--ci/snap/.snapcraft_payload194
-rwxr-xr-xci/snap/after_success.sh14
-rwxr-xr-xci/snap/deploy.sh39
-rwxr-xr-xci/snap/install.sh11
-rwxr-xr-xci/snap/script.sh8
-rw-r--r--ci/snap/travis_snapcraft.cfgbin2448 -> 0 bytes
-rw-r--r--cmake.config/CMakeLists.txt50
-rw-r--r--cmake.config/config.h.in5
-rw-r--r--cmake.config/iwyu/gcc.libc.imp226
-rw-r--r--cmake.config/iwyu/gcc.symbols.imp305
-rw-r--r--cmake.config/iwyu/mapping.imp238
-rw-r--r--cmake.config/pathdef.c.in4
-rw-r--r--cmake.deps/CMakeLists.txt116
-rw-r--r--cmake.deps/cmake/BuildGettext.cmake33
-rw-r--r--cmake.deps/cmake/BuildLibiconv.cmake29
-rw-r--r--cmake.deps/cmake/BuildLibtermkey.cmake63
-rw-r--r--cmake.deps/cmake/BuildLibuv.cmake27
-rw-r--r--cmake.deps/cmake/BuildLibvterm.cmake70
-rw-r--r--cmake.deps/cmake/BuildLua.cmake55
-rw-r--r--cmake.deps/cmake/BuildLuajit.cmake14
-rw-r--r--cmake.deps/cmake/BuildLuarocks.cmake105
-rw-r--r--cmake.deps/cmake/BuildLuv.cmake128
-rw-r--r--cmake.deps/cmake/BuildMsgpack.cmake71
-rw-r--r--cmake.deps/cmake/BuildTreesitter.cmake67
-rw-r--r--cmake.deps/cmake/BuildTreesitterParsers.cmake49
-rw-r--r--cmake.deps/cmake/BuildUnibilium.cmake54
-rw-r--r--cmake.deps/cmake/DownloadAndExtractFile.cmake195
-rw-r--r--cmake.deps/cmake/GetBinaryDeps.cmake14
-rw-r--r--cmake.deps/cmake/GettextCMakeLists.txt2
-rw-r--r--cmake.deps/cmake/LibiconvCMakeLists.txt2
-rw-r--r--cmake.deps/cmake/LibvtermCMakeLists.txt10
-rw-r--r--cmake.deps/cmake/TargetArch.cmake23
-rw-r--r--cmake.deps/cmake/TreesitterCMakeLists.txt17
-rw-r--r--cmake.deps/cmake/TreesitterParserCMakeLists.txt11
-rw-r--r--cmake.deps/cmake/UnibiliumCMakeLists.txt25
-rw-r--r--cmake.deps/cmake/libtermkeyCMakeLists.txt13
-rw-r--r--cmake.deps/patches/libuv-disable-shared.patch117
-rw-r--r--cmake.deps/patches/libvterm-Remove-VLAs-for-MSVC.patch50
-rw-r--r--cmake.packaging/CMakeLists.txt2
-rw-r--r--cmake/Download.cmake18
-rw-r--r--cmake/FindIconv.cmake3
-rw-r--r--cmake/GenerateVersion.cmake42
-rw-r--r--cmake/GetCompileFlags.cmake114
-rw-r--r--cmake/RunMsgfmt.cmake9
-rw-r--r--cmake/RunMsgmerge.cmake11
-rw-r--r--cmake/RunTests.cmake10
-rw-r--r--cmake/RunXgettext.cmake14
-rw-r--r--cmake/Util.cmake58
-rw-r--r--contrib/YouCompleteMe/README.md31
-rw-r--r--contrib/YouCompleteMe/ycm_extra_conf.py65
-rw-r--r--contrib/doxygen/customdoxygen.css757
-rw-r--r--contrib/doxygen/extra.css777
-rw-r--r--contrib/doxygen/footer.html23
-rw-r--r--contrib/doxygen/header.html36
-rw-r--r--contrib/doxygen/logo-devdoc.pngbin27382 -> 0 bytes
-rw-r--r--contrib/flake.lock12
-rw-r--r--contrib/flake.nix176
-rw-r--r--contrib/luarc.json12
-rw-r--r--contrib/vim-addon-local-vimrc/README.md18
-rw-r--r--contrib/vim-addon-local-vimrc/vimrc11
-rw-r--r--runtime/CMakeLists.txt20
-rw-r--r--runtime/autoload/ccomplete.lua857
-rw-r--r--runtime/autoload/ccomplete.vim647
-rw-r--r--runtime/autoload/dist/ft.vim1073
-rw-r--r--runtime/autoload/health.vim26
-rw-r--r--runtime/autoload/health/nvim.vim284
-rw-r--r--runtime/autoload/man.vim529
-rw-r--r--runtime/autoload/netrw.vim57
-rw-r--r--runtime/autoload/provider/clipboard.vim25
-rw-r--r--runtime/autoload/provider/node.vim10
-rw-r--r--runtime/autoload/python.vim40
-rw-r--r--runtime/autoload/tohtml.vim5
-rw-r--r--runtime/autoload/tutor.vim2
-rw-r--r--runtime/autoload/zig/fmt.vim100
-rw-r--r--runtime/colors/blue.vim22
-rw-r--r--runtime/colors/darkblue.vim12
-rw-r--r--runtime/colors/delek.vim16
-rw-r--r--runtime/colors/desert.vim12
-rw-r--r--runtime/colors/elflord.vim10
-rw-r--r--runtime/colors/evening.vim45
-rw-r--r--runtime/colors/habamax.vim12
-rw-r--r--runtime/colors/industry.vim20
-rw-r--r--runtime/colors/koehler.vim10
-rw-r--r--runtime/colors/lunaperche.vim351
-rw-r--r--runtime/colors/morning.vim12
-rw-r--r--runtime/colors/murphy.vim12
-rw-r--r--runtime/colors/pablo.vim29
-rw-r--r--runtime/colors/peachpuff.vim12
-rw-r--r--runtime/colors/quiet.vim56
-rw-r--r--runtime/colors/ron.vim10
-rw-r--r--runtime/colors/shine.vim12
-rw-r--r--runtime/colors/slate.vim12
-rw-r--r--runtime/colors/torte.vim12
-rw-r--r--runtime/colors/zellner.vim12
-rw-r--r--runtime/compiler/dotnet.vim39
-rw-r--r--runtime/compiler/hare.vim31
-rw-r--r--runtime/compiler/raco.vim14
-rw-r--r--runtime/compiler/racomake.vim14
-rw-r--r--runtime/compiler/racosetup.vim14
-rw-r--r--runtime/compiler/racotest.vim14
-rw-r--r--runtime/compiler/zig.vim28
-rw-r--r--runtime/compiler/zig_build.vim29
-rw-r--r--runtime/compiler/zig_build_exe.vim27
-rw-r--r--runtime/compiler/zig_test.vim27
-rw-r--r--runtime/doc/Makefile40
-rw-r--r--runtime/doc/api.txt1022
-rw-r--r--runtime/doc/arabic.txt22
-rw-r--r--runtime/doc/autocmd.txt83
-rw-r--r--runtime/doc/builtin.txt570
-rw-r--r--runtime/doc/change.txt24
-rw-r--r--runtime/doc/channel.txt40
-rw-r--r--runtime/doc/cmdline.txt58
-rw-r--r--runtime/doc/deprecated.txt250
-rw-r--r--runtime/doc/dev_style.txt192
-rw-r--r--runtime/doc/develop.txt144
-rw-r--r--runtime/doc/diagnostic.txt195
-rw-r--r--runtime/doc/diff.txt16
-rw-r--r--runtime/doc/digraph.txt2
-rw-r--r--runtime/doc/editing.txt143
-rw-r--r--runtime/doc/editorconfig.txt91
-rw-r--r--runtime/doc/eval.txt537
-rw-r--r--runtime/doc/filetype.txt45
-rw-r--r--runtime/doc/fold.txt17
-rw-r--r--runtime/doc/ft_ada.txt44
-rw-r--r--runtime/doc/ft_rust.txt80
-rw-r--r--runtime/doc/ft_sql.txt79
-rw-r--r--runtime/doc/gui.txt20
-rw-r--r--runtime/doc/hebrew.txt16
-rw-r--r--runtime/doc/help.txt304
-rw-r--r--runtime/doc/helphelp.txt11
-rw-r--r--runtime/doc/if_cscop.txt374
-rw-r--r--runtime/doc/if_pyth.txt62
-rw-r--r--runtime/doc/if_ruby.txt2
-rw-r--r--runtime/doc/indent.txt97
-rw-r--r--runtime/doc/index.txt62
-rw-r--r--runtime/doc/insert.txt91
-rw-r--r--runtime/doc/intro.txt84
-rw-r--r--runtime/doc/job_control.txt16
-rw-r--r--runtime/doc/lsp-extension.txt4
-rw-r--r--runtime/doc/lsp.txt949
-rw-r--r--runtime/doc/lua-guide.txt757
-rw-r--r--runtime/doc/lua.txt1320
-rw-r--r--runtime/doc/luaref.txt864
-rw-r--r--runtime/doc/luvref.txt52
-rw-r--r--runtime/doc/makehtml.awk788
-rw-r--r--runtime/doc/maketags.awk42
-rw-r--r--runtime/doc/map.txt116
-rw-r--r--runtime/doc/mbyte.txt53
-rw-r--r--runtime/doc/message.txt7
-rw-r--r--runtime/doc/mlang.txt23
-rw-r--r--runtime/doc/motion.txt30
-rw-r--r--runtime/doc/news.txt194
-rw-r--r--runtime/doc/nvim.txt53
-rw-r--r--runtime/doc/nvim_terminal_emulator.txt102
-rw-r--r--runtime/doc/options.txt764
-rw-r--r--runtime/doc/pattern.txt2
-rw-r--r--runtime/doc/pi_health.txt144
-rw-r--r--runtime/doc/pi_msgpack.txt10
-rw-r--r--runtime/doc/pi_netrw.txt49
-rw-r--r--runtime/doc/pi_spec.txt8
-rw-r--r--runtime/doc/pi_tar.txt18
-rw-r--r--runtime/doc/pi_zip.txt16
-rw-r--r--runtime/doc/print.txt721
-rw-r--r--runtime/doc/provider.txt62
-rw-r--r--runtime/doc/quickfix.txt38
-rw-r--r--runtime/doc/quickref.txt81
-rw-r--r--runtime/doc/remote.txt4
-rw-r--r--runtime/doc/repeat.txt82
-rw-r--r--runtime/doc/rileft.txt17
-rw-r--r--runtime/doc/russian.txt2
-rw-r--r--runtime/doc/sign.txt20
-rw-r--r--runtime/doc/spell.txt14
-rw-r--r--runtime/doc/starting.txt142
-rw-r--r--runtime/doc/support.txt52
-rw-r--r--runtime/doc/syntax.txt180
-rw-r--r--runtime/doc/tabpage.txt2
-rw-r--r--runtime/doc/tagsrch.txt52
-rw-r--r--runtime/doc/term.txt22
-rw-r--r--runtime/doc/testing.txt54
-rw-r--r--runtime/doc/tips.txt6
-rw-r--r--runtime/doc/treesitter.txt1007
-rw-r--r--runtime/doc/uganda.txt96
-rw-r--r--runtime/doc/ui.txt231
-rw-r--r--runtime/doc/userfunc.txt440
-rw-r--r--runtime/doc/usr_01.txt2
-rw-r--r--runtime/doc/usr_02.txt24
-rw-r--r--runtime/doc/usr_03.txt2
-rw-r--r--runtime/doc/usr_05.txt10
-rw-r--r--runtime/doc/usr_06.txt57
-rw-r--r--runtime/doc/usr_08.txt44
-rw-r--r--runtime/doc/usr_10.txt6
-rw-r--r--runtime/doc/usr_20.txt4
-rw-r--r--runtime/doc/usr_21.txt11
-rw-r--r--runtime/doc/usr_22.txt2
-rw-r--r--runtime/doc/usr_25.txt35
-rw-r--r--runtime/doc/usr_29.txt24
-rw-r--r--runtime/doc/usr_30.txt4
-rw-r--r--runtime/doc/usr_32.txt6
-rw-r--r--runtime/doc/usr_40.txt8
-rw-r--r--runtime/doc/usr_41.txt35
-rw-r--r--runtime/doc/usr_42.txt14
-rw-r--r--runtime/doc/usr_45.txt17
-rw-r--r--runtime/doc/usr_toc.txt11
-rw-r--r--runtime/doc/various.txt8
-rw-r--r--runtime/doc/vi_diff.txt4
-rw-r--r--runtime/doc/vim_diff.txt161
-rw-r--r--runtime/doc/visual.txt6
-rw-r--r--runtime/doc/windows.txt79
-rw-r--r--runtime/filetype.lua13
-rw-r--r--runtime/filetype.vim2612
-rw-r--r--runtime/ftplugin/abaqus.vim43
-rw-r--r--runtime/ftplugin/apache.vim16
-rw-r--r--runtime/ftplugin/chatito.vim15
-rw-r--r--runtime/ftplugin/checkhealth.vim12
-rw-r--r--runtime/ftplugin/crontab.vim16
-rw-r--r--runtime/ftplugin/cs.vim5
-rw-r--r--runtime/ftplugin/elixir.vim6
-rw-r--r--runtime/ftplugin/erlang.vim24
-rw-r--r--runtime/ftplugin/gitattributes.vim13
-rw-r--r--runtime/ftplugin/gitignore.vim13
-rw-r--r--runtime/ftplugin/gyp.vim14
-rw-r--r--runtime/ftplugin/hare.vim27
-rw-r--r--runtime/ftplugin/heex.vim16
-rw-r--r--runtime/ftplugin/jsonnet.vim17
-rw-r--r--runtime/ftplugin/lua.lua3
-rw-r--r--runtime/ftplugin/lua.vim41
-rw-r--r--runtime/ftplugin/lynx.vim29
-rw-r--r--runtime/ftplugin/man.vim4
-rw-r--r--runtime/ftplugin/markdown.vim36
-rw-r--r--runtime/ftplugin/mermaid.vim49
-rw-r--r--runtime/ftplugin/obse.vim70
-rw-r--r--runtime/ftplugin/openvpn.vim14
-rw-r--r--runtime/ftplugin/poefilter.vim13
-rw-r--r--runtime/ftplugin/racket.vim82
-rw-r--r--runtime/ftplugin/readline.vim25
-rw-r--r--runtime/ftplugin/sh.vim53
-rw-r--r--runtime/ftplugin/ssa.vim13
-rw-r--r--runtime/ftplugin/vdf.vim14
-rw-r--r--runtime/ftplugin/vim.vim4
-rw-r--r--runtime/ftplugin/zig.vim66
-rw-r--r--runtime/ftplugin/zimbu.vim4
-rw-r--r--runtime/ftplugin/zsh.vim2
-rw-r--r--runtime/indent/chatito.vim32
-rw-r--r--runtime/indent/erlang.vim143
-rw-r--r--runtime/indent/gyp.vim7
-rw-r--r--runtime/indent/hare.vim138
-rw-r--r--runtime/indent/json.vim3
-rw-r--r--runtime/indent/lua.vim3
-rw-r--r--runtime/indent/nginx.vim77
-rw-r--r--runtime/indent/obse.vim55
-rw-r--r--runtime/indent/racket.vim60
-rw-r--r--runtime/indent/solidity.vim442
-rw-r--r--runtime/indent/testdir/python.in18
-rw-r--r--runtime/indent/testdir/python.ok18
-rw-r--r--runtime/indent/testdir/runtest.vim15
-rw-r--r--runtime/indent/testdir/vb.in134
-rw-r--r--runtime/indent/testdir/vb.ok134
-rw-r--r--runtime/indent/testdir/vim.in7
-rw-r--r--runtime/indent/testdir/vim.ok7
-rw-r--r--runtime/indent/vb.vim105
-rw-r--r--runtime/indent/vim.vim44
-rw-r--r--runtime/indent/vue.vim14
-rw-r--r--runtime/indent/yaml.vim10
-rw-r--r--runtime/indent/zig.vim80
-rw-r--r--runtime/lua/_vim9script.lua650
-rw-r--r--runtime/lua/editorconfig.lua246
-rw-r--r--runtime/lua/man.lua626
-rw-r--r--runtime/lua/nvim/health.lua406
-rw-r--r--runtime/lua/vim/F.lua8
-rw-r--r--runtime/lua/vim/_editor.lua69
-rw-r--r--runtime/lua/vim/_init_packages.lua6
-rw-r--r--runtime/lua/vim/_inspector.lua238
-rw-r--r--runtime/lua/vim/_meta.lua871
-rw-r--r--runtime/lua/vim/diagnostic.lua358
-rw-r--r--runtime/lua/vim/filetype.lua162
-rw-r--r--runtime/lua/vim/filetype/detect.lua79
-rw-r--r--runtime/lua/vim/fs.lua175
-rw-r--r--runtime/lua/vim/health.lua15
-rw-r--r--runtime/lua/vim/highlight.lua3
-rw-r--r--runtime/lua/vim/inspect.lua32
-rw-r--r--runtime/lua/vim/keymap.lua39
-rw-r--r--runtime/lua/vim/lsp.lua481
-rw-r--r--runtime/lua/vim/lsp/buf.lua316
-rw-r--r--runtime/lua/vim/lsp/codelens.lua27
-rw-r--r--runtime/lua/vim/lsp/diagnostic.lua2
-rw-r--r--runtime/lua/vim/lsp/handlers.lua145
-rw-r--r--runtime/lua/vim/lsp/health.lua16
-rw-r--r--runtime/lua/vim/lsp/log.lua17
-rw-r--r--runtime/lua/vim/lsp/protocol.lua97
-rw-r--r--runtime/lua/vim/lsp/rpc.lua741
-rw-r--r--runtime/lua/vim/lsp/semantic_tokens.lua702
-rw-r--r--runtime/lua/vim/lsp/sync.lua2
-rw-r--r--runtime/lua/vim/lsp/util.lua207
-rw-r--r--runtime/lua/vim/secure.lua188
-rw-r--r--runtime/lua/vim/shared.lua167
-rw-r--r--runtime/lua/vim/treesitter.lua461
-rw-r--r--runtime/lua/vim/treesitter/health.lua21
-rw-r--r--runtime/lua/vim/treesitter/highlighter.lua177
-rw-r--r--runtime/lua/vim/treesitter/language.lua22
-rw-r--r--runtime/lua/vim/treesitter/languagetree.lua145
-rw-r--r--runtime/lua/vim/treesitter/playground.lua186
-rw-r--r--runtime/lua/vim/treesitter/query.lua180
-rw-r--r--runtime/lua/vim/ui.lua21
-rw-r--r--runtime/menu.vim35
-rw-r--r--runtime/neovim.ico (renamed from cmake.packaging/neovim.ico)bin122355 -> 122355 bytes
-rw-r--r--runtime/nvim.appdata.xml3
-rw-r--r--runtime/optwin.vim1065
-rw-r--r--runtime/pack/dist/opt/cfilter/plugin/cfilter.lua114
-rw-r--r--runtime/pack/dist/opt/cfilter/plugin/cfilter.vim62
-rw-r--r--runtime/pack/dist/opt/termdebug/plugin/termdebug.vim39
-rw-r--r--runtime/pack/dist/opt/vimball/doc/vimball.txt14
-rw-r--r--runtime/plugin/editorconfig.lua13
-rw-r--r--runtime/plugin/man.lua34
-rw-r--r--runtime/plugin/man.vim15
-rw-r--r--runtime/plugin/matchparen.vim9
-rw-r--r--runtime/plugin/nvim.lua7
-rw-r--r--runtime/print/ascii.ps22
-rw-r--r--runtime/print/cidfont.ps26
-rw-r--r--runtime/print/cns_roman.ps23
-rw-r--r--runtime/print/cp1250.ps40
-rw-r--r--runtime/print/cp1251.ps40
-rw-r--r--runtime/print/cp1252.ps40
-rw-r--r--runtime/print/cp1253.ps40
-rw-r--r--runtime/print/cp1254.ps40
-rw-r--r--runtime/print/cp1255.ps40
-rw-r--r--runtime/print/cp1257.ps40
-rw-r--r--runtime/print/gb_roman.ps23
-rw-r--r--runtime/print/hp-roman8.ps40
-rw-r--r--runtime/print/iso-8859-10.ps40
-rw-r--r--runtime/print/iso-8859-11.ps40
-rw-r--r--runtime/print/iso-8859-13.ps40
-rw-r--r--runtime/print/iso-8859-14.ps40
-rw-r--r--runtime/print/iso-8859-15.ps40
-rw-r--r--runtime/print/iso-8859-2.ps40
-rw-r--r--runtime/print/iso-8859-3.ps40
-rw-r--r--runtime/print/iso-8859-4.ps40
-rw-r--r--runtime/print/iso-8859-5.ps40
-rw-r--r--runtime/print/iso-8859-7.ps40
-rw-r--r--runtime/print/iso-8859-8.ps40
-rw-r--r--runtime/print/iso-8859-9.ps40
-rw-r--r--runtime/print/jis_roman.ps23
-rw-r--r--runtime/print/koi8-r.ps40
-rw-r--r--runtime/print/koi8-u.ps40
-rw-r--r--runtime/print/ks_roman.ps23
-rw-r--r--runtime/print/latin1.ps40
-rw-r--r--runtime/print/mac-roman.ps40
-rw-r--r--runtime/print/prolog.ps44
-rw-r--r--runtime/queries/c/highlights.scm2
-rw-r--r--runtime/queries/c/injections.scm3
-rw-r--r--runtime/queries/help/highlights.scm25
-rw-r--r--runtime/queries/help/injections.scm3
-rw-r--r--runtime/queries/lua/highlights.scm199
-rw-r--r--runtime/queries/lua/injections.scm22
-rw-r--r--runtime/queries/vim/highlights.scm290
-rw-r--r--runtime/queries/vim/injections.scm28
-rw-r--r--runtime/scripts.vim459
-rw-r--r--runtime/synmenu.vim2
-rw-r--r--runtime/syntax/2html.vim393
-rw-r--r--runtime/syntax/c.vim2
-rw-r--r--runtime/syntax/cabal.vim21
-rw-r--r--runtime/syntax/chatito.vim62
-rw-r--r--runtime/syntax/checkhealth.vim21
-rw-r--r--runtime/syntax/cs.vim19
-rw-r--r--runtime/syntax/debchangelog.vim6
-rw-r--r--runtime/syntax/debsources.vim6
-rw-r--r--runtime/syntax/desktop.vim6
-rw-r--r--runtime/syntax/editorconfig.vim17
-rw-r--r--runtime/syntax/erlang.vim17
-rw-r--r--runtime/syntax/fstab.vim285
-rw-r--r--runtime/syntax/gdresource.vim65
-rw-r--r--runtime/syntax/gdscript.vim103
-rw-r--r--runtime/syntax/gdshader.vim57
-rw-r--r--runtime/syntax/gitattributes.vim63
-rw-r--r--runtime/syntax/gitignore.vim29
-rw-r--r--runtime/syntax/go.vim115
-rw-r--r--runtime/syntax/gyp.vim49
-rw-r--r--runtime/syntax/hare.vim133
-rw-r--r--runtime/syntax/help.vim10
-rw-r--r--runtime/syntax/hgcommit.vim21
-rw-r--r--runtime/syntax/hlsplaylist.vim120
-rw-r--r--runtime/syntax/hollywood.vim20
-rw-r--r--runtime/syntax/html.vim12
-rw-r--r--runtime/syntax/lua.vim317
-rw-r--r--runtime/syntax/lyrics.vim43
-rw-r--r--runtime/syntax/make.vim6
-rw-r--r--runtime/syntax/markdown.vim89
-rw-r--r--runtime/syntax/mermaid.vim155
-rw-r--r--runtime/syntax/modula3.vim101
-rw-r--r--runtime/syntax/nix.vim210
-rw-r--r--runtime/syntax/nsis.vim32
-rw-r--r--runtime/syntax/obse.vim3360
-rw-r--r--runtime/syntax/openvpn.vim72
-rw-r--r--runtime/syntax/plsql.vim153
-rw-r--r--runtime/syntax/poefilter.vim167
-rw-r--r--runtime/syntax/ptcap.vim2
-rw-r--r--runtime/syntax/racket.vim656
-rw-r--r--runtime/syntax/rego.vim63
-rw-r--r--runtime/syntax/sed.vim105
-rw-r--r--runtime/syntax/sh.vim53
-rw-r--r--runtime/syntax/shared/hgcommitDiff.vim390
-rw-r--r--runtime/syntax/solidity.vim173
-rw-r--r--runtime/syntax/srt.vim62
-rw-r--r--runtime/syntax/ssa.vim63
-rw-r--r--runtime/syntax/sshconfig.vim27
-rw-r--r--runtime/syntax/sshdconfig.vim18
-rw-r--r--runtime/syntax/swayconfig.vim28
-rw-r--r--runtime/syntax/synload.vim2
-rw-r--r--runtime/syntax/syntax.vim7
-rw-r--r--runtime/syntax/vdf.vim54
-rw-r--r--runtime/syntax/vim.vim6
-rw-r--r--runtime/syntax/wdl.vim41
-rw-r--r--runtime/syntax/zig.vim292
-rw-r--r--runtime/syntax/zir.vim49
-rw-r--r--runtime/syntax/zsh.vim163
-rw-r--r--runtime/tutor/tutor.tutor.json4
-rwxr-xr-xscripts/bump-deps.sh104
-rwxr-xr-x[-rw-r--r--]scripts/bump_deps.lua601
-rwxr-xr-xscripts/check-includes.py64
-rw-r--r--scripts/cliff.toml73
-rwxr-xr-xscripts/download-unicode-files.sh19
-rw-r--r--scripts/gen_help_html.lua1110
-rw-r--r--scripts/gen_help_html.py389
-rwxr-xr-xscripts/gen_vimdoc.py94
-rwxr-xr-xscripts/genappimage.sh12
-rw-r--r--scripts/genvimvim.lua6
-rwxr-xr-xscripts/git-log-pretty-since.sh14
-rw-r--r--scripts/lintcommit.lua21
-rw-r--r--scripts/lua2dox.lua372
-rwxr-xr-xscripts/lua2dox_filter88
-rwxr-xr-xscripts/pvscheck.sh2
-rwxr-xr-xscripts/release.sh4
-rwxr-xr-xscripts/update_terminfo.sh2
-rwxr-xr-xscripts/vim-patch.sh68
-rwxr-xr-xscripts/vimpatch.lua7
-rw-r--r--src/Doxyfile2566
-rwxr-xr-xsrc/clint.py288
-rw-r--r--src/klib/kbtree.h (renamed from src/nvim/lib/kbtree.h)0
-rw-r--r--src/klib/khash.h (renamed from src/nvim/lib/khash.h)0
-rw-r--r--src/klib/klist.h (renamed from src/nvim/lib/klist.h)0
-rw-r--r--src/klib/kvec.h (renamed from src/nvim/lib/kvec.h)2
-rw-r--r--src/man/nvim.128
-rw-r--r--src/mpack/mpack_core.c3
-rwxr-xr-xsrc/nvim/CMakeLists.txt643
-rw-r--r--src/nvim/README.md10
-rw-r--r--src/nvim/api/autocmd.c126
-rw-r--r--src/nvim/api/buffer.c171
-rw-r--r--src/nvim/api/buffer.h3
-rw-r--r--src/nvim/api/command.c186
-rw-r--r--src/nvim/api/deprecated.c12
-rw-r--r--src/nvim/api/extmark.c139
-rw-r--r--src/nvim/api/extmark.h3
-rw-r--r--src/nvim/api/keysets.lua105
-rw-r--r--src/nvim/api/options.c30
-rw-r--r--src/nvim/api/private/converter.c18
-rw-r--r--src/nvim/api/private/defs.h6
-rw-r--r--src/nvim/api/private/dispatch.c29
-rw-r--r--src/nvim/api/private/dispatch.h17
-rw-r--r--src/nvim/api/private/helpers.c150
-rw-r--r--src/nvim/api/private/helpers.h9
-rw-r--r--src/nvim/api/tabpage.c16
-rw-r--r--src/nvim/api/ui.c229
-rw-r--r--src/nvim/api/ui.h2
-rw-r--r--src/nvim/api/ui_events.in.h67
-rw-r--r--src/nvim/api/vim.c259
-rw-r--r--src/nvim/api/vimscript.c33
-rw-r--r--src/nvim/api/win_config.c158
-rw-r--r--src/nvim/api/window.c42
-rw-r--r--src/nvim/arabic.c161
-rw-r--r--src/nvim/arglist.c637
-rw-r--r--src/nvim/ascii.h14
-rw-r--r--src/nvim/assert.h2
-rw-r--r--src/nvim/auevents.lua24
-rw-r--r--src/nvim/autocmd.c349
-rw-r--r--src/nvim/autocmd.h17
-rw-r--r--src/nvim/buffer.c560
-rw-r--r--src/nvim/buffer.h16
-rw-r--r--src/nvim/buffer_defs.h733
-rw-r--r--src/nvim/buffer_updates.c65
-rw-r--r--src/nvim/buffer_updates.h4
-rw-r--r--src/nvim/change.c343
-rw-r--r--src/nvim/change.h4
-rw-r--r--src/nvim/channel.c38
-rw-r--r--src/nvim/channel.h15
-rw-r--r--src/nvim/charset.c200
-rw-r--r--src/nvim/charset.h3
-rw-r--r--src/nvim/cmdexpand.c2977
-rw-r--r--src/nvim/cmdexpand.h3
-rw-r--r--src/nvim/cmdhist.c331
-rw-r--r--src/nvim/cmdhist.h3
-rw-r--r--src/nvim/context.c28
-rw-r--r--src/nvim/context.h4
-rw-r--r--src/nvim/cursor.c71
-rw-r--r--src/nvim/cursor_shape.c17
-rw-r--r--src/nvim/debugger.c283
-rw-r--r--src/nvim/decoration.c56
-rw-r--r--src/nvim/decoration.h18
-rw-r--r--src/nvim/decoration_provider.c65
-rw-r--r--src/nvim/decoration_provider.h6
-rw-r--r--src/nvim/diff.c1449
-rw-r--r--src/nvim/diff.h3
-rw-r--r--src/nvim/digraph.c214
-rw-r--r--src/nvim/drawline.c480
-rw-r--r--src/nvim/drawline.h6
-rw-r--r--src/nvim/drawscreen.c689
-rw-r--r--src/nvim/drawscreen.h17
-rw-r--r--src/nvim/edit.c1664
-rw-r--r--src/nvim/edit.h2
-rw-r--r--src/nvim/eval.c1260
-rw-r--r--src/nvim/eval.h67
-rw-r--r--src/nvim/eval.lua51
-rw-r--r--src/nvim/eval/buffer.c734
-rw-r--r--src/nvim/eval/buffer.h10
-rw-r--r--src/nvim/eval/decode.c76
-rw-r--r--src/nvim/eval/encode.c59
-rw-r--r--src/nvim/eval/encode.h16
-rw-r--r--src/nvim/eval/executor.c21
-rw-r--r--src/nvim/eval/funcs.c2710
-rw-r--r--src/nvim/eval/funcs.h22
-rw-r--r--src/nvim/eval/gc.c5
-rw-r--r--src/nvim/eval/gc.h1
-rw-r--r--src/nvim/eval/typval.c289
-rw-r--r--src/nvim/eval/typval.h469
-rw-r--r--src/nvim/eval/typval_defs.h399
-rw-r--r--src/nvim/eval/typval_encode.c.h32
-rw-r--r--src/nvim/eval/typval_encode.h4
-rw-r--r--src/nvim/eval/userfunc.c1043
-rw-r--r--src/nvim/eval/userfunc.h85
-rw-r--r--src/nvim/eval/vars.c346
-rw-r--r--src/nvim/eval/vars.h2
-rw-r--r--src/nvim/eval/window.c954
-rw-r--r--src/nvim/eval/window.h78
-rw-r--r--src/nvim/event/libuv_process.c25
-rw-r--r--src/nvim/event/libuv_process.h3
-rw-r--r--src/nvim/event/loop.c49
-rw-r--r--src/nvim/event/loop.h6
-rw-r--r--src/nvim/event/multiqueue.c6
-rw-r--r--src/nvim/event/process.c44
-rw-r--r--src/nvim/event/process.h14
-rw-r--r--src/nvim/event/rstream.c9
-rw-r--r--src/nvim/event/signal.c1
-rw-r--r--src/nvim/event/signal.h3
-rw-r--r--src/nvim/event/socket.c13
-rw-r--r--src/nvim/event/socket.h3
-rw-r--r--src/nvim/event/stream.c12
-rw-r--r--src/nvim/event/stream.h8
-rw-r--r--src/nvim/event/time.h4
-rw-r--r--src/nvim/event/wstream.c6
-rw-r--r--src/nvim/event/wstream.h3
-rw-r--r--src/nvim/ex_cmds.c1327
-rw-r--r--src/nvim/ex_cmds.h1
-rw-r--r--src/nvim/ex_cmds.lua44
-rw-r--r--src/nvim/ex_cmds2.c532
-rw-r--r--src/nvim/ex_cmds2.h2
-rw-r--r--src/nvim/ex_cmds_defs.h15
-rw-r--r--src/nvim/ex_docmd.c1193
-rw-r--r--src/nvim/ex_docmd.h3
-rw-r--r--src/nvim/ex_eval.c1084
-rw-r--r--src/nvim/ex_eval.h2
-rw-r--r--src/nvim/ex_eval_defs.h2
-rw-r--r--src/nvim/ex_getln.c1853
-rw-r--r--src/nvim/ex_getln.h12
-rw-r--r--src/nvim/ex_session.c126
-rw-r--r--src/nvim/extmark.c17
-rw-r--r--src/nvim/extmark.h7
-rw-r--r--src/nvim/extmark_defs.h4
-rw-r--r--src/nvim/file_search.c737
-rw-r--r--src/nvim/file_search.h6
-rw-r--r--src/nvim/fileio.c1630
-rw-r--r--src/nvim/fileio.h2
-rw-r--r--src/nvim/fold.c315
-rw-r--r--src/nvim/fold.h9
-rw-r--r--src/nvim/garray.c25
-rw-r--r--src/nvim/garray.h9
-rw-r--r--src/nvim/generators/c_grammar.lua4
-rw-r--r--src/nvim/generators/gen_api_dispatch.lua143
-rwxr-xr-xsrc/nvim/generators/gen_api_ui_events.lua90
-rwxr-xr-xsrc/nvim/generators/gen_declarations.lua5
-rw-r--r--src/nvim/generators/gen_eval.lua51
-rw-r--r--src/nvim/generators/gen_events.lua4
-rw-r--r--src/nvim/generators/gen_ex_cmds.lua37
-rw-r--r--src/nvim/generators/gen_keysets.lua5
-rw-r--r--src/nvim/generators/gen_options.lua13
-rw-r--r--src/nvim/generators/gen_unicode_tables.lua3
-rw-r--r--src/nvim/getchar.c655
-rw-r--r--src/nvim/globals.h149
-rw-r--r--src/nvim/grid.c70
-rw-r--r--src/nvim/grid.h19
-rw-r--r--src/nvim/grid_defs.h20
-rw-r--r--src/nvim/hardcopy.c3192
-rw-r--r--src/nvim/hardcopy.h86
-rw-r--r--src/nvim/hashtab.c41
-rw-r--r--src/nvim/hashtab.h23
-rw-r--r--src/nvim/help.c225
-rw-r--r--src/nvim/highlight.c174
-rw-r--r--src/nvim/highlight.h3
-rw-r--r--src/nvim/highlight_defs.h22
-rw-r--r--src/nvim/highlight_group.c485
-rw-r--r--src/nvim/iconv.h20
-rw-r--r--src/nvim/if_cscope.c2052
-rw-r--r--src/nvim/if_cscope.h10
-rw-r--r--src/nvim/if_cscope_defs.h64
-rw-r--r--src/nvim/indent.c521
-rw-r--r--src/nvim/indent.h2
-rw-r--r--src/nvim/indent_c.c1667
-rw-r--r--src/nvim/input.c22
-rw-r--r--src/nvim/insexpand.c3289
-rw-r--r--src/nvim/insexpand.h11
-rw-r--r--src/nvim/keycodes.c275
-rw-r--r--src/nvim/keycodes.h4
-rw-r--r--src/nvim/lib/ringbuf.h11
-rw-r--r--src/nvim/linematch.c384
-rw-r--r--src/nvim/linematch.h10
-rw-r--r--src/nvim/locale.c377
-rw-r--r--src/nvim/locale.h10
-rw-r--r--src/nvim/log.c16
-rw-r--r--src/nvim/log.h1
-rw-r--r--src/nvim/lua/converter.c31
-rw-r--r--src/nvim/lua/converter.h8
-rw-r--r--src/nvim/lua/executor.c409
-rw-r--r--src/nvim/lua/executor.h4
-rw-r--r--src/nvim/lua/spell.c18
-rw-r--r--src/nvim/lua/stdlib.c133
-rw-r--r--src/nvim/lua/treesitter.c122
-rw-r--r--src/nvim/lua/xdiff.c147
-rw-r--r--src/nvim/macros.h13
-rw-r--r--src/nvim/main.c855
-rw-r--r--src/nvim/main.h14
-rw-r--r--src/nvim/map.c8
-rw-r--r--src/nvim/map.h6
-rw-r--r--src/nvim/map_defs.h2
-rw-r--r--src/nvim/mapping.c657
-rw-r--r--src/nvim/mapping.h14
-rw-r--r--src/nvim/mark.c397
-rw-r--r--src/nvim/mark.h14
-rw-r--r--src/nvim/mark_defs.h8
-rw-r--r--src/nvim/marktree.c7
-rw-r--r--src/nvim/marktree.h5
-rw-r--r--src/nvim/match.c407
-rw-r--r--src/nvim/math.c2
-rw-r--r--src/nvim/mbyte.c770
-rw-r--r--src/nvim/mbyte.h16
-rw-r--r--src/nvim/mbyte_defs.h2
-rw-r--r--src/nvim/memfile.c42
-rw-r--r--src/nvim/memfile_defs.h4
-rw-r--r--src/nvim/memline.c1637
-rw-r--r--src/nvim/memline.h4
-rw-r--r--src/nvim/memline_defs.h4
-rw-r--r--src/nvim/memory.c174
-rw-r--r--src/nvim/memory.h16
-rw-r--r--src/nvim/menu.c259
-rw-r--r--src/nvim/menu.h6
-rw-r--r--src/nvim/menu_defs.h2
-rw-r--r--src/nvim/message.c796
-rw-r--r--src/nvim/message.h14
-rw-r--r--src/nvim/mouse.c987
-rw-r--r--src/nvim/mouse.h82
-rw-r--r--src/nvim/move.c709
-rw-r--r--src/nvim/msgpack_rpc/channel.c107
-rw-r--r--src/nvim/msgpack_rpc/channel.h2
-rw-r--r--src/nvim/msgpack_rpc/channel_defs.h2
-rw-r--r--src/nvim/msgpack_rpc/helpers.c42
-rw-r--r--src/nvim/msgpack_rpc/server.c21
-rw-r--r--src/nvim/msgpack_rpc/unpacker.c50
-rw-r--r--src/nvim/msgpack_rpc/unpacker.h6
-rw-r--r--src/nvim/normal.c1965
-rw-r--r--src/nvim/normal.h21
-rw-r--r--src/nvim/ops.c1743
-rw-r--r--src/nvim/ops.h23
-rw-r--r--src/nvim/option.c3009
-rw-r--r--src/nvim/option.h4
-rw-r--r--src/nvim/option_defs.h452
-rw-r--r--src/nvim/options.lua207
-rw-r--r--src/nvim/optionstr.c2209
-rw-r--r--src/nvim/optionstr.h2
-rw-r--r--src/nvim/os/dl.c3
-rw-r--r--src/nvim/os/env.c162
-rw-r--r--src/nvim/os/fileio.c45
-rw-r--r--src/nvim/os/fileio.h5
-rw-r--r--src/nvim/os/fs.c149
-rw-r--r--src/nvim/os/fs.h4
-rw-r--r--src/nvim/os/input.c113
-rw-r--r--src/nvim/os/input.h2
-rw-r--r--src/nvim/os/lang.c10
-rw-r--r--src/nvim/os/mem.c1
-rw-r--r--src/nvim/os/nvim.manifest20
-rw-r--r--src/nvim/os/os_defs.h4
-rw-r--r--src/nvim/os/os_win_console.c64
-rw-r--r--src/nvim/os/process.c45
-rw-r--r--src/nvim/os/pty_conpty_win.c4
-rw-r--r--src/nvim/os/pty_conpty_win.h2
-rw-r--r--src/nvim/os/pty_process.h2
-rw-r--r--src/nvim/os/pty_process_unix.c39
-rw-r--r--src/nvim/os/pty_process_unix.h2
-rw-r--r--src/nvim/os/shell.c159
-rw-r--r--src/nvim/os/signal.c13
-rw-r--r--src/nvim/os/stdpaths.c14
-rw-r--r--src/nvim/os/time.c48
-rw-r--r--src/nvim/os/tty.c6
-rw-r--r--src/nvim/os/unix_defs.h3
-rw-r--r--src/nvim/os/users.c22
-rw-r--r--src/nvim/os/win_defs.h2
-rw-r--r--src/nvim/os_unix.c73
-rw-r--r--src/nvim/os_unix.h10
-rw-r--r--src/nvim/path.c903
-rw-r--r--src/nvim/path.h6
-rw-r--r--src/nvim/plines.c217
-rw-r--r--src/nvim/plines.h16
-rw-r--r--src/nvim/po/CMakeLists.txt39
-rw-r--r--src/nvim/po/af.po99
-rw-r--r--src/nvim/po/ca.po128
-rw-r--r--src/nvim/po/cleanup.vim2
-rw-r--r--src/nvim/po/cs.cp1250.po131
-rw-r--r--src/nvim/po/cs.po131
-rw-r--r--src/nvim/po/da.po97
-rw-r--r--src/nvim/po/de.po124
-rw-r--r--src/nvim/po/en_GB.po126
-rw-r--r--src/nvim/po/eo.po98
-rw-r--r--src/nvim/po/es.po129
-rw-r--r--src/nvim/po/fi.po97
-rw-r--r--src/nvim/po/fixfilenames.vim13
-rw-r--r--src/nvim/po/fr.po101
-rw-r--r--src/nvim/po/ga.po97
-rw-r--r--src/nvim/po/it.po126
-rw-r--r--src/nvim/po/ja.euc-jp.po95
-rw-r--r--src/nvim/po/ja.po96
-rw-r--r--src/nvim/po/ko.UTF-8.po126
-rw-r--r--src/nvim/po/nb.po126
-rw-r--r--src/nvim/po/nl.po124
-rw-r--r--src/nvim/po/no.po126
-rw-r--r--src/nvim/po/pl.UTF-8.po126
-rw-r--r--src/nvim/po/pt_BR.po127
-rw-r--r--src/nvim/po/ru.po126
-rw-r--r--src/nvim/po/sk.cp1250.po127
-rw-r--r--src/nvim/po/sk.po127
-rw-r--r--src/nvim/po/sr.po98
-rw-r--r--src/nvim/po/sv.po124
-rw-r--r--src/nvim/po/tojavascript.vim18
-rw-r--r--src/nvim/po/tr.po1394
-rw-r--r--src/nvim/po/uk.po100
-rw-r--r--src/nvim/po/vi.po127
-rw-r--r--src/nvim/po/zh_CN.UTF-8.po11922
-rw-r--r--src/nvim/po/zh_TW.UTF-8.po481
-rw-r--r--src/nvim/popupmenu.c155
-rw-r--r--src/nvim/popupmenu.h18
-rw-r--r--src/nvim/profile.c99
-rw-r--r--src/nvim/quickfix.c824
-rw-r--r--src/nvim/rbuffer.c8
-rw-r--r--src/nvim/rbuffer.h2
-rw-r--r--src/nvim/regexp.c757
-rw-r--r--src/nvim/regexp.h3
-rw-r--r--src/nvim/regexp_bt.c1009
-rw-r--r--src/nvim/regexp_defs.h98
-rw-r--r--src/nvim/regexp_nfa.c806
-rw-r--r--src/nvim/runtime.c516
-rw-r--r--src/nvim/runtime.h10
-rw-r--r--src/nvim/screen.c711
-rw-r--r--src/nvim/screen.h7
-rw-r--r--src/nvim/search.c3288
-rw-r--r--src/nvim/search.h13
-rw-r--r--src/nvim/sha256.c40
-rw-r--r--src/nvim/sha256.h6
-rw-r--r--src/nvim/shada.c396
-rw-r--r--src/nvim/sign.c490
-rw-r--r--src/nvim/sign_defs.h10
-rw-r--r--src/nvim/spell.c1007
-rw-r--r--src/nvim/spell_defs.h45
-rw-r--r--src/nvim/spellfile.c1303
-rw-r--r--src/nvim/spellsuggest.c799
-rw-r--r--src/nvim/state.c31
-rw-r--r--src/nvim/state.h2
-rw-r--r--src/nvim/statusline.c900
-rw-r--r--src/nvim/statusline.h9
-rw-r--r--src/nvim/statusline_defs.h26
-rw-r--r--src/nvim/strings.c299
-rw-r--r--src/nvim/strings.h2
-rw-r--r--src/nvim/syntax.c1923
-rw-r--r--src/nvim/syntax.h1
-rw-r--r--src/nvim/syntax_defs.h2
-rw-r--r--src/nvim/tag.c2728
-rw-r--r--src/nvim/tag.h4
-rw-r--r--src/nvim/terminal.c135
-rw-r--r--src/nvim/testdir/Make_all.mak1
-rw-r--r--src/nvim/testdir/Makefile34
-rw-r--r--src/nvim/testdir/check.vim58
-rw-r--r--src/nvim/testdir/runtest.vim45
-rw-r--r--src/nvim/testdir/samples/re.freeze.txt (renamed from test/benchmark/samples/re.freeze.txt)0
-rw-r--r--src/nvim/testdir/sautest/autoload/foo.vim4
-rw-r--r--src/nvim/testdir/script_util.vim6
-rw-r--r--src/nvim/testdir/setup.vim7
-rw-r--r--src/nvim/testdir/shared.vim28
-rw-r--r--src/nvim/testdir/test42.inbin2387 -> 0 bytes
-rw-r--r--src/nvim/testdir/test42.okbin409 -> 0 bytes
-rw-r--r--src/nvim/testdir/test49.in31
-rw-r--r--src/nvim/testdir/test49.ok56
-rw-r--r--src/nvim/testdir/test49.vim6724
-rw-r--r--src/nvim/testdir/test_alot.vim1
-rw-r--r--src/nvim/testdir/test_arglist.vim180
-rw-r--r--src/nvim/testdir/test_assert.vim67
-rw-r--r--src/nvim/testdir/test_autochdir.vim4
-rw-r--r--src/nvim/testdir/test_autocmd.vim535
-rw-r--r--src/nvim/testdir/test_autoload.vim6
-rw-r--r--src/nvim/testdir/test_backup.vim35
-rw-r--r--src/nvim/testdir/test_blob.vim12
-rw-r--r--src/nvim/testdir/test_breakindent.vim202
-rw-r--r--src/nvim/testdir/test_buffer.vim256
-rw-r--r--src/nvim/testdir/test_bufline.vim116
-rw-r--r--src/nvim/testdir/test_cd.vim36
-rw-r--r--src/nvim/testdir/test_charsearch.vim61
-rw-r--r--src/nvim/testdir/test_checkpath.vim17
-rw-r--r--src/nvim/testdir/test_clientserver.vim35
-rw-r--r--src/nvim/testdir/test_cmdline.vim1733
-rw-r--r--src/nvim/testdir/test_comments.vim277
-rw-r--r--src/nvim/testdir/test_compiler.vim8
-rw-r--r--src/nvim/testdir/test_const.vim18
-rw-r--r--src/nvim/testdir/test_cpoptions.vim927
-rw-r--r--src/nvim/testdir/test_cscope.vim344
-rw-r--r--src/nvim/testdir/test_cursor_func.vim141
-rw-r--r--src/nvim/testdir/test_cursorline.vim18
-rw-r--r--src/nvim/testdir/test_debugger.vim64
-rw-r--r--src/nvim/testdir/test_delete.vim14
-rw-r--r--src/nvim/testdir/test_diffmode.vim180
-rw-r--r--src/nvim/testdir/test_digraph.vim13
-rw-r--r--src/nvim/testdir/test_display.vim159
-rw-r--r--src/nvim/testdir/test_edit.vim307
-rw-r--r--src/nvim/testdir/test_escaped_glob.vim2
-rw-r--r--src/nvim/testdir/test_eval_stuff.vim61
-rw-r--r--src/nvim/testdir/test_ex_mode.vim1
-rw-r--r--src/nvim/testdir/test_excmd.vim54
-rw-r--r--src/nvim/testdir/test_exists.vim6
-rw-r--r--src/nvim/testdir/test_exit.vim1
-rw-r--r--src/nvim/testdir/test_expand.vim89
-rw-r--r--src/nvim/testdir/test_expand_func.vim8
-rw-r--r--src/nvim/testdir/test_expr.vim190
-rw-r--r--src/nvim/testdir/test_filechanged.vim6
-rw-r--r--src/nvim/testdir/test_fileformat.vim9
-rw-r--r--src/nvim/testdir/test_filetype.vim204
-rw-r--r--src/nvim/testdir/test_filter_map.vim7
-rw-r--r--src/nvim/testdir/test_fixeol.vim90
-rw-r--r--src/nvim/testdir/test_fnamemodify.vim6
-rw-r--r--src/nvim/testdir/test_fold.vim619
-rw-r--r--src/nvim/testdir/test_functions.vim700
-rw-r--r--src/nvim/testdir/test_getcwd.vim2
-rw-r--r--src/nvim/testdir/test_getvar.vim9
-rw-r--r--src/nvim/testdir/test_gf.vim26
-rw-r--r--src/nvim/testdir/test_global.vim19
-rw-r--r--src/nvim/testdir/test_gui.vim43
-rw-r--r--src/nvim/testdir/test_hardcopy.vim204
-rw-r--r--src/nvim/testdir/test_help.vim134
-rw-r--r--src/nvim/testdir/test_help_tagjump.vim28
-rw-r--r--src/nvim/testdir/test_hide.vim2
-rw-r--r--src/nvim/testdir/test_highlight.vim29
-rw-r--r--src/nvim/testdir/test_history.vim17
-rw-r--r--src/nvim/testdir/test_increment.vim35
-rw-r--r--src/nvim/testdir/test_ins_complete.vim1353
-rw-r--r--src/nvim/testdir/test_lambda.vim38
-rw-r--r--src/nvim/testdir/test_langmap.vim35
-rw-r--r--src/nvim/testdir/test_legacy_filetype.vim4
-rw-r--r--src/nvim/testdir/test_let.vim115
-rw-r--r--src/nvim/testdir/test_lispindent.vim (renamed from src/nvim/testdir/test_lispwords.vim)31
-rw-r--r--src/nvim/testdir/test_listdict.vim212
-rw-r--r--src/nvim/testdir/test_listlbr.vim40
-rw-r--r--src/nvim/testdir/test_makeencoding.vim16
-rw-r--r--src/nvim/testdir/test_maparg.vim35
-rw-r--r--src/nvim/testdir/test_mapping.vim79
-rw-r--r--src/nvim/testdir/test_marks.vim24
-rw-r--r--src/nvim/testdir/test_match.vim127
-rw-r--r--src/nvim/testdir/test_matchfuzzy.vim18
-rw-r--r--src/nvim/testdir/test_menu.vim34
-rw-r--r--src/nvim/testdir/test_messages.vim100
-rw-r--r--src/nvim/testdir/test_method.vim23
-rw-r--r--src/nvim/testdir/test_mksession.vim49
-rw-r--r--src/nvim/testdir/test_modeline.vim84
-rw-r--r--src/nvim/testdir/test_normal.vim655
-rw-r--r--src/nvim/testdir/test_options.vim360
-rw-r--r--src/nvim/testdir/test_partial.vim5
-rw-r--r--src/nvim/testdir/test_paste.vim76
-rw-r--r--src/nvim/testdir/test_popup.vim58
-rw-r--r--src/nvim/testdir/test_preview.vim7
-rw-r--r--src/nvim/testdir/test_profile.vim47
-rw-r--r--src/nvim/testdir/test_prompt_buffer.vim4
-rw-r--r--src/nvim/testdir/test_quickfix.vim1004
-rw-r--r--src/nvim/testdir/test_quotestar.vim7
-rw-r--r--src/nvim/testdir/test_random.vim44
-rw-r--r--src/nvim/testdir/test_recover.vim414
-rw-r--r--src/nvim/testdir/test_regexp_latin.vim55
-rw-r--r--src/nvim/testdir/test_regexp_utf8.vim3
-rw-r--r--src/nvim/testdir/test_registers.vim34
-rw-r--r--src/nvim/testdir/test_reltime.vim4
-rw-r--r--src/nvim/testdir/test_retab.vim32
-rw-r--r--src/nvim/testdir/test_search.vim238
-rw-r--r--src/nvim/testdir/test_search_stat.vim8
-rw-r--r--src/nvim/testdir/test_selectmode.vim3
-rw-r--r--src/nvim/testdir/test_set.vim19
-rw-r--r--src/nvim/testdir/test_shell.vim209
-rw-r--r--src/nvim/testdir/test_signals.vim41
-rw-r--r--src/nvim/testdir/test_signs.vim68
-rw-r--r--src/nvim/testdir/test_sleep.vim1
-rw-r--r--src/nvim/testdir/test_sort.vim5
-rw-r--r--src/nvim/testdir/test_source.vim23
-rw-r--r--src/nvim/testdir/test_spell.vim68
-rw-r--r--src/nvim/testdir/test_startup.vim135
-rw-r--r--src/nvim/testdir/test_startup_utf8.vim4
-rw-r--r--src/nvim/testdir/test_statusline.vim41
-rw-r--r--src/nvim/testdir/test_substitute.vim123
-rw-r--r--src/nvim/testdir/test_swap.vim184
-rw-r--r--src/nvim/testdir/test_syntax.vim113
-rw-r--r--src/nvim/testdir/test_system.vim2
-rw-r--r--src/nvim/testdir/test_tabline.vim57
-rw-r--r--src/nvim/testdir/test_tabpage.vim15
-rw-r--r--src/nvim/testdir/test_tagfunc.vim299
-rw-r--r--src/nvim/testdir/test_tagjump.vim214
-rw-r--r--src/nvim/testdir/test_taglist.vim43
-rw-r--r--src/nvim/testdir/test_termcodes.vim26
-rw-r--r--src/nvim/testdir/test_textformat.vim302
-rw-r--r--src/nvim/testdir/test_timers.vim45
-rw-r--r--src/nvim/testdir/test_trycatch.vim220
-rw-r--r--src/nvim/testdir/test_undo.vim29
-rw-r--r--src/nvim/testdir/test_unlet.vim25
-rw-r--r--src/nvim/testdir/test_user_func.vim337
-rw-r--r--src/nvim/testdir/test_usercommands.vim108
-rw-r--r--src/nvim/testdir/test_utf8.vim87
-rw-r--r--src/nvim/testdir/test_vartabs.vim18
-rw-r--r--src/nvim/testdir/test_viminfo.vim26
-rw-r--r--src/nvim/testdir/test_vimscript.vim5870
-rw-r--r--src/nvim/testdir/test_virtualedit.vim105
-rw-r--r--src/nvim/testdir/test_visual.vim36
-rw-r--r--src/nvim/testdir/test_winbuf_close.vim20
-rw-r--r--src/nvim/testdir/test_window_cmd.vim410
-rw-r--r--src/nvim/testdir/test_window_id.vim12
-rw-r--r--src/nvim/testdir/test_writefile.vim466
-rw-r--r--src/nvim/testdir/view_util.vim16
-rw-r--r--src/nvim/testdir/vim9.vim112
-rw-r--r--src/nvim/testing.c234
-rw-r--r--src/nvim/textformat.c1138
-rw-r--r--src/nvim/textformat.h10
-rw-r--r--src/nvim/textobject.c1750
-rw-r--r--src/nvim/textobject.h11
-rw-r--r--src/nvim/tui/input.c195
-rw-r--r--src/nvim/tui/input.h6
-rw-r--r--src/nvim/tui/terminfo.c87
-rw-r--r--src/nvim/tui/terminfo.h2
-rw-r--r--src/nvim/tui/tui.c1717
-rw-r--r--src/nvim/types.h14
-rw-r--r--src/nvim/ugrid.c8
-rw-r--r--src/nvim/ugrid.h4
-rw-r--r--src/nvim/ui.c266
-rw-r--r--src/nvim/ui.h56
-rw-r--r--src/nvim/ui_bridge.c223
-rw-r--r--src/nvim/ui_bridge.h44
-rw-r--r--src/nvim/ui_client.c144
-rw-r--r--src/nvim/ui_client.h27
-rw-r--r--src/nvim/ui_compositor.c79
-rw-r--r--src/nvim/undo.c162
-rw-r--r--src/nvim/undo_defs.h4
-rw-r--r--src/nvim/usercmd.c213
-rw-r--r--src/nvim/usercmd.h11
-rw-r--r--src/nvim/version.c1244
-rw-r--r--src/nvim/version.h2
-rw-r--r--src/nvim/vim.h67
-rw-r--r--src/nvim/viml/parser/expressions.c88
-rw-r--r--src/nvim/viml/parser/expressions.h5
-rw-r--r--src/nvim/viml/parser/parser.c2
-rw-r--r--src/nvim/viml/parser/parser.h28
-rw-r--r--src/nvim/window.c2420
-rw-r--r--src/nvim/window.h80
-rw-r--r--src/uncrustify.cfg22
-rw-r--r--src/unicode/CaseFolding.txt10
-rw-r--r--src/unicode/Copyright.txt4
-rw-r--r--src/unicode/EastAsianWidth.txt74
-rw-r--r--src/unicode/UnicodeData.txt300
-rw-r--r--src/unicode/emoji-data.txt85
-rw-r--r--test/benchmark/bench_regexp_spec.lua (renamed from test/benchmark/bench_re_freeze_spec.lua)12
-rw-r--r--test/cmakeconfig/paths.lua.in1
-rw-r--r--test/compat.lua (renamed from runtime/lua/vim/compat.lua)0
-rw-r--r--test/functional/api/autocmd_spec.lua26
-rw-r--r--test/functional/api/buffer_spec.lua43
-rw-r--r--test/functional/api/buffer_updates_spec.lua2
-rw-r--r--test/functional/api/command_spec.lua107
-rw-r--r--test/functional/api/extmark_spec.lua22
-rw-r--r--test/functional/api/highlight_spec.lua41
-rw-r--r--test/functional/api/keymap_spec.lua46
-rw-r--r--test/functional/api/proc_spec.lua14
-rw-r--r--test/functional/api/server_notifications_spec.lua22
-rw-r--r--test/functional/api/server_requests_spec.lua2
-rw-r--r--test/functional/api/vim_spec.lua317
-rw-r--r--test/functional/api/window_spec.lua183
-rw-r--r--test/functional/autocmd/autocmd_spec.lua42
-rw-r--r--test/functional/autocmd/cmdline_spec.lua35
-rw-r--r--test/functional/autocmd/cursorhold_spec.lua74
-rw-r--r--test/functional/autocmd/dirchanged_spec.lua28
-rw-r--r--test/functional/autocmd/focus_spec.lua25
-rw-r--r--test/functional/autocmd/signal_spec.lua7
-rw-r--r--test/functional/autocmd/termxx_spec.lua13
-rw-r--r--test/functional/autocmd/win_scrolled_resized_spec.lua329
-rw-r--r--test/functional/autocmd/winscrolled_spec.lua85
-rw-r--r--test/functional/core/channels_spec.lua9
-rw-r--r--test/functional/core/exit_spec.lua6
-rw-r--r--test/functional/core/fileio_spec.lua132
-rw-r--r--test/functional/core/job_spec.lua49
-rw-r--r--test/functional/core/log_spec.lua9
-rw-r--r--test/functional/core/main_spec.lua6
-rw-r--r--test/functional/core/path_spec.lua18
-rw-r--r--test/functional/core/remote_spec.lua9
-rw-r--r--test/functional/core/startup_spec.lua307
-rw-r--r--test/functional/editor/K_spec.lua11
-rw-r--r--test/functional/editor/completion_spec.lua49
-rw-r--r--test/functional/editor/mark_spec.lua38
-rw-r--r--test/functional/editor/mode_insert_spec.lua8
-rw-r--r--test/functional/editor/tabpage_spec.lua48
-rw-r--r--test/functional/editor/undo_spec.lua75
-rw-r--r--test/functional/ex_cmds/cd_spec.lua6
-rw-r--r--test/functional/ex_cmds/dict_notifications_spec.lua63
-rw-r--r--test/functional/ex_cmds/map_spec.lua15
-rw-r--r--test/functional/ex_cmds/mksession_spec.lua12
-rw-r--r--test/functional/ex_cmds/normal_spec.lua17
-rw-r--r--test/functional/ex_cmds/quickfix_commands_spec.lua21
-rw-r--r--test/functional/ex_cmds/quit_spec.lua2
-rw-r--r--test/functional/ex_cmds/source_spec.lua12
-rw-r--r--test/functional/ex_cmds/swapfile_preserve_recover_spec.lua201
-rw-r--r--test/functional/ex_cmds/trust_spec.lua176
-rw-r--r--test/functional/ex_cmds/verbose_spec.lua4
-rw-r--r--test/functional/ex_cmds/write_spec.lua46
-rw-r--r--test/functional/ex_cmds/wviminfo_spec.lua4
-rw-r--r--test/functional/fixtures/CMakeLists.txt23
-rw-r--r--test/functional/fixtures/api_level_10.mpackbin0 -> 30761 bytes
-rw-r--r--test/functional/fixtures/filename_with_unicode_ααα0
-rw-r--r--test/functional/fixtures/printenv-test.c6
-rw-r--r--test/functional/fixtures/start/nvim-leftpad/lua/async_leftpad.lua4
-rw-r--r--test/functional/fixtures/startup-fail.lua7
-rw-r--r--test/functional/fixtures/startup.lua35
-rw-r--r--test/functional/fixtures/tty-test.c18
-rw-r--r--test/functional/helpers.lua262
-rw-r--r--test/functional/legacy/006_argument_list_spec.lua85
-rw-r--r--test/functional/legacy/011_autocommands_spec.lua4
-rw-r--r--test/functional/legacy/025_jump_tag_hidden_spec.lua6
-rw-r--r--test/functional/legacy/030_fileformats_spec.lua2
-rw-r--r--test/functional/legacy/045_folding_spec.lua214
-rw-r--r--test/functional/legacy/055_list_and_dict_types_spec.lua234
-rw-r--r--test/functional/legacy/063_match_and_matchadd_spec.lua100
-rw-r--r--test/functional/legacy/072_undo_file_spec.lua2
-rw-r--r--test/functional/legacy/083_tag_search_with_file_encoding_spec.lua2
-rw-r--r--test/functional/legacy/097_glob_path_spec.lua6
-rw-r--r--test/functional/legacy/arglist_spec.lua228
-rw-r--r--test/functional/legacy/autocmd_option_spec.lua6
-rw-r--r--test/functional/legacy/breakindent_spec.lua44
-rw-r--r--test/functional/legacy/buffer_spec.lua59
-rw-r--r--test/functional/legacy/cmdline_spec.lua183
-rw-r--r--test/functional/legacy/delete_spec.lua2
-rw-r--r--test/functional/legacy/digraph_spec.lua46
-rw-r--r--test/functional/legacy/display_spec.lua148
-rw-r--r--test/functional/legacy/edit_spec.lua64
-rw-r--r--test/functional/legacy/eval_spec.lua2
-rw-r--r--test/functional/legacy/excmd_spec.lua8
-rw-r--r--test/functional/legacy/filechanged_spec.lua4
-rw-r--r--test/functional/legacy/fold_spec.lua335
-rw-r--r--test/functional/legacy/gf_spec.lua3
-rw-r--r--test/functional/legacy/increment_spec.lua2
-rw-r--r--test/functional/legacy/listlbr_spec.lua96
-rw-r--r--test/functional/legacy/match_spec.lua126
-rw-r--r--test/functional/legacy/memory_usage_spec.lua19
-rw-r--r--test/functional/legacy/messages_spec.lua189
-rw-r--r--test/functional/legacy/search_spec.lua73
-rw-r--r--test/functional/legacy/search_stat_spec.lua4
-rw-r--r--test/functional/legacy/source_spec.lua32
-rw-r--r--test/functional/legacy/statusline_spec.lua83
-rw-r--r--test/functional/legacy/substitute_spec.lua (renamed from test/functional/legacy/080_substitute_spec.lua)31
-rw-r--r--test/functional/legacy/syn_attr_spec.lua78
-rw-r--r--test/functional/legacy/tabline_spec.lua100
-rw-r--r--test/functional/legacy/vimscript_spec.lua90
-rw-r--r--test/functional/legacy/visual_mode_spec.lua25
-rw-r--r--test/functional/legacy/window_cmd_spec.lua227
-rw-r--r--test/functional/lua/api_spec.lua2
-rw-r--r--test/functional/lua/buffer_updates_spec.lua20
-rw-r--r--test/functional/lua/diagnostic_spec.lua199
-rw-r--r--test/functional/lua/ffi_spec.lua19
-rw-r--r--test/functional/lua/filetype_spec.lua8
-rw-r--r--test/functional/lua/fs_spec.lua181
-rw-r--r--test/functional/lua/help_spec.lua50
-rw-r--r--test/functional/lua/inspector_spec.lua56
-rw-r--r--test/functional/lua/overrides_spec.lua4
-rw-r--r--test/functional/lua/runtime_spec.lua56
-rw-r--r--test/functional/lua/secure_spec.lua284
-rw-r--r--test/functional/lua/spell_spec.lua2
-rw-r--r--test/functional/lua/thread_spec.lua2
-rw-r--r--test/functional/lua/ui_event_spec.lua147
-rw-r--r--test/functional/lua/ui_spec.lua46
-rw-r--r--test/functional/lua/uri_spec.lua18
-rw-r--r--test/functional/lua/vim_spec.lua264
-rw-r--r--test/functional/lua/xdiff_spec.lua18
-rw-r--r--test/functional/options/autochdir_spec.lua2
-rw-r--r--test/functional/options/defaults_spec.lua99
-rw-r--r--test/functional/options/keymap_spec.lua2
-rw-r--r--test/functional/options/mousescroll_spec.lua18
-rw-r--r--test/functional/plugin/ccomplete_spec.lua61
-rw-r--r--test/functional/plugin/cfilter_spec.lua106
-rw-r--r--test/functional/plugin/editorconfig_spec.lua210
-rw-r--r--test/functional/plugin/health_spec.lua178
-rw-r--r--test/functional/plugin/lsp/codelens_spec.lua35
-rw-r--r--test/functional/plugin/lsp/helpers.lua176
-rw-r--r--test/functional/plugin/lsp/semantic_tokens_spec.lua1219
-rw-r--r--test/functional/plugin/lsp_spec.lua626
-rw-r--r--test/functional/plugin/man_spec.lua38
-rw-r--r--test/functional/plugin/shada_spec.lua73
-rw-r--r--test/functional/preload.lua5
-rw-r--r--test/functional/provider/clipboard_spec.lua64
-rw-r--r--test/functional/provider/perl_spec.lua6
-rw-r--r--test/functional/shada/compatibility_spec.lua2
-rw-r--r--test/functional/shada/merging_spec.lua2
-rw-r--r--test/functional/shada/shada_spec.lua13
-rw-r--r--test/functional/terminal/altscreen_spec.lua12
-rw-r--r--test/functional/terminal/api_spec.lua13
-rw-r--r--test/functional/terminal/buffer_spec.lua20
-rw-r--r--test/functional/terminal/channel_spec.lua15
-rw-r--r--test/functional/terminal/cursor_spec.lua12
-rw-r--r--test/functional/terminal/edit_spec.lua2
-rw-r--r--test/functional/terminal/ex_terminal_spec.lua26
-rw-r--r--test/functional/terminal/helpers.lua34
-rw-r--r--test/functional/terminal/highlight_spec.lua20
-rw-r--r--test/functional/terminal/mouse_spec.lua44
-rw-r--r--test/functional/terminal/scrollback_spec.lua29
-rw-r--r--test/functional/terminal/tui_spec.lua781
-rw-r--r--test/functional/terminal/window_spec.lua15
-rw-r--r--test/functional/terminal/window_split_tab_spec.lua4
-rw-r--r--test/functional/treesitter/highlight_spec.lua126
-rw-r--r--test/functional/treesitter/language_spec.lua46
-rw-r--r--test/functional/treesitter/node_spec.lua54
-rw-r--r--test/functional/treesitter/parser_spec.lua9
-rw-r--r--test/functional/treesitter/utils_spec.lua31
-rw-r--r--test/functional/ui/bufhl_spec.lua2
-rw-r--r--test/functional/ui/cmdline_highlight_spec.lua20
-rw-r--r--test/functional/ui/cmdline_spec.lua422
-rw-r--r--test/functional/ui/cursor_spec.lua2
-rw-r--r--test/functional/ui/decorations_spec.lua401
-rw-r--r--test/functional/ui/diff_spec.lua198
-rw-r--r--test/functional/ui/embed_spec.lua16
-rw-r--r--test/functional/ui/float_spec.lua875
-rw-r--r--test/functional/ui/fold_spec.lua189
-rw-r--r--test/functional/ui/highlight_spec.lua163
-rw-r--r--test/functional/ui/hlstate_spec.lua9
-rw-r--r--test/functional/ui/inccommand_spec.lua92
-rw-r--r--test/functional/ui/inccommand_user_spec.lua2
-rw-r--r--test/functional/ui/linematch_spec.lua995
-rw-r--r--test/functional/ui/messages_spec.lua147
-rw-r--r--test/functional/ui/mode_spec.lua20
-rw-r--r--test/functional/ui/mouse_spec.lua284
-rw-r--r--test/functional/ui/multigrid_spec.lua100
-rw-r--r--test/functional/ui/options_spec.lua10
-rw-r--r--test/functional/ui/output_spec.lua21
-rw-r--r--test/functional/ui/popupmenu_spec.lua993
-rw-r--r--test/functional/ui/screen.lua22
-rw-r--r--test/functional/ui/screen_basic_spec.lua127
-rw-r--r--test/functional/ui/searchhl_spec.lua22
-rw-r--r--test/functional/ui/sign_spec.lua97
-rw-r--r--test/functional/ui/spell_spec.lua101
-rw-r--r--test/functional/ui/statuscolumn_spec.lua462
-rw-r--r--test/functional/ui/statusline_spec.lua145
-rw-r--r--test/functional/ui/syntax_conceal_spec.lua85
-rw-r--r--test/functional/ui/tabline_spec.lua42
-rw-r--r--test/functional/ui/wildmode_spec.lua106
-rw-r--r--test/functional/ui/winbar_spec.lua7
-rw-r--r--test/functional/vimscript/api_functions_spec.lua3
-rw-r--r--test/functional/vimscript/container_functions_spec.lua2
-rw-r--r--test/functional/vimscript/ctx_functions_spec.lua12
-rw-r--r--test/functional/vimscript/eval_spec.lua80
-rw-r--r--test/functional/vimscript/executable_spec.lua28
-rw-r--r--test/functional/vimscript/execute_spec.lua4
-rw-r--r--test/functional/vimscript/exepath_spec.lua19
-rw-r--r--test/functional/vimscript/fnamemodify_spec.lua6
-rw-r--r--test/functional/vimscript/functions_spec.lua4
-rw-r--r--test/functional/vimscript/has_spec.lua4
-rw-r--r--test/functional/vimscript/hostname_spec.lua6
-rw-r--r--test/functional/vimscript/input_spec.lua77
-rw-r--r--test/functional/vimscript/json_functions_spec.lua2
-rw-r--r--test/functional/vimscript/map_functions_spec.lua69
-rw-r--r--test/functional/vimscript/msgpack_functions_spec.lua4
-rw-r--r--test/functional/vimscript/null_spec.lua2
-rw-r--r--test/functional/vimscript/server_spec.lua30
-rw-r--r--test/functional/vimscript/system_spec.lua65
-rw-r--r--test/functional/vimscript/timer_spec.lua12
-rw-r--r--test/functional/vimscript/writefile_spec.lua20
-rw-r--r--test/helpers.lua162
-rw-r--r--test/includes/CMakeLists.txt32
-rw-r--r--test/unit/buffer_spec.lua10
-rw-r--r--test/unit/eval/typval_spec.lua14
-rw-r--r--test/unit/helpers.lua213
-rw-r--r--test/unit/message_spec.lua2
-rw-r--r--test/unit/os/fs_spec.lua6
-rw-r--r--test/unit/path_spec.lua20
-rw-r--r--test/unit/search_spec.lua2
-rw-r--r--test/unit/tui_spec.lua19
1239 files changed, 125356 insertions, 95432 deletions
diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml
deleted file mode 100644
index 4be49fd153..0000000000
--- a/.builds/freebsd.yml
+++ /dev/null
@@ -1,45 +0,0 @@
-image: freebsd/latest
-
-packages:
-- cmake
-- gmake
-- ninja
-- libtool
-- automake
-- pkgconf
-- unzip
-- wget
-- gettext
-- python
-- libffi
-- gdb
-
-sources:
-- https://github.com/neovim/neovim
-
-environment:
- SOURCEHUT: 1
- LANG: en_US.UTF-8
- 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
-- build: |
- cd neovim
- gmake CMAKE_BUILD_TYPE=RelWithDebInfo CMAKE_EXTRA_FLAGS="${CMAKE_EXTRA_FLAGS}" nvim
-- functionaltest: |
- cd neovim
- gmake functionaltest
-- unittest: |
- cd neovim
- gmake unittest
-- oldtest: |
- cd neovim
- gmake oldtest
diff --git a/.builds/openbsd.yml b/.builds/openbsd.yml
deleted file mode 100644
index b5799e424b..0000000000
--- a/.builds/openbsd.yml
+++ /dev/null
@@ -1,49 +0,0 @@
-# sourcehut CI: https://builds.sr.ht/~jmk/neovim
-
-image: openbsd/latest
-
-packages:
-- autoconf-2.71
-- automake-1.16.3
-- cmake
-- gettext-runtime
-- gettext-tools
-- gmake
-- libtool
-- ninja
-- unzip-6.0p14
-- gdb
-
-sources:
-- https://github.com/neovim/neovim
-
-environment:
- SOURCEHUT: 1
- LC_CTYPE: en_US.UTF-8
- 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 ../cmake.deps/
- cmake --build . --config RelWithDebInfo
-- build: |
- mkdir neovim/build
- cd neovim/build
- cmake -G Ninja $CMAKE_EXTRA_FLAGS ..
- cmake --build . --config RelWithDebInfo
- ./bin/nvim --version
-- functionaltest: |
- cd neovim/build
- cmake --build . --config RelWithDebInfo --target functionaltest
-- oldtest: |
- cd neovim
- gmake oldtest
diff --git a/.cirrus.yml b/.cirrus.yml
new file mode 100644
index 0000000000..d9b67eafe7
--- /dev/null
+++ b/.cirrus.yml
@@ -0,0 +1,29 @@
+env:
+ CIRRUS_CLONE_DEPTH: '2'
+ LANG: en_US.UTF-8
+ CMAKE_EXTRA_FLAGS: -DCI_BUILD=ON -DMIN_LOG_LEVEL=3
+
+freebsd_task:
+ name: FreeBSD
+ only_if: $BRANCH != "master"
+ freebsd_instance:
+ image_family: freebsd-13-1
+ timeout_in: 30m
+ install_script:
+ - pkg update -f
+ - pkg install -y cmake gmake ninja libtool automake pkgconf unzip wget gettext python libffi git
+ build_deps_script:
+ - gmake deps
+ build_script:
+ - gmake CMAKE_EXTRA_FLAGS="${CMAKE_EXTRA_FLAGS}" nvim
+ workaround_script:
+ # Run tests as user "cirrus" instead of root. This is required for the
+ # permission-related tests to work correctly.
+ - pw useradd cirrus -m
+ - chown -R cirrus:cirrus .
+ functionaltest_script:
+ - sudo -u cirrus gmake functionaltest
+ unittest_script:
+ - sudo -u cirrus gmake unittest
+ oldtest_script:
+ - sudo -u cirrus gmake oldtest
diff --git a/.clang-tidy b/.clang-tidy
new file mode 100644
index 0000000000..1fe87ba501
--- /dev/null
+++ b/.clang-tidy
@@ -0,0 +1,42 @@
+Checks: >
+ -*,
+
+ bugprone-*,
+ google-*,
+ misc-*,
+ modernize-*,
+ performance-*,
+ portability-*,
+ readability-*,
+
+ -bugprone-assignment-in-if-condition,
+ -bugprone-branch-clone,
+ -bugprone-easily-swappable-parameters,
+ -bugprone-implicit-widening-of-multiplication-result,
+ -bugprone-macro-parentheses,
+ -bugprone-narrowing-conversions,
+ -bugprone-not-null-terminated-result,
+ -bugprone-reserved-identifier,
+ -bugprone-sizeof-expression,
+ -bugprone-suspicious-include,
+ -bugprone-suspicious-memory-comparison,
+ -bugprone-unused-return-value,
+ -google-readability-braces-around-statements,
+ -google-readability-function-size,
+ -misc-misplaced-const,
+ -misc-no-recursion,
+ -misc-unused-parameters,
+ -modernize-macro-to-enum,
+ -performance-no-int-to-ptr,
+ -readability-avoid-const-params-in-decls,
+ -readability-braces-around-statements,
+ -readability-else-after-return,
+ -readability-function-cognitive-complexity,
+ -readability-function-size,
+ -readability-identifier-length,
+ -readability-isolate-declaration,
+ -readability-magic-numbers,
+ -readability-misleading-indentation,
+ -readability-redundant-declaration,
+ -readability-redundant-function-ptr-dereference,
+ -readability-suspicious-call-argument,
diff --git a/.editorconfig b/.editorconfig
index 2aa956b1fc..07993e25b9 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -10,6 +10,9 @@ insert_final_newline = true
[*.{c,h,in,lua}]
max_line_length = 100
+[*.py]
+indent_size = 4
+
[{Makefile,**/Makefile,runtime/doc/*.txt}]
indent_style = tab
indent_size = 8
diff --git a/.flake8 b/.flake8
deleted file mode 100644
index 2bcd70e390..0000000000
--- a/.flake8
+++ /dev/null
@@ -1,2 +0,0 @@
-[flake8]
-max-line-length = 88
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index 23c7862b99..4dba469ebe 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -50,10 +50,32 @@ ee031eb5256bb83e0d6add2bae6fd943a4186ffe
aefdc6783cb77f09786542c90901a9e7120bea42
aa4f9c5341f5280f16cce0630ea54b84eef717b3
0adc66171a355a12494d87ebb767d509540c7ef9
+93f24403f8cc760ff47979c596976b53a8b16358
+1ffd527c837fb2465c9659273bbe5447a1352db2
+2498e9feb025361576603a0101c86393d211e31e
+0b3ae64480ea28bb57783c2269a61f0a60ffc55e
+0fc8597f011e0927e529abd11bf0ddd8d0d1eaab
+6ff245732a5a8ab821598a38fb0c5805e6bd3779
+abf758a2977c4e6cab4dfa217f56da853d85851c
+cb84f5ee530f0f32b92bed5b4ad41344e8b551aa
# typos
d238b8f6003d34cae7f65ff7585b48a2cd9449fb
4547137aaff32b20172870a549d3a28a3c7adf1c
+08616571f47cc367a5fe59b52295708b9fda3b09
+09c6ce8c4e4c6415cca9b834539ed0df461373f6
+0b0c4f7dfa4a9a564cbf44262d4bea9bdefe2dc9
+4a96e7809f4d9f6ce21869817eb95ff6dcaa1693
+61205c1defb64ac5466496b5451e4a7f3171e21e
+64116d78502e0ca611e13adf9323ef2d3fe708c2
+abc087f4c65ca547cae58518b42aee82ff4a07f6
+b8dcbcc732baf84fc48d6b272c3ade0bcb129b3b
+c815aadfccd6bada47ecfb09fe188ee7f7c5caf3
+caa6992a1071a2ac373bec21085685da4a1790d6
+df646572c53f55268a5dbb61628d7c3b302d5663
+e63e5d1dbd3dd4711efa0ecf9e844ff308b370a6
+eb123b565e201418dd135d2602dc20eea3cead39
+ff20d40321399fa187bd350f9619cf6418d7eb6e
# generated docs
ea333badd24f691c753d8048f911d1db349bc2cd
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 11f27dde21..5fd7bc37b6 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -8,6 +8,33 @@ body:
value: |
_Before reporting:_ search [existing issues](https://github.com/neovim/neovim/issues?q=is%3Aissue+is%3Aopen+label%3Abug) and check the [FAQ](https://github.com/neovim/neovim/wiki/FAQ). Usage questions such as "How do I...?" belong on the [Neovim Discourse](https://neovim.discourse.group/c/7-category/7) and will be closed.
+ - type: textarea
+ attributes:
+ label: "Describe the bug"
+ description: "Describe the current behavior. May include logs, images, or videos."
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: "Steps to reproduce"
+ description: |
+ - For build failures: list the exact steps including CMake flags (if any).
+ - For startup or shell-related problems: try `env -i TERM=ansi-256color "$(which nvim)"`.
+ placeholder: |
+ nvim --clean
+ :edit foo
+ yiwp
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: "Expected behavior"
+ description: "Describe the behavior you expect."
+ validations:
+ required: true
+
- type: input
attributes:
label: "Neovim version (nvim -v)"
@@ -47,29 +74,3 @@ body:
placeholder: "Arch User Repository (AUR)"
validations:
required: true
-
- - type: textarea
- attributes:
- label: "How to reproduce the issue"
- description: |
- - Steps to reproduce using `nvim --clean` ("factory defaults").
- - For build failures: list the exact steps including CMake flags (if any).
- - For shell-related problems: try `env -i TERM=ansi-256color "$(which nvim)"`.
- placeholder: |
- nvim --clean
- :edit foo
- yiwp
- validations:
- required: true
-
- - type: textarea
- attributes:
- label: "Expected behavior"
- description: "Describe the behavior you expect. May include logs, images, or videos."
- validations:
- required: true
- - type: textarea
- attributes:
- label: "Actual behavior"
- validations:
- required: true
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
index 2b6fa3daf4..4b9a443064 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.yml
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -8,14 +8,17 @@ body:
value: |
Before requesting: search [existing issues](https://github.com/neovim/neovim/labels/enhancement) and check the [FAQ](https://github.com/neovim/neovim/wiki/FAQ).
- - type: input
+ - type: textarea
attributes:
- label: "Feature already in Vim?"
- description: "Does the feature already exist in Vim? If possible, specify which version (or commit) that introduced it."
- placeholder: "Yes, Vim 7.3.432"
+ label: "Problem"
+ description: "Describe the problem to be solved. Include references to other projects (Vim, Emacs, etc.) if relevant."
+ placeholder: "No smurf icons available. Smurfs are useful because ..."
+ validations:
+ required: true
- type: textarea
attributes:
- label: "Feature description"
+ label: "Expected behavior"
+ description: "Describe what the new feature or behavior would look like. How does it solve the problem? Is it worth the cost?"
validations:
required: true
diff --git a/.github/ISSUE_TEMPLATE/lsp_bug_report.yml b/.github/ISSUE_TEMPLATE/lsp_bug_report.yml
index 0ed163c9be..88867ce644 100644
--- a/.github/ISSUE_TEMPLATE/lsp_bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/lsp_bug_report.yml
@@ -31,8 +31,38 @@ body:
attributes:
label: 'Steps to reproduce using "nvim -u minimal_init.lua"'
description: |
- - Download the minimal config with `curl -LO https://raw.githubusercontent.com/neovim/nvim-lspconfig/master/test/minimal_init.lua` and modify it to include any specific commands or servers pertaining to your issues.
- - _Note_: if the issue is with an autocompletion or other LSP plugin, report to that plugin's issue tracker.
+ - Create a minimal_init.lua using vim.lsp.start:
+
+ ```lua
+ --- CHANGE THESE
+ local pattern = 'the-filetype'
+ local cmd = {'name-of-language-server-executable'}
+ -- Add files/folders here that indicate the root of a project
+ local root_markers = {'.git', '.editorconfig'}
+ -- Change to table with settings if required
+ local settings = vim.empty_dict()
+
+ vim.api.nvim_create_autocmd('FileType', {
+ pattern = pattern,
+ callback = function(args)
+ local match = vim.fs.find(root_markers, { path = args.file, upward = true })[1]
+ local root_dir = match and vim.fn.fnamemodify(match, ':p:h') or vim.NIL
+ vim.lsp.start({
+ name = 'bugged-ls',
+ cmd = cmd,
+ root_dir = root_dir,
+ settings = settings
+ })
+ end
+ })
+ ```
+
+ See `:h lsp-quickstart` and `:h vim.lsp.start` for more information
+
+ - Provide a short code example and describe the folder layout
+ - Describe how to trigger the issue. E.g. using `:lua vim.lsp.buf.*` commands
+
+ _Note_: if the issue is with an autocompletion or other LSP plugin, report to that plugin's issue tracker.
validations:
required: true
diff --git a/.github/SECURITY.md b/.github/SECURITY.md
new file mode 100644
index 0000000000..a376d87912
--- /dev/null
+++ b/.github/SECURITY.md
@@ -0,0 +1,2 @@
+To report a security vulnerability to Neovim, use
+https://github.com/neovim/neovim/security/advisories/new
diff --git a/.github/actions/cache/action.yml b/.github/actions/cache/action.yml
new file mode 100644
index 0000000000..858045c02a
--- /dev/null
+++ b/.github/actions/cache/action.yml
@@ -0,0 +1,22 @@
+name: 'Cache'
+description: "This action caches neovim dependencies"
+runs:
+ using: "composite"
+ steps:
+ - run: echo "CACHE_KEY=${{ github.job }}-${{ github.base_ref }}" >> $GITHUB_ENV
+ shell: bash
+
+ - if: ${{ matrix }}
+ run: echo "CACHE_KEY=$CACHE_KEY-${{ join(matrix.*, '-') }}" >> $GITHUB_ENV
+ shell: bash
+
+ # Avoid using '**/CMakeLists.txt' (or any pattern starting with '**/') even
+ # if it makes the expression below simpler. hashFiles() has a timer that
+ # will fail the job if it times out, which can happen if there are too many
+ # files to search through.
+ - uses: actions/cache@v3
+ with:
+ path: ${{ env.CACHE_NVIM_DEPS_DIR }}
+ key: ${{ env.CACHE_KEY }}-${{ hashFiles('cmake**', 'ci/**',
+ '.github/workflows/ci.yml', 'CMakeLists.txt',
+ 'runtime/CMakeLists.txt', 'src/nvim/**/CMakeLists.txt') }}
diff --git a/.github/labeler.yml b/.github/labeler.yml
index e86c7aabe8..ad48287246 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -13,6 +13,7 @@
- src/nvim/lua/treesitter.*
- runtime/lua/vim/treesitter.lua
- runtime/lua/vim/treesitter/*
+ - runtime/queries/**/*
"diagnostic":
- runtime/lua/vim/diagnostic.lua
@@ -62,3 +63,8 @@
"filetype":
- runtime/lua/vim/filetype.lua
+ - runtime/lua/vim/filetype/detect.lua
+
+"platform:nix":
+ - contrib/flake.lock
+ - contrib/flake.nix
diff --git a/.github/scripts/install_deps_ubuntu.sh b/.github/scripts/install_deps_ubuntu.sh
new file mode 100755
index 0000000000..012409ba4a
--- /dev/null
+++ b/.github/scripts/install_deps_ubuntu.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+PACKAGES=(
+ autoconf
+ automake
+ build-essential
+ cmake
+ cpanminus
+ curl
+ gettext
+ libtool-bin
+ locales-all
+ ninja-build
+ pkg-config
+ unzip
+)
+
+sudo apt-get update
+sudo apt-get install -y "${PACKAGES[@]}"
diff --git a/.github/scripts/remove-reviewers.js b/.github/scripts/remove-reviewers.js
index 631f08e57d..40a8eca423 100644
--- a/.github/scripts/remove-reviewers.js
+++ b/.github/scripts/remove-reviewers.js
@@ -6,11 +6,13 @@ module.exports = async ({github, context}) => {
});
const reviewers = requestedReviewers.data.users.map(e => e.login)
+ const team_reviewers = requestedReviewers.data.teams.map(e => e.name);
github.rest.pulls.removeRequestedReviewers({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
- reviewers: reviewers
+ reviewers: reviewers,
+ team_reviewers: team_reviewers
});
}
diff --git a/.github/scripts/reviews.js b/.github/scripts/reviews.js
index 6e9a829353..cc6aaa1e8b 100644
--- a/.github/scripts/reviews.js
+++ b/.github/scripts/reviews.js
@@ -7,24 +7,29 @@ module.exports = async ({github, context}) => {
const labels = pr_data.data.labels.map(e => e.name)
const reviewers = new Set()
+ const team_reviewers = new Array()
if (labels.includes('api')) {
reviewers.add("bfredl")
- reviewers.add("gpanders")
reviewers.add("muniter")
}
if (labels.includes('build')) {
reviewers.add("jamessan")
+ reviewers.add("justinmk")
}
if (labels.includes('ci')) {
- reviewers.add("jamessan")
+ team_reviewers.push('ci');
}
if (labels.includes('column')) {
reviewers.add("lewis6991")
}
+ if (labels.includes('dependencies')) {
+ reviewers.add("jamessan")
+ }
+
if (labels.includes('diagnostic')) {
reviewers.add("gpanders")
}
@@ -33,10 +38,6 @@ module.exports = async ({github, context}) => {
reviewers.add("lewis6991")
}
- if (labels.includes('dependencies')) {
- reviewers.add("jamessan")
- }
-
if (labels.includes('distribution')) {
reviewers.add("jamessan")
}
@@ -52,20 +53,32 @@ module.exports = async ({github, context}) => {
if (labels.includes('filetype')) {
reviewers.add("clason")
reviewers.add("gpanders")
+ reviewers.add("smjonas")
}
- if (labels.includes('gui')) {
- reviewers.add("glacambre")
- reviewers.add("smolck")
+ if (labels.includes('lsp')) {
+ team_reviewers.push('lsp');
}
- if (labels.includes('lsp')) {
- reviewers.add("mfussenegger")
+ if (labels.includes('platform:nix')) {
+ reviewers.add("teto")
}
- if (labels.includes('treesitter')) {
+ if (labels.includes('project-management')) {
+ reviewers.add("bfredl")
+ reviewers.add("justinmk")
+ }
+
+ if (labels.includes('refactor')) {
reviewers.add("bfredl")
- reviewers.add("vigoux")
+ }
+
+ if (labels.includes('test')) {
+ reviewers.add("justinmk")
+ }
+
+ if (labels.includes('treesitter')) {
+ team_reviewers.push('treesitter');
}
if (labels.includes('typo')) {
@@ -89,6 +102,7 @@ module.exports = async ({github, context}) => {
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
- reviewers: Array.from(reviewers)
+ reviewers: Array.from(reviewers),
+ team_reviewers: team_reviewers
});
}
diff --git a/.github/scripts/unstale.js b/.github/scripts/unstale.js
new file mode 100644
index 0000000000..f645fca5cb
--- /dev/null
+++ b/.github/scripts/unstale.js
@@ -0,0 +1,19 @@
+module.exports = async ({ github, context }) => {
+ const commenter = context.actor;
+ const issue = await github.rest.issues.get({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: context.issue.number,
+ });
+ const author = issue.data.user.login;
+ const labels = issue.data.labels.map((e) => e.name);
+
+ if (author === commenter && labels.includes("needs:response")) {
+ github.rest.issues.removeLabel({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: context.issue.number,
+ name: "needs:response",
+ });
+ }
+};
diff --git a/.github/workflows/reviews.yml b/.github/workflows/add-reviewers.yml
index 34ce19d830..f1abab5c53 100644
--- a/.github/workflows/reviews.yml
+++ b/.github/workflows/add-reviewers.yml
@@ -1,7 +1,7 @@
name: "Request reviews"
on:
pull_request_target:
- types: [labeled, ready_for_review]
+ types: [labeled, ready_for_review, reopened]
jobs:
request-reviewer:
if: github.event.pull_request.state == 'open' && github.event.pull_request.draft == false
@@ -13,6 +13,7 @@ jobs:
- name: 'Request reviewers'
uses: actions/github-script@v6
with:
+ github-token: ${{ secrets.TEAM_REVIEW }}
script: |
const script = require('./.github/scripts/reviews.js')
await script({github, context})
diff --git a/.github/workflows/api-docs.yml b/.github/workflows/api-docs.yml
index 83554e2a3d..fa8a7dbca0 100644
--- a/.github/workflows/api-docs.yml
+++ b/.github/workflows/api-docs.yml
@@ -21,13 +21,17 @@ on:
jobs:
regen-api-docs:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
+ - uses: rhysd/action-setup-vim@v1
+ with:
+ neovim: true
+ version: nightly
- uses: actions/checkout@v3
with:
# Fetch depth 0 is required if called through workflow_call. In order
@@ -38,7 +42,7 @@ jobs:
- name: Install dependencies
run: |
sudo apt-get update
- sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y doxygen python3 python3-msgpack luajit
+ sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y doxygen python3 python3-msgpack
- name: Setup git config
run: |
@@ -52,7 +56,7 @@ jobs:
run: |
git checkout -b ${DOC_BRANCH}
python3 scripts/gen_vimdoc.py
- printf '::set-output name=UPDATED_DOCS::%s\n' $([ -z "$(git diff)" ]; echo $?)
+ printf 'UPDATED_DOCS=%s\n' $([ -z "$(git diff)" ]; echo $?) >> $GITHUB_OUTPUT
- name: FAIL, PR has not committed doc changes
if: ${{ steps.docs.outputs.UPDATED_DOCS != 0 && inputs.check_only }}
diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml
index b5fd22d036..019fb20689 100644
--- a/.github/workflows/backport.yml
+++ b/.github/workflows/backport.yml
@@ -21,7 +21,7 @@ jobs:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha }}
- name: Create backport PRs
- uses: zeebe-io/backport-action@v0.0.7
+ uses: zeebe-io/backport-action@v0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
github_workspace: ${{ github.workspace }}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index e22d99067a..b158d966e1 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -9,17 +9,22 @@ on:
- 'master'
- 'release-[0-9]+.[0-9]+'
paths-ignore:
- - 'runtime/doc/*'
+ - 'contrib/**'
# Cancel any in-progress CI runs for a PR if it is updated
concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }}
cancel-in-progress: true
+env:
+ UNCRUSTIFY_VERSION: uncrustify-0.75.0
+ # TEST_FILE: test/functional/core/startup_spec.lua
+ # TEST_FILTER: foo
+
jobs:
lint:
if: (github.event_name == 'pull_request' && github.base_ref == 'master') || (github.event_name == 'push' && github.ref == 'refs/heads/master')
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
timeout-minutes: 10
env:
CC: gcc
@@ -31,34 +36,8 @@ jobs:
- 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 \
- 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
+ ./.github/scripts/install_deps_ubuntu.sh
+ sudo apt-get install -y lua-check
- name: Cache uncrustify
id: cache-uncrustify
@@ -85,41 +64,40 @@ jobs:
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 }}
- key: lint-${{ hashFiles('cmake/*', '**/CMakeLists.txt', '!cmake.deps/**CMakeLists.txt') }}-${{ github.base_ref }}
+ - uses: ./.github/actions/cache
- name: Build third-party deps
run: ./ci/before_script.sh
- if: "!cancelled()"
+ name: Determine if run should be aborted
+ id: abort_job
+ run: echo "status=${{ job.status }}" >> $GITHUB_OUTPUT
+
+ - if: success() || failure() && steps.abort_job.outputs.status == 'success'
name: lintstylua
- uses: JohnnyMorganz/stylua-action@1.0.0
+ uses: JohnnyMorganz/stylua-action@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
+ version: latest
args: --check runtime/
- - if: "!cancelled()"
- name: lintlua
- run: make lintlua
-
- - if: "!cancelled()"
- name: lintpy
- run: make lintpy
+ - if: success() || failure() && steps.abort_job.outputs.status == 'success'
+ name: luacheck
+ run: |
+ cmake -B $BUILD_DIR -G Ninja
+ cmake --build $BUILD_DIR --target lintlua-luacheck
- - if: "!cancelled()"
+ - if: success() || failure() && steps.abort_job.outputs.status == 'success'
name: lintsh
run: make lintsh
- - if: "!cancelled()"
+ - if: success() || failure() && steps.abort_job.outputs.status == 'success'
name: uncrustify
run: |
${{ env.CACHE_UNCRUSTIFY }} -c ./src/uncrustify.cfg -q --replace --no-backup $(find ./src/nvim -name "*.[ch]")
- - if: "!cancelled()"
+ - if: success() || failure() && steps.abort_job.outputs.status == 'success'
name: suggester / uncrustify
uses: reviewdog/action-suggester@v1
with:
@@ -127,7 +105,7 @@ jobs:
tool_name: uncrustify
cleanup: false
- - if: "!cancelled()"
+ - if: success() || failure() && steps.abort_job.outputs.status == 'success'
name: check uncrustify
run: |
git diff --color --exit-code
@@ -142,7 +120,7 @@ jobs:
# 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
+ runs-on: ubuntu-22.04
timeout-minutes: 10
env:
CC: gcc
@@ -155,39 +133,24 @@ jobs:
- name: Install apt packages
run: |
sudo add-apt-repository ppa:neovim-ppa/stable
- sudo apt-get update
+ ./.github/scripts/install_deps_ubuntu.sh
sudo apt-get install -y \
- autoconf \
- automake \
- build-essential \
- cmake \
- 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
+ luajit
- - name: Cache artifacts
- uses: actions/cache@v3
- with:
- path: |
- ${{ env.CACHE_NVIM_DEPS_DIR }}
- key: lint-${{ hashFiles('cmake/*', '**/CMakeLists.txt', '!cmake.deps/**CMakeLists.txt') }}-${{ github.base_ref }}
+ - uses: ./.github/actions/cache
- name: Build third-party deps
run: ./ci/before_script.sh
@@ -196,10 +159,15 @@ jobs:
run: ./ci/run_tests.sh build_nvim
- if: "!cancelled()"
- name: lintc
- run: make lintc
+ name: Determine if run should be aborted
+ id: abort_job
+ run: echo "status=${{ job.status }}" >> $GITHUB_OUTPUT
- - if: "!cancelled()"
+ - if: success() || failure() && steps.abort_job.outputs.status == 'success'
+ name: clint.py
+ run: cmake --build build --target lintc-clint
+
+ - if: success() || failure() && steps.abort_job.outputs.status == 'success'
name: check-single-includes
run: make check-single-includes
@@ -213,19 +181,19 @@ jobs:
matrix:
include:
- flavor: asan
- cc: clang-13
- runner: ubuntu-20.04
+ cc: clang
+ runner: ubuntu-22.04
os: linux
- flavor: tsan
- cc: clang-13
- runner: ubuntu-20.04
+ cc: clang
+ runner: ubuntu-22.04
os: linux
- flavor: uchar
cc: gcc
- runner: ubuntu-20.04
+ runner: ubuntu-22.04
os: linux
- cc: clang
- runner: macos-11
+ runner: macos-12
os: osx
# functionaltest-lua is our dumping ground for non-mainline configurations.
@@ -236,7 +204,7 @@ jobs:
# 3. No treesitter parsers installed.
- flavor: functionaltest-lua
cc: gcc
- runner: ubuntu-20.04
+ runner: ubuntu-22.04
os: linux
cmake: minimum_required
runs-on: ${{ matrix.runner }}
@@ -252,9 +220,7 @@ jobs:
- name: Install apt packages
if: matrix.os == 'linux'
- run: |
- sudo apt-get update
- sudo apt-get install -y autoconf automake build-essential 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
+ run: ./.github/scripts/install_deps_ubuntu.sh
- name: Install minimum required version of cmake
if: matrix.cmake == 'minimum_required'
@@ -274,14 +240,6 @@ jobs:
exit 1
}
- - name: Install new clang
- if: matrix.flavor == 'asan' || matrix.flavor == 'tsan'
- run: |
- wget https://apt.llvm.org/llvm.sh
- chmod a+x llvm.sh
- sudo ./llvm.sh 13
- rm llvm.sh
-
- name: Install brew packages
if: matrix.os == 'osx'
run: |
@@ -291,12 +249,7 @@ jobs:
- name: Setup interpreter packages
run: ./ci/install.sh
- - name: Cache dependencies
- uses: actions/cache@v3
- with:
- path: |
- ${{ env.CACHE_NVIM_DEPS_DIR }}
- key: ${{ matrix.runner }}-${{ matrix.flavor }}-${{ matrix.cc }}-${{ hashFiles('cmake/*', 'cmake.deps/**', '**/CMakeLists.txt') }}-${{ github.base_ref }}
+ - uses: ./.github/actions/cache
- name: Build third-party deps
run: ./ci/before_script.sh
@@ -304,19 +257,24 @@ jobs:
- name: Build
run: ./ci/run_tests.sh build_nvim
- - if: matrix.flavor != 'tsan' && matrix.flavor != 'functionaltest-lua' && !cancelled()
+ - if: "!cancelled()"
+ name: Determine if run should be aborted
+ id: abort_job
+ run: echo "status=${{ job.status }}" >> $GITHUB_OUTPUT
+
+ - if: matrix.flavor != 'tsan' && matrix.flavor != 'functionaltest-lua' && (success() || failure() && steps.abort_job.outputs.status == 'success')
name: Unittests
run: ./ci/run_tests.sh unittests
- - if: matrix.flavor != 'tsan' && !cancelled()
+ - if: success() || failure() && steps.abort_job.outputs.status == 'success'
name: Functionaltests
run: ./ci/run_tests.sh functionaltests
- - if: "!cancelled()"
+ - if: matrix.flavor != 'tsan' && (success() || failure() && steps.abort_job.outputs.status == 'success')
name: Oldtests
run: ./ci/run_tests.sh oldtests
- - if: "!cancelled()"
+ - if: success() || failure() && steps.abort_job.outputs.status == 'success'
name: Install nvim
run: ./ci/run_tests.sh install_nvim
@@ -327,32 +285,67 @@ jobs:
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) }}
- CMAKE_BUILD_TYPE: "RelWithDebInfo"
+ DEPS_BUILD_DIR: ${{ github.workspace }}/nvim-deps
+ CACHE_NVIM_DEPS_DIR: ${{ github.workspace }}/nvim-deps
+ DEPS_PREFIX: ${{ github.workspace }}/nvim-deps/usr
name: windows (MSVC_64)
steps:
- uses: actions/checkout@v3
- - uses: actions/cache@v3
- with:
- path: ${{ env.DEPS_BUILD_DIR }}
- key: ${{ hashFiles('cmake.deps\**') }}
+ - uses: ./.github/actions/cache
+
+ - name: Set env
+ run: ./.github/workflows/env.ps1
- name: Build deps
- run: .\ci\build.ps1 -BuildDeps
+ run: |
+ cmake -S cmake.deps -B $env:DEPS_BUILD_DIR -G Ninja -DCMAKE_BUILD_TYPE='RelWithDebInfo'
+ cmake --build $env:DEPS_BUILD_DIR
- name: Build nvim
- run: .\ci\build.ps1 -Build
+ run: |
+ cmake -B build -G Ninja -DCMAKE_BUILD_TYPE='RelWithDebInfo' -DDEPS_PREFIX="$env:DEPS_PREFIX" -DCI_BUILD=ON
+ cmake --build build
- name: Install test deps
- continue-on-error: false
- run: .\ci\build.ps1 -EnsureTestDeps
+ run: |
+ $PSNativeCommandArgumentPassing = 'Legacy'
- - if: "!cancelled()"
- name: Run tests
- run: .\ci\build.ps1 -Test
+ & build\bin\nvim.exe "--version"
+
+ # Ensure that the "win32" feature is set.
+ & build\bin\nvim -u NONE --headless -c 'exe !has(\"win32\").\"cq\"'
+
+ python -m pip install pynvim
+ # Sanity check
+ python -c "import pynvim; print(str(pynvim))"
+
+ gem.cmd install --pre neovim
+ Get-Command -CommandType Application neovim-ruby-host.bat
+
+ node --version
+ npm.cmd --version
+
+ npm.cmd install -g neovim
+ Get-Command -CommandType Application neovim-node-host.cmd
+ npm.cmd link neovim
- if: "!cancelled()"
- name: Run old tests
- run: .\ci\build.ps1 -TestOld
+ name: Determine if run should be aborted
+ id: abort_job
+ run: |
+ "status=${{ job.status }}" >> $env:GITHUB_OUTPUT
+
+ - if: success() || failure() && steps.abort_job.outputs.status == 'success'
+ name: Run functionaltests
+ run: cmake --build build --target functionaltest
+
+ - if: success() || failure() && steps.abort_job.outputs.status == 'success'
+ name: Run oldtests
+ run: |
+ # 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 src\nvim\testdir) VERBOSE=1
+ $env:PATH = $OldPath
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql.yml
index b31382af37..a11a87f93a 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql.yml
@@ -16,7 +16,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- language: [ 'cpp', 'python' ]
+ language: [ 'cpp' ]
steps:
- name: Checkout repository
@@ -26,9 +26,7 @@ jobs:
run: ./.github/workflows/env.sh
- name: Install apt packages
- run: |
- sudo apt-get update
- sudo apt-get install -y autoconf automake build-essential 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
+ run: ./.github/scripts/install_deps_ubuntu.sh
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
diff --git a/.github/workflows/coverity-scan.yml b/.github/workflows/coverity.yml
index ce7822b5c1..87e2cb1453 100644
--- a/.github/workflows/coverity-scan.yml
+++ b/.github/workflows/coverity.yml
@@ -37,7 +37,7 @@ jobs:
--form email=$EMAIL \
--form file=@cov-scan.tgz \
--form version="$(git rev-parse HEAD)" \
- --form description="Weekly GHA scan" \
+ --form description="Daily GHA scan" \
'https://scan.coverity.com/builds?project=neovim%2Fneovim'
env:
TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
diff --git a/.github/workflows/env.ps1 b/.github/workflows/env.ps1
new file mode 100644
index 0000000000..8ac267f2f9
--- /dev/null
+++ b/.github/workflows/env.ps1
@@ -0,0 +1,7 @@
+$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
+ "$name=$value" >> $env:GITHUB_ENV
+ }
+}
diff --git a/.github/workflows/env.sh b/.github/workflows/env.sh
index 061588da1a..d93552fed3 100755
--- a/.github/workflows/env.sh
+++ b/.github/workflows/env.sh
@@ -18,7 +18,6 @@ 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
EOF
DEPS_CMAKE_FLAGS=
@@ -30,7 +29,6 @@ case "$FLAVOR" in
BUILD_FLAGS="$BUILD_FLAGS -DPREFER_LUA=ON"
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:intercept_tls_get_addr=0
UBSAN_OPTIONS=print_stacktrace=1 log_path=$GITHUB_WORKSPACE/build/log/ubsan
EOF
@@ -57,7 +55,7 @@ EOF
functionaltest-lua)
BUILD_FLAGS="$BUILD_FLAGS -DPREFER_LUA=ON"
FUNCTIONALTEST=functionaltest-lua
- DEPS_CMAKE_FLAGS="$DEPS_CMAKE_FLAGS -DUSE_BUNDLED_LUAJIT=OFF -DUSE_BUNDLED_TS_PARSERS=OFF"
+ DEPS_CMAKE_FLAGS="$DEPS_CMAKE_FLAGS -DUSE_BUNDLED_LUAJIT=OFF"
;;
*)
;;
diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml
index f85f9d0cda..60689029a3 100644
--- a/.github/workflows/labeler.yml
+++ b/.github/workflows/labeler.yml
@@ -44,6 +44,7 @@ jobs:
- name: 'Request reviewers'
uses: actions/github-script@v6
with:
+ github-token: ${{ secrets.TEAM_REVIEW }}
script: |
const script = require('./.github/scripts/reviews.js')
await script({github, context})
diff --git a/.github/workflows/commitlint.yml b/.github/workflows/lintcommit.yml
index 68be5436f6..a7a227865d 100644
--- a/.github/workflows/commitlint.yml
+++ b/.github/workflows/lintcommit.yml
@@ -1,8 +1,6 @@
name: "Commit Linter"
on:
- # 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:
+ pull_request:
types: [opened, synchronize, reopened, ready_for_review]
branches:
- 'master'
@@ -10,6 +8,8 @@ jobs:
lint-commits:
runs-on: ubuntu-latest
if: github.event.pull_request.draft == false
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v3
with:
diff --git a/.github/workflows/news.yml b/.github/workflows/news.yml
new file mode 100644
index 0000000000..46ac0ec02d
--- /dev/null
+++ b/.github/workflows/news.yml
@@ -0,0 +1,31 @@
+name: "news.txt check"
+on:
+ pull_request:
+ branches:
+ - 'master'
+jobs:
+ check:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+ ref: ${{ github.event.pull_request.head.sha }}
+ - name: news.txt needs to be updated
+ run: |
+ for commit in $(git rev-list HEAD~${{ github.event.pull_request.commits }}..HEAD); do
+ message=$(git log -n1 --pretty=format:%s $commit)
+ type="$(echo "$message" | sed -E 's|([[:alpha:]]+)(\(.*\))?!?:.*|\1|')"
+ breaking="$(echo "$message" | sed -E 's|[[:alpha:]]+(\(.*\))?!:.*|breaking-change|')"
+ if [[ "$type" == "feat" ]] || [[ "$breaking" == "breaking-change" ]]; then
+ ! git diff HEAD~${{ github.event.pull_request.commits }}..HEAD --quiet runtime/doc/news.txt ||
+ {
+ echo "
+ Pull request includes a new feature or a breaking change, but
+ news.txt hasn't been updated yet. news.txt is our primary way of
+ communicating changes to users so it's important to keep it up to
+ date."
+ exit 1
+ }
+ fi
+ done
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index d6933e9330..1df33962e5 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -36,8 +36,10 @@ jobs:
id: build
run: |
CC=gcc-10 make CMAKE_BUILD_TYPE=${NVIM_BUILD_TYPE} CMAKE_EXTRA_FLAGS="-DCMAKE_INSTALL_PREFIX:PATH="
- 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)"
+ printf 'version<<END\n' >> $GITHUB_OUTPUT
+ ./build/bin/nvim --version | head -n 3 >> $GITHUB_OUTPUT
+ printf 'END\n' >> $GITHUB_OUTPUT
+ printf 'release=%s\n' "$(./build/bin/nvim --version | head -n 1)" >> $GITHUB_OUTPUT
make DESTDIR="$GITHUB_WORKSPACE/build/release/nvim-linux64" install
cd "$GITHUB_WORKSPACE/build/"
cpack -C $NVIM_BUILD_TYPE
@@ -51,14 +53,28 @@ jobs:
appimage:
runs-on: ubuntu-20.04
+ container:
+ image: ubuntu:18.04
+ options: --privileged # Privileged mode is needed to load fuse module.
steps:
+ - name: Prepare container
+ run: |
+ apt-get update
+ apt-get install -y software-properties-common
+ add-apt-repository -y ppa:ubuntu-toolchain-r/test # For gcc-10.
+ add-apt-repository -y ppa:git-core/ppa # For git>=2.18.
+ apt-get update
+ apt-get install -y git gcc-10
+ apt-get install -y fuse libfuse2 # For linuxdeploy.
+ # Workaround for https://github.com/actions/checkout/issues/766.
+ git config --global --add safe.directory "$GITHUB_WORKSPACE"
- 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 gettext libtool-bin locales ninja-build pkg-config unzip
+ apt-get update
+ apt-get install -y autoconf automake build-essential cmake 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-10 make appimage-latest
- if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name == 'nightly')
@@ -113,6 +129,11 @@ jobs:
DEPS_CMAKE_FLAGS="$OSX_FLAGS"
make DESTDIR="$GITHUB_WORKSPACE/build/release/nvim-macos" install
cd "$GITHUB_WORKSPACE/build/"
+ # Make sure we build everything for M1 as well
+ for macho in bin/* lib/nvim/parser/*.so
+ do
+ lipo -info "$macho" | grep -q arm64 || exit 1
+ done
cpack -C "$NVIM_BUILD_TYPE"
- uses: actions/upload-artifact@v3
with:
@@ -126,26 +147,27 @@ jobs:
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:
- - config: MSVC_64
- archive: nvim-win64
- name: windows (${{ matrix.config }})
+ name: windows (MSVC_64)
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
+ - name: Set env
+ run: ./.github/workflows/env.ps1
- name: Build deps
- run: .\ci\build.ps1 -BuildDeps
+ run: |
+ cmake -S cmake.deps -B $env:DEPS_BUILD_DIR -G Ninja -DCMAKE_BUILD_TYPE='RelWithDebInfo'
+ cmake --build $env:DEPS_BUILD_DIR
- name: build package
- run: .\ci\build.ps1 -Package
+ run: |
+ cmake -B build -G Ninja -DCMAKE_BUILD_TYPE='RelWithDebInfo' -DDEPS_PREFIX="$env:DEPS_PREFIX"
+ cmake --build build --target package
- uses: actions/upload-artifact@v3
with:
- name: ${{ matrix.archive }}
+ name: nvim-win64
path: |
- build/${{ matrix.archive }}.msi
- build/${{ matrix.archive }}.zip
+ build/nvim-win64.msi
+ build/nvim-win64.zip
retention-days: 1
publish:
@@ -227,16 +249,31 @@ jobs:
if [ "$TAG_NAME" != "nightly" ]; then
gh release create stable $PRERELEASE --notes-file "$RUNNER_TEMP/notes.md" --title "$SUBJECT" --target $GITHUB_SHA nvim-macos/* nvim-linux64/* appimage/* nvim-win64/*
fi
+
publish-winget:
- needs: publish # run after publish job is finished
- # publish to winget only on stable releases
- if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name != 'nightly')
- runs-on: windows-latest # action can only be run on windows
+ needs: publish
+ runs-on: windows-latest
steps:
- - uses: vedantmgoyal2009/winget-releaser@latest
+ - if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name != 'nightly')
+ name: Publish stable
+ uses: vedantmgoyal2009/winget-releaser@v1
with:
identifier: Neovim.Neovim
- # the latter one is a fallback value, reference:
- # https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-a-fallback-value
- release-tag: ${{ github.event.inputs.tag_name || github.ref }}
+ release-tag: ${{ github.event.inputs.tag_name || github.ref_name }}
+ token: ${{ secrets.WINGET_TOKEN }}
+ - if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name == 'nightly')
+ name: Get nightly version
+ id: get-version
+ run: |
+ Invoke-WebRequest https://github.com/neovim/neovim/releases/download/nightly/nvim-win64.msi -OutFile setup.msi
+ Install-Module -Name 'Carbon.Windows.Installer' -Force
+ $VERSION = (Get-CMsi (Resolve-Path .\setup.msi).Path).ProductVersion
+ "version=$VERSION" >> $env:GITHUB_OUTPUT
+ - if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name == 'nightly')
+ name: Publish nightly
+ uses: vedantmgoyal2009/winget-releaser@v1
+ with:
+ identifier: Neovim.Neovim.Nightly
+ version: ${{ steps.get-version.outputs.version }}
+ release-tag: nightly
token: ${{ secrets.WINGET_TOKEN }}
diff --git a/.github/workflows/remove-reviewers-on-draft.yml b/.github/workflows/remove-reviewers.yml
index f707f79737..7ab3ef568c 100644
--- a/.github/workflows/remove-reviewers-on-draft.yml
+++ b/.github/workflows/remove-reviewers.yml
@@ -12,6 +12,7 @@ jobs:
- name: 'Remove reviewers'
uses: actions/github-script@v6
with:
+ github-token: ${{ secrets.TEAM_REVIEW }}
script: |
const script = require('./.github/scripts/remove-reviewers.js')
await script({github, context})
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
new file mode 100644
index 0000000000..c1d3ee3ff3
--- /dev/null
+++ b/.github/workflows/stale.yml
@@ -0,0 +1,42 @@
+name: 'Close stale issues and PRs'
+on:
+ schedule:
+ - cron: '30 1 * * *' # Run every day at 01:30
+ workflow_dispatch:
+ issue_comment:
+
+jobs:
+ close:
+ if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
+ runs-on: ubuntu-latest
+ permissions:
+ issues: write
+ pull-requests: write
+ steps:
+ - uses: actions/stale@v7
+ with:
+ days-before-close: 30
+ days-before-stale: -1
+ stale-issue-label: needs:response
+ stale-pr-label: needs:response
+ remove-stale-when-updated: false
+ close-issue-message: "This issue has been closed since a request for
+ information has not been answered for 30 days. It can be reopened
+ when the requested information is provided."
+ close-pr-message: "This PR has been closed since a request for
+ changes has not been answered for 30 days. It can be reopened when
+ the requested changes are provided."
+
+ remove-label:
+ if: github.event_name == 'issue_comment'
+ runs-on: ubuntu-latest
+ permissions:
+ issues: write
+ pull-requests: write
+ steps:
+ - uses: actions/checkout@v3
+ - uses: actions/github-script@v6
+ with:
+ script: |
+ const script = require('./.github/scripts/unstale.js')
+ await script({github, context})
diff --git a/.github/workflows/vim-patches.yml b/.github/workflows/vim-patches.yml
index df8c8116b5..159eb09e7c 100644
--- a/.github/workflows/vim-patches.yml
+++ b/.github/workflows/vim-patches.yml
@@ -2,6 +2,7 @@ name: vim-patches
on:
schedule:
- cron: '3 3 * * *'
+ workflow_dispatch:
jobs:
update-vim-patches:
@@ -24,6 +25,8 @@ jobs:
path: ${{ env.VIM_SOURCE_DIR }}
fetch-depth: 0
+ - run: sudo apt-get install libfuse2
+
- run: |
gh release download -R neovim/neovim -p nvim.appimage
chmod a+x nvim.appimage
@@ -40,8 +43,8 @@ jobs:
id: update-version
run: |
git checkout -b ${VERSION_BRANCH}
- nvim -i NONE -u NONE --headless +'luafile scripts/vimpatch.lua' +q
- printf '::set-output name=NEW_PATCHES::%s\n' $([ -z "$(git diff)" ]; echo $?)
+ nvim -V1 -es -i NONE +'luafile scripts/vimpatch.lua' +q
+ printf 'NEW_PATCHES=%s\n' $([ -z "$(git diff)" ]; echo $?) >> $GITHUB_OUTPUT
- name: Automatic PR
if: ${{ steps.update-version.outputs.NEW_PATCHES != 0 }}
diff --git a/.gitignore b/.gitignore
index 11479346c7..57acde8722 100644
--- a/.gitignore
+++ b/.gitignore
@@ -73,3 +73,11 @@ tags
# vim patches
/vim-*.patch
+
+# nix build results
+/result
+/result-*
+/contrib/result
+/contrib/result-*
+
+CMakeUserPresets.json
diff --git a/.luacheckrc b/.luacheckrc
index 9bbd323e84..8ef4f5ea66 100644
--- a/.luacheckrc
+++ b/.luacheckrc
@@ -19,6 +19,8 @@ cache = true
ignore = {
"631", -- max_line_length
"212/_.*", -- unused argument, for vars with "_" prefix
+ "121", -- setting read-only global variable 'vim'
+ "122", -- setting read-only field of global variable 'vim'
}
-- Global objects defined by the C code
@@ -28,6 +30,13 @@ read_globals = {
globals = {
"vim.g",
+ "vim.b",
+ "vim.w",
+ "vim.o",
+ "vim.bo",
+ "vim.wo",
+ "vim.go",
+ "vim.env"
}
exclude_files = {
diff --git a/.mailmap b/.mailmap
index 7e154b4644..ffef9f280b 100644
--- a/.mailmap
+++ b/.mailmap
@@ -15,6 +15,8 @@ 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>
+dundargoc <gocdundar@gmail.com> <33953936+dundargoc@users.noreply.github.com>
+dundargoc <gocdundar@gmail.com> Dundar Goc
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
@@ -52,6 +54,7 @@ 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
+Lewis Russell <lewis6991@gmail.com> <me@lewisr.dev>
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>
@@ -92,7 +95,7 @@ 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>
+Thomas Vigouroux <thomas.vigouroux@protonmail.com> <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
diff --git a/CMakeLists.txt b/CMakeLists.txt
index caf9658699..493c212996 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,17 +1,18 @@
# CMAKE REFERENCE
# intro: https://codingnest.com/basic-cmake/
# best practices (3.0+): https://gist.github.com/mbinna/c61dbb39bca0e4fb7d1f73b0d66a4fd1
+# pitfalls: https://izzys.casa/2019/02/everything-you-never-wanted-to-know-about-cmake/
# Version should match the tested CMAKE_URL in .github/workflows/ci.yml.
cmake_minimum_required(VERSION 3.10)
-project(nvim C)
-if(POLICY CMP0065)
- cmake_policy(SET CMP0065 NEW)
-endif()
-if(POLICY CMP0060)
- cmake_policy(SET CMP0060 NEW)
+# Can be removed once minimum version is at least 3.15
+if(POLICY CMP0092)
+ cmake_policy(SET CMP0092 NEW)
endif()
+
+project(nvim C)
+
if(POLICY CMP0075)
cmake_policy(SET CMP0075 NEW)
endif()
@@ -19,7 +20,10 @@ 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(CheckCCompilerFlag)
+include(CheckCSourceCompiles)
+include(InstallHelpers)
+include(LuaHelpers) # Find Lua interpreter
include(PreventInTreeBuilds)
include(Util)
@@ -77,18 +81,7 @@ 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)
-
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
- # CMake tries to treat /sw and /opt/local as extension of the system path, but
- # that doesn't really work out very well. Once you have a dependency that
- # resides there and have to add it as an include directory, then any other
- # dependency that could be satisfied from there must be--otherwise you can end
- # up with conflicting versions. So, let's make them more of a priority having
- # them be included as one of the first places to look for dependencies.
- list(APPEND CMAKE_PREFIX_PATH /sw /opt/local)
-
# If the macOS deployment target is not set manually (via $MACOSX_DEPLOYMENT_TARGET),
# fall back to local system version. Needs to be done both here and in cmake.deps.
if(NOT CMAKE_OSX_DEPLOYMENT_TARGET)
@@ -97,14 +90,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
OUTPUT_STRIP_TRAILING_WHITESPACE)
set(CMAKE_OSX_DEPLOYMENT_TARGET "${MACOS_VERSION}")
endif()
- message("Using deployment target ${CMAKE_OSX_DEPLOYMENT_TARGET}")
-
- # Work around some old, broken detection by CMake for knowing when to use the
- # isystem flag. Apple's compilers have supported this for quite some time
- # now.
- if(CMAKE_COMPILER_IS_GNUCC)
- set(CMAKE_INCLUDE_SYSTEM_FLAG_C "-isystem ")
- endif()
+ message(STATUS "Using deployment target ${CMAKE_OSX_DEPLOYMENT_TARGET}")
endif()
if(WIN32 OR CMAKE_SYSTEM_NAME STREQUAL "Darwin")
@@ -114,8 +100,6 @@ if(WIN32 OR CMAKE_SYSTEM_NAME STREQUAL "Darwin")
set(USE_FNAME_CASE TRUE)
endif()
-option(ENABLE_LIBINTL "enable libintl" ON)
-option(ENABLE_LIBICONV "enable libiconv" ON)
if (MINGW)
# Disable LTO by default as it may not compile
# See https://github.com/Alexpux/MINGW-packages/issues/3516
@@ -127,40 +111,28 @@ endif()
message(STATUS "CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}")
-# Build type.
-if(NOT CMAKE_BUILD_TYPE)
- message(STATUS "CMAKE_BUILD_TYPE not specified, default is 'Debug'")
- set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build" FORCE)
-else()
- message(STATUS "CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
-endif()
+set_default_buildtype()
if(CMAKE_BUILD_TYPE MATCHES Debug)
set(DEBUG 1)
else()
set(DEBUG 0)
endif()
-# Set available build types for CMake GUIs.
-# Other build types can still be set by -DCMAKE_BUILD_TYPE=...
-set_property(CACHE CMAKE_BUILD_TYPE PROPERTY
- STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
# 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 8)
+set(NVIM_VERSION_MINOR 9)
set(NVIM_VERSION_PATCH 0)
set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers
# API level
-set(NVIM_API_LEVEL 10) # Bump this after any API change.
+set(NVIM_API_LEVEL 11) # Bump this after any API change.
set(NVIM_API_LEVEL_COMPAT 0) # Adjust this after a _breaking_ API change.
set(NVIM_API_PRERELEASE true)
set(NVIM_VERSION_BUILD_TYPE "${CMAKE_BUILD_TYPE}")
# NVIM_VERSION_CFLAGS set further below.
-set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
-
# Log level (MIN_LOG_LEVEL in log.h)
if("${MIN_LOG_LEVEL}" MATCHES "^$")
# Minimize logging for release-type builds.
@@ -186,7 +158,7 @@ if(CMAKE_C_FLAGS_RELEASE MATCHES "-O3")
string(REPLACE "-O3" "-O2" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
endif()
-if(CMAKE_COMPILER_IS_GNUCC)
+if(CMAKE_C_COMPILER_ID MATCHES "GNU")
check_c_compiler_flag(-Og HAS_OG_FLAG)
else()
set(HAS_OG_FLAG 0)
@@ -206,7 +178,6 @@ endif()
# gcc 4.0+ sets _FORTIFY_SOURCE=2 automatically. This currently
# does not work with Neovim due to some uses of dynamically-sized structures.
# https://github.com/neovim/neovim/issues/223
-include(CheckCSourceCompiles)
# Include the build type's default flags in the check for _FORTIFY_SOURCE,
# otherwise we may incorrectly identify the level as acceptable and find out
@@ -218,308 +189,8 @@ if(${INIT_FLAGS_NAME})
set(CMAKE_REQUIRED_FLAGS "${${INIT_FLAGS_NAME}}")
endif()
-# Include <string.h> because some toolchains define _FORTIFY_SOURCE=2 in
-# internal header files, which should in turn be #included by <string.h>.
-check_c_source_compiles("
-#include <string.h>
-
-#if defined(_FORTIFY_SOURCE) && _FORTIFY_SOURCE > 1
-#error \"_FORTIFY_SOURCE > 1\"
-#endif
-int
-main(void)
-{
- return 0;
-}
-" HAS_ACCEPTABLE_FORTIFY)
-
-if(NOT HAS_ACCEPTABLE_FORTIFY)
- message(STATUS "Unsupported _FORTIFY_SOURCE found, forcing _FORTIFY_SOURCE=1")
- # Extract possible prefix to _FORTIFY_SOURCE (e.g. -Wp,-D_FORTIFY_SOURCE).
- STRING(REGEX MATCH "[^\ ]+-D_FORTIFY_SOURCE" _FORTIFY_SOURCE_PREFIX "${CMAKE_C_FLAGS}")
- STRING(REPLACE "-D_FORTIFY_SOURCE" "" _FORTIFY_SOURCE_PREFIX "${_FORTIFY_SOURCE_PREFIX}" )
- if(NOT _FORTIFY_SOURCE_PREFIX STREQUAL "")
- message(STATUS "Detected _FORTIFY_SOURCE Prefix=${_FORTIFY_SOURCE_PREFIX}")
- endif()
- # -U in add_definitions doesn't end up in the correct spot, so we add it to
- # the flags variable instead.
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${_FORTIFY_SOURCE_PREFIX}-U_FORTIFY_SOURCE ${_FORTIFY_SOURCE_PREFIX}-D_FORTIFY_SOURCE=1")
-endif()
-
-# Remove --sort-common from linker flags, as this seems to cause bugs (see #2641, #3374).
-# TODO: Figure out the root cause.
-if(CMAKE_EXE_LINKER_FLAGS MATCHES "--sort-common" OR
- CMAKE_SHARED_LINKER_FLAGS MATCHES "--sort-common" OR
- CMAKE_MODULE_LINKER_FLAGS MATCHES "--sort-common")
- message(STATUS "Removing --sort-common from linker flags")
- string(REGEX REPLACE ",--sort-common(=[^,]+)?" "" CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
- string(REGEX REPLACE ",--sort-common(=[^,]+)?" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}")
- string(REGEX REPLACE ",--sort-common(=[^,]+)?" "" CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS}")
-
- # If no linker flags remain for a -Wl argument, remove it.
- # '-Wl$' will match LDFLAGS="-Wl,--sort-common",
- # '-Wl ' will match LDFLAGS="-Wl,--sort-common -Wl,..."
- string(REGEX REPLACE "-Wl($| )" "" CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
- string(REGEX REPLACE "-Wl($| )" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}")
- string(REGEX REPLACE "-Wl($| )" "" CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS}")
-endif()
-
-check_c_source_compiles("
-#include <execinfo.h>
-int main(void)
-{
- void *trace[1];
- backtrace(trace, 1);
- return 0;
-}
-" HAVE_EXECINFO_BACKTRACE)
-
-check_c_source_compiles("
-int main(void)
-{
- int a = 42;
- __builtin_add_overflow(a, a, &a);
- __builtin_sub_overflow(a, a, &a);
- return 0;
-}
-" 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)
- add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE)
- add_definitions(-DWIN32)
-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)
- if(HAVE_WIMPLICIT_FALLTHROUGH_FLAG)
- 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")
- add_compile_options(-Wno-c11-extensions)
- endif()
-endif()
-
-if(MINGW)
- # Use POSIX compatible stdio in Mingw
- add_definitions(-D__USE_MINGW_ANSI_STDIO)
-endif()
-if(WIN32)
- # Windows Vista is the minimum supported version
- add_definitions(-D_WIN32_WINNT=0x0600)
-endif()
-
-# OpenBSD's GCC (4.2.1) doesn't have -Wvla
-check_c_compiler_flag(-Wvla HAS_WVLA_FLAG)
-if(HAS_WVLA_FLAG)
- add_compile_options(-Wvla)
-endif()
-
-if(UNIX)
- # -fstack-protector breaks non Unix builds even in Mingw-w64
- check_c_compiler_flag(-fstack-protector-strong HAS_FSTACK_PROTECTOR_STRONG_FLAG)
- check_c_compiler_flag(-fstack-protector HAS_FSTACK_PROTECTOR_FLAG)
-
- if(HAS_FSTACK_PROTECTOR_STRONG_FLAG)
- add_compile_options(-fstack-protector-strong)
- link_libraries(-fstack-protector-strong)
- elseif(HAS_FSTACK_PROTECTOR_FLAG)
- add_compile_options(-fstack-protector --param ssp-buffer-size=4)
- link_libraries(-fstack-protector --param ssp-buffer-size=4)
- endif()
-endif()
-
-check_c_compiler_flag(-fno-common HAVE_FNO_COMMON)
-if (HAVE_FNO_COMMON)
- add_compile_options(-fno-common)
-endif()
-
-check_c_compiler_flag(-fdiagnostics-color=auto HAS_DIAG_COLOR_FLAG)
-if(HAS_DIAG_COLOR_FLAG)
- if(CMAKE_GENERATOR MATCHES "Ninja")
- add_compile_options(-fdiagnostics-color=always)
- else()
- add_compile_options(-fdiagnostics-color=auto)
- endif()
-endif()
-
-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_UCHAR})
- # Get some test coverage for unsigned char
- add_compile_options(-funsigned-char)
- endif()
-endif()
-
option(LOG_LIST_ACTIONS "Add list actions logging" OFF)
-add_definitions(-DINCLUDE_GENERATED_DECLARATIONS)
-
-if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
- if(CMAKE_SYSTEM_NAME STREQUAL "SunOS")
- set(NO_UNDEFINED "-Wl,--no-undefined -lsocket")
- elseif(NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin")
- set(NO_UNDEFINED "-Wl,--no-undefined")
- endif()
- set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${NO_UNDEFINED}")
- set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${NO_UNDEFINED}")
-
- # For O_CLOEXEC, O_DIRECTORY, and O_NOFOLLOW flags on older systems
- # (pre POSIX.1-2008: glibc 2.11 and earlier). #4042
- # For ptsname(). #6743
- add_definitions(-D_GNU_SOURCE)
-endif()
-
-if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SIZEOF_VOID_P EQUAL 8 AND NOT PREFER_LUA AND LUAJIT_VERSION LESS "2.1.0-beta3")
- # Required for luajit < 2.1.0-beta3.
- set(CMAKE_EXE_LINKER_FLAGS
- "${CMAKE_EXE_LINKER_FLAGS} -pagezero_size 10000 -image_base 100000000")
- set(CMAKE_SHARED_LINKER_FLAGS
- "${CMAKE_SHARED_LINKER_FLAGS} -image_base 100000000")
- set(CMAKE_MODULE_LINKER_FLAGS
- "${CMAKE_MODULE_LINKER_FLAGS} -image_base 100000000")
-endif()
-
-include_directories("${PROJECT_BINARY_DIR}/cmake.config")
-include_directories("${PROJECT_SOURCE_DIR}/src")
-
-find_package(LibUV 1.28.0 REQUIRED)
-include_directories(SYSTEM ${LIBUV_INCLUDE_DIRS})
-
-find_package(Msgpack 1.0.0 REQUIRED)
-include_directories(SYSTEM ${MSGPACK_INCLUDE_DIRS})
-
-find_package(LibLUV 1.43.0 REQUIRED)
-include_directories(SYSTEM ${LIBLUV_INCLUDE_DIRS})
-
-find_package(TreeSitter REQUIRED)
-include_directories(SYSTEM ${TreeSitter_INCLUDE_DIRS})
-
-list(APPEND CMAKE_REQUIRED_INCLUDES "${TreeSitter_INCLUDE_DIRS}")
-list(APPEND CMAKE_REQUIRED_LIBRARIES "${TreeSitter_LIBRARIES}")
-check_c_source_compiles("
-#include <tree_sitter/api.h>
-int
-main(void)
-{
- TSQueryCursor *cursor = ts_query_cursor_new();
- ts_query_cursor_set_match_limit(cursor, 32);
- return 0;
-}
-" TS_HAS_SET_MATCH_LIMIT)
-if(TS_HAS_SET_MATCH_LIMIT)
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNVIM_TS_HAS_SET_MATCH_LIMIT")
-endif()
-check_c_source_compiles("
-#include <stdlib.h>
-#include <tree_sitter/api.h>
-int
-main(void)
-{
- ts_set_allocator(malloc, calloc, realloc, free);
- return 0;
-}
-" TS_HAS_SET_ALLOCATOR)
-if(TS_HAS_SET_ALLOCATOR)
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNVIM_TS_HAS_SET_ALLOCATOR")
-endif()
-
-# 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 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.
- find_package(LuaJit)
-else()
- find_package(LuaJit REQUIRED)
- set(LUA_PREFERRED_INCLUDE_DIRS ${LUAJIT_INCLUDE_DIRS})
- set(LUA_PREFERRED_LIBRARIES ${LUAJIT_LIBRARIES})
-endif()
-
-list(APPEND CMAKE_REQUIRED_INCLUDES "${MSGPACK_INCLUDE_DIRS}")
-check_c_source_compiles("
-#include <msgpack.h>
-
-int
-main(void)
-{
- return MSGPACK_OBJECT_FLOAT32;
-}
-" MSGPACK_HAS_FLOAT32)
-list(REMOVE_ITEM CMAKE_REQUIRED_INCLUDES "${MSGPACK_INCLUDE_DIRS}")
-if(MSGPACK_HAS_FLOAT32)
- add_definitions(-DNVIM_MSGPACK_HAS_FLOAT32)
-endif()
-
-option(FEAT_TUI "Enable the Terminal UI" ON)
-
-if(FEAT_TUI)
- find_package(UNIBILIUM 2.0 REQUIRED)
- include_directories(SYSTEM ${UNIBILIUM_INCLUDE_DIRS})
-
- list(APPEND CMAKE_REQUIRED_INCLUDES "${UNIBILIUM_INCLUDE_DIRS}")
- list(APPEND CMAKE_REQUIRED_LIBRARIES "${UNIBILIUM_LIBRARIES}")
- check_c_source_compiles("
- #include <unibilium.h>
-
- int
- main(void)
- {
- return unibi_num_from_var(unibi_var_from_num(0));
- }
- " UNIBI_HAS_VAR_FROM)
- list(REMOVE_ITEM CMAKE_REQUIRED_INCLUDES "${UNIBILIUM_INCLUDE_DIRS}")
- list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES "${UNIBILIUM_LIBRARIES}")
- if(UNIBI_HAS_VAR_FROM)
- add_definitions(-DNVIM_UNIBI_HAS_VAR_FROM)
- endif()
-
- find_package(LibTermkey 0.18 REQUIRED)
- include_directories(SYSTEM ${LIBTERMKEY_INCLUDE_DIRS})
-endif()
-
-find_package(LIBVTERM 0.1 REQUIRED)
-include_directories(SYSTEM ${LIBVTERM_INCLUDE_DIRS})
-
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)
@@ -534,22 +205,6 @@ if((CLANG_ASAN_UBSAN OR CLANG_MSAN OR CLANG_TSAN) AND NOT CMAKE_C_COMPILER_ID MA
message(FATAL_ERROR "Sanitizers are only supported for Clang")
endif()
-if(ENABLE_LIBICONV)
- find_package(Iconv REQUIRED)
- include_directories(SYSTEM ${Iconv_INCLUDE_DIRS})
-endif()
-
-if(ENABLE_LIBINTL)
- # LibIntl (not Intl) selects our FindLibIntl.cmake script. #8464
- find_package(LibIntl REQUIRED)
- include_directories(SYSTEM ${LibIntl_INCLUDE_DIRS})
-endif()
-
-# Determine platform's threading library. Set CMAKE_THREAD_PREFER_PTHREAD
-# explicitly to indicate a strong preference for pthread.
-set(CMAKE_THREAD_PREFER_PTHREAD ON)
-find_package(Threads REQUIRED)
-
# Place targets in bin/ or lib/ for all build configurations
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
@@ -561,8 +216,6 @@ foreach(CFGNAME ${CMAKE_CONFIGURATION_TYPES})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${CFGNAME} ${CMAKE_BINARY_DIR}/lib)
endforeach()
-# Find Lua interpreter
-include(LuaHelpers)
set(LUA_DEPENDENCIES lpeg mpack bit)
if(NOT LUA_PRG)
foreach(CURRENT_LUA_PRG luajit lua5.1 lua5.2 lua)
@@ -589,6 +242,17 @@ endif()
message(STATUS "Using Lua interpreter: ${LUA_PRG}")
+# Some of the code generation still relies on stable table ordering in order to
+# produce reproducible output - specifically the msgpack'ed data in
+# funcs_metadata.generated.h and ui_events_metadata.generated.h. This should
+# ideally be fixed in the generators, but until then as a workaround you may provide
+# a specific lua implementation that provides the needed stability by setting LUA_GEN_PRG:
+if(NOT LUA_GEN_PRG)
+ set(LUA_GEN_PRG "${LUA_PRG}" CACHE FILEPATH "Path to the lua used for code generation.")
+endif()
+
+message(STATUS "Using Lua interpreter for code generation: ${LUA_GEN_PRG}")
+
option(COMPILE_LUA "Pre-compile Lua sources into bytecode (for sources that are included in the binary)" ON)
if(COMPILE_LUA AND NOT WIN32)
@@ -612,59 +276,45 @@ if(LUAC_PRG)
message(STATUS "Using Lua compiler: ${LUAC_PRG}")
endif()
-# Setup busted.
-find_program(BUSTED_PRG NAMES busted busted.bat)
-find_program(BUSTED_LUA_PRG busted-lua)
-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(UNCRUSTIFY_PRG uncrustify)
find_program(SHELLCHECK_PRG shellcheck)
-add_glob_targets(
+add_glob_target(
REQUIRED
TARGET lintlua-luacheck
COMMAND ${LUACHECK_PRG}
FLAGS -q
GLOB_DIRS runtime/ scripts/ src/ test/
GLOB_PAT *.lua
- TOUCH_STRATEGY SINGLE
- )
+ TOUCH_STRATEGY SINGLE)
-add_glob_targets(
+add_glob_target(
TARGET lintlua-stylua
COMMAND ${STYLUA_PRG}
FLAGS --color=always --check
GLOB_DIRS runtime/
GLOB_PAT *.lua
- TOUCH_STRATEGY SINGLE
- )
+ 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(
+add_glob_target(
TARGET lintsh
COMMAND ${SHELLCHECK_PRG}
- FILES scripts/vim-patch.sh
- TOUCH_STRATEGY SINGLE
- )
+ FLAGS -x -a
+ GLOB_DIRS scripts ci
+ GLOB_PAT *.sh
+ EXCLUDE
+ scripts/pvscheck.sh
+ ci/common
+ ci/snap
+ TOUCH_STRATEGY SINGLE)
add_custom_target(lintcommit
COMMAND ${PROJECT_BINARY_DIR}/bin/nvim -u NONE -es -c [[lua require('scripts.lintcommit').main({trace=false})]]
@@ -673,7 +323,7 @@ add_custom_target(lintcommit
add_dependencies(lintcommit nvim)
add_custom_target(lint)
-add_dependencies(lint check-single-includes lintc lintlua lintpy lintsh lintcommit lintuncrustify)
+add_dependencies(lint check-single-includes lintc lintlua lintsh lintcommit)
#
# Format
@@ -698,7 +348,6 @@ 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(cmake.config)
add_subdirectory(test/functional/fixtures) # compile test programs
add_subdirectory(runtime)
@@ -709,19 +358,19 @@ if(WIN32)
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/nvim-qt/runtime/plugin)
endif()
+# Setup busted.
+find_program(BUSTED_PRG NAMES busted busted.bat)
+find_program(BUSTED_LUA_PRG busted-lua)
+if(NOT BUSTED_OUTPUT_TYPE)
+ set(BUSTED_OUTPUT_TYPE "nvim")
+endif()
+
# Setup some test-related bits. We do this after going down the tree because we
# need some of the targets.
if(BUSTED_PRG)
- get_property(TEST_INCLUDE_DIRS DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
- PROPERTY INCLUDE_DIRECTORIES)
-
- # When running tests from 'ninja' we need to use the
- # console pool: to do so we need to use the USES_TERMINAL
- # option, but this is only available in CMake 3.2
- set(TEST_TARGET_ARGS)
- list(APPEND TEST_TARGET_ARGS "USES_TERMINAL")
+ get_target_property(TEST_INCLUDE_DIRS main_lib INTERFACE_INCLUDE_DIRECTORIES)
- set(UNITTEST_PREREQS nvim-test unittest-headers)
+ set(UNITTEST_PREREQS nvim-test)
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)
@@ -736,9 +385,10 @@ if(BUSTED_PRG)
-DTEST_DIR=${CMAKE_CURRENT_SOURCE_DIR}/test
-DBUILD_DIR=${CMAKE_BINARY_DIR}
-DTEST_TYPE=unit
+ -DCIRRUS_CI=$ENV{CIRRUS_CI}
-P ${PROJECT_SOURCE_DIR}/cmake/RunTests.cmake
DEPENDS ${UNITTEST_PREREQS}
- ${TEST_TARGET_ARGS})
+ USES_TERMINAL)
set_target_properties(unittest PROPERTIES FOLDER test)
else()
message(WARNING "disabling unit tests: no Luajit FFI in ${LUA_PRG}")
@@ -766,9 +416,10 @@ if(BUSTED_PRG)
-DTEST_DIR=${CMAKE_CURRENT_SOURCE_DIR}/test
-DBUILD_DIR=${CMAKE_BINARY_DIR}
-DTEST_TYPE=functional
+ -DCIRRUS_CI=$ENV{CIRRUS_CI}
-P ${PROJECT_SOURCE_DIR}/cmake/RunTests.cmake
DEPENDS ${FUNCTIONALTEST_PREREQS}
- ${TEST_TARGET_ARGS})
+ USES_TERMINAL)
set_target_properties(functionaltest PROPERTIES FOLDER test)
add_custom_target(benchmark
@@ -781,9 +432,10 @@ if(BUSTED_PRG)
-DTEST_DIR=${CMAKE_CURRENT_SOURCE_DIR}/test
-DBUILD_DIR=${CMAKE_BINARY_DIR}
-DTEST_TYPE=benchmark
+ -DCIRRUS_CI=$ENV{CIRRUS_CI}
-P ${PROJECT_SOURCE_DIR}/cmake/RunTests.cmake
DEPENDS ${BENCHMARK_PREREQS}
- ${TEST_TARGET_ARGS})
+ USES_TERMINAL)
set_target_properties(benchmark PROPERTIES FOLDER test)
endif()
@@ -798,9 +450,10 @@ if(BUSTED_LUA_PRG)
-DTEST_DIR=${CMAKE_CURRENT_SOURCE_DIR}/test
-DBUILD_DIR=${CMAKE_BINARY_DIR}
-DTEST_TYPE=functional
+ -DCIRRUS_CI=$ENV{CIRRUS_CI}
-P ${PROJECT_SOURCE_DIR}/cmake/RunTests.cmake
DEPENDS ${FUNCTIONALTEST_PREREQS}
- ${TEST_TARGET_ARGS})
+ USES_TERMINAL)
set_target_properties(functionaltest-lua PROPERTIES FOLDER test)
endif()
diff --git a/CMakePresets.json b/CMakePresets.json
new file mode 100644
index 0000000000..62abb2697f
--- /dev/null
+++ b/CMakePresets.json
@@ -0,0 +1,113 @@
+{
+ "version": 6,
+ "configurePresets": [
+ {
+ "name": "base",
+ "generator": "Ninja",
+ "binaryDir": "${sourceDir}/build",
+ "hidden": true
+ },
+ {
+ "name": "default",
+ "displayName": "RelWithDebInfo",
+ "description": "Enables optimizations (-Og or -O2) with debug information",
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "RelWithDebInfo"
+ },
+ "inherits": ["base"]
+ },
+ {
+ "name": "debug",
+ "displayName": "Debug",
+ "description": "Disables optimizations (-O0), enables debug information",
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "Debug"
+ },
+ "inherits": ["base"]
+ },
+ {
+ "name": "release",
+ "displayName": "Release",
+ "description": "Same as RelWithDebInfo, but disables debug information",
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "Release"
+ },
+ "inherits": ["base"]
+ },
+ {
+ "name": "windows-default",
+ "displayName": "Windows x64 RelWithDebInfo",
+ "description": "Sets Ninja generator, enables optimizations with debug information for x64",
+ "generator": "Ninja",
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "RelWithDebInfo"
+ },
+ "architecture": {
+ "value": "x64",
+ "strategy": "external"
+ },
+ "vendor": {
+ "microsoft.com/VisualStudioSettings/CMake/1.0": {
+ "hostOS": ["Windows"]
+ }
+ },
+ "condition": {
+ "type": "equals",
+ "lhs": "${hostSystemName}",
+ "rhs": "Windows"
+ },
+ "inherits": ["base"]
+ },
+ {
+ "name": "iwyu",
+ "displayName": "IWYU",
+ "description": "Run include-what-you-use with the compiler",
+ "cacheVariables": {
+ "ENABLE_IWYU": "ON"
+ },
+ "inherits": ["base"]
+ }
+ ],
+ "buildPresets": [
+ {
+ "name": "default",
+ "configurePreset": "default"
+ },
+ {
+ "name": "debug",
+ "configurePreset": "debug"
+ },
+ {
+ "name": "release",
+ "configurePreset": "release"
+ },
+ {
+ "name": "windows-default",
+ "configurePreset": "windows-default",
+ "condition": {
+ "type": "equals",
+ "lhs": "${hostSystemName}",
+ "rhs": "Windows"
+ }
+ },
+ {
+ "name": "iwyu",
+ "configurePreset": "iwyu"
+ }
+ ],
+ "workflowPresets": [
+ {
+ "name": "iwyu",
+ "steps": [
+ {
+ "type": "configure",
+ "name": "iwyu"
+ },
+ {
+ "type": "build",
+ "name": "iwyu"
+ }
+ ]
+ }
+ ]
+}
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 17622fa33a..ce0f2ace05 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -10,8 +10,11 @@ low-risk/isolated tasks:
- 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)
+- [Improve documentation](#documenting)
+- [Merge a Vim patch] (requires strong familiarity with Vim)
+ - NOTE: read the above link before sending improvements to "runtime files" (anything in `runtime/`).
+ - Vimscript and documentation files are (mostly) maintained by [Vim](https://github.com/vim/vim), not Nvim.
+ - Lua files are maintained by Nvim.
Reporting problems
------------------
@@ -30,9 +33,9 @@ Reporting problems
Developer guidelines
--------------------
-- Read `:help dev` if you are working on Nvim core.
-- Read `:help dev-ui` if you are developing a UI.
-- Read `:help dev-api-client` if you are developing an API client.
+- Read [:help dev](https://neovim.io/doc/user/develop.html#dev) if you are working on Nvim core.
+- Read [:help dev-ui](https://neovim.io/doc/user/develop.html#dev-ui) if you are developing a UI.
+- Read [:help dev-api-client](https://neovim.io/doc/user/develop.html#dev-api-client) if you are developing an API client.
- Install `ninja` for faster builds of Nvim.
```
sudo apt-get install ninja-build
@@ -47,21 +50,19 @@ Pull requests (PRs)
- Your PR must include [test coverage][run-tests].
- Avoid cosmetic changes to unrelated files in the same commit.
- Use a [feature branch][git-feature-branch] instead of the master branch.
-- Use a **rebase workflow** for small PRs.
- - After addressing review comments, it's fine to rebase and force-push.
-- Use a **merge workflow** for big, high-risk PRs.
+- Use a _rebase workflow_ for small PRs.
+ - After addressing review comments, it's fine to force-push.
+- Use a _merge workflow_ (as opposed to "rebase") for big, high-risk PRs.
- Merge `master` into your PR when there are conflicts or when master
introduces breaking changes.
- - Use the `ri` git alias:
- ```
- [alias]
- ri = "!sh -c 't=\"${1:-master}\"; s=\"${2:-HEAD}\"; mb=\"$(git merge-base \"$t\" \"$s\")\"; if test \"x$mb\" = x ; then o=\"$t\"; else lm=\"$(git log -n1 --merges \"$t..$s\" --pretty=%H)\"; if test \"x$lm\" = x ; then o=\"$mb\"; else o=\"$lm\"; fi; fi; test $# -gt 0 && shift; test $# -gt 0 && shift; git rebase --interactive \"$o\" \"$@\"'"
- ```
- This avoids unnecessary rebases yet still allows you to combine related
- commits, separate monolithic commits, etc.
- Do not edit commits that come before the merge commit.
-- During a squash/fixup, use `exec make -C build unittest` between each
- pick/edit/reword.
+
+### Merging to master
+
+For maintainers: when a PR is ready to merge to master,
+
+- prefer _Squash Merge_ for "single-commit PRs" (when the PR has only one meaningful commit).
+- prefer _Merge_ for "multi-commit PRs" (when the PR has multiple meaningful commits).
### Stages: Draft and Ready for review
@@ -111,34 +112,20 @@ the VCS/git logs more valuable. The general structure of a commit message is:
### Automated builds (CI)
-Each pull request must pass the automated builds on [sourcehut] and [GitHub Actions].
+Each pull request must pass the automated builds on [Cirrus CI] and [GitHub Actions].
- CI builds are compiled with [`-Werror`][gcc-warnings], so compiler warnings
will fail the build.
-- If any tests fail, the build will fail.
- See [test/README.md#running-tests][run-tests] to run tests locally.
- Passing locally doesn't guarantee passing the CI build, because of the
- different compilers and platforms tested against.
+- If any tests fail, the build will fail. See [test/README.md#running-tests][run-tests] to run tests locally.
- CI runs [ASan] and other analyzers.
- To run valgrind locally: `VALGRIND=1 make test`
- To run Clang ASan/UBSan locally: `CC=clang make CMAKE_FLAGS="-DCLANG_ASAN_UBSAN=ON"`
- The [lint](#lint) build checks modified lines _and their immediate
neighbors_, to encourage incrementally updating the legacy style to meet our
[style](#style). (See [#3174][3174] for background.)
-- CI for freebsd and openbsd runs on [sourcehut].
- - To get a backtrace on freebsd (after connecting via ssh):
- ```sh
- sudo pkg install tmux # If you want tmux.
- lldb build/bin/nvim -c nvim.core
-
- # To get a full backtrace:
- # 1. Rebuild with debug info.
- rm -rf nvim.core build
- gmake CMAKE_BUILD_TYPE=RelWithDebInfo CMAKE_EXTRA_FLAGS="-DCI_BUILD=ON -DMIN_LOG_LEVEL=3" nvim
- # 2. Run the failing test to generate a new core file.
- TEST_FILE=test/functional/foo.lua gmake functionaltest
- lldb build/bin/nvim -c nvim.core
- ```
+- CI for FreeBSD runs on [Cirrus CI].
+- To see CI results faster in your PR, you can temporarily set `TEST_FILE` in
+ [ci.yml](https://github.com/neovim/neovim/blob/e35b9020b16985eee26e942f9a3f6b045bc3809b/.github/workflows/ci.yml#L205).
### Clang scan-build
@@ -219,10 +206,10 @@ You can lint a single file (but this will _not_ exclude legacy errors):
### Style
- You can format files by using:
-```
- make format
-```
-This will format changed Lua and C files with all appropriate flags set.
+ ```
+ make format
+ ```
+ This will format changed Lua and C files with all appropriate flags set.
- Style rules are (mostly) defined by `src/uncrustify.cfg` which tries to match
the [style-guide]. To use the Nvim `gq` command with `uncrustify`:
```
@@ -245,15 +232,66 @@ This will format changed Lua and C files with all appropriate flags set.
```
git config blame.ignoreRevsFile .git-blame-ignore-revs
```
-- Use **[universal-ctags](https://github.com/universal-ctags/ctags).**
- ("Exuberant ctags", the typical `ctags` binary provided by your distro, is
- unmaintained and won't recognize many function signatures in Neovim source.)
+
+- Recommendation is to use **[clangd]**.
+ Can use the maintained config in [nvim-lspconfig/clangd].
- Explore the source code [on the web](https://sourcegraph.com/github.com/neovim/neovim).
-- If using [lua-language-server][], symlink `contrib/luarc.json` into the
+- If using [lua-language-server], symlink `contrib/luarc.json` into the
project root:
$ ln -s contrib/luarc.json .luarc.json
+### Includes
+
+For managing includes in C files, use [include-what-you-use].
+
+- [Install include-what-you-use][include-what-you-use-install]
+- To see which includes needs fixing use the cmake preset `iwyu`:
+ ```
+ cmake --workflow --preset iwyu
+ ```
+- There's also a make target that automatically fixes the suggestions from
+ IWYU:
+ ```
+ make iwyu
+ ```
+
+See [#549][549] for more details.
+
+Documenting
+-----------
+
+Many parts of the `:help` documentation are autogenerated from C or Lua docstrings using the `./scripts/gen_vimdoc.py` script.
+You can filter the regeneration based on the target (api, lua, or lsp), or the file you changed, that need a doc refresh using `./scripts/gen_vimdoc.py -t <target>`.
+
+## Lua docstrings
+
+Lua documentation uses a subset of [EmmyLua] annotations. A rough outline of a function documentation is
+
+```lua
+--- {Brief}
+---
+--- {Long explanation}
+---
+---@param arg1 type {description}
+---@param arg2 type {description}
+{...}
+---
+---@return type {description}
+```
+
+If possible, always add type information (`table`, `string`, `number`, ...). Multiple valid types are separated by a bar (`string|table`). Indicate optional parameters via `type|nil`.
+
+If a function in your Lua module should not be documented (e.g. internal function or local function), you should set the doc comment to:
+
+```
+---@private
+```
+
+Mark functions that are deprecated as
+```
+---@deprecated
+```
Reviewing
---------
@@ -271,30 +309,36 @@ commits in the feature branch which aren't in the `master` branch; `-p`
shows each commit's diff. To show the whole surrounding function of a change
as context, use the `-W` argument as well.
+[549]: https://github.com/neovim/neovim/issues/549
+[1820]: https://github.com/neovim/neovim/pull/1820
+[3174]: https://github.com/neovim/neovim/issues/3174
+[ASan]: http://clang.llvm.org/docs/AddressSanitizer.html
+[Cirrus CI]: https://cirrus-ci.com/github/neovim/neovim
+[Clang report]: https://neovim.io/doc/reports/clang/
+[GitHub Actions]: https://github.com/neovim/neovim/actions
+[clangd]: https://clangd.llvm.org
+[Merge a Vim patch]: https://github.com/neovim/neovim/wiki/Merging-patches-from-upstream-Vim
+[complexity:low]: https://github.com/neovim/neovim/issues?q=is%3Aopen+is%3Aissue+label%3Acomplexity%3Alow
+[conventional_commits]: https://www.conventionalcommits.org
+[EmmyLua]: https://github.com/sumneko/lua-language-server/wiki/Annotations
[gcc-warnings]: https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
+[gh]: https://cli.github.com/
[git-bisect]: http://git-scm.com/book/en/v2/Git-Tools-Debugging-with-Git
[git-feature-branch]: https://www.atlassian.com/git/tutorials/comparing-workflows
[git-history-filtering]: https://www.atlassian.com/git/tutorials/git-log/filtering-the-commit-history
[git-history-rewriting]: http://git-scm.com/book/en/v2/Git-Tools-Rewriting-History
[git-rebasing]: http://git-scm.com/book/en/v2/Git-Branching-Rebasing
[github-issues]: https://github.com/neovim/neovim/issues
-[1820]: https://github.com/neovim/neovim/pull/1820
-[gh]: https://cli.github.com/
-[conventional_commits]: https://www.conventionalcommits.org
-[style-guide]: https://neovim.io/doc/user/dev_style.html#dev-style
-[ASan]: http://clang.llvm.org/docs/AddressSanitizer.html
-[run-tests]: https://github.com/neovim/neovim/blob/master/test/README.md#running-tests
-[wiki-faq]: https://github.com/neovim/neovim/wiki/FAQ
-[review-checklist]: https://github.com/neovim/neovim/wiki/Code-review-checklist
-[3174]: https://github.com/neovim/neovim/issues/3174
-[sourcehut]: https://builds.sr.ht/~jmk
-[GitHub Actions]: https://github.com/neovim/neovim/actions
-[Merge a Vim patch]: https://github.com/neovim/neovim/wiki/Merging-patches-from-upstream-Vim
-[Clang report]: https://neovim.io/doc/reports/clang/
-[complexity:low]: https://github.com/neovim/neovim/issues?q=is%3Aopen+is%3Aissue+label%3Acomplexity%3Alow
+[include-what-you-use-install]: https://github.com/include-what-you-use/include-what-you-use#how-to-install
+[include-what-you-use]: https://github.com/include-what-you-use/include-what-you-use#using-with-cmake
+[lua-language-server]: https://github.com/sumneko/lua-language-server/
[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
+[nvim-lspconfig/clangd]: https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#clangd
[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
+[review-checklist]: https://github.com/neovim/neovim/wiki/Code-review-checklist
+[run-tests]: https://github.com/neovim/neovim/blob/master/test/README.md#running-tests
+[style-guide]: https://neovim.io/doc/user/dev_style.html#dev-style
[uncrustify]: http://uncrustify.sourceforge.net/
-[lua-language-server]: https://github.com/sumneko/lua-language-server/
+[wiki-contribute-help]: https://github.com/neovim/neovim/wiki/contribute-%3Ahelp
+[wiki-faq]: https://github.com/neovim/neovim/wiki/FAQ
diff --git a/LICENSE.txt b/LICENSE.txt
index c1b8286c4b..45a4a34c56 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -8,7 +8,7 @@ Neovim's license follows:
====
Apache License
Version 2.0, January 2004
- http://www.apache.org/licenses/
+ https://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
@@ -196,10 +196,11 @@ The externally maintained libraries used by Neovim are:
- libtermkey: MIT license
- libuv. Copyright Joyent, Inc. and other Node contributors. Node.js license.
- libvterm: MIT license
+ - lua-cjson: MIT license
- lua-compat: MIT license
- tree-sitter: MIT license
- - xdiff: LGPL license
- - lua-cjson: MIT license
+ - unibilium: LGPL v3
+ - xdiff: LGPL v2
====
diff --git a/MAINTAIN.md b/MAINTAIN.md
index 9a8531fbd9..95a3916535 100644
--- a/MAINTAIN.md
+++ b/MAINTAIN.md
@@ -12,23 +12,23 @@ General guidelines
* Use automation to solve problems
* Never break the API... but sometimes break the UI
-Ticket triage
--------------
+Issue triage
+------------
In practice we haven't found a way to forecast more precisely than "next" and
"after next". So there are usually one or two (at most) planned milestones:
-- Next bugfix-release (1.0.x)
-- Next feature-release (1.x.0)
+* Next bugfix-release (1.0.x)
+* Next feature-release (1.x.0)
The forecasting problem might be solved with an explicit priority system (like
Bram's todo.txt). Meanwhile the Neovim priority system is defined by:
-- PRs nearing completion.
-- Issue labels. E.g. the `+plan` label increases the ticket's priority merely
+* PRs nearing completion.
+* Issue labels. E.g. the `+plan` label increases the ticket's priority merely
for having a plan written down: it is _closer to completion_ than tickets
without a plan.
-- Comment activity or new information.
+* Comment activity or new information.
Anything that isn't in the next milestone, and doesn't have a finished PR—is
just not something you care very much about, by construction. Post-release you
@@ -50,47 +50,86 @@ has a major bug:
1. Fix the bug on `master`.
2. Cherry-pick the fix to `release-x.y`.
3. Cut a release from `release-x.y`.
- - Run `./scripts/release.sh`
- - Update (force-push) the remote `stable` tag.
- - 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.
+ * Run `./scripts/release.sh`
+ * Update (force-push) the remote `stable` tag.
+ * The [CI job](https://github.com/neovim/neovim/blob/3d45706478cd030c3ee05b4f336164bb96138095/.github/workflows/release.yml#L11-L13)
+ will update the release assets and force-push to 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`. Note, the PR must have a description in the issue body,
-or the backport will fail.
+### Release automation
+
+Neovim automation includes a [backport bot](https://github.com/zeebe-io/backport-action).
+Trigger the action by labeling a PR with `backport release-X.Y`. See `.github/workflows/backport.yml`.
Third-party dependencies
+------------------------
+
+These "bundled" dependencies can be updated by bumping their versions in `cmake.deps/CMakeLists.txt`.
+Some can be auto-bumped by `scripts/bump_deps.lua`.
+
+* [LuaJIT](https://github.com/LuaJIT/LuaJIT)
+* [Lua](https://www.lua.org/download.html)
+* [Luv](https://github.com/luvit/luv)
+* [gettext](https://ftp.gnu.org/pub/gnu/gettext/)
+* [libiconv](https://ftp.gnu.org/pub/gnu/libiconv)
+* [libtermkey](https://github.com/neovim/libtermkey)
+* [libuv](https://github.com/libuv/libuv)
+* [libvterm](http://www.leonerd.org.uk/code/libvterm/)
+* [lua-compat](https://github.com/keplerproject/lua-compat-5.3)
+* [msys2](https://github.com/msys2/MINGW-packages) (for mingw Windows build)
+ * Changes to mingw can [break our mingw build](https://github.com/msys2/MINGW-packages/issues/9946).
+* [tree-sitter](https://github.com/tree-sitter/tree-sitter)
+* [unibilium](https://github.com/neovim/unibilium)
+
+### Vendored dependencies
+
+These dependencies are "vendored" (inlined), we must update the sources manually:
+
+* `src/mpack/`: [libmpack](https://github.com/libmpack/libmpack)
+ * send improvements upstream!
+* `src/xdiff/`: [xdiff](https://github.com/git/git/tree/master/xdiff)
+* `src/cjson/`: [lua-cjson](https://github.com/openresty/lua-cjson)
+* `src/nvim/lib/`: [Klib](https://github.com/attractivechaos/klib)
+* `runtime/lua/vim/inspect.lua`: [inspect.lua](https://github.com/kikito/inspect.lua)
+* `src/nvim/tui/terminfo_defs.h`: terminfo definitions
+ * Run `scripts/update_terminfo.sh` to update these definitions.
+* [treesitter parsers](https://github.com/neovim/neovim/blob/fcc24e43e0b5f9d801a01ff2b8f78ce8c16dd551/cmake.deps/CMakeLists.txt#L197-L210)
+
+### Forks
+
+We may maintain forks, if we are waiting on upstream changes: https://github.com/neovim/neovim/wiki/Deps
+
+CI
--------------
-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)
- - [libtermkey](https://github.com/neovim/libtermkey)
- - [libuv](https://github.com/libuv/libuv)
- - [libvterm](http://www.leonerd.org.uk/code/libvterm/)
- - [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
+### General
+
+As our CI is primarily dependent on GitHub Actions at the moment, then so will
+our CI strategy be. The following guidelines have worked well for us so far:
+
+* Never use a macOS runner if an Ubuntu or a Windows runner can be used
+ instead. This is because macOS runners have a [tighter restrictions on the
+ number of concurrent jobs](https://docs.github.com/en/actions/learn-github-actions/usage-limits-billing-and-administration#usage-limits).
+
+### Runner versions
+
+* For special-purpose jobs where the runner version doesn't really matter,
+ prefer `-latest` tags so we don't need to manually bump the versions. An
+ example of a special-purpose workflow is `labeler.yml`.
+
+* For our testing jobs, which is currently only `ci.yml`, prefer to use the
+ latest stable (i.e. non-beta) version explicitly. Avoid using the `-latest`
+ tags here as it makes it difficult to determine from an unrelated PR if a
+ failure is due to the PR itself or due to GitHub bumping the `-latest` tag
+ without our knowledge. There's also a high risk that automatic bumping the CI
+ versions will fail due to manual work being required from experience.
+
+* For our release job, which is `release.yml`, prefer to use the oldest stable
+ (i.e. non-deprecated) versions available. The reason is that we're trying to
+ produce images that work in the broadest number of environments, and
+ therefore want to use older releases.
See also
--------
-- https://github.com/neovim/neovim/issues/862
-- https://github.com/git/git/blob/master/Documentation/howto/maintain-git.txt
+* https://github.com/neovim/neovim/issues/862
+* https://github.com/git/git/blob/master/Documentation/howto/maintain-git.txt
diff --git a/Makefile b/Makefile
index c071338435..1ba2431ba2 100644
--- a/Makefile
+++ b/Makefile
@@ -66,8 +66,7 @@ ifeq ($(CMAKE_GENERATOR),Ninja)
endif
DEPS_CMAKE_FLAGS ?=
-# Back-compat: USE_BUNDLED_DEPS was the old name.
-USE_BUNDLED ?= $(USE_BUNDLED_DEPS)
+USE_BUNDLED ?=
ifneq (,$(USE_BUNDLED))
BUNDLED_CMAKE_FLAG := -DUSE_BUNDLED=$(USE_BUNDLED)
@@ -127,25 +126,25 @@ endif
src/nvim/testdir/%.vim: phony_force
+$(SINGLE_MAKE) -C src/nvim/testdir NVIM_PRG=$(NVIM_PRG) SCRIPTS= $(MAKEOVERRIDES) $(patsubst src/nvim/testdir/%.vim,%,$@)
-build/runtime/doc/tags helptags: | nvim
- +$(BUILD_TOOL) -C build runtime/doc/tags
-
-# Builds help HTML _and_ checks for invalid help tags.
-helphtml: | nvim build/runtime/doc/tags
- +$(BUILD_TOOL) -C build doc_html
-
-functionaltest functionaltest-lua unittest benchmark: | nvim
+functionaltest-lua: | nvim
$(BUILD_TOOL) -C build $@
-lintlua lintsh lintpy lintuncrustify lintc lintcfull check-single-includes generated-sources lintcommit lint formatc formatlua format: | build/.ran-cmake
+FORMAT=formatc formatlua format
+LINT=lintlua lintsh lintc check-single-includes lintcommit lint
+TEST=functionaltest unittest
+generated-sources benchmark $(FORMAT) $(LINT) $(TEST): | build/.ran-cmake
$(CMAKE_PRG) --build build --target $@
-test: functionaltest unittest
+test: $(TEST)
+
+iwyu: build/.ran-cmake
+ cmake --workflow --fresh --preset iwyu > build/iwyu.log
+ iwyu-fix-includes --only_re="src/nvim" --ignore_re="src/nvim/(auto|map.h|eval/encode.c)" --safe_headers < build/iwyu.log
+ cmake -B build -U ENABLE_IWYU
clean:
+test -d build && $(BUILD_TOOL) -C build clean || true
$(MAKE) -C src/nvim/testdir clean
- $(MAKE) -C runtime/doc clean
$(MAKE) -C runtime/indent clean
distclean:
@@ -174,4 +173,4 @@ $(DEPS_BUILD_DIR)/%: phony_force
$(BUILD_TOOL) -C $(DEPS_BUILD_DIR) $(patsubst $(DEPS_BUILD_DIR)/%,%,$@)
endif
-.PHONY: test lintlua lintpy lintsh functionaltest unittest lint lintc clean distclean nvim libnvim cmake deps install appimage checkprefix lintcommit formatc formatlua format
+.PHONY: test clean distclean nvim libnvim cmake deps install appimage checkprefix benchmark $(FORMAT) $(LINT) $(TEST)
diff --git a/README.md b/README.md
index e3c3c12104..3f2e158900 100644
--- a/README.md
+++ b/README.md
@@ -1,23 +1,19 @@
<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) |
-[Twitter](https://twitter.com/Neovim)
+ <a href="https://neovim.io/doc/">Documentation</a> |
+ <a href="https://app.element.io/#/room/#neovim:matrix.org">Chat</a>
+</h1>
-[![GitHub CI](https://github.com/neovim/neovim/workflows/CI/badge.svg)](https://github.com/neovim/neovim/actions?query=workflow%3ACI+branch%3Amaster+event%3Apush)
-[![Codecov coverage](https://img.shields.io/codecov/c/github/neovim/neovim.svg)](https://codecov.io/gh/neovim/neovim)
+[![GitHub CI](https://github.com/neovim/neovim/actions/workflows/ci.yml/badge.svg?event=push&branch=master)](https://github.com/neovim/neovim/actions?query=workflow%3ACI+branch%3Amaster+event%3Apush)
[![Coverity Scan analysis](https://scan.coverity.com/projects/2227/badge.svg)](https://scan.coverity.com/projects/2227)
[![Clang analysis](https://neovim.io/doc/reports/clang/badge.svg)](https://neovim.io/doc/reports/clang)
[![PVS-Studio analysis](https://neovim.io/doc/reports/pvs/badge.svg)](https://neovim.io/doc/reports/pvs/PVS-studio.html.d)
-
[![Packages](https://repology.org/badge/tiny-repos/neovim.svg)](https://repology.org/metapackage/neovim)
[![Debian CI](https://badges.debian.net/badges/debian/testing/neovim/version.svg)](https://buildd.debian.org/neovim)
[![Downloads](https://img.shields.io/github/downloads/neovim/neovim/total.svg?maxAge=2592001)](https://github.com/neovim/neovim/releases/)
-[![nvim](https://snapcraft.io//nvim/badge.svg)](https://snapcraft.io/nvim)
-Neovim is a project that seeks to aggressively refactor Vim in order to:
+Neovim is a project that seeks to aggressively refactor [Vim](https://www.vim.org/) in order to:
- Simplify maintenance and encourage [contributions](CONTRIBUTING.md)
- Split the work between multiple developers
@@ -32,7 +28,7 @@ Features
- Modern [GUIs](https://github.com/neovim/neovim/wiki/Related-projects#gui)
- [API access](https://github.com/neovim/neovim/wiki/Related-projects#api-clients)
- from any language including C/C++, C#, Clojure, D, Elixir, Go, Haskell, Java,
+ from any language including C/C++, C#, Clojure, D, Elixir, Go, Haskell, Java/Kotlin,
JavaScript/Node.js, Julia, Lisp, Lua, Perl, Python, Racket, Ruby, Rust
- Embedded, scriptable [terminal emulator](https://neovim.io/doc/user/nvim_terminal_emulator.html)
- Asynchronous [job control](https://github.com/neovim/neovim/pull/2247)
@@ -40,7 +36,7 @@ Features
- [XDG base directories](https://github.com/neovim/neovim/pull/3470) support
- Compatible with most Vim plugins, including Ruby and Python plugins
-See [`:help nvim-features`][nvim-features] for the full list!
+See [`:help nvim-features`][nvim-features] for the full list, and [`:help news`][nvim-news] for noteworthy changes in the latest version!
Install from package
--------------------
@@ -53,7 +49,7 @@ Pre-built packages for Windows, macOS, and Linux are found on the
Install from source
-------------------
-See the [Building Neovim](https://github.com/neovim/neovim/wiki/Building-Neovim) wiki page for details.
+See the [Building Neovim](https://github.com/neovim/neovim/wiki/Building-Neovim) wiki page and [supported platforms](https://neovim.io/doc/user/support.html#supported-platforms) for details.
The build is CMake-based, but a Makefile is provided as a convenience.
After installing the dependencies, run the following command.
@@ -108,20 +104,21 @@ Apache 2.0 license, except for contributions copied from Vim (identified by the
encouraged to make a donation for needy children in Uganda. Please see the
kcc section of the vim docs or visit the ICCF web site, available at these URLs:
- http://iccf-holland.org/
- http://www.vim.org/iccf/
- http://www.iccf.nl/
+ https://iccf-holland.org/
+ https://www.vim.org/iccf/
+ https://www.iccf.nl/
You can also sponsor the development of Vim. Vim sponsors can vote for
features. The money goes to Uganda anyway.
[license-commit]: https://github.com/neovim/neovim/commit/b17d9691a24099c9210289f16afb1a498a89d803
[nvim-features]: https://neovim.io/doc/user/vim_diff.html#nvim-features
+[nvim-news]: https://neovim.io/doc/user/news.html
[Roadmap]: https://neovim.io/roadmap/
[advanced UIs]: https://github.com/neovim/neovim/wiki/Related-projects#gui
[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
+[Ubuntu]: https://packages.ubuntu.com/search?keywords=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
diff --git a/ci/before_cache.sh b/ci/before_cache.sh
index 9bc9bb45e9..3daeb04793 100755
--- a/ci/before_cache.sh
+++ b/ci/before_cache.sh
@@ -4,7 +4,9 @@ set -e
set -o pipefail
CI_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+# shellcheck source-path=SCRIPTDIR
source "${CI_DIR}/common/build.sh"
+# shellcheck source-path=SCRIPTDIR
source "${CI_DIR}/common/suite.sh"
mkdir -p "${HOME}/.cache"
diff --git a/ci/before_script.sh b/ci/before_script.sh
index 08e0cb9103..066789af36 100755
--- a/ci/before_script.sh
+++ b/ci/before_script.sh
@@ -4,6 +4,7 @@ set -e
set -o pipefail
CI_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+# shellcheck source-path=SCRIPTDIR
source "${CI_DIR}/common/build.sh"
# Test some of the configuration variables.
diff --git a/ci/build.ps1 b/ci/build.ps1
deleted file mode 100644
index 6709a9507a..0000000000
--- a/ci/build.ps1
+++ /dev/null
@@ -1,146 +0,0 @@
-[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
-
- # Run only this test file:
- # $env:TEST_FILE = "test\functional\foo.lua"
- 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 6e7ea2c8f8..e30d0337b5 100644
--- a/ci/common/build.sh
+++ b/ci/common/build.sh
@@ -7,7 +7,7 @@ _stat() {
}
top_make() {
- printf '%78s\n' | tr ' ' '='
+ printf '%78s\n' ' ' | tr ' ' '='
ninja "$@"
}
@@ -33,6 +33,7 @@ build_deps() {
# update CMake configuration and update to newer deps versions.
cd "${DEPS_BUILD_DIR}"
echo "Configuring with '${DEPS_CMAKE_FLAGS}'."
+ # shellcheck disable=SC2086
CC= cmake -G Ninja ${DEPS_CMAKE_FLAGS} "${CI_BUILD_DIR}/cmake.deps/"
if ! top_make; then
@@ -51,10 +52,10 @@ build_nvim() {
mkdir -p "${BUILD_DIR}"
cd "${BUILD_DIR}"
- echo "Configuring with '${CMAKE_FLAGS} $@'."
+ echo "Configuring with '${CMAKE_FLAGS} $*'."
+ # shellcheck disable=SC2086
cmake -G Ninja ${CMAKE_FLAGS} "$@" "${CI_BUILD_DIR}"
-
echo "Building nvim."
if ! top_make nvim ; then
exit 1
diff --git a/ci/common/suite.sh b/ci/common/suite.sh
index 0320ac15c3..c81261d2e7 100644
--- a/ci/common/suite.sh
+++ b/ci/common/suite.sh
@@ -9,12 +9,12 @@ fail() {
local test_name="$1"
local message="$2"
- : ${message:=Test $test_name failed}
+ : "${message:=Test $test_name failed}"
local full_msg="$test_name :: $message"
echo "${full_msg}" >> "${FAIL_SUMMARY_FILE}"
echo "Failed: $full_msg"
- FAILED=1
+ export FAILED=1
}
ended_successfully() {
diff --git a/ci/common/test.sh b/ci/common/test.sh
index 7db39a0e5f..326ec162c3 100644
--- a/ci/common/test.sh
+++ b/ci/common/test.sh
@@ -57,6 +57,7 @@ check_core_dumps() {
check_logs() {
# Iterate through each log to remove an useless warning.
+ # shellcheck disable=SC2044
for log in $(find "${1}" -type f -name "${2}"); do
sed -i "${log}" \
-e '/Warning: noted but unhandled ioctl/d' \
@@ -66,6 +67,7 @@ check_logs() {
# Now do it again, but only consider files with size > 0.
local err=""
+ # shellcheck disable=SC2044
for log in $(find "${1}" -type f -name "${2}" -size +0); do
cat "${log}"
err=1
@@ -97,7 +99,7 @@ unittests() {(
functionaltests() {(
ulimit -c unlimited || true
- if ! build_make ${FUNCTIONALTEST}; then
+ if ! build_make "${FUNCTIONALTEST}"; then
fail 'functionaltests' 'Functional tests failed'
fi
submit_coverage functionaltest
@@ -132,7 +134,7 @@ check_runtime_files() {(
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" "$(printf "$message" "$file")"
+ fail "$test_name" "$(printf "%s%s" "$message" "$file")"
fi
done
)}
diff --git a/ci/run_tests.sh b/ci/run_tests.sh
index da72d09506..0ef7080628 100755
--- a/ci/run_tests.sh
+++ b/ci/run_tests.sh
@@ -4,8 +4,11 @@ set -e
set -o pipefail
CI_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+# shellcheck source-path=SCRIPTDIR
source "${CI_DIR}/common/build.sh"
+# shellcheck source-path=SCRIPTDIR
source "${CI_DIR}/common/test.sh"
+# shellcheck source-path=SCRIPTDIR
source "${CI_DIR}/common/suite.sh"
rm -f "$END_MARKER"
@@ -14,16 +17,15 @@ rm -f "$END_MARKER"
if (($# == 0)); then
tests=('build_nvim')
+ # Additional threads aren't created in the unit/old 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
tests+=('unittests')
fi
- tests+=('functionaltests')
+ tests+=('oldtests')
fi
- tests+=('oldtests' 'install_nvim')
+ tests+=('functionaltests' 'install_nvim')
else
tests=("$@")
fi
diff --git a/ci/snap/.snapcraft_payload b/ci/snap/.snapcraft_payload
deleted file mode 100644
index 29f895fad6..0000000000
--- a/ci/snap/.snapcraft_payload
+++ /dev/null
@@ -1,194 +0,0 @@
-{
- "ref": "refs/heads/master",
- "before": "66b136c43c12df3dcf8f19ff48f206ad2e4f43fc",
- "after": "1bf69c32217cc455603ce8aa2b5415d9717f0fa2",
- "repository": {
- "id": 292861950,
- "node_id": "MDEwOlJlcG9zaXRvcnkyOTI4NjE5NTA=",
- "name": "neovim-snap",
- "full_name": "hurricanehrndz/neovim-snap",
- "private": false,
- "owner": {
- "name": "hurricanehrndz",
- "email": "hurricanehrndz@users.noreply.github.com",
- "login": "hurricanehrndz",
- "id": 5804237,
- "node_id": "MDQ6VXNlcjU4MDQyMzc=",
- "avatar_url": "https://avatars0.githubusercontent.com/u/5804237?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/hurricanehrndz",
- "html_url": "https://github.com/hurricanehrndz",
- "followers_url": "https://api.github.com/users/hurricanehrndz/followers",
- "following_url": "https://api.github.com/users/hurricanehrndz/following{/other_user}",
- "gists_url": "https://api.github.com/users/hurricanehrndz/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/hurricanehrndz/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/hurricanehrndz/subscriptions",
- "organizations_url": "https://api.github.com/users/hurricanehrndz/orgs",
- "repos_url": "https://api.github.com/users/hurricanehrndz/repos",
- "events_url": "https://api.github.com/users/hurricanehrndz/events{/privacy}",
- "received_events_url": "https://api.github.com/users/hurricanehrndz/received_events",
- "type": "User",
- "site_admin": false
- },
- "html_url": "https://github.com/hurricanehrndz/neovim-snap",
- "description": "snap build for neovim",
- "fork": false,
- "url": "https://github.com/hurricanehrndz/neovim-snap",
- "forks_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/forks",
- "keys_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/keys{/key_id}",
- "collaborators_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/collaborators{/collaborator}",
- "teams_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/teams",
- "hooks_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/hooks",
- "issue_events_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/issues/events{/number}",
- "events_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/events",
- "assignees_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/assignees{/user}",
- "branches_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/branches{/branch}",
- "tags_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/tags",
- "blobs_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/git/blobs{/sha}",
- "git_tags_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/git/tags{/sha}",
- "git_refs_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/git/refs{/sha}",
- "trees_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/git/trees{/sha}",
- "statuses_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/statuses/{sha}",
- "languages_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/languages",
- "stargazers_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/stargazers",
- "contributors_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/contributors",
- "subscribers_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/subscribers",
- "subscription_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/subscription",
- "commits_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/commits{/sha}",
- "git_commits_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/git/commits{/sha}",
- "comments_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/comments{/number}",
- "issue_comment_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/issues/comments{/number}",
- "contents_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/contents/{+path}",
- "compare_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/compare/{base}...{head}",
- "merges_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/merges",
- "archive_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/{archive_format}{/ref}",
- "downloads_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/downloads",
- "issues_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/issues{/number}",
- "pulls_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/pulls{/number}",
- "milestones_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/milestones{/number}",
- "notifications_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/notifications{?since,all,participating}",
- "labels_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/labels{/name}",
- "releases_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/releases{/id}",
- "deployments_url": "https://api.github.com/repos/hurricanehrndz/neovim-snap/deployments",
- "created_at": 1599227980,
- "updated_at": "2020-09-04T14:02:38Z",
- "pushed_at": 1599228352,
- "git_url": "git://github.com/hurricanehrndz/neovim-snap.git",
- "ssh_url": "git@github.com:hurricanehrndz/neovim-snap.git",
- "clone_url": "https://github.com/hurricanehrndz/neovim-snap.git",
- "svn_url": "https://github.com/hurricanehrndz/neovim-snap",
- "homepage": null,
- "size": 0,
- "stargazers_count": 0,
- "watchers_count": 0,
- "language": null,
- "has_issues": true,
- "has_projects": true,
- "has_downloads": true,
- "has_wiki": true,
- "has_pages": false,
- "forks_count": 0,
- "mirror_url": null,
- "archived": false,
- "disabled": false,
- "open_issues_count": 0,
- "license": {
- "key": "mit",
- "name": "MIT License",
- "spdx_id": "MIT",
- "url": "https://api.github.com/licenses/mit",
- "node_id": "MDc6TGljZW5zZTEz"
- },
- "forks": 0,
- "open_issues": 0,
- "watchers": 0,
- "default_branch": "master",
- "stargazers": 0,
- "master_branch": "master"
- },
- "pusher": {
- "name": "hurricanehrndz",
- "email": "hurricanehrndz@users.noreply.github.com"
- },
- "sender": {
- "login": "hurricanehrndz",
- "id": 5804237,
- "node_id": "MDQ6VXNlcjU4MDQyMzc=",
- "avatar_url": "https://avatars0.githubusercontent.com/u/5804237?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/hurricanehrndz",
- "html_url": "https://github.com/hurricanehrndz",
- "followers_url": "https://api.github.com/users/hurricanehrndz/followers",
- "following_url": "https://api.github.com/users/hurricanehrndz/following{/other_user}",
- "gists_url": "https://api.github.com/users/hurricanehrndz/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/hurricanehrndz/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/hurricanehrndz/subscriptions",
- "organizations_url": "https://api.github.com/users/hurricanehrndz/orgs",
- "repos_url": "https://api.github.com/users/hurricanehrndz/repos",
- "events_url": "https://api.github.com/users/hurricanehrndz/events{/privacy}",
- "received_events_url": "https://api.github.com/users/hurricanehrndz/received_events",
- "type": "User",
- "site_admin": false
- },
- "created": false,
- "deleted": false,
- "forced": false,
- "base_ref": null,
- "compare": "https://github.com/hurricanehrndz/neovim-snap/compare/66b136c43c12...1bf69c32217c",
- "commits": [
- {
- "id": "1bf69c32217cc455603ce8aa2b5415d9717f0fa2",
- "tree_id": "62ea83a2349be8c930c45fdc199f71b08bf5927e",
- "distinct": true,
- "message": "Build of latest tag",
- "timestamp": "2020-09-04T14:05:40Z",
- "url": "https://github.com/hurricanehrndz/neovim-snap/commit/1bf69c32217cc455603ce8aa2b5415d9717f0fa2",
- "author": {
- "name": "Carlos Hernandez",
- "email": "carlos@techbyte.ca",
- "username": "hurricanehrndz"
- },
- "committer": {
- "name": "Carlos Hernandez",
- "email": "carlos@techbyte.ca",
- "username": "hurricanehrndz"
- },
- "added": [
-
- ],
- "removed": [
-
- ],
- "modified": [
- "snap/snapcraft.yaml"
- ]
- }
- ],
- "head_commit": {
- "id": "1bf69c32217cc455603ce8aa2b5415d9717f0fa2",
- "tree_id": "62ea83a2349be8c930c45fdc199f71b08bf5927e",
- "distinct": true,
- "message": "Build of latest tag",
- "timestamp": "2020-09-04T14:05:40Z",
- "url": "https://github.com/hurricanehrndz/neovim-snap/commit/1bf69c32217cc455603ce8aa2b5415d9717f0fa2",
- "author": {
- "name": "Carlos Hernandez",
- "email": "carlos@techbyte.ca",
- "username": "hurricanehrndz"
- },
- "committer": {
- "name": "Carlos Hernandez",
- "email": "carlos@techbyte.ca",
- "username": "hurricanehrndz"
- },
- "added": [
-
- ],
- "removed": [
-
- ],
- "modified": [
- "snap/snapcraft.yaml"
- ]
- }
-}
diff --git a/ci/snap/after_success.sh b/ci/snap/after_success.sh
deleted file mode 100755
index e66721a5e2..0000000000
--- a/ci/snap/after_success.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-
-RESULT_SNAP=$(find ./ -name "*.snap")
-
-sudo snap install "$RESULT_SNAP" --dangerous --classic
-
-/snap/bin/nvim --version
-
-SHA256=$(sha256sum "$RESULT_SNAP")
-echo "SHA256: ${SHA256} ."
diff --git a/ci/snap/deploy.sh b/ci/snap/deploy.sh
deleted file mode 100755
index 1794fc61d9..0000000000
--- a/ci/snap/deploy.sh
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-SNAP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-WEBHOOK_PAYLOAD="$(cat "${SNAP_DIR}/.snapcraft_payload")"
-PAYLOAD_SIG="${SECRET_SNAP_SIG}"
-
-
-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
- git checkout "${last_committed_tag}" 2> /dev/null
- last_git_release="$(git describe --first-parent 2> /dev/null | perl -lpe 's/v\d.\d.\d-//g')"
-
- if [[ -z "$(echo $last_snap_release | perl -ne "print if /${last_git_release}.*/")" ]]; then
- return 0
- fi
- return 1
-}
-
-
-trigger_snapcraft_webhook() {
- [[ -n "${PAYLOAD_SIG}" ]] || exit
- echo "Triggering new snap release via webhook..."
- curl -X POST \
- -H "Content-Type: application/json" \
- -H "X-Hub-Signature: sha1=${PAYLOAD_SIG}" \
- --data "${WEBHOOK_PAYLOAD}" \
- https://snapcraft.io/nvim/webhook/notify
-}
-
-
-if $(snap_release_needed); then
- echo "New snap release required"
- trigger_snapcraft_webhook
-fi
diff --git a/ci/snap/install.sh b/ci/snap/install.sh
deleted file mode 100755
index 0ceb6f0422..0000000000
--- a/ci/snap/install.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-sudo apt update
-sudo usermod -aG lxd $USER
-sudo /snap/bin/lxd.migrate -yes
-sudo /snap/bin/lxd waitready
-sudo /snap/bin/lxd init --auto
-
diff --git a/ci/snap/script.sh b/ci/snap/script.sh
deleted file mode 100755
index 21d3421044..0000000000
--- a/ci/snap/script.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-mkdir -p "$CI_BUILD_DIR/snaps-cache"
-sg lxd -c snapcraft
-
diff --git a/ci/snap/travis_snapcraft.cfg b/ci/snap/travis_snapcraft.cfg
deleted file mode 100644
index 3e6a60c30d..0000000000
--- a/ci/snap/travis_snapcraft.cfg
+++ /dev/null
Binary files differ
diff --git a/cmake.config/CMakeLists.txt b/cmake.config/CMakeLists.txt
index 8c86b89e43..6de86cbaf2 100644
--- a/cmake.config/CMakeLists.txt
+++ b/cmake.config/CMakeLists.txt
@@ -5,6 +5,26 @@ include(CheckIncludeFiles)
include(CheckCSourceRuns)
include(CheckCSourceCompiles)
+check_c_source_compiles("
+#include <execinfo.h>
+int main(void)
+{
+ void *trace[1];
+ backtrace(trace, 1);
+ return 0;
+}
+" HAVE_EXECINFO_BACKTRACE)
+
+check_c_source_compiles("
+int main(void)
+{
+ int a = 42;
+ __builtin_add_overflow(a, a, &a);
+ __builtin_sub_overflow(a, a, &a);
+ return 0;
+}
+" HAVE_BUILTIN_ADD_OVERFLOW)
+
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)
@@ -20,11 +40,6 @@ check_include_files(langinfo.h HAVE_LANGINFO_H)
check_include_files(locale.h HAVE_LOCALE_H)
check_include_files(pwd.h HAVE_PWD_H)
check_include_files(strings.h HAVE_STRINGS_H)
-check_include_files(sys/wait.h HAVE_SYS_WAIT_H)
-if(NOT HAVE_SYS_WAIT_H AND UNIX)
- # See if_cscope.c
- message(SEND_ERROR "header sys/wait.h is required for Unix")
-endif()
check_include_files(sys/utsname.h HAVE_SYS_UTSNAME_H)
check_include_files(termios.h HAVE_TERMIOS_H)
check_include_files(sys/uio.h HAVE_SYS_UIO_H)
@@ -36,11 +51,6 @@ check_function_exists(getpwent HAVE_GETPWENT)
check_function_exists(getpwnam HAVE_GETPWNAM)
check_function_exists(getpwuid HAVE_GETPWUID)
check_function_exists(readv HAVE_READV)
-
-if(Iconv_FOUND)
- set(HAVE_ICONV 1)
-endif()
-
check_function_exists(opendir HAVE_OPENDIR)
check_function_exists(readlink HAVE_READLINK)
check_function_exists(setpgid HAVE_SETPGID)
@@ -51,6 +61,26 @@ check_function_exists(strcasecmp HAVE_STRCASECMP)
check_function_exists(strncasecmp HAVE_STRNCASECMP)
check_function_exists(strptime HAVE_STRPTIME)
+check_c_source_compiles("
+#include <sys/types.h>
+#include <dirent.h>
+int main(void)
+{
+ DIR *dir = opendir(\"dirname\");
+ dirfd(dir);
+ return 0;
+}
+" HAVE_DIRFD)
+
+check_c_source_compiles("
+#include <sys/file.h>
+int main(void)
+{
+ flock(10, LOCK_SH);
+ return 0;
+}
+" HAVE_FLOCK)
+
if(CMAKE_SYSTEM_NAME STREQUAL "SunOS")
check_c_source_compiles("
#include <termios.h>
diff --git a/cmake.config/config.h.in b/cmake.config/config.h.in
index 59be83fb5e..c8377bf45c 100644
--- a/cmake.config/config.h.in
+++ b/cmake.config/config.h.in
@@ -24,7 +24,6 @@
#cmakedefine HAVE_GETPWENT
#cmakedefine HAVE_GETPWNAM
#cmakedefine HAVE_GETPWUID
-#cmakedefine HAVE_ICONV
#cmakedefine HAVE_LANGINFO_H
#cmakedefine HAVE_LOCALE_H
#cmakedefine HAVE_NL_LANGINFO_CODESET
@@ -54,10 +53,10 @@
# undef HAVE_SYS_UIO_H
# endif
#endif
+#cmakedefine HAVE_DIRFD
+#cmakedefine HAVE_FLOCK
#cmakedefine HAVE_FORKPTY
-#cmakedefine FEAT_TUI
-
#ifndef UNIT_TESTING
#cmakedefine LOG_LIST_ACTIONS
#endif
diff --git a/cmake.config/iwyu/gcc.libc.imp b/cmake.config/iwyu/gcc.libc.imp
new file mode 100644
index 0000000000..1dd0ad42c7
--- /dev/null
+++ b/cmake.config/iwyu/gcc.libc.imp
@@ -0,0 +1,226 @@
+# This was initially taken from the IWYU repository:
+# github.com/include-what-you-use/include-what-you-use/blob/164b8fe7597805ae55f029ecf6580dc46a74c7ed/gcc.libc.imp
+# It has useful mappings that are normally enabled by default, but there are
+# other default mappings that conflict with our builds. The best solution seems
+# to be to disable all defaults, import the defaults from the IWYU
+# repo and modify the rules that conflict with our build.
+#
+# TODO(dundargoc): Check if there is a way to disable specific builtin maps as
+# to avoid this file entirely.
+
+# Mappings for GNU libc
+# ( cd /usr/include && grep '^ *# *include' {sys/,net/,}* | perl -nle 'm/^([^:]+).*<([^>]+)>/ && print qq@ { include: [ "<$2>", private, "<$1>", public ] },@' | grep bits/ | sort )
+# When I saw more than one mapping for these, I typically picked
+# what I thought was the "best" one.
+[
+ { include: [ "<bits/a.out.h>", private, "<a.out.h>", public ] },
+ { include: [ "<bits/auxv.h>", private, "<sys/auxv.h>", public ] },
+ { include: [ "<bits/byteswap.h>", private, "<byteswap.h>", public ] },
+ { include: [ "<bits/cmathcalls.h>", private, "<complex.h>", public ] },
+ { include: [ "<bits/confname.h>", private, "<unistd.h>", private ] },
+ { include: [ "<bits/dirent.h>", private, "<dirent.h>", public ] },
+ { include: [ "<bits/dlfcn.h>", private, "<dlfcn.h>", public ] },
+ { include: [ "<bits/elfclass.h>", private, "<link.h>", public ] },
+ { include: [ "<bits/endian.h>", private, "<endian.h>", public ] },
+ { include: [ "<bits/environments.h>", private, "<unistd.h>", private ] },
+ { include: [ "<bits/epoll.h>", private, "<sys/epoll.h>", public ] },
+ { include: [ "<bits/errno.h>", private, "<errno.h>", public ] },
+ { include: [ "<bits/error.h>", private, "<error.h>", public ] },
+ { include: [ "<bits/eventfd.h>", private, "<sys/eventfd.h>", public ] },
+ { include: [ "<bits/fcntl.h>", private, "<fcntl.h>", public ] },
+ { include: [ "<bits/fcntl2.h>", private, "<fcntl.h>", public ] },
+ { include: [ "<bits/fenv.h>", private, "<fenv.h>", public ] },
+ { include: [ "<bits/fenvinline.h>", private, "<fenv.h>", public ] },
+ { include: [ "<bits/huge_val.h>", private, "<math.h>", public ] },
+ { include: [ "<bits/huge_valf.h>", private, "<math.h>", public ] },
+ { include: [ "<bits/huge_vall.h>", private, "<math.h>", public ] },
+ { include: [ "<bits/hwcap.h>", private, "<sys/auxv.h>", public ] },
+ { include: [ "<bits/inf.h>", private, "<math.h>", public ] },
+ { include: [ "<bits/inotify.h>", private, "<sys/inotify.h>", public ] },
+ { include: [ "<bits/ioctl-types.h>", private, "<sys/ioctl.h>", public ] },
+ { include: [ "<bits/ioctls.h>", private, "<sys/ioctl.h>", public ] },
+ { include: [ "<bits/ipc.h>", private, "<sys/ipc.h>", public ] },
+ { include: [ "<bits/ipctypes.h>", private, "<sys/ipc.h>", public ] },
+ { include: [ "<bits/libio-ldbl.h>", private, "<libio.h>", public ] },
+ { include: [ "<bits/link.h>", private, "<link.h>", public ] },
+ { include: [ "<bits/locale.h>", private, "<locale.h>", public ] },
+ { include: [ "<bits/math-finite.h>", private, "<math.h>", public ] },
+ { include: [ "<bits/mathcalls.h>", private, "<math.h>", public ] },
+ { include: [ "<bits/mathdef.h>", private, "<math.h>", public ] },
+ { include: [ "<bits/mathinline.h>", private, "<math.h>", public ] },
+ { include: [ "<bits/mman.h>", private, "<sys/mman.h>", public ] },
+ { include: [ "<bits/mman-shared.h>", private, "<sys/mman.h>", public ] },
+ { include: [ "<bits/monetary-ldbl.h>", private, "<monetary.h>", public ] },
+ { include: [ "<bits/mqueue.h>", private, "<mqueue.h>", public ] },
+ { include: [ "<bits/mqueue2.h>", private, "<mqueue.h>", public ] },
+ { include: [ "<bits/msq.h>", private, "<sys/msg.h>", public ] },
+ { include: [ "<bits/nan.h>", private, "<math.h>", public ] },
+ { include: [ "<bits/netdb.h>", private, "<netdb.h>", private ] },
+ { include: [ "<bits/param.h>", private, "<sys/param.h>", public ] },
+ { include: [ "<bits/poll.h>", private, "<sys/poll.h>", private ] },
+ { include: [ "<bits/poll2.h>", private, "<sys/poll.h>", private ] },
+ { include: [ "<bits/posix1_lim.h>", private, "<limits.h>", public ] },
+ { include: [ "<bits/posix2_lim.h>", private, "<limits.h>", public ] },
+ { include: [ "<bits/posix_opt.h>", private, "<unistd.h>", private ] },
+ { include: [ "<bits/printf-ldbl.h>", private, "<printf.h>", public ] },
+ { include: [ "<bits/pthreadtypes.h>", private, "<pthread.h>", private ] },
+ { include: [ "<bits/resource.h>", private, "<sys/resource.h>", public ] },
+ { include: [ "<bits/sched.h>", private, "<sched.h>", public ] },
+ { include: [ "<bits/select.h>", private, "<sys/select.h>", public ] },
+ { include: [ "<bits/select2.h>", private, "<sys/select.h>", public ] },
+ { include: [ "<bits/sem.h>", private, "<sys/sem.h>", public ] },
+ { include: [ "<bits/semaphore.h>", private, "<semaphore.h>", public ] },
+ { include: [ "<bits/setjmp.h>", private, "<setjmp.h>", public ] },
+ { include: [ "<bits/setjmp2.h>", private, "<setjmp.h>", public ] },
+ { include: [ "<bits/shm.h>", private, "<sys/shm.h>", public ] },
+ { include: [ "<bits/sigaction.h>", private, "<signal.h>", public ] },
+ { include: [ "<bits/sigcontext.h>", private, "<signal.h>", public ] },
+ { include: [ "<bits/siginfo.h>", private, "<signal.h>", public ] },
+ { include: [ "<bits/signum.h>", private, "<signal.h>", public ] },
+ { include: [ "<bits/sigset.h>", private, "<signal.h>", public ] },
+ { include: [ "<bits/sigstack.h>", private, "<signal.h>", public ] },
+ { include: [ "<bits/sigthread.h>", private, "<signal.h>", public ] },
+ { include: [ "<bits/sockaddr.h>", private, "<sys/un.h>", public ] },
+ { include: [ "<bits/socket.h>", private, "<sys/socket.h>", private ] },
+ { include: [ "<bits/socket2.h>", private, "<sys/socket.h>", private ] },
+ { include: [ "<bits/socket_type.h>", private, "<sys/socket.h>", private ] },
+ { include: [ "<bits/stab.def>", private, "<stab.h>", public ] },
+ { include: [ "<bits/stat.h>", private, "<sys/stat.h>", public ] },
+ { include: [ "<bits/statfs.h>", private, "<sys/statfs.h>", public ] },
+ { include: [ "<bits/statvfs.h>", private, "<sys/statvfs.h>", public ] },
+ { include: [ "<bits/stdio-ldbl.h>", private, "<stdio.h>", public ] },
+ { include: [ "<bits/stdio-lock.h>", private, "<libio.h>", public ] },
+ { include: [ "<bits/stdio.h>", private, "<stdio.h>", public ] },
+ { include: [ "<bits/stdio2.h>", private, "<stdio.h>", public ] },
+ { include: [ "<bits/stdio_lim.h>", private, "<stdio.h>", public ] },
+ { include: [ "<bits/stdlib-bsearch.h>", private, "<stdlib.h>", public ] },
+ { include: [ "<bits/stdlib-float.h>", private, "<stdlib.h>", public ] },
+ { include: [ "<bits/stdlib-ldbl.h>", private, "<stdlib.h>", public ] },
+ { include: [ "<bits/stdlib.h>", private, "<stdlib.h>", public ] },
+ { include: [ "<bits/string.h>", private, "<string.h>", public ] },
+ { include: [ "<bits/string2.h>", private, "<string.h>", public ] },
+ { include: [ "<bits/string3.h>", private, "<string.h>", public ] },
+ { include: [ "<bits/stropts.h>", private, "<stropts.h>", public ] },
+ { include: [ "<bits/struct_stat.h>", private, "<sys/stat.h>", public ] },
+ { include: [ "<bits/struct_stat.h>", private, "<ftw.h>", public ] },
+ { include: [ "<bits/sys_errlist.h>", private, "<stdio.h>", public ] },
+ { include: [ "<bits/syscall.h>", private, "<sys/syscall.h>", public ] },
+ { include: [ "<bits/sysctl.h>", private, "<sys/sysctl.h>", public ] },
+ { include: [ "<bits/syslog-ldbl.h>", private, "<sys/syslog.h>", private ] },
+ { include: [ "<bits/syslog-path.h>", private, "<sys/syslog.h>", private ] },
+ { include: [ "<bits/syslog.h>", private, "<sys/syslog.h>", private ] },
+ { include: [ "<bits/termios.h>", private, "<termios.h>", private ] },
+ { include: [ "<bits/termios-c_lflag.h>", private, "<termios.h>", private ] },
+ { include: [ "<bits/termios-struct.h>", private, "<termios.h>", private ] },
+ { include: [ "<bits/termios-tcflow.h>", private, "<termios.h>", private ] },
+ { include: [ "<bits/time.h>", private, "<time.h>", public ] },
+ { include: [ "<bits/time.h>", private, "<sys/time.h>", public ] },
+ { include: [ "<bits/timerfd.h>", private, "<sys/timerfd.h>", public ] },
+ { include: [ "<bits/timex.h>", private, "<sys/timex.h>", public ] },
+ { include: [ "<bits/types.h>", private, "<sys/types.h>", public ] },
+ { include: [ "<bits/types/siginfo_t.h>", private, "<signal.h>", public ] },
+ { include: [ "<bits/types/siginfo_t.h>", private, "<sys/wait.h>", public ] },
+ { include: [ "<bits/uio.h>", private, "<sys/uio.h>", public ] },
+ { include: [ "<bits/unistd.h>", private, "<unistd.h>", private ] },
+ { include: [ "<bits/ustat.h>", private, "<sys/ustat.h>", private ] },
+ { include: [ "<bits/utmp.h>", private, "<utmp.h>", public ] },
+ { include: [ "<bits/utmpx.h>", private, "<utmpx.h>", public ] },
+ { include: [ "<bits/utsname.h>", private, "<sys/utsname.h>", public ] },
+ { include: [ "<bits/waitflags.h>", private, "<sys/wait.h>", public ] },
+ { include: [ "<bits/waitstatus.h>", private, "<sys/wait.h>", public ] },
+ { include: [ "<bits/wchar-ldbl.h>", private, "<wchar.h>", public ] },
+ { include: [ "<bits/wchar.h>", private, "<wchar.h>", public ] },
+ { include: [ "<bits/wchar2.h>", private, "<wchar.h>", public ] },
+ { include: [ "<bits/wordsize.h>", private, "<limits.h>", public ] },
+ { include: [ "<bits/xopen_lim.h>", private, "<limits.h>", public ] },
+ { include: [ "<bits/xtitypes.h>", private, "<stropts.h>", public ] },
+ # Sometimes libc tells you what mapping to do via an '#error':
+ # # error "Never use <bits/dlfcn.h> directly; include <dlfcn.h> instead."
+ # or
+ # # error "Never include <bits/socket_type.h> directly; use <sys/socket.h> instead."
+ # ( cd /usr/include && grep -R '^ *# *error "Never use\|include' * | perl -nle 'm/<([^>]+).*directly.*<([^>]+)/ && print qq@ { include: [ "<$1>", private, "<$2>", public ] },@' | sort )
+ { include: [ "<bits/a.out.h>", private, "<a.out.h>", public ] },
+ { include: [ "<bits/byteswap-16.h>", private, "<byteswap.h>", public ] },
+ { include: [ "<bits/byteswap.h>", private, "<byteswap.h>", public ] },
+ { include: [ "<bits/cmathcalls.h>", private, "<complex.h>", public ] },
+ { include: [ "<bits/confname.h>", private, "<unistd.h>", private ] },
+ { include: [ "<bits/dirent.h>", private, "<dirent.h>", public ] },
+ { include: [ "<bits/dlfcn.h>", private, "<dlfcn.h>", public ] },
+ { include: [ "<bits/elfclass.h>", private, "<link.h>", public ] },
+ { include: [ "<bits/endian.h>", private, "<endian.h>", public ] },
+ { include: [ "<bits/epoll.h>", private, "<sys/epoll.h>", public ] },
+ { include: [ "<bits/eventfd.h>", private, "<sys/eventfd.h>", public ] },
+ { include: [ "<bits/fcntl-linux.h>", private, "<fcntl.h>", public ] },
+ { include: [ "<bits/fcntl.h>", private, "<fcntl.h>", public ] },
+ { include: [ "<bits/fenv.h>", private, "<fenv.h>", public ] },
+ { include: [ "<bits/huge_val.h>", private, "<math.h>", public ] },
+ { include: [ "<bits/huge_valf.h>", private, "<math.h>", public ] },
+ { include: [ "<bits/huge_vall.h>", private, "<math.h>", public ] },
+ { include: [ "<bits/in.h>", private, "<netinet/in.h>", private ] },
+ { include: [ "<bits/inf.h>", private, "<math.h>", public ] },
+ { include: [ "<bits/inotify.h>", private, "<sys/inotify.h>", public ] },
+ { include: [ "<bits/ioctl-types.h>", private, "<sys/ioctl.h>", public ] },
+ { include: [ "<bits/ioctls.h>", private, "<sys/ioctl.h>", public ] },
+ { include: [ "<bits/ipc.h>", private, "<sys/ipc.h>", public ] },
+ { include: [ "<bits/ipctypes.h>", private, "<sys/ipc.h>", public ] },
+ { include: [ "<bits/locale.h>", private, "<locale.h>", public ] },
+ { include: [ "<bits/math-finite.h>", private, "<math.h>", public ] },
+ { include: [ "<bits/mathdef.h>", private, "<math.h>", public ] },
+ { include: [ "<bits/mathinline.h>", private, "<math.h>", public ] },
+ { include: [ "<bits/mman-linux.h>", private, "<sys/mman.h>", public ] },
+ { include: [ "<bits/mman.h>", private, "<sys/mman.h>", public ] },
+ { include: [ "<bits/mqueue.h>", private, "<mqueue.h>", public ] },
+ { include: [ "<bits/msq.h>", private, "<sys/msg.h>", public ] },
+ { include: [ "<bits/nan.h>", private, "<math.h>", public ] },
+ { include: [ "<bits/param.h>", private, "<sys/param.h>", public ] },
+ { include: [ "<bits/poll.h>", private, "<sys/poll.h>", private ] },
+ { include: [ "<bits/predefs.h>", private, "<features.h>", public ] },
+ { include: [ "<bits/resource.h>", private, "<sys/resource.h>", public ] },
+ { include: [ "<bits/select.h>", private, "<sys/select.h>", public ] },
+ { include: [ "<bits/semaphore.h>", private, "<semaphore.h>", public ] },
+ { include: [ "<bits/sigcontext.h>", private, "<signal.h>", public ] },
+ { include: [ "<bits/signalfd.h>", private, "<sys/signalfd.h>", public ] },
+ { include: [ "<bits/stdlib-float.h>", private, "<stdlib.h>", public ] },
+ { include: [ "<bits/string.h>", private, "<string.h>", public ] },
+ { include: [ "<bits/string2.h>", private, "<string.h>", public ] },
+ { include: [ "<bits/string3.h>", private, "<string.h>", public ] },
+ { include: [ "<bits/timerfd.h>", private, "<sys/timerfd.h>", public ] },
+ { include: [ "<bits/typesizes.h>", private, "<sys/types.h>", public ] },
+ # Top-level #includes that just forward to another file:
+ # $ for i in /usr/include/*; do [ -f $i ] } && [ `wc -l < $i` = 1 ] } && echo $i; done
+ # (poll.h, syscall.h, syslog.h, ustat.h, wait.h).
+ # For each file, I looked at the list of canonical header files --
+ # http://www.opengroup.org/onlinepubs/9699919799/idx/head.html --
+ # to decide which of the two files is canonical. If neither is
+ # on the POSIX.1 1998 list, I just choose the top-level one.
+ { include: [ "<sys/poll.h>", private, "<poll.h>", public ] },
+ { include: [ "<sys/syslog.h>", private, "<syslog.h>", public ] },
+ { include: [ "<sys/ustat.h>", private, "<ustat.h>", public ] },
+ { include: [ "<wait.h>", private, "<sys/wait.h>", public ] },
+ # These are all files in bits/ that delegate to asm/ and linux/ to
+ # do all (or lots) of the work. Note these are private->private.
+ # $ for i in /usr/include/bits/*; do for dir in asm linux; do grep -H -e $dir/`basename $i` $i; done; done
+ { include: [ "<linux/errno.h>", private, "<bits/errno.h>", private ] },
+ { include: [ "<asm/ioctls.h>", private, "<bits/ioctls.h>", private ] },
+ { include: [ "<asm/socket.h>", private, "<bits/socket.h>", private ] },
+ { include: [ "<linux/socket.h>", private, "<bits/socket.h>", private ] },
+ # Some asm files have 32- and 64-bit variants:
+ # $ ls /usr/include/asm/*_{32,64}.h
+ { include: [ "<asm/posix_types_32.h>", private, "<asm/posix_types.h>", public ] },
+ { include: [ "<asm/posix_types_64.h>", private, "<asm/posix_types.h>", public ] },
+ { include: [ "<asm/unistd_32.h>", private, "<asm/unistd.h>", private ] },
+ { include: [ "<asm/unistd_64.h>", private, "<asm/unistd.h>", private ] },
+ # I don't know what grep would have found these. I found them
+ # via user report.
+ { include: [ "<asm/errno.h>", private, "<errno.h>", public ] },
+ { include: [ "<asm/errno-base.h>", private, "<errno.h>", public ] },
+ { include: [ "<asm/ptrace-abi.h>", private, "<asm/ptrace.h>", public ] },
+ { include: [ "<asm/unistd.h>", private, "<sys/syscall.h>", public ] },
+ { include: [ "<linux/limits.h>", private, "<limits.h>", public ] }, # PATH_MAX
+ { include: [ "<linux/prctl.h>", private, "<sys/prctl.h>", public ] },
+ { include: [ "<sys/ucontext.h>", private, "<ucontext.h>", public ] },
+ # Exports guaranteed by the C standard
+ { include: [ "<stdint.h>", public, "<inttypes.h>", public ] },
+]
+
+# vim: set ft=toml:
diff --git a/cmake.config/iwyu/gcc.symbols.imp b/cmake.config/iwyu/gcc.symbols.imp
new file mode 100644
index 0000000000..6066de9d09
--- /dev/null
+++ b/cmake.config/iwyu/gcc.symbols.imp
@@ -0,0 +1,305 @@
+# This was initially taken from the IWYU repository:
+# github.com/include-what-you-use/include-what-you-use/blob/164b8fe7597805ae55f029ecf6580dc46a74c7ed/gcc.symbols.imp
+# It has useful mappings that are normally enabled by default, but there are
+# other default mappings that conflict with our builds. The best solution seems
+# to be to disable all defaults, import the defaults from the IWYU repo and
+# modify the rules that conflict with our build.
+#
+# TODO(dundargoc): Check if there is a way to disable specific builtin maps as
+# to avoid this file entirely.
+
+# For library symbols that can be defined in more than one header
+# file, maps from symbol-name to legitimate header files.
+# This list was generated via
+# grep -R '__.*_defined' /usr/include | perl -nle 'm,/usr/include/([^:]*):#\s*\S+ __(.*)_defined, and print qq@ { symbol: [ "$2", public, "<$1>", public ] },@' | sort -u
+# I ignored all entries that only appeared once on the list (eg uint32_t).
+# I then added in NULL, which according to [diff.null] C.2.2.3, can
+# be defined in <clocale>, <cstddef>, <cstdio>, <cstdlib>,
+# <cstring>, <ctime>, or <cwchar>. We also allow their C
+# equivalents.
+# In each case, I ordered them so <sys/types.h> was first, if it was
+# an option for this type. That's the preferred #include all else
+# equal. The same goes for <stdint.h>. The visibility on the
+# symbol-name is ignored; by convention we always set it to private.
+[
+ { symbol: [ "aiocb", private, "<aio.h>", public ] },
+ { symbol: [ "blkcnt_t", private, "<sys/types.h>", public ] },
+ { symbol: [ "blkcnt_t", private, "<sys/stat.h>", public ] },
+ { symbol: [ "blksize_t", private, "<sys/types.h>", public ] },
+ { symbol: [ "blksize_t", private, "<sys/stat.h>", public ] },
+ { symbol: [ "cc_t", private, "<termios.h>", private ] },
+ { symbol: [ "clock_t", private, "<sys/types.h>", public ] },
+ { symbol: [ "clock_t", private, "<sys/time.h>", public ] },
+ { symbol: [ "clock_t", private, "<time.h>", public ] },
+ { symbol: [ "clockid_t", private, "<sys/types.h>", public ] },
+ { symbol: [ "clockid_t", private, "<time.h>", public ] },
+ { symbol: [ "daddr_t", private, "<sys/types.h>", public ] },
+ { symbol: [ "daddr_t", private, "<rpc/types.h>", public ] },
+ { symbol: [ "dev_t", private, "<sys/types.h>", public ] },
+ { symbol: [ "dev_t", private, "<sys/stat.h>", public ] },
+ { symbol: [ "div_t", private, "<stdlib.h>", public ] },
+ { symbol: [ "double_t", private, "<math.h>", public ] },
+ { symbol: [ "error_t", private, "<errno.h>", public ] },
+ { symbol: [ "error_t", private, "<argp.h>", public ] },
+ { symbol: [ "error_t", private, "<argz.h>", public ] },
+ { symbol: [ "fd_set", private, "<sys/select.h>", public ] },
+ { symbol: [ "fd_set", private, "<sys/time.h>", public ] },
+ { symbol: [ "fenv_t", private, "<fenv.h>", public ] },
+ { symbol: [ "fexcept_t", private, "<fenv.h>", public ] },
+ { symbol: [ "FILE", private, "<stdio.h>", public ] },
+ { symbol: [ "FILE", private, "<wchar.h>", public ] },
+ { symbol: [ "float_t", private, "<math.h>", public ] },
+ { symbol: [ "fsblkcnt_t", private, "<sys/types.h>", public ] },
+ { symbol: [ "fsblkcnt_t", private, "<sys/statvfs.h>", public ] },
+ { symbol: [ "fsfilcnt_t", private, "<sys/types.h>", public ] },
+ { symbol: [ "fsfilcnt_t", private, "<sys/statvfs.h>", public ] },
+ { symbol: [ "getopt", private, "<unistd.h>", private ] },
+ { symbol: [ "gid_t", private, "<sys/types.h>", public ] },
+ { symbol: [ "gid_t", private, "<grp.h>", public ] },
+ { symbol: [ "gid_t", private, "<pwd.h>", public ] },
+ { symbol: [ "gid_t", private, "<signal.h>", public ] },
+ { symbol: [ "gid_t", private, "<stropts.h>", public ] },
+ { symbol: [ "gid_t", private, "<sys/ipc.h>", public ] },
+ { symbol: [ "gid_t", private, "<sys/stat.h>", public ] },
+ { symbol: [ "gid_t", private, "<unistd.h>", private ] },
+ { symbol: [ "htonl", private, "<arpa/inet.h>", private ] },
+ { symbol: [ "htons", private, "<arpa/inet.h>", private ] },
+ { symbol: [ "id_t", private, "<sys/types.h>", public ] },
+ { symbol: [ "id_t", private, "<sys/resource.h>", public ] },
+ { symbol: [ "imaxdiv_t", private, "<inttypes.h>", public ] },
+ { symbol: [ "intmax_t", private, "<stdint.h>", public ] },
+ { symbol: [ "uintmax_t", private, "<stdint.h>", public ] },
+ { symbol: [ "ino64_t", private, "<sys/types.h>", public ] },
+ { symbol: [ "ino64_t", private, "<dirent.h>", public ] },
+ { symbol: [ "ino_t", private, "<sys/types.h>", public ] },
+ { symbol: [ "ino_t", private, "<dirent.h>", public ] },
+ { symbol: [ "ino_t", private, "<sys/stat.h>", public ] },
+ { symbol: [ "int8_t", private, "<stdint.h>", public ] },
+ { symbol: [ "int16_t", private, "<stdint.h>", public ] },
+ { symbol: [ "int32_t", private, "<stdint.h>", public ] },
+ { symbol: [ "int64_t", private, "<stdint.h>", public ] },
+ { symbol: [ "uint8_t", private, "<stdint.h>", public ] },
+ { symbol: [ "uint16_t", private, "<stdint.h>", public ] },
+ { symbol: [ "uint32_t", private, "<stdint.h>", public ] },
+ { symbol: [ "uint64_t", private, "<stdint.h>", public ] },
+ { symbol: [ "intptr_t", private, "<stdint.h>", public ] },
+ { symbol: [ "uintptr_t", private, "<stdint.h>", public ] },
+ { symbol: [ "iovec", private, "<sys/uio.h>", public ] },
+ { symbol: [ "iovec", private, "<sys/socket.h>", private ] },
+ { symbol: [ "itimerspec", private, "<time.h>", public ] },
+ { symbol: [ "itimerspec", private, "<sys/timerfd.h>", public ] },
+ { symbol: [ "key_t", private, "<sys/types.h>", public ] },
+ { symbol: [ "key_t", private, "<sys/ipc.h>", public ] },
+ { symbol: [ "lconv", private, "<locale.h>", public ] },
+ { symbol: [ "ldiv_t", private, "<stdlib.h>", public ] },
+ { symbol: [ "lldiv_t", private, "<stdlib.h>", public ] },
+ { symbol: [ "max_align_t", private, "<stddef.h>", public ] },
+ { symbol: [ "mode_t", private, "<sys/types.h>", public ] },
+ { symbol: [ "mode_t", private, "<fcntl.h>", public ] },
+ { symbol: [ "mode_t", private, "<ndbm.h>", public ] },
+ { symbol: [ "mode_t", private, "<spawn.h>", public ] },
+ { symbol: [ "mode_t", private, "<sys/ipc.h>", public ] },
+ { symbol: [ "mode_t", private, "<sys/mman.h>", public ] },
+ { symbol: [ "mode_t", private, "<sys/stat.h>", public ] },
+ { symbol: [ "nlink_t", private, "<sys/types.h>", public ] },
+ { symbol: [ "nlink_t", private, "<sys/stat.h>", public ] },
+ { symbol: [ "ntohl", private, "<arpa/inet.h>", private ] },
+ { symbol: [ "ntohs", private, "<arpa/inet.h>", private ] },
+ { symbol: [ "off64_t", private, "<sys/types.h>", public ] },
+ { symbol: [ "off64_t", private, "<unistd.h>", private ] },
+ { symbol: [ "off_t", private, "<sys/types.h>", public ] },
+ { symbol: [ "off_t", private, "<aio.h>", public ] },
+ { symbol: [ "off_t", private, "<fcntl.h>", public ] },
+ { symbol: [ "off_t", private, "<stdio.h>", public ] },
+ { symbol: [ "off_t", private, "<sys/mman.h>", public ] },
+ { symbol: [ "off_t", private, "<sys/stat.h>", public ] },
+ { symbol: [ "off_t", private, "<unistd.h>", private ] },
+ { symbol: [ "optarg", private, "<unistd.h>", private ] },
+ { symbol: [ "opterr", private, "<unistd.h>", private ] },
+ { symbol: [ "optind", private, "<unistd.h>", private ] },
+ { symbol: [ "optopt", private, "<unistd.h>", private ] },
+ { symbol: [ "pid_t", private, "<sys/types.h>", public ] },
+ { symbol: [ "pid_t", private, "<fcntl.h>", public ] },
+ { symbol: [ "pid_t", private, "<sched.h>", public ] },
+ { symbol: [ "pid_t", private, "<signal.h>", public ] },
+ { symbol: [ "pid_t", private, "<spawn.h>", public ] },
+ { symbol: [ "pid_t", private, "<sys/msg.h>", public ] },
+ { symbol: [ "pid_t", private, "<sys/sem.h>", public ] },
+ { symbol: [ "pid_t", private, "<sys/shm.h>", public ] },
+ { symbol: [ "pid_t", private, "<sys/wait.h>", public ] },
+ { symbol: [ "pid_t", private, "<termios.h>", private ] },
+ { symbol: [ "pid_t", private, "<time.h>", public ] },
+ { symbol: [ "pid_t", private, "<unistd.h>", private ] },
+ { symbol: [ "pid_t", private, "<utmpx.h>", public ] },
+ { symbol: [ "ptrdiff_t", private, "<stddef.h>", public ] },
+ { symbol: [ "regex_t", private, "<regex.h>", public ] },
+ { symbol: [ "regmatch_t", private, "<regex.h>", public ] },
+ { symbol: [ "regoff_t", private, "<regex.h>", public ] },
+ { symbol: [ "sigevent", private, "<signal.h>", public ] },
+ { symbol: [ "sigevent", private, "<aio.h>", public ] },
+ { symbol: [ "sigevent", private, "<mqueue.h>", public ] },
+ { symbol: [ "sigevent", private, "<time.h>", public ] },
+ { symbol: [ "siginfo_t", private, "<signal.h>", public ] },
+ { symbol: [ "siginfo_t", private, "<sys/wait.h>", public ] },
+ { symbol: [ "sigset_t", private, "<signal.h>", public ] },
+ { symbol: [ "sigset_t", private, "<spawn.h>", public ] },
+ { symbol: [ "sigset_t", private, "<sys/select.h>", public ] },
+ { symbol: [ "sigval", private, "<signal.h>", public ] },
+ { symbol: [ "sockaddr", private, "<sys/socket.h>", private ] },
+ { symbol: [ "socklen_t", private, "<sys/socket.h>", private ] },
+ { symbol: [ "socklen_t", private, "<netdb.h>", private ] },
+ { symbol: [ "ssize_t", private, "<sys/types.h>", public ] },
+ { symbol: [ "ssize_t", private, "<aio.h>", public ] },
+ { symbol: [ "ssize_t", private, "<monetary.h>", public ] },
+ { symbol: [ "ssize_t", private, "<mqueue.h>", public ] },
+ { symbol: [ "ssize_t", private, "<stdio.h>", public ] },
+ { symbol: [ "ssize_t", private, "<sys/msg.h>", public ] },
+ { symbol: [ "ssize_t", private, "<sys/socket.h>", private ] },
+ { symbol: [ "ssize_t", private, "<sys/uio.h>", public ] },
+ { symbol: [ "ssize_t", private, "<unistd.h>", private ] },
+ { symbol: [ "stat", private, "<sys/stat.h>", public ] },
+ { symbol: [ "stat", private, "<ftw.h>", public ] },
+ { symbol: [ "suseconds_t", private, "<sys/types.h>", public ] },
+ { symbol: [ "suseconds_t", private, "<sys/select.h>", public ] },
+ { symbol: [ "suseconds_t", private, "<sys/time.h>", public ] },
+ { symbol: [ "time_t", private, "<time.h>", public ] },
+ { symbol: [ "time_t", private, "<sched.h>", public ] },
+ { symbol: [ "time_t", private, "<sys/msg.h>", public ] },
+ { symbol: [ "time_t", private, "<sys/select.h>", public ] },
+ { symbol: [ "time_t", private, "<sys/sem.h>", public ] },
+ { symbol: [ "time_t", private, "<sys/shm.h>", public ] },
+ { symbol: [ "time_t", private, "<sys/stat.h>", public ] },
+ { symbol: [ "time_t", private, "<sys/time.h>", public ] },
+ { symbol: [ "time_t", private, "<sys/types.h>", public ] },
+ { symbol: [ "time_t", private, "<utime.h>", public ] },
+ { symbol: [ "timer_t", private, "<sys/types.h>", public ] },
+ { symbol: [ "timer_t", private, "<time.h>", public ] },
+ { symbol: [ "timespec", private, "<time.h>", public ] },
+ { symbol: [ "timespec", private, "<aio.h>", public ] },
+ { symbol: [ "timespec", private, "<mqueue.h>", public ] },
+ { symbol: [ "timespec", private, "<sched.h>", public ] },
+ { symbol: [ "timespec", private, "<signal.h>", public ] },
+ { symbol: [ "timespec", private, "<sys/select.h>", public ] },
+ { symbol: [ "timespec", private, "<sys/stat.h>", public ] },
+ { symbol: [ "timeval", private, "<sys/time.h>", public ] },
+ { symbol: [ "timeval", private, "<sys/resource.h>", public ] },
+ { symbol: [ "timeval", private, "<sys/select.h>", public ] },
+ { symbol: [ "timeval", private, "<utmpx.h>", public ] },
+ { symbol: [ "tm", private, "<time.h>", public ] },
+ { symbol: [ "u_char", private, "<sys/types.h>", public ] },
+ { symbol: [ "u_char", private, "<rpc/types.h>", public ] },
+ { symbol: [ "uid_t", private, "<sys/types.h>", public ] },
+ { symbol: [ "uid_t", private, "<pwd.h>", public ] },
+ { symbol: [ "uid_t", private, "<signal.h>", public ] },
+ { symbol: [ "uid_t", private, "<stropts.h>", public ] },
+ { symbol: [ "uid_t", private, "<sys/ipc.h>", public ] },
+ { symbol: [ "uid_t", private, "<sys/stat.h>", public ] },
+ { symbol: [ "uid_t", private, "<unistd.h>", private ] },
+ { symbol: [ "useconds_t", private, "<sys/types.h>", public ] },
+ { symbol: [ "useconds_t", private, "<unistd.h>", private ] },
+ { symbol: [ "wchar_t", private, "<stddef.h>", public ] },
+ { symbol: [ "wchar_t", private, "<stdlib.h>", public ] },
+ { symbol: [ "size_t", private, "<stddef.h>", public ] },
+ { symbol: [ "size_t", private, "<aio.h>", public ] },
+ { symbol: [ "size_t", private, "<glob.h>", public ] },
+ { symbol: [ "size_t", private, "<grp.h>", public ] },
+ { symbol: [ "size_t", private, "<iconv.h>", public ] },
+ { symbol: [ "size_t", private, "<monetary.h>", public ] },
+ { symbol: [ "size_t", private, "<mqueue.h>", public ] },
+ { symbol: [ "size_t", private, "<ndbm.h>", public ] },
+ { symbol: [ "size_t", private, "<pwd.h>", public ] },
+ { symbol: [ "size_t", private, "<regex.h>", public ] },
+ { symbol: [ "size_t", private, "<search.h>", public ] },
+ { symbol: [ "size_t", private, "<signal.h>", public ] },
+ { symbol: [ "size_t", private, "<stdio.h>", public ] },
+ { symbol: [ "size_t", private, "<stdlib.h>", public ] },
+ { symbol: [ "size_t", private, "<string.h>", public ] },
+ { symbol: [ "size_t", private, "<strings.h>", public ] },
+ { symbol: [ "size_t", private, "<sys/mman.h>", public ] },
+ { symbol: [ "size_t", private, "<sys/msg.h>", public ] },
+ { symbol: [ "size_t", private, "<sys/sem.h>", public ] },
+ { symbol: [ "size_t", private, "<sys/shm.h>", public ] },
+ { symbol: [ "size_t", private, "<sys/socket.h>", private ] },
+ { symbol: [ "size_t", private, "<sys/types.h>", public ] },
+ { symbol: [ "size_t", private, "<sys/uio.h>", public ] },
+ { symbol: [ "size_t", private, "<time.h>", public ] },
+ { symbol: [ "size_t", private, "<uchar.h>", public ] },
+ { symbol: [ "size_t", private, "<unistd.h>", private ] },
+ { symbol: [ "size_t", private, "<wchar.h>", public ] },
+ { symbol: [ "size_t", private, "<wordexp.h>", public ] },
+ # Macros that can be defined in more than one file, don't have the
+ # same __foo_defined guard that other types do, so the grep above
+ # doesn't discover them. Until I figure out a better way, I just
+ # add them in by hand as I discover them.
+ { symbol: [ "EOF", private, "<stdio.h>", public ] },
+ { symbol: [ "EOF", private, "<libio.h>", public ] },
+ { symbol: [ "FILE", private, "<stdio.h>", public ] },
+ { symbol: [ "MAP_POPULATE", private, "<sys/mman.h>", public ] },
+ { symbol: [ "MAP_POPULATE", private, "<linux/mman.h>", public ] },
+ { symbol: [ "MAP_STACK", private, "<sys/mman.h>", public ] },
+ { symbol: [ "MAP_STACK", private, "<linux/mman.h>", public ] },
+ { symbol: [ "MAXHOSTNAMELEN", private, "<sys/param.h>", public ] },
+ { symbol: [ "MAXHOSTNAMELEN", private, "<protocols/timed.h>", public ] },
+ { symbol: [ "SIGABRT", private, "<signal.h>", public ] },
+ { symbol: [ "SIGCHLD", private, "<signal.h>", public ] },
+ { symbol: [ "SIGCHLD", private, "<linux/signal.h>", public ] },
+ { symbol: [ "va_list", private, "<stdarg.h>", public ] },
+ { symbol: [ "va_list", private, "<stdio.h>", public ] },
+ { symbol: [ "va_list", private, "<wchar.h>", public ] },
+ # These are symbols that could be defined in either stdlib.h or
+ # malloc.h, but we always want the stdlib location.
+ { symbol: [ "malloc", private, "<stdlib.h>", public ] },
+ { symbol: [ "calloc", private, "<stdlib.h>", public ] },
+ { symbol: [ "realloc", private, "<stdlib.h>", public ] },
+ { symbol: [ "free", private, "<stdlib.h>", public ] },
+ # Entries for NULL
+ { symbol: [ "NULL", private, "<stddef.h>", public ] }, # 'canonical' location for NULL
+ { symbol: [ "NULL", private, "<clocale>", public ] },
+ { symbol: [ "NULL", private, "<cstddef>", public ] },
+ { symbol: [ "NULL", private, "<cstdio>", public ] },
+ { symbol: [ "NULL", private, "<cstdlib>", public ] },
+ { symbol: [ "NULL", private, "<cstring>", public ] },
+ { symbol: [ "NULL", private, "<ctime>", public ] },
+ { symbol: [ "NULL", private, "<cwchar>", public ] },
+ { symbol: [ "NULL", private, "<locale.h>", public ] },
+ { symbol: [ "NULL", private, "<stdio.h>", public ] },
+ { symbol: [ "NULL", private, "<stdlib.h>", public ] },
+ { symbol: [ "NULL", private, "<string.h>", public ] },
+ { symbol: [ "NULL", private, "<time.h>", public ] },
+ { symbol: [ "NULL", private, "<unistd.h>", private ] },
+ { symbol: [ "NULL", private, "<wchar.h>", public ] },
+
+ # Kludge time: almost all STL types take an allocator, but they
+ # almost always use the default value. Usually we detect that
+ # and don't try to do IWYU, but sometimes it passes through.
+ # For instance, when adding two strings, we end up calling
+ # template<_CharT,_Traits,_Alloc> ... operator+(
+ # basic_string<_CharT,_Traits,_Alloc>, ...)
+ # These look like normal template args to us, so we see they're
+ # used and declare an iwyu dependency, even though we don't need
+ # to #include the traits or alloc type ourselves. The surest way
+ # to deal with this is to just say that everyone provides
+ # std::allocator. We can add more here at need.
+ { symbol: [ "std::allocator", private, "<memory>", public ] },
+ { symbol: [ "std::allocator", private, "<string>", public ] },
+ { symbol: [ "std::allocator", private, "<vector>", public ] },
+ { symbol: [ "std::allocator", private, "<map>", public ] },
+ { symbol: [ "std::allocator", private, "<set>", public ] },
+ # A similar kludge for std::char_traits. basic_string,
+ # basic_ostream and basic_istream have this as a default template
+ # argument, and sometimes it bleeds through when clang desugars the
+ # string/ostream/istream type.
+ { symbol: [ "std::char_traits", private, "<string>", public ] },
+ { symbol: [ "std::char_traits", private, "<ostream>", public ] },
+ { symbol: [ "std::char_traits", private, "<istream>", public ] },
+
+ { symbol: [ "std::size_t", private, "<cstddef>", public ] },
+ { symbol: [ "std::size_t", private, "<cstdio>", public ] },
+ { symbol: [ "std::size_t", private, "<cstdlib>", public ] },
+ { symbol: [ "std::size_t", private, "<cstring>", public ] },
+ { symbol: [ "std::size_t", private, "<ctime>", public ] },
+ { symbol: [ "std::size_t", private, "<cuchar>", public ] },
+ { symbol: [ "std::size_t", private, "<cwchar>", public ] }
+]
+
+# vim: set ft=toml:
diff --git a/cmake.config/iwyu/mapping.imp b/cmake.config/iwyu/mapping.imp
new file mode 100644
index 0000000000..4e55aa9875
--- /dev/null
+++ b/cmake.config/iwyu/mapping.imp
@@ -0,0 +1,238 @@
+[
+ # Generated to normal headers: header.h.generated.h -> nvim/header.h
+ { include: [ '"api/autocmd.h.generated.h"', private, '"nvim/api/autocmd.h"', public ] },
+ { include: [ '"api/buffer.h.generated.h"', private, '"nvim/api/buffer.h"', public ] },
+ { include: [ '"api/command.h.generated.h"', private, '"nvim/api/command.h"', public ] },
+ { include: [ '"api/deprecated.h.generated.h"', private, '"nvim/api/deprecated.h"', public ] },
+ { include: [ '"api/extmark.h.generated.h"', private, '"nvim/api/extmark.h"', public ] },
+ { include: [ '"api/options.h.generated.h"', private, '"nvim/api/options.h"', public ] },
+ { include: [ '"api/private/converter.h.generated.h"', private, '"nvim/api/private/converter.h"', public ] },
+ { include: [ '"api/private/dispatch.h.generated.h"', private, '"nvim/api/private/dispatch.h"', public ] },
+ { include: [ '"api/private/helpers.h.generated.h"', private, '"nvim/api/private/helpers.h"', public ] },
+ { include: [ '"api/tabpage.h.generated.h"', private, '"nvim/api/tabpage.h"', public ] },
+ { include: [ '"api/ui.h.generated.h"', private, '"nvim/api/ui.h"', public ] },
+ { include: [ '"api/vim.h.generated.h"', private, '"nvim/api/vim.h"', public ] },
+ { include: [ '"api/vimscript.h.generated.h"', private, '"nvim/api/vimscript.h"', public ] },
+ { include: [ '"api/win_config.h.generated.h"', private, '"nvim/api/win_config.h"', public ] },
+ { include: [ '"api/window.h.generated.h"', private, '"nvim/api/window.h"', public ] },
+ { include: [ '"arabic.h.generated.h"', private, '"nvim/arabic.h"', public ] },
+ { include: [ '"arglist.h.generated.h"', private, '"nvim/arglist.h"', public ] },
+ { include: [ '"autocmd.h.generated.h"', private, '"nvim/autocmd.h"', public ] },
+ { include: [ '"buffer.h.generated.h"', private, '"nvim/buffer.h"', public ] },
+ { include: [ '"buffer_updates.h.generated.h"', private, '"nvim/buffer_updates.h"', public ] },
+ { include: [ '"change.h.generated.h"', private, '"nvim/change.h"', public ] },
+ { include: [ '"channel.h.generated.h"', private, '"nvim/channel.h"', public ] },
+ { include: [ '"charset.h.generated.h"', private, '"nvim/charset.h"', public ] },
+ { include: [ '"cmdexpand.h.generated.h"', private, '"nvim/cmdexpand.h"', public ] },
+ { include: [ '"cmdhist.h.generated.h"', private, '"nvim/cmdhist.h"', public ] },
+ { include: [ '"context.h.generated.h"', private, '"nvim/context.h"', public ] },
+ { include: [ '"cursor.h.generated.h"', private, '"nvim/cursor.h"', public ] },
+ { include: [ '"cursor_shape.h.generated.h"', private, '"nvim/cursor_shape.h"', public ] },
+ { include: [ '"debugger.h.generated.h"', private, '"nvim/debugger.h"', public ] },
+ { include: [ '"decoration.h.generated.h"', private, '"nvim/decoration.h"', public ] },
+ { include: [ '"decoration_provider.h.generated.h"', private, '"nvim/decoration_provider.h"', public ] },
+ { include: [ '"diff.h.generated.h"', private, '"nvim/diff.h"', public ] },
+ { include: [ '"digraph.h.generated.h"', private, '"nvim/digraph.h"', public ] },
+ { include: [ '"drawline.h.generated.h"', private, '"nvim/drawline.h"', public ] },
+ { include: [ '"drawscreen.h.generated.h"', private, '"nvim/drawscreen.h"', public ] },
+ { include: [ '"edit.h.generated.h"', private, '"nvim/edit.h"', public ] },
+ { include: [ '"eval.h.generated.h"', private, '"nvim/eval.h"', public ] },
+ { include: [ '"eval/buffer.h.generated.h"', private, '"nvim/eval/buffer.h"', public ] },
+ { include: [ '"eval/decode.h.generated.h"', private, '"nvim/eval/decode.h"', public ] },
+ { include: [ '"eval/encode.h.generated.h"', private, '"nvim/eval/encode.h"', public ] },
+ { include: [ '"eval/executor.h.generated.h"', private, '"nvim/eval/executor.h"', public ] },
+ { include: [ '"eval/funcs.h.generated.h"', private, '"nvim/eval/funcs.h"', public ] },
+ { include: [ '"eval/typval.h.generated.h"', private, '"nvim/eval/typval.h"', public ] },
+ { include: [ '"eval/userfunc.h.generated.h"', private, '"nvim/eval/userfunc.h"', public ] },
+ { include: [ '"eval/vars.h.generated.h"', private, '"nvim/eval/vars.h"', public ] },
+ { include: [ '"eval/window.h.generated.h"', private, '"nvim/eval/window.h"', public ] },
+ { include: [ '"event/libuv_process.h.generated.h"', private, '"nvim/event/libuv_process.h"', public ] },
+ { include: [ '"event/loop.h.generated.h"', private, '"nvim/event/loop.h"', public ] },
+ { include: [ '"event/multiqueue.h.generated.h"', private, '"nvim/event/multiqueue.h"', public ] },
+ { include: [ '"event/process.h.generated.h"', private, '"nvim/event/process.h"', public ] },
+ { include: [ '"event/rstream.h.generated.h"', private, '"nvim/event/rstream.h"', public ] },
+ { include: [ '"event/signal.h.generated.h"', private, '"nvim/event/signal.h"', public ] },
+ { include: [ '"event/socket.h.generated.h"', private, '"nvim/event/socket.h"', public ] },
+ { include: [ '"event/stream.h.generated.h"', private, '"nvim/event/stream.h"', public ] },
+ { include: [ '"event/time.h.generated.h"', private, '"nvim/event/time.h"', public ] },
+ { include: [ '"event/wstream.h.generated.h"', private, '"nvim/event/wstream.h"', public ] },
+ { include: [ '"ex_cmds.h.generated.h"', private, '"nvim/ex_cmds.h"', public ] },
+ { include: [ '"ex_cmds2.h.generated.h"', private, '"nvim/ex_cmds2.h"', public ] },
+ { include: [ '"ex_docmd.h.generated.h"', private, '"nvim/ex_docmd.h"', public ] },
+ { include: [ '"ex_eval.h.generated.h"', private, '"nvim/ex_eval.h"', public ] },
+ { include: [ '"ex_getln.h.generated.h"', private, '"nvim/ex_getln.h"', public ] },
+ { include: [ '"ex_session.h.generated.h"', private, '"nvim/ex_session.h"', public ] },
+ { include: [ '"extmark.h.generated.h"', private, '"nvim/extmark.h"', public ] },
+ { include: [ '"file_search.h.generated.h"', private, '"nvim/file_search.h"', public ] },
+ { include: [ '"fileio.h.generated.h"', private, '"nvim/fileio.h"', public ] },
+ { include: [ '"fold.h.generated.h"', private, '"nvim/fold.h"', public ] },
+ { include: [ '"garray.h.generated.h"', private, '"nvim/garray.h"', public ] },
+ { include: [ '"getchar.h.generated.h"', private, '"nvim/getchar.h"', public ] },
+ { include: [ '"grid.h.generated.h"', private, '"nvim/grid.h"', public ] },
+ { include: [ '"hashtab.h.generated.h"', private, '"nvim/hashtab.h"', public ] },
+ { include: [ '"help.h.generated.h"', private, '"nvim/help.h"', public ] },
+ { include: [ '"highlight.h.generated.h"', private, '"nvim/highlight.h"', public ] },
+ { include: [ '"highlight_group.h.generated.h"', private, '"nvim/highlight_group.h"', public ] },
+ { include: [ '"if_cscope.h.generated.h"', private, '"nvim/if_cscope.h"', public ] },
+ { include: [ '"indent.h.generated.h"', private, '"nvim/indent.h"', public ] },
+ { include: [ '"indent_c.h.generated.h"', private, '"nvim/indent_c.h"', public ] },
+ { include: [ '"input.h.generated.h"', private, '"nvim/input.h"', public ] },
+ { include: [ '"insexpand.h.generated.h"', private, '"nvim/insexpand.h"', public ] },
+ { include: [ '"keycodes.h.generated.h"', private, '"nvim/keycodes.h"', public ] },
+ { include: [ '"linematch.h.generated.h"', private, '"nvim/linematch.h"', public ] },
+ { include: [ '"locale.h.generated.h"', private, '"nvim/locale.h"', public ] },
+ { include: [ '"log.h.generated.h"', private, '"nvim/log.h"', public ] },
+ { include: [ '"lua/converter.h.generated.h"', private, '"nvim/lua/converter.h"', public ] },
+ { include: [ '"lua/executor.h.generated.h"', private, '"nvim/lua/executor.h"', public ] },
+ { include: [ '"lua/spell.h.generated.h"', private, '"nvim/lua/spell.h"', public ] },
+ { include: [ '"lua/stdlib.h.generated.h"', private, '"nvim/lua/stdlib.h"', public ] },
+ { include: [ '"lua/treesitter.h.generated.h"', private, '"nvim/lua/treesitter.h"', public ] },
+ { include: [ '"lua/xdiff.h.generated.h"', private, '"nvim/lua/xdiff.h"', public ] },
+ { include: [ '"main.h.generated.h"', private, '"nvim/main.h"', public ] },
+ { include: [ '"mapping.h.generated.h"', private, '"nvim/mapping.h"', public ] },
+ { include: [ '"mark.h.generated.h"', private, '"nvim/mark.h"', public ] },
+ { include: [ '"marktree.h.generated.h"', private, '"nvim/marktree.h"', public ] },
+ { include: [ '"match.h.generated.h"', private, '"nvim/match.h"', public ] },
+ { include: [ '"math.h.generated.h"', private, '"nvim/math.h"', public ] },
+ { include: [ '"mbyte.h.generated.h"', private, '"nvim/mbyte.h"', public ] },
+ { include: [ '"memfile.h.generated.h"', private, '"nvim/memfile.h"', public ] },
+ { include: [ '"memline.h.generated.h"', private, '"nvim/memline.h"', public ] },
+ { include: [ '"memory.h.generated.h"', private, '"nvim/memory.h"', public ] },
+ { include: [ '"menu.h.generated.h"', private, '"nvim/menu.h"', public ] },
+ { include: [ '"message.h.generated.h"', private, '"nvim/message.h"', public ] },
+ { include: [ '"mouse.h.generated.h"', private, '"nvim/mouse.h"', public ] },
+ { include: [ '"move.h.generated.h"', private, '"nvim/move.h"', public ] },
+ { include: [ '"msgpack_rpc/channel.h.generated.h"', private, '"nvim/msgpack_rpc/channel.h"', public ] },
+ { include: [ '"msgpack_rpc/helpers.h.generated.h"', private, '"nvim/msgpack_rpc/helpers.h"', public ] },
+ { include: [ '"msgpack_rpc/server.h.generated.h"', private, '"nvim/msgpack_rpc/server.h"', public ] },
+ { include: [ '"msgpack_rpc/unpacker.h.generated.h"', private, '"nvim/msgpack_rpc/unpacker.h"', public ] },
+ { include: [ '"normal.h.generated.h"', private, '"nvim/normal.h"', public ] },
+ { include: [ '"ops.h.generated.h"', private, '"nvim/ops.h"', public ] },
+ { include: [ '"option.h.generated.h"', private, '"nvim/option.h"', public ] },
+ { include: [ '"optionstr.h.generated.h"', private, '"nvim/optionstr.h"', public ] },
+ { include: [ '"os/dl.h.generated.h"', private, '"nvim/os/dl.h"', public ] },
+ { include: [ '"os/fileio.h.generated.h"', private, '"nvim/os/fileio.h"', public ] },
+ { include: [ '"os/fs.h.generated.h"', private, '"nvim/os/fs.h"', public ] },
+ { include: [ '"os/input.h.generated.h"', private, '"nvim/os/input.h"', public ] },
+ { include: [ '"os/lang.h.generated.h"', private, '"nvim/os/lang.h"', public ] },
+ { include: [ '"os/process.h.generated.h"', private, '"nvim/os/process.h"', public ] },
+ { include: [ '"os/pty_process_unix.h.generated.h"', private, '"nvim/os/pty_process_unix.h"', private ] },
+ { include: [ '"os/shell.h.generated.h"', private, '"nvim/os/shell.h"', public ] },
+ { include: [ '"os/signal.h.generated.h"', private, '"nvim/os/signal.h"', public ] },
+ { include: [ '"os/time.h.generated.h"', private, '"nvim/os/time.h"', public ] },
+ { include: [ '"path.h.generated.h"', private, '"nvim/path.h"', public ] },
+ { include: [ '"plines.h.generated.h"', private, '"nvim/plines.h"', public ] },
+ { include: [ '"popupmenu.h.generated.h"', private, '"nvim/popupmenu.h"', public ] },
+ { include: [ '"profile.h.generated.h"', private, '"nvim/profile.h"', public ] },
+ { include: [ '"quickfix.h.generated.h"', private, '"nvim/quickfix.h"', public ] },
+ { include: [ '"rbuffer.h.generated.h"', private, '"nvim/rbuffer.h"', public ] },
+ { include: [ '"regexp.h.generated.h"', private, '"nvim/regexp.h"', public ] },
+ { include: [ '"runtime.h.generated.h"', private, '"nvim/runtime.h"', public ] },
+ { include: [ '"screen.h.generated.h"', private, '"nvim/screen.h"', public ] },
+ { include: [ '"search.h.generated.h"', private, '"nvim/search.h"', public ] },
+ { include: [ '"sha256.h.generated.h"', private, '"nvim/sha256.h"', public ] },
+ { include: [ '"shada.h.generated.h"', private, '"nvim/shada.h"', public ] },
+ { include: [ '"sign.h.generated.h"', private, '"nvim/sign.h"', public ] },
+ { include: [ '"spell.h.generated.h"', private, '"nvim/spell.h"', public ] },
+ { include: [ '"spellfile.h.generated.h"', private, '"nvim/spellfile.h"', public ] },
+ { include: [ '"spellsuggest.h.generated.h"', private, '"nvim/spellsuggest.h"', public ] },
+ { include: [ '"state.h.generated.h"', private, '"nvim/state.h"', public ] },
+ { include: [ '"statusline.h.generated.h"', private, '"nvim/statusline.h"', public ] },
+ { include: [ '"strings.h.generated.h"', private, '"nvim/strings.h"', public ] },
+ { include: [ '"syntax.h.generated.h"', private, '"nvim/syntax.h"', public ] },
+ { include: [ '"tag.h.generated.h"', private, '"nvim/tag.h"', public ] },
+ { include: [ '"terminal.h.generated.h"', private, '"nvim/terminal.h"', public ] },
+ { include: [ '"testing.h.generated.h"', private, '"nvim/testing.h"', public ] },
+ { include: [ '"textformat.h.generated.h"', private, '"nvim/textformat.h"', public ] },
+ { include: [ '"textobject.h.generated.h"', private, '"nvim/textobject.h"', public ] },
+ { include: [ '"tui/input.h.generated.h"', private, '"nvim/tui/input.h"', public ] },
+ { include: [ '"tui/terminfo.h.generated.h"', private, '"nvim/tui/terminfo.h"', public ] },
+ { include: [ '"tui/tui.h.generated.h"', private, '"nvim/tui/tui.h"', public ] },
+ { include: [ '"ugrid.h.generated.h"', private, '"nvim/ugrid.h"', public ] },
+ { include: [ '"ui.h.generated.h"', private, '"nvim/ui.h"', public ] },
+ { include: [ '"ui_bridge.h.generated.h"', private, '"nvim/ui_bridge.h"', public ] },
+ { include: [ '"ui_client.h.generated.h"', private, '"nvim/ui_client.h"', public ] },
+ { include: [ '"ui_compositor.h.generated.h"', private, '"nvim/ui_compositor.h"', public ] },
+ { include: [ '"undo.h.generated.h"', private, '"nvim/undo.h"', public ] },
+ { include: [ '"usercmd.h.generated.h"', private, '"nvim/usercmd.h"', public ] },
+ { include: [ '"version.h.generated.h"', private, '"nvim/version.h"', public ] },
+ { include: [ '"viml/parser/expressions.h.generated.h"', private, '"nvim/viml/parser/expressions.h"', public ] },
+ { include: [ '"viml/parser/parser.h.generated.h"', private, '"nvim/viml/parser/parser.h"', public ] },
+ { include: [ '"window.h.generated.h"', private, '"nvim/window.h"', public ] },
+
+ # Generated to normal headers with a different name: header.h.generated.h -> nvim/some_other_header.h
+ { include: [ '"api/private/dispatch_wrappers.h.generated.h"', private, '"nvim/api/private/dispatch.h"', public ] },
+ { include: [ '"auevents_enum.generated.h"', private, '"nvim/autocmd.h"', public ] },
+ { include: [ '"ex_cmds_enum.generated.h"', private, '"nvim/ex_cmds_defs.h"', public ] },
+ { include: [ '"keysets.h.generated.h"', private, '"nvim/api/private/helpers.h"', public ] },
+ { include: [ '"keysets_defs.generated.h"', private, '"nvim/api/private/defs.h"', public ] },
+ { include: [ '"nvim/os/pty_process_unix.h"', private, '"nvim/os/pty_process.h"', public ] },
+ { include: [ '"nvim/os/pty_process_win.h"', private, '"nvim/os/pty_process.h"', public ] },
+ { include: [ '"nvim/os/unix_defs.h"', private, '"nvim/os/os_defs.h"', public ] },
+ { include: [ '"nvim/os/win_defs.h"', private, '"nvim/os/os_defs.h"', public ] },
+ { include: [ '"os/env.h.generated.h"', private, '"nvim/os/os.h"', public ] },
+ { include: [ '"os/fs.h.generated.h"', private, '"nvim/os/os.h"', public ] },
+ { include: [ '"os/mem.h.generated.h"', private, '"nvim/os/os.h"', public ] },
+ { include: [ '"os/stdpaths.h.generated.h"', private, '"nvim/os/os.h"', public ] },
+ { include: [ '"os/users.h.generated.h"', private, '"nvim/os/os.h"', public ] },
+ { include: [ '"regexp_bt.h.generated.h"', private, '"nvim/regexp.h"', public ] },
+ { include: [ '"ui_events_call.h.generated.h"', private, '"nvim/ui.h"', public ] },
+ { include: [ '"ui_events_client.h.generated.h"', private, '"nvim/ui_client.h"', public ] },
+ { include: [ '"ui_events_remote.generated.h"', private, '"nvim/api/ui.h"', public ] },
+ { include: [ '"ui_events_remote.h.generated.h"', private, '"nvim/api/ui.h"', public ] },
+
+ # Def to normal headers: nvim/header_defs.h -> nvim/header.h
+ #
+ # This is a public to public mapping, meaning that while IWYU can use the
+ # headers on the left, it will use the headers on the right if possible. This
+ # isn't explicitly mentioned in the IWYU docs, this is just my interpretation
+ # of its behavior.
+ { include: [ '"nvim/buffer_defs.h"', public, '"nvim/buffer.h"', public ] },
+ { include: [ '"nvim/ex_cmds_defs.h"', public, '"nvim/ex_cmds.h"', public ] },
+ { include: [ '"nvim/ex_eval_defs.h"', public, '"nvim/ex_eval.h"', public ] },
+ { include: [ '"nvim/extmark_defs.h"', public, '"nvim/extmark.h"', public ] },
+ { include: [ '"nvim/grid_defs.h"', public, '"nvim/grid.h"', public ] },
+ { include: [ '"nvim/highlight_defs.h"', public, '"nvim/highlight.h"', public ] },
+ { include: [ '"nvim/map_defs.h"', public, '"nvim/map.h"', public ] },
+ { include: [ '"nvim/mark_defs.h"', public, '"nvim/mark.h"', public ] },
+ { include: [ '"nvim/mbyte_defs.h"', public, '"nvim/mbyte.h"', public ] },
+ { include: [ '"nvim/memfile_defs.h"', public, '"nvim/memfile.h"', public ] },
+ { include: [ '"nvim/memline_defs.h"', public, '"nvim/memline.h"', public ] },
+ { include: [ '"nvim/menu_defs.h"', public, '"nvim/menu.h"', public ] },
+ { include: [ '"nvim/msgpack/channel_defs.h"', public, '"nvim/msgpack/channel.h"', public ] },
+ { include: [ '"nvim/option_defs.h"', public, '"nvim/option.h"', public ] },
+ { include: [ '"nvim/os/fs_defs.h"', public, '"nvim/os/fs.h"', public ] },
+ { include: [ '"nvim/os/os_defs.h"', public, '"nvim/os/os.h"', public ] },
+ { include: [ '"nvim/regexp_defs.h"', public, '"nvim/regexp.h"', public ] },
+ { include: [ '"nvim/sign_defs.h"', public, '"nvim/sign.h"', public ] },
+ { include: [ '"nvim/spell_defs.h"', public, '"nvim/spell.h"', public ] },
+ { include: [ '"nvim/statusline_defs.h"', public, '"nvim/statusline.h"', public ] },
+ { include: [ '"nvim/syntax_defs.h"', public, '"nvim/syntax.h"', public ] },
+ { include: [ '"nvim/tui/input_defs.h"', public, '"nvim/tui/input.h"', public ] },
+ { include: [ '"nvim/undo_defs.h"', public, '"nvim/undo.h"', public ] },
+
+ # Third party headers
+ { include: [ "<bits/types/wint_t.h>", private, "<wchar.h>", public ] },
+ { include: [ '<arpa/inet.h>', private, '<uv/unix.h>', private ] },
+ { include: [ '<bits/termios-c_cc.h>', private, '<termios.h>', private ] },
+ { include: [ '<bits/termios-c_cflag.h>', private, '<termios.h>', private ] },
+ { include: [ '<bits/termios-c_iflag.h>', private, '<termios.h>', private ] },
+ { include: [ '<bits/termios-c_oflag.h>', private, '<termios.h>', private ] },
+ { include: [ '<libintl.h>', private, '"nvim/gettext.h"', public ] },
+ { include: [ '<netdb.h>', private, '<uv/unix.h>', private ] },
+ { include: [ '<netinet/in.h>', private, '<uv/unix.h>', private ] },
+ { include: [ '<pthread.h>', private, '"nvim/os/unix_defs.h"', private ] },
+ { include: [ '<sys/socket.h>', private, '<uv/unix.h>', private ] },
+ { include: [ '<termios.h>', private, '"nvim/os/unix_defs.h"', private ] },
+ { include: [ '<unistd.h>', private, '"nvim/os/unix_defs.h"', private ] },
+ { include: [ '<uv/unix.h>', private, '<uv.h>', public ] },
+
+ # Symbols
+ { symbol: [ "MAX", private, '"nvim/macros.h"', public ] },
+ { symbol: [ "MIN", private, '"nvim/macros.h"', public ] },
+ { symbol: [ "SEEK_END", private, '<stdio.h>', public ] },
+ { symbol: [ "SEEK_SET", private, '<stdio.h>', public ] },
+ { symbol: [ "time_fd", private, '"nvim/globals.h"', public ] },
+]
+
+# vim: set ft=toml:
diff --git a/cmake.config/pathdef.c.in b/cmake.config/pathdef.c.in
index 6a8a2b205a..5d6dfa6b9f 100644
--- a/cmake.config/pathdef.c.in
+++ b/cmake.config/pathdef.c.in
@@ -4,5 +4,5 @@
char *default_vim_dir = "${CMAKE_INSTALL_FULL_DATAROOTDIR}/nvim";
char *default_vimruntime_dir = "";
char *default_lib_dir = "${CMAKE_INSTALL_FULL_LIBDIR}/nvim";
-char_u *compiled_user = (char_u *)"${USERNAME}";
-char_u *compiled_sys = (char_u *)"${HOSTNAME}";
+char *compiled_user = "${USERNAME}";
+char *compiled_sys = "${HOSTNAME}";
diff --git a/cmake.deps/CMakeLists.txt b/cmake.deps/CMakeLists.txt
index 27659b2a56..a7e71ffc92 100644
--- a/cmake.deps/CMakeLists.txt
+++ b/cmake.deps/CMakeLists.txt
@@ -2,21 +2,29 @@
cmake_minimum_required (VERSION 3.10)
project(NVIM_DEPS C)
-# Needed for: check_c_compiler_flag()
-include(CheckCCompilerFlag)
+if(POLICY CMP0135)
+ cmake_policy(SET CMP0135 NEW)
+endif()
# Point CMake at any custom modules we may ship
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" "${PROJECT_SOURCE_DIR}/../cmake")
+include(CheckCCompilerFlag)
+include(Util)
+
+set(DEPS_CMAKE_ARGS
+ -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
+ -DCMAKE_GENERATOR=${CMAKE_GENERATOR}
+ -DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}
+ -DCMAKE_POSITION_INDEPENDENT_CODE=ON)
+
+set(DEPS_CMAKE_CACHE_ARGS -DCMAKE_OSX_ARCHITECTURES:STRING=${CMAKE_OSX_ARCHITECTURES})
+
+set_default_buildtype()
+
get_property(isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(NOT isMultiConfig)
- set(BUILD_TYPE_STRING -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE})
-endif()
-
-# In Windows/MSVC CMAKE_BUILD_TYPE changes the paths/linking of the build
-# recipes (libuv, msgpack), make sure it is set
-if(NOT CMAKE_BUILD_TYPE)
- set(CMAKE_BUILD_TYPE Release)
+ list(APPEND DEPS_CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE})
endif()
set(DEFAULT_MAKE_CFLAGS CFLAGS+=-g)
@@ -37,6 +45,8 @@ else()
set(DEPS_INSTALL_DIR "${CMAKE_BINARY_DIR}/usr" CACHE PATH "Dependencies install directory.")
endif()
+list(APPEND DEPS_CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR})
+
set(DEPS_BIN_DIR "${DEPS_INSTALL_DIR}/bin" CACHE PATH "Dependencies binary install directory.")
set(DEPS_LIB_DIR "${DEPS_INSTALL_DIR}/lib" CACHE PATH "Dependencies library install directory.")
set(DEPS_BUILD_DIR "${CMAKE_BINARY_DIR}/build" CACHE PATH "Dependencies build directory.")
@@ -75,14 +85,6 @@ endif()
if(UNIX)
find_program(MAKE_PRG NAMES gmake make)
- if(MAKE_PRG)
- execute_process(
- COMMAND "${MAKE_PRG}" --version
- OUTPUT_VARIABLE MAKE_VERSION_INFO)
- if(NOT "${OUTPUT_VARIABLE}" MATCHES ".*GNU.*")
- unset(MAKE_PRG)
- endif()
- endif()
if(NOT MAKE_PRG)
message(FATAL_ERROR "GNU Make is required to build the dependencies.")
else()
@@ -105,33 +107,18 @@ if(MINGW AND CMAKE_GENERATOR MATCHES "Ninja")
endif()
endif()
-if(CMAKE_C_COMPILER_ARG1)
- set(DEPS_C_COMPILER "${CMAKE_C_COMPILER} ${CMAKE_C_COMPILER_ARG1}")
-else()
- set(DEPS_C_COMPILER "${CMAKE_C_COMPILER}")
-endif()
-
-if(CMAKE_CXX_COMPILER)
- set(DEPS_CXX_COMPILER "${CMAKE_CXX_COMPILER}")
-endif()
+set(DEPS_C_COMPILER "${CMAKE_C_COMPILER}")
if(CMAKE_OSX_SYSROOT)
set(DEPS_C_COMPILER "${DEPS_C_COMPILER} -isysroot${CMAKE_OSX_SYSROOT}")
- if(DEPS_CXX_COMPILER)
- set(DEPS_CXX_COMPILER "${DEPS_CXX_COMPILER} -isysroot${CMAKE_OSX_SYSROOT}")
- endif()
endif()
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()
@@ -144,26 +131,21 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
OUTPUT_STRIP_TRAILING_WHITESPACE)
set(CMAKE_OSX_DEPLOYMENT_TARGET "${MACOS_VERSION}")
endif()
- message("-- Using deployment target ${CMAKE_OSX_DEPLOYMENT_TARGET}")
+ message(STATUS "Using deployment target ${CMAKE_OSX_DEPLOYMENT_TARGET}")
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_directory_properties(PROPERTIES EP_PREFIX "${DEPS_BUILD_DIR}")
-set(LIBUV_URL https://github.com/libuv/libuv/archive/v1.44.2.tar.gz)
-set(LIBUV_SHA256 e6e2ba8b4c349a4182a33370bb9be5e23c51b32efb9b9e209d0e8556b73a48da)
+set(LIBUV_URL https://github.com/libuv/libuv/archive/f610339f74f7f0fcd183533d2c965ce1468b44c6.tar.gz)
+set(LIBUV_SHA256 d5f22303ba44ac60d3232b1977b404d23a349ae4e8cb83f00e7122fafe38d8c9)
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/633f265f67f322cbe2c5fd11d3e46d968ac220f7.tar.gz)
-set(LUAJIT_SHA256 2681f0a6f624a64a8dfb70a5a377d494daf38960442c547d9c468674c1afa3c2)
+set(LUAJIT_URL https://github.com/LuaJIT/LuaJIT/archive/d0e88930ddde28ff662503f9f20facf34f7265aa.tar.gz)
+set(LUAJIT_SHA256 aa354d1265814db5a1ee9dfff6049e19b148e1fd818f1ecfa4fcf2b19f6e4dd9)
set(LUA_URL https://www.lua.org/ftp/lua-5.1.5.tar.gz)
set(LUA_SHA256 2640fc56a795f29d28ef15e13c34a47e223960b0240e8cb0a82d9b0738695333)
@@ -177,25 +159,23 @@ set(UNIBILIUM_SHA256 29815283c654277ef77a3adcc8840db79ddbb20a0f0b0c8f648bd8cd49a
set(LIBTERMKEY_URL https://www.leonerd.org.uk/code/libtermkey/libtermkey-0.22.tar.gz)
set(LIBTERMKEY_SHA256 6945bd3c4aaa83da83d80a045c5563da4edd7d0374c62c0d35aec09eb3014600)
-set(LIBVTERM_URL https://www.leonerd.org.uk/code/libvterm/libvterm-0.1.4.tar.gz)
-set(LIBVTERM_SHA256 bc70349e95559c667672fc8c55b9527d9db9ada0fb80a3beda533418d782d3dd)
+set(LIBVTERM_URL https://www.leonerd.org.uk/code/libvterm/libvterm-0.3.1.tar.gz)
+set(LIBVTERM_SHA256 25a8ad9c15485368dfd0a8a9dca1aec8fea5c27da3fa74ec518d5d3787f0c397)
-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(LUV_VERSION 1.44.2-1)
+set(LUV_URL https://github.com/luvit/luv/archive/80c8c00baebe3e994d1616d4b54097c2d6e14834.tar.gz)
+set(LUV_SHA256 bcd3ffc7bc80053ab0ec87f0fe83c7632c7619879b2eb75fba88ddb3bee620e8)
set(LUA_COMPAT53_URL https://github.com/keplerproject/lua-compat-5.3/archive/v0.9.tar.gz)
set(LUA_COMPAT53_SHA256 ad05540d2d96a48725bb79a1def35cf6652a4e2ec26376e2617c8ce2baa6f416)
# 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)
+set(WINTOOLS_URL https://github.com/neovim/deps/raw/c1e7dd8de9e1b18d11dcfa0a192cd029262e5303/opt/win32tools.zip)
+set(WINTOOLS_SHA256 3c4c490a3d392ceeb1347cb77cc821a31900b688a2189276d3a1131a3f21daf1)
-set(WINGUI_URL https://github.com/equalsraf/neovim-qt/releases/download/v0.2.16.1/neovim-qt.zip)
-set(WINGUI_SHA256 ddb4492db03da407703fb0ab271c4eb060250d1a7d71200e2b3b981cb0de59de)
+set(WINGUI_URL https://github.com/equalsraf/neovim-qt/releases/download/v0.2.17/neovim-qt.zip)
+set(WINGUI_SHA256 502e386eef677c2c2e0c11d8cbb27f3e12b4d96818369417e8da4129c4580c25)
-set(WIN32YANK_X86_URL https://github.com/equalsraf/win32yank/releases/download/v0.0.4/win32yank-x86.zip)
-set(WIN32YANK_X86_SHA256 62f34e5a46c5d4a7b3f3b512e1ff7b77fedd432f42581cbe825233a996eed62c)
set(WIN32YANK_X86_64_URL https://github.com/equalsraf/win32yank/releases/download/v0.0.4/win32yank-x64.zip)
set(WIN32YANK_X86_64_SHA256 33a747a92da60fb65e668edbf7661d3d902411a2d545fe9dc08623cecd142a20)
@@ -205,11 +185,20 @@ set(GETTEXT_SHA256 66415634c6e8c3fa8b71362879ec7575e27da43da562c798a8a2f223e6e47
set(LIBICONV_URL https://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.15.tar.gz)
set(LIBICONV_SHA256 ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc8913178)
-set(TREESITTER_C_URL https://github.com/tree-sitter/tree-sitter-c/archive/v0.20.1.tar.gz)
-set(TREESITTER_C_SHA256 ffcc2ef0eded59ad1acec9aec4f9b0c7dd209fc1a85d85f8b0e81298e3dddcc2)
+set(TREESITTER_C_URL https://github.com/tree-sitter/tree-sitter-c/archive/v0.20.2.tar.gz)
+set(TREESITTER_C_SHA256 af66fde03feb0df4faf03750102a0d265b007e5d957057b6b293c13116a70af2 )
-set(TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/1f1b1eb4501ed0a2d195d37f7de15f72aa10acd0.tar.gz)
-set(TREESITTER_SHA256 a324bdb7ff507cd5157a36b908224d60ba638f8e3363070dd51aa383c7cbd790)
+set(TREESITTER_LUA_URL https://github.com/MunifTanjim/tree-sitter-lua/archive/v0.0.14.tar.gz)
+set(TREESITTER_LUA_SHA256 930d0370dc15b66389869355c8e14305b9ba7aafd36edbfdb468c8023395016d)
+
+set(TREESITTER_VIM_URL https://github.com/vigoux/tree-sitter-viml/archive/55ff1b080c09edeced9b748cf4c16d0b49d17fb9.tar.gz)
+set(TREESITTER_VIM_SHA256 1b1cd39e33c8fb02fa7fe3977e844883c2a8508a7edd621f2d21e39a9aeefa92)
+
+set(TREESITTER_HELP_URL https://github.com/neovim/tree-sitter-vimdoc/archive/ce20f13c3f12506185754888feaae3f2ad54c287.tar.gz)
+set(TREESITTER_HELP_SHA256 2b8b166438cce66064aab56a744430b1f44871f43e47f70b51246d14bb826609)
+
+set(TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/eb970a83a107cbaba52e31588355c0b09b3a308a.tar.gz)
+set(TREESITTER_SHA256 58063721182f182fb9ec5481794f2ba594e52d6c2497e0214570535054dfc78d)
if(USE_BUNDLED_UNIBILIUM)
include(BuildUnibilium)
@@ -276,15 +265,8 @@ if(WIN32)
INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory bin ${DEPS_INSTALL_DIR}/bin
COMMAND ${CMAKE_COMMAND} -E copy_directory share ${DEPS_INSTALL_DIR}/share)
- include(TargetArch)
- GetBinaryDep(TARGET "win32yank_${TARGET_ARCH}"
+ GetBinaryDep(TARGET win32yank_X86_64
INSTALL_COMMAND ${CMAKE_COMMAND} -E copy win32yank.exe ${DEPS_INSTALL_DIR}/bin)
-
- if("${TARGET_ARCH}" STREQUAL "X86_64")
- set(TARGET_ARCH x64)
- elseif(TARGET_ARCH STREQUAL "X86")
- set(TARGET_ARCH ia32)
- endif()
endif()
# clean-shared-libraries removes ${DEPS_INSTALL_DIR}/lib/nvim/parser/c.dll,
diff --git a/cmake.deps/cmake/BuildGettext.cmake b/cmake.deps/cmake/BuildGettext.cmake
index f36c00c559..1f9fd38702 100644
--- a/cmake.deps/cmake/BuildGettext.cmake
+++ b/cmake.deps/cmake/BuildGettext.cmake
@@ -1,30 +1,19 @@
if(MSVC)
+ if(USE_EXISTING_SRC_DIR)
+ unset(GETTEXT_URL)
+ endif()
ExternalProject_Add(gettext
- PREFIX ${DEPS_BUILD_DIR}
URL ${GETTEXT_URL}
+ URL_HASH SHA256=${GETTEXT_SHA256}
+ DOWNLOAD_NO_PROGRESS TRUE
DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/gettext
- DOWNLOAD_COMMAND ${CMAKE_COMMAND}
- -DPREFIX=${DEPS_BUILD_DIR}
- -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/gettext
- -DURL=${GETTEXT_URL}
- -DEXPECTED_SHA256=${GETTEXT_SHA256}
- -DTARGET=gettext
- -DUSE_EXISTING_SRC_DIR=${USE_EXISTING_SRC_DIR}
- -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake
- CONFIGURE_COMMAND ${CMAKE_COMMAND} -E copy
+ PATCH_COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/cmake/GettextCMakeLists.txt
- ${DEPS_BUILD_DIR}/src/gettext/CMakeLists.txt
- COMMAND ${CMAKE_COMMAND} ${DEPS_BUILD_DIR}/src/gettext
- -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
- # Pass toolchain
- -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN}
- ${BUILD_TYPE_STRING}
- -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}
- INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config ${CMAKE_BUILD_TYPE})
+ ${DEPS_BUILD_DIR}/src/gettext/CMakeLists.txt
+ CMAKE_ARGS ${DEPS_CMAKE_ARGS}
+ -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}
+ CMAKE_CACHE_ARGS ${DEPS_CMAKE_CACHE_ARGS})
else()
message(FATAL_ERROR "Trying to build gettext in an unsupported system ${CMAKE_SYSTEM_NAME}/${CMAKE_C_COMPILER_ID}")
endif()
diff --git a/cmake.deps/cmake/BuildLibiconv.cmake b/cmake.deps/cmake/BuildLibiconv.cmake
index 434bfd6979..4b9c07ed6c 100644
--- a/cmake.deps/cmake/BuildLibiconv.cmake
+++ b/cmake.deps/cmake/BuildLibiconv.cmake
@@ -1,28 +1,17 @@
if(MSVC)
+ if(USE_EXISTING_SRC_DIR)
+ unset(LIBICONV_URL)
+ endif()
ExternalProject_Add(libiconv
- PREFIX ${DEPS_BUILD_DIR}
URL ${LIBICONV_URL}
+ URL_HASH SHA256=${LIBICONV_SHA256}
+ DOWNLOAD_NO_PROGRESS TRUE
DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/libiconv
- DOWNLOAD_COMMAND ${CMAKE_COMMAND}
- -DPREFIX=${DEPS_BUILD_DIR}
- -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/libiconv
- -DURL=${LIBICONV_URL}
- -DEXPECTED_SHA256=${LIBICONV_SHA256}
- -DTARGET=libiconv
- -DUSE_EXISTING_SRC_DIR=${USE_EXISTING_SRC_DIR}
- -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake
- CONFIGURE_COMMAND ${CMAKE_COMMAND} -E copy
+ PATCH_COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/cmake/LibiconvCMakeLists.txt
- ${DEPS_BUILD_DIR}/src/libiconv/CMakeLists.txt
- COMMAND ${CMAKE_COMMAND} ${DEPS_BUILD_DIR}/src/libiconv
- -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
- # Pass toolchain
- -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN}
- ${BUILD_TYPE_STRING}
- -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})
+ ${DEPS_BUILD_DIR}/src/libiconv/CMakeLists.txt
+ CMAKE_ARGS ${DEPS_CMAKE_ARGS}
+ CMAKE_CACHE_ARGS ${DEPS_CMAKE_CACHE_ARGS})
else()
message(FATAL_ERROR "Trying to build libiconv in an unsupported system ${CMAKE_SYSTEM_NAME}/${CMAKE_C_COMPILER_ID}")
endif()
diff --git a/cmake.deps/cmake/BuildLibtermkey.cmake b/cmake.deps/cmake/BuildLibtermkey.cmake
index 66bac57d23..6457a864ba 100644
--- a/cmake.deps/cmake/BuildLibtermkey.cmake
+++ b/cmake.deps/cmake/BuildLibtermkey.cmake
@@ -1,57 +1,18 @@
-if(WIN32)
+if(USE_EXISTING_SRC_DIR)
+ unset(LIBTERMKEY_URL)
+endif()
ExternalProject_Add(libtermkey
- PREFIX ${DEPS_BUILD_DIR}
URL ${LIBTERMKEY_URL}
+ URL_HASH SHA256=${LIBTERMKEY_SHA256}
+ DOWNLOAD_NO_PROGRESS TRUE
DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/libtermkey
- DOWNLOAD_COMMAND ${CMAKE_COMMAND}
- -DPREFIX=${DEPS_BUILD_DIR}
- -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/libtermkey
- -DURL=${LIBTERMKEY_URL}
- -DEXPECTED_SHA256=${LIBTERMKEY_SHA256}
- -DTARGET=libtermkey
- -DUSE_EXISTING_SRC_DIR=${USE_EXISTING_SRC_DIR}
- -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake
- CONFIGURE_COMMAND ${CMAKE_COMMAND} -E copy
+ PATCH_COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/cmake/libtermkeyCMakeLists.txt
- ${DEPS_BUILD_DIR}/src/libtermkey/CMakeLists.txt
- COMMAND ${CMAKE_COMMAND} ${DEPS_BUILD_DIR}/src/libtermkey
- -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
- # Pass toolchain
- -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN}
- ${BUILD_TYPE_STRING}
- # 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}
- INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config ${CMAKE_BUILD_TYPE})
-else()
-find_package(PkgConfig REQUIRED)
-
-ExternalProject_Add(libtermkey
- PREFIX ${DEPS_BUILD_DIR}
- URL ${LIBTERMKEY_URL}
- DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/libtermkey
- DOWNLOAD_COMMAND ${CMAKE_COMMAND}
- -DPREFIX=${DEPS_BUILD_DIR}
- -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/libtermkey
- -DURL=${LIBTERMKEY_URL}
- -DEXPECTED_SHA256=${LIBTERMKEY_SHA256}
- -DTARGET=libtermkey
- -DUSE_EXISTING_SRC_DIR=${USE_EXISTING_SRC_DIR}
- -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake
- CONFIGURE_COMMAND ""
- BUILD_IN_SOURCE 1
- BUILD_COMMAND ""
- INSTALL_COMMAND ${MAKE_PRG} CC=${DEPS_C_COMPILER}
- PREFIX=${DEPS_INSTALL_DIR}
- PKG_CONFIG_PATH=${DEPS_LIB_DIR}/pkgconfig
- CFLAGS=-fPIC
- LDFLAGS+=-static
- ${DEFAULT_MAKE_CFLAGS}
- install)
-endif()
+ ${DEPS_BUILD_DIR}/src/libtermkey/CMakeLists.txt
+ CMAKE_ARGS ${DEPS_CMAKE_ARGS}
+ -DCMAKE_SHARED_LIBRARY_LINK_C_FLAGS="" # Hack to avoid -rdynamic in Mingw
+ -DUNIBILIUM_INCLUDE_DIRS=${DEPS_INSTALL_DIR}/include
+ -DUNIBILIUM_LIBRARIES=${DEPS_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}unibilium${CMAKE_STATIC_LIBRARY_SUFFIX}
+ CMAKE_CACHE_ARGS ${DEPS_CMAKE_CACHE_ARGS})
list(APPEND THIRD_PARTY_DEPS libtermkey)
diff --git a/cmake.deps/cmake/BuildLibuv.cmake b/cmake.deps/cmake/BuildLibuv.cmake
index aee3b9a43f..eb88458644 100644
--- a/cmake.deps/cmake/BuildLibuv.cmake
+++ b/cmake.deps/cmake/BuildLibuv.cmake
@@ -1,26 +1,15 @@
+if(USE_EXISTING_SRC_DIR)
+ unset(LIBUV_URL)
+endif()
ExternalProject_Add(libuv
- PREFIX ${DEPS_BUILD_DIR}
URL ${LIBUV_URL}
- CMAKE_ARGS
- -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
+ URL_HASH SHA256=${LIBUV_SHA256}
+ DOWNLOAD_NO_PROGRESS TRUE
+ DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/libuv
+ CMAKE_ARGS ${DEPS_CMAKE_ARGS}
-DCMAKE_INSTALL_LIBDIR=lib
-DBUILD_TESTING=OFF
- -DCMAKE_POSITION_INDEPENDENT_CODE=ON
-DLIBUV_BUILD_SHARED=OFF
- CMAKE_CACHE_ARGS
- -DCMAKE_OSX_ARCHITECTURES:STRING=${CMAKE_OSX_ARCHITECTURES}
- DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/libuv
- DOWNLOAD_COMMAND ${CMAKE_COMMAND}
- -DPREFIX=${DEPS_BUILD_DIR}
- -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/libuv
- -DURL=${LIBUV_URL}
- -DEXPECTED_SHA256=${LIBUV_SHA256}
- -DTARGET=libuv
- -DUSE_EXISTING_SRC_DIR=${USE_EXISTING_SRC_DIR}
- -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake
- 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-shared.patch)
+ CMAKE_CACHE_ARGS ${DEPS_CMAKE_CACHE_ARGS})
list(APPEND THIRD_PARTY_DEPS libuv)
diff --git a/cmake.deps/cmake/BuildLibvterm.cmake b/cmake.deps/cmake/BuildLibvterm.cmake
index a905733abc..1578d56fba 100644
--- a/cmake.deps/cmake/BuildLibvterm.cmake
+++ b/cmake.deps/cmake/BuildLibvterm.cmake
@@ -1,43 +1,4 @@
-# BuildLibvterm(CONFIGURE_COMMAND ... BUILD_COMMAND ... INSTALL_COMMAND ...)
-# Failing to pass a command argument will result in no command being run
-function(BuildLibvterm)
- cmake_parse_arguments(_libvterm
- ""
- ""
- "PATCH_COMMAND;CONFIGURE_COMMAND;BUILD_COMMAND;INSTALL_COMMAND"
- ${ARGN})
-
- if(NOT _libvterm_CONFIGURE_COMMAND AND NOT _libvterm_BUILD_COMMAND
- AND NOT _libvterm_INSTALL_COMMAND)
- message(FATAL_ERROR "Must pass at least one of CONFIGURE_COMMAND, BUILD_COMMAND, INSTALL_COMMAND")
- endif()
-
- ExternalProject_Add(libvterm
- PREFIX ${DEPS_BUILD_DIR}
- URL ${LIBVTERM_URL}
- DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/libvterm
- DOWNLOAD_COMMAND ${CMAKE_COMMAND}
- -DPREFIX=${DEPS_BUILD_DIR}
- -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/libvterm
- -DURL=${LIBVTERM_URL}
- -DEXPECTED_SHA256=${LIBVTERM_SHA256}
- -DTARGET=libvterm
- -DUSE_EXISTING_SRC_DIR=${USE_EXISTING_SRC_DIR}
- -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake
- PATCH_COMMAND "${_libvterm_PATCH_COMMAND}"
- BUILD_IN_SOURCE 1
- CONFIGURE_COMMAND "${_libvterm_CONFIGURE_COMMAND}"
- BUILD_COMMAND "${_libvterm_BUILD_COMMAND}"
- INSTALL_COMMAND "${_libvterm_INSTALL_COMMAND}")
-endfunction()
-
if(WIN32)
- if(MSVC)
- set(LIBVTERM_PATCH_COMMAND
- ${GIT_EXECUTABLE} -C ${DEPS_BUILD_DIR}/src/libvterm init
- COMMAND ${GIT_EXECUTABLE} -C ${DEPS_BUILD_DIR}/src/libvterm apply --ignore-whitespace
- ${CMAKE_CURRENT_SOURCE_DIR}/patches/libvterm-Remove-VLAs-for-MSVC.patch)
- endif()
set(LIBVTERM_CONFIGURE_COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/cmake/LibvtermCMakeLists.txt
${DEPS_BUILD_DIR}/src/libvterm/CMakeLists.txt
@@ -48,15 +9,10 @@ if(WIN32)
-DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
-DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}
- -DCMAKE_GENERATOR=${CMAKE_GENERATOR})
-
- if(MSVC)
- list(APPEND LIBVTERM_CONFIGURE_COMMAND "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_COMPILER_ARG1}")
- else()
- list(APPEND LIBVTERM_CONFIGURE_COMMAND "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_COMPILER_ARG1} -fPIC")
- endif()
- set(LIBVTERM_BUILD_COMMAND ${CMAKE_COMMAND} --build . --config ${CMAKE_BUILD_TYPE})
- set(LIBVTERM_INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config ${CMAKE_BUILD_TYPE})
+ -DCMAKE_GENERATOR=${CMAKE_GENERATOR}
+ -DCMAKE_POSITION_INDEPENDENT_CODE=ON)
+ set(LIBVTERM_BUILD_COMMAND ${CMAKE_COMMAND} --build . --config $<CONFIG>)
+ set(LIBVTERM_INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config $<CONFIG>)
else()
set(LIBVTERM_INSTALL_COMMAND ${MAKE_PRG} CC=${DEPS_C_COMPILER}
PREFIX=${DEPS_INSTALL_DIR}
@@ -66,9 +22,19 @@ else()
install)
endif()
-BuildLibvterm(PATCH_COMMAND ${LIBVTERM_PATCH_COMMAND}
- CONFIGURE_COMMAND ${LIBVTERM_CONFIGURE_COMMAND}
- BUILD_COMMAND ${LIBVTERM_BUILD_COMMAND}
- INSTALL_COMMAND ${LIBVTERM_INSTALL_COMMAND})
+if(USE_EXISTING_SRC_DIR)
+ unset(LIBVTERM_URL)
+endif()
+ExternalProject_Add(libvterm
+ URL ${LIBVTERM_URL}
+ URL_HASH SHA256=${LIBVTERM_SHA256}
+ DOWNLOAD_NO_PROGRESS TRUE
+ DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/libvterm
+ BUILD_IN_SOURCE 1
+ CONFIGURE_COMMAND "${LIBVTERM_CONFIGURE_COMMAND}"
+ BUILD_COMMAND "${LIBVTERM_BUILD_COMMAND}"
+ INSTALL_COMMAND "${LIBVTERM_INSTALL_COMMAND}"
+ CMAKE_ARGS ${DEPS_CMAKE_ARGS}
+ CMAKE_CACHE_ARGS ${DEPS_CMAKE_CACHE_ARGS})
list(APPEND THIRD_PARTY_DEPS libvterm)
diff --git a/cmake.deps/cmake/BuildLua.cmake b/cmake.deps/cmake/BuildLua.cmake
index a40cb7dcb2..b5ac8368a6 100644
--- a/cmake.deps/cmake/BuildLua.cmake
+++ b/cmake.deps/cmake/BuildLua.cmake
@@ -1,36 +1,3 @@
-# BuildLua(CONFIGURE_COMMAND ... BUILD_COMMAND ... INSTALL_COMMAND ...)
-# Reusable function to build lua, wraps ExternalProject_Add.
-# Failing to pass a command argument will result in no command being run
-function(BuildLua)
- cmake_parse_arguments(_lua
- ""
- ""
- "CONFIGURE_COMMAND;BUILD_COMMAND;INSTALL_COMMAND"
- ${ARGN})
-
- if(NOT _lua_CONFIGURE_COMMAND AND NOT _lua_BUILD_COMMAND
- AND NOT _lua_INSTALL_COMMAND)
- message(FATAL_ERROR "Must pass at least one of CONFIGURE_COMMAND, BUILD_COMMAND, INSTALL_COMMAND")
- endif()
-
- ExternalProject_Add(lua
- PREFIX ${DEPS_BUILD_DIR}
- URL ${LUA_URL}
- DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/lua
- DOWNLOAD_COMMAND ${CMAKE_COMMAND}
- -DPREFIX=${DEPS_BUILD_DIR}
- -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/lua
- -DURL=${LUA_URL}
- -DEXPECTED_SHA256=${LUA_SHA256}
- -DTARGET=lua
- -DUSE_EXISTING_SRC_DIR=${USE_EXISTING_SRC_DIR}
- -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake
- CONFIGURE_COMMAND "${_lua_CONFIGURE_COMMAND}"
- BUILD_IN_SOURCE 1
- BUILD_COMMAND "${_lua_BUILD_COMMAND}"
- INSTALL_COMMAND "${_lua_INSTALL_COMMAND}")
-endfunction()
-
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(LUA_TARGET linux)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
@@ -61,7 +28,7 @@ if(CLANG_ASAN_UBSAN)
endif()
set(LUA_CONFIGURE_COMMAND
- sed -e "/^CC/s@gcc@${CMAKE_C_COMPILER} ${CMAKE_C_COMPILER_ARG1}@"
+ sed -e "/^CC/s@gcc@${CMAKE_C_COMPILER}@"
-e "/^CFLAGS/s@-O2@${LUA_CFLAGS}@"
-e "/^MYLDFLAGS/s@$@${LUA_LDFLAGS}@"
-e "s@-lreadline@@g"
@@ -72,16 +39,22 @@ set(LUA_CONFIGURE_COMMAND
-e "s@\\(#define LUA_ROOT[ ]*\"\\)/usr/local@\\1${DEPS_INSTALL_DIR}@"
-i ${DEPS_BUILD_DIR}/src/lua/src/luaconf.h)
set(LUA_INSTALL_TOP_ARG "INSTALL_TOP=${DEPS_INSTALL_DIR}")
-set(LUA_BUILD_COMMAND
- ${MAKE_PRG} ${LUA_INSTALL_TOP_ARG} ${LUA_TARGET})
-set(LUA_INSTALL_COMMAND
- ${MAKE_PRG} ${LUA_INSTALL_TOP_ARG} install)
message(STATUS "Lua target is ${LUA_TARGET}")
-BuildLua(CONFIGURE_COMMAND ${LUA_CONFIGURE_COMMAND}
- BUILD_COMMAND ${LUA_BUILD_COMMAND}
- INSTALL_COMMAND ${LUA_INSTALL_COMMAND})
+if(USE_EXISTING_SRC_DIR)
+ unset(LUA_URL)
+endif()
+ExternalProject_Add(lua
+ URL ${LUA_URL}
+ URL_HASH SHA256=${LUA_SHA256}
+ DOWNLOAD_NO_PROGRESS TRUE
+ DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/lua
+ CONFIGURE_COMMAND "${LUA_CONFIGURE_COMMAND}"
+ BUILD_IN_SOURCE 1
+ BUILD_COMMAND ${MAKE_PRG} ${LUA_INSTALL_TOP_ARG} ${LUA_TARGET}
+ INSTALL_COMMAND ${MAKE_PRG} ${LUA_INSTALL_TOP_ARG} install)
+
list(APPEND THIRD_PARTY_DEPS lua)
set(BUSTED ${DEPS_INSTALL_DIR}/bin/busted)
diff --git a/cmake.deps/cmake/BuildLuajit.cmake b/cmake.deps/cmake/BuildLuajit.cmake
index 9b393310d6..1476ac31f4 100644
--- a/cmake.deps/cmake/BuildLuajit.cmake
+++ b/cmake.deps/cmake/BuildLuajit.cmake
@@ -15,18 +15,14 @@ function(BuildLuajit)
set(_luajit_TARGET "luajit")
endif()
+ if(USE_EXISTING_SRC_DIR)
+ unset(LUAJIT_URL)
+ endif()
ExternalProject_Add(${_luajit_TARGET}
- PREFIX ${DEPS_BUILD_DIR}
URL ${LUAJIT_URL}
+ URL_HASH SHA256=${LUAJIT_SHA256}
+ DOWNLOAD_NO_PROGRESS TRUE
DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/luajit
- DOWNLOAD_COMMAND ${CMAKE_COMMAND}
- -DPREFIX=${DEPS_BUILD_DIR}
- -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/luajit
- -DURL=${LUAJIT_URL}
- -DEXPECTED_SHA256=${LUAJIT_SHA256}
- -DTARGET=${_luajit_TARGET}
- -DUSE_EXISTING_SRC_DIR=${USE_EXISTING_SRC_DIR}
- -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake
CONFIGURE_COMMAND "${_luajit_CONFIGURE_COMMAND}"
BUILD_IN_SOURCE 1
BUILD_COMMAND "${_luajit_BUILD_COMMAND}"
diff --git a/cmake.deps/cmake/BuildLuarocks.cmake b/cmake.deps/cmake/BuildLuarocks.cmake
index 73f3331176..d0b4a8e7d1 100644
--- a/cmake.deps/cmake/BuildLuarocks.cmake
+++ b/cmake.deps/cmake/BuildLuarocks.cmake
@@ -1,55 +1,18 @@
-# Luarocks recipe. Luarocks 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.
-#
+# Luarocks recipe. Luarocks is only required when building Neovim.
# NOTE: LuaRocks rocks need to "DEPENDS" on the previous module, because
# running luarocks in parallel will break, e.g. when some rocks have
# the same dependency..
option(USE_BUNDLED_BUSTED "Use the bundled version of busted to run tests." ON)
-# BuildLuarocks(CONFIGURE_COMMAND ... BUILD_COMMAND ... INSTALL_COMMAND ...)
-# Reusable function to build luarocks, wraps ExternalProject_Add.
-# Failing to pass a command argument will result in no command being run
-function(BuildLuarocks)
- cmake_parse_arguments(_luarocks
- ""
- ""
- "CONFIGURE_COMMAND;BUILD_COMMAND;INSTALL_COMMAND"
- ${ARGN})
-
- if(NOT _luarocks_CONFIGURE_COMMAND AND NOT _luarocks_BUILD_COMMAND
- AND NOT _luarocks_INSTALL_COMMAND)
- message(FATAL_ERROR "Must pass at least one of CONFIGURE_COMMAND, BUILD_COMMAND, INSTALL_COMMAND")
- endif()
-
- ExternalProject_Add(luarocks
- PREFIX ${DEPS_BUILD_DIR}
- URL ${LUAROCKS_URL}
- DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/luarocks
- DOWNLOAD_COMMAND ${CMAKE_COMMAND}
- -DPREFIX=${DEPS_BUILD_DIR}
- -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/luarocks
- -DURL=${LUAROCKS_URL}
- -DEXPECTED_SHA256=${LUAROCKS_SHA256}
- -DTARGET=luarocks
- -DUSE_EXISTING_SRC_DIR=${USE_EXISTING_SRC_DIR}
- -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake
- BUILD_IN_SOURCE 1
- CONFIGURE_COMMAND "${_luarocks_CONFIGURE_COMMAND}"
- BUILD_COMMAND "${_luarocks_BUILD_COMMAND}"
- INSTALL_COMMAND "${_luarocks_INSTALL_COMMAND}")
-endfunction()
-
# The luarocks binary location
-set(LUAROCKS_BINARY ${HOSTDEPS_BIN_DIR}/luarocks)
+set(LUAROCKS_BINARY ${DEPS_BIN_DIR}/luarocks)
# Arguments for calls to 'luarocks build'
if(NOT MSVC)
# In MSVC don't pass the compiler/linker to luarocks, the bundled
# version already knows, and passing them here breaks the build
- set(LUAROCKS_BUILDARGS CC=${HOSTDEPS_C_COMPILER} LD=${HOSTDEPS_C_COMPILER})
+ set(LUAROCKS_BUILDARGS CC=${DEPS_C_COMPILER} LD=${DEPS_C_COMPILER})
endif()
# Lua version, used with rocks directories.
@@ -60,12 +23,12 @@ if(UNIX)
if(USE_BUNDLED_LUAJIT)
list(APPEND LUAROCKS_OPTS
- --with-lua=${HOSTDEPS_INSTALL_DIR}
- --with-lua-include=${HOSTDEPS_INSTALL_DIR}/include/luajit-2.1
+ --with-lua=${DEPS_INSTALL_DIR}
+ --with-lua-include=${DEPS_INSTALL_DIR}/include/luajit-2.1
--with-lua-interpreter=luajit)
elseif(USE_BUNDLED_LUA)
list(APPEND LUAROCKS_OPTS
- --with-lua=${HOSTDEPS_INSTALL_DIR})
+ --with-lua=${DEPS_INSTALL_DIR})
else()
find_package(LuaJit)
if(LUAJIT_FOUND)
@@ -90,10 +53,9 @@ if(UNIX)
endif()
endif()
- BuildLuarocks(
- CONFIGURE_COMMAND ${DEPS_BUILD_DIR}/src/luarocks/configure
- --prefix=${HOSTDEPS_INSTALL_DIR} --force-config ${LUAROCKS_OPTS}
- INSTALL_COMMAND ${MAKE_PRG} -j1 bootstrap)
+ set(LUAROCKS_CONFIGURE_COMMAND ${DEPS_BUILD_DIR}/src/luarocks/configure
+ --prefix=${DEPS_INSTALL_DIR} --force-config ${LUAROCKS_OPTS})
+ set(LUAROCKS_INSTALL_COMMAND ${MAKE_PRG} -j1 bootstrap)
elseif(MSVC OR MINGW)
if(MINGW)
@@ -103,7 +65,7 @@ elseif(MSVC OR MINGW)
endif()
# Ignore USE_BUNDLED_LUAJIT - always ON for native Win32
- BuildLuarocks(INSTALL_COMMAND install.bat /FORCECONFIG /NOREG /NOADMIN /Q /F
+ set(LUAROCKS_INSTALL_COMMAND install.bat /FORCECONFIG /NOREG /NOADMIN /Q /F
/LUA ${DEPS_INSTALL_DIR}
/LIB ${DEPS_LIB_DIR}
/BIN ${DEPS_BIN_DIR}
@@ -119,6 +81,19 @@ else()
message(FATAL_ERROR "Trying to build luarocks in an unsupported system ${CMAKE_SYSTEM_NAME}/${CMAKE_C_COMPILER_ID}")
endif()
+if(USE_EXISTING_SRC_DIR)
+ unset(LUAROCKS_URL)
+endif()
+ExternalProject_Add(luarocks
+ URL ${LUAROCKS_URL}
+ URL_HASH SHA256=${LUAROCKS_SHA256}
+ DOWNLOAD_NO_PROGRESS TRUE
+ DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/luarocks
+ BUILD_IN_SOURCE 1
+ CONFIGURE_COMMAND "${LUAROCKS_CONFIGURE_COMMAND}"
+ BUILD_COMMAND ""
+ INSTALL_COMMAND "${LUAROCKS_INSTALL_COMMAND}")
+
list(APPEND THIRD_PARTY_DEPS luarocks)
if(USE_BUNDLED_LUAJIT)
@@ -126,20 +101,18 @@ if(USE_BUNDLED_LUAJIT)
elseif(USE_BUNDLED_LUA)
add_dependencies(luarocks lua)
endif()
-set(ROCKS_DIR ${HOSTDEPS_LIB_DIR}/luarocks/rocks-${LUA_VERSION})
+set(ROCKS_DIR ${DEPS_LIB_DIR}/luarocks/rocks-${LUA_VERSION})
# mpack
add_custom_command(OUTPUT ${ROCKS_DIR}/mpack
- COMMAND ${LUAROCKS_BINARY}
- ARGS build mpack 1.0.8-0 ${LUAROCKS_BUILDARGS}
+ COMMAND ${LUAROCKS_BINARY} build mpack 1.0.8-0 ${LUAROCKS_BUILDARGS}
DEPENDS luarocks)
add_custom_target(mpack DEPENDS ${ROCKS_DIR}/mpack)
list(APPEND THIRD_PARTY_DEPS mpack)
# lpeg
add_custom_command(OUTPUT ${ROCKS_DIR}/lpeg
- COMMAND ${LUAROCKS_BINARY}
- ARGS build lpeg 1.0.2-1 ${LUAROCKS_BUILDARGS}
+ COMMAND ${LUAROCKS_BINARY} build lpeg 1.0.2-1 ${LUAROCKS_BUILDARGS}
DEPENDS mpack)
add_custom_target(lpeg DEPENDS ${ROCKS_DIR}/lpeg)
list(APPEND THIRD_PARTY_DEPS lpeg)
@@ -147,8 +120,7 @@ list(APPEND THIRD_PARTY_DEPS lpeg)
if((NOT USE_BUNDLED_LUAJIT) AND USE_BUNDLED_LUA)
# luabitop
add_custom_command(OUTPUT ${ROCKS_DIR}/luabitop
- COMMAND ${LUAROCKS_BINARY}
- ARGS build luabitop 1.0.2-3 ${LUAROCKS_BUILDARGS}
+ COMMAND ${LUAROCKS_BINARY} build luabitop 1.0.2-3 ${LUAROCKS_BUILDARGS}
DEPENDS lpeg)
add_custom_target(luabitop DEPENDS ${ROCKS_DIR}/luabitop)
list(APPEND THIRD_PARTY_DEPS luabitop)
@@ -163,29 +135,26 @@ if(USE_BUNDLED_BUSTED)
# penlight
add_custom_command(OUTPUT ${ROCKS_DIR}/penlight
- COMMAND ${LUAROCKS_BINARY}
- ARGS build penlight 1.5.4-1 ${LUAROCKS_BUILDARGS}
+ COMMAND ${LUAROCKS_BINARY} build penlight 1.5.4-1 ${LUAROCKS_BUILDARGS}
DEPENDS ${PENLIGHT_DEPENDS})
add_custom_target(penlight DEPENDS ${ROCKS_DIR}/penlight)
# busted
if(WIN32)
- set(BUSTED_EXE "${HOSTDEPS_BIN_DIR}/busted.bat")
- set(LUACHECK_EXE "${HOSTDEPS_BIN_DIR}/luacheck.bat")
+ set(BUSTED_EXE "${DEPS_BIN_DIR}/busted.bat")
+ set(LUACHECK_EXE "${DEPS_BIN_DIR}/luacheck.bat")
else()
- set(BUSTED_EXE "${HOSTDEPS_BIN_DIR}/busted")
- set(LUACHECK_EXE "${HOSTDEPS_BIN_DIR}/luacheck")
+ set(BUSTED_EXE "${DEPS_BIN_DIR}/busted")
+ set(LUACHECK_EXE "${DEPS_BIN_DIR}/luacheck")
endif()
add_custom_command(OUTPUT ${BUSTED_EXE}
- COMMAND ${LUAROCKS_BINARY}
- ARGS build busted 2.0.0 ${LUAROCKS_BUILDARGS}
+ COMMAND ${LUAROCKS_BINARY} build busted 2.0.0 ${LUAROCKS_BUILDARGS}
DEPENDS penlight)
add_custom_target(busted DEPENDS ${BUSTED_EXE})
# luacheck
add_custom_command(OUTPUT ${LUACHECK_EXE}
- COMMAND ${LUAROCKS_BINARY}
- ARGS build luacheck 0.23.0-1 ${LUAROCKS_BUILDARGS}
+ COMMAND ${LUAROCKS_BINARY} build luacheck 0.23.0-1 ${LUAROCKS_BUILDARGS}
DEPENDS busted)
add_custom_target(luacheck DEPENDS ${LUACHECK_EXE})
@@ -195,8 +164,7 @@ if(USE_BUNDLED_BUSTED)
set(NVIM_CLIENT_DEPS luacheck luv-static lua-compat-5.3)
else()
add_custom_command(OUTPUT ${ROCKS_DIR}/luv
- COMMAND ${LUAROCKS_BINARY}
- ARGS build luv ${LUV_VERSION} ${LUAROCKS_BUILDARGS}
+ COMMAND ${LUAROCKS_BINARY} build luv ${LUV_VERSION} ${LUAROCKS_BUILDARGS}
DEPENDS luacheck)
add_custom_target(luv DEPENDS ${ROCKS_DIR}/luv)
set(NVIM_CLIENT_DEPS luv)
@@ -204,8 +172,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.4-1 ${LUAROCKS_BUILDARGS}
+ COMMAND ${LUAROCKS_BINARY} build nvim-client 0.2.4-1 ${LUAROCKS_BUILDARGS}
DEPENDS ${NVIM_CLIENT_DEPS})
add_custom_target(nvim-client DEPENDS ${ROCKS_DIR}/nvim-client)
diff --git a/cmake.deps/cmake/BuildLuv.cmake b/cmake.deps/cmake/BuildLuv.cmake
index f960b24992..38c0503c5b 100644
--- a/cmake.deps/cmake/BuildLuv.cmake
+++ b/cmake.deps/cmake/BuildLuv.cmake
@@ -1,67 +1,7 @@
-# BuildLuv(PATCH_COMMAND ... CONFIGURE_COMMAND ... BUILD_COMMAND ... INSTALL_COMMAND ...)
-# Reusable function to build luv, wraps ExternalProject_Add.
-# Failing to pass a command argument will result in no command being run
-function(BuildLuv)
- cmake_parse_arguments(_luv
- ""
- ""
- "PATCH_COMMAND;CONFIGURE_COMMAND;BUILD_COMMAND;INSTALL_COMMAND"
- ${ARGN})
-
- if(NOT _luv_CONFIGURE_COMMAND AND NOT _luv_BUILD_COMMAND
- AND NOT _luv_INSTALL_COMMAND)
- message(FATAL_ERROR "Must pass at least one of CONFIGURE_COMMAND, BUILD_COMMAND, INSTALL_COMMAND")
- endif()
-
- ExternalProject_Add(lua-compat-5.3
- PREFIX ${DEPS_BUILD_DIR}
- URL ${LUA_COMPAT53_URL}
- DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/lua-compat-5.3
- DOWNLOAD_COMMAND ${CMAKE_COMMAND}
- -DPREFIX=${DEPS_BUILD_DIR}
- -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/lua-compat-5.3
- -DURL=${LUA_COMPAT53_URL}
- -DEXPECTED_SHA256=${LUA_COMPAT53_SHA256}
- -DTARGET=lua-compat-5.3
- -DUSE_EXISTING_SRC_DIR=${USE_EXISTING_SRC_DIR}
- -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake
- PATCH_COMMAND ""
- CONFIGURE_COMMAND ""
- BUILD_COMMAND ""
- INSTALL_COMMAND "")
-
- ExternalProject_Add(luv-static
- PREFIX ${DEPS_BUILD_DIR}
- DEPENDS lua-compat-5.3
- URL ${LUV_URL}
- DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/luv
- DOWNLOAD_COMMAND ${CMAKE_COMMAND}
- -DPREFIX=${DEPS_BUILD_DIR}
- -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/luv
- -DURL=${LUV_URL}
- -DEXPECTED_SHA256=${LUV_SHA256}
- -DTARGET=luv-static
- # The source is shared with BuildLuarocks (with USE_BUNDLED_LUV).
- -DSRC_DIR=${DEPS_BUILD_DIR}/src/luv
- -DUSE_EXISTING_SRC_DIR=${USE_EXISTING_SRC_DIR}
- -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake
- PATCH_COMMAND "${_luv_PATCH_COMMAND}"
- CONFIGURE_COMMAND "${_luv_CONFIGURE_COMMAND}"
- BUILD_COMMAND "${_luv_BUILD_COMMAND}"
- INSTALL_COMMAND "${_luv_INSTALL_COMMAND}"
- LIST_SEPARATOR |)
-endfunction()
-
-set(LUV_SRC_DIR ${DEPS_BUILD_DIR}/src/luv)
set(LUV_INCLUDE_FLAGS
"-I${DEPS_INSTALL_DIR}/include -I${DEPS_INSTALL_DIR}/include/luajit-2.1")
-set(LUV_CONFIGURE_COMMAND_COMMON
- ${CMAKE_COMMAND} ${LUV_SRC_DIR}
- -DCMAKE_GENERATOR=${CMAKE_GENERATOR}
- ${BUILD_TYPE_STRING}
- -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
- -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES_ALT_SEP}
+set(LUV_CMAKE_ARGS
-DLUA_BUILD_TYPE=System
-DLUA_COMPAT53_DIR=${DEPS_BUILD_DIR}/src/lua-compat-5.3
-DWITH_SHARED_LIBUV=ON
@@ -70,55 +10,55 @@ set(LUV_CONFIGURE_COMMAND_COMMON
-DBUILD_MODULE=OFF)
if(USE_BUNDLED_LUAJIT)
- list(APPEND LUV_CONFIGURE_COMMAND_COMMON -DWITH_LUA_ENGINE=LuaJit)
+ list(APPEND LUV_CMAKE_ARGS -DWITH_LUA_ENGINE=LuaJit)
elseif(USE_BUNDLED_LUA)
- list(APPEND LUV_CONFIGURE_COMMAND_COMMON -DWITH_LUA_ENGINE=Lua)
+ list(APPEND LUV_CMAKE_ARGS -DWITH_LUA_ENGINE=Lua)
else()
find_package(LuaJit)
if(LUAJIT_FOUND)
- list(APPEND LUV_CONFIGURE_COMMAND_COMMON -DWITH_LUA_ENGINE=LuaJit)
+ list(APPEND LUV_CMAKE_ARGS -DWITH_LUA_ENGINE=LuaJit)
else()
- list(APPEND LUV_CONFIGURE_COMMAND_COMMON -DWITH_LUA_ENGINE=Lua)
+ list(APPEND LUV_CMAKE_ARGS -DWITH_LUA_ENGINE=Lua)
endif()
endif()
if(USE_BUNDLED_LIBUV)
- set(LUV_CONFIGURE_COMMAND_COMMON
- ${LUV_CONFIGURE_COMMAND_COMMON}
+ list(APPEND LUV_CMAKE_ARGS
-DCMAKE_PREFIX_PATH=${DEPS_INSTALL_DIR}
-DLIBUV_LIBRARIES=uv_a)
endif()
-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
- # accidentally end up using different MSVC runtimes
- -DCMAKE_GENERATOR=${CMAKE_GENERATOR})
-else()
- set(LUV_CONFIGURE_COMMAND
- ${LUV_CONFIGURE_COMMAND_COMMON}
- -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
- "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_COMPILER_ARG1} ${LUV_INCLUDE_FLAGS} -fPIC")
- if(CMAKE_GENERATOR MATCHES "Unix Makefiles" AND
- (CMAKE_SYSTEM_NAME MATCHES ".*BSD" OR CMAKE_SYSTEM_NAME MATCHES "DragonFly"))
- set(LUV_CONFIGURE_COMMAND ${LUV_CONFIGURE_COMMAND} -DCMAKE_MAKE_PROGRAM=gmake)
- endif()
+list(APPEND LUV_CMAKE_ARGS
+ "-DCMAKE_C_FLAGS:STRING=${LUV_INCLUDE_FLAGS}")
+if(CMAKE_GENERATOR MATCHES "Unix Makefiles" AND
+ (CMAKE_SYSTEM_NAME MATCHES ".*BSD" OR CMAKE_SYSTEM_NAME MATCHES "DragonFly"))
+ list(APPEND LUV_CMAKE_ARGS -DCMAKE_MAKE_PROGRAM=gmake)
endif()
-set(LUV_BUILD_COMMAND
- ${CMAKE_COMMAND} --build . --config ${CMAKE_BUILD_TYPE})
-set(LUV_INSTALL_COMMAND
- ${CMAKE_COMMAND} --build . --target install --config ${CMAKE_BUILD_TYPE})
+if(USE_EXISTING_SRC_DIR)
+ unset(LUA_COMPAT53_URL)
+endif()
+ExternalProject_Add(lua-compat-5.3
+ URL ${LUA_COMPAT53_URL}
+ URL_HASH SHA256=${LUA_COMPAT53_SHA256}
+ DOWNLOAD_NO_PROGRESS TRUE
+ DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/lua-compat-5.3
+ CONFIGURE_COMMAND ""
+ BUILD_COMMAND ""
+ INSTALL_COMMAND "")
-BuildLuv(PATCH_COMMAND ${LUV_PATCH_COMMAND}
- CONFIGURE_COMMAND ${LUV_CONFIGURE_COMMAND}
- BUILD_COMMAND ${LUV_BUILD_COMMAND}
- INSTALL_COMMAND ${LUV_INSTALL_COMMAND})
+if(USE_EXISTING_SRC_DIR)
+ unset(LUV_URL)
+endif()
+ExternalProject_Add(luv-static
+ DEPENDS lua-compat-5.3
+ URL ${LUV_URL}
+ URL_HASH SHA256=${LUV_SHA256}
+ DOWNLOAD_NO_PROGRESS TRUE
+ DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/luv
+ SOURCE_DIR ${DEPS_BUILD_DIR}/src/luv
+ CMAKE_ARGS ${DEPS_CMAKE_ARGS} ${LUV_CMAKE_ARGS}
+ CMAKE_CACHE_ARGS ${DEPS_CMAKE_CACHE_ARGS})
list(APPEND THIRD_PARTY_DEPS luv-static)
if(USE_BUNDLED_LUAJIT)
diff --git a/cmake.deps/cmake/BuildMsgpack.cmake b/cmake.deps/cmake/BuildMsgpack.cmake
index ea3fa84d7b..431420fb62 100644
--- a/cmake.deps/cmake/BuildMsgpack.cmake
+++ b/cmake.deps/cmake/BuildMsgpack.cmake
@@ -1,65 +1,14 @@
-# BuildMsgpack(CONFIGURE_COMMAND ... BUILD_COMMAND ... INSTALL_COMMAND ...)
-# Reusable function to build msgpack, wraps ExternalProject_Add.
-# Failing to pass a command argument will result in no command being run
-function(BuildMsgpack)
- cmake_parse_arguments(_msgpack
- ""
- ""
- "CONFIGURE_COMMAND;BUILD_COMMAND;INSTALL_COMMAND"
- ${ARGN})
-
- if(NOT _msgpack_CONFIGURE_COMMAND AND NOT _msgpack_BUILD_COMMAND
- AND NOT _msgpack_INSTALL_COMMAND)
- message(FATAL_ERROR "Must pass at least one of CONFIGURE_COMMAND, BUILD_COMMAND, INSTALL_COMMAND")
- endif()
-
- ExternalProject_Add(msgpack
- PREFIX ${DEPS_BUILD_DIR}
- URL ${MSGPACK_URL}
- DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/msgpack
- DOWNLOAD_COMMAND ${CMAKE_COMMAND}
- -DPREFIX=${DEPS_BUILD_DIR}
- -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/msgpack
- -DURL=${MSGPACK_URL}
- -DEXPECTED_SHA256=${MSGPACK_SHA256}
- -DTARGET=msgpack
- -DUSE_EXISTING_SRC_DIR=${USE_EXISTING_SRC_DIR}
- -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake
- CONFIGURE_COMMAND "${_msgpack_CONFIGURE_COMMAND}"
- BUILD_COMMAND "${_msgpack_BUILD_COMMAND}"
- INSTALL_COMMAND "${_msgpack_INSTALL_COMMAND}"
- LIST_SEPARATOR |)
-endfunction()
-
-set(MSGPACK_CONFIGURE_COMMAND ${CMAKE_COMMAND} ${DEPS_BUILD_DIR}/src/msgpack
- -DMSGPACK_BUILD_TESTS=OFF
- -DMSGPACK_BUILD_EXAMPLES=OFF
- -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
- -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
- -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(MSVC)
- # Same as Unix without fPIC
- set(MSGPACK_CONFIGURE_COMMAND ${CMAKE_COMMAND} ${DEPS_BUILD_DIR}/src/msgpack
+if(USE_EXISTING_SRC_DIR)
+ unset(MSGPACK_URL)
+endif()
+ExternalProject_Add(msgpack
+ URL ${MSGPACK_URL}
+ URL_HASH SHA256=${MSGPACK_SHA256}
+ DOWNLOAD_NO_PROGRESS TRUE
+ DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/msgpack
+ CMAKE_ARGS ${DEPS_CMAKE_ARGS}
-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}
- ${BUILD_TYPE_STRING}
- "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_COMPILER_ARG1}"
- # Make sure we use the same generator, otherwise we may
- # accidentally end up using different MSVC runtimes
- -DCMAKE_GENERATOR=${CMAKE_GENERATOR})
-endif()
-
-BuildMsgpack(CONFIGURE_COMMAND ${MSGPACK_CONFIGURE_COMMAND}
- BUILD_COMMAND ${MSGPACK_BUILD_COMMAND}
- INSTALL_COMMAND ${MSGPACK_INSTALL_COMMAND})
+ CMAKE_CACHE_ARGS ${DEPS_CMAKE_CACHE_ARGS})
list(APPEND THIRD_PARTY_DEPS msgpack)
diff --git a/cmake.deps/cmake/BuildTreesitter.cmake b/cmake.deps/cmake/BuildTreesitter.cmake
index 6d98f6179c..d906e6aa59 100644
--- a/cmake.deps/cmake/BuildTreesitter.cmake
+++ b/cmake.deps/cmake/BuildTreesitter.cmake
@@ -1,59 +1,16 @@
-# BuildTreeSitter(TARGET targetname CONFIGURE_COMMAND ... BUILD_COMMAND ... INSTALL_COMMAND ...)
-function(BuildTreeSitter)
- cmake_parse_arguments(_treesitter
- "BUILD_IN_SOURCE"
- "TARGET"
- "CONFIGURE_COMMAND;BUILD_COMMAND;INSTALL_COMMAND"
- ${ARGN})
-
- if(NOT _treesitter_CONFIGURE_COMMAND AND NOT _treesitter_BUILD_COMMAND
- AND NOT _treesitter_INSTALL_COMMAND)
- message(FATAL_ERROR "Must pass at least one of CONFIGURE_COMMAND, BUILD_COMMAND, INSTALL_COMMAND")
- endif()
- if(NOT _treesitter_TARGET)
- set(_treesitter_TARGET "tree-sitter")
- endif()
-
- ExternalProject_Add(tree-sitter
- PREFIX ${DEPS_BUILD_DIR}
- URL ${TREESITTER_URL}
- DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/tree-sitter
- INSTALL_DIR ${DEPS_INSTALL_DIR}
- DOWNLOAD_COMMAND ${CMAKE_COMMAND}
- -DPREFIX=${DEPS_BUILD_DIR}
- -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/tree-sitter
- -DURL=${TREESITTER_URL}
- -DEXPECTED_SHA256=${TREESITTER_SHA256}
- -DTARGET=tree-sitter
- -DUSE_EXISTING_SRC_DIR=${USE_EXISTING_SRC_DIR}
- -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake
- BUILD_IN_SOURCE ${_treesitter_BUILD_IN_SOURCE}
- PATCH_COMMAND ""
- CONFIGURE_COMMAND "${_treesitter_CONFIGURE_COMMAND}"
- BUILD_COMMAND "${_treesitter_BUILD_COMMAND}"
- INSTALL_COMMAND "${_treesitter_INSTALL_COMMAND}")
-endfunction()
-
-if(MSVC)
- BuildTreeSitter(BUILD_IN_SOURCE
- CONFIGURE_COMMAND ${CMAKE_COMMAND} -E copy
+if(USE_EXISTING_SRC_DIR)
+ unset(TREESITTER_URL)
+endif()
+ExternalProject_Add(tree-sitter
+ URL ${TREESITTER_URL}
+ URL_HASH SHA256=${TREESITTER_SHA256}
+ DOWNLOAD_NO_PROGRESS TRUE
+ DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/tree-sitter
+ INSTALL_DIR ${DEPS_INSTALL_DIR}
+ PATCH_COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/cmake/TreesitterCMakeLists.txt
${DEPS_BUILD_DIR}/src/tree-sitter/CMakeLists.txt
- 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}
- ${BUILD_TYPE_STRING}
- -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()
- set(TS_CFLAGS "-O3 -Wall -Wextra")
- BuildTreeSitter(BUILD_IN_SOURCE
- CONFIGURE_COMMAND ""
- BUILD_COMMAND ${MAKE_PRG} CC=${DEPS_C_COMPILER} CFLAGS=${TS_CFLAGS}
- INSTALL_COMMAND ${MAKE_PRG} CC=${DEPS_C_COMPILER} PREFIX=${DEPS_INSTALL_DIR} install)
-endif()
+ CMAKE_ARGS ${DEPS_CMAKE_ARGS}
+ CMAKE_CACHE_ARGS ${DEPS_CMAKE_CACHE_ARGS})
list(APPEND THIRD_PARTY_DEPS tree-sitter)
diff --git a/cmake.deps/cmake/BuildTreesitterParsers.cmake b/cmake.deps/cmake/BuildTreesitterParsers.cmake
index 1ff86e89b9..d62b19d97d 100644
--- a/cmake.deps/cmake/BuildTreesitterParsers.cmake
+++ b/cmake.deps/cmake/BuildTreesitterParsers.cmake
@@ -1,29 +1,22 @@
-ExternalProject_Add(treesitter-c
-PREFIX ${DEPS_BUILD_DIR}
-URL ${TREESITTER_C_URL}
-DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/treesitter-c
-DOWNLOAD_COMMAND ${CMAKE_COMMAND}
- -DPREFIX=${DEPS_BUILD_DIR}
- -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/treesitter-c
- -DURL=${TREESITTER_C_URL}
- -DEXPECTED_SHA256=${TREESITTER_C_SHA256}
- -DTARGET=treesitter-c
- -DUSE_EXISTING_SRC_DIR=${USE_EXISTING_SRC_DIR}
- -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake
-PATCH_COMMAND ${CMAKE_COMMAND} -E copy
- ${CMAKE_CURRENT_SOURCE_DIR}/cmake/TreesitterParserCMakeLists.txt
- ${DEPS_BUILD_DIR}/src/treesitter-c/CMakeLists.txt
-CMAKE_ARGS
- -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
- -DCMAKE_GENERATOR=${CMAKE_GENERATOR}
- -DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}
- ${BUILD_TYPE_STRING}
- -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
- -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES_ALT_SEP}
- # Pass toolchain
- -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}
- -DPARSERLANG=c
+function(BuildTSParser LANG TS_URL TS_SHA256 TS_CMAKE_FILE)
+ set(NAME treesitter-${LANG})
+ if(USE_EXISTING_SRC_DIR)
+ unset(TS_URL)
+ endif()
+ ExternalProject_Add(${NAME}
+ URL ${TS_URL}
+ URL_HASH SHA256=${TS_SHA256}
+ DOWNLOAD_NO_PROGRESS TRUE
+ DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/${NAME}
+ PATCH_COMMAND ${CMAKE_COMMAND} -E copy
+ ${CMAKE_CURRENT_SOURCE_DIR}/cmake/${TS_CMAKE_FILE}
+ ${DEPS_BUILD_DIR}/src/${NAME}/CMakeLists.txt
+ CMAKE_ARGS ${DEPS_CMAKE_ARGS}
+ -DPARSERLANG=${LANG}
+ CMAKE_CACHE_ARGS ${DEPS_CMAKE_CACHE_ARGS})
+endfunction()
-BUILD_COMMAND ${CMAKE_COMMAND} --build . --config ${CMAKE_BUILD_TYPE}
-INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config ${CMAKE_BUILD_TYPE}
-LIST_SEPARATOR |)
+BuildTSParser(c ${TREESITTER_C_URL} ${TREESITTER_C_SHA256} TreesitterParserCMakeLists.txt)
+BuildTSParser(lua ${TREESITTER_LUA_URL} ${TREESITTER_LUA_SHA256} TreesitterParserCMakeLists.txt)
+BuildTSParser(vim ${TREESITTER_VIM_URL} ${TREESITTER_VIM_SHA256} TreesitterParserCMakeLists.txt)
+BuildTSParser(help ${TREESITTER_HELP_URL} ${TREESITTER_HELP_SHA256} TreesitterParserCMakeLists.txt)
diff --git a/cmake.deps/cmake/BuildUnibilium.cmake b/cmake.deps/cmake/BuildUnibilium.cmake
index fd5d68f9a8..9a8caf89d1 100644
--- a/cmake.deps/cmake/BuildUnibilium.cmake
+++ b/cmake.deps/cmake/BuildUnibilium.cmake
@@ -1,48 +1,12 @@
-if(WIN32)
- ExternalProject_Add(unibilium
- PREFIX ${DEPS_BUILD_DIR}
- URL ${UNIBILIUM_URL}
- DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/unibilium
- DOWNLOAD_COMMAND ${CMAKE_COMMAND}
- -DPREFIX=${DEPS_BUILD_DIR}
- -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/unibilium
- -DURL=${UNIBILIUM_URL}
- -DEXPECTED_SHA256=${UNIBILIUM_SHA256}
- -DTARGET=unibilium
- -DUSE_EXISTING_SRC_DIR=${USE_EXISTING_SRC_DIR}
- -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake
- CONFIGURE_COMMAND ${CMAKE_COMMAND} -E copy
- ${CMAKE_CURRENT_SOURCE_DIR}/cmake/UnibiliumCMakeLists.txt
- ${DEPS_BUILD_DIR}/src/unibilium/CMakeLists.txt
- COMMAND ${CMAKE_COMMAND} ${DEPS_BUILD_DIR}/src/unibilium
- -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
- # Pass toolchain
- -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN}
- -DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}
- ${BUILD_TYPE_STRING}
- -DCMAKE_GENERATOR=${CMAKE_GENERATOR}
- BUILD_COMMAND ${CMAKE_COMMAND} --build . --config ${CMAKE_BUILD_TYPE}
- INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config ${CMAKE_BUILD_TYPE})
-else()
- ExternalProject_Add(unibilium
- PREFIX ${DEPS_BUILD_DIR}
- URL ${UNIBILIUM_URL}
- DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/unibilium
- DOWNLOAD_COMMAND ${CMAKE_COMMAND}
- -DPREFIX=${DEPS_BUILD_DIR}
- -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/unibilium
- -DURL=${UNIBILIUM_URL}
- -DEXPECTED_SHA256=${UNIBILIUM_SHA256}
- -DTARGET=unibilium
- -DUSE_EXISTING_SRC_DIR=${USE_EXISTING_SRC_DIR}
- -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake
- CONFIGURE_COMMAND ""
- BUILD_IN_SOURCE 1
- BUILD_COMMAND ${MAKE_PRG} CC=${DEPS_C_COMPILER}
- PREFIX=${DEPS_INSTALL_DIR}
- CFLAGS=-fPIC
- LDFLAGS+=-static
- INSTALL_COMMAND ${MAKE_PRG} PREFIX=${DEPS_INSTALL_DIR} install)
+if(USE_EXISTING_SRC_DIR)
+ unset(UNIBILIUM_URL)
endif()
+ExternalProject_Add(unibilium
+ URL ${UNIBILIUM_URL}
+ URL_HASH SHA256=${UNIBILIUM_SHA256}
+ DOWNLOAD_NO_PROGRESS TRUE
+ DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/unibilium
+ CMAKE_ARGS ${DEPS_CMAKE_ARGS}
+ CMAKE_CACHE_ARGS ${DEPS_CMAKE_CACHE_ARGS})
list(APPEND THIRD_PARTY_DEPS unibilium)
diff --git a/cmake.deps/cmake/DownloadAndExtractFile.cmake b/cmake.deps/cmake/DownloadAndExtractFile.cmake
deleted file mode 100644
index abb1ddc81a..0000000000
--- a/cmake.deps/cmake/DownloadAndExtractFile.cmake
+++ /dev/null
@@ -1,195 +0,0 @@
-if(NOT DEFINED PREFIX)
- message(FATAL_ERROR "PREFIX must be defined.")
-endif()
-
-if(NOT DEFINED URL)
- message(FATAL_ERROR "URL must be defined.")
-endif()
-
-if(NOT DEFINED DOWNLOAD_DIR)
- message(FATAL_ERROR "DOWNLOAD_DIR must be defined.")
-endif()
-
-if(NOT DEFINED EXPECTED_SHA256)
- message(FATAL_ERROR "EXPECTED_SHA256 must be defined.")
-endif()
-
-if(NOT DEFINED TARGET)
- message(FATAL_ERROR "TARGET must be defined.")
-endif()
-
-if(NOT DEFINED SRC_DIR)
- set(SRC_DIR ${PREFIX}/src/${TARGET})
-endif()
-set(BINARY_DIR ${PREFIX}/src/${TARGET}-build)
-
-# Check whether the source has been downloaded. If true, skip it.
-# Useful for external downloads like homebrew.
-if(USE_EXISTING_SRC_DIR)
- if(EXISTS "${SRC_DIR}" AND IS_DIRECTORY "${SRC_DIR}")
- file(GLOB EXISTED_FILES "${SRC_DIR}/*")
- if(EXISTED_FILES)
- message(STATUS "${SRC_DIR} is found and not empty, skipping download and extraction. ")
- return()
- endif()
- endif()
- message(FATAL_ERROR "USE_EXISTING_SRC_DIR set to ON, but '${SRC_DIR}' does not exist or is empty.")
-endif()
-
-# Taken from ExternalProject_Add. Let's hope we can drop this one day when
-# ExternalProject_Add allows you to disable SHOW_PROGRESS on the file download.
-if(TIMEOUT)
- set(timeout_args TIMEOUT ${timeout})
- set(timeout_msg "${timeout} seconds")
-else()
- set(timeout_args "")
- set(timeout_msg "none")
-endif()
-
-string(REGEX MATCH "[^/\\?]*$" fname "${URL}")
-if(NOT "${fname}" MATCHES "(\\.|=)(bz2|tar|tgz|tar\\.gz|zip)$")
- string(REGEX MATCH "([^/\\?]+(\\.|=)(bz2|tar|tgz|tar\\.gz|zip))/.*$" match_result "${URL}")
- set(fname "${CMAKE_MATCH_1}")
-endif()
-if(NOT "${fname}" MATCHES "(\\.|=)(bz2|tar|tgz|tar\\.gz|zip)$")
- message(FATAL_ERROR "Could not extract tarball filename from url:\n ${url}")
-endif()
-string(REPLACE ";" "-" fname "${fname}")
-
-set(file ${DOWNLOAD_DIR}/${fname})
-message(STATUS "file: ${file}")
-
-set(EXISTING_SHA256 "")
-if(EXISTS ${file})
- file(SHA256 ${file} EXISTING_SHA256)
-endif()
-
-if(NOT EXISTING_SHA256 STREQUAL ${EXPECTED_SHA256})
- message(STATUS "downloading...
- src='${URL}'
- dst='${file}'
- timeout='${timeout_msg}'")
-
- file(DOWNLOAD ${URL} ${file}
- ${timeout_args}
- ${hash_args}
- STATUS status
- LOG log)
-
- list(GET status 0 status_code)
- list(GET status 1 status_string)
-
- if(NOT status_code EQUAL 0)
- # Retry on certain errors, e.g. CURLE_COULDNT_RESOLVE_HOST, which is often
- # seen with libtermkey (www.leonerd.org.uk).
- if((status_code EQUAL 6) # "Couldn't resolve host name"
- OR (status_code EQUAL 7)) # "Couldn't connect to server"
- message(STATUS "warning: retrying '${URL}' (${status_string}, status ${status_code})")
- execute_process(COMMAND ${CMAKE_COMMAND} -E sleep 10)
- file(DOWNLOAD ${URL} ${file}
- ${timeout_args}
- ${hash_args}
- STATUS status
- LOG log)
- list(GET status 0 status_code)
- list(GET status 1 status_string)
- endif()
- if(NOT status_code EQUAL 0)
- message(FATAL_ERROR "error: downloading '${URL}' failed
- status_code: ${status_code}
- status_string: ${status_string}
- log: ${log}
- ")
- endif()
- endif()
-endif()
-
-set(NULL_SHA256 "0000000000000000000000000000000000000000000000000000000000000000")
-
-# Allow users to use "SKIP" or "skip" as the sha256 to skip checking the hash.
-# You can still use the all zeros hash too.
-if((EXPECTED_SHA256 STREQUAL "SKIP") OR (EXPECTED_SHA256 STREQUAL "skip"))
- set(EXPECTED_SHA256 ${NULL_SHA256})
-endif()
-
-# We could avoid computing the SHA256 entirely if a NULL_SHA256 was given,
-# but we want to warn users of an empty file.
-file(SHA256 ${file} ACTUAL_SHA256)
-if(ACTUAL_SHA256 STREQUAL "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
- # File was empty. It's likely due to lack of SSL support.
- message(FATAL_ERROR
- "Failed to download ${URL}. The file is empty and likely means CMake "
- "was built without SSL support. Please use a version of CMake with "
- "proper SSL support. See "
- "https://github.com/neovim/neovim/wiki/Building-Neovim#build-prerequisites "
- "for more information.")
-elseif((NOT EXPECTED_SHA256 STREQUAL NULL_SHA256) AND
- (NOT EXPECTED_SHA256 STREQUAL ACTUAL_SHA256))
- # Wasn't a NULL SHA256 and we didn't match, so we fail.
- message(FATAL_ERROR
- "Failed to download ${URL}. Expected a SHA256 of "
- "${EXPECTED_SHA256} but got ${ACTUAL_SHA256} instead.")
-endif()
-
-message(STATUS "downloading... done")
-
-# Slurped from a generated extract-TARGET.cmake file.
-message(STATUS "extracting...
- src='${file}'
- dst='${SRC_DIR}'")
-
-if(NOT EXISTS "${file}")
- message(FATAL_ERROR "error: file to extract does not exist: '${file}'")
-endif()
-
-# Prepare a space for extracting:
-#
-set(i 1234)
-while(EXISTS "${SRC_DIR}/../ex-${TARGET}${i}")
- math(EXPR i "${i} + 1")
-endwhile()
-set(ut_dir "${SRC_DIR}/../ex-${TARGET}${i}")
-file(MAKE_DIRECTORY "${ut_dir}")
-
-# Extract it:
-#
-message(STATUS "extracting... [tar xfz]")
-execute_process(COMMAND ${CMAKE_COMMAND} -E tar xfz ${file}
- WORKING_DIRECTORY ${ut_dir}
- RESULT_VARIABLE rv)
-
-if(NOT rv EQUAL 0)
- message(STATUS "extracting... [error clean up]")
- file(REMOVE_RECURSE "${ut_dir}")
- message(FATAL_ERROR "error: extract of '${file}' failed")
-endif()
-
-# Analyze what came out of the tar file:
-#
-message(STATUS "extracting... [analysis]")
-file(GLOB contents "${ut_dir}/*")
-list(LENGTH contents n)
-if(NOT n EQUAL 1 OR NOT IS_DIRECTORY "${contents}")
- set(contents "${ut_dir}")
-endif()
-
-# Move "the one" directory to the final directory:
-#
-message(STATUS "extracting... [rename]")
-file(REMOVE_RECURSE ${SRC_DIR})
-get_filename_component(contents ${contents} ABSOLUTE)
-file(RENAME ${contents} ${SRC_DIR})
-
-# Remove any existing BINARY_DIR, to force a new build.
-# Without this a necessary output (e.g. libluv.a) might not be updated/installed.
-#
-message(STATUS "extracting... [clean binary dir]")
-file(REMOVE_RECURSE ${BINARY_DIR})
-file(MAKE_DIRECTORY ${BINARY_DIR})
-
-# Clean up:
-#
-message(STATUS "extracting... [clean up]")
-file(REMOVE_RECURSE "${ut_dir}")
-
-message(STATUS "extracting... done")
diff --git a/cmake.deps/cmake/GetBinaryDeps.cmake b/cmake.deps/cmake/GetBinaryDeps.cmake
index 04e3f95a29..da4376998b 100644
--- a/cmake.deps/cmake/GetBinaryDeps.cmake
+++ b/cmake.deps/cmake/GetBinaryDeps.cmake
@@ -22,18 +22,14 @@ function(GetBinaryDep)
message(FATAL_ERROR "${URL_VARNAME} and ${HASH_VARNAME} must be set")
endif()
+ if(USE_EXISTING_SRC_DIR)
+ unset(URL)
+ endif()
ExternalProject_Add(${_gettool_TARGET}
- PREFIX ${DEPS_BUILD_DIR}
URL ${URL}
+ URL_HASH SHA256=${HASH}
+ DOWNLOAD_NO_PROGRESS TRUE
DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}
- DOWNLOAD_COMMAND ${CMAKE_COMMAND}
- -DPREFIX=${DEPS_BUILD_DIR}
- -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}
- -DURL=${URL}
- -DEXPECTED_SHA256=${HASH}
- -DTARGET=${_gettool_TARGET}
- -DUSE_EXISTING_SRC_DIR=${USE_EXISTING_SRC_DIR}
- -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake
CONFIGURE_COMMAND ""
BUILD_IN_SOURCE 1
BUILD_COMMAND ""
diff --git a/cmake.deps/cmake/GettextCMakeLists.txt b/cmake.deps/cmake/GettextCMakeLists.txt
index d9f251897d..26f060ec08 100644
--- a/cmake.deps/cmake/GettextCMakeLists.txt
+++ b/cmake.deps/cmake/GettextCMakeLists.txt
@@ -327,3 +327,5 @@ install(TARGETS libintl msgmerge msgfmt xgettext
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+# vim: set ft=cmake:
diff --git a/cmake.deps/cmake/LibiconvCMakeLists.txt b/cmake.deps/cmake/LibiconvCMakeLists.txt
index b4ba6b5d7e..f6a23db864 100644
--- a/cmake.deps/cmake/LibiconvCMakeLists.txt
+++ b/cmake.deps/cmake/LibiconvCMakeLists.txt
@@ -95,3 +95,5 @@ install(TARGETS libcharset libiconv iconv
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+# vim: set ft=cmake:
diff --git a/cmake.deps/cmake/LibvtermCMakeLists.txt b/cmake.deps/cmake/LibvtermCMakeLists.txt
index ff1d2d6b79..079ad28ba0 100644
--- a/cmake.deps/cmake/LibvtermCMakeLists.txt
+++ b/cmake.deps/cmake/LibvtermCMakeLists.txt
@@ -41,14 +41,6 @@ file(GLOB VTERM_SOURCES ${CMAKE_SOURCE_DIR}/src/*.c)
add_library(vterm ${VTERM_SOURCES} ${TBL_FILES_HEADERS})
install(TARGETS vterm ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
-add_library(vterm-shared SHARED ${VTERM_SOURCES} ${TBL_FILES_HEADERS})
-set_target_properties(vterm-shared PROPERTIES
- OUTPUT_NAME vterm
- SOVERSION 0)
-install(TARGETS vterm-shared
- LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
- RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
-
install(FILES include/vterm.h include/vterm_keycodes.h
DESTINATION include)
@@ -90,3 +82,5 @@ if(Perl_FOUND)
DEPENDS ${header_path} ${perl_header_path})
endforeach()
endif()
+
+# vim: set ft=cmake:
diff --git a/cmake.deps/cmake/TargetArch.cmake b/cmake.deps/cmake/TargetArch.cmake
deleted file mode 100644
index 71ea44ec59..0000000000
--- a/cmake.deps/cmake/TargetArch.cmake
+++ /dev/null
@@ -1,23 +0,0 @@
-# Sets TARGET_ARCH to a normalized name (X86 or X86_64).
-# See https://github.com/axr/solar-cmake/blob/master/TargetArch.cmake
-include(CheckSymbolExists)
-
-# X86
-check_symbol_exists("_M_IX86" "" T_M_IX86)
-check_symbol_exists("__i386__" "" T_I386)
-if(T_M_IX86 OR T_I386)
-set(TARGET_ARCH "X86")
- return()
-endif()
-
-# X86_64
-check_symbol_exists("_M_AMD64" "" T_M_AMD64)
-check_symbol_exists("__x86_64__" "" T_X86_64)
-check_symbol_exists("__amd64__" "" T_AMD64)
-
-if(T_M_AMD64 OR T_X86_64 OR T_AMD64)
-set(TARGET_ARCH "X86_64")
- return()
-endif()
-
-message(FATAL_ERROR "Unknown target architecture")
diff --git a/cmake.deps/cmake/TreesitterCMakeLists.txt b/cmake.deps/cmake/TreesitterCMakeLists.txt
index e20b47dd74..49fb19c96a 100644
--- a/cmake.deps/cmake/TreesitterCMakeLists.txt
+++ b/cmake.deps/cmake/TreesitterCMakeLists.txt
@@ -1,15 +1,9 @@
cmake_minimum_required(VERSION 3.10)
project(tree-sitter LANGUAGES C)
-file(GLOB SRC_FILES ${PROJECT_SOURCE_DIR}/lib/src/*.c)
-foreach(sfile ${SRC_FILES})
- get_filename_component(f ${sfile} NAME)
- if(${f} MATCHES "lib.c$")
- list(REMOVE_ITEM SRC_FILES ${sfile})
- endif()
-endforeach()
-include_directories(${PROJECT_SOURCE_DIR}/lib/include)
-add_library(tree-sitter ${SRC_FILES})
+add_library(tree-sitter lib/src/lib.c)
+target_include_directories(tree-sitter
+ PRIVATE lib/src lib/include)
install(FILES
lib/include/tree_sitter/api.h
@@ -17,5 +11,6 @@ install(FILES
DESTINATION include/tree_sitter)
include(GNUInstallDirs)
-install(TARGETS tree-sitter
- LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
+install(TARGETS tree-sitter DESTINATION ${CMAKE_INSTALL_LIBDIR})
+
+# vim: set ft=cmake:
diff --git a/cmake.deps/cmake/TreesitterParserCMakeLists.txt b/cmake.deps/cmake/TreesitterParserCMakeLists.txt
index 54bc35fb8a..9bdf500aa7 100644
--- a/cmake.deps/cmake/TreesitterParserCMakeLists.txt
+++ b/cmake.deps/cmake/TreesitterParserCMakeLists.txt
@@ -1,15 +1,16 @@
cmake_minimum_required(VERSION 3.10)
-# some parsers have c++ scanner, problem?
-project(parser C) # CXX
+project(parser C)
+
+set(CMAKE_C_STANDARD 99)
+file(GLOB source_files src/*.c)
add_library(parser
MODULE
- src/parser.c
+ ${source_files}
)
set_target_properties(
parser
PROPERTIES
- POSITION_INDEPENDENT_CODE ON
OUTPUT_NAME ${PARSERLANG}
PREFIX ""
)
@@ -17,3 +18,5 @@ set_target_properties(
include_directories(src)
install(TARGETS parser LIBRARY DESTINATION lib/nvim/parser)
+
+# vim: set ft=cmake:
diff --git a/cmake.deps/cmake/UnibiliumCMakeLists.txt b/cmake.deps/cmake/UnibiliumCMakeLists.txt
deleted file mode 100644
index 9112b416fa..0000000000
--- a/cmake.deps/cmake/UnibiliumCMakeLists.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-cmake_minimum_required(VERSION 3.10)
-project(unibilium LANGUAGES C)
-
-file(GLOB SRC_FILES ${PROJECT_SOURCE_DIR}/*.c)
-add_library(unibilium ${SRC_FILES})
-set_target_properties(unibilium PROPERTIES PUBLIC_HEADER ${PROJECT_SOURCE_DIR}/unibilium.h
- VERSION "${VERSION_MAJOR}.${VERSION_MINOR}")
-
-if(NOT WIN32)
- execute_process(COMMAND "shell ncursesw6-config --terminfo-dirs 2>/dev/null || \
- ncurses6-config --terminfo-dirs 2>/dev/null || \
- ncursesw5-config --terminfo-dirs 2>/dev/null || \
- ncurses5-config --terminfo-dirs 2>/dev/null || \
- echo '/etc/terminfo:/lib/terminfo:/usr/share/terminfo:/usr/lib/terminfo:/usr/local/share/terminfo:/usr/local/lib/terminfo'"
- OUTPUT_VARIABLE TERMINFO_DIRS)
-else()
- set(TERMINFO_DIRS "\"\"")
-endif()
-target_compile_definitions(unibilium PUBLIC TERMINFO_DIRS=${TERMINFO_DIRS})
-
-include(GNUInstallDirs)
-install(TARGETS unibilium
- PUBLIC_HEADER
- ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
- PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
diff --git a/cmake.deps/cmake/libtermkeyCMakeLists.txt b/cmake.deps/cmake/libtermkeyCMakeLists.txt
index 6c02b7549d..26c9d7730b 100644
--- a/cmake.deps/cmake/libtermkeyCMakeLists.txt
+++ b/cmake.deps/cmake/libtermkeyCMakeLists.txt
@@ -20,15 +20,4 @@ install(TARGETS termkey
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
-enable_testing()
-file(GLOB TESTSOURCES "t/[0-9]*.c")
-foreach(f ${TESTSOURCES})
- get_filename_component(t ${f} NAME_WE)
- if(${t} STREQUAL 05read)
- continue()
- endif()
-
- add_executable("test_${t}" ${f} t/taplib.c)
- target_link_libraries("test_${t}" termkey)
- add_test("${t}" "test_${t}")
-endforeach()
+# vim: set ft=cmake:
diff --git a/cmake.deps/patches/libuv-disable-shared.patch b/cmake.deps/patches/libuv-disable-shared.patch
deleted file mode 100644
index 0e5722f4ba..0000000000
--- a/cmake.deps/patches/libuv-disable-shared.patch
+++ /dev/null
@@ -1,117 +0,0 @@
-From 326a1845f924432332071d03d156b7df4af7c46f Mon Sep 17 00:00:00 2001
-From: Tim Tavlintsev <ttavlintsev@enttec.com>
-Date: Thu, 21 Jul 2022 16:42:21 +1000
-Subject: [PATCH] Add CMake option LIBUV_BUILD_SHARED to enable/disable shared
- library build Fix #3637
-
----
- CMakeLists.txt | 66 +++++++++++++++++++++++++++++---------------------
- 1 file changed, 38 insertions(+), 28 deletions(-)
-
-diff --git a/CMakeLists.txt b/CMakeLists.txt
-index 2c42c3ff..a8e19980 100644
---- a/CMakeLists.txt
-+++ b/CMakeLists.txt
-@@ -28,6 +28,8 @@ cmake_dependent_option(LIBUV_BUILD_BENCH
- "Build the benchmarks when building unit tests and we are the root project" ON
- "LIBUV_BUILD_TESTS" OFF)
-
-+option(LIBUV_BUILD_SHARED "Build shared lib" ON)
-+
- # Qemu Build
- option(QEMU "build for qemu" OFF)
- if(QEMU)
-@@ -390,25 +392,27 @@ if(APPLE OR CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD|Linux|NetBSD|OpenBSD")
- list(APPEND uv_test_libraries util)
- endif()
-
--add_library(uv SHARED ${uv_sources})
--target_compile_definitions(uv
-- INTERFACE
-- USING_UV_SHARED=1
-- PRIVATE
-- BUILDING_UV_SHARED=1
-- ${uv_defines})
--target_compile_options(uv PRIVATE ${uv_cflags})
--target_include_directories(uv
-- PUBLIC
-- $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
-- $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
-- PRIVATE
-- $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src>)
--if(CMAKE_SYSTEM_NAME STREQUAL "OS390")
-- target_include_directories(uv PUBLIC $<BUILD_INTERFACE:${ZOSLIB_DIR}/include>)
-- set_target_properties(uv PROPERTIES LINKER_LANGUAGE CXX)
-+if(LIBUV_BUILD_SHARED)
-+ add_library(uv SHARED ${uv_sources})
-+ target_compile_definitions(uv
-+ INTERFACE
-+ USING_UV_SHARED=1
-+ PRIVATE
-+ BUILDING_UV_SHARED=1
-+ ${uv_defines})
-+ target_compile_options(uv PRIVATE ${uv_cflags})
-+ target_include_directories(uv
-+ PUBLIC
-+ $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
-+ $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
-+ PRIVATE
-+ $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src>)
-+ if(CMAKE_SYSTEM_NAME STREQUAL "OS390")
-+ target_include_directories(uv PUBLIC $<BUILD_INTERFACE:${ZOSLIB_DIR}/include>)
-+ set_target_properties(uv PROPERTIES LINKER_LANGUAGE CXX)
-+ endif()
-+ target_link_libraries(uv ${uv_libraries})
- endif()
--target_link_libraries(uv ${uv_libraries})
-
- add_library(uv_a STATIC ${uv_sources})
- target_compile_definitions(uv_a PRIVATE ${uv_defines})
-@@ -669,28 +673,34 @@ string(REPLACE ";" " " LIBS "${LIBS}")
- file(STRINGS configure.ac configure_ac REGEX ^AC_INIT)
- string(REGEX MATCH "([0-9]+)[.][0-9]+[.][0-9]+" PACKAGE_VERSION "${configure_ac}")
- set(UV_VERSION_MAJOR "${CMAKE_MATCH_1}")
--# The version in the filename is mirroring the behaviour of autotools.
--set_target_properties(uv PROPERTIES
-- VERSION ${UV_VERSION_MAJOR}.0.0
-- SOVERSION ${UV_VERSION_MAJOR})
-+
- set(includedir ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR})
- set(libdir ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR})
- set(prefix ${CMAKE_INSTALL_PREFIX})
--configure_file(libuv.pc.in libuv.pc @ONLY)
- configure_file(libuv-static.pc.in libuv-static.pc @ONLY)
-
- install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
- install(FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR})
--install(FILES ${PROJECT_BINARY_DIR}/libuv.pc ${PROJECT_BINARY_DIR}/libuv-static.pc
-+install(FILES ${PROJECT_BINARY_DIR}/libuv-static.pc
- DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
--install(TARGETS uv EXPORT libuvConfig
-- RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-- LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-- ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
- install(TARGETS uv_a EXPORT libuvConfig
- ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
- install(EXPORT libuvConfig DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libuv)
-
-+if(LIBUV_BUILD_SHARED)
-+ # The version in the filename is mirroring the behaviour of autotools.
-+ set_target_properties(uv PROPERTIES
-+ VERSION ${UV_VERSION_MAJOR}.0.0
-+ SOVERSION ${UV_VERSION_MAJOR})
-+ configure_file(libuv.pc.in libuv.pc @ONLY)
-+ install(FILES ${PROJECT_BINARY_DIR}/libuv.pc
-+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
-+ install(TARGETS uv EXPORT libuvConfig
-+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
-+endif()
-+
- if(MSVC)
- set(CMAKE_DEBUG_POSTFIX d)
- endif()
---
-2.37.0
-
diff --git a/cmake.deps/patches/libvterm-Remove-VLAs-for-MSVC.patch b/cmake.deps/patches/libvterm-Remove-VLAs-for-MSVC.patch
deleted file mode 100644
index e999c0fa9b..0000000000
--- a/cmake.deps/patches/libvterm-Remove-VLAs-for-MSVC.patch
+++ /dev/null
@@ -1,50 +0,0 @@
-From eb386b1d82f7d07363c9133b7aa06902ccd555fe Mon Sep 17 00:00:00 2001
-Date: Tue, 27 Feb 2018 17:54:20 -0600
-Subject: [PATCH] Remove VLAs for MSVC
-
-VLAs are replaced with calls to _alloca() because MSVC does not support them.
----
- src/state.c | 7 ++++---
- 1 file changed, 4 insertions(+), 3 deletions(-)
-
-diff --git a/src/state.c b/src/state.c
-index 84299df..f9aabb3 100644
---- a/src/state.c
-+++ b/src/state.c
-@@ -1,5 +1,6 @@
- #include "vterm_internal.h"
-
-+#include <malloc.h>
- #include <stdio.h>
- #include <string.h>
-
-@@ -236,7 +237,7 @@ static int on_text(const char bytes[], size_t len, void *user)
- VTermPos oldpos = state->pos;
-
- // We'll have at most len codepoints
-- uint32_t codepoints[len];
-+ uint32_t* codepoints = _alloca(len * sizeof(uint32_t));
- int npoints = 0;
- size_t eaten = 0;
-
-@@ -313,7 +314,7 @@ static int on_text(const char bytes[], size_t len, void *user)
-
- int width = 0;
-
-- uint32_t chars[glyph_ends - glyph_starts + 1];
-+ uint32_t* chars = _alloca((glyph_ends - glyph_starts + 1) * sizeof(uint32_t));
-
- for( ; i < glyph_ends; i++) {
- chars[i - glyph_starts] = codepoints[i];
-@@ -512,7 +513,7 @@ static int settermprop_int(VTermState *state, VTermProp prop, int v)
-
- static int settermprop_string(VTermState *state, VTermProp prop, const char *str, size_t len)
- {
-- char strvalue[len+1];
-+ char* strvalue = _alloca(len+1);
- strncpy(strvalue, str, len);
- strvalue[len] = 0;
-
---
-2.16.1.windows.4
-
diff --git a/cmake.packaging/CMakeLists.txt b/cmake.packaging/CMakeLists.txt
index 8538075388..df43f2806a 100644
--- a/cmake.packaging/CMakeLists.txt
+++ b/cmake.packaging/CMakeLists.txt
@@ -32,7 +32,7 @@ if(WIN32)
# 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)
+ set(CPACK_WIX_PRODUCT_ICON ${PROJECT_SOURCE_DIR}/runtime/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.
diff --git a/cmake/Download.cmake b/cmake/Download.cmake
deleted file mode 100644
index 50a77816bc..0000000000
--- a/cmake/Download.cmake
+++ /dev/null
@@ -1,18 +0,0 @@
-file(
- DOWNLOAD "${URL}" "${FILE}"
- STATUS status
- LOG log
-)
-
-list(GET status 0 status_code)
-list(GET status 1 status_string)
-
-if(NOT status_code EQUAL 0)
- if(NOT ALLOW_FAILURE)
- message(FATAL_ERROR "error: downloading '${URL}' failed
- status_code: ${status_code}
- status_string: ${status_string}
- log: ${log}
- ")
- endif()
-endif()
diff --git a/cmake/FindIconv.cmake b/cmake/FindIconv.cmake
index 1d0164dae9..63290fe889 100644
--- a/cmake/FindIconv.cmake
+++ b/cmake/FindIconv.cmake
@@ -1,3 +1,6 @@
+# TODO(dundargoc): FindIconv is shipped by default on cmake version 3.11+. This
+# file can be removed once we decide to upgrade minimum cmake version.
+
# - Try to find iconv
# Once done, this will define
#
diff --git a/cmake/GenerateVersion.cmake b/cmake/GenerateVersion.cmake
index 6118d8cba7..c092645140 100644
--- a/cmake/GenerateVersion.cmake
+++ b/cmake/GenerateVersion.cmake
@@ -1,30 +1,29 @@
-if(NVIM_VERSION_MEDIUM)
- message(STATUS "USING NVIM_VERSION_MEDIUM: ${NVIM_VERSION_MEDIUM}")
- return()
-endif()
-
-set(NVIM_VERSION_MEDIUM
+set(NVIM_VERSION
"v${NVIM_VERSION_MAJOR}.${NVIM_VERSION_MINOR}.${NVIM_VERSION_PATCH}${NVIM_VERSION_PRERELEASE}")
execute_process(
- COMMAND git describe --first-parent --dirty
+ COMMAND git --git-dir=${NVIM_SOURCE_DIR}/.git --work-tree=${NVIM_SOURCE_DIR} describe --first-parent --dirty --always
OUTPUT_VARIABLE GIT_TAG
- ERROR_VARIABLE ERR
- RESULT_VARIABLE RES
-)
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ RESULT_VARIABLE RES)
-if(NOT RES EQUAL 0)
- message(STATUS "Git tag extraction failed:\n" " ${GIT_TAG}${ERR}" )
- # This will only be executed once since the file will get generated afterwards.
- message(STATUS "Using NVIM_VERSION_MEDIUM: ${NVIM_VERSION_MEDIUM}")
- file(WRITE "${OUTPUT}" "${NVIM_VERSION_STRING}")
+if(RES)
+ message(STATUS "Using NVIM_VERSION: ${NVIM_VERSION}")
+ file(WRITE "${OUTPUT}" "")
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 "${NVIM_VERSION_MEDIUM}-${NVIM_VERSION_GIT}")
-set(NVIM_VERSION_STRING "#define NVIM_VERSION_MEDIUM \"${NVIM_VERSION_MEDIUM}\"\n")
+# `git describe` annotates the most recent tagged release; for pre-release
+# builds we append that to the dev version.
+if(NVIM_VERSION_PRERELEASE)
+ # Extract pre-release info: "v0.8.0-145-g0f9113907" => "145-g0f9113907"
+ string(REGEX REPLACE "^v[0-9]+.[0-9]+.[0-9]+-" "" NVIM_VERSION_GIT "${GIT_TAG}")
+ # Replace "-" with "+": "145-g0f9113907" => "145+g0f9113907"
+ string(REGEX REPLACE "^([0-9]+)-([a-z0-9]+)" "\\1+\\2" NVIM_VERSION_GIT "${NVIM_VERSION_GIT}")
+ set(NVIM_VERSION "${NVIM_VERSION}-${NVIM_VERSION_GIT}")
+endif()
+
+set(NVIM_VERSION_STRING "#define NVIM_VERSION_MEDIUM \"${NVIM_VERSION}\"\n")
string(SHA1 CURRENT_VERSION_HASH "${NVIM_VERSION_STRING}")
if(EXISTS ${OUTPUT})
@@ -32,6 +31,9 @@ if(EXISTS ${OUTPUT})
endif()
if(NOT "${NVIM_VERSION_HASH}" STREQUAL "${CURRENT_VERSION_HASH}")
- message(STATUS "Using NVIM_VERSION_MEDIUM: ${NVIM_VERSION_MEDIUM}")
+ message(STATUS "Using NVIM_VERSION: ${NVIM_VERSION}")
file(WRITE "${OUTPUT}" "${NVIM_VERSION_STRING}")
+ if(WIN32)
+ configure_file("${OUTPUT}" "${OUTPUT}" NEWLINE_STYLE UNIX)
+ endif()
endif()
diff --git a/cmake/GetCompileFlags.cmake b/cmake/GetCompileFlags.cmake
index 49b57f6f75..9b3c053871 100644
--- a/cmake/GetCompileFlags.cmake
+++ b/cmake/GetCompileFlags.cmake
@@ -1,87 +1,57 @@
function(get_compile_flags _compile_flags)
- # Create template akin to CMAKE_C_COMPILE_OBJECT.
- set(compile_flags "<CMAKE_C_COMPILER> <CFLAGS> <BUILD_TYPE_CFLAGS> <COMPILE_OPTIONS><COMPILE_DEFINITIONS> <INCLUDES>")
+ string(TOUPPER "${CMAKE_BUILD_TYPE}" build_type)
+ set(compile_flags ${CMAKE_C_COMPILER} ${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${build_type}})
- # Get C compiler.
- if(CMAKE_C_COMPILER_ARG1)
- string(REPLACE
- "<CMAKE_C_COMPILER>"
- "<CMAKE_C_COMPILER> ${CMAKE_C_COMPILER_ARG1}"
- compile_flags
- "${compile_flags}")
+ # Get flags set by target_compile_options().
+ get_target_property(opt main_lib INTERFACE_COMPILE_OPTIONS)
+ if(opt)
+ list(APPEND compile_flags ${opt})
endif()
- string(REPLACE
- "<CMAKE_C_COMPILER>"
- "${CMAKE_C_COMPILER}"
- compile_flags
- "${compile_flags}")
- # Get flags set by add_definitions().
- get_property(compile_definitions DIRECTORY PROPERTY COMPILE_DEFINITIONS)
- get_target_property(compile_definitions_target nvim COMPILE_DEFINITIONS)
- if(compile_definitions_target)
- list(APPEND compile_definitions ${compile_definitions_target})
- list(REMOVE_DUPLICATES compile_definitions)
+ get_target_property(opt nvim COMPILE_OPTIONS)
+ if(opt)
+ list(APPEND compile_flags ${opt})
endif()
- # NOTE: list(JOIN) requires CMake 3.12, string(CONCAT) requires CMake 3.
- string(REPLACE ";" " -D" compile_definitions "${compile_definitions}")
- if(compile_definitions)
- set(compile_definitions " -D${compile_definitions}")
+
+ # Get flags set by target_compile_definitions().
+ get_target_property(defs main_lib INTERFACE_COMPILE_DEFINITIONS)
+ if(defs)
+ foreach(def ${defs})
+ list(APPEND compile_flags "-D${def}")
+ endforeach()
endif()
- string(REPLACE
- "<COMPILE_DEFINITIONS>"
- "${compile_definitions}"
- compile_flags
- "${compile_flags}")
- # Get flags set by add_compile_options().
- get_property(compile_options DIRECTORY PROPERTY COMPILE_OPTIONS)
- get_target_property(compile_options_target nvim COMPILE_OPTIONS)
- if(compile_options_target)
- list(APPEND compile_options ${compile_options_target})
- list(REMOVE_DUPLICATES compile_options)
+ get_target_property(defs nvim COMPILE_DEFINITIONS)
+ if(defs)
+ foreach(def ${defs})
+ list(APPEND compile_flags "-D${def}")
+ endforeach()
endif()
- # NOTE: list(JOIN) requires CMake 3.12.
- string(REPLACE ";" " " compile_options "${compile_options}")
- string(REPLACE
- "<COMPILE_OPTIONS>"
- "${compile_options}"
- compile_flags
- "${compile_flags}")
- # Get general C flags.
- string(REPLACE
- "<CFLAGS>"
- "${CMAKE_C_FLAGS}"
- compile_flags
- "${compile_flags}")
+ # Get include directories.
+ get_target_property(dirs main_lib INTERFACE_INCLUDE_DIRECTORIES)
+ if(dirs)
+ foreach(dir ${dirs})
+ list(APPEND compile_flags "-I${dir}")
+ endforeach()
+ endif()
- # Get C flags specific to build type.
- string(TOUPPER "${CMAKE_BUILD_TYPE}" build_type)
- string(REPLACE
- "<BUILD_TYPE_CFLAGS>"
- "${CMAKE_C_FLAGS_${build_type}}"
- compile_flags
- "${compile_flags}")
+ get_target_property(dirs main_lib INTERFACE_SYSTEM_INCLUDE_DIRECTORIES)
+ if(dirs)
+ foreach(dir ${dirs})
+ list(APPEND compile_flags "-I${dir}")
+ endforeach()
+ endif()
- # Get include directories.
- get_property(include_directories_list DIRECTORY PROPERTY INCLUDE_DIRECTORIES)
- list(REMOVE_DUPLICATES include_directories_list)
- foreach(include_directory ${include_directories_list})
- set(include_directories "${include_directories} -I${include_directory}")
- endforeach()
- string(REPLACE
- "<INCLUDES>"
- "${include_directories}"
- compile_flags
- "${compile_flags}")
+ get_target_property(dirs nvim INCLUDE_DIRECTORIES)
+ if(dirs)
+ foreach(dir ${dirs})
+ list(APPEND compile_flags "-I${dir}")
+ endforeach()
+ endif()
- # Clean duplicate whitespace.
- string(REPLACE
- " "
- " "
- compile_flags
- "${compile_flags}")
+ list(REMOVE_DUPLICATES compile_flags)
+ string(REPLACE ";" " " compile_flags "${compile_flags}")
set(${_compile_flags} "${compile_flags}" PARENT_SCOPE)
endfunction()
diff --git a/cmake/RunMsgfmt.cmake b/cmake/RunMsgfmt.cmake
deleted file mode 100644
index 51606338e0..0000000000
--- a/cmake/RunMsgfmt.cmake
+++ /dev/null
@@ -1,9 +0,0 @@
-set(ENV{OLD_PO_FILE_INPUT} yes)
-
-execute_process(
- COMMAND ${MSGFMT_PRG} -o ${MO_FILE} ${PO_FILE}
- ERROR_VARIABLE err
- RESULT_VARIABLE res)
-if(NOT res EQUAL 0)
- message(FATAL_ERROR "msgfmt failed to run correctly: ${err}")
-endif()
diff --git a/cmake/RunMsgmerge.cmake b/cmake/RunMsgmerge.cmake
deleted file mode 100644
index 69e5c7276d..0000000000
--- a/cmake/RunMsgmerge.cmake
+++ /dev/null
@@ -1,11 +0,0 @@
-set(ENV{OLD_PO_FILE_INPUT} yes)
-set(ENV{OLD_PO_FILE_OUTPUT} yes)
-
-execute_process(
- COMMAND ${MSGMERGE_PRG} -q --update --backup=none --sort-by-file
- ${PO_FILE} ${POT_FILE}
- ERROR_VARIABLE err
- RESULT_VARIABLE res)
-if(NOT res EQUAL 0)
- message(FATAL_ERROR "msgmerge failed to run correctly: ${err}")
-endif()
diff --git a/cmake/RunTests.cmake b/cmake/RunTests.cmake
index e07c6dd174..c3ac5f208e 100644
--- a/cmake/RunTests.cmake
+++ b/cmake/RunTests.cmake
@@ -2,7 +2,9 @@
set(ENV{LC_ALL} "en_US.UTF-8")
if(POLICY CMP0012)
- # Handle CI=true, without dev warnings.
+ # Avoid policy warning due to CI=true. This is needed even if the main
+ # project has already set this policy as project settings aren't inherited
+ # when using cmake script mode (-P).
cmake_policy(SET CMP0012 NEW)
endif()
@@ -13,6 +15,12 @@ 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
+# TODO(dundargoc): The CIRRUS_CI environment variable isn't passed to here from
+# the main CMakeLists.txt, so we have to manually pass it to this script and
+# re-set the environment variable. Investigate if we can avoid manually setting
+# it like with the GITHUB_CI environment variable.
+set(ENV{CIRRUS_CI} ${CIRRUS_CI})
+
if(NOT DEFINED ENV{NVIM_LOG_FILE})
set(ENV{NVIM_LOG_FILE} ${BUILD_DIR}/.nvimlog)
endif()
diff --git a/cmake/RunXgettext.cmake b/cmake/RunXgettext.cmake
deleted file mode 100644
index c9328b151d..0000000000
--- a/cmake/RunXgettext.cmake
+++ /dev/null
@@ -1,14 +0,0 @@
-set(ENV{OLD_PO_FILE_INPUT} yes)
-set(ENV{OLD_PO_FILE_OUTPUT} yes)
-
-list(SORT SOURCES)
-
-execute_process(
- COMMAND ${XGETTEXT_PRG} -o ${POT_FILE} --default-domain=nvim
- --add-comments --keyword=_ --keyword=N_ -D ${SEARCH_DIR}
- ${SOURCES}
- ERROR_VARIABLE err
- RESULT_VARIABLE res)
-if(NOT res EQUAL 0)
- message(FATAL_ERROR "xgettext failed to run correctly: ${err}")
-endif()
diff --git a/cmake/Util.cmake b/cmake/Util.cmake
index b485f4606b..e15b44d29a 100644
--- a/cmake/Util.cmake
+++ b/cmake/Util.cmake
@@ -49,12 +49,13 @@
# simple be added to FILES
# GLOB_DIRS - The directories to recursively search for files with extension
# GLOB_PAT
-#
-function(add_glob_targets)
+# EXCLUDE - List of paths to skip (regex). Works on both directories and
+# files.
+function(add_glob_target)
cmake_parse_arguments(ARG
"REQUIRED"
"TARGET;COMMAND;GLOB_PAT;TOUCH_STRATEGY"
- "FLAGS;FILES;GLOB_DIRS"
+ "FLAGS;FILES;GLOB_DIRS;EXCLUDE"
${ARGN}
)
@@ -72,10 +73,19 @@ function(add_glob_targets)
endif()
foreach(gd ${ARG_GLOB_DIRS})
- file(GLOB_RECURSE globfiles ${PROJECT_SOURCE_DIR}/${gd}/${ARG_GLOB_PAT})
+ file(GLOB_RECURSE globfiles_unnormalized ${PROJECT_SOURCE_DIR}/${gd}/${ARG_GLOB_PAT})
+ set(globfiles)
+ foreach(f ${globfiles_unnormalized})
+ file(TO_CMAKE_PATH "${f}" f)
+ list(APPEND globfiles ${f})
+ endforeach()
list(APPEND ARG_FILES ${globfiles})
endforeach()
+ foreach(exclude_pattern ${ARG_EXCLUDE})
+ list(FILTER ARG_FILES EXCLUDE REGEX ${exclude_pattern})
+ endforeach()
+
if(NOT ARG_TOUCH_STRATEGY)
set(ARG_TOUCH_STRATEGY PER_FILE)
endif()
@@ -143,3 +153,43 @@ function(add_glob_targets)
add_custom_target(${ARG_TARGET} DEPENDS ${touch_list})
endfunction()
+
+# Set default build type to Debug. Also limit the list of allowable build types
+# to the ones defined in variable allowableBuildTypes.
+#
+# The correct way to specify build type (for example Release) for
+# single-configuration generators (Make and Ninja) is to run
+#
+# cmake -B build -D CMAKE_BUILD_TYPE=Release
+# cmake --build build
+#
+# while for multi-configuration generators (Visual Studio, Xcode and Ninja
+# Multi-Config) is to run
+#
+# cmake -B build
+# cmake --build build --config Release
+#
+# Passing CMAKE_BUILD_TYPE for multi-config generators will now not only
+# not be used, but also generate a warning for the user.
+function(set_default_buildtype)
+ set(allowableBuildTypes Debug Release MinSizeRel RelWithDebInfo)
+
+ get_property(isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+ if(isMultiConfig)
+ set(CMAKE_CONFIGURATION_TYPES ${allowableBuildTypes} PARENT_SCOPE)
+ if(CMAKE_BUILD_TYPE)
+ message(WARNING "CMAKE_BUILD_TYPE specified which is ignored on \
+ multi-configuration generators. Defaulting to Debug build type.")
+ endif()
+ else()
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "${allowableBuildTypes}")
+ if(NOT CMAKE_BUILD_TYPE)
+ message(STATUS "CMAKE_BUILD_TYPE not specified, default is 'Debug'")
+ set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build" FORCE)
+ elseif(NOT CMAKE_BUILD_TYPE IN_LIST allowableBuildTypes)
+ message(FATAL_ERROR "Invalid build type: ${CMAKE_BUILD_TYPE}")
+ else()
+ message(STATUS "CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
+ endif()
+ endif()
+endfunction()
diff --git a/contrib/YouCompleteMe/README.md b/contrib/YouCompleteMe/README.md
deleted file mode 100644
index 345a9d8d12..0000000000
--- a/contrib/YouCompleteMe/README.md
+++ /dev/null
@@ -1,31 +0,0 @@
-# YouCompleteMe Integration
-
-## What is this?
-
-This provides the code necessary to configure vim's YCM plugin to provide C
-semantic support (completion, go-to-definition, etc) for developers working on
-the Neovim project.
-
-## Installation
-
-### Step 1
-
-Install [YouCompleteMe](https://github.com/Valloric/YouCompleteMe).
-
-### Step 2
-
-```bash
-cp contrib/YouCompleteMe/ycm_extra_conf.py .ycm_extra_conf.py
-echo .ycm_extra_conf.py >> .git/info/exclude
-make
-```
-
-Tip: to improve source code navigation, add something like this to your nvim
-configuration:
-
-```vim
-au FileType c,cpp nnoremap <buffer> <c-]> :YcmCompleter GoTo<CR>
-```
-
-And use `ctrl+]` when the cursor is positioned in a symbol to quickly jump to a
-definition or declaration.
diff --git a/contrib/YouCompleteMe/ycm_extra_conf.py b/contrib/YouCompleteMe/ycm_extra_conf.py
deleted file mode 100644
index e436609ce2..0000000000
--- a/contrib/YouCompleteMe/ycm_extra_conf.py
+++ /dev/null
@@ -1,65 +0,0 @@
-# .ycm_extra_conf.py for nvim source code.
-import os
-import ycm_core
-
-
-def DirectoryOfThisScript():
- return os.path.dirname(os.path.abspath(__file__))
-
-
-def GetDatabase():
- compilation_database_folder = os.path.join(DirectoryOfThisScript(),
- 'build')
- if os.path.exists(compilation_database_folder):
- return ycm_core.CompilationDatabase(compilation_database_folder)
- return None
-
-
-def GetCompilationInfoForFile(filename):
- database = GetDatabase()
- if not database:
- return None
- return database.GetCompilationInfoForFile(filename)
-
-
-# It seems YCM does not resolve directories correctly. This function will
-# adjust paths in the compiler flags to be absolute
-def FixDirectories(args, compiler_working_dir):
- def adjust_path(path):
- return os.path.abspath(os.path.join(compiler_working_dir, path))
-
- adjust_next_arg = False
- new_args = []
- for arg in args:
- if adjust_next_arg:
- arg = adjust_path(arg)
- adjust_next_arg = False
- else:
- for dir_flag in ['-I', '-isystem', '-o', '-c']:
- if arg.startswith(dir_flag):
- if arg != dir_flag:
- # flag and path are concatenated in same arg
- path = arg[len(dir_flag):]
- new_path = adjust_path(path)
- arg = '{0}{1}'.format(dir_flag, new_path)
- else:
- # path is specified in next argument
- adjust_next_arg = True
- new_args.append(arg)
- return new_args
-
-
-def FlagsForFile(filename):
- compilation_info = GetCompilationInfoForFile(filename)
- if not compilation_info:
- return None
- # Add flags not needed for clang-the-binary,
- # but needed for libclang-the-library (YCM uses this last one).
- flags = FixDirectories((list(compilation_info.compiler_flags_)
- if compilation_info.compiler_flags_
- else []), compilation_info.compiler_working_dir_)
- extra_flags = ['-Wno-newline-eof']
- return {
- 'flags': flags + extra_flags,
- 'do_cache': True
- }
diff --git a/contrib/doxygen/customdoxygen.css b/contrib/doxygen/customdoxygen.css
deleted file mode 100644
index 019073805a..0000000000
--- a/contrib/doxygen/customdoxygen.css
+++ /dev/null
@@ -1,757 +0,0 @@
-
-/*
- * Title, should be H1
- */
-
-.title {
- font-family: 'Lato', sans-serif;
- font-size: 2em;
- margin: 0.67em 0 0;
-}
-
-dt {
- font-weight: bold;
-}
-
-div.multicol {
- -moz-column-gap: 1em;
- -webkit-column-gap: 1em;
- -moz-column-count: 3;
- -webkit-column-count: 3;
-}
-
-
-div.qindex, div.navtab {
- background-color: #EBEFF6;
- border: 1px solid #A3B4D7;
- text-align: center;
-}
-
-div.line {
- font-family: monospace, fixed;
- min-height: 13px;
- line-height: 1.0;
- text-wrap: unrestricted;
- white-space: -moz-pre-wrap; /* Moz */
- white-space: -pre-wrap; /* Opera 4-6 */
- white-space: -o-pre-wrap; /* Opera 7 */
- white-space: pre-wrap; /* CSS3 */
- word-wrap: break-word; /* IE 5.5+ */
- text-indent: -53px;
- padding-left: 53px;
- padding-bottom: 0px;
- margin: 0px;
-}
-
-span.lineno {
- padding-right: 4px;
- text-align: right;
- border-right: 2px solid #0F0;
- white-space: pre;
-}
-
-/* @group Code Colorization */
-
-span.keyword {
- color: #008000
-}
-
-span.keywordtype {
- color: #604020
-}
-
-span.keywordflow {
- color: #e08000
-}
-
-span.comment {
- color: #800000
-}
-
-span.preprocessor {
- color: #806020
-}
-
-span.stringliteral {
- color: #002080
-}
-
-span.charliteral {
- color: #008080
-}
-
-span.vhdldigit {
- color: #ff00ff
-}
-
-span.vhdlchar {
- color: #000000
-}
-
-span.vhdlkeyword {
- color: #700070
-}
-
-span.vhdllogic {
- color: #ff0000
-}
-
-blockquote {
- background-color: #F7F8FB;
- border-left: 2px solid #9CAFD4;
- margin: 0 24px 0 4px;
- padding: 0 12px 0 16px;
-}
-
-/* @end */
-
-hr {
- height: 0px;
- border: none;
- display: none;
-}
-
-dl {
- padding: 0 0 0 10px;
-}
-
-/* dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug */
-dl.section {
- margin-left: 0px;
- padding-left: 0px;
-}
-
-dl.note {
- margin-left:-7px;
- padding-left: 3px;
- border-left:4px solid;
- border-color: #D0C000;
-}
-
-dl.warning, dl.attention {
- margin-left:-7px;
- padding-left: 3px;
- border-left:4px solid;
- border-color: #FF0000;
-}
-
-dl.pre, dl.post, dl.invariant {
- margin-left:-7px;
- padding-left: 3px;
- border-left:4px solid;
- border-color: #00D000;
-}
-
-dl.deprecated {
- margin-left:-7px;
- padding-left: 3px;
- border-left:4px solid;
- border-color: #505050;
-}
-
-dl.todo {
- margin-left:-7px;
- padding-left: 3px;
- border-left:4px solid;
- border-color: #00C0E0;
-}
-
-dl.test {
- margin-left:-7px;
- padding-left: 3px;
- border-left:4px solid;
- border-color: #3030E0;
-}
-
-dl.bug {
- margin-left:-7px;
- padding-left: 3px;
- border-left:4px solid;
- border-color: #C08050;
-}
-
-dl.section dd {
- margin-bottom: 6px;
-}
-
-/* tooltip related style info */
-
-.ttc {
- position: absolute;
- display: none;
-}
-
-#powerTip {
- cursor: default;
- white-space: nowrap;
- background-color: white;
- border: 1px solid gray;
- border-radius: 4px 4px 4px 4px;
- box-shadow: 1px 1px 7px gray;
- display: none;
- font-size: smaller;
- max-width: 80%;
- opacity: 0.9;
- padding: 1ex 1em 1em;
- position: absolute;
- z-index: 2147483647;
-}
-
-#powerTip div.ttdoc {
- color: grey;
- font-style: italic;
-}
-
-#powerTip div.ttname a {
- font-weight: bold;
-}
-
-#powerTip div.ttname {
- font-weight: bold;
-}
-
-#powerTip div.ttdeci {
- color: #006318;
-}
-
-#powerTip div {
- margin: 0px;
- padding: 0px;
- font: 12px/16px Roboto,sans-serif;
-}
-
-#powerTip:before, #powerTip:after {
- content: "";
- position: absolute;
- margin: 0px;
-}
-
-#powerTip.n:after, #powerTip.n:before,
-#powerTip.s:after, #powerTip.s:before,
-#powerTip.w:after, #powerTip.w:before,
-#powerTip.e:after, #powerTip.e:before,
-#powerTip.ne:after, #powerTip.ne:before,
-#powerTip.se:after, #powerTip.se:before,
-#powerTip.nw:after, #powerTip.nw:before,
-#powerTip.sw:after, #powerTip.sw:before {
- border: solid transparent;
- content: " ";
- height: 0;
- width: 0;
- position: absolute;
-}
-
-#powerTip.n:after, #powerTip.s:after,
-#powerTip.w:after, #powerTip.e:after,
-#powerTip.nw:after, #powerTip.ne:after,
-#powerTip.sw:after, #powerTip.se:after {
- border-color: rgba(255, 255, 255, 0);
-}
-
-#powerTip.n:before, #powerTip.s:before,
-#powerTip.w:before, #powerTip.e:before,
-#powerTip.nw:before, #powerTip.ne:before,
-#powerTip.sw:before, #powerTip.se:before {
- border-color: rgba(128, 128, 128, 0);
-}
-
-#powerTip.n:after, #powerTip.n:before,
-#powerTip.ne:after, #powerTip.ne:before,
-#powerTip.nw:after, #powerTip.nw:before {
- top: 100%;
-}
-
-#powerTip.n:after, #powerTip.ne:after, #powerTip.nw:after {
- border-top-color: #ffffff;
- border-width: 10px;
- margin: 0px -10px;
-}
-#powerTip.n:before {
- border-top-color: #808080;
- border-width: 11px;
- margin: 0px -11px;
-}
-#powerTip.n:after, #powerTip.n:before {
- left: 50%;
-}
-
-#powerTip.nw:after, #powerTip.nw:before {
- right: 14px;
-}
-
-#powerTip.ne:after, #powerTip.ne:before {
- left: 14px;
-}
-
-#powerTip.s:after, #powerTip.s:before,
-#powerTip.se:after, #powerTip.se:before,
-#powerTip.sw:after, #powerTip.sw:before {
- bottom: 100%;
-}
-
-#powerTip.s:after, #powerTip.se:after, #powerTip.sw:after {
- border-bottom-color: #ffffff;
- border-width: 10px;
- margin: 0px -10px;
-}
-
-#powerTip.s:before, #powerTip.se:before, #powerTip.sw:before {
- border-bottom-color: #808080;
- border-width: 11px;
- margin: 0px -11px;
-}
-
-#powerTip.s:after, #powerTip.s:before {
- left: 50%;
-}
-
-#powerTip.sw:after, #powerTip.sw:before {
- right: 14px;
-}
-
-#powerTip.se:after, #powerTip.se:before {
- left: 14px;
-}
-
-#powerTip.e:after, #powerTip.e:before {
- left: 100%;
-}
-#powerTip.e:after {
- border-left-color: #ffffff;
- border-width: 10px;
- top: 50%;
- margin-top: -10px;
-}
-#powerTip.e:before {
- border-left-color: #808080;
- border-width: 11px;
- top: 50%;
- margin-top: -11px;
-}
-
-#powerTip.w:after, #powerTip.w:before {
- right: 100%;
-}
-#powerTip.w:after {
- border-right-color: #ffffff;
- border-width: 10px;
- top: 50%;
- margin-top: -10px;
-}
-#powerTip.w:before {
- border-right-color: #808080;
- border-width: 11px;
- top: 50%;
- margin-top: -11px;
-}
-
-/*
- * Centered container for all content
- */
-
-div.contents,
-div.header > *,
-ul.tablist,
-.navpath ul {
- margin:0 15px;
-}
-@media (min-width: 568px) {
- div.contents,
- div.header > *,
- ul.tablist,
- .navpath ul {
- margin: 0 auto;
- width: 90%;
- max-width: 1200px;
- }
-}
-
-/*
- * padding inside content
- */
-
-div.contents > * {
- padding-top: 8px;
- padding-bottom: 8px;
-}
-
-@media (min-width: 568px) {
- div.contents > h2,
- div.contents > div.textblock,
- div.contents > div.memitem,
- div.contents > table.memberdecls h2,
- div.contents > p {
- padding-top: 30px;
- }
-}
-
-div.contents h2 {
- margin-top: 0px;
-}
-
-div.summary {
- display: none;
-}
-
-/*
- * Tabs
- *
- * Based on doxygen tabs.css
- */
-
-.tabs, .tabs2, .tabs3 {
- width: 100%;
- background-color: #f4f4f4;
- border-top: solid 1px #ececec;
-}
-
-.tablist {
- margin: 0;
- padding: 0;
- display: table;
-}
-
-.tablist li {
- float: left;
- display: table-cell;
- line-height: 36px;
- list-style: none;
-}
-
-.tablist a {
- display: block;
- padding: 0 30px 0 0;
-}
-
-.tabs3 .tablist a {
- padding: 0 20px 0 0;
-}
-
-.tablist li.current a {
- color: #54a23d;
-}
-
-/*
- * Navpath
- */
-
-.navpath ul
-{
- padding:20px 0px;
-}
-
-.navpath li
-{
- list-style-type:none;
- padding-right: 10px;
- float:left;
-}
-
-.navpath li.navelem a
-{
- padding-left: 10px;
-}
-
-.navpath li.navelem:before {
- content: "/";
- color: #777;
-}
-
-/*
- * Member
- *
- * Styles for detailed member documentation
- */
-
-.memitem {
- border-top: solid 1px #c9c9c9;
-}
-
-.memname {
- font-weight: bold;
- font-family: monospace;
-}
-
-td.memname {
- color: #54a23d;
-}
-
-.memname td {
- vertical-align: bottom;
-}
-
-.memproto, dl.reflist dt {
- font-weight: bold;
-}
-
-.memdoc, dl.reflist dd {
-}
-
-/*
- * Parameters
- */
-
-.paramkey {
- text-align: right;
-}
-
-.paramtype {
- white-space: nowrap;
-}
-
-.paramname {
- color: #aa0e0e;
- white-space: nowrap;
-}
-
-.paramname em {
- font-style: normal;
-}
-
-.params, .retval, .exception, .tparams {
- margin-left: 0px;
- padding-left: 0px;
-}
-
-.params td {
- padding-right: 1em;
- padding-bottom: 0.5em;
-}
-
-.params .paramname, .retval .paramname {
- font-weight: bold;
- vertical-align: top;
-}
-
-.params .paramtype {
- font-style: italic;
- vertical-align: top;
-}
-
-/*
- * Inline Label etc.
- */
-
-table.mlabels {
- border-spacing: 0px;
-}
-
-td.mlabels-left {
- width: 100%;
- padding: 0px;
-}
-
-td.mlabels-right {
- vertical-align: bottom;
- padding: 0px;
- white-space: nowrap;
-}
-
-span.mlabels {
- margin-left: 8px;
-}
-
-/*
- * Member Descriptions
- */
-
-table.memberdecls {
- font-family: monospace;
- border-spacing: 0px;
- padding: 0px;
-}
-
-.memSeparator {
- line-height: 1px;
- margin: 0px;
- padding: 0 0 0.2em 0;
-}
-
-.memItemLeft, .memTemplItemLeft {
- white-space: nowrap;
-}
-
-.memItemRight {
- width: 100%;
-}
-
-.memTemplParams {
- color: #4665A2;
- white-space: nowrap;
- font-size: 80%;
-}
-
-/*
- * Fieldtable (Enums)
- */
-
-.fieldtable td, .fieldtable th {
- padding: 0 1em 0.2em 0;
-}
-
-.fieldtable td.fieldtype, .fieldtable td.fieldname {
- white-space: nowrap;
- vertical-align: top;
-}
-
-/*
- * Directory
- */
-
-.directory table {
- border-collapse:collapse;
-}
-
-.directory td {
- margin: 0px;
- padding: 0px;
- vertical-align: top;
-}
-
-.directory td.entry {
- white-space: nowrap;
- padding-right: 6px;
- padding-top: 3px;
-}
-
-.directory td.entry a {
- outline:none;
-}
-
-.directory td.entry a img {
- border: none;
-}
-
-.directory td.desc {
- width: 100%;
- padding-left: 6px;
- padding-right: 6px;
- padding-top: 3px;
-}
-
-.directory tr.even {
- padding-left: 6px;
-}
-
-.directory img {
- vertical-align: -30%;
-}
-
-.directory .levels {
- white-space: nowrap;
- width: 100%;
- text-align: right;
-}
-
-.directory .levels span {
- cursor: pointer;
- padding-left: 2px;
- padding-right: 2px;
- color: #3c92d1;
-}
-
-.arrow {
- color: #9CAFD4;
- -webkit-user-select: none;
- -khtml-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
- cursor: pointer;
- font-size: 80%;
- display: inline-block;
- width: 16px;
- height: 22px;
-}
-
-.icon {
- font-family: Arial, Helvetica;
- font-weight: bold;
- font-size: 12px;
- height: 14px;
- width: 16px;
- display: inline-block;
- background-color: #54a23d;
- color: white;
- text-align: center;
- border-radius: 4px;
- margin-left: 2px;
- margin-right: 2px;
-}
-
-.icona {
- width: 24px;
- height: 22px;
- display: inline-block;
-}
-
-.iconfopen {
- width: 24px;
- height: 18px;
- margin-bottom: 4px;
- background-image:url('ftv2folderopen.png');
- background-position: 0px 0px;
- background-repeat: repeat-y;
- vertical-align:top;
- display: inline-block;
-}
-
-.iconfclosed {
- width: 24px;
- height: 18px;
- margin-bottom: 4px;
- background-image:url('ftv2folderclosed.png');
- background-position: 0px 0px;
- background-repeat: repeat-y;
- vertical-align:top;
- display: inline-block;
-}
-
-.icondoc {
- width: 24px;
- height: 18px;
- margin-bottom: 4px;
- background-image:url('ftv2doc.png');
- background-position: 0px -1px;
- background-repeat: repeat-y;
- vertical-align:top;
- display: inline-block;
-}
-
-/*
- * Data Structure Index
- *
- * Hardcoded style attribute
- */
-
-.contents > table[style] {
- margin: 20px auto !important;
-}
-
-/*
- * Search Box
- */
-
-#MSearchBox {
- right: 4%;
-}
-
-@media print
-{
- #top { display: none; }
- #side-nav { display: none; }
- #nav-path { display: none; }
- body { overflow:visible; }
- h1, h2, h3, h4, h5, h6 { page-break-after: avoid; }
- .summary { display: none; }
- .memitem { page-break-inside: avoid; }
- #doc-content
- {
- margin-left:0 !important;
- height:auto !important;
- width:auto !important;
- overflow:inherit;
- display:inline;
- }
-}
-
diff --git a/contrib/doxygen/extra.css b/contrib/doxygen/extra.css
deleted file mode 100644
index 59e0c25848..0000000000
--- a/contrib/doxygen/extra.css
+++ /dev/null
@@ -1,777 +0,0 @@
-/*! normalize.css v3.0.0 | MIT License | git.io/normalize */
-
-/**
- * 1. Set default font family to sans-serif.
- * 2. Prevent iOS text size adjust after orientation change, without disabling
- * user zoom.
- */
-
-html {
- font-family: sans-serif; /* 1 */
- -ms-text-size-adjust: 100%; /* 2 */
- -webkit-text-size-adjust: 100%; /* 2 */
-}
-
-/**
- * Remove default margin.
- */
-
-body {
- margin: 0;
-}
-
-/* HTML5 display definitions
- ========================================================================== */
-
-/**
- * Correct `block` display not defined in IE 8/9.
- */
-
-article,
-aside,
-details,
-figcaption,
-figure,
-footer,
-header,
-hgroup,
-main,
-nav,
-section,
-summary {
- display: block;
-}
-
-/**
- * 1. Correct `inline-block` display not defined in IE 8/9.
- * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
- */
-
-audio,
-canvas,
-progress,
-video {
- display: inline-block; /* 1 */
- vertical-align: baseline; /* 2 */
-}
-
-/**
- * Prevent modern browsers from displaying `audio` without controls.
- * Remove excess height in iOS 5 devices.
- */
-
-audio:not([controls]) {
- display: none;
- height: 0;
-}
-
-/**
- * Address `[hidden]` styling not present in IE 8/9.
- * Hide the `template` element in IE, Safari, and Firefox < 22.
- */
-
-[hidden],
-template {
- display: none;
-}
-
-/* Links
- ========================================================================== */
-
-/**
- * Remove the gray background color from active links in IE 10.
- */
-
-a {
- background: transparent;
-}
-
-/**
- * Improve readability when focused and also mouse hovered in all browsers.
- */
-
-a:active,
-a:hover {
- outline: 0;
-}
-
-/* Text-level semantics
- ========================================================================== */
-
-/**
- * Address styling not present in IE 8/9, Safari 5, and Chrome.
- */
-
-abbr[title] {
- border-bottom: 1px dotted;
-}
-
-/**
- * Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
- */
-
-b,
-strong {
- font-weight: bold;
-}
-
-/**
- * Address styling not present in Safari 5 and Chrome.
- */
-
-dfn {
- font-style: italic;
-}
-
-/**
- * Address variable `h1` font-size and margin within `section` and `article`
- * contexts in Firefox 4+, Safari 5, and Chrome.
- */
-
-h1 {
- font-size: 2em;
- margin: 0.67em 0;
-}
-
-/**
- * Address styling not present in IE 8/9.
- */
-
-mark {
- background: #ff0;
- color: #000;
-}
-
-/**
- * Address inconsistent and variable font size in all browsers.
- */
-
-small {
- font-size: 80%;
-}
-
-/**
- * Prevent `sub` and `sup` affecting `line-height` in all browsers.
- */
-
-sub,
-sup {
- font-size: 75%;
- line-height: 0;
- position: relative;
- vertical-align: baseline;
-}
-
-sup {
- top: -0.5em;
-}
-
-sub {
- bottom: -0.25em;
-}
-
-/* Embedded content
- ========================================================================== */
-
-/**
- * Remove border when inside `a` element in IE 8/9.
- */
-
-img {
- border: 0;
-}
-
-/**
- * Correct overflow displayed oddly in IE 9.
- */
-
-svg:not(:root) {
- overflow: hidden;
-}
-
-/* Grouping content
- ========================================================================== */
-
-/**
- * Address margin not present in IE 8/9 and Safari 5.
- */
-
-figure {
- margin: 1em 40px;
-}
-
-/**
- * Address differences between Firefox and other browsers.
- */
-
-hr {
- -moz-box-sizing: content-box;
- box-sizing: content-box;
- height: 0;
-}
-
-/**
- * Contain overflow in all browsers.
- */
-
-pre {
- overflow: auto;
-}
-
-/**
- * Address odd `em`-unit font size rendering in all browsers.
- */
-
-code,
-kbd,
-pre,
-samp {
- font-family: monospace, monospace;
- font-size: 1em;
-}
-
-/* Forms
- ========================================================================== */
-
-/**
- * Known limitation: by default, Chrome and Safari on OS X allow very limited
- * styling of `select`, unless a `border` property is set.
- */
-
-/**
- * 1. Correct color not being inherited.
- * Known issue: affects color of disabled elements.
- * 2. Correct font properties not being inherited.
- * 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome.
- */
-
-button,
-input,
-optgroup,
-select,
-textarea {
- color: inherit; /* 1 */
- font: inherit; /* 2 */
- margin: 0; /* 3 */
-}
-
-/**
- * Address `overflow` set to `hidden` in IE 8/9/10.
- */
-
-button {
- overflow: visible;
-}
-
-/**
- * Address inconsistent `text-transform` inheritance for `button` and `select`.
- * All other form control elements do not inherit `text-transform` values.
- * Correct `button` style inheritance in Firefox, IE 8+, and Opera
- * Correct `select` style inheritance in Firefox.
- */
-
-button,
-select {
- text-transform: none;
-}
-
-/**
- * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
- * and `video` controls.
- * 2. Correct inability to style clickable `input` types in iOS.
- * 3. Improve usability and consistency of cursor style between image-type
- * `input` and others.
- */
-
-button,
-html input[type="button"], /* 1 */
-input[type="reset"],
-input[type="submit"] {
- -webkit-appearance: button; /* 2 */
- cursor: pointer; /* 3 */
-}
-
-/**
- * Re-set default cursor for disabled elements.
- */
-
-button[disabled],
-html input[disabled] {
- cursor: default;
-}
-
-/**
- * Remove inner padding and border in Firefox 4+.
- */
-
-button::-moz-focus-inner,
-input::-moz-focus-inner {
- border: 0;
- padding: 0;
-}
-
-/**
- * Address Firefox 4+ setting `line-height` on `input` using `!important` in
- * the UA stylesheet.
- */
-
-input {
- line-height: normal;
-}
-
-/**
- * It's recommended that you don't attempt to style these elements.
- * Firefox's implementation doesn't respect box-sizing, padding, or width.
- *
- * 1. Address box sizing set to `content-box` in IE 8/9/10.
- * 2. Remove excess padding in IE 8/9/10.
- */
-
-input[type="checkbox"],
-input[type="radio"] {
- box-sizing: border-box; /* 1 */
- padding: 0; /* 2 */
-}
-
-/**
- * Fix the cursor style for Chrome's increment/decrement buttons. For certain
- * `font-size` values of the `input`, it causes the cursor style of the
- * decrement button to change from `default` to `text`.
- */
-
-input[type="number"]::-webkit-inner-spin-button,
-input[type="number"]::-webkit-outer-spin-button {
- height: auto;
-}
-
-/**
- * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome.
- * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome
- * (include `-moz` to future-proof).
- */
-
-input[type="search"] {
- -webkit-appearance: textfield; /* 1 */
- -moz-box-sizing: content-box;
- -webkit-box-sizing: content-box; /* 2 */
- box-sizing: content-box;
-}
-
-/**
- * Remove inner padding and search cancel button in Safari and Chrome on OS X.
- * Safari (but not Chrome) clips the cancel button when the search input has
- * padding (and `textfield` appearance).
- */
-
-input[type="search"]::-webkit-search-cancel-button,
-input[type="search"]::-webkit-search-decoration {
- -webkit-appearance: none;
-}
-
-/**
- * Define consistent border, margin, and padding.
- */
-
-fieldset {
- border: 1px solid #c0c0c0;
- margin: 0 2px;
- padding: 0.35em 0.625em 0.75em;
-}
-
-/**
- * 1. Correct `color` not being inherited in IE 8/9.
- * 2. Remove padding so people aren't caught out if they zero out fieldsets.
- */
-
-legend {
- border: 0; /* 1 */
- padding: 0; /* 2 */
-}
-
-/**
- * Remove default vertical scrollbar in IE 8/9.
- */
-
-textarea {
- overflow: auto;
-}
-
-/**
- * Don't inherit the `font-weight` (applied by a rule above).
- * NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
- */
-
-optgroup {
- font-weight: bold;
-}
-
-/* Tables
- ========================================================================== */
-
-/**
- * Remove most spacing between table cells.
- */
-
-table {
- border-collapse: collapse;
- border-spacing: 0;
-}
-
-td,
-th {
- padding: 0;
-}
-
-/* neovim.io/css/main.css */
-
-* {
- -moz-box-sizing: border-box;
- box-sizing: border-box;
-}
-
-body {
- font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
- font-size: 15px;
- line-height: 1.4;
- color: #444;
- background-color: #fbfbfb;
-}
-@media (min-width: 568px) {
- body {
- font-size: 17px;
- }
-}
-
-a {
- color: #3c92d1;
- text-decoration: none;
-}
-
-h1,
-h2,
-h3 {
- font-family: 'Lato', sans-serif;
-}
-
-h2 {
- font-size: 24px;
- font-weight: 400;
- color: #54a23d;
- margin-bottom: 0;
-}
-@media (min-width: 568px) {
- h2 {
- font-size: 30px;
- }
-}
-h3 {
- /* color: #54a23d; */
-}
-
-blockquote {
- border-left: 5px solid #eeeeee;
- margin-left: 0;
- padding-left: 15px;
-}
-
-/*
- * Content container
- *
- * Centered container for all content
- */
-
-.container {
- margin: 0 15px;
-}
-@media (min-width: 568px) {
- .container {
- margin: 0 auto;
- width: 90%;
- max-width: 1200px;
- }
-}
-
-/*
- * Navbar
- *
- * Logo and navigation at the top of the page
- */
-
-.navbar {
- padding: 12px 0;
-}
-@media (min-width: 568px) {
- .navbar {
- padding-top: 30px;
- padding-bottom: 30px;
- }
-}
-
-.logo {
- width: 120px;
- position: relative;
- top: 4px;
-}
-@media (min-width: 568px) {
- .logo {
- width: 180px;
- }
-}
-
-.site-nav {
- display: none;
-}
-@media (min-width: 568px) {
- .site-nav {
- display: block;
- float: right;
- }
- .site-nav ul {
- list-style: none;
- }
- .site-nav li {
- display: inline-block;
- margin-top: 5px;
- margin-left: 20px;
- font-size: 18px;
- font-family: 'Lato', sans-serif;
- }
-}
-
-/*
- * Masthead
- *
- * Big intro billboard
- */
-
-.masthead {
- border-top: solid 1px #ececec;
- border-bottom: solid 1px #ececec;
- text-align: center;
- padding: 20px 12px;
-}
-@media (min-width: 568px) {
- .masthead {
- padding-top: 40px;
- padding-bottom: 40px;
- }
-}
-
-.masthead .container {
-}
-@media (min-width: 568px) {
- .masthead .container {
- width: 568px;
- margin: 0 auto;
- }
-}
-
-.masthead h1 {
- font-size: 20px;
- font-weight: 400;
-}
-@media (min-width: 568px) {
- .masthead h1 {
- font-size: 40px;
- margin-bottom: 50px;
- }
-}
-
-.masthead .lead {
- font-weight: 900;
- color: #54a23d;
- font-family: 'Lato', sans-serif;
- font-size: 16px;
- font-weight: 900;
-}
-@media (min-width: 568px) {
- .masthead .lead {
- font-size: 26px;
- }
-}
-
-/*
- * Columns
- *
- * Two columns utilizing the golden ratio
- */
-
-.col-wide,
-.col-narrow {
- display: block;
-}
-@media (min-width: 568px) {
- .col-wide {
- display: table-cell;
- width: 61.8%;
- vertical-align: top;
- }
- .col-narrow {
- display: table-cell;
- width: 38.2%;
- padding-left: 6%;
- vertical-align: top;
- }
-}
-
-/*
- * Front page
- *
- * Big section blocks for the front page
- */
-
-.front-section {
- padding: 8px 0;
-}
-@media (min-width: 568px) {
- .front-section {
- padding-top: 30px;
- padding-bottom: 30px;
- display: table;
- width: 100%;
- }
- .front-section h2:first-child {
- margin-top: 0;
- }
-}
-
-.front-section.shaded {
- background-color: #f4f4f4;
-}
-
-/*
- * FAQs
- *
- * Formatted as a definition list
- */
-
-.faqs dt {
- font-weight: 700;
-}
-
-.faqs dd {
- color: #777;
- font-size: 15px;
- margin-left: 0;
- margin-bottom: 20px;
-}
-
-/*
- * Buttons
- *
- * Fun to click
- */
-
-.btn {
- display: inline-block;
- text-align: center;
- vertical-align: middle;
- background-color: #3c92d1;
- color: #fff;
- font-family: 'Lato', sans-serif;
- border-radius: 4px;
- padding: 15px 30px;
-}
-
-.btn:hover {
- background-color: #3889c4;
-}
-
-.btn.full-width {
- width: 100%;
-}
-
-/*
- * Sponsors
- *
- * Callout-style box
- */
-.sponsors {
- background-color: #f5f5f5;
- padding: 5px 20px;
- border-radius: 4px;
- margin-top: 10px;
-}
-
-.sponsors h3 {
- color: #54a23d;
-}
-
-.first-level-sponsor {
- margin-bottom: 20px;
-}
-
-.first-level-sponsor img {
- max-width: 100%;
-}
-
-.second-level-sponsors {
- width: 100%;
-}
-
-.second-level-sponsors td {
- width: 50%;
- padding: 12px 0;
- text-align: center;
- vertical-align: middle;
-}
-
-.second-level-sponsors img {
- width: 80%;
-}
-@media (min-width: 568px) {
- .second-level-sponsor img {
- width: auto;
- }
-}
-
-/*
- * Footer
- *
- * Links, legalese, etc.
- */
-
-footer .container {
- border-top: solid 1px #ececec;
- padding: 20px 0 50px;
- font-size: 12px;
- color: #777;
-}
-@media (min-width: 568px) {
- footer .container {
- font-size: 14px;
- display: table;
- }
-}
-
-footer a {
- color: #444;
-}
-
-
-/*
- * Misc
- *
- * Odds 'n ends
- */
-
-.light {
- color: #777;
-}
-
-.light a {
- color: #444;
-}
-
-.small {
- font-size: 70%;
-}
-
-/*
- * Newsletter CSS
- */
-
-.newsletter h1 {
- margin-bottom: 0px;
-}
-
diff --git a/contrib/doxygen/footer.html b/contrib/doxygen/footer.html
deleted file mode 100644
index a6d8bb1ee4..0000000000
--- a/contrib/doxygen/footer.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<!-- HTML footer for doxygen 1.8.6-->
-<!-- start footer part -->
-<!--BEGIN GENERATE_TREEVIEW-->
-<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
- <ul>
- $navpath
- <li class="footer">$generatedby
- <a href="http://www.doxygen.org/index.html">
- <img class="footer" src="$relpath^doxygen.png" alt="doxygen"/></a> $doxygenversion </li>
- </ul>
-</div>
-<!--END GENERATE_TREEVIEW-->
-<!--BEGIN !GENERATE_TREEVIEW-->
-<footer>
- <div class="container">
- $generatedby
- <a href="http://www.doxygen.org/index.html">Doxygen</a>
- $doxygenversion
- </div>
-</footer>
-<!--END !GENERATE_TREEVIEW-->
-</body>
-</html>
diff --git a/contrib/doxygen/header.html b/contrib/doxygen/header.html
deleted file mode 100644
index c9c1509d86..0000000000
--- a/contrib/doxygen/header.html
+++ /dev/null
@@ -1,36 +0,0 @@
-<!-- HTML header for doxygen 1.8.6-->
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
-<meta http-equiv="X-UA-Compatible" content="IE=9"/>
-<meta name="generator" content="Doxygen $doxygenversion"/>
-<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
-<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
-<script type="text/javascript" src="$relpath^jquery.js"></script>
-<script type="text/javascript" src="$relpath^dynsections.js"></script>
-$treeview
-$search
-$mathjax
-<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
-$extrastylesheet
-<link href="http://fonts.googleapis.com/css?family=Lato:400,700,900" rel="stylesheet">
-</head>
-<body>
-<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
-
-<!--BEGIN TITLEAREA-->
-<header class="navbar">
- <div class="container">
- <nav class="site-nav">
- <ul>
- <li><a href="http://neovim.io/">Neovim Home</a></li>
- </ul>
- </nav>
- <a href="$relpath^index.html">
- <img src="$relpath^$projectlogo" class="logo" alt="Neovim Dev-Doc">
- </a>
- </div>
-</header>
-<!--END TITLEAREA-->
-<!-- end header part -->
diff --git a/contrib/doxygen/logo-devdoc.png b/contrib/doxygen/logo-devdoc.png
deleted file mode 100644
index a25ef2b9d7..0000000000
--- a/contrib/doxygen/logo-devdoc.png
+++ /dev/null
Binary files differ
diff --git a/contrib/flake.lock b/contrib/flake.lock
index b72e1f8d5f..c6dfb4642b 100644
--- a/contrib/flake.lock
+++ b/contrib/flake.lock
@@ -2,11 +2,11 @@
"nodes": {
"flake-utils": {
"locked": {
- "lastModified": 1644229661,
- "narHash": "sha256-1YdnJAsNy69bpcjuoKdOYQX0YxZBiCYZo4Twxerqv7k=",
+ "lastModified": 1667395993,
+ "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
"owner": "numtide",
"repo": "flake-utils",
- "rev": "3cecb5b042f7f209c56ffd8371b2711a290ec797",
+ "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
"type": "github"
},
"original": {
@@ -17,11 +17,11 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1646254136,
- "narHash": "sha256-8nQx02tTzgYO21BP/dy5BCRopE8OwE8Drsw98j+Qoaw=",
+ "lastModified": 1671983799,
+ "narHash": "sha256-Z2Ro6hFPZHkBqkVXY5/aBUzxi5xizQGvuHQ9+T5B/ks=",
"owner": "nixos",
"repo": "nixpkgs",
- "rev": "3e072546ea98db00c2364b81491b893673267827",
+ "rev": "fad51abd42ca17a60fc1d4cb9382e2d79ae31836",
"type": "github"
},
"original": {
diff --git a/contrib/flake.nix b/contrib/flake.nix
index a1072674ba..0898c943d7 100644
--- a/contrib/flake.nix
+++ b/contrib/flake.nix
@@ -8,130 +8,110 @@
outputs = { self, nixpkgs, flake-utils }:
{
- overlay = final: prev:
- let
- pkgs = nixpkgs.legacyPackages.${prev.system};
- in
- rec {
- 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
- neovim-debug = let
- stdenv = if pkgs.stdenv.isLinux then pkgs.llvmPackages_latest.stdenv else pkgs.stdenv;
- in
- ((neovim.override {
- lua = pkgs.luajit;
- inherit stdenv;
- }).overrideAttrs (oa: {
-
- dontStrip = true;
- NIX_CFLAGS_COMPILE = " -ggdb -Og";
-
- cmakeBuildType = "Debug";
- cmakeFlags = oa.cmakeFlags ++ [
- "-DMIN_LOG_LEVEL=0"
- ];
-
- disallowedReferences = [];
- }));
-
- # for neovim developers, beware of the slow binary
- neovim-developer =
- let
- lib = nixpkgs.lib;
- luacheck = pkgs.luaPackages.luacheck;
- in
- (neovim-debug.override ({ doCheck = pkgs.stdenv.isLinux; })).overrideAttrs (oa: {
- cmakeFlags = oa.cmakeFlags ++ [
- "-DLUACHECK_PRG=${luacheck}/bin/luacheck"
- "-DMIN_LOG_LEVEL=0"
- "-DENABLE_LTO=OFF"
- ] ++ pkgs.lib.optionals pkgs.stdenv.isLinux [
- # https://github.com/google/sanitizers/wiki/AddressSanitizerFlags
- # https://clang.llvm.org/docs/AddressSanitizer.html#symbolizing-the-reports
- "-DCLANG_ASAN_UBSAN=ON"
- ];
- });
- };
- } //
- flake-utils.lib.eachDefaultSystem (system:
+ overlay = final: prev: {
+
+ neovim = final.neovim-unwrapped.overrideAttrs (oa: rec {
+ version = self.shortRev or "dirty";
+ src = ../.;
+ preConfigure = ''
+ sed -i cmake.config/versiondef.h.in -e 's/@NVIM_VERSION_PRERELEASE@/-dev-${version}/'
+ '';
+ });
+
+ # a development binary to help debug issues
+ neovim-debug = let
+ stdenv = if final.stdenv.isLinux then
+ final.llvmPackages_latest.stdenv
+ else
+ final.stdenv;
+ in (final.neovim.override {
+ lua = final.luajit;
+ inherit stdenv;
+ }).overrideAttrs (oa: {
+
+ dontStrip = true;
+ NIX_CFLAGS_COMPILE = " -ggdb -Og";
+
+ cmakeBuildType = "Debug";
+ cmakeFlags = oa.cmakeFlags ++ [ "-DMIN_LOG_LEVEL=0" ];
+
+ disallowedReferences = [ ];
+ });
+
+ # for neovim developers, beware of the slow binary
+ neovim-developer = let inherit (final.luaPackages) luacheck;
+ in (final.neovim-debug.override {
+ doCheck = final.stdenv.isLinux;
+ }).overrideAttrs (oa: {
+ cmakeFlags = oa.cmakeFlags ++ [
+ "-DLUACHECK_PRG=${luacheck}/bin/luacheck"
+ "-DMIN_LOG_LEVEL=0"
+ "-DENABLE_LTO=OFF"
+ ] ++ final.lib.optionals final.stdenv.isLinux [
+ # https://github.com/google/sanitizers/wiki/AddressSanitizerFlags
+ # https://clang.llvm.org/docs/AddressSanitizer.html#symbolizing-the-reports
+ "-DCLANG_ASAN_UBSAN=ON"
+ ];
+ });
+ };
+ } // flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
overlays = [ self.overlay ];
inherit system;
};
- pythonEnv = pkgs.python3.withPackages(ps: [
+ lua = pkgs.lua5_1;
+
+ pythonEnv = pkgs.python3.withPackages (ps: [
ps.msgpack
- ps.flake8 # for 'make pylint'
]);
- in
- rec {
-
+ in {
packages = with pkgs; {
+ default = neovim;
inherit neovim neovim-debug neovim-developer;
};
checks = {
- pylint = pkgs.runCommandNoCC "pylint" {
- nativeBuildInputs = [ pythonEnv ];
- preferLocalBuild = true;
- } "make -C ${./..} pylint > $out";
-
- shlint = pkgs.runCommandNoCC "shlint" {
+ shlint = pkgs.runCommand "shlint" {
nativeBuildInputs = [ pkgs.shellcheck ];
preferLocalBuild = true;
- } "make -C ${./..} shlint > $out";
+ } "make -C ${./..} shlint > $out";
};
+ # kept for backwards-compatibility
defaultPackage = pkgs.neovim;
- apps = {
- nvim = flake-utils.lib.mkApp { drv = pkgs.neovim; name = "nvim"; };
- nvim-debug = flake-utils.lib.mkApp { drv = pkgs.neovim-debug; name = "nvim"; };
- };
-
- defaultApp = apps.nvim;
+ devShells = {
+ default = pkgs.neovim-developer.overrideAttrs (oa: {
- devShell = let
- in
- pkgs.neovim-developer.overrideAttrs(oa: {
-
- buildInputs = with pkgs; oa.buildInputs ++ [
+ buildInputs = with pkgs;
+ oa.buildInputs ++ [
cmake
+ lua.pkgs.luacheck
+ sumneko-lua-language-server
pythonEnv
include-what-you-use # for scripts/check-includes.py
jq # jq for scripts/vim-patch.sh -r
shellcheck # for `make shlint`
- doxygen # for script/gen_vimdoc.py
+ doxygen # for script/gen_vimdoc.py
clang-tools # for clangd to find the correct headers
];
- shellHook = oa.shellHook + ''
- export NVIM_PYTHON_LOG_LEVEL=DEBUG
- export NVIM_LOG_FILE=/tmp/nvim.log
- export ASAN_SYMBOLIZER_PATH=${pkgs.llvm_11}/bin/llvm-symbolizer
-
- # ASAN_OPTIONS=detect_leaks=1
- export ASAN_OPTIONS="log_path=./test.log:abort_on_error=1"
- export UBSAN_OPTIONS=print_stacktrace=1
- mkdir -p build/runtime/parser
- # nvim looks into CMAKE_INSTALL_DIR. Hack to avoid errors
- # when running the functionaltests
- mkdir -p outputs/out/share/nvim/syntax
- touch outputs/out/share/nvim/syntax/syntax.vim
-
- # for treesitter functionaltests
- mkdir -p runtime/parser
- cp -f ${pkgs.tree-sitter.builtGrammars.tree-sitter-c}/parser runtime/parser/c.so
- '';
- });
- });
+ shellHook = oa.shellHook + ''
+ export NVIM_PYTHON_LOG_LEVEL=DEBUG
+ export NVIM_LOG_FILE=/tmp/nvim.log
+ export ASAN_SYMBOLIZER_PATH=${pkgs.llvm_11}/bin/llvm-symbolizer
+
+ # ASAN_OPTIONS=detect_leaks=1
+ export ASAN_OPTIONS="log_path=./test.log:abort_on_error=1"
+ export UBSAN_OPTIONS=print_stacktrace=1
+
+ # for treesitter functionaltests
+ mkdir -p runtime/parser
+ cp -f ${pkgs.vimPlugins.nvim-treesitter.builtGrammars.c}/parser runtime/parser/c.so
+ '';
+ });
+ };
+ });
}
diff --git a/contrib/luarc.json b/contrib/luarc.json
index 8d76d1261d..ebad0581b9 100644
--- a/contrib/luarc.json
+++ b/contrib/luarc.json
@@ -10,13 +10,17 @@
"before_each",
"after_each",
"setup",
- "teardown"
+ "teardown",
+ "finally",
+ "lfs"
]
},
"workspace": {
- "library": {
- "runtime/lua": true
- },
+ "library": [
+ "runtime/lua",
+ "${3rd}/lfs/library"
+ ],
+ "checkThirdParty": false,
"maxPreload": 2000,
"preloadFileSize": 1000
},
diff --git a/contrib/vim-addon-local-vimrc/README.md b/contrib/vim-addon-local-vimrc/README.md
deleted file mode 100644
index 2ff9f0d627..0000000000
--- a/contrib/vim-addon-local-vimrc/README.md
+++ /dev/null
@@ -1,18 +0,0 @@
-# vim-addon-local-vimrc
-
-## Installation
-
-### Step 1
-
-Install [vim-addon-local-vimrc](https://github.com/MarcWeber/vim-addon-local-vimrc).
-For example with [Vundle](https://github.com/MarcWeber/vim-addon-local-vimrc):
-```vim
-Bundle 'MarcWeber/vim-addon-local-vimrc'
-```
-
-### Step 2
-
-```bash
-cp vimrc ../../.vimrc
-echo .vimrc >> ../../.git/info/exclude
-```
diff --git a/contrib/vim-addon-local-vimrc/vimrc b/contrib/vim-addon-local-vimrc/vimrc
deleted file mode 100644
index cd8a2896ed..0000000000
--- a/contrib/vim-addon-local-vimrc/vimrc
+++ /dev/null
@@ -1,11 +0,0 @@
-set modelines=0
-
-augroup LOCAL_SETUP
- autocmd!
- autocmd BufRead,BufNewFile *.h set filetype=c
- autocmd FileType c setlocal expandtab
- autocmd FileType c setlocal shiftwidth=2
- autocmd FileType c setlocal softtabstop=2
- autocmd FileType c setlocal textwidth=80
- autocmd FileType c setlocal comments=:///,://
-augroup end
diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt
index e0a0b34d28..581a4545db 100644
--- a/runtime/CMakeLists.txt
+++ b/runtime/CMakeLists.txt
@@ -63,7 +63,7 @@ foreach(DF ${DOCFILES})
endforeach()
add_custom_command(OUTPUT ${GENERATED_HELP_TAGS}
- COMMAND ${CMAKE_COMMAND} -E remove doc/*
+ COMMAND ${CMAKE_COMMAND} -E remove_directory doc
COMMAND ${CMAKE_COMMAND} -E copy_directory
${PROJECT_SOURCE_DIR}/runtime/doc doc
COMMAND "${PROJECT_BINARY_DIR}/bin/nvim"
@@ -75,8 +75,10 @@ add_custom_command(OUTPUT ${GENERATED_HELP_TAGS}
)
+# TODO: This doesn't work. wait for "nvim -l" to land?
add_custom_target(doc_html
- COMMAND make html
+ COMMAND "${PROJECT_BINARY_DIR}/bin/nvim"
+ -V1 -es --clean -c "lua require('scripts.gen_help_html').gen('./build/runtime/doc', './build/doc_html', nil, 'todo_commit_id')" -c "0cq"
DEPENDS
${GENERATED_HELP_TAGS}
WORKING_DIRECTORY "${GENERATED_RUNTIME_DIR}/doc"
@@ -108,12 +110,16 @@ if(NOT APPLE)
install_helper(
FILES ${CMAKE_CURRENT_SOURCE_DIR}/nvim.desktop
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications)
-
- install_helper(
- FILES ${CMAKE_CURRENT_SOURCE_DIR}/nvim.png
- DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/128x128/apps)
endif()
+install_helper(
+ FILES ${CMAKE_CURRENT_SOURCE_DIR}/nvim.png
+ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/128x128/apps)
+
+install_helper(
+ FILES ${CMAKE_CURRENT_SOURCE_DIR}/neovim.ico
+ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/nvim/runtime)
+
globrecurse_wrapper(RUNTIME_PROGRAMS ${CMAKE_CURRENT_SOURCE_DIR} *.awk *.sh *.bat)
foreach(PROG ${RUNTIME_PROGRAMS})
@@ -123,7 +129,7 @@ foreach(PROG ${RUNTIME_PROGRAMS})
endforeach()
globrecurse_wrapper(RUNTIME_FILES ${CMAKE_CURRENT_SOURCE_DIR}
- *.vim *.lua *.dict *.py *.rb *.ps *.spl *.tutor *.tutor.json)
+ *.vim *.lua *.scm *.dict *.py *.rb *.ps *.spl *.tutor *.tutor.json)
foreach(F ${RUNTIME_FILES})
get_filename_component(BASEDIR ${F} DIRECTORY)
diff --git a/runtime/autoload/ccomplete.lua b/runtime/autoload/ccomplete.lua
new file mode 100644
index 0000000000..f4a3eabd9a
--- /dev/null
+++ b/runtime/autoload/ccomplete.lua
@@ -0,0 +1,857 @@
+----------------------------------------
+-- This file is generated via github.com/tjdevries/vim9jit
+-- For any bugs, please first consider reporting there.
+----------------------------------------
+
+-- Ignore "value assigned to a local variable is unused" because
+-- we can't guarantee that local variables will be used by plugins
+-- luacheck: ignore 311
+
+local vim9 = require('_vim9script')
+local M = {}
+local prepended = nil
+local grepCache = nil
+local Complete = nil
+local GetAddition = nil
+local Tag2item = nil
+local Dict2info = nil
+local ParseTagline = nil
+local Tagline2item = nil
+local Tagcmd2extra = nil
+local Nextitem = nil
+local StructMembers = nil
+local SearchMembers = nil
+-- vim9script
+
+-- # Vim completion script
+-- # Language: C
+-- # Maintainer: Bram Moolenaar <Bram@vim.org>
+-- # Rewritten in Vim9 script by github user lacygoill
+-- # Last Change: 2022 Jan 31
+
+prepended = ''
+grepCache = vim.empty_dict()
+
+-- # This function is used for the 'omnifunc' option.
+
+Complete = function(findstart, abase)
+ findstart = vim9.bool(findstart)
+ if vim9.bool(findstart) then
+ -- # Locate the start of the item, including ".", "->" and "[...]".
+ local line = vim9.fn.getline('.')
+ local start = vim9.fn.charcol('.') - 1
+ local lastword = -1
+ while start > 0 do
+ if vim9.ops.RegexpMatches(vim9.index(line, vim9.ops.Minus(start, 1)), '\\w') then
+ start = start - 1
+ elseif
+ vim9.bool(vim9.ops.RegexpMatches(vim9.index(line, vim9.ops.Minus(start, 1)), '\\.'))
+ then
+ if lastword == -1 then
+ lastword = start
+ end
+ start = start - 1
+ elseif
+ vim9.bool(
+ start > 1
+ and vim9.index(line, vim9.ops.Minus(start, 2)) == '-'
+ and vim9.index(line, vim9.ops.Minus(start, 1)) == '>'
+ )
+ then
+ if lastword == -1 then
+ lastword = start
+ end
+ start = vim9.ops.Minus(start, 2)
+ elseif vim9.bool(vim9.index(line, vim9.ops.Minus(start, 1)) == ']') then
+ -- # Skip over [...].
+ local n = 0
+ start = start - 1
+ while start > 0 do
+ start = start - 1
+ if vim9.index(line, start) == '[' then
+ if n == 0 then
+ break
+ end
+ n = n - 1
+ elseif vim9.bool(vim9.index(line, start) == ']') then
+ n = n + 1
+ end
+ end
+ else
+ break
+ end
+ end
+
+ -- # Return the column of the last word, which is going to be changed.
+ -- # Remember the text that comes before it in prepended.
+ if lastword == -1 then
+ prepended = ''
+ return vim9.fn.byteidx(line, start)
+ end
+ prepended = vim9.slice(line, start, vim9.ops.Minus(lastword, 1))
+ return vim9.fn.byteidx(line, lastword)
+ end
+
+ -- # Return list of matches.
+
+ local base = prepended .. abase
+
+ -- # Don't do anything for an empty base, would result in all the tags in the
+ -- # tags file.
+ if base == '' then
+ return {}
+ end
+
+ -- # init cache for vimgrep to empty
+ grepCache = {}
+
+ -- # Split item in words, keep empty word after "." or "->".
+ -- # "aa" -> ['aa'], "aa." -> ['aa', ''], "aa.bb" -> ['aa', 'bb'], etc.
+ -- # We can't use split, because we need to skip nested [...].
+ -- # "aa[...]" -> ['aa', '[...]'], "aa.bb[...]" -> ['aa', 'bb', '[...]'], etc.
+ local items = {}
+ local s = 0
+ local arrays = 0
+ while 1 do
+ local e = vim9.fn.charidx(base, vim9.fn.match(base, '\\.\\|->\\|\\[', s))
+ if e < 0 then
+ if s == 0 or vim9.index(base, vim9.ops.Minus(s, 1)) ~= ']' then
+ vim9.fn.add(items, vim9.slice(base, s, nil))
+ end
+ break
+ end
+ if s == 0 or vim9.index(base, vim9.ops.Minus(s, 1)) ~= ']' then
+ vim9.fn.add(items, vim9.slice(base, s, vim9.ops.Minus(e, 1)))
+ end
+ if vim9.index(base, e) == '.' then
+ -- # skip over '.'
+ s = vim9.ops.Plus(e, 1)
+ elseif vim9.bool(vim9.index(base, e) == '-') then
+ -- # skip over '->'
+ s = vim9.ops.Plus(e, 2)
+ else
+ -- # Skip over [...].
+ local n = 0
+ s = e
+ e = e + 1
+ while e < vim9.fn.strcharlen(base) do
+ if vim9.index(base, e) == ']' then
+ if n == 0 then
+ break
+ end
+ n = n - 1
+ elseif vim9.bool(vim9.index(base, e) == '[') then
+ n = n + 1
+ end
+ e = e + 1
+ end
+ e = e + 1
+ vim9.fn.add(items, vim9.slice(base, s, vim9.ops.Minus(e, 1)))
+ arrays = arrays + 1
+ s = e
+ end
+ end
+
+ -- # Find the variable items[0].
+ -- # 1. in current function (like with "gd")
+ -- # 2. in tags file(s) (like with ":tag")
+ -- # 3. in current file (like with "gD")
+ local res = {}
+ if vim9.fn.searchdecl(vim9.index(items, 0), false, true) == 0 then
+ -- # Found, now figure out the type.
+ -- # TODO: join previous line if it makes sense
+ local line = vim9.fn.getline('.')
+ local col = vim9.fn.charcol('.')
+ if vim9.fn.stridx(vim9.slice(line, nil, vim9.ops.Minus(col, 1)), ';') >= 0 then
+ -- # Handle multiple declarations on the same line.
+ local col2 = vim9.ops.Minus(col, 1)
+ while vim9.index(line, col2) ~= ';' do
+ col2 = col2 - 1
+ end
+ line = vim9.slice(line, vim9.ops.Plus(col2, 1), nil)
+ col = vim9.ops.Minus(col, col2)
+ end
+ if vim9.fn.stridx(vim9.slice(line, nil, vim9.ops.Minus(col, 1)), ',') >= 0 then
+ -- # Handle multiple declarations on the same line in a function
+ -- # declaration.
+ local col2 = vim9.ops.Minus(col, 1)
+ while vim9.index(line, col2) ~= ',' do
+ col2 = col2 - 1
+ end
+ if
+ vim9.ops.RegexpMatches(
+ vim9.slice(line, vim9.ops.Plus(col2, 1), vim9.ops.Minus(col, 1)),
+ ' *[^ ][^ ]* *[^ ]'
+ )
+ then
+ line = vim9.slice(line, vim9.ops.Plus(col2, 1), nil)
+ col = vim9.ops.Minus(col, col2)
+ end
+ end
+ if vim9.fn.len(items) == 1 then
+ -- # Completing one word and it's a local variable: May add '[', '.' or
+ -- # '->'.
+ local match = vim9.index(items, 0)
+ local kind = 'v'
+ if vim9.fn.match(line, '\\<' .. match .. '\\s*\\[') > 0 then
+ match = match .. '['
+ else
+ res = Nextitem(vim9.slice(line, nil, vim9.ops.Minus(col, 1)), { '' }, 0, true)
+ if vim9.fn.len(res) > 0 then
+ -- # There are members, thus add "." or "->".
+ if vim9.fn.match(line, '\\*[ \\t(]*' .. match .. '\\>') > 0 then
+ match = match .. '->'
+ else
+ match = match .. '.'
+ end
+ end
+ end
+ res = { { ['match'] = match, ['tagline'] = '', ['kind'] = kind, ['info'] = line } }
+ elseif vim9.bool(vim9.fn.len(items) == vim9.ops.Plus(arrays, 1)) then
+ -- # Completing one word and it's a local array variable: build tagline
+ -- # from declaration line
+ local match = vim9.index(items, 0)
+ local kind = 'v'
+ local tagline = '\t/^' .. line .. '$/'
+ res = { { ['match'] = match, ['tagline'] = tagline, ['kind'] = kind, ['info'] = line } }
+ else
+ -- # Completing "var.", "var.something", etc.
+ res =
+ Nextitem(vim9.slice(line, nil, vim9.ops.Minus(col, 1)), vim9.slice(items, 1, nil), 0, true)
+ end
+ end
+
+ if vim9.fn.len(items) == 1 or vim9.fn.len(items) == vim9.ops.Plus(arrays, 1) then
+ -- # Only one part, no "." or "->": complete from tags file.
+ local tags = {}
+ if vim9.fn.len(items) == 1 then
+ tags = vim9.fn.taglist('^' .. base)
+ else
+ tags = vim9.fn.taglist('^' .. vim9.index(items, 0) .. '$')
+ end
+
+ vim9.fn_mut('filter', {
+ vim9.fn_mut('filter', {
+ tags,
+ function(_, v)
+ return vim9.ternary(vim9.fn.has_key(v, 'kind'), function()
+ return v.kind ~= 'm'
+ end, true)
+ end,
+ }, { replace = 0 }),
+ function(_, v)
+ return vim9.ops.Or(
+ vim9.ops.Or(
+ vim9.prefix['Bang'](vim9.fn.has_key(v, 'static')),
+ vim9.prefix['Bang'](vim9.index(v, 'static'))
+ ),
+ vim9.fn.bufnr('%') == vim9.fn.bufnr(vim9.index(v, 'filename'))
+ )
+ end,
+ }, { replace = 0 })
+
+ res = vim9.fn.extend(
+ res,
+ vim9.fn.map(tags, function(_, v)
+ return Tag2item(v)
+ end)
+ )
+ end
+
+ if vim9.fn.len(res) == 0 then
+ -- # Find the variable in the tags file(s)
+ local diclist = vim9.fn.filter(
+ vim9.fn.taglist('^' .. vim9.index(items, 0) .. '$'),
+ function(_, v)
+ return vim9.ternary(vim9.fn.has_key(v, 'kind'), function()
+ return v.kind ~= 'm'
+ end, true)
+ end
+ )
+
+ res = {}
+
+ for _, i in vim9.iter(vim9.fn.range(vim9.fn.len(diclist))) do
+ -- # New ctags has the "typeref" field. Patched version has "typename".
+ if vim9.bool(vim9.fn.has_key(vim9.index(diclist, i), 'typename')) then
+ res = vim9.fn.extend(
+ res,
+ StructMembers(
+ vim9.index(vim9.index(diclist, i), 'typename'),
+ vim9.slice(items, 1, nil),
+ true
+ )
+ )
+ elseif vim9.bool(vim9.fn.has_key(vim9.index(diclist, i), 'typeref')) then
+ res = vim9.fn.extend(
+ res,
+ StructMembers(
+ vim9.index(vim9.index(diclist, i), 'typeref'),
+ vim9.slice(items, 1, nil),
+ true
+ )
+ )
+ end
+
+ -- # For a variable use the command, which must be a search pattern that
+ -- # shows the declaration of the variable.
+ if vim9.index(vim9.index(diclist, i), 'kind') == 'v' then
+ local line = vim9.index(vim9.index(diclist, i), 'cmd')
+ if vim9.slice(line, nil, 1) == '/^' then
+ local col =
+ vim9.fn.charidx(line, vim9.fn.match(line, '\\<' .. vim9.index(items, 0) .. '\\>'))
+ res = vim9.fn.extend(
+ res,
+ Nextitem(
+ vim9.slice(line, 2, vim9.ops.Minus(col, 1)),
+ vim9.slice(items, 1, nil),
+ 0,
+ true
+ )
+ )
+ end
+ end
+ end
+ end
+
+ if vim9.fn.len(res) == 0 and vim9.fn.searchdecl(vim9.index(items, 0), true) == 0 then
+ -- # Found, now figure out the type.
+ -- # TODO: join previous line if it makes sense
+ local line = vim9.fn.getline('.')
+ local col = vim9.fn.charcol('.')
+ res =
+ Nextitem(vim9.slice(line, nil, vim9.ops.Minus(col, 1)), vim9.slice(items, 1, nil), 0, true)
+ end
+
+ -- # If the last item(s) are [...] they need to be added to the matches.
+ local last = vim9.fn.len(items) - 1
+ local brackets = ''
+ while last >= 0 do
+ if vim9.index(vim9.index(items, last), 0) ~= '[' then
+ break
+ end
+ brackets = vim9.index(items, last) .. brackets
+ last = last - 1
+ end
+
+ return vim9.fn.map(res, function(_, v)
+ return Tagline2item(v, brackets)
+ end)
+end
+M['Complete'] = Complete
+
+GetAddition = function(line, match, memarg, bracket)
+ bracket = vim9.bool(bracket)
+ -- # Guess if the item is an array.
+ if vim9.bool(vim9.ops.And(bracket, vim9.fn.match(line, match .. '\\s*\\[') > 0)) then
+ return '['
+ end
+
+ -- # Check if the item has members.
+ if vim9.fn.len(SearchMembers(memarg, { '' }, false)) > 0 then
+ -- # If there is a '*' before the name use "->".
+ if vim9.fn.match(line, '\\*[ \\t(]*' .. match .. '\\>') > 0 then
+ return '->'
+ else
+ return '.'
+ end
+ end
+ return ''
+end
+
+Tag2item = function(val)
+ -- # Turn the tag info "val" into an item for completion.
+ -- # "val" is is an item in the list returned by taglist().
+ -- # If it is a variable we may add "." or "->". Don't do it for other types,
+ -- # such as a typedef, by not including the info that GetAddition() uses.
+ local res = vim9.convert.decl_dict({ ['match'] = vim9.index(val, 'name') })
+
+ res[vim9.index_expr('extra')] =
+ Tagcmd2extra(vim9.index(val, 'cmd'), vim9.index(val, 'name'), vim9.index(val, 'filename'))
+
+ local s = Dict2info(val)
+ if s ~= '' then
+ res[vim9.index_expr('info')] = s
+ end
+
+ res[vim9.index_expr('tagline')] = ''
+ if vim9.bool(vim9.fn.has_key(val, 'kind')) then
+ local kind = vim9.index(val, 'kind')
+ res[vim9.index_expr('kind')] = kind
+ if kind == 'v' then
+ res[vim9.index_expr('tagline')] = '\t' .. vim9.index(val, 'cmd')
+ res[vim9.index_expr('dict')] = val
+ elseif vim9.bool(kind == 'f') then
+ res[vim9.index_expr('match')] = vim9.index(val, 'name') .. '('
+ end
+ end
+
+ return res
+end
+
+Dict2info = function(dict)
+ -- # Use all the items in dictionary for the "info" entry.
+ local info = ''
+
+ for _, k in vim9.iter(vim9.fn_mut('sort', { vim9.fn.keys(dict) }, { replace = 0 })) do
+ info = info .. k .. vim9.fn['repeat'](' ', 10 - vim9.fn.strlen(k))
+ if k == 'cmd' then
+ info = info
+ .. vim9.fn.substitute(
+ vim9.fn.matchstr(vim9.index(dict, 'cmd'), '/^\\s*\\zs.*\\ze$/'),
+ '\\\\\\(.\\)',
+ '\\1',
+ 'g'
+ )
+ else
+ local dictk = vim9.index(dict, k)
+ if vim9.fn.typename(dictk) ~= 'string' then
+ info = info .. vim9.fn.string(dictk)
+ else
+ info = info .. dictk
+ end
+ end
+ info = info .. '\n'
+ end
+
+ return info
+end
+
+ParseTagline = function(line)
+ -- # Parse a tag line and return a dictionary with items like taglist()
+ local l = vim9.fn.split(line, '\t')
+ local d = vim.empty_dict()
+ if vim9.fn.len(l) >= 3 then
+ d[vim9.index_expr('name')] = vim9.index(l, 0)
+ d[vim9.index_expr('filename')] = vim9.index(l, 1)
+ d[vim9.index_expr('cmd')] = vim9.index(l, 2)
+ local n = 2
+ if vim9.ops.RegexpMatches(vim9.index(l, 2), '^/') then
+ -- # Find end of cmd, it may contain Tabs.
+ while n < vim9.fn.len(l) and vim9.ops.NotRegexpMatches(vim9.index(l, n), '/;"$') do
+ n = n + 1
+ d[vim9.index_expr('cmd')] = vim9.index(d, 'cmd') .. ' ' .. vim9.index(l, n)
+ end
+ end
+
+ for _, i in vim9.iter(vim9.fn.range(vim9.ops.Plus(n, 1), vim9.fn.len(l) - 1)) do
+ if vim9.index(l, i) == 'file:' then
+ d[vim9.index_expr('static')] = 1
+ elseif vim9.bool(vim9.ops.NotRegexpMatches(vim9.index(l, i), ':')) then
+ d[vim9.index_expr('kind')] = vim9.index(l, i)
+ else
+ d[vim9.index_expr(vim9.fn.matchstr(vim9.index(l, i), '[^:]*'))] =
+ vim9.fn.matchstr(vim9.index(l, i), ':\\zs.*')
+ end
+ end
+ end
+
+ return d
+end
+
+Tagline2item = function(val, brackets)
+ -- # Turn a match item "val" into an item for completion.
+ -- # "val['match']" is the matching item.
+ -- # "val['tagline']" is the tagline in which the last part was found.
+ local line = vim9.index(val, 'tagline')
+ local add = GetAddition(line, vim9.index(val, 'match'), { val }, brackets == '')
+ local res = vim9.convert.decl_dict({ ['word'] = vim9.index(val, 'match') .. brackets .. add })
+
+ if vim9.bool(vim9.fn.has_key(val, 'info')) then
+ -- # Use info from Tag2item().
+ res[vim9.index_expr('info')] = vim9.index(val, 'info')
+ else
+ -- # Parse the tag line and add each part to the "info" entry.
+ local s = Dict2info(ParseTagline(line))
+ if s ~= '' then
+ res[vim9.index_expr('info')] = s
+ end
+ end
+
+ if vim9.bool(vim9.fn.has_key(val, 'kind')) then
+ res[vim9.index_expr('kind')] = vim9.index(val, 'kind')
+ elseif vim9.bool(add == '(') then
+ res[vim9.index_expr('kind')] = 'f'
+ else
+ local s = vim9.fn.matchstr(line, '\\t\\(kind:\\)\\=\\zs\\S\\ze\\(\\t\\|$\\)')
+ if s ~= '' then
+ res[vim9.index_expr('kind')] = s
+ end
+ end
+
+ if vim9.bool(vim9.fn.has_key(val, 'extra')) then
+ res[vim9.index_expr('menu')] = vim9.index(val, 'extra')
+ return res
+ end
+
+ -- # Isolate the command after the tag and filename.
+ local s = vim9.fn.matchstr(
+ line,
+ '[^\\t]*\\t[^\\t]*\\t\\zs\\(/^.*$/\\|[^\\t]*\\)\\ze\\(;"\\t\\|\\t\\|$\\)'
+ )
+ if s ~= '' then
+ res[vim9.index_expr('menu')] = Tagcmd2extra(
+ s,
+ vim9.index(val, 'match'),
+ vim9.fn.matchstr(line, '[^\\t]*\\t\\zs[^\\t]*\\ze\\t')
+ )
+ end
+ return res
+end
+
+Tagcmd2extra = function(cmd, name, fname)
+ -- # Turn a command from a tag line to something that is useful in the menu
+ local x = ''
+ if vim9.ops.RegexpMatches(cmd, '^/^') then
+ -- # The command is a search command, useful to see what it is.
+ x = vim9.fn.substitute(
+ vim9.fn.substitute(
+ vim9.fn.matchstr(cmd, '^/^\\s*\\zs.*\\ze$/'),
+ '\\<' .. name .. '\\>',
+ '@@',
+ ''
+ ),
+ '\\\\\\(.\\)',
+ '\\1',
+ 'g'
+ ) .. ' - ' .. fname
+ elseif vim9.bool(vim9.ops.RegexpMatches(cmd, '^\\d*$')) then
+ -- # The command is a line number, the file name is more useful.
+ x = fname .. ' - ' .. cmd
+ else
+ -- # Not recognized, use command and file name.
+ x = cmd .. ' - ' .. fname
+ end
+ return x
+end
+
+Nextitem = function(lead, items, depth, all)
+ all = vim9.bool(all)
+ -- # Find composing type in "lead" and match items[0] with it.
+ -- # Repeat this recursively for items[1], if it's there.
+ -- # When resolving typedefs "depth" is used to avoid infinite recursion.
+ -- # Return the list of matches.
+
+ -- # Use the text up to the variable name and split it in tokens.
+ local tokens = vim9.fn.split(lead, '\\s\\+\\|\\<')
+
+ -- # Try to recognize the type of the variable. This is rough guessing...
+ local res = {}
+
+ local body = function(_, tidx)
+ -- # Skip tokens starting with a non-ID character.
+ if vim9.ops.NotRegexpMatches(vim9.index(tokens, tidx), '^\\h') then
+ return vim9.ITER_CONTINUE
+ end
+
+ -- # Recognize "struct foobar" and "union foobar".
+ -- # Also do "class foobar" when it's C++ after all (doesn't work very well
+ -- # though).
+ if
+ (
+ vim9.index(tokens, tidx) == 'struct'
+ or vim9.index(tokens, tidx) == 'union'
+ or vim9.index(tokens, tidx) == 'class'
+ ) and vim9.ops.Plus(tidx, 1) < vim9.fn.len(tokens)
+ then
+ res = StructMembers(
+ vim9.index(tokens, tidx) .. ':' .. vim9.index(tokens, vim9.ops.Plus(tidx, 1)),
+ items,
+ all
+ )
+ return vim9.ITER_BREAK
+ end
+
+ -- # TODO: add more reserved words
+ if
+ vim9.fn.index(
+ { 'int', 'short', 'char', 'float', 'double', 'static', 'unsigned', 'extern' },
+ vim9.index(tokens, tidx)
+ ) >= 0
+ then
+ return vim9.ITER_CONTINUE
+ end
+
+ -- # Use the tags file to find out if this is a typedef.
+ local diclist = vim9.fn.taglist('^' .. vim9.index(tokens, tidx) .. '$')
+
+ local body = function(_, tagidx)
+ local item = vim9.convert.decl_dict(vim9.index(diclist, tagidx))
+
+ -- # New ctags has the "typeref" field. Patched version has "typename".
+ if vim9.bool(vim9.fn.has_key(item, 'typeref')) then
+ res = vim9.fn.extend(res, StructMembers(vim9.index(item, 'typeref'), items, all))
+ return vim9.ITER_CONTINUE
+ end
+ if vim9.bool(vim9.fn.has_key(item, 'typename')) then
+ res = vim9.fn.extend(res, StructMembers(vim9.index(item, 'typename'), items, all))
+ return vim9.ITER_CONTINUE
+ end
+
+ -- # Only handle typedefs here.
+ if vim9.index(item, 'kind') ~= 't' then
+ return vim9.ITER_CONTINUE
+ end
+
+ -- # Skip matches local to another file.
+ if
+ vim9.bool(
+ vim9.ops.And(
+ vim9.ops.And(vim9.fn.has_key(item, 'static'), vim9.index(item, 'static')),
+ vim9.fn.bufnr('%') ~= vim9.fn.bufnr(vim9.index(item, 'filename'))
+ )
+ )
+ then
+ return vim9.ITER_CONTINUE
+ end
+
+ -- # For old ctags we recognize "typedef struct aaa" and
+ -- # "typedef union bbb" in the tags file command.
+ local cmd = vim9.index(item, 'cmd')
+ local ei = vim9.fn.charidx(cmd, vim9.fn.matchend(cmd, 'typedef\\s\\+'))
+ if ei > 1 then
+ local cmdtokens = vim9.fn.split(vim9.slice(cmd, ei, nil), '\\s\\+\\|\\<')
+ if vim9.fn.len(cmdtokens) > 1 then
+ if
+ vim9.index(cmdtokens, 0) == 'struct'
+ or vim9.index(cmdtokens, 0) == 'union'
+ or vim9.index(cmdtokens, 0) == 'class'
+ then
+ local name = ''
+ -- # Use the first identifier after the "struct" or "union"
+
+ for _, ti in vim9.iter(vim9.fn.range((vim9.fn.len(cmdtokens) - 1))) do
+ if vim9.ops.RegexpMatches(vim9.index(cmdtokens, ti), '^\\w') then
+ name = vim9.index(cmdtokens, ti)
+ break
+ end
+ end
+
+ if name ~= '' then
+ res = vim9.fn.extend(
+ res,
+ StructMembers(vim9.index(cmdtokens, 0) .. ':' .. name, items, all)
+ )
+ end
+ elseif vim9.bool(depth < 10) then
+ -- # Could be "typedef other_T some_T".
+ res = vim9.fn.extend(
+ res,
+ Nextitem(vim9.index(cmdtokens, 0), items, vim9.ops.Plus(depth, 1), all)
+ )
+ end
+ end
+ end
+
+ return vim9.ITER_DEFAULT
+ end
+
+ for _, tagidx in vim9.iter(vim9.fn.range(vim9.fn.len(diclist))) do
+ local nvim9_status, nvim9_ret = body(_, tagidx)
+ if nvim9_status == vim9.ITER_BREAK then
+ break
+ elseif nvim9_status == vim9.ITER_RETURN then
+ return nvim9_ret
+ end
+ end
+
+ if vim9.fn.len(res) > 0 then
+ return vim9.ITER_BREAK
+ end
+
+ return vim9.ITER_DEFAULT
+ end
+
+ for _, tidx in vim9.iter(vim9.fn.range(vim9.fn.len(tokens))) do
+ local nvim9_status, nvim9_ret = body(_, tidx)
+ if nvim9_status == vim9.ITER_BREAK then
+ break
+ elseif nvim9_status == vim9.ITER_RETURN then
+ return nvim9_ret
+ end
+ end
+
+ return res
+end
+
+StructMembers = function(atypename, items, all)
+ all = vim9.bool(all)
+
+ -- # Search for members of structure "typename" in tags files.
+ -- # Return a list with resulting matches.
+ -- # Each match is a dictionary with "match" and "tagline" entries.
+ -- # When "all" is true find all, otherwise just return 1 if there is any member.
+
+ -- # Todo: What about local structures?
+ local fnames = vim9.fn.join(vim9.fn.map(vim9.fn.tagfiles(), function(_, v)
+ return vim9.fn.escape(v, ' \\#%')
+ end))
+ if fnames == '' then
+ return {}
+ end
+
+ local typename = atypename
+ local qflist = {}
+ local cached = 0
+ local n = ''
+ if vim9.bool(vim9.prefix['Bang'](all)) then
+ n = '1'
+ if vim9.bool(vim9.fn.has_key(grepCache, typename)) then
+ qflist = vim9.index(grepCache, typename)
+ cached = 1
+ end
+ else
+ n = ''
+ end
+ if vim9.bool(vim9.prefix['Bang'](cached)) then
+ while 1 do
+ vim.api.nvim_command(
+ 'silent! keepjumps noautocmd '
+ .. n
+ .. 'vimgrep '
+ .. '/\\t'
+ .. typename
+ .. '\\(\\t\\|$\\)/j '
+ .. fnames
+ )
+
+ qflist = vim9.fn.getqflist()
+ if vim9.fn.len(qflist) > 0 or vim9.fn.match(typename, '::') < 0 then
+ break
+ end
+ -- # No match for "struct:context::name", remove "context::" and try again.
+ typename = vim9.fn.substitute(typename, ':[^:]*::', ':', '')
+ end
+
+ if vim9.bool(vim9.prefix['Bang'](all)) then
+ -- # Store the result to be able to use it again later.
+ grepCache[vim9.index_expr(typename)] = qflist
+ end
+ end
+
+ -- # Skip over [...] items
+ local idx = 0
+ local target = ''
+ while 1 do
+ if idx >= vim9.fn.len(items) then
+ target = ''
+ break
+ end
+ if vim9.index(vim9.index(items, idx), 0) ~= '[' then
+ target = vim9.index(items, idx)
+ break
+ end
+ idx = idx + 1
+ end
+ -- # Put matching members in matches[].
+ local matches = {}
+
+ for _, l in vim9.iter(qflist) do
+ local memb = vim9.fn.matchstr(vim9.index(l, 'text'), '[^\\t]*')
+ if vim9.ops.RegexpMatches(memb, '^' .. target) then
+ -- # Skip matches local to another file.
+ if
+ vim9.fn.match(vim9.index(l, 'text'), '\tfile:') < 0
+ or vim9.fn.bufnr('%')
+ == vim9.fn.bufnr(vim9.fn.matchstr(vim9.index(l, 'text'), '\\t\\zs[^\\t]*'))
+ then
+ local item =
+ vim9.convert.decl_dict({ ['match'] = memb, ['tagline'] = vim9.index(l, 'text') })
+
+ -- # Add the kind of item.
+ local s =
+ vim9.fn.matchstr(vim9.index(l, 'text'), '\\t\\(kind:\\)\\=\\zs\\S\\ze\\(\\t\\|$\\)')
+ if s ~= '' then
+ item[vim9.index_expr('kind')] = s
+ if s == 'f' then
+ item[vim9.index_expr('match')] = memb .. '('
+ end
+ end
+
+ vim9.fn.add(matches, item)
+ end
+ end
+ end
+
+ if vim9.fn.len(matches) > 0 then
+ -- # Skip over next [...] items
+ idx = idx + 1
+ while 1 do
+ if idx >= vim9.fn.len(items) then
+ return matches
+ end
+ if vim9.index(vim9.index(items, idx), 0) ~= '[' then
+ break
+ end
+ idx = idx + 1
+ end
+
+ -- # More items following. For each of the possible members find the
+ -- # matching following members.
+ return SearchMembers(matches, vim9.slice(items, idx, nil), all)
+ end
+
+ -- # Failed to find anything.
+ return {}
+end
+
+SearchMembers = function(matches, items, all)
+ all = vim9.bool(all)
+
+ -- # For matching members, find matches for following items.
+ -- # When "all" is true find all, otherwise just return 1 if there is any member.
+ local res = {}
+
+ for _, i in vim9.iter(vim9.fn.range(vim9.fn.len(matches))) do
+ local typename = ''
+ local line = ''
+ if vim9.bool(vim9.fn.has_key(vim9.index(matches, i), 'dict')) then
+ if vim9.bool(vim9.fn.has_key(vim9.index(vim9.index(matches, i), 'dict'), 'typename')) then
+ typename = vim9.index(vim9.index(vim9.index(matches, i), 'dict'), 'typename')
+ elseif vim9.bool(vim9.fn.has_key(vim9.index(vim9.index(matches, i), 'dict'), 'typeref')) then
+ typename = vim9.index(vim9.index(vim9.index(matches, i), 'dict'), 'typeref')
+ end
+ line = '\t' .. vim9.index(vim9.index(vim9.index(matches, i), 'dict'), 'cmd')
+ else
+ line = vim9.index(vim9.index(matches, i), 'tagline')
+ local eb = vim9.fn.matchend(line, '\\ttypename:')
+ local e = vim9.fn.charidx(line, eb)
+ if e < 0 then
+ eb = vim9.fn.matchend(line, '\\ttyperef:')
+ e = vim9.fn.charidx(line, eb)
+ end
+ if e > 0 then
+ -- # Use typename field
+ typename = vim9.fn.matchstr(line, '[^\\t]*', eb)
+ end
+ end
+
+ if typename ~= '' then
+ res = vim9.fn.extend(res, StructMembers(typename, items, all))
+ else
+ -- # Use the search command (the declaration itself).
+ local sb = vim9.fn.match(line, '\\t\\zs/^')
+ local s = vim9.fn.charidx(line, sb)
+ if s > 0 then
+ local e = vim9.fn.charidx(
+ line,
+ vim9.fn.match(line, '\\<' .. vim9.index(vim9.index(matches, i), 'match') .. '\\>', sb)
+ )
+ if e > 0 then
+ res =
+ vim9.fn.extend(res, Nextitem(vim9.slice(line, s, vim9.ops.Minus(e, 1)), items, 0, all))
+ end
+ end
+ end
+ if vim9.bool(vim9.ops.And(vim9.prefix['Bang'](all), vim9.fn.len(res) > 0)) then
+ break
+ end
+ end
+
+ return res
+end
+
+-- #}}}1
+
+-- # vim: noet sw=2 sts=2
+return M
diff --git a/runtime/autoload/ccomplete.vim b/runtime/autoload/ccomplete.vim
index 95a20e16b0..d7e0ba4ac5 100644
--- a/runtime/autoload/ccomplete.vim
+++ b/runtime/autoload/ccomplete.vim
@@ -1,639 +1,8 @@
-" Vim completion script
-" Language: C
-" Maintainer: Bram Moolenaar <Bram@vim.org>
-" Last Change: 2020 Nov 14
-
-let s:cpo_save = &cpo
-set cpo&vim
-
-" This function is used for the 'omnifunc' option.
-func ccomplete#Complete(findstart, base)
- if a:findstart
- " Locate the start of the item, including ".", "->" and "[...]".
- let line = getline('.')
- let start = col('.') - 1
- let lastword = -1
- while start > 0
- if line[start - 1] =~ '\w'
- let start -= 1
- elseif line[start - 1] =~ '\.'
- if lastword == -1
- let lastword = start
- endif
- let start -= 1
- elseif start > 1 && line[start - 2] == '-' && line[start - 1] == '>'
- if lastword == -1
- let lastword = start
- endif
- let start -= 2
- elseif line[start - 1] == ']'
- " Skip over [...].
- let n = 0
- let start -= 1
- while start > 0
- let start -= 1
- if line[start] == '['
- if n == 0
- break
- endif
- let n -= 1
- elseif line[start] == ']' " nested []
- let n += 1
- endif
- endwhile
- else
- break
- endif
- endwhile
-
- " Return the column of the last word, which is going to be changed.
- " Remember the text that comes before it in s:prepended.
- if lastword == -1
- let s:prepended = ''
- return start
- endif
- let s:prepended = strpart(line, start, lastword - start)
- return lastword
- endif
-
- " Return list of matches.
-
- let base = s:prepended . a:base
-
- " Don't do anything for an empty base, would result in all the tags in the
- " tags file.
- if base == ''
- return []
- endif
-
- " init cache for vimgrep to empty
- let s:grepCache = {}
-
- " Split item in words, keep empty word after "." or "->".
- " "aa" -> ['aa'], "aa." -> ['aa', ''], "aa.bb" -> ['aa', 'bb'], etc.
- " We can't use split, because we need to skip nested [...].
- " "aa[...]" -> ['aa', '[...]'], "aa.bb[...]" -> ['aa', 'bb', '[...]'], etc.
- let items = []
- let s = 0
- let arrays = 0
- while 1
- let e = match(base, '\.\|->\|\[', s)
- if e < 0
- if s == 0 || base[s - 1] != ']'
- call add(items, strpart(base, s))
- endif
- break
- endif
- if s == 0 || base[s - 1] != ']'
- call add(items, strpart(base, s, e - s))
- endif
- if base[e] == '.'
- let s = e + 1 " skip over '.'
- elseif base[e] == '-'
- let s = e + 2 " skip over '->'
- else
- " Skip over [...].
- let n = 0
- let s = e
- let e += 1
- while e < len(base)
- if base[e] == ']'
- if n == 0
- break
- endif
- let n -= 1
- elseif base[e] == '[' " nested [...]
- let n += 1
- endif
- let e += 1
- endwhile
- let e += 1
- call add(items, strpart(base, s, e - s))
- let arrays += 1
- let s = e
- endif
- endwhile
-
- " Find the variable items[0].
- " 1. in current function (like with "gd")
- " 2. in tags file(s) (like with ":tag")
- " 3. in current file (like with "gD")
- let res = []
- if searchdecl(items[0], 0, 1) == 0
- " Found, now figure out the type.
- " TODO: join previous line if it makes sense
- let line = getline('.')
- let col = col('.')
- if stridx(strpart(line, 0, col), ';') != -1
- " Handle multiple declarations on the same line.
- let col2 = col - 1
- while line[col2] != ';'
- let col2 -= 1
- endwhile
- let line = strpart(line, col2 + 1)
- let col -= col2
- endif
- if stridx(strpart(line, 0, col), ',') != -1
- " Handle multiple declarations on the same line in a function
- " declaration.
- let col2 = col - 1
- while line[col2] != ','
- let col2 -= 1
- endwhile
- if strpart(line, col2 + 1, col - col2 - 1) =~ ' *[^ ][^ ]* *[^ ]'
- let line = strpart(line, col2 + 1)
- let col -= col2
- endif
- endif
- if len(items) == 1
- " Completing one word and it's a local variable: May add '[', '.' or
- " '->'.
- let match = items[0]
- let kind = 'v'
- if match(line, '\<' . match . '\s*\[') > 0
- let match .= '['
- else
- let res = s:Nextitem(strpart(line, 0, col), [''], 0, 1)
- if len(res) > 0
- " There are members, thus add "." or "->".
- if match(line, '\*[ \t(]*' . match . '\>') > 0
- let match .= '->'
- else
- let match .= '.'
- endif
- endif
- endif
- let res = [{'match': match, 'tagline' : '', 'kind' : kind, 'info' : line}]
- elseif len(items) == arrays + 1
- " Completing one word and it's a local array variable: build tagline
- " from declaration line
- let match = items[0]
- let kind = 'v'
- let tagline = "\t/^" . line . '$/'
- let res = [{'match': match, 'tagline' : tagline, 'kind' : kind, 'info' : line}]
- else
- " Completing "var.", "var.something", etc.
- let res = s:Nextitem(strpart(line, 0, col), items[1:], 0, 1)
- endif
- endif
-
- if len(items) == 1 || len(items) == arrays + 1
- " Only one part, no "." or "->": complete from tags file.
- if len(items) == 1
- let tags = taglist('^' . base)
- else
- let tags = taglist('^' . items[0] . '$')
- endif
-
- " Remove members, these can't appear without something in front.
- call filter(tags, 'has_key(v:val, "kind") ? v:val["kind"] != "m" : 1')
-
- " Remove static matches in other files.
- call filter(tags, '!has_key(v:val, "static") || !v:val["static"] || bufnr("%") == bufnr(v:val["filename"])')
-
- call extend(res, map(tags, 's:Tag2item(v:val)'))
- endif
-
- if len(res) == 0
- " Find the variable in the tags file(s)
- let diclist = taglist('^' . items[0] . '$')
-
- " Remove members, these can't appear without something in front.
- call filter(diclist, 'has_key(v:val, "kind") ? v:val["kind"] != "m" : 1')
-
- let res = []
- for i in range(len(diclist))
- " New ctags has the "typeref" field. Patched version has "typename".
- if has_key(diclist[i], 'typename')
- call extend(res, s:StructMembers(diclist[i]['typename'], items[1:], 1))
- elseif has_key(diclist[i], 'typeref')
- call extend(res, s:StructMembers(diclist[i]['typeref'], items[1:], 1))
- endif
-
- " For a variable use the command, which must be a search pattern that
- " shows the declaration of the variable.
- if diclist[i]['kind'] == 'v'
- let line = diclist[i]['cmd']
- if line[0] == '/' && line[1] == '^'
- let col = match(line, '\<' . items[0] . '\>')
- call extend(res, s:Nextitem(strpart(line, 2, col - 2), items[1:], 0, 1))
- endif
- endif
- endfor
- endif
-
- if len(res) == 0 && searchdecl(items[0], 1) == 0
- " Found, now figure out the type.
- " TODO: join previous line if it makes sense
- let line = getline('.')
- let col = col('.')
- let res = s:Nextitem(strpart(line, 0, col), items[1:], 0, 1)
- endif
-
- " If the last item(s) are [...] they need to be added to the matches.
- let last = len(items) - 1
- let brackets = ''
- while last >= 0
- if items[last][0] != '['
- break
- endif
- let brackets = items[last] . brackets
- let last -= 1
- endwhile
-
- return map(res, 's:Tagline2item(v:val, brackets)')
-endfunc
-
-func s:GetAddition(line, match, memarg, bracket)
- " Guess if the item is an array.
- if a:bracket && match(a:line, a:match . '\s*\[') > 0
- return '['
- endif
-
- " Check if the item has members.
- if len(s:SearchMembers(a:memarg, [''], 0)) > 0
- " If there is a '*' before the name use "->".
- if match(a:line, '\*[ \t(]*' . a:match . '\>') > 0
- return '->'
- else
- return '.'
- endif
- endif
- return ''
-endfunc
-
-" Turn the tag info "val" into an item for completion.
-" "val" is is an item in the list returned by taglist().
-" If it is a variable we may add "." or "->". Don't do it for other types,
-" such as a typedef, by not including the info that s:GetAddition() uses.
-func s:Tag2item(val)
- let res = {'match': a:val['name']}
-
- let res['extra'] = s:Tagcmd2extra(a:val['cmd'], a:val['name'], a:val['filename'])
-
- let s = s:Dict2info(a:val)
- if s != ''
- let res['info'] = s
- endif
-
- let res['tagline'] = ''
- if has_key(a:val, "kind")
- let kind = a:val['kind']
- let res['kind'] = kind
- if kind == 'v'
- let res['tagline'] = "\t" . a:val['cmd']
- let res['dict'] = a:val
- elseif kind == 'f'
- let res['match'] = a:val['name'] . '('
- endif
- endif
-
- return res
-endfunc
-
-" Use all the items in dictionary for the "info" entry.
-func s:Dict2info(dict)
- let info = ''
- for k in sort(keys(a:dict))
- let info .= k . repeat(' ', 10 - len(k))
- if k == 'cmd'
- let info .= substitute(matchstr(a:dict['cmd'], '/^\s*\zs.*\ze$/'), '\\\(.\)', '\1', 'g')
- else
- let info .= a:dict[k]
- endif
- let info .= "\n"
- endfor
- return info
-endfunc
-
-" Parse a tag line and return a dictionary with items like taglist()
-func s:ParseTagline(line)
- let l = split(a:line, "\t")
- let d = {}
- if len(l) >= 3
- let d['name'] = l[0]
- let d['filename'] = l[1]
- let d['cmd'] = l[2]
- let n = 2
- if l[2] =~ '^/'
- " Find end of cmd, it may contain Tabs.
- while n < len(l) && l[n] !~ '/;"$'
- let n += 1
- let d['cmd'] .= " " . l[n]
- endwhile
- endif
- for i in range(n + 1, len(l) - 1)
- if l[i] == 'file:'
- let d['static'] = 1
- elseif l[i] !~ ':'
- let d['kind'] = l[i]
- else
- let d[matchstr(l[i], '[^:]*')] = matchstr(l[i], ':\zs.*')
- endif
- endfor
- endif
-
- return d
-endfunc
-
-" Turn a match item "val" into an item for completion.
-" "val['match']" is the matching item.
-" "val['tagline']" is the tagline in which the last part was found.
-func s:Tagline2item(val, brackets)
- let line = a:val['tagline']
- let add = s:GetAddition(line, a:val['match'], [a:val], a:brackets == '')
- let res = {'word': a:val['match'] . a:brackets . add }
-
- if has_key(a:val, 'info')
- " Use info from Tag2item().
- let res['info'] = a:val['info']
- else
- " Parse the tag line and add each part to the "info" entry.
- let s = s:Dict2info(s:ParseTagline(line))
- if s != ''
- let res['info'] = s
- endif
- endif
-
- if has_key(a:val, 'kind')
- let res['kind'] = a:val['kind']
- elseif add == '('
- let res['kind'] = 'f'
- else
- let s = matchstr(line, '\t\(kind:\)\=\zs\S\ze\(\t\|$\)')
- if s != ''
- let res['kind'] = s
- endif
- endif
-
- if has_key(a:val, 'extra')
- let res['menu'] = a:val['extra']
- return res
- endif
-
- " Isolate the command after the tag and filename.
- let s = matchstr(line, '[^\t]*\t[^\t]*\t\zs\(/^.*$/\|[^\t]*\)\ze\(;"\t\|\t\|$\)')
- if s != ''
- let res['menu'] = s:Tagcmd2extra(s, a:val['match'], matchstr(line, '[^\t]*\t\zs[^\t]*\ze\t'))
- endif
- return res
-endfunc
-
-" Turn a command from a tag line to something that is useful in the menu
-func s:Tagcmd2extra(cmd, name, fname)
- if a:cmd =~ '^/^'
- " The command is a search command, useful to see what it is.
- let x = matchstr(a:cmd, '^/^\s*\zs.*\ze$/')
- let x = substitute(x, '\<' . a:name . '\>', '@@', '')
- let x = substitute(x, '\\\(.\)', '\1', 'g')
- let x = x . ' - ' . a:fname
- elseif a:cmd =~ '^\d*$'
- " The command is a line number, the file name is more useful.
- let x = a:fname . ' - ' . a:cmd
- else
- " Not recognized, use command and file name.
- let x = a:cmd . ' - ' . a:fname
- endif
- return x
-endfunc
-
-" Find composing type in "lead" and match items[0] with it.
-" Repeat this recursively for items[1], if it's there.
-" When resolving typedefs "depth" is used to avoid infinite recursion.
-" Return the list of matches.
-func s:Nextitem(lead, items, depth, all)
-
- " Use the text up to the variable name and split it in tokens.
- let tokens = split(a:lead, '\s\+\|\<')
-
- " Try to recognize the type of the variable. This is rough guessing...
- let res = []
- for tidx in range(len(tokens))
-
- " Skip tokens starting with a non-ID character.
- if tokens[tidx] !~ '^\h'
- continue
- endif
-
- " Recognize "struct foobar" and "union foobar".
- " Also do "class foobar" when it's C++ after all (doesn't work very well
- " though).
- if (tokens[tidx] == 'struct' || tokens[tidx] == 'union' || tokens[tidx] == 'class') && tidx + 1 < len(tokens)
- let res = s:StructMembers(tokens[tidx] . ':' . tokens[tidx + 1], a:items, a:all)
- break
- endif
-
- " TODO: add more reserved words
- if index(['int', 'short', 'char', 'float', 'double', 'static', 'unsigned', 'extern'], tokens[tidx]) >= 0
- continue
- endif
-
- " Use the tags file to find out if this is a typedef.
- let diclist = taglist('^' . tokens[tidx] . '$')
- for tagidx in range(len(diclist))
- let item = diclist[tagidx]
-
- " New ctags has the "typeref" field. Patched version has "typename".
- if has_key(item, 'typeref')
- call extend(res, s:StructMembers(item['typeref'], a:items, a:all))
- continue
- endif
- if has_key(item, 'typename')
- call extend(res, s:StructMembers(item['typename'], a:items, a:all))
- continue
- endif
-
- " Only handle typedefs here.
- if item['kind'] != 't'
- continue
- endif
-
- " Skip matches local to another file.
- if has_key(item, 'static') && item['static'] && bufnr('%') != bufnr(item['filename'])
- continue
- endif
-
- " For old ctags we recognize "typedef struct aaa" and
- " "typedef union bbb" in the tags file command.
- let cmd = item['cmd']
- let ei = matchend(cmd, 'typedef\s\+')
- if ei > 1
- let cmdtokens = split(strpart(cmd, ei), '\s\+\|\<')
- if len(cmdtokens) > 1
- if cmdtokens[0] == 'struct' || cmdtokens[0] == 'union' || cmdtokens[0] == 'class'
- let name = ''
- " Use the first identifier after the "struct" or "union"
- for ti in range(len(cmdtokens) - 1)
- if cmdtokens[ti] =~ '^\w'
- let name = cmdtokens[ti]
- break
- endif
- endfor
- if name != ''
- call extend(res, s:StructMembers(cmdtokens[0] . ':' . name, a:items, a:all))
- endif
- elseif a:depth < 10
- " Could be "typedef other_T some_T".
- call extend(res, s:Nextitem(cmdtokens[0], a:items, a:depth + 1, a:all))
- endif
- endif
- endif
- endfor
- if len(res) > 0
- break
- endif
- endfor
-
- return res
-endfunc
-
-
-" Search for members of structure "typename" in tags files.
-" Return a list with resulting matches.
-" Each match is a dictionary with "match" and "tagline" entries.
-" When "all" is non-zero find all, otherwise just return 1 if there is any
-" member.
-func s:StructMembers(typename, items, all)
- " Todo: What about local structures?
- let fnames = join(map(tagfiles(), 'escape(v:val, " \\#%")'))
- if fnames == ''
- return []
- endif
-
- let typename = a:typename
- let qflist = []
- let cached = 0
- if a:all == 0
- let n = '1' " stop at first found match
- if has_key(s:grepCache, a:typename)
- let qflist = s:grepCache[a:typename]
- let cached = 1
- endif
- else
- let n = ''
- endif
- if !cached
- while 1
- exe 'silent! keepj noautocmd ' . n . 'vimgrep /\t' . typename . '\(\t\|$\)/j ' . fnames
-
- let qflist = getqflist()
- if len(qflist) > 0 || match(typename, "::") < 0
- break
- endif
- " No match for "struct:context::name", remove "context::" and try again.
- let typename = substitute(typename, ':[^:]*::', ':', '')
- endwhile
-
- if a:all == 0
- " Store the result to be able to use it again later.
- let s:grepCache[a:typename] = qflist
- endif
- endif
-
- " Skip over [...] items
- let idx = 0
- while 1
- if idx >= len(a:items)
- let target = '' " No further items, matching all members
- break
- endif
- if a:items[idx][0] != '['
- let target = a:items[idx]
- break
- endif
- let idx += 1
- endwhile
- " Put matching members in matches[].
- let matches = []
- for l in qflist
- let memb = matchstr(l['text'], '[^\t]*')
- if memb =~ '^' . target
- " Skip matches local to another file.
- if match(l['text'], "\tfile:") < 0 || bufnr('%') == bufnr(matchstr(l['text'], '\t\zs[^\t]*'))
- let item = {'match': memb, 'tagline': l['text']}
-
- " Add the kind of item.
- let s = matchstr(l['text'], '\t\(kind:\)\=\zs\S\ze\(\t\|$\)')
- if s != ''
- let item['kind'] = s
- if s == 'f'
- let item['match'] = memb . '('
- endif
- endif
-
- call add(matches, item)
- endif
- endif
- endfor
-
- if len(matches) > 0
- " Skip over next [...] items
- let idx += 1
- while 1
- if idx >= len(a:items)
- return matches " No further items, return the result.
- endif
- if a:items[idx][0] != '['
- break
- endif
- let idx += 1
- endwhile
-
- " More items following. For each of the possible members find the
- " matching following members.
- return s:SearchMembers(matches, a:items[idx :], a:all)
- endif
-
- " Failed to find anything.
- return []
-endfunc
-
-" For matching members, find matches for following items.
-" When "all" is non-zero find all, otherwise just return 1 if there is any
-" member.
-func s:SearchMembers(matches, items, all)
- let res = []
- for i in range(len(a:matches))
- let typename = ''
- if has_key(a:matches[i], 'dict')
- if has_key(a:matches[i].dict, 'typename')
- let typename = a:matches[i].dict['typename']
- elseif has_key(a:matches[i].dict, 'typeref')
- let typename = a:matches[i].dict['typeref']
- endif
- let line = "\t" . a:matches[i].dict['cmd']
- else
- let line = a:matches[i]['tagline']
- let e = matchend(line, '\ttypename:')
- if e < 0
- let e = matchend(line, '\ttyperef:')
- endif
- if e > 0
- " Use typename field
- let typename = matchstr(line, '[^\t]*', e)
- endif
- endif
-
- if typename != ''
- call extend(res, s:StructMembers(typename, a:items, a:all))
- else
- " Use the search command (the declaration itself).
- let s = match(line, '\t\zs/^')
- if s > 0
- let e = match(line, '\<' . a:matches[i]['match'] . '\>', s)
- if e > 0
- call extend(res, s:Nextitem(strpart(line, s, e - s), a:items, 0, a:all))
- endif
- endif
- endif
- if a:all == 0 && len(res) > 0
- break
- endif
- endfor
- return res
-endfunc
-
-let &cpo = s:cpo_save
-unlet s:cpo_save
-
-" vim: noet sw=2 sts=2
+" Generated vim file by vim9jit. Please do not edit
+let s:path = expand("<script>")
+let s:lua_path = fnamemodify(s:path, ":r") . ".lua"
+let s:nvim_module = luaeval('require("_vim9script").autoload(_A)', s:lua_path)
+
+function! ccomplete#Complete(findstart, abase) abort
+ return s:nvim_module.Complete(a:findstart, a:abase)
+endfunction
diff --git a/runtime/autoload/dist/ft.vim b/runtime/autoload/dist/ft.vim
deleted file mode 100644
index 77140d62b1..0000000000
--- a/runtime/autoload/dist/ft.vim
+++ /dev/null
@@ -1,1073 +0,0 @@
-" Vim functions for file type detection
-"
-" Maintainer: Bram Moolenaar <Bram@vim.org>
-" Last Change: 2022 Apr 13
-
-" These functions are moved here from runtime/filetype.vim to make startup
-" faster.
-
-" Line continuation is used here, remove 'C' from 'cpoptions'
-let s:cpo_save = &cpo
-set cpo&vim
-
-func dist#ft#Check_inp()
- if getline(1) =~ '^\*'
- setf abaqus
- else
- let n = 1
- if line("$") > 500
- let nmax = 500
- else
- let nmax = line("$")
- endif
- while n <= nmax
- if getline(n) =~? "^header surface data"
- setf trasys
- break
- endif
- let n = n + 1
- endwhile
- endif
-endfunc
-
-" 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.
-func dist#ft#FTasm()
- " make sure b:asmsyntax exists
- if !exists("b:asmsyntax")
- let b:asmsyntax = ""
- endif
-
- if b:asmsyntax == ""
- call dist#ft#FTasmsyntax()
- endif
-
- " if b:asmsyntax still isn't set, default to asmsyntax or GNU
- if b:asmsyntax == ""
- if exists("g:asmsyntax")
- let b:asmsyntax = g:asmsyntax
- else
- let b:asmsyntax = "asm"
- endif
- endif
-
- exe "setf " . fnameescape(b:asmsyntax)
-endfunc
-
-func dist#ft#FTasmsyntax()
- " see if file contains any asmsyntax=foo overrides. If so, change
- " b:asmsyntax appropriately
- let head = " ".getline(1)." ".getline(2)." ".getline(3)." ".getline(4).
- \" ".getline(5)." "
- let match = matchstr(head, '\sasmsyntax=\zs[a-zA-Z0-9]\+\ze\s')
- if match != ''
- let b:asmsyntax = match
- elseif ((head =~? '\.title') || (head =~? '\.ident') || (head =~? '\.macro') || (head =~? '\.subtitle') || (head =~? '\.library'))
- let b:asmsyntax = "vmasm"
- endif
-endfunc
-
-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()
- if exists("g:dosbatch_syntax_for_btm") && g:dosbatch_syntax_for_btm
- setf dosbatch
- else
- setf btm
- endif
-endfunc
-
-func dist#ft#BindzoneCheck(default)
- if getline(1).getline(2).getline(3).getline(4) =~ '^; <<>> DiG [0-9.]\+.* <<>>\|$ORIGIN\|$TTL\|IN\s\+SOA'
- setf bindzone
- elseif a:default != ''
- exe 'setf ' . a: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
- while lnum <= 12
- if getline(lnum) =~# '^\(//\|inherit\|private\|protected\|nosave\|string\|object\|mapping\|mixed\)'
- setf lpc
- return
- endif
- let lnum = lnum + 1
- endwhile
- endif
- setf c
-endfunc
-
-func dist#ft#FTheader()
- if match(getline(1, min([line("$"), 200])), '^@\(interface\|end\|class\)') > -1
- if exists("g:c_syntax_for_h")
- setf objc
- else
- setf objcpp
- endif
- elseif exists("g:c_syntax_for_h")
- setf c
- elseif exists("g:ch_syntax_for_h")
- setf ch
- else
- setf cpp
- endif
-endfunc
-
-" 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.
-func dist#ft#FTchange()
- let lnum = 1
- while lnum <= 10
- if getline(lnum)[0] == '@'
- setf change
- return
- endif
- if lnum == 1 && (getline(1)[0] == '#' || getline(1)[0] == '!')
- setf ch
- return
- endif
- if getline(lnum) =~ "MODULE"
- setf chill
- return
- endif
- if getline(lnum) =~ 'main\s*(\|#\s*include\|//'
- setf ch
- return
- endif
- let lnum = lnum + 1
- endwhile
- setf chill
-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, '{'.
- " If not found, assume SGML.
- let lnum = 1
- while lnum < 6
- let line = getline(lnum)
- if line =~ '^\s*[#{]'
- setf cl
- return
- elseif line !~ '^\s*$'
- " Not a blank line, not a comment, and not a block start,
- " so doesn't look like valid cl code.
- break
- endif
- let lnum = lnum + 1
- endw
- setf dtd
-endfunc
-
-func dist#ft#ExCheck()
- let lines = getline(1, min([line("$"), 100]))
- if exists('g:filetype_euphoria')
- exe 'setf ' . g:filetype_euphoria
- elseif match(lines, '^--\|^ifdef\>\|^include\>') > -1
- setf euphoria3
- else
- setf elixir
- endif
-endfunc
-
-func dist#ft#EuphoriaCheck()
- if exists('g:filetype_euphoria')
- exe 'setf ' . g:filetype_euphoria
- else
- setf euphoria3
- endif
-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.
- setf d
- elseif match(lines, '^#!\S\+dtrace\|#pragma\s\+D\s\+option\|:\S\{-}:\S\{-}:') > -1
- setf dtrace
- else
- setf d
- endif
-endfunc
-
-func dist#ft#FTe()
- if exists('g:filetype_euphoria')
- exe 'setf ' . g:filetype_euphoria
- else
- let n = 1
- while n < 100 && n <= line("$")
- if getline(n) =~ "^\\s*\\(<'\\|'>\\)\\s*$"
- setf specman
- return
- endif
- let n = n + 1
- endwhile
- setf eiffel
- 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()
- if exists("g:filetype_fs")
- exe "setf " . g:filetype_fs
- else
- let line = getline(nextnonblank(1))
- " comments and colon definitions
- if line =~ '^\s*\.\=( ' || line =~ '^\s*\\G\= ' || line =~ '^\\$'
- \ || line =~ '^\s*: \S'
- setf forth
- else
- setf fsharp
- endif
- endif
-endfunc
-
-" Distinguish between HTML, XHTML and Django
-func dist#ft#FThtml()
- let n = 1
- while n < 10 && n <= line("$")
- if getline(n) =~ '\<DTD\s\+XHTML\s'
- setf xhtml
- return
- endif
- if getline(n) =~ '{%\s*\(extends\|block\|load\)\>\|{#\s\+'
- setf htmldjango
- return
- endif
- let n = n + 1
- endwhile
- setf FALLBACK html
-endfunc
-
-" Distinguish between standard IDL and MS-IDL
-func dist#ft#FTidl()
- let n = 1
- while n < 50 && n <= line("$")
- if getline(n) =~ '^\s*import\s\+"\(unknwn\|objidl\)\.idl"'
- setf msidl
- return
- endif
- let n = n + 1
- endwhile
- setf idl
-endfunc
-
-" Distinguish between "default", Prolog and Cproto prototype file. */
-func dist#ft#ProtoCheck(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
- " chacter before the ';'.
- if getline(2) =~ '.;$'
- setf cpp
- else
- " recognize Prolog by specific text in the first non-empty line
- " require a blank after the '%' because Perl uses "%list" and "%translate"
- let l = getline(nextnonblank(1))
- if l =~ '\<prolog\>' || l =~ '^\s*\(%\+\(\s\|$\)\|/\*\)' || l =~ ':-'
- setf prolog
- else
- exe 'setf ' .. a:default
- endif
- endif
-endfunc
-
-func dist#ft#FTm()
- if exists("g:filetype_m")
- exe "setf " . g:filetype_m
- return
- endif
-
- " excluding end(for|function|if|switch|while) common to Murphi
- let octave_block_terminators = '\<end\%(_try_catch\|classdef\|enumeration\|events\|methods\|parfor\|properties\)\>'
-
- let objc_preprocessor = '^\s*#\s*\%(import\|include\|define\|if\|ifn\=def\|undef\|line\|error\|pragma\)\>'
-
- let n = 1
- let saw_comment = 0 " Whether we've seen a multiline comment leader.
- while n < 100
- let line = getline(n)
- if line =~ '^\s*/\*'
- " /* ... */ 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.
- let saw_comment = 1
- endif
- if line =~ '^\s*//' || line =~ '^\s*@import\>' || line =~ objc_preprocessor
- setf objc
- return
- endif
- if line =~ '^\s*\%(#\|%!\)' || line =~ '^\s*unwind_protect\>' ||
- \ line =~ '\%(^\|;\)\s*' .. octave_block_terminators
- setf octave
- return
- endif
- " TODO: could be Matlab or Octave
- if line =~ '^\s*%'
- setf matlab
- return
- endif
- if line =~ '^\s*(\*'
- setf mma
- return
- endif
- if line =~ '^\c\s*\(\(type\|var\)\>\|--\)'
- setf murphi
- return
- endif
- let n = n + 1
- endwhile
-
- if saw_comment
- " 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.
- setf objc
- else
- " Default is Matlab
- setf matlab
- endif
-endfunc
-
-func dist#ft#FTmms()
- let n = 1
- while n < 20
- let line = getline(n)
- if line =~ '^\s*\(%\|//\)' || line =~ '^\*'
- setf mmix
- return
- endif
- if line =~ '^\s*#'
- setf make
- return
- endif
- let n = n + 1
- endwhile
- setf mmix
-endfunc
-
-" This function checks if one of the first five lines start with a dot. In
-" that case it is probably an nroff file: 'filetype' is set and 1 is returned.
-func dist#ft#FTnroff()
- if getline(1)[0] . getline(2)[0] . getline(3)[0] . getline(4)[0] . getline(5)[0] =~ '\.'
- setf nroff
- return 1
- endif
- return 0
-endfunc
-
-func dist#ft#FTmm()
- let n = 1
- while n < 20
- let line = getline(n)
- if line =~ '^\s*\(#\s*\(include\|import\)\>\|@import\>\|/\*\)'
- setf objcpp
- return
- endif
- let n = n + 1
- endwhile
- 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
- else
- " recognize Prolog by specific text in the first non-empty line
- " require a blank after the '%' because Perl uses "%list" and "%translate"
- let l = getline(nextnonblank(1))
- if l =~ '\<prolog\>' || l =~ '^\s*\(%\+\(\s\|$\)\|/\*\)' || l =~ ':-'
- setf prolog
- else
- setf perl
- endif
- endif
-endfunc
-
-func dist#ft#FTinc()
- if exists("g:filetype_inc")
- exe "setf " . g:filetype_inc
- else
- let lines = getline(1).getline(2).getline(3)
- if lines =~? "perlscript"
- setf aspperl
- elseif lines =~ "<%"
- setf aspvbs
- elseif lines =~ "<?"
- setf php
- " Pascal supports // comments but they're vary rarely used for file
- " headers so assume POV-Ray
- elseif lines =~ '^\s*\%({\|(\*\)' || lines =~? s:ft_pascal_keywords
- setf pascal
- elseif lines =~# '\<\%(require\|inherit\)\>' || lines =~# '[A-Z][A-Za-z0-9_:${}]*\s\+\%(??\|[?:+]\)\?= '
- setf bitbake
- else
- call dist#ft#FTasmsyntax()
- if exists("b:asmsyntax")
- exe "setf " . fnameescape(b:asmsyntax)
- else
- setf pov
- endif
- endif
- endif
-endfunc
-
-func dist#ft#FTprogress_cweb()
- if exists("g:filetype_w")
- exe "setf " . g:filetype_w
- return
- endif
- if getline(1) =~ '&ANALYZE' || getline(3) =~ '&GLOBAL-DEFINE'
- setf progress
- else
- setf cweb
- endif
-endfunc
-
-func dist#ft#FTprogress_asm()
- if exists("g:filetype_i")
- exe "setf " . g:filetype_i
- return
- endif
- " This function checks for an assembly comment the first ten lines.
- " If not found, assume Progress.
- let lnum = 1
- while lnum <= 10 && lnum < line('$')
- let line = getline(lnum)
- if line =~ '^\s*;' || line =~ '^\*'
- call dist#ft#FTasm()
- return
- elseif line !~ '^\s*$' || line =~ '^/\*'
- " Not an empty line: Doesn't look like valid assembly code.
- " Or it looks like a Progress /* comment
- break
- endif
- let lnum = lnum + 1
- endw
- setf progress
-endfunc
-
-let s:ft_pascal_comments = '^\s*\%({\|(\*\|//\)'
-let s:ft_pascal_keywords = '^\s*\%(program\|unit\|library\|uses\|begin\|procedure\|function\|const\|type\|var\)\>'
-
-func dist#ft#FTprogress_pascal()
- if exists("g:filetype_p")
- exe "setf " . g:filetype_p
- return
- endif
- " This function checks for valid Pascal syntax in the first ten lines.
- " Look for either an opening comment or a program start.
- " If not found, assume Progress.
- let lnum = 1
- while lnum <= 10 && lnum < line('$')
- let line = getline(lnum)
- if line =~ s:ft_pascal_comments || line =~? s:ft_pascal_keywords
- setf pascal
- return
- elseif line !~ '^\s*$' || line =~ '^/\*'
- " Not an empty line: Doesn't look like valid Pascal code.
- " Or it looks like a Progress /* comment
- break
- endif
- let lnum = lnum + 1
- endw
- setf progress
-endfunc
-
-func dist#ft#FTpp()
- if exists("g:filetype_pp")
- exe "setf " . g:filetype_pp
- else
- let line = getline(nextnonblank(1))
- if line =~ s:ft_pascal_comments || line =~? s:ft_pascal_keywords
- setf pascal
- else
- setf puppet
- endif
- 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("$")
-
- for n in range(1, max)
- " Rebol is easy to recognize, check for that first
- if getline(n) =~? '\<REBOL\>'
- setf rebol
- return
- endif
- endfor
-
- for n in range(1, max)
- " R has # comments
- if getline(n) =~ '^\s*#'
- setf r
- return
- endif
- " Rexx has /* comments */
- if getline(n) =~ '^\s*/\*'
- setf rexx
- return
- endif
- endfor
-
- " Nothing recognized, use user default or assume Rexx
- if exists("g:filetype_r")
- exe "setf " . g:filetype_r
- else
- " Rexx used to be the default, but R appears to be much more popular.
- setf r
- endif
-endfunc
-
-func dist#ft#McSetf()
- " Rely on the file to start with a comment.
- " MS message text files use ';', Sendmail files use '#' or 'dnl'
- for lnum in range(1, min([line("$"), 20]))
- let line = getline(lnum)
- if line =~ '^\s*\(#\|dnl\)'
- setf m4 " Sendmail .mc file
- return
- elseif line =~ '^\s*;'
- setf msmessages " MS Message text file
- return
- endif
- endfor
- setf m4 " Default: Sendmail .mc file
-endfunc
-
-" Called from filetype.vim and scripts.vim.
-func dist#ft#SetFileTypeSH(name)
- if did_filetype()
- " Filetype was already detected
- return
- endif
- if expand("<amatch>") =~ g:ft_ignore_pat
- return
- endif
- if a:name =~ '\<csh\>'
- " Some .sh scripts contain #!/bin/csh.
- call dist#ft#SetFileTypeShell("csh")
- return
- elseif a:name =~ '\<tcsh\>'
- " Some .sh scripts contain #!/bin/tcsh.
- call dist#ft#SetFileTypeShell("tcsh")
- return
- elseif a:name =~ '\<zsh\>'
- " Some .sh scripts contain #!/bin/zsh.
- call dist#ft#SetFileTypeShell("zsh")
- return
- elseif a:name =~ '\<ksh\>'
- let b:is_kornshell = 1
- if exists("b:is_bash")
- unlet b:is_bash
- endif
- if exists("b:is_sh")
- unlet b:is_sh
- endif
- elseif exists("g:bash_is_sh") || a:name =~ '\<bash\>' || a:name =~ '\<bash2\>'
- let b:is_bash = 1
- if exists("b:is_kornshell")
- unlet b:is_kornshell
- endif
- if exists("b:is_sh")
- unlet b:is_sh
- endif
- elseif a:name =~ '\<sh\>'
- let b:is_sh = 1
- if exists("b:is_kornshell")
- unlet b:is_kornshell
- endif
- if exists("b:is_bash")
- unlet b:is_bash
- endif
- endif
- call dist#ft#SetFileTypeShell("sh")
-endfunc
-
-" For shell-like file types, check for an "exec" command hidden in a comment,
-" as used for Tcl.
-" Also called from scripts.vim, thus can't be local to this script.
-func dist#ft#SetFileTypeShell(name)
- if did_filetype()
- " Filetype was already detected
- return
- endif
- if expand("<amatch>") =~ g:ft_ignore_pat
- return
- endif
- let l = 2
- while l < 20 && l < line("$") && getline(l) =~ '^\s*\(#\|$\)'
- " Skip empty and comment lines.
- let l = l + 1
- endwhile
- if l < line("$") && getline(l) =~ '\s*exec\s' && getline(l - 1) =~ '^\s*#.*\\$'
- " Found an "exec" line after a comment with continuation
- let n = substitute(getline(l),'\s*exec\s\+\([^ ]*/\)\=', '', '')
- if n =~ '\<tclsh\|\<wish'
- setf tcl
- return
- endif
- endif
- exe "setf " . a:name
-endfunc
-
-func dist#ft#CSH()
- if did_filetype()
- " Filetype was already detected
- return
- endif
- if exists("g:filetype_csh")
- call dist#ft#SetFileTypeShell(g:filetype_csh)
- elseif &shell =~ "tcsh"
- call dist#ft#SetFileTypeShell("tcsh")
- else
- call dist#ft#SetFileTypeShell("csh")
- endif
-endfunc
-
-let s:ft_rules_udev_rules_pattern = '^\s*\cudev_rules\s*=\s*"\([^"]\{-1,}\)/*".*'
-func dist#ft#FTRules()
- let path = expand('<amatch>:p')
- if path =~ '/\(etc/udev/\%(rules\.d/\)\=.*\.rules\|\%(usr/\)\=lib/udev/\%(rules\.d/\)\=.*\.rules\)$'
- setf udevrules
- return
- endif
- if path =~ '^/etc/ufw/'
- setf conf " Better than hog
- return
- endif
- if path =~ '^/\(etc\|usr/share\)/polkit-1/rules\.d'
- setf javascript
- return
- endif
- try
- let config_lines = readfile('/etc/udev/udev.conf')
- catch /^Vim\%((\a\+)\)\=:E484/
- setf hog
- return
- endtry
- let dir = expand('<amatch>:p:h')
- for line in config_lines
- if line =~ s:ft_rules_udev_rules_pattern
- let udev_rules = substitute(line, s:ft_rules_udev_rules_pattern, '\1', "")
- if dir == udev_rules
- setf udevrules
- endif
- break
- endif
- endfor
- setf hog
-endfunc
-
-func dist#ft#SQL()
- if exists("g:filetype_sql")
- exe "setf " . g:filetype_sql
- else
- setf 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
-" file.
-" (Slow test) If a file contains a 'use' statement then it is almost certainly
-" a Perl file.
-func dist#ft#FTperl()
- let dirname = expand("%:p:h:t")
- if expand("%:e") == 't' && (dirname == 't' || dirname == 'xt')
- setf perl
- return 1
- endif
- if getline(1)[0] == '#' && getline(1) =~ 'perl'
- setf perl
- return 1
- endif
- let save_cursor = getpos('.')
- call cursor(1,1)
- let has_use = search('^use\s\s*\k', 'c', 30) > 0
- call setpos('.', save_cursor)
- if has_use
- setf perl
- return 1
- endif
- 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.
-" 3. Default to "plain" or to g:tex_flavor, can be set in user's vimrc.
-func dist#ft#FTtex()
- let firstline = getline(1)
- if firstline =~ '^%&\s*\a\+'
- let format = tolower(matchstr(firstline, '\a\+'))
- let format = substitute(format, 'pdf', '', '')
- if format == 'tex'
- let format = 'latex'
- elseif format == 'plaintex'
- let format = 'plain'
- endif
- elseif expand('%') =~ 'tex/context/.*/.*.tex'
- let format = 'context'
- else
- " Default value, may be changed later:
- let format = exists("g:tex_flavor") ? g:tex_flavor : 'plain'
- " Save position, go to the top of the file, find first non-comment line.
- let save_cursor = getpos('.')
- call cursor(1,1)
- let firstNC = search('^\s*[^[:space:]%]', 'c', 1000)
- 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 . '\)',
- \ 'cnp', firstNC + 1000)
- if kwline == 1 " lpat matched
- let format = 'latex'
- elseif kwline == 2 " cpat matched
- let format = 'context'
- endif " If neither matched, keep default set above.
- " let lline = search('^\s*\\\%(' . lpat . '\)', 'cn', firstNC + 1000)
- " let cline = search('^\s*\\\%(' . cpat . '\)', 'cn', firstNC + 1000)
- " if cline > 0
- " let format = 'context'
- " endif
- " if lline > 0 && (cline == 0 || cline > lline)
- " let format = 'tex'
- " endif
- endif " firstNC
- call setpos('.', save_cursor)
- endif " firstline =~ '^%&\s*\a\+'
-
- " Translation from formats to file types. TODO: add AMSTeX, RevTex, others?
- if format == 'plain'
- setf plaintex
- elseif format == 'context'
- setf context
- else " probably LaTeX
- setf tex
- endif
- return
-endfunc
-
-func dist#ft#FTxml()
- let n = 1
- while n < 100 && n <= line("$")
- let line = getline(n)
- " DocBook 4 or DocBook 5.
- let is_docbook4 = line =~ '<!DOCTYPE.*DocBook'
- let is_docbook5 = line =~ ' xmlns="http://docbook.org/ns/docbook"'
- if is_docbook4 || is_docbook5
- let b:docbk_type = "xml"
- if is_docbook5
- let b:docbk_ver = 5
- else
- let b:docbk_ver = 4
- endif
- setf docbk
- return
- endif
- if line =~ 'xmlns:xbl="http://www.mozilla.org/xbl"'
- setf xbl
- return
- endif
- let n += 1
- endwhile
- setf xml
-endfunc
-
-func dist#ft#FTy()
- let n = 1
- while n < 100 && n <= line("$")
- let line = getline(n)
- if line =~ '^\s*%'
- setf yacc
- return
- endif
- if getline(n) =~ '^\s*\(#\|class\>\)' && getline(n) !~ '^\s*#\s*include'
- setf racc
- return
- endif
- let n = n + 1
- endwhile
- setf yacc
-endfunc
-
-func dist#ft#Redif()
- let lnum = 1
- while lnum <= 5 && lnum < line('$')
- if getline(lnum) =~ "^\ctemplate-type:"
- setf redif
- return
- endif
- let lnum = lnum + 1
- endwhile
-endfunc
-
-" This function is called for all files under */debian/patches/*, make sure not
-" to non-dep3patch files, such as README and other text files.
-func dist#ft#Dep3patch()
- if expand('%:t') ==# 'series'
- return
- endif
-
- for ln in getline(1, 100)
- if ln =~# '^\%(Description\|Subject\|Origin\|Bug\|Forwarded\|Author\|From\|Reviewed-by\|Acked-by\|Last-Updated\|Applied-Upstream\):'
- setf dep3patch
- return
- elseif ln =~# '^---'
- " end of headers found. stop processing
- return
- endif
- endfor
-endfunc
-
-" This function checks the first 15 lines for appearance of 'FoamFile'
-" and then 'object' in a following line.
-" In that case, it's probably an OpenFOAM file
-func dist#ft#FTfoam()
- let ffile = 0
- let lnum = 1
- while lnum <= 15
- if getline(lnum) =~# '^FoamFile'
- let ffile = 1
- elseif ffile == 1 && getline(lnum) =~# '^\s*object'
- setf foam
- return
- endif
- let lnum = lnum + 1
- endwhile
-endfunc
-
-" Determine if a *.tf file is TF mud client or terraform
-func dist#ft#FTtf()
- let numberOfLines = line('$')
- for i in range(1, numberOfLines)
- let currentLine = trim(getline(i))
- let firstCharacter = currentLine[0]
- if firstCharacter !=? ";" && firstCharacter !=? "/" && firstCharacter !=? ""
- setf terraform
- return
- endif
- endfor
- setf tf
-endfunc
-
-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/health.vim b/runtime/autoload/health.vim
index a693868381..5fd4627b11 100644
--- a/runtime/autoload/health.vim
+++ b/runtime/autoload/health.vim
@@ -5,8 +5,13 @@ function! health#check(plugin_names) abort
\ ? s:discover_healthchecks()
\ : s:get_healthcheck(a:plugin_names)
- " create scratch-buffer
- execute 'tab sbuffer' nvim_create_buf(v:true, v:true)
+ " Create buffer and open in a tab, unless this is the default buffer when Nvim starts.
+ let emptybuf = (bufnr('$') == 1 && empty(getline(1)) && 1 == line('$'))
+ execute (emptybuf ? 'buffer' : 'tab sbuffer') nvim_create_buf(v:true, v:true)
+ if bufexists('health://')
+ bwipe health://
+ endif
+ file health://
setfiletype checkhealth
if empty(healthchecks)
@@ -38,7 +43,7 @@ function! health#check(plugin_names) abort
\ name, v:throwpoint, v:exception))
endif
endtry
- let header = [name. ': ' . func, repeat('=', 72)]
+ let header = [repeat('=', 78), name .. ': ' .. func, '']
" remove empty line after header from report_start
let s:output = s:output[0] == '' ? s:output[1:] : s:output
let s:output = header + s:output + ['']
@@ -47,8 +52,7 @@ function! health#check(plugin_names) abort
endfor
endif
- " needed for plasticboy/vim-markdown, because it uses fdm=expr
- normal! zR
+ " Clear the 'Running healthchecks...' message.
redraw|echo ''
endfunction
@@ -58,7 +62,7 @@ endfunction
" Starts a new report.
function! health#report_start(name) abort
- call s:collect_output("\n## " . a:name)
+ call s:collect_output(printf("\n%s ~", a:name))
endfunction
" Indents lines *except* line 1 of a string if it contains newlines.
@@ -81,7 +85,7 @@ endfunction
" Format a message for a specific report item.
" a:1: Optional advice (string or list)
function! s:format_report_message(status, msg, ...) abort " {{{
- let output = ' - ' . a:status . ': ' . s:indent_after_line1(a:msg, 4)
+ let output = '- ' .. a:status .. (empty(a:status) ? '' : ' ') .. s:indent_after_line1(a:msg, 2)
" Optional parameters
if a:0 > 0
@@ -92,9 +96,9 @@ function! s:format_report_message(status, msg, ...) abort " {{{
" Report each suggestion
if !empty(advice)
- let output .= "\n - ADVICE:"
+ let output .= "\n - ADVICE:"
for suggestion in advice
- let output .= "\n - " . s:indent_after_line1(suggestion, 10)
+ let output .= "\n - " . s:indent_after_line1(suggestion, 6)
endfor
endif
endif
@@ -102,9 +106,9 @@ function! s:format_report_message(status, msg, ...) abort " {{{
return s:help_to_link(output)
endfunction " }}}
-" Use {msg} to report information in the current section
+" Reports a message as a listitem in the current section.
function! health#report_info(msg) abort " {{{
- call s:collect_output(s:format_report_message('INFO', a:msg))
+ call s:collect_output(s:format_report_message('', a:msg))
endfunction " }}}
" Reports a successful healthcheck.
diff --git a/runtime/autoload/health/nvim.vim b/runtime/autoload/health/nvim.vim
deleted file mode 100644
index 9b387095ee..0000000000
--- a/runtime/autoload/health/nvim.vim
+++ /dev/null
@@ -1,284 +0,0 @@
-let s:suggest_faq = 'https://github.com/neovim/neovim/wiki/FAQ'
-
-function! s:check_config() abort
- let ok = v:true
- call health#report_start('Configuration')
-
- let vimrc = empty($MYVIMRC) ? stdpath('config').'/init.vim' : $MYVIMRC
- if !filereadable(vimrc)
- let ok = v:false
- let has_vim = filereadable(expand('~/.vimrc'))
- call health#report_warn((-1 == getfsize(vimrc) ? 'Missing' : 'Unreadable').' user config file: '.vimrc,
- \[ has_vim ? ':help nvim-from-vim' : ':help init.vim' ])
- endif
-
- " If $VIM is empty we don't care. Else make sure it is valid.
- if !empty($VIM) && !filereadable($VIM.'/runtime/doc/nvim.txt')
- let ok = v:false
- call health#report_error('$VIM is invalid: '.$VIM)
- endif
-
- if exists('$NVIM_TUI_ENABLE_CURSOR_SHAPE')
- let ok = v:false
- call health#report_warn('$NVIM_TUI_ENABLE_CURSOR_SHAPE is ignored in Nvim 0.2+',
- \ [ "Use the 'guicursor' option to configure cursor shape. :help 'guicursor'",
- \ 'https://github.com/neovim/neovim/wiki/Following-HEAD#20170402' ])
- endif
-
- if v:ctype ==# 'C'
- let ok = v:false
- call health#report_error('Locale does not support UTF-8. Unicode characters may not display correctly.'
- \ .printf("\n$LANG=%s $LC_ALL=%s $LC_CTYPE=%s", $LANG, $LC_ALL, $LC_CTYPE),
- \ [ 'If using tmux, try the -u option.',
- \ 'Ensure that your terminal/shell/tmux/etc inherits the environment, or set $LANG explicitly.' ,
- \ 'Configure your system locale.' ])
- endif
-
- if &paste
- let ok = v:false
- call health#report_error("'paste' is enabled. This option is only for pasting text.\nIt should not be set in your config.",
- \ [ 'Remove `set paste` from your init.vim, if applicable.',
- \ 'Check `:verbose set paste?` to see if a plugin or script set the option.', ])
- endif
-
- let writeable = v:true
- let shadafile = empty(&shada) ? &shada : substitute(matchstr(
- \ split(&shada, ',')[-1], '^n.\+'), '^n', '', '')
- let shadafile = empty(&shadafile) ? empty(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
- " create a shada file
- try
- wshada
- catch /.*/
- let writeable = v:false
- endtry
- endif
- if !writeable || (!empty(shadafile) &&
- \ (!filereadable(shadafile) || !filewritable(shadafile)))
- let ok = v:false
- call health#report_error('shada file is not '.
- \ ((!writeable || filereadable(shadafile)) ?
- \ 'writeable' : 'readable').":\n".shadafile)
- endif
-
- if ok
- call health#report_ok('no issues found')
- endif
-endfunction
-
-" Load the remote plugin manifest file and check for unregistered plugins
-function! s:check_rplugin_manifest() abort
- call health#report_start('Remote Plugins')
- let existing_rplugins = {}
-
- for item in remote#host#PluginsForHost('python')
- let existing_rplugins[item.path] = 'python'
- endfor
-
- for item in remote#host#PluginsForHost('python3')
- let existing_rplugins[item.path] = 'python3'
- endfor
-
- let require_update = 0
-
- for path in map(split(&runtimepath, ','), 'resolve(v:val)')
- let python_glob = glob(path.'/rplugin/python*', 1, 1)
- if empty(python_glob)
- continue
- endif
-
- let python_dir = python_glob[0]
- let python_version = fnamemodify(python_dir, ':t')
-
- for script in glob(python_dir.'/*.py', 1, 1)
- \ + glob(python_dir.'/*/__init__.py', 1, 1)
- let contents = join(readfile(script))
- if contents =~# '\<\%(from\|import\)\s\+neovim\>'
- if script =~# '[\/]__init__\.py$'
- let script = tr(fnamemodify(script, ':h'), '\', '/')
- endif
-
- if !has_key(existing_rplugins, script)
- let msg = printf('"%s" is not registered.', fnamemodify(path, ':t'))
- if python_version ==# 'pythonx'
- if !has('python3')
- let msg .= ' (python3 not available)'
- endif
- elseif !has(python_version)
- let msg .= printf(' (%s not available)', python_version)
- else
- let require_update = 1
- endif
-
- call health#report_warn(msg)
- endif
-
- break
- endif
- endfor
- endfor
-
- if require_update
- call health#report_warn('Out of date', ['Run `:UpdateRemotePlugins`'])
- else
- call health#report_ok('Up to date')
- endif
-endfunction
-
-function! s:check_performance() abort
- call health#report_start('Performance')
-
- " check buildtype
- let buildtype = matchstr(execute('version'), '\v\cbuild type:?\s*[^\n\r\t ]+')
- if empty(buildtype)
- call health#report_error('failed to get build type from :version')
- elseif buildtype =~# '\v(MinSizeRel|Release|RelWithDebInfo)'
- call health#report_ok(buildtype)
- else
- call health#report_info(buildtype)
- call health#report_warn(
- \ 'Non-optimized '.(has('debug')?'(DEBUG) ':'').'build. Nvim will be slower.',
- \ ['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(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(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'
- endif
- endif
- return val
-endfunction
-
-function! s:check_tmux() abort
- if empty($TMUX) || !executable('tmux')
- return
- endif
- call health#report_start('tmux')
-
- " check escape-time
- let suggestions = ["set escape-time in ~/.tmux.conf:\nset-option -sg escape-time 10",
- \ s:suggest_faq]
- let tmux_esc_time = s:get_tmux_option('escape-time')
- if tmux_esc_time !=# 'error'
- if empty(tmux_esc_time)
- call health#report_error('`escape-time` is not set', suggestions)
- elseif tmux_esc_time > 300
- call health#report_error(
- \ '`escape-time` ('.tmux_esc_time.') is higher than 300ms', suggestions)
- else
- call health#report_ok('escape-time: '.tmux_esc_time)
- endif
- endif
-
- " check focus-events
- let suggestions = ["(tmux 1.9+ only) Set `focus-events` in ~/.tmux.conf:\nset-option -g focus-events on"]
- let tmux_focus_events = s:get_tmux_option('focus-events')
- call health#report_info('Checking stuff')
- if tmux_focus_events !=# 'error'
- if empty(tmux_focus_events) || tmux_focus_events !=# 'on'
- call health#report_warn(
- \ "`focus-events` is not enabled. |'autoread'| may not work.", suggestions)
- else
- call health#report_ok('focus-events: '.tmux_focus_events)
- endif
- endif
-
- " check default-terminal and $TERM
- call health#report_info('$TERM: '.$TERM)
- let cmd = 'tmux show-option -qvg default-terminal'
- 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(split(cmd))
- let tmux_default_term = substitute(out, '\v(\s|\r|\n)', '', 'g')
- endif
-
- if v:shell_error
- call health#report_error('command failed: '.cmd."\n".out)
- elseif tmux_default_term !=# $TERM
- call health#report_info('default-terminal: '.tmux_default_term)
- call health#report_error(
- \ '$TERM differs from the tmux `default-terminal` setting. Colors might look wrong.',
- \ ['$TERM may have been set by some rc (.bashrc, .zshrc, ...).'])
- elseif $TERM !~# '\v(tmux-256color|screen-256color)'
- call health#report_error(
- \ '$TERM should be "screen-256color" or "tmux-256color" in tmux. Colors might look wrong.',
- \ ["Set default-terminal in ~/.tmux.conf:\nset-option -g default-terminal \"screen-256color\"",
- \ s:suggest_faq])
- endif
-
- " check for RGB capabilities
- 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
- call health#report_warn(
- \ "Neither Tc nor RGB capability set. True colors are disabled. |'termguicolors'| won't work properly.",
- \ ["Put this in your ~/.tmux.conf and replace XXX by your $TERM outside of tmux:\nset-option -sa terminal-overrides ',XXX:RGB'",
- \ "For older tmux versions use this instead:\nset-option -ga terminal-overrides ',XXX:Tc'"])
- endif
-endfunction
-
-function! s:check_terminal() abort
- if !executable('infocmp')
- return
- endif
- call health#report_start('terminal')
- let cmd = 'infocmp -L'
- let out = system(split(cmd))
- let kbs_entry = matchstr(out, 'key_backspace=[^,[:space:]]*')
- let kdch1_entry = matchstr(out, 'key_dc=[^,[:space:]]*')
-
- if v:shell_error
- \ && (!has('win32')
- \ || empty(matchstr(out,
- \ 'infocmp: couldn''t open terminfo file .\+'
- \ ..'\%(conemu\|vtpcon\|win32con\)')))
- call health#report_error('command failed: '.cmd."\n".out)
- else
- call health#report_info('key_backspace (kbs) terminfo entry: '
- \ .(empty(kbs_entry) ? '? (not found)' : kbs_entry))
- call health#report_info('key_dc (kdch1) terminfo entry: '
- \ .(empty(kbs_entry) ? '? (not found)' : kdch1_entry))
- endif
- for env_var in ['XTERM_VERSION', 'VTE_VERSION', 'TERM_PROGRAM', 'COLORTERM', 'SSH_TTY']
- if exists('$'.env_var)
- call health#report_info(printf("$%s='%s'", env_var, eval('$'.env_var)))
- endif
- endfor
-endfunction
-
-function! health#nvim#check() abort
- call s:check_config()
- call s:check_performance()
- call s:check_rplugin_manifest()
- call s:check_terminal()
- call s:check_tmux()
-endfunction
diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim
deleted file mode 100644
index b8a73a64c9..0000000000
--- a/runtime/autoload/man.vim
+++ /dev/null
@@ -1,529 +0,0 @@
-" Maintainer: Anmol Sethi <hi@nhooyr.io>
-
-if exists('s:loaded_man')
- finish
-endif
-let s:loaded_man = 1
-
-let s:find_arg = '-w'
-let s:localfile_arg = v:true " Always use -l if possible. #6683
-
-function! man#init() abort
- try
- " Check for -l support.
- call s:get_page(s:get_path('', 'man'))
- catch /command error .*/
- let s:localfile_arg = v:false
- endtry
-endfunction
-
-function! man#open_page(count, mods, ...) abort
- if a:0 > 2
- call s:error('too many arguments')
- return
- elseif a:0 == 0
- let ref = &filetype ==# 'man' ? expand('<cWORD>') : expand('<cword>')
- if empty(ref)
- call s:error('no identifier under cursor')
- return
- endif
- elseif a:0 ==# 1
- let ref = a:1
- else
- " Combine the name and sect into a manpage reference so that all
- " verification/extraction can be kept in a single function.
- " If a:2 is a reference as well, that is fine because it is the only
- " reference that will match.
- let ref = a:2.'('.a:1.')'
- endif
- try
- let [sect, name] = s:extract_sect_and_name_ref(ref)
- if a:count >= 0
- let sect = string(a:count)
- endif
- let path = s:verify_exists(sect, name)
- let [sect, name] = s:extract_sect_and_name_path(path)
- catch
- call s:error(v:exception)
- return
- endtry
-
- let [l:buf, l:save_tfu] = [bufnr(), &tagfunc]
- try
- setlocal tagfunc=man#goto_tag
- let l:target = l:name . '(' . l:sect . ')'
- if a:mods !~# 'tab' && s:find_man()
- execute 'silent keepalt tag' l:target
- else
- execute 'silent keepalt' a:mods 'stag' l:target
- endif
- call s:set_options(v:false)
- finally
- call setbufvar(l:buf, '&tagfunc', l:save_tfu)
- endtry
-
- let b:man_sect = sect
-endfunction
-
-" Called when a man:// buffer is opened.
-function! man#read_page(ref) abort
- try
- let [sect, name] = s:extract_sect_and_name_ref(a:ref)
- let path = s:verify_exists(sect, name)
- let [sect, name] = s:extract_sect_and_name_path(path)
- let page = s:get_page(path)
- catch
- call s:error(v:exception)
- return
- endtry
- let b:man_sect = sect
- call s:put_page(page)
-endfunction
-
-" Handler for s:system() function.
-function! s:system_handler(jobid, data, event) dict abort
- if a:event is# 'stdout' || a:event is# 'stderr'
- let self[a:event] .= join(a:data, "\n")
- else
- let self.exit_code = a:data
- endif
-endfunction
-
-" Run a system command and timeout after 30 seconds.
-function! s:system(cmd, ...) abort
- let opts = {
- \ 'stdout': '',
- \ 'stderr': '',
- \ 'exit_code': 0,
- \ 'on_stdout': function('s:system_handler'),
- \ 'on_stderr': function('s:system_handler'),
- \ 'on_exit': function('s:system_handler'),
- \ }
- let jobid = jobstart(a:cmd, opts)
-
- if jobid < 1
- throw printf('command error %d: %s', jobid, join(a:cmd))
- endif
-
- let res = jobwait([jobid], 30000)
- if res[0] == -1
- try
- call jobstop(jobid)
- throw printf('command timed out: %s', join(a:cmd))
- catch /^Vim(call):E900:/
- endtry
- elseif res[0] == -2
- throw printf('command interrupted: %s', join(a:cmd))
- endif
- if opts.exit_code != 0
- throw printf("command error (%d) %s: %s", jobid, join(a:cmd), substitute(opts.stderr, '\_s\+$', '', &gdefault ? '' : 'g'))
- endif
-
- return opts.stdout
-endfunction
-
-function! s:set_options(pager) abort
- setlocal noswapfile buftype=nofile bufhidden=hide
- setlocal nomodified readonly nomodifiable
- let b:pager = a:pager
- setlocal filetype=man
-endfunction
-
-function! s:get_page(path) abort
- " Disable hard-wrap by using a big $MANWIDTH (max 1000 on some systems #9065).
- " Soft-wrap: ftplugin/man.vim sets wrap/breakindent/….
- " Hard-wrap: driven by `man`.
- let manwidth = !get(g:, 'man_hardwrap', 1) ? 999 : (empty($MANWIDTH) ? winwidth(0) : $MANWIDTH)
- " Force MANPAGER=cat to ensure Vim is not recursively invoked (by man-db).
- " http://comments.gmane.org/gmane.editors.vim.devel/29085
- " Set MAN_KEEP_FORMATTING so Debian man doesn't discard backspaces.
- let cmd = ['env', 'MANPAGER=cat', 'MANWIDTH='.manwidth, 'MAN_KEEP_FORMATTING=1', 'man']
- return s:system(cmd + (s:localfile_arg ? ['-l', a:path] : [a:path]))
-endfunction
-
-function! s:put_page(page) abort
- setlocal modifiable noreadonly noswapfile
- silent keepjumps %delete _
- silent put =a:page
- while getline(1) =~# '^\s*$'
- silent keepjumps 1delete _
- endwhile
- " XXX: nroff justifies text by filling it with whitespace. That interacts
- " badly with our use of $MANWIDTH=999. Hack around this by using a fixed
- " size for those whitespace regions.
- silent! keeppatterns keepjumps %s/\s\{199,}/\=repeat(' ', 10)/g
- 1
- lua require("man").highlight_man_page()
- call s:set_options(v:false)
-endfunction
-
-function! man#show_toc() abort
- let bufname = bufname('%')
- let info = getloclist(0, {'winid': 1})
- if !empty(info) && getwinvar(info.winid, 'qf_toc') ==# bufname
- lopen
- return
- endif
-
- let toc = []
- let lnum = 2
- let last_line = line('$') - 1
- 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)
- endwhile
-
- call setloclist(0, toc, ' ')
- call setloclist(0, [], 'a', {'title': 'Man TOC'})
- lopen
- let w:qf_toc = bufname
-endfunction
-
-" attempt to extract the name and sect out of 'name(sect)'
-" otherwise just return the largest string of valid characters in ref
-function! s:extract_sect_and_name_ref(ref) abort
- if a:ref[0] ==# '-' " try ':Man -pandoc' with this disabled.
- throw 'manpage name cannot start with ''-'''
- endif
- let ref = matchstr(a:ref, '[^()]\+([^()]\+)')
- if empty(ref)
- let name = matchstr(a:ref, '[^()]\+')
- if empty(name)
- throw 'manpage reference cannot contain only parentheses'
- endif
- return ['', s:spaces_to_underscores(name)]
- endif
- let left = split(ref, '(')
- " see ':Man 3X curses' on why tolower.
- " TODO(nhooyr) Not sure if this is portable across OSs
- " but I have not seen a single uppercase section.
- return [tolower(split(left[1], ')')[0]), s:spaces_to_underscores(left[0])]
-endfunction
-
-" replace spaces in a man page name with underscores
-" intended for PostgreSQL, which has man pages like 'CREATE_TABLE(7)';
-" while editing SQL source code, it's nice to visually select 'CREATE TABLE'
-" and hit 'K', which requires this transformation
-function! s:spaces_to_underscores(str)
- return substitute(a:str, ' ', '_', 'g')
-endfunction
-
-function! s:get_path(sect, name) abort
- " Some man implementations (OpenBSD) return all available paths from the
- " search command. Previously, this function would simply select the first one.
- "
- " However, some searches will report matches that are incorrect:
- " man -w strlen may return string.3 followed by strlen.3, and therefore
- " selecting the first would get us the wrong page. Thus, we must find the
- " first matching one.
- "
- " There's yet another special case here. Consider the following:
- " If you run man -w strlen and string.3 comes up first, this is a problem. We
- " should search for a matching named one in the results list.
- " However, if you search for man -w clock_gettime, you will *only* get
- " clock_getres.2, which is the right page. Searching the resuls for
- " clock_gettime will no longer work. In this case, we should just use the
- " first one that was found in the correct section.
- "
- " Finally, we can avoid relying on -S or -s here since they are very
- " inconsistently supported. Instead, call -w with a section and a name.
- if empty(a:sect)
- let results = split(s:system(['man', s:find_arg, a:name]))
- else
- let results = split(s:system(['man', s:find_arg, a:sect, a:name]))
- endif
-
- if empty(results)
- return ''
- endif
-
- " find any that match the specified name
- let namematches = filter(copy(results), 'fnamemodify(v:val, ":t") =~ a:name')
- let sectmatches = []
-
- if !empty(namematches) && !empty(a:sect)
- let sectmatches = filter(copy(namematches), 'fnamemodify(v:val, ":e") == a:sect')
- endif
-
- return substitute(get(sectmatches, 0, get(namematches, 0, results[0])), '\n\+$', '', '')
-endfunction
-
-" s:verify_exists attempts to find the path to a manpage
-" based on the passed section and name.
-"
-" 1. If the passed section is empty, b:man_default_sects is used.
-" 2. If manpage could not be found with the given sect and name,
-" then another attempt is made with b:man_default_sects.
-" 3. If it still could not be found, then we try again without a section.
-" 4. If still not found but $MANSECT is set, then we try again with $MANSECT
-" unset.
-"
-" This function is careful to avoid duplicating a search if a previous
-" step has already done it. i.e if we use b:man_default_sects in step 1,
-" then we don't do it again in step 2.
-function! s:verify_exists(sect, name) abort
- let sect = a:sect
-
- if empty(sect)
- " no section specified, so search with b:man_default_sects
- if exists('b:man_default_sects')
- let sects = split(b:man_default_sects, ',')
- for sec in sects
- try
- let res = s:get_path(sec, a:name)
- if !empty(res)
- return res
- endif
- catch /^command error (/
- endtry
- endfor
- endif
- else
- " try with specified section
- try
- let res = s:get_path(sect, a:name)
- if !empty(res)
- return res
- endif
- catch /^command error (/
- endtry
-
- " try again with b:man_default_sects
- if exists('b:man_default_sects')
- let sects = split(b:man_default_sects, ',')
- for sec in sects
- try
- let res = s:get_path(sec, a:name)
- if !empty(res)
- return res
- endif
- catch /^command error (/
- endtry
- endfor
- endif
- endif
-
- " if none of the above worked, we will try with no section
- try
- let res = s:get_path('', a:name)
- if !empty(res)
- return res
- endif
- catch /^command error (/
- endtry
-
- " if that still didn't work, we will check for $MANSECT and try again with it
- " unset
- if !empty($MANSECT)
- try
- let MANSECT = $MANSECT
- call setenv('MANSECT', v:null)
- let res = s:get_path('', a:name)
- if !empty(res)
- return res
- endif
- catch /^command error (/
- finally
- call setenv('MANSECT', MANSECT)
- endtry
- endif
-
- " finally, if that didn't work, there is no hope
- throw 'no manual entry for ' . a:name
-endfunction
-
-" Extracts the name/section from the 'path/name.sect', because sometimes the actual section is
-" more specific than what we provided to `man` (try `:Man 3 App::CLI`).
-" Also on linux, name seems to be case-insensitive. So for `:Man PRIntf`, we
-" still want the name of the buffer to be 'printf'.
-function! s:extract_sect_and_name_path(path) abort
- let tail = fnamemodify(a:path, ':t')
- if a:path =~# '\.\%([glx]z\|bz2\|lzma\|Z\)$' " valid extensions
- let tail = fnamemodify(tail, ':r')
- endif
- let sect = matchstr(tail, '\.\zs[^.]\+$')
- let name = matchstr(tail, '^.\+\ze\.')
- return [sect, name]
-endfunction
-
-function! s:find_man() abort
- let l:win = 1
- while l:win <= winnr('$')
- let l:buf = winbufnr(l:win)
- if getbufvar(l:buf, '&filetype', '') ==# 'man'
- execute l:win.'wincmd w'
- return 1
- endif
- let l:win += 1
- endwhile
- return 0
-endfunction
-
-function! s:error(msg) abort
- redraw
- echohl ErrorMsg
- echon 'man.vim: ' a:msg
- echohl None
-endfunction
-
-" see s:extract_sect_and_name_ref on why tolower(sect)
-function! man#complete(arg_lead, cmd_line, cursor_pos) abort
- let args = split(a:cmd_line)
- let cmd_offset = index(args, 'Man')
- if cmd_offset > 0
- " Prune all arguments up to :Man itself. Otherwise modifier commands like
- " :tab, :vertical, etc. would lead to a wrong length.
- let args = args[cmd_offset:]
- endif
- let l = len(args)
- if l > 3
- return
- elseif l ==# 1
- let name = ''
- let sect = ''
- elseif a:arg_lead =~# '^[^()]\+([^()]*$'
- " cursor (|) is at ':Man printf(|' or ':Man 1 printf(|'
- " The later is is allowed because of ':Man pri<TAB>'.
- " It will offer 'priclass.d(1m)' even though section is specified as 1.
- let tmp = split(a:arg_lead, '(')
- let name = tmp[0]
- let sect = tolower(get(tmp, 1, ''))
- return s:complete(sect, '', name)
- elseif args[1] !~# '^[^()]\+$'
- " cursor (|) is at ':Man 3() |' or ':Man (3|' or ':Man 3() pri|'
- " or ':Man 3() pri |'
- return
- elseif l ==# 2
- if empty(a:arg_lead)
- " cursor (|) is at ':Man 1 |'
- let name = ''
- let sect = tolower(args[1])
- else
- " cursor (|) is at ':Man pri|'
- if a:arg_lead =~# '\/'
- " if the name is a path, complete files
- " TODO(nhooyr) why does this complete the last one automatically
- return glob(a:arg_lead.'*', 0, 1)
- endif
- let name = a:arg_lead
- let sect = ''
- endif
- elseif a:arg_lead !~# '^[^()]\+$'
- " cursor (|) is at ':Man 3 printf |' or ':Man 3 (pr)i|'
- return
- else
- " cursor (|) is at ':Man 3 pri|'
- let name = a:arg_lead
- let sect = tolower(args[1])
- endif
- return s:complete(sect, sect, name)
-endfunction
-
-function! s:get_paths(sect, name, do_fallback) abort
- " callers must try-catch this, as some `man` implementations don't support `s:find_arg`
- try
- let mandirs = join(split(s:system(['man', s:find_arg]), ':\|\n'), ',')
- let paths = globpath(mandirs, 'man?/'.a:name.'*.'.a:sect.'*', 0, 1)
- try
- " Prioritize the result from verify_exists as it obeys b:man_default_sects.
- let first = s:verify_exists(a:sect, a:name)
- let paths = filter(paths, 'v:val !=# first')
- let paths = [first] + paths
- catch
- endtry
- return paths
- catch
- if !a:do_fallback
- throw v:exception
- endif
-
- " Fallback to a single path, with the page we're trying to find.
- try
- return [s:verify_exists(a:sect, a:name)]
- catch
- return []
- endtry
- endtry
-endfunction
-
-function! s:complete(sect, psect, name) abort
- let pages = s:get_paths(a:sect, a:name, v:false)
- " We remove duplicates in case the same manpage in different languages was found.
- return uniq(sort(map(pages, 's:format_candidate(v:val, a:psect)'), 'i'))
-endfunction
-
-function! s:format_candidate(path, psect) abort
- if a:path =~# '\.\%(pdf\|in\)$' " invalid extensions
- return
- endif
- let [sect, name] = s:extract_sect_and_name_path(a:path)
- if sect ==# a:psect
- return name
- elseif sect =~# a:psect.'.\+$'
- " We include the section if the user provided section is a prefix
- " of the actual section.
- return name.'('.sect.')'
- endif
-endfunction
-
-" Called when Nvim is invoked as $MANPAGER.
-function! man#init_pager() abort
- if getline(1) =~# '^\s*$'
- silent keepjumps 1delete _
- else
- keepjumps 1
- endif
- lua require("man").highlight_man_page()
- " Guess the ref from the heading (which is usually uppercase, so we cannot
- " know the correct casing, cf. `man glDrawArraysInstanced`).
- let ref = substitute(matchstr(getline(1), '^[^)]\+)'), ' ', '_', 'g')
- try
- let b:man_sect = s:extract_sect_and_name_ref(ref)[0]
- catch
- let b:man_sect = ''
- endtry
- if -1 == match(bufname('%'), 'man:\/\/') " Avoid duplicate buffers, E95.
- execute 'silent file man://'.tolower(fnameescape(ref))
- endif
-
- call s:set_options(v:true)
-endfunction
-
-function! man#goto_tag(pattern, flags, info) abort
- let [l:sect, l:name] = s:extract_sect_and_name_ref(a:pattern)
-
- let l:paths = s:get_paths(l:sect, l:name, v:true)
- let l:structured = []
-
- for l:path in l:paths
- let [l:sect, l:name] = s:extract_sect_and_name_path(l:path)
- let l:structured += [{
- \ 'name': l:name,
- \ 'title': l:name . '(' . l:sect . ')'
- \ }]
- endfor
-
- if &cscopetag
- " return only a single entry so we work well with :cstag (#11675)
- let l:structured = l:structured[:0]
- endif
-
- return map(l:structured, {
- \ _, entry -> {
- \ 'name': entry.name,
- \ 'filename': 'man://' . entry.title,
- \ 'cmd': '1'
- \ }
- \ })
-endfunction
-
-call man#init()
diff --git a/runtime/autoload/netrw.vim b/runtime/autoload/netrw.vim
index ef0282848f..2fcf0b32c7 100644
--- a/runtime/autoload/netrw.vim
+++ b/runtime/autoload/netrw.vim
@@ -1751,8 +1751,10 @@ fun! s:NetrwOptionsRestore(vt)
" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt,'~'.expand("<slnum>"))
if !exists("{a:vt}netrw_optionsave")
" call Decho("case ".a:vt."netrw_optionsave : doesn't exist",'~'.expand("<slnum>"))
-" call Decho("..doing filetype detect anyway")
- filetype detect
+ if !isdirectory(expand('%'))
+" call Decho("..doing filetype detect anyway")
+ filetype detect
+ endif
" call Decho("..settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt,'~'.expand("<slnum>"))
" call Decho("..ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
" call Dret("s:NetrwOptionsRestore : ".a:vt."netrw_optionsave doesn't exist")
@@ -1859,9 +1861,11 @@ fun! s:NetrwOptionsRestore(vt)
" were having their filetype detect-generated settings overwritten by
" NetrwOptionRestore.
if &ft != "netrw"
-" call Decho("before: filetype detect (ft=".&ft.")",'~'.expand("<slnum>"))
- filetype detect
-" call Decho("after : filetype detect (ft=".&ft.")",'~'.expand("<slnum>"))
+ if !isdirectory(expand('%'))
+" call Decho("before: filetype detect (ft=".&ft.")",'~'.expand("<slnum>"))
+ filetype detect
+" call Decho("after : filetype detect (ft=".&ft.")",'~'.expand("<slnum>"))
+ endif
endif
" call Decho("(s:NetrwOptionsRestore) lines=".&lines)
" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt,'~'.expand("<slnum>"))
@@ -6442,7 +6446,6 @@ fun! s:NetrwMaps(islocal)
" if !hasmapto('<Plug>NetrwMarkFileGrep') |nmap <buffer> <silent> <nowait> mg <Plug>NetrwMarkFileGrep|endif
" if !hasmapto('<Plug>NetrwMarkHideSfx') |nmap <buffer> <silent> <nowait> mh <Plug>NetrwMarkHideSfx|endif
" if !hasmapto('<Plug>NetrwMarkFileMove') |nmap <buffer> <silent> <nowait> mm <Plug>NetrwMarkFileMove|endif
-" if !hasmapto('<Plug>NetrwMarkFilePrint') |nmap <buffer> <silent> <nowait> mp <Plug>NetrwMarkFilePrint|endif
" if !hasmapto('<Plug>NetrwMarkFileRegexp') |nmap <buffer> <silent> <nowait> mr <Plug>NetrwMarkFileRegexp|endif
" if !hasmapto('<Plug>NetrwMarkFileSource') |nmap <buffer> <silent> <nowait> ms <Plug>NetrwMarkFileSource|endif
" if !hasmapto('<Plug>NetrwMarkFileTag') |nmap <buffer> <silent> <nowait> mT <Plug>NetrwMarkFileTag|endif
@@ -6505,7 +6508,6 @@ fun! s:NetrwMaps(islocal)
nnoremap <buffer> <silent> <nowait> mg :<c-u>call <SID>NetrwMarkFileGrep(1)<cr>
nnoremap <buffer> <silent> <nowait> mh :<c-u>call <SID>NetrwMarkHideSfx(1)<cr>
nnoremap <buffer> <silent> <nowait> mm :<c-u>call <SID>NetrwMarkFileMove(1)<cr>
- nnoremap <buffer> <silent> <nowait> mp :<c-u>call <SID>NetrwMarkFilePrint(1)<cr>
nnoremap <buffer> <silent> <nowait> mr :<c-u>call <SID>NetrwMarkFileRegexp(1)<cr>
nnoremap <buffer> <silent> <nowait> ms :<c-u>call <SID>NetrwMarkFileSource(1)<cr>
nnoremap <buffer> <silent> <nowait> mT :<c-u>call <SID>NetrwMarkFileTag(1)<cr>
@@ -6618,7 +6620,6 @@ fun! s:NetrwMaps(islocal)
nnoremap <buffer> <silent> <nowait> mg :<c-u>call <SID>NetrwMarkFileGrep(0)<cr>
nnoremap <buffer> <silent> <nowait> mh :<c-u>call <SID>NetrwMarkHideSfx(0)<cr>
nnoremap <buffer> <silent> <nowait> mm :<c-u>call <SID>NetrwMarkFileMove(0)<cr>
- nnoremap <buffer> <silent> <nowait> mp :<c-u>call <SID>NetrwMarkFilePrint(0)<cr>
nnoremap <buffer> <silent> <nowait> mr :<c-u>call <SID>NetrwMarkFileRegexp(0)<cr>
nnoremap <buffer> <silent> <nowait> ms :<c-u>call <SID>NetrwMarkFileSource(0)<cr>
nnoremap <buffer> <silent> <nowait> mT :<c-u>call <SID>NetrwMarkFileTag(0)<cr>
@@ -7836,46 +7837,6 @@ fun! s:NetrwMarkFileMove(islocal)
endfun
" ---------------------------------------------------------------------
-" s:NetrwMarkFilePrint: (invoked by mp) This function prints marked files {{{2
-" using the hardcopy command. Local marked-file list only.
-fun! s:NetrwMarkFilePrint(islocal)
-" call Dfunc("s:NetrwMarkFilePrint(islocal=".a:islocal.")")
- let curbufnr= bufnr("%")
-
- " sanity check
- if !exists("s:netrwmarkfilelist_{curbufnr}") || empty(s:netrwmarkfilelist_{curbufnr})
- NetrwKeepj call netrw#ErrorMsg(2,"there are no marked files in this window (:help netrw-mf)",66)
-" call Dret("s:NetrwMarkFilePrint")
- return
- endif
-" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>"))
- let curdir= s:NetrwGetCurdir(a:islocal)
-
- if exists("s:netrwmarkfilelist_{curbufnr}")
- let netrwmarkfilelist = s:netrwmarkfilelist_{curbufnr}
- call s:NetrwUnmarkList(curbufnr,curdir)
- for fname in netrwmarkfilelist
- if a:islocal
- if g:netrw_keepdir
- let fname= s:ComposePath(curdir,fname)
- endif
- else
- let fname= curdir.fname
- endif
- 1split
- " the autocmds will handle both local and remote files
-" call Decho("exe sil e ".escape(fname,' '),'~'.expand("<slnum>"))
- exe "sil NetrwKeepj e ".fnameescape(fname)
-" call Decho("hardcopy",'~'.expand("<slnum>"))
- hardcopy
- q
- endfor
- 2match none
- endif
-" call Dret("s:NetrwMarkFilePrint")
-endfun
-
-" ---------------------------------------------------------------------
" s:NetrwMarkFileRegexp: (invoked by mr) This function is used to mark {{{2
" files when given a regexp (for which a prompt is
" issued) (matches to name of files).
diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim
index 991bed6bbd..98c80f1843 100644
--- a/runtime/autoload/provider/clipboard.vim
+++ b/runtime/autoload/provider/clipboard.vim
@@ -97,18 +97,24 @@ function! provider#clipboard#Executable() abort
let s:copy['*'] = ['wl-copy', '--foreground', '--primary', '--type', 'text/plain']
let s:paste['*'] = ['wl-paste', '--no-newline', '--primary']
return 'wl-copy'
- elseif !empty($DISPLAY) && executable('xclip')
- let s:copy['+'] = ['xclip', '-quiet', '-i', '-selection', 'clipboard']
- let s:paste['+'] = ['xclip', '-o', '-selection', 'clipboard']
- let s:copy['*'] = ['xclip', '-quiet', '-i', '-selection', 'primary']
- let s:paste['*'] = ['xclip', '-o', '-selection', 'primary']
- return 'xclip'
+ elseif !empty($WAYLAND_DISPLAY) && executable('waycopy') && executable('waypaste')
+ let s:copy['+'] = ['waycopy', '-t', 'text/plain']
+ let s:paste['+'] = ['waypaste', '-t', 'text/plain']
+ let s:copy['*'] = s:copy['+']
+ let s:paste['*'] = s:paste['+']
+ return 'wayclip'
elseif !empty($DISPLAY) && executable('xsel') && s:cmd_ok('xsel -o -b')
let s:copy['+'] = ['xsel', '--nodetach', '-i', '-b']
let s:paste['+'] = ['xsel', '-o', '-b']
let s:copy['*'] = ['xsel', '--nodetach', '-i', '-p']
let s:paste['*'] = ['xsel', '-o', '-p']
return 'xsel'
+ elseif !empty($DISPLAY) && executable('xclip')
+ let s:copy['+'] = ['xclip', '-quiet', '-i', '-selection', 'clipboard']
+ let s:paste['+'] = ['xclip', '-o', '-selection', 'clipboard']
+ let s:copy['*'] = ['xclip', '-quiet', '-i', '-selection', 'primary']
+ let s:paste['*'] = ['xclip', '-o', '-selection', 'primary']
+ return 'xclip'
elseif executable('lemonade')
let s:copy['+'] = ['lemonade', 'copy']
let s:paste['+'] = ['lemonade', 'paste']
@@ -139,7 +145,12 @@ function! provider#clipboard#Executable() abort
let s:paste['*'] = s:paste['+']
return 'termux-clipboard'
elseif !empty($TMUX) && executable('tmux')
- let s:copy['+'] = ['tmux', 'load-buffer', '-']
+ let ver = matchlist(systemlist(['tmux', '-V'])[0], '\vtmux %(next-)?(\d+)\.(\d+)')
+ if len(ver) >= 3 && (ver[1] > 3 || (ver[1] == 3 && ver[2] >= 2))
+ let s:copy['+'] = ['tmux', 'load-buffer', '-w', '-']
+ else
+ let s:copy['+'] = ['tmux', 'load-buffer', '-']
+ endif
let s:paste['+'] = ['tmux', 'save-buffer', '-']
let s:copy['*'] = s:copy['+']
let s:paste['*'] = s:paste['+']
diff --git a/runtime/autoload/provider/node.vim b/runtime/autoload/provider/node.vim
index 45b1dd4fd7..87af0094fe 100644
--- a/runtime/autoload/provider/node.vim
+++ b/runtime/autoload/provider/node.vim
@@ -71,13 +71,11 @@ function! provider#node#Detect() abort
let yarn_opts = deepcopy(s:NodeHandler)
let yarn_opts.entry_point = '/node_modules/neovim/bin/cli.js'
" `yarn global dir` is slow (> 250ms), try the default path first
- " XXX: The following code is not portable
" https://github.com/yarnpkg/yarn/issues/2049#issuecomment-263183768
- if has('unix')
- let yarn_default_path = $HOME . '/.config/yarn/global/' . yarn_opts.entry_point
- if filereadable(yarn_default_path)
- return [yarn_default_path, '']
- endif
+ let yarn_config_dir = has('win32') ? '/AppData/Local/Yarn/Data' : '/.config/yarn'
+ let yarn_default_path = $HOME . yarn_config_dir . '/global/' . yarn_opts.entry_point
+ if filereadable(yarn_default_path)
+ return [yarn_default_path, '']
endif
let yarn_opts.job_id = jobstart('yarn global dir', yarn_opts)
endif
diff --git a/runtime/autoload/python.vim b/runtime/autoload/python.vim
index e45dbd9db8..1eaad09ef5 100644
--- a/runtime/autoload/python.vim
+++ b/runtime/autoload/python.vim
@@ -3,13 +3,19 @@
let s:keepcpo= &cpo
set cpo&vim
-" searchpair() can be slow, limit the time to 150 msec or what is put in
-" g:pyindent_searchpair_timeout
-let s:searchpair_timeout = get(g:, 'pyindent_searchpair_timeout', 150)
-
-" Identing inside parentheses can be very slow, regardless of the searchpair()
-" timeout, so let the user disable this feature if he doesn't need it
-let s:disable_parentheses_indenting = get(g:, 'pyindent_disable_parentheses_indenting', v:false)
+" need to inspect some old g:pyindent_* variables to be backward compatible
+let g:python_indent = extend(get(g:, 'python_indent', {}), #{
+ \ closed_paren_align_last_line: v:true,
+ \ open_paren: get(g:, 'pyindent_open_paren', 'shiftwidth() * 2'),
+ \ nested_paren: get(g:, 'pyindent_nested_paren', 'shiftwidth()'),
+ \ continue: get(g:, 'pyindent_continue', 'shiftwidth() * 2'),
+ "\ searchpair() can be slow, limit the time to 150 msec or what is put in
+ "\ g:python_indent.searchpair_timeout
+ \ searchpair_timeout: get(g:, 'pyindent_searchpair_timeout', 150),
+ "\ Identing inside parentheses can be very slow, regardless of the searchpair()
+ "\ timeout, so let the user disable this feature if he doesn't need it
+ \ disable_parentheses_indenting: get(g:, 'pyindent_disable_parentheses_indenting', v:false),
+ \ }, 'keep')
let s:maxoff = 50 " maximum number of lines to look backwards for ()
@@ -18,7 +24,7 @@ function s:SearchBracket(fromlnum, flags)
\ {-> synstack('.', col('.'))
\ ->map({_, id -> id->synIDattr('name')})
\ ->match('\%(Comment\|Todo\|String\)$') >= 0},
- \ [0, a:fromlnum - s:maxoff]->max(), s:searchpair_timeout)
+ \ [0, a:fromlnum - s:maxoff]->max(), g:python_indent.searchpair_timeout)
endfunction
" See if the specified line is already user-dedented from the expected value.
@@ -38,7 +44,7 @@ function python#GetIndent(lnum, ...)
if a:lnum > 1 && getline(a:lnum - 2) =~ '\\$'
return indent(a:lnum - 1)
endif
- return indent(a:lnum - 1) + (exists("g:pyindent_continue") ? eval(g:pyindent_continue) : (shiftwidth() * 2))
+ return indent(a:lnum - 1) + get(g:, 'pyindent_continue', g:python_indent.continue)->eval()
endif
" If the start of the line is in a string don't change the indent.
@@ -55,7 +61,7 @@ function python#GetIndent(lnum, ...)
return 0
endif
- if s:disable_parentheses_indenting == 1
+ if g:python_indent.disable_parentheses_indenting == 1
let plindent = indent(plnum)
let plnumstart = plnum
else
@@ -70,8 +76,12 @@ function python#GetIndent(lnum, ...)
" 100, 200, 300, 400)
call cursor(a:lnum, 1)
let [parlnum, parcol] = s:SearchBracket(a:lnum, 'nbW')
- if parlnum > 0 && parcol != col([parlnum, '$']) - 1
- return parcol
+ if parlnum > 0
+ if parcol != col([parlnum, '$']) - 1
+ return parcol
+ elseif getline(a:lnum) =~ '^\s*[])}]' && !g:python_indent.closed_paren_align_last_line
+ return indent(parlnum)
+ endif
endif
call cursor(plnum, 1)
@@ -123,9 +133,11 @@ function python#GetIndent(lnum, ...)
" When the start is inside parenthesis, only indent one 'shiftwidth'.
let [pp, _] = s:SearchBracket(a:lnum, 'bW')
if pp > 0
- return indent(plnum) + (exists("g:pyindent_nested_paren") ? eval(g:pyindent_nested_paren) : shiftwidth())
+ return indent(plnum)
+ \ + get(g:, 'pyindent_nested_paren', g:python_indent.nested_paren)->eval()
endif
- return indent(plnum) + (exists("g:pyindent_open_paren") ? eval(g:pyindent_open_paren) : (shiftwidth() * 2))
+ return indent(plnum)
+ \ + get(g:, 'pyindent_open_paren', g:python_indent.open_paren)->eval()
endif
if plnumstart == p
return indent(plnum)
diff --git a/runtime/autoload/tohtml.vim b/runtime/autoload/tohtml.vim
index 66f1cb46cb..4ae17815ba 100644
--- a/runtime/autoload/tohtml.vim
+++ b/runtime/autoload/tohtml.vim
@@ -712,6 +712,9 @@ func! tohtml#GetUserSettings() "{{{
call tohtml#GetOption(user_settings, 'no_foldcolumn', user_settings.ignore_folding)
call tohtml#GetOption(user_settings, 'hover_unfold', 0 )
call tohtml#GetOption(user_settings, 'no_pre', 0 )
+ call tohtml#GetOption(user_settings, 'no_doc', 0 )
+ call tohtml#GetOption(user_settings, 'no_links', 0 )
+ call tohtml#GetOption(user_settings, 'no_modeline', 0 )
call tohtml#GetOption(user_settings, 'no_invalid', 0 )
call tohtml#GetOption(user_settings, 'whole_filler', 0 )
call tohtml#GetOption(user_settings, 'use_xhtml', 0 )
@@ -752,7 +755,7 @@ func! tohtml#GetUserSettings() "{{{
" pre_wrap doesn't do anything if not using pre or not using CSS
if user_settings.no_pre || !user_settings.use_css
- let user_settings.pre_wrap=0
+ let user_settings.pre_wrap = 0
endif
"}}}
diff --git a/runtime/autoload/tutor.vim b/runtime/autoload/tutor.vim
index abf5c5e2c8..4da4213826 100644
--- a/runtime/autoload/tutor.vim
+++ b/runtime/autoload/tutor.vim
@@ -104,7 +104,7 @@ function! tutor#CheckLine(line)
if exists('b:tutor_metadata') && has_key(b:tutor_metadata, 'expect')
let bufn = bufnr('%')
let ctext = getline(a:line)
- let signs = sign_getplaced('.', {'lnum': a:line})[0].signs
+ let signs = sign_getplaced(bufn, {'lnum': a:line})[0].signs
if !empty(signs)
call sign_unplace('', {'id': signs[0].id})
endif
diff --git a/runtime/autoload/zig/fmt.vim b/runtime/autoload/zig/fmt.vim
new file mode 100644
index 0000000000..b78c1994dd
--- /dev/null
+++ b/runtime/autoload/zig/fmt.vim
@@ -0,0 +1,100 @@
+" Adapted from fatih/vim-go: autoload/go/fmt.vim
+"
+" Copyright 2011 The Go Authors. All rights reserved.
+" Use of this source code is governed by a BSD-style
+" license that can be found in the LICENSE file.
+"
+" Upstream: https://github.com/ziglang/zig.vim
+
+function! zig#fmt#Format() abort
+ " Save cursor position and many other things.
+ let view = winsaveview()
+
+ if !executable('zig')
+ echohl Error | echomsg "no zig binary found in PATH" | echohl None
+ return
+ endif
+
+ let cmdline = 'zig fmt --stdin --ast-check'
+ let current_buf = bufnr('')
+
+ " The formatted code is output on stdout, the errors go on stderr.
+ if exists('*systemlist')
+ silent let out = systemlist(cmdline, current_buf)
+ else
+ silent let out = split(system(cmdline, current_buf))
+ endif
+ if len(out) == 1
+ if out[0] == "error: unrecognized parameter: '--ast-check'"
+ let cmdline = 'zig fmt --stdin'
+ if exists('*systemlist')
+ silent let out = systemlist(cmdline, current_buf)
+ else
+ silent let out = split(system(cmdline, current_buf))
+ endif
+ endif
+ endif
+ let err = v:shell_error
+
+
+ if err == 0
+ " remove undo point caused via BufWritePre.
+ try | silent undojoin | catch | endtry
+
+ " Replace the file content with the formatted version.
+ if exists('*deletebufline')
+ call deletebufline(current_buf, len(out), line('$'))
+ else
+ silent execute ':' . len(out) . ',' . line('$') . ' delete _'
+ endif
+ call setline(1, out)
+
+ " No errors detected, close the loclist.
+ call setloclist(0, [], 'r')
+ lclose
+ elseif get(g:, 'zig_fmt_parse_errors', 1)
+ let errors = s:parse_errors(expand('%'), out)
+
+ call setloclist(0, [], 'r', {
+ \ 'title': 'Errors',
+ \ 'items': errors,
+ \ })
+
+ let max_win_height = get(g:, 'zig_fmt_max_window_height', 5)
+ " Prevent the loclist from becoming too long.
+ let win_height = min([max_win_height, len(errors)])
+ " Open the loclist, but only if there's at least one error to show.
+ execute 'silent! lwindow ' . win_height
+ endif
+
+ call winrestview(view)
+
+ if err != 0
+ echohl Error | echomsg "zig fmt returned error" | echohl None
+ return
+ endif
+
+ " Run the syntax highlighter on the updated content and recompute the folds if
+ " needed.
+ syntax sync fromstart
+endfunction
+
+" parse_errors parses the given errors and returns a list of parsed errors
+function! s:parse_errors(filename, lines) abort
+ " list of errors to be put into location list
+ let errors = []
+ for line in a:lines
+ let tokens = matchlist(line, '^\(.\{-}\):\(\d\+\):\(\d\+\)\s*\(.*\)')
+ if !empty(tokens)
+ call add(errors,{
+ \"filename": a:filename,
+ \"lnum": tokens[2],
+ \"col": tokens[3],
+ \"text": tokens[4],
+ \ })
+ endif
+ endfor
+
+ return errors
+endfunction
+" vim: sw=2 ts=2 et
diff --git a/runtime/colors/blue.vim b/runtime/colors/blue.vim
index 0d2f07c654..aa99bacd3b 100644
--- a/runtime/colors/blue.vim
+++ b/runtime/colors/blue.vim
@@ -4,7 +4,7 @@
" Maintainer: Original maintainer Steven Vertigan <steven@vertigan.wattle.id.au>
" Website: https://github.com/vim/colorschemes
" License: Same as Vim
-" Last Updated: Fri Aug 5 12:25:12 2022
+" Last Updated: Fri 02 Sep 2022 09:41:44 MSK
" Generated by Colortemplate v2.2.0
@@ -13,10 +13,14 @@ set background=dark
hi clear
let g:colors_name = 'blue'
-let s:t_Co = exists('&t_Co') ? (&t_Co ? &t_Co : 0) : -1
+let s:t_Co = &t_Co
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']
+ " Nvim uses g:terminal_color_{0-15} instead
+ for i in range(g:terminal_ansi_colors->len())
+ let g:terminal_color_{i} = g:terminal_ansi_colors[i]
+ endfor
endif
hi Normal guifg=#ffd700 guibg=#000087 gui=NONE cterm=NONE
hi CursorLine guifg=NONE guibg=#005faf gui=NONE cterm=NONE
@@ -57,7 +61,7 @@ hi ToolbarLine guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NO
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 WarningMsg guifg=#d787d7 guibg=NONE gui=NONE cterm=NONE
hi WildMenu guifg=#000087 guibg=#ffd700 gui=NONE cterm=NONE
hi debugBreakpoint guifg=#00ff00 guibg=#000087 gui=reverse cterm=reverse
hi debugPC guifg=#5fffff guibg=#000087 gui=reverse cterm=reverse
@@ -120,6 +124,8 @@ hi! link Structure Type
hi! link Tag Special
hi! link Typedef Type
hi! link Terminal Normal
+hi! link MessageWindow Pmenu
+hi! link PopupNotification Todo
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
@@ -165,7 +171,7 @@ if s:t_Co >= 256
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 WarningMsg ctermfg=176 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
@@ -228,6 +234,8 @@ if s:t_Co >= 256
hi! link Tag Special
hi! link Typedef Type
hi! link Terminal Normal
+ hi! link MessageWindow Pmenu
+ hi! link PopupNotification Todo
hi DiffAdd ctermfg=231 ctermbg=65 cterm=NONE
hi DiffChange ctermfg=231 ctermbg=67 cterm=NONE
hi DiffText ctermfg=16 ctermbg=251 cterm=NONE
@@ -276,7 +284,7 @@ if s:t_Co >= 16
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 WarningMsg ctermfg=magenta 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
@@ -339,6 +347,8 @@ if s:t_Co >= 16
hi! link Tag Special
hi! link Typedef Type
hi! link Terminal Normal
+ hi! link MessageWindow Pmenu
+ hi! link PopupNotification Todo
hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
hi DiffChange ctermfg=white ctermbg=blue cterm=NONE
hi DiffText ctermfg=black ctermbg=grey cterm=NONE
@@ -449,6 +459,8 @@ if s:t_Co >= 8
hi! link Tag Special
hi! link Typedef Type
hi! link Terminal Normal
+ hi! link MessageWindow Pmenu
+ hi! link PopupNotification Todo
hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
hi DiffChange ctermfg=white ctermbg=darkblue cterm=NONE
hi DiffText ctermfg=black ctermbg=grey cterm=NONE
diff --git a/runtime/colors/darkblue.vim b/runtime/colors/darkblue.vim
index b4c286af37..c7bba4471e 100644
--- a/runtime/colors/darkblue.vim
+++ b/runtime/colors/darkblue.vim
@@ -4,7 +4,7 @@
" Maintainer: Original author Bohdan Vlasyuk <bohdan@vstu.edu.ua>
" Website: https://github.com/vim/colorschemes
" License: Same as Vim
-" Last Updated: Mon Aug 8 15:21:06 2022
+" Last Updated: Fri 02 Sep 2022 09:40:36 MSK
" Generated by Colortemplate v2.2.0
@@ -13,10 +13,14 @@ set background=dark
hi clear
let g:colors_name = 'darkblue'
-let s:t_Co = exists('&t_Co') ? (&t_Co ? &t_Co : 0) : -1
+let s:t_Co = &t_Co
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']
+ " Nvim uses g:terminal_color_{0-15} instead
+ for i in range(g:terminal_ansi_colors->len())
+ let g:terminal_color_{i} = g:terminal_ansi_colors[i]
+ endfor
endif
hi! link Terminal Normal
hi! link CursorColumn CursorLine
@@ -65,6 +69,8 @@ hi! link diffCommon WarningMsg
hi! link diffBDiffer WarningMsg
hi! link lCursor Cursor
hi! link CurSearch Search
+hi! link MessageWindow Pmenu
+hi! link PopupNotification Todo
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
@@ -171,6 +177,8 @@ if s:t_Co >= 256
hi! link diffBDiffer WarningMsg
hi! link lCursor Cursor
hi! link CurSearch Search
+ hi! link MessageWindow Pmenu
+ hi! link PopupNotification Todo
hi Normal ctermfg=252 ctermbg=17 cterm=NONE
hi Conceal ctermfg=NONE ctermbg=NONE cterm=NONE
hi ColorColumn ctermfg=252 ctermbg=88 cterm=NONE
diff --git a/runtime/colors/delek.vim b/runtime/colors/delek.vim
index bd9c5cf52d..d9db90f2c5 100644
--- a/runtime/colors/delek.vim
+++ b/runtime/colors/delek.vim
@@ -4,7 +4,7 @@
" Maintainer: Original maintainer David Schweikert <david@schweikert.ch>
" Website: https://github.com/vim/colorschemes
" License: Same as Vim
-" Last Updated: Mon Aug 8 15:21:07 2022
+" Last Updated: Sun 04 Sep 2022 09:31:26 MSK
" Generated by Colortemplate v2.2.0
@@ -13,10 +13,14 @@ set background=light
hi clear
let g:colors_name = 'delek'
-let s:t_Co = exists('&t_Co') ? (&t_Co ? &t_Co : 0) : -1
+let s:t_Co = &t_Co
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']
+ " Nvim uses g:terminal_color_{0-15} instead
+ for i in range(g:terminal_ansi_colors->len())
+ let g:terminal_color_{i} = g:terminal_ansi_colors[i]
+ endfor
endif
hi! link Terminal Normal
hi! link LineNrAbove LineNr
@@ -25,6 +29,8 @@ hi! link CurSearch Search
hi! link CursorLineFold CursorLine
hi! link CursorLineSign CursorLine
hi! link ErrorMsg Error
+hi! link MessageWindow Pmenu
+hi! link PopupNotification Todo
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
@@ -57,7 +63,7 @@ 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 Question guifg=#008700 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
@@ -97,6 +103,8 @@ if s:t_Co >= 256
hi! link CursorLineFold CursorLine
hi! link CursorLineSign CursorLine
hi! link ErrorMsg Error
+ hi! link MessageWindow Pmenu
+ hi! link PopupNotification Todo
hi Normal ctermfg=16 ctermbg=231 cterm=NONE
hi EndOfBuffer ctermfg=250 ctermbg=NONE cterm=NONE
hi StatusLine ctermfg=226 ctermbg=18 cterm=bold
@@ -129,7 +137,7 @@ if s:t_Co >= 256
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 Question ctermfg=28 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
diff --git a/runtime/colors/desert.vim b/runtime/colors/desert.vim
index e9b8108d19..0b56740664 100644
--- a/runtime/colors/desert.vim
+++ b/runtime/colors/desert.vim
@@ -4,7 +4,7 @@
" Maintainer: Original maintainer Hans Fugal <hans@fugal.net>
" Website: https://github.com/vim/colorschemes
" License: Same as Vim
-" Last Updated: Mon Aug 8 15:21:08 2022
+" Last Updated: Fri 02 Sep 2022 09:39:21 MSK
" Generated by Colortemplate v2.2.0
@@ -13,10 +13,14 @@ set background=dark
hi clear
let g:colors_name = 'desert'
-let s:t_Co = exists('&t_Co') ? (&t_Co ? &t_Co : 0) : -1
+let s:t_Co = &t_Co
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']
+ " Nvim uses g:terminal_color_{0-15} instead
+ for i in range(g:terminal_ansi_colors->len())
+ let g:terminal_color_{i} = g:terminal_ansi_colors[i]
+ endfor
endif
hi! link Terminal Normal
hi! link LineNrAbove LineNr
@@ -25,6 +29,8 @@ hi! link CurSearch Search
hi! link CursorLineFold CursorLine
hi! link CursorLineSign CursorLine
hi! link EndOfBuffer NonText
+hi! link MessageWindow Pmenu
+hi! link PopupNotification Todo
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
@@ -97,6 +103,8 @@ if s:t_Co >= 256
hi! link CursorLineFold CursorLine
hi! link CursorLineSign CursorLine
hi! link EndOfBuffer NonText
+ hi! link MessageWindow Pmenu
+ hi! link PopupNotification Todo
hi Normal ctermfg=231 ctermbg=236 cterm=NONE
hi StatusLine ctermfg=236 ctermbg=144 cterm=NONE
hi StatusLineNC ctermfg=242 ctermbg=144 cterm=NONE
diff --git a/runtime/colors/elflord.vim b/runtime/colors/elflord.vim
index 4bfda2a083..4a33e33eec 100644
--- a/runtime/colors/elflord.vim
+++ b/runtime/colors/elflord.vim
@@ -3,7 +3,7 @@
" Maintainer: original maintainer Ron Aaron <ron@ronware.org>
" Website: https://www.github.com/vim/colorschemes
" License: Same as Vim
-" Last Updated: Mon Aug 8 15:21:08 2022
+" Last Updated: Fri 02 Sep 2022 09:44:22 MSK
" Generated by Colortemplate v2.2.0
@@ -12,7 +12,7 @@ set background=dark
hi clear
let g:colors_name = 'elflord'
-let s:t_Co = exists('&t_Co') ? (&t_Co ? &t_Co : 0) : -1
+let s:t_Co = &t_Co
hi! link Terminal Normal
hi! link Boolean Constant
@@ -43,9 +43,15 @@ hi! link lCursor Cursor
hi! link CurSearch Search
hi! link CursorLineFold CursorLine
hi! link CursorLineSign CursorLine
+hi! link MessageWindow Pmenu
+hi! link PopupNotification Todo
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']
+ " Nvim uses g:terminal_color_{0-15} instead
+ for i in range(g:terminal_ansi_colors->len())
+ let g:terminal_color_{i} = g:terminal_ansi_colors[i]
+ endfor
endif
hi Normal guifg=#00ffff guibg=#000000 gui=NONE cterm=NONE
hi QuickFixLine guifg=#ffffff guibg=#2e8b57 gui=NONE cterm=NONE
diff --git a/runtime/colors/evening.vim b/runtime/colors/evening.vim
index 7e0609e9c4..70ae55aa8d 100644
--- a/runtime/colors/evening.vim
+++ b/runtime/colors/evening.vim
@@ -4,7 +4,7 @@
" Maintainer: Original maintainer Steven Vertigan <steven@vertigan.wattle.id.au>
" Website: https://github.com/vim/colorschemes
" License: Same as Vim
-" Last Updated: Mon Aug 8 15:21:09 2022
+" Last Updated: Sun 04 Sep 2022 09:48:34 MSK
" Generated by Colortemplate v2.2.0
@@ -13,10 +13,14 @@ set background=dark
hi clear
let g:colors_name = 'evening'
-let s:t_Co = exists('&t_Co') ? (&t_Co ? &t_Co : 0) : -1
+let s:t_Co = &t_Co
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']
+ let g:terminal_ansi_colors = ['#000000', '#cd0000', '#00cd00', '#cdcd00', '#0087ff', '#cd00cd', '#00cdcd', '#e5e5e5', '#7f7f7f', '#ff0000', '#00ff00', '#ffff00', '#5c5cff', '#ff00ff', '#00ffff', '#ffffff']
+ " Nvim uses g:terminal_color_{0-15} instead
+ for i in range(g:terminal_ansi_colors->len())
+ let g:terminal_color_{i} = g:terminal_ansi_colors[i]
+ endfor
endif
hi! link VertSplit StatusLineNC
hi! link StatusLineTerm StatusLine
@@ -64,6 +68,8 @@ hi! link String Constant
hi! link Structure Type
hi! link Tag Special
hi! link Typedef Type
+hi! link MessageWindow Pmenu
+hi! link PopupNotification Todo
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
@@ -98,7 +104,7 @@ 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 WarningMsg guifg=#ff0000 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
@@ -170,6 +176,8 @@ if s:t_Co >= 256
hi! link Structure Type
hi! link Tag Special
hi! link Typedef Type
+ hi! link MessageWindow Pmenu
+ hi! link PopupNotification Todo
hi Normal ctermfg=231 ctermbg=236 cterm=NONE
hi ColorColumn ctermfg=NONE ctermbg=88 cterm=NONE
hi CursorLine ctermfg=NONE ctermbg=241 cterm=NONE
@@ -204,7 +212,7 @@ if s:t_Co >= 256
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 WarningMsg ctermfg=196 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
@@ -279,6 +287,8 @@ if s:t_Co >= 16
hi! link Structure Type
hi! link Tag Special
hi! link Typedef Type
+ hi! link MessageWindow Pmenu
+ hi! link PopupNotification Todo
hi Normal ctermfg=white ctermbg=black cterm=NONE
hi ColorColumn ctermfg=white ctermbg=darkred cterm=NONE
hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
@@ -313,7 +323,7 @@ if s:t_Co >= 16
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 WarningMsg ctermfg=red 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
@@ -494,13 +504,26 @@ endif
" 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: x_black #000000 16 black
+" Color: x_darkred #cd0000 160 darkred
+" Color: x_darkgreen #00cd00 40 darkgreen
+" Color: x_darkyellow #cdcd00 184 darkyellow
+" Color: x_darkblue_m #0087ff 33 darkblue
+" Color: x_darkmagenta #cd00cd 164 darkmagenta
+" Color: x_darkcyan #00cdcd 44 darkcyan
+" Color: x_gray #e5e5e5 254 gray
+" Color: x_darkgray #7f7f7f 244 darkgray
+" Color: x_red #ff0000 196 red
+" Color: x_green #00ff00 46 green
+" Color: x_yellow #ffff00 226 yellow
+" Color: x_blue #5c5cff 63 blue
+" Color: x_magenta #ff00ff 201 magenta
+" Color: x_cyan #00ffff 51 cyan
+" Color: x_white #ffffff 231 white
+" Term colors: x_black x_darkred x_darkgreen x_darkyellow x_darkblue_m x_darkmagenta x_darkcyan x_gray
+" Term colors: x_darkgray x_red x_green x_yellow x_blue x_magenta x_cyan x_white
" Color: bgDiffA #5F875F 65 darkgreen
" Color: bgDiffC #5F87AF 67 blue
" Color: bgDiffD #AF5FAF 133 magenta
diff --git a/runtime/colors/habamax.vim b/runtime/colors/habamax.vim
index 169fc28021..f6aa5609b1 100644
--- a/runtime/colors/habamax.vim
+++ b/runtime/colors/habamax.vim
@@ -4,7 +4,7 @@
" Maintainer: Maxim Kim <habamax@gmail.com>
" Website: https://github.com/vim/colorschemes
" License: Same as Vim
-" Last Updated: Mon Aug 8 15:21:10 2022
+" Last Updated: Fri 02 Sep 2022 09:45:11 MSK
" Generated by Colortemplate v2.2.0
@@ -13,14 +13,20 @@ set background=dark
hi clear
let g:colors_name = 'habamax'
-let s:t_Co = exists('&t_Co') ? (&t_Co ? &t_Co : 0) : -1
+let s:t_Co = &t_Co
if (has('termguicolors') && &termguicolors) || has('gui_running')
let g:terminal_ansi_colors = ['#1c1c1c', '#d75f5f', '#87af87', '#afaf87', '#5f87af', '#af87af', '#5f8787', '#9e9e9e', '#767676', '#d7875f', '#afd7af', '#d7d787', '#87afd7', '#d7afd7', '#87afaf', '#bcbcbc']
+ " Nvim uses g:terminal_color_{0-15} instead
+ for i in range(g:terminal_ansi_colors->len())
+ let g:terminal_color_{i} = g:terminal_ansi_colors[i]
+ endfor
endif
hi! link Terminal Normal
hi! link StatuslineTerm Statusline
hi! link StatuslineTermNC StatuslineNC
+hi! link MessageWindow Pmenu
+hi! link PopupNotification Todo
hi! link javaScriptFunction Statement
hi! link javaScriptIdentifier Statement
hi! link sqlKeyword Statement
@@ -142,6 +148,8 @@ if s:t_Co >= 256
hi! link Terminal Normal
hi! link StatuslineTerm Statusline
hi! link StatuslineTermNC StatuslineNC
+ hi! link MessageWindow Pmenu
+ hi! link PopupNotification Todo
hi! link javaScriptFunction Statement
hi! link javaScriptIdentifier Statement
hi! link sqlKeyword Statement
diff --git a/runtime/colors/industry.vim b/runtime/colors/industry.vim
index 359600fa4a..f09786000d 100644
--- a/runtime/colors/industry.vim
+++ b/runtime/colors/industry.vim
@@ -4,7 +4,7 @@
" Maintainer: Original maintainer Shian Lee.
" Website: https://github.com/vim/colorschemes
" License: Same as Vim
-" Last Updated: Mon Aug 8 15:21:11 2022
+" Last Updated: Sun 04 Sep 2022 09:50:04 MSK
" Generated by Colortemplate v2.2.0
@@ -13,10 +13,14 @@ set background=dark
hi clear
let g:colors_name = 'industry'
-let s:t_Co = exists('&t_Co') ? (&t_Co ? &t_Co : 0) : -1
+let s:t_Co = &t_Co
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']
+ " Nvim uses g:terminal_color_{0-15} instead
+ for i in range(g:terminal_ansi_colors->len())
+ let g:terminal_color_{i} = g:terminal_ansi_colors[i]
+ endfor
endif
hi Normal guifg=#dadada guibg=#000000 gui=NONE cterm=NONE
hi EndOfBuffer guifg=#444444 guibg=#000000 gui=NONE cterm=NONE
@@ -51,7 +55,7 @@ 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 WarningMsg guifg=#ff0000 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
@@ -84,6 +88,8 @@ hi! link LineNrBelow LineNr
hi! link CurSearch Search
hi! link CursorLineFold CursorLine
hi! link CursorLineSign CursorLine
+hi! link MessageWindow Pmenu
+hi! link PopupNotification Todo
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
@@ -123,7 +129,7 @@ if s:t_Co >= 256
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 WarningMsg ctermfg=196 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
@@ -156,6 +162,8 @@ if s:t_Co >= 256
hi! link CurSearch Search
hi! link CursorLineFold CursorLine
hi! link CursorLineSign CursorLine
+ hi! link MessageWindow Pmenu
+ hi! link PopupNotification Todo
hi DiffAdd ctermfg=231 ctermbg=65 cterm=NONE
hi DiffChange ctermfg=231 ctermbg=67 cterm=NONE
hi DiffText ctermfg=16 ctermbg=251 cterm=NONE
@@ -198,7 +206,7 @@ if s:t_Co >= 16
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 WarningMsg ctermfg=red 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
@@ -231,6 +239,8 @@ if s:t_Co >= 16
hi! link CurSearch Search
hi! link CursorLineFold CursorLine
hi! link CursorLineSign CursorLine
+ hi! link MessageWindow Pmenu
+ hi! link PopupNotification Todo
hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
hi DiffChange ctermfg=white ctermbg=blue cterm=NONE
hi DiffText ctermfg=black ctermbg=grey cterm=NONE
diff --git a/runtime/colors/koehler.vim b/runtime/colors/koehler.vim
index 04476d1ee9..67719123a2 100644
--- a/runtime/colors/koehler.vim
+++ b/runtime/colors/koehler.vim
@@ -3,7 +3,7 @@
" Maintainer: original maintainer Ron Aaron <ron@ronware.org>
" Website: https://www.github.com/vim/colorschemes
" License: Same as Vim
-" Last Updated: Mon Aug 8 15:21:12 2022
+" Last Updated: Fri 02 Sep 2022 09:23:56 MSK
" Generated by Colortemplate v2.2.0
@@ -12,7 +12,7 @@ set background=dark
hi clear
let g:colors_name = 'koehler'
-let s:t_Co = exists('&t_Co') ? (&t_Co ? &t_Co : 0) : -1
+let s:t_Co = &t_Co
hi! link Terminal Normal
hi! link Boolean Constant
@@ -49,9 +49,15 @@ hi! link lCursor Cursor
hi! link CurSearch Search
hi! link CursorLineFold CursorLine
hi! link CursorLineSign CursorLine
+hi! link MessageWindow Pmenu
+hi! link PopupNotification Todo
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']
+ " Nvim uses g:terminal_color_{0-15} instead
+ for i in range(g:terminal_ansi_colors->len())
+ let g:terminal_color_{i} = g:terminal_ansi_colors[i]
+ endfor
endif
hi Normal guifg=#ffffff guibg=#000000 gui=NONE cterm=NONE
hi ColorColumn guifg=NONE guibg=#8b0000 gui=NONE cterm=NONE
diff --git a/runtime/colors/lunaperche.vim b/runtime/colors/lunaperche.vim
index d6c73eb28c..2954f622aa 100644
--- a/runtime/colors/lunaperche.vim
+++ b/runtime/colors/lunaperche.vim
@@ -4,14 +4,14 @@
" Maintainer: Maxim Kim <habamax@gmail.com>
" Website: https://www.github.com/vim/colorschemes
" License: Vim License (see `:help license`)
-" Last Updated: Thu Aug 18 14:36:32 2022
+" Last Updated: Fri 16 Sep 2022 13:15:33 MSK
" Generated by Colortemplate v2.2.0
hi clear
let g:colors_name = 'lunaperche'
-let s:t_Co = exists('&t_Co') ? (&t_Co ? &t_Co : 0) : -1
+let s:t_Co = &t_Co
hi! link helpVim Title
hi! link helpHeader Title
@@ -26,12 +26,15 @@ hi! link fugitiveUnstagedModifier PreProc
hi! link fugitiveHash Constant
hi! link diffFile PreProc
hi! link markdownHeadingDelimiter Special
-hi! link rstSectionDelimiter PreProc
-hi! link rstDirective Special
+hi! link rstSectionDelimiter Statement
+hi! link rstDirective PreProc
hi! link rstHyperlinkReference Special
-hi! link rstFieldName Special
+hi! link rstFieldName Constant
hi! link rstDelimiter Special
hi! link rstInterpretedText Special
+hi! link rstCodeBlock Normal
+hi! link rstLiteralBlock rstCodeBlock
+hi! link markdownUrl String
hi! link colortemplateKey Statement
hi! link xmlTagName Statement
hi! link javaScriptFunction Statement
@@ -81,25 +84,56 @@ hi! link shOption Normal
hi! link shCommandSub Normal
hi! link shDerefPattern shQuote
hi! link shDerefOp Special
+hi! link phpStorageClass Statement
+hi! link phpStructure Statement
+hi! link phpInclude Statement
+hi! link phpDefine Statement
+hi! link phpSpecialFunction Normal
+hi! link phpParent Normal
+hi! link phpComparison Normal
+hi! link phpOperator Normal
+hi! link phpVarSelector Special
+hi! link phpMemberSelector Special
+hi! link phpDocCustomTags phpDocTags
+hi! link javaExternal Statement
+hi! link javaType Statement
+hi! link javaScopeDecl Statement
+hi! link javaClassDecl Statement
+hi! link javaStorageClass Statement
+hi! link javaDocParam PreProc
+hi! link csStorage Statement
+hi! link csAccessModifier Statement
+hi! link csClass Statement
+hi! link csModifier Statement
+hi! link csAsyncModifier Statement
+hi! link csLogicSymbols Normal
+hi! link csClassType Normal
+hi! link csType Statement
hi! link Terminal Normal
hi! link StatuslineTerm Statusline
hi! link StatuslineTermNC StatuslineNC
hi! link LineNrAbove LineNr
hi! link LineNrBelow LineNr
+hi! link MessageWindow PMenu
+hi! link PopupNotification Todo
if &background ==# 'dark'
if (has('termguicolors') && &termguicolors) || has('gui_running')
- let g:terminal_ansi_colors = ['#000000', '#af5f5f', '#5faf5f', '#af875f', '#5f87af', '#d787af', '#5fafaf', '#c6c6c6', '#767676', '#ff5f5f', '#5fd75f', '#ffd787', '#87afd7', '#ffafd7', '#5fd7d7', '#ffffff']
+ let g:terminal_ansi_colors = ['#000000', '#af5f5f', '#5faf5f', '#af875f', '#5f87af', '#d787d7', '#5fafaf', '#c6c6c6', '#767676', '#ff5f5f', '#5fd75f', '#ffd787', '#5fafff', '#ff87ff', '#5fd7d7', '#ffffff']
+ " Nvim uses g:terminal_color_{0-15} instead
+ for i in range(g:terminal_ansi_colors->len())
+ let g:terminal_color_{i} = g:terminal_ansi_colors[i]
+ endfor
endif
hi Normal guifg=#c6c6c6 guibg=#000000 gui=NONE cterm=NONE
- hi Statusline guifg=#000000 guibg=#c6c6c6 gui=bold cterm=bold
- hi StatuslineNC guifg=#000000 guibg=#767676 gui=NONE cterm=NONE
+ hi Statusline guifg=#c6c6c6 guibg=#000000 gui=bold,reverse cterm=bold,reverse
+ hi StatuslineNC guifg=#767676 guibg=#000000 gui=reverse cterm=reverse
hi VertSplit guifg=#767676 guibg=#767676 gui=NONE cterm=NONE
hi TabLine guifg=#000000 guibg=#c6c6c6 gui=NONE cterm=NONE
hi TabLineFill guifg=NONE guibg=#767676 gui=NONE cterm=NONE
hi TabLineSel guifg=#ffffff guibg=#000000 gui=bold cterm=bold
hi ToolbarLine guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
hi ToolbarButton guifg=#000000 guibg=#ffffff gui=NONE cterm=NONE
- hi QuickFixLine guifg=#000000 guibg=#87afd7 gui=NONE cterm=NONE
+ hi QuickFixLine guifg=#000000 guibg=#5fafff gui=NONE cterm=NONE
hi CursorLineNr guifg=#ffffff guibg=NONE gui=bold cterm=bold
hi LineNr guifg=#585858 guibg=NONE gui=NONE cterm=NONE
hi NonText guifg=#585858 guibg=NONE gui=NONE cterm=NONE
@@ -107,7 +141,7 @@ if &background ==# 'dark'
hi EndOfBuffer guifg=#585858 guibg=NONE gui=NONE cterm=NONE
hi SpecialKey guifg=#585858 guibg=NONE gui=NONE cterm=NONE
hi Pmenu guifg=NONE guibg=#1c1c1c gui=NONE cterm=NONE
- hi PmenuSel guifg=NONE guibg=#005f00 gui=NONE cterm=NONE
+ hi PmenuSel guifg=NONE guibg=#444444 gui=NONE cterm=NONE
hi PmenuThumb guifg=NONE guibg=#c6c6c6 gui=NONE cterm=NONE
hi PmenuSbar guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
hi SignColumn guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
@@ -115,7 +149,7 @@ if &background ==# 'dark'
hi ErrorMsg guifg=#ffffff guibg=#ff5f5f gui=NONE cterm=NONE
hi ModeMsg guifg=#ffd787 guibg=NONE gui=reverse cterm=reverse
hi MoreMsg guifg=#5fd75f guibg=NONE gui=NONE cterm=NONE
- hi Question guifg=#ffafd7 guibg=NONE gui=NONE cterm=NONE
+ hi Question guifg=#ff87ff guibg=NONE gui=NONE cterm=NONE
hi WarningMsg guifg=#ff5f5f guibg=NONE gui=NONE cterm=NONE
hi Todo guifg=#5fd7d7 guibg=#000000 gui=reverse cterm=reverse
hi Search guifg=#000000 guibg=#ffd787 gui=NONE cterm=NONE
@@ -124,7 +158,7 @@ if &background ==# 'dark'
hi WildMenu guifg=#000000 guibg=#ffd787 gui=bold cterm=bold
hi debugPC guifg=#5f87af guibg=NONE gui=reverse cterm=reverse
hi debugBreakpoint guifg=#5fafaf guibg=NONE gui=reverse cterm=reverse
- hi Cursor guifg=#ffffff guibg=#000000 gui=reverse cterm=reverse
+ hi Cursor guifg=NONE guibg=NONE gui=reverse ctermfg=NONE ctermbg=NONE cterm=reverse
hi lCursor guifg=#ff5fff guibg=#000000 gui=reverse cterm=reverse
hi Visual guifg=#ffffff guibg=#005f87 gui=NONE cterm=NONE
hi MatchParen guifg=#c5e7c5 guibg=#000000 gui=reverse cterm=reverse
@@ -136,17 +170,18 @@ if &background ==# 'dark'
hi SpellBad guifg=NONE guibg=NONE guisp=#ff5f5f gui=undercurl ctermfg=NONE ctermbg=NONE cterm=NONE
hi SpellCap guifg=NONE guibg=NONE guisp=#5fafaf gui=undercurl ctermfg=NONE ctermbg=NONE cterm=NONE
hi SpellLocal guifg=NONE guibg=NONE guisp=#5faf5f gui=undercurl ctermfg=NONE ctermbg=NONE cterm=NONE
- hi SpellRare guifg=NONE guibg=NONE guisp=#ffafd7 gui=undercurl ctermfg=NONE ctermbg=NONE cterm=NONE
- hi Comment guifg=#87afd7 guibg=NONE gui=NONE cterm=NONE
- hi Constant guifg=#ffd787 guibg=NONE gui=NONE cterm=NONE
+ hi SpellRare guifg=NONE guibg=NONE guisp=#ff87ff gui=undercurl ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Comment guifg=#5fafff guibg=NONE gui=NONE cterm=NONE
+ hi Constant guifg=#ff87ff guibg=NONE gui=NONE cterm=NONE
+ hi String guifg=#ffd787 guibg=NONE gui=NONE cterm=NONE
hi Identifier guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
- hi Statement guifg=#eeeeee guibg=NONE gui=bold cterm=bold
- hi Type guifg=#5fd75f guibg=NONE gui=bold cterm=bold
- hi PreProc guifg=#af875f guibg=NONE gui=NONE cterm=NONE
+ hi Statement guifg=#e4e4e4 guibg=NONE gui=bold cterm=bold
+ hi Type guifg=#5fd75f guibg=NONE gui=NONE cterm=NONE
+ hi PreProc guifg=#5fd7d7 guibg=NONE gui=NONE cterm=NONE
hi Special guifg=#5fafaf guibg=NONE gui=NONE cterm=NONE
hi Underlined guifg=NONE guibg=NONE gui=underline ctermfg=NONE ctermbg=NONE cterm=underline
hi Title guifg=NONE guibg=NONE gui=bold ctermfg=NONE ctermbg=NONE cterm=bold
- hi Directory guifg=#5fd7d7 guibg=NONE gui=bold cterm=bold
+ hi Directory guifg=#5fafff guibg=NONE gui=bold cterm=bold
hi Conceal guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
hi Ignore guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
hi DiffAdd guifg=#000000 guibg=#af87af gui=NONE cterm=NONE
@@ -155,8 +190,8 @@ if &background ==# 'dark'
hi DiffDelete guifg=#d78787 guibg=NONE gui=NONE cterm=NONE
hi diffAdded guifg=#5fd75f guibg=NONE gui=NONE cterm=NONE
hi diffRemoved guifg=#d78787 guibg=NONE gui=NONE cterm=NONE
- hi diffSubname guifg=#ffafd7 guibg=NONE gui=NONE cterm=NONE
- hi dirType guifg=#d787af guibg=NONE gui=NONE cterm=NONE
+ hi diffSubname guifg=#ff87ff guibg=NONE gui=NONE cterm=NONE
+ hi dirType guifg=#d787d7 guibg=NONE gui=NONE cterm=NONE
hi dirPermissionUser guifg=#5faf5f guibg=NONE gui=NONE cterm=NONE
hi dirPermissionGroup guifg=#af875f guibg=NONE gui=NONE cterm=NONE
hi dirPermissionOther guifg=#5fafaf guibg=NONE gui=NONE cterm=NONE
@@ -164,17 +199,20 @@ if &background ==# 'dark'
hi dirGroup guifg=#767676 guibg=NONE gui=NONE cterm=NONE
hi dirTime guifg=#767676 guibg=NONE gui=NONE cterm=NONE
hi dirSize guifg=#ffd787 guibg=NONE gui=NONE cterm=NONE
- hi dirSizeMod guifg=#d787af guibg=NONE gui=NONE cterm=NONE
+ hi dirSizeMod guifg=#d787d7 guibg=NONE gui=NONE cterm=NONE
hi FilterMenuDirectorySubtle guifg=#878787 guibg=NONE gui=NONE cterm=NONE
hi dirFilterMenuBookmarkPath guifg=#878787 guibg=NONE gui=NONE cterm=NONE
hi dirFilterMenuHistoryPath guifg=#878787 guibg=NONE gui=NONE cterm=NONE
hi FilterMenuLineNr guifg=#878787 guibg=NONE gui=NONE cterm=NONE
- hi CocMenuSel guifg=NONE guibg=#005f00 gui=NONE cterm=NONE
hi CocSearch guifg=#ffd787 guibg=NONE gui=NONE cterm=NONE
else
" Light background
if (has('termguicolors') && &termguicolors) || has('gui_running')
- let g:terminal_ansi_colors = ['#000000', '#870000', '#008700', '#875f00', '#005faf', '#870087', '#005f5f', '#808080', '#767676', '#d70000', '#87d787', '#d7d787', '#0087d7', '#af00af', '#00afaf', '#ffffff']
+ let g:terminal_ansi_colors = ['#000000', '#af0000', '#008700', '#af5f00', '#005fd7', '#af00af', '#005f5f', '#808080', '#767676', '#d70000', '#87d787', '#ffd787', '#0087d7', '#ff00ff', '#008787', '#ffffff']
+ " Nvim uses g:terminal_color_{0-15} instead
+ for i in range(g:terminal_ansi_colors->len())
+ let g:terminal_color_{i} = g:terminal_ansi_colors[i]
+ endfor
endif
hi Normal guifg=#000000 guibg=#ffffff gui=NONE cterm=NONE
hi Statusline guifg=#ffffff guibg=#000000 gui=bold cterm=bold
@@ -193,46 +231,47 @@ else
hi EndOfBuffer guifg=#9e9e9e guibg=NONE gui=NONE cterm=NONE
hi SpecialKey guifg=#9e9e9e guibg=NONE gui=NONE cterm=NONE
hi Pmenu guifg=NONE guibg=#eeeeee gui=NONE cterm=NONE
- hi PmenuSel guifg=NONE guibg=#afd7af gui=NONE cterm=NONE
+ hi PmenuSel guifg=NONE guibg=#c6c6c6 gui=NONE cterm=NONE
hi PmenuThumb guifg=NONE guibg=#767676 gui=NONE cterm=NONE
hi PmenuSbar guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
hi SignColumn guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
hi Error guifg=#ffffff guibg=#d70000 gui=NONE cterm=NONE
hi ErrorMsg guifg=#ffffff guibg=#d70000 gui=NONE cterm=NONE
- hi ModeMsg guifg=#d7d787 guibg=#000000 gui=reverse cterm=reverse
+ hi ModeMsg guifg=#ffd787 guibg=#000000 gui=reverse cterm=reverse
hi MoreMsg guifg=#008700 guibg=NONE gui=bold cterm=bold
- hi Question guifg=#870087 guibg=NONE gui=bold cterm=bold
+ hi Question guifg=#af00af guibg=NONE gui=bold cterm=bold
hi WarningMsg guifg=#d70000 guibg=NONE gui=bold cterm=bold
- hi Todo guifg=#005f5f guibg=#ffffff gui=reverse cterm=reverse
- hi Search guifg=#000000 guibg=#d7d787 gui=NONE cterm=NONE
+ hi Todo guifg=#008787 guibg=#ffffff gui=reverse cterm=reverse
+ hi Search guifg=#000000 guibg=#ffd787 gui=NONE cterm=NONE
hi IncSearch guifg=#000000 guibg=#87d787 gui=NONE cterm=NONE
hi CurSearch guifg=#000000 guibg=#87d787 gui=NONE cterm=NONE
- hi WildMenu guifg=#000000 guibg=#d7d787 gui=bold cterm=bold
- hi debugPC guifg=#005faf guibg=NONE gui=reverse cterm=reverse
+ hi WildMenu guifg=#000000 guibg=#ffd787 gui=bold cterm=bold
+ hi debugPC guifg=#005fd7 guibg=NONE gui=reverse cterm=reverse
hi debugBreakpoint guifg=#005f5f guibg=NONE gui=reverse cterm=reverse
- hi Cursor guifg=#000000 guibg=#ffffff gui=reverse cterm=reverse
+ hi Cursor guifg=NONE guibg=NONE gui=reverse ctermfg=NONE ctermbg=NONE cterm=reverse
hi lCursor guifg=#ff00ff guibg=#000000 gui=reverse cterm=reverse
hi Visual guifg=#ffffff guibg=#5f87af gui=NONE cterm=NONE
hi MatchParen guifg=NONE guibg=#c5e7c5 gui=NONE cterm=NONE
- hi VisualNOS guifg=#ffffff guibg=#00afaf gui=NONE cterm=NONE
+ hi VisualNOS guifg=#ffffff guibg=#008787 gui=NONE cterm=NONE
hi CursorLine guifg=NONE guibg=#e4e4e4 gui=NONE cterm=NONE
hi CursorColumn guifg=NONE guibg=#e4e4e4 gui=NONE cterm=NONE
hi Folded guifg=#767676 guibg=#eeeeee gui=NONE cterm=NONE
hi ColorColumn guifg=NONE guibg=#eeeeee gui=NONE cterm=NONE
- hi SpellBad guifg=NONE guibg=NONE guisp=#870000 gui=undercurl ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi SpellBad guifg=NONE guibg=NONE guisp=#af0000 gui=undercurl ctermfg=NONE ctermbg=NONE cterm=NONE
hi SpellCap guifg=NONE guibg=NONE guisp=#005f5f gui=undercurl ctermfg=NONE ctermbg=NONE cterm=NONE
hi SpellLocal guifg=NONE guibg=NONE guisp=#008700 gui=undercurl ctermfg=NONE ctermbg=NONE cterm=NONE
- hi SpellRare guifg=NONE guibg=NONE guisp=#af00af gui=undercurl ctermfg=NONE ctermbg=NONE cterm=NONE
- hi Comment guifg=#005faf guibg=NONE gui=NONE cterm=NONE
- hi Constant guifg=#870000 guibg=NONE gui=NONE cterm=NONE
+ hi SpellRare guifg=NONE guibg=NONE guisp=#ff00ff gui=undercurl ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Comment guifg=#005fd7 guibg=NONE gui=NONE cterm=NONE
+ hi Constant guifg=#af00af guibg=NONE gui=NONE cterm=NONE
+ hi String guifg=#af5f00 guibg=NONE gui=NONE cterm=NONE
hi Identifier guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
hi Statement guifg=#000000 guibg=NONE gui=bold cterm=bold
- hi Type guifg=#008700 guibg=NONE gui=bold cterm=bold
- hi PreProc guifg=#875f00 guibg=NONE gui=NONE cterm=NONE
- hi Special guifg=#005f5f guibg=NONE gui=NONE cterm=NONE
+ hi Type guifg=#008700 guibg=NONE gui=NONE cterm=NONE
+ hi PreProc guifg=#005f5f guibg=NONE gui=NONE cterm=NONE
+ hi Special guifg=#008787 guibg=NONE gui=NONE cterm=NONE
hi Underlined guifg=NONE guibg=NONE gui=underline ctermfg=NONE ctermbg=NONE cterm=underline
hi Title guifg=NONE guibg=NONE gui=bold ctermfg=NONE ctermbg=NONE cterm=bold
- hi Directory guifg=#005faf guibg=NONE gui=bold cterm=bold
+ hi Directory guifg=#005fd7 guibg=NONE gui=bold cterm=bold
hi Conceal guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
hi Ignore guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
hi DiffAdd guifg=#000000 guibg=#d7afd7 gui=NONE cterm=NONE
@@ -241,23 +280,22 @@ else
hi DiffDelete guifg=#870000 guibg=NONE gui=NONE cterm=NONE
hi diffAdded guifg=#008700 guibg=NONE gui=NONE cterm=NONE
hi diffRemoved guifg=#d70000 guibg=NONE gui=NONE cterm=NONE
- hi diffSubname guifg=#870087 guibg=NONE gui=NONE cterm=NONE
+ hi diffSubname guifg=#af00af guibg=NONE gui=NONE cterm=NONE
hi dirType guifg=#005f5f guibg=NONE gui=NONE cterm=NONE
- hi dirPermissionUser guifg=#875f00 guibg=NONE gui=NONE cterm=NONE
+ hi dirPermissionUser guifg=#af5f00 guibg=NONE gui=NONE cterm=NONE
hi dirPermissionGroup guifg=#008700 guibg=NONE gui=NONE cterm=NONE
- hi dirPermissionOther guifg=#870087 guibg=NONE gui=NONE cterm=NONE
+ hi dirPermissionOther guifg=#af00af guibg=NONE gui=NONE cterm=NONE
hi dirOwner guifg=#808080 guibg=NONE gui=NONE cterm=NONE
hi dirGroup guifg=#808080 guibg=NONE gui=NONE cterm=NONE
hi dirTime guifg=#808080 guibg=NONE gui=NONE cterm=NONE
- hi dirSize guifg=#870000 guibg=NONE gui=NONE cterm=NONE
+ hi dirSize guifg=#af0000 guibg=NONE gui=NONE cterm=NONE
hi dirSizeMod guifg=#005f5f guibg=NONE gui=NONE cterm=NONE
hi dirLink guifg=#008700 guibg=NONE gui=bold cterm=bold
hi dirFilterMenuBookmarkPath guifg=#626262 guibg=NONE gui=NONE cterm=NONE
hi dirFilterMenuHistoryPath guifg=#626262 guibg=NONE gui=NONE cterm=NONE
hi FilterMenuDirectorySubtle guifg=#626262 guibg=NONE gui=NONE cterm=NONE
hi FilterMenuLineNr guifg=#626262 guibg=NONE gui=NONE cterm=NONE
- hi CocMenuSel guifg=NONE guibg=#afd7af gui=NONE cterm=NONE
- hi CocSearch guifg=#870000 guibg=NONE gui=NONE cterm=NONE
+ hi CocSearch guifg=#af0000 guibg=NONE gui=NONE cterm=NONE
endif
if s:t_Co >= 256
@@ -274,12 +312,15 @@ if s:t_Co >= 256
hi! link fugitiveHash Constant
hi! link diffFile PreProc
hi! link markdownHeadingDelimiter Special
- hi! link rstSectionDelimiter PreProc
- hi! link rstDirective Special
+ hi! link rstSectionDelimiter Statement
+ hi! link rstDirective PreProc
hi! link rstHyperlinkReference Special
- hi! link rstFieldName Special
+ hi! link rstFieldName Constant
hi! link rstDelimiter Special
hi! link rstInterpretedText Special
+ hi! link rstCodeBlock Normal
+ hi! link rstLiteralBlock rstCodeBlock
+ hi! link markdownUrl String
hi! link colortemplateKey Statement
hi! link xmlTagName Statement
hi! link javaScriptFunction Statement
@@ -329,22 +370,49 @@ if s:t_Co >= 256
hi! link shCommandSub Normal
hi! link shDerefPattern shQuote
hi! link shDerefOp Special
+ hi! link phpStorageClass Statement
+ hi! link phpStructure Statement
+ hi! link phpInclude Statement
+ hi! link phpDefine Statement
+ hi! link phpSpecialFunction Normal
+ hi! link phpParent Normal
+ hi! link phpComparison Normal
+ hi! link phpOperator Normal
+ hi! link phpVarSelector Special
+ hi! link phpMemberSelector Special
+ hi! link phpDocCustomTags phpDocTags
+ hi! link javaExternal Statement
+ hi! link javaType Statement
+ hi! link javaScopeDecl Statement
+ hi! link javaClassDecl Statement
+ hi! link javaStorageClass Statement
+ hi! link javaDocParam PreProc
+ hi! link csStorage Statement
+ hi! link csAccessModifier Statement
+ hi! link csClass Statement
+ hi! link csModifier Statement
+ hi! link csAsyncModifier Statement
+ hi! link csLogicSymbols Normal
+ hi! link csClassType Normal
+ hi! link csType Statement
hi! link Terminal Normal
hi! link StatuslineTerm Statusline
hi! link StatuslineTermNC StatuslineNC
hi! link LineNrAbove LineNr
hi! link LineNrBelow LineNr
+ hi! link MessageWindow PMenu
+ hi! link PopupNotification Todo
if &background ==# 'dark'
hi Normal ctermfg=251 ctermbg=16 cterm=NONE
- hi Statusline ctermfg=16 ctermbg=251 cterm=bold
- hi StatuslineNC ctermfg=16 ctermbg=243 cterm=NONE
+ hi Statusline ctermfg=251 ctermbg=16 cterm=bold,reverse
+ hi StatuslineNC ctermfg=243 ctermbg=16 cterm=reverse
hi VertSplit ctermfg=243 ctermbg=243 cterm=NONE
hi TabLine ctermfg=16 ctermbg=251 cterm=NONE
hi TabLineFill ctermfg=NONE ctermbg=243 cterm=NONE
hi TabLineSel ctermfg=231 ctermbg=16 cterm=bold
hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
hi ToolbarButton ctermfg=16 ctermbg=231 cterm=NONE
- hi QuickFixLine ctermfg=16 ctermbg=110 cterm=NONE
+ hi QuickFixLine ctermfg=16 ctermbg=75 cterm=NONE
hi CursorLineNr ctermfg=231 ctermbg=NONE cterm=bold
hi LineNr ctermfg=240 ctermbg=NONE cterm=NONE
hi NonText ctermfg=240 ctermbg=NONE cterm=NONE
@@ -352,7 +420,7 @@ if s:t_Co >= 256
hi EndOfBuffer ctermfg=240 ctermbg=NONE cterm=NONE
hi SpecialKey ctermfg=240 ctermbg=NONE cterm=NONE
hi Pmenu ctermfg=NONE ctermbg=234 cterm=NONE
- hi PmenuSel ctermfg=NONE ctermbg=22 cterm=NONE
+ hi PmenuSel ctermfg=NONE ctermbg=238 cterm=NONE
hi PmenuThumb ctermfg=NONE ctermbg=251 cterm=NONE
hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE
hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE
@@ -360,7 +428,7 @@ if s:t_Co >= 256
hi ErrorMsg ctermfg=231 ctermbg=203 cterm=NONE
hi ModeMsg ctermfg=222 ctermbg=NONE cterm=reverse
hi MoreMsg ctermfg=77 ctermbg=NONE cterm=NONE
- hi Question ctermfg=218 ctermbg=NONE cterm=NONE
+ hi Question ctermfg=213 ctermbg=NONE cterm=NONE
hi WarningMsg ctermfg=203 ctermbg=NONE cterm=NONE
hi Todo ctermfg=116 ctermbg=16 cterm=reverse
hi Search ctermfg=16 ctermbg=222 cterm=NONE
@@ -379,17 +447,18 @@ if s:t_Co >= 256
hi SpellBad ctermfg=203 ctermbg=NONE cterm=underline
hi SpellCap ctermfg=73 ctermbg=NONE cterm=underline
hi SpellLocal ctermfg=77 ctermbg=NONE cterm=underline
- hi SpellRare ctermfg=218 ctermbg=NONE cterm=underline
- hi Comment ctermfg=110 ctermbg=NONE cterm=NONE
- hi Constant ctermfg=222 ctermbg=NONE cterm=NONE
+ hi SpellRare ctermfg=213 ctermbg=NONE cterm=underline
+ hi Comment ctermfg=75 ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=213 ctermbg=NONE cterm=NONE
+ hi String ctermfg=222 ctermbg=NONE cterm=NONE
hi Identifier ctermfg=NONE ctermbg=NONE cterm=NONE
- hi Statement ctermfg=255 ctermbg=NONE cterm=bold
- hi Type ctermfg=77 ctermbg=NONE cterm=bold
- hi PreProc ctermfg=137 ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=254 ctermbg=NONE cterm=bold
+ hi Type ctermfg=77 ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=116 ctermbg=NONE cterm=NONE
hi Special ctermfg=73 ctermbg=NONE cterm=NONE
hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline
hi Title ctermfg=NONE ctermbg=NONE cterm=bold
- hi Directory ctermfg=116 ctermbg=NONE cterm=bold
+ hi Directory ctermfg=75 ctermbg=NONE cterm=bold
hi Conceal ctermfg=NONE ctermbg=NONE cterm=NONE
hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
hi DiffAdd ctermfg=16 ctermbg=139 cterm=NONE
@@ -398,8 +467,8 @@ if s:t_Co >= 256
hi DiffDelete ctermfg=174 ctermbg=NONE cterm=NONE
hi diffAdded ctermfg=77 ctermbg=NONE cterm=NONE
hi diffRemoved ctermfg=174 ctermbg=NONE cterm=NONE
- hi diffSubname ctermfg=218 ctermbg=NONE cterm=NONE
- hi dirType ctermfg=175 ctermbg=NONE cterm=NONE
+ hi diffSubname ctermfg=213 ctermbg=NONE cterm=NONE
+ hi dirType ctermfg=176 ctermbg=NONE cterm=NONE
hi dirPermissionUser ctermfg=71 ctermbg=NONE cterm=NONE
hi dirPermissionGroup ctermfg=137 ctermbg=NONE cterm=NONE
hi dirPermissionOther ctermfg=73 ctermbg=NONE cterm=NONE
@@ -407,12 +476,11 @@ if s:t_Co >= 256
hi dirGroup ctermfg=243 ctermbg=NONE cterm=NONE
hi dirTime ctermfg=243 ctermbg=NONE cterm=NONE
hi dirSize ctermfg=222 ctermbg=NONE cterm=NONE
- hi dirSizeMod ctermfg=175 ctermbg=NONE cterm=NONE
+ hi dirSizeMod ctermfg=176 ctermbg=NONE cterm=NONE
hi FilterMenuDirectorySubtle ctermfg=102 ctermbg=NONE cterm=NONE
hi dirFilterMenuBookmarkPath ctermfg=102 ctermbg=NONE cterm=NONE
hi dirFilterMenuHistoryPath ctermfg=102 ctermbg=NONE cterm=NONE
hi FilterMenuLineNr ctermfg=102 ctermbg=NONE cterm=NONE
- hi CocMenuSel ctermfg=NONE ctermbg=22 cterm=NONE
hi CocSearch ctermfg=222 ctermbg=NONE cterm=NONE
else
" Light background
@@ -433,44 +501,45 @@ if s:t_Co >= 256
hi EndOfBuffer ctermfg=247 ctermbg=NONE cterm=NONE
hi SpecialKey ctermfg=247 ctermbg=NONE cterm=NONE
hi Pmenu ctermfg=NONE ctermbg=255 cterm=NONE
- hi PmenuSel ctermfg=NONE ctermbg=151 cterm=NONE
+ hi PmenuSel ctermfg=NONE ctermbg=251 cterm=NONE
hi PmenuThumb ctermfg=NONE ctermbg=243 cterm=NONE
hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE
hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE
hi Error ctermfg=231 ctermbg=160 cterm=NONE
hi ErrorMsg ctermfg=231 ctermbg=160 cterm=NONE
- hi ModeMsg ctermfg=186 ctermbg=16 cterm=reverse
+ hi ModeMsg ctermfg=222 ctermbg=16 cterm=reverse
hi MoreMsg ctermfg=28 ctermbg=NONE cterm=bold
- hi Question ctermfg=90 ctermbg=NONE cterm=bold
+ hi Question ctermfg=127 ctermbg=NONE cterm=bold
hi WarningMsg ctermfg=160 ctermbg=NONE cterm=bold
- hi Todo ctermfg=23 ctermbg=231 cterm=reverse
- hi Search ctermfg=16 ctermbg=186 cterm=NONE
+ hi Todo ctermfg=30 ctermbg=231 cterm=reverse
+ hi Search ctermfg=16 ctermbg=222 cterm=NONE
hi IncSearch ctermfg=16 ctermbg=114 cterm=NONE
hi CurSearch ctermfg=16 ctermbg=114 cterm=NONE
- hi WildMenu ctermfg=16 ctermbg=186 cterm=bold
- hi debugPC ctermfg=25 ctermbg=NONE cterm=reverse
+ hi WildMenu ctermfg=16 ctermbg=222 cterm=bold
+ hi debugPC ctermfg=26 ctermbg=NONE cterm=reverse
hi debugBreakpoint ctermfg=23 ctermbg=NONE cterm=reverse
hi Visual ctermfg=231 ctermbg=67 cterm=NONE
hi MatchParen ctermfg=30 ctermbg=231 cterm=reverse
- hi VisualNOS ctermfg=231 ctermbg=37 cterm=NONE
+ hi VisualNOS ctermfg=231 ctermbg=30 cterm=NONE
hi CursorLine ctermfg=NONE ctermbg=254 cterm=NONE
hi CursorColumn ctermfg=NONE ctermbg=254 cterm=NONE
hi Folded ctermfg=243 ctermbg=255 cterm=NONE
hi ColorColumn ctermfg=NONE ctermbg=255 cterm=NONE
- hi SpellBad ctermfg=88 ctermbg=NONE cterm=underline
+ hi SpellBad ctermfg=124 ctermbg=NONE cterm=underline
hi SpellCap ctermfg=23 ctermbg=NONE cterm=underline
hi SpellLocal ctermfg=28 ctermbg=NONE cterm=underline
hi SpellRare ctermfg=133 ctermbg=NONE cterm=underline
- hi Comment ctermfg=25 ctermbg=NONE cterm=NONE
- hi Constant ctermfg=88 ctermbg=NONE cterm=NONE
+ hi Comment ctermfg=26 ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=127 ctermbg=NONE cterm=NONE
+ hi String ctermfg=130 ctermbg=NONE cterm=NONE
hi Identifier ctermfg=NONE ctermbg=NONE cterm=NONE
hi Statement ctermfg=16 ctermbg=NONE cterm=bold
- hi Type ctermfg=28 ctermbg=NONE cterm=bold
- hi PreProc ctermfg=94 ctermbg=NONE cterm=NONE
- hi Special ctermfg=23 ctermbg=NONE cterm=NONE
+ hi Type ctermfg=28 ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=23 ctermbg=NONE cterm=NONE
+ hi Special ctermfg=30 ctermbg=NONE cterm=NONE
hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline
hi Title ctermfg=NONE ctermbg=NONE cterm=bold
- hi Directory ctermfg=25 ctermbg=NONE cterm=bold
+ hi Directory ctermfg=26 ctermbg=NONE cterm=bold
hi Conceal ctermfg=NONE ctermbg=NONE cterm=NONE
hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
hi DiffAdd ctermfg=16 ctermbg=182 cterm=NONE
@@ -479,23 +548,22 @@ if s:t_Co >= 256
hi DiffDelete ctermfg=88 ctermbg=NONE cterm=NONE
hi diffAdded ctermfg=28 ctermbg=NONE cterm=NONE
hi diffRemoved ctermfg=160 ctermbg=NONE cterm=NONE
- hi diffSubname ctermfg=90 ctermbg=NONE cterm=NONE
+ hi diffSubname ctermfg=127 ctermbg=NONE cterm=NONE
hi dirType ctermfg=23 ctermbg=NONE cterm=NONE
- hi dirPermissionUser ctermfg=94 ctermbg=NONE cterm=NONE
+ hi dirPermissionUser ctermfg=130 ctermbg=NONE cterm=NONE
hi dirPermissionGroup ctermfg=28 ctermbg=NONE cterm=NONE
- hi dirPermissionOther ctermfg=90 ctermbg=NONE cterm=NONE
+ hi dirPermissionOther ctermfg=127 ctermbg=NONE cterm=NONE
hi dirOwner ctermfg=244 ctermbg=NONE cterm=NONE
hi dirGroup ctermfg=244 ctermbg=NONE cterm=NONE
hi dirTime ctermfg=244 ctermbg=NONE cterm=NONE
- hi dirSize ctermfg=88 ctermbg=NONE cterm=NONE
+ hi dirSize ctermfg=124 ctermbg=NONE cterm=NONE
hi dirSizeMod ctermfg=23 ctermbg=NONE cterm=NONE
hi dirLink ctermfg=28 ctermbg=NONE cterm=bold
hi dirFilterMenuBookmarkPath ctermfg=241 ctermbg=NONE cterm=NONE
hi dirFilterMenuHistoryPath ctermfg=241 ctermbg=NONE cterm=NONE
hi FilterMenuDirectorySubtle ctermfg=241 ctermbg=NONE cterm=NONE
hi FilterMenuLineNr ctermfg=241 ctermbg=NONE cterm=NONE
- hi CocMenuSel ctermfg=NONE ctermbg=151 cterm=NONE
- hi CocSearch ctermfg=88 ctermbg=NONE cterm=NONE
+ hi CocSearch ctermfg=124 ctermbg=NONE cterm=NONE
endif
unlet s:t_Co
finish
@@ -504,8 +572,8 @@ endif
if s:t_Co >= 16
if &background ==# 'dark'
hi Normal ctermfg=grey ctermbg=black cterm=NONE
- hi Statusline ctermfg=black ctermbg=grey cterm=bold
- hi StatuslineNC ctermfg=black ctermbg=darkgrey cterm=NONE
+ hi Statusline ctermfg=grey ctermbg=black cterm=bold,reverse
+ hi StatuslineNC ctermfg=darkgrey ctermbg=black cterm=reverse
hi VertSplit ctermfg=darkgrey ctermbg=darkgrey cterm=NONE
hi TabLine ctermfg=black ctermbg=grey cterm=NONE
hi TabLineFill ctermfg=NONE ctermbg=darkgrey cterm=NONE
@@ -520,7 +588,7 @@ if s:t_Co >= 16
hi EndOfBuffer ctermfg=grey ctermbg=NONE cterm=NONE
hi SpecialKey ctermfg=grey ctermbg=NONE cterm=NONE
hi Pmenu ctermfg=black ctermbg=darkgrey cterm=NONE
- hi PmenuSel ctermfg=black ctermbg=darkgreen cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=darkcyan cterm=NONE
hi PmenuThumb ctermfg=NONE ctermbg=grey cterm=NONE
hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE
hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE
@@ -549,15 +617,16 @@ if s:t_Co >= 16
hi SpellLocal ctermfg=green ctermbg=NONE cterm=underline
hi SpellRare ctermfg=magenta ctermbg=NONE cterm=underline
hi Comment ctermfg=blue ctermbg=NONE cterm=NONE
- hi Constant ctermfg=yellow ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=magenta ctermbg=NONE cterm=NONE
+ hi String ctermfg=yellow ctermbg=NONE cterm=NONE
hi Identifier ctermfg=NONE ctermbg=NONE cterm=NONE
hi Statement ctermfg=grey ctermbg=NONE cterm=bold
- hi Type ctermfg=green ctermbg=NONE cterm=bold
- hi PreProc ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi Type ctermfg=green ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=cyan ctermbg=NONE cterm=NONE
hi Special ctermfg=darkcyan ctermbg=NONE cterm=NONE
hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline
hi Title ctermfg=NONE ctermbg=NONE cterm=bold
- hi Directory ctermfg=cyan ctermbg=NONE cterm=bold
+ hi Directory ctermfg=blue ctermbg=NONE cterm=bold
hi Conceal ctermfg=NONE ctermbg=NONE cterm=NONE
hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
hi DiffAdd ctermfg=black ctermbg=darkmagenta cterm=NONE
@@ -567,6 +636,20 @@ if s:t_Co >= 16
hi diffAdded ctermfg=green ctermbg=NONE cterm=NONE
hi diffRemoved ctermfg=darkred ctermbg=NONE cterm=NONE
hi diffSubname ctermfg=magenta ctermbg=NONE cterm=NONE
+ hi dirType ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi dirPermissionUser ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi dirPermissionGroup ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi dirPermissionOther ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi dirOwner ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi dirGroup ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi dirTime ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi dirSize ctermfg=yellow ctermbg=NONE cterm=NONE
+ hi dirSizeMod ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi FilterMenuDirectorySubtle ctermfg=grey ctermbg=NONE cterm=NONE
+ hi dirFilterMenuBookmarkPath ctermfg=grey ctermbg=NONE cterm=NONE
+ hi dirFilterMenuHistoryPath ctermfg=grey ctermbg=NONE cterm=NONE
+ hi FilterMenuLineNr ctermfg=grey ctermbg=NONE cterm=NONE
+ hi CocSearch ctermfg=yellow ctermbg=NONE cterm=NONE
else
" Light background
hi Normal ctermfg=black ctermbg=white cterm=NONE
@@ -586,7 +669,7 @@ if s:t_Co >= 16
hi EndOfBuffer ctermfg=darkgrey ctermbg=NONE cterm=NONE
hi SpecialKey ctermfg=darkgrey ctermbg=NONE cterm=NONE
hi Pmenu ctermfg=black ctermbg=grey cterm=NONE
- hi PmenuSel ctermfg=black ctermbg=darkgreen cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=darkcyan cterm=NONE
hi PmenuThumb ctermfg=NONE ctermbg=darkgrey cterm=NONE
hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE
hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE
@@ -596,7 +679,7 @@ if s:t_Co >= 16
hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=bold
hi Question ctermfg=darkmagenta ctermbg=NONE cterm=bold
hi WarningMsg ctermfg=red ctermbg=NONE cterm=bold
- hi Todo ctermfg=darkcyan ctermbg=white cterm=reverse
+ hi Todo ctermfg=cyan ctermbg=white cterm=reverse
hi Search ctermfg=black ctermbg=yellow cterm=NONE
hi IncSearch ctermfg=black ctermbg=green cterm=NONE
hi CurSearch ctermfg=black ctermbg=green cterm=NONE
@@ -615,12 +698,13 @@ if s:t_Co >= 16
hi SpellLocal ctermfg=darkgreen ctermbg=NONE cterm=underline
hi SpellRare ctermfg=magenta ctermbg=NONE cterm=underline
hi Comment ctermfg=darkblue ctermbg=NONE cterm=NONE
- hi Constant ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi String ctermfg=darkyellow ctermbg=NONE cterm=NONE
hi Identifier ctermfg=NONE ctermbg=NONE cterm=NONE
hi Statement ctermfg=black ctermbg=NONE cterm=bold
- hi Type ctermfg=darkgreen ctermbg=NONE cterm=bold
- hi PreProc ctermfg=darkyellow ctermbg=NONE cterm=NONE
- hi Special ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Type ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Special ctermfg=cyan ctermbg=NONE cterm=NONE
hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline
hi Title ctermfg=NONE ctermbg=NONE cterm=bold
hi Directory ctermfg=darkblue ctermbg=NONE cterm=bold
@@ -633,6 +717,21 @@ if s:t_Co >= 16
hi diffAdded ctermfg=darkgreen ctermbg=NONE cterm=NONE
hi diffRemoved ctermfg=red ctermbg=NONE cterm=NONE
hi diffSubname ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi dirType ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi dirPermissionUser ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi dirPermissionGroup ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi dirPermissionOther ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi dirOwner ctermfg=grey ctermbg=NONE cterm=NONE
+ hi dirGroup ctermfg=grey ctermbg=NONE cterm=NONE
+ hi dirTime ctermfg=grey ctermbg=NONE cterm=NONE
+ hi dirSize ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi dirSizeMod ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi dirLink ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi dirFilterMenuBookmarkPath ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi dirFilterMenuHistoryPath ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi FilterMenuDirectorySubtle ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi FilterMenuLineNr ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi CocSearch ctermfg=darkred ctermbg=NONE cterm=NONE
endif
unlet s:t_Co
finish
@@ -656,10 +755,10 @@ if s:t_Co >= 8
hi FoldColumn ctermfg=black ctermbg=NONE cterm=NONE
hi EndOfBuffer ctermfg=black ctermbg=NONE cterm=NONE
hi SpecialKey ctermfg=black ctermbg=NONE cterm=NONE
- hi Pmenu ctermfg=black ctermbg=grey cterm=NONE
+ hi Pmenu ctermfg=NONE ctermbg=grey cterm=NONE
hi PmenuThumb ctermfg=NONE ctermbg=darkgreen cterm=NONE
hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE
- hi PmenuSel ctermfg=black ctermbg=darkgreen cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=darkcyan cterm=NONE
hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE
hi Error ctermfg=grey ctermbg=darkred cterm=NONE
hi ErrorMsg ctermfg=grey ctermbg=darkred cterm=NONE
@@ -674,7 +773,7 @@ if s:t_Co >= 8
hi WildMenu ctermfg=black ctermbg=darkyellow cterm=bold
hi debugPC ctermfg=darkblue ctermbg=NONE cterm=reverse
hi debugBreakpoint ctermfg=darkcyan ctermbg=NONE cterm=reverse
- hi Visual ctermfg=black ctermbg=darkblue cterm=NONE
+ hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse
hi MatchParen ctermfg=darkcyan ctermbg=black cterm=reverse
hi VisualNOS ctermfg=black ctermbg=darkcyan cterm=NONE
hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
@@ -688,13 +787,13 @@ if s:t_Co >= 8
hi Comment ctermfg=darkblue ctermbg=NONE cterm=NONE
hi Constant ctermfg=darkred ctermbg=NONE cterm=NONE
hi Identifier ctermfg=NONE ctermbg=NONE cterm=NONE
- hi Statement ctermfg=black ctermbg=NONE cterm=bold
- hi Type ctermfg=darkgreen ctermbg=NONE cterm=bold
- hi PreProc ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=grey ctermbg=NONE cterm=bold
+ hi Type ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=darkcyan ctermbg=NONE cterm=NONE
hi Special ctermfg=darkcyan ctermbg=NONE cterm=NONE
hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline
hi Title ctermfg=NONE ctermbg=NONE cterm=bold
- hi Directory ctermfg=darkcyan ctermbg=NONE cterm=bold
+ hi Directory ctermfg=darkblue ctermbg=NONE cterm=bold
hi Conceal ctermfg=NONE ctermbg=NONE cterm=NONE
hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
hi DiffAdd ctermfg=black ctermbg=darkmagenta cterm=NONE
@@ -719,10 +818,10 @@ if s:t_Co >= 8
hi FoldColumn ctermfg=black ctermbg=NONE cterm=NONE
hi EndOfBuffer ctermfg=black ctermbg=NONE cterm=NONE
hi SpecialKey ctermfg=black ctermbg=NONE cterm=NONE
- hi Pmenu ctermfg=grey ctermbg=black cterm=NONE
+ hi Pmenu ctermfg=NONE ctermbg=black cterm=NONE
hi PmenuThumb ctermfg=NONE ctermbg=darkgreen cterm=NONE
hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE
- hi PmenuSel ctermfg=black ctermbg=darkgreen cterm=NONE
+ hi PmenuSel ctermfg=NONE ctermbg=darkcyan cterm=NONE
hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE
hi Error ctermfg=grey ctermbg=darkred cterm=NONE
hi ErrorMsg ctermfg=grey ctermbg=darkred cterm=NONE
@@ -737,7 +836,7 @@ if s:t_Co >= 8
hi WildMenu ctermfg=black ctermbg=darkyellow cterm=bold
hi debugPC ctermfg=darkblue ctermbg=NONE cterm=reverse
hi debugBreakpoint ctermfg=darkcyan ctermbg=NONE cterm=reverse
- hi Visual ctermfg=grey ctermbg=darkblue cterm=NONE
+ hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse
hi MatchParen ctermfg=darkcyan ctermbg=grey cterm=reverse
hi VisualNOS ctermfg=black ctermbg=darkcyan cterm=NONE
hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
@@ -752,12 +851,12 @@ if s:t_Co >= 8
hi Constant ctermfg=darkred ctermbg=NONE cterm=NONE
hi Identifier ctermfg=NONE ctermbg=NONE cterm=NONE
hi Statement ctermfg=black ctermbg=NONE cterm=bold
- hi Type ctermfg=darkgreen ctermbg=NONE cterm=bold
- hi PreProc ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi Type ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=darkcyan ctermbg=NONE cterm=NONE
hi Special ctermfg=darkcyan ctermbg=NONE cterm=NONE
hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline
hi Title ctermfg=black ctermbg=NONE cterm=bold
- hi Directory ctermfg=darkcyan ctermbg=NONE cterm=bold
+ hi Directory ctermfg=darkblue ctermbg=NONE cterm=bold
hi Conceal ctermfg=NONE ctermbg=NONE cterm=NONE
hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
hi DiffAdd ctermfg=black ctermbg=darkmagenta cterm=NONE
@@ -848,14 +947,14 @@ endif
" Color: color03 #AF875F 137 darkyellow
" Color: color11 #FFD787 222 yellow
" Color: color04 #5F87AF 67 darkblue
-" Color: color12 #87AFD7 110 blue
-" Color: color05 #D787AF 175 darkmagenta
-" Color: color13 #FFAFD7 218 magenta
+" Color: color12 #5FAFFF 75 blue
+" Color: color05 #D787D7 176 darkmagenta
+" Color: color13 #FF87FF 213 magenta
" Color: color06 #5FAFAF 73 darkcyan
" Color: color14 #5FD7D7 116 cyan
" Color: color07 #C6C6C6 251 grey
" Color: color15 #FFFFFF 231 white
-" Color: colorDimWhite #EEEEEE 255 grey
+" Color: colorDimWhite #E4E4E4 254 grey
" Color: colorLine #262626 235 darkgrey
" Color: colorB #1C1C1C 234 darkgrey
" Color: colorNonT #585858 240 grey
@@ -864,7 +963,7 @@ endif
" Color: colorlC #FF5FFF 207 magenta
" Color: colorV #005F87 24 darkblue
" Color: colorMP #C5E7C5 30 darkcyan
-" Color: colorPMenuSel #005F00 22 darkgreen
+" Color: colorPMenuSel #444444 238 darkcyan
" Color: colorDim #878787 102 grey
" Color: diffAdd #AF87AF 139 darkmagenta
" Color: diffDelete #D78787 174 darkred
@@ -876,18 +975,18 @@ endif
" Background: light
" Color: color00 #000000 16 black
" Color: color08 #767676 243 darkgrey
-" Color: color01 #870000 88 darkred
+" Color: color01 #AF0000 124 darkred
" Color: color09 #D70000 160 red
" Color: color02 #008700 28 darkgreen
" Color: color10 #87D787 114 green
-" Color: color03 #875F00 94 darkyellow
-" Color: color11 #D7D787 186 yellow
-" Color: color04 #005FAF 25 darkblue
+" Color: color03 #AF5F00 130 darkyellow
+" Color: color11 #FFD787 222 yellow
+" Color: color04 #005FD7 26 darkblue
" Color: color12 #0087D7 32 blue
-" Color: color05 #870087 90 darkmagenta
-" Color: color13 #AF00AF 133 magenta
+" Color: color05 #AF00AF 127 darkmagenta
+" Color: color13 #FF00FF 133 magenta
" Color: color06 #005F5F 23 darkcyan
-" Color: color14 #00AFAF 37 cyan
+" Color: color14 #008787 30 cyan
" Color: color07 #808080 244 grey
" Color: color15 #FFFFFF 231 white
" Color: colorLine #E4E4E4 254 grey
@@ -898,7 +997,7 @@ endif
" Color: colorlC #FF00FF 201 magenta
" Color: colorV #5F87AF 67 darkblue
" Color: colorMP #C5E7C5 30 darkcyan
-" Color: colorPMenuSel #AFD7AF 151 darkgreen
+" Color: colorPMenuSel #C6C6C6 251 darkcyan
" Color: colorDim #626262 241 darkgrey
" Color: diffAdd #D7AFD7 182 darkmagenta
" Color: diffDelete #870000 88 darkred
diff --git a/runtime/colors/morning.vim b/runtime/colors/morning.vim
index 5764d0df78..5c6a617137 100644
--- a/runtime/colors/morning.vim
+++ b/runtime/colors/morning.vim
@@ -4,7 +4,7 @@
" Maintainer: Original maintainer Bram Moolenaar <Bram@vim.org>
" Website: https://github.com/vim/colorschemes
" License: Same as Vim
-" Last Updated: Mon Aug 8 15:21:13 2022
+" Last Updated: Fri 02 Sep 2022 09:46:24 MSK
" Generated by Colortemplate v2.2.0
@@ -13,10 +13,14 @@ set background=light
hi clear
let g:colors_name = 'morning'
-let s:t_Co = exists('&t_Co') ? (&t_Co ? &t_Co : 0) : -1
+let s:t_Co = &t_Co
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']
+ " Nvim uses g:terminal_color_{0-15} instead
+ for i in range(g:terminal_ansi_colors->len())
+ let g:terminal_color_{i} = g:terminal_ansi_colors[i]
+ endfor
endif
hi! link Terminal Normal
hi! link LineNrAbove LineNr
@@ -26,6 +30,8 @@ hi! link CursorLineFold CursorLine
hi! link CursorLineSign CursorLine
hi! link StatuslineTerm Statusline
hi! link StatuslineTermNC StatuslineNC
+hi! link MessageWindow Pmenu
+hi! link PopupNotification Todo
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
@@ -96,6 +102,8 @@ if s:t_Co >= 256
hi! link CursorLineSign CursorLine
hi! link StatuslineTerm Statusline
hi! link StatuslineTermNC StatuslineNC
+ hi! link MessageWindow Pmenu
+ hi! link PopupNotification Todo
hi Normal ctermfg=16 ctermbg=254 cterm=NONE
hi EndOfBuffer ctermfg=21 ctermbg=252 cterm=bold
hi Folded ctermfg=18 ctermbg=252 cterm=NONE
diff --git a/runtime/colors/murphy.vim b/runtime/colors/murphy.vim
index b7cf6ce985..47d7dbe22e 100644
--- a/runtime/colors/murphy.vim
+++ b/runtime/colors/murphy.vim
@@ -4,7 +4,7 @@
" Maintainer: Original maintainer Ron Aaron <ron@ronware.org>.
" Website: https://github.com/vim/colorschemes
" License: Same as Vim
-" Last Updated: Mon Aug 8 15:21:14 2022
+" Last Updated: Fri 02 Sep 2022 09:47:20 MSK
" Generated by Colortemplate v2.2.0
@@ -13,10 +13,14 @@ set background=dark
hi clear
let g:colors_name = 'murphy'
-let s:t_Co = exists('&t_Co') ? (&t_Co ? &t_Co : 0) : -1
+let s:t_Co = &t_Co
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']
+ " Nvim uses g:terminal_color_{0-15} instead
+ for i in range(g:terminal_ansi_colors->len())
+ let g:terminal_color_{i} = g:terminal_ansi_colors[i]
+ endfor
endif
hi! link Terminal Normal
hi! link LineNrAbove LineNr
@@ -26,6 +30,8 @@ hi! link CursorLineFold CursorLine
hi! link CursorLineSign CursorLine
hi! link StatusLineTerm StatusLine
hi! link StatusLineTermNC StatusLineNC
+hi! link MessageWindow Pmenu
+hi! link PopupNotification Todo
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
@@ -96,6 +102,8 @@ if s:t_Co >= 256
hi! link CursorLineSign CursorLine
hi! link StatusLineTerm StatusLine
hi! link StatusLineTermNC StatusLineNC
+ hi! link MessageWindow Pmenu
+ hi! link PopupNotification Todo
hi Normal ctermfg=120 ctermbg=16 cterm=NONE
hi EndOfBuffer ctermfg=21 ctermbg=16 cterm=NONE
hi StatusLine ctermfg=231 ctermbg=18 cterm=NONE
diff --git a/runtime/colors/pablo.vim b/runtime/colors/pablo.vim
index 521ea44aaf..8766cc4776 100644
--- a/runtime/colors/pablo.vim
+++ b/runtime/colors/pablo.vim
@@ -3,7 +3,7 @@
" Maintainer: Original maintainerRon Aaron <ron@ronware.org>
" Website: https://github.com/vim/colorschemes
" License: Same as Vim
-" Last Updated: Mon Aug 8 15:21:15 2022
+" Last Updated: Wed 14 Sep 2022 19:05:27 MSK
" Generated by Colortemplate v2.2.0
@@ -12,18 +12,24 @@ set background=dark
hi clear
let g:colors_name = 'pablo'
-let s:t_Co = exists('&t_Co') ? (&t_Co ? &t_Co : 0) : -1
+let s:t_Co = &t_Co
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']
+ " Nvim uses g:terminal_color_{0-15} instead
+ for i in range(g:terminal_ansi_colors->len())
+ let g:terminal_color_{i} = g:terminal_ansi_colors[i]
+ endfor
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! link MessageWindow Pmenu
+hi! link PopupNotification Todo
+hi Normal guifg=#ffffff guibg=#000000 gui=NONE cterm=NONE
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
@@ -88,13 +94,15 @@ 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! link MessageWindow Pmenu
+ hi! link PopupNotification Todo
+ hi Normal ctermfg=231 ctermbg=16 cterm=NONE
hi Comment ctermfg=244 ctermbg=NONE cterm=NONE
hi Constant ctermfg=51 ctermbg=NONE cterm=NONE
hi Identifier ctermfg=37 ctermbg=NONE cterm=NONE
@@ -117,7 +125,7 @@ if s:t_Co >= 256
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 WarningMsg ctermfg=196 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
@@ -163,12 +171,6 @@ endif
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
@@ -191,7 +193,7 @@ if s:t_Co >= 16
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 WarningMsg ctermfg=red 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
@@ -282,7 +284,6 @@ if s:t_Co >= 8
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
@@ -405,7 +406,7 @@ endif
" Color: SpecialKey #00ffff 81 cyan
" Color: StatusLineTerm #90ee90 121 darkgreen
" Color: Title #ff00ff 225 magenta
-" Color: WarningMsg #ff0000 224 darkred
+" Color: WarningMsg #ff0000 196 red
" Color: ToolbarLine #7f7f7f 242 darkgrey
" Color: ToolbarButton #d3d3d3 254 grey
" Color: Underlined #80a0ff 111 darkgreen
diff --git a/runtime/colors/peachpuff.vim b/runtime/colors/peachpuff.vim
index 3e896ffdeb..0bab72dace 100644
--- a/runtime/colors/peachpuff.vim
+++ b/runtime/colors/peachpuff.vim
@@ -4,7 +4,7 @@
" 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: Mon Aug 8 15:21:16 2022
+" Last Updated: Fri 02 Sep 2022 09:50:02 MSK
" Generated by Colortemplate v2.2.0
@@ -13,10 +13,14 @@ set background=light
hi clear
let g:colors_name = 'peachpuff'
-let s:t_Co = exists('&t_Co') ? (&t_Co ? &t_Co : 0) : -1
+let s:t_Co = &t_Co
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']
+ " Nvim uses g:terminal_color_{0-15} instead
+ for i in range(g:terminal_ansi_colors->len())
+ let g:terminal_color_{i} = g:terminal_ansi_colors[i]
+ endfor
endif
hi! link Terminal Normal
hi! link LineNrAbove LineNr
@@ -24,6 +28,8 @@ hi! link LineNrBelow LineNr
hi! link CurSearch Search
hi! link CursorLineFold CursorLine
hi! link CursorLineSign CursorLine
+hi! link MessageWindow Pmenu
+hi! link PopupNotification Todo
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
@@ -94,6 +100,8 @@ if s:t_Co >= 256
hi! link CurSearch Search
hi! link CursorLineFold CursorLine
hi! link CursorLineSign CursorLine
+ hi! link MessageWindow Pmenu
+ hi! link PopupNotification Todo
hi Normal ctermfg=16 ctermbg=223 cterm=NONE
hi Folded ctermfg=16 ctermbg=252 cterm=NONE
hi CursorLine ctermfg=NONE ctermbg=180 cterm=NONE
diff --git a/runtime/colors/quiet.vim b/runtime/colors/quiet.vim
index 4c4baa994c..d286839250 100644
--- a/runtime/colors/quiet.vim
+++ b/runtime/colors/quiet.vim
@@ -4,18 +4,20 @@
" Maintainer: neutaaaaan <neutaaaaan-gh@protonmail.com>
" Website: https://github.com/vim/colorschemes
" License: Vim License (see `:help license`)`
-" Last Updated: 2022-08-14 15:17:11
+" Last Updated: Fri 16 Sep 2022 09:52:50 MSK
" Generated by Colortemplate v2.2.0
hi clear
let g:colors_name = 'quiet'
-let s:t_Co = exists('&t_Co') ? (&t_Co ? &t_Co : 0) : -1
+let s:t_Co = &t_Co
hi! link Terminal Normal
hi! link StatusLineTerm StatusLine
hi! link StatusLineTermNC StatusLineNC
+hi! link MessageWindow Pmenu
+hi! link PopupNotification Todo
hi! link Boolean Constant
hi! link Character Constant
hi! link Conditional Statement
@@ -47,6 +49,10 @@ hi! link debugPC CursorLine
if &background ==# 'dark'
if (has('termguicolors') && &termguicolors) || has('gui_running')
let g:terminal_ansi_colors = ['#080808', '#d7005f', '#00af5f', '#d78700', '#0087d7', '#d787d7', '#00afaf', '#dadada', '#707070', '#ff005f', '#00d75f', '#ffaf00', '#5fafff', '#ff87ff', '#00d7d7', '#ffffff']
+ " Nvim uses g:terminal_color_{0-15} instead
+ for i in range(g:terminal_ansi_colors->len())
+ let g:terminal_color_{i} = g:terminal_ansi_colors[i]
+ endfor
endif
hi Normal guifg=#dadada guibg=#080808 gui=NONE cterm=NONE
hi ColorColumn guifg=NONE guibg=#1c1c1c gui=NONE cterm=NONE
@@ -59,8 +65,8 @@ if &background ==# 'dark'
hi DiffChange guifg=#87afd7 guibg=#080808 gui=reverse cterm=reverse
hi DiffDelete guifg=#d75f5f guibg=#080808 gui=reverse cterm=reverse
hi DiffText guifg=#d787d7 guibg=#080808 gui=reverse cterm=reverse
- hi Directory guifg=#dadada guibg=#080808 gui=NONE cterm=NONE
- hi EndOfBuffer guifg=#dadada guibg=#080808 gui=NONE cterm=NONE
+ hi Directory guifg=#dadada guibg=NONE gui=NONE cterm=NONE
+ hi EndOfBuffer guifg=#dadada guibg=NONE gui=NONE cterm=NONE
hi ErrorMsg guifg=#dadada guibg=#080808 gui=reverse cterm=reverse
hi FoldColumn guifg=#707070 guibg=NONE gui=NONE cterm=NONE
hi Folded guifg=#707070 guibg=#080808 gui=NONE cterm=NONE
@@ -84,13 +90,13 @@ if &background ==# 'dark'
hi SpellLocal guifg=#d787d7 guibg=NONE guisp=#d787d7 gui=undercurl cterm=underline
hi SpellRare guifg=#00afaf guibg=NONE guisp=#00afaf gui=undercurl cterm=underline
hi StatusLine guifg=#080808 guibg=#dadada gui=bold cterm=bold
- hi StatusLineNC guifg=#707070 guibg=#080808 gui=underline cterm=underline
- hi TabLine guifg=#707070 guibg=#080808 gui=underline cterm=underline
+ hi StatusLineNC guifg=#707070 guibg=#080808 gui=reverse cterm=reverse
+ hi TabLine guifg=#707070 guibg=#080808 gui=reverse cterm=reverse
hi TabLineFill guifg=#dadada guibg=NONE gui=NONE cterm=NONE
hi TabLineSel guifg=#080808 guibg=#dadada gui=bold cterm=bold
- hi Title guifg=#dadada guibg=NONE gui=NONE cterm=NONE
+ hi Title guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
hi VertSplit guifg=#707070 guibg=#080808 gui=NONE cterm=NONE
- hi Visual guifg=NONE guibg=NONE gui=reverse ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi Visual guifg=#ffaf00 guibg=#080808 gui=reverse cterm=reverse
hi VisualNOS guifg=NONE guibg=#303030 gui=NONE cterm=NONE
hi WarningMsg guifg=#dadada guibg=NONE gui=NONE cterm=NONE
hi WildMenu guifg=#00afff guibg=#080808 gui=bold cterm=bold
@@ -112,6 +118,10 @@ else
" Light background
if (has('termguicolors') && &termguicolors) || has('gui_running')
let g:terminal_ansi_colors = ['#080808', '#af0000', '#005f00', '#af5f00', '#005faf', '#870087', '#008787', '#d7d7d7', '#626262', '#d70000', '#008700', '#d78700', '#0087d7', '#af00af', '#00afaf', '#ffffff']
+ " Nvim uses g:terminal_color_{0-15} instead
+ for i in range(g:terminal_ansi_colors->len())
+ let g:terminal_color_{i} = g:terminal_ansi_colors[i]
+ endfor
endif
hi Normal guifg=#080808 guibg=#d7d7d7 gui=NONE cterm=NONE
hi ColorColumn guifg=NONE guibg=#e4e4e4 gui=NONE cterm=NONE
@@ -153,9 +163,9 @@ else
hi TabLine guifg=#080808 guibg=#a8a8a8 gui=NONE cterm=NONE
hi TabLineFill guifg=#080808 guibg=#d7d7d7 gui=NONE cterm=NONE
hi TabLineSel guifg=#eeeeee guibg=#080808 gui=bold cterm=bold
- hi Title guifg=#080808 guibg=NONE gui=NONE cterm=NONE
+ hi Title guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
hi VertSplit guifg=#626262 guibg=#d7d7d7 gui=NONE cterm=NONE
- hi Visual guifg=NONE guibg=NONE gui=reverse ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi Visual guifg=#ffaf00 guibg=#080808 gui=reverse cterm=reverse
hi VisualNOS guifg=NONE guibg=#eeeeee gui=NONE cterm=NONE
hi WarningMsg guifg=#080808 guibg=NONE gui=NONE cterm=NONE
hi WildMenu guifg=#080808 guibg=#eeeeee gui=bold cterm=bold
@@ -188,8 +198,8 @@ if s:t_Co >= 256
hi DiffChange ctermfg=110 ctermbg=232 cterm=reverse
hi DiffDelete ctermfg=167 ctermbg=232 cterm=reverse
hi DiffText ctermfg=176 ctermbg=232 cterm=reverse
- hi Directory ctermfg=253 ctermbg=232 cterm=NONE
- hi EndOfBuffer ctermfg=253 ctermbg=232 cterm=NONE
+ hi Directory ctermfg=253 ctermbg=NONE cterm=NONE
+ hi EndOfBuffer ctermfg=253 ctermbg=NONE cterm=NONE
hi ErrorMsg ctermfg=253 ctermbg=232 cterm=reverse
hi FoldColumn ctermfg=242 ctermbg=NONE cterm=NONE
hi Folded ctermfg=242 ctermbg=232 cterm=NONE
@@ -213,13 +223,13 @@ if s:t_Co >= 256
hi SpellLocal ctermfg=176 ctermbg=NONE cterm=underline
hi SpellRare ctermfg=37 ctermbg=NONE cterm=underline
hi StatusLine ctermfg=232 ctermbg=253 cterm=bold
- hi StatusLineNC ctermfg=242 ctermbg=232 cterm=underline
- hi TabLine ctermfg=242 ctermbg=232 cterm=underline
+ hi StatusLineNC ctermfg=242 ctermbg=232 cterm=reverse
+ hi TabLine ctermfg=242 ctermbg=232 cterm=reverse
hi TabLineFill ctermfg=253 ctermbg=NONE cterm=NONE
hi TabLineSel ctermfg=232 ctermbg=253 cterm=bold
- hi Title ctermfg=253 ctermbg=NONE cterm=NONE
+ hi Title ctermfg=NONE ctermbg=NONE cterm=NONE
hi VertSplit ctermfg=242 ctermbg=232 cterm=NONE
- hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi Visual ctermfg=214 ctermbg=232 cterm=reverse
hi VisualNOS ctermfg=NONE ctermbg=236 cterm=NONE
hi WarningMsg ctermfg=253 ctermbg=NONE cterm=NONE
hi WildMenu ctermfg=39 ctermbg=232 cterm=bold
@@ -279,9 +289,9 @@ if s:t_Co >= 256
hi TabLine ctermfg=232 ctermbg=248 cterm=NONE
hi TabLineFill ctermfg=232 ctermbg=188 cterm=NONE
hi TabLineSel ctermfg=255 ctermbg=232 cterm=bold
- hi Title ctermfg=232 ctermbg=NONE cterm=NONE
+ hi Title ctermfg=NONE ctermbg=NONE cterm=NONE
hi VertSplit ctermfg=241 ctermbg=188 cterm=NONE
- hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi Visual ctermfg=214 ctermbg=232 cterm=reverse
hi VisualNOS ctermfg=NONE ctermbg=255 cterm=NONE
hi WarningMsg ctermfg=232 ctermbg=NONE cterm=NONE
hi WildMenu ctermfg=232 ctermbg=255 cterm=bold
@@ -348,7 +358,7 @@ if s:t_Co >= 16
hi TabLineSel ctermfg=NONE ctermbg=NONE cterm=bold,reverse
hi Title ctermfg=NONE ctermbg=NONE cterm=NONE
hi VertSplit ctermfg=NONE ctermbg=NONE cterm=NONE
- hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi Visual ctermfg=darkyellow ctermbg=black cterm=reverse
hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=NONE
hi WarningMsg ctermfg=NONE ctermbg=NONE cterm=standout
hi WildMenu ctermfg=NONE ctermbg=NONE cterm=bold
@@ -410,7 +420,7 @@ if s:t_Co >= 16
hi TabLineSel ctermfg=NONE ctermbg=NONE cterm=bold,reverse
hi Title ctermfg=NONE ctermbg=NONE cterm=NONE
hi VertSplit ctermfg=NONE ctermbg=NONE cterm=NONE
- hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi Visual ctermfg=darkyellow ctermbg=black cterm=reverse
hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=NONE
hi WarningMsg ctermfg=NONE ctermbg=NONE cterm=standout
hi WildMenu ctermfg=NONE ctermbg=NONE cterm=bold
@@ -477,7 +487,7 @@ if s:t_Co >= 8
hi TabLineSel ctermfg=NONE ctermbg=NONE cterm=bold,reverse
hi Title ctermfg=NONE ctermbg=NONE cterm=NONE
hi VertSplit ctermfg=NONE ctermbg=NONE cterm=NONE
- hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi Visual ctermfg=darkyellow ctermbg=black cterm=reverse
hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=NONE
hi WarningMsg ctermfg=NONE ctermbg=NONE cterm=standout
hi WildMenu ctermfg=NONE ctermbg=NONE cterm=bold
@@ -539,7 +549,7 @@ if s:t_Co >= 8
hi TabLineSel ctermfg=NONE ctermbg=NONE cterm=bold,reverse
hi Title ctermfg=NONE ctermbg=NONE cterm=NONE
hi VertSplit ctermfg=NONE ctermbg=NONE cterm=NONE
- hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi Visual ctermfg=darkyellow ctermbg=black cterm=reverse
hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=NONE
hi WarningMsg ctermfg=NONE ctermbg=NONE cterm=standout
hi WildMenu ctermfg=NONE ctermbg=NONE cterm=bold
@@ -650,7 +660,7 @@ endif
" Color: diffred #d75f5f 167 darkred
" Color: diffgreen #00af00 34 darkgreen
" Color: diffblue #87afd7 110 darkblue
-" Color: diffpink #d787d7 176 darkmagenta
+" Color: diffpink #d787d7 176 darkmagenta
" Color: uipink #ff00af 199 magenta
" Color: uilime #afff00 154 green
" Color: uiteal #00ffaf 49 green
diff --git a/runtime/colors/ron.vim b/runtime/colors/ron.vim
index a529d6c199..d3a692a69f 100644
--- a/runtime/colors/ron.vim
+++ b/runtime/colors/ron.vim
@@ -3,7 +3,7 @@
" Maintainer: original maintainer Ron Aaron <ron@ronware.org>
" Website: https://www.github.com/vim/colorschemes
" License: Same as Vim
-" Last Updated: Mon Aug 8 15:21:18 2022
+" Last Updated: Fri 02 Sep 2022 09:50:56 MSK
" Generated by Colortemplate v2.2.0
@@ -12,7 +12,7 @@ set background=dark
hi clear
let g:colors_name = 'ron'
-let s:t_Co = exists('&t_Co') ? (&t_Co ? &t_Co : 0) : -1
+let s:t_Co = &t_Co
hi! link Terminal Normal
hi! link Boolean Constant
@@ -46,9 +46,15 @@ hi! link CursorLineFold CursorLine
hi! link CursorLineSign CursorLine
hi! link LineNrAbove LineNr
hi! link LineNrBelow LineNr
+hi! link MessageWindow Pmenu
+hi! link PopupNotification Todo
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']
+ " Nvim uses g:terminal_color_{0-15} instead
+ for i in range(g:terminal_ansi_colors->len())
+ let g:terminal_color_{i} = g:terminal_ansi_colors[i]
+ endfor
endif
hi Normal guifg=#00ffff guibg=#000000 gui=NONE cterm=NONE
hi ColorColumn guifg=NONE guibg=#cd0000 gui=NONE cterm=NONE
diff --git a/runtime/colors/shine.vim b/runtime/colors/shine.vim
index b84be280e1..b30ac415d0 100644
--- a/runtime/colors/shine.vim
+++ b/runtime/colors/shine.vim
@@ -4,7 +4,7 @@
" Maintainer: Original maintainer is Yasuhiro Matsumoto <mattn@mail.goo.ne.jp>
" Website: https://github.com/vim/colorschemes
" License: Same as Vim
-" Last Updated: Mon Aug 8 15:21:19 2022
+" Last Updated: Fri 02 Sep 2022 09:51:42 MSK
" Generated by Colortemplate v2.2.0
@@ -13,10 +13,14 @@ set background=light
hi clear
let g:colors_name = 'shine'
-let s:t_Co = exists('&t_Co') ? (&t_Co ? &t_Co : 0) : -1
+let s:t_Co = &t_Co
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']
+ " Nvim uses g:terminal_color_{0-15} instead
+ for i in range(g:terminal_ansi_colors->len())
+ let g:terminal_color_{i} = g:terminal_ansi_colors[i]
+ endfor
endif
hi! link Terminal Normal
hi! link LineNrAbove LineNr
@@ -28,6 +32,8 @@ hi! link EndOfBuffer NonText
hi! link ErrorMsg Error
hi! link Tag Special
hi! link Operator Statement
+hi! link MessageWindow Pmenu
+hi! link PopupNotification Todo
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
@@ -104,6 +110,8 @@ if s:t_Co >= 256
hi! link ErrorMsg Error
hi! link Tag Special
hi! link Operator Statement
+ hi! link MessageWindow Pmenu
+ hi! link PopupNotification Todo
hi Normal ctermfg=16 ctermbg=231 cterm=NONE
hi Folded ctermfg=18 ctermbg=253 cterm=NONE
hi CursorLine ctermfg=NONE ctermbg=253 cterm=NONE
diff --git a/runtime/colors/slate.vim b/runtime/colors/slate.vim
index 49dbc6387c..6da572d9a7 100644
--- a/runtime/colors/slate.vim
+++ b/runtime/colors/slate.vim
@@ -4,7 +4,7 @@
" Maintainer: Original maintainer Ralph Amissah <ralph@amissah.com>
" Website: https://github.com/vim/colorschemes
" License: Same as Vim
-" Last Updated: Tue Aug 16 08:11:08 2022
+" Last Updated: Fri 02 Sep 2022 09:52:25 MSK
" Generated by Colortemplate v2.2.0
@@ -13,10 +13,14 @@ set background=dark
hi clear
let g:colors_name = 'slate'
-let s:t_Co = exists('&t_Co') ? (&t_Co ? &t_Co : 0) : -1
+let s:t_Co = &t_Co
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']
+ " Nvim uses g:terminal_color_{0-15} instead
+ for i in range(g:terminal_ansi_colors->len())
+ let g:terminal_color_{i} = g:terminal_ansi_colors[i]
+ endfor
endif
hi! link Terminal Normal
hi! link LineNrAbove LineNr
@@ -24,6 +28,8 @@ hi! link LineNrBelow LineNr
hi! link CurSearch Search
hi! link CursorLineFold CursorLine
hi! link CursorLineSign CursorLine
+hi! link MessageWindow Pmenu
+hi! link PopupNotification Todo
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
@@ -99,6 +105,8 @@ if s:t_Co >= 256
hi! link CurSearch Search
hi! link CursorLineFold CursorLine
hi! link CursorLineSign CursorLine
+ hi! link MessageWindow Pmenu
+ hi! link PopupNotification Todo
hi Normal ctermfg=231 ctermbg=235 cterm=NONE
hi EndOfBuffer ctermfg=68 ctermbg=NONE cterm=NONE
hi StatusLine ctermfg=16 ctermbg=144 cterm=NONE
diff --git a/runtime/colors/torte.vim b/runtime/colors/torte.vim
index ce36507ae8..bec681bb4e 100644
--- a/runtime/colors/torte.vim
+++ b/runtime/colors/torte.vim
@@ -4,7 +4,7 @@
" Maintainer: Original maintainer Thorsten Maerz <info@netztorte.de>
" Website: https://github.com/vim/colorschemes
" License: Same as Vim
-" Last Updated: Mon Aug 8 15:21:22 2022
+" Last Updated: Fri 02 Sep 2022 09:53:21 MSK
" Generated by Colortemplate v2.2.0
@@ -13,10 +13,14 @@ set background=dark
hi clear
let g:colors_name = 'torte'
-let s:t_Co = exists('&t_Co') ? (&t_Co ? &t_Co : 0) : -1
+let s:t_Co = &t_Co
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']
+ " Nvim uses g:terminal_color_{0-15} instead
+ for i in range(g:terminal_ansi_colors->len())
+ let g:terminal_color_{i} = g:terminal_ansi_colors[i]
+ endfor
endif
hi! link Terminal Normal
hi! link LineNrAbove LineNr
@@ -26,6 +30,8 @@ hi! link CursorLineFold CursorLine
hi! link CursorLineSign CursorLine
hi! link StatusLineTerm StatusLine
hi! link StatusLineTermNC StatusLineNC
+hi! link MessageWindow Pmenu
+hi! link PopupNotification Todo
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
@@ -97,6 +103,8 @@ if s:t_Co >= 256
hi! link CursorLineSign CursorLine
hi! link StatusLineTerm StatusLine
hi! link StatusLineTermNC StatusLineNC
+ hi! link MessageWindow Pmenu
+ hi! link PopupNotification Todo
hi Normal ctermfg=251 ctermbg=16 cterm=NONE
hi Comment ctermfg=111 ctermbg=NONE cterm=NONE
hi Constant ctermfg=217 ctermbg=NONE cterm=NONE
diff --git a/runtime/colors/zellner.vim b/runtime/colors/zellner.vim
index 135591052c..af48ef86dc 100644
--- a/runtime/colors/zellner.vim
+++ b/runtime/colors/zellner.vim
@@ -4,7 +4,7 @@
" Maintainer: Original maintainer Ron Aaron <ron@ronware.org>
" Website: https://github.com/vim/colorschemes
" License: Same as Vim
-" Last Updated: Mon Aug 8 15:21:23 2022
+" Last Updated: Fri 02 Sep 2022 09:54:15 MSK
" Generated by Colortemplate v2.2.0
@@ -13,10 +13,14 @@ set background=light
hi clear
let g:colors_name = 'zellner'
-let s:t_Co = exists('&t_Co') ? (&t_Co ? &t_Co : 0) : -1
+let s:t_Co = &t_Co
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']
+ " Nvim uses g:terminal_color_{0-15} instead
+ for i in range(g:terminal_ansi_colors->len())
+ let g:terminal_color_{i} = g:terminal_ansi_colors[i]
+ endfor
endif
hi! link Terminal Normal
hi! link LineNrAbove LineNr
@@ -24,6 +28,8 @@ hi! link LineNrBelow LineNr
hi! link CurSearch Search
hi! link CursorLineFold CursorLine
hi! link CursorLineSign CursorLine
+hi! link MessageWindow Pmenu
+hi! link PopupNotification Todo
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
@@ -95,6 +101,8 @@ if s:t_Co >= 256
hi! link CurSearch Search
hi! link CursorLineFold CursorLine
hi! link CursorLineSign CursorLine
+ hi! link MessageWindow Pmenu
+ hi! link PopupNotification Todo
hi Normal ctermfg=16 ctermbg=231 cterm=NONE
hi Folded ctermfg=18 ctermbg=252 cterm=NONE
hi CursorLine ctermfg=NONE ctermbg=254 cterm=NONE
diff --git a/runtime/compiler/dotnet.vim b/runtime/compiler/dotnet.vim
new file mode 100644
index 0000000000..ac64084663
--- /dev/null
+++ b/runtime/compiler/dotnet.vim
@@ -0,0 +1,39 @@
+" Vim compiler file
+" Compiler: dotnet build (.NET CLI)
+" Maintainer: Nick Jensen <nickspoon@gmail.com>
+" Last Change: 2022-12-06
+" License: Vim (see :h license)
+" Repository: https://github.com/nickspoons/vim-cs
+
+if exists("current_compiler")
+ finish
+endif
+let current_compiler = "dotnet"
+
+if exists(":CompilerSet") != 2 " older Vim always used :setlocal
+ command -nargs=* CompilerSet setlocal <args>
+endif
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+if get(g:, "dotnet_errors_only", v:false)
+ CompilerSet makeprg=dotnet\ build\ -nologo
+ \\ -consoleloggerparameters:NoSummary
+ \\ -consoleloggerparameters:ErrorsOnly
+else
+ CompilerSet makeprg=dotnet\ build\ -nologo\ -consoleloggerparameters:NoSummary
+endif
+
+if get(g:, "dotnet_show_project_file", v:true)
+ CompilerSet errorformat=%E%f(%l\\,%c):\ %trror\ %m,
+ \%W%f(%l\\,%c):\ %tarning\ %m,
+ \%-G%.%#
+else
+ CompilerSet errorformat=%E%f(%l\\,%c):\ %trror\ %m\ [%.%#],
+ \%W%f(%l\\,%c):\ %tarning\ %m\ [%.%#],
+ \%-G%.%#
+endif
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
diff --git a/runtime/compiler/hare.vim b/runtime/compiler/hare.vim
new file mode 100644
index 0000000000..c0fa68cc00
--- /dev/null
+++ b/runtime/compiler/hare.vim
@@ -0,0 +1,31 @@
+" Vim compiler file
+" Compiler: Hare Compiler
+" Maintainer: Amelia Clarke <me@rsaihe.dev>
+" Last Change: 2022-09-21
+
+if exists("g:current_compiler")
+ finish
+endif
+let g:current_compiler = "hare"
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+if exists(':CompilerSet') != 2
+ command -nargs=* CompilerSet setlocal <args>
+endif
+
+if filereadable("Makefile") || filereadable("makefile")
+ CompilerSet makeprg=make
+else
+ CompilerSet makeprg=hare\ build
+endif
+
+CompilerSet errorformat=
+ \Error\ %f:%l:%c:\ %m,
+ \Syntax\ error:\ %.%#\ at\ %f:%l:%c\\,\ %m,
+ \%-G%.%#
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
+" vim: tabstop=2 shiftwidth=2 expandtab
diff --git a/runtime/compiler/raco.vim b/runtime/compiler/raco.vim
new file mode 100644
index 0000000000..bd10859aa9
--- /dev/null
+++ b/runtime/compiler/raco.vim
@@ -0,0 +1,14 @@
+" Vim compiler file
+" Compiler: raco (Racket command-line tools)
+" Maintainer: D. Ben Knoble <ben.knoble+github@gmail.com>
+" URL: https://github.com/benknoble/vim-racket
+" Last Change: 2022 Aug 12
+
+let current_compiler = 'raco'
+
+if exists(":CompilerSet") != 2
+ command -nargs=* CompilerSet setlocal <args>
+endif
+
+CompilerSet makeprg=raco
+CompilerSet errorformat=%f:%l:%c:%m
diff --git a/runtime/compiler/racomake.vim b/runtime/compiler/racomake.vim
new file mode 100644
index 0000000000..dae95fec42
--- /dev/null
+++ b/runtime/compiler/racomake.vim
@@ -0,0 +1,14 @@
+" Vim compiler file
+" Compiler: raco make (Racket command-line tools)
+" Maintainer: D. Ben Knoble <ben.knoble+github@gmail.com>
+" URL: https://github.com/benknoble/vim-racket
+" Last Change: 2022 Aug 12
+
+let current_compiler = 'racomake'
+
+if exists(":CompilerSet") != 2
+ command -nargs=* CompilerSet setlocal <args>
+endif
+
+CompilerSet makeprg=raco\ make\ --\ %
+CompilerSet errorformat=%f:%l:%c:%m
diff --git a/runtime/compiler/racosetup.vim b/runtime/compiler/racosetup.vim
new file mode 100644
index 0000000000..1efe8a15a2
--- /dev/null
+++ b/runtime/compiler/racosetup.vim
@@ -0,0 +1,14 @@
+" Vim compiler file
+" Compiler: raco setup (Racket command-line tools)
+" Maintainer: D. Ben Knoble <ben.knoble+github@gmail.com>
+" URL: https://github.com/benknoble/vim-racket
+" Last Change: 2022 Aug 12
+
+let current_compiler = 'racosetup'
+
+if exists(":CompilerSet") != 2
+ command -nargs=* CompilerSet setlocal <args>
+endif
+
+CompilerSet makeprg=raco\ setup
+CompilerSet errorformat=%f:%l:%c:%m
diff --git a/runtime/compiler/racotest.vim b/runtime/compiler/racotest.vim
new file mode 100644
index 0000000000..d2a1a3c0f3
--- /dev/null
+++ b/runtime/compiler/racotest.vim
@@ -0,0 +1,14 @@
+" Vim compiler file
+" Compiler: raco test (Racket command-line tools)
+" Maintainer: D. Ben Knoble <ben.knoble+github@gmail.com>
+" URL: https://github.com/benknoble/vim-racket
+" Last Change: 2022 Aug 12
+
+let current_compiler = 'racotest'
+
+if exists(":CompilerSet") != 2
+ command -nargs=* CompilerSet setlocal <args>
+endif
+
+CompilerSet makeprg=raco\ test\ %
+CompilerSet errorformat=location:%f:%l:%c
diff --git a/runtime/compiler/zig.vim b/runtime/compiler/zig.vim
new file mode 100644
index 0000000000..2cc6831329
--- /dev/null
+++ b/runtime/compiler/zig.vim
@@ -0,0 +1,28 @@
+" Vim compiler file
+" Compiler: Zig Compiler
+" Upstream: https://github.com/ziglang/zig.vim
+
+if exists("current_compiler")
+ finish
+endif
+let current_compiler = "zig"
+
+let s:save_cpo = &cpo
+set cpo&vim
+
+if exists(":CompilerSet") != 2
+ command -nargs=* CompilerSet setlocal <args>
+endif
+
+" a subcommand must be provided for the this compiler (test, build-exe, etc)
+if has('patch-7.4.191')
+ CompilerSet makeprg=zig\ \$*\ \%:S
+else
+ CompilerSet makeprg=zig\ \$*\ \"%\"
+endif
+
+" TODO: improve errorformat as needed.
+
+let &cpo = s:save_cpo
+unlet s:save_cpo
+" vim: tabstop=8 shiftwidth=4 softtabstop=4 expandtab
diff --git a/runtime/compiler/zig_build.vim b/runtime/compiler/zig_build.vim
new file mode 100644
index 0000000000..0441267b64
--- /dev/null
+++ b/runtime/compiler/zig_build.vim
@@ -0,0 +1,29 @@
+" Vim compiler file
+" Compiler: Zig Compiler (zig build)
+" Upstream: https://github.com/ziglang/zig.vim
+
+if exists('current_compiler')
+ finish
+endif
+runtime compiler/zig.vim
+let current_compiler = 'zig_build'
+
+let s:save_cpo = &cpo
+set cpo&vim
+
+
+if exists(':CompilerSet') != 2
+ command -nargs=* CompilerSet setlocal <args>
+endif
+
+if exists('g:zig_build_makeprg_params')
+ execute 'CompilerSet makeprg=zig\ build\ '.escape(g:zig_build_makeprg_params, ' \|"').'\ $*'
+else
+ CompilerSet makeprg=zig\ build\ $*
+endif
+
+" TODO: anything to add to errorformat for zig build specifically?
+
+let &cpo = s:save_cpo
+unlet s:save_cpo
+" vim: tabstop=8 shiftwidth=4 softtabstop=4 expandtab
diff --git a/runtime/compiler/zig_build_exe.vim b/runtime/compiler/zig_build_exe.vim
new file mode 100644
index 0000000000..20f0bb3366
--- /dev/null
+++ b/runtime/compiler/zig_build_exe.vim
@@ -0,0 +1,27 @@
+" Vim compiler file
+" Compiler: Zig Compiler (zig build-exe)
+" Upstream: https://github.com/ziglang/zig.vim
+
+if exists('current_compiler')
+ finish
+endif
+runtime compiler/zig.vim
+let current_compiler = 'zig_build_exe'
+
+let s:save_cpo = &cpo
+set cpo&vim
+
+
+if exists(':CompilerSet') != 2
+ command -nargs=* CompilerSet setlocal <args>
+endif
+
+if has('patch-7.4.191')
+ CompilerSet makeprg=zig\ build-exe\ \%:S\ \$*
+else
+ CompilerSet makeprg=zig\ build-exe\ \"%\"\ \$*
+endif
+
+let &cpo = s:save_cpo
+unlet s:save_cpo
+" vim: tabstop=8 shiftwidth=4 softtabstop=4 expandtab
diff --git a/runtime/compiler/zig_test.vim b/runtime/compiler/zig_test.vim
new file mode 100644
index 0000000000..a82d2a6378
--- /dev/null
+++ b/runtime/compiler/zig_test.vim
@@ -0,0 +1,27 @@
+" Vim compiler file
+" Compiler: Zig Compiler (zig test)
+" Upstream: https://github.com/ziglang/zig.vim
+
+if exists('current_compiler')
+ finish
+endif
+runtime compiler/zig.vim
+let current_compiler = 'zig_test'
+
+let s:save_cpo = &cpo
+set cpo&vim
+
+
+if exists(':CompilerSet') != 2
+ command -nargs=* CompilerSet setlocal <args>
+endif
+
+if has('patch-7.4.191')
+ CompilerSet makeprg=zig\ test\ \%:S\ \$*
+else
+ CompilerSet makeprg=zig\ test\ \"%\"\ \$*
+endif
+
+let &cpo = s:save_cpo
+unlet s:save_cpo
+" vim: tabstop=8 shiftwidth=4 softtabstop=4 expandtab
diff --git a/runtime/doc/Makefile b/runtime/doc/Makefile
deleted file mode 100644
index 18d32c0820..0000000000
--- a/runtime/doc/Makefile
+++ /dev/null
@@ -1,40 +0,0 @@
-#
-# Makefile for the Vim documentation on Unix
-#
-# If you get "don't know how to make scratch", first run make in the source
-# directory. Or remove the include below.
-
-AWK = awk
-
-DOCS = $(wildcard *.txt)
-HTMLS = $(DOCS:.txt=.html)
-
-.SUFFIXES:
-.SUFFIXES: .c .o .txt .html
-
-# Awk version of .txt to .html conversion.
-html: noerrors vimindex.html $(HTMLS)
- @if test -f errors.log; then cat errors.log; fi
-
-noerrors:
- -rm -f errors.log
-
-$(HTMLS): tags.ref
-
-.txt.html:
- $(AWK) -f makehtml.awk $< >$@
-
-# index.html is the starting point for HTML, but for the help files it is
-# help.txt. Therefore use vimindex.html for index.txt.
-index.html: help.txt
- $(AWK) -f makehtml.awk help.txt >index.html
-
-vimindex.html: index.txt
- $(AWK) -f makehtml.awk index.txt >vimindex.html
-
-tags.ref tags.html: tags
- $(AWK) -f maketags.awk tags >tags.html
-
-clean:
- -rm -f *.html tags.ref $(HTMLS) errors.log tags
-
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index a388592981..0e1cc3c28c 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -51,7 +51,7 @@ Connecting to the socket is the easiest way a programmer can test the API,
which can be done through any msgpack-rpc client library or full-featured
|api-client|. Here's a Ruby script that prints "hello world!" in the current
Nvim instance:
->
+>ruby
#!/usr/bin/env ruby
# Requires msgpack-rpc: gem install msgpack-rpc
#
@@ -79,10 +79,11 @@ functions can be called interactively:
<
You can also embed Nvim via |jobstart()|, and communicate using |rpcrequest()|
and |rpcnotify()|:
->
+>vim
let nvim = jobstart(['nvim', '--embed'], {'rpc': v:true})
echo rpcrequest(nvim, 'nvim_eval', '"Hello " . "world!"')
call jobstop(nvim)
+<
==============================================================================
API Definitions *api-definitions*
@@ -92,7 +93,7 @@ The Nvim C API defines custom types for all function parameters. Some are just
typedefs around C99 standard types, others are Nvim-defined data structures.
Basic types ~
-
+>
API Type C type
------------------------------------------------------------------------
Nil
@@ -103,7 +104,7 @@ Basic types ~
Array
Dictionary (msgpack: map)
Object
-
+<
Note: empty Array is accepted as a valid argument for Dictionary parameter.
Special types (msgpack EXT) ~
@@ -115,13 +116,13 @@ Special types (msgpack EXT) ~
The EXT object data is the (integer) object handle. The EXT type codes given
in the |api-metadata| `types` key are stable: they will not change and are
thus forward-compatible.
-
+>
EXT Type C type Data
------------------------------------------------------------------------
Buffer enum value kObjectTypeBuffer |bufnr()|
Window enum value kObjectTypeWindow |window-ID|
Tabpage enum value kObjectTypeTabpage internal handle
-
+<
*api-indexing*
Most of the API uses 0-based indices, and ranges are end-exclusive. For the
@@ -130,19 +131,19 @@ end of a range, -1 denotes the last line/column.
Exception: the following API functions use "mark-like" indexing (1-based
lines, 0-based columns):
- |nvim_get_mark()|
- |nvim_buf_get_mark()|
- |nvim_buf_set_mark()|
- |nvim_win_get_cursor()|
- |nvim_win_set_cursor()|
+- |nvim_get_mark()|
+- |nvim_buf_get_mark()|
+- |nvim_buf_set_mark()|
+- |nvim_win_get_cursor()|
+- |nvim_win_set_cursor()|
Exception: the following API functions use |extmarks| indexing (0-based
indices, end-inclusive):
- |nvim_buf_del_extmark()|
- |nvim_buf_get_extmark_by_id()|
- |nvim_buf_get_extmarks()|
- |nvim_buf_set_extmark()|
+- |nvim_buf_del_extmark()|
+- |nvim_buf_get_extmark_by_id()|
+- |nvim_buf_get_extmarks()|
+- |nvim_buf_set_extmark()|
*api-fast*
Most API functions are "deferred": they are queued on the main loop and
@@ -162,19 +163,19 @@ and return values.
Nvim exposes its API metadata as a Dictionary with these items:
-version Nvim version, API level/compatibility
-version.api_level API version integer *api-level*
-version.api_compatible API is backwards-compatible with this level
-version.api_prerelease Declares the API as unstable/unreleased >
- (version.api_prerelease && fn.since == version.api_level)
-functions API function signatures, containing |api-types| info
- describing the return value and parameters.
-ui_events |UI| event signatures
-ui_options Supported |ui-option|s
-{fn}.since API level where function {fn} was introduced
-{fn}.deprecated_since API level where function {fn} was deprecated
-types Custom handle types defined by Nvim
-error_types Possible error types returned by API functions
+- version Nvim version, API level/compatibility
+- version.api_level API version integer *api-level*
+- version.api_compatible API is backwards-compatible with this level
+- version.api_prerelease Declares the API as unstable/unreleased
+ `(version.api_prerelease && fn.since == version.api_level)`
+- functions API function signatures, containing |api-types| info
+ describing the return value and parameters.
+- ui_events |UI| event signatures
+- ui_options Supported |ui-option|s
+- {fn}.since API level where function {fn} was introduced
+- {fn}.deprecated_since API level where function {fn} was deprecated
+- types Custom handle types defined by Nvim
+- error_types Possible error types returned by API functions
About the `functions` map:
@@ -200,9 +201,9 @@ any of these approaches:
Example (requires Python "pyyaml" and "msgpack-python" modules): >
nvim --api-info | python -c 'import msgpack, sys, yaml; yaml.dump(msgpack.unpackb(sys.stdin.buffer.read()), sys.stdout)'
<
- 3. Use the |api_info()| Vimscript function. >
+ 3. Use the |api_info()| Vimscript function. >vim
:lua print(vim.inspect(vim.fn.api_info()))
-< Example using |filter()| to exclude non-deprecated API functions: >
+< Example using |filter()| to exclude non-deprecated API functions: >vim
:new|put =map(filter(api_info().functions, '!has_key(v:val,''deprecated_since'')'), 'v:val.name')
==============================================================================
@@ -230,6 +231,15 @@ As Nvim evolves the API may change in compliance with this CONTRACT:
- Existing items will not be removed (after release).
- Deprecated functions will not be removed until Nvim version 2.0
+"Private" interfaces are NOT covered by this contract:
+
+- Undocumented (not in :help) functions or events of any kind
+- nvim__x ("double underscore") functions
+
+The idea is "versionless evolution", in the words of Rich Hickey:
+- Relaxing a requirement should be a compatible change.
+- Strengthening a promise should be a compatible change.
+
==============================================================================
Global events *api-global-events*
@@ -345,16 +355,16 @@ LUA ~
In-process Lua plugins can receive buffer updates in the form of Lua
callbacks. These callbacks are called frequently in various contexts;
|textlock| prevents changing buffer contents and window layout (use
-|vim.schedule| to defer such operations to the main loop instead).
+|vim.schedule()| to defer such operations to the main loop instead).
|nvim_buf_attach()| will take keyword args for the callbacks. "on_lines" will
receive parameters ("lines", {buf}, {changedtick}, {firstline}, {lastline},
{new_lastline}, {old_byte_size} [, {old_utf32_size}, {old_utf16_size}]).
Unlike remote channel events the text contents are not passed. The new text can
-be accessed inside the callback as
-
- `vim.api.nvim_buf_get_lines(buf, firstline, new_lastline, true)`
+be accessed inside the callback as >lua
+ vim.api.nvim_buf_get_lines(buf, firstline, new_lastline, true)
+<
{old_byte_size} is the total size of the replaced region {firstline} to
{lastline} in bytes, including the final newline after {lastline}. if
`utf_sizes` is set to true in |nvim_buf_attach()| keyword args, then the
@@ -390,7 +400,7 @@ performance can be improved by calling |nvim_buf_add_highlight()| as an
asynchronous notification, after first (synchronously) requesting a source id.
Example using the Python API client (|pynvim|):
->
+>python
src = vim.new_highlight_source()
buf = vim.current.buffer
for i in range(5):
@@ -404,7 +414,7 @@ clear highlights from a specific source, in a specific line range or the
entire buffer by passing in the line range 0, -1 (the latter is the default in
python as used above).
-Example using the API from Vimscript: >
+Example using the API from Vimscript: >vim
call nvim_buf_set_lines(0, 0, 0, v:true, ["test text"])
let src = nvim_buf_add_highlight(0, 0, "String", 1, 0, 4)
@@ -428,12 +438,12 @@ 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: >
+its config is non-empty: >lua
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;
@@ -446,7 +456,7 @@ 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: >
+Example: create a float with scratch buffer: >vim
let buf = nvim_create_buf(v:false, v:true)
call nvim_buf_set_lines(buf, 0, -1, v:true, ["test", "text"])
@@ -455,10 +465,10 @@ Example: create a float with scratch buffer: >
let win = nvim_open_win(buf, 0, opts)
" optional: change highlight, otherwise Pmenu is used
call nvim_win_set_option(win, 'winhl', 'Normal:MyHighlight')
->
+<
==============================================================================
-Extended marks *api-extended-marks* *extmarks*
+Extended marks *api-extended-marks* *extmarks* *extmark*
Extended marks (extmarks) represent buffer annotations that track text changes
in the buffer. They can represent cursors, folds, misspelled words, anything
@@ -499,30 +509,32 @@ Let's set an extmark at the first row (row=0) and third column (column=2).
01 2345678
0 ex|ample..
-< ^ extmark position
->
+ ^ extmark position
+<
+>vim
let g:mark_ns = nvim_create_namespace('myplugin')
let g:mark_id = nvim_buf_set_extmark(0, g:mark_ns, 0, 2, {})
<
-We can get the mark by its id: >
+We can get the mark by its id: >vim
echo nvim_buf_get_extmark_by_id(0, g:mark_ns, g:mark_id, {})
- => [0, 2]
+ " => [0, 2]
-We can get all marks in a buffer by |namespace| (or by a range): >
+We can get all marks in a buffer by |namespace| (or by a range): >vim
echo nvim_buf_get_extmarks(0, g:mark_ns, 0, -1, {})
- => [[1, 0, 2]]
+ " => [[1, 0, 2]]
Deleting all surrounding text does NOT remove an extmark! To remove extmarks
use |nvim_buf_del_extmark()|. Deleting "x" in our example: >
0 12345678
0 e|ample..
-< ^ extmark position
->
+ ^ extmark position
+<
+>vim
echo nvim_buf_get_extmark_by_id(0, g:mark_ns, g:mark_id, {})
- => [0, 1]
+ " => [0, 1]
<
Note: Extmark "gravity" decides how it will shift after a text edit.
See |nvim_buf_set_extmark()|
@@ -544,9 +556,9 @@ nvim__get_runtime({pat}, {all}, {*opts}) *nvim__get_runtime()*
|api-fast|
Parameters: ~
- {pat} pattern of files to search for
- {all} whether to return all matches or only the first
- {opts} 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
@@ -558,7 +570,7 @@ nvim__id({obj}) *nvim__id()*
in plugins.
Parameters: ~
- {obj} Object to return.
+ • {obj} Object to return.
Return: ~
its argument.
@@ -570,7 +582,7 @@ nvim__id_array({arr}) *nvim__id_array()*
in plugins.
Parameters: ~
- {arr} Array to return.
+ • {arr} Array to return.
Return: ~
its argument.
@@ -582,7 +594,7 @@ nvim__id_dictionary({dct}) *nvim__id_dictionary()*
in plugins.
Parameters: ~
- {dct} Dictionary to return.
+ • {dct} Dictionary to return.
Return: ~
its argument.
@@ -594,7 +606,7 @@ nvim__id_float({flt}) *nvim__id_float()*
in plugins.
Parameters: ~
- {flt} Value to return.
+ • {flt} Value to return.
Return: ~
its argument.
@@ -624,7 +636,7 @@ nvim_call_atomic({calls}) *nvim_call_atomic()*
|RPC| only
Parameters: ~
- {calls} an array of calls, where each call is described by an array
+ • {calls} an array of calls, where each call is described by an array
with two elements: the request name, and an array of
arguments.
@@ -648,18 +660,18 @@ nvim_chan_send({chan}, {data}) *nvim_chan_send()*
Attributes: ~
|RPC| only
- |vim.api| only
+ Lua |vim.api| only
Parameters: ~
- {chan} id of the channel
- {data} data to write. 8-bit clean: can contain NUL bytes.
+ • {chan} id of the channel
+ • {data} data to write. 8-bit clean: can contain NUL bytes.
nvim_create_buf({listed}, {scratch}) *nvim_create_buf()*
Creates a new, empty, unnamed buffer.
Parameters: ~
- {listed} Sets 'buflisted'
- {scratch} Creates a "throwaway" |scratch-buffer| for temporary work
+ • {listed} Sets 'buflisted'
+ • {scratch} Creates a "throwaway" |scratch-buffer| for temporary work
(always 'nomodified'). Also sets 'nomodeline' on the
buffer.
@@ -690,7 +702,7 @@ nvim_del_mark({name}) *nvim_del_mark()*
fails with error if a lowercase or buffer local named mark is used.
Parameters: ~
- {name} Mark name
+ • {name} Mark name
Return: ~
true if the mark was deleted, else false.
@@ -703,31 +715,35 @@ nvim_del_var({name}) *nvim_del_var()*
Removes a global (g:) variable.
Parameters: ~
- {name} Variable name
+ • {name} Variable name
-nvim_echo({chunks}, {history}, {opts}) *nvim_echo()*
+nvim_echo({chunks}, {history}, {*opts}) *nvim_echo()*
Echo a message.
Parameters: ~
- {chunks} A list of [text, hl_group] arrays, each representing a text
+ • {chunks} A list of [text, hl_group] arrays, each representing a text
chunk with specified highlight. `hl_group` element can be
omitted for no highlight.
- {history} if true, add to |message-history|.
- {opts} Optional parameters. Reserved for future use.
+ • {history} if true, add to |message-history|.
+ • {opts} Optional parameters.
+ • verbose: Message was printed as a result of 'verbose'
+ option if Nvim was invoked with -V3log_file, the message
+ will be redirected to the log_file and suppressed from
+ direct output.
nvim_err_write({str}) *nvim_err_write()*
Writes a message to the Vim error buffer. Does not append "\n", the
message is buffered (won't display) until a linefeed is written.
Parameters: ~
- {str} Message
+ • {str} Message
nvim_err_writeln({str}) *nvim_err_writeln()*
Writes a message to the Vim error buffer. Appends "\n", so the buffer is
flushed (and displayed).
Parameters: ~
- {str} Message
+ • {str} Message
See also: ~
nvim_err_write()
@@ -739,8 +755,8 @@ nvim_eval_statusline({str}, {*opts}) *nvim_eval_statusline()*
|api-fast|
Parameters: ~
- {str} Statusline string (see 'statusline').
- {opts} Optional parameters.
+ • {str} Statusline string (see 'statusline').
+ • {opts} Optional parameters.
• winid: (number) |window-ID| of the window to use as context
for statusline.
• maxwidth: (number) Maximum width of statusline.
@@ -750,7 +766,7 @@ nvim_eval_statusline({str}, {*opts}) *nvim_eval_statusline()*
• 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
+ statusline. When true, {winid} is ignored. Mutually
exclusive with {use_winbar}.
Return: ~
@@ -759,7 +775,7 @@ nvim_eval_statusline({str}, {*opts}) *nvim_eval_statusline()*
• width: (number) Display width of the statusline.
• highlights: Array containing highlight information of the
statusline. Only included when the "highlights" key in {opts} is
- |TRUE|. Each element of the array is a |Dictionary| with these keys:
+ true. Each element of the array is a |Dictionary| with these keys:
• start: (number) Byte index (0-based) of first character that uses
the highlight.
• group: (string) Name of highlight group.
@@ -775,8 +791,8 @@ nvim_exec_lua({code}, {args}) *nvim_exec_lua()*
|RPC| only
Parameters: ~
- {code} Lua code to execute
- {args} Arguments to the code
+ • {code} Lua code to execute
+ • {args} Arguments to the code
Return: ~
Return value of Lua code if present or NIL.
@@ -791,15 +807,15 @@ nvim_feedkeys({keys}, {mode}, {escape_ks}) *nvim_feedkeys()*
with escape_ks=false) to replace |keycodes|, then pass the result to
nvim_feedkeys().
- Example: >
+ Example: >vim
:let key = nvim_replace_termcodes("<C-o>", v:true, v:false, v:true)
:call nvim_feedkeys(key, 'n', v:false)
<
Parameters: ~
- {keys} to be typed
- {mode} behavior flags, see |feedkeys()|
- {escape_ks} If true, escape K_SPECIAL bytes in `keys` This should be
+ • {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.
@@ -847,13 +863,13 @@ nvim_get_color_by_name({name}) *nvim_get_color_by_name()*
Returns the 24-bit RGB value of a |nvim_get_color_map()| color name or
"#rrggbb" hexadecimal string.
- Example: >
+ Example: >vim
:echo nvim_get_color_by_name("Pink")
:echo nvim_get_color_by_name("#cbcbcb")
<
Parameters: ~
- {name} Color name or "#rrggbb" string
+ • {name} Color name or "#rrggbb" string
Return: ~
24-bit RGB value, or -1 for invalid argument.
@@ -871,7 +887,7 @@ nvim_get_context({*opts}) *nvim_get_context()*
Gets a map of the current editor state.
Parameters: ~
- {opts} Optional parameters.
+ • {opts} Optional parameters.
• types: List of |context-types| ("regs", "jumps", "bufs",
"gvars", …) to gather, or empty for "all".
@@ -906,8 +922,8 @@ nvim_get_hl_by_id({hl_id}, {rgb}) *nvim_get_hl_by_id()*
Gets a highlight definition by id. |hlID()|
Parameters: ~
- {hl_id} Highlight id as returned by |hlID()|
- {rgb} Export RGB colors
+ • {hl_id} Highlight id as returned by |hlID()|
+ • {rgb} Export RGB colors
Return: ~
Highlight definition map
@@ -919,8 +935,8 @@ nvim_get_hl_by_name({name}, {rgb}) *nvim_get_hl_by_name()*
Gets a highlight definition by name.
Parameters: ~
- {name} Highlight group name
- {rgb} Export RGB colors
+ • {name} Highlight group name
+ • {rgb} Export RGB colors
Return: ~
Highlight definition map
@@ -937,7 +953,7 @@ nvim_get_keymap({mode}) *nvim_get_keymap()*
Gets a list of global (non-buffer-local) |mapping| definitions.
Parameters: ~
- {mode} Mode short-name ("n", "i", "v", ...)
+ • {mode} Mode short-name ("n", "i", "v", ...)
Return: ~
Array of |maparg()|-like dictionaries describing mappings. The
@@ -953,8 +969,8 @@ nvim_get_mark({name}, {opts}) *nvim_get_mark()*
fails with error if a lowercase or buffer local named mark is used.
Parameters: ~
- {name} Mark name
- {opts} Optional parameters. Reserved for future use.
+ • {name} Mark name
+ • {opts} Optional parameters. Reserved for future use.
Return: ~
4-tuple (row, col, buffer, buffername), (0, 0, 0, '') if the mark is
@@ -989,7 +1005,7 @@ nvim_get_proc_children({pid}) *nvim_get_proc_children()*
nvim_get_runtime_file({name}, {all}) *nvim_get_runtime_file()*
Find files in runtime directories
- 'name' can contain wildcards. For example
+ "name" can contain wildcards. For example
nvim_get_runtime_file("colors/*.vim", true) will return all color scheme
files. Always use forward slashes (/) in the search pattern for
subdirectories regardless of platform.
@@ -1000,8 +1016,8 @@ nvim_get_runtime_file({name}, {all}) *nvim_get_runtime_file()*
|api-fast|
Parameters: ~
- {name} pattern of files to search for
- {all} whether to return all matches or only the first
+ • {name} pattern of files to search for
+ • {all} whether to return all matches or only the first
Return: ~
list of absolute paths to the found files
@@ -1010,7 +1026,7 @@ nvim_get_var({name}) *nvim_get_var()*
Gets a global (g:) variable.
Parameters: ~
- {name} Variable name
+ • {name} Variable name
Return: ~
Variable value
@@ -1019,7 +1035,7 @@ nvim_get_vvar({name}) *nvim_get_vvar()*
Gets a v: variable.
Parameters: ~
- {name} Variable name
+ • {name} Variable name
Return: ~
Variable value
@@ -1043,7 +1059,7 @@ nvim_input({keys}) *nvim_input()*
|api-fast|
Parameters: ~
- {keys} to be typed
+ • {keys} to be typed
Return: ~
Number of bytes actually written (can be fewer than requested if the
@@ -1067,16 +1083,18 @@ nvim_input_mouse({button}, {action}, {modifier}, {grid}, {row}, {col})
|api-fast|
Parameters: ~
- {button} Mouse button: one of "left", "right", "middle", "wheel".
- {action} For ordinary buttons, one of "press", "drag", "release".
+ • {button} Mouse button: one of "left", "right", "middle", "wheel",
+ "move".
+ • {action} For ordinary buttons, one of "press", "drag", "release".
For the wheel, one of "up", "down", "left", "right".
- {modifier} String of modifiers each represented by a single char. The
+ Ignored for "move".
+ • {modifier} String of modifiers each represented by a single char. The
same specifiers are used as for a key press, except that
the "-" separator is optional, so "C-A-", "c-a" and "CA"
can all be used to specify Ctrl+Alt+click.
- {grid} Grid number if the client uses |ui-multigrid|, else 0.
- {row} Mouse row-position (zero-based, like redraw events)
- {col} Mouse column-position (zero-based, like redraw events)
+ • {grid} Grid number if the client uses |ui-multigrid|, else 0.
+ • {row} Mouse row-position (zero-based, like redraw events)
+ • {col} Mouse column-position (zero-based, like redraw events)
nvim_list_bufs() *nvim_list_bufs()*
Gets the current list of buffer handles
@@ -1115,7 +1133,7 @@ nvim_list_uis() *nvim_list_uis()*
• "width" Requested width of the UI
• "rgb" true if the UI uses RGB colors (false implies |cterm-colors|)
• "ext_..." Requested UI extensions, see |ui-option|
- • "chan" Channel id of remote UI (not present for TUI)
+ • "chan" |channel-id| of remote UI
nvim_list_wins() *nvim_list_wins()*
Gets the current list of window handles.
@@ -1127,7 +1145,7 @@ nvim_load_context({dict}) *nvim_load_context()*
Sets the current editor state from the given |context| map.
Parameters: ~
- {dict} |Context| map.
+ • {dict} |Context| map.
nvim_notify({msg}, {log_level}, {opts}) *nvim_notify()*
Notify the user with a message
@@ -1136,9 +1154,9 @@ nvim_notify({msg}, {log_level}, {opts}) *nvim_notify()*
echo area but can be overridden to trigger desktop notifications.
Parameters: ~
- {msg} Message to display to the user
- {log_level} The log level
- {opts} Reserved for future use.
+ • {msg} Message to display to the user
+ • {log_level} The log level
+ • {opts} Reserved for future use.
nvim_open_term({buffer}, {opts}) *nvim_open_term()*
Open a terminal instance in a buffer
@@ -1156,13 +1174,13 @@ nvim_open_term({buffer}, {opts}) *nvim_open_term()*
virtual terminal having the intended size.
Parameters: ~
- {buffer} the buffer to use (expected to be empty)
- {opts} Optional parameters.
+ • {buffer} the buffer to use (expected to be empty)
+ • {opts} Optional parameters.
• on_input: lua callback for input sent, i e keypresses in
terminal mode. Note: keypresses are sent raw as they would
be to the pty master end. For instance, a carriage return
is sent as a "\r", not as a "\n". |textlock| applies. It
- is possible to call |nvim_chan_send| directly in the
+ is possible to call |nvim_chan_send()| directly in the
callback however. ["input", term, bufnr, data]
Return: ~
@@ -1173,7 +1191,7 @@ nvim_out_write({str}) *nvim_out_write()*
message is buffered (won't display) until a linefeed is written.
Parameters: ~
- {str} Message
+ • {str} Message
nvim_paste({data}, {crlf}, {phase}) *nvim_paste()*
Pastes at cursor, in any mode.
@@ -1190,9 +1208,9 @@ nvim_paste({data}, {crlf}, {phase}) *nvim_paste()*
not allowed when |textlock| is active
Parameters: ~
- {data} Multiline input. May be binary (containing NUL bytes).
- {crlf} Also break lines at CR and CRLF.
- {phase} -1: paste in a single call (i.e. without streaming). To
+ • {data} Multiline input. May be binary (containing NUL bytes).
+ • {crlf} Also break lines at CR and CRLF.
+ • {phase} -1: paste in a single call (i.e. without streaming). To
"stream" a paste, call `nvim_paste` sequentially with these `phase` values:
• 1: starts the paste (exactly once)
• 2: continues the paste (zero or more times)
@@ -1212,15 +1230,15 @@ nvim_put({lines}, {type}, {after}, {follow}) *nvim_put()*
not allowed when |textlock| is active
Parameters: ~
- {lines} |readfile()|-style list of lines. |channel-lines|
- {type} Edit behavior: any |getregtype()| result, or:
+ • {lines} |readfile()|-style list of lines. |channel-lines|
+ • {type} Edit behavior: any |getregtype()| result, or:
• "b" |blockwise-visual| mode (may include width, e.g. "b3")
• "c" |charwise| mode
• "l" |linewise| mode
• "" guess by contents, see |setreg()|
- {after} If true insert after cursor (like |p|), or before (like
+ • {after} If true insert after cursor (like |p|), or before (like
|P|).
- {follow} If true place cursor at end of inserted text.
+ • {follow} If true place cursor at end of inserted text.
*nvim_replace_termcodes()*
nvim_replace_termcodes({str}, {from_part}, {do_lt}, {special})
@@ -1228,10 +1246,10 @@ nvim_replace_termcodes({str}, {from_part}, {do_lt}, {special})
the internal representation.
Parameters: ~
- {str} String to be converted.
- {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 "\r" char.
+ • {str} String to be converted.
+ • {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 "\r" char.
See also: ~
replace_termcodes
@@ -1239,20 +1257,22 @@ nvim_replace_termcodes({str}, {from_part}, {do_lt}, {special})
*nvim_select_popupmenu_item()*
nvim_select_popupmenu_item({item}, {insert}, {finish}, {opts})
- Selects an item in the completion popupmenu.
+ Selects an item in the completion popup menu.
- If |ins-completion| is not active this API call is silently ignored.
- Useful for an external UI using |ui-popupmenu| to control the popupmenu
- with the mouse. Can also be used in a mapping; use <cmd> |:map-cmd| to
- ensure the mapping doesn't end completion mode.
+ If neither |ins-completion| nor |cmdline-completion| popup menu is active
+ this API call is silently ignored. Useful for an external UI using
+ |ui-popupmenu| to control the popup menu with the mouse. Can also be used
+ in a mapping; use <Cmd> |:map-cmd| or a Lua mapping to ensure the mapping
+ doesn't end completion mode.
Parameters: ~
- {item} Index (zero-based) of the item to select. Value of -1
+ • {item} Index (zero-based) of the item to select. Value of -1
selects nothing and restores the original text.
- {insert} Whether the selection should be inserted in the buffer.
- {finish} Finish the completion and dismiss the popupmenu. Implies
- `insert`.
- {opts} Optional parameters. Reserved for future use.
+ • {insert} For |ins-completion|, whether the selection should be
+ inserted in the buffer. Ignored for |cmdline-completion|.
+ • {finish} Finish the completion and dismiss the popup menu. Implies
+ {insert}.
+ • {opts} Optional parameters. Reserved for future use.
*nvim_set_client_info()*
nvim_set_client_info({name}, {version}, {type}, {methods}, {attributes})
@@ -1274,8 +1294,8 @@ nvim_set_client_info({name}, {version}, {type}, {methods}, {attributes})
|RPC| only
Parameters: ~
- {name} Short name for the connected client
- {version} Dictionary describing the version, with these (optional)
+ • {name} Short name for the connected client
+ • {version} Dictionary describing the version, with these (optional)
keys:
• "major" major version (defaults to 0 if not set, for
no release yet)
@@ -1284,7 +1304,7 @@ nvim_set_client_info({name}, {version}, {type}, {methods}, {attributes})
• "prerelease" string describing a prerelease, like
"dev" or "beta1"
• "commit" hash or similar identifier of commit
- {type} Must be one of the following values. Client libraries
+ • {type} Must be one of the following values. Client libraries
should default to "remote" unless overridden by the
user.
• "remote" remote client connected to Nvim.
@@ -1293,7 +1313,7 @@ nvim_set_client_info({name}, {version}, {type}, {methods}, {attributes})
example, IDE/editor implementing a vim mode).
• "host" plugin host, typically started by nvim
• "plugin" single plugin, started by nvim
- {methods} Builtin methods in the client. For a host, this does not
+ • {methods} Builtin methods in the client. For a host, this does not
include plugin methods which will be discovered later.
The key should be the method name, the values are dicts
with these (optional) keys (more keys may be added in
@@ -1305,7 +1325,7 @@ nvim_set_client_info({name}, {version}, {type}, {methods}, {attributes})
• "nargs" Number of arguments. Could be a single integer
or an array of two integers, minimum and maximum
inclusive.
- {attributes} Arbitrary string:string map of informal client
+ • {attributes} Arbitrary string:string map of informal client
properties. Suggested keys:
• "website": Client homepage URL (e.g. GitHub
repository)
@@ -1321,13 +1341,13 @@ nvim_set_current_buf({buffer}) *nvim_set_current_buf()*
not allowed when |textlock| is active
Parameters: ~
- {buffer} Buffer handle
+ • {buffer} Buffer handle
nvim_set_current_dir({dir}) *nvim_set_current_dir()*
Changes the global working directory.
Parameters: ~
- {dir} Directory path
+ • {dir} Directory path
nvim_set_current_line({line}) *nvim_set_current_line()*
Sets the current line.
@@ -1336,7 +1356,7 @@ nvim_set_current_line({line}) *nvim_set_current_line()*
not allowed when |textlock| is active
Parameters: ~
- {line} Line contents
+ • {line} Line contents
nvim_set_current_tabpage({tabpage}) *nvim_set_current_tabpage()*
Sets the current tabpage.
@@ -1345,7 +1365,7 @@ nvim_set_current_tabpage({tabpage}) *nvim_set_current_tabpage()*
not allowed when |textlock| is active
Parameters: ~
- {tabpage} Tabpage handle
+ • {tabpage} Tabpage handle
nvim_set_current_win({window}) *nvim_set_current_win()*
Sets the current window.
@@ -1354,7 +1374,7 @@ nvim_set_current_win({window}) *nvim_set_current_win()*
not allowed when |textlock| is active
Parameters: ~
- {window} Window handle
+ • {window} Window handle
nvim_set_hl({ns_id}, {name}, {*val}) *nvim_set_hl()*
Sets a highlight group.
@@ -1372,10 +1392,10 @@ nvim_set_hl({ns_id}, {name}, {*val}) *nvim_set_hl()*
using these values results in an error.
Parameters: ~
- {ns_id} Namespace id for this highlight |nvim_create_namespace()|.
+ • {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:
+ • {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"
@@ -1394,31 +1414,31 @@ nvim_set_hl({ns_id}, {name}, {*val}) *nvim_set_hl()*
• 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|
+ • ctermfg: Sets foreground of cterm color |ctermfg|
+ • ctermbg: Sets background of cterm color |ctermbg|
• cterm: cterm attribute map, like |highlight-args|. If not
set, cterm attributes will match those from the attribute
map documented above.
nvim_set_hl_ns({ns_id}) *nvim_set_hl_ns()*
Set active namespace for highlights. This can be set for a single window,
- see |nvim_win_set_hl_ns|.
+ see |nvim_win_set_hl_ns()|.
Parameters: ~
- {ns_id} the namespace to use
+ • {ns_id} the namespace to use
nvim_set_hl_ns_fast({ns_id}) *nvim_set_hl_ns_fast()*
Set active namespace for highlights while redrawing.
This function meant to be called while redrawing, primarily from
- |nvim_set_decoration_provider| on_win and on_line callbacks, which are
+ |nvim_set_decoration_provider()| on_win and on_line callbacks, which are
allowed to change the namespace during a redraw cycle.
Attributes: ~
|api-fast|
Parameters: ~
- {ns_id} the namespace to activate
+ • {ns_id} the namespace to activate
nvim_set_keymap({mode}, {lhs}, {rhs}, {*opts}) *nvim_set_keymap()*
Sets a global |mapping| for the given mode.
@@ -1428,22 +1448,22 @@ nvim_set_keymap({mode}, {lhs}, {rhs}, {*opts}) *nvim_set_keymap()*
Unlike |:map|, leading/trailing whitespace is accepted as part of the
{lhs} or {rhs}. Empty {rhs} is |<Nop>|. |keycodes| are replaced as usual.
- Example: >
+ Example: >vim
call nvim_set_keymap('n', ' <NL>', '', {'nowait': v:true})
<
- is equivalent to: >
+ is equivalent to: >vim
nmap <nowait> <Space><NL> <Nop>
<
Parameters: ~
- {mode} Mode short-name (map command prefix: "n", "i", "v", "x", …) or
+ • {mode} Mode short-name (map command prefix: "n", "i", "v", "x", …) or
"!" for |:map!|, or empty string for |:map|.
- {lhs} Left-hand-side |{lhs}| of the mapping.
- {rhs} Right-hand-side |{rhs}| of the mapping.
- {opts} Optional parameters map: keys are |:map-arguments|, values are
+ • {lhs} Left-hand-side |{lhs}| of the mapping.
+ • {rhs} Right-hand-side |{rhs}| of the mapping.
+ • {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".
+ 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
@@ -1456,22 +1476,22 @@ nvim_set_var({name}, {value}) *nvim_set_var()*
Sets a global (g:) variable.
Parameters: ~
- {name} Variable name
- {value} Variable value
+ • {name} Variable name
+ • {value} Variable value
nvim_set_vvar({name}, {value}) *nvim_set_vvar()*
Sets a v: variable, if it is not readonly.
Parameters: ~
- {name} Variable name
- {value} Variable value
+ • {name} Variable name
+ • {value} Variable value
nvim_strwidth({text}) *nvim_strwidth()*
Calculates the number of display cells occupied by `text`. Control
characters including <Tab> count as one cell.
Parameters: ~
- {text} Some text
+ • {text} Some text
Return: ~
Number of cells
@@ -1483,7 +1503,7 @@ nvim_subscribe({event}) *nvim_subscribe()*
|RPC| only
Parameters: ~
- {event} Event type string
+ • {event} Event type string
nvim_unsubscribe({event}) *nvim_unsubscribe()*
Unsubscribes to event broadcasts.
@@ -1492,7 +1512,7 @@ nvim_unsubscribe({event}) *nvim_unsubscribe()*
|RPC| only
Parameters: ~
- {event} Event type string
+ • {event} Event type string
==============================================================================
@@ -1505,9 +1525,9 @@ nvim_call_dict_function({dict}, {fn}, {args})
On execution error: fails with VimL error, updates v:errmsg.
Parameters: ~
- {dict} Dictionary, or String evaluating to a VimL |self| dict
- {fn} Name of the function defined on the VimL dict
- {args} Function arguments packed in an Array
+ • {dict} Dictionary, or String evaluating to a VimL |self| dict
+ • {fn} Name of the function defined on the VimL dict
+ • {args} Function arguments packed in an Array
Return: ~
Result of the function call
@@ -1518,8 +1538,8 @@ nvim_call_function({fn}, {args}) *nvim_call_function()*
On execution error: fails with VimL error, updates v:errmsg.
Parameters: ~
- {fn} Function to call
- {args} Function arguments packed in an Array
+ • {fn} Function to call
+ • {args} Function arguments packed in an Array
Return: ~
Result of the function call
@@ -1536,7 +1556,7 @@ nvim_command({command}) *nvim_command()*
|nvim_parse_cmd()| in conjunction with |nvim_cmd()|.
Parameters: ~
- {command} Ex command string
+ • {command} Ex command string
nvim_eval({expr}) *nvim_eval()*
Evaluates a VimL |expression|. Dictionaries and Lists are recursively
@@ -1545,7 +1565,7 @@ nvim_eval({expr}) *nvim_eval()*
On execution error: fails with VimL error, updates v:errmsg.
Parameters: ~
- {expr} VimL expression string
+ • {expr} VimL expression string
Return: ~
Evaluation result or expanded object
@@ -1560,8 +1580,8 @@ nvim_exec({src}, {output}) *nvim_exec()*
On execution error: fails with VimL error, updates v:errmsg.
Parameters: ~
- {src} Vimscript code
- {output} Capture and return all (non-error, non-shell |:!|) output
+ • {src} Vimscript code
+ • {output} Capture and return all (non-error, non-shell |:!|) output
Return: ~
Output (non-error, non-shell |:!|) if `output` is true, else empty
@@ -1580,8 +1600,8 @@ nvim_parse_expression({expr}, {flags}, {highlight})
|api-fast|
Parameters: ~
- {expr} Expression to parse. Always treated as a single line.
- {flags} Flags:
+ • {expr} Expression to parse. Always treated as a single line.
+ • {flags} Flags:
• "m" if multiple expressions in a row are allowed (only
the first one will be parsed),
• "E" if EOC tokens are not allowed (determines whether
@@ -1593,7 +1613,7 @@ nvim_parse_expression({expr}, {flags}, {highlight})
• "E" to parse like for "<C-r>=".
• empty string for ":call".
• "lm" to parse for ":let".
- {highlight} If true, return value will also include "highlight" key
+ • {highlight} If true, return value will also include "highlight" key
containing array of 4-tuples (arrays) (Integer, Integer,
Integer, String), where first three numbers define the
highlighted region and represent line, starting column
@@ -1661,7 +1681,7 @@ 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.
+ • {buffer} Buffer handle, or 0 for current buffer.
See also: ~
nvim_create_user_command
@@ -1674,15 +1694,15 @@ nvim_buf_del_user_command({buffer}, {name})
|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.
+ • {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.
+ • {buffer} Buffer handle, or 0 for current buffer
+ • {opts} Optional parameters. Currently not used.
Return: ~
Map of maps describing commands.
@@ -1694,16 +1714,22 @@ nvim_cmd({*cmd}, {*opts}) *nvim_cmd()*
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.
+ expand filenames, etc. Command arguments may also be Number, Boolean or
+ String.
+
+ The first argument may also be used instead of count for commands that
+ support it in order to make their usage simpler with |vim.cmd()|. For
+ example, instead of `vim.cmd.bdelete{ count = 2 }`, you may do
+ `vim.cmd.bdelete(2)`.
On execution error: fails with VimL error, updates v:errmsg.
Parameters: ~
- {cmd} Command to execute. Must be a Dictionary that can contain the
+ • {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.
+ • {opts} Optional parameters.
• output: (boolean, default false) Whether to return command
output.
@@ -1724,19 +1750,20 @@ nvim_create_user_command({name}, {command}, {*opts})
{command} is the replacement text or Lua function to execute.
- Example: >
+ Example: >vim
: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
+ • {name} Name of the new user command. Must begin with an uppercase
letter.
- {command} Replacement command to execute when this user command is
+ • {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:
+ • name: (string) Command name
• args: (string) The args passed to the command, if any
|<args>|
• fargs: (table) The args split by unescaped whitespace
@@ -1756,7 +1783,7 @@ nvim_create_user_command({name}, {command}, {*opts})
• 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
+ • {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
@@ -1774,7 +1801,7 @@ nvim_del_user_command({name}) *nvim_del_user_command()*
Delete a user-defined command.
Parameters: ~
- {name} Name of the command to delete.
+ • {name} Name of the command to delete.
nvim_get_commands({*opts}) *nvim_get_commands()*
Gets a map of global (non-buffer-local) Ex commands.
@@ -1782,7 +1809,7 @@ nvim_get_commands({*opts}) *nvim_get_commands()*
Currently only |user-commands| are supported, not builtin Ex commands.
Parameters: ~
- {opts} Optional parameters. Currently only supports {"builtin":false}
+ • {opts} Optional parameters. Currently only supports {"builtin":false}
Return: ~
Map of maps describing commands.
@@ -1796,24 +1823,25 @@ nvim_parse_cmd({str}, {opts}) *nvim_parse_cmd()*
|api-fast|
Parameters: ~
- {str} Command line string to parse. Cannot contain "\n".
- {opts} Optional parameters. Reserved for future use.
+ • {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.
+ • range: (array) (optional) Command range (|<line1>| |<line2>|).
+ Omitted if command doesn't accept a range. Otherwise, has no
+ elements if no range was specified, one element if only a single
+ range item was specified, or two elements if both range items were
+ specified.
+ • count: (number) (optional) Command |<count>|. Omitted if command
+ cannot take a count.
+ • reg: (string) (optional) Command |<register>|. Omitted 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.
+ • addr: (string) Value of |:command-addr|. Uses short name or "line"
+ for -addr=lines.
• 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.
@@ -1839,13 +1867,14 @@ nvim_parse_cmd({str}, {opts}) *nvim_parse_cmd()*
• browse: (boolean) |:browse|.
• confirm: (boolean) |:confirm|.
• hide: (boolean) |:hide|.
+ • horizontal: (boolean) |:horizontal|.
• keepalt: (boolean) |:keepalt|.
• keepjumps: (boolean) |:keepjumps|.
• keepmarks: (boolean) |:keepmarks|.
• keeppatterns: (boolean) |:keeppatterns|.
• lockmarks: (boolean) |:lockmarks|.
• noswapfile: (boolean) |:noswapfile|.
- • tab: (integer) |:tab|.
+ • tab: (integer) |:tab|. -1 when omitted.
• verbose: (integer) |:verbose|. -1 when omitted.
• vertical: (boolean) |:vertical|.
• split: (string) Split modifier string, is an empty string when
@@ -1864,8 +1893,8 @@ 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
+ • {buffer} Buffer handle, or 0 for current buffer
+ • {name} Option name
Return: ~
Option value
@@ -1875,15 +1904,15 @@ nvim_buf_set_option({buffer}, {name}, {value}) *nvim_buf_set_option()*
(only works if there's a global fallback)
Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
- {name} Option name
- {value} Option value
+ • {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|.
+ dictionaries as detailed at |nvim_get_option_info()|.
Return: ~
dictionary of all options
@@ -1892,7 +1921,7 @@ nvim_get_option({name}) *nvim_get_option()*
Gets the global value of an option.
Parameters: ~
- {name} Option name
+ • {name} Option name
Return: ~
Option value (global)
@@ -1915,7 +1944,7 @@ nvim_get_option_info({name}) *nvim_get_option_info()*
• flaglist: List of single char flags
Parameters: ~
- {name} Option name
+ • {name} Option name
Return: ~
Option Information
@@ -1927,8 +1956,8 @@ nvim_get_option_value({name}, {*opts}) *nvim_get_option_value()*
current buffer or window, unless "buf" or "win" is set in {opts}.
Parameters: ~
- {name} Option name
- {opts} Optional 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.
@@ -1942,8 +1971,8 @@ nvim_set_option({name}, {value}) *nvim_set_option()*
Sets the global value of an option.
Parameters: ~
- {name} Option name
- {value} New option value
+ • {name} Option name
+ • {value} New option value
*nvim_set_option_value()*
nvim_set_option_value({name}, {value}, {*opts})
@@ -1954,10 +1983,10 @@ nvim_set_option_value({name}, {value}, {*opts})
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
+ • {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.
@@ -1966,8 +1995,8 @@ 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
+ • {window} Window handle, or 0 for current window
+ • {name} Option name
Return: ~
Option value
@@ -1977,9 +2006,9 @@ nvim_win_set_option({window}, {name}, {value}) *nvim_win_set_option()*
(only works if there's a global fallback)
Parameters: ~
- {window} Window handle, or 0 for current window
- {name} Option name
- {value} Option value
+ • {window} Window handle, or 0 for current window
+ • {name} Option name
+ • {value} Option value
==============================================================================
@@ -2003,20 +2032,20 @@ whether a buffer is loaded.
nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()*
Activates buffer-update events on a channel, or as Lua callbacks.
- Example (Lua): capture buffer updates in a global `events` variable (use "print(vim.inspect(events))" to see its contents): >
+ Example (Lua): capture buffer updates in a global `events` variable (use "print(vim.inspect(events))" to see its contents): >lua
events = {}
vim.api.nvim_buf_attach(0, false, {
on_lines=function(...) table.insert(events, {...}) end})
<
Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
- {send_buffer} True if the initial notification should contain the
+ • {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
callbacks.
- {opts} Optional parameters.
+ • {opts} Optional parameters.
• on_lines: Lua callback invoked on change. Return `true` to detach. Args:
• the string "lines"
• buffer handle
@@ -2087,11 +2116,11 @@ nvim_buf_call({buffer}, {fun}) *nvim_buf_call()*
buffer/window currently, like |termopen()|.
Attributes: ~
- |vim.api| only
+ Lua |vim.api| only
Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
- {fun} Function to call inside the buffer (currently lua callable
+ • {buffer} Buffer handle, or 0 for current buffer
+ • {fun} Function to call inside the buffer (currently lua callable
only)
Return: ~
@@ -2102,7 +2131,7 @@ nvim_buf_del_keymap({buffer}, {mode}, {lhs}) *nvim_buf_del_keymap()*
Unmaps a buffer-local |mapping| for the given mode.
Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
+ • {buffer} Buffer handle, or 0 for current buffer
See also: ~
|nvim_del_keymap()|
@@ -2115,8 +2144,8 @@ nvim_buf_del_mark({buffer}, {name}) *nvim_buf_del_mark()*
buffer it will return false.
Parameters: ~
- {buffer} Buffer to set the mark on
- {name} Mark name
+ • {buffer} Buffer to set the mark on
+ • {name} Mark name
Return: ~
true if the mark was deleted, else false.
@@ -2129,8 +2158,8 @@ nvim_buf_del_var({buffer}, {name}) *nvim_buf_del_var()*
Removes a buffer-scoped (b:) variable
Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
- {name} Variable name
+ • {buffer} Buffer handle, or 0 for current buffer
+ • {name} Variable name
nvim_buf_delete({buffer}, {opts}) *nvim_buf_delete()*
Deletes the buffer. See |:bwipeout|
@@ -2139,8 +2168,8 @@ nvim_buf_delete({buffer}, {opts}) *nvim_buf_delete()*
not allowed when |textlock| is active
Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
- {opts} Optional parameters. Keys:
+ • {buffer} Buffer handle, or 0 for current buffer
+ • {opts} Optional parameters. Keys:
• force: Force deletion and ignore unsaved changes.
• unload: Unloaded only, do not delete. See |:bunload|
@@ -2151,7 +2180,7 @@ nvim_buf_detach({buffer}) *nvim_buf_detach()*
|RPC| only
Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
+ • {buffer} Buffer handle, or 0 for current buffer
Return: ~
False if detach failed (because the buffer isn't loaded); otherwise
@@ -2165,7 +2194,7 @@ nvim_buf_get_changedtick({buffer}) *nvim_buf_get_changedtick()*
Gets a changed tick of a buffer
Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
+ • {buffer} Buffer handle, or 0 for current buffer
Return: ~
`b:changedtick` value.
@@ -2174,8 +2203,8 @@ nvim_buf_get_keymap({buffer}, {mode}) *nvim_buf_get_keymap()*
Gets a list of buffer-local |mapping| definitions.
Parameters: ~
- {mode} Mode short-name ("n", "i", "v", ...)
- {buffer} Buffer handle, or 0 for current buffer
+ • {mode} Mode short-name ("n", "i", "v", ...)
+ • {buffer} Buffer handle, or 0 for current buffer
Return: ~
Array of |maparg()|-like dictionaries describing mappings. The
@@ -2193,10 +2222,10 @@ nvim_buf_get_lines({buffer}, {start}, {end}, {strict_indexing})
`strict_indexing` is set.
Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
- {start} First line index
- {end} Last line index, exclusive
- {strict_indexing} Whether out-of-bounds should be an error.
+ • {buffer} Buffer handle, or 0 for current buffer
+ • {start} First line index
+ • {end} Last line index, exclusive
+ • {strict_indexing} Whether out-of-bounds should be an error.
Return: ~
Array of lines, or empty array for unloaded buffer.
@@ -2208,8 +2237,8 @@ nvim_buf_get_mark({buffer}, {name}) *nvim_buf_get_mark()*
Marks are (1,0)-indexed. |api-indexing|
Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
- {name} Mark name
+ • {buffer} Buffer handle, or 0 for current buffer
+ • {name} Mark name
Return: ~
(row, col) tuple, (0, 0) if the mark is not set, or is an
@@ -2223,7 +2252,7 @@ nvim_buf_get_name({buffer}) *nvim_buf_get_name()*
Gets the full file name for the buffer
Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
+ • {buffer} Buffer handle, or 0 for current buffer
Return: ~
Buffer name
@@ -2240,8 +2269,8 @@ nvim_buf_get_offset({buffer}, {index}) *nvim_buf_get_offset()*
for unloaded buffer.
Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
- {index} Line index
+ • {buffer} Buffer handle, or 0 for current buffer
+ • {index} Line index
Return: ~
Integer byte offset, or -1 for unloaded buffer.
@@ -2260,12 +2289,12 @@ nvim_buf_get_text({buffer}, {start_row}, {start_col}, {end_row}, {end_col},
Prefer |nvim_buf_get_lines()| when retrieving entire lines.
Parameters: ~
- {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.
+ • {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: ~
Array of lines, or empty array for unloaded buffer.
@@ -2274,8 +2303,8 @@ nvim_buf_get_var({buffer}, {name}) *nvim_buf_get_var()*
Gets a buffer-scoped (b:) variable.
Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
- {name} Variable name
+ • {buffer} Buffer handle, or 0 for current buffer
+ • {name} Variable name
Return: ~
Variable value
@@ -2285,7 +2314,7 @@ nvim_buf_is_loaded({buffer}) *nvim_buf_is_loaded()*
about unloaded buffers.
Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
+ • {buffer} Buffer handle, or 0 for current buffer
Return: ~
true if the buffer is valid and loaded, false otherwise.
@@ -2298,7 +2327,7 @@ nvim_buf_is_valid({buffer}) *nvim_buf_is_valid()*
for more info about unloaded buffers.
Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
+ • {buffer} Buffer handle, or 0 for current buffer
Return: ~
true if the buffer is valid, false otherwise.
@@ -2307,7 +2336,7 @@ nvim_buf_line_count({buffer}) *nvim_buf_line_count()*
Returns the number of lines in the given buffer.
Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
+ • {buffer} Buffer handle, or 0 for current buffer
Return: ~
Line count, or 0 for unloaded buffer. |api-buffer|
@@ -2317,7 +2346,7 @@ nvim_buf_set_keymap({buffer}, {mode}, {lhs}, {rhs}, {*opts})
Sets a buffer-local |mapping| for the given mode.
Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
+ • {buffer} Buffer handle, or 0 for current buffer
See also: ~
|nvim_set_keymap()|
@@ -2340,11 +2369,14 @@ nvim_buf_set_lines({buffer}, {start}, {end}, {strict_indexing}, {replacement})
not allowed when |textlock| is active
Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
- {start} First line index
- {end} Last line index, exclusive
- {strict_indexing} Whether out-of-bounds should be an error.
- {replacement} Array of lines to use as replacement
+ • {buffer} Buffer handle, or 0 for current buffer
+ • {start} First line index
+ • {end} Last line index, exclusive
+ • {strict_indexing} Whether out-of-bounds should be an error.
+ • {replacement} Array of lines to use as replacement
+
+ See also: ~
+ |nvim_buf_set_text()|
*nvim_buf_set_mark()*
nvim_buf_set_mark({buffer}, {name}, {line}, {col}, {opts})
@@ -2357,11 +2389,11 @@ nvim_buf_set_mark({buffer}, {name}, {line}, {col}, {opts})
Passing 0 as line deletes the mark
Parameters: ~
- {buffer} Buffer to set the mark on
- {name} Mark name
- {line} Line number
- {col} Column/row number
- {opts} Optional parameters. Reserved for future use.
+ • {buffer} Buffer to set the mark on
+ • {name} Mark name
+ • {line} Line number
+ • {col} Column/row number
+ • {opts} Optional parameters. Reserved for future use.
Return: ~
true if the mark was set, else false.
@@ -2374,8 +2406,8 @@ nvim_buf_set_name({buffer}, {name}) *nvim_buf_set_name()*
Sets the full file name for a buffer
Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
- {name} Buffer name
+ • {buffer} Buffer handle, or 0 for current buffer
+ • {name} Buffer name
*nvim_buf_set_text()*
nvim_buf_set_text({buffer}, {start_row}, {start_col}, {end_row}, {end_col},
@@ -2397,20 +2429,23 @@ nvim_buf_set_text({buffer}, {start_row}, {start_col}, {end_row}, {end_col},
lines.
Parameters: ~
- {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
+ • {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
+
+ See also: ~
+ |nvim_buf_set_lines()|
nvim_buf_set_var({buffer}, {name}, {value}) *nvim_buf_set_var()*
Sets a buffer-scoped (b:) variable
Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
- {name} Variable name
- {value} Variable value
+ • {buffer} Buffer handle, or 0 for current buffer
+ • {name} Variable name
+ • {value} Variable value
==============================================================================
@@ -2441,12 +2476,12 @@ nvim_buf_add_highlight({buffer}, {ns_id}, {hl_group}, {line}, {col_start},
|nvim_create_namespace()| to create a new empty namespace.
Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
- {ns_id} namespace to use or -1 for ungrouped highlight
- {hl_group} Name of the highlight group to use
- {line} Line to highlight (zero-indexed)
- {col_start} Start of (byte-indexed) column range to highlight
- {col_end} End of (byte-indexed) column range to highlight, or -1 to
+ • {buffer} Buffer handle, or 0 for current buffer
+ • {ns_id} namespace to use or -1 for ungrouped highlight
+ • {hl_group} Name of the highlight group to use
+ • {line} Line to highlight (zero-indexed)
+ • {col_start} Start of (byte-indexed) column range to highlight
+ • {col_end} End of (byte-indexed) column range to highlight, or -1 to
highlight to end of line
Return: ~
@@ -2454,39 +2489,39 @@ nvim_buf_add_highlight({buffer}, {ns_id}, {hl_group}, {line}, {col_start},
*nvim_buf_clear_namespace()*
nvim_buf_clear_namespace({buffer}, {ns_id}, {line_start}, {line_end})
- Clears namespaced objects (highlights, extmarks, virtual text) from a
+ Clears |namespace|d objects (highlights, |extmarks|, virtual text) from a
region.
Lines are 0-indexed. |api-indexing| To clear the namespace in the entire
buffer, specify line_start=0 and line_end=-1.
Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
- {ns_id} Namespace to clear, or -1 to clear all namespaces.
- {line_start} Start of range of lines to clear
- {line_end} End of range of lines to clear (exclusive) or -1 to
+ • {buffer} Buffer handle, or 0 for current buffer
+ • {ns_id} Namespace to clear, or -1 to clear all namespaces.
+ • {line_start} Start of range of lines to clear
+ • {line_end} End of range of lines to clear (exclusive) or -1 to
clear to end of buffer.
nvim_buf_del_extmark({buffer}, {ns_id}, {id}) *nvim_buf_del_extmark()*
- Removes an extmark.
+ Removes an |extmark|.
Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
- {ns_id} Namespace id from |nvim_create_namespace()|
- {id} Extmark id
+ • {buffer} Buffer handle, or 0 for current buffer
+ • {ns_id} Namespace id from |nvim_create_namespace()|
+ • {id} Extmark id
Return: ~
true if the extmark was found, else false
*nvim_buf_get_extmark_by_id()*
nvim_buf_get_extmark_by_id({buffer}, {ns_id}, {id}, {opts})
- Gets the position (0-indexed) of an extmark.
+ Gets the position (0-indexed) of an |extmark|.
Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
- {ns_id} Namespace id from |nvim_create_namespace()|
- {id} Extmark id
- {opts} Optional parameters. Keys:
+ • {buffer} Buffer handle, or 0 for current buffer
+ • {ns_id} Namespace id from |nvim_create_namespace()|
+ • {id} Extmark id
+ • {opts} Optional parameters. Keys:
• details: Whether to include the details dict
Return: ~
@@ -2494,45 +2529,43 @@ nvim_buf_get_extmark_by_id({buffer}, {ns_id}, {id}, {opts})
*nvim_buf_get_extmarks()*
nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {opts})
- Gets extmarks in "traversal order" from a |charwise| region defined by
+ Gets |extmarks| in "traversal order" from a |charwise| region defined by
buffer positions (inclusive, 0-indexed |api-indexing|).
Region can be given as (row,col) tuples, or valid extmark ids (whose
positions define the bounds). 0 and -1 are understood as (0,0) and (-1,-1)
- respectively, thus the following are equivalent:
->
- nvim_buf_get_extmarks(0, my_ns, 0, -1, {})
- nvim_buf_get_extmarks(0, my_ns, [0,0], [-1,-1], {})
+ respectively, thus the following are equivalent: >lua
+ vim.api.nvim_buf_get_extmarks(0, my_ns, 0, -1, {})
+ vim.api.nvim_buf_get_extmarks(0, my_ns, {0,0}, {-1,-1}, {})
<
If `end` is less than `start`, traversal works backwards. (Useful with
`limit`, to get the first marks prior to a given position.)
- Example:
->
- local a = vim.api
- local pos = a.nvim_win_get_cursor(0)
- local ns = a.nvim_create_namespace('my-plugin')
- -- Create new extmark at line 1, column 1.
- local m1 = a.nvim_buf_set_extmark(0, ns, 0, 0, {})
- -- Create new extmark at line 3, column 1.
- 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.
- local all = a.nvim_buf_get_extmarks(0, ns, 0, -1, {})
- print(vim.inspect(ms))
+ Example: >lua
+ local a = vim.api
+ local pos = a.nvim_win_get_cursor(0)
+ local ns = a.nvim_create_namespace('my-plugin')
+ -- Create new extmark at line 1, column 1.
+ local m1 = a.nvim_buf_set_extmark(0, ns, 0, 0, {})
+ -- Create new extmark at line 3, column 1.
+ local m2 = a.nvim_buf_set_extmark(0, ns, 2, 0, {})
+ -- Get extmarks only from line 3.
+ local ms = a.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {})
+ -- Get all marks in this buffer + namespace.
+ local all = a.nvim_buf_get_extmarks(0, ns, 0, -1, {})
+ print(vim.inspect(ms))
<
Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
- {ns_id} Namespace id from |nvim_create_namespace()|
- {start} Start of range: a 0-indexed (row, col) or valid extmark id
+ • {buffer} Buffer handle, or 0 for current buffer
+ • {ns_id} Namespace id from |nvim_create_namespace()|
+ • {start} Start of range: a 0-indexed (row, col) or valid extmark id
(whose position defines the bound). |api-indexing|
- {end} End of range (inclusive): a 0-indexed (row, col) or valid
+ • {end} End of range (inclusive): a 0-indexed (row, col) or valid
extmark id (whose position defines the bound).
|api-indexing|
- {opts} Optional parameters. Keys:
+ • {opts} Optional parameters. Keys:
• limit: Maximum number of marks to return
• details Whether to include the details dict
@@ -2541,7 +2574,7 @@ nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {opts})
*nvim_buf_set_extmark()*
nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {*opts})
- Creates or updates an extmark.
+ Creates or updates an |extmark|.
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
@@ -2553,11 +2586,11 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {*opts})
range of text, and also to associate virtual text to the mark.
Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
- {ns_id} Namespace id from |nvim_create_namespace()|
- {line} Line where to place the mark, 0-based. |api-indexing|
- {col} Column where to place the mark, 0-based. |api-indexing|
- {opts} Optional parameters.
+ • {buffer} Buffer handle, or 0 for current buffer
+ • {ns_id} Namespace id from |nvim_create_namespace()|
+ • {line} Line where to place the mark, 0-based. |api-indexing|
+ • {col} Column where to place the mark, 0-based. |api-indexing|
+ • {opts} Optional parameters.
• id : id of the extmark to edit.
• end_row : ending line of the mark, 0-based inclusive.
• end_col : ending col of the mark, 0-based exclusive.
@@ -2569,11 +2602,11 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {*opts})
• virt_text : virtual text to link to this mark. A list of
[text, highlight] tuples, each representing a text chunk
with specified highlight. `highlight` element can either
- be a a single highlight group, or an array of multiple
+ be a single highlight group, or an array of multiple
highlight groups that will be stacked (highest priority
last). A highlight group can be supplied either as a
string or as an integer, the latter which can be obtained
- using |nvim_get_hl_id_by_name|.
+ using |nvim_get_hl_id_by_name()|.
• virt_text_pos : position of virtual text. Possible values:
• "eol": right after eol character (default)
• "overlay": display over the specified column, without
@@ -2605,7 +2638,7 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {*opts})
• virt_lines_above: place virtual lines above instead.
• virt_lines_leftcol: Place extmarks in the leftmost column
of the window, bypassing sign and number columns.
- • ephemeral : for use with |nvim_set_decoration_provider|
+ • ephemeral : for use with |nvim_set_decoration_provider()|
callbacks. The mark will only be used for the current
redraw cycle, and not be permantently stored in the
buffer.
@@ -2643,6 +2676,8 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {*opts})
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|.
+ • spell: boolean indicating that spell checking should be
+ performed within this extmark
• 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
@@ -2652,7 +2687,7 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {*opts})
Id of the created/updated extmark
nvim_create_namespace({name}) *nvim_create_namespace()*
- Creates a new *namespace* or gets an existing one.
+ Creates a new namespace or gets an existing one. *namespace*
Namespaces are used for buffer highlights and virtual text, see
|nvim_buf_add_highlight()| and |nvim_buf_set_extmark()|.
@@ -2662,26 +2697,26 @@ nvim_create_namespace({name}) *nvim_create_namespace()*
new, anonymous namespace is created.
Parameters: ~
- {name} Namespace name or empty string
+ • {name} Namespace name or empty string
Return: ~
Namespace id
nvim_get_namespaces() *nvim_get_namespaces()*
- Gets existing, non-anonymous namespaces.
+ Gets existing, non-anonymous |namespace|s.
Return: ~
dict that maps from names to namespace ids.
*nvim_set_decoration_provider()*
-nvim_set_decoration_provider({ns_id}, {opts})
- Set or change decoration provider for a namespace
+nvim_set_decoration_provider({ns_id}, {*opts})
+ Set or change decoration provider for a |namespace|
This is a very general purpose interface for having lua callbacks being
triggered during the redraw code.
- The expected usage is to set extmarks for the currently redrawn buffer.
- |nvim_buf_set_extmark| can be called to add marks on a per-window or
+ The expected usage is to set |extmarks| for the currently redrawn buffer.
+ |nvim_buf_set_extmark()| can be called to add marks on a per-window or
per-lines basis. Use the `ephemeral` key to only use the mark for the
current screen redraw (the callback will be called again for the next
redraw ).
@@ -2702,11 +2737,11 @@ nvim_set_decoration_provider({ns_id}, {opts})
quite dubious for the moment.
Attributes: ~
- |vim.api| only
+ Lua |vim.api| only
Parameters: ~
- {ns_id} Namespace id from |nvim_create_namespace()|
- {opts} Callbacks invoked during redraw:
+ • {ns_id} Namespace id from |nvim_create_namespace()|
+ • {opts} Table of callbacks:
• on_start: called first on each screen redraw ["start",
tick]
• on_buf: called for each buffer being redrawn (before window
@@ -2726,11 +2761,11 @@ nvim_win_call({window}, {fun}) *nvim_win_call()*
Calls a function with window as temporary current window.
Attributes: ~
- |vim.api| only
+ Lua |vim.api| only
Parameters: ~
- {window} Window handle, or 0 for current window
- {fun} Function to call inside the window (currently lua callable
+ • {window} Window handle, or 0 for current window
+ • {fun} Function to call inside the window (currently lua callable
only)
Return: ~
@@ -2748,8 +2783,8 @@ nvim_win_close({window}, {force}) *nvim_win_close()*
not allowed when |textlock| is active
Parameters: ~
- {window} Window handle, or 0 for current window
- {force} Behave like `:close!` The last window of a buffer with
+ • {window} Window handle, or 0 for current window
+ • {force} Behave like `:close!` The last window of a buffer with
unwritten changes can be closed. The buffer will become
hidden, even if 'hidden' is not set.
@@ -2757,23 +2792,25 @@ nvim_win_del_var({window}, {name}) *nvim_win_del_var()*
Removes a window-scoped (w:) variable
Parameters: ~
- {window} Window handle, or 0 for current window
- {name} Variable name
+ • {window} Window handle, or 0 for current window
+ • {name} Variable name
nvim_win_get_buf({window}) *nvim_win_get_buf()*
Gets the current buffer in a window
Parameters: ~
- {window} Window handle, or 0 for current window
+ • {window} Window handle, or 0 for current window
Return: ~
Buffer handle
nvim_win_get_cursor({window}) *nvim_win_get_cursor()*
- Gets the (1,0)-indexed cursor position in the window. |api-indexing|
+ Gets the (1,0)-indexed, buffer-relative cursor position for a given window
+ (different windows showing the same buffer have independent cursor
+ positions). |api-indexing|
Parameters: ~
- {window} Window handle, or 0 for current window
+ • {window} Window handle, or 0 for current window
Return: ~
(row, col) tuple
@@ -2782,7 +2819,7 @@ nvim_win_get_height({window}) *nvim_win_get_height()*
Gets the window height
Parameters: ~
- {window} Window handle, or 0 for current window
+ • {window} Window handle, or 0 for current window
Return: ~
Height as a count of rows
@@ -2791,7 +2828,7 @@ nvim_win_get_number({window}) *nvim_win_get_number()*
Gets the window number
Parameters: ~
- {window} Window handle, or 0 for current window
+ • {window} Window handle, or 0 for current window
Return: ~
Window number
@@ -2800,7 +2837,7 @@ nvim_win_get_position({window}) *nvim_win_get_position()*
Gets the window position in display cells. First position is zero.
Parameters: ~
- {window} Window handle, or 0 for current window
+ • {window} Window handle, or 0 for current window
Return: ~
(row, col) tuple with the window position
@@ -2809,7 +2846,7 @@ nvim_win_get_tabpage({window}) *nvim_win_get_tabpage()*
Gets the window tabpage
Parameters: ~
- {window} Window handle, or 0 for current window
+ • {window} Window handle, or 0 for current window
Return: ~
Tabpage that contains the window
@@ -2818,8 +2855,8 @@ nvim_win_get_var({window}, {name}) *nvim_win_get_var()*
Gets a window-scoped (w:) variable
Parameters: ~
- {window} Window handle, or 0 for current window
- {name} Variable name
+ • {window} Window handle, or 0 for current window
+ • {name} Variable name
Return: ~
Variable value
@@ -2828,7 +2865,7 @@ nvim_win_get_width({window}) *nvim_win_get_width()*
Gets the window width
Parameters: ~
- {window} Window handle, or 0 for current window
+ • {window} Window handle, or 0 for current window
Return: ~
Width as a count of columns
@@ -2839,19 +2876,19 @@ nvim_win_hide({window}) *nvim_win_hide()*
Like |:hide| the buffer becomes hidden unless another window is editing
it, or 'bufhidden' is `unload`, `delete` or `wipe` as opposed to |:close|
- or |nvim_win_close|, which will close the buffer.
+ or |nvim_win_close()|, which will close the buffer.
Attributes: ~
not allowed when |textlock| is active
Parameters: ~
- {window} Window handle, or 0 for current window
+ • {window} Window handle, or 0 for current window
nvim_win_is_valid({window}) *nvim_win_is_valid()*
Checks if a window is valid
Parameters: ~
- {window} Window handle, or 0 for current window
+ • {window} Window handle, or 0 for current window
Return: ~
true if the window is valid, false otherwise
@@ -2863,48 +2900,48 @@ nvim_win_set_buf({window}, {buffer}) *nvim_win_set_buf()*
not allowed when |textlock| is active
Parameters: ~
- {window} Window handle, or 0 for current window
- {buffer} Buffer handle
+ • {window} Window handle, or 0 for current window
+ • {buffer} Buffer handle
nvim_win_set_cursor({window}, {pos}) *nvim_win_set_cursor()*
Sets the (1,0)-indexed cursor position in the window. |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
+ • {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.
Parameters: ~
- {window} Window handle, or 0 for current window
- {height} Height as a count of rows
+ • {window} Window handle, or 0 for current window
+ • {height} Height as a count of rows
nvim_win_set_hl_ns({window}, {ns_id}) *nvim_win_set_hl_ns()*
Set highlight namespace for a window. This will use highlights defined in
this namespace, but fall back to global highlights (ns=0) when missing.
- This takes predecence over the 'winhighlight' option.
+ This takes precedence over the 'winhighlight' option.
Parameters: ~
- {ns_id} the namespace to use
+ • {ns_id} the namespace to use
nvim_win_set_var({window}, {name}, {value}) *nvim_win_set_var()*
Sets a window-scoped (w:) variable
Parameters: ~
- {window} Window handle, or 0 for current window
- {name} Variable name
- {value} Variable value
+ • {window} Window handle, or 0 for current window
+ • {name} Variable name
+ • {value} Variable value
nvim_win_set_width({window}, {width}) *nvim_win_set_width()*
Sets the window width. This will only succeed if the screen is split
vertically.
Parameters: ~
- {window} Window handle, or 0 for current window
- {width} Width as a count of columns
+ • {window} Window handle, or 0 for current window
+ • {width} Width as a count of columns
==============================================================================
@@ -2935,12 +2972,12 @@ nvim_open_win({buffer}, {enter}, {*config}) *nvim_open_win()*
could let floats hover outside of the main window like a tooltip, but this
should not be used to specify arbitrary WM screen positions.
- Example (Lua): window-relative float >
+ Example (Lua): window-relative float >lua
vim.api.nvim_open_win(0, false,
{relative='win', row=3, col=3, width=12, height=3})
<
- Example (Lua): buffer-relative float (travels as buffer is scrolled) >
+ Example (Lua): buffer-relative float (travels as buffer is scrolled) >lua
vim.api.nvim_open_win(0, false,
{relative='win', width=12, height=3, bufpos={100,10}})
<
@@ -2949,15 +2986,16 @@ nvim_open_win({buffer}, {enter}, {*config}) *nvim_open_win()*
not allowed when |textlock| is active
Parameters: ~
- {buffer} Buffer to display, or 0 for current buffer
- {enter} Enter the window (make it the current window)
- {config} Map defining the window configuration. Keys:
+ • {buffer} Buffer to display, or 0 for current buffer
+ • {enter} Enter the window (make it the current window)
+ • {config} Map defining the window configuration. Keys:
• relative: Sets the window layout to "floating", placed at
(row,col) coordinates relative to:
• "editor" The global editor grid
• "win" Window given by the `win` field, or current
window.
• "cursor" Cursor position in current window.
+ • "mouse" Mouse position
• win: |window-ID| for relative="win".
• anchor: Decides which corner of the float to place at
@@ -3005,10 +3043,10 @@ nvim_open_win({buffer}, {enter}, {*config}) *nvim_open_win()*
Disables 'number', 'relativenumber', 'cursorline',
'cursorcolumn', 'foldcolumn', 'spell' and 'list'
options. 'signcolumn' is changed to `auto` and
- 'colorcolumn' is cleared. The end-of-buffer region is
- hidden by setting `eob` flag of 'fillchars' to a space
- char, and clearing the |EndOfBuffer| region in
- 'winhighlight'.
+ 'colorcolumn' is cleared. 'statuscolumn' is changed to
+ empty. The end-of-buffer region is hidden by setting
+ `eob` flag of 'fillchars' to a space char, and clearing
+ the |hl-EndOfBuffer| region in 'winhighlight'.
• border: Style of (optional) window border. This can either
be a string or an array. The string values are
@@ -3034,9 +3072,14 @@ nvim_open_win({buffer}, {enter}, {*config}) *nvim_open_win()*
borders but not horizontal ones. By default,
`FloatBorder` highlight is used, which links to
`WinSeparator` when not defined. It could also be
- specified by character: [ {"+", "MyCorner"}, {"x",
- "MyBorder"} ].
-
+ specified by character: [ ["+", "MyCorner"], ["x",
+ "MyBorder"] ].
+
+ • title: Title (optional) in window border, String or list.
+ List is [text, highlight] tuples. if is string the default
+ highlight group is `FloatTitle`.
+ • title_pos: Title position must set with title option.
+ value can be of `left` `center` `right` default is left.
• noautocmd: If true then no buffer-related autocommand
events such as |BufEnter|, |BufLeave| or |BufWinEnter| may
fire from calling this function.
@@ -3052,7 +3095,7 @@ nvim_win_get_config({window}) *nvim_win_get_config()*
`relative` is empty for normal windows.
Parameters: ~
- {window} Window handle, or 0 for current window
+ • {window} Window handle, or 0 for current window
Return: ~
Map defining the window configuration, see |nvim_open_win()|
@@ -3065,8 +3108,8 @@ nvim_win_set_config({window}, {*config}) *nvim_win_set_config()*
changed. `row`/`col` and `relative` must be reconfigured together.
Parameters: ~
- {window} Window handle, or 0 for current window
- {config} Map defining the window configuration, see |nvim_open_win()|
+ • {window} Window handle, or 0 for current window
+ • {config} Map defining the window configuration, see |nvim_open_win()|
See also: ~
|nvim_open_win()|
@@ -3079,14 +3122,14 @@ nvim_tabpage_del_var({tabpage}, {name}) *nvim_tabpage_del_var()*
Removes a tab-scoped (t:) variable
Parameters: ~
- {tabpage} Tabpage handle, or 0 for current tabpage
- {name} Variable name
+ • {tabpage} Tabpage handle, or 0 for current tabpage
+ • {name} Variable name
nvim_tabpage_get_number({tabpage}) *nvim_tabpage_get_number()*
Gets the tabpage number
Parameters: ~
- {tabpage} Tabpage handle, or 0 for current tabpage
+ • {tabpage} Tabpage handle, or 0 for current tabpage
Return: ~
Tabpage number
@@ -3095,8 +3138,8 @@ nvim_tabpage_get_var({tabpage}, {name}) *nvim_tabpage_get_var()*
Gets a tab-scoped (t:) variable
Parameters: ~
- {tabpage} Tabpage handle, or 0 for current tabpage
- {name} Variable name
+ • {tabpage} Tabpage handle, or 0 for current tabpage
+ • {name} Variable name
Return: ~
Variable value
@@ -3105,7 +3148,7 @@ nvim_tabpage_get_win({tabpage}) *nvim_tabpage_get_win()*
Gets the current window in a tabpage
Parameters: ~
- {tabpage} Tabpage handle, or 0 for current tabpage
+ • {tabpage} Tabpage handle, or 0 for current tabpage
Return: ~
Window handle
@@ -3114,7 +3157,7 @@ nvim_tabpage_is_valid({tabpage}) *nvim_tabpage_is_valid()*
Checks if a tabpage is valid
Parameters: ~
- {tabpage} Tabpage handle, or 0 for current tabpage
+ • {tabpage} Tabpage handle, or 0 for current tabpage
Return: ~
true if the tabpage is valid, false otherwise
@@ -3123,7 +3166,7 @@ nvim_tabpage_list_wins({tabpage}) *nvim_tabpage_list_wins()*
Gets the windows in a tabpage
Parameters: ~
- {tabpage} Tabpage handle, or 0 for current tabpage
+ • {tabpage} Tabpage handle, or 0 for current tabpage
Return: ~
List of windows in `tabpage`
@@ -3133,9 +3176,9 @@ nvim_tabpage_set_var({tabpage}, {name}, {value})
Sets a tab-scoped (t:) variable
Parameters: ~
- {tabpage} Tabpage handle, or 0 for current tabpage
- {name} Variable name
- {value} Variable value
+ • {tabpage} Tabpage handle, or 0 for current tabpage
+ • {name} Variable name
+ • {value} Variable value
==============================================================================
@@ -3143,10 +3186,10 @@ 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|.
+ particular autocmd, see |nvim_del_autocmd()|.
Parameters: ~
- {opts} Parameters
+ • {opts} Parameters
• event: (string|table) Examples:
• event: "pat1"
• event: { "pat1" }
@@ -3171,15 +3214,15 @@ nvim_clear_autocmds({*opts}) *nvim_clear_autocmds()*
nvim_create_augroup({name}, {*opts}) *nvim_create_augroup()*
Create or get an autocommand group |autocmd-groups|.
- To get an existing group id, do: >
+ To get an existing group id, do: >lua
local id = vim.api.nvim_create_augroup("MyGroup", {
clear = false
})
<
Parameters: ~
- {name} String: The name of the group
- {opts} Dictionary 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|.
@@ -3190,84 +3233,54 @@ nvim_create_augroup({name}, {*opts}) *nvim_create_augroup()*
|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"
+ Creates an |autocommand| event handler, defined by `callback` (Lua function or Vimscript function name string) or `command` (Ex command string).
+ Example using Lua callback: >lua
vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, {
pattern = {"*.c", "*.h"},
- callback = myluafun, -- Or myvimfun
+ callback = function(ev)
+ print(string.format('event fired: s', vim.inspect(ev)))
+ end
})
<
- 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: >
+ Example using an Ex command as the handler: >lua
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"}
+ Note: `pattern` is NOT automatically expanded (unlike with |:autocmd|), thus names like
+ "$HOME" and "~" must be expanded explicitly: >lua
+ pattern = vim.fn.expand("~") .. "/some/path/*.py"
<
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
+ • {event} (string|array) Event(s) that will trigger the handler
+ (`callback` or `command`).
+ • {opts} Options dict:
+ • group (string|integer) optional: autocommand group name or
+ id to match against.
+ • pattern (string|array) optional: pattern(s) to match
+ literally |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>|
+ • desc (string) optional: description (for documentation and
+ troubleshooting).
+ • callback (function|string) optional: Lua function (or
+ Vimscript function name, if string) called when the
+ event(s) is triggered. Lua callback can return true to
+ delete the autocommand, and receives a table argument with
+ these keys:
+ • id: (number) autocommand id
+ • event: (string) name of the triggered event
+ |autocmd-events|
+ • group: (number|nil) autocommand group id, if any
+ • match: (string) expanded value of |<amatch>|
+ • buf: (number) expanded value of |<abuf>|
+ • file: (string) expanded value of |<afile>|
• data: (any) arbitrary data passed to
|nvim_exec_autocmds()|
@@ -3279,7 +3292,7 @@ nvim_create_autocmd({event}, {*opts}) *nvim_create_autocmd()*
autocommands |autocmd-nested|.
Return: ~
- Integer id of the created autocommand.
+ Autocommand id (number)
See also: ~
|autocommand|
@@ -3290,12 +3303,12 @@ nvim_del_augroup_by_id({id}) *nvim_del_augroup_by_id()*
To get a group id one can use |nvim_get_autocmds()|.
- NOTE: behavior differs from |augroup-delete|. When deleting a group,
+ 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.
+ • {id} Integer The id of the group.
See also: ~
|nvim_del_augroup_by_name()|
@@ -3304,15 +3317,15 @@ nvim_del_augroup_by_id({id}) *nvim_del_augroup_by_id()*
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,
+ 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.
+ • {name} String The name of the group.
See also: ~
- |autocommand-groups|
+ |autocmd-groups|
nvim_del_autocmd({id}) *nvim_del_autocmd()*
Delete an autocommand by id.
@@ -3320,7 +3333,7 @@ nvim_del_autocmd({id}) *nvim_del_autocmd()*
NOTE: Only autocommands created via the API have an id.
Parameters: ~
- {id} Integer The id returned by nvim_create_autocmd
+ • {id} Integer The id returned by nvim_create_autocmd
See also: ~
|nvim_create_autocmd()|
@@ -3330,8 +3343,8 @@ nvim_exec_autocmds({event}, {*opts}) *nvim_exec_autocmds()*
|autocmd-execute|.
Parameters: ~
- {event} (String|Array) The event or events to execute
- {opts} Dictionary of autocommand options:
+ • {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 "*"
@@ -3349,7 +3362,7 @@ nvim_exec_autocmds({event}, {*opts}) *nvim_exec_autocmds()*
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: >
+ These examples will get autocommands matching ALL the given criteria: >lua
-- Matches all criteria
autocommands = vim.api.nvim_get_autocmds({
group = "MyGroup",
@@ -3367,13 +3380,16 @@ nvim_get_autocmds({*opts}) *nvim_get_autocmds()*
autocommands that match any combination of them.
Parameters: ~
- {opts} Dictionary with at least one of the following:
+ • {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|.
+ |autocmd-pattern|. Cannot be used with {buffer}
+ • buffer: Buffer number or list of buffer numbers for buffer
+ local autocommands |autocmd-buflocal|. Cannot be used with
+ {pattern}
Return: ~
Array of autocommands matching the criteria, with each item containing
@@ -3413,9 +3429,9 @@ nvim_ui_attach({width}, {height}, {options}) *nvim_ui_attach()*
|RPC| only
Parameters: ~
- {width} Requested screen columns
- {height} Requested screen rows
- {options} |ui-option| map
+ • {width} Requested screen columns
+ • {height} Requested screen rows
+ • {options} |ui-option| map
nvim_ui_detach() *nvim_ui_detach()*
Deactivates UI events on the channel.
@@ -3427,8 +3443,8 @@ nvim_ui_detach() *nvim_ui_detach()*
*nvim_ui_pum_set_bounds()*
nvim_ui_pum_set_bounds({width}, {height}, {row}, {col})
- Tells Nvim the geometry of the popumenu, to align floating windows with an
- external popup menu.
+ Tells Nvim the geometry of the popupmenu, to align floating windows with
+ an external popup menu.
Note that this method is not to be confused with
|nvim_ui_pum_set_height()|, which sets the number of visible items in the
@@ -3441,20 +3457,26 @@ nvim_ui_pum_set_bounds({width}, {height}, {row}, {col})
|RPC| only
Parameters: ~
- {width} Popupmenu width.
- {height} Popupmenu height.
- {row} Popupmenu row.
- {col} Popupmenu height.
+ • {width} Popupmenu width.
+ • {height} Popupmenu height.
+ • {row} Popupmenu row.
+ • {col} Popupmenu height.
nvim_ui_pum_set_height({height}) *nvim_ui_pum_set_height()*
- Tells Nvim the number of elements displaying in the popumenu, to decide
+ Tells Nvim the number of elements displaying in the popupmenu, to decide
<PageUp> and <PageDown> movement.
Attributes: ~
|RPC| only
Parameters: ~
- {height} Popupmenu height, must be greater than zero.
+ • {height} Popupmenu height, must be greater than zero.
+
+nvim_ui_set_focus({gained}) *nvim_ui_set_focus()*
+ Tells the nvim server if focus was gained or lost by the GUI.
+
+ Attributes: ~
+ |RPC| only
nvim_ui_set_option({name}, {value}) *nvim_ui_set_option()*
TODO: Documentation
@@ -3479,8 +3501,8 @@ nvim_ui_try_resize_grid({grid}, {width}, {height})
|RPC| only
Parameters: ~
- {grid} The handle of the grid to be changed.
- {width} The new requested width.
- {height} The new requested height.
+ • {grid} The handle of the grid to be changed.
+ • {width} The new requested width.
+ • {height} The new requested height.
vim:tw=78:ts=8:sw=4:sts=4:et:ft=help:norl:
diff --git a/runtime/doc/arabic.txt b/runtime/doc/arabic.txt
index 0df861111c..0a80edb981 100644
--- a/runtime/doc/arabic.txt
+++ b/runtime/doc/arabic.txt
@@ -14,8 +14,9 @@ It is best to view this file with these settings within VIM's GUI: >
:set arabicshape
+------------------------------------------------------------------------------
Introduction
-------------
+
Arabic is a rather demanding language in which a number of special
features are required. Characters are right-to-left oriented and
ought to appear as such on the screen (i.e. from right to left).
@@ -34,8 +35,9 @@ The commands, prompts and help files are not in Arabic, therefore
the user interface remains the standard Vi interface.
+------------------------------------------------------------------------------
Highlights
-----------
+
o Editing left-to-right files as in the original Vim hasn't changed.
o Viewing and editing files in right-to-left windows. File
@@ -64,8 +66,8 @@ o Proper Bidirectional functionality is possible given Vim is
started within a Bidi capable terminal emulator.
+------------------------------------------------------------------------------
Arabic Fonts *arabicfonts*
-------------
Vim requires monospaced fonts of which there are many out there.
Arabic requires ISO-8859-6 as well as Presentation Form-B fonts
@@ -75,8 +77,8 @@ Do an Internet search or check www.arabeyes.org for further
info on where to obtain the necessary Arabic fonts.
+------------------------------------------------------------------------------
Font Installation
------------------
o Installation of fonts for X Window systems (Unix/Linux)
@@ -88,8 +90,9 @@ o Installation of fonts for X Window systems (Unix/Linux)
% xset +fp path_name_of_arabic_fonts_directory
+------------------------------------------------------------------------------
Usage
------
+
Prior to the actual usage of Arabic within Vim, a number of settings
need to be accounted for and invoked.
@@ -259,8 +262,8 @@ o Enable Arabic settings [short-cut]
':set arabicshape' to your vimrc file.
+------------------------------------------------------------------------------
Keymap/Keyboard *arabickeymap*
----------------
The character/letter encoding used in Vim is the standard UTF-8.
It is widely discouraged that any other encoding be used or even
@@ -276,7 +279,7 @@ o Keyboard
+ CTRL-^ in insert/replace mode toggles between Arabic/Latin mode
+ Keyboard mapping is based on the Microsoft's Arabic keymap (the
- de facto standard in the Arab world):
+ de facto standard in the Arab world): >
+---------------------------------------------------------------------+
|! |@ |# |$ |% |^ |& |* |( |) |_ |+ || |~ ّ |
@@ -291,17 +294,18 @@ o Keyboard
|Z ~ |X ْ |C { |V } |B لآ |N آ |M ' |< , |> . |? ؟ |
|z ئ |x ء |c ؤ |v ر |b لا |n ى |m ة |, و |. ز |/ ظ |
+-------------------------------------------------+
+<
+------------------------------------------------------------------------------
Restrictions
-------------
o Vim in its GUI form does not currently support Bi-directionality
(i.e. the ability to see both Arabic and Latin intermixed within
the same line).
+------------------------------------------------------------------------------
Known Bugs
-----------
There is one known minor bug,
diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt
index 63226fe701..8cc4754880 100644
--- a/runtime/doc/autocmd.txt
+++ b/runtime/doc/autocmd.txt
@@ -57,7 +57,7 @@ The special pattern <buffer> or <buffer=N> defines a buffer-local autocommand.
See |autocmd-buflocal|.
Note: The ":autocmd" command can only be followed by another command when the
-'|' appears where the pattern is expected. This works: >
+"|" appears where the pattern is expected. This works: >
:augroup mine | au! BufRead | augroup END
But this sees "augroup" as part of the defined command: >
:augroup mine | au! BufRead * | augroup END
@@ -412,6 +412,8 @@ CmdwinLeave Before leaving the command-line window.
|cmdwin-char|
*ColorScheme*
ColorScheme After loading a color scheme. |:colorscheme|
+ Not triggered if the color scheme is not
+ found.
The pattern is matched against the
colorscheme name. <afile> can be used for the
name of the actual file where this option was
@@ -588,6 +590,7 @@ FileChangedShell When Vim notices that the modification time of
- executing a shell command
- |:checktime|
- |FocusGained|
+
Not used when 'autoread' is set and the buffer
was not changed. If a FileChangedShell
autocommand exists the warning message and
@@ -678,16 +681,12 @@ FuncUndefined When a user function is used but it isn't
UIEnter After a UI connects via |nvim_ui_attach()|, or
after builtin TUI is started, after |VimEnter|.
Sets these |v:event| keys:
- chan: 0 for builtin TUI
- 1 for |--embed|
- |channel-id| of the UI otherwise
+ chan: |channel-id| of the UI
*UILeave*
UILeave After a UI disconnects from Nvim, or after
builtin TUI is stopped, after |VimLeave|.
Sets these |v:event| keys:
- chan: 0 for builtin TUI
- 1 for |--embed|
- |channel-id| of the UI otherwise
+ chan: |channel-id| of the UI
*InsertChange*
InsertChange When typing <Insert> while in Insert or
Replace mode. The |v:insertmode| variable
@@ -803,7 +802,7 @@ OptionSet After setting an option (except during
QuickFixCmdPre Before a quickfix command is run (|:make|,
|:lmake|, |:grep|, |:lgrep|, |:grepadd|,
|:lgrepadd|, |:vimgrep|, |:lvimgrep|,
- |:vimgrepadd|, |:lvimgrepadd|, |:cscope|,
+ |:vimgrepadd|, |:lvimgrepadd|,
|:cfile|, |:cgetfile|, |:caddfile|, |:lfile|,
|:lgetfile|, |:laddfile|, |:helpgrep|,
|:lhelpgrep|, |:cexpr|, |:cgetexpr|,
@@ -820,8 +819,8 @@ QuickFixCmdPre Before a quickfix command is run (|:make|,
QuickFixCmdPost Like QuickFixCmdPre, but after a quickfix
command is run, before jumping to the first
location. For |:cfile| and |:lfile| commands
- it is run after error file is read and before
- moving to the first error.
+ it is run after the error file is read and
+ before moving to the first error.
See |QuickFixCmdPost-example|.
*QuitPre*
QuitPre When using `:quit`, `:wq` or `:qall`, before
@@ -833,13 +832,13 @@ QuitPre When using `:quit`, `:wq` or `:qall`, before
See also |ExitPre|, |WinClosed|.
*RemoteReply*
RemoteReply When a reply from a Vim that functions as
- server was received |server2client()|. The
+ server was received server2client(). The
pattern is matched against the {serverid}.
<amatch> is equal to the {serverid} from which
the reply was sent, and <afile> is the actual
reply string.
Note that even if an autocommand is defined,
- the reply should be read with |remote_read()|
+ the reply should be read with remote_read()
to consume it.
*SearchWrapped*
SearchWrapped After making a search with |n| or |N| if the
@@ -993,6 +992,10 @@ TextChangedP After a change was made to the text in the
current buffer in Insert mode, only when the
popup menu is visible. Otherwise the same as
TextChanged.
+ *TextChangedT*
+TextChangedT After a change was made to the text in the
+ current buffer in |Terminal-mode|. Otherwise
+ the same as TextChanged.
*TextYankPost*
TextYankPost Just after a |yank| or |deleting| command, but not
if the black hole register |quote_| is used nor
@@ -1058,8 +1061,9 @@ VimResume After Nvim resumes from |suspend| state.
*VimSuspend*
VimSuspend Before Nvim enters |suspend| state.
*WinClosed*
-WinClosed After closing a window. The pattern is
- matched against the |window-ID|. Both
+WinClosed When closing a window, just before it is
+ removed from the window layout. The pattern
+ is matched against the |window-ID|. Both
<amatch> and <afile> are set to the |window-ID|.
After WinLeave.
Non-recursive (event cannot trigger itself).
@@ -1088,23 +1092,48 @@ WinNew When a new window was created. Not done for
Before WinEnter.
*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 After any window in the current tab page
+ scrolled the text (horizontally or vertically)
+ or changed width or height. See
+ |win-scrolled-resized|.
+
+ The pattern is matched against the |window-ID|
+ of the first window that scrolled or resized.
+ Both <amatch> and <afile> are set to the
+ |window-ID|.
+
+ |v:event| is set with information about size
+ and scroll changes. |WinScrolled-event|
+
+ Only starts triggering after startup finished
+ and the first screen redraw was done.
+ Does not trigger when defining the first
+ WinScrolled or WinResized event, but may
+ trigger when adding more.
+
+ Non-recursive: the event will not trigger
+ while executing commands for the WinScrolled
+ event. However, if the command causes a
+ window to scroll or change size, then another
WinScrolled event will be triggered later.
- Does not trigger when the command is added,
- only after the first scroll or resize.
+
+
+ *WinResized*
+WinResized After a window in the current tab page changed
+ width or height.
+ See |win-scrolled-resized|.
+
+ |v:event| is set with information about size
+ changes. |WinResized-event|
+
+ Same behavior as |WinScrolled| for the
+ pattern, triggering and recursiveness.
==============================================================================
6. Patterns *autocmd-pattern* *{aupat}*
-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: >
+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
@@ -1204,7 +1233,7 @@ still executed.
==============================================================================
7. Buffer-local autocommands *autocmd-buflocal* *autocmd-buffer-local*
- *<buffer=N>* *<buffer=abuf>* *E680*
+ *<buffer>* *<buffer=N>* *<buffer=abuf>* *E680*
Buffer-local autocommands are attached to a specific buffer. They are useful
if the buffer does not have a name and when the name does not match a specific
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index 0be9e9b9d1..4d2c85b134 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -4,11 +4,11 @@
VIM REFERENCE MANUAL by Bram Moolenaar
-Builtin functions *builtin-functions*
+Builtin functions *vimscript-functions* *builtin-functions*
-1. Overview |builtin-function-list|
-2. Details |builtin-function-details|
-3. Matching a pattern in a String |string-match|
+For functions grouped by what they are used for see |function-list|.
+
+ Type |gO| to see the table of contents.
==============================================================================
1. Overview *builtin-function-list*
@@ -68,8 +68,8 @@ 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}
+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
@@ -78,13 +78,13 @@ 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}
charclass({string}) Number character class of {string}
-charcol({expr}) Number column number of cursor or mark
+charcol({expr} [, {winid}]) 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
+col({expr} [, {winid}]) 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
@@ -96,8 +96,6 @@ 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|
@@ -136,7 +134,8 @@ exists({expr}) Number |TRUE| if {expr} exists
exp({expr}) Float exponential of {expr}
expand({expr} [, {nosuf} [, {list}]])
any expand special keywords in {expr}
-expandcmd({expr}) String expand {expr} like with `:edit`
+expandcmd({string} [, {options}])
+ String expand {string} like with `:edit`
extend({expr1}, {expr2} [, {expr3}])
List/Dict insert items of {expr2} into {expr1}
feedkeys({string} [, {mode}]) Number add key sequence to typeahead buffer
@@ -171,8 +170,10 @@ 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}
+getbufoneline({buf}, {lnum}) String line {lnum} of buffer {buf}
getbufvar({buf}, {varname} [, {def}])
any variable {varname} in buffer {buf}
+getcellwidths() List get character cell width overrides
getchangelist([{buf}]) List list of change list items
getchar([expr]) Number or String
get one character from the user
@@ -222,6 +223,7 @@ gettabvar({nr}, {varname} [, {def}])
gettabwinvar({tabnr}, {winnr}, {name} [, {def}])
any {name} in {winnr} in tab page {tabnr}
gettagstack([{nr}]) Dict get the tag stack of window {nr}
+gettext({text}) String lookup translation of {text}
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
@@ -279,6 +281,8 @@ 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}
+keytrans({string}) String translate internal keycodes to a form
+ that can be used by |:map|
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
@@ -315,9 +319,9 @@ matchfuzzypos({list}, {str} [, {dict}])
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}
+ String {count}th match of {pat} in {expr}
matchstrpos({expr}, {pat} [, {start} [, {count}]])
- List {count}'th match of {pat} in {expr}
+ 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}
menu_info({name} [, {mode}]) Dict get menu item information
@@ -348,6 +352,7 @@ pyxeval({expr}) any evaluate |python_x| expression
rand([{expr}]) Number get pseudo-random number
range({expr} [, {max} [, {stride}]])
List items from {expr} to {max}
+readblob({fname}) Blob read a |Blob| from {fname}
readdir({dir} [, {expr}]) List file names in {dir} selected by {expr}
readfile({fname} [, {type} [, {max}]])
List get list of lines from file {fname}
@@ -400,6 +405,7 @@ setbufvar({buf}, {varname}, {val}) set {varname} in buffer {buf} to {val}
setcellwidths({list}) none set character cell width overrides
setcharpos({expr}, {list}) Number set the {expr} position to {list}
setcharsearch({dict}) Dict set character search from {dict}
+setcmdline({str} [, {pos}]) Number set command-line
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
@@ -463,10 +469,11 @@ str2list({expr} [, {utf8}]) List convert each character of {expr} to
ASCII/UTF-8 value
str2nr({expr} [, {base} [, {quoted}]])
Number convert String to Number
+strcharlen({expr}) Number character length of the String {expr}
strcharpart({str}, {start} [, {len}])
String {len} characters of {str} at
character {start}
-strchars({expr} [, {skipcc}]) Number character length of the String {expr}
+strchars({expr} [, {skipcc}]) Number character count of the String {expr}
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}
@@ -527,6 +534,8 @@ 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
+virtcol2col({winid}, {lnum}, {col})
+ Number byte index of a character on screen
visualmode([expr]) String last visual mode used
wait({timeout}, {condition} [, {interval}])
Number Wait until {condition} is satisfied
@@ -631,6 +640,7 @@ append({lnum}, {text}) *append()*
text line below line {lnum} in the current buffer.
Otherwise append {text} as one text line below line {lnum} in
the current buffer.
+ Any type of item is accepted and converted to a String.
{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),
@@ -649,9 +659,10 @@ appendbufline({buf}, {lnum}, {text}) *appendbufline()*
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.
+ {lnum} is the line number to append below. Note that using
+ |line()| would use the current buffer, not the one appending
+ to. Use "$" to append at the end of the buffer. Other string
+ values are not supported.
On success 0 is returned, on failure 1 is returned.
@@ -784,7 +795,8 @@ browsedir({title}, {initdir})
browsing is not possible, an empty string is returned.
bufadd({name}) *bufadd()*
- Add a buffer to the buffer list with String {name}.
+ Add a buffer to the buffer list with name {name} (must be a
+ String).
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
@@ -835,7 +847,8 @@ 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.
+ then there is no change. If the buffer is not related to a
+ file the no file is read (e.g., when 'buftype' is "nofile").
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()|.
@@ -910,7 +923,8 @@ bufwinid({buf}) *bufwinid()*
echo "A window containing buffer 1 is " .. (bufwinid(1))
<
- Only deals with the current tab page.
+ Only deals with the current tab page. See |win_findbuf()| for
+ finding more.
Can also be used as a |method|: >
FindBuffer()->bufwinid()
@@ -943,7 +957,7 @@ byte2line({byte}) *byte2line()*
GetOffset()->byte2line()
byteidx({expr}, {nr}) *byteidx()*
- Return byte index of the {nr}'th character in the String
+ 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
@@ -1027,7 +1041,7 @@ chanclose({id} [, {stream}]) *chanclose()*
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.
+ omitted.
chansend({id}, {data}) *chansend()*
Send data to channel {id}. For a job, it writes it to the
@@ -1078,8 +1092,8 @@ charclass({string}) *charclass()*
Returns 0 if {string} is not a |String|.
- *charcol()*
-charcol({expr}) Same as |col()| but returns the character index of the column
+charcol({expr} [, {winid}]) *charcol()*
+ Same as |col()| but returns the character index of the column
position given with {expr} instead of the byte position.
Example:
@@ -1126,16 +1140,20 @@ chdir({dir}) *chdir()*
directory (|:tcd|) then changes the tabpage local
directory.
- Otherwise, changes the global directory.
+ {dir} must be a String.
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
+ if save_dir != ""
" ... do some work
call chdir(save_dir)
endif
+
+< Can also be used as a |method|: >
+ GetDir()->chdir()
<
cindent({lnum}) *cindent()*
Get the amount of indent for line {lnum} according the C
@@ -1157,8 +1175,8 @@ clearmatches([{win}]) *clearmatches()*
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
+col({expr} [, {winid}) *col()*
+ 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
@@ -1173,6 +1191,8 @@ col({expr}) The result is a Number, which is the byte index of the column
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.
+ With the optional {winid} argument the values are obtained for
+ that window instead of the current window.
To get the line number use |line()|. To get both use
|getpos()|.
For the screen column position use |virtcol()|. For the
@@ -1183,16 +1203,15 @@ col({expr}) The result is a Number, which is the byte index of the column
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.
+< The first column is 1. Returns 0 if {expr} is invalid or when
+ the window with ID {winid} is not found.
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>
+ line. Also, when using a <Cmd> mapping the cursor isn't
+ moved, this can be used to obtain the column in Insert mode: >
+ :imap <F2> <Cmd>echo col(".").."\n"<CR>
< Can also be used as a |method|: >
GetPos()->col()
@@ -1270,7 +1289,7 @@ complete_info([{what}]) *complete_info()*
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]
+ inserted Inserted string. [NOT IMPLEMENTED YET]
*complete_info_mode*
mode values are:
@@ -1425,47 +1444,6 @@ count({comp}, {expr} [, {ic} [, {start}]]) *count()*
Can also be used as a |method|: >
mylist->count(val)
<
- *cscope_connection()*
-cscope_connection([{num} , {dbpath} [, {prepend}]])
- Checks for the existence of a |cscope| connection. If no
- 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|).
@@ -1508,9 +1486,10 @@ cursor({list})
|setcursorcharpos()|.
Does not change the jumplist.
+ {lnum} is used like with |getline()|, except that if {lnum} is
+ zero, the cursor will stay in the current line.
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.
@@ -1526,6 +1505,18 @@ cursor({list})
Can also be used as a |method|: >
GetCursorPos()->cursor()
+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-debug|.
+ (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()
+
deepcopy({expr} [, {noref}]) *deepcopy()* *E698*
Make a copy of {expr}. For Numbers and Strings this isn't
different from using {expr} directly.
@@ -1607,9 +1598,9 @@ dictwatcheradd({dict}, {pattern}, {callback}) *dictwatcheradd()*
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
+ 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:
@@ -1967,18 +1958,6 @@ exp({expr}) *exp()*
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.
@@ -2010,6 +1989,8 @@ expand({string} [, {nosuf} [, {list}]]) *expand()*
a function
<SID> "<SNR>123_" where "123" is the
current script ID |<SID>|
+ <script> sourced script file, or script file
+ where the current function was defined
<stack> call stack
<cword> word under the cursor
<cWORD> WORD under the cursor
@@ -2043,6 +2024,9 @@ expand({string} [, {nosuf} [, {list}]]) *expand()*
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 'verbose' is set then expanding '%', '#' and <> items
+ will result in an error message if the argument cannot be
+ expanded.
When {string} does not start with '%', '#' or '<', it is
expanded like a file name is expanded on the command line.
@@ -2068,18 +2052,27 @@ expand({string} [, {nosuf} [, {list}]]) *expand()*
Can also be used as a |method|: >
Getpattern()->expand()
-expandcmd({string}) *expandcmd()*
+expandcmd({string} [, {options}]) *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.
+
+ The following items are supported in the {options} Dict
+ argument:
+ errmsg If set to TRUE, error messages are displayed
+ if an error is encountered during expansion.
+ By default, error messages are not displayed.
+
Returns the expanded string. If an error is encountered
during expansion, the unmodified {string} is returned.
+
Example: >
:echo expandcmd('make %<.o')
-< make /path/runtime/doc/builtin.o ~
-
+ make /path/runtime/doc/builtin.o
+ :echo expandcmd('make %<.o', {'errmsg': v:true})
+<
Can also be used as a |method|: >
GetCommand()->expandcmd()
<
@@ -2305,7 +2298,7 @@ flatten({list} [, {maxdepth}]) *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.
+ {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
@@ -2496,10 +2489,11 @@ funcref({name} [, {arglist}] [, {dict}])
Can also be used as a |method|: >
GetFuncname()->funcref([arg])
<
- *function()* *E700* *E922* *E923*
+ *function()* *partial* *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 be the name of 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}
@@ -2518,30 +2512,56 @@ function({name} [, {arglist}] [, {dict}])
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')
+< With a |method|: >
+ func Callback(one, two, three)
+ "...
+ let Partial = function('Callback', ['two'])
+ "...
+ eval 'one'->Partial('three')
+< Invokes the function as with: >
+ call Callback('one', 'two', 'three')
+
+< The function() call can be nested to add more arguments to the
+ Funcref. The extra arguments are appended to the list of
+ arguments. Example: >
+ func Callback(arg1, arg2, name)
+ "...
+ let Func = function('Callback', ['one'])
+ let Func2 = function(Func, ['two'])
+ "...
+ call Func2('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 use of function() is not needed when there are no extra
+ arguments, these two are equivalent, if Callback() is defined
+ as context.Callback(): >
+ let Func = function('Callback', context)
+ let Func = context.Callback
< 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)
@@ -2587,7 +2607,7 @@ get({dict}, {key} [, {default}])
{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.
+ "default" when it does not exist.
get({func}, {what})
Get item {what} from Funcref {func}. Possible values for
{what} are:
@@ -2667,11 +2687,13 @@ getbufinfo([{dict}])
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.
+ |List| with only the line {lnum} is returned. See
+ `getbufoneline()` for only getting the line.
For the use of {buf}, see |bufname()| above.
@@ -2694,6 +2716,11 @@ getbufline({buf}, {lnum} [, {end}])
< Can also be used as a |method|: >
GetBufnr()->getbufline(lnum)
+<
+ *getbufoneline()*
+getbufoneline({buf}, {lnum})
+ Just like `getbufline()` but only get one line and return it
+ as a string.
getbufvar({buf}, {varname} [, {def}]) *getbufvar()*
The result is the value of option or local buffer variable
@@ -2719,6 +2746,13 @@ getbufvar({buf}, {varname} [, {def}]) *getbufvar()*
< Can also be used as a |method|: >
GetBufnr()->getbufvar(varname)
<
+getcellwidths() *getcellwidths()*
+ Returns a |List| of cell widths of character ranges overridden
+ by |setcellwidths()|. The format is equal to the argument of
+ |setcellwidths()|. If no character ranges have their cell
+ widths overridden, an empty List is returned.
+
+
getchangelist([{buf}]) *getchangelist()*
Returns the |changelist| for the buffer {buf}. For the use
of {buf}, see |bufname()| above. If buffer {buf} doesn't
@@ -2870,7 +2904,8 @@ getcmdcompltype() *getcmdcompltype()*
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()|.
+ Also see |getcmdtype()|, |setcmdpos()|, |getcmdline()| and
+ |setcmdline()|.
Returns an empty string when completion is not defined.
getcmdline() *getcmdline()*
@@ -2879,7 +2914,8 @@ getcmdline() *getcmdline()*
|c_CTRL-R_=|.
Example: >
:cmap <F7> <C-\>eescape(getcmdline(), ' \')<CR>
-< Also see |getcmdtype()|, |getcmdpos()| and |setcmdpos()|.
+< Also see |getcmdtype()|, |getcmdpos()|, |setcmdpos()| and
+ |setcmdline()|.
Returns an empty string when entering a password or using
|inputsecret()|.
@@ -2889,7 +2925,8 @@ getcmdpos() *getcmdpos()*
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()|.
+ Also see |getcmdtype()|, |setcmdpos()|, |getcmdline()| and
+ |setcmdline()|.
getcmdscreenpos() *getcmdscreenpos()*
Return the screen position of the cursor in the command line
@@ -2898,7 +2935,8 @@ getcmdscreenpos() *getcmdscreenpos()*
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()|.
+ Also see |getcmdpos()|, |setcmdpos()|, |getcmdline()| and
+ |setcmdline()|.
getcmdtype() *getcmdtype()*
Return the current command-line type. Possible return values
@@ -2928,12 +2966,12 @@ getcompletion({pat}, {type} [, {filtered}]) *getcompletion()*
arglist file names in argument list
augroup autocmd groups
buffer buffer names
- behave :behave suboptions
+ behave |:behave| suboptions
+ breakpoint |:breakadd| and |:breakdel| 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
@@ -2945,7 +2983,7 @@ getcompletion({pat}, {type} [, {filtered}]) *getcompletion()*
function function name
help help subjects
highlight highlight groups
- history :history suboptions
+ history |:history| suboptions
locale locale names (as output of locale -a)
mapclear buffer argument
mapping mapping name
@@ -2953,6 +2991,7 @@ getcompletion({pat}, {type} [, {filtered}]) *getcompletion()*
messages |:messages| suboptions
option options
packadd optional package |pack-add| names
+ scriptnames sourced script names |:scriptnames|
shellcmd Shell command
sign |:sign| suboptions
syntax syntax file names |'syntax'|
@@ -2970,6 +3009,13 @@ getcompletion({pat}, {type} [, {filtered}]) *getcompletion()*
is applied to filter the results. Otherwise all the matches
are returned. The 'wildignorecase' option always applies.
+ If the 'wildoptions' option contains "fuzzy", then fuzzy
+ matching is used to get the completion matches. Otherwise
+ regular expression matching is used. Thus this function
+ follows the user preference, what happens on the command line.
+ If you do not want this you can make 'wildoptions' empty
+ before calling getcompletion() and restore it afterwards.
+
If {type} is "cmdline", then the |cmdline-completion| result is
returned. For example, to complete the possible values after
a ":call" command: >
@@ -2990,7 +3036,7 @@ getcurpos([{winid}])
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
+ the cursor is returned in "col". To get the character
position, use |getcursorcharpos()|.
The optional {winid} argument can specify the window. It can
@@ -3178,7 +3224,8 @@ getline({lnum} [, {end}])
< Can also be used as a |method|: >
ComputeLnum()->getline()
-< To get lines from another buffer see |getbufline()|
+< To get lines from another buffer see |getbufline()| and
+ |getbufoneline()|
getloclist({nr} [, {what}]) *getloclist()*
Returns a |List| with all the entries in the location list for
@@ -3196,7 +3243,7 @@ getloclist({nr} [, {what}]) *getloclist()*
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
+ 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
@@ -3245,18 +3292,18 @@ getmatches([{win}]) *getmatches()*
an empty list is returned.
Example: >
:echo getmatches()
-< [{'group': 'MyGroup1', 'pattern': 'TODO',
- 'priority': 10, 'id': 1}, {'group': 'MyGroup2',
- 'pattern': 'FIXME', 'priority': 10, 'id': 2}] >
+< [{"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}] >
+< [{"group": "MyGroup1", "pattern": "TODO",
+ "priority": 10, "id": 1}, {"group": "MyGroup2",
+ "pattern": "FIXME", "priority": 10, "id": 2}] >
:unlet m
<
getmousepos() *getmousepos()*
@@ -3369,7 +3416,7 @@ getqflist([{what}]) *getqflist()*
|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'.
+ 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
@@ -3455,7 +3502,7 @@ getreginfo([{regname}]) *getreginfo()*
Dictionary with the following entries:
regcontents List of lines contained in register
{regname}, like
- |getreg|({regname}, 1, 1).
+ getreg({regname}, 1, 1).
regtype the type of register {regname}, as in
|getregtype()|.
isunnamed Boolean flag, v:true if this register
@@ -3580,6 +3627,19 @@ gettagstack([{winnr}]) *gettagstack()*
Can also be used as a |method|: >
GetWinnr()->gettagstack()
+
+gettext({text}) *gettext()*
+ Translate String {text} if possible.
+ This is mainly for use in the distributed Vim scripts. When
+ generating message translations the {text} is extracted by
+ xgettext, the translator can add the translated message in the
+ .po file and Vim will lookup the translation when gettext() is
+ called.
+ For {text} double quoted strings are preferred, because
+ xgettext does not understand escaping in single quoted
+ strings.
+
+
getwininfo([{winid}]) *getwininfo()*
Returns information about windows as a |List| with Dictionaries.
@@ -3759,15 +3819,15 @@ 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
+ To get the system name use |vim.loop|.os_uname() in Lua: >
+ :lua print(vim.loop.os_uname().sysname)
+
+< If the code has a syntax error then Vimscript may skip the
+ rest of the line. 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
@@ -4371,7 +4431,7 @@ jobstart({cmd} [, {opts}]) *jobstart()*
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|)
+ pairs extending (or replace with "clear_env")
the current environment. |jobstart-env|
height: (number) Height of the `pty` terminal.
|on_exit|: (function) Callback invoked when the job exits.
@@ -4495,6 +4555,16 @@ keys({dict}) *keys()*
Can also be used as a |method|: >
mydict->keys()
+keytrans({string}) *keytrans()*
+ Turn the internal byte representation of keys into a form that
+ can be used for |:map|. E.g. >
+ :let xx = "\<C-Home>"
+ :echo keytrans(xx)
+< <C-Home>
+
+ Can also be used as a |method|: >
+ "\<C-Home>"->keytrans()
+
< *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
@@ -4903,7 +4973,7 @@ match({expr}, {pat} [, {start} [, {count}]]) *match()*
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
+ 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)
@@ -4955,7 +5025,7 @@ matchadd({group}, {pattern} [, {priority} [, {id} [, {dict}]]])
respectively. 3 is reserved for use by the |matchparen|
plugin.
If the {id} argument is not specified or -1, |matchadd()|
- automatically chooses a free ID.
+ automatically chooses a free ID, which is at least 1000.
The optional {dict} argument allows for further custom
values. Currently this is used to specify a match specific
@@ -4963,7 +5033,7 @@ matchadd({group}, {pattern} [, {priority} [, {id} [, {dict}]]])
highlighted matches. The dict can have the following members:
conceal Special character to show instead of the
- match (only for |hl-Conceal| highlighed
+ match (only for |hl-Conceal| highlighted
matches, see |:syn-cchar|)
window Instead of the current window use the
window with this number or window ID.
@@ -5013,8 +5083,6 @@ matchaddpos({group}, {pos} [, {priority} [, {id} [, {dict}]]])
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: >
@@ -5135,7 +5203,7 @@ matchfuzzy({list}, {str} [, {dict}]) *matchfuzzy()*
: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']. >
+< results in `['two one', 'one two']` . >
:echo ['one two', 'two one']->matchfuzzy('two one',
\ {'matchseq': 1})
< results in ['two one'].
@@ -5155,12 +5223,12 @@ matchfuzzypos({list}, {str} [, {dict}]) *matchfuzzypos()*
Example: >
:echo matchfuzzypos(['testing'], 'tsg')
-< results in [['testing'], [[0, 2, 6]], [99]] >
+< results in [["testing"], [[0, 2, 6]], [99]] >
:echo matchfuzzypos(['clay', 'lacy'], 'la')
-< results in [['lacy', 'clay'], [[0, 1], [1, 2]], [153, 133]] >
+< 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]]
+< 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
@@ -5580,12 +5648,19 @@ nvim_...({...}) *E5555* *nvim_...()* *eval-api*
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.
+ Also see `and()` and `xor()`.
Example: >
:let bits = or(bits, 0x80)
< Can also be used as a |method|: >
:let bits = bits->or(0x80)
-pathshorten({expr} [, {len}]) *pathshorten()*
+< Rationale: The reason this is a function and not using the "|"
+ character like many languages, is that Vi has always used "|"
+ to separate commands. In many places it would not be clear if
+ "|" is an operator or a command separator.
+
+
+pathshorten({path} [, {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.
@@ -6005,9 +6080,19 @@ rand([{expr}]) *rand()*
Can also be used as a |method|: >
seed->rand()
<
+
+readblob({fname}) *readblob()*
+ Read file {fname} in binary mode and return a |Blob|.
+ When the file can't be opened an error message is given and
+ the result is an empty |Blob|.
+ Also see |readfile()| and |writefile()|.
+
+
*readdir()*
readdir({directory} [, {expr}])
Return a list with file and directory names in {directory}.
+ You can also use |glob()| if you don't need to do complicated
+ things, such as limiting the number of matches.
When {expr} is omitted all entries are included.
When {expr} is given, it is evaluated to check what to do:
@@ -6037,6 +6122,7 @@ readdir({directory} [, {expr}])
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
@@ -6048,8 +6134,6 @@ readfile({fname} [, {type} [, {max}]])
- 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.
@@ -6066,6 +6150,9 @@ readfile({fname} [, {type} [, {max}]])
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.
+ Deprecated (use |readblob()| instead): When {type} contains
+ "B" a |Blob| is returned with the binary data of the file
+ unmodified.
When the file can't be opened an error message is given and
the result is an empty list.
Also see |writefile()|.
@@ -6106,7 +6193,9 @@ 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()*
+reltime()
+reltime({start})
+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
@@ -6160,7 +6249,8 @@ reltimestr({time}) *reltimestr()*
Can also be used as a |method|: >
reltime(start)->reltimestr()
<
-remove({list}, {idx} [, {end}]) *remove()*
+remove({list}, {idx})
+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
@@ -6178,7 +6268,8 @@ remove({list}, {idx} [, {end}]) *remove()*
Can also be used as a |method|: >
mylist->remove(idx)
-remove({blob}, {idx} [, {end}])
+remove({blob}, {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
@@ -6417,8 +6508,10 @@ search({pattern} [, {flags} [, {stopline} [, {timeout} [, {skip}]]]])
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.
+ search starts one column after the start of the match. This
+ matters for overlapping matches. See |cpo-c|. You can also
+ insert "\ze" to change where the match ends, see |/\ze|.
+
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
@@ -6607,7 +6700,7 @@ searchcount([{options}]) *searchcount()*
pos |List| `[lnum, col, off]` value
when recomputing the result.
this changes "current" result
- value. see |cursor()|, |getpos()
+ value. see |cursor()|, |getpos()|
(default: cursor's position)
Can also be used as a |method|: >
@@ -6763,18 +6856,24 @@ serverstart([{address}]) *serverstart()*
|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: >
+ Returns the address string (which may differ from the
+ {address} argument, see below).
+
+ - If {address} has a colon (":") it is a TCP/IPv4/IPv6 address
+ where the last ":" separates host and port (empty or zero
+ assigns a random port).
+ - Else {address} is the path to a named pipe (except on Windows).
+ - If {address} has no slashes ("/") it is treated as the
+ "name" part of a generated path in this format: >
+ stdpath("run").."/{name}.{pid}.{counter}"
+< - If {address} is omitted the name is "nvim". >
+ :echo serverstart()
+ => /tmp/nvim.bram/oknANW/nvim.15430.5
+
+< Example bash command to list all Nvim servers: >
+ ls ${XDG_RUNTIME_DIR:-${TMPDIR}nvim.${USER}}/*/nvim.*.0
+
+< Example named pipe: >
if has('win32')
echo serverstart('\\.\pipe\nvim-pipe-1234')
else
@@ -6839,29 +6938,38 @@ setbufvar({buf}, {varname}, {val}) *setbufvar()*
setcellwidths({list}) *setcellwidths()*
Specify overrides for cell widths of character ranges. This
- tells Vim how wide characters are, counted in screen cells.
- This overrides 'ambiwidth'. Example: >
- setcellwidths([[0xad, 0xad, 1],
- \ [0x2194, 0x2199, 2]])
-
-< *E1109* *E1110* *E1111* *E1112* *E1113* *E1114*
- The {list} argument is a list of lists with each three
- numbers. These three numbers are [low, high, width]. "low"
- and "high" can be the same, in which case this refers to one
- character. Otherwise it is the range of characters from "low"
- to "high" (inclusive). "width" is either 1 or 2, indicating
- the character width in screen cells.
+ tells Vim how wide characters are when displayed in the
+ terminal, counted in screen cells. The values override
+ 'ambiwidth'. Example: >
+ call setcellwidths([
+ \ [0x111, 0x111, 1],
+ \ [0x2194, 0x2199, 2],
+ \ ])
+
+< The {list} argument is a List of Lists with each three
+ numbers: [{low}, {high}, {width}]. *E1109* *E1110*
+ {low} and {high} can be the same, in which case this refers to
+ one character. Otherwise it is the range of characters from
+ {low} to {high} (inclusive). *E1111* *E1114*
+ Only characters with value 0x80 and higher can be used.
+
+ {width} must be either 1 or 2, indicating the character width
+ in screen cells. *E1112*
An error is given if the argument is invalid, also when a
- range overlaps with another.
- Only characters with value 0x100 and higher can be used.
+ range overlaps with another. *E1113*
If the new value causes 'fillchars' or 'listchars' to become
invalid it is rejected and an error is given.
- To clear the overrides pass an empty list: >
- setcellwidths([]);
+ To clear the overrides pass an empty {list}: >
+ call setcellwidths([])
+
< You can use the script $VIMRUNTIME/tools/emoji_list.vim to see
- the effect for known emoji characters.
+ the effect for known emoji characters. Move the cursor
+ through the text to check if the cell widths of your terminal
+ match with what Vim knows about each emoji. If it doesn't
+ look right you need to adjust the {list} argument.
+
setcharpos({expr}, {list}) *setcharpos()*
Same as |setpos()| but uses the specified column number as the
@@ -6900,6 +7008,16 @@ setcharsearch({dict}) *setcharsearch()*
Can also be used as a |method|: >
SavedSearch()->setcharsearch()
+setcmdline({str} [, {pos}]) *setcmdline()*
+ Set the command line to {str} and set the cursor position to
+ {pos}.
+ If {pos} is omitted, the cursor is positioned after the text.
+ Returns 0 when successful, 1 when not editing the command
+ line.
+
+ Can also be used as a |method|: >
+ GetText()->setcmdline()
+
setcmdpos({pos}) *setcmdpos()*
Set the cursor position in the command line to byte position
{pos}. The first position is 1.
@@ -6912,8 +7030,8 @@ setcmdpos({pos}) *setcmdpos()*
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.
+ Returns 0 when successful, 1 when not editing the command
+ line.
Can also be used as a |method|: >
GetPos()->setcmdpos()
@@ -6972,6 +7090,8 @@ setline({lnum}, {text}) *setline()*
{lnum} is used like with |getline()|.
When {lnum} is just below the last line the {text} will be
added below the last line.
+ {text} can be any type or a List of any type, each item is
+ converted to a String.
If this succeeds, FALSE is returned. If this fails (most likely
because {lnum} is invalid) TRUE is returned.
@@ -7014,8 +7134,8 @@ setloclist({nr}, {list} [, {action} [, {what}]]) *setloclist()*
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
+ 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
@@ -7150,7 +7270,7 @@ setqflist({list} [, {action} [, {what}]]) *setqflist()*
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 '$',
+ 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}
@@ -7195,6 +7315,7 @@ setqflist({list} [, {action} [, {what}]]) *setqflist()*
*setreg()*
setreg({regname}, {value} [, {options}])
Set the register {regname} to {value}.
+ If {regname} is "" or "@", the unnamed register '"' is used.
The {regname} argument is a string.
{value} may be any value returned by |getreg()| or
@@ -7642,7 +7763,7 @@ sqrt({expr}) *sqrt()*
:echo sqrt(100)
< 10.0 >
:echo sqrt(-4.01)
-< str2float('nan')
+< str2float("nan")
NaN may be different, it depends on system libraries.
Can also be used as a |method|: >
@@ -7697,14 +7818,13 @@ stdpath({what}) *stdpath()* *E6100*
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 String User data directory.
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.
+ drafts, swap, undo, |shada|.
Example: >
:echo stdpath("config")
@@ -7769,6 +7889,21 @@ str2nr({string} [, {base}]) *str2nr()*
Can also be used as a |method|: >
GetText()->str2nr()
+
+strcharlen({string}) *strcharlen()*
+ The result is a Number, which is the number of characters
+ in String {string}. Composing characters are ignored.
+ |strchars()| can count the number of characters, counting
+ composing characters separately.
+
+ Returns 0 if {string} is empty or on error.
+
+ Also see |strlen()|, |strdisplaywidth()| and |strwidth()|.
+
+ Can also be used as a |method|: >
+ GetText()->strcharlen()
+
+
strcharpart({src}, {start} [, {len}]) *strcharpart()*
Like |strpart()| but using character index and length instead
of byte index and length. Composing characters are counted
@@ -7783,12 +7918,14 @@ strcharpart({src}, {start} [, {len}]) *strcharpart()*
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.
+ |strcharlen()| always does this.
Returns zero on error.
@@ -7883,7 +8020,7 @@ stridx({haystack}, {needle} [, {start}]) *stridx()*
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
@@ -8027,7 +8164,7 @@ strwidth({string}) *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}
+ 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.
@@ -8175,7 +8312,7 @@ synIDattr({synID}, {what} [, {mode}]) *synIDattr()*
"bg" background color (as with "fg")
"font" font name (only available in the GUI)
|highlight-font|
- "sp" special color (as with "fg") |highlight-guisp|
+ "sp" special color (as with "fg") |guisp|
"fg#" like "fg", but for the GUI and the GUI is
running the name in "#RRGGBB" form
"bg#" like "fg#" for "bg"
@@ -8191,6 +8328,7 @@ synIDattr({synID}, {what} [, {mode}]) *synIDattr()*
"underdotted" "1" if dotted underlined
"underdashed" "1" if dashed underlined
"strikethrough" "1" if struckthrough
+ "altfont" "1" if alternative font
"nocombine" "1" if nocombine
Returns an empty string on error.
@@ -8231,12 +8369,12 @@ synconcealed({lnum}, {col}) *synconcealed()*
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]
+ 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()*
@@ -8780,6 +8918,26 @@ virtcol({expr}) *virtcol()*
< Can also be used as a |method|: >
GetPos()->virtcol()
+virtcol2col({winid}, {lnum}, {col}) *virtcol2col()*
+ The result is a Number, which is the byte index of the
+ character in window {winid} at buffer line {lnum} and virtual
+ column {col}.
+
+ If {col} is greater than the last virtual column in line
+ {lnum}, then the byte index of the character at the last
+ virtual column is returned.
+
+ The {winid} argument can be the window number or the
+ |window-ID|. If this is zero, then the current window is used.
+
+ Returns -1 if the window {winid} doesn't exist or the buffer
+ line {lnum} or virtual column {col} is invalid.
+
+ See also |screenpos()|, |virtcol()| and |col()|.
+
+ Can also be used as a |method|: >
+ GetWinid()->virtcol2col(lnum, col)
+
visualmode([{expr}]) *visualmode()*
The result is a String, which describes the last Visual mode
used in the current buffer. Initially it returns an empty
@@ -8833,7 +8991,12 @@ win_execute({id}, {command} [, {silent}]) *win_execute()*
have unexpected side effects. Use |:noautocmd| if needed.
Example: >
call win_execute(winid, 'syntax enable')
-<
+< Doing the same with `setwinvar()` would not trigger
+ autocommands and not actually show syntax highlighting.
+
+ When window {id} does not exist then no error is given and
+ an empty string is returned.
+
Can also be used as a |method|, the base is passed as the
second argument: >
GetCommand()->win_execute(winid)
@@ -8914,6 +9077,7 @@ win_move_separator({nr}, {offset}) *win_move_separator()*
FALSE otherwise.
This will fail for the rightmost window and a full-width
window, since it has no separator on the right.
+ Only works for the current tab page. *E1308*
Can also be used as a |method|: >
GetWinnr()->win_move_separator(offset)
@@ -8928,6 +9092,7 @@ win_move_statusline({nr}, {offset}) *win_move_statusline()*
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.
+ Only works for the current tab page.
Can also be used as a |method|: >
GetWinnr()->win_move_statusline(offset)
@@ -9014,12 +9179,12 @@ winlayout([{tabnr}]) *winlayout()*
returns an empty list.
For a leaf window, it returns:
- ['leaf', {winid}]
+ ["leaf", {winid}]
For horizontally split windows, which form a column, it
returns:
- ['col', [{nested list of windows}]]
+ ["col", [{nested list of windows}]]
For vertically split windows, which form a row, it returns:
- ['row', [{nested list of windows}]]
+ ["row", [{nested list of windows}]]
Example: >
" Only one window in the tab page
@@ -9211,6 +9376,7 @@ writefile({object}, {fname} [, {flags}])
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.
+ Also see `and()` and `or()`.
Example: >
:let bits = xor(bits, 0x80)
<
diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt
index a4ff4474e6..990ba3d8fd 100644
--- a/runtime/doc/change.txt
+++ b/runtime/doc/change.txt
@@ -204,7 +204,6 @@ gR Enter Virtual Replace mode: Each character you type
*v_S*
{Visual}["x]S Delete the highlighted lines [into register x] and
start insert (for {Visual} see |Visual-mode|).
-
*v_R*
{Visual}["x]R Currently just like {Visual}["x]S. In a next version
it might work differently.
@@ -441,13 +440,13 @@ steps to make a numbered list.
SHIFTING LINES LEFT OR RIGHT *shift-left-right*
*<*
-<{motion} Shift {motion} lines one 'shiftwidth' leftwards.
+ <{motion} Shift {motion} lines one 'shiftwidth' leftwards.
If the 'shiftwidth' option is set to zero, the amount
of indent is calculated at the first non-blank
character in the line.
*<<*
-<< Shift [count] lines one 'shiftwidth' leftwards.
+ << Shift [count] lines one 'shiftwidth' leftwards.
*v_<*
{Visual}[count]< Shift the highlighted lines [count] 'shiftwidth'
@@ -1130,11 +1129,20 @@ 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
-each of the selected lines (thus replacing the blockwise selected region by a
-block of the pasted line).
+ *blockwise-put*
+When a register contains text from one line (characterwise), using a
+blockwise Visual selection, putting that register will paste that text
+repeatedly in each of the selected lines, thus replacing the blockwise
+selected region by multiple copies of the register text. For example:
+ - yank the word "TEXT" into a register with `yw`
+ - select a visual block, marked with "v" in this text:
+ aaavvaaa
+ bbbvvbbb
+ cccvvccc
+ - press `p`, results in:
+ aaaTEXTaaa
+ bbbTEXTbbb
+ cccTEXTccc
*blockwise-register*
If you use a blockwise Visual mode command to get the text into the register,
diff --git a/runtime/doc/channel.txt b/runtime/doc/channel.txt
index d4bed7a5f2..1c52b2d692 100644
--- a/runtime/doc/channel.txt
+++ b/runtime/doc/channel.txt
@@ -48,21 +48,22 @@ a job channel using RPC, bytes can still be read over its stderr. Similarly,
only bytes can be written to Nvim's own stderr.
*channel-callback*
-on_stdout({chan-id}, {data}, {name}) *on_stdout*
-on_stderr({chan-id}, {data}, {name}) *on_stderr*
-on_stdin({chan-id}, {data}, {name}) *on_stdin*
-on_data({chan-id}, {data}, {name}) *on_data*
+- on_stdout({chan-id}, {data}, {name}) *on_stdout*
+- on_stderr({chan-id}, {data}, {name}) *on_stderr*
+- on_stdin({chan-id}, {data}, {name}) *on_stdin*
+- on_data({chan-id}, {data}, {name}) *on_data*
+
Scripts can react to channel activity (received data) via callback
functions assigned to the `on_stdout`, `on_stderr`, `on_stdin`, or
`on_data` option keys. Callbacks should be fast: avoid potentially
slow/expensive work.
Parameters: ~
- {chan-id} Channel handle. |channel-id|
- {data} Raw data (|readfile()|-style list of strings) read from
+ - {chan-id} Channel handle. |channel-id|
+ - {data} Raw data (|readfile()|-style list of strings) read from
the channel. EOF is a single-item list: `['']`. First and
last items may be partial lines! |channel-lines|
- {name} Stream name (string) like "stdout", so the same function
+ - {name} Stream name (string) like "stdout", so the same function
can handle multiple streams. Event names depend on how the
channel was opened and in what mode/protocol.
@@ -83,13 +84,14 @@ on_data({chan-id}, {data}, {name}) *on_data*
the final `['']` emitted at EOF):
- `foobar` may arrive as `['fo'], ['obar']`
- `foo\nbar` may arrive as
- `['foo','bar']`
- or `['foo',''], ['bar']`
- or `['foo'], ['','bar']`
- or `['fo'], ['o','bar']`
+ - `['foo','bar']`
+ - or `['foo',''], ['bar']`
+ - or `['foo'], ['','bar']`
+ - or `['fo'], ['o','bar']`
+
There are two ways to deal with this:
- 1. To wait for the entire output, use |channel-buffered| mode.
- 2. To read line-by-line, use the following code: >
+ - 1. To wait for the entire output, use |channel-buffered| mode.
+ - 2. To read line-by-line, use the following code: >vim
let s:lines = ['']
func! s:on_event(job_id, data, event) dict
let eof = (a:data == [''])
@@ -106,7 +108,7 @@ callbacks.
Data can be sent to the channel using the |chansend()| function. Here is a
simple example, echoing some data through a cat-process:
->
+>vim
function! s:OnEvent(id, data, event) dict
let str = join(a:data, "\n")
echomsg str
@@ -117,7 +119,7 @@ simple example, echoing some data through a cat-process:
Here is a example of setting a buffer to the result of grep, but only after
all data has been processed:
->
+>vim
function! s:OnEvent(id, data, event) dict
call nvim_buf_set_lines(2, 0, -1, v:true, a:data)
endfunction
@@ -140,7 +142,7 @@ However, change of PTY size can be signaled to the slave using |jobresize()|.
See also |terminal-emulator|.
Terminal characteristics (termios) for |:terminal| and PTY channels are copied
-from the host TTY, or if Nvim is |--headless| it uses default values: >
+from the host TTY, or if Nvim is |--headless| it uses default values: >vim
:echo system('nvim --headless +"te stty -a" +"sleep 1" +"1,/^$/print" +q')
==============================================================================
@@ -161,7 +163,7 @@ used as a channel. See also |--embed|.
Call |stdioopen()| during |startup| to open the stdio channel as |channel-id| 1.
Nvim's stderr is always available as |v:stderr|, a write-only bytes channel.
-Example: >
+Example: >vim
func! OnEvent(id, data, event)
if a:data == [""]
quit
@@ -170,7 +172,7 @@ Example: >
endfunc
call stdioopen({'on_stdin': 'OnEvent'})
<
-Put this in `uppercase.vim` and run: >
+Put this in `uppercase.vim` and run: >bash
nvim --headless --cmd "source uppercase.vim"
==============================================================================
@@ -221,7 +223,7 @@ start of the line.
Here is an example for Unix. It starts a shell in the background and prompts
for the next shell command. Output from the shell is displayed above the
-prompt. >
+prompt. >vim
" Function handling a line of text that has been typed.
func TextEntered(text)
diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt
index 29eff75bfa..b4923b0d70 100644
--- a/runtime/doc/cmdline.txt
+++ b/runtime/doc/cmdline.txt
@@ -524,7 +524,6 @@ that see the '"' as part of their argument:
:cexpr (and the like)
:cdo (and the like)
:command
- :cscope (and the like)
:debug
:display
:echo (and the like)
@@ -566,7 +565,6 @@ followed by another Vim command:
:cdo
:cfdo
:command
- :cscope
:debug
:eval
:folddoopen
@@ -575,7 +573,6 @@ followed by another Vim command:
:global
:help
:helpgrep
- :lcscope
:ldo
:lfdo
:lhelpgrep
@@ -586,7 +583,6 @@ followed by another Vim command:
:python
:registers
:read !
- :scscope
:sign
:terminal
:vglobal
@@ -694,7 +690,9 @@ Line numbers may be specified with: *:range* *{address}*
'T position of mark T (uppercase); when the mark is in
another file it cannot be used in a range
/{pattern}[/] the next line where {pattern} matches *:/*
+ also see |:range-pattern| below
?{pattern}[?] the previous line where {pattern} matches *:?*
+ also see |:range-pattern| below
\/ the next line where the previously used search
pattern matches
\? the previous line where the previously used search
@@ -702,11 +700,49 @@ Line numbers may be specified with: *:range* *{address}*
\& the next line where the previously used substitute
pattern matches
+ *:range-offset*
Each may be followed (several times) by '+' or '-' and an optional number.
This number is added or subtracted from the preceding line number. If the
number is omitted, 1 is used. If there is nothing before the '+' or '-' then
the current line is used.
-
+ *:range-closed-fold*
+When a line number after the comma is in a closed fold it is adjusted to the
+last line of the fold, thus the whole fold is included.
+
+When a number is added this is done after the adjustment to the last line of
+the fold. This means these lines are additionally included in the range. For
+example: >
+ :3,4+2print
+On this text:
+ 1 one ~
+ 2 two ~
+ 3 three ~
+ 4 four FOLDED ~
+ 5 five FOLDED ~
+ 6 six ~
+ 7 seven ~
+ 8 eight ~
+Where lines four and five are a closed fold, ends up printing lines 3 to 7.
+The 7 comes from the "4" in the range, which is adjusted to the end of the
+closed fold, which is 5, and then the offset 2 is added.
+
+An example for subtracting (which isn't very useful): >
+ :2,4-1print
+On this text:
+ 1 one ~
+ 2 two ~
+ 3 three FOLDED~
+ 4 four FOLDED ~
+ 5 five FOLDED ~
+ 6 six FOLDED ~
+ 7 seven ~
+ 8 eight ~
+Where lines three to six are a closed fold, ends up printing lines 2 to 6.
+The 6 comes from the "4" in the range, which is adjusted to the end of the
+closed fold, which is 6, and then 1 is subtracted, then this is still in the
+closed fold and the last line of that fold is used, which is 6.
+
+ *:range-pattern*
The "/" and "?" after {pattern} are required to separate the pattern from
anything that follows.
@@ -762,9 +798,9 @@ always be swapped then.
Count and Range *N:*
-When giving a count before entering ":", this is translated into:
+When giving a count before entering ":", this is translated into: >
:.,.+(count - 1)
-In words: The 'count' lines at and after the cursor. Example: To delete
+In words: The "count" lines at and after the cursor. Example: To delete
three lines: >
3:d<CR> is translated into: .,.+2d<CR>
<
@@ -881,7 +917,7 @@ Note: these are typed literally, they are not special keys!
file name of the sourced file. *E498*
When executing a function, is replaced with the call stack,
as with <stack> (this is for backwards compatibility, using
- <stack> is preferred).
+ <stack> or <script> is preferred).
Note that filename-modifiers are useless when <sfile> is
not used inside a script.
*:<stack>* *<stack>*
@@ -891,6 +927,12 @@ Note: these are typed literally, they are not special keys!
".." in between items. E.g.:
"function {function-name1}[{lnum}]..{function-name2}[{lnum}]"
If there is no call stack you get error *E489* .
+ *:<script>* *<script>*
+ <script> When executing a `:source` command, is replaced with the file
+ name of the sourced file. When executing a function, is
+ replaced with the file name of the script where it is
+ defined.
+ If the file name cannot be determined you get error *E1274* .
*:<slnum>* *<slnum>*
<slnum> When executing a `:source` command, is replaced with the
line number. *E842*
diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt
index eb6d9b6dc9..1bdd13ac0c 100644
--- a/runtime/doc/deprecated.txt
+++ b/runtime/doc/deprecated.txt
@@ -10,150 +10,144 @@ 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.
==============================================================================
-
-API ~
-*nvim_buf_clear_highlight()* Use |nvim_buf_clear_namespace()| instead.
-*nvim_buf_set_virtual_text()* Use |nvim_buf_set_extmark()| instead.
-*nvim_command_output()* Use |nvim_exec()| instead.
-*nvim_execute_lua()* Use |nvim_exec_lua()| instead.
-
-Commands ~
-*:rv*
-*:rviminfo* Deprecated alias to |:rshada| command.
-*:wv*
-*:wviminfo* Deprecated alias to |:wshada| command.
-
-Environment Variables ~
-*$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.
-*EncodingChanged* Never fired; 'encoding' is always "utf-8".
-*FileEncoding* Never fired; equivalent to |EncodingChanged|.
-*GUIEnter* Never fired; use |UIEnter| instead.
-*GUIFailed* Never fired.
-
-Keycodes ~
-*<MouseDown>* Use <ScrollWheelUp> instead.
-*<MouseUp>* Use <ScrollWheelDown> instead.
-
-Functions ~
-*buffer_exists()* Obsolete name for |bufexists()|.
-*buffer_name()* Obsolete name for |bufname()|.
-*buffer_number()* Obsolete name for |bufnr()|.
-*file_readable()* Obsolete name for |filereadable()|.
-*highlight_exists()* Obsolete name for |hlexists()|.
-*highlightID()* Obsolete name for |hlID()|.
-*inputdialog()* Use |input()| instead.
-*jobclose()* Obsolete name for |chanclose()|
-*jobsend()* Obsolete name for |chansend()|
-*last_buffer_nr()* Obsolete name for bufnr("$").
-*rpcstop()* Deprecated. Instead use |jobstop()| to stop any job,
- or chanclose(id, "rpc") to close RPC communication
+Deprecated features
+
+API
+- *nvim_buf_clear_highlight()* Use |nvim_buf_clear_namespace()| instead.
+- *nvim_buf_set_virtual_text()* Use |nvim_buf_set_extmark()| instead.
+- *nvim_command_output()* Use |nvim_exec()| instead.
+- *nvim_execute_lua()* Use |nvim_exec_lua()| instead.
+
+COMMANDS
+- *:rv* *:rviminfo* Deprecated alias to |:rshada| command.
+- *:wv* *:wviminfo* Deprecated alias to |:wshada| command.
+
+ENVIRONMENT VARIABLES
+- *$NVIM_LISTEN_ADDRESS*
+ - Deprecated way to:
+ - set the server name (use |--listen| or |serverstart()| instead)
+ - get the server name (use |v:servername| instead)
+ - detect a parent Nvim (use |$NVIM| instead)
+ - Ignored if --listen is given.
+ - Unset by |terminal| and |jobstart()| unless explicitly given by the "env"
+ option. Example: >vim
+ call jobstart(['foo'], { 'env': { 'NVIM_LISTEN_ADDRESS': v:servername } })
+<
+
+EVENTS
+- *BufCreate* Use |BufAdd| instead.
+- *EncodingChanged* Never fired; 'encoding' is always "utf-8".
+- *FileEncoding* Never fired; equivalent to |EncodingChanged|.
+- *GUIEnter* Never fired; use |UIEnter| instead.
+- *GUIFailed* Never fired.
+
+KEYCODES
+- *<MouseDown>* Use <ScrollWheelUp> instead.
+- *<MouseUp>* Use <ScrollWheelDown> instead.
+
+FUNCTIONS
+- *buffer_exists()* Obsolete name for |bufexists()|.
+- *buffer_name()* Obsolete name for |bufname()|.
+- *buffer_number()* Obsolete name for |bufnr()|.
+- *file_readable()* Obsolete name for |filereadable()|.
+- *health#report_error* Use Lua |vim.health.report_error()| instead.
+- *health#report_info* Use Lua |vim.health.report_info()| instead.
+- *health#report_ok* Use Lua |vim.health.report_ok()| instead.
+- *health#report_start* Use Lua |vim.health.report_start()| instead.
+- *health#report_warn* Use Lua |vim.health.report_warn()| instead.
+- *highlight_exists()* Obsolete name for |hlexists()|.
+- *highlightID()* Obsolete name for |hlID()|.
+- *inputdialog()* Use |input()| instead.
+- *jobclose()* Obsolete name for |chanclose()|
+- *jobsend()* Obsolete name for |chansend()|
+- *last_buffer_nr()* Obsolete name for bufnr("$").
+- *rpcstop()* Use |jobstop()| instead to stop any job, or
+ `chanclose(id, "rpc")` to close RPC communication
without stopping the job. Use chanclose(id) to close
any socket.
-Highlights ~
-
-*hl-VertSplit* Use |hl-WinSeparator| instead.
-
-LSP Diagnostics ~
+HIGHLIGHTS
+- *hl-VertSplit* Use |hl-WinSeparator| instead.
+LSP DIAGNOSTICS
For each of the functions below, use the corresponding function in
|vim.diagnostic| instead (unless otherwise noted). For example, use
|vim.diagnostic.get()| instead of |vim.lsp.diagnostic.get()|.
-*vim.lsp.diagnostic.clear()* Use |vim.diagnostic.hide()| instead.
-*vim.lsp.diagnostic.disable()*
-*vim.lsp.diagnostic.display()* Use |vim.diagnostic.show()| instead.
-*vim.lsp.diagnostic.enable()*
-*vim.lsp.diagnostic.get()*
-*vim.lsp.diagnostic.get_all()* Use |vim.diagnostic.get()| instead.
-*vim.lsp.diagnostic.get_count()* Use |vim.diagnostic.get()| instead.
-*vim.lsp.diagnostic.get_line_diagnostics()*
- Use |vim.diagnostic.get()| instead.
-*vim.lsp.diagnostic.get_next()*
-*vim.lsp.diagnostic.get_next_pos()*
-*vim.lsp.diagnostic.get_prev()*
-*vim.lsp.diagnostic.get_prev_pos()*
-*vim.lsp.diagnostic.get_virtual_text_chunks_for_line()*
- No replacement. Use options provided by
- |vim.diagnostic.config()| to customize
- virtual text.
-*vim.lsp.diagnostic.goto_next()*
-*vim.lsp.diagnostic.goto_prev()*
-*vim.lsp.diagnostic.redraw()* Use |vim.diagnostic.show()| instead.
-*vim.lsp.diagnostic.reset()*
-*vim.lsp.diagnostic.save()* Use |vim.diagnostic.set()| instead.
-*vim.lsp.diagnostic.set_loclist()* Use |vim.diagnostic.setloclist()| instead.
-*vim.lsp.diagnostic.set_qflist()* Use |vim.diagnostic.setqflist()| instead.
-
-The following have been replaced by |vim.diagnostic.open_float()|.
-
-*vim.lsp.diagnostic.show_line_diagnostics()*
-*vim.lsp.diagnostic.show_position_diagnostics()*
+- *vim.lsp.diagnostic.clear()* Use |vim.diagnostic.hide()| instead.
+- *vim.lsp.diagnostic.disable()*
+- *vim.lsp.diagnostic.display()* Use |vim.diagnostic.show()| instead.
+- *vim.lsp.diagnostic.enable()*
+- *vim.lsp.diagnostic.get()*
+- *vim.lsp.diagnostic.get_all()* Use |vim.diagnostic.get()| instead.
+- *vim.lsp.diagnostic.get_count()* Use |vim.diagnostic.get()| instead.
+- *vim.lsp.diagnostic.get_line_diagnostics()* Use |vim.diagnostic.get()| instead.
+- *vim.lsp.diagnostic.get_next()*
+- *vim.lsp.diagnostic.get_next_pos()*
+- *vim.lsp.diagnostic.get_prev()*
+- *vim.lsp.diagnostic.get_prev_pos()*
+- *vim.lsp.diagnostic.get_virtual_text_chunks_for_line()* No replacement. Use
+ options provided by |vim.diagnostic.config()| to customize virtual text.
+- *vim.lsp.diagnostic.goto_next()*
+- *vim.lsp.diagnostic.goto_prev()*
+- *vim.lsp.diagnostic.redraw()* Use |vim.diagnostic.show()| instead.
+- *vim.lsp.diagnostic.reset()*
+- *vim.lsp.diagnostic.save()* Use |vim.diagnostic.set()| instead.
+- *vim.lsp.diagnostic.set_loclist()* Use |vim.diagnostic.setloclist()| instead.
+- *vim.lsp.diagnostic.set_qflist()* Use |vim.diagnostic.setqflist()| instead.
+- *vim.lsp.diagnostic.show_line_diagnostics()* Use |vim.diagnostic.open_float()| instead.
+- *vim.lsp.diagnostic.show_position_diagnostics()* Use |vim.diagnostic.open_float()| instead.
The following are deprecated without replacement. These functions are moved
internally and are no longer exposed as part of the API. Instead, use
|vim.diagnostic.config()| and |vim.diagnostic.show()|.
-*vim.lsp.diagnostic.set_signs()*
-*vim.lsp.diagnostic.set_underline()*
-*vim.lsp.diagnostic.set_virtual_text()*
-
-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
+- *vim.lsp.diagnostic.set_signs()*
+- *vim.lsp.diagnostic.set_underline()*
+- *vim.lsp.diagnostic.set_virtual_text()*
+
+LSP FUNCTIONS
+- *vim.lsp.buf.range_code_action()* Use |vim.lsp.buf.code_action()| with
+ the `range` parameter.
+- *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.
-
-Modifiers ~
-*cpo-<*
-*:menu-<special>*
-*:menu-special* <> notation is always enabled.
-*:map-<special>*
-*:map-special* <> notation is always enabled.
-
-Normal commands ~
-*]f*
-*[f* Same as "gf".
-
-Options ~
-*'cscopeverbose'* Enabled by default. Use |:silent| instead.
-*'exrc'* *'ex'* Security risk: downloaded files could include
- a malicious .nvimrc or .exrc file. See 'secure'.
- Recommended alternative: define an autocommand in your
- |vimrc| to set options for a matching directory.
-'gd'
-'gdefault' Enables the |:substitute| flag 'g' by default.
-*'fe'* 'fenc'+'enc' before Vim 6.0; no longer used.
-*'highlight'* *'hl'* Names of builtin |highlight-groups| cannot be changed.
-*'langnoremap'* Deprecated alias to 'nolangremap'.
-'sessionoptions' Flags "unix", "slash" are ignored and always enabled.
-*'vi'*
-'viewoptions' Flags "unix", "slash" are ignored and always enabled.
-*'viminfo'* Deprecated alias to 'shada' option.
-*'viminfofile'* Deprecated alias to 'shadafile' option.
-
-UI extensions~
-*ui-wildmenu* Use |ui-cmdline| with |ui-popupmenu| instead. Enabled
+- *vim.lsp.buf.formatting()* Use |vim.lsp.buf.format()| with
+ {async = true} instead.
+- *vim.lsp.buf.range_formatting()* Use |vim.lsp.formatexpr()|
+ or |vim.lsp.buf.format()| instead.
+
+LUA
+- *vim.register_keystroke_callback()* Use |vim.on_key()| instead.
+
+NORMAL COMMANDS
+- *]f* *[f* Same as "gf".
+
+OPTIONS
+- *cpo-<* *:menu-<special>* *:menu-special* *:map-<special>* *:map-special*
+ `<>` notation is always enabled.
+- 'gdefault' Enables the |:substitute| flag 'g' by default.
+- *'fe'* 'fenc'+'enc' before Vim 6.0; no longer used.
+- *'highlight'* *'hl'* Names of builtin |highlight-groups| cannot be changed.
+- *'langnoremap'* Deprecated alias to 'nolangremap'.
+- 'sessionoptions' Flags "unix", "slash" are ignored and always enabled.
+- *'vi'*
+- 'viewoptions' Flags "unix", "slash" are ignored and always enabled.
+- *'viminfo'* Deprecated alias to 'shada' option.
+- *'viminfofile'* Deprecated alias to 'shadafile' option.
+
+UI EXTENSIONS
+- *ui-wildmenu* Use |ui-cmdline| with |ui-popupmenu| instead. Enabled
by the `ext_wildmenu` |ui-option|. Emits these events:
- ["wildmenu_show", items]
- ["wildmenu_select", selected]
- ["wildmenu_hide"]
+ - `["wildmenu_show", items]`
+ - `["wildmenu_select", selected]`
+ - `["wildmenu_hide"]`
-Variables~
-*b:terminal_job_pid* PID of the top-level process in a |:terminal|.
+VARIABLES
+- *b:terminal_job_pid* PID of the top-level process in a |:terminal|.
Use `jobpid(&channel)` instead.
+
vim:noet:tw=78:ts=8:ft=help:norl:
diff --git a/runtime/doc/dev_style.txt b/runtime/doc/dev_style.txt
index 77253e7831..b96b01dbff 100644
--- a/runtime/doc/dev_style.txt
+++ b/runtime/doc/dev_style.txt
@@ -8,7 +8,7 @@ Nvim style guide *dev-style*
This is style guide for developers working on Nvim's source code.
-License: CC-By 3.0 http://creativecommons.org/licenses/by/3.0/
+License: CC-By 3.0 https://creativecommons.org/licenses/by/3.0/
Type |gO| to see the table of contents.
@@ -38,7 +38,7 @@ All header files should have `#define` guards to prevent multiple inclusion.
The format of the symbol name should be `NVIM_<DIRECTORY>_<FILE>_H`.
In foo/bar.h:
->
+>c
#ifndef NVIM_FOO_BAR_H
#define NVIM_FOO_BAR_H
@@ -71,7 +71,7 @@ C99 allows you to declare variables anywhere in a function. Declare them in as
local a scope as possible, and as close to the first use as possible. This
makes it easier for the reader to find the declaration and see what type the
variable is and what it was initialized to. In particular, initialization
-should be used instead of declaration and assignment, e.g. >
+should be used instead of declaration and assignment, e.g. >c
int i;
i = f(); // BAD: initialization separate from declaration.
@@ -110,7 +110,7 @@ Variable-length arrays can cause hard to detect stack overflows.
Postincrement and Postdecrement ~
-Use postfix form (`i++`) in statements. >
+Use postfix form (`i++`) in statements. >c
for (int i = 0; i < 3; i++) { }
int j = ++i; // OK: ++i is used as an expression.
@@ -136,7 +136,7 @@ Use `const` pointers whenever possible. Avoid `const` on non-pointer parameter d
before the "noun" (`int`).
That said, while we encourage putting `const` first, we do not require it.
- But be consistent with the code around you! >
+ But be consistent with the code around you! >c
void foo(const char *p, int i);
}
@@ -176,21 +176,14 @@ Type unsigned signed
Booleans ~
-Use `bool` to represent boolean values. >
+Use `bool` to represent boolean values. >c
int loaded = 1; // BAD: loaded should have type bool.
-Variable declarations ~
-
-Declare only one variable per line. >
-
- int i, j = 1
-
-
Conditions ~
-Don't use "yoda-conditions". Use at most one assignment per condition. >
+Don't use "yoda-conditions". Use at most one assignment per condition. >c
if (1 == x) {
@@ -203,7 +196,7 @@ Function declarations ~
Every function must not have a separate declaration.
-Function declarations are created by the gendeclarations.lua script. >
+Function declarations are created by the gendeclarations.lua script. >c
static void f(void);
@@ -216,7 +209,7 @@ Function declarations are created by the gendeclarations.lua script. >
General translation unit layout ~
The definitions of public functions precede the definitions of static
-functions. >
+functions. >c
<HEADER>
@@ -237,7 +230,7 @@ if .c file does not contain any static functions.
Included file name consists of the .c file name without extension, preceded by
the directory name relative to src/nvim. Name of the file containing static
functions declarations ends with `.c.generated.h`, `*.h.generated.h` files
-contain only non-static function declarations. >
+contain only non-static function declarations. >c
// src/nvim/foo.c file
#include <stddef.h>
@@ -281,7 +274,7 @@ comparisons, and structure alignment.
`#pragma pack()` and `__declspec(align())`.
- Use the `LL` or `ULL` suffixes as needed to create 64-bit constants. For
- example: >
+ example: >c
int64_t my_value = 0x123456789LL;
uint64_t my_mask = 3ULL << 48;
@@ -295,7 +288,7 @@ Use `sizeof(varname)` when you take the size of a particular variable.
`sizeof(varname)` will update appropriately if someone changes the variable
type either now or later. You may use `sizeof(type)` for code unrelated to any
particular variable, such as code that manages an external or internal data
-format where a variable of an appropriate C type is not convenient. >
+format where a variable of an appropriate C type is not convenient. >c
Struct data;
memset(&data, 0, sizeof(data));
@@ -331,7 +324,7 @@ Give as descriptive a name as possible, within reason. Do not worry about
saving horizontal space as it is far more important to make your code
immediately understandable by a new reader. Do not use abbreviations that are
ambiguous or unfamiliar to readers outside your project, and do not abbreviate
-by deleting letters within a word. >
+by deleting letters within a word. >c
int price_count_reader; // No abbreviation.
int num_errors; // "num" is a widespread convention.
@@ -368,7 +361,7 @@ Typedef-ed structs and enums start with a capital letter and have a capital
letter for each new word, with no underscores: `MyExcitingStruct`.
Non-Typedef-ed structs and enums are all lowercase with underscores between
-words: `struct my_exciting_struct` . >
+words: `struct my_exciting_struct` . >c
struct my_struct {
...
@@ -383,7 +376,7 @@ instance: `my_exciting_local_variable`.
Common Variable names ~
- For example: >
+ For example: >c
string table_name; // OK: uses underscore.
string tablename; // OK: all lowercase.
@@ -393,7 +386,7 @@ instance: `my_exciting_local_variable`.
Struct Variables ~
- Data members in structs should be named like regular variables. >
+ Data members in structs should be named like regular variables. >c
struct url_table_properties {
string name;
@@ -413,7 +406,7 @@ Use a `k` followed by mixed case: `kDaysInAWeek`.
All compile-time constants, whether they are declared locally or globally,
follow a slightly different naming convention from other variables. Use a `k`
-followed by words with uppercase first letters: >
+followed by words with uppercase first letters: >c
const int kDaysInAWeek = 7;
@@ -423,7 +416,7 @@ Function names are all lowercase, with underscores between words. For
instance: `my_exceptional_function()`. All functions in the same header file
should have a common prefix.
-In `os_unix.h`: >
+In `os_unix.h`: >c
void unix_open(const char *path);
void unix_user_id(void);
@@ -436,7 +429,7 @@ normal operation.
Enumerator Names ~
-Enumerators should be named like constants: `kEnumName`. >
+Enumerators should be named like constants: `kEnumName`. >c
enum url_table_errors {
kOK = 0,
@@ -447,7 +440,7 @@ Enumerators should be named like constants: `kEnumName`. >
Macro Names ~
-They're like this: `MY_MACRO_THAT_SCARES_CPP_DEVELOPERS`. >
+They're like this: `MY_MACRO_THAT_SCARES_CPP_DEVELOPERS`. >c
#define ROUND(x) ...
#define PI_ROUNDED 5.0
@@ -468,7 +461,7 @@ Nvim uses Doxygen comments.
Comment Style ~
-Use the `//`-style syntax only. >
+Use the `//`-style syntax only. >c
// This is a comment spanning
// multiple lines
@@ -496,7 +489,7 @@ Start each file with a description of its contents.
mention in the `.c` that the documentation is in the `.h` file.
Do not duplicate comments in both the `.h` and the `.c`. Duplicated
- comments diverge. >
+ comments diverge. >c
/// A brief description of this file.
///
@@ -507,7 +500,7 @@ Start each file with a description of its contents.
Struct Comments ~
Every struct definition should have accompanying comments that describes what
-it is for and how it should be used. >
+it is for and how it should be used. >c
/// Window info stored with a buffer.
///
@@ -529,7 +522,7 @@ 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, and follow the necessary doxygen style. >
+be consistent within one struct, and follow the necessary doxygen style. >c
struct wininfo_S {
WinInfo *wi_next; ///< Next entry or NULL for last entry.
@@ -567,8 +560,7 @@ of a function describe operation.
- If the function allocates memory that the caller must free.
- Whether any of the arguments can be a null pointer.
- If there are any performance implications of how a function is used.
- - If the function is re-entrant. What are its synchronization assumptions?
- >
+ - If the function is re-entrant. What are its synchronization assumptions? >c
/// Brief description of the function.
///
/// Detailed description.
@@ -596,7 +588,7 @@ of a function describe operation.
Note you should not just repeat the comments given with the function
declaration, in the `.h` file or wherever. It's okay to recapitulate
briefly what the function does, but the focus of the comments should be on
- how it does it. >
+ how it does it. >c
// Note that we don't use Doxygen comments here.
Iterator *get_iterator(void *arg1, void *arg2)
@@ -614,7 +606,7 @@ comments are required.
Global Variables ~
All global variables should have a comment describing what they are and
- what they are used for. For example: >
+ what they are used for. For example: >c
/// The total number of tests cases that we run
/// through in this regression test.
@@ -630,7 +622,7 @@ interesting, or important parts of your code.
Also, lines that are non-obvious should get a comment at the end of the
line. These end-of-line comments should be separated from the code by 2
- spaces. Example: >
+ spaces. Example: >c
// If we have enough memory, mmap the data portion too.
mmap_budget = max<int64>(0, mmap_budget - index_->length());
@@ -643,7 +635,7 @@ interesting, or important parts of your code.
function returns.
If you have several comments on subsequent lines, it can often be more
- readable to line them up: >
+ readable to line them up: >c
do_something(); // Comment here so the comments line up.
do_something_else_that_is_longer(); // Comment here so there are two spaces between
@@ -659,7 +651,7 @@ interesting, or important parts of your code.
When you pass in a null pointer, boolean, or literal integer values to
functions, you should consider adding a comment about what they are, or
make your code self-documenting by using constants. For example, compare:
- >
+ >c
bool success = calculate_something(interesting_value,
10,
@@ -667,7 +659,7 @@ interesting, or important parts of your code.
NULL); // What are these arguments??
<
- versus: >
+ versus: >c
bool success = calculate_something(interesting_value,
10, // Default base value.
@@ -675,7 +667,7 @@ interesting, or important parts of your code.
NULL); // No callback.
<
- Or alternatively, constants or self-describing variables: >
+ Or alternatively, constants or self-describing variables: >c
const int kDefaultBaseValue = 10;
const bool kFirstTimeCalling = false;
@@ -690,7 +682,7 @@ interesting, or important parts of your code.
Note that you should never describe the code itself. Assume that the
person reading the code knows C better than you do, even though he or she
- does not know what you are trying to do: >
+ does not know what you are trying to do: >c
// Now go through the b array and make sure that if i occurs,
// the next element is i+1.
@@ -725,7 +717,7 @@ about the problem referenced by the `TODO`. The main purpose is to have a
consistent `TODO` format that can be searched to find the person who can
provide more details upon request. A `TODO` is not a commitment that the
person referenced will fix the problem. Thus when you create a `TODO`, it is
-almost always your name that is given. >
+almost always your name that is given. >c
// TODO(kl@gmail.com): Use a "*" here for concatenation operator.
// TODO(Zeke): change this to use relations.
@@ -789,72 +781,23 @@ example, `"\uFEFF"`, is the Unicode zero-width no-break space character, which
would be invisible if included in the source as straight UTF-8.
-Function Declarations and Definitions ~
-
-Return type on the same line as function name, parameters on the same line if
-they fit.
-
-Functions look like this: >
-
- ReturnType function_name(Type par_name1, Type par_name2)
- {
- do_something();
- ...
- }
-
-If you have too much text to fit on one line: >
-
- ReturnType really_long_function_name(Type par_name1, Type par_name2,
- Type par_name3)
- {
- do_something();
- ...
- }
-
-or if you cannot fit even the first parameter (but only then): >
-
- ReturnType really_really_really_long_function_name(
- Type par_name1, // 4 space indent
- Type par_name2,
- Type par_name3)
- {
- do_something(); // 2 space indent
- ...
- }
-
-Some points to note:
-
-- The open parenthesis is always on the same line as the function name.
-- There is never a space between the function name and the open parenthesis.
-- There is never a space between the parentheses and the parameters.
-- The open curly brace is always on the next line.
-- The close curly brace is always on the last line by itself.
-- There should be a space between the close parenthesis and the open curly
- brace.
-- All parameters should be named, with identical names in the declaration and
- implementation.
-- All parameters should be aligned if possible.
-- Default indentation is 2 spaces.
-- Wrapped parameters have a 4 space indent.
-
-
Function Calls ~
On one line if it fits; otherwise, wrap arguments at the parenthesis.
-Function calls have the following format: >
+Function calls have the following format: >c
bool retval = do_something(argument1, argument2, argument3);
If the arguments do not all fit on one line, they should be broken up onto
multiple lines, with each subsequent line aligned with the first argument. Do
-not add spaces after the open paren or before the close paren: >
+not add spaces after the open paren or before the close paren: >c
bool retval = do_something(averyveryveryverylongargument1,
argument2, argument3);
If the function has many arguments, consider having one per line if this makes
-the code more readable: >
+the code more readable: >c
bool retval = do_something(argument1,
argument2,
@@ -862,7 +805,7 @@ the code more readable: >
argument4);
Arguments may optionally all be placed on subsequent lines, with one line per
-argument: >
+argument: >c
if (...) {
...
@@ -886,7 +829,7 @@ place but with one space after the `{` and one space before the `}`
If the braced list follows a name (e.g. a type or variable name), format as if
the `{}` were the parentheses of a function call with that name. If there is
-no name, assume a zero-length name. >
+no name, assume a zero-length name. >c
struct my_struct m = { // Here, you could also break before {.
superlongvariablename1,
@@ -896,18 +839,6 @@ no name, assume a zero-length name. >
interiorwrappinglist2 } };
-Conditionals ~
-
-Don't use spaces inside parentheses. >
-
- if (condition) { // no spaces inside parentheses
- ... // 2 space indent.
- } else if (...) { // The else goes on the same line as the closing brace.
- ...
- } else {
- ...
- }
-
Loops and Switch Statements ~
Annotate non-trivial fall-through between cases.
@@ -915,7 +846,7 @@ 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
warn you if any values are not handled). If the default case should never
-execute, simply `assert`: >
+execute, simply `assert`: >c
switch (var) {
case 0:
@@ -928,45 +859,12 @@ execute, simply `assert`: >
assert(false);
}
-Pointer Expressions ~
-
-No spaces around period or arrow. Pointer operators do not have trailing
-spaces.
-
-The following are examples of correctly-formatted pointer and reference
-expressions: >
-
- x = *p;
- p = &x;
- x = r.y;
- x = r->y;
-
-Note that:
-
- - There are no spaces around the period or arrow when accessing a member.
- - Pointer operators have no space after the * or &.
-
-Boolean Expressions ~
-
-When you have a boolean expression that is longer than the standard line
-length, keep operators at the start of the line. >
-
- if (this_one_thing > this_other_thing
- && a_third_thing == a_fourth_thing
- && yet_another && last_one) {
- ...
- }
-
-Also note that you should always use the punctuation operators, such as `&&`
-and `~`, rather than the word operators, such as `and` and `compl`.
-
-
Return Values ~
Do not needlessly surround the `return` expression with parentheses.
Use parentheses in `return expr`; only where you would use them in `x =
-expr;`. >
+expr;`. >c
return result;
return (some_long_condition && another_condition);
@@ -980,12 +878,12 @@ Horizontal Whitespace ~
Use of horizontal whitespace depends on location.
General ~
->
+>c
int x[] = { 0 }; // Spaces inside braces for braced-init-list.
<
Variables ~
->
+>c
int long_variable = 0; // Don't align assignments.
int i = 1;
@@ -1002,14 +900,12 @@ Use of horizontal whitespace depends on location.
Operators ~
->
+>c
x = 0; // Assignment operators always have spaces around
// them.
x = -5; // No spaces separating unary operators and their
x++; // arguments.
if (x && !y)
- ...
- i = (int)d; // No spaces after a cast operator.
<
Vertical Whitespace ~
diff --git a/runtime/doc/develop.txt b/runtime/doc/develop.txt
index b31ac47bda..ff48ae3e26 100644
--- a/runtime/doc/develop.txt
+++ b/runtime/doc/develop.txt
@@ -28,11 +28,9 @@ The Neo bits of Nvim should make it a better Vim, without becoming a
completely different editor.
- In matters of taste, prefer Vim/Unix tradition. If there is no relevant
Vim/Unix tradition, consider the "common case".
-- A feature that people do not know about is a useless feature. Don't add
- obscure features, or at least add hints in documentation that they exist.
-- There is no limit to the features that can be added. Selecting new features
- is based on (1) what users ask for, (2) how much effort it takes to
- implement and (3) someone actually implementing it.
+- There is no limit to the features that can be added. Select new features
+ based on (1) what users ask for, (2) how much effort it takes to implement
+ and (3) someone actually implementing it.
- Backwards compatibility is a feature. The RPC API in particular should
never break.
@@ -48,7 +46,7 @@ NVIM IS... WELL DOCUMENTED *design-documented*
NVIM IS... FAST AND SMALL *design-speed-size*
-Keep Nvim small and fast.
+Keep Nvim small and fast. This directly affects versatility and usability.
- Computers are becoming faster and bigger each year. Vim can grow too, but
no faster than computers are growing. Keep Vim usable on older systems.
- Many users start Vim from a shell very often. Startup time must be short.
@@ -57,7 +55,8 @@ Keep Nvim small and fast.
- Don't forget that some people use Vim over a slow connection. Minimize the
communication overhead.
- Vim is a component among other components. Don't turn it into a massive
- application, but have it work well together with other programs.
+ application, but have it work well together with other programs
+ ("composability").
NVIM IS... MAINTAINABLE *design-maintain*
@@ -119,7 +118,7 @@ reflects whether Python support is working.
*provider-reload*
Sometimes a GUI or other application may want to force a provider to
"reload". To reload a provider, undefine its "loaded" flag, then use
-|:runtime| to reload it: >
+|:runtime| to reload it: >vim
:unlet g:loaded_clipboard_provider
:runtime autoload/provider/clipboard.vim
@@ -131,6 +130,7 @@ DOCUMENTATION *dev-doc*
(docstrings, user manual, website materials, newsletters, …). Don't mince
words. Personality and flavor, used sparingly, are welcome--but in general,
optimize for the reader's time and energy: be "precise yet concise".
+ - See https://developers.google.com/style/tone
- Prefer the active voice: "Foo does X", not "X is done by Foo".
- Vim differences:
- Do not prefix help tags with "nvim-". Use |vim_diff.txt| to catalog
@@ -144,13 +144,33 @@ DOCUMENTATION *dev-doc*
- Use "tui-" to prefix help tags related to the host terminal, and "TUI"
in prose if possible.
- Docstrings: do not start parameter descriptions with "The" or "A" unless it
- is critical to avoid ambiguity.
- GOOD: >
- /// @param dirname Path fragment before `pend`
-< BAD: >
- /// @param dirname The path fragment before `pend`
+ is critical to avoid ambiguity. >
+ GOOD:
+ /// @param dirname Path fragment before `pend`
+ BAD:
+ /// @param dirname The path fragment before `pend`
<
+Documentation format ~
+
+For Nvim-owned docs, use the following strict subset of "vimdoc" to ensure
+the help doc renders nicely in other formats (such as HTML:
+https://neovim.io/doc/user ).
+
+Strict "vimdoc" subset:
+
+- Use lists (like this!) prefixed with "-", "*", or "•", for adjacent lines
+ that you don't want auto-wrapped. Lists are always rendered with "flow"
+ (soft-wrapped) layout instead of preformatted (hard-wrapped) layout common
+ in legacy :help docs.
+ - Limitation: currently the parser https://github.com/neovim/tree-sitter-vimdoc
+ does not understand numbered listitems, so use a bullet symbol (- or •)
+ before numbered items, e.g. "- 1." instead of "1.".
+- Separate blocks (paragraphs) of content by a blank line(s).
+- Do not use indentation in random places—that prevents the page from using
+ "flow" layout. If you need a preformatted section, put it in
+ a |help-codeblock| starting with ">".
+
C docstrings ~
Nvim API documentation lives in the source code, as docstrings (Doxygen
@@ -163,7 +183,8 @@ Docstring format:
`@note`, `@param`, `@returns`
- Limited markdown is supported.
- List-items start with `-` (useful to nest or "indent")
-- Use `<pre>` for code samples.
+- Use `<pre>` for code samples.
+ Code samples can be annotated as `vim` or `lua`
Example: the help for |nvim_open_win()| is generated from a docstring defined
in src/nvim/api/win_config.c like this: >
@@ -172,7 +193,7 @@ in src/nvim/api/win_config.c like this: >
/// ...
///
/// Example (Lua): window-relative float
- /// <pre>
+ /// <pre>lua
/// vim.api.nvim_open_win(0, false,
/// {relative='win', row=3, col=3, width=12, height=3})
/// </pre>
@@ -201,7 +222,8 @@ Docstring format:
`---@see`, `---@param`, `---@returns`
- Limited markdown is supported.
- List-items start with `-` (useful to nest or "indent")
-- Use `<pre>` for code samples.
+- Use `<pre>` for code samples.
+ Code samples can be annotated as `vim` or `lua`
Example: the help for |vim.paste()| is generated from a docstring decorating
vim.paste in runtime/lua/vim/_editor.lua like this: >
@@ -210,7 +232,7 @@ vim.paste in runtime/lua/vim/_editor.lua like this: >
--- (such as the |TUI|) pastes text into the editor.
---
--- Example: To remove ANSI color codes when pasting:
- --- <pre>
+ --- <pre>lua
--- vim.paste = (function()
--- local overridden = vim.paste
--- ...
@@ -227,14 +249,27 @@ vim.paste in runtime/lua/vim/_editor.lua like this: >
LUA *dev-lua*
- Keep the core Lua modules |lua-stdlib| simple. Avoid elaborate OOP or
- pseudo-OOP designs. Plugin authors just want functions to call, they don't
- want to learn a big, fancy inheritance hierarchy. Thus avoid specialized
- objects; tables or values are usually better.
+ pseudo-OOP designs. Plugin authors just want functions to call, not a big,
+ fancy inheritance hierarchy.
+- Avoid requiring or returning special objects in the Nvim stdlib. Plain
+ tables or values are easier to serialize, easier to construct from literals,
+ easier to inspect and print, and inherently compatible with all Lua plugins.
+ (This guideline doesn't apply to opaque, non-data objects like `vim.cmd`.)
API *dev-api*
-Use this template to name new RPC |API| functions:
+- Avoid "mutually exclusive" parameters--via constraints or limitations, if
+ necessary. For example nvim_create_autocmd() has mutually exclusive
+ "callback" and "command" args; but the "command" arg could be eliminated by
+ simply not supporting Vimscript function names, and treating a string
+ "callback" arg as an Ex command (which can call Vimscript functions). The
+ "buffer" arg could also be eliminated by treating a number "pattern" as
+ a buffer number.
+
+ *dev-api-naming*
+Use this format to name new RPC |API| functions:
+
nvim_{thing}_{action}_{arbitrary-qualifiers}
If the function acts on an object then {thing} is the name of that object
@@ -243,31 +278,50 @@ 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
- 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
+ - add Append to, or insert into, a collection
+ - call Call a function
+ - create Create a new (non-trivial) thing
+ - del Delete a thing (or group of things)
+ - eval Evaluate an expression
+ - exec Execute code
+ - fmt Format
+ - get Get things (often by a query)
+ - open Open
+ - parse Parse something into a structured form
+ - set Set a thing (or group of things)
+
+Do NOT use these deprecated verbs:
+ - list Redundant with "get"
+
+Use consistent names for {thing} (nouns) in API functions: buffer is called
"buf" everywhere, not "buffer" in some places and "buf" in others.
+ - buf Buffer
+ - chan |channel|
+ - cmd Command
+ - cmdline Command-line UI or input
+ - fn Function
+ - hl Highlight
+ - pos Position
+ - proc System process
+ - tabpage Tabpage
+ - win Window
+
+Do NOT use these deprecated nouns:
+ - buffer
+ - command
+ - window
Example:
- `nvim_get_current_line` acts on the global editor state; the common
- {action} "get" is used but {thing} is omitted.
-
-Example:
- `nvim_buf_add_highlight` acts on a `Buffer` object (the first parameter)
- and uses the common {action} "add".
+ `nvim_get_keymap('v')` operates in a global context (first parameter is not
+ a Buffer). The "get" {action} indicates that it gets anything matching the
+ given filter parameter. There is no need for a "list" action because
+ `nvim_get_keymap('')` (i.e., empty filter) returns all items.
Example:
- `nvim_list_bufs` operates in a global context (first parameter is not
- a Buffer). The common {action} "list" indicates that it lists all bufs
- (plural) in the global context.
+ `nvim_buf_del_mark` acts on a `Buffer` object (the first parameter)
+ and uses the "del" {action}.
-Use this template to name new API events:
+Use this format to name new API events:
nvim_{thing}_{event}_event
Example:
@@ -309,10 +363,10 @@ a good name: it's idiomatic and unambiguous. If the package is named "neovim",
it confuses users, and complicates documentation and discussions.
Examples of API-client package names:
- GOOD: nvim-racket
- GOOD: pynvim
- BAD: python-client
- BAD: neovim
+- GOOD: nvim-racket
+- GOOD: pynvim
+- BAD: python-client
+- BAD: neovim
API client implementation guidelines ~
@@ -378,4 +432,4 @@ Use the "on_" prefix to name event handlers and also the interface for
a confused collection of naming conventions for these related concepts.
- vim:tw=78:ts=8:noet:ft=help:norl:
+ vim:tw=78:ts=8:sw=4:et:ft=help:norl:
diff --git a/runtime/doc/diagnostic.txt b/runtime/doc/diagnostic.txt
index e1b52746be..7066a3739a 100644
--- a/runtime/doc/diagnostic.txt
+++ b/runtime/doc/diagnostic.txt
@@ -68,11 +68,11 @@ The "severity" key in a diagnostic is one of the values defined in
Functions that take a severity as an optional parameter (e.g.
|vim.diagnostic.get()|) accept one of two forms:
-1. A single |vim.diagnostic.severity| value: >
+1. A single |vim.diagnostic.severity| value: >lua
vim.diagnostic.get(0, { severity = vim.diagnostic.severity.WARN })
-2. A table with a "min" or "max" key (or both): >
+2. A table with a "min" or "max" key (or both): >lua
vim.diagnostic.get(0, { severity = { min = vim.diagnostic.severity.WARN } })
@@ -107,7 +107,7 @@ Nvim provides these handlers by default: "virtual_text", "signs", and
*diagnostic-handlers-example*
The example below creates a new handler that notifies the user of diagnostics
-with |vim.notify()|: >
+with |vim.notify()|: >lua
-- It's good practice to namespace custom handlers to avoid collisions
vim.diagnostic.handlers["my/notify"] = {
@@ -135,7 +135,7 @@ In this example, there is nothing to do when diagnostics are hidden, so we
omit the "hide" function.
Existing handlers can be overridden. For example, use the following to only
-show a sign for the highest severity diagnostic on a given line: >
+show a sign for the highest severity diagnostic on a given line: >lua
-- Create a custom namespace. This will aggregate signs from all other
-- namespaces and only show the one with the highest severity on a
@@ -185,7 +185,7 @@ own default highlight groups.
For example, the default highlighting for |hl-DiagnosticSignError| is linked
to |hl-DiagnosticError|. To change the default (and therefore the linked
-highlights), use the |:highlight| command: >
+highlights), use the |:highlight| command: >vim
highlight DiagnosticError guifg="BrightRed"
<
@@ -209,6 +209,11 @@ DiagnosticHint
Used as the base highlight group.
Other Diagnostic highlights link to this by default (except Underline)
+ *hl-DiagnosticOk*
+DiagnosticOk
+ Used as the base highlight group.
+ Other Diagnostic highlights link to this by default (except Underline)
+
*hl-DiagnosticVirtualTextError*
DiagnosticVirtualTextError
Used for "Error" diagnostic virtual text.
@@ -225,6 +230,10 @@ DiagnosticVirtualTextInfo
DiagnosticVirtualTextHint
Used for "Hint" diagnostic virtual text.
+ *hl-DiagnosticVirtualTextOk*
+DiagnosticVirtualTextOk
+ Used for "Ok" diagnostic virtual text.
+
*hl-DiagnosticUnderlineError*
DiagnosticUnderlineError
Used to underline "Error" diagnostics.
@@ -241,6 +250,10 @@ DiagnosticUnderlineInfo
DiagnosticUnderlineHint
Used to underline "Hint" diagnostics.
+ *hl-DiagnosticUnderlineOk*
+DiagnosticUnderlineOk
+ Used to underline "Ok" diagnostics.
+
*hl-DiagnosticFloatingError*
DiagnosticFloatingError
Used to color "Error" diagnostic messages in diagnostics float.
@@ -258,6 +271,10 @@ DiagnosticFloatingInfo
DiagnosticFloatingHint
Used to color "Hint" diagnostic messages in diagnostics float.
+ *hl-DiagnosticFloatingOk*
+DiagnosticFloatingOk
+ Used to color "Ok" diagnostic messages in diagnostics float.
+
*hl-DiagnosticSignError*
DiagnosticSignError
Used for "Error" signs in sign column.
@@ -274,12 +291,16 @@ DiagnosticSignInfo
DiagnosticSignHint
Used for "Hint" signs in sign column.
+ *hl-DiagnosticSignOk*
+DiagnosticSignOk
+ Used for "Ok" signs in sign column.
+
==============================================================================
SIGNS *diagnostic-signs*
Signs are defined for each diagnostic severity. The default text for each sign
is the first letter of the severity name (for example, "E" for ERROR). Signs
-can be customized using the following: >
+can be customized using the following: >vim
sign define DiagnosticSignError text=E texthl=DiagnosticSignError linehl= numhl=
sign define DiagnosticSignWarn text=W texthl=DiagnosticSignWarn linehl= numhl=
@@ -295,10 +316,18 @@ option in the "signs" table of |vim.diagnostic.config()| or 10 if unset).
EVENTS *diagnostic-events*
*DiagnosticChanged*
-DiagnosticChanged After diagnostics have changed.
+DiagnosticChanged After diagnostics have changed. When used from Lua,
+ the new diagnostics are passed to the autocmd
+ callback in the "data" table.
+
+Example: >lua
-Example: >
- autocmd DiagnosticChanged * lua vim.diagnostic.setqflist({ open = false })
+ vim.api.nvim_create_autocmd('DiagnosticChanged', {
+ callback = function(args)
+ local diagnostics = args.data.diagnostics
+ vim.pretty_print(diagnostics)
+ end,
+ })
<
==============================================================================
Lua module: vim.diagnostic *diagnostic-api*
@@ -312,12 +341,12 @@ config({opts}, {namespace}) *vim.diagnostic.config()*
|vim.diagnostic.show()|). Ephemeral configuration has highest priority,
followed by namespace configuration, and finally global configuration.
- For example, if a user enables virtual text globally with >
+ For example, if a user enables virtual text globally with >lua
vim.diagnostic.config({ virtual_text = true })
<
- and a diagnostic producer sets diagnostics with >
+ and a diagnostic producer sets diagnostics with >lua
vim.diagnostic.set(ns, 0, diagnostics, { virtual_text = false })
<
@@ -334,7 +363,7 @@ config({opts}, {namespace}) *vim.diagnostic.config()*
any of the above.
Parameters: ~
- {opts} (table|nil) When omitted or "nil", retrieve the current
+ • {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
@@ -357,16 +386,21 @@ config({opts}, {namespace}) *vim.diagnostic.config()*
the beginning of the virtual text.
• prefix: (string) Prepend diagnostic message with
prefix.
+ • suffix: (string or function) Append diagnostic
+ message with suffix. If a function, it must have the
+ signature (diagnostic) -> string, where {diagnostic}
+ is of type |diagnostic-structure|. This can be used
+ to render an LSP diagnostic error code.
• format: (function) A function that takes a diagnostic
as input and returns a string. The return value is
- the text used to display the diagnostic. Example: >
+ the text used to display the diagnostic. Example: >lua
- function(diagnostic)
- if diagnostic.severity == vim.diagnostic.severity.ERROR then
- return string.format("E: %s", diagnostic.message)
+ function(diagnostic)
+ if diagnostic.severity == vim.diagnostic.severity.ERROR then
+ return string.format("E: %s", diagnostic.message)
+ end
+ return diagnostic.message
end
- return diagnostic.message
- end
<
• signs: (default true) Use signs for diagnostics.
@@ -389,57 +423,57 @@ config({opts}, {namespace}) *vim.diagnostic.config()*
severities are 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.
+ • {namespace} (number|nil) Update the options for the given namespace.
When omitted, update the global diagnostic options.
disable({bufnr}, {namespace}) *vim.diagnostic.disable()*
Disable diagnostics in the given buffer.
Parameters: ~
- {bufnr} (number|nil) Buffer number, or 0 for current buffer. When
+ • {bufnr} (number|nil) Buffer number, or 0 for current buffer. When
omitted, disable diagnostics in all buffers.
- {namespace} (number|nil) Only disable diagnostics for the given
+ • {namespace} (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 buffer. When
+ • {bufnr} (number|nil) Buffer number, or 0 for current buffer. When
omitted, enable diagnostics in all buffers.
- {namespace} (number|nil) Only enable diagnostics for the given
+ • {namespace} (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
+ • {list} (table) A list of quickfix items from |getqflist()| or
|getloclist()|.
Return: ~
- array of diagnostics |diagnostic-structure|
+ Diagnostic [] array of |diagnostic-structure|
get({bufnr}, {opts}) *vim.diagnostic.get()*
Get current diagnostics.
Parameters: ~
- {bufnr} (number|nil) Buffer number to get diagnostics from. Use 0 for
+ • {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:
+ • {opts} (table|nil) A table with the following keys:
• namespace: (number) Limit diagnostics to the given
namespace.
• lnum: (number) Limit diagnostics to the given line number.
• severity: See |diagnostic-severity|.
Return: ~
- (table) A list of diagnostic items |diagnostic-structure|.
+ Diagnostic [] 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
@@ -454,43 +488,45 @@ 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|nil) See |vim.diagnostic.goto_next()|
Return: ~
- (table) Next diagnostic
+ Diagnostic|nil 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|nil) See |vim.diagnostic.goto_next()|
Return: ~
- (table) Next diagnostic position as a (row, col) tuple.
+ table|false Next diagnostic position as a (row, col) tuple or false if
+ no next diagnostic.
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} nil|table See |vim.diagnostic.goto_next()|
Return: ~
- (table) Previous diagnostic
+ Diagnostic|nil 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|nil) See |vim.diagnostic.goto_next()|
Return: ~
- (table) Previous diagnostic position as a (row, col) tuple.
+ table|false Previous diagnostic position as a (row, col) tuple or
+ false if there is no prior diagnostic
goto_next({opts}) *vim.diagnostic.goto_next()*
Move to the next diagnostic.
Parameters: ~
- {opts} (table|nil) Configuration table with the following keys:
+ • {opts} (table|nil) Configuration table with the following keys:
• namespace: (number) Only consider diagnostics from the given
namespace.
• cursor_position: (cursor position) Cursor position as a
@@ -511,7 +547,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|nil) See |vim.diagnostic.goto_next()|
hide({namespace}, {bufnr}) *vim.diagnostic.hide()*
Hide currently displayed diagnostics.
@@ -524,11 +560,23 @@ hide({namespace}, {bufnr}) *vim.diagnostic.hide()*
|vim.diagnostic.disable()|.
Parameters: ~
- {namespace} (number|nil) Diagnostic namespace. When omitted, hide
- diagnostics from all namespaces.
- {bufnr} (number|nil) Buffer number, or 0 for current buffer. When
+ • {namespace} (number|nil) Diagnostic namespace. When omitted, hide diagnostics from all
+ namespaces.
+ • {bufnr} (number|nil) Buffer number, or 0 for current buffer. When
omitted, hide diagnostics in all buffers.
+is_disabled({bufnr}, {namespace}) *vim.diagnostic.is_disabled()*
+ Check whether diagnostics are disabled in a given buffer.
+
+ Parameters: ~
+ • {bufnr} (number|nil) Buffer number, or 0 for current buffer.
+ • {namespace} (number|nil) Diagnostic namespace. When omitted, checks if all diagnostics are
+ disabled in {bufnr}. Otherwise, only checks if
+ diagnostics from {namespace} are disabled.
+
+ Return: ~
+ (boolean)
+
*vim.diagnostic.match()*
match({str}, {pat}, {groups}, {severity_map}, {defaults})
Parse a diagnostic from a string.
@@ -538,34 +586,34 @@ match({str}, {pat}, {groups}, {severity_map}, {defaults})
WARNING filename:27:3: Variable 'foo' does not exist
<
- This can be parsed into a diagnostic |diagnostic-structure| with: >
+ This can be parsed into a diagnostic |diagnostic-structure| with: >lua
- local s = "WARNING filename:27:3: Variable 'foo' does not exist"
- local pattern = "^(%w+) %w+:(%d+):(%d+): (.+)$"
- local groups = { "severity", "lnum", "col", "message" }
- vim.diagnostic.match(s, pattern, groups, { WARNING = vim.diagnostic.WARN })
+ local s = "WARNING filename:27:3: Variable 'foo' does not exist"
+ local pattern = "^(%w+) %w+:(%d+):(%d+): (.+)$"
+ local groups = { "severity", "lnum", "col", "message" }
+ vim.diagnostic.match(s, pattern, groups, { WARNING = vim.diagnostic.WARN })
<
Parameters: ~
- {str} (string) String to parse diagnostics from.
- {pat} (string) Lua pattern with capture groups.
- {groups} (table) List of fields in a |diagnostic-structure| to
+ • {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 from
+ • {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
+ • {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.
Return: ~
- diagnostic |diagnostic-structure| or `nil` if {pat} fails to match
- {str}.
+ Diagnostic|nil: |diagnostic-structure| or `nil` if {pat} fails to
+ match {str}.
open_float({opts}, {...}) *vim.diagnostic.open_float()*
Show diagnostics in a floating window.
Parameters: ~
- {opts} (table|nil) Configuration table with the same keys as
+ • {opts} (table|nil) Configuration table with the same keys as
|vim.lsp.util.open_floating_preview()| in addition to the
following:
• bufnr: (number) Buffer number to show diagnostics from.
@@ -610,9 +658,12 @@ open_float({opts}, {...}) *vim.diagnostic.open_float()*
{prefix} is a string, it is prepended to each diagnostic in
the window with no highlight. Overrides the setting from
|vim.diagnostic.config()|.
+ • suffix: Same as {prefix}, but appends the text to the
+ diagnostic instead of prepending it. Overrides the setting
+ from |vim.diagnostic.config()|.
Return: ~
- tuple ({float_bufnr}, {win_id})
+ number|nil, number|nil: ({float_bufnr}, {win_id})
reset({namespace}, {bufnr}) *vim.diagnostic.reset()*
Remove all diagnostics from the given namespace.
@@ -623,27 +674,27 @@ reset({namespace}, {bufnr}) *vim.diagnostic.reset()*
re-displayed, use |vim.diagnostic.hide()|.
Parameters: ~
- {namespace} (number|nil) Diagnostic namespace. When omitted, remove
- diagnostics from all namespaces.
- {bufnr} (number|nil) Remove diagnostics for the given buffer.
+ • {namespace} (number|nil) Diagnostic namespace. When omitted, remove diagnostics from all
+ namespaces.
+ • {bufnr} (number|nil) Remove diagnostics for the given buffer.
When omitted, diagnostics are removed for all buffers.
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 keys:
+ • {opts} (table|nil) Configuration table with the following keys:
• namespace: (number) Only add diagnostics from the given
namespace.
• winnr: (number, default 0) Window number to set location
@@ -658,7 +709,7 @@ setqflist({opts}) *vim.diagnostic.setqflist()*
Add all diagnostics to the quickfix list.
Parameters: ~
- {opts} (table|nil) Configuration table with the following keys:
+ • {opts} (table|nil) Configuration table with the following keys:
• namespace: (number) Only add diagnostics from the given
namespace.
• open: (boolean, default true) Open quickfix list after
@@ -672,17 +723,17 @@ show({namespace}, {bufnr}, {diagnostics}, {opts})
Display diagnostics for the given namespace and buffer.
Parameters: ~
- {namespace} (number|nil) Diagnostic namespace. When omitted, show
- diagnostics from all namespaces.
- {bufnr} (number|nil) Buffer number, or 0 for current buffer.
+ • {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,
+ • {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()*
@@ -690,9 +741,9 @@ toqflist({diagnostics}) *vim.diagnostic.toqflist()*
passed to |setqflist()| or |setloclist()|.
Parameters: ~
- {diagnostics} (table) List of diagnostics |diagnostic-structure|.
+ • {diagnostics} (table) List of diagnostics |diagnostic-structure|.
Return: ~
- array of quickfix list items |setqflist-what|
+ table[] of quickfix list items |setqflist-what|
vim:tw=78:ts=8:sw=4:sts=4:et:ft=help:norl:
diff --git a/runtime/doc/diff.txt b/runtime/doc/diff.txt
index 9c5792dd43..382d025d3c 100644
--- a/runtime/doc/diff.txt
+++ b/runtime/doc/diff.txt
@@ -137,6 +137,10 @@ Otherwise they are set to their default value:
'foldmethod' "manual"
'foldcolumn' 0
+'foldenable' will most-likely be reset to off. That is when 'foldmethod' is
+restored to "manual". The folds themselves are not cleared but they should
+not show up, resetting 'foldenable' is the best way to do that.
+
==============================================================================
2. Viewing diffs *view-diffs*
@@ -388,6 +392,11 @@ mode, so that a CTRL-Z doesn't end the text on DOS.
The `redraw!` command may not be needed, depending on whether executing a
shell command shows something on the display or not.
+If the 'diffexpr' expression starts with s: or |<SID>|, then it is replaced
+with the script ID (|local-function|). Example: >
+ set diffexpr=s:MyDiffExpr()
+ set diffexpr=<SID>SomeDiffExpr()
+<
*E810* *E97*
Vim will do a test if the diff output looks alright. If it doesn't, you will
get an error message. Possible causes:
@@ -401,7 +410,7 @@ to see more messages.
The self-installing Vim for MS-Windows includes a diff program. If you don't
have it you might want to download a diff.exe. For example from
-http://gnuwin32.sourceforge.net/packages/diffutils.htm.
+https://gnuwin32.sourceforge.net/packages/diffutils.htm.
USING PATCHES *diff-patchexpr*
@@ -439,4 +448,9 @@ evaluating 'patchexpr'. This hopefully avoids that files in the current
directory are accidentally patched. Vim will also delete files starting with
v:fname_in and ending in ".rej" and ".orig".
+If the 'patchexpr' expression starts with s: or |<SID>|, then it is replaced
+with the script ID (|local-function|). Example: >
+ set patchexpr=s:MyPatchExpr()
+ set patchexpr=<SID>SomePatchExpr()
+<
vim:tw=78:ts=8:noet:ft=help:norl:
diff --git a/runtime/doc/digraph.txt b/runtime/doc/digraph.txt
index eb3de0111f..ce0a929bc1 100644
--- a/runtime/doc/digraph.txt
+++ b/runtime/doc/digraph.txt
@@ -156,7 +156,7 @@ These are the RFC1345 digraphs for the one-byte characters. See the output of
":digraphs" for the others.
EURO
-
+ *euro* *euro-digraph*
Exception: RFC1345 doesn't specify the euro sign. In Vim the digraph =e was
added for this. Note the difference between latin1, where the digraph Cu is
used for the currency sign, and latin9 (iso-8859-15), where the digraph =e is
diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt
index dcb0bf8a2e..f77db5fab3 100644
--- a/runtime/doc/editing.txt
+++ b/runtime/doc/editing.txt
@@ -345,9 +345,9 @@ escaped with a backslash.
Wildcards in {file} are expanded, but as with file completion, 'wildignore'
and 'suffixes' apply. Which wildcards are supported depends on the system.
These are the common ones:
- ? matches one character
- * matches anything, including nothing
- ** matches anything, including nothing, recurses into directories
+ `?` matches one character
+ `*` matches anything, including nothing
+ `**` matches anything, including nothing, recurses into directories
[abc] match 'a', 'b' or 'c'
To avoid the special meaning of the wildcards prepend a backslash. However,
@@ -406,7 +406,7 @@ external command, by putting an equal sign right after the first backtick,
e.g.: >
:e `=tempname()`
The expression can contain just about anything, thus this can also be used to
-avoid the special meaning of '"', '|', '%' and '#'. However, 'wildignore'
+avoid the special meaning of '"', "|", '%' and '#'. However, 'wildignore'
does apply like to other wildcards.
Environment variables in the expression are expanded when evaluating the
@@ -423,9 +423,8 @@ Note that such expressions are only supported in places where a filename is
expected as an argument to an Ex-command.
*++opt* *[++opt]*
-The [++opt] argument can be used to force the value of 'fileformat',
-'fileencoding' or 'binary' to a value for one command, and to specify the
-behavior for bad characters. The form is: >
+The [++opt] argument can be used to set some options for one command, and to
+specify the behavior for bad characters. The form is: >
++{optname}
Or: >
++{optname}={value}
@@ -436,11 +435,11 @@ Where {optname} is one of: *++ff* *++enc* *++bin* *++nobin* *++edit*
bin or binary sets 'binary'
nobin or nobinary resets 'binary'
bad specifies behavior for bad characters
- edit for |:read| only: keep option values as if editing
- a file
+ edit for |:read|: keeps options as if editing a file
+ p for |:write|: creates the file's parent directory
-{value} cannot contain white space. It can be any valid value for these
-options. Examples: >
+{value} cannot contain whitespace. It can be any valid value for the options.
+Examples: >
:e ++ff=unix
This edits the same file again with 'fileformat' set to "unix". >
@@ -450,9 +449,24 @@ This writes the current buffer to "newfile" in latin1 format.
The message given when writing a file will show "[converted]" when
'fileencoding' or the value specified with ++enc differs from 'encoding'.
-There may be several ++opt arguments, separated by white space. They must all
+There may be several ++opt arguments, separated by whitespace. They must all
appear before any |+cmd| argument.
+ *++p*
+The "++p" flag creates the parent directory of the file if it does not exist.
+For example if you edit "foo/bar/file.txt", the ":write ++p" command creates
+"foo/bar/" if necessary before writing the file. >
+
+ :edit foo/bar/file.txt
+ :write ++p
+
+If you want :write (without "++p") to always create missing parent
+directories, add this autocmd to your config: >
+
+ " Auto-create parent directories (except for URIs "://").
+ au BufWritePre,FileWritePre * if @% !~# '\(://\)' | call mkdir(expand('<afile>:p:h'), 'p') | endif
+<
+
*++bad*
The argument of "++bad=" specifies what happens with characters that can't be
converted and illegal bytes. It can be one of three things:
@@ -545,6 +559,44 @@ Before editing binary, executable or Vim script files you should set the
option. This will avoid the use of 'fileformat'. Without this you risk that
single <NL> characters are unexpectedly replaced with <CR><NL>.
+END OF LINE AND END OF FILE *eol-and-eof*
+
+Vim has several options to control the file format:
+ 'fileformat' the <EOL> style: Unix, DOS, Mac
+ 'endofline' whether the last line ends with a <EOL>
+ 'endoffile' whether the file ends with a CTRL-Z
+ 'fixendofline' whether to fix eol and eof
+
+The first three values are normally detected automatically when reading the
+file and are used when writing the text to a file. While editing the buffer
+it looks like every line has a line ending and the CTRL-Z isn't there (an
+exception is when 'binary' is set, it works differently then).
+
+The 'fixendofline' option can be used to choose what to write. You can also
+change the option values to write the file differently than how it was read.
+
+Here are some examples how to use them.
+
+If you want files in Unix format (every line NL terminated): >
+ setl ff=unix fixeol
+You should probably do this on any Unix-like system. Also modern MS-Windows
+systems tend to work well with this. It is recommended to always use this
+format for Vim scripts.
+
+If you want to use an old MS-DOS file in a modern environment, fixing line
+endings and dropping CTRL-Z, but keeping the <CR><NL> style <EOL>: >
+ setl ff=dos fixeol
+This is useful for many MS-Windows programs, they regularly expect the
+<CR><NL> line endings.
+
+If you want to drop the final <EOL> and add a final CTRL-Z (e.g. for an old
+system like CP/M): >
+ setl ff=dos nofixeol noeol eof
+
+If you want to preserve the fileformat exactly as-is, including any final
+<EOL> and final CTRL-Z: >
+ setl nofixeol
+
==============================================================================
3. The argument list *argument-list* *arglist*
@@ -842,7 +894,7 @@ changed. This is done for all *.c files.
Example: >
:args *.[ch]
:argdo %s/\<my_foo\>/My_Foo/ge | update
-This changes the word "my_foo" to "My_Foo" in all *.c and *.h files. The "e"
+This changes the word "my_foo" to "My_Foo" in all "*.c" and "*.h" files. The "e"
flag is used for the ":substitute" command to avoid an error for files where
"my_foo" isn't used. ":update" writes the file only if changes were made.
@@ -855,11 +907,13 @@ Note: When the 'write' option is off, you are not able to write any file.
*E502* *E503* *E504* *E505*
*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
- another reason why the file can't be written.
- For ++opt see |++opt|, but only ++bin, ++nobin, ++ff
- and ++enc are effective.
+ the normal way to save changes to a file. Fails when
+ 'readonly' is set or when there is another reason why
+ the file can't be written, such as when the parent
+ directory doesn't exist (use |++p| to avoid that).
+ For ++opt see |++opt|, but only ++p, ++bin, ++nobin,
+ ++ff and ++enc are effective.
+
:w[rite]! [++opt] Like ":write", but forcefully write when 'readonly' is
set or there is another reason why writing was
@@ -941,7 +995,7 @@ WRITING WITH MULTIPLE BUFFERS *buffer-write*
Vim will warn you if you try to overwrite a file that has been changed
-elsewhere. See |timestamp|.
+elsewhere (unless "!" was used). See |timestamp|.
*backup* *E207* *E506* *E507* *E508* *E509* *E510*
If you write to an existing file (but do not append) while the 'backup',
@@ -1222,8 +1276,8 @@ unmodified.
For MS-Windows you can modify the filters that are used in the browse
dialog. By setting the g:browsefilter or b:browsefilter variables, you can
change the filters globally or locally to the buffer. The variable is set to
-a string in the format "{filter label}\t{pattern};{pattern}\n" where {filter
-label} is the text that appears in the "Files of Type" comboBox, and {pattern}
+a string in the format "{filter label}\t{pattern};{pattern}\n" where "{filter
+label}" is the text that appears in the "Files of Type" comboBox, and {pattern}
is the pattern which filters the filenames. Several patterns can be given,
separated by ';'.
@@ -1271,6 +1325,7 @@ exist, the next-higher scope in the hierarchy applies.
:cd[!] {path} Change the current directory to {path}.
If {path} is relative, it is searched for in the
directories listed in |'cdpath'|.
+ Clear any window-local directory.
Does not change the meaning of an already opened file,
because its full path name is remembered. Files from
the |arglist| may change though!
@@ -1481,8 +1536,8 @@ doing something there and closing it should be OK (if there are no side
effects from other autocommands). Closing unrelated windows and buffers will
get you into trouble.
-Before writing a file the timestamp is checked. If it has changed, Vim will
-ask if you really want to overwrite the file:
+Before writing a file, the timestamp is checked (unless "!" was used).
+If it has changed, Vim will ask if you really want to overwrite the file:
WARNING: The file has been changed since reading it!!!
Do you really want to write to it (y/n)?
@@ -1521,8 +1576,8 @@ which is slightly different.
There are three different types of searching:
1) Downward search: *starstar*
- Downward search uses the wildcards '*', '**' and possibly others
- supported by your operating system. '*' and '**' are handled inside Vim,
+ Downward search uses the wildcards "*", "**" and possibly others
+ supported by your operating system. "*" and "**" are handled inside Vim,
so they work on all operating systems. Note that "**" only acts as a
special wildcard when it is at the start of a name.
@@ -1530,12 +1585,12 @@ There are three different types of searching:
search pattern this would be ".*". Note that the "." is not used for file
searching.
- '**' is more sophisticated:
+ "**" is more sophisticated:
- It ONLY matches directories.
- It matches up to 30 directories deep by default, so you can use it to
search an entire directory tree
- The maximum number of levels matched can be given by appending a number
- to '**'.
+ to "**".
Thus '/usr/**2' can match: >
/usr
/usr/include
@@ -1546,14 +1601,14 @@ There are three different types of searching:
....
< It does NOT match '/usr/include/g++/std' as this would be three
levels.
- The allowed number range is 0 ('**0' is removed) to 100
+ The allowed number range is 0 ("**0" is removed) to 100
If the given number is smaller than 0 it defaults to 30, if it's
bigger than 100 then 100 is used. The system also has a limit on the
path length, usually 256 or 1024 bytes.
- - '**' can only be at the end of the path or be followed by a path
+ - "**" can only be at the end of the path or be followed by a path
separator or by a number and a path separator.
- You can combine '*' and '**' in any order: >
+ You can combine "*" and "**" in any order: >
/usr/**/sys/*
/usr/*tory/sys/**
/usr/**2/sys/*
@@ -1610,4 +1665,32 @@ There are three different types of searching:
currently work with 'path' items that contain a URL or use the double star
with depth limiter (/usr/**2) or upward search (;) notations.
+==============================================================================
+12. Trusted Files *trust*
+
+Nvim has the ability to execute arbitrary code through the 'exrc' option. In
+order to prevent executing code from untrusted sources, Nvim has the concept of
+"trusted files". An untrusted file will not be executed without the user's
+consent, and a user can permanently mark a file as trusted or untrusted using
+the |:trust| command or the |vim.secure.read()| function.
+
+ *:trust* *E5570*
+:trust [++deny] [++remove] [{file}]
+
+ Manage files in the trust database. Without any options
+ or arguments, :trust adds the file associated with the
+ current buffer to the trust database, along with the
+ SHA256 hash of its contents.
+
+ [++deny] marks the file associated with the current
+ buffer (or {file}, if given) as denied; no prompts will
+ be displayed to the user and the file will never be
+ executed.
+
+ [++remove] removes the file associated with the current
+ buffer (or {file}, if given) from the trust database.
+ Future attempts to read the file in a secure setting
+ (i.e. with 'exrc' or |vim.secure.read()|) will prompt
+ the user if the file is trusted.
+
vim:tw=78:ts=8:noet:ft=help:norl:
diff --git a/runtime/doc/editorconfig.txt b/runtime/doc/editorconfig.txt
new file mode 100644
index 0000000000..04a057e5ff
--- /dev/null
+++ b/runtime/doc/editorconfig.txt
@@ -0,0 +1,91 @@
+*editorconfig.txt* Nvim
+
+
+ NVIM REFERENCE MANUAL
+
+
+EditorConfig integration *editorconfig*
+
+Nvim natively supports EditorConfig. When a file is opened, Nvim searches
+upward through all of the parent directories of that file looking for
+".editorconfig" files. Each of these is parsed and any properties that match
+the opened file are applied.
+
+For more information on EditorConfig, see https://editorconfig.org/.
+
+ *g:editorconfig* *b:editorconfig*
+EditorConfig integration can be disabled globally by adding >lua
+
+ vim.g.editorconfig = false
+<
+to the user's |init.lua| file (or the Vimscript equivalent to |init.vim|). It
+can also be disabled per-buffer by setting the |b:editorconfig| buffer-local
+variable to `false`.
+
+When Nvim finds a valid .editorconfig file it will store the applied
+properties in the buffer variable |b:editorconfig| if it was not already set to
+`false` by the user.
+
+ *editorconfig-properties*
+The following properties are supported by default:
+
+ *editorconfig_root*
+root If "true", then stop searching for .editorconfig files
+ in parent directories. This property must be at the
+ top-level of the .editorconfig file (i.e. it must not
+ be within a glob section).
+
+ *editorconfig_charset*
+charset One of "utf-8", "utf-8-bom", "latin1", "utf-16be", or
+ "utf-16le". Sets the 'fileencoding' and 'bomb'
+ options.
+
+ *editorconfig_end_of_line*
+end_of_line One of "lf", "crlf", or "cr". These correspond to
+ setting 'fileformat' to "unix", "dos", or "mac",
+ respectively.
+
+ *editorconfig_indent_style*
+indent_style One of "tab" or "space". Sets the 'expandtab' option.
+
+ *editorconfig_indent_size*
+indent_size A number indicating the size of a single indent.
+ Alternatively, use the value "tab" to use the value of
+ the tab_width property. Sets the 'shiftwidth' and
+ 'softtabstop'.
+
+ *editorconfig_insert_final_newline*
+insert_final_newline "true" or "false" to ensure the file always has a
+ trailing newline as its last byte. Sets the
+ 'fixendofline' and 'endofline' options.
+
+ *editorconfig_max_line_length*
+max_line_length A number indicating the maximum length of a single
+ line. Sets the 'textwidth' option.
+
+ *editorconfig_tab_width*
+tab_width The display size of a single tab character. Sets the
+ 'tabstop' option.
+
+ *editorconfig_trim_trailing_whitespace*
+trim_trailing_whitespace
+ When "true", trailing whitespace is automatically
+ removed when the buffer is written.
+
+ *editorconfig-custom-properties*
+New properties can be added by adding a new entry to the "properties" table.
+The table key is a property name and the value is a callback function which
+accepts the number of the buffer to be modified, the value of the property
+in the .editorconfig file, and (optionally) a table containing all of the
+other properties and their values (useful for properties which depend on other
+properties). The value is always a string and must be coerced if necessary.
+Example: >lua
+
+ require('editorconfig').properties.foo = function(bufnr, val, opts)
+ if opts.charset and opts.charset ~= "utf-8" then
+ error("foo can only be set when charset is utf-8", 0)
+ end
+ vim.b[bufnr].foo = val
+ end
+<
+ vim:tw=78:ts=8:noet:ft=help:norl:
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 376adfec7f..58759a6053 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -4,7 +4,7 @@
VIM REFERENCE MANUAL by Bram Moolenaar
-Expression evaluation *expression* *expr* *E15* *eval*
+Expression evaluation *vimscript* *expression* *expr* *E15* *eval*
Using expressions is introduced in chapter 41 of the user manual |usr_41.txt|.
@@ -40,7 +40,7 @@ List An ordered sequence of items, see |List| for details.
Dictionary An associative, unordered array: Each entry has a key and a
value. |Dictionary|
Examples:
- {'blue': "#0000ff", 'red': "#ff0000"}
+ {"blue": "#0000ff", "red": "#ff0000"}
#{blue: "#0000ff", red: "#ff0000"}
Blob Binary Large Object. Stores any sequence of bytes. See |Blob|
@@ -229,13 +229,13 @@ is not available it returns zero or the default value you specify: >
List concatenation ~
-
+ *list-concatenation*
Two lists can be concatenated with the "+" operator: >
:let longlist = mylist + [5, 6]
:let mylist += [7, 8]
-To prepend or append an item turn the item into a list by putting [] around
-it. To change a list in-place see |list-modification| below.
+To prepend or append an item, turn the item into a list by putting [] around
+it. To change a list in-place, refer to |list-modification| below.
Sublist ~
@@ -457,7 +457,7 @@ 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', leading zeros are dropped. The
empty string can also be used as a key.
- *literal-Dict*
+ *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 '_'.
Example: >
@@ -681,7 +681,7 @@ similar to -1. >
:let shortblob = myblob[2:2] " Blob with one byte: 0z22
: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
+If the first index is beyond the last byte of the Blob or the second index is
before the first index, the result is an empty Blob. There is no error
message.
@@ -704,8 +704,8 @@ The length of the replaced bytes must be exactly the same as the value
provided. *E972*
To change part of a blob you can specify the first and last byte to be
-modified. The value must at least have the number of bytes in the range: >
- :let blob[3:5] = [3, 4, 5]
+modified. The value must have the same number of bytes in the range: >
+ :let blob[3:5] = 0z334455
You can also use the functions |add()|, |remove()| and |insert()|.
@@ -840,8 +840,8 @@ Example: >
All expressions within one level are parsed from left to right.
+------------------------------------------------------------------------------
expr1 *expr1* *ternary* *E109*
------
expr2 ? expr1 : expr1
@@ -867,8 +867,8 @@ You should always put a space before the ':', otherwise it can be mistaken for
use in a variable such as "a:1".
+------------------------------------------------------------------------------
expr2 and expr3 *expr2* *expr3*
----------------
expr3 || expr3 .. logical OR *expr-barbar*
expr4 && expr4 .. logical AND *expr-&&*
@@ -906,8 +906,8 @@ This is valid whether "b" has been defined or not. The second clause will
only be evaluated if "b" has been defined.
+------------------------------------------------------------------------------
expr4 *expr4*
------
expr5 {cmp} expr5
@@ -1010,8 +1010,9 @@ can be matched like an ordinary character. Examples:
"foo\nbar" =~ "\\n" evaluates to 0
+------------------------------------------------------------------------------
expr5 and expr6 *expr5* *expr6*
----------------
+
expr6 + expr6 Number addition, |List| or |Blob| concatenation *expr-+*
expr6 - expr6 Number subtraction *expr--*
expr6 . expr6 String concatenation *expr-.*
@@ -1064,8 +1065,9 @@ None of these work for |Funcref|s.
. and % do not work for Float. *E804*
+------------------------------------------------------------------------------
expr7 *expr7*
------
+
! expr7 logical NOT *expr-!*
- expr7 unary minus *expr-unary--*
+ expr7 unary plus *expr-unary-+*
@@ -1082,8 +1084,9 @@ These three can be repeated and mixed. Examples:
--9 == 9
+------------------------------------------------------------------------------
expr8 *expr8*
------
+
This expression is either |expr9| or a sequence of the alternatives below,
in any order. E.g., these are all possible:
expr8[expr1].name
@@ -1228,11 +1231,15 @@ And NOT: >
\ ->map(mapexpr)
\ ->sort()
\ ->join()
-<
+
+When using the lambda form there must be no white space between the } and the
+(.
+
*expr9*
+------------------------------------------------------------------------------
number
-------
+
number number constant *expr-number*
*0x* *hex-number* *0o* *octal-number* *binary-number*
@@ -1294,9 +1301,9 @@ function. Example: >
< 7.853981633974483e-01
-
+------------------------------------------------------------------------------
string *string* *String* *expr-string* *E114*
-------
+
"string" string constant *expr-quote*
Note that double quotes are used.
@@ -1335,16 +1342,17 @@ encodings. Use "\u00ff" to store character 255 correctly as UTF-8.
Note that "\000" and "\x00" force the end of the string.
+------------------------------------------------------------------------------
blob-literal *blob-literal* *E973*
-------------
Hexadecimal starting with 0z or 0Z, with an arbitrary number of bytes.
The sequence must be an even number of hex characters. Example: >
:let b = 0zFF00ED015DAF
+------------------------------------------------------------------------------
literal-string *literal-string* *E115*
----------------
+
'string' string constant *expr-'*
Note that single quotes are used.
@@ -1358,8 +1366,9 @@ to be doubled. These two commands are equivalent: >
if a =~ '\s*'
+------------------------------------------------------------------------------
option *expr-option* *E112* *E113*
-------
+
&option option value, local value if possible
&g:option global option value
&l:option local option value
@@ -1373,8 +1382,9 @@ and there is no buffer-local or window-local value, the global value is used
anyway.
+------------------------------------------------------------------------------
register *expr-register* *@r*
---------
+
@r contents of register 'r'
The result is the contents of the named register, as a single string.
@@ -1391,8 +1401,9 @@ nesting *expr-nesting* *E110*
(expr1) nested expression
+------------------------------------------------------------------------------
environment variable *expr-env*
---------------------
+
$VAR environment variable
The String value of any environment variable. When it is not defined, the
@@ -1417,20 +1428,23 @@ The first one probably doesn't echo anything, the second echoes the $shell
variable (if your shell supports it).
+------------------------------------------------------------------------------
internal variable *expr-variable*
------------------
+
variable internal variable
See below |internal-variables|.
+------------------------------------------------------------------------------
function call *expr-function* *E116* *E118* *E119* *E120*
--------------
+
function(expr1, ...) function call
See below |functions|.
+------------------------------------------------------------------------------
lambda expression *expr-lambda* *lambda*
------------------
+
{args -> expr1} lambda expression
A lambda expression creates a new unnamed function which returns the result of
@@ -1521,7 +1535,7 @@ specified by what is prepended:
|tabpage-variable| t: Local to the current tab page.
|global-variable| g: Global.
|local-variable| l: Local to a function.
-|script-variable| s: Local to a |:source|'ed Vim script.
+|script-variable| s: Local to a |:source|d Vim script.
|function-argument| a: Function argument (only inside a function).
|vim-variable| v: Global, predefined by Vim.
@@ -1704,17 +1718,13 @@ v:charconvert_to
Only valid while evaluating the 'charconvert' option.
*v:cmdarg* *cmdarg-variable*
-v:cmdarg This variable is used for two purposes:
- 1. The extra arguments given to a file read/write command.
- Currently these are "++enc=" and "++ff=". This variable is
- set before an autocommand event for a file read/write
- command is triggered. There is a leading space to make it
- possible to append this variable directly after the
- read/write command. Note: The "+cmd" argument isn't
- included here, because it will be executed anyway.
- 2. When printing a PostScript file with ":hardcopy" this is
- the argument for the ":hardcopy" command. This can be used
- in 'printexpr'.
+v:cmdarg
+ The extra arguments ("++p", "++enc=", "++ff=") given to a file
+ read/write command. This is set before an autocommand event
+ for a file read/write command is triggered. There is a
+ leading space to make it possible to append this variable
+ directly after the read/write command. Note: "+cmd" isn't
+ included here, because it will be executed anyway.
*v:collate* *collate-variable*
v:collate The current locale setting for collation order of the runtime
@@ -1777,7 +1787,7 @@ v:exiting Exit code, or |v:null| before invoking the |VimLeavePre|
and |VimLeave| autocmds. See |:q|, |:x| and |:cquit|.
Example: >
:au VimLeave * echo "Exit value is " .. v:exiting
-
+<
*v:echospace* *echospace-variable*
v:echospace Number of screen cells that can be used for an `:echo` message
in the last screen line before causing the |hit-enter-prompt|.
@@ -1912,17 +1922,16 @@ v:fname_in The name of the input file. Valid while evaluating:
'charconvert' file to be converted
'diffexpr' original file
'patchexpr' original file
- 'printexpr' file to be printed
And set to the swap file name for |SwapExists|.
*v:fname_out* *fname_out-variable*
v:fname_out The name of the output file. Only valid while
evaluating:
option used for ~
- 'charconvert' resulting converted file (*)
+ 'charconvert' resulting converted file [1]
'diffexpr' output of diff
'patchexpr' resulting patched file
- (*) When doing conversion for a write command (e.g., ":w
+ [1] When doing conversion for a write command (e.g., ":w
file") it will be equal to v:fname_in. When doing conversion
for a read command (e.g., ":e file") it will be a temporary
file and different from v:fname_in.
@@ -1992,10 +2001,10 @@ v:lc_time The current locale setting for time messages of the runtime
command. See |multi-lang|.
*v:lnum* *lnum-variable*
-v:lnum Line number for the 'foldexpr' |fold-expr|, 'formatexpr' and
- 'indentexpr' expressions, tab page number for 'guitablabel'
- and 'guitabtooltip'. Only valid while one of these
- expressions is being evaluated. Read-only when in the
+v:lnum Line number for the 'foldexpr' |fold-expr|, 'formatexpr',
+ 'indentexpr' and 'statuscolumn' expressions, tab page number
+ for 'guitablabel' and 'guitabtooltip'. Only valid while one of
+ these expressions is being evaluated. Read-only when in the
|sandbox|.
*v:lua* *lua-variable*
@@ -2129,6 +2138,10 @@ v:register The name of the register in effect for the current normal mode
'*' or '+'.
Also see |getreg()| and |setreg()|
+ *v:relnum* *relnum-variable*
+v:relnum Relative line number for the 'statuscolumn' expression.
+ Read-only.
+
*v:scrollstart* *scrollstart-variable*
v:scrollstart String describing the script or function that caused the
screen to scroll up. It's only set when it is empty, thus the
@@ -2138,9 +2151,11 @@ v:scrollstart String describing the script or function that caused the
hit-enter prompt.
*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| (deprecated) at startup.
+v:servername Primary listen-address of Nvim, the first item returned by
+ |serverlist()|. Usually this is the named pipe created by Nvim
+ at |startup| or given by |--listen| (or the deprecated
+ |$NVIM_LISTEN_ADDRESS| env var).
+
See also |serverstart()| |serverstop()|.
Read-only.
@@ -2280,6 +2295,13 @@ v:version Vim version number: major version times 100 plus minor
:if has("nvim-0.2.1")
<
+ *v:virtnum* *virtnum-variable*
+v:virtnum Virtual line number for the 'statuscolumn' expression.
+ Negative when drawing the status column for virtual lines, zero
+ when drawing an actual buffer line, and positive when drawing
+ the wrapped part of a buffer line.
+ Read-only.
+
*v:vim_did_enter* *vim_did_enter-variable*
v:vim_did_enter 0 during startup, 1 just before |VimEnter|.
Read-only.
@@ -2309,397 +2331,10 @@ help file: |builtin-functions|.
5. Defining functions *user-function*
New functions can be defined. These can be called just like builtin
-functions. The function executes a sequence of Ex commands. Normal mode
-commands can be executed with the |:normal| command.
-
-The function name must start with an uppercase letter, to avoid confusion with
-builtin functions. To prevent from using the same name in different scripts
-avoid obvious, short names. A good habit is to start the function name with
-the name of the script, e.g., "HTMLcolor()".
-
-It's also possible to use curly braces, see |curly-braces-names|. And the
-|autoload| facility is useful to define a function only when it's called.
-
- *local-function*
-A function local to a script must start with "s:". A local script function
-can only be called from within the script and from functions, user commands
-and autocommands defined in the script. It is also possible to call the
-function from a mapping defined in the script, but then |<SID>| must be used
-instead of "s:" when the mapping is expanded outside of the script.
-There are only script-local functions, no buffer-local or window-local
-functions.
-
- *:fu* *:function* *E128* *E129* *E123*
-:fu[nction] List all functions and their arguments.
-
-:fu[nction][!] {name} List function {name}, annotated with line numbers
- unless "!" is given.
- {name} may be a |Dictionary| |Funcref| entry: >
- :function dict.init
-
-:fu[nction] /{pattern} List functions with a name matching {pattern}.
- Example that lists all functions ending with "File": >
- :function /File$
-<
- *:function-verbose*
-When 'verbose' is non-zero, listing a function will also display where it was
-last defined. Example: >
+functions. The function takes arguments, executes a sequence of Ex commands
+and can return a value.
- :verbose function SetFileTypeSH
- function SetFileTypeSH(name)
- Last set from /usr/share/vim/vim-7.0/filetype.vim
-<
-See |:verbose-cmd| for more information.
-
- *E124* *E125* *E853* *E884*
-:fu[nction][!] {name}([arguments]) [range] [abort] [dict] [closure]
- Define a new function by the name {name}. The body of
- the function follows in the next lines, until the
- matching |:endfunction|.
-
- The name must be made of alphanumeric characters and
- '_', and must start with a capital or "s:" (see
- above). Note that using "b:" or "g:" is not allowed.
- (since patch 7.4.260 E884 is given if the function
- name has a colon in the name, e.g. for "foo:bar()".
- Before that patch no error was given).
-
- {name} can also be a |Dictionary| entry that is a
- |Funcref|: >
- :function dict.init(arg)
-< "dict" must be an existing dictionary. The entry
- "init" is added if it didn't exist yet. Otherwise [!]
- is required to overwrite an existing function. The
- result is a |Funcref| to a numbered function. The
- function can only be used with a |Funcref| and will be
- deleted if there are no more references to it.
- *E127* *E122*
- When a function by this name already exists and [!] is
- not used an error message is given. There is one
- exception: When sourcing a script again, a function
- that was previously defined in that script will be
- silently replaced.
- When [!] is used, an existing function is silently
- replaced. Unless it is currently being executed, that
- is an error.
- NOTE: Use ! wisely. If used without care it can cause
- an existing function to be replaced unexpectedly,
- which is hard to debug.
-
- For the {arguments} see |function-argument|.
-
- *:func-range* *a:firstline* *a:lastline*
- When the [range] argument is added, the function is
- expected to take care of a range itself. The range is
- passed as "a:firstline" and "a:lastline". If [range]
- is excluded, ":{range}call" will call the function for
- each line in the range, with the cursor on the start
- of each line. See |function-range-example|.
- The cursor is still moved to the first line of the
- range, as is the case with all Ex commands.
- *:func-abort*
- When the [abort] argument is added, the function will
- abort as soon as an error is detected.
- *:func-dict*
- When the [dict] argument is added, the function must
- be invoked through an entry in a |Dictionary|. The
- local variable "self" will then be set to the
- dictionary. See |Dictionary-function|.
- *:func-closure* *E932*
- When the [closure] argument is added, the function
- can access variables and arguments from the outer
- scope. This is usually called a closure. In this
- example Bar() uses "x" from the scope of Foo(). It
- remains referenced even after Foo() returns: >
- :function! Foo()
- : let x = 0
- : function! Bar() closure
- : let x += 1
- : return x
- : endfunction
- : return funcref('Bar')
- :endfunction
-
- :let F = Foo()
- :echo F()
-< 1 >
- :echo F()
-< 2 >
- :echo F()
-< 3
-
- *function-search-undo*
- The last used search pattern and the redo command "."
- will not be changed by the function. This also
- implies that the effect of |:nohlsearch| is undone
- when the function returns.
-
- *:endf* *:endfunction* *E126* *E193* *W22*
-:endf[unction] [argument]
- The end of a function definition. Best is to put it
- on a line by its own, without [argument].
-
- [argument] can be:
- | command command to execute next
- \n command command to execute next
- " comment always ignored
- anything else ignored, warning given when
- 'verbose' is non-zero
- The support for a following command was added in Vim
- 8.0.0654, before that any argument was silently
- ignored.
-
- To be able to define a function inside an `:execute`
- command, use line breaks instead of |:bar|: >
- :exe "func Foo()\necho 'foo'\nendfunc"
-<
- *:delf* *:delfunction* *E131* *E933*
-:delf[unction][!] {name}
- Delete function {name}.
- {name} can also be a |Dictionary| entry that is a
- |Funcref|: >
- :delfunc dict.init
-< This will remove the "init" entry from "dict". The
- function is deleted if there are no more references to
- it.
- With the ! there is no error if the function does not
- exist.
- *:retu* *:return* *E133*
-:retu[rn] [expr] Return from a function. When "[expr]" is given, it is
- evaluated and returned as the result of the function.
- If "[expr]" is not given, the number 0 is returned.
- When a function ends without an explicit ":return",
- the number 0 is returned.
- Note that there is no check for unreachable lines,
- thus there is no warning if commands follow ":return".
-
- If the ":return" is used after a |:try| 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 function. The function
- returns at the outermost ":endtry".
-
- *function-argument* *a:var*
-An argument can be defined by giving its name. In the function this can then
-be used as "a:name" ("a:" for argument).
- *a:0* *a:1* *a:000* *E740* *...*
-Up to 20 arguments can be given, separated by commas. After the named
-arguments an argument "..." can be specified, which means that more arguments
-may optionally be following. In the function the extra arguments can be used
-as "a:1", "a:2", etc. "a:0" is set to the number of extra arguments (which
-can be 0). "a:000" is set to a |List| that contains these arguments. Note
-that "a:1" is the same as "a:000[0]".
- *E742*
-The a: scope and the variables in it cannot be changed, they are fixed.
-However, if a composite type is used, such as |List| or |Dictionary| , you can
-change their contents. Thus you can pass a |List| to a function and have the
-function add an item to it. If you want to make sure the function cannot
-change a |List| or |Dictionary| use |:lockvar|.
-
-It is also possible to define a function without any arguments. You must
-still supply the () then.
-
-It is allowed to define another function inside a function body.
-
- *optional-function-argument*
-You can provide default values for positional named arguments. This makes
-them optional for function calls. When a positional argument is not
-specified at a call, the default expression is used to initialize it.
-This only works for functions declared with |function|, not for
-lambda expressions |expr-lambda|.
-
-Example: >
- function Something(key, value = 10)
- echo a:key .. ": " .. a:value
- endfunction
- call Something('empty') "empty: 10"
- call Something('key', 20) "key: 20"
-
-The argument default expressions are evaluated at the time of the function
-call, not definition. Thus it is possible to use an expression which is
-invalid the moment the function is defined. The expressions are also only
-evaluated when arguments are not specified during a call.
-
- *E989*
-Optional arguments with default expressions must occur after any mandatory
-arguments. You can use "..." after all optional named arguments.
-
-It is possible for later argument defaults to refer to prior arguments,
-but not the other way around. They must be prefixed with "a:", as with all
-arguments.
-
-Example that works: >
- :function Okay(mandatory, optional = a:mandatory)
- :endfunction
-Example that does NOT work: >
- :function NoGood(first = a:second, second = 10)
- :endfunction
-<
-When not using "...", the number of arguments in a function call must be at
-least equal to the number of mandatory named arguments. When using "...", the
-number of arguments may be larger than the total of mandatory and optional
-arguments.
-
- *local-variables*
-Inside a function local variables can be used. These will disappear when the
-function returns. Global variables need to be accessed with "g:".
-
-Example: >
- :function Table(title, ...)
- : echohl Title
- : echo a:title
- : echohl None
- : echo a:0 .. " items:"
- : for s in a:000
- : echon ' ' .. s
- : endfor
- :endfunction
-
-This function can then be called with: >
- call Table("Table", "line1", "line2")
- call Table("Empty Table")
-
-To return more than one value, return a |List|: >
- :function Compute(n1, n2)
- : if a:n2 == 0
- : return ["fail", 0]
- : endif
- : return ["ok", a:n1 / a:n2]
- :endfunction
-
-This function can then be called with: >
- :let [success, div] = Compute(102, 6)
- :if success == "ok"
- : echo div
- :endif
-<
- *:cal* *:call* *E107* *E117*
-:[range]cal[l] {name}([arguments])
- Call a function. The name of the function and its arguments
- are as specified with `:function`. Up to 20 arguments can be
- used. The returned value is discarded.
- Without a range and for functions that accept a range, the
- function is called once. When a range is given the cursor is
- positioned at the start of the first line before executing the
- function.
- When a range is given and the function doesn't handle it
- itself, the function is executed for each line in the range,
- with the cursor in the first column of that line. The cursor
- is left at the last line (possibly moved by the last function
- call). The arguments are re-evaluated for each line. Thus
- this works:
- *function-range-example* >
- :function Mynumber(arg)
- : echo line(".") .. " " .. a:arg
- :endfunction
- :1,5call Mynumber(getline("."))
-<
- The "a:firstline" and "a:lastline" are defined anyway, they
- can be used to do something different at the start or end of
- the range.
-
- Example of a function that handles the range itself: >
-
- :function Cont() range
- : execute (a:firstline + 1) .. "," .. a:lastline .. 's/^/\t\\ '
- :endfunction
- :4,8call Cont()
-<
- This function inserts the continuation character "\" in front
- of all the lines in the range, except the first one.
-
- When the function returns a composite value it can be further
- dereferenced, but the range will not be used then. Example: >
- :4,8call GetDict().method()
-< Here GetDict() gets the range but method() does not.
-
- *E132*
-The recursiveness of user functions is restricted with the |'maxfuncdepth'|
-option.
-
-It is also possible to use `:eval`. It does not support a range, but does
-allow for method chaining, e.g.: >
- eval GetList()->Filter()->append('$')
-
-
-AUTOMATICALLY LOADING FUNCTIONS ~
- *autoload-functions*
-When using many or large functions, it's possible to automatically define them
-only when they are used. There are two methods: with an autocommand and with
-the "autoload" directory in 'runtimepath'.
-
-
-Using an autocommand ~
-
-This is introduced in the user manual, section |41.14|.
-
-The autocommand is useful if you have a plugin that is a long Vim script file.
-You can define the autocommand and quickly quit the script with `:finish`.
-That makes Vim startup faster. The autocommand should then load the same file
-again, setting a variable to skip the `:finish` command.
-
-Use the FuncUndefined autocommand event with a pattern that matches the
-function(s) to be defined. Example: >
-
- :au FuncUndefined BufNet* source ~/vim/bufnetfuncs.vim
-
-The file "~/vim/bufnetfuncs.vim" should then define functions that start with
-"BufNet". Also see |FuncUndefined|.
-
-
-Using an autoload script ~
- *autoload* *E746*
-This is introduced in the user manual, section |41.15|.
-
-Using a script in the "autoload" directory is simpler, but requires using
-exactly the right file name. A function that can be autoloaded has a name
-like this: >
-
- :call filename#funcname()
-
-When such a function is called, and it is not defined yet, Vim will search the
-"autoload" directories in 'runtimepath' for a script file called
-"filename.vim". For example "~/.config/nvim/autoload/filename.vim". That
-file should then define the function like this: >
-
- function filename#funcname()
- echo "Done!"
- endfunction
-
-The file name and the name used before the # in the function must match
-exactly, and the defined function must have the name exactly as it will be
-called.
-
-It is possible to use subdirectories. Every # in the function name works like
-a path separator. Thus when calling a function: >
-
- :call foo#bar#func()
-
-Vim will look for the file "autoload/foo/bar.vim" in 'runtimepath'.
-
-This also works when reading a variable that has not been set yet: >
-
- :let l = foo#bar#lvar
-
-However, when the autoload script was already loaded it won't be loaded again
-for an unknown variable.
-
-When assigning a value to such a variable nothing special happens. This can
-be used to pass settings to the autoload script before it's loaded: >
-
- :let foo#bar#toggle = 1
- :call foo#bar#func()
-
-Note that when you make a mistake and call a function that is supposed to be
-defined in an autoload script, but the script doesn't actually define the
-function, you will get an error message for the missing function. If you fix
-the autoload script it won't be automatically loaded again. Either restart
-Vim or manually source the script.
-
-Also note that if you have two script files, and one calls a function in the
-other and vice versa, before the used function is defined, it won't work.
-Avoid using the autoload functionality at the toplevel.
+You can find most information about defining functions in |userfunc.txt|.
==============================================================================
6. Curly braces names *curly-braces-names*
@@ -2915,7 +2550,7 @@ text...
echo 'done'
endif
END
-< Results in: ["if ok", " echo 'done'", "endif"]
+< Results in: `["if ok", " echo 'done'", "endif"]`
The marker must line up with "let" and the indentation
of the first line is removed from all the text lines.
Specifically: all the leading indentation exactly
@@ -3046,6 +2681,8 @@ text...
[depth] is relevant when locking a |List| or
|Dictionary|. It specifies how deep the locking goes:
+ 0 Lock the variable {name} but not its
+ value.
1 Lock the |List| or |Dictionary| itself,
cannot add or remove items, but can
still change their values.
@@ -3059,7 +2696,14 @@ text...
|Dictionary|, one level deeper.
The default [depth] is 2, thus when {name} is a |List|
or |Dictionary| the values cannot be changed.
- *E743*
+
+ Example with [depth] 0: >
+ let mylist = [1, 2, 3]
+ lockvar 0 mylist
+ let mylist[0] = 77 " OK
+ call add(mylist, 4] " OK
+ let mylist = [7, 8, 9] " Error!
+< *E743*
For unlimited depth use [!] and omit [depth].
However, there is a maximum depth of 100 to catch
loops.
@@ -3080,6 +2724,8 @@ text...
Unlock the internal variable {name}. Does the
opposite of |:lockvar|.
+ No error is given if {name} does not exist.
+
: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.
@@ -3167,6 +2813,9 @@ text...
iterate over. Unlike with |List|, modifying the
|Blob| does not affect the iteration.
+ When {object} is a |String| each item is a string with
+ one character, plus any combining characters.
+
:for [{var1}, {var2}, ...] in {listlist}
:endfo[r]
Like `:for` above, but each item in {listlist} must be
@@ -3556,7 +3205,7 @@ this pending exception or command is discarded.
For examples see |throw-catch| and |try-finally|.
-NESTING OF TRY CONDITIONALS *try-nesting*
+NESTING OF TRY CONDITIONALS *try-nesting*
Try conditionals can be nested arbitrarily. That is, a complete try
conditional can be put into the try block, a catch clause, or the finally
@@ -4500,7 +4149,7 @@ This example sorts lines with a specific compare function. >
As a one-liner: >
:call setline(1, sort(getline(1, '$'), function("Strcmp")))
-
+<
scanf() replacement ~
*sscanf*
diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt
index 7fff74a963..a53c287d48 100644
--- a/runtime/doc/filetype.txt
+++ b/runtime/doc/filetype.txt
@@ -148,6 +148,7 @@ variables can be used to overrule the filetype used for certain extensions:
*.fs g:filetype_fs |ft-forth-syntax|
*.i g:filetype_i |ft-progress-syntax|
*.inc g:filetype_inc
+ *.lsl g:filetype_lsl
*.m g:filetype_m |ft-mathematica-syntax|
*.mod g:filetype_mod
*.p g:filetype_p |ft-pascal-syntax|
@@ -175,15 +176,13 @@ This means that the contents of compressed files are not inspected.
*new-filetype*
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. The following explains the legacy Vim mechanism (enabled
-if |do_legacy_filetype| is set). For Nvim's default mechanism, see
-|vim.filetype.add()|.
+to add it. The recommended way is to use |vim.filetype.add()| to add it to
+Nvim's builtin filetype detection mechanism. If you want to handle the
+detection manually, proceed as follows:
A. If you want to overrule all default file type checks.
This works by writing one file for each filetype. The disadvantage is that
- there can be many files. The advantage is that you can simply drop this
+ there can be many files. The advantage is that you can simply drop this
file in the right directory to make it work.
*ftdetect*
1. Create your user runtime directory. You would normally use the first
@@ -273,28 +272,14 @@ D. If your filetype can only be detected by inspecting the contents of the
means that your rules override the default rules in
$VIMRUNTIME/scripts.vim.
- *remove-filetype*
-If a file type is detected that is wrong for you, install a filetype.lua,
-filetype.vim or scripts.vim to catch it (see above). You can set 'filetype' to
-a non-existing name to avoid that it will be set later anyway: >
- :set filetype=ignored
-
-If you are setting up a system with many users, and you don't want each user
-to add/remove the same filetypes, consider writing the filetype.vim and
-scripts.vim files in a runtime directory that is used for everybody. Check
-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*
+ *remove-filetype*
+If a file type is detected that is wrong for you, you can set 'filetype' to
+a non-existing name such as `ignored` to avoid that it will be set later anyway.
+
+ *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.
+the `did_load_filetypes` global variable. If this variable exists, the default
+`$VIMRUNTIME/filetype.lua` will not run.
*plugin-details*
The "plugin" directory can be in any of the directories in the 'runtimepath'
@@ -549,7 +534,7 @@ 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"
+ let g:freebasic_lang = "fblite"
GIT COMMIT *ft-gitcommit-plugin*
@@ -586,12 +571,12 @@ Local mappings:
to the end of the file in Normal mode. This means "> " is inserted in
each line.
-MAN *ft-man-plugin* *:Man* *man.vim*
+MAN *ft-man-plugin* *:Man* *man.lua*
View manpages in Nvim. Supports highlighting, completion, locales, and
navigation. Also see |find-manpage|.
-man.vim will always attempt to reuse the closest man window (above/left) but
+man.lua will always attempt to reuse the closest man window (above/left) but
otherwise create a split.
The case sensitivity of completion is controlled by 'fileignorecase'.
diff --git a/runtime/doc/fold.txt b/runtime/doc/fold.txt
index 9e3d78faff..35a3be35fb 100644
--- a/runtime/doc/fold.txt
+++ b/runtime/doc/fold.txt
@@ -116,6 +116,11 @@ method can be very slow!
Try to avoid the "=", "a" and "s" return values, since Vim often has to search
backwards for a line for which the fold level is defined. This can be slow.
+If the 'foldexpr' expression starts with s: or |<SID>|, then it is replaced
+with the script ID (|local-function|). Examples: >
+ set foldexpr=s:MyFoldExpr()
+ set foldexpr=<SID>SomeFoldExpr()
+<
An example of using "a1" and "s1": For a multi-line C comment, a line
containing "/*" would return "a1" to start a fold, and a line containing "*/"
would return "s1" to end the fold after that line: >
@@ -491,7 +496,7 @@ is evaluated to obtain the text displayed for a closed fold. Example: >
This shows the first line of the fold, with "/*", "*/" and "{{{" removed.
Note the use of backslashes to avoid some characters to be interpreted by the
-":set" command. It's simpler to define a function and call that: >
+":set" command. It is much simpler to define a function and call it: >
:set foldtext=MyFoldText()
:function MyFoldText()
@@ -521,6 +526,11 @@ The resulting line is truncated to fit in the window, it never wraps.
When there is room after the text, it is filled with the character specified
by 'fillchars'.
+If the 'foldtext' expression starts with s: or |<SID>|, then it is replaced
+with the script ID (|local-function|). Examples: >
+ set foldtext=s:MyFoldText()
+ set foldtext=<SID>SomeFoldText()
+<
Note that backslashes need to be used for characters that the ":set" command
handles differently: Space, backslash and double-quote. |option-backslash|
@@ -576,6 +586,11 @@ line is folded, it cannot be displayed there.
Many movement commands handle a sequence of folded lines like an empty line.
For example, the "w" command stops once in the first column.
+When starting a search in a closed fold it will not find a match in the
+current fold. It's like a forward search always starts from the end of the
+closed fold, while a backwards search starts from the start of the closed
+fold.
+
When in Insert mode, the cursor line is never folded. That allows you to see
what you type!
diff --git a/runtime/doc/ft_ada.txt b/runtime/doc/ft_ada.txt
index f6dfa708fb..3321228009 100644
--- a/runtime/doc/ft_ada.txt
+++ b/runtime/doc/ft_ada.txt
@@ -51,7 +51,7 @@ for a complete list.
To enable them, assign a value to the option. For example, to turn one on:
>
> let g:ada_standard_types = 1
->
+
To disable them use ":unlet". Example:
>
> unlet g:ada_standard_types
@@ -288,7 +288,7 @@ g:ada_rainbow_color bool (true when exists)
rainbow_parenthesis for this to work.
*g:ada_folding*
-g:ada_folding set ('sigpft')
+g:ada_folding set ("sigpft")
Use folding for Ada sources.
's': activate syntax folding on load
'p': fold packages
@@ -296,10 +296,10 @@ g:ada_folding set ('sigpft')
't': fold types
'c': fold conditionals
'g': activate gnat pretty print folding on load
- 'i': lone 'is' folded with line above
- 'b': lone 'begin' folded with line above
- 'p': lone 'private' folded with line above
- 'x': lone 'exception' folded with line above
+ 'i': lone "is" folded with line above
+ 'b': lone "begin" folded with line above
+ 'p': lone "private" folded with line above
+ 'x': lone "exception" folded with line above
'i': activate indent folding on load
Note: Syntax folding is in an early (unusable) stage and
@@ -334,10 +334,10 @@ g:ada_omni_with_keywords
completion (|i_CTRL-X_CTRL-U|).
*g:ada_extended_tagging*
-g:ada_extended_tagging enum ('jump', 'list')
+g:ada_extended_tagging enum ("jump", "list")
use extended tagging, two options are available
- 'jump': use tjump to jump.
- 'list': add tags quick fix list.
+ "jump": use tjump to jump.
+ "list": add tags quick fix list.
Normal tagging does not support function or operator
overloading as these features are not available in C and
tagging was originally developed for C.
@@ -359,8 +359,8 @@ g:ada_with_gnat_project_files bool (true when exists)
*g:ada_default_compiler*
g:ada_default_compiler string
- set default compiler. Currently supported are 'gnat' and
- 'decada'.
+ set default compiler. Currently supported are "gnat" and
+ "decada".
An "exists" type is a boolean considered true when the variable is defined and
false when the variable is undefined. The value to which the variable is set
@@ -406,14 +406,14 @@ makes no difference.
g:gnat object
Control object which manages GNAT compiles. The object
is created when the first Ada source code is loaded provided
- that |g:ada_default_compiler| is set to 'gnat'. See
+ that |g:ada_default_compiler| is set to "gnat". See
|gnat_members| for details.
*g:decada*
g:decada object
Control object which manages Dec Ada compiles. The object
is created when the first Ada source code is loaded provided
- that |g:ada_default_compiler| is set to 'decada'. See
+ that |g:ada_default_compiler| is set to "decada". See
|decada_members| for details.
------------------------------------------------------------------------------
@@ -459,11 +459,11 @@ ada#List_Tag([{line}, {col}]) *ada#Listtags()*
ada#Jump_Tag ({ident}, {mode}) *ada#Jump_Tag()*
List all occurrences of the Ada entity under the cursor (or at
given line/column) in the tag jump list. Mode can either be
- 'tjump' or 'stjump'.
+ "tjump" or "stjump".
ada#Create_Tags ({option}) *ada#Create_Tags()*
- Creates tag file using Ctags. The option can either be 'file'
- for the current file, 'dir' for the directory of the current
+ Creates tag file using Ctags. The option can either be "file"
+ for the current file, "dir" for the directory of the current
file or a file name.
gnat#Insert_Tags_Header() *gnat#Insert_Tags_Header()*
@@ -486,25 +486,25 @@ You can optionally install the following extra plug-ins. They work well with
Ada and enhance the ability of the Ada mode:
backup.vim
- http://www.vim.org/scripts/script.php?script_id=1537
+ https://www.vim.org/scripts/script.php?script_id=1537
Keeps as many backups as you like so you don't have to.
-rainbow_parenthsis.vim
- http://www.vim.org/scripts/script.php?script_id=1561
+rainbow_parenthesis.vim
+ https://www.vim.org/scripts/script.php?script_id=1561
Very helpful since Ada uses only '(' and ')'.
nerd_comments.vim
- http://www.vim.org/scripts/script.php?script_id=1218
+ https://www.vim.org/scripts/script.php?script_id=1218
Excellent commenting and uncommenting support for almost any
programming language.
matchit.vim
- http://www.vim.org/scripts/script.php?script_id=39
+ https://www.vim.org/scripts/script.php?script_id=39
'%' jumping for any language. The normal '%' jump only works for '{}'
style languages. The Ada mode will set the needed search patterns.
taglist.vim
- http://www.vim.org/scripts/script.php?script_id=273
+ https://www.vim.org/scripts/script.php?script_id=273
Source code explorer sidebar. There is a patch for Ada available.
The GNU Ada Project distribution (http://gnuada.sourceforge.net) of Vim
diff --git a/runtime/doc/ft_rust.txt b/runtime/doc/ft_rust.txt
index 5c8782ec7a..a5d5b558dc 100644
--- a/runtime/doc/ft_rust.txt
+++ b/runtime/doc/ft_rust.txt
@@ -1,70 +1,70 @@
-*ft_rust.txt* For Vim version 8.1. Last change: 2017 Nov 02
+*ft_rust.txt* Nvim
This is documentation for the Rust filetype plugin.
==============================================================================
-CONTENTS *rust*
+CONTENTS *rust*
-1. Introduction |rust-intro|
-2. Settings |rust-settings|
-3. Commands |rust-commands|
-4. Mappings |rust-mappings|
+1. Introduction |rust-intro|
+2. Settings |rust-settings|
+3. Commands |rust-commands|
+4. Mappings |rust-mappings|
==============================================================================
-INTRODUCTION *rust-intro*
+INTRODUCTION *rust-intro*
This plugin provides syntax and supporting functionality for the Rust
filetype.
==============================================================================
-SETTINGS *rust-settings*
+SETTINGS *rust-settings*
This plugin has a few variables you can define in your vimrc that change the
behavior of the plugin.
- *g:rustc_path*
+ *g:rustc_path*
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"
<
- *g:rustc_makeprg_no_percent*
+ *g:rustc_makeprg_no_percent*
g:rustc_makeprg_no_percent~
Set this option to 1 to have 'makeprg' default to "rustc" instead of
"rustc %": >
let g:rustc_makeprg_no_percent = 1
<
- *g:rust_conceal*
+ *g:rust_conceal*
g:rust_conceal~
Set this option to turn on the basic |conceal| support: >
let g:rust_conceal = 1
<
- *g:rust_conceal_mod_path*
+ *g:rust_conceal_mod_path*
g:rust_conceal_mod_path~
Set this option to turn on |conceal| for the path connecting token
"::": >
let g:rust_conceal_mod_path = 1
<
- *g:rust_conceal_pub*
+ *g:rust_conceal_pub*
g:rust_conceal_pub~
Set this option to turn on |conceal| for the "pub" token: >
let g:rust_conceal_pub = 1
<
- *g:rust_recommended_style*
+ *g:rust_recommended_style*
g:rust_recommended_style~
- Set this option to enable vim indentation and textwidth settings to
- conform to style conventions of the rust standard library (i.e. use 4
- spaces for indents and sets 'textwidth' to 99). This option is enabled
+ Set this option to enable vim indentation and textwidth settings to
+ conform to style conventions of the rust standard library (i.e. use 4
+ spaces for indents and sets 'textwidth' to 99). This option is enabled
by default. To disable it: >
let g:rust_recommended_style = 0
<
- *g:rust_fold*
+ *g:rust_fold*
g:rust_fold~
Set this option to turn on |folding|: >
let g:rust_fold = 1
@@ -76,53 +76,53 @@ g:rust_fold~
2 Braced blocks are folded. 'foldlevel' is left at the
global value (all folds are closed by default).
- *g:rust_bang_comment_leader*
+ *g:rust_bang_comment_leader*
g:rust_bang_comment_leader~
Set this option to 1 to preserve the leader on multi-line doc comments
using the /*! syntax: >
let g:rust_bang_comment_leader = 1
<
- *g:ftplugin_rust_source_path*
+ *g:ftplugin_rust_source_path*
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'
<
- *g:rustfmt_command*
+ *g:rustfmt_command*
g:rustfmt_command~
- Set this option to the name of the 'rustfmt' executable in your $PATH. If
- not specified it defaults to 'rustfmt' : >
+ Set this option to the name of the "rustfmt" executable in your $PATH. If
+ not specified it defaults to "rustfmt" : >
let g:rustfmt_command = 'rustfmt'
<
- *g:rustfmt_autosave*
+ *g:rustfmt_autosave*
g:rustfmt_autosave~
Set this option to 1 to run |:RustFmt| automatically when saving a
buffer. If not specified it defaults to 0 : >
let g:rustfmt_autosave = 0
<
- *g:rustfmt_fail_silently*
+ *g:rustfmt_fail_silently*
g:rustfmt_fail_silently~
- Set this option to 1 to prevent 'rustfmt' from populating the
+ Set this option to 1 to prevent "rustfmt" from populating the
|location-list| with errors. If not specified it defaults to 0: >
let g:rustfmt_fail_silently = 0
<
- *g:rustfmt_options*
+ *g:rustfmt_options*
g:rustfmt_options~
- Set this option to a string of options to pass to 'rustfmt'. The
- write-mode is already set to 'overwrite'. If not specified it
+ Set this option to a string of options to pass to "rustfmt". The
+ write-mode is already set to "overwrite". If not specified it
defaults to '' : >
let g:rustfmt_options = ''
<
- *g:rust_playpen_url*
+ *g:rust_playpen_url*
g:rust_playpen_url~
Set this option to override the URL for the playpen to use: >
let g:rust_playpen_url = 'https://play.rust-lang.org/'
<
- *g:rust_shortener_url*
+ *g:rust_shortener_url*
g:rust_shortener_url~
Set this option to override the URL for the URL shortener: >
let g:rust_shortener_url = 'https://is.gd/'
@@ -130,9 +130,9 @@ g:rust_shortener_url~
==============================================================================
-COMMANDS *rust-commands*
+COMMANDS *rust-commands*
-:RustRun [args] *:RustRun*
+:RustRun [args] *:RustRun*
:RustRun! [rustc-args] [--] [args]
Compiles and runs the current file. If it has unsaved changes,
it will be saved first using |:update|. If the current file is
@@ -150,7 +150,7 @@ COMMANDS *rust-commands*
If |g:rustc_path| is defined, it is used as the path to rustc.
Otherwise it is assumed rustc can be found in $PATH.
-:RustExpand [args] *:RustExpand*
+:RustExpand [args] *:RustExpand*
:RustExpand! [TYPE] [args]
Expands the current file using --pretty and displays the
results in a new split. If the current file has unsaved
@@ -169,7 +169,7 @@ COMMANDS *rust-commands*
If |g:rustc_path| is defined, it is used as the path to rustc.
Otherwise it is assumed rustc can be found in $PATH.
-:RustEmitIr [args] *:RustEmitIr*
+:RustEmitIr [args] *:RustEmitIr*
Compiles the current file to LLVM IR and displays the results
in a new split. If the current file has unsaved changes, it
will be saved first using |:update|. If the current file is an
@@ -180,7 +180,7 @@ COMMANDS *rust-commands*
If |g:rustc_path| is defined, it is used as the path to rustc.
Otherwise it is assumed rustc can be found in $PATH.
-:RustEmitAsm [args] *:RustEmitAsm*
+:RustEmitAsm [args] *:RustEmitAsm*
Compiles the current file to assembly and displays the results
in a new split. If the current file has unsaved changes, it
will be saved first using |:update|. If the current file is an
@@ -191,7 +191,7 @@ COMMANDS *rust-commands*
If |g:rustc_path| is defined, it is used as the path to rustc.
Otherwise it is assumed rustc can be found in $PATH.
-:RustPlay *:RustPlay*
+:RustPlay *:RustPlay*
This command will only work if you have web-api.vim installed
(available at https://github.com/mattn/webapi-vim). It sends the
current selection, or if nothing is selected, the entirety of the
@@ -204,7 +204,7 @@ COMMANDS *rust-commands*
|g:rust_shortener_url| is the base URL for the shortener, by
default "https://is.gd/"
-:RustFmt *:RustFmt*
+:RustFmt *:RustFmt*
Runs |g:rustfmt_command| on the current buffer. If
|g:rustfmt_options| is set then those will be passed to the
executable.
@@ -214,12 +214,12 @@ COMMANDS *rust-commands*
|g:rustfmt_command|. If |g:rustfmt_fail_silently| is set to 1
then it will not populate the |location-list|.
-:RustFmtRange *:RustFmtRange*
+:RustFmtRange *:RustFmtRange*
Runs |g:rustfmt_command| with selected range. See
|:RustFmt| for any other information.
==============================================================================
-MAPPINGS *rust-mappings*
+MAPPINGS *rust-mappings*
This plugin defines mappings for |[[| and |]]| to support hanging indents.
diff --git a/runtime/doc/ft_sql.txt b/runtime/doc/ft_sql.txt
index 335faf266e..21a244e67a 100644
--- a/runtime/doc/ft_sql.txt
+++ b/runtime/doc/ft_sql.txt
@@ -37,9 +37,10 @@ The SQL ftplugin provides a number of options to assist with file
navigation.
+------------------------------------------------------------------------------
1.1 Matchit *sql-matchit*
------------
-The matchit plugin (http://www.vim.org/scripts/script.php?script_id=39)
+
+The matchit plugin (https://www.vim.org/scripts/script.php?script_id=39)
provides many additional features and can be customized for different
languages. The matchit plugin is configured by defining a local
buffer variable, b:match_words. Pressing the % key while on various
@@ -85,8 +86,9 @@ The following keywords are supported: >
returns
+------------------------------------------------------------------------------
1.2 Text Object Motions *sql-object-motions*
------------------------
+
Vim has a number of predefined keys for working with text |object-motions|.
This filetype plugin attempts to translate these keys to maps which make sense
for the SQL language.
@@ -99,8 +101,9 @@ file): >
[] move backwards to the previous 'end'
+------------------------------------------------------------------------------
1.3 Predefined Object Motions *sql-predefined-objects*
------------------------------
+
Most relational databases support various standard features, tables, indices,
triggers and stored procedures. Each vendor also has a variety of proprietary
objects. The next set of maps have been created to help move between these
@@ -166,8 +169,9 @@ with comments: >
+------------------------------------------------------------------------------
1.4 Macros *sql-macros*
-----------
+
Vim's feature to find macro definitions, |'define'|, is supported using this
regular expression: >
\c\<\(VARIABLE\|DECLARE\|IN\|OUT\|INOUT\)\>
@@ -230,8 +234,9 @@ The majority of people work with only one vendor's database product, it would
be nice to specify a default in your |init.vim|.
+------------------------------------------------------------------------------
2.1 SQLSetType *sqlsettype* *SQLSetType*
---------------
+
For the people that work with many different databases, it is nice to be
able to flip between the various vendors rules (indent, syntax) on a per
buffer basis, at any time. The ftplugin/sql.vim file defines this function: >
@@ -244,7 +249,7 @@ key to complete the optional parameter.
After typing the function name and a space, you can use the completion to
supply a parameter. The function takes the name of the Vim script you want to
source. Using the |cmdline-completion| feature, the SQLSetType function will
-search the |'runtimepath'| for all Vim scripts with a name containing 'sql'.
+search the |'runtimepath'| for all Vim scripts with a name containing "sql".
This takes the guess work out of the spelling of the names. The following are
examples: >
:SQLSetType
@@ -259,8 +264,9 @@ of available Vim script names: >
:SQL<Tab><space><Tab>
+------------------------------------------------------------------------------
2.2 SQLGetType *sqlgettype* *SQLGetType*
---------------
+
At anytime you can determine which SQL dialect you are using by calling the
SQLGetType command. The ftplugin/sql.vim file defines this function: >
SQLGetType
@@ -269,8 +275,9 @@ This will echo: >
Current SQL dialect in use:sqlanywhere
+------------------------------------------------------------------------------
2.3 SQL Dialect Default *sql-type-default*
------------------------
+
As mentioned earlier, the default syntax rules for Vim is based on Oracle
(PL/SQL). You can override this default by placing one of the following in
your |init.vim|: >
@@ -296,7 +303,7 @@ exist.
3. Adding new SQL Dialects *sql-adding-dialects*
If you begin working with a SQL dialect which does not have any customizations
-available with the default Vim distribution you can check http://www.vim.org
+available with the default Vim distribution you can check https://www.vim.org
to see if any customization currently exist. If not, you can begin by cloning
an existing script. Read |filetype-plugins| for more details.
@@ -325,8 +332,9 @@ highlight rules. The dynamic mode populates the popups with data retrieved
directly from a database. This includes, table lists, column lists,
procedures names and more.
+------------------------------------------------------------------------------
4.1 Static Mode *sql-completion-static*
----------------
+
The static popups created contain items defined by the active syntax rules
while editing a file with a filetype of SQL. The plugin defines (by default)
various maps to help the user refine the list of items to be displayed.
@@ -399,11 +407,12 @@ Here are some examples of the entries which are pulled from the syntax files: >
- Integer, Char, Varchar, Date, DateTime, Timestamp, ...
+------------------------------------------------------------------------------
4.2 Dynamic Mode *sql-completion-dynamic*
-----------------
+
Dynamic mode populates the popups with data directly from a database. In
order for the dynamic feature to be enabled you must have the dbext.vim
-plugin installed, (http://vim.sourceforge.net/script.php?script_id=356).
+plugin installed, (https://vim.sourceforge.net/script.php?script_id=356).
Dynamic mode is used by several features of the SQL completion plugin.
After installing the dbext plugin see the dbext-tutorial for additional
@@ -448,8 +457,8 @@ necessary to clear the plugins cache. The default map for this is: >
imap <buffer> <C-C>R <C-\><C-O>:call sqlcomplete#Map('ResetCache')<CR><C-X><C-O>
+------------------------------------------------------------------------------
4.3 SQL Tutorial *sql-completion-tutorial*
-----------------
This tutorial is designed to take you through the common features of the SQL
completion plugin so that: >
@@ -462,8 +471,8 @@ First, create a new buffer: >
:e tutorial.sql
-Static features
----------------
+Static features ~
+
To take you through the various lists, simply enter insert mode, hit:
<C-C>s (show SQL statements)
At this point, you can page down through the list until you find "select".
@@ -484,10 +493,10 @@ depending on the syntax file you are using. The SQL Anywhere syntax file
DECLARE customer_id <C-C>T <-- Choose a type from the list
-Dynamic features
-----------------
+Dynamic features ~
+
To take advantage of the dynamic features you must first install the
-dbext.vim plugin (http://vim.sourceforge.net/script.php?script_id=356). It
+dbext.vim plugin (https://vim.sourceforge.net/script.php?script_id=356). It
also comes with a tutorial. From the SQL completion plugin's perspective,
the main feature dbext provides is a connection to a database. dbext
connection profiles are the most efficient mechanism to define connection
@@ -597,8 +606,8 @@ Similar to the table list, <C-C>v, will display a list of views in the
database.
+------------------------------------------------------------------------------
4.4 Completion Customization *sql-completion-customization*
-----------------------------
The SQL completion plugin can be customized through various options set in
your |init.vim|: >
@@ -657,14 +666,14 @@ your |init.vim|: >
option.
>
+------------------------------------------------------------------------------
4.5 SQL Maps *sql-completion-maps*
-------------
The default SQL maps have been described in other sections of this document in
greater detail. Here is a list of the maps with a brief description of each.
-Static Maps
------------
+Static Maps ~
+
These are maps which use populate the completion list using Vim's syntax
highlighting rules. >
<C-C>a
@@ -680,8 +689,8 @@ highlighting rules. >
<C-C>s
< - Displays all SQL syntax items defined as 'sqlStatement'. >
-Dynamic Maps
-------------
+Dynamic Maps ~
+
These are maps which use populate the completion list using the dbext.vim
plugin. >
<C-C>t
@@ -713,8 +722,8 @@ plugin. >
< - This maps removes all cached items and forces the SQL completion
to regenerate the list of items.
-Customizing Maps
-----------------
+Customizing Maps ~
+
You can create as many additional key maps as you like. Generally, the maps
will be specifying different syntax highlight groups.
@@ -733,8 +742,8 @@ chosen since it will work on both Windows and *nix platforms. On the windows
platform you can also use <C-Space> or ALT keys.
+------------------------------------------------------------------------------
4.6 Using with other filetypes *sql-completion-filetypes*
-------------------------------
Many times SQL can be used with different filetypes. For example Perl, Java,
PHP, Javascript can all interact with a database. Often you need both the SQL
@@ -746,8 +755,8 @@ This can be enabled easily with the following steps (assuming a Perl file): >
2. :set filetype=sql
3. :set ft=perl
-Step 1
-------
+Step 1 ~
+
Begins by editing a Perl file. Vim automatically sets the filetype to
"perl". By default, Vim runs the appropriate filetype file
ftplugin/perl.vim. If you are using the syntax completion plugin by following
@@ -755,9 +764,9 @@ the directions at |ft-syntax-omni| then the |'omnifunc'| option has been set to
"syntax#Complete". Pressing <C-X><C-O> will display the omni popup containing
the syntax items for Perl.
-Step 2
-------
-Manually setting the filetype to 'sql' will also fire the appropriate filetype
+Step 2 ~
+
+Manually setting the filetype to "sql" will also fire the appropriate filetype
files ftplugin/sql.vim. This file will define a number of buffer specific
maps for SQL completion, see |sql-completion-maps|. Now these maps have
been created and the SQL completion plugin has been initialized. All SQL
@@ -767,8 +776,8 @@ begin with <C-C>, the maps will toggle the |'omnifunc'| when in use. So you
can use <C-X><C-O> to continue using the completion for Perl (using the syntax
completion plugin) and <C-C> to use the SQL completion features.
-Step 3
-------
+Step 3 ~
+
Setting the filetype back to Perl sets all the usual "perl" related items back
as they were.
diff --git a/runtime/doc/gui.txt b/runtime/doc/gui.txt
index 8f09e5225f..1fce9fa491 100644
--- a/runtime/doc/gui.txt
+++ b/runtime/doc/gui.txt
@@ -44,8 +44,8 @@ Scrollbars *gui-scrollbars*
There are vertical scrollbars and a horizontal scrollbar. You may
configure which ones appear with the 'guioptions' option.
-The interface looks like this (with ":set guioptions=mlrb"):
-
+The interface looks like this (with `:set guioptions=mlrb`):
+>
+------------------------------+ `
| File Edit Help | <- Menu bar (m) `
+-+--------------------------+-+ `
@@ -66,7 +66,7 @@ The interface looks like this (with ":set guioptions=mlrb"):
+-+--------------------------+-+ `
| |< #### >| | <- Bottom `
+-+--------------------------+-+ scrollbar (b) `
-
+<
Any of the scrollbar or menu components may be turned off by not putting the
appropriate letter in the 'guioptions' string. The bottom scrollbar is
only useful when 'nowrap' is set.
@@ -123,7 +123,7 @@ message). Keep Shift pressed to change to the directory instead.
If Vim happens to be editing a command line, the names of the dropped files
and directories will be inserted at the cursor. This allows you to use these
names with any Ex command. Special characters (space, tab, double quote and
-'|'; backslash on non-MS-Windows systems) will be escaped.
+"|"; backslash on non-MS-Windows systems) will be escaped.
==============================================================================
Menus *menus*
@@ -450,8 +450,8 @@ The default "PopUp" menu is: >
anoremenu PopUp.Paste "+gP
vnoremenu PopUp.Paste "+P
vnoremenu PopUp.Delete "_x
- nnoremenu PopUp.Select\ All> ggVG
- vnoremenu PopUp.Select\ All> gg0oG$
+ 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>
@@ -569,14 +569,14 @@ Tooltips & Menu tips
See section |42.4| in the user manual.
*:tmenu*
-:tm[enu] {menupath} {rhs} Define a tip for a menu or tool. {only in
- X11 and Win32 GUI}
+:tm[enu] {menupath} {rhs} Define a tip for a menu or tool. (only in
+ X11 and Win32 GUI)
-:tm[enu] [menupath] List menu tips. {only in X11 and Win32 GUI}
+:tm[enu] [menupath] List menu tips. (only in X11 and Win32 GUI)
*:tunmenu*
:tu[nmenu] {menupath} Remove a tip for a menu or tool.
- {only in X11 and Win32 GUI}
+ (only in X11 and Win32 GUI)
Note: To create menus for terminal mode, use |:tlmenu| instead.
diff --git a/runtime/doc/hebrew.txt b/runtime/doc/hebrew.txt
index 2f4b137bd3..76266d777f 100644
--- a/runtime/doc/hebrew.txt
+++ b/runtime/doc/hebrew.txt
@@ -11,8 +11,9 @@ Lottem. <alottem at gmail dot com> Ron Aaron <ron at ronware dot org> is
currently helping support these features.
+------------------------------------------------------------------------------
Introduction
-------------
+
Hebrew-specific options are 'hkmap', 'hkmapp' 'keymap'=hebrew and 'aleph'.
Hebrew-useful options are 'delcombine', 'allowrevins', 'revins', 'rightleft'
and 'rightleftcmd'.
@@ -22,8 +23,9 @@ from right to left instead of the usual left to right. This is useful
primarily when editing Hebrew or other Middle-Eastern languages.
See |rileft.txt| for further details.
+------------------------------------------------------------------------------
Details
---------------
+
+ Options:
+ 'rightleft' ('rl') sets window orientation to right-to-left. This means
that the logical text 'ABC' will be displayed as 'CBA', and will start
@@ -31,7 +33,7 @@ Details
+ 'hkmap' ('hk') sets keyboard mapping to Hebrew, in insert/replace modes.
+ 'aleph' ('al'), numeric, holds the decimal code of Aleph, for keyboard
mapping.
- + 'hkmapp' ('hkp') sets keyboard mapping to 'phonetic hebrew'
+ + 'hkmapp' ('hkp') sets keyboard mapping to "phonetic hebrew"
NOTE: these three ('hkmap', 'hkmapp' and 'aleph') are obsolete. You should
use ":set keymap=hebrewp" instead.
@@ -51,7 +53,7 @@ Details
('deco' does nothing if UTF8 encoding is not active).
+ Vim arguments:
- + 'vim -H file' starts editing a Hebrew file, i.e. 'rightleft' and 'hkmap'
+ + `vim -H file` starts editing a Hebrew file, i.e. 'rightleft' and 'hkmap'
are set.
+ Keyboard:
@@ -116,8 +118,9 @@ when exiting 'revins' via CTRL-_, the cursor moves to the end of the typed
text (if possible).
+------------------------------------------------------------------------------
Pasting when in a rightleft window
-----------------------------------
+
When cutting text with the mouse and pasting it in a rightleft window
the text will be reversed, because the characters come from the cut buffer
from the left to the right, while inserted in the file from the right to
@@ -125,8 +128,9 @@ the left. In order to avoid it, toggle 'revins' (by typing CTRL-? or CTRL-_)
before pasting.
+------------------------------------------------------------------------------
Hebrew characters and the 'isprint' variable
---------------------------------------------
+
Sometimes Hebrew character codes are in the non-printable range defined by
the 'isprint' variable. For example in the Linux console, the Hebrew font
encoding starts from 128, while the default 'isprint' variable is @,161-255.
diff --git a/runtime/doc/help.txt b/runtime/doc/help.txt
index 04e31e0680..07f898f99c 100644
--- a/runtime/doc/help.txt
+++ b/runtime/doc/help.txt
@@ -1,6 +1,6 @@
*help.txt* Nvim
- VIM - main help file
+ NVIM - help
k
Move around: Use the cursor keys, or "h" to go left, h l
"j" to go down, "k" to go up, "l" to go right. j
@@ -37,169 +37,149 @@ Get specific help: It is possible to go directly to whatever you want help
Vim stands for Vi IMproved. Most of Vim was made by Bram Moolenaar, but only
through the help of many others. See |credits|.
+
+==============================================================================
+NVIM DOCUMENTATION
+
+------------------------------------------------------------------------------
+ABOUT NVIM *reference_toc* *doc-file-list* *Q_ct*
+
+|news| News since the previous release
+|nvim| Transitioning from Vim
+|vim-differences| Nvim compared to Vim
+|user-manual| User manual: How to accomplish editing tasks.
+|quickref| Overview of common commands
+|tutor| 30-minute interactive course for beginners
+|copying| About copyrights
+|iccf| Helping poor children in Uganda
+|sponsor| Sponsor Vim development, become a registered Vim user
+|www| Vim on the World Wide Web
+|bugs| Where to send bug reports
+|support| Supported platforms
+
+------------------------------------------------------------------------------
+GENERAL
+
+|intro| Introduction to Vim; notation used in help files
+|helphelp| Using the :help files
+|index| Index of all commands
+|tips| Various tips on using Vim
+|message.txt| (Error) messages and explanations
+|uganda.txt| Vim distribution and what to do with your money
+
+------------------------------------------------------------------------------
+BASIC EDITING
+
+|starting| Starting Vim, Vim command arguments, initialisation
+|edit-files| Editing and writing files
+|motion.txt| Commands for moving around
+|scrolling| Scrolling the text in the window
+|insert.txt| Insert and Replace mode
+|change.txt| Deleting and replacing text
+|undo-redo| Undo and Redo
+|repeat.txt| Repeating commands, Vim scripts and debugging
+|visual-mode| Using Visual mode (selecting text)
+|various| Various other commands
+|crash-recovery| Recovering from a crash
+
+------------------------------------------------------------------------------
+ADVANCED EDITING
+
+|cmdline| Command-line editing
+|options| Description of all options
+|pattern-searches| Vim regexp patterns and search commands
+|key-mapping| Key mapping (shortcuts), abbreviations
+|tags| Tags and special searches
+|windows| Commands for using windows and buffers
+|tabpage| Commands for using tabpages
+|spell| Spell checking
+|diff| Comparing files
+|folding| Hide (fold) ranges of lines
+|terminal| Embedded terminal emulator
+
+------------------------------------------------------------------------------
+API (EXTENSIBILITY/SCRIPTING/PLUGINS)
+
+|api| Nvim API via RPC, Lua and VimL
+|ui| Nvim UI protocol
+|lua-guide| Nvim Lua guide
+|lua| Lua API
+|luaref| Lua reference manual
+|luvref| Luv (|vim.loop|) reference manual
+|autocmd| Event handlers
+|job-control| Spawn and control multiple processes
+|channel| Nvim asynchronous IO
+|vimscript| Vimscript reference
+|vimscript-functions| Vimscript functions
+|testing.txt| Vimscript testing functions
+|remote-plugin| Nvim remote plugins
+
+------------------------------------------------------------------------------
+PROGRAMMING LANGUAGE SUPPORT
+
+|lsp| Language Server Protocol (LSP)
+|diagnostic-api| Diagnostic framework
+|treesitter| Incremental syntax parsing
+|indent.txt| automatic indenting for C and other languages
+|syntax| syntax highlighting
+|filetype| Settings for specific types of files
+|quickfix| Commands for a quick edit-compile-fix cycle
+|ft_ada.txt| Ada filetype plugin
+|ft_ps1.txt| PowerShell filetype plugin
+|ft_raku.txt| Raku filetype plugin
+|ft_rust.txt| Rust filetype plugin
+|ft_sql.txt| SQL filetype plugin
+
+------------------------------------------------------------------------------
+UI
+
+|tui| Builtin UI
+|gui| External (graphical) UIs
+|signs| Signs displayed as window decorations (the "gutter")
+
+------------------------------------------------------------------------------
+LANGUAGE SUPPORT
+
+|digraph| List of available digraphs
+|mbyte.txt| Multibyte text support
+|mlang.txt| Non-English language support
+|rileft.txt| Right-to-left editing mode
+|arabic.txt| Arabic language support and editing
+|hebrew.txt| Hebrew language support and editing
+|russian.txt| Russian language support and editing
+
------------------------------------------------------------------------------
- *doc-file-list* *Q_ct*
-BASIC:
-|quickref| Overview of the most common commands you will use
-|tutor| 30-minute interactive course for beginners
-|copying| About copyrights
-|iccf| Helping poor children in Uganda
-|sponsor| Sponsor Vim development, become a registered Vim user
-|www| Vim on the World Wide Web
-|bugs| Where to send bug reports
-
-USER MANUAL: These files explain how to accomplish an editing task.
-
-|usr_toc.txt| Table Of Contents
-
-Getting Started ~
-|usr_01.txt| About the manuals
-|usr_02.txt| The first steps in Vim
-|usr_03.txt| Moving around
-|usr_04.txt| Making small changes
-|usr_05.txt| Set your settings
-|usr_06.txt| Using syntax highlighting
-|usr_07.txt| Editing more than one file
-|usr_08.txt| Splitting windows
-|usr_09.txt| Using the GUI
-|usr_10.txt| Making big changes
-|usr_11.txt| Recovering from a crash
-|usr_12.txt| Clever tricks
-
-Editing Effectively ~
-|usr_20.txt| Typing command-line commands quickly
-|usr_21.txt| Go away and come back
-|usr_22.txt| Finding the file to edit
-|usr_23.txt| Editing other files
-|usr_24.txt| Inserting quickly
-|usr_25.txt| Editing formatted text
-|usr_26.txt| Repeating
-|usr_27.txt| Search commands and patterns
-|usr_28.txt| Folding
-|usr_29.txt| Moving through programs
-|usr_30.txt| Editing programs
-|usr_31.txt| Exploiting the GUI
-|usr_32.txt| The undo tree
-
-Tuning Vim ~
-|usr_40.txt| Make new commands
-|usr_41.txt| Write a Vim script
-|usr_42.txt| Add new menus
-|usr_43.txt| Using filetypes
-|usr_44.txt| Your own syntax highlighted
-|usr_45.txt| Select your language
-
-
-REFERENCE MANUAL: These files explain every detail of Vim. *reference_toc*
-
-General subjects ~
-|intro.txt| general introduction to Vim; notation used in help files
-|nvim.txt| Transitioning from Vim
-|help.txt| overview and quick reference (this file)
-|helphelp.txt| about using the help files
-|index.txt| alphabetical index of all commands
-|help-tags| all the tags you can jump to (index of tags)
-|tips.txt| various tips on using Vim
-|message.txt| (error) messages and explanations
-|develop.txt| development of Nvim
-|debug.txt| debugging Vim itself
-|uganda.txt| Vim distribution conditions and what to do with your money
-
-Basic editing ~
-|starting.txt| starting Vim, Vim command arguments, initialisation
-|editing.txt| editing and writing files
-|motion.txt| commands for moving around
-|scroll.txt| scrolling the text in the window
-|insert.txt| Insert and Replace mode
-|change.txt| deleting and replacing text
-|undo.txt| Undo and Redo
-|repeat.txt| repeating commands, Vim scripts and debugging
-|visual.txt| using the Visual mode (selecting a text area)
-|various.txt| various remaining commands
-|recover.txt| recovering from a crash
-
-Advanced editing ~
-|cmdline.txt| Command-line editing
-|options.txt| description of all options
-|pattern.txt| regexp patterns and search commands
-|map.txt| key mapping and abbreviations
-|tagsrch.txt| tags and special searches
-|windows.txt| commands for using multiple windows and buffers
-|tabpage.txt| commands for using multiple tab pages
-|spell.txt| spell checking
-|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
-
-Special issues ~
-|testing.txt| testing Vim and Vim scripts
-|print.txt| printing
-|remote_plugin.txt| Nvim support for remote plugins
-
-Programming language support ~
-|indent.txt| automatic indenting for C and other languages
-|lsp.txt| Language Server Protocol (LSP)
-|treesitter.txt| tree-sitter library for incremental parsing of buffers
-|diagnostic.txt| Diagnostic framework
-|syntax.txt| syntax highlighting
-|filetype.txt| settings done specifically for a type of file
-|quickfix.txt| commands for a quick edit-compile-fix cycle
-|provider.txt| Built-in remote plugin hosts
-|ft_ada.txt| Ada (the programming language) support
-|ft_ps1.txt| Filetype plugin for Windows PowerShell
-|ft_raku.txt| Filetype plugin for Raku
-|ft_rust.txt| Filetype plugin for Rust
-|ft_sql.txt| about the SQL filetype plugin
-
-Language support ~
-|digraph.txt| list of available digraphs
-|mbyte.txt| multibyte text support
-|mlang.txt| non-English language support
-|rileft.txt| right-to-left editing mode
-|arabic.txt| Arabic language support and editing
-|hebrew.txt| Hebrew language support and editing
-|russian.txt| Russian language support and editing
-
-GUI ~
-|gui.txt| Graphical User Interface (GUI)
-
-Interfaces ~
-|if_cscop.txt| using Cscope with Vim
-|if_perl.txt| Perl interface
-|if_pyth.txt| Python interface
-|if_ruby.txt| Ruby interface
-|sign.txt| debugging signs
-
-Versions ~
-|vim_diff.txt| Main differences between Nvim and Vim
-|vi_diff.txt| Main differences between Vim and Vi
-|deprecated.txt| Deprecated items that have been or will be removed
-
-Other ~
-|terminal_emulator.txt| Terminal buffers
-|term.txt| Terminal UI
-|ui.txt| Nvim UI protocol
-|channel.txt| Nvim asynchronous IO
-|dev_style.txt| Nvim style guide
-|job_control.txt| Spawn and control multiple processes
-|luaref.txt| Lua reference manual
-|luvref.txt| Luv (|vim.loop|) reference manual
+INTEROP
+
+|provider| Builtin remote plugin hosts
+|if_perl| Perl interface
+|if_pyth| Python interface
+|if_ruby| Ruby interface
+
+------------------------------------------------------------------------------
+VERSIONS
+
+|deprecated| Deprecated features that will be removed
+|vi-differences| Differences between Vim and Vi
+
+------------------------------------------------------------------------------
+DEVELOPING NVIM
+
+|dev| Development of Nvim
+|dev-style| Development style guidelines
+|debug.txt| Debugging Vim itself
*standard-plugin-list*
Standard plugins ~
-|matchit.txt| Extended |%| matching
-|pi_gzip.txt| Reading and writing compressed files
-|pi_health.txt| Healthcheck framework
-|pi_msgpack.txt| msgpack utilities
-|pi_netrw.txt| Reading and writing files over a network
-|pi_paren.txt| Highlight matching parens
-|pi_spec.txt| Filetype plugin to work with rpm spec files
-|pi_tar.txt| Tar file explorer
-|pi_zip.txt| Zip archive explorer
+|matchit.txt| Extended |%| matching
+|pi_gzip.txt| Reading and writing compressed files
+|pi_health.txt| Healthcheck framework
+|pi_msgpack.txt| msgpack utilities
+|pi_netrw.txt| Reading and writing files over a network
+|pi_paren.txt| Highlight matching parens
+|pi_spec.txt| Filetype plugin to work with rpm spec files
+|pi_tar.txt| Tar file explorer
+|pi_zip.txt| Zip archive explorer
LOCAL ADDITIONS: *local-additions*
@@ -212,8 +192,8 @@ CTRL-T, CTRL-O, g<RightMouse>, or <C-RightMouse> to go back to where you were.
Note that tags are within | characters, but when highlighting is enabled these
characters are hidden. That makes it easier to read a command.
-Anyway, you can use CTRL-] on any word, also when it is not within |, and Vim
-will try to find help for it. Especially for options in single quotes, e.g.
+You can use CTRL-] on any word (even if it is not within "|") and Nvim will
+try to find help for it. Especially for options in single quotes, e.g.
'hlsearch'.
------------------------------------------------------------------------------
diff --git a/runtime/doc/helphelp.txt b/runtime/doc/helphelp.txt
index 569995d319..da307dd241 100644
--- a/runtime/doc/helphelp.txt
+++ b/runtime/doc/helphelp.txt
@@ -212,12 +212,6 @@ This is done when viewing the file in Vim, the file itself is not changed. It
is done by going through all help files and obtaining the first line of each
file. The files in $VIMRUNTIME/doc are skipped.
- *help-xterm-window*
-If you want to have the help in another xterm window, you could use this
-command: >
- :!xterm -e vim +help &
-<
-
*:helpt* *:helptags*
*E150* *E151* *E152* *E153* *E154* *E670* *E856*
:helpt[ags] [++t] {dir}
@@ -257,7 +251,7 @@ At this moment translations are available for:
Japanese - multiple authors
Polish - translated by Mikolaj Machowski
Russian - translated by Vassily Ragosin
-See the Vim website to find them: http://www.vim.org/translations.php
+See the Vim website to find them: https://www.vim.org/translations.php
A set of translated help files consists of these files:
@@ -348,7 +342,7 @@ should begin with the name of the Vim plugin. The tag name is usually right
aligned on a line.
When referring to an existing help tag and to create a hot-link, place the
-name between two bars (|) eg. |help-writing|.
+name between two bars ("|") eg. |help-writing|.
When referring to a Vim command and to create a hot-link, place the
name between two backticks, eg. inside `:filetype`. You will see this is
@@ -372,6 +366,7 @@ To separate sections in a help file, place a series of '=' characters in a
line starting from the first column. The section separator line is highlighted
differently.
+ *help-codeblock*
To quote a block of ex-commands verbatim, place a greater than (>) character
at the end of the line before the block and a less than (<) character as the
first non-blank on a line following the block. Any line starting in column 1
diff --git a/runtime/doc/if_cscop.txt b/runtime/doc/if_cscop.txt
deleted file mode 100644
index 8947aefc1b..0000000000
--- a/runtime/doc/if_cscop.txt
+++ /dev/null
@@ -1,374 +0,0 @@
-*if_cscop.txt* Nvim
-
-
- VIM REFERENCE MANUAL by Andy Kahn
-
- *cscope* *Cscope*
-Cscope is a "code intelligence" tool that helps you navigate C programs. It
-can also perform some refactoring tasks, such as renaming a global variable in
-all source files. Think of it as "ctags on steroids".
-
-See |cscope-usage| for a quickstart.
-
- Type |gO| to see the table of contents.
-
-==============================================================================
-Cscope introduction *cscope-intro*
-
-
-Cscope is designed to answer questions like:
- Where is this symbol used?
- Where is it defined?
- Where did this variable get its value?
- What is this global symbol's definition?
- Where is this function in the source files?
- What functions call this function?
- What functions are called by this function?
- Where does the message "out of space" come from?
- Where is this source file in the directory structure?
- What files include this header file?
-
-Cscope answers these questions from a symbol database that it builds the first
-time it is used on the source files. On a subsequent call, cscope rebuilds
-the database only if a source file has changed or the list of source files is
-different. When the database is rebuilt the data for the unchanged files is
-copied from the old database, which makes rebuilding much faster than the
-initial build.
-
-See |cscope-usage| to get started.
-
-==============================================================================
-Cscope commands *cscope-commands*
-
- *:cscope* *:cs* *:scs* *:scscope* *E259* *E262* *E560* *E561*
-All cscope commands are accessed through suboptions to the cscope commands.
- `:cscope` or `:cs` is the main command
- `:scscope` or `:scs` does the same and splits the window
- `:lcscope` or `:lcs` uses the location list, see |:lcscope|
-
-The available subcommands are:
-
- *E563* *E564* *E566* *E568* *E622* *E623* *E625*
- *E626* *E609*
- add : Add a new cscope database/connection.
-
- USAGE :cs add {file|dir} [pre-path] [flags]
-
- [pre-path] is the pathname used with the -P command to cscope.
-
- [flags] are any additional flags you want to pass to cscope.
-
- EXAMPLES >
- :cscope add /usr/local/cdb/cscope.out
- :cscope add /projects/vim/cscope.out /usr/local/vim
- :cscope add cscope.out /usr/local/vim -C
-<
- *cscope-find* *cs-find* *E567*
- find : Query cscope. All cscope query options are available
- except option #5 ("Change this grep pattern").
-
- USAGE :cs find {querytype} {name}
-
- {querytype} corresponds to the actual cscope line
- interface numbers as well as default nvi commands:
-
- 0 or s: Find this C symbol
- 1 or g: Find this definition
- 2 or d: Find functions called by this function
- 3 or c: Find functions calling this function
- 4 or t: Find this text string
- 6 or e: Find this egrep pattern
- 7 or f: Find this file
- 8 or i: Find files #including this file
- 9 or a: Find places where this symbol is assigned a value
-
- For all types, except 4 and 6, leading white space for {name} is
- removed. For 4 and 6 there is exactly one space between {querytype}
- and {name}. Further white space is included in {name}.
-
- EXAMPLES >
- :cscope find c vim_free
- :cscope find 3 vim_free
-<
- These two examples perform the same query: functions calling
- "vim_free". >
-
- :cscope find t initOnce
- :cscope find t initOnce
-<
- The first one searches for the text "initOnce", the second one for
- " initOnce". >
-
- :cscope find 0 DEFAULT_TERM
-<
- Executing this example on the source code for Vim 5.1 produces the
- following output:
-
- Cscope tag: DEFAULT_TERM
- # line filename / context / line
- 1 1009 vim-5.1-gtk/src/term.c <<GLOBAL>>
- #define DEFAULT_TERM (char_u *)"amiga"
- 2 1013 vim-5.1-gtk/src/term.c <<GLOBAL>>
- #define DEFAULT_TERM (char_u *)"win32"
- 3 1017 vim-5.1-gtk/src/term.c <<GLOBAL>>
- #define DEFAULT_TERM (char_u *)"pcterm"
- 4 1021 vim-5.1-gtk/src/term.c <<GLOBAL>>
- #define DEFAULT_TERM (char_u *)"ansi"
- 5 1025 vim-5.1-gtk/src/term.c <<GLOBAL>>
- #define DEFAULT_TERM (char_u *)"vt52"
- 6 1029 vim-5.1-gtk/src/term.c <<GLOBAL>>
- #define DEFAULT_TERM (char_u *)"os2ansi"
- 7 1033 vim-5.1-gtk/src/term.c <<GLOBAL>>
- #define DEFAULT_TERM (char_u *)"ansi"
- 8 1037 vim-5.1-gtk/src/term.c <<GLOBAL>>
- # undef DEFAULT_TERM
- 9 1038 vim-5.1-gtk/src/term.c <<GLOBAL>>
- #define DEFAULT_TERM (char_u *)"beos-ansi"
- 10 1042 vim-5.1-gtk/src/term.c <<GLOBAL>>
- #define DEFAULT_TERM (char_u *)"mac-ansi"
- 11 1335 vim-5.1-gtk/src/term.c <<set_termname>>
- term = DEFAULT_TERM;
- 12 1459 vim-5.1-gtk/src/term.c <<set_termname>>
- if (STRCMP(term, DEFAULT_TERM))
- 13 1826 vim-5.1-gtk/src/term.c <<termcapinit>>
- term = DEFAULT_TERM;
- 14 1833 vim-5.1-gtk/src/term.c <<termcapinit>>
- term = DEFAULT_TERM;
- 15 3635 vim-5.1-gtk/src/term.c <<update_tcap>>
- p = find_builtin_term(DEFAULT_TERM);
- Enter nr of choice (<CR> to abort):
-
- The output shows several pieces of information:
- 1. The tag number (there are 15 in this example).
- 2. The line number where the tag occurs.
- 3. The filename where the tag occurs.
- 4. The context of the tag (e.g., global, or the function name).
- 5. The line from the file itself.
-
- help : Show a brief synopsis.
-
- USAGE :cs help
-
- *E261*
- kill : Kill a cscope connection (or kill all cscope connections).
-
- USAGE :cs kill {num|partial_name}
-
- To kill a cscope connection, the connection number or a partial
- name must be specified. The partial name is simply any part of
- the pathname of the cscope database. Kill a cscope connection
- using the partial name with caution!
-
- If the specified connection number is -1, then _ALL_ cscope
- connections will be killed.
-
- reset : Reinit all cscope connections.
-
- USAGE :cs reset
-
- show : Show cscope connections.
-
- USAGE :cs show
-
- *:lcscope* *:lcs*
-This command is same as the ":cscope" command, except when the
-'cscopequickfix' option is set, the location list for the current window is
-used instead of the quickfix list to show the cscope results.
-
- *:cstag* *E257* *E562*
-If you use cscope as well as ctags, |:cstag| allows you to search one or
-the other before making a jump. For example, you can choose to first
-search your cscope database(s) for a match, and if one is not found, then
-your tags file(s) will be searched. The order in which this happens
-is determined by the value of |csto|. See |cscope-options| for more
-details.
-
-|:cstag| performs the equivalent of ":cs find g" on the identifier when
-searching through the cscope database(s).
-
-|:cstag| performs the equivalent of |:tjump| on the identifier when searching
-through your tags file(s).
-
-
-==============================================================================
-Cscope options *cscope-options*
-
-Use the |:set| command to set all cscope options. Ideally, you would do
-this in one of your startup files (e.g., vimrc). Some cscope related
-variables are only valid within |init.vim|. Setting them after vim has
-started will have no effect!
-
- *cscopeprg* *csprg*
-'cscopeprg' specifies the command to execute cscope. The default is
-"cscope". For example: >
- :set csprg=/usr/local/bin/cscope
-<
- *cscopequickfix* *csqf* *E469*
-'cscopequickfix' specifies whether to use quickfix window to show cscope
-results. This is a list of comma-separated values. Each item consists of
-|cscope-find| command (s, g, d, c, t, e, f, i or a) and flag (+, - or 0).
-'+' indicates that results must be appended to quickfix window,
-'-' implies previous results clearance, '0' or command absence - don't use
-quickfix. Search is performed from start until first command occurrence.
-The default value is "" (don't use quickfix anyway). The following value
-seems to be useful: >
- :set cscopequickfix=s-,c-,d-,i-,t-,e-,a-
-<
- *cscopetag* *cst*
-If 'cscopetag' is set, the commands ":tag" and CTRL-] as well as "vim -t"
-will always use |:cstag| instead of the default :tag behavior. Effectively,
-by setting 'cst', you will always search your cscope databases as well as
-your tag files. The default is off.
-
- *cscoperelative* *csre*
-If 'cscoperelative' is set, then in absence of a prefix given to cscope
-(prefix is the argument of -P option of cscope), basename of cscope.out
-location (usually the project root directory) will be used as the prefix
-to construct an absolute path. The default is off. Note: This option is
-only effective when cscope (cscopeprg) is initialized without a prefix
-path (-P).
-
- *cscopetagorder* *csto*
-The value of 'csto' determines the order in which |:cstag| performs a search.
-If 'csto' is set to zero, cscope database(s) are searched first, followed
-by tag file(s) if cscope did not return any matches. If 'csto' is set to
-one, tag file(s) are searched before cscope database(s). The default is zero.
-
- *cscopepathcomp* *cspc*
-'cscopepathcomp' determines how many components of a file's path to display.
-With the default value of zero the entire path will be displayed.
-The value one will display only the filename with no path. Other values
-display that many components. For example: >
- :set cscopepathcomp=3
-will display the last 3 components of the file's path, including the file
-name itself.
-
-==============================================================================
-Using cscope in Nvim *cscope-usage* *cscope-howtouse*
-
-To get started, build the cscope database in your project root directory: >
- cscope -bcqR
-
-See the cscope manpage for details: >
- :Man cscope
-
-By default the cscope database file is named "cscope.out". After building the
-database, connect to it from Nvim: >
- :cscope add cscope.out
-
-That establishes a cscope connection for Nvim to use. You can check the
-result with ":cs show". It will show something like:
-
- # pid database name prepend path
- 0 28806 cscope.out <none>
-
-Once a cscope connection is established, you can make queries to cscope and
-the results will be printed. Queries are made using the command ":cs find".
-For example: >
- :cs find g ALIGN_SIZE
-
-To make this easier you can configure mappings, see |cscope-suggestions|.
-
-If the results return only one match, you will automatically be taken to it.
-If there is more than one match, you will be given a selection screen to pick
-the match you want to go to. After you have jumped to the new location,
-simply hit Ctrl-T to get back to the previous one.
-
-
-==============================================================================
-Limitations *cscope-limitations*
-
-Hard-coded limitation: doing a |:tjump| when |:cstag| searches the tag files
-is not configurable (e.g., you can't do a tselect instead).
-
-
-==============================================================================
-Sample config *cscope-suggestions*
-
-Copy this into your init.vim (adjust paths for your system): >
-
- if has("cscope")
- set csprg=/usr/local/bin/cscope
- set csto=0
- set cst
- " add any database in current directory
- if filereadable("cscope.out")
- silent cs add cscope.out
- " else add database pointed to by environment
- elseif $CSCOPE_DB != ""
- silent cs add $CSCOPE_DB
- endif
- endif
-
-By setting 'cscopetag', we have effectively replaced all instances of the :tag
-command with :cstag. This includes :tag, Ctrl-], and "vim -t". In doing
-this, the regular tag command not only searches your ctags generated tag
-files, but your cscope databases as well.
-
-Some users may want to keep the regular tag behavior and have a different
-shortcut to access :cstag. For example, one could map Ctrl-_ (underscore)
-to :cstag with the following command: >
-
- map <C-_> :cstag <C-R>=expand("<cword>")<CR><CR>
-
-A couple of very commonly used cscope queries (using ":cs find") is to
-find all functions calling a certain function and to find all occurrences
-of a particular C symbol. To do this, you can use these mappings as an
-example: >
-
- map g<C-]> :cs find 3 <C-R>=expand("<cword>")<CR><CR>
- map g<C-\> :cs find 0 <C-R>=expand("<cword>")<CR><CR>
-
-These mappings for Ctrl-] (right bracket) and Ctrl-\ (backslash) allow you to
-place your cursor over the function name or C symbol and quickly query cscope
-for any matches.
-
-Or you may use the following scheme, inspired by Vim/Cscope tutorial from
-Cscope Home Page (http://cscope.sourceforge.net/): >
-
- nmap <C-_>s :cs find s <C-R>=expand("<cword>")<CR><CR>
- nmap <C-_>g :cs find g <C-R>=expand("<cword>")<CR><CR>
- nmap <C-_>c :cs find c <C-R>=expand("<cword>")<CR><CR>
- nmap <C-_>t :cs find t <C-R>=expand("<cword>")<CR><CR>
- nmap <C-_>e :cs find e <C-R>=expand("<cword>")<CR><CR>
- nmap <C-_>f :cs find f <C-R>=expand("<cfile>")<CR><CR>
- nmap <C-_>i :cs find i ^<C-R>=expand("<cfile>")<CR>$<CR>
- nmap <C-_>d :cs find d <C-R>=expand("<cword>")<CR><CR>
- nmap <C-_>a :cs find a <C-R>=expand("<cword>")<CR><CR>
-
- " Using 'CTRL-spacebar' then a search type makes the vim window
- " split horizontally, with search result displayed in
- " the new window.
-
- nmap <C-Space>s :scs find s <C-R>=expand("<cword>")<CR><CR>
- nmap <C-Space>g :scs find g <C-R>=expand("<cword>")<CR><CR>
- nmap <C-Space>c :scs find c <C-R>=expand("<cword>")<CR><CR>
- nmap <C-Space>t :scs find t <C-R>=expand("<cword>")<CR><CR>
- nmap <C-Space>e :scs find e <C-R>=expand("<cword>")<CR><CR>
- nmap <C-Space>f :scs find f <C-R>=expand("<cfile>")<CR><CR>
- nmap <C-Space>i :scs find i ^<C-R>=expand("<cfile>")<CR>$<CR>
- nmap <C-Space>d :scs find d <C-R>=expand("<cword>")<CR><CR>
- nmap <C-Space>a :scs find a <C-R>=expand("<cword>")<CR><CR>
-
- " Hitting CTRL-space *twice* before the search type does a vertical
- " split instead of a horizontal one
-
- nmap <C-Space><C-Space>s
- \:vert scs find s <C-R>=expand("<cword>")<CR><CR>
- nmap <C-Space><C-Space>g
- \:vert scs find g <C-R>=expand("<cword>")<CR><CR>
- nmap <C-Space><C-Space>c
- \:vert scs find c <C-R>=expand("<cword>")<CR><CR>
- nmap <C-Space><C-Space>t
- \:vert scs find t <C-R>=expand("<cword>")<CR><CR>
- nmap <C-Space><C-Space>e
- \:vert scs find e <C-R>=expand("<cword>")<CR><CR>
- nmap <C-Space><C-Space>i
- \:vert scs find i ^<C-R>=expand("<cfile>")<CR>$<CR>
- nmap <C-Space><C-Space>d
- \:vert scs find d <C-R>=expand("<cword>")<CR><CR>
- nmap <C-Space><C-Space>a
- \:vert scs find a <C-R>=expand("<cword>")<CR><CR>
-<
-
- vim:tw=78:ts=8:noet:ft=help:norl:
diff --git a/runtime/doc/if_pyth.txt b/runtime/doc/if_pyth.txt
index 9b434e61d7..4c184ddf94 100644
--- a/runtime/doc/if_pyth.txt
+++ b/runtime/doc/if_pyth.txt
@@ -16,7 +16,7 @@ Commands *python-commands*
*:python* *:py* *E263* *E264* *E887*
:[range]py[thon] {stmt}
Execute Python statement {stmt}. A simple check if
- the `:python` command is working: >
+ the `:python` command is working: >vim
:python print "Hello"
:[range]py[thon] << [endmarker]
@@ -31,7 +31,7 @@ The {endmarker} below the {script} must NOT be preceded by any white space.
If [endmarker] is omitted from after the "<<", a dot '.' must be used after
{script}, like for the |:append| and |:insert| commands.
-Example: >
+Example: >vim
function! IcecreamInitialize()
python << EOF
class StrawberryIcecream:
@@ -40,7 +40,7 @@ Example: >
EOF
endfunction
-To see what version of Python you have: >
+To see what version of Python you have: >vim
:python print(sys.version)
There is no need to "import sys", it's done by default.
@@ -64,12 +64,12 @@ Note: Python is very sensitive to indenting. Make sure the "class" line and
is the whole file: "1,$".
Examples:
->
+>vim
:pydo return "%s\t%d" % (line[::-1], len(line))
:pydo if line: return "%4d: %s" % (linenr, line)
<
One can use `:pydo` in possible conjunction with `:py` to filter a range using
-python. For example: >
+python. For example: >vim
:py3 << EOF
needle = vim.eval('@a')
@@ -94,12 +94,13 @@ In the case of :pyfile, the code to execute is the contents of the given file.
Python commands cannot be used in the |sandbox|.
-To pass arguments you need to set sys.argv[] explicitly. Example: >
+To pass arguments you need to set sys.argv[] explicitly. Example: >vim
:python sys.argv = ["foo", "bar"]
:pyfile myscript.py
-Here are some examples *python-examples* >
+Here are some examples *python-examples*
+>vim
:python from vim import *
:python from string import upper
@@ -113,7 +114,7 @@ to the next, just like the Python REPL.
*script-here*
When using a script language in-line, you might want to skip this when the
language isn't supported. Note that this mechanism doesn't work:
->
+>vim
if has('python')
python << EOF
this will NOT work!
@@ -121,7 +122,7 @@ language isn't supported. Note that this mechanism doesn't work:
endif
Instead, put the Python command in a function and call that function:
->
+>vim
if has('python')
function DefPython()
python << EOF
@@ -139,10 +140,10 @@ 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
methods, three constants, and one error object. You need to import the vim
-module before using it: >
+module before using it: >vim
:python import vim
-Overview >
+Overview >vim
:py print "Hello" # displays a message
:py vim.command(cmd) # execute an Ex command
:py w = vim.windows[n] # gets window "n"
@@ -166,10 +167,10 @@ Methods of the "vim" module
vim.command(str) *python-command*
Executes the vim (ex-mode) command str. Returns None.
- Examples: >
+ Examples: >vim
:py vim.command("set tw=72")
:py vim.command("%s/aaa/bbb/g")
-< The following definition executes Normal mode commands: >
+< The following definition executes Normal mode commands: >python
def normal(str):
vim.command("normal "+str)
# Note the use of single quotes to delimit a string containing
@@ -177,7 +178,7 @@ vim.command(str) *python-command*
normal('"a2dd"aP')
< *E659*
The ":python" command cannot be used recursively with Python 2.2 and
- older. This only works with Python 2.3 and later: >
+ older. This only works with Python 2.3 and later: >vim
:py vim.command("python print 'Hello again Python'")
vim.eval(str) *python-eval*
@@ -187,7 +188,7 @@ vim.eval(str) *python-eval*
- a list if the Vim expression evaluates to a Vim list
- a dictionary if the Vim expression evaluates to a Vim dictionary
Dictionaries and lists are recursively expanded.
- Examples: >
+ Examples: >vim
:py text_width = vim.eval("&tw")
:py str = vim.eval("12+12") # NB result is a string! Use
# string.atoi() to convert to
@@ -215,7 +216,7 @@ Error object of the "vim" module
vim.error *python-error*
Upon encountering a Vim error, Python raises an exception of type
vim.error.
- Example: >
+ Example: >python
try:
vim.command("put a")
except vim.error:
@@ -229,7 +230,7 @@ Constants of the "vim" module
vim.buffers *python-buffers*
A mapping object providing access to the list of vim buffers. The
- object supports the following operations: >
+ object supports the following operations: >vim
:py b = vim.buffers[i] # Indexing (read-only)
:py b in vim.buffers # Membership test
:py n = len(vim.buffers) # Number of elements
@@ -237,7 +238,7 @@ vim.buffers *python-buffers*
<
vim.windows *python-windows*
A sequence object providing access to the list of vim windows. The
- object supports the following operations: >
+ object supports the following operations: >vim
:py w = vim.windows[i] # Indexing (read-only)
:py w in vim.windows # Membership test
:py n = len(vim.windows) # Number of elements
@@ -251,7 +252,7 @@ vim.windows *python-windows*
vim.tabpages *python-tabpages*
A sequence object providing access to the list of vim tab pages. The
- object supports the following operations: >
+ object supports the following operations: >vim
:py t = vim.tabpages[i] # Indexing (read-only)
:py t in vim.tabpages # Membership test
:py n = len(vim.tabpages) # Number of elements
@@ -277,7 +278,7 @@ vim.current *python-current*
switching to given buffer, window or tab page. It is the only way to
switch UI objects in python: you can't assign to
|python-tabpage|.window attribute. To switch without triggering
- autocommands use >
+ autocommands use >vim
py << EOF
saved_eventignore = vim.options['eventignore']
vim.options['eventignore'] = 'all'
@@ -330,7 +331,7 @@ 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}/python3 and {rtp}/pythonx for each {rtp} found in 'runtimepath'.
-Implementation is similar to the following, but written in C: >
+Implementation is similar to the following, but written in C: >python
from imp import find_module, load_module
import vim
@@ -461,12 +462,12 @@ The buffer object methods are:
numbers s and e |inclusive|.
Note that when adding a line it must not contain a line break character '\n'.
-A trailing '\n' is allowed and ignored, so that you can do: >
+A trailing '\n' is allowed and ignored, so that you can do: >vim
:py b.append(f.readlines())
Buffer object type is available using "Buffer" attribute of vim module.
-Examples (assume b is the current buffer) >
+Examples (assume b is the current buffer) >vim
:py print b.name # write the buffer file name
:py b[0] = "hello!!!" # replace the top line
:py b[:] = None # delete the whole buffer
@@ -605,10 +606,10 @@ variants explicitly if Python 3 is required.
{script}
{endmarker}
The `:py3` and `:python3` commands work similar to `:python`. A
- simple check if the `:py3` command is working: >
+ simple check if the `:py3` command is working: >vim
:py3 print("Hello")
<
- To see what version of Python you have: >
+ To see what version of Python you have: >vim
:py3 import sys
:py3 print(sys.version)
< *:py3file*
@@ -619,11 +620,12 @@ variants explicitly if Python 3 is required.
The `:py3do` command works similar to `:pydo`.
*E880*
-Raising SystemExit exception in python isn't endorsed way to quit vim, use: >
+Raising SystemExit exception in python isn't endorsed way to quit vim, use:
+>vim
:py vim.command("qall!")
<
*has-python*
-You can test if Python is available with: >
+You can test if Python is available with: >vim
if has('pythonx')
echo 'there is Python'
endif
@@ -642,10 +644,10 @@ 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 the same as `:python3`. To check if `:pyx` works: >
+`:pyx` and `:pythonx` work the same as `:python3`. To check if `:pyx` works: >vim
:pyx print("Hello")
-To see what version of Python is being used: >
+To see what version of Python is being used: >vim
:pyx import sys
:pyx print(sys.version)
<
@@ -656,7 +658,7 @@ To see what version of Python is being used: >
`: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: >vim
if has('pythonx')
echo 'pyx* commands are available. (Python ' .. &pyx .. ')'
endif
diff --git a/runtime/doc/if_ruby.txt b/runtime/doc/if_ruby.txt
index 47305c65fb..d88f59eb73 100644
--- a/runtime/doc/if_ruby.txt
+++ b/runtime/doc/if_ruby.txt
@@ -7,7 +7,7 @@ The Ruby Interface to Vim *if_ruby* *ruby* *Ruby*
*E266* *E267* *E268* *E269* *E270* *E271* *E272* *E273*
-The home page for ruby is http://www.ruby-lang.org/. You can find links for
+The home page for ruby is https://www.ruby-lang.org/. You can find links for
downloading Ruby there.
Type |gO| to see the table of contents.
diff --git a/runtime/doc/indent.txt b/runtime/doc/indent.txt
index 1a1d8e30b0..f3e196b426 100644
--- a/runtime/doc/indent.txt
+++ b/runtime/doc/indent.txt
@@ -35,7 +35,7 @@ The rest of this section describes the 'cindent' option.
Note that 'cindent' indenting does not work for every code scenario. Vim
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
+that toplevel functions have a "{" in the first column. Otherwise they are
easily confused with declarations.
These five options control C program indenting:
@@ -60,12 +60,12 @@ used instead. The format of 'cinkeys' and 'indentkeys' is equal.
The default is "0{,0},0),0],:,0#,!^F,o,O,e" which specifies that indenting
occurs as follows:
- "0{" if you type '{' as the first character in a line
- "0}" if you type '}' as the first character in a line
- "0)" if you type ')' as the first character in a line
- "0]" if you type ']' as the first character in a line
- ":" if you type ':' after a label or case statement
- "0#" if you type '#' as the first character in a line
+ "0{" if you type "{" as the first character in a line
+ "0}" if you type "}" as the first character in a line
+ "0)" if you type ")" as the first character in a line
+ "0]" if you type "]" as the first character in a line
+ ":" if you type ":" after a label or case statement
+ "0#" if you type "#" as the first character in a line
"!^F" if you type CTRL-F (which is not inserted)
"o" if you type a <CR> anywhere or use the "o" command (not in
insert mode!)
@@ -74,21 +74,21 @@ occurs as follows:
line
Characters that can precede each key: *i_CTRL-F*
-! When a '!' precedes the key, Vim will not insert the key but will
+! When a "!" precedes the key, Vim will not insert the key but will
instead reindent the current line. This allows you to define a
command key for reindenting the current line. CTRL-F is the default
key for this. Be careful if you define CTRL-I for this because CTRL-I
is the ASCII code for <Tab>.
-* When a '*' precedes the key, Vim will reindent the line before
+* When a "*" precedes the key, Vim will reindent the line before
inserting the key. If 'cinkeys' contains "*<Return>", Vim reindents
the current line before opening a new line.
-0 When a zero precedes the key (but appears after '!' or '*') Vim will
+0 When a zero precedes the key (but appears after "!" or "*") Vim will
reindent the line only if the key is the first character you type in
the line. When used before "=" Vim will only reindent the line if
there is only white space before the word.
-When neither '!' nor '*' precedes the key, Vim reindents the line after you
-type the key. So ';' sets the indentation of a line which includes the ';'.
+When neither "!" nor "*" precedes the key, Vim reindents the line after you
+type the key. So ";" sets the indentation of a line which includes the ";".
Special key names:
<> Angle brackets mean spelled-out names of keys. For example: "<Up>",
@@ -154,8 +154,8 @@ The examples below assume a 'shiftwidth' of 4.
eN Add N to the prevailing indent inside a set of braces if the
opening brace at the End of the line (more precise: is not the
first character in a line). This is useful if you want a
- different indent when the '{' is at the start of the line from
- when '{' is at the end of the line. (default 0).
+ different indent when the "{" is at the start of the line from
+ when "{" is at the end of the line. (default 0).
cino= cino=e2 cino=e-2 >
if (cond) { if (cond) { if (cond) {
@@ -169,8 +169,8 @@ The examples below assume a 'shiftwidth' of 4.
*cino-n*
nN Add N to the prevailing indent for a statement after an "if",
"while", etc., if it is NOT inside a set of braces. This is
- useful if you want a different indent when there is no '{'
- before the statement from when there is a '{' before it.
+ useful if you want a different indent when there is no "{"
+ before the statement from when there is a "{" before it.
(default 0).
cino= cino=n2 cino=n-2 >
@@ -193,7 +193,7 @@ The examples below assume a 'shiftwidth' of 4.
int foo; int foo; int foo;
<
*cino-{*
- {N Place opening braces N characters from the prevailing indent.
+ `{N` Place opening braces N characters from the prevailing indent.
This applies only for opening braces that are inside other
braces. (default 0).
@@ -203,7 +203,7 @@ The examples below assume a 'shiftwidth' of 4.
foo; foo; foo;
<
*cino-}*
- }N Place closing braces N characters from the matching opening
+ `}N` Place closing braces N characters from the matching opening
brace. (default 0).
cino= cino={2,}-0.5s cino=}2 >
@@ -724,7 +724,7 @@ Fortran with (possibly multiple) loops ending on a labelled executable
statement of almost arbitrary type. Correct indentation requires
compiler-quality parsing. Old code with do loops ending on labelled statements
of arbitrary type can be indented with elaborate programs such as Tidy
-(http://www.unb.ca/chem/ajit/f_tidy.htm). Structured do/continue loops are
+(https://www.unb.ca/chem/ajit/f_tidy.htm). Structured do/continue loops are
also left unindented because continue statements are also used for purposes
other than ending a do loop. Programs such as Tidy can convert structured
do/continue loops to the do/enddo form. Do loops of the do/enddo variety can
@@ -846,7 +846,7 @@ own 'formatoptions'): >
Else, 't' will be removed from the 'formatoptions' string and "qrowcb" will be
added, see |fo-table| for more information.
--------------
+
*PHP_outdentSLComments*
To add extra indentation to single-line comments: >
@@ -858,7 +858,7 @@ Only single-line comments will be affected such as: >
# Comment
// Comment
/* Comment */
--------------
+<
*PHP_default_indenting*
To add extra indentation to every PHP lines with N being the number of
@@ -878,18 +878,17 @@ For example, with N = 1, this will give:
$command_hist = TRUE;
?>
(Notice the extra indentation between the PHP container markers and the code)
--------------
*PHP_outdentphpescape*
To indent PHP escape tags as the surrounding non-PHP code (only affects the
PHP escape tags): >
:let g:PHP_outdentphpescape = 0
--------------
+<
*PHP_removeCRwhenUnix*
To automatically remove '\r' characters when the 'fileformat' is set to Unix: >
:let g:PHP_removeCRwhenUnix = 1
--------------
+<
*PHP_BracesAtCodeLevel*
To indent braces at the same level than the code they contain: >
@@ -908,7 +907,6 @@ Instead of: >
NOTE: Indenting will be a bit slower if this option is used because some
optimizations won't be available.
--------------
*PHP_vintage_case_default_indent*
To indent 'case:' and 'default:' statements in switch() blocks: >
@@ -918,7 +916,6 @@ In PHP braces are not required inside 'case/default' blocks therefore 'case:'
and 'default:' are indented at the same level than the 'switch()' to avoid
meaningless indentation. You can use the above option to return to the
traditional way.
--------------
*PHP_noArrowMatching*
By default the indent script will indent multi-line chained calls by matching
@@ -927,17 +924,16 @@ the position of the '->': >
$user_name_very_long->name()
->age()
->info();
-
+<
You can revert to the classic way of indenting by setting this option to 1: >
:let g:PHP_noArrowMatching = 1
-
+<
You will obtain the following result: >
$user_name_very_long->name()
->age()
->info();
-
--------------
+<
*PHP_IndentFunctionCallParameters*
Extra indentation levels to add to parameters in multi-line function calls. >
@@ -954,14 +950,13 @@ Function call arguments will indent 1 extra level. For two-space indentation: >
$and_that
);
}
-
--------------
+<
*PHP_IndentFunctionDeclarationParameters*
Extra indentation levels to add to arguments in multi-line function
definitions. >
let g:PHP_IndentFunctionDeclarationParameters = 1
-
+<
Function arguments in declarations will indent 1 extra level. For two-space
indentation: >
@@ -974,30 +969,46 @@ indentation: >
$and_that
);
}
-
+<
PYTHON *ft-python-indent*
-The amount of indent can be set for the following situations. The examples
-given are the defaults. Note that the variables are set to an expression, so
-that you can change the value of 'shiftwidth' later.
+The amount of indent can be set with the `g:python_indent` |Dictionary|, which
+needs to be created before adding the items: >
+ let g:python_indent = {}
+The examples given are the defaults. Note that the dictionary values are set
+to an expression, so that you can change the value of 'shiftwidth' later
+without having to update these values.
Indent after an open paren: >
- let g:pyindent_open_paren = 'shiftwidth() * 2'
+ let g:python_indent.open_paren = 'shiftwidth() * 2'
Indent after a nested paren: >
- let g:pyindent_nested_paren = 'shiftwidth()'
+ let g:python_indent.nested_paren = 'shiftwidth()'
Indent for a continuation line: >
- let g:pyindent_continue = 'shiftwidth() * 2'
+ let g:python_indent.continue = 'shiftwidth() * 2'
+
+By default, the closing paren on a multiline construct lines up under the first
+non-whitespace character of the previous line.
+If you prefer that it's lined up under the first character of the line that
+starts the multiline construct, reset this key: >
+ let g:python_indent.closed_paren_align_last_line = v:false
The method uses |searchpair()| to look back for unclosed parentheses. This
can sometimes be slow, thus it timeouts after 150 msec. If you notice the
indenting isn't correct, you can set a larger timeout in msec: >
- let g:pyindent_searchpair_timeout = 500
+ let g:python_indent.searchpair_timeout = 500
If looking back for unclosed parenthesis is still too slow, especially during
a copy-paste operation, or if you don't need indenting inside multi-line
parentheses, you can completely disable this feature: >
- let g:pyindent_disable_parentheses_indenting = 1
+ let g:python_indent.disable_parentheses_indenting = 1
+
+For backward compatibility, these variables are also supported: >
+ g:pyindent_open_paren
+ g:pyindent_nested_paren
+ g:pyindent_continue
+ g:pyindent_searchpair_timeout
+ g:pyindent_disable_parentheses_indenting
R *ft-r-indent*
@@ -1129,7 +1140,6 @@ to the vimrc file, which causes the previous alignment example to change: >
);
END ENTITY sync;
-----------------------------------------
Alignment of right-hand side assignment "<=" statements are performed by
default. This causes the following alignment example: >
@@ -1148,7 +1158,6 @@ to the vimrc file, which causes the previous alignment example to change: >
(sig_b OR sig_c)) OR
(bus_a(0) AND sig_d);
-----------------------------------------
Full-line comments (lines that begin with "--") are indented to be aligned with
the very previous line's comment, PROVIDED that a whitespace follows after
diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt
index 7f3ef20762..a6aa036b55 100644
--- a/runtime/doc/index.txt
+++ b/runtime/doc/index.txt
@@ -13,7 +13,6 @@ to look for deleting something, use: "/delete".
For an overview of options see |option-list|.
For an overview of built-in functions see |functions|.
For a list of Vim variables see |vim-variable|.
-For a complete listing of all help items see |help-tags|.
Type |gO| to see the table of contents.
@@ -21,7 +20,7 @@ For a complete listing of all help items see |help-tags|.
1. Insert mode *insert-index*
tag char action in Insert mode ~
------------------------------------------------------------------------
+------------------------------------------------------------------------------ ~
|i_CTRL-@| CTRL-@ insert previously inserted text and stop
insert
|i_CTRL-A| CTRL-A insert previously inserted text
@@ -61,7 +60,7 @@ tag char action in Insert mode ~
|i_CTRL-Q| CTRL-Q same as CTRL-V, unless used for terminal
control flow
|i_CTRL-SHIFT-Q| CTRL-SHIFT-Q {char}
- like CTRL-Q unless |modifyOtherKeys| is active
+ like CTRL-Q unless |tui-modifyOtherKeys| is active
|i_CTRL-R| CTRL-R {register}
insert the contents of a register
|i_CTRL-R_CTRL-R| CTRL-R CTRL-R {register}
@@ -79,7 +78,7 @@ tag char action in Insert mode ~
line
|i_CTRL-V| CTRL-V {char} insert next non-digit literally
|i_CTRL-SHIFT-V| CTRL-SHIFT-V {char}
- like CTRL-V unless |modifyOtherKeys| is active
+ like CTRL-V unless |tui-modifyOtherKeys| is active
|i_CTRL-V_digit| CTRL-V {number} insert three digit decimal number as a single
byte.
|i_CTRL-W| CTRL-W delete word before the cursor
@@ -184,7 +183,7 @@ SECTION a section that possibly starts with '}' instead of '{'
note: 1 = cursor movement command; 2 = can be undone/redone
tag char note action in Normal mode ~
-------------------------------------------------------------------------------
+------------------------------------------------------------------------------ ~
CTRL-@ not used
|CTRL-A| CTRL-A 2 add N to number at/after cursor
|CTRL-B| CTRL-B 1 scroll N screens Backwards
@@ -454,14 +453,14 @@ tag char note action in Normal mode ~
|<S-Up>| <S-Up> 1 same as CTRL-B
|<Undo>| <Undo> 2 same as "u"
|<Up>| <Up> 1 same as "k"
-|<ScrollWheelDown>| <ScrollWheelDown> move window three lines down
-|<S-ScrollWheelDown>| <S-ScrollWheelDown> move window one page down
-|<ScrollWheelUp>| <ScrollWheelUp> move window three lines up
-|<S-ScrollWheelUp>| <S-ScrollWheelUp> move window one page up
-|<ScrollWheelLeft>| <ScrollWheelLeft> move window six columns left
-|<S-ScrollWheelLeft>| <S-ScrollWheelLeft> move window one page left
-|<ScrollWheelRight>| <ScrollWheelRight> move window six columns right
-|<S-ScrollWheelRight>| <S-ScrollWheelRight> move window one page right
+*<ScrollWheelDown>* <ScrollWheelDown> move window three lines down
+*<S-ScrollWheelDown>* <S-ScrollWheelDown> move window one page down
+*<ScrollWheelUp>* <ScrollWheelUp> move window three lines up
+*<S-ScrollWheelUp>* <S-ScrollWheelUp> move window one page up
+*<ScrollWheelLeft>* <ScrollWheelLeft> move window six columns left
+*<S-ScrollWheelLeft>* <S-ScrollWheelLeft> move window one page left
+*<ScrollWheelRight>* <ScrollWheelRight> move window six columns right
+*<S-ScrollWheelRight>* <S-ScrollWheelRight> move window one page right
==============================================================================
2.1 Text objects *objects*
@@ -469,7 +468,7 @@ tag char note action in Normal mode ~
These can be used after an operator or in Visual mode to select an object.
tag command action in op-pending and Visual mode ~
-------------------------------------------------------------------------------
+------------------------------------------------------------------------------ ~
|v_aquote| a" double quoted string
|v_a'| a' single quoted string
|v_a(| a( same as ab
@@ -511,7 +510,7 @@ tag command action in op-pending and Visual mode ~
2.2 Window commands *CTRL-W*
tag command action in Normal mode ~
-------------------------------------------------------------------------------
+------------------------------------------------------------------------------ ~
|CTRL-W_CTRL-B| CTRL-W CTRL-B same as "CTRL-W b"
|CTRL-W_CTRL-C| CTRL-W CTRL-C same as "CTRL-W c"
|CTRL-W_CTRL-D| CTRL-W CTRL-D same as "CTRL-W d"
@@ -609,7 +608,7 @@ tag command action in Normal mode ~
2.3 Square bracket commands *[* *]*
tag char note action in Normal mode ~
-------------------------------------------------------------------------------
+------------------------------------------------------------------------------ ~
|[_CTRL-D| [ CTRL-D jump to first #define found in current and
included files matching the word under the
cursor, start searching at beginning of
@@ -699,8 +698,8 @@ tag char note action in Normal mode ~
2.4 Commands starting with 'g' *g*
tag char note action in Normal mode ~
-------------------------------------------------------------------------------
-|g_CTRL-A| g CTRL-A dump a memory profile
+------------------------------------------------------------------------------ ~
+g_CTRL-A g CTRL-A dump a memory profile
|g_CTRL-G| g CTRL-G show information about current cursor
position
|g_CTRL-H| g CTRL-H start Select block mode
@@ -802,7 +801,7 @@ tag char note action in Normal mode ~
2.5 Commands starting with 'z' *z*
tag char note action in Normal mode ~
-------------------------------------------------------------------------------
+------------------------------------------------------------------------------ ~
|z<CR>| z<CR> redraw, cursor line to top of window,
cursor on first non-blank
|zN<CR>| z{height}<CR> redraw, make window {height} lines high
@@ -876,7 +875,7 @@ tag char note action in Normal mode ~
These can be used after an operator, but before a {motion} has been entered.
tag char action in Operator-pending mode ~
------------------------------------------------------------------------
+------------------------------------------------------------------------------ ~
|o_v| v force operator to work charwise
|o_V| V force operator to work linewise
|o_CTRL-V| CTRL-V force operator to work blockwise
@@ -888,7 +887,7 @@ Most commands in Visual mode are the same as in Normal mode. The ones listed
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 Normal mode
|v_CTRL-A| CTRL-A 2 add N to number in highlighted text
@@ -1008,7 +1007,7 @@ Normal characters are inserted at the current cursor position.
file names, tags, commands etc. as appropriate.
tag command action in Command-line editing mode ~
-------------------------------------------------------------------------------
+------------------------------------------------------------------------------ ~
CTRL-@ not used
|c_CTRL-A| CTRL-A do completion on the pattern in front of the
cursor and insert all matches
@@ -1111,23 +1110,23 @@ to terminal mode.
You found it, Arthur! *holy-grail*
==============================================================================
-6. EX commands *ex-cmd-index* *:index*
+6. EX commands *ex-commands* *ex-cmd-index* *:index*
This is a brief but complete listing of all the ":" commands, without
mentioning any arguments. The optional part of the command name is inside [].
The commands are sorted on the non-optional part of their name.
tag command action ~
-------------------------------------------------------------------------------
+------------------------------------------------------------------------------ ~
|:| : nothing
|:range| :{range} go to last line in {range}
|:!| :! filter lines or execute an external command
|:!!| :!! repeat last ":!" command
|:#| :# same as ":number"
|:&| :& repeat last ":substitute"
-|:star| :* execute contents of a register
+|:star| :* use the last Visual area, like :'<,'>
|:<| :< shift lines one 'shiftwidth' left
-|:=| := print the cursor line number
+|:=| := print the last line number
|:>| :> shift lines one 'shiftwidth' right
|:@| :@ execute contents of a register
|:@@| :@@ repeat the previous ":@"
@@ -1209,8 +1208,8 @@ tag command action ~
|:cgetfile| :cg[etfile] read file with error messages
|:changes| :changes print the change list
|:chdir| :chd[ir] change directory
-|:checkhealth| :checkh[ealth] run healthchecks
-|:checkpath| :che[ckpath] list included files
+|:checkhealth| :che[ckhealth] run healthchecks
+|:checkpath| :checkp[ath] list included files
|:checktime| :checkt[ime] check timestamp of loaded buffers
|:chistory| :chi[story] list the error lists
|:clast| :cla[st] go to the specified error, default last one
@@ -1240,8 +1239,6 @@ tag command action ~
|:cpfile| :cpf[ile] go to last error in previous file
|:cquit| :cq[uit] quit Vim with an error code
|:crewind| :cr[ewind] go to the specified error, default first one
-|:cscope| :cs[cope] execute cscope command
-|:cstag| :cst[ag] use cscope to jump to a tag
|:cunmap| :cu[nmap] like ":unmap" but for Command-line mode
|:cunabbrev| :cuna[bbrev] like ":unabbrev" but for Command-line mode
|:cunmenu| :cunme[nu] remove menu for Command-line mode
@@ -1313,7 +1310,6 @@ tag command action ~
|:grepadd| :grepa[dd] like :grep, but append to current list
|:gui| :gu[i] start the GUI
|:gvim| :gv[im] start the GUI
-|:hardcopy| :ha[rdcopy] send text to the printer
|:help| :h[elp] open a help window
|:helpclose| :helpc[lose] close one help window
|:helpgrep| :helpg[rep] like ":grep" but searches help files
@@ -1321,6 +1317,7 @@ tag command action ~
|:highlight| :hi[ghlight] specify highlighting methods
|:hide| :hid[e] hide current buffer for a command
|:history| :his[tory] print a history list
+|:horizontal| :hor[izontal] following window command work horizontally
|:insert| :i[nsert] insert text
|:iabbrev| :ia[bbrev] like ":abbrev" but for Insert mode
|:iabclear| :iabc[lear] like ":abclear" but for Insert mode
@@ -1365,7 +1362,6 @@ tag command action ~
|:lcd| :lc[d] change directory locally
|:lchdir| :lch[dir] change directory locally
|:lclose| :lcl[ose] close location window
-|:lcscope| :lcs[cope] like ":cscope" but uses location list
|:ldo| :ld[o] execute command in valid location list entries
|:lfdo| :lfd[o] execute command in each file in location list
|:left| :le[ft] left align lines
@@ -1546,7 +1542,6 @@ tag command action ~
buffer list
|:scriptnames| :scr[iptnames] list names of all sourced Vim scripts
|:scriptencoding| :scripte[ncoding] encoding used in sourced Vim script
-|:scscope| :scs[cope] split window and execute cscope command
|:set| :se[t] show or set options
|:setfiletype| :setf[iletype] set 'filetype', unless it was set already
|:setglobal| :setg[lobal] show global values of options
@@ -1637,6 +1632,7 @@ tag command action ~
|: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
+|:trust| :trust add or remove file from trust database
|: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-mode|
diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt
index 6b0899334b..e608b431f2 100644
--- a/runtime/doc/insert.txt
+++ b/runtime/doc/insert.txt
@@ -257,8 +257,8 @@ CTRL-] Trigger abbreviation, without inserting a character.
*i_<Insert>*
<Insert> Toggle between Insert and Replace mode.
------------------------------------------------------------------------
+-----------------------------------------------------------------------
*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:
@@ -378,6 +378,7 @@ CTRL-G u close undo sequence, start new change *i_CTRL-G_u*
CTRL-G U don't start a new undo block with the next *i_CTRL-G_U*
left/right cursor movement, if the cursor
stays within the same line
+
-----------------------------------------------------------------------
The CTRL-O command sometimes has a side effect: If the cursor was beyond the
@@ -650,9 +651,9 @@ When the popup menu is displayed there are a few more special keys, see
|popupmenu-keys|.
Note: The keys that are valid in CTRL-X mode are not mapped. This allows for
-":map ^F ^X^F" to work (where ^F is CTRL-F and ^X is CTRL-X). The key that
-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.
+`:map <C-F> <C-X><C-F>` to work. The key that 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.
*E565*
Note: While completion is active Insert mode can't be used recursively and
@@ -661,10 +662,10 @@ 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): >
- :inoremap ^] ^X^]
- :inoremap ^F ^X^F
- :inoremap ^D ^X^D
- :inoremap ^L ^X^L
+ :inoremap <C-]> <C-X><C-]>
+ :inoremap <C-F> <C-X><C-F>
+ :inoremap <C-D> <C-X><C-D>
+ :inoremap <C-L> <C-X><C-L>
As a special case, typing CTRL-R to perform register insertion (see
|i_CTRL-R|) will not exit CTRL-X mode. This is primarily to allow the use of
@@ -857,29 +858,27 @@ invoked and what it should return.
Here is an example that uses the "aiksaurus" command (provided by Magnus
Groß): >
- func Thesaur(findstart, base)
- if a:findstart
- let line = getline('.')
- let start = col('.') - 1
- while start > 0 && line[start - 1] =~ '\a'
- let start -= 1
- endwhile
- return start
- else
- let res = []
- let h = ''
- for l in split(system('aiksaurus ' .. shellescape(a:base)), '\n')
- if l[:3] == '=== '
- let h = substitute(l[4:], ' =*$', '', '')
- elseif l[0] =~ '\a'
- call extend(res, map(split(l, ', '), {_, val -> {'word': val, 'menu': '('.h.')'}}))
- endif
- endfor
- return res
- endif
- endfunc
-
- set thesaurusfunc=Thesaur
+ func Thesaur(findstart, base)
+ if a:findstart
+ return searchpos('\<', 'bnW', line('.'))[1] - 1
+ endif
+ let res = []
+ let h = ''
+ for l in systemlist('aiksaurus ' .. shellescape(a:base))
+ if l[:3] == '=== '
+ let h = '(' .. substitute(l[4:], ' =*$', ')', '')
+ elseif l ==# 'Alphabetically similar known words are: '
+ let h = "\U0001f52e"
+ elseif l[0] =~ '\a' || (h ==# "\U0001f52e" && l[0] ==# "\t")
+ call extend(res, map(split(substitute(l, '^\t', '', ''), ', '), {_, val -> {'word': val, 'menu': h}}))
+ endif
+ endfor
+ return res
+ endfunc
+
+ if exists('+thesaurusfunc')
+ set thesaurusfunc=Thesaur
+ endif
Completing keywords in the current and included files *compl-keyword*
@@ -1126,7 +1125,8 @@ that contains the List. The Dict can have these items:
leading text is changed.
Other items are ignored.
-For acting upon end of completion, see the |CompleteDone| autocommand event.
+For acting upon end of completion, see the |CompleteDonePre| and
+|CompleteDone| autocommand event.
For example, the function can contain this: >
let matches = ... list of words ...
@@ -1349,16 +1349,8 @@ 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
-
-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/
- https://github.com/universal-ctags/ctags-win32
+Universal Ctags is preferred, Exuberant Ctags is no longer maintained.
If you want to complete system functions you can do something like this. Use
ctags to generate a tags file for all the system header files: >
@@ -1458,14 +1450,14 @@ DOM compatibility
At the moment (beginning of 2006) there are two main browsers - MS Internet
Explorer and Mozilla Firefox. These two applications are covering over 90% of
market. Theoretically standards are created by W3C organisation
-(http://www.w3c.org) but they are not always followed/implemented.
-
+(https://www.w3.org/) but they are not always followed/implemented.
+>
IE FF W3C Omni completion ~
+/- +/- + + ~
+ + - + ~
+ - - - ~
- + - - ~
-
+<
Regardless from state of implementation in browsers but if element is defined
in standards, completion plugin will place element in suggestion list. When
both major engines implemented element, even if this is not in standards it
@@ -1479,7 +1471,6 @@ 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:
@@ -1519,7 +1510,7 @@ RUBY *ft-ruby-omni*
NOTE: |compl-omni| for Ruby code requires |provider-ruby| to be installed.
Ruby completion will parse your buffer on demand in order to provide a list of
-completions. These completions will be drawn from modules loaded by 'require'
+completions. These completions will be drawn from modules loaded by "require"
and modules defined in the current buffer.
The completions provided by CTRL-X CTRL-O are sensitive to the context:
@@ -1533,7 +1524,7 @@ The completions provided by CTRL-X CTRL-O are sensitive to the context:
3. After '.', '::' or ':' Methods applicable to the object being
dereferenced
- 4. After ':' or ':foo' Symbol name (beginning with 'foo')
+ 4. After ':' or ':foo' Symbol name (beginning with "foo")
Notes:
- Vim will load/evaluate code in order to provide completions. This may
@@ -1759,7 +1750,7 @@ In the example four special elements are visible:
dialect.
2. If the list containing possible values of attributes has one element and
this element is equal to the name of the attribute this attribute will be
- treated as boolean and inserted as 'attrname' and not as 'attrname="'
+ treated as boolean and inserted as "attrname" and not as 'attrname="'
3. "vimxmltaginfo" - a special key with a Dictionary containing tag
names as keys and two element List as values, for additional menu info and
the long description.
@@ -1778,12 +1769,12 @@ DTD -> Vim *dtd2vim*
On |www| is the script |dtd2vim| which parses DTD and creates an XML data file
for Vim XML omni completion.
- dtd2vim: http://www.vim.org/scripts/script.php?script_id=1462
+ dtd2vim: https://www.vim.org/scripts/script.php?script_id=1462
Check the beginning of that file for usage details.
The script requires perl and:
- perlSGML: http://savannah.nongnu.org/projects/perlsgml
+ perlSGML: https://savannah.nongnu.org/projects/perlsgml
Commands
diff --git a/runtime/doc/intro.txt b/runtime/doc/intro.txt
index ae80935032..6bf6850ccf 100644
--- a/runtime/doc/intro.txt
+++ b/runtime/doc/intro.txt
@@ -308,7 +308,7 @@ These names for keys are used in the documentation. They can also be used
with the ":map" command.
notation meaning equivalent decimal value(s) ~
------------------------------------------------------------------------
+----------------------------------------------------------------------- ~
<Nul> zero CTRL-@ 0 (stored as 10) *<Nul>*
<BS> backspace CTRL-H 8 *backspace*
<Tab> tab CTRL-I 9 *tab* *Tab*
@@ -373,7 +373,7 @@ notation meaning equivalent decimal value(s) ~
<M-…> alt-key or meta-key *META* *ALT* *<M-*
<A-…> same as <M-…> *<A-*
<D-…> command-key or "super" key *<D-*
------------------------------------------------------------------------
+----------------------------------------------------------------------- ~
Note:
@@ -408,14 +408,18 @@ the ":map" command. The rules are:
The <> notation uses <lt> to escape the special meaning of key names. Using a
backslash also works, but only when 'cpoptions' does not include the 'B' flag.
-Examples for mapping CTRL-H to the six characters "<Home>": >
+Examples for mapping CTRL-H to the six characters "<Home>": >vim
:imap <C-H> \<Home>
:imap <C-H> <lt>Home>
The first one only works when the 'B' flag is not in 'cpoptions'. The second
one always works.
-To get a literal "<lt>" in a mapping: >
+To get a literal "<lt>" in a mapping: >vim
:map <C-L> <lt>lt>
+The notation can be used in a double quoted strings, using "\<" at the start,
+e.g. "\<C-Space>". This results in a special key code. To convert this back
+to readable text use `keytrans()`.
+
==============================================================================
Modes, introduction *vim-modes-intro* *vim-modes*
@@ -516,39 +520,39 @@ CTRL-O in Insert mode you get a beep but you are still in Insert mode, type
TO mode ~
Normal Visual Select Insert Replace Cmd-line Ex ~
FROM mode ~
-Normal v V ^V *4 *1 R gR : / ? ! Q
-Visual *2 ^G c C -- : --
-Select *5 ^O ^G *6 -- -- --
+Normal v V ^V `*4` *1 R gR : / ? ! Q
+Visual `*2` ^G c C -- : --
+Select `*5` ^O ^G `*6` -- -- --
Insert <Esc> -- -- <Insert> -- --
Replace <Esc> -- -- <Insert> -- --
-Command-line *3 -- -- :start -- --
+Command-line `*3` -- -- :start -- --
Ex :vi -- -- -- -- --
-- not possible
-*1 Go from Normal mode to Insert mode by giving the command "i", "I", "a",
- "A", "o", "O", "c", "C", "s" or S".
-*2 Go from Visual mode to Normal mode by giving a non-movement command, which
- causes the command to be executed, or by hitting <Esc> "v", "V" or "CTRL-V"
- (see |v_v|), which just stops Visual mode without side effects.
-*3 Go from Command-line mode to Normal mode by:
- - Hitting <CR> or <NL>, which causes the entered command to be executed.
- - Deleting the complete line (e.g., with CTRL-U) and giving a final <BS>.
- - Hitting CTRL-C or <Esc>, which quits the command-line without executing
- the command.
- In the last case <Esc> may be the character defined with the 'wildchar'
- option, in which case it will start command-line completion. You can
- ignore that and type <Esc> again.
-*4 Go from Normal to Select mode by:
- - use the mouse to select text while 'selectmode' contains "mouse"
- - use a non-printable command to move the cursor while keeping the Shift
- key pressed, and the 'selectmode' option contains "key"
- - use "v", "V" or "CTRL-V" while 'selectmode' contains "cmd"
- - use "gh", "gH" or "g CTRL-H" |g_CTRL-H|
-*5 Go from Select mode to Normal mode by using a non-printable command to move
- the cursor, without keeping the Shift key pressed.
-*6 Go from Select mode to Insert mode by typing a printable character. The
- selection is deleted and the character is inserted.
+* 1 Go from Normal mode to Insert mode by giving the command "i", "I", "a",
+ "A", "o", "O", "c", "C", "s" or S".
+* 2 Go from Visual mode to Normal mode by giving a non-movement command, which
+ causes the command to be executed, or by hitting <Esc> "v", "V" or "CTRL-V"
+ (see |v_v|), which just stops Visual mode without side effects.
+* 3 Go from Command-line mode to Normal mode by:
+ - Hitting <CR> or <NL>, which causes the entered command to be executed.
+ - Deleting the complete line (e.g., with CTRL-U) and giving a final <BS>.
+ - Hitting CTRL-C or <Esc>, which quits the command-line without executing
+ the command.
+ In the last case <Esc> may be the character defined with the 'wildchar'
+ option, in which case it will start command-line completion. You can
+ ignore that and type <Esc> again.
+* 4 Go from Normal to Select mode by:
+ - use the mouse to select text while 'selectmode' contains "mouse"
+ - use a non-printable command to move the cursor while keeping the Shift
+ key pressed, and the 'selectmode' option contains "key"
+ - use "v", "V" or "CTRL-V" while 'selectmode' contains "cmd"
+ - use "gh", "gH" or "g CTRL-H" |g_CTRL-H|
+* 5 Go from Select mode to Normal mode by using a non-printable command to move
+ the cursor, without keeping the Shift key pressed.
+* 6 Go from Select mode to Insert mode by typing a printable character. The
+ selection is deleted and the character is inserted.
*CTRL-\_CTRL-N* *i_CTRL-\_CTRL-N* *c_CTRL-\_CTRL-N*
*v_CTRL-\_CTRL-N* *t_CTRL-\_CTRL-N*
@@ -585,26 +589,26 @@ Lines longer than the window width will wrap, unless the 'wrap' option is off
If the window has room after the last line of the buffer, Vim will show '~' in
the first column of the last lines in the window, like this:
-
+>
+-----------------------+
|some line |
|last line |
|~ |
|~ |
+-----------------------+
-
+<
Thus the '~' lines indicate that the end of the buffer was reached.
If the last line in a window doesn't fit, Vim will indicate this with a '@' in
the first column of the last lines in the window, like this:
-
+>
+-----------------------+
|first line |
|second line |
|@ |
|@ |
+-----------------------+
-
+<
Thus the '@' lines indicate that there is a line that doesn't fit in the
window.
@@ -612,14 +616,14 @@ When the "lastline" flag is present in the 'display' option, you will not see
'@' characters at the left side of window. If the last line doesn't fit
completely, only the part that fits is shown, and the last three characters of
the last line are replaced with "@@@", like this:
-
+>
+-----------------------+
|first line |
|second line |
|a very long line that d|
|oesn't fit in the wi@@@|
+-----------------------+
-
+<
If there is a single line that is too long to fit in the window, this is a
special situation. Vim will show only part of the line, around where the
cursor is. There are no special characters shown, so that you can edit all
@@ -700,9 +704,9 @@ Definitions *definitions* *jargon*
A screen contains one or more windows, separated by status lines and with the
command line at the bottom.
-
+>
+-------------------------------+
-screen | window 1 | window 2 |
+ screen | window 1 | window 2 |
| | |
| | |
|= status line =|= status line =|
@@ -712,7 +716,7 @@ screen | window 1 | window 2 |
|==== status line ==============|
|command line |
+-------------------------------+
-
+<
The command line is also used for messages. It scrolls up the screen when
there is not enough room in the command line.
diff --git a/runtime/doc/job_control.txt b/runtime/doc/job_control.txt
index 6a9d865c40..37a4e2ebb1 100644
--- a/runtime/doc/job_control.txt
+++ b/runtime/doc/job_control.txt
@@ -30,7 +30,7 @@ Usage *job-control-usage*
To control jobs, use the "job…" family of functions: |jobstart()|,
|jobstop()|, etc.
-Example: >
+Example: >vim
function! s:OnEvent(job_id, data, event) dict
if a:event == 'stdout'
@@ -51,7 +51,7 @@ Example: >
let job1 = jobstart(['bash'], extend({'shell': 'shell 1'}, s:callbacks))
let job2 = jobstart(['bash', '-c', 'for i in {1..10}; do echo hello $i!; sleep 1; done'], extend({'shell': 'shell 2'}, s:callbacks))
-To test the above script, copy it to a file ~/foo.vim and run it: >
+To test the above script, copy it to a file ~/foo.vim and run it: >bash
nvim -u ~/foo.vim
<
Description of what happens:
@@ -75,7 +75,7 @@ Arguments passed to on_exit callback:
will not trigger the on_stdout/on_stderr callback (but if the process
ends, the on_exit callback will be invoked).
For example, "ruby -e" buffers output, so small strings will be
- buffered unless "auto-flushing" ($stdout.sync=true) is enabled. >
+ buffered unless "auto-flushing" ($stdout.sync=true) is enabled. >vim
function! Receive(job_id, data, event)
echom printf('%s: %s',a:event,string(a:data))
endfunction
@@ -92,7 +92,7 @@ Arguments passed to on_exit callback:
- `abc\nefg` may arrive as `['abc', '']`, `['efg']` or `['abc']`,
`['','efg']`, or even `['ab']`, `['c','efg']`.
Easy way to deal with this: initialize a list as `['']`, then append
- to it as follows: >
+ to it as follows: >vim
let s:chunks = ['']
func! s:on_stdout(job_id, data, event) dict
let s:chunks[-1] .= a:data[0]
@@ -101,7 +101,7 @@ Arguments passed to on_exit callback:
<
The |jobstart-options| dictionary is passed as |self| to the callback.
-The above example could be written in this "object-oriented" style: >
+The above example could be written in this "object-oriented" style: >vim
let Shell = {}
@@ -129,16 +129,16 @@ The above example could be written in this "object-oriented" style: >
let instance = Shell.new('bomb',
\ 'for i in $(seq 9 -1 1); do echo $i 1>&$((i % 2 + 1)); sleep 1; done')
<
-To send data to the job's stdin, use |chansend()|: >
+To send data to the job's stdin, use |chansend()|: >vim
:call chansend(job1, "ls\n")
:call chansend(job1, "invalid-command\n")
:call chansend(job1, "exit\n")
<
-A job may be killed with |jobstop()|: >
+A job may be killed with |jobstop()|: >vim
:call jobstop(job1)
<
A job may be killed at any time with the |jobstop()| function:
->
+>vim
:call jobstop(job1)
<
Individual streams can be closed without killing the job, see |chanclose()|.
diff --git a/runtime/doc/lsp-extension.txt b/runtime/doc/lsp-extension.txt
index 6e9ad940c7..fe72e9eb18 100644
--- a/runtime/doc/lsp-extension.txt
+++ b/runtime/doc/lsp-extension.txt
@@ -6,7 +6,7 @@
The `vim.lsp` Lua module is a framework for building LSP plugins.
1. Start with |vim.lsp.start_client()| and |vim.lsp.buf_attach_client()|.
- 2. Peek at the API: >
+ 2. Peek at the API: >vim
:lua print(vim.inspect(vim.lsp))
< 3. See |lsp-extension-example| for a full example.
@@ -30,7 +30,7 @@ The example will:
3. Create a new LSP for that root directory if one doesn't exist.
4. Attach the buffer to the client for that root directory.
->
+>lua
-- Some path manipulation utilities
local function is_dir(filename)
local stat = vim.loop.fs_stat(filename)
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index 7fc0daa0ca..215515a2d9 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -33,40 +33,40 @@ Follow these steps to get LSP features:
2. Configure the LSP client per language server.
A minimal example:
->
+>lua
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.
+ See |vim.lsp.start()| for details.
3. Configure keymaps and autocmds to utilize LSP features.
See |lsp-config|.
-<
+
*lsp-config*
Starting a LSP client will automatically report diagnostics via
-|vim.diagnostic|. Read |vim.diagnostic.config| to learn how to customize the
+|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
+- '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.
-- |formatexpr| is set to |vim.lsp.formatexpr| if both |formatprg| and
- |formatexpr| are empty. This allows to format lines via |gq| if the language
+- 'formatexpr' is set to |vim.lsp.formatexpr()| if both 'formatprg' and
+ 'formatexpr' are empty. This allows to format lines via |gq| if the language
server supports it.
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:
->
+>lua
vim.api.nvim_create_autocmd('LspAttach', {
callback = function(args)
vim.keymap.set('n', 'K', vim.lsp.buf.hover, { buffer = args.buf })
@@ -86,7 +86,7 @@ The most used functions are:
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:
->
+>lua
vim.api.nvim_create_autocmd('LspAttach', {
callback = function(args)
local client = vim.lsp.get_client_by_id(args.data.client_id)
@@ -100,7 +100,7 @@ calls behind capability checks:
To learn what capabilities are available you can run the following command in
a buffer with a started LSP client:
->
+>vim
:lua =vim.lsp.get_active_clients()[1].server_capabilities
<
@@ -110,14 +110,14 @@ Full list of features provided by default can be found in |lsp-buf|.
FAQ *lsp-faq*
- Q: How to force-reload LSP?
- A: Stop all clients, then reload the buffer. >
+ A: Stop all clients, then reload the buffer. >vim
:lua vim.lsp.stop_client(vim.lsp.get_active_clients())
:edit
- Q: Why isn't completion working?
A: In the buffer where you want to use LSP, check that 'omnifunc' is set to
- "v:lua.vim.lsp.omnifunc": >
+ "v:lua.vim.lsp.omnifunc": >vim
:verbose set omnifunc?
@@ -126,13 +126,14 @@ FAQ *lsp-faq*
"after/ftplugin/python.vim".
- Q: How do I run a request synchronously (e.g. for formatting on file save)?
- A: Use the `_sync` variant of the function provided by |lsp-buf|, if it
- exists.
+ A: Check if the function has an `async` parameter and set the value to
+ false.
- E.g. code formatting: >
+ E.g. code formatting: >vim
" Auto-format *.rs (rust) files prior to saving them
- autocmd BufWritePre *.rs lua vim.lsp.buf.formatting_sync(nil, 1000)
+ " (async = false is the default for format)
+ autocmd BufWritePre *.rs lua vim.lsp.buf.format({ async = false })
<
*lsp-vs-treesitter*
@@ -161,7 +162,7 @@ to the given buffer. |lsp-buf|
LSP request/response handlers are implemented as Lua functions (see
|lsp-handler|). The |vim.lsp.handlers| table defines default handlers used
-when creating a new client. Keys are LSP method names: >
+when creating a new client. Keys are LSP method names: >vim
:lua print(vim.inspect(vim.tbl_keys(vim.lsp.handlers)))
<
@@ -189,6 +190,7 @@ specification. These LSP requests/notifications are defined by default:
textDocument/typeDefinition*
window/logMessage
window/showMessage
+ window/showDocument
window/showMessageRequest
workspace/applyEdit
workspace/symbol
@@ -244,7 +246,7 @@ For |lsp-request|, each |lsp-handler| has this signature: >
Where `err` must be shaped like an RPC error:
`{ code, message, data? }`
- You can use |vim.lsp.rpc_response_error()| to create this object.
+ You can use |vim.lsp.rpc.rpc_response_error()| to create this object.
For |lsp-notification|, each |lsp-handler| has this signature: >
@@ -289,7 +291,7 @@ To configure the behavior of a builtin |lsp-handler|, the convenient method
To configure the behavior of |vim.lsp.diagnostic.on_publish_diagnostics()|,
consider the following example, where a new |lsp-handler| is created using
- |vim.lsp.with()| that no longer generates signs for the diagnostics: >
+ |vim.lsp.with()| that no longer generates signs for the diagnostics: >lua
vim.lsp.handlers["textDocument/publishDiagnostics"] = vim.lsp.with(
vim.lsp.diagnostic.on_publish_diagnostics, {
@@ -299,7 +301,7 @@ To configure the behavior of a builtin |lsp-handler|, the convenient method
)
<
To enable signs, use |vim.lsp.with()| again to create and assign a new
- |lsp-handler| to |vim.lsp.handlers| for the associated method: >
+ |lsp-handler| to |vim.lsp.handlers| for the associated method: >lua
vim.lsp.handlers["textDocument/publishDiagnostics"] = vim.lsp.with(
vim.lsp.diagnostic.on_publish_diagnostics, {
@@ -309,7 +311,7 @@ To configure the behavior of a builtin |lsp-handler|, the convenient method
)
<
To configure a handler on a per-server basis, you can use the {handlers} key
- for |vim.lsp.start_client()| >
+ for |vim.lsp.start_client()| >lua
vim.lsp.start_client {
..., -- Other configuration omitted.
@@ -323,7 +325,8 @@ To configure the behavior of a builtin |lsp-handler|, the convenient method
},
}
<
- or if using 'nvim-lspconfig', you can use the {handlers} key of `setup()`: >
+ or if using "nvim-lspconfig", you can use the {handlers} key of `setup()`:
+ >lua
require('lspconfig').rust_analyzer.setup {
handlers = {
@@ -337,8 +340,8 @@ To configure the behavior of a builtin |lsp-handler|, the convenient method
}
<
Some handlers do not have an explicitly named handler function (such as
- |on_publish_diagnostics()|). To override these, first create a reference
- to the existing handler: >
+ ||vim.lsp.diagnostic.on_publish_diagnostics()|). To override these, first
+ create a reference to the existing handler: >lua
local on_references = vim.lsp.handlers["textDocument/references"]
vim.lsp.handlers["textDocument/references"] = vim.lsp.with(
@@ -355,14 +358,14 @@ Handlers can be set by:
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: >
+ To override the handler for the `"textDocument/definition"` method: >lua
vim.lsp.handlers["textDocument/definition"] = my_custom_default_definition
<
-- The {handlers} parameter for |vim.lsp.start_client|.
+- The {handlers} parameter for |vim.lsp.start_client()|.
This will set the |lsp-handler| as the default handler for this server.
- For example: >
+ For example: >lua
vim.lsp.start_client {
..., -- Other configuration omitted.
@@ -374,7 +377,7 @@ Handlers can be set by:
- The {handler} parameter for |vim.lsp.buf_request()|.
This will set the |lsp-handler| ONLY for the current request.
- For example: >
+ For example: >lua
vim.lsp.buf_request(
0,
@@ -401,7 +404,7 @@ and helper functions for creating protocol-related objects.
https://github.com/microsoft/language-server-protocol/raw/gh-pages/_specifications/specification-3-14.md
For example `vim.lsp.protocol.ErrorCodes` allows reverse lookup by number or
-name: >
+name: >lua
vim.lsp.protocol.TextDocumentSyncKind.Full == 1
vim.lsp.protocol.TextDocumentSyncKind[1] == "Full"
@@ -424,7 +427,7 @@ For the format of the notification message, see:
- `context` table|nil. `ctx` from |lsp-handler|
This table can be used with vim.fn.setqflist or vim.fn.setloclist. E.g.:
->
+>lua
local function on_list(options)
vim.fn.setqflist({}, ' ', options)
vim.api.nvim_command('cfirst')
@@ -434,7 +437,7 @@ This table can be used with vim.fn.setqflist or vim.fn.setloclist. E.g.:
vim.lsp.buf.references(nil, {on_list=on_list})
<
If you prefer loclist do something like this:
->
+>lua
local function on_list(options)
vim.fn.setloclist(0, {}, ' ', options)
vim.api.nvim_command('lopen')
@@ -468,7 +471,7 @@ LspCodeLens
|nvim_buf_set_extmark()|.
LspCodeLensSeparator *hl-LspCodeLensSeparator*
- Used to color the separator between two or more code lens.
+ Used to color the separator between two or more code lenses.
*lsp-highlight-signature*
@@ -485,7 +488,7 @@ 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: >
+callback in the "data" table. Example: >lua
vim.api.nvim_create_autocmd("LspAttach", {
callback = function(args)
@@ -503,7 +506,7 @@ callback in the "data" table. Example: >
*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: >
+callback in the "data" table. Example: >lua
vim.api.nvim_create_autocmd("LspDetach", {
callback = function(args)
@@ -513,7 +516,7 @@ callback in the "data" table. Example: >
end,
})
<
-In addition, the following |User| |autocommands| are provided:
+Also the following |User| |autocommand|s are provided:
LspProgressUpdate *LspProgressUpdate*
Upon receipt of a progress notification from the server. See
@@ -523,7 +526,7 @@ LspRequest *LspRequest*
After a change to the active set of pending LSP requests. See {requests}
in |vim.lsp.client|.
-Example: >
+Example: >vim
autocmd User LspProgressUpdate redrawstatus
autocmd User LspRequest redrawstatus
<
@@ -538,8 +541,8 @@ buf_attach_client({bufnr}, {client_id}) *vim.lsp.buf_attach_client()*
Without calling this, the server won't be notified of changes to a buffer.
Parameters: ~
- {bufnr} (number) Buffer handle, or 0 for current
- {client_id} (number) Client id
+ • {bufnr} (number) Buffer handle, or 0 for current
+ • {client_id} (number) Client id
buf_detach_client({bufnr}, {client_id}) *vim.lsp.buf_detach_client()*
Detaches client from the specified buffer. Note: While the server is
@@ -547,23 +550,23 @@ buf_detach_client({bufnr}, {client_id}) *vim.lsp.buf_detach_client()*
send notifications should it ignore this notification.
Parameters: ~
- {bufnr} (number) Buffer handle, or 0 for current
- {client_id} (number) Client id
+ • {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.
Parameters: ~
- {bufnr} (number) Buffer handle, or 0 for current
- {client_id} (number) the client id
+ • {bufnr} (number) Buffer handle, or 0 for current
+ • {client_id} (number) the client id
buf_notify({bufnr}, {method}, {params}) *vim.lsp.buf_notify()*
Send a notification to a server
Parameters: ~
- {bufnr} [number] (optional): The number of the buffer
- {method} [string]: Name of the request method
- {params} [string]: Arguments to send to the server
+ • {bufnr} (number|nil) The number of the buffer
+ • {method} (string) Name of the request method
+ • {params} (any) Arguments to send to the server
Return: ~
true if any client returns true; false otherwise
@@ -575,10 +578,10 @@ buf_request_all({bufnr}, {method}, {params}, {callback})
|vim.lsp.buf_request()| but the return result and callback are different.
Parameters: ~
- {bufnr} (number) Buffer handle, or 0 for current.
- {method} (string) LSP method name
- {params} (optional, table) Parameters to send to the server
- {callback} (function) The callback to call when all requests are
+ • {bufnr} (number) Buffer handle, or 0 for current.
+ • {method} (string) LSP method name
+ • {params} (table|nil) Parameters to send to the server
+ • {callback} (function) The callback to call when all requests are
finished.
Return: ~
@@ -594,11 +597,11 @@ buf_request_sync({bufnr}, {method}, {params}, {timeout_ms})
result is different. Wait maximum of {timeout_ms} (default 1000) ms.
Parameters: ~
- {bufnr} (number) Buffer handle, or 0 for current.
- {method} (string) LSP method name
- {params} (optional, table) Parameters to send to the server
- {timeout_ms} (optional, number, default=1000) Maximum time in
- milliseconds to wait for a result.
+ • {bufnr} (number) Buffer handle, or 0 for current.
+ • {method} (string) LSP method name
+ • {params} (table|nil) Parameters to send to the server
+ • {timeout_ms} (number|nil) Maximum time in milliseconds to wait for a
+ result. Defaults to 1000
Return: ~
Map of client_id:request_result. On timeout, cancel or error, returns
@@ -665,7 +668,7 @@ client_is_stopped({client_id}) *vim.lsp.client_is_stopped()*
Checks whether a client is stopped.
Parameters: ~
- {client_id} (Number)
+ • {client_id} (number)
Return: ~
true if client is stopped, false otherwise.
@@ -675,10 +678,10 @@ 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} (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: >
+ number as arguments. Example: >lua
vim.lsp.for_each_buffer_client(0, function(client, client_id, bufnr)
print(vim.inspect(client))
@@ -695,7 +698,7 @@ formatexpr({opts}) *vim.lsp.formatexpr()*
'v:lua.vim.lsp.formatexpr(#{timeout_ms:250})')`.
Parameters: ~
- {opts} (table) options for customizing the formatting expression
+ • {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.
@@ -704,7 +707,7 @@ 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
+ • {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
@@ -719,17 +722,17 @@ 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
+ (list) of buffer ids
get_client_by_id({client_id}) *vim.lsp.get_client_by_id()*
Gets a client by id, or nil if the id is invalid. The returned 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
@@ -744,8 +747,8 @@ omnifunc({findstart}, {base}) *vim.lsp.omnifunc()*
Implements 'omnifunc' compatible LSP completion.
Parameters: ~
- {findstart} 0 or 1, decides behavior
- {base} If findstart=0, text to match against
+ • {findstart} (number) 0 or 1, decides behavior
+ • {base} (number) findstart=0, text to match against
Return: ~
(number) Decided by {findstart}:
@@ -767,7 +770,7 @@ set_log_level({level}) *vim.lsp.set_log_level()*
Use `lsp.log_levels` for reverse lookup.
Parameters: ~
- {level} [number|string] the case insensitive level name or number
+ • {level} (number|string) the case insensitive level name or number
See also: ~
|vim.lsp.log_levels|
@@ -777,173 +780,170 @@ start({config}, {opts}) *vim.lsp.start()*
running client if one is found matching `name` and `root_dir`. Attaches
the current buffer to the client.
- Example:
->
+ Example: >lua
- 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]),
- })
+ 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.
+ See |vim.lsp.start_client()| for all available options. The most important
+ are:
+
+ • `name` arbitrary name for the LSP client. Should be unique per language
+ server.
+ • `cmd` command (in list form) used to start the language server. Must be
+ absolute, or found on `$PATH`. 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` list of `{ uri:string, name: string }` tables
+ specifying the project root folders used by the language server. If
+ `nil` the property is derived from `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
+ 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:
+ • {config} (table) Same configuration as documented in
+ |vim.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.
+ • bufnr (number) Buffer handle to attach to if starting or
+ re-using a client (0 for current).
Return: ~
- (number) client_id
+ (number|nil) client_id
start_client({config}) *vim.lsp.start_client()*
Starts and initializes a client with the given configuration.
- Parameter `cmd` is required.
-
- The following parameters describe fields in the {config} table.
-
- Parameters: ~
- {cmd} (required, string or list treated like
- |jobstart()|) Base command that initiates the LSP
- client.
- {cmd_cwd} (string, default=|getcwd()|) Directory to launch
- the `cmd` 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 with `k=v` pairs or both. Non-string values are coerced to
- string. Example: >
-
- { "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; }
+ Field `cmd` in {config} is required.
+
+ Parameters: ~
+ • {config} (table) Configuration for the server:
+ • cmd: (table|string|fun(dispatchers: table):table) command
+ string or list treated like |jobstart()|. The command must
+ launch the language server process. `cmd` can also be a
+ function that creates an RPC client. The function receives
+ a dispatchers table and must return a table with the
+ functions `request`, `notify`, `is_closing` and
+ `terminate` See |vim.lsp.rpc.request()| and
+ |vim.lsp.rpc.notify()| For TCP there is a built-in rpc
+ client factory: |vim.lsp.rpc.connect()|
+ • cmd_cwd: (string, default=|getcwd()|) Directory to launch
+ the `cmd` 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
+ with `k=v` pairs or both. Non-string values are coerced to string.
+ Example: >
+
+ { "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 rootPath will be derived from the
- first workspace folder in this list. See
- `workspaceFolders` in the LSP spec.
- {capabilities} Map overriding the default capabilities defined
- by |vim.lsp.protocol.make_client_capabilities()|,
- passed to the language server on initialization.
- Hint: use make_client_capabilities() and modify
- its result.
- • Note: To send an empty dictionary use
- `{[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
- case-sensitive.
- {commands} (table) Table that maps string of clientside
- commands to user-defined functions. Commands
- passed to start_client take precedence over the
- global command registry. Each key must be a
- unique command name, and the value is a function
- which is called if any LSP action (code action,
- code lenses, ...) triggers the command.
- {init_options} Values to pass in the initialization request as
- `initializationOptions`. See `initialize` in the
- LSP spec.
- {name} (string, default=client-id) Name in log messages.
- {get_language_id} function(bufnr, filetype) -> language ID as
- string. Defaults to the filetype.
- {offset_encoding} (default="utf-16") One of "utf-8", "utf-16", or
- "utf-32" which is the encoding that the LSP
- server expects. Client does not verify this is
- correct.
- {on_error} Callback with parameters (code, ...), invoked
- when the client operation throws an error. `code`
- is a number describing the error. Other arguments
- may be passed depending on the error kind. See
- |vim.lsp.rpc.client_errors| for possible errors.
- Use `vim.lsp.rpc.client_errors[code]` to get
- human-friendly name.
- {before_init} Callback with parameters (initialize_params,
- config) invoked before the LSP "initialize"
- phase, where `params` contains the parameters
- being sent to the server and `config` is the
- config that was passed to
- |vim.lsp.start_client()|. You can use this to
- modify parameters before they are sent.
- {on_init} Callback (client, initialize_result) invoked
- after LSP "initialize", where `result` is a table
- of `capabilities` and anything else the server
- may send. For example, clangd sends
- `initialize_result.offsetEncoding` if
- `capabilities.offsetEncoding` was sent to it. You
- can only modify the `client.offset_encoding` here
- before any notifications are sent. Most language
- servers expect to be sent client specified
- settings after initialization. Neovim does not
- make this assumption. A
- `workspace/didChangeConfiguration` notification
- should be sent to the server during on_init.
- {on_exit} Callback (code, signal, client_id) invoked on
- client exit.
- • code: exit code of the process
- • signal: number describing the signal used to
- terminate (if any)
- • client_id: client handle
- {on_attach} Callback (client, bufnr) invoked when client
- attaches to a buffer.
- {trace} "off" | "messages" | "verbose" | nil passed
- directly to the language server in the initialize
- request. Invalid/empty values will default to
- "off"
- {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 150):
- Debounce didChange notifications to the server
- by the given number in milliseconds. No
- debounce occurs if nil
- • exit_timeout (number|boolean, default false):
- Milliseconds to wait for server to exit cleanly
- after sending the 'shutdown' request before
- sending kill -15. If set to false, nvim exits
- immediately after sending the 'shutdown'
- request to the server.
- {root_dir} (string) Directory where the LSP server will base
- its workspaceFolders, rootUri, and rootPath on
- initialization.
+ • 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 rootPath will be derived from the first
+ workspace folder in this list. See `workspaceFolders` in
+ the LSP spec.
+ • capabilities: Map overriding the default capabilities
+ defined by |vim.lsp.protocol.make_client_capabilities()|,
+ passed to the language server on initialization. Hint: use
+ make_client_capabilities() and modify its result.
+ • Note: To send an empty dictionary use
+ `{[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 case-sensitive.
+ • commands: table Table that maps string of clientside
+ commands to user-defined functions. Commands passed to
+ start_client take precedence over the global command
+ registry. Each key must be a unique command name, and the
+ value is a function which is called if any LSP action
+ (code action, code lenses, ...) triggers the command.
+ • init_options Values to pass in the initialization request
+ as `initializationOptions`. See `initialize` in the LSP
+ spec.
+ • name: (string, default=client-id) Name in log messages.
+ • get_language_id: function(bufnr, filetype) -> language ID
+ as string. Defaults to the filetype.
+ • offset_encoding: (default="utf-16") One of "utf-8",
+ "utf-16", or "utf-32" which is the encoding that the LSP
+ server expects. Client does not verify this is correct.
+ • on_error: Callback with parameters (code, ...), invoked
+ when the client operation throws an error. `code` is a
+ number describing the error. Other arguments may be passed
+ depending on the error kind. See
+ `vim.lsp.rpc.client_errors` for possible errors. Use
+ `vim.lsp.rpc.client_errors[code]` to get human-friendly
+ name.
+ • before_init: Callback with parameters (initialize_params,
+ config) invoked before the LSP "initialize" phase, where
+ `params` contains the parameters being sent to the server
+ and `config` is the config that was passed to
+ |vim.lsp.start_client()|. You can use this to modify
+ parameters before they are sent.
+ • on_init: Callback (client, initialize_result) invoked
+ after LSP "initialize", where `result` is a table of
+ `capabilities` and anything else the server may send. For
+ example, clangd sends `initialize_result.offsetEncoding`
+ if `capabilities.offsetEncoding` was sent to it. You can
+ only modify the `client.offset_encoding` here before any
+ notifications are sent. Most language servers expect to be
+ sent client specified settings after initialization.
+ Neovim does not make this assumption. A
+ `workspace/didChangeConfiguration` notification should be
+ sent to the server during on_init.
+ • on_exit Callback (code, signal, client_id) invoked on
+ client exit.
+ • code: exit code of the process
+ • signal: number describing the signal used to terminate
+ (if any)
+ • client_id: client handle
+
+ • on_attach: Callback (client, bufnr) invoked when client
+ attaches to a buffer.
+ • trace: ("off" | "messages" | "verbose" | nil) passed
+ directly to the language server in the initialize request.
+ Invalid/empty values will default to "off"
+ • 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 150): Debounce
+ didChange notifications to the server by the given
+ number in milliseconds. No debounce occurs if nil
+ • exit_timeout (number|boolean, default false):
+ Milliseconds to wait for server to exit cleanly after
+ sending the "shutdown" request before sending kill -15.
+ If set to false, nvim exits immediately after sending
+ the "shutdown" request to the server.
+
+ • root_dir: (string) Directory where the LSP server will
+ base its workspaceFolders, rootUri, and rootPath on
+ initialization.
Return: ~
Client id. |vim.lsp.get_client_by_id()| Note: client may not be fully
@@ -953,19 +953,18 @@ start_client({config}) *vim.lsp.start_client()*
stop_client({client_id}, {force}) *vim.lsp.stop_client()*
Stops a client(s).
- You can also use the `stop()` function on a |vim.lsp.client| object. To
- stop all clients:
->
+ You can also use the `stop()` function on a |vim.lsp.client| object. To stop all clients: >lua
- vim.lsp.stop_client(vim.lsp.get_active_clients())
+ vim.lsp.stop_client(vim.lsp.get_active_clients())
<
By default asks the server to shutdown, unless stop was requested already
for this client, then force-shutdown is attempted.
Parameters: ~
- {client_id} client id or |vim.lsp.client| object, or list thereof
- {force} (boolean) (optional) shutdown forcefully
+ • {client_id} number|table id or |vim.lsp.client| object, or list
+ thereof
+ • {force} (boolean|nil) shutdown forcefully
tagfunc({...}) *vim.lsp.tagfunc()*
Provides an interface between the built-in client and 'tagfunc'.
@@ -976,8 +975,8 @@ tagfunc({...}) *vim.lsp.tagfunc()*
LSP servers, falls back to using built-in tags.
Parameters: ~
- {pattern} Pattern used to find a workspace symbol
- {flags} See |tag-function|
+ • {pattern} (string) Pattern used to find a workspace symbol
+ • {flags} (string) See |tag-function|
Return: ~
A list of matching tags
@@ -986,8 +985,8 @@ with({handler}, {override_config}) *vim.lsp.with()*
Function to manage overriding defaults for LSP handlers.
Parameters: ~
- {handler} (function) See |lsp-handler|
- {override_config} (table) Table containing the keys to override
+ • {handler} (function) See |lsp-handler|
+ • {override_config} (table) Table containing the keys to override
behavior of the {handler}
@@ -1006,13 +1005,15 @@ code_action({options}) *vim.lsp.buf.code_action()*
Selects a code action available at the current cursor position.
Parameters: ~
- {options} (table|nil) Optional table which holds the following
+ • {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`.
+ • triggerKind (number|nil): The reason why code actions
+ were requested.
• filter: (function|nil) Predicate taking an `CodeAction`
and returning a boolean.
@@ -1027,19 +1028,20 @@ code_action({options}) *vim.lsp.buf.code_action()*
See also: ~
https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction
+ vim.lsp.protocol.constants.CodeActionTriggerKind
completion({context}) *vim.lsp.buf.completion()*
Retrieves the completion items at the current cursor position. Can only be
called in Insert mode.
Parameters: ~
- {context} (context support not yet implemented) Additional
+ • {context} (context support not yet implemented) Additional
information about the context in which a completion was
triggered (how it was triggered, and by which trigger
character, if applicable)
See also: ~
- |vim.lsp.protocol.constants.CompletionTriggerKind|
+ vim.lsp.protocol.constants.CompletionTriggerKind
declaration({options}) *vim.lsp.buf.declaration()*
Jumps to the declaration of the symbol under the cursor.
@@ -1048,7 +1050,7 @@ declaration({options}) *vim.lsp.buf.declaration()*
|vim.lsp.buf.definition()| instead.
Parameters: ~
- {options} (table|nil) additional options
+ • {options} (table|nil) additional options
• reuse_win: (boolean) Jump to existing window if buffer is
already open.
• on_list: (function) handler for list results. See
@@ -1058,7 +1060,7 @@ definition({options}) *vim.lsp.buf.definition()*
Jumps to the definition of the symbol under the cursor.
Parameters: ~
- {options} (table|nil) additional options
+ • {options} (table|nil) additional options
• reuse_win: (boolean) Jump to existing window if buffer is
already open.
• on_list: (function) handler for list results. See
@@ -1067,22 +1069,22 @@ definition({options}) *vim.lsp.buf.definition()*
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`, e.g.:
->
- autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()
- autocmd CursorHoldI <buffer> lua vim.lsp.buf.document_highlight()
- autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references()
+ by events such as `CursorHold` , e.g.: >vim
+ autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()
+ autocmd CursorHoldI <buffer> lua vim.lsp.buf.document_highlight()
+ autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references()
<
Note: Usage of |vim.lsp.buf.document_highlight()| requires the following
highlight groups to be defined or you won't be able to see the actual
- highlights. |LspReferenceText| |LspReferenceRead| |LspReferenceWrite|
+ highlights. |hl-LspReferenceText| |hl-LspReferenceRead|
+ |hl-LspReferenceWrite|
document_symbol({options}) *vim.lsp.buf.document_symbol()*
Lists all symbols in the current buffer in the quickfix window.
Parameters: ~
- {options} (table|nil) additional options
+ • {options} (table|nil) additional options
• on_list: (function) handler for list results. See
|lsp-on-list-handler|
@@ -1090,7 +1092,7 @@ execute_command({command_params}) *vim.lsp.buf.execute_command()*
Executes an LSP server command.
Parameters: ~
- {command_params} (table) 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
@@ -1100,7 +1102,7 @@ format({options}) *vim.lsp.buf.format()*
server clients.
Parameters: ~
- {options} table|nil Optional table which holds the following optional
+ • {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
@@ -1114,12 +1116,12 @@ format({options}) *vim.lsp.buf.format()*
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: • >
+ Clients matching the predicate are included. Example: • >lua
- -- Never request typescript-language-server for formatting
- vim.lsp.buf.format {
- filter = function(client) return client.name ~= "tsserver" end
- }
+ -- 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
@@ -1128,59 +1130,11 @@ format({options}) *vim.lsp.buf.format()*
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} (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
-
- *vim.lsp.buf.formatting_seq_sync()*
-formatting_seq_sync({options}, {timeout_ms}, {order})
- Formats the current buffer by sequentially requesting formatting from
- attached clients.
-
- Useful when multiple clients with formatting capability are attached.
-
- Since it's synchronous, can be used for running on save, to make sure
- buffer is formatted prior to being saved. {timeout_ms} is passed on to the
- |vim.lsp.client| `request_sync` method. Example: >
-
- vim.api.nvim_command[[autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_seq_sync()]]
-<
-
- Parameters: ~
- {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})
- Performs |vim.lsp.buf.formatting()| synchronously.
-
- Useful for running on save, to make sure buffer is formatted prior to
- being saved. {timeout_ms} is passed on to |vim.lsp.buf_request_sync()|.
- Example:
->
-
- autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_sync()
-<
-
- Parameters: ~
- {options} (table|nil) with valid `FormattingOptions` entries
- {timeout_ms} (number) Request timeout
-
- See also: ~
- |vim.lsp.buf.formatting_seq_sync|
+ • range (table|nil) Range to format. Table must contain
+ `start` and `end` keys with {row, col} tuples using (1,0)
+ indexing. Defaults to current selection in visual mode
+ Defaults to `nil` in other modes, formatting the full
+ buffer
hover() *vim.lsp.buf.hover()*
Displays hover information about the symbol under the cursor in a floating
@@ -1191,14 +1145,14 @@ implementation({options}) *vim.lsp.buf.implementation()*
quickfix window.
Parameters: ~
- {options} (table|nil) additional options
+ • {options} (table|nil) additional options
• on_list: (function) handler for list results. See
|lsp-on-list-handler|
incoming_calls() *vim.lsp.buf.incoming_calls()*
Lists all the call sites of the symbol under the cursor in the |quickfix|
window. If the symbol can resolve to multiple items, the user can pick one
- in the |inputlist|.
+ in the |inputlist()|.
list_workspace_folders() *vim.lsp.buf.list_workspace_folders()*
List workspace folders.
@@ -1206,41 +1160,15 @@ list_workspace_folders() *vim.lsp.buf.list_workspace_folders()*
outgoing_calls() *vim.lsp.buf.outgoing_calls()*
Lists all the items that are called by the symbol under the cursor in the
|quickfix| window. If the symbol can resolve to multiple items, the user
- can pick one in the |inputlist|.
-
- *vim.lsp.buf.range_code_action()*
-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:
- • 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`.
- {start_pos} ({number, number}, optional) mark-indexed position.
- Defaults to the start of the last visual selection.
- {end_pos} ({number, number}, optional) mark-indexed position.
- Defaults to the end of the last visual selection.
-
- *vim.lsp.buf.range_formatting()*
-range_formatting({options}, {start_pos}, {end_pos})
- Formats a given range.
-
- Parameters: ~
- {options} Table with valid `FormattingOptions` entries.
- {start_pos} ({number, number}, optional) mark-indexed position.
- Defaults to the start of the last visual selection.
- {end_pos} ({number, number}, optional) mark-indexed position.
- Defaults to the end of the last visual selection.
+ can pick one in the |inputlist()|.
references({context}, {options}) *vim.lsp.buf.references()*
Lists all the references to the symbol under the cursor in the quickfix
window.
Parameters: ~
- {context} (table) Context for the request
- {options} (table|nil) additional options
+ • {context} (table|nil) Context for the request
+ • {options} (table|nil) additional options
• on_list: (function) handler for list results. See
|lsp-on-list-handler|
@@ -1256,9 +1184,9 @@ rename({new_name}, {options}) *vim.lsp.buf.rename()*
Renames all references to the symbol under the cursor.
Parameters: ~
- {new_name} (string|nil) If not provided, the user will be prompted
+ • {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
+ • {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.
@@ -1280,7 +1208,7 @@ 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
+ • {options} (table|nil) additional options
• reuse_win: (boolean) Jump to existing window if buffer is
already open.
• on_list: (function) handler for list results. See
@@ -1294,8 +1222,8 @@ workspace_symbol({query}, {options}) *vim.lsp.buf.workspace_symbol()*
string means no filtering is done.
Parameters: ~
- {query} (string, optional)
- {options} (table|nil) additional options
+ • {query} (string, optional)
+ • {options} (table|nil) additional options
• on_list: (function) handler for list results. See
|lsp-on-list-handler|
@@ -1308,14 +1236,14 @@ 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})
|lsp-handler| for the method "textDocument/publishDiagnostics"
See |vim.diagnostic.config()| for configuration options. Handler-specific
- configuration can be set using |vim.lsp.with()|: >
+ configuration can be set using |vim.lsp.with()|: >lua
vim.lsp.handlers["textDocument/publishDiagnostics"] = vim.lsp.with(
vim.lsp.diagnostic.on_publish_diagnostics, {
@@ -1337,25 +1265,32 @@ on_publish_diagnostics({_}, {result}, {ctx}, {config})
<
Parameters: ~
- {config} (table) Configuration table (see |vim.diagnostic.config()|).
+ • {config} (table) Configuration table (see |vim.diagnostic.config()|).
==============================================================================
Lua module: vim.lsp.codelens *lsp-codelens*
+clear({client_id}, {bufnr}) *vim.lsp.codelens.clear()*
+ Clear the lenses
+
+ Parameters: ~
+ • {client_id} (number|nil) filter by client_id. All clients if nil
+ • {bufnr} (number|nil) filter by buffer. All buffers if nil
+
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 current buffer.
+ • {bufnr} (number) Buffer number. 0 can be used for the current buffer.
Return: ~
(table) (`CodeLens[]`)
@@ -1368,8 +1303,9 @@ refresh() *vim.lsp.codelens.refresh()*
Refresh the codelens for the current buffer
It is recommended to trigger this using an autocmd or via keymap.
->
- autocmd BufEnter,CursorHold,InsertLeave <buffer> lua vim.lsp.codelens.refresh()
+
+ Example: >vim
+ autocmd BufEnter,CursorHold,InsertLeave <buffer> lua vim.lsp.codelens.refresh()
<
run() *vim.lsp.codelens.run()*
@@ -1379,27 +1315,89 @@ 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)
+
+
+==============================================================================
+Lua module: vim.lsp.semantic_tokens *lsp-semantic_tokens*
+
+force_refresh({bufnr}) *vim.lsp.semantic_tokens.force_refresh()*
+ Force a refresh of all semantic tokens
+
+ Only has an effect if the buffer is currently active for semantic token
+ highlighting (|vim.lsp.semantic_tokens.start()| has been called for it)
+
+ Parameters: ~
+ • {bufnr} (nil|number) default: current buffer
+
+ *vim.lsp.semantic_tokens.get_at_pos()*
+get_at_pos({bufnr}, {row}, {col})
+ Return the semantic token(s) at the given position. If called without
+ arguments, returns the token under the cursor.
+
+ Parameters: ~
+ • {bufnr} (number|nil) Buffer number (0 for current buffer, default)
+ • {row} (number|nil) Position row (default cursor position)
+ • {col} (number|nil) Position column (default cursor position)
+
+ Return: ~
+ (table|nil) List of tokens at position
+
+start({bufnr}, {client_id}, {opts}) *vim.lsp.semantic_tokens.start()*
+ Start the semantic token highlighting engine for the given buffer with the
+ given client. The client must already be attached to the buffer.
+
+ NOTE: This is currently called automatically by
+ |vim.lsp.buf_attach_client()|. To opt-out of semantic highlighting with a
+ server that supports it, you can delete the semanticTokensProvider table
+ from the {server_capabilities} of your client in your |LspAttach| callback
+ or your configuration's `on_attach` callback: >lua
+
+ client.server_capabilities.semanticTokensProvider = nil
+<
+
+ Parameters: ~
+ • {bufnr} (number)
+ • {client_id} (number)
+ • {opts} (nil|table) Optional keyword arguments
+ • debounce (number, default: 200): Debounce token
+ requests to the server by the given number in
+ milliseconds
+
+stop({bufnr}, {client_id}) *vim.lsp.semantic_tokens.stop()*
+ Stop the semantic token highlighting engine for the given buffer with the
+ given client.
+
+ NOTE: This is automatically called by a |LspDetach| autocmd that is set up
+ as part of `start()`, so you should only need this function to manually
+ disengage the semantic token engine without fully detaching the LSP client
+ from the buffer.
+
+ Parameters: ~
+ • {bufnr} (number)
+ • {client_id} (number)
==============================================================================
Lua module: vim.lsp.handlers *lsp-handlers*
hover({_}, {result}, {ctx}, {config}) *vim.lsp.handlers.hover()*
- |lsp-handler| for the method "textDocument/hover" >
-
- vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(
- vim.lsp.handlers.hover, {
- -- Use a sharp border with `FloatBorder` highlights
- border = "single"
- }
- )
+ |lsp-handler| for the method "textDocument/hover" >lua
+
+ vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(
+ vim.lsp.handlers.hover, {
+ -- Use a sharp border with `FloatBorder` highlights
+ border = "single",
+ -- add the title in hover float window
+ title = "hover"
+ }
+ )
<
Parameters: ~
- {config} (table) Configuration table.
+ • {config} (table) Configuration table.
• border: (default=nil)
• Add borders to the floating window
• See |nvim_open_win()|
@@ -1407,21 +1405,21 @@ hover({_}, {result}, {ctx}, {config}) *vim.lsp.handlers.hover()*
*vim.lsp.handlers.signature_help()*
signature_help({_}, {result}, {ctx}, {config})
|lsp-handler| for the method "textDocument/signatureHelp". The active
- parameter is highlighted with |hl-LspSignatureActiveParameter|. >
-
- vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with(
- vim.lsp.handlers.signature_help, {
- -- Use a sharp border with `FloatBorder` highlights
- border = "single"
- }
- )
+ parameter is highlighted with |hl-LspSignatureActiveParameter|. >lua
+
+ vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with(
+ vim.lsp.handlers.signature_help, {
+ -- Use a sharp border with `FloatBorder` highlights
+ border = "single"
+ }
+ )
<
Parameters: ~
- {config} (table) Configuration table.
+ • {config} (table) Configuration table.
• border: (default=nil)
• Add borders to the floating window
- • See |vim.api.nvim_open_win()|
+ • See |nvim_open_win()|
==============================================================================
@@ -1433,8 +1431,8 @@ apply_text_document_edit({text_document_edit}, {index}, {offset_encoding})
document.
Parameters: ~
- {text_document_edit} table: a `TextDocumentEdit` object
- {index} number: Optional index of the edit, if from a
+ • {text_document_edit} (table) a `TextDocumentEdit` object
+ • {index} (number) Optional index of the edit, if from a
list of edits (or nil, if not from a list)
See also: ~
@@ -1445,9 +1443,9 @@ 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
+ • {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
@@ -1457,37 +1455,37 @@ apply_workspace_edit({workspace_edit}, {offset_encoding})
Applies a `WorkspaceEdit`.
Parameters: ~
- {workspace_edit} (table) `WorkspaceEdit`
- {offset_encoding} (string) utf-8|utf-16|utf-32 (required)
+ • {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` objects to
+ • {bufnr} (number) Buffer id
+ • {references} (table) List of `DocumentHighlight` objects to
highlight
- {offset_encoding} (string) One of "utf-8", "utf-16", "utf-32".
+ • {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
+ https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentContentChangeEvent
*vim.lsp.util.character_offset()*
character_offset({buf}, {row}, {col}, {offset_encoding})
Returns the UTF-32 and UTF-16 offsets for a position in a certain buffer.
Parameters: ~
- {buf} (number) buffer number (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
+ • {buf} (number) buffer number (0 for current)
+ • {row} 0-indexed line
+ • {col} 0-indexed byte offset in line
+ • {offset_encoding} (string) utf-8|utf-16|utf-32|nil defaults to
`offset_encoding` of first client of `buf`
Return: ~
@@ -1502,9 +1500,9 @@ convert_input_to_markdown_lines({input}, {contents})
`textDocument/signatureHelp`, and potentially others.
Parameters: ~
- {input} (`MarkedString` | `MarkedString[]` | `MarkupContent`)
- {contents} (table, optional, default `{}`) List of strings to extend
- with converted lines
+ • {input} (`MarkedString` | `MarkedString[]` | `MarkupContent`)
+ • {contents} (table|nil) List of strings to extend with converted
+ lines. Defaults to {}.
Return: ~
{contents}, extended with lines of converted markdown.
@@ -1517,14 +1515,14 @@ convert_signature_help_to_markdown_lines({signature_help}, {ft}, {triggers})
Converts `textDocument/SignatureHelp` response to markdown lines.
Parameters: ~
- {signature_help} Response of `textDocument/SignatureHelp`
- {ft} optional filetype that will be use as the `lang` for
+ • {signature_help} Response of `textDocument/SignatureHelp`
+ • {ft} optional filetype that will be use as the `lang` for
the label markdown code block
- {triggers} optional list of trigger characters from the lsp
+ • {triggers} optional list of trigger characters from the lsp
server. used to better determine parameter offsets
Return: ~
- list of lines of converted markdown.
+ (list) of lines of converted markdown.
See also: ~
https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp
@@ -1534,7 +1532,7 @@ extract_completion_items({result})
Can be used to extract the completion items from a `textDocument/completion` request, which may return one of `CompletionItem[]` , `CompletionList` or null.
Parameters: ~
- {result} (table) The result of a `textDocument/completion` request
+ • {result} (table) The result of a `textDocument/completion` request
Return: ~
(table) List of completion items
@@ -1546,26 +1544,26 @@ get_effective_tabstop({bufnr}) *vim.lsp.util.get_effective_tabstop()*
Returns indentation size.
Parameters: ~
- {bufnr} (number|nil): Buffer handle, defaults to current
+ • {bufnr} (number|nil) Buffer handle, defaults to current
Return: ~
(number) indentation size
See also: ~
- |shiftwidth|
+ 'shiftwidth'
*vim.lsp.util.jump_to_location()*
jump_to_location({location}, {offset_encoding}, {reuse_win})
Jumps to a location.
Parameters: ~
- {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.
+ • {location} (table) (`Location`|`LocationLink`)
+ • {offset_encoding} "utf-8" | "utf-16" | "utf-32"
+ • {reuse_win} (boolean|nil) Jump to existing window if buffer is
+ already open.
Return: ~
- `true` if the jump succeeded
+ (boolean) `true` if the jump succeeded
*vim.lsp.util.locations_to_items()*
locations_to_items({locations}, {offset_encoding})
@@ -1576,8 +1574,8 @@ locations_to_items({locations}, {offset_encoding})
|setloclist()|.
Parameters: ~
- {locations} (table) list of `Location`s or `LocationLink`s
- {offset_encoding} (string) offset_encoding for locations
+ • {locations} (table) list of `Location`s or `LocationLink`s
+ • {offset_encoding} (string) offset_encoding for locations
utf-8|utf-16|utf-32
Return: ~
@@ -1587,8 +1585,8 @@ lookup_section({settings}, {section}) *vim.lsp.util.lookup_section()*
Helper function to return nested values in language server settings
Parameters: ~
- {settings} a table of language server settings
- {section} a string indicating the field of the settings table
+ • {settings} a table of language server settings
+ • {section} a string indicating the field of the settings table
Return: ~
(table or string) The value of settings accessed via section
@@ -1599,14 +1597,15 @@ make_floating_popup_options({width}, {height}, {opts})
table can be passed to |nvim_open_win()|.
Parameters: ~
- {width} (number) window width (in character cells)
- {height} (number) window height (in character cells)
- {opts} (table, optional)
+ • {width} (number) window width (in character cells)
+ • {height} (number) window height (in character cells)
+ • {opts} (table, optional)
• offset_x (number) offset to add to `col`
• offset_y (number) offset to add to `row`
• border (string or table) override `border`
• focusable (string or table) override `focusable`
• zindex (string or table) override `zindex`, defaults to 50
+ • relative ("mouse"|"cursor") defaults to "cursor"
Return: ~
(table) Options
@@ -1617,7 +1616,7 @@ make_formatting_params({options})
cursor position.
Parameters: ~
- {options} (table|nil) with valid `FormattingOptions` entries
+ • {options} (table|nil) with valid `FormattingOptions` entries
Return: ~
`DocumentFormattingParams` object
@@ -1631,13 +1630,13 @@ make_given_range_params({start_pos}, {end_pos}, {bufnr}, {offset_encoding})
similar to |vim.lsp.util.make_range_params()|.
Parameters: ~
- {start_pos} number[]|nil {row, col} mark-indexed position.
+ • {start_pos} number[]|nil {row, col} mark-indexed position.
Defaults to the start of the last visual selection.
- {end_pos} number[]|nil {row, col} mark-indexed position.
+ • {end_pos} number[]|nil {row, col} mark-indexed position.
Defaults to the end of the last visual selection.
- {bufnr} (number|nil) buffer handle or 0 for current,
+ • {bufnr} (number|nil) buffer handle or 0 for current,
defaults to current
- {offset_encoding} "utf-8"|"utf-16"|"utf-32"|nil defaults to
+ • {offset_encoding} "utf-8"|"utf-16"|"utf-32"|nil defaults to
`offset_encoding` of first client of `bufnr`
Return: ~
@@ -1650,9 +1649,9 @@ make_position_params({window}, {offset_encoding})
cursor position.
Parameters: ~
- {window} number|nil: window handle or 0 for current,
+ • {window} (number|nil) window handle or 0 for current,
defaults to current
- {offset_encoding} (string) utf-8|utf-16|utf-32|nil defaults to
+ • {offset_encoding} (string|nil) utf-8|utf-16|utf-32|nil defaults to
`offset_encoding` of first client of buffer of
`window`
@@ -1670,9 +1669,9 @@ make_range_params({window}, {offset_encoding})
`textDocument/rangeFormatting`.
Parameters: ~
- {window} number|nil: window handle or 0 for current,
+ • {window} (number|nil) window handle or 0 for current,
defaults to current
- {offset_encoding} "utf-8"|"utf-16"|"utf-32"|nil defaults to
+ • {offset_encoding} "utf-8"|"utf-16"|"utf-32"|nil defaults to
`offset_encoding` of first client of buffer of
`window`
@@ -1685,7 +1684,7 @@ make_text_document_params({bufnr})
Creates a `TextDocumentIdentifier` object for the current buffer.
Parameters: ~
- {bufnr} number|nil: Buffer handle, defaults to current
+ • {bufnr} (number|nil) Buffer handle, defaults to current
Return: ~
`TextDocumentIdentifier`
@@ -1698,18 +1697,18 @@ make_workspace_params({added}, {removed})
Create the workspace params
Parameters: ~
- {added}
- {removed}
+ • {added}
+ • {removed}
*vim.lsp.util.open_floating_preview()*
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 are passed
- on to |vim.api.nvim_open_win()|)
+ • {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 |nvim_open_win()|)
• height: (number) height of floating window
• width: (number) width of floating window
• wrap: (boolean, default true) wrap long lines
@@ -1737,7 +1736,7 @@ 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
@@ -1751,7 +1750,7 @@ preview_location({location}, {opts}) *vim.lsp.util.preview_location()*
definition)
Parameters: ~
- {location} a single `Location` or `LocationLink`
+ • {location} a single `Location` or `LocationLink`
Return: ~
(bufnr,winnr) buffer and window number of floating window or nil
@@ -1760,7 +1759,7 @@ rename({old_fname}, {new_fname}, {opts}) *vim.lsp.util.rename()*
Rename old_fname to new_fname
Parameters: ~
- {opts} (table)
+ • {opts} (table)
set_lines({lines}, {A}, {B}, {new_lines}) *vim.lsp.util.set_lines()*
Replaces text in a range with new text.
@@ -1768,14 +1767,30 @@ set_lines({lines}, {A}, {B}, {new_lines}) *vim.lsp.util.set_lines()*
CAUTION: Changes in-place!
Parameters: ~
- {lines} (table) Original list of strings
- {A} (table) Start position; a 2-tuple of {line, col} numbers
- {B} (table) End position; a 2-tuple of {line, col} numbers
- {new_lines} A list of strings to replace the original
+ • {lines} (table) Original list of strings
+ • {A} (table) Start position; a 2-tuple of {line, col} numbers
+ • {B} (table) End position; a 2-tuple of {line, col} numbers
+ • {new_lines} A list of strings to replace the original
Return: ~
(table) The modified {lines} object
+ *vim.lsp.util.show_document()*
+show_document({location}, {offset_encoding}, {opts})
+ Shows document and optionally jumps to the location.
+
+ Parameters: ~
+ • {location} (table) (`Location`|`LocationLink`)
+ • {offset_encoding} "utf-8" | "utf-16" | "utf-32"
+ • {opts} (table|nil) options
+ • reuse_win (boolean) Jump to existing window if
+ buffer is already open.
+ • focus (boolean) Whether to focus/jump to location
+ if possible. Defaults to true.
+
+ Return: ~
+ (boolean) `true` if succeeded
+
*vim.lsp.util.stylize_markdown()*
stylize_markdown({bufnr}, {contents}, {opts})
Converts markdown into syntax highlighted regions by stripping the code
@@ -1789,8 +1804,8 @@ stylize_markdown({bufnr}, {contents}, {opts})
`open_floating_preview` instead
Parameters: ~
- {contents} (table) of lines to show in window
- {opts} dictionary with optional fields
+ • {contents} (table) of lines to show in window
+ • {opts} dictionary with optional fields
• height of floating window
• width of floating window
• wrap_at character to wrap at for computing height
@@ -1807,7 +1822,7 @@ symbols_to_items({symbols}, {bufnr}) *vim.lsp.util.symbols_to_items()*
Converts symbols to quickfix list items.
Parameters: ~
- {symbols} DocumentSymbol[] or SymbolInformation[]
+ • {symbols} DocumentSymbol[] or SymbolInformation[]
*vim.lsp.util.text_document_completion_list_to_complete_items()*
text_document_completion_list_to_complete_items({result}, {prefix})
@@ -1815,10 +1830,10 @@ text_document_completion_list_to_complete_items({result}, {prefix})
vim-compatible |complete-items|.
Parameters: ~
- {result} The result of a `textDocument/completion` call, e.g. from
+ • {result} The result of a `textDocument/completion` call, e.g. from
|vim.lsp.buf.completion()|, which may be one of
`CompletionItem[]`, `CompletionList` or `null`
- {prefix} (string) the prefix to filter the completion items
+ • {prefix} (string) the prefix to filter the completion items
Return: ~
{ matches = complete-items table, incomplete = bool }
@@ -1830,7 +1845,7 @@ trim_empty_lines({lines}) *vim.lsp.util.trim_empty_lines()*
Removes empty lines from the beginning and end.
Parameters: ~
- {lines} (table) list of lines to trim
+ • {lines} (table) list of lines to trim
Return: ~
(table) trimmed list of lines
@@ -1843,10 +1858,10 @@ try_trim_markdown_code_blocks({lines})
CAUTION: Modifies the input in-place!
Parameters: ~
- {lines} (table) list of lines
+ • {lines} (table) list of lines
Return: ~
- (string) filetype or 'markdown' if it was unchanged.
+ (string) filetype or "markdown" if it was unchanged.
==============================================================================
@@ -1868,20 +1883,20 @@ 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
+ • {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.
Parameters: ~
- {level} (string or number) One of `vim.lsp.log.levels`
+ • {level} (string|number) One of `vim.lsp.log.levels`
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
@@ -1890,11 +1905,22 @@ should_log({level}) *vim.lsp.log.should_log()*
==============================================================================
Lua module: vim.lsp.rpc *lsp-rpc*
+connect({host}, {port}) *vim.lsp.rpc.connect()*
+ Create a LSP RPC client factory that connects via TCP to the given host
+ and port
+
+ Parameters: ~
+ • {host} (string)
+ • {port} (number)
+
+ Return: ~
+ (function)
+
format_rpc_error({err}) *vim.lsp.rpc.format_rpc_error()*
Constructs an error message from an LSP error object.
Parameters: ~
- {err} (table) The error object
+ • {err} (table) The error object
Return: ~
(string) The formatted error message
@@ -1903,8 +1929,8 @@ notify({method}, {params}) *vim.lsp.rpc.notify()*
Sends a notification to the LSP server.
Parameters: ~
- {method} (string) The invoked LSP method
- {params} (table): Parameters for the invoked LSP method
+ • {method} (string) The invoked LSP method
+ • {params} (table|nil) Parameters for the invoked LSP method
Return: ~
(bool) `true` if notification could be sent, `false` if not
@@ -1914,10 +1940,11 @@ request({method}, {params}, {callback}, {notify_reply_callback})
Sends a request to the LSP server and runs {callback} upon response.
Parameters: ~
- {method} (string) The invoked LSP method
- {params} (table) Parameters for the invoked LSP method
- {callback} (function) Callback to invoke
- {notify_reply_callback} (function|nil) Callback to invoke as soon as
+ • {method} (string) The invoked LSP method
+ • {params} (table|nil) Parameters for the invoked LSP
+ method
+ • {callback} (function) Callback to invoke
+ • {notify_reply_callback} (function|nil) Callback to invoke as soon as
a request is no longer pending
Return: ~
@@ -1929,28 +1956,29 @@ rpc_response_error({code}, {message}, {data})
Creates an RPC response object/table.
Parameters: ~
- {code} (number) RPC error code defined in
+ • {code} (number) RPC error code defined in
`vim.lsp.protocol.ErrorCodes`
- {message} (string|nil) arbitrary message to send to server
- {data} any|nil arbitrary data to send to server
+ • {message} (string|nil) arbitrary message to send to server
+ • {data} any|nil arbitrary data to send to server
*vim.lsp.rpc.start()*
start({cmd}, {cmd_args}, {dispatchers}, {extra_spawn_params})
Starts an LSP server process and create an LSP RPC client object to
- interact with it. Communication with the server is currently limited to
- stdio.
+ interact with it. Communication with the spawned process happens via
+ stdio. For communication via TCP, spawn a process manually and use
+ |vim.lsp.rpc.connect()|
Parameters: ~
- {cmd} (string) Command to start the LSP server.
- {cmd_args} (table) List of additional string arguments to
+ • {cmd} (string) Command to start the LSP server.
+ • {cmd_args} (table) List of additional string arguments to
pass to {cmd}.
- {dispatchers} (table|nil) Dispatchers for LSP message types.
+ • {dispatchers} (table|nil) Dispatchers for LSP message types.
Valid dispatcher names are:
• `"notification"`
• `"server_request"`
• `"on_error"`
• `"on_exit"`
- {extra_spawn_params} (table|nil) Additional context for the LSP
+ • {extra_spawn_params} (table|nil) Additional context for the LSP
server process. May contain:
• {cwd} (string) Working directory for the LSP
server process
@@ -1962,11 +1990,8 @@ start({cmd}, {cmd_args}, {dispatchers}, {extra_spawn_params})
Methods:
• `notify()` |vim.lsp.rpc.notify()|
• `request()` |vim.lsp.rpc.request()|
-
- Members:
- • {pid} (number) The LSP server's PID.
- • {handle} A handle for low-level interaction with the LSP server
- process |vim.loop|.
+ • `is_closing()` returns a boolean indicating if the RPC is closing.
+ • `terminate()` terminates the RPC client.
==============================================================================
@@ -1977,17 +2002,17 @@ 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 difference
- {lastline} (number) line to begin search in old_lines for last
+ • {prev_lines} (table) list of lines
+ • {curr_lines} (table) list of lines
+ • {firstline} (number) line to begin search for first difference
+ • {lastline} (number) line to begin search in old_lines for last
difference
- {new_lastline} (number) line to begin search in new_lines for last
+ • {new_lastline} (number) line to begin search in new_lines for last
difference
- {offset_encoding} (string) encoding requested by language server
+ • {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/lsp/3.17/specification/#textDocumentContentChangeEvent
==============================================================================
@@ -2003,7 +2028,7 @@ resolve_capabilities({server_capabilities})
Creates a normalized object describing LSP server capabilities.
Parameters: ~
- {server_capabilities} (table) Table of capabilities supported by the
+ • {server_capabilities} (table) Table of capabilities supported by the
server
Return: ~
diff --git a/runtime/doc/lua-guide.txt b/runtime/doc/lua-guide.txt
new file mode 100644
index 0000000000..b971a7d2ad
--- /dev/null
+++ b/runtime/doc/lua-guide.txt
@@ -0,0 +1,757 @@
+*lua-guide.txt* Nvim
+
+ NVIM REFERENCE MANUAL
+
+ Guide to using Lua in Nvim
+
+
+ Type |gO| to see the table of contents.
+
+==============================================================================
+Introduction *lua-guide*
+
+This guide will go through the basics of using Lua in Neovim. It is not meant
+to be a comprehensive encyclopedia of all available features, nor will it
+detail all intricacies. Think of it as a survival kit -- the bare minimum
+needed to know to comfortably get started on using Lua in Neovim.
+
+An important thing to note is that this isn't a guide to the Lua language
+itself. Rather, this is a guide on how to configure and modify Neovim through
+the Lua language and the functions we provide to help with this. Take a look
+at |luaref| and |lua-concepts| if you'd like to learn more about Lua itself.
+Similarly, this guide assumes some familiarity with the basics of Neovim
+(commands, options, mappings, autocommands), which are covered in the
+|user-manual|.
+
+------------------------------------------------------------------------------
+Some words on the API *lua-guide-api*
+
+The purpose of this guide is to introduce the different ways of interacting
+with Neovim through Lua (the "API"). This API consists of three different
+layers:
+
+1. The "Vim API" inherited from Vim: |ex-commands| and |builtin-functions| as
+well as |user-function|s in Vimscript. These are accessed through |vim.cmd()|
+and |vim.fn| respectively, which are discussed under |lua-guide-vimscript|
+below.
+
+2. The "Neovim API" written in C for use in remote plugins and GUIs; see |api|.
+These functions are accessed through |vim.api|.
+
+3. The "Lua API" written in and specifically for Lua. These are any other
+functions accessible through `vim.*` not mentioned already; see |lua-stdlib|.
+
+This distinction is important, as API functions inherit behavior from their
+original layer: For example, Neovim API functions always need all arguments to
+be specified even if Lua itself allows omitting arguments (which are then
+passed as `nil`); and Vim API functions can use 0-based indexing even if Lua
+arrays are 1-indexed by default.
+
+Through this, any possible interaction can be done through Lua without writing
+a complete new API from scratch. For this reason, functions are usually not
+duplicated between layers unless there is a significant benefit in
+functionality or performance (e.g., you can map Lua functions directly through
+|nvim_create_autocmd()| but not through |:autocmd|). In case there are multiple
+ways of achieving the same thing, this guide will only cover what is most
+convenient to use from Lua.
+
+==============================================================================
+Using Lua *lua-guide-using-Lua*
+
+To run Lua code from the Neovim command line, use the |:lua| command:
+>vim
+ :lua print("Hello!")
+<
+Note: each |:lua| command has its own scope and variables declared with the
+local keyword are not accessible outside of the command. This won't work:
+>vim
+ :lua local foo = 1
+ :lua print(foo)
+ " prints "nil" instead of "1"
+<
+You can also use `:lua=`, which is the same as `:lua vim.pretty_print(...)`,
+to conveniently check the value of a variable or a table:
+>lua
+ :lua=package
+<
+To run a Lua script in an external file, you can use the |:source| command
+exactly like for a Vimscript file:
+>vim
+ :source ~/programs/baz/myluafile.lua
+<
+Finally, you can include Lua code in a Vimscript file by putting it inside a
+|lua-heredoc| block:
+>vim
+ lua << EOF
+ local tbl = {1, 2, 3}
+ for k, v in ipairs(tbl) do
+ print(v)
+ end
+ EOF
+<
+------------------------------------------------------------------------------
+Using Lua files on startup *lua-guide-config*
+
+Neovim supports using `init.vim` or `init.lua` as the configuration file, but
+not both at the same time. This should be placed in your |config| directory,
+which is typically `~/.config/nvim` for Linux, BSD, or macOS, and
+`~/AppData/Local/nvim/` for Windows. Note that you can use Lua in `init.vim`
+and Vimscript in `init.lua`, which will be covered below.
+
+If you'd like to run any other Lua script on |startup| automatically, then you
+can simply put it in `plugin/` in your |'runtimepath'|.
+
+------------------------------------------------------------------------------
+Lua modules *lua-guide-modules*
+
+If you want to load Lua files on demand, you can place them in the `lua/`
+directory in your |'runtimepath'| and load them with `require`. (This is the
+Lua equivalent of Vimscript's |autoload| mechanism.)
+
+Let's assume you have the following directory structure:
+>
+ ~/.config/nvim
+ |-- after/
+ |-- ftplugin/
+ |-- lua/
+ | |-- myluamodule.lua
+ | |-- other_modules/
+ | |-- anothermodule.lua
+ | |-- init.lua
+ |-- plugin/
+ |-- syntax/
+ |-- init.vim
+<
+
+Then the following Lua code will load `myluamodule.lua`:
+>lua
+ require("myluamodule")
+<
+Note the absence of a `.lua` extension.
+
+Similarly, loading `other_modules/anothermodule.lua` is done via
+>lua
+ require('other_modules/anothermodule')
+ -- or
+ require('other_modules.anothermodule')
+<
+
+Note how "submodules" are just subdirectories; the `.` is equivalent to the
+path separator `/` (even on Windows).
+
+A folder containing an |init.lua| file can be required directly, without
+having to specify the name of the file:
+>lua
+ require('other_modules') -- loads other_modules/init.lua
+<
+Requiring a nonexistent module or a module which contains syntax errors aborts
+the currently executing script. `pcall()` may be used to catch such errors. The
+following example tries to load the `module_with_error` and only calls one of
+its functions if this succeeds and prints an error message otherwise:
+>lua
+ local ok, mymod = pcall(require, 'module_with_error')
+ if not ok then
+ print("Module had an error")
+ else
+ mymod.function()
+ end
+<
+In contrast to |:source|, |require()| not only searches through all `lua/` directories
+under |'runtimepath'|, it also cache the module on first use. Calling
+`require()` a second time will therefore _not_ execute the script again and
+instead return the cached file. To rerun the file, you need to remove it from
+the cache manually first:
+>lua
+ package.loaded['myluamodule'] = nil
+ require('myluamodule') -- read and execute the module again from disk
+<
+------------------------------------------------------------------------------
+See also:
+• |lua-require|
+• |luaref-pcall()|
+
+==============================================================================
+Using Vim commands and functions from Lua *lua-guide-vimscript*
+
+All Vim commands and functions are accessible from Lua.
+
+------------------------------------------------------------------------------
+Vim commands *lua-guide-vim-commands*
+
+To run an arbitrary Vim command from Lua, pass it as a string to |vim.cmd()|:
+>lua
+ vim.cmd("colorscheme habamax")
+<
+Note that special characters will need to be escaped with backslashes:
+>lua
+ vim.cmd("%s/\\Vfoo/bar/g")
+<
+An alternative is to use a literal string (see |luaref-literal|) delimited by
+double brackets `[[ ]]` as in
+>lua
+ vim.cmd([[%s/\Vfoo/bar/g]])
+<
+Another benefit of using literal strings is that they can be multiple lines;
+this allows you to pass multiple commands to a single call of |vim.cmd()|:
+>lua
+ vim.cmd([[
+ highlight Error guibg=red
+ highlight link Warning Error
+ ]])
+<
+This is the converse of |lua-heredoc| and allows you to include Vimscript code in
+your `init.lua`.
+
+If you want to build your Vim command programmatically, the following form can
+be useful (all these are equivalent to the corresponding line above):
+>lua
+ vim.cmd.colorscheme("habamax")
+ vim.cmd.highlight({ "Error", "guibg=red" })
+ vim.cmd.highlight({ "link", "Warning", "Error" })
+<
+------------------------------------------------------------------------------
+Vimscript functions *lua-guide-vim-functions*
+
+Use |vim.fn| to call Vimscript functions from Lua. Data types between Lua and
+Vimscript are automatically converted:
+>lua
+ print(vim.fn.printf('Hello from %s', 'Lua'))
+
+ local reversed_list = vim.fn.reverse({ 'a', 'b', 'c' })
+ print(vim.inspect(reversed_list)) -- { "c", "b", "a" }
+
+ local function print_stdout(chan_id, data, name)
+ print(data[1])
+ end
+
+ vim.fn.jobstart('ls', { on_stdout = print_stdout })
+ print(vim.fn.printf('Hello from %s', 'Lua'))
+<
+This works for both |builtin-functions| and |user-function|s.
+
+Note that hashes (`#`) are not valid characters for identifiers in Lua, so,
+e.g., |autoload| functions have to be called with this syntax:
+>lua
+ vim.fn['my#autoload#function']()
+<
+------------------------------------------------------------------------------
+See also:
+• |builtin-functions|: alphabetic list of all Vimscript functions
+• |function-list|: list of all Vimscript functions grouped by topic
+• |:runtime|: run all Lua scripts matching a pattern in |'runtimepath'|
+• |package.path|: list of all paths searched by `require()`
+
+==============================================================================
+Variables *lua-guide-variables*
+
+Variables can be set and read using the following wrappers, which directly
+correspond to their |variable-scope|:
+
+• |vim.g|: global variables (|g:|)
+• |vim.b|: variables for the current buffer (|b:|)
+• |vim.w|: variables for the current window (|w:|)
+• |vim.t|: variables for the current tabpage (|t:|)
+• |vim.v|: predefined Vim variables (|v:|)
+• |vim.env|: environment variables defined in the editor session
+
+Data types are converted automatically. For example:
+>lua
+ vim.g.some_global_variable = {
+ key1 = "value",
+ key2 = 300
+ }
+
+ print(vim.inspect(vim.g.some_global_variable))
+ --> { key1 = "value", key2 = 300 }
+<
+You can target specific buffers (via number), windows (via |window-ID|), or
+tabpages by indexing the wrappers:
+>lua
+ vim.b[2].myvar = 1 -- set myvar for buffer number 2
+ vim.w[1005].myothervar = true -- set myothervar for window ID 1005
+<
+Some variable names may contain characters that cannot be used for identifiers
+in Lua. You can still manipulate these variables by using the syntax
+>lua
+ vim.g['my#variable'] = 1
+<
+Note that you cannot directly change fields of array variables. This won't
+work:
+>lua
+ vim.g.some_global_variable.key2 = 400
+ vim.pretty_print(vim.g.some_global_variable)
+ --> { key1 = "value", key2 = 300 }
+<
+Instead, you need to create an intermediate Lua table and change this:
+>lua
+ local temp_table = vim.g.some_global_variable
+ temp_table.key2 = 400
+ vim.g.some_global_variable = temp_table
+ vim.pretty_print(vim.g.some_global_variable)
+ --> { key1 = "value", key2 = 400 }
+<
+To delete a variable, simply set it to `nil`:
+>lua
+ vim.g.myvar = nil
+<
+------------------------------------------------------------------------------
+See also:
+• |lua-vim-variables|
+
+==============================================================================
+Options *lua-guide-options*
+
+There are two complementary ways of setting |options| via Lua.
+
+------------------------------------------------------------------------------
+vim.opt
+
+The most convenient way for setting global and local options, e.g., in `init.lua`,
+is through `vim.opt` and friends:
+
+• |vim.opt|: behaves like |:set|
+• |vim.opt_global|: behaves like |:setglobal|
+• |vim.opt_local|: behaves like |:setlocal|
+
+For example, the Vimscript commands
+>vim
+ set smarttab
+ set nosmarttab
+<
+are equivalent to
+>lua
+ vim.opt.smarttab = true
+ vim.opt.smarttab = false
+<
+In particular, they allow an easy way to working with list-like, map-like, and
+set-like options through Lua tables: Instead of
+>vim
+ set wildignore=*.o,*.a,__pycache__
+ set listchars=space:_,tab:>~
+ set formatoptions=njt
+<
+you can use
+>lua
+ vim.opt.wildignore = { '*.o', '*.a', '__pycache__' }
+ vim.opt.listchars = { space = '_', tab = '>~' }
+ vim.opt.formatoptions = { n = true, j = true, t = true }
+<
+These wrappers also come with methods that work similarly to their |:set+=|,
+|:set^=| and |:set-=| counterparts in Vimscript:
+>lua
+ vim.opt.shortmess:append({ I = true })
+ vim.opt.wildignore:prepend('*.o')
+ vim.opt.whichwrap:remove({ 'b', 's' })
+<
+The price to pay is that you cannot access the option values directly but must
+use |vim.opt:get()|:
+>lua
+ print(vim.opt.smarttab)
+ --> {...} (big table)
+ print(vim.opt.smarttab:get())
+ --> false
+ vim.pretty_print(vim.opt.listchars:get())
+ --> { space = '_', tab = '>~' }
+<
+------------------------------------------------------------------------------
+vim.o
+
+For this reason, there exists a more direct variable-like access using `vim.o`
+and friends, similarly to how you can get and set options via `:echo &number`
+and `:let &listchars='space:_,tab:>~'`:
+
+• |vim.o|: behaves like |:set|
+• |vim.go|: behaves like |:setglobal|
+• |vim.bo|: for buffer-scoped options
+• |vim.wo|: for window-scoped options
+
+For example:
+>lua
+ vim.o.smarttab = false -- :set nosmarttab
+ print(vim.o.smarttab)
+ --> false
+ vim.o.listchars = 'space:_,tab:>~' -- :set listchars='space:_,tab:>~'
+ print(vim.o.listchars)
+ --> 'space:_,tab:>~'
+ vim.o.isfname = vim.o.isfname .. ',@-@' -- :set isfname+=@-@
+ print(vim.o.isfname)
+ --> '@,48-57,/,.,-,_,+,,,#,$,%,~,=,@-@'
+ vim.bo.shiftwidth = 4 -- :setlocal shiftwidth=4
+ print(vim.bo.shiftwidth)
+ --> 4
+<
+Just like variables, you can specify a buffer number or |window-ID| for buffer
+and window options, respectively. If no number is given, the current buffer or
+window is used:
+>lua
+ vim.bo[4].expandtab = true -- sets expandtab to true in buffer 4
+ vim.wo.number = true -- sets number to true in current window
+ print(vim.wo[0].number) --> true
+<
+------------------------------------------------------------------------------
+See also:
+• |lua-options|
+
+==============================================================================
+Mappings *lua-guide-mappings*
+
+You can map either Vim commands or Lua functions to key sequences.
+
+------------------------------------------------------------------------------
+Creating mappings *lua-guide-mappings-set*
+
+Mappings can be created using |vim.keymap.set()|. This function takes three
+mandatory arguments:
+• {mode} is a string or a table of strings containing the mode
+ prefix for which the mapping will take effect. The prefixes are the ones
+ listed in |:map-modes|, or "!" for |:map!|, or empty string for |:map|.
+• {lhs} is a string with the key sequences that should trigger the mapping.
+• {rhs} is either a string with a Vim command or a Lua function that should
+ be executed when the {lhs} is entered.
+ An empty string is equivalent to |<Nop>|, which disables a key.
+
+Examples:
+>lua
+ -- Normal mode mapping for Vim command
+ vim.keymap.set('n', '<Leader>ex1', '<cmd>echo "Example 1"<cr>')
+ -- Normal and Command-line mode mapping for Vim command
+ vim.keymap.set({'n', 'c'}, '<Leader>ex2', '<cmd>echo "Example 2"<cr>')
+ -- Normal mode mapping for Lua function
+ vim.keymap.set('n', '<Leader>ex3', vim.treesitter.start)
+ -- Normal mode mapping for Lua function with arguments
+ vim.keymap.set('n', '<Leader>ex4', function() print('Example 4') end)
+<
+You can map functions from Lua modules via
+>lua
+ vim.keymap.set('n', '<Leader>pl1', require('plugin').action)
+<
+Note that this loads the plugin at the time the mapping is defined. If you
+want to defer the loading to the time when the mapping is executed (as for
+|autoload| functions), wrap it in `function() end`:
+>lua
+ vim.keymap.set('n', '<Leader>pl2', function() require('plugin').action() end)
+<
+The fourth, optional, argument is a table with keys that modify the behavior
+of the mapping such as those from |:map-arguments|. The following are the most
+useful options:
+• `buffer`: If given, only set the mapping for the buffer with the specified
+ number; `0` or `true` means the current buffer. >lua
+ -- set mapping for the current buffer
+ vim.keymap.set('n', '<Leader>pl1', require('plugin').action, { buffer = true })
+ -- set mapping for the buffer number 4
+ vim.keymap.set('n', '<Leader>pl1', require('plugin').action, { buffer = 4 })
+<
+• `silent`: If set to `true`, suppress output such as error messages. >lua
+ vim.keymap.set('n', '<Leader>pl1', require('plugin').action, { silent = true })
+<
+• `expr`: If set to `true`, do not execute the {rhs} but use the return value
+ as input. Special |keycodes| are converted automatically. For example, the following
+ mapping replaces <down> with <c-n> in the popupmenu only: >lua
+ vim.keymap.set('c', '<down>', function()
+ if vim.fn.pumvisible() == 1 then return '<c-n>' end
+ return '<down>'
+ end, { expr = true })
+<
+• `desc`: A string that is shown when listing mappings with, e.g., |:map|.
+ This is useful since Lua functions as {rhs} are otherwise only listed as
+ `Lua: <number> <source file>:<line>`. Plugins should therefore always use this
+ for mappings they create. >lua
+ vim.keymap.set('n', '<Leader>pl1', require('plugin').action,
+ { desc = 'Execute action from plugin' })
+<
+• `remap`: By default, all mappings are nonrecursive by default (i.e.,
+ |vim.keymap.set()| behaves like |:noremap|). If the {rhs} is itself a mapping
+ that should be executed, set `remap = true`: >lua
+ vim.keymap.set('n', '<Leader>ex1', '<cmd>echo "Example 1"<cr>')
+ -- add a shorter mapping
+ vim.keymap.set('n', 'e', '<Leader>ex1', { remap = true })
+<
+ Note: |<Plug>| mappings are always expanded even with the default `remap = false`: >lua
+ vim.keymap.set('n', '[%', '<Plug>(MatchitNormalMultiBackward)')
+<
+------------------------------------------------------------------------------
+Removing mappings *lua-guide-mappings-del*
+
+A specific mapping can be removed with |vim.keymap.del()|:
+>lua
+ vim.keymap.del('n', '<Leader>ex1')
+ vim.keymap.del({'n', 'c'}, '<Leader>ex2', {buffer = true})
+<
+------------------------------------------------------------------------------
+See also:
+• `vim.api.`|nvim_get_keymap()|: return all global mapping
+• `vim.api.`|nvim_buf_get_keymap()|: return all mappings for buffer
+
+==============================================================================
+Autocommands *lua-guide-autocommands*
+
+An |autocommand| is a Vim command or a Lua function that is automatically
+executed whenever one or more |events| are triggered, e.g., when a file is
+read or written, or when a window is created. These are accessible from Lua
+through the Neovim API.
+
+------------------------------------------------------------------------------
+Creating autocommands *lua-guide-autocommand-create*
+
+Autocommands are created using `vim.api.`|nvim_create_autocmd()|, which takes
+two mandatory arguments:
+• {event}: a string or table of strings containing the event(s) which should
+ trigger the command or function.
+• {opts}: a table with keys that control what should happen when the event(s)
+ are triggered.
+
+The most important options are:
+
+• `pattern`: A string or table of strings containing the |autocmd-pattern|.
+ Note: Environment variable like `$HOME` and `~` are not automatically
+ expanded; you need to explicitly use `vim.fn.`|expand()| for this.
+• `command`: A string containing a Vim command.
+• `callback`: A Lua function.
+
+You must specify one and only one of `command` and `callback`. If `pattern` is
+omitted, it defaults to `pattern = '*'`.
+Examples:
+>lua
+ vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, {
+ pattern = {"*.c", "*.h"},
+ command = "echo 'Entering a C or C++ file'",
+ })
+
+ -- Same autocommand written with a Lua function instead
+ vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, {
+ pattern = {"*.c", "*.h"},
+ callback = function() print("Entering a C or C++ file") end,
+ })
+
+ -- User event triggered by MyPlugin
+ vim.api.nvim_create_autocmd("User", {
+ pattern = "MyPlugin",
+ callback = function() print("My Plugin Works!") end,
+ })
+<
+
+Neovim will always call a Lua function with a single table containing information
+about the triggered autocommand. The most useful keys are
+• `match`: a string that matched the `pattern` (see |<amatch>|)
+• `buf`: the number of the buffer the event was triggered in (see |<abuf>|)
+• `file`: the file name of the buffer the event was triggered in (see |<afile>|)
+• `data`: a table with other relevant data that is passed for some events
+
+For example, this allows you to set buffer-local mappings for some filetypes:
+>lua
+ vim.api.nvim.create_autocmd("FileType", {
+ pattern = "lua",
+ callback = function(args)
+ vim.keymap.set('n', 'K', vim.lsp.buf.hover, { buffer = args.buf })
+ end
+ })
+<
+This means that if your callback itself takes an (even optional) argument, you
+must wrap it in `function() end` to avoid an error:
+>lua
+ vim.api.nvim_create_autocmd('TextYankPost', {
+ callback = function() vim.highlight.on_yank() end
+ })
+<
+(Since unused arguments can be omitted in Lua function definitions, this is
+equivalent to `function(args) ... end`.)
+
+Instead of using a pattern, you can create a buffer-local autocommand (see
+|autocmd-buflocal|) with `buffer`; in this case, `pattern` cannot be used:
+>lua
+ -- set autocommand for current buffer
+ vim.api.nvim_create_autocmd("CursorHold", {
+ buffer = 0,
+ callback = function() print("hold") end,
+ })
+
+ -- set autocommand for buffer number 33
+ vim.api.nvim_create_autocmd("CursorHold", {
+ buffer = 33,
+ callback = function() print("hold") end,
+ })
+<
+Similarly to mappings, you can (and should) add a description using `desc`:
+>lua
+ vim.api.nvim_create_autocmd('TextYankPost', {
+ callback = function() vim.highlight.on_yank() end,
+ desc = "Briefly highlight yanked text"
+ })
+<
+Finally, you can group autocommands using the `group` key; this will be
+covered in detail in the next section.
+
+------------------------------------------------------------------------------
+Grouping autocommands *lua-guide-autocommands-group*
+
+Autocommand groups can be used to group related autocommands together; see
+|autocmd-groups|. This is useful for organizing autocommands and especially
+for preventing autocommands to be set multiple times.
+
+Groups can be created with `vim.api.`|nvim_create_augroup()|. This function
+takes two mandatory arguments: a string with the name of a group and a table
+determining whether the group should be cleared (i.e., all grouped
+autocommands removed) if it already exists. The function returns a number that
+is the internal identifier of the group. Groups can be specified either by
+this identifier or by the name (but only if the group has been created first).
+
+For example, a common Vimscript pattern for autocommands defined in files that
+may be reloaded is
+>vim
+ augroup vimrc
+ " Remove all vimrc autocommands
+ autocmd!
+ au BufNewFile,BufRead *.html set shiftwidth=4
+ au BufNewFile,BufRead *.html set expandtab
+ augroup END
+<
+This is equivalent to the following Lua code:
+>lua
+ local mygroup = vim.api.nvim_create_augroup('vimrc', { clear = true })
+ vim.api.nvim_create_autocmd({ 'BufNewFile', 'BufRead' }, {
+ pattern = '*.html',
+ group = mygroup,
+ command = 'set shiftwidth=4',
+ })
+ vim.api.nvim_create_autocmd({ 'BufNewFile', 'BufRead' }, {
+ pattern = '*.html',
+ group = 'vimrc', -- equivalent to group=mygroup
+ command = 'set expandtab',
+ })
+<
+Autocommand groups are unique for a given name, so you can reuse them, e.g.,
+in a different file:
+>lua
+ local mygroup = vim.api.nvim_create_augroup('vimrc', { clear = false })
+ vim.api.nvim_create_autocmd({ 'BufNewFile', 'BufRead' }, {
+ pattern = '*.html',
+ group = mygroup,
+ command = 'set shiftwidth=4',
+ })
+<
+------------------------------------------------------------------------------
+Deleting autocommands *lua-guide-autocommands-delete*
+
+You can use `vim.api.`|nvim_clear_autocmds()| to remove autocommands. This
+function takes a single mandatory argument that is a table of keys describing
+the autocommands that are to be removed:
+>lua
+ -- Delete all BufEnter and InsertLeave autocommands
+ vim.api.nvim_clear_autocmds({event = {"BufEnter", "InsertLeave"}})
+
+ -- Delete all autocommands that uses "*.py" pattern
+ vim.api.nvim_clear_autocmds({pattern = "*.py"})
+
+ -- Delete all autocommands in group "scala"
+ vim.api.nvim_clear_autocmds({group = "scala"})
+
+ -- Delete all ColorScheme autocommands in current buffer
+ vim.api.nvim_clear_autocmds({event = "ColorScheme", buffer = 0 })
+<
+Note: Autocommands in groups will only be removed if the `group` key is
+specified, even if another option matches it.
+
+------------------------------------------------------------------------------
+See also
+• |nvim_get_autocmds()|: return all matching autocommands
+• |nvim_exec_autocmds()|: execute all matching autocommands
+
+==============================================================================
+User commands *lua-guide-usercommands*
+
+|user-commands| are custom Vim commands that call a Vimscript or Lua function.
+Just like built-in commands, they can have arguments, act on ranges, or have
+custom completion of arguments. As these are most useful for plugins, we will
+cover only the basics of this advanced topic.
+
+------------------------------------------------------------------------------
+Creating user commands *lua-guide-usercommands-create*
+
+User commands can be created through the Neovim API with
+`vim.api.`|nvim_create_user_command()|. This function takes three mandatory
+arguments:
+• a string that is the name of the command (which must start with an uppercase
+ letter to distinguish it from builtin commands);
+• a string containing Vim commands or a Lua function that is executed when the
+ command is invoked;
+• a table with |command-attributes|; in addition, it can contain the keys
+ `desc` (a string describing the command); `force` (set to `false` to avoid
+ replacing an already existing command with the same name), and `preview` (a
+ Lua function that is used for |:command-preview|).
+
+Example:
+>lua
+ vim.api.nvim_create_user_command('Test', 'echo "It works!"', {})
+ vim.cmd.Test()
+ --> It works!
+<
+(Note that the third argument is mandatory even if no attributes are given.)
+
+Lua functions are called with a single table argument containing arguments and
+modifiers. The most important are:
+• `name`: a string with the command name
+• `fargs`: a table containing the command arguments split by whitespace (see |<f-args>|)
+• `bang`: `true` if the command was executed with a `!` modifier (see |<bang>|)
+• `line1`: the starting line number of the command range (see |<line1>|)
+• `line2`: the final line number of the command range (see |<line2>|)
+• `range`: the number of items in the command range: 0, 1, or 2 (see |<range>|)
+• `count`: any count supplied (see |<count>|)
+• `smods`: a table containing the command modifiers (see |<mods>|)
+
+For example:
+>lua
+ vim.api.nvim_create_user_command('Upper',
+ function(opts)
+ print(string.upper(opts.fargs[1]))
+ end,
+ { nargs = 1 })
+
+ vim.cmd.Upper('foo')
+ --> FOO
+<
+The `complete` attribute can take a Lua function in addition to the
+attributes listed in |:command-complete|. >lua
+
+ vim.api.nvim_create_user_command('Upper',
+ function(opts)
+ print(string.upper(opts.fargs[1]))
+ end,
+ { nargs = 1,
+ complete = function(ArgLead, CmdLine, CursorPos)
+ -- return completion candidates as a list-like table
+ return { "foo", "bar", "baz" }
+ end,
+ })
+<
+Buffer-local user commands are created with `vim.api.`|nvim_buf_create_user_command()|.
+Here the first argument is the buffer number (`0` being the current buffer);
+the remaining arguments are the same as for |nvim_create_user_command()|:
+>lua
+ vim.api.nvim_buf_create_user_command(0, 'Upper',
+ function(opts)
+ print(string.upper(opts.fargs[1]))
+ end,
+ { nargs = 1 })
+<
+------------------------------------------------------------------------------
+Deleting user commands *lua-guide-usercommands-delete*
+
+User commands can be deleted with `vim.api.`|nvim_del_user_command()|. The only
+argument is the name of the command:
+>lua
+ vim.api.nvim_del_user_command('Upper')
+<
+To delete buffer-local user commands use `vim.api.`|nvim_buf_del_user_command()|.
+Here the first argument is the buffer number (`0` being the current buffer),
+and second is command name:
+>lua
+ vim.api.nvim_buf_del_user_command(4, 'Upper')
+<
+==============================================================================
+Credits *lua-guide-credits*
+This guide is in large part taken from nanotee's Lua guide:
+https://github.com/nanotee/nvim-lua-guide
+
+Thank you @nanotee!
+
+vim:tw=78:ts=8:sw=4:sts=4:et:ft=help:norl:
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
index 42f3a5e432..47249a484b 100644
--- a/runtime/doc/lua.txt
+++ b/runtime/doc/lua.txt
@@ -11,30 +11,119 @@ Lua engine *lua* *Lua*
==============================================================================
INTRODUCTION *lua-intro*
-The Lua 5.1 language is builtin and always available. Try this command to get
-an idea of what lurks beneath: >
+The Lua 5.1 script engine is builtin and always available. Try this command to
+get an idea of what lurks beneath: >vim
: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 (|lua-vimscript| |vim.api|). Together these
+"namespaces" form the Nvim programming interface.
+
+Lua plugins and user config are automatically discovered and loaded, just like
+Vimscript. See |lua-guide| for practical guidance.
+
+You can also run Lua scripts from your shell using the |-l| argument: >
+ nvim -l foo.lua [args...]
+<
+ *lua-compat*
+Lua 5.1 is the permanent interface for Nvim Lua. Plugins need only consider
+Lua 5.1, not worry about forward-compatibility with future Lua versions. If
+Nvim ever ships with Lua 5.4+, a Lua 5.1 compatibility shim will be provided
+so that old plugins continue to work transparently.
+
+==============================================================================
+LUA CONCEPTS AND IDIOMS *lua-concepts*
+
+Lua is very simple: this means that, while there are some quirks, once you
+internalize those quirks, everything works the same everywhere. Scopes
+(closures) in particular are very consistent, unlike JavaScript or most other
+languages.
+
+Lua has three fundamental mechanisms—one for "each major aspect of
+programming": tables, closures, and coroutines.
+https://www.lua.org/doc/cacm2018.pdf
+- Tables are the "object" or container datastructure: they represent both
+ lists and maps, you can extend them to represent your own datatypes and
+ change their behavior using |luaref-metatable| (like Python's "datamodel").
+- EVERY scope in Lua is a closure: a function is a closure, a module is
+ a closure, a `do` block (|luaref-do|) is a closure--and they all work the
+ same. A Lua module is literally just a big closure discovered on the "path"
+ (where your modules are found: |package.cpath|).
+- Stackful coroutines enable cooperative multithreading, generators, and
+ versatile control for both Lua and its host (Nvim).
+
+ *lua-call-function*
+Lua functions can be called in multiple ways. Consider the function: >lua
+ local foo = function(a, b)
+ print("A: ", a)
+ print("B: ", b)
+ end
+
+The first way to call this function is: >lua
+ foo(1, 2)
+ -- ==== Result ====
+ -- A: 1
+ -- B: 2
+
+This way of calling a function is familiar from most scripting languages.
+In Lua, any missing arguments are passed as `nil`. Example: >lua
+ foo(1)
+ -- ==== Result ====
+ -- A: 1
+ -- B: nil
+
+Furthermore it is not an error if extra parameters are passed, they are just
+discarded.
+
+ *kwargs*
+When calling a function, you can omit the parentheses if the function takes
+exactly one string literal (`"foo"`) or table literal (`{1,2,3}`). The latter
+is often used to approximate "named parameters" ("kwargs" or "keyword args")
+as in languages like Python and C#. Example: >lua
+ local func_with_opts = function(opts)
+ local will_do_foo = opts.foo
+ local filename = opts.filename
+ ...
+ end
+
+ func_with_opts { foo = true, filename = "hello.world" }
<
-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.
+There's nothing special going on here except that parentheses are treated as
+whitespace. But visually, this small bit of sugar gets reasonably close to
+a "keyword args" interface.
+
+It is of course also valid to call the function with parentheses: >lua
+ func_with_opts({ foo = true, filename = "hello.world" })
+<
+Nvim tends to prefer the keyword args style.
+
+------------------------------------------------------------------------------
+LUA PATTERNS *lua-patterns*
-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.
+Lua intentionally does not support regular expressions, instead it has limited
+"patterns" |luaref-patterns| which avoid the performance pitfalls of extended
+regex. Lua scripts can also use Vim regex via |vim.regex()|.
-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.
+These examples use |string.match()| to demonstrate Lua patterns: >lua
+
+ print(string.match("foo123bar123", "%d+"))
+ -- 123
+ print(string.match("foo123bar123", "[^%d]+"))
+ -- foo
+ print(string.match("foo123bar123", "[abc]+"))
+ -- ba
+ print(string.match("foo.bar", "%.bar"))
+ -- .bar
==============================================================================
-IMPORTING LUA MODULES *lua-require*
+IMPORTING LUA MODULES *require()* *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 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
@@ -43,13 +132,11 @@ back to Lua's default search mechanism. The first script found is run and
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.
+executing any script. For further details on `require()`, see |luaref-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:
-
+and loads the first module found ("first wins"): >
foo/lua/mod.lua
foo/lua/mod/init.lua
bar/lua/mod.lua
@@ -58,10 +145,11 @@ and loads the first module found:
foo/lua/mod.dll
bar/lua/mod.so
bar/lua/mod.dll
-
+<
+ *lua-package-path*
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
+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`).
@@ -70,37 +158,33 @@ 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:
-1. Given that
+- 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
- `./?.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
- 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
- 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 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, a `/lua` path segment is inserted
- between path and suffix, leaving:
-
- - `/foo/bar/lua/?.so`
- - `/foo/bar/lua/a?d/j/g.elf`
- - `/abc/lua/?.so`
- - `/abc/lua/a?d/j/g.elf`
-
-6. New paths are prepended to the original |package.cpath|.
-
-The result will look like this:
-
- `/foo/bar,/xxx;yyy/baz,/abc` ('runtimepath')
- × `./?.so;/def/ghi/a?d/j/g.elf;/def/?.so` (`package.cpath`)
-
- = `/foo/bar/lua/?.so;/foo/bar/lua/a?d/j/g.elf;/abc/lua/?.so;/abc/lua/a?d/j/g.elf;./?.so;/def/ghi/a?d/j/g.elf;/def/?.so`
+ - initial |package.cpath| (defined at compile-time or derived from
+ `$LUA_CPATH` / `$LUA_INIT`) 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
+ 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
+ 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 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 a `/lua` path segment is inserted
+ between path and suffix, leaving:
+ - `/foo/bar/lua/?.so`
+ - `/foo/bar/lua/a?d/j/g.elf`
+ - `/abc/lua/?.so`
+ - `/abc/lua/a?d/j/g.elf`
+- 6. New paths are prepended to the original |package.cpath|.
+
+The result will look like this: >
+
+ /foo/bar,/xxx;yyy/baz,/abc ('runtimepath')
+ × ./?.so;/def/ghi/a?d/j/g.elf;/def/?.so (package.cpath)
+ = /foo/bar/lua/?.so;/foo/bar/lua/a?d/j/g.elf;/abc/lua/?.so;/abc/lua/a?d/j/g.elf;./?.so;/def/ghi/a?d/j/g.elf;/def/?.so
Note:
@@ -113,7 +197,7 @@ Note:
- Although adjustments happen automatically, Nvim does not track current
values of |package.path| or |package.cpath|. If you happen to delete some
- paths from there you can set 'runtimepath' to trigger an update: >
+ paths from there you can set 'runtimepath' to trigger an update: >vim
let &runtimepath = &runtimepath
- Skipping paths from 'runtimepath' which contain semicolons applies both to
@@ -122,170 +206,13 @@ Note:
it is better to not have them in 'runtimepath' at all.
==============================================================================
-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 functions can be called in multiple ways. Consider the function: >
-
- local example_func = function(a, b)
- print("A is: ", a)
- print("B is: ", b)
- end
-<
-The first way to call this function is: >
-
- example_func(1, 2)
- -- ==== Result ====
- -- A is: 1
- -- B is: 2
-<
-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.
-
-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
- local filename = opts.filename
-
- ...
- end
-
- 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 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 |luaref-patterns|.
-
-Examples (`string.match` extracts the first match): >
-
- print(string.match("foo123bar123", "%d+"))
- -- -> 123
-
- 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*
-
-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: >
-
- function charblob#encode_buffer()
- call setline(1, luaeval(
- \ 'require("charblob").encode(unpack(_A))',
- \ [getline(1, '$'), &textwidth, ' ']))
- endfunction
-<
-plugin/charblob.vim: >
-
- if exists('g:charblob_loaded')
- finish
- endif
- let g:charblob_loaded = 1
-
- command MakeCharBlob :call charblob#encode_buffer()
-<
-lua/charblob.lua: >
-
- local function charblob_bytes_iter(lines)
- local init_s = {
- next_line_idx = 1,
- next_byte_idx = 1,
- lines = lines,
- }
- local function next(s, _)
- if lines[s.next_line_idx] == nil then
- return nil
- end
- if s.next_byte_idx > #(lines[s.next_line_idx]) then
- s.next_line_idx = s.next_line_idx + 1
- s.next_byte_idx = 1
- return ('\n'):byte()
- end
- local ret = lines[s.next_line_idx]:byte(s.next_byte_idx)
- if ret == ('\n'):byte() then
- ret = 0 -- See :h NL-used-for-NUL.
- end
- s.next_byte_idx = s.next_byte_idx + 1
- return ret
- end
- return next, init_s, nil
- end
-
- local function charblob_encode(lines, textwidth, indent)
- local ret = {
- 'const unsigned char blob[] = {',
- indent,
- }
- for byte in charblob_bytes_iter(lines) do
- -- .- space + number (width 3) + comma
- if #(ret[#ret]) + 5 > textwidth then
- ret[#ret + 1] = indent
- else
- ret[#ret] = ret[#ret] .. ' '
- end
- ret[#ret] = ret[#ret] .. (('%3u,'):format(byte))
- end
- ret[#ret + 1] = '};'
- return ret
- end
-
- return {
- bytes_iter = charblob_bytes_iter,
- encode = charblob_encode,
- }
-<
-==============================================================================
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
has its own scope (closure), so only global variables are shared between
command calls. The |lua-stdlib| modules, user modules, and anything else on
-|lua-package-path| are available.
+|package.path| are available.
The Lua print() function redirects its output to the Nvim message area, with
arguments separated by " " (space) instead of "\t" (tab).
@@ -296,11 +223,11 @@ arguments separated by " " (space) instead of "\t" (tab).
chunk is evaluated as an expression and printed. `:lua =expr` is
equivalent to `:lua print(vim.inspect(expr))`
- Examples: >
+ Examples: >vim
:lua vim.api.nvim_command('echo "Hello, Nvim!"')
-< To see the Lua version: >
+< To see the Lua version: >vim
:lua print(_VERSION)
-< To see the LuaJIT version: >
+< To see the LuaJIT version: >vim
:lua =jit.version
<
*:lua-heredoc*
@@ -311,7 +238,7 @@ arguments separated by " " (space) instead of "\t" (tab).
be preceded by whitespace. You can omit [endmarker] after the "<<" and use
a dot "." after {script} (similar to |:append|, |:insert|).
- Example: >
+ Example: >vim
function! CurrentLineInfo()
lua << EOF
local linenr = vim.api.nvim_win_get_cursor(0)[1]
@@ -333,13 +260,13 @@ arguments separated by " " (space) instead of "\t" (tab).
that becomes the text of the corresponding buffer line. Default [range] is
the whole file: "1,$".
- Examples: >
+ Examples: >vim
:luado return string.format("%s\t%d", line:reverse(), #line)
:lua require"lpeg"
:lua -- balanced parenthesis grammar:
:lua bp = lpeg.P{ "(" * ((1 - lpeg.S"()") + lpeg.V(1))^0 * ")" }
- :luado if bp:match(line) then return "-->\t" .. line end
+ :luado if bp:match(line) then return "=>\t" .. line end
<
*:luafile*
:luafile {file}
@@ -347,7 +274,7 @@ arguments separated by " " (space) instead of "\t" (tab).
The whole argument is used as the filename (like |:edit|), spaces do not
need to be escaped. Alternatively you can |:source| Lua files.
- Examples: >
+ Examples: >vim
:luafile script.lua
:luafile %
<
@@ -358,7 +285,7 @@ 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: >
+semantically equivalent in Lua to: >lua
local chunkheader = "local _A = select(1, ...) return "
function luaeval (expstr, arg)
@@ -372,11 +299,11 @@ converted to a |Blob|. Conversion of other Lua types is an error.
The magic global "_A" contains the second argument to luaeval().
-Example: >
+Example: >vim
:echo luaeval('_A[1] + _A[2]', [40, 2])
- 42
+ " 42
:echo luaeval('string.match(_A, "[a-z]+")', 'XYXfoo123')
- foo
+ " 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.
@@ -408,7 +335,7 @@ cases there is the following agreement:
form a 1-step sequence from 1 to N are ignored, as well as all
non-integral keys.
-Examples: >
+Examples: >vim
:echo luaeval('math.pi')
:function Rand(x,y) " random uniform between x and y
@@ -425,29 +352,29 @@ treated specially.
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 >
- v:lua.func(arg1, arg2)
-is equivalent to the Lua chunk >
+which are global or accessible from global tables. The expression >vim
+ call v:lua.func(arg1, arg2)
+is equivalent to the Lua chunk >lua
return func(...)
-where the args are converted to Lua values. The expression >
- v:lua.somemod.func(args)
-is equivalent to the Lua chunk >
+where the args are converted to Lua values. The expression >vim
+ call v:lua.somemod.func(args)
+is equivalent to the Lua chunk >lua
return somemod.func(...)
-In addition, functions of packages can be accessed like >
- v:lua.require'mypack'.func(arg1, arg2)
- v:lua.require'mypack.submod'.func(arg1, arg2)
+In addition, functions of packages can be accessed like >vim
+ call v:lua.require'mypack'.func(arg1, arg2)
+ call v:lua.require'mypack.submod'.func(arg1, arg2)
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).
The `v:lua` prefix may be used to call Lua functions as |method|s. For
-example: >
- arg1->v:lua.somemod.func(arg2)
+example: >vim
+ :eval 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: >
+For example consider the following Lua omnifunc handler: >lua
function mymod.omnifunc(findstart, base)
if findstart == 1 then
@@ -459,10 +386,10 @@ For example consider the following Lua omnifunc handler: >
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,
-or use the require syntax as specified above to access it from a package.
+or use require() as shown above to access it from a package.
Note: `v:lua` without a call is not allowed in a Vimscript expression:
-|Funcref|s cannot represent Lua functions. The following are errors: >
+|Funcref|s cannot represent Lua functions. The following are errors: >vim
let g:Myvar = v:lua.myfunc " Error
call SomeFunc(v:lua.mycallback) " Error
@@ -476,7 +403,7 @@ The Nvim Lua "standard library" (stdlib) is the `vim` module, which exposes
various functions and sub-modules. It is always loaded, thus `require("vim")`
is unnecessary.
-You can peek at the module properties: >
+You can peek at the module properties: >vim
:lua print(vim.inspect(vim))
@@ -496,7 +423,7 @@ Result is something like this: >
...
}
-To find documentation on e.g. the "deepcopy" function: >
+To find documentation on e.g. the "deepcopy" function: >vim
:help vim.deepcopy()
@@ -508,7 +435,7 @@ VIM.LOOP *lua-loop* *vim.loop*
`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: >vim
:lua print(vim.inspect(vim.loop))
<
@@ -517,14 +444,14 @@ see |luv-intro| for a full reference manual.
*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: >lua
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: >
+To avoid the error use |vim.schedule_wrap()| to defer the callback: >lua
local timer = vim.loop.new_timer()
timer:start(1000, 0, vim.schedule_wrap(function()
@@ -536,7 +463,7 @@ wrapping.)
Example: repeating timer
1. Save this code to a file.
- 2. Execute it with ":luafile %". >
+ 2. Execute it with ":luafile %". >lua
-- Create a timer handle (implementation detail: uv_timer_t).
local timer = vim.loop.new_timer()
@@ -557,7 +484,7 @@ Example: File-change detection *watch-file*
3. Use ":Watch %" to watch any file.
4. Try editing the file from another text editor.
5. Observe that the file reloads in Nvim (because on_change() calls
- |:checktime|). >
+ |:checktime|). >lua
local w = vim.loop.new_fs_event()
local function on_change(err, fname, status)
@@ -580,7 +507,7 @@ Example: TCP echo-server *tcp-server*
1. Save this code to a file.
2. Execute it with ":luafile %".
3. Note the port number.
- 4. Connect from any TCP client (e.g. "nc 0.0.0.0 36795"): >
+ 4. Connect from any TCP client (e.g. "nc 0.0.0.0 36795"): >lua
local function create_server(host, port, on_connect)
local server = vim.loop.new_tcp()
@@ -617,7 +544,7 @@ 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|
+- `require` in threads can use lua packages from the global |package.path|
- `print()` and `vim.inspect`
- `vim.diff`
- most utility functions in `vim.*` for working with pure lua values
@@ -629,16 +556,16 @@ 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
->
+>vim
au TextYankPost * silent! lua vim.highlight.on_yank()
<
to your `init.vim`. You can customize the highlight group and the duration of
the highlight via
->
+>vim
au TextYankPost * silent! lua vim.highlight.on_yank {higroup="IncSearch", timeout=150}
<
If you want to exclude visual selections from highlighting on yank, use
->
+>vim
au TextYankPost * silent! lua vim.highlight.on_yank {on_visual=false}
<
vim.highlight.on_yank({opts}) *vim.highlight.on_yank()*
@@ -656,14 +583,14 @@ vim.highlight.range({bufnr}, {ns}, {hlgroup}, {start}, {finish}, {opts})
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:
+ • {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'`
+ or blockwise, see |setreg()|), default `'v'`
• `inclusive`: range includes end position,
default `false`
• `priority`: priority of highlight, default
@@ -674,6 +601,7 @@ 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
+ • `semantic_tokens`: `125`, used for LSP semantic token 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
@@ -711,25 +639,24 @@ vim.diff({a}, {b}, {opts}) *vim.diff()*
Run diff on strings {a} and {b}. Any indices returned by this function,
either directly or via callback arguments, are 1-based.
- Examples: >
-
+ Examples: >lua
vim.diff('a\n', 'b\nc\n')
- -->
- @@ -1 +1,2 @@
- -a
- +b
- +c
+ -- =>
+ -- @@ -1 +1,2 @@
+ -- -a
+ -- +b
+ -- +c
vim.diff('a\n', 'b\nc\n', {result_type = 'indices'})
- -->
- {
- {1, 1, 1, 2}
- }
+ -- =>
+ -- {
+ -- {1, 1, 1, 2}
+ -- }
<
Parameters: ~
- {a} First string to compare
- {b} Second string to compare
- {opts} Optional parameters:
+ • {a} First string to compare
+ • {b} Second string to compare
+ • {opts} Optional parameters:
• `on_hunk` (callback):
Invoked for each hunk in the diff. Return a negative number
to cancel the callback for any remaining hunks.
@@ -742,6 +669,9 @@ vim.diff({a}, {b}, {opts}) *vim.diff()*
• "unified": (default) String in unified format.
• "indices": Array of hunk locations.
Note: This option is ignored if `on_hunk` is used.
+ • `linematch` (boolean): Run linematch on the resulting hunks
+ from xdiff. Requires `result_type = indices`, ignored
+ otherwise.
• `algorithm` (string):
Diff algorithm to use. Values:
• "myers" the default algorithm
@@ -782,6 +712,22 @@ vim.mpack.decode({str}) *vim.mpack.decode*
Decodes (or "unpacks") the msgpack-encoded {str} to a Lua object.
------------------------------------------------------------------------------
+VIM.JSON *lua-json*
+
+The *vim.json* module provides encoding and decoding of Lua objects to and
+from JSON-encoded strings. Supports |vim.NIL| and |vim.empty_dict()|.
+
+vim.json.encode({obj}) *vim.json.encode*
+ Encodes (or "packs") Lua object {obj} as JSON in a Lua string.
+
+vim.json.decode({str}[, {opts}]) *vim.json.decode*
+ Decodes (or "unpacks") the JSON-encoded {str} to a Lua object.
+
+ {opts} is a table with the key `luanil = { object: bool, array: bool }`
+ that controls whether `null` in JSON objects or arrays should be converted
+ to Lua `nil` instead of `vim.NIL`.
+
+------------------------------------------------------------------------------
VIM.SPELL *lua-spell*
vim.spell.check({str}) *vim.spell.check()*
@@ -792,16 +738,15 @@ vim.spell.check({str}) *vim.spell.check()*
'spellfile', 'spellcapcheck' and 'spelloptions' which can all be local to
the buffer. Consider calling this with |nvim_buf_call()|.
- Example: >
-
+ Example: >lua
vim.spell.check("the quik brown fox")
- -->
- {
- {'quik', 'bad', 4}
- }
+ -- =>
+ -- {
+ -- {'quik', 'bad', 5}
+ -- }
<
Parameters: ~
- {str} String to spell check.
+ • {str} String to spell check.
Return: ~
List of tuples with three items:
@@ -818,7 +763,7 @@ VIM *lua-builtin*
vim.api.{func}({...}) *vim.api*
Invokes Nvim |API| function {func} with arguments {...}.
- Example: call the "nvim_get_current_line()" API function: >
+ Example: call the "nvim_get_current_line()" API function: >lua
print(tostring(vim.api.nvim_get_current_line()))
vim.version() *vim.version*
@@ -881,6 +826,22 @@ vim.str_byteindex({str}, {index} [, {use_utf16}]) *vim.str_byteindex()*
An {index} in the middle of a UTF-16 sequence is rounded upwards to
the end of that sequence.
+vim.iconv({str}, {from}, {to}[, {opts}]) *vim.iconv()*
+ The result is a String, which is the text {str} converted from
+ encoding {from} to encoding {to}. When the conversion fails `nil` 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".
+
+ Parameters: ~
+ • {str} (string) Text to convert
+ • {from} (string) Encoding of {str}
+ • {to} (string) Target encoding
+
+ Returns: ~
+ Converted string if conversion succeeds, `nil` otherwise.
+
vim.schedule({callback}) *vim.schedule()*
Schedules {callback} to be invoked soon by the main event-loop. Useful
to avoid |textlock| or other temporary restrictions.
@@ -890,12 +851,12 @@ 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
+ Note: The {fn} is |vim.schedule_wrap()|ped automatically, so API functions are
safe to call.
Parameters: ~
- {fn} Callback to call once {timeout} expires
- {timeout} Time in ms to wait before calling {fn}
+ • {fn} Callback to call once {timeout} expires
+ • {timeout} Time in ms to wait before calling {fn}
Returns: ~
|vim.loop|.new_timer() object
@@ -908,10 +869,10 @@ vim.wait({time} [, {callback}, {interval}, {fast_only}]) *vim.wait()*
this time.
Parameters: ~
- {time} Number of milliseconds to wait
- {callback} Optional callback. Waits until {callback} returns true
- {interval} (Approximate) number of milliseconds to wait between polls
- {fast_only} If true, only |api-fast| events will be processed.
+ • {time} Number of milliseconds to wait
+ • {callback} Optional callback. Waits until {callback} returns true
+ • {interval} (Approximate) number of milliseconds to wait between polls
+ • {fast_only} If true, only |api-fast| events will be processed.
If called from while in an |api-fast| event, will
automatically be set to `true`.
@@ -927,7 +888,7 @@ vim.wait({time} [, {callback}, {interval}, {fast_only}]) *vim.wait()*
If {callback} errors, the error is raised.
- Examples: >
+ Examples: >lua
---
-- Wait for 100 ms, allowing other events to process
@@ -951,6 +912,43 @@ vim.wait({time} [, {callback}, {interval}, {fast_only}]) *vim.wait()*
end
<
+vim.ui_attach({ns}, {options}, {callback}) *vim.ui_attach()*
+ Attach to ui events, similar to |nvim_ui_attach()| but receive events
+ as lua callback. Can be used to implement screen elements like
+ popupmenu or message handling in lua.
+
+ {options} should be a dictionary-like table, where `ext_...` options should
+ be set to true to receive events for the respective external element.
+
+ {callback} receives event name plus additional parameters. See |ui-popupmenu|
+ and the sections below for event format for respective events.
+
+ WARNING: This api is considered experimental. Usability will vary for
+ different screen elements. In particular `ext_messages` behavior is subject
+ to further changes and usability improvements. This is expected to be
+ used to handle messages when setting 'cmdheight' to zero (which is
+ likewise experimental).
+
+ Example (stub for a |ui-popupmenu| implementation): >lua
+
+ ns = vim.api.nvim_create_namespace('my_fancy_pum')
+
+ vim.ui_attach(ns, {ext_popupmenu=true}, function(event, ...)
+ if event == "popupmenu_show" then
+ local items, selected, row, col, grid = ...
+ print("display pum ", #items)
+ elseif event == "popupmenu_select" then
+ local selected = ...
+ print("selected", selected)
+ elseif event == "popupmenu_hide" then
+ print("FIN")
+ end
+ end)
+
+vim.ui_detach({ns}) *vim.ui_detach()*
+ Detach a callback previously attached with |vim.ui_attach()| for the
+ given namespace {ns}.
+
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
@@ -959,7 +957,7 @@ vim.type_idx *vim.type_idx*
vim.val_idx *vim.val_idx*
Value index for tables representing |Float|s. A table representing
- floating-point value 1.0 looks like this: >
+ floating-point value 1.0 looks like this: >lua
{
[vim.type_idx] = vim.types.float,
[vim.val_idx] = 1.0,
@@ -1000,12 +998,13 @@ 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()*
Invokes |vim-function| or |user-function| {func} with arguments {...}.
See also |vim.fn|.
- Equivalent to: >
+ Equivalent to: >lua
vim.fn[func]({...})
vim.cmd({command})
@@ -1013,7 +1012,7 @@ vim.cmd({command})
vim.fn.{func}({...}) *vim.fn*
Invokes |vim-function| or |user-function| {func} with arguments {...}.
- To call autoload functions, use the syntax: >
+ To call autoload functions, use the syntax: >lua
vim.fn['some#function']({...})
<
Unlike vim.api.|nvim_call_function()| this converts directly between Vim
@@ -1036,13 +1035,27 @@ from Lua conveniently and idiomatically by referencing the `vim.*` Lua tables
described below. In this way you can easily read and modify global Vimscript
variables from Lua.
-Example: >
+Example: >lua
vim.g.foo = 5 -- Set the g:foo Vimscript variable.
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
<
+
+Note that setting dictionary fields directly will not write them back into
+Nvim. This is because the index into the namespace simply returns a copy.
+Instead the whole dictionary must be written as one. This can be achieved by
+creating a short-lived temporary.
+
+Example: >lua
+
+ vim.g.my_dict.field1 = 'value' -- Does not work
+
+ local my_dict = vim.g.my_dict --
+ my_dict.field1 = 'value' -- Instead do
+ vim.g.my_dict = my_dict --
+
vim.g *vim.g*
Global (|g:|) editor variables.
Key with no value returns `nil`.
@@ -1070,86 +1083,153 @@ 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`.
- Example: >
+ Example: >lua
vim.env.FOO = 'bar'
print(vim.env.TERM)
<
+ *lua-options*
*lua-vim-options*
- *lua-vim-opt*
*lua-vim-set*
- *lua-vim-optlocal*
*lua-vim-setlocal*
-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
-from within Lua.
+Vim options can be accessed through |vim.o|, which behaves like Vimscript
+|:set|.
Examples: ~
To set a boolean toggle:
- In Vimscript:
- `set number`
+ Vimscript: `set number`
+ Lua: `vim.o.number = true`
- In Lua:
- `vim.opt.number = true`
+ To set a string value:
+ Vimscript: `set wildignore=*.o,*.a,__pycache__`
+ Lua: `vim.o.wildignore = '*.o,*.a,__pycache__'`
- To set an array of values:
- In Vimscript:
- `set wildignore=*.o,*.a,__pycache__`
+Similarly, there is |vim.bo| and |vim.wo| for setting buffer-scoped and
+window-scoped options. Note that this must NOT be confused with
+|local-options| and |:setlocal|. There is also |vim.go| that only accesses the
+global value of a |global-local| option, see |:setglobal|.
- In Lua, there are two ways you can do this now. One is very similar to
- the Vimscript form:
- `vim.opt.wildignore = '*.o,*.a,__pycache__'`
+vim.o *vim.o*
+ Get or set |options|. Like `:set`. Invalid key is an error.
- However, vim.opt also supports a more elegent way of setting
- list-style options by using lua tables:
- `vim.opt.wildignore = { '*.o', '*.a', '__pycache__' }`
+ Note: this works on both buffer-scoped and window-scoped options using the
+ current buffer and window.
- To replicate the behavior of |:set+=|, use: >
+ Example: >lua
+ vim.o.cmdheight = 4
+ print(vim.o.columns)
+ print(vim.o.foo) -- error: invalid key
+<
+vim.go *vim.go*
+ Get or set global |options|. Like `:setglobal`. Invalid key is
+ an error.
- -- vim.opt supports appending options via the "+" operator
- vim.opt.wildignore = vim.opt.wildignore + { "*.pyc", "node_modules" }
+ Note: this is different from |vim.o| because this accesses the global
+ option value and thus is mostly useful for use with |global-local|
+ options.
- -- or using the `:append(...)` method
- vim.opt.wildignore:append { "*.pyc", "node_modules" }
+ Example: >lua
+ vim.go.cmdheight = 4
+ print(vim.go.columns)
+ print(vim.go.bar) -- error: invalid key
<
- To replicate the behavior of |:set^=|, use: >
+vim.bo[{bufnr}] *vim.bo*
+ Get or set buffer-scoped |options| for the buffer with number {bufnr}.
+ Like `:set` and `:setlocal`. If [{bufnr}] is omitted then the current
+ buffer is used. Invalid {bufnr} or key is an error.
- -- vim.opt supports prepending options via the "^" operator
- vim.opt.wildignore = vim.opt.wildignore ^ { "new_first_value" }
+ Note: this is equivalent to both `:set` and `:setlocal`.
- -- or using the `:prepend(...)` method
- vim.opt.wildignore:prepend { "new_first_value" }
+ Example: >lua
+ local bufnr = vim.api.nvim_get_current_buf()
+ vim.bo[bufnr].buflisted = true -- same as vim.bo.buflisted = true
+ print(vim.bo.comments)
+ print(vim.bo.baz) -- error: invalid key
<
- To replicate the behavior of |:set-=|, use: >
+vim.wo[{winid}] *vim.wo*
+ Get or set window-scoped |options| for the window with handle {winid}.
+ Like `:set`. If [{winid}] is omitted then the current window is used.
+ Invalid {winid} or key is an error.
- -- vim.opt supports removing options via the "-" operator
- vim.opt.wildignore = vim.opt.wildignore - { "node_modules" }
+ Note: this does not access |local-options| (`:setlocal`) instead use: >lua
+ nvim_get_option_value(OPTION, { scope = 'local', win = winid })
+ nvim_set_option_value(OPTION, VALUE, { scope = 'local', win = winid }
+<
+ Example: >lua
+ local winid = vim.api.nvim_get_current_win()
+ vim.wo[winid].number = true -- same as vim.wo.number = true
+ print(vim.wo.foldmarker)
+ print(vim.wo.quux) -- error: invalid key
+<
- -- or using the `:remove(...)` method
- vim.opt.wildignore:remove { "node_modules" }
+
+
+ *vim.opt_local*
+ *vim.opt_global*
+ *vim.opt*
+
+
+A special interface |vim.opt| exists for conveniently interacting with list-
+and map-style option from Lua: It allows accessing them as Lua tables and
+offers object-oriented method for adding and removing entries.
+
+ Examples: ~
+
+ The following methods of setting a list-style option are equivalent:
+ In Vimscript: >vim
+ set wildignore=*.o,*.a,__pycache__
+<
+ In Lua using `vim.o`: >lua
+ vim.o.wildignore = '*.o,*.a,__pycache__'
+<
+ In Lua using `vim.opt`: >lua
+ vim.opt.wildignore = { '*.o', '*.a', '__pycache__' }
+<
+ To replicate the behavior of |:set+=|, use: >lua
+
+ vim.opt.wildignore:append { "*.pyc", "node_modules" }
<
- To set a map of values:
- In Vimscript:
- `set listchars=space:_,tab:>~`
+ To replicate the behavior of |:set^=|, use: >lua
- In Lua:
- `vim.opt.listchars = { space = '_', tab = '>~' }`
+ vim.opt.wildignore:prepend { "new_first_value" }
+<
+ To replicate the behavior of |:set-=|, use: >lua
+ vim.opt.wildignore:remove { "node_modules" }
+<
+ The following methods of setting a map-style option are equivalent:
+ In Vimscript: >vim
+ set listchars=space:_,tab:>~
+<
+ In Lua using `vim.o`: >lua
+ vim.o.listchars = 'space:_,tab:>~'
+<
+ In Lua using `vim.opt`: >lua
+ vim.opt.listchars = { space = '_', tab = '>~' }
+<
-In any of the above examples, to replicate the behavior |setlocal|, use
-`vim.opt_local`. Additionally, to replicate the behavior of |setglobal|, use
-`vim.opt_global`.
- *vim.opt*
+Note that |vim.opt| returns an `Option` object, not the value of the option,
+which is accessed through |vim.opt:get()|:
-|vim.opt| returns an Option object.
+ Examples: ~
-For example: `local listchar_object = vim.opt.listchars`
+ The following methods of getting a list-style option are equivalent:
+ In Vimscript: >vim
+ echo wildignore
+<
+ In Lua using `vim.o`: >lua
+ print(vim.o.wildignore)
+<
+ In Lua using `vim.opt`: >lua
+ vim.pretty_print(vim.opt.wildignore:get())
+<
+
+In any of the above examples, to replicate the behavior |:setlocal|, use
+`vim.opt_local`. Additionally, to replicate the behavior of |:setglobal|, use
+`vim.opt_global`.
-An `Option` has the following methods:
*vim.opt:get()*
@@ -1159,10 +1239,10 @@ Option:get()
values will be returned in exactly the same fashion.
For values that are comma-separated lists, an array will be returned with
- the values as entries in the array: >
+ the values as entries in the array: >lua
vim.cmd [[set wildignore=*.pyc,*.o]]
- print(vim.inspect(vim.opt.wildignore:get()))
+ vim.pretty_print(vim.opt.wildignore:get())
-- { "*.pyc", "*.o", }
for _, ignore_pattern in ipairs(vim.opt.wildignore:get()) do
@@ -1172,21 +1252,21 @@ Option:get()
-- Will ignore: *.o
<
For values that are comma-separated maps, a table will be returned with
- the names as keys and the values as entries: >
+ the names as keys and the values as entries: >lua
vim.cmd [[set listchars=space:_,tab:>~]]
- print(vim.inspect(vim.opt.listchars:get()))
+ vim.pretty_print(vim.opt.listchars:get())
-- { space = "_", tab = ">~", }
for char, representation in pairs(vim.opt.listchars:get()) do
- print(char, "->", representation)
+ print(char, "=>", representation)
end
<
For values that are lists of flags, a set will be returned with the flags
- as keys and `true` as entries. >
+ as keys and `true` as entries. >lua
vim.cmd [[set formatoptions=njtcroql]]
- print(vim.inspect(vim.opt.formatoptions:get()))
+ vim.pretty_print(vim.opt.formatoptions:get())
-- { n = true, j = true, c = true, ... }
local format_opts = vim.opt.formatoptions:get()
@@ -1199,94 +1279,29 @@ Option:append(value)
Append a value to string-style options. See |:set+=|
- These are equivalent:
- `vim.opt.formatoptions:append('j')`
- `vim.opt.formatoptions = vim.opt.formatoptions + 'j'`
-
+ These are equivalent: >lua
+ vim.opt.formatoptions:append('j')
+ vim.opt.formatoptions = vim.opt.formatoptions + 'j'
+<
*vim.opt:prepend()*
Option:prepend(value)
Prepend a value to string-style options. See |:set^=|
- These are equivalent:
- `vim.opt.wildignore:prepend('*.o')`
- `vim.opt.wildignore = vim.opt.wildignore ^ '*.o'`
-
+ These are equivalent: >lua
+ vim.opt.wildignore:prepend('*.o')
+ vim.opt.wildignore = vim.opt.wildignore ^ '*.o'
+<
*vim.opt:remove()*
Option:remove(value)
Remove a value from string-style options. See |:set-=|
- These are equivalent:
- `vim.opt.wildignore:remove('*.pyc')`
- `vim.opt.wildignore = vim.opt.wildignore - '*.pyc'`
-
-
-In general, using `vim.opt` will provide the expected result when the user is
-used to interacting with editor |options| via `set`. There are still times
-where the user may want to set particular options via a shorthand in Lua,
-which is where |vim.o|, |vim.bo|, |vim.wo|, and |vim.go| come into play.
-
-The behavior of |vim.o|, |vim.bo|, |vim.wo|, and |vim.go| is designed to
-follow that of |:set|, |:setlocal|, and |:setglobal| which can be seen in the
-table below:
-
- lua command global_value local_value ~
-vim.o :set set set
-vim.bo/vim.wo :setlocal - set
-vim.go :setglobal set -
-
-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)
- print(vim.o.foo) -- error: invalid key
-<
-vim.go *vim.go*
- Get or set an |option|. Invalid key is an error.
-
- This is a wrapper around |nvim_set_option_value()| and
- |nvim_get_option_value()|.
-
- NOTE: This is different from |vim.o| because this ONLY sets the global
- option, which generally produces confusing behavior for options with
- |global-local| values.
-
- Example: >
- vim.go.cmdheight = 4
- print(vim.go.columns)
- print(vim.go.bar) -- error: invalid key
-<
-vim.bo[{bufnr}] *vim.bo*
- Get or set buffer-scoped |local-options| for the buffer with number {bufnr}.
- If [{bufnr}] is omitted, use the current buffer. Invalid {bufnr} or key is
- an error.
-
- This is a wrapper around |nvim_set_option_value()| and
- |nvim_get_option_value()| with `opts = {scope = local, buf = bufnr}` .
-
- Example: >
- local bufnr = vim.api.nvim_get_current_buf()
- vim.bo[bufnr].buflisted = true -- same as vim.bo.buflisted = true
- print(vim.bo.comments)
- print(vim.bo.baz) -- error: invalid key
+ These are equivalent: >lua
+ vim.opt.wildignore:remove('*.pyc')
+ vim.opt.wildignore = vim.opt.wildignore - '*.pyc'
<
-vim.wo[{winid}] *vim.wo*
- Get or set window-scoped |local-options| for the window with handle {winid}.
- If [{winid}] is omitted, use the current window. Invalid {winid} or key
- is an error.
- This is a wrapper around |nvim_set_option_value()| and
- |nvim_get_option_value()| with `opts = {scope = local, win = winid}` .
-
- Example: >
- local winid = vim.api.nvim_get_current_win()
- vim.wo[winid].number = true -- same as vim.wo.number = true
- print(vim.wo.foldmarker)
- print(vim.wo.quux) -- error: invalid key
-<
==============================================================================
Lua module: vim *lua-vim*
@@ -1296,7 +1311,7 @@ cmd({command}) *vim.cmd()*
Note that `vim.cmd` can be indexed with a command name to return a
callable function to the command.
- Example: >
+ Example: >lua
vim.cmd('echo 42')
vim.cmd([[
@@ -1325,7 +1340,7 @@ cmd({command}) *vim.cmd()*
<
Parameters: ~
- {command} string|table Command(s) to execute. If a string, executes
+ • {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
@@ -1342,31 +1357,31 @@ connection_failure_errmsg({consequence})
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
- safe to call.
+ Use to do a one-shot timer that calls `fn` Note: The {fn} is |vim.schedule_wrap()|ped automatically, so API functions
+ are safe to call.
Parameters: ~
- {fn} Callback to call once `timeout` expires
- {timeout} Number of milliseconds to wait before calling `fn`
+ • {fn} (function) Callback to call once `timeout` expires
+ • {timeout} integer Number of milliseconds to wait before calling `fn`
Return: ~
- timer luv timer object
+ (table) 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
+ • {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
+ • {plugin} string|nil Plugin name that the function will be
removed from. Defaults to "Nvim".
- {backtrace} boolean|nil Prints backtrace. Defaults to true.
+ • {backtrace} boolean|nil Prints backtrace. Defaults to true.
inspect({object}, {options}) *vim.inspect()*
- Return a human-readable representation of the given object.
+ Gets a human-readable representation of the given object.
See also: ~
https://github.com/kikito/inspect.lua
@@ -1380,9 +1395,9 @@ notify({msg}, {level}, {opts}) *vim.notify()*
writes to |:messages|.
Parameters: ~
- {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.
+ • {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.
notify_once({msg}, {level}, {opts}) *vim.notify_once()*
Display a notification only one time.
@@ -1391,9 +1406,9 @@ notify_once({msg}, {level}, {opts}) *vim.notify_once()*
display a notification.
Parameters: ~
- {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.
+ • {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.
Return: ~
(boolean) true if message was displayed, else false
@@ -1412,11 +1427,11 @@ on_key({fn}, {ns_id}) *vim.on_key()*
{fn} will receive the keys after mappings have been evaluated
Parameters: ~
- {fn} function: Callback function. It should take one string
+ • {fn} (function) Callback function. It should take one string
argument. On each key press, Nvim passes the key char to
fn(). |i_CTRL-V| If {fn} is nil, it removes the callback for
the associated {ns_id}
- {ns_id} number? Namespace ID. If nil or 0, generates and returns a
+ • {ns_id} number? Namespace ID. If nil or 0, generates and returns a
new |nvim_create_namespace()| id.
Return: ~
@@ -1430,7 +1445,7 @@ paste({lines}, {phase}) *vim.paste()*
Paste handler, invoked by |nvim_paste()| when a conforming UI (such as the
|TUI|) pastes text into the editor.
- Example: To remove ANSI color codes when pasting: >
+ Example: To remove ANSI color codes when pasting: >lua
vim.paste = (function(overridden)
return function(lines, phase)
@@ -1444,27 +1459,28 @@ paste({lines}, {phase}) *vim.paste()*
<
Parameters: ~
- {lines} |readfile()|-style list of lines to paste. |channel-lines|
- {phase} -1: "non-streaming" paste: the call contains all lines. If
- paste is "streamed", `phase` indicates the stream state:
+ • {lines} string[] # |readfile()|-style list of lines to paste.
+ |channel-lines|
+ • {phase} paste_phase -1: "non-streaming" paste: the call contains all
+ lines. If paste is "streamed", `phase` indicates the stream state:
• 1: starts the paste (exactly once)
• 2: continues the paste (zero or more times)
• 3: ends the paste (exactly once)
Return: ~
- false if client should cancel the paste.
+ (boolean) # false if client should cancel the paste.
See also: ~
- |paste|
+ |paste| @alias paste_phase -1 | 1 | 2 | 3
pretty_print({...}) *vim.pretty_print()*
- Prints given arguments in human-readable format. Example: >
+ Prints given arguments in human-readable format. Example: >lua
-- Print highlight group Normal and store it's contents in a variable.
local hl_normal = vim.pretty_print(vim.api.nvim_get_hl_by_name("Normal", true))
<
Return: ~
- given arguments.
+ any # given arguments.
See also: ~
|vim.inspect()|
@@ -1474,25 +1490,82 @@ region({bufnr}, {pos1}, {pos2}, {regtype}, {inclusive}) *vim.region()*
points
Parameters: ~
- {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
+ • {bufnr} (number) of buffer
+ • {pos1} integer[] (line, column) tuple marking beginning of
+ region
+ • {pos2} integer[] (line, column) tuple marking end of region
+ • {regtype} (string) type of selection, see |setreg()|
+ • {inclusive} (boolean) indicating whether the selection is
end-inclusive
Return: ~
- region lua table of the form {linenr = {startcol,endcol}}
+ (table) region Table of the form `{linenr = {startcol,endcol}}`
schedule_wrap({cb}) *vim.schedule_wrap()*
Defers callback `cb` until the Nvim API is safe to call.
+ Parameters: ~
+ • {cb} (function)
+
+ Return: ~
+ (function)
+
See also: ~
|lua-loop-callbacks|
|vim.schedule()|
|vim.in_fast_event()|
+==============================================================================
+Lua module: inspector *lua-inspector*
+
+inspect_pos({bufnr}, {row}, {col}, {filter}) *vim.inspect_pos()*
+ Get all the items at a given buffer position.
+
+ Can also be pretty-printed with `:Inspect!`. *:Inspect!*
+
+ Parameters: ~
+ • {bufnr} (number|nil) defaults to the current buffer
+ • {row} (number|nil) row to inspect, 0-based. Defaults to the row of
+ the current cursor
+ • {col} (number|nil) col to inspect, 0-based. Defaults to the col of
+ the current cursor
+ • {filter} (table|nil) a table with key-value pairs to filter the items
+ • syntax (boolean): include syntax based highlight groups
+ (defaults to true)
+ • treesitter (boolean): include treesitter based highlight
+ groups (defaults to true)
+ • extmarks (boolean|"all"): include extmarks. When `all`,
+ then extmarks without a `hl_group` will also be included
+ (defaults to true)
+ • semantic_tokens (boolean): include semantic tokens
+ (defaults to true)
+
+ Return: ~
+ (table) a table with the following key-value pairs. Items are in
+ "traversal order":
+ • treesitter: a list of treesitter captures
+ • syntax: a list of syntax groups
+ • semantic_tokens: a list of semantic tokens
+ • extmarks: a list of extmarks
+ • buffer: the buffer used to get the items
+ • row: the row used to get the items
+ • col: the col used to get the items
+
+show_pos({bufnr}, {row}, {col}, {filter}) *vim.show_pos()*
+ Show all the items at a given buffer position.
+
+ Can also be shown with `:Inspect`. *:Inspect*
+
+ Parameters: ~
+ • {bufnr} (number|nil) defaults to the current buffer
+ • {row} (number|nil) row to inspect, 0-based. Defaults to the row of
+ the current cursor
+ • {col} (number|nil) col to inspect, 0-based. Defaults to the col of
+ the current cursor
+ • {filter} (table|nil) see |vim.inspect_pos()|
+
+
deep_equal({a}, {b}) *vim.deep_equal()*
@@ -1501,8 +1574,8 @@ deep_equal({a}, {b}) *vim.deep_equal()*
Tables are compared recursively unless they both provide the `eq` metamethod. All other types are compared using the equality `==` operator.
Parameters: ~
- {a} any First value
- {b} any Second value
+ • {a} any First value
+ • {b} any Second value
Return: ~
(boolean) `true` if values are equals, else `false`
@@ -1515,17 +1588,40 @@ deepcopy({orig}) *vim.deepcopy()*
not copied and will throw an error.
Parameters: ~
- {orig} (table) Table to copy
+ • {orig} (table) Table to copy
Return: ~
(table) Table of copied keys and (nested) values.
+defaulttable({create}) *vim.defaulttable()*
+ Creates a table whose members are automatically created when accessed, if
+ they don't already exist.
+
+ They mimic defaultdict in python.
+
+ If {create} is `nil`, this will create a defaulttable whose constructor
+ function is this function, effectively allowing to create nested tables on
+ the fly:
+
+ >lua
+
+ local a = vim.defaulttable()
+ a.b.c = 1
+<
+
+ Parameters: ~
+ • {create} (function|nil) The function called to create a missing
+ value.
+
+ Return: ~
+ (table) Empty table with metamethod
+
endswith({s}, {suffix}) *vim.endswith()*
Tests if `s` ends with `suffix`.
Parameters: ~
- {s} (string) String
- {suffix} (string) Suffix to match
+ • {s} (string) String
+ • {suffix} (string) Suffix to match
Return: ~
(boolean) `true` if `suffix` is a suffix of `s`
@@ -1534,9 +1630,9 @@ gsplit({s}, {sep}, {plain}) *vim.gsplit()*
Splits a string at each instance of a separator.
Parameters: ~
- {s} (string) String to split
- {sep} (string) Separator or pattern
- {plain} (boolean) If `true` use `sep` literally (passed to
+ • {s} (string) String to split
+ • {sep} (string) Separator or pattern
+ • {plain} (boolean|nil) If `true` use `sep` literally (passed to
string.find)
Return: ~
@@ -1544,6 +1640,7 @@ gsplit({s}, {sep}, {plain}) *vim.gsplit()*
See also: ~
|vim.split()|
+ |luaref-patterns|
https://www.lua.org/pil/20.2.html
http://lua-users.org/wiki/StringLibraryTutorial
@@ -1551,7 +1648,7 @@ is_callable({f}) *vim.is_callable()*
Returns true if object `f` can be called as a function.
Parameters: ~
- {f} any Any object
+ • {f} any Any object
Return: ~
(boolean) `true` if `f` is callable, else `false`
@@ -1562,10 +1659,10 @@ list_extend({dst}, {src}, {start}, {finish}) *vim.list_extend()*
NOTE: This mutates dst!
Parameters: ~
- {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`
+ • {dst} (table) List which will be modified and appended to
+ • {src} (table) List from which values will be inserted
+ • {start} (number|nil) Start index on src. Defaults to 1
+ • {finish} (number|nil) Final index on src. Defaults to `#src`
Return: ~
(table) dst
@@ -1578,18 +1675,18 @@ list_slice({list}, {start}, {finish}) *vim.list_slice()*
(inclusive)
Parameters: ~
- {list} (table) Table
- {start} (number) Start range of slice
- {finish} (number) End range of slice
+ • {list} (list) Table
+ • {start} (number|nil) Start range of slice
+ • {finish} (number|nil) End range of slice
Return: ~
- (table) Copy of table sliced from start to finish (inclusive)
+ (list) Copy of table sliced from start to finish (inclusive)
pesc({s}) *vim.pesc()*
Escapes magic chars in |lua-patterns|.
Parameters: ~
- {s} (string) String to escape
+ • {s} (string) String to escape
Return: ~
(string) %-escaped pattern string
@@ -1597,28 +1694,40 @@ pesc({s}) *vim.pesc()*
See also: ~
https://github.com/rxi/lume
+spairs({t}) *vim.spairs()*
+ Enumerate a table sorted by its keys.
+
+ Parameters: ~
+ • {t} (table) List-like table
+
+ Return: ~
+ iterator over sorted keys and their values
+
+ See also: ~
+ Based on https://github.com/premake/premake-core/blob/master/src/base/table.lua
+
split({s}, {sep}, {kwargs}) *vim.split()*
Splits a string at each instance of a separator.
- Examples: >
+ Examples: >lua
- split(":aa::b:", ":") --> {'','aa','','b',''}
- split("axaby", "ab?") --> {'','x','y'}
- split("x*yz*o", "*", {plain=true}) --> {'x','yz','o'}
+ split(":aa::b:", ":") --> {'','aa','','b',''}
+ split("axaby", "ab?") --> {'','x','y'}
+ split("x*yz*o", "*", {plain=true}) --> {'x','yz','o'}
split("|x|y|z|", "|", {trimempty=true}) --> {'x', 'y', 'z'}
<
Parameters: ~
- {s} (string) String to split
- {sep} (string) Separator or pattern
- {kwargs} (table) Keyword arguments:
+ • {s} (string) String to split
+ • {sep} (string) Separator or pattern
+ • {kwargs} (table|nil) 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: ~
- (table) List of split components
+ string[] List of split components
See also: ~
|vim.gsplit()|
@@ -1627,8 +1736,8 @@ startswith({s}, {prefix}) *vim.startswith()*
Tests if `s` starts with `prefix`.
Parameters: ~
- {s} (string) String
- {prefix} (string) Prefix to match
+ • {s} (string) String
+ • {prefix} (string) Prefix to match
Return: ~
(boolean) `true` if `prefix` is a prefix of `s`
@@ -1640,7 +1749,7 @@ tbl_add_reverse_lookup({o}) *vim.tbl_add_reverse_lookup()*
Note that this modifies the input.
Parameters: ~
- {o} (table) Table to add the reverse to
+ • {o} (table) Table to add the reverse to
Return: ~
(table) o
@@ -1649,22 +1758,23 @@ tbl_contains({t}, {value}) *vim.tbl_contains()*
Checks if a list-like (vector) table contains `value`.
Parameters: ~
- {t} (table) Table to check
- {value} any Value to compare
+ • {t} (table) Table to check
+ • {value} any Value to compare
Return: ~
(boolean) `true` if `t` contains `value`
tbl_count({t}) *vim.tbl_count()*
Counts the number of non-nil values in table `t`.
->
- vim.tbl_count({ a=1, b=2 }) => 2
- vim.tbl_count({ 1, 2 }) => 2
+ >lua
+
+ vim.tbl_count({ a=1, b=2 }) --> 2
+ vim.tbl_count({ 1, 2 }) --> 2
<
Parameters: ~
- {t} (table) Table
+ • {t} (table) Table
Return: ~
(number) Number of non-nil values in table
@@ -1676,29 +1786,29 @@ tbl_deep_extend({behavior}, {...}) *vim.tbl_deep_extend()*
Merges recursively two or more map-like tables.
Parameters: ~
- {behavior} (string) Decides what to do if a key is found in more than
+ • {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
- {...} (table) Two or more map-like tables
+ • {...} (table) Two or more map-like tables
Return: ~
(table) Merged table
See also: ~
- |tbl_extend()|
+ |vim.tbl_extend()|
tbl_extend({behavior}, {...}) *vim.tbl_extend()*
Merges two or more map-like tables.
Parameters: ~
- {behavior} (string) Decides what to do if a key is found in more than
+ • {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
- {...} (table) Two or more map-like tables
+ • {...} (table) Two or more map-like tables
Return: ~
(table) Merged table
@@ -1710,8 +1820,8 @@ tbl_filter({func}, {t}) *vim.tbl_filter()*
Filter a table using a predicate function
Parameters: ~
- {func} function|table Function or callable table
- {t} (table) Table
+ • {func} (function) Function
+ • {t} (table) Table
Return: ~
(table) Table of filtered values
@@ -1721,7 +1831,7 @@ tbl_flatten({t}) *vim.tbl_flatten()*
"unrolled" and appended to the result.
Parameters: ~
- {t} (table) List-like table
+ • {t} (table) List-like table
Return: ~
(table) Flattened copy of the given list-like table
@@ -1733,15 +1843,15 @@ 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: >
+ Examples: >lua
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
+ • {o} (table) Table to index
+ • {...} (string) Optional strings (0 or more, variadic) via which to
index the table
Return: ~
@@ -1751,7 +1861,7 @@ tbl_isempty({t}) *vim.tbl_isempty()*
Checks if a table is empty.
Parameters: ~
- {t} (table) Table to check
+ • {t} (table) Table to check
Return: ~
(boolean) `true` if `t` is empty
@@ -1767,7 +1877,7 @@ tbl_islist({t}) *vim.tbl_islist()*
for example from |rpcrequest()| or |vim.fn|.
Parameters: ~
- {t} (table) Table
+ • {t} (table) Table
Return: ~
(boolean) `true` if array-like table, else `false`
@@ -1777,10 +1887,10 @@ tbl_keys({t}) *vim.tbl_keys()*
return table of keys is not guaranteed.
Parameters: ~
- {t} (table) Table
+ • {t} (table) Table
Return: ~
- (table) List of keys
+ (list) List of keys
See also: ~
From https://github.com/premake/premake-core/blob/master/src/base/table.lua
@@ -1789,8 +1899,8 @@ tbl_map({func}, {t}) *vim.tbl_map()*
Apply a function to all values of a table.
Parameters: ~
- {func} function|table Function or callable table
- {t} (table) Table
+ • {func} (function) Function
+ • {t} (table) Table
Return: ~
(table) Table of transformed values
@@ -1800,27 +1910,28 @@ tbl_values({t}) *vim.tbl_values()*
return table of values is not guaranteed.
Parameters: ~
- {t} (table) Table
+ • {t} (table) Table
Return: ~
- (table) List of values
+ (list) List of values
trim({s}) *vim.trim()*
Trim whitespace (Lua pattern "%s") from both sides of a string.
Parameters: ~
- {s} (string) String to trim
+ • {s} (string) String to trim
Return: ~
(string) String with whitespace removed from its beginning and end
See also: ~
+ |luaref-patterns|
https://www.lua.org/pil/20.2.html
validate({opt}) *vim.validate()*
Validates a parameter specification (types and values).
- Usage example: >
+ Usage example: >lua
function user.new(name, age, hobbies)
vim.validate{
@@ -1832,29 +1943,29 @@ validate({opt}) *vim.validate()*
end
<
- Examples with explicit argument values (can be run directly): >
+ Examples with explicit argument values (can be run directly): >lua
vim.validate{arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}}
- => NOP (success)
+ --> NOP (success)
vim.validate{arg1={1, 'table'}}
- => error('arg1: expected table, got number')
+ --> error('arg1: expected table, got number')
vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}}
- => error('arg1: expected even number, got 3')
+ --> error('arg1: expected even number, got 3')
<
- If multiple types are valid they can be given as a list. >
+ If multiple types are valid they can be given as a list. >lua
vim.validate{arg1={{'foo'}, {'table', 'string'}}, arg2={'foo', {'table', 'string'}}}
- => NOP (success)
+ --> NOP (success)
vim.validate{arg1={1, {'string', table'}}}
- => error('arg1: expected string|table, got number')
+ --> error('arg1: expected string|table, got number')
<
Parameters: ~
- {opt} (table) Names of parameters to validate. Each key is a
+ • {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
@@ -1879,7 +1990,7 @@ uri_from_bufnr({bufnr}) *vim.uri_from_bufnr()*
Get a URI from a bufnr
Parameters: ~
- {bufnr} (number)
+ • {bufnr} (number)
Return: ~
(string) URI
@@ -1888,7 +1999,7 @@ 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
@@ -1898,7 +2009,7 @@ uri_to_bufnr({uri}) *vim.uri_to_bufnr()*
the uri already exists.
Parameters: ~
- {uri} (string)
+ • {uri} (string)
Return: ~
(number) bufnr
@@ -1907,7 +2018,7 @@ 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
@@ -1919,7 +2030,7 @@ Lua module: ui *lua-ui*
input({opts}, {on_confirm}) *vim.ui.input()*
Prompts the user for input
- Example: >
+ Example: >lua
vim.ui.input({ prompt = 'Enter value for shiftwidth: ' }, function(input)
vim.o.shiftwidth = tonumber(input)
@@ -1927,7 +2038,7 @@ input({opts}, {on_confirm}) *vim.ui.input()*
<
Parameters: ~
- {opts} (table) Additional options. See |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 completion
@@ -1936,14 +2047,15 @@ input({opts}, {on_confirm}) *vim.ui.input()*
"-complete=" argument. See |:command-completion|
• highlight (function) Function that will be used for
highlighting user inputs.
- {on_confirm} (function) ((input|nil) -> ()) Called once the user
+ • {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.
+ typed (it might be an empty string if nothing was
+ entered), or `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: >
+ Example: >lua
vim.ui.select({ 'tabs', 'spaces' }, {
prompt = 'Select tabs or spaces:',
@@ -1960,8 +2072,8 @@ select({items}, {opts}, {on_choice}) *vim.ui.select()*
<
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
@@ -1971,7 +2083,7 @@ select({items}, {opts}, {on_choice}) *vim.ui.select()*
item shape. Plugins reimplementing `vim.ui.select` may
wish to use this to infer the structure or semantics of
`items`, or the context in which select() was called.
- {on_choice} (function) ((item|nil, idx|nil) -> ()) Called once the
+ • {on_choice} (function) ((item|nil, idx|nil) -> ()) Called once the
user made a choice. `idx` is the 1-based index of `item`
within `items`. `nil` if the user aborted the dialog.
@@ -1999,14 +2111,14 @@ add({filetypes}) *vim.filetype.add()*
Filename patterns can specify an optional priority to resolve cases when a
file path matches multiple patterns. Higher priorities are matched first.
- When omitted, the priority defaults to 0.
+ When omitted, the priority defaults to 0. A pattern can contain
+ environment variables of the form "${SOME_VAR}" that will be automatically
+ expanded. If the environment variable is not set, the pattern won't be
+ matched.
See $VIMRUNTIME/lua/vim/filetype.lua for more examples.
- Note that Lua filetype detection is disabled when |g:do_legacy_filetype|
- is set.
-
- Example: >
+ Example: >lua
vim.filetype.add({
extension = {
@@ -2029,6 +2141,8 @@ add({filetypes}) *vim.filetype.add()*
['.*/etc/foo/.*'] = 'fooscript',
-- Using an optional priority
['.*/etc/foo/.*%.conf'] = { 'dosini', { priority = 10 } },
+ -- A pattern containing an environment variable
+ ['${XDG_CONFIG_HOME}/foo/git'] = 'git',
['README.(a+)$'] = function(path, bufnr, ext)
if ext == 'md' then
return 'markdown'
@@ -2040,7 +2154,7 @@ add({filetypes}) *vim.filetype.add()*
})
<
- To add a fallback match on contents (see |new-filetype-scripts|), use >
+ To add a fallback match on contents, use >lua
vim.filetype.add {
pattern = {
@@ -2060,7 +2174,7 @@ add({filetypes}) *vim.filetype.add()*
<
Parameters: ~
- {filetypes} (table) A table containing new filetype maps (see
+ • {filetypes} (table) A table containing new filetype maps (see
example).
match({args}) *vim.filetype.match()*
@@ -2079,23 +2193,24 @@ match({args}) *vim.filetype.match()*
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 })
+ >lua
+
+ -- Using a buffer number
+ vim.filetype.match({ buf = 42 })
- -- Override the filename of the given buffer
- vim.filetype.match({ buf = 42, filename = 'foo.c' })
+ -- 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 a filename without a buffer
+ vim.filetype.match({ filename = 'main.lua' })
- -- Using file contents
- vim.filetype.match({ contents = {'#!/usr/bin/env bash'} })
+ -- Using file contents
+ vim.filetype.match({ contents = {'#!/usr/bin/env bash'} })
<
Parameters: ~
- {args} (table) Table specifying which matching strategy to use.
+ • {args} (table) Table specifying which matching strategy to use.
Accepted keys are:
• buf (number): Buffer number to use for matching. Mutually
exclusive with {contents}
@@ -2121,7 +2236,7 @@ match({args}) *vim.filetype.match()*
Lua module: keymap *lua-keymap*
del({modes}, {lhs}, {opts}) *vim.keymap.del()*
- Remove an existing mapping. Examples: >
+ Remove an existing mapping. Examples: >lua
vim.keymap.del('n', 'lhs')
@@ -2129,7 +2244,7 @@ del({modes}, {lhs}, {opts}) *vim.keymap.del()*
<
Parameters: ~
- {opts} (table) A table of optional arguments:
+ • {opts} (table|nil) A table of optional arguments:
• buffer: (number or boolean) Remove a mapping from the given
buffer. When "true" or 0, use the current buffer.
@@ -2137,7 +2252,7 @@ del({modes}, {lhs}, {opts}) *vim.keymap.del()*
|vim.keymap.set()|
set({mode}, {lhs}, {rhs}, {opts}) *vim.keymap.set()*
- Add a new |mapping|. Examples: >
+ Add a new |mapping|. Examples: >lua
-- Can add mapping to Lua functions
vim.keymap.set('n', 'lhs', function() print("real lua function") end)
@@ -2156,25 +2271,25 @@ set({mode}, {lhs}, {rhs}, {opts}) *vim.keymap.set()*
vim.keymap.set('n', '[%', '<Plug>(MatchitNormalMultiBackward)')
<
- Note that in a mapping like: >
+ Note that in a mapping like: >lua
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: >
+ example: >lua
vim.keymap.set('n', 'asdf', function() return require('jkl').my_fun() end)
<
Parameters: ~
- {mode} string|table Same mode short names as |nvim_set_keymap()|. Can
+ • {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.
- {rhs} string|function Right-hand side |{rhs}| of the mapping. Can
+ • {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|.
+ • {opts} (table|nil) A table of |:map-arguments|.
• Accepts options accepted by the {opts} parameter in
|nvim_set_keymap()|, with the following notable differences:
• replace_keycodes: Defaults to `true` if "expr" is `true`.
@@ -2200,18 +2315,23 @@ basename({file}) *vim.fs.basename()*
Return the basename of the given file or directory
Parameters: ~
- {file} (string) File or directory
+ • {file} (string) File or directory
Return: ~
(string) Basename of {file}
-dir({path}) *vim.fs.dir()*
+dir({path}, {opts}) *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
+ • {path} (string) An absolute or relative path to the directory to
iterate over. The path is first normalized
|vim.fs.normalize()|.
+ • {opts} table|nil Optional keyword arguments:
+ • depth: integer|nil How deep the traverse (default 1)
+ • skip: (fun(dir_name: string): boolean)|nil Predicate to
+ control traversal. Return false to stop searching the
+ current directory. Only useful when depth > 1
Return: ~
Iterator over files and directories in {path}. Each iteration yields
@@ -2222,7 +2342,7 @@ dirname({file}) *vim.fs.dirname()*
Return the parent directory of the given file or directory
Parameters: ~
- {file} (string) File or directory
+ • {file} (string) File or directory
Return: ~
(string) Parent directory of {file}
@@ -2236,15 +2356,18 @@ find({names}, {opts}) *vim.fs.find()*
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
+ The search can be narrowed to find only files 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:
+ • {names} (string|table|fun(name: string): boolean) Names of the files
+ and directories to find. Must be base names, paths and globs
+ are not supported. The function is called per file and
+ directory within the traversed directories to test if they
+ match {names}.
+ • {opts} (table) Optional keyword arguments:
• path (string): Path to begin searching from. If omitted,
- the current working directory is used.
+ the |current-directory| is used.
• upward (boolean, default false): If true, search upward
through parent directories. Otherwise, search through child
directories (recursively).
@@ -2252,13 +2375,14 @@ find({names}, {opts}) *vim.fs.find()*
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.
+ match {names} 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
+ (table) Normalized paths |vim.fs.normalize()| of all matching files or
+ directories
normalize({path}) *vim.fs.normalize()*
Normalize a path to a standard format. A tilde (~) character at the
@@ -2266,20 +2390,20 @@ normalize({path}) *vim.fs.normalize()*
backslash (\) characters are converted to forward slashes (/). Environment
variables are also expanded.
- Example: >
+ Examples: >lua
- vim.fs.normalize('C:\Users\jdoe')
- => 'C:/Users/jdoe'
+ vim.fs.normalize('C:\\Users\\jdoe')
+ --> 'C:/Users/jdoe'
- vim.fs.normalize('~/src/neovim')
- => '/home/jdoe/src/neovim'
+ vim.fs.normalize('~/src/neovim')
+ --> '/home/jdoe/src/neovim'
- vim.fs.normalize('$XDG_CONFIG_HOME/nvim/init.vim')
- => '/Users/jdoe/.config/nvim/init.vim'
+ vim.fs.normalize('$XDG_CONFIG_HOME/nvim/init.vim')
+ --> '/Users/jdoe/.config/nvim/init.vim'
<
Parameters: ~
- {path} (string) Path to normalize
+ • {path} (string) Path to normalize
Return: ~
(string) Normalized path
@@ -2287,7 +2411,7 @@ normalize({path}) *vim.fs.normalize()*
parents({start}) *vim.fs.parents()*
Iterate over all the parents of the given file or directory.
- Example: >
+ Example: >lua
local root_dir
for dir in vim.fs.parents(vim.api.nvim_buf_get_name(0)) do
@@ -2303,9 +2427,49 @@ parents({start}) *vim.fs.parents()*
<
Parameters: ~
- {start} (string) Initial file or directory.
+ • {start} (string) Initial file or directory.
Return: ~
(function) Iterator
+
+==============================================================================
+Lua module: secure *lua-secure*
+
+read({path}) *vim.secure.read()*
+ Attempt to read the file at {path} prompting the user if the file should
+ be trusted. The user's choice is persisted in a trust database at
+ $XDG_STATE_HOME/nvim/trust.
+
+ Parameters: ~
+ • {path} (string) Path to a file to read.
+
+ Return: ~
+ (string|nil) The contents of the given file if it exists and is
+ trusted, or nil otherwise.
+
+ See also: ~
+ |:trust|
+
+trust({opts}) *vim.secure.trust()*
+ Manage the trust database.
+
+ The trust database is located at |$XDG_STATE_HOME|/nvim/trust.
+
+ Parameters: ~
+ • {opts} (table)
+ • action (string): "allow" to add a file to the trust database
+ and trust it, "deny" to add a file to the trust database and
+ deny it, "remove" to remove file from the trust database
+ • path (string|nil): Path to a file to update. Mutually
+ exclusive with {bufnr}. Cannot be used when {action} is
+ "allow".
+ • bufnr (number|nil): Buffer number to update. Mutually
+ exclusive with {path}.
+
+ Return: ~
+ (boolean, string) success, msg:
+ • true and full path of target file if operation was successful
+ • false and error message on failure
+
vim:tw=78:ts=8:sw=4:sts=4:et:ft=help:norl:
diff --git a/runtime/doc/luaref.txt b/runtime/doc/luaref.txt
index 9dbd2d4de5..aafdd5c43e 100644
--- a/runtime/doc/luaref.txt
+++ b/runtime/doc/luaref.txt
@@ -20,76 +20,10 @@
See |luaref-copyright| for copyright and licenses.
- CONTENTS
- ============
-
- 1 INTRODUCTION........................|luaref-intro|
-
- 2 THE LANGUAGE........................|luaref-language|
- 2.1 Lexical Conventions...............|luaref-langLexConv|
- 2.2 Values and Types..................|luaref-langValTypes|
- 2.2.1 Coercion........................|luaref-langCoercion|
- 2.3 Variables.........................|luaref-langVariables|
- 2.4 Statements........................|luaref-langStats|
- 2.4.1 Chunks..........................|luaref-langChunks|
- 2.4.2 Blocks..........................|luaref-langBlocks|
- 2.4.3 Assignment......................|luaref-langAssign|
- 2.4.4 Control Structures..............|luaref-langContStructs|
- 2.4.5 For Statement...................|luaref-langForStat|
- 2.4.6 Function Calls as Statements....|luaref-langFuncStat|
- 2.4.7 Local Declarations..............|luaref-langLocalDec|
- 2.5 Expressions.......................|luaref-langExpressions|
- 2.5.1 Arithmetic Operators............|luaref-langArithOp|
- 2.5.2 Relational Operators............|luaref-langRelOp|
- 2.5.3 Logical Operators...............|luaref-langLogOp|
- 2.5.4 Concatenation...................|luaref-langConcat|
- 2.5.5 The Length Operator.............|luaref-langLength|
- 2.5.6 Precedence......................|luaref-langPrec|
- 2.5.7 Table Constructors..............|luaref-langTableConst|
- 2.5.8 Function Calls..................|luaref-langFuncCalls|
- 2.5.9 Function Definitions............|luaref-langFuncDefs|
- 2.6 Visibility Rules..................|luaref-langVisibRules|
- 2.7 Error Handling....................|luaref-langError|
- 2.8 Metatables........................|luaref-langMetatables|
- 2.9 Environments......................|luaref-langEnvironments|
- 2.10 Garbage Collection...............|luaref-langGC|
- 2.10.1 Garbage-Collection Metamethods.|luaref-langGCMeta|
- 2.10.2 Weak Tables....................|luaref-langWeaktables|
- 2.11 Coroutines.......................|luaref-langCoro|
-
- 3 THE APPLICATION PROGRAM INTERFACE...|luaref-api|
- 3.1 The Stack.........................|luaref-apiStack|
- 3.2 Stack Size........................|luaref-apiStackSize|
- 3.3 Pseudo-Indices....................|luaref-apiPseudoIndices|
- 3.4 C Closures........................|luaref-apiCClosures|
- 3.5 Registry..........................|luaref-apiRegistry|
- 3.6 Error Handling in C...............|luaref-apiError|
- 3.7 Functions and Types...............|luaref-apiFunctions|
- 3.8 The Debug Interface...............|luaref-apiDebug|
-
- 4 THE AUXILIARY LIBRARY...............|luaref-aux|
- 4.1 Functions and Types...............|luaref-auxFunctions|
-
- 5 STANDARD LIBRARIES..................|luaref-lib|
- 5.1 Basic Functions...................|luaref-libBasic|
- 5.2 Coroutine Manipulation............|luaref-libCoro|
- 5.3 Modules...........................|luaref-libModule|
- 5.4 String Manipulation...............|luaref-libString|
- 5.4.1 Patterns........................|luaref-libStringPat|
- 5.5 Table Manipulation................|luaref-libTable|
- 5.6 Mathematical Functions............|luaref-libMath|
- 5.7 Input and Output Facilities.......|luaref-libIO|
- 5.8 Operating System Facilities.......|luaref-libOS|
- 5.9 The Debug Library.................|luaref-libDebug|
-
- A BIBLIOGRAPHY........................|luaref-bibliography|
- B COPYRIGHT & LICENSES................|luaref-copyright|
- C LUAREF DOC..........................|luaref-doc|
-
+Type |gO| to see the table of contents.
==============================================================================
1 INTRODUCTION *luaref-intro*
-==============================================================================
Lua is an extension programming language designed to support general
procedural programming with data description facilities. It also offers good
@@ -120,14 +54,13 @@ Lua means "moon" in Portuguese and is pronounced LOO-ah.
==============================================================================
2 THE LANGUAGE *luaref-language*
-==============================================================================
This section describes the lexis, the syntax, and the semantics of Lua. In
other words, this section describes which tokens are valid, how they can be
combined, and what their combinations mean.
The language constructs will be explained using the usual extended BNF
-notation, in which { `a` } means 0 or more `a`'s, and [ `a` ] means an optional `a`.
+notation, in which `{ a }` means 0 or more `a`'s, and `[ a ]` means an optional `a`.
==============================================================================
2.1 Lexical Conventions *luaref-langLexConv*
@@ -203,7 +136,7 @@ For convenience, when the opening long bracket is immediately followed by a
newline, the newline is not included in the string. As an example, in a system
using ASCII (in which `a` is coded as 97, newline is coded as 10, and `1` is
coded as 49), the five literals below denote the same string:
->
+>lua
a = 'alo\n123"'
a = "alo\n123\""
a = '\97lo\10\04923"'
@@ -292,7 +225,7 @@ parameter passing, and function returns always manipulate references to such
values; these operations do not imply any kind of copy.
The library function `type` returns a string describing the type of a given
-value (see |luaref-type|).
+value (see |luaref-type()|).
------------------------------------------------------------------------------
2.2.1 Coercion *luaref-langCoercion*
@@ -303,7 +236,7 @@ string to a number, following the usual conversion rules. Conversely, whenever
a number is used where a string is expected, the number is converted to a
string, in a reasonable format. For complete control of how numbers are
converted to strings, use the `format` function from the string library (see
-|luaref-string.format|).
+|string.format()|).
==============================================================================
2.3 Variables *luaref-langVariables*
@@ -344,13 +277,13 @@ has its own reference to an environment, so that all global variables in this
function will refer to this environment table. When a function is created, it
inherits the environment from the function that created it. To get the
environment table of a Lua function, you call `getfenv` (see
-|luaref-getfenv|). To replace it, you call `setfenv` (see |luaref-setfenv|).
+|lua_getfenv()|). To replace it, you call `setfenv` (see |luaref-setfenv()|).
(You can only manipulate the environment of C functions through the debug
library; see |luaref-libDebug|.)
An access to a global variable `x` is equivalent to `_env.x`, which in turn is
equivalent to
->
+>lua
gettable_event(_env, "x")
<
where `_env` is the environment of the running function. (The `_env` variable is
@@ -433,13 +366,13 @@ before the adjustment (except when the call is enclosed in parentheses; see
The assignment statement first evaluates all its expressions and only then are
the assignments performed. Thus the code
->
+>lua
i = 3
i, a[i] = i+1, 20
<
sets `a[3]` to 20, without affecting `a[4]` because the `i` in `a[i]` is evaluated (to
3) before it is assigned 4. Similarly, the line
->
+>lua
x, y = y, x
<
exchanges the values of `x` and `y`.
@@ -452,7 +385,7 @@ defined or callable in Lua. We use it here only for explanatory purposes.)
An assignment to a global variable `x = val` is equivalent to the
assignment `_env.x = val`, which in turn is equivalent to
->
+>lua
settable_event(_env, "x", val)
<
where `_env` is the environment of the running function. (The `_env` variable is
@@ -517,19 +450,20 @@ The `block` is repeated for `name` starting at the value of the first `exp`, unt
it passes the second `exp` by steps of the third `exp`. More precisely,
a `for` statement like
- `for var =` `e1, e2, e3` `do` `block` `end`
+ `for var = e1, e2, e3 do block end`
-is equivalent to the code:
+is equivalent to the code: >lua
- `do`
- `local` `var, limit, step` `= tonumber(e1), tonumber(e2), tonumber(e3)`
- `if not (` `var` `and` `limit` `and` `step` `) then error() end`
- `while (` `step` `>0 and` `var` `<=` `limit` `)`
- `or (` `step` `<=0 and` `var` `>=` `limit` `) do`
- `block`
- `var` `=` `var` `+` `step`
- `end`
- `end`
+ do
+ local var, limit, step = tonumber(e1), tonumber(e2), tonumber(e3)
+ if not ( var and limit and step ) then error() end
+ while ( step >0 and var <= limit )
+ or ( step <=0 and var >= limit ) do
+ block
+ var = var + step
+ end
+ end
+<
Note the following:
@@ -555,18 +489,18 @@ A `for` statement like
`for` `var1, ..., varn` `in` `explist` `do` `block` `end`
-is equivalent to the code:
-
- `do`
- `local` `f, s, var` `=` `explist`
- `while true do`
- `local` `var1, ..., varn` `=` `f(s, var)`
- `var` `=` `var1`
- `if` `var` `== nil then break end`
- `block`
- `end`
- `end`
+is equivalent to the code: >lua
+ do
+ local f, s, var = explist
+ while true do
+ local var1, ..., varn = f(s, var)
+ var = var1
+ if var == nil then break end
+ block
+ end
+ end
+<
Note the following:
- `explist` is evaluated only once. Its results are an iterator function,
@@ -634,7 +568,7 @@ they are explained in |luaref-langFuncDefs|.
Binary operators comprise arithmetic operators (see |luaref-langArithOp|),
relational operators (see |luaref-langRelOp|), logical operators (see
|luaref-langLogOp|), and the concatenation operator (see |luaref-langConcat|).
-Unary operators comprise the unary minus (see |luaref-labgArithOp|), the unary
+Unary operators comprise the unary minus (see |luaref-langArithOp|), the unary
`not` (see |luaref-langLogOp|), and the unary length operator (see
|luaref-langLength|).
@@ -648,7 +582,7 @@ adjusts the result list to one element, discarding all values except the first
one.
Here are some examples:
->
+>lua
f() -- adjusted to 0 results
g(f(), x) -- f() is adjusted to 1 result
g(x, f()) -- g gets x plus all results from f()
@@ -681,7 +615,7 @@ or strings that can be converted to numbers (see |luaref-langCoercion|), then al
operations have the usual meaning. Exponentiation works for any exponent. For
instance, `x^(-0.5)` computes the inverse of the square root of `x`. Modulo is
defined as
->
+>lua
a % b == a - math.floor(a/b)*b
<
That is, it is the remainder of a division that rounds the quotient towards
@@ -808,11 +742,11 @@ key `exp1` and value `exp2`. A field of the form `name = exp` is equivalent to
`["name"] = exp`. Finally, fields of the form `exp` are equivalent to
`[i] = exp`, where `i` are consecutive numerical integers, starting with 1.
Fields in the other formats do not affect this counting. For example,
->
+>lua
a = { [f(1)] = g; "x", "y"; x = 1, f(x), [30] = 23; 45 }
<
is equivalent to
->
+>lua
do
local t = {}
t[f(1)] = g
@@ -868,7 +802,7 @@ argument list is a single new table. A call of the form `f'` `string` `'`
As an exception to the free-format syntax of Lua, you cannot put a line break
before the `(` in a function call. This restriction avoids some ambiguities
in the language. If you write
->
+>lua
a = f
(g).x(a)
<
@@ -886,7 +820,7 @@ function. Note that a tail call only happens with a particular syntax, where
the `return` has one single function call as argument; this syntax makes the
calling function return exactly the returns of the called function. So, none
of the following examples are tail calls:
->
+>lua
return (f(x)) -- results adjusted to 1
return 2 * f(x)
return x, f(x) -- additional results
@@ -967,7 +901,7 @@ expression is used as the last element of a list of expressions, then no
adjustment is made (unless the call is enclosed in parentheses).
As an example, consider the following definitions:
->
+>lua
function f(a, b) end
function g(a, b, ...) end
function r() return 1,2,3 end
@@ -1008,7 +942,7 @@ is syntactic sugar for
Lua is a lexically scoped language. The scope of variables begins at the first
statement after their declaration and lasts until the end of the innermost
block that includes the declaration. Consider the following example:
->
+>lua
x = 10 -- global variable
do -- new block
local x = x -- new `x`, with value 10
@@ -1033,7 +967,7 @@ function.
Notice that each execution of a local statement defines new local variables.
Consider the following example:
->
+>lua
a = {}
local x = 20
for i=1,10 do
@@ -1050,13 +984,13 @@ them share the same `x`.
Because Lua is an embedded extension language, all Lua actions start from
C code in the host program calling a function from the Lua library (see
-|luaref-lua_pcall|). Whenever an error occurs during Lua compilation or
+|lua_pcall()|). Whenever an error occurs during Lua compilation or
execution, control returns to C, which can take appropriate measures (such as
print an error message).
Lua code can explicitly generate an error by calling the `error` function (see
-|luaref-error|). If you need to catch errors in Lua, you can use
-the `pcall` function (see |luaref-pcall|).
+|luaref-error()|). If you need to catch errors in Lua, you can use
+the `pcall` function (see |luaref-pcall()|).
==============================================================================
2.8 Metatables *luaref-metatable* *luaref-langMetatables*
@@ -1074,10 +1008,10 @@ previous example, the event is "add" and the metamethod is the function that
performs the addition.
You can query the metatable of any value through the `getmetatable` function
-(see |luaref-getmetatable|).
+(see |luaref-getmetatable()|).
You can replace the metatable of tables through the `setmetatable` function (see
-|luaref-setmetatable|). You cannot change the metatable of other types from Lua
+|luaref-setmetatable()|). You cannot change the metatable of other types from Lua
(except using the debug library); you must use the C API for that.
Tables and userdata have individual metatables (although multiple tables and
@@ -1109,7 +1043,7 @@ given object, we use the expression
metatable(obj)[event]
<
This should be read as
->
+>lua
rawget(metatable(obj) or {}, event)
<
That is, the access to a metamethod does not invoke other metamethods, and the
@@ -1123,13 +1057,13 @@ the `+` operation.
The function `getbinhandler` below defines how Lua chooses a handler for a
binary operation. First, Lua tries the first operand. If its type does not
define a handler for the operation, then Lua tries the second operand.
->
+>lua
function getbinhandler (op1, op2, event)
return metatable(op1)[event] or metatable(op2)[event]
end
<
By using this function, the behavior of the `op1 + op2` is
->
+>lua
function add_event (op1, op2)
local o1, o2 = tonumber(op1), tonumber(op2)
if o1 and o2 then -- both operands are numeric?
@@ -1170,7 +1104,7 @@ with the function `pow` (from the C math library) as the primitive operation.
"unm": *__unm()*
------
the unary `-` operation.
->
+>lua
function unm_event (op)
local o = tonumber(op)
if o then -- operand is numeric?
@@ -1190,7 +1124,7 @@ the unary `-` operation.
"concat": *__concat()*
---------
the `..` (concatenation) operation.
->
+>lua
function concat_event (op1, op2)
if (type(op1) == "string" or type(op1) == "number") and
(type(op2) == "string" or type(op2) == "number") then
@@ -1208,7 +1142,7 @@ the `..` (concatenation) operation.
"len": *__len()*
------
the `#` operation.
->
+>lua
function len_event (op)
if type(op) == "string" then
return strlen(op) -- primitive string length
@@ -1233,7 +1167,7 @@ The function `getcomphandler` defines how Lua chooses a metamethod for
comparison operators. A metamethod only is selected when both objects being
compared have the same type and the same metamethod for the selected
operation.
->
+>lua
function getcomphandler (op1, op2, event)
if type(op1) ~= type(op2) then return nil end
local mm1 = metatable(op1)[event]
@@ -1242,7 +1176,7 @@ operation.
end
<
The "eq" event is defined as follows:
->
+>lua
function eq_event (op1, op2)
if type(op1) ~= type(op2) then -- different types?
return false -- different objects
@@ -1264,7 +1198,7 @@ The "eq" event is defined as follows:
"lt": *__lt()*
-----
the `<` operation.
->
+>lua
function lt_event (op1, op2)
if type(op1) == "number" and type(op2) == "number" then
return op1 < op2 -- numeric comparison
@@ -1285,7 +1219,7 @@ the `<` operation.
"le": *__le()*
-----
the `<=` operation.
->
+>lua
function le_event (op1, op2)
if type(op1) == "number" and type(op2) == "number" then
return op1 <= op2 -- numeric comparison
@@ -1313,7 +1247,7 @@ to `not (b < a)`.
"index": *__index()*
--------
The indexing access `table[key]`.
->
+>lua
function gettable_event (table, key)
local h
if type(table) == "table" then
@@ -1335,7 +1269,7 @@ The indexing access `table[key]`.
"newindex": *__newindex()*
-----------
The indexing assignment `table[key] = value`.
->
+>lua
function settable_event (table, key, value)
local h
if type(table) == "table" then
@@ -1357,7 +1291,7 @@ The indexing assignment `table[key] = value`.
"call": *__call()*
-------
called when Lua calls a value.
->
+>lua
function function_event (func, ...)
if type(func) == "function" then
return func(...) -- primitive call
@@ -1385,8 +1319,8 @@ convenience feature for programmers to associate a table to a userdata.
Environments associated with threads are called global environments. They are
used as the default environment for their threads and non-nested functions
-created by the thread (through `loadfile` |luaref-loadfile|, `loadstring`
-|luaref-loadstring| or `load` |luaref-load|) and can be directly accessed by C
+created by the thread (through `loadfile` |luaref-loadfile()|, `loadstring`
+|luaref-loadstring()| or `load` |luaref-load()|) and can be directly accessed by C
code (see |luaref-apiPseudoIndices|).
Environments associated with C functions can be directly accessed by C code
@@ -1399,10 +1333,10 @@ used as the default environment for other Lua functions created by the
function.
You can change the environment of a Lua function or the running thread by
-calling `setfenv` (see |luaref-setenv|). You can get the environment of a Lua
-function or the running thread by calling `getfenv` (see |luaref-getfenv|). To
-manipulate the environment of other objects (userdata, C functions, other
-threads) you must use the C API.
+calling `setfenv`. You can get the environment of a Lua function or the
+running thread by calling `getfenv` (see |lua_getfenv()|). To manipulate the
+environment of other objects (userdata, C functions, other threads) you must
+use the C API.
==============================================================================
2.10 Garbage Collection *luaref-langGC*
@@ -1432,8 +1366,8 @@ collector too slow and may result in the collector never finishing a cycle.
The default, 2, means that the collector runs at "twice" the speed of memory
allocation.
-You can change these numbers by calling `lua_gc` (see |luaref-lua_gc|) in C or
-`collectgarbage` (see |luaref-collectgarbage|) in Lua. Both get percentage
+You can change these numbers by calling `lua_gc` (see |lua_gc()|) in C or
+`collectgarbage` (see |luaref-collectgarbage()|) in Lua. Both get percentage
points as arguments (so an argument of 100 means a real value of 1). With
these functions you can also control the collector directly (e.g., stop and
restart it).
@@ -1452,7 +1386,7 @@ Garbage userdata with a field `__gc` in their metatables are not collected
immediately by the garbage collector. Instead, Lua puts them in a list. After
the collection, Lua does the equivalent of the following function for each
userdata in that list:
->
+>lua
function gc_event (udata)
local h = metatable(udata).__gc
if h then
@@ -1496,12 +1430,12 @@ multithread systems, however, a coroutine only suspends its execution by
explicitly calling a yield function.
You create a coroutine with a call to `coroutine.create` (see
-|luaref-coroutine.create|). Its sole argument is a function that is the main
+|coroutine.create()|). Its sole argument is a function that is the main
function of the coroutine. The `create` function only creates a new coroutine
and returns a handle to it (an object of type `thread`); it does not start the
coroutine execution.
-When you first call `coroutine.resume` (see |luaref-coroutine.resume|),
+When you first call `coroutine.resume` (see |coroutine.resume()|),
passing as its first argument the thread returned by `coroutine.create`, the
coroutine starts its execution, at the first line of its main function. Extra
arguments passed to `coroutine.resume` are passed on to the coroutine main
@@ -1516,7 +1450,7 @@ main function. In case of errors, `coroutine.resume` returns `false` plus an
error message.
A coroutine yields by calling `coroutine.yield` (see
-|luaref-coroutine.yield|). When a coroutine yields, the corresponding
+|coroutine.yield()|). When a coroutine yields, the corresponding
`coroutine.resume` returns immediately, even if the yield happens inside
nested function calls (that is, not in the main function, but in a function
directly or indirectly called by the main function). In the case of a yield,
@@ -1526,7 +1460,7 @@ its execution from the point where it yielded, with the call to
`coroutine.yield` returning any extra arguments passed to `coroutine.resume`.
Like `coroutine.create`, the `coroutine.wrap` function (see
-|luaref-coroutine.wrap|) also creates a coroutine, but instead of returning
+|coroutine.wrap()|) also creates a coroutine, but instead of returning
the coroutine itself, it returns a function that, when called, resumes the
coroutine. Any arguments passed to this function go as extra arguments to
`coroutine.resume`. `coroutine.wrap` returns all the values returned by
@@ -1535,7 +1469,7 @@ coroutine. Any arguments passed to this function go as extra arguments to
propagated to the caller.
As an example, consider the next code:
->
+>lua
function foo1 (a)
print("foo", a)
return coroutine.yield(2*a)
@@ -1569,7 +1503,6 @@ When you run it, it produces the following output:
==============================================================================
3 THE APPLICATION PROGRAM INTERFACE *luaref-API*
-==============================================================================
This section describes the C API for Lua, that is, the set of C functions
available to the host program to communicate with Lua. All API functions and
@@ -1595,7 +1528,7 @@ Whenever Lua calls C, the called function gets a new stack, which is
independent of previous stacks and of stacks of C functions that are still
active. This stack initially contains any arguments to the C function and it
is where the C function pushes its results to be returned to the caller (see
-|luaref-lua_CFunction|).
+|lua_CFunction()|).
*luaref-stackindex*
For convenience, most query operations in the API do not follow a strict stack
@@ -1615,7 +1548,7 @@ if `1 <= abs(index) <= top`).
When you interact with Lua API, you are responsible for ensuring consistency.
In particular, you are responsible for controlling stack overflow. You can
use the function `lua_checkstack` to grow the stack size (see
-|luaref-lua_checkstack|).
+|lua_checkstack()|).
Whenever Lua calls C, it ensures that at least `LUA_MINSTACK` stack positions
are available. `LUA_MINSTACK` is defined as 20, so that usually you do not
@@ -1626,7 +1559,7 @@ Most query functions accept as indices any value inside the available stack
space, that is, indices up to the maximum stack size you have set through
`lua_checkstack`. Such indices are called acceptable indices. More formally,
we define an acceptable index as follows:
->
+>lua
(index < 0 && abs(index) <= top) || (index > 0 && index <= stackspace)
<
Note that 0 is never an acceptable index.
@@ -1647,7 +1580,7 @@ pseudo-index `LUA_ENVIRONINDEX`.
To access and change the value of global variables, you can use regular table
operations over an environment table. For instance, to access the value of a
global variable, do
->
+>c
lua_getfield(L, LUA_GLOBALSINDEX, varname);
<
@@ -1656,14 +1589,14 @@ global variable, do
When a C function is created, it is possible to associate some values with it,
thus creating a C closure; these values are called upvalues and are accessible
-to the function whenever it is called (see |luaref-lua_pushcclosure|).
+to the function whenever it is called (see |lua_pushcclosure()|).
Whenever a C function is called, its upvalues are located at specific
pseudo-indices. These pseudo-indices are produced by the macro
-`lua_upvalueindex` (see |luaref-lua_upvalueindex|). The first value associated
-with a function is at position `lua_upvalueindex(1)`, and so on. Any access to
-`lua_upvalueindex(` `n` `)`, where `n` is greater than the number of upvalues of
-the current function, produces an acceptable (but invalid) index.
+`lua_upvalueindex`. The first value associated with a function is at position
+`lua_upvalueindex(1)`, and so on. Any access to `lua_upvalueindex(` `n` `)`,
+where `n` is greater than the number of upvalues of the current function,
+produces an acceptable (but invalid) index.
==============================================================================
3.5 Registry *luaref-registry* *luaref-apiRegistry*
@@ -1694,11 +1627,11 @@ Almost any function in the API may raise an error, for instance due to a
memory allocation error. The following functions run in protected mode (that
is, they create a protected environment to run), so they never raise an error:
`lua_newstate`, `lua_close`, `lua_load`, `lua_pcall`, and `lua_cpcall` (see
-|luaref-lua_newstate|, |luaref-lua_close|, |luaref-lua_load|,
-|luaref-lua_pcall|, and |luaref-lua_cpcall|).
+|lua_newstate()|, |lua_close()|, |lua_load()|,
+|lua_pcall()|, and |lua_cpcall()|).
Inside a C function you can raise an error by calling `lua_error` (see
-|luaref-lua_error|).
+|lua_error()|).
==============================================================================
3.7 Functions and Types *luaref-apiFunctions*
@@ -1706,7 +1639,7 @@ Inside a C function you can raise an error by calling `lua_error` (see
Here we list all functions and types from the C API in alphabetical order.
lua_Alloc *lua_Alloc()*
->
+>c
typedef void * (*lua_Alloc) (void *ud,
void *ptr,
size_t osize,
@@ -1715,7 +1648,7 @@ lua_Alloc *lua_Alloc()*
The type of the memory-allocation function used by Lua states. The
allocator function must provide a functionality similar to `realloc`,
but not exactly the same. Its arguments are `ud`, an opaque pointer
- passed to `lua_newstate` (see |luaref-lua_newstate|); `ptr`, a pointer
+ passed to `lua_newstate` (see |lua_newstate()|); `ptr`, a pointer
to the block being allocated/reallocated/freed; `osize`, the original
size of the block; `nsize`, the new size of the block. `ptr` is `NULL`
if and only if `osize` is zero. When `nsize` is zero, the allocator
@@ -1729,8 +1662,8 @@ lua_Alloc *lua_Alloc()*
Here is a simple implementation for the allocator function. It is used
in the auxiliary library by `luaL_newstate` (see
- |luaref-luaL_newstate|).
->
+ |luaL_newstate()|).
+>c
static void *l_alloc (void *ud, void *ptr, size_t osize,
size_t nsize) {
(void)ud; (void)osize; /* not used */
@@ -1747,7 +1680,7 @@ lua_Alloc *lua_Alloc()*
behaviors.
lua_atpanic *lua_atpanic()*
->
+>c
lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf);
<
Sets a new panic function and returns the old one.
@@ -1761,7 +1694,7 @@ lua_atpanic *lua_atpanic()*
stack.
lua_call *lua_call()*
->
+>c
void lua_call (lua_State *L, int nargs, int nresults);
<
Calls a function.
@@ -1785,11 +1718,11 @@ lua_call *lua_call()*
The following example shows how the host program may do the equivalent
to this Lua code:
->
+>lua
a = f("how", t.x, 14)
<
Here it is in C:
->
+>c
lua_getfield(L, LUA_GLOBALSINDEX, "f"); // function to be called
lua_pushstring(L, "how"); // 1st argument
lua_getfield(L, LUA_GLOBALSINDEX, "t"); // table to be indexed
@@ -1804,7 +1737,7 @@ lua_call *lua_call()*
practice.
lua_CFunction *luaref-cfunction* *lua_CFunction()*
->
+>c
typedef int (*lua_CFunction) (lua_State *L);
<
Type for C functions.
@@ -1813,7 +1746,7 @@ lua_CFunction *luaref-cfunction* *lua_CFunction()*
following protocol, which defines the way parameters and results are
passed: a C function receives its arguments from Lua in its stack in
direct order (the first argument is pushed first). So, when the
- function starts, `lua_gettop(L)` (see |luaref-lua_gettop|) returns the
+ function starts, `lua_gettop(L)` (see |lua_gettop()|) returns the
number of arguments received by the function. The first argument (if
any) is at index 1 and its last argument is at index `lua_gettop(L)`.
To return values to Lua, a C function just pushes them onto the stack,
@@ -1825,7 +1758,7 @@ lua_CFunction *luaref-cfunction* *lua_CFunction()*
*luaref-cfunctionexample*
As an example, the following function receives a variable number of
numerical arguments and returns their average and sum:
->
+>c
static int foo (lua_State *L) {
int n = lua_gettop(L); /* number of arguments */
lua_Number sum = 0;
@@ -1844,7 +1777,7 @@ lua_CFunction *luaref-cfunction* *lua_CFunction()*
<
lua_checkstack *lua_checkstack()*
->
+>c
int lua_checkstack (lua_State *L, int extra);
<
Ensures that there are at least `extra` free stack slots in the stack.
@@ -1853,7 +1786,7 @@ lua_checkstack *lua_checkstack()*
the new size, it is left unchanged.
lua_close *lua_close()*
->
+>c
void lua_close (lua_State *L);
<
Destroys all objects in the given Lua state (calling the corresponding
@@ -1865,7 +1798,7 @@ lua_close *lua_close()*
are not needed, to avoid growing too large.
lua_concat *lua_concat()*
->
+>c
void lua_concat (lua_State *L, int n);
<
Concatenates the `n` values at the top of the stack, pops them, and
@@ -1875,35 +1808,35 @@ lua_concat *lua_concat()*
usual semantics of Lua (see |luaref-langConcat|).
lua_cpcall *lua_cpcall()*
->
+>c
int lua_cpcall (lua_State *L, lua_CFunction func, void *ud);
<
Calls the C function `func` in protected mode. `func` starts with only
one element in its stack, a light userdata containing `ud`. In case of
errors, `lua_cpcall` returns the same error codes as `lua_pcall` (see
- |luaref-lua_pcall|), plus the error object on the top of the stack;
+ |lua_pcall()|), plus the error object on the top of the stack;
otherwise, it returns zero, and does not change the stack. All values
returned by `func` are discarded.
lua_createtable *lua_createtable()*
->
+>c
void lua_createtable (lua_State *L, int narr, int nrec);
<
Creates a new empty table and pushes it onto the stack. The new table
has space pre-allocated for `narr` array elements and `nrec` non-array
elements. This pre-allocation is useful when you know exactly how many
elements the table will have. Otherwise you can use the function
- `lua_newtable` (see |luaref-lua_newtable|).
+ `lua_newtable` (see |lua_newtable()|).
lua_dump *lua_dump()*
->
+>c
int lua_dump (lua_State *L, lua_Writer writer, void *data);
<
Dumps a function as a binary chunk. Receives a Lua function on the top
of the stack and produces a binary chunk that, if loaded again,
results in a function equivalent to the one dumped. As it produces
parts of the chunk, `lua_dump` calls function `writer` (see
- |luaref-lua_Writer|) with the given `data` to write them.
+ |lua_Writer()|) with the given `data` to write them.
The value returned is the error code returned by the last call to the
writer; 0 means no errors.
@@ -1911,7 +1844,7 @@ lua_dump *lua_dump()*
This function does not pop the Lua function from the stack.
lua_equal *lua_equal()*
->
+>c
int lua_equal (lua_State *L, int index1, int index2);
<
Returns 1 if the two values in acceptable indices `index1` and
@@ -1920,15 +1853,15 @@ lua_equal *lua_equal()*
if any of the indices is non valid.
lua_error *lua_error()*
->
+>c
int lua_error (lua_State *L);
<
Generates a Lua error. The error message (which can actually be a Lua
value of any type) must be on the stack top. This function does a long
- jump, and therefore never returns (see |luaref-luaL_error|).
+ jump, and therefore never returns (see |luaL_error()|).
lua_gc *lua_gc()*
->
+>c
int lua_gc (lua_State *L, int what, int data);
<
Controls the garbage collector.
@@ -1936,46 +1869,46 @@ lua_gc *lua_gc()*
This function performs several tasks, according to the value of the
parameter `what`:
- `LUA_GCSTOP` stops the garbage collector.
- `LUA_GCRESTART` restarts the garbage collector.
- `LUA_GCCOLLECT` performs a full garbage-collection cycle.
- `LUA_GCCOUNT` returns the current amount of memory (in Kbytes) in
+ - `LUA_GCSTOP` stops the garbage collector.
+ - `LUA_GCRESTART` restarts the garbage collector.
+ - `LUA_GCCOLLECT` performs a full garbage-collection cycle.
+ - `LUA_GCCOUNT` returns the current amount of memory (in Kbytes) in
use by Lua.
- `LUA_GCCOUNTB` returns the remainder of dividing the current
+ - `LUA_GCCOUNTB` returns the remainder of dividing the current
amount of bytes of memory in use by Lua by 1024.
- `LUA_GCSTEP` performs an incremental step of garbage collection.
+ - `LUA_GCSTEP` performs an incremental step of garbage collection.
The step "size" is controlled by `data` (larger
values mean more steps) in a non-specified way. If
you want to control the step size you must
experimentally tune the value of `data`. The
function returns 1 if the step finished a
garbage-collection cycle.
- `LUA_GCSETPAUSE` sets `data` /100 as the new value for the
+ - `LUA_GCSETPAUSE` sets `data` /100 as the new value for the
`pause` of the collector (see |luaref-langGC|).
The function returns the previous value of the
pause.
- `LUA_GCSETSTEPMUL` sets `data` /100 as the new value for the
+ - `LUA_GCSETSTEPMUL`sets `data` /100 as the new value for the
`step` `multiplier` of the collector (see
|luaref-langGC|). The function returns the
previous value of the step multiplier.
lua_getallocf *lua_getallocf()*
->
+>c
lua_Alloc lua_getallocf (lua_State *L, void **ud);
<
Returns the memory-allocation function of a given state. If `ud` is
not `NULL`, Lua stores in `*ud` the opaque pointer passed to
- `lua_newstate` (see |luaref-lua_newstate|).
+ `lua_newstate` (see |lua_newstate()|).
lua_getfenv *lua_getfenv()*
->
+>c
void lua_getfenv (lua_State *L, int index);
<
Pushes onto the stack the environment table of the value at the given
index.
lua_getfield *lua_getfield()*
->
+>c
void lua_getfield (lua_State *L, int index, const char *k);
<
Pushes onto the stack the value `t[k]`, where `t` is the value at the
@@ -1983,17 +1916,17 @@ lua_getfield *lua_getfield()*
metamethod for the "index" event (see |luaref-langMetatables|).
lua_getglobal *lua_getglobal()*
->
+>c
void lua_getglobal (lua_State *L, const char *name);
<
Pushes onto the stack the value of the global `name`. It is defined as
a macro:
->
+>c
#define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, s)
<
lua_getmetatable *lua_getmetatable()*
->
+>c
int lua_getmetatable (lua_State *L, int index);
<
Pushes onto the stack the metatable of the value at the given
@@ -2002,7 +1935,7 @@ lua_getmetatable *lua_getmetatable()*
stack.
lua_gettable *lua_gettable()*
->
+>c
void lua_gettable (lua_State *L, int index);
<
Pushes onto the stack the value `t[k]`, where `t` is the value at the
@@ -2014,7 +1947,7 @@ lua_gettable *lua_gettable()*
the "index" event (see |luaref-langMetatables|).
lua_gettop *lua_gettop()*
->
+>c
int lua_gettop (lua_State *L);
<
Returns the index of the top element in the stack. Because indices
@@ -2023,7 +1956,7 @@ lua_gettop *lua_gettop()*
0 means an empty stack).
lua_insert *lua_insert()*
->
+>c
void lua_insert (lua_State *L, int index);
<
Moves the top element into the given valid index, shifting up the
@@ -2031,7 +1964,7 @@ lua_insert *lua_insert()*
pseudo-index, because a pseudo-index is not an actual stack position.
lua_Integer *lua_Integer()*
->
+>c
typedef ptrdiff_t lua_Integer;
<
The type used by the Lua API to represent integral values.
@@ -2040,77 +1973,77 @@ lua_Integer *lua_Integer()*
type the machine handles "comfortably".
lua_isboolean *lua_isboolean()*
->
+>c
int lua_isboolean (lua_State *L, int index);
<
Returns 1 if the value at the given acceptable index has type boolean,
and 0 otherwise.
lua_iscfunction *lua_iscfunction()*
->
+>c
int lua_iscfunction (lua_State *L, int index);
<
Returns 1 if the value at the given acceptable index is a C function,
and 0 otherwise.
lua_isfunction *lua_isfunction()*
->
+>c
int lua_isfunction (lua_State *L, int index);
<
Returns 1 if the value at the given acceptable index is a function
(either C or Lua), and 0 otherwise.
lua_islightuserdata *lua_islightuserdata()*
->
+>c
int lua_islightuserdata (lua_State *L, int index);
<
Returns 1 if the value at the given acceptable index is a light
userdata, and 0 otherwise.
lua_isnil *lua_isnil()*
->
+>c
int lua_isnil (lua_State *L, int index);
<
Returns 1 if the value at the given acceptable index is `nil`, and 0
otherwise.
lua_isnumber *lua_isnumber()*
->
+>c
int lua_isnumber (lua_State *L, int index);
<
Returns 1 if the value at the given acceptable index is a number or a
string convertible to a number, and 0 otherwise.
lua_isstring *lua_isstring()*
->
+>c
int lua_isstring (lua_State *L, int index);
<
Returns 1 if the value at the given acceptable index is a string or a
number (which is always convertible to a string), and 0 otherwise.
lua_istable *lua_istable()*
->
+>c
int lua_istable (lua_State *L, int index);
<
Returns 1 if the value at the given acceptable index is a table, and
0 otherwise.
lua_isthread *lua_isthread()*
->
+>c
int lua_isthread (lua_State *L, int index);
<
Returns 1 if the value at the given acceptable index is a thread, and
0 otherwise.
lua_isuserdata *lua_isuserdata()*
->
+>c
int lua_isuserdata (lua_State *L, int index);
<
Returns 1 if the value at the given acceptable index is a userdata
(either full or light), and 0 otherwise.
lua_lessthan *lua_lessthan()*
->
+>c
int lua_lessthan (lua_State *L, int index1, int index2);
<
Returns 1 if the value at acceptable index `index1` is smaller than
@@ -2119,7 +2052,7 @@ lua_lessthan *lua_lessthan()*
Also returns 0 if any of the indices is non valid.
lua_load *lua_load()*
->
+>c
int lua_load (lua_State *L,
lua_Reader reader,
void *data,
@@ -2136,17 +2069,17 @@ lua_load *lua_load()*
This function only loads a chunk; it does not run it.
`lua_load` automatically detects whether the chunk is text or binary,
- and loads it accordingly (see program `luac`, |luaref-luac|).
+ and loads it accordingly (see program `luac`).
The `lua_load` function uses a user-supplied `reader` function to read
- the chunk (see |luaref-lua_Reader|). The `data` argument is an opaque
+ the chunk (see |lua_Reader()|). The `data` argument is an opaque
value passed to the reader function.
The `chunkname` argument gives a name to the chunk, which is used for
error messages and in debug information (see |luaref-apiDebug|).
lua_newstate *lua_newstate()*
->
+>c
lua_State *lua_newstate (lua_Alloc f, void *ud);
<
Creates a new, independent state. Returns `NULL` if cannot create the
@@ -2156,19 +2089,19 @@ lua_newstate *lua_newstate()*
simply passes to the allocator in every call.
lua_newtable *lua_newtable()*
->
+>c
void lua_newtable (lua_State *L);
<
Creates a new empty table and pushes it onto the stack. It is
equivalent to `lua_createtable(L, 0, 0)` (see
- |luaref-lua_createtable|).
+ |lua_createtable()|).
lua_newthread *lua_newthread()*
->
+>c
lua_State *lua_newthread (lua_State *L);
<
Creates a new thread, pushes it on the stack, and returns a pointer to
- a `lua_State` (see |luaref-lua_State|) that represents this new
+ a `lua_State` (see |lua_State()|) that represents this new
thread. The new state returned by this function shares with the
original state all global objects (such as tables), but has an
independent execution stack.
@@ -2177,7 +2110,7 @@ lua_newthread *lua_newthread()*
are subject to garbage collection, like any Lua object.
lua_newuserdata *lua_newuserdata()*
->
+>c
void *lua_newuserdata (lua_State *L, size_t size);
<
This function allocates a new block of memory with the given size,
@@ -2195,7 +2128,7 @@ lua_newuserdata *lua_newuserdata()*
is collected again then Lua frees its corresponding memory.
lua_next *lua_next()*
->
+>c
int lua_next (lua_State *L, int index);
<
Pops a key from the stack, and pushes a key-value pair from the table
@@ -2205,7 +2138,7 @@ lua_next *lua_next()*
*luaref-tabletraversal*
A typical traversal looks like this:
->
+>c
/* table is in the stack at index 't' */
lua_pushnil(L); /* first key */
while (lua_next(L, t) != 0) {
@@ -2218,12 +2151,12 @@ lua_next *lua_next()*
}
<
While traversing a table, do not call `lua_tolstring` (see
- |luaref-lua_tolstring|) directly on a key, unless you know that the
+ |lua_tolstring()|) directly on a key, unless you know that the
key is actually a string. Recall that `lua_tolstring` `changes` the
value at the given index; this confuses the next call to `lua_next`.
lua_Number *lua_Number()*
->
+>c
typedef double lua_Number;
<
The type of numbers in Lua. By default, it is double, but that can be
@@ -2233,7 +2166,7 @@ lua_Number *lua_Number()*
another type for numbers (e.g., float or long).
lua_objlen *lua_objlen()*
->
+>c
size_t lua_objlen (lua_State *L, int index);
<
Returns the "length" of the value at the given acceptable index: for
@@ -2242,13 +2175,13 @@ lua_objlen *lua_objlen()*
block of memory allocated for the userdata; for other values, it is 0.
lua_pcall *lua_pcall()*
->
+>c
lua_pcall (lua_State *L, int nargs, int nresults, int errfunc);
<
Calls a function in protected mode.
Both `nargs` and `nresults` have the same meaning as in `lua_call`
- (see |luaref-lua_call|). If there are no errors during the call,
+ (see |lua_call()|). If there are no errors during the call,
`lua_pcall` behaves exactly like `lua_call`. However, if there is any
error, `lua_pcall` catches it, pushes a single value on the stack (the
error message), and returns an error code. Like `lua_call`,
@@ -2277,19 +2210,19 @@ lua_pcall *lua_pcall()*
- `LUA_ERRERR` error while running the error handler function.
lua_pop *lua_pop()*
->
+>c
void lua_pop (lua_State *L, int n);
<
Pops `n` elements from the stack.
lua_pushboolean *lua_pushboolean()*
->
+>c
void lua_pushboolean (lua_State *L, int b);
<
Pushes a boolean value with value `b` onto the stack.
lua_pushcclosure *lua_pushcclosure()*
->
+>c
void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n);
<
Pushes a new C closure onto the stack.
@@ -2305,7 +2238,7 @@ lua_pushcclosure *lua_pushcclosure()*
pops these values from the stack.
lua_pushcfunction *lua_pushcfunction()*
->
+>c
void lua_pushcfunction (lua_State *L, lua_CFunction f);
<
Pushes a C function onto the stack. This function receives a pointer
@@ -2314,15 +2247,15 @@ lua_pushcfunction *lua_pushcfunction()*
Any function to be registered in Lua must follow the correct protocol
to receive its parameters and return its results (see
- |luaref-lua_CFunction|).
+ |lua_CFunction()|).
`lua_pushcfunction` is defined as a macro:
->
+>c
#define lua_pushcfunction(L,f) lua_pushcclosure(L,f,0)
<
lua_pushfstring *lua_pushfstring()*
->
+>c
const char *lua_pushfstring (lua_State *L, const char *fmt, ...);
<
Pushes onto the stack a formatted string and returns a pointer to this
@@ -2341,13 +2274,13 @@ lua_pushfstring *lua_pushfstring()*
character).
lua_pushinteger *lua_pushinteger()*
->
+>c
void lua_pushinteger (lua_State *L, lua_Integer n);
<
Pushes a number with value `n` onto the stack.
lua_pushlightuserdata *lua_pushlightuserdata()*
->
+>c
void lua_pushlightuserdata (lua_State *L, void *p);
<
Pushes a light userdata onto the stack.
@@ -2359,7 +2292,7 @@ lua_pushlightuserdata *lua_pushlightuserdata()*
same C address.
lua_pushlstring *lua_pushlstring()*
->
+>c
void lua_pushlstring (lua_State *L, const char *s, size_t len);
<
Pushes the string pointed to by `s` with size `len` onto the stack.
@@ -2368,19 +2301,19 @@ lua_pushlstring *lua_pushlstring()*
returns. The string can contain embedded zeros.
lua_pushnil *lua_pushnil()*
->
+>c
void lua_pushnil (lua_State *L);
<
Pushes a nil value onto the stack.
lua_pushnumber *lua_pushnumber()*
->
+>c
void lua_pushnumber (lua_State *L, lua_Number n);
<
Pushes a number with value `n` onto the stack.
lua_pushstring *lua_pushstring()*
->
+>c
void lua_pushstring (lua_State *L, const char *s);
<
Pushes the zero-terminated string pointed to by `s` onto the stack.
@@ -2390,30 +2323,30 @@ lua_pushstring *lua_pushstring()*
end at the first zero.
lua_pushthread *lua_pushthread()*
->
+>c
int lua_pushthread (lua_State *L);
<
Pushes the thread represented by `L` onto the stack. Returns 1 if this
thread is the main thread of its state.
lua_pushvalue *lua_pushvalue()*
->
+>c
void lua_pushvalue (lua_State *L, int index);
<
Pushes a copy of the element at the given valid index onto the stack.
lua_pushvfstring *lua_pushvfstring()*
->
+>c
const char *lua_pushvfstring (lua_State *L,
const char *fmt,
va_list argp);
<
- Equivalent to `lua_pushfstring` (see |luaref-pushfstring|), except
+ Equivalent to `lua_pushfstring` (see |lua_pushfstring()|), except
that it receives a `va_list` instead of a variable number of
arguments.
lua_rawequal *lua_rawequal()*
->
+>c
int lua_rawequal (lua_State *L, int index1, int index2);
<
Returns 1 if the two values in acceptable indices `index1` and
@@ -2422,14 +2355,14 @@ lua_rawequal *lua_rawequal()*
valid.
lua_rawget *lua_rawget()*
->
+>c
void lua_rawget (lua_State *L, int index);
<
- Similar to `lua_gettable` (see |luaref-lua_gettable|), but does a raw
+ Similar to `lua_gettable` (see |lua_gettable()|), but does a raw
access (i.e., without metamethods).
lua_rawgeti *lua_rawgeti()*
->
+>c
void lua_rawgeti (lua_State *L, int index, int n);
<
Pushes onto the stack the value `t[n]`, where `t` is the value at the
@@ -2437,14 +2370,14 @@ lua_rawgeti *lua_rawgeti()*
invoke metamethods.
lua_rawset *lua_rawset()*
->
+>c
void lua_rawset (lua_State *L, int index);
<
- Similar to `lua_settable` (see |luaref-lua_settable|), but does a raw
+ Similar to `lua_settable` (see |lua_settable()|), but does a raw
assignment (i.e., without metamethods).
lua_rawseti *lua_rawseti()*
->
+>c
void lua_rawseti (lua_State *L, int index, int n);
<
Does the equivalent of `t[n] = v`, where `t` is the value at the given
@@ -2454,12 +2387,12 @@ lua_rawseti *lua_rawseti()*
that is, it does not invoke metamethods.
lua_Reader *lua_Reader()*
->
+>c
typedef const char * (*lua_Reader) (lua_State *L,
void *data,
size_t *size);
<
- The reader function used by `lua_load` (see |luaref-lua_load|). Every
+ The reader function used by `lua_load` (see |lua_load()|). Every
time it needs another piece of the chunk, `lua_load` calls the reader,
passing along its `data` parameter. The reader must return a pointer
to a block of memory with a new piece of the chunk and set `size` to
@@ -2469,20 +2402,20 @@ lua_Reader *lua_Reader()*
zero.
lua_register *lua_register()*
->
+>c
void lua_register (lua_State *L,
const char *name,
lua_CFunction f);
<
Sets the C function `f` as the new value of global `name`. It is
defined as a macro:
->
+>c
#define lua_register(L,n,f) \
(lua_pushcfunction(L, f), lua_setglobal(L, n))
<
lua_remove *lua_remove()*
->
+>c
void lua_remove (lua_State *L, int index);
<
Removes the element at the given valid index, shifting down the
@@ -2490,7 +2423,7 @@ lua_remove *lua_remove()*
pseudo-index, because a pseudo-index is not an actual stack position.
lua_replace *lua_replace()*
->
+>c
void lua_replace (lua_State *L, int index);
<
Moves the top element into the given position (and pops it), without
@@ -2498,35 +2431,35 @@ lua_replace *lua_replace()*
position).
lua_resume *lua_resume()*
->
+>c
int lua_resume (lua_State *L, int narg);
<
Starts and resumes a coroutine in a given thread.
To start a coroutine, you first create a new thread (see
- |luaref-lua_newthread|); then you push onto its stack the main
+ |lua_newthread()|); then you push onto its stack the main
function plus any arguments; then you call `lua_resume` (see
- |luaref-lua_resume|) with `narg` being the number of arguments. This
+ |lua_resume()|) with `narg` being the number of arguments. This
call returns when the coroutine suspends or finishes its execution.
When it returns, the stack contains all values passed to `lua_yield`
- (see |luaref-lua_yield|), or all values returned by the body function.
+ (see |lua_yield()|), or all values returned by the body function.
`lua_resume` returns `LUA_YIELD` if the coroutine yields, 0 if the
coroutine finishes its execution without errors, or an error code in
- case of errors (see |luaref-lua_pcall|). In case of errors, the stack
+ case of errors (see |lua_pcall()|). In case of errors, the stack
is not unwound, so you can use the debug API over it. The error
message is on the top of the stack. To restart a coroutine, you put on
its stack only the values to be passed as results from `lua_yield`,
and then call `lua_resume`.
lua_setallocf *lua_setallocf()*
->
+>c
void lua_setallocf (lua_State *L, lua_Alloc f, void *ud);
<
Changes the allocator function of a given state to `f` with user data
`ud`.
lua_setfenv *lua_setfenv()*
->
+>c
int lua_setfenv (lua_State *L, int index);
<
Pops a table from the stack and sets it as the new environment for the
@@ -2535,7 +2468,7 @@ lua_setfenv *lua_setfenv()*
Otherwise it returns 1.
lua_setfield *lua_setfield()*
->
+>c
void lua_setfield (lua_State *L, int index, const char *k);
<
Does the equivalent to `t[k] = v`, where `t` is the value at the given
@@ -2546,24 +2479,24 @@ lua_setfield *lua_setfield()*
|luaref-langMetatables|).
lua_setglobal *lua_setglobal()*
->
+>c
void lua_setglobal (lua_State *L, const char *name);
<
Pops a value from the stack and sets it as the new value of global
`name`. It is defined as a macro:
->
+>c
#define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, s)
<
lua_setmetatable *lua_setmetatable()*
->
+>c
int lua_setmetatable (lua_State *L, int index);
<
Pops a table from the stack and sets it as the new metatable for the
value at the given acceptable index.
lua_settable *lua_settable()*
->
+>c
void lua_settable (lua_State *L, int index);
<
Does the equivalent to `t[k] = v`, where `t` is the value at the given
@@ -2575,7 +2508,7 @@ lua_settable *lua_settable()*
(see |luaref-langMetatables|).
lua_settop *lua_settop()*
->
+>c
void lua_settop (lua_State *L, int index);
<
Accepts any acceptable index, or 0, and sets the stack top to this
@@ -2584,7 +2517,7 @@ lua_settop *lua_settop()*
elements are removed.
lua_State *lua_State()*
->
+>c
typedef struct lua_State lua_State;
<
Opaque structure that keeps the whole state of a Lua interpreter. The
@@ -2593,10 +2526,10 @@ lua_State *lua_State()*
A pointer to this state must be passed as the first argument to every
function in the library, except to `lua_newstate` (see
- |luaref-lua_newstate|), which creates a Lua state from scratch.
+ |lua_newstate()|), which creates a Lua state from scratch.
lua_status *lua_status()*
->
+>c
int lua_status (lua_State *L);
<
Returns the status of the thread `L`.
@@ -2606,7 +2539,7 @@ lua_status *lua_status()*
suspended.
lua_toboolean *lua_toboolean()*
->
+>c
int lua_toboolean (lua_State *L, int index);
<
Converts the Lua value at the given acceptable index to a C boolean
@@ -2614,21 +2547,21 @@ lua_toboolean *lua_toboolean()*
any Lua value different from `false` and `nil`; otherwise it returns
0. It also returns 0 when called with a non-valid index. (If you want
to accept only actual boolean values, use `lua_isboolean`
- |luaref-lua_isboolean| to test the value's type.)
+ |lua_isboolean()| to test the value's type.)
lua_tocfunction *lua_tocfunction()*
->
+>c
lua_CFunction lua_tocfunction (lua_State *L, int index);
<
Converts a value at the given acceptable index to a C function. That
value must be a C function; otherwise it returns `NULL`.
lua_tointeger *lua_tointeger()*
->
+>c
lua_Integer lua_tointeger (lua_State *L, int idx);
<
Converts the Lua value at the given acceptable index to the signed
- integral type `lua_Integer` (see |luaref-lua_Integer|). The Lua value
+ integral type `lua_Integer` (see |lua_Integer()|). The Lua value
must be a number or a string convertible to a number (see
|luaref-langCoercion|); otherwise, `lua_tointeger` returns 0.
@@ -2636,7 +2569,7 @@ lua_tointeger *lua_tointeger()*
way.
lua_tolstring *lua_tolstring()*
->
+>c
const char *lua_tolstring (lua_State *L, int index, size_t *len);
<
Converts the Lua value at the given acceptable index to a C string. If
@@ -2644,7 +2577,7 @@ lua_tolstring *lua_tolstring()*
Lua value must be a string or a number; otherwise, the function
returns `NULL`. If the value is a number, then `lua_tolstring` also
`changes the actual value in the stack to a` `string`. (This change
- confuses `lua_next` |luaref-lua_next| when `lua_tolstring` is applied
+ confuses `lua_next` |lua_next()| when `lua_tolstring` is applied
to keys during a table traversal.)
`lua_tolstring` returns a fully aligned pointer to a string inside the
@@ -2655,16 +2588,16 @@ lua_tolstring *lua_tolstring()*
value is removed from the stack.
lua_tonumber *lua_tonumber()*
->
+>c
lua_Number lua_tonumber (lua_State *L, int index);
<
Converts the Lua value at the given acceptable index to the C type
- `lua_Number` (see |luaref-lua_Number|). The Lua value must be a number
+ `lua_Number` (see |lua_Number()|). The Lua value must be a number
or a string convertible to a number (see |luaref-langCoercion|);
otherwise, `lua_tonumber` returns 0.
lua_topointer *lua_topointer()*
->
+>c
const void *lua_topointer (lua_State *L, int index);
<
Converts the value at the given acceptable index to a generic C
@@ -2676,22 +2609,22 @@ lua_topointer *lua_topointer()*
Typically this function is used only for debug information.
lua_tostring *lua_tostring()*
->
+>c
const char *lua_tostring (lua_State *L, int index);
<
- Equivalent to `lua_tolstring` (see |luaref-lua_tolstring|) with `len`
+ Equivalent to `lua_tolstring` (see |lua_tolstring()|) with `len`
equal to `NULL`.
lua_tothread *lua_tothread()*
->
+>c
lua_State *lua_tothread (lua_State *L, int index);
<
Converts the value at the given acceptable index to a Lua thread
- (represented as `lua_State*` |luaref-lua_State|). This value must be a
+ (represented as `lua_State*` |lua_State()|). This value must be a
thread; otherwise, the function returns `NULL`.
lua_touserdata *lua_touserdata()*
->
+>c
void *lua_touserdata (lua_State *L, int index);
<
If the value at the given acceptable index is a full userdata, returns
@@ -2699,7 +2632,7 @@ lua_touserdata *lua_touserdata()*
pointer. Otherwise, it returns `NULL`.
lua_type *lua_type()*
->
+>c
int lua_type (lua_State *L, int index);
<
Returns the type of the value in the given acceptable index, or
@@ -2710,20 +2643,20 @@ lua_type *lua_type()*
`LUA_TUSERDATA`, `LUA_TTHREAD`, and `LUA_TLIGHTUSERDATA`.
lua_typename *lua_typename()*
->
+>c
const char *lua_typename (lua_State *L, int tp);
<
Returns the name of the type encoded by the value `tp`, which must be
one the values returned by `lua_type`.
lua_Writer *lua_Writer()*
->
+>c
typedef int (*lua_Writer) (lua_State *L,
const void* p,
size_t sz,
void* ud);
<
- The writer function used by `lua_dump` (see |luaref-lua_dump|). Every
+ The writer function used by `lua_dump` (see |lua_dump()|). Every
time it produces another piece of chunk, `lua_dump` calls the writer,
passing along the buffer to be written (`p`), its size (`sz`), and the
`data` parameter supplied to `lua_dump`.
@@ -2732,7 +2665,7 @@ lua_Writer *lua_Writer()*
means an error and stops `lua_dump` from calling the writer again.
lua_xmove *lua_xmove()*
->
+>c
void lua_xmove (lua_State *from, lua_State *to, int n);
<
Exchange values between different threads of the `same` global state.
@@ -2741,19 +2674,19 @@ lua_xmove *lua_xmove()*
onto the stack `to`.
lua_yield *lua_yield()*
->
+>c
int lua_yield (lua_State *L, int nresults);
<
Yields a coroutine.
This function should only be called as the return expression of a C
function, as follows:
->
+>c
return lua_yield (L, nresults);
<
When a C function calls `lua_yield` in that way, the running coroutine
suspends its execution, and the call to `lua_resume` (see
- |luaref-lua_resume|) that started this coroutine returns. The
+ |lua_resume()|) that started this coroutine returns. The
parameter `nresults` is the number of values from the stack that are
passed as results to `lua_resume`.
@@ -2782,50 +2715,52 @@ need "inside information" from the interpreter.
lua_Debug *lua_Debug()*
- `typedef struct lua_Debug {`
- `int event;`
- `const char *name; /* (n) */`
- `const char *namewhat; /* (n) */`
- `const char *what; /* (S) */`
- `const char *source; /* (S) */`
- `int currentline; /* (l) */`
- `int nups; /* (u) number of upvalues */`
- `int linedefined; /* (S) */`
- `int lastlinedefined; /* (S) */`
- `char short_src[LUA_IDSIZE]; /* (S) */`
- `/* private part */`
- `other fields`
- `} lua_Debug;`
+>c
+ typedef struct lua_Debug {
+ int event;
+ const char *name; /* (n) */
+ const char *namewhat; /* (n) */
+ const char *what; /* (S) */
+ const char *source; /* (S) */
+ int currentline; /* (l) */
+ int nups; /* (u) number of upvalues */
+ int linedefined; /* (S) */
+ int lastlinedefined; /* (S) */
+ char short_src[LUA_IDSIZE]; /* (S) */
+ /* private part */
+ other fields
+ } lua_Debug;
+<
A structure used to carry different pieces of information about an active
-function. `lua_getstack` (see |luaref-lua_getstack|) fills only the private part
+function. `lua_getstack` (see |lua_getstack()|) fills only the private part
of this structure, for later use. To fill the other fields of `lua_Debug` with
-useful information, call `lua_getinfo` (see |luaref-lua_getinfo|).
+useful information, call `lua_getinfo` (see |lua_getinfo()|).
The fields of `lua_Debug` have the following meaning:
- `source` If the function was defined in a string, then `source` is
+- `source` If the function was defined in a string, then `source` is
that string. If the function was defined in a file, then
`source` starts with a `@` followed by the file name.
- `short_src` a "printable" version of `source`, to be used in error messages.
- `linedefined` the line number where the definition of the function starts.
- `lastlinedefined` the line number where the definition of the function ends.
- `what` the string `"Lua"` if the function is a Lua function,
+- `short_src` a "printable" version of `source`, to be used in error messages.
+- `linedefined` the line number where the definition of the function starts.
+- `lastlinedefined` the line number where the definition of the function ends.
+- `what` the string `"Lua"` if the function is a Lua function,
`"C"` if it is a C function, `"main"` if it is the main
part of a chunk, and `"tail"` if it was a function that
did a tail call. In the latter case, Lua has no other
information about the function.
- `currentline` the current line where the given function is executing.
+- `currentline` the current line where the given function is executing.
When no line information is available, `currentline` is
set to -1.
- `name` a reasonable name for the given function. Because
+- `name` a reasonable name for the given function. Because
functions in Lua are first-class values, they do not have
a fixed name: some functions may be the value of multiple
global variables, while others may be stored only in a
table field. The `lua_getinfo` function checks how the
function was called to find a suitable name. If it cannot
find a name, then `name` is set to `NULL`.
- `namewhat` explains the `name` field. The value of `namewhat` can be
+- `namewhat` explains the `name` field. The value of `namewhat` can be
`"global"`, `"local"`, `"method"`, `"field"`,
`"upvalue"`, or `""` (the empty string), according to how
the function was called. (Lua uses the empty string when
@@ -2833,40 +2768,40 @@ The fields of `lua_Debug` have the following meaning:
upvalues of the function.
lua_gethook *lua_gethook()*
->
+>c
lua_Hook lua_gethook (lua_State *L);
<
Returns the current hook function.
lua_gethookcount *lua_gethookcount()*
->
+>c
int lua_gethookcount (lua_State *L);
<
Returns the current hook count.
lua_gethookmask *lua_gethookmask()*
->
+>c
int lua_gethookmask (lua_State *L);
<
Returns the current hook mask.
lua_getinfo *lua_getinfo()*
->
+>c
int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);
<
Returns information about a specific function or function invocation.
To get information about a function invocation, the parameter `ar`
must be a valid activation record that was filled by a previous call
- to `lua_getstack` (see |luaref-lua_getstack|) or given as argument to
- a hook (see |luaref-lua_Hook|).
+ to `lua_getstack` (see |lua_getstack()|) or given as argument to
+ a hook (see |lua_Hook()|).
To get information about a function you push it onto the stack and
start the `what` string with the character `>`. (In that case,
`lua_getinfo` pops the function in the top of the stack.) For
instance, to know in which line a function `f` was defined, you can
write the following code:
->
+>c
lua_Debug ar;
lua_getfield(L, LUA_GLOBALSINDEX, "f"); /* get global 'f' */
lua_getinfo(L, ">S", &ar);
@@ -2891,13 +2826,13 @@ lua_getinfo *lua_getinfo()*
`what`).
lua_getlocal *lua_getlocal()*
->
+>c
const char *lua_getlocal (lua_State *L, lua_Debug *ar, int n);
<
Gets information about a local variable of a given activation record.
The parameter `ar` must be a valid activation record that was filled
- by a previous call to `lua_getstack` (see |luaref-lua_getstack|) or
- given as argument to a hook (see |luaref-lua_Hook|). The index `n`
+ by a previous call to `lua_getstack` (see |lua_getstack()|) or
+ given as argument to a hook (see |lua_Hook()|). The index `n`
selects which local variable to inspect (1 is the first parameter or
active local variable, and so on, until the last active local
variable). `lua_getlocal` pushes the variable's value onto the stack
@@ -2911,12 +2846,12 @@ lua_getlocal *lua_getlocal()*
number of active local variables.
lua_getstack *lua_getstack()*
->
+>c
int lua_getstack (lua_State *L, int level, lua_Debug *ar);
<
Gets information about the interpreter runtime stack.
- This function fills parts of a `lua_Debug` (see |luaref-lua_Debug|)
+ This function fills parts of a `lua_Debug` (see |lua_Debug()|)
structure with an identification of the `activation record` of the
function executing at a given level. Level 0 is the current running
function, whereas level `n+1` is the function that has called level
@@ -2924,7 +2859,7 @@ lua_getstack *lua_getstack()*
with a level greater than the stack depth, it returns 0.
lua_getupvalue *lua_getupvalue()*
->
+>c
const char *lua_getupvalue (lua_State *L, int funcindex, int n);
<
Gets information about a closure's upvalue. (For Lua functions,
@@ -2940,7 +2875,7 @@ lua_getupvalue *lua_getupvalue()*
string `""` as a name for all upvalues.
lua_Hook *lua_Hook()*
->
+>c
typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
<
Type for debugging hook functions.
@@ -2951,7 +2886,7 @@ lua_Hook *lua_Hook()*
`LUA_HOOKTAILRET`, `LUA_HOOKLINE`, and `LUA_HOOKCOUNT`. Moreover, for
line events, the field `currentline` is also set. To get the value of
any other field in `ar`, the hook must call `lua_getinfo` (see
- |luaref-lua_getinfo|). For return events, `event` may be
+ |lua_getinfo()|). For return events, `event` may be
`LUA_HOOKRET`, the normal value, or `LUA_HOOKTAILRET`. In the latter
case, Lua is simulating a return from a function that did a tail call;
in this case, it is useless to call `lua_getinfo`.
@@ -2962,7 +2897,7 @@ lua_Hook *lua_Hook()*
lua_sethook *lua_sethook()*
->
+>c
int lua_sethook (lua_State *L, lua_Hook f, int mask, int count);
<
Sets the debugging hook function.
@@ -2991,12 +2926,12 @@ lua_sethook *lua_sethook()*
A hook is disabled by setting `mask` to zero.
lua_setlocal *lua_setlocal()*
->
+>c
const char *lua_setlocal (lua_State *L, lua_Debug *ar, int n);
<
Sets the value of a local variable of a given activation record.
Parameters `ar` and `n` are as in `lua_getlocal` (see
- |luaref-lua_getlocal|). `lua_setlocal` assigns the value at the top of
+ |lua_getlocal()|). `lua_setlocal` assigns the value at the top of
the stack to the variable and returns its name. It also pops the value
from the stack.
@@ -3004,13 +2939,13 @@ lua_setlocal *lua_setlocal()*
number of active local variables.
lua_setupvalue *lua_setupvalue()*
->
+>c
const char *lua_setupvalue (lua_State *L, int funcindex, int n);
<
Sets the value of a closure's upvalue. It assigns the value at the top
of the stack to the upvalue and returns its name. It also pops the
value from the stack. Parameters `funcindex` and `n` are as in the
- `lua_getupvalue` (see |luaref-lua_getupvalue|).
+ `lua_getupvalue` (see |lua_getupvalue()|).
Returns `NULL` (and pops nothing) when the index is greater than the
number of upvalues.
@@ -3018,7 +2953,7 @@ lua_setupvalue *lua_setupvalue()*
*luaref-debugexample*
As an example, the following function lists the names of all local
variables and upvalues for a function at a given level of the stack:
->
+>c
int listvars (lua_State *L, int level) {
lua_Debug ar;
int i;
@@ -3042,7 +2977,6 @@ lua_setupvalue *lua_setupvalue()*
==============================================================================
4 THE AUXILIARY LIBRARY *luaref-aux*
-==============================================================================
The auxiliary library provides several convenient functions to interface C
with Lua. While the basic API provides the primitive functions for all
@@ -3068,46 +3002,46 @@ Here we list all functions and types from the auxiliary library in
alphabetical order.
luaL_addchar *luaL_addchar()*
->
+>c
void luaL_addchar (luaL_Buffer *B, char c);
<
- Adds the character `c` to the buffer `B` (see |luaref-luaL_Buffer|).
+ Adds the character `c` to the buffer `B` (see |luaL_Buffer()|).
luaL_addlstring *luaL_addlstring()*
->
+>c
void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l);
<
Adds the string pointed to by `s` with length `l` to the buffer `B`
- (see |luaref-luaL_Buffer|). The string may contain embedded zeros.
+ (see |luaL_Buffer()|). The string may contain embedded zeros.
luaL_addsize *luaL_addsize()*
->
+>c
void luaL_addsize (luaL_Buffer *B, size_t n);
<
- Adds to the buffer `B` (see |luaref-luaL_Buffer|) a string of length
+ Adds to the buffer `B` (see |luaL_Buffer()|) a string of length
`n` previously copied to the buffer area (see
- |luaref-luaL_prepbuffer|).
+ |luaL_prepbuffer()|).
luaL_addstring *luaL_addstring()*
->
+>c
void luaL_addstring (luaL_Buffer *B, const char *s);
<
Adds the zero-terminated string pointed to by `s` to the buffer `B`
- (see |luaref-luaL_Buffer|). The string may not contain embedded zeros.
+ (see |luaL_Buffer()|). The string may not contain embedded zeros.
luaL_addvalue *luaL_addvalue()*
->
+>c
void luaL_addvalue (luaL_Buffer *B);
<
Adds the value at the top of the stack to the buffer `B` (see
- |luaref-luaL_Buffer|). Pops the value.
+ |luaL_Buffer()|). Pops the value.
This is the only function on string buffers that can (and must) be
called with an extra element on the stack, which is the value to be
added to the buffer.
luaL_argcheck *luaL_argcheck()*
->
+>c
void luaL_argcheck (lua_State *L,
int cond,
int narg,
@@ -3120,7 +3054,7 @@ luaL_argcheck *luaL_argcheck()*
<
luaL_argerror *luaL_argerror()*
->
+>c
int luaL_argerror (lua_State *L, int narg, const char *extramsg);
<
Raises an error with the following message, where `func` is retrieved
@@ -3132,7 +3066,7 @@ luaL_argerror *luaL_argerror()*
functions as `return luaL_argerror(` `args` `)`.
luaL_Buffer *luaL_Buffer()*
->
+>c
typedef struct luaL_Buffer luaL_Buffer;
<
Type for a `string buffer`.
@@ -3142,11 +3076,11 @@ luaL_Buffer *luaL_Buffer()*
- First you declare a variable `b` of type `luaL_Buffer`.
- Then you initialize it with a call `luaL_buffinit(L, &b)` (see
- |luaref-luaL_buffinit|).
+ |luaL_buffinit()|).
- Then you add string pieces to the buffer calling any of the
`luaL_add*` functions.
- You finish by calling `luaL_pushresult(&b)` (see
- |luaref-luaL_pushresult|). This call leaves the final string on the
+ |luaL_pushresult()|). This call leaves the final string on the
top of the stack.
During its normal operation, a string buffer uses a variable number of
@@ -3156,19 +3090,19 @@ luaL_Buffer *luaL_Buffer()*
that is, when you call a buffer operation, the stack is at the same
level it was immediately after the previous buffer operation. (The
only exception to this rule is `luaL_addvalue`
- |luaref-luaL_addvalue|.) After calling `luaL_pushresult` the stack is
+ |luaL_addvalue()|.) After calling `luaL_pushresult` the stack is
back to its level when the buffer was initialized, plus the final
string on its top.
luaL_buffinit *luaL_buffinit()*
->
+>c
void luaL_buffinit (lua_State *L, luaL_Buffer *B);
<
Initializes a buffer `B`. This function does not allocate any space;
- the buffer must be declared as a variable (see |luaref-luaL_Buffer|).
+ the buffer must be declared as a variable (see |luaL_Buffer()|).
luaL_callmeta *luaL_callmeta()*
->
+>c
int luaL_callmeta (lua_State *L, int obj, const char *e);
<
Calls a metamethod.
@@ -3181,49 +3115,49 @@ luaL_callmeta *luaL_callmeta()*
0 (without pushing any value on the stack).
luaL_checkany *luaL_checkany()*
->
+>c
void luaL_checkany (lua_State *L, int narg);
<
Checks whether the function has an argument of any type (including
`nil`) at position `narg`.
luaL_checkint *luaL_checkint()*
->
+>c
int luaL_checkint (lua_State *L, int narg);
<
Checks whether the function argument `narg` is a number and returns
this number cast to an `int`.
luaL_checkinteger *luaL_checkinteger()*
->
+>c
lua_Integer luaL_checkinteger (lua_State *L, int narg);
<
Checks whether the function argument `narg` is a number and returns
- this number cast to a `lua_Integer` (see |luaref-lua_Integer|).
+ this number cast to a `lua_Integer` (see |lua_Integer()|).
luaL_checklong *luaL_checklong()*
->
+>c
long luaL_checklong (lua_State *L, int narg);
<
Checks whether the function argument `narg` is a number and returns
this number cast to a `long`.
luaL_checklstring *luaL_checklstring()*
->
+>c
const char *luaL_checklstring (lua_State *L, int narg, size_t *l);
<
Checks whether the function argument `narg` is a string and returns
this string; if `l` is not `NULL` fills `*l` with the string's length.
luaL_checknumber *luaL_checknumber()*
->
+>c
lua_Number luaL_checknumber (lua_State *L, int narg);
<
Checks whether the function argument `narg` is a number and returns
- this number (see |luaref-lua_Number|).
+ this number (see |lua_Number()|).
luaL_checkoption *luaL_checkoption()*
->
+>c
int luaL_checkoption (lua_State *L,
int narg,
const char *def,
@@ -3243,7 +3177,7 @@ luaL_checkoption *luaL_checkoption()*
select options.)
luaL_checkstack *luaL_checkstack()*
->
+>c
void luaL_checkstack (lua_State *L, int sz, const char *msg);
<
Grows the stack size to `top + sz` elements, raising an error if the
@@ -3251,53 +3185,53 @@ luaL_checkstack *luaL_checkstack()*
the error message.
luaL_checkstring *luaL_checkstring()*
->
+>c
const char *luaL_checkstring (lua_State *L, int narg);
<
Checks whether the function argument `narg` is a string and returns
this string.
luaL_checktype *luaL_checktype()*
->
+>c
void luaL_checktype (lua_State *L, int narg, int t);
<
Checks whether the function argument `narg` has type `t` (see
- |luaref-lua_type|).
+ |lua_type()|).
luaL_checkudata *luaL_checkudata()*
->
+>c
void *luaL_checkudata (lua_State *L, int narg, const char *tname);
<
Checks whether the function argument `narg` is a userdata of the type
- `tname` (see |luaref-luaL_newmetatable|).
+ `tname` (see |luaL_newmetatable()|).
luaL_dofile *luaL_dofile()*
->
+>c
int luaL_dofile (lua_State *L, const char *filename);
<
Loads and runs the given file. It is defined as the following macro:
->
+>c
(luaL_loadfile(L, filename) || lua_pcall(L, 0, LUA_MULTRET, 0))
<
It returns 0 if there are no errors or 1 in case of errors.
luaL_dostring *luaL_dostring()*
->
+>c
int luaL_dostring (lua_State *L, const char *str);
<
Loads and runs the given string. It is defined as the following macro:
->
+>c
(luaL_loadstring(L, str) || lua_pcall(L, 0, LUA_MULTRET, 0))
<
It returns 0 if there are no errors or 1 in case of errors.
luaL_error *luaL_error()*
->
+>c
int luaL_error (lua_State *L, const char *fmt, ...);
<
Raises an error. The error message format is given by `fmt` plus any
extra arguments, following the same rules of `lua_pushfstring` (see
- |luaref-lua_pushfstring|). It also adds at the beginning of the
+ |lua_pushfstring()|). It also adds at the beginning of the
message the file name and the line number where the error occurred, if
this information is available.
@@ -3305,7 +3239,7 @@ luaL_error *luaL_error()*
functions as `return luaL_error(` `args` `)`.
luaL_getmetafield *luaL_getmetafield()*
->
+>c
int luaL_getmetafield (lua_State *L, int obj, const char *e);
<
Pushes onto the stack the field `e` from the metatable of the object
@@ -3313,14 +3247,14 @@ luaL_getmetafield *luaL_getmetafield()*
metatable does not have this field, returns 0 and pushes nothing.
luaL_getmetatable *luaL_getmetatable()*
->
+>c
void luaL_getmetatable (lua_State *L, const char *tname);
<
Pushes onto the stack the metatable associated with name `tname` in
- the registry (see |luaref-luaL_newmetatable|).
+ the registry (see |luaL_newmetatable()|).
luaL_gsub *luaL_gsub()*
->
+>c
const char *luaL_gsub (lua_State *L,
const char *s,
const char *p,
@@ -3331,25 +3265,25 @@ luaL_gsub *luaL_gsub()*
returns it.
luaL_loadbuffer *luaL_loadbuffer()*
->
+>c
int luaL_loadbuffer (lua_State *L,
const char *buff,
size_t sz,
const char *name);
<
Loads a buffer as a Lua chunk. This function uses `lua_load` (see
- |luaref-lua_load|) to load the chunk in the buffer pointed to by
+ |lua_load()|) to load the chunk in the buffer pointed to by
`buff` with size `sz`.
This function returns the same results as `lua_load`. `name` is the
chunk name, used for debug information and error messages.
luaL_loadfile *luaL_loadfile()*
->
+>c
int luaL_loadfile (lua_State *L, const char *filename);
<
Loads a file as a Lua chunk. This function uses `lua_load` (see
- |luaref-lua_load|) to load the chunk in the file named `filename`. If
+ |lua_load()|) to load the chunk in the file named `filename`. If
`filename` is `NULL`, then it loads from the standard input. The first
line in the file is ignored if it starts with a `#`.
@@ -3359,11 +3293,11 @@ luaL_loadfile *luaL_loadfile()*
As `lua_load`, this function only loads the chunk; it does not run it.
luaL_loadstring *luaL_loadstring()*
->
+>c
int luaL_loadstring (lua_State *L, const char *s);
<
Loads a string as a Lua chunk. This function uses `lua_load` (see
- |luaref-lua_load|) to load the chunk in the zero-terminated string
+ |lua_load()|) to load the chunk in the zero-terminated string
`s`.
This function returns the same results as `lua_load`.
@@ -3372,7 +3306,7 @@ luaL_loadstring *luaL_loadstring()*
run it.
luaL_newmetatable *luaL_newmetatable()*
->
+>c
int luaL_newmetatable (lua_State *L, const char *tname);
<
If the registry already has the key `tname`, returns 0. Otherwise,
@@ -3383,27 +3317,27 @@ luaL_newmetatable *luaL_newmetatable()*
`tname` in the registry.
luaL_newstate *luaL_newstate()*
->
+>c
lua_State *luaL_newstate (void);
<
Creates a new Lua state. It calls `lua_newstate` (see
- |luaref-lua_newstate|) with an allocator based on the standard C
+ |lua_newstate()|) with an allocator based on the standard C
`realloc` function and then sets a panic function (see
- |luaref-lua_atpanic|) that prints an error message to the standard
+ |lua_atpanic()|) that prints an error message to the standard
error output in case of fatal errors.
Returns the new state, or `NULL` if there is a memory allocation
error.
luaL_openlibs *luaL_openlibs()*
->
+>c
void luaL_openlibs (lua_State *L);
<
Opens all standard Lua libraries into the given state. See also
|luaref-openlibs| for details on how to open individual libraries.
luaL_optint *luaL_optint()*
->
+>c
int luaL_optint (lua_State *L, int narg, int d);
<
If the function argument `narg` is a number, returns this number cast
@@ -3411,17 +3345,17 @@ luaL_optint *luaL_optint()*
Otherwise, raises an error.
luaL_optinteger *luaL_optinteger()*
->
+>c
lua_Integer luaL_optinteger (lua_State *L,
int narg,
lua_Integer d);
<
If the function argument `narg` is a number, returns this number cast
- to a `lua_Integer` (see |luaref-lua_Integer|). If this argument is
+ to a `lua_Integer` (see |lua_Integer()|). If this argument is
absent or is `nil`, returns `d`. Otherwise, raises an error.
luaL_optlong *luaL_optlong()*
->
+>c
long luaL_optlong (lua_State *L, int narg, long d);
<
If the function argument `narg` is a number, returns this number cast
@@ -3429,7 +3363,7 @@ luaL_optlong *luaL_optlong()*
Otherwise, raises an error.
luaL_optlstring *luaL_optlstring()*
->
+>c
const char *luaL_optlstring (lua_State *L,
int narg,
const char *d,
@@ -3442,7 +3376,7 @@ luaL_optlstring *luaL_optlstring()*
If `l` is not `NULL`, fills the position `*l` with the results' length.
luaL_optnumber *luaL_optnumber()*
->
+>c
lua_Number luaL_optnumber (lua_State *L, int narg, lua_Number d);
<
If the function argument `narg` is a number, returns this number. If
@@ -3450,7 +3384,7 @@ luaL_optnumber *luaL_optnumber()*
error.
luaL_optstring *luaL_optstring()*
->
+>c
const char *luaL_optstring (lua_State *L,
int narg,
const char *d);
@@ -3460,24 +3394,24 @@ luaL_optstring *luaL_optstring()*
error.
luaL_prepbuffer *luaL_prepbuffer()*
->
+>c
char *luaL_prepbuffer (luaL_Buffer *B);
<
Returns an address to a space of size `LUAL_BUFFERSIZE` where you can
- copy a string to be added to buffer `B` (see |luaref-luaL_Buffer|).
+ copy a string to be added to buffer `B` (see |luaL_Buffer()|).
After copying the string into this space you must call `luaL_addsize`
- (see |luaref-luaL_addsize|) with the size of the string to actually
+ (see |luaL_addsize()|) with the size of the string to actually
add it to the buffer.
luaL_pushresult *luaL_pushresult()*
->
+>c
void luaL_pushresult (luaL_Buffer *B);
<
Finishes the use of buffer `B` leaving the final string on the top of
the stack.
luaL_ref *luaL_ref()*
->
+>c
int luaL_ref (lua_State *L, int t);
<
Creates and returns a `reference`, in the table at index `t`, for the
@@ -3486,8 +3420,8 @@ luaL_ref *luaL_ref()*
A reference is a unique integer key. As long as you do not manually
add integer keys into table `t`, `luaL_ref` ensures the uniqueness of
the key it returns. You can retrieve an object referred by reference
- `r` by calling `lua_rawgeti(L, t, r)` (see |luaref-lua_rawgeti|).
- Function `luaL_unref` (see |luaref-luaL_unref|) frees a reference and
+ `r` by calling `lua_rawgeti(L, t, r)` (see |lua_rawgeti()|).
+ Function `luaL_unref` (see |luaL_unref()|) frees a reference and
its associated object.
If the object at the top of the stack is `nil`, `luaL_ref` returns the
@@ -3495,19 +3429,19 @@ luaL_ref *luaL_ref()*
different from any reference returned by `luaL_ref`.
luaL_Reg *luaL_Reg()*
->
+>c
typedef struct luaL_Reg {
const char *name;
lua_CFunction func;
} luaL_Reg;
<
Type for arrays of functions to be registered by `luaL_register` (see
- |luaref-luaL_register|). `name` is the function name and `func` is a
+ |luaL_register()|). `name` is the function name and `func` is a
pointer to the function. Any array of `luaL_Reg` must end with a
sentinel entry in which both `name` and `func` are `NULL`.
luaL_register *luaL_register()*
->
+>c
void luaL_register (lua_State *L,
const char *libname,
const luaL_Reg *l);
@@ -3515,7 +3449,7 @@ luaL_register *luaL_register()*
Opens a library.
When called with `libname` equal to `NULL`, it simply registers all
- functions in the list `l` (see |luaref-luaL_Reg|) into the table on
+ functions in the list `l` (see |luaL_Reg()|) into the table on
the top of the stack.
When called with a non-null `libname`, `luaL_register` creates a new
@@ -3528,13 +3462,13 @@ luaL_register *luaL_register()*
In any case the function leaves the table on the top of the stack.
luaL_typename *luaL_typename()*
->
+>c
const char *luaL_typename (lua_State *L, int idx);
<
Returns the name of the type of the value at index `idx`.
luaL_typerror *luaL_typerror()*
->
+>c
int luaL_typerror (lua_State *L, int narg, const char *tname);
<
Generates an error with a message like the following:
@@ -3543,22 +3477,22 @@ luaL_typerror *luaL_typerror()*
`expected, got` `rt` `)`
where `location` is produced by `luaL_where` (see
- |luaref-luaL_where|), `func` is the name of the current function, and
+ |luaL_where()|), `func` is the name of the current function, and
`rt` is the type name of the actual argument.
luaL_unref *luaL_unref()*
->
+>c
void luaL_unref (lua_State *L, int t, int ref);
<
Releases reference `ref` from the table at index `t` (see
- |luaref-luaL_ref|). The entry is removed from the table, so that the
+ |luaL_ref()|). The entry is removed from the table, so that the
referred object can be collected. The reference `ref` is also freed to
be used again.
If `ref` is `LUA_NOREF` or `LUA_REFNIL`, `luaL_unref` does nothing.
luaL_where *luaL_where()*
->
+>c
void luaL_where (lua_State *L, int lvl);
<
Pushes onto the stack a string identifying the current position of the
@@ -3574,7 +3508,6 @@ luaL_where *luaL_where()*
==============================================================================
5 STANDARD LIBRARIES *luaref-Lib*
-==============================================================================
The standard libraries provide useful functions that are implemented directly
through the C API. Some of these functions provide essential services to the
@@ -3601,14 +3534,14 @@ functions as fields of a global table or as methods of its objects.
*luaref-openlibs*
To have access to these libraries, the C host program should call the
`luaL_openlibs` function, which opens all standard libraries (see
-|luaref-luaL_openlibs|). Alternatively, the host program can open the libraries
+|luaL_openlibs()|). Alternatively, the host program can open the libraries
individually by calling `luaopen_base` (for the basic library),
`luaopen_package` (for the package library), `luaopen_string` (for the string
library), `luaopen_table` (for the table library), `luaopen_math` (for the
mathematical library), `luaopen_io` (for the I/O and the Operating System
libraries), and `luaopen_debug` (for the debug library). These functions are
declared in `lualib.h` and should not be called directly: you must call them
-like any other Lua C function, e.g., by using `lua_call` (see |luaref-lua_call|).
+like any other Lua C function, e.g., by using `lua_call` (see |lua_call()|).
==============================================================================
5.1 Basic Functions *luaref-libBasic*
@@ -3700,15 +3633,15 @@ load({func} [, {chunkname}]) *luaref-load()*
information.
loadfile([{filename}]) *luaref-loadfile()*
- Similar to `load` (see |luaref-load|), but gets the chunk from file
+ Similar to `load` (see |luaref-load()|), but gets the chunk from file
{filename} or from the standard input, if no file name is given.
loadstring({string} [, {chunkname}]) *luaref-loadstring()*
- Similar to `load` (see |luaref-load|), but gets the chunk from the
+ Similar to `load` (see |luaref-load()|), but gets the chunk from the
given {string}.
To load and run a given string, use the idiom
->
+>lua
assert(loadstring(s))()
<
@@ -3724,14 +3657,14 @@ next({table} [, {index}]) *luaref-next()*
The order in which the indices are enumerated is not specified, `even
for` `numeric indices`. (To traverse a table in numeric order, use a
- numerical `for` or the `ipairs` |luaref-ipairs| function.)
+ numerical `for` or the `ipairs` |luaref-ipairs()| function.)
The behavior of `next` is `undefined` if, during the traversal, you
assign any value to a non-existent field in the table. You may however
modify existing fields. In particular, you may clear existing fields.
pairs({t}) *luaref-pairs()*
- Returns three values: the `next` |luaref-next| function, the table
+ Returns three values: the `next` |luaref-next()| function, the table
{t}, and `nil`, so that the construction
`for k,v in pairs(t) do` `body` `end`
@@ -3749,10 +3682,10 @@ pcall({f}, {arg1}, {...}) *luaref-pcall()*
print({...}) *luaref-print()*
Receives any number of arguments, and prints their values to `stdout`,
- using the `tostring` |luaref-tostring| function to convert them to
+ using the `tostring` |luaref-tostring()| function to convert them to
strings. `print` is not intended for formatted output, but only as a
quick way to show a value, typically for debugging. For formatted
- output, use `string.format` (see |luaref-string.format|).
+ output, use `string.format` (see |string.format()|).
rawequal({v1}, {v2}) *luaref-rawequal()*
Checks whether {v1} is equal to {v2}, without invoking any metamethod.
@@ -3807,7 +3740,7 @@ tonumber({e} [, {base}]) *luaref-tonumber()*
tostring({e}) *luaref-tostring()*
Receives an argument of any type and converts it to a string in a
reasonable format. For complete control of how numbers are converted,
- use `string.format` (see |luaref-string.format|).
+ use `string.format` (see |string.format()|).
*__tostring*
If the metatable of {e} has a `"__tostring"` field, `tostring` calls
@@ -3823,7 +3756,7 @@ type({v}) *luaref-type()*
unpack({list} [, {i} [, {j}]]) *luaref-unpack()*
Returns the elements from the given table. This function is equivalent
to
->
+>lua
return list[i], list[i+1], ..., list[j]
<
except that the above code can be written only for a fixed number of
@@ -3836,7 +3769,7 @@ _VERSION *luaref-_VERSION()*
`"Lua 5.1"` .
xpcall({f}, {err}) *luaref-xpcall()*
- This function is similar to `pcall` (see |luaref-pcall|), except that
+ This function is similar to `pcall` (see |luaref-pcall()|), except that
you can set a new error handler.
`xpcall` calls function {f} in protected mode, using {err} as the
@@ -3901,7 +3834,7 @@ coroutine.yield({...}) *coroutine.yield()*
The package library provides basic facilities for loading and building modules
in Lua. It exports two of its functions directly in the global environment:
-`require` and `module` (see |luaref-require| and |luaref-module|). Everything else is
+`require` and `module` (see |luaref-require()| and |luaref-module()|). Everything else is
exported in a table `package`.
module({name} [, {...}]) *luaref-module()*
@@ -3914,7 +3847,7 @@ module({name} [, {...}]) *luaref-module()*
`t._PACKAGE` with the package name (the full module name minus last
component; see below). Finally, `module` sets `t` as the new
environment of the current function and the new value of
- `package.loaded[name]`, so that `require` (see |luaref-require|)
+ `package.loaded[name]`, so that `require` (see |luaref-require()|)
returns `t`.
If {name} is a compound name (that is, one with components separated
@@ -3968,7 +3901,7 @@ require({modname}) *luaref-require()*
If there is any error loading or running the module, or if it cannot
find any loader for the module, then `require` signals an error.
-package.cpath *package.cpath()*
+package.cpath *package.cpath*
The path used by `require` to search for a C loader.
Lua initializes the C path `package.cpath` in the same way it
@@ -3985,7 +3918,7 @@ package.loadlib({libname}, {funcname}) *package.loadlib()*
Dynamically links the host program with the C library {libname}.
Inside this library, looks for a function {funcname} and returns this
function as a C function. (So, {funcname} must follow the protocol
- (see |luaref-lua_CFunction|)).
+ (see |lua_CFunction()|)).
This is a low-level function. It completely bypasses the package and
module system. Unlike `require`, it does not perform any path
@@ -3998,7 +3931,7 @@ package.loadlib({libname}, {funcname}) *package.loadlib()*
available on some platforms (Windows, Linux, Mac OS X, Solaris, BSD,
plus other Unix systems that support the `dlfcn` standard).
-package.path *package.path()*
+package.path *package.path*
The path used by `require` to search for a Lua loader.
At start-up, Lua initializes this variable with the value of the
@@ -4019,7 +3952,7 @@ package.path *package.path()*
order.
package.preload *package.preload()*
- A table to store loaders for specific modules (see |luaref-require|).
+ A table to store loaders for specific modules (see |luaref-require()|).
package.seeall({module}) *package.seeall()*
Sets a metatable for {module} with its `__index` field referring to
@@ -4059,7 +3992,7 @@ string.char({...}) *string.char()*
string.dump({function}) *string.dump()*
Returns a string containing a binary representation of the given
- function, so that a later |luaref-loadstring| on this string returns a
+ function, so that a later |luaref-loadstring()| on this string returns a
copy of the function. {function} must be a Lua function without
upvalues.
@@ -4088,11 +4021,11 @@ string.format({formatstring}, {...}) *string.format()*
interpreter: the string is written between double quotes, and all
double quotes, newlines, embedded zeros, and backslashes in the string
are correctly escaped when written. For instance, the call
->
+>lua
string.format('%q', 'a string with "quotes" and \n new line')
<
will produce the string:
->
+>lua
"a string with \"quotes\" and \
new line"
<
@@ -4110,7 +4043,7 @@ string.gmatch({s}, {pattern}) *string.gmatch()*
in each call.
As an example, the following loop
->
+>lua
s = "hello world from Lua"
for w in string.gmatch(s, "%a+") do
print(w)
@@ -4119,7 +4052,7 @@ string.gmatch({s}, {pattern}) *string.gmatch()*
will iterate over all the words from string {s}, printing one per
line. The next example collects all pairs `key=value` from the given
string into a table:
->
+>lua
t = {}
s = "from=world, to=Lua"
for k, v in string.gmatch(s, "(%w+)=(%w+)") do
@@ -4127,7 +4060,7 @@ string.gmatch({s}, {pattern}) *string.gmatch()*
end
<
-string.gsub({s}, {pattern}, {repl} [, {n}]) *string.gsu{b}()*
+string.gsub({s}, {pattern}, {repl} [, {n}]) *string.gsub()*
Returns a copy of {s} in which all occurrences of the {pattern} have
been replaced by a replacement string specified by {repl}, which may
be a string, a table, or a function. `gsub` also returns, as its
@@ -4158,7 +4091,7 @@ string.gsub({s}, {pattern}, {repl} [, {n}]) *string.gsu{b}()*
occurrence of `pattern` is replaced.
Here are some examples:
->
+>lua
x = string.gsub("hello world", "(%w+)", "%1 %1")
--> x="hello hello world world"
@@ -4341,7 +4274,7 @@ table.foreach({table}, {f}) *table.foreach()*
returns a non-`nil` value, then the loop is broken, and this value is
returned as the final value of `table.foreach`.
- See |luaref-next| for extra information about table traversals.
+ See |luaref-next()| for extra information about table traversals.
table.foreachi({table}, {f}) *table.foreachi()*
Executes the given {f} over the numerical indices of {table}. For each
@@ -4658,7 +4591,7 @@ file:setvbuf({mode} [, {size}]) *luaref-file:setvbuf()*
immediately.
`"full"` full buffering; output operation is performed only when
the buffer is full (or when you explicitly `flush` the file
- (see |luaref-io.flush|).
+ (see |io.flush()|).
`"line"` line buffering; output is buffered until a newline is
output or there is any input from some special files (such as
a terminal device).
@@ -4669,7 +4602,7 @@ file:setvbuf({mode} [, {size}]) *luaref-file:setvbuf()*
file:write({...}) *luaref-file:write()*
Writes the value of each of its arguments to `file`. The arguments
must be strings or numbers. To write other values, use `tostring`
- |luaref-tostring| or `string.format` |luaref-string.format| before
+ |luaref-tostring()| or `string.format` |string.format()| before
`write`.
==============================================================================
@@ -4686,7 +4619,7 @@ os.date([{format} [, {time}]]) *os.date()*
according to the given string {format}.
If the {time} argument is present, this is the time to be formatted
- (see the `os.time` function |luaref-os.time| for a description of this
+ (see the `os.time` function |os.time()| for a description of this
value). Otherwise, `date` formats the current time.
If {format} starts with `!`, then the date is formatted in
@@ -4744,7 +4677,7 @@ os.time([{table}]) *os.time()*
representing the date and time specified by the given table. This
table must have fields `year`, `month`, and `day`, and may have fields
`hour`, `min`, `sec`, and `isdst` (for a description of these fields,
- see the `os.date` function |luaref-os.date|).
+ see the `os.date` function |os.date()|).
The returned value is a number, whose meaning depends on your system.
In POSIX, Windows, and some other systems, this number counts the
@@ -4802,7 +4735,7 @@ debug.getinfo([{thread},] {function} [, {what}]) *debug.getinfo()*
functions, then `getinfo` returns `nil`.
The returned table may contain all the fields returned by
- `lua_getinfo` (see |luaref-lua_getinfo|), with the string {what}
+ `lua_getinfo` (see |lua_getinfo()|), with the string {what}
describing which fields to fill in. The default for {what} is to get
all information available, except the table of valid lines. If
present, the option `f` adds a field named `func` with the function
@@ -4821,7 +4754,7 @@ debug.getlocal([{thread},] {level}, {local}) *debug.getlocal()*
last active local variable.) The function returns `nil` if there is no
local variable with the given index, and raises an error when called
with a {level} out of range. (You can call `debug.getinfo`
- |luaref-debug.getinfo| to check whether the level is valid.)
+ |debug.getinfo()| to check whether the level is valid.)
Variable names starting with `(` (open parentheses) represent
internal variables (loop control variables, temporaries, and C
@@ -4894,12 +4827,11 @@ debug.traceback([{thread},] [{message}] [,{level}]) *debug.traceback()*
==============================================================================
A BIBLIOGRAPHY *luaref-bibliography*
-==============================================================================
This help file is a minor adaptation from this main reference:
- R. Ierusalimschy, L. H. de Figueiredo, and W. Celes.,
- "Lua: 5.1 reference manual", http://www.lua.org/manual/5.1/manual.html
+ "Lua: 5.1 reference manual", https://www.lua.org/manual/5.1/manual.html
Lua is discussed in these references:
@@ -4920,8 +4852,7 @@ Lua is discussed in these references:
"Proc. of V Brazilian Symposium on Programming Languages" (2001) B-14-B-28.
==============================================================================
-B COPYRIGHT & LICENSES *luaref-copyright*
-==============================================================================
+B COPYRIGHT AND LICENSES *luaref-copyright*
This help file has the same copyright and license as Lua 5.1 and the Lua 5.1
manual:
@@ -4938,29 +4869,28 @@ furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 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.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 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.
==============================================================================
C LUAREF DOC *luarefvim* *luarefvimdoc* *luaref-help* *luaref-doc*
-==============================================================================
This is a Vim help file containing a reference for Lua 5.1, and it is -- with
a few exceptions and adaptations -- a copy of the Lua 5.1 Reference Manual
(see |luaref-bibliography|). For usage information, refer to
-|luaref-docUsage|. For copyright information, see |luaref-copyright|.
+|luaref-doc|. For copyright information, see |luaref-copyright|.
The main ideas and concepts on how to implement this reference were taken from
Christian Habermann's CRefVim project
-(http://www.vim.org/scripts/script.php?script_id=614).
+(https://www.vim.org/scripts/script.php?script_id=614).
Adapted for bundled Nvim documentation; the original plugin can be found at
-http://www.vim.org/scripts/script.php?script_id=1291
+https://www.vim.org/scripts/script.php?script_id=1291
------------------------------------------------------------------------------
vi:tw=78:ts=4:ft=help:norl:et
diff --git a/runtime/doc/luvref.txt b/runtime/doc/luvref.txt
index ee45444b42..859e75e4af 100644
--- a/runtime/doc/luvref.txt
+++ b/runtime/doc/luvref.txt
@@ -3,7 +3,7 @@
LUV REFERENCE MANUAL
-
+ *luvref*
This file documents the Lua bindings for the LibUV library which is used for
Nvim's event-loop and is accessible from Lua via |vim.loop| (e.g., |uv.version()|
is exposed as `vim.loop.version()`).
@@ -28,7 +28,7 @@ TCP Echo Server Example~
Here is a small example showing a TCP echo server:
- >
+ >lua
local uv = vim.loop
local server = uv.new_tcp()
@@ -97,7 +97,7 @@ used here to facilitate documenting consistent behavior:
CONTENTS *luv-contents*
This documentation is mostly a retelling of the libuv API documentation
-(http://docs.libuv.org/en/v1.x/api.html) within the context of luv's Lua API.
+(https://docs.libuv.org/en/v1.x/api.html) within the context of luv's Lua API.
Low-level implementation details and unexposed C functions and types are not
documented here except for when they are relevant to behavior seen in the Lua
module.
@@ -250,7 +250,7 @@ uv.loop_configure({option}, {...}) *uv.loop_configure()*
An example of a valid call to this function is:
- >
+ >lua
uv.loop_configure("block_signal", "sigprof")
<
@@ -343,7 +343,7 @@ uv.walk({callback}) *uv.walk()*
Returns: Nothing.
- >
+ >lua
-- Example usage of uv.walk to close all handles that
-- aren't already closing.
uv.walk(function (handle)
@@ -613,7 +613,7 @@ uv.new_timer() *uv.new_timer()*
Returns: `uv_timer_t userdata` or `fail`
- >
+ >lua
-- Creating a simple setTimeout wrapper
local function setTimeout(timeout, callback)
local timer = uv.new_timer()
@@ -737,7 +737,7 @@ uv.timer_get_due_in({timer}) *uv.timer_get_due_in()*
Prepare handles will run the given callback once per loop iteration, right
before polling for I/O.
- >
+ >lua
local prepare = uv.new_prepare()
prepare:start(function()
print("Before I/O polling")
@@ -782,7 +782,7 @@ uv.prepare_stop({prepare}) *uv.prepare_stop()*
Check handles will run the given callback once per loop iteration, right after
polling for I/O.
- >
+ >lua
local check = uv.new_check()
check:start(function()
print("After I/O polling")
@@ -834,7 +834,7 @@ blocking for I/O.
WARNING: Despite the name, idle handles will get their callbacks called on
every loop iteration, not when the loop is actually "idle".
- >
+ >lua
local idle = uv.new_idle()
idle:start(function()
print("Before I/O polling, no blocking")
@@ -879,7 +879,7 @@ uv.idle_stop({check}) *uv.idle_stop()*
Async handles allow the user to "wakeup" the event loop and get a callback
called from another thread.
- >
+ >lua
local async
async = uv.new_async(function()
print("async operation ran")
@@ -933,7 +933,7 @@ uv.async_send({async}, {...}) *uv.async_send()*
Poll handles are used to watch file descriptors for readability and
writability, similar to the purpose of poll(2)
-(http://linux.die.net/man/2/poll).
+(https://linux.die.net/man/2/poll).
The purpose of poll handles is to enable integrating external libraries that
rely on the event loop to signal it about the socket status changes, like
@@ -1062,7 +1062,7 @@ Unix Notes:
will lead to unpredictable behavior and is strongly discouraged. Future
versions of libuv may simply reject them.
- >
+ >lua
-- Create a new signal handler
local signal = uv.new_signal()
-- Define a handler function
@@ -1164,7 +1164,7 @@ uv.spawn({path}, {options}, {on_exit}) *uv.spawn()*
permissions to use the setuid or setgid specified, or not
having enough memory to allocate for the new process.
- >
+ >lua
local stdin = uv.new_pipe()
local stdout = uv.new_pipe()
local stderr = uv.new_pipe()
@@ -1358,7 +1358,7 @@ uv.accept({stream}, {client_stream}) *uv.accept()*
Returns: `0` or `fail`
- >
+ >lua
server:listen(128, function (err)
local client = uv.new_tcp()
server:accept(client)
@@ -1382,7 +1382,7 @@ uv.read_start({stream}, {callback}) *uv.read_start()*
Returns: `0` or `fail`
- >
+ >lua
stream:read_start(function (err, chunk)
if err then
-- handle read error
@@ -1690,7 +1690,7 @@ uv.tcp_connect({tcp}, {host}, {port}, {callback}) *uv.tcp_connect()*
Returns: `uv_connect_t userdata` or `fail`
- >
+ >lua
local client = uv.new_tcp()
client:connect("127.0.0.1", 8080, function (err)
-- check error and carry on.
@@ -1755,7 +1755,7 @@ uv.socketpair([{socktype}, [{protocol}, [{flags1}, [{flags2}]]]])
Returns: `table` or `fail`
- `[1, 2]` : `integer` (file descriptor)
- >
+ >lua
-- Simple read/write with tcp
local fds = uv.socketpair(nil, nil, {nonblock=true}, {nonblock=true})
@@ -1780,7 +1780,7 @@ uv.socketpair([{socktype}, [{protocol}, [{flags1}, [{flags2}]]]])
Pipe handles provide an abstraction over local domain sockets on Unix and
named pipes on Windows.
- >
+ >lua
local pipe = uv.new_pipe(false)
pipe:bind('/tmp/sock.test')
@@ -1959,7 +1959,7 @@ uv.pipe({read_flags}, {write_flags}) *uv.pipe()*
- `read` : `integer` (file descriptor)
- `write` : `integer` (file descriptor)
- >
+ >lua
-- Simple read/write with pipe_open
local fds = uv.pipe({nonblock=true}, {nonblock=true})
@@ -1983,7 +1983,7 @@ uv.pipe({read_flags}, {write_flags}) *uv.pipe()*
TTY handles represent a stream for the console.
- >
+ >lua
-- Simple echo program
local stdin = uv.new_tty(0, true)
local stdout = uv.new_tty(1, false)
@@ -2537,7 +2537,7 @@ FS call.
Synchronous and asynchronous versions of `readFile` (with naive error
handling) are implemented below as an example:
- >
+ >lua
local function readFileSync(path)
local fd = assert(uv.fs_open(path, "r", 438))
local stat = assert(uv.fs_fstat(fd))
@@ -2550,7 +2550,7 @@ handling) are implemented below as an example:
print("synchronous read", data)
<
- >
+ >lua
local function readFile(path, callback)
uv.fs_open(path, "r", 438, function(err, fd)
assert(not err, err)
@@ -2626,7 +2626,7 @@ uv.fs_read({fd}, {size} [, {offset} [, {callback}]]) *uv.fs_read()*
indicates EOF.
If `offset` is nil or omitted, it will default to `-1`, which
- indicates 'use and update the current file offset.'
+ indicates "use and update the current file offset."
Note: When `offset` is >= 0, the current file offset will not
be updated by the read.
@@ -2665,7 +2665,7 @@ uv.fs_write({fd}, {data} [, {offset} [, {callback}]]) *uv.fs_write()*
written.
If `offset` is nil or omitted, it will default to `-1`, which
- indicates 'use and update the current file offset.'
+ indicates "use and update the current file offset."
Note: When `offset` is >= 0, the current file offset will not
be updated by the write.
@@ -3253,7 +3253,7 @@ Libuv provides a threadpool which can be used to run user code and get
notified in the loop thread. This threadpool is internally used to run all
file system operations, as well as `getaddrinfo` and `getnameinfo` requests.
- >
+ >lua
local function work_callback(a, b)
return a + b
end
@@ -3355,7 +3355,7 @@ uv.getnameinfo({address} [, {callback}]) *uv.getnameinfo()*
- `family`: `string` or `integer` or `nil`
- `callback`: `callable` (async version) or `nil` (sync
version)
- - `err`: `nil` or `sring`
+ - `err`: `nil` or `string`
- `host`: `string` or `nil`
- `service`: `string` or `nil`
diff --git a/runtime/doc/makehtml.awk b/runtime/doc/makehtml.awk
deleted file mode 100644
index 50f5611fa7..0000000000
--- a/runtime/doc/makehtml.awk
+++ /dev/null
@@ -1,788 +0,0 @@
-BEGIN {
- # some initialization variables
- asciiart="no";
- wasset="no";
- lineset=0;
- sample="no";
- while ( getline ti <"tags.ref" > 0 ) {
- nf=split(ti,tag," ");
- # as help.txt renders into index.html and index.txt -> vimindex.html,
- # this hack is needed to get the links right to those pages.
- if ( tag[2] == "index.txt" ) {
- tag[2] = "vimindex.txt"
- } else if ( tag[2] == "help.txt" ) {
- tag[2] = "index.txt"
- }
- tagkey[tag[1]]="yes";tagref[tag[1]]=tag[2];
- }
- skip_word["and"]="yes";
- skip_word["backspace"]="yes";
- skip_word["beep"]="yes";
- skip_word["bugs"]="yes";
- skip_word["da"]="yes";
- skip_word["end"]="yes";
- skip_word["ftp"]="yes";
- skip_word["go"]="yes";
- skip_word["help"]="yes";
- skip_word["home"]="yes";
- skip_word["news"]="yes";
- skip_word["index"]="yes";
- skip_word["insert"]="yes";
- skip_word["into"]="yes";
- skip_word["put"]="yes";
- skip_word["reference"]="yes";
- skip_word["section"]="yes";
- skip_word["space"]="yes";
- skip_word["starting"]="yes";
- skip_word["toggle"]="yes";
- skip_word["various"]="yes";
- skip_word["version"]="yes";
- skip_word["is"]="yes";
-}
-#
-# protect special chars
-#
-/[><&]/ {gsub(/&/,"\\&amp;");gsub(/>/,"\\&gt;");gsub(/</,"\\&lt;");gsub("","\\&aacute;");}
-#
-# end of sample lines by non-blank in first column
-#
-sample == "yes" && substr($0,1,4) == "&lt;" { sample = "no"; gsub(/^&lt;/, " "); }
-sample == "yes" && substr($0,1,1) != " " && substr($0,1,1) != " " && length($0) > 0 { sample = "no" }
-#
-# sample lines printed bold unless empty...
-#
-sample == "yes" && $0 =="" { print ""; next; }
-sample == "yes" && $0 !="" { print "<B>" $0 "</B>"; next; }
-#
-# start of sample lines in next line
-#
-$0 == "&gt;" { sample = "yes"; print ""; next; }
-substr($0,length($0)-4,5) == " &gt;" { sample = "yes"; gsub(/ &gt;$/, ""); }
-#
-# header lines printed bold, colored
-#
-substr($0,length($0),1) == "~" { print "<B><FONT COLOR=\"PURPLE\">" substr($0,1,length($0)-1) "</FONT></B>"; next; }
-#
-#ad hoc code
-#
-/^"\|& / {gsub(/\|/,"\\&#124;"); }
-/ = b / {gsub(/ b /," \\&#98; "); }
-#
-# one letter tag
-#
-/[ ]\*.\*[ ]/ {gsub(/\*/,"ZWWZ"); }
-#
-# isolated "*"
-#
-/[ ]\*[ ]/ {gsub(/ \* /," \\&#42; ");
- gsub(/ \* /," \\&#42; ");
- gsub(/ \* /," \\&#42; ");
- gsub(/ \* /," \\&#42; "); }
-#
-# tag start
-#
-/[ ]\*[^ ]/ {gsub(/ \*/," ZWWZ");gsub(/ \*/," ZWWZ");}
-/^\*[^ ]/ {gsub(/^\*/,"ZWWZ");}
-#
-# tag end
-#
-/[^ ]\*$/ {gsub(/\*$/,"ZWWZ");}
-/[^ \/ ]\*[ ]/ {gsub(/\*/,"ZWWZ");}
-#
-# isolated "|"
-#
-/[ ]\|[ ]/ {gsub(/ \| /," \\&#124; ");
- gsub(/ \| /," \\&#124; ");
- gsub(/ \| /," \\&#124; ");
- gsub(/ \| /," \\&#124; "); }
-/'\|'/ { gsub(/'\|'/,"'\\&#124;'"); }
-/\^V\|/ {gsub(/\^V\|/,"^V\\&#124;");}
-/ \\\| / {gsub(/\|/,"\\&#124;");}
-#
-# one letter pipes and "||" false pipe (digraphs)
-#
-/[ ]\|.\|[ ]/ && asciiart == "no" {gsub(/\|/,"YXXY"); }
-/^\|.\|[ ]/ {gsub(/\|/,"YXXY"); }
-/\|\|/ {gsub(/\|\|/,"\\&#124;\\&#124;"); }
-/^shellpipe/ {gsub(/\|/,"\\&#124;"); }
-#
-# pipe start
-#
-/[ ]\|[^ ]/ && asciiart == "no" {gsub(/ \|/," YXXY");
- gsub(/ \|/," YXXY");}
-/^\|[^ ]/ {gsub(/^\|/,"YXXY");}
-#
-# pipe end
-#
-/[^ ]\|$/ && asciiart == "no" {gsub(/\|$/,"YXXY");}
-/[^ ]\|[s ,.); ]/ && asciiart == "no" {gsub(/\|/,"YXXY");}
-/[^ ]\|]/ && asciiart == "no" {gsub(/\|/,"YXXY");}
-#
-# various
-#
-/'"/ {gsub(/'"/,"\\&#39;\\&#34;'");}
-/"/ {gsub(/"/,"\\&quot;");}
-/%/ {gsub(/%/,"\\&#37;");}
-
-NR == 1 { nf=split(FILENAME,f,".")
- print "<HTML>";
-
- print "<HEAD>"
- if ( FILENAME == "mbyte.txt" ) {
- # needs utf-8 as uses many languages
- print "<META HTTP-EQUIV=\"Content-type\" content=\"text/html; charset=UTF-8\">";
- } else {
- # common case - Latin1
- print "<META HTTP-EQUIV=\"Content-type\" content=\"text/html; charset=ISO-8859-1\">";
- }
- print "<TITLE>Nvim documentation: " f[1] "</TITLE>";
- print "</HEAD>";
-
- print "<BODY BGCOLOR=\"#ffffff\">";
- print "<H1>Nvim documentation: " f[1] "</H1>";
- print "<A NAME=\"top\"></A>";
- if ( FILENAME != "help.txt" ) {
- print "<A HREF=\"index.html\">main help file</A>\n";
- }
- print "<HR>";
- print "<PRE>";
- filename=f[1]".html";
-}
-
-# set to a low value to test for few lines of text
-# NR == 99999 { exit; }
-
-# ignore underlines and tags
-substr($0,1,5) == " vim:" { next; }
-substr($0,1,4) == "vim:" { next; }
-# keep just whole lines of "-", "="
-substr($0,1,3) == "===" && substr($0,75,1) != "=" { next; }
-substr($0,1,3) == "---" && substr($0,75,1) != "-" { next; }
-
-{
- nstar = split($0,s,"ZWWZ");
- for ( i=2 ; i <= nstar ; i=i+2 ) {
- nbla=split(s[i],blata,"[ ]");
- if ( nbla > 1 ) {
- gsub("ZWWZ","*");
- nstar = split($0,s,"ZWWZ");
- }
- }
- npipe = split($0,p,"YXXY");
- for ( i=2 ; i <= npipe ; i=i+2 ) {
- nbla=split(p[i],blata,"[ ]");
- if ( nbla > 1 ) {
- gsub("YXXY","|");
- ntabs = split($0,p,"YXXY");
- }
- }
-}
-
-
-FILENAME == "gui.txt" && asciiart == "no" \
- && $0 ~ /\+----/ && $0 ~ /----\+/ {
- asciiart= "yes";
- asciicnt=0;
- }
-
-FILENAME == "usr_20.txt" && asciiart == "no" \
- && $0 ~ /an empty line at the end:/ {
- asciiart= "yes";
- asciicnt=0;
- }
-
-asciiart == "yes" && $0=="" { asciicnt++; }
-
-asciiart == "yes" && asciicnt == 2 { asciiart = "no"; }
-
-asciiart == "yes" { npipe = 1; }
-# { print NR " <=> " asciiart; }
-
-#
-# line contains "*"
-#
-nstar > 2 && npipe < 3 {
- printf("\n");
- for ( i=1; i <= nstar ; i=i+2 ) {
- this=s[i];
- put_this();
- ii=i+1;
- nbla = split(s[ii],blata," ");
- if ( ii <= nstar ) {
- if ( nbla == 1 && substr(s[ii],length(s[ii]),1) != " " ) {
- printf("*<A NAME=\"%s\"></A>",s[ii]);
- printf("<B>%s</B>*",s[ii]);
- } else {
- printf("*%s*",s[ii]);
- }
- }
- }
- printf("\n");
- next;
- }
-#
-# line contains "|"
-#
-npipe > 2 && nstar < 3 {
- if ( npipe%2 == 0 ) {
- for ( i=1; i < npipe ; i++ ) {
- gsub("ZWWZ","*",p[i]);
- printf("%s|",p[i]);
- }
- printf("%s\n",p[npipe]);
- next;
- }
- for ( i=1; i <= npipe ; i++ )
- {
- if ( i % 2 == 1 ) {
- gsub("ZWWZ","*",p[i]);
- this=p[i];
- put_this();
- }
- else {
- nfn=split(p[i],f,".");
- if ( nfn == 1 || f[2] == "" || f[1] == "" || length(f[2]) < 3 ) {
- find_tag1();
- }
- else {
- if ( f[1] == "index" ) {
- printf "|<A HREF=\"vimindex.html\">" p[i] "</A>|";
- } else {
- if ( f[1] == "help" ) {
- printf "|<A HREF=\"index.html\">" p[i] "</A>|";
- } else {
- printf "|<A HREF=\"" f[1] ".html\">" p[i] "</A>|";
- }
- }
- }
- }
- }
- printf("\n");
- next;
- }
-#
-# line contains both "|" and "*"
-#
-npipe > 2 && nstar > 2 {
- printf("\n");
- for ( j=1; j <= nstar ; j=j+2 ) {
- npipe = split(s[j],p,"YXXY");
- if ( npipe > 1 ) {
- for ( np=1; np<=npipe; np=np+2 ) {
- this=p[np];
- put_this();
- i=np+1;find_tag1();
- }
- } else {
- this=s[j];
- put_this();
- }
- jj=j+1;
- nbla = split(s[jj],blata," ");
- if ( jj <= nstar && nbla == 1 && s[jj] != "" ) {
- printf("*<A NAME=\"%s\"></A>",s[jj]);
- printf("<B>%s</B>*",s[jj]);
- } else {
- if ( s[jj] != "" ) {
- printf("*%s*",s[jj]);
- }
- }
- }
- printf("\n");
- next;
- }
-#
-# line contains e-mail address john.doe@some.place.edu
-#
-$0 ~ /@/ && $0 ~ /[a-zA-Z0-9]@[a-z]/ \
- {
- nemail=split($0,em," ");
- if ( substr($0,1,1) == " " ) { printf(" "); }
- for ( i=1; i <= nemail; i++ ) {
- if ( em[i] ~ /@/ ) {
- if ( substr(em[i],2,3) == "lt;" && substr(em[i],length(em[i])-2,3) == "gt;" ) {
- mailaddr=substr(em[i],5,length(em[i])-8);
- printf("<A HREF=\"mailto:%s\">&lt;%s&gt;</A> ",mailaddr,mailaddr);
- } else {
- if ( substr(em[i],2,3) == "lt;" && substr(em[i],length(em[i])-3,3) == "gt;" ) {
- mailaddr=substr(em[i],5,length(em[i])-9);
- printf("<A HREF=\"mailto:%s\">&lt;%s&gt;</A>%s ",mailaddr,mailaddr,substr(em[i],length(em[i]),1));
- } else {
- printf("<A HREF=\"mailto:%s\">%s</A> ",em[i],em[i]);
- }
- }
- } else {
- printf("%s ",em[i]);
- }
- }
- #print "*** " NR " " FILENAME " - possible mail ref";
- printf("\n");
- next;
- }
-#
-# line contains http / ftp reference
-#
-$0 ~ /http:\/\// || $0 ~ /ftp:\/\// {
- gsub("URL:","");
- gsub("&lt;","");
- gsub("&gt;","");
- gsub("\\(","");
- gsub("\\)","");
- nemail=split($0,em," ");
- for ( i=1; i <= nemail; i++ ) {
- if ( substr(em[i],1,5) == "http:" ||
- substr(em[i],1,4) == "ftp:" ) {
- if ( substr(em[i],length(em[i]),1) != "." ) {
- printf(" <A HREF=\"%s\">%s</A>",em[i],em[i]);
- } else {
- em[i]=substr(em[i],1,length(em[i])-1);
- printf(" <A HREF=\"%s\">%s</A>.",em[i],em[i]);
- }
- } else {
- printf(" %s",em[i]);
- }
- }
- #print "*** " NR " " FILENAME " - possible http ref";
- printf("\n");
- next;
- }
-#
-# some lines contains just one "almost regular" "*"...
-#
-nstar == 2 {
- this=s[1];
- put_this();
- printf("*");
- this=s[2];
- put_this();
- printf("\n");
- next;
- }
-#
-# regular line
-#
- { ntabs = split($0,tb," ");
- for ( i=1; i < ntabs ; i++) {
- this=tb[i];
- put_this();
- printf(" ");
- }
- this=tb[ntabs];
- put_this();
- printf("\n");
- }
-
-
-asciiart == "yes" && $0 ~ /\+-\+--/ \
- && $0 ~ "scrollbar" { asciiart = "no"; }
-
-END {
- topback();
- print "</PRE>\n</BODY>\n\n\n</HTML>"; }
-
-#
-# as main we keep index.txt (by default)
-#
-function topback () {
- if ( FILENAME != "tags" ) {
- if ( FILENAME != "help.txt" ) {
- printf("<A HREF=\"#top\">top</A> - ");
- printf("<A HREF=\"index.html\">main help file</A>\n");
- } else {
- printf("<A HREF=\"#top\">top</A>\n");
- }
- }
-}
-
-function find_tag1() {
- if ( p[i] == "" ) { return; }
- if ( tagkey[p[i]] == "yes" ) {
- which=tagref[p[i]];
- put_href();
- return;
- }
- # if not found, then we have a problem
- print "============================================" >>"errors.log";
- print FILENAME ", line " NR ", pointer: >>" p[i] "<<" >>"errors.log";
- print $0 >>"errors.log";
- which="intro.html";
- put_href();
-}
-
-function see_tag() {
-# ad-hoc code:
-if ( atag == "\"--" || atag == "--\"" ) { return; }
-if_already();
-if ( already == "yes" ) {
- printf("%s",aword);
- return;
- }
-allow_one_char="no";
-find_tag2();
-if ( done == "yes" ) { return; }
-rightchar=substr(atag,length(atag),1);
-if ( rightchar == "." \
- || rightchar == "," \
- || rightchar == ":" \
- || rightchar == ";" \
- || rightchar == "!" \
- || rightchar == "?" \
- || rightchar == ")" ) {
- atag=substr(atag,1,length(atag)-1);
- if_already();
- if ( already == "yes" ) {
- printf("%s",aword);
- return;
- }
- find_tag2();
- if ( done == "yes" ) { printf("%s",rightchar);return; }
- leftchar=substr(atag,1,1);
- lastbut1=substr(atag,length(atag),1);
- if ( leftchar == "'" && lastbut1 == "'" ) {
- allow_one_char="yes";
- atag=substr(atag,2,length(atag)-2);
- if_already();
- if ( already == "yes" ) {
- printf("%s",aword);
- return;
- }
- printf("%s",leftchar);
- aword=substr(atag,1,length(atag))""lastbut1""rightchar;
- find_tag2();
- if ( done == "yes" ) { printf("%s%s",lastbut1,rightchar);return; }
- }
- }
-atag=aword;
-leftchar=substr(atag,1,1);
-if ( leftchar == "'" && rightchar == "'" ) {
- allow_one_char="yes";
- atag=substr(atag,2,length(atag)-2);
- if ( atag == "<" ) { printf(" |%s|%s| ",atag,p[2]); }
- if_already();
- if ( already == "yes" ) {
- printf("%s",aword);
- return;
- }
- printf("%s",leftchar);
- find_tag2();
- if ( done == "yes" ) { printf("%s",rightchar);return; }
- printf("%s%s",atag,rightchar);
- return;
- }
-last2=substr(atag,length(atag)-1,2);
-first2=substr(atag,1,2);
-if ( first2 == "('" && last2 == "')" ) {
- allow_one_char="yes";
- atag=substr(atag,3,length(atag)-4);
- if_already();
- if ( already == "yes" ) {
- printf("%s",aword);
- return;
- }
- printf("%s",first2);
- find_tag2();
- if ( done == "yes" ) { printf("%s",last2);return; }
- printf("%s%s",atag,last2);
- return;
- }
-if ( last2 == ".)" ) {
- atag=substr(atag,1,length(atag)-2);
- if_already();
- if ( already == "yes" ) {
- printf("%s",aword);
- return;
- }
- find_tag2();
- if ( done == "yes" ) { printf("%s",last2);return; }
- printf("%s%s",atag,last2);
- return;
- }
-if ( last2 == ")." ) {
- atag=substr(atag,1,length(atag)-2);
- find_tag2();
- if_already();
- if ( already == "yes" ) {
- printf("%s",aword);
- return;
- }
- if ( done == "yes" ) { printf("%s",last2);return; }
- printf("%s%s",atag,last2);
- return;
- }
-first6=substr(atag,1,6);
-last6=substr(atag,length(atag)-5,6);
-if ( last6 == atag ) {
- printf("%s",aword);
- return;
- }
-last6of7=substr(atag,length(atag)-6,6);
-if ( first6 == "&quot;" && last6of7 == "&quot;" && length(atag) > 12 ) {
- allow_one_char="yes";
- atag=substr(atag,7,length(atag)-13);
- if_already();
- if ( already == "yes" ) {
- printf("%s",aword);
- return;
- }
- printf("%s",first6);
- find_tag2();
- if ( done == "yes" ) { printf("&quot;%s",rightchar); return; }
- printf("%s&quot;%s",atag,rightchar);
- return;
- }
-if ( first6 == "&quot;" && last6 != "&quot;" ) {
- allow_one_char="yes";
- atag=substr(atag,7,length(atag)-6);
- if ( atag == "[" ) { printf("&quot;%s",atag); return; }
- if ( atag == "." ) { printf("&quot;%s",atag); return; }
- if ( atag == ":" ) { printf("&quot;%s",atag); return; }
- if ( atag == "a" ) { printf("&quot;%s",atag); return; }
- if ( atag == "A" ) { printf("&quot;%s",atag); return; }
- if ( atag == "g" ) { printf("&quot;%s",atag); return; }
- if_already();
- if ( already == "yes" ) {
- printf("&quot;%s",atag);
- return;
- }
- printf("%s",first6);
- find_tag2();
- if ( done == "yes" ) { return; }
- printf("%s",atag);
- return;
- }
-if ( last6 == "&quot;" && first6 == "&quot;" ) {
- allow_one_char="yes";
- atag=substr(atag,7,length(atag)-12);
- if_already();
- if ( already == "yes" ) {
- printf("%s",aword);
- return;
- }
- printf("%s",first6);
- find_tag2();
- if ( done == "yes" ) { printf("%s",last6);return; }
- printf("%s%s",atag,last6);
- return;
- }
-last6of7=substr(atag,length(atag)-6,6);
-if ( last6of7 == "&quot;" && first6 == "&quot;" ) {
- allow_one_char="yes";
- atag=substr(atag,7,length(atag)-13);
- #printf("\natag=%s,aword=%s\n",atag,aword);
- if_already();
- if ( already == "yes" ) {
- printf("%s",aword);
- return;
- }
- printf("%s",first6);
- find_tag2();
- if ( done == "yes" ) { printf("%s%s",last6of7,rightchar);return; }
- printf("%s%s%s",atag,last6of7,rightchar);
- return;
- }
-printf("%s",aword);
-}
-
-function find_tag2() {
- done="no";
- # no blanks present in a tag...
- ntags=split(atag,blata,"[ ]");
- if ( ntags > 1 ) { return; }
- if ( ( allow_one_char == "no" ) && \
- ( index("!#$%&'()+,-./0:;=?@ACINX\\[\\]^_`at\\{\\}~",atag) !=0 ) ) {
- return;
- }
- if ( skip_word[atag] == "yes" ) { return; }
- if ( wasset == "yes" && lineset == NR ) {
- wasset="no";
- see_opt();
- if ( done_opt == "yes" ) {return;}
- }
- if ( wasset == "yes" && lineset != NR ) {
- wasset="no";
- }
- if ( atag == ":set" ) {
- wasset="yes";
- lineset=NR;
- }
- if ( tagkey[atag] == "yes" ) {
- which=tagref[atag];
- put_href2();
- done="yes";
- }
-}
-
-function find_tag3() {
- done="no";
- # no blanks present in a tag...
- ntags=split(btag,blata,"[ ]");
- if ( ntags > 1 ) { return; }
- if ( ( allow_one_char == "no" ) && \
- ( index("!#$%&'()+,-./0:;=?@ACINX\\[\\]^_`at\\{\\}~",btag) !=0 ) ) {
- return;
- }
- if ( skip_word[btag] == "yes" ) { return; }
- if ( tagkey[btag] == "yes" ) {
- which=tagref[btag];
- put_href3();
- done="yes";
- }
-}
-
-function put_href() {
- if ( p[i] == "" ) { return; }
- if ( which == FILENAME ) {
- printf("|<A HREF=\"#%s\">%s</A>|",p[i],p[i]);
- }
- else {
- nz=split(which,zz,".");
- if ( zz[2] == "txt" || zz[1] == "tags" ) {
- printf("|<A HREF=\"%s.html#%s\">%s</A>|",zz[1],p[i],p[i]);
- }
- else {
- printf("|<A HREF=\"intro.html#%s\">%s</A>|",p[i],p[i]);
- }
- }
-}
-
-function put_href2() {
- if ( atag == "" ) { return; }
- if ( which == FILENAME ) {
- printf("<A HREF=\"#%s\">%s</A>",atag,atag);
- }
- else {
- nz=split(which,zz,".");
- if ( zz[2] == "txt" || zz[1] == "tags" ) {
- printf("<A HREF=\"%s.html#%s\">%s</A>",zz[1],atag,atag);
- }
- else {
- printf("<A HREF=\"intro.html#%s\">%s</A>",atag,atag);
- }
- }
-}
-
-function put_href3() {
- if ( btag == "" ) { return; }
- if ( which == FILENAME ) {
- printf("<A HREF=\"#%s\">%s</A>",btag,btag2);
- }
- else {
- nz=split(which,zz,".");
- if ( zz[2] == "txt" || zz[1] == "tags" ) {
- printf("<A HREF=\"%s.html#%s\">%s</A>",zz[1],btag,btag2);
- }
- else {
- printf("<A HREF=\"intro.html#%s\">%s</A>",btag,btag2);
- }
- }
-}
-
-function put_this() {
- ntab=split(this,ta," ");
- for ( nta=1 ; nta <= ntab ; nta++ ) {
- ata=ta[nta];
- lata=length(ata);
- aword="";
- for ( iata=1 ; iata <=lata ; iata++ ) {
- achar=substr(ata,iata,1);
- if ( achar != " " ) { aword=aword""achar; }
- else {
- if ( aword != "" ) { atag=aword;
- see_tag();
- aword="";
- printf(" "); }
- else {
- printf(" ");
- }
- }
- }
- if ( aword != "" ) { atag=aword;
- see_tag();
- }
- if ( nta != ntab ) { printf(" "); }
- }
-}
-
-function if_already() {
- already="no";
- if ( npipe < 2 ) { return; }
- if ( atag == ":au" && p[2] == ":autocmd" ) { already="yes";return; }
- for ( npp=2 ; npp <= npipe ; npp=npp+2 ) {
- if ( ( (index(p[npp],atag)) != 0 \
- && length(p[npp]) > length(atag) \
- && length(atag) >= 1 \
- ) \
- || (p[npp] == atag) \
- ) {
- # printf("p=|%s|,tag=|%s| ",p[npp],atag);
- already="yes"; return; }
- }
-}
-
-function see_opt() {
- done_opt="no";
- stag=atag;
- nfields = split(atag,tae,"=");
- if ( nfields > 1 ) {
- btag="'"tae[1]"'";
- btag2=tae[1];
- find_tag3();
- if (done == "yes") {
- for ( ntae=2 ; ntae <= nfields ; ntae++ ) {
- printf("=%s",tae[ntae]);
- }
- atag=stag;
- done_opt="yes";
- return;
- }
- btag=tae[1];
- btag2=tae[1];
- find_tag3();
- if ( done=="yes" ) {
- for ( ntae=2 ; ntae <= nfields ; ntae++ ) {
- printf("=%s",tae[ntae]);
- }
- atag=stag;
- done_opt="yes";
- return;
- }
- }
- nfields = split(atag,tae,"&quot;");
- if ( nfields > 1 ) {
- btag="'"tae[1]"'";
- btag2=tae[1];
- find_tag3();
- if (done == "yes") {
- printf("&quot;");
- atag=stag;
- done_opt="yes";
- return;
- }
- btag=tae[1];
- btag2=tae[1];
- find_tag3();
- if (done == "yes") {
- printf("&quot;");
- atag=stag;
- done_opt="yes";
- return;
- }
- }
- btag="'"tae[1]"'";
- btag2=tae[1];
- find_tag3();
- if (done == "yes") {
- atag=stag;
- done_opt="yes";
- return;
- }
- btag=tae[1];
- btag2=tae[1];
- find_tag3();
- if (done == "yes") {
- atag=stag;
- done_opt="yes";
- return;
- }
- atag=stag;
-}
diff --git a/runtime/doc/maketags.awk b/runtime/doc/maketags.awk
deleted file mode 100644
index c6b2cd91f3..0000000000
--- a/runtime/doc/maketags.awk
+++ /dev/null
@@ -1,42 +0,0 @@
-BEGIN { FS=" "; }
-
-NR == 1 { nf=split(FILENAME,f,".")
- print "<HTML>";
- print "<HEAD><TITLE>" f[1] "</TITLE></HEAD>";
- print "<BODY BGCOLOR=\"#ffffff\">";
- print "<H1>Vim Documentation: " f[1] "</H1>";
- print "<A NAME=\"top\"></A>";
- print "<HR>";
- print "<PRE>";
-}
-
-{
- #
- # protect special chars
- #
- gsub(/&/,"\\&amp;");
- gsub(/>/,"\\&gt;");
- gsub(/</,"\\&lt;");
- gsub(/"/,"\\&quot;");
- gsub(/%/,"\\&#37;");
-
- nf=split($0,tag," ");
- tagkey[t]=tag[1];tagref[t]=tag[2];tagnum[t]=NR;
- print $1 " " $2 " line " NR >"tags.ref"
- n=split($2,w,".");
- printf ("|<A HREF=\"%s.html#%s\">%s</A>| %s\n",w[1],$1,$1,$2);
-}
-
-END {
- topback();
- print "</PRE>\n</BODY>\n\n\n</HTML>";
- }
-
-#
-# as main we keep index.txt (by default)
-# other candidate, help.txt
-#
-function topback () {
- printf("<A HREF=\"#top\">top</A> - ");
- printf("<A HREF=\"help.html\">back to help</A>\n");
-}
diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt
index ca1ddaabd4..cb8b162eb6 100644
--- a/runtime/doc/map.txt
+++ b/runtime/doc/map.txt
@@ -215,6 +215,9 @@ The search string will not be echoed when using this mapping. Messages from
the executed command are still given though. To shut them up too, add a
":silent" in the executed command: >
:map <silent> ,h :exe ":silent normal /Header\r"<CR>
+Note that the effect of a command might also be silenced, e.g., when the
+mapping selects another entry for command line completion it won't be
+displayed.
Prompts will still be given, e.g., for inputdialog().
Using "<silent>" for an abbreviation is possible, but will cause redrawing of
the command line to fail.
@@ -648,6 +651,10 @@ The special key name "<Plug>" can be used for an internal mapping, which is
not to be matched with any key sequence. This is useful in plugins
|using-<Plug>|.
+ *<MouseMove>*
+The special key name "<MouseMove>" can be used to handle mouse movement. It
+needs to be enabled with 'mousemoveevent'.
+
*<Char>* *<Char->*
To map a character by its decimal, octal or hexadecimal number the <Char>
construct can be used:
@@ -659,9 +666,9 @@ This is useful to specify a (multibyte) character in a 'keymap' file.
Upper and lowercase differences are ignored.
*map-comments*
-It is not possible to put a comment after these commands, because the '"'
+It is not possible to put a comment after these commands, because the `"`
character is considered to be part of the {lhs} or {rhs}. However, one can
-use |", since this starts a new, empty command with a comment.
+use `|"`, since this starts a new, empty command with a comment.
*map_bar* *map-bar*
Since the '|' character is used to separate a map command from the next
@@ -691,8 +698,8 @@ To avoid mapping of the characters you type in insert or Command-line mode,
type a CTRL-V first. The mapping in Insert mode is disabled if the 'paste'
option is on.
*map-error*
-Note that when an error is encountered (that causes an error message or beep)
-the rest of the mapping is not executed. This is Vi-compatible.
+Note that when an error is encountered (that causes an error message or might
+cause a beep) the rest of the mapping is not executed. This is Vi-compatible.
Note that the second character (argument) of the commands @zZtTfF[]rm'`"v
and CTRL-X is not mapped. This was done to be able to use all the named
@@ -865,28 +872,56 @@ Here is an example that counts the number of spaces with <F4>: >
" doubling <F4> works on a line
nnoremap <expr> <F4><F4> CountSpaces() .. '_'
- function CountSpaces(type = '') abort
+ function CountSpaces(context = {}, type = '') abort
if a:type == ''
- set opfunc=CountSpaces
+ let context = #{
+ \ dot_command: v:false,
+ \ extend_block: '',
+ \ virtualedit: [&l:virtualedit, &g:virtualedit],
+ \ }
+ let &operatorfunc = function('CountSpaces', [context])
+ set virtualedit=block
return 'g@'
endif
- let sel_save = &selection
- let reg_save = getreginfo('"')
- let cb_save = &clipboard
- let visual_marks_save = [getpos("'<"), getpos("'>")]
+ let save = #{
+ \ clipboard: &clipboard,
+ \ selection: &selection,
+ \ virtualedit: [&l:virtualedit, &g:virtualedit],
+ \ register: getreginfo('"'),
+ \ visual_marks: [getpos("'<"), getpos("'>")],
+ \ }
try
- set clipboard= selection=inclusive
- let commands = #{line: "'[V']y", char: "`[v`]y", block: "`[\<c-v>`]y"}
- silent exe 'noautocmd keepjumps normal! ' .. get(commands, a:type, '')
- echom count(getreg('"'), ' ')
+ set clipboard= selection=inclusive virtualedit=
+ let commands = #{
+ \ line: "'[V']",
+ \ char: "`[v`]",
+ \ block: "`[\<C-V>`]",
+ \ }[a:type]
+ let [_, _, col, off] = getpos("']")
+ if off != 0
+ let vcol = getline("'[")->strpart(0, col + off)->strdisplaywidth()
+ if vcol >= [line("'["), '$']->virtcol() - 1
+ let a:context.extend_block = '$'
+ else
+ let a:context.extend_block = vcol .. '|'
+ endif
+ endif
+ if a:context.extend_block != ''
+ let commands ..= 'oO' .. a:context.extend_block
+ endif
+ let commands ..= 'y'
+ execute 'silent noautocmd keepjumps normal! ' .. commands
+ echomsg getreg('"')->count(' ')
finally
- call setreg('"', reg_save)
- call setpos("'<", visual_marks_save[0])
- call setpos("'>", visual_marks_save[1])
- let &clipboard = cb_save
- let &selection = sel_save
+ call setreg('"', save.register)
+ call setpos("'<", save.visual_marks[0])
+ call setpos("'>", save.visual_marks[1])
+ let &clipboard = save.clipboard
+ let &selection = save.selection
+ let [&l:virtualedit, &g:virtualedit] = get(a:context.dot_command ? save : a:context, 'virtualedit')
+ let a:context.dot_command = v:true
endtry
endfunction
@@ -1336,7 +1371,6 @@ completion can be enabled:
-complete=color color schemes
-complete=command Ex command (and arguments)
-complete=compiler compilers
- -complete=cscope |:cscope| suboptions
-complete=dir directory names
-complete=environment environment variable names
-complete=event autocommand events
@@ -1398,9 +1432,11 @@ The function arguments are:
The function may use these for determining context. For the "custom"
argument, it is not necessary to filter candidates against the (implicit
pattern in) ArgLead. Vim will filter the candidates with its regexp engine
-after function return, and this is probably more efficient in most cases. For
-the "customlist" argument, Vim will not filter the returned completion
-candidates and the user supplied function should filter the candidates.
+after function return, and this is probably more efficient in most cases. If
+'wildoptions' contains "fuzzy", then the candidates will be filtered using
+|fuzzy-matching|. For the "customlist" argument, Vim will not
+filter the returned completion candidates and the user supplied function
+should filter the candidates.
The following example lists user names to a Finger command >
:com -complete=custom,ListUsers -nargs=1 Finger !finger <args>
@@ -1437,7 +1473,7 @@ Possible attributes are:
number.
-count=N A count (default N) which is specified either in the line
number position, or as an initial argument (like |:Next|).
- Specifying -count (without a default) acts like -count=0
+ -count Acts like -count=0
Note that -range=N and -count=N are mutually exclusive - only one should be
specified.
@@ -1448,14 +1484,16 @@ 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 line Range of lines (this is the default)
+ -addr=lines Range of lines (this is the default for -range)
-addr=arguments arg Range for arguments
-addr=buffers buf Range for buffers (also not loaded buffers)
-addr=loaded_buffers load Range for loaded buffers
-addr=windows win Range for windows
-addr=tabs tab Range for tab pages
-addr=quickfix qf Range for quickfix entries
- -addr=other ? other kind of range
+ -addr=other ? Other kind of range; can use ".", "$" and "%"
+ as with "lines" (this is the default for
+ -count)
Incremental preview ~
@@ -1623,11 +1661,11 @@ The valid escape sequences are
*<mods>* *<q-mods>* *:command-modifiers*
<mods> The command modifiers, if specified. Otherwise, expands to
nothing. Supported modifiers are |:aboveleft|, |:belowright|,
- |:botright|, |:browse|, |:confirm|, |:hide|, |:keepalt|,
- |:keepjumps|, |:keepmarks|, |:keeppatterns|, |:leftabove|,
- |:lockmarks|, |:noautocmd|, |:noswapfile| |:rightbelow|,
- |:sandbox|, |:silent|, |:tab|, |:topleft|, |:unsilent|,
- |:verbose|, and |:vertical|.
+ |:botright|, |:browse|, |:confirm|, |:hide|, |:horizontal|,
+ |:keepalt|, |:keepjumps|, |:keepmarks|, |:keeppatterns|,
+ |:leftabove|, |:lockmarks|, |:noautocmd|, |:noswapfile|
+ |:rightbelow|, |:sandbox|, |:silent|, |:tab|, |:topleft|,
+ |:unsilent|, |:verbose|, and |:vertical|.
Note that |:filter| is not supported.
Examples: >
command! -nargs=+ -complete=file MyEdit
@@ -1660,7 +1698,8 @@ The valid escape sequences are
If the first two characters of an escape sequence are "q-" (for example,
<q-args>) then the value is quoted in such a way as to make it a valid value
for use in an expression. This uses the argument as one single value.
-When there is no argument <q-args> is an empty string.
+When there is no argument <q-args> is an empty string. See the
+|q-args-example| below.
*<f-args>*
To allow commands to pass their arguments on to a user-defined function, there
is a special form <f-args> ("function args"). This splits the command
@@ -1670,10 +1709,10 @@ See the Mycmd example below. If no arguments are given <f-args> is removed.
To embed whitespace into an argument of <f-args>, prepend a backslash.
<f-args> replaces every pair of backslashes (\\) with one backslash. A
backslash followed by a character other than white space or a backslash
-remains unmodified. Overview:
+remains unmodified. Also see |f-args-example| below. Overview:
command <f-args> ~
- XX ab 'ab'
+ XX ab "ab"
XX a\b 'a\b'
XX a\ b 'a b'
XX a\ b 'a ', 'b'
@@ -1684,7 +1723,8 @@ remains unmodified. Overview:
XX a\\\\b 'a\\b'
XX a\\\\ b 'a\\', 'b'
-Examples >
+
+Examples for user commands: >
" Delete everything after here to the end
:com Ddel +,$d
@@ -1700,7 +1740,8 @@ Examples >
" Count the number of lines in the range
:com! -range -nargs=0 Lines echo <line2> - <line1> + 1 "lines"
- " Call a user function (example of <f-args>)
+< *f-args-example*
+Call a user function (example of <f-args>) >
:com -nargs=* Mycmd call Myfunc(<f-args>)
When executed as: >
@@ -1708,7 +1749,8 @@ When executed as: >
This will invoke: >
:call Myfunc("arg1","arg2")
- :" A more substantial example
+< *q-args-example*
+A more substantial example: >
:function Allargs(command)
: let i = 0
: while i < argc()
diff --git a/runtime/doc/mbyte.txt b/runtime/doc/mbyte.txt
index 2aa49cee1e..99dfa54218 100644
--- a/runtime/doc/mbyte.txt
+++ b/runtime/doc/mbyte.txt
@@ -86,9 +86,8 @@ You can also set 'guifont' alone, the Nvim GUI will try to find a matching
INPUT
There are several ways to enter multibyte characters:
-- For X11 XIM can be used. See |XIM|.
-- For MS-Windows IME can be used. See |IME|.
-- For all systems keymaps can be used. See |mbyte-keymap|.
+- Your system IME can be used.
+- Keymaps can be used. See |mbyte-keymap|.
The options 'iminsert', 'imsearch' and 'imcmdline' can be used to choose
the different input methods or disable them temporarily.
@@ -335,41 +334,14 @@ Vim will automatically convert from one to another encoding in several places:
"utf-8" (requires a gettext version that supports this).
- When reading a Vim script where |:scriptencoding| is different from
"utf-8".
-Most of these require the |+iconv| feature. Conversion for reading and
-writing files may also be specified with the 'charconvert' option.
+Most of these require iconv. Conversion for reading and writing files may
+also be specified with the 'charconvert' option.
Useful utilities for converting the charset:
All: iconv
GNU iconv can convert most encodings. Unicode is used as the
intermediate encoding, which allows conversion from and to all other
- encodings. See http://www.gnu.org/directory/libiconv.html.
-
- Japanese: nkf
- Nkf is "Network Kanji code conversion Filter". One of the most unique
- facility of nkf is the guess of the input Kanji code. So, you don't
- need to know what the inputting file's |charset| is. When convert to
- EUC-JP from ISO-2022-JP or Shift_JIS, simply do the following command
- in Vim:
- :%!nkf -e
- Nkf can be found at:
- http://www.sfc.wide.ad.jp/~max/FreeBSD/ports/distfiles/nkf-1.62.tar.gz
-
- Chinese: hc
- Hc is "Hanzi Converter". Hc convert a GB file to a Big5 file, or Big5
- file to GB file. Hc can be found at:
- ftp://ftp.cuhk.hk/pub/chinese/ifcss/software/unix/convert/hc-30.tar.gz
-
- Korean: hmconv
- Hmconv is Korean code conversion utility especially for E-mail. It can
- convert between EUC-KR and ISO-2022-KR. Hmconv can be found at:
- ftp://ftp.kaist.ac.kr/pub/hangul/code/hmconv/
-
- Multilingual: lv
- Lv is a Powerful Multilingual File Viewer. And it can be worked as
- |charset| converter. Supported |charset|: ISO-2022-CN, ISO-2022-JP,
- ISO-2022-KR, EUC-CN, EUC-JP, EUC-KR, EUC-TW, UTF-7, UTF-8, ISO-8859
- series, Shift_JIS, Big5 and HZ. Lv can be found at:
- http://www.ff.iij4u.or.jp/~nrt/lv/index.html
+ encodings. See https://directory.fsf.org/wiki/Libiconv.
*mbyte-conversion*
@@ -405,17 +377,6 @@ is suitable for complex input, such as CJK.
large overhead in communication, but it provides safe synchronization with
no restrictions on applications.
- For example, there are xwnmo and kinput2 Japanese |IM-server|, both are
- FrontEnd system. Xwnmo is distributed with Wnn (see below), kinput2 can be
- found at: ftp://ftp.sra.co.jp/pub/x11/kinput2/
-
- For Chinese, there's a great XIM server named "xcin", you can input both
- Traditional and Simplified Chinese characters. And it can accept other
- locale if you make a correct input table. Xcin can be found at:
- http://cle.linux.org.tw/xcin/
- Others are scim: http://scim.freedesktop.org/ and fcitx:
- http://www.fcitx.org/
-
- Conversion Server
*conversion-server*
Some system needs additional server: conversion server. Most of Japanese
@@ -664,7 +625,7 @@ and what the keymaps are to get those characters:
glyph encoding keymap ~
Char UTF-8 cp1255 hebrew hebrewp name ~
-א 0x5d0 0xe0 t a 'alef
+א 0x5d0 0xe0 t a ´alef
ב 0x5d1 0xe1 c b bet
ג 0x5d2 0xe2 d g gimel
ד 0x5d3 0xe3 s d dalet
@@ -745,7 +706,7 @@ Char UTF-8 hebrew name
Combining forms:
ﬠ 0xfb20 X` Alternative `ayin
-ﬡ 0xfb21 X' Alternative 'alef
+ﬡ 0xfb21 X' Alternative ´alef
ﬢ 0xfb22 X-d Alternative dalet
ﬣ 0xfb23 X-h Alternative he
ﬤ 0xfb24 X-k Alternative kaf
diff --git a/runtime/doc/message.txt b/runtime/doc/message.txt
index dac4df5ee9..dffdb5950f 100644
--- a/runtime/doc/message.txt
+++ b/runtime/doc/message.txt
@@ -573,9 +573,7 @@ when using ":w"), therefore Vim requires using a ! after the command, e.g.:
VirtualBinding
Messages like this appear when starting up. This is not a Vim problem, your
-X11 configuration is wrong. You can find a hint on how to solve this here:
-http://groups.yahoo.com/group/solarisonintel/message/12179.
-[this URL is no longer valid]
+X11 configuration is wrong.
*W10* >
Warning: Changing a readonly file
@@ -603,6 +601,7 @@ probably means that some other program changed the file. You will have to
find out what happened, and decide which version of the file you want to keep.
Set the 'autoread' option if you want to do this automatically.
This message is not given when 'buftype' is not empty.
+Also see the |FileChangedShell| autocommand.
There is one situation where you get this message even though there is nothing
wrong: If you save a file in Windows on the day the daylight saving time
@@ -821,7 +820,7 @@ Type effect ~
the clipboard ("* and "+ registers)
{menu-entry} what the menu is defined to in
Cmdline-mode.
- <LeftMouse> (*) next page
+ <LeftMouse> next page (*)
Any other key causes the meaning of the keys to be displayed.
diff --git a/runtime/doc/mlang.txt b/runtime/doc/mlang.txt
index 9d3a51302d..84b8498d39 100644
--- a/runtime/doc/mlang.txt
+++ b/runtime/doc/mlang.txt
@@ -92,27 +92,10 @@ use of "-" and "_".
:lang mes en
<
-MS-WINDOWS MESSAGE TRANSLATIONS *win32-gettext*
-
-If you used the self-installing .exe file, message translations should work
-already. Otherwise get the libintl.dll file if you don't have it yet:
-
- http://sourceforge.net/projects/gettext
-Or:
- https://mlocati.github.io/gettext-iconv-windows/
-
-This also contains tools xgettext, msgformat and others.
-
-libintl.dll should be placed in same directory as (g)vim.exe, or one of the
-directories listed in the PATH environment value. Vim also looks for the
-alternate names "libintl-8.dll" and "intl.dll".
-
Message files (vim.mo) have to be placed in "$VIMRUNTIME/lang/xx/LC_MESSAGES",
-where "xx" is the abbreviation of the language (mostly two letters).
-
-If you write your own translations you need to generate the .po file and
-convert it to a .mo file. You need to get the source distribution and read
-the file "src/po/README.txt".
+where "xx" is the abbreviation of the language (mostly two letters). If you
+write your own translations you need to generate the .po file and convert it
+to a .mo file.
To overrule the automatic choice of the language, set the $LANG variable to
the language of your choice. use "en" to disable translations. >
diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt
index 511b1bd7b2..929efee19f 100644
--- a/runtime/doc/motion.txt
+++ b/runtime/doc/motion.txt
@@ -309,10 +309,10 @@ g<Down> [count] display lines downward. |exclusive| motion.
an operator, because it's not linewise.
*-*
-- <minus> [count] lines upward, on the first non-blank
+`-` <minus> [count] lines upward, on the first non-blank
character |linewise|.
-+ or *+*
+`+` or *+*
CTRL-M or *CTRL-M* *<CR>*
<CR> [count] lines downward, on the first non-blank
character |linewise|.
@@ -438,9 +438,9 @@ between Vi and Vim.
} [count] |paragraph|s forward. |exclusive| motion.
*]]*
-]] [count] |section|s forward or to the next '{' in the
+]] [count] |section|s forward or to the next "{" in the
first column. When used after an operator, then also
- stops below a '}' in the first column. |exclusive|
+ stops below a "}" in the first column. |exclusive|
Note that |exclusive-linewise| often applies.
*][*
@@ -449,12 +449,12 @@ between Vi and Vim.
Note that |exclusive-linewise| often applies.
*[[*
-[[ [count] |section|s backward or to the previous '{' in
+[[ [count] |section|s backward or to the previous "{" in
the first column. |exclusive|
Note that |exclusive-linewise| often applies.
*[]*
-[] [count] |section|s backward or to the previous '}' in
+[] [count] |section|s backward or to the previous "}" in
the first column. |exclusive|
Note that |exclusive-linewise| often applies.
@@ -831,12 +831,12 @@ deletes the lines from the cursor position to mark 't'. Hint: Use mark 't' for
Top, 'b' for Bottom, etc.. Lowercase marks are restored when using undo and
redo.
-Uppercase marks 'A to 'Z include the file name.
-You can use them to jump from file to file. You can only use an uppercase
-mark with an operator if the mark is in the current file. The line number of
-the mark remains correct, even if you insert/delete lines or edit another file
-for a moment. When the 'shada' option is not empty, uppercase marks are
-kept in the .shada file. See |shada-file-marks|.
+Uppercase marks 'A to 'Z include the file name. You can use them to jump from
+file to file. You can only use an uppercase mark with an operator if the mark
+is in the current file. The line number of the mark remains correct, even if
+you insert/delete lines or edit another file for a moment. When the 'shada'
+option is not empty, uppercase marks are kept in the .shada file. See
+|shada-file-marks|.
Numbered marks '0 to '9 are quite different. They can not be set directly.
They are only present when using a shada file |shada-file|. Basically '0
@@ -1004,8 +1004,8 @@ These commands are not marks themselves, but jump to a mark:
Note that ":keepjumps" must be used for every command.
When invoking a function the commands in that function
can still change the jumplist. Also, for
- ":keepjumps exe 'command '" the "command" won't keep
- jumps. Instead use: ":exe 'keepjumps command'"
+ `:keepjumps exe 'command '` the "command" won't keep
+ jumps. Instead use: `:exe 'keepjumps command'`
==============================================================================
8. Jumps *jump-motions*
@@ -1172,7 +1172,7 @@ g; Go to [count] older position in change list.
(not a motion command)
*g,* *E663*
-g, Go to [count] newer cursor position in change list.
+g, Go to [count] newer position in change list.
Just like |g;| but in the opposite direction.
(not a motion command)
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
new file mode 100644
index 0000000000..5c234677ef
--- /dev/null
+++ b/runtime/doc/news.txt
@@ -0,0 +1,194 @@
+*news.txt* Nvim
+
+
+ NVIM REFERENCE MANUAL
+
+
+Notable changes in Nvim 0.9 from 0.8 *news*
+
+ Type |gO| to see the table of contents.
+
+==============================================================================
+BREAKING CHANGES *news-breaking*
+
+The following changes may require adaptations in user config or plugins.
+
+• Cscope support is now removed (see |cscope| and |nvim-features-removed|):
+ - Commands removed:
+ - `:cscope`
+ - `:lcscope`
+ - `:scscope`
+ - `:cstag`
+ - Options removed:
+ - `cscopepathcomp`
+ - `cscopeprg`
+ - `cscopequickfix`
+ - `cscoperelative`
+ - `cscopetag`
+ - `cscopetagorder`
+ - `cscopeverbose`
+ - Eval functions removed:
+ - `cscope_connection()`
+
+ Note: support for |ctags| remains with no plans to remove.
+
+ See https://github.com/neovim/neovim/pull/20545 for more information.
+
+• `:hardcopy` is now removed (see |hardcopy| and |nvim-features-removed|):
+ - Commands removed:
+ - `:hardcopy`
+ - Options removed:
+ - `printdevice`
+ - `printencoding`
+ - `printexpr`
+ - `printfont`
+ - `printheader`
+ - `printmbcharset`
+
+• libiconv is now a required build dependency.
+
+==============================================================================
+NEW FEATURES *news-features*
+
+The following new APIs or features were added.
+
+• |nvim_open_win()| now accepts a relative `mouse` option to open a floating win
+ relative to the mouse. Note that the mouse doesn't update frequently without
+ setting `vim.o.mousemoveevent = true`
+
+• EditorConfig support is now builtin. This is enabled by default and happens
+ automatically. To disable it, users should add >lua
+
+ vim.g.editorconfig = false
+<
+ (or the Vimscript equivalent) to their |config| file.
+
+• Run Lua scripts from your shell using |-l|. >
+ nvim -l foo.lua --arg1 --arg2
+< Also works with stdin: >
+ echo "print(42)" | nvim -l -
+
+• Added a |vim.lsp.codelens.clear()| function to clear codelenses.
+
+• |vim.inspect_pos()|, |vim.show_pos()| and |:Inspect| allow a user to get or show items
+ at a given buffer position. Currently this includes treesitter captures,
+ semantic tokens, syntax groups and extmarks.
+
+• Added support for semantic token highlighting to the LSP client. This
+ functionality is enabled by default when a client that supports this feature
+ is attached to a buffer. Opt-out can be performed by deleting the
+ `semanticTokensProvider` from the LSP client's {server_capabilities} in the
+ `LspAttach` callback.
+
+ See |lsp-semantic_tokens| for more information.
+
+• |vim.treesitter.show_tree()| opens a split window showing a text
+ representation of the nodes in a language tree for the current buffer.
+
+• Added support for the `willSave` and `willSaveWaitUntil` capabilities to the
+ LSP client. `willSaveWaitUntil` allows a server to modify a document before it
+ gets saved. Example use-cases by language servers include removing unused
+ imports, or formatting the file.
+
+• Treesitter syntax highlighting for `help` files now supports highlighted
+ code examples. To enable, create a `.config/nvim/ftplugin/help.lua` with
+ the contents >lua
+ vim.treesitter.start()
+<
+ Note: Highlighted code examples are only available in the Nvim manual, not
+ in help files taken from Vim. The treesitter `help` parser is also work in
+ progress and not guaranteed to correctly highlight every help file in the
+ wild.
+
+• |vim.secure.trust()|, |:trust| allows the user to manage files in trust
+ database.
+
+• |vim.diagnostic.open_float()| (and therefore |vim.diagnostic.config()|) now
+ accepts a `suffix` option which, by default, renders LSP error codes.
+ Similarly, the `virtual_text` configuration in |vim.diagnostic.config()| now
+ has a `suffix` option which does nothing by default.
+
+• |vim.fs.dir()| now has a `opts` argument with a depth field to allow
+ recursively searching a directory tree.
+
+• |vim.secure.read()| reads a file and prompts the user if it should be
+ trusted and, if so, returns the file's contents.
+
+• When using Nvim inside tmux 3.2 or later, the default clipboard provider
+ will now copy to the system clipboard. |provider-clipboard|
+
+• |'showcmdloc'| option to display the 'showcmd' information in the
+ status line or tab line. A new %S statusline item is available to place
+ the 'showcmd' text in a custom 'statusline'. Useful for when |'cmdheight'|
+ is set to 0.
+
+• |'splitkeep'| option to control the scroll behavior of horizontal splits.
+
+• |'statuscolumn'| option to customize the area to the side of a window,
+ normally containing the fold, sign and number columns. This new option follows
+ the 'statusline' syntax and can be used to transform the line numbers, create
+ mouse click callbacks for |signs|, introduce a custom margin or separator etc.
+
+• |nvim_select_popupmenu_item()| now supports |cmdline-completion| popup menu.
+
+• |'diffopt'| now includes a `linematch` option to enable a second-stage diff
+ on individual hunks to provide much more accurate diffs. This option is also
+ available to |vim.diff()|
+
+ See https://github.com/neovim/neovim/pull/14537.
+
+• |vim.diagnostic.is_disabled()| checks if diagnostics are disabled in a given
+ buffer or namespace.
+
+• |--remote-ui| option was added to connect to a remote instance and display
+ in it in a |TUI| in the local terminal. This can be used run a headless nvim
+ instance in the background and display its UI on demand, which previously
+ only was possible using an external UI implementation.
+
+• Several improvements were made to make the code generation scripts more
+ deterministic, and a `LUA_GEN_PRG` build parameter has been introduced to
+ allow for a workaround for some remaining reproducibility problems.
+
+• |:highlight| now supports an additional attribute "altfont".
+
+==============================================================================
+CHANGED FEATURES *news-changes*
+
+The following changes to existing APIs or features add new behavior.
+
+• 'exrc' now supports `.nvim.lua` file.
+• 'exrc' is no longer marked deprecated.
+
+• The |TUI| is changed to run in a separate process (previously, a separate
+ thread was used). This is not supposed to be a visible change to the user,
+ but might be the cause of subtle changes of behavior and bugs.
+
+ Previously, the TUI could be disabled as a build time feature (+tui/-tui),
+ resulting in a nvim binary which only could be run headless or embedded
+ in an external process. As of this version, TUI is always available.
+
+• API calls now show more information about where an exception happened.
+
+==============================================================================
+REMOVED FEATURES *news-removed*
+
+The following deprecated functions or APIs were removed.
+
+• It is no longer possible to scroll the whole screen when showing messages
+ longer than 'cmdheight'. |msgsep| is now always enabled even if 'display'
+ doesn't contain the "msgsep" flag.
+
+• `filetype.vim` is removed in favor of |lua-filetype|
+ (Note that filetype logic and tests still align with Vim, so additions or
+ changes need to be contributed there first.)
+ See https://github.com/neovim/neovim/pull/20674.
+
+==============================================================================
+DEPRECATIONS *news-deprecations*
+
+The following functions are now deprecated and will be removed in the next
+release.
+
+
+
+ vim:tw=78:ts=8:sw=2:et:ft=help:norl:
diff --git a/runtime/doc/nvim.txt b/runtime/doc/nvim.txt
index 513d27ccad..ef407922da 100644
--- a/runtime/doc/nvim.txt
+++ b/runtime/doc/nvim.txt
@@ -1,33 +1,33 @@
*nvim.txt* Nvim
- NVIM REFERENCE MANUAL
+ NVIM REFERENCE MANUAL
-Nvim *nvim* *nvim-intro*
+Nvim *nvim* *nvim-intro*
Nvim is based on Vim by Bram Moolenaar.
If you already use Vim see |nvim-from-vim| for a quickstart.
-If you are new to Vim, try the 30-minute tutorial: >
+If you are new to Vim, try the 30-minute tutorial: >vim
:Tutor<Enter>
Nvim is emphatically a fork of Vim, not a clone: compatibility with Vim
-(especially editor and VimL features) is maintained where possible. See
+(especially editor and Vimscript features) is maintained where possible. See
|vim-differences| for the complete reference of differences from Vim.
- Type |gO| to see the table of contents.
+ Type |gO| to see the table of contents.
==============================================================================
-Transitioning from Vim *nvim-from-vim*
+Transitioning from Vim *nvim-from-vim*
-1. To start the transition, create your |init.vim| (user config) file: >
+1. To start the transition, create your |init.vim| (user config) file: >vim
- :call mkdir(stdpath('config'), 'p')
:exe 'edit '.stdpath('config').'/init.vim'
+ :write ++p
-2. Add these contents to the file: >
+2. Add these contents to the file: >vim
set runtimepath^=~/.vim runtimepath+=~/.vim/after
let &packpath = &runtimepath
@@ -38,32 +38,37 @@ Transitioning from Vim *nvim-from-vim*
See |provider-python| and |provider-clipboard| for additional software you
might need to use some features.
-Your Vim configuration might not be entirely Nvim-compatible.
-See |vim-differences| for the full list of changes.
-
-The |'ttymouse'| option, for example, was removed from Nvim (mouse support
-should work without it). If you use the same |vimrc| for Vim and Nvim,
-consider guarding |'ttymouse'| in your configuration like so:
->
+Your Vim configuration might not be entirely Nvim-compatible (see
+|vim-differences|). For example the |'ttymouse'| option was removed from Nvim,
+because mouse support is always enabled if possible. If you use the same
+|vimrc| for Vim and Nvim you could guard |'ttymouse'| in your configuration
+like so:
+>vim
if !has('nvim')
set ttymouse=xterm2
endif
-<
-Conversely, if you have Nvim specific configuration items, you could do
-this:
->
+
+And for Nvim-specific configuration, you can do this:
+>vim
if has('nvim')
tnoremap <Esc> <C-\><C-n>
endif
-<
+
For a more granular approach use |exists()|:
->
+>vim
if exists(':tnoremap')
tnoremap <Esc> <C-\><C-n>
endif
-<
+
Now you should be able to explore Nvim more comfortably. Check |nvim-features|
for more information.
+ *portable-config*
+Because Nvim follows the XDG |base-directories| standard, configuration on
+Windows is stored in ~/AppData instead of ~/.config. But you can still share
+the same Nvim configuration on all of your machines, by creating
+~/AppData/Local/nvim/init.vim containing just this line: >vim
+ source ~/.config/nvim/init.vim
+
==============================================================================
- vim:tw=78:ts=8:noet:ft=help:norl:
+ vim:tw=78:ts=8:et:ft=help:norl:
diff --git a/runtime/doc/nvim_terminal_emulator.txt b/runtime/doc/nvim_terminal_emulator.txt
index 546f92e92f..96f99528ed 100644
--- a/runtime/doc/nvim_terminal_emulator.txt
+++ b/runtime/doc/nvim_terminal_emulator.txt
@@ -27,12 +27,12 @@ There are several ways to create a terminal buffer:
- Run the |:terminal| command.
- Call the |nvim_open_term()| or |termopen()| function.
-- Edit a "term://" buffer. Examples: >
+- Edit a "term://" buffer. Examples: >vim
:edit term://bash
:vsplit term://top
< Note: To open a "term://" buffer from an autocmd, the |autocmd-nested|
- modifier is required. >
+ modifier is required. >vim
autocmd VimEnter * ++nested split term://sh
< (This is only mentioned for reference; use |:terminal| instead.)
@@ -62,13 +62,13 @@ Terminal-mode forces these local options:
Terminal-mode has its own |:tnoremap| namespace for mappings, this can be used
to automate any terminal interaction.
-To map <Esc> to exit terminal-mode: >
+To map <Esc> to exit terminal-mode: >vim
:tnoremap <Esc> <C-\><C-n>
-To simulate |i_CTRL-R| in terminal-mode: >
+To simulate |i_CTRL-R| in terminal-mode: >vim
:tnoremap <expr> <C-R> '<C-\><C-N>"'.nr2char(getchar()).'pi'
-To use `ALT+{h,j,k,l}` to navigate windows from any mode: >
+To use `ALT+{h,j,k,l}` to navigate windows from any mode: >vim
:tnoremap <A-h> <C-\><C-N><C-w>h
:tnoremap <A-j> <C-\><C-N><C-w>j
:tnoremap <A-k> <C-\><C-N><C-w>k
@@ -109,7 +109,7 @@ global configuration.
- 'list' is disabled
- 'wrap' is disabled
-You can change the defaults with a TermOpen autocommand: >
+You can change the defaults with a TermOpen autocommand: >vim
au TermOpen * setlocal list
TERMINAL COLORS ~
@@ -117,7 +117,7 @@ TERMINAL COLORS ~
The `{g,b}:terminal_color_x` variables control the terminal color palette,
where `x` is the color index between 0 and 255 inclusive. The variables are
read during |TermOpen|. The value must be a color name or hexadecimal string.
-Example: >
+Example: >vim
let g:terminal_color_4 = '#ff0000'
let g:terminal_color_5 = 'green'
Only works for RGB UIs (see 'termguicolors'); for 256-color terminals the
@@ -131,7 +131,7 @@ Status Variables *terminal-status*
Terminal buffers maintain some buffer-local variables and options. The values
are initialized before TermOpen, so you can use them in a local 'statusline'.
-Example: >
+Example: >vim
:autocmd TermOpen * setlocal statusline=%{b:term_title}
- *b:term_title* Terminal title (user-writable), typically displayed in the
@@ -141,10 +141,10 @@ Example: >
input to the terminal.
- The |TermClose| event gives the terminal job exit code in the |v:event|
"status" field. For example, this autocmd closes terminal buffers if the job
- exited without error: >
+ exited without error: >vim
autocmd TermClose * if !v:event.status | exe 'bdelete! '..expand('<abuf>') | endif
-Use |jobwait()| to check if the terminal job has finished: >
+Use |jobwait()| to check if the terminal job has finished: >vim
let running = jobwait([&channel], 0)[0] == -1
==============================================================================
@@ -156,11 +156,11 @@ Vim this also works remotely over an ssh connection.
Starting ~
*termdebug-starting*
-Load the plugin with this command: >
+Load the plugin with this command: >vim
packadd termdebug
< *:Termdebug*
To start debugging use `:Termdebug` or `:TermdebugCommand` followed by the
-command name, for example: >
+command name, for example: >vim
:Termdebug vim
This opens two windows:
@@ -189,16 +189,16 @@ Only one debugger can be active at a time.
*:TermdebugCommand*
If you want to give specific commands to the command being debugged, you can
use the `:TermdebugCommand` command followed by the command name and
-additional parameters. >
+additional parameters. >vim
:TermdebugCommand vim --clean -c ':set nu'
Both the `:Termdebug` and `:TermdebugCommand` support an optional "!" bang
argument to start the command right away, without pausing at the gdb window
-(and cursor will be in the debugged window). For example: >
+(and cursor will be in the debugged window). For example: >vim
:TermdebugCommand! vim --clean
To attach gdb to an already running executable or use a core file, pass extra
-arguments. E.g.: >
+arguments. E.g.: >vim
:Termdebug vim core
:Termdebug vim 98343
@@ -212,7 +212,7 @@ Start in the Vim "src" directory and build Vim: >
% make
Start Vim: >
% ./vim
-Load the termdebug plugin and start debugging Vim: >
+Load the termdebug plugin and start debugging Vim: >vim
:packadd termdebug
:Termdebug vim
You should now have three windows:
@@ -223,7 +223,7 @@ You should now have three windows:
Put focus on the gdb window and type: >
break ex_help
run
-Vim will start running in the program window. Put focus there and type: >
+Vim will start running in the program window. Put focus there and type: >vim
:help gui
Gdb will run into the ex_help breakpoint. The source window now shows the
ex_cmds.c file. A red "1 " marker will appear in the signcolumn where the
@@ -329,7 +329,7 @@ Other commands ~
Events ~
*termdebug-events*
-Four autocommands can be used: >
+Four autocommands can be used: >vim
au User TermdebugStartPre echomsg 'debugging starting'
au User TermdebugStartPost echomsg 'debugging started'
au User TermdebugStopPre echomsg 'debugging stopping'
@@ -355,6 +355,20 @@ TermdebugStopPost After debugging has ended, gdb-related windows
the state before the debugging was restored.
+Customizing ~
+ *termdebug-customizing* *g:termdebug_config*
+In the past several global variables were used for configuration. These are
+deprecated and using the g:termdebug_config dictionary is preferred. When
+g:termdebug_config exists the other global variables will NOT be used.
+The recommended way is to start with an empty dictionary: >vim
+ let g:termdebug_config = {}
+
+Then you can add entries to the dictionary as mentioned below. The
+deprecated global variable names are mentioned for completeness. If you are
+switching over to using g:termdebug_config you can find the old variable name
+and take over the value, then delete the deprecated variable.
+
+
Prompt mode ~
*termdebug-prompt*
When on MS-Windows, gdb will run in a buffer with 'buftype' set to "prompt".
@@ -366,23 +380,23 @@ This works slightly differently:
- A separate :terminal window will be opened to run the debugged program in.
*termdebug_use_prompt*
-Prompt mode can be used with: >
+Prompt mode can be used with: >vim
let g:termdebug_config['use_prompt'] = 1
-Or if there is no g:termdebug_config: >
+If there is no g:termdebug_config you can use: >vim
let g:termdebug_use_prompt = 1
<
*termdebug_map_K*
-The K key is normally mapped to :Evaluate. If you do not want this use: >
+The K key is normally mapped to :Evaluate. If you do not want this use: >vim
let g:termdebug_config['map_K'] = 0
-Or if there is no g:termdebug_config: >
+If there is no g:termdebug_config you can use: >vim
let g:termdebug_map_K = 0
<
*termdebug_disasm_window*
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: >
+the "disasm_window_height" entry can be used to set the window height: >vim
let g:termdebug_config['disasm_window'] = 1
let g:termdebug_config['disasm_window_height'] = 15
-or, if there is no g:termdebug_config: >
+If there is no g:termdebug_config you can use: >vim
let g:termdebug_disasm_window = 15
Any value greater than 1 will set the Asm window height to that value.
@@ -400,45 +414,38 @@ interrupt the running program. But after using the MI command
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 ~
*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`: >
+`:Termdebug`: >vim
let g:termdebug_config['command'] = "mygdb"
-Or if there is no g:termdebug_config: >
+If there is no g:termdebug_config you can use: >vim
let g:termdebugger = "mygdb"
-If the command needs an argument use a List: >
+If the command needs an argument use a List: >vim
let g:termdebug_config['command'] = ['rr', 'replay', '--']
-Or if there is no g:termdebug_config: >
+If there is no g:termdebug_config you can use: >vim
let g:termdebugger = ['rr', 'replay', '--']
To not use neovim floating windows for previewing variable evaluation, set the
-`g:termdebug_useFloatingHover` variable like this: >
+`g:termdebug_useFloatingHover` variable like this: >vim
let g:termdebug_useFloatingHover = 0
If you are a mouse person, you can also define a mapping using your right
click to one of the terminal command like evaluate the variable under the
-cursor: >
+cursor: >vim
nnoremap <RightMouse> :Evaluate<CR>
-or set/unset a breakpoint: >
+or set/unset a breakpoint: >vim
nnoremap <RightMouse> :Break<CR>
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: >
+If you want to modify them, add a function to filter the argument list: >vim
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: >
+"pty", use a function to add the necessary arguments: >vim
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.
@@ -451,7 +458,7 @@ Then your gdb is too old.
Colors ~
- *hl-debugPC* *hl-debugBreakpoint*
+ *hl-debugPC* *hl-debugBreakpoint*
The color of the signs can be adjusted with these highlight groups:
- debugPC the current position
- debugBreakpoint a breakpoint
@@ -467,34 +474,31 @@ When 'background' is "dark":
Shortcuts ~
*termdebug_shortcuts*
-
You can define your own shortcuts (mappings) to control gdb, that can work in
-any window, using the TermDebugSendCommand() function. Example: >
+any window, using the TermDebugSendCommand() function. Example: >vim
map ,w :call TermDebugSendCommand('where')<CR>
The argument is the gdb command.
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: >
+If you don't want this then disable it with: >vim
let g:termdebug_config['popup'] = 0
-or if there is no g:termdebug_config: >
+If there is no g:termdebug_config you can use: >vim
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: >
+split: >vim
let g:termdebug_config['wide'] = 163
-Or if there is no g:termdebug_config: >
+If there is no g:termdebug_config you can use: >vim
let g:termdebug_wide = 163
This will set 'columns' to 163 when `:Termdebug` is used. The value is
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 2e0c1f8cc4..b1af90a604 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -145,7 +145,7 @@ This sets the 'titlestring' option to "hi" and 'iconstring' to "there": >
Similarly, the double quote character starts a comment. To include the '"' in
the option value, use '\"' instead. This example sets the 'titlestring'
-option to 'hi "there"': >
+option to "hi "there"": >
:set titlestring=hi\ \"there\"
For Win32 backslashes in file names are mostly not removed. More precise: For
@@ -163,7 +163,7 @@ halved and when you expect the backslashes to be kept. The third gives a
result which is probably not what you want. Avoid it.
*add-option-flags* *remove-option-flags*
- *E539* *E550* *E551* *E552*
+ *E539*
Some options are a list of flags. When you want to add a flag to such an
option, without changing the existing ones, you can do it like this: >
:set guioptions+=a
@@ -313,14 +313,23 @@ Note: In the future more global options can be made |global-local|. Using
*option-value-function*
-Some options ('completefunc', 'imactivatefunc', 'imstatusfunc', 'omnifunc',
-'operatorfunc', 'quickfixtextfunc' and 'tagfunc') are set to a function name
-or a function reference or a lambda function. Examples:
+Some options ('completefunc', 'omnifunc', 'operatorfunc', 'quickfixtextfunc',
+'tagfunc' and 'thesaurusfunc') are set to a function name or a function
+reference or a lambda function. When using a lambda it will be converted to
+the name, e.g. "<lambda>123". Examples:
>
set opfunc=MyOpFunc
- set opfunc=function("MyOpFunc")
- set opfunc=funcref("MyOpFunc")
- set opfunc={t\ ->\ MyOpFunc(t)}
+ set opfunc=function('MyOpFunc')
+ set opfunc=funcref('MyOpFunc')
+ set opfunc={a\ ->\ MyOpFunc(a)}
+ " set using a funcref variable
+ let Fn = function('MyTagFunc')
+ let &tagfunc = Fn
+ " set using a lambda expression
+ let &tagfunc = {t -> MyTagFunc(t)}
+ " set using a variable with lambda expression
+ let L = {a, b, c -> MyTagFunc(a, b , c)}
+ let &tagfunc = L
<
Setting the filetype
@@ -440,9 +449,9 @@ se[t] the string "set " or "se " (note the space); When
: a colon
[text] any text or empty
-Examples:
- /* vim: set ai tw=75: */ ~
- /* Vim: set ai tw=75: */ ~
+Examples: >
+ /* vim: set ai tw=75: */
+ /* Vim: set ai tw=75: */
The white space before {vi:|vim:|Vim:|ex:} is required. This minimizes the
chance that a normal word like "lex:" is caught. There is one exception:
@@ -479,10 +488,10 @@ number can be specified where "vim:" or "Vim:" is used:
vim={vers}: version {vers}
vim>{vers}: version after {vers}
{vers} is 700 for Vim 7.0 (hundred times the major version plus minor).
-For example, to use a modeline only for Vim 7.0:
- /* vim700: set foldmethod=marker */ ~
-To use a modeline for Vim after version 7.2:
- /* vim>702: set cole=2: */ ~
+For example, to use a modeline only for Vim 7.0: >
+ /* vim700: set foldmethod=marker */
+To use a modeline for Vim after version 7.2: >
+ /* vim>702: set cole=2: */
There can be no blanks between "vim" and the ":".
The modeline is ignored if {vers} does not fit in an integer.
@@ -491,16 +500,16 @@ The number of lines that are checked can be set with the 'modelines' option.
If 'modeline' is off or 'modelines' is 0 no lines are checked.
Note that for the first form all of the rest of the line is used, thus a line
-like:
- /* vi:ts=4: */ ~
-will give an error message for the trailing "*/". This line is OK:
- /* vi:set ts=4: */ ~
+like: >
+ /* vi:ts=4: */
+will give an error message for the trailing "*/". This line is OK: >
+ /* vi:set ts=4: */
If an error is detected the rest of the line is skipped.
If you want to include a ':' in a set command precede it with a '\'. The
-backslash in front of the ':' will be removed. Example:
- /* vi:set fillchars=stl\:^,vert\:\|: */ ~
+backslash in front of the ':' will be removed. Example: >
+ /* vi:set fillchars=stl\:^,vert\:\|: */
This sets the 'fillchars' option to "stl:^,vert:\|". Only a single backslash
before the ':' is removed. Thus to include "\:" you have to specify "\\:".
*E992*
@@ -621,7 +630,7 @@ A jump table for the options with a short description can be found at |Q_op|.
by Vim with the width of glyphs in the font. Perhaps it also has
to be set to "double" under CJK MS-Windows when the system locale is
set to one of CJK locales. See Unicode Standard Annex #11
- (http://www.unicode.org/reports/tr11).
+ (https://www.unicode.org/reports/tr11).
*'autochdir'* *'acd'* *'noautochdir'* *'noacd'*
'autochdir' 'acd' boolean (default off)
@@ -1053,19 +1062,26 @@ A jump table for the options with a short description can be found at |Q_op|.
text should normally be narrower. This prevents
text indented almost to the right window border
occupying lot of vertical space when broken.
+ (default: 20)
shift:{n} After applying 'breakindent', the wrapped line's
beginning will be shifted by the given number of
characters. It permits dynamic French paragraph
indentation (negative) or emphasizing the line
continuation (positive).
+ (default: 0)
sbr Display the 'showbreak' value before applying the
additional indent.
+ (default: off)
list:{n} Adds an additional indent for lines that match a
numbered or bulleted list (using the
'formatlistpat' setting).
list:-1 Uses the length of a match with 'formatlistpat'
for indentation.
- The default value for min is 20, shift and list is 0.
+ (default: 0)
+ column:{n} Indent at column {n}. Will overrule the other
+ sub-options. Note: an additional indent may be
+ added for the 'showbreak' setting.
+ (default: off)
*'browsedir'* *'bsdir'*
'browsedir' 'bsdir' string (default: "last")
@@ -1309,7 +1325,7 @@ A jump table for the options with a short description can be found at |Q_op|.
These names are recognized:
*clipboard-unnamed*
- unnamed When included, Vim will use the clipboard register '*'
+ unnamed When included, Vim will use the clipboard register "*"
for all yank, delete, change and put operations which
would normally go to the unnamed register. When a
register is explicitly specified, it will always be
@@ -1320,8 +1336,8 @@ A jump table for the options with a short description can be found at |Q_op|.
*clipboard-unnamedplus*
unnamedplus A variant of the "unnamed" flag which uses the
- clipboard register '+' (|quoteplus|) instead of
- register '*' for all yank, delete, change and put
+ clipboard register "+" (|quoteplus|) instead of
+ register "*" for all yank, delete, change and put
operations which would normally go to the unnamed
register. When "unnamed" is also included to the
option, yank and delete operations (but not put)
@@ -1337,9 +1353,14 @@ A jump table for the options with a short description can be found at |Q_op|.
page can have a different value.
When 'cmdheight' is zero, there is no command-line unless it is being
- used. Some informative messages will not be displayed, any other
- messages will cause the |hit-enter| prompt. Expect some other
- unexpected behavior too.
+ used. The command-line will cover the last line of the screen when
+ shown.
+
+ WARNING: `cmdheight=0` is considered experimental. Expect some
+ unwanted behaviour. Some 'shortmess' flags and similar
+ mechanism might fail to take effect, causing unwanted hit-enter
+ prompts. Some informative messages, both from Nvim itself and
+ plugins, will not be displayed.
*'cmdwinheight'* *'cwh'*
'cmdwinheight' 'cwh' number (default 7)
@@ -1434,7 +1455,9 @@ A jump table for the options with a short description can be found at |Q_op|.
This option specifies a function to be used for Insert mode completion
with CTRL-X CTRL-U. |i_CTRL-X_CTRL-U|
See |complete-functions| for an explanation of how the function is
- invoked and what it should return.
+ invoked and what it should return. The value can be the name of a
+ function, a |lambda| or a |Funcref|. See |option-value-function| for
+ more information.
This option cannot be set from a |modeline| or in the |sandbox|, for
security reasons.
@@ -1787,43 +1810,6 @@ A jump table for the options with a short description can be found at |Q_op|.
_ When using |cw| on a word, do not include the
whitespace following the word in the motion.
- *'cscopepathcomp'* *'cspc'*
-'cscopepathcomp' 'cspc' number (default 0)
- global
- Determines how many components of the path to show in a list of tags.
- See |cscopepathcomp|.
-
- *'cscopeprg'* *'csprg'*
-'cscopeprg' 'csprg' string (default "cscope")
- global
- Specifies the command to execute cscope. See |cscopeprg|.
- This option cannot be set from a |modeline| or in the |sandbox|, for
- security reasons.
-
- *'cscopequickfix'* *'csqf'*
-'cscopequickfix' 'csqf' string (default "")
- global
- Specifies whether to use quickfix window to show cscope results.
- See |cscopequickfix|.
-
- *'cscoperelative'* *'csre'* *'nocscoperelative'* *'nocsre'*
-'cscoperelative' 'csre' boolean (default off)
- global
- In the absence of a prefix (-P) for cscope. setting this option enables
- to use the basename of cscope.out path as the prefix.
- See |cscoperelative|.
-
- *'cscopetag'* *'cst'* *'nocscopetag'* *'nocst'*
-'cscopetag' 'cst' boolean (default off)
- global
- Use cscope for tag commands. See |cscope-options|.
-
- *'cscopetagorder'* *'csto'*
-'cscopetagorder' 'csto' number (default 0)
- global
- Determines the order in which ":cstag" performs a search. See
- |cscopetagorder|.
-
*'cursorbind'* *'crb'* *'nocursorbind'* *'nocrb'*
'cursorbind' 'crb' boolean (default off)
local to window
@@ -1904,9 +1890,9 @@ A jump table for the options with a short description can be found at |Q_op|.
^\(#\s*define\|[a-z]*\s*const\s*[a-z]*\)
< You can also use "\ze" just before the name and continue the pattern
to check what is following. E.g. for Javascript, if a function is
- defined with "func_name = function(args)": >
+ defined with `func_name = function(args)`: >
^\s*\ze\i\+\s*=\s*function(
-< If the function is defined with "func_name : function() {...": >
+< If the function is defined with `func_name : function() {...`: >
^\s*\ze\i\+\s*[:]\s*(*function\s*(
< When using the ":set" command, you need to double the backslashes!
To avoid that use `:let` with a single quote string: >
@@ -2049,6 +2035,16 @@ A jump table for the options with a short description can be found at |Q_op|.
Use the indent heuristic for the internal
diff library.
+ linematch:{n} Enable a second stage diff on each generated
+ hunk in order to align lines. When the total
+ number of lines in a hunk exceeds {n}, the
+ second stage diff will not be performed as
+ very large hunks can cause noticeable lag. A
+ recommended setting is "linematch:60", as this
+ will enable alignment for a 2 buffer diff with
+ hunks of up to 30 lines each, or a 3 buffer
+ diff with hunks of up to 20 lines each.
+
algorithm:{text} Use the specified diff algorithm with the
internal diff engine. Currently supported
algorithms are:
@@ -2123,9 +2119,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")
+'display' 'dy' string (default "lastline")
global
- Change the way text is displayed. This is comma-separated list of
+ Change the way text is displayed. This is a 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
@@ -2135,14 +2131,14 @@ A jump table for the options with a short description can be found at |Q_op|.
column of the last screen line. Overrules "lastline".
uhex Show unprintable characters hexadecimal as <xx>
instead of using ^C and ~C.
- msgsep When showing messages longer than 'cmdheight', only
- scroll the message lines, not the entire screen. The
- separator line is decorated by |hl-MsgSeparator| and
- the "msgsep" flag of 'fillchars'.
+ msgsep Obsolete flag. Allowed but takes no effect. |msgsep|
When neither "lastline" nor "truncate" is included, a last line that
doesn't fit is replaced with "@" lines.
+ The "@" character can be changed by setting the "lastline" item in
+ 'fillchars'. The character is highlighted with |hl-NonText|.
+
*'eadirection'* *'ead'*
'eadirection' 'ead' string (default "both")
global
@@ -2167,6 +2163,16 @@ A jump table for the options with a short description can be found at |Q_op|.
See 'fileencoding' to control file-content encoding.
+ *'endoffile'* *'eof'* *'noendoffile'* *'noeof'*
+'endoffile' 'eof' boolean (default off)
+ local to buffer
+ Indicates that a CTRL-Z character was found at the end of the file
+ when reading it. Normally only happens when 'fileformat' is "dos".
+ When writing a file and this option is off and the 'binary' option
+ is on, or 'fixeol' option is off, no CTRL-Z will be written at the
+ end of the file.
+ See |eol-and-eof| for example settings.
+
*'endofline'* *'eol'* *'noendofline'* *'noeol'*
'endofline' 'eol' boolean (default on)
local to buffer
@@ -2181,6 +2187,7 @@ A jump table for the options with a short description can be found at |Q_op|.
to remember the presence of a <EOL> for the last line in the file, so
that when you write the file the situation from the original file can
be kept. But you can change it if you want to.
+ See |eol-and-eof| for example settings.
*'equalalways'* *'ea'* *'noequalalways'* *'noea'*
'equalalways' 'ea' boolean (default on)
@@ -2257,6 +2264,22 @@ A jump table for the options with a short description can be found at |Q_op|.
This option is reset when the 'paste' option is set and restored when
the 'paste' option is reset.
+ *'exrc'* *'ex'* *'noexrc'* *'noex'*
+'exrc' 'ex' boolean (default off)
+ global
+ Enables the reading of .nvim.lua, .nvimrc, and .exrc files in the current
+ directory.
+
+ The file is only sourced if the user indicates the file is trusted. If
+ it is, the SHA256 hash of the file contents and the full path of the
+ file are persisted to a trust database. The user is only prompted
+ again if the file contents change. See |vim.secure.read()|.
+
+ Use |:trust| to manage the trusted file database.
+
+ This option cannot be set from a |modeline| or in the |sandbox|, for
+ security reasons.
+
*'fileencoding'* *'fenc'* *E213*
'fileencoding' 'fenc' string (default: "")
local to buffer
@@ -2438,13 +2461,13 @@ A jump table for the options with a short description can be found at |Q_op|.
this use the ":filetype on" command. |:filetype|
Setting this option to a different value is most useful in a modeline,
for a file for which the file type is not automatically recognized.
- Example, for in an IDL file:
- /* vim: set filetype=idl : */ ~
- |FileType| |filetypes|
+ Example, for in an IDL file: >
+ /* vim: set filetype=idl : */
+< |FileType| |filetypes|
When a dot appears in the value then this separates two filetype
- names. Example:
- /* vim: set filetype=c.doxygen : */ ~
- This will use the "c" filetype first, then the "doxygen" filetype.
+ names. Example: >
+ /* vim: set filetype=c.doxygen : */
+< This will use the "c" filetype first, then the "doxygen" filetype.
This works both for filetype plugins and for syntax files. More than
one dot may appear.
This option is not copied to another buffer, independent of the 's' or
@@ -2454,28 +2477,31 @@ A jump table for the options with a short description can be found at |Q_op|.
*'fillchars'* *'fcs'*
'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:
+ Characters to fill the statuslines, vertical separators and special
+ lines in the window.
+ It is a comma-separated list of items. Each item has a name, a colon
+ and the value of that item:
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
+ stl ' ' or '^' statusline of the current window
+ stlnc ' ' or '=' statusline of the non-current windows
+ wbr ' ' window bar
+ horiz '─' or '-' horizontal separators |:split|
+ horizup '┴' or '-' upwards facing horizontal separator
+ horizdown '┬' or '-' downwards facing horizontal separator
+ vert '│' or '|' vertical separators |:vsplit|
+ vertleft '┤' or '|' left facing vertical separator
+ vertright '├' or '|' right facing vertical separator
+ verthoriz '┼' 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
- foldsep:c '│' or '|' open fold middle marker
- diff:c '-' deleted lines of the 'diff' option
- msgsep:c ' ' message separator 'display'
- eob:c '~' empty lines at the end of a buffer
+ fold '·' or '-' filling 'foldtext'
+ foldopen '-' mark the beginning of a fold
+ foldclose '+' show a closed fold
+ foldsep '│' or '|' open fold middle marker
+ diff '-' deleted lines of the 'diff' option
+ msgsep ' ' message separator 'display'
+ eob '~' empty lines at the end of a buffer
+ lastline '@' 'display' contains lastline/truncate
Any one that is omitted will fall back to the default. For "stl" and
"stlnc" the space will be used when there is highlighting, '^' or '='
@@ -2500,29 +2526,31 @@ A jump table for the options with a short description can be found at |Q_op|.
The highlighting used for these items:
item highlight group ~
- stl:c StatusLine |hl-StatusLine|
- stlnc:c StatusLineNC |hl-StatusLineNC|
- 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|
+ stl StatusLine |hl-StatusLine|
+ stlnc StatusLineNC |hl-StatusLineNC|
+ wbr WinBar |hl-WinBar| or |hl-WinBarNC|
+ horiz WinSeparator |hl-WinSeparator|
+ horizup WinSeparator |hl-WinSeparator|
+ horizdown WinSeparator |hl-WinSeparator|
+ vert WinSeparator |hl-WinSeparator|
+ vertleft WinSeparator |hl-WinSeparator|
+ vertright WinSeparator |hl-WinSeparator|
+ verthoriz WinSeparator |hl-WinSeparator|
+ fold Folded |hl-Folded|
+ diff DiffDelete |hl-DiffDelete|
+ eob EndOfBuffer |hl-EndOfBuffer|
+ lastline NonText |hl-NonText|
*'fixendofline'* *'fixeol'* *'nofixendofline'* *'nofixeol'*
'fixendofline' 'fixeol' boolean (default on)
local to buffer
When writing a file and this option is on, <EOL> at the end of file
- will be restored if missing. Turn this option off if you want to
+ will be restored if missing. Turn this option off if you want to
preserve the situation from the original file.
When the 'binary' option is set the value of this option doesn't
matter.
See the 'endofline' option.
+ See |eol-and-eof| for example settings.
*'foldclose'* *'fcl'*
'foldclose' 'fcl' string (default "")
@@ -2710,6 +2738,11 @@ A jump table for the options with a short description can be found at |Q_op|.
When the expression evaluates to non-zero Vim will fall back to using
the internal format mechanism.
+ If the expression starts with s: or |<SID>|, then it is replaced with
+ the script ID (|local-function|). Example: >
+ set formatexpr=s:MyFormatExpr()
+ set formatexpr=<SID>SomeFormatExpr()
+<
The expression will be evaluated in the |sandbox| when set from a
modeline, see |sandbox-option|. That stops the option from working,
since changing the buffer text is not allowed.
@@ -3323,13 +3356,22 @@ A jump table for the options with a short description can be found at |Q_op|.
local to buffer
Expression to be used to transform the string found with the 'include'
option to a file name. Mostly useful to change "." to "/" for Java: >
- :set includeexpr=substitute(v:fname,'\\.','/','g')
+ :setlocal includeexpr=substitute(v:fname,'\\.','/','g')
< The "v:fname" variable will be set to the file name that was detected.
-
+ Note the double backslash: the `:set` command first halves them, then
+ one remains in the value, where "\." matches a dot literally. For
+ simple character replacements `tr()` avoids the need for escaping: >
+ :setlocal includeexpr=tr(v:fname,'.','/')
+<
Also used for the |gf| command if an unmodified file name can't be
found. Allows doing "gf" on the name after an 'include' statement.
Also used for |<cfile>|.
+ If the expression starts with s: or |<SID>|, then it is replaced with
+ the script ID (|local-function|). Example: >
+ set includeexpr=s:MyIncludeExpr(v:fname)
+ set includeexpr=<SID>SomeIncludeExpr(v:fname)
+<
The expression will be evaluated in the |sandbox| when set from a
modeline, see |sandbox-option|.
This option cannot be set in a modeline when 'modelineexpr' is off.
@@ -3380,11 +3422,16 @@ A jump table for the options with a short description can be found at |Q_op|.
in Insert mode as specified with the 'indentkeys' option.
When this option is not empty, it overrules the 'cindent' and
'smartindent' indenting. When 'lisp' is set, this option is
- overridden by the Lisp indentation algorithm.
+ is only used when 'lispoptions' contains "expr:1".
When 'paste' is set this option is not used for indenting.
The expression is evaluated with |v:lnum| set to the line number for
which the indent is to be computed. The cursor is also in this line
when the expression is evaluated (but it may be moved around).
+ If the expression starts with s: or |<SID>|, then it is replaced with
+ the script ID (|local-function|). Example: >
+ set indentexpr=s:MyIndentExpr()
+ set indentexpr=<SID>SomeIndentExpr()
+<
The expression must return the number of spaces worth of indent. It
can return "-1" to keep the current indent (this means 'autoindent' is
used for the indent).
@@ -3747,6 +3794,17 @@ A jump table for the options with a short description can be found at |Q_op|.
calling an external program if 'equalprg' is empty.
This option is not used when 'paste' is set.
+ *'lispoptions'* *'lop'*
+'lispoptions' 'lop' string (default "")
+ local to buffer
+ Comma-separated list of items that influence the Lisp indenting when
+ enabled with the |'lisp'| option. Currently only one item is
+ supported:
+ expr:1 use 'indentexpr' for Lisp indenting when it is set
+ expr:0 do not use 'indentexpr' for Lisp indenting (default)
+ Note that when using 'indentexpr' the `=` operator indents all the
+ lines, otherwise the first line is not indented (Vi-compatible).
+
*'lispwords'* *'lw'*
'lispwords' 'lw' string (default is very long)
global or local to buffer |global-local|
@@ -3785,21 +3843,21 @@ A jump table for the options with a short description can be found at |Q_op|.
The third character is optional.
tab:xy The 'x' is always used, then 'y' as many times as will
- fit. Thus "tab:>-" displays:
+ fit. Thus "tab:>-" displays: >
>
>-
>--
etc.
-
+<
tab:xyz The 'z' is always used, then 'x' is prepended, and
then 'y' is used as many times as will fit. Thus
- "tab:<->" displays:
+ "tab:<->" displays: >
>
<>
<->
<-->
etc.
-
+<
When "tab:" is omitted, a tab is shown as ^I.
*lcs-space*
space:c Character to show for a space. When omitted, spaces
@@ -3811,22 +3869,25 @@ A jump table for the options with a short description can be found at |Q_op|.
setting, except for single spaces. When omitted, the
"space" setting is used. For example,
`:set listchars=multispace:---+` shows ten consecutive
- spaces as:
- ---+---+-- ~
+ 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-leadmultispace*
+<
+ *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 ~
+ consecutive leading spaces as: >
+ ---+---+--XXX
+<
Where "XXX" denotes the first non-blank characters in
the line.
*lcs-trail*
@@ -3911,9 +3972,9 @@ A jump table for the options with a short description can be found at |Q_op|.
`:lgrepadd`, `:cfile`, `:cgetfile`, `:caddfile`, `:lfile`, `:lgetfile`,
and `:laddfile`.
- This would be mostly useful when you use MS-Windows. If |+iconv| is
- enabled and GNU libiconv is used, setting 'makeencoding' to "char" has
- the same effect as setting to the system locale encoding. Example: >
+ This would be mostly useful when you use MS-Windows. If iconv is
+ enabled, setting 'makeencoding' to "char" has the same effect as
+ setting to the system locale encoding. Example: >
:set makeencoding=char " system locale is used
<
*'makeprg'* *'mp'*
@@ -4065,7 +4126,8 @@ A jump table for the options with a short description can be found at |Q_op|.
checked for set commands. If 'modeline' is off or 'modelines' is zero
no lines are checked. See |modeline|.
- *'modifiable'* *'ma'* *'nomodifiable'* *'noma'* *E21*
+ *'modifiable'* *'ma'* *'nomodifiable'* *'noma'*
+ *E21*
'modifiable' 'ma' boolean (default on)
local to buffer
When off the buffer contents cannot be changed. The 'fileformat' and
@@ -4229,6 +4291,15 @@ 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.
+ *'mousemoveevent'* *'mousemev'*
+'mousemoveevent' 'mousemev' boolean (default off)
+ global
+ When on, mouse move events are delivered to the input queue and are
+ available for mapping. The default, off, avoids the mouse movement
+ overhead except when needed.
+ Warning: Setting this option can make pending mappings to be aborted
+ when the mouse is moved.
+
*'mousescroll'*
'mousescroll' string (default "ver:3,hor:6")
global
@@ -4289,7 +4360,7 @@ A jump table for the options with a short description can be found at |Q_op|.
w x updown up-down sizing arrows
w x leftright left-right sizing arrows
w x busy The system's usual busy pointer
- w x no The system's usual 'no input' pointer
+ w x no The system's usual "no input" pointer
x udsizing indicates up-down resizing
x lrsizing indicates left-right resizing
x crosshair like a big thin +
@@ -4366,12 +4437,12 @@ A jump table for the options with a short description can be found at |Q_op|.
'nonu' 'nu' 'nonu' 'nu'
'nornu' 'nornu' 'rnu' 'rnu'
-
+>
|apple | 1 apple | 2 apple | 2 apple
|pear | 2 pear | 1 pear | 1 pear
|nobody | 3 nobody | 0 nobody |3 nobody
|there | 4 there | 1 there | 1 there
-
+<
*'numberwidth'* *'nuw'*
'numberwidth' 'nuw' number (default: 4)
local to window
@@ -4392,7 +4463,9 @@ A jump table for the options with a short description can be found at |Q_op|.
This option specifies a function to be used for Insert mode omni
completion with CTRL-X CTRL-O. |i_CTRL-X_CTRL-O|
See |complete-functions| for an explanation of how the function is
- invoked and what it should return.
+ invoked and what it should return. The value can be the name of a
+ function, a |lambda| or a |Funcref|. See |option-value-function| for
+ more information.
This option is usually set by a filetype plugin:
|:filetype-plugin-on|
This option cannot be set from a |modeline| or in the |sandbox|, for
@@ -4555,7 +4628,7 @@ A jump table for the options with a short description can be found at |Q_op|.
< - A directory name may end in a ':' or '/'.
- Environment variables are expanded |:set_env|.
- When using |netrw.vim| URLs can be used. For example, adding
- "http://www.vim.org" will make ":find index.html" work.
+ "https://www.vim.org" will make ":find index.html" work.
- Search upwards and downwards in a directory tree using "*", "**" and
";". See |file-searching| for info and syntax.
- Careful with '\' characters, type two to get one in the option: >
@@ -4611,58 +4684,6 @@ A jump table for the options with a short description can be found at |Q_op|.
set. It's normally not set directly, but by using one of the commands
|:ptag|, |:pedit|, etc.
- *'printdevice'* *'pdev'*
-'printdevice' 'pdev' string (default empty)
- global
- The name of the printer to be used for |:hardcopy|.
- See |pdev-option|.
- This option cannot be set from a |modeline| or in the |sandbox|, for
- security reasons.
-
- *'printencoding'* *'penc'*
-'printencoding' 'penc' string (default empty, except for some systems)
- global
- Sets the character encoding used when printing.
- See |penc-option|.
-
- *'printexpr'* *'pexpr'*
-'printexpr' 'pexpr' string (default: see below)
- global
- Expression used to print the PostScript produced with |:hardcopy|.
- See |pexpr-option|.
- This option cannot be set from a |modeline| or in the |sandbox|, for
- security reasons.
-
- *'printfont'* *'pfn'*
-'printfont' 'pfn' string (default "courier")
- global
- The name of the font that will be used for |:hardcopy|.
- See |pfn-option|.
-
- *'printheader'* *'pheader'*
-'printheader' 'pheader' string (default "%<%f%h%m%=Page %N")
- global
- The format of the header produced in |:hardcopy| output.
- See |pheader-option|.
-
- *'printmbcharset'* *'pmbcs'*
-'printmbcharset' 'pmbcs' string (default "")
- global
- The CJK character set to be used for CJK output from |:hardcopy|.
- See |pmbcs-option|.
-
- *'printmbfont'* *'pmbfn'*
-'printmbfont' 'pmbfn' string (default "")
- global
- List of font names to be used for CJK output from |:hardcopy|.
- See |pmbfn-option|.
-
- *'printoptions'* *'popt'*
-'printoptions' 'popt' string (default "")
- global
- List of items that control the format of the output of |:hardcopy|.
- See |popt-option|.
-
*'pumblend'* *'pb'*
'pumblend' 'pb' number (default 0)
global
@@ -4920,8 +4941,7 @@ A jump table for the options with a short description can be found at |Q_op|.
$XDG_CONFIG_HOME/nvim/after")
global
List of directories to be searched for these runtime files:
- filetype.vim filetypes by file name |new-filetype|
- scripts.vim filetypes by file contents |new-filetype-scripts|
+ filetype.lua filetypes |new-filetype|
autoload/ automatically loaded scripts |autoload-functions|
colors/ color scheme files |:colorscheme|
compiler/ compiler files |:compiler|
@@ -4930,10 +4950,12 @@ A jump table for the options with a short description can be found at |Q_op|.
indent/ indent scripts |indent-expression|
keymap/ key mapping files |mbyte-keymap|
lang/ menu translations |:menutrans|
+ lua/ |Lua| plugins
menu.vim GUI menus |menu.vim|
pack/ packages |:packadd|
+ parser/ |treesitter| syntax parsers
plugin/ plugin scripts |write-plugin|
- print/ files for printing |postscript-print-encoding|
+ query/ |treesitter| queries
rplugin/ |remote-plugin| scripts
spell/ spell checking files |spell|
syntax/ syntax files |mysyntaxfile|
@@ -4954,20 +4976,20 @@ 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. Applications state home directory, for files that contain your
- session state (eg. backupdir, viewdir, undodir, etc).
+ 5. Session state directory, for state data such as swap, backupdir,
+ viewdir, undodir, etc.
Given by `stdpath("state")`. |$XDG_STATE_HOME|
- 6. $VIMRUNTIME, for files distributed with Neovim.
+ 6. $VIMRUNTIME, for files distributed with Nvim.
*after-directory*
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
+ ordering. This is for preferences to overrule or add to the
distributed defaults or system-wide settings (rarely needed).
- *rtp-packages*
- "start" packages will additionally be used to search for runtime files
- after these, but package entries are not visible in `:set rtp`.
- See |runtime-search-path| for more information. "opt" packages
- will be explicitly added to &rtp when |:packadd| is used.
+ *packages-runtimepath*
+ "start" packages will also be searched (|runtime-search-path|) for
+ runtime files after these, though such packages are not explicitly
+ reported in &runtimepath. But "opt" packages are explicitly added to
+ &runtimepath by |:packadd|.
Note that, unlike 'path', no wildcards like "**" are allowed. Normal
wildcards are allowed, but can significantly slow down searching for
@@ -4977,18 +4999,13 @@ A jump table for the options with a short description can be found at |Q_op|.
Example: >
:set runtimepath=~/vimruntime,/mygroup/vim,$VIMRUNTIME
< This will use the directory "~/vimruntime" first (containing your
- personal Vim runtime files), then "/mygroup/vim" (shared between a
- group of people) and finally "$VIMRUNTIME" (the distributed runtime
- files).
- You probably should always include $VIMRUNTIME somewhere, to use the
- distributed runtime files. You can put a directory before $VIMRUNTIME
- to find files which replace a distributed runtime files. You can put
- a directory after $VIMRUNTIME to find files which add to distributed
- runtime files.
- When Vim is started with |--clean| the home directory entries are not
- included.
- This option cannot be set from a |modeline| or in the |sandbox|, for
- security reasons.
+ personal Nvim runtime files), then "/mygroup/vim", and finally
+ "$VIMRUNTIME" (the default runtime files).
+ You can put a directory before $VIMRUNTIME to find files which replace
+ distributed runtime files. You can put a directory after $VIMRUNTIME
+ to find files which add to distributed runtime files.
+
+ With |--clean| the home directory entries are not included.
*'scroll'* *'scr'*
'scroll' 'scr' number (default: half the window height)
@@ -5084,19 +5101,6 @@ A jump table for the options with a short description can be found at |Q_op|.
two letters (See |object-motions|). The default makes a section start
at the nroff macros ".SH", ".NH", ".H", ".HU", ".nh" and ".sh".
- *'secure'* *'nosecure'* *E523*
-'secure' boolean (default off)
- global
- When on, ":autocmd", shell and write commands are not allowed in
- ".nvimrc" and ".exrc" in the current directory and map commands are
- displayed. Switch it off only if you know that you will not run into
- problems, or when the 'exrc' option is off. On Unix this option is
- only used if the ".nvimrc" or ".exrc" is not owned by you. This can be
- dangerous if the systems allows users to do a "chown". You better set
- 'secure' at the end of your |init.vim| then.
- This option cannot be set from a |modeline| or in the |sandbox|, for
- security reasons.
-
*'selection'* *'sel'*
'selection' 'sel' string (default "inclusive")
global
@@ -5341,7 +5345,7 @@ A jump table for the options with a short description can be found at |Q_op|.
To use PowerShell: >
let &shell = executable('pwsh') ? 'pwsh' : 'powershell'
let &shellcmdflag = '-NoLogo -NoProfile -ExecutionPolicy RemoteSigned -Command [Console]::InputEncoding=[Console]::OutputEncoding=[System.Text.Encoding]::UTF8;'
- let &shellredir = '-RedirectStandardOutput %s -NoNewWindow -Wait'
+ let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode'
let &shellpipe = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode'
set shellquote= shellxquote=
@@ -5509,42 +5513,48 @@ A jump table for the options with a short description can be found at |Q_op|.
messages, for example with CTRL-G, and to avoid some other messages.
It is a list of flags:
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, 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]"
- w use "[w]" instead of "written" for file write message
+ f use "(3 of 5)" instead of "(file 3 of 5)" *shm-f*
+ i use "[noeol]" instead of "[Incomplete last line]" *shm-i*
+ l use "999L, 888B" instead of "999 lines, 888 bytes" *shm-l*
+ m use "[+]" instead of "[Modified]" *shm-m*
+ n use "[New]" instead of "[New File]" *shm-n*
+ r use "[RO]" instead of "[readonly]" *shm-r*
+ w use "[w]" instead of "written" for file write message *shm-w*
and "[a]" instead of "appended" for ':w >> file' command
- x use "[dos]" instead of "[dos format]", "[unix]" instead of
- "[unix format]" and "[mac]" instead of "[mac format]".
- a all of the above abbreviations
-
- o overwrite message for writing a file with subsequent message
- for reading a file (useful for ":wn" or when 'autowrite' on)
- O message for reading a file overwrites any previous message.
- Also for quickfix message (e.g., ":cn").
- s don't give "search hit BOTTOM, continuing at TOP" or "search
- hit TOP, continuing at BOTTOM" messages; when using the search
- count do not show "W" after the count message (see S below)
- t truncate file message at the start if it is too long to fit
- on the command-line, "<" will appear in the left most column.
- Ignored in Ex mode.
- T truncate other messages in the middle if they are too long to
- fit on the command line. "..." will appear in the middle.
- Ignored in Ex mode.
- W don't give "written" or "[w]" when writing a file
- A don't give the "ATTENTION" message when an existing swap file
- is found.
- I don't give the intro message when starting Vim |:intro|.
- c don't give |ins-completion-menu| messages. For example,
- "-- XXX completion (YYY)", "match 1 of 2", "The only match",
- "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
- S do not show search count message when searching, e.g.
+ x use "[dos]" instead of "[dos format]", "[unix]" *shm-x*
+ instead of "[unix format]" and "[mac]" instead of "[mac
+ format]"
+ a all of the above abbreviations *shm-a*
+
+ o overwrite message for writing a file with subsequent *shm-o*
+ message for reading a file (useful for ":wn" or when
+ 'autowrite' on)
+ O message for reading a file overwrites any previous *shm-O*
+ message; also for quickfix message (e.g., ":cn")
+ s don't give "search hit BOTTOM, continuing at TOP" or *shm-s*
+ "search hit TOP, continuing at BOTTOM" messages; when using
+ the search count do not show "W" after the count message (see
+ S below)
+ t truncate file message at the start if it is too long *shm-t*
+ to fit on the command-line, "<" will appear in the left most
+ column; ignored in Ex mode
+ T truncate other messages in the middle if they are too *shm-T*
+ long to fit on the command line; "..." will appear in the
+ middle; ignored in Ex mode
+ W don't give "written" or "[w]" when writing a file *shm-W*
+ A don't give the "ATTENTION" message when an existing *shm-A*
+ swap file is found
+ I don't give the intro message when starting Vim, *shm-I*
+ see |:intro|
+ c don't give |ins-completion-menu| messages; for *shm-c*
+ example, "-- XXX completion (YYY)", "match 1 of 2", "The only
+ match", "Pattern not found", "Back at original", etc.
+ C don't give messages while scanning for ins-completion *shm-C*
+ items, for instance "scanning tags"
+ q use "recording" instead of "recording @a" *shm-q*
+ F don't give the file info when editing a file, like *shm-F*
+ `:silent` was used for the command
+ S do not show search count message when searching, e.g. *shm-S*
"[1/5]"
This gives you the opportunity to avoid that a change between buffers
@@ -5581,7 +5591,6 @@ A jump table for the options with a short description can be found at |Q_op|.
global
Show (partial) command in the last line of the screen. Set this
option off if your terminal is slow.
- The option has no effect when 'cmdheight' is zero.
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"
@@ -5589,6 +5598,23 @@ A jump table for the options with a short description can be found at |Q_op|.
- When selecting more than one line, the number of lines.
- When selecting a block, the size in screen characters:
{lines}x{columns}.
+ This information can be displayed in an alternative location using the
+ 'showcmdloc' option, useful when 'cmdheight' is 0.
+
+ *'showcmdloc'* *'sloc'*
+'showcmdloc' 'sloc' string (default "last")
+ global
+ This option can be used to display the (partially) entered command in
+ another location. Possible values are:
+ last Last line of the screen (default).
+ statusline Status line of the current window.
+ tabline First line of the screen if 'showtabline' is enabled.
+ Setting this option to "statusline" or "tabline" means that these will
+ be redrawn whenever the command changes, which can be on every key
+ pressed.
+ The %S 'statusline' item can be used in 'statusline' or 'tabline' to
+ place the text. Without a custom 'statusline' or 'tabline' it will be
+ displayed in a convenient location.
*'showfulltag'* *'sft'* *'noshowfulltag'* *'nosft'*
'showfulltag' 'sft' boolean (default off)
@@ -5692,9 +5718,9 @@ A jump table for the options with a short description can be found at |Q_op|.
"yes:[1-9]" always, with fixed space for signs up to the given
number (maximum 9), e.g. "yes:3"
"number" display signs in the 'number' column. If the number
- column is not present, then behaves like 'auto'.
+ column is not present, then behaves like "auto".
- Note regarding 'orphaned signs': with signcolumn numbers higher than
+ 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 appearance not appear weird
@@ -5722,11 +5748,11 @@ A jump table for the options with a short description can be found at |Q_op|.
alternative.
Normally 'autoindent' should also be on when using 'smartindent'.
An indent is automatically inserted:
- - After a line ending in '{'.
+ - After a line ending in "{".
- After a line starting with a keyword from 'cinwords'.
- - Before a line starting with '}' (only with the "O" command).
+ - Before a line starting with "}" (only with the "O" command).
When typing '}' as the first character in a new line, that line is
- given the same indent as the matching '{'.
+ given the same indent as the matching "{".
When typing '#' as the first character in a new line, the indent for
that line is removed, the '#' is put in the first column. The indent
is restored for the next line. If you don't want this, use this
@@ -5860,10 +5886,14 @@ A jump table for the options with a short description can be found at |Q_op|.
'spelloptions' 'spo' string (default "")
local to buffer
A comma-separated list of options for spell checking:
- camel When a word is CamelCased, assume "Cased" is a
+ 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
start of a new word.
+ noplainbuffer Only spellcheck a buffer when 'syntax' is enabled,
+ or when extmarks are set within the buffer. Only
+ designated regions of the buffer are spellchecked in
+ this case.
*'spellsuggest'* *'sps'*
'spellsuggest' 'sps' string (default "best")
@@ -5939,6 +5969,22 @@ A jump table for the options with a short description can be found at |Q_op|.
When on, splitting a window will put the new window below the current
one. |:split|
+ *'splitkeep'* *'spk'*
+'splitkeep' 'spk' string (default "cursor")
+ global
+ The value of this option determines the scroll behavior when opening,
+ closing or resizing horizontal splits.
+
+ Possible values are:
+ cursor Keep the same relative cursor position.
+ screen Keep the text on the same screen line.
+ topline Keep the topline the same.
+
+ For the "screen" and "topline" values, the cursor position will be
+ changed when necessary. In this case, the jumplist will be populated
+ with the previous cursor position. For "screen", the text cannot always
+ be kept on the same screen line when 'wrap' is enabled.
+
*'splitright'* *'spr'* *'nosplitright'* *'nospr'*
'splitright' 'spr' boolean (default off)
global
@@ -5958,6 +6004,60 @@ A jump table for the options with a short description can be found at |Q_op|.
In case of buffer changing commands the cursor is placed at the column
where it was the last time the buffer was edited.
+ *'statuscolumn'* *'stc'*
+'statuscolumn' 'stc' string (default: empty)
+ local to window
+ EXPERIMENTAL
+ When non-empty, this option determines the content of the area to the
+ side of a window, normally containing the fold, sign and number columns.
+ The format of this option is like that of 'statusline'.
+
+ Some of the items from the 'statusline' format are different for
+ 'statuscolumn':
+
+ %l line number of currently drawn line
+ %r relative line number of currently drawn line
+ %s sign column for currently drawn line
+ %C fold column for currently drawn line
+
+ NOTE: To draw the sign and fold columns, their items must be included in
+ 'statuscolumn'. Even when they are not included, the status column width
+ will adapt to the 'signcolumn' and 'foldcolumn' width.
+
+ The |v:lnum| variable holds the line number to be drawn.
+ The |v:relnum| variable holds the relative line number to be drawn.
+ The |v:virtnum| variable is negative when drawing virtual lines, zero
+ when drawing the actual buffer line, and positive when
+ drawing the wrapped part of a buffer line.
+
+ NOTE: The %@ click execute function item is supported as well but the
+ specified function will be the same for each row in the same column.
+ It cannot be switched out through a dynamic 'statuscolumn' format, the
+ handler should be written with this in mind.
+
+ Examples: >vim
+ " Relative number with bar separator and click handlers:
+ :set statuscolumn=%@SignCb@%s%=%T%@NumCb@%r│%T
+
+ " Right aligned relative cursor line number:
+ :let &stc='%=%{v:relnum?v:relnum:v:lnum} '
+
+ " Line numbers in hexadecimal for non wrapped part of lines:
+ :let &stc='%=%{v:virtnum>0?"":printf("%x",v:lnum)} '
+
+ " Human readable line numbers with thousands separator:
+ :let &stc='%{substitute(v:lnum,"\\d\\zs\\ze\\'
+ . '%(\\d\\d\\d\\)\\+$",",","g")}'
+
+ " Both relative and absolute line numbers with different
+ " highlighting for odd and even relative numbers:
+ :let &stc='%#NonText#%{&nu?v:lnum:""}' .
+ '%=%{&rnu&&(v:lnum%2)?"\ ".v:relnum:""}' .
+ '%#LineNr#%{&rnu&&!(v:lnum%2)?"\ ".v:relnum:""}'
+
+< WARNING: this expression is evaluated for each screen line so defining
+ an expensive expression can negatively affect render performance.
+
*'statusline'* *'stl'* *E540* *E542*
'statusline' 'stl' string (default empty)
global or local to window |global-local|
@@ -5982,6 +6082,8 @@ A jump table for the options with a short description can be found at |Q_op|.
When there is error while evaluating the option then it will be made
empty to avoid further errors. Otherwise screen updating would loop.
+ When the result contains unprintable characters the result is
+ unpredictable.
Note that the only effect of 'ruler' when this option is set (and
'laststatus' is 2 or 3) is controlling the output of |CTRL-G|.
@@ -5989,12 +6091,12 @@ A jump table for the options with a short description can be found at |Q_op|.
field meaning ~
- Left justify the item. The default is right justified
when minwid is larger than the length of the item.
- 0 Leading zeroes in numeric items. Overridden by '-'.
- minwid Minimum width of the item, padding as set by '-' & '0'.
+ 0 Leading zeroes in numeric items. Overridden by "-".
+ minwid Minimum width of the item, padding as set by "-" & "0".
Value must be 50 or less.
- maxwid Maximum width of the item. Truncation occurs with a '<'
+ maxwid Maximum width of the item. Truncation occurs with a "<"
on the left for text items. Numeric items will be
- shifted down to maxwid-2 digits followed by '>'number
+ shifted down to maxwid-2 digits followed by ">"number
where number is the amount of missing digits, much like
an exponential notation.
item A one letter code as described below.
@@ -6030,7 +6132,6 @@ A jump table for the options with a short description can be found at |Q_op|.
o N Byte number in file of byte under cursor, first byte is 1.
Mnemonic: Offset from start of file (with one added)
O N As above, in hexadecimal.
- N N Printer page number. (Only works in the 'printheader' option.)
l N Line number.
L N Number of lines in buffer.
c N Column number (byte index).
@@ -6040,24 +6141,25 @@ A jump table for the options with a short description can be found at |Q_op|.
P S Percentage through file of displayed window. This is like the
percentage described for 'ruler'. Always 3 in length, unless
translated.
+ S S 'showcmd' content, see 'showcmdloc'.
a S Argument list status as in default title. ({current} of {max})
Empty if the argument file count is zero or one.
- { NF Evaluate expression between '%{' and '}' and substitute result.
- Note that there is no '%' before the closing '}'. The
- expression cannot contain a '}' character, call a function to
+ { NF Evaluate expression between "%{" and "}" and substitute result.
+ Note that there is no "%" before the closing "}". The
+ expression cannot contain a "}" character, call a function to
work around that. See |stl-%{| below.
- {% - This is almost same as { except the result of the expression is
+ `{%` - This is almost same as "{" except the result of the expression is
re-evaluated as a statusline format string. Thus if the
- return value of expr contains % items they will get expanded.
- The expression can contain the } character, the end of
- expression is denoted by %}.
+ return value of expr contains "%" items they will get expanded.
+ The expression can contain the "}" character, the end of
+ expression is denoted by "%}".
For example: >
func! Stl_filename() abort
return "%t"
endfunc
< `stl=%{Stl_filename()}` results in `"%t"`
`stl=%{%Stl_filename()%}` results in `"Name of current file"`
- %} - End of `{%` expression
+ %} - End of "{%" expression
( - Start of item group. Can be used for setting the width and
alignment of a section. Must be followed by %) somewhere.
) - End of item group. No width fields allowed.
@@ -6252,12 +6354,12 @@ A jump table for the options with a short description can be found at |Q_op|.
Otherwise this option does not always reflect the current syntax (the
b:current_syntax variable does).
This option is most useful in a modeline, for a file which syntax is
- not automatically recognized. Example, in an IDL file:
- /* vim: set syntax=idl : */ ~
- When a dot appears in the value then this separates two filetype
- names. Example:
- /* vim: set syntax=c.doxygen : */ ~
- This will use the "c" syntax first, then the "doxygen" syntax.
+ not automatically recognized. Example, in an IDL file: >
+ /* vim: set syntax=idl : */
+< When a dot appears in the value then this separates two filetype
+ names. Example: >
+ /* vim: set syntax=c.doxygen : */
+< This will use the "c" syntax first, then the "doxygen" syntax.
Note that the second one must be prepared to be loaded as an addition,
otherwise it will be skipped. More than one dot may appear.
To switch off syntax highlighting for the current file, use: >
@@ -6310,7 +6412,7 @@ A jump table for the options with a short description can be found at |Q_op|.
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.
The value must be more than 0 and less than 10000.
There are four main ways to use tabs in Vim:
@@ -6403,7 +6505,9 @@ A jump table for the options with a short description can be found at |Q_op|.
This option specifies a function to be used to perform tag searches.
The function gets the tag pattern and should return a List of matching
tags. See |tag-function| for an explanation of how to write the
- function and an example.
+ function and an example. The value can be the name of a function, a
+ |lambda| or a |Funcref|. See |option-value-function| for more
+ information.
*'taglength'* *'tl'*
'taglength' 'tl' number (default 0)
@@ -6465,7 +6569,7 @@ A jump table for the options with a short description can be found at |Q_op|.
'termguicolors' 'tgc' boolean (default off)
global
Enables 24-bit RGB color in the |TUI|. Uses "gui" |:highlight|
- attributes instead of "cterm" attributes. |highlight-guifg|
+ attributes instead of "cterm" attributes. |guifg|
Requires an ISO-8613-3 compatible terminal.
*'termpastefilter'* *'tpf'*
@@ -6526,6 +6630,8 @@ A jump table for the options with a short description can be found at |Q_op|.
global or local to buffer |global-local|
This option specifies a function to be used for thesaurus completion
with CTRL-X CTRL-T. |i_CTRL-X_CTRL-T| See |compl-thesaurusfunc|.
+ The value can be the name of a function, a |lambda| or a |Funcref|.
+ See |option-value-function| for more information.
This option cannot be set from a |modeline| or in the |sandbox|, for
security reasons.
@@ -6732,11 +6838,11 @@ A jump table for the options with a short description can be found at |Q_op|.
final value applying to all subsequent tabs.
For example, when editing assembly language files where statements
- start in the 8th column and comments in the 40th, it may be useful
+ start in the 9th column and comments in the 41st, it may be useful
to use the following: >
:set varsofttabstop=8,32,8
-< This will set soft tabstops at the 8th and 40th columns, and at every
- 8th column thereafter.
+< This will set soft tabstops with 8 and 8 + 32 spaces, and 8 more
+ for every column thereafter.
Note that the value of |'softtabstop'| will be ignored while
'varsofttabstop' is set.
@@ -6757,28 +6863,31 @@ A jump table for the options with a short description can be found at |Q_op|.
*'verbose'* *'vbs'*
'verbose' 'vbs' number (default 0)
global
- When bigger than zero, Vim will give messages about what it is doing.
- Currently, these messages are given:
- >= 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.
- >= 8 Files for which a group of autocommands is executed.
- >= 9 Every executed autocommand.
- >= 11 Finding items in a path
- >= 12 Every executed function.
- >= 13 When an exception is thrown, caught, finished, or discarded.
- >= 14 Anything pending in a ":finally" clause.
- >= 15 Every executed Ex command from a script (truncated at 200
- characters).
- >= 16 Every executed Ex command.
-
- This option can also be set with the "-V" argument. See |-V|.
- This option is also set by the |:verbose| command.
-
- When the 'verbosefile' option is set then the verbose messages are not
- displayed.
+ Sets the verbosity level. Also set by |-V| and |:verbose|.
+
+ Tracing of options in Lua scripts is activated at level 1; Lua scripts
+ are not traced with verbose=0, for performance.
+
+ If greater than or equal to a given level, Nvim produces the following
+ messages:
+
+ Level Messages ~
+ ----------------------------------------------------------------------
+ 1 Lua assignments to options, mappings, etc.
+ 2 When a file is ":source"'ed, or |shada| file is read or written.
+ 3 UI info, terminal capabilities.
+ 4 Shell commands.
+ 5 Every searched tags file and include file.
+ 8 Files for which a group of autocommands is executed.
+ 9 Executed autocommands.
+ 11 Finding items in a path.
+ 12 Vimscript function calls.
+ 13 When an exception is thrown, caught, finished, or discarded.
+ 14 Anything pending in a ":finally" clause.
+ 15 Ex commands from a script (truncated at 200 characters).
+ 16 Ex commands.
+
+ If 'verbosefile' is set then the verbose messages are not displayed.
*'verbosefile'* *'vfile'*
'verbosefile' 'vfile' string (default empty)
@@ -6938,15 +7047,18 @@ A jump table for the options with a short description can be found at |Q_op|.
*'wildmenu'* *'wmnu'* *'nowildmenu'* *'nowmnu'*
'wildmenu' 'wmnu' boolean (default on)
global
- Enables "enhanced mode" of command-line completion. When user hits
- <Tab> (or 'wildchar') to invoke completion, the possible matches are
- shown in a menu just above the command-line (see 'wildoptions'), with
- the first match highlighted (overwriting the statusline). Keys that
- show the previous/next match (<Tab>/CTRL-P/CTRL-N) highlight the
- match.
+ When 'wildmenu' is on, command-line completion operates in an enhanced
+ mode. On pressing 'wildchar' (usually <Tab>) to invoke completion,
+ the possible matches are shown.
+ When 'wildoptions' contains "pum", then the completion matches are
+ shown in a popup menu. Otherwise they are displayed just above the
+ command line, with the first match highlighted (overwriting the status
+ line, if there is one).
+ Keys that show the previous/next match, such as <Tab> or
+ CTRL-P/CTRL-N, cause the highlight to move to the appropriate match.
'wildmode' must specify "full": "longest" and "list" do not start
'wildmenu' mode. You can check the current mode with |wildmenumode()|.
- The menu is canceled when a key is hit that is not used for selecting
+ The menu is cancelled when a key is hit that is not used for selecting
a completion.
While the menu is active these keys have special meanings:
@@ -7022,6 +7134,14 @@ A jump table for the options with a short description can be found at |Q_op|.
global
A list of words that change how |cmdline-completion| is done.
The following values are supported:
+ fuzzy Use |fuzzy-matching| to find completion matches. When
+ this value is specified, wildcard expansion will not
+ be used for completion. The matches will be sorted by
+ the "best match" rather than alphabetically sorted.
+ This will find more matches than the wildcard
+ expansion. Currently fuzzy matching based completion
+ is not supported for file and directory names and
+ instead wildcard expansion is used.
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
@@ -7117,7 +7237,7 @@ A jump table for the options with a short description can be found at |Q_op|.
the window.
Note: highlight namespaces take precedence over 'winhighlight'.
- See |nvim_win_set_hl_ns| and |nvim_set_hl|.
+ See |nvim_win_set_hl_ns()| and |nvim_set_hl()|.
Highlights of vertical separators are determined by the window to the
left of the separator. The 'tabline' highlight of a tabpage is
diff --git a/runtime/doc/pattern.txt b/runtime/doc/pattern.txt
index 371a210847..5357aaa3f1 100644
--- a/runtime/doc/pattern.txt
+++ b/runtime/doc/pattern.txt
@@ -372,7 +372,7 @@ Vim includes two regexp engines:
1. An old, backtracking engine that supports everything.
2. A new, NFA engine that works much faster on some patterns, possibly slower
on some patterns.
-
+ *E1281*
Vim will automatically select the right engine for you. However, if you run
into a problem or want to specifically select one engine or the other, you can
prepend one of the following to the pattern:
diff --git a/runtime/doc/pi_health.txt b/runtime/doc/pi_health.txt
index 04e04b5165..5ba5d1beef 100644
--- a/runtime/doc/pi_health.txt
+++ b/runtime/doc/pi_health.txt
@@ -7,11 +7,12 @@ Author: TJ DeVries <devries.timothyj@gmail.com>
==============================================================================
Introduction *health*
-health.vim is a minimal framework to help with troubleshooting user
-configuration. Nvim ships with healthchecks for configuration, performance,
-python support, ruby support, clipboard support, and more.
+health.vim is a minimal framework to help users troubleshoot configuration and
+any other environment conditions that a plugin might care about. Nvim ships
+with healthchecks for configuration, performance, python support, ruby
+support, clipboard support, and more.
-To run the healthchecks, use this command: >
+To run all healthchecks, use: >
:checkhealth
<
@@ -20,8 +21,8 @@ Plugin authors are encouraged to write new healthchecks. |health-dev|
==============================================================================
Commands *health-commands*
- *:checkhealth* *:CheckHealth*
-:checkhealth Run all healthchecks.
+ *:che* *:checkhealth* *:CheckHealth*
+:che[ckhealth] Run all healthchecks.
*E5009*
Nvim depends on |$VIMRUNTIME|, 'runtimepath' and 'packpath' to
find the standard "runtime files" for syntax highlighting,
@@ -29,29 +30,27 @@ Commands *health-commands*
:checkhealth). If the runtime files cannot be found then
those features will not work.
-:checkhealth {plugins}
+:che[ckhealth] {plugins}
Run healthcheck(s) for one or more plugins. E.g. to run only
the standard Nvim healthcheck: >
:checkhealth nvim
<
To run the healthchecks for the "foo" and "bar" plugins
- (assuming these plugins are on 'runtimepath' or 'packpath' and
- they have implemented the Lua or Vimscript interface
- require("foo.health").check() and health#bar#check(),
- respectively): >
+ (assuming they are on 'runtimepath' and they have implemented
+ the Lua `require("foo.health").check()` interface): >
:checkhealth foo bar
<
- To run healthchecks for lua submodules, use dot notation or
- "*" to refer to all submodules. For example nvim provides
- `vim.lsp` and `vim.treesitter` >
+ To run healthchecks for Lua submodules, use dot notation or
+ "*" to refer to all submodules. For example Nvim provides
+ `vim.lsp` and `vim.treesitter`: >
:checkhealth vim.lsp vim.treesitter
:checkhealth vim*
<
==============================================================================
-Lua Functions *health-functions-lua* *health-lua* *vim.health*
+Functions *health-functions* *vim.health*
-The Lua "health" module can be used to create new healthchecks (see also
-|health-functions-vim|). To get started, simply use:
+The Lua "health" module can be used to create new healthchecks. To get started
+see |health-dev|.
vim.health.report_start({name}) *vim.health.report_start()*
Starts a new report. Most plugins should call this only once, but if
@@ -65,36 +64,43 @@ vim.health.report_ok({msg}) *vim.health.report_ok()*
Reports a "success" message.
vim.health.report_warn({msg} [, {advice}]) *vim.health.report_warn()*
- Reports a warning. {advice} is an optional List of suggestions.
+ Reports a warning. {advice} is an optional list of suggestions to
+ present to the user.
vim.health.report_error({msg} [, {advice}]) *vim.health.report_error()*
- Reports an error. {advice} is an optional List of suggestions.
+ Reports an error. {advice} is an optional list of suggestions to
+ present to the user.
==============================================================================
-Create a Lua healthcheck *health-dev-lua*
-
-Healthchecks are functions that check the user environment, configuration,
-etc. Nvim has built-in healthchecks in $VIMRUNTIME/autoload/health/.
-
-To add a new healthcheck for your own plugin, simply define a Lua module in
-your plugin that returns a table with a "check()" function. |:checkhealth|
-will automatically find and invoke this function.
-
-If your plugin is named "foo", then its healthcheck module should be a file in
-one of these locations on 'runtimepath' or 'packpath':
+Create a healthcheck *health-dev*
+
+Healthchecks are functions that check the user environment, configuration, or
+any other prerequisites that a plugin cares about. Nvim ships with
+healthchecks in:
+ - $VIMRUNTIME/autoload/health/
+ - $VIMRUNTIME/lua/vim/lsp/health.lua
+ - $VIMRUNTIME/lua/vim/treesitter/health.lua
+ - and more...
+
+To add a new healthcheck for your own plugin, simply create a "health.lua"
+module on 'runtimepath' that returns a table with a "check()" function. Then
+|:checkhealth| will automatically find and invoke the function.
+
+For example if your plugin is named "foo", define your healthcheck module at
+one of these locations (on 'runtimepath'):
- lua/foo/health/init.lua
- lua/foo/health.lua
-If your plugin provides a submodule named "bar" for which you want a separate
-healthcheck, define the healthcheck at one of these locations on 'runtimepath'
-or 'packpath':
+If your plugin also provides a submodule named "bar" for which you want
+a separate healthcheck, define the healthcheck at one of these locations:
- lua/foo/bar/health/init.lua
- lua/foo/bar/health.lua
-All submodules should return a Lua table containing the method `check()`.
+All such health modules must return a Lua table containing a `check()`
+function.
-Copy this sample code into `lua/foo/health/init.lua` or `lua/foo/health.lua`,
-replacing "foo" in the path with your plugin name: >
+Copy this sample code into `lua/foo/health.lua`, replacing "foo" in the path
+with your plugin name: >
local M = {}
@@ -102,9 +108,9 @@ replacing "foo" in the path with your plugin name: >
vim.health.report_start("my_plugin report")
-- make sure setup function parameters are ok
if check_setup() then
- vim.health.report_ok("Setup function is correct")
+ vim.health.report_ok("Setup is correct")
else
- vim.health.report_error("Setup function is incorrect")
+ vim.health.report_error("Setup is incorrect")
end
-- do some more checking
-- ...
@@ -112,67 +118,5 @@ replacing "foo" in the path with your plugin name: >
return M
-==============================================================================
-Vimscript Functions *health-functions-vimscript* *health-vimscript*
-
-health.vim functions are for creating new healthchecks. (See also
-|health-functions-lua|)
-
-health#report_start({name}) *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*
- Reports an informational message.
-
-health#report_ok({msg}) *health#report_ok*
- Reports a "success" message.
-
-health#report_warn({msg} [, {advice}]) *health#report_warn*
- Reports a warning. {advice} is an optional List of suggestions.
-
-health#report_error({msg} [, {advice}]) *health#report_error*
- Reports an error. {advice} is an optional List of suggestions.
-
-health#{plugin}#check() *health.user_checker*
- Healthcheck function for {plugin}. Called by |:checkhealth|
- automatically. Example: >
-
- function! health#my_plug#check() abort
- silent call s:check_environment_vars()
- silent call s:check_python_configuration()
- endfunction
-<
-==============================================================================
-Create a healthcheck *health-dev-vim*
-
-Healthchecks are functions that check the user environment, configuration,
-etc. Nvim has built-in healthchecks in $VIMRUNTIME/autoload/health/.
-
-To add a new healthcheck for your own plugin, simply define a
-health#{plugin}#check() function in autoload/health/{plugin}.vim.
-|:checkhealth| automatically finds and invokes such functions.
-
-If your plugin is named "foo", then its healthcheck function must be >
- health#foo#check()
-
-defined in this file on 'runtimepath' or 'packpath':
- - autoload/health/foo.vim
-
-Copy this sample code into autoload/health/foo.vim and replace "foo" with your
-plugin name: >
- function! health#foo#check() abort
- call health#report_start('sanity checks')
- " perform arbitrary checks
- " ...
-
- if looks_good
- call health#report_ok('found required dependencies')
- else
- call health#report_error('cannot find foo',
- \ ['npm install --save foo'])
- endif
- endfunction
vim:et:tw=78:ts=8:ft=help:fdm=marker
diff --git a/runtime/doc/pi_msgpack.txt b/runtime/doc/pi_msgpack.txt
index 801c56e49f..24a31f1de7 100644
--- a/runtime/doc/pi_msgpack.txt
+++ b/runtime/doc/pi_msgpack.txt
@@ -63,16 +63,16 @@ msgpack#is_uint({msgpack-value}) *msgpack#is_uint()*
*msgpack#strftime*
msgpack#strftime({format}, {msgpack-integer}) *msgpack#strftime()*
Same as |strftime()|, but second argument may be
- |msgpack-special-dict|. Requires |+python| or |+python3| to really
- work with |msgpack-special-dict|s.
+ |msgpack-special-dict|. Requires |Python| to really work with
+ |msgpack-special-dict|s.
*msgpack#strptime*
msgpack#strptime({format}, {time}) *msgpack#strptime()*
Reverse of |msgpack#strftime()|: for any time and format
|msgpack#equal|( |msgpack#strptime|(format, |msgpack#strftime|(format,
- time)), time) be true. Requires |+python| or |+python3|, without it
- only supports non-|msgpack-special-dict| nonnegative times and format
- equal to `%Y-%m-%dT%H:%M:%S`.
+ time)), time) be true. Requires ||Python|, without it only supports
+ non-|msgpack-special-dict| nonnegative times and format equal to
+ `%Y-%m-%dT%H:%M:%S`.
msgpack#int_dict_to_str({msgpack-special-int}) *msgpack#int_dict_to_str()*
Function which converts |msgpack-special-dict| integer value to
diff --git a/runtime/doc/pi_netrw.txt b/runtime/doc/pi_netrw.txt
index 1eaa76264f..5167b4baf7 100644
--- a/runtime/doc/pi_netrw.txt
+++ b/runtime/doc/pi_netrw.txt
@@ -2353,7 +2353,7 @@ MARKED FILES: DIFF *netrw-md* {{{2
(See |netrw-mf| and |netrw-mr| for how to mark files)
(uses the global marked file list)
-Use |vimdiff| to visualize difference between selected files (two or
+Use vimdiff to visualize difference between selected files (two or
three may be selected for this). Uses the global marked file list.
MARKED FILES: EDITING *netrw-me* {{{2
@@ -2419,15 +2419,6 @@ from the current window (where one does the mf) to the target.
Associated setting variable: |g:netrw_localmovecmd| |g:netrw_ssh_cmd|
-MARKED FILES: PRINTING *netrw-mp* {{{2
- (See |netrw-mf| and |netrw-mr| for how to mark files)
- (uses the local marked file list)
-
-When "mp" is used, netrw will apply the |:hardcopy| command to marked files.
-What netrw does is open each file in a one-line window, execute hardcopy, then
-close the one-line window.
-
-
MARKED FILES: SOURCING *netrw-ms* {{{2
(See |netrw-mf| and |netrw-mr| for how to mark files)
(uses the local marked file list)
@@ -2782,7 +2773,7 @@ your browsing preferences. (see also: |netrw-settings|)
history are saved (as .netrwbook and
.netrwhist).
Netrw uses |expand()| on the string.
- default: stdpath('data') (see |stdpath()|)
+ default: stdpath("data") (see |stdpath()|)
*g:netrw_keepdir* =1 (default) keep current directory immune from
the browsing directory.
@@ -2821,12 +2812,12 @@ your browsing preferences. (see also: |netrw-settings|)
function 'netrw_gitignore#Hide() automatically
hiding all gitignored files.
For more details see |netrw-gitignore|.
+ default: ""
- Examples:
- let g:netrw_list_hide= '.*\.swp$'
- let g:netrw_list_hide= netrw_gitignore#Hide() .. '.*\.swp$'
- default: ""
-
+ Examples: >
+ let g:netrw_list_hide= '.*\.swp$'
+ let g:netrw_list_hide= netrw_gitignore#Hide() .. '.*\.swp$'
+<
*g:netrw_localcopycmd* ="cp" Linux/Unix/MacOS/Cygwin
=expand("$COMSPEC") Windows
Copies marked files (|netrw-mf|) to target
@@ -3268,7 +3259,7 @@ If there are marked files: (see |netrw-mf|)
mr [query: reply with *.c]
R [query: reply with s/^\(.*\)\.c$/\1.cpp/]
<
- This example will mark all *.c files and then rename them to *.cpp
+ This example will mark all "*.c" files and then rename them to "*.cpp"
files. Netrw will protect you from overwriting local files without
confirmation, but not remote ones.
@@ -3280,7 +3271,7 @@ If there are marked files: (see |netrw-mf|)
<c-x><c-x> : a pair of contiguous ctrl-x's tells netrw to ignore any
portion of the string preceding the double ctrl-x's.
<
- WARNING:~
+ WARNING: ~
Note that moving files is a dangerous operation; copies are safer. That's
because a "move" for remote files is actually a copy + delete -- and if
@@ -3469,7 +3460,7 @@ Example: Clear netrw's marked file list via a mapping on gu >
*netrw-p4*
P4. I would like long listings to be the default. {{{2
- Put the following statement into your |.vimrc|: >
+ Put the following statement into your |vimrc|: >
let g:netrw_liststyle= 1
<
@@ -3482,7 +3473,7 @@ Example: Clear netrw's marked file list via a mapping on gu >
Does your system's strftime() accept the "%c" to yield dates
such as "Sun Apr 27 11:49:23 1997"? If not, do a
"man strftime" and find out what option should be used. Then
- put it into your |.vimrc|: >
+ put it into your |vimrc|: >
let g:netrw_timefmt= "%X" (where X is the option)
<
@@ -3490,7 +3481,7 @@ Example: Clear netrw's marked file list via a mapping on gu >
P6. I want my current directory to track my browsing. {{{2
How do I do that?
- Put the following line in your |.vimrc|:
+ Put the following line in your |vimrc|:
>
let g:netrw_keepdir= 0
<
@@ -3569,7 +3560,7 @@ Example: Clear netrw's marked file list via a mapping on gu >
http://www.chiark.greenend.org.uk/~sgtatham/putty/0.60/htmldoc/Chapter8.html#pubkey-gettingready
(8.3 Getting ready for public key authentication)
<
- How to use a private key with 'pscp': >
+ How to use a private key with "pscp": >
http://www.chiark.greenend.org.uk/~sgtatham/putty/0.60/htmldoc/Chapter5.html
(5.2.4 Using public key authentication with PSCP)
@@ -3776,9 +3767,9 @@ Example: Clear netrw's marked file list via a mapping on gu >
<
*netrw-P22*
P22. I get an error message when I try to copy or move a file: {{{2
-
+>
**error** (netrw) tried using g:netrw_localcopycmd<cp>; it doesn't work!
-
+<
What's wrong?
Netrw uses several system level commands to do things (see
@@ -3839,7 +3830,7 @@ netrw:
or
http://vim.sourceforge.net/scripts/script.php?script_id=120
- Decho.vim is provided as a "vimball"; see |vimball-intro|. You
+ Decho.vim is provided as a "vimball". You
should edit the Decho.vba.gz file and source it in: >
vim Decho.vba.gz
@@ -3917,7 +3908,7 @@ netrw:
* Installed |g:netrw_clipboard| setting
* Installed option bypass for |'guioptions'|
a/A settings
- * Changed popup_beval() to |popup_atcursor()|
+ * Changed popup_beval() to popup_atcursor()
in netrw#ErrorMsg (lacygoill). Apparently
popup_beval doesn't reliably close the
popup when the mouse is moved.
@@ -3943,7 +3934,7 @@ netrw:
did not restore options correctly that
had a single quote in the option string.
Apr 13, 2020 * implemented error handling via popup
- windows (see |popup_beval()|)
+ windows (see popup_beval())
Apr 30, 2020 * (reported by Manatsu Takahashi) while
using Lexplore, a modified file could
be overwritten. Sol'n: will not overwrite,
@@ -4001,9 +3992,9 @@ netrw:
Nov 22, 2016 * (glacambre) reported that files containing
spaces weren't being obtained properly via
scp. Fix: apparently using single quotes
- such as with 'file name' wasn't enough; the
+ such as with "file name" wasn't enough; the
spaces inside the quotes also had to be
- escaped (ie. 'file\ name').
+ escaped (ie. "file\ name").
* Also fixed obtain (|netrw-O|) to be able to
obtain files with spaces in their names
Dec 20, 2016 * (xc1427) Reported that using "I" (|netrw-I|)
diff --git a/runtime/doc/pi_spec.txt b/runtime/doc/pi_spec.txt
index 6d45a0f064..d485d6ad49 100644
--- a/runtime/doc/pi_spec.txt
+++ b/runtime/doc/pi_spec.txt
@@ -34,8 +34,8 @@ also check if the name, version and release matches. The plugin is smart
enough to ask you if it should update the package release, if you have not
done so.
+------------------------------------------------------------------------------
Setting a map *spec-setting-a-map*
--------------
As you should know, you can easily set a map to access any Vim command (or
anything, for that matter). If you don't like the default map of
@@ -54,8 +54,8 @@ This command will add a map only in the spec file buffers.
==============================================================================
2. Customizing *spec-customizing*
+------------------------------------------------------------------------------
The format string *spec_chglog_format*
------------------
You can easily customize how your spec file entry will look like. To do
this just set the variable "spec_chglog_format" in your vimrc file like
@@ -72,8 +72,8 @@ address once.
To discover which format options you can use, take a look at the strftime()
function man page.
+------------------------------------------------------------------------------
Where to insert new items *spec_chglog_prepend*
--------------------------
The plugin will usually insert new %changelog entry items (note that it's
not the entry itself) after the existing ones. If you set the
@@ -83,8 +83,8 @@ spec_chglog_prepend variable >
it will insert new items before the existing ones.
+------------------------------------------------------------------------------
Inserting release info *spec_chglog_release_info*
-----------------------
If you want, the plugin may automatically insert release information
on each changelog entry. One advantage of turning this feature on is
diff --git a/runtime/doc/pi_tar.txt b/runtime/doc/pi_tar.txt
index c6c0596ea0..2230b82dec 100644
--- a/runtime/doc/pi_tar.txt
+++ b/runtime/doc/pi_tar.txt
@@ -80,25 +80,25 @@ Copyright 2005-2017: *tar-copyright*
These options are variables that one may change, typically in one's
<.vimrc> file.
- Default
- Variable Value Explanation
- *g:tar_browseoptions* "Ptf" used to get a list of contents
- *g:tar_readoptions* "OPxf" used to extract a file from a tarball
- *g:tar_cmd* "tar" the name of the tar program
- *g:tar_nomax* 0 if true, file window will not be maximized
- *g:tar_secure* undef if exists:
+ Default
+ Variable Value Explanation
+ *g:tar_browseoptions* "Ptf" used to get a list of contents
+ *g:tar_readoptions* "OPxf" used to extract a file from a tarball
+ *g:tar_cmd* "tar" the name of the tar program
+ *g:tar_nomax* 0 if true, file window will not be maximized
+ *g:tar_secure* undef if exists:
"--"s will be used to prevent unwanted
option expansion in tar commands.
Please be sure that your tar command
accepts "--"; Posix compliant tar
utilities do accept them.
if not exists:
- The tar plugin will reject any tar
+ The tar plugin will reject any tar
files or member files that begin with
"-"
Not all tar's support the "--" which is why
it isn't default.
- *g:tar_writeoptions* "uf" used to update/replace a file
+ *g:tar_writeoptions* "uf" used to update/replace a file
==============================================================================
diff --git a/runtime/doc/pi_zip.txt b/runtime/doc/pi_zip.txt
index 2bbd6eea06..9b531d78b4 100644
--- a/runtime/doc/pi_zip.txt
+++ b/runtime/doc/pi_zip.txt
@@ -39,7 +39,7 @@ Copyright: Copyright (C) 2005-2015 Charles E Campbell *zip-copyright*
OPTIONS~
- *g:zip_nomax*
+ *g:zip_nomax*
If this variable exists and is true, the file window will not be
automatically maximized when opened.
@@ -54,21 +54,21 @@ Copyright: Copyright (C) 2005-2015 Charles E Campbell *zip-copyright*
under Windows ("). If you'd rather have no quotes, simply set
g:zip_shq to the empty string (let g:zip_shq= "") in your <.vimrc>.
- *g:zip_unzipcmd*
+ *g:zip_unzipcmd*
Use this option to specify the program which does the duty of "unzip".
It's used during browsing. By default: >
- let g:zip_unzipcmd= "unzip"
+ let g:zip_unzipcmd= "unzip"
<
*g:zip_zipcmd*
Use this option to specify the program which does the duty of "zip".
It's used during the writing (updating) of a file already in a zip
file; by default: >
- let g:zip_zipcmd= "zip"
+ let g:zip_zipcmd= "zip"
<
*g:zip_extractcmd*
This option specifies the program (and any options needed) used to
extract a file from a zip archive. By default, >
- let g:zip_extractcmd= g:zip_unzipcmd
+ let g:zip_extractcmd= g:zip_unzipcmd
<
PREVENTING LOADING~
@@ -103,14 +103,14 @@ Copyright: Copyright (C) 2005-2015 Charles E Campbell *zip-copyright*
==============================================================================
4. History *zip-history* {{{1
v32 Oct 22, 2021 * to avoid an issue with a vim 8.2 patch, zipfile: has
- been changed to zipfile:// . This often shows up
+ been changed to zipfile:// . This often shows up
as zipfile:/// with zipped files that are root-based.
v29 Apr 02, 2017 * (Klartext) reported that an encrypted zip file could
- opened but the swapfile held unencrypted contents.
+ opened but the swapfile held unencrypted contents.
The solution is to edit the contents of a zip file
using the |:noswapfile| modifier.
v28 Oct 08, 2014 * changed the sanity checks for executables to reflect
- the command actually to be attempted in zip#Read()
+ the command actually to be attempted in zip#Read()
and zip#Write()
* added the extraction of a file capability
Nov 30, 2015 * added *.epub to the |g:zipPlugin_ext| list
diff --git a/runtime/doc/print.txt b/runtime/doc/print.txt
deleted file mode 100644
index 924fab175e..0000000000
--- a/runtime/doc/print.txt
+++ /dev/null
@@ -1,721 +0,0 @@
-*print.txt* Nvim
-
-
- VIM REFERENCE MANUAL by Bram Moolenaar
-
-
-Printing *printing*
-
- Type |gO| to see the table of contents.
-
-==============================================================================
-1. Introduction *print-intro*
-
-On MS-Windows Vim can print your text on any installed printer. On other
-systems a PostScript file is produced. This can be directly sent to a
-PostScript printer. For other printers a program like ghostscript needs to be
-used.
-
-Note: If you have problems printing with |:hardcopy|, an alternative is to use
-|:TOhtml| and print the resulting html file from a browser.
-
- *:ha* *:hardcopy* *E237* *E238* *E324*
-:[range]ha[rdcopy][!] [arguments]
- Send [range] lines (default whole file) to the
- printer.
-
- On MS-Windows a dialog is displayed to allow selection
- of printer, paper size etc. To skip the dialog, use
- the [!]. In this case the printer defined by
- 'printdevice' is used, or, if 'printdevice' is empty,
- the system default printer.
-
- For systems other than MS-Windows, PostScript is
- written in a temp file and 'printexpr' is used to
- actually print it. Then [arguments] can be used by
- 'printexpr' through |v:cmdarg|. Otherwise [arguments]
- is ignored. 'printoptions' can be used to specify
- paper size, duplex, etc.
- Note: If you want PDF, there are tools such as
- "ps2pdf" that can convert the PostScript to PDF.
-
-:[range]ha[rdcopy][!] >{filename}
- As above, but write the resulting PostScript in file
- {filename}.
- Things like "%" are expanded |cmdline-special|
- Careful: An existing file is silently overwritten.
- On MS-Windows use the "print to file" feature of the
- printer driver.
-
-Progress is displayed during printing as a page number and a percentage. To
-abort printing use the interrupt key (CTRL-C or, on MS-systems, CTRL-Break).
-
-Printer output is controlled by the 'printfont' and 'printoptions' options.
-'printheader' specifies the format of a page header.
-
-The printed file is always limited to the selected margins, irrespective of
-the current window's 'wrap' or 'linebreak' settings. The "wrap" item in
-'printoptions' can be used to switch wrapping off.
-The current highlighting colors are used in the printout, with the following
-considerations:
-1) The normal background is always rendered as white (i.e. blank paper).
-2) White text or the default foreground is rendered as black, so that it shows
- up!
-3) If 'background' is "dark", then the colours are darkened to compensate for
- the fact that otherwise they would be too bright to show up clearly on
- white paper.
-
-==============================================================================
-2. Print options *print-options*
-
-Here are the details for the options that change the way printing is done.
-For generic info about setting options see |options.txt|.
-
- *pdev-option*
-'printdevice' 'pdev' string (default empty)
- global
-This defines the name of the printer to be used when the |:hardcopy| command
-is issued with a bang (!) to skip the printer selection dialog. On Win32, it
-should be the printer name exactly as it appears in the standard printer
-dialog.
-If the option is empty, then vim will use the system default printer for
-":hardcopy!"
-
- *penc-option* *E620*
-'printencoding' 'penc' String (default empty, except for:
- Windows: cp1252,
- Macintosh: mac-roman,
- HPUX: hp-roman8)
- global
-Sets the character encoding used when printing. This option tells Vim which
-print character encoding file from the "print" directory in 'runtimepath' to
-use.
-
-This option will accept any value from |encoding-names|. Any recognized names
-are converted to Vim standard names - see 'encoding' for more details. Names
-not recognized by Vim will just be converted to lower case and underscores
-replaced with '-' signs.
-
-If 'printencoding' is empty or Vim cannot find the file then it will use
-'encoding' (if it is set an 8-bit encoding) to find the print character
-encoding file. If Vim is unable to find a character encoding file then it
-will use the "latin1" print character encoding file.
-
-When 'encoding' is set to a multibyte encoding, Vim will try to convert
-characters to the printing encoding for printing (if 'printencoding' is empty
-then the conversion will be to latin1). If no conversion is possible then
-printing will fail. Any characters that cannot be converted will be replaced
-with upside down question marks.
-
-Two print character encoding files are provided to support default Mac and
-HPUX character encodings and are used by default on these platforms. Code page
-1252 print character encoding is used by default on the Windows platform.
-
- *pexpr-option*
-'printexpr' 'pexpr' String (default: see below)
- global
-Expression that is evaluated to print the PostScript produced with
-|:hardcopy|.
-The file name to be printed is in |v:fname_in|.
-The arguments to the ":hardcopy" command are in |v:cmdarg|.
-The expression must take care of deleting the file after printing it.
-When there is an error, the expression must return a non-zero number.
-If there is no error, return zero or an empty string.
-The default for non MS-Windows systems is to simply use "lpr" to print the
-file: >
-
- system(['lpr']
- + (empty(&printdevice)?[]:['-P', &printdevice])
- + [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)
-
-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 delete(a:fname)
- : return v:shell_error
- :endfunc
-
-Be aware that some print programs return control before they have read the
-file. If you delete the file too soon it will not be printed. These programs
-usually offer an option to have them remove the file when printing is done.
- *E365*
-If evaluating the expression fails or it results in a non-zero number, you get
-an error message. In that case Vim will delete the file. In the default
-value for non-MS-Windows a trick is used: Adding "v:shell_error" will result
-in a non-zero number when the system() call fails.
-
-This option cannot be set from a |modeline| or in the |sandbox|, for security
-reasons.
-
- *pfn-option* *E613*
-'printfont' 'pfn' string (default "courier")
- global
-This is the name of the font that will be used for the |:hardcopy| command's
-output. It has the same format as the 'guifont' option, except that only one
-font may be named, and the special "guifont=*" syntax is not available.
-
-In the Win32 GUI version this specifies a font name with its extra attributes,
-as with the 'guifont' option.
-
-For other systems, only ":h11" is recognized, where "11" is the point size of
-the font. When omitted, the point size is 10.
-
- *pheader-option*
-'printheader' 'pheader' string (default "%<%f%h%m%=Page %N")
- global
-This defines the format of the header produced in |:hardcopy| output. The
-option is defined in the same way as the 'statusline' option. The same simple
-header is used when this option is empty.
-
- *pmbcs-option*
-'printmbcharset' 'pmbcs' string (default "")
- global
-Sets the CJK character set to be used when generating CJK output from
-|:hardcopy|. The following predefined values are currently recognised by Vim:
-
- Value Description ~
- Chinese GB_2312-80
- (Simplified) GBT_12345-90
- MAC Apple Mac Simplified Chinese
- GBT-90_MAC GB/T 12345-90 Apple Mac Simplified
- Chinese
- GBK GBK (GB 13000.1-93)
- ISO10646 ISO 10646-1:1993
-
- Chinese CNS_1993 CNS 11643-1993, Planes 1 & 2
- (Traditional) BIG5
- ETEN Big5 with ETen extensions
- ISO10646 ISO 10646-1:1993
-
- Japanese JIS_C_1978
- JIS_X_1983
- JIS_X_1990
- MSWINDOWS Win3.1/95J (JIS X 1997 + NEC +
- IBM extensions)
- KANJITALK6 Apple Mac KanjiTalk V6.x
- KANJITALK7 Apple Mac KanjiTalk V7.x
-
- Korean KS_X_1992
- MAC Apple Macintosh Korean
- MSWINDOWS KS X 1992 with MS extensions
- ISO10646 ISO 10646-1:1993
-
-Only certain combinations of the above values and 'printencoding' are
-possible. The following tables show the valid combinations:
-
- euc-cn gbk ucs-2 utf-8 ~
- Chinese GB_2312-80 x
- (Simplified) GBT_12345-90 x
- MAC x
- GBT-90_MAC x
- GBK x
- ISO10646 x x
-
- euc-tw big5 ucs-2 utf-8 ~
- Chinese CNS_1993 x
- (Traditional) BIG5 x
- ETEN x
- ISO10646 x x
-
- euc-jp sjis ucs-2 utf-8 ~
- Japanese JIS_C_1978 x x
- JIS_X_1983 x x
- JIS_X_1990 x x x
- MSWINDOWS x
- KANJITALK6 x
- KANJITALK7 x
-
- euc-kr cp949 ucs-2 utf-8 ~
- Korean KS_X_1992 x
- MAC x
- MSWINDOWS x
- ISO10646 x x
-
-To set up the correct encoding and character set for printing some
-Japanese text you would do the following; >
- :set printencoding=euc-jp
- :set printmbcharset=JIS_X_1983
-
-If 'printmbcharset' is not one of the above values then it is assumed to
-specify a custom multibyte character set and no check will be made that it is
-compatible with the value for 'printencoding'. Vim will look for a file
-defining the character set in the "print" directory in 'runtimepath'.
-
- *pmbfn-option*
-'printmbfont' 'pmbfn' string (default "")
- global
-This is a comma-separated list of fields for font names to be used when
-generating CJK output from |:hardcopy|. Each font name has to be preceded
-with a letter indicating the style the font is to be used for as follows:
-
- r:{font-name} font to use for normal characters
- b:{font-name} font to use for bold characters
- i:{font-name} font to use for italic characters
- o:{font-name} font to use for bold-italic characters
-
-A field with the r: prefix must be specified when doing CJK printing. The
-other fontname specifiers are optional. If a specifier is missing then
-another font will be used as follows:
-
- if b: is missing, then use r:
- if i: is missing, then use r:
- if o: is missing, then use b:
-
-Some CJK fonts do not contain characters for codes in the ASCII code range.
-Also, some characters in the CJK ASCII code ranges differ in a few code points
-from traditional ASCII characters. There are two additional fields to control
-printing of characters in the ASCII code range.
-
- c:yes Use Courier font for characters in the ASCII
- c:no (default) code range.
-
- a:yes Use ASCII character set for codes in the ASCII
- a:no (default) code range.
-
-The following is an example of specifying two multibyte fonts, one for normal
-and italic printing and one for bold and bold-italic printing, and using
-Courier to print codes in the ASCII code range but using the national
-character set: >
- :set printmbfont=r:WadaMin-Regular,b:WadaMin-Bold,c:yes
-<
- *popt-option*
-'printoptions' 'popt' string (default "")
- global
-This is a comma-separated list of items that control the format of the output
-of |:hardcopy|:
-
- left:{spec} left margin (default: 10pc)
- right:{spec} right margin (default: 5pc)
- top:{spec} top margin (default: 5pc)
- bottom:{spec} bottom margin (default: 5pc)
- {spec} is a number followed by "in" for inches, "pt"
- for points (1 point is 1/72 of an inch), "mm" for
- millimeters or "pc" for a percentage of the media
- size.
- Weird example:
- left:2in,top:30pt,right:16mm,bottom:3pc
- If the unit is not recognized there is no error and
- the default value is used.
-
- header:{nr} Number of lines to reserve for the header.
- Only the first line is actually filled, thus when {nr}
- is 2 there is one empty line. The header is formatted
- according to 'printheader'.
- header:0 Do not print a header.
- header:2 (default) Use two lines for the header
-
- syntax:n Do not use syntax highlighting. This is faster and
- thus useful when printing large files.
- syntax:y Do syntax highlighting.
- syntax:a (default) Use syntax highlighting if the printer appears to be
- able to print color or grey.
-
- number:y Include line numbers in the printed output.
- number:n (default) No line numbers.
-
- wrap:y (default) Wrap long lines.
- wrap:n Truncate long lines.
-
- duplex:off Print on one side.
- duplex:long (default) Print on both sides (when possible), bind on long
- side.
- duplex:short Print on both sides (when possible), bind on short
- side.
-
- collate:y (default) Collating: 1 2 3, 1 2 3, 1 2 3
- collate:n No collating: 1 1 1, 2 2 2, 3 3 3
-
- jobsplit:n (default) Do all copies in one print job
- jobsplit:y Do each copy as a separate print job. Useful when
- doing N-up postprocessing.
-
- portrait:y (default) Orientation is portrait.
- portrait:n Orientation is landscape.
- *a4* *letter*
- paper:A4 (default) Paper size: A4
- paper:{name} Paper size from this table:
- {name} size in cm size in inch ~
- 10x14 25.4 x 35.57 10 x 14
- A3 29.7 x 42 11.69 x 16.54
- A4 21 x 29.7 8.27 x 11.69
- A5 14.8 x 21 5.83 x 8.27
- B4 25 x 35.3 10.12 x 14.33
- B5 17.6 x 25 7.17 x 10.12
- executive 18.42 x 26.67 7.25 x 10.5
- folio 21 x 33 8.27 x 13
- ledger 43.13 x 27.96 17 x 11
- legal 21.59 x 35.57 8.5 x 14
- letter 21.59 x 27.96 8.5 x 11
- quarto 21.59 x 27.5 8.5 x 10.83
- statement 13.97 x 21.59 5.5 x 8.5
- tabloid 27.96 x 43.13 11 x 17
-
- formfeed:n (default) Treat form feed characters (0x0c) as a normal print
- character.
- formfeed:y When a form feed character is encountered, continue
- printing of the current line at the beginning of the
- first line on a new page.
-
-The item indicated with (default) is used when the item is not present. The
-values are not always used, especially when using a dialog to select the
-printer and options.
-Example: >
- :set printoptions=paper:letter,duplex:off
-
-==============================================================================
-3. PostScript Printing *postscript-printing*
- *E455* *E456* *E457* *E624*
-Provided you have enough disk space there should be no problems generating a
-PostScript file. You need to have the runtime files correctly installed (if
-you can find the help files, they probably are).
-
-There are currently a number of limitations with PostScript printing:
-
-- 'printfont' - The font name is ignored (the Courier family is always used -
- it should be available on all PostScript printers) but the font size is
- used.
-
-- 'printoptions' - The duplex setting is used when generating PostScript
- output, but it is up to the printer to take notice of the setting. If the
- printer does not support duplex printing then it should be silently ignored.
- Some printers, however, don't print at all.
-
-- 8-bit support - While a number of 8-bit print character encodings are
- supported it is possible that some characters will not print. Whether a
- character will print depends on the font in the printer knowing the
- character. Missing characters will be replaced with an upside down question
- mark, or a space if that character is also not known by the font. It may be
- possible to get all the characters in an encoding to print by installing a
- new version of the Courier font family.
-
-- Multi-byte support - Currently Vim will try to convert multibyte characters
- to the 8-bit encoding specified by 'printencoding' (or latin1 if it is
- empty). Any characters that are not successfully converted are shown as
- unknown characters. Printing will fail if Vim cannot convert the multibyte
- to the 8-bit encoding.
-
-==============================================================================
-4. Custom 8-bit Print Character Encodings *postscript-print-encoding*
- *E618* *E619*
-To use your own print character encoding when printing 8-bit character data
-you need to define your own PostScript font encoding vector. Details on how
-to define a font encoding vector is beyond the scope of this help file, but
-you can find details in the PostScript Language Reference Manual, 3rd Edition,
-published by Addison-Wesley and available in PDF form at
-http://www.adobe.com/. The following describes what you need to do for Vim to
-locate and use your print character encoding.
-
-i. Decide on a unique name for your encoding vector, one that does not clash
- with any of the recognized or standard encoding names that Vim uses (see
- |encoding-names| for a list), and that no one else is likely to use.
-ii. Copy $VIMRUNTIME/print/latin1.ps to the print subdirectory in your
- 'runtimepath' and rename it with your unique name.
-iii. Edit your renamed copy of latin1.ps, replacing all occurrences of latin1
- with your unique name (don't forget the line starting %%Title:), and
- modify the array of glyph names to define your new encoding vector. The
- array must have exactly 256 entries or you will not be able to print!
-iv. Within Vim, set 'printencoding' to your unique encoding name and then
- print your file. Vim will now use your custom print character encoding.
-
-Vim will report an error with the resource file if you change the order or
-content of the first 3 lines, other than the name of the encoding on the line
-starting %%Title: or the version number on the line starting %%Version:.
-
-[Technical explanation for those that know PostScript - Vim looks for a file
-with the same name as the encoding it will use when printing. The file
-defines a new PostScript Encoding resource called /VIM-name, where name is the
-print character encoding Vim will use.]
-
-==============================================================================
-5. PostScript CJK Printing *postscript-cjk-printing*
- *E673* *E674* *E675*
-
-Vim supports printing of Chinese, Japanese, and Korean files. Setting up Vim
-to correctly print CJK files requires setting up a few more options.
-
-Each of these countries has many standard character sets and encodings which
-require that both be specified when printing. In addition, CJK fonts normally
-do not have the concept of italic glyphs and use different weight or stroke
-style to achieve emphasis when printing. This in turn requires a different
-approach to specifying fonts to use when printing.
-
-The encoding and character set are specified with the 'printencoding' and
-'printmbcharset' options. If 'printencoding' is not specified then 'encoding'
-is used as normal. If 'printencoding' is specified then characters will be
-translated to this encoding for printing. You should ensure that the encoding
-is compatible with the character set needed for the file contents or some
-characters may not appear when printed.
-
-The fonts to use for CJK printing are specified with 'printmbfont'. This
-option allows you to specify different fonts to use when printing characters
-which are syntax highlighted with the font styles normal, italic, bold and
-bold-italic.
-
-No CJK fonts are supplied with Vim. There are some free Korean, Japanese, and
-Traditional Chinese fonts available at:
-
- http://examples.oreilly.com/cjkvinfo/adobe/samples/
-
-You can find descriptions of the various fonts in the read me file at
-
- http://examples.oreilly.de/english_examples/cjkvinfo/adobe/00README
-
-Please read your printer documentation on how to install new fonts.
-
-CJK fonts can be large containing several thousand glyphs, and it is not
-uncommon to find that they only contain a subset of a national standard. It
-is not unusual to find the fonts to not include characters for codes in the
-ASCII code range. If you find half-width Roman characters are not appearing
-in your printout then you should configure Vim to use the Courier font the
-half-width ASCII characters with 'printmbfont'. If your font does not include
-other characters then you will need to find another font that does.
-
-Another issue with ASCII characters, is that the various national character
-sets specify a couple of different glyphs in the ASCII code range. If you
-print ASCII text using the national character set you may see some unexpected
-characters. If you want true ASCII code printing then you need to configure
-Vim to output ASCII characters for the ASCII code range with 'printmbfont'.
-
-It is possible to define your own multibyte character set although this
-should not be attempted lightly. A discussion on the process if beyond the
-scope of these help files. You can find details on CMap (character map) files
-in the document 'Adobe CMap and CIDFont Files Specification, Version 1.0',
-available from http://www.adobe.com as a PDF file.
-
-==============================================================================
-6. PostScript Printing Troubleshooting *postscript-print-trouble*
- *E621*
-Usually the only sign of a problem when printing with PostScript is that your
-printout does not appear. If you are lucky you may get a printed page that
-tells you the PostScript operator that generated the error that prevented the
-print job completing.
-
-There are a number of possible causes as to why the printing may have failed:
-
-- Wrong version of the prolog resource file. The prolog resource file
- contains some PostScript that Vim needs to be able to print. Each version
- of Vim needs one particular version. Make sure you have correctly installed
- the runtime files, and don't have any old versions of a file called prolog
- in the print directory in your 'runtimepath' directory.
-
-- Paper size. Some PostScript printers will abort printing a file if they do
- not support the requested paper size. By default Vim uses A4 paper. Find
- out what size paper your printer normally uses and set the appropriate paper
- size with 'printoptions'. If you cannot find the name of the paper used,
- measure a sheet and compare it with the table of supported paper sizes listed
- for 'printoptions', using the paper that is closest in both width AND height.
- Note: The dimensions of actual paper may vary slightly from the ones listed.
- If there is no paper listed close enough, then you may want to try psresize
- from PSUtils, discussed below.
-
-- Two-sided printing (duplex). Normally a PostScript printer that does not
- support two-sided printing will ignore any request to do it. However, some
- printers may abort the job altogether. Try printing with duplex turned off.
- Note: Duplex prints can be achieved manually using PS utils - see below.
-
-- Collated printing. As with Duplex printing, most PostScript printers that
- do not support collating printouts will ignore a request to do so. Some may
- not. Try printing with collation turned off.
-
-- Syntax highlighting. Some print management code may prevent the generated
- PostScript file from being printed on a black and white printer when syntax
- highlighting is turned on, even if solid black is the only color used. Try
- printing with syntax highlighting turned off.
-
-A safe printoptions setting to try is: >
-
- :set printoptions=paper:A4,duplex:off,collate:n,syntax:n
-
-Replace "A4" with the paper size that best matches your printer paper.
-
-==============================================================================
-7. PostScript Utilities *postscript-print-util*
-
-7.1 Ghostscript
-
-Ghostscript is a PostScript and PDF interpreter that can be used to display
-and print on non-PostScript printers PostScript and PDF files. It can also
-generate PDF files from PostScript.
-
-Ghostscript will run on a wide variety of platforms.
-
-There are three available versions:
-
-- AFPL Ghostscript (formerly Aladdin Ghostscript) which is free for
- non-commercial use. It can be obtained from:
-
- http://www.cs.wisc.edu/~ghost/
-
-- GNU Ghostscript which is available under the GNU General Public License. It
- can be obtained from:
-
- ftp://mirror.cs.wisc.edu/pub/mirrors/ghost/gnu/
-
-- A commercial version for inclusion in commercial products.
-
-Additional information on Ghostscript can also be found at:
-
- http://www.ghostscript.com/
-
-Support for a number of non PostScript printers is provided in the
-distribution as standard, but if you cannot find support for your printer
-check the Ghostscript site for other printers not included by default.
-
-
-7.2 Ghostscript Previewers.
-
-The interface to Ghostscript is very primitive so a number of graphical front
-ends have been created. These allow easier PostScript file selection,
-previewing at different zoom levels, and printing. Check supplied
-documentation for full details.
-
-X11
-
-- Ghostview. Obtainable from:
-
- http://www.cs.wisc.edu/~ghost/gv/
-
-- gv. Derived from Ghostview. Obtainable from:
-
- http://wwwthep.physik.uni-mainz.de/~plass/gv/
-
- Copies (possibly not the most recent) can be found at:
-
- http://www.cs.wisc.edu/~ghost/gv/
-
-MS-Windows
-
-- GSview. Obtainable from:
-
- http://www.cs.wisc.edu/~ghost/gsview/
-
-Linux
-
-- GSview. Linux version of the popular MS-Windows previewer.
- Obtainable from:
-
- http://www.cs.wisc.edu/~ghost/gsview/
-
-- BMV. Different from Ghostview and gv in that it doesn't use X but svgalib.
- Obtainable from:
-
- ftp://sunsite.unc.edu/pub/Linux/apps/graphics/viewers/svga/bmv-1.2.tgz
-
-
-7.3 PSUtils
-
-PSUtils is a collection of utility programs for manipulating PostScript
-documents. Binary distributions are available for many platforms, as well as
-the full source. PSUtils can be found at:
-
- http://knackered.org/angus/psutils
-
-The utilities of interest include:
-
-- psnup. Convert PS files for N-up printing.
-- psselect. Select page range and order of printing.
-- psresize. Change the page size.
-- psbook. Reorder and lay out pages ready for making a book.
-
-The output of one program can be used as the input to the next, allowing for
-complex print document creation.
-
-
-N-UP PRINTING
-
-The psnup utility takes an existing PostScript file generated from Vim and
-convert it to an n-up version. The simplest way to create a 2-up printout is
-to first create a PostScript file with: >
-
- :hardcopy > test.ps
-
-Then on your command line execute: >
-
- psnup -n 2 test.ps final.ps
-
-Note: You may get warnings from some Ghostscript previewers for files produced
-by psnup - these may safely be ignored.
-
-Finally print the file final.ps to your PostScript printer with your
-platform's print command. (You will need to delete the two PostScript files
-afterwards yourself.) 'printexpr' could be modified to perform this extra
-step before printing.
-
-
-ALTERNATE DUPLEX PRINTING
-
-It is possible to achieve a poor man's version of duplex printing using the PS
-utility psselect. This utility has options -e and -o for printing just the
-even or odd pages of a PS file respectively.
-
-First generate a PS file with the 'hardcopy' command, then generate new
-files with all the odd and even numbered pages with: >
-
- psselect -o test.ps odd.ps
- psselect -e test.ps even.ps
-
-Next print odd.ps with your platform's normal print command. Then take the
-print output, turn it over and place it back in the paper feeder. Now print
-even.ps with your platform's print command. All the even pages should now
-appear on the back of the odd pages.
-
-There are a couple of points to bear in mind:
-
-1. Position of the first page. If the first page is on top of the printout
- when printing the odd pages then you need to reverse the order that the odd
- pages are printed. This can be done with the -r option to psselect. This
- will ensure page 2 is printed on the back of page 1.
- Note: it is better to reverse the odd numbered pages rather than the even
- numbered in case there are an odd number of pages in the original PS file.
-
-2. Paper flipping. When turning over the paper with the odd pages printed on
- them you may have to either flip them horizontally (along the long edge) or
- vertically (along the short edge), as well as possibly rotating them 180
- degrees. All this depends on the printer - it will be more obvious for
- desktop ink jets than for small office laser printers where the paper path
- is hidden from view.
-
-
-==============================================================================
-8. Formfeed Characters *printing-formfeed*
-
-By default Vim does not do any special processing of formfeed control
-characters. Setting the 'printoptions' formfeed item will make Vim recognize
-formfeed characters and continue printing the current line at the beginning
-of the first line on a new page. The use of formfeed characters provides
-rudimentary print control but there are certain things to be aware of.
-
-Vim will always start printing a line (including a line number if enabled)
-containing a formfeed character, even if it is the first character on the
-line. This means if a line starting with a formfeed character is the first
-line of a page then Vim will print a blank page.
-
-Since the line number is printed at the start of printing the line containing
-the formfeed character, the remainder of the line printed on the new page
-will not have a line number printed for it (in the same way as the wrapped
-lines of a long line when wrap in 'printoptions' is enabled).
-
-If the formfeed character is the last character on a line, then printing will
-continue on the second line of the new page, not the first. This is due to
-Vim processing the end of the line after the formfeed character and moving
-down a line to continue printing.
-
-Due to the points made above it is recommended that when formfeed character
-processing is enabled, printing of line numbers is disabled, and that form
-feed characters are not the last character on a line. Even then you may need
-to adjust the number of lines before a formfeed character to prevent
-accidental blank pages.
-
-==============================================================================
- vim:tw=78:ts=8:noet:ft=help:norl:
diff --git a/runtime/doc/provider.txt b/runtime/doc/provider.txt
index 9fd35f19c5..5375d971f0 100644
--- a/runtime/doc/provider.txt
+++ b/runtime/doc/provider.txt
@@ -36,7 +36,7 @@ itself).
For Python 3 plugins:
1. Make sure Python 3.4+ is available in your $PATH.
-2. Install the module (try "python" if "python3" is missing): >
+2. Install the module (try "python" if "python3" is missing): >bash
python3 -m pip install --user --upgrade pynvim
The pip `--upgrade` flag ensures that you get the latest version even if
@@ -46,7 +46,7 @@ See also |python-virtualenv|.
Note: The old "neovim" module was renamed to "pynvim".
https://github.com/neovim/neovim/wiki/Following-HEAD#20181118
-If you run into problems, uninstall _both_ then install "pynvim" again: >
+If you run into problems, uninstall _both_ then install "pynvim" again: >bash
python -m pip uninstall neovim pynvim
python -m pip install --user --upgrade pynvim
@@ -55,11 +55,11 @@ PYTHON PROVIDER CONFIGURATION ~
*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"). >
+check for has("python3"). >vim
let g:python3_host_prog = '/path/to/python3'
<
*g:loaded_python3_provider*
-To disable Python 3 support: >
+To disable Python 3 support: >vim
let g:loaded_python3_provider = 0
@@ -70,13 +70,13 @@ virtualenv for Neovim and hard-code the interpreter path via
|g:python3_host_prog| so that the "pynvim" package is not required
for each virtualenv.
-Example using pyenv: >
+Example using pyenv: >bash
pyenv install 3.4.4
pyenv virtualenv 3.4.4 py3nvim
pyenv activate py3nvim
python3 -m pip install pynvim
pyenv which python # Note the path
-The last command reports the interpreter path, add it to your init.vim: >
+The last command reports the interpreter path, add it to your init.vim: >vim
let g:python3_host_prog = '/path/to/py3nvim/bin/python'
See also: https://github.com/zchee/deoplete-jedi/wiki/Setting-up-Python-for-Neovim
@@ -90,7 +90,7 @@ Nvim supports Ruby |remote-plugin|s and the Vim legacy |ruby-vim| interface
RUBY QUICKSTART ~
-To use Ruby plugins with Nvim, install the latest "neovim" RubyGem: >
+To use Ruby plugins with Nvim, install the latest "neovim" RubyGem: >bash
gem install neovim
Run |:checkhealth| to see if your system is up-to-date.
@@ -98,7 +98,7 @@ Run |:checkhealth| to see if your system is up-to-date.
RUBY PROVIDER CONFIGURATION ~
*g:loaded_ruby_provider*
-To disable Ruby support: >
+To disable Ruby support: >vim
let g:loaded_ruby_provider = 0
<
*g:ruby_host_prog*
@@ -106,10 +106,10 @@ Command to start the Ruby host. By default this is "neovim-ruby-host". With
project-local Ruby versions (via tools like RVM or rbenv) setting this can
avoid the need to install the "neovim" gem in every project.
-To use an absolute path (e.g. to an rbenv installation): >
+To use an absolute path (e.g. to an rbenv installation): >vim
let g:ruby_host_prog = '~/.rbenv/versions/2.4.1/bin/neovim-ruby-host'
-To use the RVM "system" Ruby installation: >
+To use the RVM "system" Ruby installation: >vim
let g:ruby_host_prog = 'rvm system do neovim-ruby-host'
==============================================================================
@@ -125,7 +125,7 @@ Note: Only perl versions from 5.22 onward are supported.
PERL QUICKSTART~
-To use perl remote-plugins with Nvim, install the "Neovim::Ext" cpan package: >
+To use perl remote-plugins with Nvim, install the "Neovim::Ext" cpan package: >bash
cpanm -n Neovim::Ext
Run |:checkhealth| to see if your system is up-to-date.
@@ -133,12 +133,12 @@ Run |:checkhealth| to see if your system is up-to-date.
PERL PROVIDER CONFIGURATION~
*g:loaded_perl_provider*
-To disable Perl support: >
+To disable Perl support: >vim
:let g:loaded_perl_provider = 0
<
*g:perl_host_prog*
Command to start the Perl executable. Must be set before any
-check for has("perl"). >
+check for has("perl"). >vim
let g:perl_host_prog = '/path/to/perl'
<
==============================================================================
@@ -150,7 +150,7 @@ https://github.com/neovim/node-client/
NODEJS QUICKSTART~
-To use javascript remote-plugins with Nvim, install the "neovim" npm package: >
+To use javascript remote-plugins with Nvim, install the "neovim" npm package: >bash
npm install -g neovim
Run |:checkhealth| to see if your system is up-to-date.
@@ -158,14 +158,14 @@ Run |:checkhealth| to see if your system is up-to-date.
NODEJS PROVIDER CONFIGURATION~
*g:loaded_node_provider*
-To disable Node.js support: >
+To disable Node.js support: >vim
:let g:loaded_node_provider = 0
<
*g:node_host_prog*
Command to start the Node.js host. Setting this makes startup faster.
By default, Nvim searches for "neovim-node-host" using "npm root -g", which
-can be slow. To avoid this, set g:node_host_prog to the host path: >
+can be slow. To avoid this, set g:node_host_prog to the host path: >vim
let g:node_host_prog = '/usr/local/bin/neovim-node-host'
<
==============================================================================
@@ -176,7 +176,7 @@ a |provider| which transparently uses shell commands to communicate with the
system clipboard or any other clipboard "backend".
To ALWAYS use the clipboard for ALL operations (instead of interacting with
-the '+' and/or '*' registers explicitly): >
+the "+" and/or "*" registers explicitly): >vim
set clipboard+=unnamedplus
See 'clipboard' for details and options.
@@ -188,17 +188,18 @@ registers. Nvim looks for these clipboard tools, in order of priority:
- |g:clipboard|
- pbcopy, pbpaste (macOS)
- wl-copy, wl-paste (if $WAYLAND_DISPLAY is set)
+ - waycopy, waypaste (if $WAYLAND_DISPLAY is set)
- xclip (if $DISPLAY is set)
- xsel (if $DISPLAY is set)
- lemonade (for SSH) https://github.com/pocke/lemonade
- - doitclient (for SSH) http://www.chiark.greenend.org.uk/~sgtatham/doit/
+ - doitclient (for SSH) https://www.chiark.greenend.org.uk/~sgtatham/doit/
- win32yank (Windows)
- termux (via termux-clipboard-set, termux-clipboard-set)
- tmux (if $TMUX is set)
*g:clipboard*
To configure a custom clipboard tool, set g:clipboard to a dictionary.
-For example this configuration integrates the tmux clipboard: >
+For example this configuration integrates the tmux clipboard: >vim
let g:clipboard = {
\ 'name': 'myClipboard',
@@ -218,7 +219,8 @@ the selection until the copy command process dies. When pasting, if the copy
process has not died the cached selection is applied.
g:clipboard can also use functions (see |lambda|) instead of strings.
-For example this configuration uses the g:foo variable as a fake clipboard: >
+For example this configuration uses the g:foo variable as a fake clipboard:
+>vim
let g:clipboard = {
\ 'name': 'myClipboard',
@@ -236,11 +238,27 @@ The "copy" function stores a list of lines and the register type. The "paste"
function returns the clipboard as a `[lines, regtype]` list, where `lines` is
a list of lines and `regtype` is a register type conforming to |setreg()|.
+ *clipboard-wsl*
+For Windows WSL, try this g:clipboard definition:
+>vim
+ let g:clipboard = {
+ \ 'name': 'WslClipboard',
+ \ 'copy': {
+ \ '+': 'clip.exe',
+ \ '*': 'clip.exe',
+ \ },
+ \ 'paste': {
+ \ '+': 'powershell.exe -c [Console]::Out.Write($(Get-Clipboard -Raw).tostring().replace("`r", ""))',
+ \ '*': 'powershell.exe -c [Console]::Out.Write($(Get-Clipboard -Raw).tostring().replace("`r", ""))',
+ \ },
+ \ 'cache_enabled': 0,
+ \ }
+
==============================================================================
Paste *provider-paste* *paste*
"Paste" is a separate concept from |clipboard|: paste means "dump a bunch of
-text to the editor", whereas clipboard provides features like |quote-+| to get
+text to the editor", whereas clipboard provides features like |quote+| to get
and set the OS clipboard directly. For example, middle-click or CTRL-SHIFT-v
(macOS: CMD-v) in your terminal is "paste", not "clipboard": the terminal
application (Nvim) just gets a stream of text, it does not interact with the
@@ -266,7 +284,7 @@ many commands. Use the |cmdline-window| if you really want to paste multiple
lines to the cmdline.
You can implement a custom paste handler by redefining |vim.paste()|.
-Example: >
+Example: >lua
vim.paste = (function(lines, phase)
vim.api.nvim_put(lines, 'c', true, true)
diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt
index dab3bfa280..b1f7c927cc 100644
--- a/runtime/doc/quickfix.txt
+++ b/runtime/doc/quickfix.txt
@@ -466,7 +466,7 @@ You can parse a list of lines using 'errorformat' without creating or
modifying a quickfix list using the |getqflist()| function. Examples: >
echo getqflist({'lines' : ["F1:10:Line10", "F2:20:Line20"]})
echo getqflist({'lines' : systemlist('grep -Hn quickfix *')})
-This returns a dictionary where the 'items' key contains the list of quickfix
+This returns a dictionary where the "items" key contains the list of quickfix
entries parsed from lines. The following shows how to use a custom
'errorformat' to parse the lines without modifying the 'errorformat' option: >
echo getqflist({'efm' : '%f#%l#%m', 'lines' : ['F1#10#Line']})
@@ -585,7 +585,7 @@ can go back to the unfiltered list using the |:colder|/|:lolder| command.
quickfix command or function, the |b:changedtick|
variable is incremented. You can get the number of
this buffer using the getqflist() and getloclist()
- functions by passing the 'qfbufnr' item. For a
+ functions by passing the "qfbufnr" item. For a
location list, this buffer is wiped out when the
location list is removed.
@@ -916,7 +916,7 @@ To get the number of the current list in the stack: >
screen).
5. The errorfile is read using 'errorformat'.
6. All relevant |QuickFixCmdPost| autocommands are
- executed. See example below.
+ executed. See example below.
7. If [!] is not given the first error is jumped to.
8. The errorfile is deleted.
9. You can now move through the errors with commands
@@ -1259,6 +1259,21 @@ not "b:current_compiler". What the command actually does is the following:
For writing a compiler plugin, see |write-compiler-plugin|.
+DOTNET *compiler-dotnet*
+
+The .NET CLI compiler outputs both errors and warnings by default. The output
+may be limited to include only errors, by setting the g:dotnet_errors_only
+variable to |v:true|.
+
+The associated project name is included in each error and warning. To suppress
+the project name, set the g:dotnet_show_project_file variable to |v:false|.
+
+Example: limit output to only display errors, and suppress the project name: >
+ let dotnet_errors_only = v:true
+ let dotnet_show_project_file = v:false
+ compiler dotnet
+<
+
GCC *quickfix-gcc* *compiler-gcc*
There's one variable you can set for the GCC compiler:
@@ -1287,7 +1302,7 @@ PYUNIT COMPILER *compiler-pyunit*
This is not actually a compiler, but a unit testing framework for the
Python language. It is included into standard Python distribution
starting from version 2.0. For older versions, you can get it from
-http://pyunit.sourceforge.net.
+https://pyunit.sourceforge.net.
When you run your tests with the help of the framework, possible errors
are parsed by Vim and presented for you in quick-fix mode.
@@ -1298,8 +1313,6 @@ Useful values for the 'makeprg' options therefore are:
setlocal makeprg=./alltests.py " Run a testsuite
setlocal makeprg=python\ %:S " Run a single testcase
-Also see http://vim.sourceforge.net/tip_view.php?tip_id=280.
-
TEX COMPILER *compiler-tex*
@@ -1572,8 +1585,9 @@ A call of |:clist| writes them accordingly with their correct filenames:
Unlike the other prefixes that all match against whole lines, %P, %Q and %O
can be used to match several patterns in the same line. Thus it is possible
-to parse even nested files like in the following line:
+to parse even nested files like in the following line: >
{"file1" {"file2" error1} error2 {"file3" error3 {"file4" error4 error5}}}
+<
The %O then parses over strings that do not contain any push/pop file name
information. See |errorformat-LaTeX| for an extended example.
@@ -1823,7 +1837,7 @@ In English, that sed script:
it as a "continuation of a multi-line message."
*errorformat-ant*
-For ant (http://jakarta.apache.org/) the above errorformat has to be modified
+For ant (https://jakarta.apache.org/) the above errorformat has to be modified
to honour the leading [javac] in front of each javac output line: >
:set efm=%A\ %#[javac]\ %f:%l:\ %m,%-Z\ %#[javac]\ %p^,%-C%.%#
@@ -1933,13 +1947,13 @@ by Vim.
The default format for the lines displayed in the quickfix window and location
list window is:
-
+>
<filename>|<lnum> col <col>|<text>
-
+<
The values displayed in each line correspond to the "bufnr", "lnum", "col" and
"text" fields returned by the |getqflist()| function.
-For some quickfix/location lists, the displayed text need to be customized.
+For some quickfix/location lists, the displayed text needs to be customized.
For example, if only the filename is present for a quickfix entry, then the
two "|" field separator characters after the filename are not needed. Another
use case is to customize the path displayed for a filename. By default, the
@@ -1965,7 +1979,7 @@ The function should return a single line of text to display in the quickfix
window for each entry from start_idx to end_idx. The function can obtain
information about the entries using the |getqflist()| function and specifying
the quickfix list identifier "id". For a location list, getloclist() function
-can be used with the 'winid' argument. If an empty list is returned, then the
+can be used with the "winid" argument. If an empty list is returned, then the
default format is used to display all the entries. If an item in the returned
list is an empty string, then the default format is used to display the
corresponding entry.
diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt
index 6f16db5cc2..d17df3cd61 100644
--- a/runtime/doc/quickref.txt
+++ b/runtime/doc/quickref.txt
@@ -3,7 +3,8 @@
VIM REFERENCE MANUAL by Bram Moolenaar
- Quick reference guide
+==============================================================================
+Quick reference guide
*quickref* *Contents*
tag subject tag subject ~
@@ -30,10 +31,10 @@
|Q_fo| Folding
------------------------------------------------------------------------------
-N is used to indicate an optional count that can be given before the command.
-------------------------------------------------------------------------------
*Q_lr* Left-right motions
+N is used to indicate an optional count that can be given before the command.
+
|h| N h left (also: CTRL-H, <BS>, or <Left> key)
|l| N l right (also: <Space> or <Right> key)
|0| 0 to first character in the line (also: <Home> key)
@@ -56,6 +57,7 @@ N is used to indicate an optional count that can be given before the command.
|;| N ; repeat the last "f", "F", "t", or "T" N times
|,| N , repeat the last "f", "F", "t", or "T" N times in
opposite direction
+
------------------------------------------------------------------------------
*Q_ud* Up-down motions
@@ -73,6 +75,7 @@ N is used to indicate an optional count that can be given before the command.
given, otherwise it is the |%| command
|gk| N gk up N screen lines (differs from "k" when line wraps)
|gj| N gj down N screen lines (differs from "j" when line wraps)
+
------------------------------------------------------------------------------
*Q_tm* Text object motions
@@ -105,6 +108,7 @@ N is used to indicate an optional count that can be given before the command.
|]#| N ]# N times forward to unclosed "#else" or "#endif"
|[star| N [* N times back to start of comment "/*"
|]star| N ]* N times forward to end of comment "*/"
+
------------------------------------------------------------------------------
*Q_pa* Pattern searches
@@ -168,6 +172,7 @@ N is used to indicate an optional count that can be given before the command.
b[+num] [num] identical to s[+num] above (mnemonic: begin)
b[-num] [num] identical to s[-num] above (mnemonic: begin)
;{search-command} execute {search-command} next
+
------------------------------------------------------------------------------
*Q_ma* Marks and motions
@@ -188,6 +193,7 @@ N is used to indicate an optional count that can be given before the command.
|CTRL-O| N CTRL-O go to Nth older position in jump list
|CTRL-I| N CTRL-I go to Nth newer position in jump list
|:ju| :ju[mps] print the jump list
+
------------------------------------------------------------------------------
*Q_vm* Various motions
@@ -202,6 +208,7 @@ N is used to indicate an optional count that can be given before the command.
|go| N go go to Nth byte in the buffer
|:go| :[range]go[to] [off] go to [off] byte in the buffer
+
------------------------------------------------------------------------------
*Q_ta* Using tags
@@ -229,6 +236,7 @@ N is used to indicate an optional count that can be given before the command.
|:ptjump| :ptj[ump] like ":tjump" but show tag in preview window
|:pclose| :pc[lose] close tag preview window
|CTRL-W_z| CTRL-W z close tag preview window
+
------------------------------------------------------------------------------
*Q_sc* Scrolling
@@ -247,6 +255,7 @@ These only work when 'wrap' is off:
|zl| N zl scroll screen N characters to the left
|zH| N zH scroll screen half a screenwidth to the right
|zL| N zL scroll screen half a screenwidth to the left
+
------------------------------------------------------------------------------
*Q_in* Inserting text
@@ -263,6 +272,7 @@ These only work when 'wrap' is off:
in Visual block mode:
|v_b_I| I insert the same text in front of all the selected lines
|v_b_A| A append the same text after all the selected lines
+
------------------------------------------------------------------------------
*Q_ai* Insert mode keys
@@ -279,6 +289,7 @@ moving around:
|i_<S-Up>| shift-up/down one screenful backward/forward
|i_<End>| <End> cursor after last character in the line
|i_<Home>| <Home> cursor to first character in the line
+
------------------------------------------------------------------------------
*Q_ss* Special keys in Insert mode
@@ -313,6 +324,7 @@ moving around:
|i_0_CTRL-D| 0 CTRL-D delete all indent in the current line
|i_^_CTRL-D| ^ CTRL-D delete all indent in the current line,
restore indent in next line
+
------------------------------------------------------------------------------
*Q_di* Digraphs
@@ -325,12 +337,14 @@ In Insert or Command-line mode:
enter digraph
|i_digraph| {char1} <BS> {char2}
enter digraph if 'digraph' option set
+
------------------------------------------------------------------------------
*Q_si* Special inserts
|:r| :r [file] insert the contents of [file] below the cursor
|:r!| :r! {command} insert the standard output of {command} below the
cursor
+
------------------------------------------------------------------------------
*Q_de* Deleting text
@@ -346,6 +360,7 @@ In Insert or Command-line mode:
|gJ| N gJ like "J", but without inserting spaces
|v_gJ| {visual}gJ like "{visual}J", but without inserting spaces
|:d| :[range]d [x] delete [range] lines [into register x]
+
------------------------------------------------------------------------------
*Q_cm* Copying and moving text
@@ -363,6 +378,7 @@ In Insert or Command-line mode:
|[p| N [p like P, but adjust indent to current line
|gp| N gp like p, but leave cursor after the new text
|gP| N gP like P, but leave cursor after the new text
+
------------------------------------------------------------------------------
*Q_ch* Changing text
@@ -418,6 +434,7 @@ In Insert or Command-line mode:
left-align the lines in [range] (with [indent])
|:ri| :[range]ri[ght] [width]
right-align the lines in [range]
+
------------------------------------------------------------------------------
*Q_co* Complex changes
@@ -444,6 +461,7 @@ In Insert or Command-line mode:
|:ret| :[range]ret[ab][!] [tabstop]
set 'tabstop' to new value and adjust white space
accordingly
+
------------------------------------------------------------------------------
*Q_vi* Visual mode
@@ -457,6 +475,7 @@ In Insert or Command-line mode:
|v_v| v highlight characters or stop highlighting
|v_V| V highlight linewise or stop highlighting
|v_CTRL-V| CTRL-V highlight blockwise or stop highlighting
+
------------------------------------------------------------------------------
*Q_to* Text objects (only in Visual mode or after an operator)
@@ -509,6 +528,7 @@ In Insert or Command-line mode:
|:sl| :sl[eep] [sec]
don't do anything for [sec] seconds
|gs| N gs goto Sleep for N seconds
+
------------------------------------------------------------------------------
*Q_km* Key mapping
@@ -556,6 +576,7 @@ In Insert or Command-line mode:
like ":mkvimrc", but store current files,
windows, etc. too, to be able to continue
this session later
+
------------------------------------------------------------------------------
*Q_ab* Abbreviations
@@ -570,6 +591,7 @@ In Insert or Command-line mode:
|:abclear| :abc[lear] remove all abbreviations
|:cabclear| :cabc[lear] remove all abbr's for Cmdline mode
|:iabclear| :iabc[lear] remove all abbr's for Insert mode
+
------------------------------------------------------------------------------
*Q_op* Options
@@ -615,10 +637,6 @@ Short explanation of each option: *option-list*
'backupdir' 'bdir' list of directories for the backup file
'backupext' 'bex' extension used for the backup file
'backupskip' 'bsk' no backup for files that match these patterns
-'balloondelay' 'bdlay' delay in mS before a balloon may pop up
-'ballooneval' 'beval' switch on balloon evaluation in the GUI
-'balloonevalterm' 'bevalterm' switch on balloon evaluation in the terminal
-'balloonexpr' 'bexpr' expression to show in balloon
'belloff' 'bo' do not ring the bell for these reasons
'binary' 'bin' read/write/edit file in binary mode
'bomb' prepend a Byte Order Mark to the file
@@ -649,17 +667,12 @@ Short explanation of each option: *option-list*
'complete' 'cpt' specify how Insert mode completion works
'completefunc' 'cfu' function to be used for Insert mode completion
'completeopt' 'cot' options for Insert mode completion
+'completeslash' 'csl' like 'shellslash' for completion
'concealcursor' 'cocu' whether concealable text is hidden in cursor line
'conceallevel' 'cole' whether concealable text is shown or hidden
'confirm' 'cf' ask what to do about unsaved/read-only files
'copyindent' 'ci' make 'autoindent' use existing indent structure
'cpoptions' 'cpo' flags for Vi-compatible behavior
-'cscopepathcomp' 'cspc' how many components of the path to show
-'cscopeprg' 'csprg' command to execute cscope
-'cscopequickfix' 'csqf' use quickfix window for cscope results
-'cscoperelative' 'csre' Use cscope.out path basename as prefix
-'cscopetag' 'cst' use cscope for tag commands
-'cscopetagorder' 'csto' determines ":cstag" search order
'cursorbind' 'crb' move cursor in window as it moves in other windows
'cursorcolumn' 'cuc' highlight the screen column of the cursor
'cursorline' 'cul' highlight the screen line of the cursor
@@ -676,6 +689,7 @@ Short explanation of each option: *option-list*
'display' 'dy' list of flags for how to display text
'eadirection' 'ead' in which direction 'equalalways' works
'encoding' 'enc' encoding used internally
+'endoffile' 'eof' write CTRL-Z at end of the file
'endofline' 'eol' write <EOL> for last line in file
'equalalways' 'ea' windows are automatically made the same size
'equalprg' 'ep' external program to use for "=" command
@@ -684,7 +698,7 @@ Short explanation of each option: *option-list*
'errorformat' 'efm' description of the lines in the error file
'eventignore' 'ei' autocommand events that are ignored
'expandtab' 'et' use spaces when <Tab> is inserted
-'exrc' 'ex' read .nvimrc and .exrc in the current directory
+'exrc' 'ex' read init files in the current directory
'fileencoding' 'fenc' file encoding for multibyte text
'fileencodings' 'fencs' automatically detected character encodings
'fileformat' 'ff' file format used for file I/O
@@ -759,6 +773,7 @@ Short explanation of each option: *option-list*
'lines' number of lines in the display
'linespace' 'lsp' number of pixel lines to use between characters
'lisp' automatic indenting for Lisp
+'lispoptions' 'lop' changes how Lisp indenting is done
'lispwords' 'lw' words that change how lisp indenting works
'list' show <Tab> and <EOL>
'listchars' 'lcs' characters for displaying in list mode
@@ -785,6 +800,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
+'mousemoveevent' 'mousemev' report mouse moves with <MouseMove>
'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
@@ -801,23 +817,12 @@ Short explanation of each option: *option-list*
'patchexpr' 'pex' expression used to patch a file
'patchmode' 'pm' keep the oldest version of a file
'path' 'pa' list of directories searched with "gf" et.al.
-'perldll' name of the Perl dynamic library
'preserveindent' 'pi' preserve the indent structure when reindenting
'previewheight' 'pvh' height of the preview window
'previewpopup' 'pvp' use popup window for preview
'previewwindow' 'pvw' identifies the preview window
-'printdevice' 'pdev' name of the printer to be used for :hardcopy
-'printencoding' 'penc' encoding to be used for printing
-'printexpr' 'pexpr' expression used to print PostScript for :hardcopy
-'printfont' 'pfn' name of the font to be used for :hardcopy
-'printheader' 'pheader' format of the header used for :hardcopy
-'printmbcharset' 'pmbcs' CJK character set to be used for :hardcopy
-'printmbfont' 'pmbfn' font names to be used for CJK output of :hardcopy
-'printoptions' 'popt' controls the format of :hardcopy output
-'pumheight' 'ph' maximum height of the popup menu
+'pumheight' 'ph' maximum number of items to show in the popup menu
'pumwidth' 'pw' minimum width of the popup menu
-'pythondll' name of the Python 2 dynamic library
-'pythonthreedll' name of the Python 3 dynamic library
'pyxversion' 'pyx' Python version used for pyx* commands
'quoteescape' 'qe' escape characters used in a string
'readonly' 'ro' disallow writing the buffer
@@ -828,7 +833,6 @@ Short explanation of each option: *option-list*
'revins' 'ri' inserting characters will work backwards
'rightleft' 'rl' window is right-to-left oriented
'rightleftcmd' 'rlc' commands for which editing works right-to-left
-'rubydll' name of the Ruby dynamic library
'ruler' 'ru' show cursor line and column in the status line
'rulerformat' 'ruf' custom format for the ruler
'runtimepath' 'rtp' list of directories used for runtime files
@@ -856,7 +860,8 @@ Short explanation of each option: *option-list*
'shiftwidth' 'sw' number of spaces to use for (auto)indent step
'shortmess' 'shm' list of flags, reduce length of messages
'showbreak' 'sbr' string to use at the start of wrapped lines
-'showcmd' 'sc' show (partial) command in status line
+'showcmd' 'sc' show (partial) command somewhere
+'showcmdloc' 'sloc' where to show (partial) command
'showfulltag' 'sft' show full tag pattern when completing tag
'showmatch' 'sm' briefly jump to matching bracket if insert one
'showmode' 'smd' message on status line to show current mode
@@ -875,8 +880,10 @@ Short explanation of each option: *option-list*
'spelloptions' 'spo' options for spell checking
'spellsuggest' 'sps' method(s) used to suggest spelling corrections
'splitbelow' 'sb' new window from split is below the current one
+'splitkeep' 'spk' determines scroll behavior for split windows
'splitright' 'spr' new window is put right of the current one
'startofline' 'sol' commands move cursor to first non-blank in line
+'statuscolumn' 'stc' custom format for the status column
'statusline' 'stl' custom format for the status line
'suffixes' 'su' suffixes that are ignored with multiple match
'suffixesadd' 'sua' suffixes added when searching for a file
@@ -889,6 +896,7 @@ Short explanation of each option: *option-list*
'tabstop' 'ts' number of spaces that <Tab> in file uses
'tagbsearch' 'tbs' use binary searching in tags files
'tagcase' 'tc' how to handle case when searching in tags files
+'tagfunc' 'tfu' function to get list of tag matches
'taglength' 'tl' number of significant characters for a tag
'tagrelative' 'tr' file names in tag file are relative
'tags' 'tag' list of file names used by the tag command
@@ -947,18 +955,21 @@ Short explanation of each option: *option-list*
'writeany' 'wa' write to file with no need for "!" override
'writebackup' 'wb' make a backup before overwriting a file
'writedelay' 'wd' delay this many msec for each char (for debug)
+
------------------------------------------------------------------------------
*Q_ur* Undo/Redo commands
|u| N u undo last N changes
|CTRL-R| N CTRL-R redo last N undone changes
|U| U restore last changed line
+
------------------------------------------------------------------------------
*Q_et* External commands
|:!| :!{command} execute {command} with a shell
|K| K lookup keyword under the cursor with
'keywordprg' program (default: "man")
+
------------------------------------------------------------------------------
*Q_qf* Quickfix commands
@@ -982,6 +993,7 @@ Short explanation of each option: *option-list*
error
|:grep| :gr[ep] [args] execute 'grepprg' to find matches and jump to
the first one
+
------------------------------------------------------------------------------
*Q_vc* Various commands
@@ -1007,6 +1019,7 @@ Short explanation of each option: *option-list*
unsaved changes or read-only files
|:browse| :browse {command} open/read/write file, using a
file selection dialog
+
------------------------------------------------------------------------------
*Q_ce* Command-line editing
@@ -1053,6 +1066,7 @@ Context-sensitive completion on the command-line:
to next match
|c_CTRL-P| CTRL-P after 'wildchar' with multiple matches: go
to previous match
+
------------------------------------------------------------------------------
*Q_ra* Ex ranges
@@ -1073,6 +1087,7 @@ Context-sensitive completion on the command-line:
(default: 1)
|:range| -[num] subtract [num] from the preceding line
number (default: 1)
+
------------------------------------------------------------------------------
*Q_ex* Special Ex characters
@@ -1105,6 +1120,7 @@ Context-sensitive completion on the command-line:
|::r| :r root (extension removed)
|::e| :e extension
|::s| :s/{pat}/{repl}/ substitute {pat} with {repl}
+
------------------------------------------------------------------------------
*Q_st* Starting Vim
@@ -1141,6 +1157,7 @@ Context-sensitive completion on the command-line:
|--help| --help show list of arguments and exit
|--version| --version show version info and exit
|--| - read file from stdin
+
------------------------------------------------------------------------------
*Q_ed* Editing a file
@@ -1160,6 +1177,7 @@ Context-sensitive completion on the command-line:
position
|:file| :f[ile] {name} set the current file name to {name}
|:files| :files show alternate file names
+
------------------------------------------------------------------------------
*Q_fl* Using the argument list |argument-list|
@@ -1180,6 +1198,7 @@ Context-sensitive completion on the command-line:
|:Next| :N[ext] :sN[ext] edit previous file
|:first| :fir[st] :sfir[st] edit first file
|:last| :la[st] :sla[st] edit last file
+
------------------------------------------------------------------------------
*Q_wq* Writing and quitting
@@ -1217,6 +1236,7 @@ Context-sensitive completion on the command-line:
|:stop| :st[op][!] suspend Vim or start new shell; if 'aw' option
is set and [!] not given write the buffer
|CTRL-Z| CTRL-Z same as ":stop"
+
------------------------------------------------------------------------------
*Q_ac* Automatic Commands
@@ -1248,6 +1268,7 @@ Context-sensitive completion on the command-line:
with {pat}
|:autocmd| :au! {event} {pat} {cmd} remove all autocommands for {event}
with {pat} and enter new one
+
------------------------------------------------------------------------------
*Q_wi* Multi-window commands
@@ -1293,6 +1314,7 @@ Context-sensitive completion on the command-line:
|CTRL-W_>| CTRL-W > increase current window width
|CTRL-W_bar| CTRL-W | set current window width (default:
widest possible)
+
------------------------------------------------------------------------------
*Q_bu* Buffer list commands
@@ -1314,6 +1336,7 @@ Context-sensitive completion on the command-line:
|:bfirst| :bfirst :sbfirst to first arg/buf
|:blast| :blast :sblast to last arg/buf
|:bmodified| :[N]bmod [N] :[N]sbmod [N] to Nth modified buf
+
------------------------------------------------------------------------------
*Q_sy* Syntax Highlighting
@@ -1340,6 +1363,7 @@ Context-sensitive completion on the command-line:
|:filetype| :filetype plugin indent on
switch on file type detection, with
automatic indenting and settings
+
------------------------------------------------------------------------------
*Q_gu* GUI commands
@@ -1352,6 +1376,7 @@ Context-sensitive completion on the command-line:
add toolbar item, giving {rhs}
|:tmenu| :tmenu {mpath} {text} add tooltip to menu {mpath}
|:unmenu| :unmenu {mpath} remove menu {mpath}
+
------------------------------------------------------------------------------
*Q_fo* Folding
diff --git a/runtime/doc/remote.txt b/runtime/doc/remote.txt
index 0c1e3438de..4610088ab0 100644
--- a/runtime/doc/remote.txt
+++ b/runtime/doc/remote.txt
@@ -52,6 +52,10 @@ The following command line arguments are available:
*--remote-expr*
--remote-expr {expr} Evaluate {expr} in server and print the result
on stdout.
+ *--remote-ui*
+ --remote-ui Display the UI of the server in the terminal.
+ Fully interactive: keyboard and mouse input
+ are forwarded to the server.
*--server*
--server {addr} Connect to the named pipe or socket at the
given address for executing remote commands.
diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt
index 508565dea4..1bbd20702b 100644
--- a/runtime/doc/repeat.txt
+++ b/runtime/doc/repeat.txt
@@ -83,7 +83,8 @@ pattern and do not match another pattern: >
This first finds all lines containing "found", but only executes {cmd} when
there is no match for "notfound".
-To execute a non-Ex command, you can use the `:normal` command: >
+Any Ex command can be used, see |ex-cmd-index|. To execute a Normal mode
+command, you can use the `:normal` command: >
:g/pat/normal {commands}
Make sure that {commands} ends with a whole command, otherwise Vim will wait
for you to type the rest of the command for each match. The screen will not
@@ -288,7 +289,7 @@ For writing a Vim script, see chapter 41 of the user manual |usr_41.txt|.
how this can be useful.
This is normally done automatically during startup,
- after loading your .vimrc file. With this command it
+ after loading your |vimrc| file. With this command it
can be done earlier.
Packages will be loaded only once. Using
@@ -470,7 +471,7 @@ 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
+To add a comment in between the lines start with `'"\ '`. Notice the space
after the backslash. Example: >
let array = [
"\ first entry comment
@@ -490,29 +491,40 @@ Rationale:
continuation lines to be part of the comment. Since it was like this
for a long time, when making it possible to add a comment halfway a
sequence of continuation lines, it was not possible to use \", since
- that was a valid continuation line. Using '"\ ' comes closest, even
+ that was a valid continuation line. Using `'"\ '` comes closest, even
though it may look a bit weird. Requiring the space after the
backslash is to make it very unlikely this is a normal comment line.
==============================================================================
Using Vim packages *packages*
-A Vim package is a directory that contains one or more plugins. The
-advantages over normal plugins:
-- A package can be downloaded as an archive and unpacked in its own directory.
- Thus the files are not mixed with files of other plugins. That makes it
- easy to update and remove.
-- A package can be a git, mercurial, etc. repository. That makes it really
- easy to update.
-- A package can contain multiple plugins that depend on each other.
-- A package can contain plugins that are automatically loaded on startup and
- ones that are only loaded when needed with `:packadd`.
+A Vim "package" is a directory that contains |plugin|s. Compared to normal
+plugins, a package can...
+- be downloaded as an archive and unpacked in its own directory, so the files
+ are not mixed with files of other plugins.
+- be a git, mercurial, etc. repository, thus easy to update.
+- contain multiple plugins that depend on each other.
+- contain plugins that are automatically loaded on startup ("start" packages,
+ located in "pack/*/start/*") and ones that are only loaded when needed with
+ |:packadd| ("opt" packages, located in "pack/*/opt/*").
+ *runtime-search-path*
+Nvim searches for |:runtime| files in:
+ 1. all paths in 'runtimepath'
+ 2. all "pack/*/start/*" dirs
+
+Note that the "pack/*/start/*" paths are not explicitly included in
+'runtimepath', so they will not be reported by ":set rtp" or "echo &rtp".
+Scripts can use |nvim_list_runtime_paths()| to list all used directories, and
+|nvim_get_runtime_file()| to query for specific files or sub-folders within
+the runtime path. Example: >
+ " List all runtime dirs and packages with Lua paths.
+ :echo nvim_get_runtime_file("lua/", v:true)
Using a package and loading automatically ~
-Let's assume your Vim files are in the "~/.local/share/nvim/site" directory
-and you want to add a package from a zip archive "/tmp/foopack.zip":
+Let's assume your Nvim files are in "~/.local/share/nvim/site" and you want to
+add a package from a zip archive "/tmp/foopack.zip":
% mkdir -p ~/.local/share/nvim/site/pack/foo
% cd ~/.local/share/nvim/site/pack/foo
% unzip /tmp/foopack.zip
@@ -525,28 +537,22 @@ You would now have these files under ~/.local/share/nvim/site:
pack/foo/start/foobar/syntax/some.vim
pack/foo/opt/foodebug/plugin/debugger.vim
- *runtime-search-path*
-When runtime files are searched for, first all paths in 'runtimepath' are
-searched, then all "pack/*/start/*" dirs are searched. However, package entries
-are not visible in `:set rtp` or `echo &rtp`, as the final concatenated path
-would be too long and get truncated. To list all used directories, use
-|nvim_list_runtime_paths()|. In addition |nvim_get_runtime_file()| can be used
-to query for specific files or sub-folders within the runtime path. For
-instance to list all runtime dirs and packages with lua paths, use >
-
- :echo nvim_get_runtime_file("lua/", v:true)
+On startup after processing your |config|, Nvim scans all directories in
+'packpath' for plugins in "pack/*/start/*", then loads the plugins.
-<When Vim starts up, after processing your .vimrc, it scans all directories in
-'packpath' for plugins under the "pack/*/start" directory, and all the plugins
-are loaded.
+To allow for calling into package functionality while parsing your |vimrc|,
+|:colorscheme| and |autoload| will both automatically search under 'packpath'
+as well in addition to 'runtimepath'. See the documentation for each for
+details.
-In the example Vim will find "pack/foo/start/foobar/plugin/foo.vim" and load it.
+In the example Nvim will find "pack/foo/start/foobar/plugin/foo.vim" and load
+it.
-If the "foobar" plugin kicks in and sets the 'filetype' to "some", Vim will
+If the "foobar" plugin kicks in and sets the 'filetype' to "some", Nvim will
find the syntax/some.vim file, because its directory is in the runtime search
path.
-Vim will also load ftdetect files, if there are any.
+Nvim will also load ftdetect files, if there are any.
Note that the files under "pack/foo/opt" are not loaded automatically, only the
ones under "pack/foo/start". See |pack-add| below for how the "opt" directory
@@ -588,12 +594,12 @@ This searches for "pack/*/opt/foodebug" in 'packpath' and will find
it.
This could be done if some conditions are met. For example, depending on
-whether Vim supports a feature or a dependency is missing.
+whether Nvim supports a feature or a dependency is missing.
You can also load an optional plugin at startup, by putting this command in
-your |.vimrc|: >
+your |config|: >
:packadd! foodebug
-The extra "!" is so that the plugin isn't loaded if Vim was started with
+The extra "!" is so that the plugin isn't loaded if Nvim was started with
|--noplugin|.
It is perfectly normal for a package to only have files in the "opt"
@@ -605,7 +611,7 @@ Where to put what ~
Since color schemes, loaded with `:colorscheme`, are found below
"pack/*/start" and "pack/*/opt", you could put them anywhere. We recommend
you put them below "pack/*/opt", for example
-".vim/pack/mycolors/opt/dark/colors/very_dark.vim".
+"~/.config/nvim/pack/mycolors/opt/dark/colors/very_dark.vim".
Filetype plugins should go under "pack/*/start", so that they are always
found. Unless you have more than one plugin for a file type and want to
@@ -683,8 +689,8 @@ found automatically. Your package would have these files:
< pack/foo/start/lib/autoload/foolib.vim >
func foolib#getit()
-This works, because start packages will be used to look for autoload files,
-when sourcing the plugins.
+This works, because start packages will be searched for autoload files, when
+sourcing the plugins.
==============================================================================
Debugging scripts *debug-scripts*
diff --git a/runtime/doc/rileft.txt b/runtime/doc/rileft.txt
index aa11462595..4f68ab562c 100644
--- a/runtime/doc/rileft.txt
+++ b/runtime/doc/rileft.txt
@@ -12,8 +12,9 @@ These functions were originally created by Avner Lottem:
E-mail: alottem@iil.intel.com
Phone: +972-4-8307322
+------------------------------------------------------------------------------
Introduction
-------------
+
Some languages such as Arabic, Farsi, Hebrew (among others) require the
ability to display their text from right-to-left. Files in those languages
are stored conventionally and the right-to-left requirement is only a
@@ -32,8 +33,9 @@ as this kind of support is out of the scope of a simple addition to an
existing editor (and it's not sanctioned by Unicode either).
+------------------------------------------------------------------------------
Highlights
-----------
+
o Editing left-to-right files as in the original Vim, no change.
o Viewing and editing files in right-to-left windows. File orientation
@@ -56,11 +58,11 @@ o Many languages use and require right-to-left support. These languages
current supported languages include - |arabic.txt| and |hebrew.txt|.
+------------------------------------------------------------------------------
Of Interest...
---------------
o Invocations
- -----------
+
+ 'rightleft' ('rl') sets window orientation to right-to-left.
+ 'delcombine' ('deco'), boolean, if editing UTF-8 encoded languages,
allows one to remove a composing character which gets superimposed
@@ -69,7 +71,7 @@ o Invocations
(such as search) to be utilized in right-to-left orientation as well.
o Typing backwards *ins-reverse*
- ----------------
+
In lieu of using the full-fledged 'rightleft' option, one can opt for
reverse insertion. When the 'revins' (reverse insert) option is set,
inserting happens backwards. This can be used to type right-to-left
@@ -85,15 +87,16 @@ o Typing backwards *ins-reverse*
in the status line when reverse Insert mode is active.
o Pasting when in a rightleft window
- ----------------------------------
+
When cutting text with the mouse and pasting it in a rightleft window
the text will be reversed, because the characters come from the cut buffer
from the left to the right, while inserted in the file from the right to
the left. In order to avoid it, toggle 'revins' before pasting.
+------------------------------------------------------------------------------
Bugs
-----
+
o Does not handle CTRL-A and CTRL-X commands (add and subtract) correctly
when in rightleft window.
diff --git a/runtime/doc/russian.txt b/runtime/doc/russian.txt
index a2bc9f3b5e..8d3ed360c8 100644
--- a/runtime/doc/russian.txt
+++ b/runtime/doc/russian.txt
@@ -45,7 +45,7 @@ If you wish to use messages, help files, menus and other items translated to
Russian, you will need to install the RuVim Language Pack, available in
different codepages from
- http://www.sourceforge.net/projects/ruvim/
+ https://www.sourceforge.net/projects/ruvim/
After downloading an archive from RuVim project, unpack it into your
$VIMRUNTIME directory. We recommend using UTF-8 archive.
diff --git a/runtime/doc/sign.txt b/runtime/doc/sign.txt
index a2a5645baa..d09d0f226f 100644
--- a/runtime/doc/sign.txt
+++ b/runtime/doc/sign.txt
@@ -334,8 +334,10 @@ See |sign_getplaced()| for the equivalent Vim script function.
:sign place group=* buffer={nr}
List signs in all the groups placed in buffer {nr}.
+:sign place List placed signs in the global group in all files.
+
:sign place group={group}
- List placed signs in all sign groups in all the files.
+ List placed signs with sign group {group} in all files.
:sign place group=*
List placed signs in all sign groups in all files.
@@ -381,15 +383,14 @@ sign_define({list})
icon full path to the bitmap file for the sign.
linehl highlight group used for the whole line the
sign is placed in.
+ numhl highlight group used for the line number where
+ the sign is placed.
text text that is displayed when there is no icon
or the GUI is not being used.
texthl highlight group used for the text item
culhl highlight group used for the text item when
the cursor is on the same line as the sign and
'cursorline' is enabled.
- numhl highlight group used for 'number' column at the
- associated line. Overrides |hl-LineNr|,
- |hl-CursorLineNr|.
If the sign named {name} already exists, then the attributes
of the sign are updated.
@@ -431,6 +432,8 @@ sign_getdefined([{name}]) *sign_getdefined()*
linehl highlight group used for the whole line the
sign is placed in; not present if not set.
name name of the sign
+ numhl highlight group used for the line number where
+ the sign is placed; not present if not set.
text text that is displayed when there is no icon
or the GUI is not being used.
texthl highlight group used for the text item; not
@@ -439,9 +442,6 @@ sign_getdefined([{name}]) *sign_getdefined()*
the cursor is on the same line as the sign and
'cursorline' is enabled; not present if not
set.
- numhl highlight group used for 'number' column at the
- associated line. Overrides |hl-LineNr|,
- |hl-CursorLineNr|; not present if not set.
Returns an empty List if there are no signs and when {name} is
not found.
@@ -606,9 +606,9 @@ sign_placelist({list})
then a new unique identifier is allocated.
Otherwise the specified number is used. See
|sign-identifier| for more information.
- lnum line number in the buffer {buf} where the
- sign is to be placed. For the accepted values,
- see |line()|.
+ lnum line number in the buffer where the sign is to
+ be placed. For the accepted values, see
+ |line()|.
name name of the sign to place. See |sign_define()|
for more information.
priority priority of the sign. When multiple signs are
diff --git a/runtime/doc/spell.txt b/runtime/doc/spell.txt
index 23d5905ec3..98a6af1b8b 100644
--- a/runtime/doc/spell.txt
+++ b/runtime/doc/spell.txt
@@ -482,7 +482,7 @@ You can create a Vim spell file from the .aff and .dic files that Myspell
uses. Myspell is used by OpenOffice.org and Mozilla. The OpenOffice .oxt
files are zip files which contain the .aff and .dic files. You should be able
to find them here:
- http://extensions.services.openoffice.org/dictionary
+ https://extensions.services.openoffice.org/dictionary
The older, OpenOffice 2 files may be used if this doesn't work:
http://wiki.services.openoffice.org/wiki/Dictionaries
You can also use a plain word list. The results are the same, the choice
@@ -764,13 +764,13 @@ them before the Vim word list is made. The tools for this can be found in the
The format for the affix and word list files is based on what Myspell uses
(the spell checker of Mozilla and OpenOffice.org). A description can be found
here:
- http://lingucomponent.openoffice.org/affix.readme ~
+ https://lingucomponent.openoffice.org/affix.readme ~
Note that affixes are case sensitive, this isn't obvious from the description.
Vim supports quite a few extras. They are described below |spell-affix-vim|.
Attempts have been made to keep this compatible with other spell checkers, so
that the same files can often be used. One other project that offers more
-than Myspell is Hunspell ( http://hunspell.sf.net ).
+than Myspell is Hunspell ( https://hunspell.github.io ).
WORD LIST FORMAT *spell-dic-format*
@@ -886,7 +886,7 @@ right encoding.
*spell-AUTHOR* *spell-EMAIL* *spell-COPYRIGHT*
NAME Name of the language
VERSION 1.0.1 with fixes
- HOME http://www.myhome.eu
+ HOME https://www.example.com
AUTHOR John Doe
EMAIL john AT Doe DOT net
COPYRIGHT LGPL
@@ -992,8 +992,8 @@ Note: even when using "num" or "long" the number of flags available to
compounding and prefixes is limited to about 250.
-AFFIXES
- *spell-PFX* *spell-SFX*
+AFFIXES *spell-PFX* *spell-SFX*
+
The usual PFX (prefix) and SFX (suffix) lines are supported (see the Myspell
documentation or the Aspell manual:
http://aspell.net/man-html/Affix-Compression.html).
@@ -1583,7 +1583,7 @@ CHECKCOMPOUNDTRIPLE (Hunspell) *spell-CHECKCOMPOUNDTRIPLE*
Forbid three identical characters when compounding. Not
supported.
-CHECKSHARPS (Hunspell)) *spell-CHECKSHARPS*
+CHECKSHARPS (Hunspell) *spell-CHECKSHARPS*
SS letter pair in uppercased (German) words may be upper case
sharp s (ß). Not supported.
diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt
index d57a85423c..179bacdb24 100644
--- a/runtime/doc/starting.txt
+++ b/runtime/doc/starting.txt
@@ -9,7 +9,7 @@ Starting Vim *starting*
Type |gO| to see the table of contents.
==============================================================================
-Nvim arguments *vim-arguments*
+Nvim arguments *cli-arguments*
Most often, Nvim is started to edit a single file with the command: >
@@ -31,11 +31,11 @@ filename One or more file names. The first one will be the current
To avoid a file name starting with a '-' being interpreted as
an option, precede the arglist with "--", e.g.: >
nvim -- -filename
-< All arguments after the "--" will be interpreted as file names,
- no other options or "+command" argument can follow.
+< All arguments after "--" are interpreted as file names, no
+ other options or "+command" arguments can follow.
*--*
-- Alias for stdin (standard input).
+`-` Alias for stdin (standard input).
Example: >
echo text | nvim - file
< "text" is read into buffer 1, "file" is opened as buffer 2.
@@ -82,12 +82,10 @@ argument.
--help *-h* *--help* *-?*
-?
-h Give usage (help) message and exit.
- See |info-message| about capturing the text.
--version *-v* *--version*
-v Print version information and exit. Same output as for
|:version| command.
- See |info-message| about capturing the text.
*--clean*
--clean Mimics a fresh install of Nvim:
@@ -145,15 +143,12 @@ argument.
these commands, independently from "-c" commands.
*-S*
--S {file} The {file} will be sourced after the first file has been read.
- This is an easy way to do the equivalent of: >
+-S [file] Executes Vimscript or Lua (".lua") [file] after the first file
+ has been read. See also |:source|. If [file] is not given,
+ defaults to "Session.vim". Equivalent to: >
-c "source {file}"
-< It can be mixed with "-c" arguments and repeated like "-c".
- The limit of 10 "-c" arguments applies here as well.
- {file} cannot start with a "-".
-
--S Works like "-S Session.vim". Only when used as the last
- argument or when another "-" option follows.
+< Can be repeated like "-c", subject to the same limit of 10
+ "-c" arguments. {file} cannot start with a "-".
-L *-L* *-r*
-r Recovery mode. Without a file name argument, a list of
@@ -195,8 +190,9 @@ argument.
-E reads stdin as text (into buffer 1).
-es *-es* *-Es* *-s-ex* *silent-mode*
--Es Silent mode (no UI), for scripting. Unrelated to |-s|.
- Disables most prompts, messages, warnings and errors.
+-Es Script mode, aka "silent mode", aka "batch mode". No UI,
+ disables most prompts and messages. Unrelated to |-s|.
+ See also |-S| to run script files.
-es reads/executes stdin as Ex commands. >
printf "put ='foo'\n%%print\n" | nvim -es
@@ -205,18 +201,44 @@ argument.
send commands. >
printf "foo\n" | nvim -Es +"%print"
-< Output of these commands is displayed (to stdout):
- :print
+< These commands display on stdout:
:list
:number
- :set (to display option values)
- When 'verbose' is set messages are printed to stderr. >
- echo foo | nvim -V1 -es
+ :print
+ :set
+ With |:verbose| or 'verbose', other commands display on stderr: >
+ nvim -es +":verbose echo 'foo'"
+ nvim -V1 -es +foo
-< User |config| is skipped (unless given with |-u|).
+< User |config| is skipped unless |-u| was given.
Swap file is skipped (like |-n|).
User |shada| is loaded (unless "-i NONE" is given).
+ *-l*
+-l {script} [args]
+ Executes Lua {script} non-interactively (no UI) with optional
+ [args] after processing any preceding Nvim |cli-arguments|,
+ then exits. Exits 1 on Lua error. See |-S| to run multiple Lua
+ scripts without args, with a UI.
+ *lua-args*
+ All [args] are treated as {script} arguments and stored in the
+ Lua `_G.arg` global table, thus "-l" ends processing of Nvim
+ arguments. The {script} name is stored at `_G.arg[0]`.
+
+ Sets 'verbose' to 1 (like "-V1"), so Lua `print()` writes to
+ output.
+
+ Arguments before "-l" are processed before executing {script}.
+ This example quits before executing "foo.lua": >
+ nvim +q -l foo.lua
+< This loads Lua module "bar" before executing "foo.lua": >
+ nvim +"lua require('bar')" -l foo.lua
+<
+ Skips user |config| unless |-u| was given.
+ Disables plugins unless 'loadplugins' was set.
+ Disables |shada| unless |-i| was given.
+ Disables swapfile (like |-n|).
+
*-b*
-b Binary mode. File I/O will only recognize <NL> to separate
lines. The 'expandtab' option will be reset. The 'textwidth'
@@ -224,9 +246,6 @@ argument.
is set. This is done after reading the |vimrc| but before
reading any file in the arglist. See also |edit-binary|.
- *-l*
--l Lisp mode. Sets the 'lisp' and 'showmatch' options on.
-
*-A*
-A Arabic mode. Sets the 'arabic' option on.
@@ -241,10 +260,10 @@ argument.
Example: >
nvim -V8
--V[N]{filename}
- Like -V and set 'verbosefile' to {filename}. Messages are not
- displayed; instead they are written to the file {filename}.
- {filename} must not start with a digit.
+-V[N]{file}
+ Like -V and sets 'verbosefile' to {file} (must not start with
+ a digit). Messages are not displayed, instead they are
+ written to {file}.
Example: >
nvim -V20vimlog
<
@@ -403,18 +422,20 @@ accordingly, proceeding as follows:
The |-V| argument can be used to display or log what happens next,
useful for debugging the initializations.
-3. Wait for UI to connect.
+3. Start a server (unless |--listen| was given) and set |v:servername|.
+
+4. Wait for UI to connect.
Nvim started with |--embed| waits for the UI to connect before
proceeding to load user configuration.
-4. Setup |default-mappings| and |default-autocmds|. Create |popup-menu|.
+5. Setup |default-mappings| and |default-autocmds|. Create |popup-menu|.
-5. Enable filetype and indent plugins.
+6. 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, …).
+7. 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*
@@ -445,32 +466,33 @@ accordingly, proceeding as follows:
*VIMINIT* *EXINIT* *$MYVIMRC*
b. Locations searched for initializations, in order of preference:
- $VIMINIT environment variable (Ex command line).
- - User |config|: $XDG_CONFIG_HOME/nvim/init.vim.
- - Other config: {dir}/nvim/init.vim where {dir} is any directory
- in $XDG_CONFIG_DIRS.
+ - User |config|: $XDG_CONFIG_HOME/nvim/init.vim (or init.lua).
+ - Other config: {dir}/nvim/init.vim (or init.lua) where {dir} is any
+ directory in $XDG_CONFIG_DIRS.
- $EXINIT environment variable (Ex command line).
|$MYVIMRC| is set to the first valid location unless it was already
set or when using $VIMINIT.
c. If the 'exrc' option is on (which is NOT the default), the current
- directory is searched for two files. The first that exists is used,
- the others are ignored.
- - The file ".nvimrc"
- - The file ".exrc"
+ directory is searched for the following files, in order of precedence:
+ - ".nvim.lua"
+ - ".nvimrc"
+ - ".exrc"
+ The first that exists is used, the others are ignored.
-7. Enable filetype detection.
+8. Enable filetype detection.
This does the same as the command: >
- :runtime! filetype.lua filetype.vim
+ :runtime! filetype.lua
< Skipped if ":filetype off" was called or if the "-u NONE" command line
argument was given.
-8. Enable syntax highlighting.
+9. 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.
-9. Load the plugin scripts. *load-plugins*
+10. Load the plugin scripts. *load-plugins*
This does the same as the command: >
:runtime! plugin/**/*.vim
:runtime! plugin/**/*.lua
@@ -482,13 +504,13 @@ accordingly, proceeding as follows:
However, directories in 'runtimepath' ending in "after" are skipped
here and only loaded after packages, see below.
Loading plugins won't be done when:
- - The 'loadplugins' option was reset in a vimrc file.
+ - The |'loadplugins'| option was reset in a vimrc file.
- The |--noplugin| command line argument is used.
- The |--clean| command line argument is used.
- The "-u NONE" command line argument is used |-u|.
- Note that using "-c 'set noloadplugins'" doesn't work, because the
+ Note that using `-c 'set noloadplugins'` doesn't work, because the
commands from the command line have not been executed yet. You can
- use "--cmd 'set noloadplugins'" or "--cmd 'set loadplugins'" |--cmd|.
+ use `--cmd 'set noloadplugins'` or `--cmd 'set loadplugins'` |--cmd|.
Packages are loaded. These are plugins, as above, but found in the
"start" directory of each entry in 'packpath'. Every plugin directory
@@ -500,21 +522,21 @@ accordingly, proceeding as follows:
if packages have been found, but that should not add a directory
ending in "after".
-10. Set 'shellpipe' and 'shellredir'
+11. 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.
-11. Set 'updatecount' to zero, if "-n" command argument used
+12. Set 'updatecount' to zero, if "-n" command argument used
-12. Set binary options if the |-b| flag was given.
+13. Set binary options if the |-b| flag was given.
-13. Read the |shada-file|.
+14. Read the |shada-file|.
-14. Read the quickfix file if the |-q| flag was given, or exit on failure.
+15. Read the quickfix file if the |-q| flag was given, or exit on failure.
-15. Open all windows
+16. 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
@@ -524,7 +546,7 @@ accordingly, proceeding as follows:
Buffers for all windows will be loaded, without triggering |BufAdd|
autocommands.
-16. Execute startup commands
+17. Execute startup commands
If a |-t| flag was given, the tag is jumped to.
Commands given with |-c| and |+cmd| are executed.
The starting flag is reset, has("vim_starting") will now return zero.
@@ -1184,7 +1206,7 @@ exactly four MessagePack objects:
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
@@ -1215,7 +1237,7 @@ exactly four MessagePack objects:
sp Binary N/A Actual pattern. Required.
sb Boolean false True if search direction is
backward.
- * any none Other keys are allowed for
+ `*` any none Other keys are allowed for
compatibility reasons, see
|shada-compatibility|.
3 (SubString) Array containing last |:substitute| replacement string.
@@ -1286,7 +1308,7 @@ exactly four MessagePack objects:
GlobalMark and LocalMark
entries.
f Binary N/A File name. Required.
- * any none Other keys are allowed for
+ `*` any none Other keys are allowed for
compatibility reasons, see
|shada-compatibility|.
9 (BufferList) Array containing maps. Each map in the array
@@ -1296,10 +1318,10 @@ exactly four MessagePack objects:
greater then zero.
c UInteger 0 Position column number.
f Binary N/A File name. Required.
- * any none Other keys are allowed for
+ `*` 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*
@@ -1359,7 +1381,7 @@ 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('log')/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/support.txt b/runtime/doc/support.txt
new file mode 100644
index 0000000000..481959d8f1
--- /dev/null
+++ b/runtime/doc/support.txt
@@ -0,0 +1,52 @@
+*support.txt* Nvim
+
+
+ NVIM REFERENCE MANUAL
+
+
+Support *support*
+
+ Type |gO| to see the table of contents.
+
+==============================================================================
+Supported platforms *supported-platforms*
+
+`System` `Tier` `Versions` `Tested versions`
+Linux 1 >= 2.6.32, glibc >= 2.12 Ubuntu 22.04
+macOS (Intel) 1 >= 10.15 macOS 12
+Windows 64-bit 1 >= 8 Windows Server 2019
+FreeBSD 1 >= 10 FreeBSD 13
+macOS (M1) 2 >= 10.15
+OpenBSD 2 >= 7
+MinGW 2 MinGW-w64
+
+Support types ~
+
+* Tier 1: Officially supported and tested with CI. Any contributed patch
+ MUST NOT break such systems.
+
+* Tier 2: Officially supported, but not necessarily tested with CI. These
+ systems are maintained to the best of our ability, without being a top
+ priority.
+
+* Tier 3: Not tested and no guarantees, but may work.
+
+Adding support for a new platform ~
+
+IMPORTANT: Before attempting to add support for a new platform please open
+an issue about it for discussion.
+
+
+==============================================================================
+Common
+
+Some common notes when adding support for new platforms:
+
+Cmake is the only supported build system. The platform must be buildable with cmake.
+
+All functionality related to the new platform must be implemented in its own
+file inside `src/nvim/os` unless it's already done in a common file, in which
+case adding an `#ifdef` is fine.
+
+
+ vim:tw=78:ts=8:et:ft=help:norl:
diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt
index b74611633f..bd5a4f1926 100644
--- a/runtime/doc/syntax.txt
+++ b/runtime/doc/syntax.txt
@@ -181,16 +181,16 @@ Vim will only load the first syntax file found, assuming that it sets
b:current_syntax.
-NAMING CONVENTIONS *group-name* *{group-name}* *E669* *W18*
+NAMING CONVENTIONS *group-name* *{group-name}* *E669* *E5248*
A syntax group name is to be used for syntax items that match the same kind of
thing. These are then linked to a highlight group that specifies the color.
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. The maximum length of a group name is
-about 200 bytes. *E1249*
+The name for a highlight or syntax group must consist of ASCII letters,
+digits, underscores, periods and `@` characters. As a regexp it is
+`[a-zA-Z0-9_.@]*`. 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.
@@ -464,7 +464,7 @@ Force to omit the line numbers: >
Go back to the default to use 'number' by deleting the variable: >
:unlet g:html_number_lines
<
- *g:html_line_ids*
+ *g:html_line_ids*
Default: 1 if |g:html_number_lines| is set, 0 otherwise.
When 1, adds an HTML id attribute to each line number, or to an empty <span>
inserted for that purpose if no line numbers are shown. This ID attribute
@@ -656,6 +656,22 @@ the rendered page generated by 2html.vim.
>
:let g:html_no_pre = 1
<
+ *g:html_no_doc*
+Default: 0.
+When 1 it doesn't generate a full HTML document with a DOCTYPE, <head>,
+<body>, etc. If |g:html_use_css| is enabled (the default) you'll have to
+define the CSS manually. The |g:html_dynamic_folds| and |g:html_line_ids|
+settings (off by default) also insert some JavaScript.
+
+
+ *g:html_no_links*
+Default: 0.
+Don't generate <a> tags for text that looks like an URL.
+
+ *g:html_no_modeline*
+Default: 0.
+Don't generate a modeline disabling folding.
+
*g:html_expand_tabs*
Default: 0 if 'tabstop' is 8, 'expandtab' is 0, 'vartabstop' is not in use,
and no fold column or line numbers occur in the generated HTML;
@@ -687,13 +703,13 @@ Automatic detection works for the encodings mentioned specifically by name in
|encoding-names|, but TOhtml will only automatically use those encodings with
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.
+below). See https://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
the generated HTML, as recommended by W3C:
- http://www.w3.org/International/questions/qa-choosing-encodings
- http://www.w3.org/International/questions/qa-byte-order-mark
+ https://www.w3.org/International/questions/qa-choosing-encodings
+ https://www.w3.org/International/questions/qa-byte-order-mark
*g:html_use_encoding*
Default: none, uses IANA name for current 'fileencoding' as above.
@@ -832,7 +848,7 @@ files are included:
asm68k Motorola 680x0 assembly
asmh8300 Hitachi H-8300 version of GNU assembly
ia64 Intel Itanium 64
- fasm Flat assembly (http://flatassembler.net)
+ fasm Flat assembly (https://flatassembler.net)
masm Microsoft assembly (probably works for any 80x86)
nasm Netwide assembly
tasm Turbo Assembly (with opcodes 80x86 up to Pentium, and
@@ -1393,9 +1409,9 @@ Two syntax highlighting files exist for Euphoria. One for Euphoria
version 3.1.1, which is the default syntax highlighting file, and one for
Euphoria version 4.0.5 or later.
-Euphoria version 3.1.1 (http://www.rapideuphoria.com/) is still necessary
+Euphoria version 3.1.1 (https://www.rapideuphoria.com/) is still necessary
for developing applications for the DOS platform, which Euphoria version 4
-(http://www.openeuphoria.org/) does not support.
+(https://www.openeuphoria.org/) does not support.
The following file extensions are auto-detected as Euphoria file type:
@@ -1452,7 +1468,7 @@ Elixir.
FLEXWIKI *flexwiki.vim* *ft-flexwiki-syntax*
-FlexWiki is an ASP.NET-based wiki package available at http://www.flexwiki.com
+FlexWiki is an ASP.NET-based wiki package available at https://www.flexwiki.com
NOTE: This site currently doesn't work, on Wikipedia is mentioned that
development stopped in 2009.
@@ -1808,7 +1824,7 @@ are read during initialization) >
:let html_my_rendering=1
If you'd like to see an example download mysyntax.vim at
-http://www.fleiner.com/vim/download.html
+https://www.fleiner.com/vim/download.html
You can also disable this rendering by adding the following line to your
vimrc file: >
@@ -1838,6 +1854,16 @@ following two lines to the syntax coloring file for that language
Now you just need to make sure that you add all regions that contain
the preprocessor language to the cluster htmlPreproc.
+ *html-folding*
+The HTML syntax file provides syntax |folding| (see |:syn-fold|) between start
+and end tags. This can be turned on by >
+
+ :let g:html_syntax_folding = 1
+ :set foldmethod=syntax
+
+Note: Syntax folding might slow down syntax highlighting significantly,
+especially for large files.
+
HTML/OS (by Aestiva) *htmlos.vim* *ft-htmlos-syntax*
@@ -1936,7 +1962,7 @@ highlight them use: >
:let java_highlight_java_lang_ids=1
You can also highlight identifiers of most standard Java packages if you
-download the javaid.vim script at http://www.fleiner.com/vim/download.html.
+download the javaid.vim script at https://www.fleiner.com/vim/download.html.
If you prefer to only highlight identifiers of a certain package, say java.io
use the following: >
:let java_highlight_java_io=1
@@ -2057,7 +2083,7 @@ The g:lisp_rainbow option provides 10 levels of individual colorization for
the parentheses and backquoted parentheses. Because of the quantity of
colorization levels, unlike non-rainbow highlighting, the rainbow mode
specifies its highlighting using ctermfg and guifg, thereby bypassing the
-usual colorscheme control using standard highlighting groups. The actual
+usual color scheme control using standard highlighting groups. The actual
highlighting used depends on the dark/bright setting (see |'bg'|).
@@ -2365,7 +2391,7 @@ you set the variable: >
:let papp_include_html=1
-in your startup file it will try to syntax-hilight html code inside phtml
+in your startup file it will try to syntax-highlight html code inside phtml
sections, but this is relatively slow and much too colourful to be able to
edit sensibly. ;)
@@ -2449,7 +2475,7 @@ from the rest of the name (like 'PkgName::' in '$PkgName::VarName'): >
(In Vim 6.x it was the other way around: "perl_want_scope_in_variables"
enabled it.)
-If you do not want complex things like '@{${"foo"}}' to be parsed: >
+If you do not want complex things like `@{${"foo"}}` to be parsed: >
:let perl_no_extended_vars = 1
@@ -2897,7 +2923,7 @@ Default folding is rather detailed, i.e., small syntax units like "if", "do",
You can set "ruby_foldable_groups" to restrict which groups are foldable: >
- :let ruby_foldable_groups = 'if case %'
+ :let ruby_foldable_groups = 'if case %'
<
The value is a space-separated list of keywords:
@@ -2905,22 +2931,22 @@ The value is a space-separated list of keywords:
-------- ------------------------------------- ~
ALL Most block syntax (default)
NONE Nothing
- if "if" or "unless" block
+ if "if" or "unless" block
def "def" block
class "class" block
module "module" block
- do "do" block
+ do "do" block
begin "begin" block
case "case" block
for "for", "while", "until" loops
- { Curly bracket block or hash literal
- [ Array literal
- % Literal with "%" notation, e.g.: %w(STRING), %!STRING!
- / Regexp
+ { Curly bracket block or hash literal
+ [ Array literal
+ % Literal with "%" notation, e.g.: %w(STRING), %!STRING!
+ / Regexp
string String and shell command output (surrounded by ', ", `)
- : Symbol
- # Multiline comment
- << Here documents
+ : Symbol
+ # Multiline comment
+ << Here documents
__END__ Source code after "__END__" directive
*ruby_no_expensive*
@@ -2986,16 +3012,25 @@ satisfied with it for my own projects.
SED *sed.vim* *ft-sed-syntax*
To make tabs stand out from regular blanks (accomplished by using Todo
-highlighting on the tabs), define "highlight_sedtabs" by putting >
-
- :let highlight_sedtabs = 1
+highlighting on the tabs), define "g:sed_highlight_tabs" by putting >
+ :let g:sed_highlight_tabs = 1
+<
in the vimrc file. (This special highlighting only applies for tabs
inside search patterns, replacement texts, addresses or text included
by an Append/Change/Insert command.) If you enable this option, it is
also a good idea to set the tab width to one character; by doing that,
you can easily count the number of tabs in a string.
+GNU sed allows comments after text on the same line. BSD sed only allows
+comments where "#" is the first character of the line. To enforce BSD-style
+comments, i.e. mark end-of-line comments as errors, use: >
+
+ :let g:sed_dialect = "bsd"
+<
+Note that there are other differences between GNU sed and BSD sed which are
+not (yet) affected by this setting.
+
Bugs:
The transform command (y) is treated exactly like the substitute
@@ -3118,7 +3153,7 @@ The default is to use the twice sh_minlines. Set it to a smaller number to
speed up displaying. The disadvantage is that highlight errors may appear.
syntax/sh.vim tries to flag certain problems as errors; usually things like
-extra ']'s, 'done's, 'fi's, etc. If you find the error handling problematic
+unmatched "]", "done", "fi", etc. If you find the error handling problematic
for your purposes, you may suppress such error highlighting by putting
the following line in your .vimrc: >
@@ -3353,13 +3388,11 @@ of specialized LaTeX commands, syntax, and fonts. If you're using such a
package you'll often wish that the distributed syntax/tex.vim would support
it. However, clearly this is impractical. So please consider using the
techniques in |mysyntaxfile-add| to extend or modify the highlighting provided
-by syntax/tex.vim. Please consider uploading any extensions that you write,
-which typically would go in $HOME/after/syntax/tex/[pkgname].vim, to
-http://vim.sf.net/.
+by syntax/tex.vim.
I've included some support for various popular packages on my website: >
- http://www.drchip.org/astronaut/vim/index.html#LATEXPKGS
+ https://www.drchip.org/astronaut/vim/index.html#LATEXPKGS
<
The syntax files there go into your .../after/syntax/tex/ directory.
@@ -3538,6 +3571,14 @@ highlighting is to put the following line in your |vimrc|: >
<
+WDL *wdl.vim* *wdl-syntax*
+
+The Workflow Description Language is a way to specify data processing workflows
+with a human-readable and writeable syntax. This is used a lot in
+bioinformatics. More info on the spec can be found here:
+https://github.com/openwdl/wdl
+
+
XF86CONFIG *xf86conf.vim* *ft-xf86conf-syntax*
The syntax of XF86Config file differs in XFree86 v3.x and v4.x. Both
@@ -3690,12 +3731,13 @@ DEFINING CASE *:syn-case* *E390*
items until the next ":syntax case" command are affected.
:sy[ntax] case
- Show either "syntax case match" or "syntax case ignore" (translated).
+ Show either "syntax case match" or "syntax case ignore".
DEFINING FOLDLEVEL *:syn-foldlevel*
-:sy[ntax] foldlevel [start | minimum]
+:sy[ntax] foldlevel start
+:sy[ntax] foldlevel minimum
This defines how the foldlevel of a line is computed when using
foldmethod=syntax (see |fold-syntax| and |:syn-fold|):
@@ -3708,11 +3750,14 @@ DEFINING FOLDLEVEL *:syn-foldlevel*
may close and open horizontally within a line.
:sy[ntax] foldlevel
- Show either "syntax foldlevel start" or "syntax foldlevel minimum".
+ Show the current foldlevel method, either "syntax foldlevel start" or
+ "syntax foldlevel minimum".
SPELL CHECKING *:syn-spell*
-:sy[ntax] spell [toplevel | notoplevel | default]
+:sy[ntax] spell toplevel
+:sy[ntax] spell notoplevel
+:sy[ntax] spell default
This defines where spell checking is to be done for text that is not
in a syntax item:
@@ -3727,8 +3772,8 @@ SPELL CHECKING *:syn-spell*
To activate spell checking the 'spell' option must be set.
:sy[ntax] spell
- Show either "syntax spell toplevel", "syntax spell notoplevel" or
- "syntax spell default" (translated).
+ Show the current syntax spell checking method, either "syntax spell
+ toplevel", "syntax spell notoplevel" or "syntax spell default".
SYNTAX ISKEYWORD SETTING *:syn-iskeyword*
@@ -3739,7 +3784,7 @@ SYNTAX ISKEYWORD SETTING *:syn-iskeyword*
clear: Syntax specific iskeyword setting is disabled and the
buffer-local 'iskeyword' setting is used.
- {option} Set the syntax 'iskeyword' option to a new value.
+ {option} Set the syntax 'iskeyword' option to a new value.
Example: >
:syntax iskeyword @,48-57,192-255,$,_
@@ -4326,7 +4371,7 @@ IMPLICIT CONCEAL *:syn-conceal-implicit*
given explicitly.
:sy[ntax] conceal
- Show either "syntax conceal on" or "syntax conceal off" (translated).
+ Show either "syntax conceal on" or "syntax conceal off".
==============================================================================
8. Syntax patterns *:syn-pattern* *E401* *E402*
@@ -4806,7 +4851,7 @@ Note that the ":syntax" command can be abbreviated to ":sy", although ":syn"
is mostly used, because it looks better.
==============================================================================
-12. Highlight command *:highlight* *:hi* *E28* *E411* *E415*
+13. Highlight command *:highlight* *:hi* *E28* *E411* *E415*
There are two types of highlight groups:
- The built-in |highlight-groups|.
@@ -4838,7 +4883,7 @@ in their own color.
To customize a color scheme use another name, e.g.
"~/.config/nvim/colors/mine.vim", and use `:runtime` to
- load the original colorscheme: >
+ load the original color scheme: >
runtime colors/evening.vim
hi Statement ctermfg=Blue guifg=Blue
@@ -4846,7 +4891,7 @@ in their own color.
|ColorSchemePre| autocommand event is triggered.
After the color scheme has been loaded the
|ColorScheme| autocommand event is triggered.
- For info about writing a colorscheme file: >
+ For info about writing a color scheme file: >
:edit $VIMRUNTIME/colors/README.txt
:hi[ghlight] List all the current highlight groups that have
@@ -4857,7 +4902,7 @@ in their own color.
*highlight-clear* *:hi-clear*
:hi[ghlight] clear Reset all highlighting to the defaults. Removes all
- highlighting for groups added by the user!
+ highlighting for groups added by the user.
Uses the current value of 'background' to decide which
default colors to use.
If there was a default link, restore it. |:hi-link|
@@ -4913,7 +4958,8 @@ the same syntax file on all UIs.
*bold* *underline* *undercurl*
*underdouble* *underdotted*
*underdashed* *inverse* *italic*
- *standout* *nocombine* *strikethrough*
+ *standout* *strikethrough* *altfont*
+ *nocombine*
cterm={attr-list} *attr-list* *highlight-cterm* *E418*
attr-list is a comma-separated list (without spaces) of the
following items (in any order):
@@ -4928,6 +4974,7 @@ cterm={attr-list} *attr-list* *highlight-cterm* *E418*
inverse same as reverse
italic
standout
+ altfont
nocombine override attributes instead of combining them
NONE no attributes used (used to reset it)
@@ -4935,7 +4982,7 @@ cterm={attr-list} *attr-list* *highlight-cterm* *E418*
have the same effect.
"undercurl", "underdouble", "underdotted", and "underdashed" fall back
to "underline" in a terminal that does not support them. The color is
- set using |highlight-guisp|.
+ set using |guisp|.
start={term-list} *highlight-start* *E422*
stop={term-list} *term-list* *highlight-stop*
@@ -4956,8 +5003,8 @@ stop={term-list} *term-list* *highlight-stop*
like "<Esc>" and "<Space>". Example:
start=<Esc>[27h;<Esc>[<Space>r;
-ctermfg={color-nr} *highlight-ctermfg* *E421*
-ctermbg={color-nr} *highlight-ctermbg*
+ctermfg={color-nr} *ctermfg* *E421*
+ctermbg={color-nr} *ctermbg*
The {color-nr} argument is a color number. Its range is zero to
(not including) the number of |tui-colors| available.
The actual color with this number depends on the type of terminal
@@ -4997,7 +5044,7 @@ 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
+ 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
@@ -5016,7 +5063,7 @@ ctermbg={color-nr} *highlight-ctermbg*
explicitly. This causes the highlight groups that depend on
'background' to change! This means you should set the colors for
Normal first, before setting other colors.
- When a colorscheme is being used, changing 'background' causes it to
+ When a color scheme is being used, changing 'background' causes it to
be reloaded, which may reset all colors (including Normal). First
delete the "g:colors_name" variable when you don't want this.
@@ -5064,9 +5111,9 @@ font={font-name} *highlight-font*
Example: >
:hi comment font='Monospace 10'
-guifg={color-name} *highlight-guifg*
-guibg={color-name} *highlight-guibg*
-guisp={color-name} *highlight-guisp*
+guifg={color-name} *guifg*
+guibg={color-name} *guibg*
+guisp={color-name} *guisp*
These give the foreground (guifg), background (guibg) and special
(guisp) color to use in the GUI. "guisp" is used for various
underlines.
@@ -5099,7 +5146,7 @@ guisp={color-name} *highlight-guisp*
"gg" is the Green value
"bb" is the Blue value
All values are hexadecimal, range from "00" to "ff". Examples: >
- :highlight Comment guifg=#11f0c3 guibg=#ff00ff
+ :highlight Comment guifg=#11f0c3 guibg=#ff00ff
<
blend={integer} *highlight-blend*
Override the blend level for a highlight group within the popupmenu
@@ -5123,7 +5170,7 @@ 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*
@@ -5174,10 +5221,10 @@ LineNrBelow Line number for when the 'relativenumber'
*hl-CursorLineNr*
CursorLineNr Like LineNr when 'cursorline' is set and 'cursorlineopt'
contains "number" or is "both", for the cursor line.
- *hl-CursorLineSign*
-CursorLineSign Like SignColumn when 'cursorline' is set for the cursor line.
*hl-CursorLineFold*
CursorLineFold Like FoldColumn when 'cursorline' is set for the cursor line.
+ *hl-CursorLineSign*
+CursorLineSign Like SignColumn when 'cursorline' is set for the cursor line.
*hl-MatchParen*
MatchParen Character under the cursor or just before it, if it
is a paired bracket, and its match. |pi_paren.txt|
@@ -5187,7 +5234,7 @@ ModeMsg 'showmode' message (e.g., "-- INSERT --").
*hl-MsgArea*
MsgArea Area for messages and cmdline.
*hl-MsgSeparator*
-MsgSeparator Separator for scrolled messages, `msgsep` flag of 'display'.
+MsgSeparator Separator for scrolled messages |msgsep|.
*hl-MoreMsg*
MoreMsg |more-prompt|
*hl-NonText*
@@ -5288,7 +5335,7 @@ Tooltip Current font, background and foreground of the tooltips.
Applicable highlight arguments: font, guibg, guifg.
==============================================================================
-13. Linking groups *:hi-link* *:highlight-link* *E412* *E413*
+14. Linking groups *:hi-link* *:highlight-link* *E412* *E413*
When you want to use the same highlighting for several syntax groups, you
can do this more easily by linking the groups into one common highlight
@@ -5397,8 +5444,7 @@ 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 Universal Ctags (found at https://ctags.io) or Exuberant ctags (found
-at http://ctags.sf.net).
+must use Universal Ctags (https://ctags.io) or Exuberant ctags.
Put these lines in your Makefile:
@@ -5448,7 +5494,7 @@ is loaded into that window or the file is reloaded.
When splitting the window, the new window will use the original syntax.
==============================================================================
-17. Color xterms *xterm-color* *color-xterm*
+18. Color xterms *xterm-color* *color-xterm*
*colortest.vim*
To test your color setup, a file has been included in the Vim distribution.
@@ -5458,7 +5504,7 @@ To use it, execute this command: >
Nvim uses 256-color and |true-color| terminal capabilities wherever possible.
==============================================================================
-18. When syntax is slow *:syntime*
+19. When syntax is slow *:syntime*
This is aimed at authors of a syntax file.
diff --git a/runtime/doc/tabpage.txt b/runtime/doc/tabpage.txt
index 9197710819..49b2773253 100644
--- a/runtime/doc/tabpage.txt
+++ b/runtime/doc/tabpage.txt
@@ -186,8 +186,8 @@ 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 last accessed tab page
:tabnext - " go to the previous tab page
:tabnext -1 " as above
:tabnext + " go to the next tab page
diff --git a/runtime/doc/tagsrch.txt b/runtime/doc/tagsrch.txt
index 2485290667..0f785dd1eb 100644
--- a/runtime/doc/tagsrch.txt
+++ b/runtime/doc/tagsrch.txt
@@ -59,9 +59,9 @@ CTRL-] Jump to the definition of the keyword under the
CTRL-] is the default telnet escape key. When you type CTRL-] to jump to a
tag, you will get the telnet prompt instead. Most versions of telnet allow
changing or disabling the default escape key. See the telnet man page. You
-can 'telnet -E {Hostname}' to disable the escape character, or 'telnet -e
-{EscapeCharacter} {Hostname}' to specify another escape character. If
-possible, try to use "ssh" instead of "telnet" to avoid this problem.
+can `telnet -E {Hostname}` to disable the escape character, or
+`telnet -e {EscapeCharacter} {Hostname}` to specify another escape character.
+If possible, try to use "ssh" instead of "telnet" to avoid this problem.
*tag-priority*
When there are multiple matches for a tag, this priority is used:
@@ -404,7 +404,7 @@ is added to the command and on the 'autowrite' option:
tag in file autowrite ~
current file changed ! option action ~
------------------------------------------------------------------------------
+ ---------------------------------------------------------------------------
yes x x x goto tag
no no x x read other file, goto tag
no yes yes x abandon current file, read other file, goto
@@ -412,7 +412,7 @@ current file changed ! option action ~
no yes no on write current file, read other file, goto
tag
no yes no off fail
------------------------------------------------------------------------------
+ ---------------------------------------------------------------------------
- If the tag is in the current file, the command will always work.
- If the tag is in another file and the current file was not changed, the
@@ -514,18 +514,13 @@ ctags As found on most Unix systems. Only supports C. Only
universal ctags A maintained version of ctags based on exuberant
ctags. See https://ctags.io.
*Exuberant_ctags*
-exuberant ctags This is a very good one. It works for C, C++, Java,
- Fortran, Eiffel and others. It can generate tags for
- many items. See http://ctags.sourceforge.net.
- No new version since 2009.
+exuberant ctags Works for C, C++, Java, Fortran, Eiffel and others.
+ See https://ctags.sourceforge.net. No new version
+ since 2009.
JTags For Java, in Java. It can be found at
- http://www.fleiner.com/jtags/.
+ https://www.fleiner.com/jtags/.
ptags.py For Python, in Python. Found in your Python source
directory at Tools/scripts/ptags.py.
-ptags For Perl, in Perl. It can be found at
- http://www.eleves.ens.fr:8080/home/nthiery/Tags/.
-gnatxref For Ada. See http://www.gnuada.org/. gnatxref is
- part of the gnat package.
The lines in the tags file must have one of these two formats:
@@ -566,8 +561,8 @@ ctags).
with Vi, it ignores the following fields. Example:
APP file /^static int APP;$/;" v
When {tagaddress} is not a line number or search pattern, then
- {term} must be |;". Here the bar ends the command (excluding
- the bar) and ;" is used to have Vi ignore the rest of the
+ {term} must be `|;"`. Here the bar ends the command (excluding
+ the bar) and `;"` is used to have Vi ignore the rest of the
line. Example:
APP file.c call cursor(3, 4)|;" v
@@ -629,8 +624,7 @@ If the command is a normal search command (it starts and ends with "/" or
"?"), some special handling is done:
- Searching starts on line 1 of the file.
The direction of the search is forward for "/", backward for "?".
- Note that 'wrapscan' does not matter, the whole file is always searched. (Vi
- does use 'wrapscan', which caused tags sometimes not be found.)
+ Note that 'wrapscan' does not matter, the whole file is always searched.
- If the search fails, another try is done ignoring case. If that fails too,
a search is done for:
"^tagname[ \t]*("
@@ -834,10 +828,10 @@ CTRL-W d Open a new window, with the cursor on the first
(default: whole file).
See |:search-args| for [/] and [!].
- *:che* *:chec* *:check* *:checkpath*
-:che[ckpath] List all the included files that could not be found.
+ *:checkp* *:checkpath*
+:checkp[ath] List all the included files that could not be found.
-:che[ckpath]! List all the included files.
+:checkp[ath]! List all the included files.
*:search-args*
Common arguments for the commands above:
@@ -854,9 +848,9 @@ Common arguments for the commands above:
a comment (even though syntax highlighting does recognize it).
Note: Since a macro definition mostly doesn't look like a comment, the
[!] makes no difference for ":dlist", ":dsearch" and ":djump".
-[/] A pattern can be surrounded by '/'. Without '/' only whole words are
- matched, using the pattern "\<pattern\>". Only after the second '/' a
- next command can be appended with '|'. Example: >
+[/] A pattern can be surrounded by "/". Without "/" only whole words are
+ matched, using the pattern "\<pattern\>". Only after the second "/" a
+ next command can be appended with "|". Example: >
:isearch /string/ | echo "the last one"
< For a ":djump", ":dsplit", ":dlist" and ":dsearch" command the pattern
is used as a literal string, not as a search pattern.
@@ -870,13 +864,15 @@ like |CTRL-]|.
The function used for generating the taglist is specified by setting the
'tagfunc' option. The function will be called with three arguments:
- a:pattern The tag identifier or pattern used during the tag search.
- a:flags String containing flags to control the function behavior.
- a:info Dict containing the following entries:
+ pattern The tag identifier or pattern used during the tag search.
+ flags String containing flags to control the function behavior.
+ info Dict containing the following entries:
buf_ffname Full filename which can be used for priority.
user_data Custom data String, if stored in the tag
stack previously by tagfunc.
+Note that "a:" needs to be prepended to the argument name when using it.
+
Currently up to three flags may be passed to the tag function:
'c' The function was invoked by a normal command being processed
(mnemonic: the tag function may use the context around the
@@ -912,6 +908,8 @@ If the function returns |v:null| instead of a List, a standard tag lookup will
be performed instead.
It is not allowed to change the tagstack from inside 'tagfunc'. *E986*
+It is not allowed to close a window or change window from inside 'tagfunc'.
+*E1299*
The following is a hypothetical example of a function used for 'tagfunc'. It
uses the output of |taglist()| to generate the result: a list of tags in the
diff --git a/runtime/doc/term.txt b/runtime/doc/term.txt
index cd6798a5de..847b4b6112 100644
--- a/runtime/doc/term.txt
+++ b/runtime/doc/term.txt
@@ -29,7 +29,7 @@ whole.
Building your own terminfo is usually as simple as running this as
a non-superuser:
>
- curl -LO http://invisible-island.net/datafiles/current/terminfo.src.gz
+ curl -LO https://invisible-island.net/datafiles/current/terminfo.src.gz
gunzip terminfo.src.gz
tic terminfo.src
<
@@ -76,7 +76,7 @@ supplying an external one with entries for the terminal type.
Settings depending on terminal *term-dependent-settings*
If you want to set terminal-dependent options or mappings, you can do this in
-your init.vim. Example: >
+your init.vim. Example: >vim
if $TERM =~ '^\(rxvt\|screen\|interix\|putty\)\(-.*\)\?$'
set notermguicolors
@@ -222,9 +222,9 @@ are not in terminfo you must add them by setting "terminal-overrides" in
~/.tmux.conf .
See the tmux(1) manual page for the details of how and what to do in the tmux
-configuration file. It will look something like: >
+configuration file. It will look something like: >bash
set -ga terminal-overrides '*:Ss=\E[%p1%d q:Se=\E[ q'
-<or (alas!) for Konsole 18.07.70 or older, something more complex like: >
+<or (alas!) for Konsole 18.07.70 or older, something more complex like: >bash
set -ga terminal-overrides 'xterm*:\E]50;CursorShape=%?%p1%{3}%<%t%{0}%e%{1}%;%d\007'
<
==============================================================================
@@ -262,7 +262,7 @@ See the "Options" chapter |options|.
If you are using a color terminal that is slow when displaying lines beyond
the end of a buffer, this is because Nvim is drawing the whitespace twice, in
-two sets of colours and attributes. To prevent this, use this command: >
+two sets of colours and attributes. To prevent this, use this command: >vim
hi NonText cterm=NONE ctermfg=NONE
This draws the spaces with the default colours and attributes, which allows the
second pass of drawing to be optimized away. Note: Although in theory the
@@ -372,7 +372,7 @@ that has a match selects until that match (like using "v%"). If the match is
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: >
+An example, for using a double click to jump to the tag under the cursor: >vim
:map <2-LeftMouse> :exe "tag " .. expand("<cword>")<CR>
Dragging the mouse with a double click (button-down, button-up, button-down
@@ -380,6 +380,8 @@ and then drag) will result in whole words to be selected. This continues
until the button is released, at which point the selection is per character
again.
+For scrolling with the mouse see |scroll-mouse-wheel|.
+
In Insert mode, when a selection is started, Vim goes into Normal mode
temporarily. When Visual or Select mode ends, it returns to Insert mode.
This is like using CTRL-O in Insert mode. Select mode is used when the
@@ -408,23 +410,23 @@ The X1 and X2 buttons refer to the extra buttons found on some mice. The
'Microsoft Explorer' mouse has these buttons available to the right thumb.
Currently X1 and X2 only work on Win32 and X11 environments.
-Examples: >
+Examples: >vim
:noremap <MiddleMouse> <LeftMouse><MiddleMouse>
Paste at the position of the middle mouse button click (otherwise the paste
-would be done at the cursor position). >
+would be done at the cursor position). >vim
:noremap <LeftRelease> <LeftRelease>y
Immediately yank the selection, when using Visual mode.
Note the use of ":noremap" instead of "map" to avoid a recursive mapping.
->
+>vim
:map <X1Mouse> <C-O>
:map <X2Mouse> <C-I>
Map the X1 and X2 buttons to go forwards and backwards in the jump list, see
|CTRL-O| and |CTRL-I|.
*mouse-swap-buttons*
-To swap the meaning of the left and right mouse buttons: >
+To swap the meaning of the left and right mouse buttons: >vim
:noremap <LeftMouse> <RightMouse>
:noremap <LeftDrag> <RightDrag>
:noremap <LeftRelease> <RightRelease>
diff --git a/runtime/doc/testing.txt b/runtime/doc/testing.txt
index 4e4a908d0f..ef5e179c86 100644
--- a/runtime/doc/testing.txt
+++ b/runtime/doc/testing.txt
@@ -20,17 +20,11 @@ and for testing plugins.
Vim can be tested after building it, usually with "make test".
The tests are located in the directory "src/testdir".
-There are several types of tests added over time:
- test33.in oldest, don't add any of these
- test_something.in old style tests
- test_something.vim new style tests
-
*new-style-testing*
-New tests should be added as new style tests. These use functions such as
-|assert_equal()| to keep the test commands and the expected result in one
-place.
- *old-style-testing*
-In some cases an old style test needs to be used.
+New tests should be added as new style tests. The test scripts are named
+test_<feature>.vim (replace <feature> with the feature under test). These use
+functions such as |assert_equal()| to keep the test commands and the expected
+result in one place.
Find more information in the file src/testdir/README.txt.
@@ -98,18 +92,46 @@ assert_exception({error} [, {msg}]) *assert_exception()*
catch
call assert_exception('E492:')
endtry
-
-assert_fails({cmd} [, {error} [, {msg}]]) *assert_fails()*
+<
+ *assert_fails()*
+assert_fails({cmd} [, {error} [, {msg} [, {lnum} [, {context}]]]])
Run {cmd} and add an error message to |v:errors| if it does
- NOT produce an error. Also see |assert-return|.
- When {error} is given it must match in |v:errmsg|.
+ NOT produce an error or when {error} is not found in the
+ error message. Also see |assert-return|.
+
+ When {error} is a string it must be found literally in the
+ first reported error. Most often this will be the error code,
+ including the colon, e.g. "E123:". >
+ assert_fails('bad cmd', 'E987:')
+<
+ When {error} is a |List| with one or two strings, these are
+ used as patterns. The first pattern is matched against the
+ first reported error: >
+ assert_fails('cmd', ['E987:.*expected bool'])
+< The second pattern, if present, is matched against the last
+ reported error. To only match the last error use an empty
+ string for the first error: >
+ assert_fails('cmd', ['', 'E987:'])
+<
+ If {msg} is empty then it is not used. Do this to get the
+ default message when passing the {lnum} argument.
+
+ When {lnum} is present and not negative, and the {error}
+ argument is present and matches, then this is compared with
+ the line number at which the error was reported. That can be
+ the line number in a function or in a script.
+
+ When {context} is present it is used as a pattern and matched
+ against the context (script name or function name) where
+ {lnum} is located in.
+
Note that beeping is not considered an error, and some failing
commands only beep. Use |assert_beeps()| for those.
Can also be used as a |method|: >
GetCmd()->assert_fails('E99:')
-assert_false({actual} [, {msg}]) *assert_false()*
+assert_false({actual} [, {msg}]) *assert_false()*
When {actual} is not false an error message is added to
|v:errors|, like with |assert_equal()|.
Also see |assert-return|.
@@ -134,7 +156,7 @@ assert_match({pattern}, {actual} [, {msg}])
When {pattern} does not match {actual} an error message is
added to |v:errors|. Also see |assert-return|.
- {pattern} is used as with |=~|: The matching is always done
+ {pattern} is used as with |expr-=~|: The matching is always done
like 'magic' was set and 'cpoptions' is empty, no matter what
the actual value of 'magic' or 'cpoptions' is.
diff --git a/runtime/doc/tips.txt b/runtime/doc/tips.txt
index d913b53c6b..5d5e89da77 100644
--- a/runtime/doc/tips.txt
+++ b/runtime/doc/tips.txt
@@ -8,7 +8,7 @@ Tips and ideas for using Vim *tips*
These are just a few that we thought would be helpful for many users.
You can find many more tips on the wiki. The URL can be found on
-http://www.vim.org
+https://www.vim.org
Don't forget to browse the user manual, it also contains lots of useful tips
|usr_toc.txt|.
@@ -297,7 +297,7 @@ be able to give comments to the parts of the mapping. >
(<> notation |<>|. Note that this is all typed literally. ^W is "^" "W", not
CTRL-W.)
-Note that the last comment starts with |", because the ":execute" command
+Note that the last comment starts with `|"`, because the ":execute" command
doesn't accept a comment directly.
You also need to set 'textwidth' to a non-zero value, e.g., >
@@ -454,7 +454,7 @@ the current window, try this custom `:HelpCurwin` command:
>
command -bar -nargs=? -complete=help HelpCurwin execute s:HelpCurwin(<q-args>)
let s:did_open_help = v:false
-
+
function s:HelpCurwin(subject) abort
let mods = 'silent noautocmd keepalt'
if !s:did_open_help
diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt
index 52531a1525..917863eef8 100644
--- a/runtime/doc/treesitter.txt
+++ b/runtime/doc/treesitter.txt
@@ -4,77 +4,75 @@
NVIM REFERENCE MANUAL
-Tree-sitter integration *treesitter*
+Treesitter integration *treesitter*
- Type |gO| to see the table of contents.
-
-------------------------------------------------------------------------------
-VIM.TREESITTER *lua-treesitter*
-
-Nvim integrates the tree-sitter library for incremental parsing of buffers.
+Nvim integrates the `tree-sitter` library for incremental parsing of buffers:
+https://tree-sitter.github.io/tree-sitter/
- *vim.treesitter.language_version*
-The latest parser ABI version that is supported by the bundled tree-sitter
-library.
+WARNING: Treesitter support is still experimental and subject to frequent
+changes. This documentation may also not fully reflect the latest changes.
- *vim.treesitter.minimum_language_version*
-The earliest parser ABI version that is supported by the bundled tree-sitter
-library.
+ Type |gO| to see the table of contents.
-Parser files *treesitter-parsers*
+==============================================================================
+PARSER FILES *treesitter-parsers*
Parsers are the heart of tree-sitter. They are libraries that tree-sitter will
-search for in the `parser` runtime directory. Currently Nvim does not provide
-the tree-sitter parsers, instead these must be built separately, for instance
-using the tree-sitter utility. The only exception is a C parser being included
-in official builds for testing purposes. Parsers are searched for as
-`parser/{lang}.*` in any 'runtimepath' directory.
-A parser can also be loaded manually using a full path: >
+search for in the `parser` runtime directory. By default, Nvim bundles only
+parsers for C, Lua, and Vimscript, but parsers can be installed manually or
+via a plugin like https://github.com/nvim-treesitter/nvim-treesitter.
+Parsers are searched for as `parser/{lang}.*` in any 'runtimepath' directory.
+If multiple parsers for the same language are found, the first one is used.
+(This typically implies the priority "user config > plugins > bundled".
+A parser can also be loaded manually using a full path: >lua
vim.treesitter.require_language("python", "/path/to/python.so")
+<
+==============================================================================
+LANGUAGE TREES *treesitter-languagetree*
+ *LanguageTree*
-<Create a parser for a buffer and a given language (if another plugin uses the
-same buffer/language combination, it will be safely reused). Use >
+As buffers can contain multiple languages (e.g., Vimscript commands in a Lua
+file), multiple parsers may be needed to parse the full buffer. These are
+combined in a |LanguageTree| object.
- parser = vim.treesitter.get_parser(bufnr, lang)
+To create a LanguageTree (parser object) for a buffer and a given language,
+use >lua
-<`bufnr=0` can be used for current buffer. `lang` will default to 'filetype'.
+ tsparser = vim.treesitter.get_parser(bufnr, lang)
+<
+`bufnr=0` can be used for current buffer. `lang` will default to 'filetype'.
Currently, the parser will be retained for the lifetime of a buffer but this
is subject to change. A plugin should keep a reference to the parser object as
long as it wants incremental updates.
+Whenever you need to access the current syntax tree, parse the buffer: >lua
-Parser methods *lua-treesitter-parser*
-
-tsparser:parse() *tsparser:parse()*
-Whenever you need to access the current syntax tree, parse the buffer: >
+ tstree = tsparser:parse()
+<
+This will return a table of immutable |treesitter-tree|s 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.
- tstree = parser:parse()
+Note: To use the parser directly inside a |nvim_buf_attach()| Lua callback,
+you must call |vim.treesitter.get_parser()| before you register your callback.
+But preferably 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.
-<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.
+See |lua-treesitter-languagetree| for the list of available methods.
-Note: to use the parser directly inside a |nvim_buf_attach| Lua callback, you
-must call `get_parser()` before you register your callback. But preferably
-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.
+==============================================================================
+TREESITTER TREES *treesitter-tree*
+ *tstree*
-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): >
- {
- {node1, node2},
- ...
- }
-<
- `node1` and `node2` are both considered part of the same region and will
- be parsed together with the parser in the same context.
+A "treesitter tree" represents the parsed contents of a buffer, which can be
+used to perform further analysis. It is a |luaref-userdata| reference to an
+object held by the tree-sitter library.
-Tree methods *lua-treesitter-tree*
+An instance `tstree` of a treesitter tree supports the following methods.
tstree:root() *tstree:root()*
Return the root node of this tree.
@@ -82,8 +80,15 @@ tstree:root() *tstree:root()*
tstree:copy() *tstree:copy()*
Returns a copy of the `tstree`.
+==============================================================================
+TREESITTER NODES *treesitter-node*
+ *tsnode*
+
+A "treesitter node" represents one specific element of the parsed contents of
+a buffer, which can be captured by a |Query| for, e.g., highlighting. It is a
+|luaref-userdata| reference to an object held by the tree-sitter library.
-Node methods *lua-treesitter-node*
+An instance `tsnode` of a treesitter node supports the following methods.
tsnode:parent() *tsnode:parent()*
Get the node's immediate parent.
@@ -160,10 +165,10 @@ 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
+ 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
+ Note: The `id` is not guaranteed to be unique for nodes from different
trees.
*tsnode:descendant_for_range()*
@@ -176,90 +181,93 @@ tsnode:named_descendant_for_range({start_row}, {start_col}, {end_row}, {end_col}
Get the smallest named node within this node that spans the given range of
(row, column) positions
-Query *lua-treesitter-query*
+==============================================================================
+TREESITTER QUERIES *treesitter-query*
+
+Treesitter queries are a way to extract information about a parsed |tstree|,
+e.g., for the purpose of highlighting. Briefly, a `query` consists of one or
+more patterns. A `pattern` is defined over node types in the syntax tree. A
+`match` corresponds to specific elements of the syntax tree which match a
+pattern. Patterns may optionally define captures 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.
+
+Queries are written in a lisp-like language documented in
+https://tree-sitter.github.io/tree-sitter/using-parsers#query-syntax
+Note: The predicates listed there page differ from those Nvim supports. See
+|treesitter-predicates| for a complete list of predicates supported by Nvim.
-Tree-sitter queries are supported, they are a way to do pattern-matching over
-a tree, using a simple to write lisp-like format. See
-https://tree-sitter.github.io/tree-sitter/using-parsers#query-syntax for more
-information on how to write queries.
+Nvim looks for queries as `*.scm` files in a `queries` directory under
+`runtimepath`, where each file contains queries for a specific language and
+purpose, e.g., `queries/lua/highlights.scm` for highlighting Lua files.
+By default, the first query on `runtimepath` is used (which usually implies
+that user config takes precedence over plugins, which take precedence over
+queries bundled with Neovim). If a query should extend other queries instead
+of replacing them, use |treesitter-query-modeline-extends|.
-Note: The predicates listed in the web page above differ from those Neovim
-supports. See |lua-treesitter-predicates| for a complete list of predicates
-supported by Neovim.
+See |lua-treesitter-query| for the list of available methods for working with
+treesitter queries from Lua.
-A `query` consists of one or more patterns. A `pattern` is defined over node
-types in the syntax tree. A `match` corresponds to specific elements of the
-syntax tree which match a pattern. Patterns may optionally define captures
-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 *treesitter-predicates*
+
+Predicates are special scheme nodes that are evaluated to conditionally capture
+nodes. For example, the `eq?` predicate can be used as follows: >
-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 : >
((identifier) @foo (#eq? @foo "foo"))
+<
+to only match identifier corresponding to the `"foo"` text.
-This will only match identifier corresponding to the `"foo"` text.
-Here is a list of built-in predicates :
+The following predicates are built in:
- `eq?` *ts-predicate-eq?*
- This predicate will check text correspondence between nodes or
- strings: >
+ `eq?` *treesitter-predicate-eq?*
+ Match a string against the text corresponding to a node: >
((identifier) @foo (#eq? @foo "foo"))
((node1) @left (node2) @right (#eq? @left @right))
<
- `match?` *ts-predicate-match?*
- `vim-match?` *ts-predicate-vim-match?*
- This will match if the provided vim regex matches the text
- corresponding to a node: >
+ `match?` *treesitter-predicate-match?*
+ `vim-match?` *treesitter-predicate-vim-match?*
+ Match a |regexp| against the text 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.
+< Note: The `^` and `$` anchors will match the start and end of the
+ node's text.
- `lua-match?` *ts-predicate-lua-match?*
- This will match the same way than |match?| but using lua regexes.
+ `lua-match?` *treesitter-predicate-lua-match?*
+ Match |lua-patterns| against the text corresponding to a node,
+ similar to `match?`
- `contains?` *ts-predicate-contains?*
- Will check if any of the following arguments appears in the text
- corresponding to the node: >
+ `contains?` *treesitter-predicate-contains?*
+ Match a string against parts of the text corresponding to a node: >
((identifier) @foo (#contains? @foo "foo"))
- ((identifier) @foo-bar (#contains @foo-bar "foo" "bar"))
+ ((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 arguments: >
+ `any-of?` *treesitter-predicate-any-of?*
+ Match any of the given strings against the text corresponding to
+ a node: >
((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.
-<
+ keywords, as it has been optimized for this.
+
*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({name}, {handler})
+Further predicates can be added via |vim.treesitter.query.add_predicate()|.
+Use |vim.treesitter.query.list_predicates()| to list all available
+predicates.
-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()
-This lists the currently available predicates to use in queries.
+TREESITTER QUERY DIRECTIVES *treesitter-directives*
-Treesitter Query Directive *lua-treesitter-directives*
+Treesitter directives store metadata for a node or match and perform side
+effects. For example, the `set!` directive 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"))
+<
+The following directives are built in:
-Built-in directives:
-
- `set!` *ts-directive-set!*
+ `set!` *treesitter-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).
@@ -273,7 +281,7 @@ Built-in directives:
((identifier) @foo (#set! @foo "kind" "parameter"))
((node1) @left (node2) @right (#set! "type" "pair"))
<
- `offset!` *ts-directive-offset!*
+ `offset!` *treesitter-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`.
@@ -289,120 +297,408 @@ Built-in directives:
((identifier) @constant (#offset! @constant 0 1 0 -1))
<
-Treesitter syntax highlighting (WIP) *lua-treesitter-highlight*
+Further directives can be added via |vim.treesitter.query.add_directive()|.
+Use |vim.treesitter.query.list_directives()| to list all available
+directives.
-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: >
+TREESITTER QUERY MODELINES *treesitter-query-modeline*
- local query = [[
- "for" @keyword
- "if" @keyword
- "return" @keyword
+Neovim supports to customize the behavior of the queries using a set of
+"modelines", that is comments in the queries starting with `;`. Here are the
+currently supported modeline alternatives:
- (string_literal) @string
- (number_literal) @number
- (comment) @comment
+ `inherits: {lang}...` *treesitter-query-modeline-inherits*
+ Specifies that this query should inherit the queries from {lang}.
+ This will recursively descend in the queries of {lang} unless wrapped
+ in parentheses: `({lang})`.
+ Note: This is meant to be used to include queries from another
+ language. If you want your query to extend the queries of the same
+ language, use `extends`.
+
+ `extends` *treesitter-query-modeline-extends*
+ Specifies that this query should be used as an extension for the
+ query, i.e. that it should be merged with the others.
+ Note: The order of the extensions, and the query that will be used as
+ a base depends on your 'runtimepath' value.
+
+Note: These modeline comments must be at the top of the query, but can be
+repeated, for example, the following two modeline blocks are both valid: >
+
+ ;; inherits: foo,bar
+ ;; extends
+
+ ;; extends
+ ;;
+ ;; inherits: baz
+<
+==============================================================================
+TREESITTER SYNTAX HIGHLIGHTING *treesitter-highlight*
+
+Syntax highlighting is specified through queries named `highlights.scm`,
+which match a |tsnode| in the parsed |tstree| to a `capture` that can be
+assigned a highlight group. For example, the query >
+
+ (parameters (identifier) @parameter)
+<
+matches any `identifier` node inside a function `parameter` node (e.g., the
+`bar` in `foo(bar)`) to the capture named `@parameter`. It is also possible to
+match literal expressions (provided the parser returns them): >
- (preproc_function_def name: (identifier) @function)
+ "return" @keyword.return
+<
+Assuming a suitable parser and `highlights.scm` query is found in runtimepath,
+treesitter highlighting for the current buffer can be enabled simply via
+|vim.treesitter.start()|.
+
+ *treesitter-highlight-groups*
+The capture names, with `@` included, are directly usable as highlight groups.
+For many commonly used captures, the corresponding highlight groups are linked
+to Nvim's standard |highlight-groups| by default but can be overridden in
+colorschemes.
+
+A fallback system is implemented, so that more specific groups fallback to
+more generic ones. For instance, in a language that has separate doc comments,
+`@comment.doc` could be used. If this group is not defined, the highlighting
+for an ordinary `@comment` is used. This way, existing color schemes already
+work out of the box, but it is possible to add more specific variants for
+queries that make them available.
+
+As an additional rule, capture highlights can always be specialized by
+language, by appending the language name after an additional dot. For
+instance, to highlight comments differently per language: >vim
+
+ hi @comment.c guifg=Blue
+ hi @comment.lua guifg=DarkBlue
+ hi link @comment.doc.java String
+<
+The following captures are linked by default to standard |group-name|s:
+>
+ @text.literal Comment
+ @text.reference Identifier
+ @text.title Title
+ @text.uri Underlined
+ @text.underline Underlined
+ @text.todo Todo
+
+ @comment Comment
+ @punctuation Delimiter
+
+ @constant Constant
+ @constant.builtin Special
+ @constant.macro Define
+ @define Define
+ @macro Macro
+ @string String
+ @string.escape SpecialChar
+ @string.special SpecialChar
+ @character Character
+ @character.special SpecialChar
+ @number Number
+ @boolean Boolean
+ @float Float
+
+ @function Function
+ @function.builtin Special
+ @function.macro Macro
+ @parameter Identifier
+ @method Function
+ @field Identifier
+ @property Identifier
+ @constructor Special
+
+ @conditional Conditional
+ @repeat Repeat
+ @label Label
+ @operator Operator
+ @keyword Keyword
+ @exception Exception
+
+ @variable Identifier
+ @type Type
+ @type.definition Typedef
+ @storageclass StorageClass
+ @structure Structure
+ @namespace Identifier
+ @include Include
+ @preproc PreProc
+ @debug Debug
+ @tag Tag
+<
+ *treesitter-highlight-spell*
+The special `@spell` capture can be used to indicate that a node should be
+spell checked by Nvim's builtin |spell| checker. For example, the following
+capture marks comments as to be checked: >
- ; ... more definitions
- ]]
+ (comment) @spell
+<
- highlighter = vim.treesitter.TSHighlighter.new(query, bufnr, lang)
- -- alternatively, to use the current buffer and its filetype:
- -- highlighter = vim.treesitter.TSHighlighter.new(query)
+There is also `@nospell` which disables spellchecking regions with `@spell`.
- -- Don't recreate the highlighter for the same buffer, instead
- -- modify the query like this:
- local query2 = [[ ... ]]
- highlighter:set_query(query2)
+ *treesitter-highlight-conceal*
+Treesitter highlighting supports |conceal| via the `conceal` metadata. By
+convention, nodes to be concealed are captured as `@conceal`, but any capture
+can be used. For example, the following query can be used to hide code block
+delimiters in Markdown: >
-As mentioned above the supported predicate is currently only `eq?`. `match?`
-predicates behave like matching always fails. As an addition a capture which
-begin with an upper-case letter like `@WarningMsg` will map directly to this
-highlight group, if defined. Also if the predicate begins with upper-case and
-contains a dot only the part before the first will be interpreted as the
-highlight group. As an example, this warns of a binary expression with two
-identical identifiers, highlighting both as |hl-WarningMsg|: >
+ (fenced_code_block_delimiter) @conceal (#set! conceal "")
+<
+It is also possible to replace a node with a single character, which (unlike
+legacy syntax) can be given a custom highlight. For example, the following
+(ill-advised) query replaces the `!=` operator by a Unicode glyph, which is
+still highlighted the same as other operators: >
- ((binary_expression left: (identifier) @WarningMsg.left right: (identifier) @WarningMsg.right)
- (eq? @WarningMsg.left @WarningMsg.right))
+ "!=" @operator (#set! conceal "≠")
<
-Treesitter Highlighting Priority *lua-treesitter-highlight-priority*
+Conceals specified in this way respect 'conceallevel'.
-Tree-sitter uses |nvim_buf_set_extmark()| to set highlights with a default
+ *treesitter-highlight-priority*
+Treesitter 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: >
- (
- (super_important_node) @ImportantHighlight
- ; Give the whole query highlight priority higher than the default (100)
- (set! "priority" 105)
- )
+ (super_important_node) @ImportantHighlight (#set! "priority" 105)
<
+==============================================================================
+VIM.TREESITTER *lua-treesitter*
+
+The remainder of this document is a reference manual for the `vim.treesitter`
+Lua module, which is the main interface for Nvim's tree-sitter integration.
+Most of the following content is automatically generated from the function
+documentation.
+
+
+ *vim.treesitter.language_version*
+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.
==============================================================================
Lua module: vim.treesitter *lua-treesitter-core*
-get_parser({bufnr}, {lang}, {opts}) *get_parser()*
- Gets the parser for this bufnr / ft combination.
+ *vim.treesitter.get_captures_at_cursor()*
+get_captures_at_cursor({winnr})
+ Returns a list of highlight capture names under the cursor
+
+ Parameters: ~
+ • {winnr} (number|nil) Window handle or 0 for current window (default)
+
+ Return: ~
+ string[] List of capture names
+
+ *vim.treesitter.get_captures_at_pos()*
+get_captures_at_pos({bufnr}, {row}, {col})
+ Returns a list of highlight captures at the given position
+
+ Each capture is represented by a table containing the capture name as a
+ string as well as a table of metadata (`priority`, `conceal`, ...; empty
+ if none are defined).
+
+ Parameters: ~
+ • {bufnr} (number) Buffer number (0 for current buffer)
+ • {row} (number) Position row
+ • {col} (number) Position column
+
+ Return: ~
+ table[] List of captures `{ capture = "capture name", metadata = { ...
+ } }`
+
+get_node_at_cursor({winnr}) *vim.treesitter.get_node_at_cursor()*
+ Returns the smallest named node under the cursor
+
+ Parameters: ~
+ • {winnr} (number|nil) Window handle or 0 for current window (default)
+
+ Return: ~
+ (string) Name of node under the cursor
+
+ *vim.treesitter.get_node_at_pos()*
+get_node_at_pos({bufnr}, {row}, {col}, {opts})
+ Returns the smallest named node at the given position
+
+ Parameters: ~
+ • {bufnr} (number) Buffer number (0 for current buffer)
+ • {row} (number) Position row
+ • {col} (number) Position column
+ • {opts} (table) Optional keyword arguments:
+ • lang string|nil Parser language
+ • ignore_injections boolean Ignore injected languages
+ (default true)
+
+ Return: ~
+ userdata|nil |tsnode| under the cursor
+
+get_node_range({node_or_range}) *vim.treesitter.get_node_range()*
+ Returns the node's range or an unpacked range table
+
+ Parameters: ~
+ • {node_or_range} (userdata|table) |tsnode| or table of positions
+
+ Return: ~
+ (table) `{ start_row, start_col, end_row, end_col }`
+
+get_parser({bufnr}, {lang}, {opts}) *vim.treesitter.get_parser()*
+ Returns the parser for a specific buffer and filetype and attaches it to
+ the buffer
+
+ If needed, this will create the parser.
+
+ Parameters: ~
+ • {bufnr} (number|nil) Buffer the parser should be tied to (default:
+ current buffer)
+ • {lang} (string|nil) Filetype of this parser (default: buffer
+ filetype)
+ • {opts} (table|nil) Options to pass to the created language tree
+
+ Return: ~
+ LanguageTree |LanguageTree| object to use for parsing
+
+ *vim.treesitter.get_string_parser()*
+get_string_parser({str}, {lang}, {opts})
+ Returns a string parser
- If needed this will create the parser. Unconditionally attach the provided
- callback
+ Parameters: ~
+ • {str} (string) Text to parse
+ • {lang} (string) Language of this string
+ • {opts} (table|nil) Options to pass to the created language tree
+
+ Return: ~
+ LanguageTree |LanguageTree| object to use for parsing
+
+is_ancestor({dest}, {source}) *vim.treesitter.is_ancestor()*
+ Determines whether a node is the ancestor of another
+
+ Parameters: ~
+ • {dest} userdata Possible ancestor |tsnode|
+ • {source} userdata Possible descendant |tsnode|
+
+ Return: ~
+ (boolean) True if {dest} is an ancestor of {source}
+
+ *vim.treesitter.is_in_node_range()*
+is_in_node_range({node}, {line}, {col})
+ Determines whether (line, col) position is in node range
Parameters: ~
- {bufnr} The buffer the parser should be tied to
- {lang} The filetype of this parser
- {opts} Options object to pass to the created language tree
+ • {node} userdata |tsnode| defining the range
+ • {line} (number) Line (0-based)
+ • {col} (number) Column (0-based)
Return: ~
- The parser
+ (boolean) True if the position is in node range
-get_string_parser({str}, {lang}, {opts}) *get_string_parser()*
- Gets a string parser
+node_contains({node}, {range}) *vim.treesitter.node_contains()*
+ Determines if a node contains a range
Parameters: ~
- {str} The string to parse
- {lang} The language of this string
- {opts} Options to pass to the created language tree
+ • {node} userdata |tsnode|
+ • {range} (table)
+
+ Return: ~
+ (boolean) True if the {node} contains the {range}
+
+show_tree({opts}) *vim.treesitter.show_tree()*
+ Open a window that displays a textual representation of the nodes in the
+ language tree.
+
+ While in the window, press "a" to toggle display of anonymous nodes, "I"
+ to toggle the display of the source language of each node, and press
+ <Enter> to jump to the node under the cursor in the source buffer.
+
+ Parameters: ~
+ • {opts} (table|nil) Optional options table with the following possible
+ keys:
+ • lang (string|nil): The language of the source buffer. If
+ omitted, the filetype of the source buffer is used.
+ • bufnr (number|nil): Buffer to draw the tree into. If
+ omitted, a new buffer is created.
+ • winid (number|nil): Window id to display the tree buffer in.
+ If omitted, a new window is created with {command}.
+ • command (string|nil): Vimscript command to create the
+ window. Default value is "topleft 60vnew". Only used when
+ {winid} is nil.
+ • title (string|fun(bufnr:number):string|nil): Title of the
+ window. If a function, it accepts the buffer number of the
+ source buffer as its only argument and should return a
+ string.
+
+start({bufnr}, {lang}) *vim.treesitter.start()*
+ Starts treesitter highlighting for a buffer
+
+ Can be used in an ftplugin or FileType autocommand.
+
+ Note: By default, disables regex syntax highlighting, which may be
+ required for some plugins. In this case, add `vim.bo.syntax = 'on'` after
+ the call to `start`.
+
+ Example: >lua
+
+ vim.api.nvim_create_autocmd( 'FileType', { pattern = 'tex',
+ callback = function(args)
+ vim.treesitter.start(args.buf, 'latex')
+ vim.bo[args.buf].syntax = 'on' -- only if additional legacy syntax is needed
+ end
+ })
+<
+
+ Parameters: ~
+ • {bufnr} (number|nil) Buffer to be highlighted (default: current
+ buffer)
+ • {lang} (string|nil) Language of the parser (default: buffer
+ filetype)
+
+stop({bufnr}) *vim.treesitter.stop()*
+ Stops treesitter highlighting for a buffer
+
+ Parameters: ~
+ • {bufnr} (number|nil) Buffer to stop highlighting (default: current
+ buffer)
==============================================================================
-Lua module: vim.treesitter.language *treesitter-language*
+Lua module: vim.treesitter.language *lua-treesitter-language*
-inspect_language({lang}) *inspect_language()*
+inspect_language({lang}) *vim.treesitter.language.inspect_language()*
Inspects the provided language.
Inspecting provides some useful information on the language like node
names, ...
Parameters: ~
- {lang} The language.
+ • {lang} (string) Language
+
+ Return: ~
+ (table)
-require_language({lang}, {path}, {silent}) *require_language()*
- Asserts that the provided language is installed, and optionally provide a
- path for the parser
+ *vim.treesitter.language.require_language()*
+require_language({lang}, {path}, {silent}, {symbol_name})
+ Asserts that a parser for the language {lang} is installed.
- Parsers are searched in the `parser` runtime directory.
+ Parsers are searched in the `parser` runtime directory, or the provided
+ {path}
Parameters: ~
- {lang} The language the parser should parse
- {path} Optional path the parser is located at
- {silent} Don't throw an error if language not found
+ • {lang} (string) Language the parser should parse
+ • {path} (string|nil) Optional path the parser is located at
+ • {silent} (boolean|nil) Don't throw an error if language not
+ found
+ • {symbol_name} (string|nil) Internal symbol name for the language to
+ load
+
+ Return: ~
+ (boolean) If the specified language is installed
==============================================================================
-Lua module: vim.treesitter.query *treesitter-query*
+Lua module: vim.treesitter.query *lua-treesitter-query*
-add_directive({name}, {handler}, {force}) *add_directive()*
+ *vim.treesitter.query.add_directive()*
+add_directive({name}, {handler}, {force})
Adds a new directive to be used in queries
Handlers can set match level data by setting directly on the metadata
@@ -411,61 +707,85 @@ add_directive({name}, {handler}, {force}) *add_directive()*
`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, metadata)
-
-add_predicate({name}, {handler}, {force}) *add_predicate()*
+ • {name} (string) Name of the directive, without leading #
+ • {handler} function(match:table, pattern:string, bufnr:number,
+ predicate:string[], metadata:table)
+ • match: see |treesitter-query|
+ • node-level data are accessible via `match[capture_id]`
+
+ • pattern: see |treesitter-query|
+ • predicate: list of strings containing the full directive
+ being called, e.g. `(node (#set! conceal "-"))` would get
+ the predicate `{ "#set!", "conceal", "-" }`
+
+ *vim.treesitter.query.add_predicate()*
+add_predicate({name}, {handler}, {force})
Adds a new predicate to be used in queries
Parameters: ~
- {name} the name of the predicate, without leading #
- {handler} the handler function to be used signature will be (match,
- pattern, bufnr, predicate)
-
-get_node_text({node}, {source}) *get_node_text()*
+ • {name} (string) Name of the predicate, without leading #
+ • {handler} function(match:table, pattern:string, bufnr:number,
+ predicate:string[])
+ • see |vim.treesitter.query.add_directive()| for argument
+ meanings
+
+ *vim.treesitter.query.get_node_text()*
+get_node_text({node}, {source}, {opts})
Gets the text corresponding to a given node
Parameters: ~
- {node} the node
- {source} The buffer or string from which the node is extracted
+ • {node} userdata |tsnode|
+ • {source} (number|string) Buffer or string from which the {node} is
+ extracted
+ • {opts} (table|nil) Optional parameters.
+ • concat: (boolean) Concatenate result in a string (default
+ true)
+
+ Return: ~
+ (string[]|string)
-get_query({lang}, {query_name}) *get_query()*
+get_query({lang}, {query_name}) *vim.treesitter.query.get_query()*
Returns the runtime query {query_name} for {lang}.
Parameters: ~
- {lang} The language to use for the query
- {query_name} The name of the query (i.e. "highlights")
+ • {lang} (string) Language to use for the query
+ • {query_name} (string) Name of the query (e.g. "highlights")
Return: ~
- The corresponding query, parsed.
+ Query Parsed query
- *get_query_files()*
+ *vim.treesitter.query.get_query_files()*
get_query_files({lang}, {query_name}, {is_included})
Gets the list of files used to make up a query
Parameters: ~
- {lang} The language
- {query_name} The name of the query to load
- {is_included} Internal parameter, most of the time left as `nil`
+ • {lang} (string) Language to get query for
+ • {query_name} (string) Name of the query to load (e.g., "highlights")
+ • {is_included} (boolean|nil) Internal parameter, most of the time left
+ as `nil`
-list_directives() *list_directives()*
+ Return: ~
+ string[] query_files List of files to load for given query and
+ language
+
+list_directives() *vim.treesitter.query.list_directives()*
Lists the currently available directives to use in queries.
Return: ~
- The list of supported directives.
+ string[] List of supported directives.
+
+list_predicates() *vim.treesitter.query.list_predicates()*
+ Lists the currently available predicates to use in queries.
-list_predicates() *list_predicates()*
Return: ~
- The list of supported predicates.
+ string[] List of supported predicates.
-parse_query({lang}, {query}) *parse_query()*
+parse_query({lang}, {query}) *vim.treesitter.query.parse_query()*
Parse {query} as a string. (If the query is in a file, the caller 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.
+ 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 context about {query}.
• `captures` contains the list of unique capture names defined in {query}.
@@ -473,208 +793,214 @@ parse_query({lang}, {query}) *parse_query()*
• `info.patterns` contains information about predicates.
Parameters: ~
- {lang} (string) The language
- {query} (string) A string containing the query (s-expr syntax)
+ • {lang} (string) Language to use for the query
+ • {query} (string) Query in s-expr syntax
Return: ~
- The query
+ Query Parsed query
*Query:iter_captures()*
Query:iter_captures({self}, {node}, {source}, {start}, {stop})
Iterate over all captures from all matches inside {node}
- {source} is needed if the query contains predicates, then the caller must
+ {source} is needed if the query contains predicates; then the caller must
ensure to use a freshly parsed tree consistent with the current text of
the buffer (if relevant). {start_row} and {end_row} can be used to limit
matches inside a row range (this is typically used with root node as the
- node, i e to get syntax highlight matches in the current viewport). When
- omitted the start and end row values are used from the given node.
+ {node}, i.e., to get syntax highlight matches in the current viewport).
+ When omitted, the {start} and {end} row values are used from the given
+ node.
- The iterator returns three values, a numeric id identifying the capture,
+ The iterator returns three values: a numeric id identifying the capture,
the captured node, and metadata from any directives processing the match.
- The following example shows how to get captures by name:
->
-
- for id, node, metadata in query:iter_captures(tree:root(), bufnr, first, last) do
- local name = query.captures[id] -- name of the capture in the query
- -- typically useful info about the node:
- local type = node:type() -- type of the captured node
- local row1, col1, row2, col2 = node:range() -- range of the capture
- ... use the info here ...
- end
+ The following example shows how to get captures by name: >lua
+
+ for id, node, metadata in query:iter_captures(tree:root(), bufnr, first, last) do
+ local name = query.captures[id] -- name of the capture in the query
+ -- typically useful info about the node:
+ local type = node:type() -- type of the captured node
+ local row1, col1, row2, col2 = node:range() -- range of the capture
+ ... use the info here ...
+ end
<
Parameters: ~
- {node} The node under which the search will occur
- {source} The source buffer or string to extract text from
- {start} The starting line of the search
- {stop} The stopping line of the search (end-exclusive)
- {self}
+ • {node} userdata |tsnode| under which the search will occur
+ • {source} (number|string) Source buffer or string to extract text from
+ • {start} (number) Starting line for the search
+ • {stop} (number) Stopping line for the search (end-exclusive)
+ • {self}
Return: ~
- The matching capture id
- The captured node
+ (number) capture Matching capture id
+ (table) capture_node Capture for {node}
+ (table) metadata for the {capture}
*Query:iter_matches()*
Query:iter_matches({self}, {node}, {source}, {start}, {stop})
Iterates the matches of self on a given range.
- Iterate over all matches within a node. The arguments are the same as for
- |query:iter_captures()| but the iterated values are different: an
+ Iterate over all matches within a {node}. The arguments are the same as
+ for |Query:iter_captures()| but the iterated values are different: an
(1-based) index of the pattern in the query, 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:
->
+ 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 is an example iterating over all captures in every match: >lua
- for pattern, match, metadata in cquery:iter_matches(tree:root(), bufnr, first, last) do
- for id, node in pairs(match) do
- local name = query.captures[id]
- -- `node` was captured by the `name` capture in the match
+ for pattern, match, metadata in cquery:iter_matches(tree:root(), bufnr, first, last) do
+ for id, node in pairs(match) do
+ local name = query.captures[id]
+ -- `node` was captured by the `name` capture in the match
- local node_data = metadata[id] -- Node level metadata
+ local node_data = metadata[id] -- Node level metadata
- ... use the info here ...
- end
- end
+ ... use the info here ...
+ end
+ end
<
Parameters: ~
- {node} The node under which the search will occur
- {source} The source buffer or string to search
- {start} The starting line of the search
- {stop} The stopping line of the search (end-exclusive)
- {self}
+ • {node} userdata |tsnode| under which the search will occur
+ • {source} (number|string) Source buffer or string to search
+ • {start} (number) Starting line for the search
+ • {stop} (number) Stopping line for the search (end-exclusive)
+ • {self}
Return: ~
- The matching pattern id
- The matching match
+ (number) pattern id
+ (table) match
+ (table) metadata
-set_query({lang}, {query_name}, {text}) *set_query()*
- Sets the runtime query {query_name} for {lang}
+ *vim.treesitter.query.set_query()*
+set_query({lang}, {query_name}, {text})
+ Sets the runtime query named {query_name} for {lang}
This allows users to override any runtime files and/or configuration set
by plugins.
Parameters: ~
- {lang} string: The language to use for the query
- {query_name} string: The name of the query (i.e. "highlights")
- {text} string: The query text (unparsed).
+ • {lang} (string) Language to use for the query
+ • {query_name} (string) Name of the query (e.g., "highlights")
+ • {text} (string) Query text (unparsed).
==============================================================================
-Lua module: vim.treesitter.highlighter *treesitter-highlighter*
+Lua module: vim.treesitter.highlighter *lua-treesitter-highlighter*
-new({tree}, {opts}) *highlighter.new()*
+new({tree}, {opts}) *vim.treesitter.highlighter.new()*
Creates a new highlighter using
Parameters: ~
- {tree} The language tree to use for highlighting
- {opts} Table used to configure the highlighter
- • queries: Table to overwrite queries used by the highlighter
+ • {tree} LanguageTree |LanguageTree| parser object to use for highlighting
+ • {opts} (table|nil) Configuration of the highlighter:
+ • queries table overwrite queries used by the highlighter
+
+ Return: ~
+ TSHighlighter Created highlighter object
TSHighlighter:destroy({self}) *TSHighlighter:destroy()*
Removes all internal references to the highlighter
Parameters: ~
- {self}
-
-TSHighlighter:get_query({self}, {lang}) *TSHighlighter:get_query()*
- Gets the query used for
-
- Parameters: ~
- {lang} A language used by the highlighter.
- {self}
+ • {self}
==============================================================================
-Lua module: vim.treesitter.languagetree *treesitter-languagetree*
-
-LanguageTree:add_child({self}, {lang}) *LanguageTree:add_child()*
- Adds a child language to this tree.
-
- If the language already exists as a child, it will first be removed.
-
- Parameters: ~
- {lang} The language to add.
- {self}
+Lua module: vim.treesitter.languagetree *lua-treesitter-languagetree*
LanguageTree:children({self}) *LanguageTree:children()*
Returns a map of language to child tree.
Parameters: ~
- {self}
+ • {self}
LanguageTree:contains({self}, {range}) *LanguageTree:contains()*
- Determines whether {range} is contained in this language tree
+ Determines whether {range} is contained in the |LanguageTree|.
Parameters: ~
- {range} A range, that is a `{ start_line, start_col, end_line,
- end_col }` table.
- {self}
+ • {range} (table) `{ start_line, start_col, end_line, end_col }`
+ • {self}
+
+ Return: ~
+ (boolean)
LanguageTree:destroy({self}) *LanguageTree:destroy()*
- Destroys this language tree and all its children.
+ Destroys this |LanguageTree| and all its children.
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}
+ • {self}
*LanguageTree:for_each_child()*
LanguageTree:for_each_child({self}, {fn}, {include_self})
- Invokes the callback for each LanguageTree and it's children recursively
+ Invokes the callback for each |LanguageTree| and its children recursively
Parameters: ~
- {fn} The function to invoke. This is invoked with arguments
- (tree: LanguageTree, lang: string)
- {include_self} Whether to include the invoking tree in the results.
- {self}
+ • {fn} function(tree: LanguageTree, lang: string)
+ • {include_self} (boolean) Whether to include the invoking tree in the
+ results
+ • {self}
LanguageTree:for_each_tree({self}, {fn}) *LanguageTree:for_each_tree()*
- Invokes the callback for each treesitter trees recursively.
+ Invokes the callback for each |LanguageTree| recursively.
- Note, this includes the invoking language tree's trees as well.
+ Note: This includes the invoking tree's child trees as well.
Parameters: ~
- {fn} The callback to invoke. The callback is invoked with arguments
- (tree: TSTree, languageTree: LanguageTree)
- {self}
+ • {fn} function(tree: TSTree, languageTree: LanguageTree)
+ • {self}
LanguageTree:included_regions({self}) *LanguageTree:included_regions()*
Gets the set of included regions
Parameters: ~
- {self}
+ • {self}
LanguageTree:invalidate({self}, {reload}) *LanguageTree:invalidate()*
Invalidates this parser and all its children
Parameters: ~
- {self}
+ • {self}
LanguageTree:is_valid({self}) *LanguageTree:is_valid()*
Determines whether this tree is valid. If the tree is invalid, call `parse()` . This will return the updated tree.
Parameters: ~
- {self}
+ • {self}
LanguageTree:lang({self}) *LanguageTree:lang()*
Gets the language of this tree node.
Parameters: ~
- {self}
+ • {self}
*LanguageTree:language_for_range()*
LanguageTree:language_for_range({self}, {range})
- Gets the appropriate language that contains {range}
+ Gets the appropriate language that contains {range}.
Parameters: ~
- {range} A text range, see |LanguageTree:contains|
- {self}
+ • {range} (table) `{ start_line, start_col, end_line, end_col }`
+ • {self}
+
+ Return: ~
+ LanguageTree Managing {range}
+
+ *LanguageTree:named_node_for_range()*
+LanguageTree:named_node_for_range({self}, {range}, {opts})
+ Gets the smallest named node that contains {range}.
+
+ Parameters: ~
+ • {range} (table) `{ start_line, start_col, end_line, end_col }`
+ • {opts} (table|nil) Optional keyword arguments:
+ • ignore_injections boolean Ignore injected languages
+ (default true)
+ • {self}
+
+ Return: ~
+ userdata|nil Found |tsnode|
LanguageTree:parse({self}) *LanguageTree:parse()*
Parses all defined regions using a treesitter parser for the language this
@@ -682,14 +1008,18 @@ LanguageTree:parse({self}) *LanguageTree:parse()*
determine if any child languages should be created.
Parameters: ~
- {self}
+ • {self}
+
+ Return: ~
+ userdata[] Table of parsed |tstree|
+ (table) Change list
LanguageTree:register_cbs({self}, {cbs}) *LanguageTree:register_cbs()*
- Registers callbacks for the parser.
+ Registers callbacks for the |LanguageTree|.
Parameters: ~
- {cbs} (table) An |nvim_buf_attach()|-like table argument with the
- following keys :
+ • {cbs} (table) An |nvim_buf_attach()|-like table argument with the
+ following handlers:
• `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
@@ -699,61 +1029,50 @@ LanguageTree:register_cbs({self}, {cbs}) *LanguageTree:register_cbs()*
tree.
• `on_child_removed` : emitted when a child is removed from
the tree.
- {self}
+ • {self}
-LanguageTree:remove_child({self}, {lang}) *LanguageTree:remove_child()*
- Removes a child language from this tree.
+LanguageTree:source({self}) *LanguageTree:source()*
+ Returns the source content of the language tree (bufnr or string).
Parameters: ~
- {lang} The language to remove.
- {self}
-
- *LanguageTree:set_included_regions()*
-LanguageTree:set_included_regions({self}, {regions})
- Sets the included regions that should be parsed by this parser. A region
- is a set of nodes and/or ranges that will be parsed in the same context.
-
- For example, `{ { node1 }, { node2} }` is two separate regions. This will
- be parsed by the parser in two different contexts... thus resulting in two
- separate trees.
+ • {self}
- `{ { node1, node2 } }` is a single region consisting of two nodes. This
- will be parsed by the parser in a single context... thus resulting in a
- single tree.
-
- This allows for embedded languages to be parsed together across different
- nodes, which is useful for templating languages like ERB and EJS.
-
- Note, this call invalidates the tree and requires it to be parsed again.
+ *LanguageTree:tree_for_range()*
+LanguageTree:tree_for_range({self}, {range}, {opts})
+ Gets the tree that contains {range}.
Parameters: ~
- {regions} (table) list of regions this tree should manage and parse.
- {self}
-
-LanguageTree:source({self}) *LanguageTree:source()*
- Returns the source content of the language tree (bufnr or string).
+ • {range} (table) `{ start_line, start_col, end_line, end_col }`
+ • {opts} (table|nil) Optional keyword arguments:
+ • ignore_injections boolean Ignore injected languages
+ (default true)
+ • {self}
- Parameters: ~
- {self}
+ Return: ~
+ userdata|nil Contained |tstree|
LanguageTree:trees({self}) *LanguageTree:trees()*
Returns all trees this language tree contains. Does not include child
languages.
Parameters: ~
- {self}
+ • {self}
-new({source}, {lang}, {opts}) *languagetree.new()*
- Represents a single treesitter parser for a language. The language can
- contain child languages with in its range, hence the tree.
+new({source}, {lang}, {opts}) *vim.treesitter.languagetree.new()*
+ A |LanguageTree| holds the treesitter parser for a given language {lang}
+ used to parse a buffer. As the buffer may contain injected languages, the LanguageTree needs to store parsers for these child languages as well (which in turn
+ may contain child languages themselves, hence the name).
Parameters: ~
- {source} Can be a bufnr or a string of text to parse
- {lang} The language this tree represents
- {opts} Options table
- {opts.injections} A table of language to injection query strings.
- This is useful for overriding the built-in runtime
- file searching for the injection language query per
- language.
+ • {source} (number|string) Buffer or a string of text to parse
+ • {lang} (string) Root language this tree represents
+ • {opts} (table|nil) Optional keyword arguments:
+ • injections table Mapping language to injection query
+ strings. This is useful for overriding the built-in
+ runtime file searching for the injection language query
+ per language.
+
+ Return: ~
+ LanguageTree |LanguageTree| parser object
vim:tw=78:ts=8:sw=4:sts=4:et:ft=help:norl:
diff --git a/runtime/doc/uganda.txt b/runtime/doc/uganda.txt
index 23dfa082a0..d8fc26ad17 100644
--- a/runtime/doc/uganda.txt
+++ b/runtime/doc/uganda.txt
@@ -11,9 +11,9 @@ Vim is Charityware. You can use and copy it as much as you like, but you are
encouraged to make a donation for needy children in Uganda. Please see |kcc|
below or visit the ICCF web site, available at these URLs:
- http://iccf-holland.org/
- http://www.vim.org/iccf/
- http://www.iccf.nl/
+ https://iccf-holland.org/
+ https://www.vim.org/iccf/
+ https://www.iccf.nl/
You can also sponsor the development of Vim. Vim sponsors can vote for
features. See |sponsor|. The money goes to Uganda anyway.
@@ -166,10 +166,11 @@ households are stimulated to build a proper latrine. I helped setting up a
production site for cement slabs. These are used to build a good latrine.
They are sold below cost price.
-There is a small clinic at the project, which provides children and their
-family with medical help. When needed, transport to a hospital is offered.
-Immunization programs are carried out and help is provided when an epidemic is
-breaking out (measles and cholera have been a problem).
+There is a clinic at the project, which provides children and their family
+medical help. Since 2020 a maternity ward was added and 24/7 service is
+available. When needed, transport to a hospital is offered. Immunization
+programs are carried out and help is provided when an epidemic is breaking out
+(measles and cholera have been a problem).
*donate*
Summer 1994 to summer 1995 I spent a whole year at the centre, working as a
volunteer. I have helped to expand the centre and worked in the area of water
@@ -211,44 +212,29 @@ Check the ICCF web site for the latest information! See |iccf| for the URL.
USA: The methods mentioned below can be used.
- Sending a check to the Nehemiah Group Outreach Society (NGOS)
- is no longer possible, unfortunately. We are looking for
- another way to get you an IRS tax receipt.
- For sponsoring a child contact KCF in Canada (see below). US
- checks can be sent to them to lower banking costs.
-
-Canada: Contact Kibaale Children's Fund (KCF) in Surrey, Canada. They
- take care of the Canadian sponsors for the children in
- Kibaale. KCF forwards 100% of the money to the project in
- Uganda. You can send them a one time donation directly.
+ If you must send a check send it to our Canadian partner:
+ https://www.kuwasha.net/
+
+Canada: Contact Kuwasha in Surrey, Canada. They take care of the
+ Canadian sponsors for the children in Kibaale. Kuwasha
+ forwards 100% of the money to the project in Uganda. You can
+ send them a one time donation directly.
Please send me a note so that I know what has been donated
- because of Vim. Ask KCF for information about sponsorship.
- Kibaale Children's Fund c/o Pacific Academy
- 10238-168 Street
- Surrey, B.C. V4N 1Z4
- Canada
- Phone: 604-581-5353
- If you make a donation to Kibaale Children's Fund (KCF) you
- will receive a tax receipt which can be submitted with your
- tax return.
-
-Holland: Transfer to the account of "Stichting ICCF Holland" in Lisse.
- This will allow for tax deduction if you live in Holland.
- Postbank, nr. 4548774
- IBAN: NL95 INGB 0004 5487 74
+ because of Vim. Look on their site for information about
+ sponsorship: https://www.kuwasha.net/
+ If you make a donation to Kuwasha you will receive a tax
+ receipt which can be submitted with your tax return.
+
+Holland: Transfer to the account of "Stichting ICCF Holland" in
+ Amersfoort. This will allow for tax deduction if you live in
+ Holland. ING bank, IBAN: NL95 INGB 0004 5487 74
Germany: It is possible to make donations that allow for a tax return.
Check the ICCF web site for the latest information:
- http://iccf-holland.org/germany.html
-
-World: Use a postal money order. That should be possible from any
- country, mostly from the post office. Use this name (which is
- in my passport): "Abraham Moolenaar". Use Euro for the
- currency if possible.
+ https://iccf-holland.org/germany.html
-Europe: Use a bank transfer if possible. Your bank should have a form
- that you can use for this. See "Others" below for the swift
- code and IBAN number.
+Europe: Use a bank transfer if possible. See "Others" below for the
+ swift code and IBAN number.
Any other method should work. Ask for information about
sponsorship.
@@ -258,28 +244,12 @@ Credit Card: You can use PayPal to send money with a Credit card. This is
https://www.paypal.com/en_US/mrb/pal=XAC62PML3GF8Q
The e-mail address for sending the money to is:
Bram@iccf-holland.org
- For amounts above 400 Euro ($500) sending a check is
- preferred.
-
-Others: Transfer to one of these accounts if possible:
- Postbank, account 4548774
- Swift code: INGB NL 2A
- IBAN: NL95 INGB 0004 5487 74
- under the name "stichting ICCF Holland", Lisse
- If that doesn't work:
- Rabobank Lisse, account 3765.05.117
- Swift code: RABO NL 2U
- under the name "Bram Moolenaar", Lisse
- Otherwise, send a check in euro or US dollars to the address
- below. Minimal amount: $70 (my bank does not accept smaller
- amounts for foreign check, sorry)
-
-Address to send checks to:
- Bram Moolenaar
- Finsterruetihof 1
- 8134 Adliswil
- Switzerland
-
-This address is expected to be valid for a long time.
+
+Others: Transfer to this account if possible:
+ ING bank: IBAN: NL95 INGB 0004 5487 74
+ Swift code: INGBNL2A
+ under the name "stichting ICCF Holland", Amersfoort
+ Checks are not accepted.
+
vim:tw=78:ts=8:noet:ft=help:norl:
diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt
index 3fb9ed1125..3110d0817c 100644
--- a/runtime/doc/ui.txt
+++ b/runtime/doc/ui.txt
@@ -23,40 +23,41 @@ screen grid with a size of width × height cells. This is typically done by an
embedder at startup (see |ui-startup|), but UIs can also connect to a running
Nvim instance and invoke nvim_ui_attach(). The `options` parameter is a map
with these (optional) keys:
+
*ui-rgb*
- `rgb` Decides the color format.
- true: (default) 24-bit RGB colors
- false: Terminal colors (8-bit, max 256)
- *ui-override*
- `override` Decides how UI capabilities are resolved.
- true: Enable requested UI capabilities, even
- if not supported by all connected UIs
- (including |TUI|).
- false: (default) Disable UI capabilities not
- supported by all connected UIs
- (including TUI).
- *ui-ext-options*
- `ext_cmdline` Externalize the cmdline. |ui-cmdline|
- `ext_hlstate` Detailed highlight state. |ui-hlstate|
- Sets `ext_linegrid` implicitly.
- `ext_linegrid` Line-based grid events. |ui-linegrid|
- Deactivates |ui-grid-old| implicitly.
- `ext_messages` Externalize messages. |ui-messages|
- Sets `ext_linegrid` and `ext_cmdline` implicitly.
- `ext_multigrid` Per-window grid events. |ui-multigrid|
- Sets `ext_linegrid` implicitly.
- `ext_popupmenu` Externalize |popupmenu-completion| and
- '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|.
+- `rgb` Decides the color format.
+ - true: (default) 24-bit RGB colors
+ - false: Terminal colors (8-bit, max 256)
+ *ui-override*
+- `override` Decides how UI capabilities are resolved.
+ - true: Enable requested UI capabilities, even if not
+ supported by all connected UIs (including |TUI|).
+ - false: (default) Disable UI capabilities not
+ supported by all connected UIs (including TUI).
+ *ui-ext-options*
+- `ext_cmdline` Externalize the cmdline. |ui-cmdline|
+- `ext_hlstate` Detailed highlight state. |ui-hlstate|
+ Sets `ext_linegrid` implicitly.
+- `ext_linegrid` Line-based grid events. |ui-linegrid|
+ Deactivates |ui-grid-old| implicitly.
+- `ext_messages` Externalize messages. |ui-messages|
+ Sets `ext_linegrid` and `ext_cmdline` implicitly.
+- `ext_multigrid` Per-window grid events. |ui-multigrid|
+ Sets `ext_linegrid` implicitly.
+- `ext_popupmenu` Externalize |popupmenu-completion| and
+ '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|.
+ `stdin_tty` Tells if `stdin` is a `tty` or not.
+ `stdout_tty` Tells if `stdout` is a `tty` or not.
Specifying an unknown option is an error; UIs can check the |api-metadata|
`ui_options` key for supported options.
@@ -120,7 +121,7 @@ for forward-compatibility. |api-contract|
UI startup *ui-startup*
UI embedders (clients that start Nvim with |--embed| and later call
-|nvim_ui_attach()|) must start Nvim without |--headless|: >
+|nvim_ui_attach()|) must start Nvim without |--headless|: >bash
nvim --embed
Nvim will pause before loading startup files and reading buffers, so the UI
has a chance to invoke requests and do early initialization. Startup will
@@ -133,14 +134,18 @@ procedure:
1. Invoke |nvim_get_api_info()|, if needed to setup the client library and/or
to get the list of supported UI extensions.
+
2. Do any configuration that should be happen before user config is loaded.
Buffers and windows are not available at this point, but this could be used
to set |g:| variables visible to init.vim
+
3. If the UI wants to do additional setup after user config is loaded,
- register a VimEnter autocmd: >
+ register a VimEnter autocmd: >vim
nvim_command("autocmd VimEnter * call rpcrequest(1, 'vimenter')")
-<4. Now invoke |nvim_ui_attach()|. The UI must handle user input by now:
+
+4. Now invoke |nvim_ui_attach()|. The UI must handle user input by now:
sourcing init.vim and loading buffers might lead to blocking prompts.
+
5. If step 3 was used, Nvim will send a blocking "vimenter" request to the UI.
Inside this request handler, the UI can safely do any initialization before
entering normal mode, for example reading variables set by init.vim.
@@ -164,13 +169,13 @@ Global Events *ui-global*
The following UI events are always emitted, and describe global state of
the editor.
-["set_title", title]
-["set_icon", icon]
+["set_title", title] ~
+["set_icon", icon] ~
Set the window title, and icon (minimized) window title, respectively.
In windowing systems not distinguishing between the two, "set_icon"
can be ignored.
-["mode_info_set", cursor_style_enabled, mode_info]
+["mode_info_set", cursor_style_enabled, mode_info] ~
`cursor_style_enabled` is a boolean indicating if the UI should set
the cursor style. `mode_info` is a list of mode property maps. The
current mode is given by the `mode_idx` field of the `mode_change`
@@ -197,20 +202,21 @@ the editor.
`hl_id`: Use `attr_id` instead.
`hl_lm`: Use `attr_id_lm` instead.
-["option_set", name, value]
+["option_set", name, value] ~
UI-related option changed, where `name` is one of:
- 'arabicshape'
- 'ambiwidth'
- 'emoji'
- 'guifont'
- 'guifontwide'
- 'linespace'
- 'mousefocus'
- 'pumblend'
- 'showtabline'
- 'termguicolors'
- "ext_*" (all |ui-ext-options|)
+ - 'arabicshape'
+ - 'ambiwidth'
+ - 'emoji'
+ - 'guifont'
+ - 'guifontwide'
+ - 'linespace'
+ - 'mousefocus'
+ - 'mousemoveevent'
+ - 'pumblend'
+ - 'showtabline'
+ - 'termguicolors'
+ - "ext_*" (all |ui-ext-options|)
Triggered when the UI first connects to Nvim, and whenever an option
is changed by the user or a plugin.
@@ -223,7 +229,7 @@ the editor.
however a UI might still use such options when rendering raw text
sent from Nvim, like for |ui-cmdline|.
-["mode_change", mode, mode_idx]
+["mode_change", mode, mode_idx] ~
Editor mode changed. The `mode` parameter is a string representing
the current mode. `mode_idx` is an index into the array emitted in
the `mode_info_set` event. UIs should change the cursor style
@@ -232,30 +238,30 @@ the editor.
instance more submodes and temporary states might be represented as
separate modes.
-["mouse_on"]
-["mouse_off"]
+["mouse_on"] ~
+["mouse_off"] ~
'mouse' was enabled/disabled in the current editor mode. Useful for
a terminal UI, or embedding into an application where Nvim mouse would
conflict with other usages of the mouse. Other UI:s may ignore this event.
-["busy_start"]
-["busy_stop"]
+["busy_start"] ~
+["busy_stop"] ~
Indicates to the UI that it must stop rendering the cursor. This event
is misnamed and does not actually have anything to do with busyness.
-["suspend"]
+["suspend"] ~
|:suspend| command or |CTRL-Z| mapping is used. A terminal client (or
another client where it makes sense) could suspend itself. Other
clients can safely ignore it.
-["update_menu"]
+["update_menu"] ~
The menu mappings changed.
-["bell"]
-["visual_bell"]
+["bell"] ~
+["visual_bell"] ~
Notify the user with an audible or visual bell, respectively.
-["flush"]
+["flush"] ~
Nvim is done redrawing the screen. For an implementation that renders
to an internal buffer, this is the time to display the redrawn parts
to the user.
@@ -278,11 +284,11 @@ be created; to enable per-window grids, activate |ui-multigrid|.
Highlight attribute groups are predefined. UIs should maintain a table to map
numerical highlight ids to the actual attributes.
-["grid_resize", grid, width, height]
+["grid_resize", grid, width, height] ~
Resize a `grid`. If `grid` wasn't seen by the client before, a new grid is
being created with this size.
-["default_colors_set", rgb_fg, rgb_bg, rgb_sp, cterm_fg, cterm_bg]
+["default_colors_set", rgb_fg, rgb_bg, rgb_sp, cterm_fg, cterm_bg] ~
The first three arguments set the default foreground, background and
special colors respectively. `cterm_fg` and `cterm_bg` specifies the
default color codes to use in a 256-color terminal.
@@ -299,7 +305,7 @@ numerical highlight ids to the actual attributes.
screen with changed background color itself.
*ui-event-hl_attr_define*
-["hl_attr_define", id, rgb_attr, cterm_attr, info]
+["hl_attr_define", id, rgb_attr, cterm_attr, info] ~
Add a highlight with `id` to the highlight table, with the
attributes specified by the `rgb_attr` and `cterm_attr` dicts, with the
following (all optional) keys.
@@ -318,6 +324,7 @@ numerical highlight ids to the actual attributes.
`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.
+ `altfont`: alternative font.
`blend`: Blend level (0-100). Could be used by UIs to
support blending floating windows to the
background or to signal a transparent cursor.
@@ -345,7 +352,7 @@ numerical highlight ids to the actual attributes.
`info` is an empty array by default, and will be used by the
|ui-hlstate| extension explained below.
-["hl_group_set", name, hl_id]
+["hl_group_set", name, hl_id] ~
The bulitin highlight group `name` was set to use the attributes `hl_id`
defined by a previous `hl_attr_define` call. This event is not needed
to render the grids which use attribute ids directly, but is useful
@@ -354,7 +361,7 @@ numerical highlight ids to the actual attributes.
use the |hl-Pmenu| family of builtin highlights.
*ui-event-grid_line*
-["grid_line", grid, row, col_start, cells]
+["grid_line", grid, row, col_start, cells] ~
Redraw a continuous part of a `row` on a `grid`, starting at the column
`col_start`. `cells` is an array of arrays each with 1 to 3 items:
`[text(, hl_id, repeat)]` . `text` is the UTF-8 text that should be put in
@@ -373,19 +380,19 @@ numerical highlight ids to the actual attributes.
enough to cover the remaining line, will be sent when the rest of the
line should be cleared.
-["grid_clear", grid]
+["grid_clear", grid] ~
Clear a `grid`.
-["grid_destroy", grid]
+["grid_destroy", grid] ~
`grid` will not be used anymore and the UI can free any data associated
with it.
-["grid_cursor_goto", grid, row, column]
+["grid_cursor_goto", grid, row, column] ~
Makes `grid` the current grid and `row, column` the cursor position on this
grid. This event will be sent at most once in a `redraw` batch and
indicates the visible cursor position.
-["grid_scroll", grid, top, bot, left, right, rows, cols]
+["grid_scroll", grid, top, bot, left, right, rows, cols] ~
Scroll a region of `grid`. This is semantically unrelated to editor
|scrolling|, rather this is an optimized way to say "copy these screen
cells".
@@ -438,30 +445,30 @@ Grid Events (cell-based) *ui-grid-old*
This is the legacy representation of the screen grid, emitted if |ui-linegrid|
is not active. New UIs should implement |ui-linegrid| instead.
-["resize", width, height]
+["resize", width, height] ~
The grid is resized to `width` and `height` cells.
-["clear"]
+["clear"] ~
Clear the grid.
-["eol_clear"]
+["eol_clear"] ~
Clear from the cursor position to the end of the current line.
-["cursor_goto", row, col]
+["cursor_goto", row, col] ~
Move the cursor to position (row, col). Currently, the same cursor is
used to define the position for text insertion and the visible cursor.
However, only the last cursor position, after processing the entire
array in the "redraw" event, is intended to be a visible cursor
position.
-["update_fg", color]
-["update_bg", color]
-["update_sp", color]
+["update_fg", color] ~
+["update_bg", color] ~
+["update_sp", color] ~
Set the default foreground, background and special colors
respectively.
*ui-event-highlight_set*
-["highlight_set", attrs]
+["highlight_set", attrs] ~
Set the attributes that the next text put on the grid will have.
`attrs` is a dict with the keys below. Any absent key is reset
to its default value. Color defaults are set by the `update_fg` etc
@@ -481,18 +488,18 @@ is not active. New UIs should implement |ui-linegrid| instead.
`underdotted`: underdotted text. The dots have `special` color.
`underdashed`: underdashed text. The dashes have `special` color.
-["put", text]
+["put", text] ~
The (utf-8 encoded) string `text` is put at the cursor position
(and the cursor is advanced), with the highlights as set by the
last `highlight_set` update.
-["set_scroll_region", top, bot, left, right]
+["set_scroll_region", top, bot, left, right] ~
Define the scroll region used by `scroll` below.
Note: ranges are end-inclusive, which is inconsistent with API
conventions.
-["scroll", count]
+["scroll", count] ~
Scroll the text in the scroll region. The diagrams below illustrate
what will happen, depending on the scroll direction. "=" is used to
represent the SR(scroll region) boundaries and "-" the moved rectangles.
@@ -575,7 +582,7 @@ The multigrid extension gives UIs more control over how windows are displayed:
per-window. Or reserve space around the border of the window for its own
elements, such as scrollbars from the UI toolkit.
- A dedicated grid is used for messages, which may scroll over the window
- area. (Alternatively |ext_messages| can be used).
+ area. (Alternatively |ui-messages| can be used).
By default, the grid size is handled by Nvim and set to the outer grid size
(i.e. the size of the window frame in Nvim) whenever the split is created.
@@ -587,46 +594,46 @@ A window can be hidden and redisplayed without its grid being deallocated.
This can happen multiple times for the same window, for instance when switching
tabs.
-["win_pos", grid, win, start_row, start_col, width, height]
+["win_pos", grid, win, start_row, start_col, width, height] ~
Set the position and size of the grid in Nvim (i.e. the outer grid
size). If the window was previously hidden, it should now be shown
again.
-["win_float_pos", grid, win, anchor, anchor_grid, anchor_row, anchor_col, focusable]
+["win_float_pos", grid, win, anchor, anchor_grid, anchor_row, anchor_col, focusable] ~
Display or reconfigure floating window `win`. The window should be
displayed above another grid `anchor_grid` at the specified position
`anchor_row` and `anchor_col`. For the meaning of `anchor` and more
details of positioning, see |nvim_open_win()|.
-["win_external_pos", grid, win]
+["win_external_pos", grid, win] ~
Display or reconfigure external window `win`. The window should be
displayed as a separate top-level window in the desktop environment,
or something similar.
-["win_hide", grid]
+["win_hide", grid] ~
Stop displaying the window. The window can be shown again later.
-["win_close", grid]
+["win_close", grid] ~
Close the window.
-["msg_set_pos", grid, row, scrolled, sep_char]
- Display messages on `grid`. The grid will be displayed at `row` on the
- default grid (grid=1), covering the full column width. `scrolled`
+["msg_set_pos", grid, row, scrolled, sep_char] ~
+ Display messages on `grid`. The grid will be displayed at `row` on
+ the default grid (grid=1), covering the full column width. `scrolled`
indicates whether the message area has been scrolled to cover other
- grids. It can be useful to draw a separator then ('display' msgsep
- flag). The Builtin TUI draws a full line filled with `sep_char` and
- |hl-MsgSeparator| highlight.
+ grids. It can be useful to draw a separator then |msgsep|. The Builtin
+ TUI draws a full line filled with `sep_char` ('fillchars' msgsep
+ field) and |hl-MsgSeparator| highlight.
- When |ext_messages| is active, no message grid is used, and this event
+ When |ui-messages| is active, no message grid is used, and this event
will not be sent.
-["win_viewport", grid, win, topline, botline, curline, curcol]
+["win_viewport", grid, win, topline, botline, curline, curcol] ~
Indicates the range of buffer text displayed in the window, as well
as the cursor position in the buffer. All positions are zero-based.
`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]
+["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.
@@ -638,7 +645,7 @@ Activated by the `ext_popupmenu` |ui-option|.
This UI extension delegates presentation of the |popupmenu-completion| and
command-line 'wildmenu'.
-["popupmenu_show", items, selected, row, col, grid]
+["popupmenu_show", items, selected, row, col, grid] ~
Show |popupmenu-completion|. `items` is an array of completion items
to show; each item is an array of the form [word, kind, menu, info] as
defined at |complete-items|, except that `word` is replaced by `abbr`
@@ -650,12 +657,12 @@ command-line 'wildmenu'.
set to -1 to indicate the popupmenu should be anchored to the external
cmdline. Then `col` will be a byte position in the cmdline text.
-["popupmenu_select", selected]
+["popupmenu_select", selected] ~
Select an item in the current popupmenu. `selected` is a zero-based
index into the array of items from the last popupmenu_show event, or
-1 if no item is selected.
-["popupmenu_hide"]
+["popupmenu_hide"] ~
Hide the popupmenu.
==============================================================================
@@ -663,7 +670,7 @@ Tabline Events *ui-tabline*
Activated by the `ext_tabline` |ui-option|.
-["tabline_update", curtab, tabs, curbuf, buffers]
+["tabline_update", curtab, tabs, curbuf, buffers] ~
Tabline was updated. UIs should present this data in a custom tabline
widget. Note: options `curbuf` + `buffers` were added in API7.
curtab: Current Tabpage
@@ -679,7 +686,7 @@ Activated by the `ext_cmdline` |ui-option|.
This UI extension delegates presentation of the |cmdline| (except 'wildmenu').
For command-line 'wildmenu' UI events, activate |ui-popupmenu|.
-["cmdline_show", content, pos, firstc, prompt, indent, level]
+["cmdline_show", content, pos, firstc, prompt, indent, level] ~
content: List of [attrs, string]
[[{}, "t"], [attrs, "est"], ...]
@@ -702,10 +709,10 @@ For command-line 'wildmenu' UI events, activate |ui-popupmenu|.
prompt has level 2. A command line invoked from the |cmdline-window|
has a higher level than than the edited command line.
-["cmdline_pos", pos, level]
+["cmdline_pos", pos, level] ~
Change the cursor position in the cmdline.
-["cmdline_special_char", c, shift, level]
+["cmdline_special_char", c, shift, level] ~
Display a special char in the cmdline at the cursor position. This is
typically used to indicate a pending state, e.g. after |c_CTRL-V|. If
`shift` is true the text after the cursor should be shifted, otherwise
@@ -713,12 +720,12 @@ For command-line 'wildmenu' UI events, activate |ui-popupmenu|.
Should be hidden at next cmdline_show.
-["cmdline_hide"]
+["cmdline_hide"] ~
Hide the cmdline.
-["cmdline_block_show", lines]
+["cmdline_block_show", lines] ~
Show a block of context to the current command line. For example if
- the user defines a |:function| interactively: >
+ the user defines a |:function| interactively: >vim
:function Foo()
: echo "foo"
:
@@ -726,10 +733,10 @@ For command-line 'wildmenu' UI events, activate |ui-popupmenu|.
`lines` is a list of lines of highlighted chunks, in the same form as
the "cmdline_show" `contents` parameter.
-["cmdline_block_append", line]
+["cmdline_block_append", line] ~
Append a line at the end of the currently shown block.
-["cmdline_block_hide"]
+["cmdline_block_hide"] ~
Hide the block.
==============================================================================
@@ -746,7 +753,7 @@ Nvim will not allocate screen space for the cmdline or messages, and
'cmdheight' will be forced zero. Cmdline state is emitted as |ui-cmdline|
events, which the UI must handle.
-["msg_show", kind, content, replace_last]
+["msg_show", kind, content, replace_last] ~
Display a message to the user.
kind
@@ -780,25 +787,25 @@ events, which the UI must handle.
true: Replace the message in the most-recent `msg_show` call,
but any other visible message should still remain.
-["msg_clear"]
+["msg_clear"] ~
Clear all messages currently displayed by "msg_show". (Messages sent
by other "msg_" events below will not be affected).
-["msg_showmode", content]
+["msg_showmode", content] ~
Shows 'showmode' and |recording| messages. `content` has the same
format as in "msg_show". This event is sent with empty `content` to
hide the last message.
-["msg_showcmd", content]
+["msg_showcmd", content] ~
Shows 'showcmd' messages. `content` has the same format as in "msg_show".
This event is sent with empty `content` to hide the last message.
-["msg_ruler", content]
+["msg_ruler", content] ~
Used to display 'ruler' when there is no space for the ruler in a
statusline. `content` has the same format as in "msg_show". This event is
sent with empty `content` to hide the last message.
-["msg_history_show", entries]
+["msg_history_show", entries] ~
Sent when |:messages| command is invoked. History is sent as a list of
entries, where each entry is a `[kind, content]` tuple.
diff --git a/runtime/doc/userfunc.txt b/runtime/doc/userfunc.txt
new file mode 100644
index 0000000000..9c428175bb
--- /dev/null
+++ b/runtime/doc/userfunc.txt
@@ -0,0 +1,440 @@
+*userfunc.txt* Nvim
+
+
+ VIM REFERENCE MANUAL by Bram Moolenaar
+
+
+Defining and using functions.
+
+This is introduced in section |41.7| of the user manual.
+
+ Type |gO| to see the table of contents.
+
+==============================================================================
+
+1. Defining a function ~
+ *define-function*
+New functions can be defined. These can be called just like builtin
+functions. The function executes a sequence of Ex commands. Normal mode
+commands can be executed with the |:normal| command.
+
+The function name must start with an uppercase letter, to avoid confusion with
+builtin functions. To prevent from using the same name in different scripts
+make them script-local. If you do use a global function then avoid obvious,
+short names. A good habit is to start the function name with the name of the
+script, e.g., "HTMLcolor()".
+
+It is also possible to use curly braces, see |curly-braces-names|.
+
+The |autoload| facility is useful to define a function only when it's called.
+
+ *local-function*
+A function local to a script must start with "s:". A local script function
+can only be called from within the script and from functions, user commands
+and autocommands defined in the script. It is also possible to call the
+function from a mapping defined in the script, but then |<SID>| must be used
+instead of "s:" when the mapping is expanded outside of the script.
+There are only script-local functions, no buffer-local or window-local
+functions.
+
+ *:fu* *:function* *E128* *E129* *E123*
+:fu[nction] List all functions and their arguments.
+
+:fu[nction][!] {name} List function {name}, annotated with line numbers
+ unless "!" is given.
+ {name} may be a |Dictionary| |Funcref| entry: >
+ :function dict.init
+
+:fu[nction] /{pattern} List functions with a name matching {pattern}.
+ Example that lists all functions ending with "File": >
+ :function /File$
+<
+ *:function-verbose*
+When 'verbose' is non-zero, listing a function will also display where it was
+last defined. Example: >
+
+ :verbose function SetFileTypeSH
+ function SetFileTypeSH(name)
+ Last set from /usr/share/vim/vim-7.0/filetype.vim
+<
+See |:verbose-cmd| for more information.
+
+ *E124* *E125* *E853* *E884*
+:fu[nction][!] {name}([arguments]) [range] [abort] [dict] [closure]
+ Define a new function by the name {name}. The body of
+ the function follows in the next lines, until the
+ matching |:endfunction|.
+
+ The name must be made of alphanumeric characters and
+ '_', and must start with a capital or "s:" (see
+ above). Note that using "b:" or "g:" is not allowed.
+ (since patch 7.4.260 E884 is given if the function
+ name has a colon in the name, e.g. for "foo:bar()".
+ Before that patch no error was given).
+
+ {name} can also be a |Dictionary| entry that is a
+ |Funcref|: >
+ :function dict.init(arg)
+< "dict" must be an existing dictionary. The entry
+ "init" is added if it didn't exist yet. Otherwise [!]
+ is required to overwrite an existing function. The
+ result is a |Funcref| to a numbered function. The
+ function can only be used with a |Funcref| and will be
+ deleted if there are no more references to it.
+ *E127* *E122*
+ When a function by this name already exists and [!] is
+ not used an error message is given. There is one
+ exception: When sourcing a script again, a function
+ that was previously defined in that script will be
+ silently replaced.
+ When [!] is used, an existing function is silently
+ replaced. Unless it is currently being executed, that
+ is an error.
+ NOTE: Use ! wisely. If used without care it can cause
+ an existing function to be replaced unexpectedly,
+ which is hard to debug.
+
+ For the {arguments} see |function-argument|.
+
+ *:func-range* *a:firstline* *a:lastline*
+ When the [range] argument is added, the function is
+ expected to take care of a range itself. The range is
+ passed as "a:firstline" and "a:lastline". If [range]
+ is excluded, ":{range}call" will call the function for
+ each line in the range, with the cursor on the start
+ of each line. See |function-range-example|.
+ The cursor is still moved to the first line of the
+ range, as is the case with all Ex commands.
+ *:func-abort*
+ When the [abort] argument is added, the function will
+ abort as soon as an error is detected.
+ *:func-dict*
+ When the [dict] argument is added, the function must
+ be invoked through an entry in a |Dictionary|. The
+ local variable "self" will then be set to the
+ dictionary. See |Dictionary-function|.
+ *:func-closure* *E932*
+ When the [closure] argument is added, the function
+ can access variables and arguments from the outer
+ scope. This is usually called a closure. In this
+ example Bar() uses "x" from the scope of Foo(). It
+ remains referenced even after Foo() returns: >
+ :function! Foo()
+ : let x = 0
+ : function! Bar() closure
+ : let x += 1
+ : return x
+ : endfunction
+ : return funcref('Bar')
+ :endfunction
+
+ :let F = Foo()
+ :echo F()
+< 1 >
+ :echo F()
+< 2 >
+ :echo F()
+< 3
+
+ *function-search-undo*
+ The last used search pattern and the redo command "."
+ will not be changed by the function. This also
+ implies that the effect of |:nohlsearch| is undone
+ when the function returns.
+
+ *:endf* *:endfunction* *E126* *E193* *W22*
+:endf[unction] [argument]
+ The end of a function definition. Best is to put it
+ on a line by its own, without [argument].
+
+ [argument] can be:
+ | command command to execute next
+ \n command command to execute next
+ " comment always ignored
+ anything else ignored, warning given when
+ 'verbose' is non-zero
+ The support for a following command was added in Vim
+ 8.0.0654, before that any argument was silently
+ ignored.
+
+ To be able to define a function inside an `:execute`
+ command, use line breaks instead of |:bar|: >
+ :exe "func Foo()\necho 'foo'\nendfunc"
+<
+ *:delf* *:delfunction* *E131* *E933*
+:delf[unction][!] {name}
+ Delete function {name}.
+ {name} can also be a |Dictionary| entry that is a
+ |Funcref|: >
+ :delfunc dict.init
+< This will remove the "init" entry from "dict". The
+ function is deleted if there are no more references to
+ it.
+ With the ! there is no error if the function does not
+ exist.
+ *:retu* *:return* *E133*
+:retu[rn] [expr] Return from a function. When "[expr]" is given, it is
+ evaluated and returned as the result of the function.
+ If "[expr]" is not given, the number 0 is returned.
+ When a function ends without an explicit ":return",
+ the number 0 is returned.
+ Note that there is no check for unreachable lines,
+ thus there is no warning if commands follow ":return".
+ Also, there is no check if the following
+ line contains a valid command. Forgetting the line
+ continuation backslash may go unnoticed: >
+ return 'some text'
+ .. ' some more text'
+< Will happily return "some text" without an error. It
+ should have been: >
+ return 'some text'
+ \ .. ' some more text'
+<
+ If the ":return" is used after a |:try| 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 function. The function
+ returns at the outermost ":endtry".
+
+ *function-argument* *a:var*
+An argument can be defined by giving its name. In the function this can then
+be used as "a:name" ("a:" for argument).
+ *a:0* *a:1* *a:000* *E740* *...*
+Up to 20 arguments can be given, separated by commas. After the named
+arguments an argument "..." can be specified, which means that more arguments
+may optionally be following. In the function the extra arguments can be used
+as "a:1", "a:2", etc. "a:0" is set to the number of extra arguments (which
+can be 0). "a:000" is set to a |List| that contains these arguments. Note
+that "a:1" is the same as "a:000[0]".
+ *E742*
+The a: scope and the variables in it cannot be changed, they are fixed.
+However, if a composite type is used, such as |List| or |Dictionary| , you can
+change their contents. Thus you can pass a |List| to a function and have the
+function add an item to it. If you want to make sure the function cannot
+change a |List| or |Dictionary| use |:lockvar|.
+
+It is also possible to define a function without any arguments. You must
+still supply the () then.
+
+It is allowed to define another function inside a function body.
+
+ *optional-function-argument*
+You can provide default values for positional named arguments. This makes
+them optional for function calls. When a positional argument is not
+specified at a call, the default expression is used to initialize it.
+This only works for functions declared with `:function`, not for
+lambda expressions |expr-lambda|.
+
+Example: >
+ function Something(key, value = 10)
+ echo a:key .. ": " .. a:value
+ endfunction
+ call Something('empty') "empty: 10"
+ call Something('key', 20) "key: 20"
+
+The argument default expressions are evaluated at the time of the function
+call, not definition. Thus it is possible to use an expression which is
+invalid the moment the function is defined. The expressions are also only
+evaluated when arguments are not specified during a call.
+
+ *E989*
+Optional arguments with default expressions must occur after any mandatory
+arguments. You can use "..." after all optional named arguments.
+
+It is possible for later argument defaults to refer to prior arguments,
+but not the other way around. They must be prefixed with "a:", as with all
+arguments.
+
+Example that works: >
+ :function Okay(mandatory, optional = a:mandatory)
+ :endfunction
+Example that does NOT work: >
+ :function NoGood(first = a:second, second = 10)
+ :endfunction
+<
+When not using "...", the number of arguments in a function call must be at
+least equal to the number of mandatory named arguments. When using "...", the
+number of arguments may be larger than the total of mandatory and optional
+arguments.
+
+ *local-variables*
+Inside a function local variables can be used. These will disappear when the
+function returns. Global variables need to be accessed with "g:". Inside
+functions local variables are accessed without prepending anything. But you
+can also prepend "l:" if you like. This is required for some reserved names,
+such as "version".
+
+Example: >
+ :function Table(title, ...)
+ : echohl Title
+ : echo a:title
+ : echohl None
+ : echo a:0 .. " items:"
+ : for s in a:000
+ : echon ' ' .. s
+ : endfor
+ :endfunction
+
+This function can then be called with: >
+ call Table("Table", "line1", "line2")
+ call Table("Empty Table")
+
+To return more than one value, return a |List|: >
+ :function Compute(n1, n2)
+ : if a:n2 == 0
+ : return ["fail", 0]
+ : endif
+ : return ["ok", a:n1 / a:n2]
+ :endfunction
+
+This function can then be called with: >
+ :let [success, div] = Compute(102, 6)
+ :if success == "ok"
+ : echo div
+ :endif
+<
+==============================================================================
+
+2. Calling a function ~
+ *:cal* *:call* *E107* *E117*
+:[range]cal[l] {name}([arguments])
+ Call a function. The name of the function and its arguments
+ are as specified with `:function`. Up to 20 arguments can be
+ used. The returned value is discarded.
+ Without a range and for functions that accept a range, the
+ function is called once. When a range is given the cursor is
+ positioned at the start of the first line before executing the
+ function.
+ When a range is given and the function doesn't handle it
+ itself, the function is executed for each line in the range,
+ with the cursor in the first column of that line. The cursor
+ is left at the last line (possibly moved by the last function
+ call). The arguments are re-evaluated for each line. Thus
+ this works:
+ *function-range-example* >
+ :function Mynumber(arg)
+ : echo line(".") .. " " .. a:arg
+ :endfunction
+ :1,5call Mynumber(getline("."))
+<
+ The "a:firstline" and "a:lastline" are defined anyway, they
+ can be used to do something different at the start or end of
+ the range.
+
+ Example of a function that handles the range itself: >
+
+ :function Cont() range
+ : execute (a:firstline + 1) .. "," .. a:lastline .. 's/^/\t\\ '
+ :endfunction
+ :4,8call Cont()
+<
+ This function inserts the continuation character "\" in front
+ of all the lines in the range, except the first one.
+
+ When the function returns a composite value it can be further
+ dereferenced, but the range will not be used then. Example: >
+ :4,8call GetDict().method()
+< Here GetDict() gets the range but method() does not.
+
+ *E132*
+The recursiveness of user functions is restricted with the |'maxfuncdepth'|
+option.
+
+It is also possible to use `:eval`. It does not support a range, but does
+allow for method chaining, e.g.: >
+ eval GetList()->Filter()->append('$')
+
+A function can also be called as part of evaluating an expression or when it
+is used as a method: >
+ let x = GetList()
+ let y = GetList()->Filter()
+
+
+==============================================================================
+3. Automatically loading functions ~
+ *autoload-functions*
+When using many or large functions, it's possible to automatically define them
+only when they are used. There are two methods: with an autocommand and with
+the "autoload" directory in 'runtimepath'.
+
+
+Using an autocommand ~
+
+This is introduced in the user manual, section |41.14|.
+
+The autocommand is useful if you have a plugin that is a long Vim script file.
+You can define the autocommand and quickly quit the script with `:finish`.
+That makes Vim startup faster. The autocommand should then load the same file
+again, setting a variable to skip the `:finish` command.
+
+Use the FuncUndefined autocommand event with a pattern that matches the
+function(s) to be defined. Example: >
+
+ :au FuncUndefined BufNet* source ~/vim/bufnetfuncs.vim
+
+The file "~/vim/bufnetfuncs.vim" should then define functions that start with
+"BufNet". Also see |FuncUndefined|.
+
+
+Using an autoload script ~
+ *autoload* *E746*
+This is introduced in the user manual, section |41.15|.
+
+Using a script in the "autoload" directory is simpler, but requires using
+exactly the right file name. A function that can be autoloaded has a name
+like this: >
+
+ :call filename#funcname()
+
+When such a function is called, and it is not defined yet, Vim will search the
+"autoload" directories in 'runtimepath' for a script file called
+"filename.vim". For example "~/.config/nvim/autoload/filename.vim". That
+file should then define the function like this: >
+
+ function filename#funcname()
+ echo "Done!"
+ endfunction
+
+If the file doesn't exist, Vim will also search in 'packpath' (under "start")
+to allow calling packages' functions from your |vimrc| when the packages have
+not been added to 'runtimepath' yet (see |packages|).
+
+The file name and the name used before the # in the function must match
+exactly, and the defined function must have the name exactly as it will be
+called.
+
+It is possible to use subdirectories. Every # in the function name works like
+a path separator. Thus when calling a function: >
+
+ :call foo#bar#func()
+
+Vim will look for the file "autoload/foo/bar.vim" in 'runtimepath'.
+
+This also works when reading a variable that has not been set yet: >
+
+ :let l = foo#bar#lvar
+
+However, when the autoload script was already loaded it won't be loaded again
+for an unknown variable.
+
+When assigning a value to such a variable nothing special happens. This can
+be used to pass settings to the autoload script before it's loaded: >
+
+ :let foo#bar#toggle = 1
+ :call foo#bar#func()
+
+Note that when you make a mistake and call a function that is supposed to be
+defined in an autoload script, but the script doesn't actually define the
+function, you will get an error message for the missing function. If you fix
+the autoload script it won't be automatically loaded again. Either restart
+Vim or manually source the script.
+
+Also note that if you have two script files, and one calls a function in the
+other and vice versa, before the used function is defined, it won't work.
+Avoid using the autoload functionality at the toplevel.
+
+Hint: If you distribute a bunch of scripts read |distribute-script|.
+
+
+ vim:tw=78:ts=8:noet:ft=help:norl:
diff --git a/runtime/doc/usr_01.txt b/runtime/doc/usr_01.txt
index 52fbf06ec4..f0e2462fae 100644
--- a/runtime/doc/usr_01.txt
+++ b/runtime/doc/usr_01.txt
@@ -85,7 +85,7 @@ The Vim user manual and reference manual are Copyright (c) 1988-2003 by Bram
Moolenaar. This material may be distributed only subject to the terms and
conditions set forth in the Open Publication License, v1.0 or later. The
latest version is presently available at:
- http://www.opencontent.org/openpub/
+ https://www.opencontent.org/openpub/
People who contribute to the manuals must agree with the above copyright
notice.
diff --git a/runtime/doc/usr_02.txt b/runtime/doc/usr_02.txt
index f822e7d4b8..11afe39742 100644
--- a/runtime/doc/usr_02.txt
+++ b/runtime/doc/usr_02.txt
@@ -33,7 +33,7 @@ On Unix you can type this at any command prompt. If you are running Microsoft
Windows, open a Command Prompt and enter the command. In either case, Vim
starts editing a file called file.txt. Because this is a new file, you get a
blank window. This is what your screen will look like:
-
+>
+---------------------------------------+
|# |
|~ |
@@ -43,7 +43,7 @@ blank window. This is what your screen will look like:
|"file.txt" [New file] |
+---------------------------------------+
('#' is the cursor position.)
-
+<
The tilde (~) lines indicate lines not in the file. In other words, when Vim
runs out of file to display, it displays tilde lines. At the bottom of the
screen, a message line indicates the file is named file.txt and shows that you
@@ -83,7 +83,7 @@ limerick, this is what you type: >
After typing "turtle" you press the <Enter> key to start a new line. Finally
you press the <Esc> key to stop Insert mode and go back to Normal mode. You
now have two lines of text in your Vim window:
-
+>
+---------------------------------------+
|A very intelligent turtle |
|Found programming Unix a hurdle |
@@ -91,7 +91,7 @@ now have two lines of text in your Vim window:
|~ |
| |
+---------------------------------------+
-
+<
WHAT IS THE MODE?
@@ -105,7 +105,7 @@ with a colon). Finish this command by pressing the <Enter> key (all commands
that start with a colon are finished this way).
Now, if you type the "i" command Vim will display --INSERT-- at the bottom
of the window. This indicates you are in Insert mode.
-
+>
+---------------------------------------+
|A very intelligent turtle |
|Found programming Unix a hurdle |
@@ -113,7 +113,7 @@ of the window. This indicates you are in Insert mode.
|~ |
|-- INSERT -- |
+---------------------------------------+
-
+<
If you press <Esc> to go back to Normal mode the last line will be made blank.
@@ -182,7 +182,7 @@ throwback to the old days of the typewriter, when you deleted things by typing
xxxx over them.) Move the cursor to the beginning of the first line, for
example, and type xxxxxxx (seven x's) to delete "A very ". The result should
look like this:
-
+>
+---------------------------------------+
|intelligent turtle |
|Found programming Unix a hurdle |
@@ -190,14 +190,14 @@ look like this:
|~ |
| |
+---------------------------------------+
-
+<
Now you can insert new text, for example by typing: >
iA young <Esc>
This begins an insert (the i), inserts the words "A young", and then exits
insert mode (the final <Esc>). The result:
-
+>
+---------------------------------------+
|A young intelligent turtle |
|Found programming Unix a hurdle |
@@ -205,13 +205,13 @@ insert mode (the final <Esc>). The result:
|~ |
| |
+---------------------------------------+
-
+<
DELETING A LINE
To delete a whole line use the "dd" command. The following line will
then move up to fill the gap:
-
+>
+---------------------------------------+
|Found programming Unix a hurdle |
|~ |
@@ -219,7 +219,7 @@ then move up to fill the gap:
|~ |
| |
+---------------------------------------+
-
+<
DELETING A LINE BREAK
diff --git a/runtime/doc/usr_03.txt b/runtime/doc/usr_03.txt
index 74674fdb42..2b0d40ba32 100644
--- a/runtime/doc/usr_03.txt
+++ b/runtime/doc/usr_03.txt
@@ -224,7 +224,7 @@ you can see? This figure shows the three commands you can use:
+---------------------------+
Hints: "H" stands for Home, "M" for Middle and "L" for Last. Alternatively,
-"H" for high, "M" for Middle and "L" for low.
+"H" for High, "M" for Middle and "L" for Low.
==============================================================================
*03.6* Telling where you are
diff --git a/runtime/doc/usr_05.txt b/runtime/doc/usr_05.txt
index 0e94d9a1b1..24d6185eae 100644
--- a/runtime/doc/usr_05.txt
+++ b/runtime/doc/usr_05.txt
@@ -107,7 +107,7 @@ Display an incomplete command in the lower right corner of the Vim window,
left of the ruler. For example, when you type "2f", Vim is waiting for you to
type the character to find and "2f" is displayed. When you press "w" next,
the "2fw" command is executed and the displayed "2f" is removed.
-
+>
+-------------------------------------------------+
|text in the Vim window |
|~ |
@@ -119,7 +119,7 @@ the "2fw" command is executed and the displayed "2f" is removed.
>
set incsearch
-
+<
Display matches for a search pattern while you type.
>
@@ -127,8 +127,8 @@ Display matches for a search pattern while you type.
This defines a key mapping. More about that in the next section. This
defines the "Q" command to do formatting with the "gq" operator. This is how
-it worked before Vim 5.0. Otherwise the "Q" command starts Ex mode, but you
-will not need it.
+it worked before Vim 5.0. Otherwise the "Q" command repeats the last recorded
+register.
>
vnoremap _g y:exe "grep /" .. escape(@", '\\/') .. "/ *.c *.h"<CR>
@@ -320,7 +320,7 @@ Where can you find plugins?
- Some are always loaded, you can see them in the directory $VIMRUNTIME/plugin.
- Some come with Vim. You can find them in the directory $VIMRUNTIME/macros
and its sub-directories and under $VIM/vimfiles/pack/dist/opt/.
-- Download from the net. There is a large collection on http://www.vim.org.
+- Download from the net. There is a large collection on https://www.vim.org.
- They are sometimes posted in a Vim maillist.
- You could write one yourself, see |write-plugin|.
diff --git a/runtime/doc/usr_06.txt b/runtime/doc/usr_06.txt
index 8eda33b4f0..755e6e816a 100644
--- a/runtime/doc/usr_06.txt
+++ b/runtime/doc/usr_06.txt
@@ -14,8 +14,7 @@ screen.
|06.2| No or wrong colors?
|06.3| Different colors
|06.4| With colors or without colors
-|06.5| Printing with colors
-|06.6| Further reading
+|06.5| Further reading
Next chapter: |usr_07.txt| Editing more than one file
Previous chapter: |usr_05.txt| Set your settings
@@ -191,59 +190,7 @@ buffer, set the 'syntax' option: >
:set syntax=ON
<
==============================================================================
-*06.5* Printing with colors *syntax-printing*
-
-In the MS-Windows version you can print the current file with this command: >
-
- :hardcopy
-
-You will get the usual printer dialog, where you can select the printer and a
-few settings. If you have a color printer, the paper output should look the
-same as what you see inside Vim. But when you use a dark background the
-colors will be adjusted to look good on white paper.
-
-There are several options that change the way Vim prints:
- 'printdevice'
- 'printheader'
- 'printfont'
- 'printoptions'
-
-To print only a range of lines, use Visual mode to select the lines and then
-type the command: >
-
- v100j:hardcopy
-
-"v" starts Visual mode. "100j" moves a hundred lines down, they will be
-highlighted. Then ":hardcopy" will print those lines. You can use other
-commands to move in Visual mode, of course.
-
-This also works on Unix, if you have a PostScript printer. Otherwise, you
-will have to do a bit more work. You need to convert the text to HTML first,
-and then print it from a web browser.
-
-Convert the current file to HTML with this command: >
-
- :TOhtml
-
-In case that doesn't work: >
-
- :source $VIMRUNTIME/syntax/2html.vim
-
-You will see it crunching away, this can take quite a while for a large file.
-Some time later another window shows the HTML code. Now write this somewhere
-(doesn't matter where, you throw it away later):
->
- :write main.c.html
-
-Open this file in your favorite browser and print it from there. If all goes
-well, the output should look exactly as it does in Vim. See |2html.vim| for
-details. Don't forget to delete the HTML file when you are done with it.
-
-Instead of printing, you could also put the HTML file on a web server, and let
-others look at the colored text.
-
-==============================================================================
-*06.6* Further reading
+*06.5* Further reading
|usr_44.txt| Your own syntax highlighted.
|syntax| All the details.
diff --git a/runtime/doc/usr_08.txt b/runtime/doc/usr_08.txt
index 1d20913a14..0ba03a4861 100644
--- a/runtime/doc/usr_08.txt
+++ b/runtime/doc/usr_08.txt
@@ -32,7 +32,7 @@ The easiest way to open a new window is to use the following command: >
This command splits the screen into two windows and leaves the cursor in the
top one:
-
+>
+----------------------------------+
|/* file one.c */ |
|~ |
@@ -43,7 +43,7 @@ top one:
|one.c=============================|
| |
+----------------------------------+
-
+<
What you see here is two windows on the same file. The line with "====" is
the status line. It displays information about the window above it. (In
practice the status line will be in reverse video.)
@@ -87,7 +87,7 @@ The following command opens a second window and starts editing the given file:
:split two.c
If you were editing one.c, then the result looks like this:
-
+>
+----------------------------------+
|/* file two.c */ |
|~ |
@@ -98,7 +98,7 @@ If you were editing one.c, then the result looks like this:
|one.c=============================|
| |
+----------------------------------+
-
+<
To open a window on a new, empty file, use this: >
:new
@@ -170,7 +170,7 @@ or: >
:vsplit two.c
The result looks something like this:
-
+>
+--------------------------------------+
|/* file two.c */ |/* file one.c */ |
|~ |~ |
@@ -179,7 +179,7 @@ The result looks something like this:
|two.c===============one.c=============|
| |
+--------------------------------------+
-
+<
Actually, the | lines in the middle will be in reverse video. This is called
the vertical separator. It separates the two windows left and right of it.
@@ -218,7 +218,7 @@ cursor keys can also be used, if you like.
You have split a few windows, but now they are in the wrong place. Then you
need a command to move the window somewhere else. For example, you have three
windows like this:
-
+>
+----------------------------------+
|/* file two.c */ |
|~ |
@@ -233,7 +233,7 @@ windows like this:
|one.c=============================|
| |
+----------------------------------+
-
+<
Clearly the last one should be at the top. Go to that window (using CTRL-W w)
and then type this command: >
@@ -244,7 +244,7 @@ the very top. You will notice that K is again used for moving upwards.
When you have vertical splits, CTRL-W K will move the current window to the
top and make it occupy the full width of the Vim window. If this is your
layout:
-
+>
+-------------------------------------------+
|/* two.c */ |/* three.c */ |/* one.c */ |
|~ |~ |~ |
@@ -255,9 +255,9 @@ layout:
|two.c=========three.c=========one.c========|
| |
+-------------------------------------------+
-
+<
Then using CTRL-W K in the middle window (three.c) will result in:
-
+>
+-------------------------------------------+
|/* three.c */ |
|~ |
@@ -268,7 +268,7 @@ Then using CTRL-W K in the middle window (three.c) will result in:
|two.c==================one.c===============|
| |
+-------------------------------------------+
-
+<
The other three similar commands (you can probably guess these now):
CTRL-W H move window to the far left
@@ -316,7 +316,7 @@ To make Vim open a window for each file, start it with the "-o" argument: >
vim -o one.txt two.txt three.txt
This results in:
-
+>
+-------------------------------+
|file one.txt |
|~ |
@@ -329,7 +329,7 @@ This results in:
|three.txt======================|
| |
+-------------------------------+
-
+<
The "-O" argument is used to get vertically split windows.
When Vim is already running, the ":all" command opens a window for each
file in the argument list. ":vertical all" does it with vertical splits.
@@ -347,7 +347,7 @@ Type this command in a shell to start Nvim in diff mode: >
Vim will start, with two windows side by side. You will only see the line
in which you added characters, and a few lines above and below it.
-
+>
VV VV
+-----------------------------------------+
|+ +--123 lines: /* a|+ +--123 lines: /* a| <- fold
@@ -366,7 +366,7 @@ in which you added characters, and a few lines above and below it.
|main.c~==============main.c==============|
| |
+-----------------------------------------+
-
+<
(This picture doesn't show the highlighting, use "nvim -d" for that.)
The lines that were not modified have been collapsed into one line. This is
@@ -519,7 +519,7 @@ Assume you are editing "thisfile". To create a new tab page use this command: >
This will edit the file "thatfile" in a window that occupies the whole Vim
window. And you will notice a bar at the top with the two file names:
-
+>
+----------------------------------+
| thisfile | /thatfile/ __________X| (thatfile is bold)
|/* thatfile */ |
@@ -530,13 +530,13 @@ window. And you will notice a bar at the top with the two file names:
|~ |
| |
+----------------------------------+
-
+<
You now have two tab pages. The first one has a window for "thisfile" and the
second one a window for "thatfile". It's like two pages that are on top of
each other, with a tab sticking out of each page showing the file name.
Now use the mouse to click on "thisfile" in the top line. The result is
-
+>
+----------------------------------+
| /thisfile/ | thatfile __________X| (thisfile is bold)
|/* thisfile */ |
@@ -547,7 +547,7 @@ Now use the mouse to click on "thisfile" in the top line. The result is
|~ |
| |
+----------------------------------+
-
+<
Thus you can switch between tab pages by clicking on the label in the top
line. If you don't have a mouse or don't want to use it, you can use the "gt"
command. Mnemonic: Goto Tab.
@@ -558,7 +558,7 @@ Now let's create another tab page with the command: >
This makes a new tab page with one window that is editing the same buffer as
the window we were in:
-
+>
+-------------------------------------+
| thisfile | /thisfile/ | thatfile __X| (thisfile is bold)
|/* thisfile */ |
@@ -569,7 +569,7 @@ the window we were in:
|~ |
| |
+-------------------------------------+
-
+<
You can put ":tab" before any Ex command that opens a window. The window will
be opened in a new tab page. Another example: >
diff --git a/runtime/doc/usr_10.txt b/runtime/doc/usr_10.txt
index 8844671e01..3e45fda882 100644
--- a/runtime/doc/usr_10.txt
+++ b/runtime/doc/usr_10.txt
@@ -813,10 +813,10 @@ REDRAWING THE SCREEN
If the external command produced an error message, the display may have been
messed up. Vim is very efficient and only redraws those parts of the screen
that it knows need redrawing. But it can't know about what another program
-has written. To tell Vim to redraw the screen: >
-
+has written. To tell Vim to redraw the screen:
+>
CTRL-L
-
+<
==============================================================================
Next chapter: |usr_11.txt| Recovering from a crash
diff --git a/runtime/doc/usr_20.txt b/runtime/doc/usr_20.txt
index 6a8836c8e8..2b69862fe1 100644
--- a/runtime/doc/usr_20.txt
+++ b/runtime/doc/usr_20.txt
@@ -338,7 +338,7 @@ Open the command line window with this command: >
Vim now opens a (small) window at the bottom. It contains the command line
history, and an empty line at the end:
-
+>
+-------------------------------------+
|other window |
|~ |
@@ -353,7 +353,7 @@ history, and an empty line at the end:
|command-line=========================|
| |
+-------------------------------------+
-
+<
You are now in Normal mode. You can use the "hjkl" keys to move around. For
example, move up with "5k" to the ":e config.h.in" line. Type "$h" to go to
the "i" of "in" and type "cwout". Now you have changed the line to:
diff --git a/runtime/doc/usr_21.txt b/runtime/doc/usr_21.txt
index add5d48073..191d333f3d 100644
--- a/runtime/doc/usr_21.txt
+++ b/runtime/doc/usr_21.txt
@@ -255,7 +255,8 @@ well stand for "source").
The windows that were open are restored, with the same position and size as
before. Mappings and option values are like before.
What exactly is restored depends on the 'sessionoptions' option. The
-default value is "blank,buffers,curdir,folds,help,options,winsize".
+default value is:
+"blank,buffers,curdir,folds,help,options,tabpages,winsize,terminal".
blank keep empty windows
buffers all buffers, not only the ones in a window
@@ -263,7 +264,9 @@ default value is "blank,buffers,curdir,folds,help,options,winsize".
folds folds, also manually created ones
help the help window
options all options and mappings
+ tabpages all tab pages
winsize window sizes
+ terminal include terminal windows
Change this to your liking. To also restore the size of the Vim window, for
example, use: >
@@ -299,7 +302,7 @@ session file as a starting point.
use, and save this in a session. Then you can go back to this layout whenever
you want.
For example, this is a nice layout to use:
-
+>
+----------------------------------------+
| VIM - main help file |
| |
@@ -315,7 +318,7 @@ you want.
|~/=========|[No File]===================|
| |
+----------------------------------------+
-
+<
This has a help window at the top, so that you can read this text. The narrow
vertical window on the left contains a file explorer. This is a Vim plugin
that lists the contents of a directory. You can select files to edit there.
@@ -451,7 +454,7 @@ Use this format for the modeline:
The "any-text" indicates that you can put any text before and after the part
that Vim will use. This allows making it look like a comment, like what was
-done above with /* and */.
+done above with "/*" and "*/".
The " vim:" part is what makes Vim recognize this line. There must be
white space before "vim", or "vim" must be at the start of the line. Thus
using something like "gvim:" will not work.
diff --git a/runtime/doc/usr_22.txt b/runtime/doc/usr_22.txt
index f53d578456..539bda3980 100644
--- a/runtime/doc/usr_22.txt
+++ b/runtime/doc/usr_22.txt
@@ -220,7 +220,7 @@ a tab page share this directory except for windows with a window-local
directory. Any new windows opened in this tab page will use this directory as
the current working directory. Using a `:cd` command in a tab page will not
change the working directory of tab pages which have a tab local directory.
-When the global working directory is changed using the ":cd" command in a tab
+When the global working directory is changed using the `:cd` command in a tab
page, it will also change the current tab page working directory.
diff --git a/runtime/doc/usr_25.txt b/runtime/doc/usr_25.txt
index 2efb67e55f..955d2ae5f0 100644
--- a/runtime/doc/usr_25.txt
+++ b/runtime/doc/usr_25.txt
@@ -325,16 +325,16 @@ Let's attempt to show this with one line of text. The cursor is on the "w" of
currently visible. The "window"s below the text indicate the text that is
visible after the command left of it.
- |<-- current window -->|
+ `|<-- current window -->|`
some long text, part of which is visible in the window ~
- ze |<-- window -->|
- zH |<-- window -->|
- 4zh |<-- window -->|
- zh |<-- window -->|
- zl |<-- window -->|
- 4zl |<-- window -->|
- zL |<-- window -->|
- zs |<-- window -->|
+ ze `|<-- window -->|`
+ zH `|<-- window -->|`
+ 4zh `|<-- window -->|`
+ zh `|<-- window -->|`
+ zl `|<-- window -->|`
+ 4zl `|<-- window -->|`
+ zL `|<-- window -->|`
+ zs `|<-- window -->|`
MOVING WITH WRAP OFF
@@ -350,7 +350,7 @@ scroll:
gM to middle of the text in this line
g$ to last visible character in this line
- |<-- window -->|
+ `|<-- window -->|`
some long text, part of which is visible in one line ~
g0 g^ gm gM g$
@@ -365,7 +365,7 @@ broken halfway, which makes them hard to read.
'linebreak' option. Vim then breaks lines at an appropriate place when
displaying the line. The text in the file remains unchanged.
Without 'linebreak' text might look like this:
-
+>
+---------------------------------+
|letter generation program for a b|
|ank. They wanted to send out a s|
@@ -373,12 +373,13 @@ displaying the line. The text in the file remains unchanged.
|eir richest 1000 customers. Unfo|
|rtunately for the programmer, he |
+---------------------------------+
+<
After: >
:set linebreak
it looks like this:
-
+>
+---------------------------------+
|letter generation program for a |
|bank. They wanted to send out a |
@@ -386,7 +387,7 @@ it looks like this:
|their richest 1000 customers. |
|Unfortunately for the programmer,|
+---------------------------------+
-
+<
Related options:
'breakat' specifies the characters where a break can be inserted.
'showbreak' specifies a string to show at the start of broken line.
@@ -425,7 +426,7 @@ That looks complicated. Let's break it up in pieces:
into one line.
Starting with this text, containing eight lines broken at column 30:
-
+>
+----------------------------------+
|A letter generation program |
|for a bank. They wanted to |
@@ -436,9 +437,9 @@ Starting with this text, containing eight lines broken at column 30:
|customers. Unfortunately for |
|the programmer, |
+----------------------------------+
-
+<
You end up with two lines:
-
+>
+----------------------------------+
|A letter generation program for a |
|bank. They wanted to send out a s|
@@ -446,7 +447,7 @@ You end up with two lines:
|To their richest 1000 customers. |
|Unfortunately for the programmer, |
+----------------------------------+
-
+<
Note that this doesn't work when the separating line is blank but not empty;
when it contains spaces and/or tabs. This command does work with blank lines:
>
diff --git a/runtime/doc/usr_29.txt b/runtime/doc/usr_29.txt
index d8c556c281..751cb9a902 100644
--- a/runtime/doc/usr_29.txt
+++ b/runtime/doc/usr_29.txt
@@ -33,9 +33,8 @@ 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 Universal/Exuberant ctags at:
- http://ctags.io ~
- http://ctags.sf.net ~
+If you do not have it yet, you can find Universal ctags at:
+ https://ctags.io ~
Universal ctags is preferred, Exuberant ctags is no longer being developed.
@@ -54,7 +53,7 @@ function.
The "write_line" function calls "write_char". You need to figure out what
it does. So you position the cursor over the call to "write_char" and press
CTRL-]. Now you are at the definition of "write_char".
-
+>
+-------------------------------------+
|void write_block(char **s; int cnt) |
|{ |
@@ -80,7 +79,7 @@ CTRL-]. Now you are at the definition of "write_char".
| putchar((int)(unsigned char)c); |
|} |
+------------------------------------+
-
+<
The ":tags" command shows the list of tags that you traversed through:
:tags
@@ -268,9 +267,6 @@ doesn't work if the tags file isn't sorted.
The 'taglength' option can be used to tell Vim the number of significant
characters in a tag.
-Cscope is a free program. It does not only find places where an identifier is
-declared, but also where it is used. See |cscope|.
-
==============================================================================
*29.2* The preview window
@@ -429,7 +425,7 @@ MOVING IN COMMENTS
To move back to the start of a comment use "[/". Move forward to the end of a
comment with "]/". This only works for /* - */ comments.
-
+>
+-> +-> /*
| [/ | * A comment about --+
[/ | +-- * wonderful life. | ]/
@@ -438,7 +434,7 @@ comment with "]/". This only works for /* - */ comments.
+-- foo = bar * 3; --+
| ]/
/* a short comment */ <-+
-
+<
==============================================================================
*29.4* Finding global identifiers
@@ -579,7 +575,7 @@ and jump to the first place where the word under the cursor is used: >
Hint: Goto Definition. This command is very useful to find a variable or
function that was declared locally ("static", in C terms). Example (cursor on
"counter"):
-
+>
+-> static int counter = 0;
|
| int get_counter(void)
@@ -587,7 +583,7 @@ function that was declared locally ("static", in C terms). Example (cursor on
| ++counter;
+-- return counter;
}
-
+<
To restrict the search even further, and look only in the current function,
use this command: >
@@ -597,7 +593,7 @@ This will go back to the start of the current function and find the first
occurrence of the word under the cursor. Actually, it searches backwards to
an empty line above a "{" in the first column. From there it searches forward
for the identifier. Example (cursor on "idx"):
-
+>
int find_entry(char *name)
{
+-> int idx;
@@ -606,7 +602,7 @@ for the identifier. Example (cursor on "idx"):
| if (strcmp(table[idx].name, name) == 0)
+-- return idx;
}
-
+<
==============================================================================
Next chapter: |usr_30.txt| Editing programs
diff --git a/runtime/doc/usr_30.txt b/runtime/doc/usr_30.txt
index 98d1780cc4..7e7b3b21f4 100644
--- a/runtime/doc/usr_30.txt
+++ b/runtime/doc/usr_30.txt
@@ -56,7 +56,7 @@ From this you can see that you have errors in the file "main.c". When you
press <Enter>, Vim displays the file "main.c", with the cursor positioned on
line 6, the first line with an error. You did not need to specify the file or
the line number, Vim knew where to go by looking in the error messages.
-
+>
+---------------------------------------------------+
|int main() |
|{ |
@@ -69,7 +69,7 @@ the line number, Vim knew where to go by looking in the error messages.
| ~ |
|(3 of 12): too many arguments to function 'do_sub' |
+---------------------------------------------------+
-
+<
The following command goes to where the next error occurs: >
:cnext
diff --git a/runtime/doc/usr_32.txt b/runtime/doc/usr_32.txt
index 8b489ea1e0..324efccf25 100644
--- a/runtime/doc/usr_32.txt
+++ b/runtime/doc/usr_32.txt
@@ -169,10 +169,10 @@ To travel forward in time again use the |:later| command: >
The arguments are "s", "m" and "h", just like with |:earlier|.
If you want even more details, or want to manipulate the information, you can
-use the |undotree()| function. To see what it returns: >
-
+use the |undotree()| function. To see what it returns:
+>
:echo undotree()
-
+<
==============================================================================
Next chapter: |usr_40.txt| Make new commands
diff --git a/runtime/doc/usr_40.txt b/runtime/doc/usr_40.txt
index f47c933124..8befb15528 100644
--- a/runtime/doc/usr_40.txt
+++ b/runtime/doc/usr_40.txt
@@ -226,7 +226,7 @@ When using a space inside a mapping, use <Space> (seven characters): >
This makes the spacebar move a blank-separated word forward.
It is not possible to put a comment directly after a mapping, because the "
-character is considered to be part of the mapping. You can use |", this
+character is considered to be part of the mapping. You can use `|"`, this
starts a new, empty command with a comment. Example: >
:map <Space> W| " Use spacebar to move forward a word
@@ -657,10 +657,10 @@ To ignore all events, use the following command: >
:set eventignore=all
-To set it back to the normal behavior, make 'eventignore' empty: >
-
+To set it back to the normal behavior, make 'eventignore' empty:
+>
:set eventignore=
-
+<
==============================================================================
Next chapter: |usr_41.txt| Write a Vim script
diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt
index 0c907bfb68..910aebae70 100644
--- a/runtime/doc/usr_41.txt
+++ b/runtime/doc/usr_41.txt
@@ -604,6 +604,8 @@ String manipulation: *string-functions*
fnameescape() escape a file name for use with a Vim command
tr() translate characters from one set to another
strtrans() translate a string to make it printable
+ keytrans() translate internal keycodes to a form that
+ can be used by |:map|
tolower() turn a string to lowercase
toupper() turn a string to uppercase
charclass() class of a character
@@ -617,10 +619,12 @@ String manipulation: *string-functions*
stridx() first index of a short string in a long string
strridx() last index of a short string in a long string
strlen() length of a string in bytes
- strchars() length of a string in characters
+ strcharlen() length of a string in characters
+ strchars() number of characters in a string
strwidth() size of string when displayed
strdisplaywidth() size of string when displayed, deals with tabs
setcellwidths() set character cell width overrides
+ getcellwidths() get character cell width overrides
substitute() substitute a pattern match with a string
submatch() get a specific match in ":s" and substitute()
strpart() get part of a string using byte index
@@ -637,6 +641,7 @@ String manipulation: *string-functions*
execute() execute an Ex command and get the output
win_execute() like execute() but in a specified window
trim() trim characters from a string
+ gettext() lookup message translation
List manipulation: *list-functions*
get() get an item without error for wrong index
@@ -745,6 +750,7 @@ Cursor and mark position: *cursor-functions* *mark-functions*
screencol() get screen column of the cursor
screenrow() get screen row of the cursor
screenpos() screen row and col of a text character
+ virtcol2col() byte index of a text character on screen
getcurpos() get position of the cursor
getpos() get position of cursor, mark, etc.
setpos() set position of cursor, mark, etc.
@@ -780,6 +786,13 @@ Working with text in the current buffer: *text-functions*
getcharsearch() return character search information
setcharsearch() set character search information
+Working with text in another buffer:
+ getbufline() get a list of lines from the specified buffer
+ getbufoneline() get a one line from the specified buffer
+ setbufline() replace a line in the specified buffer
+ appendbufline() append a list of lines in the specified buffer
+ deletebufline() delete lines from a specified buffer
+
*system-functions* *file-functions*
System functions and manipulation of files:
glob() expand wildcards
@@ -814,6 +827,7 @@ System functions and manipulation of files:
setenv() set an environment variable
hostname() name of the system
readfile() read a file into a List of lines
+ readblob() read a file into a Blob
readdir() get a List of file names in a directory
writefile() write a List of lines or Blob into a file
@@ -832,8 +846,10 @@ Buffers, windows and the argument list:
argidx() current position in the argument list
arglistid() get id of the argument list
argv() get one entry from the argument list
+ bufadd() add a file to the list of buffers
bufexists() check if a buffer exists
buflisted() check if a buffer exists and is listed
+ bufload() ensure a buffer is loaded
bufloaded() check if a buffer exists and is loaded
bufname() get the name of a specific buffer
bufnr() get the buffer number of a specific buffer
@@ -844,10 +860,6 @@ Buffers, windows and the argument list:
bufwinid() get the window ID of a specific buffer
bufwinnr() get the window number of a specific buffer
winbufnr() get the buffer number of a specific window
- getbufline() get a list of lines from the specified buffer
- setbufline() replace a line in the specified buffer
- appendbufline() append a list of lines in the specified buffer
- deletebufline() delete lines from a specified buffer
win_findbuf() find windows containing a buffer
win_getid() get window ID of a window
win_gettype() get type of window
@@ -872,6 +884,7 @@ Command line: *command-line-functions*
getcmdpos() get position of the cursor in the command line
getcmdscreenpos() get screen position of the cursor in the
command line
+ setcmdline() set the current 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
@@ -1012,6 +1025,7 @@ Testing: *test-functions*
assert_beeps() assert that a command beeps
assert_nobeep() assert that a command does not cause a beep
assert_fails() assert that a command fails
+ assert_report() report a test failure
Timers: *timer-functions*
timer_start() create a timer
@@ -1046,7 +1060,6 @@ Various: *various-functions*
exists() check if a variable, function, etc. exists
has() check if a feature is supported in Vim
changenr() return number of most recent change
- cscope_connection() check if a cscope connection exists
did_filetype() check if a FileType autocommand was used
eventhandler() check if invoked by an event handler
getpid() get process ID of Vim
@@ -1069,8 +1082,8 @@ Various: *various-functions*
wordcount() get byte/word/char count of buffer
luaeval() evaluate |Lua| expression
- py3eval() evaluate Python expression (|+python3|)
- pyeval() evaluate Python expression (|+python|)
+ py3eval() evaluate |Python| expression
+ pyeval() evaluate |Python| expression
pyxeval() evaluate |python_x| expression
rubyeval() evaluate |Ruby| expression
@@ -1706,7 +1719,7 @@ There is a little "catch" with comments for some commands. Examples: >
:execute cmd " do it
:!ls *.c " list C files
-The abbreviation 'dev' will be expanded to 'development " shorthand'. The
+The abbreviation "dev" will be expanded to 'development " shorthand'. The
mapping of <F3> will actually be the whole line after the 'o# ....' including
the '" insert include'. The "execute" command will give an error. The "!"
command will send everything after it to the shell, causing an error for an
@@ -1757,7 +1770,7 @@ does not exist as a mapped sequence. An error will be issued, which is very
hard to identify, because the ending whitespace character in ":unmap ,ab " is
not visible.
-And this is the same as what happens when one uses a comment after an 'unmap'
+And this is the same as what happens when one uses a comment after an "unmap"
command: >
:unmap ,ab " comment
@@ -2619,7 +2632,7 @@ Further reading: |autoload|.
==============================================================================
*41.16* Distributing Vim scripts *distribute-script*
-Vim users will look for scripts on the Vim website: http://www.vim.org.
+Vim users will look for scripts on the Vim website: https://www.vim.org.
If you made something that is useful for others, share it!
Vim scripts can be used on any system. There might not be a tar or gzip
diff --git a/runtime/doc/usr_42.txt b/runtime/doc/usr_42.txt
index 470f4e0fe5..9c5e3db72c 100644
--- a/runtime/doc/usr_42.txt
+++ b/runtime/doc/usr_42.txt
@@ -81,7 +81,7 @@ the far right.
The second number (340) determines the location of the item within the
pull-down menu. Lower numbers go on top, higher number on the bottom. These
are the priorities in the File menu:
-
+>
+-----------------+
10.310 |Open... |
10.320 |Split-Open... |
@@ -99,7 +99,7 @@ are the priorities in the File menu:
10.610 |Save-Exit |
10.620 |Exit |
+-----------------+
-
+<
Notice that there is room in between the numbers. This is where you can
insert your own items, if you really want to (it's often better to leave the
standard menus alone and add a new menu for your own items).
@@ -168,11 +168,11 @@ inserts a CTRL-C or CTRL-O for you. For example, if you use this command:
Then the resulting menu commands will be:
- Normal mode: *
- Visual mode: CTRL-C *
- Operator-pending mode: CTRL-C *
- Insert mode: CTRL-O *
- Command-line mode: CTRL-C *
+ Normal mode: `*`
+ Visual mode: CTRL-C `*`
+ Operator-pending mode: CTRL-C `*`
+ Insert mode: CTRL-O `*`
+ Command-line mode: CTRL-C `*`
When in Command-line mode the CTRL-C will abandon the command typed so far.
In Visual and Operator-pending mode CTRL-C will stop the mode. The CTRL-O in
diff --git a/runtime/doc/usr_45.txt b/runtime/doc/usr_45.txt
index 3199c4d8ea..95a2bc8f79 100644
--- a/runtime/doc/usr_45.txt
+++ b/runtime/doc/usr_45.txt
@@ -71,8 +71,8 @@ directory src/po/README.txt.
programmer. You must know both English and the language you are translating
to, of course.
When you are satisfied with the translation, consider making it available
-to others. Upload it at vim-online (http://vim.sf.net) or e-mail it to
-the Vim maintainer <maintainer@vim.org>. Or both.
+to others. Upload it to https://github.com/vim/vim or e-mail it to the Vim
+maintainer <maintainer@vim.org>. Or both.
==============================================================================
*45.2* Language for Menus
@@ -166,10 +166,7 @@ script files, etc. You can regard 'encoding' as the setting for the internals
of Vim.
This example assumes you have this font on your system. The name in the
example is for the X Window System. This font is in a package that is used to
-enhance xterm with Unicode support. If you don't have this font, you might
-find it here:
-
- http://www.cl.cam.ac.uk/~mgk25/download/ucs-fonts.tar.gz ~
+enhance xterm with Unicode support.
For MS-Windows, some fonts have a limited number of Unicode characters. Try
using the "Courier New" font. You can use the Edit/Select Font... menu to
@@ -178,10 +175,7 @@ though. Example: >
:set guifont=courier_new:h12
-If it doesn't work well, try getting a fontpack. If Microsoft didn't move it,
-you can find it here:
-
- http://www.microsoft.com/typography/fonts/default.aspx ~
+If it doesn't work well, try getting a fontpack.
Now you have told Vim to use Unicode internally and display text with a
Unicode font.
@@ -300,8 +294,7 @@ can use digraphs. This was already explained in |24.9|.
keyboard, you will want to use an Input Method (IM). This requires learning
the translation from typed keys to resulting character. When you need an IM
you probably already have one on your system. It should work with Vim like
-with other programs. For details see |mbyte-XIM| for the X Window system and
-|mbyte-IME| for MS-Windows.
+with other programs.
KEYMAPS
diff --git a/runtime/doc/usr_toc.txt b/runtime/doc/usr_toc.txt
index bf9c02882c..dd0d5784f5 100644
--- a/runtime/doc/usr_toc.txt
+++ b/runtime/doc/usr_toc.txt
@@ -5,7 +5,7 @@
Table Of Contents *user-manual*
==============================================================================
-Overview ~
+Overview
Getting Started
|usr_01.txt| About the manuals
@@ -52,7 +52,7 @@ The user manual is online:
https://neovim.io/doc/user
==============================================================================
-Getting Started ~
+Getting Started
Read this from start to end to learn the essential commands.
@@ -111,8 +111,7 @@ Read this from start to end to learn the essential commands.
|06.2| No or wrong colors?
|06.3| Different colors
|06.4| With colors or without colors
- |06.5| Printing with colors
- |06.6| Further reading
+ |06.5| Further reading
|usr_07.txt| Editing more than one file
|07.1| Edit another file
@@ -167,7 +166,7 @@ Read this from start to end to learn the essential commands.
|12.8| Find where a word is used
==============================================================================
-Editing Effectively ~
+Editing Effectively
Subjects that can be read independently.
@@ -275,7 +274,7 @@ Subjects that can be read independently.
|32.4| Time travelling
==============================================================================
-Tuning Vim ~
+Tuning Vim
Make Vim work as you like it.
diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt
index cae9c76030..e13d892fd6 100644
--- a/runtime/doc/various.txt
+++ b/runtime/doc/various.txt
@@ -102,9 +102,7 @@ g8 Print the hex values of the bytes used in the
*:p* *:pr* *:print* *E749*
:[range]p[rint] [flags]
Print [range] lines (default current line).
- Note: If you are looking for a way to print your text
- on paper see |:hardcopy|. In the GUI you can use the
- File.Print menu entry.
+ In the GUI you can use the File.Print menu entry.
See |ex-flags| for [flags].
The |:filter| command can be used to only show lines
matching a pattern.
@@ -260,6 +258,10 @@ g8 Print the hex values of the bytes used in the
Use |jobstart()| instead. >
:call jobstart('foo', {'detach':1})
<
+ For powershell, chaining a stringed executable path
+ requires using the call operator (&). >
+ :!Write-Output "1`n2" | & "C:\Windows\System32\sort.exe" /r
+<
*E34*
Any "!" in {cmd} is replaced with the previous
external command (see also 'cpoptions'), unless
diff --git a/runtime/doc/vi_diff.txt b/runtime/doc/vi_diff.txt
index cef2859eb5..afabddb7f9 100644
--- a/runtime/doc/vi_diff.txt
+++ b/runtime/doc/vi_diff.txt
@@ -337,10 +337,6 @@ Viminfo.
The 'viminfo' option can be set to select which items to store in the
.viminfo file. This is off by default.
-Printing. |printing|
- The |:hardcopy| command sends text to the printer. This can include
- syntax highlighting.
-
Mouse support. |mouse-using|
The mouse is supported in the GUI version, in an xterm for Unix, for
BSDs with sysmouse, for Linux with gpm, and for Win32. It can be used
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index 53effa1443..bb3b670b24 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -36,9 +36,8 @@ centralized reference of the differences.
- 'belloff' defaults to "all"
- 'compatible' is always disabled
- 'complete' excludes "i"
-- 'cscopeverbose' is enabled
- 'directory' defaults to ~/.local/state/nvim/swap// (|xdg|), auto-created
-- 'display' defaults to "lastline,msgsep"
+- 'display' defaults to "lastline"
- 'encoding' is UTF-8 (cf. 'fileencoding' for file-content encoding)
- 'fillchars' defaults (in effect) to "vert:│,fold:·,sep:│"
- 'formatoptions' defaults to "tcqj"
@@ -73,42 +72,41 @@ centralized reference of the differences.
- 'wildmenu' is enabled
- 'wildoptions' defaults to "pum,tagfile"
-- |man.vim| plugin is enabled, so |:Man| is available by default.
-- |matchit| plugin is enabled. To disable it in your config: >
+- |man.lua| plugin is enabled, so |:Man| is available by default.
+- |matchit| plugin is enabled. To disable it in your config: >vim
:let loaded_matchit = 1
- |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 >
+By default the mouse is enabled, and <RightMouse> opens a |popup-menu| with
+standard actions ("Cut", "Copy", "Paste", …). Mouse is NOT enabled in
+|command-mode| or the |more-prompt|, so you can temporarily disable it just by
+typing ":".
+
+If you don't like this you can disable the mouse in your |config| using any of
+the following:
+- Disable mouse completely by unsetting the 'mouse' option: >vim
set mouse=
-<
-- no |popup-menu| but the right button extends selection >
+- Pressing <RightMouse> extends selection instead of showing popup-menu: >vim
set mousemodel=extend
->
-- pressing ALT+LeftMouse releases mouse until main cursor moves >
- nnoremap <M-LeftMouse> <Cmd>
+- Pressing <A-LeftMouse> releases mouse until the cursor moves: >vim
+ nnoremap <A-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.
-
+To remove the "How-to disable mouse" menu item and the separator above it: >vim
+ aunmenu PopUp.How-to\ disable\ mouse
+ aunmenu PopUp.-1-
+<
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".
->
+>vim
nnoremap Y y$
nnoremap <C-L> <Cmd>nohlsearch<Bar>diffupdate<Bar>normal! <C-L><CR>
inoremap <C-U> <C-G>u<C-U>
@@ -131,7 +129,7 @@ nvim_cmdwin:
==============================================================================
3. New Features *nvim-features*
-MAJOR COMPONENTS ~
+MAJOR COMPONENTS
API |API|
Job control |job-control|
@@ -149,7 +147,7 @@ Terminal emulator |terminal|
Vimscript parser |nvim_parse_expression()|
XDG base directories |xdg|
-USER EXPERIENCE ~
+USER EXPERIENCE
Working intuitively and consistently is a major goal of Nvim.
@@ -162,7 +160,7 @@ Working intuitively and consistently is a major goal of Nvim.
- Nvim avoids features that cannot be provided on all platforms; instead that
is delegated to external plugins/extensions. E.g. the `-X` platform-specific
option is "sometimes" available in Vim (with potential surprises:
- http://stackoverflow.com/q/14635295).
+ https://stackoverflow.com/q/14635295).
- Vim's internal test functions (test_autochdir(), test_settime(), etc.) are
not exposed (nor implemented); instead Nvim has a robust API.
@@ -180,7 +178,7 @@ Some features are built in that otherwise required external plugins:
- Highlighting the yanked region, see |lua-highlight|.
-ARCHITECTURE ~
+ARCHITECTURE
External plugins run in separate processes. |remote-plugin| This improves
stability and allows those plugins to work without blocking the editor. Even
@@ -191,7 +189,7 @@ Platform and I/O facilities are built upon libuv. Nvim benefits from libuv
features and bug fixes, and other projects benefit from improvements to libuv
by Nvim developers.
-FEATURES ~
+FEATURES
Command-line highlighting:
The expression prompt (|@=|, |c_CTRL-R_=|, |i_CTRL-R_=|) is highlighted
@@ -210,6 +208,7 @@ Commands:
|:match| can be invoked before highlight group is defined
|:source| works with Lua
User commands can support |:command-preview| to show results as you type
+ |:write| with "++p" flag creates parent directories.
Events:
|RecordingEnter|
@@ -230,6 +229,7 @@ Functions:
|stdpath()|
|system()|, |systemlist()| can run {cmd} directly (without 'shell')
|matchadd()| can be called before highlight group is defined
+ |writefile()| with "p" flag creates parent directories.
Highlight groups:
|highlight-blend| controls blend level for a highlight group
@@ -256,9 +256,8 @@ Normal commands:
Options:
'cpoptions' flags: |cpo-_|
- 'display' flags: "msgsep" minimizes scrolling when showing messages
'guicursor' works in the terminal
- 'fillchars' flags: "msgsep" (see 'display'), "horiz", "horizup",
+ 'fillchars' flags: "msgsep", "horiz", "horizup",
"horizdown", "vertleft", "vertright", "verthoriz"
'foldcolumn' supports up to 9 dynamic/fixed columns
'inccommand' shows interactive results for |:substitute|-like commands
@@ -272,6 +271,7 @@ Options:
'tabline' %@Func@foo%X can call any function on mouse-click
'winblend' pseudo-transparency in floating windows |api-floatwin|
'winhighlight' window-local highlights
+ 'diffopt' has the option `linematch`.
Signs:
Signs are removed if the associated line is deleted.
@@ -281,7 +281,20 @@ Variables:
|v:windowid| is always available (for use by external UIs)
==============================================================================
-4. Changed features *nvim-features-changed*
+4. Upstreamed features *nvim-upstreamed*
+
+These Nvim features were later integrated into Vim.
+
+- 'fillchars' flags: "eob"
+- 'wildoptions' flags: "pum" enables popupmenu for wildmode completion
+- |<Cmd>|
+- |WinClosed|
+- |WinScrolled|
+- |:sign-define| "numhl" argument
+- |:source| works with anonymous (no file) scripts
+
+==============================================================================
+5. Changed features *nvim-features-changed*
Nvim always builds with all features, in contrast to Vim which may have
certain features removed/added at compile-time. |feature-compile|
@@ -306,7 +319,7 @@ are always available and may be used simultaneously. See |provider-python|.
structures.
2. |string()| fails immediately on nested containers, not when recursion limit
was exceeded.
-2. When |:echo| encounters duplicate containers like >
+2. When |:echo| encounters duplicate containers like >vim
let l = []
echo [l, l]
@@ -372,6 +385,7 @@ Lua interface (|lua.txt|):
Commands:
|:doautocmd| does not warn about "No matching autocommands".
|:wincmd| accepts a count.
+ `:write!` does not show a prompt if the file was updated externally.
Command line completion:
The meanings of arrow keys do not change depending on 'wildoptions'.
@@ -390,6 +404,9 @@ Highlight groups:
using |n| or |N|
|hl-CursorLine| is low-priority unless foreground color is set
|hl-VertSplit| superseded by |hl-WinSeparator|
+ Highlight groups names are allowed to contain the characters `.` and `@`.
+ It is an error to define a highlight group with a name that doesn't match
+ the regexp `[a-zA-Z0-9_.@]*` (see |group-name|).
Macro/|recording| behavior
Replay of a macro recorded during :lmap produces the same actions as when it
@@ -413,10 +430,12 @@ Normal commands:
Options:
'ttimeout', 'ttimeoutlen' behavior was simplified
- |jumpoptions| "stack" behavior
- |jumpoptions| "view" tries to restore the |mark-view| when moving through
+ '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
+ 'exrc' searches for ".nvim.lua", ".nvimrc", or ".exrc" files. The user is
+ prompted whether to trust the file.
Shell:
Shell output (|:!|, |:make|, …) is always routed through the UI, so it
@@ -460,7 +479,7 @@ TUI:
<
*'term'* *E529* *E530* *E531*
'term' reflects the terminal type derived from |$TERM| and other environment
- checks. For debugging only; not reliable during startup. >
+ checks. For debugging only; not reliable during startup. >vim
:echo &term
< "builtin_x" means one of the |builtin-terms| was chosen, because the expected
terminfo file was not found on the system.
@@ -475,6 +494,11 @@ TUI:
UI/Display:
|Visual| selection highlights the character at cursor. |visual-use|
+ messages: When showing messages longer than 'cmdheight', only
+ scroll the message lines, not the entire screen. The
+ separator line is decorated by |hl-MsgSeparator| and
+ the "msgsep" flag of 'fillchars'. *msgsep*
+
Vimscript compatibility:
`count` does not alias to |v:count|
`errmsg` does not alias to |v:errmsg|
@@ -492,17 +516,20 @@ Working directory (Vim implemented some of these later than Nvim):
working directory. Use `getcwd(-1, -1)` to get the global working directory.
==============================================================================
-5. Missing legacy features *nvim-features-missing*
+6. Missing legacy features *nvim-features-missing*
-Some legacy Vim features are not implemented:
+Some legacy Vim features are not yet implemented:
-- |if_lua|: Nvim Lua API is not compatible with Vim's "if_lua"
+- *if_lua* : Nvim |Lua| API is not compatible with Vim's "if_lua"
- *if_mzscheme*
-- |if_py|: *python-bindeval* *python-Function* are not supported
+- |if_pyth|: *python-bindeval* *python-Function* are not supported
- *if_tcl*
+*:gui*
+*:gvim*
+
==============================================================================
-6. Removed features *nvim-features-removed*
+7. Removed features *nvim-features-removed*
These Vim features were intentionally removed from Nvim.
@@ -522,6 +549,7 @@ Aliases:
Commands:
:fixdel
+ :hardcopy
:helpfind
:mode (no longer accepts an argument)
:open
@@ -533,6 +561,10 @@ Commands:
:sleep! (does not hide the cursor; same as :sleep)
:smile
:tearoff
+ :cstag
+ :cscope
+ :lcscope
+ :scscope
Compile-time features:
Emacs tags support
@@ -540,6 +572,7 @@ Compile-time features:
Eval:
Vim9script
+ *cscope_connection()*
*js_encode()*
*js_decode()*
*v:none* (used by Vim to represent JavaScript "undefined"); use |v:null| instead.
@@ -553,7 +586,7 @@ Events:
Highlight groups:
*hl-StatusLineTerm* *hl-StatusLineTermNC* are unnecessary because Nvim
supports 'winhighlight' window-local highlights.
- For example, to mimic Vim's StatusLineTerm: >
+ For example, to mimic Vim's StatusLineTerm: >vim
hi StatusLineTerm ctermfg=black ctermbg=green
hi StatusLineTermNC ctermfg=green
autocmd TermOpen,WinEnter * if &buftype=='terminal'
@@ -562,18 +595,25 @@ Highlight groups:
<
Options:
- 'antialias'
+ antialias
*'balloondelay'* *'bdlay'*
*'ballooneval'* *'beval'* *'noballooneval'* *'nobeval'*
*'balloonexpr'* *'bexpr'*
- 'bioskey' (MS-DOS)
- 'conskey' (MS-DOS)
+ bioskey (MS-DOS)
+ conskey (MS-DOS)
*'cp'* *'nocompatible'* *'nocp'* *'compatible'* (Nvim is always "nocompatible".)
'cpoptions' (gjkHw<*- and all POSIX flags were removed)
*'cryptmethod'* *'cm'* *'key'* (Vim encryption implementation)
+ cscopepathcomp
+ cscopeprg
+ cscopequickfix
+ cscoperelative
+ cscopetag
+ cscopetagorder
+ cscopeverbose
*'ed'* *'edcompatible'* *'noed'* *'noedcompatible'*
'encoding' ("utf-8" is always used)
- 'esckeys'
+ esckeys
'guioptions' "t" flag was removed
*'guifontset'* *'gfs'* (Use 'guifont' instead.)
*'guipty'* (Nvim uses pipes and PTYs consistently on all platforms.)
@@ -582,7 +622,7 @@ Options:
*'imactivatekey'* *'imak'*
*'imstatusfunc'* *'imsf'*
*'insertmode'* *'im'* Use the following script to emulate 'insertmode':
->
+>vim
autocmd BufWinEnter * startinsert
inoremap <Esc> <C-X><C-Z><C-]>
inoremap <C-C> <C-X><C-Z>
@@ -614,18 +654,27 @@ Options:
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.
+ *'maxmem'* Nvim delegates memory-management to the OS.
+ *'maxmemtot'* Nvim delegates memory-management to the OS.
+ *'printdevice'*
+ *'printencoding'*
+ *'printexpr'*
+ *'printfont'*
+ *'printheader'*
+ *'printmbcharset'*
*'prompt'* *'noprompt'*
*'remap'* *'noremap'*
*'restorescreen'* *'rs'* *'norestorescreen'* *'nors'*
- 'shelltype'
+ *'secure'*
+ Everything is allowed in 'exrc' files since they must be explicitly marked
+ trusted.
+ *'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'
+ textauto
+ textmode
*'toolbar'* *'tb'*
*'toolbariconsize'* *'tbis'*
*'ttybuiltin'* *'tbi'* *'nottybuiltin'* *'notbi'*
@@ -633,7 +682,10 @@ Options:
*'ttymouse'* *'ttym'*
*'ttyscroll'* *'tsl'*
*'ttytype'* *'tty'*
- 'weirdinvert'
+ weirdinvert
+
+Performance:
+ Folds are not updated during insert-mode.
Startup:
--literal (file args are always literal; to expand wildcards on Windows, use
@@ -683,5 +735,14 @@ TUI:
at how the terminal is sending CSI. Nvim does not issue such a sequence and
always uses 7-bit control sequences.
+Cscope:
+ *cscope*
+ Cscope support has been removed in favour of LSP based solutions.
+
+Hardcopy:
+ *hardcopy*
+ `:hardcopy` was removed. Instead, use `:TOhtml` and print the resulting HTML
+ using a web browser or some other HTML viewer.
+
==============================================================================
vim:tw=78:ts=8:sw=2:et:ft=help:norl:
diff --git a/runtime/doc/visual.txt b/runtime/doc/visual.txt
index 5383ea4f72..0c6bd4f3a1 100644
--- a/runtime/doc/visual.txt
+++ b/runtime/doc/visual.txt
@@ -103,7 +103,7 @@ gn Search forward for the last used search pattern, like
E.g., "dgn" deletes the text of the next match.
If Visual mode is active, extends the selection
until the end of the next match.
- 'wrapscan' applies
+ 'wrapscan' applies.
Note: Unlike `n` the search direction does not depend
on the previous search command.
@@ -501,11 +501,11 @@ 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>
+ :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>
+ :snoremap <C-K> <Esc>
<
Users will expect printable characters to replace the selected area.
Therefore avoid mapping printable characters in Select mode. Or use
diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt
index 7355cec522..61f5013f47 100644
--- a/runtime/doc/windows.txt
+++ b/runtime/doc/windows.txt
@@ -119,7 +119,7 @@ windows.
*filler-lines*
The lines after the last buffer line in a window are called filler lines. By
-default, these lines start with a tilde (~) character. The 'eob' item in the
+default, these lines start with a tilde (~) character. The "eob" item in the
'fillchars' option can be used to change this character. By default, these
characters are highlighted as NonText (|hl-NonText|). The EndOfBuffer
highlight group (|hl-EndOfBuffer|) can be used to change the highlighting of
@@ -148,7 +148,7 @@ CTRL-W CTRL-S *CTRL-W_CTRL-S*
Note: CTRL-S does not work on all terminals and might block
further input, use CTRL-Q to get going again.
Also see |++opt| and |+cmd|.
- *E242*
+ *E242* *E1159*
Be careful when splitting a window in an autocommand, it may
mess up the window layout if this happens while making other
window layout changes.
@@ -163,6 +163,8 @@ CTRL-W v *CTRL-W_v*
3. 'eadirection' isn't "ver", and
4. one of the other windows is wider than the current or new
window.
+ If N was given make the new window N columns wide, if
+ possible.
Note: In other places CTRL-Q does the same as CTRL-V, but here
it doesn't!
@@ -233,9 +235,16 @@ and 'winminwidth' are relevant.
*:vert* *:vertical*
:vert[ical] {cmd}
Execute {cmd}. If it contains a command that splits a window,
- it will be split vertically.
+ it will be split vertically. For `vertical wincmd =` windows
+ will be equalized only vertically.
Doesn't work for |:execute| and |:normal|.
+ *:hor* *:horizontal*
+:hor[izontal] {cmd}
+ Execute {cmd}. Currently only makes a difference for
+ `horizontal wincmd =`, which will equalize windows only
+ horizontally.
+
:lefta[bove] {cmd} *:lefta* *:leftabove*
:abo[veleft] {cmd} *:abo* *:aboveleft*
Execute {cmd}. If it contains a command that splits a window,
@@ -275,16 +284,15 @@ Opens a vertically split, full-height window on the "tags" file at the far
left of the Vim window.
+------------------------------------------------------------------------------
Closing a window
-----------------
:q[uit]
:{count}q[uit] *:count_quit*
CTRL-W q *CTRL-W_q*
CTRL-W CTRL-Q *CTRL-W_CTRL-Q*
- Without {count}: Quit the current window. If {count} is
- given quit the {count} window
-
+ Without {count}: Quit the current window. If {count} is
+ given quit the {count} window.
*edit-window*
When quitting the last edit window (not counting help or
preview windows), exit Vim.
@@ -343,7 +351,7 @@ CTRL-W CTRL-C *CTRL-W_CTRL-C*
window, but that does not work, because the CTRL-C cancels the
command.
- *:hide*
+ *:hide*
:hid[e]
:{count}hid[e]
Without {count}: Quit the current window, unless it is the
@@ -528,6 +536,10 @@ CTRL-W = Make all windows (almost) equally high and wide, but use
'winheight' and 'winwidth' for the current window.
Windows with 'winfixheight' set keep their height and windows
with 'winfixwidth' set keep their width.
+ To equalize only vertically (make window equally high) use
+ `vertical wincmd =` .
+ To equalize only horizontally (make window equally wide) use
+ `horizontal wincmd =` .
:res[ize] -N *:res* *:resize* *CTRL-W_-*
CTRL-W - Decrease current window height by N (default 1).
@@ -595,6 +607,53 @@ it).
The minimal height and width of a window is set with 'winminheight' and
'winminwidth'. These are hard values, a window will never become smaller.
+
+WinScrolled and WinResized autocommands ~
+ *win-scrolled-resized*
+If you want to get notified of changes in window sizes, the |WinResized|
+autocommand event can be used.
+If you want to get notified of text in windows scrolling vertically or
+horizontally, the |WinScrolled| autocommand event can be used. This will also
+trigger in window size changes.
+Exception: the events will not be triggered when the text scrolls for
+'incsearch'.
+ *WinResized-event*
+The |WinResized| event is triggered after updating the display, several
+windows may have changed size then. A list of the IDs of windows that changed
+since last time is provided in the v:event.windows variable, for example:
+ [1003, 1006]
+ *WinScrolled-event*
+The |WinScrolled| event is triggered after |WinResized|, and also if a window
+was scrolled. That can be vertically (the text at the top of the window
+changed) or horizontally (when 'wrap' is off or when the first displayed part
+of the first line changes). Note that |WinScrolled| will trigger many more
+times than |WinResized|, it may slow down editing a bit.
+
+The information provided by |WinScrolled| is a dictionary for each window that
+has changes, using the window ID as the key, and a total count of the changes
+with the key "all". Example value for |v:event|:
+ {
+ all: {width: 0, height: 2, leftcol: 0, skipcol: 0, topline: 1, topfill: 0},
+ 1003: {width: 0, height: -1, leftcol: 0, skipcol: 0, topline: 0, topfill: 0},
+ 1006: {width: 0, height: 1, leftcol: 0, skipcol: 0, topline: 1, topfill: 0},
+ }
+
+Note that the "all" entry has the absolute values of the individual windows
+accumulated.
+
+If you need more information about what changed, or you want to "debounce" the
+events (not handle every event to avoid doing too much work), you may want to
+use the `winlayout()` and `getwininfo()` functions.
+
+|WinScrolled| and |WinResized| do not trigger when the first autocommand is
+added, only after the first scroll or resize. They may trigger when switching
+to another tab page.
+
+The commands executed are expected to not cause window size or scroll changes.
+If this happens anyway, the event will trigger again very soon. In other
+words: Just before triggering the event, the current sizes and scroll
+positions are stored and used to decide whether there was a change.
+
==============================================================================
7. Argument and buffer list commands *buffer-list*
@@ -634,8 +693,8 @@ Note: ":next" is an exception, because it must accept a list of file names
for compatibility with Vi.
+------------------------------------------------------------------------------
The argument list and multiple windows
---------------------------------------
The current position in the argument list can be different for each window.
Remember that when doing ":e file", the position in the argument list stays
@@ -738,6 +797,7 @@ can also get to them with the buffer list commands, like ":bnext".
the current window.
{cmd} can contain '|' to concatenate several commands.
{cmd} must not open or close windows or reorder them.
+
Also see |:tabdo|, |:argdo|, |:bufdo|, |:cdo|, |:ldo|,
|:cfdo| and |:lfdo|.
@@ -765,6 +825,7 @@ can also get to them with the buffer list commands, like ":bnext".
autocommand event is disabled by adding it to
'eventignore'. This considerably speeds up editing
each buffer.
+
Also see |:tabdo|, |:argdo|, |:windo|, |:cdo|, |:ldo|,
|:cfdo| and |:lfdo|.
diff --git a/runtime/filetype.lua b/runtime/filetype.lua
index 9f5b5fd0dc..f772785d21 100644
--- a/runtime/filetype.lua
+++ b/runtime/filetype.lua
@@ -1,12 +1,11 @@
--- Skip if legacy filetype is enabled or filetype detection is disabled
-if vim.g.do_legacy_filetype or vim.g.did_load_filetypes then
+if vim.g.did_load_filetypes then
return
end
vim.g.did_load_filetypes = 1
vim.api.nvim_create_augroup('filetypedetect', { clear = false })
-vim.api.nvim_create_autocmd({ 'BufRead', 'BufNewFile' }, {
+vim.api.nvim_create_autocmd({ 'BufRead', 'BufNewFile', 'StdinReadPost' }, {
group = 'filetypedetect',
callback = function(args)
local ft, on_detect = vim.filetype.match({ filename = args.match, buf = args.buf })
@@ -14,10 +13,14 @@ vim.api.nvim_create_autocmd({ 'BufRead', 'BufNewFile' }, {
-- 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 } }, {})
+ vim.api.nvim_buf_call(args.buf, function()
+ vim.api.nvim_cmd({ cmd = 'setf', args = { 'FALLBACK', ft } }, {})
+ end)
end
else
- vim.api.nvim_buf_set_option(args.buf, 'filetype', ft)
+ vim.api.nvim_buf_call(args.buf, function()
+ vim.api.nvim_cmd({ cmd = 'setf', args = { ft } }, {})
+ end)
if on_detect then
on_detect(args.buf)
end
diff --git a/runtime/filetype.vim b/runtime/filetype.vim
deleted file mode 100644
index 52a20d5c10..0000000000
--- a/runtime/filetype.vim
+++ /dev/null
@@ -1,2612 +0,0 @@
-" Vim support file to detect file types
-"
-" Maintainer: Bram Moolenaar <Bram@vim.org>
-" Last Change: 2022 Jul 5
-
-" Only run this if enabled
-if !exists("do_legacy_filetype")
- finish
-endif
-
-" Listen very carefully, I will say this only once
-if exists("did_load_filetypes")
- finish
-endif
-let did_load_filetypes = 1
-
-" Line continuation is used here, remove 'C' from 'cpoptions'
-let s:cpo_save = &cpo
-set cpo&vim
-
-augroup filetypedetect
-
-" Ignored extensions
-if exists("*fnameescape")
-au BufNewFile,BufRead ?\+.orig,?\+.bak,?\+.old,?\+.new,?\+.dpkg-dist,?\+.dpkg-old,?\+.dpkg-new,?\+.dpkg-bak,?\+.rpmsave,?\+.rpmnew,?\+.pacsave,?\+.pacnew
- \ exe "doau filetypedetect BufRead " . fnameescape(expand("<afile>:r"))
-au BufNewFile,BufRead *~
- \ let s:name = expand("<afile>") |
- \ let s:short = substitute(s:name, '\~$', '', '') |
- \ if s:name != s:short && s:short != "" |
- \ exe "doau filetypedetect BufRead " . fnameescape(s:short) |
- \ endif |
- \ unlet! s:name s:short
-au BufNewFile,BufRead ?\+.in
- \ if expand("<afile>:t") != "configure.in" |
- \ exe "doau filetypedetect BufRead " . fnameescape(expand("<afile>:r")) |
- \ endif
-elseif &verbose > 0
- echomsg "Warning: some filetypes will not be recognized because this version of Vim does not have fnameescape()"
-endif
-
-" Pattern used to match file names which should not be inspected.
-" Currently finds compressed files.
-if !exists("g:ft_ignore_pat")
- let g:ft_ignore_pat = '\.\(Z\|gz\|bz2\|zip\|tgz\)$'
-endif
-
-" Function used for patterns that end in a star: don't set the filetype if the
-" 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)
- if expand("<amatch>") !~ g:ft_ignore_pat
- exe 'setf ' . a:ft
- endif
-endfunc
-
-" Vim help file
-au BufNewFile,BufRead $VIMRUNTIME/doc/*.txt setf help
-
-" Abaqus or Trasys
-au BufNewFile,BufRead *.inp call dist#ft#Check_inp()
-
-" 8th (Firth-derivative)
-au BufNewFile,BufRead *.8th setf 8th
-
-" A-A-P recipe
-au BufNewFile,BufRead *.aap setf aap
-
-" A2ps printing utility
-au BufNewFile,BufRead */etc/a2ps.cfg,*/etc/a2ps/*.cfg,a2psrc,.a2psrc setf a2ps
-
-" ABAB/4
-au BufNewFile,BufRead *.abap setf abap
-
-" ABC music notation
-au BufNewFile,BufRead *.abc setf abc
-
-" ABEL
-au BufNewFile,BufRead *.abl setf abel
-
-" AceDB
-au BufNewFile,BufRead *.wrm setf acedb
-
-" Ada (83, 9X, 95)
-au BufNewFile,BufRead *.adb,*.ads,*.ada setf ada
-au BufNewFile,BufRead *.gpr setf ada
-
-" AHDL
-au BufNewFile,BufRead *.tdf setf ahdl
-
-" AIDL
-au BufNewFile,BufRead *.aidl setf aidl
-
-" AMPL
-au BufNewFile,BufRead *.run setf ampl
-
-" Ant
-au BufNewFile,BufRead build.xml setf ant
-
-" Arduino
-au BufNewFile,BufRead *.ino,*.pde setf arduino
-
-" Apache config file
-au BufNewFile,BufRead .htaccess,*/etc/httpd/*.conf setf apache
-au BufNewFile,BufRead */etc/apache2/sites-*/*.com setf apache
-
-" XA65 MOS6510 cross assembler
-au BufNewFile,BufRead *.a65 setf a65
-
-" Applescript
-au BufNewFile,BufRead *.scpt setf applescript
-
-" Applix ELF
-au BufNewFile,BufRead *.am
- \ if expand("<afile>") !~? 'Makefile.am\>' | setf elf | endif
-
-" ALSA configuration
-au BufNewFile,BufRead .asoundrc,*/usr/share/alsa/alsa.conf,*/etc/asound.conf setf alsaconf
-
-" Arc Macro Language
-au BufNewFile,BufRead *.aml setf aml
-
-" APT config file
-au BufNewFile,BufRead apt.conf setf aptconf
-au BufNewFile,BufRead */.aptitude/config setf aptconf
-" more generic pattern far down
-
-" Arch Inventory file
-au BufNewFile,BufRead .arch-inventory,=tagging-method setf arch
-
-" ART*Enterprise (formerly ART-IM)
-au BufNewFile,BufRead *.art setf art
-
-" AsciiDoc
-au BufNewFile,BufRead *.asciidoc,*.adoc setf asciidoc
-
-" ASN.1
-au BufNewFile,BufRead *.asn,*.asn1 setf asn
-
-" Active Server Pages (with Visual Basic Script)
-au BufNewFile,BufRead *.asa
- \ if exists("g:filetype_asa") |
- \ exe "setf " . g:filetype_asa |
- \ else |
- \ setf aspvbs |
- \ endif
-
-" Active Server Pages (with Perl or Visual Basic Script)
-au BufNewFile,BufRead *.asp
- \ if exists("g:filetype_asp") |
- \ exe "setf " . g:filetype_asp |
- \ elseif getline(1) . getline(2) . getline(3) =~? "perlscript" |
- \ setf aspperl |
- \ else |
- \ setf aspvbs |
- \ endif
-
-" 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()
-
-" Assembly - Macro (VAX)
-au BufNewFile,BufRead *.mar setf vmasm
-
-" Astro
-au BufNewFile,BufRead *.astro setf astro
-
-" Atlas
-au BufNewFile,BufRead *.atl,*.as setf atlas
-
-" Atom is based on XML
-au BufNewFile,BufRead *.atom setf xml
-
-" Autoit v3
-au BufNewFile,BufRead *.au3 setf autoit
-
-" Autohotkey
-au BufNewFile,BufRead *.ahk setf autohotkey
-
-" Automake
-au BufNewFile,BufRead [mM]akefile.am,GNUmakefile.am setf automake
-
-" Autotest .at files are actually m4
-au BufNewFile,BufRead *.at setf m4
-
-" Avenue
-au BufNewFile,BufRead *.ave setf ave
-
-" Awk
-au BufNewFile,BufRead *.awk,*.gawk setf awk
-
-" B
-au BufNewFile,BufRead *.mch,*.ref,*.imp setf b
-
-" BASIC or Visual 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
-
-" IBasic file (similar to QBasic)
-au BufNewFile,BufRead *.iba,*.ibi setf ibasic
-
-" FreeBasic file (similar to QBasic)
-au BufNewFile,BufRead *.fb setf freebasic
-
-" 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()
-
-" BC calculator
-au BufNewFile,BufRead *.bc setf bc
-
-" BDF font
-au BufNewFile,BufRead *.bdf setf bdf
-
-" Beancount
-au BufNewFile,BufRead *.beancount setf beancount
-
-" BibTeX bibliography database file
-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
-
-" BIND zone
-au BufNewFile,BufRead named.root setf bindzone
-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
-
-" BSDL
-au BufNewFile,BufRead *.bsd,*.bsdl setf bsdl
-
-" Bazel (http://bazel.io)
-autocmd BufRead,BufNewFile *.bzl,*.bazel,WORKSPACE setf bzl
-if has("fname_case")
- " There is another check for BUILD further below.
- autocmd BufRead,BufNewFile *.BUILD,BUILD setf bzl
-endif
-
-" C or lpc
-au BufNewFile,BufRead *.c call dist#ft#FTlpc()
-au BufNewFile,BufRead *.lpc,*.ulpc setf lpc
-
-" Calendar
-au BufNewFile,BufRead calendar setf calendar
-
-" C#
-au BufNewFile,BufRead *.cs,*.csx setf cs
-
-" CSDL
-au BufNewFile,BufRead *.csdl setf csdl
-
-" Cabal
-au BufNewFile,BufRead *.cabal setf cabal
-
-" Cdrdao TOC
-au BufNewFile,BufRead *.toc setf cdrtoc
-
-" Cdrdao config
-au BufNewFile,BufRead */etc/cdrdao.conf,*/etc/defaults/cdrdao,*/etc/default/cdrdao,.cdrdao setf cdrdaoconf
-
-" Cfengine
-au BufNewFile,BufRead cfengine.conf setf cfengine
-
-" ChaiScript
-au BufRead,BufNewFile *.chai setf chaiscript
-
-" Comshare Dimension Definition Language
-au BufNewFile,BufRead *.cdl setf cdl
-
-" Conary Recipe
-au BufNewFile,BufRead *.recipe setf conaryrecipe
-
-" Controllable Regex Mutilator
-au BufNewFile,BufRead *.crm setf crm
-
-" Cyn++
-au BufNewFile,BufRead *.cyn setf cynpp
-
-" Cynlib
-" .cc and .cpp files can be C++ or Cynlib.
-au BufNewFile,BufRead *.cc
- \ if exists("cynlib_syntax_for_cc")|setf cynlib|else|setf cpp|endif
-au BufNewFile,BufRead *.cpp
- \ if exists("cynlib_syntax_for_cpp")|setf cynlib|else|setf cpp|endif
-
-" C++
-au BufNewFile,BufRead *.cxx,*.c++,*.hh,*.hxx,*.hpp,*.ipp,*.moc,*.tcc,*.inl setf cpp
-if has("fname_case")
- au BufNewFile,BufRead *.C,*.H setf cpp
-endif
-
-" .h files can be C, Ch C++, ObjC or ObjC++.
-" Set c_syntax_for_h if you want C, ch_syntax_for_h if you want Ch. ObjC is
-" detected automatically.
-au BufNewFile,BufRead *.h call dist#ft#FTheader()
-
-" Ch (CHscript)
-au BufNewFile,BufRead *.chf setf ch
-
-" TLH files are C++ headers generated by Visual C++'s #import from typelibs
-au BufNewFile,BufRead *.tlh setf cpp
-
-" Cascading Style Sheets
-au BufNewFile,BufRead *.css setf css
-
-" Century Term Command Scripts (*.cmd too)
-au BufNewFile,BufRead *.con setf cterm
-
-" Changelog
-au BufNewFile,BufRead changelog.Debian,changelog.dch,NEWS.Debian,NEWS.dch,*/debian/changelog
- \ setf debchangelog
-
-au BufNewFile,BufRead [cC]hange[lL]og
- \ if getline(1) =~ '; urgency='
- \| setf debchangelog
- \| else
- \| setf changelog
- \| endif
-
-au BufNewFile,BufRead NEWS
- \ if getline(1) =~ '; urgency='
- \| setf debchangelog
- \| endif
-
-" CHILL
-au BufNewFile,BufRead *..ch setf chill
-
-" Changes for WEB and CWEB or CHILL
-au BufNewFile,BufRead *.ch call dist#ft#FTchange()
-
-" ChordPro
-au BufNewFile,BufRead *.chopro,*.crd,*.cho,*.crdpro,*.chordpro setf chordpro
-
-" Clean
-au BufNewFile,BufRead *.dcl,*.icl setf clean
-
-" Clever
-au BufNewFile,BufRead *.eni setf cl
-
-" Clever or dtd
-au BufNewFile,BufRead *.ent call dist#ft#FTent()
-
-" 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
-
-" Cmake
-au BufNewFile,BufRead CMakeLists.txt,*.cmake,*.cmake.in setf cmake
-
-" Cmusrc
-au BufNewFile,BufRead */.cmus/{autosave,rc,command-history,*.theme} setf cmusrc
-au BufNewFile,BufRead */cmus/{rc,*.theme} setf cmusrc
-
-" Cobol
-au BufNewFile,BufRead *.cbl,*.cob,*.lib setf cobol
-" cobol or zope form controller python script? (heuristic)
-au BufNewFile,BufRead *.cpy
- \ if getline(1) =~ '^##' |
- \ setf python |
- \ else |
- \ setf cobol |
- \ endif
-
-" Coco/R
-au BufNewFile,BufRead *.atg setf coco
-
-" Cold Fusion
-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,*.[dD]ockerfile setf dockerfile
-
-" WildPackets EtherPeek Decoder
-au BufNewFile,BufRead *.dcd setf dcd
-
-" Enlightenment configuration files
-au BufNewFile,BufRead *enlightenment/*.cfg setf c
-
-" Eterm
-au BufNewFile,BufRead *Eterm/*.cfg setf eterm
-
-" Elixir or Euphoria
-au BufNewFile,BufRead *.ex call dist#ft#ExCheck()
-
-" Elixir
-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")
- au BufNewFile,BufRead *.EU,*.EW,*.EX,*.EXU,*.EXW call dist#ft#EuphoriaCheck()
-endif
-
-" Lynx config files
-au BufNewFile,BufRead lynx.cfg setf lynx
-
-" Modula-3 configuration language (must be before *.cfg and *makefile)
-au BufNewFile,BufRead *.quake,cm3.cfg setf m3quake
-au BufNewFile,BufRead m3makefile,m3overrides setf m3build
-
-" Quake
-au BufNewFile,BufRead *baseq[2-3]/*.cfg,*id1/*.cfg setf quake
-au BufNewFile,BufRead *quake[1-3]/*.cfg setf quake
-
-" Quake C
-au BufNewFile,BufRead *.qc setf c
-
-" Configure files
-au BufNewFile,BufRead *.cfg\c call dist#ft#FTcfg()
-
-" Cucumber
-au BufNewFile,BufRead *.feature setf cucumber
-
-" Communicating Sequential Processes
-au BufNewFile,BufRead *.csp,*.fdr setf csp
-
-" CUPL logic description and simulation
-au BufNewFile,BufRead *.pld setf cupl
-au BufNewFile,BufRead *.si setf cuplsim
-
-" Dart
-au BufRead,BufNewfile *.dart,*.drt setf dart
-
-" Debian Control
-au BufNewFile,BufRead */debian/control setf debcontrol
-au BufNewFile,BufRead control
- \ if getline(1) =~ '^Source:'
- \| setf debcontrol
- \| endif
-
-" Debian Copyright
-au BufNewFile,BufRead */debian/copyright setf debcopyright
-au BufNewFile,BufRead copyright
- \ if getline(1) =~ '^Format:'
- \| setf debcopyright
- \| endif
-
-" Debian Sources.list
-au BufNewFile,BufRead */etc/apt/sources.list setf debsources
-au BufNewFile,BufRead */etc/apt/sources.list.d/*.list setf debsources
-
-" Deny hosts
-au BufNewFile,BufRead denyhosts.conf setf denyhosts
-
-" dnsmasq(8) configuration files
-au BufNewFile,BufRead */etc/dnsmasq.conf setf dnsmasq
-
-" ROCKLinux package description
-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
-au BufNewFile,BufRead *.desktop,*.directory setf desktop
-
-" Dict config
-au BufNewFile,BufRead dict.conf,.dictrc setf dictconf
-
-" Dictd config
-au BufNewFile,BufRead dictd*.conf setf dictdconf
-
-" DEP3 formatted patch files
-au BufNewFile,BufRead */debian/patches/* call dist#ft#Dep3patch()
-
-" Diff files
-au BufNewFile,BufRead *.diff,*.rej setf diff
-au BufNewFile,BufRead *.patch
- \ if getline(1) =~# '^From [0-9a-f]\{40,\} Mon Sep 17 00:00:00 2001$' |
- \ setf gitsendemail |
- \ else |
- \ setf diff |
- \ endif
-
-" Dircolors
-au BufNewFile,BufRead .dir_colors,.dircolors,*/etc/DIR_COLORS setf dircolors
-
-" Diva (with Skill) or InstallShield
-au BufNewFile,BufRead *.rul
- \ if getline(1).getline(2).getline(3).getline(4).getline(5).getline(6) =~? 'InstallShield' |
- \ setf ishd |
- \ else |
- \ setf diva |
- \ endif
-
-" DCL (Digital Command Language - vms) or DNS zone file
-au BufNewFile,BufRead *.com call dist#ft#BindzoneCheck('dcl')
-
-" DOT
-au BufNewFile,BufRead *.dot,*.gv setf dot
-
-" Dune
-au BufNewFile,BufRead jbuild,dune,dune-project,dune-workspace setf dune
-
-" Dylan - lid files
-au BufNewFile,BufRead *.lid setf dylanlid
-
-" Dylan - intr files (melange)
-au BufNewFile,BufRead *.intr setf dylanintr
-
-" Dylan
-au BufNewFile,BufRead *.dylan setf dylan
-
-" Microsoft Module Definition
-au BufNewFile,BufRead *.def setf def
-
-" Dracula
-au BufNewFile,BufRead *.drac,*.drc,*lvs,*lpe setf dracula
-
-" Datascript
-au BufNewFile,BufRead *.ds setf datascript
-
-" dsl: DSSSL or Structurizr
-au BufNewFile,BufRead *.dsl
- \ if getline(1) =~ '^\s*<\!' |
- \ setf dsl |
- \ else |
- \ setf structurizr |
- \ endif
-
-" DTD (Document Type Definition for XML)
-au BufNewFile,BufRead *.dtd setf dtd
-
-" DTS/DSTI (device tree files)
-au BufNewFile,BufRead *.dts,*.dtsi setf dts
-
-" EDIF (*.edf,*.edif,*.edn,*.edo) or edn
-au BufNewFile,BufRead *.ed\(f\|if\|o\) setf edif
-au BufNewFile,BufRead *.edn
- \ if getline(1) =~ '^\s*(\s*edif\>' |
- \ setf edif |
- \ else |
- \ setf clojure |
- \ endif
-
-" EditorConfig (close enough to dosini)
-au BufNewFile,BufRead .editorconfig setf dosini
-
-" Embedix Component Description
-au BufNewFile,BufRead *.ecd setf ecd
-
-" Eiffel or Specman or Euphoria
-au BufNewFile,BufRead *.e,*.E call dist#ft#FTe()
-
-" Elinks configuration
-au BufNewFile,BufRead elinks.conf setf elinks
-
-" ERicsson LANGuage; Yaws is erlang too
-au BufNewFile,BufRead *.erl,*.hrl,*.yaws setf erlang
-
-" Elm
-au BufNewFile,BufRead *.elm setf elm
-
-" Elm Filter Rules file
-au BufNewFile,BufRead filter-rules setf elmfilt
-
-" ESMTP rc file
-au BufNewFile,BufRead *esmtprc setf esmtprc
-
-" ESQL-C
-au BufNewFile,BufRead *.ec,*.EC setf esqlc
-
-" Esterel
-au BufNewFile,BufRead *.strl setf esterel
-
-" Essbase script
-au BufNewFile,BufRead *.csc setf csc
-
-" Exim
-au BufNewFile,BufRead exim.conf setf exim
-
-" Expect
-au BufNewFile,BufRead *.exp setf expect
-
-" Exports
-au BufNewFile,BufRead exports setf exports
-
-" Falcon
-au BufNewFile,BufRead *.fal setf falcon
-
-" Fantom
-au BufNewFile,BufRead *.fan,*.fwt setf fan
-
-" Factor
-au BufNewFile,BufRead *.factor setf factor
-
-" Fennel
-autocmd BufRead,BufNewFile *.fnl setf fennel
-
-" Fetchmail RC file
-au BufNewFile,BufRead .fetchmailrc setf fetchmail
-
-" Fish shell
-au BufNewFile,BufRead *.fish setf fish
-
-" FlexWiki - disabled, because it has side effects when a .wiki file
-" is not actually FlexWiki
-"au BufNewFile,BufRead *.wiki setf flexwiki
-
-" Focus Executable
-au BufNewFile,BufRead *.fex,*.focexec setf focexec
-
-" Focus Master file (but not for auto.master)
-au BufNewFile,BufRead auto.master setf conf
-au BufNewFile,BufRead *.mas,*.master setf master
-
-" Forth
-au BufNewFile,BufRead *.ft,*.fth setf forth
-
-" Reva Forth
-au BufNewFile,BufRead *.frt setf reva
-
-" Fortran
-if has("fname_case")
- au BufNewFile,BufRead *.F,*.FOR,*.FPP,*.FTN,*.F77,*.F90,*.F95,*.F03,*.F08 setf fortran
-endif
-au BufNewFile,BufRead *.f,*.for,*.fortran,*.fpp,*.ftn,*.f77,*.f90,*.f95,*.f03,*.f08 setf fortran
-
-" Framescript
-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()
-
-" F#
-au BufNewFile,BufRead *.fsi,*.fsx setf fsharp
-
-" GDB command files
-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
-
-" Gemtext
-au BufNewFile,BufRead *.gmi,*.gemini setf gemtext
-
-" Gift (Moodle)
-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 */.config/git/config setf gitconfig
-au BufNewFile,BufRead *.git/config.worktree setf gitconfig
-au BufNewFile,BufRead *.git/worktrees/*/config.worktree setf gitconfig
-au BufNewFile,BufRead .gitmodules,*.git/modules/*/config setf gitconfig
-if !empty($XDG_CONFIG_HOME)
- au BufNewFile,BufRead $XDG_CONFIG_HOME/git/config setf gitconfig
-endif
-au BufNewFile,BufRead git-rebase-todo setf gitrebase
-au BufRead,BufNewFile .gitsendemail.msg.?????? setf gitsendemail
-au BufNewFile,BufRead *.git/*
- \ if getline(1) =~# '^\x\{40,\}\>\|^ref: ' |
- \ setf git |
- \ endif
-
-" 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
-
-" GPG
-au BufNewFile,BufRead */.gnupg/options setf gpg
-au BufNewFile,BufRead */.gnupg/gpg.conf setf gpg
-au BufNewFile,BufRead */usr/*/gnupg/options.skel setf gpg
-if !empty($GNUPGHOME)
- au BufNewFile,BufRead $GNUPGHOME/options setf gpg
- au BufNewFile,BufRead $GNUPGHOME/gpg.conf setf gpg
-endif
-
-" gnash(1) configuration files
-au BufNewFile,BufRead gnashrc,.gnashrc,gnashpluginrc,.gnashpluginrc setf gnash
-
-" Gitolite
-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,.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
-
-" Groovy
-au BufNewFile,BufRead *.gradle,*.groovy setf groovy
-
-" GNU Server Pages
-au BufNewFile,BufRead *.gsp setf gsp
-
-" Group file
-au BufNewFile,BufRead */etc/group,*/etc/group-,*/etc/group.edit,*/etc/gshadow,*/etc/gshadow-,*/etc/gshadow.edit,*/var/backups/group.bak,*/var/backups/gshadow.bak setf group
-
-" 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
-au BufNewFile,BufRead *.chs setf chaskell
-au BufNewFile,BufRead cabal.project setf cabalproject
-au BufNewFile,BufRead $HOME/.cabal/config setf cabalconfig
-au BufNewFile,BufRead cabal.config setf cabalconfig
-
-" Haste
-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
-
-" HTML with M4
-au BufNewFile,BufRead *.html.m4 setf htmlm4
-
-" Some template. Used to be HTML Cheetah.
-au BufNewFile,BufRead *.tmpl setf template
-
-" Host config
-au BufNewFile,BufRead */etc/host.conf setf hostconf
-
-" Hosts access
-au BufNewFile,BufRead */etc/hosts.allow,*/etc/hosts.deny setf hostsaccess
-
-" Hyper Builder
-au BufNewFile,BufRead *.hb setf hb
-
-" Httest
-au BufNewFile,BufRead *.htt,*.htb setf httest
-
-" i3
-au BufNewFile,BufRead */i3/config setf i3config
-au BufNewFile,BufRead */.i3/config setf i3config
-
-" sway
-au BufNewFile,BufRead */sway/config setf swayconfig
-au BufNewFile,BufRead */.sway/config setf swayconfig
-
-" Icon
-au BufNewFile,BufRead *.icn setf icon
-
-" IDL (Interface Description Language)
-au BufNewFile,BufRead *.idl call dist#ft#FTidl()
-
-" Microsoft IDL (Interface Description Language) Also *.idl
-" MOF = WMI (Windows Management Instrumentation) Managed Object Format
-au BufNewFile,BufRead *.odl,*.mof setf msidl
-
-" Icewm menu
-au BufNewFile,BufRead */.icewm/menu setf icemenu
-
-" Indent profile (must come before IDL *.pro!)
-au BufNewFile,BufRead .indent.pro setf indent
-au BufNewFile,BufRead indent.pro call dist#ft#ProtoCheck('indent')
-
-" IDL (Interactive Data Language)
-au BufNewFile,BufRead *.pro call dist#ft#ProtoCheck('idlang')
-
-" Indent RC
-au BufNewFile,BufRead indentrc setf indent
-
-" Inform
-au BufNewFile,BufRead *.inf,*.INF setf inform
-
-" Initng
-au BufNewFile,BufRead */etc/initng/*/*.i,*.ii setf initng
-
-" Innovation Data Processing
-au BufRead,BufNewFile upstream.dat\c,upstream.*.dat\c,*.upstream.dat\c setf upstreamdat
-au BufRead,BufNewFile fdrupstream.log,upstream.log\c,upstream.*.log\c,*.upstream.log\c,UPSTREAM-*.log\c setf upstreamlog
-au BufRead,BufNewFile upstreaminstall.log\c,upstreaminstall.*.log\c,*.upstreaminstall.log\c setf upstreaminstalllog
-au BufRead,BufNewFile usserver.log\c,usserver.*.log\c,*.usserver.log\c setf usserverlog
-au BufRead,BufNewFile usw2kagt.log\c,usw2kagt.*.log\c,*.usw2kagt.log\c setf usw2kagtlog
-
-" Ipfilter
-au BufNewFile,BufRead ipf.conf,ipf6.conf,ipf.rules setf ipfilter
-
-" Informix 4GL (source - canonical, include file, I4GL+M4 preproc.)
-au BufNewFile,BufRead *.4gl,*.4gh,*.m4gl setf fgl
-
-" .INI file for MSDOS
-au BufNewFile,BufRead *.ini setf dosini
-
-" SysV Inittab
-au BufNewFile,BufRead inittab setf inittab
-
-" Inno Setup
-au BufNewFile,BufRead *.iss setf iss
-
-" J
-au BufNewFile,BufRead *.ijs setf j
-
-" JAL
-au BufNewFile,BufRead *.jal,*.JAL setf jal
-
-" Jam
-au BufNewFile,BufRead *.jpl,*.jpr setf jam
-
-" Java
-au BufNewFile,BufRead *.java,*.jav setf java
-
-" JavaCC
-au BufNewFile,BufRead *.jj,*.jjt setf javacc
-
-" JavaScript, ECMAScript, ES module script, CommonJS script
-au BufNewFile,BufRead *.js,*.javascript,*.es,*.mjs,*.cjs setf javascript
-
-" JavaScript with React
-au BufNewFile,BufRead *.jsx setf javascriptreact
-
-" Java Server Pages
-au BufNewFile,BufRead *.jsp setf jsp
-
-" Java Properties resource file (note: doesn't catch font.properties.pl)
-au BufNewFile,BufRead *.properties,*.properties_??,*.properties_??_?? setf jproperties
-
-" Jess
-au BufNewFile,BufRead *.clp setf jess
-
-" Jgraph
-au BufNewFile,BufRead *.jgr setf jgraph
-
-" Jovial
-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
-
-" Jupyter Notebook is also json
-au BufNewFile,BufRead *.ipynb setf json
-
-" Other files that look like json
-au BufNewFile,BufRead .babelrc,.eslintrc,.prettierrc,.firebaserc setf json
-
-" JSONC
-au BufNewFile,BufRead *.jsonc setf jsonc
-
-" Julia
-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
-
-" Kivy
-au BufNewFile,BufRead *.kv setf kivy
-
-" Kotlin
-au BufNewFile,BufRead *.kt,*.ktm,*.kts setf kotlin
-
-" KDE script
-au BufNewFile,BufRead *.ks setf kscript
-
-" Kconfig
-au BufNewFile,BufRead Kconfig,Kconfig.debug setf kconfig
-
-" Lace (ISE)
-au BufNewFile,BufRead *.ace,*.ACE setf lace
-
-" Latte
-au BufNewFile,BufRead *.latte,*.lte setf latte
-
-" Limits
-au BufNewFile,BufRead */etc/limits,*/etc/*limits.conf,*/etc/*limits.d/*.conf setf limits
-
-" 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
-
-" Ld loader
-au BufNewFile,BufRead *.ld setf ld
-
-" Ledger
-au BufRead,BufNewFile *.ldg,*.ledger,*.journal setf ledger
-
-" Less
-au BufNewFile,BufRead *.less setf less
-
-" Lex
-au BufNewFile,BufRead *.lex,*.l,*.lxx,*.l++ setf lex
-
-" Libao
-au BufNewFile,BufRead */etc/libao.conf,*/.libao setf libao
-
-" Libsensors
-au BufNewFile,BufRead */etc/sensors.conf,*/etc/sensors3.conf setf sensors
-
-" LFTP
-au BufNewFile,BufRead lftp.conf,.lftprc,*lftp/rc setf lftp
-
-" Lifelines (or Lex for C++!)
-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")
- au BufNewFile,BufRead *.lsp,*.lisp,*.asd,*.el,*.cl,*.L,.emacs,.sawfishrc setf lisp
-else
- au BufNewFile,BufRead *.lsp,*.lisp,*.asd,*.el,*.cl,.emacs,.sawfishrc setf lisp
-endif
-
-" SBCL implementation of Common Lisp
-au BufNewFile,BufRead sbclrc,.sbclrc setf lisp
-
-" Liquid
-au BufNewFile,BufRead *.liquid setf liquid
-
-" Lite
-au BufNewFile,BufRead *.lite,*.lt setf lite
-
-" LiteStep RC files
-au BufNewFile,BufRead */LiteStep/*/*.rc setf litestep
-
-" Login access
-au BufNewFile,BufRead */etc/login.access setf loginaccess
-
-" Login defs
-au BufNewFile,BufRead */etc/login.defs setf logindefs
-
-" Logtalk
-au BufNewFile,BufRead *.lgt setf logtalk
-
-" LOTOS
-au BufNewFile,BufRead *.lot,*.lotos setf lotos
-
-" Lout (also: *.lt)
-au BufNewFile,BufRead *.lou,*.lout setf lout
-
-" Lua
-au BufNewFile,BufRead *.lua setf lua
-
-" Luarocks
-au BufNewFile,BufRead *.rockspec setf lua
-
-" Linden Scripting Language (Second Life)
-au BufNewFile,BufRead *.lsl setf lsl
-
-" Lynx style file (or LotusScript!)
-au BufNewFile,BufRead *.lss setf lss
-
-" M4
-au BufNewFile,BufRead *.m4
- \ if expand("<afile>") !~? 'html.m4$\|fvwm2rc' | setf m4 | endif
-
-" MaGic Point
-au BufNewFile,BufRead *.mgp setf mgp
-
-" Mail (for Elm, trn, mutt, muttng, rn, slrn, neomutt)
-au BufNewFile,BufRead snd.\d\+,.letter,.letter.\d\+,.followup,.article,.article.\d\+,pico.\d\+,mutt{ng,}-*-\w\+,mutt[[:alnum:]_-]\\\{6\},neomutt-*-\w\+,neomutt[[:alnum:]_-]\\\{6\},ae\d\+.txt,/tmp/SLRN[0-9A-Z.]\+,*.eml setf mail
-
-" Mail aliases
-au BufNewFile,BufRead */etc/mail/aliases,*/etc/aliases setf mailaliases
-
-" Mailcap configuration file
-au BufNewFile,BufRead .mailcap,mailcap setf mailcap
-
-" Makefile
-au BufNewFile,BufRead *[mM]akefile,*.mk,*.mak,*.dsp setf make
-
-" MakeIndex
-au BufNewFile,BufRead *.ist,*.mst setf ist
-
-" Mallard
-au BufNewFile,BufRead *.page setf mallard
-
-" Manpage
-au BufNewFile,BufRead *.man setf nroff
-
-" Man config
-au BufNewFile,BufRead */etc/man.conf,man.config setf manconf
-
-" Maple V
-au BufNewFile,BufRead *.mv,*.mpl,*.mws setf maple
-
-" Map (UMN mapserver config file)
-au BufNewFile,BufRead *.map setf map
-
-" Markdown
-au BufNewFile,BufRead *.markdown,*.mdown,*.mkd,*.mkdn,*.mdwn,*.md setf markdown
-
-" Mason
-au BufNewFile,BufRead *.mason,*.mhtml,*.comp setf mason
-
-" Mathematica, Matlab, Murphi, Objective C or Octave
-au BufNewFile,BufRead *.m call dist#ft#FTm()
-
-" Mathematica notebook
-au BufNewFile,BufRead *.nb setf mma
-
-" Maya Extension Language
-au BufNewFile,BufRead *.mel setf mel
-
-" Mercurial (hg) commit file
-au BufNewFile,BufRead hg-editor-*.txt setf hgcommit
-
-" Mercurial config (looks like generic config file)
-au BufNewFile,BufRead *.hgrc,*hgrc setf cfg
-
-" Meson Build system config
-au BufNewFile,BufRead meson.build,meson_options.txt setf meson
-au BufNewFile,BufRead *.wrap setf dosini
-
-" Messages (logs mostly)
-au BufNewFile,BufRead */log/{auth,cron,daemon,debug,kern,lpr,mail,messages,news/news,syslog,user}{,.log,.err,.info,.warn,.crit,.notice}{,.[0-9]*,-[0-9]*} setf messages
-
-" Metafont
-au BufNewFile,BufRead *.mf setf mf
-
-" MetaPost
-au BufNewFile,BufRead *.mp setf mp
-au BufNewFile,BufRead *.mpxl,*.mpiv,*.mpvi let b:mp_metafun = 1 | setf mp
-
-" MGL
-au BufNewFile,BufRead *.mgl setf mgl
-
-" MIX - Knuth assembly
-au BufNewFile,BufRead *.mix,*.mixal setf mix
-
-" MMIX or VMS makefile
-au BufNewFile,BufRead *.mms call dist#ft#FTmms()
-
-" Symbian meta-makefile definition (MMP)
-au BufNewFile,BufRead *.mmp setf mmp
-
-" 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, 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
-
-" Monk
-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
-
-" MPD is based on XML
-au BufNewFile,BufRead *.mpd setf xml
-
-" Mplayer config
-au BufNewFile,BufRead mplayer.conf,*/.mplayer/config setf mplayerconf
-
-" Motorola S record
-au BufNewFile,BufRead *.s19,*.s28,*.s37,*.mot,*.srec setf srec
-
-" Mrxvtrc
-au BufNewFile,BufRead mrxvtrc,.mrxvtrc setf mrxvtrc
-
-" Msql
-au BufNewFile,BufRead *.msql setf msql
-
-" Mysql
-au BufNewFile,BufRead *.mysql setf mysql
-
-" Tcl Shell RC file
-au BufNewFile,BufRead tclsh.rc setf tcl
-
-" M$ Resource files
-" /etc/Muttrc.d/file.rc is muttrc
-au BufNewFile,BufRead *.rc,*.rch
- \ if expand("<afile>") !~ "/etc/Muttrc.d/" |
- \ setf rc |
- \ endif
-
-" MuPAD source
-au BufRead,BufNewFile *.mu setf mupad
-
-" Mush
-au BufNewFile,BufRead *.mush setf mush
-
-" Mutt setup file (also for Muttng)
-au BufNewFile,BufRead Mutt{ng,}rc setf muttrc
-
-" N1QL
-au BufRead,BufNewfile *.n1ql,*.nql setf n1ql
-
-" Nano
-au BufNewFile,BufRead */etc/nanorc,*.nanorc setf nanorc
-
-" Nastran input/DMAP
-"au BufNewFile,BufRead *.dat setf nastran
-
-" Natural
-au BufNewFile,BufRead *.NS[ACGLMNPS] setf natural
-
-" Noemutt setup file
-au BufNewFile,BufRead Neomuttrc setf neomuttrc
-
-" Netrc
-au BufNewFile,BufRead .netrc setf netrc
-
-" Nginx
-au BufNewFile,BufRead *.nginx,nginx*.conf,*nginx.conf,*/etc/nginx/*,*/usr/local/nginx/conf/*,*/nginx/*.conf setf nginx
-
-" 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
-
-" Novell netware batch files
-au BufNewFile,BufRead *.ncf setf ncf
-
-" Nroff/Troff (*.ms and *.t are checked below)
-au BufNewFile,BufRead *.me
- \ if expand("<afile>") != "read.me" && expand("<afile>") != "click.me" |
- \ setf nroff |
- \ endif
-au BufNewFile,BufRead *.tr,*.nr,*.roff,*.tmac,*.mom setf nroff
-au BufNewFile,BufRead *.[1-9] call dist#ft#FTnroff()
-
-" Nroff or Objective C++
-au BufNewFile,BufRead *.mm call dist#ft#FTmm()
-
-" Not Quite C
-au BufNewFile,BufRead *.nqc setf nqc
-
-" NSE - Nmap Script Engine - uses Lua syntax
-au BufNewFile,BufRead *.nse setf lua
-
-" NSIS
-au BufNewFile,BufRead *.nsi,*.nsh setf nsis
-
-" OCaml
-au BufNewFile,BufRead *.ml,*.mli,*.mll,*.mly,.ocamlinit,*.mlt,*.mlp,*.mlip,*.mli.cppo,*.ml.cppo setf ocaml
-
-" Occam
-au BufNewFile,BufRead *.occ setf occam
-
-" Octave
-au BufNewFile,BufRead octave.conf,.octaverc,octaverc setf octave
-
-" Omnimark
-au BufNewFile,BufRead *.xom,*.xin setf omnimark
-
-" OPAM
-au BufNewFile,BufRead opam,*.opam,*.opam.template setf opam
-
-" OpenFOAM
-au BufNewFile,BufRead [a-zA-Z0-9]*Dict\(.*\)\=,[a-zA-Z]*Properties\(.*\)\=,*Transport\(.*\),fvSchemes,fvSolution,fvConstrains,fvModels,*/constant/g,*/0\(\.orig\)\=/* call dist#ft#FTfoam()
-
-" OpenROAD
-au BufNewFile,BufRead *.or setf openroad
-
-" 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
-
-" Org
-au BufNewFile,BufRead *.org,*.org_archive setf org
-
-" Packet filter conf
-au BufNewFile,BufRead pf.conf setf pf
-
-" 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 conf |
- \ endif
-
-" Pam conf
-au BufNewFile,BufRead */etc/pam.conf setf pamconf
-
-" Pam environment
-au BufNewFile,BufRead pam_env.conf,.pam_environment setf pamenv
-
-" 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
-
-" Pascal or Puppet manifest
-au BufNewFile,BufRead *.pp call dist#ft#FTpp()
-
-" Delphi or Lazarus program file
-au BufNewFile,BufRead *.dpr,*.lpr setf pascal
-
-" Free Pascal makefile definition file
-au BufNewFile,BufRead *.fpc setf fpcmake
-
-" PDF
-au BufNewFile,BufRead *.pdf setf pdf
-
-" PCMK - HAE - crm configure edit
-au BufNewFile,BufRead *.pcmk setf pcmk
-
-" Perl
-if has("fname_case")
- au BufNewFile,BufRead *.pl,*.PL call dist#ft#FTpl()
-else
- au BufNewFile,BufRead *.pl call dist#ft#FTpl()
-endif
-au BufNewFile,BufRead *.plx,*.al,*.psgi setf perl
-
-" Perl, XPM or XPM2
-au BufNewFile,BufRead *.pm
- \ if getline(1) =~ "XPM2" |
- \ setf xpm2 |
- \ elseif getline(1) =~ "XPM" |
- \ setf xpm |
- \ else |
- \ setf perl |
- \ endif
-
-" Perl POD
-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.
-" 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
-
-" Pike and Cmod
-au BufNewFile,BufRead *.pike,*.pmod setf pike
-au BufNewFile,BufRead *.cmod setf cmod
-
-" Pinfo config
-au BufNewFile,BufRead */etc/pinforc,*/.pinforc setf pinfo
-
-" Palm Resource compiler
-au BufNewFile,BufRead *.rcp setf pilrc
-
-" Pine config
-au BufNewFile,BufRead .pinerc,pinerc,.pinercex,pinercex setf pine
-
-" Pipenv Pipfiles
-au BufNewFile,BufRead Pipfile setf toml
-au BufNewFile,BufRead Pipfile.lock setf json
-
-" PL/1, PL/I
-au BufNewFile,BufRead *.pli,*.pl1 setf pli
-
-" PL/M (also: *.inp)
-au BufNewFile,BufRead *.plm,*.p36,*.pac setf plm
-
-" PL/SQL
-au BufNewFile,BufRead *.pls,*.plsql setf plsql
-
-" PLP
-au BufNewFile,BufRead *.plp setf plp
-
-" PO and PO template (GNU gettext)
-au BufNewFile,BufRead *.po,*.pot setf po
-
-" Postfix main config
-au BufNewFile,BufRead main.cf setf pfmain
-
-" PostScript (+ font files, encapsulated PostScript, Adobe Illustrator)
-au BufNewFile,BufRead *.ps,*.pfa,*.afm,*.eps,*.epsf,*.epsi,*.ai setf postscr
-
-" PostScript Printer Description
-au BufNewFile,BufRead *.ppd setf ppd
-
-" Povray
-au BufNewFile,BufRead *.pov setf pov
-
-" Povray configuration
-au BufNewFile,BufRead .povrayrc setf povini
-
-" Povray, Pascal, PHP or assembly
-au BufNewFile,BufRead *.inc call dist#ft#FTinc()
-
-" PowerShell
-au BufNewFile,BufRead *.ps1,*.psd1,*.psm1,*.pssc setf ps1
-au BufNewFile,BufRead *.ps1xml setf ps1xml
-au BufNewFile,BufRead *.cdxml,*.psc1 setf xml
-
-" Printcap and Termcap
-au BufNewFile,BufRead *printcap
- \ let b:ptcap_type = "print" | setf ptcap
-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
-
-" PPWizard
-au BufNewFile,BufRead *.it,*.ih setf ppwiz
-
-" Pug
-au BufRead,BufNewFile *.pug setf pug
-
-" Puppet
-au BufNewFile,BufRead Puppetfile setf ruby
-
-" Embedded Puppet
-au BufNewFile,BufRead *.epp setf epuppet
-
-" Obj 3D file format
-" TODO: is there a way to avoid MS-Windows Object files?
-au BufNewFile,BufRead *.obj setf obj
-
-" Oracle Pro*C/C++
-au BufNewFile,BufRead *.pc setf proc
-
-" Privoxy actions file
-au BufNewFile,BufRead *.action setf privoxy
-
-" Procmail
-au BufNewFile,BufRead .procmail,.procmailrc setf procmail
-
-" Progress or CWEB
-au BufNewFile,BufRead *.w call dist#ft#FTprogress_cweb()
-
-" Progress or assembly
-au BufNewFile,BufRead *.i call dist#ft#FTprogress_asm()
-
-" Progress or Pascal
-au BufNewFile,BufRead *.p call dist#ft#FTprogress_pascal()
-
-" Software Distributor Product Specification File (POSIX 1387.2-1995)
-au BufNewFile,BufRead *.psf setf psf
-au BufNewFile,BufRead INDEX,INFO
- \ if getline(1) =~ '^\s*\(distribution\|installed_software\|root\|bundle\|product\)\s*$' |
- \ setf psf |
- \ endif
-
-" Prolog
-au BufNewFile,BufRead *.pdb setf prolog
-
-" Promela
-au BufNewFile,BufRead *.pml setf promela
-
-" Property Specification Language (PSL)
-au BufNewFile,BufRead *.psl setf psl
-
-" Google protocol buffers
-au BufNewFile,BufRead *.proto setf proto
-au BufNewFile,BufRead *.pbtxt setf pbtxt
-
-" Poke
-au BufNewFile,BufRead *.pk setf poke
-
-" Protocols
-au BufNewFile,BufRead */etc/protocols setf protocols
-
-" Pyret
-au BufNewFile,BufRead *.arr setf pyret
-
-" Pyrex
-au BufNewFile,BufRead *.pyx,*.pxd setf pyrex
-
-" Python, Python Shell Startup and Python Stub Files
-" Quixote (Python-based web framework)
-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
-
-" Quarto
-au BufRead,BufNewFile *.qmd setf quarto
-
-" Radiance
-au BufNewFile,BufRead *.rad,*.mat setf radiance
-
-" Raku (formerly Perl6)
-au BufNewFile,BufRead *.pm6,*.p6,*.t6,*.pod6,*.raku,*.rakumod,*.rakudoc,*.rakutest setf raku
-
-" Ratpoison config/command files
-au BufNewFile,BufRead .ratpoisonrc,ratpoisonrc setf ratpoison
-
-" RCS file
-au BufNewFile,BufRead *\,v setf rcs
-
-" Readline
-au BufNewFile,BufRead .inputrc,inputrc setf readline
-
-" Registry for MS-Windows
-au BufNewFile,BufRead *.reg
- \ if getline(1) =~? '^REGEDIT[0-9]*\s*$\|^Windows Registry Editor Version \d*\.\d*\s*$' | setf registry | endif
-
-" Renderman Interface Bytestream
-au BufNewFile,BufRead *.rib setf rib
-
-" Rego Policy Language
-au BufNewFile,BufRead *.rego setf rego
-
-" Rexx
-au BufNewFile,BufRead *.rex,*.orx,*.rxo,*.rxj,*.jrexx,*.rexxj,*.rexx,*.testGroup,*.testUnit setf rexx
-
-" R Help file
-if has("fname_case")
- au BufNewFile,BufRead *.rd,*.Rd setf rhelp
-else
- au BufNewFile,BufRead *.rd setf rhelp
-endif
-
-" R noweb file
-if has("fname_case")
- au BufNewFile,BufRead *.Rnw,*.rnw,*.Snw,*.snw setf rnoweb
-else
- au BufNewFile,BufRead *.rnw,*.snw setf rnoweb
-endif
-
-" R Markdown file
-if has("fname_case")
- au BufNewFile,BufRead *.Rmd,*.rmd,*.Smd,*.smd setf rmd
-else
- au BufNewFile,BufRead *.rmd,*.smd setf rmd
-endif
-
-" RSS looks like XML
-au BufNewFile,BufRead *.rss setf xml
-
-" R reStructuredText file
-if has("fname_case")
- au BufNewFile,BufRead *.Rrst,*.rrst,*.Srst,*.srst setf rrst
-else
- au BufNewFile,BufRead *.rrst,*.srst setf rrst
-endif
-
-" Rexx, Rebol or R
-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
-
-" Relax NG Compact
-au BufNewFile,BufRead *.rnc setf rnc
-
-" Relax NG XML
-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
-
-" Rpcgen
-au BufNewFile,BufRead *.x setf rpcgen
-
-" MikroTik RouterOS script
-au BufRead,BufNewFile *.rsc setf routeros
-
-" reStructuredText Documentation Format
-au BufNewFile,BufRead *.rst setf rst
-
-" RTF
-au BufNewFile,BufRead *.rtf setf rtf
-
-" Interactive Ruby shell
-au BufNewFile,BufRead .irbrc,irbrc setf ruby
-
-" Ruby
-au BufNewFile,BufRead *.rb,*.rbw setf ruby
-
-" RubyGems
-au BufNewFile,BufRead *.gemspec setf ruby
-
-" RBS (Ruby Signature)
-au BufNewFile,BufRead *.rbs setf rbs
-
-" Rackup
-au BufNewFile,BufRead *.ru setf ruby
-
-" Bundler
-au BufNewFile,BufRead Gemfile setf ruby
-
-" Ruby on Rails
-au BufNewFile,BufRead *.builder,*.rxml,*.rjs setf ruby
-
-" Rantfile and Rakefile is like Ruby
-au BufNewFile,BufRead [rR]antfile,*.rant,[rR]akefile,*.rake setf ruby
-
-" Rust
-au BufNewFile,BufRead *.rs setf rust
-au BufNewFile,BufRead Cargo.lock,*/.cargo/config,*/.cargo/credentials setf toml
-
-" S-lang (or shader language, or SmallLisp)
-au BufNewFile,BufRead *.sl setf slang
-
-" Samba config
-au BufNewFile,BufRead smb.conf setf samba
-
-" SAS script
-au BufNewFile,BufRead *.sas setf sas
-
-" Sass
-au BufNewFile,BufRead *.sass setf sass
-
-" Sather
-au BufNewFile,BufRead *.sa setf sather
-
-" 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
-
-
-" SCSS
-au BufNewFile,BufRead *.scss setf scss
-
-" SD: Streaming Descriptors
-au BufNewFile,BufRead *.sd setf sd
-
-" SDL
-au BufNewFile,BufRead *.sdl,*.pr setf sdl
-
-" sed
-au BufNewFile,BufRead *.sed setf sed
-
-" svelte
-au BufNewFile,BufRead *.svelte setf svelte
-
-" Sieve (RFC 3028, 5228)
-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 or
-" Maxima.
-au BufNewFile,BufRead *.mc call dist#ft#McSetf()
-
-" Services
-au BufNewFile,BufRead */etc/services setf services
-
-" Service Location config
-au BufNewFile,BufRead */etc/slp.conf setf slpconf
-
-" Service Location registration
-au BufNewFile,BufRead */etc/slp.reg setf slpreg
-
-" Service Location SPI
-au BufNewFile,BufRead */etc/slp.spi setf slpspi
-
-" Setserial config
-au BufNewFile,BufRead */etc/serial.conf setf setserial
-
-" SGML
-au BufNewFile,BufRead *.sgm,*.sgml
- \ if getline(1).getline(2).getline(3).getline(4).getline(5) =~? 'linuxdoc' |
- \ setf sgmllnx |
- \ elseif getline(1) =~ '<!DOCTYPE.*DocBook' || getline(2) =~ '<!DOCTYPE.*DocBook' |
- \ let b:docbk_type = "sgml" |
- \ let b:docbk_ver = 4 |
- \ setf docbk |
- \ else |
- \ setf sgml |
- \ endif
-
-" SGMLDECL
-au BufNewFile,BufRead *.decl,*.dcl,*.dec
- \ if getline(1).getline(2).getline(3) =~? '^<!SGML' |
- \ setf sgmldecl |
- \ endif
-
-" SGML catalog file
-au BufNewFile,BufRead catalog setf catalog
-
-" Shell scripts (sh, ksh, bash, bash2, csh); Allow .profile_foo etc.
-" Gentoo ebuilds, Arch Linux PKGBUILDs and Alpine Linux APKBUILDs are actually
-" bash scripts.
-" NOTE: Patterns ending in a star are further down, these have lower priority.
-au BufNewFile,BufRead .bashrc,bashrc,bash.bashrc,.bash[_-]profile,.bash[_-]logout,.bash[_-]aliases,bash-fc[-.],*.ebuild,*.bash,*.eclass,PKGBUILD,APKBUILD call dist#ft#SetFileTypeSH("bash")
-au BufNewFile,BufRead .kshrc,*.ksh call dist#ft#SetFileTypeSH("ksh")
-au BufNewFile,BufRead */etc/profile,.profile,*.sh,*.env call dist#ft#SetFileTypeSH(getline(1))
-
-" Shell script (Arch Linux) or PHP file (Drupal)
-au BufNewFile,BufRead *.install
- \ if getline(1) =~ '<?php' |
- \ setf php |
- \ else |
- \ call dist#ft#SetFileTypeSH("bash") |
- \ endif
-
-" tcsh scripts (patterns ending in a star further below)
-au BufNewFile,BufRead .tcshrc,*.tcsh,tcsh.tcshrc,tcsh.login call dist#ft#SetFileTypeShell("tcsh")
-
-" csh scripts, but might also be tcsh scripts (on some systems csh is tcsh)
-" (patterns ending in a start further below)
-au BufNewFile,BufRead .login,.cshrc,csh.cshrc,csh.login,csh.logout,*.csh,.alias call dist#ft#CSH()
-
-" Zig
-au BufNewFile,BufRead *.zig setf zig
-
-" Z-Shell script (patterns ending in a star further below)
-au BufNewFile,BufRead .zprofile,*/etc/zprofile,.zfbfmarks setf zsh
-au BufNewFile,BufRead .zshrc,.zshenv,.zlogin,.zlogout,.zcompdump setf zsh
-au BufNewFile,BufRead *.zsh setf zsh
-
-" Scheme
-au BufNewFile,BufRead *.scm,*.ss,*.sld,*.rkt,*.rktd,*.rktl setf scheme
-
-" Screen RC
-au BufNewFile,BufRead .screenrc,screenrc setf screen
-
-" Sexplib
-au BufNewFile,BufRead *.sexp setf sexplib
-
-" Simula
-au BufNewFile,BufRead *.sim setf simula
-
-" SINDA
-au BufNewFile,BufRead *.sin,*.s85 setf sinda
-
-" SiSU
-au BufNewFile,BufRead *.sst,*.ssm,*.ssi,*.-sst,*._sst setf sisu
-au BufNewFile,BufRead *.sst.meta,*.-sst.meta,*._sst.meta setf sisu
-
-" SKILL
-au BufNewFile,BufRead *.il,*.ils,*.cdf setf skill
-
-" SLRN
-au BufNewFile,BufRead .slrnrc setf slrnrc
-au BufNewFile,BufRead *.score setf slrnsc
-
-" Smalltalk
-au BufNewFile,BufRead *.st setf st
-
-" Smalltalk (and Rexx, TeX, and Visual Basic)
-au BufNewFile,BufRead *.cls call dist#ft#FTcls()
-
-" Smarty templates
-au BufNewFile,BufRead *.tpl setf smarty
-
-" SMIL or XML
-au BufNewFile,BufRead *.smil
- \ if getline(1) =~ '<?\s*xml.*?>' |
- \ setf xml |
- \ else |
- \ setf smil |
- \ endif
-
-" SMIL or SNMP MIB file
-au BufNewFile,BufRead *.smi
- \ if getline(1) =~ '\<smil\>' |
- \ setf smil |
- \ else |
- \ setf mib |
- \ endif
-
-" SMITH
-au BufNewFile,BufRead *.smt,*.smith setf smith
-
-" Snobol4 and spitbol
-au BufNewFile,BufRead *.sno,*.spt setf snobol4
-
-" SNMP MIB files
-au BufNewFile,BufRead *.mib,*.my setf mib
-
-" Snort Configuration
-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
-
-" Spec (Linux RPM)
-au BufNewFile,BufRead *.spec setf spec
-
-" Speedup (AspenTech plant simulator)
-au BufNewFile,BufRead *.speedup,*.spdata,*.spd setf spup
-
-" Slice
-au BufNewFile,BufRead *.ice setf slice
-
-" Microsoft Visual Studio Solution
-au BufNewFile,BufRead *.sln setf solution
-au BufNewFile,BufRead *.slnf setf json
-
-" Spice
-au BufNewFile,BufRead *.sp,*.spice setf spice
-
-" Spyce
-au BufNewFile,BufRead *.spy,*.spi setf spyce
-
-" Squid
-au BufNewFile,BufRead squid.conf setf squid
-
-" SQL for Oracle Designer
-au BufNewFile,BufRead *.tyb,*.typ,*.tyc,*.pkb,*.pks setf sql
-
-" SQL
-au BufNewFile,BufRead *.sql call dist#ft#SQL()
-
-" SQLJ
-au BufNewFile,BufRead *.sqlj setf sqlj
-
-" SQR
-au BufNewFile,BufRead *.sqr,*.sqi setf sqr
-
-" Squirrel
-au BufNewFile,BufRead *.nut setf squirrel
-
-" OpenSSH configuration
-au BufNewFile,BufRead ssh_config,*/.ssh/config,*/.ssh/*.conf setf sshconfig
-au BufNewFile,BufRead */etc/ssh/ssh_config.d/*.conf setf sshconfig
-
-" OpenSSH server configuration
-au BufNewFile,BufRead sshd_config setf sshdconfig
-au BufNewFile,BufRead */etc/ssh/sshd_config.d/*.conf setf sshdconfig
-
-" Stata
-au BufNewFile,BufRead *.ado,*.do,*.imata,*.mata setf stata
-" Also *.class, but not when it's a Java bytecode file
-au BufNewFile,BufRead *.class
- \ if getline(1) !~ "^\xca\xfe\xba\xbe" | setf stata | endif
-
-" SMCL
-au BufNewFile,BufRead *.hlp,*.ihlp,*.smcl setf smcl
-
-" Stored Procedures
-au BufNewFile,BufRead *.stp setf stp
-
-" Standard ML
-au BufNewFile,BufRead *.sml setf sml
-
-" Sratus VOS command macro
-au BufNewFile,BufRead *.cm setf voscm
-
-" Swift
-au BufNewFile,BufRead *.swift setf swift
-au BufNewFile,BufRead *.swift.gyb setf swiftgyb
-
-" Swift Intermediate Language
-au BufNewFile,BufRead *.sil setf sil
-
-" Sysctl
-au BufNewFile,BufRead */etc/sysctl.conf,*/etc/sysctl.d/*.conf setf sysctl
-
-" Systemd unit files
-au BufNewFile,BufRead */systemd/*.{automount,dnssd,link,mount,netdev,network,nspawn,path,service,slice,socket,swap,target,timer} setf systemd
-" Systemd overrides
-au BufNewFile,BufRead */etc/systemd/*.conf.d/*.conf setf systemd
-au BufNewFile,BufRead */etc/systemd/system/*.d/*.conf setf systemd
-au BufNewFile,BufRead */.config/systemd/user/*.d/*.conf setf systemd
-" Systemd temp files
-au BufNewFile,BufRead */etc/systemd/system/*.d/.#* setf systemd
-au BufNewFile,BufRead */etc/systemd/system/.#* setf systemd
-au BufNewFile,BufRead */.config/systemd/user/*.d/.#* setf systemd
-au BufNewFile,BufRead */.config/systemd/user/.#* setf systemd
-
-" Synopsys Design Constraints
-au BufNewFile,BufRead *.sdc setf sdc
-
-" Sudoers
-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
-
-" Tags
-au BufNewFile,BufRead tags setf tags
-
-" TAK
-au BufNewFile,BufRead *.tak setf tak
-
-" Task
-au BufRead,BufNewFile {pending,completed,undo}.data setf taskdata
-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
-
-" Telix Salt
-au BufNewFile,BufRead *.slt setf tsalt
-
-" Tera Term Language or Turtle
-au BufRead,BufNewFile *.ttl
- \ if getline(1) =~ '^@\?\(prefix\|base\)' |
- \ setf turtle |
- \ else |
- \ setf teraterm |
- \ endif
-
-" Terminfo
-au BufNewFile,BufRead *.ti setf terminfo
-
-" Terraform variables
-au BufRead,BufNewFile *.tfvars setf terraform-vars
-
-" TeX
-au BufNewFile,BufRead *.latex,*.sty,*.dtx,*.ltx,*.bbl setf tex
-au BufNewFile,BufRead *.tex call dist#ft#FTtex()
-
-" ConTeXt
-au BufNewFile,BufRead *.mkii,*.mkiv,*.mkvi,*.mkxl,*.mklx setf context
-
-" Texinfo
-au BufNewFile,BufRead *.texinfo,*.texi,*.txi setf texinfo
-
-" TeX configuration
-au BufNewFile,BufRead texmf.cnf setf texmf
-
-" Tidy config
-au BufNewFile,BufRead .tidyrc,tidyrc,tidy.conf setf tidy
-
-" TF mud client
-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
-
-" TOML
-au BufNewFile,BufRead *.toml setf toml
-
-" TPP - Text Presentation Program
-au BufNewFile,BufRead *.tpp setf tpp
-
-" Treetop
-au BufRead,BufNewFile *.treetop setf treetop
-
-" Trustees
-au BufNewFile,BufRead trustees.conf setf trustees
-
-" TSS - Geometry
-au BufNewFile,BufReadPost *.tssgm setf tssgm
-
-" TSS - Optics
-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
-
-" TWIG files
-au BufNewFile,BufReadPost *.twig setf twig
-
-" Typescript or Qt translation file (which is XML)
-au BufNewFile,BufReadPost *.ts
- \ if getline(1) =~ '<?xml' |
- \ setf xml |
- \ else |
- \ setf typescript |
- \ endif
-
-" TypeScript with React
-au BufNewFile,BufRead *.tsx setf typescriptreact
-
-" Motif UIT/UIL files
-au BufNewFile,BufRead *.uit,*.uil setf uil
-
-" Udev conf
-au BufNewFile,BufRead */etc/udev/udev.conf setf udevconf
-
-" Udev permissions
-au BufNewFile,BufRead */etc/udev/permissions.d/*.permissions setf udevperm
-"
-" Udev symlinks config
-au BufNewFile,BufRead */etc/udev/cdsymlinks.conf setf sh
-
-" UnrealScript
-au BufNewFile,BufRead *.uc setf uc
-
-" Updatedb
-au BufNewFile,BufRead */etc/updatedb.conf setf updatedb
-
-" Upstart (init(8)) config files
-au BufNewFile,BufRead */usr/share/upstart/*.conf setf upstart
-au BufNewFile,BufRead */usr/share/upstart/*.override setf upstart
-au BufNewFile,BufRead */etc/init/*.conf,*/etc/init/*.override setf upstart
-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
-
-" Verilog-AMS HDL
-au BufNewFile,BufRead *.va,*.vams setf verilogams
-
-" SystemVerilog
-au BufNewFile,BufRead *.sv,*.svh setf systemverilog
-
-" VHDL
-au BufNewFile,BufRead *.hdl,*.vhd,*.vhdl,*.vbe,*.vst,*.vho setf vhdl
-
-" Vim script
-au BufNewFile,BufRead *.vim,*.vba,.exrc,_exrc setf vim
-
-" Viminfo file
-au BufNewFile,BufRead .viminfo,_viminfo setf viminfo
-
-" Virata Config Script File or Drupal module
-au BufRead,BufNewFile *.hw,*.module,*.pkg
- \ if getline(1) =~ '<?php' |
- \ setf php |
- \ else |
- \ setf virata |
- \ endif
-
-" Visual Basic (also uses *.bas) or FORM
-au BufNewFile,BufRead *.frm call dist#ft#FTfrm()
-
-" SaxBasic is close to Visual Basic
-au BufNewFile,BufRead *.sba setf vb
-
-" Vgrindefs file
-au BufNewFile,BufRead vgrindefs setf vgrindefs
-
-" VRML V1.0c
-au BufNewFile,BufRead *.wrl setf vrml
-
-" Vroom (vim testing and executable documentation)
-au BufNewFile,BufRead *.vroom setf vroom
-
-" Vue.js Single File Component
-au BufNewFile,BufRead *.vue setf vue
-
-" WebAssembly
-au BufNewFile,BufRead *.wast,*.wat setf wast
-
-" Webmacro
-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
-
-" Winbatch
-au BufNewFile,BufRead *.wbt setf winbatch
-
-" WSML
-au BufNewFile,BufRead *.wsml setf wsml
-
-" WPL
-au BufNewFile,BufRead *.wpl setf xml
-
-" WvDial
-au BufNewFile,BufRead wvdial.conf,.wvdialrc setf wvdial
-
-" CVS RC file
-au BufNewFile,BufRead .cvsrc setf cvsrc
-
-" CVS commit file
-au BufNewFile,BufRead cvs\d\+ setf cvs
-
-" WEB (*.web is also used for Winbatch: Guess, based on expecting "%" comment
-" lines in a WEB file).
-au BufNewFile,BufRead *.web
- \ if getline(1)[0].getline(2)[0].getline(3)[0].getline(4)[0].getline(5)[0] =~ "%" |
- \ setf web |
- \ else |
- \ setf winbatch |
- \ endif
-
-" Windows Scripting Host and Windows Script Component
-au BufNewFile,BufRead *.ws[fc] setf wsh
-
-" XHTML
-au BufNewFile,BufRead *.xhtml,*.xht setf xhtml
-
-" X Pixmap (dynamically sets colors, this used to trigger on BufEnter to make
-" it work better, but that breaks setting 'filetype' manually)
-au BufNewFile,BufRead *.xpm
- \ if getline(1) =~ "XPM2" |
- \ setf xpm2 |
- \ else |
- \ setf xpm |
- \ endif
-au BufNewFile,BufRead *.xpm2 setf xpm2
-
-" XFree86 config
-au BufNewFile,BufRead XF86Config
- \ if getline(1) =~ '\<XConfigurator\>' |
- \ let b:xf86conf_xfree86_version = 3 |
- \ endif |
- \ setf xf86conf
-au BufNewFile,BufRead */xorg.conf.d/*.conf
- \ let b:xf86conf_xfree86_version = 4 |
- \ setf xf86conf
-
-" Xorg config
-au BufNewFile,BufRead xorg.conf,xorg.conf-4 let b:xf86conf_xfree86_version = 4 | setf xf86conf
-
-" Xinetd conf
-au BufNewFile,BufRead */etc/xinetd.conf setf xinetd
-
-" XS Perl extension interface language
-au BufNewFile,BufRead *.xs setf xs
-
-" X resources file
-au BufNewFile,BufRead .Xdefaults,.Xpdefaults,.Xresources,xdm-config,*.ad setf xdefaults
-
-" Xmath
-au BufNewFile,BufRead *.msc,*.msf setf xmath
-au BufNewFile,BufRead *.ms
- \ if !dist#ft#FTnroff() | setf xmath | endif
-
-" XML specific variants: docbk and xbl
-au BufNewFile,BufRead *.xml call dist#ft#FTxml()
-
-" XMI (holding UML models) is also XML
-au BufNewFile,BufRead *.xmi setf xml
-
-" CSPROJ files are Visual Studio.NET's XML-based C# project config files
-au BufNewFile,BufRead *.csproj,*.csproj.user setf xml
-
-" FSPROJ files are Visual Studio.NET's XML-based F# project config files
-au BufNewFile,BufRead *.fsproj,*.fsproj.user setf xml
-
-" VBPROJ files are Visual Studio.NET's XML-based Visual Basic project config files
-au BufNewFile,BufRead *.vbproj,*.vbproj.user setf xml
-
-" Qt Linguist translation source and Qt User Interface Files are XML
-" However, for .ts Typescript is more common.
-au BufNewFile,BufRead *.ui setf xml
-
-" TPM's are RDF-based descriptions of TeX packages (Nikolai Weibull)
-au BufNewFile,BufRead *.tpm setf xml
-
-" Xdg menus
-au BufNewFile,BufRead */etc/xdg/menus/*.menu setf xml
-
-" ATI graphics driver configuration
-au BufNewFile,BufRead fglrxrc setf xml
-
-" Web Services Description Language (WSDL)
-au BufNewFile,BufRead *.wsdl setf xml
-
-" XLIFF (XML Localisation Interchange File Format) is also XML
-au BufNewFile,BufRead *.xlf setf xml
-au BufNewFile,BufRead *.xliff setf xml
-
-" XML User Interface Language
-au BufNewFile,BufRead *.xul setf xml
-
-" X11 xmodmap (also see below)
-au BufNewFile,BufRead *Xmodmap setf xmodmap
-
-" Xquery
-au BufNewFile,BufRead *.xq,*.xql,*.xqm,*.xquery,*.xqy setf xquery
-
-" XSD
-au BufNewFile,BufRead *.xsd setf xsd
-
-" Xslt
-au BufNewFile,BufRead *.xsl,*.xslt setf xslt
-
-" Yacc
-au BufNewFile,BufRead *.yy,*.yxx,*.y++ setf yacc
-
-" Yacc or racc
-au BufNewFile,BufRead *.y call dist#ft#FTy()
-
-" Yaml
-au BufNewFile,BufRead *.yaml,*.yml setf yaml
-
-" Raml
-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
-au BufNewFile,BufRead *.zut setf zimbutempl
-
-" Zope
-" dtml (zope dynamic template markup language), pt (zope page template),
-" cpt (zope form controller page template)
-au BufNewFile,BufRead *.dtml,*.pt,*.cpt call dist#ft#FThtml()
-" zsql (zope sql method)
-au BufNewFile,BufRead *.zsql call dist#ft#SQL()
-
-" Z80 assembler asz80
-au BufNewFile,BufRead *.z8a setf z8a
-
-augroup END
-
-
-" Source the user-specified filetype file, for backwards compatibility with
-" Vim 5.x.
-if exists("myfiletypefile") && filereadable(expand(myfiletypefile))
- execute "source " . myfiletypefile
-endif
-
-
-" Check for "*" after loading myfiletypefile, so that scripts.vim is only used
-" when there are no matching file name extensions.
-" Don't do this for compressed files.
-augroup filetypedetect
-au BufNewFile,BufRead *
- \ if !did_filetype() && expand("<amatch>") !~ g:ft_ignore_pat
- \ | runtime! scripts.vim | endif
-au StdinReadPost * if !did_filetype() | runtime! scripts.vim | endif
-
-
-" Plain text files, needs to be far down to not override others. This avoids
-" the "conf" type being used if there is a line starting with '#'.
-" But before patterns matching everything in a directory.
-au BufNewFile,BufRead *.text,README,LICENSE,COPYING,AUTHORS setf text
-
-
-" Extra checks for when no filetype has been detected now. Mostly used for
-" patterns that end in "*". E.g., "zsh*" matches "zsh.vim", but that's a Vim
-" script file.
-" Most of these should call s:StarSetf() to avoid names ending in .gz and the
-" like are used.
-
-" More Apache style config files
-au BufNewFile,BufRead */etc/proftpd/*.conf*,*/etc/proftpd/conf.*/* call s:StarSetf('apachestyle')
-au BufNewFile,BufRead proftpd.conf* call s:StarSetf('apachestyle')
-
-" More Apache config files
-au BufNewFile,BufRead access.conf*,apache.conf*,apache2.conf*,httpd.conf*,srm.conf* call s:StarSetf('apache')
-au BufNewFile,BufRead */etc/apache2/*.conf*,*/etc/apache2/conf.*/*,*/etc/apache2/mods-*/*,*/etc/apache2/sites-*/*,*/etc/httpd/conf.*/*,*/etc/httpd/mods-*/*,*/etc/httpd/sites-*/*,*/etc/httpd/conf.d/*.conf* call s:StarSetf('apache')
-
-" APT config file
-au BufNewFile,BufRead */etc/apt/apt.conf.d/{[-_[:alnum:]]\+,[-_.[:alnum:]]\+.conf} call s:StarSetf('aptconf')
-
-" Asterisk config file
-au BufNewFile,BufRead *asterisk/*.conf* call s:StarSetf('asterisk')
-au BufNewFile,BufRead *asterisk*/*voicemail.conf* call s:StarSetf('asteriskvm')
-
-" Bazaar version control
-au BufNewFile,BufRead bzr_log.* setf bzr
-
-" Bazel build file
-if !has("fname_case")
- au BufNewFile,BufRead *.BUILD,BUILD setf bzl
-endif
-
-" BIND zone
-au BufNewFile,BufRead */named/db.*,*/bind/db.* call s:StarSetf('bindzone')
-
-au BufNewFile,BufRead cabal.project.* call s:StarSetf('cabalproject')
-
-" Calendar
-au BufNewFile,BufRead */.calendar/*,
- \*/share/calendar/*/calendar.*,*/share/calendar/calendar.*
- \ call s:StarSetf('calendar')
-
-" Changelog
-au BufNewFile,BufRead [cC]hange[lL]og*
- \ if getline(1) =~ '; urgency='
- \| call s:StarSetf('debchangelog')
- \|else
- \| call s:StarSetf('changelog')
- \|endif
-
-" Crontab
-au BufNewFile,BufRead crontab,crontab.*,*/etc/cron.d/* call s:StarSetf('crontab')
-
-" dnsmasq(8) configuration
-au BufNewFile,BufRead */etc/dnsmasq.d/* call s:StarSetf('dnsmasq')
-
-" Dockerfile
-au BufNewFile,BufRead Dockerfile.*,Containerfile.* call s:StarSetf('dockerfile')
-
-" Dracula
-au BufNewFile,BufRead drac.* call s:StarSetf('dracula')
-
-" Fvwm
-au BufNewFile,BufRead */.fvwm/* call s:StarSetf('fvwm')
-au BufNewFile,BufRead *fvwmrc*,*fvwm95*.hook
- \ let b:fvwm_version = 1 | call s:StarSetf('fvwm')
-au BufNewFile,BufRead *fvwm2rc*
- \ if expand("<afile>:e") == "m4"
- \| call s:StarSetf('fvwm2m4')
- \|else
- \| let b:fvwm_version = 2 | call s:StarSetf('fvwm')
- \|endif
-
-" Gedcom
-au BufNewFile,BufRead */tmp/lltmp* call s:StarSetf('gedcom')
-
-" Git
-au BufNewFile,BufRead */.gitconfig.d/*,*/etc/gitconfig.d/* call s:StarSetf('gitconfig')
-
-" Gitolite
-au BufNewFile,BufRead */gitolite-admin/conf/* call s:StarSetf('gitolite')
-
-" GTK RC
-au BufNewFile,BufRead .gtkrc*,gtkrc* call s:StarSetf('gtkrc')
-
-" Jam
-au BufNewFile,BufRead Prl*.*,JAM*.* call s:StarSetf('jam')
-
-" Jargon
-au! BufNewFile,BufRead *jarg*
- \ if getline(1).getline(2).getline(3).getline(4).getline(5) =~? 'THIS IS THE JARGON FILE'
- \| call s:StarSetf('jargon')
- \|endif
-
-" Java Properties resource file (note: doesn't catch font.properties.pl)
-au BufNewFile,BufRead *.properties_??_??_* call s:StarSetf('jproperties')
-
-" Kconfig
-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')
-
-" Makefile
-au BufNewFile,BufRead [mM]akefile* call s:StarSetf('make')
-
-" Ruby Makefile
-au BufNewFile,BufRead [rR]akefile* call s:StarSetf('ruby')
-
-" Mail (also matches muttrc.vim, so this is below the other checks)
-au BufNewFile,BufRead {neo,}mutt[[:alnum:]._-]\\\{6\} setf mail
-
-au BufNewFile,BufRead reportbug-* call s:StarSetf('mail')
-
-" Modconf
-au BufNewFile,BufRead */etc/modutils/*
- \ if executable(expand("<afile>")) != 1
- \| call s:StarSetf('modconf')
- \|endif
-au BufNewFile,BufRead */etc/modprobe.* call s:StarSetf('modconf')
-
-" Mutt setup files (must be before catch *.rc)
-au BufNewFile,BufRead */etc/Muttrc.d/* call s:StarSetf('muttrc')
-
-" Mutt setup file
-au BufNewFile,BufRead .mutt{ng,}rc*,*/.mutt{ng,}/mutt{ng,}rc* call s:StarSetf('muttrc')
-au BufNewFile,BufRead mutt{ng,}rc*,Mutt{ng,}rc* call s:StarSetf('muttrc')
-
-" Neomutt setup file
-au BufNewFile,BufRead .neomuttrc*,*/.neomutt/neomuttrc* call s:StarSetf('neomuttrc')
-au BufNewFile,BufRead neomuttrc*,Neomuttrc* call s:StarSetf('neomuttrc')
-
-" Nroff macros
-au BufNewFile,BufRead tmac.* call s:StarSetf('nroff')
-
-" OpenBSD hostname.if
-au BufNewFile,BufRead */etc/hostname.* call s:StarSetf('config')
-
-" Pam conf
-au BufNewFile,BufRead */etc/pam.d/* call s:StarSetf('pamconf')
-
-" Printcap and Termcap
-au BufNewFile,BufRead *printcap*
- \ if !did_filetype()
- \| let b:ptcap_type = "print" | call s:StarSetf('ptcap')
- \|endif
-au BufNewFile,BufRead *termcap*
- \ if !did_filetype()
- \| let b:ptcap_type = "term" | call s:StarSetf('ptcap')
- \|endif
-
-" ReDIF
-" Only used when the .rdf file was not detected to be XML.
-au BufRead,BufNewFile *.rdf call dist#ft#Redif()
-
-" Remind
-au BufNewFile,BufRead .reminders* call s:StarSetf('remind')
-
-" SGML catalog file
-au BufNewFile,BufRead sgml.catalog* call s:StarSetf('catalog')
-
-" avoid doc files being recognized a shell files
-au BufNewFile,BufRead */doc/{,.}bash[_-]completion{,.d,.sh}{,/*} setf text
-
-" Shell scripts ending in a star
-au BufNewFile,BufRead .bashrc*,.bash[_-]profile*,.bash[_-]logout*,.bash[_-]aliases*,bash-fc[-.]*,PKGBUILD*,APKBUILD*,*/{,.}bash[_-]completion{,.d,.sh}{,/*} call dist#ft#SetFileTypeSH("bash")
-au BufNewFile,BufRead .kshrc* call dist#ft#SetFileTypeSH("ksh")
-au BufNewFile,BufRead .profile* call dist#ft#SetFileTypeSH(getline(1))
-
-" Sudoers
-au BufNewFile,BufRead */etc/sudoers.d/* call s:StarSetf('sudoers')
-
-" tcsh scripts ending in a star
-au BufNewFile,BufRead .tcshrc* call dist#ft#SetFileTypeShell("tcsh")
-
-" csh scripts ending in a star
-au BufNewFile,BufRead .login*,.cshrc* call dist#ft#CSH()
-
-" tmux configuration with arbitrary extension
-au BufNewFile,BufRead {.,}tmux*.conf* setf tmux
-
-" VHDL
-au BufNewFile,BufRead *.vhdl_[0-9]* call s:StarSetf('vhdl')
-
-" Vim script
-au BufNewFile,BufRead *vimrc* call s:StarSetf('vim')
-
-" Subversion commit file
-au BufNewFile,BufRead svn-commit*.tmp setf svn
-
-" X resources file
-au BufNewFile,BufRead Xresources*,*/app-defaults/*,*/Xresources/* call s:StarSetf('xdefaults')
-
-" XFree86 config
-au BufNewFile,BufRead XF86Config-4*
- \ let b:xf86conf_xfree86_version = 4 | call s:StarSetf('xf86conf')
-au BufNewFile,BufRead XF86Config*
- \ if getline(1) =~ '\<XConfigurator\>'
- \| let b:xf86conf_xfree86_version = 3
- \|endif
- \|call s:StarSetf('xf86conf')
-
-" X11 xmodmap
-au BufNewFile,BufRead *xmodmap* call s:StarSetf('xmodmap')
-
-" Xinetd conf
-au BufNewFile,BufRead */etc/xinetd.d/* call s:StarSetf('xinetd')
-
-" yum conf (close enough to dosini)
-au BufNewFile,BufRead */etc/yum.repos.d/* call s:StarSetf('dosini')
-
-" Z-Shell script ending in a star
-au BufNewFile,BufRead .zsh*,.zlog*,.zcompdump* call s:StarSetf('zsh')
-au BufNewFile,BufRead zsh*,zlog* call s:StarSetf('zsh')
-
-
-" Help files match *.txt but should have a last line that is a modeline.
-au BufNewFile,BufRead *.txt
- \ if getline('$') !~ 'vim:.*ft=help'
- \| setf text
- \| endif
-
-if !exists('g:did_load_ftdetect')
- " Use the filetype detect plugins. They may overrule any of the previously
- " detected filetypes.
- runtime! ftdetect/*.vim
- runtime! ftdetect/*.lua
-endif
-
-" NOTE: The above command could have ended the filetypedetect autocmd group
-" and started another one. Let's make sure it has ended to get to a consistent
-" state.
-augroup END
-
-" Generic configuration file. Use FALLBACK, it's just guessing!
-au filetypedetect BufNewFile,BufRead,StdinReadPost *
- \ if !did_filetype() && expand("<amatch>") !~ g:ft_ignore_pat
- \ && (getline(1) =~ '^#' || getline(2) =~ '^#' || getline(3) =~ '^#'
- \ || getline(4) =~ '^#' || getline(5) =~ '^#') |
- \ setf FALLBACK conf |
- \ endif
-
-
-" If the GUI is already running, may still need to install the Syntax menu.
-" Don't do it when the 'M' flag is included in 'guioptions'.
-if has("menu") && has("gui_running")
- \ && !exists("did_install_syntax_menu") && &guioptions !~# "M"
- source <sfile>:p:h/menu.vim
-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)
- let output = ''
- for f in a:testlist
- try
- exe f
- catch
- let output = output . "\n" . f . ": " . v:exception
- endtry
- endfor
- return output
-endfunc
-
-" Restore 'cpoptions'
-let &cpo = s:cpo_save
-unlet s:cpo_save
diff --git a/runtime/ftplugin/abaqus.vim b/runtime/ftplugin/abaqus.vim
index 3faeff621a..5931cd921d 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 <costerwi@gmail.com>
-" Last Change: 2022 Aug 03
+" Last Change: 2022 Oct 08
" Only do this when not done yet for this buffer
if exists("b:did_ftplugin") | finish | endif
@@ -66,25 +66,44 @@ if exists("loaded_matchit") && !exists("b:match_words")
endif
if !exists("no_plugin_maps") && !exists("no_abaqus_maps")
- " Define keys used to move [count] keywords backward or forward.
- noremap <silent><buffer> [[ ?^\*\a<CR>:nohlsearch<CR>
- noremap <silent><buffer> ]] /^\*\a<CR>:nohlsearch<CR>
+ " Map [[ and ]] keys to move [count] keywords backward or forward
+ nnoremap <silent><buffer> ]] :call <SID>Abaqus_NextKeyword(1)<CR>
+ nnoremap <silent><buffer> [[ :call <SID>Abaqus_NextKeyword(-1)<CR>
+ function! <SID>Abaqus_NextKeyword(direction)
+ .mark '
+ if a:direction < 0
+ let flags = 'b'
+ else
+ let flags = ''
+ endif
+ let l:count = abs(a:direction) * v:count1
+ while l:count > 0 && search("^\\*\\a", flags)
+ let l:count -= 1
+ endwhile
+ endfunction
- " Define key to toggle commenting of the current line or range
+ " Map \\ to toggle commenting of the current line or range
noremap <silent><buffer> <LocalLeader><LocalLeader>
\ :call <SID>Abaqus_ToggleComment()<CR>j
function! <SID>Abaqus_ToggleComment() range
- if strpart(getline(a:firstline), 0, 2) == "**"
- " Un-comment all lines in range
- silent execute a:firstline . ',' . a:lastline . 's/^\*\*//'
- else
- " Comment all lines in range
- silent execute a:firstline . ',' . a:lastline . 's/^/**/'
- endif
+ if strpart(getline(a:firstline), 0, 2) == "**"
+ " Un-comment all lines in range
+ silent execute a:firstline . ',' . a:lastline . 's/^\*\*//'
+ else
+ " Comment all lines in range
+ silent execute a:firstline . ',' . a:lastline . 's/^/**/'
+ endif
+ endfunction
+
+ " Map \s to swap first two comma separated fields
+ noremap <silent><buffer> <LocalLeader>s :call <SID>Abaqus_Swap()<CR>
+ function! <SID>Abaqus_Swap() range
+ silent execute a:firstline . ',' . a:lastline . 's/\([^*,]*\),\([^,]*\)/\2,\1/'
endfunction
let b:undo_ftplugin .= "|unmap <buffer> [[|unmap <buffer> ]]"
\ . "|unmap <buffer> <LocalLeader><LocalLeader>"
+ \ . "|unmap <buffer> <LocalLeader>s"
endif
" Undo must be done in nocompatible mode for <LocalLeader>.
diff --git a/runtime/ftplugin/apache.vim b/runtime/ftplugin/apache.vim
new file mode 100644
index 0000000000..9f612f5447
--- /dev/null
+++ b/runtime/ftplugin/apache.vim
@@ -0,0 +1,16 @@
+" Vim filetype plugin
+" Language: apache configuration file
+" Maintainer: Per Juchtmans <dubgeiser+vimNOSPAM@gmail.com>
+" Last Change: 2022 Oct 22
+
+if exists("b:did_ftplugin")
+ finish
+endif
+let b:did_ftplugin = 1
+
+setlocal comments=:#
+setlocal commentstring=#\ %s
+
+let b:undo_ftplugin = "setlocal comments< commentstring<"
+
+" vim: nowrap sw=2 sts=2 ts=8 noet:
diff --git a/runtime/ftplugin/chatito.vim b/runtime/ftplugin/chatito.vim
new file mode 100644
index 0000000000..af212e9581
--- /dev/null
+++ b/runtime/ftplugin/chatito.vim
@@ -0,0 +1,15 @@
+" Vim filetype plugin
+" Language: Chatito
+" Maintainer: ObserverOfTime <chronobserver@disroot.org>
+" Last Change: 2022 Sep 19
+
+if exists('b:did_ftplugin')
+ finish
+endif
+let b:did_ftplugin = 1
+
+setlocal comments=:#,:// commentstring=#\ %s
+" indent of 4 spaces is mandated by the spec
+setlocal expandtab softtabstop=4 shiftwidth=4
+
+let b:undo_ftplugin = 'setl com< cms< et< sts< sw<'
diff --git a/runtime/ftplugin/checkhealth.vim b/runtime/ftplugin/checkhealth.vim
index 3d8e9ace1a..62a1970b4a 100644
--- a/runtime/ftplugin/checkhealth.vim
+++ b/runtime/ftplugin/checkhealth.vim
@@ -1,20 +1,18 @@
" Vim filetype plugin
-" Language: Neovim checkhealth buffer
-" Last Change: 2021 Dec 15
+" Language: Nvim :checkhealth buffer
+" Last Change: 2022 Nov 10
if exists("b:did_ftplugin")
finish
endif
-runtime! ftplugin/markdown.vim ftplugin/markdown_*.vim ftplugin/markdown/*.vim
+runtime! ftplugin/help.vim
setlocal wrap breakindent linebreak
-setlocal conceallevel=2 concealcursor=nc
-setlocal keywordprg=:help
let &l:iskeyword='!-~,^*,^|,^",192-255'
if exists("b:undo_ftplugin")
- let b:undo_ftplugin .= "|setl wrap< bri< lbr< cole< cocu< kp< isk<"
+ let b:undo_ftplugin .= "|setl wrap< bri< lbr< kp< isk<"
else
- let b:undo_ftplugin = "setl wrap< bri< lbr< cole< cocu< kp< isk<"
+ let b:undo_ftplugin = "setl wrap< bri< lbr< kp< isk<"
endif
diff --git a/runtime/ftplugin/crontab.vim b/runtime/ftplugin/crontab.vim
new file mode 100644
index 0000000000..8dac007ccc
--- /dev/null
+++ b/runtime/ftplugin/crontab.vim
@@ -0,0 +1,16 @@
+" Vim filetype plugin
+" Language: crontab
+" Maintainer: Keith Smiley <keithbsmiley@gmail.com>
+" Last Change: 2022 Sep 11
+
+" Only do this when not done yet for this buffer
+if exists("b:did_ftplugin")
+ finish
+endif
+
+" Don't load another plugin for this buffer
+let b:did_ftplugin = 1
+
+let b:undo_ftplugin = "setl commentstring<"
+
+setlocal commentstring=#\ %s
diff --git a/runtime/ftplugin/cs.vim b/runtime/ftplugin/cs.vim
index f53ffcbc8e..0734d11d22 100644
--- a/runtime/ftplugin/cs.vim
+++ b/runtime/ftplugin/cs.vim
@@ -2,7 +2,7 @@
" Language: C#
" Maintainer: Nick Jensen <nickspoon@gmail.com>
" Former Maintainer: Johannes Zellner <johannes@zellner.org>
-" Last Change: 2021-12-07
+" Last Change: 2022-11-16
" License: Vim (see :h license)
" Repository: https://github.com/nickspoons/vim-cs
@@ -25,8 +25,9 @@ let b:undo_ftplugin = 'setl com< fo<'
if exists('loaded_matchit') && !exists('b:match_words')
" #if/#endif support included by default
+ let b:match_ignorecase = 0
let b:match_words = '\%(^\s*\)\@<=#\s*region\>:\%(^\s*\)\@<=#\s*endregion\>,'
- let b:undo_ftplugin .= ' | unlet! b:match_words'
+ let b:undo_ftplugin .= ' | unlet! b:match_ignorecase b:match_words'
endif
if (has('gui_win32') || has('gui_gtk')) && !exists('b:browsefilter')
diff --git a/runtime/ftplugin/elixir.vim b/runtime/ftplugin/elixir.vim
index c423c2acb7..50f63673dc 100644
--- a/runtime/ftplugin/elixir.vim
+++ b/runtime/ftplugin/elixir.vim
@@ -1,7 +1,7 @@
" Elixir filetype plugin
" Language: Elixir
" Maintainer: Mitchell Hanberg <vimNOSPAM@mitchellhanberg.com>
-" Last Change: 2022 August 10
+" Last Change: 2022 Sep 20
if exists("b:did_ftplugin")
finish
@@ -23,7 +23,11 @@ if exists('loaded_matchit') && !exists('b:match_words')
\ ',{:},\[:\],(:)'
endif
+setlocal shiftwidth=2 softtabstop=2 expandtab iskeyword+=!,?
+setlocal comments=:#
setlocal commentstring=#\ %s
+let b:undo_ftplugin = 'setlocal sw< sts< et< isk< com< cms<'
+
let &cpo = s:save_cpo
unlet s:save_cpo
diff --git a/runtime/ftplugin/erlang.vim b/runtime/ftplugin/erlang.vim
index c775247f51..31fa0c3213 100644
--- a/runtime/ftplugin/erlang.vim
+++ b/runtime/ftplugin/erlang.vim
@@ -5,7 +5,7 @@
" Contributors: Ricardo Catalinas Jiménez <jimenezrick@gmail.com>
" Eduardo Lopez (http://github.com/tapichu)
" Arvid Bjurklint (http://github.com/slarwise)
-" Last Update: 2021-Jan-08
+" Last Update: 2021-Nov-22
" License: Vim license
" URL: https://github.com/vim-erlang/vim-erlang-runtime
@@ -30,6 +30,28 @@ setlocal commentstring=%%s
setlocal formatoptions+=ro
+if get(g:, 'erlang_extend_path', 1)
+ " typical erlang.mk paths
+ let &l:path = join([
+ \ 'deps/*/include',
+ \ 'deps/*/src',
+ \ 'deps/*/test',
+ \ 'deps/*/apps/*/include',
+ \ 'deps/*/apps/*/src',
+ \ &g:path], ',')
+ " typical rebar3 paths
+ let &l:path = join([
+ \ 'apps/*/include',
+ \ 'apps/*/src',
+ \ '_build/default/lib/*/src',
+ \ '_build/default/*/include',
+ \ &l:path], ',')
+ " typical erlang paths
+ let &l:path = join(['include', 'src', 'test', &l:path], ',')
+
+ set wildignore+=*/.erlang.mk/*,*.beam
+endif
+
setlocal suffixesadd=.erl,.hrl
let &l:include = '^\s*-\%(include\|include_lib\)\s*("\zs\f*\ze")'
diff --git a/runtime/ftplugin/gitattributes.vim b/runtime/ftplugin/gitattributes.vim
new file mode 100644
index 0000000000..2025d009d2
--- /dev/null
+++ b/runtime/ftplugin/gitattributes.vim
@@ -0,0 +1,13 @@
+" Vim filetype plugin
+" Language: git attributes
+" Maintainer: ObserverOfTime <chronobserver@disroot.org>
+" Last Change: 2022 Sep 08
+
+if exists('b:did_ftplugin')
+ finish
+endif
+let b:did_ftplugin = 1
+
+setl comments=:# commentstring=#\ %s
+
+let b:undo_ftplugin = 'setl com< cms<'
diff --git a/runtime/ftplugin/gitignore.vim b/runtime/ftplugin/gitignore.vim
new file mode 100644
index 0000000000..3502dd2717
--- /dev/null
+++ b/runtime/ftplugin/gitignore.vim
@@ -0,0 +1,13 @@
+" Vim filetype plugin
+" Language: git ignore
+" Maintainer: ObserverOfTime <chronobserver@disroot.org>
+" Last Change: 2022 Sep 10
+
+if exists('b:did_ftplugin')
+ finish
+endif
+let b:did_ftplugin = 1
+
+setl comments=:# commentstring=#\ %s
+
+let b:undo_ftplugin = 'setl com< cms<'
diff --git a/runtime/ftplugin/gyp.vim b/runtime/ftplugin/gyp.vim
new file mode 100644
index 0000000000..becfcadb6d
--- /dev/null
+++ b/runtime/ftplugin/gyp.vim
@@ -0,0 +1,14 @@
+" Vim filetype plugin
+" Language: GYP
+" Maintainer: ObserverOfTime <chronobserver@disroot.org>
+" Last Change: 2022 Sep 27
+
+if exists('b:did_ftplugin')
+ finish
+endif
+let b:did_ftplugin = 1
+
+setlocal formatoptions-=t
+setlocal commentstring=#\ %s comments=b:#,fb:-
+
+let b:undo_ftplugin = 'setlocal fo< cms< com<'
diff --git a/runtime/ftplugin/hare.vim b/runtime/ftplugin/hare.vim
new file mode 100644
index 0000000000..bb10daf38c
--- /dev/null
+++ b/runtime/ftplugin/hare.vim
@@ -0,0 +1,27 @@
+" Vim filetype plugin
+" Language: Hare
+" Maintainer: Amelia Clarke <me@rsaihe.dev>
+" Previous Maintainer: Drew DeVault <sir@cmpwn.com>
+" Last Updated: 2022-09-21
+
+" Only do this when not done yet for this buffer
+if exists('b:did_ftplugin')
+ finish
+endif
+
+" Don't load another plugin for this buffer
+let b:did_ftplugin = 1
+
+setlocal noexpandtab
+setlocal tabstop=8
+setlocal shiftwidth=0
+setlocal softtabstop=0
+setlocal textwidth=80
+setlocal commentstring=//\ %s
+
+" 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
+
+compiler hare
+" vim: tabstop=2 shiftwidth=2 expandtab
diff --git a/runtime/ftplugin/heex.vim b/runtime/ftplugin/heex.vim
new file mode 100644
index 0000000000..5274d59fbf
--- /dev/null
+++ b/runtime/ftplugin/heex.vim
@@ -0,0 +1,16 @@
+" Elixir filetype plugin
+" Language: HEEx
+" Maintainer: Mitchell Hanberg <vimNOSPAM@mitchellhanberg.com>
+" Last Change: 2022 Sep 21
+
+if exists("b:did_ftplugin")
+ finish
+endif
+let b:did_ftplugin = 1
+
+setlocal shiftwidth=2 softtabstop=2 expandtab
+
+setlocal comments=:<%!--
+setlocal commentstring=<%!--\ %s\ --%>
+
+let b:undo_ftplugin = 'set sw< sts< et< com< cms<'
diff --git a/runtime/ftplugin/jsonnet.vim b/runtime/ftplugin/jsonnet.vim
new file mode 100644
index 0000000000..1e621e1867
--- /dev/null
+++ b/runtime/ftplugin/jsonnet.vim
@@ -0,0 +1,17 @@
+" Vim filetype plugin
+" Language: Jsonnet
+" Maintainer: Cezary Drożak <cezary@drozak.net>
+" URL: https://github.com/google/vim-jsonnet
+" Last Change: 2022-09-08
+
+" Only do this when not done yet for this buffer
+if exists("b:did_ftplugin")
+ finish
+endif
+
+" Don't load another plugin for this buffer
+let b:did_ftplugin = 1
+
+setlocal commentstring=//\ %s
+
+let b:undo_ftplugin = "setlocal commentstring<"
diff --git a/runtime/ftplugin/lua.lua b/runtime/ftplugin/lua.lua
new file mode 100644
index 0000000000..415cf28f9a
--- /dev/null
+++ b/runtime/ftplugin/lua.lua
@@ -0,0 +1,3 @@
+if vim.g.ts_highlight_lua then
+ vim.treesitter.start()
+end
diff --git a/runtime/ftplugin/lua.vim b/runtime/ftplugin/lua.vim
index 2604257594..88b1fc9d44 100644
--- a/runtime/ftplugin/lua.vim
+++ b/runtime/ftplugin/lua.vim
@@ -1,46 +1,49 @@
" Vim filetype plugin file.
-" Language: Lua
+" Language: Lua
" Maintainer: Doug Kearns <dougkearns@gmail.com>
" Previous Maintainer: Max Ischenko <mfi@ukr.net>
-" Last Change: 2021 Nov 15
+" Contributor: Dorai Sitaram <ds26@gte.com>
+" C.D. MacEachern <craig.daniel.maceachern@gmail.com>
+" Last Change: 2022 Nov 19
-" Only do this when not done yet for this buffer
if exists("b:did_ftplugin")
finish
endif
-
-" Don't load another plugin for this buffer
let b:did_ftplugin = 1
let s:cpo_save = &cpo
set cpo&vim
-" Set 'formatoptions' to break comment lines but not other lines, and insert
-" the comment leader when hitting <CR> or using "o".
+setlocal comments=:--
+setlocal commentstring=--\ %s
setlocal formatoptions-=t formatoptions+=croql
-setlocal comments=:--
-setlocal commentstring=--%s
+let &l:define = '\<function\|\<local\%(\s\+function\)\='
+
+" TODO: handle init.lua
+setlocal includeexpr=tr(v:fname,'.','/')
setlocal suffixesadd=.lua
-let b:undo_ftplugin = "setlocal fo< com< cms< sua<"
+let b:undo_ftplugin = "setlocal cms< com< def< fo< inex< sua<"
if exists("loaded_matchit") && !exists("b:match_words")
let b:match_ignorecase = 0
let b:match_words =
- \ '\<\%(do\|function\|if\)\>:' .
- \ '\<\%(return\|else\|elseif\)\>:' .
- \ '\<end\>,' .
- \ '\<repeat\>:\<until\>,' .
- \ '\%(--\)\=\[\(=*\)\[:]\1]'
- let b:undo_ftplugin .= " | unlet! b:match_words b:match_ignorecase"
+ \ '\<\%(do\|function\|if\)\>:' ..
+ \ '\<\%(return\|else\|elseif\)\>:' ..
+ \ '\<end\>,' ..
+ \ '\<repeat\>:\<until\>,' ..
+ \ '\%(--\)\=\[\(=*\)\[:]\1]'
+ let b:undo_ftplugin ..= " | unlet! b:match_words b:match_ignorecase"
endif
if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter")
- let b:browsefilter = "Lua Source Files (*.lua)\t*.lua\n" .
- \ "All Files (*.*)\t*.*\n"
- let b:undo_ftplugin .= " | unlet! b:browsefilter"
+ let b:browsefilter = "Lua Source Files (*.lua)\t*.lua\n" ..
+ \ "All Files (*.*)\t*.*\n"
+ let b:undo_ftplugin ..= " | unlet! b:browsefilter"
endif
let &cpo = s:cpo_save
unlet s:cpo_save
+
+" vim: nowrap sw=2 sts=2 ts=8 noet:
diff --git a/runtime/ftplugin/lynx.vim b/runtime/ftplugin/lynx.vim
new file mode 100644
index 0000000000..b76c69f0ae
--- /dev/null
+++ b/runtime/ftplugin/lynx.vim
@@ -0,0 +1,29 @@
+" Vim filetype plugin file
+" Language: Lynx Web Browser Configuration
+" Maintainer: Doug Kearns <dougkearns@gmail.com>
+" Last Change: 2022 Sep 09
+
+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 cms< com< fo<"
+
+if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter")
+ let b:browsefilter = "Lynx Configuration Files (lynx.cfg .lynxrc)\tlynx.cfg;.lynxrc\n" ..
+ \ "All Files (*.*)\t*.*\n"
+ let b:undo_ftplugin ..= " | unlet! b:browsefilter"
+endif
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
+
+" vim: nowrap sw=2 sts=2 ts=8 noet:
diff --git a/runtime/ftplugin/man.vim b/runtime/ftplugin/man.vim
index d7a08a9941..277ce3c0b3 100644
--- a/runtime/ftplugin/man.vim
+++ b/runtime/ftplugin/man.vim
@@ -17,12 +17,12 @@ setlocal iskeyword=@-@,:,a-z,A-Z,48-57,_,.,-,(,)
setlocal nonumber norelativenumber
setlocal foldcolumn=0 colorcolumn=0 nolist nofoldenable
-setlocal tagfunc=man#goto_tag
+setlocal tagfunc=v:lua.require'man'.goto_tag
if !exists('g:no_plugin_maps') && !exists('g:no_man_maps')
nnoremap <silent> <buffer> j gj
nnoremap <silent> <buffer> k gk
- nnoremap <silent> <buffer> gO :call man#show_toc()<CR>
+ nnoremap <silent> <buffer> gO :lua require'man'.show_toc()<CR>
nnoremap <silent> <buffer> <2-LeftMouse> :Man<CR>
if get(b:, 'pager')
nnoremap <silent> <buffer> <nowait> q :lclose<CR><C-W>q
diff --git a/runtime/ftplugin/markdown.vim b/runtime/ftplugin/markdown.vim
index fc1d9e068b..2b963f139d 100644
--- a/runtime/ftplugin/markdown.vim
+++ b/runtime/ftplugin/markdown.vim
@@ -1,7 +1,7 @@
" Vim filetype plugin
-" Language: Markdown
-" Maintainer: Tim Pope <vimNOSPAM@tpope.org>
-" Last Change: 2019 Dec 05
+" Language: Markdown
+" Maintainer: Tim Pope <https://github.com/tpope/vim-markdown>
+" Last Change: 2022 Oct 13
if exists("b:did_ftplugin")
finish
@@ -9,18 +9,33 @@ endif
runtime! ftplugin/html.vim ftplugin/html_*.vim ftplugin/html/*.vim
+let s:keepcpo= &cpo
+set cpo&vim
+
setlocal comments=fb:*,fb:-,fb:+,n:> commentstring=<!--%s-->
setlocal formatoptions+=tcqln formatoptions-=r formatoptions-=o
-setlocal formatlistpat=^\\s*\\d\\+\\.\\s\\+\\\|^[-*+]\\s\\+\\\|^\\[^\\ze[^\\]]\\+\\]:
+setlocal formatlistpat=^\\s*\\d\\+\\.\\s\\+\\\|^\\s*[-*+]\\s\\+\\\|^\\[^\\ze[^\\]]\\+\\]:\\&^.\\{4\\}
if exists('b:undo_ftplugin')
- let b:undo_ftplugin .= "|setl cms< com< fo< flp<"
+ let b:undo_ftplugin .= "|setl cms< com< fo< flp< et< ts< sts< sw<"
else
- let b:undo_ftplugin = "setl cms< com< fo< flp<"
+ let b:undo_ftplugin = "setl cms< com< fo< flp< et< ts< sts< sw<"
+endif
+
+if get(g:, 'markdown_recommended_style', 1)
+ setlocal expandtab tabstop=4 softtabstop=4 shiftwidth=4
+endif
+
+if !exists("g:no_plugin_maps") && !exists("g:no_markdown_maps")
+ nnoremap <silent><buffer> [[ :<C-U>call search('\%(^#\{1,5\}\s\+\S\\|^\S.*\n^[=-]\+$\)', "bsW")<CR>
+ nnoremap <silent><buffer> ]] :<C-U>call search('\%(^#\{1,5\}\s\+\S\\|^\S.*\n^[=-]\+$\)', "sW")<CR>
+ xnoremap <silent><buffer> [[ :<C-U>exe "normal! gv"<Bar>call search('\%(^#\{1,5\}\s\+\S\\|^\S.*\n^[=-]\+$\)', "bsW")<CR>
+ xnoremap <silent><buffer> ]] :<C-U>exe "normal! gv"<Bar>call search('\%(^#\{1,5\}\s\+\S\\|^\S.*\n^[=-]\+$\)', "sW")<CR>
+ let b:undo_ftplugin .= '|sil! nunmap <buffer> [[|sil! nunmap <buffer> ]]|sil! xunmap <buffer> [[|sil! xunmap <buffer> ]]'
endif
function! s:NotCodeBlock(lnum) abort
- return synIDattr(synID(v:lnum, 1, 1), 'name') !=# 'markdownCode'
+ return synIDattr(synID(a:lnum, 1, 1), 'name') !=# 'markdownCode'
endfunction
function! MarkdownFold() abort
@@ -64,11 +79,14 @@ function! MarkdownFoldText() abort
return hash_indent.' '.title.' '.linecount
endfunction
-if has("folding") && exists("g:markdown_folding")
+if has("folding") && get(g:, "markdown_folding", 0)
setlocal foldexpr=MarkdownFold()
setlocal foldmethod=expr
setlocal foldtext=MarkdownFoldText()
- let b:undo_ftplugin .= " foldexpr< foldmethod< foldtext<"
+ let b:undo_ftplugin .= "|setl foldexpr< foldmethod< foldtext<"
endif
+let &cpo = s:keepcpo
+unlet s:keepcpo
+
" vim:set sw=2:
diff --git a/runtime/ftplugin/mermaid.vim b/runtime/ftplugin/mermaid.vim
new file mode 100644
index 0000000000..fe84ab37cf
--- /dev/null
+++ b/runtime/ftplugin/mermaid.vim
@@ -0,0 +1,49 @@
+" Vim filetype plugin
+" Language: Mermaid
+" Maintainer: Craig MacEachern <https://github.com/craigmac/vim-mermaid>
+" Last Change: 2022 Oct 13
+
+if exists("b:did_ftplugin")
+ finish
+endif
+
+let s:keepcpo= &cpo
+set cpo&vim
+
+" Use mermaid live editor's style
+setlocal expandtab
+setlocal shiftwidth=2
+setlocal softtabstop=-1
+setlocal tabstop=4
+
+" TODO: comments, formatlist stuff, based on what?
+setlocal comments=b:#,fb:-
+setlocal commentstring=#\ %s
+setlocal formatoptions+=tcqln formatoptions-=r formatoptions-=o
+setlocal formatlistpat=^\\s*\\d\\+\\.\\s\\+\\\|^\\s*[-*+]\\s\\+\\\|^\\[^\\ze[^\\]]\\+\\]:\\&^.\\{4\\}
+
+if exists('b:undo_ftplugin')
+ let b:undo_ftplugin .= "|setl cms< com< fo< flp< et< ts< sts< sw<"
+else
+ let b:undo_ftplugin = "setl cms< com< fo< flp< et< ts< sts< sw<"
+endif
+
+if !exists("g:no_plugin_maps") && !exists("g:no_markdown_maps")
+ nnoremap <silent><buffer> [[ :<C-U>call search('\%(^#\{1,5\}\s\+\S\\|^\S.*\n^[=-]\+$\)', "bsW")<CR>
+ nnoremap <silent><buffer> ]] :<C-U>call search('\%(^#\{1,5\}\s\+\S\\|^\S.*\n^[=-]\+$\)', "sW")<CR>
+ xnoremap <silent><buffer> [[ :<C-U>exe "normal! gv"<Bar>call search('\%(^#\{1,5\}\s\+\S\\|^\S.*\n^[=-]\+$\)', "bsW")<CR>
+ xnoremap <silent><buffer> ]] :<C-U>exe "normal! gv"<Bar>call search('\%(^#\{1,5\}\s\+\S\\|^\S.*\n^[=-]\+$\)', "sW")<CR>
+ let b:undo_ftplugin .= '|sil! nunmap <buffer> [[|sil! nunmap <buffer> ]]|sil! xunmap <buffer> [[|sil! xunmap <buffer> ]]'
+endif
+
+" if has("folding") && get(g:, "markdown_folding", 0)
+" setlocal foldexpr=MarkdownFold()
+" setlocal foldmethod=expr
+" setlocal foldtext=MarkdownFoldText()
+" let b:undo_ftplugin .= "|setl foldexpr< foldmethod< foldtext<"
+" endif
+
+let &cpo = s:keepcpo
+unlet s:keepcpo
+
+" vim:set sw=2:
diff --git a/runtime/ftplugin/obse.vim b/runtime/ftplugin/obse.vim
new file mode 100644
index 0000000000..6d865f05ee
--- /dev/null
+++ b/runtime/ftplugin/obse.vim
@@ -0,0 +1,70 @@
+" Vim filetype plugin file
+" Language: Oblivion Language (obl)
+" Original Creator: Kat <katisntgood@gmail.com>
+" Maintainer: Kat <katisntgood@gmail.com>
+" Created: August 08, 2021
+" Last Change: 13 November 2022
+
+if exists("b:did_ftplugin")
+ finish
+endif
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+let b:undo_ftplugin = "setl com< cms<"
+
+noremap <script> <buffer> <silent> [[ <nop>
+noremap <script> <buffer> <silent> ]] <nop>
+
+noremap <script> <buffer> <silent> [] <nop>
+noremap <script> <buffer> <silent> ][ <nop>
+
+setlocal commentstring=;%s
+setlocal comments=:;
+
+function s:NextSection(type, backwards, visual)
+ if a:visual
+ normal! gv
+ endif
+
+ if a:type == 1
+ let pattern = '\v(\n\n^\S|%^)'
+ let flags = 'e'
+ elseif a:type == 2
+ let pattern = '\v^\S.*'
+ let flags = ''
+ endif
+
+ if a:backwards
+ let dir = '?'
+ else
+ let dir = '/'
+ endif
+
+ execute 'silent normal! ' . dir . pattern . dir . flags . "\r"
+endfunction
+
+noremap <script> <buffer> <silent> ]]
+ \ :call <SID>NextSection(1, 0, 0)<cr>
+
+noremap <script> <buffer> <silent> [[
+ \ :call <SID>NextSection(1, 1, 0)<cr>
+
+noremap <script> <buffer> <silent> ][
+ \ :call <SID>NextSection(2, 0, 0)<cr>
+
+noremap <script> <buffer> <silent> []
+ \ :call <SID>NextSection(2, 1, 0)<cr>
+
+vnoremap <script> <buffer> <silent> ]]
+ \ :<c-u>call <SID>NextSection(1, 0, 1)<cr>
+vnoremap <script> <buffer> <silent> [[
+ \ :<c-u>call <SID>NextSection(1, 1, 1)<cr>
+vnoremap <script> <buffer> <silent> ][
+ \ :<c-u>call <SID>NextSection(2, 0, 1)<cr>
+vnoremap <script> <buffer> <silent> []
+ \ :<c-u>call <SID>NextSection(2, 1, 1)<cr>
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
diff --git a/runtime/ftplugin/openvpn.vim b/runtime/ftplugin/openvpn.vim
new file mode 100644
index 0000000000..56c0f25b39
--- /dev/null
+++ b/runtime/ftplugin/openvpn.vim
@@ -0,0 +1,14 @@
+" Vim filetype plugin
+" Language: OpenVPN
+" Maintainer: ObserverOfTime <chronobserver@disroot.org>
+" Last Change: 2022 Oct 16
+
+if exists('b:did_ftplugin')
+ finish
+endif
+let b:did_ftplugin = 1
+
+setlocal iskeyword+=-,.,/
+setlocal comments=:#,:; commentstring=#%s
+
+let b:undo_ftplugin = 'setl isk< com< cms<'
diff --git a/runtime/ftplugin/poefilter.vim b/runtime/ftplugin/poefilter.vim
new file mode 100644
index 0000000000..92c2def630
--- /dev/null
+++ b/runtime/ftplugin/poefilter.vim
@@ -0,0 +1,13 @@
+" Vim filetype plugin
+" Language: PoE item filter
+" Maintainer: ObserverOfTime <chronobserver@disroot.org>
+" Last Change: 2022 Oct 07
+
+if exists('b:did_ftplugin')
+ finish
+endif
+let b:did_ftplugin = 1
+
+setlocal comments=:# commentstring=#\ %s
+
+let b:undo_ftplugin = 'setl com< cms<'
diff --git a/runtime/ftplugin/racket.vim b/runtime/ftplugin/racket.vim
new file mode 100644
index 0000000000..3aa413397e
--- /dev/null
+++ b/runtime/ftplugin/racket.vim
@@ -0,0 +1,82 @@
+" Vim filetype plugin
+" Language: Racket
+" Maintainer: D. Ben Knoble <ben.knoble+github@gmail.com>
+" Previous Maintainer: Will Langstroth <will@langstroth.com>
+" URL: https://github.com/benknoble/vim-racket
+" Last Change: 2022 Aug 29
+
+if exists("b:did_ftplugin")
+ finish
+endif
+let b:did_ftplugin = 1
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+" quick hack to allow adding values
+setlocal iskeyword=@,!,#-',*-:,<-Z,a-z,~,_,94
+
+" Enable auto begin new comment line when continuing from an old comment line
+setlocal comments=:;;;;,:;;;,:;;,:;
+setlocal formatoptions+=r
+
+"setlocal commentstring=;;%s
+setlocal commentstring=#\|\ %s\ \|#
+
+setlocal formatprg=raco\ fmt
+
+" Undo our settings when the filetype changes away from Racket
+" (this should be amended if settings/mappings are added above!)
+let b:undo_ftplugin =
+ \ "setlocal iskeyword< lispwords< lisp< comments< formatoptions< formatprg<"
+ \. " | setlocal commentstring<"
+
+if !exists("no_plugin_maps") && !exists("no_racket_maps")
+ " Simply setting keywordprg like this works:
+ " setlocal keywordprg=raco\ docs
+ " but then vim says:
+ " "press ENTER or type a command to continue"
+ " We avoid the annoyance of having to hit enter by remapping K directly.
+ function s:RacketDoc(word) abort
+ execute 'silent !raco docs --' shellescape(a:word)
+ redraw!
+ endfunction
+ nnoremap <buffer> <Plug>RacketDoc :call <SID>RacketDoc(expand('<cword>'))<CR>
+ nmap <buffer> K <Plug>RacketDoc
+
+ " For the visual mode K mapping, it's slightly more convoluted to get the
+ " selected text:
+ function! s:Racket_visual_doc()
+ try
+ let l:old_a = @a
+ normal! gv"ay
+ call system("raco docs '". @a . "'")
+ redraw!
+ return @a
+ finally
+ let @a = l:old_a
+ endtry
+ endfunction
+
+ xnoremap <buffer> <Plug>RacketDoc :call <SID>Racket_visual_doc()<cr>
+ xmap <buffer> K <Plug>RacketDoc
+
+ let b:undo_ftplugin .=
+ \ " | silent! execute 'nunmap <buffer> K'"
+ \. " | silent! execute 'xunmap <buffer> K'"
+endif
+
+if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter")
+ let b:browsefilter =
+ \ "Racket Source Files (*.rkt *.rktl)\t*.rkt;*.rktl\n"
+ \. "All Files (*.*)\t*.*\n"
+ let b:undo_ftplugin .= " | unlet! b:browsefilter"
+endif
+
+if exists("loaded_matchit") && !exists("b:match_words")
+ let b:match_words = '#|:|#'
+ let b:undo_ftplugin .= " | unlet! b:match_words"
+endif
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
diff --git a/runtime/ftplugin/readline.vim b/runtime/ftplugin/readline.vim
index e9ef93ec7f..eba7122347 100644
--- a/runtime/ftplugin/readline.vim
+++ b/runtime/ftplugin/readline.vim
@@ -1,7 +1,8 @@
" Vim filetype plugin file
-" Language: readline(3) configuration file
-" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
-" Latest Revision: 2008-07-09
+" Language: readline(3) configuration file
+" Maintainer: Doug Kearns <dougkearns@gmail.com>
+" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
+" Last Change: 2022 Dec 09
if exists("b:did_ftplugin")
finish
@@ -11,9 +12,25 @@ 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 com< cms< fo<"
-setlocal comments=:# commentstring=#\ %s formatoptions-=t formatoptions+=croql
+if exists("loaded_matchit") && !exists("b:match_words")
+ let b:match_ignorecase = 0
+ let b:match_words = '$if:$else:$endif'
+ let b:undo_ftplugin ..= " | unlet! b:match_ignorecase b:match_words"
+endif
+
+if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter")
+ let b:browsefilter = "Readline Intialization Files (inputrc .inputrc)\tinputrc;*.inputrc\n" ..
+ \ "All Files (*.*)\t*.*\n"
+ let b:undo_ftplugin ..= " | unlet! b:browsefilter"
+endif
let &cpo = s:cpo_save
unlet s:cpo_save
+
+" vim: nowrap sw=2 sts=2 ts=8 noet:
diff --git a/runtime/ftplugin/sh.vim b/runtime/ftplugin/sh.vim
index 93a46f63e2..b6fdb8f3e2 100644
--- a/runtime/ftplugin/sh.vim
+++ b/runtime/ftplugin/sh.vim
@@ -1,12 +1,12 @@
" Vim filetype plugin file
-" Language: sh
-"
-" This runtime file is looking for a new maintainer.
-"
-" Former maintainer: Dan Sharp
-" Last Changed: 20 Jan 2009
-
-if exists("b:did_ftplugin") | finish | endif
+" Language: sh
+" Maintainer: Doug Kearns <dougkearns@gmail.com>
+" Previous Maintainer: Dan Sharp
+" Last Change: 2022 Sep 07
+
+if exists("b:did_ftplugin")
+ finish
+endif
let b:did_ftplugin = 1
" Make sure the continuation lines below do not cause problems in
@@ -14,28 +14,35 @@ let b:did_ftplugin = 1
let s:save_cpo = &cpo
set cpo-=C
-setlocal commentstring=#%s
+setlocal comments=:#
+setlocal commentstring=#\ %s
+setlocal formatoptions-=t formatoptions+=croql
+
+let b:undo_ftplugin = "setl com< cms< fo<"
" Shell: thanks to Johannes Zellner
-if exists("loaded_matchit")
- let s:sol = '\%(;\s*\|^\s*\)\@<=' " start of line
- let b:match_words =
- \ s:sol.'if\>:' . s:sol.'elif\>:' . s:sol.'else\>:' . s:sol. 'fi\>,' .
- \ s:sol.'\%(for\|while\)\>:' . s:sol. 'done\>,' .
- \ s:sol.'case\>:' . s:sol. 'esac\>'
+if exists("loaded_matchit") && !exists("b:match_words")
+ let b:match_ignorecase = 0
+ let s:sol = '\%(;\s*\|^\s*\)\@<=' " start of line
+ let b:match_words =
+ \ s:sol .. 'if\>:' .. s:sol.'elif\>:' .. s:sol.'else\>:' .. s:sol .. 'fi\>,' ..
+ \ s:sol .. '\%(for\|while\)\>:' .. s:sol .. 'done\>,' ..
+ \ s:sol .. 'case\>:' .. s:sol .. 'esac\>'
+ unlet s:sol
+ let b:undo_ftplugin ..= " | unlet! b:match_ignorecase b:match_words"
endif
" Change the :browse e filter to primarily show shell-related files.
-if has("gui_win32")
- let b:browsefilter="Bourne Shell Scripts (*.sh)\t*.sh\n" .
- \ "Korn Shell Scripts (*.ksh)\t*.ksh\n" .
- \ "Bash Shell Scripts (*.bash)\t*.bash\n" .
- \ "All Files (*.*)\t*.*\n"
+if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter")
+ let b:browsefilter = "Bourne Shell Scripts (*.sh)\t*.sh\n" ..
+ \ "Korn Shell Scripts (*.ksh)\t*.ksh\n" ..
+ \ "Bash Shell Scripts (*.bash)\t*.bash\n" ..
+ \ "All Files (*.*)\t*.*\n"
+ let b:undo_ftplugin ..= " | unlet! b:browsefilter"
endif
-" Undo the stuff we changed.
-let b:undo_ftplugin = "setlocal cms< | unlet! b:browsefilter b:match_words"
-
" Restore the saved compatibility options.
let &cpo = s:save_cpo
unlet s:save_cpo
+
+" vim: nowrap sw=2 sts=2 ts=8 noet:
diff --git a/runtime/ftplugin/ssa.vim b/runtime/ftplugin/ssa.vim
new file mode 100644
index 0000000000..04cc7a9bde
--- /dev/null
+++ b/runtime/ftplugin/ssa.vim
@@ -0,0 +1,13 @@
+" Vim filetype plugin
+" Language: SubStation Alpha
+" Maintainer: ObserverOfTime <chronobserver@disroot.org>
+" Last Change: 2022 Oct 10
+
+if exists('b:did_ftplugin')
+ finish
+endif
+let b:did_ftplugin = 1
+
+setlocal comments=:;,:!: commentstring=;\ %s
+
+let b:undo_ftplugin = 'setl com< cms<'
diff --git a/runtime/ftplugin/vdf.vim b/runtime/ftplugin/vdf.vim
new file mode 100644
index 0000000000..973d7c0e48
--- /dev/null
+++ b/runtime/ftplugin/vdf.vim
@@ -0,0 +1,14 @@
+" Vim filetype plugin
+" Language: Valve Data Format
+" Maintainer: ObserverOfTime <chronobserver@disroot.org>
+" Last Change: 2022 Sep 15
+
+if exists('b:did_ftplugin')
+ finish
+endif
+let b:did_ftplugin = 1
+
+setl comments=:// commentstring=//\ %s
+setl foldmethod=syntax
+
+let b:undo_ftplugin = 'setl com< cms< fdm<'
diff --git a/runtime/ftplugin/vim.vim b/runtime/ftplugin/vim.vim
index 772899cb42..b64bb55d68 100644
--- a/runtime/ftplugin/vim.vim
+++ b/runtime/ftplugin/vim.vim
@@ -1,7 +1,7 @@
" Vim filetype plugin
" Language: Vim
" Maintainer: Bram Moolenaar <Bram@vim.org>
-" Last Change: 2022 Aug 4
+" Last Change: 2022 Nov 27
" Only do this when not done yet for this buffer
if exists("b:did_ftplugin")
@@ -98,7 +98,7 @@ if exists("loaded_matchit")
" func name
" require a parenthesis following, then there can be an "endfunc".
let b:match_words =
- \ '\<\%(fu\%[nction]\|def\)!\=\s\+\S\+(:\%(\%(^\||\)\s*\)\@<=\<retu\%[rn]\>:\%(\%(^\||\)\s*\)\@<=\<\%(endf\%[unction]\|enddef\)\>,' .
+ \ '\<\%(fu\%[nction]\|def\)!\=\s\+\S\+\s*(:\%(\%(^\||\)\s*\)\@<=\<retu\%[rn]\>:\%(\%(^\||\)\s*\)\@<=\<\%(endf\%[unction]\|enddef\)\>,' .
\ '\<\(wh\%[ile]\|for\)\>:\%(\%(^\||\)\s*\)\@<=\<brea\%[k]\>:\%(\%(^\||\)\s*\)\@<=\<con\%[tinue]\>:\%(\%(^\||\)\s*\)\@<=\<end\(w\%[hile]\|fo\%[r]\)\>,' .
\ '\<if\>:\%(\%(^\||\)\s*\)\@<=\<el\%[seif]\>:\%(\%(^\||\)\s*\)\@<=\<en\%[dif]\>,' .
\ '{:},' .
diff --git a/runtime/ftplugin/zig.vim b/runtime/ftplugin/zig.vim
new file mode 100644
index 0000000000..e740a52849
--- /dev/null
+++ b/runtime/ftplugin/zig.vim
@@ -0,0 +1,66 @@
+" Vim filetype plugin file
+" Language: Zig
+" Upstream: https://github.com/ziglang/zig.vim
+
+" Only do this when not done yet for this buffer
+if exists("b:did_ftplugin")
+ finish
+endif
+
+let b:did_ftplugin = 1
+
+let s:cpo_orig = &cpo
+set cpo&vim
+
+compiler zig_build
+
+" Match Zig builtin fns
+setlocal iskeyword+=@-@
+
+" Recomended code style, no tabs and 4-space indentation
+setlocal expandtab
+setlocal tabstop=8
+setlocal softtabstop=4
+setlocal shiftwidth=4
+
+setlocal formatoptions-=t formatoptions+=croql
+
+setlocal suffixesadd=.zig,.zir
+
+if has('comments')
+ setlocal comments=:///,://!,://,:\\\\
+ setlocal commentstring=//\ %s
+endif
+
+if has('find_in_path')
+ let &l:includeexpr='substitute(v:fname, "^([^.])$", "\1.zig", "")'
+ let &l:include='\v(\@import>|\@cInclude>|^\s*\#\s*include)'
+endif
+
+let &l:define='\v(<fn>|<const>|<var>|^\s*\#\s*define)'
+
+if !exists('g:zig_std_dir') && exists('*json_decode') && executable('zig')
+ silent let s:env = system('zig env')
+ if v:shell_error == 0
+ let g:zig_std_dir = json_decode(s:env)['std_dir']
+ endif
+ unlet! s:env
+endif
+
+if exists('g:zig_std_dir')
+ let &l:path = &l:path . ',' . g:zig_std_dir
+endif
+
+let b:undo_ftplugin =
+ \ 'setl isk< et< ts< sts< sw< fo< sua< mp< com< cms< inex< inc< pa<'
+
+augroup vim-zig
+ autocmd! * <buffer>
+ autocmd BufWritePre <buffer> if get(g:, 'zig_fmt_autosave', 1) | call zig#fmt#Format() | endif
+augroup END
+
+let b:undo_ftplugin .= '|au! vim-zig * <buffer>'
+
+let &cpo = s:cpo_orig
+unlet s:cpo_orig
+" vim: tabstop=8 shiftwidth=4 softtabstop=4 expandtab
diff --git a/runtime/ftplugin/zimbu.vim b/runtime/ftplugin/zimbu.vim
index e365ccf07e..cbe2f55572 100644
--- a/runtime/ftplugin/zimbu.vim
+++ b/runtime/ftplugin/zimbu.vim
@@ -1,7 +1,7 @@
" Vim filetype plugin file
" Language: Zimbu
" Maintainer: Bram Moolenaar <Bram@vim.org>
-" Last Change: 2021 Nov 12
+" Last Change: 2022 Sep 07
" Only do this when not done yet for this buffer
if exists("b:did_ftplugin")
@@ -28,7 +28,7 @@ endif
" Set 'comments' to format dashed lists in comments.
" And to keep Zudocu comment characters.
-setlocal comments=sO:#\ -,mO:#\ \ ,:#=,:#-,:#%,:#
+setlocal comments=sO:#\ -,mO:#\ \ ,exO:#/,s:/*,m:\ ,ex:*/,:#=,:#-,:#%,:#
setlocal errorformat^=%f\ line\ %l\ col\ %c:\ %m,ERROR:\ %m
diff --git a/runtime/ftplugin/zsh.vim b/runtime/ftplugin/zsh.vim
index 34410f1c62..0ca8077305 100644
--- a/runtime/ftplugin/zsh.vim
+++ b/runtime/ftplugin/zsh.vim
@@ -2,7 +2,7 @@
" Language: Zsh shell script
" Maintainer: Christian Brabandt <cb@256bit.org>
" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
-" Latest Revision: 2020-09-01
+" Latest Revision: 2021-04-03
" License: Vim (see :h license)
" Repository: https://github.com/chrisbra/vim-zsh
diff --git a/runtime/indent/chatito.vim b/runtime/indent/chatito.vim
new file mode 100644
index 0000000000..1ff5e9e3f1
--- /dev/null
+++ b/runtime/indent/chatito.vim
@@ -0,0 +1,32 @@
+" Vim indent file
+" Language: Chatito
+" Maintainer: ObserverOfTime <chronobserver@disroot.org>
+" Last Change: 2022 Sep 20
+
+if exists('b:did_indent')
+ finish
+endif
+let b:did_indent = 1
+
+setlocal indentexpr=GetChatitoIndent()
+setlocal indentkeys=o,O,*<Return>,0#,!^F
+
+let b:undo_indent = 'setl inde< indk<'
+
+if exists('*GetChatitoIndent')
+ finish
+endif
+
+function GetChatitoIndent()
+ let l:prev = v:lnum - 1
+ if getline(prevnonblank(l:prev)) =~# '^[~%@]\['
+ " shift indent after definitions
+ return shiftwidth()
+ elseif getline(l:prev) !~# '^\s*$'
+ " maintain indent in sentences
+ return indent(l:prev)
+ else
+ " reset indent after a blank line
+ return 0
+ end
+endfunction
diff --git a/runtime/indent/erlang.vim b/runtime/indent/erlang.vim
index 4e7bf4ef4d..7aa38587a6 100644
--- a/runtime/indent/erlang.vim
+++ b/runtime/indent/erlang.vim
@@ -4,7 +4,7 @@
" Contributors: Edwin Fine <efine145_nospam01 at usa dot net>
" Pawel 'kTT' Salata <rockplayer.pl@gmail.com>
" Ricardo Catalinas Jiménez <jimenezrick@gmail.com>
-" Last Update: 2020-Jun-11
+" Last Update: 2022-Sep-06
" License: Vim license
" URL: https://github.com/vim-erlang/vim-erlang-runtime
@@ -30,7 +30,9 @@ else
endif
setlocal indentexpr=ErlangIndent()
-setlocal indentkeys+=0=end,0=of,0=catch,0=after,0=when,0=),0=],0=},0=>>
+setlocal indentkeys+=0=end,0=of,0=catch,0=after,0=else,0=when,0=),0=],0=},0=>>
+
+let b:undo_indent = "setl inde< indk<"
" Only define the functions once
if exists("*ErlangIndent")
@@ -235,8 +237,8 @@ function! s:GetTokensFromLine(line, string_continuation, atom_continuation,
" Two-character tokens
elseif i + 1 < linelen &&
- \ index(['->', '<<', '>>', '||', '==', '/=', '=<', '>=', '++', '--',
- \ '::'],
+ \ index(['->', '<<', '>>', '||', '==', '/=', '=<', '>=', '?=', '++',
+ \ '--', '::'],
\ a:line[i : i + 1]) != -1
call add(indtokens, [a:line[i : i + 1], vcol, i])
let next_i = i + 2
@@ -558,8 +560,8 @@ function! s:IsCatchStandalone(lnum, i)
let is_standalone = 0
elseif prev_token =~# '[a-z]'
if index(['after', 'and', 'andalso', 'band', 'begin', 'bnot', 'bor', 'bsl',
- \ 'bsr', 'bxor', 'case', 'catch', 'div', 'not', 'or', 'orelse',
- \ 'rem', 'try', 'xor'], prev_token) != -1
+ \ 'bsr', 'bxor', 'case', 'catch', 'div', 'maybe', 'not', 'or',
+ \ 'orelse', 'rem', 'try', 'xor'], prev_token) != -1
" If catch is after these keywords, it is standalone
let is_standalone = 1
else
@@ -568,7 +570,7 @@ function! s:IsCatchStandalone(lnum, i)
"
" Keywords:
" - may precede 'catch': end
- " - may not precede 'catch': fun if of receive when
+ " - may not precede 'catch': else fun if of receive when
" - unused: cond let query
let is_standalone = 0
endif
@@ -577,7 +579,7 @@ function! s:IsCatchStandalone(lnum, i)
let is_standalone = 0
else
" This 'else' branch includes the following tokens:
- " -> == /= =< < >= > =:= =/= + - * / ++ -- :: < > ; ( [ { ? = ! . |
+ " -> == /= =< < >= > ?= =:= =/= + - * / ++ -- :: < > ; ( [ { ? = ! . |
let is_standalone = 1
endif
@@ -590,6 +592,7 @@ endfunction
" Purpose:
" This function is called when a begin-type element ('begin', 'case',
" '[', '<<', etc.) is found. It asks the caller to return if the stack
+" if already empty.
" Parameters:
" stack: [token]
" token: string
@@ -758,7 +761,7 @@ endfunction
function! s:SearchEndPair(lnum, curr_col)
return s:SearchPair(
\ a:lnum, a:curr_col,
- \ '\C\<\%(case\|try\|begin\|receive\|if\)\>\|' .
+ \ '\C\<\%(case\|try\|begin\|receive\|if\|maybe\)\>\|' .
\ '\<fun\>\%(\s\|\n\|%.*$\|[A-Z_@][a-zA-Z_@]*\)*(',
\ '',
\ '\<end\>')
@@ -847,6 +850,7 @@ function! s:ErlangCalcIndent2(lnum, stack)
if ret | return res | endif
" case EXPR of BRANCHES end
+ " if BRANCHES end
" try EXPR catch BRANCHES end
" try EXPR after BODY end
" try EXPR catch BRANCHES after BODY end
@@ -855,15 +859,17 @@ function! s:ErlangCalcIndent2(lnum, stack)
" try EXPR of BRANCHES catch BRANCHES after BODY end
" receive BRANCHES end
" receive BRANCHES after BRANCHES end
+ " maybe EXPR end
+ " maybe EXPR else BRANCHES end
" This branch is not Emacs-compatible
- elseif (index(['of', 'receive', 'after', 'if'], token) != -1 ||
+ elseif (index(['of', 'receive', 'after', 'if', 'else'], token) != -1 ||
\ (token ==# 'catch' && !s:IsCatchStandalone(lnum, i))) &&
\ !last_token_of_line &&
\ (empty(stack) || stack ==# ['when'] || stack ==# ['->'] ||
\ stack ==# ['->', ';'])
- " If we are after of/receive, but these are not the last
+ " If we are after of/receive/etc, but these are not the last
" tokens of the line, we want to indent like this:
"
" % stack == []
@@ -889,21 +895,21 @@ function! s:ErlangCalcIndent2(lnum, stack)
" stack = ['when'] => LTI is a guard
if empty(stack) || stack == ['->', ';']
call s:Log(' LTI is in a condition after ' .
- \'"of/receive/after/if/catch" -> return')
+ \'"of/receive/after/if/else/catch" -> return')
return stored_vcol
elseif stack == ['->']
call s:Log(' LTI is in a branch after ' .
- \'"of/receive/after/if/catch" -> return')
+ \'"of/receive/after/if/else/catch" -> return')
return stored_vcol + shiftwidth()
elseif stack == ['when']
call s:Log(' LTI is in a guard after ' .
- \'"of/receive/after/if/catch" -> return')
+ \'"of/receive/after/if/else/catch" -> return')
return stored_vcol + shiftwidth()
else
return s:UnexpectedToken(token, stack)
endif
- elseif index(['case', 'if', 'try', 'receive'], token) != -1
+ elseif index(['case', 'if', 'try', 'receive', 'maybe'], token) != -1
" stack = [] => LTI is a condition
" stack = ['->'] => LTI is a branch
@@ -913,45 +919,47 @@ function! s:ErlangCalcIndent2(lnum, stack)
" pass
elseif (token ==# 'case' && stack[0] ==# 'of') ||
\ (token ==# 'if') ||
+ \ (token ==# 'maybe' && stack[0] ==# 'else') ||
\ (token ==# 'try' && (stack[0] ==# 'of' ||
\ stack[0] ==# 'catch' ||
\ stack[0] ==# 'after')) ||
\ (token ==# 'receive')
" From the indentation point of view, the keyword
- " (of/catch/after/end) before the LTI is what counts, so
+ " (of/catch/after/else/end) before the LTI is what counts, so
" when we reached these tokens, and the stack already had
- " a catch/after/end, we didn't modify it.
+ " a catch/after/else/end, we didn't modify it.
"
- " This way when we reach case/try/receive (i.e. now),
- " there is at most one of/catch/after/end token in the
+ " This way when we reach case/try/receive/maybe (i.e. now),
+ " there is at most one of/catch/after/else/end token in the
" stack.
if token ==# 'case' || token ==# 'try' ||
- \ (token ==# 'receive' && stack[0] ==# 'after')
+ \ (token ==# 'receive' && stack[0] ==# 'after') ||
+ \ (token ==# 'maybe' && stack[0] ==# 'else')
call s:Pop(stack)
endif
if empty(stack)
call s:Log(' LTI is in a condition; matching ' .
- \'"case/if/try/receive" found')
+ \'"case/if/try/receive/maybe" found')
let stored_vcol = curr_vcol + shiftwidth()
elseif stack[0] ==# 'align_to_begin_element'
call s:Pop(stack)
let stored_vcol = curr_vcol
elseif len(stack) > 1 && stack[0] ==# '->' && stack[1] ==# ';'
call s:Log(' LTI is in a condition; matching ' .
- \'"case/if/try/receive" found')
+ \'"case/if/try/receive/maybe" found')
call s:Pop(stack)
call s:Pop(stack)
let stored_vcol = curr_vcol + shiftwidth()
elseif stack[0] ==# '->'
call s:Log(' LTI is in a branch; matching ' .
- \'"case/if/try/receive" found')
+ \'"case/if/try/receive/maybe" found')
call s:Pop(stack)
let stored_vcol = curr_vcol + 2 * shiftwidth()
elseif stack[0] ==# 'when'
call s:Log(' LTI is in a guard; matching ' .
- \'"case/if/try/receive" found')
+ \'"case/if/try/receive/maybe" found')
call s:Pop(stack)
let stored_vcol = curr_vcol + 2 * shiftwidth() + 2
endif
@@ -1213,7 +1221,7 @@ function! s:ErlangCalcIndent2(lnum, stack)
if empty(stack)
call s:Push(stack, ';')
- elseif index([';', '->', 'when', 'end', 'after', 'catch'],
+ elseif index([';', '->', 'when', 'end', 'after', 'catch', 'else'],
\stack[0]) != -1
" Pass:
"
@@ -1223,10 +1231,10 @@ function! s:ErlangCalcIndent2(lnum, stack)
" should keep that, because they signify the type of the
" LTI (branch, condition or guard).
" - From the indentation point of view, the keyword
- " (of/catch/after/end) before the LTI is what counts, so
- " if the stack already has a catch/after/end, we don't
- " modify it. This way when we reach case/try/receive,
- " there will be at most one of/catch/after/end token in
+ " (of/catch/after/else/end) before the LTI is what counts, so
+ " if the stack already has a catch/after/else/end, we don't
+ " modify it. This way when we reach case/try/receive/maybe,
+ " there will be at most one of/catch/after/else/end token in
" the stack.
else
return s:UnexpectedToken(token, stack)
@@ -1242,7 +1250,8 @@ function! s:ErlangCalcIndent2(lnum, stack)
" stack = ['->'] -> LTI is a condition
" stack = ['->', ';'] -> LTI is a branch
call s:Push(stack, '->')
- elseif index(['->', 'when', 'end', 'after', 'catch'], stack[0]) != -1
+ elseif index(['->', 'when', 'end', 'after', 'catch', 'else'],
+ \stack[0]) != -1
" Pass:
"
" - If the stack top is another '->', then one '->' is
@@ -1250,10 +1259,10 @@ function! s:ErlangCalcIndent2(lnum, stack)
" - If the stack top is a 'when', then we should keep
" that, because this signifies that LTI is a in a guard.
" - From the indentation point of view, the keyword
- " (of/catch/after/end) before the LTI is what counts, so
- " if the stack already has a catch/after/end, we don't
- " modify it. This way when we reach case/try/receive,
- " there will be at most one of/catch/after/end token in
+ " (of/catch/after/else/end) before the LTI is what counts, so
+ " if the stack already has a catch/after/else/end, we don't
+ " modify it. This way when we reach case/try/receive/maybe,
+ " there will be at most one of/catch/after/else/end token in
" the stack.
else
return s:UnexpectedToken(token, stack)
@@ -1283,7 +1292,8 @@ function! s:ErlangCalcIndent2(lnum, stack)
" LTI
call s:Push(stack, token)
endif
- elseif index(['->', 'when', 'end', 'after', 'catch'], stack[0]) != -1
+ elseif index(['->', 'when', 'end', 'after', 'catch', 'else'],
+ \stack[0]) != -1
" Pass:
" - If the stack top is another 'when', then one 'when' is
" enough.
@@ -1291,21 +1301,63 @@ function! s:ErlangCalcIndent2(lnum, stack)
" should keep that, because they signify the type of the
" LTI (branch, condition or guard).
" - From the indentation point of view, the keyword
- " (of/catch/after/end) before the LTI is what counts, so
- " if the stack already has a catch/after/end, we don't
- " modify it. This way when we reach case/try/receive,
- " there will be at most one of/catch/after/end token in
+ " (of/catch/after/else/end) before the LTI is what counts, so
+ " if the stack already has a catch/after/else/end, we don't
+ " modify it. This way when we reach case/try/receive/maybe,
+ " there will be at most one of/catch/after/else/end token in
" the stack.
else
return s:UnexpectedToken(token, stack)
endif
- elseif token ==# 'of' || token ==# 'after' ||
+ elseif token ==# 'of' || token ==# 'after' || token ==# 'else' ||
\ (token ==# 'catch' && !s:IsCatchStandalone(lnum, i))
- if token ==# 'after'
- " If LTI is between an 'after' and the corresponding
- " 'end', then let's return
+ if token ==# 'after' || token ==# 'else'
+ " If LTI is between an after/else and the corresponding 'end', then
+ " let's return because calculating the indentation based on
+ " after/else is enough.
+ "
+ " Example:
+ " receive A after
+ " LTI
+ " maybe A else
+ " LTI
+ "
+ " Note about Emacs compabitility {{{
+ "
+ " It would be fine to indent the examples above the following way:
+ "
+ " receive A after
+ " LTI
+ " maybe A else
+ " LTI
+ "
+ " We intend it the way above because that is how Emacs does it.
+ " Also, this is a bit faster.
+ "
+ " We are still not 100% Emacs compatible because of placing the
+ " 'end' after the indented blocks.
+ "
+ " Emacs example:
+ "
+ " receive A after
+ " LTI
+ " end,
+ " maybe A else
+ " LTI
+ " end % Yes, it's here (in OTP 25.0, might change
+ " % later)
+ "
+ " vim-erlang example:
+ "
+ " receive A after
+ " LTI
+ " end,
+ " maybe A else
+ " LTI
+ " end
+ " }}}
let [ret, res] = s:BeginElementFoundIfEmpty(stack, token, curr_vcol,
\stored_vcol, shiftwidth())
if ret | return res | endif
@@ -1313,7 +1365,8 @@ function! s:ErlangCalcIndent2(lnum, stack)
if empty(stack) || stack[0] ==# '->' || stack[0] ==# 'when'
call s:Push(stack, token)
- elseif stack[0] ==# 'catch' || stack[0] ==# 'after' || stack[0] ==# 'end'
+ elseif stack[0] ==# 'catch' || stack[0] ==# 'after' ||
+ \stack[0] ==# 'else' || stack[0] ==# 'end'
" Pass: From the indentation point of view, the keyword
" (of/catch/after/end) before the LTI is what counts, so
" if the stack already has a catch/after/end, we don't
@@ -1403,7 +1456,7 @@ function! ErlangIndent()
endif
let ml = matchlist(currline,
- \'^\(\s*\)\(\%(end\|of\|catch\|after\)\>\|[)\]}]\|>>\)')
+ \'^\(\s*\)\(\%(end\|of\|catch\|after\|else\)\>\|[)\]}]\|>>\)')
" If the line has a special beginning, but not a standalone catch
if !empty(ml) && !(ml[2] ==# 'catch' && s:IsCatchStandalone(v:lnum, 0))
diff --git a/runtime/indent/gyp.vim b/runtime/indent/gyp.vim
new file mode 100644
index 0000000000..c3980ac568
--- /dev/null
+++ b/runtime/indent/gyp.vim
@@ -0,0 +1,7 @@
+" Vim indent file
+" Language: GYP
+" Maintainer: ObserverOfTime <chronobserver@disroot.org>
+" Last Change: 2022 Sep 27
+
+" JSON indent works well
+runtime! indent/json.vim
diff --git a/runtime/indent/hare.vim b/runtime/indent/hare.vim
new file mode 100644
index 0000000000..bc4fea4e61
--- /dev/null
+++ b/runtime/indent/hare.vim
@@ -0,0 +1,138 @@
+" Vim indent file
+" Language: Hare
+" Maintainer: Amelia Clarke <me@rsaihe.dev>
+" Last Change: 2022 Sep 22
+
+if exists("b:did_indent")
+ finish
+endif
+let b:did_indent = 1
+
+if !has("cindent") || !has("eval")
+ finish
+endif
+
+setlocal cindent
+
+" L0 -> don't deindent labels
+" (s -> use one indent after a trailing (
+" m1 -> if ) starts a line, indent it the same as its matching (
+" ks -> add an extra indent to extra lines in an if expression or for expression
+" j1 -> indent code inside {} one level when in parentheses
+" J1 -> see j1
+" *0 -> don't search for unclosed block comments
+" #1 -> don't deindent lines that begin with #
+setlocal cinoptions=L0,(s,m1,ks,j1,J1,*0,#1
+
+" Controls which keys reindent the current line.
+" 0{ -> { at beginning of line
+" 0} -> } at beginning of line
+" 0) -> ) at beginning of line
+" 0] -> ] at beginning of line
+" !^F -> <C-f> (not inserted)
+" o -> <CR> or `o` command
+" O -> `O` command
+" e -> else
+" 0=case -> case
+setlocal indentkeys=0{,0},0),0],!^F,o,O,e,0=case
+
+setlocal cinwords=if,else,for,switch,match
+
+setlocal indentexpr=GetHareIndent()
+
+function! FloorCindent(lnum)
+ return cindent(a:lnum) / shiftwidth() * shiftwidth()
+endfunction
+
+function! GetHareIndent()
+ let line = getline(v:lnum)
+ let prevlnum = prevnonblank(v:lnum - 1)
+ let prevline = getline(prevlnum)
+ let prevprevline = getline(prevnonblank(prevlnum - 1))
+
+ " This is all very hacky and imperfect, but it's tough to do much better when
+ " working with regex-based indenting rules.
+
+ " If the previous line ended with =, indent by one shiftwidth.
+ if prevline =~# '\v\=\s*(//.*)?$'
+ return indent(prevlnum) + shiftwidth()
+ endif
+
+ " If the previous line ended in a semicolon and the line before that ended
+ " with =, deindent by one shiftwidth.
+ if prevline =~# '\v;\s*(//.*)?$' && prevprevline =~# '\v\=\s*(//.*)?$'
+ return indent(prevlnum) - shiftwidth()
+ endif
+
+ " TODO: The following edge-case is still indented incorrectly:
+ " case =>
+ " if (foo) {
+ " bar;
+ " };
+ " | // cursor is incorrectly deindented by one shiftwidth.
+ "
+ " This only happens if the {} block is the first statement in the case body.
+ " If `case` is typed, the case will also be incorrectly deindented by one
+ " shiftwidth. Are you having fun yet?
+
+ " Deindent cases.
+ if line =~# '\v^\s*case'
+ " If the previous line was also a case, don't do any special indenting.
+ if prevline =~# '\v^\s*case'
+ return indent(prevlnum)
+ end
+
+ " If the previous line was a multiline case, deindent by one shiftwidth.
+ if prevline =~# '\v\=\>\s*(//.*)?$'
+ return indent(prevlnum) - shiftwidth()
+ endif
+
+ " If the previous line started a block, deindent by one shiftwidth.
+ " This handles the first case in a switch/match block.
+ if prevline =~# '\v\{\s*(//.*)?$'
+ return FloorCindent(v:lnum) - shiftwidth()
+ end
+
+ " If the previous line ended in a semicolon and the line before that wasn't
+ " a case, deindent by one shiftwidth.
+ if prevline =~# '\v;\s*(//.*)?$' && prevprevline !~# '\v\=\>\s*(//.*)?$'
+ return FloorCindent(v:lnum) - shiftwidth()
+ end
+
+ let l:indent = FloorCindent(v:lnum)
+
+ " If a normal cindent would indent the same amount as the previous line,
+ " deindent by one shiftwidth. This fixes some issues with `case let` blocks.
+ if l:indent == indent(prevlnum)
+ return l:indent - shiftwidth()
+ endif
+
+ " Otherwise, do a normal cindent.
+ return l:indent
+ endif
+
+ " Don't indent an extra shiftwidth for cases which span multiple lines.
+ if prevline =~# '\v\=\>\s*(//.*)?$' && prevline !~# '\v^\s*case\W'
+ return indent(prevlnum)
+ endif
+
+ " Indent the body of a case.
+ " If the previous line ended in a semicolon and the line before that was a
+ " case, don't do any special indenting.
+ if prevline =~# '\v;\s*(//.*)?$' && prevprevline =~# '\v\=\>\s*(//.*)?$' && line !~# '\v^\s*}'
+ return indent(prevlnum)
+ endif
+
+ let l:indent = FloorCindent(v:lnum)
+
+ " If the previous line was a case and a normal cindent wouldn't indent, indent
+ " an extra shiftwidth.
+ if prevline =~# '\v\=\>\s*(//.*)?$' && l:indent == indent(prevlnum)
+ return l:indent + shiftwidth()
+ endif
+
+ " If everything above is false, do a normal cindent.
+ return l:indent
+endfunction
+
+" vim: tabstop=2 shiftwidth=2 expandtab
diff --git a/runtime/indent/json.vim b/runtime/indent/json.vim
index 09c7d7a85a..510f7e8f42 100644
--- a/runtime/indent/json.vim
+++ b/runtime/indent/json.vim
@@ -3,6 +3,7 @@
" Maintainer: Eli Parra <eli@elzr.com> https://github.com/elzr/vim-json
" Last Change: 2020 Aug 30
" https://github.com/jakar/vim-json/commit/20b650e22aa750c4ab6a66aa646bdd95d7cd548a#diff-e81fc111b2052e306d126bd9989f7b7c
+" 2022 Sep 07: b:undo_indent added by Doug Kearns
" Original Author: Rogerz Zhang <rogerz.zhang at gmail.com> http://github.com/rogerz/vim-json
" Acknowledgement: Based off of vim-javascript maintained by Darrick Wiebe
" http://www.vim.org/scripts/script.php?script_id=2765
@@ -22,6 +23,8 @@ setlocal nosmartindent
setlocal indentexpr=GetJSONIndent(v:lnum)
setlocal indentkeys=0{,0},0),0[,0],!^F,o,O,e
+let b:undo_indent = "setl inde< indk< si<"
+
" Only define the function once.
if exists("*GetJSONIndent")
finish
diff --git a/runtime/indent/lua.vim b/runtime/indent/lua.vim
index 604cd333c9..0d1f934a03 100644
--- a/runtime/indent/lua.vim
+++ b/runtime/indent/lua.vim
@@ -3,6 +3,7 @@
" Maintainer: Marcus Aurelius Farias <marcus.cf 'at' bol.com.br>
" First Author: Max Ischenko <mfi 'at' ukr.net>
" Last Change: 2017 Jun 13
+" 2022 Sep 07: b:undo_indent added by Doug Kearns
" Only load this indent file when no other was loaded.
if exists("b:did_indent")
@@ -18,6 +19,8 @@ setlocal indentkeys+=0=end,0=until
setlocal autoindent
+let b:undo_indent = "setlocal autoindent< indentexpr< indentkeys<"
+
" Only define the function once.
if exists("*GetLuaIndent")
finish
diff --git a/runtime/indent/nginx.vim b/runtime/indent/nginx.vim
index 8cef7662e0..65506099f4 100644
--- a/runtime/indent/nginx.vim
+++ b/runtime/indent/nginx.vim
@@ -1,19 +1,78 @@
" Vim indent file
" Language: nginx.conf
" Maintainer: Chris Aumann <me@chr4.org>
-" Last Change: 2022 Apr 06
+" Last Change: 2022 Dec 01
-if exists("b:did_indent")
- finish
+" Only load this indent file when no other was loaded.
+if exists('b:did_indent')
+ finish
endif
let b:did_indent = 1
-setlocal indentexpr=
+setlocal indentexpr=GetNginxIndent()
-" cindent actually works for nginx' simple file structure
-setlocal cindent
+setlocal indentkeys=0{,0},0#,!^F,o,O
-" Just make sure that the comments are not reset as defs would be.
-setlocal cinkeys-=0#
+let b:undo_indent = 'setl inde< indk<'
-let b:undo_indent = "setl inde< cin< cink<"
+" Only define the function once.
+if exists('*GetNginxIndent')
+ finish
+endif
+
+function GetNginxIndent() abort
+ let plnum = s:PrevNotAsBlank(v:lnum - 1)
+
+ " Hit the start of the file, use zero indent.
+ if plnum == 0
+ return 0
+ endif
+
+ let ind = indent(plnum)
+
+ " Add a 'shiftwidth' after '{'
+ if s:AsEndWith(getline(plnum), '{')
+ let ind = ind + shiftwidth()
+ end
+
+ " Subtract a 'shiftwidth' on '}'
+ " This is the part that requires 'indentkeys'.
+ if getline(v:lnum) =~ '^\s*}'
+ let ind = ind - shiftwidth()
+ endif
+
+ let pplnum = s:PrevNotAsBlank(plnum - 1)
+
+ if s:IsLineContinuation(plnum)
+ if !s:IsLineContinuation(pplnum)
+ let ind = ind + shiftwidth()
+ end
+ else
+ if s:IsLineContinuation(pplnum)
+ let ind = ind - shiftwidth()
+ end
+ endif
+
+ return ind
+endfunction
+
+" Find the first line at or above {lnum} that is non-blank and not a comment.
+function s:PrevNotAsBlank(lnum) abort
+ let lnum = prevnonblank(a:lnum)
+ while lnum > 0
+ if getline(lnum) !~ '^\s*#'
+ break
+ endif
+ let lnum = prevnonblank(lnum - 1)
+ endwhile
+ return lnum
+endfunction
+
+" Check whether {line} ends with {pat}, ignoring trailing comments.
+function s:AsEndWith(line, pat) abort
+ return a:line =~ a:pat . '\m\s*\%(#.*\)\?$'
+endfunction
+
+function s:IsLineContinuation(lnum) abort
+ return a:lnum > 0 && !s:AsEndWith(getline(a:lnum), '[;{}]')
+endfunction
diff --git a/runtime/indent/obse.vim b/runtime/indent/obse.vim
new file mode 100644
index 0000000000..6603723dba
--- /dev/null
+++ b/runtime/indent/obse.vim
@@ -0,0 +1,55 @@
+" Vim indent file
+" Language: Oblivion Language (obl)
+" Original Creator: Kat <katisntgood@gmail.com>
+" Maintainer: Kat <katisntgood@gmail.com>
+" Created: 01 November 2021
+" Last Change: 13 November 2022
+
+if exists("b:did_indent")
+ finish
+endif
+let b:did_indent = 1
+let b:undo_indent = 'setlocal indentkeys< indentexpr<'
+
+setlocal indentexpr=GetOblIndent()
+setlocal indentkeys+==~endif,=~else,=~loop,=~end
+
+if exists("*GetOblIndent")
+ finish
+endif
+let s:keepcpo = &cpo
+set cpo&vim
+
+let s:SKIP_LINES = '^\s*\(;.*\)'
+function! GetOblIndent()
+
+ let lnum = prevnonblank(v:lnum - 1)
+ let cur_text = getline(v:lnum)
+ if lnum == 0
+ return 0
+ endif
+ let prev_text = getline(lnum)
+ let found_cont = 0
+ let ind = indent(lnum)
+
+ " indent next line on start terms
+ let i = match(prev_text, '\c^\s*\(\s\+\)\?\(\(if\|while\|foreach\|begin\|else\%[if]\)\>\)')
+ if i >= 0
+ let ind += shiftwidth()
+ if strpart(prev_text, i, 1) == '|' && has('syntax_items')
+ \ && synIDattr(synID(lnum, i, 1), "name") =~ '\(Comment\|String\)$'
+ let ind -= shiftwidth()
+ endif
+ endif
+ " indent current line on end/else terms
+ if cur_text =~ '\c^\s*\(\s\+\)\?\(\(loop\|endif\|else\%[if]\)\>\)'
+ let ind = ind - shiftwidth()
+ " if we are at a begin block just go to column 0
+ elseif cur_text =~ '\c^\s*\(\s\+\)\?\(\(begin\|end\)\>\)'
+ let ind = 0
+ endif
+ return ind
+endfunction
+
+let &cpo = s:keepcpo
+unlet s:keepcpo
diff --git a/runtime/indent/racket.vim b/runtime/indent/racket.vim
new file mode 100644
index 0000000000..93bd38fbff
--- /dev/null
+++ b/runtime/indent/racket.vim
@@ -0,0 +1,60 @@
+" Vim indent file
+" Language: Racket
+" Maintainer: D. Ben Knoble <ben.knoble+github@gmail.com>
+" Previous Maintainer: Will Langstroth <will@langstroth.com>
+" URL: https://github.com/benknoble/vim-racket
+" Last Change: 2022 Aug 12
+
+if exists("b:did_indent")
+ finish
+endif
+let b:did_indent = 1
+
+setlocal lisp autoindent nosmartindent
+
+setlocal lispwords+=module,module*,module+,parameterize,let-values,let*-values,letrec-values,local
+setlocal lispwords+=define/contract
+setlocal lispwords+=λ
+setlocal lispwords+=with-handlers
+setlocal lispwords+=define-values,opt-lambda,case-lambda,syntax-rules,with-syntax,syntax-case,syntax-parse
+setlocal lispwords+=define-for-syntax,define-syntax-parser,define-syntax-parse-rule,define-syntax-class,define-splicing-syntax-class
+setlocal lispwords+=define-signature,unit,unit/sig,compund-unit/sig,define-values/invoke-unit/sig
+setlocal lispwords+=define-opt/c,define-syntax-rule
+setlocal lispwords+=define-test-suite
+setlocal lispwords+=struct
+setlocal lispwords+=with-input-from-file,with-output-to-file
+
+" Racket OOP
+" TODO missing a lot of define-like forms here (e.g., define/augment, etc.)
+setlocal lispwords+=class,class*,mixin,interface,class/derived
+setlocal lispwords+=define/public,define/pubment,define/public-final
+setlocal lispwords+=define/override,define/overment,define/override-final
+setlocal lispwords+=define/augment,define/augride,define/augment-final
+setlocal lispwords+=define/private
+
+" kanren
+setlocal lispwords+=fresh,run,run*,project,conde,condu
+
+" loops
+setlocal lispwords+=for,for/list,for/fold,for*,for*/list,for*/fold,for/or,for/and,for*/or,for*/and
+setlocal lispwords+=for/hash,for/hasheq,for/hasheqv,for/sum,for/flvector,for*/flvector,for/vector,for*/vector,for*/sum,for*/hash,for*/hasheq,for*/hasheqv
+setlocal lispwords+=for/async
+setlocal lispwords+=for/set,for*/set
+setlocal lispwords+=for/first,for*/first
+
+setlocal lispwords+=match,match*,match/values,define/match,match-lambda,match-lambda*,match-lambda**
+setlocal lispwords+=match-let,match-let*,match-let-values,match-let*-values
+setlocal lispwords+=match-letrec,match-define,match-define-values
+
+setlocal lispwords+=let/cc,let/ec
+
+" qi
+setlocal lispwords+=define-flow,define-switch,flow-lambda,switch-lambda,on,switch,π,λ01
+setlocal lispwords+=define-qi-syntax,define-qi-syntax-parser,define-qi-syntax-rule
+
+" gui-easy
+setlocal lispwords+=if-view,case-view,cond-view,list-view,dyn-view
+setlocal lispwords+=case/dep
+setlocal lispwords+=define/obs
+
+let b:undo_indent = "setlocal lisp< ai< si< lw<"
diff --git a/runtime/indent/solidity.vim b/runtime/indent/solidity.vim
new file mode 100644
index 0000000000..caed726c0a
--- /dev/null
+++ b/runtime/indent/solidity.vim
@@ -0,0 +1,442 @@
+" Vim indent file
+" Language: Solidity
+" Acknowledgement: Based off of vim-javascript
+" Maintainer: Cothi (jiungdev@gmail.com)
+" Original Author: tomlion (https://github.com/tomlion/vim-solidity)
+" Last Changed: 2022 Sep 27
+"
+" 0. Initialization {{{1
+" =================
+
+" Only load this indent file when no other was loaded.
+if exists("b:did_indent")
+ finish
+endif
+let b:did_indent = 1
+
+setlocal nosmartindent
+
+" Now, set up our indentation expression and keys that trigger it.
+setlocal indentexpr=GetSolidityIndent()
+setlocal indentkeys=0{,0},0),0],0\,,!^F,o,O,e
+
+" Only define the function once.
+if exists("*GetSolidityIndent")
+ finish
+endif
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+" 1. Variables {{{1
+" ============
+
+let s:js_keywords = '^\s*\(break\|case\|catch\|continue\|debugger\|default\|delete\|do\|else\|finally\|for\|function\|if\|in\|instanceof\|new\|return\|switch\|this\|throw\|try\|typeof\|var\|void\|while\|with\)'
+
+" Regex of syntax group names that are or delimit string or are comments.
+let s:syng_strcom = 'string\|regex\|comment\c'
+
+" Regex of syntax group names that are strings.
+let s:syng_string = 'regex\c'
+
+" Regex of syntax group names that are strings or documentation.
+let s:syng_multiline = 'comment\c'
+
+" Regex of syntax group names that are line comment.
+let s:syng_linecom = 'linecomment\c'
+
+" Expression used to check whether we should skip a match with searchpair().
+let s:skip_expr = "synIDattr(synID(line('.'),col('.'),1),'name') =~ '".s:syng_strcom."'"
+
+let s:line_term = '\s*\%(\%(\/\/\).*\)\=$'
+
+" Regex that defines continuation lines, not including (, {, or [.
+let s:continuation_regex = '\%([\\*+/.:]\|\%(<%\)\@<![=-]\|\W[|&?]\|||\|&&\)' . s:line_term
+
+" Regex that defines continuation lines.
+" TODO: this needs to deal with if ...: and so on
+let s:msl_regex = '\%([\\*+/.:([]\|\%(<%\)\@<![=-]\|\W[|&?]\|||\|&&\)' . s:line_term
+
+let s:one_line_scope_regex = '\<\%(if\|else\|for\|while\)\>[^{;]*' . s:line_term
+
+" Regex that defines blocks.
+let s:block_regex = '\%([{[]\)\s*\%(|\%([*@]\=\h\w*,\=\s*\)\%(,\s*[*@]\=\h\w*\)*|\)\=' . s:line_term
+
+let s:var_stmt = '^\s*var'
+
+let s:comma_first = '^\s*,'
+let s:comma_last = ',\s*$'
+
+let s:ternary = '^\s\+[?|:]'
+let s:ternary_q = '^\s\+?'
+
+" 2. Auxiliary Functions {{{1
+" ======================
+
+" Check if the character at lnum:col is inside a string, comment, or is ascii.
+function s:IsInStringOrComment(lnum, col)
+ return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_strcom
+endfunction
+
+" Check if the character at lnum:col is inside a string.
+function s:IsInString(lnum, col)
+ return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_string
+endfunction
+
+" Check if the character at lnum:col is inside a multi-line comment.
+function s:IsInMultilineComment(lnum, col)
+ return !s:IsLineComment(a:lnum, a:col) && synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_multiline
+endfunction
+
+" Check if the character at lnum:col is a line comment.
+function s:IsLineComment(lnum, col)
+ return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_linecom
+endfunction
+
+" Find line above 'lnum' that isn't empty, in a comment, or in a string.
+function s:PrevNonBlankNonString(lnum)
+ let in_block = 0
+ let lnum = prevnonblank(a:lnum)
+ while lnum > 0
+ " Go in and out of blocks comments as necessary.
+ " If the line isn't empty (with opt. comment) or in a string, end search.
+ let line = getline(lnum)
+ if line =~ '/\*'
+ if in_block
+ let in_block = 0
+ else
+ break
+ endif
+ elseif !in_block && line =~ '\*/'
+ let in_block = 1
+ elseif !in_block && line !~ '^\s*\%(//\).*$' && !(s:IsInStringOrComment(lnum, 1) && s:IsInStringOrComment(lnum, strlen(line)))
+ break
+ endif
+ let lnum = prevnonblank(lnum - 1)
+ endwhile
+ return lnum
+endfunction
+
+" Find line above 'lnum' that started the continuation 'lnum' may be part of.
+function s:GetMSL(lnum, in_one_line_scope)
+ " Start on the line we're at and use its indent.
+ let msl = a:lnum
+ let lnum = s:PrevNonBlankNonString(a:lnum - 1)
+ while lnum > 0
+ " If we have a continuation line, or we're in a string, use line as MSL.
+ " Otherwise, terminate search as we have found our MSL already.
+ let line = getline(lnum)
+ let col = match(line, s:msl_regex) + 1
+ if (col > 0 && !s:IsInStringOrComment(lnum, col)) || s:IsInString(lnum, strlen(line))
+ let msl = lnum
+ else
+ " Don't use lines that are part of a one line scope as msl unless the
+ " flag in_one_line_scope is set to 1
+ "
+ if a:in_one_line_scope
+ break
+ end
+ let msl_one_line = s:Match(lnum, s:one_line_scope_regex)
+ if msl_one_line == 0
+ break
+ endif
+ endif
+ let lnum = s:PrevNonBlankNonString(lnum - 1)
+ endwhile
+ return msl
+endfunction
+
+function s:RemoveTrailingComments(content)
+ let single = '\/\/\(.*\)\s*$'
+ let multi = '\/\*\(.*\)\*\/\s*$'
+ return substitute(substitute(a:content, single, '', ''), multi, '', '')
+endfunction
+
+" Find if the string is inside var statement (but not the first string)
+function s:InMultiVarStatement(lnum)
+ let lnum = s:PrevNonBlankNonString(a:lnum - 1)
+
+" let type = synIDattr(synID(lnum, indent(lnum) + 1, 0), 'name')
+
+ " loop through previous expressions to find a var statement
+ while lnum > 0
+ let line = getline(lnum)
+
+ " if the line is a js keyword
+ if (line =~ s:js_keywords)
+ " check if the line is a var stmt
+ " if the line has a comma first or comma last then we can assume that we
+ " are in a multiple var statement
+ if (line =~ s:var_stmt)
+ return lnum
+ endif
+
+ " other js keywords, not a var
+ return 0
+ endif
+
+ let lnum = s:PrevNonBlankNonString(lnum - 1)
+ endwhile
+
+ " beginning of program, not a var
+ return 0
+endfunction
+
+" Find line above with beginning of the var statement or returns 0 if it's not
+" this statement
+function s:GetVarIndent(lnum)
+ let lvar = s:InMultiVarStatement(a:lnum)
+ let prev_lnum = s:PrevNonBlankNonString(a:lnum - 1)
+
+ if lvar
+ let line = s:RemoveTrailingComments(getline(prev_lnum))
+
+ " if the previous line doesn't end in a comma, return to regular indent
+ if (line !~ s:comma_last)
+ return indent(prev_lnum) - &sw
+ else
+ return indent(lvar) + &sw
+ endif
+ endif
+
+ return -1
+endfunction
+
+
+" Check if line 'lnum' has more opening brackets than closing ones.
+function s:LineHasOpeningBrackets(lnum)
+ let open_0 = 0
+ let open_2 = 0
+ let open_4 = 0
+ let line = getline(a:lnum)
+ let pos = match(line, '[][(){}]', 0)
+ while pos != -1
+ if !s:IsInStringOrComment(a:lnum, pos + 1)
+ let idx = stridx('(){}[]', line[pos])
+ if idx % 2 == 0
+ let open_{idx} = open_{idx} + 1
+ else
+ let open_{idx - 1} = open_{idx - 1} - 1
+ endif
+ endif
+ let pos = match(line, '[][(){}]', pos + 1)
+ endwhile
+ return (open_0 > 0) . (open_2 > 0) . (open_4 > 0)
+endfunction
+
+function s:Match(lnum, regex)
+ let col = match(getline(a:lnum), a:regex) + 1
+ return col > 0 && !s:IsInStringOrComment(a:lnum, col) ? col : 0
+endfunction
+
+function s:IndentWithContinuation(lnum, ind, width)
+ " Set up variables to use and search for MSL to the previous line.
+ let p_lnum = a:lnum
+ let lnum = s:GetMSL(a:lnum, 1)
+ let line = getline(lnum)
+
+ " If the previous line wasn't a MSL and is continuation return its indent.
+ " TODO: the || s:IsInString() thing worries me a bit.
+ if p_lnum != lnum
+ if s:Match(p_lnum,s:continuation_regex)||s:IsInString(p_lnum,strlen(line))
+ return a:ind
+ endif
+ endif
+
+ " Set up more variables now that we know we aren't continuation bound.
+ let msl_ind = indent(lnum)
+
+ " If the previous line ended with [*+/.-=], start a continuation that
+ " indents an extra level.
+ if s:Match(lnum, s:continuation_regex)
+ if lnum == p_lnum
+ return msl_ind + a:width
+ else
+ return msl_ind
+ endif
+ endif
+
+ return a:ind
+endfunction
+
+function s:InOneLineScope(lnum)
+ let msl = s:GetMSL(a:lnum, 1)
+ if msl > 0 && s:Match(msl, s:one_line_scope_regex)
+ return msl
+ endif
+ return 0
+endfunction
+
+function s:ExitingOneLineScope(lnum)
+ let msl = s:GetMSL(a:lnum, 1)
+ if msl > 0
+ " if the current line is in a one line scope ..
+ if s:Match(msl, s:one_line_scope_regex)
+ return 0
+ else
+ let prev_msl = s:GetMSL(msl - 1, 1)
+ if s:Match(prev_msl, s:one_line_scope_regex)
+ return prev_msl
+ endif
+ endif
+ endif
+ return 0
+endfunction
+
+" 3. GetSolidityIndent Function {{{1
+" =========================
+
+function GetSolidityIndent()
+ " 3.1. Setup {{{2
+ " ----------
+
+ " Set up variables for restoring position in file. Could use v:lnum here.
+ let vcol = col('.')
+
+ " 3.2. Work on the current line {{{2
+ " -----------------------------
+
+ let ind = -1
+ " Get the current line.
+ let line = getline(v:lnum)
+ " previous nonblank line number
+ let prevline = prevnonblank(v:lnum - 1)
+
+ " If we got a closing bracket on an empty line, find its match and indent
+ " according to it. For parentheses we indent to its column - 1, for the
+ " others we indent to the containing line's MSL's level. Return -1 if fail.
+ let col = matchend(line, '^\s*[],})]')
+ if col > 0 && !s:IsInStringOrComment(v:lnum, col)
+ call cursor(v:lnum, col)
+
+ let lvar = s:InMultiVarStatement(v:lnum)
+ if lvar
+ let prevline_contents = s:RemoveTrailingComments(getline(prevline))
+
+ " check for comma first
+ if (line[col - 1] =~ ',')
+ " if the previous line ends in comma or semicolon don't indent
+ if (prevline_contents =~ '[;,]\s*$')
+ return indent(s:GetMSL(line('.'), 0))
+ " get previous line indent, if it's comma first return prevline indent
+ elseif (prevline_contents =~ s:comma_first)
+ return indent(prevline)
+ " otherwise we indent 1 level
+ else
+ return indent(lvar) + &sw
+ endif
+ endif
+ endif
+
+
+ let bs = strpart('(){}[]', stridx(')}]', line[col - 1]) * 2, 2)
+ if searchpair(escape(bs[0], '\['), '', bs[1], 'bW', s:skip_expr) > 0
+ if line[col-1]==')' && col('.') != col('$') - 1
+ let ind = virtcol('.')-1
+ else
+ let ind = indent(s:GetMSL(line('.'), 0))
+ endif
+ endif
+ return ind
+ endif
+
+ " If the line is comma first, dedent 1 level
+ if (getline(prevline) =~ s:comma_first)
+ return indent(prevline) - &sw
+ endif
+
+ if (line =~ s:ternary)
+ if (getline(prevline) =~ s:ternary_q)
+ return indent(prevline)
+ else
+ return indent(prevline) + &sw
+ endif
+ endif
+
+ " If we are in a multi-line comment, cindent does the right thing.
+ if s:IsInMultilineComment(v:lnum, 1) && !s:IsLineComment(v:lnum, 1)
+ return cindent(v:lnum)
+ endif
+
+ " Check for multiple var assignments
+" let var_indent = s:GetVarIndent(v:lnum)
+" if var_indent >= 0
+" return var_indent
+" endif
+
+ " 3.3. Work on the previous line. {{{2
+ " -------------------------------
+
+ " If the line is empty and the previous nonblank line was a multi-line
+ " comment, use that comment's indent. Deduct one char to account for the
+ " space in ' */'.
+ if line =~ '^\s*$' && s:IsInMultilineComment(prevline, 1)
+ return indent(prevline) - 1
+ endif
+
+ " Find a non-blank, non-multi-line string line above the current line.
+ let lnum = s:PrevNonBlankNonString(v:lnum - 1)
+
+ " If the line is empty and inside a string, use the previous line.
+ if line =~ '^\s*$' && lnum != prevline
+ return indent(prevnonblank(v:lnum))
+ endif
+
+ " At the start of the file use zero indent.
+ if lnum == 0
+ return 0
+ endif
+
+ " Set up variables for current line.
+ let line = getline(lnum)
+ let ind = indent(lnum)
+
+ " If the previous line ended with a block opening, add a level of indent.
+ if s:Match(lnum, s:block_regex)
+ return indent(s:GetMSL(lnum, 0)) + &sw
+ endif
+
+ " If the previous line contained an opening bracket, and we are still in it,
+ " add indent depending on the bracket type.
+ if line =~ '[[({]'
+ let counts = s:LineHasOpeningBrackets(lnum)
+ if counts[0] == '1' && searchpair('(', '', ')', 'bW', s:skip_expr) > 0
+ if col('.') + 1 == col('$')
+ return ind + &sw
+ else
+ return virtcol('.')
+ endif
+ elseif counts[1] == '1' || counts[2] == '1'
+ return ind + &sw
+ else
+ call cursor(v:lnum, vcol)
+ end
+ endif
+
+ " 3.4. Work on the MSL line. {{{2
+ " --------------------------
+
+ let ind_con = ind
+ let ind = s:IndentWithContinuation(lnum, ind_con, &sw)
+
+ " }}}2
+ "
+ "
+ let ols = s:InOneLineScope(lnum)
+ if ols > 0
+ let ind = ind + &sw
+ else
+ let ols = s:ExitingOneLineScope(lnum)
+ while ols > 0 && ind > 0
+ let ind = ind - &sw
+ let ols = s:InOneLineScope(ols - 1)
+ endwhile
+ endif
+
+ return ind
+endfunction
+
+" }}}1
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
diff --git a/runtime/indent/testdir/python.in b/runtime/indent/testdir/python.in
index e6f05f22bc..57719ee430 100644
--- a/runtime/indent/testdir/python.in
+++ b/runtime/indent/testdir/python.in
@@ -1,6 +1,24 @@
# vim: set ft=python sw=4 et:
# START_INDENT
+dict = {
+'a': 1,
+'b': 2,
+'c': 3,
+}
+# END_INDENT
+
+# START_INDENT
+# INDENT_EXE let [g:python_indent.open_paren, g:python_indent.closed_paren_align_last_line] = ['shiftwidth()', v:false]
+dict = {
+'a': 1,
+'b': 2,
+'c': 3,
+}
+# END_INDENT
+
+# START_INDENT
+# INDENT_EXE let g:python_indent.open_paren = 'shiftwidth() * 2'
# INDENT_EXE syntax match pythonFoldMarkers /{{{\d*/ contained containedin=pythonComment
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx {{{1
diff --git a/runtime/indent/testdir/python.ok b/runtime/indent/testdir/python.ok
index df3de8f186..f5ebbc2285 100644
--- a/runtime/indent/testdir/python.ok
+++ b/runtime/indent/testdir/python.ok
@@ -1,6 +1,24 @@
# vim: set ft=python sw=4 et:
# START_INDENT
+dict = {
+ 'a': 1,
+ 'b': 2,
+ 'c': 3,
+ }
+# END_INDENT
+
+# START_INDENT
+# INDENT_EXE let [g:python_indent.open_paren, g:python_indent.closed_paren_align_last_line] = ['shiftwidth()', v:false]
+dict = {
+ 'a': 1,
+ 'b': 2,
+ 'c': 3,
+}
+# END_INDENT
+
+# START_INDENT
+# INDENT_EXE let g:python_indent.open_paren = 'shiftwidth() * 2'
# INDENT_EXE syntax match pythonFoldMarkers /{{{\d*/ contained containedin=pythonComment
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx {{{1
diff --git a/runtime/indent/testdir/runtest.vim b/runtime/indent/testdir/runtest.vim
index 6bbd33cacd..fa4e16e381 100644
--- a/runtime/indent/testdir/runtest.vim
+++ b/runtime/indent/testdir/runtest.vim
@@ -11,6 +11,7 @@ syn on
set nowrapscan
set report=9999
set modeline
+set debug=throw
au! SwapExists * call HandleSwapExists()
func HandleSwapExists()
@@ -84,7 +85,12 @@ for fname in glob('testdir/*.in', 1, 1)
exe start + 1
if pattern == ''
- exe 'normal =' . (end - 1) . 'G'
+ try
+ exe 'normal =' . (end - 1) . 'G'
+ catch
+ call append(indent_at, 'ERROR: ' . v:exception)
+ let failed = 1
+ endtry
else
let lnum = search(pattern)
if lnum <= 0
@@ -99,7 +105,12 @@ for fname in glob('testdir/*.in', 1, 1)
else
exe lnum - 1
endif
- normal ==
+ try
+ normal ==
+ catch
+ call append(indent_at, 'ERROR: ' . v:exception)
+ let failed = 1
+ endtry
endif
endif
endwhile
diff --git a/runtime/indent/testdir/vb.in b/runtime/indent/testdir/vb.in
new file mode 100644
index 0000000000..1653ae6f80
--- /dev/null
+++ b/runtime/indent/testdir/vb.in
@@ -0,0 +1,134 @@
+' vim: filetype=vb shiftwidth=4 expandtab
+'
+' START_INDENT
+Public Type GEmployeeRecord ' Create user-defined type.
+ID As Integer ' Define elements of data type.
+Name As String * 20
+Address As String * 30
+Phone As Long
+HireDate As Date
+End Type
+
+Public Enum InterfaceColors
+icMistyRose = &HE1E4FF&
+icSlateGray = &H908070&
+icDodgerBlue = &HFF901E&
+icDeepSkyBlue = &HFFBF00&
+icSpringGreen = &H7FFF00&
+icForestGreen = &H228B22&
+icGoldenrod = &H20A5DA&
+icFirebrick = &H2222B2&
+End Enum
+
+Enum SecurityLevel
+IllegalEntry = -1
+SecurityLevel1 = 0
+SecurityLevel2 = 1
+End Enum
+
+Public Function TestConditional (number As Integer, ext As String) As Boolean
+Dim inRange As Boolean
+
+Select Case number
+Case <= 0
+inRange = False
+Case > 10
+inRange = False
+Case Else
+inRange = True
+End Select
+
+' This is a special case identified in the indent script.
+Select Case number
+End Select
+
+If ext = ".xlm" Then
+If inRange Then
+TestConditional = True
+Else
+TestConditional = False
+End If
+ElseIf ext = ".xlsx" Then
+If inRange Then
+TestConditional = False
+Else
+TestConditional = True
+End If
+Else
+TestConditional = False
+End If
+End Function
+
+Private Sub TestIterators (lLimit As Integer, uLimit As Integer)
+Dim a() As Variant
+Dim elmt As Variant
+Dim found As Boolean
+Dim indx As Integer
+Const specialValue As Integer = 5
+
+If uLimit < lLimit Then
+Exit Sub
+End If
+
+ReDim a(lLimit To uLimit)
+For indx=lLimit To Ulimit
+a(indx) = 2 * indx
+Next indx
+
+found = False
+For Each elmt in a
+If elmt = specialValue Then
+found = True
+End If
+Next elmt
+
+If found then
+indx = uLimit
+Do While indx >= lLimit
+indx = indx - 1
+Loop
+End If
+
+End Sub
+
+Public Sub TestMultiline (cellAddr As String, rowNbr As Long)
+Dim rng As Range
+
+Set rng = Range(cellAddr)
+With rng
+.Cells(1,1).Value = _
+"Line 1 of multiline string; " & _
+"Line 2 of multiline string; " & _
+"Line 3 of multiline string"
+End With
+
+' The following lines have whitespace after the underscore character
+' and therefore do not form a valid multiline statement. The indent
+' script correctly treats them as four single line statements contrary
+' to the author's obvious indent.
+rng..Cells(1,1).Value = _
+"Line 1 of multiline string; " & _
+"Line 2 of multiline string; " & _
+"Line 3 of multiline string"
+
+End Sub
+
+Private Sub TestStmtLabel()
+GoTo stmtLabel
+
+' Statement labels are never indented
+stmtLabel:
+
+End Sub
+
+Sub TestTypeKeyword()
+Type EmployeeRecord ' Create user-defined type.
+ID As Integer ' Define elements of data type.
+Name As String * 20
+Address As String * 30
+Phone As Long
+HireDate As Date
+End Type
+Dim varType As EmployeeRecord
+End Sub
+' END_INDENT
diff --git a/runtime/indent/testdir/vb.ok b/runtime/indent/testdir/vb.ok
new file mode 100644
index 0000000000..143c688708
--- /dev/null
+++ b/runtime/indent/testdir/vb.ok
@@ -0,0 +1,134 @@
+' vim: filetype=vb shiftwidth=4 expandtab
+'
+' START_INDENT
+Public Type GEmployeeRecord ' Create user-defined type.
+ ID As Integer ' Define elements of data type.
+ Name As String * 20
+ Address As String * 30
+ Phone As Long
+ HireDate As Date
+End Type
+
+Public Enum InterfaceColors
+ icMistyRose = &HE1E4FF&
+ icSlateGray = &H908070&
+ icDodgerBlue = &HFF901E&
+ icDeepSkyBlue = &HFFBF00&
+ icSpringGreen = &H7FFF00&
+ icForestGreen = &H228B22&
+ icGoldenrod = &H20A5DA&
+ icFirebrick = &H2222B2&
+End Enum
+
+Enum SecurityLevel
+ IllegalEntry = -1
+ SecurityLevel1 = 0
+ SecurityLevel2 = 1
+End Enum
+
+Public Function TestConditional (number As Integer, ext As String) As Boolean
+ Dim inRange As Boolean
+
+ Select Case number
+ Case <= 0
+ inRange = False
+ Case > 10
+ inRange = False
+ Case Else
+ inRange = True
+ End Select
+
+ ' This is a special case identified in the indent script.
+ Select Case number
+ End Select
+
+ If ext = ".xlm" Then
+ If inRange Then
+ TestConditional = True
+ Else
+ TestConditional = False
+ End If
+ ElseIf ext = ".xlsx" Then
+ If inRange Then
+ TestConditional = False
+ Else
+ TestConditional = True
+ End If
+ Else
+ TestConditional = False
+ End If
+End Function
+
+Private Sub TestIterators (lLimit As Integer, uLimit As Integer)
+ Dim a() As Variant
+ Dim elmt As Variant
+ Dim found As Boolean
+ Dim indx As Integer
+ Const specialValue As Integer = 5
+
+ If uLimit < lLimit Then
+ Exit Sub
+ End If
+
+ ReDim a(lLimit To uLimit)
+ For indx=lLimit To Ulimit
+ a(indx) = 2 * indx
+ Next indx
+
+ found = False
+ For Each elmt in a
+ If elmt = specialValue Then
+ found = True
+ End If
+ Next elmt
+
+ If found then
+ indx = uLimit
+ Do While indx >= lLimit
+ indx = indx - 1
+ Loop
+ End If
+
+End Sub
+
+Public Sub TestMultiline (cellAddr As String, rowNbr As Long)
+ Dim rng As Range
+
+ Set rng = Range(cellAddr)
+ With rng
+ .Cells(1,1).Value = _
+ "Line 1 of multiline string; " & _
+ "Line 2 of multiline string; " & _
+ "Line 3 of multiline string"
+ End With
+
+ ' The following lines have whitespace after the underscore character
+ ' and therefore do not form a valid multiline statement. The indent
+ ' script correctly treats them as four single line statements contrary
+ ' to the author's obvious indent.
+ rng..Cells(1,1).Value = _
+ "Line 1 of multiline string; " & _
+ "Line 2 of multiline string; " & _
+ "Line 3 of multiline string"
+
+End Sub
+
+Private Sub TestStmtLabel()
+ GoTo stmtLabel
+
+ ' Statement labels are never indented
+stmtLabel:
+
+End Sub
+
+Sub TestTypeKeyword()
+ Type EmployeeRecord ' Create user-defined type.
+ ID As Integer ' Define elements of data type.
+ Name As String * 20
+ Address As String * 30
+ Phone As Long
+ HireDate As Date
+ End Type
+ Dim varType As EmployeeRecord
+End Sub
+' END_INDENT
diff --git a/runtime/indent/testdir/vim.in b/runtime/indent/testdir/vim.in
index 873045bc2c..5eb262f50a 100644
--- a/runtime/indent/testdir/vim.in
+++ b/runtime/indent/testdir/vim.in
@@ -36,6 +36,13 @@ let t = [
\ },
\ ]
+def Func()
+ var d = dd
+ ->extend({
+ })
+ eval 0
+enddef
+
" END_INDENT
" START_INDENT
diff --git a/runtime/indent/testdir/vim.ok b/runtime/indent/testdir/vim.ok
index 8e70abe619..932eebef43 100644
--- a/runtime/indent/testdir/vim.ok
+++ b/runtime/indent/testdir/vim.ok
@@ -36,6 +36,13 @@ let t = [
\ },
\ ]
+def Func()
+ var d = dd
+ ->extend({
+ })
+ eval 0
+enddef
+
" END_INDENT
" START_INDENT
diff --git a/runtime/indent/vb.vim b/runtime/indent/vb.vim
index 4d05345565..bc7142f428 100644
--- a/runtime/indent/vb.vim
+++ b/runtime/indent/vb.vim
@@ -1,8 +1,12 @@
" Vim indent file
" Language: VisualBasic (ft=vb) / Basic (ft=basic) / SaxBasic (ft=vb)
" Author: Johannes Zellner <johannes@zellner.org>
+" Maintainer: Michael Soyka (mssr953@gmail.com)
" Last Change: Fri, 18 Jun 2004 07:22:42 CEST
" Small update 2010 Jul 28 by Maxim Kim
+" 2022/12/15: add support for multiline statements.
+" 2022/12/21: move VbGetIndent from global to script-local scope
+" 2022/12/26: recognize "Type" keyword
if exists("b:did_indent")
finish
@@ -10,28 +14,33 @@ endif
let b:did_indent = 1
setlocal autoindent
-setlocal indentexpr=VbGetIndent(v:lnum)
+setlocal indentexpr=s:VbGetIndent(v:lnum)
setlocal indentkeys&
-setlocal indentkeys+==~else,=~elseif,=~end,=~wend,=~case,=~next,=~select,=~loop,<:>
+setlocal indentkeys+==~else,=~elseif,=~end,=~wend,=~case,=~next,=~select,=~loop
let b:undo_indent = "set ai< indentexpr< indentkeys<"
" Only define the function once.
-if exists("*VbGetIndent")
+if exists("*s:VbGetIndent")
finish
endif
-fun! VbGetIndent(lnum)
+function s:VbGetIndent(lnum)
+ let this_lnum = a:lnum
+ let this_line = getline(this_lnum)
+
" labels and preprocessor get zero indent immediately
- let this_line = getline(a:lnum)
let LABELS_OR_PREPROC = '^\s*\(\<\k\+\>:\s*$\|#.*\)'
if this_line =~? LABELS_OR_PREPROC
return 0
endif
+
+ " Get the current value of "shiftwidth"
+ let bShiftwidth = shiftwidth()
" Find a non-blank line above the current line.
" Skip over labels and preprocessor directives.
- let lnum = a:lnum
+ let lnum = this_lnum
while lnum > 0
let lnum = prevnonblank(lnum - 1)
let previous_line = getline(lnum)
@@ -45,34 +54,102 @@ fun! VbGetIndent(lnum)
return 0
endif
- let ind = indent(lnum)
+ " Variable "previous_line" now contains the text in buffer line "lnum".
+
+ " Multi-line statements have the underscore character at end-of-line:
+ "
+ " object.method(arguments, _
+ " arguments, _
+ " arguments)
+ "
+ " and require extra logic to determine the correct indentation.
+ "
+ " Case 1: Line "lnum" is the first line of a multiline statement.
+ " Line "lnum" will have a trailing underscore character
+ " but the preceding non-blank line does not.
+ " Line "this_lnum" will be indented relative to "lnum".
+ "
+ " Case 2: Line "lnum" is the last line of a multiline statement.
+ " Line "lnum" will not have a trailing underscore character
+ " but the preceding non-blank line will.
+ " Line "this_lnum" will have the same indentation as the starting
+ " line of the multiline statement.
+ "
+ " Case 3: Line "lnum" is neither the first nor last line.
+ " Lines "lnum" and "lnum-1" will have a trailing underscore
+ " character.
+ " Line "this_lnum" will have the same indentation as the preceding
+ " line.
+ "
+ " No matter which case it is, the starting line of the statement must be
+ " found. It will be assumed that multiline statements cannot have
+ " intermingled comments, statement labels, preprocessor directives or
+ " blank lines.
+ "
+ let lnum_is_continued = (previous_line =~ '_$')
+ if lnum > 1
+ let before_lnum = prevnonblank(lnum-1)
+ let before_previous_line = getline(before_lnum)
+ else
+ let before_lnum = 0
+ let before_previous_line = ""
+ endif
+
+ if before_previous_line !~ '_$'
+ " Variable "previous_line" contains the start of a statement.
+ "
+ let ind = indent(lnum)
+ if lnum_is_continued
+ let ind += bShiftwidth
+ endif
+ elseif ! lnum_is_continued
+ " Line "lnum" contains the last line of a multiline statement.
+ " Need to find where this multiline statement begins
+ "
+ while before_lnum > 0
+ let before_lnum -= 1
+ if getline(before_lnum) !~ '_$'
+ let before_lnum += 1
+ break
+ endif
+ endwhile
+ if before_lnum == 0
+ let before_lnum = 1
+ endif
+ let previous_line = getline(before_lnum)
+ let ind = indent(before_lnum)
+ else
+ " Line "lnum" is not the first or last line of a multiline statement.
+ "
+ let ind = indent(lnum)
+ endif
" Add
- if previous_line =~? '^\s*\<\(begin\|\%(\%(private\|public\|friend\)\s\+\)\=\%(function\|sub\|property\)\|select\|case\|default\|if\|else\|elseif\|do\|for\|while\|enum\|with\)\>'
- let ind = ind + shiftwidth()
+ if previous_line =~? '^\s*\<\(begin\|\%(\%(private\|public\|friend\)\s\+\)\=\%(function\|sub\|property\|enum\|type\)\|select\|case\|default\|if\|else\|elseif\|do\|for\|while\|with\)\>'
+ let ind = ind + bShiftwidth
endif
" Subtract
if this_line =~? '^\s*\<end\>\s\+\<select\>'
if previous_line !~? '^\s*\<select\>'
- let ind = ind - 2 * shiftwidth()
+ let ind = ind - 2 * bShiftwidth
else
" this case is for an empty 'select' -- 'end select'
" (w/o any case statements) like:
"
" select case readwrite
" end select
- let ind = ind - shiftwidth()
+ let ind = ind - bShiftwidth
endif
elseif this_line =~? '^\s*\<\(end\|else\|elseif\|until\|loop\|next\|wend\)\>'
- let ind = ind - shiftwidth()
+ let ind = ind - bShiftwidth
elseif this_line =~? '^\s*\<\(case\|default\)\>'
if previous_line !~? '^\s*\<select\>'
- let ind = ind - shiftwidth()
+ let ind = ind - bShiftwidth
endif
endif
return ind
-endfun
+endfunction
" vim:sw=4
diff --git a/runtime/indent/vim.vim b/runtime/indent/vim.vim
index 8076b2df07..3beb70d255 100644
--- a/runtime/indent/vim.vim
+++ b/runtime/indent/vim.vim
@@ -33,7 +33,9 @@ function GetVimIndent()
endtry
endfunc
-let s:lineContPat = '^\s*\(\\\|"\\ \)'
+" Legacy script line continuation and Vim9 script operators that must mean an
+" expression that continues from the previous line.
+let s:lineContPat = '^\s*\(\\\|"\\ \|->\)'
function GetVimIndentIntern()
" If the current line has line continuation and the previous one too, use
@@ -133,15 +135,15 @@ function GetVimIndentIntern()
endif
endif
- " For a line starting with "}" find the matching "{". If it is at the start
- " of the line align with it, probably end of a block.
+ " For a line starting with "}" find the matching "{". Align with that line,
+ " it is either the matching block start or dictionary start.
" Use the mapped "%" from matchit to find the match, otherwise we may match
" a { inside a comment or string.
if cur_text =~ '^\s*}'
if maparg('%') != ''
exe v:lnum
silent! normal %
- if line('.') < v:lnum && getline('.') =~ '^\s*{'
+ if line('.') < v:lnum
let ind = indent('.')
endif
else
@@ -149,19 +151,33 @@ function GetVimIndentIntern()
endif
endif
- " Below a line starting with "}" find the matching "{". If it is at the
- " end of the line we must be below the end of a dictionary.
- if prev_text =~ '^\s*}'
- if maparg('%') != ''
- exe lnum
- silent! normal %
- if line('.') == lnum || getline('.') !~ '^\s*{'
- let ind = ind - shiftwidth()
+ " Look back for a line to align with
+ while lnum > 1
+ " Below a line starting with "}" find the matching "{".
+ if prev_text =~ '^\s*}'
+ if maparg('%') != ''
+ exe lnum
+ silent! normal %
+ if line('.') < lnum
+ let lnum = line('.')
+ let ind = indent(lnum)
+ let prev_text = getline(lnum)
+ else
+ break
+ endif
+ else
+ " todo: use searchpair() to find a match
+ break
endif
+ elseif prev_text =~ s:lineContPat
+ " looks like a continuation like, go back one line
+ let lnum = lnum - 1
+ let ind = indent(lnum)
+ let prev_text = getline(lnum)
else
- " todo: use searchpair() to find a match
+ break
endif
- endif
+ endwhile
" Below a line starting with "]" we must be below the end of a list.
" Include a "}" and "},} in case a dictionary ends too.
diff --git a/runtime/indent/vue.vim b/runtime/indent/vue.vim
new file mode 100644
index 0000000000..f6fe350a8f
--- /dev/null
+++ b/runtime/indent/vue.vim
@@ -0,0 +1,14 @@
+" Vim indent file placeholder
+" Language: Vue
+" Maintainer: None, please volunteer if you have a real Vue indent script
+" Last Change: 2022 Dec 24
+
+" Only load this indent file when no other was loaded.
+if exists("b:did_indent")
+ finish
+endif
+" don't set b:did_indent, otherwise html indenting won't be activated
+" let b:did_indent = 1
+
+" Html comes closest
+runtime! indent/html.vim
diff --git a/runtime/indent/yaml.vim b/runtime/indent/yaml.vim
index d732c37c05..93fd8ea6f6 100644
--- a/runtime/indent/yaml.vim
+++ b/runtime/indent/yaml.vim
@@ -1,7 +1,7 @@
" Vim indent file
" Language: YAML
" Maintainer: Nikolai Pavlov <zyx.vim@gmail.com>
-" Last Update: Lukas Reineke
+" Last Updates: Lukas Reineke, "lacygoill"
" Last Change: 2022 Jun 17
" Only load this indent file when no other was loaded.
@@ -29,8 +29,8 @@ function s:FindPrevLessIndentedLine(lnum, ...)
let prevlnum = prevnonblank(a:lnum-1)
let curindent = a:0 ? a:1 : indent(a:lnum)
while prevlnum
- \&& indent(prevlnum) >= curindent
- \&& getline(prevlnum) !~# '^\s*#'
+ \ && indent(prevlnum) >= curindent
+ \ && getline(prevlnum) !~# '^\s*#'
let prevlnum = prevnonblank(prevlnum-1)
endwhile
return prevlnum
@@ -54,7 +54,7 @@ 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%(%\x\x|' .. 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\!'
@@ -63,7 +63,7 @@ let s:c_tag_handle = '\v%(' .. s:c_named_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%(%\x\x|' .. s:ns_word_char .. '\v|[#/;?:@&=+$,.!~*''()[\]])'
+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.
diff --git a/runtime/indent/zig.vim b/runtime/indent/zig.vim
new file mode 100644
index 0000000000..e3ce8aa410
--- /dev/null
+++ b/runtime/indent/zig.vim
@@ -0,0 +1,80 @@
+" Vim filetype indent file
+" Language: Zig
+" Upstream: https://github.com/ziglang/zig.vim
+
+" Only load this indent file when no other was loaded.
+if exists("b:did_indent")
+ finish
+endif
+let b:did_indent = 1
+
+if (!has("cindent") || !has("eval"))
+ finish
+endif
+
+setlocal cindent
+
+" L0 -> 0 indent for jump labels (i.e. case statement in c).
+" j1 -> indenting for "javascript object declarations"
+" J1 -> see j1
+" w1 -> starting a new line with `(` at the same indent as `(`
+" m1 -> if `)` starts a line, match its indent with the first char of its
+" matching `(` line
+" (s -> use one indent, when starting a new line after a trailing `(`
+setlocal cinoptions=L0,m1,(s,j1,J1,l1
+
+" cinkeys: controls what keys trigger indent formatting
+" 0{ -> {
+" 0} -> }
+" 0) -> )
+" 0] -> ]
+" !^F -> make CTRL-F (^F) reindent the current line when typed
+" o -> when <CR> or `o` is used
+" O -> when the `O` command is used
+setlocal cinkeys=0{,0},0),0],!^F,o,O
+
+setlocal indentexpr=GetZigIndent(v:lnum)
+
+let b:undo_indent = "setlocal cindent< cinkeys< cinoptions< indentexpr<"
+
+function! GetZigIndent(lnum)
+ let curretLineNum = a:lnum
+ let currentLine = getline(a:lnum)
+
+ " cindent doesn't handle multi-line strings properly, so force no indent
+ if currentLine =~ '^\s*\\\\.*'
+ return -1
+ endif
+
+ let prevLineNum = prevnonblank(a:lnum-1)
+ let prevLine = getline(prevLineNum)
+
+ " for lines that look like
+ " },
+ " };
+ " try treating them the same as a }
+ if prevLine =~ '\v^\s*},$'
+ if currentLine =~ '\v^\s*};$' || currentLine =~ '\v^\s*}$'
+ return indent(prevLineNum) - 4
+ endif
+ return indent(prevLineNum-1) - 4
+ endif
+ if currentLine =~ '\v^\s*},$'
+ return indent(prevLineNum) - 4
+ endif
+ if currentLine =~ '\v^\s*};$'
+ return indent(prevLineNum) - 4
+ endif
+
+
+ " cindent doesn't handle this case correctly:
+ " switch (1): {
+ " 1 => true,
+ " ~
+ " ^---- indents to here
+ if prevLine =~ '.*=>.*,$' && currentLine !~ '.*}$'
+ return indent(prevLineNum)
+ endif
+
+ return cindent(a:lnum)
+endfunction
diff --git a/runtime/lua/_vim9script.lua b/runtime/lua/_vim9script.lua
new file mode 100644
index 0000000000..363d061451
--- /dev/null
+++ b/runtime/lua/_vim9script.lua
@@ -0,0 +1,650 @@
+-------------------------------------------------------------------------------
+-- This file is auto generated by vim9jit. Do not edit by hand.
+-- All content is in the source repository.
+-- Bugs should be reported to: github.com/tjdevries/vim9jit
+--
+-- In addition, this file is considered "private" by neovim. You should
+-- not expect any of the APIs, functions, etc to be stable. They are subject
+-- to change at any time.
+-------------------------------------------------------------------------------
+
+local vim9 = (function()
+ local M = {}
+
+ M.ternary = function(cond, if_true, if_false)
+ if cond then
+ if type(if_true) == 'function' then
+ return if_true()
+ else
+ return if_true
+ end
+ else
+ if type(if_false) == 'function' then
+ return if_false()
+ else
+ return if_false
+ end
+ end
+ end
+
+ M.fn_ref = function(module, name, copied, ...)
+ for _, val in ipairs({ ... }) do
+ table.insert(copied, val)
+ end
+
+ local funcref = name
+ if type(funcref) == 'function' then
+ return funcref(unpack(copied))
+ elseif type(funcref) == 'string' then
+ if vim.fn.exists('*' .. funcref) == 1 then
+ return vim.fn[funcref](unpack(copied))
+ end
+
+ if module[funcref] then
+ module[funcref](unpack(copied))
+ end
+
+ error('unknown function: ' .. funcref)
+ else
+ error(string.format('unable to call funcref: %s', funcref))
+ end
+ end
+
+ M.fn_mut = function(name, args, info)
+ local result = vim.fn._Vim9ScriptFn(name, args)
+ for idx, val in pairs(result[2]) do
+ M.replace(args[idx], val)
+ end
+
+ -- Substitute returning the reference to the
+ -- returned value
+ if info.replace then
+ return args[info.replace + 1]
+ end
+
+ return result[1]
+ end
+
+ M.replace = function(orig, new)
+ if type(orig) == 'table' and type(new) == 'table' then
+ for k in pairs(orig) do
+ orig[k] = nil
+ end
+
+ for k, v in pairs(new) do
+ orig[k] = v
+ end
+
+ return orig
+ end
+
+ return new
+ end
+
+ M.index = function(obj, idx)
+ if vim.tbl_islist(obj) then
+ if idx < 0 then
+ return obj[#obj + idx + 1]
+ else
+ return obj[idx + 1]
+ end
+ elseif type(obj) == 'table' then
+ return obj[idx]
+ elseif type(obj) == 'string' then
+ return string.sub(obj, idx + 1, idx + 1)
+ end
+
+ error('invalid type for indexing: ' .. vim.inspect(obj))
+ end
+
+ M.index_expr = function(idx)
+ if type(idx) == 'string' then
+ return idx
+ elseif type(idx) == 'number' then
+ return idx + 1
+ else
+ error(string.format('not yet handled: %s', vim.inspect(idx)))
+ end
+ end
+
+ M.slice = function(obj, start, finish)
+ if start == nil then
+ start = 0
+ end
+
+ if start < 0 then
+ start = #obj + start
+ end
+ assert(type(start) == 'number')
+
+ if finish == nil then
+ finish = #obj
+ end
+
+ if finish < 0 then
+ finish = #obj + finish
+ end
+ assert(type(finish) == 'number')
+
+ local slicer
+ if vim.tbl_islist(obj) then
+ slicer = vim.list_slice
+ elseif type(obj) == 'string' then
+ slicer = string.sub
+ else
+ error('invalid type for slicing: ' .. vim.inspect(obj))
+ end
+
+ return slicer(obj, start + 1, finish + 1)
+ end
+
+ -- Currently unused, but this could be used to embed vim9jit within a
+ -- running nvim application and transpile "on the fly" as files are
+ -- sourced. There would still need to be some work done to make that
+ -- work correctly with imports and what not, but overall it could
+ -- work well for calling ":source X" from within a vimscript/vim9script
+ -- function
+ M.make_source_cmd = function()
+ local group = vim.api.nvim_create_augroup('vim9script-source', {})
+ vim.api.nvim_create_autocmd('SourceCmd', {
+ pattern = '*.vim',
+ group = group,
+ callback = function(a)
+ local file = vim.fn.readfile(a.file)
+ for _, line in ipairs(file) do
+ -- TODO: Or starts with def <something>
+ -- You can use def in legacy vim files
+ if vim.startswith(line, 'vim9script') then
+ -- TODO: Use the rust lib to actually
+ -- generate the corresponding lua code and then
+ -- execute that (instead of sourcing it directly)
+ return
+ end
+ end
+
+ vim.api.nvim_exec(table.concat(file, '\n'), false)
+ end,
+ })
+ end
+
+ M.iter = function(expr)
+ if vim.tbl_islist(expr) then
+ return ipairs(expr)
+ else
+ return pairs(expr)
+ end
+ end
+
+ M.ITER_DEFAULT = 0
+ M.ITER_CONTINUE = 1
+ M.ITER_BREAK = 2
+ M.ITER_RETURN = 3
+
+ return M
+end)()
+
+vim.cmd([[
+function! _Vim9ScriptFn(name, args) abort
+ try
+ let ret = function(a:name, a:args)()
+ catch
+ echo "Failed..."
+ echo a:name
+ echo a:args
+
+ throw v:errmsg
+ endtry
+
+ return [ret, a:args]
+endfunction
+]])
+
+vim9['autoload'] = (function()
+ return function(path)
+ return loadfile(path)()
+ end
+end)()
+vim9['bool'] = (function()
+ return function(...)
+ return vim9.convert.to_vim_bool(...)
+ end
+end)()
+vim9['convert'] = (function()
+ local M = {}
+
+ M.decl_bool = function(val)
+ if type(val) == 'boolean' then
+ return val
+ elseif type(val) == 'number' then
+ if val == 0 then
+ return false
+ elseif val == 1 then
+ return true
+ else
+ error(string.format('bad number passed to bool declaration: %s', val))
+ end
+ end
+
+ error(string.format('invalid bool declaration: %s', vim.inspect(val)))
+ end
+
+ M.decl_dict = function(val)
+ if type(val) == 'nil' then
+ return vim.empty_dict()
+ elseif type(val) == 'table' then
+ if vim.tbl_isempty(val) then
+ return vim.empty_dict()
+ elseif vim.tbl_islist(val) then
+ error(string.format('Cannot pass list to dictionary? %s', vim.inspect(val)))
+ else
+ return val
+ end
+ end
+
+ error(string.format('invalid dict declaration: %s', vim.inspect(val)))
+ end
+
+ M.to_vim_bool = function(val)
+ if type(val) == 'boolean' then
+ return val
+ elseif type(val) == 'number' then
+ return val ~= 0
+ elseif type(val) == 'string' then
+ return string.len(val) ~= 0
+ elseif type(val) == 'table' then
+ return not vim.tbl_isempty(val)
+ elseif val == nil then
+ return false
+ end
+
+ error('unhandled type: ' .. vim.inspect(val))
+ end
+
+ return M
+end)()
+vim9['fn'] = (function()
+ local M = {}
+
+ M.insert = function(list, item, idx)
+ if idx == nil then
+ idx = 1
+ end
+
+ table.insert(list, idx + 1, item)
+
+ return list
+ end
+
+ M.extend = function(left, right, expr3)
+ if expr3 ~= nil then
+ error("haven't written this code yet")
+ end
+
+ if vim.tbl_islist(right) then
+ vim.list_extend(left, right)
+ return left
+ else
+ -- local result = vim.tbl_extend(left, right)
+ for k, v in pairs(right) do
+ left[k] = v
+ end
+
+ return left
+ end
+ end
+
+ M.add = function(list, item)
+ table.insert(list, item)
+ return list
+ end
+
+ M.has_key = function(obj, key)
+ return not not obj[key]
+ end
+
+ M.prop_type_add = function(...)
+ local args = { ... }
+ print('[prop_type_add]', vim.inspect(args))
+ end
+
+ do
+ local has_overrides = {
+ -- We do have vim9script ;) that's this plugin
+ ['vim9script'] = true,
+
+ -- Include some vim patches that are sometimes required by variuos vim9script plugins
+ -- that we implement via vim9jit
+ [ [[patch-8.2.2261]] ] = true,
+ [ [[patch-8.2.4257]] ] = true,
+ }
+
+ M.has = function(patch)
+ if has_overrides[patch] then
+ return true
+ end
+
+ return vim.fn.has(patch)
+ end
+ end
+
+ --[=[
+Currently missing patch, can be removed in the future.
+
+readdirex({directory} [, {expr} [, {dict}]]) *readdirex()*
+ Extended version of |readdir()|.
+ Return a list of Dictionaries with file and directory
+ information in {directory}.
+ This is useful if you want to get the attributes of file and
+ directory at the same time as getting a list of a directory.
+ This is much faster than calling |readdir()| then calling
+ |getfperm()|, |getfsize()|, |getftime()| and |getftype()| for
+ each file and directory especially on MS-Windows.
+ The list will by default be sorted by name (case sensitive),
+ the sorting can be changed by using the optional {dict}
+ argument, see |readdir()|.
+
+ The Dictionary for file and directory information has the
+ following items:
+ group Group name of the entry. (Only on Unix)
+ name Name of the entry.
+ perm Permissions of the entry. See |getfperm()|.
+ size Size of the entry. See |getfsize()|.
+ time Timestamp of the entry. See |getftime()|.
+ type Type of the entry.
+ On Unix, almost same as |getftype()| except:
+ Symlink to a dir "linkd"
+ Other symlink "link"
+ On MS-Windows:
+ Normal file "file"
+ Directory "dir"
+ Junction "junction"
+ Symlink to a dir "linkd"
+ Other symlink "link"
+ Other reparse point "reparse"
+ user User name of the entry's owner. (Only on Unix)
+ On Unix, if the entry is a symlink, the Dictionary includes
+ the information of the target (except the "type" item).
+ On MS-Windows, it includes the information of the symlink
+ itself because of performance reasons.
+--]=]
+ M.readdirex = function(dir)
+ local files = vim.fn.readdir(dir)
+ local direx = {}
+ for _, f in ipairs(files) do
+ table.insert(direx, {
+ name = f,
+ type = vim.fn.getftype(f),
+ })
+ end
+
+ return direx
+ end
+
+ M.mapnew = function(tbl, expr)
+ return vim.fn.map(tbl, expr)
+ end
+
+ M.typename = function(val)
+ local ty = type(val)
+ if ty == 'string' then
+ return 'string'
+ elseif ty == 'boolean' then
+ return 'bool'
+ elseif ty == 'number' then
+ return 'number'
+ else
+ error(string.format('typename: %s', val))
+ end
+ end
+
+ -- Popup menu stuff: Could be rolled into other plugin later
+ -- but currently is here for testing purposes (and implements
+ -- some very simple compat layers at the moment)
+ do
+ local pos_map = {
+ topleft = 'NW',
+ topright = 'NE',
+ botleft = 'SW',
+ botright = 'SE',
+ }
+
+ M.popup_menu = function(_, options)
+ -- print "OPTIONS:"
+
+ local buf = vim.api.nvim_create_buf(false, true)
+ local win = vim.api.nvim_open_win(buf, true, {
+ relative = 'editor',
+ style = 'minimal',
+ anchor = pos_map[options.pos],
+ height = options.maxheight or options.minheight,
+ width = options.maxwidth or options.minwidth,
+ row = options.line,
+ col = options.col,
+ })
+
+ if options.filter then
+ local loop
+ loop = function()
+ vim.cmd([[redraw!]])
+ local ok, ch = pcall(vim.fn.getcharstr)
+ if not ok then
+ return
+ end -- interrupted
+
+ if ch == '<C-C>' then
+ return
+ end
+
+ if not require('vim9script').bool(options.filter(nil, ch)) then
+ vim.cmd.normal(ch)
+ end
+
+ vim.schedule(loop)
+ end
+
+ vim.schedule(loop)
+ end
+
+ return win
+ end
+
+ M.popup_settext = function(id, text)
+ if type(text) == 'string' then
+ -- text = vim.split(text, "\n")
+ error("Haven't handled string yet")
+ end
+
+ local lines = {}
+ for _, obj in ipairs(text) do
+ table.insert(lines, obj.text)
+ end
+
+ vim.api.nvim_buf_set_lines(vim.api.nvim_win_get_buf(id), 0, -1, false, lines)
+ end
+
+ M.popup_filter_menu = function()
+ print('ok, just pretend we filtered the menu')
+ end
+
+ M.popup_setoptions = function(id, _)
+ print('setting options...', id)
+ end
+ end
+
+ M = setmetatable(M, {
+ __index = vim.fn,
+ })
+
+ return M
+end)()
+vim9['heredoc'] = (function()
+ local M = {}
+
+ M.trim = function(lines)
+ local min_whitespace = 9999
+ for _, line in ipairs(lines) do
+ local _, finish = string.find(line, '^%s*')
+ min_whitespace = math.min(min_whitespace, finish)
+ end
+
+ local trimmed_lines = {}
+ for _, line in ipairs(lines) do
+ table.insert(trimmed_lines, string.sub(line, min_whitespace + 1))
+ end
+
+ return trimmed_lines
+ end
+
+ return M
+end)()
+vim9['import'] = (function()
+ local imported = {}
+ imported.autoload = setmetatable({}, {
+ __index = function(_, name)
+ local luaname = 'autoload/' .. string.gsub(name, '%.vim$', '.lua')
+ local runtime_file = vim.api.nvim_get_runtime_file(luaname, false)[1]
+ if not runtime_file then
+ error('unable to find autoload file:' .. name)
+ end
+
+ return imported.absolute[vim.fn.fnamemodify(runtime_file, ':p')]
+ end,
+ })
+
+ imported.absolute = setmetatable({}, {
+ __index = function(self, name)
+ if vim.loop.fs_stat(name) then
+ local result = loadfile(name)()
+ rawset(self, name, result)
+
+ return result
+ end
+
+ error(string.format('unabled to find absolute file: %s', name))
+ end,
+ })
+
+ return function(info)
+ local name = info.name
+
+ if info.autoload then
+ return imported.autoload[info.name]
+ end
+
+ local debug_info = debug.getinfo(2, 'S')
+ local sourcing_path = vim.fn.fnamemodify(string.sub(debug_info.source, 2), ':p')
+
+ -- Relative paths
+ if vim.startswith(name, '../') or vim.startswith(name, './') then
+ local luaname = string.gsub(name, '%.vim$', '.lua')
+ local directory = vim.fn.fnamemodify(sourcing_path, ':h')
+ local search = directory .. '/' .. luaname
+ return imported.absolute[search]
+ end
+
+ if vim.startswith(name, '/') then
+ error('absolute path')
+ -- local luaname = string.gsub(name, "%.vim", ".lua")
+ -- local runtime_file = vim.api.nvim_get_runtime_file(luaname, false)[1]
+ -- if runtime_file then
+ -- runtime_file = vim.fn.fnamemodify(runtime_file, ":p")
+ -- return loadfile(runtime_file)()
+ -- end
+ end
+
+ error('Unhandled case' .. vim.inspect(info) .. vim.inspect(debug_info))
+ end
+end)()
+vim9['ops'] = (function()
+ local lib = vim9
+
+ local M = {}
+
+ M['And'] = function(left, right)
+ return lib.bool(left) and lib.bool(right)
+ end
+
+ M['Or'] = function(left, right)
+ return lib.bool(left) or lib.bool(right)
+ end
+
+ M['Plus'] = function(left, right)
+ return left + right
+ end
+
+ M['Multiply'] = function(left, right)
+ return left * right
+ end
+
+ M['Divide'] = function(left, right)
+ return left / right
+ end
+
+ M['StringConcat'] = function(left, right)
+ return left .. right
+ end
+
+ M['EqualTo'] = function(left, right)
+ return left == right
+ end
+
+ M['NotEqualTo'] = function(left, right)
+ return not M['EqualTo'](left, right)
+ end
+
+ M['LessThan'] = function(left, right)
+ return left < right
+ end
+
+ M['LessThanOrEqual'] = function(left, right)
+ return left <= right
+ end
+
+ M['GreaterThan'] = function(left, right)
+ return left > right
+ end
+
+ M['GreaterThanOrEqual'] = function(left, right)
+ return left >= right
+ end
+
+ M['RegexpMatches'] = function(left, right)
+ return not not vim.regex(right):match_str(left)
+ end
+
+ M['RegexpMatchesIns'] = function(left, right)
+ return not not vim.regex('\\c' .. right):match_str(left)
+ end
+
+ M['NotRegexpMatches'] = function(left, right)
+ return not M['RegexpMatches'](left, right)
+ end
+
+ M['Modulo'] = function(left, right)
+ return left % right
+ end
+
+ M['Minus'] = function(left, right)
+ -- TODO: This is not right :)
+ return left - right
+ end
+
+ return M
+end)()
+vim9['prefix'] = (function()
+ local lib = vim9
+
+ local M = {}
+
+ M['Minus'] = function(right)
+ return -right
+ end
+
+ M['Bang'] = function(right)
+ return not lib.bool(right)
+ end
+
+ return M
+end)()
+
+return vim9
diff --git a/runtime/lua/editorconfig.lua b/runtime/lua/editorconfig.lua
new file mode 100644
index 0000000000..2079006234
--- /dev/null
+++ b/runtime/lua/editorconfig.lua
@@ -0,0 +1,246 @@
+local M = {}
+
+M.properties = {}
+
+--- Modified version of the builtin assert that does not include error position information
+---
+---@param v any Condition
+---@param message string Error message to display if condition is false or nil
+---@return any v if not false or nil, otherwise an error is displayed
+---
+---@private
+local function assert(v, message)
+ return v or error(message, 0)
+end
+
+--- Show a warning message
+---
+---@param msg string Message to show
+---
+---@private
+local function warn(msg, ...)
+ vim.notify(string.format(msg, ...), vim.log.levels.WARN, {
+ title = 'editorconfig',
+ })
+end
+
+function M.properties.charset(bufnr, val)
+ assert(
+ vim.tbl_contains({ 'utf-8', 'utf-8-bom', 'latin1', 'utf-16be', 'utf-16le' }, val),
+ 'charset must be one of "utf-8", "utf-8-bom", "latin1", "utf-16be", or "utf-16le"'
+ )
+ if val == 'utf-8' or val == 'utf-8-bom' then
+ vim.bo[bufnr].fileencoding = 'utf-8'
+ vim.bo[bufnr].bomb = val == 'utf-8-bom'
+ elseif val == 'utf-16be' then
+ vim.bo[bufnr].fileencoding = 'utf-16'
+ else
+ vim.bo[bufnr].fileencoding = val
+ end
+end
+
+function M.properties.end_of_line(bufnr, val)
+ vim.bo[bufnr].fileformat = assert(
+ ({ lf = 'unix', crlf = 'dos', cr = 'mac' })[val],
+ 'end_of_line must be one of "lf", "crlf", or "cr"'
+ )
+end
+
+function M.properties.indent_style(bufnr, val, opts)
+ assert(val == 'tab' or val == 'space', 'indent_style must be either "tab" or "space"')
+ vim.bo[bufnr].expandtab = val == 'space'
+ if val == 'tab' and not opts.indent_size then
+ vim.bo[bufnr].shiftwidth = 0
+ vim.bo[bufnr].softtabstop = 0
+ end
+end
+
+function M.properties.indent_size(bufnr, val, opts)
+ if val == 'tab' then
+ vim.bo[bufnr].shiftwidth = 0
+ vim.bo[bufnr].softtabstop = 0
+ else
+ local n = assert(tonumber(val), 'indent_size must be a number')
+ vim.bo[bufnr].shiftwidth = n
+ vim.bo[bufnr].softtabstop = -1
+ if not opts.tab_width then
+ vim.bo[bufnr].tabstop = n
+ end
+ end
+end
+
+function M.properties.tab_width(bufnr, val)
+ vim.bo[bufnr].tabstop = assert(tonumber(val), 'tab_width must be a number')
+end
+
+function M.properties.max_line_length(bufnr, val)
+ local n = tonumber(val)
+ if n then
+ vim.bo[bufnr].textwidth = n
+ else
+ assert(val == 'off', 'max_line_length must be a number or "off"')
+ vim.bo[bufnr].textwidth = 0
+ end
+end
+
+function M.properties.trim_trailing_whitespace(bufnr, val)
+ assert(
+ val == 'true' or val == 'false',
+ 'trim_trailing_whitespace must be either "true" or "false"'
+ )
+ if val == 'true' then
+ vim.api.nvim_create_autocmd('BufWritePre', {
+ group = 'editorconfig',
+ buffer = bufnr,
+ callback = function()
+ local view = vim.fn.winsaveview()
+ vim.api.nvim_command('silent! undojoin')
+ vim.api.nvim_command('silent keepjumps keeppatterns %s/\\s\\+$//e')
+ vim.fn.winrestview(view)
+ end,
+ })
+ else
+ vim.api.nvim_clear_autocmds({
+ event = 'BufWritePre',
+ group = 'editorconfig',
+ buffer = bufnr,
+ })
+ end
+end
+
+function M.properties.insert_final_newline(bufnr, val)
+ assert(val == 'true' or val == 'false', 'insert_final_newline must be either "true" or "false"')
+ vim.bo[bufnr].fixendofline = val == 'true'
+ vim.bo[bufnr].endofline = val == 'true'
+end
+
+--- Modified version of |glob2regpat()| that does not match path separators on *.
+---
+--- This function replaces single instances of * with the regex pattern [^/]*. However, the star in
+--- the replacement pattern also gets interpreted by glob2regpat, so we insert a placeholder, pass
+--- it through glob2regpat, then replace the placeholder with the actual regex pattern.
+---
+---@param glob string Glob to convert into a regular expression
+---@return string Regular expression
+---
+---@private
+local function glob2regpat(glob)
+ local placeholder = '@@PLACEHOLDER@@'
+ return (
+ string.gsub(
+ vim.fn.glob2regpat(
+ vim.fn.substitute(
+ string.gsub(glob, '{(%d+)%.%.(%d+)}', '[%1-%2]'),
+ '\\*\\@<!\\*\\*\\@!',
+ placeholder,
+ 'g'
+ )
+ ),
+ placeholder,
+ '[^/]*'
+ )
+ )
+end
+
+--- Parse a single line in an EditorConfig file
+---
+---@param line string Line
+---@return string|nil If the line contains a pattern, the glob pattern
+---@return string|nil If the line contains a key-value pair, the key
+---@return string|nil If the line contains a key-value pair, the value
+---
+---@private
+local function parse_line(line)
+ if line:find('^%s*[^ #;]') then
+ local glob = (line:match('%b[]') or ''):match('^%s*%[(.*)%]%s*$')
+ if glob then
+ return glob, nil, nil
+ end
+
+ local key, val = line:match('^%s*([^:= ][^:=]-)%s*[:=]%s*(.-)%s*$')
+ if key ~= nil and val ~= nil then
+ return nil, key:lower(), val:lower()
+ end
+ end
+end
+
+--- Parse options from an .editorconfig file
+---
+---@param filepath string File path of the file to apply EditorConfig settings to
+---@param dir string Current directory
+---@return table Table of options to apply to the given file
+---
+---@private
+local function parse(filepath, dir)
+ local pat = nil
+ local opts = {}
+ local f = io.open(dir .. '/.editorconfig')
+ if f then
+ for line in f:lines() do
+ local glob, key, val = parse_line(line)
+ if glob then
+ glob = glob:find('/') and (dir .. '/' .. glob:gsub('^/', '')) or ('**/' .. glob)
+ local ok, regpat = pcall(glob2regpat, glob)
+ if ok then
+ pat = vim.regex(regpat)
+ else
+ pat = nil
+ warn('editorconfig: Error occurred while parsing glob pattern "%s": %s', glob, regpat)
+ end
+ elseif key ~= nil and val ~= nil then
+ if key == 'root' then
+ opts.root = val == 'true'
+ elseif pat and pat:match_str(filepath) then
+ opts[key] = val
+ end
+ end
+ end
+ f:close()
+ end
+ return opts
+end
+
+--- Configure the given buffer with options from an .editorconfig file
+---
+---@param bufnr number Buffer number to configure
+---
+---@private
+function M.config(bufnr)
+ bufnr = bufnr or vim.api.nvim_get_current_buf()
+ local path = vim.fs.normalize(vim.api.nvim_buf_get_name(bufnr))
+ if vim.bo[bufnr].buftype ~= '' or not vim.bo[bufnr].modifiable or path == '' then
+ return
+ end
+
+ local opts = {}
+ for parent in vim.fs.parents(path) do
+ for k, v in pairs(parse(path, parent)) do
+ if opts[k] == nil then
+ opts[k] = v
+ end
+ end
+
+ if opts.root then
+ break
+ end
+ end
+
+ local applied = {}
+ for opt, val in pairs(opts) do
+ if val ~= 'unset' then
+ local func = M.properties[opt]
+ if func then
+ local ok, err = pcall(func, bufnr, val, opts)
+ if ok then
+ applied[opt] = val
+ else
+ warn('editorconfig: invalid value for option %s: %s. %s', opt, val, err)
+ end
+ end
+ end
+ end
+
+ vim.b[bufnr].editorconfig = applied
+end
+
+return M
diff --git a/runtime/lua/man.lua b/runtime/lua/man.lua
index 5da3d2a92f..732a4ab92e 100644
--- a/runtime/lua/man.lua
+++ b/runtime/lua/man.lua
@@ -1,7 +1,89 @@
-require('vim.compat')
+local api, fn = vim.api, vim.fn
+local FIND_ARG = '-w'
+local localfile_arg = true -- Always use -l if possible. #6683
local buf_hls = {}
+local M = {}
+
+local function man_error(msg)
+ M.errormsg = 'man.lua: ' .. vim.inspect(msg)
+ error(M.errormsg)
+end
+
+-- Run a system command and timeout after 30 seconds.
+local function system(cmd_, silent, env)
+ local stdout_data = {}
+ local stderr_data = {}
+ local stdout = vim.loop.new_pipe(false)
+ local stderr = vim.loop.new_pipe(false)
+
+ local done = false
+ local exit_code
+
+ -- We use the `env` command here rather than the env option to vim.loop.spawn since spawn will
+ -- completely overwrite the environment when we just want to modify the existing one.
+ --
+ -- Overwriting mainly causes problems NixOS which relies heavily on a non-standard environment.
+ local cmd
+ if env then
+ cmd = { 'env' }
+ vim.list_extend(cmd, env)
+ vim.list_extend(cmd, cmd_)
+ else
+ cmd = cmd_
+ end
+
+ local handle
+ handle = vim.loop.spawn(cmd[1], {
+ args = vim.list_slice(cmd, 2),
+ stdio = { nil, stdout, stderr },
+ }, function(code)
+ exit_code = code
+ stdout:close()
+ stderr:close()
+ handle:close()
+ done = true
+ end)
+
+ if handle then
+ stdout:read_start(function(_, data)
+ stdout_data[#stdout_data + 1] = data
+ end)
+ stderr:read_start(function(_, data)
+ stderr_data[#stderr_data + 1] = data
+ end)
+ else
+ stdout:close()
+ stderr:close()
+ if not silent then
+ local cmd_str = table.concat(cmd, ' ')
+ man_error(string.format('command error: %s', cmd_str))
+ end
+ end
+
+ vim.wait(30000, function()
+ return done
+ end)
+
+ if not done then
+ if handle then
+ handle:close()
+ stdout:close()
+ stderr:close()
+ end
+ local cmd_str = table.concat(cmd, ' ')
+ man_error(string.format('command timed out: %s', cmd_str))
+ end
+
+ if exit_code ~= 0 and not silent then
+ local cmd_str = table.concat(cmd, ' ')
+ man_error(string.format("command error '%s': %s", cmd_str, table.concat(stderr_data)))
+ end
+
+ return table.concat(stdout_data)
+end
+
local function highlight_line(line, linenr)
local chars = {}
local prev_char = ''
@@ -152,21 +234,549 @@ 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.bo.modifiable
+ vim.bo.modifiable = true
- local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
+ local lines = api.nvim_buf_get_lines(0, 0, -1, false)
for i, line in ipairs(lines) do
lines[i] = highlight_line(line, i)
end
- vim.api.nvim_buf_set_lines(0, 0, -1, false, lines)
+ api.nvim_buf_set_lines(0, 0, -1, false, lines)
for _, args in ipairs(buf_hls) do
- vim.api.nvim_buf_add_highlight(unpack(args))
+ api.nvim_buf_add_highlight(unpack(args))
end
buf_hls = {}
- vim.api.nvim_buf_set_option(0, 'modifiable', mod)
+ vim.bo.modifiable = mod
+end
+
+-- replace spaces in a man page name with underscores
+-- intended for PostgreSQL, which has man pages like 'CREATE_TABLE(7)';
+-- while editing SQL source code, it's nice to visually select 'CREATE TABLE'
+-- and hit 'K', which requires this transformation
+local function spaces_to_underscores(str)
+ local res = str:gsub('%s', '_')
+ return res
end
-return { highlight_man_page = highlight_man_page }
+local function get_path(sect, name, silent)
+ name = name or ''
+ sect = sect or ''
+ -- Some man implementations (OpenBSD) return all available paths from the
+ -- search command. Previously, this function would simply select the first one.
+ --
+ -- However, some searches will report matches that are incorrect:
+ -- man -w strlen may return string.3 followed by strlen.3, and therefore
+ -- selecting the first would get us the wrong page. Thus, we must find the
+ -- first matching one.
+ --
+ -- There's yet another special case here. Consider the following:
+ -- If you run man -w strlen and string.3 comes up first, this is a problem. We
+ -- should search for a matching named one in the results list.
+ -- However, if you search for man -w clock_gettime, you will *only* get
+ -- clock_getres.2, which is the right page. Searching the resuls for
+ -- clock_gettime will no longer work. In this case, we should just use the
+ -- first one that was found in the correct section.
+ --
+ -- Finally, we can avoid relying on -S or -s here since they are very
+ -- inconsistently supported. Instead, call -w with a section and a name.
+ local cmd
+ if sect == '' then
+ cmd = { 'man', FIND_ARG, name }
+ else
+ cmd = { 'man', FIND_ARG, sect, name }
+ end
+
+ local lines = system(cmd, silent)
+ local results = vim.split(lines or {}, '\n', { trimempty = true })
+
+ if #results == 0 then
+ return
+ end
+
+ -- `man -w /some/path` will return `/some/path` for any existent file, which
+ -- stops us from actually determining if a path has a corresponding man file.
+ -- Since `:Man /some/path/to/man/file` isn't supported anyway, we should just
+ -- error out here if we detect this is the case.
+ if sect == '' and #results == 1 and results[1] == name then
+ return
+ end
+
+ -- find any that match the specified name
+ local namematches = vim.tbl_filter(function(v)
+ return fn.fnamemodify(v, ':t'):match(name)
+ end, results) or {}
+ local sectmatches = {}
+
+ if #namematches > 0 and sect ~= '' then
+ sectmatches = vim.tbl_filter(function(v)
+ return fn.fnamemodify(v, ':e') == sect
+ end, namematches)
+ end
+
+ return fn.substitute(sectmatches[1] or namematches[1] or results[1], [[\n\+$]], '', '')
+end
+
+local function matchstr(text, pat_or_re)
+ local re = type(pat_or_re) == 'string' and vim.regex(pat_or_re) or pat_or_re
+
+ local s, e = re:match_str(text)
+
+ if s == nil then
+ return
+ end
+
+ return text:sub(vim.str_utfindex(text, s) + 1, vim.str_utfindex(text, e))
+end
+
+-- attempt to extract the name and sect out of 'name(sect)'
+-- otherwise just return the largest string of valid characters in ref
+local function extract_sect_and_name_ref(ref)
+ ref = ref or ''
+ if ref:sub(1, 1) == '-' then -- try ':Man -pandoc' with this disabled.
+ man_error("manpage name cannot start with '-'")
+ end
+ local ref1 = ref:match('[^()]+%([^()]+%)')
+ if not ref1 then
+ local name = ref:match('[^()]+')
+ if not name then
+ man_error('manpage reference cannot contain only parentheses: ' .. ref)
+ end
+ return '', spaces_to_underscores(name)
+ end
+ local parts = vim.split(ref1, '(', { plain = true })
+ -- see ':Man 3X curses' on why tolower.
+ -- TODO(nhooyr) Not sure if this is portable across OSs
+ -- but I have not seen a single uppercase section.
+ local sect = vim.split(parts[2] or '', ')', { plain = true })[1]:lower()
+ local name = spaces_to_underscores(parts[1])
+ return sect, name
+end
+
+-- verify_exists attempts to find the path to a manpage
+-- based on the passed section and name.
+--
+-- 1. If manpage could not be found with the given sect and name,
+-- then try all the sections in b:man_default_sects.
+-- 2. If it still could not be found, then we try again without a section.
+-- 3. If still not found but $MANSECT is set, then we try again with $MANSECT
+-- unset.
+local function verify_exists(sect, name, silent)
+ if sect and sect ~= '' then
+ local ret = get_path(sect, name, true)
+ if ret then
+ return ret
+ end
+ end
+
+ if vim.b.man_default_sects ~= nil then
+ local sects = vim.split(vim.b.man_default_sects, ',', { plain = true, trimempty = true })
+ for _, sec in ipairs(sects) do
+ local ret = get_path(sec, name, true)
+ if ret then
+ return ret
+ end
+ end
+ end
+
+ -- if none of the above worked, we will try with no section
+ local res_empty_sect = get_path('', name, true)
+ if res_empty_sect then
+ return res_empty_sect
+ end
+
+ -- if that still didn't work, we will check for $MANSECT and try again with it
+ -- unset
+ if vim.env.MANSECT then
+ local mansect = vim.env.MANSECT
+ vim.env.MANSECT = nil
+ local res = get_path('', name, true)
+ vim.env.MANSECT = mansect
+ if res then
+ return res
+ end
+ end
+
+ if not silent then
+ -- finally, if that didn't work, there is no hope
+ man_error('no manual entry for ' .. name)
+ end
+end
+
+local EXT_RE = vim.regex([[\.\%([glx]z\|bz2\|lzma\|Z\)$]])
+
+-- Extracts the name/section from the 'path/name.sect', because sometimes the actual section is
+-- more specific than what we provided to `man` (try `:Man 3 App::CLI`).
+-- Also on linux, name seems to be case-insensitive. So for `:Man PRIntf`, we
+-- still want the name of the buffer to be 'printf'.
+local function extract_sect_and_name_path(path)
+ local tail = fn.fnamemodify(path, ':t')
+ if EXT_RE:match_str(path) then -- valid extensions
+ tail = fn.fnamemodify(tail, ':r')
+ end
+ local name, sect = tail:match('^(.+)%.([^.]+)$')
+ return sect, name
+end
+
+local function find_man()
+ if vim.bo.filetype == 'man' then
+ return true
+ end
+
+ local win = 1
+ while win <= fn.winnr('$') do
+ local buf = fn.winbufnr(win)
+ if vim.bo[buf].filetype == 'man' then
+ vim.cmd(win .. 'wincmd w')
+ return true
+ end
+ win = win + 1
+ end
+ return false
+end
+
+local function set_options(pager)
+ vim.bo.swapfile = false
+ vim.bo.buftype = 'nofile'
+ vim.bo.bufhidden = 'hide'
+ vim.bo.modified = false
+ vim.bo.readonly = true
+ vim.bo.modifiable = false
+ vim.b.pager = pager
+ vim.bo.filetype = 'man'
+end
+
+local function get_page(path, silent)
+ -- Disable hard-wrap by using a big $MANWIDTH (max 1000 on some systems #9065).
+ -- Soft-wrap: ftplugin/man.lua sets wrap/breakindent/….
+ -- Hard-wrap: driven by `man`.
+ local manwidth
+ if (vim.g.man_hardwrap or 1) ~= 1 then
+ manwidth = 999
+ elseif vim.env.MANWIDTH then
+ manwidth = vim.env.MANWIDTH
+ else
+ manwidth = api.nvim_win_get_width(0)
+ end
+
+ local cmd = localfile_arg and { 'man', '-l', path } or { 'man', path }
+
+ -- Force MANPAGER=cat to ensure Vim is not recursively invoked (by man-db).
+ -- http://comments.gmane.org/gmane.editors.vim.devel/29085
+ -- Set MAN_KEEP_FORMATTING so Debian man doesn't discard backspaces.
+ return system(cmd, silent, {
+ 'MANPAGER=cat',
+ 'MANWIDTH=' .. manwidth,
+ 'MAN_KEEP_FORMATTING=1',
+ })
+end
+
+local function put_page(page)
+ vim.bo.modifiable = true
+ vim.bo.readonly = false
+ vim.bo.swapfile = false
+
+ api.nvim_buf_set_lines(0, 0, -1, false, vim.split(page, '\n'))
+
+ while fn.getline(1):match('^%s*$') do
+ api.nvim_buf_set_lines(0, 0, 1, false, {})
+ end
+ -- XXX: nroff justifies text by filling it with whitespace. That interacts
+ -- badly with our use of $MANWIDTH=999. Hack around this by using a fixed
+ -- size for those whitespace regions.
+ vim.cmd([[silent! keeppatterns keepjumps %s/\s\{199,}/\=repeat(' ', 10)/g]])
+ vim.cmd('1') -- Move cursor to first line
+ highlight_man_page()
+ set_options(false)
+end
+
+local function format_candidate(path, psect)
+ if matchstr(path, [[\.\%(pdf\|in\)$]]) then -- invalid extensions
+ return ''
+ end
+ local sect, name = extract_sect_and_name_path(path)
+ if sect == psect then
+ return name
+ elseif sect and name and matchstr(sect, psect .. '.\\+$') then -- invalid extensions
+ -- We include the section if the user provided section is a prefix
+ -- of the actual section.
+ return ('%s(%s)'):format(name, sect)
+ end
+ return ''
+end
+
+local function move_elem_to_head(list, elem)
+ local list1 = vim.tbl_filter(function(v)
+ return v ~= elem
+ end, list)
+ return { elem, unpack(list1) }
+end
+
+local function get_paths(sect, name)
+ -- Try several sources for getting the list man directories:
+ -- 1. `man -w` (works on most systems)
+ -- 2. `manpath`
+ -- 3. $MANPATH
+ local mandirs_raw = vim.F.npcall(system, { 'man', FIND_ARG })
+ or vim.F.npcall(system, { 'manpath', '-q' })
+ or vim.env.MANPATH
+
+ if not mandirs_raw then
+ man_error("Could not determine man directories from: 'man -w', 'manpath' or $MANPATH")
+ end
+
+ local mandirs = table.concat(vim.split(mandirs_raw, '[:\n]', { trimempty = true }), ',')
+ local paths = fn.globpath(mandirs, 'man?/' .. name .. '*.' .. sect .. '*', false, true)
+
+ -- Prioritize the result from verify_exists as it obeys b:man_default_sects.
+ local first = verify_exists(sect, name, true)
+ if first then
+ paths = move_elem_to_head(paths, first)
+ end
+
+ return paths
+end
+
+local function complete(sect, psect, name)
+ local pages = get_paths(sect, name)
+ -- We remove duplicates in case the same manpage in different languages was found.
+ return fn.uniq(fn.sort(vim.tbl_map(function(v)
+ return format_candidate(v, psect)
+ end, pages) or {}, 'i'))
+end
+
+-- see extract_sect_and_name_ref on why tolower(sect)
+function M.man_complete(arg_lead, cmd_line, _)
+ local args = vim.split(cmd_line, '%s+', { trimempty = true })
+ local cmd_offset = fn.index(args, 'Man')
+ if cmd_offset > 0 then
+ -- Prune all arguments up to :Man itself. Otherwise modifier commands like
+ -- :tab, :vertical, etc. would lead to a wrong length.
+ args = vim.list_slice(args, cmd_offset + 1)
+ end
+
+ if #args > 3 then
+ return {}
+ end
+
+ if #args == 1 then
+ -- returning full completion is laggy. Require some arg_lead to complete
+ -- return complete('', '', '')
+ return {}
+ end
+
+ if arg_lead:match('^[^()]+%([^()]*$') then
+ -- cursor (|) is at ':Man printf(|' or ':Man 1 printf(|'
+ -- The later is is allowed because of ':Man pri<TAB>'.
+ -- It will offer 'priclass.d(1m)' even though section is specified as 1.
+ local tmp = vim.split(arg_lead, '(', { plain = true })
+ local name = tmp[1]
+ local sect = (tmp[2] or ''):lower()
+ return complete(sect, '', name)
+ end
+
+ if not args[2]:match('^[^()]+$') then
+ -- cursor (|) is at ':Man 3() |' or ':Man (3|' or ':Man 3() pri|'
+ -- or ':Man 3() pri |'
+ return {}
+ end
+
+ if #args == 2 then
+ local name, sect
+ if arg_lead == '' then
+ -- cursor (|) is at ':Man 1 |'
+ name = ''
+ sect = args[1]:lower()
+ else
+ -- cursor (|) is at ':Man pri|'
+ if arg_lead:match('/') then
+ -- if the name is a path, complete files
+ -- TODO(nhooyr) why does this complete the last one automatically
+ return fn.glob(arg_lead .. '*', false, true)
+ end
+ name = arg_lead
+ sect = ''
+ end
+ return complete(sect, sect, name)
+ end
+
+ if not arg_lead:match('[^()]+$') then
+ -- cursor (|) is at ':Man 3 printf |' or ':Man 3 (pr)i|'
+ return {}
+ end
+
+ -- cursor (|) is at ':Man 3 pri|'
+ local name = arg_lead
+ local sect = args[2]:lower()
+ return complete(sect, sect, name)
+end
+
+function M.goto_tag(pattern, _, _)
+ local sect, name = extract_sect_and_name_ref(pattern)
+
+ local paths = get_paths(sect, name)
+ local structured = {}
+
+ for _, path in ipairs(paths) do
+ sect, name = extract_sect_and_name_path(path)
+ if sect and name then
+ structured[#structured + 1] = {
+ name = name,
+ title = name .. '(' .. sect .. ')',
+ }
+ end
+ end
+
+ return vim.tbl_map(function(entry)
+ return {
+ name = entry.name,
+ filename = 'man://' .. entry.title,
+ cmd = '1',
+ }
+ end, structured)
+end
+
+-- Called when Nvim is invoked as $MANPAGER.
+function M.init_pager()
+ if fn.getline(1):match('^%s*$') then
+ api.nvim_buf_set_lines(0, 0, 1, false, {})
+ else
+ vim.cmd('keepjumps 1')
+ end
+ highlight_man_page()
+ -- Guess the ref from the heading (which is usually uppercase, so we cannot
+ -- know the correct casing, cf. `man glDrawArraysInstanced`).
+ local ref = fn.substitute(matchstr(fn.getline(1), [[^[^)]\+)]]) or '', ' ', '_', 'g')
+ local ok, res = pcall(extract_sect_and_name_ref, ref)
+ vim.b.man_sect = ok and res or ''
+
+ if not fn.bufname('%'):match('man://') then -- Avoid duplicate buffers, E95.
+ vim.cmd.file({ 'man://' .. fn.fnameescape(ref):lower(), mods = { silent = true } })
+ end
+
+ set_options(true)
+end
+
+function M.open_page(count, smods, args)
+ if #args > 2 then
+ man_error('too many arguments')
+ end
+
+ local ref
+ if #args == 0 then
+ ref = vim.bo.filetype == 'man' and fn.expand('<cWORD>') or fn.expand('<cword>')
+ if ref == '' then
+ man_error('no identifier under cursor')
+ end
+ elseif #args == 1 then
+ ref = args[1]
+ else
+ -- Combine the name and sect into a manpage reference so that all
+ -- verification/extraction can be kept in a single function.
+ -- If args[2] is a reference as well, that is fine because it is the only
+ -- reference that will match.
+ ref = ('%s(%s)'):format(args[2], args[1])
+ end
+
+ local sect, name = extract_sect_and_name_ref(ref)
+ if count >= 0 then
+ sect = tostring(count)
+ end
+
+ local path = verify_exists(sect, name)
+ sect, name = extract_sect_and_name_path(path)
+
+ local buf = fn.bufnr()
+ local save_tfu = vim.bo[buf].tagfunc
+ vim.bo[buf].tagfunc = "v:lua.require'man'.goto_tag"
+
+ local target = ('%s(%s)'):format(name, sect)
+
+ local ok, ret = pcall(function()
+ if smods.tab == -1 and find_man() then
+ vim.cmd.tag({ target, mods = { silent = true, keepalt = true } })
+ else
+ smods.silent = true
+ smods.keepalt = true
+ vim.cmd.stag({ target, mods = smods })
+ end
+ end)
+
+ vim.bo[buf].tagfunc = save_tfu
+
+ if not ok then
+ error(ret)
+ else
+ set_options(false)
+ end
+
+ vim.b.man_sect = sect
+end
+
+-- Called when a man:// buffer is opened.
+function M.read_page(ref)
+ local sect, name = extract_sect_and_name_ref(ref)
+ local path = verify_exists(sect, name)
+ sect = extract_sect_and_name_path(path)
+ local page = get_page(path)
+ vim.b.man_sect = sect
+ put_page(page)
+end
+
+function M.show_toc()
+ local bufname = fn.bufname('%')
+ local info = fn.getloclist(0, { winid = 1 })
+ if info ~= '' and vim.w[info.winid].qf_toc == bufname then
+ vim.cmd.lopen()
+ return
+ end
+
+ local toc = {}
+ local lnum = 2
+ local last_line = fn.line('$') - 1
+ local section_title_re = vim.regex([[^\%( \{3\}\)\=\S.*$]])
+ local flag_title_re = vim.regex([[^\s\+\%(+\|-\)\S\+]])
+ while lnum and lnum < last_line do
+ local text = fn.getline(lnum)
+ if section_title_re:match_str(text) then
+ -- if text is a section title
+ toc[#toc + 1] = {
+ bufnr = fn.bufnr('%'),
+ lnum = lnum,
+ text = text,
+ }
+ elseif flag_title_re:match_str(text) then
+ -- if text is a flag title. we strip whitespaces and prepend two
+ -- spaces to have a consistent format in the loclist.
+ toc[#toc + 1] = {
+ bufnr = fn.bufnr('%'),
+ lnum = lnum,
+ text = ' ' .. fn.substitute(text, [[^\s*\(.\{-}\)\s*$]], [[\1]], ''),
+ }
+ end
+ lnum = fn.nextnonblank(lnum + 1)
+ end
+
+ fn.setloclist(0, toc, ' ')
+ fn.setloclist(0, {}, 'a', { title = 'Man TOC' })
+ vim.cmd.lopen()
+ vim.w.qf_toc = bufname
+end
+
+local function init()
+ local path = get_path('', 'man', true)
+ local page
+ if path ~= nil then
+ -- Check for -l support.
+ page = get_page(path, true)
+ end
+
+ if page == '' or page == nil then
+ localfile_arg = false
+ end
+end
+
+init()
+
+return M
diff --git a/runtime/lua/nvim/health.lua b/runtime/lua/nvim/health.lua
new file mode 100644
index 0000000000..b76106f241
--- /dev/null
+++ b/runtime/lua/nvim/health.lua
@@ -0,0 +1,406 @@
+local M = {}
+local health = require('vim.health')
+
+local fn_bool = function(key)
+ return function(...)
+ return vim.fn[key](...) == 1
+ end
+end
+
+local has = fn_bool('has')
+local executable = fn_bool('executable')
+local empty = fn_bool('empty')
+local filereadable = fn_bool('filereadable')
+local filewritable = fn_bool('filewritable')
+
+local shell_error = function()
+ return vim.v.shell_error ~= 0
+end
+
+local suggest_faq = 'https://github.com/neovim/neovim/wiki/Building-Neovim#optimized-builds'
+
+local function check_runtime()
+ health.report_start('Runtime')
+ -- Files from an old installation.
+ local bad_files = {
+ ['plugin/man.vim'] = false,
+ ['scripts.vim'] = false,
+ ['autoload/man.vim'] = false,
+ }
+ local bad_files_msg = ''
+ for k, _ in pairs(bad_files) do
+ local path = ('%s/%s'):format(vim.env.VIMRUNTIME, k)
+ if vim.loop.fs_stat(path) then
+ bad_files[k] = true
+ bad_files_msg = ('%s%s\n'):format(bad_files_msg, path)
+ end
+ end
+
+ local ok = (bad_files_msg == '')
+ local info = ok and health.report_ok or health.report_info
+ info(string.format('$VIMRUNTIME: %s', vim.env.VIMRUNTIME))
+ if not ok then
+ health.report_error(
+ string.format(
+ '$VIMRUNTIME has files from an old installation (this can cause weird behavior):\n%s',
+ bad_files_msg
+ ),
+ { 'Delete $VIMRUNTIME (or uninstall Nvim), then reinstall Nvim.' }
+ )
+ end
+end
+
+local function check_config()
+ health.report_start('Configuration')
+ local ok = true
+
+ local vimrc = (
+ empty(vim.env.MYVIMRC) and vim.fn.stdpath('config') .. '/init.vim' or vim.env.MYVIMRC
+ )
+ if not filereadable(vimrc) then
+ ok = false
+ local has_vim = filereadable(vim.fn.expand('~/.vimrc'))
+ health.report_warn(
+ (-1 == vim.fn.getfsize(vimrc) and 'Missing' or 'Unreadable') .. ' user config file: ' .. vimrc,
+ { has_vim and ':help nvim-from-vim' or ':help init.vim' }
+ )
+ end
+
+ -- If $VIM is empty we don't care. Else make sure it is valid.
+ if not empty(vim.env.VIM) and not filereadable(vim.env.VIM .. '/runtime/doc/nvim.txt') then
+ ok = false
+ health.report_error('$VIM is invalid: ' .. vim.env.VIM)
+ end
+
+ if vim.env.NVIM_TUI_ENABLE_CURSOR_SHAPE then
+ ok = false
+ health.report_warn('$NVIM_TUI_ENABLE_CURSOR_SHAPE is ignored in Nvim 0.2+', {
+ "Use the 'guicursor' option to configure cursor shape. :help 'guicursor'",
+ 'https://github.com/neovim/neovim/wiki/Following-HEAD#20170402',
+ })
+ end
+
+ if vim.v.ctype == 'C' then
+ ok = false
+ health.report_error(
+ 'Locale does not support UTF-8. Unicode characters may not display correctly.'
+ .. ('\n$LANG=%s $LC_ALL=%s $LC_CTYPE=%s'):format(
+ vim.env.LANG,
+ vim.env.LC_ALL,
+ vim.env.LC_CTYPE
+ ),
+ {
+ 'If using tmux, try the -u option.',
+ 'Ensure that your terminal/shell/tmux/etc inherits the environment, or set $LANG explicitly.',
+ 'Configure your system locale.',
+ }
+ )
+ end
+
+ if vim.o.paste == 1 then
+ ok = false
+ health.report_error(
+ "'paste' is enabled. This option is only for pasting text.\nIt should not be set in your config.",
+ {
+ 'Remove `set paste` from your init.vim, if applicable.',
+ 'Check `:verbose set paste?` to see if a plugin or script set the option.',
+ }
+ )
+ end
+
+ local writeable = true
+ local shadaopt = vim.fn.split(vim.o.shada, ',')
+ local shadafile = (
+ empty(vim.o.shada) and vim.o.shada
+ or vim.fn.substitute(vim.fn.matchstr(shadaopt[#shadaopt], '^n.\\+'), '^n', '', '')
+ )
+ shadafile = (
+ empty(vim.o.shadafile)
+ and (empty(shadafile) and vim.fn.stdpath('state') .. '/shada/main.shada' or vim.fn.expand(
+ shadafile
+ ))
+ or (vim.o.shadafile == 'NONE' and '' or vim.o.shadafile)
+ )
+ if not empty(shadafile) and empty(vim.fn.glob(shadafile)) then
+ -- Since this may be the first time Nvim has been run, try to create a shada file.
+ if not pcall(vim.cmd.wshada) then
+ writeable = false
+ end
+ end
+ if
+ not writeable
+ or (not empty(shadafile) and (not filereadable(shadafile) or not filewritable(shadafile)))
+ then
+ ok = false
+ health.report_error(
+ 'shada file is not '
+ .. ((not writeable or filereadable(shadafile)) and 'writeable' or 'readable')
+ .. ':\n'
+ .. shadafile
+ )
+ end
+
+ if ok then
+ health.report_ok('no issues found')
+ end
+end
+
+local function check_performance()
+ health.report_start('Performance')
+
+ -- Check buildtype
+ local buildtype = vim.fn.matchstr(vim.fn.execute('version'), [[\v\cbuild type:?\s*[^\n\r\t ]+]])
+ if empty(buildtype) then
+ health.report_error('failed to get build type from :version')
+ elseif vim.regex([[\v(MinSizeRel|Release|RelWithDebInfo)]]):match_str(buildtype) then
+ health.report_ok(buildtype)
+ else
+ health.report_info(buildtype)
+ health.report_warn(
+ 'Non-optimized ' .. (has('debug') and '(DEBUG) ' or '') .. 'build. Nvim will be slower.',
+ {
+ 'Install a different Nvim package, or rebuild with `CMAKE_BUILD_TYPE=RelWithDebInfo`.',
+ suggest_faq,
+ }
+ )
+ end
+
+ -- check for slow shell invocation
+ local slow_cmd_time = 1.5
+ local start_time = vim.fn.reltime()
+ vim.fn.system('echo')
+ local elapsed_time = vim.fn.reltimefloat(vim.fn.reltime(start_time))
+ if elapsed_time > slow_cmd_time then
+ health.report_warn(
+ 'Slow shell invocation (took ' .. vim.fn.printf('%.2f', elapsed_time) .. ' seconds).'
+ )
+ end
+end
+
+-- Load the remote plugin manifest file and check for unregistered plugins
+local function check_rplugin_manifest()
+ health.report_start('Remote Plugins')
+
+ local existing_rplugins = {}
+ for _, item in ipairs(vim.fn['remote#host#PluginsForHost']('python')) do
+ existing_rplugins[item.path] = 'python'
+ end
+
+ for item in ipairs(vim.fn['remote#host#PluginsForHost']('python3')) do
+ existing_rplugins[item.path] = 'python3'
+ end
+
+ local require_update = false
+ local handle_path = function(path)
+ local python_glob = vim.fn.glob(path .. '/rplugin/python*', true, true)
+ if empty(python_glob) then
+ return
+ end
+
+ local python_dir = python_glob[1]
+ local python_version = vim.fn.fnamemodify(python_dir, ':t')
+
+ local scripts = vim.fn.glob(python_dir .. '/*.py', true, true)
+ vim.list_extend(scripts, vim.fn.glob(python_dir .. '/*/__init__.py', true, true))
+
+ for script in ipairs(scripts) do
+ local contents = vim.fn.join(vim.fn.readfile(script))
+ if vim.regex([[\<\%(from\|import\)\s\+neovim\>]]):match_str(contents) then
+ if vim.regex([[[\/]__init__\.py$]]):match_str(script) then
+ script = vim.fn.tr(vim.fn.fnamemodify(script, ':h'), '\\', '/')
+ end
+ if not existing_rplugins[script] then
+ local msg = vim.fn.printf('"%s" is not registered.', vim.fn.fnamemodify(path, ':t'))
+ if python_version == 'pythonx' then
+ if not has('python3') then
+ msg = msg .. ' (python3 not available)'
+ end
+ elseif not has(python_version) then
+ msg = msg .. vim.fn.printf(' (%s not available)', python_version)
+ else
+ require_update = true
+ end
+
+ health.report_warn(msg)
+ end
+
+ break
+ end
+ end
+ end
+
+ for _, path in ipairs(vim.fn.map(vim.fn.split(vim.o.runtimepath, ','), 'resolve(v:val)')) do
+ handle_path(path)
+ end
+
+ if require_update then
+ health.report_warn('Out of date', { 'Run `:UpdateRemotePlugins`' })
+ else
+ health.report_ok('Up to date')
+ end
+end
+
+local function check_tmux()
+ if empty(vim.env.TMUX) or not executable('tmux') then
+ return
+ end
+
+ local get_tmux_option = function(option)
+ local cmd = 'tmux show-option -qvg ' .. option -- try global scope
+ local out = vim.fn.system(vim.fn.split(cmd))
+ local val = vim.fn.substitute(out, [[\v(\s|\r|\n)]], '', 'g')
+ if shell_error() then
+ health.report_error('command failed: ' .. cmd .. '\n' .. out)
+ return 'error'
+ elseif empty(val) then
+ cmd = 'tmux show-option -qvgs ' .. option -- try session scope
+ out = vim.fn.system(vim.fn.split(cmd))
+ val = vim.fn.substitute(out, [[\v(\s|\r|\n)]], '', 'g')
+ if shell_error() then
+ health.report_error('command failed: ' .. cmd .. '\n' .. out)
+ return 'error'
+ end
+ end
+ return val
+ end
+
+ health.report_start('tmux')
+
+ -- check escape-time
+ local suggestions =
+ { 'set escape-time in ~/.tmux.conf:\nset-option -sg escape-time 10', suggest_faq }
+ local tmux_esc_time = get_tmux_option('escape-time')
+ if tmux_esc_time ~= 'error' then
+ if empty(tmux_esc_time) then
+ health.report_error('`escape-time` is not set', suggestions)
+ elseif tonumber(tmux_esc_time) > 300 then
+ health.report_error(
+ '`escape-time` (' .. tmux_esc_time .. ') is higher than 300ms',
+ suggestions
+ )
+ else
+ health.report_ok('escape-time: ' .. tmux_esc_time)
+ end
+ end
+
+ -- check focus-events
+ local tmux_focus_events = get_tmux_option('focus-events')
+ if tmux_focus_events ~= 'error' then
+ if empty(tmux_focus_events) or tmux_focus_events ~= 'on' then
+ health.report_warn(
+ "`focus-events` is not enabled. |'autoread'| may not work.",
+ { '(tmux 1.9+ only) Set `focus-events` in ~/.tmux.conf:\nset-option -g focus-events on' }
+ )
+ else
+ health.report_ok('focus-events: ' .. tmux_focus_events)
+ end
+ end
+
+ -- check default-terminal and $TERM
+ health.report_info('$TERM: ' .. vim.env.TERM)
+ local cmd = 'tmux show-option -qvg default-terminal'
+ local out = vim.fn.system(vim.fn.split(cmd))
+ local tmux_default_term = vim.fn.substitute(out, [[\v(\s|\r|\n)]], '', 'g')
+ if empty(tmux_default_term) then
+ cmd = 'tmux show-option -qvgs default-terminal'
+ out = vim.fn.system(vim.fn.split(cmd))
+ tmux_default_term = vim.fn.substitute(out, [[\v(\s|\r|\n)]], '', 'g')
+ end
+
+ if shell_error() then
+ health.report_error('command failed: ' .. cmd .. '\n' .. out)
+ elseif tmux_default_term ~= vim.env.TERM then
+ health.report_info('default-terminal: ' .. tmux_default_term)
+ health.report_error(
+ '$TERM differs from the tmux `default-terminal` setting. Colors might look wrong.',
+ { '$TERM may have been set by some rc (.bashrc, .zshrc, ...).' }
+ )
+ elseif not vim.regex([[\v(tmux-256color|screen-256color)]]):match_str(vim.env.TERM) then
+ health.report_error(
+ '$TERM should be "screen-256color" or "tmux-256color" in tmux. Colors might look wrong.',
+ {
+ 'Set default-terminal in ~/.tmux.conf:\nset-option -g default-terminal "screen-256color"',
+ suggest_faq,
+ }
+ )
+ end
+
+ -- check for RGB capabilities
+ local info = vim.fn.system({ 'tmux', 'display-message', '-p', '#{client_termfeatures}' })
+ info = vim.split(vim.trim(info), ',', { trimempty = true })
+ if not vim.tbl_contains(info, 'RGB') then
+ local has_rgb = false
+ if #info == 0 then
+ -- client_termfeatures may not be supported; fallback to checking show-messages
+ info = vim.fn.system({ 'tmux', 'show-messages', '-JT' })
+ has_rgb = info:find(' Tc: (flag) true', 1, true) or info:find(' RGB: (flag) true', 1, true)
+ end
+ if not has_rgb then
+ health.report_warn(
+ "Neither Tc nor RGB capability set. True colors are disabled. |'termguicolors'| won't work properly.",
+ {
+ "Put this in your ~/.tmux.conf and replace XXX by your $TERM outside of tmux:\nset-option -sa terminal-features ',XXX:RGB'",
+ "For older tmux versions use this instead:\nset-option -ga terminal-overrides ',XXX:Tc'",
+ }
+ )
+ end
+ end
+end
+
+local function check_terminal()
+ if not executable('infocmp') then
+ return
+ end
+
+ health.report_start('terminal')
+ local cmd = 'infocmp -L'
+ local out = vim.fn.system(vim.fn.split(cmd))
+ local kbs_entry = vim.fn.matchstr(out, 'key_backspace=[^,[:space:]]*')
+ local kdch1_entry = vim.fn.matchstr(out, 'key_dc=[^,[:space:]]*')
+
+ if
+ shell_error()
+ and (
+ not has('win32')
+ or empty(
+ vim.fn.matchstr(
+ out,
+ [[infocmp: couldn't open terminfo file .\+\%(conemu\|vtpcon\|win32con\)]]
+ )
+ )
+ )
+ then
+ health.report_error('command failed: ' .. cmd .. '\n' .. out)
+ else
+ health.report_info(
+ vim.fn.printf(
+ 'key_backspace (kbs) terminfo entry: `%s`',
+ (empty(kbs_entry) and '? (not found)' or kbs_entry)
+ )
+ )
+
+ health.report_info(
+ vim.fn.printf(
+ 'key_dc (kdch1) terminfo entry: `%s`',
+ (empty(kbs_entry) and '? (not found)' or kdch1_entry)
+ )
+ )
+ end
+
+ for env_var in ipairs({ 'XTERM_VERSION', 'VTE_VERSION', 'TERM_PROGRAM', 'COLORTERM', 'SSH_TTY' }) do
+ if vim.env[env_var] then
+ health.report_info(vim.fn.printf('$%s="%s"', env_var, vim.env[env_var]))
+ end
+ end
+end
+
+function M.check()
+ check_config()
+ check_runtime()
+ check_performance()
+ check_rplugin_manifest()
+ check_terminal()
+ check_tmux()
+end
+
+return M
diff --git a/runtime/lua/vim/F.lua b/runtime/lua/vim/F.lua
index bca5ddf68b..3e370c0a84 100644
--- a/runtime/lua/vim/F.lua
+++ b/runtime/lua/vim/F.lua
@@ -2,8 +2,12 @@ local F = {}
--- Returns {a} if it is not nil, otherwise returns {b}.
---
----@param a
----@param b
+---@generic A
+---@generic B
+---
+---@param a A
+---@param b B
+---@return A | B
function F.if_nil(a, b)
if a == nil then
return b
diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua
index b8a7f71145..da8764fbd4 100644
--- a/runtime/lua/vim/_editor.lua
+++ b/runtime/lua/vim/_editor.lua
@@ -12,21 +12,8 @@
-- Guideline: "If in doubt, put it in the runtime".
--
-- Most functions should live directly in `vim.`, not in submodules.
--- The only "forbidden" names are those claimed by legacy `if_lua`:
--- $ vim
--- :lua for k,v in pairs(vim) do print(k) end
--- buffer
--- open
--- window
--- lastline
--- firstline
--- type
--- line
--- eval
--- dict
--- beep
--- list
--- command
+--
+-- Compatibility with Vim's `if_lua` is explicitly a non-goal.
--
-- Reference (#6580):
-- - https://github.com/luafun/luafun
@@ -36,8 +23,6 @@
-- - https://github.com/bakpakin/Fennel (pretty print, repl)
-- - https://github.com/howl-editor/howl/tree/master/lib/howl/util
-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
for k, v in pairs({
@@ -51,6 +36,7 @@ for k, v in pairs({
ui = true,
health = true,
fs = true,
+ secure = true,
}) do
vim._submodules[k] = v
end
@@ -122,9 +108,7 @@ function vim._os_proc_children(ppid)
return children
end
--- TODO(ZyX-I): Create compatibility layer.
-
---- Return a human-readable representation of the given object.
+--- Gets a human-readable representation of the given object.
---
---@see https://github.com/kikito/inspect.lua
---@see https://github.com/mpeterv/vinspect
@@ -139,7 +123,7 @@ do
--- (such as the |TUI|) pastes text into the editor.
---
--- Example: To remove ANSI color codes when pasting:
- --- <pre>
+ --- <pre>lua
--- vim.paste = (function(overridden)
--- return function(lines, phase)
--- for i,line in ipairs(lines) do
@@ -152,14 +136,15 @@ do
--- </pre>
---
---@see |paste|
+ ---@alias paste_phase -1 | 1 | 2 | 3
---
- ---@param lines |readfile()|-style list of lines to paste. |channel-lines|
- ---@param phase -1: "non-streaming" paste: the call contains all lines.
+ ---@param lines string[] # |readfile()|-style list of lines to paste. |channel-lines|
+ ---@param phase paste_phase -1: "non-streaming" paste: the call contains all lines.
--- If paste is "streamed", `phase` indicates the stream state:
--- - 1: starts the paste (exactly once)
--- - 2: continues the paste (zero or more times)
--- - 3: ends the paste (exactly once)
- ---@returns false if client should cancel the paste.
+ ---@returns boolean # false if client should cancel the paste.
function vim.paste(lines, phase)
local now = vim.loop.now()
local is_first_chunk = phase < 2
@@ -183,7 +168,8 @@ do
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)
+ -- "t" flag is also needed so the pasted text is saved in cmdline history.
+ vim.api.nvim_feedkeys(line1, 'nt', true)
end
return true
end
@@ -255,6 +241,8 @@ end
---@see |lua-loop-callbacks|
---@see |vim.schedule()|
---@see |vim.in_fast_event()|
+---@param cb function
+---@return function
function vim.schedule_wrap(cb)
return function(...)
local args = vim.F.pack_len(...)
@@ -292,7 +280,7 @@ end
--- command.
---
--- Example:
---- <pre>
+--- <pre>lua
--- vim.cmd('echo 42')
--- vim.cmd([[
--- augroup My_group
@@ -399,11 +387,11 @@ end
--- Get a table of lines with start, end columns for a region marked by two points
---
---@param bufnr number of buffer
----@param pos1 (line, column) tuple marking beginning of region
----@param pos2 (line, column) tuple marking end of region
----@param regtype type of selection (:help setreg)
+---@param pos1 integer[] (line, column) tuple marking beginning of region
+---@param pos2 integer[] (line, column) tuple marking end of region
+---@param regtype string type of selection, see |setreg()|
---@param inclusive boolean indicating whether the selection is end-inclusive
----@return region lua table of the form {linenr = {startcol,endcol}}
+---@return table region Table of the form `{linenr = {startcol,endcol}}`
function vim.region(bufnr, pos1, pos2, regtype, inclusive)
if not vim.api.nvim_buf_is_loaded(bufnr) then
vim.fn.bufload(bufnr)
@@ -430,11 +418,16 @@ function vim.region(bufnr, pos1, pos2, regtype, inclusive)
c2 = c1 + regtype:sub(2)
-- and adjust for non-ASCII characters
bufline = vim.api.nvim_buf_get_lines(bufnr, l, l + 1, true)[1]
- if c1 < #bufline then
+ local utflen = vim.str_utfindex(bufline, #bufline)
+ if c1 <= utflen then
c1 = vim.str_byteindex(bufline, c1)
+ else
+ c1 = #bufline + 1
end
- if c2 < #bufline then
+ if c2 <= utflen then
c2 = vim.str_byteindex(bufline, c2)
+ else
+ c2 = #bufline + 1
end
else
c1 = (l == pos1[1]) and pos1[2] or 0
@@ -448,11 +441,11 @@ end
--- 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
+--- Note: The {fn} is |vim.schedule_wrap()|ped automatically, so API functions are
--- safe to call.
----@param fn Callback to call once `timeout` expires
----@param timeout Number of milliseconds to wait before calling `fn`
----@return timer luv timer object
+---@param fn function Callback to call once `timeout` expires
+---@param timeout integer Number of milliseconds to wait before calling `fn`
+---@return table timer luv timer object
function vim.defer_fn(fn, timeout)
vim.validate({ fn = { fn, 'c', true } })
local timer = vim.loop.new_timer()
@@ -753,12 +746,12 @@ end
---Prints given arguments in human-readable format.
---Example:
----<pre>
+---<pre>lua
--- -- Print highlight group Normal and store it's contents in a variable.
--- local hl_normal = vim.pretty_print(vim.api.nvim_get_hl_by_name("Normal", true))
---</pre>
---@see |vim.inspect()|
----@return given arguments.
+---@return any # given arguments.
function vim.pretty_print(...)
local objects = {}
for i = 1, select('#', ...) do
diff --git a/runtime/lua/vim/_init_packages.lua b/runtime/lua/vim/_init_packages.lua
index 7e3c73667e..0c4ee8636d 100644
--- a/runtime/lua/vim/_init_packages.lua
+++ b/runtime/lua/vim/_init_packages.lua
@@ -1,6 +1,3 @@
--- 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
@@ -59,6 +56,9 @@ setmetatable(vim, {
if vim._submodules[key] then
t[key] = require('vim.' .. key)
return t[key]
+ elseif key == 'inspect_pos' or key == 'show_pos' then
+ require('vim._inspector')
+ return t[key]
elseif vim.startswith(key, 'uri_') then
local val = require('vim.uri')[key]
if val ~= nil then
diff --git a/runtime/lua/vim/_inspector.lua b/runtime/lua/vim/_inspector.lua
new file mode 100644
index 0000000000..f46a525910
--- /dev/null
+++ b/runtime/lua/vim/_inspector.lua
@@ -0,0 +1,238 @@
+---@class InspectorFilter
+---@field syntax boolean include syntax based highlight groups (defaults to true)
+---@field treesitter boolean include treesitter based highlight groups (defaults to true)
+---@field extmarks boolean|"all" include extmarks. When `all`, then extmarks without a `hl_group` will also be included (defaults to true)
+---@field semantic_tokens boolean include semantic tokens (defaults to true)
+local defaults = {
+ syntax = true,
+ treesitter = true,
+ extmarks = true,
+ semantic_tokens = true,
+}
+
+---Get all the items at a given buffer position.
+---
+---Can also be pretty-printed with `:Inspect!`. *:Inspect!*
+---
+---@param bufnr? number defaults to the current buffer
+---@param row? number row to inspect, 0-based. Defaults to the row of the current cursor
+---@param col? number col to inspect, 0-based. Defaults to the col of the current cursor
+---@param filter? InspectorFilter (table|nil) a table with key-value pairs to filter the items
+--- - syntax (boolean): include syntax based highlight groups (defaults to true)
+--- - treesitter (boolean): include treesitter based highlight groups (defaults to true)
+--- - extmarks (boolean|"all"): include extmarks. When `all`, then extmarks without a `hl_group` will also be included (defaults to true)
+--- - semantic_tokens (boolean): include semantic tokens (defaults to true)
+---@return {treesitter:table,syntax:table,extmarks:table,semantic_tokens:table,buffer:number,col:number,row:number} (table) a table with the following key-value pairs. Items are in "traversal order":
+--- - treesitter: a list of treesitter captures
+--- - syntax: a list of syntax groups
+--- - semantic_tokens: a list of semantic tokens
+--- - extmarks: a list of extmarks
+--- - buffer: the buffer used to get the items
+--- - row: the row used to get the items
+--- - col: the col used to get the items
+function vim.inspect_pos(bufnr, row, col, filter)
+ filter = vim.tbl_deep_extend('force', defaults, filter or {})
+
+ bufnr = bufnr or 0
+ if row == nil or col == nil then
+ -- get the row/col from the first window displaying the buffer
+ local win = bufnr == 0 and vim.api.nvim_get_current_win() or vim.fn.bufwinid(bufnr)
+ if win == -1 then
+ error('row/col is required for buffers not visible in a window')
+ end
+ local cursor = vim.api.nvim_win_get_cursor(win)
+ row, col = cursor[1] - 1, cursor[2]
+ end
+ bufnr = bufnr == 0 and vim.api.nvim_get_current_buf() or bufnr
+
+ local results = {
+ treesitter = {},
+ syntax = {},
+ extmarks = {},
+ semantic_tokens = {},
+ buffer = bufnr,
+ row = row,
+ col = col,
+ }
+
+ -- resolve hl links
+ ---@private
+ local function resolve_hl(data)
+ if data.hl_group then
+ local hlid = vim.api.nvim_get_hl_id_by_name(data.hl_group)
+ local name = vim.fn.synIDattr(vim.fn.synIDtrans(hlid), 'name')
+ data.hl_group_link = name
+ end
+ return data
+ end
+
+ -- treesitter
+ if filter.treesitter then
+ for _, capture in pairs(vim.treesitter.get_captures_at_pos(bufnr, row, col)) do
+ capture.hl_group = '@' .. capture.capture
+ table.insert(results.treesitter, resolve_hl(capture))
+ end
+ end
+
+ -- syntax
+ if filter.syntax then
+ for _, i1 in ipairs(vim.fn.synstack(row + 1, col + 1)) do
+ table.insert(results.syntax, resolve_hl({ hl_group = vim.fn.synIDattr(i1, 'name') }))
+ end
+ end
+
+ -- semantic tokens
+ if filter.semantic_tokens then
+ for _, token in ipairs(vim.lsp.semantic_tokens.get_at_pos(bufnr, row, col) or {}) do
+ token.hl_groups = {
+ type = resolve_hl({ hl_group = '@' .. token.type }),
+ modifiers = vim.tbl_map(function(modifier)
+ return resolve_hl({ hl_group = '@' .. modifier })
+ end, token.modifiers or {}),
+ }
+ table.insert(results.semantic_tokens, token)
+ end
+ end
+
+ -- extmarks
+ if filter.extmarks then
+ for ns, nsid in pairs(vim.api.nvim_get_namespaces()) do
+ if ns:find('vim_lsp_semantic_tokens') ~= 1 then
+ local extmarks = vim.api.nvim_buf_get_extmarks(bufnr, nsid, 0, -1, { details = true })
+ for _, extmark in ipairs(extmarks) do
+ extmark = {
+ ns_id = nsid,
+ ns = ns,
+ id = extmark[1],
+ row = extmark[2],
+ col = extmark[3],
+ opts = resolve_hl(extmark[4]),
+ }
+ local end_row = extmark.opts.end_row or extmark.row -- inclusive
+ local end_col = extmark.opts.end_col or (extmark.col + 1) -- exclusive
+ if
+ (filter.extmarks == 'all' or extmark.opts.hl_group) -- filter hl_group
+ and (row >= extmark.row and row <= end_row) -- within the rows of the extmark
+ and (row > extmark.row or col >= extmark.col) -- either not the first row, or in range of the col
+ and (row < end_row or col < end_col) -- either not in the last row or in range of the col
+ then
+ table.insert(results.extmarks, extmark)
+ end
+ end
+ end
+ end
+ end
+ return results
+end
+
+---Show all the items at a given buffer position.
+---
+---Can also be shown with `:Inspect`. *:Inspect*
+---
+---@param bufnr? number defaults to the current buffer
+---@param row? number row to inspect, 0-based. Defaults to the row of the current cursor
+---@param col? number col to inspect, 0-based. Defaults to the col of the current cursor
+---@param filter? InspectorFilter (table|nil) see |vim.inspect_pos()|
+function vim.show_pos(bufnr, row, col, filter)
+ local items = vim.inspect_pos(bufnr, row, col, filter)
+
+ local lines = { {} }
+
+ ---@private
+ local function append(str, hl)
+ table.insert(lines[#lines], { str, hl })
+ end
+
+ ---@private
+ local function nl()
+ table.insert(lines, {})
+ end
+
+ ---@private
+ local function item(data, comment)
+ append(' - ')
+ append(data.hl_group, data.hl_group)
+ append(' ')
+ if data.hl_group ~= data.hl_group_link then
+ append('links to ', 'MoreMsg')
+ append(data.hl_group_link, data.hl_group_link)
+ append(' ')
+ end
+ if comment then
+ append(comment, 'Comment')
+ end
+ nl()
+ end
+
+ -- treesitter
+ if #items.treesitter > 0 then
+ append('Treesitter', 'Title')
+ nl()
+ for _, capture in ipairs(items.treesitter) do
+ item(capture, capture.lang)
+ end
+ nl()
+ end
+
+ if #items.semantic_tokens > 0 then
+ append('Semantic Tokens', 'Title')
+ nl()
+ for _, token in ipairs(items.semantic_tokens) do
+ local client = vim.lsp.get_client_by_id(token.client_id)
+ client = client and (' (' .. client.name .. ')') or ''
+ item(token.hl_groups.type, 'type' .. client)
+ for _, modifier in ipairs(token.hl_groups.modifiers) do
+ item(modifier, 'modifier' .. client)
+ end
+ end
+ nl()
+ end
+
+ -- syntax
+ if #items.syntax > 0 then
+ append('Syntax', 'Title')
+ nl()
+ for _, syn in ipairs(items.syntax) do
+ item(syn)
+ end
+ nl()
+ end
+ -- extmarks
+ if #items.extmarks > 0 then
+ append('Extmarks', 'Title')
+ nl()
+ for _, extmark in ipairs(items.extmarks) do
+ if extmark.opts.hl_group then
+ item(extmark.opts, extmark.ns)
+ else
+ append(' - ')
+ append(extmark.ns, 'Comment')
+ nl()
+ end
+ end
+ nl()
+ end
+
+ if #lines[#lines] == 0 then
+ table.remove(lines)
+ end
+
+ local chunks = {}
+ for _, line in ipairs(lines) do
+ vim.list_extend(chunks, line)
+ table.insert(chunks, { '\n' })
+ end
+ if #chunks == 0 then
+ chunks = {
+ {
+ 'No items found at position '
+ .. items.row
+ .. ','
+ .. items.col
+ .. ' in buffer '
+ .. items.buffer,
+ },
+ }
+ end
+ vim.api.nvim_echo(chunks, false, {})
+end
diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua
index f1652718ee..104f29c4c0 100644
--- a/runtime/lua/vim/_meta.lua
+++ b/runtime/lua/vim/_meta.lua
@@ -1,173 +1,113 @@
--- prevents luacheck from making lints for setting things on vim
-local vim = assert(vim)
-
local a = vim.api
-local validate = vim.validate
-
-local SET_TYPES = setmetatable({
- SET = 0,
- LOCAL = 1,
- GLOBAL = 2,
-}, { __index = error })
-
-local options_info = nil
-local buf_options = nil
-local glb_options = nil
-local win_options = nil
-
-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
- 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
+-- TODO(tjdevries): Improve option metadata so that this doesn't have to be hardcoded.
+-- Can be done in a separate PR.
+local key_value_options = {
+ fillchars = true,
+ fcs = true,
+ listchars = true,
+ lcs = true,
+ winhighlight = true,
+ winhl = true,
+}
+
+--- Convert a vimoption_T style dictionary to the correct OptionType associated with it.
+---@return string
+local function get_option_metatype(name, info)
+ if info.type == 'string' then
+ if info.flaglist then
+ return 'set'
+ elseif info.commalist then
+ if key_value_options[name] then
+ return 'map'
end
+ return 'array'
end
-
- return result
+ return 'string'
end
-
- buf_options = get_scoped_options('buf')
- glb_options = get_scoped_options('global')
- win_options = get_scoped_options('win')
+ return info.type
end
-local function make_meta_accessor(get, set, del, validator)
- validator = validator or function()
- return true
- end
-
- validate({
- get = { get, 'f' },
- set = { set, 'f' },
- del = { del, 'f', true },
- validator = { validator, 'f' },
- })
+local options_info = setmetatable({}, {
+ __index = function(t, k)
+ local info = a.nvim_get_option_info(k)
+ info.metatype = get_option_metatype(k, info)
+ rawset(t, k, info)
+ return rawget(t, k)
+ end,
+})
- local mt = {}
- function mt:__newindex(k, v)
- if not validator(k) then
- return
+vim.env = setmetatable({}, {
+ __index = function(_, k)
+ local v = vim.fn.getenv(k)
+ if v == vim.NIL then
+ return nil
end
+ return v
+ end,
- if del and v == nil then
- return del(k)
- end
- return set(k, v)
- end
- function mt:__index(k)
- if not validator(k) then
- return
- end
+ __newindex = function(_, k, v)
+ vim.fn.setenv(k, v)
+ end,
+})
- return get(k)
+local function opt_validate(option_name, target_scope)
+ local scope = options_info[option_name].scope
+ if scope ~= target_scope then
+ local scope_to_string = { buf = 'buffer', win = 'window' }
+ error(
+ string.format(
+ [['%s' is a %s option, not a %s option. See ":help %s"]],
+ option_name,
+ scope_to_string[scope] or scope,
+ scope_to_string[target_scope] or target_scope,
+ option_name
+ )
+ )
end
- return setmetatable({}, mt)
end
-vim.env = make_meta_accessor(function(k)
- local v = vim.fn.getenv(k)
- if v == vim.NIL then
- return nil
- end
- return v
-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
- return new_buf_opt_accessor(k)
- end
-
- return a.nvim_get_option_value(k, { buf = bufnr or 0 })
- end
-
- local function set(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)
- )
- elseif glb_options[k] then
- error(
- string.format([['%s' is a global option, not a buffer option. See ":help %s"]], k, k)
- )
- end
+local function new_opt_accessor(handle, scope)
+ return setmetatable({}, {
+ __index = function(_, k)
+ if handle == nil and type(k) == 'number' then
+ return new_opt_accessor(k, scope)
end
+ opt_validate(k, scope)
+ return a.nvim_get_option_value(k, { [scope] = handle or 0 })
+ end,
- return true
- end)
- end
-
- vim.bo = new_buf_opt_accessor(nil)
+ __newindex = function(_, k, v)
+ opt_validate(k, scope)
+ return a.nvim_set_option_value(k, v, { [scope] = handle or 0 })
+ end,
+ })
end
-do -- window option accessor
- local function new_win_opt_accessor(winnr)
- local function get(k)
- if winnr == nil and type(k) == 'number' then
- return new_win_opt_accessor(k)
- end
- return a.nvim_get_option_value(k, { win = winnr or 0 })
- end
-
- local function set(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)
- )
- elseif glb_options[k] then
- error(
- string.format([['%s' is a global option, not a window option. See ":help %s"]], k, k)
- )
- end
- end
-
- return true
- end)
- end
-
- vim.wo = new_win_opt_accessor(nil)
-end
+vim.bo = new_opt_accessor(nil, 'buf')
+vim.wo = new_opt_accessor(nil, 'win')
-- 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 = setmetatable({}, {
+ __index = function(_, k)
+ return a.nvim_get_option_value(k, { scope = 'global' })
+ end,
+ __newindex = 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 = setmetatable({}, {
+ __index = function(_, k)
+ return a.nvim_get_option_value(k, {})
+ end,
+ __newindex = function(_, k, v)
+ return a.nvim_set_option_value(k, v, {})
+ end,
+})
---@brief [[
--- vim.opt, vim.opt_local and vim.opt_global implementation
@@ -178,11 +118,8 @@ end)
---@brief ]]
--- Preserves the order and does not mutate the original list
-local remove_duplicate_values = function(t)
+local function remove_duplicate_values(t)
local result, seen = {}, {}
- 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)
@@ -194,61 +131,6 @@ local remove_duplicate_values = function(t)
return result
end
--- TODO(tjdevries): Improve option metadata so that this doesn't have to be hardcoded.
--- Can be done in a separate PR.
-local key_value_options = {
- fillchars = true,
- listchars = true,
- winhl = true,
-}
-
----@class OptionTypes
---- Option Type Enum
-local OptionTypes = setmetatable({
- BOOLEAN = 0,
- 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,
-})
-
---- 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
- return OptionTypes.BOOLEAN
- elseif info.type == 'number' then
- return OptionTypes.NUMBER
- 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')
- return OptionTypes.MAP
- end
-
- if info.flaglist then
- return OptionTypes.SET
- elseif info.commalist then
- return OptionTypes.ARRAY
- end
-
- error('Fallthrough in OptionTypes')
- else
- 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)
@@ -269,373 +151,323 @@ local function assert_valid_value(name, value, types)
)
end
+local function passthrough(_, x)
+ return x
+end
+
+local function tbl_merge(left, right)
+ return vim.tbl_extend('force', left, right)
+end
+
+local function tbl_remove(t, value)
+ if type(value) == 'string' then
+ t[value] = nil
+ else
+ for _, v in ipairs(value) do
+ t[v] = nil
+ end
+ end
+
+ return t
+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' },
+ boolean = { 'boolean' },
+ number = { 'number' },
+ string = { 'string' },
+ set = { 'string', 'table' },
+ array = { 'string', 'table' },
+ map = { 'string', 'table' },
}
---- Convert a lua value to a vimoption_T value
-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.SET] = function(info, value)
- if type(value) == 'string' then
- return value
- end
+-- 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 = {
+ boolean = passthrough,
+ number = passthrough,
+ string = passthrough,
- if info.flaglist and info.commalist then
- local keys = {}
- for k, v in pairs(value) do
- if v then
- table.insert(keys, k)
- end
- end
+ set = function(info, value)
+ if type(value) == 'string' then
+ return value
+ end
- table.sort(keys)
- return table.concat(keys, ',')
- else
- local result = ''
- for k, v in pairs(value) do
- if v then
- result = result .. k
- end
+ if info.flaglist and info.commalist then
+ local keys = {}
+ for k, v in pairs(value) do
+ if v then
+ table.insert(keys, k)
end
-
- return result
end
- end,
- [OptionTypes.ARRAY] = function(info, value)
- if type(value) == 'string' then
- return value
- end
- if not info.allows_duplicates then
- value = remove_duplicate_values(value)
+ table.sort(keys)
+ return table.concat(keys, ',')
+ else
+ local result = ''
+ for k, v in pairs(value) do
+ if v then
+ result = result .. k
+ end
end
- return table.concat(value, ',')
- end,
- [OptionTypes.MAP] = function(_, value)
- if type(value) == 'string' then
- return value
- end
+ return result
+ end
+ end,
- local result = {}
- for opt_key, opt_value in pairs(value) do
- table.insert(result, string.format('%s:%s', opt_key, opt_value))
- end
+ array = function(info, value)
+ if type(value) == 'string' then
+ return value
+ end
+ if not info.allows_duplicates then
+ value = remove_duplicate_values(value)
+ end
+ return table.concat(value, ',')
+ end,
- table.sort(result)
- return table.concat(result, ',')
- end,
- }
+ map = function(_, value)
+ if type(value) == 'string' then
+ return value
+ end
- return function(name, info, value)
- if value == nil then
- return vim.NIL
+ local result = {}
+ for opt_key, opt_value in pairs(value) do
+ table.insert(result, string.format('%s:%s', opt_key, opt_value))
end
- local option_type = get_option_type(name, info)
- assert_valid_value(name, value, valid_types[option_type])
+ table.sort(result)
+ return table.concat(result, ',')
+ end,
+}
- return to_vim_value[option_type](info, value)
+--- Convert a lua value to a vimoption_T value
+local function convert_value_to_vim(name, info, value)
+ if value == nil then
+ return vim.NIL
end
-end)()
---- Converts a vimoption_T style value to a Lua value
-local convert_value_to_lua = (function()
- -- Map of OptionType to functions that take vimoption_T values and 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,
+ assert_valid_value(name, value, valid_types[info.metatype])
- [OptionTypes.ARRAY] = function(info, value)
- if type(value) == 'table' then
- if not info.allows_duplicates then
- value = remove_duplicate_values(value)
- end
+ return to_vim_value[info.metatype](info, value)
+end
- return value
- end
+-- 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 = {
+ boolean = passthrough,
+ number = passthrough,
+ string = passthrough,
- -- Empty strings mean that there is nothing there,
- -- so empty table should be returned.
- if value == '' then
- return {}
+ array = function(info, value)
+ if type(value) == 'table' then
+ if not info.allows_duplicates then
+ value = remove_duplicate_values(value)
end
- -- Handles unescaped commas in a list.
- if string.find(value, ',,,') then
- local comma_split = vim.split(value, ',,,')
- local left = comma_split[1]
- local right = comma_split[2]
+ return value
+ end
- local result = {}
- vim.list_extend(result, vim.split(left, ','))
- table.insert(result, ',')
- vim.list_extend(result, vim.split(right, ','))
+ -- Empty strings mean that there is nothing there,
+ -- so empty table should be returned.
+ if value == '' then
+ return {}
+ end
- table.sort(result)
+ -- Handles unescaped commas in a list.
+ if string.find(value, ',,,') then
+ local left, right = unpack(vim.split(value, ',,,'))
- return result
- end
+ local result = {}
+ vim.list_extend(result, vim.split(left, ','))
+ table.insert(result, ',')
+ vim.list_extend(result, vim.split(right, ','))
- if string.find(value, ',^,,', 1, true) then
- local comma_split = vim.split(value, ',^,,', true)
- local left = comma_split[1]
- local right = comma_split[2]
+ table.sort(result)
- local result = {}
- vim.list_extend(result, vim.split(left, ','))
- table.insert(result, '^,')
- vim.list_extend(result, vim.split(right, ','))
+ return result
+ end
- table.sort(result)
+ if string.find(value, ',^,,', 1, true) then
+ local left, right = unpack(vim.split(value, ',^,,', true))
- return result
- end
+ local result = {}
+ vim.list_extend(result, vim.split(left, ','))
+ table.insert(result, '^,')
+ vim.list_extend(result, vim.split(right, ','))
- return vim.split(value, ',')
- end,
+ table.sort(result)
- [OptionTypes.SET] = function(info, value)
- if type(value) == 'table' then
- return value
- end
+ return result
+ end
- -- Empty strings mean that there is nothing there,
- -- so empty table should be returned.
- if value == '' then
- return {}
- end
+ return vim.split(value, ',')
+ end,
- assert(info.flaglist, 'That is the only one I know how to handle')
+ set = function(info, value)
+ if type(value) == 'table' then
+ return value
+ end
- if info.flaglist and info.commalist then
- local split_value = vim.split(value, ',')
- local result = {}
- for _, v in ipairs(split_value) do
- result[v] = true
- end
+ -- Empty strings mean that there is nothing there,
+ -- so empty table should be returned.
+ if value == '' then
+ return {}
+ end
- return result
- else
- local result = {}
- for i = 1, #value do
- result[value:sub(i, i)] = true
- end
+ assert(info.flaglist, 'That is the only one I know how to handle')
- return result
+ if info.flaglist and info.commalist then
+ local split_value = vim.split(value, ',')
+ local result = {}
+ for _, v in ipairs(split_value) do
+ result[v] = true
end
- end,
- [OptionTypes.MAP] = function(info, raw_value)
- if type(raw_value) == 'table' then
- return raw_value
+ return result
+ else
+ local result = {}
+ for i = 1, #value do
+ result[value:sub(i, i)] = true
end
- assert(info.commalist, 'Only commas are supported currently')
-
- local result = {}
+ return result
+ end
+ end,
- 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, ':'))
- key = vim.trim(key)
+ map = function(info, raw_value)
+ if type(raw_value) == 'table' then
+ return raw_value
+ end
- result[key] = value
- end
+ assert(info.commalist, 'Only commas are supported currently')
- return result
- end,
- }
+ local result = {}
- return function(name, info, option_value)
- return to_lua_value[get_option_type(name, info)](info, option_value)
- end
-end)()
+ 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, ':'))
+ key = vim.trim(key)
---- Handles the mutation of various different values.
-local value_mutator = function(name, info, current, new, mutator)
- return mutator[get_option_type(name, info)](current, new)
-end
+ result[key] = value
+ end
---- Handles the '^' operator
-local prepend_value = (function()
- local methods = {
- [OptionTypes.NUMBER] = function()
- error("The '^' operator is not currently supported for")
- end,
+ return result
+ end,
+}
- [OptionTypes.STRING] = function(left, right)
- return right .. left
- end,
+--- Converts a vimoption_T style value to a Lua value
+local function convert_value_to_lua(info, option_value)
+ return to_lua_value[info.metatype](info, option_value)
+end
- [OptionTypes.ARRAY] = function(left, right)
- for i = #right, 1, -1 do
- table.insert(left, 1, right[i])
- end
+local prepend_methods = {
+ number = function()
+ error("The '^' operator is not currently supported for")
+ end,
- return left
- end,
+ string = function(left, right)
+ return right .. left
+ end,
- [OptionTypes.MAP] = function(left, right)
- return vim.tbl_extend('force', left, right)
- end,
+ array = function(left, right)
+ for i = #right, 1, -1 do
+ table.insert(left, 1, right[i])
+ end
- [OptionTypes.SET] = function(left, right)
- return vim.tbl_extend('force', left, right)
- end,
- }
+ return left
+ 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
- )
- end
-end)()
+ map = tbl_merge,
+ set = tbl_merge,
+}
---- Handles the '+' operator
-local add_value = (function()
- local methods = {
- [OptionTypes.NUMBER] = function(left, right)
- return left + right
- end,
+--- Handles the '^' operator
+local function prepend_value(info, current, new)
+ return prepend_methods[info.metatype](
+ convert_value_to_lua(info, current),
+ convert_value_to_lua(info, new)
+ )
+end
- [OptionTypes.STRING] = function(left, right)
- return left .. right
- end,
+local add_methods = {
+ number = function(left, right)
+ return left + right
+ end,
- [OptionTypes.ARRAY] = function(left, right)
- for _, v in ipairs(right) do
- table.insert(left, v)
- end
+ string = function(left, right)
+ return left .. right
+ end,
- return left
- end,
+ array = function(left, right)
+ for _, v in ipairs(right) do
+ table.insert(left, v)
+ end
- [OptionTypes.MAP] = function(left, right)
- return vim.tbl_extend('force', left, right)
- end,
+ return left
+ end,
- [OptionTypes.SET] = function(left, right)
- return vim.tbl_extend('force', left, right)
- end,
- }
+ map = tbl_merge,
+ set = tbl_merge,
+}
- 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
- )
- end
-end)()
+--- Handles the '+' operator
+local function add_value(info, current, new)
+ return add_methods[info.metatype](
+ convert_value_to_lua(info, current),
+ convert_value_to_lua(info, new)
+ )
+end
---- Handles the '-' operator
-local remove_value = (function()
- local remove_one_item = function(t, val)
- if vim.tbl_islist(t) then
- local remove_index = nil
- for i, v in ipairs(t) do
- if v == val then
- remove_index = i
- end
+local function remove_one_item(t, val)
+ if vim.tbl_islist(t) then
+ local remove_index = nil
+ for i, v in ipairs(t) do
+ if v == val then
+ remove_index = i
end
+ end
- if remove_index then
- table.remove(t, remove_index)
- end
- else
- t[val] = nil
+ if remove_index then
+ table.remove(t, remove_index)
end
+ else
+ t[val] = nil
end
+end
- local methods = {
- [OptionTypes.NUMBER] = function(left, right)
- return left - right
- end,
-
- [OptionTypes.STRING] = function()
- error('Subtraction not supported for strings.')
- end,
-
- [OptionTypes.ARRAY] = function(left, right)
- if type(right) == 'string' then
- remove_one_item(left, right)
- else
- for _, v in ipairs(right) do
- remove_one_item(left, v)
- end
- end
+local remove_methods = {
+ number = function(left, right)
+ return left - right
+ end,
- return left
- end,
+ string = function()
+ error('Subtraction not supported for strings.')
+ end,
- [OptionTypes.MAP] = function(left, right)
- if type(right) == 'string' then
- left[right] = nil
- else
- for _, v in ipairs(right) do
- left[v] = nil
- end
+ array = function(left, right)
+ if type(right) == 'string' then
+ remove_one_item(left, right)
+ else
+ for _, v in ipairs(right) do
+ remove_one_item(left, v)
end
+ end
- return left
- end,
-
- [OptionTypes.SET] = function(left, right)
- if type(right) == 'string' then
- left[right] = nil
- else
- for _, v in ipairs(right) do
- left[v] = nil
- end
- end
+ return left
+ end,
- return left
- end,
- }
+ map = tbl_remove,
+ set = tbl_remove,
+}
- return function(name, info, current, new)
- return value_mutator(name, info, convert_value_to_lua(name, info, current), new, methods)
- end
-end)()
+--- Handles the '-' operator
+local function remove_value(info, current, new)
+ return remove_methods[info.metatype](convert_value_to_lua(info, current), new)
+end
-local create_option_metatable = function(set_type)
- local set_mt, option_mt
+local function create_option_accessor(scope)
+ local option_mt
- local make_option = function(name, value)
- _setup()
+ local function make_option(name, value)
local info = assert(options_info[name], 'Not a valid option name: ' .. name)
if type(value) == 'table' and getmetatable(value) == option_mt then
@@ -651,67 +483,58 @@ local create_option_metatable = function(set_type)
}, option_mt)
end
- local scope
- if set_type == SET_TYPES.GLOBAL then
- scope = 'global'
- elseif set_type == SET_TYPES.LOCAL then
- scope = 'local'
- end
-
option_mt = {
-- To set a value, instead use:
-- opt[my_option] = value
_set = function(self)
local value = convert_value_to_vim(self._name, self._info, self._value)
a.nvim_set_option_value(self._name, value, { scope = scope })
-
- return self
end,
get = function(self)
- return convert_value_to_lua(self._name, self._info, self._value)
+ return convert_value_to_lua(self._info, self._value)
end,
append = function(self, right)
- return self:__add(right):_set()
+ self._value = add_value(self._info, self._value, right)
+ self:_set()
end,
__add = function(self, right)
- return make_option(self._name, add_value(self._name, self._info, self._value, right))
+ return make_option(self._name, add_value(self._info, self._value, right))
end,
prepend = function(self, right)
- return self:__pow(right):_set()
+ self._value = prepend_value(self._info, self._value, right)
+ self:_set()
end,
__pow = function(self, right)
- return make_option(self._name, prepend_value(self._name, self._info, self._value, right))
+ return make_option(self._name, prepend_value(self._info, self._value, right))
end,
remove = function(self, right)
- return self:__sub(right):_set()
+ self._value = remove_value(self._info, self._value, right)
+ self:_set()
end,
__sub = function(self, right)
- return make_option(self._name, remove_value(self._name, self._info, self._value, right))
+ return make_option(self._name, remove_value(self._info, self._value, right))
end,
}
option_mt.__index = option_mt
- set_mt = {
+ return setmetatable({}, {
__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, {}))
end,
__newindex = function(_, k, v)
- local opt = make_option(k, v)
- opt:_set()
+ make_option(k, v):_set()
end,
- }
-
- return set_mt
+ })
end
-vim.opt = setmetatable({}, create_option_metatable(SET_TYPES.SET))
-vim.opt_local = setmetatable({}, create_option_metatable(SET_TYPES.LOCAL))
-vim.opt_global = setmetatable({}, create_option_metatable(SET_TYPES.GLOBAL))
+vim.opt = create_option_accessor()
+vim.opt_local = create_option_accessor('local')
+vim.opt_global = create_option_accessor('global')
diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua
index 3f71d4f70d..6fd000a029 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -1,7 +1,8 @@
-local if_nil = vim.F.if_nil
+local api, if_nil = vim.api, vim.F.if_nil
local M = {}
+---@enum DiagnosticSeverity
M.severity = {
ERROR = 1,
WARN = 2,
@@ -45,18 +46,24 @@ local bufnr_and_namespace_cacher_mt = {
end,
}
-local diagnostic_cache = setmetatable({}, {
- __index = function(t, bufnr)
- assert(bufnr > 0, 'Invalid buffer number')
- vim.api.nvim_buf_attach(bufnr, false, {
- on_detach = function()
- rawset(t, bufnr, nil) -- clear cache
- end,
- })
- t[bufnr] = {}
- return t[bufnr]
- end,
-})
+local diagnostic_cache
+do
+ local group = api.nvim_create_augroup('DiagnosticBufWipeout', {})
+ diagnostic_cache = setmetatable({}, {
+ __index = function(t, bufnr)
+ assert(bufnr > 0, 'Invalid buffer number')
+ api.nvim_create_autocmd('BufWipeout', {
+ group = group,
+ buffer = bufnr,
+ callback = function()
+ rawset(t, bufnr, nil)
+ end,
+ })
+ t[bufnr] = {}
+ return t[bufnr]
+ end,
+ })
+end
local diagnostic_cache_extmarks = setmetatable({}, bufnr_and_namespace_cacher_mt)
local diagnostic_attached_buffers = {}
@@ -239,25 +246,12 @@ end)()
---@private
local function get_bufnr(bufnr)
if not bufnr or bufnr == 0 then
- return vim.api.nvim_get_current_buf()
+ return api.nvim_get_current_buf()
end
return bufnr
end
---@private
-local function is_disabled(namespace, bufnr)
- local ns = M.get_namespace(namespace)
- if ns.disabled then
- return true
- end
-
- if type(diagnostic_disabled[bufnr]) == 'table' then
- return diagnostic_disabled[bufnr][namespace]
- end
- return diagnostic_disabled[bufnr]
-end
-
----@private
local function diagnostic_lines(diagnostics)
if not diagnostics then
return {}
@@ -293,7 +287,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 = 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
@@ -306,7 +300,7 @@ local function restore_extmarks(bufnr, last)
if not found[extmark[1]] then
local opts = extmark[4]
opts.id = extmark[1]
- pcall(vim.api.nvim_buf_set_extmark, bufnr, ns, extmark[2], extmark[3], opts)
+ pcall(api.nvim_buf_set_extmark, bufnr, ns, extmark[2], extmark[3], opts)
end
end
end
@@ -316,7 +310,7 @@ end
local function save_extmarks(namespace, bufnr)
bufnr = get_bufnr(bufnr)
if not diagnostic_attached_buffers[bufnr] then
- vim.api.nvim_buf_attach(bufnr, false, {
+ api.nvim_buf_attach(bufnr, false, {
on_lines = function(_, _, _, _, _, last)
restore_extmarks(bufnr, last - 1)
end,
@@ -327,7 +321,7 @@ local function save_extmarks(namespace, bufnr)
diagnostic_attached_buffers[bufnr] = true
end
diagnostic_cache_extmarks[bufnr][namespace] =
- vim.api.nvim_buf_get_extmarks(bufnr, namespace, 0, -1, { details = true })
+ api.nvim_buf_get_extmarks(bufnr, namespace, 0, -1, { details = true })
end
local registered_autocmds = {}
@@ -351,19 +345,6 @@ local function execute_scheduled_display(namespace, bufnr)
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' }
@@ -373,8 +354,8 @@ local function schedule_display(namespace, bufnr, args)
local key = make_augroup_key(namespace, bufnr)
if not registered_autocmds[key] then
- local group = vim.api.nvim_create_augroup(key, { clear = true })
- vim.api.nvim_create_autocmd(insert_leave_auto_cmds, {
+ local group = api.nvim_create_augroup(key, { clear = true })
+ api.nvim_create_autocmd(insert_leave_auto_cmds, {
group = group,
buffer = bufnr,
callback = function()
@@ -391,7 +372,7 @@ local function clear_scheduled_display(namespace, bufnr)
local key = make_augroup_key(namespace, bufnr)
if registered_autocmds[key] then
- vim.api.nvim_del_augroup_by_name(key)
+ api.nvim_del_augroup_by_name(key)
registered_autocmds[key] = nil
end
end
@@ -406,7 +387,7 @@ local function get_diagnostics(bufnr, opts, clamp)
-- Memoized results of buf_line_count per bufnr
local buf_line_count = setmetatable({}, {
__index = function(t, k)
- t[k] = vim.api.nvim_buf_line_count(k)
+ t[k] = api.nvim_buf_line_count(k)
return rawget(t, k)
end,
})
@@ -414,7 +395,7 @@ local function get_diagnostics(bufnr, opts, clamp)
---@private
local function add(b, d)
if not opts.lnum or d.lnum == opts.lnum then
- if clamp and vim.api.nvim_buf_is_loaded(b) then
+ if clamp and api.nvim_buf_is_loaded(b) then
local line_count = buf_line_count[b] - 1
if
d.lnum > line_count
@@ -435,32 +416,31 @@ local function get_diagnostics(bufnr, opts, clamp)
end
end
+ ---@private
+ local function add_all_diags(buf, diags)
+ for _, diagnostic in pairs(diags) do
+ add(buf, diagnostic)
+ end
+ end
+
if namespace == nil and bufnr == nil then
for b, t in pairs(diagnostic_cache) do
for _, v in pairs(t) do
- for _, diagnostic in pairs(v) do
- add(b, diagnostic)
- end
+ add_all_diags(b, v)
end
end
elseif namespace == nil then
bufnr = get_bufnr(bufnr)
for iter_namespace in pairs(diagnostic_cache[bufnr]) do
- for _, diagnostic in pairs(diagnostic_cache[bufnr][iter_namespace]) do
- add(bufnr, diagnostic)
- end
+ add_all_diags(bufnr, diagnostic_cache[bufnr][iter_namespace])
end
elseif bufnr == nil then
for b, t in pairs(diagnostic_cache) do
- for _, diagnostic in pairs(t[namespace] or {}) do
- add(b, diagnostic)
- end
+ add_all_diags(b, t[namespace] or {})
end
else
bufnr = get_bufnr(bufnr)
- for _, diagnostic in pairs(diagnostic_cache[bufnr][namespace] or {}) do
- add(bufnr, diagnostic)
- end
+ add_all_diags(bufnr, diagnostic_cache[bufnr][namespace] or {})
end
if opts.severity then
@@ -478,7 +458,7 @@ local function set_list(loclist, opts)
local winnr = opts.winnr or 0
local bufnr
if loclist then
- bufnr = vim.api.nvim_win_get_buf(winnr)
+ bufnr = api.nvim_win_get_buf(winnr)
end
-- Don't clamp line numbers since the quickfix list can already handle line
-- numbers beyond the end of the buffer
@@ -490,7 +470,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 'botright copen')
+ api.nvim_command(loclist and 'lwindow' or 'botright cwindow')
end
end
@@ -499,7 +479,7 @@ local function next_diagnostic(position, search_forward, bufnr, opts, namespace)
position[1] = position[1] - 1
bufnr = get_bufnr(bufnr)
local wrap = vim.F.if_nil(opts.wrap, true)
- local line_count = vim.api.nvim_buf_line_count(bufnr)
+ local line_count = api.nvim_buf_line_count(bufnr)
local diagnostics =
get_diagnostics(bufnr, vim.tbl_extend('keep', opts, { namespace = namespace }), true)
local line_diagnostics = diagnostic_lines(diagnostics)
@@ -513,7 +493,7 @@ 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 = #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)
@@ -549,17 +529,17 @@ local function diagnostic_move_pos(opts, pos)
opts = opts or {}
local float = vim.F.if_nil(opts.float, true)
- local win_id = opts.win_id or vim.api.nvim_get_current_win()
+ local win_id = opts.win_id or api.nvim_get_current_win()
if not pos then
- vim.api.nvim_echo({ { 'No more valid diagnostics to move to', 'WarningMsg' } }, true, {})
+ api.nvim_echo({ { 'No more valid diagnostics to move to', 'WarningMsg' } }, true, {})
return
end
- vim.api.nvim_win_call(win_id, function()
+ 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] })
+ api.nvim_win_set_cursor(win_id, { pos[1] + 1, pos[2] })
-- Open folds under the cursor
vim.cmd('normal! zv')
end)
@@ -568,7 +548,7 @@ local function diagnostic_move_pos(opts, pos)
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),
+ bufnr = api.nvim_win_get_buf(win_id),
scope = 'cursor',
focus = false,
}))
@@ -585,12 +565,12 @@ end
--- followed by namespace configuration, and finally global configuration.
---
--- For example, if a user enables virtual text globally with
---- <pre>
+--- <pre>lua
--- vim.diagnostic.config({ virtual_text = true })
--- </pre>
---
--- and a diagnostic producer sets diagnostics with
---- <pre>
+--- <pre>lua
--- vim.diagnostic.set(ns, 0, diagnostics, { virtual_text = false })
--- </pre>
---
@@ -620,16 +600,20 @@ end
--- * spacing: (number) Amount of empty spaces inserted at the beginning
--- of the virtual text.
--- * prefix: (string) Prepend diagnostic message with prefix.
+--- * suffix: (string or function) Append diagnostic message with suffix.
+--- If a function, it must have the signature (diagnostic) ->
+--- string, where {diagnostic} is of type |diagnostic-structure|.
+--- This can be used to render an LSP diagnostic error code.
--- * format: (function) A function that takes a diagnostic as input and
--- returns a string. The return value is the text used to display
--- the diagnostic. Example:
---- <pre>
---- function(diagnostic)
---- if diagnostic.severity == vim.diagnostic.severity.ERROR then
---- return string.format("E: %s", diagnostic.message)
+--- <pre>lua
+--- function(diagnostic)
+--- if diagnostic.severity == vim.diagnostic.severity.ERROR then
+--- return string.format("E: %s", diagnostic.message)
+--- end
+--- return diagnostic.message
--- end
---- return diagnostic.message
---- end
--- </pre>
--- - signs: (default true) Use signs for diagnostics. Options:
--- * severity: Only show signs for diagnostics matching the given severity
@@ -673,13 +657,13 @@ function M.config(opts, namespace)
if namespace then
for bufnr, v in pairs(diagnostic_cache) do
- if vim.api.nvim_buf_is_loaded(bufnr) and v[namespace] then
+ if api.nvim_buf_is_loaded(bufnr) and v[namespace] then
M.show(namespace, bufnr)
end
end
else
for bufnr, v in pairs(diagnostic_cache) do
- if vim.api.nvim_buf_is_loaded(bufnr) then
+ if api.nvim_buf_is_loaded(bufnr) then
for ns in pairs(v) do
M.show(ns, bufnr)
end
@@ -714,13 +698,14 @@ function M.set(namespace, bufnr, diagnostics, opts)
set_diagnostic_cache(namespace, bufnr, diagnostics)
end
- if vim.api.nvim_buf_is_loaded(bufnr) then
+ if api.nvim_buf_is_loaded(bufnr) then
M.show(namespace, bufnr, nil, opts)
end
- vim.api.nvim_exec_autocmds('DiagnosticChanged', {
+ api.nvim_exec_autocmds('DiagnosticChanged', {
modeline = false,
buffer = bufnr,
+ data = { diagnostics = diagnostics },
})
end
@@ -732,7 +717,7 @@ function M.get_namespace(namespace)
vim.validate({ namespace = { namespace, 'n' } })
if not all_namespaces[namespace] then
local name
- for k, v in pairs(vim.api.nvim_get_namespaces()) do
+ for k, v in pairs(api.nvim_get_namespaces()) do
if namespace == v then
name = k
break
@@ -757,6 +742,18 @@ function M.get_namespaces()
return vim.deepcopy(all_namespaces)
end
+---@class Diagnostic
+---@field buffer number
+---@field lnum number 0-indexed
+---@field end_lnum nil|number 0-indexed
+---@field col number 0-indexed
+---@field end_col nil|number 0-indexed
+---@field severity DiagnosticSeverity
+---@field message string
+---@field source nil|string
+---@field code nil|string
+---@field user_data nil|any arbitrary data plugins can add
+
--- Get current diagnostics.
---
---@param bufnr number|nil Buffer number to get diagnostics from. Use 0 for
@@ -765,7 +762,7 @@ end
--- - namespace: (number) Limit diagnostics to the given namespace.
--- - lnum: (number) Limit diagnostics to the given line number.
--- - severity: See |diagnostic-severity|.
----@return table A list of diagnostic items |diagnostic-structure|.
+---@return Diagnostic[] table A list of diagnostic items |diagnostic-structure|.
function M.get(bufnr, opts)
vim.validate({
bufnr = { bufnr, 'n', true },
@@ -777,22 +774,23 @@ end
--- Get the previous diagnostic closest to the cursor position.
---
----@param opts table See |vim.diagnostic.goto_next()|
----@return table Previous diagnostic
+---@param opts nil|table See |vim.diagnostic.goto_next()|
+---@return Diagnostic|nil Previous diagnostic
function M.get_prev(opts)
opts = opts or {}
- local win_id = opts.win_id or vim.api.nvim_get_current_win()
- local bufnr = vim.api.nvim_win_get_buf(win_id)
- local cursor_position = opts.cursor_position or vim.api.nvim_win_get_cursor(win_id)
+ local win_id = opts.win_id or api.nvim_get_current_win()
+ local bufnr = api.nvim_win_get_buf(win_id)
+ local cursor_position = opts.cursor_position or api.nvim_win_get_cursor(win_id)
return next_diagnostic(cursor_position, false, bufnr, opts, opts.namespace)
end
--- Return the position of the previous diagnostic in the current buffer.
---
----@param opts table See |vim.diagnostic.goto_next()|
----@return table Previous diagnostic position as a (row, col) tuple.
+---@param opts table|nil See |vim.diagnostic.goto_next()|
+---@return table|false Previous diagnostic position as a (row, col) tuple or false if there is no
+--- prior diagnostic
function M.get_prev_pos(opts)
local prev = M.get_prev(opts)
if not prev then
@@ -803,29 +801,30 @@ function M.get_prev_pos(opts)
end
--- Move to the previous diagnostic in the current buffer.
----@param opts table See |vim.diagnostic.goto_next()|
+---@param opts table|nil See |vim.diagnostic.goto_next()|
function M.goto_prev(opts)
return diagnostic_move_pos(opts, M.get_prev_pos(opts))
end
--- Get the next diagnostic closest to the cursor position.
---
----@param opts table See |vim.diagnostic.goto_next()|
----@return table Next diagnostic
+---@param opts table|nil See |vim.diagnostic.goto_next()|
+---@return Diagnostic|nil Next diagnostic
function M.get_next(opts)
opts = opts or {}
- local win_id = opts.win_id or vim.api.nvim_get_current_win()
- local bufnr = vim.api.nvim_win_get_buf(win_id)
- local cursor_position = opts.cursor_position or vim.api.nvim_win_get_cursor(win_id)
+ local win_id = opts.win_id or api.nvim_get_current_win()
+ local bufnr = api.nvim_win_get_buf(win_id)
+ local cursor_position = opts.cursor_position or api.nvim_win_get_cursor(win_id)
return next_diagnostic(cursor_position, true, bufnr, opts, opts.namespace)
end
--- Return the position of the next diagnostic in the current buffer.
---
----@param opts table See |vim.diagnostic.goto_next()|
----@return table Next diagnostic position as a (row, col) tuple.
+---@param opts table|nil See |vim.diagnostic.goto_next()|
+---@return table|false Next diagnostic position as a (row, col) tuple or false if no next
+--- diagnostic.
function M.get_next_pos(opts)
local next = M.get_next(opts)
if not next then
@@ -909,7 +908,7 @@ M.handlers.signs = {
end,
hide = function(namespace, bufnr)
local ns = M.get_namespace(namespace)
- if ns.user_data.sign_group then
+ if ns.user_data.sign_group and api.nvim_buf_is_valid(bufnr) then
vim.fn.sign_unplace(ns.user_data.sign_group, { buffer = bufnr })
end
end,
@@ -937,7 +936,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 = api.nvim_create_namespace('')
end
local underline_ns = ns.user_data.underline_ns
@@ -964,7 +963,9 @@ M.handlers.underline = {
local ns = M.get_namespace(namespace)
if ns.user_data.underline_ns then
diagnostic_cache_extmarks[bufnr][ns.user_data.underline_ns] = {}
- vim.api.nvim_buf_clear_namespace(bufnr, ns.user_data.underline_ns, 0, -1)
+ if api.nvim_buf_is_valid(bufnr) then
+ api.nvim_buf_clear_namespace(bufnr, ns.user_data.underline_ns, 0, -1)
+ end
end
end,
}
@@ -1003,7 +1004,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 = api.nvim_create_namespace('')
end
local virt_text_ns = ns.user_data.virt_text_ns
@@ -1015,7 +1016,7 @@ M.handlers.virtual_text = {
local virt_texts = M._get_virt_text_chunks(line_diagnostics, opts.virtual_text)
if virt_texts then
- vim.api.nvim_buf_set_extmark(bufnr, virt_text_ns, line, 0, {
+ api.nvim_buf_set_extmark(bufnr, virt_text_ns, line, 0, {
hl_mode = 'combine',
virt_text = virt_texts,
})
@@ -1027,7 +1028,9 @@ M.handlers.virtual_text = {
local ns = M.get_namespace(namespace)
if ns.user_data.virt_text_ns then
diagnostic_cache_extmarks[bufnr][ns.user_data.virt_text_ns] = {}
- vim.api.nvim_buf_clear_namespace(bufnr, ns.user_data.virt_text_ns, 0, -1)
+ if api.nvim_buf_is_valid(bufnr) then
+ api.nvim_buf_clear_namespace(bufnr, ns.user_data.virt_text_ns, 0, -1)
+ end
end
end,
}
@@ -1045,6 +1048,7 @@ function M._get_virt_text_chunks(line_diags, opts)
opts = opts or {}
local prefix = opts.prefix or '■'
+ local suffix = opts.suffix or ''
local spacing = opts.spacing or 4
-- Create a little more space between virtual text and contents
@@ -1058,8 +1062,11 @@ function M._get_virt_text_chunks(line_diags, opts)
-- TODO(tjdevries): Allow different servers to be shown first somehow?
-- TODO(tjdevries): Display server name associated with these?
if last.message then
+ if type(suffix) == 'function' then
+ suffix = suffix(last) or ''
+ end
table.insert(virt_texts, {
- string.format('%s %s', prefix, last.message:gsub('\r', ''):gsub('\n', ' ')),
+ string.format('%s %s%s', prefix, last.message:gsub('\r', ''):gsub('\n', ' '), suffix),
virtual_text_highlight_map[last.severity],
})
@@ -1099,6 +1106,27 @@ function M.hide(namespace, bufnr)
end
end
+--- Check whether diagnostics are disabled in a given buffer.
+---
+---@param bufnr number|nil Buffer number, or 0 for current buffer.
+---@param namespace number|nil Diagnostic namespace. When omitted, checks if
+--- all diagnostics are disabled in {bufnr}.
+--- Otherwise, only checks if diagnostics from
+--- {namespace} are disabled.
+---@return boolean
+function M.is_disabled(bufnr, namespace)
+ bufnr = get_bufnr(bufnr)
+ if namespace and M.get_namespace(namespace).disabled then
+ return true
+ end
+
+ if type(diagnostic_disabled[bufnr]) == 'table' then
+ return diagnostic_disabled[bufnr][namespace]
+ end
+
+ return diagnostic_disabled[bufnr] ~= nil
+end
+
--- Display diagnostics for the given namespace and buffer.
---
---@param namespace number|nil Diagnostic namespace. When omitted, show
@@ -1142,7 +1170,7 @@ function M.show(namespace, bufnr, diagnostics, opts)
return
end
- if is_disabled(namespace, bufnr) then
+ if M.is_disabled(bufnr, namespace) then
return
end
@@ -1159,7 +1187,7 @@ function M.show(namespace, bufnr, diagnostics, opts)
if opts.update_in_insert then
clear_scheduled_display(namespace, bufnr)
else
- local mode = vim.api.nvim_get_mode()
+ local mode = api.nvim_get_mode()
if string.sub(mode.mode, 1, 1) == 'i' then
schedule_display(namespace, bufnr, opts)
return
@@ -1226,7 +1254,9 @@ end
--- string, it is prepended to each diagnostic in the window with no
--- highlight.
--- Overrides the setting from |vim.diagnostic.config()|.
----@return tuple ({float_bufnr}, {win_id})
+--- - suffix: Same as {prefix}, but appends the text to the diagnostic instead of
+--- prepending it. Overrides the setting from |vim.diagnostic.config()|.
+---@return number|nil, number|nil: ({float_bufnr}, {win_id})
function M.open_float(opts, ...)
-- Support old (bufnr, opts) signature
local bufnr
@@ -1257,7 +1287,7 @@ function M.open_float(opts, ...)
local lnum, col
if scope == 'line' or scope == 'cursor' then
if not opts.pos then
- local pos = vim.api.nvim_win_get_cursor(0)
+ local pos = api.nvim_win_get_cursor(0)
lnum = pos[1] - 1
col = pos[2]
elseif type(opts.pos) == 'number' then
@@ -1279,7 +1309,7 @@ function M.open_float(opts, ...)
end, diagnostics)
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]
+ local line_length = #api.nvim_buf_get_lines(bufnr, lnum, lnum + 1, true)[1]
diagnostics = vim.tbl_filter(function(d)
return d.lnum == lnum
and math.min(d.col, line_length - 1) <= col
@@ -1311,9 +1341,7 @@ function M.open_float(opts, ...)
vim.validate({
header = {
header,
- function(v)
- return type(v) == 'string' or type(v) == 'table'
- end,
+ { 'string', 'table' },
"'string' or 'table'",
},
})
@@ -1321,11 +1349,11 @@ function M.open_float(opts, ...)
-- Don't insert any lines for an empty string
if string.len(if_nil(header[1], '')) > 0 then
table.insert(lines, header[1])
- table.insert(highlights, { 0, header[2] or 'Bold' })
+ table.insert(highlights, { hlname = header[2] or 'Bold' })
end
elseif #header > 0 then
table.insert(lines, header)
- table.insert(highlights, { 0, 'Bold' })
+ table.insert(highlights, { hlname = 'Bold' })
end
end
@@ -1347,9 +1375,7 @@ function M.open_float(opts, ...)
vim.validate({
prefix = {
prefix_opt,
- function(v)
- return type(v) == 'string' or type(v) == 'table' or type(v) == 'function'
- end,
+ { 'string', 'table', 'function' },
"'string' or 'table' or 'function'",
},
})
@@ -1360,18 +1386,52 @@ function M.open_float(opts, ...)
end
end
+ local suffix_opt = if_nil(opts.suffix, function(diagnostic)
+ return diagnostic.code and string.format(' [%s]', diagnostic.code) or ''
+ end)
+
+ local suffix, suffix_hl_group
+ if suffix_opt then
+ vim.validate({
+ suffix = {
+ suffix_opt,
+ { 'string', 'table', 'function' },
+ "'string' or 'table' or 'function'",
+ },
+ })
+ if type(suffix_opt) == 'string' then
+ suffix, suffix_hl_group = suffix_opt, 'NormalFloat'
+ elseif type(suffix_opt) == 'table' then
+ suffix, suffix_hl_group = suffix_opt[1] or '', suffix_opt[2] or 'NormalFloat'
+ end
+ end
+
for i, diagnostic in ipairs(diagnostics) do
if prefix_opt and type(prefix_opt) == 'function' then
prefix, prefix_hl_group = prefix_opt(diagnostic, i, #diagnostics)
prefix, prefix_hl_group = prefix or '', prefix_hl_group or 'NormalFloat'
end
+ if suffix_opt and type(suffix_opt) == 'function' then
+ suffix, suffix_hl_group = suffix_opt(diagnostic, i, #diagnostics)
+ suffix, suffix_hl_group = suffix or '', suffix_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 })
- for j = 2, #message_lines do
- table.insert(lines, string.rep(' ', #prefix) .. message_lines[j])
- table.insert(highlights, { 0, hiname })
+ for j = 1, #message_lines do
+ local pre = j == 1 and prefix or string.rep(' ', #prefix)
+ local suf = j == #message_lines and suffix or ''
+ table.insert(lines, pre .. message_lines[j] .. suf)
+ table.insert(highlights, {
+ hlname = hiname,
+ prefix = {
+ length = j == 1 and #prefix or 0,
+ hlname = prefix_hl_group,
+ },
+ suffix = {
+ length = j == #message_lines and #suffix or 0,
+ hlname = suffix_hl_group,
+ },
+ })
end
end
@@ -1380,12 +1440,17 @@ function M.open_float(opts, ...)
opts.focus_id = scope
end
local float_bufnr, winnr = require('vim.lsp.util').open_floating_preview(lines, 'plaintext', opts)
- for i, hi in ipairs(highlights) do
- local prefixlen, hiname, prefix_hiname = unpack(hi)
- if prefix_hiname then
- vim.api.nvim_buf_add_highlight(float_bufnr, -1, prefix_hiname, i - 1, 0, prefixlen)
+ for i, hl in ipairs(highlights) do
+ local line = lines[i]
+ local prefix_len = hl.prefix and hl.prefix.length or 0
+ local suffix_len = hl.suffix and hl.suffix.length or 0
+ if prefix_len > 0 then
+ api.nvim_buf_add_highlight(float_bufnr, -1, hl.prefix.hlname, i - 1, 0, prefix_len)
+ end
+ api.nvim_buf_add_highlight(float_bufnr, -1, hl.hlname, i - 1, prefix_len, #line - suffix_len)
+ if suffix_len > 0 then
+ api.nvim_buf_add_highlight(float_bufnr, -1, hl.suffix.hlname, i - 1, #line - suffix_len, -1)
end
- vim.api.nvim_buf_add_highlight(float_bufnr, -1, hiname, i - 1, prefixlen, -1)
end
return float_bufnr, winnr
@@ -1416,10 +1481,15 @@ function M.reset(namespace, bufnr)
M.hide(iter_namespace, iter_bufnr)
end
- vim.api.nvim_exec_autocmds('DiagnosticChanged', {
- modeline = false,
- buffer = iter_bufnr,
- })
+ if api.nvim_buf_is_valid(iter_bufnr) then
+ api.nvim_exec_autocmds('DiagnosticChanged', {
+ modeline = false,
+ buffer = iter_bufnr,
+ data = { diagnostics = {} },
+ })
+ else
+ diagnostic_cache[iter_bufnr] = nil
+ end
end
end
@@ -1522,11 +1592,11 @@ end
---
--- This can be parsed into a diagnostic |diagnostic-structure|
--- with:
---- <pre>
---- local s = "WARNING filename:27:3: Variable 'foo' does not exist"
---- local pattern = "^(%w+) %w+:(%d+):(%d+): (.+)$"
---- local groups = { "severity", "lnum", "col", "message" }
---- vim.diagnostic.match(s, pattern, groups, { WARNING = vim.diagnostic.WARN })
+--- <pre>lua
+--- local s = "WARNING filename:27:3: Variable 'foo' does not exist"
+--- local pattern = "^(%w+) %w+:(%d+):(%d+): (.+)$"
+--- local groups = { "severity", "lnum", "col", "message" }
+--- vim.diagnostic.match(s, pattern, groups, { WARNING = vim.diagnostic.WARN })
--- </pre>
---
---@param str string String to parse diagnostics from.
@@ -1538,7 +1608,7 @@ end
---@param 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.
----@return diagnostic |diagnostic-structure| or `nil` if {pat} fails to match {str}.
+---@return Diagnostic|nil: |diagnostic-structure| or `nil` if {pat} fails to match {str}.
function M.match(str, pat, groups, severity_map, defaults)
vim.validate({
str = { str, 's' },
@@ -1585,7 +1655,7 @@ local errlist_type_map = {
--- passed to |setqflist()| or |setloclist()|.
---
---@param diagnostics table List of diagnostics |diagnostic-structure|.
----@return array of quickfix list items |setqflist-what|
+---@return table[] of quickfix list items |setqflist-what|
function M.toqflist(diagnostics)
vim.validate({
diagnostics = {
@@ -1610,7 +1680,11 @@ function M.toqflist(diagnostics)
end
table.sort(list, function(a, b)
if a.bufnr == b.bufnr then
- return a.lnum < b.lnum
+ if a.lnum == b.lnum then
+ return a.col < b.col
+ else
+ return a.lnum < b.lnum
+ end
else
return a.bufnr < b.bufnr
end
@@ -1622,7 +1696,7 @@ end
---
---@param list table A list of quickfix items from |getqflist()| or
--- |getloclist()|.
----@return array of diagnostics |diagnostic-structure|
+---@return Diagnostic[] array of |diagnostic-structure|
function M.fromqflist(list)
vim.validate({
list = {
diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index 99c98764dd..9293c828b8 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -170,13 +170,14 @@ local extension = {
return require('vim.filetype.detect').bindzone(bufnr, 'dcl')
end,
db = function(path, bufnr)
- return require('vim.filetype.detect').bindzone(bufnr, '')
+ return require('vim.filetype.detect').bindzone(bufnr)
end,
bicep = 'bicep',
bb = 'bitbake',
bbappend = 'bitbake',
bbclass = 'bitbake',
bl = 'blank',
+ blp = 'blueprint',
bsd = 'bsdl',
bsdl = 'bsdl',
bst = 'bst',
@@ -189,6 +190,7 @@ local extension = {
BUILD = 'bzl',
qc = 'c',
cabal = 'cabal',
+ capnp = 'capnp',
cdl = 'cdl',
toc = 'cdrtoc',
cfc = 'cf',
@@ -201,6 +203,7 @@ local extension = {
return require('vim.filetype.detect').change(bufnr)
end,
chs = 'chaskell',
+ chatito = 'chatito',
chopro = 'chordpro',
crd = 'chordpro',
crdpro = 'chordpro',
@@ -255,6 +258,7 @@ local extension = {
cc = function(path, bufnr)
return vim.g.cynlib_syntax_for_cc and 'cynlib' or 'cpp'
end,
+ cql = 'cqlang',
crm = 'crm',
csx = 'cs',
cs = 'cs',
@@ -324,11 +328,7 @@ local extension = {
end,
eex = 'eelixir',
leex = 'eelixir',
- am = function(path, bufnr)
- if not path:lower():find('makefile%.am$') then
- return 'elf'
- end
- end,
+ am = 'elf',
exs = 'elixir',
elm = 'elm',
elv = 'elvish',
@@ -416,15 +416,18 @@ local extension = {
fs = function(path, bufnr)
return require('vim.filetype.detect').fs(bufnr)
end,
+ fsh = 'fsh',
fsi = 'fsharp',
fsx = 'fsharp',
fusion = 'fusion',
gdb = 'gdb',
gdmo = 'gdmo',
mo = 'gdmo',
- tres = 'gdresource',
tscn = 'gdresource',
+ tres = 'gdresource',
gd = 'gdscript',
+ gdshader = 'gdshader',
+ shader = 'gdshader',
ged = 'gedcom',
gmi = 'gemtext',
gemini = 'gemtext',
@@ -444,6 +447,8 @@ local extension = {
gsp = 'gsp',
gjs = 'javascript.glimmer',
gts = 'typescript.glimmer',
+ gyp = 'gyp',
+ gypi = 'gyp',
hack = 'hack',
hackpartial = 'hack',
haml = 'haml',
@@ -469,6 +474,8 @@ local extension = {
hex = 'hex',
['h32'] = 'hex',
hjson = 'hjson',
+ m3u = 'hlsplaylist',
+ m3u8 = 'hlsplaylist',
hog = 'hog',
hws = 'hollywood',
hoon = 'hoon',
@@ -538,6 +545,7 @@ local extension = {
mjs = 'javascript',
javascript = 'javascript',
js = 'javascript',
+ jsm = 'javascript',
cjs = 'javascript',
jsx = 'javascriptreact',
clp = 'jess',
@@ -546,6 +554,7 @@ local extension = {
jov = 'jovial',
jovial = 'jovial',
properties = 'jproperties',
+ jq = 'jq',
slnf = 'json',
json = 'json',
jsonp = 'json',
@@ -554,6 +563,8 @@ local extension = {
['json-patch'] = 'json',
json5 = 'json5',
jsonc = 'jsonc',
+ jsonnet = 'jsonnet',
+ libsonnet = 'jsonnet',
jsp = 'jsp',
jl = 'julia',
kv = 'kivy',
@@ -600,11 +611,14 @@ local extension = {
c = function(path, bufnr)
return require('vim.filetype.detect').lpc(bufnr)
end,
- lsl = 'lsl',
+ lsl = function(path, bufnr)
+ return require('vim.filetype.detect').lsl(bufnr)
+ end,
lss = 'lss',
nse = 'lua',
rockspec = 'lua',
lua = 'lua',
+ lrc = 'lyrics',
m = function(path, bufnr)
return require('vim.filetype.detect').m(bufnr)
end,
@@ -644,6 +658,9 @@ local extension = {
dmt = 'maxima',
wxm = 'maxima',
mel = 'mel',
+ mmd = 'mermaid',
+ mmdc = 'mermaid',
+ mermaid = 'mermaid',
mf = 'mf',
mgl = 'mgl',
mgp = 'mgp',
@@ -662,6 +679,7 @@ local extension = {
DEF = 'modula2',
['m2'] = 'modula2',
mi = 'modula2',
+ lm3 = 'modula3',
ssc = 'monk',
monk = 'monk',
tsc = 'monk',
@@ -695,6 +713,9 @@ local extension = {
nanorc = 'nanorc',
ncf = 'ncf',
nginx = 'nginx',
+ nim = 'nim',
+ nims = 'nim',
+ nimble = 'nim',
ninja = 'ninja',
nix = 'nix',
nqc = 'nqc',
@@ -707,6 +728,10 @@ local extension = {
nsi = 'nsis',
nsh = 'nsis',
obj = 'obj',
+ obl = 'obse',
+ obse = 'obse',
+ oblivion = 'obse',
+ obscript = 'obse',
mlt = 'ocaml',
mly = 'ocaml',
mll = 'ocaml',
@@ -720,6 +745,7 @@ local extension = {
opam = 'opam',
['or'] = 'openroad',
scad = 'openscad',
+ ovpn = 'openvpn',
ora = 'ora',
org = 'org',
org_archive = 'org',
@@ -741,6 +767,7 @@ local extension = {
php = 'php',
phpt = 'php',
phtml = 'php',
+ theme = 'php',
pike = 'pike',
pmod = 'pike',
rcp = 'pilrc',
@@ -758,6 +785,7 @@ local extension = {
po = 'po',
pot = 'po',
pod = 'pod',
+ filter = 'poefilter',
pk = 'poke',
ps = 'postscr',
epsi = 'postscr',
@@ -900,7 +928,9 @@ local extension = {
sig = function(path, bufnr)
return require('vim.filetype.detect').sig(bufnr)
end,
- sil = 'sil',
+ sil = function(path, bufnr)
+ return require('vim.filetype.detect').sil(bufnr)
+ end,
sim = 'simula',
['s85'] = 'sinda',
sin = 'sinda',
@@ -916,12 +946,14 @@ local extension = {
ice = 'slice',
score = 'slrnsc',
sol = 'solidity',
+ smali = 'smali',
tpl = 'smarty',
ihlp = 'smcl',
smcl = 'smcl',
hlp = 'smcl',
smith = 'smith',
smt = 'smith',
+ smithy = 'smithy',
sml = 'sml',
spt = 'snobol4',
sno = 'snobol4',
@@ -950,6 +982,9 @@ local extension = {
srec = 'srec',
mot = 'srec',
['s19'] = 'srec',
+ srt = 'srt',
+ ssa = 'ssa',
+ ass = 'ssa',
st = 'st',
imata = 'stata',
['do'] = 'stata',
@@ -987,6 +1022,7 @@ local extension = {
texinfo = 'texinfo',
text = 'text',
tfvars = 'terraform-vars',
+ thrift = 'thrift',
tla = 'tla',
tli = 'tli',
toml = 'toml',
@@ -1002,6 +1038,8 @@ local extension = {
ts = function(path, bufnr)
return M.getlines(bufnr, 1):find('<%?xml') and 'xml' or 'typescript'
end,
+ mts = 'typescript',
+ cts = 'typescript',
tsx = 'typescriptreact',
uc = 'uc',
uit = 'uil',
@@ -1011,6 +1049,12 @@ local extension = {
dsm = 'vb',
ctl = 'vb',
vbs = 'vb',
+ vdf = 'vdf',
+ vdmpp = 'vdmpp',
+ vpp = 'vdmpp',
+ vdmrt = 'vdmrt',
+ vdmsl = 'vdmsl',
+ vdm = 'vdmsl',
vr = 'vera',
vri = 'vera',
vrh = 'vera',
@@ -1023,6 +1067,7 @@ local extension = {
hdl = 'vhdl',
vho = 'vhdl',
vbe = 'vhdl',
+ tape = 'vhs',
vim = 'vim',
vba = 'vim',
mar = 'vmasm',
@@ -1032,6 +1077,7 @@ local extension = {
vue = 'vue',
wat = 'wast',
wast = 'wast',
+ wdl = 'wdl',
wm = 'webmacro',
wbt = 'winbatch',
wml = 'wml',
@@ -1078,6 +1124,7 @@ local extension = {
yang = 'yang',
['z8a'] = 'z8a',
zig = 'zig',
+ zir = 'zir',
zu = 'zimbu',
zut = 'zimbutempl',
zsh = 'zsh',
@@ -1276,9 +1323,9 @@ local filename = {
['GNUmakefile.am'] = 'automake',
['named.root'] = 'bindzone',
WORKSPACE = 'bzl',
+ ['WORKSPACE.bzlmod'] = 'bzl',
BUILD = 'bzl',
['cabal.project'] = 'cabalproject',
- [vim.env.HOME .. '/cabal.config'] = 'cabalconfig',
['cabal.config'] = 'cabalconfig',
calendar = 'calendar',
catalog = 'catalog',
@@ -1332,13 +1379,13 @@ local filename = {
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',
+ ['.editorconfig'] = 'editorconfig',
['elinks.conf'] = 'elinks',
['mix.lock'] = 'elixir',
['filter-rules'] = 'elmfilt',
@@ -1371,6 +1418,8 @@ local filename = {
['EDIT_DESCRIPTION'] = 'gitcommit',
['.gitconfig'] = 'gitconfig',
['.gitmodules'] = 'gitconfig',
+ ['.gitattributes'] = 'gitattributes',
+ ['.gitignore'] = 'gitignore',
['gitolite.conf'] = 'gitolite',
['git-rebase-todo'] = 'gitrebase',
gkrellmrc = 'gkrellmrc',
@@ -1379,6 +1428,7 @@ local filename = {
gnashpluginrc = 'gnash',
gnashrc = 'gnash',
['.gnuplot'] = 'gnuplot',
+ ['go.sum'] = 'gosum',
['go.work'] = 'gowork',
['.gprc'] = 'gp',
['/.gnupg/gpg.conf'] = 'gpg',
@@ -1408,11 +1458,15 @@ local filename = {
['ipf.conf'] = 'ipfilter',
['ipf6.conf'] = 'ipfilter',
['ipf.rules'] = 'ipfilter',
- ['.eslintrc'] = 'json',
- ['.babelrc'] = 'json',
['Pipfile.lock'] = 'json',
['.firebaserc'] = 'json',
['.prettierrc'] = 'json',
+ ['.babelrc'] = 'jsonc',
+ ['.eslintrc'] = 'jsonc',
+ ['.hintrc'] = 'jsonc',
+ ['.jsfmtrc'] = 'jsonc',
+ ['.jshintrc'] = 'jsonc',
+ ['.swrc'] = 'jsonc',
Kconfig = 'kconfig',
['Kconfig.debug'] = 'kconfig',
['lftp.conf'] = 'lftp',
@@ -1427,6 +1481,10 @@ local filename = {
['.sawfishrc'] = 'lisp',
['/etc/login.access'] = 'loginaccess',
['/etc/login.defs'] = 'logindefs',
+ ['.lsl'] = function(path, bufnr)
+ return require('vim.filetype.detect').lsl(bufnr)
+ end,
+ ['.luacheckrc'] = 'lua',
['lynx.cfg'] = 'lynx',
['m3overrides'] = 'm3build',
['m3makefile'] = 'm3build',
@@ -1472,8 +1530,11 @@ local filename = {
['/etc/shadow-'] = 'passwd',
['/etc/shadow'] = 'passwd',
['/etc/passwd.edit'] = 'passwd',
+ ['latexmkrc'] = 'perl',
+ ['.latexmkrc'] = 'perl',
['pf.conf'] = 'pf',
['main.cf'] = 'pfmain',
+ ['main.cf.proto'] = 'pfmain',
pinerc = 'pine',
['.pinercex'] = 'pine',
['.pinerc'] = 'pine',
@@ -1506,6 +1567,9 @@ local filename = {
['.pythonstartup'] = 'python',
['.pythonrc'] = 'python',
SConstruct = 'python',
+ ['.Rprofile'] = 'r',
+ ['Rprofile'] = 'r',
+ ['Rprofile.site'] = 'r',
ratpoisonrc = 'ratpoison',
['.ratpoisonrc'] = 'ratpoison',
inputrc = 'readline',
@@ -1632,6 +1696,8 @@ local filename = {
fglrxrc = 'xml',
['/etc/blkid.tab'] = 'xml',
['/etc/blkid.tab.old'] = 'xml',
+ ['.clang-format'] = 'yaml',
+ ['.clang-tidy'] = 'yaml',
['/etc/zprofile'] = 'zsh',
['.zlogin'] = 'zsh',
['.zlogout'] = 'zsh',
@@ -1684,6 +1750,7 @@ local pattern = {
['.*/meta%-.*/conf/.*%.conf'] = 'bitbake',
['bzr_log%..*'] = 'bzr',
['.*enlightenment/.*%.cfg'] = 'c',
+ ['${HOME}/cabal%.config'] = 'cabalconfig',
['cabal%.project%..*'] = starsetf('cabalproject'),
['.*/%.calendar/.*'] = starsetf('calendar'),
['.*/share/calendar/.*/calendar%..*'] = starsetf('calendar'),
@@ -1702,7 +1769,7 @@ local pattern = {
{ priority = -1 },
},
['[cC]hange[lL]og.*'] = starsetf(function(path, bufnr)
- require('vim.filetype.detect').changelog(bufnr)
+ return require('vim.filetype.detect').changelog(bufnr)
end),
['.*%.%.ch'] = 'chill',
['.*%.cmake%.in'] = 'cmake',
@@ -1744,6 +1811,8 @@ local pattern = {
['.*/etc/DIR_COLORS'] = 'dircolors',
['.*/etc/dnsmasq%.conf'] = 'dnsmasq',
['php%.ini%-.*'] = 'dosini',
+ ['.*/%.aws/config'] = 'confini',
+ ['.*/%.aws/credentials'] = 'confini',
['.*/etc/pacman%.conf'] = 'confini',
['.*/etc/yum%.conf'] = 'dosini',
['.*lvs'] = 'dracula',
@@ -1808,26 +1877,21 @@ local pattern = {
['.*/%.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,
+ ['${XDG_CONFIG_HOME}/git/config'] = 'gitconfig',
+ ['.*%.git/info/attributes'] = 'gitattributes',
+ ['.*/etc/gitattributes'] = 'gitattributes',
+ ['.*/%.config/git/attributes'] = 'gitattributes',
+ ['${XDG_CONFIG_HOME}/git/attributes'] = 'gitattributes',
+ ['.*%.git/info/exclude'] = 'gitignore',
+ ['.*/%.config/git/ignore'] = 'gitignore',
+ ['${XDG_CONFIG_HOME}/git/ignore'] = 'gitignore',
['%.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,
+ ['${GNUPGHOME}/options'] = 'gpg',
+ ['${GNUPGHOME}/gpg%.conf'] = 'gpg',
['.*/etc/group'] = 'group',
['.*/etc/gshadow'] = 'group',
['.*/etc/group%.edit'] = 'group',
@@ -1841,7 +1905,7 @@ local pattern = {
['.*/etc/grub%.conf'] = 'grub',
-- gtkrc* and .gtkrc*
['%.?gtkrc.*'] = starsetf('gtkrc'),
- [vim.env.VIMRUNTIME .. '/doc/.*%.txt'] = 'help',
+ ['${VIMRUNTIME}/doc/.*%.txt'] = 'help',
['hg%-editor%-.*%.txt'] = 'hgcommit',
['.*/etc/host%.conf'] = 'hostconf',
['.*/etc/hosts%.deny'] = 'hostsaccess',
@@ -1855,7 +1919,9 @@ local pattern = {
['Prl.*%..*'] = starsetf('jam'),
['.*%.properties_..'] = 'jproperties',
['.*%.properties_.._..'] = 'jproperties',
+ ['org%.eclipse%..*%.prefs'] = 'jproperties',
['.*%.properties_.._.._.*'] = starsetf('jproperties'),
+ ['[jt]sconfig.*%.json'] = 'jsonc',
['Kconfig%..*'] = starsetf('kconfig'),
['.*%.[Ss][Uu][Bb]'] = 'krl',
['lilo%.conf.*'] = starsetf('lilo'),
@@ -2009,6 +2075,7 @@ local pattern = {
['.*%.ml%.cppo'] = 'ocaml',
['.*%.mli%.cppo'] = 'ocaml',
['.*%.opam%.template'] = 'opam',
+ ['.*/openvpn/.*/.*%.conf'] = 'openvpn',
['.*%.[Oo][Pp][Ll]'] = 'opl',
['.*/etc/pam%.conf'] = 'pamconf',
['.*/etc/pam%.d/.*'] = starsetf('pamconf'),
@@ -2245,13 +2312,14 @@ end
--- Filename patterns can specify an optional priority to resolve cases when a
--- file path matches multiple patterns. Higher priorities are matched first.
--- When omitted, the priority defaults to 0.
+--- A pattern can contain environment variables of the form "${SOME_VAR}" that will
+--- be automatically expanded. If the environment variable is not set, the pattern
+--- won't be matched.
---
--- See $VIMRUNTIME/lua/vim/filetype.lua for more examples.
---
---- Note that Lua filetype detection is disabled when |g:do_legacy_filetype| is set.
----
--- Example:
---- <pre>
+--- <pre>lua
--- vim.filetype.add({
--- extension = {
--- foo = 'fooscript',
@@ -2273,6 +2341,8 @@ end
--- ['.*/etc/foo/.*'] = 'fooscript',
--- -- Using an optional priority
--- ['.*/etc/foo/.*%.conf'] = { 'dosini', { priority = 10 } },
+--- -- A pattern containing an environment variable
+--- ['${XDG_CONFIG_HOME}/foo/git'] = 'git',
--- ['README.(%a+)$'] = function(path, bufnr, ext)
--- if ext == 'md' then
--- return 'markdown'
@@ -2284,8 +2354,8 @@ end
--- })
--- </pre>
---
---- To add a fallback match on contents (see |new-filetype-scripts|), use
---- <pre>
+--- To add a fallback match on contents, use
+--- <pre>lua
--- vim.filetype.add {
--- pattern = {
--- ['.*'] = {
@@ -2346,8 +2416,28 @@ local function dispatch(ft, path, bufnr, ...)
end
end
+-- Lookup table/cache for patterns that contain an environment variable pattern, e.g. ${SOME_VAR}.
+local expand_env_lookup = {}
+
---@private
local function match_pattern(name, path, tail, pat)
+ if expand_env_lookup[pat] == nil then
+ expand_env_lookup[pat] = pat:find('%${') ~= nil
+ end
+ if expand_env_lookup[pat] then
+ local return_early
+ pat = pat:gsub('%${(%S-)}', function(env)
+ -- If an environment variable is present in the pattern but not set, there is no match
+ if not vim.env[env] then
+ return_early = true
+ return nil
+ end
+ return vim.env[env]
+ end)
+ if return_early then
+ return false
+ end
+ end
-- If the pattern contains a / match against the full path, otherwise just the tail
local fullpat = '^' .. pat .. '$'
local matches
@@ -2377,7 +2467,7 @@ end
--- Each of the three options is specified using a key to the single argument of this function.
--- Example:
---
---- <pre>
+--- <pre>lua
--- -- Using a buffer number
--- vim.filetype.match({ buf = 42 })
---
diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua
index 2be9dcff88..edffdde9c7 100644
--- a/runtime/lua/vim/filetype/detect.lua
+++ b/runtime/lua/vim/filetype/detect.lua
@@ -181,7 +181,7 @@ function M.cls(bufnr)
return vim.g.filetype_cls
end
local line = getlines(bufnr, 1)
- if line:find('^%%') then
+ if line:find('^[%%\\]') then
return 'tex'
elseif line:find('^#') and line:lower():find('rexx') then
return 'rexx'
@@ -634,6 +634,19 @@ function M.lpc(bufnr)
return 'c'
end
+function M.lsl(bufnr)
+ if vim.g.filetype_lsl then
+ return vim.g.filetype_lsl
+ end
+
+ local line = nextnonblank(bufnr, 1)
+ if findany(line, { '^%s*%%', ':%s*trait%s*$' }) then
+ return 'larch'
+ else
+ return 'lsl'
+ end
+end
+
function M.m(bufnr)
if vim.g.filetype_m then
return vim.g.filetype_m
@@ -1084,11 +1097,10 @@ 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*|',
+ '|%w+|',
'%+%s%w*%s{',
'%*ar%s',
})
@@ -1153,13 +1165,14 @@ function M.sh(path, contents, name)
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
+ elseif vim.g.bash_is_sh or matchregex(name, [[\<\(bash\|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
+ -- Ubuntu links sh to dash
+ elseif matchregex(name, [[\<\(sh\|dash\)\>]]) then
on_detect = function(b)
vim.b[b].is_sh = 1
vim.b[b].is_kornshell = nil
@@ -1194,6 +1207,19 @@ function M.shell(path, contents, name)
return name
end
+-- Swift Intermediate Language or SILE
+function M.sil(bufnr)
+ for _, line in ipairs(getlines(bufnr, 1, 100)) do
+ if line:find('^%s*[\\%%]') then
+ return 'sile'
+ elseif line:find('^%s*%S') then
+ return 'sil'
+ end
+ end
+ -- No clue, default to "sil"
+ return 'sil'
+end
+
-- SMIL or SNMP MIB file
function M.smi(bufnr)
local line = getlines(bufnr, 1)
@@ -1230,17 +1256,15 @@ end
-- 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
+ local matched, _, format = getlines(bufnr, 1):find('^%%&%s*(%a+)')
+ if matched 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
+ -- Default value, may be changed later:
+ format = vim.g.tex_flavor or 'plaintex'
+
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\>]]
@@ -1249,26 +1273,25 @@ function M.tex(path, bufnr)
-- 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
+ for _, line in ipairs(getlines(bufnr, i, i + 1000)) do
+ if matchregex(line, [[\c^\s*\\\%(]] .. lpat .. [[\)]]) then
return 'tex'
- elseif cpat_match then
+ elseif matchregex(line, [[\c^\s*\\\%(]] .. cpat .. [[\)]]) 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 -- if matched
+
+ -- Translation from formats to file types. TODO: add AMSTeX, RevTex, others?
+ if format == 'plain' then
+ return 'plaintex'
+ elseif format == 'plaintex' or format == 'context' then
+ return format
+ else
+ -- Probably LaTeX
+ return 'tex'
end
end
@@ -1434,8 +1457,8 @@ local function match_from_hashbang(contents, path)
name = 'wish'
end
- if matchregex(name, [[^\(bash\d*\|\|ksh\d*\|sh\)\>]]) then
- -- Bourne-like shell scripts: bash bash2 ksh ksh93 sh
+ if matchregex(name, [[^\(bash\d*\|dash\|ksh\d*\|sh\)\>]]) then
+ -- Bourne-like shell scripts: bash bash2 dash 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')
diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua
index ce845eda15..a0d2c4c339 100644
--- a/runtime/lua/vim/fs.lua
+++ b/runtime/lua/vim/fs.lua
@@ -1,9 +1,11 @@
local M = {}
+local iswin = vim.loop.os_uname().sysname == 'Windows_NT'
+
--- Iterate over all the parents of the given file or directory.
---
--- Example:
---- <pre>
+--- <pre>lua
--- 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
@@ -40,7 +42,19 @@ function M.dirname(file)
if file == nil then
return nil
end
- return vim.fn.fnamemodify(file, ':h')
+ vim.validate({ file = { file, 's' } })
+ if iswin and file:match('^%w:[\\/]?$') then
+ return (file:gsub('\\', '/'))
+ elseif not file:match('[\\/]') then
+ return '.'
+ elseif file == '/' or file:match('^/[^/]+$') then
+ return '/'
+ end
+ local dir = file:match('[/\\]$') and file:sub(1, #file - 1) or file:match('^([/\\]?.+)[/\\]')
+ if iswin and dir:match('^%w:$') then
+ return dir .. '/'
+ end
+ return (dir:gsub('\\', '/'))
end
--- Return the basename of the given file or directory
@@ -48,21 +62,75 @@ end
---@param file (string) File or directory
---@return (string) Basename of {file}
function M.basename(file)
- return vim.fn.fnamemodify(file, ':t')
+ if file == nil then
+ return nil
+ end
+ vim.validate({ file = { file, 's' } })
+ if iswin and file:match('^%w:[\\/]?$') then
+ return ''
+ end
+ return file:match('[/\\]$') and '' or (file:match('[^\\/]*$'):gsub('\\', '/'))
+end
+
+---@private
+local function join_paths(...)
+ return (table.concat({ ... }, '/'):gsub('//+', '/'))
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()|.
+--- @param opts table|nil Optional keyword arguments:
+--- - depth: integer|nil How deep the traverse (default 1)
+--- - skip: (fun(dir_name: string): boolean)|nil Predicate
+--- to control traversal. Return false to stop searching the current directory.
+--- Only useful when depth > 1
+---
---@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))
+function M.dir(path, opts)
+ opts = opts or {}
+
+ vim.validate({
+ path = { path, { 'string' } },
+ depth = { opts.depth, { 'number' }, true },
+ skip = { opts.skip, { 'function' }, true },
+ })
+
+ if not opts.depth or opts.depth == 1 then
+ return function(fs)
+ return vim.loop.fs_scandir_next(fs)
+ end,
+ vim.loop.fs_scandir(M.normalize(path))
+ end
+
+ --- @async
+ return coroutine.wrap(function()
+ local dirs = { { path, 1 } }
+ while #dirs > 0 do
+ local dir0, level = unpack(table.remove(dirs, 1))
+ local dir = level == 1 and dir0 or join_paths(path, dir0)
+ local fs = vim.loop.fs_scandir(M.normalize(dir))
+ while fs do
+ local name, t = vim.loop.fs_scandir_next(fs)
+ if not name then
+ break
+ end
+ local f = level == 1 and name or join_paths(dir0, name)
+ coroutine.yield(f, t)
+ if
+ opts.depth
+ and level < opts.depth
+ and t == 'directory'
+ and (not opts.skip or opts.skip(f) ~= false)
+ then
+ dirs[#dirs + 1] = { f, level + 1 }
+ end
+ end
+ end
+ end)
end
--- Find files or directories in the given path.
@@ -73,14 +141,18 @@ end
--- 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
+--- The search can be narrowed to find only files 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 names (string|table|fun(name: string): boolean) Names of the files
+--- and directories to find.
+--- Must be base names, paths and globs are not supported.
+--- The function is called per file and directory within the
+--- traversed directories to test if they match {names}.
+---
---@param opts (table) Optional keyword arguments:
--- - path (string): Path to begin searching from. If
---- omitted, the current working directory is used.
+--- omitted, the |current-directory| is used.
--- - upward (boolean, default false): If true, search
--- upward through parent directories. Otherwise,
--- search through child directories
@@ -89,16 +161,16 @@ end
--- 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
+--- files and directories that match {names} 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
+---@return (table) Normalized paths |vim.fs.normalize()| of all matching files or directories
function M.find(names, opts)
opts = opts or {}
vim.validate({
- names = { names, { 's', 't' } },
+ names = { names, { 's', 't', 'f' } },
path = { opts.path, 's', true },
upward = { opts.upward, 'b', true },
stop = { opts.stop, 's', true },
@@ -123,18 +195,31 @@ function M.find(names, opts)
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
+ local test
+
+ if type(names) == 'function' then
+ test = function(p)
+ local t = {}
+ for name, type in M.dir(p) do
+ if names(name) and (not opts.type or opts.type == type) then
+ table.insert(t, join_paths(p, name))
+ end
end
+ return t
end
+ else
+ test = function(p)
+ local t = {}
+ for _, name in ipairs(names) do
+ local f = join_paths(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
+ return t
+ end
end
for _, match in ipairs(test(path)) do
@@ -162,17 +247,25 @@ function M.find(names, opts)
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
+ for other, type_ in M.dir(dir) do
+ local f = join_paths(dir, other)
+ if type(names) == 'function' then
+ if names(other) and (not opts.type or opts.type == type_) then
if add(f) then
return matches
end
end
+ else
+ 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
end
- if type == 'directory' then
+ if type_ == 'directory' then
dirs[#dirs + 1] = f
end
end
@@ -187,23 +280,29 @@ end
--- backslash (\\) characters are converted to forward slashes (/). Environment
--- variables are also expanded.
---
---- Example:
---- <pre>
---- vim.fs.normalize('C:\\Users\\jdoe')
---- => 'C:/Users/jdoe'
+--- Examples:
+--- <pre>lua
+--- vim.fs.normalize('C:\\\\Users\\\\jdoe')
+--- --> 'C:/Users/jdoe'
---
---- vim.fs.normalize('~/src/neovim')
---- => '/home/jdoe/src/neovim'
+--- vim.fs.normalize('~/src/neovim')
+--- --> '/home/jdoe/src/neovim'
---
---- vim.fs.normalize('$XDG_CONFIG_HOME/nvim/init.vim')
---- => '/Users/jdoe/.config/nvim/init.vim'
+--- 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('\\', '/'))
+ return (
+ path
+ :gsub('^~$', vim.loop.os_homedir())
+ :gsub('^~/', vim.loop.os_homedir() .. '/')
+ :gsub('%$([%w_]+)', vim.loop.os_getenv)
+ :gsub('\\', '/')
+ )
end
return M
diff --git a/runtime/lua/vim/health.lua b/runtime/lua/vim/health.lua
index b875da0abc..044880e076 100644
--- a/runtime/lua/vim/health.lua
+++ b/runtime/lua/vim/health.lua
@@ -23,7 +23,20 @@ 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.-$', '')
+
+ -- Get full path, make sure all slashes are '/'
+ path = vim.fs.normalize(path)
+
+ -- Remove everything up to the last /lua/ folder
+ path = path:gsub('^.*/lua/', '')
+
+ -- Remove the filename (health.lua)
+ path = vim.fn.fnamemodify(path, ':h')
+
+ -- Change slashes to dots
+ path = path:gsub('/', '.')
+
+ return path
else
-- Vim: transform "../autoload/health/provider.vim" into "provider"
return vim.fn.fnamemodify(path, ':t:r')
diff --git a/runtime/lua/vim/highlight.lua b/runtime/lua/vim/highlight.lua
index ddd504a0e0..20ad48dd27 100644
--- a/runtime/lua/vim/highlight.lua
+++ b/runtime/lua/vim/highlight.lua
@@ -5,6 +5,7 @@ local M = {}
M.priorities = {
syntax = 50,
treesitter = 100,
+ semantic_tokens = 125,
diagnostics = 150,
user = 200,
}
@@ -41,7 +42,7 @@ end
---@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)
+-- - regtype type of range (see |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)
diff --git a/runtime/lua/vim/inspect.lua b/runtime/lua/vim/inspect.lua
index 0a53fb203b..c232f69590 100644
--- a/runtime/lua/vim/inspect.lua
+++ b/runtime/lua/vim/inspect.lua
@@ -89,8 +89,38 @@ local function escape(str)
)
end
+-- List of lua keywords
+local luaKeywords = {
+ ['and'] = true,
+ ['break'] = true,
+ ['do'] = true,
+ ['else'] = true,
+ ['elseif'] = true,
+ ['end'] = true,
+ ['false'] = true,
+ ['for'] = true,
+ ['function'] = true,
+ ['goto'] = true,
+ ['if'] = true,
+ ['in'] = true,
+ ['local'] = true,
+ ['nil'] = true,
+ ['not'] = true,
+ ['or'] = true,
+ ['repeat'] = true,
+ ['return'] = true,
+ ['then'] = true,
+ ['true'] = true,
+ ['until'] = true,
+ ['while'] = true,
+}
+
local function isIdentifier(str)
- return type(str) == 'string' and not not str:match('^[_%a][_%a%d]*$')
+ return type(str) == 'string'
+ -- identifier must start with a letter and underscore, and be followed by letters, numbers, and underscores
+ and not not str:match('^[_%a][_%a%d]*$')
+ -- lua keywords are not valid identifiers
+ and not luaKeywords[str]
end
local flr = math.floor
diff --git a/runtime/lua/vim/keymap.lua b/runtime/lua/vim/keymap.lua
index 219de16b5c..ef1c66ea20 100644
--- a/runtime/lua/vim/keymap.lua
+++ b/runtime/lua/vim/keymap.lua
@@ -2,7 +2,7 @@ local keymap = {}
--- Add a new |mapping|.
--- Examples:
---- <pre>
+--- <pre>lua
--- -- Can add mapping to Lua functions
--- vim.keymap.set('n', 'lhs', function() print("real lua function") end)
---
@@ -21,13 +21,13 @@ local keymap = {}
--- </pre>
---
--- Note that in a mapping like:
---- <pre>
+--- <pre>lua
--- 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:
---- <pre>
+--- <pre>lua
--- vim.keymap.set('n', 'asdf', function() return require('jkl').my_fun() end)
--- </pre>
---
@@ -36,17 +36,17 @@ local keymap = {}
---@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.
--
----@param opts table A table of |:map-arguments|.
---- + Accepts options accepted by the {opts} parameter in |nvim_set_keymap()|,
---- with the following notable differences:
---- - replace_keycodes: Defaults to `true` if "expr" is `true`.
---- - noremap: Always overridden with the inverse of "remap" (see below).
---- + In addition to those options, the table accepts the following keys:
---- - buffer: (number or boolean) Add a mapping to the given buffer.
---- When `0` or `true`, use the current buffer.
---- - remap: (boolean) Make the mapping recursive.
---- This is the inverse of the "noremap" option from |nvim_set_keymap()|.
---- Defaults to `false`.
+---@param opts table|nil A table of |:map-arguments|.
+--- + Accepts options accepted by the {opts} parameter in |nvim_set_keymap()|,
+--- with the following notable differences:
+--- - replace_keycodes: Defaults to `true` if "expr" is `true`.
+--- - noremap: Always overridden with the inverse of "remap" (see below).
+--- + In addition to those options, the table accepts the following keys:
+--- - buffer: (number or boolean) Add a mapping to the given buffer.
+--- When `0` or `true`, use the current buffer.
+--- - remap: (boolean) Make the mapping recursive.
+--- This is the inverse of the "noremap" option from |nvim_set_keymap()|.
+--- Defaults to `false`.
---@see |nvim_set_keymap()|
function keymap.set(mode, lhs, rhs, opts)
vim.validate({
@@ -57,7 +57,6 @@ function keymap.set(mode, lhs, rhs, opts)
})
opts = vim.deepcopy(opts) or {}
- local is_rhs_luaref = type(rhs) == 'function'
mode = type(mode) == 'string' and { mode } or mode
if opts.expr and opts.replace_keycodes ~= false then
@@ -73,7 +72,7 @@ function keymap.set(mode, lhs, rhs, opts)
opts.remap = nil
end
- if is_rhs_luaref then
+ if type(rhs) == 'function' then
opts.callback = rhs
rhs = ''
end
@@ -94,14 +93,14 @@ end
--- Remove an existing mapping.
--- Examples:
---- <pre>
+--- <pre>lua
--- vim.keymap.del('n', 'lhs')
---
--- vim.keymap.del({'n', 'i', 'v'}, '<leader>w', { buffer = 5 })
--- </pre>
----@param 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.
+---@param opts table|nil A table of optional arguments:
+--- - buffer: (number or boolean) Remove a mapping from the given buffer.
+--- When "true" or 0, use the current buffer.
---@see |vim.keymap.set()|
---
function keymap.del(modes, lhs, opts)
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index fd64c1a495..c5392ac154 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -4,8 +4,8 @@ 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 semantic_tokens = require('vim.lsp.semantic_tokens')
-local vim = vim
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,
@@ -26,6 +26,7 @@ local lsp = {
buf = require('vim.lsp.buf'),
diagnostic = require('vim.lsp.diagnostic'),
codelens = require('vim.lsp.codelens'),
+ semantic_tokens = semantic_tokens,
util = util,
-- Allow raw RPC access.
@@ -57,6 +58,8 @@ lsp._request_name_to_capability = {
['textDocument/formatting'] = { 'documentFormattingProvider' },
['textDocument/completion'] = { 'completionProvider' },
['textDocument/documentHighlight'] = { 'documentHighlightProvider' },
+ ['textDocument/semanticTokens/full'] = { 'semanticTokensProvider' },
+ ['textDocument/semanticTokens/full/delta'] = { 'semanticTokensProvider' },
}
-- TODO improve handling of scratch buffers with LSP attached.
@@ -64,7 +67,7 @@ lsp._request_name_to_capability = {
---@private
--- Concatenates and writes a list of strings to the Vim error buffer.
---
----@param {...} (List of strings) List to write to the buffer
+---@param {...} table[] List to write to the buffer
local function err_message(...)
nvim_err_writeln(table.concat(vim.tbl_flatten({ ... })))
nvim_command('redraw')
@@ -73,7 +76,7 @@ end
---@private
--- Returns the buffer number for the given {bufnr}.
---
----@param bufnr (number) Buffer number to resolve. Defaults to the current
+---@param bufnr (number|nil) Buffer number to resolve. Defaults to the current
---buffer if not given.
---@returns bufnr (number) Number of requested buffer
local function resolve_bufnr(bufnr)
@@ -241,9 +244,9 @@ end
---@private
--- Augments a validator function with support for optional (nil) values.
---
----@param fn (function(v)) The original validator function; should return a
+---@param fn (fun(v)) The original validator function; should return a
---bool.
----@returns (function(v)) The augmented function. Also returns true if {v} is
+---@returns (fun(v)) The augmented function. Also returns true if {v} is
---`nil`.
local function optional_validator(fn)
return function(v)
@@ -289,7 +292,12 @@ local function validate_client_config(config)
'flags.debounce_text_changes must be a number with the debounce time in milliseconds'
)
- local cmd, cmd_args = lsp._cmd_parts(config.cmd)
+ local cmd, cmd_args
+ if type(config.cmd) == 'function' then
+ cmd = config.cmd
+ else
+ cmd, cmd_args = lsp._cmd_parts(config.cmd)
+ end
local offset_encoding = valid_encodings.UTF16
if config.offset_encoding then
offset_encoding = validate_encoding(config.offset_encoding)
@@ -809,8 +817,7 @@ end
--- Attaches the current buffer to the client.
---
--- Example:
----
---- <pre>
+--- <pre>lua
--- vim.lsp.start({
--- name = 'my-server-name',
--- cmd = {'name-of-language-server-executable'},
@@ -818,25 +825,18 @@ end
--- })
--- </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.
+--- See |vim.lsp.start_client()| for all available options. The most important are:
---
---- `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.
+--- - `name` arbitrary name for the LSP client. Should be unique per language server.
+--- - `cmd` command (in list form) used to start the language server. Must be absolute, or found on
+--- `$PATH`. 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` list of `{ uri:string, name: string }` tables specifying the project root
+--- folders used by the language server. If `nil` the property is derived from `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
@@ -844,26 +844,35 @@ end
---
---
--- To ensure a language server is only started for languages it can handle,
---- make sure to call |vim.lsp.start| within a |FileType| autocmd.
+--- 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 config table Same configuration as documented in |vim.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
+--- - bufnr (number)
+--- Buffer handle to attach to if starting or re-using a
+--- client (0 for current).
+---@return number|nil 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()
+ config.name = config.name
+ if not config.name and type(config.cmd) == 'table' then
+ config.name = config.cmd[1] and vim.fs.basename(config.cmd[1]) or nil
+ end
+ local bufnr = opts.bufnr
+ if bufnr == nil or bufnr == 0 then
+ bufnr = api.nvim_get_current_buf()
+ end
for _, clients in ipairs({ uninitialized_clients, lsp.get_active_clients() }) do
for _, client in pairs(clients) do
if reuse_client(client, config) then
@@ -888,110 +897,114 @@ end
--
--- Starts and initializes a client with the given configuration.
---
---- Parameter `cmd` is required.
----
---- The following parameters describe fields in the {config} table.
----
----
----@param cmd: (required, string or list treated like |jobstart()|) Base command
---- that initiates the LSP client.
----
----@param cmd_cwd: (string, default=|getcwd()|) Directory to launch
---- the `cmd` process. Not related to `root_dir`.
----
----@param cmd_env: (table) Environment flags to pass to the LSP on
---- spawn. Can be specified using keys like a map or as a list with `k=v`
---- pairs or both. Non-string values are coerced to string.
---- Example:
---- <pre>
---- { "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
---- the LSP spec.
----
----@param capabilities Map overriding the default capabilities defined by
---- |vim.lsp.protocol.make_client_capabilities()|, passed to the language
---- server on initialization. Hint: use make_client_capabilities() and modify
---- its result.
---- - Note: To send an empty dictionary use
---- `{[vim.type_idx]=vim.types.dictionary}`, else it will be encoded as an
---- array.
+--- Field `cmd` in {config} is required.
+---
+---@param config (table) Configuration for the server:
+--- - cmd: (table|string|fun(dispatchers: table):table) command string or
+--- list treated like |jobstart()|. The command must launch the language server
+--- process. `cmd` can also be a function that creates an RPC client.
+--- The function receives a dispatchers table and must return a table with the
+--- functions `request`, `notify`, `is_closing` and `terminate`
+--- See |vim.lsp.rpc.request()| and |vim.lsp.rpc.notify()|
+--- For TCP there is a built-in rpc client factory: |vim.lsp.rpc.connect()|
+---
+--- - cmd_cwd: (string, default=|getcwd()|) Directory to launch
+--- the `cmd` 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 with `k=v`
+--- pairs or both. Non-string values are coerced to string.
+--- Example:
+--- <pre>
+--- { "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; }
+--- </pre>
+---
+--- - 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 rootPath will be
+--- derived from the first workspace folder in this list. See `workspaceFolders` in
+--- the LSP spec.
+---
+--- - capabilities: Map overriding the default capabilities defined by
+--- |vim.lsp.protocol.make_client_capabilities()|, passed to the language
+--- server on initialization. Hint: use make_client_capabilities() and modify
+--- its result.
+--- - Note: To send an empty dictionary use
+--- `{[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 case-sensitive.
+---
+--- - commands: table Table that maps string of clientside commands to user-defined functions.
+--- Commands passed to start_client take precedence over the global command registry. Each key
+--- must be a unique command name, and the value is a function which is called if any LSP action
+--- (code action, code lenses, ...) triggers the command.
+---
+--- - init_options Values to pass in the initialization request
+--- as `initializationOptions`. See `initialize` in the LSP spec.
+---
+--- - name: (string, default=client-id) Name in log messages.
+---
+--- - get_language_id: function(bufnr, filetype) -> language ID as string.
+--- Defaults to the filetype.
+---
+--- - offset_encoding: (default="utf-16") One of "utf-8", "utf-16",
+--- or "utf-32" which is the encoding that the LSP server expects. Client does
+--- not verify this is correct.
+---
+--- - on_error: Callback with parameters (code, ...), invoked
+--- when the client operation throws an error. `code` is a number describing
+--- the error. Other arguments may be passed depending on the error kind. See
+--- `vim.lsp.rpc.client_errors` for possible errors.
+--- Use `vim.lsp.rpc.client_errors[code]` to get human-friendly name.
+---
+--- - before_init: Callback with parameters (initialize_params, config)
+--- invoked before the LSP "initialize" phase, where `params` contains the
+--- parameters being sent to the server and `config` is the config that was
+--- passed to |vim.lsp.start_client()|. You can use this to modify parameters before
+--- they are sent.
+---
+--- - on_init: Callback (client, initialize_result) invoked after LSP
+--- "initialize", where `result` is a table of `capabilities` and anything else
+--- the server may send. For example, clangd sends
+--- `initialize_result.offsetEncoding` if `capabilities.offsetEncoding` was
+--- sent to it. You can only modify the `client.offset_encoding` here before
+--- any notifications are sent. Most language servers expect to be sent client specified settings after
+--- initialization. Neovim does not make this assumption. A
+--- `workspace/didChangeConfiguration` notification should be sent
+--- to the server during on_init.
+---
+--- - on_exit Callback (code, signal, client_id) invoked on client
+--- exit.
+--- - code: exit code of the process
+--- - signal: number describing the signal used to terminate (if any)
+--- - client_id: client handle
---
----@param handlers Map of language server method names to |lsp-handler|
+--- - on_attach: Callback (client, bufnr) invoked when client
+--- attaches to a buffer.
---
----@param settings Map with language server specific settings. These are
---- returned to the language server if requested via `workspace/configuration`.
---- Keys are case-sensitive.
+--- - trace: ("off" | "messages" | "verbose" | nil) passed directly to the language
+--- server in the initialize request. Invalid/empty values will default to "off"
---
----@param commands table Table that maps string of clientside commands to user-defined functions.
---- Commands passed to start_client take precedence over the global command registry. Each key
---- must be a unique command name, and the value is a function which is called if any LSP action
---- (code action, code lenses, ...) triggers the command.
+--- - 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 150): Debounce didChange
+--- notifications to the server by the given number in milliseconds. No debounce
+--- occurs if nil
+--- - exit_timeout (number|boolean, default false): Milliseconds to wait for server to
+--- exit cleanly after sending the "shutdown" request before sending kill -15.
+--- If set to false, nvim exits immediately after sending the "shutdown" request to the server.
---
----@param init_options Values to pass in the initialization request
---- as `initializationOptions`. See `initialize` in the LSP spec.
----
----@param name (string, default=client-id) Name in log messages.
----
----@param get_language_id function(bufnr, filetype) -> language ID as string.
---- Defaults to the filetype.
----
----@param offset_encoding (default="utf-16") One of "utf-8", "utf-16",
---- or "utf-32" which is the encoding that the LSP server expects. Client does
---- not verify this is correct.
----
----@param on_error Callback with parameters (code, ...), invoked
---- when the client operation throws an error. `code` is a number describing
---- the error. Other arguments may be passed depending on the error kind. See
---- |vim.lsp.rpc.client_errors| for possible errors.
---- Use `vim.lsp.rpc.client_errors[code]` to get human-friendly name.
----
----@param before_init Callback with parameters (initialize_params, config)
---- invoked before the LSP "initialize" phase, where `params` contains the
---- parameters being sent to the server and `config` is the config that was
---- passed to |vim.lsp.start_client()|. You can use this to modify parameters before
---- they are sent.
----
----@param on_init Callback (client, initialize_result) invoked after LSP
---- "initialize", where `result` is a table of `capabilities` and anything else
---- the server may send. For example, clangd sends
---- `initialize_result.offsetEncoding` if `capabilities.offsetEncoding` was
---- sent to it. You can only modify the `client.offset_encoding` here before
---- any notifications are sent. Most language servers expect to be sent client specified settings after
---- initialization. Neovim does not make this assumption. A
---- `workspace/didChangeConfiguration` notification should be sent
---- to the server during on_init.
----
----@param on_exit Callback (code, signal, client_id) invoked on client
---- exit.
---- - code: exit code of the process
---- - signal: number describing the signal used to terminate (if any)
---- - client_id: client handle
----
----@param on_attach Callback (client, bufnr) invoked when client
---- attaches to a buffer.
----
----@param trace: "off" | "messages" | "verbose" | nil passed directly to the language
---- 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 150): Debounce didChange
---- notifications to the server by the given number in milliseconds. No debounce
---- occurs if nil
---- - exit_timeout (number|boolean, default false): Milliseconds to wait for server to
---- exit cleanly after sending the 'shutdown' request before sending kill -15.
---- If set to false, nvim exits immediately after sending the 'shutdown' request to the server.
----
----@param 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.
---
---@returns Client id. |vim.lsp.get_client_by_id()| Note: client may not be
--- fully initialized. Use `on_init` to do any actions once
@@ -1065,7 +1078,7 @@ function lsp.start_client(config)
---
---@param code (number) Error code
---@param err (...) Other arguments may be passed depending on the error kind
- ---@see |vim.lsp.rpc.client_errors| for possible errors. Use
+ ---@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()
@@ -1134,41 +1147,47 @@ function lsp.start_client(config)
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)
+ client_ids[client_id] = nil
+ if vim.tbl_isempty(client_ids) then
+ unset_defaults(bufnr)
+ end
end)
end
end
- local client = active_clients[client_id] and active_clients[client_id]
- or uninitialized_clients[client_id]
- active_clients[client_id] = nil
- uninitialized_clients[client_id] = nil
- -- Client can be absent if executable starts, but initialize fails
- -- init/attach won't have happened
- if client then
- changetracking.reset(client)
- 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)
- vim.schedule(function()
+ -- Schedule the deletion of the client object so that it exists in the execution of LspDetach
+ -- autocommands
+ vim.schedule(function()
+ local client = active_clients[client_id] and active_clients[client_id]
+ or uninitialized_clients[client_id]
+ active_clients[client_id] = nil
+ uninitialized_clients[client_id] = nil
+
+ -- Client can be absent if executable starts, but initialize fails
+ -- init/attach won't have happened
+ if client then
+ changetracking.reset(client)
+ 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)
vim.notify(msg, vim.log.levels.WARN)
- end)
- end
+ end
+ end)
end
-- Start the RPC client.
- local rpc = lsp_rpc.start(cmd, cmd_args, dispatch, {
- cwd = config.cmd_cwd,
- env = config.cmd_env,
- detached = config.detached,
- })
+ local rpc
+ if type(cmd) == 'function' then
+ rpc = cmd(dispatch)
+ else
+ rpc = lsp_rpc.start(cmd, cmd_args, dispatch, {
+ cwd = config.cmd_cwd,
+ env = config.cmd_env,
+ detached = config.detached,
+ })
+ end
-- Return nil if client fails to start
if not rpc then
@@ -1269,8 +1288,6 @@ function lsp.start_client(config)
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
-- These are the cleaned up capabilities we use for dynamically deciding
-- when to send certain events to clients.
@@ -1348,7 +1365,7 @@ function lsp.start_client(config)
---
---@param method (string) LSP method name.
---@param params (table) LSP request params.
- ---@param handler (function, optional) Response |lsp-handler| for this method.
+ ---@param handler (function|nil) Response |lsp-handler| for this method.
---@param bufnr (number) Buffer handle (0 for current).
---@returns ({status}, [request_id]): {status} is a bool indicating
---whether the request was successful. If it is `false`, then it will
@@ -1359,8 +1376,10 @@ function lsp.start_client(config)
---@see |vim.lsp.buf_request()|
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))
+ handler = assert(
+ resolve_handler(method),
+ 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)
@@ -1378,7 +1397,7 @@ function lsp.start_client(config)
nvim_exec_autocmds('User', { pattern = 'LspRequest', modeline = false })
end)
- if success then
+ if success and request_id then
client.requests[request_id] = { type = 'pending', bufnr = bufnr, method = method }
nvim_exec_autocmds('User', { pattern = 'LspRequest', modeline = false })
end
@@ -1393,8 +1412,8 @@ function lsp.start_client(config)
---
---@param method (string) LSP method name.
---@param params (table) LSP request params.
- ---@param timeout_ms (number, optional, default=1000) Maximum time in
- ---milliseconds to wait for a result.
+ ---@param timeout_ms (number|nil) Maximum time in milliseconds to wait for
+ --- a result. Defaults to 1000
---@param bufnr (number) Buffer handle (0 for current).
---@returns { err=err, result=result }, a dictionary, where `err` and `result` come from the |lsp-handler|.
---On timeout, cancel or error, returns `(nil, err)` where `err` is a
@@ -1417,7 +1436,9 @@ function lsp.start_client(config)
end, 10)
if not wait_result then
- client.cancel_request(request_id)
+ if request_id then
+ client.cancel_request(request_id)
+ end
return nil, wait_result_reason[reason]
end
return request_result
@@ -1464,14 +1485,13 @@ function lsp.start_client(config)
--- you request to stop a client which has previously been requested to
--- shutdown, it will automatically escalate and force shutdown.
---
- ---@param force (bool, optional)
+ ---@param force boolean|nil
function client.stop(force)
- local handle = rpc.handle
- if handle:is_closing() then
+ if rpc.is_closing() then
return
end
if force or not client.initialized or graceful_shutdown_failed then
- handle:kill(15)
+ rpc.terminate()
return
end
-- Sending a signal after a process has exited is acceptable.
@@ -1480,7 +1500,7 @@ function lsp.start_client(config)
rpc.notify('exit')
else
-- If there was an error in the shutdown request, then term to be safe.
- handle:kill(15)
+ rpc.terminate()
graceful_shutdown_failed = true
end
end)
@@ -1492,7 +1512,7 @@ function lsp.start_client(config)
---@returns (bool) true if client is stopped or in the process of being
---stopped; false otherwise
function client.is_stopped()
- return rpc.handle:is_closing()
+ return rpc.is_closing()
end
---@private
@@ -1513,6 +1533,16 @@ function lsp.start_client(config)
-- TODO(ashkan) handle errors.
pcall(config.on_attach, client, bufnr)
end
+
+ -- schedule the initialization of semantic tokens to give the above
+ -- on_attach and LspAttach callbacks the ability to schedule wrap the
+ -- opt-out (deleting the semanticTokensProvider from capabilities)
+ vim.schedule(function()
+ if vim.tbl_get(client.server_capabilities, 'semanticTokensProvider', 'full') then
+ semantic_tokens.start(bufnr, client.id)
+ end
+ end)
+
client.attached_buffers[bufnr] = true
end
@@ -1526,15 +1556,21 @@ 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
- end
- util.buf_versions[bufnr] = changedtick
- changetracking.send_changes(bufnr, firstline, lastline, new_lastline)
+ 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
+ changetracking.send_changes(bufnr, firstline, lastline, new_lastline)
+ end
end
---@private
@@ -1598,9 +1634,37 @@ function lsp.buf_attach_client(bufnr, client_id)
all_buffer_active_clients[bufnr] = buffer_client_ids
local uri = vim.uri_from_bufnr(bufnr)
- local augroup = ('lsp_c_%d_b_%d_did_save'):format(client_id, bufnr)
+ local augroup = ('lsp_c_%d_b_%d_save'):format(client_id, bufnr)
+ local group = api.nvim_create_augroup(augroup, { clear = true })
+ api.nvim_create_autocmd('BufWritePre', {
+ group = group,
+ buffer = bufnr,
+ desc = 'vim.lsp: textDocument/willSave',
+ callback = function(ctx)
+ for_each_buffer_client(ctx.buf, function(client)
+ local params = {
+ textDocument = {
+ uri = uri,
+ },
+ reason = protocol.TextDocumentSaveReason.Manual,
+ }
+ if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'willSave') then
+ client.notify('textDocument/willSave', params)
+ end
+ if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'willSaveWaitUntil') then
+ local result, err =
+ client.request_sync('textDocument/willSaveWaitUntil', params, 1000, ctx.buf)
+ if result and result.result then
+ util.apply_text_edits(result.result, ctx.buf, client.offset_encoding)
+ elseif err then
+ log.error(vim.inspect(err))
+ end
+ end
+ end)
+ end,
+ })
api.nvim_create_autocmd('BufWritePost', {
- group = api.nvim_create_augroup(augroup, { clear = true }),
+ group = group,
buffer = bufnr,
desc = 'vim.lsp: textDocument/didSave handler',
callback = function(ctx)
@@ -1627,6 +1691,7 @@ function lsp.buf_attach_client(bufnr, client_id)
if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'openClose') then
client.notify('textDocument/didClose', params)
end
+ client.attached_buffers[bufnr] = nil
end)
util.buf_versions[bufnr] = nil
all_buffer_active_clients[bufnr] = nil
@@ -1737,16 +1802,15 @@ end
---
--- You can also use the `stop()` function on a |vim.lsp.client| object.
--- To stop all clients:
----
---- <pre>
+--- <pre>lua
--- vim.lsp.stop_client(vim.lsp.get_active_clients())
--- </pre>
---
--- By default asks the server to shutdown, unless stop was requested
--- already for this client, then force-shutdown is attempted.
---
----@param client_id client id or |vim.lsp.client| object, or list thereof
----@param force boolean (optional) shutdown forcefully
+---@param client_id number|table id or |vim.lsp.client| object, or list thereof
+---@param force boolean|nil shutdown forcefully
function lsp.stop_client(client_id, force)
local ids = type(client_id) == 'table' and client_id or { client_id }
for _, id in ipairs(ids) do
@@ -1760,10 +1824,16 @@ function lsp.stop_client(client_id, force)
end
end
+---@class vim.lsp.get_active_clients.filter
+---@field id number|nil Match clients by id
+---@field bufnr number|nil match clients attached to the given buffer
+---@field name string|nil match clients by name
+
--- Get active clients.
---
----@param filter (table|nil) A table with key-value pairs used to filter the
---- returned clients. The available keys are:
+---@param filter vim.lsp.get_active_clients.filter|nil (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
@@ -1780,7 +1850,8 @@ function lsp.get_active_clients(filter)
for client_id in pairs(t) do
local client = active_clients[client_id]
if
- (filter.id == nil or client.id == filter.id)
+ client
+ and (filter.id == nil or client.id == filter.id)
and (filter.name == nil or client.name == filter.name)
then
clients[#clients + 1] = client
@@ -1911,7 +1982,7 @@ end
---
---@param bufnr (number) Buffer handle, or 0 for current.
---@param method (string) LSP method name
----@param params (optional, table) Parameters to send to the server
+---@param params (table|nil) Parameters to send to the server
---@param callback (function) The callback to call when all requests are finished.
-- Unlike `buf_request`, this will collect all the responses from each server instead of handling them.
-- A map of client_id:request_result will be provided to the callback
@@ -1953,9 +2024,9 @@ end
---
---@param bufnr (number) Buffer handle, or 0 for current.
---@param method (string) LSP method name
----@param params (optional, table) Parameters to send to the server
----@param timeout_ms (optional, number, default=1000) Maximum time in
---- milliseconds to wait for a result.
+---@param params (table|nil) Parameters to send to the server
+---@param timeout_ms (number|nil) Maximum time in milliseconds to wait for a
+--- result. Defaults to 1000
---
---@returns Map of client_id:request_result. On timeout, cancel or error,
--- returns `(nil, err)` where `err` is a string describing the failure
@@ -1980,9 +2051,9 @@ function lsp.buf_request_sync(bufnr, method, params, timeout_ms)
end
--- Send a notification to a server
----@param bufnr [number] (optional): The number of the buffer
----@param method [string]: Name of the request method
----@param params [string]: Arguments to send to the server
+---@param bufnr (number|nil) The number of the buffer
+---@param method (string) Name of the request method
+---@param params (any) Arguments to send to the server
---
---@returns true if any client returns true; false otherwise
function lsp.buf_notify(bufnr, method, params)
@@ -2023,8 +2094,8 @@ end
---@see |complete-items|
---@see |CompleteDone|
---
----@param findstart 0 or 1, decides behavior
----@param base If findstart=0, text to match against
+---@param findstart number 0 or 1, decides behavior
+---@param base number findstart=0, text to match against
---
---@returns (number) Decided by {findstart}:
--- - findstart=0: column where the completion starts, or -2 or -3
@@ -2153,8 +2224,8 @@ end
--- Otherwise, uses "workspace/symbol". If no results are returned from
--- any LSP servers, falls back to using built-in tags.
---
----@param pattern Pattern used to find a workspace symbol
----@param flags See |tag-function|
+---@param pattern string Pattern used to find a workspace symbol
+---@param flags string See |tag-function|
---
---@returns A list of matching tags
function lsp.tagfunc(...)
@@ -2163,7 +2234,7 @@ end
---Checks whether a client is stopped.
---
----@param client_id (Number)
+---@param client_id (number)
---@returns true if client is stopped, false otherwise.
function lsp.client_is_stopped(client_id)
return active_clients[client_id] == nil
@@ -2172,7 +2243,7 @@ end
--- Gets a map of client_id:client pairs for the given buffer, where each value
--- is a |vim.lsp.client| object.
---
----@param bufnr (optional, number): Buffer handle, or 0 for current
+---@param bufnr (number|nil): 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)
@@ -2201,7 +2272,7 @@ lsp.log_levels = log.levels
---
---@see |vim.lsp.log_levels|
---
----@param level [number|string] the case insensitive level name or number
+---@param level (number|string) the case insensitive level name or number
function lsp.set_log_level(level)
if type(level) == 'string' or type(level) == 'number' then
log.set_level(level)
@@ -2222,7 +2293,7 @@ end
---@param fn function Function to run on each client attached to buffer
--- {bufnr}. The function takes the client, client ID, and
--- buffer number as arguments. Example:
---- <pre>
+--- <pre>lua
--- vim.lsp.for_each_buffer_client(0, function(client, client_id, bufnr)
--- print(vim.inspect(client))
--- end)
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index 6a070928d9..6ac885c78f 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -1,4 +1,3 @@
-local vim = vim
local api = vim.api
local validate = vim.validate
local util = require('vim.lsp.util')
@@ -111,7 +110,7 @@ end
--- about the context in which a completion was triggered (how it was triggered,
--- and by which trigger character, if applicable)
---
----@see |vim.lsp.protocol.constants.CompletionTriggerKind|
+---@see vim.lsp.protocol.constants.CompletionTriggerKind
function M.completion(context)
local params = util.make_position_params()
params.context = context
@@ -119,35 +118,30 @@ function M.completion(context)
end
---@private
---- If there is more than one client that supports the given method,
---- asks the user to select one.
---
----@returns The client that the user selected or nil
-local function select_client(method, on_choice)
- validate({
- on_choice = { on_choice, 'function', false },
- })
- local clients = vim.tbl_values(vim.lsp.buf_get_clients())
- clients = vim.tbl_filter(function(client)
- return client.supports_method(method)
- end, clients)
- -- better UX when choices are always in the same order (between restarts)
- table.sort(clients, function(a, b)
- return a.name < b.name
- end)
-
- if #clients > 1 then
- vim.ui.select(clients, {
- prompt = 'Select a language server:',
- format_item = function(client)
- return client.name
- end,
- }, on_choice)
- elseif #clients < 1 then
- on_choice(nil)
- else
- on_choice(clients[1])
- end
+---@return table {start={row, col}, end={row, col}} using (1, 0) indexing
+local function range_from_selection()
+ -- TODO: Use `vim.region()` instead https://github.com/neovim/neovim/pull/13896
+
+ -- [bufnum, lnum, col, off]; both row and column 1-indexed
+ local start = vim.fn.getpos('v')
+ local end_ = vim.fn.getpos('.')
+ local start_row = start[2]
+ local start_col = start[3]
+ local end_row = end_[2]
+ local end_col = end_[3]
+
+ -- A user can start visual selection at the end and move backwards
+ -- Normalize the range to start < end
+ if start_row == end_row and end_col < start_col then
+ end_col, start_col = start_col, end_col
+ elseif end_row < start_row then
+ start_row, end_row = end_row, start_row
+ start_col, end_col = end_col, start_col
+ end
+ return {
+ ['start'] = { start_row, start_col - 1 },
+ ['end'] = { end_row, end_col - 1 },
+ }
end
--- Formats a buffer using the attached (and optionally filtered) language
@@ -168,11 +162,11 @@ end
--- 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>lua
+--- -- Never request typescript-language-server for formatting
+--- vim.lsp.buf.format {
+--- filter = function(client) return client.name ~= "tsserver" end
+--- }
--- </pre>
---
--- - async boolean|nil
@@ -184,7 +178,12 @@ end
--- 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.
-
+---
+--- - range (table|nil) Range to format.
+--- Table must contain `start` and `end` keys with {row, col} tuples using
+--- (1,0) indexing.
+--- Defaults to current selection in visual mode
+--- Defaults to `nil` in other modes, formatting the full buffer
function M.format(options)
options = options or {}
local bufnr = options.bufnr or api.nvim_get_current_buf()
@@ -198,24 +197,40 @@ function M.format(options)
clients = vim.tbl_filter(options.filter, clients)
end
+ local mode = api.nvim_get_mode().mode
+ local range = options.range
+ if not range and mode == 'v' or mode == 'V' then
+ range = range_from_selection()
+ end
+ local method = range and 'textDocument/rangeFormatting' or 'textDocument/formatting'
+
clients = vim.tbl_filter(function(client)
- return client.supports_method('textDocument/formatting')
+ return client.supports_method(method)
end, clients)
if #clients == 0 then
vim.notify('[LSP] Format request failed, no matching language servers.')
end
+ ---@private
+ local function set_range(client, params)
+ if range then
+ local range_params =
+ util.make_given_range_params(range.start, range['end'], bufnr, client.offset_encoding)
+ params.range = range_params.range
+ end
+ return params
+ 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']
+ local params = set_range(client, util.make_formatting_params(options.formatting_options))
+ client.request(method, params, function(...)
+ local handler = client.handlers[method] or vim.lsp.handlers[method]
handler(...)
do_format(next(clients, idx))
end, bufnr)
@@ -224,8 +239,8 @@ function M.format(options)
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)
+ local params = set_range(client, util.make_formatting_params(options.formatting_options))
+ local result, err = client.request_sync(method, params, timeout_ms, bufnr)
if result and result.result then
util.apply_text_edits(result.result, bufnr, client.offset_encoding)
elseif err then
@@ -235,138 +250,6 @@ function M.format(options)
end
end
---- Formats the current buffer.
----
----@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 = api.nvim_get_current_buf()
- select_client('textDocument/formatting', function(client)
- if client == nil then
- return
- end
-
- return client.request('textDocument/formatting', params, nil, bufnr)
- end)
-end
-
---- Performs |vim.lsp.buf.formatting()| synchronously.
----
---- Useful for running on save, to make sure buffer is formatted prior to being
---- saved. {timeout_ms} is passed on to |vim.lsp.buf_request_sync()|. Example:
----
---- <pre>
---- autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_sync()
---- </pre>
----
----@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 = api.nvim_get_current_buf()
- select_client('textDocument/formatting', function(client)
- if client == nil then
- return
- end
-
- local result, err = client.request_sync('textDocument/formatting', params, timeout_ms, bufnr)
- if result and result.result then
- util.apply_text_edits(result.result, bufnr, client.offset_encoding)
- elseif err then
- vim.notify('vim.lsp.buf.formatting_sync: ' .. err, vim.log.levels.WARN)
- end
- end)
-end
-
---- Formats the current buffer by sequentially requesting formatting from attached clients.
----
---- Useful when multiple clients with formatting capability are attached.
----
---- Since it's synchronous, can be used for running on save, to make sure buffer is formatted
---- prior to being saved. {timeout_ms} is passed on to the |vim.lsp.client| `request_sync` method.
---- Example:
---- <pre>
---- vim.api.nvim_command[[autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_seq_sync()]]
---- </pre>
----
----@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)
- 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
- -- if the client exists, move to the end of the list
- for i, client in pairs(clients) do
- if client.name == client_name then
- table.insert(clients, table.remove(clients, i))
- break
- end
- end
- end
-
- -- loop through the clients and make synchronous formatting requests
- for _, client in pairs(clients) do
- 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,
- api.nvim_get_current_buf()
- )
- if result and result.result then
- util.apply_text_edits(result.result, bufnr, client.offset_encoding)
- elseif err then
- vim.notify(
- string.format('vim.lsp.buf.formatting_seq_sync: (%s) %s', client.name, err),
- vim.log.levels.WARN
- )
- end
- end
- end
-end
-
---- Formats a given range.
----
----@param options Table with valid `FormattingOptions` entries.
----@param start_pos ({number, number}, optional) mark-indexed position.
----Defaults to the start of the last visual selection.
----@param end_pos ({number, number}, optional) mark-indexed position.
----Defaults to the end of the last visual selection.
-function M.range_formatting(options, start_pos, end_pos)
- local params = util.make_given_range_params(start_pos, end_pos)
- params.options = util.make_formatting_params(options).options
- select_client('textDocument/rangeFormatting', function(client)
- if client == nil then
- return
- end
-
- return client.request('textDocument/rangeFormatting', params)
- end)
-end
-
--- Renames all references to the symbol under the cursor.
---
---@param new_name string|nil If not provided, the user will be prompted for a new
@@ -500,7 +383,7 @@ end
--- Lists all the references to the symbol under the cursor in the quickfix window.
---
----@param context (table) Context for the request
+---@param context (table|nil) Context for the request
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references
---@param options table|nil additional options
--- - on_list: (function) handler for list results. See |lsp-on-list-handler|
@@ -565,14 +448,14 @@ end
--- Lists all the call sites of the symbol under the cursor in the
--- |quickfix| window. If the symbol can resolve to multiple
---- items, the user can pick one in the |inputlist|.
+--- items, the user can pick one in the |inputlist()|.
function M.incoming_calls()
call_hierarchy('callHierarchy/incomingCalls')
end
--- Lists all the items that are called by the symbol under the
--- cursor in the |quickfix| window. If the symbol can resolve to
---- multiple items, the user can pick one in the |inputlist|.
+--- multiple items, the user can pick one in the |inputlist()|.
function M.outgoing_calls()
call_hierarchy('callHierarchy/outgoingCalls')
end
@@ -581,7 +464,7 @@ end
---
function M.list_workspace_folders()
local workspace_folders = {}
- for _, client in pairs(vim.lsp.buf_get_clients()) do
+ for _, client in pairs(vim.lsp.get_active_clients({ bufnr = 0 })) do
for _, folder in pairs(client.workspace_folders or {}) do
table.insert(workspace_folders, folder.name)
end
@@ -604,9 +487,9 @@ function M.add_workspace_folder(workspace_folder)
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 _, client in pairs(vim.lsp.get_active_clients({ bufnr = 0 })) do
local found = false
for _, folder in pairs(client.workspace_folders or {}) do
if folder.name == workspace_folder then
@@ -639,7 +522,7 @@ function M.remove_workspace_folder(workspace_folder)
{ {} },
{ { uri = vim.uri_from_fname(workspace_folder), name = workspace_folder } }
)
- for _, client in pairs(vim.lsp.buf_get_clients()) do
+ for _, client in pairs(vim.lsp.get_active_clients({ bufnr = 0 })) do
for idx, folder in pairs(client.workspace_folders) do
if folder.name == workspace_folder then
vim.lsp.buf_notify(0, 'workspace/didChangeWorkspaceFolders', params)
@@ -672,18 +555,17 @@ end
--- 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`, e.g.:
----
---- <pre>
---- autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()
---- autocmd CursorHoldI <buffer> lua vim.lsp.buf.document_highlight()
---- autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references()
+--- <pre>vim
+--- autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()
+--- autocmd CursorHoldI <buffer> lua vim.lsp.buf.document_highlight()
+--- autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references()
--- </pre>
---
--- Note: Usage of |vim.lsp.buf.document_highlight()| requires the following highlight groups
--- to be defined or you won't be able to see the actual highlights.
---- |LspReferenceText|
---- |LspReferenceRead|
---- |LspReferenceWrite|
+--- |hl-LspReferenceText|
+--- |hl-LspReferenceRead|
+--- |hl-LspReferenceWrite|
function M.document_highlight()
local params = util.make_position_params()
request('textDocument/documentHighlight', params)
@@ -851,6 +733,7 @@ end
--- List of LSP `CodeActionKind`s used to filter the code actions.
--- Most language servers support values like `refactor`
--- or `quickfix`.
+--- - triggerKind (number|nil): The reason why code actions were requested.
--- - filter: (function|nil)
--- Predicate taking an `CodeAction` and returning a boolean.
--- - apply: (boolean|nil)
@@ -864,6 +747,7 @@ end
--- using mark-like indexing. See |api-indexing|
---
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction
+---@see vim.lsp.protocol.constants.CodeActionTriggerKind
function M.code_action(options)
validate({ options = { options, 't', true } })
options = options or {}
@@ -873,6 +757,9 @@ function M.code_action(options)
options = { options = options }
end
local context = options.context or {}
+ if not context.triggerKind then
+ context.triggerKind = vim.lsp.protocol.CodeActionTriggerKind.Invoked
+ end
if not context.diagnostics then
local bufnr = api.nvim_get_current_buf()
context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics(bufnr)
@@ -885,23 +772,8 @@ function M.code_action(options)
local end_ = assert(options.range['end'], 'range must have a `end` property')
params = util.make_given_range_params(start, end_)
elseif mode == 'v' or mode == 'V' then
- -- [bufnum, lnum, col, off]; both row and column 1-indexed
- local start = vim.fn.getpos('v')
- local end_ = vim.fn.getpos('.')
- local start_row = start[2]
- local start_col = start[3]
- local end_row = end_[2]
- local end_col = end_[3]
-
- -- A user can start visual selection at the end and move backwards
- -- Normalize the range to start < end
- if start_row == end_row and end_col < start_col then
- end_col, start_col = start_col, end_col
- elseif end_row < start_row then
- start_row, end_row = end_row, start_row
- start_col, end_col = end_col, start_col
- end
- params = util.make_given_range_params({ start_row, start_col - 1 }, { end_row, end_col - 1 })
+ local range = range_from_selection()
+ params = util.make_given_range_params(range.start, range['end'])
else
params = util.make_range_params()
end
@@ -909,34 +781,6 @@ function M.code_action(options)
code_action_request(params, options)
end
---- Performs |vim.lsp.buf.code_action()| for a given range.
----
----
----@param context table|nil `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`.
----@param start_pos ({number, number}, optional) mark-indexed position.
----Defaults to the start of the last visual selection.
----@param end_pos ({number, number}, optional) mark-indexed position.
----Defaults to the end of the last visual selection.
-function M.range_code_action(context, start_pos, end_pos)
- vim.deprecate('vim.lsp.buf.range_code_action', 'vim.lsp.buf.code_action', '0.9.0')
- validate({ context = { context, 't', true } })
- context = context or {}
- if not context.diagnostics then
- 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
- code_action_request(params)
-end
-
--- Executes an LSP server command.
---
---@param command_params table A valid `ExecuteCommandParams` object
diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua
index 4fa02c8db2..17489ed84d 100644
--- a/runtime/lua/vim/lsp/codelens.lua
+++ b/runtime/lua/vim/lsp/codelens.lua
@@ -108,13 +108,36 @@ function M.run()
end
end
+---@private
+local function resolve_bufnr(bufnr)
+ return bufnr == 0 and api.nvim_get_current_buf() or bufnr
+end
+
+--- Clear the lenses
+---
+---@param client_id number|nil filter by client_id. All clients if nil
+---@param bufnr number|nil filter by buffer. All buffers if nil
+function M.clear(client_id, bufnr)
+ local buffers = bufnr and { resolve_bufnr(bufnr) } or vim.tbl_keys(lens_cache_by_buf)
+ for _, iter_bufnr in pairs(buffers) do
+ local client_ids = client_id and { client_id } or vim.tbl_keys(namespaces)
+ for _, iter_client_id in pairs(client_ids) do
+ local ns = namespaces[iter_client_id]
+ lens_cache_by_buf[iter_bufnr][iter_client_id] = {}
+ api.nvim_buf_clear_namespace(iter_bufnr, ns, 0, -1)
+ end
+ end
+end
+
--- Display the lenses using virtual text
---
---@param lenses table of lenses to display (`CodeLens[] | null`)
---@param bufnr number
---@param client_id number
function M.display(lenses, bufnr, client_id)
+ local ns = namespaces[client_id]
if not lenses or not next(lenses) then
+ api.nvim_buf_clear_namespace(bufnr, ns, 0, -1)
return
end
local lenses_by_lnum = {}
@@ -126,7 +149,6 @@ function M.display(lenses, bufnr, client_id)
end
table.insert(line_lenses, lens)
end
- local ns = namespaces[client_id]
local num_lines = api.nvim_buf_line_count(bufnr)
for i = 0, num_lines do
local line_lenses = lenses_by_lnum[i] or {}
@@ -241,7 +263,8 @@ end
---
--- It is recommended to trigger this using an autocmd or via keymap.
---
---- <pre>
+--- Example:
+--- <pre>vim
--- autocmd BufEnter,CursorHold,InsertLeave <buffer> lua vim.lsp.codelens.refresh()
--- </pre>
---
diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua
index 1f9d084e2b..5e2bf75f1b 100644
--- a/runtime/lua/vim/lsp/diagnostic.lua
+++ b/runtime/lua/vim/lsp/diagnostic.lua
@@ -150,7 +150,7 @@ end
---
--- See |vim.diagnostic.config()| for configuration options. Handler-specific
--- configuration can be set using |vim.lsp.with()|:
---- <pre>
+--- <pre>lua
--- vim.lsp.handlers["textDocument/publishDiagnostics"] = vim.lsp.with(
--- vim.lsp.diagnostic.on_publish_diagnostics, {
--- -- Enable underline, use default values
diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index 624436bc9b..5096100a60 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -1,7 +1,6 @@
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
local M = {}
@@ -82,22 +81,38 @@ M['window/workDoneProgress/create'] = function(_, result, ctx)
end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showMessageRequest
+---@param result lsp.ShowMessageRequestParams
M['window/showMessageRequest'] = function(_, result)
- local actions = result.actions
- print(result.message)
- 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))
- 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
+ local actions = result.actions or {}
+ local co, is_main = coroutine.running()
+ if co and not is_main then
+ local opts = {
+ prompt = result.message .. ': ',
+ format_item = function(action)
+ return (action.title:gsub('\r\n', '\\r\\n')):gsub('\n', '\\n')
+ end,
+ }
+ vim.ui.select(actions, opts, function(choice)
+ -- schedule to ensure resume doesn't happen _before_ yield with
+ -- default synchronous vim.ui.select
+ vim.schedule(function()
+ coroutine.resume(co, choice or vim.NIL)
+ end)
+ end)
+ return coroutine.yield()
else
- return actions[choice]
+ 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))
+ end
+ local choice = vim.fn.inputlist(option_strings)
+ if choice < 1 or choice > #actions then
+ return vim.NIL
+ else
+ return actions[choice]
+ end
end
end
@@ -116,9 +131,10 @@ end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit
M['workspace/applyEdit'] = function(_, workspace_edit, ctx)
- if not workspace_edit then
- return
- end
+ assert(
+ workspace_edit,
+ 'workspace/applyEdit must be called with `ApplyWorkspaceEditParams`. Server is violating the specification'
+ )
-- TODO(ashkan) Do something more with label?
local client_id = ctx.client_id
local client = vim.lsp.get_client_by_id(client_id)
@@ -298,13 +314,15 @@ M['textDocument/completion'] = function(_, result, _, _)
end
--- |lsp-handler| for the method "textDocument/hover"
---- <pre>
---- vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(
---- vim.lsp.handlers.hover, {
---- -- Use a sharp border with `FloatBorder` highlights
---- border = "single"
---- }
---- )
+--- <pre>lua
+--- vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(
+--- vim.lsp.handlers.hover, {
+--- -- Use a sharp border with `FloatBorder` highlights
+--- border = "single",
+--- -- add the title in hover float window
+--- title = "hover"
+--- }
+--- )
--- </pre>
---@param config table Configuration table.
--- - border: (default=nil)
@@ -313,8 +331,14 @@ end
function M.hover(_, result, ctx, config)
config = config or {}
config.focus_id = ctx.method
+ if api.nvim_get_current_buf() ~= ctx.bufnr then
+ -- Ignore result since buffer changed. This happens for slow language servers.
+ return
+ end
if not (result and result.contents) then
- vim.notify('No information available')
+ if config.silent ~= true then
+ vim.notify('No information available')
+ end
return
end
local markdown_lines = util.convert_input_to_markdown_lines(result.contents)
@@ -378,21 +402,25 @@ M['textDocument/implementation'] = location_handler
--- |lsp-handler| for the method "textDocument/signatureHelp".
--- The active parameter is highlighted with |hl-LspSignatureActiveParameter|.
---- <pre>
---- vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with(
---- vim.lsp.handlers.signature_help, {
---- -- Use a sharp border with `FloatBorder` highlights
---- border = "single"
---- }
---- )
+--- <pre>lua
+--- vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with(
+--- vim.lsp.handlers.signature_help, {
+--- -- Use a sharp border with `FloatBorder` highlights
+--- border = "single"
+--- }
+--- )
--- </pre>
---@param config table Configuration table.
--- - border: (default=nil)
--- - Add borders to the floating window
---- - See |vim.api.nvim_open_win()|
+--- - See |nvim_open_win()|
function M.signature_help(_, result, ctx, config)
config = config or {}
config.focus_id = ctx.method
+ if api.nvim_get_current_buf() ~= ctx.bufnr then
+ -- Ignore result since buffer changed. This happens for slow language servers.
+ return
+ end
-- When use `autocmd CompleteDone <silent><buffer> lua vim.lsp.buf.signature_help()` to call signatureHelp handler
-- If the completion item doesn't have signatures It will make noise. Change to use `print` that can use `<silent>` to ignore
if not (result and result.signatures and result.signatures[1]) then
@@ -512,6 +540,55 @@ M['window/showMessage'] = function(_, result, ctx, _)
return result
end
+--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showDocument
+M['window/showDocument'] = function(_, result, ctx, _)
+ local uri = result.uri
+
+ if result.external then
+ -- TODO(lvimuser): ask the user for confirmation
+ local cmd
+ if vim.fn.has('win32') == 1 then
+ cmd = { 'cmd.exe', '/c', 'start', '""', uri }
+ elseif vim.fn.has('macunix') == 1 then
+ cmd = { 'open', uri }
+ else
+ cmd = { 'xdg-open', uri }
+ end
+
+ local ret = vim.fn.system(cmd)
+ if vim.v.shell_error ~= 0 then
+ return {
+ success = false,
+ error = {
+ code = protocol.ErrorCodes.UnknownErrorCode,
+ message = ret,
+ },
+ }
+ end
+
+ return { success = true }
+ end
+
+ 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)
+ if not client then
+ err_message({ 'LSP[', client_name, '] client has shut down after sending ', ctx.method })
+ return vim.NIL
+ end
+
+ local location = {
+ uri = uri,
+ range = result.selection,
+ }
+
+ local success = util.show_document(location, client.offset_encoding, {
+ reuse_win = true,
+ focus = result.takeFocus,
+ })
+ return { success = success or false }
+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)
diff --git a/runtime/lua/vim/lsp/health.lua b/runtime/lua/vim/lsp/health.lua
index ba730e3d6d..987707e661 100644
--- a/runtime/lua/vim/lsp/health.lua
+++ b/runtime/lua/vim/lsp/health.lua
@@ -2,8 +2,8 @@ local M = {}
--- Performs a healthcheck for LSP
function M.check()
- local report_info = vim.fn['health#report_info']
- local report_warn = vim.fn['health#report_warn']
+ local report_info = vim.health.report_info
+ local report_warn = vim.health.report_warn
local log = require('vim.lsp.log')
local current_log_level = log.get_level()
@@ -27,6 +27,18 @@ function M.check()
local report_fn = (log_size / 1000000 > 100 and report_warn or report_info)
report_fn(string.format('Log size: %d KB', log_size / 1000))
+
+ local clients = vim.lsp.get_active_clients()
+ vim.health.report_start('vim.lsp: Active Clients')
+ if next(clients) then
+ for _, client in pairs(clients) do
+ report_info(
+ string.format('%s (id=%s, root_dir=%s)', client.name, client.id, client.config.root_dir)
+ )
+ end
+ else
+ report_info('No active clients')
+ end
end
return M
diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua
index 6c6ba0f206..d1a78572aa 100644
--- a/runtime/lua/vim/lsp/log.lua
+++ b/runtime/lua/vim/lsp/log.lua
@@ -20,6 +20,17 @@ local format_func = function(arg)
end
do
+ ---@private
+ local function notify(msg, level)
+ if vim.in_fast_event() then
+ vim.schedule(function()
+ vim.notify(msg, level)
+ end)
+ else
+ vim.notify(msg, level)
+ end
+ end
+
local path_sep = vim.loop.os_uname().version:match('Windows') and '\\' or '/'
---@private
local function path_join(...)
@@ -53,7 +64,7 @@ do
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)
+ notify(err_msg, vim.log.levels.ERROR)
return false
end
@@ -64,7 +75,7 @@ do
log_info.size / (1000 * 1000),
logfilename
)
- vim.notify(warn_msg)
+ notify(warn_msg)
end
-- Start message for logging
@@ -130,7 +141,7 @@ end
vim.tbl_add_reverse_lookup(log.levels)
--- Sets the current log level.
----@param level (string or number) One of `vim.lsp.log.levels`
+---@param level (string|number) One of `vim.lsp.log.levels`
function log.set_level(level)
if type(level) == 'string' then
current_log_level =
diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua
index 27da60b4ae..12345b6c8c 100644
--- a/runtime/lua/vim/lsp/protocol.lua
+++ b/runtime/lua/vim/lsp/protocol.lua
@@ -20,6 +20,14 @@ function transform_schema_to_table()
end
--]=]
+---@class lsp.ShowMessageRequestParams
+---@field type lsp.MessageType
+---@field message string
+---@field actions nil|lsp.MessageActionItem[]
+
+---@class lsp.MessageActionItem
+---@field title string
+
local constants = {
DiagnosticSeverity = {
-- Reports an error.
@@ -39,6 +47,7 @@ local constants = {
Deprecated = 2,
},
+ ---@enum lsp.MessageType
MessageType = {
-- An error message.
Error = 1,
@@ -142,6 +151,7 @@ local constants = {
},
-- Represents reasons why a text document is saved.
+ ---@enum lsp.TextDocumentSaveReason
TextDocumentSaveReason = {
-- Manually triggered, e.g. by the user pressing save, by starting debugging,
-- or by an API call.
@@ -294,6 +304,17 @@ local constants = {
-- Base kind for an organize imports source action
SourceOrganizeImports = 'source.organizeImports',
},
+ -- The reason why code actions were requested.
+ ---@enum lsp.CodeActionTriggerKind
+ CodeActionTriggerKind = {
+ -- Code actions were explicitly requested by the user or by an extension.
+ Invoked = 1,
+ -- Code actions were requested automatically.
+ --
+ -- This typically happens when current selection in a file changes, but can
+ -- also be triggered when file content changes.
+ Automatic = 2,
+ },
}
for k, v in pairs(constants) do
@@ -619,14 +640,63 @@ export interface WorkspaceClientCapabilities {
function protocol.make_client_capabilities()
return {
textDocument = {
- synchronization = {
+ semanticTokens = {
dynamicRegistration = false,
+ tokenTypes = {
+ 'namespace',
+ 'type',
+ 'class',
+ 'enum',
+ 'interface',
+ 'struct',
+ 'typeParameter',
+ 'parameter',
+ 'variable',
+ 'property',
+ 'enumMember',
+ 'event',
+ 'function',
+ 'method',
+ 'macro',
+ 'keyword',
+ 'modifier',
+ 'comment',
+ 'string',
+ 'number',
+ 'regexp',
+ 'operator',
+ 'decorator',
+ },
+ tokenModifiers = {
+ 'declaration',
+ 'definition',
+ 'readonly',
+ 'static',
+ 'deprecated',
+ 'abstract',
+ 'async',
+ 'modification',
+ 'documentation',
+ 'defaultLibrary',
+ },
+ formats = { 'relative' },
+ requests = {
+ -- TODO(jdrouhard): Add support for this
+ range = false,
+ full = { delta = true },
+ },
- -- TODO(ashkan) Send textDocument/willSave before saving (BufWritePre)
- willSave = false,
+ overlappingTokenSupport = true,
+ -- TODO(jdrouhard): Add support for this
+ multilineTokenSupport = false,
+ serverCancelSupport = false,
+ augmentsSyntaxTokens = true,
+ },
+ synchronization = {
+ dynamicRegistration = false,
- -- TODO(ashkan) Implement textDocument/willSaveWaitUntil
- willSaveWaitUntil = false,
+ willSave = true,
+ willSaveWaitUntil = true,
-- Send textDocument/didSave after saving (BufWritePost)
didSave = true,
@@ -637,7 +707,7 @@ function protocol.make_client_capabilities()
codeActionLiteralSupport = {
codeActionKind = {
valueSet = (function()
- local res = vim.tbl_values(protocol.CodeActionKind)
+ local res = vim.tbl_values(constants.CodeActionKind)
table.sort(res)
return res
end)(),
@@ -742,6 +812,9 @@ function protocol.make_client_capabilities()
end)(),
},
},
+ callHierarchy = {
+ dynamicRegistration = false,
+ },
},
workspace = {
symbol = {
@@ -765,9 +838,9 @@ function protocol.make_client_capabilities()
workspaceEdit = {
resourceOperations = { 'rename', 'create', 'delete' },
},
- },
- callHierarchy = {
- dynamicRegistration = false,
+ semanticTokens = {
+ refreshSupport = true,
+ },
},
experimental = nil,
window = {
@@ -778,7 +851,7 @@ function protocol.make_client_capabilities()
},
},
showDocument = {
- support = false,
+ support = true,
},
},
}
@@ -861,8 +934,8 @@ function protocol._resolve_capabilities_compat(server_capabilities)
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_will_save = if_nil(textDocumentSync.willSave, true),
+ text_document_will_save_wait_until = if_nil(textDocumentSync.willSaveWaitUntil, true),
text_document_save = if_nil(textDocumentSync.save, false),
text_document_save_include_text = if_nil(
type(textDocumentSync.save) == 'table' and textDocumentSync.save.includeText,
diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua
index 0926912066..f1492601ff 100644
--- a/runtime/lua/vim/lsp/rpc.lua
+++ b/runtime/lua/vim/lsp/rpc.lua
@@ -1,4 +1,3 @@
-local vim = vim
local uv = vim.loop
local log = require('vim.lsp.log')
local protocol = require('vim.lsp.protocol')
@@ -241,247 +240,162 @@ function default_dispatchers.on_error(code, err)
local _ = log.error() and log.error('client_error:', client_errors[code], err)
end
---- Starts an LSP server process and create an LSP RPC client object to
---- interact with it. Communication with the server is currently limited to stdio.
----
----@param cmd (string) Command to start the LSP server.
----@param cmd_args (table) List of additional string arguments to pass to {cmd}.
----@param dispatchers table|nil Dispatchers for LSP message types. Valid
----dispatcher names are:
---- - `"notification"`
---- - `"server_request"`
---- - `"on_error"`
---- - `"on_exit"`
----@param extra_spawn_params table|nil Additional context for the LSP
---- server process. May contain:
---- - {cwd} (string) Working directory for the LSP server process
---- - {env} (table) Additional environment variables for LSP server process
----@returns Client RPC object.
----
----@returns Methods:
---- - `notify()` |vim.lsp.rpc.notify()|
---- - `request()` |vim.lsp.rpc.request()|
----
----@returns Members:
---- - {pid} (number) The LSP server's PID.
---- - {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 },
- })
-
- if extra_spawn_params and extra_spawn_params.cwd then
- assert(is_dir(extra_spawn_params.cwd), 'cwd must be a directory')
- end
- if dispatchers then
- local user_dispatchers = dispatchers
- dispatchers = {}
- for dispatch_name, default_dispatch in pairs(default_dispatchers) do
- 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))
- 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.
- then
- user_dispatcher = schedule_wrap(user_dispatcher)
- end
- dispatchers[dispatch_name] = user_dispatcher
- else
- dispatchers[dispatch_name] = default_dispatch
- end
+---@private
+local function create_read_loop(handle_body, on_no_chunk, on_error)
+ local parse_chunk = coroutine.wrap(request_parser_loop)
+ parse_chunk()
+ return function(err, chunk)
+ if err then
+ on_error(err)
+ return
end
- else
- dispatchers = default_dispatchers
- end
-
- local stdin = uv.new_pipe(false)
- local stdout = uv.new_pipe(false)
- local stderr = uv.new_pipe(false)
-
- local message_index = 0
- local message_callbacks = {}
- local notify_reply_callbacks = {}
- local handle, pid
- do
- ---@private
- --- Callback for |vim.loop.spawn()| Closes all streams and runs the `on_exit` dispatcher.
- ---@param code (number) Exit code
- ---@param signal (number) Signal that was used to terminate (if any)
- local function onexit(code, signal)
- stdin:close()
- stdout:close()
- stderr:close()
- handle:close()
- -- Make sure that message_callbacks/notify_reply_callbacks can be gc'd.
- message_callbacks = nil
- notify_reply_callbacks = nil
- dispatchers.on_exit(code, signal)
- end
- local spawn_params = {
- 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
+ if not chunk then
+ if on_no_chunk then
+ on_no_chunk()
end
+ return
end
- handle, pid = uv.spawn(cmd, spawn_params, onexit)
- if handle == nil then
- stdin:close()
- stdout:close()
- stderr:close()
- 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.'
+
+ while true do
+ local headers, body = parse_chunk(chunk)
+ if headers then
+ handle_body(body)
+ chunk = ''
else
- msg = msg .. string.format(' with error message: %s', pid)
+ break
end
- vim.notify(msg, vim.log.levels.WARN)
- return
end
end
+end
- ---@private
- --- Encodes {payload} into a JSON-RPC message and sends it to the remote
- --- process.
- ---
- ---@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 encoded = vim.json.encode(payload)
- stdin:write(format_message_with_content_length(encoded))
- return true
- end
+---@class RpcClient
+---@field message_index number
+---@field message_callbacks table
+---@field notify_reply_callbacks table
+---@field transport table
+---@field dispatchers table
- -- FIXME: DOC: Should be placed on the RPC client object returned by
- -- `start()`
- --
- --- Sends a notification to the LSP server.
- ---@param method (string) The invoked LSP method
- ---@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,
- })
- end
+---@class RpcClient
+local Client = {}
- ---@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,
- })
+---@private
+function Client:encode_and_send(payload)
+ local _ = log.debug() and log.debug('rpc.send', payload)
+ if self.transport.is_closing() then
+ return false
end
+ local encoded = vim.json.encode(payload)
+ self.transport.write(format_message_with_content_length(encoded))
+ return true
+end
- -- FIXME: DOC: Should be placed on the RPC client object returned by
- -- `start()`
- --
- --- Sends a request to the LSP server and runs {callback} upon response.
- ---
- ---@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|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 },
- })
- 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,
- })
- if result then
- if message_callbacks then
- message_callbacks[message_id] = schedule_wrap(callback)
- else
- return false
- end
- if notify_reply_callback and notify_reply_callbacks then
- notify_reply_callbacks[message_id] = schedule_wrap(notify_reply_callback)
- end
- return result, message_id
+---@private
+--- Sends a notification to the LSP server.
+---@param method (string) The invoked LSP method
+---@param params (any): Parameters for the invoked LSP method
+---@returns (bool) `true` if notification could be sent, `false` if not
+function Client:notify(method, params)
+ return self:encode_and_send({
+ jsonrpc = '2.0',
+ method = method,
+ params = params,
+ })
+end
+
+---@private
+--- sends an error object to the remote LSP process.
+function Client:send_response(request_id, err, result)
+ return self:encode_and_send({
+ id = request_id,
+ jsonrpc = '2.0',
+ error = err,
+ result = result,
+ })
+end
+
+---@private
+--- Sends a request to the LSP server and runs {callback} upon response.
+---
+---@param method (string) The invoked LSP method
+---@param params (table|nil) Parameters for the invoked LSP method
+---@param callback (function) Callback to invoke
+---@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
+function Client:request(method, params, callback, notify_reply_callback)
+ validate({
+ callback = { callback, 'f' },
+ notify_reply_callback = { notify_reply_callback, 'f', true },
+ })
+ self.message_index = self.message_index + 1
+ local message_id = self.message_index
+ local result = self:encode_and_send({
+ id = message_id,
+ jsonrpc = '2.0',
+ method = method,
+ params = params,
+ })
+ local message_callbacks = self.message_callbacks
+ local notify_reply_callbacks = self.notify_reply_callbacks
+ if result then
+ if message_callbacks then
+ message_callbacks[message_id] = schedule_wrap(callback)
else
return false
end
+ if notify_reply_callback and notify_reply_callbacks then
+ notify_reply_callbacks[message_id] = schedule_wrap(notify_reply_callback)
+ end
+ return result, message_id
+ else
+ return false
end
+end
- stderr:read_start(function(_, chunk)
- if chunk then
- local _ = log.error() and log.error('rpc', cmd, 'stderr', chunk)
- end
- end)
+---@private
+function Client:on_error(errkind, ...)
+ assert(client_errors[errkind])
+ -- TODO what to do if this fails?
+ pcall(self.dispatchers.on_error, errkind, ...)
+end
- ---@private
- local function on_error(errkind, ...)
- assert(client_errors[errkind])
- -- TODO what to do if this fails?
- pcall(dispatchers.on_error, errkind, ...)
- end
- ---@private
- local function pcall_handler(errkind, status, head, ...)
- if not status then
- on_error(errkind, head, ...)
- return status, head
- end
- return status, head, ...
- end
- ---@private
- local function try_call(errkind, fn, ...)
- return pcall_handler(errkind, pcall(fn, ...))
+---@private
+function Client:pcall_handler(errkind, status, head, ...)
+ if not status then
+ self:on_error(errkind, head, ...)
+ return status, head
end
+ return status, head, ...
+end
- -- TODO periodically check message_callbacks for old requests past a certain
- -- time and log them. This would require storing the timestamp. I could call
- -- them with an error then, perhaps.
+---@private
+function Client:try_call(errkind, fn, ...)
+ return self:pcall_handler(errkind, pcall(fn, ...))
+end
- ---@private
- local function handle_body(body)
- local ok, decoded = pcall(vim.json.decode, body, { luanil = { object = true } })
- if not ok then
- on_error(client_errors.INVALID_SERVER_JSON, decoded)
- return
- end
- local _ = log.debug() and log.debug('rpc.receive', decoded)
+-- TODO periodically check message_callbacks for old requests past a certain
+-- time and log them. This would require storing the timestamp. I could call
+-- them with an error then, perhaps.
- if type(decoded.method) == 'string' and decoded.id then
- local err
- -- Schedule here so that the users functions don't trigger an error and
- -- we can still use the result.
- schedule(function()
+---@private
+function Client:handle_body(body)
+ local ok, decoded = pcall(vim.json.decode, body, { luanil = { object = true } })
+ if not ok then
+ self:on_error(client_errors.INVALID_SERVER_JSON, decoded)
+ return
+ end
+ local _ = log.debug() and log.debug('rpc.receive', decoded)
+
+ if type(decoded.method) == 'string' and decoded.id then
+ local err
+ -- Schedule here so that the users functions don't trigger an error and
+ -- we can still use the result.
+ schedule(function()
+ coroutine.wrap(function()
local status, result
- status, result, err = try_call(
+ status, result, err = self:try_call(
client_errors.SERVER_REQUEST_HANDLER_ERROR,
- dispatchers.server_request,
+ self.dispatchers.server_request,
decoded.method,
decoded.params
)
@@ -491,8 +405,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
{ 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
+ if result == nil and err == nil then
error(
string.format(
'method %q: either a result or an error must be sent to the server in response',
@@ -516,120 +429,328 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
err = rpc_response_error(protocol.ErrorCodes.InternalError, result)
result = nil
end
- send_response(decoded.id, err, result)
- end)
- -- 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 = assert(tonumber(decoded.id), 'response id must be a number')
-
- -- 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' },
- })
- notify_reply_callback(result_id)
- notify_reply_callbacks[result_id] = nil
- end
+ self:send_response(decoded.id, err, result)
+ end)()
+ end)
+ -- 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 = assert(tonumber(decoded.id), 'response id must be a number')
+
+ -- Notify the user that a response was received for the request
+ local notify_reply_callbacks = self.notify_reply_callbacks
+ 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' },
+ })
+ notify_reply_callback(result_id)
+ notify_reply_callbacks[result_id] = nil
+ end
- -- Do not surface RequestCancelled to users, it is RPC-internal.
- 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)
- mute_error = true
- end
+ local message_callbacks = self.message_callbacks
- if mute_error then
- -- Clear any callback since this is cancelled now.
- -- This is safe to do assuming that these conditions hold:
- -- - The server will not send a result callback after this cancellation.
- -- - If the server sent this cancellation ACK after sending the result, the user of this RPC
- -- client will ignore the result themselves.
- if result_id and message_callbacks then
- message_callbacks[result_id] = nil
- end
- return
+ -- Do not surface RequestCancelled to users, it is RPC-internal.
+ 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)
+ mute_error = true
+ end
+
+ if mute_error then
+ -- Clear any callback since this is cancelled now.
+ -- This is safe to do assuming that these conditions hold:
+ -- - The server will not send a result callback after this cancellation.
+ -- - If the server sent this cancellation ACK after sending the result, the user of this RPC
+ -- client will ignore the result themselves.
+ if result_id and message_callbacks then
+ message_callbacks[result_id] = nil
end
+ return
end
+ end
- local callback = message_callbacks and message_callbacks[result_id]
- if callback then
- message_callbacks[result_id] = nil
- validate({
- callback = { callback, 'f' },
+ local callback = message_callbacks and message_callbacks[result_id]
+ if callback then
+ message_callbacks[result_id] = nil
+ validate({
+ callback = { callback, 'f' },
+ })
+ if decoded.error then
+ decoded.error = setmetatable(decoded.error, {
+ __tostring = format_rpc_error,
})
- if decoded.error then
- decoded.error = setmetatable(decoded.error, {
- __tostring = format_rpc_error,
- })
- end
- 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)
end
- elseif type(decoded.method) == 'string' then
- -- Notification
- try_call(
- client_errors.NOTIFICATION_HANDLER_ERROR,
- dispatchers.notification,
- decoded.method,
- decoded.params
+ self:try_call(
+ client_errors.SERVER_RESULT_CALLBACK_ERROR,
+ callback,
+ decoded.error,
+ decoded.result
)
else
- -- Invalid server message
- on_error(client_errors.INVALID_SERVER_MESSAGE, decoded)
+ self:on_error(client_errors.NO_RESULT_CALLBACK_FOUND, decoded)
+ local _ = log.error() and log.error('No callback found for server response id ' .. result_id)
end
+ elseif type(decoded.method) == 'string' then
+ -- Notification
+ self:try_call(
+ client_errors.NOTIFICATION_HANDLER_ERROR,
+ self.dispatchers.notification,
+ decoded.method,
+ decoded.params
+ )
+ else
+ -- Invalid server message
+ self:on_error(client_errors.INVALID_SERVER_MESSAGE, decoded)
end
+end
- local request_parser = coroutine.wrap(request_parser_loop)
- request_parser()
- stdout:read_start(function(err, chunk)
- if err then
- -- TODO better handling. Can these be intermittent errors?
- on_error(client_errors.READ_ERROR, err)
- return
- end
- -- This should signal that we are done reading from the client.
- if not chunk then
- return
- end
- -- Flush anything in the parser by looping until we don't get a result
- -- anymore.
- while true do
- local headers, body = request_parser(chunk)
- -- If we successfully parsed, then handle the response.
- if headers then
- handle_body(body)
- -- Set chunk to empty so that we can call request_parser to get
- -- anything existing in the parser to flush.
- chunk = ''
+---@private
+---@return RpcClient
+local function new_client(dispatchers, transport)
+ local state = {
+ message_index = 0,
+ message_callbacks = {},
+ notify_reply_callbacks = {},
+ transport = transport,
+ dispatchers = dispatchers,
+ }
+ return setmetatable(state, { __index = Client })
+end
+
+---@private
+---@param client RpcClient
+local function public_client(client)
+ local result = {}
+
+ ---@private
+ function result.is_closing()
+ return client.transport.is_closing()
+ end
+
+ ---@private
+ function result.terminate()
+ client.transport.terminate()
+ end
+
+ --- Sends a request to the LSP server and runs {callback} upon response.
+ ---
+ ---@param method (string) The invoked LSP method
+ ---@param params (table|nil) Parameters for the invoked LSP method
+ ---@param callback (function) Callback to invoke
+ ---@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
+ function result.request(method, params, callback, notify_reply_callback)
+ return client:request(method, params, callback, notify_reply_callback)
+ end
+
+ --- Sends a notification to the LSP server.
+ ---@param method (string) The invoked LSP method
+ ---@param params (table|nil): Parameters for the invoked LSP method
+ ---@returns (bool) `true` if notification could be sent, `false` if not
+ function result.notify(method, params)
+ return client:notify(method, params)
+ end
+
+ return result
+end
+
+---@private
+local function merge_dispatchers(dispatchers)
+ if dispatchers then
+ local user_dispatchers = dispatchers
+ dispatchers = {}
+ for dispatch_name, default_dispatch in pairs(default_dispatchers) do
+ 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))
+ 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.
+ then
+ user_dispatcher = schedule_wrap(user_dispatcher)
+ end
+ dispatchers[dispatch_name] = user_dispatcher
else
- break
+ dispatchers[dispatch_name] = default_dispatch
end
end
- end)
+ else
+ dispatchers = default_dispatchers
+ end
+ return dispatchers
+end
+
+--- Create a LSP RPC client factory that connects via TCP to the given host
+--- and port
+---
+---@param host string
+---@param port number
+---@return function
+local function connect(host, port)
+ return function(dispatchers)
+ dispatchers = merge_dispatchers(dispatchers)
+ local tcp = uv.new_tcp()
+ local closing = false
+ local transport = {
+ write = function(msg)
+ tcp:write(msg)
+ end,
+ is_closing = function()
+ return closing
+ end,
+ terminate = function()
+ if not closing then
+ closing = true
+ tcp:shutdown()
+ tcp:close()
+ dispatchers.on_exit(0, 0)
+ end
+ end,
+ }
+ local client = new_client(dispatchers, transport)
+ tcp:connect(host, port, function(err)
+ if err then
+ vim.schedule(function()
+ vim.notify(
+ string.format('Could not connect to %s:%s, reason: %s', host, port, vim.inspect(err)),
+ vim.log.levels.WARN
+ )
+ end)
+ return
+ end
+ local handle_body = function(body)
+ client:handle_body(body)
+ end
+ tcp:read_start(create_read_loop(handle_body, transport.terminate, function(read_err)
+ client:on_error(client_errors.READ_ERROR, read_err)
+ end))
+ end)
- return {
- pid = pid,
- handle = handle,
- request = request,
- notify = notify,
+ return public_client(client)
+ end
+end
+
+--- Starts an LSP server process and create an LSP RPC client object to
+--- interact with it. Communication with the spawned process happens via stdio. For
+--- communication via TCP, spawn a process manually and use |vim.lsp.rpc.connect()|
+---
+---@param cmd (string) Command to start the LSP server.
+---@param cmd_args (table) List of additional string arguments to pass to {cmd}.
+---@param dispatchers table|nil Dispatchers for LSP message types. Valid
+---dispatcher names are:
+--- - `"notification"`
+--- - `"server_request"`
+--- - `"on_error"`
+--- - `"on_exit"`
+---@param extra_spawn_params table|nil Additional context for the LSP
+--- server process. May contain:
+--- - {cwd} (string) Working directory for the LSP server process
+--- - {env} (table) Additional environment variables for LSP server process
+---@returns Client RPC object.
+---
+---@returns Methods:
+--- - `notify()` |vim.lsp.rpc.notify()|
+--- - `request()` |vim.lsp.rpc.request()|
+--- - `is_closing()` returns a boolean indicating if the RPC is closing.
+--- - `terminate()` terminates the RPC client.
+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 },
+ })
+
+ if extra_spawn_params and extra_spawn_params.cwd then
+ assert(is_dir(extra_spawn_params.cwd), 'cwd must be a directory')
+ end
+
+ dispatchers = merge_dispatchers(dispatchers)
+ local stdin = uv.new_pipe(false)
+ local stdout = uv.new_pipe(false)
+ local stderr = uv.new_pipe(false)
+ local handle, pid
+
+ local client = new_client(dispatchers, {
+ write = function(msg)
+ stdin:write(msg)
+ end,
+ is_closing = function()
+ return handle == nil or handle:is_closing()
+ end,
+ terminate = function()
+ if handle then
+ handle:kill(15)
+ end
+ end,
+ })
+
+ ---@private
+ --- Callback for |vim.loop.spawn()| Closes all streams and runs the `on_exit` dispatcher.
+ ---@param code (number) Exit code
+ ---@param signal (number) Signal that was used to terminate (if any)
+ local function onexit(code, signal)
+ stdin:close()
+ stdout:close()
+ stderr:close()
+ handle:close()
+ dispatchers.on_exit(code, signal)
+ end
+ local spawn_params = {
+ 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
+ stdin:close()
+ stdout:close()
+ stderr:close()
+ local msg = string.format('Spawning language server with cmd: `%s` failed', cmd)
+ if string.match(pid, 'ENOENT') then
+ msg = msg
+ .. '. The language server is either not installed, missing from PATH, or not executable.'
+ else
+ msg = msg .. string.format(' with error message: %s', pid)
+ end
+ vim.notify(msg, vim.log.levels.WARN)
+ return
+ end
+
+ stderr:read_start(function(_, chunk)
+ if chunk then
+ local _ = log.error() and log.error('rpc', cmd, 'stderr', chunk)
+ end
+ end)
+
+ local handle_body = function(body)
+ client:handle_body(body)
+ end
+ stdout:read_start(create_read_loop(handle_body, nil, function(err)
+ client:on_error(client_errors.READ_ERROR, err)
+ end))
+
+ return public_client(client)
end
return {
start = start,
+ connect = connect,
rpc_response_error = rpc_response_error,
format_rpc_error = format_rpc_error,
client_errors = client_errors,
+ create_read_loop = create_read_loop,
}
-- vim:sw=2 ts=2 et
diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua
new file mode 100644
index 0000000000..b1bc48dac6
--- /dev/null
+++ b/runtime/lua/vim/lsp/semantic_tokens.lua
@@ -0,0 +1,702 @@
+local api = vim.api
+local handlers = require('vim.lsp.handlers')
+local util = require('vim.lsp.util')
+
+--- @class STTokenRange
+--- @field line number line number 0-based
+--- @field start_col number start column 0-based
+--- @field end_col number end column 0-based
+--- @field type string token type as string
+--- @field modifiers string[] token modifiers as strings
+--- @field extmark_added boolean whether this extmark has been added to the buffer yet
+---
+--- @class STCurrentResult
+--- @field version number document version associated with this result
+--- @field result_id string resultId from the server; used with delta requests
+--- @field highlights STTokenRange[] cache of highlight ranges for this document version
+--- @field tokens number[] raw token array as received by the server. used for calculating delta responses
+--- @field namespace_cleared boolean whether the namespace was cleared for this result yet
+---
+--- @class STActiveRequest
+--- @field request_id number the LSP request ID of the most recent request sent to the server
+--- @field version number the document version associated with the most recent request
+---
+--- @class STClientState
+--- @field namespace number
+--- @field active_request STActiveRequest
+--- @field current_result STCurrentResult
+
+---@class STHighlighter
+---@field active table<number, STHighlighter>
+---@field bufnr number
+---@field augroup number augroup for buffer events
+---@field debounce number milliseconds to debounce requests for new tokens
+---@field timer table uv_timer for debouncing requests for new tokens
+---@field client_state table<number, STClientState>
+local STHighlighter = { active = {} }
+
+---@private
+local function binary_search(tokens, line)
+ local lo = 1
+ local hi = #tokens
+ while lo < hi do
+ local mid = math.floor((lo + hi) / 2)
+ if tokens[mid].line < line then
+ lo = mid + 1
+ else
+ hi = mid
+ end
+ end
+ return lo
+end
+
+--- Extracts modifier strings from the encoded number in the token array
+---
+---@private
+---@return string[]
+local function modifiers_from_number(x, modifiers_table)
+ local modifiers = {}
+ local idx = 1
+ while x > 0 do
+ if _G.bit then
+ if _G.bit.band(x, 1) == 1 then
+ modifiers[#modifiers + 1] = modifiers_table[idx]
+ end
+ x = _G.bit.rshift(x, 1)
+ else
+ --TODO(jdrouhard): remove this branch once `bit` module is available for non-LuaJIT (#21222)
+ if x % 2 == 1 then
+ modifiers[#modifiers + 1] = modifiers_table[idx]
+ end
+ x = math.floor(x / 2)
+ end
+ idx = idx + 1
+ end
+
+ return modifiers
+end
+
+--- Converts a raw token list to a list of highlight ranges used by the on_win callback
+---
+---@private
+---@return STTokenRange[]
+local function tokens_to_ranges(data, bufnr, client)
+ local legend = client.server_capabilities.semanticTokensProvider.legend
+ local token_types = legend.tokenTypes
+ local token_modifiers = legend.tokenModifiers
+ local ranges = {}
+
+ local line
+ local start_char = 0
+ for i = 1, #data, 5 do
+ local delta_line = data[i]
+ line = line and line + delta_line or delta_line
+ local delta_start = data[i + 1]
+ start_char = delta_line == 0 and start_char + delta_start or delta_start
+
+ -- data[i+3] +1 because Lua tables are 1-indexed
+ local token_type = token_types[data[i + 3] + 1]
+ local modifiers = modifiers_from_number(data[i + 4], token_modifiers)
+
+ ---@private
+ local function _get_byte_pos(char_pos)
+ return util._get_line_byte_from_position(bufnr, {
+ line = line,
+ character = char_pos,
+ }, client.offset_encoding)
+ end
+
+ local start_col = _get_byte_pos(start_char)
+ local end_col = _get_byte_pos(start_char + data[i + 2])
+
+ if token_type then
+ ranges[#ranges + 1] = {
+ line = line,
+ start_col = start_col,
+ end_col = end_col,
+ type = token_type,
+ modifiers = modifiers,
+ extmark_added = false,
+ }
+ end
+ end
+
+ return ranges
+end
+
+--- Construct a new STHighlighter for the buffer
+---
+---@private
+---@param bufnr number
+function STHighlighter.new(bufnr)
+ local self = setmetatable({}, { __index = STHighlighter })
+
+ self.bufnr = bufnr
+ self.augroup = api.nvim_create_augroup('vim_lsp_semantic_tokens:' .. bufnr, { clear = true })
+ self.client_state = {}
+
+ STHighlighter.active[bufnr] = self
+
+ api.nvim_buf_attach(bufnr, false, {
+ on_lines = function(_, buf)
+ local highlighter = STHighlighter.active[buf]
+ if not highlighter then
+ return true
+ end
+ highlighter:on_change()
+ end,
+ on_reload = function(_, buf)
+ local highlighter = STHighlighter.active[buf]
+ if highlighter then
+ highlighter:reset()
+ highlighter:send_request()
+ end
+ end,
+ on_detach = function(_, buf)
+ local highlighter = STHighlighter.active[buf]
+ if highlighter then
+ highlighter:destroy()
+ end
+ end,
+ })
+
+ api.nvim_create_autocmd({ 'BufWinEnter', 'InsertLeave' }, {
+ buffer = self.bufnr,
+ group = self.augroup,
+ callback = function()
+ self:send_request()
+ end,
+ })
+
+ api.nvim_create_autocmd('LspDetach', {
+ buffer = self.bufnr,
+ group = self.augroup,
+ callback = function(args)
+ self:detach(args.data.client_id)
+ if vim.tbl_isempty(self.client_state) then
+ self:destroy()
+ end
+ end,
+ })
+
+ return self
+end
+
+---@private
+function STHighlighter:destroy()
+ for client_id, _ in pairs(self.client_state) do
+ self:detach(client_id)
+ end
+
+ api.nvim_del_augroup_by_id(self.augroup)
+ STHighlighter.active[self.bufnr] = nil
+end
+
+---@private
+function STHighlighter:attach(client_id)
+ local state = self.client_state[client_id]
+ if not state then
+ state = {
+ namespace = api.nvim_create_namespace('vim_lsp_semantic_tokens:' .. client_id),
+ active_request = {},
+ current_result = {},
+ }
+ self.client_state[client_id] = state
+ end
+end
+
+---@private
+function STHighlighter:detach(client_id)
+ local state = self.client_state[client_id]
+ if state then
+ --TODO: delete namespace if/when that becomes possible
+ api.nvim_buf_clear_namespace(self.bufnr, state.namespace, 0, -1)
+ self.client_state[client_id] = nil
+ end
+end
+
+--- This is the entry point for getting all the tokens in a buffer.
+---
+--- For the given clients (or all attached, if not provided), this sends a request
+--- to ask for semantic tokens. If the server supports delta requests, that will
+--- be prioritized if we have a previous requestId and token array.
+---
+--- This function will skip servers where there is an already an active request in
+--- flight for the same version. If there is a stale request in flight, that is
+--- cancelled prior to sending a new one.
+---
+--- Finally, if the request was successful, the requestId and document version
+--- are saved to facilitate document synchronization in the response.
+---
+---@private
+function STHighlighter:send_request()
+ local version = util.buf_versions[self.bufnr]
+
+ self:reset_timer()
+
+ for client_id, state in pairs(self.client_state) do
+ local client = vim.lsp.get_client_by_id(client_id)
+
+ local current_result = state.current_result
+ local active_request = state.active_request
+
+ -- Only send a request for this client if the current result is out of date and
+ -- there isn't a current a request in flight for this version
+ if client and current_result.version ~= version and active_request.version ~= version then
+ -- cancel stale in-flight request
+ if active_request.request_id then
+ client.cancel_request(active_request.request_id)
+ active_request = {}
+ state.active_request = active_request
+ end
+
+ local spec = client.server_capabilities.semanticTokensProvider.full
+ local hasEditProvider = type(spec) == 'table' and spec.delta
+
+ local params = { textDocument = util.make_text_document_params(self.bufnr) }
+ local method = 'textDocument/semanticTokens/full'
+
+ if hasEditProvider and current_result.result_id then
+ method = method .. '/delta'
+ params.previousResultId = current_result.result_id
+ end
+ local success, request_id = client.request(method, params, function(err, response, ctx)
+ -- look client up again using ctx.client_id instead of using a captured
+ -- client object
+ local c = vim.lsp.get_client_by_id(ctx.client_id)
+ local highlighter = STHighlighter.active[ctx.bufnr]
+ if not err and c and highlighter then
+ highlighter:process_response(response, c, version)
+ end
+ end, self.bufnr)
+
+ if success then
+ active_request.request_id = request_id
+ active_request.version = version
+ end
+ end
+ end
+end
+
+--- This function will parse the semantic token responses and set up the cache
+--- (current_result). It also performs document synchronization by checking the
+--- version of the document associated with the resulting request_id and only
+--- performing work if the response is not out-of-date.
+---
+--- Delta edits are applied if necessary, and new highlight ranges are calculated
+--- and stored in the buffer state.
+---
+--- Finally, a redraw command is issued to force nvim to redraw the screen to
+--- pick up changed highlight tokens.
+---
+---@private
+function STHighlighter:process_response(response, client, version)
+ local state = self.client_state[client.id]
+ if not state then
+ return
+ end
+
+ -- ignore stale responses
+ if state.active_request.version and version ~= state.active_request.version then
+ return
+ end
+
+ -- reset active request
+ state.active_request = {}
+
+ -- skip nil responses
+ if response == nil then
+ return
+ end
+
+ -- if we have a response to a delta request, update the state of our tokens
+ -- appropriately. if it's a full response, just use that
+ local tokens
+ local token_edits = response.edits
+ if token_edits then
+ table.sort(token_edits, function(a, b)
+ return a.start < b.start
+ end)
+
+ tokens = {}
+ local old_tokens = state.current_result.tokens
+ local idx = 1
+ for _, token_edit in ipairs(token_edits) do
+ vim.list_extend(tokens, old_tokens, idx, token_edit.start)
+ if token_edit.data then
+ vim.list_extend(tokens, token_edit.data)
+ end
+ idx = token_edit.start + token_edit.deleteCount + 1
+ end
+ vim.list_extend(tokens, old_tokens, idx)
+ else
+ tokens = response.data
+ end
+
+ -- Update the state with the new results
+ local current_result = state.current_result
+ current_result.version = version
+ current_result.result_id = response.resultId
+ current_result.tokens = tokens
+ current_result.highlights = tokens_to_ranges(tokens, self.bufnr, client)
+ current_result.namespace_cleared = false
+
+ api.nvim_command('redraw!')
+end
+
+--- on_win handler for the decoration provider (see |nvim_set_decoration_provider|)
+---
+--- If there is a current result for the buffer and the version matches the
+--- current document version, then the tokens are valid and can be applied. As
+--- the buffer is drawn, this function will add extmark highlights for every
+--- token in the range of visible lines. Once a highlight has been added, it
+--- sticks around until the document changes and there's a new set of matching
+--- highlight tokens available.
+---
+--- If this is the first time a buffer is being drawn with a new set of
+--- highlights for the current document version, the namespace is cleared to
+--- remove extmarks from the last version. It's done here instead of the response
+--- handler to avoid the "blink" that occurs due to the timing between the
+--- response handler and the actual redraw.
+---
+---@private
+function STHighlighter:on_win(topline, botline)
+ for _, state in pairs(self.client_state) do
+ local current_result = state.current_result
+ if current_result.version and current_result.version == util.buf_versions[self.bufnr] then
+ if not current_result.namespace_cleared then
+ api.nvim_buf_clear_namespace(self.bufnr, state.namespace, 0, -1)
+ current_result.namespace_cleared = true
+ end
+
+ -- We can't use ephemeral extmarks because the buffer updates are not in
+ -- sync with the list of semantic tokens. There's a delay between the
+ -- buffer changing and when the LSP server can respond with updated
+ -- tokens, and we don't want to "blink" the token highlights while
+ -- updates are in flight, and we don't want to use stale tokens because
+ -- they likely won't line up right with the actual buffer.
+ --
+ -- Instead, we have to use normal extmarks that can attach to locations
+ -- in the buffer and are persisted between redraws.
+ local highlights = current_result.highlights
+ local idx = binary_search(highlights, topline)
+
+ for i = idx, #highlights do
+ local token = highlights[i]
+
+ if token.line > botline then
+ break
+ end
+
+ if not token.extmark_added then
+ -- `strict = false` is necessary here for the 1% of cases where the
+ -- current result doesn't actually match the buffer contents. Some
+ -- LSP servers can respond with stale tokens on requests if they are
+ -- still processing changes from a didChange notification.
+ --
+ -- LSP servers that do this _should_ follow up known stale responses
+ -- with a refresh notification once they've finished processing the
+ -- didChange notification, which would re-synchronize the tokens from
+ -- our end.
+ --
+ -- The server I know of that does this is clangd when the preamble of
+ -- a file changes and the token request is processed with a stale
+ -- preamble while the new one is still being built. Once the preamble
+ -- finishes, clangd sends a refresh request which lets the client
+ -- re-synchronize the tokens.
+ api.nvim_buf_set_extmark(self.bufnr, state.namespace, token.line, token.start_col, {
+ hl_group = '@' .. token.type,
+ end_col = token.end_col,
+ priority = vim.highlight.priorities.semantic_tokens,
+ strict = false,
+ })
+
+ -- TODO(bfredl) use single extmark when hl_group supports table
+ if #token.modifiers > 0 then
+ for _, modifier in pairs(token.modifiers) do
+ api.nvim_buf_set_extmark(self.bufnr, state.namespace, token.line, token.start_col, {
+ hl_group = '@' .. modifier,
+ end_col = token.end_col,
+ priority = vim.highlight.priorities.semantic_tokens + 1,
+ strict = false,
+ })
+ end
+ end
+
+ token.extmark_added = true
+ end
+ end
+ end
+ end
+end
+
+--- Reset the buffer's highlighting state and clears the extmark highlights.
+---
+---@private
+function STHighlighter:reset()
+ for client_id, state in pairs(self.client_state) do
+ api.nvim_buf_clear_namespace(self.bufnr, state.namespace, 0, -1)
+ state.current_result = {}
+ if state.active_request.request_id then
+ local client = vim.lsp.get_client_by_id(client_id)
+ assert(client)
+ client.cancel_request(state.active_request.request_id)
+ state.active_request = {}
+ end
+ end
+end
+
+--- Mark a client's results as dirty. This method will cancel any active
+--- requests to the server and pause new highlights from being added
+--- in the on_win callback. The rest of the current results are saved
+--- in case the server supports delta requests.
+---
+---@private
+---@param client_id number
+function STHighlighter:mark_dirty(client_id)
+ local state = self.client_state[client_id]
+ assert(state)
+
+ -- if we clear the version from current_result, it'll cause the
+ -- next request to be sent and will also pause new highlights
+ -- from being added in on_win until a new result comes from
+ -- the server
+ if state.current_result then
+ state.current_result.version = nil
+ end
+
+ if state.active_request.request_id then
+ local client = vim.lsp.get_client_by_id(client_id)
+ assert(client)
+ client.cancel_request(state.active_request.request_id)
+ state.active_request = {}
+ end
+end
+
+---@private
+function STHighlighter:on_change()
+ self:reset_timer()
+ if self.debounce > 0 then
+ self.timer = vim.defer_fn(function()
+ self:send_request()
+ end, self.debounce)
+ else
+ self:send_request()
+ end
+end
+
+---@private
+function STHighlighter:reset_timer()
+ local timer = self.timer
+ if timer then
+ self.timer = nil
+ if not timer:is_closing() then
+ timer:stop()
+ timer:close()
+ end
+ end
+end
+
+local M = {}
+
+--- Start the semantic token highlighting engine for the given buffer with the
+--- given client. The client must already be attached to the buffer.
+---
+--- NOTE: This is currently called automatically by |vim.lsp.buf_attach_client()|. To
+--- opt-out of semantic highlighting with a server that supports it, you can
+--- delete the semanticTokensProvider table from the {server_capabilities} of
+--- your client in your |LspAttach| callback or your configuration's
+--- `on_attach` callback:
+--- <pre>lua
+--- client.server_capabilities.semanticTokensProvider = nil
+--- </pre>
+---
+---@param bufnr number
+---@param client_id number
+---@param opts (nil|table) Optional keyword arguments
+--- - debounce (number, default: 200): Debounce token requests
+--- to the server by the given number in milliseconds
+function M.start(bufnr, client_id, opts)
+ vim.validate({
+ bufnr = { bufnr, 'n', false },
+ client_id = { client_id, 'n', false },
+ })
+
+ opts = opts or {}
+ assert(
+ (not opts.debounce or type(opts.debounce) == 'number'),
+ 'opts.debounce must be a number with the debounce time in milliseconds'
+ )
+
+ local client = vim.lsp.get_client_by_id(client_id)
+ if not client then
+ vim.notify('[LSP] No client with id ' .. client_id, vim.log.levels.ERROR)
+ return
+ end
+
+ if not vim.lsp.buf_is_attached(bufnr, client_id) then
+ vim.notify(
+ '[LSP] Client with id ' .. client_id .. ' not attached to buffer ' .. bufnr,
+ vim.log.levels.WARN
+ )
+ return
+ end
+
+ if not vim.tbl_get(client.server_capabilities, 'semanticTokensProvider', 'full') then
+ vim.notify('[LSP] Server does not support semantic tokens', vim.log.levels.WARN)
+ return
+ end
+
+ local highlighter = STHighlighter.active[bufnr]
+
+ if not highlighter then
+ highlighter = STHighlighter.new(bufnr)
+ highlighter.debounce = opts.debounce or 200
+ else
+ highlighter.debounce = math.max(highlighter.debounce, opts.debounce or 200)
+ end
+
+ highlighter:attach(client_id)
+ highlighter:send_request()
+end
+
+--- Stop the semantic token highlighting engine for the given buffer with the
+--- given client.
+---
+--- NOTE: This is automatically called by a |LspDetach| autocmd that is set up as part
+--- of `start()`, so you should only need this function to manually disengage the semantic
+--- token engine without fully detaching the LSP client from the buffer.
+---
+---@param bufnr number
+---@param client_id number
+function M.stop(bufnr, client_id)
+ vim.validate({
+ bufnr = { bufnr, 'n', false },
+ client_id = { client_id, 'n', false },
+ })
+
+ local highlighter = STHighlighter.active[bufnr]
+ if not highlighter then
+ return
+ end
+
+ highlighter:detach(client_id)
+
+ if vim.tbl_isempty(highlighter.client_state) then
+ highlighter:destroy()
+ end
+end
+
+--- Return the semantic token(s) at the given position.
+--- If called without arguments, returns the token under the cursor.
+---
+---@param bufnr number|nil Buffer number (0 for current buffer, default)
+---@param row number|nil Position row (default cursor position)
+---@param col number|nil Position column (default cursor position)
+---
+---@return table|nil (table|nil) List of tokens at position
+function M.get_at_pos(bufnr, row, col)
+ if bufnr == nil or bufnr == 0 then
+ bufnr = api.nvim_get_current_buf()
+ end
+
+ local highlighter = STHighlighter.active[bufnr]
+ if not highlighter then
+ return
+ end
+
+ if row == nil or col == nil then
+ local cursor = api.nvim_win_get_cursor(0)
+ row, col = cursor[1] - 1, cursor[2]
+ end
+
+ local tokens = {}
+ for client_id, client in pairs(highlighter.client_state) do
+ local highlights = client.current_result.highlights
+ if highlights then
+ local idx = binary_search(highlights, row)
+ for i = idx, #highlights do
+ local token = highlights[i]
+
+ if token.line > row then
+ break
+ end
+
+ if token.start_col <= col and token.end_col > col then
+ token.client_id = client_id
+ tokens[#tokens + 1] = token
+ end
+ end
+ end
+ end
+ return tokens
+end
+
+--- Force a refresh of all semantic tokens
+---
+--- Only has an effect if the buffer is currently active for semantic token
+--- highlighting (|vim.lsp.semantic_tokens.start()| has been called for it)
+---
+---@param bufnr (nil|number) default: current buffer
+function M.force_refresh(bufnr)
+ vim.validate({
+ bufnr = { bufnr, 'n', true },
+ })
+
+ if bufnr == nil or bufnr == 0 then
+ bufnr = api.nvim_get_current_buf()
+ end
+
+ local highlighter = STHighlighter.active[bufnr]
+ if not highlighter then
+ return
+ end
+
+ highlighter:reset()
+ highlighter:send_request()
+end
+
+--- |lsp-handler| for the method `workspace/semanticTokens/refresh`
+---
+--- Refresh requests are sent by the server to indicate a project-wide change
+--- that requires all tokens to be re-requested by the client. This handler will
+--- invalidate the current results of all buffers and automatically kick off a
+--- new request for buffers that are displayed in a window. For those that aren't, a
+--- the BufWinEnter event should take care of it next time it's displayed.
+---
+---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#semanticTokens_refreshRequest
+handlers['workspace/semanticTokens/refresh'] = function(err, _, ctx)
+ if err then
+ return vim.NIL
+ end
+
+ for _, bufnr in ipairs(vim.lsp.get_buffers_by_client_id(ctx.client_id)) do
+ local highlighter = STHighlighter.active[bufnr]
+ if highlighter and highlighter.client_state[ctx.client_id] then
+ highlighter:mark_dirty(ctx.client_id)
+
+ if not vim.tbl_isempty(vim.fn.win_findbuf(bufnr)) then
+ highlighter:send_request()
+ end
+ end
+ end
+
+ return vim.NIL
+end
+
+local namespace = api.nvim_create_namespace('vim_lsp_semantic_tokens')
+api.nvim_set_decoration_provider(namespace, {
+ on_win = function(_, _, bufnr, topline, botline)
+ local highlighter = STHighlighter.active[bufnr]
+ if highlighter then
+ highlighter:on_win(topline, botline)
+ end
+ end,
+})
+
+--- for testing only! there is no guarantee of API stability with this!
+---
+---@private
+M.__STHighlighter = STHighlighter
+
+return M
diff --git a/runtime/lua/vim/lsp/sync.lua b/runtime/lua/vim/lsp/sync.lua
index 0d65e86b55..826352f036 100644
--- a/runtime/lua/vim/lsp/sync.lua
+++ b/runtime/lua/vim/lsp/sync.lua
@@ -392,7 +392,7 @@ end
---@param lastline number line to begin search in old_lines for last difference
---@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
+---@returns table TextDocumentContentChangeEvent see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentContentChangeEvent
function M.compute_diff(
prev_lines,
curr_lines,
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 283099bbcf..38051e6410 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -1,6 +1,5 @@
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
@@ -110,6 +109,15 @@ local function split_lines(value)
return split(value, '\n', true)
end
+---@private
+local function create_window_without_focus()
+ local prev = vim.api.nvim_get_current_win()
+ vim.cmd.new()
+ local new = vim.api.nvim_get_current_win()
+ vim.api.nvim_set_current_win(prev)
+ return new
+end
+
--- Convert byte index to `encoding` index.
--- Convenience wrapper around vim.str_utfindex
---@param line string line to be indexed
@@ -459,35 +467,52 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding)
text = split(text_edit.newText, '\n', true),
}
- -- Some LSP servers may return +1 range of the buffer content but nvim_buf_set_text can't accept it so we should fix it here.
local max = 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 the whole edit is after the lines in the buffer we can simply add the new text to the end
+ -- of the buffer.
+ if max <= e.start_row then
+ api.nvim_buf_set_lines(bufnr, max, max, false, e.text)
+ else
+ local last_line_len = #(get_line(bufnr, math.min(e.end_row, max - 1)) or '')
+ -- 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.
if max <= e.end_row then
e.end_row = max - 1
- e.end_col = len
+ e.end_col = last_line_len
+ has_eol_text_edit = true
+ else
+ -- If the replacement is over the end of a line (i.e. e.end_col is out of bounds and the
+ -- replacement text ends with a newline We can likely assume that the replacement is assumed
+ -- to be meant to replace the newline with another newline and we need to make sure this
+ -- doesn't add an extra empty line. E.g. when the last line to be replaced contains a '\r'
+ -- in the file some servers (clangd on windows) will include that character in the line
+ -- while nvim_buf_set_text doesn't count it as part of the line.
+ if
+ e.end_col > last_line_len
+ and #text_edit.newText > 0
+ and string.sub(text_edit.newText, -1) == '\n'
+ then
+ table.remove(e.text, #e.text)
+ end
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)
- is_cursor_fixed = true
- elseif e.end_row == cursor.row and e.end_col <= cursor.col then
- cursor.row = cursor.row + (#e.text - row_count)
- cursor.col = #e.text[#e.text] + (cursor.col - e.end_col)
- if #e.text == 1 then
- cursor.col = cursor.col + e.start_col
+ -- Make sure we don't go out of bounds for e.end_col
+ e.end_col = math.min(last_line_len, e.end_col)
+
+ 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)
+ is_cursor_fixed = true
+ elseif e.end_row == cursor.row and e.end_col <= cursor.col then
+ cursor.row = cursor.row + (#e.text - row_count)
+ cursor.col = #e.text[#e.text] + (cursor.col - e.end_col)
+ if #e.text == 1 then
+ cursor.col = cursor.col + e.start_col
+ end
+ is_cursor_fixed = true
end
- is_cursor_fixed = true
end
end
@@ -755,8 +780,11 @@ local function create_file(change)
-- from spec: Overwrite wins over `ignoreIfExists`
local fname = vim.uri_to_fname(change.uri)
if not opts.ignoreIfExists or opts.overwrite then
+ vim.fn.mkdir(vim.fs.dirname(fname), 'p')
local file = io.open(fname, 'w')
- file:close()
+ if file then
+ file:close()
+ end
end
vim.fn.bufadd(fname)
end
@@ -828,7 +856,7 @@ end
--- `textDocument/signatureHelp`, and potentially others.
---
---@param input (`MarkedString` | `MarkedString[]` | `MarkupContent`)
----@param contents (table, optional, default `{}`) List of strings to extend with converted lines
+---@param contents (table|nil) List of strings to extend with converted lines. Defaults to {}.
---@returns {contents}, extended with lines of converted markdown.
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover
function M.convert_input_to_markdown_lines(input, contents)
@@ -887,8 +915,8 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers
return
end
--The active signature. If omitted or the value lies outside the range of
- --`signatures` the value defaults to zero or is ignored if `signatures.length
- --=== 0`. Whenever possible implementors should make an active decision about
+ --`signatures` the value defaults to zero or is ignored if `signatures.length == 0`.
+ --Whenever possible implementors should make an active decision about
--the active signature and shouldn't rely on a default value.
local contents = {}
local active_hl
@@ -987,6 +1015,7 @@ end
--- - border (string or table) override `border`
--- - focusable (string or table) override `focusable`
--- - zindex (string or table) override `zindex`, defaults to 50
+--- - relative ("mouse"|"cursor") defaults to "cursor"
---@returns (table) Options
function M.make_floating_popup_options(width, height, opts)
validate({
@@ -1001,7 +1030,8 @@ function M.make_floating_popup_options(width, height, opts)
local anchor = ''
local row, col
- local lines_above = vim.fn.winline() - 1
+ local lines_above = opts.relative == 'mouse' and vim.fn.getmousepos().line - 1
+ or vim.fn.winline() - 1
local lines_below = vim.fn.winheight(0) - lines_above
if lines_above < lines_below then
@@ -1014,7 +1044,9 @@ function M.make_floating_popup_options(width, height, opts)
row = 0
end
- if vim.fn.wincol() + width + (opts.offset_x or 0) <= api.nvim_get_option('columns') then
+ local wincol = opts.relative == 'mouse' and vim.fn.getmousepos().column or vim.fn.wincol()
+
+ if wincol + width + (opts.offset_x or 0) <= api.nvim_get_option('columns') then
anchor = anchor .. 'W'
col = 0
else
@@ -1022,64 +1054,103 @@ function M.make_floating_popup_options(width, height, opts)
col = 1
end
+ local title = (opts.border and opts.title) and opts.title or nil
+ local title_pos
+
+ if title then
+ title_pos = opts.title_pos or 'center'
+ end
+
return {
anchor = anchor,
col = col + (opts.offset_x or 0),
height = height,
focusable = opts.focusable,
- relative = 'cursor',
+ relative = opts.relative == 'mouse' and 'mouse' or 'cursor',
row = row + (opts.offset_y or 0),
style = 'minimal',
width = width,
border = opts.border or default_border,
zindex = opts.zindex or 50,
+ title = title,
+ title_pos = title_pos,
}
end
---- Jumps to a location.
+--- Shows document and optionally jumps to the location.
---
---@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, offset_encoding, reuse_win)
+---@param offset_encoding "utf-8" | "utf-16" | "utf-32"
+---@param opts table|nil options
+--- - reuse_win (boolean) Jump to existing window if buffer is already open.
+--- - focus (boolean) Whether to focus/jump to location if possible. Defaults to true.
+---@return boolean `true` if succeeded
+function M.show_document(location, offset_encoding, opts)
-- location may be Location or LocationLink
local uri = location.uri or location.targetUri
if uri == nil then
- return
+ return false
end
if offset_encoding == nil then
- vim.notify_once(
- 'jump_to_location must be called with valid offset encoding',
- vim.log.levels.WARN
- )
+ vim.notify_once('show_document 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'")
- -- 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')
+ opts = opts or {}
+ local focus = vim.F.if_nil(opts.focus, true)
+ if focus then
+ -- Save position in jumplist
+ vim.cmd("normal! m'")
- --- Jump to new location (adjusting for UTF-16 encoding of characters)
- local win = reuse_win and bufwinid(bufnr)
- if win then
+ -- 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')
+ end
+
+ local win = opts.reuse_win and bufwinid(bufnr)
+ or focus and api.nvim_get_current_win()
+ or create_window_without_focus()
+
+ api.nvim_buf_set_option(bufnr, 'buflisted', true)
+ api.nvim_win_set_buf(win, bufnr)
+ if focus then
api.nvim_set_current_win(win)
- else
- api.nvim_buf_set_option(bufnr, 'buflisted', true)
- api.nvim_set_current_buf(bufnr)
end
+
+ -- location may be Location or LocationLink
local range = location.range or location.targetSelectionRange
- local row = range.start.line
- 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')
+ if range then
+ --- Jump to new location (adjusting for encoding of characters)
+ local row = range.start.line
+ local col = get_line_byte_from_position(bufnr, range.start, offset_encoding)
+ api.nvim_win_set_cursor(win, { row + 1, col })
+ api.nvim_win_call(win, function()
+ -- Open folds under the cursor
+ vim.cmd('normal! zv')
+ end)
+ end
+
return true
end
+--- Jumps to a location.
+---
+---@param location table (`Location`|`LocationLink`)
+---@param offset_encoding "utf-8" | "utf-16" | "utf-32"
+---@param reuse_win boolean|nil Jump to existing window if buffer is already open.
+---@return boolean `true` if the jump succeeded
+function M.jump_to_location(location, offset_encoding, reuse_win)
+ if offset_encoding == nil then
+ vim.notify_once(
+ 'jump_to_location must be called with valid offset encoding',
+ vim.log.levels.WARN
+ )
+ end
+
+ return M.show_document(location, offset_encoding, { reuse_win = reuse_win, focus = true })
+end
+
--- Previews a location in a floating window
---
--- behavior depends on type of location:
@@ -1194,7 +1265,7 @@ function M.stylize_markdown(bufnr, contents, opts)
-- when ft is nil, we get the ft from the regex match
local matchers = {
block = { nil, '```+([a-zA-Z0-9_]*)', '```+' },
- pre = { '', '<pre>', '</pre>' },
+ pre = { nil, '<pre>([a-z0-9]*)', '</pre>' },
code = { '', '<code>', '</code>' },
text = { 'text', '<text>', '</text>' },
}
@@ -1219,8 +1290,6 @@ function M.stylize_markdown(bufnr, contents, opts)
-- Clean up
contents = M._trim(contents, opts)
- -- Insert blank line separator after code block?
- local add_sep = opts.separator == nil and true or opts.separator
local stripped = {}
local highlights = {}
-- keep track of lnums that contain markdown
@@ -1248,7 +1317,7 @@ function M.stylize_markdown(bufnr, contents, opts)
finish = #stripped,
})
-- add a separator, but not on the last line
- if add_sep and i < #contents then
+ if opts.separator and i < #contents then
table.insert(stripped, '---')
markdown_lines[#stripped] = true
end
@@ -1484,7 +1553,7 @@ end
---
---@param contents table of lines to show in window
---@param syntax string of syntax to set for opened buffer
----@param opts table with optional fields (additional keys are passed on to |vim.api.nvim_open_win()|)
+---@param opts table with optional fields (additional keys are passed on to |nvim_open_win()|)
--- - height: (number) height of floating window
--- - width: (number) width of floating window
--- - wrap: (boolean, default true) wrap long lines
@@ -1612,7 +1681,7 @@ 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".
- ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#documentHighlight
+ ---@see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentContentChangeEvent
function M.buf_highlight_references(bufnr, references, offset_encoding)
validate({
bufnr = { bufnr, 'n', true },
@@ -1799,7 +1868,7 @@ end
--- CAUTION: Modifies the input in-place!
---
---@param lines (table) list of lines
----@returns (string) filetype or 'markdown' if it was unchanged.
+---@returns (string) filetype or "markdown" if it was unchanged.
function M.try_trim_markdown_code_blocks(lines)
local language_id = lines[1]:match('^```(.*)')
if language_id then
@@ -1843,7 +1912,7 @@ end
--- Creates a `TextDocumentPositionParams` object for the current buffer and cursor position.
---
---@param window number|nil: window handle or 0 for current, defaults to current
----@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window`
+---@param offset_encoding string|nil utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window`
---@returns `TextDocumentPositionParams` object
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams
function M.make_position_params(window, offset_encoding)
@@ -1866,7 +1935,7 @@ function M._get_offset_encoding(bufnr)
local offset_encoding
- for _, client in pairs(vim.lsp.buf_get_clients(bufnr)) do
+ for _, client in pairs(vim.lsp.get_active_clients({ bufnr = bufnr })) do
if client.offset_encoding == nil then
vim.notify_once(
string.format(
@@ -1972,7 +2041,7 @@ function M.make_workspace_params(added, removed)
end
--- Returns indentation size.
---
----@see |shiftwidth|
+---@see 'shiftwidth'
---@param bufnr (number|nil): Buffer handle, defaults to current
---@returns (number) indentation size
function M.get_effective_tabstop(bufnr)
diff --git a/runtime/lua/vim/secure.lua b/runtime/lua/vim/secure.lua
new file mode 100644
index 0000000000..443b152273
--- /dev/null
+++ b/runtime/lua/vim/secure.lua
@@ -0,0 +1,188 @@
+local M = {}
+
+---@private
+--- Reads trust database from $XDG_STATE_HOME/nvim/trust.
+---
+---@return (table) Contents of trust database, if it exists. Empty table otherwise.
+local function read_trust()
+ local trust = {}
+ local f = io.open(vim.fn.stdpath('state') .. '/trust', 'r')
+ if f then
+ local contents = f:read('*a')
+ if contents then
+ for line in vim.gsplit(contents, '\n') do
+ local hash, file = string.match(line, '^(%S+) (.+)$')
+ if hash and file then
+ trust[file] = hash
+ end
+ end
+ end
+ f:close()
+ end
+ return trust
+end
+
+---@private
+--- Writes provided {trust} table to trust database at
+--- $XDG_STATE_HOME/nvim/trust.
+---
+---@param trust (table) Trust table to write
+local function write_trust(trust)
+ vim.validate({ trust = { trust, 't' } })
+ local f = assert(io.open(vim.fn.stdpath('state') .. '/trust', 'w'))
+
+ local t = {}
+ for p, h in pairs(trust) do
+ t[#t + 1] = string.format('%s %s\n', h, p)
+ end
+ f:write(table.concat(t))
+ f:close()
+end
+
+--- Attempt to read the file at {path} prompting the user if the file should be
+--- trusted. The user's choice is persisted in a trust database at
+--- $XDG_STATE_HOME/nvim/trust.
+---
+---@see |:trust|
+---
+---@param path (string) Path to a file to read.
+---
+---@return (string|nil) The contents of the given file if it exists and is
+--- trusted, or nil otherwise.
+function M.read(path)
+ vim.validate({ path = { path, 's' } })
+ local fullpath = vim.loop.fs_realpath(vim.fs.normalize(path))
+ if not fullpath then
+ return nil
+ end
+
+ local trust = read_trust()
+
+ if trust[fullpath] == '!' then
+ -- File is denied
+ return nil
+ end
+
+ local contents
+ do
+ local f = io.open(fullpath, 'r')
+ if not f then
+ return nil
+ end
+ contents = f:read('*a')
+ f:close()
+ end
+
+ local hash = vim.fn.sha256(contents)
+ if trust[fullpath] == hash then
+ -- File already exists in trust database
+ return contents
+ end
+
+ -- File either does not exist in trust database or the hash does not match
+ local ok, result = pcall(
+ vim.fn.confirm,
+ string.format('%s is not trusted.', fullpath),
+ '&ignore\n&view\n&deny\n&allow',
+ 1
+ )
+
+ if not ok and result ~= 'Keyboard interrupt' then
+ error(result)
+ elseif not ok or result == 0 or result == 1 then
+ -- Cancelled or ignored
+ return nil
+ elseif result == 2 then
+ -- View
+ vim.cmd('sview ' .. fullpath)
+ return nil
+ elseif result == 3 then
+ -- Deny
+ trust[fullpath] = '!'
+ contents = nil
+ elseif result == 4 then
+ -- Allow
+ trust[fullpath] = hash
+ end
+
+ write_trust(trust)
+
+ return contents
+end
+
+--- Manage the trust database.
+---
+--- The trust database is located at |$XDG_STATE_HOME|/nvim/trust.
+---
+---@param opts (table):
+--- - action (string): "allow" to add a file to the trust database and trust it,
+--- "deny" to add a file to the trust database and deny it,
+--- "remove" to remove file from the trust database
+--- - path (string|nil): Path to a file to update. Mutually exclusive with {bufnr}.
+--- Cannot be used when {action} is "allow".
+--- - bufnr (number|nil): Buffer number to update. Mutually exclusive with {path}.
+---@return (boolean, string) success, msg:
+--- - true and full path of target file if operation was successful
+--- - false and error message on failure
+function M.trust(opts)
+ vim.validate({
+ path = { opts.path, 's', true },
+ bufnr = { opts.bufnr, 'n', true },
+ action = {
+ opts.action,
+ function(m)
+ return m == 'allow' or m == 'deny' or m == 'remove'
+ end,
+ [["allow" or "deny" or "remove"]],
+ },
+ })
+
+ local path = opts.path
+ local bufnr = opts.bufnr
+ local action = opts.action
+
+ assert(not path or not bufnr, '"path" and "bufnr" are mutually exclusive')
+
+ if action == 'allow' then
+ assert(not path, '"path" is not valid when action is "allow"')
+ end
+
+ local fullpath
+ if path then
+ fullpath = vim.loop.fs_realpath(vim.fs.normalize(path))
+ elseif bufnr then
+ local bufname = vim.api.nvim_buf_get_name(bufnr)
+ if bufname == '' then
+ return false, 'buffer is not associated with a file'
+ end
+ fullpath = vim.loop.fs_realpath(vim.fs.normalize(bufname))
+ else
+ error('one of "path" or "bufnr" is required')
+ end
+
+ if not fullpath then
+ return false, string.format('invalid path: %s', path)
+ end
+
+ local trust = read_trust()
+
+ if action == 'allow' then
+ local newline = vim.bo[bufnr].fileformat == 'unix' and '\n' or '\r\n'
+ local contents = table.concat(vim.api.nvim_buf_get_lines(bufnr, 0, -1, false), newline)
+ if vim.bo[bufnr].endofline then
+ contents = contents .. newline
+ end
+ local hash = vim.fn.sha256(contents)
+
+ trust[fullpath] = hash
+ elseif action == 'deny' then
+ trust[fullpath] = '!'
+ elseif action == 'remove' then
+ trust[fullpath] = nil
+ end
+
+ write_trust(trust)
+ return true, fullpath
+end
+
+return M
diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua
index e1b4ed4ea9..cc48e3f193 100644
--- a/runtime/lua/vim/shared.lua
+++ b/runtime/lua/vim/shared.lua
@@ -6,7 +6,7 @@
-- 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 {}
+vim = vim or {}
--- Returns a deep copy of the given object. Non-table objects are copied as
--- in a typical Lua assignment, whereas table objects are copied recursively.
@@ -14,8 +14,9 @@ local vim = vim or {}
--- same functions as those in the input table. Userdata and threads are not
--- copied and will throw an error.
---
----@param orig table Table to copy
----@return table Table of copied keys and (nested) values.
+---@generic T: table
+---@param orig T Table to copy
+---@return T Table of copied keys and (nested) values.
function vim.deepcopy(orig) end -- luacheck: no unused
vim.deepcopy = (function()
local function _id(v)
@@ -48,6 +49,9 @@ vim.deepcopy = (function()
if f then
return f(orig, cache or {})
else
+ if type(orig) == 'userdata' and orig == vim.NIL then
+ return vim.NIL
+ end
error('Cannot deepcopy object of type ' .. type(orig))
end
end
@@ -56,13 +60,14 @@ end)()
--- Splits a string at each instance of a separator.
---
---@see |vim.split()|
+---@see |luaref-patterns|
---@see https://www.lua.org/pil/20.2.html
---@see http://lua-users.org/wiki/StringLibraryTutorial
---
---@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
+---@param plain (boolean|nil) If `true` use `sep` literally (passed to string.find)
+---@return fun():string (function) Iterator over the split components
function vim.gsplit(s, sep, plain)
vim.validate({ s = { s, 's' }, sep = { sep, 's' }, plain = { plain, 'b', true } })
@@ -98,10 +103,10 @@ end
--- Splits a string at each instance of a separator.
---
--- Examples:
---- <pre>
---- split(":aa::b:", ":") --> {'','aa','','b',''}
---- split("axaby", "ab?") --> {'','x','y'}
---- split("x*yz*o", "*", {plain=true}) --> {'x','yz','o'}
+--- <pre>lua
+--- split(":aa::b:", ":") --> {'','aa','','b',''}
+--- split("axaby", "ab?") --> {'','x','y'}
+--- split("x*yz*o", "*", {plain=true}) --> {'x','yz','o'}
--- split("|x|y|z|", "|", {trimempty=true}) --> {'x', 'y', 'z'}
--- </pre>
---
@@ -109,11 +114,11 @@ end
---
---@param s string String to split
---@param sep string Separator or pattern
----@param kwargs table Keyword arguments:
+---@param kwargs (table|nil) 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 table List of split components
+---@return string[] List of split components
function vim.split(s, sep, kwargs)
local plain
local trimempty = false
@@ -156,8 +161,9 @@ end
---
---@see From https://github.com/premake/premake-core/blob/master/src/base/table.lua
---
----@param t table Table
----@return table List of keys
+---@generic T: table
+---@param t table<T, any> (table) Table
+---@return T[] (list) List of keys
function vim.tbl_keys(t)
assert(type(t) == 'table', string.format('Expected table, got %s', type(t)))
@@ -171,8 +177,9 @@ 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 Table
----@return table List of values
+---@generic T
+---@param t table<any, T> (table) Table
+---@return T[] (list) List of values
function vim.tbl_values(t)
assert(type(t) == 'table', string.format('Expected table, got %s', type(t)))
@@ -185,8 +192,9 @@ end
--- Apply a function to all values of a table.
---
----@param func function|table Function or callable table
----@param t table Table
+---@generic T
+---@param func fun(value: T): any (function) Function
+---@param t table<any, T> (table) Table
---@return table Table of transformed values
function vim.tbl_map(func, t)
vim.validate({ func = { func, 'c' }, t = { t, 't' } })
@@ -200,9 +208,10 @@ end
--- Filter a table using a predicate function
---
----@param func function|table Function or callable table
----@param t table Table
----@return table Table of filtered values
+---@generic T
+---@param func fun(value: T): boolean (function) Function
+---@param t table<any, T> (table) Table
+---@return T[] (table) Table of filtered values
function vim.tbl_filter(func, t)
vim.validate({ func = { func, 'c' }, t = { t, 't' } })
@@ -302,14 +311,16 @@ end
--- Merges recursively two or more map-like tables.
---
----@see |tbl_extend()|
+---@see |vim.tbl_extend()|
---
----@param behavior string Decides what to do if a key is found in more than one map:
+---@generic T1: table
+---@generic T2: table
+---@param behavior "error"|"keep"|"force" (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 ... table Two or more map-like tables
----@return table Merged table
+---@param ... T2 Two or more map-like tables
+---@return T1|T2 (table) Merged table
function vim.tbl_deep_extend(behavior, ...)
return tbl_extend(behavior, true, ...)
end
@@ -373,7 +384,7 @@ end
--- Return `nil` if the key does not exist.
---
--- Examples:
---- <pre>
+--- <pre>lua
--- vim.tbl_get({ key = { nested_key = true }}, 'key', 'nested_key') == true
--- vim.tbl_get({ key = {}}, 'key', 'nested_key') == nil
--- </pre>
@@ -385,15 +396,14 @@ end
function vim.tbl_get(o, ...)
local keys = { ... }
if #keys == 0 then
- return
+ return nil
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
+ return nil
+ elseif type(o) ~= 'table' and next(keys, i) then
+ return nil
end
end
return o
@@ -405,11 +415,12 @@ end
---
---@see |vim.tbl_extend()|
---
----@param dst table List which will be modified and appended to
+---@generic T: table
+---@param dst T 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
+---@param start (number|nil) Start index on src. Defaults to 1
+---@param finish (number|nil) Final index on src. Defaults to `#src`
+---@return T dst
function vim.list_extend(dst, src, start, finish)
vim.validate({
dst = { dst, 't' },
@@ -447,6 +458,33 @@ function vim.tbl_flatten(t)
return result
end
+--- Enumerate a table sorted by its keys.
+---
+---@see Based on https://github.com/premake/premake-core/blob/master/src/base/table.lua
+---
+---@param t table List-like table
+---@return iterator over sorted keys and their values
+function vim.spairs(t)
+ assert(type(t) == 'table', string.format('Expected table, got %s', type(t)))
+
+ -- collect the keys
+ local keys = {}
+ for k in pairs(t) do
+ table.insert(keys, k)
+ end
+ table.sort(keys)
+
+ -- Return the iterator function.
+ -- TODO(justinmk): Return "iterator function, table {t}, and nil", like pairs()?
+ local i = 0
+ return function()
+ i = i + 1
+ if keys[i] then
+ return keys[i], t[keys[i]]
+ end
+ end
+end
+
--- Tests if a Lua table can be treated as an array.
---
--- Empty table `{}` is assumed to be an array, unless it was created by
@@ -476,7 +514,7 @@ function vim.tbl_islist(t)
-- TODO(bfredl): in the future, we will always be inside nvim
-- then this check can be deleted.
if vim._empty_dict_mt == nil then
- return nil
+ return false
end
return getmetatable(t) ~= vim._empty_dict_mt
end
@@ -484,9 +522,9 @@ end
--- Counts the number of non-nil values in table `t`.
---
---- <pre>
---- vim.tbl_count({ a=1, b=2 }) => 2
---- vim.tbl_count({ 1, 2 }) => 2
+--- <pre>lua
+--- vim.tbl_count({ a=1, b=2 }) --> 2
+--- vim.tbl_count({ 1, 2 }) --> 2
--- </pre>
---
---@see https://github.com/Tieske/Penlight/blob/master/lua/pl/tablex.lua
@@ -504,10 +542,11 @@ end
--- Creates a copy of a table containing only elements from start to end (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)
+---@generic T
+---@param list T[] (list) Table
+---@param start number|nil Start range of slice
+---@param finish number|nil End range of slice
+---@return T[] (list) 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
@@ -518,6 +557,7 @@ end
--- Trim whitespace (Lua pattern "%s") from both sides of a string.
---
+---@see |luaref-patterns|
---@see https://www.lua.org/pil/20.2.html
---@param s string String to trim
---@return string String with whitespace removed from its beginning and end
@@ -533,7 +573,7 @@ end
---@return string %-escaped pattern string
function vim.pesc(s)
vim.validate({ s = { s, 's' } })
- return s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1')
+ return (s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1'))
end
--- Tests if `s` starts with `prefix`.
@@ -559,7 +599,7 @@ end
--- Validates a parameter specification (types and values).
---
--- Usage example:
---- <pre>
+--- <pre>lua
--- function user.new(name, age, hobbies)
--- vim.validate{
--- name={name, 'string'},
@@ -571,24 +611,24 @@ end
--- </pre>
---
--- Examples with explicit argument values (can be run directly):
---- <pre>
+--- <pre>lua
--- vim.validate{arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}}
---- => NOP (success)
+--- --> NOP (success)
---
--- vim.validate{arg1={1, 'table'}}
---- => error('arg1: expected table, got number')
+--- --> error('arg1: expected table, got number')
---
--- vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}}
---- => error('arg1: expected even number, got 3')
+--- --> error('arg1: expected even number, got 3')
--- </pre>
---
--- If multiple types are valid they can be given as a list.
---- <pre>
+--- <pre>lua
--- vim.validate{arg1={{'foo'}, {'table', 'string'}}, arg2={'foo', {'table', 'string'}}}
---- => NOP (success)
+--- --> NOP (success)
---
--- vim.validate{arg1={1, {'string', table'}}}
---- => error('arg1: expected string|table, got number')
+--- --> error('arg1: expected string|table, got number')
---
--- </pre>
---
@@ -715,5 +755,30 @@ function vim.is_callable(f)
return type(m.__call) == 'function'
end
+--- Creates a table whose members are automatically created when accessed, if they don't already
+--- exist.
+---
+--- They mimic defaultdict in python.
+---
+--- If {create} is `nil`, this will create a defaulttable whose constructor function is
+--- this function, effectively allowing to create nested tables on the fly:
+---
+--- <pre>lua
+--- local a = vim.defaulttable()
+--- a.b.c = 1
+--- </pre>
+---
+---@param create function|nil The function called to create a missing value.
+---@return table Empty table with metamethod
+function vim.defaulttable(create)
+ create = create or vim.defaulttable
+ return setmetatable({}, {
+ __index = function(tbl, key)
+ rawset(tbl, key, create())
+ return rawget(tbl, key)
+ end,
+ })
+end
+
return vim
-- vim:sw=2 ts=2 et
diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua
index 70f2c425ed..582922ecb6 100644
--- a/runtime/lua/vim/treesitter.lua
+++ b/runtime/lua/vim/treesitter.lua
@@ -3,10 +3,7 @@ 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 parsers = setmetatable({}, { __mode = 'v' })
local M = vim.tbl_extend('error', query, language)
@@ -28,13 +25,15 @@ setmetatable(M, {
end,
})
---- Creates a new parser.
+--- Creates a new parser
---
---- It is not recommended to use this, use vim.treesitter.get_parser() instead.
+--- It is not recommended to use this; use |get_parser()| instead.
---
----@param bufnr The buffer the parser will be tied to
----@param lang The language of the parser
----@param opts Options to pass to the created language tree
+---@param bufnr string Buffer the parser will be tied to (0 for current buffer)
+---@param lang string Language of the parser
+---@param opts (table|nil) Options to pass to the created language tree
+---
+---@return LanguageTree |LanguageTree| object to use for parsing
function M._create_parser(bufnr, lang, opts)
language.require_language(lang)
if bufnr == 0 then
@@ -74,16 +73,15 @@ function M._create_parser(bufnr, lang, opts)
return self
end
---- Gets the parser for this bufnr / ft combination.
+--- Returns the parser for a specific buffer and filetype and attaches it to the buffer
---
---- If needed this will create the parser.
---- Unconditionally attach the provided callback
+--- If needed, this will create the parser.
---
----@param bufnr The buffer the parser should be tied to
----@param lang The filetype of this parser
----@param opts Options object to pass to the created language tree
+---@param bufnr (number|nil) Buffer the parser should be tied to (default: current buffer)
+---@param lang (string|nil) Filetype of this parser (default: buffer filetype)
+---@param opts (table|nil) Options to pass to the created language tree
---
----@returns The parser
+---@return LanguageTree |LanguageTree| object to use for parsing
function M.get_parser(bufnr, lang, opts)
opts = opts or {}
@@ -103,11 +101,13 @@ function M.get_parser(bufnr, lang, opts)
return parsers[bufnr]
end
---- Gets a string parser
+--- Returns a string parser
+---
+---@param str string Text to parse
+---@param lang string Language of this string
+---@param opts (table|nil) Options to pass to the created language tree
---
----@param str The string to parse
----@param lang The language of this string
----@param opts Options to pass to the created language tree
+---@return LanguageTree |LanguageTree| object to use for parsing
function M.get_string_parser(str, lang, opts)
vim.validate({
str = { str, 'string' },
@@ -118,4 +118,425 @@ function M.get_string_parser(str, lang, opts)
return LanguageTree.new(str, lang, opts)
end
+--- Determines whether a node is the ancestor of another
+---
+---@param dest userdata Possible ancestor |tsnode|
+---@param source userdata Possible descendant |tsnode|
+---
+---@return boolean True if {dest} is an ancestor of {source}
+function M.is_ancestor(dest, source)
+ if not (dest and source) then
+ return false
+ end
+
+ local current = source
+ while current ~= nil do
+ if current == dest then
+ return true
+ end
+
+ current = current:parent()
+ end
+
+ return false
+end
+
+--- Returns the node's range or an unpacked range table
+---
+---@param node_or_range (userdata|table) |tsnode| or table of positions
+---
+---@return table `{ start_row, start_col, end_row, end_col }`
+function M.get_node_range(node_or_range)
+ if type(node_or_range) == 'table' then
+ return unpack(node_or_range)
+ else
+ return node_or_range:range()
+ end
+end
+
+--- Determines whether (line, col) position is in node range
+---
+---@param node userdata |tsnode| defining the range
+---@param line number Line (0-based)
+---@param col number Column (0-based)
+---
+---@return boolean True if the position is in node range
+function M.is_in_node_range(node, line, col)
+ local start_line, start_col, end_line, end_col = M.get_node_range(node)
+ if line >= start_line and line <= end_line then
+ if line == start_line and line == end_line then
+ return col >= start_col and col < end_col
+ elseif line == start_line then
+ return col >= start_col
+ elseif line == end_line then
+ return col < end_col
+ else
+ return true
+ end
+ else
+ return false
+ end
+end
+
+--- Determines if a node contains a range
+---
+---@param node userdata |tsnode|
+---@param range table
+---
+---@return boolean True if the {node} contains the {range}
+function M.node_contains(node, range)
+ local start_row, start_col, end_row, end_col = node: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])
+
+ return start_fits and end_fits
+end
+
+--- Returns a list of highlight captures at the given position
+---
+--- Each capture is represented by a table containing the capture name as a string as
+--- well as a table of metadata (`priority`, `conceal`, ...; empty if none are defined).
+---
+---@param bufnr number Buffer number (0 for current buffer)
+---@param row number Position row
+---@param col number Position column
+---
+---@return table[] List of captures `{ capture = "capture name", metadata = { ... } }`
+function M.get_captures_at_pos(bufnr, row, col)
+ if bufnr == 0 then
+ bufnr = a.nvim_get_current_buf()
+ end
+ local buf_highlighter = M.highlighter.active[bufnr]
+
+ if not buf_highlighter then
+ return {}
+ end
+
+ local matches = {}
+
+ buf_highlighter.tree:for_each_tree(function(tstree, tree)
+ if not tstree then
+ return
+ end
+
+ local root = tstree:root()
+ local root_start_row, _, root_end_row, _ = root:range()
+
+ -- Only worry about trees within the line range
+ if root_start_row > row or root_end_row < row then
+ return
+ end
+
+ local q = buf_highlighter:get_query(tree:lang())
+
+ -- Some injected languages may not have highlight queries.
+ if not q:query() then
+ return
+ end
+
+ local iter = q:query():iter_captures(root, buf_highlighter.bufnr, row, row + 1)
+
+ for capture, node, metadata in iter do
+ if M.is_in_node_range(node, row, col) then
+ local c = q._query.captures[capture] -- name of the capture in the query
+ if c ~= nil then
+ table.insert(matches, { capture = c, metadata = metadata, lang = tree:lang() })
+ end
+ end
+ end
+ end, true)
+ return matches
+end
+
+--- Returns a list of highlight capture names under the cursor
+---
+---@param winnr (number|nil) Window handle or 0 for current window (default)
+---
+---@return string[] List of capture names
+function M.get_captures_at_cursor(winnr)
+ winnr = winnr or 0
+ local bufnr = a.nvim_win_get_buf(winnr)
+ local cursor = a.nvim_win_get_cursor(winnr)
+
+ local data = M.get_captures_at_pos(bufnr, cursor[1] - 1, cursor[2])
+
+ local captures = {}
+
+ for _, capture in ipairs(data) do
+ table.insert(captures, capture.capture)
+ end
+
+ return captures
+end
+
+--- Returns the smallest named node at the given position
+---
+---@param bufnr number Buffer number (0 for current buffer)
+---@param row number Position row
+---@param col number Position column
+---@param opts table Optional keyword arguments:
+--- - lang string|nil Parser language
+--- - ignore_injections boolean Ignore injected languages (default true)
+---
+---@return userdata|nil |tsnode| under the cursor
+function M.get_node_at_pos(bufnr, row, col, opts)
+ if bufnr == 0 then
+ bufnr = a.nvim_get_current_buf()
+ end
+ local ts_range = { row, col, row, col }
+
+ local root_lang_tree = M.get_parser(bufnr, opts.lang)
+ if not root_lang_tree then
+ return
+ end
+
+ return root_lang_tree:named_node_for_range(ts_range, opts)
+end
+
+--- Returns the smallest named node under the cursor
+---
+---@param winnr (number|nil) Window handle or 0 for current window (default)
+---
+---@return string Name of node under the cursor
+function M.get_node_at_cursor(winnr)
+ winnr = winnr or 0
+ local bufnr = a.nvim_win_get_buf(winnr)
+ local cursor = a.nvim_win_get_cursor(winnr)
+
+ return M.get_node_at_pos(bufnr, cursor[1] - 1, cursor[2], { ignore_injections = false }):type()
+end
+
+--- Starts treesitter highlighting for a buffer
+---
+--- Can be used in an ftplugin or FileType autocommand.
+---
+--- Note: By default, disables regex syntax highlighting, which may be required for some plugins.
+--- In this case, add ``vim.bo.syntax = 'on'`` after the call to `start`.
+---
+--- Example:
+--- <pre>lua
+--- vim.api.nvim_create_autocmd( 'FileType', { pattern = 'tex',
+--- callback = function(args)
+--- vim.treesitter.start(args.buf, 'latex')
+--- vim.bo[args.buf].syntax = 'on' -- only if additional legacy syntax is needed
+--- end
+--- })
+--- </pre>
+---
+---@param bufnr (number|nil) Buffer to be highlighted (default: current buffer)
+---@param lang (string|nil) Language of the parser (default: buffer filetype)
+function M.start(bufnr, lang)
+ bufnr = bufnr or a.nvim_get_current_buf()
+ local parser = M.get_parser(bufnr, lang)
+ M.highlighter.new(parser)
+end
+
+--- Stops treesitter highlighting for a buffer
+---
+---@param bufnr (number|nil) Buffer to stop highlighting (default: current buffer)
+function M.stop(bufnr)
+ bufnr = bufnr or a.nvim_get_current_buf()
+
+ if M.highlighter.active[bufnr] then
+ M.highlighter.active[bufnr]:destroy()
+ end
+end
+
+--- Open a window that displays a textual representation of the nodes in the language tree.
+---
+--- While in the window, press "a" to toggle display of anonymous nodes, "I" to toggle the
+--- display of the source language of each node, and press <Enter> to jump to the node under the
+--- cursor in the source buffer.
+---
+---@param opts table|nil Optional options table with the following possible keys:
+--- - lang (string|nil): The language of the source buffer. If omitted, the
+--- filetype of the source buffer is used.
+--- - bufnr (number|nil): Buffer to draw the tree into. If omitted, a new
+--- buffer is created.
+--- - winid (number|nil): Window id to display the tree buffer in. If omitted,
+--- a new window is created with {command}.
+--- - command (string|nil): Vimscript command to create the window. Default
+--- value is "topleft 60vnew". Only used when {winid} is nil.
+--- - title (string|fun(bufnr:number):string|nil): Title of the window. If a
+--- function, it accepts the buffer number of the source buffer as its only
+--- argument and should return a string.
+function M.show_tree(opts)
+ vim.validate({
+ opts = { opts, 't', true },
+ })
+
+ opts = opts or {}
+
+ local Playground = require('vim.treesitter.playground')
+ local buf = a.nvim_get_current_buf()
+ local win = a.nvim_get_current_win()
+ local pg = assert(Playground:new(buf, opts.lang))
+
+ -- Close any existing playground window
+ if vim.b[buf].playground then
+ local w = vim.b[buf].playground
+ if a.nvim_win_is_valid(w) then
+ a.nvim_win_close(w, true)
+ end
+ end
+
+ local w = opts.winid
+ if not w then
+ vim.cmd(opts.command or 'topleft 60vnew')
+ w = a.nvim_get_current_win()
+ end
+
+ local b = opts.bufnr
+ if b then
+ a.nvim_win_set_buf(w, b)
+ else
+ b = a.nvim_win_get_buf(w)
+ end
+
+ vim.b[buf].playground = w
+
+ vim.wo[w].scrolloff = 5
+ vim.wo[w].wrap = false
+ vim.bo[b].buflisted = false
+ vim.bo[b].buftype = 'nofile'
+ vim.bo[b].bufhidden = 'wipe'
+
+ local title = opts.title
+ if not title then
+ local bufname = a.nvim_buf_get_name(buf)
+ title = string.format('Syntax tree for %s', vim.fn.fnamemodify(bufname, ':.'))
+ elseif type(title) == 'function' then
+ title = title(buf)
+ end
+
+ assert(type(title) == 'string', 'Window title must be a string')
+ a.nvim_buf_set_name(b, title)
+
+ pg:draw(b)
+
+ vim.fn.matchadd('Comment', '\\[[0-9:-]\\+\\]')
+ vim.fn.matchadd('String', '".*"')
+
+ a.nvim_buf_clear_namespace(buf, pg.ns, 0, -1)
+ a.nvim_buf_set_keymap(b, 'n', '<CR>', '', {
+ desc = 'Jump to the node under the cursor in the source buffer',
+ callback = function()
+ local row = a.nvim_win_get_cursor(w)[1]
+ local pos = pg:get(row)
+ a.nvim_set_current_win(win)
+ a.nvim_win_set_cursor(win, { pos.lnum + 1, pos.col })
+ end,
+ })
+ a.nvim_buf_set_keymap(b, 'n', 'a', '', {
+ desc = 'Toggle anonymous nodes',
+ callback = function()
+ pg.opts.anon = not pg.opts.anon
+ pg:draw(b)
+ end,
+ })
+ a.nvim_buf_set_keymap(b, 'n', 'I', '', {
+ desc = 'Toggle language display',
+ callback = function()
+ pg.opts.lang = not pg.opts.lang
+ pg:draw(b)
+ end,
+ })
+
+ local group = a.nvim_create_augroup('treesitter/playground', {})
+
+ a.nvim_create_autocmd('CursorMoved', {
+ group = group,
+ buffer = b,
+ callback = function()
+ a.nvim_buf_clear_namespace(buf, pg.ns, 0, -1)
+ local row = a.nvim_win_get_cursor(w)[1]
+ local pos = pg:get(row)
+ a.nvim_buf_set_extmark(buf, pg.ns, pos.lnum, pos.col, {
+ end_row = pos.end_lnum,
+ end_col = math.max(0, pos.end_col),
+ hl_group = 'Visual',
+ })
+ end,
+ })
+
+ a.nvim_create_autocmd('CursorMoved', {
+ group = group,
+ buffer = buf,
+ callback = function()
+ if not a.nvim_buf_is_loaded(b) then
+ return true
+ end
+
+ a.nvim_buf_clear_namespace(b, pg.ns, 0, -1)
+
+ local cursor = a.nvim_win_get_cursor(win)
+ local cursor_node = M.get_node_at_pos(buf, cursor[1] - 1, cursor[2], {
+ lang = opts.lang,
+ ignore_injections = false,
+ })
+ if not cursor_node then
+ return
+ end
+
+ local cursor_node_id = cursor_node:id()
+ for i, v in pg:iter() do
+ if v.id == cursor_node_id then
+ local start = v.depth
+ local end_col = start + #v.text
+ a.nvim_buf_set_extmark(b, pg.ns, i - 1, start, {
+ end_col = end_col,
+ hl_group = 'Visual',
+ })
+ a.nvim_win_set_cursor(w, { i, 0 })
+ break
+ end
+ end
+ end,
+ })
+
+ a.nvim_create_autocmd({ 'TextChanged', 'InsertLeave' }, {
+ group = group,
+ buffer = buf,
+ callback = function()
+ if not a.nvim_buf_is_loaded(b) then
+ return true
+ end
+
+ pg = assert(Playground:new(buf, opts.lang))
+ pg:draw(b)
+ end,
+ })
+
+ a.nvim_create_autocmd('BufLeave', {
+ group = group,
+ buffer = b,
+ callback = function()
+ a.nvim_buf_clear_namespace(buf, pg.ns, 0, -1)
+ end,
+ })
+
+ a.nvim_create_autocmd('BufLeave', {
+ group = group,
+ buffer = buf,
+ callback = function()
+ if not a.nvim_buf_is_loaded(b) then
+ return true
+ end
+
+ a.nvim_buf_clear_namespace(b, pg.ns, 0, -1)
+ end,
+ })
+
+ a.nvim_create_autocmd('BufHidden', {
+ group = group,
+ buffer = buf,
+ once = true,
+ callback = function()
+ if a.nvim_win_is_valid(w) then
+ a.nvim_win_close(w, true)
+ end
+ end,
+ })
+end
+
return M
diff --git a/runtime/lua/vim/treesitter/health.lua b/runtime/lua/vim/treesitter/health.lua
index 3bd59ca282..c0a1eca0ce 100644
--- a/runtime/lua/vim/treesitter/health.lua
+++ b/runtime/lua/vim/treesitter/health.lua
@@ -1,36 +1,33 @@
local M = {}
local ts = vim.treesitter
+local health = require('vim.health')
--- Lists the parsers currently installed
---
----@return A list of parsers
+---@return string[] list of parser files
function M.list_parsers()
return vim.api.nvim_get_runtime_file('parser/*', true)
end
--- Performs a healthcheck for treesitter integration
function M.check()
- local report_info = vim.fn['health#report_info']
- local report_ok = vim.fn['health#report_ok']
- local report_error = vim.fn['health#report_error']
local parsers = M.list_parsers()
- report_info(string.format('Runtime ABI version : %d', ts.language_version))
+ health.report_info(string.format('Nvim runtime ABI version: %d', ts.language_version))
for _, parser in pairs(parsers) do
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))
+ if not is_loadable or not ret then
+ health.report_error(
+ string.format('Parser "%s" failed to load (path: %s): %s', parsername, parser, ret or '?')
+ )
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)
+ health.report_ok(
+ string.format('Parser: %-10s ABI: %d, path: %s', parsername, lang._abi_version, parser)
)
- else
- report_error(string.format('Unable to load parser for %s', parsername))
end
end
end
diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua
index e27a5fa9c3..d77a0d0d03 100644
--- a/runtime/lua/vim/treesitter/highlighter.lua
+++ b/runtime/lua/vim/treesitter/highlighter.lua
@@ -2,6 +2,7 @@ local a = vim.api
local query = require('vim.treesitter.query')
-- support reload for quick experimentation
+---@class TSHighlighter
local TSHighlighter = rawget(vim.treesitter, 'TSHighlighter') or {}
TSHighlighter.__index = TSHighlighter
@@ -12,105 +13,18 @@ TSHighlighterQuery.__index = TSHighlighterQuery
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
- a.nvim_set_hl(0, from, { link = to, default = true })
- end
-
- return from
-end
-
--- 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)
- return firstc ~= string.lower(firstc)
-end
-
---@private
function TSHighlighterQuery.new(lang, query_string)
local self = setmetatable({}, { __index = TSHighlighterQuery })
self.hl_cache = setmetatable({}, {
__index = function(table, capture)
- local hl, is_vim_highlight = self:_get_hl_from_capture(capture)
- if not is_vim_highlight then
- hl = _link_default_highlight_once(lang .. hl, hl)
+ local name = self._query.captures[capture]
+ local id = 0
+ if not vim.startswith(name, '_') then
+ id = a.nvim_get_hl_id_by_name('@' .. name .. '.' .. lang)
end
- local id = a.nvim_get_hl_id_by_name(hl)
-
rawset(table, capture, id)
return id
end,
@@ -130,25 +44,12 @@ function TSHighlighterQuery:query()
return self._query
end
----@private
---- Get the hl from capture.
---- Returns a tuple { highlight_name: string, is_builtin: bool }
-function TSHighlighterQuery:_get_hl_from_capture(capture)
- local name = self._query.captures[capture]
-
- if is_highlight_name(name) then
- -- From "Normal.left" only keep "Normal"
- return vim.split(name, '.', true)[1], true
- else
- return TSHighlighter.hl_map[name] or 0, false
- end
-end
-
--- Creates a new highlighter using @param tree
---
----@param tree The language tree to use for highlighting
----@param opts Table used to configure the highlighter
---- - queries: Table to overwrite queries used by the highlighter
+---@param tree LanguageTree |LanguageTree| parser object to use for highlighting
+---@param opts (table|nil) Configuration of the highlighter:
+--- - queries table overwrite queries used by the highlighter
+---@return TSHighlighter Created highlighter object
function TSHighlighter.new(tree, opts)
local self = setmetatable({}, TSHighlighter)
@@ -187,7 +88,10 @@ function TSHighlighter.new(tree, opts)
end
end
- a.nvim_buf_set_option(self.bufnr, 'syntax', '')
+ self.orig_spelloptions = vim.bo[self.bufnr].spelloptions
+
+ vim.bo[self.bufnr].syntax = ''
+ vim.b[self.bufnr].ts_highlight = true
TSHighlighter.active[self.bufnr] = self
@@ -196,9 +100,13 @@ 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.cmd.runtime({ 'syntax/synload.vim', bang = true })
end
+ a.nvim_buf_call(self.bufnr, function()
+ vim.opt_local.spelloptions:append('noplainbuffer')
+ end)
+
self.tree:parse()
return self
@@ -209,6 +117,14 @@ function TSHighlighter:destroy()
if TSHighlighter.active[self.bufnr] then
TSHighlighter.active[self.bufnr] = nil
end
+
+ if vim.api.nvim_buf_is_loaded(self.bufnr) then
+ vim.bo[self.bufnr].spelloptions = self.orig_spelloptions
+ vim.b[self.bufnr].ts_highlight = nil
+ if vim.g.syntax_on == 1 then
+ a.nvim_exec_autocmds('FileType', { group = 'syntaxset', buffer = self.bufnr })
+ end
+ end
end
---@private
@@ -246,8 +162,10 @@ function TSHighlighter:on_changedtree(changes)
end
--- Gets the query used for @param lang
----
----@param lang A language used by the highlighter.
+--
+---@private
+---@param lang string Language used by the highlighter.
+---@return Query
function TSHighlighter:get_query(lang)
if not self._queries[lang] then
self._queries[lang] = TSHighlighterQuery.new(lang)
@@ -257,7 +175,7 @@ function TSHighlighter:get_query(lang)
end
---@private
-local function on_line_impl(self, buf, line)
+local function on_line_impl(self, buf, line, is_spell_nav)
self.tree:for_each_tree(function(tstree, tree)
if not tstree then
return
@@ -294,14 +212,26 @@ local function on_line_impl(self, buf, line)
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
+ local capture_name = highlighter_query:query().captures[capture]
+ local spell = nil
+ if capture_name == 'spell' then
+ spell = true
+ elseif capture_name == 'nospell' then
+ spell = false
+ end
+
+ -- Give nospell a higher priority so it always overrides spell captures.
+ local spell_pri_offset = capture_name == 'nospell' and 1 or 0
+
+ if hl and end_row >= line and (not is_spell_nav or spell ~= nil) 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
+ priority = (tonumber(metadata.priority) or 100) + spell_pri_offset, -- Low but leaves room below
conceal = metadata.conceal,
+ spell = spell,
})
end
if start_row > line then
@@ -318,7 +248,21 @@ function TSHighlighter._on_line(_, _win, buf, line, _)
return
end
- on_line_impl(self, buf, line)
+ on_line_impl(self, buf, line, false)
+end
+
+---@private
+function TSHighlighter._on_spell_nav(_, _, buf, srow, _, erow, _)
+ local self = TSHighlighter.active[buf]
+ if not self then
+ return
+ end
+
+ self:reset_highlight_state()
+
+ for row = srow, erow do
+ on_line_impl(self, buf, row, true)
+ end
end
---@private
@@ -345,6 +289,7 @@ a.nvim_set_decoration_provider(ns, {
on_buf = TSHighlighter._on_buf,
on_win = TSHighlighter._on_win,
on_line = TSHighlighter._on_line,
+ _on_spell_nav = TSHighlighter._on_spell_nav,
})
return TSHighlighter
diff --git a/runtime/lua/vim/treesitter/language.lua b/runtime/lua/vim/treesitter/language.lua
index dfb6f5be84..c92d63b8c4 100644
--- a/runtime/lua/vim/treesitter/language.lua
+++ b/runtime/lua/vim/treesitter/language.lua
@@ -2,14 +2,16 @@ local a = vim.api
local M = {}
---- Asserts that the provided language is installed, and optionally provide a path for the parser
+--- Asserts that a parser for the language {lang} is installed.
---
---- Parsers are searched in the `parser` runtime directory.
+--- Parsers are searched in the `parser` runtime directory, or the provided {path}
---
----@param lang The language the parser should parse
----@param path Optional path the parser is located at
----@param silent Don't throw an error if language not found
-function M.require_language(lang, path, silent)
+---@param lang string Language the parser should parse
+---@param path (string|nil) Optional path the parser is located at
+---@param silent (boolean|nil) Don't throw an error if language not found
+---@param symbol_name (string|nil) Internal symbol name for the language to load
+---@return boolean If the specified language is installed
+function M.require_language(lang, path, silent, symbol_name)
if vim._ts_has_language(lang) then
return true
end
@@ -21,7 +23,6 @@ function M.require_language(lang, path, silent)
return false
end
- -- TODO(bfredl): help tag?
error("no parser for '" .. lang .. "' language, see :help treesitter-parsers")
end
path = paths[1]
@@ -29,10 +30,10 @@ function M.require_language(lang, path, silent)
if silent then
return pcall(function()
- vim._ts_add_language(path, lang)
+ vim._ts_add_language(path, lang, symbol_name)
end)
else
- vim._ts_add_language(path, lang)
+ vim._ts_add_language(path, lang, symbol_name)
end
return true
@@ -42,7 +43,8 @@ end
---
--- Inspecting provides some useful information on the language like node names, ...
---
----@param lang The language.
+---@param lang string Language
+---@return table
function M.inspect_language(lang)
M.require_language(lang)
return vim._ts_inspect_language(lang)
diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua
index 4d3b0631a2..a1e96f8ef2 100644
--- a/runtime/lua/vim/treesitter/languagetree.lua
+++ b/runtime/lua/vim/treesitter/languagetree.lua
@@ -2,19 +2,35 @@ local a = vim.api
local query = require('vim.treesitter.query')
local language = require('vim.treesitter.language')
+---@class LanguageTree
+---@field _callbacks function[] Callback handlers
+---@field _children LanguageTree[] Injected languages
+---@field _injection_query table Queries defining injected languages
+---@field _opts table Options
+---@field _parser userdata Parser for language
+---@field _regions table List of regions this tree should manage and parse
+---@field _lang string Language name
+---@field _regions table
+---@field _source (number|string) Buffer or string to parse
+---@field _trees userdata[] Reference to parsed |tstree| (one for each language)
+---@field _valid boolean If the parsed tree is valid
+
local LanguageTree = {}
LanguageTree.__index = LanguageTree
---- Represents a single treesitter parser for a language.
---- The language can contain child languages with in its range,
---- hence the tree.
+--- A |LanguageTree| holds the treesitter parser for a given language {lang} used
+--- to parse a buffer. As the buffer may contain injected languages, the LanguageTree
+--- needs to store parsers for these child languages as well (which in turn may contain
+--- child languages themselves, hence the name).
---
----@param source Can be a bufnr or a string of text to parse
----@param lang The language this tree represents
----@param opts Options table
----@param opts.injections A table of language to injection query strings.
---- This is useful for overriding the built-in runtime file
---- searching for the injection language query per language.
+---@param source (number|string) Buffer or a string of text to parse
+---@param lang string Root language this tree represents
+---@param opts (table|nil) Optional keyword arguments:
+--- - injections table Mapping language to injection query strings.
+--- This is useful for overriding the built-in
+--- runtime file searching for the injection language
+--- query per language.
+---@return LanguageTree |LanguageTree| parser object
function LanguageTree.new(source, lang, opts)
language.require_language(lang)
opts = opts or {}
@@ -94,6 +110,9 @@ end
--- for the language this tree represents.
--- This will run the injection query for this language to
--- determine if any child languages should be created.
+---
+---@return userdata[] Table of parsed |tstree|
+---@return table Change list
function LanguageTree:parse()
if self._valid then
return self._trees
@@ -167,10 +186,10 @@ function LanguageTree:parse()
return self._trees, changes
end
---- Invokes the callback for each LanguageTree and it's children recursively
+--- Invokes the callback for each |LanguageTree| and its children recursively
---
----@param fn The function to invoke. This is invoked with arguments (tree: LanguageTree, lang: string)
----@param include_self Whether to include the invoking tree in the results.
+---@param fn function(tree: LanguageTree, lang: string)
+---@param include_self boolean Whether to include the invoking tree in the results
function LanguageTree:for_each_child(fn, include_self)
if include_self then
fn(self, self._lang)
@@ -181,12 +200,11 @@ function LanguageTree:for_each_child(fn, include_self)
end
end
---- Invokes the callback for each treesitter trees recursively.
+--- Invokes the callback for each |LanguageTree| recursively.
---
---- Note, this includes the invoking language tree's trees as well.
+--- Note: This includes the invoking tree's child trees as well.
---
----@param fn The callback to invoke. The callback is invoked with arguments
---- (tree: TSTree, languageTree: LanguageTree)
+---@param fn function(tree: TSTree, languageTree: LanguageTree)
function LanguageTree:for_each_tree(fn)
for _, tree in ipairs(self._trees) do
fn(tree, self)
@@ -197,11 +215,13 @@ function LanguageTree:for_each_tree(fn)
end
end
---- Adds a child language to this tree.
+--- Adds a child language to this |LanguageTree|.
---
--- If the language already exists as a child, it will first be removed.
---
----@param lang The language to add.
+---@private
+---@param lang string Language to add.
+---@return LanguageTree Injected |LanguageTree|
function LanguageTree:add_child(lang)
if self._children[lang] then
self:remove_child(lang)
@@ -215,9 +235,10 @@ function LanguageTree:add_child(lang)
return self._children[lang]
end
---- Removes a child language from this tree.
+--- Removes a child language from this |LanguageTree|.
---
----@param lang The language to remove.
+---@private
+---@param lang string Language to remove.
function LanguageTree:remove_child(lang)
local child = self._children[lang]
@@ -229,12 +250,11 @@ function LanguageTree:remove_child(lang)
end
end
---- Destroys this language tree and all its children.
+--- Destroys this |LanguageTree| and all its children.
---
--- Any cleanup logic should be performed here.
---
---- Note:
---- This DOES NOT remove this tree from a parent. Instead,
+--- 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
@@ -243,23 +263,24 @@ function LanguageTree:destroy()
end
end
---- Sets the included regions that should be parsed by this parser.
+--- Sets the included regions that should be parsed by this |LanguageTree|.
--- A region is a set of nodes and/or ranges that will be parsed in the same context.
---
---- For example, `{ { node1 }, { node2} }` is two separate regions.
---- This will be parsed by the parser in two different contexts... thus resulting
+--- For example, `{ { node1 }, { node2} }` contains two separate regions.
+--- They will be parsed by the parser in two different contexts, thus resulting
--- in two separate trees.
---
---- `{ { node1, node2 } }` is a single region consisting of two nodes.
---- This will be parsed by the parser in a single context... thus resulting
+--- On the other hand, `{ { node1, node2 } }` is a single region consisting of
+--- two nodes. This will be parsed by the parser in a single context, thus resulting
--- in a single tree.
---
--- This allows for embedded languages to be parsed together across different
--- nodes, which is useful for templating languages like ERB and EJS.
---
---- Note, this call invalidates the tree and requires it to be parsed again.
+--- Note: This call invalidates the tree and requires it to be parsed again.
---
----@param regions (table) list of regions this tree should manage and parse.
+---@private
+---@param regions table List of regions this tree should manage and parse.
function LanguageTree:set_included_regions(regions)
-- Transform the tables from 4 element long to 6 element long (with byte offset)
for _, region in ipairs(regions) do
@@ -288,7 +309,7 @@ function LanguageTree:set_included_regions(regions)
-- Trees are no longer valid now that we have changed regions.
-- TODO(vigoux,steelsojka): Look into doing this smarter so we can use some of the
-- old trees for incremental parsing. Currently, this only
- -- effects injected languages.
+ -- affects injected languages.
self._trees = {}
self:invalidate()
end
@@ -299,7 +320,7 @@ function LanguageTree:included_regions()
end
---@private
-local function get_node_range(node, id, metadata)
+local function get_range_from_metadata(node, id, metadata)
if metadata[id] and metadata[id].range then
return metadata[id].range
end
@@ -362,7 +383,7 @@ function LanguageTree:_get_injections()
elseif name == 'combined' then
combined = true
elseif name == 'content' and #ranges == 0 then
- table.insert(ranges, get_node_range(node, id, metadata))
+ table.insert(ranges, get_range_from_metadata(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
@@ -371,7 +392,7 @@ function LanguageTree:_get_injections()
end
if #ranges == 0 then
- table.insert(ranges, get_node_range(node, id, metadata))
+ table.insert(ranges, get_range_from_metadata(node, id, metadata))
end
end
end
@@ -493,8 +514,8 @@ function LanguageTree:_on_detach(...)
self:_do_callback('detach', ...)
end
---- Registers callbacks for the parser.
----@param cbs table An |nvim_buf_attach()|-like table argument with the following keys :
+--- Registers callbacks for the |LanguageTree|.
+---@param cbs table An |nvim_buf_attach()|-like table argument with the following handlers:
--- - `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
@@ -536,9 +557,10 @@ local function tree_contains(tree, range)
return start_fits and end_fits
end
---- Determines whether {range} is contained in this language tree
+--- Determines whether {range} is contained in the |LanguageTree|.
---
----@param range A range, that is a `{ start_line, start_col, end_line, end_col }` table.
+---@param range table `{ start_line, start_col, end_line, end_col }`
+---@return boolean
function LanguageTree:contains(range)
for _, tree in pairs(self._trees) do
if tree_contains(tree, range) then
@@ -549,9 +571,52 @@ function LanguageTree:contains(range)
return false
end
---- Gets the appropriate language that contains {range}
+--- Gets the tree that contains {range}.
+---
+---@param range table `{ start_line, start_col, end_line, end_col }`
+---@param opts table|nil Optional keyword arguments:
+--- - ignore_injections boolean Ignore injected languages (default true)
+---@return userdata|nil Contained |tstree|
+function LanguageTree:tree_for_range(range, opts)
+ opts = opts or {}
+ local ignore = vim.F.if_nil(opts.ignore_injections, true)
+
+ if not ignore then
+ for _, child in pairs(self._children) do
+ for _, tree in pairs(child:trees()) do
+ if tree_contains(tree, range) then
+ return tree
+ end
+ end
+ end
+ end
+
+ for _, tree in pairs(self._trees) do
+ if tree_contains(tree, range) then
+ return tree
+ end
+ end
+
+ return nil
+end
+
+--- Gets the smallest named node that contains {range}.
+---
+---@param range table `{ start_line, start_col, end_line, end_col }`
+---@param opts table|nil Optional keyword arguments:
+--- - ignore_injections boolean Ignore injected languages (default true)
+---@return userdata|nil Found |tsnode|
+function LanguageTree:named_node_for_range(range, opts)
+ local tree = self:tree_for_range(range, opts)
+ if tree then
+ return tree:root():named_descendant_for_range(unpack(range))
+ end
+end
+
+--- Gets the appropriate language that contains {range}.
---
----@param range A text range, see |LanguageTree:contains|
+---@param range table `{ start_line, start_col, end_line, end_col }`
+---@return LanguageTree Managing {range}
function LanguageTree:language_for_range(range)
for _, child in pairs(self._children) do
if child:contains(range) then
diff --git a/runtime/lua/vim/treesitter/playground.lua b/runtime/lua/vim/treesitter/playground.lua
new file mode 100644
index 0000000000..bb073290c6
--- /dev/null
+++ b/runtime/lua/vim/treesitter/playground.lua
@@ -0,0 +1,186 @@
+local api = vim.api
+
+local M = {}
+
+---@class Playground
+---@field ns number API namespace
+---@field opts table Options table with the following keys:
+--- - anon (boolean): If true, display anonymous nodes
+--- - lang (boolean): If true, display the language alongside each node
+---
+---@class Node
+---@field id number Node id
+---@field text string Node text
+---@field named boolean True if this is a named (non-anonymous) node
+---@field depth number Depth of the node within the tree
+---@field lnum number Beginning line number of this node in the source buffer
+---@field col number Beginning column number of this node in the source buffer
+---@field end_lnum number Final line number of this node in the source buffer
+---@field end_col number Final column number of this node in the source buffer
+---@field lang string Source language of this node
+
+--- Traverse all child nodes starting at {node}.
+---
+--- This is a recursive function. The {depth} parameter indicates the current recursion level.
+--- {lang} is a string indicating the language of the tree currently being traversed. Each traversed
+--- node is added to {tree}. When recursion completes, {tree} is an array of all nodes in the order
+--- they were visited.
+---
+--- {injections} is a table mapping node ids from the primary tree to language tree injections. Each
+--- injected language has a series of trees nested within the primary language's tree, and the root
+--- node of each of these trees is contained within a node in the primary tree. The {injections}
+--- table maps nodes in the primary tree to root nodes of injected trees.
+---
+---@param node userdata Starting node to begin traversal |tsnode|
+---@param depth number Current recursion depth
+---@param lang string Language of the tree currently being traversed
+---@param injections table Mapping of node ids to root nodes of injected language trees (see
+--- explanation above)
+---@param tree Node[] Output table containing a list of tables each representing a node in the tree
+---@private
+local function traverse(node, depth, lang, injections, tree)
+ local injection = injections[node:id()]
+ if injection then
+ traverse(injection.root, depth, injection.lang, injections, tree)
+ end
+
+ for child, field in node:iter_children() do
+ local type = child:type()
+ local lnum, col, end_lnum, end_col = child:range()
+ local named = child:named()
+ local text
+ if named then
+ if field then
+ text = string.format('%s: (%s)', field, type)
+ else
+ text = string.format('(%s)', type)
+ end
+ else
+ text = string.format('"%s"', type:gsub('\n', '\\n'))
+ end
+
+ table.insert(tree, {
+ id = child:id(),
+ text = text,
+ named = named,
+ depth = depth,
+ lnum = lnum,
+ col = col,
+ end_lnum = end_lnum,
+ end_col = end_col,
+ lang = lang,
+ })
+
+ traverse(child, depth + 1, lang, injections, tree)
+ end
+
+ return tree
+end
+
+--- Create a new Playground object.
+---
+---@param bufnr number Source buffer number
+---@param lang string|nil Language of source buffer
+---
+---@return Playground|nil
+---@return string|nil Error message, if any
+---
+---@private
+function M.new(self, bufnr, lang)
+ local ok, parser = pcall(vim.treesitter.get_parser, bufnr or 0, lang)
+ if not ok then
+ return nil, 'No parser available for the given buffer'
+ end
+
+ -- For each child tree (injected language), find the root of the tree and locate the node within
+ -- the primary tree that contains that root. Add a mapping from the node in the primary tree to
+ -- the root in the child tree to the {injections} table.
+ local root = parser:parse()[1]:root()
+ local injections = {}
+ parser:for_each_child(function(child, lang_)
+ child:for_each_tree(function(tree)
+ local r = tree:root()
+ local node = root:named_descendant_for_range(r:range())
+ if node then
+ injections[node:id()] = {
+ lang = lang_,
+ root = r,
+ }
+ end
+ end)
+ end)
+
+ local nodes = traverse(root, 0, parser:lang(), injections, {})
+
+ local named = {}
+ for _, v in ipairs(nodes) do
+ if v.named then
+ named[#named + 1] = v
+ end
+ end
+
+ local t = {
+ ns = api.nvim_create_namespace(''),
+ nodes = nodes,
+ named = named,
+ opts = {
+ anon = false,
+ lang = false,
+ },
+ }
+
+ setmetatable(t, self)
+ self.__index = self
+ return t
+end
+
+--- Write the contents of this Playground into {bufnr}.
+---
+---@param bufnr number Buffer number to write into.
+---@private
+function M.draw(self, bufnr)
+ vim.bo[bufnr].modifiable = true
+ local lines = {}
+ for _, item in self:iter() do
+ lines[#lines + 1] = table.concat({
+ string.rep(' ', item.depth),
+ item.text,
+ item.lnum == item.end_lnum
+ and string.format(' [%d:%d-%d]', item.lnum + 1, item.col + 1, item.end_col)
+ or string.format(
+ ' [%d:%d-%d:%d]',
+ item.lnum + 1,
+ item.col + 1,
+ item.end_lnum + 1,
+ item.end_col
+ ),
+ self.opts.lang and string.format(' %s', item.lang) or '',
+ })
+ end
+ api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
+ vim.bo[bufnr].modifiable = false
+end
+
+--- Get node {i} from this Playground object.
+---
+--- The node number is dependent on whether or not anonymous nodes are displayed.
+---
+---@param i number Node number to get
+---@return Node
+---@private
+function M.get(self, i)
+ local t = self.opts.anon and self.nodes or self.named
+ return t[i]
+end
+
+--- Iterate over all of the nodes in this Playground object.
+---
+---@return function Iterator over all nodes in this Playground
+---@return table
+---@return number
+---@private
+function M.iter(self)
+ return ipairs(self.opts.anon and self.nodes or self.named)
+end
+
+return M
diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua
index 103e85abfd..dbf134573d 100644
--- a/runtime/lua/vim/treesitter/query.lua
+++ b/runtime/lua/vim/treesitter/query.lua
@@ -3,6 +3,11 @@ local language = require('vim.treesitter.language')
-- query: pattern matching on trees
-- predicate matching is implemented in lua
+--
+---@class Query
+---@field captures string[] List of captures used in query
+---@field info table Contains used queries, predicates, directives
+---@field query userdata Parsed query
local Query = {}
Query.__index = Query
@@ -34,11 +39,24 @@ local function safe_read(filename, read_quantifier)
return content
end
+---@private
+--- Adds {ilang} to {base_langs}, only if {ilang} is different than {lang}
+---
+---@return boolean true If lang == ilang
+local function add_included_lang(base_langs, lang, ilang)
+ if lang == ilang then
+ return true
+ end
+ table.insert(base_langs, ilang)
+ return false
+end
+
--- Gets the list of files used to make up a query
---
----@param lang The language
----@param query_name The name of the query to load
----@param is_included Internal parameter, most of the time left as `nil`
+---@param lang string Language to get query for
+---@param query_name string Name of the query to load (e.g., "highlights")
+---@param is_included (boolean|nil) Internal parameter, most of the time left as `nil`
+---@return string[] query_files List of files to load for given query and language
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))
@@ -47,6 +65,9 @@ function M.get_query_files(lang, query_name, is_included)
return {}
end
+ local base_query = nil
+ local extensions = {}
+
local base_langs = {}
-- Now get the base languages by looking at the first line of every file
@@ -55,27 +76,53 @@ function M.get_query_files(lang, query_name, is_included)
--
-- {language} ::= {lang} | ({lang})
local MODELINE_FORMAT = '^;+%s*inherits%s*:?%s*([a-z_,()]+)%s*$'
+ local EXTENDS_FORMAT = '^;+%s*extends%s*$'
- for _, file in ipairs(lang_files) do
- local modeline = safe_read(file, '*l')
+ for _, filename in ipairs(lang_files) do
+ local file, err = io.open(filename, 'r')
+ if not file then
+ error(err)
+ end
- if modeline then
- local langlist = modeline:match(MODELINE_FORMAT)
+ local extension = false
+ for modeline in
+ function()
+ return file:read('*l')
+ end
+ do
+ if not vim.startswith(modeline, ';') then
+ break
+ end
+
+ local langlist = modeline:match(MODELINE_FORMAT)
if langlist then
for _, incllang in ipairs(vim.split(langlist, ',', true)) do
local is_optional = incllang:match('%(.*%)')
if is_optional then
if not is_included then
- table.insert(base_langs, incllang:sub(2, #incllang - 1))
+ if add_included_lang(base_langs, lang, incllang:sub(2, #incllang - 1)) then
+ extension = true
+ end
end
else
- table.insert(base_langs, incllang)
+ if add_included_lang(base_langs, lang, incllang) then
+ extension = true
+ end
end
end
+ elseif modeline:match(EXTENDS_FORMAT) then
+ extension = true
end
end
+
+ if extension then
+ table.insert(extensions, filename)
+ elseif base_query == nil then
+ base_query = filename
+ end
+ io.close(file)
end
local query_files = {}
@@ -83,7 +130,8 @@ function M.get_query_files(lang, query_name, is_included)
local base_files = M.get_query_files(base_lang, query_name, true)
vim.list_extend(query_files, base_files)
end
- vim.list_extend(query_files, lang_files)
+ vim.list_extend(query_files, { base_query })
+ vim.list_extend(query_files, extensions)
return query_files
end
@@ -109,24 +157,24 @@ local explicit_queries = setmetatable({}, {
end,
})
---- Sets the runtime query {query_name} for {lang}
+--- Sets the runtime query named {query_name} for {lang}
---
--- This allows users to override any runtime files and/or configuration
--- set by plugins.
---
----@param lang string: The language to use for the query
----@param query_name string: The name of the query (i.e. "highlights")
----@param text string: The query text (unparsed).
+---@param lang string Language to use for the query
+---@param query_name string Name of the query (e.g., "highlights")
+---@param text string Query text (unparsed).
function M.set_query(lang, query_name, text)
explicit_queries[lang][query_name] = M.parse_query(lang, text)
end
--- Returns the runtime query {query_name} for {lang}.
---
----@param lang The language to use for the query
----@param query_name The name of the query (i.e. "highlights")
+---@param lang string Language to use for the query
+---@param query_name string Name of the query (e.g. "highlights")
---
----@return The corresponding query, parsed.
+---@return Query Parsed query
function M.get_query(lang, query_name)
if explicit_queries[lang][query_name] then
return explicit_queries[lang][query_name]
@@ -140,12 +188,9 @@ 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,
-})
+local query_cache = vim.defaulttable(function()
+ return setmetatable({}, { __mode = 'v' })
+end)
--- Parse {query} as a string. (If the query is in a file, the caller
--- should read the contents into a string before calling).
@@ -160,10 +205,10 @@ local query_cache = setmetatable({}, {
--- -` info.captures` also points to `captures`.
--- - `info.patterns` contains information about predicates.
---
----@param lang string The language
----@param query string A string containing the query (s-expr syntax)
+---@param lang string Language to use for the query
+---@param query string Query in s-expr syntax
---
----@returns The query
+---@return Query Parsed query
function M.parse_query(lang, query)
language.require_language(lang)
local cached = query_cache[lang][query]
@@ -181,9 +226,15 @@ end
--- Gets the text corresponding to a given node
---
----@param node the node
----@param source The buffer or string from which the node is extracted
-function M.get_node_text(node, source)
+---@param node userdata |tsnode|
+---@param source (number|string) Buffer or string from which the {node} is extracted
+---@param opts (table|nil) Optional parameters.
+--- - concat: (boolean) Concatenate result in a string (default true)
+---@return (string[]|string)
+function M.get_node_text(node, source, opts)
+ opts = opts or {}
+ local concat = vim.F.if_nil(opts.concat, true)
+
local start_row, start_col, start_byte = node:start()
local end_row, end_col, end_byte = node:end_()
@@ -210,7 +261,7 @@ function M.get_node_text(node, source)
end
end
- return table.concat(lines, '\n')
+ return concat and table.concat(lines, '\n') or lines
elseif type(source) == 'string' then
return source:sub(start_byte + 1, end_byte)
end
@@ -367,9 +418,9 @@ local directive_handlers = {
--- Adds a new predicate to be used in queries
---
----@param name the name of the predicate, without leading #
----@param handler the handler function to be used
---- signature will be (match, pattern, bufnr, predicate)
+---@param name string Name of the predicate, without leading #
+---@param handler function(match:table, pattern:string, bufnr:number, predicate:string[])
+--- - see |vim.treesitter.query.add_directive()| for argument meanings
function M.add_predicate(name, handler, force)
if predicate_handlers[name] and not force then
error(string.format('Overriding %s', name))
@@ -385,9 +436,13 @@ end
--- 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, metadata)
+---@param name string Name of the directive, without leading #
+---@param handler function(match:table, pattern:string, bufnr:number, predicate:string[], metadata:table)
+--- - match: see |treesitter-query|
+--- - node-level data are accessible via `match[capture_id]`
+--- - pattern: see |treesitter-query|
+--- - predicate: list of strings containing the full directive being called, e.g.
+--- `(node (#set! conceal "-"))` would get the predicate `{ "#set!", "conceal", "-" }`
function M.add_directive(name, handler, force)
if directive_handlers[name] and not force then
error(string.format('Overriding %s', name))
@@ -397,12 +452,13 @@ function M.add_directive(name, handler, force)
end
--- Lists the currently available directives to use in queries.
----@return The list of supported directives.
+---@return string[] List of supported directives.
function M.list_directives()
return vim.tbl_keys(directive_handlers)
end
----@return The list of supported predicates.
+--- Lists the currently available predicates to use in queries.
+---@return string[] List of supported predicates.
function M.list_predicates()
return vim.tbl_keys(predicate_handlers)
end
@@ -489,18 +545,17 @@ end
--- Iterate over all captures from all matches inside {node}
---
---- {source} is needed if the query contains predicates, then the caller
+--- {source} is needed if the query contains predicates; then the caller
--- must ensure to use a freshly parsed tree consistent with the current
--- text of the buffer (if relevant). {start_row} and {end_row} can be used to limit
--- matches inside a row range (this is typically used with root node
---- as the node, i e to get syntax highlight matches in the current
---- viewport). When omitted the start and end row values are used from the given node.
+--- as the {node}, i.e., to get syntax highlight matches in the current
+--- viewport). When omitted, the {start} and {end} row values are used from the given node.
---
---- The iterator returns three values, a numeric id identifying the capture,
+--- The iterator returns three values: a numeric id identifying the capture,
--- the captured node, and metadata from any directives processing the match.
--- The following example shows how to get captures by name:
----
---- <pre>
+--- <pre>lua
--- for id, node, metadata in query:iter_captures(tree:root(), bufnr, first, last) do
--- local name = query.captures[id] -- name of the capture in the query
--- -- typically useful info about the node:
@@ -510,13 +565,14 @@ end
--- end
--- </pre>
---
----@param node The node under which the search will occur
----@param source The source buffer or string to extract text from
----@param start The starting line of the search
----@param stop The stopping line of the search (end-exclusive)
+---@param node userdata |tsnode| under which the search will occur
+---@param source (number|string) Source buffer or string to extract text from
+---@param start number Starting line for the search
+---@param stop number Stopping line for the search (end-exclusive)
---
----@returns The matching capture id
----@returns The captured node
+---@return number capture Matching capture id
+---@return table capture_node Capture for {node}
+---@return table metadata for the {capture}
function Query:iter_captures(node, source, start, stop)
if type(source) == 'number' and source == 0 then
source = vim.api.nvim_get_current_buf()
@@ -546,15 +602,14 @@ end
--- Iterates the matches of self on a given range.
---
---- Iterate over all matches within a node. The arguments are the same as
---- for |query:iter_captures()| but the iterated values are different:
+--- Iterate over all matches within a {node}. The arguments are the same as
+--- for |Query:iter_captures()| but the iterated values are different:
--- an (1-based) index of the pattern in the query, 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,
+--- 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:
----
---- <pre>
+--- Here is an example iterating over all captures in every match:
+--- <pre>lua
--- for pattern, match, metadata in cquery:iter_matches(tree:root(), bufnr, first, last) do
--- for id, node in pairs(match) do
--- local name = query.captures[id]
@@ -567,13 +622,14 @@ end
--- end
--- </pre>
---
----@param node The node under which the search will occur
----@param source The source buffer or string to search
----@param start The starting line of the search
----@param stop The stopping line of the search (end-exclusive)
+---@param node userdata |tsnode| under which the search will occur
+---@param source (number|string) Source buffer or string to search
+---@param start number Starting line for the search
+---@param stop number Stopping line for the search (end-exclusive)
---
----@returns The matching pattern id
----@returns The matching match
+---@return number pattern id
+---@return table match
+---@return table metadata
function Query:iter_matches(node, source, start, stop)
if type(source) == 'number' and source == 0 then
source = vim.api.nvim_get_current_buf()
diff --git a/runtime/lua/vim/ui.lua b/runtime/lua/vim/ui.lua
index 6f1ce3089d..8f5be15221 100644
--- a/runtime/lua/vim/ui.lua
+++ b/runtime/lua/vim/ui.lua
@@ -21,7 +21,7 @@ local M = {}
---
---
--- Example:
---- <pre>
+--- <pre>lua
--- vim.ui.select({ 'tabs', 'spaces' }, {
--- prompt = 'Select tabs or spaces:',
--- format_item = function(item)
@@ -73,11 +73,12 @@ end
--- user inputs.
---@param on_confirm function ((input|nil) -> ())
--- Called once the user confirms or abort the input.
---- `input` is what the user typed.
+--- `input` is what the user typed (it might be
+--- an empty string if nothing was entered), or
--- `nil` if the user aborted the dialog.
---
--- Example:
---- <pre>
+--- <pre>lua
--- vim.ui.input({ prompt = 'Enter value for shiftwidth: ' }, function(input)
--- vim.o.shiftwidth = tonumber(input)
--- end)
@@ -88,11 +89,17 @@ function M.input(opts, on_confirm)
})
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)
- else
+
+ -- Note that vim.fn.input({}) returns an empty string when cancelled.
+ -- vim.ui.input() should distinguish aborting from entering an empty string.
+ local _canceled = vim.NIL
+ opts = vim.tbl_extend('keep', opts, { cancelreturn = _canceled })
+
+ local ok, input = pcall(vim.fn.input, opts)
+ if not ok or input == _canceled then
on_confirm(nil)
+ else
+ on_confirm(input)
end
end
diff --git a/runtime/menu.vim b/runtime/menu.vim
index 0a5ac36095..2671bb51cb 100644
--- a/runtime/menu.vim
+++ b/runtime/menu.vim
@@ -2,7 +2,7 @@
" You can also use this as a start for your own set of menus.
"
" Maintainer: Bram Moolenaar <Bram@vim.org>
-" Last Change: 2020 Mar 29
+" Last Change: 2022 Nov 27
" Note that ":an" (short for ":anoremenu") is often used to make a menu work
" in all modes and avoid side effects from mappings defined by the user.
@@ -89,6 +89,21 @@ an 9999.75 &Help.-sep2- <Nop>
an 9999.80 &Help.&Version :version<CR>
an 9999.90 &Help.&About :intro<CR>
+if exists(':tlmenu')
+ tlnoremenu 9999.10 &Help.&Overview<Tab><F1> <C-W>:help<CR>
+ tlnoremenu 9999.20 &Help.&User\ Manual <C-W>:help usr_toc<CR>
+ tlnoremenu 9999.30 &Help.&How-To\ Links <C-W>:help how-to<CR>
+ tlnoremenu <silent> 9999.40 &Help.&Find\.\.\. <C-W>:call <SID>Helpfind()<CR>
+ tlnoremenu 9999.45 &Help.-sep1- <Nop>
+ tlnoremenu 9999.50 &Help.&Credits <C-W>:help credits<CR>
+ tlnoremenu 9999.60 &Help.Co&pying <C-W>:help copying<CR>
+ tlnoremenu 9999.70 &Help.&Sponsor/Register <C-W>:help sponsor<CR>
+ tlnoremenu 9999.70 &Help.O&rphans <C-W>:help kcc<CR>
+ tlnoremenu 9999.75 &Help.-sep2- <Nop>
+ tlnoremenu 9999.80 &Help.&Version <C-W>:version<CR>
+ tlnoremenu 9999.90 &Help.&About <C-W>:intro<CR>
+endif
+
fun! s:Helpfind()
if !exists("g:menutrans_help_dialog")
let g:menutrans_help_dialog = "Enter a command or word to find help on:\n\nPrepend i_ for Input mode commands (e.g.: i_CTRL-X)\nPrepend c_ for command-line editing commands (e.g.: c_<Del>)\nPrepend ' for an option name (e.g.: 'shiftwidth')"
@@ -124,12 +139,7 @@ if has("diff")
an 10.420 &File.Split\ Patched\ &By\.\.\. :browse vert diffpatch<CR>
endif
-if has("printer")
- an 10.500 &File.-SEP3- <Nop>
- an 10.510 &File.&Print :hardcopy<CR>
- vunmenu &File.&Print
- vnoremenu &File.&Print :hardcopy<CR>
-elseif has("unix")
+if has("unix")
an 10.500 &File.-SEP3- <Nop>
an 10.510 &File.&Print :w !lpr<CR>
vunmenu &File.&Print
@@ -702,6 +712,11 @@ func s:BMCanAdd(name, num)
return 0
endif
+ " no name with control characters
+ if a:name =~ '[\x01-\x1f]'
+ return 0
+ endif
+
" no special buffer, such as terminal or popup
let buftype = getbufvar(a:num, '&buftype')
if buftype != '' && buftype != 'nofile' && buftype != 'nowrite'
@@ -1049,11 +1064,7 @@ if has("toolbar")
an <silent> 1.20 ToolBar.Save :if expand("%") == ""<Bar>browse confirm w<Bar>else<Bar>confirm w<Bar>endif<CR>
an 1.30 ToolBar.SaveAll :browse confirm wa<CR>
- if has("printer")
- an 1.40 ToolBar.Print :hardcopy<CR>
- vunmenu ToolBar.Print
- vnoremenu ToolBar.Print :hardcopy<CR>
- elseif has("unix")
+ if has("unix")
an 1.40 ToolBar.Print :w !lpr<CR>
vunmenu ToolBar.Print
vnoremenu ToolBar.Print :w !lpr<CR>
diff --git a/cmake.packaging/neovim.ico b/runtime/neovim.ico
index e0c151c966..e0c151c966 100644
--- a/cmake.packaging/neovim.ico
+++ b/runtime/neovim.ico
Binary files differ
diff --git a/runtime/nvim.appdata.xml b/runtime/nvim.appdata.xml
index 1464c27694..7411a7190a 100644
--- a/runtime/nvim.appdata.xml
+++ b/runtime/nvim.appdata.xml
@@ -26,6 +26,9 @@
</screenshots>
<releases>
+ <release date="2022-12-29" version="0.8.2"/>
+ <release date="2022-11-14" version="0.8.1"/>
+ <release date="2022-09-30" version="0.8.0"/>
<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"/>
diff --git a/runtime/optwin.vim b/runtime/optwin.vim
index 02cf573ea3..200254321e 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: 2022 Apr 07
+" Last Change: 2022 Dec 16
" If there already is an option window, jump to that one.
let buf = bufnr('option-window')
@@ -20,7 +20,7 @@ let s:cpo_save = &cpo
set cpo&vim
" function to be called when <CR> is hit in the option-window
-fun! <SID>CR()
+func <SID>CR()
" If on a continued comment line, go back to the first comment line
let lnum = search("^[^\t]", 'bWcn')
@@ -47,10 +47,10 @@ fun! <SID>CR()
elseif match(line, '^ \=[0-9]') >= 0
exe "norm! /" . line . "\<CR>zt"
endif
-endfun
+endfunc
" function to be called when <Space> is hit in the option-window
-fun! <SID>Space()
+func <SID>Space()
let lnum = line(".")
let line = getline(lnum)
@@ -67,16 +67,17 @@ fun! <SID>Space()
endif
endif
-endfun
+endfunc
-let s:local_to_window = '(local to window)'
-let s:local_to_buffer = '(local to buffer)'
-let s:global_or_local = '(global or local to buffer)'
+let s:local_to_window = gettext('(local to window)')
+let s:local_to_buffer = gettext('(local to buffer)')
+let s:global_or_local = gettext('(global or local to buffer)')
" find the window in which the option applies
" returns 0 for global option, 1 for local option, -1 for error
-fun! <SID>Find(lnum)
- if getline(a:lnum - 1) =~ "(local to"
+func <SID>Find(lnum)
+ let line = getline(a:lnum - 1)
+ if line =~ s:local_to_window || line =~ s:local_to_buffer
let local = 1
let thiswin = winnr()
wincmd p
@@ -95,10 +96,10 @@ fun! <SID>Find(lnum)
let local = -1
endif
return local
-endfun
+endfunc
" Update a "set" line in the option window
-fun! <SID>Update(lnum, line, local, thiswin)
+func <SID>Update(lnum, line, local, thiswin)
" get the new value of the option and update the option window line
if match(a:line, "=") >= 0
let name = substitute(a:line, '^ \tset \([^=]*\)=.*', '\1', "")
@@ -123,7 +124,7 @@ fun! <SID>Update(lnum, line, local, thiswin)
endif
endif
set nomodified
-endfun
+endfunc
" Reset 'title' and 'icon' to make it work faster.
" Reset 'undolevels' to avoid undo'ing until the buffer is empty.
@@ -149,13 +150,13 @@ exe $OPTWIN_CMD . ' new option-window'
setlocal ts=15 tw=0 noro buftype=nofile
" Insert help and a "set" command for each option.
-call append(0, '" Each "set" line shows the current value of an option (on the left).')
-call append(1, '" Hit <CR> on a "set" line to execute it.')
-call append(2, '" A boolean option will be toggled.')
-call append(3, '" For other options you can edit the value before hitting <CR>.')
-call append(4, '" Hit <CR> on a help line to open a help window on this option.')
-call append(5, '" Hit <CR> on an index line to jump there.')
-call append(6, '" Hit <Space> on a "set" line to refresh it.')
+call append(0, gettext('" Each "set" line shows the current value of an option (on the left).'))
+call append(1, gettext('" Hit <Enter> on a "set" line to execute it.'))
+call append(2, gettext('" A boolean option will be toggled.'))
+call append(3, gettext('" For other options you can edit the value before hitting <Enter>.'))
+call append(4, gettext('" Hit <Enter> on a help line to open a help window on this option.'))
+call append(5, gettext('" Hit <Enter> on an index line to jump there.'))
+call append(6, gettext('" Hit <Space> on a "set" line to refresh it.'))
" These functions are called often below. Keep them fast!
@@ -170,34 +171,34 @@ func <SID>AddOption(name, text)
endfunc
" Init a local binary option
-fun! <SID>BinOptionL(name)
+func <SID>BinOptionL(name)
let val = getwinvar(winnr('#'), '&' . a:name)
call append("$", substitute(substitute(" \tset " . val . a:name . "\t" .
\!val . a:name, "0", "no", ""), "1", "", ""))
-endfun
+endfunc
" Init a global binary option
-fun! <SID>BinOptionG(name, val)
+func <SID>BinOptionG(name, val)
call append("$", substitute(substitute(" \tset " . a:val . a:name . "\t" .
\!a:val . a:name, "0", "no", ""), "1", "", ""))
-endfun
+endfunc
" Init a local string option
-fun! <SID>OptionL(name)
+func <SID>OptionL(name)
let val = escape(getwinvar(winnr('#'), '&' . a:name), " \t\\\"|")
call append("$", " \tset " . a:name . "=" . val)
-endfun
+endfunc
" Init a global string option
-fun! <SID>OptionG(name, val)
+func <SID>OptionG(name, val)
call append("$", " \tset " . a:name . "=" . escape(a:val, " \t\\\"|"))
-endfun
+endfunc
let s:idx = 1
let s:lnum = line("$")
call append("$", "")
-fun! <SID>Header(text)
+func <SID>Header(text)
let line = s:idx . " " . a:text
if s:idx < 10
let line = " " . line
@@ -208,15 +209,15 @@ fun! <SID>Header(text)
call append(s:lnum, line)
let s:idx = s:idx + 1
let s:lnum = s:lnum + 1
-endfun
+endfunc
" Get the value of 'pastetoggle'. It could be a special key.
-fun! <SID>PTvalue()
+func <SID>PTvalue()
redir @a
silent set pt
redir END
return substitute(@a, '[^=]*=\(.*\)', '\1', "")
-endfun
+endfunc
" Restore the previous value of 'cpoptions' here, it's used below.
let &cpo = s:cpo_save
@@ -224,1054 +225,1016 @@ let &cpo = s:cpo_save
" List of all options, organized by function.
" The text should be sufficient to know what the option is used for.
-call <SID>Header("important")
-call append("$", "compatible\tbehave very Vi compatible (not advisable)")
+call <SID>Header(gettext("important"))
+call <SID>AddOption("compatible", gettext("behave very Vi compatible (not advisable)"))
call <SID>BinOptionG("cp", &cp)
-call append("$", "cpoptions\tlist of flags to specify Vi compatibility")
+call <SID>AddOption("cpoptions", gettext("list of flags to specify Vi compatibility"))
call <SID>OptionG("cpo", &cpo)
-call append("$", "paste\tpaste mode, insert typed text literally")
+call <SID>AddOption("paste", gettext("paste mode, insert typed text literally"))
call <SID>BinOptionG("paste", &paste)
-call append("$", "pastetoggle\tkey sequence to toggle paste mode")
+call <SID>AddOption("pastetoggle", gettext("key sequence to toggle paste mode"))
if &pt =~ "\x80"
call append("$", " \tset pt=" . <SID>PTvalue())
else
call <SID>OptionG("pt", &pt)
endif
-call append("$", "runtimepath\tlist of directories used for runtime files and plugins")
+call <SID>AddOption("runtimepath", gettext("list of directories used for runtime files and plugins"))
call <SID>OptionG("rtp", &rtp)
-call append("$", "packpath\tlist of directories used for plugin packages")
+call <SID>AddOption("packpath", gettext("list of directories used for plugin packages"))
call <SID>OptionG("pp", &pp)
-call append("$", "helpfile\tname of the main help file")
+call <SID>AddOption("helpfile", gettext("name of the main help file"))
call <SID>OptionG("hf", &hf)
-call <SID>Header("moving around, searching and patterns")
-call append("$", "whichwrap\tlist of flags specifying which commands wrap to another line")
-call append("$", "\t(local to window)")
-call <SID>OptionL("ww")
-call append("$", "startofline\tmany jump commands move the cursor to the first non-blank")
-call append("$", "\tcharacter of a line")
+call <SID>Header(gettext("moving around, searching and patterns"))
+call <SID>AddOption("whichwrap", gettext("list of flags specifying which commands wrap to another line"))
+call <SID>OptionG("ww", &ww)
+call <SID>AddOption("startofline", gettext("many jump commands move the cursor to the first non-blank\ncharacter of a line"))
call <SID>BinOptionG("sol", &sol)
-call append("$", "paragraphs\tnroff macro names that separate paragraphs")
+call <SID>AddOption("paragraphs", gettext("nroff macro names that separate paragraphs"))
call <SID>OptionG("para", &para)
-call append("$", "sections\tnroff macro names that separate sections")
+call <SID>AddOption("sections", gettext("nroff macro names that separate sections"))
call <SID>OptionG("sect", &sect)
-call append("$", "path\tlist of directory names used for file searching")
-call append("$", "\t(global or local to buffer)")
+call <SID>AddOption("path", gettext("list of directory names used for file searching"))
+call append("$", "\t" .. s:global_or_local)
call <SID>OptionG("pa", &pa)
-call <SID>AddOption("cdhome", ":cd without argument goes to the home directory")
+call <SID>AddOption("cdhome", gettext(":cd without argument goes to the home directory"))
call <SID>BinOptionG("cdh", &cdh)
-call append("$", "cdpath\tlist of directory names used for :cd")
+call <SID>AddOption("cdpath", gettext("list of directory names used for :cd"))
call <SID>OptionG("cd", &cd)
if exists("+autochdir")
- call append("$", "autochdir\tchange to directory of file in buffer")
+ call <SID>AddOption("autochdir", gettext("change to directory of file in buffer"))
call <SID>BinOptionG("acd", &acd)
endif
-call append("$", "wrapscan\tsearch commands wrap around the end of the buffer")
+call <SID>AddOption("wrapscan", gettext("search commands wrap around the end of the buffer"))
call <SID>BinOptionG("ws", &ws)
-call append("$", "incsearch\tshow match for partly typed search command")
+call <SID>AddOption("incsearch", gettext("show match for partly typed search command"))
call <SID>BinOptionG("is", &is)
-call append("$", "magic\tchange the way backslashes are used in search patterns")
+call <SID>AddOption("magic", gettext("change the way backslashes are used in search patterns"))
call <SID>BinOptionG("magic", &magic)
-call append("$", "regexpengine\tselect the default regexp engine used")
+call <SID>AddOption("regexpengine", gettext("select the default regexp engine used"))
call <SID>OptionG("re", &re)
-call append("$", "ignorecase\tignore case when using a search pattern")
+call <SID>AddOption("ignorecase", gettext("ignore case when using a search pattern"))
call <SID>BinOptionG("ic", &ic)
-call append("$", "smartcase\toverride 'ignorecase' when pattern has upper case characters")
+call <SID>AddOption("smartcase", gettext("override 'ignorecase' when pattern has upper case characters"))
call <SID>BinOptionG("scs", &scs)
-call append("$", "casemap\twhat method to use for changing case of letters")
+call <SID>AddOption("casemap", gettext("what method to use for changing case of letters"))
call <SID>OptionG("cmp", &cmp)
-call append("$", "maxmempattern\tmaximum amount of memory in Kbyte used for pattern matching")
+call <SID>AddOption("maxmempattern", gettext("maximum amount of memory in Kbyte used for pattern matching"))
call append("$", " \tset mmp=" . &mmp)
-call append("$", "define\tpattern for a macro definition line")
-call append("$", "\t(global or local to buffer)")
+call <SID>AddOption("define", gettext("pattern for a macro definition line"))
+call append("$", "\t" .. s:global_or_local)
call <SID>OptionG("def", &def)
if has("find_in_path")
- call append("$", "include\tpattern for an include-file line")
- call append("$", "\t(local to buffer)")
+ call <SID>AddOption("include", gettext("pattern for an include-file line"))
+ call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("inc")
- call append("$", "includeexpr\texpression used to transform an include line to a file name")
- call append("$", "\t(local to buffer)")
+ call <SID>AddOption("includeexpr", gettext("expression used to transform an include line to a file name"))
+ call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("inex")
endif
-call <SID>Header("tags")
-call append("$", "tagbsearch\tuse binary searching in tags files")
+call <SID>Header(gettext("tags"))
+call <SID>AddOption("tagbsearch", gettext("use binary searching in tags files"))
call <SID>BinOptionG("tbs", &tbs)
-call append("$", "taglength\tnumber of significant characters in a tag name or zero")
+call <SID>AddOption("taglength", gettext("number of significant characters in a tag name or zero"))
call append("$", " \tset tl=" . &tl)
-call append("$", "tags\tlist of file names to search for tags")
-call append("$", "\t(global or local to buffer)")
+call <SID>AddOption("tags", gettext("list of file names to search for tags"))
+call append("$", "\t" .. s:global_or_local)
call <SID>OptionG("tag", &tag)
-call append("$", "tagcase\thow to handle case when searching in tags files:")
-call append("$", "\t\"followic\" to follow 'ignorecase', \"ignore\" or \"match\"")
-call append("$", "\t(global or local to buffer)")
+call <SID>AddOption("tagcase", gettext("how to handle case when searching in tags files:\n\"followic\" to follow 'ignorecase', \"ignore\" or \"match\""))
+call append("$", "\t" .. s:global_or_local)
call <SID>OptionG("tc", &tc)
-call append("$", "tagrelative\tfile names in a tags file are relative to the tags file")
+call <SID>AddOption("tagrelative", gettext("file names in a tags file are relative to the tags file"))
call <SID>BinOptionG("tr", &tr)
-call append("$", "tagstack\ta :tag command will use the tagstack")
+call <SID>AddOption("tagstack", gettext("a :tag command will use the tagstack"))
call <SID>BinOptionG("tgst", &tgst)
-call append("$", "showfulltag\twhen completing tags in Insert mode show more info")
+call <SID>AddOption("showfulltag", gettext("when completing tags in Insert mode show more info"))
call <SID>BinOptionG("sft", &sft)
if has("eval")
- call append("$", "tagfunc\ta function to be used to perform tag searches")
- call append("$", "\t(local to buffer)")
+ call <SID>AddOption("tagfunc", gettext("a function to be used to perform tag searches"))
+ call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("tfu")
endif
-if has("cscope")
- call append("$", "cscopeprg\tcommand for executing cscope")
- call <SID>OptionG("csprg", &csprg)
- call append("$", "cscopetag\tuse cscope for tag commands")
- call <SID>BinOptionG("cst", &cst)
- call append("$", "cscopetagorder\t0 or 1; the order in which \":cstag\" performs a search")
- call append("$", " \tset csto=" . &csto)
- call append("$", "cscopeverbose\tgive messages when adding a cscope database")
- call <SID>BinOptionG("csverb", &csverb)
- call append("$", "cscopepathcomp\thow many components of the path to show")
- call append("$", " \tset cspc=" . &cspc)
- call append("$", "cscopequickfix\twhen to open a quickfix window for cscope")
- call <SID>OptionG("csqf", &csqf)
- call append("$", "cscoperelative\tfile names in a cscope file are relative to that file")
- call <SID>BinOptionG("csre", &csre)
-endif
-call <SID>Header("displaying text")
-call append("$", "scroll\tnumber of lines to scroll for CTRL-U and CTRL-D")
-call append("$", "\t(local to window)")
+call <SID>Header(gettext("displaying text"))
+call <SID>AddOption("scroll", gettext("number of lines to scroll for CTRL-U and CTRL-D"))
+call append("$", "\t" .. s:local_to_window)
call <SID>OptionL("scr")
-call append("$", "scrolloff\tnumber of screen lines to show around the cursor")
+call <SID>AddOption("scrolloff", gettext("number of screen lines to show around the cursor"))
call append("$", " \tset so=" . &so)
-call append("$", "wrap\tlong lines wrap")
-call append("$", "\t(local to window)")
+call <SID>AddOption("wrap", gettext("long lines wrap"))
+call append("$", "\t" .. s:local_to_window)
call <SID>BinOptionL("wrap")
-call append("$", "linebreak\twrap long lines at a character in 'breakat'")
-call append("$", "\t(local to window)")
+call <SID>AddOption("linebreak", gettext("wrap long lines at a character in 'breakat'"))
+call append("$", "\t" .. s:local_to_window)
call <SID>BinOptionL("lbr")
-call append("$", "breakindent\tpreserve indentation in wrapped text")
-call append("$", "\t(local to window)")
+call <SID>AddOption("breakindent", gettext("preserve indentation in wrapped text"))
+call append("$", "\t" .. s:local_to_window)
call <SID>BinOptionL("bri")
-call append("$", "breakindentopt\tadjust breakindent behaviour")
-call append("$", "\t(local to window)")
+call <SID>AddOption("breakindentopt", gettext("adjust breakindent behaviour"))
+call append("$", "\t" .. s:local_to_window)
call <SID>OptionL("briopt")
-call append("$", "breakat\twhich characters might cause a line break")
+call <SID>AddOption("breakat", gettext("which characters might cause a line break"))
call <SID>OptionG("brk", &brk)
-call append("$", "showbreak\tstring to put before wrapped screen lines")
+call <SID>AddOption("showbreak", gettext("string to put before wrapped screen lines"))
call <SID>OptionG("sbr", &sbr)
-call append("$", "sidescroll\tminimal number of columns to scroll horizontally")
+call <SID>AddOption("sidescroll", gettext("minimal number of columns to scroll horizontally"))
call append("$", " \tset ss=" . &ss)
-call append("$", "sidescrolloff\tminimal number of columns to keep left and right of the cursor")
+call <SID>AddOption("sidescrolloff", gettext("minimal number of columns to keep left and right of the cursor"))
call append("$", " \tset siso=" . &siso)
-call append("$", "display\tinclude \"lastline\" to show the last line even if it doesn't fit")
-call append("$", "\tinclude \"uhex\" to show unprintable characters as a hex number")
+call <SID>AddOption("display", gettext("include \"lastline\" to show the last line even if it doesn't fit\ninclude \"uhex\" to show unprintable characters as a hex number"))
call <SID>OptionG("dy", &dy)
-call append("$", "fillchars\tcharacters to use for the status line, folds and filler lines")
+call <SID>AddOption("fillchars", gettext("characters to use for the status line, folds and filler lines"))
call <SID>OptionG("fcs", &fcs)
-call append("$", "cmdheight\tnumber of lines used for the command-line")
+call <SID>AddOption("cmdheight", gettext("number of lines used for the command-line"))
call append("$", " \tset ch=" . &ch)
-call append("$", "columns\twidth of the display")
+call <SID>AddOption("columns", gettext("width of the display"))
call append("$", " \tset co=" . &co)
-call append("$", "lines\tnumber of lines in the display")
+call <SID>AddOption("lines", gettext("number of lines in the display"))
call append("$", " \tset lines=" . &lines)
-call append("$", "window\tnumber of lines to scroll for CTRL-F and CTRL-B")
+call <SID>AddOption("window", gettext("number of lines to scroll for CTRL-F and CTRL-B"))
call append("$", " \tset window=" . &window)
-call append("$", "lazyredraw\tdon't redraw while executing macros")
+call <SID>AddOption("lazyredraw", gettext("don't redraw while executing macros"))
call <SID>BinOptionG("lz", &lz)
if has("reltime")
- call append("$", "redrawtime\ttimeout for 'hlsearch' and :match highlighting in msec")
+ call <SID>AddOption("redrawtime", gettext("timeout for 'hlsearch' and :match highlighting in msec"))
call append("$", " \tset rdt=" . &rdt)
endif
-call append("$", "writedelay\tdelay in msec for each char written to the display")
-call append("$", "\t(for debugging)")
+call <SID>AddOption("writedelay", gettext("delay in msec for each char written to the display\n(for debugging)"))
call append("$", " \tset wd=" . &wd)
-call append("$", "list\tshow <Tab> as ^I and end-of-line as $")
-call append("$", "\t(local to window)")
+call <SID>AddOption("list", gettext("show <Tab> as ^I and end-of-line as $"))
+call append("$", "\t" .. s:local_to_window)
call <SID>BinOptionL("list")
-call append("$", "listchars\tlist of strings used for list mode")
+call <SID>AddOption("listchars", gettext("list of strings used for list mode"))
call <SID>OptionG("lcs", &lcs)
-call append("$", "number\tshow the line number for each line")
-call append("$", "\t(local to window)")
+call <SID>AddOption("number", gettext("show the line number for each line"))
+call append("$", "\t" .. s:local_to_window)
call <SID>BinOptionL("nu")
-call append("$", "relativenumber\tshow the relative line number for each line")
-call append("$", "\t(local to window)")
+call <SID>AddOption("relativenumber", gettext("show the relative line number for each line"))
+call append("$", "\t" .. s:local_to_window)
call <SID>BinOptionL("rnu")
if has("linebreak")
- call append("$", "numberwidth\tnumber of columns to use for the line number")
- call append("$", "\t(local to window)")
+ call <SID>AddOption("numberwidth", gettext("number of columns to use for the line number"))
+ call append("$", "\t" .. s:local_to_window)
call <SID>OptionL("nuw")
endif
if has("conceal")
- call append("$", "conceallevel\tcontrols whether concealable text is hidden")
- call append("$", "\t(local to window)")
+ call <SID>AddOption("conceallevel", gettext("controls whether concealable text is hidden"))
+ call append("$", "\t" .. s:local_to_window)
call <SID>OptionL("cole")
- call append("$", "concealcursor\tmodes in which text in the cursor line can be concealed")
- call append("$", "\t(local to window)")
+ call <SID>AddOption("concealcursor", gettext("modes in which text in the cursor line can be concealed"))
+ call append("$", "\t" .. s:local_to_window)
call <SID>OptionL("cocu")
endif
-call <SID>Header("syntax, highlighting and spelling")
-call append("$", "background\t\"dark\" or \"light\"; the background color brightness")
+call <SID>Header(gettext("syntax, highlighting and spelling"))
+call <SID>AddOption("background", gettext("\"dark\" or \"light\"; the background color brightness"))
call <SID>OptionG("bg", &bg)
-call append("$", "filetype\ttype of file; triggers the FileType event when set")
-call append("$", "\t(local to buffer)")
+call <SID>AddOption("filetype", gettext("type of file; triggers the FileType event when set"))
+call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("ft")
if has("syntax")
- call append("$", "syntax\tname of syntax highlighting used")
- call append("$", "\t(local to buffer)")
+ call <SID>AddOption("syntax", gettext("name of syntax highlighting used"))
+ call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("syn")
- call append("$", "synmaxcol\tmaximum column to look for syntax items")
- call append("$", "\t(local to buffer)")
+ call <SID>AddOption("synmaxcol", gettext("maximum column to look for syntax items"))
+ call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("smc")
endif
-call append("$", "highlight\twhich highlighting to use for various occasions")
+call <SID>AddOption("highlight", gettext("which highlighting to use for various occasions"))
call <SID>OptionG("hl", &hl)
-call append("$", "hlsearch\thighlight all matches for the last used search pattern")
+call <SID>AddOption("hlsearch", gettext("highlight all matches for the last used search pattern"))
call <SID>BinOptionG("hls", &hls)
if has("termguicolors")
- call append("$", "termguicolors\tuse GUI colors for the terminal")
+ call <SID>AddOption("termguicolors", gettext("use GUI colors for the terminal"))
call <SID>BinOptionG("tgc", &tgc)
endif
if has("syntax")
- call append("$", "cursorcolumn\thighlight the screen column of the cursor")
- call append("$", "\t(local to window)")
+ call <SID>AddOption("cursorcolumn", gettext("highlight the screen column of the cursor"))
+ call append("$", "\t" .. s:local_to_window)
call <SID>BinOptionL("cuc")
- call append("$", "cursorline\thighlight the screen line of the cursor")
- call append("$", "\t(local to window)")
+ call <SID>AddOption("cursorline", gettext("highlight the screen line of the cursor"))
+ call append("$", "\t" .. s:local_to_window)
call <SID>BinOptionL("cul")
- call append("$", "cursorlineopt\tspecifies which area 'cursorline' highlights")
- call append("$", "\t(local to window)")
+ call <SID>AddOption("cursorlineopt", gettext("specifies which area 'cursorline' highlights"))
+ call append("$", "\t" .. s:local_to_window)
call <SID>OptionL("culopt")
- call append("$", "colorcolumn\tcolumns to highlight")
- call append("$", "\t(local to window)")
+ call <SID>AddOption("colorcolumn", gettext("columns to highlight"))
+ call append("$", "\t" .. s:local_to_window)
call <SID>OptionL("cc")
- call append("$", "spell\thighlight spelling mistakes")
- call append("$", "\t(local to window)")
+ call <SID>AddOption("spell", gettext("highlight spelling mistakes"))
+ call append("$", "\t" .. s:local_to_window)
call <SID>BinOptionL("spell")
- call append("$", "spelllang\tlist of accepted languages")
- call append("$", "\t(local to buffer)")
+ call <SID>AddOption("spelllang", gettext("list of accepted languages"))
+ call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("spl")
- call append("$", "spellfile\tfile that \"zg\" adds good words to")
- call append("$", "\t(local to buffer)")
+ call <SID>AddOption("spellfile", gettext("file that \"zg\" adds good words to"))
+ call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("spf")
- call append("$", "spellcapcheck\tpattern to locate the end of a sentence")
- call append("$", "\t(local to buffer)")
+ call <SID>AddOption("spellcapcheck", gettext("pattern to locate the end of a sentence"))
+ call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("spc")
- call append("$", "spelloptions\tflags to change how spell checking works")
- call append("$", "\t(local to buffer)")
+ call <SID>AddOption("spelloptions", gettext("flags to change how spell checking works"))
+ call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("spo")
- call append("$", "spellsuggest\tmethods used to suggest corrections")
+ call <SID>AddOption("spellsuggest", gettext("methods used to suggest corrections"))
call <SID>OptionG("sps", &sps)
- call append("$", "mkspellmem\tamount of memory used by :mkspell before compressing")
+ call <SID>AddOption("mkspellmem", gettext("amount of memory used by :mkspell before compressing"))
call <SID>OptionG("msm", &msm)
endif
-call <SID>Header("multiple windows")
-call append("$", "laststatus\t0, 1, 2 or 3; when to use a status line for the last window")
+call <SID>Header(gettext("multiple windows"))
+call <SID>AddOption("laststatus", gettext("0, 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")
+ call <SID>AddOption("statuscolumn", gettext("custom format for the status column"))
+ call append("$", "\t" .. s:local_to_window)
+ call <SID>OptionG("stc", &stc)
+ call <SID>AddOption("statusline", gettext("alternate format to be used for a status line"))
call <SID>OptionG("stl", &stl)
endif
-call append("$", "equalalways\tmake all windows the same size when adding/removing windows")
+call <SID>AddOption("equalalways", gettext("make all windows the same size when adding/removing windows"))
call <SID>BinOptionG("ea", &ea)
-call append("$", "eadirection\tin which direction 'equalalways' works: \"ver\", \"hor\" or \"both\"")
+call <SID>AddOption("eadirection", gettext("in which direction 'equalalways' works: \"ver\", \"hor\" or \"both\""))
call <SID>OptionG("ead", &ead)
-call append("$", "winheight\tminimal number of lines used for the current window")
+call <SID>AddOption("winheight", gettext("minimal number of lines used for the current window"))
call append("$", " \tset wh=" . &wh)
-call append("$", "winminheight\tminimal number of lines used for any window")
+call <SID>AddOption("winminheight", gettext("minimal number of lines used for any window"))
call append("$", " \tset wmh=" . &wmh)
-call append("$", "winfixheight\tkeep the height of the window")
-call append("$", "\t(local to window)")
+call <SID>AddOption("winfixheight", gettext("keep the height of the window"))
+call append("$", "\t" .. s:local_to_window)
call <SID>BinOptionL("wfh")
-call append("$", "winfixwidth\tkeep the width of the window")
-call append("$", "\t(local to window)")
+call <SID>AddOption("winfixwidth", gettext("keep the width of the window"))
+call append("$", "\t" .. s:local_to_window)
call <SID>BinOptionL("wfw")
-call append("$", "winwidth\tminimal number of columns used for the current window")
+call <SID>AddOption("winwidth", gettext("minimal number of columns used for the current window"))
call append("$", " \tset wiw=" . &wiw)
-call append("$", "winminwidth\tminimal number of columns used for any window")
+call <SID>AddOption("winminwidth", gettext("minimal number of columns used for any window"))
call append("$", " \tset wmw=" . &wmw)
-call append("$", "helpheight\tinitial height of the help window")
+call <SID>AddOption("helpheight", gettext("initial height of the help window"))
call append("$", " \tset hh=" . &hh)
if has("quickfix")
- " call append("$", "previewpopup\tuse a popup window for preview")
+ " call <SID>AddOption("previewpopup", gettext("use a popup window for preview"))
" call append("$", " \tset pvp=" . &pvp)
- call append("$", "previewheight\tdefault height for the preview window")
+ call <SID>AddOption("previewheight", gettext("default height for the preview window"))
call append("$", " \tset pvh=" . &pvh)
- call append("$", "previewwindow\tidentifies the preview window")
- call append("$", "\t(local to window)")
+ call <SID>AddOption("previewwindow", gettext("identifies the preview window"))
+ call append("$", "\t" .. s:local_to_window)
call <SID>BinOptionL("pvw")
endif
-call append("$", "hidden\tdon't unload a buffer when no longer shown in a window")
+call <SID>AddOption("hidden", gettext("don't unload a buffer when no longer shown in a window"))
call <SID>BinOptionG("hid", &hid)
-call append("$", "switchbuf\t\"useopen\" and/or \"split\"; which window to use when jumping")
-call append("$", "\tto a buffer")
+call <SID>AddOption("switchbuf", gettext("\"useopen\" and/or \"split\"; which window to use when jumping\nto a buffer"))
call <SID>OptionG("swb", &swb)
-call append("$", "splitbelow\ta new window is put below the current one")
+call <SID>AddOption("splitbelow", gettext("a new window is put below the current one"))
call <SID>BinOptionG("sb", &sb)
-call append("$", "splitright\ta new window is put right of the current one")
+call <SID>AddOption("splitkeep", gettext("determines scroll behavior for split windows"))
+call <SID>BinOptionG("spk", &spk)
+call <SID>AddOption("splitright", gettext("a new window is put right of the current one"))
call <SID>BinOptionG("spr", &spr)
-call append("$", "scrollbind\tthis window scrolls together with other bound windows")
-call append("$", "\t(local to window)")
+call <SID>AddOption("scrollbind", gettext("this window scrolls together with other bound windows"))
+call append("$", "\t" .. s:local_to_window)
call <SID>BinOptionL("scb")
-call append("$", "scrollopt\t\"ver\", \"hor\" and/or \"jump\"; list of options for 'scrollbind'")
+call <SID>AddOption("scrollopt", gettext("\"ver\", \"hor\" and/or \"jump\"; list of options for 'scrollbind'"))
call <SID>OptionG("sbo", &sbo)
-call append("$", "cursorbind\tthis window's cursor moves together with other bound windows")
-call append("$", "\t(local to window)")
+call <SID>AddOption("cursorbind", gettext("this window's cursor moves together with other bound windows"))
+call append("$", "\t" .. s:local_to_window)
call <SID>BinOptionL("crb")
if has("terminal")
- call append("$", "termsize\tsize of a terminal window")
- call append("$", "\t(local to window)")
+ call <SID>AddOption("termsize", gettext("size of a terminal window"))
+ call append("$", "\t" .. s:local_to_window)
call <SID>OptionL("tms")
- call append("$", "termkey\tkey that precedes Vim commands in a terminal window")
- call append("$", "\t(local to window)")
+ call <SID>AddOption("termkey", gettext("key that precedes Vim commands in a terminal window"))
+ call append("$", "\t" .. s:local_to_window)
call <SID>OptionL("tk")
endif
-call <SID>Header("multiple tab pages")
-call append("$", "showtabline\t0, 1 or 2; when to use a tab pages line")
+call <SID>Header(gettext("multiple tab pages"))
+call <SID>AddOption("showtabline", gettext("0, 1 or 2; when to use a tab pages line"))
call append("$", " \tset stal=" . &stal)
-call append("$", "tabpagemax\tmaximum number of tab pages to open for -p and \"tab all\"")
+call <SID>AddOption("tabpagemax", gettext("maximum number of tab pages to open for -p and \"tab all\""))
call append("$", " \tset tpm=" . &tpm)
-call append("$", "tabline\tcustom tab pages line")
+call <SID>AddOption("tabline", gettext("custom tab pages line"))
call <SID>OptionG("tal", &tal)
if has("gui")
- call append("$", "guitablabel\tcustom tab page label for the GUI")
+ call <SID>AddOption("guitablabel", gettext("custom tab page label for the GUI"))
call <SID>OptionG("gtl", &gtl)
- call append("$", "guitabtooltip\tcustom tab page tooltip for the GUI")
+ call <SID>AddOption("guitabtooltip", gettext("custom tab page tooltip for the GUI"))
call <SID>OptionG("gtt", &gtt)
endif
-call <SID>Header("terminal")
-call append("$", "scrolljump\tminimal number of lines to scroll at a time")
+call <SID>Header(gettext("terminal"))
+call <SID>AddOption("scrolljump", gettext("minimal number of lines to scroll at a time"))
call append("$", " \tset sj=" . &sj)
if has("gui") || has("msdos") || has("win32")
- call append("$", "guicursor\tspecifies what the cursor looks like in different modes")
+ call <SID>AddOption("guicursor", gettext("specifies what the cursor looks like in different modes"))
call <SID>OptionG("gcr", &gcr)
endif
if has("title")
let &title = s:old_title
- call append("$", "title\tshow info in the window title")
+ call <SID>AddOption("title", gettext("show info in the window title"))
call <SID>BinOptionG("title", &title)
set notitle
- call append("$", "titlelen\tpercentage of 'columns' used for the window title")
+ call <SID>AddOption("titlelen", gettext("percentage of 'columns' used for the window title"))
call append("$", " \tset titlelen=" . &titlelen)
- call append("$", "titlestring\twhen not empty, string to be used for the window title")
+ call <SID>AddOption("titlestring", gettext("when not empty, string to be used for the window title"))
call <SID>OptionG("titlestring", &titlestring)
- call append("$", "titleold\tstring to restore the title to when exiting Vim")
+ call <SID>AddOption("titleold", gettext("string to restore the title to when exiting Vim"))
call <SID>OptionG("titleold", &titleold)
let &icon = s:old_icon
- call append("$", "icon\tset the text of the icon for this window")
+ call <SID>AddOption("icon", gettext("set the text of the icon for this window"))
call <SID>BinOptionG("icon", &icon)
set noicon
- call append("$", "iconstring\twhen not empty, text for the icon of this window")
+ call <SID>AddOption("iconstring", gettext("when not empty, text for the icon of this window"))
call <SID>OptionG("iconstring", &iconstring)
endif
-call <SID>Header("using the mouse")
-call append("$", "mouse\tlist of flags for using the mouse")
+call <SID>Header(gettext("using the mouse"))
+call <SID>AddOption("mouse", gettext("list of flags for using the mouse"))
call <SID>OptionG("mouse", &mouse)
if has("gui")
- call append("$", "mousefocus\tthe window with the mouse pointer becomes the current one")
+ call <SID>AddOption("mousefocus", gettext("the window with the mouse pointer becomes the current one"))
call <SID>BinOptionG("mousef", &mousef)
- call append("$", "mousehide\thide the mouse pointer while typing")
+ call <SID>AddOption("mousehide", gettext("hide the mouse pointer while typing"))
call <SID>BinOptionG("mh", &mh)
endif
-call append("$", "mousemodel\t\"extend\", \"popup\" or \"popup_setpos\"; what the right")
-call append("$", "\tmouse button is used for")
+call <SID>AddOption("mousemodel", gettext("\"extend\", \"popup\" or \"popup_setpos\"; what the right\nmouse button is used for"))
call <SID>OptionG("mousem", &mousem)
-call append("$", "mousetime\tmaximum time in msec to recognize a double-click")
+call <SID>AddOption("mousetime", gettext("maximum time in msec to recognize a double-click"))
call append("$", " \tset mouset=" . &mouset)
if has("mouseshape")
- call append("$", "mouseshape\twhat the mouse pointer looks like in different modes")
+ call <SID>AddOption("mouseshape", gettext("what the mouse pointer looks like in different modes"))
call <SID>OptionG("mouses", &mouses)
endif
if has("gui")
- call <SID>Header("GUI")
- call append("$", "guifont\tlist of font names to be used in the GUI")
+ call <SID>Header(gettext("GUI"))
+ call <SID>AddOption("guifont", gettext("list of font names to be used in the GUI"))
call <SID>OptionG("gfn", &gfn)
if has("xfontset")
- call append("$", "guifontset\tpair of fonts to be used, for multibyte editing")
+ call <SID>AddOption("guifontset", gettext("pair of fonts to be used, for multibyte editing"))
call <SID>OptionG("gfs", &gfs)
endif
- call append("$", "guifontwide\tlist of font names to be used for double-wide characters")
+ call <SID>AddOption("guifontwide", gettext("list of font names to be used for double-wide characters"))
call <SID>OptionG("gfw", &gfw)
- call append("$", "guioptions\tlist of flags that specify how the GUI works")
+ call <SID>AddOption("guioptions", gettext("list of flags that specify how the GUI works"))
call <SID>OptionG("go", &go)
if has("gui_gtk")
- call append("$", "toolbar\t\"icons\", \"text\" and/or \"tooltips\"; how to show the toolbar")
+ call <SID>AddOption("toolbar", gettext("\"icons\", \"text\" and/or \"tooltips\"; how to show the toolbar"))
call <SID>OptionG("tb", &tb)
if has("gui_gtk2")
- call append("$", "toolbariconsize\tsize of toolbar icons")
+ call <SID>AddOption("toolbariconsize", gettext("size of toolbar icons"))
call <SID>OptionG("tbis", &tbis)
endif
endif
if has("browse")
- call append("$", "browsedir\t\"last\", \"buffer\" or \"current\": which directory used for the file browser")
+ call <SID>AddOption("browsedir", gettext("\"last\", \"buffer\" or \"current\": which directory used for the file browser"))
call <SID>OptionG("bsdir", &bsdir)
endif
if has("multi_lang")
- call append("$", "langmenu\tlanguage to be used for the menus")
+ call <SID>AddOption("langmenu", gettext("language to be used for the menus"))
call <SID>OptionG("langmenu", &lm)
endif
- call append("$", "menuitems\tmaximum number of items in one menu")
+ call <SID>AddOption("menuitems", gettext("maximum number of items in one menu"))
call append("$", " \tset mis=" . &mis)
if has("winaltkeys")
- call append("$", "winaltkeys\t\"no\", \"yes\" or \"menu\"; how to use the ALT key")
+ call <SID>AddOption("winaltkeys", gettext("\"no\", \"yes\" or \"menu\"; how to use the ALT key"))
call <SID>OptionG("wak", &wak)
endif
- call append("$", "linespace\tnumber of pixel lines to use between characters")
+ call <SID>AddOption("linespace", gettext("number of pixel lines to use between characters"))
call append("$", " \tset lsp=" . &lsp)
if has("balloon_eval") || has("balloon_eval_term")
- call append("$", "balloondelay\tdelay in milliseconds before a balloon may pop up")
+ call <SID>AddOption("balloondelay", gettext("delay in milliseconds before a balloon may pop up"))
call append("$", " \tset bdlay=" . &bdlay)
if has("balloon_eval")
- call append("$", "ballooneval\tuse balloon evaluation in the GUI")
+ call <SID>AddOption("ballooneval", gettext("use balloon evaluation in the GUI"))
call <SID>BinOptionG("beval", &beval)
endif
if has("balloon_eval_term")
- call append("$", "balloonevalterm\tuse balloon evaluation in the terminal")
+ call <SID>AddOption("balloonevalterm", gettext("use balloon evaluation in the terminal"))
call <SID>BinOptionG("bevalterm", &beval)
endif
if has("eval")
- call append("$", "balloonexpr\texpression to show in balloon eval")
+ call <SID>AddOption("balloonexpr", gettext("expression to show in balloon eval"))
call append("$", " \tset bexpr=" . &bexpr)
endif
endif
endif
-if has("printer")
- call <SID>Header("printing")
- call append("$", "printoptions\tlist of items that control the format of :hardcopy output")
- call <SID>OptionG("popt", &popt)
- call append("$", "printdevice\tname of the printer to be used for :hardcopy")
- call <SID>OptionG("pdev", &pdev)
- if has("postscript")
- call append("$", "printexpr\texpression used to print the PostScript file for :hardcopy")
- call <SID>OptionG("pexpr", &pexpr)
- endif
- call append("$", "printfont\tname of the font to be used for :hardcopy")
- call <SID>OptionG("pfn", &pfn)
- call append("$", "printheader\tformat of the header used for :hardcopy")
- call <SID>OptionG("pheader", &pheader)
- if has("postscript")
- call append("$", "printencoding\tencoding used to print the PostScript file for :hardcopy")
- call <SID>OptionG("penc", &penc)
- endif
- call append("$", "printmbcharset\tthe CJK character set to be used for CJK output from :hardcopy")
- call <SID>OptionG("pmbcs", &pmbcs)
- call append("$", "printmbfont\tlist of font names to be used for CJK output from :hardcopy")
- call <SID>OptionG("pmbfn", &pmbfn)
-endif
-
-call <SID>Header("messages and info")
-call append("$", "terse\tadd 's' flag in 'shortmess' (don't show search message)")
+call <SID>Header(gettext("messages and info"))
+call <SID>AddOption("terse", gettext("add 's' flag in 'shortmess' (don't show search message)"))
call <SID>BinOptionG("terse", &terse)
-call append("$", "shortmess\tlist of flags to make messages shorter")
+call <SID>AddOption("shortmess", gettext("list of flags to make messages shorter"))
call <SID>OptionG("shm", &shm)
-call append("$", "showcmd\tshow (partial) command keys in the status line")
+call <SID>AddOption("showcmd", gettext("show (partial) command keys in location given by 'showcmdloc'"))
let &sc = s:old_sc
call <SID>BinOptionG("sc", &sc)
set nosc
-call append("$", "showmode\tdisplay the current mode in the status line")
+call <SID>AddOption("showcmdloc", gettext("location where to show the (partial) command keys for 'showcmd'"))
+ call <SID>OptionG("sloc", &sloc)
+call <SID>AddOption("showmode", gettext("display the current mode in the status line"))
call <SID>BinOptionG("smd", &smd)
-call append("$", "ruler\tshow cursor position below each window")
+call <SID>AddOption("ruler", gettext("show cursor position below each window"))
let &ru = s:old_ru
call <SID>BinOptionG("ru", &ru)
set noru
if has("statusline")
- call append("$", "rulerformat\talternate format to be used for the ruler")
+ call <SID>AddOption("rulerformat", gettext("alternate format to be used for the ruler"))
call <SID>OptionG("ruf", &ruf)
endif
-call append("$", "report\tthreshold for reporting number of changed lines")
+call <SID>AddOption("report", gettext("threshold for reporting number of changed lines"))
call append("$", " \tset report=" . &report)
-call append("$", "verbose\tthe higher the more messages are given")
+call <SID>AddOption("verbose", gettext("the higher the more messages are given"))
call append("$", " \tset vbs=" . &vbs)
-call append("$", "verbosefile\tfile to write messages in")
+call <SID>AddOption("verbosefile", gettext("file to write messages in"))
call <SID>OptionG("vfile", &vfile)
-call append("$", "more\tpause listings when the screen is full")
+call <SID>AddOption("more", gettext("pause listings when the screen is full"))
call <SID>BinOptionG("more", &more)
if has("dialog_con") || has("dialog_gui")
- call append("$", "confirm\tstart a dialog when a command fails")
+ call <SID>AddOption("confirm", gettext("start a dialog when a command fails"))
call <SID>BinOptionG("cf", &cf)
endif
-call append("$", "errorbells\tring the bell for error messages")
+call <SID>AddOption("errorbells", gettext("ring the bell for error messages"))
call <SID>BinOptionG("eb", &eb)
-call append("$", "visualbell\tuse a visual bell instead of beeping")
+call <SID>AddOption("visualbell", gettext("use a visual bell instead of beeping"))
call <SID>BinOptionG("vb", &vb)
-call append("$", "belloff\tdo not ring the bell for these reasons")
+call <SID>AddOption("belloff", gettext("do not ring the bell for these reasons"))
call <SID>OptionG("belloff", &belloff)
if has("multi_lang")
- call append("$", "helplang\tlist of preferred languages for finding help")
+ call <SID>AddOption("helplang", gettext("list of preferred languages for finding help"))
call <SID>OptionG("hlg", &hlg)
endif
-call <SID>Header("selecting text")
-call append("$", "selection\t\"old\", \"inclusive\" or \"exclusive\"; how selecting text behaves")
+call <SID>Header(gettext("selecting text"))
+call <SID>AddOption("selection", gettext("\"old\", \"inclusive\" or \"exclusive\"; how selecting text behaves"))
call <SID>OptionG("sel", &sel)
-call append("$", "selectmode\t\"mouse\", \"key\" and/or \"cmd\"; when to start Select mode")
-call append("$", "\tinstead of Visual mode")
+call <SID>AddOption("selectmode", gettext("\"mouse\", \"key\" and/or \"cmd\"; when to start Select mode\ninstead of Visual mode"))
call <SID>OptionG("slm", &slm)
if has("clipboard")
- call append("$", "clipboard\t\"unnamed\" to use the * register like unnamed register")
- call append("$", "\t\"autoselect\" to always put selected text on the clipboard")
+ call <SID>AddOption("clipboard", gettext("\"unnamed\" to use the * register like unnamed register\n\"autoselect\" to always put selected text on the clipboard"))
call <SID>OptionG("cb", &cb)
endif
-call append("$", "keymodel\t\"startsel\" and/or \"stopsel\"; what special keys can do")
+call <SID>AddOption("keymodel", gettext("\"startsel\" and/or \"stopsel\"; what special keys can do"))
call <SID>OptionG("km", &km)
-call <SID>Header("editing text")
-call append("$", "undolevels\tmaximum number of changes that can be undone")
-call append("$", "\t(global or local to buffer)")
+call <SID>Header(gettext("editing text"))
+call <SID>AddOption("undolevels", gettext("maximum number of changes that can be undone"))
+call append("$", "\t" .. s:global_or_local)
call append("$", " \tset ul=" . s:old_ul)
-call append("$", "undofile\tautomatically save and restore undo history")
+call <SID>AddOption("undofile", gettext("automatically save and restore undo history"))
call <SID>BinOptionG("udf", &udf)
-call append("$", "undodir\tlist of directories for undo files")
+call <SID>AddOption("undodir", gettext("list of directories for undo files"))
call <SID>OptionG("udir", &udir)
-call append("$", "undoreload\tmaximum number lines to save for undo on a buffer reload")
+call <SID>AddOption("undoreload", gettext("maximum number lines to save for undo on a buffer reload"))
call append("$", " \tset ur=" . &ur)
-call append("$", "modified\tchanges have been made and not written to a file")
+call <SID>AddOption("modified", gettext("changes have been made and not written to a file"))
call append("$", "\t" .. s:local_to_buffer)
call <SID>BinOptionL("mod")
-call append("$", "readonly\tbuffer is not to be written")
+call <SID>AddOption("readonly", gettext("buffer is not to be written"))
call append("$", "\t" .. s:local_to_buffer)
call <SID>BinOptionL("ro")
-call <SID>AddOption("modifiable", "changes to the text are possible")
+call <SID>AddOption("modifiable", gettext("changes to the text are possible"))
call append("$", "\t" .. s:local_to_buffer)
call <SID>BinOptionL("ma")
-call append("$", "textwidth\tline length above which to break a line")
+call <SID>AddOption("textwidth", gettext("line length above which to break a line"))
call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("tw")
-call append("$", "wrapmargin\tmargin from the right in which to break a line")
+call <SID>AddOption("wrapmargin", gettext("margin from the right in which to break a line"))
call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("wm")
-call append("$", "backspace\tspecifies what <BS>, CTRL-W, etc. can do in Insert mode")
+call <SID>AddOption("backspace", gettext("specifies what <BS>, CTRL-W, etc. can do in Insert mode"))
call append("$", " \tset bs=" . &bs)
-call append("$", "comments\tdefinition of what comment lines look like")
-call append("$", "\t(local to buffer)")
+call <SID>AddOption("comments", gettext("definition of what comment lines look like"))
+call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("com")
-call append("$", "formatoptions\tlist of flags that tell how automatic formatting works")
-call append("$", "\t(local to buffer)")
+call <SID>AddOption("formatoptions", gettext("list of flags that tell how automatic formatting works"))
+call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("fo")
-call append("$", "formatlistpat\tpattern to recognize a numbered list")
-call append("$", "\t(local to buffer)")
+call <SID>AddOption("formatlistpat", gettext("pattern to recognize a numbered list"))
+call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("flp")
if has("eval")
- call append("$", "formatexpr\texpression used for \"gq\" to format lines")
- call append("$", "\t(local to buffer)")
+ call <SID>AddOption("formatexpr", gettext("expression used for \"gq\" to format lines"))
+ call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("fex")
endif
if has("insert_expand")
- call append("$", "complete\tspecifies how Insert mode completion works for CTRL-N and CTRL-P")
- call append("$", "\t(local to buffer)")
+ call <SID>AddOption("complete", gettext("specifies how Insert mode completion works for CTRL-N and CTRL-P"))
+ call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("cpt")
- call append("$", "completeopt\twhether to use a popup menu for Insert mode completion")
+ call <SID>AddOption("completeopt", gettext("whether to use a popup menu for Insert mode completion"))
call <SID>OptionG("cot", &cot)
- call append("$", "pumheight\tmaximum height of the popup menu")
+ call <SID>AddOption("pumheight", gettext("maximum height of the popup menu"))
call <SID>OptionG("ph", &ph)
- if exists("&pw")
- call append("$", "pumwidth\tminimum width of the popup menu")
- call <SID>OptionG("pw", &pw)
- endif
- call append("$", "completefunc\tuser defined function for Insert mode completion")
- call append("$", "\t(local to buffer)")
+ call <SID>AddOption("pumwidth", gettext("minimum width of the popup menu"))
+ call <SID>OptionG("pw", &pw)
+ call <SID>AddOption("completefunc", gettext("user defined function for Insert mode completion"))
+ call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("cfu")
- call append("$", "omnifunc\tfunction for filetype-specific Insert mode completion")
- call append("$", "\t(local to buffer)")
+ call <SID>AddOption("omnifunc", gettext("function for filetype-specific Insert mode completion"))
+ call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("ofu")
- call append("$", "dictionary\tlist of dictionary files for keyword completion")
- call append("$", "\t(global or local to buffer)")
+ call <SID>AddOption("dictionary", gettext("list of dictionary files for keyword completion"))
+ call append("$", "\t" .. s:global_or_local)
call <SID>OptionG("dict", &dict)
- call append("$", "thesaurus\tlist of thesaurus files for keyword completion")
- call append("$", "\t(global or local to buffer)")
+ call <SID>AddOption("thesaurus", gettext("list of thesaurus files for keyword completion"))
+ call append("$", "\t" .. s:global_or_local)
call <SID>OptionG("tsr", &tsr)
+ call <SID>AddOption("thesaurusfunc", gettext("function used for thesaurus completion"))
+ call append("$", "\t" .. s:global_or_local)
+ call <SID>OptionG("tsrfu", &tsrfu)
endif
-call append("$", "infercase\tadjust case of a keyword completion match")
-call append("$", "\t(local to buffer)")
+call <SID>AddOption("infercase", gettext("adjust case of a keyword completion match"))
+call append("$", "\t" .. s:local_to_buffer)
call <SID>BinOptionL("inf")
if has("digraphs")
- call append("$", "digraph\tenable entering digraphs with c1 <BS> c2")
+ call <SID>AddOption("digraph", gettext("enable entering digraphs with c1 <BS> c2"))
call <SID>BinOptionG("dg", &dg)
endif
-call append("$", "tildeop\tthe \"~\" command behaves like an operator")
+call <SID>AddOption("tildeop", gettext("the \"~\" command behaves like an operator"))
call <SID>BinOptionG("top", &top)
-call <SID>AddOption("operatorfunc", "function called for the \"g@\" operator")
+call <SID>AddOption("operatorfunc", gettext("function called for the \"g@\" operator"))
call <SID>OptionG("opfunc", &opfunc)
-call append("$", "showmatch\twhen inserting a bracket, briefly jump to its match")
+call <SID>AddOption("showmatch", gettext("when inserting a bracket, briefly jump to its match"))
call <SID>BinOptionG("sm", &sm)
-call append("$", "matchtime\ttenth of a second to show a match for 'showmatch'")
+call <SID>AddOption("matchtime", gettext("tenth of a second to show a match for 'showmatch'"))
call append("$", " \tset mat=" . &mat)
-call append("$", "matchpairs\tlist of pairs that match for the \"%\" command")
+call <SID>AddOption("matchpairs", gettext("list of pairs that match for the \"%\" command"))
call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("mps")
-call append("$", "joinspaces\tuse two spaces after '.' when joining a line")
+call <SID>AddOption("joinspaces", gettext("use two spaces after '.' when joining a line"))
call <SID>BinOptionG("js", &js)
-call <SID>AddOption("nrformats", "\"alpha\", \"octal\", \"hex\", \"bin\" and/or \"unsigned\"; number formats\nrecognized for CTRL-A and CTRL-X commands")
+call <SID>AddOption("nrformats", gettext("\"alpha\", \"octal\", \"hex\", \"bin\" and/or \"unsigned\"; number formats\nrecognized for CTRL-A and CTRL-X commands"))
call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("nf")
-call <SID>Header("tabs and indenting")
-call append("$", "tabstop\tnumber of spaces a <Tab> in the text stands for")
-call append("$", "\t(local to buffer)")
+call <SID>Header(gettext("tabs and indenting"))
+call <SID>AddOption("tabstop", gettext("number of spaces a <Tab> in the text stands for"))
+call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("ts")
-call append("$", "shiftwidth\tnumber of spaces used for each step of (auto)indent")
-call append("$", "\t(local to buffer)")
+call <SID>AddOption("shiftwidth", gettext("number of spaces used for each step of (auto)indent"))
+call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("sw")
if has("vartabs")
- call append("$", "vartabstop\tlist of number of spaces a tab counts for")
- call append("$", "\t(local to buffer)")
+ call <SID>AddOption("vartabstop", gettext("list of number of spaces a tab counts for"))
+ call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("vts")
- call append("$", "varsofttabstop\tlist of number of spaces a soft tabsstop counts for")
- call append("$", "\t(local to buffer)")
+ call <SID>AddOption("varsofttabstop", gettext("list of number of spaces a soft tabsstop counts for"))
+ call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("vsts")
endif
-call append("$", "smarttab\ta <Tab> in an indent inserts 'shiftwidth' spaces")
+call <SID>AddOption("smarttab", gettext("a <Tab> in an indent inserts 'shiftwidth' spaces"))
call <SID>BinOptionG("sta", &sta)
-call append("$", "softtabstop\tif non-zero, number of spaces to insert for a <Tab>")
-call append("$", "\t(local to buffer)")
+call <SID>AddOption("softtabstop", gettext("if non-zero, number of spaces to insert for a <Tab>"))
+call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("sts")
-call append("$", "shiftround\tround to 'shiftwidth' for \"<<\" and \">>\"")
+call <SID>AddOption("shiftround", gettext("round to 'shiftwidth' for \"<<\" and \">>\""))
call <SID>BinOptionG("sr", &sr)
-call append("$", "expandtab\texpand <Tab> to spaces in Insert mode")
-call append("$", "\t(local to buffer)")
+call <SID>AddOption("expandtab", gettext("expand <Tab> to spaces in Insert mode"))
+call append("$", "\t" .. s:local_to_buffer)
call <SID>BinOptionL("et")
-call append("$", "autoindent\tautomatically set the indent of a new line")
-call append("$", "\t(local to buffer)")
+call <SID>AddOption("autoindent", gettext("automatically set the indent of a new line"))
+call append("$", "\t" .. s:local_to_buffer)
call <SID>BinOptionL("ai")
if has("smartindent")
- call append("$", "smartindent\tdo clever autoindenting")
- call append("$", "\t(local to buffer)")
+ call <SID>AddOption("smartindent", gettext("do clever autoindenting"))
+ call append("$", "\t" .. s:local_to_buffer)
call <SID>BinOptionL("si")
endif
if has("cindent")
- call append("$", "cindent\tenable specific indenting for C code")
- call append("$", "\t(local to buffer)")
+ call <SID>AddOption("cindent", gettext("enable specific indenting for C code"))
+ call append("$", "\t" .. s:local_to_buffer)
call <SID>BinOptionL("cin")
- call append("$", "cinoptions\toptions for C-indenting")
- call append("$", "\t(local to buffer)")
+ call <SID>AddOption("cinoptions", gettext("options for C-indenting"))
+ call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("cino")
- call append("$", "cinkeys\tkeys that trigger C-indenting in Insert mode")
- call append("$", "\t(local to buffer)")
+ call <SID>AddOption("cinkeys", gettext("keys that trigger C-indenting in Insert mode"))
+ call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("cink")
- call append("$", "cinwords\tlist of words that cause more C-indent")
- call append("$", "\t(local to buffer)")
+ call <SID>AddOption("cinwords", gettext("list of words that cause more C-indent"))
+ call append("$", "\t" .. s: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>AddOption("cinscopedecls", gettext("list of scope declaration names used by cino-g"))
+ call append("$", "\t" .. s: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>AddOption("indentexpr", gettext("expression used to obtain the indent of a line"))
+ call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("inde")
- call append("$", "indentkeys\tkeys that trigger indenting with 'indentexpr' in Insert mode")
- call append("$", "\t(local to buffer)")
+ call <SID>AddOption("indentkeys", gettext("keys that trigger indenting with 'indentexpr' in Insert mode"))
+ call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("indk")
endif
-call append("$", "copyindent\tcopy whitespace for indenting from previous line")
-call append("$", "\t(local to buffer)")
+call <SID>AddOption("copyindent", gettext("copy whitespace for indenting from previous line"))
+call append("$", "\t" .. s:local_to_buffer)
call <SID>BinOptionL("ci")
-call append("$", "preserveindent\tpreserve kind of whitespace when changing indent")
-call append("$", "\t(local to buffer)")
+call <SID>AddOption("preserveindent", gettext("preserve kind of whitespace when changing indent"))
+call append("$", "\t" .. s:local_to_buffer)
call <SID>BinOptionL("pi")
if has("lispindent")
- call append("$", "lisp\tenable lisp mode")
- call append("$", "\t(local to buffer)")
+ call <SID>AddOption("lisp", gettext("enable lisp mode"))
+ call append("$", "\t" .. s:local_to_buffer)
call <SID>BinOptionL("lisp")
- call append("$", "lispwords\twords that change how lisp indenting works")
+ call <SID>AddOption("lispwords", gettext("words that change how lisp indenting works"))
call <SID>OptionL("lw")
+ call <SID>AddOption("lispoptions", gettext("options for Lisp indenting"))
+ call <SID>OptionL("lop")
endif
if has("folding")
- call <SID>Header("folding")
- call <SID>AddOption("foldenable", "unset to display all folds open")
+ call <SID>Header(gettext("folding"))
+ call <SID>AddOption("foldenable", gettext("unset to display all folds open"))
call append("$", "\t" .. s:local_to_window)
call <SID>BinOptionL("fen")
- call append("$", "foldlevel\tfolds with a level higher than this number will be closed")
+ call <SID>AddOption("foldlevel", gettext("folds with a level higher than this number will be closed"))
call append("$", "\t" .. s:local_to_window)
call <SID>OptionL("fdl")
- call append("$", "foldlevelstart\tvalue for 'foldlevel' when starting to edit a file")
+ call <SID>AddOption("foldlevelstart", gettext("value for 'foldlevel' when starting to edit a file"))
call append("$", " \tset fdls=" . &fdls)
- call append("$", "foldcolumn\twidth of the column used to indicate folds")
- call append("$", "\t(local to window)")
+ call <SID>AddOption("foldcolumn", gettext("width of the column used to indicate folds"))
+ call append("$", "\t" .. s:local_to_window)
call <SID>OptionL("fdc")
- call append("$", "foldtext\texpression used to display the text of a closed fold")
- call append("$", "\t(local to window)")
+ call <SID>AddOption("foldtext", gettext("expression used to display the text of a closed fold"))
+ call append("$", "\t" .. s:local_to_window)
call <SID>OptionL("fdt")
- call append("$", "foldclose\tset to \"all\" to close a fold when the cursor leaves it")
+ call <SID>AddOption("foldclose", gettext("set to \"all\" to close a fold when the cursor leaves it"))
call <SID>OptionG("fcl", &fcl)
- call append("$", "foldopen\tspecifies for which commands a fold will be opened")
+ call <SID>AddOption("foldopen", gettext("specifies for which commands a fold will be opened"))
call <SID>OptionG("fdo", &fdo)
- call append("$", "foldminlines\tminimum number of screen lines for a fold to be closed")
- call append("$", "\t(local to window)")
+ call <SID>AddOption("foldminlines", gettext("minimum number of screen lines for a fold to be closed"))
+ call append("$", "\t" .. s:local_to_window)
call <SID>OptionL("fml")
- call append("$", "commentstring\ttemplate for comments; used to put the marker in")
+ call <SID>AddOption("commentstring", gettext("template for comments; used to put the marker in"))
call <SID>OptionL("cms")
- call <SID>AddOption("foldmethod", "folding type: \"manual\", \"indent\", \"expr\", \"marker\",\n\"syntax\" or \"diff\"")
+ call <SID>AddOption("foldmethod", gettext("folding type: \"manual\", \"indent\", \"expr\", \"marker\",\n\"syntax\" or \"diff\""))
call append("$", "\t" .. s:local_to_window)
call <SID>OptionL("fdm")
- call append("$", "foldexpr\texpression used when 'foldmethod' is \"expr\"")
+ call <SID>AddOption("foldexpr", gettext("expression used when 'foldmethod' is \"expr\""))
call append("$", "\t" .. s:local_to_window)
call <SID>OptionL("fde")
- call append("$", "foldignore\tused to ignore lines when 'foldmethod' is \"indent\"")
+ call <SID>AddOption("foldignore", gettext("used to ignore lines when 'foldmethod' is \"indent\""))
call append("$", "\t" .. s:local_to_window)
call <SID>OptionL("fdi")
- call append("$", "foldmarker\tmarkers used when 'foldmethod' is \"marker\"")
+ call <SID>AddOption("foldmarker", gettext("markers used when 'foldmethod' is \"marker\""))
call append("$", "\t" .. s:local_to_window)
call <SID>OptionL("fmr")
- call append("$", "foldnestmax\tmaximum fold depth for when 'foldmethod' is \"indent\" or \"syntax\"")
+ call <SID>AddOption("foldnestmax", gettext("maximum fold depth for when 'foldmethod' is \"indent\" or \"syntax\""))
call append("$", "\t" .. s:local_to_window)
call <SID>OptionL("fdn")
endif
if has("diff")
- call <SID>Header("diff mode")
- call append("$", "diff\tuse diff mode for the current window")
- call append("$", "\t(local to window)")
+ call <SID>Header(gettext("diff mode"))
+ call <SID>AddOption("diff", gettext("use diff mode for the current window"))
+ call append("$", "\t" .. s:local_to_window)
call <SID>BinOptionL("diff")
- call append("$", "diffopt\toptions for using diff mode")
+ call <SID>AddOption("diffopt", gettext("options for using diff mode"))
call <SID>OptionG("dip", &dip)
- call append("$", "diffexpr\texpression used to obtain a diff file")
+ call <SID>AddOption("diffexpr", gettext("expression used to obtain a diff file"))
call <SID>OptionG("dex", &dex)
- call append("$", "patchexpr\texpression used to patch a file")
+ call <SID>AddOption("patchexpr", gettext("expression used to patch a file"))
call <SID>OptionG("pex", &pex)
endif
-call <SID>Header("mapping")
-call append("$", "maxmapdepth\tmaximum depth of mapping")
+call <SID>Header(gettext("mapping"))
+call <SID>AddOption("maxmapdepth", gettext("maximum depth of mapping"))
call append("$", " \tset mmd=" . &mmd)
-call append("$", "remap\trecognize mappings in mapped keys")
-call append("$", "timeout\tallow timing out halfway into a mapping")
+call <SID>AddOption("timeout", gettext("allow timing out halfway into a mapping"))
call <SID>BinOptionG("to", &to)
-call append("$", "ttimeout\tallow timing out halfway into a key code")
+call <SID>AddOption("ttimeout", gettext("allow timing out halfway into a key code"))
call <SID>BinOptionG("ttimeout", &ttimeout)
-call append("$", "timeoutlen\ttime in msec for 'timeout'")
+call <SID>AddOption("timeoutlen", gettext("time in msec for 'timeout'"))
call append("$", " \tset tm=" . &tm)
-call append("$", "ttimeoutlen\ttime in msec for 'ttimeout'")
+call <SID>AddOption("ttimeoutlen", gettext("time in msec for 'ttimeout'"))
call append("$", " \tset ttm=" . &ttm)
-call <SID>Header("reading and writing files")
-call append("$", "modeline\tenable using settings from modelines when reading a file")
-call append("$", "\t(local to buffer)")
+call <SID>Header(gettext("reading and writing files"))
+call <SID>AddOption("modeline", gettext("enable using settings from modelines when reading a file"))
+call append("$", "\t" .. s:local_to_buffer)
call <SID>BinOptionL("ml")
-call append("$", "modelineexpr\tallow setting expression options from a modeline")
+call <SID>AddOption("modelineexpr", gettext("allow setting expression options from a modeline"))
call <SID>BinOptionG("mle", &mle)
-call append("$", "modelines\tnumber of lines to check for modelines")
+call <SID>AddOption("modelines", gettext("number of lines to check for modelines"))
call append("$", " \tset mls=" . &mls)
-call append("$", "binary\tbinary file editing")
-call append("$", "\t(local to buffer)")
+call <SID>AddOption("binary", gettext("binary file editing"))
+call append("$", "\t" .. s:local_to_buffer)
call <SID>BinOptionL("bin")
-call append("$", "endofline\tlast line in the file has an end-of-line")
-call append("$", "\t(local to buffer)")
+call <SID>AddOption("endofline", gettext("last line in the file has an end-of-line"))
+call append("$", "\t" .. s:local_to_buffer)
call <SID>BinOptionL("eol")
-call append("$", "fixendofline\tfixes missing end-of-line at end of text file")
-call append("$", "\t(local to buffer)")
+call <SID>AddOption("endoffile", gettext("last line in the file followed by CTRL-Z"))
+call append("$", "\t" .. s:local_to_buffer)
+call <SID>BinOptionL("eof")
+call <SID>AddOption("fixendofline", gettext("fixes missing end-of-line at end of text file"))
+call append("$", "\t" .. s:local_to_buffer)
call <SID>BinOptionL("fixeol")
-call append("$", "bomb\tprepend a Byte Order Mark to the file")
-call append("$", "\t(local to buffer)")
+call <SID>AddOption("bomb", gettext("prepend a Byte Order Mark to the file"))
+call append("$", "\t" .. s:local_to_buffer)
call <SID>BinOptionL("bomb")
-call append("$", "fileformat\tend-of-line format: \"dos\", \"unix\" or \"mac\"")
-call append("$", "\t(local to buffer)")
+call <SID>AddOption("fileformat", gettext("end-of-line format: \"dos\", \"unix\" or \"mac\""))
+call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("ff")
-call append("$", "fileformats\tlist of file formats to look for when editing a file")
+call <SID>AddOption("fileformats", gettext("list of file formats to look for when editing a file"))
call <SID>OptionG("ffs", &ffs)
-call append("$", "\t(local to buffer)")
-call append("$", "write\twriting files is allowed")
+call <SID>AddOption("write", gettext("writing files is allowed"))
call <SID>BinOptionG("write", &write)
-call append("$", "writebackup\twrite a backup file before overwriting a file")
+call <SID>AddOption("writebackup", gettext("write a backup file before overwriting a file"))
call <SID>BinOptionG("wb", &wb)
-call append("$", "backup\tkeep a backup after overwriting a file")
+call <SID>AddOption("backup", gettext("keep a backup after overwriting a file"))
call <SID>BinOptionG("bk", &bk)
-call append("$", "backupskip\tpatterns that specify for which files a backup is not made")
+call <SID>AddOption("backupskip", gettext("patterns that specify for which files a backup is not made"))
call append("$", " \tset bsk=" . &bsk)
-call append("$", "backupcopy\twhether to make the backup as a copy or rename the existing file")
-call append("$", "\t(global or local to buffer)")
+call <SID>AddOption("backupcopy", gettext("whether to make the backup as a copy or rename the existing file"))
+call append("$", "\t" .. s:global_or_local)
call append("$", " \tset bkc=" . &bkc)
-call append("$", "backupdir\tlist of directories to put backup files in")
+call <SID>AddOption("backupdir", gettext("list of directories to put backup files in"))
call <SID>OptionG("bdir", &bdir)
-call append("$", "backupext\tfile name extension for the backup file")
+call <SID>AddOption("backupext", gettext("file name extension for the backup file"))
call <SID>OptionG("bex", &bex)
-call append("$", "autowrite\tautomatically write a file when leaving a modified buffer")
+call <SID>AddOption("autowrite", gettext("automatically write a file when leaving a modified buffer"))
call <SID>BinOptionG("aw", &aw)
-call append("$", "autowriteall\tas 'autowrite', but works with more commands")
+call <SID>AddOption("autowriteall", gettext("as 'autowrite', but works with more commands"))
call <SID>BinOptionG("awa", &awa)
-call append("$", "writeany\talways write without asking for confirmation")
+call <SID>AddOption("writeany", gettext("always write without asking for confirmation"))
call <SID>BinOptionG("wa", &wa)
-call append("$", "autoread\tautomatically read a file when it was modified outside of Vim")
-call append("$", "\t(global or local to buffer)")
+call <SID>AddOption("autoread", gettext("automatically read a file when it was modified outside of Vim"))
+call append("$", "\t" .. s:global_or_local)
call <SID>BinOptionG("ar", &ar)
-call append("$", "patchmode\tkeep oldest version of a file; specifies file name extension")
+call <SID>AddOption("patchmode", gettext("keep oldest version of a file; specifies file name extension"))
call <SID>OptionG("pm", &pm)
-call append("$", "fsync\tforcibly sync the file to disk after writing it")
+call <SID>AddOption("fsync", gettext("forcibly sync the file to disk after writing it"))
call <SID>BinOptionG("fs", &fs)
-call <SID>Header("the swap file")
-call append("$", "directory\tlist of directories for the swap file")
+call <SID>Header(gettext("the swap file"))
+call <SID>AddOption("directory", gettext("list of directories for the swap file"))
call <SID>OptionG("dir", &dir)
-call append("$", "swapfile\tuse a swap file for this buffer")
-call append("$", "\t(local to buffer)")
+call <SID>AddOption("swapfile", gettext("use a swap file for this buffer"))
+call append("$", "\t" .. s:local_to_buffer)
call <SID>BinOptionL("swf")
-call append("$", "updatecount\tnumber of characters typed to cause a swap file update")
+call <SID>AddOption("updatecount", gettext("number of characters typed to cause a swap file update"))
call append("$", " \tset uc=" . &uc)
-call append("$", "updatetime\ttime in msec after which the swap file will be updated")
+call <SID>AddOption("updatetime", gettext("time in msec after which the swap file will be updated"))
call append("$", " \tset ut=" . &ut)
-call <SID>Header("command line editing")
-call <SID>AddOption("history", "how many command lines are remembered")
+call <SID>Header(gettext("command line editing"))
+call <SID>AddOption("history", gettext("how many command lines are remembered"))
call append("$", " \tset hi=" . &hi)
-call append("$", "wildchar\tkey that triggers command-line expansion")
+call <SID>AddOption("wildchar", gettext("key that triggers command-line expansion"))
call append("$", " \tset wc=" . &wc)
-call append("$", "wildcharm\tlike 'wildchar' but can also be used in a mapping")
+call <SID>AddOption("wildcharm", gettext("like 'wildchar' but can also be used in a mapping"))
call append("$", " \tset wcm=" . &wcm)
-call append("$", "wildmode\tspecifies how command line completion works")
+call <SID>AddOption("wildmode", gettext("specifies how command line completion works"))
call <SID>OptionG("wim", &wim)
if has("wildoptions")
- call append("$", "wildoptions\tempty or \"tagfile\" to list file name of matching tags")
+ call <SID>AddOption("wildoptions", gettext("empty or \"tagfile\" to list file name of matching tags"))
call <SID>OptionG("wop", &wop)
endif
-call append("$", "suffixes\tlist of file name extensions that have a lower priority")
+call <SID>AddOption("suffixes", gettext("list of file name extensions that have a lower priority"))
call <SID>OptionG("su", &su)
if has("file_in_path")
- call append("$", "suffixesadd\tlist of file name extensions added when searching for a file")
- call append("$", "\t(local to buffer)")
+ call <SID>AddOption("suffixesadd", gettext("list of file name extensions added when searching for a file"))
+ call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("sua")
endif
if has("wildignore")
- call append("$", "wildignore\tlist of patterns to ignore files for file name completion")
+ call <SID>AddOption("wildignore", gettext("list of patterns to ignore files for file name completion"))
call <SID>OptionG("wig", &wig)
endif
-call append("$", "fileignorecase\tignore case when using file names")
+call <SID>AddOption("fileignorecase", gettext("ignore case when using file names"))
call <SID>BinOptionG("fic", &fic)
-call append("$", "wildignorecase\tignore case when completing file names")
+call <SID>AddOption("wildignorecase", gettext("ignore case when completing file names"))
call <SID>BinOptionG("wic", &wic)
if has("wildmenu")
- call append("$", "wildmenu\tcommand-line completion shows a list of matches")
+ call <SID>AddOption("wildmenu", gettext("command-line completion shows a list of matches"))
call <SID>BinOptionG("wmnu", &wmnu)
endif
-call append("$", "cedit\tkey used to open the command-line window")
+call <SID>AddOption("cedit", gettext("key used to open the command-line window"))
call <SID>OptionG("cedit", &cedit)
-call append("$", "cmdwinheight\theight of the command-line window")
+call <SID>AddOption("cmdwinheight", gettext("height of the command-line window"))
call <SID>OptionG("cwh", &cwh)
-call <SID>Header("executing external commands")
-call append("$", "shell\tname of the shell program used for external commands")
+call <SID>Header(gettext("executing external commands"))
+call <SID>AddOption("shell", gettext("name of the shell program used for external commands"))
call <SID>OptionG("sh", &sh)
-call append("$", "shellquote\tcharacter(s) to enclose a shell command in")
+call <SID>AddOption("shellquote", gettext("character(s) to enclose a shell command in"))
call <SID>OptionG("shq", &shq)
-call append("$", "shellxquote\tlike 'shellquote' but include the redirection")
+call <SID>AddOption("shellxquote", gettext("like 'shellquote' but include the redirection"))
call <SID>OptionG("sxq", &sxq)
-call append("$", "shellxescape\tcharacters to escape when 'shellxquote' is (")
+call <SID>AddOption("shellxescape", gettext("characters to escape when 'shellxquote' is ("))
call <SID>OptionG("sxe", &sxe)
-call append("$", "shellcmdflag\targument for 'shell' to execute a command")
+call <SID>AddOption("shellcmdflag", gettext("argument for 'shell' to execute a command"))
call <SID>OptionG("shcf", &shcf)
-call append("$", "shellredir\tused to redirect command output to a file")
+call <SID>AddOption("shellredir", gettext("used to redirect command output to a file"))
call <SID>OptionG("srr", &srr)
-call append("$", "shelltemp\tuse a temp file for shell commands instead of using a pipe")
+call <SID>AddOption("shelltemp", gettext("use a temp file for shell commands instead of using a pipe"))
call <SID>BinOptionG("stmp", &stmp)
-call append("$", "equalprg\tprogram used for \"=\" command")
-call append("$", "\t(global or local to buffer)")
+call <SID>AddOption("equalprg", gettext("program used for \"=\" command"))
+call append("$", "\t" .. s:global_or_local)
call <SID>OptionG("ep", &ep)
-call append("$", "formatprg\tprogram used to format lines with \"gq\" command")
+call <SID>AddOption("formatprg", gettext("program used to format lines with \"gq\" command"))
call <SID>OptionG("fp", &fp)
-call append("$", "keywordprg\tprogram used for the \"K\" command")
+call <SID>AddOption("keywordprg", gettext("program used for the \"K\" command"))
call <SID>OptionG("kp", &kp)
-call append("$", "warn\twarn when using a shell command and a buffer has changes")
+call <SID>AddOption("warn", gettext("warn when using a shell command and a buffer has changes"))
call <SID>BinOptionG("warn", &warn)
if has("quickfix")
- call <SID>Header("running make and jumping to errors (quickfix)")
- call append("$", "errorfile\tname of the file that contains error messages")
+ call <SID>Header(gettext("running make and jumping to errors (quickfix)"))
+ call <SID>AddOption("errorfile", gettext("name of the file that contains error messages"))
call <SID>OptionG("ef", &ef)
- call append("$", "errorformat\tlist of formats for error messages")
- call append("$", "\t(global or local to buffer)")
+ call <SID>AddOption("errorformat", gettext("list of formats for error messages"))
+ call append("$", "\t" .. s:global_or_local)
call <SID>OptionG("efm", &efm)
- call append("$", "makeprg\tprogram used for the \":make\" command")
- call append("$", "\t(global or local to buffer)")
+ call <SID>AddOption("makeprg", gettext("program used for the \":make\" command"))
+ call append("$", "\t" .. s:global_or_local)
call <SID>OptionG("mp", &mp)
- call append("$", "shellpipe\tstring used to put the output of \":make\" in the error file")
+ call <SID>AddOption("shellpipe", gettext("string used to put the output of \":make\" in the error file"))
call <SID>OptionG("sp", &sp)
- call append("$", "makeef\tname of the errorfile for the 'makeprg' command")
+ call <SID>AddOption("makeef", gettext("name of the errorfile for the 'makeprg' command"))
call <SID>OptionG("mef", &mef)
- call append("$", "grepprg\tprogram used for the \":grep\" command")
- call append("$", "\t(global or local to buffer)")
+ call <SID>AddOption("grepprg", gettext("program used for the \":grep\" command"))
+ call append("$", "\t" .. s:global_or_local)
call <SID>OptionG("gp", &gp)
- call append("$", "grepformat\tlist of formats for output of 'grepprg'")
+ call <SID>AddOption("grepformat", gettext("list of formats for output of 'grepprg'"))
call <SID>OptionG("gfm", &gfm)
- call append("$", "makeencoding\tencoding of the \":make\" and \":grep\" output")
- call append("$", "\t(global or local to buffer)")
+ call <SID>AddOption("makeencoding", gettext("encoding of the \":make\" and \":grep\" output"))
+ call append("$", "\t" .. s:global_or_local)
call <SID>OptionG("menc", &menc)
endif
if has("win32")
- call <SID>Header("system specific")
- call <SID>AddOption("shellslash", "use forward slashes in file names; for Unix-like shells")
+ call <SID>Header(gettext("system specific"))
+ call <SID>AddOption("shellslash", gettext("use forward slashes in file names; for Unix-like shells"))
call <SID>BinOptionG("ssl", &ssl)
- call <SID>AddOption("completeslash", "specifies slash/backslash used for completion")
+ call <SID>AddOption("completeslash", gettext("specifies slash/backslash used for completion"))
call <SID>OptionG("csl", &csl)
endif
-call <SID>Header("language specific")
-call append("$", "isfname\tspecifies the characters in a file name")
+call <SID>Header(gettext("language specific"))
+call <SID>AddOption("isfname", gettext("specifies the characters in a file name"))
call <SID>OptionG("isf", &isf)
-call append("$", "isident\tspecifies the characters in an identifier")
+call <SID>AddOption("isident", gettext("specifies the characters in an identifier"))
call <SID>OptionG("isi", &isi)
-call append("$", "iskeyword\tspecifies the characters in a keyword")
-call append("$", "\t(local to buffer)")
+call <SID>AddOption("iskeyword", gettext("specifies the characters in a keyword"))
+call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("isk")
-call append("$", "isprint\tspecifies printable characters")
+call <SID>AddOption("isprint", gettext("specifies printable characters"))
call <SID>OptionG("isp", &isp)
if has("textobjects")
- call append("$", "quoteescape\tspecifies escape characters in a string")
- call append("$", "\t(local to buffer)")
+ call <SID>AddOption("quoteescape", gettext("specifies escape characters in a string"))
+ call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("qe")
endif
if has("rightleft")
- call append("$", "rightleft\tdisplay the buffer right-to-left")
- call append("$", "\t(local to window)")
+ call <SID>AddOption("rightleft", gettext("display the buffer right-to-left"))
+ call append("$", "\t" .. s:local_to_window)
call <SID>BinOptionL("rl")
- call append("$", "rightleftcmd\twhen to edit the command-line right-to-left")
- call append("$", "\t(local to window)")
+ call <SID>AddOption("rightleftcmd", gettext("when to edit the command-line right-to-left"))
+ call append("$", "\t" .. s:local_to_window)
call <SID>OptionL("rlc")
- call append("$", "revins\tinsert characters backwards")
+ call <SID>AddOption("revins", gettext("insert characters backwards"))
call <SID>BinOptionG("ri", &ri)
- call append("$", "allowrevins\tallow CTRL-_ in Insert and Command-line mode to toggle 'revins'")
+ call <SID>AddOption("allowrevins", gettext("allow CTRL-_ in Insert and Command-line mode to toggle 'revins'"))
call <SID>BinOptionG("ari", &ari)
- call append("$", "aleph\tthe ASCII code for the first letter of the Hebrew alphabet")
+ call <SID>AddOption("aleph", gettext("the ASCII code for the first letter of the Hebrew alphabet"))
call append("$", " \tset al=" . &al)
- call append("$", "hkmap\tuse Hebrew keyboard mapping")
+ call <SID>AddOption("hkmap", gettext("use Hebrew keyboard mapping"))
call <SID>BinOptionG("hk", &hk)
- call append("$", "hkmapp\tuse phonetic Hebrew keyboard mapping")
+ call <SID>AddOption("hkmapp", gettext("use phonetic Hebrew keyboard mapping"))
call <SID>BinOptionG("hkp", &hkp)
endif
if has("arabic")
- call append("$", "arabic\tprepare for editing Arabic text")
- call append("$", "\t(local to window)")
+ call <SID>AddOption("arabic", gettext("prepare for editing Arabic text"))
+ call append("$", "\t" .. s:local_to_window)
call <SID>BinOptionL("arab")
- call append("$", "arabicshape\tperform shaping of Arabic characters")
+ call <SID>AddOption("arabicshape", gettext("perform shaping of Arabic characters"))
call <SID>BinOptionG("arshape", &arshape)
- call append("$", "termbidi\tterminal will perform bidi handling")
+ call <SID>AddOption("termbidi", gettext("terminal will perform bidi handling"))
call <SID>BinOptionG("tbidi", &tbidi)
endif
if has("keymap")
- call append("$", "keymap\tname of a keyboard mapping")
+ call <SID>AddOption("keymap", gettext("name of a keyboard mapping"))
call <SID>OptionL("kmp")
endif
if has("langmap")
- call append("$", "langmap\tlist of characters that are translated in Normal mode")
+ call <SID>AddOption("langmap", gettext("list of characters that are translated in Normal mode"))
call <SID>OptionG("lmap", &lmap)
- call append("$", "langremap\tapply 'langmap' to mapped characters")
+ call <SID>AddOption("langremap", gettext("apply 'langmap' to mapped characters"))
call <SID>BinOptionG("lrm", &lrm)
endif
if has("xim")
- call append("$", "imdisable\twhen set never use IM; overrules following IM options")
+ call <SID>AddOption("imdisable", gettext("when set never use IM; overrules following IM options"))
call <SID>BinOptionG("imd", &imd)
endif
-call append("$", "iminsert\tin Insert mode: 1: use :lmap; 2: use IM; 0: neither")
-call append("$", "\t(local to window)")
+call <SID>AddOption("iminsert", gettext("in Insert mode: 1: use :lmap; 2: use IM; 0: neither"))
+call append("$", "\t" .. s:local_to_window)
call <SID>OptionL("imi")
-call append("$", "imsearch\tentering a search pattern: 1: use :lmap; 2: use IM; 0: neither")
-call append("$", "\t(local to window)")
+call <SID>AddOption("imsearch", gettext("entering a search pattern: 1: use :lmap; 2: use IM; 0: neither"))
+call append("$", "\t" .. s:local_to_window)
call <SID>OptionL("ims")
if has("xim")
- call append("$", "imcmdline\twhen set always use IM when starting to edit a command line")
+ call <SID>AddOption("imcmdline", gettext("when set always use IM when starting to edit a command line"))
call <SID>BinOptionG("imc", &imc)
- call append("$", "imstatusfunc\tfunction to obtain IME status")
+ call <SID>AddOption("imstatusfunc", gettext("function to obtain IME status"))
call <SID>OptionG("imsf", &imsf)
- call append("$", "imactivatefunc\tfunction to enable/disable IME")
+ call <SID>AddOption("imactivatefunc", gettext("function to enable/disable IME"))
call <SID>OptionG("imaf", &imaf)
endif
-call <SID>Header("multi-byte characters")
-call <SID>AddOption("encoding", "character encoding used in Nvim: \"utf-8\"")
+call <SID>Header(gettext("multi-byte characters"))
+call <SID>AddOption("encoding", gettext("character encoding used in Nvim: \"utf-8\""))
call <SID>OptionG("enc", &enc)
-call append("$", "fileencoding\tcharacter encoding for the current file")
+call <SID>AddOption("fileencoding", gettext("character encoding for the current file"))
call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("fenc")
-call append("$", "fileencodings\tautomatically detected character encodings")
+call <SID>AddOption("fileencodings", gettext("automatically detected character encodings"))
call <SID>OptionG("fencs", &fencs)
-call append("$", "charconvert\texpression used for character encoding conversion")
+call <SID>AddOption("charconvert", gettext("expression used for character encoding conversion"))
call <SID>OptionG("ccv", &ccv)
-call append("$", "delcombine\tdelete combining (composing) characters on their own")
+call <SID>AddOption("delcombine", gettext("delete combining (composing) characters on their own"))
call <SID>BinOptionG("deco", &deco)
-call append("$", "maxcombine\tmaximum number of combining (composing) characters displayed")
+call <SID>AddOption("maxcombine", gettext("maximum number of combining (composing) characters displayed"))
call <SID>OptionG("mco", &mco)
if has("xim") && has("gui_gtk")
- call append("$", "imactivatekey\tkey that activates the X input method")
+ call <SID>AddOption("imactivatekey", gettext("key that activates the X input method"))
call <SID>OptionG("imak", &imak)
endif
-call append("$", "ambiwidth\twidth of ambiguous width characters")
+call <SID>AddOption("ambiwidth", gettext("width of ambiguous width characters"))
call <SID>OptionG("ambw", &ambw)
-call append("$", "emoji\temoji characters are full width")
+call <SID>AddOption("emoji", gettext("emoji characters are full width"))
call <SID>BinOptionG("emo", &emo)
-call <SID>Header("various")
-call <SID>AddOption("virtualedit", "when to use virtual editing: \"block\", \"insert\", \"all\"\nand/or \"onemore\"")
+call <SID>Header(gettext("various"))
+call <SID>AddOption("virtualedit", gettext("when to use virtual editing: \"block\", \"insert\", \"all\"\nand/or \"onemore\""))
call <SID>OptionG("ve", &ve)
-call append("$", "eventignore\tlist of autocommand events which are to be ignored")
+call <SID>AddOption("eventignore", gettext("list of autocommand events which are to be ignored"))
call <SID>OptionG("ei", &ei)
-call append("$", "loadplugins\tload plugin scripts when starting up")
+call <SID>AddOption("loadplugins", gettext("load plugin scripts when starting up"))
call <SID>BinOptionG("lpl", &lpl)
-call append("$", "exrc\tenable reading .vimrc/.exrc/.gvimrc in the current directory")
+call <SID>AddOption("exrc", gettext("enable reading .vimrc/.exrc/.gvimrc in the current directory"))
call <SID>BinOptionG("ex", &ex)
-call append("$", "secure\tsafer working with script files in the current directory")
+call <SID>AddOption("secure", gettext("safer working with script files in the current directory"))
call <SID>BinOptionG("secure", &secure)
-call append("$", "gdefault\tuse the 'g' flag for \":substitute\"")
+call <SID>AddOption("gdefault", gettext("use the 'g' flag for \":substitute\""))
call <SID>BinOptionG("gd", &gd)
if exists("+opendevice")
- call append("$", "opendevice\tallow reading/writing devices")
+ call <SID>AddOption("opendevice", gettext("allow reading/writing devices"))
call <SID>BinOptionG("odev", &odev)
endif
if exists("+maxfuncdepth")
- call append("$", "maxfuncdepth\tmaximum depth of function calls")
+ call <SID>AddOption("maxfuncdepth", gettext("maximum depth of function calls"))
call append("$", " \tset mfd=" . &mfd)
endif
if has("mksession")
- call append("$", "sessionoptions\tlist of words that specifies what to put in a session file")
+ call <SID>AddOption("sessionoptions", gettext("list of words that specifies what to put in a session file"))
call <SID>OptionG("ssop", &ssop)
- call append("$", "viewoptions\tlist of words that specifies what to save for :mkview")
+ call <SID>AddOption("viewoptions", gettext("list of words that specifies what to save for :mkview"))
call <SID>OptionG("vop", &vop)
- call append("$", "viewdir\tdirectory where to store files with :mkview")
+ call <SID>AddOption("viewdir", gettext("directory where to store files with :mkview"))
call <SID>OptionG("vdir", &vdir)
endif
if has("shada")
- call append("$", "viminfo\tlist that specifies what to write in the ShaDa file")
+ call <SID>AddOption("viminfo", gettext("list that specifies what to write in the ShaDa file"))
call <SID>OptionG("vi", &vi)
endif
if has("quickfix")
- call append("$", "bufhidden\twhat happens with a buffer when it's no longer in a window")
+ call <SID>AddOption("bufhidden", gettext("what happens with a buffer when it's no longer in a window"))
call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("bh")
- call <SID>AddOption("buftype", "empty, \"nofile\", \"nowrite\", \"quickfix\", etc.: type of buffer")
+ call <SID>AddOption("buftype", gettext("empty, \"nofile\", \"nowrite\", \"quickfix\", etc.: type of buffer"))
call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("bt")
endif
-call append("$", "buflisted\twhether the buffer shows up in the buffer list")
-call append("$", "\t(local to buffer)")
+call <SID>AddOption("buflisted", gettext("whether the buffer shows up in the buffer list"))
+call append("$", "\t" .. s:local_to_buffer)
call <SID>BinOptionL("bl")
-call append("$", "debug\tset to \"msg\" to see all error messages")
+call <SID>AddOption("debug", gettext("set to \"msg\" to see all error messages"))
call append("$", " \tset debug=" . &debug)
-call append("$", "signcolumn\twhether to show the signcolumn")
-call append("$", "\t(local to window)")
+call <SID>AddOption("signcolumn", gettext("whether to show the signcolumn"))
+call append("$", "\t" .. s:local_to_window)
call <SID>OptionL("scl")
set cpo&vim
@@ -1297,13 +1260,13 @@ if has("syntax")
endif
endif
if exists("&mzschemedll")
- call append("$", "mzschemedll\tname of the Tcl dynamic library")
+ call <SID>AddOption("mzschemedll", gettext("name of the MzScheme dynamic library"))
call <SID>OptionG("mzschemedll", &mzschemedll)
- call append("$", "mzschemegcdll\tname of the Tcl GC dynamic library")
+ call <SID>AddOption("mzschemegcdll", gettext("name of the MzScheme GC dynamic library"))
call <SID>OptionG("mzschemegcdll", &mzschemegcdll)
endif
if has('pythonx')
- call append("$", "pyxversion\twhether to use Python 2 or 3")
+ call <SID>AddOption("pyxversion", gettext("whether to use Python 2 or 3"))
call append("$", " \tset pyx=" . &wd)
endif
@@ -1320,7 +1283,7 @@ augroup optwin
\ call <SID>unload() | delfun <SID>unload
augroup END
-fun! <SID>unload()
+func <SID>unload()
delfun <SID>CR
delfun <SID>Space
delfun <SID>Find
@@ -1331,7 +1294,7 @@ fun! <SID>unload()
delfun <SID>BinOptionG
delfun <SID>Header
au! optwin
-endfun
+endfunc
" Restore the previous value of 'title' and 'icon'.
let &title = s:old_title
diff --git a/runtime/pack/dist/opt/cfilter/plugin/cfilter.lua b/runtime/pack/dist/opt/cfilter/plugin/cfilter.lua
new file mode 100644
index 0000000000..20c158da65
--- /dev/null
+++ b/runtime/pack/dist/opt/cfilter/plugin/cfilter.lua
@@ -0,0 +1,114 @@
+----------------------------------------
+-- This file is generated via github.com/tjdevries/vim9jit
+-- For any bugs, please first consider reporting there.
+----------------------------------------
+
+-- Ignore "value assigned to a local variable is unused" because
+-- we can't guarantee that local variables will be used by plugins
+-- luacheck: ignore 311
+
+local vim9 = require('_vim9script')
+local M = {}
+local Qf_filter = nil
+-- vim9script
+
+-- # cfilter.vim: Plugin to filter entries from a quickfix/location list
+-- # Last Change: Jun 30, 2022
+-- # Maintainer: Yegappan Lakshmanan (yegappan AT yahoo DOT com)
+-- # Version: 2.0
+-- #
+-- # Commands to filter the quickfix list:
+-- # :Cfilter[!] /{pat}/
+-- # Create a new quickfix list from entries matching {pat} in the current
+-- # quickfix list. Both the file name and the text of the entries are
+-- # matched against {pat}. If ! is supplied, then entries not matching
+-- # {pat} are used. The pattern can be optionally enclosed using one of
+-- # the following characters: ', ", /. If the pattern is empty, then the
+-- # last used search pattern is used.
+-- # :Lfilter[!] /{pat}/
+-- # Same as :Cfilter but operates on the current location list.
+-- #
+
+Qf_filter = function(qf, searchpat, bang)
+ qf = vim9.bool(qf)
+ local Xgetlist = function() end
+ local Xsetlist = function() end
+ local cmd = ''
+ local firstchar = ''
+ local lastchar = ''
+ local pat = ''
+ local title = ''
+ local Cond = function() end
+ local items = {}
+
+ if vim9.bool(qf) then
+ Xgetlist = function(...)
+ return vim.fn['getqflist'](...)
+ end
+ Xsetlist = function(...)
+ return vim.fn['setqflist'](...)
+ end
+ cmd = ':Cfilter' .. bang
+ else
+ Xgetlist = function(...)
+ return vim9.fn_ref(M, 'getloclist', vim.deepcopy({ 0 }), ...)
+ end
+
+ Xsetlist = function(...)
+ return vim9.fn_ref(M, 'setloclist', vim.deepcopy({ 0 }), ...)
+ end
+
+ cmd = ':Lfilter' .. bang
+ end
+
+ firstchar = vim9.index(searchpat, 0)
+ lastchar = vim9.slice(searchpat, -1, nil)
+ if firstchar == lastchar and (firstchar == '/' or firstchar == '"' or firstchar == "'") then
+ pat = vim9.slice(searchpat, 1, -2)
+ if pat == '' then
+ -- # Use the last search pattern
+ pat = vim.fn.getreg('/')
+ end
+ else
+ pat = searchpat
+ end
+
+ if pat == '' then
+ return
+ end
+
+ if bang == '!' then
+ Cond = function(_, val)
+ return vim9.ops.NotRegexpMatches(val.text, pat)
+ and vim9.ops.NotRegexpMatches(vim9.fn.bufname(val.bufnr), pat)
+ end
+ else
+ Cond = function(_, val)
+ return vim9.ops.RegexpMatches(val.text, pat)
+ or vim9.ops.RegexpMatches(vim9.fn.bufname(val.bufnr), pat)
+ end
+ end
+
+ items = vim9.fn_mut('filter', { Xgetlist(), Cond }, { replace = 0 })
+ title = cmd .. ' /' .. pat .. '/'
+ Xsetlist({}, ' ', { ['title'] = title, ['items'] = items })
+end
+
+vim.api.nvim_create_user_command('Cfilter', function(__vim9_arg_1)
+ Qf_filter(true, __vim9_arg_1.args, (__vim9_arg_1.bang and '!' or ''))
+end, {
+ bang = true,
+ nargs = '+',
+ complete = nil,
+})
+
+vim.api.nvim_create_user_command('Lfilter', function(__vim9_arg_1)
+ Qf_filter(false, __vim9_arg_1.args, (__vim9_arg_1.bang and '!' or ''))
+end, {
+ bang = true,
+ nargs = '+',
+ complete = nil,
+})
+
+-- # vim: shiftwidth=2 sts=2 expandtab
+return M
diff --git a/runtime/pack/dist/opt/cfilter/plugin/cfilter.vim b/runtime/pack/dist/opt/cfilter/plugin/cfilter.vim
deleted file mode 100644
index fe4455fe2e..0000000000
--- a/runtime/pack/dist/opt/cfilter/plugin/cfilter.vim
+++ /dev/null
@@ -1,62 +0,0 @@
-" cfilter.vim: Plugin to filter entries from a quickfix/location list
-" Last Change: Aug 23, 2018
-" Maintainer: Yegappan Lakshmanan (yegappan AT yahoo DOT com)
-" Version: 1.1
-"
-" Commands to filter the quickfix list:
-" :Cfilter[!] /{pat}/
-" Create a new quickfix list from entries matching {pat} in the current
-" quickfix list. Both the file name and the text of the entries are
-" matched against {pat}. If ! is supplied, then entries not matching
-" {pat} are used. The pattern can be optionally enclosed using one of
-" the following characters: ', ", /. If the pattern is empty, then the
-" last used search pattern is used.
-" :Lfilter[!] /{pat}/
-" Same as :Cfilter but operates on the current location list.
-"
-if exists("loaded_cfilter")
- finish
-endif
-let loaded_cfilter = 1
-
-func s:Qf_filter(qf, searchpat, bang)
- if a:qf
- let Xgetlist = function('getqflist')
- let Xsetlist = function('setqflist')
- let cmd = ':Cfilter' . a:bang
- else
- let Xgetlist = function('getloclist', [0])
- let Xsetlist = function('setloclist', [0])
- let cmd = ':Lfilter' . a:bang
- endif
-
- let firstchar = a:searchpat[0]
- let lastchar = a:searchpat[-1:]
- if firstchar == lastchar &&
- \ (firstchar == '/' || firstchar == '"' || firstchar == "'")
- let pat = a:searchpat[1:-2]
- if pat == ''
- " Use the last search pattern
- let pat = @/
- endif
- else
- let pat = a:searchpat
- endif
-
- if pat == ''
- return
- endif
-
- if a:bang == '!'
- let cond = 'v:val.text !~# pat && bufname(v:val.bufnr) !~# pat'
- else
- let cond = 'v:val.text =~# pat || bufname(v:val.bufnr) =~# pat'
- endif
-
- let items = filter(Xgetlist(), cond)
- let title = cmd . ' /' . pat . '/'
- call Xsetlist([], ' ', {'title' : title, 'items' : items})
-endfunc
-
-com! -nargs=+ -bang Cfilter call s:Qf_filter(1, <q-args>, <q-bang>)
-com! -nargs=+ -bang Lfilter call s:Qf_filter(0, <q-args>, <q-bang>)
diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
index 802ebd42b5..99fd7dba42 100644
--- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
+++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
@@ -2,7 +2,7 @@
"
" Author: Bram Moolenaar
" Copyright: Vim license applies, see ":help license"
-" Last Change: 2022 Jun 24
+" Last Change: 2022 Nov 10
"
" WORK IN PROGRESS - The basics works stable, more to come
" Note: In general you need at least GDB 7.12 because this provides the
@@ -154,10 +154,16 @@ func s:StartDebug_internal(dict)
let s:save_columns = 0
let s:allleft = 0
- if exists('g:termdebug_wide')
- if &columns < g:termdebug_wide
+ let wide = 0
+ if exists('g:termdebug_config')
+ let wide = get(g:termdebug_config, 'wide', 0)
+ elseif exists('g:termdebug_wide')
+ let wide = g:termdebug_wide
+ endif
+ if wide > 0
+ if &columns < wide
let s:save_columns = &columns
- let &columns = g:termdebug_wide
+ let &columns = wide
" If we make the Vim window wider, use the whole left half for the debug
" windows.
let s:allleft = 1
@@ -168,7 +174,12 @@ func s:StartDebug_internal(dict)
endif
" Override using a terminal window by setting g:termdebug_use_prompt to 1.
- let use_prompt = exists('g:termdebug_use_prompt') && g:termdebug_use_prompt
+ let use_prompt = 0
+ if exists('g:termdebug_config')
+ let use_prompt = get(g:termdebug_config, 'use_prompt', 0)
+ elseif exists('g:termdebug_use_prompt')
+ let use_prompt = g:termdebug_use_prompt
+ endif
if !has('win32') && !use_prompt
let s:way = 'terminal'
else
@@ -903,7 +914,14 @@ func s:InstallCommands()
endif
if has('menu') && &mouse != ''
- call s:InstallWinbar()
+ " install the window toolbar by default, can be disabled in the config
+ let winbar = 1
+ if exists('g:termdebug_config')
+ let winbar = get(g:termdebug_config, 'winbar', 1)
+ endif
+ if winbar
+ call s:InstallWinbar()
+ endif
let popup = 1
if exists('g:termdebug_config')
@@ -964,14 +982,7 @@ func s:DeleteCommands()
nunmap K
else
" 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,
- \ 'noremap': s:k_map_saved.noremap ? v:true : v:false,
- \ 'nowait': s:k_map_saved.nowait ? v:true : v:false,
- \ 'script': s:k_map_saved.script ? v:true : v:false,
- \ 'silent': s:k_map_saved.silent ? v:true : v:false,
- \ })
+ call mapset('n', 0, s:k_map_saved)
endif
unlet s:k_map_saved
endif
diff --git a/runtime/pack/dist/opt/vimball/doc/vimball.txt b/runtime/pack/dist/opt/vimball/doc/vimball.txt
index 9965a216e4..602fe85954 100644
--- a/runtime/pack/dist/opt/vimball/doc/vimball.txt
+++ b/runtime/pack/dist/opt/vimball/doc/vimball.txt
@@ -88,7 +88,7 @@ MAKING A VIMBALL *:MkVimball*
If you wish to force slashes into the filename, that can also be done
by using the exclamation mark (ie. :MkVimball! path/filename).
- The tip at http://vim.wikia.com/wiki/Using_VimBall_with_%27Make%27
+ The tip at https://vim.wikia.com/wiki/Using_VimBall_with_%27Make%27
has a good idea on how to automate the production of vimballs using
make.
@@ -171,12 +171,12 @@ WINDOWS *vimball-windows*
>
Item Tool/Suite Free Website
---- ---------- ---- -------
- 7zip tool y http://www.7-zip.org/
- Winzip tool n http://www.winzip.com/downwz.htm
- unxutils suite y http://unxutils.sourceforge.net/
- cygwin suite y http://www.cygwin.com/
- GnuWin32 suite y http://gnuwin32.sourceforge.net/
- MinGW suite y http://www.mingw.org/
+ 7zip tool y https://www.7-zip.org/
+ Winzip tool n https://www.winzip.com/downwz.htm
+ unxutils suite y https://unxutils.sourceforge.net/
+ cygwin suite y https://www.cygwin.com/
+ GnuWin32 suite y https://gnuwin32.sourceforge.net/
+ MinGW suite y https://www.mingw.org/
<
==============================================================================
diff --git a/runtime/plugin/editorconfig.lua b/runtime/plugin/editorconfig.lua
new file mode 100644
index 0000000000..54cd0e828e
--- /dev/null
+++ b/runtime/plugin/editorconfig.lua
@@ -0,0 +1,13 @@
+local group = vim.api.nvim_create_augroup('editorconfig', {})
+vim.api.nvim_create_autocmd({ 'BufNewFile', 'BufRead', 'BufFilePost' }, {
+ group = group,
+ callback = function(args)
+ -- Buffer-local enable has higher priority
+ local enable = vim.F.if_nil(vim.b.editorconfig, vim.F.if_nil(vim.g.editorconfig, true))
+ if not enable then
+ return
+ end
+
+ require('editorconfig').config(args.buf)
+ end,
+})
diff --git a/runtime/plugin/man.lua b/runtime/plugin/man.lua
new file mode 100644
index 0000000000..4b1528b0cb
--- /dev/null
+++ b/runtime/plugin/man.lua
@@ -0,0 +1,34 @@
+if vim.g.loaded_man ~= nil then
+ return
+end
+vim.g.loaded_man = true
+
+vim.api.nvim_create_user_command('Man', function(params)
+ local man = require('man')
+ if params.bang then
+ man.init_pager()
+ else
+ local ok, err = pcall(man.open_page, params.count, params.smods, params.fargs)
+ if not ok then
+ vim.notify(man.errormsg or err, vim.log.levels.ERROR)
+ end
+ end
+end, {
+ bang = true,
+ bar = true,
+ addr = 'other',
+ nargs = '*',
+ complete = function(...)
+ return require('man').man_complete(...)
+ end,
+})
+
+local augroup = vim.api.nvim_create_augroup('man', {})
+
+vim.api.nvim_create_autocmd('BufReadCmd', {
+ group = augroup,
+ pattern = 'man://*',
+ callback = function(params)
+ require('man').read_page(vim.fn.matchstr(params.match, 'man://\\zs.*'))
+ end,
+})
diff --git a/runtime/plugin/man.vim b/runtime/plugin/man.vim
deleted file mode 100644
index b10677593f..0000000000
--- a/runtime/plugin/man.vim
+++ /dev/null
@@ -1,15 +0,0 @@
-" Maintainer: Anmol Sethi <hi@nhooyr.io>
-
-if exists('g:loaded_man')
- finish
-endif
-let g:loaded_man = 1
-
-command! -bang -bar -addr=other -complete=customlist,man#complete -nargs=* Man
- \ if <bang>0 | call man#init_pager() |
- \ else | call man#open_page(<count>, <q-mods>, <f-args>) | endif
-
-augroup man
- autocmd!
- autocmd BufReadCmd man://* call man#read_page(matchstr(expand('<amatch>'), 'man://\zs.*'))
-augroup END
diff --git a/runtime/plugin/matchparen.vim b/runtime/plugin/matchparen.vim
index cc4f38f669..3982489b92 100644
--- a/runtime/plugin/matchparen.vim
+++ b/runtime/plugin/matchparen.vim
@@ -1,12 +1,11 @@
" Vim plugin for showing matching parens
" Maintainer: Bram Moolenaar <Bram@vim.org>
-" Last Change: 2021 Apr 08
+" Last Change: 2022 Dec 01
" Exit quickly when:
" - this plugin was already loaded (or disabled)
" - when 'compatible' is set
-" - the "CursorMoved" autocmd event is not available.
-if exists("g:loaded_matchparen") || &cp || !exists("##CursorMoved")
+if exists("g:loaded_matchparen") || &cp
finish
endif
let g:loaded_matchparen = 1
@@ -20,8 +19,8 @@ endif
augroup matchparen
" Replace all matchparen autocommands
- autocmd! CursorMoved,CursorMovedI,WinEnter * call s:Highlight_Matching_Pair()
- autocmd! WinLeave * call s:Remove_Matches()
+ autocmd! CursorMoved,CursorMovedI,WinEnter,BufWinEnter,WinScrolled * call s:Highlight_Matching_Pair()
+ autocmd! WinLeave,BufLeave * call s:Remove_Matches()
if exists('##TextChanged')
autocmd! TextChanged,TextChangedI * call s:Highlight_Matching_Pair()
endif
diff --git a/runtime/plugin/nvim.lua b/runtime/plugin/nvim.lua
new file mode 100644
index 0000000000..815886f896
--- /dev/null
+++ b/runtime/plugin/nvim.lua
@@ -0,0 +1,7 @@
+vim.api.nvim_create_user_command('Inspect', function(cmd)
+ if cmd.bang then
+ vim.pretty_print(vim.inspect_pos())
+ else
+ vim.show_pos()
+ end
+end, { desc = 'Inspect highlights and extmarks at the cursor', bang = true })
diff --git a/runtime/print/ascii.ps b/runtime/print/ascii.ps
deleted file mode 100644
index 5fcffb655f..0000000000
--- a/runtime/print/ascii.ps
+++ /dev/null
@@ -1,22 +0,0 @@
-%!PS-Adobe-3.0 Resource-Encoding
-%%Title: VIM-ascii
-%%Version: 1.0 0
-%%EndComments
-/VIM-ascii[
-32{/.notdef}repeat
-/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
-/parenleft /parenright /asterisk /plus /comma /minus /period /slash
-/zero /one /two /three /four /five /six /seven
-/eight /nine /colon /semicolon /less /equal /greater /question
-/at /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 /bracketleft /backslash /bracketright /asciicircum /underscore
-/grave /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 /braceleft /bar /braceright /asciitilde /.notdef
-128{/.notdef}repeat]
-/Encoding defineresource pop
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/cidfont.ps b/runtime/print/cidfont.ps
deleted file mode 100644
index a06ebc8c4c..0000000000
--- a/runtime/print/cidfont.ps
+++ /dev/null
@@ -1,26 +0,0 @@
-%!PS-Adobe-3.0 Resource-ProcSet
-%%Title: VIM-CIDFont
-%%Version: 1.0 0
-%%EndComments
-% Editing of this file is NOT RECOMMENDED. You run a very good risk of causing
-% all PostScript printing from VIM failing if you do. PostScript is not called
-% a write-only language for nothing!
-/CP currentpacking d T setpacking
-/SB 256 string d
-/CIDN? systemdict/composefont known d /GS? systemdict/.makeoperator known d
-CIDN?{
-GS?{/vim_findresource{2 copy resourcestatus not{1 index SB cvs runlibfile}{
-pop pop}ifelse findresource}bd/vim_composefont{0 get/CIDFont vim_findresource
-exch/CMap vim_findresource exch[exch]composefont pop}bd}{/vim_findresource
-/findresource ld/vim_composefont{composefont pop}bd}ifelse
-}{
-/vim_fontname{0 get SB cvs length dup SB exch(-)putinterval 1 add dup SB exch
-dup 256 exch sub getinterval 3 -1 roll exch cvs length add SB exch 0 exch
-getinterval cvn}bd/vim_composefont{vim_fontname findfont d}bd
-} ifelse
-/cfs{exch scalefont d}bd
-/sffs{findfont 3 1 roll 1 index mul exch 2 index/FontMatrix get matrix copy
-scale makefont d}bd
-CP setpacking
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/cns_roman.ps b/runtime/print/cns_roman.ps
deleted file mode 100644
index dba385cae0..0000000000
--- a/runtime/print/cns_roman.ps
+++ /dev/null
@@ -1,23 +0,0 @@
-%!PS-Adobe-3.0 Resource-Encoding
-%%Title: VIM-cns_roman
-%%Version: 1.0 0
-%%EndComments
-% Different to ASCII at code point 126
-/VIM-cns_roman[
-32{/.notdef}repeat
-/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
-/parenleft /parenright /asterisk /plus /comma /minus /period /slash
-/zero /one /two /three /four /five /six /seven
-/eight /nine /colon /semicolon /less /equal /greater /question
-/at /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 /bracketleft /backslash /bracketright /asciicircum /underscore
-/grave /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 /braceleft /bar /braceright /overline /.notdef
-128{/.notdef}repeat]
-/Encoding defineresource pop
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/cp1250.ps b/runtime/print/cp1250.ps
deleted file mode 100644
index 9e733cc760..0000000000
--- a/runtime/print/cp1250.ps
+++ /dev/null
@@ -1,40 +0,0 @@
-%!PS-Adobe-3.0 Resource-Encoding
-%%Title: VIM-cp1250
-%%Version: 1.0 0
-%%EndComments
-/VIM-cp1250[
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
-/parenleft /parenright /asterisk /plus /comma /minus /period /slash
-/zero /one /two /three /four /five /six /seven
-/eight /nine /colon /semicolon /less /equal /greater /question
-/at /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 /bracketleft /backslash /bracketright /asciicircum /underscore
-/grave /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 /braceleft /bar /braceright /tilde /.notdef
-/Euro /.notdef /quotesinglbase /.notdef /quotedblbase /ellipsis /dagger /daggerdbl
-/.notdef /perthousand /Scaron /guilsinglleft /Sacute /Tcaron /Zcaron /Zacute
-/.notdef /quoteleft /quoteright /quotedblleft /quotedblright /bullet /endash /emdash
-/.notdef /trademark /scaron /guilsinglright /sacute /tcaron /zcaron /zacute
-/space /caron /breve /Lslash /currency /Aogonek /brokenbar /section
-/dieresis /copyright /Scedilla /guillemotleft /logicalnot /hyphen /registered /Zdotaccent
-/degree /plusminus /ogonek /lslash /acute /mu /paragraph /periodcentered
-/cedilla /aogonek /scedilla /guillemotright /Lcaron /hungarumlaut /lcaron /zdotaccent
-/Racute /Aacute /Acircumflex /Abreve /Adieresis /Lacute /Cacute /Ccedilla
-/Ccaron /Eacute /Eogonek /Edieresis /Ecaron /Iacute /Icircumflex /Dcaron
-/Dcroat /Nacute /Ncaron /Oacute /Ocircumflex /Ohungarumlaut /Odieresis /multiply
-/Rcaron /Uring /Uacute /Uhungarumlaut /Udieresis /Yacute /Tcedilla /germandbls
-/racute /aacute /acircumflex /abreve /adieresis /lacute /cacute /ccedilla
-/ccaron /eacute /eogonek /edieresis /ecaron /iacute /icircumflex /dcaron
-/dcroat /nacute /ncaron /oacute /ocircumflex /ohungarumlaut /odieresis /divide
-/rcaron /uring /uacute /uhungarumlaut /udieresis /yacute /tcedilla /dotaccent]
-/Encoding defineresource pop
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/cp1251.ps b/runtime/print/cp1251.ps
deleted file mode 100644
index 7137504e7a..0000000000
--- a/runtime/print/cp1251.ps
+++ /dev/null
@@ -1,40 +0,0 @@
-%!PS-Adobe-3.0 Resource-Encoding
-%%Title: VIM-cp1251
-%%Version: 1.0 0
-%%EndComments
-/VIM-cp1251[
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
-/parenleft /parenright /asterisk /plus /comma /minus /period /slash
-/zero /one /two /three /four /five /six /seven
-/eight /nine /colon /semicolon /less /equal /greater /question
-/at /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 /bracketleft /backslash /bracketright /asciicircum /underscore
-/grave /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 /braceleft /bar /braceright /asciitilde /.notdef
-/afii10051 /afii10052 /quotesinglbase /afii10100 /quotedblbase /ellipsis /dagger /daggerdbl
-/Euro /perthousand /afii10058 /guilsinglleft /afii10059 /afii10061 /afii10060 /afii10145
-/afii10099 /quoteleft /quoteright /quotedblleft /quotedblright /bullet /endash /emdash
-/.notdef /trademark /afii10106 /guilsinglright /afii10107 /afii10109 /afii10108 /afii10193
-/space /afii10062 /afii10110 /afii10057 /currency /afii10050 /brokenbar /section
-/afii10023 /copyright /afii10053 /guillemotleft /logicalnot /hyphen /registered /afii10056
-/degree /plusminus /afii10055 /afii10103 /afii10098 /mu /paragraph /periodcentered
-/afii10071 /afii61352 /afii10101 /guillemotright /afii10105 /afii10054 /afii10102 /afii10104
-/afii10017 /afii10018 /afii10019 /afii10020 /afii10021 /afii10022 /afii10024 /afii10025
-/afii10026 /afii10027 /afii10028 /afii10029 /afii10030 /afii10031 /afii10032 /afii10033
-/afii10034 /afii10035 /afii10036 /afii10037 /afii10038 /afii10039 /afii10040 /afii10041
-/afii10042 /afii10043 /afii10044 /afii10045 /afii10046 /afii10047 /afii10048 /afii10049
-/afii10065 /afii10066 /afii10067 /afii10068 /afii10069 /afii10070 /afii10072 /afii10073
-/afii10074 /afii10075 /afii10076 /afii10077 /afii10078 /afii10079 /afii10080 /afii10081
-/afii10082 /afii10083 /afii10084 /afii10085 /afii10086 /afii10087 /afii10088 /afii10089
-/afii10090 /afii10091 /afii10092 /afii10093 /afii10094 /afii10095 /afii10096 /afii10097]
-/Encoding defineresource pop
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/cp1252.ps b/runtime/print/cp1252.ps
deleted file mode 100644
index a4dd7e675f..0000000000
--- a/runtime/print/cp1252.ps
+++ /dev/null
@@ -1,40 +0,0 @@
-%!PS-Adobe-3.0 Resource-Encoding
-%%Title: VIM-cp1252
-%%Version: 1.0 0
-%%EndComments
-/VIM-cp1252[
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
-/parenleft /parenright /asterisk /plus /comma /minus /period /slash
-/zero /one /two /three /four /five /six /seven
-/eight /nine /colon /semicolon /less /equal /greater /question
-/at /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 /bracketleft /backslash /bracketright /asciicircum /underscore
-/grave /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 /braceleft /bar /braceright /asciitilde /.notdef
-/Euro /.notdef /quotesinglbase /florin /quotedblbase /ellipsis /dagger /daggerdbl
-/circumflex /perthousand /Scaron /guilsinglleft /OE /.notdef /Zcaron /.notdef
-/.notdef /quoteleft /quoteright /quotedblleft /quotedblright /bullet /endash /emdash
-/tilde /trademark /scaron /guilsinglright /oe /.notdef /zcaron /Ydieresis
-/space /exclamdown /cent /sterling /currency /yen /brokenbar /section
-/dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron
-/degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered
-/cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown
-/Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla
-/Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis
-/Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply
-/Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls
-/agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla
-/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis
-/eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide
-/oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis]
-/Encoding defineresource pop
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/cp1253.ps b/runtime/print/cp1253.ps
deleted file mode 100644
index 0482232af1..0000000000
--- a/runtime/print/cp1253.ps
+++ /dev/null
@@ -1,40 +0,0 @@
-%!PS-Adobe-3.0 Resource-Encoding
-%%Title: VIM-cp1253
-%%Version: 1.0 0
-%%EndComments
-/VIM-cp1253[
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
-/parenleft /parenright /asterisk /plus /comma /minus /period /slash
-/zero /one /two /three /four /five /six /seven
-/eight /nine /colon /semicolon /less /equal /greater /question
-/at /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 /bracketleft /backslash /bracketright /asciicircum /underscore
-/grave /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 /braceleft /bar /braceright /asciitilde /.notdef
-/.notdef /.notdef /quotesinglbase /florin /quotedblbase /ellipsis /dagger /daggerdbl
-/.notdef /perthousand /.notdef /guilsinglleft /.notdef /.notdef /.notdef /.notdef
-/.notdef /quoteleft /quoteright /quotedblleft /quotedblright /bullet /endash /emdash
-/.notdef /trademark /.notdef /guilsinglright /.notdef /.notdef /.notdef /.notdef
-/space /dieresistonos /Alphatonos /sterling /currency /yen /brokenbar /section
-/dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /emdash
-/degree /plusminus /twosuperior /threesuperior /tonos /mu /paragraph /periodcentered
-/Epsilontonos /Etatonos /Iotatonos /guillemotright /Omicrontonos /onehalf /Upsilontonos /Omegatonos
-/iotadieresistonos /Alpha /Beta /Gamma /Delta /Epsilon /Zeta /Eta
-/Theta /Iota /Kappa /Lambda /Mu /Nu /Xi /Omicron
-/Pi /Rho /.notdef /Sigma /Tau /Upsilon /Phi /Chi
-/Psi /Omega /Iotadieresis /Upsilondieresis /alphatonos /epsilontonos /etatonos /iotatonos
-/upsilondieresistonos /alpha /beta /gamma /delta /epsilon /zeta /eta
-/theta /iota /kappa /lambda /mu /nu /xi /omicron
-/pi /rho /sigma1 /sigma /tau /upsilon /phi /chi
-/psi /omega /iotadieresis /upsilondieresis /omicrontonos /upsilontonos /omegatonos /.notdef]
-/Encoding defineresource pop
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/cp1254.ps b/runtime/print/cp1254.ps
deleted file mode 100644
index 9fe7e47710..0000000000
--- a/runtime/print/cp1254.ps
+++ /dev/null
@@ -1,40 +0,0 @@
-%!PS-Adobe-3.0 Resource-Encoding
-%%Title: VIM-cp1254
-%%Version: 1.0 0
-%%EndComments
-/VIM-cp1254[
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
-/parenleft /parenright /asterisk /plus /comma /minus /period /slash
-/zero /one /two /three /four /five /six /seven
-/eight /nine /colon /semicolon /less /equal /greater /question
-/at /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 /bracketleft /backslash /bracketright /asciicircum /underscore
-/grave /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 /braceleft /bar /braceright /asciitilde /.notdef
-/Euro /.notdef /quotesinglbase /florin /quotedblbase /ellipsis /dagger /daggerdbl
-/circumflex /perthousand /Scaron /guilsinglleft /OE /.notdef /Zcaron /.notdef
-/.notdef /quoteleft /quoteright /quotedblleft /quotedblright /bullet /endash /emdash
-/tilde /trademark /scaron /guilsinglright /oe /.notdef /zcaron /Ydieresis
-/space /exclamdown /cent /sterling /currency /yen /brokenbar /section
-/dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron
-/degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered
-/cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown
-/Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla
-/Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis
-/Gbreve /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply
-/Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Idotaccent /Scedilla /germandbls
-/agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla
-/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis
-/gbreve /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide
-/oslash /ugrave /uacute /ucircumflex /udieresis /dotlessi /scedilla /ydieresis]
-/Encoding defineresource pop
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/cp1255.ps b/runtime/print/cp1255.ps
deleted file mode 100644
index cd82f46a0e..0000000000
--- a/runtime/print/cp1255.ps
+++ /dev/null
@@ -1,40 +0,0 @@
-%!PS-Adobe-3.0 Resource-Encoding
-%%Title: VIM-cp1255
-%%Version: 1.0 0
-%%EndComments
-/VIM-cp1255[
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
-/parenleft /parenright /asterisk /plus /comma /minus /period /slash
-/zero /one /two /three /four /five /six /seven
-/eight /nine /colon /semicolon /less /equal /greater /question
-/at /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 /bracketleft /backslash /bracketright /asciicircum /underscore
-/grave /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 /braceleft /bar /braceright /asciitilde /.notdef
-/.notdef /.notdef /quotesinglbase /florin /quotedblbase /ellipsis /dagger /daggerdbl
-/circumflex /perthousand /.notdef /guilsinglleft /.notdef /.notdef /.notdef /.notdef
-/.notdef /quoteleft /quoteright /quotedblleft /quotedblright /bullet /endash /emdash
-/tilde /trademark /.notdef /guilsinglright /.notdef /.notdef /.notdef /.notdef
-/space /.notdef /cent /sterling /newsheqelsign /yen /brokenbar /section
-/dieresis /copyright /.notdef /guillemotleft /logicalnot /hyphen /registered /macron
-/degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered
-/.notdef /onesuperior /.notdef /guillemotright /onequarter /onehalf /threequarters /.notdef
-/sheva /hatafsegol /hatafpatah /hatafqamats /hiriq /tsere /segol /patah
-/qamats /holam /.notdef /qubuts /dagesh /meteg /maqaf /rafe
-/paseq /shindot /sindot /sofpasuq /doublevav /vavyod /doubleyod /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/alef /bet /gimel /dalet /he /vav /zayin /het
-/tet /yod /finalkaf /kaf /lamed /finalmem /mem /finalnun
-/nun /samekh /ayin /finalpe /pe /finaltsadi /tsadi /qof
-/resh /shin /tav /.notdef /.notdef /.notdef /.notdef /.notdef]
-/Encoding defineresource pop
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/cp1257.ps b/runtime/print/cp1257.ps
deleted file mode 100644
index 2e5d7a7b2d..0000000000
--- a/runtime/print/cp1257.ps
+++ /dev/null
@@ -1,40 +0,0 @@
-%!PS-Adobe-3.0 Resource-Encoding
-%%Title: VIM-cp1257
-%%Version: 1.0 0
-%%EndComments
-/VIM-cp1257[
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
-/parenleft /parenright /asterisk /plus /comma /minus /period /slash
-/zero /one /two /three /four /five /six /seven
-/eight /nine /colon /semicolon /less /equal /greater /question
-/at /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 /bracketleft /backslash /bracketright /asciicircum /underscore
-/grave /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 /braceleft /bar /braceright /asciitilde /.notdef
-/.notdef /.notdef /quotesinglbase /.notdef /quotedblbase /ellipsis /dagger /daggerdbl
-/.notdef /perthousand /.notdef /guilsinglleft /.notdef /.notdef /.notdef /.notdef
-/.notdef /quoteleft /quoteright /quotedblleft /quotedblright /bullet /endash /emdash
-/.notdef /trademark /.notdef /guilsinglright /.notdef /.notdef /.notdef /.notdef
-/space /caron /breve /sterling /currency /.notdef /brokenbar /section
-/dieresis /copyright /Rcedilla /guillemotleft /logicalnot /hyphen /registered /AE
-/degree /plusminus /ogonek /threesuperior /acute /mu /paragraph /periodcentered
-/cedilla /onesuperior /rcedilla /guillemotright /onequarter /onehalf /threequarters /ae
-/Aogonek /Iogonek /Amacron /Cacute /Adieresis /Aring /Eogonek /Emacron
-/Ccaron /Eacute /Zacute /Edot /Gcedilla /Kcedilla /Imacron /Lcedilla
-/Scaron /Nacute /Ncedilla /Oacute /Omacron /Otilde /Odieresis /multiply
-/Uogonek /Lslash /Sacute /Umacron /Udieresis /Zdotaccent /Zcaron /germandbls
-/aogonek /iogonek /amacron /cacute /adieresis /aring /eogonek /emacron
-/ccaron /eacute /zacute /edot /gcedilla /kcedilla /imacron /lcedilla
-/scaron /nacute /ncedilla /oacute /omacron /otilde /odieresis /divide
-/uogonek /lslash /sacute /umacron /udieresis /zdotaccent /zcaron /dotaccent]
-/Encoding defineresource pop
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/gb_roman.ps b/runtime/print/gb_roman.ps
deleted file mode 100644
index fa78dbf5d6..0000000000
--- a/runtime/print/gb_roman.ps
+++ /dev/null
@@ -1,23 +0,0 @@
-%!PS-Adobe-3.0 Resource-Encoding
-%%Title: VIM-gb_roman
-%%Version: 1.0 0
-%%EndComments
-% Different to ASCII at code points 36 and 126
-/VIM-gb_roman[
-32{/.notdef}repeat
-/space /exclam /quotedbl /numbersign /yuan /percent /ampersand /quotesingle
-/parenleft /parenright /asterisk /plus /comma /minus /period /slash
-/zero /one /two /three /four /five /six /seven
-/eight /nine /colon /semicolon /less /equal /greater /question
-/at /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 /bracketleft /backslash /bracketright /asciicircum /underscore
-/grave /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 /braceleft /bar /braceright /overline /.notdef
-128{/.notdef}repeat]
-/Encoding defineresource pop
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/hp-roman8.ps b/runtime/print/hp-roman8.ps
deleted file mode 100644
index d71b876db1..0000000000
--- a/runtime/print/hp-roman8.ps
+++ /dev/null
@@ -1,40 +0,0 @@
-%!PS-Adobe-3.0 Resource-Encoding
-%%Title: VIM-hp-roman8
-%%Version: 1.0 0
-%%EndComments
-/VIM-hp-roman8[
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
-/parenleft /parenright /asterisk /plus /comma /minus /period /slash
-/zero /one /two /three /four /five /six /seven
-/eight /nine /colon /semicolon /less /equal /greater /question
-/at /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 /bracketleft /backslash /bracketright /asciicircum /underscore
-/grave /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 /braceleft /bar /braceright /asciitilde /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /Agrave /Acircumflex /Egrave /Ecircumflex /Edieresis /Icircumflex /Idieresis
-/acute /grave /circumflex /dieresis /tilde /Ugrave /Ucircumflex /lira
-/macron /Yacute /yacute /degree /Ccedilla /ccedilla /Ntilde /ntilde
-/exclamdown /questiondown /currency /sterling /yen /section /florin /cent
-/acircumflex /ecircumflex /ocircumflex /ucircumflex /aacute /eacute /oacute /uacute
-/agrave /egrave /ograve /ugrave /adieresis /edieresis /odieresis /udieresis
-/Aring /icircumflex /Oslash /AE /aring /iacute /oslash /ae
-/Adieresis /igrave /Odieresis /Udieresis /Eacute /idieresis /germandbls /Ocircumflex
-/Aacute /Atilde /atilde /Eth /eth /Iacute /Igrave /Oacute
-/Ograve /Otilde /otilde /Scaron /scaron /Uacute /Ydieresis /ydieresis
-/Thorn /thorn /periodcentered /mu /paragraph /threequarters /hyphen /onequarter
-/onehalf /ordfeminine /ordmasculine /guillemotleft /filledbox /guillemotright /plusminus /.notdef]
-/Encoding defineresource pop
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/iso-8859-10.ps b/runtime/print/iso-8859-10.ps
deleted file mode 100644
index 7d8e2a0f96..0000000000
--- a/runtime/print/iso-8859-10.ps
+++ /dev/null
@@ -1,40 +0,0 @@
-%!PS-Adobe-3.0 Resource-Encoding
-%%Title: VIM-iso-8859-10
-%%Version: 1.0 0
-%%EndComments
-/VIM-iso-8859-10[
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
-/parenleft /parenright /asterisk /plus /comma /hyphen /period /slash
-/zero /one /two /three /four /five /six /seven
-/eight /nine /colon /semicolon /less /equal /greater /question
-/at /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 /bracketleft /backslash /bracketright /asciicircum /underscore
-/grave /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 /braceleft /bar /braceright /asciitilde /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /Aogonek /Emacron /Gcedilla /Imacron /Itilde /Kcedilla /section
-/Lcedilla /Dcroat /Scaron /Tbar /Zcaron /endash /Umacron /Eng
-/degree /aogonek /emacron /gcedilla /imacron /itilde /kcedilla /periodcentered
-/lcedilla /dcroat /scaron /tbar /zcaron /emdash /umacron /eng
-/Amacron /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Iogonek
-/Ccaron /Eacute /Eogonek /Edieresis /Edot /Iacute /Icircumflex /Idieresis
-/Eth /Ncedilla /Omacron /Oacute /Ocircumflex /Otilde /Odieresis /Utilde
-/Oslash /Uogonek /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls
-/amacron /aacute /acircumflex /atilde /adieresis /aring /ae /iogonek
-/ccaron /eacute /eogonek /edieresis /edot /iacute /icircumflex /idieresis
-/eth /ncedilla /omacron /oacute /ocircumflex /otilde /odieresis /utilde
-/oslash /uogonek /uacute /ucircumflex /udieresis /yacute /thorn /kgreenlandic]
-/Encoding defineresource pop
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/iso-8859-11.ps b/runtime/print/iso-8859-11.ps
deleted file mode 100644
index 78f775befc..0000000000
--- a/runtime/print/iso-8859-11.ps
+++ /dev/null
@@ -1,40 +0,0 @@
-%!PS-Adobe-3.0 Resource-Encoding
-%%Title: VIM-iso-8859-11
-%%Version: 1.0 0
-%%EndComments
-/VIM-iso-8859-11[
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
-/parenleft /parenright /asterisk /plus /comma /minus /period /slash
-/zero /one /two /three /four /five /six /seven
-/eight /nine /colon /semicolon /less /equal /greater /question
-/at /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 /bracketleft /backslash /bracketright /asciicircum /underscore
-/grave /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 /braceleft /bar /braceright /asciitilde /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /uni0E01 /uni0E02 /uni0E03 /uni0E04 /uni0E05 /uni0E06 /uni0E07
-/uni0E08 /uni0E09 /uni0E0A /uni0E0B /uni0E0C /uni0E0D /uni0E0E /uni0E0F
-/uni0E10 /uni0E11 /uni0E12 /uni0E13 /uni0E14 /uni0E15 /uni0E16 /uni0E17
-/uni0E18 /uni0E19 /uni0E1A /uni0E1B /uni0E1C /uni0E1D /uni0E1E /uni0E1F
-/uni0E20 /uni0E21 /uni0E22 /uni0E23 /uni0E24 /uni0E25 /uni0E26 /uni0E27
-/uni0E28 /uni0E29 /uni0E2A /uni0E2B /uni0E2C /uni0E2D /uni0E2E /uni0E2F
-/uni0E30 /uni0E31 /uni0E32 /uni0E33 /uni0E34 /uni0E35 /uni0E36 /uni0E37
-/uni0E38 /uni0E39 /uni0E3A /.notdef /space /.notdef /.notdef /uni0E3F
-/uni0E40 /uni0E41 /uni0E42 /uni0E43 /uni0E44 /uni0E45 /uni0E46 /uni0E47
-/uni0E48 /uni0E49 /uni0E4A /uni0E4B /uni0E4C /uni0E4D /uni0E4E /uni0E4F
-/uni0E50 /uni0E51 /uni0E52 /uni0E53 /uni0E54 /uni0E55 /uni0E56 /uni0E57
- /uni0E58 /uni0E59 /uni0E5A /.notdef /.notdef /.notdef /.notdef /.notdef]
-/Encoding defineresource pop
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/iso-8859-13.ps b/runtime/print/iso-8859-13.ps
deleted file mode 100644
index b4348f6619..0000000000
--- a/runtime/print/iso-8859-13.ps
+++ /dev/null
@@ -1,40 +0,0 @@
-%!PS-Adobe-3.0 Resource-Encoding
-%%Title: VIM-iso-8859-13
-%%Version: 1.0 0
-%%EndComments
-/VIM-iso-8859-13[
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
-/parenleft /parenright /asterisk /plus /comma /minus /period /slash
-/zero /one /two /three /four /five /six /seven
-/eight /nine /colon /semicolon /less /equal /greater /question
-/at /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 /bracketleft /backslash /bracketright /asciicircum /underscore
-/grave /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 /braceleft /bar /braceright /asciitilde /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /quotedblright /cent /sterling /currency /quotedblbase /brokenbar /section
-/Oslash /copyright /Rcedilla /guillemotleft /logicalnot /hyphen /registered /AE
-/degree /plusminus /twosuperior /threesuperior /quotedblleft /mu /paragraph /periodcentered
-/oslash /onesuperior /rcedilla /guillemotright /onequarter /onehalf /threequarters /ae
-/Aogonek /Iogonek /Amacron /Cacute /Adieresis /Aring /Eogonek /Emacron
-/Ccaron /Eacute /Zacute /Edot /Gcedilla /Kcedilla /Imacron /Lcedilla
-/Scaron /Nacute /Ncedilla /Oacute /Omacron /Otilde /Odieresis /multiply
-/Uogonek /Lslash /Sacute /Umacron /Udieresis /Zdotaccent /Zcaron /germandbls
-/aogonek /iogonek /amacron /cacute /adieresis /aring /eogonek /emacron
-/ccaron /eacute /zacute /edot /gcedilla /kcedilla /imacron /lcedilla
-/scaron /nacute /ncedilla /oacute /omacron /otilde /odieresis /divide
-/uogonek /lslash /sacute /umacron /udieresis /zdotaccent /zcaron /quoteright]
-/Encoding defineresource pop
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/iso-8859-14.ps b/runtime/print/iso-8859-14.ps
deleted file mode 100644
index cdfe04268a..0000000000
--- a/runtime/print/iso-8859-14.ps
+++ /dev/null
@@ -1,40 +0,0 @@
-%!PS-Adobe-3.0 Resource-Encoding
-%%Title: VIM-iso-8859-14
-%%Version: 1.0 0
-%%EndComments
-/VIM-iso-8859-14[
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
-/parenleft /parenright /asterisk /plus /comma /minus /period /slash
-/zero /one /two /three /four /five /six /seven
-/eight /nine /colon /semicolon /less /equal /greater /question
-/at /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 /bracketleft /backslash /bracketright /asciicircum /underscore
-/grave /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 /braceleft /bar /braceright /asciitilde /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /uni1E02 /uni1E03 /sterling /Cdotaccent /cdotaccent /uni1E0A /section
-/Wgrave /copyright /Wacute /uni1E0B /Ygrave /hyphen /registered /Ydieresis
-/uni1E1E /uni1E1F /Gdotaccent /gdotaccent /uni1E40 /uni1E41 /paragraph /uni1E56
-/wgrave /uni1E57 /wacute /uni1E60 /ygrave /Wdieresis /wdieresis /uni1E61
-/Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla
-/Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis
-/Wcircumflex /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /uni1E6A
-/Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Ycircumflex /germandbls
-/agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla
-/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis
-/wcircumflex /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /uni1E6B
-/oslash /ugrave /uacute /ucircumflex /udieresis /yacute /ycircumflex /ydieresis]
-/Encoding defineresource pop
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/iso-8859-15.ps b/runtime/print/iso-8859-15.ps
deleted file mode 100644
index 46ea691ff1..0000000000
--- a/runtime/print/iso-8859-15.ps
+++ /dev/null
@@ -1,40 +0,0 @@
-%!PS-Adobe-3.0 Resource-Encoding
-%%Title: VIM-iso-8859-15
-%%Version: 1.0 0
-%%EndComments
-/VIM-iso-8859-15[
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
-/parenleft /parenright /asterisk /plus /comma /minus /period /slash
-/zero /one /two /three /four /five /six /seven
-/eight /nine /colon /semicolon /less /equal /greater /question
-/at /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 /bracketleft /backslash /bracketright /asciicircum /underscore
-/grave /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 /braceleft /bar /braceright /asciitilde /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /exclamdown /cent /sterling /Euro /yen /Scaron /section
-/scaron /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron
-/degree /plusminus /twosuperior /threesuperior /Zcaron /mu /paragraph /periodcentered
-/zcaron /onesuperior /ordmasculine /guillemotright /OE /oe /Ydieresis /questiondown
-/Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla
-/Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis
-/Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply
-/Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls
-/agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla
-/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis
-/eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide
-/oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis]
-/Encoding defineresource pop
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/iso-8859-2.ps b/runtime/print/iso-8859-2.ps
deleted file mode 100644
index f6e1933be7..0000000000
--- a/runtime/print/iso-8859-2.ps
+++ /dev/null
@@ -1,40 +0,0 @@
-%!PS-Adobe-3.0 Resource-Encoding
-%%Title: VIM-iso-8859-2
-%%Version: 1.0 0
-%%EndComments
-/VIM-iso-8859-2[
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
-/parenleft /parenright /asterisk /plus /comma /minus /period /slash
-/zero /one /two /three /four /five /six /seven
-/eight /nine /colon /semicolon /less /equal /greater /question
-/at /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 /bracketleft /backslash /bracketright /asciicircum /underscore
-/grave /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 /braceleft /bar /braceright /asciitilde /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /Aogonek /breve /Lslash /currency /Lcaron /Sacute /section
-/dieresis /Scaron /Scedilla /Tcaron /Zacute /hyphen /Zcaron /Zdotaccent
-/degree /aogonek /ogonek /lslash /acute /lcaron /sacute /caron
-/cedilla /scaron /scedilla /tcaron /zacute /hungarumlaut /zcaron /zdotaccent
-/Racute /Aacute /Acircumflex /Abreve /Adieresis /Lacute /Cacute /Ccedilla
-/Ccaron /Eacute /Eogonek /Edieresis /Ecaron /Iacute /Icircumflex /Dcaron
-/Dcroat /Nacute /Ncaron /Oacute /Ocircumflex /Ohungarumlaut /Odieresis /multiply
-/Rcaron /Uring /Uacute /Uhungarumlaut /Udieresis /Yacute /Tcedilla /germandbls
-/racute /aacute /acircumflex /abreve /adieresis /lacute /cacute /ccedilla
-/ccaron /eacute /eogonek /edieresis /ecaron /iacute /icircumflex /dcaron
-/dcroat /nacute /ncaron /oacute /ocircumflex /ohungarumlaut /odieresis /divide
-/rcaron /uring /uacute /uhungarumlaut /udieresis /yacute /tcedilla /dotaccent]
-/Encoding defineresource pop
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/iso-8859-3.ps b/runtime/print/iso-8859-3.ps
deleted file mode 100644
index b5a3474fb3..0000000000
--- a/runtime/print/iso-8859-3.ps
+++ /dev/null
@@ -1,40 +0,0 @@
-%!PS-Adobe-3.0 Resource-Encoding
-%%Title: VIM-iso-8859-3
-%%Version: 1.0 0
-%%EndComments
-/VIM-iso-8859-3[
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
-/parenleft /parenright /asterisk /plus /comma /minus /period /slash
-/zero /one /two /three /four /five /six /seven
-/eight /nine /colon /semicolon /less /equal /greater /question
-/at /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 /bracketleft /backslash /bracketright /asciicircum /underscore
-/grave /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 /braceleft /bar /braceright /asciitilde /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /Hbar /breve /sterling /currency /.notdef /Hcircumflex /section
-/dieresis /Idot /Scedilla /Gbreve /Jcircumflex /hyphen /.notdef /Zdotaccent
-/degree /hbar /twosuperior /threesuperior /acute /mu /hcircumflex /periodcentered
-/cedilla /dotlessi /scedilla /gbreve /jcircumflex /onehalf /.notdef /zdotaccent
-/Agrave /Aacute /Acircumflex /.notdef /Adieresis /Cdotaccent /Ccircumflex /Ccedilla
-/Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis
-/.notdef /Ntilde /Ograve /Oacute /Ocircumflex /Gdotaccent /Odieresis /multiply
-/Gcircumflex /Ugrave /Uacute /Ucircumflex /Udieresis /Ubreve /Scircumflex /germandbls
-/agrave /aacute /acircumflex /.notdef /adieresis /cdotaccent /ccircumflex /ccedilla
-/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis
-/.notdef /ntilde /ograve /oacute /ocircumflex /gdotaccent /odieresis /divide
-/gcircumflex /ugrave /uacute /ucircumflex /udieresis /ubreve /scircumflex /dotaccent]
-/Encoding defineresource pop
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/iso-8859-4.ps b/runtime/print/iso-8859-4.ps
deleted file mode 100644
index c917d1ff37..0000000000
--- a/runtime/print/iso-8859-4.ps
+++ /dev/null
@@ -1,40 +0,0 @@
-%!PS-Adobe-3.0 Resource-Encoding
-%%Title: VIM-iso-8859-4
-%%Version: 1.0 0
-%%EndComments
-/VIM-iso-8859-4[
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
-/parenleft /parenright /asterisk /plus /comma /minus /period /slash
-/zero /one /two /three /four /five /six /seven
-/eight /nine /colon /semicolon /less /equal /greater /question
-/at /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 /bracketleft /backslash /bracketright /asciicircum /underscore
-/grave /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 /braceleft /bar /braceright /asciitilde /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /Aogonek /kgreenlandic /Rcedilla /currency /Itilde /Lcedilla /section
-/dieresis /Scaron /Emacron /Gcedilla /Tbar /.notdef /Zcaron /macron
-/degree /aogonek /ogonek /rcedilla /acute /itilde /lcedilla /caron
-/cedilla /scaron /emacron /gcedilla /tbar /Eng /zcaron /eng
-/Amacron /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Iogonek
-/Ccaron /Eacute /Eogonek /Edieresis /Edot /Iacute /Icircumflex /Imacron
-/Dcroat /Ncedilla /Omacron /Kcedilla /Ocircumflex /Otilde /Odieresis /multiply
-/Oslash /Uogonek /Uacute /Ucircumflex /Udieresis /Utilde /Umacron /germandbls
-/amacron /aacute /acircumflex /atilde /adieresis /aring /ae /iogonek
-/ccaron /eacute /eogonek /edieresis /edot /iacute /icircumflex /imacron
-/dcroat /ncedilla /omacron /kcedilla /ocircumflex /otilde /odieresis /divide
-/oslash /uogonek /uacute /ucircumflex /udieresis /utilde /umacron /dotaccent]
-/Encoding defineresource pop
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/iso-8859-5.ps b/runtime/print/iso-8859-5.ps
deleted file mode 100644
index dbe9628900..0000000000
--- a/runtime/print/iso-8859-5.ps
+++ /dev/null
@@ -1,40 +0,0 @@
-%!PS-Adobe-3.0 Resource-Encoding
-%%Title: VIM-iso-8859-5
-%%Version: 1.0 0
-%%EndComments
-/VIM-iso-8859-5[
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
-/parenleft /parenright /asterisk /plus /comma /minus /period /slash
-/zero /one /two /three /four /five /six /seven
-/eight /nine /colon /semicolon /less /equal /greater /question
-/at /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 /bracketleft /backslash /bracketright /asciicircum /underscore
-/grave /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 /braceleft /bar /braceright /asciitilde /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /afii10023 /afii10051 /afii10052 /afii10053 /afii10054 /afii10055 /afii10056
-/afii10057 /afii10058 /afii10059 /afii10060 /afii10061 /.notdef /afii10062 /afii10145
-/afii10017 /afii10018 /afii10019 /afii10020 /afii10021 /afii10022 /afii10024 /afii10025
-/afii10026 /afii10027 /afii10028 /afii10029 /afii10030 /afii10031 /afii10032 /afii10033
-/afii10034 /afii10035 /afii10036 /afii10037 /afii10038 /afii10039 /afii10040 /afii10041
-/afii10042 /afii10043 /afii10044 /afii10045 /afii10046 /afii10047 /afii10048 /afii10049
-/afii10065 /afii10066 /afii10067 /afii10068 /afii10069 /afii10070 /afii10072 /afii10073
-/afii10074 /afii10075 /afii10076 /afii10077 /afii10078 /afii10079 /afii10080 /afii10081
-/afii10082 /afii10083 /afii10084 /afii10085 /afii10086 /afii10087 /afii10088 /afii10089
-/afii10090 /afii10091 /afii10092 /afii10093 /afii10094 /afii10095 /afii10096 /afii10097
-/afii61352 /afii10071 /afii10099 /afii10100 /afii10101 /afii10102 /afii10103 /afii10104
-/afii10105 /afii10106 /afii10107 /afii10108 /afii10109 /section /afii10110 /afii10193]
-/Encoding defineresource pop
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/iso-8859-7.ps b/runtime/print/iso-8859-7.ps
deleted file mode 100644
index fc16bf1a61..0000000000
--- a/runtime/print/iso-8859-7.ps
+++ /dev/null
@@ -1,40 +0,0 @@
-%!PS-Adobe-3.0 Resource-Encoding
-%%Title: VIM-iso-8859-7
-%%Version: 1.0 0
-%%EndComments
-/VIM-iso-8859-7[
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
-/parenleft /parenright /asterisk /plus /comma /minus /period /slash
-/zero /one /two /three /four /five /six /seven
-/eight /nine /colon /semicolon /less /equal /greater /question
-/at /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 /bracketleft /backslash /bracketright /asciicircum /underscore
-/grave /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 /braceleft /bar /braceright /asciitilde /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /quotereversed /quoteright /sterling /.notdef /.notdef /brokenbar /section
-/dieresis /copyright /.notdef /guillemotleft /logicalnot /.notdef /.notdef /emdash
-/degree /plusminus /twosuperior /threesuperior /tonos /dieresistonos /Alphatonos /periodcentered
-/Epsilontonos /Etatonos /Iotatonos /guillemotright /Omicrontonos /onehalf /Upsilontonos /Omegatonos
-/iotadieresistonos /Alpha /Beta /Gamma /Delta /Epsilon /Zeta /Eta
-/Theta /Iota /Kappa /Lambda /Mu /Nu /Xi /Omicron
-/Pi /Rho /.notdef /Sigma /Tau /Upsilon /Phi /Chi
-/Psi /Omega /Iotadieresis /Upsilondieresis /alphatonos /epsilontonos /etatonos /iotatonos
-/upsilondieresistonos /alpha /beta /gamma /delta /epsilon /zeta /eta
-/theta /iota /kappa /lambda /mu /nu /xi /omicron
-/pi /rho /sigma1 /sigma /tau /upsilon /phi /chi
-/psi /omega /iotadieresis /upsilondieresis /omicrontonos /upsilontonos /omegatonos /.notdef]
-/Encoding defineresource pop
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/iso-8859-8.ps b/runtime/print/iso-8859-8.ps
deleted file mode 100644
index 15193cc8ea..0000000000
--- a/runtime/print/iso-8859-8.ps
+++ /dev/null
@@ -1,40 +0,0 @@
-%!PS-Adobe-3.0 Resource-Encoding
-%%Title: VIM-iso-8859-8
-%%Version: 1.0 0
-%%EndComments
-/VIM-iso-8859-8[
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
-/parenleft /parenright /asterisk /plus /comma /minus /period /slash
-/zero /one /two /three /four /five /six /seven
-/eight /nine /colon /semicolon /less /equal /greater /question
-/at /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 /bracketleft /backslash /bracketright /asciicircum /underscore
-/grave /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 /braceleft /bar /braceright /asciitilde /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /.notdef /cent /sterling /currency /yen /brokenbar /section
-/dieresis /copyright /multiply /guillemotleft /logicalnot /hyphen /registered /macron
-/degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered
-/cedilla /onesuperior /divide /guillemotright /onequarter /onehalf /threequarters /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /underscoredbl
-/alef /bet /gimel /dalet /he /vav /zayin /het
-/tet /yod /finalkaf /kaf /lamed /finalmem /mem /finalnun
-/nun /samekh /ayin /finalpe /pe /finaltsadi /tsadi /qof
-/resh /shin /tav /.notdef /.notdef /.notdef /.notdef /.notdef]
-/Encoding defineresource pop
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/iso-8859-9.ps b/runtime/print/iso-8859-9.ps
deleted file mode 100644
index d40f6e9864..0000000000
--- a/runtime/print/iso-8859-9.ps
+++ /dev/null
@@ -1,40 +0,0 @@
-%!PS-Adobe-3.0 Resource-Encoding
-%%Title: VIM-iso-8859-9
-%%Version: 1.0 0
-%%EndComments
-/VIM-iso-8859-9[
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
-/parenleft /parenright /asterisk /plus /comma /minus /period /slash
-/zero /one /two /three /four /five /six /seven
-/eight /nine /colon /semicolon /less /equal /greater /question
-/at /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 /bracketleft /backslash /bracketright /asciicircum /underscore
-/grave /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 /braceleft /bar /braceright /asciitilde /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /exclamdown /cent /sterling /currency /yen /brokenbar /section
-/dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron
-/degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered
-/cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown
-/Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla
-/Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis
-/Gbreve /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply
-/Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Idotaccent /Scedilla /germandbls
-/agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla
-/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis
-/gbreve /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide
-/oslash /ugrave /uacute /ucircumflex /udieresis /dotlessi /scedilla /ydieresis]
-/Encoding defineresource pop
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/jis_roman.ps b/runtime/print/jis_roman.ps
deleted file mode 100644
index f24a8069a3..0000000000
--- a/runtime/print/jis_roman.ps
+++ /dev/null
@@ -1,23 +0,0 @@
-%!PS-Adobe-3.0 Resource-Encoding
-%%Title: VIM-jis_roman
-%%Version: 1.0 0
-%%EndComments
-% Different to ASCII at code points 92 and 126
-/VIM-jis_roman[
-32{/.notdef}repeat
-/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
-/parenleft /parenright /asterisk /plus /comma /minus /period /slash
-/zero /one /two /three /four /five /six /seven
-/eight /nine /colon /semicolon /less /equal /greater /question
-/at /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 /bracketleft /yen /bracketright /asciicircum /underscore
-/grave /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 /braceleft /bar /braceright /overline /.notdef
-128{/.notdef}repeat]
-/Encoding defineresource pop
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/koi8-r.ps b/runtime/print/koi8-r.ps
deleted file mode 100644
index d42daabb49..0000000000
--- a/runtime/print/koi8-r.ps
+++ /dev/null
@@ -1,40 +0,0 @@
-%!PS-Adobe-3.0 Resource-Encoding
-%%Title: VIM-koi8-r
-%%Version: 1.0 0
-%%EndComments
-/VIM-koi8-r[
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
-/parenleft /parenright /asterisk /plus /comma /minus /period /slash
-/zero /one /two /three /four /five /six /seven
-/eight /nine /colon /semicolon /less /equal /greater /question
-/at /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 /bracketleft /backslash /bracketright /asciicircum /underscore
-/grave /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 /braceleft /bar /braceright /asciitilde /.notdef
-/SF100000 /SF110000 /SF010000 /SF030000 /SF020000 /SF040000 /SF080000 /SF090000
-/SF060000 /SF070000 /SF050000 /upblock /dnblock /block /lfblock /rtblock
-/ltshade /shade /dkshade /integraltp /filledbox /bullet /radical /approxequal
-/lessequal /greaterequal /space /integralbt /degree /twosuperior /periodcentered /divide
-/SF430000 /SF240000 /SF510000 /afii10071 /SF520000 /SF390000 /SF220000 /SF210000
-/SF250000 /SF500000 /SF490000 /SF380000 /SF280000 /SF270000 /SF260000 /SF360000
-/SF370000 /SF420000 /SF190000 /afii10023 /SF200000 /SF230000 /SF470000 /SF480000
-/SF410000 /SF450000 /SF460000 /SF400000 /SF540000 /SF530000 /SF440000 /copyright
-/afii10096 /afii10065 /afii10066 /afii10088 /afii10069 /afii10070 /afii10086 /afii10068
-/afii10087 /afii10074 /afii10075 /afii10076 /afii10077 /afii10078 /afii10079 /afii10080
-/afii10081 /afii10097 /afii10082 /afii10083 /afii10084 /afii10085 /afii10072 /afii10067
-/afii10094 /afii10093 /afii10073 /afii10090 /afii10095 /afii10091 /afii10089 /afii10092
-/afii10048 /afii10017 /afii10018 /afii10040 /afii10021 /afii10022 /afii10038 /afii10020
-/afii10039 /afii10026 /afii10027 /afii10028 /afii10029 /afii10030 /afii10031 /afii10032
-/afii10033 /afii10049 /afii10034 /afii10035 /afii10036 /afii10037 /afii10024 /afii10019
-/afii10046 /afii10045 /afii10025 /afii10042 /afii10047 /afii10043 /afii10041 /afii10044]
-/Encoding defineresource pop
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/koi8-u.ps b/runtime/print/koi8-u.ps
deleted file mode 100644
index 53631049fe..0000000000
--- a/runtime/print/koi8-u.ps
+++ /dev/null
@@ -1,40 +0,0 @@
-%!PS-Adobe-3.0 Resource-Encoding
-%%Title: VIM-koi8-u
-%%Version: 1.0 0
-%%EndComments
-/VIM-koi8-u[
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
-/parenleft /parenright /asterisk /plus /comma /minus /period /slash
-/zero /one /two /three /four /five /six /seven
-/eight /nine /colon /semicolon /less /equal /greater /question
-/at /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 /bracketleft /backslash /bracketright /asciicircum /underscore
-/grave /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 /braceleft /bar /braceright /asciitilde /.notdef
-/SF100000 /SF110000 /SF010000 /SF030000 /SF020000 /SF040000 /SF080000 /SF090000
-/SF060000 /SF070000 /SF050000 /upblock /dnblock /block /lfblock /rtblock
-/ltshade /shade /dkshade /integraltp /filledbox /bullet /radical /approxequal
-/lessequal /greaterequal /space /integralbt /degree /twosuperior /periodcentered /divide
-/SF430000 /SF240000 /SF510000 /afii10071 /afii10101 /SF390000 /afii10103 /afii10104
-/SF250000 /SF500000 /SF490000 /SF380000 /SF280000 /afii10098 /SF260000 /SF360000
-/SF370000 /SF420000 /SF190000 /afii10023 /afii10053 /SF230000 /afii10055 /afii10056
-/SF410000 /SF450000 /SF460000 /SF400000 /SF540000 /afii10050 /SF440000 /copyright
-/afii10096 /afii10065 /afii10066 /afii10088 /afii10069 /afii10070 /afii10086 /afii10068
-/afii10087 /afii10074 /afii10075 /afii10076 /afii10077 /afii10078 /afii10079 /afii10080
-/afii10081 /afii10097 /afii10082 /afii10083 /afii10084 /afii10085 /afii10072 /afii10067
-/afii10094 /afii10093 /afii10073 /afii10090 /afii10095 /afii10091 /afii10089 /afii10092
-/afii10048 /afii10017 /afii10018 /afii10040 /afii10021 /afii10022 /afii10038 /afii10020
-/afii10039 /afii10026 /afii10027 /afii10028 /afii10029 /afii10030 /afii10031 /afii10032
-/afii10033 /afii10049 /afii10034 /afii10035 /afii10036 /afii10037 /afii10024 /afii10019
-/afii10046 /afii10045 /afii10025 /afii10042 /afii10047 /afii10043 /afii10041 /afii10044]
-/Encoding defineresource pop
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/ks_roman.ps b/runtime/print/ks_roman.ps
deleted file mode 100644
index b688550a65..0000000000
--- a/runtime/print/ks_roman.ps
+++ /dev/null
@@ -1,23 +0,0 @@
-%!PS-Adobe-3.0 Resource-Encoding
-%%Title: VIM-ks_roman
-%%Version: 1.0 0
-%%EndComments
-% Different to ASCII at code points 96 and 126
-/VIM-ks_roman[
-32{/.notdef}repeat
-/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
-/parenleft /parenright /asterisk /plus /comma /minus /period /slash
-/zero /one /two /three /four /five /six /seven
-/eight /nine /colon /semicolon /less /equal /greater /question
-/at /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 /bracketleft /won /bracketright /asciicircum /underscore
-/grave /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 /braceleft /bar /braceright /overline /.notdef
-128{/.notdef}repeat]
-/Encoding defineresource pop
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/latin1.ps b/runtime/print/latin1.ps
deleted file mode 100644
index 569db9bfe0..0000000000
--- a/runtime/print/latin1.ps
+++ /dev/null
@@ -1,40 +0,0 @@
-%!PS-Adobe-3.0 Resource-Encoding
-%%Title: VIM-latin1
-%%Version: 1.0 0
-%%EndComments
-/VIM-latin1[
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
-/parenleft /parenright /asterisk /plus /comma /minus /period /slash
-/zero /one /two /three /four /five /six /seven
-/eight /nine /colon /semicolon /less /equal /greater /question
-/at /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 /bracketleft /backslash /bracketright /asciicircum /underscore
-/grave /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 /braceleft /bar /braceright /asciitilde /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /exclamdown /cent /sterling /currency /yen /brokenbar /section
-/dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron
-/degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered
-/cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown
-/Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla
-/Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis
-/Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply
-/Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls
-/agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla
-/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis
-/eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide
-/oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis]
-/Encoding defineresource pop
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/mac-roman.ps b/runtime/print/mac-roman.ps
deleted file mode 100644
index b0941be650..0000000000
--- a/runtime/print/mac-roman.ps
+++ /dev/null
@@ -1,40 +0,0 @@
-%!PS-Adobe-3.0 Resource-Encoding
-%%Title: VIM-mac-roman
-%%Version: 1.0 0
-%%EndComments
-/VIM-mac-roman[
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
-/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
-/parenleft /parenright /asterisk /plus /comma /minus /period /slash
-/zero /one /two /three /four /five /six /seven
-/eight /nine /colon /semicolon /less /equal /greater /question
-/at /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 /bracketleft /backslash /bracketright /asciicircum /underscore
-/grave /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 /braceleft /bar /braceright /asciitilde /.notdef
-/Adieresis /Aring /Ccedilla /Eacute /Ntilde /Odieresis /Udieresis /aacute
-/agrave /acircumflex /adieresis /atilde /aring /ccedilla /eacute /egrave
-/ecircumflex /edieresis /iacute /igrave /icircumflex /idieresis /ntilde /oacute
-/ograve /ocircumflex /odieresis /otilde /uacute /ugrave /ucircumflex /udieresis
-/dagger /degree /cent /sterling /section /bullet /paragraph /germandbls
-/registered /copyright /trademark /acute /dieresis /notequal /AE /Oslash
-/infinity /plusminus /lessequal /greaterequal /yen /mu /partialdiff /summation
-/Pi /pi /integral /ordfeminine /ordmasculine /Omega /ae /oslash
-/questiondown /exclamdown /logicalnot /radical /florin /approxequal /delta /guillemotleft
-/guillemotright /ellipsis /space /Agrave /Atilde /Otilde /OE /oe
-/endash /emdash /quotedblleft /quotedblright /quoteleft /quoteright /divide /lozenge
-/ydieresis /Ydieresis /fraction /currency /guilsinglleft /guilsinglright /fi /fl
-/daggerdbl /periodcentered /quotesinglbase /quotedblbase /perthousand /Acircumflex /Ecircumflex /Aacute
-/Edieresis /Egrave /Iacute /Icircumflex /Idieresis /Igrave /Oacute /Ocircumflex
-/heart /Ograve /Uacute /Ucircumflex /Ugrave /dotlessi /circumflex /tilde
-/macron /breve /dotaccent /ring /cedilla /hungarumlaut /ogonek /caron]
-/Encoding defineresource pop
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/print/prolog.ps b/runtime/print/prolog.ps
deleted file mode 100644
index 620856999f..0000000000
--- a/runtime/print/prolog.ps
+++ /dev/null
@@ -1,44 +0,0 @@
-%!PS-Adobe-3.0 Resource-ProcSet
-%%Title: VIM-Prolog
-%%Version: 1.4 1
-%%EndComments
-% Editing of this file is NOT RECOMMENDED. You run a very good risk of causing
-% all PostScript printing from VIM failing if you do. PostScript is not called
-% a write-only language for nothing!
-/packedarray where not{userdict begin/setpacking/pop load def/currentpacking
-false def end}{pop}ifelse/CP currentpacking def true setpacking
-/bd{bind def}bind def/ld{load def}bd/ed{exch def}bd/d/def ld
-/db{dict begin}bd/cde{currentdict end}bd
-/T true d/F false d
-/SO null d/sv{/SO save d}bd/re{SO restore}bd
-/L2 systemdict/languagelevel 2 copy known{get exec}{pop pop 1}ifelse 2 ge d
-/m/moveto ld/s/show ld /ms{m s}bd /g/setgray ld/r/setrgbcolor ld/sp{showpage}bd
-/gs/gsave ld/gr/grestore ld/cp/currentpoint ld
-/ul{gs UW setlinewidth cp UO add 2 copy newpath m 3 1 roll add exch lineto
-stroke gr}bd
-/bg{gs r cp BO add 4 -2 roll rectfill gr}bd
-/sl{90 rotate 0 exch translate}bd
-L2{
-/sspd{mark exch{setpagedevice}stopped cleartomark}bd
-/nc{1 db/NumCopies ed cde sspd}bd
-/sps{3 db/Orientation ed[3 1 roll]/PageSize ed/ImagingBBox null d cde sspd}bd
-/dt{2 db/Tumble ed/Duplex ed cde sspd}bd
-/c{1 db/Collate ed cde sspd}bd
-}{
-/nc{/#copies ed}bd
-/sps{statusdict/setpage get exec}bd
-/dt{statusdict/settumble 2 copy known{get exec}{pop pop pop}ifelse
-statusdict/setduplexmode 2 copy known{get exec}{pop pop pop}ifelse}bd
-/c{pop}bd
-}ifelse
-/ffs{findfont exch scalefont d}bd/sf{setfont}bd
-/ref{1 db findfont dup maxlength dict/NFD ed{exch dup/FID ne{exch NFD 3 1 roll
-put}{pop pop}ifelse}forall/Encoding findresource dup length 256 eq{NFD/Encoding
-3 -1 roll put}{pop}ifelse NFD dup/FontType get 3 ne{/CharStrings}{/CharProcs}
-ifelse 2 copy known{2 copy get dup maxlength dict copy[/questiondown/space]{2
-copy known{2 copy get 2 index/.notdef 3 -1 roll put pop exit}if pop}forall put
-}{pop pop}ifelse dup NFD/FontName 3 -1 roll put NFD definefont pop end}bd
-CP setpacking
-(\004)cvn{}bd
-% vim:ff=unix:
-%%EOF
diff --git a/runtime/queries/c/highlights.scm b/runtime/queries/c/highlights.scm
index 260750a85b..33e6df74ab 100644
--- a/runtime/queries/c/highlights.scm
+++ b/runtime/queries/c/highlights.scm
@@ -101,6 +101,7 @@
[ "(" ")" "[" "]" "{" "}"] @punctuation.bracket
(string_literal) @string
+(string_literal) @spell
(system_lib_string) @string
(null) @constant.builtin
@@ -148,6 +149,7 @@
(comment) @comment
+(comment) @spell
;; Parameters
(parameter_declaration
diff --git a/runtime/queries/c/injections.scm b/runtime/queries/c/injections.scm
new file mode 100644
index 0000000000..7e9e73449d
--- /dev/null
+++ b/runtime/queries/c/injections.scm
@@ -0,0 +1,3 @@
+(preproc_arg) @c
+
+; (comment) @comment
diff --git a/runtime/queries/help/highlights.scm b/runtime/queries/help/highlights.scm
new file mode 100644
index 0000000000..c0d88301bc
--- /dev/null
+++ b/runtime/queries/help/highlights.scm
@@ -0,0 +1,25 @@
+(h1) @text.title
+(h2) @text.title
+(h3) @text.title
+(column_heading) @text.title
+(column_heading
+ "~" @conceal (#set! conceal ""))
+(tag
+ "*" @conceal (#set! conceal "")
+ text: (_) @label)
+(taglink
+ "|" @conceal (#set! conceal "")
+ text: (_) @text.reference)
+(optionlink
+ text: (_) @text.reference)
+(codespan
+ "`" @conceal (#set! conceal "")
+ text: (_) @text.literal)
+(codeblock) @text.literal
+(codeblock
+ [">" (language)] @conceal (#set! conceal ""))
+(block
+ "<" @conceal (#set! conceal ""))
+(argument) @parameter
+(keycode) @string.special
+(url) @text.uri
diff --git a/runtime/queries/help/injections.scm b/runtime/queries/help/injections.scm
new file mode 100644
index 0000000000..09bbe44e84
--- /dev/null
+++ b/runtime/queries/help/injections.scm
@@ -0,0 +1,3 @@
+(codeblock
+ (language) @language
+ (code) @content)
diff --git a/runtime/queries/lua/highlights.scm b/runtime/queries/lua/highlights.scm
new file mode 100644
index 0000000000..2c0dc5447a
--- /dev/null
+++ b/runtime/queries/lua/highlights.scm
@@ -0,0 +1,199 @@
+;; Keywords
+
+"return" @keyword.return
+
+[
+ "goto"
+ "in"
+ "local"
+] @keyword
+
+(label_statement) @label
+
+(break_statement) @keyword
+
+(do_statement
+[
+ "do"
+ "end"
+] @keyword)
+
+(while_statement
+[
+ "while"
+ "do"
+ "end"
+] @repeat)
+
+(repeat_statement
+[
+ "repeat"
+ "until"
+] @repeat)
+
+(if_statement
+[
+ "if"
+ "elseif"
+ "else"
+ "then"
+ "end"
+] @conditional)
+
+(elseif_statement
+[
+ "elseif"
+ "then"
+ "end"
+] @conditional)
+
+(else_statement
+[
+ "else"
+ "end"
+] @conditional)
+
+(for_statement
+[
+ "for"
+ "do"
+ "end"
+] @repeat)
+
+(function_declaration
+[
+ "function"
+ "end"
+] @keyword.function)
+
+(function_definition
+[
+ "function"
+ "end"
+] @keyword.function)
+
+;; Operators
+
+[
+ "and"
+ "not"
+ "or"
+] @keyword.operator
+
+[
+ "+"
+ "-"
+ "*"
+ "/"
+ "%"
+ "^"
+ "#"
+ "=="
+ "~="
+ "<="
+ ">="
+ "<"
+ ">"
+ "="
+ "&"
+ "~"
+ "|"
+ "<<"
+ ">>"
+ "//"
+ ".."
+] @operator
+
+;; Punctuations
+
+[
+ ";"
+ ":"
+ ","
+ "."
+] @punctuation.delimiter
+
+;; Brackets
+
+[
+ "("
+ ")"
+ "["
+ "]"
+ "{"
+ "}"
+] @punctuation.bracket
+
+;; Variables
+
+(identifier) @variable
+
+((identifier) @variable.builtin
+ (#eq? @variable.builtin "self"))
+
+(variable_list
+ attribute: (attribute
+ (["<" ">"] @punctuation.bracket
+ (identifier) @attribute)))
+
+;; Constants
+
+((identifier) @constant
+ (#lua-match? @constant "^[A-Z][A-Z_0-9]*$"))
+
+(vararg_expression) @constant
+
+(nil) @constant.builtin
+
+[
+ (false)
+ (true)
+] @boolean
+
+;; Tables
+
+(field name: (identifier) @field)
+
+(dot_index_expression field: (identifier) @field)
+
+(table_constructor
+[
+ "{"
+ "}"
+] @constructor)
+
+;; Functions
+
+(parameters (identifier) @parameter)
+
+(function_call name: (identifier) @function.call)
+(function_declaration name: (identifier) @function)
+
+(function_call name: (dot_index_expression field: (identifier) @function.call))
+(function_declaration name: (dot_index_expression field: (identifier) @function))
+
+(method_index_expression method: (identifier) @method)
+
+(function_call
+ (identifier) @function.builtin
+ (#any-of? @function.builtin
+ ;; built-in functions in Lua 5.1
+ "assert" "collectgarbage" "dofile" "error" "getfenv" "getmetatable" "ipairs"
+ "load" "loadfile" "loadstring" "module" "next" "pairs" "pcall" "print"
+ "rawequal" "rawget" "rawset" "require" "select" "setfenv" "setmetatable"
+ "tonumber" "tostring" "type" "unpack" "xpcall"))
+
+;; Others
+
+(comment) @comment
+(comment) @spell
+
+(hash_bang_line) @comment
+
+(number) @number
+
+(string) @string
+(string) @spell
+
+;; Error
+(ERROR) @error
diff --git a/runtime/queries/lua/injections.scm b/runtime/queries/lua/injections.scm
new file mode 100644
index 0000000000..0e67329139
--- /dev/null
+++ b/runtime/queries/lua/injections.scm
@@ -0,0 +1,22 @@
+((function_call
+ name: [
+ (identifier) @_cdef_identifier
+ (_ _ (identifier) @_cdef_identifier)
+ ]
+ arguments: (arguments (string content: _ @c)))
+ (#eq? @_cdef_identifier "cdef"))
+
+((function_call
+ name: (_) @_vimcmd_identifier
+ arguments: (arguments (string content: _ @vim)))
+ (#any-of? @_vimcmd_identifier "vim.cmd" "vim.api.nvim_command" "vim.api.nvim_exec" "vim.api.nvim_cmd"))
+
+; ((function_call
+; name: (_) @_vimcmd_identifier
+; arguments: (arguments (string content: _ @query) .))
+; (#eq? @_vimcmd_identifier "vim.treesitter.query.set_query"))
+
+; ;; highlight string as query if starts with `;; query`
+; ((string ("string_content") @query) (#lua-match? @query "^%s*;+%s?query"))
+
+; (comment) @comment
diff --git a/runtime/queries/vim/highlights.scm b/runtime/queries/vim/highlights.scm
new file mode 100644
index 0000000000..239b0a0b37
--- /dev/null
+++ b/runtime/queries/vim/highlights.scm
@@ -0,0 +1,290 @@
+(identifier) @variable
+((identifier) @constant
+ (#lua-match? @constant "^[A-Z][A-Z_0-9]*$"))
+
+;; Keywords
+
+[
+ "if"
+ "else"
+ "elseif"
+ "endif"
+] @conditional
+
+[
+ "try"
+ "catch"
+ "finally"
+ "endtry"
+ "throw"
+] @exception
+
+[
+ "for"
+ "endfor"
+ "in"
+ "while"
+ "endwhile"
+ "break"
+ "continue"
+] @repeat
+
+[
+ "function"
+ "endfunction"
+] @keyword.function
+
+;; Function related
+(function_declaration name: (_) @function)
+(call_expression function: (identifier) @function.call)
+(call_expression function: (scoped_identifier (identifier) @function.call))
+(parameters (identifier) @parameter)
+(default_parameter (identifier) @parameter)
+
+[ (bang) (spread) ] @punctuation.special
+
+[ (no_option) (inv_option) (default_option) (option_name) ] @variable.builtin
+[
+ (scope)
+ "a:"
+ "$"
+] @namespace
+
+;; Commands and user defined commands
+
+[
+ "let"
+ "unlet"
+ "const"
+ "call"
+ "execute"
+ "normal"
+ "set"
+ "setfiletype"
+ "setlocal"
+ "silent"
+ "echo"
+ "echon"
+ "echohl"
+ "echomsg"
+ "echoerr"
+ "autocmd"
+ "augroup"
+ "return"
+ "syntax"
+ "filetype"
+ "source"
+ "lua"
+ "ruby"
+ "perl"
+ "python"
+ "highlight"
+ "command"
+ "delcommand"
+ "comclear"
+ "colorscheme"
+ "startinsert"
+ "stopinsert"
+ "global"
+ "runtime"
+ "wincmd"
+ "cnext"
+ "cprevious"
+ "cNext"
+ "vertical"
+ "leftabove"
+ "aboveleft"
+ "rightbelow"
+ "belowright"
+ "topleft"
+ "botright"
+ (unknown_command_name)
+ "edit"
+ "enew"
+ "find"
+ "ex"
+ "visual"
+ "view"
+ "eval"
+] @keyword
+(map_statement cmd: _ @keyword)
+(command_name) @function.macro
+
+;; Filetype command
+
+(filetype_statement [
+ "detect"
+ "plugin"
+ "indent"
+ "on"
+ "off"
+] @keyword)
+
+;; Syntax command
+
+(syntax_statement (keyword) @string)
+(syntax_statement [
+ "enable"
+ "on"
+ "off"
+ "reset"
+ "case"
+ "spell"
+ "foldlevel"
+ "iskeyword"
+ "keyword"
+ "match"
+ "cluster"
+ "region"
+ "clear"
+ "include"
+] @keyword)
+
+(syntax_argument name: _ @keyword)
+
+[
+ "<buffer>"
+ "<nowait>"
+ "<silent>"
+ "<script>"
+ "<expr>"
+ "<unique>"
+] @constant.builtin
+
+(augroup_name) @namespace
+
+(au_event) @constant
+(normal_statement (commands) @constant)
+
+;; Highlight command
+
+(hl_attribute
+ key: _ @property
+ val: _ @constant)
+
+(hl_group) @type
+
+(highlight_statement [
+ "default"
+ "link"
+ "clear"
+] @keyword)
+
+;; Command command
+
+(command) @string
+
+(command_attribute
+ name: _ @property
+ val: (behavior
+ name: _ @constant
+ val: (identifier)? @function)?)
+
+;; Edit command
+(plus_plus_opt
+ val: _? @constant) @property
+(plus_cmd "+" @property) @property
+
+;; Runtime command
+
+(runtime_statement (where) @keyword.operator)
+
+;; Colorscheme command
+
+(colorscheme_statement (name) @string)
+
+;; Literals
+
+(string_literal) @string
+(integer_literal) @number
+(float_literal) @float
+(comment) @comment @spell
+(line_continuation_comment) @comment @spell
+(pattern) @string.special
+(pattern_multi) @string.regex
+(filename) @string
+(heredoc (body) @string)
+(heredoc (parameter) @keyword)
+[ (marker_definition) (endmarker) ] @label
+(literal_dictionary (literal_key) @label)
+((scoped_identifier
+ (scope) @_scope . (identifier) @boolean)
+ (#eq? @_scope "v:")
+ (#any-of? @boolean "true" "false"))
+
+;; Operators
+
+[
+ "||"
+ "&&"
+ "&"
+ "+"
+ "-"
+ "*"
+ "/"
+ "%"
+ ".."
+ "is"
+ "isnot"
+ "=="
+ "!="
+ ">"
+ ">="
+ "<"
+ "<="
+ "=~"
+ "!~"
+ "="
+ "+="
+ "-="
+ "*="
+ "/="
+ "%="
+ ".="
+ "..="
+ "<<"
+ "=<<"
+ (match_case)
+] @operator
+
+; Some characters have different meanings based on the context
+(unary_operation "!" @operator)
+(binary_operation "." @operator)
+
+
+;; Punctuation
+
+[
+ "("
+ ")"
+ "{"
+ "}"
+ "["
+ "]"
+ "#{"
+] @punctuation.bracket
+
+(field_expression "." @punctuation.delimiter)
+
+[
+ ","
+ ":"
+] @punctuation.delimiter
+
+(ternary_expression ["?" ":"] @conditional)
+
+; Options
+((set_value) @number
+ (#match? @number "^[0-9]+(\.[0-9]+)?$"))
+
+(inv_option "!" @operator)
+(set_item "?" @operator)
+
+((set_item
+ option: (option_name) @_option
+ value: (set_value) @function)
+ (#any-of? @_option
+ "tagfunc" "tfu"
+ "completefunc" "cfu"
+ "omnifunc" "ofu"
+ "operatorfunc" "opfunc"))
diff --git a/runtime/queries/vim/injections.scm b/runtime/queries/vim/injections.scm
new file mode 100644
index 0000000000..fdd025bfd9
--- /dev/null
+++ b/runtime/queries/vim/injections.scm
@@ -0,0 +1,28 @@
+(lua_statement (script (body) @lua))
+(lua_statement (chunk) @lua)
+(ruby_statement (script (body) @ruby))
+(ruby_statement (chunk) @ruby)
+(python_statement (script (body) @python))
+(python_statement (chunk) @python)
+;; If we support perl at some point...
+;; (perl_statement (script (body) @perl))
+;; (perl_statement (chunk) @perl)
+
+(autocmd_statement (pattern) @regex)
+
+((set_item
+ option: (option_name) @_option
+ value: (set_value) @vim)
+ (#any-of? @_option
+ "includeexpr" "inex"
+ "printexpr" "pexpr"
+ "formatexpr" "fex"
+ "indentexpr" "inde"
+ "foldtext" "fdt"
+ "foldexpr" "fde"
+ "diffexpr" "dex"
+ "patchexpr" "pex"
+ "charconvert" "ccv"))
+
+(comment) @comment
+(line_continuation_comment) @comment
diff --git a/runtime/scripts.vim b/runtime/scripts.vim
deleted file mode 100644
index 2d8bfdcb05..0000000000
--- a/runtime/scripts.vim
+++ /dev/null
@@ -1,459 +0,0 @@
-" Vim support file to detect file types in scripts
-"
-" Maintainer: Bram Moolenaar <Bram@vim.org>
-" Last change: 2021 Jan 22
-
-" This file is called by an autocommand for every file that has just been
-" loaded into a buffer. It checks if the type of file can be recognized by
-" the file contents. The autocommand is in $VIMRUNTIME/filetype.vim.
-"
-" Note that the pattern matches are done with =~# to avoid the value of the
-" '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()
- finish
-endif
-
-" Load the user defined scripts file first
-" Only do this when the FileType autocommand has not been triggered yet
-if exists("myscriptsfile") && filereadable(expand(myscriptsfile))
- execute "source " . myscriptsfile
- if did_filetype()
- finish
- endif
-endif
-
-" Line continuation is used here, remove 'C' from 'cpoptions'
-let s:cpo_save = &cpo
-set cpo&vim
-
-let s:line1 = getline(1)
-
-if s:line1 =~# "^#!"
- " A script that starts with "#!".
-
- " 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 s:line1 =~# '^#!\s*\S*\<env\s'
- let s:line1 = substitute(s:line1, '\S\+=\S\+', '', 'g')
- let s:line1 = substitute(s:line1, '\(-[iS]\|--ignore-environment\|--split-string\)', '', '')
- let s:line1 = substitute(s:line1, '\<env\s\+', '', '')
- endif
-
- " 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]".
- if s:line1 =~# '^#!\s*\a:[/\\]'
- let s:name = substitute(s:line1, '^#!.*[/\\]\(\i\+\).*', '\1', '')
- elseif s:line1 =~# '^#!.*\<env\>'
- let s:name = substitute(s:line1, '^#!.*\<env\>\s\+\(\i\+\).*', '\1', '')
- elseif s:line1 =~# '^#!\s*[^/\\ ]*\>\([^/\\]\|$\)'
- let s:name = substitute(s:line1, '^#!\s*\([^/\\ ]*\>\).*', '\1', '')
- else
- let s:name = substitute(s:line1, '^#!\s*\S*[/\\]\(\i\+\).*', '\1', '')
- endif
-
- " tcl scripts may have #!/bin/sh in the first line and "exec wish" in the
- " third line. Suggested by Steven Atkinson.
- if getline(3) =~# '^exec wish'
- let s:name = 'wish'
- endif
-
- " Bourne-like shell scripts: bash bash2 ksh ksh93 sh
- if s:name =~# '^\(bash\d*\|\|ksh\d*\|sh\)\>'
- call dist#ft#SetFileTypeSH(s:line1) " defined in filetype.vim
-
- " csh scripts
- elseif s:name =~# '^csh\>'
- if exists("g:filetype_csh")
- call dist#ft#SetFileTypeShell(g:filetype_csh)
- else
- call dist#ft#SetFileTypeShell("csh")
- endif
-
- " tcsh scripts
- elseif s:name =~# '^tcsh\>'
- call dist#ft#SetFileTypeShell("tcsh")
-
- " Z shell scripts
- elseif s:name =~# '^zsh\>'
- set ft=zsh
-
- " TCL scripts
- elseif s:name =~# '^\(tclsh\|wish\|expectk\|itclsh\|itkwish\)\>'
- set ft=tcl
-
- " Expect scripts
- elseif s:name =~# '^expect\>'
- set ft=expect
-
- " Gnuplot scripts
- elseif s:name =~# '^gnuplot\>'
- set ft=gnuplot
-
- " Makefiles
- elseif s:name =~# 'make\>'
- set ft=make
-
- " Pike
- elseif s:name =~# '^pike\%(\>\|[0-9]\)'
- set ft=pike
-
- " Lua
- elseif s:name =~# 'lua'
- set ft=lua
-
- " Perl
- elseif s:name =~# 'perl'
- set ft=perl
-
- " PHP
- elseif s:name =~# 'php'
- set ft=php
-
- " Python
- elseif s:name =~# 'python'
- set ft=python
-
- " Groovy
- elseif s:name =~# '^groovy\>'
- set ft=groovy
-
- " Raku
- elseif s:name =~# 'raku'
- set ft=raku
-
- " Ruby
- elseif s:name =~# 'ruby'
- set ft=ruby
-
- " JavaScript
- elseif s:name =~# 'node\(js\)\=\>\|js\>' || s:name =~# 'rhino\>'
- set ft=javascript
-
- " BC calculator
- elseif s:name =~# '^bc\>'
- set ft=bc
-
- " sed
- elseif s:name =~# 'sed\>'
- set ft=sed
-
- " OCaml-scripts
- elseif s:name =~# 'ocaml'
- set ft=ocaml
-
- " Awk scripts; also finds "gawk"
- elseif s:name =~# 'awk\>'
- set ft=awk
-
- " Website MetaLanguage
- elseif s:name =~# 'wml'
- set ft=wml
-
- " Scheme scripts
- elseif s:name =~# 'scheme'
- set ft=scheme
-
- " CFEngine scripts
- elseif s:name =~# 'cfengine'
- set ft=cfengine
-
- " Erlang scripts
- elseif s:name =~# 'escript'
- set ft=erlang
-
- " Haskell
- elseif s:name =~# 'haskell'
- set ft=haskell
-
- " Scala
- elseif s:name =~# 'scala\>'
- set ft=scala
-
- " Clojure
- elseif s:name =~# 'clojure'
- set ft=clojure
-
- " Free Pascal
- elseif s:name =~# 'instantfpc\>'
- set ft=pascal
-
- " Fennel
- elseif s:name =~# 'fennel\>'
- set ft=fennel
-
- " MikroTik RouterOS script
- elseif s:name =~# 'rsc\>'
- set ft=routeros
-
- " Fish shell
- elseif s:name =~# 'fish\>'
- set ft=fish
-
- " Gforth
- elseif s:name =~# 'gforth\>'
- set ft=forth
-
- " Icon
- elseif s:name =~# 'icon\>'
- set ft=icon
-
- " Guile
- elseif s:name =~# 'guile'
- set ft=scheme
-
- endif
- unlet s:name
-
-else
- " File does not start with "#!".
-
- let s:line2 = getline(2)
- let s:line3 = getline(3)
- let s:line4 = getline(4)
- let s:line5 = getline(5)
-
- " Bourne-like shell scripts: sh ksh bash bash2
- if s:line1 =~# '^:$'
- call dist#ft#SetFileTypeSH(s:line1) " defined in filetype.vim
-
- " Z shell scripts
- elseif s:line1 =~# '^#compdef\>' || s:line1 =~# '^#autoload\>' ||
- \ "\n".s:line1."\n".s:line2."\n".s:line3."\n".s:line4."\n".s:line5 =~# '\n\s*emulate\s\+\%(-[LR]\s\+\)\=[ckz]\=sh\>'
- set ft=zsh
-
- " ELM Mail files
- elseif s:line1 =~# '^From \([a-zA-Z][a-zA-Z_0-9\.=-]*\(@[^ ]*\)\=\|-\) .* \(19\|20\)\d\d$'
- set ft=mail
-
- " Mason
- elseif s:line1 =~# '^<[%&].*>'
- set ft=mason
-
- " Vim scripts (must have '" vim' as the first line to trigger this)
- elseif s:line1 =~# '^" *[vV]im$'
- set ft=vim
-
- " libcxx and libstdc++ standard library headers like "iostream" do not have
- " an extension, recognize the Emacs file mode.
- elseif s:line1 =~? '-\*-.*C++.*-\*-'
- set ft=cpp
-
- " MOO
- elseif s:line1 =~# '^\*\* LambdaMOO Database, Format Version \%([1-3]\>\)\@!\d\+ \*\*$'
- set ft=moo
-
- " 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)
- elseif s:line1 =~# '^\(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\)'
- \ || (s:line1 =~# '^--- ' && s:line2 =~# '^+++ ')
- \ || (s:line1 =~# '^\* looking for ' && s:line2 =~# '^\* comparing to ')
- \ || (s:line1 =~# '^\*\*\* ' && s:line2 =~# '^--- ')
- \ || (s:line1 =~# '^=== ' && ((s:line2 =~# '^=\{66\}' && s:line3 =~# '^--- ' && s:line4 =~# '^+++') || (s:line2 =~# '^--- ' && s:line3 =~# '^+++ ')))
- \ || (s:line1 =~# '^=== \(removed\|added\|renamed\|modified\)')
- set ft=diff
-
- " PostScript Files (must have %!PS as the first line, like a2ps output)
- elseif s:line1 =~# '^%![ \t]*PS'
- set ft=postscr
-
- " M4 scripts: Guess there is a line that starts with "dnl".
- elseif s:line1 =~# '^\s*dnl\>'
- \ || s:line2 =~# '^\s*dnl\>'
- \ || s:line3 =~# '^\s*dnl\>'
- \ || s:line4 =~# '^\s*dnl\>'
- \ || s:line5 =~# '^\s*dnl\>'
- set ft=m4
-
- " AmigaDos scripts
- elseif $TERM == "amiga"
- \ && (s:line1 =~# "^;" || s:line1 =~? '^\.bra')
- set ft=amiga
-
- " SiCAD scripts (must have procn or procd as the first line to trigger this)
- elseif s:line1 =~? '^ *proc[nd] *$'
- set ft=sicad
-
- " Purify log files start with "**** Purify"
- elseif s:line1 =~# '^\*\*\*\* Purify'
- set ft=purifylog
-
- " XML
- elseif s:line1 =~# '<?\s*xml.*?>'
- set ft=xml
-
- " XHTML (e.g.: PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN")
- elseif s:line1 =~# '\<DTD\s\+XHTML\s'
- set ft=xhtml
-
- " HTML (e.g.: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN")
- " Avoid "doctype html", used by slim.
- elseif s:line1 =~? '<!DOCTYPE\s\+html\>'
- set ft=html
-
- " PDF
- elseif s:line1 =~# '^%PDF-'
- set ft=pdf
-
- " XXD output
- elseif s:line1 =~# '^\x\{7}: \x\{2} \=\x\{2} \=\x\{2} \=\x\{2} '
- set ft=xxd
-
- " RCS/CVS log output
- elseif s:line1 =~# '^RCS file:' || s:line2 =~# '^RCS file:'
- set ft=rcslog
-
- " CVS commit
- elseif s:line2 =~# '^CVS:' || getline("$") =~# '^CVS: '
- set ft=cvs
-
- " Prescribe
- elseif s:line1 =~# '^!R!'
- set ft=prescribe
-
- " Send-pr
- elseif s:line1 =~# '^SEND-PR:'
- set ft=sendpr
-
- " SNNS files
- elseif s:line1 =~# '^SNNS network definition file'
- set ft=snnsnet
- elseif s:line1 =~# '^SNNS pattern definition file'
- set ft=snnspat
- elseif s:line1 =~# '^SNNS result file'
- set ft=snnsres
-
- " Virata
- elseif s:line1 =~# '^%.\{-}[Vv]irata'
- \ || s:line2 =~# '^%.\{-}[Vv]irata'
- \ || s:line3 =~# '^%.\{-}[Vv]irata'
- \ || s:line4 =~# '^%.\{-}[Vv]irata'
- \ || s:line5 =~# '^%.\{-}[Vv]irata'
- set ft=virata
-
- " Strace
- elseif s:line1 =~# '[0-9:.]* *execve(' || s:line1 =~# '^__libc_start_main'
- set ft=strace
-
- " VSE JCL
- elseif s:line1 =~# '^\* $$ JOB\>' || s:line1 =~# '^// *JOB\>'
- set ft=vsejcl
-
- " TAK and SINDA
- elseif s:line4 =~# 'K & K Associates' || s:line2 =~# 'TAK 2000'
- set ft=takout
- elseif s:line3 =~# 'S Y S T E M S I M P R O V E D '
- set ft=sindaout
- elseif getline(6) =~# 'Run Date: '
- set ft=takcmp
- elseif getline(9) =~# 'Node File 1'
- set ft=sindacmp
-
- " DNS zone files
- elseif s:line1.s:line2.s:line3.s:line4 =~# '^; <<>> DiG [0-9.]\+.* <<>>\|$ORIGIN\|$TTL\|IN\s\+SOA'
- set ft=bindzone
-
- " BAAN
- elseif s:line1 =~# '|\*\{1,80}' && s:line2 =~# 'VRC '
- \ || s:line2 =~# '|\*\{1,80}' && s:line3 =~# 'VRC '
- set ft=baan
-
- " Valgrind
- elseif s:line1 =~# '^==\d\+== valgrind' || s:line3 =~# '^==\d\+== Using valgrind'
- set ft=valgrind
-
- " Go docs
- elseif s:line1 =~# '^PACKAGE DOCUMENTATION$'
- set ft=godoc
-
- " Renderman Interface Bytestream
- elseif s:line1 =~# '^##RenderMan'
- set ft=rib
-
- " Scheme scripts
- elseif s:line1 =~# 'exec\s\+\S*scheme' || s:line2 =~# 'exec\s\+\S*scheme'
- set ft=scheme
-
- " Git output
- elseif s:line1 =~# '^\(commit\|tree\|object\) \x\{40,\}\>\|^tag \S\+$'
- set ft=git
-
- " Gprof (gnu profiler)
- elseif s:line1 == 'Flat profile:'
- \ && s:line2 == ''
- \ && s:line3 =~# '^Each sample counts as .* seconds.$'
- set ft=gprof
-
- " Erlang terms
- " (See also: http://www.gnu.org/software/emacs/manual/html_node/emacs/Choosing-Modes.html#Choosing-Modes)
- elseif s:line1 =~? '-\*-.*erlang.*-\*-'
- set ft=erlang
-
- " YAML
- elseif s:line1 =~# '^%YAML'
- set ft=yaml
-
- " MikroTik RouterOS script
- 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
- while getline(s:lnum) =~# "^? " && s:lnum < line("$")
- let s:lnum += 1
- endwhile
- if getline(s:lnum) =~# '^Index:\s\+\f\+$'
- set ft=diff
-
- " locale input files: Formal Definitions of Cultural Conventions
- " filename must be like en_US, fr_FR@euro or en_US.UTF-8
- elseif expand("%") =~# '\a\a_\a\a\($\|[.@]\)\|i18n$\|POSIX$\|translit_'
- let s:lnum = 1
- while s:lnum < 100 && s:lnum < line("$")
- if getline(s:lnum) =~# '^LC_\(IDENTIFICATION\|CTYPE\|COLLATE\|MONETARY\|NUMERIC\|TIME\|MESSAGES\|PAPER\|TELEPHONE\|MEASUREMENT\|NAME\|ADDRESS\)$'
- setf fdcc
- break
- endif
- let s:lnum += 1
- endwhile
- endif
- unlet s:lnum
-
- endif
-
- unlet s:line2 s:line3 s:line4 s:line5
-
-endif
-
-" Restore 'cpoptions'
-let &cpo = s:cpo_save
-
-unlet s:cpo_save s:line1
diff --git a/runtime/synmenu.vim b/runtime/synmenu.vim
index ba9e6a198d..a664e7689d 100644
--- a/runtime/synmenu.vim
+++ b/runtime/synmenu.vim
@@ -2,7 +2,7 @@
" This file is normally sourced from menu.vim.
"
" Maintainer: Bram Moolenaar <Bram@vim.org>
-" Last Change: 2017 Oct 28
+" Last Change: 2022 Oct 04
" Define the SetSyn function, used for the Syntax menu entries.
" Set 'filetype' and also 'syntax' if it is manually selected.
diff --git a/runtime/syntax/2html.vim b/runtime/syntax/2html.vim
index 8adbd76950..ce6797deaa 100644
--- a/runtime/syntax/2html.vim
+++ b/runtime/syntax/2html.vim
@@ -1,6 +1,6 @@
" Vim syntax support file
" Maintainer: Ben Fritz <fritzophrenic@gmail.com>
-" Last Change: 2020 Jan 05
+" Last Change: 2022 Dec 26
"
" Additional contributors:
"
@@ -815,202 +815,204 @@ endif
" HTML header, with the title and generator ;-). Left free space for the CSS,
" to be filled at the end.
-call extend(s:lines, [
- \ "<html>",
- \ "<head>"])
-" include encoding as close to the top as possible, but only if not already
-" contained in XML information (to avoid haggling over content type)
-if s:settings.encoding != "" && !s:settings.use_xhtml
- if s:html5
- call add(s:lines, '<meta charset="' . s:settings.encoding . '"' . s:tag_close)
- else
- call add(s:lines, "<meta http-equiv=\"content-type\" content=\"text/html; charset=" . s:settings.encoding . '"' . s:tag_close)
- endif
-endif
-call extend(s:lines, [
- \ ("<title>".expand("%:p:~")."</title>"),
- \ ("<meta name=\"Generator\" content=\"Vim/".v:version/100.".".v:version%100.'"'.s:tag_close),
- \ ("<meta name=\"plugin-version\" content=\"".s:pluginversion.'"'.s:tag_close)
- \ ])
-call add(s:lines, '<meta name="syntax" content="'.s:current_syntax.'"'.s:tag_close)
-call add(s:lines, '<meta name="settings" content="'.
- \ join(filter(keys(s:settings),'s:settings[v:val]'),',').
- \ ',prevent_copy='.s:settings.prevent_copy.
- \ ',use_input_for_pc='.s:settings.use_input_for_pc.
- \ '"'.s:tag_close)
-call add(s:lines, '<meta name="colorscheme" content="'.
- \ (exists('g:colors_name')
- \ ? g:colors_name
- \ : 'none'). '"'.s:tag_close)
-
-if s:settings.use_css
+if !s:settings.no_doc
call extend(s:lines, [
- \ "<style" . (s:html5 ? "" : " type=\"text/css\"") . ">",
- \ s:settings.use_xhtml ? "" : "<!--"])
- let s:ieonly = []
- if s:settings.dynamic_folds
- if s:settings.hover_unfold
- " if we are doing hover_unfold, use css 2 with css 1 fallback for IE6
- call extend(s:lines, [
- \ ".FoldColumn { text-decoration: none; white-space: pre; }",
- \ "",
- \ "body * { margin: 0; padding: 0; }", "",
- \ ".open-fold > span.Folded { display: none; }",
- \ ".open-fold > .fulltext { display: inline; }",
- \ ".closed-fold > .fulltext { display: none; }",
- \ ".closed-fold > span.Folded { display: inline; }",
- \ "",
- \ ".open-fold > .toggle-open { display: none; }",
- \ ".open-fold > .toggle-closed { display: inline; }",
- \ ".closed-fold > .toggle-open { display: inline; }",
- \ ".closed-fold > .toggle-closed { display: none; }",
- \ "", "",
- \ '/* opening a fold while hovering won''t be supported by IE6 and other',
- \ "similar browsers, but it should fail gracefully. */",
- \ ".closed-fold:hover > .fulltext { display: inline; }",
- \ ".closed-fold:hover > .toggle-filler { display: none; }",
- \ ".closed-fold:hover > .Folded { display: none; }"])
- " TODO: IE6 is REALLY old and I can't even test it anymore. Maybe we
- " should remove this? Leave it in for now, it was working at one point,
- " and doesn't affect any modern browsers. Even newer IE versions should
- " support the above code and ignore the following.
- let s:ieonly = [
- \ "<!--[if lt IE 7]><style type=\"text/css\">",
- \ ".open-fold .fulltext { display: inline; }",
- \ ".open-fold span.Folded { display: none; }",
- \ ".open-fold .toggle-open { display: none; }",
- \ ".open-fold .toggle-closed { display: inline; }",
- \ "",
- \ ".closed-fold .fulltext { display: none; }",
- \ ".closed-fold span.Folded { display: inline; }",
- \ ".closed-fold .toggle-open { display: inline; }",
- \ ".closed-fold .toggle-closed { display: none; }",
- \ "</style>",
- \ "<![endif]-->",
- \]
+ \ "<html>",
+ \ "<head>"])
+ " include encoding as close to the top as possible, but only if not already
+ " contained in XML information (to avoid haggling over content type)
+ if s:settings.encoding != "" && !s:settings.use_xhtml
+ if s:html5
+ call add(s:lines, '<meta charset="' . s:settings.encoding . '"' . s:tag_close)
else
- " if we aren't doing hover_unfold, use CSS 1 only
- call extend(s:lines, [
- \ ".FoldColumn { text-decoration: none; white-space: pre; }",
- \ ".open-fold .fulltext { display: inline; }",
- \ ".open-fold span.Folded { display: none; }",
- \ ".open-fold .toggle-open { display: none; }",
- \ ".open-fold .toggle-closed { display: inline; }",
- \ "",
- \ ".closed-fold .fulltext { display: none; }",
- \ ".closed-fold span.Folded { display: inline; }",
- \ ".closed-fold .toggle-open { display: inline; }",
- \ ".closed-fold .toggle-closed { display: none; }",
- \])
+ call add(s:lines, "<meta http-equiv=\"content-type\" content=\"text/html; charset=" . s:settings.encoding . '"' . s:tag_close)
endif
endif
- " else we aren't doing any dynamic folding, no need for any special rules
-
call extend(s:lines, [
- \ s:settings.use_xhtml ? "" : '-->',
- \ "</style>",
- \])
- call extend(s:lines, s:ieonly)
- unlet s:ieonly
-endif
+ \ ("<title>".expand("%:p:~")."</title>"),
+ \ ("<meta name=\"Generator\" content=\"Vim/".v:version/100.".".v:version%100.'"'.s:tag_close),
+ \ ("<meta name=\"plugin-version\" content=\"".s:pluginversion.'"'.s:tag_close)
+ \ ])
+ call add(s:lines, '<meta name="syntax" content="'.s:current_syntax.'"'.s:tag_close)
+ call add(s:lines, '<meta name="settings" content="'.
+ \ join(filter(keys(s:settings),'s:settings[v:val]'),',').
+ \ ',prevent_copy='.s:settings.prevent_copy.
+ \ ',use_input_for_pc='.s:settings.use_input_for_pc.
+ \ '"'.s:tag_close)
+ call add(s:lines, '<meta name="colorscheme" content="'.
+ \ (exists('g:colors_name')
+ \ ? g:colors_name
+ \ : 'none'). '"'.s:tag_close)
-let s:uses_script = s:settings.dynamic_folds || s:settings.line_ids
+ if s:settings.use_css
+ call extend(s:lines, [
+ \ "<style" . (s:html5 ? "" : " type=\"text/css\"") . ">",
+ \ s:settings.use_xhtml ? "" : "<!--"])
+ let s:ieonly = []
+ if s:settings.dynamic_folds
+ if s:settings.hover_unfold
+ " if we are doing hover_unfold, use css 2 with css 1 fallback for IE6
+ call extend(s:lines, [
+ \ ".FoldColumn { text-decoration: none; white-space: pre; }",
+ \ "",
+ \ "body * { margin: 0; padding: 0; }", "",
+ \ ".open-fold > span.Folded { display: none; }",
+ \ ".open-fold > .fulltext { display: inline; }",
+ \ ".closed-fold > .fulltext { display: none; }",
+ \ ".closed-fold > span.Folded { display: inline; }",
+ \ "",
+ \ ".open-fold > .toggle-open { display: none; }",
+ \ ".open-fold > .toggle-closed { display: inline; }",
+ \ ".closed-fold > .toggle-open { display: inline; }",
+ \ ".closed-fold > .toggle-closed { display: none; }",
+ \ "", "",
+ \ '/* opening a fold while hovering won''t be supported by IE6 and other',
+ \ "similar browsers, but it should fail gracefully. */",
+ \ ".closed-fold:hover > .fulltext { display: inline; }",
+ \ ".closed-fold:hover > .toggle-filler { display: none; }",
+ \ ".closed-fold:hover > .Folded { display: none; }"])
+ " TODO: IE6 is REALLY old and I can't even test it anymore. Maybe we
+ " should remove this? Leave it in for now, it was working at one point,
+ " and doesn't affect any modern browsers. Even newer IE versions should
+ " support the above code and ignore the following.
+ let s:ieonly = [
+ \ "<!--[if lt IE 7]><style type=\"text/css\">",
+ \ ".open-fold .fulltext { display: inline; }",
+ \ ".open-fold span.Folded { display: none; }",
+ \ ".open-fold .toggle-open { display: none; }",
+ \ ".open-fold .toggle-closed { display: inline; }",
+ \ "",
+ \ ".closed-fold .fulltext { display: none; }",
+ \ ".closed-fold span.Folded { display: inline; }",
+ \ ".closed-fold .toggle-open { display: inline; }",
+ \ ".closed-fold .toggle-closed { display: none; }",
+ \ "</style>",
+ \ "<![endif]-->",
+ \]
+ else
+ " if we aren't doing hover_unfold, use CSS 1 only
+ call extend(s:lines, [
+ \ ".FoldColumn { text-decoration: none; white-space: pre; }",
+ \ ".open-fold .fulltext { display: inline; }",
+ \ ".open-fold span.Folded { display: none; }",
+ \ ".open-fold .toggle-open { display: none; }",
+ \ ".open-fold .toggle-closed { display: inline; }",
+ \ "",
+ \ ".closed-fold .fulltext { display: none; }",
+ \ ".closed-fold span.Folded { display: inline; }",
+ \ ".closed-fold .toggle-open { display: inline; }",
+ \ ".closed-fold .toggle-closed { display: none; }",
+ \])
+ endif
+ endif
+ " else we aren't doing any dynamic folding, no need for any special rules
-" insert script tag if needed
-if s:uses_script
- call extend(s:lines, [
- \ "",
- \ "<script" . (s:html5 ? "" : " type='text/javascript'") . ">",
- \ s:settings.use_xhtml ? '//<![CDATA[' : "<!--"])
-endif
+ call extend(s:lines, [
+ \ s:settings.use_xhtml ? "" : '-->',
+ \ "</style>",
+ \])
+ call extend(s:lines, s:ieonly)
+ unlet s:ieonly
+ endif
-" insert javascript to toggle folds open and closed
-if s:settings.dynamic_folds
- call extend(s:lines, [
- \ "",
- \ "function toggleFold(objID)",
- \ "{",
- \ " var fold;",
- \ " fold = document.getElementById(objID);",
- \ " if (fold.className == 'closed-fold')",
- \ " {",
- \ " fold.className = 'open-fold';",
- \ " }",
- \ " else if (fold.className == 'open-fold')",
- \ " {",
- \ " fold.className = 'closed-fold';",
- \ " }",
- \ "}"
- \ ])
-endif
+ let s:uses_script = s:settings.dynamic_folds || s:settings.line_ids
-if s:settings.line_ids
- " insert javascript to get IDs from line numbers, and to open a fold before
- " jumping to any lines contained therein
- call extend(s:lines, [
- \ "",
- \ "/* function to open any folds containing a jumped-to line before jumping to it */",
- \ "function JumpToLine()",
- \ "{",
- \ " var lineNum;",
- \ " lineNum = window.location.hash;",
- \ " lineNum = lineNum.substr(1); /* strip off '#' */",
- \ "",
- \ " if (lineNum.indexOf('L') == -1) {",
- \ " lineNum = 'L'+lineNum;",
- \ " }",
- \ " var lineElem = document.getElementById(lineNum);"
- \ ])
+ " insert script tag if needed
+ if s:uses_script
+ call extend(s:lines, [
+ \ "",
+ \ "<script" . (s:html5 ? "" : " type='text/javascript'") . ">",
+ \ s:settings.use_xhtml ? '//<![CDATA[' : "<!--"])
+ endif
+ " insert javascript to toggle folds open and closed
if s:settings.dynamic_folds
call extend(s:lines, [
\ "",
- \ " /* navigate upwards in the DOM tree to open all folds containing the line */",
- \ " var node = lineElem;",
- \ " while (node && node.id != 'vimCodeElement".s:settings.id_suffix."')",
+ \ "function toggleFold(objID)",
+ \ "{",
+ \ " var fold;",
+ \ " fold = document.getElementById(objID);",
+ \ " if (fold.className == 'closed-fold')",
+ \ " {",
+ \ " fold.className = 'open-fold';",
+ \ " }",
+ \ " else if (fold.className == 'open-fold')",
\ " {",
- \ " if (node.className == 'closed-fold')",
- \ " {",
- \ " node.className = 'open-fold';",
- \ " }",
- \ " node = node.parentNode;",
+ \ " fold.className = 'closed-fold';",
\ " }",
+ \ "}"
\ ])
endif
- call extend(s:lines, [
- \ " /* Always jump to new location even if the line was hidden inside a fold, or",
- \ " * we corrected the raw number to a line ID.",
- \ " */",
- \ " if (lineElem) {",
- \ " lineElem.scrollIntoView(true);",
- \ " }",
- \ " return true;",
- \ "}",
- \ "if ('onhashchange' in window) {",
- \ " window.onhashchange = JumpToLine;",
- \ "}"
- \ ])
-endif
-" insert script closing tag if needed
-if s:uses_script
- call extend(s:lines, [
- \ '',
- \ s:settings.use_xhtml ? '//]]>' : '-->',
- \ "</script>"
- \ ])
-endif
+ if s:settings.line_ids
+ " insert javascript to get IDs from line numbers, and to open a fold before
+ " jumping to any lines contained therein
+ call extend(s:lines, [
+ \ "",
+ \ "/* function to open any folds containing a jumped-to line before jumping to it */",
+ \ "function JumpToLine()",
+ \ "{",
+ \ " var lineNum;",
+ \ " lineNum = window.location.hash;",
+ \ " lineNum = lineNum.substr(1); /* strip off '#' */",
+ \ "",
+ \ " if (lineNum.indexOf('L') == -1) {",
+ \ " lineNum = 'L'+lineNum;",
+ \ " }",
+ \ " var lineElem = document.getElementById(lineNum);"
+ \ ])
+
+ if s:settings.dynamic_folds
+ call extend(s:lines, [
+ \ "",
+ \ " /* navigate upwards in the DOM tree to open all folds containing the line */",
+ \ " var node = lineElem;",
+ \ " while (node && node.id != 'vimCodeElement".s:settings.id_suffix."')",
+ \ " {",
+ \ " if (node.className == 'closed-fold')",
+ \ " {",
+ \ " node.className = 'open-fold';",
+ \ " }",
+ \ " node = node.parentNode;",
+ \ " }",
+ \ ])
+ endif
+ call extend(s:lines, [
+ \ " /* Always jump to new location even if the line was hidden inside a fold, or",
+ \ " * we corrected the raw number to a line ID.",
+ \ " */",
+ \ " if (lineElem) {",
+ \ " lineElem.scrollIntoView(true);",
+ \ " }",
+ \ " return true;",
+ \ "}",
+ \ "if ('onhashchange' in window) {",
+ \ " window.onhashchange = JumpToLine;",
+ \ "}"
+ \ ])
+ endif
-call extend(s:lines, ["</head>",
- \ "<body".(s:settings.line_ids ? " onload='JumpToLine();'" : "").">"])
+ " insert script closing tag if needed
+ if s:uses_script
+ call extend(s:lines, [
+ \ '',
+ \ s:settings.use_xhtml ? '//]]>' : '-->',
+ \ "</script>"
+ \ ])
+ endif
+
+ call extend(s:lines, ["</head>",
+ \ "<body".(s:settings.line_ids ? " onload='JumpToLine();'" : "").">"])
+endif
if s:settings.no_pre
" if we're not using CSS we use a font tag which can't have a div inside
if s:settings.use_css
- call extend(s:lines, ["<div id='vimCodeElement".s:settings.id_suffix."'>"])
+ call extend(s:lines, ["<div id='vimCodeElement" .. s:settings.id_suffix .. "'>"])
endif
else
- call extend(s:lines, ["<pre id='vimCodeElement".s:settings.id_suffix."'>"])
+ call extend(s:lines, ["<pre id='vimCodeElement" .. s:settings.id_suffix .. "'>"])
endif
exe s:orgwin . "wincmd w"
@@ -1721,12 +1723,15 @@ endif
if s:settings.no_pre
if !s:settings.use_css
" Close off the font tag that encapsulates the whole <body>
- call extend(s:lines, ["</font>", "</body>", "</html>"])
+ call extend(s:lines, ["</font>"])
else
- call extend(s:lines, ["</div>", "</body>", "</html>"])
+ call extend(s:lines, ["</div>"])
endif
else
- call extend(s:lines, ["</pre>", "</body>", "</html>"])
+ call extend(s:lines, ["</pre>"])
+endif
+if !s:settings.no_doc
+ call extend(s:lines, ["</body>", "</html>"])
endif
exe s:newwin . "wincmd w"
@@ -1742,15 +1747,15 @@ unlet s:lines
" The generated HTML is admittedly ugly and takes a LONG time to fold.
" Make sure the user doesn't do syntax folding when loading a generated file,
" using a modeline.
-call append(line('$'), "<!-- vim: set foldmethod=manual : -->")
+if !s:settings.no_modeline
+ call append(line('$'), "<!-- vim: set foldmethod=manual : -->")
+endif
" Now, when we finally know which, we define the colors and styles
-if s:settings.use_css
+if s:settings.use_css && !s:settings.no_doc
1;/<style\>/+1
-endif
-" Normal/global attributes
-if s:settings.use_css
+ " Normal/global attributes
if s:settings.no_pre
call append('.', "body { color: " . s:fgc . "; background-color: " . s:bgc . "; font-family: ". s:htmlfont ."; }")
+
@@ -1874,7 +1879,9 @@ if s:settings.use_css
endif
endif
endif
-else
+endif
+
+if !s:settings.use_css && !s:settings.no_doc
" For Netscape 4, set <body> attributes too, though, strictly speaking, it's
" incorrect.
execute '%s:<body\([^>]*\):<body bgcolor="' . s:bgc . '" text="' . s:fgc . '"\1>\r<font face="'. s:htmlfont .'"'
@@ -1882,7 +1889,7 @@ endif
" Gather attributes for all other classes. Do diff first so that normal
" highlight groups are inserted before it.
-if s:settings.use_css
+if s:settings.use_css && !s:settings.no_doc
if s:diff_mode
call append('.', filter(map(keys(s:diffstylelist), "s:diffstylelist[v:val]"), 'v:val != ""'))
endif
@@ -1892,20 +1899,22 @@ if s:settings.use_css
endif
" Add hyperlinks
-" TODO: add option to not do this? Maybe just make the color the same as the
-" text highlight group normally is?
-%s+\(https\=://\S\{-}\)\(\([.,;:}]\=\(\s\|$\)\)\|[\\"'<>]\|&gt;\|&lt;\|&quot;\)+<a href="\1">\1</a>\2+ge
+if !s:settings.no_links
+ %s+\(https\=://\S\{-}\)\(\([.,;:}]\=\(\s\|$\)\)\|[\\"'<>]\|&gt;\|&lt;\|&quot;\)+<a href="\1">\1</a>\2+ge
+endif
" The DTD
-if s:settings.use_xhtml
- exe "normal! gg$a\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"
-elseif s:html5
- exe "normal! gg0i<!DOCTYPE html>\n"
-else
- exe "normal! gg0i<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n"
+if !s:settings.no_doc
+ if s:settings.use_xhtml
+ exe "normal! gg$a\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"
+ elseif s:html5
+ exe "normal! gg0i<!DOCTYPE html>\n"
+ else
+ exe "normal! gg0i<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n"
+ endif
endif
-if s:settings.use_xhtml
+if s:settings.use_xhtml && !s:settings.no_doc
exe "normal! gg/<html/e\na xmlns=\"http://www.w3.org/1999/xhtml\"\e"
endif
diff --git a/runtime/syntax/c.vim b/runtime/syntax/c.vim
index 890e9ae1a7..50878a78ea 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: 2022 Apr 24
+" Last Change: 2022 Oct 05
" Quit when a (custom) syntax file was already loaded
if exists("b:current_syntax")
diff --git a/runtime/syntax/cabal.vim b/runtime/syntax/cabal.vim
index 92e6b8331e..74cda51266 100644
--- a/runtime/syntax/cabal.vim
+++ b/runtime/syntax/cabal.vim
@@ -4,7 +4,9 @@
" Maintainer: Marcin Szamotulski <profunctor@pm.me>
" Previous Maintainer: Vincent Berthoux <twinside@gmail.com>
" File Types: .cabal
-" Last Change: 21 Nov 2020
+" Last Change: 22 Oct 2022
+" v1.6: Added support for foreign-libraries
+" Added highlighting for various fields
" v1.5: Incorporated changes from
" https://github.com/sdiehl/haskell-vim-proto/blob/master/vim/syntax/cabal.vim
" Use `syn keyword` instead of `syn match`.
@@ -61,13 +63,14 @@ syn keyword cabalCategory contained
\ test-suite
\ source-repository
\ flag
+ \ foreign-library
\ custom-setup
\ common
syn match cabalCategoryTitle contained /[^{]*\ze{\?/
syn match cabalCategoryRegion
\ contains=cabalCategory,cabalCategoryTitle
\ nextgroup=cabalCategory skipwhite
- \ /^\c\s*\(contained\|executable\|library\|benchmark\|test-suite\|source-repository\|flag\|custom-setup\|common\)\+\s*\%(.*$\|$\)/
+ \ /^\c\s*\(contained\|executable\|library\|benchmark\|test-suite\|source-repository\|flag\|foreign-library\|custom-setup\|common\)\+\s*\%(.*$\|$\)/
syn keyword cabalTruth true false
" cabalStatementRegion which limits the scope of cabalStatement keywords, this
@@ -77,6 +80,7 @@ syn keyword cabalStatement contained containedin=cabalStatementRegion
\ default-language
\ default-extensions
\ author
+ \ autogen-includes
\ autogen-modules
\ asm-sources
\ asm-options
@@ -84,7 +88,7 @@ syn keyword cabalStatement contained containedin=cabalStatementRegion
\ bug-reports
\ build-depends
\ build-tools
- \ build-tools-depends
+ \ build-tool-depends
\ build-type
\ buildable
\ c-sources
@@ -95,6 +99,7 @@ syn keyword cabalStatement contained containedin=cabalStatementRegion
\ cmm-sources
\ cmm-options
\ cpp-options
+ \ cxx-options
\ cxx-sources
\ data-dir
\ data-files
@@ -111,7 +116,9 @@ syn keyword cabalStatement contained containedin=cabalStatementRegion
\ extra-framework-dirs
\ extra-ghci-libraries
\ extra-lib-dirs
+ \ extra-lib-dirs-static
\ extra-libraries
+ \ extra-libraries-static
\ extra-library-flavours
\ extra-source-files
\ extra-tmp-files
@@ -133,6 +140,8 @@ syn keyword cabalStatement contained containedin=cabalStatementRegion
\ install-includes
\ js-sources
\ ld-options
+ \ lib-version-info
+ \ lib-version-linux
\ license
\ license-file
\ location
@@ -141,20 +150,26 @@ syn keyword cabalStatement contained containedin=cabalStatementRegion
\ manual
\ mixins
\ module
+ \ mod-def-file
\ name
\ nhc98-options
+ \ options
\ other-extensions
\ other-language
\ other-languages
\ other-modules
\ package-url
\ pkgconfig-depends
+ \ scope
\ setup-depends
+ \ signatures
\ stability
\ subdir
\ synopsis
+ \ reexported-modules
\ tag
\ tested-with
+ \ test-module
\ type
\ version
\ virtual-modules
diff --git a/runtime/syntax/chatito.vim b/runtime/syntax/chatito.vim
new file mode 100644
index 0000000000..d89307cf06
--- /dev/null
+++ b/runtime/syntax/chatito.vim
@@ -0,0 +1,62 @@
+" Vim syntax file
+" Language: Chatito
+" Maintainer: ObserverOfTime <chronobserver@disroot.org>
+" Filenames: *.chatito
+" Last Change: 2022 Sep 19
+
+if exists('b:current_syntax')
+ finish
+endif
+
+" Comment
+syn keyword chatitoTodo contained TODO FIXME XXX
+syn match chatitoComment /^#.*/ contains=chatitoTodo,@Spell
+syn match chatitoComment +^//.*+ contains=chatitoTodo,@Spell
+
+" Import
+syn match chatitoImport /^import \+.*$/ transparent contains=chatitoImportKeyword,chatitoImportFile
+syn keyword chatitoImportKeyword import contained nextgroup=chatitoImportFile
+syn match chatitoImportFile /.*$/ contained skipwhite
+
+" Intent
+syn match chatitoIntent /^%\[[^\]?]\+\]\((.\+)\)\=$/ contains=chatitoArgs
+
+" Slot
+syn match chatitoSlot /^@\[[^\]?#]\+\(#[^\]?#]\+\)\=\]\((.\+)\)\=$/ contains=chatitoArgs,chatitoVariation
+syn match chatitoSlot /@\[[^\]?#]\+\(#[^\]?#]\+\)\=?\=\]/ contained contains=chatitoOpt,chatitoVariation
+
+" Alias
+syn match chatitoAlias /^\~\[[^\]?]\+\]\=$/
+syn match chatitoAlias /\~\[[^\]?]\+?\=\]/ contained contains=chatitoOpt
+
+" Probability
+syn match chatitoProbability /\*\[\d\+\(\.\d\+\)\=%\=\]/ contained
+
+" Optional
+syn match chatitoOpt '?' contained
+
+" Arguments
+syn match chatitoArgs /(.\+)/ contained
+
+" Variation
+syn match chatitoVariation /#[^\]?#]\+/ contained
+
+" Value
+syn match chatitoValue /^ \{4\}\zs.\+$/ contains=chatitoProbability,chatitoSlot,chatitoAlias,@Spell
+
+" Errors
+syn match chatitoError /^\t/
+
+hi def link chatitoAlias String
+hi def link chatitoArgs Special
+hi def link chatitoComment Comment
+hi def link chatitoError Error
+hi def link chatitoImportKeyword Include
+hi def link chatitoIntent Statement
+hi def link chatitoOpt SpecialChar
+hi def link chatitoProbability Number
+hi def link chatitoSlot Identifier
+hi def link chatitoTodo Todo
+hi def link chatitoVariation Special
+
+let b:current_syntax = 'chatito'
diff --git a/runtime/syntax/checkhealth.vim b/runtime/syntax/checkhealth.vim
index 37f1822740..4b0ce75a54 100644
--- a/runtime/syntax/checkhealth.vim
+++ b/runtime/syntax/checkhealth.vim
@@ -1,26 +1,21 @@
" Vim syntax file
-" Language: Neovim checkhealth buffer
-" Last Change: 2021 Dec 15
+" Language: Nvim :checkhealth buffer
+" Last Change: 2022 Nov 10
if exists("b:current_syntax")
finish
endif
-runtime! syntax/markdown.vim
+runtime! syntax/help.vim
unlet! b:current_syntax
syn case match
-" We do not care about markdown syntax errors
-if hlexists('markdownError')
- syn clear markdownError
-endif
-
-syn keyword healthError ERROR[:] containedin=markdownCodeBlock,mkdListItemLine
-syn keyword healthWarning WARNING[:] containedin=markdownCodeBlock,mkdListItemLine
-syn keyword healthSuccess OK[:] containedin=markdownCodeBlock,mkdListItemLine
-syn match healthHelp "|.\{-}|" containedin=markdownCodeBlock,mkdListItemLine contains=healthBar
-syn match healthBar "|" contained conceal
+syn keyword healthError ERROR[:]
+syn keyword healthWarning WARNING[:]
+syn keyword healthSuccess OK[:]
+syn match helpSectionDelim "^======*\n.*$"
+syn match healthHeadingChar "=" conceal cchar=─ contained containedin=helpSectionDelim
hi def link healthError Error
hi def link healthWarning WarningMsg
diff --git a/runtime/syntax/cs.vim b/runtime/syntax/cs.vim
index 722ddbedf6..104470ac4b 100644
--- a/runtime/syntax/cs.vim
+++ b/runtime/syntax/cs.vim
@@ -3,7 +3,7 @@
" Maintainer: Nick Jensen <nickspoon@gmail.com>
" Former Maintainers: Anduin Withers <awithers@anduin.com>
" Johannes Zellner <johannes@zellner.org>
-" Last Change: 2022-03-01
+" Last Change: 2022-11-16
" Filenames: *.cs
" License: Vim (see :h license)
" Repository: https://github.com/nickspoons/vim-cs
@@ -25,6 +25,9 @@ syn keyword csType bool byte char decimal double float int long object sbyte sho
syn keyword csType nint nuint " contextual
syn keyword csStorage enum interface namespace struct
+syn match csStorage "\<record\ze\_s\+@\=\h\w*\_s*[<(:{;]"
+syn match csStorage "\%(\<\%(partial\|new\|public\|protected\|internal\|private\|abstract\|sealed\|static\|unsafe\|readonly\)\)\@9<=\_s\+record\>"
+syn match csStorage "\<record\ze\_s\+\%(class\|struct\)"
syn match csStorage "\<delegate\>"
syn keyword csRepeat break continue do for foreach goto return while
syn keyword csConditional else if switch
@@ -44,6 +47,9 @@ syn keyword csManagedModifier managed unmanaged contained
" Modifiers
syn match csUsingModifier "\<global\ze\_s\+using\>"
syn keyword csAccessModifier internal private protected public
+syn keyword csModifier operator nextgroup=csCheckedModifier skipwhite skipempty
+syn keyword csCheckedModifier checked contained
+
" TODO: in new out
syn keyword csModifier abstract const event override readonly sealed static virtual volatile
syn match csModifier "\<\%(extern\|fixed\|unsafe\)\>"
@@ -76,7 +82,7 @@ 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 csUnspecifiedStatement as in is nameof out params ref sizeof stackalloc using
syn keyword csUnsupportedStatement value
syn keyword csUnspecifiedKeyword explicit implicit
@@ -183,7 +189,7 @@ syn match csUnicodeNumber +\\u\x\{4}+ contained contains=csUnicodeSpecifier disp
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 region csString matchgroup=csQuote start=+"+ end=+"\%(u8\)\=+ 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
@@ -200,7 +206,7 @@ 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 csInterpolatedString matchgroup=csQuote start=+\$"+ end=+"\%(u8\)\=+ 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
@@ -210,10 +216,10 @@ syn match csInterpolationFormat +:[^}]\+}+ contained contains=csInterpolationFor
syn match csInterpolationAlignDel +,+ contained display
syn match csInterpolationFormatDel +:+ contained display
-syn region csVerbatimString matchgroup=csQuote start=+@"+ end=+"+ skip=+""+ extend contains=csVerbatimQuote,@Spell
+syn region csVerbatimString matchgroup=csQuote start=+@"+ end=+"\%(u8\)\=+ skip=+""+ extend contains=csVerbatimQuote,@Spell
syn match csVerbatimQuote +""+ contained
-syn region csInterVerbString matchgroup=csQuote start=+$@"+ start=+@$"+ end=+"+ skip=+""+ extend contains=csInterpolation,csEscapedInterpolation,csSpecialChar,csSpecialError,csUnicodeNumber,csVerbatimQuote,@Spell
+syn region csInterVerbString matchgroup=csQuote start=+$@"+ start=+@$"+ end=+"\%(u8\)\=+ skip=+""+ extend contains=csInterpolation,csEscapedInterpolation,csSpecialChar,csSpecialError,csUnicodeNumber,csVerbatimQuote,@Spell
syn cluster csString contains=csString,csInterpolatedString,csVerbatimString,csInterVerbString
@@ -256,6 +262,7 @@ hi def link csException Exception
hi def link csModifier StorageClass
hi def link csAccessModifier csModifier
hi def link csAsyncModifier csModifier
+hi def link csCheckedModifier csModifier
hi def link csManagedModifier csModifier
hi def link csUsingModifier csModifier
diff --git a/runtime/syntax/debchangelog.vim b/runtime/syntax/debchangelog.vim
index 9bd836801e..691c3778c1 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: 2022 Jul 25
+" Last Change: 2022 Oct 29
" URL: https://salsa.debian.org/vim-team/vim-debian/blob/master/syntax/debchangelog.vim
" Standard syntax initialization
@@ -21,9 +21,9 @@ let s:cpo = &cpo
set cpo-=C
let s:supported = [
\ 'oldstable', 'stable', 'testing', 'unstable', 'experimental', 'sid', 'rc-buggy',
- \ 'buster', 'bullseye', 'bookworm', 'trixie',
+ \ 'buster', 'bullseye', 'bookworm', 'trixie', 'forky',
\
- \ 'trusty', 'xenial', 'bionic', 'focal', 'jammy', 'kinetic',
+ \ 'trusty', 'xenial', 'bionic', 'focal', 'jammy', 'kinetic', 'lunar',
\ 'devel'
\ ]
let s:unsupported = [
diff --git a/runtime/syntax/debsources.vim b/runtime/syntax/debsources.vim
index ea9e59ea8e..9b75797b54 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: 2022 Jul 25
+" Last Change: 2022 Oct 29
" URL: https://salsa.debian.org/vim-team/vim-debian/blob/master/syntax/debsources.vim
" Standard syntax initialization
@@ -23,9 +23,9 @@ let s:cpo = &cpo
set cpo-=C
let s:supported = [
\ 'oldstable', 'stable', 'testing', 'unstable', 'experimental', 'sid', 'rc-buggy',
- \ 'buster', 'bullseye', 'bookworm', 'trixie',
+ \ 'buster', 'bullseye', 'bookworm', 'trixie', 'forky',
\
- \ 'trusty', 'xenial', 'bionic', 'focal', 'jammy', 'kinetic',
+ \ 'trusty', 'xenial', 'bionic', 'focal', 'jammy', 'kinetic', 'lunar',
\ 'devel'
\ ]
let s:unsupported = [
diff --git a/runtime/syntax/desktop.vim b/runtime/syntax/desktop.vim
index 2c1102238d..461ba855b9 100644
--- a/runtime/syntax/desktop.vim
+++ b/runtime/syntax/desktop.vim
@@ -3,7 +3,7 @@
" Filenames: *.desktop, *.directory
" Maintainer: Eisuke Kawashima ( e.kawaschima+vim AT gmail.com )
" Previous Maintainer: Mikolaj Machowski ( mikmach AT wp DOT pl )
-" Last Change: 2020-06-11
+" Last Change: 2022 Sep 22
" Version Info: desktop.vim 1.5
" References:
" - https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.5.html (2020-04-27)
@@ -60,10 +60,10 @@ syn match dtLocaleSuffix
" Boolean Value {{{2
syn match dtBoolean
- \ /^\%(DBusActivatable\|Hidden\|NoDisplay\|PrefersNonDefaultGPU\|StartupNotify\|Terminal\)\s*=\s*\%(true\|false\)/
+ \ /^\%(DBusActivatable\|Hidden\|NoDisplay\|PrefersNonDefaultGPU\|SingleMainWindow\|StartupNotify\|Terminal\)\s*=\s*\%(true\|false\)/
\ contains=dtBooleanKey,dtDelim,dtBooleanValue transparent
syn keyword dtBooleanKey
- \ DBusActivatable Hidden NoDisplay PrefersNonDefaultGPU StartupNotify Terminal
+ \ DBusActivatable Hidden NoDisplay PrefersNonDefaultGPU SingleMainWindow StartupNotify Terminal
\ contained nextgroup=dtDelim
if s:desktop_enable_kde
diff --git a/runtime/syntax/editorconfig.vim b/runtime/syntax/editorconfig.vim
new file mode 100644
index 0000000000..4392d2b2d5
--- /dev/null
+++ b/runtime/syntax/editorconfig.vim
@@ -0,0 +1,17 @@
+runtime! syntax/dosini.vim
+unlet! b:current_syntax
+
+syntax match editorconfigUnknownProperty "^\s*\zs\w\+\ze\s*="
+syntax keyword editorconfigProperty root
+
+lua<<
+local props = {}
+for k in pairs(require('editorconfig').properties) do
+ props[#props + 1] = k
+end
+vim.cmd(string.format('syntax keyword editorconfigProperty %s', table.concat(props, ' ')))
+.
+
+hi def link editorconfigProperty dosiniLabel
+
+let b:current_syntax = 'editorconfig'
diff --git a/runtime/syntax/erlang.vim b/runtime/syntax/erlang.vim
index b8cbf07bb2..0b256193ab 100644
--- a/runtime/syntax/erlang.vim
+++ b/runtime/syntax/erlang.vim
@@ -2,7 +2,7 @@
" Language: Erlang (http://www.erlang.org)
" Maintainer: Csaba Hoch <csaba.hoch@gmail.com>
" Contributor: Adam Rutkowski <hq@mtod.org>
-" Last Update: 2020-May-26
+" Last Update: 2022-Sep-06
" License: Vim license
" URL: https://github.com/vim-erlang/vim-erlang-runtime
@@ -61,7 +61,8 @@ syn match erlangQuotedAtomModifier '\\\%(\o\{1,3}\|x\x\x\|x{\x\+}\|\^.\|.\)' con
syn match erlangModifier '\$\%([^\\]\|\\\%(\o\{1,3}\|x\x\x\|x{\x\+}\|\^.\|.\)\)'
" Operators, separators
-syn match erlangOperator '==\|=:=\|/=\|=/=\|<\|=<\|>\|>=\|=>\|:=\|++\|--\|=\|!\|<-\|+\|-\|\*\|\/'
+syn match erlangOperator '==\|=:=\|/=\|=/=\|<\|=<\|>\|>=\|=>\|:=\|?=\|++\|--\|=\|!\|<-\|+\|-\|\*\|\/'
+syn match erlangEqualsBinary '=<<\%(<\)\@!'
syn keyword erlangOperator div rem or xor bor bxor bsl bsr and band not bnot andalso orelse
syn match erlangBracket '{\|}\|\[\|]\||\|||'
syn match erlangPipe '|'
@@ -76,7 +77,8 @@ syn match erlangGlobalFuncCall '\<\%(\a[[:alnum:]_@]*\%(\s\|\n\|%.*\n\)*\.\%(\s\
syn match erlangGlobalFuncRef '\<\%(\a[[:alnum:]_@]*\%(\s\|\n\|%.*\n\)*\.\%(\s\|\n\|%.*\n\)*\)*\a[[:alnum:]_@]*\%(\s\|\n\|%.*\n\)*:\%(\s\|\n\|%.*\n\)*\a[[:alnum:]_@]*\>\%(\%(\s\|\n\|%.*\n\)*/\)\@=' contains=erlangComment,erlangVariable
" Variables, macros, records, maps
-syn match erlangVariable '\<[A-Z_][[:alnum:]_@]*'
+syn match erlangVariable '\<[A-Z][[:alnum:]_@]*'
+syn match erlangAnonymousVariable '\<_[[:alnum:]_@]*'
syn match erlangMacro '??\=[[:alnum:]_@]\+'
syn match erlangMacro '\%(-define(\)\@<=[[:alnum:]_@]\+'
syn region erlangQuotedMacro start=/??\=\s*'/ end=/'/ contains=erlangQuotedAtomModifier
@@ -92,7 +94,7 @@ syn match erlangBitType '\%(\/\%(\s\|\n\|%.*\n\)*\)\@<=\%(integer\|float\|binary
" Constants and Directives
syn match erlangUnknownAttribute '^\s*-\%(\s\|\n\|%.*\n\)*\l[[:alnum:]_@]*' contains=erlangComment
-syn match erlangAttribute '^\s*-\%(\s\|\n\|%.*\n\)*\%(behaviou\=r\|compile\|export\(_type\)\=\|file\|import\|module\|author\|copyright\|doc\|vsn\|on_load\|optional_callbacks\)\>' contains=erlangComment
+syn match erlangAttribute '^\s*-\%(\s\|\n\|%.*\n\)*\%(behaviou\=r\|compile\|export\(_type\)\=\|file\|import\|module\|author\|copyright\|doc\|vsn\|on_load\|optional_callbacks\|feature\)\>' contains=erlangComment
syn match erlangInclude '^\s*-\%(\s\|\n\|%.*\n\)*\%(include\|include_lib\)\>' contains=erlangComment
syn match erlangRecordDef '^\s*-\%(\s\|\n\|%.*\n\)*record\>' contains=erlangComment
syn match erlangDefine '^\s*-\%(\s\|\n\|%.*\n\)*\%(define\|undef\)\>' contains=erlangComment
@@ -100,8 +102,8 @@ syn match erlangPreCondit '^\s*-\%(\s\|\n\|%.*\n\)*\%(ifdef\|ifndef\|else\|endif
syn match erlangType '^\s*-\%(\s\|\n\|%.*\n\)*\%(spec\|type\|opaque\|callback\)\>' contains=erlangComment
" Keywords
-syn keyword erlangKeyword after begin case catch cond end fun if let of
-syn keyword erlangKeyword receive when try
+syn keyword erlangKeyword after begin case catch cond end fun if let of else
+syn keyword erlangKeyword receive when try maybe
" Build-in-functions (BIFs)
syn keyword erlangBIF abs alive apply atom_to_binary atom_to_list contained
@@ -174,6 +176,7 @@ hi def link erlangModifier Special
" Operators, separators
hi def link erlangOperator Operator
+hi def link erlangEqualsBinary ErrorMsg
hi def link erlangRightArrow Operator
if s:old_style
hi def link erlangBracket Normal
@@ -191,6 +194,7 @@ hi def link erlangLocalFuncRef Normal
hi def link erlangGlobalFuncCall Function
hi def link erlangGlobalFuncRef Function
hi def link erlangVariable Normal
+hi def link erlangAnonymousVariable erlangVariable
hi def link erlangMacro Normal
hi def link erlangQuotedMacro Normal
hi def link erlangRecord Normal
@@ -203,6 +207,7 @@ hi def link erlangLocalFuncRef Normal
hi def link erlangGlobalFuncCall Normal
hi def link erlangGlobalFuncRef Normal
hi def link erlangVariable Identifier
+hi def link erlangAnonymousVariable erlangVariable
hi def link erlangMacro Macro
hi def link erlangQuotedMacro Macro
hi def link erlangRecord Structure
diff --git a/runtime/syntax/fstab.vim b/runtime/syntax/fstab.vim
index 318488713b..7e18c267f7 100644
--- a/runtime/syntax/fstab.vim
+++ b/runtime/syntax/fstab.vim
@@ -2,8 +2,8 @@
" Language: fstab file
" Maintainer: Radu Dineiu <radu.dineiu@gmail.com>
" URL: https://raw.github.com/rid9/vim-fstab/master/syntax/fstab.vim
-" Last Change: 2020 Dec 30
-" Version: 1.4
+" Last Change: 2022 Dec 11
+" Version: 1.6.2
"
" Credits:
" David Necas (Yeti) <yeti@physics.muni.cz>
@@ -56,71 +56,124 @@ syn keyword fsMountPointKeyword contained none swap
" Type
syn cluster fsTypeCluster contains=fsTypeKeyword,fsTypeUnknown
syn match fsTypeUnknown /\s\+\zs\w\+/ contained
-syn keyword fsTypeKeyword contained adfs ados affs anon_inodefs atfs audiofs auto autofs bdev befs bfs btrfs binfmt_misc cd9660 cfs cgroup cifs coda configfs cpuset cramfs devfs devpts devtmpfs e2compr efs ext2 ext2fs ext3 ext4 fdesc ffs filecore fuse fuseblk fusectl hfs hpfs hugetlbfs iso9660 jffs jffs2 jfs kernfs lfs linprocfs mfs minix mqueue msdos ncpfs nfs nfsd nilfs2 none ntfs null nwfs overlay ovlfs pipefs portal proc procfs pstore ptyfs qnx4 reiserfs ramfs romfs securityfs shm smbfs squashfs sockfs sshfs std subfs swap sysfs sysv tcfs tmpfs udf ufs umap umsdos union usbfs userfs vfat vs3fs vxfs wrapfs wvfs xenfs xfs zisofs
+syn keyword fsTypeKeyword contained adfs ados affs anon_inodefs atfs audiofs auto autofs bdev befs bfs btrfs binfmt_misc cd9660 ceph cfs cgroup cifs coda coherent configfs cpuset cramfs debugfs devfs devpts devtmpfs dlmfs e2compr ecryptfs efivarfs efs erofs exfat ext2 ext2fs ext3 ext4 f2fs fdesc ffs filecore fuse fuseblk fusectl gfs2 hfs hfsplus hpfs hugetlbfs iso9660 jffs jffs2 jfs kernfs lfs linprocfs mfs minix mqueue msdos ncpfs nfs nfs4 nfsd nilfs2 none ntfs ntfs3 null nwfs ocfs2 omfs overlay ovlfs pipefs portal proc procfs pstore ptyfs pvfs2 qnx4 qnx6 reiserfs ramfs romfs rpc_pipefs securityfs shm smbfs spufs squashfs sockfs sshfs std subfs swap sysfs sysv tcfs tmpfs ubifs udf ufs umap umsdos union usbfs userfs v9fs vfat virtiofs vs3fs vxfs wrapfs wvfs xenfs xenix xfs zisofs zonefs
" Options
" -------
" Options: General
syn cluster fsOptionsCluster contains=fsOperator,fsOptionsGeneral,fsOptionsKeywords,fsTypeUnknown
syn match fsOptionsNumber /\d\+/
+syn match fsOptionsNumberSigned /[-+]\?\d\+/
syn match fsOptionsNumberOctal /[0-8]\+/
syn match fsOptionsString /[a-zA-Z0-9_-]\+/
+syn keyword fsOptionsTrueFalse true false
syn keyword fsOptionsYesNo yes no
+syn keyword fsOptionsYN y n
+syn keyword fsOptions01 0 1
syn cluster fsOptionsCheckCluster contains=fsOptionsExt2Check,fsOptionsFatCheck
syn keyword fsOptionsSize 512 1024 2048
-syn keyword fsOptionsGeneral async atime auto bind current defaults dev devgid devmode devmtime devuid dirsync exec force fstab kudzu loop mand move noatime noauto noclusterr noclusterw nodev nodevmtime nodiratime noexec nomand norelatime nosuid nosymfollow nouser owner rbind rdonly relatime remount ro rq rw suid suiddir supermount sw sync union update user users wxallowed xx nofail failok
+syn keyword fsOptionsGeneral async atime auto bind current defaults dev devgid devmode devmtime devuid dirsync exec force fstab kudzu loop managed mand move noatime noauto noclusterr noclusterw nodev nodevmtime nodiratime noexec nomand norelatime nosuid nosymfollow nouser owner pamconsole rbind rdonly relatime remount ro rq rw suid suiddir supermount sw sync union update user users wxallowed xx nofail failok lazytime
syn match fsOptionsGeneral /_netdev/
+syn match fsOptionsKeywords contained /\<x-systemd\.\%(requires\|before\|after\|wanted-by\|required-by\|requires-mounts-for\|idle-timeout\|device-timeout\|mount-timeout\)=/ nextgroup=fsOptionsString
+syn match fsOptionsKeywords contained /\<x-systemd\.\%(device-bound\|automount\|makefs\|growfs\|rw-only\)/
+syn match fsOptionsKeywords contained /\<x-initrd\.mount/
+
+syn match fsOptionsKeywords contained /\<cache=/ nextgroup=fsOptionsCache
+syn keyword fsOptionsCache yes no none strict loose fscache mmap
+
+syn match fsOptionsKeywords contained /\<dax=/ nextgroup=fsOptionsDax
+syn keyword fsOptionsDax inode never always
+
+syn match fsOptionsKeywords contained /\<errors=/ nextgroup=fsOptionsErrors
+syn keyword fsOptionsErrors contained continue panic withdraw remount-ro recover zone-ro zone-offline repair
+
+syn match fsOptionsKeywords contained /\<\%(sec\)=/ nextgroup=fsOptionsSecurityMode
+syn keyword fsOptionsSecurityMode contained none krb5 krb5i ntlm ntlmi ntlmv2 ntlmv2i ntlmssp ntlmsspi sys lkey lkeyi lkeyp spkm spkmi spkmp
+
" Options: adfs
syn match fsOptionsKeywords contained /\<\%([ug]id\|o\%(wn\|th\)mask\)=/ nextgroup=fsOptionsNumber
+syn match fsOptionsKeywords contained /\<ftsuffix=/ nextgroup=fsOptions01
" Options: affs
-syn match fsOptionsKeywords contained /\<\%(set[ug]id\|mode\|reserved\)=/ nextgroup=fsOptionsNumber
+syn match fsOptionsKeywords contained /\<mode=/ nextgroup=fsOptionsString
+syn match fsOptionsKeywords contained /\<\%(set[ug]id\|reserved\)=/ nextgroup=fsOptionsNumber
syn match fsOptionsKeywords contained /\<\%(prefix\|volume\|root\)=/ nextgroup=fsOptionsString
syn match fsOptionsKeywords contained /\<bs=/ nextgroup=fsOptionsSize
-syn keyword fsOptionsKeywords contained protect usemp verbose
+syn keyword fsOptionsKeywords contained protect usemp verbose nofilenametruncate mufs
" Options: btrfs
-syn match fsOptionsKeywords contained /\<\%(subvol\|subvolid\|subvolrootid\|device\|compress\|compress-force\|fatal_errors\)=/ nextgroup=fsOptionsString
+syn match fsOptionsKeywords contained /\<\%(subvol\|subvolid\|subvolrootid\|device\|compress\|compress-force\|check_int_print_mask\|space_cache\)=/ nextgroup=fsOptionsString
syn match fsOptionsKeywords contained /\<\%(max_inline\|alloc_start\|thread_pool\|metadata_ratio\|check_int_print_mask\)=/ nextgroup=fsOptionsNumber
-syn keyword fsOptionsKeywords contained degraded nodatasum nodatacow nobarrier ssd ssd_spread noacl notreelog flushoncommit space_cache nospace_cache clear_cache user_subvol_rm_allowed autodefrag inode_cache enospc_debug recovery check_int check_int_data skip_balance discard
+syn match fsOptionsKeywords contained /\<discard=/ nextgroup=fsOptionsBtrfsDiscard
+syn keyword fsOptionsBtrfsDiscard sync async
+syn match fsOptionsKeywords contained /\<fatal_errors=/ nextgroup=fsOptionsBtrfsFatalErrors
+syn keyword fsOptionsBtrfsFatalErrors bug panic
+syn match fsOptionsKeywords contained /\<fragment=/ nextgroup=fsOptionsBtrfsFragment
+syn keyword fsOptionsBtrfsFragment data metadata all
+syn keyword fsOptionsKeywords contained degraded datasum nodatasum datacow nodatacow barrier nobarrier ssd ssd_spread nossd nossd_spread noacl treelog notreelog flushoncommit noflushoncommit space_cache nospace_cache clear_cache user_subvol_rm_allowed autodefrag noautodefrag inode_cache noinode_cache enospc_debug noenospc_debug recovery check_int check_int_data skip_balance discard nodiscard compress compress-force nologreplay rescan_uuid_tree rescue usebackuproot
" Options: cd9660
syn keyword fsOptionsKeywords contained extatt gens norrip nostrictjoilet
+" Options: ceph
+syn match fsOptionsKeywords contained /\<\%(mon_addr\|fsid\|rasize\|mount_timeout\|caps_max\)=/ nextgroup=fsOptionsString
+syn keyword fsOptionsKeywords contained rbytes norbytes nocrc dcache nodcache noasyncreaddir noquotadf nocopyfrom
+syn match fsOptionsKeywords contained /\<recover_session=/ nextgroup=fsOptionsCephRecoverSession
+syn keyword fsOptionsCephRecoverSession contained no clean
+
+" Options: cifs
+syn match fsOptionsKeywords contained /\<\%(user\|password\|credentials\|servernetbiosname\|servern\|netbiosname\|file_mode\|dir_mode\|ip\|domain\|prefixpath\)=/ nextgroup=fsOptionsString
+syn match fsOptionsKeywords contained /\<\%(cruid\|backupuid\|backupgid\)=/ nextgroup=fsOptionsNumber
+syn keyword fsOptionsKeywords contained forceuid forcegid guest setuids nosetuids perm noperm dynperm strictcache rwpidforward mapchars nomapchars cifsacl nocase ignorecase nobrl sfu serverino noserverino nounix fsc multiuser posixpaths noposixpaths
+
" Options: devpts
" -- everything already defined
+" Options: ecryptfs
+syn match fsOptionsKeywords contained /\<\%(ecryptfs_\%(sig\|fnek_sig\|cipher\|key_bytes\)\|key\)=/ nextgroup=fsOptionsString
+syn keyword fsOptionsKeywords contained ecryptfs_passthrough no_sig_cache ecryptfs_encrypted_view ecryptfs_xattr
+syn match fsOptionsKeywords contained /\<ecryptfs_enable_filename_crypto=/ nextgroup=fsOptionsYN
+syn match fsOptionsKeywords contained /\<verbosity=/ nextgroup=fsOptions01
+
+" Options: erofs
+syn match fsOptionsKeywords contained /\<cache_strategy=/ nextgroup=fsOptionsEroCacheStrategy
+syn keyword fsOptionsEroCacheStrategy contained disabled readahead readaround
+
" Options: ext2
syn match fsOptionsKeywords contained /\<check=*/ nextgroup=@fsOptionsCheckCluster
-syn match fsOptionsKeywords contained /\<errors=/ nextgroup=fsOptionsExt2Errors
syn match fsOptionsKeywords contained /\<\%(res[gu]id\|sb\)=/ nextgroup=fsOptionsNumber
syn keyword fsOptionsExt2Check contained none normal strict
-syn keyword fsOptionsExt2Errors contained continue panic
-syn match fsOptionsExt2Errors contained /\<remount-ro\>/
+syn match fsOptionsErrors contained /\<remount-ro\>/
syn keyword fsOptionsKeywords contained acl bsddf minixdf debug grpid bsdgroups minixdf nocheck nogrpid oldalloc orlov sysvgroups nouid32 nobh user_xattr nouser_xattr
" Options: ext3
syn match fsOptionsKeywords contained /\<journal=/ nextgroup=fsOptionsExt3Journal
syn match fsOptionsKeywords contained /\<data=/ nextgroup=fsOptionsExt3Data
+syn match fsOptionsKeywords contained /\<data_err=/ nextgroup=fsOptionsExt3DataErr
syn match fsOptionsKeywords contained /\<commit=/ nextgroup=fsOptionsNumber
+syn match fsOptionsKeywords contained /\<jqfmt=/ nextgroup=fsOptionsExt3Jqfmt
+syn match fsOptionsKeywords contained /\<\%(usrjquota\|grpjquota\)=/ nextgroup=fsOptionsString
syn keyword fsOptionsExt3Journal contained update inum
syn keyword fsOptionsExt3Data contained journal ordered writeback
+syn keyword fsOptionsExt3DataErr contained ignore abort
+syn keyword fsOptionsExt3Jqfmt contained vfsold vfsv0 vfsv1
syn keyword fsOptionsKeywords contained noload user_xattr nouser_xattr acl
" Options: ext4
syn match fsOptionsKeywords contained /\<journal=/ nextgroup=fsOptionsExt4Journal
syn match fsOptionsKeywords contained /\<data=/ nextgroup=fsOptionsExt4Data
-syn match fsOptionsKeywords contained /\<barrier=/ nextgroup=fsOptionsExt4Barrier
+syn match fsOptionsKeywords contained /\<barrier=/ nextgroup=fsOptions01
syn match fsOptionsKeywords contained /\<journal_dev=/ nextgroup=fsOptionsNumber
syn match fsOptionsKeywords contained /\<resuid=/ nextgroup=fsOptionsNumber
syn match fsOptionsKeywords contained /\<resgid=/ nextgroup=fsOptionsNumber
syn match fsOptionsKeywords contained /\<sb=/ nextgroup=fsOptionsNumber
-syn match fsOptionsKeywords contained /\<commit=/ nextgroup=fsOptionsNumber
+syn match fsOptionsKeywords contained /\<\%(commit\|inode_readahead_blks\|stripe\|max_batch_time\|min_batch_time\|init_itable\|max_dir_size_kb\)=/ nextgroup=fsOptionsNumber
+syn match fsOptionsKeywords contained /\<journal_ioprio=/ nextgroup=fsOptionsExt4JournalIoprio
syn keyword fsOptionsExt4Journal contained update inum
syn keyword fsOptionsExt4Data contained journal ordered writeback
-syn match fsOptionsExt4Barrier /[0-1]/
-syn keyword fsOptionsKeywords contained noload extents orlov oldalloc user_xattr nouser_xattr acl noacl reservation noreservation bsddf minixdf check=none nocheck debug grpid nogroupid sysvgroups bsdgroups quota noquota grpquota usrquota bh nobh
+syn keyword fsOptionsExt4JournalIoprio contained 0 1 2 3 4 5 6 7
+syn keyword fsOptionsKeywords contained noload extents orlov oldalloc user_xattr nouser_xattr acl noacl reservation noreservation bsddf minixdf check=none nocheck debug grpid nogroupid sysvgroups bsdgroups quota noquota grpquota usrquota bh nobh journal_checksum nojournal_checksum journal_async_commit delalloc nodelalloc auto_da_alloc noauto_da_alloc noinit_itable block_validity noblock_validity dioread_lock dioread_nolock i_version nombcache prjquota
" Options: fat
syn match fsOptionsKeywords contained /\<blocksize=/ nextgroup=fsOptionsSize
@@ -135,39 +188,124 @@ syn keyword fsOptionsConv contained b t a binary text auto
syn keyword fsOptionsFatType contained 12 16 32
syn keyword fsOptionsKeywords contained quiet sys_immutable showexec dots nodots
+" Options: fuse
+syn match fsOptionsKeywords contained /\<\%(fd\|user_id\|group_id\|blksize\)=/ nextgroup=fsOptionsNumber
+syn match fsOptionsKeywords contained /\<\%(rootmode\)=/ nextgroup=fsOptionsString
+
" Options: hfs
-syn match fsOptionsKeywords contained /\<\%(creator|type\)=/ nextgroup=fsOptionsString
+syn match fsOptionsKeywords contained /\<\%(creator\|type\)=/ nextgroup=fsOptionsString
syn match fsOptionsKeywords contained /\<\%(dir\|file\|\)_umask=/ nextgroup=fsOptionsNumberOctal
syn match fsOptionsKeywords contained /\<\%(session\|part\)=/ nextgroup=fsOptionsNumber
+" Options: hfsplus
+syn match fsOptionsKeywords contained /\<nls=/ nextgroup=fsOptionsString
+syn keyword fsOptionsKeywords contained decompose nodecompose
+
+" Options: f2fs
+syn match fsOptionsKeywords contained /\<background_gc=/ nextgroup=fsOptionsF2fsBackgroundGc
+syn keyword fsOptionsF2fsBackgroundGc contained on off sync
+syn match fsOptionsKeywords contained /\<active_logs=/ nextgroup=fsOptionsF2fsActiveLogs
+syn keyword fsOptionsF2fsActiveLogs contained 2 4 6
+syn match fsOptionsKeywords contained /\<alloc_mode=/ nextgroup=fsOptionsF2fsAllocMode
+syn keyword fsOptionsF2fsAllocMode contained reuse default
+syn match fsOptionsKeywords contained /\<fsync_mode=/ nextgroup=fsOptionsF2fsFsyncMode
+syn keyword fsOptionsF2fsFsyncMode contained posix strict nobarrier
+syn match fsOptionsKeywords contained /\<compress_mode=/ nextgroup=fsOptionsF2fsCompressMode
+syn keyword fsOptionsF2fsCompressMode contained fs user
+syn match fsOptionsKeywords contained /\<discard_unit=/ nextgroup=fsOptionsF2fsDiscardUnit
+syn keyword fsOptionsF2fsDiscardUnit contained block segment section
+syn match fsOptionsKeywords contained /\<memory=/ nextgroup=fsOptionsF2fsMemory
+syn keyword fsOptionsF2fsMemory contained normal low
+syn match fsOptionsKeywords contained /\<\%(inline_xattr_size\|reserve_root\|fault_injection\|fault_type\|io_bits\|compress_log_size\)=/ nextgroup=fsOptionsNumber
+syn match fsOptionsKeywords contained /\<\%(prjjquota\|test_dummy_encryption\|checkpoint\|compress_algorithm\|compress_extension\|nocompress_extension\)=/ nextgroup=fsOptionsString
+syn keyword fsOptionsKeyWords contained gc_merge nogc_merge disable_roll_forward no_heap disable_ext_identify inline_xattr noinline_xattr inline_data noinline_data inline_dentry noinline_dentry flush_merge fastboot extent_cache noextent_cache data_flush offusrjquota offgrpjquota offprjjquota test_dummy_encryption checkpoint_merge nocheckpoint_merge compress_chksum compress_cache inlinecrypt atgc
+
" Options: ffs
syn keyword fsOptionsKeyWords contained noperm softdep
+" Options: gfs2
+syn match fsOptionsKeywords contained /\<\%(lockproto\|locktable\)=/ nextgroup=fsOptionsString
+syn match fsOptionsKeywords contained /\<\%(quota_quantum\|statfs_quantum\|statfs_percent\)=/ nextgroup=fsOptionsNumber
+syn match fsOptionsKeywords contained /\<quota=/ nextgroup=fsOptionsGfs2Quota
+syn keyword fsOptionsGfs2Quota contained off account on
+syn keyword fsOptionsKeywords contained localcaching localflocks ignore_local_fs upgrade spectator meta
+
" Options: hpfs
syn match fsOptionsKeywords contained /\<case=/ nextgroup=fsOptionsHpfsCase
syn keyword fsOptionsHpfsCase contained lower asis
+syn match fsOptionsKeywords contained /\<chkdsk=/ nextgroup=fsOptionsHpfsChkdsk
+syn keyword fsOptionsHpfsChkdsk contained no errors always
+syn match fsOptionsKeywords contained /\<eas=/ nextgroup=fsOptionsHpfsEas
+syn keyword fsOptionsHpfsEas contained no ro rw
+syn match fsOptionsKeywords contained /\<timeshift=/ nextgroup=fsOptionsNumberSigned
" Options: iso9660
syn match fsOptionsKeywords contained /\<map=/ nextgroup=fsOptionsIsoMap
syn match fsOptionsKeywords contained /\<block=/ nextgroup=fsOptionsSize
-syn match fsOptionsKeywords contained /\<\%(session\|sbsector\)=/ nextgroup=fsOptionsNumber
+syn match fsOptionsKeywords contained /\<\%(session\|sbsector\|dmode\)=/ nextgroup=fsOptionsNumber
syn keyword fsOptionsIsoMap contained n o a normal off acorn
-syn keyword fsOptionsKeywords contained norock nojoilet unhide cruft
+syn keyword fsOptionsKeywords contained norock nojoliet hide unhide cruft overriderockperm showassoc
syn keyword fsOptionsConv contained m mtext
" Options: jfs
syn keyword fsOptionsKeywords nointegrity integrity
" Options: nfs
-syn match fsOptionsKeywords contained /\<\%(rsize\|wsize\|timeo\|retrans\|acregmin\|acregmax\|acdirmin\|acdirmax\|actimeo\|retry\|port\|mountport\|mounthost\|mountprog\|mountvers\|nfsprog\|nfsvers\|namelen\)=/ nextgroup=fsOptionsString
-syn keyword fsOptionsKeywords contained bg fg soft hard intr cto ac tcp udp lock nobg nofg nosoft nohard nointr noposix nocto noac notcp noudp nolock
+syn match fsOptionsKeywords contained /\<lookupcache=/ nextgroup=fsOptionsNfsLookupCache
+syn keyword fsOptionsNfsLookupCache contained all none pos positive
+syn match fsOptionsKeywords contained /\<local_lock=/ nextgroup=fsOptionsNfsLocalLock
+syn keyword fsOptionsNfsLocalLock contained all flock posix none
+syn match fsOptionsKeywords contained /\<\%(mounthost\|mountprog\|nfsprog\|namelen\|proto\|mountproto\|clientaddr\)=/ nextgroup=fsOptionsString
+syn match fsOptionsKeywords contained /\<\%(timeo\|retrans\|[rw]size\|acregmin\|acregmax\|acdirmin\|acdirmax\|actimeo\|retry\|port\|mountport\|mountvers\|namlen\|nfsvers\|vers\|minorversion\)=/ nextgroup=fsOptionsNumber
+syn keyword fsOptionsKeywords contained bg fg soft hard intr cto ac tcp udp lock nobg nofg nosoft nohard nointr noposix nocto noac notcp noudp nolock sharecache nosharecache resvport noresvport rdirplus nordirplus
+
+" Options: nilfs2
+syn match fsOptionsKeywords contained /\<order=/ nextgroup=fsOptionsNilfs2Order
+syn keyword fsOptionsNilfs2Order contained relaxed strict
+syn match fsOptionsKeywords contained /\<\%([cp]p\)=/ nextgroup=fsOptionsNumber
+syn keyword fsOptionsKeywords contained nogc
" Options: ntfs
+syn match fsOptionsKeywords contained /\<mft_zone_multiplier=/ nextgroup=fsOptionsNtfsMftZoneMultiplier
+syn keyword fsOptionsNtfsMftZoneMultiplier contained 1 2 3 4
syn match fsOptionsKeywords contained /\<\%(posix=*\|uni_xlate=\)/ nextgroup=fsOptionsNumber
+syn match fsOptionsKeywords contained /\<\%(sloppy\|show_sys_files\|case_sensitive\|disable_sparse\)=/ nextgroup=fsOptionsTrueFalse
syn keyword fsOptionsKeywords contained utf8
+" Options: ntfs3
+syn keyword fsOptionsKeywords contained noacsrules nohidden sparse showmeta prealloc
+
+" Options: ntfs-3g
+syn match fsOptionsKeywords contained /\<\%(usermapping\|locale\|streams_interface\)=/ nextgroup=fsOptionsString
+syn keyword fsOptionsKeywords contained permissions inherit recover norecover ignore_case remove_hiberfile hide_hid_files hide_dot_files windows_names silent no_def_opts efs_raw compression nocompression no_detach
+
+" Options: ocfs2
+syn match fsOptionsKeywords contained /\<\%(resv_level\|dir_resv_level\)=/ nextgroup=fsOptionsOcfs2ResvLevel
+syn keyword fsOptionsOcfs2ResvLevel contained 0 1 2 3 4 5 6 7 8
+syn match fsOptionsKeywords contained /\<coherency=/ nextgroup=fsOptionsOcfs2Coherency
+syn keyword fsOptionsOcfs2Coherency contained full buffered
+syn match fsOptionsKeywords contained /\<\%(atime_quantum\|preferred_slot\|localalloc\)=/ nextgroup=fsOptionsNumber
+syn keyword fsOptionsKeywords contained strictatime inode64
+
+" Options: overlay
+syn match fsOptionsKeywords contained /\<redirect_dir=/ nextgroup=fsOptionsOverlayRedirectDir
+syn keyword fsOptionsOverlayRedirectDir contained on follow off nofollow
+
" Options: proc
-" -- everything already defined
+syn match fsOptionsKeywords contained /\<\%(hidepid\|subset\)=/ nextgroup=fsOptionsString
+
+" Options: qnx4
+syn match fsOptionsKeywords contained /\<bitmap=/ nextgroup=fsOptionsQnx4Bitmap
+syn keyword fsOptionsQnx4Bitmap contained always lazy nonrmv
+syn keyword fsOptionsKeywords contained grown noembed overalloc unbusy
+
+" Options: qnx6
+syn match fsOptionsKeywords contained /\<hold=/ nextgroup=fsOptionsQnx6Hold
+syn keyword fsOptionsQnx6Hold contained allow root deny
+syn match fsOptionsKeywords contained /\<sync=/ nextgroup=fsOptionsQnx6Sync
+syn keyword fsOptionsQnx6Sync contained mandatory optional none
+syn match fsOptionsKeywords contained /\<snapshot=/ nextgroup=fsOptionsNumber
+syn keyword fsOptionsKeywords contained alignio
" Options: reiserfs
syn match fsOptionsKeywords contained /\<hash=/ nextgroup=fsOptionsReiserHash
@@ -176,7 +314,7 @@ syn keyword fsOptionsReiserHash contained rupasov tea r5 detect
syn keyword fsOptionsKeywords contained hashed_relocation noborder nolog notail no_unhashed_relocation replayonly
" Options: sshfs
-syn match fsOptionsKeywords contained /\<\%(BatchMode\|ChallengeResponseAuthentication\|CheckHostIP\|ClearAllForwardings\|Compression\|EnableSSHKeysign\|ForwardAgent\|ForwardX11\|ForwardX11Trusted\|GatewayPorts\|GSSAPIAuthentication\|GSSAPIDelegateCredentials\|HashKnownHosts\|HostbasedAuthentication\|IdentitiesOnly\|NoHostAuthenticationForLocalhost\|PasswordAuthentication\|PubkeyAuthentication\|RhostsRSAAuthentication\|RSAAuthentication\|TCPKeepAlive\|UsePrivilegedPort\|cache\)=/ nextgroup=fsOptionsYesNo
+syn match fsOptionsKeywords contained /\<\%(BatchMode\|ChallengeResponseAuthentication\|CheckHostIP\|ClearAllForwardings\|Compression\|EnableSSHKeysign\|ForwardAgent\|ForwardX11\|ForwardX11Trusted\|GatewayPorts\|GSSAPIAuthentication\|GSSAPIDelegateCredentials\|HashKnownHosts\|HostbasedAuthentication\|IdentitiesOnly\|NoHostAuthenticationForLocalhost\|PasswordAuthentication\|PubkeyAuthentication\|RhostsRSAAuthentication\|RSAAuthentication\|TCPKeepAlive\|UsePrivilegedPort\)=/ nextgroup=fsOptionsYesNo
syn match fsOptionsKeywords contained /\<\%(ControlMaster\|StrictHostKeyChecking\|VerifyHostKeyDNS\)=/ nextgroup=fsOptionsSshYesNoAsk
syn match fsOptionsKeywords contained /\<\%(AddressFamily\|BindAddress\|Cipher\|Ciphers\|ControlPath\|DynamicForward\|EscapeChar\|GlobalKnownHostsFile\|HostKeyAlgorithms\|HostKeyAlias\|HostName\|IdentityFile\|KbdInteractiveDevices\|LocalForward\|LogLevel\|MACs\|PreferredAuthentications\|Protocol\|ProxyCommand\|RemoteForward\|RhostsAuthentication\|SendEnv\|SmartcardDevice\|User\|UserKnownHostsFile\|XAuthLocation\|comment\|workaround\|idmap\|ssh_command\|sftp_server\|fsname\)=/ nextgroup=fsOptionsString
syn match fsOptionsKeywords contained /\<\%(CompressionLevel\|ConnectionAttempts\|ConnectTimeout\|NumberOfPasswordPrompts\|Port\|ServerAliveCountMax\|ServerAliveInterval\|cache_timeout\|cache_X_timeout\|ssh_protocol\|directport\|max_read\|umask\|uid\|gid\|entry_timeout\|negative_timeout\|attr_timeout\)=/ nextgroup=fsOptionsNumber
@@ -190,12 +328,19 @@ syn keyword fsOptionsKeywords contained procuid
" Options: swap
syn match fsOptionsKeywords contained /\<pri=/ nextgroup=fsOptionsNumber
+" Options: ubifs
+syn match fsOptionsKeywords contained /\<\%(compr\|auth_key\|auth_hash_name\)=/ nextgroup=fsOptionsString
+syn keyword fsOptionsKeywords contained bulk_read no_bulk_read chk_data_crc no_chk_data_crc
+
" Options: tmpfs
+syn match fsOptionsKeywords contained /\<huge=/ nextgroup=fsOptionsTmpfsHuge
+syn keyword fsOptionsTmpfsHuge contained never always within_size advise deny force
+syn match fsOptionsKeywords contained /\<\%(size\|mpol\)=/ nextgroup=fsOptionsString
syn match fsOptionsKeywords contained /\<nr_\%(blocks\|inodes\)=/ nextgroup=fsOptionsNumber
" Options: udf
syn match fsOptionsKeywords contained /\<\%(anchor\|partition\|lastblock\|fileset\|rootdir\)=/ nextgroup=fsOptionsString
-syn keyword fsOptionsKeywords contained unhide undelete strict novrs
+syn keyword fsOptionsKeywords contained unhide undelete strict nostrict novrs adinicb noadinicb shortad longad
" Options: ufs
syn match fsOptionsKeywords contained /\<ufstype=/ nextgroup=fsOptionsUfsType
@@ -208,14 +353,32 @@ syn keyword fsOptionsUfsError contained panic lock umount repair
syn match fsOptionsKeywords contained /\<\%(dev\|bus\|list\)\%(id\|gid\)=/ nextgroup=fsOptionsNumber
syn match fsOptionsKeywords contained /\<\%(dev\|bus\|list\)mode=/ nextgroup=fsOptionsNumberOctal
+" Options: v9fs
+syn match fsOptionsKeywords contained /\<\%(trans\)=/ nextgroup=fsOptionsV9Trans
+syn keyword fsOptionsV9Trans unix tcp fd virtio rdma
+syn match fsOptionsKeywords contained /\<debug=/ nextgroup=fsOptionsV9Debug
+syn keyword fsOptionsV9Debug 0x01 0x02 0x04 0x08 0x10 0x20 0x40 0x80 0x100 0x200 0x400 0x800
+syn match fsOptionsKeywords contained /\<version=/ nextgroup=fsOptionsV9Version
+syn keyword fsOptionsV9Version 9p2000 9p2000.u 9p2000.L
+syn match fsOptionsKeywords contained /\<\%([ua]name\|[rw]fdno\|access\)=/ nextgroup=fsOptionsString
+syn match fsOptionsKeywords contained /\<msize=/ nextgroup=fsOptionsNumber
+syn keyword fsOptionsKeywords contained noextend dfltuid dfltgid afid nodevmap cachetag
+
" Options: vfat
-syn keyword fsOptionsKeywords contained nonumtail posix utf8
-syn match fsOptionsKeywords contained /shortname=/ nextgroup=fsOptionsVfatShortname
+syn match fsOptionsKeywords contained /\<shortname=/ nextgroup=fsOptionsVfatShortname
syn keyword fsOptionsVfatShortname contained lower win95 winnt mixed
+syn match fsOptionsKeywords contained /\<nfs=/ nextgroup=fsOptionsVfatNfs
+syn keyword fsOptionsVfatNfs contained stale_rw nostale_ro
+syn match fsOptionsKeywords contained /\<\%(tz\|dos1xfloppy\)=/ nextgroup=fsOptionsString
+syn match fsOptionsKeywords contained /\<\%(allow_utime\|codepage\)=/ nextgroup=fsOptionsNumber
+syn match fsOptionsKeywords contained /\<time_offset=/ nextgroup=fsOptionsNumberSigned
+syn keyword fsOptionsKeywords contained nonumtail posix utf8 usefree flush rodir
" Options: xfs
-syn match fsOptionsKeywords contained /\%(biosize\|logbufs\|logbsize\|logdev\|rtdev\|sunit\|swidth\)=/ nextgroup=fsOptionsString
-syn keyword fsOptionsKeywords contained dmapi xdsm noalign noatime noquota norecovery osyncisdsync quota usrquota uqnoenforce grpquota gqnoenforce
+syn match fsOptionsKeywords contained /\<logbufs=/ nextgroup=fsOptionsXfsLogBufs
+syn keyword fsOptionsXfsLogBufs contained 2 3 4 5 6 7 8
+syn match fsOptionsKeywords contained /\%(allocsize\|biosize\|logbsize\|logdev\|rtdev\|sunit\|swidth\)=/ nextgroup=fsOptionsString
+syn keyword fsOptionsKeywords contained dmapi xdsm noalign noatime noquota norecovery osyncisdsync quota usrquota uqnoenforce grpquota gqnoenforce attr2 noattr2 filestreams ikeep noikeep inode32 inode64 largeio nolargeio nouuid uquota qnoenforce gquota pquota pqnoenforce swalloc wsync
" Frequency / Pass No.
syn cluster fsFreqPassCluster contains=fsFreqPassNumber,fsFreqPassError
@@ -257,31 +420,71 @@ hi def link fsMountPointError Error
hi def link fsMountPointKeyword Keyword
hi def link fsFreqPassError Error
-hi def link fsOptionsGeneral Type
-hi def link fsOptionsKeywords Keyword
-hi def link fsOptionsNumber Number
-hi def link fsOptionsNumberOctal Number
-hi def link fsOptionsString String
-hi def link fsOptionsSize Number
+hi def link fsOptionsBtrfsDiscard String
+hi def link fsOptionsBtrfsFatalErrors String
+hi def link fsOptionsBtrfsFragment String
+hi def link fsOptionsCache String
+hi def link fsOptionsCephRecoverSession String
+hi def link fsOptionsConv String
+hi def link fsOptionsDax String
+hi def link fsOptionsEroCacheStrategy String
+hi def link fsOptionsErrors String
hi def link fsOptionsExt2Check String
-hi def link fsOptionsExt2Errors String
-hi def link fsOptionsExt3Journal String
hi def link fsOptionsExt3Data String
-hi def link fsOptionsExt4Journal String
+hi def link fsOptionsExt3DataErr String
+hi def link fsOptionsExt3Journal String
+hi def link fsOptionsExt3Jqfmt String
hi def link fsOptionsExt4Data String
-hi def link fsOptionsExt4Barrier Number
+hi def link fsOptionsExt4Journal String
+hi def link fsOptionsExt4JournalIoprio Number
+hi def link fsOptionsF2fsActiveLogs Number
+hi def link fsOptionsF2fsAllocMode String
+hi def link fsOptionsF2fsBackgroundGc String
+hi def link fsOptionsF2fsCompressMode String
+hi def link fsOptionsF2fsDiscardUnit String
+hi def link fsOptionsF2fsFsyncMode String
+hi def link fsOptionsF2fsMemory String
hi def link fsOptionsFatCheck String
-hi def link fsOptionsConv String
hi def link fsOptionsFatType Number
-hi def link fsOptionsYesNo String
+hi def link fsOptionsGeneral Type
+hi def link fsOptionsGfs2Quota String
hi def link fsOptionsHpfsCase String
+hi def link fsOptionsHpfsChkdsk String
+hi def link fsOptionsHpfsEas String
hi def link fsOptionsIsoMap String
+hi def link fsOptionsKeywords Keyword
+hi def link fsOptionsNfsLocalLock String
+hi def link fsOptionsNfsLookupCache String
+hi def link fsOptionsNilfs2Order String
+hi def link fsOptionsNtfsMftZoneMultiplier Number
+hi def link fsOptionsNumber Number
+hi def link fsOptionsNumberOctal Number
+hi def link fsOptionsNumberSigned Number
+hi def link fsOptionsOcfs2Coherency String
+hi def link fsOptionsOcfs2ResvLevel Number
+hi def link fsOptionsOverlayRedirectDir String
+hi def link fsOptionsQnx4Bitmap String
+hi def link fsOptionsQnx6Hold String
+hi def link fsOptionsQnx6Sync String
hi def link fsOptionsReiserHash String
+hi def link fsOptionsSecurityMode String
+hi def link fsOptionsSize Number
hi def link fsOptionsSshYesNoAsk String
-hi def link fsOptionsUfsType String
+hi def link fsOptionsString String
+hi def link fsOptionsTmpfsHuge String
hi def link fsOptionsUfsError String
-
+hi def link fsOptionsUfsType String
+hi def link fsOptionsV9Debug String
+hi def link fsOptionsV9Trans String
+hi def link fsOptionsV9Version String
+hi def link fsOptionsVfatNfs String
hi def link fsOptionsVfatShortname String
+hi def link fsOptionsXfsLogBufs Number
+
+hi def link fsOptionsTrueFalse Boolean
+hi def link fsOptionsYesNo String
+hi def link fsOptionsYN String
+hi def link fsOptions01 Number
let b:current_syntax = "fstab"
diff --git a/runtime/syntax/gdresource.vim b/runtime/syntax/gdresource.vim
new file mode 100644
index 0000000000..7e1a2513e2
--- /dev/null
+++ b/runtime/syntax/gdresource.vim
@@ -0,0 +1,65 @@
+" Vim syntax file for Godot resource (scenes)
+" Language: gdresource
+" Maintainer: Maxim Kim <habamax@gmail.com>
+" Filenames: *.tscn, *.tres
+" Website: https://github.com/habamax/vim-gdscript
+
+if exists("b:current_syntax")
+ finish
+endif
+
+let s:keepcpo = &cpo
+set cpo&vim
+
+syn match gdResourceNumber "\<0x\%(_\=\x\)\+\>"
+syn match gdResourceNumber "\<0b\%(_\=[01]\)\+\>"
+syn match gdResourceNumber "\<\d\%(_\=\d\)*\>"
+syn match gdResourceNumber "\<\d\%(_\=\d\)*\%(e[+-]\=\d\%(_\=\d\)*\)\=\>"
+syn match gdResourceNumber "\<\d\%(_\=\d\)*\.\%(e[+-]\=\d\%(_\=\d\)*\)\=\%(\W\|$\)\@="
+syn match gdResourceNumber "\%(^\|\W\)\@1<=\%(\d\%(_\=\d\)*\)\=\.\d\%(_\=\d\)*\%(e[+-]\=\d\%(_\=\d\)*\)\=\>"
+
+syn keyword gdResourceKeyword true false
+
+syn region gdResourceString
+ \ start=+[uU]\="+ end='"' skip='\\\\\|\\"'
+ \ contains=@Spell keepend
+
+" Section
+syn region gdResourceSection matchgroup=gdResourceSectionDelimiter
+ \ start='^\[' end=']\s*$'
+ \ oneline keepend
+ \ contains=gdResourceSectionName,gdResourceSectionAttribute
+
+syn match gdResourceSectionName '\[\@<=\S\+' contained skipwhite
+syn match gdResourceSectionAttribute '\S\+\s*=\s*\S\+'
+ \ skipwhite keepend contained
+ \ contains=gdResourceSectionAttributeName,gdResourceSectionAttributeValue
+syn match gdResourceSectionAttributeName '\S\+\ze\(\s*=\)' skipwhite contained
+syn match gdResourceSectionAttributeValue '\(=\s*\)\zs\S\+\ze' skipwhite
+ \ contained
+ \ contains=gdResourceString,gdResourceNumber,gdResourceKeyword
+
+
+" Section body
+syn match gdResourceAttribute '^\s*\S\+\s*=.*$'
+ \ skipwhite keepend
+ \ contains=gdResourceAttributeName,gdResourceAttributeValue
+
+syn match gdResourceAttributeName '\S\+\ze\(\s*=\)' skipwhite contained
+syn match gdResourceAttributeValue '\(=\s*\)\zs.*$' skipwhite
+ \ contained
+ \ contains=gdResourceString,gdResourceNumber,gdResourceKeyword
+
+
+hi def link gdResourceNumber Constant
+hi def link gdResourceKeyword Constant
+hi def link gdResourceSectionName Statement
+hi def link gdResourceSectionDelimiter Delimiter
+hi def link gdResourceSectionAttributeName Type
+hi def link gdResourceAttributeName Identifier
+hi def link gdResourceString String
+
+let b:current_syntax = "gdresource"
+
+let &cpo = s:keepcpo
+unlet s:keepcpo
diff --git a/runtime/syntax/gdscript.vim b/runtime/syntax/gdscript.vim
new file mode 100644
index 0000000000..48af153513
--- /dev/null
+++ b/runtime/syntax/gdscript.vim
@@ -0,0 +1,103 @@
+" Vim syntax file for Godot gdscript
+" Language: gdscript
+" Maintainer: Maxim Kim <habamax@gmail.com>
+" Website: https://github.com/habamax/vim-gdscript
+" Filenames: *.gd
+
+if exists("b:current_syntax")
+ finish
+endif
+
+let s:keepcpo = &cpo
+set cpo&vim
+
+syntax sync maxlines=100
+
+syn keyword gdscriptConditional if else elif match
+syn keyword gdscriptRepeat for while break continue
+
+syn keyword gdscriptOperator is as not and or in
+
+syn match gdscriptBlockStart ":\s*$"
+
+syn keyword gdscriptKeyword null self owner parent tool
+syn keyword gdscriptBoolean false true
+
+syn keyword gdscriptStatement remote master puppet remotesync mastersync puppetsync sync
+syn keyword gdscriptStatement return pass
+syn keyword gdscriptStatement static const enum
+syn keyword gdscriptStatement breakpoint assert
+syn keyword gdscriptStatement onready
+syn keyword gdscriptStatement class_name extends
+
+syn keyword gdscriptType void bool int float String contained
+syn match gdscriptType ":\s*\zs\h\w*" contained
+syn match gdscriptType "->\s*\zs\h\w*" contained
+
+syn keyword gdscriptStatement var nextgroup=gdscriptTypeDecl skipwhite
+syn keyword gdscriptStatement const nextgroup=gdscriptTypeDecl skipwhite
+syn match gdscriptTypeDecl "\h\w*\s*:\s*\h\w*" contains=gdscriptType contained skipwhite
+syn match gdscriptTypeDecl "->\s*\h\w*" contains=gdscriptType skipwhite
+
+syn keyword gdscriptStatement export nextgroup=gdscriptExportTypeDecl skipwhite
+syn match gdscriptExportTypeDecl "(.\{-}[,)]" contains=gdscriptOperator,gdscriptType contained skipwhite
+
+syn keyword gdscriptStatement setget nextgroup=gdscriptSetGet,gdscriptSetGetSeparator skipwhite
+syn match gdscriptSetGet "\h\w*" nextgroup=gdscriptSetGetSeparator display contained skipwhite
+syn match gdscriptSetGetSeparator "," nextgroup=gdscriptSetGet display contained skipwhite
+
+syn keyword gdscriptStatement class func signal nextgroup=gdscriptFunctionName skipwhite
+syn match gdscriptFunctionName "\h\w*" nextgroup=gdscriptFunctionParams display contained skipwhite
+syn match gdscriptFunctionParams "(.*)" contains=gdscriptTypeDecl display contained skipwhite
+
+syn match gdscriptNode "\$\h\w*\%(/\h\w*\)*"
+
+syn match gdscriptComment "#.*$" contains=@Spell,gdscriptTodo
+
+syn region gdscriptString matchgroup=gdscriptQuotes
+ \ start=+[uU]\=\z(['"]\)+ end="\z1" skip="\\\\\|\\\z1"
+ \ contains=gdscriptEscape,@Spell
+
+syn region gdscriptString matchgroup=gdscriptTripleQuotes
+ \ start=+[uU]\=\z('''\|"""\)+ end="\z1" keepend
+ \ contains=gdscriptEscape,@Spell
+
+syn match gdscriptEscape +\\[abfnrtv'"\\]+ contained
+syn match gdscriptEscape "\\$"
+
+" Numbers
+syn match gdscriptNumber "\<0x\%(_\=\x\)\+\>"
+syn match gdscriptNumber "\<0b\%(_\=[01]\)\+\>"
+syn match gdscriptNumber "\<\d\%(_\=\d\)*\>"
+syn match gdscriptNumber "\<\d\%(_\=\d\)*\%(e[+-]\=\d\%(_\=\d\)*\)\=\>"
+syn match gdscriptNumber "\<\d\%(_\=\d\)*\.\%(e[+-]\=\d\%(_\=\d\)*\)\=\%(\W\|$\)\@="
+syn match gdscriptNumber "\%(^\|\W\)\@1<=\%(\d\%(_\=\d\)*\)\=\.\d\%(_\=\d\)*\%(e[+-]\=\d\%(_\=\d\)*\)\=\>"
+
+" XXX, TODO, etc
+syn keyword gdscriptTodo TODO XXX FIXME HACK NOTE BUG contained
+
+hi def link gdscriptStatement Statement
+hi def link gdscriptKeyword Keyword
+hi def link gdscriptConditional Conditional
+hi def link gdscriptBoolean Boolean
+hi def link gdscriptOperator Operator
+hi def link gdscriptRepeat Repeat
+hi def link gdscriptSetGet Function
+hi def link gdscriptFunctionName Function
+hi def link gdscriptBuiltinStruct Typedef
+hi def link gdscriptComment Comment
+hi def link gdscriptString String
+hi def link gdscriptQuotes String
+hi def link gdscriptTripleQuotes String
+hi def link gdscriptEscape Special
+hi def link gdscriptNode PreProc
+hi def link gdscriptType Type
+hi def link gdscriptNumber Number
+hi def link gdscriptBlockStart Special
+hi def link gdscriptTodo Todo
+
+
+let b:current_syntax = "gdscript"
+
+let &cpo = s:keepcpo
+unlet s:keepcpo
diff --git a/runtime/syntax/gdshader.vim b/runtime/syntax/gdshader.vim
new file mode 100644
index 0000000000..f0d9f7edd9
--- /dev/null
+++ b/runtime/syntax/gdshader.vim
@@ -0,0 +1,57 @@
+" Vim syntax file for Godot shading language
+" Language: gdshader
+" Maintainer: Maxim Kim <habamax@gmail.com>
+" Filenames: *.gdshader
+
+if exists("b:current_syntax")
+ finish
+endif
+
+syn keyword gdshaderConditional if else switch case default
+syn keyword gdshaderRepeat for while do
+syn keyword gdshaderStatement return discard
+syn keyword gdshaderBoolean true false
+
+syn keyword gdshaderKeyword shader_type render_mode
+syn keyword gdshaderKeyword in out inout
+syn keyword gdshaderKeyword lowp mediump highp
+syn keyword gdshaderKeyword uniform varying const
+syn keyword gdshaderKeyword flat smooth
+
+syn keyword gdshaderType float vec2 vec3 vec4
+syn keyword gdshaderType uint uvec2 uvec3 uvec4
+syn keyword gdshaderType int ivec2 ivec3 ivec4
+syn keyword gdshaderType void bool
+syn keyword gdshaderType bvec2 bvec3 bvec4
+syn keyword gdshaderType mat2 mat3 mat4
+syn keyword gdshaderType sampler2D isampler2D usampler2D samplerCube
+
+syn match gdshaderMember "\v<(\.)@<=[a-z_]+\w*>"
+syn match gdshaderBuiltin "\v<[A-Z_]+[A-Z0-9_]*>"
+syn match gdshaderFunction "\v<\w*>(\()@="
+
+syn match gdshaderNumber "\v<\d+(\.)@!>"
+syn match gdshaderFloat "\v<\d*\.\d+(\.)@!>"
+syn match gdshaderFloat "\v<\d*\.=\d+(e-=\d+)@="
+syn match gdshaderExponent "\v(\d*\.=\d+)@<=e-=\d+>"
+
+syn match gdshaderComment "\v//.*$" contains=@Spell
+syn region gdshaderComment start="/\*" end="\*/" contains=@Spell
+syn keyword gdshaderTodo TODO FIXME XXX NOTE BUG HACK OPTIMIZE containedin=gdshaderComment
+
+hi def link gdshaderConditional Conditional
+hi def link gdshaderRepeat Repeat
+hi def link gdshaderStatement Statement
+hi def link gdshaderBoolean Boolean
+hi def link gdshaderKeyword Keyword
+hi def link gdshaderMember Identifier
+hi def link gdshaderBuiltin Identifier
+hi def link gdshaderFunction Function
+hi def link gdshaderType Type
+hi def link gdshaderNumber Number
+hi def link gdshaderFloat Float
+hi def link gdshaderExponent Special
+hi def link gdshaderComment Comment
+hi def link gdshaderTodo Todo
+
+let b:current_syntax = "gdshader"
diff --git a/runtime/syntax/gitattributes.vim b/runtime/syntax/gitattributes.vim
new file mode 100644
index 0000000000..b6d997f45d
--- /dev/null
+++ b/runtime/syntax/gitattributes.vim
@@ -0,0 +1,63 @@
+" Vim syntax file
+" Language: git attributes
+" Maintainer: ObserverOfTime <chronobserver@disroot.org>
+" Filenames: .gitattributes, *.git/info/attributes
+" Last Change: 2022 Sep 09
+
+if exists('b:current_syntax')
+ finish
+endif
+
+let s:cpo_save = &cpoptions
+set cpoptions&vim
+
+" Comment
+syn keyword gitattributesTodo contained TODO FIXME XXX
+syn match gitattributesComment /^\s*#.*/ contains=gitattributesTodo
+
+" Pattern
+syn match gitattributesPattern /^\s*#\@!\(".\+"\|\S\+\)/ skipwhite
+ \ nextgroup=gitattributesAttrPrefixed,gitattributesAttrAssigned skipwhite
+ \ contains=gitattributesGlob,gitattributesRange,gitattributesSeparator
+syn match gitattributesGlob /\\\@1<![?*]/ contained
+syn match gitattributesRange /\\\@1<!\[.\{-}\]/ contained
+syn match gitattributesSeparator '/' contained
+
+" Attribute
+syn match gitattributesAttrPrefixed /[!-]\?[A-Za-z0-9_.][-A-Za-z0-9_.]*/
+ \ transparent contained skipwhite
+ \ nextgroup=gitattributesAttrPrefixed,gitattributesAttrAssigned
+ \ contains=gitattributesPrefix,gitattributesName
+syn match gitattributesAttrAssigned /[A-Za-z0-9_.][-A-Za-z0-9_.]*=\S\+/
+ \ transparent contained skipwhite
+ \ nextgroup=gitattributesAttrPrefixed,gitattributesAttrAssigned
+ \ contains=gitattributesName,gitattributesAssign,gitattributesBoolean,gitattributesString
+syn match gitattributesName /[A-Za-z0-9_.][-A-Za-z0-9_.]*/
+ \ contained nextgroup=gitattributesAssign
+syn match gitattributesPrefix /[!-]/ contained
+ \ nextgroup=gitAttributesName
+syn match gitattributesAssign '=' contained
+ \ nextgroup=gitattributesBoolean,gitattributesString
+syn match gitattributesString /=\@1<=\S\+/ contained
+syn keyword gitattributesBoolean true false contained
+
+" Macro
+syn match gitattributesMacro /^\s*\[attr\]\s*\S\+/
+ \ nextgroup=gitattributesAttribute skipwhite
+
+hi def link gitattributesAssign Operator
+hi def link gitattributesBoolean Boolean
+hi def link gitattributesComment Comment
+hi def link gitattributesGlob Special
+hi def link gitattributesMacro Define
+hi def link gitattributesName Identifier
+hi def link gitattributesPrefix SpecialChar
+hi def link gitattributesRange Special
+hi def link gitattributesSeparator Delimiter
+hi def link gitattributesString String
+hi def link gitattributesTodo Todo
+
+let b:current_syntax = 'gitattributes'
+
+let &cpoptions = s:cpo_save
+unlet s:cpo_save
diff --git a/runtime/syntax/gitignore.vim b/runtime/syntax/gitignore.vim
new file mode 100644
index 0000000000..8e6d098acd
--- /dev/null
+++ b/runtime/syntax/gitignore.vim
@@ -0,0 +1,29 @@
+" Vim syntax file
+" Language: git ignore
+" Maintainer: ObserverOfTime <chronobserver@disroot.org>
+" Filenames: .gitignore, *.git/info/exclude
+" Last Change: 2022 Sep 10
+
+if exists('b:current_syntax')
+ finish
+endif
+
+" Comment
+syn keyword gitignoreTodo contained TODO FIXME XXX
+syn match gitignoreComment /^#.*/ contains=gitignoreTodo
+
+" Pattern
+syn match gitignorePattern /^#\@!.*$/ contains=gitignoreNegation,gitignoreGlob,gitignoreRange,gitignoreSeparator
+syn match gitignoreNegation /^!/ contained
+syn match gitignoreGlob /\\\@1<![?*]/ contained
+syn match gitignoreRange /\\\@1<!\[.\{-}\]/ contained
+syn match gitignoreSeparator '/' contained
+
+hi def link gitignoreComment Comment
+hi def link gitignoreGlob Special
+hi def link gitignoreNegation SpecialChar
+hi def link gitignoreRange Special
+hi def link gitignoreSeparator Delimiter
+hi def link gitignoreTodo Todo
+
+let b:current_syntax = 'gitignore'
diff --git a/runtime/syntax/go.vim b/runtime/syntax/go.vim
index 0c326254b8..904c8ad7f2 100644
--- a/runtime/syntax/go.vim
+++ b/runtime/syntax/go.vim
@@ -5,7 +5,7 @@
" go.vim: Vim syntax file for Go.
" Language: Go
" Maintainer: Billie Cleek <bhcleek@gmail.com>
-" Latest Revision: 2021-09-18
+" Latest Revision: 2022-11-17
" License: BSD-style. See LICENSE file in source repository.
" Repository: https://github.com/fatih/vim-go
@@ -117,7 +117,7 @@ hi def link goLabel Label
hi def link goRepeat Repeat
" Predefined types
-syn keyword goType chan map bool string error
+syn keyword goType chan map bool string error any comparable
syn keyword goSignedInts int int8 int16 int32 int64 rune
syn keyword goUnsignedInts byte uint uint8 uint16 uint32 uint64 uintptr
syn keyword goFloats float32 float64
@@ -187,6 +187,8 @@ else
syn region goRawString start=+`+ end=+`+
endif
+syn match goImportString /^\%(\s\+\|import \)\(\h\w* \)\?\zs"[^"]\+"$/ contained containedin=goImport
+
if s:HighlightFormatStrings()
" [n] notation is valid for specifying explicit argument indexes
" 1. Match a literal % not preceded by a %.
@@ -204,6 +206,7 @@ if s:HighlightFormatStrings()
hi def link goFormatSpecifier goSpecialString
endif
+hi def link goImportString String
hi def link goString String
hi def link goRawString String
@@ -223,9 +226,9 @@ endif
" import
if s:FoldEnable('import')
- syn region goImport start='import (' end=')' transparent fold contains=goImport,goString,goComment
+ syn region goImport start='import (' end=')' transparent fold contains=goImport,goImportString,goComment
else
- syn region goImport start='import (' end=')' transparent contains=goImport,goString,goComment
+ syn region goImport start='import (' end=')' transparent contains=goImport,goImportString,goComment
endif
" var, const
@@ -245,14 +248,10 @@ endif
syn match goSingleDecl /\%(import\|var\|const\) [^(]\@=/ contains=goImport,goVar,goConst
" Integers
-syn match goDecimalInt "\<-\=\(0\|[1-9]_\?\(\d\|\d\+_\?\d\+\)*\)\%([Ee][-+]\=\d\+\)\=\>"
-syn match goDecimalError "\<-\=\(_\(\d\+_*\)\+\|\([1-9]\d*_*\)\+__\(\d\+_*\)\+\|\([1-9]\d*_*\)\+_\+\)\%([Ee][-+]\=\d\+\)\=\>"
-syn match goHexadecimalInt "\<-\=0[xX]_\?\(\x\+_\?\)\+\>"
-syn match goHexadecimalError "\<-\=0[xX]_\?\(\x\+_\?\)*\(\([^ \t0-9A-Fa-f_)]\|__\)\S*\|_\)\>"
-syn match goOctalInt "\<-\=0[oO]\?_\?\(\o\+_\?\)\+\>"
-syn match goOctalError "\<-\=0[0-7oO_]*\(\([^ \t0-7oOxX_/)\]\}\:;]\|[oO]\{2,\}\|__\)\S*\|_\|[oOxX]\)\>"
-syn match goBinaryInt "\<-\=0[bB]_\?\([01]\+_\?\)\+\>"
-syn match goBinaryError "\<-\=0[bB]_\?[01_]*\([^ \t01_)]\S*\|__\S*\|_\)\>"
+syn match goDecimalInt "\<-\=\%(0\|\%(\d\|\d_\d\)\+\)\>"
+syn match goHexadecimalInt "\<-\=0[xX]_\?\%(\x\|\x_\x\)\+\>"
+syn match goOctalInt "\<-\=0[oO]\?_\?\%(\o\|\o_\o\)\+\>"
+syn match goBinaryInt "\<-\=0[bB]_\?\%([01]\|[01]_[01]\)\+\>"
hi def link goDecimalInt Integer
hi def link goDecimalError Error
@@ -265,19 +264,55 @@ hi def link goBinaryError Error
hi def link Integer Number
" Floating point
-syn match goFloat "\<-\=\d\+\.\d*\%([Ee][-+]\=\d\+\)\=\>"
-syn match goFloat "\<-\=\.\d\+\%([Ee][-+]\=\d\+\)\=\>"
+"float_lit = decimal_float_lit | hex_float_lit .
+"
+"decimal_float_lit = decimal_digits "." [ decimal_digits ] [ decimal_exponent ] |
+" decimal_digits decimal_exponent |
+" "." decimal_digits [ decimal_exponent ] .
+"decimal_exponent = ( "e" | "E" ) [ "+" | "-" ] decimal_digits .
+"
+"hex_float_lit = "0" ( "x" | "X" ) hex_mantissa hex_exponent .
+"hex_mantissa = [ "_" ] hex_digits "." [ hex_digits ] |
+" [ "_" ] hex_digits |
+" "." hex_digits .
+"hex_exponent = ( "p" | "P" ) [ "+" | "-" ] decimal_digits .
+" decimal floats with a decimal point
+syn match goFloat "\<-\=\%(0\|\%(\d\|\d_\d\)\+\)\.\%(\%(\%(\d\|\d_\d\)\+\)\=\%([Ee][-+]\=\%(\d\|\d_\d\)\+\)\=\>\)\="
+syn match goFloat "\s\zs-\=\.\%(\d\|\d_\d\)\+\%(\%([Ee][-+]\=\%(\d\|\d_\d\)\+\)\>\)\="
+" decimal floats without a decimal point
+syn match goFloat "\<-\=\%(0\|\%(\d\|\d_\d\)\+\)[Ee][-+]\=\%(\d\|\d_\d\)\+\>"
+" hexadecimal floats with a decimal point
+syn match goHexadecimalFloat "\<-\=0[xX]\%(_\x\|\x\)\+\.\%(\%(\x\|\x_\x\)\+\)\=\%([Pp][-+]\=\%(\d\|\d_\d\)\+\)\=\>"
+syn match goHexadecimalFloat "\<-\=0[xX]\.\%(\x\|\x_\x\)\+\%([Pp][-+]\=\%(\d\|\d_\d\)\+\)\=\>"
+" hexadecimal floats without a decimal point
+syn match goHexadecimalFloat "\<-\=0[xX]\%(_\x\|\x\)\+[Pp][-+]\=\%(\d\|\d_\d\)\+\>"
hi def link goFloat Float
+hi def link goHexadecimalFloat Float
" Imaginary literals
-syn match goImaginary "\<-\=\d\+i\>"
-syn match goImaginary "\<-\=\d\+[Ee][-+]\=\d\+i\>"
-syn match goImaginaryFloat "\<-\=\d\+\.\d*\%([Ee][-+]\=\d\+\)\=i\>"
-syn match goImaginaryFloat "\<-\=\.\d\+\%([Ee][-+]\=\d\+\)\=i\>"
-
-hi def link goImaginary Number
-hi def link goImaginaryFloat Float
+syn match goImaginaryDecimal "\<-\=\%(0\|\%(\d\|\d_\d\)\+\)i\>"
+syn match goImaginaryHexadecimal "\<-\=0[xX]_\?\%(\x\|\x_\x\)\+i\>"
+syn match goImaginaryOctal "\<-\=0[oO]\?_\?\%(\o\|\o_\o\)\+i\>"
+syn match goImaginaryBinary "\<-\=0[bB]_\?\%([01]\|[01]_[01]\)\+i\>"
+
+" imaginary decimal floats with a decimal point
+syn match goImaginaryFloat "\<-\=\%(0\|\%(\d\|\d_\d\)\+\)\.\%(\%(\%(\d\|\d_\d\)\+\)\=\%([Ee][-+]\=\%(\d\|\d_\d\)\+\)\=\)\=i\>"
+syn match goImaginaryFloat "\s\zs-\=\.\%(\d\|\d_\d\)\+\%([Ee][-+]\=\%(\d\|\d_\d\)\+\)\=i\>"
+" imaginary decimal floats without a decimal point
+syn match goImaginaryFloat "\<-\=\%(0\|\%(\d\|\d_\d\)\+\)[Ee][-+]\=\%(\d\|\d_\d\)\+i\>"
+" imaginary hexadecimal floats with a decimal point
+syn match goImaginaryHexadecimalFloat "\<-\=0[xX]\%(_\x\|\x\)\+\.\%(\%(\x\|\x_\x\)\+\)\=\%([Pp][-+]\=\%(\d\|\d_\d\)\+\)\=i\>"
+syn match goImaginaryHexadecimalFloat "\<-\=0[xX]\.\%(\x\|\x_\x\)\+\%([Pp][-+]\=\%(\d\|\d_\d\)\+\)\=i\>"
+" imaginary hexadecimal floats without a decimal point
+syn match goImaginaryHexadecimalFloat "\<-\=0[xX]\%(_\x\|\x\)\+[Pp][-+]\=\%(\d\|\d_\d\)\+i\>"
+
+hi def link goImaginaryDecimal Number
+hi def link goImaginaryHexadecimal Number
+hi def link goImaginaryOctal Number
+hi def link goImaginaryBinary Number
+hi def link goImaginaryFloat Float
+hi def link goImaginaryHexadecimalFloat Float
" Spaces after "[]"
if s:HighlightArrayWhitespaceError()
@@ -346,6 +381,8 @@ if s:HighlightOperators()
syn match goOperator /\%(<<\|>>\|&^\)=\?/
" match remaining two-char operators: := && || <- ++ --
syn match goOperator /:=\|||\|<-\|++\|--/
+ " match ~
+ syn match goOperator /\~/
" match ...
hi def link goPointerOperator goOperator
@@ -353,13 +390,37 @@ if s:HighlightOperators()
endif
hi def link goOperator Operator
+" -> type constraint opening bracket
+" |-> start non-counting group
+" || -> any word character
+" || | -> at least one, as many as possible
+" || | | -> start non-counting group
+" || | | | -> match ~
+" || | | | | -> at most once
+" || | | | | | -> allow a slice type
+" || | | | | | | -> any word character
+" || | | | | | | | -> start a non-counting group
+" || | | | | | | | | -> that matches word characters and |
+" || | | | | | | | | | -> close the non-counting group
+" || | | | | | | | | | | -> close the non-counting group
+" || | | | | | | | | | | |-> any number of matches
+" || | | | | | | | | | | || -> start a non-counting group
+" || | | | | | | | | | | || | -> a comma and whitespace
+" || | | | | | | | | | | || | | -> at most once
+" || | | | | | | | | | | || | | | -> close the non-counting group
+" || | | | | | | | | | | || | | | | -> at least one of those non-counting groups, as many as possible
+" || | | | | | -------- | | | | || | | | | | -> type constraint closing bracket
+" || | | | | || | | | | | || | | | | | |
+syn match goTypeParams /\[\%(\w\+\s\+\%(\~\?\%(\[]\)\?\w\%(\w\||\)\)*\%(,\s*\)\?\)\+\]/ nextgroup=goSimpleParams,goDeclType contained
+
" Functions;
if s:HighlightFunctions() || s:HighlightFunctionParameters()
syn match goDeclaration /\<func\>/ nextgroup=goReceiver,goFunction,goSimpleParams skipwhite skipnl
+ syn match goReceiverDecl /(\s*\zs\%(\%(\w\+\s\+\)\?\*\?\w\+\%(\[\%(\%(\[\]\)\?\w\+\%(,\s*\)\?\)\+\]\)\?\)\ze\s*)/ contained contains=goReceiverVar,goReceiverType,goPointerOperator
syn match goReceiverVar /\w\+\ze\s\+\%(\w\|\*\)/ nextgroup=goPointerOperator,goReceiverType skipwhite skipnl contained
syn match goPointerOperator /\*/ nextgroup=goReceiverType contained skipwhite skipnl
- syn match goFunction /\w\+/ nextgroup=goSimpleParams contained skipwhite skipnl
- syn match goReceiverType /\w\+/ contained
+ syn match goFunction /\w\+/ nextgroup=goSimpleParams,goTypeParams contained skipwhite skipnl
+ syn match goReceiverType /\w\+\%(\[\%(\%(\[\]\)\?\w\+\%(,\s*\)\?\)\+\]\)\?\ze\s*)/ contained
if s:HighlightFunctionParameters()
syn match goSimpleParams /(\%(\w\|\_s\|[*\.\[\],\{\}<>-]\)*)/ contained contains=goParamName,goType nextgroup=goFunctionReturn skipwhite skipnl
syn match goFunctionReturn /(\%(\w\|\_s\|[*\.\[\],\{\}<>-]\)*)/ contained contains=goParamName,goType skipwhite skipnl
@@ -369,7 +430,7 @@ if s:HighlightFunctions() || s:HighlightFunctionParameters()
hi def link goReceiverVar goParamName
hi def link goParamName Identifier
endif
- syn match goReceiver /(\s*\w\+\%(\s\+\*\?\s*\w\+\)\?\s*)\ze\s*\w/ contained nextgroup=goFunction contains=goReceiverVar skipwhite skipnl
+ syn match goReceiver /(\s*\%(\w\+\s\+\)\?\*\?\s*\w\+\%(\[\%(\%(\[\]\)\?\w\+\%(,\s*\)\?\)\+\]\)\?\s*)\ze\s*\w/ contained nextgroup=goFunction contains=goReceiverDecl skipwhite skipnl
else
syn keyword goDeclaration func
endif
@@ -377,7 +438,7 @@ hi def link goFunction Function
" Function calls;
if s:HighlightFunctionCalls()
- syn match goFunctionCall /\w\+\ze(/ contains=goBuiltins,goDeclaration
+ syn match goFunctionCall /\w\+\ze\%(\[\%(\%(\[]\)\?\w\+\(,\s*\)\?\)\+\]\)\?(/ contains=goBuiltins,goDeclaration
endif
hi def link goFunctionCall Type
@@ -404,7 +465,7 @@ hi def link goField Identifier
if s:HighlightTypes()
syn match goTypeConstructor /\<\w\+{\@=/
syn match goTypeDecl /\<type\>/ nextgroup=goTypeName skipwhite skipnl
- syn match goTypeName /\w\+/ contained nextgroup=goDeclType skipwhite skipnl
+ syn match goTypeName /\w\+/ contained nextgroup=goDeclType,goTypeParams skipwhite skipnl
syn match goDeclType /\<\%(interface\|struct\)\>/ skipwhite skipnl
hi def link goReceiverType Type
else
@@ -444,7 +505,7 @@ if s:HighlightBuildConstraints()
" The rs=s+2 option lets the \s*+build portion be part of the inner region
" instead of the matchgroup so it will be highlighted as a goBuildKeyword.
syn region goBuildComment matchgroup=goBuildCommentStart
- \ start="//\s*+build\s"rs=s+2 end="$"
+ \ start="//\(\s*+build\s\|go:build\)"rs=s+2 end="$"
\ contains=goBuildKeyword,goBuildDirectives
hi def link goBuildCommentStart Comment
hi def link goBuildDirectives Type
diff --git a/runtime/syntax/gyp.vim b/runtime/syntax/gyp.vim
new file mode 100644
index 0000000000..14c07b8726
--- /dev/null
+++ b/runtime/syntax/gyp.vim
@@ -0,0 +1,49 @@
+" Vim syntax file
+" Language: GYP
+" Maintainer: ObserverOfTime <chronobserver@disroot.org>
+" Filenames: *.gyp,*.gypi
+" Last Change: 2022 Sep 27
+
+if !exists('g:main_syntax')
+ if exists('b:current_syntax') && b:current_syntax ==# 'gyp'
+ finish
+ endif
+ let g:main_syntax = 'gyp'
+endif
+
+" Based on JSON syntax
+runtime! syntax/json.vim
+
+" Single quotes are allowed
+syn clear jsonStringSQError
+
+syn match jsonKeywordMatch /'\([^']\|\\\'\)\+'[[:blank:]\r\n]*\:/ contains=jsonKeyword
+if has('conceal') && (!exists('g:vim_json_conceal') || g:vim_json_conceal==1)
+ syn region jsonKeyword matchgroup=jsonQuote start=/'/ end=/'\ze[[:blank:]\r\n]*\:/ concealends contained
+else
+ syn region jsonKeyword matchgroup=jsonQuote start=/'/ end=/'\ze[[:blank:]\r\n]*\:/ contained
+endif
+
+syn match jsonStringMatch /'\([^']\|\\\'\)\+'\ze[[:blank:]\r\n]*[,}\]]/ contains=jsonString
+if has('conceal') && (!exists('g:vim_json_conceal') || g:vim_json_conceal==1)
+ syn region jsonString oneline matchgroup=jsonQuote start=/'/ skip=/\\\\\|\\'/ end=/'/ concealends contains=jsonEscape contained
+else
+ syn region jsonString oneline matchgroup=jsonQuote start=/'/ skip=/\\\\\|\\'/ end=/'/ contains=jsonEscape contained
+endif
+
+" Trailing commas are allowed
+if !exists('g:vim_json_warnings') || g:vim_json_warnings==1
+ syn clear jsonTrailingCommaError
+endif
+
+" Python-style comments are allowed
+syn match jsonComment /#.*$/ contains=jsonTodo,@Spell
+syn keyword jsonTodo FIXME NOTE TODO XXX TBD contained
+
+hi def link jsonComment Comment
+hi def link jsonTodo Todo
+
+let b:current_syntax = 'gyp'
+if g:main_syntax ==# 'gyp'
+ unlet g:main_syntax
+endif
diff --git a/runtime/syntax/hare.vim b/runtime/syntax/hare.vim
new file mode 100644
index 0000000000..07cf33fb11
--- /dev/null
+++ b/runtime/syntax/hare.vim
@@ -0,0 +1,133 @@
+" PRELUDE {{{1
+" Vim syntax file
+" Language: Hare
+" Maintainer: Amelia Clarke <me@rsaihe.dev>
+" Last Change: 2022-09-21
+
+if exists("b:current_syntax")
+ finish
+endif
+let b:current_syntax = "hare"
+
+" SYNTAX {{{1
+syn case match
+
+" KEYWORDS {{{2
+syn keyword hareConditional if else match switch
+syn keyword hareKeyword break continue return yield
+syn keyword hareKeyword defer
+syn keyword hareKeyword fn
+syn keyword hareKeyword let
+syn keyword hareLabel case
+syn keyword hareOperator as is
+syn keyword hareRepeat for
+syn keyword hareStorageClass const def export nullable static
+syn keyword hareStructure enum struct union
+syn keyword hareTypedef type
+
+" C ABI.
+syn keyword hareKeyword vastart vaarg vaend
+
+" BUILTINS {{{2
+syn keyword hareBuiltin abort
+syn keyword hareBuiltin alloc free
+syn keyword hareBuiltin append delete insert
+syn keyword hareBuiltin assert
+syn keyword hareBuiltin len offset
+
+" TYPES {{{2
+syn keyword hareType bool
+syn keyword hareType char str
+syn keyword hareType f32 f64
+syn keyword hareType u8 u16 u32 u64 i8 i16 i32 i64
+syn keyword hareType uint int
+syn keyword hareType rune
+syn keyword hareType uintptr
+syn keyword hareType void
+
+" C ABI.
+syn keyword hareType valist
+
+" LITERALS {{{2
+syn keyword hareBoolean true false
+syn keyword hareNull null
+
+" Number literals.
+syn match hareNumber "\v(\.@1<!|\.\.)\zs<\d+([Ee][+-]?\d+)?(z|[iu](8|16|32|64)?)?>" display
+syn match hareNumber "\v(\.@1<!|\.\.)\zs<0b[01]+(z|[iu](8|16|32|64)?)?>" display
+syn match hareNumber "\v(\.@1<!|\.\.)\zs<0o\o+(z|[iu](8|16|32|64)?)?>" display
+syn match hareNumber "\v(\.@1<!|\.\.)\zs<0x\x+(z|[iu](8|16|32|64)?)?>" display
+
+" Floating-point number literals.
+syn match hareFloat "\v<\d+\.\d+([Ee][+-]?\d+)?(f32|f64)?>" display
+syn match hareFloat "\v<\d+([Ee][+-]?\d+)?(f32|f64)>" display
+
+" String and rune literals.
+syn match hareEscape "\\[\\'"0abfnrtv]" contained display
+syn match hareEscape "\v\\(x\x{2}|u\x{4}|U\x{8})" contained display
+syn match hareFormat "\v\{\d*(\%\d*|(:[ 0+-]?\d*(\.\d+)?[Xbox]?))?}" contained display
+syn match hareFormat "\({{\|}}\)" contained display
+syn region hareRune start="'" end="'\|$" skip="\\'" contains=hareEscape display extend
+syn region hareString start=+"+ end=+"\|$+ skip=+\\"+ contains=hareEscape,hareFormat display extend
+syn region hareString start="`" end="`\|$" contains=hareFormat display
+
+" MISCELLANEOUS {{{2
+syn keyword hareTodo FIXME TODO XXX contained
+
+" Attributes.
+syn match hareAttribute "@[a-z]*"
+
+" Blocks.
+syn region hareBlock start="{" end="}" fold transparent
+
+" Comments.
+syn region hareComment start="//" end="$" contains=hareCommentDoc,hareTodo,@Spell display keepend
+syn region hareCommentDoc start="\[\[" end="]]\|\ze\_s" contained display
+
+" The size keyword can be either a builtin or a type.
+syn match hareBuiltin "\v<size>\ze(\_s*//.*\_$)*\_s*\(" contains=hareComment
+syn match hareType "\v<size>((\_s*//.*\_$)*\_s*\()@!" contains=hareComment
+
+" Trailing whitespace.
+syn match hareSpaceError "\v\s+$" display excludenl
+syn match hareSpaceError "\v\zs +\ze\t" display
+
+" Use statement.
+syn region hareUse start="\v^\s*\zsuse>" end=";" contains=hareComment display
+
+syn match hareErrorAssertion "\v(^([^/]|//@!)*\)\_s*)@<=!\=@!"
+syn match hareQuestionMark "?"
+
+" DEFAULT HIGHLIGHTING {{{1
+hi def link hareAttribute Keyword
+hi def link hareBoolean Boolean
+hi def link hareBuiltin Function
+hi def link hareComment Comment
+hi def link hareCommentDoc SpecialComment
+hi def link hareConditional Conditional
+hi def link hareEscape SpecialChar
+hi def link hareFloat Float
+hi def link hareFormat SpecialChar
+hi def link hareKeyword Keyword
+hi def link hareLabel Label
+hi def link hareNull Constant
+hi def link hareNumber Number
+hi def link hareOperator Operator
+hi def link hareQuestionMark Special
+hi def link hareRepeat Repeat
+hi def link hareRune Character
+hi def link hareStorageClass StorageClass
+hi def link hareString String
+hi def link hareStructure Structure
+hi def link hareTodo Todo
+hi def link hareType Type
+hi def link hareTypedef Typedef
+hi def link hareUse PreProc
+
+hi def link hareSpaceError Error
+autocmd InsertEnter * hi link hareSpaceError NONE
+autocmd InsertLeave * hi link hareSpaceError Error
+
+hi def hareErrorAssertion ctermfg=red cterm=bold guifg=red gui=bold
+
+" vim: tabstop=8 shiftwidth=2 expandtab
diff --git a/runtime/syntax/help.vim b/runtime/syntax/help.vim
index 01915d23d7..8b469d7242 100644
--- a/runtime/syntax/help.vim
+++ b/runtime/syntax/help.vim
@@ -1,7 +1,7 @@
" Vim syntax file
" Language: Vim help file
" Maintainer: Bram Moolenaar (Bram@vim.org)
-" Last Change: 2021 Jun 13
+" Last Change: 2022 Nov 13
" Quit when a (custom) syntax file was already loaded
if exists("b:current_syntax")
@@ -11,13 +11,14 @@ endif
let s:cpo_save = &cpo
set cpo&vim
-syn match helpHeadline "^[-A-Z .][-A-Z0-9 .()_]*\ze\(\s\+\*\|$\)"
+syn match helpHeadline "^[A-Z.][-A-Z0-9 .,()_']*?\=\ze\(\s\+\*\|$\)"
syn match helpSectionDelim "^===.*===$"
syn match helpSectionDelim "^---.*--$"
+" Neovim: support language annotation in codeblocks
if has("conceal")
- syn region helpExample matchgroup=helpIgnore start=" >$" start="^>$" end="^[^ \t]"me=e-1 end="^<" concealends
+ syn region helpExample matchgroup=helpIgnore start=" >[a-z0-9]*$" start="^>[a-z0-9]*$" end="^[^ \t]"me=e-1 end="^<" concealends
else
- syn region helpExample matchgroup=helpIgnore start=" >$" start="^>$" end="^[^ \t]"me=e-1 end="^<"
+ syn region helpExample matchgroup=helpIgnore start=" >[a-z0-9]*$" start="^>[a-z0-9]*$" end="^[^ \t]"me=e-1 end="^<"
endif
syn match helpHyperTextJump "\\\@<!|[#-)!+-~]\+|" contains=helpBar
syn match helpHyperTextEntry "\*[#-)!+-~]\+\*\s"he=e-1 contains=helpStar
@@ -39,6 +40,7 @@ syn match helpVim "VIM REFERENCE.*"
syn match helpVim "NVIM REFERENCE.*"
syn match helpOption "'[a-z]\{2,\}'"
syn match helpOption "'t_..'"
+syn match helpNormal "'ab'"
syn match helpCommand "`[^` \t]\+`"hs=s+1,he=e-1 contains=helpBacktick
syn match helpCommand "\(^\|[^a-z"[]\)\zs`[^`]\+`\ze\([^a-z\t."']\|$\)"hs=s+1,he=e-1 contains=helpBacktick
syn match helpHeader "\s*\zs.\{-}\ze\s\=\~$" nextgroup=helpIgnore
diff --git a/runtime/syntax/hgcommit.vim b/runtime/syntax/hgcommit.vim
index 37fe9db8bf..e9f31bef61 100644
--- a/runtime/syntax/hgcommit.vim
+++ b/runtime/syntax/hgcommit.vim
@@ -1,8 +1,8 @@
" Vim syntax file
-" Language: hg (Mercurial) commit file
+" Language: hg/sl (Mercurial / Sapling) commit file
" Maintainer: Ken Takata <kentkt at csc dot jp>
-" Last Change: 2012 Aug 23
-" Filenames: hg-editor-*.txt
+" Max Coplan <mchcopl@gmail.com>
+" Last Change: 2022-12-08
" License: VIM License
" URL: https://github.com/k-takata/hg-vim
@@ -10,12 +10,15 @@ if exists("b:current_syntax")
finish
endif
-syn match hgcommitComment "^HG:.*$" contains=@NoSpell
-syn match hgcommitUser "^HG: user: \zs.*$" contains=@NoSpell contained containedin=hgcommitComment
-syn match hgcommitBranch "^HG: branch \zs.*$" contains=@NoSpell contained containedin=hgcommitComment
-syn match hgcommitAdded "^HG: \zsadded .*$" contains=@NoSpell contained containedin=hgcommitComment
-syn match hgcommitChanged "^HG: \zschanged .*$" contains=@NoSpell contained containedin=hgcommitComment
-syn match hgcommitRemoved "^HG: \zsremoved .*$" contains=@NoSpell contained containedin=hgcommitComment
+syn match hgcommitComment "^\%(SL\|HG\): .*$" contains=@NoSpell
+syn match hgcommitUser "^\%(SL\|HG\): user: \zs.*$" contains=@NoSpell contained containedin=hgcommitComment
+syn match hgcommitBranch "^\%(SL\|HG\): branch \zs.*$" contains=@NoSpell contained containedin=hgcommitComment
+syn match hgcommitAdded "^\%(SL\|HG\): \zsadded .*$" contains=@NoSpell contained containedin=hgcommitComment
+syn match hgcommitChanged "^\%(SL\|HG\): \zschanged .*$" contains=@NoSpell contained containedin=hgcommitComment
+syn match hgcommitRemoved "^\%(SL\|HG\): \zsremoved .*$" contains=@NoSpell contained containedin=hgcommitComment
+
+syn region hgcommitDiff start=/\%(^\(SL\|HG\): diff --\%(git\|cc\|combined\) \)\@=/ end=/^\%(diff --\|$\|@@\@!\|[^[:alnum:]\ +-]\S\@!\)\@=/ fold contains=@hgcommitDiff
+syn include @hgcommitDiff syntax/shared/hgcommitDiff.vim
hi def link hgcommitComment Comment
hi def link hgcommitUser String
diff --git a/runtime/syntax/hlsplaylist.vim b/runtime/syntax/hlsplaylist.vim
new file mode 100644
index 0000000000..245eee213b
--- /dev/null
+++ b/runtime/syntax/hlsplaylist.vim
@@ -0,0 +1,120 @@
+" Vim syntax file
+" Language: HLS Playlist
+" Maintainer: Benoît Ryder <benoit@ryder.fr>
+" Latest Revision: 2022-09-23
+
+if exists("b:current_syntax")
+ finish
+endif
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+" Comment line
+syn match hlsplaylistComment "^#\(EXT\)\@!.*$"
+" Segment URL
+syn match hlsplaylistUrl "^[^#].*$"
+
+" Unknown tags, assume an attribute list or nothing
+syn match hlsplaylistTagUnknown "^#EXT[^:]*$"
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagUnknown start="^#EXT[^:]*\ze:" end="$" keepend contains=hlsplaylistAttributeList
+
+" Basic Tags
+syn match hlsplaylistTagHeader "^#EXTM3U$"
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagHeader start="^#EXT-X-VERSION\ze:" end="$" keepend contains=hlsplaylistValueInt
+
+" Media or Multivariant Playlist Tags
+syn match hlsplaylistTagHeader "^#EXT-X-INDEPENDENT-SEGMENTS$"
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagDelimiter start="^#EXT-X-START\ze:" end="$" keepend contains=hlsplaylistAttributeList
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStandard start="^#EXT-X-DEFINE\ze:" end="$" keepend contains=hlsplaylistAttributeList
+
+" Media Playlist Tags
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagHeader start="^#EXT-X-TARGETDURATION\ze:" end="$" keepend contains=hlsplaylistValueFloat
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagHeader start="^#EXT-X-MEDIA-SEQUENCE\ze:" end="$" keepend contains=hlsplaylistValueInt
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagHeader start="^#EXT-X-DISCONTINUITY-SEQUENCE\ze:" end="$" keepend contains=hlsplaylistValueInt
+syn match hlsplaylistTagDelimiter "^#EXT-X-ENDLIST$"
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagHeader start="^#EXT-X-PLAYLIST-TYPE\ze:" end="$" keepend contains=hlsplaylistAttributeEnum
+syn match hlsplaylistTagStandard "^#EXT-X-I-FRAME-ONLY$"
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagHeader start="^#EXT-X-PART-INF\ze:" end="$" keepend contains=hlsplaylistAttributeList
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagHeader start="^#EXT-X-SERVER-CONTROL\ze:" end="$" keepend contains=hlsplaylistAttributeList
+
+" Media Segment Tags
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStatement start="^#EXTINF\ze:" end="$" keepend contains=hlsplaylistValueFloat,hlsplaylistExtInfDesc
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStandard start="^#EXT-X-BYTERANGE\ze:" end="$" keepend contains=hlsplaylistValueInt
+syn match hlsplaylistTagDelimiter "^#EXT-X-DISCONTINUITY$"
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStandard start="^#EXT-X-KEY\ze:" end="$" keepend contains=hlsplaylistAttributeList
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStandard start="^#EXT-X-MAP\ze:" end="$" keepend contains=hlsplaylistAttributeList
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStandard start="^#EXT-X-PROGRAM-DATE-TIME\ze:" end="$" keepend contains=hlsplaylistValueDateTime
+syn match hlsplaylistTagDelimiter "^#EXT-X-GAP$"
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStandard start="^#EXT-X-BITRATE\ze:" end="$" keepend contains=hlsplaylistValueFloat
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStatement start="^#EXT-X-PART\ze:" end="$" keepend contains=hlsplaylistAttributeList
+
+" Media Metadata Tags
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStandard start="^#EXT-X-DATERANGE\ze:" end="$" keepend contains=hlsplaylistAttributeList
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStandard start="^#EXT-X-SKIP\ze:" end="$" keepend contains=hlsplaylistAttributeList
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStatement start="^#EXT-X-PRELOAD-HINT\ze:" end="$" keepend contains=hlsplaylistAttributeList
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStatement start="^#EXT-X-RENDITION-REPORT\ze:" end="$" keepend contains=hlsplaylistAttributeList
+
+" Multivariant Playlist Tags
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStandard start="^#EXT-X-MEDIA\ze:" end="$" keepend contains=hlsplaylistAttributeList
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStatement start="^#EXT-X-STREAM-INF\ze:" end="$" keepend contains=hlsplaylistAttributeList
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStatement start="^#EXT-X-I-FRAME-STREAM-INF\ze:" end="$" keepend contains=hlsplaylistAttributeList
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStandard start="^#EXT-X-SESSION-DATA\ze:" end="$" keepend contains=hlsplaylistAttributeList
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStandard start="^#EXT-X-SESSION-KEY\ze:" end="$" keepend contains=hlsplaylistAttributeList
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStandard start="^#EXT-X-CONTENT-STEERING\ze:" end="$" keepend contains=hlsplaylistAttributeList
+
+" Attributes
+syn region hlsplaylistAttributeList start=":" end="$" keepend contained
+ \ contains=hlsplaylistAttributeName,hlsplaylistAttributeInt,hlsplaylistAttributeHex,hlsplaylistAttributeFloat,hlsplaylistAttributeString,hlsplaylistAttributeEnum,hlsplaylistAttributeResolution,hlsplaylistAttributeUri
+" Common attributes
+syn match hlsplaylistAttributeName "[A-Za-z-]\+\ze=" contained
+syn match hlsplaylistAttributeEnum "=\zs[A-Za-z][A-Za-z0-9-_]*" contained
+syn match hlsplaylistAttributeString +=\zs"[^"]*"+ contained
+syn match hlsplaylistAttributeInt "=\zs\d\+" contained
+syn match hlsplaylistAttributeFloat "=\zs-\?\d*\.\d*" contained
+syn match hlsplaylistAttributeHex "=\zs0[xX]\d*" contained
+syn match hlsplaylistAttributeResolution "=\zs\d\+x\d\+" contained
+" Allow different highligting for URI attributes
+syn region hlsplaylistAttributeUri matchgroup=hlsplaylistAttributeName start="\zsURI\ze" end="\(,\|$\)" contained contains=hlsplaylistUriQuotes
+syn region hlsplaylistUriQuotes matchgroup=hlsplaylistAttributeString start=+"+ end=+"+ keepend contained contains=hlsplaylistUriValue
+syn match hlsplaylistUriValue /[^" ]\+/ contained
+" Individual values
+syn match hlsplaylistValueInt "[0-9]\+" contained
+syn match hlsplaylistValueFloat "\(\d\+\|\d*\.\d*\)" contained
+syn match hlsplaylistExtInfDesc ",\zs.*$" contained
+syn match hlsplaylistValueDateTime "\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d\(\.\d*\)\?\(Z\|\d\d:\?\d\d\)$" contained
+
+
+" Define default highlighting
+
+hi def link hlsplaylistComment Comment
+hi def link hlsplaylistUrl NONE
+
+hi def link hlsplaylistTagHeader Special
+hi def link hlsplaylistTagStandard Define
+hi def link hlsplaylistTagDelimiter Delimiter
+hi def link hlsplaylistTagStatement Statement
+hi def link hlsplaylistTagUnknown Special
+
+hi def link hlsplaylistUriQuotes String
+hi def link hlsplaylistUriValue Underlined
+hi def link hlsplaylistAttributeQuotes String
+hi def link hlsplaylistAttributeName Identifier
+hi def link hlsplaylistAttributeInt Number
+hi def link hlsplaylistAttributeHex Number
+hi def link hlsplaylistAttributeFloat Float
+hi def link hlsplaylistAttributeString String
+hi def link hlsplaylistAttributeEnum Constant
+hi def link hlsplaylistAttributeResolution Constant
+hi def link hlsplaylistValueInt Number
+hi def link hlsplaylistValueFloat Float
+hi def link hlsplaylistExtInfDesc String
+hi def link hlsplaylistValueDateTime Constant
+
+
+let b:current_syntax = "hlsplaylist"
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
+
+" vim: sts=2 sw=2 et
diff --git a/runtime/syntax/hollywood.vim b/runtime/syntax/hollywood.vim
index ce5ba29553..fcd03a68f0 100644
--- a/runtime/syntax/hollywood.vim
+++ b/runtime/syntax/hollywood.vim
@@ -1,14 +1,14 @@
" Vim syntax file
-" Language: Hollywood 9.0
-" Maintainer: Tom Crecelius <holly@net-eclipse.net>
-" First Author: Tom Crecelius <holly@net-eclipse.net>
-" Last Change: 2021 April 13
-" Highlighting Issues:
+" Language: Hollywood 9.1
+" Maintainer: Ola Sder <rolfkopman@gmail.com>
+" First Author: Tom Crecelius <holly@net-eclipse.net>
+" Last Change: 2022 Nov 09
+" Highlighting Issues:
" Depending on your colour schema, Strings or Comments might be highlighted in
" a way, you don't like. If so, try one of the following settings after
" opening a hollywood script:
"
-" :hi link hwString MoreMsg
+" :hi link hwString MoreMsg
" :hi link hwString NonText
" :hi link hwString String
"
@@ -60,10 +60,10 @@ syn region hwThenElse transparent matchgroup=hwCond start="\<Then\>" end="$" end
" If .. EndIf
syn region hwIfEndIf transparent matchgroup=hwCond start="\<If\>\(\(.\{-}Then.\{-}\)\@!\)" end="\<EndIf\>" contains=ALLBUT,hwTodo,hwSpecial,hwIn,hwStep,hwLineStatement skipwhite skipempty
-" Else ... EndIf
+" Else ... EndIf
syn region hwElseEndIf contained transparent matchgroup=hwCond start="\<Else\>" end="\<EndIf\>"me=e-5 contains=ALLBUT,hwTodo,hwSpecial,hwElseIf,hwElseEndIf,hwIn,hwStep,hwFallThrough,hwLineStatement
-" Then
+" Then
"syn keyword hwLineStatement Then contained
" Forever
syn keyword hwLineStatement Forever contained
@@ -92,7 +92,7 @@ syn region hwLoopBlock transparent matchgroup=hwRepeat start="\<Repeat\>" end="\
" While ... Wend/Do
syn region hwLoopBlock transparent matchgroup=hwRepeat start="\<While\>" end="\<Do\>" end="\<Wend\>" contains=ALLBUT,hwTodo,hwSpecial,hwElseIf,hwElse,hwIn,hwStep,hwLineStatement skipwhite skipempty
-" For .. To
+" For .. To
syn region hwForTo transparent matchgroup=hwRepeat start="\<For\>" end="\<To\>"me=e-2 skipwhite skipempty nextgroup=hwToNext
" To .. Next
@@ -113,7 +113,7 @@ syn match hwPreProcessor "@\<\%(ANIM\|APPAUTHOR\|APPCOPYRIGHT\|APPDESCRIPTION\|A
" predefined constants
syn match hwConstant "#\<\%(ACTIVEWINDOW\|ADF_ANIM\|ADF_FX\|ADF_MOVEOBJECT\|ALL\|ALPHABETICAL\|ALPHACHANNEL\|ALPHANUMERICAL\|AMIGAICON_DEVICE\|AMIGAICON_DISK\|AMIGAICON_DRAWER\|AMIGAICON_GARBAGE\|AMIGAICON_HIDE\|AMIGAICON_KICKSTART\|AMIGAICON_NONE\|AMIGAICON_PROJECT\|AMIGAICON_SETPOSITION\|AMIGAICON_SETTITLE\|AMIGAICON_SHOW\|AMIGAICON_TOOL\|ANIM\|ANIMSTREAM\|ANIMTYPE_RASTER\|ANIMTYPE_VECTOR\|ANMFMT_GIF\|ANMFMT_IFF\|ANMFMT_MJPEG\|ANTIALIAS\|AQUA\|ARC\|ASYNCDRAW\|ASYNCOBJ\|ATTRACTIVE\|ATTRADAPTER\|ATTRALPHAINTENSITY\|ATTRBGPIC\|ATTRBITRATE\|ATTRBORDERBOTTOM\|ATTRBORDERLEFT\|ATTRBORDERLESS\|ATTRBORDERPEN\|ATTRBORDERRIGHT\|ATTRBORDERTOP\|ATTRBULLETPEN\|ATTRCANSEEK\|ATTRCLIPREGION\|ATTRCOUNT\|ATTRCURFRAME\|ATTRCURSORX\|ATTRCURSORY\|ATTRCURSUBSONG\|ATTRCYCLE\|ATTRDENSITY\|ATTRDEPTH\|ATTRDISPLAY\|ATTRDITHERMODE\|ATTRDOUBLEBUFFER\|ATTRDRIVER\|ATTRDURATION\|ATTRELAPSE\|ATTRENCODING\|ATTRFIXED\|ATTRFONTAA\|ATTRFONTASCENDER\|ATTRFONTCHARMAP\|ATTRFONTDEPTH\|ATTRFONTDESCENDER\|ATTRFONTENGINE\|ATTRFONTNAME\|ATTRFONTPALETTE\|ATTRFONTSCALABLE\|ATTRFONTSIZE\|ATTRFONTTRANSPARENTPEN\|ATTRFONTTYPE\|ATTRFORMAT\|ATTRFRAMEDELAY\|ATTRFUNCTION\|ATTRGROUP\|ATTRHARDWARE\|ATTRHASALPHA\|ATTRHASMASK\|ATTRHEIGHT\|ATTRHOSTDEPTH\|ATTRHOSTHEIGHT\|ATTRHOSTMONITORS\|ATTRHOSTSCALE\|ATTRHOSTSCALEX\|ATTRHOSTSCALEY\|ATTRHOSTTASKBAR\|ATTRHOSTTITLEBARHEIGHT\|ATTRHOSTWIDTH\|ATTRID\|ATTRIMMERSIVEMODE\|ATTRINTERPOLATE\|ATTRKEYBOARD\|ATTRLAYERID\|ATTRLAYERS\|ATTRLAYERSON\|ATTRLOADER\|ATTRMARGINLEFT\|ATTRMARGINRIGHT\|ATTRMASKMODE\|ATTRMAXHEIGHT\|ATTRMAXIMIZED\|ATTRMAXWIDTH\|ATTRMENU\|ATTRMODE\|ATTRMONITOR\|ATTRNOCLOSE\|ATTRNOHIDE\|ATTRNOMODESWITCH\|ATTRNUMENTRIES\|ATTRNUMFRAMES\|ATTRNUMSUBSONGS\|ATTRONSCREEN\|ATTRORIENTATION\|ATTROUTPUTDEVICE\|ATTRPALETTE\|ATTRPALETTEMODE\|ATTRPAUSED\|ATTRPEN\|ATTRPITCH\|ATTRPLAYING\|ATTRPOINTER\|ATTRPOSITION\|ATTRPUBSCREEN\|ATTRRAWHEIGHT\|ATTRRAWWIDTH\|ATTRRAWXPOS\|ATTRRAWYPOS\|ATTRSCALEHEIGHT\|ATTRSCALEMODE\|ATTRSCALESWITCH\|ATTRSCALEWIDTH\|ATTRSHADOWPEN\|ATTRSIZE\|ATTRSIZEABLE\|ATTRSPRITES\|ATTRSTANDARD\|ATTRSTATE\|ATTRSYSTEMBARS\|ATTRTEXT\|ATTRTITLE\|ATTRTRANSPARENTCOLOR\|ATTRTRANSPARENTPEN\|ATTRTYPE\|ATTRUSERDATA\|ATTRVISIBLE\|ATTRWIDTH\|ATTRXDPI\|ATTRXPOS\|ATTRXSERVER\|ATTRYDPI\|ATTRYPOS\|ATTRZPOS\|BARS\|BAUD_115200\|BAUD_1200\|BAUD_19200\|BAUD_2400\|BAUD_300\|BAUD_38400\|BAUD_460800\|BAUD_4800\|BAUD_57600\|BAUD_600\|BAUD_9600\|BEEPERROR\|BEEPINFORMATION\|BEEPQUESTION\|BEEPSYSTEM\|BEEPWARNING\|BGPIC\|BGPICPART\|BIGENDIAN\|BIGSINE\|BITMAP_DEFAULT\|BLACK\|BLEND\|BLUE\|BOLD\|BOOLEAN\|BORDER\|BOTTOM\|BOTTOMOUT\|BOUNCE\|BOX\|BRUSH\|BRUSH_VS_BOX\|BRUSHPART\|BULLET_ARROW\|BULLET_BOX\|BULLET_CHECKMARK\|BULLET_CIRCLE\|BULLET_CROSS\|BULLET_DASH\|BULLET_DIAMOND\|BULLET_LALPHA\|BULLET_LALPHADOUBLE\|BULLET_LALPHASINGLE\|BULLET_LROMAN\|BULLET_LROMANDOUBLE\|BULLET_LROMANSINGLE\|BULLET_NONE\|BULLET_NUMERIC\|BULLET_NUMERICDOUBLE\|BULLET_NUMERICSINGLE\|BULLET_UALPHA\|BULLET_UALPHADOUBLE\|BULLET_UALPHASINGLE\|BULLET_UROMAN\|BULLET_UROMANDOUBLE\|BULLET_UROMANSINGLE\|BYTE\|CAPBUTT\|CAPROUND\|CAPSQUARE\|CARDBOTTOM\|CARDTOP\|CENTER\|CHARMAP_ADOBECUSTOM\|CHARMAP_ADOBEEXPERT\|CHARMAP_ADOBELATIN1\|CHARMAP_ADOBESTANDARD\|CHARMAP_APPLEROMAN\|CHARMAP_BIG5\|CHARMAP_DEFAULT\|CHARMAP_JOHAB\|CHARMAP_MSSYMBOL\|CHARMAP_OLDLATIN2\|CHARMAP_SJIS\|CHARMAP_UNICODE\|CHARMAP_WANSUNG\|CHIPMEMORY\|CIRCLE\|CLIENT\|CLIPBOARD_EMPTY\|CLIPBOARD_IMAGE\|CLIPBOARD_SOUND\|CLIPBOARD_TEXT\|CLIPBOARD_UNKNOWN\|CLIPREGION\|CLOCKWIPE\|CLOSEWINDOW\|CONICAL\|COPYFILE_FAILED\|COPYFILE_OVERWRITE\|COPYFILE_STATUS\|COPYFILE_UNPROTECT\|COUNTBOTH\|COUNTDIRECTORIES\|COUNTFILES\|COUNTRY_AFGHANISTAN\|COUNTRY_ALANDISLANDS\|COUNTRY_ALBANIA\|COUNTRY_ALGERIA\|COUNTRY_AMERICANSAMOA\|COUNTRY_ANDORRA\|COUNTRY_ANGOLA\|COUNTRY_ANGUILLA\|COUNTRY_ANTARCTICA\|COUNTRY_ANTIGUAANDBARBUDA\|COUNTRY_ARGENTINA\|COUNTRY_ARMENIA\|COUNTRY_ARUBA\|COUNTRY_AUSTRALIA\|COUNTRY_AUSTRIA\|COUNTRY_AZERBAIJAN\|COUNTRY_BAHAMAS\|COUNTRY_BAHRAIN\|COUNTRY_BANGLADESH\|COUNTRY_BARBADOS\|COUNTRY_BELARUS\|COUNTRY_BELGIUM\|COUNTRY_BELIZE\|COUNTRY_BENIN\|COUNTRY_BERMUDA\|COUNTRY_BESISLANDS\|COUNTRY_BHUTAN\|COUNTRY_BOLIVIA\|COUNTRY_BOSNIAANDHERZEGOVINA\|COUNTRY_BOTSWANA\|COUNTRY_BOUVETISLAND\|COUNTRY_BRAZIL\|COUNTRY_BRUNEI\|COUNTRY_BULGARIA\|COUNTRY_BURKINAFASO\|COUNTRY_BURUNDI\|COUNTRY_CAMBODIA\|COUNTRY_CAMEROON\|COUNTRY_CANADA\|COUNTRY_CAPEVERDE\|COUNTRY_CAYMANISLANDS\|COUNTRY_CENTRALAFRICANREPUBLIC\|COUNTRY_CHAD\|COUNTRY_CHILE\|COUNTRY_CHINA\|COUNTRY_CHRISTMASISLAND\|COUNTRY_COCOSISLANDS\|COUNTRY_COLOMBIA\|COUNTRY_COMOROS\|COUNTRY_CONGO\|COUNTRY_COOKISLANDS\|COUNTRY_COSTARICA\|COUNTRY_CROATIA\|COUNTRY_CUBA\|COUNTRY_CURACAO\|COUNTRY_CYPRUS\|COUNTRY_CZECHREPUBLIC\|COUNTRY_DENMARK\|COUNTRY_DJIBOUTI\|COUNTRY_DOMINICA\|COUNTRY_DOMINICANREPUBLIC\|COUNTRY_DRCONGO\|COUNTRY_ECUADOR\|COUNTRY_EGYPT\|COUNTRY_ELSALVADOR\|COUNTRY_EQUATORIALGUINEA\|COUNTRY_ERITREA\|COUNTRY_ESTONIA\|COUNTRY_ETHIOPIA\|COUNTRY_FALKLANDISLANDS\|COUNTRY_FAROEISLANDS\|COUNTRY_FIJI\|COUNTRY_FINLAND\|COUNTRY_FRANCE\|COUNTRY_FRENCHGUIANA\|COUNTRY_FRENCHPOLYNESIA\|COUNTRY_GABON\|COUNTRY_GAMBIA\|COUNTRY_GEORGIA\|COUNTRY_GERMANY\|COUNTRY_GHANA\|COUNTRY_GIBRALTAR\|COUNTRY_GREECE\|COUNTRY_GREENLAND\|COUNTRY_GRENADA\|COUNTRY_GUADELOUPE\|COUNTRY_GUAM\|COUNTRY_GUATEMALA\|COUNTRY_GUERNSEY\|COUNTRY_GUINEA\|COUNTRY_GUINEABISSAU\|COUNTRY_GUYANA\|COUNTRY_HAITI\|COUNTRY_HOLYSEE\|COUNTRY_HONDURAS\|COUNTRY_HONGKONG\|COUNTRY_HUNGARY\|COUNTRY_ICELAND\|COUNTRY_INDIA\|COUNTRY_INDONESIA\|COUNTRY_IRAN\|COUNTRY_IRAQ\|COUNTRY_IRELAND\|COUNTRY_ISLEOFMAN\|COUNTRY_ISRAEL\|COUNTRY_ITALY\|COUNTRY_IVORYCOAST\|COUNTRY_JAMAICA\|COUNTRY_JAPAN\|COUNTRY_JERSEY\|COUNTRY_JORDAN\|COUNTRY_KAZAKHSTAN\|COUNTRY_KENYA\|COUNTRY_KIRIBATI\|COUNTRY_KUWAIT\|COUNTRY_KYRGYZSTAN\|COUNTRY_LAOS\|COUNTRY_LATVIA\|COUNTRY_LEBANON\|COUNTRY_LESOTHO\|COUNTRY_LIBERIA\|COUNTRY_LIBYA\|COUNTRY_LIECHTENSTEIN\|COUNTRY_LITHUANIA\|COUNTRY_LUXEMBOURG\|COUNTRY_MACAO\|COUNTRY_MACEDONIA\|COUNTRY_MADAGASCAR\|COUNTRY_MALAWI\|COUNTRY_MALAYSIA\|COUNTRY_MALDIVES\|COUNTRY_MALI\|COUNTRY_MALTA\|COUNTRY_MARSHALLISLANDS\|COUNTRY_MARTINIQUE\|COUNTRY_MAURITANIA\|COUNTRY_MAURITIUS\|COUNTRY_MAYOTTE\|COUNTRY_MEXICO\|COUNTRY_MICRONESIA\|COUNTRY_MOLDOVA\|COUNTRY_MONACO\|COUNTRY_MONGOLIA\|COUNTRY_MONTENEGRO\|COUNTRY_MONTSERRAT\|COUNTRY_MOROCCO\|COUNTRY_MOZAMBIQUE\|COUNTRY_MYANMAR\|COUNTRY_NAMIBIA\|COUNTRY_NAURU\|COUNTRY_NEPAL\|COUNTRY_NETHERLANDS\|COUNTRY_NEWCALEDONIA\|COUNTRY_NEWZEALAND\|COUNTRY_NICARAGUA\|COUNTRY_NIGER\|COUNTRY_NIGERIA\|COUNTRY_NIUE\|COUNTRY_NORFOLKISLAND\|COUNTRY_NORTHKOREA\|COUNTRY_NORWAY\|COUNTRY_OMAN\|COUNTRY_PAKISTAN\|COUNTRY_PALAU\|COUNTRY_PALESTINE\|COUNTRY_PANAMA\|COUNTRY_PAPUANEWGUINEA\|COUNTRY_PARAGUAY\|COUNTRY_PERU\|COUNTRY_PHILIPPINES\|COUNTRY_PITCAIRN\|COUNTRY_POLAND\|COUNTRY_PORTUGAL\|COUNTRY_PUERTORICO\|COUNTRY_QATAR\|COUNTRY_REUNION\|COUNTRY_ROMANIA\|COUNTRY_RUSSIA\|COUNTRY_RWANDA\|COUNTRY_SAINTBARTHELEMY\|COUNTRY_SAINTHELENA\|COUNTRY_SAINTKITTSANDNEVIS\|COUNTRY_SAINTLUCIA\|COUNTRY_SAINTVINCENT\|COUNTRY_SAMOA\|COUNTRY_SANMARINO\|COUNTRY_SAOTOMEANDPRINCIPE\|COUNTRY_SAUDIARABIA\|COUNTRY_SENEGAL\|COUNTRY_SERBIA\|COUNTRY_SEYCHELLES\|COUNTRY_SIERRALEONE\|COUNTRY_SINGAPORE\|COUNTRY_SLOVAKIA\|COUNTRY_SLOVENIA\|COUNTRY_SOLOMONISLANDS\|COUNTRY_SOMALIA\|COUNTRY_SOUTHAFRICA\|COUNTRY_SOUTHKOREA\|COUNTRY_SOUTHSUDAN\|COUNTRY_SPAIN\|COUNTRY_SRILANKA\|COUNTRY_SUDAN\|COUNTRY_SURINAME\|COUNTRY_SWAZILAND\|COUNTRY_SWEDEN\|COUNTRY_SWITZERLAND\|COUNTRY_SYRIA\|COUNTRY_TAIWAN\|COUNTRY_TAJIKISTAN\|COUNTRY_TANZANIA\|COUNTRY_THAILAND\|COUNTRY_TIMOR\|COUNTRY_TOGO\|COUNTRY_TONGA\|COUNTRY_TRINIDADANDTOBAGO\|COUNTRY_TUNISIA\|COUNTRY_TURKEY\|COUNTRY_TURKMENISTAN\|COUNTRY_TUVALU\|COUNTRY_UAE\|COUNTRY_UGANDA\|COUNTRY_UK\|COUNTRY_UKRAINE\|COUNTRY_UNKNOWN\|COUNTRY_URUGUAY\|COUNTRY_USA\|COUNTRY_UZBEKISTAN\|COUNTRY_VANUATU\|COUNTRY_VENEZUELA\|COUNTRY_VIETNAM\|COUNTRY_YEMEN\|COUNTRY_ZAMBIA\|COUNTSEPARATE\|CR_DEAD\|CR_RUNNING\|CR_SUSPENDED\|CROSSFADE\|CRUSHBOTTOM\|CRUSHLEFT\|CRUSHRIGHT\|CRUSHTOP\|DAMPED\|DATA_5\|DATA_6\|DATA_7\|DATA_8\|DATEDAY\|DATELOCAL\|DATELOCALNATIVE\|DATEMONTH\|DATETIME\|DATEUTC\|DATEYEAR\|DEFAULTICON\|DEFAULTSPEED\|DEINTERLACE_DEFAULT\|DEINTERLACE_DOUBLE\|DELETEFILE_FAILED\|DELETEFILE_STATUS\|DELETEFILE_UNPROTECT\|DENSITY_HIGH\|DENSITY_LOW\|DENSITY_MEDIUM\|DENSITY_NONE\|DIAGONAL\|DIRECTORY\|DIRMONITOR_ADD\|DIRMONITOR_CHANGE\|DIRMONITOR_REMOVE\|DISPLAY\|DISPMODE_ASK\|DISPMODE_FAKEFULLSCREEN\|DISPMODE_FULLSCREEN\|DISPMODE_FULLSCREENSCALE\|DISPMODE_MODEREQUESTER\|DISPMODE_MODESWITCH\|DISPMODE_SYSTEMSCALE\|DISPMODE_WINDOWED\|DISPSTATE_CLOSED\|DISPSTATE_MINIMIZED\|DISPSTATE_OPEN\|DISSOLVE\|DITHERMODE_FLOYDSTEINBERG\|DITHERMODE_NONE\|DOSTYPE_DIRECTORY\|DOSTYPE_FILE\|DOUBLE\|DOUBLEBUFFER\|DOWNLOADFILE_STATUS\|DTR_OFF\|DTR_ON\|DURATION_LONG\|DURATION_SHORT\|EDGE\|ELLIPSE\|ENCODING_AMIGA\|ENCODING_ISO8859_1\|ENCODING_RAW\|ENCODING_UTF8\|EOF\|ERR_8OR16BITONLY\|ERR_ACCELERATOR\|ERR_ADDAPPICON\|ERR_ADDAPPWIN\|ERR_ADDSYSEVENT\|ERR_ADDTASK\|ERR_ADFFREEDISP\|ERR_ADFWRONGDISP\|ERR_AFILEPROP\|ERR_AHI\|ERR_ALLOCALPHA\|ERR_ALLOCCHANNEL\|ERR_ALLOCCHUNKY\|ERR_ALLOCMASK\|ERR_ALRDYDECLRD\|ERR_ALREADYASYNC\|ERR_ALSAPCM\|ERR_AMIGAGUIDE\|ERR_ANIMDISK\|ERR_ANIMFRAME\|ERR_ANTIALIAS\|ERR_APPLET\|ERR_APPLETVERSION\|ERR_APPLICATION\|ERR_ARGS\|ERR_ARRAYDECLA\|ERR_ASSERTFAILED\|ERR_ATSUI\|ERR_AUDIOCONVERTER\|ERR_BACKFILL\|ERR_BAD8SVX\|ERR_BADBASE64\|ERR_BADBYTECODE\|ERR_BADCALLBACKRET\|ERR_BADCONSTANT\|ERR_BADDIMENSIONS\|ERR_BADENCODING\|ERR_BADINTEGER\|ERR_BADIP\|ERR_BADLAYERTYPE\|ERR_BADPLATFORM\|ERR_BADSIGNATURE\|ERR_BADUPVALUES\|ERR_BADURL\|ERR_BADWAVE\|ERR_BADYIELD\|ERR_BEGINREFRESH\|ERR_BGPICBUTTON\|ERR_BGPICPALETTE\|ERR_BGPICTYPE\|ERR_BITMAP\|ERR_BLKWOENDBLK\|ERR_BRACECLOSE\|ERR_BRACEOPEN\|ERR_BRACKETCLOSE\|ERR_BRACKETOPEN\|ERR_BRUSHLINK\|ERR_BRUSHSIZE\|ERR_BRUSHTYPE\|ERR_CACHEERROR\|ERR_CASECST\|ERR_CHANGEDIR\|ERR_CHANNELRANGE\|ERR_CHRCSTEMPTY\|ERR_CHRCSTLEN\|ERR_CLIPFORMAT\|ERR_CLIPOPEN\|ERR_CLIPREAD\|ERR_CLIPWRITE\|ERR_CLOSEDDISPLAY\|ERR_CLOSEFILE\|ERR_CMDASVAR\|ERR_CMPUNSUPPORTED\|ERR_COLORSPACE\|ERR_COMMENTSTRUCT\|ERR_COMMODITY\|ERR_COMPLEXEXPR\|ERR_COMPLEXPATTERN\|ERR_COMPLEXWHILE\|ERR_CONCAT\|ERR_CONFIG\|ERR_CONFIG2\|ERR_CONITEMS\|ERR_CONSOLEARG\|ERR_CONTEXTMENU\|ERR_COORDSRANGE\|ERR_COREFOUNDATION\|ERR_CORETEXT\|ERR_CREATEDIR\|ERR_CREATEDOCKY\|ERR_CREATEEVENT\|ERR_CREATEGC\|ERR_CREATEICON\|ERR_CREATEMENU\|ERR_CREATEPORT\|ERR_CREATESHORTCUT\|ERR_CSTDOUBLEDEF\|ERR_CTRLSTRUCT\|ERR_CYIELD\|ERR_DATATYPEALPHA\|ERR_DATATYPESAVE\|ERR_DATATYPESAVE2\|ERR_DBLENCODING\|ERR_DBPALETTE\|ERR_DBTRANSWIN\|ERR_DBVIDEOLAYER\|ERR_DDAUTOSCALE\|ERR_DDMOBILE\|ERR_DDRECVIDEO\|ERR_DEADRESUME\|ERR_DEFFONT\|ERR_DELETEFILE\|ERR_DEMO\|ERR_DEMO2\|ERR_DEMO3\|ERR_DEPTHMISMATCH\|ERR_DEPTHRANGE\|ERR_DESERIALIZE\|ERR_DIFFDEPTH\|ERR_DIFFENCODING\|ERR_DINPUT\|ERR_DIRECTSHOW\|ERR_DIRLOCK\|ERR_DISPLAYADAPTERSUPPORT\|ERR_DISPLAYDESKTOP\|ERR_DISPLAYDESKTOPPAL\|ERR_DISPLAYSIZE\|ERR_DISPMINIMIZED\|ERR_DLOPEN\|ERR_DOUBLEDECLA\|ERR_DOUBLEMENU\|ERR_DRAWPATH\|ERR_DSOUNDNOTIFY\|ERR_DSOUNDNOTIPOS\|ERR_DSOUNDPLAY\|ERR_ELSEIFAFTERELSE\|ERR_ELSETWICE\|ERR_ELSEWOIF\|ERR_EMPTYMENUTREE\|ERR_EMPTYOBJ\|ERR_EMPTYPATH\|ERR_EMPTYSCRIPT\|ERR_EMPTYTABLE\|ERR_ENDBLKWOBLK\|ERR_ENDDOUBLEBUFFER\|ERR_ENDFUNCWOFUNC\|ERR_ENDIFWOIF\|ERR_ENDSWCHWOSWCH\|ERR_ENDWITHWOWITH\|ERR_EQUALEXPECTED\|ERR_ERRORCALLED\|ERR_ESCREPLACE\|ERR_EVNTEXPCTED\|ERR_EXAMINE\|ERR_EXECUTE\|ERR_EXETYPE\|ERR_FGRABVIDSTATE\|ERR_FIELDINIT\|ERR_FILEEXIST\|ERR_FILEFORMAT\|ERR_FILENOTFOUND\|ERR_FILESIZE\|ERR_FINDACTIVITY\|ERR_FINDANIM\|ERR_FINDANIMSTREAM\|ERR_FINDAPPLICATION\|ERR_FINDARRAY\|ERR_FINDASYNCDRAW\|ERR_FINDASYNCOBJ\|ERR_FINDBGPIC\|ERR_FINDBRUSH\|ERR_FINDBUTTON\|ERR_FINDCLIENT\|ERR_FINDCLIPREGION\|ERR_FINDCST\|ERR_FINDDIR\|ERR_FINDDISPLAY\|ERR_FINDFILE\|ERR_FINDFONT\|ERR_FINDFONT2\|ERR_FINDICON\|ERR_FINDINTERVAL\|ERR_FINDLAYER\|ERR_FINDLAYERDATA\|ERR_FINDMEMBLK\|ERR_FINDMENU\|ERR_FINDMENUITEM\|ERR_FINDMONITOR\|ERR_FINDMOVE\|ERR_FINDMUSIC\|ERR_FINDOBJECTDATA\|ERR_FINDPALETTE\|ERR_FINDPATH\|ERR_FINDPLUGIN\|ERR_FINDPOINTER\|ERR_FINDPORT\|ERR_FINDSAMPLE\|ERR_FINDSELECTOR\|ERR_FINDSERIAL\|ERR_FINDSERVER\|ERR_FINDSPRITE\|ERR_FINDTEXTOBJECT\|ERR_FINDTIMEOUT\|ERR_FINDTIMER\|ERR_FINDUDPOBJECT\|ERR_FINDVIDEO\|ERR_FIRSTPREPROC\|ERR_FONTFORMAT\|ERR_FONTPATH\|ERR_FONTPATH2\|ERR_FORBIDMODAL\|ERR_FOREVERWOREPEAT\|ERR_FORWONEXT\|ERR_FRAMEGRABBER\|ERR_FREEABGPIC\|ERR_FREEADISPLAY\|ERR_FREECURPOINTER\|ERR_FT2\|ERR_FTPAUTH\|ERR_FTPERROR\|ERR_FULLSCREEN\|ERR_FUNCARGS\|ERR_FUNCDECLA\|ERR_FUNCEXPECTED\|ERR_FUNCJMP\|ERR_FUNCREMOVED\|ERR_FUNCTABLEARG\|ERR_FUNCWOENDFUNC\|ERR_GETDISKOBJ\|ERR_GETIFADDRS\|ERR_GETMONITORINFO\|ERR_GETSHORTCUT\|ERR_GRABSCREEN\|ERR_GROUPNAMEUSED\|ERR_GTK\|ERR_GUIGFX\|ERR_HEXPOINT\|ERR_HOSTNAME\|ERR_HTTPERROR\|ERR_HTTPTE\|ERR_HWBMCLOSEDISP\|ERR_HWBRUSH\|ERR_HWBRUSHFUNC\|ERR_HWDBFREEDISP\|ERR_ICONDIMS\|ERR_ICONENTRY\|ERR_ICONPARMS\|ERR_ICONSIZE\|ERR_ICONSTANDARD\|ERR_ICONVECTOR\|ERR_IFWOENDIF\|ERR_IMAGEERROR\|ERR_INCOMPATBRUSH\|ERR_INISYNTAX\|ERR_INITSERIAL\|ERR_INTERNAL\|ERR_INTERNAL1\|ERR_INTEXPECTED\|ERR_INVALIDDATE\|ERR_INVALIDUTF8\|ERR_INVALIDUTF8ARG\|ERR_INVCAPIDX\|ERR_INVINSERT\|ERR_INVNEXTKEY\|ERR_INVPATCAP\|ERR_INVREPLACE\|ERR_JAVA\|ERR_JAVAMETHOD\|ERR_JOYSTICK\|ERR_KEYFILE\|ERR_KEYNOTFOUND\|ERR_KEYWORD\|ERR_KICKSTART\|ERR_LABELDECLA\|ERR_LABELDOUBLE\|ERR_LABINFOR\|ERR_LABINFUNC\|ERR_LABINIF\|ERR_LABINWHILE\|ERR_LABMAINBLK\|ERR_LAYERRANGE\|ERR_LAYERSOFF\|ERR_LAYERSON\|ERR_LAYERSUPPORT\|ERR_LAYERSUPPORT2\|ERR_LAYERSWITCH\|ERR_LEGACYPTMOD\|ERR_LFSYNTAX\|ERR_LINKFONT\|ERR_LINKPLUGIN\|ERR_LOADFRAME\|ERR_LOADICON\|ERR_LOADPICTURE\|ERR_LOADPICTURE2\|ERR_LOADPLUGIN\|ERR_LOADSOUND\|ERR_LOADVIDEO\|ERR_LOCK\|ERR_LOCK2\|ERR_LOCKBMAP\|ERR_LOCKEDOBJ\|ERR_LOOPRANGE\|ERR_LOWFREQ\|ERR_MAGICKEY\|ERR_MALFORMPAT1\|ERR_MALFORMPAT2\|ERR_MASKNALPHA\|ERR_MAXLINES\|ERR_MAXLOCALS\|ERR_MAXPARAMS\|ERR_MAXUPVALS\|ERR_MEDIAFOUNDATION\|ERR_MEM\|ERR_MEMCODE\|ERR_MEMCST\|ERR_MEMRANGE\|ERR_MENUATTACHED\|ERR_MENUCOMPLEXITY\|ERR_MISSINGBRACKET\|ERR_MISSINGFIELD\|ERR_MISSINGOPBRACK\|ERR_MISSINGPARAMTR\|ERR_MISSINGSEPARTR\|ERR_MIXMUSMOD\|ERR_MOBILE\|ERR_MODIFYAANIM\|ERR_MODIFYABG\|ERR_MODIFYABGPIC\|ERR_MODIFYABR\|ERR_MODIFYPSMP\|ERR_MODIFYSPRITE\|ERR_MODIFYSPRITE2\|ERR_MONITORDIR\|ERR_MONITORFULLSCREEN\|ERR_MONITORRANGE\|ERR_MOVEFILE\|ERR_MSGPORT\|ERR_MULDISMOBILE\|ERR_MULTIBGPIC\|ERR_MULTIDISPLAYS\|ERR_MUSFMTSUPPORT\|ERR_MUSNOTPAUSED\|ERR_MUSNOTPLYNG\|ERR_MUSNOTPLYNG2\|ERR_MUSPAUSED\|ERR_MUSPLAYING\|ERR_NAMETOOLONG\|ERR_NAMEUSED\|ERR_NEEDAPPLICATION\|ERR_NEEDCOMPOSITE\|ERR_NEEDMORPHOS2\|ERR_NEEDOS41\|ERR_NEEDPALETTEIMAGE\|ERR_NEGCOORDS\|ERR_NEWHWPLUGIN\|ERR_NEXTWOFOR\|ERR_NOABSPATH\|ERR_NOACCESS\|ERR_NOALPHA\|ERR_NOANMLAYER\|ERR_NOAPPLET\|ERR_NOARGBVISUAL\|ERR_NOBLOCKBREAK\|ERR_NOCALLBACK\|ERR_NOCHANNEL\|ERR_NOCHAR\|ERR_NOCLIPREG\|ERR_NOCOLON\|ERR_NOCOMMA\|ERR_NOCOMPRESS\|ERR_NOCONSTANTS\|ERR_NOCONTEXTMENU\|ERR_NOCOORDCST\|ERR_NODIRPATTERN\|ERR_NODISLAYERS\|ERR_NODISPMODES\|ERR_NODOUBLEBUFFER\|ERR_NOFALLTHROUGH\|ERR_NOFILTERNAME\|ERR_NOFMBHANDLER\|ERR_NOFUNCTION\|ERR_NOHWFUNC\|ERR_NOJOYATPORT\|ERR_NOKEYWORDS\|ERR_NOLAYERS\|ERR_NOLOOP\|ERR_NOLOOPCONT\|ERR_NOMASKBRUSH\|ERR_NOMENU\|ERR_NOMIMEVIEWER\|ERR_NOMUSICCB\|ERR_NONE\|ERR_NONSUSPENDEDRESUME\|ERR_NOPALETTE\|ERR_NOPALETTEIMAGE\|ERR_NOPALETTEMODE\|ERR_NORETVAL\|ERR_NOREXX\|ERR_NOSPRITES\|ERR_NOTADIR\|ERR_NOTENOUGHPIXELS\|ERR_NOTIGER\|ERR_NOTPROTRACKER\|ERR_NOTRANSPARENCY\|ERR_NOTXTLAYER\|ERR_NUMBEREXPECTED\|ERR_NUMCALLBACK\|ERR_NUMCONCAT\|ERR_NUMEXPECTED\|ERR_NUMSTRCMP\|ERR_NUMTABLEARG\|ERR_OLDAPPLET\|ERR_OPENANIM\|ERR_OPENANIM2\|ERR_OPENAUDIO\|ERR_OPENFONT\|ERR_OPENLIB\|ERR_OPENSERIAL\|ERR_OPENSOCKET\|ERR_OPENSOUND\|ERR_OPENSOUND2\|ERR_OUTOFRANGE\|ERR_PAKFORMAT\|ERR_PALETTEFILL\|ERR_PALETTEMODE\|ERR_PALSCREEN\|ERR_PEERNAME\|ERR_PENRANGE\|ERR_PERCENTFORMAT\|ERR_PERCENTFORMATSTR\|ERR_PIPE\|ERR_PIXELFORMAT\|ERR_PIXELRANGE\|ERR_PLAYERCOMP\|ERR_PLAYVIDEO\|ERR_PLUGINARCH\|ERR_PLUGINDOUBLET\|ERR_PLUGINSUPPORT\|ERR_PLUGINSYMBOL\|ERR_PLUGINTYPE\|ERR_PLUGINVER\|ERR_POINTERFORMAT\|ERR_POINTERIMG\|ERR_PORTNOTAVAIL\|ERR_PREPROCSYM\|ERR_PROTMETATABLE\|ERR_PUBSCREEN\|ERR_QUICKTIME\|ERR_RADIOTOGGLEMENU\|ERR_RANDOMIZE\|ERR_READ\|ERR_READFILE\|ERR_READFUNC\|ERR_READONLY\|ERR_READRANGE\|ERR_READTABLE\|ERR_READVIDEOPIXELS\|ERR_RECVCLOSED\|ERR_RECVTIMEOUT\|ERR_RECVUNKNOWN\|ERR_REGCLASS\|ERR_REGISTRYREAD\|ERR_REGISTRYWRITE\|ERR_REMADLAYER\|ERR_RENAME\|ERR_RENDER\|ERR_RENDERADLAYER\|ERR_RENDERCALLBACK\|ERR_RENDERER\|ERR_REPEATWOUNTIL\|ERR_REQAUTH\|ERR_REQUIREFIELD\|ERR_REQUIREPLUGIN\|ERR_REQUIRETAGFMT\|ERR_RETWOGOSUB\|ERR_REVDWORD\|ERR_REWINDDIR\|ERR_REXXERR\|ERR_SATELLITE\|ERR_SATFREEDISP\|ERR_SAVEANIM\|ERR_SAVEICON\|ERR_SAVEIMAGE\|ERR_SAVEPNG\|ERR_SAVERALPHA\|ERR_SAVESAMPLE\|ERR_SCALEBGPIC\|ERR_SCREEN\|ERR_SCREENMODE\|ERR_SCREENSIZE\|ERR_SCRPIXFMT\|ERR_SEEK\|ERR_SEEKFILE\|ERR_SEEKFORMAT\|ERR_SEEKRANGE\|ERR_SELECTALPHACHANNEL\|ERR_SELECTANIM\|ERR_SELECTBG\|ERR_SELECTBGPIC\|ERR_SELECTBGPIC2\|ERR_SELECTBRUSH\|ERR_SELECTMASK\|ERR_SEMAPHORE\|ERR_SENDDATA\|ERR_SENDMESSAGE\|ERR_SENDTIMEOUT\|ERR_SENDUNKNOWN\|ERR_SERIALIO\|ERR_SERIALIZE\|ERR_SERIALIZETYPE\|ERR_SETADAPTER\|ERR_SETENV\|ERR_SETFILEATTR\|ERR_SETFILECOMMENT\|ERR_SETFILEDATE\|ERR_SETMENU\|ERR_SHORTIF\|ERR_SIGNAL\|ERR_SMODEALPHA\|ERR_SMPRANGE\|ERR_SOCKET\|ERR_SOCKNAME\|ERR_SOCKOPT\|ERR_SORTFUNC\|ERR_SPRITELINK\|ERR_SPRITEONSCREEN\|ERR_SPRITEONSCREEN2\|ERR_SQBRACKETCLOSE\|ERR_SQBRACKETOPEN\|ERR_STACK\|ERR_STAT\|ERR_STRCALLBACK\|ERR_STREAMASSAMPLE\|ERR_STREXPECTED\|ERR_STRINGCST\|ERR_STRINGEXPECTED\|ERR_STRORNUM\|ERR_STRTABLEARG\|ERR_STRTOOSHORT\|ERR_SURFACE\|ERR_SWCHWOENDSWCH\|ERR_SYNTAXERROR\|ERR_SYNTAXLEVELS\|ERR_SYSBUTTON\|ERR_SYSIMAGE\|ERR_SYSTOOOLD\|ERR_TABCALLBACK\|ERR_TABEXPECTED\|ERR_TABEXPECTED2\|ERR_TABEXPECTED3\|ERR_TABLEDECLA\|ERR_TABLEINDEX\|ERR_TABLEORNIL\|ERR_TABLEOVERFLOW\|ERR_TAGEXPECTED\|ERR_TASKSETUP\|ERR_TEXTARG\|ERR_TEXTCONVERT\|ERR_TEXTCONVERT2\|ERR_TEXTSYNTAX\|ERR_TEXTURE\|ERR_TFIMAGE\|ERR_TFVANIM\|ERR_TFVBGPICBRUSH\|ERR_TFVBRUSH\|ERR_TFVBRUSHBGPIC\|ERR_THREAD\|ERR_THREADEXPECTED\|ERR_TIMER\|ERR_TOKENEXPECTED\|ERR_TOOMANYARGS\|ERR_TOOMANYCAPTURES\|ERR_TOOSMALL2\|ERR_TRANSBGMOBILE\|ERR_TRANSBRUSH\|ERR_TRAYICON\|ERR_TRIALCOMPILE\|ERR_TRIALINCLUDE\|ERR_TRIALLIMIT\|ERR_TRIALSAVEVID\|ERR_UDEXPECTED\|ERR_UNBALANCEDPAT\|ERR_UNEXPECTEDEOF\|ERR_UNEXPECTEDSYM\|ERR_UNFINISHEDCAPTURE\|ERR_UNIMPLCMD\|ERR_UNKNOWN\|ERR_UNKNOWNANMOUT\|ERR_UNKNOWNATTR\|ERR_UNKNOWNCMD\|ERR_UNKNOWNCOND\|ERR_UNKNOWNFILTER\|ERR_UNKNOWNICNOUT\|ERR_UNKNOWNIMGOUT\|ERR_UNKNOWNMIMETYPE\|ERR_UNKNOWNMUSFMT\|ERR_UNKNOWNPALETTE\|ERR_UNKNOWNSEC\|ERR_UNKNOWNSEQ\|ERR_UNKNOWNSMPOUT\|ERR_UNKNOWNTAG\|ERR_UNKNUMFMT\|ERR_UNKPROTOCOL\|ERR_UNKTEXTFMT\|ERR_UNMPARENTHESES\|ERR_UNSETENV\|ERR_UNSUPPORTEDFEAT\|ERR_UNTERMINTDSTR\|ERR_UNTILWOREPEAT\|ERR_UPDATEICON\|ERR_UPLOADFORBIDDEN\|ERR_USERABORT\|ERR_VALUEEXPECTED\|ERR_VAREXPECTED\|ERR_VARLENGTH\|ERR_VARSYNTAX\|ERR_VECGFXPLUGIN\|ERR_VECTORANIM\|ERR_VECTORBRUSH\|ERR_VERSION\|ERR_VFONT\|ERR_VFONTTYPE\|ERR_VIDATTACHED\|ERR_VIDEOFRAME\|ERR_VIDEOINIT\|ERR_VIDEOLAYER\|ERR_VIDEOLAYERDRV\|ERR_VIDEOSTRATEGY\|ERR_VIDEOTRANS\|ERR_VIDLAYERFUNC\|ERR_VIDNOTPAUSED\|ERR_VIDNOTPLAYING\|ERR_VIDPAUSED\|ERR_VIDPLAYING\|ERR_VIDRECMULTI\|ERR_VIDRECTRANS\|ERR_VIDSTOPPED\|ERR_VISUALINFO\|ERR_VMMISMATCH\|ERR_WARPOS\|ERR_WENDWOWHILE\|ERR_WHILEWOWEND\|ERR_WINDOW\|ERR_WITHWOENDWITH\|ERR_WRITE\|ERR_WRITEFILE\|ERR_WRITEJPEG\|ERR_WRITEONLY\|ERR_WRONGCLIPREG\|ERR_WRONGCMDRECVIDEO\|ERR_WRONGDTYPE\|ERR_WRONGFLOAT\|ERR_WRONGHEX\|ERR_WRONGID\|ERR_WRONGOP\|ERR_WRONGOPCST\|ERR_WRONGSPRITESIZE\|ERR_WRONGUSAGE\|ERR_WRONGVSTRATEGY\|ERR_XCURSOR\|ERR_XDISPLAY\|ERR_XF86VIDMODEEXT\|ERR_XFIXES\|ERR_YIELD\|ERR_ZERODIVISION\|ERR_ZLIBDATA\|ERR_ZLIBIO\|ERR_ZLIBSTREAM\|ERR_ZLIBVERSION\|EVENTHANDLER\|FADE\|FASTMEMORY\|FASTSPEED\|FILE\|FILEATTR_ARCHIVE\|FILEATTR_DELETE_USR\|FILEATTR_EXECUTE_GRP\|FILEATTR_EXECUTE_OTH\|FILEATTR_EXECUTE_USR\|FILEATTR_HIDDEN\|FILEATTR_NORMAL\|FILEATTR_PURE\|FILEATTR_READ_GRP\|FILEATTR_READ_OTH\|FILEATTR_READ_USR\|FILEATTR_READONLY\|FILEATTR_SCRIPT\|FILEATTR_SYSTEM\|FILEATTR_WRITE_GRP\|FILEATTR_WRITE_OTH\|FILEATTR_WRITE_USR\|FILETYPE_ANIM\|FILETYPE_ICON\|FILETYPE_IMAGE\|FILETYPE_SOUND\|FILETYPE_VIDEO\|FILETYPEFLAGS_ALPHA\|FILETYPEFLAGS_FPS\|FILETYPEFLAGS_QUALITY\|FILETYPEFLAGS_SAVE\|FILLCOLOR\|FILLGRADIENT\|FILLNONE\|FILLRULEEVENODD\|FILLRULEWINDING\|FILLTEXTURE\|FLOAT\|FLOW_HARDWARE\|FLOW_OFF\|FLOW_XON_XOFF\|FONT\|FONTENGINE_INBUILT\|FONTENGINE_NATIVE\|FONTSLANT_ITALIC\|FONTSLANT_OBLIQUE\|FONTSLANT_ROMAN\|FONTTYPE_BITMAP\|FONTTYPE_COLOR\|FONTTYPE_VECTOR\|FONTWEIGHT_BLACK\|FONTWEIGHT_BOLD\|FONTWEIGHT_BOOK\|FONTWEIGHT_DEMIBOLD\|FONTWEIGHT_EXTRABLACK\|FONTWEIGHT_EXTRABOLD\|FONTWEIGHT_EXTRALIGHT\|FONTWEIGHT_HEAVY\|FONTWEIGHT_LIGHT\|FONTWEIGHT_MEDIUM\|FONTWEIGHT_NORMAL\|FONTWEIGHT_REGULAR\|FONTWEIGHT_SEMIBOLD\|FONTWEIGHT_THIN\|FONTWEIGHT_ULTRABLACK\|FONTWEIGHT_ULTRABOLD\|FONTWEIGHT_ULTRALIGHT\|FRAMEMODE_FULL\|FRAMEMODE_SINGLE\|FREESPACE\|FTPASCII\|FTPBINARY\|FUCHSIA\|FUNCTION\|GRAY\|GREEN\|HBLINDS128\|HBLINDS16\|HBLINDS32\|HBLINDS64\|HBLINDS8\|HCLOSECURTAIN\|HCLOSEGATE\|HEXNUMERICAL\|HFLIPCOIN\|HFLOWBOTTOM\|HFLOWTOP\|HIDEBRUSH\|HIDELAYER\|HKEY_CLASSES_ROOT\|HKEY_CURRENT_CONFIG\|HKEY_CURRENT_USER\|HKEY_LOCAL_MACHINE\|HKEY_USERS\|HLINES\|HLINES2\|HLOWFLIPCOIN\|HOLLYWOOD\|HOPENCURTAIN\|HOPENGATE\|HSPLIT\|HSTRANGEPUSH\|HSTRETCHCENTER\|HSTRIPES\|HSTRIPES16\|HSTRIPES2\|HSTRIPES32\|HSTRIPES4\|HSTRIPES64\|HSTRIPES8\|HW_64BIT\|HW_AMIGA\|HW_AMIGAOS3\|HW_AMIGAOS4\|HW_ANDROID\|HW_AROS\|HW_IOS\|HW_LINUX\|HW_LITTLE_ENDIAN\|HW_MACOS\|HW_MORPHOS\|HW_REVISION\|HW_VERSION\|HW_WARPOS\|HW_WINDOWS\|ICNFMT_HOLLYWOOD\|ICON\|IMAGETYPE_RASTER\|IMAGETYPE_VECTOR\|IMGFMT_BMP\|IMGFMT_GIF\|IMGFMT_ILBM\|IMGFMT_JPEG\|IMGFMT_NATIVE\|IMGFMT_PLUGIN\|IMGFMT_PNG\|IMGFMT_TIFF\|IMGFMT_UNKNOWN\|IMMERSIVE_LEANBACK\|IMMERSIVE_NONE\|IMMERSIVE_NORMAL\|IMMERSIVE_STICKY\|INACTIVEWINDOW\|INF\|INSERTBRUSH\|INTEGER\|INTERVAL\|IO_BUFFERED\|IO_FAKE64\|IO_LITTLEENDIAN\|IO_SIGNED\|IO_UNBUFFERED\|IO_UNSIGNED\|IPAUTO\|IPUNKNOWN\|IPV4\|IPV6\|ITALIC\|JOINBEVEL\|JOINMITER\|JOINROUND\|JOYDOWN\|JOYDOWNLEFT\|JOYDOWNRIGHT\|JOYLEFT\|JOYNODIR\|JOYRIGHT\|JOYUP\|JOYUPLEFT\|JOYUPRIGHT\|JUSTIFIED\|KEEPASPRAT\|KEEPPOSITION\|LANGUAGE_ABKHAZIAN\|LANGUAGE_AFAR\|LANGUAGE_AFRIKAANS\|LANGUAGE_AKAN\|LANGUAGE_ALBANIAN\|LANGUAGE_AMHARIC\|LANGUAGE_ARABIC\|LANGUAGE_ARAGONESE\|LANGUAGE_ARMENIAN\|LANGUAGE_ASSAMESE\|LANGUAGE_AVARIC\|LANGUAGE_AVESTAN\|LANGUAGE_AYMARA\|LANGUAGE_AZERBAIJANI\|LANGUAGE_BAMBARA\|LANGUAGE_BASHKIR\|LANGUAGE_BASQUE\|LANGUAGE_BELARUSIAN\|LANGUAGE_BENGALI\|LANGUAGE_BIHARI\|LANGUAGE_BISLAMA\|LANGUAGE_BOSNIAN\|LANGUAGE_BRETON\|LANGUAGE_BULGARIAN\|LANGUAGE_BURMESE\|LANGUAGE_CATALAN\|LANGUAGE_CENTRALKHMER\|LANGUAGE_CHAMORRO\|LANGUAGE_CHECHEN\|LANGUAGE_CHICHEWA\|LANGUAGE_CHINESE\|LANGUAGE_CHURCHSLAVIC\|LANGUAGE_CHUVASH\|LANGUAGE_CORNISH\|LANGUAGE_CORSICAN\|LANGUAGE_CREE\|LANGUAGE_CROATIAN\|LANGUAGE_CZECH\|LANGUAGE_DANISH\|LANGUAGE_DIVEHI\|LANGUAGE_DUTCH\|LANGUAGE_DZONGKHA\|LANGUAGE_ENGLISH\|LANGUAGE_ESPERANTO\|LANGUAGE_ESTONIAN\|LANGUAGE_EWE\|LANGUAGE_FAROESE\|LANGUAGE_FIJIAN\|LANGUAGE_FINNISH\|LANGUAGE_FRENCH\|LANGUAGE_FULAH\|LANGUAGE_GAELIC\|LANGUAGE_GALICIAN\|LANGUAGE_GANDA\|LANGUAGE_GEORGIAN\|LANGUAGE_GERMAN\|LANGUAGE_GREEK\|LANGUAGE_GREENLANDIC\|LANGUAGE_GUARANI\|LANGUAGE_GUJARATI\|LANGUAGE_HAITIAN\|LANGUAGE_HAUSA\|LANGUAGE_HEBREW\|LANGUAGE_HERERO\|LANGUAGE_HINDI\|LANGUAGE_HIRIMOTU\|LANGUAGE_HUNGARIAN\|LANGUAGE_ICELANDIC\|LANGUAGE_IDO\|LANGUAGE_IGBO\|LANGUAGE_INDONESIAN\|LANGUAGE_INTERLINGUA\|LANGUAGE_INTERLINGUE\|LANGUAGE_INUKTITUT\|LANGUAGE_INUPIAQ\|LANGUAGE_IRISH\|LANGUAGE_ITALIAN\|LANGUAGE_JAPANESE\|LANGUAGE_JAVANESE\|LANGUAGE_KANNADA\|LANGUAGE_KANURI\|LANGUAGE_KASHMIRI\|LANGUAGE_KAZAKH\|LANGUAGE_KIKUYU\|LANGUAGE_KINYARWANDA\|LANGUAGE_KIRGHIZ\|LANGUAGE_KOMI\|LANGUAGE_KONGO\|LANGUAGE_KOREAN\|LANGUAGE_KUANYAMA\|LANGUAGE_KURDISH\|LANGUAGE_LAO\|LANGUAGE_LATIN\|LANGUAGE_LATVIAN\|LANGUAGE_LIMBURGAN\|LANGUAGE_LINGALA\|LANGUAGE_LITHUANIAN\|LANGUAGE_LUBAKATANGA\|LANGUAGE_LUXEMBOURGISH\|LANGUAGE_MACEDONIAN\|LANGUAGE_MALAGASY\|LANGUAGE_MALAY\|LANGUAGE_MALAYALAM\|LANGUAGE_MALTESE\|LANGUAGE_MANX\|LANGUAGE_MAORI\|LANGUAGE_MARATHI\|LANGUAGE_MARSHALLESE\|LANGUAGE_MONGOLIAN\|LANGUAGE_NAURU\|LANGUAGE_NAVAJO\|LANGUAGE_NDONGA\|LANGUAGE_NEPALI\|LANGUAGE_NORTHERNSAMI\|LANGUAGE_NORTHNDEBELE\|LANGUAGE_NORWEGIAN\|LANGUAGE_NORWEGIANBOKMAL\|LANGUAGE_NORWEGIANNYNORSK\|LANGUAGE_OCCITAN\|LANGUAGE_OJIBWA\|LANGUAGE_ORIYA\|LANGUAGE_OROMO\|LANGUAGE_OSSETIAN\|LANGUAGE_PALI\|LANGUAGE_PANJABI\|LANGUAGE_PASHTO\|LANGUAGE_PERSIAN\|LANGUAGE_POLISH\|LANGUAGE_PORTUGUESE\|LANGUAGE_QUECHUA\|LANGUAGE_ROMANIAN\|LANGUAGE_ROMANSH\|LANGUAGE_RUNDI\|LANGUAGE_RUSSIAN\|LANGUAGE_SAMOAN\|LANGUAGE_SANGO\|LANGUAGE_SANSKRIT\|LANGUAGE_SARDINIAN\|LANGUAGE_SERBIAN\|LANGUAGE_SHONA\|LANGUAGE_SICHUANYI\|LANGUAGE_SINDHI\|LANGUAGE_SINHALA\|LANGUAGE_SLOVAK\|LANGUAGE_SLOVENIAN\|LANGUAGE_SOMALI\|LANGUAGE_SOUTHERNSOTHO\|LANGUAGE_SOUTHNDEBELE\|LANGUAGE_SPANISH\|LANGUAGE_SUNDANESE\|LANGUAGE_SWAHILI\|LANGUAGE_SWATI\|LANGUAGE_SWEDISH\|LANGUAGE_TAGALOG\|LANGUAGE_TAHITIAN\|LANGUAGE_TAJIK\|LANGUAGE_TAMIL\|LANGUAGE_TATAR\|LANGUAGE_TELUGU\|LANGUAGE_THAI\|LANGUAGE_TIBETAN\|LANGUAGE_TIGRINYA\|LANGUAGE_TONGA\|LANGUAGE_TSONGA\|LANGUAGE_TSWANA\|LANGUAGE_TURKISH\|LANGUAGE_TURKMEN\|LANGUAGE_TWI\|LANGUAGE_UIGHUR\|LANGUAGE_UKRAINIAN\|LANGUAGE_UNKNOWN\|LANGUAGE_URDU\|LANGUAGE_UZBEK\|LANGUAGE_VENDA\|LANGUAGE_VIETNAMESE\|LANGUAGE_WALLOON\|LANGUAGE_WELSH\|LANGUAGE_WESTERNFRISIAN\|LANGUAGE_WOLOF\|LANGUAGE_XHOSA\|LANGUAGE_YIDDISH\|LANGUAGE_YORUBA\|LANGUAGE_ZHUANG\|LANGUAGE_ZULU\|LAYER\|LAYER_VS_BOX\|LAYERBUTTON\|LEFT\|LEFTOUT\|LIGHTUSERDATA\|LIME\|LINE\|LINEAR\|LITTLEENDIAN\|LONG\|LOWERCURVE\|MAROON\|MASK\|MASKAND\|MASKINVISIBLE\|MASKOR\|MASKVANILLACOPY\|MASKVISIBLE\|MASKXOR\|MEMORY\|MENU\|MENUITEM_DISABLED\|MENUITEM_RADIO\|MENUITEM_SELECTED\|MENUITEM_TOGGLE\|MILLISECONDS\|MODE_READ\|MODE_READWRITE\|MODE_WRITE\|MODLALT\|MODLCOMMAND\|MODLCONTROL\|MODLSHIFT\|MODRALT\|MODRCOMMAND\|MODRCONTROL\|MODRSHIFT\|MONO16\|MONO8\|MONOSPACE\|MOVEFILE_COPY\|MOVEFILE_COPYFAILED\|MOVEFILE_DELETE\|MOVEFILE_DELETEFAILED\|MOVEFILE_UNPROTECT\|MOVELIST\|MOVEWINDOW\|MUSIC\|NAN\|NATIVE\|NATIVEENDIAN\|NAVY\|NETWORKCONNECTION\|NETWORKSERVER\|NETWORKUDP\|NEXTFRAME\|NEXTFRAME2\|NIL\|NOCOLOR\|NONE\|NOPEN\|NORMAL\|NORMALSPEED\|NOTRANSPARENCY\|NUMBER\|NUMERICAL\|OLIVE\|ONBUTTONCLICK\|ONBUTTONCLICKALL\|ONBUTTONOVER\|ONBUTTONOVERALL\|ONBUTTONRIGHTCLICK\|ONBUTTONRIGHTCLICKALL\|ONKEYDOWN\|ONKEYDOWNALL\|ORIENTATION_LANDSCAPE\|ORIENTATION_LANDSCAPEREV\|ORIENTATION_NONE\|ORIENTATION_PORTRAIT\|ORIENTATION_PORTRAITREV\|PALETTE\|PALETTE_AGA\|PALETTE_CGA\|PALETTE_DEFAULT\|PALETTE_EGA\|PALETTE_GRAY128\|PALETTE_GRAY16\|PALETTE_GRAY256\|PALETTE_GRAY32\|PALETTE_GRAY4\|PALETTE_GRAY64\|PALETTE_GRAY8\|PALETTE_MACINTOSH\|PALETTE_MONOCHROME\|PALETTE_OCS\|PALETTE_WINDOWS\|PALETTE_WORKBENCH\|PALETTEMODE_PEN\|PALETTEMODE_REMAP\|PARITY_EVEN\|PARITY_NONE\|PARITY_ODD\|PERMREQ_READEXTERNAL\|PERMREQ_WRITEEXTERNAL\|PI\|PIXELZOOM1\|PIXELZOOM2\|PLOT\|PLUGINCAPS_ANIM\|PLUGINCAPS_AUDIOADAPTER\|PLUGINCAPS_CONVERT\|PLUGINCAPS_DIRADAPTER\|PLUGINCAPS_DISPLAYADAPTER\|PLUGINCAPS_EXTENSION\|PLUGINCAPS_FILEADAPTER\|PLUGINCAPS_ICON\|PLUGINCAPS_IMAGE\|PLUGINCAPS_IPCADAPTER\|PLUGINCAPS_LIBRARY\|PLUGINCAPS_NETWORKADAPTER\|PLUGINCAPS_REQUESTERADAPTER\|PLUGINCAPS_REQUIRE\|PLUGINCAPS_SAVEANIM\|PLUGINCAPS_SAVEICON\|PLUGINCAPS_SAVEIMAGE\|PLUGINCAPS_SAVESAMPLE\|PLUGINCAPS_SERIALIZE\|PLUGINCAPS_SOUND\|PLUGINCAPS_TIMERADAPTER\|PLUGINCAPS_VECTOR\|PLUGINCAPS_VIDEO\|POINTER\|POLYGON\|PRGTYPE_APPLET\|PRGTYPE_PROGRAM\|PRGTYPE_SCRIPT\|PRINT\|PURPLE\|PUSHBOTTOM\|PUSHLEFT\|PUSHRIGHT\|PUSHTOP\|PUZZLE\|QUADRECT\|QUARTERS\|RADIAL\|RANDOMEFFECT\|RANDOMPARAMETER\|RECEIVEALL\|RECEIVEBYTES\|RECEIVEDATA_PACKET\|RECEIVELINE\|RECTBACKCENTER\|RECTBACKEAST\|RECTBACKNORTH\|RECTBACKNORTHEAST\|RECTBACKNORTHWEST\|RECTBACKSOUTH\|RECTBACKSOUTHEAST\|RECTBACKSOUTHWEST\|RECTBACKWEST\|RECTCENTER\|RECTEAST\|RECTNORTH\|RECTNORTHEAST\|RECTNORTHWEST\|RECTSOUTH\|RECTSOUTHEAST\|RECTSOUTHWEST\|RECTWEST\|RED\|REMOVELAYER\|REQ_CAMERA\|REQ_GALLERY\|REQ_HIDEICONS\|REQ_MULTISELECT\|REQ_NORMAL\|REQ_SAVEMODE\|REQICON_ERROR\|REQICON_INFORMATION\|REQICON_NONE\|REQICON_QUESTION\|REQICON_WARNING\|REVEALBOTTOM\|REVEALLEFT\|REVEALRIGHT\|REVEALTOP\|RIGHT\|RIGHTOUT\|ROLLLEFT\|ROLLTOP\|RTS_OFF\|RTS_ON\|SAMPLE\|SANS\|SCALEMODE_AUTO\|SCALEMODE_LAYER\|SCALEMODE_NONE\|SCROLLBOTTOM\|SCROLLEAST\|SCROLLLEFT\|SCROLLNORTH\|SCROLLNORTHEAST\|SCROLLNORTHWEST\|SCROLLRIGHT\|SCROLLSOUTH\|SCROLLSOUTHEAST\|SCROLLSOUTHWEST\|SCROLLTOP\|SCROLLWEST\|SECONDS\|SEEK_BEGINNING\|SEEK_CURRENT\|SEEK_END\|SELMODE_COMBO\|SELMODE_LAYERS\|SELMODE_NORMAL\|SERIAL\|SERIF\|SERVER\|SHADOW\|SHAPE\|SHDWEAST\|SHDWNORTH\|SHDWNORTHEAST\|SHDWNORTHWEST\|SHDWSOUTH\|SHDWSOUTHEAST\|SHDWSOUTHWEST\|SHDWWEST\|SHORT\|SILVER\|SIMPLEBUTTON\|SINE\|SIZEWINDOW\|SLIDEBOTTOM\|SLIDELEFT\|SLIDERIGHT\|SLIDETOP\|SLOWSPEED\|SMOOTHOUT\|SMPFMT_WAVE\|SNAPDESKTOP\|SNAPDISPLAY\|SNAPWINDOW\|SPIRAL\|SPRITE\|SPRITE_VS_BOX\|SPRITE_VS_BRUSH\|STAR\|STDERR\|STDIN\|STDOUT\|STDPTR_BUSY\|STDPTR_CUSTOM\|STDPTR_SYSTEM\|STEREO16\|STEREO8\|STOP_1\|STOP_2\|STRETCHBOTTOM\|STRETCHLEFT\|STRETCHRIGHT\|STRETCHTOP\|STRING\|STRUDEL\|SUN\|SWISS\|TABLE\|TEAL\|TEXTOBJECT\|TEXTOUT\|THREAD\|TICKS\|TIMEOUT\|TIMER\|TOP\|TOPOUT\|TRUETYPE_DEFAULT\|TURNDOWNBOTTOM\|TURNDOWNLEFT\|TURNDOWNRIGHT\|TURNDOWNTOP\|UDPCLIENT\|UDPNONE\|UDPOBJECT\|UDPSERVER\|UNDERLINED\|UNDO\|UPLOADFILE_RESPONSE\|UPLOADFILE_STATUS\|UPNDOWN\|UPPERCURVE\|USEDSPACE\|USELAYERPOSITION\|USERDATA\|VANILLACOPY\|VBLINDS128\|VBLINDS16\|VBLINDS32\|VBLINDS64\|VBLINDS8\|VCLOSECURTAIN\|VCLOSEGATE\|VECTORPATH\|VFLIPCOIN\|VFLOWLEFT\|VFLOWRIGHT\|VIDDRV_HOLLYWOOD\|VIDDRV_OS\|VIDEO\|VIEWMODE_DATE\|VIEWMODE_ICONS\|VIEWMODE_NAME\|VIEWMODE_NONE\|VIEWMODE_SIZE\|VIEWMODE_TYPE\|VLINES\|VLINES2\|VLOWFLIPCOIN\|VOID\|VOPENCURTAIN\|VOPENGATE\|VSPLIT\|VSTRANGEPUSH\|VSTRETCHCENTER\|VSTRIPES\|VSTRIPES16\|VSTRIPES2\|VSTRIPES32\|VSTRIPES4\|VSTRIPES64\|VSTRIPES8\|WALLPAPERLEFT\|WALLPAPERTOP\|WATER1\|WATER2\|WATER3\|WATER4\|WHITE\|WORD\|YELLOW\|ZOOMCENTER\|ZOOMEAST\|ZOOMIN\|ZOOMNORTH\|ZOOMNORTHEAST\|ZOOMNORTHWEST\|ZOOMOUT\|ZOOMSOUTH\|ZOOMSOUTHEAST\|ZOOMSOUTHWEST\|ZOOMWEST\)\>"
" Hollywood Functions
-syn keyword hwFunction ACos ARGB ASin ATan ATan2 Abs ActivateDisplay Add AddArcToPath AddBoxToPath AddCircleToPath AddEllipseToPath AddFontPath AddIconImage AddMove AddStr AddTab AddTextToPath AllocMem AllocMemFromPointer AllocMemFromVirtualFile AppendPath ApplyPatch Arc ArcDistortBrush ArrayToStr Asc Assert AsyncDrawFrame BGPicToBrush BarrelDistortBrush Base64Str Beep BeginAnimStream BeginDoubleBuffer BeginRefresh BinStr BitClear BitComplement BitSet BitTest BitXor Blue BlurBrush Box BreakEventHandler BreakWhileMouseOn BrushToBGPic BrushToGray BrushToMonochrome BrushToPenArray BrushToRGBArray ByteAsc ByteChr ByteLen ByteOffset ByteStrStr ByteVal CRC32 CRC32Str CallJavaMethod CancelAsyncDraw CancelAsyncOperation CanonizePath Cast Ceil ChangeApplicationIcon ChangeBrushTransparency ChangeDirectory ChangeDisplayMode ChangeDisplaySize ChangeInterval CharOffset CharWidth CharcoalBrush CheckEvent CheckEvents Chr Circle ClearClipboard ClearEvents ClearInterval ClearMove ClearObjectData ClearPath ClearScreen ClearSerialQueue ClearTimeout CloseAmigaGuide CloseAnim CloseAudio CloseCatalog CloseConnection CloseDirectory CloseDisplay CloseFile CloseFont CloseMusic ClosePath CloseResourceMonitor CloseSerialPort CloseServer CloseUDPObject CloseVideo Cls CollectGarbage Collision ColorRequest CompareDates CompareStr CompressFile Concat ConsolePrint ConsolePrintNR ConsolePrompt ContinueAsyncOperation ContrastBrush ContrastPalette ConvertStr ConvertToBrush CopyAnim CopyBGPic CopyBrush CopyFile CopyMem CopyObjectData CopyPalette CopyPath CopyPens CopySample CopySprite CopyTable CopyTextObject Cos CountDirectoryEntries CountJoysticks CountStr CreateAnim CreateBGPic CreateBorderBrush CreateBrush CreateButton CreateClipRegion CreateCoroutine CreateDisplay CreateGradientBGPic CreateGradientBrush CreateIcon CreateKeyDown CreateLayer CreateList CreateMenu CreateMusic CreatePalette CreatePointer CreatePort CreateRainbowBGPic CreateRexxPort CreateSample CreateServer CreateShadowBrush CreateShortcut CreateSprite CreateTextObject CreateTexturedBGPic CreateTexturedBrush CreateUDPObject CropBrush CtrlCQuit CurveTo CyclePalette DateToTimestamp DateToUTC DebugOutput DebugPrint DebugPrintNR DebugPrompt DebugStr DebugVal DecompressFile DecreasePointer DefineVirtualFile DefineVirtualFileFromString Deg DeleteAlphaChannel DeleteButton DeleteFile DeleteMask DeletePrefs DeselectMenuItem DeserializeTable DirectoryItems DisableButton DisableEvent DisableLayers DisableLineHook DisableMenuItem DisablePlugin DisableVWait DisplayAnimFrame DisplayBGPic DisplayBGPicPart DisplayBGPicPartFX DisplayBrush DisplayBrushFX DisplayBrushPart DisplaySprite DisplayTextObject DisplayTextObjectFX DisplayTransitionFX DisplayVideoFrame Div DoMove DownloadFile DrawPath DumpButtons DumpLayers DumpMem DumpVideo DumpVideoTime EdgeBrush Ellipse EmbossBrush EmptyStr EnableButton EnableEvent EnableLayers EnableLineHook EnableMenuItem EnablePlugin EnableVWait End EndDoubleBuffer EndRefresh EndSelect EndianSwap EndsWith Eof Error EscapeQuit Eval Execute Exists ExitOnError Exp ExtractPalette FileAttributes FileLength FileLines FilePart FilePos FileRequest FileSize FileToString FillMem FillMusicBuffer FindStr FinishAnimStream FinishAsyncDraw Flip FlipBrush FlipSprite FloodFill Floor FlushFile FlushMusicBuffer FlushSerialPort FontRequest ForEach ForEachI ForcePathUse ForceSound ForceVideoDriver ForceVideoMode FormatStr FrExp Frac FreeAnim FreeBGPic FreeBrush FreeClipRegion FreeDisplay FreeEventCache FreeGlyphCache FreeIcon FreeLayers FreeMem FreeMenu FreeModule FreePalette FreePath FreePointer FreeSample FreeSprite FreeTextObject FullPath GCInfo GammaBrush GammaPalette GetAnimFrame GetApplicationInfo GetApplicationList GetAsset GetAttribute GetAvailableFonts GetBaudRate GetBestPen GetBrushLink GetBrushPen GetBulletColor GetCatalogString GetChannels GetCharMaps GetClipboard GetCommandLine GetConnectionIP GetConnectionPort GetConnectionProtocol GetConstant GetCoroutineStatus GetCountryInfo GetCurrentDirectory GetCurrentPoint GetDTR GetDash GetDataBits GetDate GetDateNum GetDefaultEncoding GetDirectoryEntry GetDisplayModes GetEnv GetErrorName GetEventCode GetFPSLimit GetFileArgument GetFileAttributes GetFillRule GetFillStyle GetFlowControl GetFontColor GetFontStyle GetFormStyle GetFrontScreen GetHostName GetIconProperties GetItem GetKerningPair GetLanguageInfo GetLastError GetLayerAtPos GetLayerPen GetLayerStyle GetLineCap GetLineJoin GetLineWidth GetLocalIP GetLocalInterfaces GetLocalPort GetLocalProtocol GetMACAddress GetMemPointer GetMemString GetMemoryInfo GetMetaTable GetMiterLimit GetMonitors GetObjectData GetObjectType GetObjects GetPalettePen GetParity GetPathExtents GetPatternPosition GetPen GetPlugins GetProgramDirectory GetProgramInfo GetPubScreens GetRTS GetRandomColor GetRandomFX GetRealColor GetSampleData GetShortcutPath GetSongPosition GetStartDirectory GetStopBits GetSystemCountry GetSystemInfo GetSystemLanguage GetTempFileName GetTime GetTimeZone GetTimer GetTimestamp GetType GetVersion GetVideoFrame GetVolumeInfo GetVolumeName GetWeekday GrabDesktop Green HaveFreeChannel HaveItem HaveObject HaveObjectData HavePlugin HaveVolume HexStr HideDisplay HideKeyboard HideLayer HideLayerFX HidePointer HideScreen Hypot IIf IPairs IgnoreCase ImageRequest InKeyStr IncreasePointer InsertItem InsertLayer InsertSample InsertStr InstallEventHandler Int Intersection InvertAlphaChannel InvertBrush InvertMask InvertPalette IsAbsolutePath IsAlNum IsAlpha IsAnim IsBrushEmpty IsChannelPlaying IsCntrl IsDigit IsDirectory IsFinite IsGraph IsInf IsKeyDown IsLeftMouse IsLower IsMenuItemDisabled IsMenuItemSelected IsMidMouse IsModule IsMusic IsMusicPlaying IsNan IsNil IsOnline IsPathEmpty IsPicture IsPrint IsPunct IsRightMouse IsSample IsSamplePlaying IsSound IsSpace IsTableEmpty IsUnicode IsUpper IsVideo IsVideoPlaying IsXDigit JoyDir JoyFire LayerExists LayerToBack LayerToFront Ld LdExp LeftMouseQuit LeftStr LegacyControl Limit Line LineTo ListItems ListRequest Ln LoadAnim LoadAnimFrame LoadBGPic LoadBrush LoadIcon LoadModule LoadPalette LoadPlugin LoadPrefs LoadSample LoadSprite Locate Log LowerStr MD5 MD5Str MakeButton MakeDate MakeDirectory MakeHostPath MatchPattern Max MemToTable MidStr Min MixBrush MixRGB MixSample Mod ModifyAnimFrames ModifyButton ModifyKeyDown ModifyLayerFrames ModulateBrush ModulatePalette MonitorDirectory MouseX MouseY MoveAnim MoveBrush MoveDisplay MoveFile MoveLayer MovePointer MoveSprite MoveTextObject MoveTo Mul NPrint NextDirectoryEntry NextFrame NextItem NormalizePath OilPaintBrush OpenAmigaGuide OpenAnim OpenAudio OpenCatalog OpenConnection OpenDirectory OpenDisplay OpenFile OpenFont OpenMusic OpenResourceMonitor OpenSerialPort OpenURL OpenVideo Pack PadNum Pairs PaletteToGray ParseDate PathItems PathPart PathRequest PathToBrush PatternFindStr PatternFindStrDirect PatternFindStrShort PatternReplaceStr PauseLayer PauseModule PauseMusic PauseTimer PauseVideo Peek PeekClipboard PenArrayToBrush PerformSelector PermissionRequest PerspectiveDistortBrush Pi PixelateBrush PlayAnim PlayAnimDisk PlayLayer PlayModule PlayMusic PlaySample PlaySubsong PlayVideo Plot Poke PolarDistortBrush PollSerialQueue Polygon Pow Print QuantizeBrush RGB RGBArrayToBrush Rad RaiseOnError RasterizeBrush RawDiv RawEqual RawGet RawSet ReadBrushPixel ReadByte ReadBytes ReadChr ReadDirectory ReadFloat ReadFunction ReadInt ReadLine ReadMem ReadPen ReadPixel ReadRegistryKey ReadSerialData ReadShort ReadString ReadTable ReceiveData ReceiveUDPData Red ReduceAlphaChannel RefreshDisplay RelCurveTo RelLineTo RelMoveTo RemapBrush RemoveBrushPalette RemoveButton RemoveIconImage RemoveItem RemoveKeyDown RemoveLayer RemoveLayerFX RemoveLayers RemoveSprite RemoveSprites Rename RepeatStr ReplaceColors ReplaceStr ResetKeyStates ResetTabs ResetTimer ResolveHostName ResumeCoroutine ResumeLayer ResumeModule ResumeMusic ResumeTimer ResumeVideo ReverseFindStr ReverseStr RewindDirectory RightStr Rnd RndF RndStrong Rol Ror RotateBrush RotateLayer RotateTextObject Round Rt Run RunCallback RunRexxScript Sar SaveAnim SaveBrush SaveIcon SavePalette SavePrefs SaveSample SaveSnapshot ScaleAnim ScaleBGPic ScaleBrush ScaleLayer ScaleSprite ScaleTextObject Seek SeekLayer SeekMusic SeekVideo SelectAlphaChannel SelectAnim SelectBGPic SelectBrush SelectDisplay SelectLayer SelectMask SelectMenuItem SelectPalette SendApplicationMessage SendData SendMessage SendRexxCommand SendUDPData SepiaToneBrush SerializeTable SetAlphaIntensity SetAnimFrameDelay SetBaudRate SetBorderPen SetBrushDepth SetBrushPalette SetBrushPen SetBrushTransparency SetBrushTransparentPen SetBulletColor SetBulletPen SetChannelVolume SetClipRegion SetClipboard SetCycleTable SetDTR SetDash SetDataBits SetDefaultEncoding SetDepth SetDisplayAttributes SetDitherMode SetDrawPen SetDrawTagsDefault SetEnv SetEventTimeout SetFPSLimit SetFileAttributes SetFileEncoding SetFillRule SetFillStyle SetFlowControl SetFont SetFontColor SetFontStyle SetFormStyle SetGradientPalette SetIOMode SetIconProperties SetInterval SetLayerAnchor SetLayerBorder SetLayerDepth SetLayerFilter SetLayerLight SetLayerName SetLayerPalette SetLayerPen SetLayerShadow SetLayerStyle SetLayerTint SetLayerTransparency SetLayerTransparentPen SetLayerVolume SetLayerZPos SetLineCap SetLineJoin SetLineWidth SetListItems SetMargins SetMaskMode SetMasterVolume SetMetaTable SetMiterLimit SetMusicVolume SetNetworkProtocol SetNetworkTimeout SetObjectData SetPalette SetPaletteDepth SetPaletteMode SetPalettePen SetPaletteTransparentPen SetPanning SetParity SetPen SetPitch SetPointer SetRTS SetScreenTitle SetShadowPen SetSpriteZPos SetStandardIconImage SetStandardPalette SetStopBits SetSubtitle SetTimeout SetTimerElapse SetTitle SetTransparentPen SetTransparentThreshold SetTrayIcon SetVectorEngine SetVideoPosition SetVideoSize SetVideoVolume SetVolume SetWBIcon Sgn SharpenBrush Shl ShowDisplay ShowKeyboard ShowLayer ShowLayerFX ShowNotification ShowPointer ShowRinghioMessage ShowScreen ShowToast Shr Sin SolarizeBrush SolarizePalette Sort SplitStr Sqrt StartPath StartSubPath StartTimer StartsWith StopChannel StopLayer StopModule StopMusic StopSample StopTimer StopVideo StrLen StrStr StrToArray StringRequest StringToFile StripStr Sub SwapLayers SwirlBrush SystemRequest TableItems TableToMem Tan TextExtent TextHeight TextOut TextWidth TimerElapsed TimestampToDate TintBrush TintPalette ToHostName ToIP ToNumber ToString ToUserData TransformBrush TransformLayer TranslateLayer TranslatePath TrimBrush TrimStr UTCToDate UndefineVirtualStringFile Undo UndoFX UnleftStr UnmidStr Unpack UnrightStr UnsetEnv UploadFile UpperStr UseCarriageReturn UseFont VWait Val ValidateDate ValidateStr Vibrate Wait WaitEvent WaitKeyDown WaitLeftMouse WaitMidMouse WaitPatternPosition WaitRightMouse WaitSampleEnd WaitSongPosition WaitTimer WaterRippleBrush WhileKeyDown WhileMouseDown WhileMouseOn WhileRightMouseDown Wrap WriteAnimFrame WriteBrushPixel WriteByte WriteBytes WriteChr WriteFloat WriteFunction WriteInt WriteLine WriteMem WritePen WriteRegistryKey WriteSerialData WriteShort WriteString WriteTable YieldCoroutine
+syn keyword hwFunction Abs ACos ARGB ActivateDisplay Add AddArcToPath AddBoxToPath AddCircleToPath AddEllipseToPath AddFontPath AddIconImage AddMove AddStr AddTab AddTextToPath AllocMem AllocMemFromPointer AllocMemFromVirtualFile AppendPath ApplyPatch Arc ArcDistortBrush ArrayToStr Asc ASin Assert AsyncDrawFrame ATan ATan2 BarrelDistortBrush Base64Str Beep BeginAnimStream BeginDoubleBuffer BeginRefresh BGPicToBrush BinStr BitClear BitComplement BitSet BitTest BitXor Blue BlurBrush Box BreakEventHandler BreakWhileMouseOn BrushToBGPic BrushToGray BrushToMonochrome BrushToPenArray BrushToRGBArray ByteAsc ByteChr ByteLen ByteOffset ByteStrStr ByteVal CRC32 CRC32Str CallJavaMethod CancelAsyncDraw CancelAsyncOperation CanonizePath Cast Ceil ChangeApplicationIcon ChangeBrushTransparency ChangeDirectory ChangeDisplayMode ChangeDisplaySize ChangeInterval CharcoalBrush CharOffset CharWidth CheckEvent CheckEvents Chr Circle ClearClipboard ClearEvents ClearInterval ClearMove ClearObjectData ClearPath ClearScreen ClearSerialQueue ClearTimeout CloseAmigaGuide CloseAnim CloseAudio CloseCatalog CloseConnection CloseDirectory CloseDisplay CloseFile CloseFont CloseMusic ClosePath CloseResourceMonitor CloseSerialPort CloseServer CloseUDPObject CloseVideo Cls CollectGarbage Collision ColorRequest CompareDates CompareStr CompressFile Concat ConsolePrint ConsolePrintNR ConsolePrompt ContinueAsyncOperation ContrastBrush ContrastPalette ConvertStr ConvertToBrush CopyAnim CopyBGPic CopyBrush CopyFile CopyLayer CopyMem CopyObjectData CopyPalette CopyPath CopyPens CopySample CopySprite CopyTable CopyTextObject Cos CountDirectoryEntries CountJoysticks CountStr CreateAnim CreateBGPic CreateBorderBrush CreateBrush CreateButton CreateClipRegion CreateCoroutine CreateDisplay CreateGradientBGPic CreateGradientBrush CreateIcon CreateKeyDown CreateLayer CreateList CreateMenu CreateMusic CreatePalette CreatePointer CreatePort CreateRainbowBGPic CreateRexxPort CreateSample CreateServer CreateShadowBrush CreateShortcut CreateSprite CreateTextObject CreateTexturedBGPic CreateTexturedBrush CreateUDPObject CropBrush CtrlCQuit CurveTo CyclePalette DateToTimestamp DateToUTC DebugOutput DebugPrint DebugPrintNR DebugPrompt DebugStr DebugVal DecompressFile DecreasePointer DefineVirtualFile DefineVirtualFileFromString Deg DeleteAlphaChannel DeleteButton DeleteFile DeleteMask DeletePrefs DeselectMenuItem DeserializeTable DirectoryItems DisableButton DisableEvent DisableEventHandler DisableLayers DisableLineHook DisableMenuItem DisablePlugin DisablePrecalculation DisableVWait DisplayAnimFrame DisplayBGPic DisplayBGPicPart DisplayBGPicPartFX DisplayBrush DisplayBrushFX DisplayBrushPart DisplaySprite DisplayTextObject DisplayTextObjectFX DisplayTransitionFX DisplayVideoFrame Div DoMove DownloadFile DrawPath DumpButtons DumpLayers DumpMem DumpVideo DumpVideoTime EdgeBrush Ellipse EmbossBrush EmptyStr EnableButton EnableEventHandler EnableEvent EnableLayers EnableLineHook EnableMenuItem EnablePlugin EnablePrecalculation EnableVWait End EndDoubleBuffer EndianSwap EndRefresh EndSelect EndsWith Eof Error EscapeQuit Eval Execute Exists ExitOnError Exp ExtractPalette FileAttributes FileLength FileLines FilePart FilePos FileRequest FileSize FileToString FillMem FillMusicBuffer FindStr FinishAnimStream FinishAsyncDraw Flip FlipBrush FlipSprite FloodFill Floor FlushFile FlushMusicBuffer FlushSerialPort FontRequest ForcePathUse ForceSound ForceVideoDriver ForceVideoMode ForEach ForEachI FormatStr Frac FreeAnim FreeBGPic FreeBrush FreeClipRegion FreeDisplay FreeEventCache FreeGlyphCache FreeIcon FreeLayers FreeMem FreeMenu FreeModule FreePalette FreePath FreePointer FreeSample FreeSprite FreeTextObject FrExp FullPath GammaBrush GammaPalette GCInfo GetAnimFrame GetApplicationInfo GetApplicationList GetAsset GetAttribute GetAvailableFonts GetBaudRate GetBestPen GetBrushLink GetBrushPen GetBulletColor GetCatalogString GetChannels GetCharMaps GetClipboard GetCommandLine GetConnectionIP GetConnectionPort GetConnectionProtocol GetConstant GetCoroutineStatus GetCountryInfo GetCurrentDirectory GetCurrentPoint GetDash GetDataBits GetDate GetDateNum GetDefaultEncoding GetDirectoryEntry GetDisplayModes GetDTR GetEnv GetErrorName GetEventCode GetFileArgument GetFileAttributes GetFillRule GetFillStyle GetFlowControl GetFontColor GetFontStyle GetFormStyle GetFPSLimit GetFrontScreen GetHostName GetIconProperties GetItem GetKerningPair GetLanguageInfo GetLastError GetLayerAtPos GetLayerPen GetLayerStyle GetLineCap GetLineJoin GetLineWidth GetLocalInterfaces GetLocalIP GetLocalPort GetLocalProtocol GetMACAddress GetMemoryInfo GetMemPointer GetMemString GetMetaTable GetMiterLimit GetMonitors GetObjectData GetObjects GetObjectType GetPalettePen GetParity GetPathExtents GetPatternPosition GetPen GetPlugins GetProgramDirectory GetProgramInfo GetPubScreens GetRandomColor GetRandomFX GetRealColor GetRTS GetSampleData GetShortcutPath GetSongPosition GetStartDirectory GetStopBits GetSystemCountry GetSystemInfo GetSystemLanguage GetTempFileName GetTime GetTimer GetTimestamp GetTimeZone GetType GetVersion GetVideoFrame GetVolumeInfo GetVolumeName GetWeekday Gosub Goto GrabDesktop Green HaveFreeChannel HaveItem HaveObject HaveObjectData HavePlugin HaveVolume HexStr HideDisplay HideKeyboard HideLayer HideLayerFX HidePointer HideScreen Hypot IgnoreCase IIf ImageRequest IncreasePointer InKeyStr InsertItem InsertLayer InsertSample InsertStr InstallEventHandler Intersection Int InvertAlphaChannel InvertBrush InvertMask InvertPalette IPairs IsAbsolutePath IsAlNum IsAlpha IsAnim IsAnimPlaying IsBrushEmpty IsChannelPlaying IsCntrl IsDigit IsDirectory IsFinite IsGraph IsInf IsKeyDown IsLeftMouse IsLower IsMenuItemDisabled IsMenuItemSelected IsMidMouse IsModule IsMusicPlaying IsMusic IsNan IsNil IsOnline IsPathEmpty IsPicture IsPrint IsPunct IsRightMouse IsSamplePlaying IsSample IsSound IsSpace IsTableEmpty IsUnicode IsUpper IsVideo IsVideoPlaying IsXDigit JoyDir JoyFire Label LayerExists LayerToBack LayerToFront Ld LdExp LeftMouseQuit LeftStr LegacyControl Limit Line LineTo ListItems ListRequest Ln LoadAnim LoadAnimFrame LoadBGPic LoadBrush LoadIcon LoadModule LoadPalette LoadPlugin LoadPrefs LoadSample LoadSprite Locate Log LowerStr MakeButton MakeDate MakeDirectory MakeHostPath MatchPattern Max MD5 MD5Str MemToTable MidStr Min MixBrush MixRGB MixSample ModifyAnimFrames ModifyButton ModifyKeyDown ModifyLayerFrames ModulateBrush Mod ModulatePalette MonitorDirectory MouseX MouseY MoveAnim MoveBrush MoveDisplay MoveFile MoveLayer MovePointer MoveSprite MoveTextObject MoveTo Mul NextDirectoryEntry NextFrame NextItem NormalizePath NPrint OilPaintBrush OpenAmigaGuide OpenAnim OpenAudio OpenCatalog OpenConnection OpenDirectory OpenDisplay OpenFile OpenFont OpenMusic OpenResourceMonitor OpenSerialPort OpenURL OpenVideo Pack PadNum Pairs PaletteToGray ParseDate PathItems PathPart PathRequest PathToBrush PatternFindStr PatternFindStrDirect PatternFindStrShort PatternReplaceStr PauseLayer PauseModule PauseMusic PauseTimer PauseVideo PeekClipboard Peek PenArrayToBrush PerformSelector PermissionRequest PerspectiveDistortBrush Pi PixelateBrush PlayAnim PlayAnimDisk PlayLayer PlayModule PlayMusic PlaySample PlaySubsong PlayVideo Plot Poke PolarDistortBrush PollSerialQueue Polygon Pow Print QuantizeBrush Rad RaiseOnError RasterizeBrush RawDiv RawEqual RawGet RawSet ReadBrushPixel ReadByte ReadBytes ReadChr ReadDirectory ReadFloat ReadFunction ReadInt ReadLine ReadMem ReadPen ReadPixel ReadRegistryKey ReadSerialData ReadShort ReadString ReadTable ReceiveData ReceiveUDPData Red ReduceAlphaChannel RefreshDisplay RelCurveTo RelLineTo RelMoveTo RemapBrush RemoveBrushPalette RemoveButton RemoveIconImage RemoveItem RemoveKeyDown RemoveLayer RemoveLayerFX RemoveLayers RemoveSprite RemoveSprites Rename RepeatStr ReplaceColors ReplaceStr ResetKeyStates ResetTabs ResetTimer ResolveHostName ResumeCoroutine ResumeLayer ResumeModule ResumeMusic ResumeTimer ResumeVideo ReverseFindStr ReverseStr RewindDirectory RGB RGBArrayToBrush RightStr Rnd RndF RndStrong Rol Ror RotateBrush RotateLayer RotateTextObject Round Rt Run RunCallback RunRexxScript Sar SaveAnim SaveBrush SaveIcon SavePalette SavePrefs SaveSample SaveSnapshot ScaleAnim ScaleBGPic ScaleBrush ScaleLayer ScaleSprite ScaleTextObject Seek SeekLayer SeekMusic SeekVideo SelectAlphaChannel SelectAnim SelectBGPic SelectBrush SelectDisplay SelectLayer SelectMask SelectMenuItem SelectPalette SendApplicationMessage SendData SendMessage SendRexxCommand SendUDPData SepiaToneBrush SerializeTable SetAlphaIntensity SetAnimFrameDelay SetBaudRate SetBorderPen SetBrushDepth SetBrushPalette SetBrushPen SetBrushTransparency SetBrushTransparentPen SetBulletColor SetBulletPen SetChannelVolume SetClipboard SetClipRegion SetCycleTable SetDash SetDataBits SetDefaultEncoding SetDepth SetDisplayAttributes SetDitherMode SetDrawPen SetDrawTagsDefault SetDTR SetEnv SetEventTimeout SetFileAttributes SetFileEncoding SetFillRule SetFillStyle SetFlowControl SetFont SetFontColor SetFontStyle SetFormStyle SetFPSLimit SetGradientPalette SetIconProperties SetInterval SetIOMode SetLayerAnchor SetLayerBorder SetLayerDepth SetLayerFilter SetLayerLight SetLayerName SetLayerPalette SetLayerPen SetLayerShadow SetLayerStyle SetLayerTint SetLayerTransparency SetLayerTransparentPen SetLayerVolume SetLayerZPos SetLineCap SetLineJoin SetLineWidth SetListItems SetMargins SetMaskMode SetMasterVolume SetMetaTable SetMiterLimit SetMusicVolume SetNetworkProtocol SetNetworkTimeout SetObjectData SetPalette SetPaletteDepth SetPaletteMode SetPalettePen SetPaletteTransparentPen SetPanning SetParity SetPen SetPitch SetPointer SetRTS SetScreenTitle SetShadowPen SetSpriteZPos SetStandardIconImage SetStandardPalette SetStopBits SetSubtitle SetTimeout SetTimerElapse SetTitle SetTransparentPen SetTransparentThreshold SetTrayIcon SetVarType SetVectorEngine SetVideoPosition SetVideoSize SetVideoVolume SetVolume SetWBIcon Sgn SharpenBrush Shl ShowDisplay ShowKeyboard ShowLayer ShowLayerFX ShowNotification ShowPointer ShowRinghioMessage ShowScreen ShowToast Shr Sin SolarizeBrush SolarizePalette Sort SplitStr Sqrt StartPath StartSubPath StartTimer StartsWith StopAnim StopChannel StopLayer StopModule StopMusic StopSample StopTimer StopVideo StringRequest StringToFile StripStr StrLen StrStr StrToArray Sub SwapLayers SwirlBrush SystemRequest TableItems TableToMem Tan TextExtent TextHeight TextOut TextWidth TimerElapsed TimestampToDate TintBrush TintPalette ToHostName ToIP ToNumber ToString ToUserData TransformBrush TransformLayer TranslateLayer TranslatePath TrimBrush TrimStr UndefineVirtualStringFile Undo UndoFX UnleftStr UnmidStr Unpack UnrightStr UnsetEnv UploadFile UpperStr Usage UseCarriageReturn UseFont UTCToDate Val ValidateDate ValidateStr Vibrate VWait Wait WaitAnimEnd WaitEvent WaitKeyDown WaitLeftMouse WaitMidMouse WaitPatternPosition WaitRightMouse WaitSampleEnd WaitSongPosition WaitTimer WaterRippleBrush WhileKeyDown WhileMouseDown WhileMouseOn WhileRightMouseDown Wrap WriteAnimFrame WriteBrushPixel WriteByte WriteBytes WriteChr WriteFloat WriteFunction WriteInt WriteLine WriteMem WritePen WriteRegistryKey WriteSerialData WriteShort WriteString WriteTable YieldCoroutine
" user-defined constants
syn match hwUserConstant "#\<\u\+\>"
diff --git a/runtime/syntax/html.vim b/runtime/syntax/html.vim
index 9061bdee90..605db3ae1c 100644
--- a/runtime/syntax/html.vim
+++ b/runtime/syntax/html.vim
@@ -3,7 +3,7 @@
" Maintainer: Doug Kearns <dougkearns@gmail.com>
" Previous Maintainers: Jorge Maldonado Ventura <jorgesumle@freakspot.net>
" Claudio Fleiner <claudio@fleiner.com>
-" Last Change: 2022 Jul 20
+" Last Change: 2022 Nov 18
" Please check :help html.vim for some comments and a description of the options
@@ -272,6 +272,16 @@ if main_syntax == "html"
syn sync minlines=10
endif
+" Folding
+" Originally by Ingo Karkat and Marcus Zanona
+if get(g:, "html_syntax_folding", 0)
+ syn region htmlFold start="<\z(\<\%(area\|base\|br\|col\|command\|embed\|hr\|img\|input\|keygen\|link\|meta\|param\|source\|track\|wbr\>\)\@![a-z-]\+\>\)\%(\_s*\_[^/]\?>\|\_s\_[^>]*\_[^>/]>\)" end="</\z1\_s*>" fold transparent keepend extend containedin=htmlHead,htmlH\d
+ " fold comments (the real ones and the old Netscape ones)
+ if exists("html_wrong_comments")
+ syn region htmlComment start=+<!--+ end=+--\s*>\%(\n\s*<!--\)\@!+ contains=@Spell fold
+ endif
+endif
+
" The default highlighting.
hi def link htmlTag Function
hi def link htmlEndTag Identifier
diff --git a/runtime/syntax/lua.vim b/runtime/syntax/lua.vim
index b398e2e5c6..9c5a490582 100644
--- a/runtime/syntax/lua.vim
+++ b/runtime/syntax/lua.vim
@@ -1,11 +1,12 @@
" Vim syntax file
-" 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: 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
+" Language: Lua 4.0, Lua 5.0, Lua 5.1, Lua 5.2 and Lua 5.3
+" Maintainer: Marcus Aurelius Farias <masserahguard-lua 'at' yahoo com>
+" First Author: Carlos Augusto Teixeira Mendes <cmendes 'at' inf puc-rio br>
+" Last Change: 2022 Sep 07
+" Options: lua_version = 4 or 5
+" lua_subversion = 0 (for 4.0 or 5.0)
+" or 1, 2, 3 (for 5.1, 5.2 or 5.3)
+" the default is 5.3
" quit when a syntax file was already loaded
if exists("b:current_syntax")
@@ -16,70 +17,78 @@ let s:cpo_save = &cpo
set cpo&vim
if !exists("lua_version")
- " Default is lua 5.2
+ " Default is lua 5.3
let lua_version = 5
- let lua_subversion = 2
+ let lua_subversion = 3
elseif !exists("lua_subversion")
- " lua_version exists, but lua_subversion doesn't. So, set it to 0
+ " lua_version exists, but lua_subversion doesn't. In this case set it to 0
let lua_subversion = 0
endif
syn case match
" syncing method
-syn sync minlines=100
+syn sync minlines=1000
-" Comments
-syn keyword luaTodo contained TODO FIXME XXX
-syn match luaComment "--.*$" contains=luaTodo,@Spell
-if lua_version == 5 && lua_subversion == 0
- syn region luaComment matchgroup=luaComment start="--\[\[" end="\]\]" contains=luaTodo,luaInnerComment,@Spell
- syn region luaInnerComment contained transparent start="\[\[" end="\]\]"
-elseif lua_version > 5 || (lua_version == 5 && lua_subversion >= 1)
- " Comments in Lua 5.1: --[[ ... ]], [=[ ... ]=], [===[ ... ]===], etc.
- syn region luaComment matchgroup=luaComment start="--\[\z(=*\)\[" end="\]\z1\]" contains=luaTodo,@Spell
+if lua_version >= 5
+ syn keyword luaMetaMethod __add __sub __mul __div __pow __unm __concat
+ syn keyword luaMetaMethod __eq __lt __le
+ syn keyword luaMetaMethod __index __newindex __call
+ syn keyword luaMetaMethod __metatable __mode __gc __tostring
endif
-" First line may start with #!
-syn match luaComment "\%^#!.*"
+if lua_version > 5 || (lua_version == 5 && lua_subversion >= 1)
+ syn keyword luaMetaMethod __mod __len
+endif
+
+if lua_version > 5 || (lua_version == 5 && lua_subversion >= 2)
+ syn keyword luaMetaMethod __pairs
+endif
+
+if lua_version > 5 || (lua_version == 5 && lua_subversion >= 3)
+ syn keyword luaMetaMethod __idiv __name
+ syn keyword luaMetaMethod __band __bor __bxor __bnot __shl __shr
+endif
+
+if lua_version > 5 || (lua_version == 5 && lua_subversion >= 4)
+ syn keyword luaMetaMethod __close
+endif
" catch errors caused by wrong parenthesis and wrong curly brackets or
" keywords placed outside their respective blocks
-syn region luaParen transparent start='(' end=')' contains=ALLBUT,luaParenError,luaTodo,luaSpecial,luaIfThen,luaElseifThen,luaElse,luaThenEnd,luaBlock,luaLoopBlock,luaIn,luaStatement
-syn region luaTableBlock transparent matchgroup=luaTable start="{" end="}" contains=ALLBUT,luaBraceError,luaTodo,luaSpecial,luaIfThen,luaElseifThen,luaElse,luaThenEnd,luaBlock,luaLoopBlock,luaIn,luaStatement
+syn region luaParen transparent start='(' end=')' contains=TOP,luaParenError
syn match luaParenError ")"
-syn match luaBraceError "}"
+syn match luaError "}"
syn match luaError "\<\%(end\|else\|elseif\|then\|until\|in\)\>"
-" function ... end
-syn region luaFunctionBlock transparent matchgroup=luaFunction start="\<function\>" end="\<end\>" contains=ALLBUT,luaTodo,luaSpecial,luaElseifThen,luaElse,luaThenEnd,luaIn
+" Function declaration
+syn region luaFunctionBlock transparent matchgroup=luaFunction start="\<function\>" end="\<end\>" contains=TOP
-" if ... then
-syn region luaIfThen transparent matchgroup=luaCond start="\<if\>" end="\<then\>"me=e-4 contains=ALLBUT,luaTodo,luaSpecial,luaElseifThen,luaElse,luaIn nextgroup=luaThenEnd skipwhite skipempty
+" else
+syn keyword luaCondElse matchgroup=luaCond contained containedin=luaCondEnd else
" then ... end
-syn region luaThenEnd contained transparent matchgroup=luaCond start="\<then\>" end="\<end\>" contains=ALLBUT,luaTodo,luaSpecial,luaThenEnd,luaIn
+syn region luaCondEnd contained transparent matchgroup=luaCond start="\<then\>" end="\<end\>" contains=TOP
" elseif ... then
-syn region luaElseifThen contained transparent matchgroup=luaCond start="\<elseif\>" end="\<then\>" contains=ALLBUT,luaTodo,luaSpecial,luaElseifThen,luaElse,luaThenEnd,luaIn
+syn region luaCondElseif contained containedin=luaCondEnd transparent matchgroup=luaCond start="\<elseif\>" end="\<then\>" contains=TOP
-" else
-syn keyword luaElse contained else
+" if ... then
+syn region luaCondStart transparent matchgroup=luaCond start="\<if\>" end="\<then\>"me=e-4 contains=TOP nextgroup=luaCondEnd skipwhite skipempty
" do ... end
-syn region luaBlock transparent matchgroup=luaStatement start="\<do\>" end="\<end\>" contains=ALLBUT,luaTodo,luaSpecial,luaElseifThen,luaElse,luaThenEnd,luaIn
-
+syn region luaBlock transparent matchgroup=luaStatement start="\<do\>" end="\<end\>" contains=TOP
" repeat ... until
-syn region luaLoopBlock transparent matchgroup=luaRepeat start="\<repeat\>" end="\<until\>" contains=ALLBUT,luaTodo,luaSpecial,luaElseifThen,luaElse,luaThenEnd,luaIn
+syn region luaRepeatBlock transparent matchgroup=luaRepeat start="\<repeat\>" end="\<until\>" contains=TOP
" while ... do
-syn region luaLoopBlock transparent matchgroup=luaRepeat start="\<while\>" end="\<do\>"me=e-2 contains=ALLBUT,luaTodo,luaSpecial,luaIfThen,luaElseifThen,luaElse,luaThenEnd,luaIn nextgroup=luaBlock skipwhite skipempty
+syn region luaWhile transparent matchgroup=luaRepeat start="\<while\>" end="\<do\>"me=e-2 contains=TOP nextgroup=luaBlock skipwhite skipempty
" for ... do and for ... in ... do
-syn region luaLoopBlock transparent matchgroup=luaRepeat start="\<for\>" end="\<do\>"me=e-2 contains=ALLBUT,luaTodo,luaSpecial,luaIfThen,luaElseifThen,luaElse,luaThenEnd nextgroup=luaBlock skipwhite skipempty
+syn region luaFor transparent matchgroup=luaRepeat start="\<for\>" end="\<do\>"me=e-2 contains=TOP nextgroup=luaBlock skipwhite skipempty
-syn keyword luaIn contained in
+syn keyword luaFor contained containedin=luaFor in
" other keywords
syn keyword luaStatement return local break
@@ -87,35 +96,59 @@ if lua_version > 5 || (lua_version == 5 && lua_subversion >= 2)
syn keyword luaStatement goto
syn match luaLabel "::\I\i*::"
endif
+
+" operators
syn keyword luaOperator and or not
+
+if (lua_version == 5 && lua_subversion >= 3) || lua_version > 5
+ syn match luaSymbolOperator "[#<>=~^&|*/%+-]\|\.\{2,3}"
+elseif lua_version == 5 && (lua_subversion == 1 || lua_subversion == 2)
+ syn match luaSymbolOperator "[#<>=~^*/%+-]\|\.\{2,3}"
+else
+ syn match luaSymbolOperator "[<>=~^*/+-]\|\.\{2,3}"
+endif
+
+" comments
+syn keyword luaTodo contained TODO FIXME XXX
+syn match luaComment "--.*$" contains=luaTodo,@Spell
+if lua_version == 5 && lua_subversion == 0
+ syn region luaComment matchgroup=luaCommentDelimiter start="--\[\[" end="\]\]" contains=luaTodo,luaInnerComment,@Spell
+ syn region luaInnerComment contained transparent start="\[\[" end="\]\]"
+elseif lua_version > 5 || (lua_version == 5 && lua_subversion >= 1)
+ " Comments in Lua 5.1: --[[ ... ]], [=[ ... ]=], [===[ ... ]===], etc.
+ syn region luaComment matchgroup=luaCommentDelimiter start="--\[\z(=*\)\[" end="\]\z1\]" contains=luaTodo,@Spell
+endif
+
+" first line may start with #!
+syn match luaComment "\%^#!.*"
+
syn keyword luaConstant nil
if lua_version > 4
syn keyword luaConstant true false
endif
-" Strings
-if lua_version < 5
- syn match luaSpecial contained "\\[\\abfnrtv\'\"]\|\\[[:digit:]]\{,3}"
-elseif lua_version == 5
+" strings
+syn match luaSpecial contained #\\[\\abfnrtv'"[\]]\|\\[[:digit:]]\{,3}#
+if lua_version == 5
if lua_subversion == 0
- syn match luaSpecial contained #\\[\\abfnrtv'"[\]]\|\\[[:digit:]]\{,3}#
- syn region luaString2 matchgroup=luaString start=+\[\[+ end=+\]\]+ contains=luaString2,@Spell
+ syn region luaString2 matchgroup=luaStringDelimiter start=+\[\[+ end=+\]\]+ contains=luaString2,@Spell
else
- if lua_subversion == 1
- syn match luaSpecial contained #\\[\\abfnrtv'"]\|\\[[:digit:]]\{,3}#
- else " Lua 5.2
- syn match luaSpecial contained #\\[\\abfnrtvz'"]\|\\x[[:xdigit:]]\{2}\|\\[[:digit:]]\{,3}#
+ if lua_subversion >= 2
+ syn match luaSpecial contained #\\z\|\\x[[:xdigit:]]\{2}#
+ endif
+ if lua_subversion >= 3
+ syn match luaSpecial contained #\\u{[[:xdigit:]]\+}#
endif
- syn region luaString2 matchgroup=luaString start="\[\z(=*\)\[" end="\]\z1\]" contains=@Spell
+ syn region luaString2 matchgroup=luaStringDelimiter start="\[\z(=*\)\[" end="\]\z1\]" contains=@Spell
endif
endif
-syn region luaString start=+'+ end=+'+ skip=+\\\\\|\\'+ contains=luaSpecial,@Spell
-syn region luaString start=+"+ end=+"+ skip=+\\\\\|\\"+ contains=luaSpecial,@Spell
+syn region luaString matchgroup=luaStringDelimiter start=+'+ end=+'+ skip=+\\\\\|\\'+ contains=luaSpecial,@Spell
+syn region luaString matchgroup=luaStringDelimiter start=+"+ end=+"+ skip=+\\\\\|\\"+ contains=luaSpecial,@Spell
" integer number
syn match luaNumber "\<\d\+\>"
" floating point number, with dot, optional exponent
-syn match luaNumber "\<\d\+\.\d*\%([eE][-+]\=\d\+\)\=\>"
+syn match luaNumber "\<\d\+\.\d*\%([eE][-+]\=\d\+\)\="
" floating point number, starting with a dot, optional exponent
syn match luaNumber "\.\d\+\%([eE][-+]\=\d\+\)\=\>"
" floating point number, without dot, with exponent
@@ -130,8 +163,15 @@ if lua_version >= 5
endif
endif
+" tables
+syn region luaTableBlock transparent matchgroup=luaTable start="{" end="}" contains=TOP,luaStatement
+
+" methods
+syntax match luaFunc ":\@<=\k\+"
+
+" built-in functions
syn keyword luaFunc assert collectgarbage dofile error next
-syn keyword luaFunc print rawget rawset tonumber tostring type _VERSION
+syn keyword luaFunc print rawget rawset self tonumber tostring type _VERSION
if lua_version == 4
syn keyword luaFunc _ALERT _ERRORMESSAGE gcinfo
@@ -168,30 +208,26 @@ elseif lua_version == 5
syn match luaFunc /\<package\.loaded\>/
syn match luaFunc /\<package\.loadlib\>/
syn match luaFunc /\<package\.path\>/
+ syn match luaFunc /\<package\.preload\>/
if lua_subversion == 1
syn keyword luaFunc getfenv setfenv
syn keyword luaFunc loadstring module unpack
syn match luaFunc /\<package\.loaders\>/
- syn match luaFunc /\<package\.preload\>/
syn match luaFunc /\<package\.seeall\>/
- elseif lua_subversion == 2
+ elseif lua_subversion >= 2
syn keyword luaFunc _ENV rawlen
syn match luaFunc /\<package\.config\>/
syn match luaFunc /\<package\.preload\>/
syn match luaFunc /\<package\.searchers\>/
syn match luaFunc /\<package\.searchpath\>/
- syn match luaFunc /\<bit32\.arshift\>/
- syn match luaFunc /\<bit32\.band\>/
- syn match luaFunc /\<bit32\.bnot\>/
- syn match luaFunc /\<bit32\.bor\>/
- syn match luaFunc /\<bit32\.btest\>/
- syn match luaFunc /\<bit32\.bxor\>/
- syn match luaFunc /\<bit32\.extract\>/
- syn match luaFunc /\<bit32\.lrotate\>/
- syn match luaFunc /\<bit32\.lshift\>/
- syn match luaFunc /\<bit32\.replace\>/
- syn match luaFunc /\<bit32\.rrotate\>/
- syn match luaFunc /\<bit32\.rshift\>/
+ endif
+
+ if lua_subversion >= 3
+ syn match luaFunc /\<coroutine\.isyieldable\>/
+ endif
+ if lua_subversion >= 4
+ syn keyword luaFunc warn
+ syn match luaFunc /\<coroutine\.close\>/
endif
syn match luaFunc /\<coroutine\.running\>/
endif
@@ -200,6 +236,7 @@ elseif lua_version == 5
syn match luaFunc /\<coroutine\.status\>/
syn match luaFunc /\<coroutine\.wrap\>/
syn match luaFunc /\<coroutine\.yield\>/
+
syn match luaFunc /\<string\.byte\>/
syn match luaFunc /\<string\.char\>/
syn match luaFunc /\<string\.dump\>/
@@ -218,6 +255,18 @@ elseif lua_version == 5
syn match luaFunc /\<string\.match\>/
syn match luaFunc /\<string\.reverse\>/
endif
+ if lua_subversion >= 3
+ syn match luaFunc /\<string\.pack\>/
+ syn match luaFunc /\<string\.packsize\>/
+ syn match luaFunc /\<string\.unpack\>/
+ 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
+
if lua_subversion == 0
syn match luaFunc /\<table\.getn\>/
syn match luaFunc /\<table\.setn\>/
@@ -225,19 +274,40 @@ elseif lua_version == 5
syn match luaFunc /\<table\.foreachi\>/
elseif lua_subversion == 1
syn match luaFunc /\<table\.maxn\>/
- elseif lua_subversion == 2
+ elseif lua_subversion >= 2
syn match luaFunc /\<table\.pack\>/
syn match luaFunc /\<table\.unpack\>/
+ if lua_subversion >= 3
+ syn match luaFunc /\<table\.move\>/
+ endif
endif
syn match luaFunc /\<table\.concat\>/
- syn match luaFunc /\<table\.sort\>/
syn match luaFunc /\<table\.insert\>/
+ syn match luaFunc /\<table\.sort\>/
syn match luaFunc /\<table\.remove\>/
+
+ if lua_subversion == 2
+ syn match luaFunc /\<bit32\.arshift\>/
+ syn match luaFunc /\<bit32\.band\>/
+ syn match luaFunc /\<bit32\.bnot\>/
+ syn match luaFunc /\<bit32\.bor\>/
+ syn match luaFunc /\<bit32\.btest\>/
+ syn match luaFunc /\<bit32\.bxor\>/
+ syn match luaFunc /\<bit32\.extract\>/
+ syn match luaFunc /\<bit32\.lrotate\>/
+ syn match luaFunc /\<bit32\.lshift\>/
+ syn match luaFunc /\<bit32\.replace\>/
+ syn match luaFunc /\<bit32\.rrotate\>/
+ syn match luaFunc /\<bit32\.rshift\>/
+ endif
+
syn match luaFunc /\<math\.abs\>/
syn match luaFunc /\<math\.acos\>/
syn match luaFunc /\<math\.asin\>/
syn match luaFunc /\<math\.atan\>/
- syn match luaFunc /\<math\.atan2\>/
+ if lua_subversion < 3
+ syn match luaFunc /\<math\.atan2\>/
+ endif
syn match luaFunc /\<math\.ceil\>/
syn match luaFunc /\<math\.sin\>/
syn match luaFunc /\<math\.cos\>/
@@ -251,25 +321,36 @@ elseif lua_version == 5
if lua_subversion == 0
syn match luaFunc /\<math\.mod\>/
syn match luaFunc /\<math\.log10\>/
- else
- if lua_subversion == 1
- syn match luaFunc /\<math\.log10\>/
- endif
+ elseif lua_subversion == 1
+ syn match luaFunc /\<math\.log10\>/
+ endif
+ if lua_subversion >= 1
syn match luaFunc /\<math\.huge\>/
syn match luaFunc /\<math\.fmod\>/
syn match luaFunc /\<math\.modf\>/
- syn match luaFunc /\<math\.cosh\>/
- syn match luaFunc /\<math\.sinh\>/
- syn match luaFunc /\<math\.tanh\>/
+ if lua_subversion == 1 || lua_subversion == 2
+ syn match luaFunc /\<math\.cosh\>/
+ syn match luaFunc /\<math\.sinh\>/
+ syn match luaFunc /\<math\.tanh\>/
+ endif
endif
- syn match luaFunc /\<math\.pow\>/
syn match luaFunc /\<math\.rad\>/
syn match luaFunc /\<math\.sqrt\>/
- syn match luaFunc /\<math\.frexp\>/
- syn match luaFunc /\<math\.ldexp\>/
+ if lua_subversion < 3
+ syn match luaFunc /\<math\.pow\>/
+ syn match luaFunc /\<math\.frexp\>/
+ syn match luaFunc /\<math\.ldexp\>/
+ else
+ syn match luaFunc /\<math\.maxinteger\>/
+ syn match luaFunc /\<math\.mininteger\>/
+ syn match luaFunc /\<math\.tointeger\>/
+ syn match luaFunc /\<math\.type\>/
+ syn match luaFunc /\<math\.ult\>/
+ endif
syn match luaFunc /\<math\.random\>/
syn match luaFunc /\<math\.randomseed\>/
syn match luaFunc /\<math\.pi\>/
+
syn match luaFunc /\<io\.close\>/
syn match luaFunc /\<io\.flush\>/
syn match luaFunc /\<io\.input\>/
@@ -284,6 +365,7 @@ elseif lua_version == 5
syn match luaFunc /\<io\.tmpfile\>/
syn match luaFunc /\<io\.type\>/
syn match luaFunc /\<io\.write\>/
+
syn match luaFunc /\<os\.clock\>/
syn match luaFunc /\<os\.date\>/
syn match luaFunc /\<os\.difftime\>/
@@ -295,6 +377,7 @@ elseif lua_version == 5
syn match luaFunc /\<os\.setlocale\>/
syn match luaFunc /\<os\.time\>/
syn match luaFunc /\<os\.tmpname\>/
+
syn match luaFunc /\<debug\.debug\>/
syn match luaFunc /\<debug\.gethook\>/
syn match luaFunc /\<debug\.getinfo\>/
@@ -307,53 +390,49 @@ elseif lua_version == 5
if lua_subversion == 1
syn match luaFunc /\<debug\.getfenv\>/
syn match luaFunc /\<debug\.setfenv\>/
+ endif
+ if lua_subversion >= 1
syn match luaFunc /\<debug\.getmetatable\>/
syn match luaFunc /\<debug\.setmetatable\>/
syn match luaFunc /\<debug\.getregistry\>/
- elseif lua_subversion == 2
- syn match luaFunc /\<debug\.getmetatable\>/
- syn match luaFunc /\<debug\.setmetatable\>/
- syn match luaFunc /\<debug\.getregistry\>/
- syn match luaFunc /\<debug\.getuservalue\>/
- syn match luaFunc /\<debug\.setuservalue\>/
- 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\>/
+ if lua_subversion >= 2
+ syn match luaFunc /\<debug\.getuservalue\>/
+ syn match luaFunc /\<debug\.setuservalue\>/
+ syn match luaFunc /\<debug\.upvalueid\>/
+ syn match luaFunc /\<debug\.upvaluejoin\>/
+ endif
+ if lua_subversion >= 4
+ syn match luaFunc /\<debug.setcstacklimit\>/
+ endif
endif
endif
" Define the default highlighting.
" Only when an item doesn't have highlighting yet
-hi def link luaStatement Statement
-hi def link luaRepeat Repeat
-hi def link luaFor Repeat
-hi def link luaString String
-hi def link luaString2 String
-hi def link luaNumber Number
-hi def link luaOperator Operator
-hi def link luaIn Operator
-hi def link luaConstant Constant
-hi def link luaCond Conditional
-hi def link luaElse Conditional
-hi def link luaFunction Function
-hi def link luaComment Comment
-hi def link luaTodo Todo
-hi def link luaTable Structure
-hi def link luaError Error
-hi def link luaParenError Error
-hi def link luaBraceError Error
-hi def link luaSpecial SpecialChar
-hi def link luaFunc Identifier
-hi def link luaLabel Label
+hi def link luaStatement Statement
+hi def link luaRepeat Repeat
+hi def link luaFor Repeat
+hi def link luaString String
+hi def link luaString2 String
+hi def link luaStringDelimiter luaString
+hi def link luaNumber Number
+hi def link luaOperator Operator
+hi def link luaSymbolOperator luaOperator
+hi def link luaConstant Constant
+hi def link luaCond Conditional
+hi def link luaCondElse Conditional
+hi def link luaFunction Function
+hi def link luaMetaMethod Function
+hi def link luaComment Comment
+hi def link luaCommentDelimiter luaComment
+hi def link luaTodo Todo
+hi def link luaTable Structure
+hi def link luaError Error
+hi def link luaParenError Error
+hi def link luaSpecial SpecialChar
+hi def link luaFunc Identifier
+hi def link luaLabel Label
let b:current_syntax = "lua"
diff --git a/runtime/syntax/lyrics.vim b/runtime/syntax/lyrics.vim
new file mode 100644
index 0000000000..42a288b51b
--- /dev/null
+++ b/runtime/syntax/lyrics.vim
@@ -0,0 +1,43 @@
+" Vim syntax file
+" Language: LyRiCs
+" Maintainer: ObserverOfTime <chronobserver@disroot.org>
+" Filenames: *.lrc
+" Last Change: 2022 Sep 18
+
+if exists('b:current_syntax')
+ finish
+endif
+
+let s:cpo_save = &cpoptions
+set cpoptions&vim
+
+syn case ignore
+
+" Errors
+syn match lrcError /^.\+$/
+
+" ID tags
+syn match lrcTag /^\s*\[\a\+:.\+\]\s*$/ contains=lrcTagName,lrcTagValue
+syn match lrcTagName contained nextgroup=lrcTagValue
+ \ /\[\zs\(al\|ar\|au\|by\|encoding\|la\|id\|length\|offset\|re\|ti\|ve\)\ze:/
+syn match lrcTagValue /:\zs.\+\ze\]/ contained
+
+" Lyrics
+syn match lrcLyricTime /^\s*\[\d\d:\d\d\.\d\d\]/
+ \ contains=lrcNumber nextgroup=lrcLyricLine
+syn match lrcLyricLine /.*$/ contained contains=lrcWordTime,@Spell
+syn match lrcWordTime /<\d\d:\d\d\.\d\d>/ contained contains=lrcNumber,@NoSpell
+syn match lrcNumber /[+-]\=\d\+/ contained
+
+hi def link lrcLyricTime Label
+hi def link lrcNumber Number
+hi def link lrcTag PreProc
+hi def link lrcTagName Identifier
+hi def link lrcTagValue String
+hi def link lrcWordTime Special
+hi def link lrcError Error
+
+let b:current_syntax = 'lyrics'
+
+let &cpoptions = s:cpo_save
+unlet s:cpo_save
diff --git a/runtime/syntax/make.vim b/runtime/syntax/make.vim
index 68f7ee21ea..b4573044ca 100644
--- a/runtime/syntax/make.vim
+++ b/runtime/syntax/make.vim
@@ -3,7 +3,7 @@
" Maintainer: Roland Hieber <rohieb+vim-iR0jGdkV@rohieb.name>, <https://github.com/rohieb>
" Previous Maintainer: Claudio Fleiner <claudio@fleiner.com>
" URL: https://github.com/vim/vim/blob/master/runtime/syntax/make.vim
-" Last Change: 2020 Oct 16
+" Last Change: 2022 Nov 06
" quit when a syntax file was already loaded
if exists("b:current_syntax")
@@ -45,11 +45,11 @@ syn match makeImplicit "^\.[A-Za-z0-9_./\t -]\+\s*:$"me=e-1
syn match makeImplicit "^\.[A-Za-z0-9_./\t -]\+\s*:[^=]"me=e-2
syn region makeTarget transparent matchgroup=makeTarget
- \ start="^[~A-Za-z0-9_./$()%-][A-Za-z0-9_./\t $()%-]*&\?:\?:\{1,2}[^:=]"rs=e-1
+ \ start="^[~A-Za-z0-9_./$(){}%-][A-Za-z0-9_./\t ${}()%-]*&\?:\?:\{1,2}[^:=]"rs=e-1
\ end="[^\\]$"
\ keepend contains=makeIdent,makeSpecTarget,makeNextLine,makeComment,makeDString
\ skipnl nextGroup=makeCommands
-syn match makeTarget "^[~A-Za-z0-9_./$()%*@-][A-Za-z0-9_./\t $()%*@-]*&\?::\=\s*$"
+syn match makeTarget "^[~A-Za-z0-9_./$(){}%*@-][A-Za-z0-9_./\t $(){}%*@-]*&\?::\=\s*$"
\ contains=makeIdent,makeSpecTarget,makeComment
\ skipnl nextgroup=makeCommands,makeCommandError
diff --git a/runtime/syntax/markdown.vim b/runtime/syntax/markdown.vim
index 17b61c2fa4..44187ff18c 100644
--- a/runtime/syntax/markdown.vim
+++ b/runtime/syntax/markdown.vim
@@ -1,8 +1,8 @@
" Vim syntax file
" Language: Markdown
-" Maintainer: Tim Pope <vimNOSPAM@tpope.org>
+" Maintainer: Tim Pope <https://github.com/tpope/vim-markdown>
" Filenames: *.markdown
-" Last Change: 2020 Jan 14
+" Last Change: 2022 Oct 13
if exists("b:current_syntax")
finish
@@ -12,6 +12,12 @@ if !exists('main_syntax')
let main_syntax = 'markdown'
endif
+if has('folding')
+ let s:foldmethod = &l:foldmethod
+ let s:foldtext = &l:foldtext
+endif
+let s:iskeyword = &l:iskeyword
+
runtime! syntax/html.vim
unlet! b:current_syntax
@@ -26,17 +32,33 @@ for s:type in map(copy(g:markdown_fenced_languages),'matchstr(v:val,"[^=]*$")')
if s:type =~ '\.'
let b:{matchstr(s:type,'[^.]*')}_subtype = matchstr(s:type,'\.\zs.*')
endif
- exe 'syn include @markdownHighlight'.substitute(s:type,'\.','','g').' syntax/'.matchstr(s:type,'[^.]*').'.vim'
+ syn case match
+ exe 'syn include @markdownHighlight_'.tr(s:type,'.','_').' syntax/'.matchstr(s:type,'[^.]*').'.vim'
unlet! b:current_syntax
let s:done_include[matchstr(s:type,'[^.]*')] = 1
endfor
unlet! s:type
unlet! s:done_include
+syn spell toplevel
+if exists('s:foldmethod') && s:foldmethod !=# &l:foldmethod
+ let &l:foldmethod = s:foldmethod
+ unlet s:foldmethod
+endif
+if exists('s:foldtext') && s:foldtext !=# &l:foldtext
+ let &l:foldtext = s:foldtext
+ unlet s:foldtext
+endif
+if s:iskeyword !=# &l:iskeyword
+ let &l:iskeyword = s:iskeyword
+endif
+unlet s:iskeyword
+
if !exists('g:markdown_minlines')
let g:markdown_minlines = 50
endif
execute 'syn sync minlines=' . g:markdown_minlines
+syn sync linebreaks=1
syn case ignore
syn match markdownValid '[<>]\c[a-z/$!]\@!' transparent contains=NONE
@@ -52,16 +74,16 @@ syn match markdownH2 "^.\+\n-\+$" contained contains=@markdownInline,markdownHea
syn match markdownHeadingRule "^[=-]\+$" contained
-syn region markdownH1 matchgroup=markdownH1Delimiter start="##\@!" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained
-syn region markdownH2 matchgroup=markdownH2Delimiter start="###\@!" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained
-syn region markdownH3 matchgroup=markdownH3Delimiter start="####\@!" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained
-syn region markdownH4 matchgroup=markdownH4Delimiter start="#####\@!" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained
-syn region markdownH5 matchgroup=markdownH5Delimiter start="######\@!" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained
-syn region markdownH6 matchgroup=markdownH6Delimiter start="#######\@!" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained
+syn region markdownH1 matchgroup=markdownH1Delimiter start=" \{,3}#\s" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained
+syn region markdownH2 matchgroup=markdownH2Delimiter start=" \{,3}##\s" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained
+syn region markdownH3 matchgroup=markdownH3Delimiter start=" \{,3}###\s" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained
+syn region markdownH4 matchgroup=markdownH4Delimiter start=" \{,3}####\s" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained
+syn region markdownH5 matchgroup=markdownH5Delimiter start=" \{,3}#####\s" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained
+syn region markdownH6 matchgroup=markdownH6Delimiter start=" \{,3}######\s" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained
syn match markdownBlockquote ">\%(\s\|$\)" contained nextgroup=@markdownBlock
-syn region markdownCodeBlock start=" \|\t" end="$" contained
+syn region markdownCodeBlock start="^\n\( \{4,}\|\t\)" end="^\ze \{,3}\S.*$" keepend
" TODO: real nesting
syn match markdownListMarker "\%(\t\| \{0,4\}\)[-*+]\%(\s\+\S\)\@=" contained
@@ -79,7 +101,7 @@ syn region markdownUrlTitle matchgroup=markdownUrlTitleDelimiter start=+"+ end=+
syn region markdownUrlTitle matchgroup=markdownUrlTitleDelimiter start=+'+ end=+'+ keepend contained
syn region markdownUrlTitle matchgroup=markdownUrlTitleDelimiter start=+(+ end=+)+ keepend contained
-syn region markdownLinkText matchgroup=markdownLinkTextDelimiter start="!\=\[\%(\%(\_[^][]\|\[\_[^][]*\]\)*]\%( \=[[(]\)\)\@=" end="\]\%( \=[[(]\)\@=" nextgroup=markdownLink,markdownId skipwhite contains=@markdownInline,markdownLineStart
+syn region markdownLinkText matchgroup=markdownLinkTextDelimiter start="!\=\[\%(\_[^][]*\%(\[\_[^][]*\]\_[^][]*\)*]\%( \=[[(]\)\)\@=" end="\]\%( \=[[(]\)\@=" nextgroup=markdownLink,markdownId skipwhite contains=@markdownInline,markdownLineStart
syn region markdownLink matchgroup=markdownLinkDelimiter start="(" end=")" contains=markdownUrl keepend contained
syn region markdownId matchgroup=markdownIdDelimiter start="\[" end="\]" keepend contained
syn region markdownAutomaticLink matchgroup=markdownUrlDelimiter start="<\%(\w\+:\|[[:alnum:]_+-]\+@\)\@=" end=">" keepend oneline
@@ -88,31 +110,38 @@ let s:concealends = ''
if has('conceal') && get(g:, 'markdown_syntax_conceal', 1) == 1
let s:concealends = ' concealends'
endif
-exe 'syn region markdownItalic matchgroup=markdownItalicDelimiter start="\S\@<=\*\|\*\S\@=" end="\S\@<=\*\|\*\S\@=" skip="\\\*" contains=markdownLineStart,@Spell' . s:concealends
-exe 'syn region markdownItalic matchgroup=markdownItalicDelimiter start="\w\@<!_\S\@=" end="\S\@<=_\w\@!" skip="\\_" contains=markdownLineStart,@Spell' . s:concealends
-exe 'syn region markdownBold matchgroup=markdownBoldDelimiter start="\S\@<=\*\*\|\*\*\S\@=" end="\S\@<=\*\*\|\*\*\S\@=" skip="\\\*" contains=markdownLineStart,markdownItalic,@Spell' . s:concealends
-exe 'syn region markdownBold matchgroup=markdownBoldDelimiter start="\w\@<!__\S\@=" end="\S\@<=__\w\@!" skip="\\_" contains=markdownLineStart,markdownItalic,@Spell' . s:concealends
-exe 'syn region markdownBoldItalic matchgroup=markdownBoldItalicDelimiter start="\S\@<=\*\*\*\|\*\*\*\S\@=" end="\S\@<=\*\*\*\|\*\*\*\S\@=" skip="\\\*" contains=markdownLineStart,@Spell' . s:concealends
-exe 'syn region markdownBoldItalic matchgroup=markdownBoldItalicDelimiter start="\w\@<!___\S\@=" end="\S\@<=___\w\@!" skip="\\_" contains=markdownLineStart,@Spell' . s:concealends
+exe 'syn region markdownItalic matchgroup=markdownItalicDelimiter start="\*\S\@=" end="\S\@<=\*\|^$" skip="\\\*" contains=markdownLineStart,@Spell' . s:concealends
+exe 'syn region markdownItalic matchgroup=markdownItalicDelimiter start="\w\@<!_\S\@=" end="\S\@<=_\w\@!\|^$" skip="\\_" contains=markdownLineStart,@Spell' . s:concealends
+exe 'syn region markdownBold matchgroup=markdownBoldDelimiter start="\*\*\S\@=" end="\S\@<=\*\*\|^$" skip="\\\*" contains=markdownLineStart,markdownItalic,@Spell' . s:concealends
+exe 'syn region markdownBold matchgroup=markdownBoldDelimiter start="\w\@<!__\S\@=" end="\S\@<=__\w\@!\|^$" skip="\\_" contains=markdownLineStart,markdownItalic,@Spell' . s:concealends
+exe 'syn region markdownBoldItalic matchgroup=markdownBoldItalicDelimiter start="\*\*\*\S\@=" end="\S\@<=\*\*\*\|^$" skip="\\\*" contains=markdownLineStart,@Spell' . s:concealends
+exe 'syn region markdownBoldItalic matchgroup=markdownBoldItalicDelimiter start="\w\@<!___\S\@=" end="\S\@<=___\w\@!\|^$" skip="\\_" contains=markdownLineStart,@Spell' . s:concealends
+exe 'syn region markdownStrike matchgroup=markdownStrikeDelimiter start="\~\~\S\@=" end="\S\@<=\~\~\|^$" contains=markdownLineStart,@Spell' . s:concealends
syn region markdownCode matchgroup=markdownCodeDelimiter start="`" end="`" keepend contains=markdownLineStart
syn region markdownCode matchgroup=markdownCodeDelimiter start="`` \=" end=" \=``" keepend contains=markdownLineStart
-syn region markdownCode matchgroup=markdownCodeDelimiter start="^\s*````*.*$" end="^\s*````*\ze\s*$" keepend
+syn region markdownCodeBlock matchgroup=markdownCodeDelimiter start="^\s*\z(`\{3,\}\).*$" end="^\s*\z1\ze\s*$" keepend
+syn region markdownCodeBlock matchgroup=markdownCodeDelimiter start="^\s*\z(\~\{3,\}\).*$" end="^\s*\z1\ze\s*$" keepend
syn match markdownFootnote "\[^[^\]]\+\]"
syn match markdownFootnoteDefinition "^\[^[^\]]\+\]:"
-if main_syntax ==# 'markdown'
- let s:done_include = {}
- for s:type in g:markdown_fenced_languages
- if has_key(s:done_include, matchstr(s:type,'[^.]*'))
- continue
- endif
- exe 'syn region markdownHighlight'.substitute(matchstr(s:type,'[^=]*$'),'\..*','','').' matchgroup=markdownCodeDelimiter start="^\s*````*\s*\%({.\{-}\.\)\='.matchstr(s:type,'[^=]*').'}\=\S\@!.*$" end="^\s*````*\ze\s*$" keepend contains=@markdownHighlight'.substitute(matchstr(s:type,'[^=]*$'),'\.','','g') . s:concealends
- let s:done_include[matchstr(s:type,'[^.]*')] = 1
- endfor
- unlet! s:type
- unlet! s:done_include
+let s:done_include = {}
+for s:type in g:markdown_fenced_languages
+ if has_key(s:done_include, matchstr(s:type,'[^.]*'))
+ continue
+ endif
+ exe 'syn region markdownHighlight_'.substitute(matchstr(s:type,'[^=]*$'),'\..*','','').' matchgroup=markdownCodeDelimiter start="^\s*\z(`\{3,\}\)\s*\%({.\{-}\.\)\='.matchstr(s:type,'[^=]*').'}\=\S\@!.*$" end="^\s*\z1\ze\s*$" keepend contains=@markdownHighlight_'.tr(matchstr(s:type,'[^=]*$'),'.','_') . s:concealends
+ exe 'syn region markdownHighlight_'.substitute(matchstr(s:type,'[^=]*$'),'\..*','','').' matchgroup=markdownCodeDelimiter start="^\s*\z(\~\{3,\}\)\s*\%({.\{-}\.\)\='.matchstr(s:type,'[^=]*').'}\=\S\@!.*$" end="^\s*\z1\ze\s*$" keepend contains=@markdownHighlight_'.tr(matchstr(s:type,'[^=]*$'),'.','_') . s:concealends
+ let s:done_include[matchstr(s:type,'[^.]*')] = 1
+endfor
+unlet! s:type
+unlet! s:done_include
+
+if get(b:, 'markdown_yaml_head', get(g:, 'markdown_yaml_head', main_syntax ==# 'markdown'))
+ syn include @markdownYamlTop syntax/yaml.vim
+ unlet! b:current_syntax
+ syn region markdownYamlHead start="\%^---$" end="^\%(---\|\.\.\.\)\s*$" keepend contains=@markdownYamlTop,@Spell
endif
syn match markdownEscape "\\[][\\`*_{}()<>#+.!-]"
@@ -156,6 +185,8 @@ hi def link markdownBold htmlBold
hi def link markdownBoldDelimiter markdownBold
hi def link markdownBoldItalic htmlBoldItalic
hi def link markdownBoldItalicDelimiter markdownBoldItalic
+hi def link markdownStrike htmlStrike
+hi def link markdownStrikeDelimiter markdownStrike
hi def link markdownCodeDelimiter Delimiter
hi def link markdownEscape Special
diff --git a/runtime/syntax/mermaid.vim b/runtime/syntax/mermaid.vim
new file mode 100644
index 0000000000..afdbcc3d62
--- /dev/null
+++ b/runtime/syntax/mermaid.vim
@@ -0,0 +1,155 @@
+" Vim syntax file
+" Language: Mermaid
+" Maintainer: Craig MacEahern <https://github.com/craigmac/vim-mermaid>
+" Filenames: *.mmd
+" Last Change: 2022 Nov 22
+
+if exists("b:current_syntax")
+ finish
+endif
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+syntax iskeyword @,48-57,192-255,$,_,-,:
+syntax keyword mermaidKeyword
+ \ _blank
+ \ _self
+ \ _parent
+ \ _top
+ \ ::icon
+ \ accDescr
+ \ accTitle
+ \ actor
+ \ activate
+ \ alt
+ \ and
+ \ as
+ \ autonumber
+ \ branch
+ \ break
+ \ callback
+ \ checkout
+ \ class
+ \ classDef
+ \ classDiagram
+ \ click
+ \ commit
+ \ commitgitGraph
+ \ critical
+ \ dataFormat
+ \ dateFormat
+ \ deactivate
+ \ direction
+ \ element
+ \ else
+ \ end
+ \ erDiagram
+ \ flowchart
+ \ gantt
+ \ gitGraph
+ \ graph
+ \ journey
+ \ link
+ \ LR
+ \ TD
+ \ TB
+ \ RL
+ \ loop
+ \ merge
+ \ mindmap root
+ \ Note
+ \ Note right of
+ \ Note left of
+ \ Note over
+ \ note
+ \ note right of
+ \ note left of
+ \ note over
+ \ opt
+ \ option
+ \ par
+ \ participant
+ \ pie
+ \ rect
+ \ requirement
+ \ rgb
+ \ section
+ \ sequenceDiagram
+ \ state
+ \ stateDiagram
+ \ stateDiagram-v2
+ \ style
+ \ subgraph
+ \ title
+highlight link mermaidKeyword Keyword
+
+syntax match mermaidStatement "|"
+syntax match mermaidStatement "--\?[>x)]>\?+\?-\?"
+syntax match mermaidStatement "\~\~\~"
+syntax match mermaidStatement "--"
+syntax match mermaidStatement "---"
+syntax match mermaidStatement "-->"
+syntax match mermaidStatement "-\."
+syntax match mermaidStatement "\.->"
+syntax match mermaidStatement "-\.-"
+syntax match mermaidStatement "-\.\.-"
+syntax match mermaidStatement "-\.\.\.-"
+syntax match mermaidStatement "=="
+syntax match mermaidStatement "==>"
+syntax match mermaidStatement "===>"
+syntax match mermaidStatement "====>"
+syntax match mermaidStatement "&"
+syntax match mermaidStatement "--o"
+syntax match mermaidStatement "--x"
+syntax match mermaidStatement "x--x"
+syntax match mermaidStatement "-----"
+syntax match mermaidStatement "---->"
+syntax match mermaidStatement "==="
+syntax match mermaidStatement "===="
+syntax match mermaidStatement "====="
+syntax match mermaidStatement ":::"
+syntax match mermaidStatement "<|--"
+syntax match mermaidStatement "\*--"
+syntax match mermaidStatement "o--"
+syntax match mermaidStatement "o--o"
+syntax match mermaidStatement "<--"
+syntax match mermaidStatement "<-->"
+syntax match mermaidStatement "\.\."
+syntax match mermaidStatement "<\.\."
+syntax match mermaidStatement "<|\.\."
+syntax match mermaidStatement "--|>"
+syntax match mermaidStatement "--\*"
+syntax match mermaidStatement "--o"
+syntax match mermaidStatement "\.\.>"
+syntax match mermaidStatement "\.\.|>"
+syntax match mermaidStatement "<|--|>"
+syntax match mermaidStatement "||--o{"
+highlight link mermaidStatement Statement
+
+syntax match mermaidIdentifier "[\+-]\?\w\+(.*)[\$\*]\?"
+highlight link mermaidIdentifier Identifier
+
+syntax match mermaidType "[\+-\#\~]\?\cint\>"
+syntax match mermaidType "[\+-\#\~]\?\cString\>"
+syntax match mermaidType "[\+-\#\~]\?\cbool\>"
+syntax match mermaidType "[\+-\#\~]\?\cBigDecimal\>"
+syntax match mermaidType "[\+-\#\~]\?\cList\~.\+\~"
+syntax match mermaidType "<<\w\+>>"
+highlight link mermaidType Type
+
+syntax match mermaidComment "%%.*$"
+highlight link mermaidComment Comment
+
+syntax region mermaidDirective start="%%{" end="\}%%"
+highlight link mermaidDirective PreProc
+
+syntax region mermaidString start=/"/ skip=/\\"/ end=/"/
+highlight link mermaidString String
+
+let b:current_syntax = "mermaid"
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
+
+" vim:set sw=2:
diff --git a/runtime/syntax/modula3.vim b/runtime/syntax/modula3.vim
index b179303799..390a1a90ff 100644
--- a/runtime/syntax/modula3.vim
+++ b/runtime/syntax/modula3.vim
@@ -2,18 +2,29 @@
" Language: Modula-3
" Maintainer: Doug Kearns <dougkearns@gmail.com>
" Previous Maintainer: Timo Pedersen <dat97tpe@ludat.lth.se>
-" Last Change: 2021 Apr 08
+" Last Change: 2022 Oct 31
if exists("b:current_syntax")
finish
endif
-" Modula-3 keywords
-syn keyword modula3Keyword ANY ARRAY AS BITS BRANDED BY CASE CONST DEFINITION
-syn keyword modula3Keyword EVAL EXIT EXCEPT EXCEPTION EXIT EXPORTS FINALLY
-syn keyword modula3Keyword FROM GENERIC IMPORT LOCK METHOD OF RAISE RAISES
-syn keyword modula3Keyword READONLY RECORD REF RETURN SET TRY TYPE TYPECASE
-syn keyword modula3Keyword UNSAFE VALUE VAR WITH
+" Whitespace errors {{{1
+if exists("modula3_space_errors")
+ if !exists("modula3_no_trail_space_error")
+ syn match modula3SpaceError display excludenl "\s\+$"
+ endif
+ if !exists("modula3_no_tab_space_error")
+ syn match modula3SpaceError display " \+\t"me=e-1
+ endif
+endif
+
+" Keywords {{{1
+syn keyword modula3Keyword ANY ARRAY AS BITS BRANDED BY CASE CONST
+syn keyword modula3Keyword DEFINITION EVAL EXIT EXCEPT EXCEPTION EXIT
+syn keyword modula3Keyword EXPORTS FINALLY FROM GENERIC IMPORT LOCK METHOD
+syn keyword modula3Keyword OF RAISE RAISES READONLY RECORD REF
+syn keyword modula3Keyword RETURN SET TRY TYPE TYPECASE UNSAFE
+syn keyword modula3Keyword VALUE VAR WITH
syn match modula3keyword "\<UNTRACED\>"
@@ -22,44 +33,73 @@ syn keyword modula3Block PROCEDURE FUNCTION MODULE INTERFACE REPEAT THEN
syn keyword modula3Block BEGIN END OBJECT METHODS OVERRIDES RECORD REVEAL
syn keyword modula3Block WHILE UNTIL DO TO IF FOR ELSIF ELSE LOOP
-" Reserved identifiers
+" Reserved identifiers {{{1
syn keyword modula3Identifier ABS ADR ADRSIZE BITSIZE BYTESIZE CEILING DEC
syn keyword modula3Identifier DISPOSE FIRST FLOAT FLOOR INC ISTYPE LAST
syn keyword modula3Identifier LOOPHOLE MAX MIN NARROW NEW NUMBER ORD ROUND
syn keyword modula3Identifier SUBARRAY TRUNC TYPECODE VAL
-" Predefined types
+" Predefined types {{{1
syn keyword modula3Type ADDRESS BOOLEAN CARDINAL CHAR EXTENDED INTEGER
syn keyword modula3Type LONGCARD LONGINT LONGREAL MUTEX NULL REAL REFANY TEXT
syn keyword modula3Type WIDECHAR
syn match modula3Type "\<\%(UNTRACED\s\+\)\=ROOT\>"
-" Operators
-syn keyword modulaOperator DIV MOD IN AND OR NOT
+" Operators {{{1
+syn keyword modula3Operator DIV MOD
+syn keyword modula3Operator IN
+syn keyword modula3Operator NOT AND OR
+" TODO: exclude = from declarations
if exists("modula3_operators")
syn match modula3Operator "\^"
- syn match modula3Operator "+\|-\|\*\|/\|&"
- " TODO: need to exclude = in procedure definitions
- syn match modula3Operator "<=\|<\|>=\|>\|:\@<!=\|#"
+ syn match modula3Operator "[-+/*]"
+ syn match modula3Operator "&"
+ syn match modula3Operator "<=\|<:\@!\|>=\|>"
+ syn match modula3Operator ":\@<!=\|#"
endif
+" Literals {{{1
+
" Booleans
syn keyword modula3Boolean TRUE FALSE
" Nil
syn keyword modula3Nil NIL
-" Integers
-syn match modula3Integer "\<\d\+L\=\>"
-syn match modula3Integer "\<\d\d\=_\x\+L\=\>"
+" Numbers {{{2
+
+" NOTE: Negated numbers are constant expressions not literals
+
+syn case ignore
+
+ " Integers
+
+ syn match modula3Integer "\<\d\+L\=\>"
-" Reals
-syn match modula3Real "\c\<\d\+\.\d\+\%([EDX][+-]\=\d\+\)\=\>"
+ if exists("modula3_number_errors")
+ syn match modula3IntegerError "\<\d\d\=_\x\+L\=\>"
+ endif
+
+ let s:digits = "0123456789ABCDEF"
+ for s:radix in range(2, 16)
+ " Nvim does not support interpolated strings yet.
+ " exe $'syn match modula3Integer "\<{s:radix}_[{s:digits[:s:radix - 1]}]\+L\=\>"'
+ exe 'syn match modula3Integer "\<' .. s:radix .. '_[' .. s:digits[:s:radix - 1] .. ']\+L\=\>"'
+ endfor
+ unlet s:digits s:radix
+
+ " Reals
+ syn match modula3Real "\<\d\+\.\d\+\%([EDX][+-]\=\d\+\)\=\>"
+
+syn case match
+
+" Strings and characters {{{2
" String escape sequences
syn match modula3Escape "\\['"ntrf]" contained display
+" TODO: limit to <= 377 (255)
syn match modula3Escape "\\\o\{3}" contained display
syn match modula3Escape "\\\\" contained display
@@ -69,13 +109,23 @@ syn match modula3Character "'\%([^']\|\\.\|\\\o\{3}\)'" contains=modula3Escape
" Strings
syn region modula3String start=+"+ end=+"+ contains=modula3Escape
-" Pragmas
+" Pragmas {{{1
+" EXTERNAL INLINE ASSERT TRACE FATAL UNUSED OBSOLETE CALLBACK EXPORTED PRAGMA NOWARN LINE LL LL.sup SPEC
+" Documented: INLINE ASSERT TRACE FATAL UNUSED OBSOLETE NOWARN
syn region modula3Pragma start="<\*" end="\*>"
-" Comments
-syn region modula3Comment start="(\*" end="\*)" contains=modula3Comment,@Spell
+" Comments {{{1
+if !exists("modula3_no_comment_fold")
+ syn region modula3Comment start="(\*" end="\*)" contains=modula3Comment,@Spell fold
+ syn region modula3LineCommentBlock start="^\s*(\*.*\*)\s*\n\%(^\s*(\*.*\*)\s*$\)\@=" end="^\s*(\*.*\*)\s*\n\%(^\s*(\*.*\*)\s*$\)\@!" contains=modula3Comment transparent fold keepend
+else
+ syn region modula3Comment start="(\*" end="\*)" contains=modula3Comment,@Spell
+endif
+
+" Syncing "{{{1
+syn sync minlines=100
-" Default highlighting
+" Default highlighting {{{1
hi def link modula3Block Statement
hi def link modula3Boolean Boolean
hi def link modula3Character Character
@@ -85,12 +135,13 @@ hi def link modula3Identifier Keyword
hi def link modula3Integer Number
hi def link modula3Keyword Statement
hi def link modula3Nil Constant
+hi def link modula3IntegerError Error
hi def link modula3Operator Operator
hi def link modula3Pragma PreProc
hi def link modula3Real Float
hi def link modula3String String
-hi def link modula3Type Type
+hi def link modula3Type Type "}}}
let b:current_syntax = "modula3"
-" vim: nowrap sw=2 sts=2 ts=8 noet:
+" vim: nowrap sw=2 sts=2 ts=8 noet fdm=marker:
diff --git a/runtime/syntax/nix.vim b/runtime/syntax/nix.vim
new file mode 100644
index 0000000000..c07676a4a8
--- /dev/null
+++ b/runtime/syntax/nix.vim
@@ -0,0 +1,210 @@
+" Vim syntax file
+" Language: Nix
+" Maintainer: James Fleming <james@electronic-quill.net>
+" Original Author: Daiderd Jordan <daiderd@gmail.com>
+" Acknowledgement: Based on vim-nix maintained by Daiderd Jordan <daiderd@gmail.com>
+" https://github.com/LnL7/vim-nix
+" License: MIT
+" Last Change: 2022 Dec 06
+
+if exists("b:current_syntax")
+ finish
+endif
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+syn keyword nixBoolean true false
+syn keyword nixNull null
+syn keyword nixRecKeyword rec
+
+syn keyword nixOperator or
+syn match nixOperator '!=\|!'
+syn match nixOperator '<=\?'
+syn match nixOperator '>=\?'
+syn match nixOperator '&&'
+syn match nixOperator '//\='
+syn match nixOperator '=='
+syn match nixOperator '?'
+syn match nixOperator '||'
+syn match nixOperator '++\='
+syn match nixOperator '-'
+syn match nixOperator '\*'
+syn match nixOperator '->'
+
+syn match nixParen '[()]'
+syn match nixInteger '\d\+'
+
+syn keyword nixTodo FIXME NOTE TODO OPTIMIZE XXX HACK contained
+syn match nixComment '#.*' contains=nixTodo,@Spell
+syn region nixComment start=+/\*+ end=+\*/+ contains=nixTodo,@Spell
+
+syn region nixInterpolation matchgroup=nixInterpolationDelimiter start="\${" end="}" contained contains=@nixExpr,nixInterpolationParam
+
+syn match nixSimpleStringSpecial /\\\%([nrt"\\$]\|$\)/ contained
+syn match nixStringSpecial /''['$]/ contained
+syn match nixStringSpecial /\$\$/ contained
+syn match nixStringSpecial /''\\[nrt]/ contained
+
+syn match nixSimpleStringSpecial /\$\$/ contained
+
+syn match nixInvalidSimpleStringEscape /\\[^nrt"\\$]/ contained
+syn match nixInvalidStringEscape /''\\[^nrt]/ contained
+
+syn region nixSimpleString matchgroup=nixStringDelimiter start=+"+ skip=+\\"+ end=+"+ contains=nixInterpolation,nixSimpleStringSpecial,nixInvalidSimpleStringEscape
+syn region nixString matchgroup=nixStringDelimiter start=+''+ skip=+''['$\\]+ end=+''+ contains=nixInterpolation,nixStringSpecial,nixInvalidStringEscape
+
+syn match nixFunctionCall "[a-zA-Z_][a-zA-Z0-9_'-]*"
+
+syn match nixPath "[a-zA-Z0-9._+-]*\%(/[a-zA-Z0-9._+-]\+\)\+"
+syn match nixHomePath "\~\%(/[a-zA-Z0-9._+-]\+\)\+"
+syn match nixSearchPath "[a-zA-Z0-9._+-]\+\%(\/[a-zA-Z0-9._+-]\+\)*" contained
+syn match nixPathDelimiter "[<>]" contained
+syn match nixSearchPathRef "<[a-zA-Z0-9._+-]\+\%(\/[a-zA-Z0-9._+-]\+\)*>" contains=nixSearchPath,nixPathDelimiter
+syn match nixURI "[a-zA-Z][a-zA-Z0-9.+-]*:[a-zA-Z0-9%/?:@&=$,_.!~*'+-]\+"
+
+syn match nixAttributeDot "\." contained
+syn match nixAttribute "[a-zA-Z_][a-zA-Z0-9_'-]*\ze\%([^a-zA-Z0-9_'.-]\|$\)" contained
+syn region nixAttributeAssignment start="=" end="\ze;" contained contains=@nixExpr
+syn region nixAttributeDefinition start=/\ze[a-zA-Z_"$]/ end=";" contained contains=nixComment,nixAttribute,nixInterpolation,nixSimpleString,nixAttributeDot,nixAttributeAssignment
+
+syn region nixInheritAttributeScope start="(" end="\ze)" contained contains=@nixExpr
+syn region nixAttributeDefinition matchgroup=nixInherit start="\<inherit\>" end=";" contained contains=nixComment,nixInheritAttributeScope,nixAttribute
+
+syn region nixAttributeSet start="{" end="}" contains=nixComment,nixAttributeDefinition
+
+" vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+syn region nixArgumentDefinitionWithDefault matchgroup=nixArgumentDefinition start="[a-zA-Z_][a-zA-Z0-9_'-]*\ze\%(\s\|#.\{-\}\n\|\n\|/\*\_.\{-\}\*/\)*?\@=" matchgroup=NONE end="[,}]\@=" transparent contained contains=@nixExpr
+" vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+syn match nixArgumentDefinition "[a-zA-Z_][a-zA-Z0-9_'-]*\ze\%(\s\|#.\{-\}\n\|\n\|/\*\_.\{-\}\*/\)*[,}]\@=" contained
+syn match nixArgumentEllipsis "\.\.\." contained
+syn match nixArgumentSeparator "," contained
+
+" vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+syn match nixArgOperator '@\%(\s\|#.\{-\}\n\|\n\|/\*\_.\{-\}\*/\)*[a-zA-Z_][a-zA-Z0-9_'-]*\%(\s\|#.\{-\}\n\|\n\|/\*\_.\{-\}\*/\)*:'he=s+1 contained contains=nixAttribute
+
+" vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+syn match nixArgOperator '[a-zA-Z_][a-zA-Z0-9_'-]*\%(\s\|#.\{-\}\n\|\n\|/\*\_.\{-\}\*/\)*@'hs=e-1 contains=nixAttribute nextgroup=nixFunctionArgument
+
+" This is a bit more complicated, because function arguments can be passed in a
+" very similar form on how attribute sets are defined and two regions with the
+" same start patterns will shadow each other. Instead of a region we could use a
+" match on {\_.\{-\}}, which unfortunately doesn't take nesting into account.
+"
+" So what we do instead is that we look forward until we are sure that it's a
+" function argument. Unfortunately, we need to catch comments and both vertical
+" and horizontal white space, which the following regex should hopefully do:
+"
+" "\%(\s\|#.\{-\}\n\|\n\|/\*\_.\{-\}\*/\)*"
+"
+" It is also used throught the whole file and is marked with 'v's as well.
+"
+" Fortunately the matching rules for function arguments are much simpler than
+" for real attribute sets, because we can stop when we hit the first ellipsis or
+" default value operator, but we also need to paste the "whitespace & comments
+" eating" regex all over the place (marked with 'v's):
+"
+" Region match 1: { foo ? ... } or { foo, ... } or { ... } (ellipsis)
+" vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv {----- identifier -----}vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+syn region nixFunctionArgument start="{\ze\%(\s\|#.\{-\}\n\|\n\|/\*\_.\{-\}\*/\)*\%([a-zA-Z_][a-zA-Z0-9_'-]*\%(\s\|#.\{-\}\n\|\n\|/\*\_.\{-\}\*/\)*[,?}]\|\.\.\.\)" end="}" contains=nixComment,nixArgumentDefinitionWithDefault,nixArgumentDefinition,nixArgumentEllipsis,nixArgumentSeparator nextgroup=nixArgOperator
+
+" Now it gets more tricky, because we need to look forward for the colon, but
+" there could be something like "{}@foo:", even though it's highly unlikely.
+"
+" Region match 2: {}
+" vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv@vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv{----- identifier -----} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+syn region nixFunctionArgument start="{\ze\%(\s\|#.\{-\}\n\|\n\|/\*\_.\{-\}\*/\)*}\%(\%(\s\|#.\{-\}\n\|\n\|/\*\_.\{-\}\*/\)*@\%(\s\|#.\{-\}\n\|\n\|/\*\_.\{-\}\*/\)*[a-zA-Z_][a-zA-Z0-9_'-]*\)\%(\s\|#.\{-\}\n\|\n\|/\*\_.\{-\}\*/\)*:" end="}" contains=nixComment nextgroup=nixArgOperator
+
+" vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+syn match nixSimpleFunctionArgument "[a-zA-Z_][a-zA-Z0-9_'-]*\ze\%(\s\|#.\{-\}\n\|\n\|/\*\_.\{-\}\*/\)*:\([\n ]\)\@="
+
+syn region nixList matchgroup=nixListBracket start="\[" end="\]" contains=@nixExpr
+
+syn region nixLetExpr matchgroup=nixLetExprKeyword start="\<let\>" end="\<in\>" contains=nixComment,nixAttributeDefinition
+
+syn keyword nixIfExprKeyword then contained
+syn region nixIfExpr matchgroup=nixIfExprKeyword start="\<if\>" end="\<else\>" contains=@nixExpr,nixIfExprKeyword
+
+syn region nixWithExpr matchgroup=nixWithExprKeyword start="\<with\>" matchgroup=NONE end=";" contains=@nixExpr
+
+syn region nixAssertExpr matchgroup=nixAssertKeyword start="\<assert\>" matchgroup=NONE end=";" contains=@nixExpr
+
+syn cluster nixExpr contains=nixBoolean,nixNull,nixOperator,nixParen,nixInteger,nixRecKeyword,nixConditional,nixBuiltin,nixSimpleBuiltin,nixComment,nixFunctionCall,nixFunctionArgument,nixArgOperator,nixSimpleFunctionArgument,nixPath,nixHomePath,nixSearchPathRef,nixURI,nixAttributeSet,nixList,nixSimpleString,nixString,nixLetExpr,nixIfExpr,nixWithExpr,nixAssertExpr,nixInterpolation
+
+" These definitions override @nixExpr and have to come afterwards:
+
+syn match nixInterpolationParam "[a-zA-Z_][a-zA-Z0-9_'-]*\%(\.[a-zA-Z_][a-zA-Z0-9_'-]*\)*" contained
+
+" Non-namespaced Nix builtins as of version 2.0:
+syn keyword nixSimpleBuiltin
+ \ abort baseNameOf derivation derivationStrict dirOf fetchGit
+ \ fetchMercurial fetchTarball import isNull map mapAttrs placeholder removeAttrs
+ \ scopedImport throw toString
+
+
+" Namespaced and non-namespaced Nix builtins as of version 2.0:
+syn keyword nixNamespacedBuiltin contained
+ \ abort add addErrorContext all any attrNames attrValues baseNameOf
+ \ catAttrs compareVersions concatLists concatStringsSep currentSystem
+ \ currentTime deepSeq derivation derivationStrict dirOf div elem elemAt
+ \ fetchGit fetchMercurial fetchTarball fetchurl filter \ filterSource
+ \ findFile foldl' fromJSON functionArgs genList \ genericClosure getAttr
+ \ getEnv hasAttr hasContext hashString head import intersectAttrs isAttrs
+ \ isBool isFloat isFunction isInt isList isNull isString langVersion
+ \ length lessThan listToAttrs map mapAttrs match mul nixPath nixVersion
+ \ parseDrvName partition path pathExists placeholder readDir readFile
+ \ removeAttrs replaceStrings scopedImport seq sort split splitVersion
+ \ storeDir storePath stringLength sub substring tail throw toFile toJSON
+ \ toPath toString toXML trace tryEval typeOf unsafeDiscardOutputDependency
+ \ unsafeDiscardStringContext unsafeGetAttrPos valueSize fromTOML bitAnd
+ \ bitOr bitXor floor ceil
+
+syn match nixBuiltin "builtins\.[a-zA-Z']\+"he=s+9 contains=nixComment,nixNamespacedBuiltin
+
+hi def link nixArgOperator Operator
+hi def link nixArgumentDefinition Identifier
+hi def link nixArgumentEllipsis Operator
+hi def link nixAssertKeyword Keyword
+hi def link nixAttribute Identifier
+hi def link nixAttributeDot Operator
+hi def link nixBoolean Boolean
+hi def link nixBuiltin Special
+hi def link nixComment Comment
+hi def link nixConditional Conditional
+hi def link nixHomePath Include
+hi def link nixIfExprKeyword Keyword
+hi def link nixInherit Keyword
+hi def link nixInteger Integer
+hi def link nixInterpolation Macro
+hi def link nixInterpolationDelimiter Delimiter
+hi def link nixInterpolationParam Macro
+hi def link nixInvalidSimpleStringEscape Error
+hi def link nixInvalidStringEscape Error
+hi def link nixLetExprKeyword Keyword
+hi def link nixNamespacedBuiltin Special
+hi def link nixNull Constant
+hi def link nixOperator Operator
+hi def link nixPath Include
+hi def link nixPathDelimiter Delimiter
+hi def link nixRecKeyword Keyword
+hi def link nixSearchPath Include
+hi def link nixSimpleBuiltin Keyword
+hi def link nixSimpleFunctionArgument Identifier
+hi def link nixSimpleString String
+hi def link nixSimpleStringSpecial SpecialChar
+hi def link nixString String
+hi def link nixStringDelimiter Delimiter
+hi def link nixStringSpecial Special
+hi def link nixTodo Todo
+hi def link nixURI Include
+hi def link nixWithExprKeyword Keyword
+
+" This could lead up to slow syntax highlighting for large files, but usually
+" large files such as all-packages.nix are one large attribute set, so if we'd
+" use sync patterns we'd have to go back to the start of the file anyway
+syn sync fromstart
+
+let b:current_syntax = "nix"
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
diff --git a/runtime/syntax/nsis.vim b/runtime/syntax/nsis.vim
index 3a73fe0989..49fa17abf1 100644
--- a/runtime/syntax/nsis.vim
+++ b/runtime/syntax/nsis.vim
@@ -3,7 +3,7 @@
" Maintainer: Ken Takata
" URL: https://github.com/k-takata/vim-nsis
" Previous Maintainer: Alex Jakushev <Alex.Jakushev@kemek.lt>
-" Last Change: 2020-10-18
+" Last Change: 2022-11-05
" quit when a syntax file was already loaded
if exists("b:current_syntax")
@@ -394,9 +394,13 @@ syn keyword nsisInstruction contained CreateShortcut nextgroup=nsisCreateShortcu
syn region nsisCreateShortcutOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisCreateShortcutKwd
syn match nsisCreateShortcutKwd contained "/NoWorkingDir\>"
+syn keyword nsisInstruction contained GetWinVer nextgroup=nsisGetWinVerOpt skipwhite
+syn region nsisGetWinVerOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisGetWinVerKwd
+syn keyword nsisGetWinVerKwd contained Major Minor Build ServicePack
+
syn keyword nsisInstruction contained GetDLLVersion GetDLLVersionLocal nextgroup=nsisGetDLLVersionOpt skipwhite
-syn region nsisGetDLLVersionOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisGetDLLVersionKwd
-syn match nsisGetDLLVersionKwd contained "/ProductVersion\>"
+syn region nsisGetDLLVersionOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisGetDLLVersionKwd
+syn match nsisGetDLLVersionKwd contained "/ProductVersion\>"
syn keyword nsisInstruction contained GetFullPathName nextgroup=nsisGetFullPathNameOpt skipwhite
syn region nsisGetFullPathNameOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisGetFullPathNameKwd
@@ -562,10 +566,19 @@ syn match nsisSystem contained "!execute\>"
syn match nsisSystem contained "!makensis\>"
syn match nsisSystem contained "!packhdr\>"
syn match nsisSystem contained "!finalize\>"
+syn match nsisSystem contained "!uninstfinalize\>"
syn match nsisSystem contained "!system\>"
syn match nsisSystem contained "!tempfile\>"
-syn match nsisSystem contained "!getdllversion\>"
-syn match nsisSystem contained "!gettlbversion\>"
+
+" Add 'P' to avoid conflicts with nsisGetDLLVersionOpt. ('P' for preprocessor.)
+syn match nsisSystem contained "!getdllversion\>" nextgroup=nsisPGetdllversionOpt skipwhite
+syn region nsisPGetdllversionOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisPGetdllversionKwd
+syn match nsisPGetdllversionKwd contained "/\%(noerrors\|packed\|productversion\)\>"
+
+syn match nsisSystem contained "!gettlbversion\>" nextgroup=nsisPGettlbversionOpt skipwhite
+syn region nsisPGettlbversionOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisPGettlbversionKwd
+syn match nsisPGettlbversionKwd contained "/\%(noerrors\|packed\)\>"
+
syn match nsisSystem contained "!warning\>"
syn match nsisSystem contained "!pragma\>" nextgroup=nsisPragmaOpt skipwhite
@@ -581,7 +594,10 @@ syn match nsisDefine contained "!define\>" nextgroup=nsisDefineOpt skipwhite
syn region nsisDefineOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisDefineKwd
syn match nsisDefineKwd contained "/\%(ifndef\|redef\|date\|utcdate\|file\|intfmt\|math\)\>"
-syn match nsisDefine contained "!undef\>"
+syn match nsisDefine contained "!undef\>" nextgroup=nsisUndefineOpt skipwhite
+syn region nsisUndefineOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisUndefineKwd
+syn match nsisUndefineKwd contained "/noerrors\>"
+
syn match nsisPreCondit contained "!ifdef\>"
syn match nsisPreCondit contained "!ifndef\>"
@@ -659,6 +675,7 @@ hi def link nsisWriteRegMultiStrKwd Constant
hi def link nsisSetRegViewKwd Constant
hi def link nsisCopyFilesKwd Constant
hi def link nsisCreateShortcutKwd Constant
+hi def link nsisGetWinVerKwd Constant
hi def link nsisGetDLLVersionKwd Constant
hi def link nsisGetFullPathNameKwd Constant
hi def link nsisFileAttrib Constant
@@ -696,9 +713,12 @@ hi def link nsisIncludeKwd Constant
hi def link nsisAddplugindirKwd Constant
hi def link nsisAppendfileKwd Constant
hi def link nsisDelfileKwd Constant
+hi def link nsisPGetdllversionKwd Constant
+hi def link nsisPGettlbversionKwd Constant
hi def link nsisPragmaKwd Constant
hi def link nsisVerboseKwd Constant
hi def link nsisDefineKwd Constant
+hi def link nsisUndefineKwd Constant
hi def link nsisIfKwd Constant
hi def link nsisSearchparseKwd Constant
hi def link nsisSearchreplaceKwd Constant
diff --git a/runtime/syntax/obse.vim b/runtime/syntax/obse.vim
new file mode 100644
index 0000000000..4ff04281f3
--- /dev/null
+++ b/runtime/syntax/obse.vim
@@ -0,0 +1,3360 @@
+" Vim syntax file
+" Language: Oblivion Language (obl)
+" Original Creator: Ulthar Seramis
+" Maintainer: Kat <katisntgood@gmail.com>
+" Latest Revision: 13 November 2022
+
+if exists("b:current_syntax")
+ finish
+endif
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+" obse is case insensitive
+syntax case ignore
+
+" Statements {{{
+syn keyword obseStatement set let to skipwhite
+" the second part needs to be separate as to not mess up the next group
+syn match obseStatementTwo ":="
+" }}}
+
+" Regex matched objects {{{
+" these are matched with regex and thus must be set first
+syn match obseNames '\w\+'
+syn match obseScriptNameRegion '\i\+' contained
+syn match obseVariable '\w*\S' contained
+syn match obseReference '\zs\w\+\>\ze\.'
+" }}}
+
+" Operators {{{
+syn match obseOperator "\v\*"
+syn match obseOperator "\v\-"
+syn match obseOperator "\v\+"
+syn match obseOperator "\v\/"
+syn match obseOperator "\v\^"
+syn match obseOperator "\v\="
+syn match obseOperator "\v\>"
+syn match obseOperator "\v\<"
+syn match obseOperator "\v\!"
+syn match obseOperator "\v\&"
+syn match obseOperator "\v\|"
+" }}}
+
+" Numbers {{{
+syn match obseInt '\d\+'
+syn match obseInt '[-+]\d\+'
+syn match obseFloat '\d\+\.\d*'
+syn match obseFloat '[-+]\d\+\.\d*'
+" }}}
+
+" Comments and strings {{{
+syn region obseComment start=";" end="$" keepend fold contains=obseToDo
+syn region obseString start=/"/ end=/"/ keepend fold contains=obseStringFormatting
+syn match obseStringFormatting "%%" contained
+syn match obseStringFormatting "%a" contained
+syn match obseStringFormatting "%B" contained
+syn match obseStringFormatting "%b" contained
+syn match obseStringFormatting "%c" contained
+syn match obseStringFormatting "%e" contained
+syn match obseStringFormatting "%g" contained
+syn match obseStringFormatting "%i" contained
+syn match obseStringFormatting "%k" contained
+syn match obseStringFormatting "%n" contained
+syn match obseStringFormatting "%p" contained
+syn match obseStringFormatting "%ps" contained
+syn match obseStringFormatting "%pp" contained
+syn match obseStringFormatting "%po" contained
+syn match obseStringFormatting "%q" contained
+syn match obseStringFormatting "%r" contained
+syn match obseStringFormatting "%v" contained
+syn match obseStringFormatting "%x" contained
+syn match obseStringFormatting "%z" contained
+syn match obseStringFormatting "%{" contained
+syn match obseStringFormatting "%}" contained
+syn match obseStringFormatting "%\d*.\d*f" contained
+syn match obseStringFormatting "% \d*.\d*f" contained
+syn match obseStringFormatting "%-\d*.\d*f" contained
+syn match obseStringFormatting "%+\d*.\d*f" contained
+syn match obseStringFormatting "%\d*.\d*e" contained
+syn match obseStringFormatting "%-\d*.\d*e" contained
+syn match obseStringFormatting "% \d*.\d*e" contained
+syn match obseStringFormatting "%+\d*.\d*e" contained
+syn keyword obseToDo contained TODO todo Todo ToDo FIXME fixme NOTE note
+" }}}
+
+
+" Conditionals {{{
+syn match obseCondition "If"
+syn match obseCondition "Eval"
+syn match obseCondition "Return"
+syn match obseCondition "EndIf"
+syn match obseCondition "ElseIf"
+syn match obseCondition "Else"
+" }}}
+
+" Repeat loops {{{
+syn match obseRepeat "Label"
+syn match obseRepeat "GoTo"
+syn match obseRepeat "While"
+syn match obseRepeat "Loop"
+syn match obseRepeat "ForEach"
+syn match obseRepeat "Break"
+syn match obseRepeat "Continue"
+" }}}
+
+" Basic Types {{{
+syn keyword obseTypes array_var float int long ref reference short string_var nextgroup=obseNames skipwhite
+syn keyword obseOtherKey Player player playerRef playerREF PlayerRef PlayerREF
+syn keyword obseScriptName ScriptName scriptname Scriptname scn nextgroup=obseScriptNameRegion skipwhite
+syn keyword obseBlock Begin End
+" }}}
+
+" Fold {{{
+setlocal foldmethod=syntax
+syn cluster obseNoFold contains=obseComment,obseString
+syn region obseFoldIfContainer
+ \ start="^\s*\<if\>"
+ \ end="^\s*\<endif\>"
+ \ keepend extend
+ \ containedin=ALLBUT,@obseNoFold
+ \ contains=ALLBUT,obseScriptName,obseScriptNameRegion
+syn region obseFoldIf
+ \ start="^\s*\<if\>"
+ \ end="^\s*\<endif\>"
+ \ fold
+ \ keepend
+ \ contained containedin=obseFoldIfContainer
+ \ nextgroup=obseFoldElseIf,obseFoldElse
+ \ contains=TOP,NONE
+syn region obseFoldElseIf
+ \ start="^\s*\<elseif\>"
+ \ end="^\s*\<endif\>"
+ \ fold
+ \ keepend
+ \ contained containedin=obseFoldIfContainer
+ \ nextgroup=obseFoldElseIf,obseFoldElse
+ \ contains=TOP
+syn region obseFoldElse
+ \ start="^\s*\<else\>"
+ \ end="^\s*\<endif\>"
+ \ fold
+ \ keepend
+ \ contained containedin=obseFoldIfContainer
+ \ contains=TOP
+syn region obseFoldWhile
+ \ start="^\s*\<while\>"
+ \ end="^\s*\<loop\>"
+ \ fold
+ \ keepend extend
+ \ contains=TOP
+ \ containedin=ALLBUT,@obseNoFold
+" fold for loops
+syn region obseFoldFor
+ \ start="^\s*\<foreach\>"
+ \ end="^\s*\<loop\>"
+ \ fold
+ \ keepend extend
+ \ contains=TOP
+ \ containedin=ALLBUT,@obseNoFold
+ \ nextgroup=obseVariable
+" }}}
+
+" Skills and Attributes {{{
+syn keyword skillAttribute
+ \ Strength
+ \ Willpower
+ \ Speed
+ \ Personality
+ \ Intelligence
+ \ Agility
+ \ Endurance
+ \ Luck
+ \ Armorer
+ \ Athletics
+ \ Blade
+ \ Block
+ \ Blunt
+ \ HandToHand
+ \ HeavyArmor
+ \ Alchemy
+ \ Alteration
+ \ Conjuration
+ \ Destruction
+ \ Illusion
+ \ Mysticism
+ \ Restoration
+ \ Acrobatics
+ \ LightArmor
+ \ Marksman
+ \ Mercantile
+ \ Security
+ \ Sneak
+ \ Speechcraft
+" }}}
+
+" Block Types {{{
+syn keyword obseBlockType
+ \ ExitGame
+ \ ExitToMainMenu
+ \ Function
+ \ GameMode
+ \ LoadGame
+ \ MenuMode
+ \ OnActivate
+ \ OnActorDrop
+ \ OnActorEquip
+ \ OnActorUnequip
+ \ OnAdd
+ \ OnAlarm
+ \ OnAlarmTrespass
+ \ OnAlarmVictim
+ \ OnAttack
+ \ OnBlock
+ \ OnBowAttack
+ \ OnClick
+ \ OnClose
+ \ OnCreatePotion
+ \ OnCreateSpell
+ \ OnDeath
+ \ OnDodge
+ \ OnDrinkPotion
+ \ OnDrop
+ \ OnEatIngredient
+ \ OnEnchant
+ \ OnEquip
+ \ OnFallImpact
+ \ OnHealthDamage
+ \ OnHit
+ \ OnHitWith
+ \ OnKnockout
+ \ OnLoad
+ \ OnMagicApply
+ \ OnMagicCast
+ \ OnMagicEffectHit
+ \ OnMagicEffectHit2
+ \ OnMapMarkerAdd
+ \ OnMouseover
+ \ OnMurder
+ \ OnNewGame
+ \ OnOpen
+ \ OnPackageChange
+ \ OnPackageDone
+ \ OnPackageStart
+ \ OnQuestComplete
+ \ OnRecoil
+ \ OnRelease
+ \ OnReset
+ \ OnSaveIni
+ \ OnScriptedSkillUp
+ \ OnScrollCast
+ \ OnSell
+ \ OnSkillUp
+ \ OnSoulTrap
+ \ OnSpellCast
+ \ OnStagger
+ \ OnStartCombat
+ \ OnTrigger
+ \ OnTriggerActor
+ \ OnTriggerMob
+ \ OnUnequip
+ \ OnVampireFeed
+ \ OnWaterDive
+ \ OnWaterSurface
+ \ PostLoadGame
+ \ QQQ
+ \ SaveGame
+ \ ScriptEffectFinish
+ \ ScriptEffectStart
+ \ ScriptEffectUpdate
+" }}}
+
+" Functions {{{
+" CS functions {{{
+syn keyword csFunction
+ \ Activate
+ \ AddAchievement
+ \ AddFlames
+ \ AddItem
+ \ AddScriptPackage
+ \ AddSpell
+ \ AddTopic
+ \ AdvSkill
+ \ AdvancePCLevel
+ \ AdvancePCSkill
+ \ Autosave
+ \ CanHaveFlames
+ \ CanPayCrimeGold
+ \ Cast
+ \ ClearOwnership
+ \ CloseCurrentOblivionGate
+ \ CloseOblivionGate
+ \ CompleteQuest
+ \ CreateFullActorCopy
+ \ DeleteFullActorCopy
+ \ Disable
+ \ DisableLinkedPathPoints
+ \ DisablePlayerControls
+ \ Dispel
+ \ DispelAllSpells
+ \ Drop
+ \ DropMe
+ \ DuplicateAllItems
+ \ DuplicateNPCStats
+ \ Enable
+ \ EnableFastTravel
+ \ EnableLinkedPathPoints
+ \ EnablePlayerControls
+ \ EquipItem
+ \ EssentialDeathReload
+ \ EvaluatePackage
+ \ ForceAV
+ \ ForceActorValue
+ \ ForceCloseOblivionGate
+ \ ForceFlee
+ \ ForceTakeCover
+ \ ForceWeather
+ \ GetAV
+ \ GetActionRef
+ \ GetActorValue
+ \ GetAlarmed
+ \ GetAmountSoldStolen
+ \ GetAngle
+ \ GetArmorRating
+ \ GetArmorRatingUpperBody
+ \ GetAttacked
+ \ GetBarterGold
+ \ GetBaseAV
+ \ GetBaseActorValue
+ \ GetButtonPressed
+ \ GetClassDefaultMatch
+ \ GetClothingValue
+ \ GetContainer
+ \ GetCrime
+ \ GetCrimeGold
+ \ GetCrimeKnown
+ \ GetCurrentAIPackage
+ \ GetCurrentAIProcedure
+ \ GetCurrentTime
+ \ GetCurrentWeatherPercent
+ \ GetDayOfWeek
+ \ GetDead
+ \ GetDeadCount
+ \ GetDestroyed
+ \ GetDetected
+ \ GetDetectionLevel
+ \ GetDisabled
+ \ GetDisposition
+ \ GetDistance
+ \ GetDoorDefaultOpen
+ \ GetEquipped
+ \ GetFactionRank
+ \ GetFactionRankDifference
+ \ GetFactionReaction
+ \ GetFatiguePercentage
+ \ GetForceRun
+ \ GetForceSneak
+ \ GetFriendHit
+ \ GetFurnitureMarkerID
+ \ GetGS
+ \ GetGameSetting
+ \ GetGlobalValue
+ \ GetGold
+ \ GetHeadingAngle
+ \ GetIdleDoneOnce
+ \ GetIgnoreFriendlyHits
+ \ GetInCell
+ \ GetInCellParam
+ \ GetInFaction
+ \ GetInSameCell
+ \ GetInWorldspace
+ \ GetInvestmentGold
+ \ GetIsAlerted
+ \ GetIsClass
+ \ GetIsClassDefault
+ \ GetIsCreature
+ \ GetIsCurrentPackage
+ \ GetIsCurrentWeather
+ \ GetIsGhost
+ \ GetIsID
+ \ GetIsPlayableRace
+ \ GetIsPlayerBirthsign
+ \ GetIsRace
+ \ GetIsReference
+ \ GetIsSex
+ \ GetIsUsedItem
+ \ GetIsUsedItemType
+ \ GetItemCount
+ \ GetKnockedState
+ \ GetLOS
+ \ GetLevel
+ \ GetLockLevel
+ \ GetLocked
+ \ GetMenuHasTrait
+ \ GetName
+ \ GetNoRumors
+ \ GetOffersServicesNow
+ \ GetOpenState
+ \ GetPCExpelled
+ \ GetPCFactionAttack
+ \ GetPCFactionMurder
+ \ GetPCFactionSteal
+ \ GetPCFactionSubmitAuthority
+ \ GetPCFame
+ \ GetPCInFaction
+ \ GetPCInfamy
+ \ GetPCIsClass
+ \ GetPCIsRace
+ \ GetPCIsSex
+ \ GetPCMiscStat
+ \ GetPCSleepHours
+ \ GetPackageTarget
+ \ GetParentRef
+ \ GetPersuasionNumber
+ \ GetPlayerControlsDisabled
+ \ GetPlayerHasLastRiddenHorse
+ \ GetPlayerInSEWorld
+ \ GetPos
+ \ GetQuestRunning
+ \ GetQuestVariable
+ \ GetRandomPercent
+ \ GetRestrained
+ \ GetScale
+ \ GetScriptVariable
+ \ GetSecondsPassed
+ \ GetSelf
+ \ GetShouldAttack
+ \ GetSitting
+ \ GetSleeping
+ \ GetStage
+ \ GetStageDone
+ \ GetStartingAngle
+ \ GetStartingPos
+ \ GetTalkedToPC
+ \ GetTalkedToPCParam
+ \ GetTimeDead
+ \ GetTotalPersuasionNumber
+ \ GetTrespassWarningLevel
+ \ GetUnconscious
+ \ GetUsedItemActivate
+ \ GetUsedItemLevel
+ \ GetVampire
+ \ GetWalkSpeed
+ \ GetWeaponAnimType
+ \ GetWeaponSkillType
+ \ GetWindSpeed
+ \ GoToJail
+ \ HasFlames
+ \ HasMagicEffect
+ \ HasVampireFed
+ \ IsActionRef
+ \ IsActor
+ \ IsActorAVictim
+ \ IsActorDetected
+ \ IsActorEvil
+ \ IsActorUsingATorch
+ \ IsActorsAIOff
+ \ IsAnimPlayer
+ \ IsCellOwner
+ \ IsCloudy
+ \ IsContinuingPackagePCNear
+ \ IsCurrentFurnitureObj
+ \ IsCurrentFurnitureRef
+ \ IsEssential
+ \ IsFacingUp
+ \ IsGuard
+ \ IsHorseStolen
+ \ IsIdlePlaying
+ \ IsInCombat
+ \ IsInDangerousWater
+ \ IsInInterior
+ \ IsInMyOwnedCell
+ \ IsLeftUp
+ \ IsOwner
+ \ IsPCAMurderer
+ \ IsPCSleeping
+ \ IsPlayerInJail
+ \ IsPlayerMovingIntoNewSpace
+ \ IsPlayersLastRiddenHorse
+ \ IsPleasant
+ \ IsRaining
+ \ IsRidingHorse
+ \ IsRunning
+ \ IsShieldOut
+ \ IsSneaking
+ \ IsSnowing
+ \ IsSpellTarget
+ \ IsSwimming
+ \ IsTalking
+ \ IsTimePassing
+ \ IsTorchOut
+ \ IsTrespassing
+ \ IsTurnArrest
+ \ IsWaiting
+ \ IsWeaponOut
+ \ IsXBox
+ \ IsYielding
+ \ Kill
+ \ KillActor
+ \ KillAllActors
+ \ Lock
+ \ Look
+ \ LoopGroup
+ \ Message
+ \ MessageBox
+ \ ModAV
+ \ ModActorValue
+ \ ModAmountSoldStolen
+ \ ModBarterGold
+ \ ModCrimeGold
+ \ ModDisposition
+ \ ModFactionRank
+ \ ModFactionReaction
+ \ ModPCAttribute
+ \ ModPCA
+ \ ModPCFame
+ \ ModPCInfamy
+ \ ModPCMiscStat
+ \ ModPCSkill
+ \ ModPCS
+ \ ModScale
+ \ MoveTo
+ \ MoveToMarker
+ \ PCB
+ \ PayFine
+ \ PayFineThief
+ \ PickIdle
+ \ PlaceAtMe
+ \ PlayBink
+ \ PlayGroup
+ \ PlayMagicEffectVisuals
+ \ PlayMagicShaderVisuals
+ \ PlaySound
+ \ PlaySound3D
+ \ PositionCell
+ \ PositionWorld
+ \ PreloadMagicEffect
+ \ PurgeCellBuffers
+ \ PushActorAway
+ \ RefreshTopicList
+ \ ReleaseWeatherOverride
+ \ RemoveAllItems
+ \ RemoveFlames
+ \ RemoveItem
+ \ RemoveMe
+ \ RemoveScriptPackage
+ \ RemoveSpell
+ \ Reset3DState
+ \ ResetFallDamageTimer
+ \ ResetHealth
+ \ ResetInterior
+ \ Resurrect
+ \ Rotate
+ \ SCAOnActor
+ \ SameFaction
+ \ SameFactionAsPC
+ \ SameRace
+ \ SameRaceAsPC
+ \ SameSex
+ \ SameSexAsPC
+ \ Say
+ \ SayTo
+ \ ScriptEffectElapsedSeconds
+ \ SelectPlayerSpell
+ \ SendTrespassAlarm
+ \ SetAV
+ \ SetActorAlpha
+ \ SetActorFullName
+ \ SetActorRefraction
+ \ SetActorValue
+ \ SetActorsAI
+ \ SetAlert
+ \ SetAllReachable
+ \ SetAllVisible
+ \ SetAngle
+ \ SetAtStart
+ \ SetBarterGold
+ \ SetCellFullName
+ \ SetCellOwnership
+ \ SetCellPublicFlag
+ \ SetClass
+ \ SetCrimeGold
+ \ SetDestroyed
+ \ SetDoorDefaultOpen
+ \ SetEssential
+ \ SetFactionRank
+ \ SetFactionReaction
+ \ SetForceRun
+ \ SetForceSneak
+ \ SetGhost
+ \ SetIgnoreFriendlyHits
+ \ SetInCharGen
+ \ SetInvestmentGold
+ \ SetItemValue
+ \ SetLevel
+ \ SetNoAvoidance
+ \ SetNoRumors
+ \ SetOpenState
+ \ SetOwnership
+ \ SetPCExpelled
+ \ SetPCFactionAttack
+ \ SetPCFactionMurder
+ \ SetPCFactionSteal
+ \ SetPCFactionSubmitAuthority
+ \ SetPCFame
+ \ SetPCInfamy
+ \ SetPCSleepHours
+ \ SetPackDuration
+ \ SetPlayerBirthsign
+ \ SetPlayerInSEWorld
+ \ SetPos
+ \ SetQuestObject
+ \ SetRestrained
+ \ SetRigidBodyMass
+ \ SetScale
+ \ SetSceneIsComplex
+ \ SetShowQuestItems
+ \ SetSize
+ \ SetStage
+ \ SetUnconscious
+ \ SetWeather
+ \ ShowBirthsignMenu
+ \ ShowClassMenu
+ \ ShowDialogSubtitles
+ \ ShowEnchantment
+ \ ShowMap
+ \ ShowRaceMenu
+ \ ShowSpellMaking
+ \ SkipAnim
+ \ StartCombat
+ \ StartConversation
+ \ StartQuest
+ \ StopCombat
+ \ StopCombatAlarmOnActor
+ \ StopLook
+ \ StopMagicEffectVisuals
+ \ StopMagicShaderVisuals
+ \ StopQuest
+ \ StopWaiting
+ \ StreamMusic
+ \ This
+ \ ToggleActorsAI
+ \ TrapUpdate
+ \ TriggerHitShader
+ \ UnequipItem
+ \ Unlock
+ \ VampireFeed
+ \ Wait
+ \ WakeUpPC
+ \ WhichServiceMenu
+ \ Yield
+ \ evp
+ \ pms
+ \ saa
+ \ sms
+" }}}
+
+" OBSE Functions {{{
+syn keyword obseFunction
+ \ abs
+ \ acos
+ \ activate2
+ \ actorvaluetocode
+ \ actorvaluetostring
+ \ actorvaluetostringc
+ \ addeffectitem
+ \ addeffectitemc
+ \ addfulleffectitem
+ \ addfulleffectitemc
+ \ additemns
+ \ addmagiceffectcounter
+ \ addmagiceffectcounterc
+ \ addmecounter
+ \ addmecounterc
+ \ addspellns
+ \ addtoleveledlist
+ \ ahammerkey
+ \ animpathincludes
+ \ appendtoname
+ \ asciitochar
+ \ asin
+ \ atan
+ \ atan2
+ \ avstring
+ \ calcleveleditem
+ \ calclevitemnr
+ \ calclevitems
+ \ cancastpower
+ \ cancorpsecheck
+ \ canfasttravelfromworld
+ \ cantraveltomapmarker
+ \ ceil
+ \ chartoascii
+ \ clearactivequest
+ \ clearhotkey
+ \ clearleveledlist
+ \ clearownershipt
+ \ clearplayerslastriddenhorse
+ \ clickmenubutton
+ \ cloneform
+ \ closeallmenus
+ \ closetextinput
+ \ colvec
+ \ comparefemalebipedpath
+ \ comparefemalegroundpath
+ \ comparefemaleiconpath
+ \ compareiconpath
+ \ comparemalebipedpath
+ \ comparemalegroundpath
+ \ comparemaleiconpath
+ \ comparemodelpath
+ \ comparename
+ \ comparenames
+ \ comparescripts
+ \ con_cal
+ \ con_getinisetting
+ \ con_hairtint
+ \ con_loadgame
+ \ con_modwatershader
+ \ con_playerspellbook
+ \ con_quitgame
+ \ con_refreshini
+ \ con_runmemorypass
+ \ con_save
+ \ con_saveini
+ \ con_setcamerafov
+ \ con_setclipdist
+ \ con_setfog
+ \ con_setgamesetting
+ \ con_setgamma
+ \ con_sethdrparam
+ \ con_setimagespaceglow
+ \ con_setinisetting
+ \ con_setskyparam
+ \ con_settargetrefraction
+ \ con_settargetrefractionfire
+ \ con_sexchange
+ \ con_tcl
+ \ con_tfc
+ \ con_tgm
+ \ con_toggleai
+ \ con_togglecombatai
+ \ con_toggledetection
+ \ con_togglemapmarkers
+ \ con_togglemenus
+ \ con_waterdeepcolor
+ \ con_waterreflectioncolor
+ \ con_watershallowcolor
+ \ copyalleffectitems
+ \ copyeyes
+ \ copyfemalebipedpath
+ \ copyfemalegroundpath
+ \ copyfemaleiconpath
+ \ copyhair
+ \ copyiconpath
+ \ copyir
+ \ copymalebipedpath
+ \ copymalegroundpath
+ \ copymaleiconpath
+ \ copymodelpath
+ \ copyname
+ \ copyntheffectitem
+ \ copyrace
+ \ cos
+ \ cosh
+ \ createtempref
+ \ creaturehasnohead
+ \ creaturehasnoleftarm
+ \ creaturehasnomovement
+ \ creaturehasnorightarm
+ \ creaturenocombatinwater
+ \ creatureusesweaponandshield
+ \ dacos
+ \ dasin
+ \ datan
+ \ datan2
+ \ dcos
+ \ dcosh
+ \ debugprint
+ \ deletefrominputtext
+ \ deletereference
+ \ disablecontrol
+ \ disablekey
+ \ disablemouse
+ \ dispatchevent
+ \ dispelnthactiveeffect
+ \ dispelnthae
+ \ dsin
+ \ dsinh
+ \ dtan
+ \ dtanh
+ \ enablecontrol
+ \ enablekey
+ \ enablemouse
+ \ equipitem2
+ \ equipitem2ns
+ \ equipitemns
+ \ equipitemsilent
+ \ equipme
+ \ eval
+ \ evaluatepackage
+ \ eventhandlerexist
+ \ exp
+ \ factionhasspecialcombat
+ \ fileexists
+ \ floor
+ \ fmod
+ \ forcecolumnvector
+ \ forcerowvector
+ \ generateidentitymatrix
+ \ generaterotationmatrix
+ \ generatezeromatrix
+ \ getactiveeffectcasters
+ \ getactiveeffectcodes
+ \ getactiveeffectcount
+ \ getactivemenucomponentid
+ \ getactivemenufilter
+ \ getactivemenumode
+ \ getactivemenuobject
+ \ getactivemenuref
+ \ getactivemenuselection
+ \ getactivequest
+ \ getactiveuicomponentfullname
+ \ getactiveuicomponentid
+ \ getactiveuicomponentname
+ \ getactoralpha
+ \ getactorbaselevel
+ \ getactorlightamount
+ \ getactormaxlevel
+ \ getactormaxswimbreath
+ \ getactorminlevel
+ \ getactorpackages
+ \ getactorsoullevel
+ \ getactorvaluec
+ \ getalchmenuapparatus
+ \ getalchmenuingredient
+ \ getalchmenuingredientcount
+ \ getallies
+ \ getallmodlocaldata
+ \ getaltcontrol2
+ \ getapbowench
+ \ getapench
+ \ getapparatustype
+ \ getappoison
+ \ getarmorar
+ \ getarmortype
+ \ getarrayvariable
+ \ getarrowprojectilebowenchantment
+ \ getarrowprojectileenchantment
+ \ getarrowprojectilepoison
+ \ getattackdamage
+ \ getavc
+ \ getavforbaseactor
+ \ getavforbaseactorc
+ \ getavmod
+ \ getavmodc
+ \ getavskillmastery
+ \ getavskillmasteryc
+ \ getbarteritem
+ \ getbarteritemquantity
+ \ getbaseactorvaluec
+ \ getbaseav2
+ \ getbaseav2c
+ \ getbaseav3
+ \ getbaseav3c
+ \ getbaseitems
+ \ getbaseobject
+ \ getbipediconpath
+ \ getbipedmodelpath
+ \ getbipedslotmask
+ \ getbirthsignspells
+ \ getbookcantbetaken
+ \ getbookisscroll
+ \ getbooklength
+ \ getbookskilltaught
+ \ getbooktext
+ \ getboundingbox
+ \ getboundingradius
+ \ getcalcalllevels
+ \ getcalceachincount
+ \ getcallingscript
+ \ getcellbehavesasexterior
+ \ getcellchanged
+ \ getcellclimate
+ \ getcelldetachtime
+ \ getcellfactionrank
+ \ getcelllighting
+ \ getcellmusictype
+ \ getcellnorthrotation
+ \ getcellresethours
+ \ getcellwatertype
+ \ getchancenone
+ \ getclass
+ \ getclassattribute
+ \ getclassmenuhighlightedclass
+ \ getclassmenuselectedclass
+ \ getclassskill
+ \ getclassskills
+ \ getclassspecialization
+ \ getclimatehasmasser
+ \ getclimatehassecunda
+ \ getclimatemoonphaselength
+ \ getclimatesunrisebegin
+ \ getclimatesunriseend
+ \ getclimatesunsetbegin
+ \ getclimatesunsetend
+ \ getclimatevolatility
+ \ getclosesound
+ \ getcloudspeedlower
+ \ getcloudspeedupper
+ \ getcombatspells
+ \ getcombatstyle
+ \ getcombatstyleacrobaticsdodgechance
+ \ getcombatstyleattackchance
+ \ getcombatstyleattackduringblockmult
+ \ getcombatstyleattacknotunderattackmult
+ \ getcombatstyleattackskillmodbase
+ \ getcombatstyleattackskillmodmult
+ \ getcombatstyleattackunderattackmult
+ \ getcombatstyleblockchance
+ \ getcombatstyleblocknotunderattackmult
+ \ getcombatstyleblockskillmodbase
+ \ getcombatstyleblockskillmodmult
+ \ getcombatstyleblockunderattackmult
+ \ getcombatstylebuffstandoffdist
+ \ getcombatstyledodgebacknotunderattackmult
+ \ getcombatstyledodgebacktimermax
+ \ getcombatstyledodgebacktimermin
+ \ getcombatstyledodgebackunderattackmult
+ \ getcombatstyledodgechance
+ \ getcombatstyledodgefatiguemodbase
+ \ getcombatstyledodgefatiguemodmult
+ \ getcombatstyledodgefwattackingmult
+ \ getcombatstyledodgefwnotattackingmult
+ \ getcombatstyledodgefwtimermax
+ \ getcombatstyledodgefwtimermin
+ \ getcombatstyledodgelrchance
+ \ getcombatstyledodgelrtimermax
+ \ getcombatstyledodgelrtimermin
+ \ getcombatstyledodgenotunderattackmult
+ \ getcombatstyledodgeunderattackmult
+ \ getcombatstyleencumberedspeedmodbase
+ \ getcombatstyleencumberedspeedmodmult
+ \ getcombatstylefleeingdisabled
+ \ getcombatstylegroupstandoffdist
+ \ getcombatstyleh2hbonustoattack
+ \ getcombatstyleholdtimermax
+ \ getcombatstyleholdtimermin
+ \ getcombatstyleidletimermax
+ \ getcombatstyleidletimermin
+ \ getcombatstyleignorealliesinarea
+ \ getcombatstylekobonustoattack
+ \ getcombatstylekobonustopowerattack
+ \ getcombatstylemeleealertok
+ \ getcombatstylepowerattackchance
+ \ getcombatstylepowerattackfatiguemodbase
+ \ getcombatstylepowerattackfatiguemodmult
+ \ getcombatstyleprefersranged
+ \ getcombatstylerangedstandoffdist
+ \ getcombatstylerangemaxmult
+ \ getcombatstylerangeoptimalmult
+ \ getcombatstylerejectsyields
+ \ getcombatstylerushattackchance
+ \ getcombatstylerushattackdistmult
+ \ getcombatstylestaggerbonustoattack
+ \ getcombatstylestaggerbonustopowerattack
+ \ getcombatstyleswitchdistmelee
+ \ getcombatstyleswitchdistranged
+ \ getcombatstylewillyield
+ \ getcombattarget
+ \ getcompletedquests
+ \ getcontainermenuview
+ \ getcontainerrespawns
+ \ getcontrol
+ \ getcreaturebasescale
+ \ getcreaturecombatskill
+ \ getcreatureflies
+ \ getcreaturemagicskill
+ \ getcreaturemodelpaths
+ \ getcreaturereach
+ \ getcreaturesoullevel
+ \ getcreaturesound
+ \ getcreaturesoundbase
+ \ getcreaturestealthskill
+ \ getcreatureswims
+ \ getcreaturetype
+ \ getcreaturewalks
+ \ getcrosshairref
+ \ getcurrentcharge
+ \ getcurrentclimateid
+ \ getcurrenteditorpackage
+ \ getcurrenteventname
+ \ getcurrenthealth
+ \ getcurrentpackage
+ \ getcurrentpackageprocedure
+ \ getcurrentquests
+ \ getcurrentregion
+ \ getcurrentregions
+ \ getcurrentscript
+ \ getcurrentsoullevel
+ \ getcurrentweatherid
+ \ getcursorpos
+ \ getdebugselection
+ \ getdescription
+ \ getdoorteleportrot
+ \ getdoorteleportx
+ \ getdoorteleporty
+ \ getdoorteleportz
+ \ geteditorid
+ \ geteditorsize
+ \ getenchantment
+ \ getenchantmentcharge
+ \ getenchantmentcost
+ \ getenchantmenttype
+ \ getenchmenubaseitem
+ \ getenchmenuenchitem
+ \ getenchmenusoulgem
+ \ getequipmentslot
+ \ getequipmentslotmask
+ \ getequippedcurrentcharge
+ \ getequippedcurrenthealth
+ \ getequippeditems
+ \ getequippedobject
+ \ getequippedtorchtimeleft
+ \ getequippedweaponpoison
+ \ geteyes
+ \ getfactions
+ \ getfalltimer
+ \ getfirstref
+ \ getfirstrefincell
+ \ getfogdayfar
+ \ getfogdaynear
+ \ getfognightfar
+ \ getfognightnear
+ \ getfollowers
+ \ getformfrommod
+ \ getformidstring
+ \ getfps
+ \ getfullgoldvalue
+ \ getgamedifficulty
+ \ getgameloaded
+ \ getgamerestarted
+ \ getgodmode
+ \ getgoldvalue
+ \ getgridstoload
+ \ getgroundsurfacematerial
+ \ gethair
+ \ gethaircolor
+ \ gethdrvalue
+ \ gethidesamulet
+ \ gethidesrings
+ \ gethighactors
+ \ gethorse
+ \ gethotkeyitem
+ \ geticonpath
+ \ getignoresresistance
+ \ getingredient
+ \ getingredientchance
+ \ getinputtext
+ \ getinventoryobject
+ \ getinvrefsforitem
+ \ getitems
+ \ getkeyname
+ \ getkeypress
+ \ getlastcreatedpotion
+ \ getlastcreatedspell
+ \ getlastenchanteditem
+ \ getlastsigilstonecreateditem
+ \ getlastsigilstoneenchanteditem
+ \ getlastss
+ \ getlastsscreated
+ \ getlastssitem
+ \ getlasttransactionitem
+ \ getlasttransactionquantity
+ \ getlastuniquecreatedpotion
+ \ getlastusedsigilstone
+ \ getlevcreaturetemplate
+ \ getleveledspells
+ \ getlevitembylevel
+ \ getlevitemindexbyform
+ \ getlevitemindexbylevel
+ \ getlightduration
+ \ getlightningfrequency
+ \ getlightradius
+ \ getlightrgb
+ \ getlinkeddoor
+ \ getloadedtypearray
+ \ getlocalgravity
+ \ getloopsound
+ \ getlowactors
+ \ getluckmodifiedskill
+ \ getmagiceffectareasound
+ \ getmagiceffectareasoundc
+ \ getmagiceffectbarterfactor
+ \ getmagiceffectbarterfactorc
+ \ getmagiceffectbasecost
+ \ getmagiceffectbasecostc
+ \ getmagiceffectboltsound
+ \ getmagiceffectboltsoundc
+ \ getmagiceffectcastingsound
+ \ getmagiceffectcastingsoundc
+ \ getmagiceffectchars
+ \ getmagiceffectcharsc
+ \ getmagiceffectcode
+ \ getmagiceffectcounters
+ \ getmagiceffectcountersc
+ \ getmagiceffectenchantfactor
+ \ getmagiceffectenchantfactorc
+ \ getmagiceffectenchantshader
+ \ getmagiceffectenchantshaderc
+ \ getmagiceffecthitshader
+ \ getmagiceffecthitshaderc
+ \ getmagiceffecthitsound
+ \ getmagiceffecthitsoundc
+ \ getmagiceffecticon
+ \ getmagiceffecticonc
+ \ getmagiceffectlight
+ \ getmagiceffectlightc
+ \ getmagiceffectmodel
+ \ getmagiceffectmodelc
+ \ getmagiceffectname
+ \ getmagiceffectnamec
+ \ getmagiceffectnumcounters
+ \ getmagiceffectnumcountersc
+ \ getmagiceffectotheractorvalue
+ \ getmagiceffectotheractorvaluec
+ \ getmagiceffectprojectilespeed
+ \ getmagiceffectprojectilespeedc
+ \ getmagiceffectresistvalue
+ \ getmagiceffectresistvaluec
+ \ getmagiceffectschool
+ \ getmagiceffectschoolc
+ \ getmagiceffectusedobject
+ \ getmagiceffectusedobjectc
+ \ getmagicitemeffectcount
+ \ getmagicitemtype
+ \ getmagicprojectilespell
+ \ getmapmarkers
+ \ getmapmarkertype
+ \ getmapmenumarkername
+ \ getmapmenumarkerref
+ \ getmaxav
+ \ getmaxavc
+ \ getmaxlevel
+ \ getmeareasound
+ \ getmeareasoundc
+ \ getmebarterc
+ \ getmebasecost
+ \ getmebasecostc
+ \ getmeboltsound
+ \ getmeboltsoundc
+ \ getmecastingsound
+ \ getmecastingsoundc
+ \ getmecounters
+ \ getmecountersc
+ \ getmeebarter
+ \ getmeebarterc
+ \ getmeenchant
+ \ getmeenchantc
+ \ getmeenchantshader
+ \ getmeenchantshaderc
+ \ getmehitshader
+ \ getmehitshaderc
+ \ getmehitsound
+ \ getmehitsoundc
+ \ getmeicon
+ \ getmeiconc
+ \ getmelight
+ \ getmelightc
+ \ getmemodel
+ \ getmemodelc
+ \ getmename
+ \ getmenamec
+ \ getmenufloatvalue
+ \ getmenumcounters
+ \ getmenumcountersc
+ \ getmenustringvalue
+ \ getmeotheractorvalue
+ \ getmeotheractorvaluec
+ \ getmeprojspeed
+ \ getmeprojspeedc
+ \ getmerchantcontainer
+ \ getmeresistvalue
+ \ getmeresistvaluec
+ \ getmeschool
+ \ getmeschoolc
+ \ getmessageboxtype
+ \ getmeusedobject
+ \ getmeusedobjectc
+ \ getmiddlehighactors
+ \ getmieffectcount
+ \ getminlevel
+ \ getmitype
+ \ getmodelpath
+ \ getmodindex
+ \ getmodlocaldata
+ \ getmousebuttonpress
+ \ getmousebuttonsswapped
+ \ getmpspell
+ \ getnextref
+ \ getnthacitveeffectmagnitude
+ \ getnthactiveeffectactorvalue
+ \ getnthactiveeffectbounditem
+ \ getnthactiveeffectcaster
+ \ getnthactiveeffectcode
+ \ getnthactiveeffectdata
+ \ getnthactiveeffectduration
+ \ getnthactiveeffectenchantobject
+ \ getnthactiveeffectmagicenchantobject
+ \ getnthactiveeffectmagicitem
+ \ getnthactiveeffectmagicitemindex
+ \ getnthactiveeffectmagnitude
+ \ getnthactiveeffectsummonref
+ \ getnthactiveeffecttimeelapsed
+ \ getnthaeav
+ \ getnthaebounditem
+ \ getnthaecaster
+ \ getnthaecode
+ \ getnthaedata
+ \ getnthaeduration
+ \ getnthaeindex
+ \ getnthaemagicenchantobject
+ \ getnthaemagicitem
+ \ getnthaemagnitude
+ \ getnthaesummonref
+ \ getnthaetime
+ \ getnthchildref
+ \ getnthdetectedactor
+ \ getntheffectitem
+ \ getntheffectitemactorvalue
+ \ getntheffectitemarea
+ \ getntheffectitemcode
+ \ getntheffectitemduration
+ \ getntheffectitemmagnitude
+ \ getntheffectitemname
+ \ getntheffectitemrange
+ \ getntheffectitemscript
+ \ getntheffectitemscriptname
+ \ getntheffectitemscriptschool
+ \ getntheffectitemscriptvisualeffect
+ \ getntheiarea
+ \ getntheiav
+ \ getntheicode
+ \ getntheiduration
+ \ getntheimagnitude
+ \ getntheiname
+ \ getntheirange
+ \ getntheiscript
+ \ getntheisschool
+ \ getntheisvisualeffect
+ \ getnthexplicitref
+ \ getnthfaction
+ \ getnthfactionrankname
+ \ getnthfollower
+ \ getnthlevitem
+ \ getnthlevitemcount
+ \ getnthlevitemlevel
+ \ getnthmagiceffectcounter
+ \ getnthmagiceffectcounterc
+ \ getnthmecounter
+ \ getnthmecounterc
+ \ getnthmodname
+ \ getnthpackage
+ \ getnthplayerspell
+ \ getnthracebonusskill
+ \ getnthracespell
+ \ getnthspell
+ \ getnumchildrefs
+ \ getnumdetectedactors
+ \ getnumericinisetting
+ \ getnumexplicitrefs
+ \ getnumfactions
+ \ getnumfollowers
+ \ getnumitems
+ \ getnumkeyspressed
+ \ getnumlevitems
+ \ getnumloadedmods
+ \ getnumloadedplugins
+ \ getnummousebuttonspressed
+ \ getnumpackages
+ \ getnumranks
+ \ getnumrefs
+ \ getnumrefsincell
+ \ getobjectcharge
+ \ getobjecthealth
+ \ getobjecttype
+ \ getobliviondirectory
+ \ getoblrevision
+ \ getoblversion
+ \ getopenkey
+ \ getopensound
+ \ getowner
+ \ getowningfactionrank
+ \ getowningfactionrequiredrank
+ \ getpackageallowfalls
+ \ getpackageallowswimming
+ \ getpackagealwaysrun
+ \ getpackagealwayssneak
+ \ getpackagearmorunequipped
+ \ getpackagecontinueifpcnear
+ \ getpackagedata
+ \ getpackagedefensivecombat
+ \ getpackagelocationdata
+ \ getpackagelockdoorsatend
+ \ getpackagelockdoorsatlocation
+ \ getpackagelockdoorsatstart
+ \ getpackagemustcomplete
+ \ getpackagemustreachlocation
+ \ getpackagenoidleanims
+ \ getpackageoffersservices
+ \ getpackageonceperday
+ \ getpackagescheduledata
+ \ getpackageskipfalloutbehavior
+ \ getpackagetargetdata
+ \ getpackageunlockdoorsatend
+ \ getpackageunlockdoorsatlocation
+ \ getpackageunlockdoorsatstart
+ \ getpackageusehorse
+ \ getpackageweaponsunequipped
+ \ getparentcell
+ \ getparentcellowner
+ \ getparentcellowningfactionrank
+ \ getparentcellowningfactionrequiredrank
+ \ getparentcellwaterheight
+ \ getparentworldspace
+ \ getpathnodelinkedref
+ \ getpathnodepos
+ \ getpathnodesinradius
+ \ getpathnodesinrect
+ \ getpcattributebonus
+ \ getpcattributebonusc
+ \ getpclastdroppeditem
+ \ getpclastdroppeditemref
+ \ getpclasthorse
+ \ getpclastloaddoor
+ \ getpcmajorskillups
+ \ getpcmovementspeedmodifier
+ \ getpcspelleffectivenessmodifier
+ \ getpctrainingsessionsused
+ \ getplayerbirthsign
+ \ getplayerskilladvances
+ \ getplayerskilladvancesc
+ \ getplayerskilluse
+ \ getplayerskillusec
+ \ getplayerslastactivatedloaddoor
+ \ getplayerslastriddenhorse
+ \ getplayerspell
+ \ getplayerspellcount
+ \ getpluginversion
+ \ getplyerspellcount
+ \ getprocesslevel
+ \ getprojectile
+ \ getprojectiledistancetraveled
+ \ getprojectilelifetime
+ \ getprojectilesource
+ \ getprojectilespeed
+ \ getprojectiletype
+ \ getqmcurrent
+ \ getqmitem
+ \ getqmmaximum
+ \ getqr
+ \ getquality
+ \ getquantitymenucurrentquantity
+ \ getquantitymenuitem
+ \ getquantitymenumaximumquantity
+ \ getrace
+ \ getraceattribute
+ \ getraceattributec
+ \ getracedefaulthair
+ \ getraceeyes
+ \ getracehairs
+ \ getracereaction
+ \ getracescale
+ \ getraceskillbonus
+ \ getraceskillbonusc
+ \ getracespellcount
+ \ getracevoice
+ \ getraceweight
+ \ getrawformidstring
+ \ getrefcount
+ \ getrefvariable
+ \ getrequiredskillexp
+ \ getrequiredskillexpc
+ \ getrider
+ \ getscript
+ \ getscriptactiveeffectindex
+ \ getselectedspells
+ \ getservicesmask
+ \ getsigilstoneuses
+ \ getskillgoverningattribute
+ \ getskillgoverningattributec
+ \ getskillspecialization
+ \ getskillspecializationc
+ \ getskilluseincrement
+ \ getskilluseincrementc
+ \ getsoulgemcapacity
+ \ getsoullevel
+ \ getsoundattenuation
+ \ getsoundplaying
+ \ getsourcemodindex
+ \ getspecialanims
+ \ getspellareaeffectignoreslos
+ \ getspellcount
+ \ getspelldisallowabsorbreflect
+ \ getspelleffectiveness
+ \ getspellexplodeswithnotarget
+ \ getspellhostile
+ \ getspellimmunetosilence
+ \ getspellmagickacost
+ \ getspellmasterylevel
+ \ getspellpcstart
+ \ getspells
+ \ getspellschool
+ \ getspellscripteffectalwaysapplies
+ \ getspelltype
+ \ getstageentries
+ \ getstageids
+ \ getstringgamesetting
+ \ getstringinisetting
+ \ getsundamage
+ \ getsunglare
+ \ gettailmodelpath
+ \ gettargets
+ \ gettelekinesisref
+ \ getteleportcell
+ \ getteleportcellname
+ \ getterrainheight
+ \ gettextinputcontrolpressed
+ \ gettextinputcursorpos
+ \ gettexturepath
+ \ gettilechildren
+ \ gettiletraits
+ \ gettimeleft
+ \ gettotalactiveeffectmagnitude
+ \ gettotalactiveeffectmagnitudec
+ \ gettotalaeabilitymagnitude
+ \ gettotalaeabilitymagnitudec
+ \ gettotalaealchemymagnitude
+ \ gettotalaealchemymagnitudec
+ \ gettotalaeallspellsmagnitude
+ \ gettotalaeallspellsmagnitudec
+ \ gettotalaediseasemagnitude
+ \ gettotalaediseasemagnitudec
+ \ gettotalaeenchantmentmagnitude
+ \ gettotalaeenchantmentmagnitudec
+ \ gettotalaelesserpowermagnitude
+ \ gettotalaelesserpowermagnitudec
+ \ gettotalaemagnitude
+ \ gettotalaemagnitudec
+ \ gettotalaenonabilitymagnitude
+ \ gettotalaenonabilitymagnitudec
+ \ gettotalaepowermagnitude
+ \ gettotalaepowermagnitudec
+ \ gettotalaespellmagnitude
+ \ gettotalaespellmagnitudec
+ \ gettotalpcattributebonus
+ \ gettrainerlevel
+ \ gettrainerskill
+ \ gettransactioninfo
+ \ gettransdelta
+ \ gettravelhorse
+ \ getusedpowers
+ \ getusertime
+ \ getvariable
+ \ getvelocity
+ \ getverticalvelocity
+ \ getwaterheight
+ \ getwatershader
+ \ getweahtercloudspeedupper
+ \ getweaponreach
+ \ getweaponspeed
+ \ getweapontype
+ \ getweatherclassification
+ \ getweathercloudspeedlower
+ \ getweathercloudspeedupper
+ \ getweathercolor
+ \ getweatherfogdayfar
+ \ getweatherfogdaynear
+ \ getweatherfognightfar
+ \ getweatherfognightnear
+ \ getweatherhdrvalue
+ \ getweatherlightningfrequency
+ \ getweatheroverride
+ \ getweathersundamage
+ \ getweathersunglare
+ \ getweathertransdelta
+ \ getweatherwindspeed
+ \ getweight
+ \ getworldparentworld
+ \ getworldspaceparentworldspace
+ \ globalvariableexists
+ \ hammerkey
+ \ hasbeenpickedup
+ \ haseffectshader
+ \ haslowlevelprocessing
+ \ hasmodel
+ \ hasname
+ \ hasnopersuasion
+ \ hasspell
+ \ hastail
+ \ hasvariable
+ \ haswater
+ \ holdkey
+ \ iconpathincludes
+ \ identitymat
+ \ incrementplayerskilluse
+ \ incrementplayerskillusec
+ \ ininvertfasttravel
+ \ insertininputtext
+ \ isactivatable
+ \ isactivator
+ \ isactorrespawning
+ \ isalchemyitem
+ \ isammo
+ \ isanimgroupplaying
+ \ isanimplaying
+ \ isapparatus
+ \ isarmor
+ \ isattacking
+ \ isautomaticdoor
+ \ isbartermenuactive
+ \ isbipediconpathvalid
+ \ isbipedmodelpathvalid
+ \ isblocking
+ \ isbook
+ \ iscantwait
+ \ iscasting
+ \ iscellpublic
+ \ isclassattribute
+ \ isclassattributec
+ \ isclassskill
+ \ isclassskillc
+ \ isclonedform
+ \ isclothing
+ \ isconsoleopen
+ \ iscontainer
+ \ iscontrol
+ \ iscontroldisabled
+ \ iscontrolpressed
+ \ iscreature
+ \ iscreaturebiped
+ \ isdigit
+ \ isdiseased
+ \ isdodging
+ \ isdoor
+ \ isequipped
+ \ isfactionevil
+ \ isfactionhidden
+ \ isfemale
+ \ isflora
+ \ isflying
+ \ isfood
+ \ isformvalid
+ \ isfurniture
+ \ isgamemessagebox
+ \ isglobalcollisiondisabled
+ \ isharvested
+ \ ishiddendoor
+ \ isiconpathvalid
+ \ isinair
+ \ isingredient
+ \ isinoblivion
+ \ isjumping
+ \ iskey
+ \ iskeydisabled
+ \ iskeypressed
+ \ iskeypressed2
+ \ iskeypressed3
+ \ isletter
+ \ islight
+ \ islightcarriable
+ \ isloaddoor
+ \ ismagiceffectcanrecover
+ \ ismagiceffectcanrecoverc
+ \ ismagiceffectdetrimental
+ \ ismagiceffectdetrimentalc
+ \ ismagiceffectforenchanting
+ \ ismagiceffectforenchantingc
+ \ ismagiceffectforspellmaking
+ \ ismagiceffectforspellmakingc
+ \ ismagiceffecthostile
+ \ ismagiceffecthostilec
+ \ ismagiceffectmagnitudepercent
+ \ ismagiceffectmagnitudepercentc
+ \ ismagiceffectonselfallowed
+ \ ismagiceffectonselfallowedc
+ \ ismagiceffectontargetallowed
+ \ ismagiceffectontargetallowedc
+ \ ismagiceffectontouchallowed
+ \ ismagiceffectontouchallowedc
+ \ ismagicitemautocalc
+ \ ismajor
+ \ ismajorc
+ \ ismajorref
+ \ ismapmarkervisible
+ \ ismecanrecover
+ \ ismecanrecoverc
+ \ ismedetrimental
+ \ ismedetrimentalc
+ \ ismeforenchanting
+ \ ismeforenchantingc
+ \ ismeforspellmaking
+ \ ismeforspellmakingc
+ \ ismehostile
+ \ ismehostilec
+ \ ismemagnitudepercent
+ \ ismemagnitudepercentc
+ \ ismeonselfallowed
+ \ ismeonselfallowedc
+ \ ismeontargetallowed
+ \ ismeontargetallowedc
+ \ ismeontouchallowed
+ \ ismeontouchallowedc
+ \ isminimalusedoor
+ \ ismiscitem
+ \ ismodelpathvalid
+ \ ismodloaded
+ \ ismovingbackward
+ \ ismovingforward
+ \ ismovingleft
+ \ ismovingright
+ \ isnaked
+ \ isnthactiveeffectapplied
+ \ isntheffectitemscripted
+ \ isntheffectitemscripthostile
+ \ isntheishostile
+ \ isobliviongate
+ \ isoblivioninterior
+ \ isoblivionworld
+ \ isofflimits
+ \ isonground
+ \ ispathnodedisabled
+ \ ispcleveloffset
+ \ ispersistent
+ \ isplayable
+ \ isplayable2
+ \ isplugininstalled
+ \ ispoison
+ \ ispotion
+ \ ispowerattacking
+ \ isprintable
+ \ ispunctuation
+ \ isquestcomplete
+ \ isquestitem
+ \ isracebonusskill
+ \ isracebonusskillc
+ \ israceplayable
+ \ isrecoiling
+ \ isrefdeleted
+ \ isreference
+ \ isrefessential
+ \ isscripted
+ \ issigilstone
+ \ issoulgem
+ \ isspellhostile
+ \ isstaggered
+ \ issummonable
+ \ istaken
+ \ istextinputinuse
+ \ isthirdperson
+ \ isturningleft
+ \ isturningright
+ \ isunderwater
+ \ isunsaferespawns
+ \ isuppercase
+ \ isweapon
+ \ leftshift
+ \ linktodoor
+ \ loadgameex
+ \ log
+ \ log10
+ \ logicaland
+ \ logicalnot
+ \ logicalor
+ \ logicalxor
+ \ magiceffectcodefromchars
+ \ magiceffectfromchars
+ \ magiceffectfromcode
+ \ magiceffectfxpersists
+ \ magiceffectfxpersistsc
+ \ magiceffecthasnoarea
+ \ magiceffecthasnoareac
+ \ magiceffecthasnoduration
+ \ magiceffecthasnodurationc
+ \ magiceffecthasnohiteffect
+ \ magiceffecthasnohiteffectc
+ \ magiceffecthasnoingredient
+ \ magiceffecthasnoingredientc
+ \ magiceffecthasnomagnitude
+ \ magiceffecthasnomagnitudec
+ \ magiceffectusesarmor
+ \ magiceffectusesarmorc
+ \ magiceffectusesattribute
+ \ magiceffectusesattributec
+ \ magiceffectusescreature
+ \ magiceffectusescreaturec
+ \ magiceffectusesotheractorvalue
+ \ magiceffectusesotheractorvaluec
+ \ magiceffectusesskill
+ \ magiceffectusesskillc
+ \ magiceffectusesweapon
+ \ magiceffectusesweaponc
+ \ magichaseffect
+ \ magichaseffectc
+ \ magicitemhaseffect
+ \ magicitemhaseffectcode
+ \ magicitemhaseffectcount
+ \ magicitemhaseffectcountc
+ \ magicitemhaseffectcountcode
+ \ magicitemhaseffectitemscript
+ \ matadd
+ \ matchpotion
+ \ matinv
+ \ matmult
+ \ matrixadd
+ \ matrixdeterminant
+ \ matrixinvert
+ \ matrixmultiply
+ \ matrixrref
+ \ matrixscale
+ \ matrixsubtract
+ \ matrixtrace
+ \ matrixtranspose
+ \ matscale
+ \ matsubtract
+ \ mecodefromchars
+ \ mefxpersists
+ \ mefxpersistsc
+ \ mehasnoarea
+ \ mehasnoareac
+ \ mehasnoduration
+ \ mehasnodurationc
+ \ mehasnohiteffect
+ \ mehasnohiteffectc
+ \ mehasnoingredient
+ \ mehasnoingredientc
+ \ mehasnomagnitude
+ \ mehasnomagnitudec
+ \ menuholdkey
+ \ menumode
+ \ menureleasekey
+ \ menutapkey
+ \ messageboxex
+ \ messageex
+ \ meusesarmor
+ \ meusesarmorc
+ \ meusesattribute
+ \ meusesattributec
+ \ meusescreature
+ \ meusescreaturec
+ \ meusesotheractorvalue
+ \ meusesotheractorvaluec
+ \ meusesskill
+ \ meusesskillc
+ \ meusesweapon
+ \ meusesweaponc
+ \ modactorvalue2
+ \ modactorvaluec
+ \ modarmorar
+ \ modattackdamage
+ \ modav2
+ \ modavc
+ \ modavmod
+ \ modavmodc
+ \ modcurrentcharge
+ \ modelpathincludes
+ \ modenchantmentcharge
+ \ modenchantmentcost
+ \ modequippedcurrentcharge
+ \ modequippedcurrenthealth
+ \ modfemalebipedpath
+ \ modfemalegroundpath
+ \ modfemaleiconpath
+ \ modgoldvalue
+ \ modiconpath
+ \ modlocaldataexists
+ \ modmalebipedpath
+ \ modmalegroundpath
+ \ modmaleiconpath
+ \ modmodelpath
+ \ modname
+ \ modnthactiveeffectmagnitude
+ \ modnthaemagnitude
+ \ modntheffectitemarea
+ \ modntheffectitemduration
+ \ modntheffectitemmagnitude
+ \ modntheffectitemscriptname
+ \ modntheiarea
+ \ modntheiduration
+ \ modntheimagnitude
+ \ modntheisname
+ \ modobjectcharge
+ \ modobjecthealth
+ \ modpcmovementspeed
+ \ modpcspelleffectiveness
+ \ modplayerskillexp
+ \ modplayerskillexpc
+ \ modquality
+ \ modsigilstoneuses
+ \ modspellmagickacost
+ \ modweaponreach
+ \ modweaponspeed
+ \ modweight
+ \ movemousex
+ \ movemousey
+ \ movetextinputcursor
+ \ nameincludes
+ \ numtohex
+ \ offersapparatus
+ \ offersarmor
+ \ offersbooks
+ \ offersclothing
+ \ offersingredients
+ \ offerslights
+ \ offersmagicitems
+ \ offersmiscitems
+ \ offerspotions
+ \ offersrecharging
+ \ offersrepair
+ \ offersservicesc
+ \ offersspells
+ \ offerstraining
+ \ offersweapons
+ \ oncontroldown
+ \ onkeydown
+ \ opentextinput
+ \ outputlocalmappicturesoverride
+ \ overrideactorswimbreath
+ \ parentcellhaswater
+ \ pathedgeexists
+ \ playidle
+ \ pow
+ \ print
+ \ printactivetileinfo
+ \ printc
+ \ printd
+ \ printtileinfo
+ \ printtoconsole
+ \ questexists
+ \ racos
+ \ rand
+ \ rasin
+ \ ratan
+ \ ratan2
+ \ rcos
+ \ rcosh
+ \ refreshcurrentclimate
+ \ releasekey
+ \ removealleffectitems
+ \ removebasespell
+ \ removeenchantment
+ \ removeequippedweaponpoison
+ \ removeeventhandler
+ \ removefromleveledlist
+ \ removeitemns
+ \ removelevitembylevel
+ \ removemeir
+ \ removemodlocaldata
+ \ removentheffect
+ \ removentheffectitem
+ \ removenthlevitem
+ \ removenthmagiceffectcounter
+ \ removenthmagiceffectcounterc
+ \ removenthmecounter
+ \ removenthmecounterc
+ \ removescript
+ \ removescr
+ \ removespellns
+ \ resetallvariables
+ \ resetfalrior
+ \ resolvemodindex
+ \ rightshift
+ \ rotmat
+ \ rowvec
+ \ rsin
+ \ rsinh
+ \ rtan
+ \ rtanh
+ \ runbatchscript
+ \ runscriptline
+ \ saespassalarm
+ \ setactivequest
+ \ setactrfullname
+ \ setactormaxswimbreath
+ \ setactorrespawns
+ \ setactorswimbreath
+ \ setactorvaluec
+ \ setalvisible
+ \ setaltcontrol2
+ \ setapparatustype
+ \ setarmorar
+ \ setarmortype
+ \ setarrowprojectilebowenchantment
+ \ setarrowprojectileenchantment
+ \ setarrowprojectilepoison
+ \ setattackdamage
+ \ setavc
+ \ setavmod
+ \ setavmodc
+ \ setbaseform
+ \ setbipediconpathex
+ \ setbipedmodelpathex
+ \ setbipedslotmask
+ \ setbookcantbetaken
+ \ setbookisscroll
+ \ setbookskilltaught
+ \ setbuttonpressed
+ \ setcalcalllevels
+ \ setcamerafov2
+ \ setcancastpower
+ \ setcancorpsecheck
+ \ setcanfasttravelfromworld
+ \ setcantraveltomapmarker
+ \ setcantwait
+ \ setcellbehavesasexterior
+ \ setcellclimate
+ \ setcellhaswater
+ \ setcellispublic
+ \ setcelllighting
+ \ setcellmusictype
+ \ setcellublicflag
+ \ setcellresethours
+ \ setcellwaterheight
+ \ setcellwatertype
+ \ setchancenone
+ \ setclassattribute
+ \ setclassattributec
+ \ setclassskills
+ \ setclassskills2
+ \ setclassspecialization
+ \ setclimatehasmasser
+ \ setclimatehasmassser
+ \ setclimatehassecunda
+ \ setclimatemoonphaselength
+ \ setclimatesunrisebegin
+ \ setclimatesunriseend
+ \ setclimatesunsetbegin
+ \ setclimatesunsetend
+ \ setclimatevolatility
+ \ setclosesound
+ \ setcloudspeedlower
+ \ setcloudspeedupper
+ \ setcombatstyle
+ \ setcombatstyleacrobaticsdodgechance
+ \ setcombatstyleattackchance
+ \ setcombatstyleattackduringblockmult
+ \ setcombatstyleattacknotunderattackmult
+ \ setcombatstyleattackskillmodbase
+ \ setcombatstyleattackskillmodmult
+ \ setcombatstyleattackunderattackmult
+ \ setcombatstyleblockchance
+ \ setcombatstyleblocknotunderattackmult
+ \ setcombatstyleblockskillmodbase
+ \ setcombatstyleblockskillmodmult
+ \ setcombatstyleblockunderattackmult
+ \ setcombatstylebuffstandoffdist
+ \ setcombatstyledodgebacknotunderattackmult
+ \ setcombatstyledodgebacktimermax
+ \ setcombatstyledodgebacktimermin
+ \ setcombatstyledodgebackunderattackmult
+ \ setcombatstyledodgechance
+ \ setcombatstyledodgefatiguemodbase
+ \ setcombatstyledodgefatiguemodmult
+ \ setcombatstyledodgefwattackingmult
+ \ setcombatstyledodgefwnotattackingmult
+ \ setcombatstyledodgefwtimermax
+ \ setcombatstyledodgefwtimermin
+ \ setcombatstyledodgelrchance
+ \ setcombatstyledodgelrtimermax
+ \ setcombatstyledodgelrtimermin
+ \ setcombatstyledodgenotunderattackmult
+ \ setcombatstyledodgeunderattackmult
+ \ setcombatstyleencumberedspeedmodbase
+ \ setcombatstyleencumberedspeedmodmult
+ \ setcombatstylefleeingdisabled
+ \ setcombatstylegroupstandoffdist
+ \ setcombatstyleh2hbonustoattack
+ \ setcombatstyleholdtimermax
+ \ setcombatstyleholdtimermin
+ \ setcombatstyleidletimermax
+ \ setcombatstyleidletimermin
+ \ setcombatstyleignorealliesinarea
+ \ setcombatstylekobonustoattack
+ \ setcombatstylekobonustopowerattack
+ \ setcombatstylemeleealertok
+ \ setcombatstylepowerattackchance
+ \ setcombatstylepowerattackfatiguemodbase
+ \ setcombatstylepowerattackfatiguemodmult
+ \ setcombatstyleprefersranged
+ \ setcombatstylerangedstandoffdist
+ \ setcombatstylerangemaxmult
+ \ setcombatstylerangeoptimalmult
+ \ setcombatstylerejectsyields
+ \ setcombatstylerushattackchance
+ \ setcombatstylerushattackdistmult
+ \ setcombatstylestaggerbonustoattack
+ \ setcombatstylestaggerbonustopowerattack
+ \ setcombatstyleswitchdistmelee
+ \ setcombatstyleswitchdistranged
+ \ setcombatstylewillyield
+ \ setcontainerrespawns
+ \ setcontrol
+ \ setcreatureskill
+ \ setcreaturesoundbase
+ \ setcreaturetype
+ \ setcurrentcharge
+ \ setcurrenthealth
+ \ setcurrentsoullevel
+ \ setdebugmode
+ \ setdescription
+ \ setdetectionstate
+ \ setdisableglobalcollision
+ \ setdoorteleport
+ \ setenchantment
+ \ setenchantmentcharge
+ \ setenchantmentcost
+ \ setenchantmenttype
+ \ setequipmentslot
+ \ setequippedcurrentcharge
+ \ setequippedcurrenthealth
+ \ setequippedweaponpoison
+ \ seteventhandler
+ \ seteyes
+ \ setfactionevil
+ \ setfactionhasspecialcombat
+ \ setfactionhidden
+ \ setfactonreaction
+ \ setfactionspecialcombat
+ \ setfemale
+ \ setfemalebipedpath
+ \ setfemalegroundpath
+ \ setfemaleiconpath
+ \ setflycameraspeedmult
+ \ setfogdayfar
+ \ setfogdaynear
+ \ setfognightfar
+ \ setfognightnear
+ \ setforcsneak
+ \ setfunctionvalue
+ \ setgamedifficulty
+ \ setgoldvalue
+ \ setgoldvalue_t
+ \ setgoldvaluet
+ \ sethair
+ \ setharvested
+ \ sethasbeenpickedup
+ \ sethdrvalue
+ \ sethidesamulet
+ \ sethidesrings
+ \ sethotkeyitem
+ \ seticonpath
+ \ setignoresresistance
+ \ setingredient
+ \ setingredientchance
+ \ setinputtext
+ \ setinvertfasttravel
+ \ setisautomaticdoor
+ \ setiscontrol
+ \ setisfood
+ \ setishiddendoor
+ \ setisminimalusedoor
+ \ setisobliviongate
+ \ setisplayable
+ \ setlevcreaturetemplate
+ \ setlightduration
+ \ setlightningfrequency
+ \ setlightradius
+ \ setlightrgb
+ \ setlocalgravity
+ \ setlocalgravityvector
+ \ setloopsound
+ \ setlowlevelprocessing
+ \ setmaagiceffectuseactorvalue
+ \ setmagiceffectareasound
+ \ setmagiceffectareasoundc
+ \ setmagiceffectbarterfactor
+ \ setmagiceffectbarterfactorc
+ \ setmagiceffectbasecost
+ \ setmagiceffectbasecostc
+ \ setmagiceffectboltsound
+ \ setmagiceffectboltsoundc
+ \ setmagiceffectcanrecover
+ \ setmagiceffectcanrecoverc
+ \ setmagiceffectcastingsound
+ \ setmagiceffectcastingsoundc
+ \ setmagiceffectcounters
+ \ setmagiceffectcountersc
+ \ setmagiceffectenchantfactor
+ \ setmagiceffectenchantfactorc
+ \ setmagiceffectenchantshader
+ \ setmagiceffectenchantshaderc
+ \ setmagiceffectforenchanting
+ \ setmagiceffectforenchantingc
+ \ setmagiceffectforspellmaking
+ \ setmagiceffectforspellmakingc
+ \ setmagiceffectfxpersists
+ \ setmagiceffectfxpersistsc
+ \ setmagiceffecthitshader
+ \ setmagiceffecthitshaderc
+ \ setmagiceffecthitsound
+ \ setmagiceffecthitsoundc
+ \ setmagiceffecticon
+ \ setmagiceffecticonc
+ \ setmagiceffectisdetrimental
+ \ setmagiceffectisdetrimentalc
+ \ setmagiceffectishostile
+ \ setmagiceffectishostilec
+ \ setmagiceffectlight
+ \ setmagiceffectlightc
+ \ setmagiceffectmagnitudepercent
+ \ setmagiceffectmagnitudepercentc
+ \ setmagiceffectmodel
+ \ setmagiceffectmodelc
+ \ setmagiceffectname
+ \ setmagiceffectnamec
+ \ setmagiceffectnoarea
+ \ setmagiceffectnoareac
+ \ setmagiceffectnoduration
+ \ setmagiceffectnodurationc
+ \ setmagiceffectnohiteffect
+ \ setmagiceffectnohiteffectc
+ \ setmagiceffectnoingredient
+ \ setmagiceffectnoingredientc
+ \ setmagiceffectnomagnitude
+ \ setmagiceffectnomagnitudec
+ \ setmagiceffectonselfallowed
+ \ setmagiceffectonselfallowedc
+ \ setmagiceffectontargetallowed
+ \ setmagiceffectontargetallowedc
+ \ setmagiceffectontouchallowed
+ \ setmagiceffectontouchallowedc
+ \ setmagiceffectotheractorvalue
+ \ setmagiceffectotheractorvaluec
+ \ setmagiceffectprojectilespeed
+ \ setmagiceffectprojectilespeedc
+ \ setmagiceffectresistvalue
+ \ setmagiceffectresistvaluec
+ \ setmagiceffectschool
+ \ setmagiceffectschoolc
+ \ setmagiceffectuseactorvaluec
+ \ setmagiceffectusedobject
+ \ setmagiceffectusedobjectc
+ \ setmagiceffectusesactorvalue
+ \ setmagiceffectusesactorvaluec
+ \ setmagiceffectusesarmor
+ \ setmagiceffectusesarmorc
+ \ setmagiceffectusesattribute
+ \ setmagiceffectusesattributec
+ \ setmagiceffectusescreature
+ \ setmagiceffectusescreaturec
+ \ setmagiceffectusesskill
+ \ setmagiceffectusesskillc
+ \ setmagiceffectusesweapon
+ \ setmagiceffectusesweaponc
+ \ setmagicitemautocalc
+ \ setmagicprojectilespell
+ \ setmalebipedpath
+ \ setmalegroundpath
+ \ setmaleiconpath
+ \ setmapmarkertype
+ \ setmapmarkervisible
+ \ setmeareasound
+ \ setmeareasoundc
+ \ setmebarterfactor
+ \ setmebarterfactorc
+ \ setmebasecost
+ \ setmebasecostc
+ \ setmeboltsound
+ \ setmeboltsoundc
+ \ setmecanrecover
+ \ setmecanrecoverc
+ \ setmecastingsound
+ \ setmecastingsoundc
+ \ setmeenchantfactor
+ \ setmeenchantfactorc
+ \ setmeenchantshader
+ \ setmeenchantshaderc
+ \ setmeforenchanting
+ \ setmeforenchantingc
+ \ setmeforspellmaking
+ \ setmeforspellmakingc
+ \ setmefxpersists
+ \ setmefxpersistsc
+ \ setmehitshader
+ \ setmehitshaderc
+ \ setmehitsound
+ \ setmehitsoundc
+ \ setmeicon
+ \ setmeiconc
+ \ setmeisdetrimental
+ \ setmeisdetrimentalc
+ \ setmeishostile
+ \ setmeishostilec
+ \ setmelight
+ \ setmelightc
+ \ setmemagnitudepercent
+ \ setmemagnitudepercentc
+ \ setmemodel
+ \ setmemodelc
+ \ setmename
+ \ setmenamec
+ \ setmenoarea
+ \ setmenoareac
+ \ setmenoduration
+ \ setmenodurationc
+ \ setmenohiteffect
+ \ setmenohiteffectc
+ \ setmenoingredient
+ \ setmenoingredientc
+ \ setmenomagnitude
+ \ setmenomagnitudec
+ \ setmenufloatvalue
+ \ setmenustringvalue
+ \ setmeonselfallowed
+ \ setmeonselfallowedc
+ \ setmeontargetallowed
+ \ setmeontargetallowedc
+ \ setmeontouchallowed
+ \ setmeontouchallowedc
+ \ setmeotheractorvalue
+ \ setmeotheractorvaluec
+ \ setmeprojectilespeed
+ \ setmeprojectilespeedc
+ \ setmerchantcontainer
+ \ setmeresistvalue
+ \ setmeresistvaluec
+ \ setmeschool
+ \ setmeschoolc
+ \ setmessageicon
+ \ setmessagesound
+ \ setmeuseactorvalue
+ \ setmeuseactorvaluec
+ \ setmeusedobject
+ \ setmeusedobjectc
+ \ setmeusesarmor
+ \ setmeusesarmorc
+ \ setmeusesattribute
+ \ setmeusesattributec
+ \ setmeusescreature
+ \ setmeusescreaturec
+ \ setmeusesskill
+ \ setmeusesskillc
+ \ setmeusesweapon
+ \ setmeusesweaponc
+ \ setmodelpath
+ \ setmodlocaldata
+ \ setmousespeedx
+ \ setmousespeedy
+ \ setmpspell
+ \ setname
+ \ setnameex
+ \ setnopersuasion
+ \ setnthactiveeffectmagnitude
+ \ setnthaemagnitude
+ \ setntheffectitemactorvalue
+ \ setntheffectitemactorvaluec
+ \ setntheffectitemarea
+ \ setntheffectitemduration
+ \ setntheffectitemmagnitude
+ \ setntheffectitemrange
+ \ setntheffectitemscript
+ \ setntheffectitemscripthostile
+ \ setntheffectitemscriptname
+ \ setntheffectitemscriptnameex
+ \ setntheffectitemscriptschool
+ \ setntheffectitemscriptvisualeffect
+ \ setntheffectitemscriptvisualeffectc
+ \ setntheiarea
+ \ setntheiav
+ \ setntheiavc
+ \ setntheiduration
+ \ setntheimagnitude
+ \ setntheirange
+ \ setntheiscript
+ \ setntheishostile
+ \ setntheisname
+ \ setntheisschool
+ \ setntheisvisualeffect
+ \ setntheisvisualeffectc
+ \ setnthfactionranknameex
+ \ setnumericgamesetting
+ \ setnumericinisetting
+ \ setobjectcharge
+ \ setobjecthealth
+ \ setoffersapparatus
+ \ setoffersarmor
+ \ setoffersbooks
+ \ setoffersclothing
+ \ setoffersingredients
+ \ setofferslights
+ \ setoffersmagicitems
+ \ setoffersmiscitems
+ \ setofferspotions
+ \ setoffersrecharging
+ \ setoffersrepair
+ \ setoffersservicesc
+ \ setoffersspells
+ \ setofferstraining
+ \ setoffersweapons
+ \ setolmpgrids
+ \ setopenkey
+ \ setopensound
+ \ setopenstip
+ \ setownership_t
+ \ setowningrequiredrank
+ \ setpackageallowfalls
+ \ setpackageallowswimming
+ \ setpackagealwaysrun
+ \ setpackagealwayssneak
+ \ setpackagearmorunequipped
+ \ setpackagecontinueifpcnear
+ \ setpackagedata
+ \ setpackagedefensivecombat
+ \ setpackagelocationdata
+ \ setpackagelockdoorsatend
+ \ setpackagelockdoorsatlocation
+ \ setpackagelockdoorsatstart
+ \ setpackagemustcomplete
+ \ setpackagemustreachlocation
+ \ setpackagenoidleanims
+ \ setpackageoffersservices
+ \ setpackageonceperday
+ \ setpackagescheduledata
+ \ setpackageskipfalloutbehavior
+ \ setpackagetarget
+ \ setpackagetargetdata
+ \ setpackageunlockdoorsatend
+ \ setpackageunlockdoorsatlocation
+ \ setpackageunlockdoorsatstart
+ \ setpackageusehorse
+ \ setpackageweaponsunequipped
+ \ setparentcellowningfactionrequiredrank
+ \ setpathnodedisabled
+ \ setpcamurderer
+ \ setpcattributebonus
+ \ setpcattributebonusc
+ \ setpcexpy
+ \ setpcleveloffset
+ \ setpcmajorskillups
+ \ setpctrainingsessionsused
+ \ setplayerbseworld
+ \ setplayerprojectile
+ \ setplayerskeletonpath
+ \ setplayerskilladvances
+ \ setplayerskilladvancesc
+ \ setplayerslastriddenhorse
+ \ setpos_t
+ \ setpowertimer
+ \ setprojectilesource
+ \ setprojectilespeed
+ \ setquality
+ \ setquestitem
+ \ setracealias
+ \ setraceplayable
+ \ setracescale
+ \ setracevoice
+ \ setraceweight
+ \ setrefcount
+ \ setrefessential
+ \ setreale
+ \ setscaleex
+ \ setscript
+ \ setsigilstoneuses
+ \ setskillgoverningattribute
+ \ setskillgoverningattributec
+ \ setskillspecialization
+ \ setskillspecializationc
+ \ setskilluseincrement
+ \ setskilluseincrementc
+ \ setsoulgemcapacity
+ \ setsoullevel
+ \ setsoundattenuation
+ \ setspellareaeffectignoreslos
+ \ setspelldisallowabsorbreflect
+ \ setspellexplodeswithnotarget
+ \ setspellhostile
+ \ setspellimmunetosilence
+ \ setspellmagickacost
+ \ setspellmasterylevel
+ \ setspellpcstart
+ \ setspellscripteffectalwaysapplies
+ \ setspelltype
+ \ setstagedate
+ \ setstagetext
+ \ setstringgamesettingex
+ \ setstringinisetting
+ \ setsummonable
+ \ setsundamage
+ \ setsunglare
+ \ settaken
+ \ settextinputcontrolhandler
+ \ settextinputdefaultcontrolsdisabled
+ \ settextinputhandler
+ \ settexturepath
+ \ settimeleft
+ \ settrainerlevel
+ \ settrainerskill
+ \ settransdelta
+ \ settravelhorse
+ \ setunsafecontainer
+ \ setvelocity
+ \ setverticalvelocity
+ \ setweaponreach
+ \ setweaponspeed
+ \ setweapontype
+ \ setweathercloudspeedlower
+ \ setweathercloudspeedupper
+ \ setweathercolor
+ \ setweatherfogdayfar
+ \ setweatherfogdaynear
+ \ setweatherfognightfar
+ \ setweatherfognightnear
+ \ setweatherhdrvalue
+ \ setweatherlightningfrequency
+ \ setweathersundamage
+ \ setweathersunglare
+ \ setweathertransdelta
+ \ setweatherwindspeed
+ \ setweight
+ \ setwindspeed
+ \ showellmaking
+ \ sin
+ \ sinh
+ \ skipansqrt
+ \ squareroot
+ \ startcc
+ \ stringtoactorvalue
+ \ tan
+ \ tanh
+ \ tapcontrol
+ \ tapkey
+ \ testexpr
+ \ thiactorsai
+ \ togglecreaturemodel
+ \ togglefirstperson
+ \ toggleskillperk
+ \ togglespecialanim
+ \ tolower
+ \ tonumber
+ \ tostring
+ \ toupper
+ \ trapuphitshader
+ \ triggerplayerskilluse
+ \ triggerplayerskillusec
+ \ typeof
+ \ uncompletequest
+ \ unequipitemns
+ \ unequipitemsilent
+ \ unequipme
+ \ unhammerkey
+ \ unsetstagetext
+ \ update3d
+ \ updatecontainermenu
+ \ updatespellpurchasemenu
+ \ updatetextinput
+ \ vecmag
+ \ vecnorm
+ \ vectorcross
+ \ vectordot
+ \ vectormagnitude
+ \ vectornormalize
+ \ zeromat
+" }}}
+
+" Array Functions {{{
+syn keyword obseArrayFunction
+ \ ar_Append
+ \ ar_BadNumericIndex
+ \ ar_BadStringIndex
+ \ ar_Construct
+ \ ar_Copy
+ \ ar_CustomSort
+ \ ar_DeepCopy
+ \ ar_Dump
+ \ ar_DumpID
+ \ ar_Erase
+ \ ar_Find
+ \ ar_First
+ \ ar_HasKey
+ \ ar_Insert
+ \ ar_InsertRange
+ \ ar_Keys
+ \ ar_Last
+ \ ar_List
+ \ ar_Map
+ \ ar_Next
+ \ ar_Null
+ \ ar_Prev
+ \ ar_Range
+ \ ar_Resize
+ \ ar_Size
+ \ ar_Sort
+ \ ar_SortAlpha
+" }}}
+
+" String Functions {{{
+syn keyword obseStringFunction
+ \ sv_ToLower
+ \ sv_ToUpper
+ \ sv_Compare
+ \ sv_Construct
+ \ sv_Count
+ \ sv_Destruct
+ \ sv_Erase
+ \ sv_Find
+ \ sv_Insert
+ \ sv_Length
+ \ sv_Percentify
+ \ sv_Replace
+ \ sv_Split
+ \ sv_ToNumeric
+" }}}
+
+" Pluggy Functions {{{
+syn keyword pluggyFunction
+ \ ArrayCmp
+ \ ArrayCount
+ \ ArrayEsp
+ \ ArrayProtect
+ \ ArraySize
+ \ AutoSclHudS
+ \ AutoSclHudT
+ \ CopyArray
+ \ CopyString
+ \ CreateArray
+ \ CreateEspBook
+ \ CreateString
+ \ DelAllHudSs
+ \ DelAllHudTs
+ \ DelFile
+ \ DelHudS
+ \ DelHudT
+ \ DelTxtFile
+ \ DestroyAllArrays
+ \ DestroyAllStrings
+ \ DestroyArray
+ \ DestroyString
+ \ DupArray
+ \ EspToString
+ \ FileToString
+ \ FindFirstFile
+ \ FindFloatInArray
+ \ FindInArray
+ \ FindNextFile
+ \ FindRefInArray
+ \ FirstFreeInArray
+ \ FirstInArray
+ \ FixName
+ \ FixNameEx
+ \ FloatToString
+ \ FmtString
+ \ FromOBSEString
+ \ FromTSFC
+ \ GetEsp
+ \ GetFileSize
+ \ GetInArray
+ \ GetRefEsp
+ \ GetTypeInArray
+ \ Halt
+ \ HasFixedName
+ \ HudSEsp
+ \ HudSProtect
+ \ HudS_Align
+ \ HudS_L
+ \ HudS_Opac
+ \ HudS_SclX
+ \ HudS_SclY
+ \ HudS_Show
+ \ HudS_Tex
+ \ HudS_X
+ \ HudS_Y
+ \ HudTEsp
+ \ HudTInfo
+ \ HudTProtect
+ \ HudT_Align
+ \ HudT_Font
+ \ HudT_L
+ \ HudT_Opac
+ \ HudT_SclX
+ \ HudT_SclY
+ \ HudT_Show
+ \ HudT_Text
+ \ HudT_X
+ \ HudT_Y
+ \ HudsInfo
+ \ IniDelKey
+ \ IniGetNthSection
+ \ IniKeyExists
+ \ IniReadFloat
+ \ IniReadInt
+ \ IniReadRef
+ \ IniReadString
+ \ IniSectionsCount
+ \ IniWriteFloat
+ \ IniWriteInt
+ \ IniWriteRef
+ \ IniWriteString
+ \ IntToHex
+ \ IntToString
+ \ IsHUDEnabled
+ \ IsPluggyDataReset
+ \ KillMenu
+ \ LC
+ \ LongToRef
+ \ ModRefEsp
+ \ NewHudS
+ \ NewHudT
+ \ PackArray
+ \ PauseBox
+ \ PlgySpcl
+ \ RefToLong
+ \ RefToString
+ \ RemInArray
+ \ RenFile
+ \ RenTxtFile
+ \ ResetName
+ \ RunBatString
+ \ SanString
+ \ ScreenInfo
+ \ SetFloatInArray
+ \ SetHudT
+ \ SetInArray
+ \ SetRefInArray
+ \ SetString
+ \ StrLC
+ \ StringCat
+ \ StringCmp
+ \ StringEsp
+ \ StringGetName
+ \ StringGetNameEx
+ \ StringIns
+ \ StringLen
+ \ StringMsg
+ \ StringMsgBox
+ \ StringPos
+ \ StringProtect
+ \ StringRep
+ \ StringSetName
+ \ StringSetNameEx
+ \ StringToFloat
+ \ StringToInt
+ \ StringToRef
+ \ StringToTxtFile
+ \ ToOBSE
+ \ ToOBSEString
+ \ ToTSFC
+ \ TxtFileExists
+ \ UserFileExists
+ \ csc
+ \ rcsc
+" }}}
+
+" tfscFunction {{{
+syn keyword tfscFunction
+ \ StrAddNewLine
+ \ StrAppend
+ \ StrAppendCharCode
+ \ StrCat
+ \ StrClear
+ \ StrClearLast
+ \ StrCompare
+ \ StrCopy
+ \ StrDel
+ \ StrDeleteAll
+ \ StrExpr
+ \ StrGetFemaleBipedPath
+ \ StrGetFemaleGroundPath
+ \ StrGetFemaleIconPath
+ \ StrGetMaleBipedPath
+ \ StrGetMaleIconPath
+ \ StrGetModelPath
+ \ StrGetName
+ \ StrGetNthEffectItemScriptName
+ \ StrGetNthFactionRankName
+ \ StrGetRandomName
+ \ StrIDReplace
+ \ StrLength
+ \ StrLoad
+ \ StrMessageBox
+ \ StrNew
+ \ StrPrint
+ \ StrReplace
+ \ StrSave
+ \ StrSet
+ \ StrSetFemaleBipedPath
+ \ StrSetFemaleGroundPath
+ \ StrSetFemaleIconPath
+ \ StrSetMaleBipedPath
+ \ StrSetMaleIconPath
+ \ StrSetModelPath
+ \ StrSetName
+ \ StrSetNthEffectItemScriptName
+" }}}
+
+" Blockhead Functions {{{
+syn keyword blockheadFunction
+ \ GetBodyAssetOverride
+ \ GetFaceGenAge
+ \ GetHeadAssetOverride
+ \ RefreshAnimData
+ \ RegisterEquipmentOverrideHandler
+ \ ResetAgeTextureOverride
+ \ ResetBodyAssetOverride
+ \ ResetHeadAssetOverride
+ \ SetAgeTextureOverride
+ \ SetBodyAssetOverride
+ \ SetFaceGenAge
+ \ SetHeadAssetOverride
+ \ ToggleAnimOverride
+ \ UnregisterEquipmentOverrideHandler
+" }}}
+
+" switchNightEyeShaderFunction {{{
+syn keyword switchNightEyeShaderFunction
+ \ EnumNightEyeShader
+ \ SetNightEyeShader
+" }}}
+
+" Oblivion Reloaded Functions {{{
+syn keyword obseivionReloadedFunction
+ \ cameralookat
+ \ cameralookatposition
+ \ camerareset
+ \ camerarotate
+ \ camerarotatetoposition
+ \ cameratranslate
+ \ cameratranslatetoposition
+ \ getlocationname
+ \ getsetting
+ \ getversion
+ \ getweathername
+ \ isthirdperson
+ \ setcustomconstant
+ \ setextraeffectenabled
+ \ setsetting
+" }}}
+" menuQue Functions {{{
+syn keyword menuQueFunction
+ \ GetAllSkills
+ \ GetAVSkillMasteryLevelC
+ \ GetAVSkillMasteryLevelF
+ \ GetFontLoaded
+ \ GetGenericButtonPressed
+ \ GetLoadedFonts
+ \ GetLocalMapSeen
+ \ GetMenuEventType
+ \ GetMenuFloatValue
+ \ GetMenuStringValue
+ \ GetMouseImage
+ \ GetMousePos
+ \ GetPlayerSkillAdvancesF
+ \ GetPlayerSkillUseF
+ \ GetRequiredSkillExpC
+ \ GetRequiredSkillExpF
+ \ GetSkillCode
+ \ GetSkillForm
+ \ GetSkillGoverningAttributeF
+ \ GetSkillSpecializationC
+ \ GetSkillSpecializationF
+ \ GetSkillUseIncrementF
+ \ GetTextEditBox
+ \ GetTextEditString
+ \ GetTrainingMenuCost
+ \ GetTrainingMenuLevel
+ \ GetTrainingMenuSkill
+ \ GetWorldMapData
+ \ GetWorldMapDoor
+ \ IncrementPlayerSkillUseF
+ \ InsertXML
+ \ InsertXMLTemplate
+ \ IsTextEditInUse
+ \ Kyoma_Test
+ \ ModPlayerSkillExpF
+ \ mqCreateMenuFloatValue
+ \ mqCreateMenuStringValue
+ \ mqGetActiveQuest
+ \ mqGetActiveQuestTargets
+ \ mqGetCompletedQuests
+ \ mqGetCurrentQuests
+ \ mqGetEnchMenuBaseItem
+ \ mqGetHighlightedClass
+ \ mqGetMapMarkers
+ \ mqGetMenuActiveChildIndex
+ \ mqGetMenuActiveFloatValue
+ \ mqGetMenuActiveStringValue
+ \ mqGetMenuChildCount
+ \ mqGetMenuChildFloatValue
+ \ mqGetMenuChildHasTrait
+ \ mqGetMenuChildName
+ \ mqGetMenuChildStringValue
+ \ mqGetMenuGlobalFloatValue
+ \ mqGetMenuGlobalStringValue
+ \ mqGetQuestCompleted
+ \ mqGetSelectedClass
+ \ mqSetActiveQuest
+ \ mqSetMenuActiveFloatValue
+ \ mqSetMenuActiveStringValue
+ \ mqSetMenuChildFloatValue
+ \ mqSetMenuChildStringValue
+ \ mqSetMenuGlobalStringValue
+ \ mqSetMenuGlobalFloatValue
+ \ mqSetMessageBoxSource
+ \ mqUncompleteQuest
+ \ RemoveMenuEventHandler
+ \ SetMenuEventHandler
+ \ SetMouseImage
+ \ SetPlayerSkillAdvancesF
+ \ SetSkillGoverningAttributeF
+ \ SetSkillSpecializationC
+ \ SetSkillSpecializationF
+ \ SetSkillUseIncrementF
+ \ SetTextEditString
+ \ SetTrainerSkillC
+ \ SetWorldMapData
+ \ ShowGenericMenu
+ \ ShowLevelUpMenu
+ \ ShowMagicPopupMenu
+ \ ShowTextEditMenu
+ \ ShowTrainingMenu
+ \ tile_FadeFloat
+ \ tile_GetFloat
+ \ tile_GetInfo
+ \ tile_GetName
+ \ tile_GetString
+ \ tile_GetVar
+ \ tile_HasTrait
+ \ tile_SetFloat
+ \ tile_SetString
+ \ TriggerPlayerSkillUseF
+ \ UpdateLocalMap
+" }}}
+
+" eaxFunction {{{
+syn keyword eaxFunction
+ \ CreateEAXeffect
+ \ DeleteEAXeffect
+ \ DisableEAX
+ \ EAXcopyEffect
+ \ EAXeffectExists
+ \ EAXeffectsAreEqual
+ \ EAXgetActiveEffect
+ \ EAXnumEffects
+ \ EAXpushEffect
+ \ EAXpopEffect
+ \ EAXremoveAllInstances
+ \ EAXremoveFirstInstance
+ \ EAXstackIsEmpty
+ \ EAXstackSize
+ \ EnableEAX
+ \ GetEAXAirAbsorptionHF
+ \ GetEAXDecayHFRatio
+ \ GetEAXDecayTime
+ \ GetEAXEnvironment
+ \ GetEAXEnvironmentSize
+ \ GetEAXEnvironmentDiffusion
+ \ GetEAXReflections
+ \ GetEAXReflectionsDelay
+ \ GetEAXReverb
+ \ GetEAXReverbDelay
+ \ GetEAXRoom
+ \ GetEAXRoomHF
+ \ GetEAXRoomRolloffFactor
+ \ InitializeEAX
+ \ IsEAXEnabled
+ \ IsEAXInitialized
+ \ SetEAXAirAbsorptionHF
+ \ SetEAXallProperties
+ \ SetEAXDecayTime
+ \ SetEAXDecayHFRatio
+ \ SetEAXEnvironment
+ \ SetEAXEnvironmentSize
+ \ SetEAXEnvironmentDiffusion
+ \ SetEAXReflections
+ \ SetEAXReflectionsDelay
+ \ SetEAXReverb
+ \ SetEAXReverbDelay
+ \ SetEAXRoom
+ \ SetEAXRoomHF
+ \ SetEAXRoomRolloffFactor
+" }}}
+
+" networkPipeFunction {{{
+syn keyword networkPipeFunction
+ \ NetworkPipe_CreateClient
+ \ NetworkPipe_GetData
+ \ NetworkPipe_IsNewGame
+ \ NetworkPipe_KillClient
+ \ NetworkPipe_Receive
+ \ NetworkPipe_SetData
+ \ NetworkPipe_Send
+ \ NetworkPipe_StartService
+ \ NetworkPipe_StopService
+" }}}
+
+" nifseFunction {{{
+syn keyword nifseFunction
+ \ BSFurnitureMarkerGetPositionRefs
+ \ BSFurnitureMarkerSetPositionRefs
+ \ GetNifTypeIndex
+ \ NiAVObjectAddProperty
+ \ NiAVObjectClearCollisionObject
+ \ NiAVObjectCopyCollisionObject
+ \ NiAVObjectDeleteProperty
+ \ NiAVObjectGetCollisionMode
+ \ NiAVObjectGetCollisionObject
+ \ NiAVObjectGetLocalRotation
+ \ NiAVObjectGetLocalScale
+ \ NiAVObjectGetLocalTransform
+ \ NiAVObjectGetLocalTranslation
+ \ NiAVObjectGetNumProperties
+ \ NiAVObjectGetProperties
+ \ NiAVObjectGetPropertyByType
+ \ NiAVObjectSetCollisionMode
+ \ NiAVObjectSetLocalRotation
+ \ NiAVObjectSetLocalScale
+ \ NiAVObjectSetLocalTransform
+ \ NiAVObjectSetLocalTranslation
+ \ NiAlphaPropertyGetBlendState
+ \ NiAlphaPropertyGetDestinationBlendFunction
+ \ NiAlphaPropertyGetSourceBlendFunction
+ \ NiAlphaPropertyGetTestFunction
+ \ NiAlphaPropertyGetTestState
+ \ NiAlphaPropertyGetTestThreshold
+ \ NiAlphaPropertyGetTriangleSortMode
+ \ NiAlphaPropertySetBlendState
+ \ NiAlphaPropertySetDestinationBlendFunction
+ \ NiAlphaPropertySetSourceBlendFunction
+ \ NiAlphaPropertySetTestFunction
+ \ NiAlphaPropertySetTestState
+ \ NiAlphaPropertySetTestThreshold
+ \ NiAlphaPropertySetTriangleSortMode
+ \ NiExtraDataGetArray
+ \ NiExtraDataGetName
+ \ NiExtraDataGetNumber
+ \ NiExtraDataGetString
+ \ NiExtraDataSetArray
+ \ NiExtraDataSetName
+ \ NiExtraDataSetNumber
+ \ NiExtraDataSetString
+ \ NiMaterialPropertyGetAmbientColor
+ \ NiMaterialPropertyGetDiffuseColor
+ \ NiMaterialPropertyGetEmissiveColor
+ \ NiMaterialPropertyGetGlossiness
+ \ NiMaterialPropertyGetSpecularColor
+ \ NiMaterialPropertyGetTransparency
+ \ NiMaterialPropertySetAmbientColor
+ \ NiMaterialPropertySetDiffuseColor
+ \ NiMaterialPropertySetEmissiveColor
+ \ NiMaterialPropertySetGlossiness
+ \ NiMaterialPropertySetSpecularColor
+ \ NiMaterialPropertySetTransparency
+ \ NiNodeAddChild
+ \ NiNodeCopyChild
+ \ NiNodeDeleteChild
+ \ NiNodeGetChildByName
+ \ NiNodeGetChildren
+ \ NiNodeGetNumChildren
+ \ NiObjectGetType
+ \ NiObjectGetTypeName
+ \ NiObjectNETAddExtraData
+ \ NiObjectNETDeleteExtraData
+ \ NiObjectNETGetExtraData
+ \ NiObjectNETGetExtraDataByName
+ \ NiObjectNETGetName
+ \ NiObjectNETGetNumExtraData
+ \ NiObjectNETSetName
+ \ NiObjectTypeDerivesFrom
+ \ NiSourceTextureGetFile
+ \ NiSourceTextureIsExternal
+ \ NiSourceTextureSetExternalTexture
+ \ NiStencilPropertyGetFaceDrawMode
+ \ NiStencilPropertyGetFailAction
+ \ NiStencilPropertyGetPassAction
+ \ NiStencilPropertyGetStencilFunction
+ \ NiStencilPropertyGetStencilMask
+ \ NiStencilPropertyGetStencilRef
+ \ NiStencilPropertyGetStencilState
+ \ NiStencilPropertyGetZFailAction
+ \ NiStencilPropertySetFaceDrawMode
+ \ NiStencilPropertySetFailAction
+ \ NiStencilPropertySetPassAction
+ \ NiStencilPropertySetStencilFunction
+ \ NiStencilPropertySetStencilMask
+ \ NiStencilPropertySetStencilRef
+ \ NiStencilPropertySetStencilState
+ \ NiStencilPropertySetZFailAction
+ \ NiTexturingPropertyAddTextureSource
+ \ NiTexturingPropertyDeleteTextureSource
+ \ NiTexturingPropertyGetTextureCenterOffset
+ \ NiTexturingPropertyGetTextureClampMode
+ \ NiTexturingPropertyGetTextureCount
+ \ NiTexturingPropertyGetTextureFilterMode
+ \ NiTexturingPropertyGetTextureFlags
+ \ NiTexturingPropertyGetTextureRotation
+ \ NiTexturingPropertyGetTextureSource
+ \ NiTexturingPropertyGetTextureTiling
+ \ NiTexturingPropertyGetTextureTranslation
+ \ NiTexturingPropertyGetTextureUVSet
+ \ NiTexturingPropertyHasTexture
+ \ NiTexturingPropertySetTextureCenterOffset
+ \ NiTexturingPropertySetTextureClampMode
+ \ NiTexturingPropertySetTextureCount
+ \ NiTexturingPropertySetTextureFilterMode
+ \ NiTexturingPropertySetTextureFlags
+ \ NiTexturingPropertySetTextureHasTransform
+ \ NiTexturingPropertySetTextureRotation
+ \ NiTexturingPropertySetTextureTiling
+ \ NiTexturingPropertySetTextureTranslation
+ \ NiTexturingPropertySetTextureUVSet
+ \ NiTexturingPropertyTextureHasTransform
+ \ NiVertexColorPropertyGetLightingMode
+ \ NiVertexColorPropertyGetVertexMode
+ \ NiVertexColorPropertySetLightingMode
+ \ NiVertexColorPropertySetVertexMode
+ \ NifClose
+ \ NifGetAltGrip
+ \ NifGetBackShield
+ \ NifGetNumBlocks
+ \ NifGetOffHand
+ \ NifGetOriginalPath
+ \ NifGetPath
+ \ NifOpen
+ \ NifWriteToDisk
+" }}}
+
+" reidFunction {{{
+syn keyword reidFunction
+ \ GetRuntimeEditorID
+" }}}
+
+" runtimeDebuggerFunction {{{
+syn keyword runtimeDebuggerFunction
+ \ DebugBreak
+ \ ToggleDebugBreaking
+" }}}
+
+" addActorValuesFunction {{{
+syn keyword addActorValuesFunction
+ \ DumpActorValueC
+ \ DumpActorValueF
+ \ GetActorValueBaseCalcC
+ \ GetActorValueBaseCalcF
+ \ GetActorValueCurrentC
+ \ GetActorValueCurrentF
+ \ GetActorValueMaxC
+ \ GetActorValueMaxF
+ \ GetActorValueModC
+ \ GetActorValueModF
+ \ ModActorValueModC
+ \ ModActorValueModF
+ \ SetActorValueModC
+ \ SetActorValueModF
+ \ DumpAVC
+ \ DumpAVF
+ \ GetAVModC
+ \ GetAVModF
+ \ ModAVModC
+ \ ModAVModF
+ \ SetAVModC
+ \ SetAVModF
+ \ GetAVBaseCalcC
+ \ GetAVBaseCalcF
+ \ GetAVMaxC
+ \ GetAVMaxF
+ \ GetAVCurrentC
+ \ GetAVCurrent
+" }}}
+
+" memoryDumperFunction {{{
+syn keyword memoryDumperFunction
+ \ SetDumpAddr
+ \ SetDumpType
+ \ SetFadeAmount
+ \ SetObjectAddr
+ \ ShowMemoryDump
+" }}}
+
+" algoholFunction {{{
+syn keyword algoholFunction
+ \ QFromAxisAngle
+ \ QFromEuler
+ \ QInterpolate
+ \ QMultQuat
+ \ QMultVector3
+ \ QNormalize
+ \ QToEuler
+ \ V3Crossproduct
+ \ V3Length
+ \ V3Normalize
+" }}}
+
+" soundCommandsFunction {{{
+syn keyword soundCommandsFunction
+ \ FadeMusic
+ \ GetEffectsVolume
+ \ GetFootVolume
+ \ GetMasterVolume
+ \ GetMusicVolume
+ \ GetVoiceVolume
+ \ PlayMusicFile
+ \ SetEffectsVolume
+ \ SetFootVolume
+ \ SetMasterVolume
+ \ SetMusicVolume
+ \ SetVoiceVolume
+" }}}
+
+" emcFunction {{{
+syn keyword emcFunction
+ \ emcAddPathToPlaylist
+ \ emcCreatePlaylist
+ \ emcGetAllPlaylists
+ \ emcGetAfterBattleDelay
+ \ emcGetBattleDelay
+ \ emcGetEffectsVolume
+ \ emcGetFadeTime
+ \ emcGetFootVolume
+ \ emcGetMasterVolume
+ \ emcGetMaxRestoreTime
+ \ emcGetMusicSpeed
+ \ emcGetMusicType
+ \ emcGetMusicVolume
+ \ emcGetPauseTime
+ \ emcGetPlaylist
+ \ emcGetPlaylistTracks
+ \ emcGetTrackName
+ \ emcGetTrackDuration
+ \ emcGetTrackPosition
+ \ emcGetVoiceVolume
+ \ emcIsBattleOverridden
+ \ emcIsMusicOnHold
+ \ emcIsMusicSwitching
+ \ emcIsPlaylistActive
+ \ emcMusicNextTrack
+ \ emcMusicPause
+ \ emcMusicRestart
+ \ emcMusicResume
+ \ emcMusicStop
+ \ emcPlaylistExists
+ \ emcPlayTrack
+ \ emcRestorePlaylist
+ \ emcSetAfterBattleDelay
+ \ emcSetBattleDelay
+ \ emcSetBattleOverride
+ \ emcSetEffectsVolume
+ \ emcSetFadeTime
+ \ emcSetFootVolume
+ \ emcSetMasterVolume
+ \ emcSetMaxRestoreTime
+ \ emcSetMusicHold
+ \ emcSetMusicSpeed
+ \ emcSetMusicVolume
+ \ emcSetPauseTime
+ \ emcSetPlaylist
+ \ emcSetTrackPosition
+ \ emcSetMusicType
+ \ emcSetVoiceVolume
+" }}}
+
+" vipcxjFunction {{{
+syn keyword vipcxjFunction
+ \ vcAddMark
+ \ vcGetFilePath
+ \ vcGetHairColorRGB
+ \ vcGetValueNumeric
+ \ vcGetValueString
+ \ vcIsMarked
+ \ vcPrintIni
+ \ vcSetActorState
+ \ vcSetHairColor
+ \ vcSetHairColorRGB
+ \ vcSetHairColorRGB3P
+" }}}
+
+" cameraCommandsFunction {{{
+syn keyword cameraCommandsFunction
+ \ CameraGetRef
+ \ CameraLookAt
+ \ CameraLookAtPosition
+ \ CameraMove
+ \ CameraMoveToPosition
+ \ CameraReset
+ \ CameraRotate
+ \ CameraRotateToPosition
+ \ CameraSetRef
+ \ CameraStopLook
+" }}}
+
+" obmeFunction {{{
+syn keyword obmeFunction
+ \ ClearNthEIBaseCost
+ \ ClearNthEIEffectName
+ \ ClearNthEIHandlerParam
+ \ ClearNthEIHostility
+ \ ClearNthEIIconPath
+ \ ClearNthEIResistAV
+ \ ClearNthEISchool
+ \ ClearNthEIVFXCode
+ \ CreateMgef
+ \ GetMagicEffectHandlerC
+ \ GetMagicEffectHandlerParamC
+ \ GetMagicEffectHostilityC
+ \ GetNthEIBaseCost
+ \ GetNthEIEffectName
+ \ GetNthEIHandlerParam
+ \ GetNthEIHostility
+ \ GetNthEIIconPath
+ \ GetNthEIResistAV
+ \ GetNthEISchool
+ \ GetNthEIVFXCode
+ \ ResolveMgefCode
+ \ SetMagicEffectHandlerC
+ \ SetMagicEffectHandlerIntParamC
+ \ SetMagicEffectHandlerRefParamC
+ \ SetMagicEffectHostilityC
+ \ SetNthEIBaseCost
+ \ SetNthEIEffectName
+ \ SetNthEIHandlerIntParam
+ \ SetNthEIHandlerRefParam
+ \ SetNthEIHostility
+ \ SetNthEIIconPath
+ \ SetNthEIResistAV
+ \ SetNthEISchool
+ \ SetNthEIVFXCode
+" }}}
+
+" conscribeFunction {{{
+syn keyword conscribeFunction
+ \ DeleteLinesFromLog
+ \ GetLogLineCount
+ \ GetRegisteredLogNames
+ \ ReadFromLog
+ \ RegisterLog
+ \ Scribe
+ \ UnregisterLog
+" }}}
+
+" systemDialogFunction {{{
+syn keyword systemDialogFunction
+ \ Sysdlg_Browser
+ \ Sysdlg_ReadBrowser
+ \ Sysdlg_TextInput
+" }}}
+
+" csiFunction {{{
+syn keyword csiFunction
+ \ ClearSpellIcon
+ \ HasAssignedIcon
+ \ OverwriteSpellIcon
+ \ SetSpellIcon
+" }}}
+
+" haelFunction {{{
+syn keyword haelFunction
+ \ GetHUDActiveEffectLimit
+ \ SetHUDActiveEffectLimit
+" }}}
+
+" lcdFunction {{{
+syn keyword lcdFunction
+ \ lcd_addinttobuffer
+ \ lcd_addtexttobuffer
+ \ lcd_clearrect
+ \ lcd_cleartextbuffer
+ \ lcd_close
+ \ lcd_drawcircle
+ \ lcd_drawgrid
+ \ lcd_drawint
+ \ lcd_drawline
+ \ lcd_drawprogressbarh
+ \ lcd_drawprogressbarv
+ \ lcd_drawprogresscircle
+ \ lcd_drawrect
+ \ lcd_drawtext
+ \ lcd_drawtextbuffer
+ \ lcd_drawtexture
+ \ lcd_flush
+ \ lcd_getbuttonstate
+ \ lcd_getheight
+ \ lcd_getwidth
+ \ lcd_ismulti
+ \ lcd_isopen
+ \ lcd_open
+ \ lcd_refresh
+ \ lcd_savebuttonsnapshot
+ \ lcd_scale
+ \ lcd_setfont
+" }}}
+
+" Deprecated: {{{
+syn keyword obDeprecated
+ \ SetAltControl
+ \ GetAltControl
+ \ RefreshControlMap
+" }}}
+" }}}
+
+if !exists("did_obl_inits")
+
+ let did_obl_inits = 1
+ hi def link obseStatement Statement
+ hi def link obseStatementTwo Statement
+ hi def link obseDescBlock String
+ hi def link obseComment Comment
+ hi def link obseString String
+ hi def link obseStringFormatting Keyword
+ hi def link obseFloat Float
+ hi def link obseInt Number
+ hi def link obseToDo Todo
+ hi def link obseTypes Type
+ hi def link obseCondition Conditional
+ hi def link obseOperator Operator
+ hi def link obseOtherKey Special
+ hi def link obseScriptName Special
+ hi def link obseBlock Conditional
+ hi def link obseBlockType Structure
+ hi def link obseScriptNameRegion Underlined
+ hi def link obseNames Identifier
+ hi def link obseVariable Identifier
+ hi def link obseReference Special
+ hi def link obseRepeat Repeat
+
+ hi def link csFunction Function
+ hi def link obseFunction Function
+ hi def link obseArrayFunction Function
+ hi def link pluggyFunction Function
+ hi def link obseStringFunction Function
+ hi def link obseArrayFunction Function
+ hi def link tsfcFunction Function
+ hi def link blockheadFunction Function
+ hi def link switchNightEyeShaderFunction Function
+ hi def link obseivionReloadedFunction Function
+ hi def link menuQueFunction Function
+ hi def link eaxFunction Function
+ hi def link networkPipeFunction Function
+ hi def link nifseFunction Function
+ hi def link reidFunction Function
+ hi def link runtimeDebuggerFunction Function
+ hi def link addActorValuesFunction Function
+ hi def link memoryDumperFunction Function
+ hi def link algoholFunction Function
+ hi def link soundCommandsFunction Function
+ hi def link emcFunction Function
+ hi def link vipcxjFunction Function
+ hi def link cameraCommands Function
+ hi def link obmeFunction Function
+ hi def link conscribeFunction Function
+ hi def link systemDialogFunction Function
+ hi def link csiFunction Function
+ hi def link haelFunction Function
+ hi def link lcdFunction Function
+ hi def link skillAttribute String
+ hi def link obDeprecated WarningMsg
+
+endif
+
+let b:current_syntax = 'obse'
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
diff --git a/runtime/syntax/openvpn.vim b/runtime/syntax/openvpn.vim
new file mode 100644
index 0000000000..02fd24bf39
--- /dev/null
+++ b/runtime/syntax/openvpn.vim
@@ -0,0 +1,72 @@
+" Vim syntax file
+" Language: OpenVPN
+" Maintainer: ObserverOfTime <chronobserver@disroot.org>
+" Filenames: *.ovpn
+" Last Change: 2022 Oct 16
+
+if exists('b:current_syntax')
+ finish
+endif
+
+let s:cpo_save = &cpoptions
+set cpoptions&vim
+
+" Options
+syntax match openvpnOption /^[a-z-]\+/
+ \ skipwhite nextgroup=openvpnArgList
+syntax match openvpnArgList /.*$/ transparent contained
+ \ contains=openvpnArgument,openvpnNumber,
+ \ openvpnIPv4Address,openvpnIPv6Address,
+ \ openvpnSignal,openvpnComment
+
+" Arguments
+syntax match openvpnArgument /[^\\"' \t]\+/
+ \ contained contains=openvpnEscape
+syntax region openvpnArgument matchgroup=openvpnQuote
+ \ start=/"/ skip=/\\"/ end=/"/
+ \ oneline contained contains=openvpnEscape
+syntax region openvpnArgument matchgroup=openvpnQuote
+ \ start=/'/ skip=/\\'/ end=/'/
+ \ oneline contained
+syntax match openvpnEscape /\\[\\" \t]/ contained
+
+" Numbers
+syntax match openvpnNumber /\<[1-9][0-9]*\(\.[0-9]\+\)\?\>/ contained
+
+" Signals
+syntax match openvpnSignal /SIG\(HUP\|INT\|TERM\|USER[12]\)/ contained
+
+" IP addresses
+syntax match openvpnIPv4Address /\(\d\{1,3}\.\)\{3}\d\{1,3}/
+ \ contained nextgroup=openvpnSlash
+syntax match openvpnIPv6Address /\([A-F0-9]\{1,4}:\)\{7}\[A-F0-9]\{1,4}/
+ \ contained nextgroup=openvpnSlash
+syntax match openvpnSlash "/" contained
+ \ nextgroup=openvpnIPv4Address,openvpnIPv6Address,openvpnNumber
+
+" Inline files
+syntax region openvpnInline matchgroup=openvpnTag
+ \ start=+^<\z([a-z-]\+\)>+ end=+^</\z1>+
+
+" Comments
+syntax keyword openvpnTodo contained TODO FIXME NOTE XXX
+syntax match openvpnComment /^[;#].*$/ contains=openvpnTodo
+syntax match openvpnComment /\s\+\zs[;#].*$/ contains=openvpnTodo
+
+hi def link openvpnArgument String
+hi def link openvpnComment Comment
+hi def link openvpnEscape SpecialChar
+hi def link openvpnIPv4Address Constant
+hi def link openvpnIPv6Address Constant
+hi def link openvpnNumber Number
+hi def link openvpnOption Keyword
+hi def link openvpnQuote Quote
+hi def link openvpnSignal Special
+hi def link openvpnSlash Delimiter
+hi def link openvpnTag Tag
+hi def link openvpnTodo Todo
+
+let b:current_syntax = 'openvpn'
+
+let &cpoptions = s:cpo_save
+unlet s:cpo_save
diff --git a/runtime/syntax/plsql.vim b/runtime/syntax/plsql.vim
index 7b36c0a180..9b4df09ac7 100644
--- a/runtime/syntax/plsql.vim
+++ b/runtime/syntax/plsql.vim
@@ -4,12 +4,16 @@
" 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)
+" Last Change: Sep 19, 2022
+" History Carsten Czarski (carsten dot czarski at oracle com)
+" add handling for typical SQL*Plus commands (rem, start, host, set, etc)
+" add error highlight for non-breaking space
+" Lee Lindley (lee dot lindley at gmail dot com)
+" use get with default 0 instead of exists per Bram suggestion
+" make procedure folding optional
" 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>.
+" revised folding
" Eugene Lysyonok (lysyonok at inbox ru)
" Added folding.
" Geoff Evans & Bill Pribyl (bill at plnet dot org)
@@ -23,12 +27,19 @@
" To enable folding (It does setlocal foldmethod=syntax)
" let plsql_fold = 1
"
+" To disable folding procedure/functions (recommended if you habitually
+" do not put the method name on the END statement)
+" let plsql_disable_procedure_fold = 1
+"
" From my vimrc file -- turn syntax and syntax folding on,
" associate file suffixes as plsql, open all the folds on file open
+" syntax enable
" 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
+" au Syntax plsql set foldcolumn=2 "optional if you want to see choosable folds on the left
+
if exists("b:current_syntax")
finish
@@ -46,15 +57,20 @@ syn case ignore
syn match plsqlGarbage "[^ \t()]"
syn match plsqlIdentifier "[a-z][a-z0-9$_#]*"
+syn match plsqlSqlPlusDefine "&&\?[a-z][a-z0-9$_#]*\.\?"
syn match plsqlHostIdentifier ":[a-z][a-z0-9$_#]*"
+" The Non-Breaking is often accidentally typed (Mac User: Opt+Space, after typing the "|", Opt+7);
+" error highlight for these avoids running into annoying compiler errors.
+syn match plsqlIllegalSpace "[\xa0]"
+
" When wanted, highlight the trailing whitespace.
-if exists("plsql_space_errors")
- if !exists("plsql_no_trail_space_error")
+if get(g:,"plsql_space_errors",0) == 1
+ if get(g:,"plsql_no_trail_space_error",0) == 0
syn match plsqlSpaceError "\s\+$"
endif
- if !exists("plsql_no_tab_space_error")
+ if get(g:,"plsql_no_tab_space_error",0) == 0
syn match plsqlSpaceError " \+\t"me=e-1
endif
endif
@@ -71,7 +87,6 @@ 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\)\>"
@@ -134,14 +149,15 @@ syn keyword plsqlKeyword CPU_TIME CRASH CREATE_FILE_DEST CREATE_STORED_OUTLINES
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 match plsqlKeyword "\<CURSOR\>"
+syn keyword plsqlKeyword 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 DEFERRABLE DEFERRED 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
@@ -180,7 +196,7 @@ syn keyword plsqlKeyword HELP HEXTORAW HEXTOREF HIDDEN HIDE HIERARCHICAL HIERARC
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 HINTSET_END 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
@@ -333,7 +349,7 @@ syn keyword plsqlKeyword SELF SEMIJOIN SEMIJOIN_DRIVER SEMI_TO_INNER SENSITIVE S
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 SHARD_CHUNK_ID SHARED SHARED_POOL SHARE_OF SHARING SHD$COL$MAP SHELFLIFE
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
@@ -436,7 +452,7 @@ syn keyword plsqlKeyword VECTOR_READ_TRACE VECTOR_TRANSFORM VECTOR_TRANSFORM_DIM
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 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
@@ -459,13 +475,13 @@ syn keyword plsqlReserved MINUS MODE NOCOMPRESS NOWAIT NUMBER_BASE OCICOLL OCIDA
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 PRAGMA PRIOR PUBLIC RAISE RECORD RELIES_ON 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
+syn keyword plsqlReserved VALUES 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
@@ -515,7 +531,7 @@ 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")
+if get(g:,"plsql_legacy_sql_keywords",0) == 1
" 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
@@ -565,7 +581,7 @@ 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
-if exists("plsql_highlight_triggers")
+if get(g:,"plsql_highlight_triggers",0) == 1
syn keyword plsqlTrigger INSERTING UPDATING DELETING
endif
@@ -575,18 +591,18 @@ syn match plsqlEND "\<END\>"
syn match plsqlISAS "\<\(IS\|AS\)\>"
" Various types of comments.
-syntax region plsqlCommentL start="--" skip="\\$" end="$" keepend extend contains=@plsqlCommentGroup,plsqlSpaceError
-if exists("plsql_fold")
+syntax region plsqlCommentL start="--" skip="\\$" end="$" keepend extend contains=@plsqlCommentGroup,plsqlSpaceError,plsqlIllegalSpace,plsqlSqlplusDefine
+if get(g:,"plsql_fold",0) == 1
syntax region plsqlComment
\ start="/\*" end="\*/"
\ extend
- \ contains=@plsqlCommentGroup,plsqlSpaceError
+ \ contains=@plsqlCommentGroup,plsqlSpaceError,plsqlIllegalSpace,plsqlSqlplusDefine
\ fold
else
syntax region plsqlComment
\ start="/\*" end="\*/"
\ extend
- \ contains=@plsqlCommentGroup,plsqlSpaceError
+ \ contains=@plsqlCommentGroup,plsqlSpaceError,plsqlIllegalSpace,plsqlSqlplusDefine
endif
syn cluster plsqlCommentAll contains=plsqlCommentL,plsqlComment
@@ -609,23 +625,23 @@ syn match plsqlFloatLiteral contained "\.\(\d\+\([eE][+-]\?\d\+\)\?\)[fd]\?"
" 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
+syn cluster plsqlIdentifiers contains=plsqlIdentifier,plsqlQuotedIdentifier,plsqlSqlPlusDefine
" 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
+if get(g:,"plsql_fold",0) == 1
+ syn region plsqlStringLiteral matchgroup=plsqlOperator start=+n\?'+ skip=+''+ end=+'+ contains=plsqlSqlplusDefine fold keepend extend
+ syn region plsqlStringLiteral matchgroup=plsqlOperator start=+n\?q'\z([^[(<{]\)+ end=+\z1'+ contains=plsqlSqlplusDefine fold keepend extend
+ syn region plsqlStringLiteral matchgroup=plsqlOperator start=+n\?q'<+ end=+>'+ contains=plsqlSqlplusDefine fold keepend extend
+ syn region plsqlStringLiteral matchgroup=plsqlOperator start=+n\?q'{+ end=+}'+ contains=plsqlSqlplusDefine fold keepend extend
+ syn region plsqlStringLiteral matchgroup=plsqlOperator start=+n\?q'(+ end=+)'+ contains=plsqlSqlplusDefine fold keepend extend
+ syn region plsqlStringLiteral matchgroup=plsqlOperator start=+n\?q'\[+ end=+]'+ contains=plsqlSqlplusDefine 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=+]'+
+ syn region plsqlStringLiteral matchgroup=plsqlOperator start=+n\?'+ skip=+''+ end=+'+ contains=plsqlSqlplusDefine
+ syn region plsqlStringLiteral matchgroup=plsqlOperator start=+n\?q'\z([^[(<{]\)+ end=+\z1'+ contains=plsqlSqlplusDefine
+ syn region plsqlStringLiteral matchgroup=plsqlOperator start=+n\?q'<+ end=+>'+ contains=plsqlSqlplusDefine
+ syn region plsqlStringLiteral matchgroup=plsqlOperator start=+n\?q'{+ end=+}'+ contains=plsqlSqlplusDefine
+ syn region plsqlStringLiteral matchgroup=plsqlOperator start=+n\?q'(+ end=+)'+ contains=plsqlSqlplusDefine
+ syn region plsqlStringLiteral matchgroup=plsqlOperator start=+n\?q'\[+ end=+]'+ contains=plsqlSqlplusDefine
endif
syn keyword plsqlBooleanLiteral TRUE FALSE
@@ -639,10 +655,10 @@ 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("plsql_bracket_error")
+if get(g:,"plsql_bracket_error",0) == 1
" 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")
+ if get(g:,"plsql_fold",0) == 1
syn region plsqlParen start='(' end=')' contains=ALLBUT,@plsqlParenGroup,plsqlErrInBracket fold keepend extend transparent
else
syn region plsqlParen transparent start='(' end=')' contains=ALLBUT,@plsqlParenGroup,plsqlErrInBracket
@@ -652,7 +668,7 @@ if exists("plsql_bracket_error")
syn region plsqlBracket transparent start='\[' end=']' contains=ALLBUT,@plsqlParenGroup,plsqlErrInParen
syn match plsqlErrInBracket contained "[);{}]"
else
- if exists("plsql_fold")
+ if get(g:,"plsql_fold",0) == 1
syn region plsqlParen start='(' end=')' contains=ALLBUT,@plsqlParenGroup,plsqlErrInParen fold keepend extend transparent
else
syn region plsqlParen transparent start='(' end=')' contains=ALLBUT,@plsqlParenGroup,plsqlErrInParen
@@ -673,12 +689,16 @@ syn match plsqlConditional "\<END\>\_s\+\<IF\>"
syn match plsqlCase "\<END\>\_s\+\<CASE\>"
syn match plsqlCase "\<CASE\>"
-if exists("plsql_fold")
+syn region plsqlSqlPlusCommentL start="^\(REM\)\( \|$\)" skip="\\$" end="$" keepend extend contains=@plsqlCommentGroup,plsqlSpaceError,plsqlIllegalSpace
+syn region plsqlSqlPlusCommand start="^\(SET\|DEFINE\|PROMPT\|ACCEPT\|EXEC\|HOST\|SHOW\|VAR\|VARIABLE\|COL\|WHENEVER\|TIMING\)\( \|$\)" skip="\\$" end="$" keepend extend
+syn region plsqlSqlPlusRunFile start="^\(@\|@@\)" skip="\\$" end="$" keepend extend
+
+if get(g:,"plsql_fold",0) == 1
setlocal foldmethod=syntax
syn sync fromstart
syn cluster plsqlProcedureGroup contains=plsqlProcedure
- syn cluster plsqlOnlyGroup contains=@plsqlProcedure,plsqlConditionalBlock,plsqlLoopBlock,plsqlBlock
+ syn cluster plsqlOnlyGroup contains=@plsqlProcedure,plsqlConditionalBlock,plsqlLoopBlock,plsqlBlock,plsqlCursor
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\>"
@@ -698,24 +718,40 @@ if exists("plsql_fold")
\ 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*;"
+ if get(g:,"plsql_disable_procedure_fold",0) == 0
+ " 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.
+ "
+ " If you habitualy do not put the method name in the END statement,
+ " this can be expensive because it searches to end of file on every
+ " procedure/function declaration
+ "
+ "\ start="\(create\(\_s\+or\_s\+replace\)\?\_s\+\)\?\<\(procedure\|function\)\>\_s\+\z([a-z][a-z0-9$_#]*\)"
+ syntax region plsqlProcedure
+ \ 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
+ endif
+
+ syntax region plsqlCursor
+ \ start="\<cursor\>\_s\+[a-z][a-z0-9$_#]*\(\_s*([^)]\+)\)\?\(\_s\+return\_s\+\S\+\)\?\_s\+is"
+ \ end=";"
\ fold
\ keepend
\ extend
\ transparent
- \ contains=ALLBUT,plsqlBlock
+ \ contains=ALLBUT,@plsqlOnlyGroup
syntax region plsqlBlock
\ start="\<begin\>"
@@ -801,8 +837,15 @@ hi def link plsqlComment2String String
hi def link plsqlTrigger Function
hi def link plsqlTypeAttribute StorageClass
hi def link plsqlTodo Todo
+
+hi def link plsqlIllegalSpace Error
+hi def link plsqlSqlPlusDefine PreProc
+hi def link plsqlSqlPlusCommand PreProc
+hi def link plsqlSqlPlusRunFile Include
+hi def link plsqlSqlPlusCommentL Comment
+
" to be able to change them after loading, need override whether defined or not
-if exists("plsql_legacy_sql_keywords")
+if get(g:,"plsql_legacy_sql_keywords",0) == 1
hi link plsqlSQLKeyword Function
hi link plsqlSymbol Normal
hi link plsqlParen Normal
diff --git a/runtime/syntax/poefilter.vim b/runtime/syntax/poefilter.vim
new file mode 100644
index 0000000000..f7e92034ee
--- /dev/null
+++ b/runtime/syntax/poefilter.vim
@@ -0,0 +1,167 @@
+" Vim syntax file
+" Language: PoE item filter
+" Maintainer: ObserverOfTime <chronobserver@disroot.org>
+" Filenames: *.filter
+" Last Change: 2022 Oct 07
+
+if exists('b:current_syntax')
+ finish
+endif
+
+let s:cpo_save = &cpoptions
+set cpoptions&vim
+
+" Comment
+syn keyword poefilterTodo TODO NOTE XXX contained
+syn match poefilterCommentTag /\[[0-9A-Z\[\]]\+\]/ contained
+syn match poefilterComment /#.*$/ contains=poefilterTodo,poefilterCommentTag,@Spell
+
+" Blocks
+syn keyword poefilterBlock Show Hide
+
+" Conditions
+syn keyword poefilterCondition
+ \ AlternateQuality
+ \ AnyEnchantment
+ \ BlightedMap
+ \ Corrupted
+ \ ElderItem
+ \ ElderMap
+ \ FracturedItem
+ \ Identified
+ \ Mirrored
+ \ Replica
+ \ Scourged
+ \ ShapedMap
+ \ ShaperItem
+ \ SynthesisedItem
+ \ UberBlightedMap
+ \ skipwhite nextgroup=poefilterBoolean
+syn keyword poefilterCondition
+ \ ArchnemesisMod
+ \ BaseType
+ \ Class
+ \ EnchantmentPassiveNode
+ \ HasEnchantment
+ \ HasExplicitMod
+ \ ItemLevel
+ \ SocketGroup
+ \ Sockets
+ \ skipwhite nextgroup=poefilterOperator,poefilterString
+syn keyword poefilterCondition
+ \ AreaLevel
+ \ BaseArmour
+ \ BaseDefencePercentile
+ \ BaseEnergyShield
+ \ BaseEvasion
+ \ BaseWard
+ \ CorruptedMods
+ \ DropLevel
+ \ EnchantmentPassiveNum
+ \ GemLevel
+ \ HasEaterOfWorldsImplicit
+ \ HasSearingExarchImplicit
+ \ Height
+ \ LinkedSockets
+ \ MapTier
+ \ Quality
+ \ StackSize
+ \ Width
+ \ skipwhite nextgroup=poefilterOperator,poefilterNumber
+syn keyword poefilterCondition
+ \ GemQualityType
+ \ skipwhite nextgroup=poefilterString,poefilterQuality
+syn keyword poefilterCondition
+ \ HasInfluence
+ \ skipwhite nextgroup=poefilterString,poefilterInfluence
+syn keyword poefilterCondition
+ \ Rarity
+ \ skipwhite nextgroup=poefilterString,poefilterRarity
+
+" Actions
+syn keyword poefilterAction
+ \ PlayAlertSound
+ \ PlayAlertSoundPositional
+ \ skipwhite nextgroup=poefilterNumber,poefilterDisable
+syn keyword poefilterAction
+ \ CustomAlertSound
+ \ CustomAlertSoundOptional
+ \ skipwhite nextgroup=poefilterString
+syn keyword poefilterAction
+ \ DisableDropSound
+ \ EnableDropSound
+ \ DisableDropSoundIfAlertSound
+ \ EnableDropSoundIfAlertSound
+ \ skipwhite nextgroup=poefilterBoolean
+syn keyword poefilterAction
+ \ MinimapIcon
+ \ SetBackgroundColor
+ \ SetBorderColor
+ \ SetFontSize
+ \ SetTextColor
+ \ skipwhite nextgroup=poefilterNumber
+syn keyword poefilterAction
+ \ PlayEffect
+ \ skipwhite nextgroup=poefilterColour
+
+" Operators
+syn match poefilterOperator /!\|[<>=]=\?/ contained
+ \ skipwhite nextgroup=poefilterString,poefilterNumber,
+ \ poefilterQuality,poefilterRarity,poefilterInfluence
+
+" Arguments
+syn match poefilterString /[-a-zA-Z0-9:,']/ contained contains=@Spell
+ \ skipwhite nextgroup=poefilterString,poefilterNumber,
+ \ poefilterQuality,poefilterRarity,poefilterInfluence
+syn region poefilterString matchgroup=poefilterQuote keepend
+ \ start=/"/ end=/"/ concealends contained contains=@Spell
+ \ skipwhite nextgroup=poefilterString,poefilterNumber,
+ \ poefilterQuality,poefilterRarity,poefilterInfluence
+syn match poefilterNumber /-1\|0\|[1-9][0-9]*/ contained
+ \ skipwhite nextgroup=poefilterString,poefilterNumber,
+ \ poefilterQuality,poefilterRarity,poefilterInfluence,poefilterColour
+syn keyword poefilterBoolean True False contained
+
+" Special arguments (conditions)
+syn keyword poefilterQuality Superior Divergent Anomalous Phantasmal
+ \ contained skipwhite nextgroup=poefilterString,poefilterQuality
+syn keyword poefilterRarity Normal Magic Rare Unique
+ \ contained skipwhite nextgroup=poefilterString,poefilterRarity
+syn keyword poefilterInfluence Shaper Elder
+ \ Crusader Hunter Redeemer Warlord None
+ \ contained skipwhite nextgroup=poefilterString,poefilterInfluence
+
+" Special arguments (actions)
+syn keyword poefilterColour Red Green Blue Brown
+ \ White Yellow Cyan Grey Orange Pink Purple
+ \ contained skipwhite nextgroup=poefilterShape,poefilterTemp
+syn keyword poefilterShape Circle Diamond Hecagon Square Star Triangle
+ \ Cross Moon Raindrop Kite Pentagon UpsideDownHouse contained
+syn keyword poefilterDisable None contained
+syn keyword poefilterTemp Temp contained
+
+" Colours
+
+hi def link poefilterAction Statement
+hi def link poefilterBlock Structure
+hi def link poefilterBoolean Boolean
+hi def link poefilterColour Special
+hi def link poefilterComment Comment
+hi def link poefilterCommentTag SpecialComment
+hi def link poefilterCondition Conditional
+hi def link poefilterDisable Constant
+hi def link poefilterInfluence Special
+hi def link poefilterNumber Number
+hi def link poefilterOperator Operator
+hi def link poefilterQuality Special
+hi def link poefilterQuote Delimiter
+hi def link poefilterRarity Special
+hi def link poefilterShape Special
+hi def link poefilterString String
+hi def link poefilterTemp StorageClass
+hi def link poefilterTodo Todo
+
+let b:current_syntax = 'poefilter'
+
+let &cpoptions = s:cpo_save
+unlet s:cpo_save
diff --git a/runtime/syntax/ptcap.vim b/runtime/syntax/ptcap.vim
index 1ebeb5227b..5db7bda896 100644
--- a/runtime/syntax/ptcap.vim
+++ b/runtime/syntax/ptcap.vim
@@ -53,7 +53,7 @@ syn match ptcapNumberError "#0x\x*[^[:xdigit:]:\\]"lc=1 contained
" The `=' operator assigns a string to the preceding flag
syn match ptcapOperator "[@#=]" contained
-" Some terminal capabilites have special names like `#5' and `@1', and we
+" Some terminal capabilities have special names like `#5' and `@1', and we
" need special rules to match these properly
syn match ptcapSpecialCap "\W[#@]\d" contains=ptcapDelimiter contained
diff --git a/runtime/syntax/racket.vim b/runtime/syntax/racket.vim
new file mode 100644
index 0000000000..b1ed2b454c
--- /dev/null
+++ b/runtime/syntax/racket.vim
@@ -0,0 +1,656 @@
+" Vim syntax file
+" Language: Racket
+" Maintainer: D. Ben Knoble <ben.knoble+github@gmail.com>
+" Previous Maintainer: Will Langstroth <will@langstroth.com>
+" URL: https://github.com/benknoble/vim-racket
+" Description: Contains all of the keywords in #lang racket
+" Last Change: 2022 Aug 12
+
+" Initializing:
+if exists("b:current_syntax")
+ finish
+endif
+
+" Highlight unmatched parens
+syntax match racketError ,[]})],
+
+if version < 800
+ set iskeyword=33,35-39,42-58,60-90,94,95,97-122,126,_
+else
+ " syntax iskeyword 33,35-39,42-58,60-90,94,95,97-122,126,_
+ " converted from decimal to char
+ " :s/\d\+/\=submatch(0)->str2nr()->nr2char()/g
+ " but corrected to remove duplicate _, move ^ to end
+ syntax iskeyword @,!,#-',*-:,<-Z,a-z,~,_,^
+ " expanded
+ " syntax iskeyword !,#,$,%,&,',*,+,,,-,.,/,0-9,:,<,=,>,?,@,A-Z,_,a-z,~,^
+endif
+
+" Forms in order of appearance at
+" http://docs.racket-lang.org/reference/index.html
+"
+syntax keyword racketSyntax module module* module+ require provide quote
+syntax keyword racketSyntax #%datum #%expression #%top #%variable-reference #%app
+syntax keyword racketSyntax lambda case-lambda let let* letrec
+syntax keyword racketSyntax let-values let*-values let-syntax letrec-syntax
+syntax keyword racketSyntax let-syntaxes letrec-syntaxes letrec-syntaxes+values
+syntax keyword racketSyntax local shared
+syntax keyword racketSyntax if cond and or case define else =>
+syntax keyword racketSyntax define define-values define-syntax define-syntaxes
+syntax keyword racketSyntax define-for-syntax define-require-syntax define-provide-syntax
+syntax keyword racketSyntax define-syntax-rule
+syntax keyword racketSyntax define-record-type
+syntax keyword racketSyntax begin begin0
+syntax keyword racketSyntax begin-for-syntax
+syntax keyword racketSyntax when unless
+syntax keyword racketSyntax set! set!-values
+syntax keyword racketSyntax for for/list for/vector for/hash for/hasheq for/hasheqv
+syntax keyword racketSyntax for/and for/or for/lists for/first
+syntax keyword racketSyntax for/last for/fold
+syntax keyword racketSyntax for* for*/list for*/vector for*/hash for*/hasheq for*/hasheqv
+syntax keyword racketSyntax for*/and for*/or for*/lists for*/first
+syntax keyword racketSyntax for*/last for*/fold
+syntax keyword racketSyntax for/fold/derived for*/fold/derived
+syntax keyword racketSyntax define-sequence-syntax :do-in do
+syntax keyword racketSyntax with-continuation-mark
+syntax keyword racketSyntax quasiquote unquote unquote-splicing quote-syntax
+syntax keyword racketSyntax #%top-interaction
+syntax keyword racketSyntax define-package open-package package-begin
+syntax keyword racketSyntax define* define*-values define*-syntax define*-syntaxes open*-package
+syntax keyword racketSyntax package? package-exported-identifiers package-original-identifiers
+syntax keyword racketSyntax block #%stratified-body
+
+" 8 Contracts
+" 8.2 Function contracts
+syntax keyword racketSyntax -> ->* ->i ->d case-> dynamic->* unconstrained-domain->
+
+" 8.6.1 Nested Contract Boundaries
+syntax keyword racketSyntax with-contract define/contract define-struct/contract
+syntax keyword racketSyntax invariant-assertion current-contract-region
+
+" 9 Pattern Matching
+syntax keyword racketSyntax match match* match/values define/match
+syntax keyword racketSyntax match-lambda match-lambda* match-lambda**
+syntax keyword racketSyntax match-let match-let* match-let-values match-let*-values
+syntax keyword racketSyntax match-letrec match-define match-define-values
+
+" 10.2.3 Handling Exceptions
+syntax keyword racketSyntax with-handlers with-handlers*
+
+" 10.4 Continuations
+syntax keyword racketSyntax let/cc let/ec
+
+" 10.4.1 Additional Control Operators
+syntax keyword racketSyntax % prompt control prompt-at control-at reset shift
+syntax keyword racketSyntax reset-at shift-at prompt0 reset0 control0 shift0
+syntax keyword racketSyntax prompt0-at reset0-at control0-at shift0-at
+syntax keyword racketSyntax set cupto
+
+" 11.3.2 Parameters
+syntax keyword racketSyntax parameterize parameterize*
+
+" 12.5 Writing
+syntax keyword racketSyntax write display displayln print
+syntax keyword racketSyntax fprintf printf eprintf format
+syntax keyword racketSyntax print-pair-curly-braces print-mpair-curly-braces print-unreadable
+syntax keyword racketSyntax print-graph print-struct print-box print-vector-length print-hash-table
+syntax keyword racketSyntax print-boolean-long-form print-reader-abbreviations print-as-expression print-syntax-width
+syntax keyword racketSyntax current-write-relative-directory port-write-handler port-display-handler
+syntax keyword racketSyntax port-print-handler global-port-print-handler
+
+" 13.7 Custodians
+syntax keyword racketSyntax custodian? custodian-memory-accounting-available? custodian-box?
+syntax keyword racketSyntax make-custodian custodian-shutdown-all current-custodian custodian-managed-list
+syntax keyword racketSyntax custodian-require-memory custodian-limit-memory
+syntax keyword racketSyntax make-custodian-box custodian-box-value
+
+" lambda sign
+syntax match racketSyntax /\<[\u03bb]\>/
+
+
+" Functions ==================================================================
+
+syntax keyword racketFunc boolean? not equal? eqv? eq? equal?/recur immutable?
+syntax keyword racketFunc true false symbol=? boolean=? false?
+syntax keyword racketFunc number? complex? real? rational? integer?
+syntax keyword racketFunc exact-integer? exact-nonnegative-integer?
+syntax keyword racketFunc exact-positive-integer? inexact-real?
+syntax keyword racketFunc fixnum? flonum? zero? positive? negative?
+syntax keyword racketFunc even? odd? exact? inexact?
+syntax keyword racketFunc inexact->exact exact->inexact
+
+" 3.2.2 General Arithmetic
+
+" 3.2.2.1 Arithmetic
+syntax keyword racketFunc + - * / quotient remainder quotient/remainder modulo
+syntax keyword racketFunc add1 sub1 abs max min gcd lcm round exact-round floor
+syntax keyword racketFunc ceiling truncate numerator denominator rationalize
+
+" 3.2.2.2 Number Comparison
+syntax keyword racketFunc = < <= > >=
+
+" 3.2.2.3 Powers and Roots
+syntax keyword racketFunc sqrt integer-sqrt integer-sqrt/remainder
+syntax keyword racketFunc expt exp log
+
+" 3.2.2.3 Trigonometric Functions
+syntax keyword racketFunc sin cos tan asin acos atan
+
+" 3.2.2.4 Complex Numbers
+syntax keyword racketFunc make-rectangular make-polar
+syntax keyword racketFunc real-part imag-part magnitude angle
+syntax keyword racketFunc bitwise-ior bitwise-and bitwise-xor bitwise-not
+syntax keyword racketFunc bitwise-bit-set? bitwise-bit-field arithmetic-shift
+syntax keyword racketFunc integer-length
+
+" 3.2.2.5 Random Numbers
+syntax keyword racketFunc random random-seed
+syntax keyword racketFunc make-pseudo-random-generator pseudo-random-generator?
+syntax keyword racketFunc current-pseudo-random-generator pseudo-random-generator->vector
+syntax keyword racketFunc vector->pseudo-random-generator vector->pseudo-random-generator!
+
+" 3.2.2.8 Number-String Conversions
+syntax keyword racketFunc number->string string->number real->decimal-string
+syntax keyword racketFunc integer->integer-bytes
+syntax keyword racketFunc floating-point-bytes->real real->floating-point-bytes
+syntax keyword racketFunc system-big-endian?
+
+" 3.2.2.9 Extra Constants and Functions
+syntax keyword racketFunc pi sqr sgn conjugate sinh cosh tanh order-of-magnitude
+
+" 3.2.3 Flonums
+
+" 3.2.3.1 Flonum Arithmetic
+syntax keyword racketFunc fl+ fl- fl* fl/ flabs
+syntax keyword racketFunc fl= fl< fl> fl<= fl>= flmin flmax
+syntax keyword racketFunc flround flfloor flceiling fltruncate
+syntax keyword racketFunc flsin flcos fltan flasin flacos flatan
+syntax keyword racketFunc fllog flexp flsqrt
+syntax keyword racketFunc ->fl fl->exact-integer make-flrectangular
+syntax keyword racketFunc flreal-part flimag-part
+
+" 3.2.3.2 Flonum Vectors
+syntax keyword racketFunc flvector? flvector make-flvector flvector-length
+syntax keyword racketFunc flvector-ref flvector-set! flvector-copy in-flvector
+syntax keyword racketFunc shared-flvector make-shared-flvector
+syntax keyword racketSyntax for/flvector for*/flvector
+
+" 3.2.4 Fixnums
+syntax keyword racketFunc fx+ fx- fx* fxquotient fxremainder fxmodulo fxabs
+syntax keyword racketFunc fxand fxior fxxor fxnot fxlshift fxrshift
+syntax keyword racketFunc fx= fx< fx> fx<= fx>= fxmin fxmax fx->fl fl->fx
+
+" 3.2.4.2 Fixnum Vectors
+syntax keyword racketFunc fxvector? fxvector make-fxvector fxvector-length
+syntax keyword racketFunc fxvector-ref fxvector-set! fxvector-copy in-fxvector
+syntax keyword racketFunc for/fxvector for*/fxvector
+syntax keyword racketFunc shared-fxvector make-shared-fxvector
+
+" 3.3 Strings
+syntax keyword racketFunc string? make-string string string->immutable-string string-length
+syntax keyword racketFunc string-ref string-set! substring string-copy string-copy!
+syntax keyword racketFunc string-fill! string-append string->list list->string
+syntax keyword racketFunc build-string string=? string<? string<=? string>? string>=?
+syntax keyword racketFunc string-ci=? string-ci<? string-ci<=? string-ci>? string-ci>=?
+syntax keyword racketFunc string-upcase string-downcase string-titlecase string-foldcase
+syntax keyword racketFunc string-normalize-nfd string-normalize-nfc string-normalize-nfkc
+syntax keyword racketFunc string-normalize-spaces string-trim
+syntax keyword racketFunc string-locale=? string-locale>? string-locale<?
+syntax keyword racketFunc string-locale-ci=? string-locale<=?
+syntax keyword racketFunc string-locale-upcase string-locale-downcase
+syntax keyword racketFunc string-append* string-join
+
+" 3.4 Bytestrings
+syntax keyword racketFunc bytes? make-bytes bytes bytes->immutable-bytes byte?
+syntax keyword racketFunc bytes-length bytes-ref bytes-set! subbytes bytes-copy
+syntax keyword racketFunc bytes-copy! bytes-fill! bytes-append bytes->list list->bytes
+syntax keyword racketFunc make-shared-bytes shared-bytes
+syntax keyword racketFunc bytes=? bytes<? bytes>?
+syntax keyword racketFunc bytes->string/utf-8 bytes->string/latin-1
+syntax keyword racketFunc string->bytes/locale string->bytes/latin-1 string->bytes/utf-8
+syntax keyword racketFunc string-utf-8-length bytes-utf8-ref bytes-utf-8-index
+syntax keyword racketFunc bytes-open-converter bytes-close-converter
+syntax keyword racketFunc bytes-convert bytes-convert-end bytes-converter?
+syntax keyword racketFunc locale-string-encoding
+
+" 3.5 Characters
+syntax keyword racketFunc char? char->integer integer->char
+syntax keyword racketFunc char=? char<? char<=? char>? char>=?
+syntax keyword racketFunc char-ci=? char-ci<? char-ci<=? char-ci>? char-ci>=?
+syntax keyword racketFunc char-alphabetic? char-lower-case? char-upper-case? char-title-case?
+syntax keyword racketFunc char-numeric? char-symbolic? char-punctuation? char-graphic?
+syntax keyword racketFunc char-whitespace? char-blank?
+syntax keyword racketFunc char-iso-control? char-general-category
+syntax keyword racketFunc make-known-char-range-list
+syntax keyword racketFunc char-upcase char-downcase char-titlecase char-foldcase
+
+" 3.6 Symbols
+syntax keyword racketFunc symbol? symbol-interned? symbol-unreadable?
+syntax keyword racketFunc symbol->string string->symbol
+syntax keyword racketFunc string->uninterned-symbol string->unreadable-symbol
+syntax keyword racketFunc gensym
+
+" 3.7 Regular Expressions
+syntax keyword racketFunc regexp? pregexp? byte-regexp? byte-pregexp?
+syntax keyword racketFunc regexp pregexp byte-regexp byte-pregexp
+syntax keyword racketFunc regexp-quote regexp-match regexp-match*
+syntax keyword racketFunc regexp-try-match regexp-match-positions
+syntax keyword racketFunc regexp-match-positions* regexp-match?
+syntax keyword racketFunc regexp-match-peek-positions regexp-match-peek-immediate
+syntax keyword racketFunc regexp-match-peek regexp-match-peek-positions*
+syntax keyword racketFunc regexp-match/end regexp-match-positions/end
+syntax keyword racketFunc regexp-match-peek-positions-immediat/end
+syntax keyword racketFunc regexp-split regexp-replace regexp-replace*
+syntax keyword racketFunc regexp-replace-quote
+
+" 3.8 Keywords
+syntax keyword racketFunc keyword? keyword->string string->keyword keyword<?
+
+" 3.9 Pairs and Lists
+syntax keyword racketFunc pair? null? cons car cdr null
+syntax keyword racketFunc list? list list* build-list length
+syntax keyword racketFunc list-ref list-tail append reverse map andmap ormap
+syntax keyword racketFunc for-each foldl foldr filter remove remq remv remove*
+syntax keyword racketFunc remq* remv* sort member memv memq memf
+syntax keyword racketFunc findf assoc assv assq assf
+syntax keyword racketFunc caar cadr cdar cddr caaar caadr cadar caddr cdaar
+syntax keyword racketFunc cddar cdddr caaaar caaadr caadar caaddr cadadr caddar
+syntax keyword racketFunc cadddr cdaaar cdaadr cdadar cddaar cdddar cddddr
+
+" 3.9.7 Additional List Functions and Synonyms
+" (require racket/list)
+syntax keyword racketFunc empty cons? empty? first rest
+syntax keyword racketFunc second third fourth fifth sixth seventh eighth ninth tenth
+syntax keyword racketFunc last last-pair make-list take drop split-at
+syntax keyword racketFunc take-right drop-right split-at-right add-between
+syntax keyword racketFunc append* flatten remove-duplicates filter-map
+syntax keyword racketFunc count partition append-map filter-not shuffle
+syntax keyword racketFunc argmin argmax make-reader-graph placeholder? make-placeholder
+syntax keyword racketFunc placeholder-set! placeholder-get hash-placeholder?
+syntax keyword racketFunc make-hash-placeholder make-hasheq-placeholder
+syntax keyword racketFunc make-hasheqv-placeholder make-immutable-hasheqv
+
+" 3.10 Mutable Pairs and Lists
+syntax keyword racketFunc mpair? mcons mcar mcdr
+
+" 3.11 Vectors
+syntax keyword racketFunc vector? make-vector vector vector-immutable vector-length
+syntax keyword racketFunc vector-ref vector-set! vector->list list->vector
+syntax keyword racketFunc vector->immutable-vector vector-fill! vector-copy!
+syntax keyword racketFunc vector->values build-vector vector-set*! vector-map
+syntax keyword racketFunc vector-map! vector-append vector-take vector-take-right
+syntax keyword racketFunc vector-drop vector-drop-right vector-split-at
+syntax keyword racketFunc vector-split-at-right vector-copy vector-filter
+syntax keyword racketFunc vector-filter-not vector-count vector-argmin vector-argmax
+syntax keyword racketFunc vector-member vector-memv vector-memq
+
+" 3.12 Boxes
+syntax keyword racketFunc box? box box-immutable unbox set-box!
+
+" 3.13 Hash Tables
+syntax keyword racketFunc hash? hash-equal? hash-eqv? hash-eq? hash-weak? hash
+syntax keyword racketFunc hasheq hasheqv
+syntax keyword racketFunc make-hash make-hasheqv make-hasheq make-weak-hash make-weak-hasheqv
+syntax keyword racketFunc make-weak-hasheq make-immutable-hash make-immutable-hasheqv
+syntax keyword racketFunc make-immutable-hasheq
+syntax keyword racketFunc hash-set! hash-set*! hash-set hash-set* hash-ref hash-ref!
+syntax keyword racketFunc hash-has-key? hash-update! hash-update hash-remove!
+syntax keyword racketFunc hash-remove hash-map hash-keys hash-values
+syntax keyword racketFunc hash->list hash-for-each hash-count
+syntax keyword racketFunc hash-iterate-first hash-iterate-next hash-iterate-key
+syntax keyword racketFunc hash-iterate-value hash-copy eq-hash-code eqv-hash-code
+syntax keyword racketFunc equal-hash-code equal-secondary-hash-code
+
+" 3.15 Dictionaries
+syntax keyword racketFunc dict? dict-mutable? dict-can-remove-keys? dict-can-functional-set?
+syntax keyword racketFunc dict-set! dict-set*! dict-set dict-set* dict-has-key? dict-ref
+syntax keyword racketFunc dict-ref! dict-update! dict-update dict-remove! dict-remove
+syntax keyword racketFunc dict-map dict-for-each dict-count dict-iterate-first dict-iterate-next
+syntax keyword racketFunc dict-iterate-key dict-iterate-value in-dict in-dict-keys
+syntax keyword racketFunc in-dict-values in-dict-pairs dict-keys dict-values
+syntax keyword racketFunc dict->list prop: dict prop: dict/contract dict-key-contract
+syntax keyword racketFunc dict-value-contract dict-iter-contract make-custom-hash
+syntax keyword racketFunc make-immutable-custom-hash make-weak-custom-hash
+
+" 3.16 Sets
+syntax keyword racketFunc set seteqv seteq set-empty? set-count set-member?
+syntax keyword racketFunc set-add set-remove set-union set-intersect set-subtract
+syntax keyword racketFunc set-symmetric-difference set=? subset? proper-subset?
+syntax keyword racketFunc set-map set-for-each set? set-equal? set-eqv? set-eq?
+syntax keyword racketFunc set/c in-set for/set for/seteq for/seteqv for*/set
+syntax keyword racketFunc for*/seteq for*/seteqv list->set list->seteq
+syntax keyword racketFunc list->seteqv set->list
+
+" 3.17 Procedures
+syntax keyword racketFunc procedure? apply compose compose1 procedure-rename procedure->method
+syntax keyword racketFunc keyword-apply procedure-arity procedure-arity?
+syntax keyword racketFunc procedure-arity-includes? procedure-reduce-arity
+syntax keyword racketFunc procedure-keywords make-keyword-procedure
+syntax keyword racketFunc procedure-reduce-keyword-arity procedure-struct-type?
+syntax keyword racketFunc procedure-extract-target checked-procedure-check-and-extract
+syntax keyword racketFunc primitive? primitive-closure? primitive-result-arity
+syntax keyword racketFunc identity const thunk thunk* negate curry curryr
+
+" 3.18 Void
+syntax keyword racketFunc void void?
+
+" 4.1 Defining Structure Types
+syntax keyword racketFunc struct struct-field-index define-struct define-struct define-struct/derived
+
+" 4.2 Creating Structure Types
+syntax keyword racketFunc make-struct-type make-struct-field-accessor make-struct-field-mutator
+
+" 4.3 Structure Type Properties
+syntax keyword racketFunc make-struct-type-property struct-type-property? struct-type-property-accessor-procedure?
+
+" 4.4 Copying and Updating Structures
+syntax keyword racketFunc struct-copy
+
+" 4.5 Structure Utilities
+syntax keyword racketFunc struct->vector struct? struct-type?
+syntax keyword racketFunc struct-constructor-procedure? struct-predicate-procedure? struct-accessor-procedure? struct-mutator-procedure?
+syntax keyword racketFunc prefab-struct-key make-prefab-struct prefab-key->struct-type
+
+" 4.6 Structure Type Transformer Binding
+syntax keyword racketFunc struct-info? check-struct-info? make-struct-info extract-struct-info
+syntax keyword racketFunc struct-auto-info? struct-auto-info-lists
+
+" 5.1 Creating Interfaces
+syntax keyword racketFunc interface interface*
+
+" 5.2 Creating Classes
+syntax keyword racketFunc class* class inspect
+syntax keyword racketFunc init init-field field inherit field init-rest
+syntax keyword racketFunc public public* pubment pubment* public-final public-final*
+syntax keyword racketFunc override override* overment overment* override-final override-final*
+syntax keyword racketFunc augride augride* augment augment* augment-final augment-final*
+syntax keyword racketFunc abstract inherit inherit/super inherit/inner
+syntax keyword racketFunc rename-inner rename-super
+syntax keyword racketFunc define/public define/pubment define/public-final
+syntax keyword racketFunc define/override define/overment define/override-final
+syntax keyword racketFunc define/augride define/augment define/augment-final
+syntax keyword racketFunc private* define/private
+
+" 5.2.3 Methods
+syntax keyword racketFunc class/derived
+syntax keyword racketFunc super inner define-local-member-name define-member-name
+syntax keyword racketFunc member-name-key generate-member-key member-name-key?
+syntax keyword racketFunc member-name-key=? member-name-key-hash-code
+
+" 5.3 Creating Objects
+syntax keyword racketFunc make-object instantiate new
+syntax keyword racketFunc super-make-object super-instantiate super-new
+
+"5.4 Field and Method Access
+syntax keyword racketFunc method-id send send/apply send/keyword-apply dynamic-send send*
+syntax keyword racketFunc get-field set-field! field-bound?
+syntax keyword racketFunc class-field-accessor class-field-mutator
+
+"5.4.3 Generics
+syntax keyword racketFunc generic send-generic make-generic
+
+" 8.1 Data-strucure contracts
+syntax keyword racketFunc flat-contract-with-explanation flat-named-contract
+" TODO where do any/c and none/c `value`s go?
+syntax keyword racketFunc or/c first-or/c and/c not/c =/c </c >/c <=/c >=/c
+syntax keyword racketFunc between/c real-in integer-in char-in natural-number/c
+syntax keyword racketFunc string-len/c printable/c one-of/c symbols vectorof
+syntax keyword racketFunc vector-immutableof vector/c box/c box-immutable/c listof
+syntax keyword racketFunc non-empty-listof list*of cons/c cons/dc list/c *list/c
+syntax keyword racketFunc syntax/c struct/c struct/dc parameter/c
+syntax keyword racketFunc procedure-arity-includes/c hash/c hash/dc channel/c
+syntax keyword racketFunc prompt-tag/c continuation-mark-key/c evt/c promise/c
+syntax keyword racketFunc flat-contract flat-contract-predicate suggest/c
+
+" 9.1 Multiple Values
+syntax keyword racketFunc values call-with-values
+
+" 10.2.2 Raising Exceptions
+syntax keyword racketFunc raise error raise-user-error raise-argument-error
+syntax keyword racketFunc raise-result-error raise-argument-error raise-range-error
+syntax keyword racketFunc raise-type-error raise-mismatch-error raise-arity-error
+syntax keyword racketFunc raise-syntax-error
+
+" 10.2.3 Handling Exceptions
+syntax keyword racketFunc call-with-exception-handler uncaught-exception-handler
+
+" 10.2.4 Configuring Default Handlers
+syntax keyword racketFunc error-escape-handler error-display-handler error-print-width
+syntax keyword racketFunc error-print-context-length error-values->string-handler
+syntax keyword racketFunc error-print-source-location
+
+" 10.2.5 Built-in Exception Types
+syntax keyword racketFunc exn exn:fail exn:fail:contract exn:fail:contract:arity
+syntax keyword racketFunc exn:fail:contract:divide-by-zero exn:fail:contract:non-fixnum-result
+syntax keyword racketFunc exn:fail:contract:continuation exn:fail:contract:variable
+syntax keyword racketFunc exn:fail:syntax exn:fail:syntax:unbound exn:fail:syntax:missing-module
+syntax keyword racketFunc exn:fail:read exn:fail:read:eof exn:fail:read:non-char
+syntax keyword racketFunc exn:fail:filesystem exn:fail:filesystem:exists
+syntax keyword racketFunc exn:fail:filesystem:version exn:fail:filesystem:errno
+syntax keyword racketFunc exn:fail:filesystem:missing-module
+syntax keyword racketFunc exn:fail:network exn:fail:network:errno exn:fail:out-of-memory
+syntax keyword racketFunc exn:fail:unsupported exn:fail:user
+syntax keyword racketFunc exn:break exn:break:hang-up exn:break:terminate
+
+" 10.3 Delayed Evaluation
+syntax keyword racketFunc promise? delay lazy force promise-forced? promise-running?
+
+" 10.3.1 Additional Promise Kinds
+syntax keyword racketFunc delay/name promise/name delay/strict delay/sync delay/thread delay/idle
+
+" 10.4 Continuations
+syntax keyword racketFunc call-with-continuation-prompt abort-current-continuation make-continuation-prompt-tag
+syntax keyword racketFunc default-continuation-prompt-tag call-with-current-continuation call/cc
+syntax keyword racketFunc call-with-composable-continuation call-with-escape-continuation call/ec
+syntax keyword racketFunc call-with-continuation-barrier continuation-prompt-available
+syntax keyword racketFunc continuation? continuation-prompt-tag dynamic-wind
+
+" 10.4.1 Additional Control Operators
+syntax keyword racketFunc call/prompt abort/cc call/comp abort fcontrol spawn splitter new-prompt
+
+" 11.3.2 Parameters
+syntax keyword racketFunc make-parameter make-derived-parameter parameter?
+syntax keyword racketFunc parameter-procedure=? current-parameterization
+syntax keyword racketFunc call-with-parameterization parameterization?
+
+" 14.1.1 Manipulating Paths
+syntax keyword racketFunc path? path-string? path-for-some-system? string->path path->string path->bytes
+syntax keyword racketFunc string->path-element bytes->path-element path-element->string path-element->bytes
+syntax keyword racketFunc path-convention-type system-path-convention-type build-type
+syntax keyword racketFunc build-type/convention-type
+syntax keyword racketFunc absolute-path? relative-path? complete-path?
+syntax keyword racketFunc path->complete-path path->directory-path
+syntax keyword racketFunc resolve-path cleanse-path expand-user-path simplify-path normal-case-path split-path
+syntax keyword racketFunc path-replace-suffix path-add-suffix
+
+" 14.1.2 More Path Utilities
+syntax keyword racketFunc explode-path file-name-from-path filename-extension find-relative-path normalize-path
+syntax keyword racketFunc path-element? path-only simple-form-path some-simple-path->string string->some-system-path
+
+" 15.6 Time
+syntax keyword racketFunc current-seconds current-inexact-milliseconds
+syntax keyword racketFunc seconds->date current-milliseconds
+
+
+syntax match racketDelimiter !\<\.\>!
+
+syntax cluster racketTop contains=racketSyntax,racketFunc,racketDelimiter
+
+syntax match racketConstant ,\<\*\k\+\*\>,
+syntax match racketConstant ,\<<\k\+>\>,
+
+" Non-quoted lists, and strings
+syntax region racketStruc matchgroup=racketParen start="("rs=s+1 end=")"re=e-1 contains=@racketTop
+syntax region racketStruc matchgroup=racketParen start="#("rs=s+2 end=")"re=e-1 contains=@racketTop
+syntax region racketStruc matchgroup=racketParen start="{"rs=s+1 end="}"re=e-1 contains=@racketTop
+syntax region racketStruc matchgroup=racketParen start="#{"rs=s+2 end="}"re=e-1 contains=@racketTop
+syntax region racketStruc matchgroup=racketParen start="\["rs=s+1 end="\]"re=e-1 contains=@racketTop
+syntax region racketStruc matchgroup=racketParen start="#\["rs=s+2 end="\]"re=e-1 contains=@racketTop
+
+for lit in ['hash', 'hasheq', 'hasheqv']
+ execute printf('syntax match racketLit "\<%s\>" nextgroup=@racketParen containedin=ALLBUT,.*String,.*Comment', '#'.lit)
+endfor
+
+for lit in ['rx', 'rx#', 'px', 'px#']
+ execute printf('syntax match racketRe "\<%s\>" nextgroup=@racketString containedin=ALLBUT,.*String,.*Comment,', '#'.lit)
+endfor
+
+unlet lit
+
+" Simple literals
+
+" Strings
+
+syntax match racketStringEscapeError "\\." contained display
+
+syntax match racketStringEscape "\\[abtnvfre'"\\]" contained display
+syntax match racketStringEscape "\\$" contained display
+syntax match racketStringEscape "\\\o\{1,3}\|\\x\x\{1,2}" contained display
+
+syntax match racketUStringEscape "\\u\x\{1,4}\|\\U\x\{1,8}" contained display
+syntax match racketUStringEscape "\\u\x\{4}\\u\x\{4}" contained display
+
+syntax region racketString start=/\%(\\\)\@<!"/ skip=/\\[\\"]/ end=/"/ contains=racketStringEscapeError,racketStringEscape,racketUStringEscape
+syntax region racketString start=/#"/ skip=/\\[\\"]/ end=/"/ contains=racketStringEscapeError,racketStringEscape
+
+if exists("racket_no_string_fold")
+ syn region racketString start=/#<<\z(.*\)$/ end=/^\z1$/
+else
+ syn region racketString start=/#<<\z(.*\)$/ end=/^\z1$/ fold
+endif
+
+
+syntax cluster racketTop add=racketError,racketConstant,racketStruc,racketString
+
+" Numbers
+
+" anything which doesn't match the below rules, but starts with a #d, #b, #o,
+" #x, #i, or #e, is an error
+syntax match racketNumberError "\<#[xdobie]\k*"
+
+syntax match racketContainedNumberError "\<#o\k*[^-+0-7delfinas#./@]\>"
+syntax match racketContainedNumberError "\<#b\k*[^-+01delfinas#./@]\>"
+syntax match racketContainedNumberError "\<#[ei]#[ei]"
+syntax match racketContainedNumberError "\<#[xdob]#[xdob]"
+
+" start with the simpler sorts
+syntax match racketNumber "\<\(#[dobie]\)\{0,2}[-+]\?\(\d\+\|\d\+#*\.\|\d*\.\d\+\)#*\(/\d\+#*\)\?\([sdlef][-+]\?\d\+#*\)\?\>" contains=racketContainedNumberError
+syntax match racketNumber "\<\(#[dobie]\)\{0,2}[-+]\?\d\+/\d\+\>" contains=racketContainedNumberError
+syntax match racketNumber "\<\(#[dobie]\)\{0,2}[-+]\?\d\+/\d\+[-+]\d\+\(/\d\+\)\?i\>" contains=racketContainedNumberError
+
+" different possible ways of expressing complex values
+syntax match racketNumber "\<\(#[dobie]\)\{0,2}[-+]\(\d\+\|\d\+#*\.\|\d*\.\d\+\)#*\(/\d\+#*\)\?\([sdlef][-+]\?\d\+#*\)\?i\>" contains=racketContainedNumberError
+syntax match racketNumber "\<\(#[dobie]\)\{0,2}[-+]\?\(\d\+\|\d\+#*\.\|\d*\.\d\+\)#*\(/\d\+#*\)\?\([sdlef][-+]\?\d\+#*\)\?[-+]\(\d\+\|\d\+#*\.\|\d*\.\d\+\)#*\(/\d\+#*\)\?\([sdlef][-+]\?\d\+#*\)\?i\>" contains=racketContainedNumberError
+syntax match racketNumber "\<\(#[dobie]\)\{0,2}[-+]\(inf\|nan\)\.[0f][-+]\(\d\+\|\d\+#*\.\|\d*\.\d\+\)#*\(/\d\+#*\)\?\([sdlef][-+]\?\d\+#*\)\?i\>" contains=racketContainedNumberError
+syntax match racketNumber "\<\(#[dobie]\)\{0,2}[-+]\?\(\d\+\|\d\+#*\.\|\d*\.\d\+\)#*\(/\d\+#*\)\?\([sdlef][-+]\?\d\+#*\)\?[-+]\(inf\|nan\)\.[0f]i\>" contains=racketContainedNumberError
+syntax match racketNumber "\<\(#[dobie]\)\{0,2}[-+]\?\(\d\+\|\d\+#*\.\|\d*\.\d\+\)#*\(/\d\+#*\)\?\([sdlef][-+]\?\d\+#*\)\?@[-+]\?\(\d\+\|\d\+#*\.\|\d*\.\d\+\)#*\(/\d\+#*\)\?\([sdlef][-+]\?\d\+#*\)\?\>" contains=racketContainedNumberError
+syntax match racketNumber "\<\(#[dobie]\)\{0,2}[-+]\(inf\|nan\)\.[0f]@[-+]\?\(\d\+\|\d\+#*\.\|\d*\.\d\+\)#*\(/\d\+#*\)\?\([sdlef][-+]\?\d\+#*\)\?\>" contains=racketContainedNumberError
+syntax match racketNumber "\<\(#[dobie]\)\{0,2}[-+]\?\(\d\+\|\d\+#*\.\|\d*\.\d\+\)#*\(/\d\+#*\)\?\([sdlef][-+]\?\d\+#*\)\?@[-+]\(inf\|nan\)\.[0f]\>" contains=racketContainedNumberError
+
+" hex versions of the above (separate because of the different possible exponent markers)
+syntax match racketNumber "\<\(#x\|#[ei]#x\|#x#[ei]\)[-+]\?\(\x\+\|\x\+#*\.\|\x*\.\x\+\)#*\(/\x\+#*\)\?\([sl][-+]\?\x\+#*\)\?\>"
+syntax match racketNumber "\<\(#x\|#[ei]#x\|#x#[ei]\)[-+]\?\x\+/\x\+\>"
+syntax match racketNumber "\<\(#x\|#[ei]#x\|#x#[ei]\)[-+]\?\x\+/\x\+[-+]\x\+\(/\x\+\)\?i\>"
+
+syntax match racketNumber "\<\(#x\|#[ei]#x\|#x#[ei]\)[-+]\(\x\+\|\x\+#*\.\|\x*\.\x\+\)#*\(/\x\+#*\)\?\([sl][-+]\?\x\+#*\)\?i\>"
+syntax match racketNumber "\<\(#x\|#[ei]#x\|#x#[ei]\)[-+]\?\(\x\+\|\x\+#*\.\|\x*\.\x\+\)#*\(/\x\+#*\)\?\([sl][-+]\?\x\+#*\)\?[-+]\(\x\+\|\x\+#*\.\|\x*\.\x\+\)#*\(/\x\+#*\)\?\([sl][-+]\?\x\+#*\)\?i\>"
+syntax match racketNumber "\<\(#x\|#[ei]#x\|#x#[ei]\)[-+]\(inf\|nan\)\.[0f][-+]\(\x\+\|\x\+#*\.\|\x*\.\x\+\)#*\(/\x\+#*\)\?\([sl][-+]\?\x\+#*\)\?i\>"
+syntax match racketNumber "\<\(#x\|#[ei]#x\|#x#[ei]\)[-+]\?\(\x\+\|\x\+#*\.\|\x*\.\x\+\)#*\(/\x\+#*\)\?\([sl][-+]\?\x\+#*\)\?[-+]\(inf\|nan\)\.[0f]i\>"
+syntax match racketNumber "\<\(#x\|#[ei]#x\|#x#[ei]\)[-+]\?\(\x\+\|\x\+#*\.\|\x*\.\x\+\)#*\(/\x\+#*\)\?\([sl][-+]\?\x\+#*\)\?@[-+]\?\(\x\+\|\x\+#*\.\|\x*\.\x\+\)#*\(/\x\+#*\)\?\([sl][-+]\?\x\+#*\)\?\>"
+syntax match racketNumber "\<\(#x\|#[ei]#x\|#x#[ei]\)[-+]\(inf\|nan\)\.[0f]@[-+]\?\(\x\+\|\x\+#*\.\|\x*\.\x\+\)#*\(/\x\+#*\)\?\([sl][-+]\?\x\+#*\)\?\>"
+syntax match racketNumber "\<\(#x\|#[ei]#x\|#x#[ei]\)[-+]\?\(\x\+\|\x\+#*\.\|\x*\.\x\+\)#*\(/\x\+#*\)\?\([sl][-+]\?\x\+#*\)\?@[-+]\(inf\|nan\)\.[0f]\>"
+
+" these work for any radix
+syntax match racketNumber "\<\(#[xdobie]\)\{0,2}[-+]\(inf\|nan\)\.[0f]i\?\>" contains=racketContainedNumberError
+syntax match racketNumber "\<\(#[xdobie]\)\{0,2}[-+]\(inf\|nan\)\.[0f][-+]\(inf\|nan\)\.[0f]i\>" contains=racketContainedNumberError
+syntax match racketNumber "\<\(#[xdobie]\)\{0,2}[-+]\(inf\|nan\)\.[0f]@[-+]\(inf\|nan\)\.[0f]\>" contains=racketContainedNumberError
+
+syntax keyword racketBoolean #t #f #true #false #T #F
+
+syntax match racketError "\<#\\\k*\>"
+
+syntax match racketChar "\<#\\.\w\@!"
+syntax match racketChar "\<#\\space\>"
+syntax match racketChar "\<#\\newline\>"
+syntax match racketChar "\<#\\return\>"
+syntax match racketChar "\<#\\null\?\>"
+syntax match racketChar "\<#\\backspace\>"
+syntax match racketChar "\<#\\tab\>"
+syntax match racketChar "\<#\\linefeed\>"
+syntax match racketChar "\<#\\vtab\>"
+syntax match racketChar "\<#\\page\>"
+syntax match racketChar "\<#\\rubout\>"
+syntax match racketChar "\<#\\\o\{1,3}\>"
+syntax match racketChar "\<#\\x\x\{1,2}\>"
+syntax match racketChar "\<#\\u\x\{1,6}\>"
+
+syntax cluster racketTop add=racketNumber,racketBoolean,racketChar
+
+" Command-line parsing
+syntax keyword racketExtFunc command-line current-command-line-arguments once-any help-labels multi once-each
+
+syntax match racketSyntax "#lang "
+syntax match racketExtSyntax "#:\k\+"
+
+syntax cluster racketTop add=racketExtFunc,racketExtSyntax
+
+" syntax quoting, unquoting and quasiquotation
+syntax match racketQuote "#\?['`]"
+
+syntax match racketUnquote "#,"
+syntax match racketUnquote "#,@"
+syntax match racketUnquote ","
+syntax match racketUnquote ",@"
+
+" Comments
+syntax match racketSharpBang "\%^#![ /].*" display
+syntax match racketComment /;.*$/ contains=racketTodo,racketNote,@Spell
+syntax region racketMultilineComment start=/#|/ end=/|#/ contains=racketMultilineComment,racketTodo,racketNote,@Spell
+syntax match racketFormComment "#;" nextgroup=@racketTop
+
+syntax match racketTodo /\C\<\(FIXME\|TODO\|XXX\)\ze:\?\>/ contained
+syntax match racketNote /\CNOTE\ze:\?/ contained
+
+syntax cluster racketTop add=racketQuote,racketUnquote,racketComment,racketMultilineComment,racketFormComment
+
+" Synchronization and the wrapping up...
+syntax sync match matchPlace grouphere NONE "^[^ \t]"
+" ... i.e. synchronize on a line that starts at the left margin
+
+" Define the default highlighting.
+highlight default link racketSyntax Statement
+highlight default link racketFunc Function
+
+highlight default link racketString String
+highlight default link racketStringEscape Special
+highlight default link racketUStringEscape Special
+highlight default link racketStringEscapeError Error
+highlight default link racketChar Character
+highlight default link racketBoolean Boolean
+
+highlight default link racketNumber Number
+highlight default link racketNumberError Error
+highlight default link racketContainedNumberError Error
+
+highlight default link racketQuote SpecialChar
+highlight default link racketUnquote SpecialChar
+
+highlight default link racketDelimiter Delimiter
+highlight default link racketParen Delimiter
+highlight default link racketConstant Constant
+
+highlight default link racketLit Type
+highlight default link racketRe Type
+
+highlight default link racketComment Comment
+highlight default link racketMultilineComment Comment
+highlight default link racketFormComment SpecialChar
+highlight default link racketSharpBang Comment
+highlight default link racketTodo Todo
+highlight default link racketNote SpecialComment
+highlight default link racketError Error
+
+highlight default link racketExtSyntax Type
+highlight default link racketExtFunc PreProc
+
+let b:current_syntax = "racket"
diff --git a/runtime/syntax/rego.vim b/runtime/syntax/rego.vim
index a04fc7007b..bc82030488 100644
--- a/runtime/syntax/rego.vim
+++ b/runtime/syntax/rego.vim
@@ -2,7 +2,7 @@
" Language: rego policy language
" Maintainer: Matt Dunford (zenmatic@gmail.com)
" URL: https://github.com/zenmatic/vim-syntax-rego
-" Last Change: 2019 Dec 12
+" Last Change: 2022 Dec 4
" https://www.openpolicyagent.org/docs/latest/policy-language/
@@ -14,36 +14,56 @@ endif
syn case match
syn keyword regoDirective package import allow deny
-syn keyword regoKeywords as default else false not null true with some
+syn keyword regoKeywords as default else every false if import package not null true with some in print
syn keyword regoFuncAggregates count sum product max min sort all any
-syn match regoFuncArrays "\<array\.\(concat\|slice\)\>"
+syn match regoFuncArrays "\<array\.\(concat\|slice\|reverse\)\>"
syn keyword regoFuncSets intersection union
-syn keyword regoFuncStrings concat /\<contains\>/ endswith format_int indexof lower replace split sprintf startswith substring trim trim_left trim_prefix trim_right trim_suffix trim_space upper
-syn match regoFuncStrings2 "\<strings\.replace_n\>"
+syn keyword regoFuncStrings concat /\<contains\>/ endswith format_int indexof indexof_n lower replace split sprintf startswith substring trim trim_left trim_prefix trim_right trim_suffix trim_space upper
+syn match regoFuncStrings2 "\<strings\.\(replace_n\|reverse\|any_prefix_match\|any_suffix_match\)\>"
syn match regoFuncStrings3 "\<contains\>"
syn keyword regoFuncRegex re_match
-syn match regoFuncRegex2 "\<regex\.\(split\|globs_match\|template_match\|find_n\|find_all_string_submatch_n\)\>"
+syn match regoFuncRegex2 "\<regex\.\(is_valid\|split\|globs_match\|template_match\|find_n\|find_all_string_submatch_n\|replace\)\>"
+syn match regoFuncUuid "\<uuid.rfc4122\>"
+syn match regoFuncBits "\<bits\.\(or\|and\|negate\|xor\|lsh\|rsh\)\>"
+syn match regoFuncObject "\<object\.\(get\|remove\|subset\|union\|union_n\|filter\)\>"
syn match regoFuncGlob "\<glob\.\(match\|quote_meta\)\>"
-syn match regoFuncUnits "\<units\.parse_bytes\>"
+syn match regoFuncUnits "\<units\.parse\(_bytes\)\=\>"
syn keyword regoFuncTypes is_number is_string is_boolean is_array is_set is_object is_null type_name
-syn match regoFuncEncoding1 "\<\(base64\|base64url\)\.\(encode\|decode\)\>"
-syn match regoFuncEncoding2 "\<urlquery\.\(encode\|decode\|encode_object\)\>"
-syn match regoFuncEncoding3 "\<\(json\|yaml\)\.\(marshal\|unmarshal\)\>"
+syn match regoFuncEncoding1 "\<base64\.\(encode\|decode\|is_valid\)\>"
+syn match regoFuncEncoding2 "\<base64url\.\(encode\(_no_pad\)\=\|decode\)\>"
+syn match regoFuncEncoding3 "\<urlquery\.\(encode\|decode\|\(en\|de\)code_object\)\>"
+syn match regoFuncEncoding4 "\<\(json\|yaml\)\.\(is_valid\|marshal\|unmarshal\)\>"
+syn match regoFuncEncoding5 "\<json\.\(filter\|patch\|remove\)\>"
syn match regoFuncTokenSigning "\<io\.jwt\.\(encode_sign_raw\|encode_sign\)\>"
-syn match regoFuncTokenVerification "\<io\.jwt\.\(verify_rs256\|verify_ps256\|verify_es256\|verify_hs256\|decode\|decode_verify\)\>"
-syn match regoFuncTime "\<time\.\(now_ns\|parse_ns\|parse_rfc3339_ns\|parse_duration_ns\|date\|clock\|weekday\)\>"
-syn match regoFuncCryptography "\<crypto\.x509\.parse_certificates\>"
+syn match regoFuncTokenVerification1 "\<io\.jwt\.\(decode\|decode_verify\)\>"
+syn match regoFuncTokenVerification2 "\<io\.jwt\.verify_\(rs\|ps\|es\|hs\)\(256\|384\|512\)\>"
+syn match regoFuncTime "\<time\.\(now_ns\|parse_ns\|parse_rfc3339_ns\|parse_duration_ns\|date\|clock\|weekday\|diff\|add_date\)\>"
+syn match regoFuncCryptography "\<crypto\.x509\.\(parse_certificates\|parse_certificate_request\|parse_and_verify_certificates\|parse_rsa_private_key\)\>"
+syn match regoFuncCryptography "\<crypto\.\(md5\|sha1\|sha256\)"
+syn match regoFuncCryptography "\<crypto\.hmac\.\(md5\|sha1\|sha256\|sha512\)"
syn keyword regoFuncGraphs walk
+syn match regoFuncGraphs2 "\<graph\.reachable\(_paths\)\=\>"
+syn match regoFuncGraphQl "\<graphql\.\(\(schema_\)\=is_valid\|parse\(_\(and_verify\|query\|schema\)\)\=\)\>"
syn match regoFuncHttp "\<http\.send\>"
-syn match regoFuncNet "\<net\.\(cidr_contains\|cidr_intersects\)\>"
-syn match regoFuncRego "\<rego\.parse_module\>"
+syn match regoFuncNet "\<net\.\(cidr_merge\|cidr_contains\|cidr_contains_matches\|cidr_intersects\|cidr_expand\|lookup_ip_addr\|cidr_is_valid\)\>"
+syn match regoFuncRego "\<rego\.\(parse_module\|metadata\.\(rule\|chain\)\)\>"
syn match regoFuncOpa "\<opa\.runtime\>"
syn keyword regoFuncDebugging trace
+syn match regoFuncRand "\<rand\.intn\>"
+syn match regoFuncNumbers "\<numbers\.\(range\|intn\)\>"
+syn keyword regoFuncNumbers round ceil floor abs
+
+syn match regoFuncSemver "\<semver\.\(is_valid\|compare\)\>"
+syn keyword regoFuncConversions to_number
+syn match regoFuncHex "\<hex\.\(encode\|decode\)\>"
+
+hi def link regoFuncUuid Statement
+hi def link regoFuncBits Statement
hi def link regoDirective Statement
hi def link regoKeywords Statement
hi def link regoFuncAggregates Statement
@@ -60,16 +80,27 @@ hi def link regoFuncTypes Statement
hi def link regoFuncEncoding1 Statement
hi def link regoFuncEncoding2 Statement
hi def link regoFuncEncoding3 Statement
+hi def link regoFuncEncoding4 Statement
+hi def link regoFuncEncoding5 Statement
hi def link regoFuncTokenSigning Statement
-hi def link regoFuncTokenVerification Statement
+hi def link regoFuncTokenVerification1 Statement
+hi def link regoFuncTokenVerification2 Statement
hi def link regoFuncTime Statement
hi def link regoFuncCryptography Statement
hi def link regoFuncGraphs Statement
+hi def link regoFuncGraphQl Statement
+hi def link regoFuncGraphs2 Statement
hi def link regoFuncHttp Statement
hi def link regoFuncNet Statement
hi def link regoFuncRego Statement
hi def link regoFuncOpa Statement
hi def link regoFuncDebugging Statement
+hi def link regoFuncObject Statement
+hi def link regoFuncNumbers Statement
+hi def link regoFuncSemver Statement
+hi def link regoFuncConversions Statement
+hi def link regoFuncHex Statement
+hi def link regoFuncRand Statement
" https://www.openpolicyagent.org/docs/latest/policy-language/#strings
syn region regoString start=+"+ skip=+\\\\\|\\"+ end=+"+
diff --git a/runtime/syntax/sed.vim b/runtime/syntax/sed.vim
index 63b39db81f..d1f631df4b 100644
--- a/runtime/syntax/sed.vim
+++ b/runtime/syntax/sed.vim
@@ -1,30 +1,42 @@
" Vim syntax file
-" Language: sed
-" Maintainer: Haakon Riiser <hakonrk@fys.uio.no>
-" URL: http://folk.uio.no/hakonrk/vim/syntax/sed.vim
-" Last Change: 2010 May 29
+" Language: sed
+" Maintainer: Doug Kearns <dougkearns@gmail.com>
+" Previous Maintainer: Haakon Riiser <hakonrk@fys.uio.no>
+" Contributor: Jack Haden-Enneking
+" Last Change: 2022 Oct 15
" quit when a syntax file was already loaded
if exists("b:current_syntax")
- finish
+ finish
endif
+syn keyword sedTodo contained TODO FIXME XXX
+
syn match sedError "\S"
syn match sedWhitespace "\s\+" contained
syn match sedSemicolon ";"
syn match sedAddress "[[:digit:]$]"
syn match sedAddress "\d\+\~\d\+"
-syn region sedAddress matchgroup=Special start="[{,;]\s*/\(\\/\)\="lc=1 skip="[^\\]\(\\\\\)*\\/" end="/I\=" contains=sedTab,sedRegexpMeta
-syn region sedAddress matchgroup=Special start="^\s*/\(\\/\)\=" skip="[^\\]\(\\\\\)*\\/" end="/I\=" contains=sedTab,sedRegexpMeta
-syn match sedComment "^\s*#.*$"
-syn match sedFunction "[dDgGhHlnNpPqQx=]\s*\($\|;\)" contains=sedSemicolon,sedWhitespace
+syn region sedAddress matchgroup=Special start="[{,;]\s*/\%(\\/\)\="lc=1 skip="[^\\]\%(\\\\\)*\\/" end="/I\=" contains=sedTab,sedRegexpMeta
+syn region sedAddress matchgroup=Special start="^\s*/\%(\\/\)\=" skip="[^\\]\%(\\\\\)*\\/" end="/I\=" contains=sedTab,sedRegexpMeta
+syn match sedFunction "[dDgGhHlnNpPqQx=]\s*\%($\|;\)" contains=sedSemicolon,sedWhitespace
+if exists("g:sed_dialect") && g:sed_dialect ==? "bsd"
+ syn match sedComment "^\s*#.*$" contains=sedTodo
+else
+ syn match sedFunction "[dDgGhHlnNpPqQx=]\s*\ze#" contains=sedSemicolon,sedWhitespace
+ syn match sedComment "#.*$" contains=sedTodo
+endif
syn match sedLabel ":[^;]*"
-syn match sedLineCont "^\(\\\\\)*\\$" contained
-syn match sedLineCont "[^\\]\(\\\\\)*\\$"ms=e contained
+syn match sedLineCont "^\%(\\\\\)*\\$" contained
+syn match sedLineCont "[^\\]\%(\\\\\)*\\$"ms=e contained
syn match sedSpecial "[{},!]"
-if exists("highlight_sedtabs")
- syn match sedTab "\t" contained
+
+" continue to silently support the old name
+let s:highlight_tabs = v:false
+if exists("g:highlight_sedtabs") || get(g:, "sed_highlight_tabs", 0)
+ let s:highlight_tabs = v:true
+ syn match sedTab "\t" contained
endif
" Append/Change/Insert
@@ -34,39 +46,39 @@ syn region sedBranch matchgroup=sedFunction start="[bt]" matchgroup=sedSemicolon
syn region sedRW matchgroup=sedFunction start="[rw]" matchgroup=sedSemicolon end=";\|$" contains=sedWhitespace
" Substitution/transform with various delimiters
-syn region sedFlagwrite matchgroup=sedFlag start="w" matchgroup=sedSemicolon end=";\|$" contains=sedWhitespace contained
-syn match sedFlag "[[:digit:]gpI]*w\=" contains=sedFlagwrite contained
+syn region sedFlagWrite matchgroup=sedFlag start="w" matchgroup=sedSemicolon end=";\|$" contains=sedWhitespace contained
+syn match sedFlag "[[:digit:]gpI]*w\=" contains=sedFlagWrite contained
syn match sedRegexpMeta "[.*^$]" contained
syn match sedRegexpMeta "\\." contains=sedTab contained
syn match sedRegexpMeta "\[.\{-}\]" contains=sedTab contained
syn match sedRegexpMeta "\\{\d\*,\d*\\}" contained
-syn match sedRegexpMeta "\\(.\{-}\\)" contains=sedTab contained
-syn match sedReplaceMeta "&\|\\\($\|.\)" contains=sedTab contained
+syn match sedRegexpMeta "\\%(.\{-}\\)" contains=sedTab contained
+syn match sedReplaceMeta "&\|\\\%($\|.\)" contains=sedTab contained
" Metacharacters: $ * . \ ^ [ ~
" @ is used as delimiter and treated on its own below
-let __at = char2nr("@")
-let __sed_i = char2nr(" ") " ASCII: 32, EBCDIC: 64
+let s:at = char2nr("@")
+let s:i = char2nr(" ") " ASCII: 32, EBCDIC: 64
if has("ebcdic")
- let __sed_last = 255
+ let s:last = 255
else
- let __sed_last = 126
+ let s:last = 126
endif
-let __sed_metacharacters = '$*.\^[~'
-while __sed_i <= __sed_last
- let __sed_delimiter = escape(nr2char(__sed_i), __sed_metacharacters)
- if __sed_i != __at
- exe 'syn region sedAddress matchgroup=Special start=@\\'.__sed_delimiter.'\(\\'.__sed_delimiter.'\)\=@ skip=@[^\\]\(\\\\\)*\\'.__sed_delimiter.'@ end=@'.__sed_delimiter.'I\=@ contains=sedTab'
- exe 'syn region sedRegexp'.__sed_i 'matchgroup=Special start=@'.__sed_delimiter.'\(\\\\\|\\'.__sed_delimiter.'\)*@ skip=@[^\\'.__sed_delimiter.']\(\\\\\)*\\'.__sed_delimiter.'@ end=@'.__sed_delimiter.'@me=e-1 contains=sedTab,sedRegexpMeta keepend contained nextgroup=sedReplacement'.__sed_i
- exe 'syn region sedReplacement'.__sed_i 'matchgroup=Special start=@'.__sed_delimiter.'\(\\\\\|\\'.__sed_delimiter.'\)*@ skip=@[^\\'.__sed_delimiter.']\(\\\\\)*\\'.__sed_delimiter.'@ end=@'.__sed_delimiter.'@ contains=sedTab,sedReplaceMeta keepend contained nextgroup=sedFlag'
- endif
- let __sed_i = __sed_i + 1
+let s:metacharacters = '$*.\^[~'
+while s:i <= s:last
+ let s:delimiter = escape(nr2char(s:i), s:metacharacters)
+ if s:i != s:at
+ exe 'syn region sedAddress matchgroup=Special start=@\\'.s:delimiter.'\%(\\'.s:delimiter.'\)\=@ skip=@[^\\]\%(\\\\\)*\\'.s:delimiter.'@ end=@'.s:delimiter.'[IM]\=@ contains=sedTab'
+ exe 'syn region sedRegexp'.s:i 'matchgroup=Special start=@'.s:delimiter.'\%(\\\\\|\\'.s:delimiter.'\)*@ skip=@[^\\'.s:delimiter.']\%(\\\\\)*\\'.s:delimiter.'@ end=@'.s:delimiter.'@me=e-1 contains=sedTab,sedRegexpMeta keepend contained nextgroup=sedReplacement'.s:i
+ exe 'syn region sedReplacement'.s:i 'matchgroup=Special start=@'.s:delimiter.'\%(\\\\\|\\'.s:delimiter.'\)*@ skip=@[^\\'.s:delimiter.']\%(\\\\\)*\\'.s:delimiter.'@ end=@'.s:delimiter.'@ contains=sedTab,sedReplaceMeta keepend contained nextgroup=@sedFlags'
+ endif
+ let s:i = s:i + 1
endwhile
-syn region sedAddress matchgroup=Special start=+\\@\(\\@\)\=+ skip=+[^\\]\(\\\\\)*\\@+ end=+@I\=+ contains=sedTab,sedRegexpMeta
-syn region sedRegexp64 matchgroup=Special start=+@\(\\\\\|\\@\)*+ skip=+[^\\@]\(\\\\\)*\\@+ end=+@+me=e-1 contains=sedTab,sedRegexpMeta keepend contained nextgroup=sedReplacement64
-syn region sedReplacement64 matchgroup=Special start=+@\(\\\\\|\\@\)*+ skip=+[^\\@]\(\\\\\)*\\@+ end=+@+ contains=sedTab,sedReplaceMeta keepend contained nextgroup=sedFlag
+syn region sedAddress matchgroup=Special start=+\\@\%(\\@\)\=+ skip=+[^\\]\%(\\\\\)*\\@+ end=+@I\=+ contains=sedTab,sedRegexpMeta
+syn region sedRegexp64 matchgroup=Special start=+@\%(\\\\\|\\@\)*+ skip=+[^\\@]\%(\\\\\)*\\@+ end=+@+me=e-1 contains=sedTab,sedRegexpMeta keepend contained nextgroup=sedReplacement64
+syn region sedReplacement64 matchgroup=Special start=+@\%(\\\\\|\\@\)*+ skip=+[^\\@]\%(\\\\\)*\\@+ end=+@+ contains=sedTab,sedReplaceMeta keepend contained nextgroup=sedFlag
-" Since the syntax for the substituion command is very similar to the
+" Since the syntax for the substitution command is very similar to the
" syntax for the transform command, I use the same pattern matching
" for both commands. There is one problem -- the transform command
" (y) does not allow any flags. To save memory, I ignore this problem.
@@ -80,7 +92,7 @@ hi def link sedComment Comment
hi def link sedDelete Function
hi def link sedError Error
hi def link sedFlag Type
-hi def link sedFlagwrite Constant
+hi def link sedFlagWrite Constant
hi def link sedFunction Function
hi def link sedLabel Label
hi def link sedLineCont Special
@@ -88,23 +100,24 @@ hi def link sedPutHoldspc Function
hi def link sedReplaceMeta Special
hi def link sedRegexpMeta Special
hi def link sedRW Constant
-hi def link sedSemicolon Special
+hi def link sedSemicolon Special
hi def link sedST Function
hi def link sedSpecial Special
+hi def link sedTodo Todo
hi def link sedWhitespace NONE
-if exists("highlight_sedtabs")
-hi def link sedTab Todo
+if s:highlight_tabs
+ hi def link sedTab Todo
endif
-let __sed_i = char2nr(" ") " ASCII: 32, EBCDIC: 64
-while __sed_i <= __sed_last
-exe "hi def link sedRegexp".__sed_i "Macro"
-exe "hi def link sedReplacement".__sed_i "NONE"
-let __sed_i = __sed_i + 1
+let s:i = char2nr(" ") " ASCII: 32, EBCDIC: 64
+while s:i <= s:last
+ exe "hi def link sedRegexp".s:i "Macro"
+ exe "hi def link sedReplacement".s:i "NONE"
+ let s:i = s:i + 1
endwhile
-
-unlet __sed_i __sed_last __sed_delimiter __sed_metacharacters
+unlet s:i s:last s:delimiter s:metacharacters s:at
+unlet s:highlight_tabs
let b:current_syntax = "sed"
-" vim: sts=4 sw=4 ts=8
+" vim: nowrap sw=2 sts=2 ts=8 noet:
diff --git a/runtime/syntax/sh.vim b/runtime/syntax/sh.vim
index e44699faa5..6722d62c89 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: Jun 29, 2022
-" Version: 202
+" Last Change: Nov 25, 2022
+" Version: 204
" 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
@@ -84,15 +84,9 @@ elseif g:sh_fold_enabled != 0 && !has("folding")
let g:sh_fold_enabled= 0
echomsg "Ignoring g:sh_fold_enabled=".g:sh_fold_enabled."; need to re-compile vim for +fold support"
endif
-if !exists("s:sh_fold_functions")
- let s:sh_fold_functions= and(g:sh_fold_enabled,1)
-endif
-if !exists("s:sh_fold_heredoc")
- let s:sh_fold_heredoc = and(g:sh_fold_enabled,2)
-endif
-if !exists("s:sh_fold_ifdofor")
- let s:sh_fold_ifdofor = and(g:sh_fold_enabled,4)
-endif
+let s:sh_fold_functions= and(g:sh_fold_enabled,1)
+let s:sh_fold_heredoc = and(g:sh_fold_enabled,2)
+let s:sh_fold_ifdofor = and(g:sh_fold_enabled,4)
if g:sh_fold_enabled && &fdm == "manual"
" Given that the user provided g:sh_fold_enabled
" AND g:sh_fold_enabled is manual (usual default)
@@ -113,6 +107,9 @@ endif
" Set up folding commands for shell {{{1
" =================================
+sil! delc ShFoldFunctions
+sil! delc ShFoldHereDoc
+sil! delc ShFoldIfDoFor
if s:sh_fold_functions
com! -nargs=* ShFoldFunctions <args> fold
else
@@ -141,7 +138,7 @@ endif
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
+syn cluster shCaseList contains=@shCommandSubList,shCaseEsac,shColon,shCommandSub,shCommandSubBQ,shComment,shDblBrace,shDo,shEcho,shExpr,shFor,shHereDoc,shIf,shHereString,shRedir,shSetList,shSource,shStatement,shVariable,shCtrlSeq
if exists("b:is_kornshell") || exists("b:is_bash")
syn cluster shCaseList add=shForPP
endif
@@ -415,22 +412,22 @@ syn match shBQComment contained "#.\{-}\ze`" contains=@shCommentGroup
" Here Documents: {{{1
" (modified by Felipe Contreras)
" =========================================
-ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc01 start="<<\s*\z([^ \t|>]\+\)" matchgroup=shHereDoc01 end="^\z1\s*$" contains=@shDblQuoteList
-ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc02 start="<<-\s*\z([^ \t|>]\+\)" matchgroup=shHereDoc02 end="^\s*\z1\s*$" contains=@shDblQuoteList
-ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc03 start="<<\s*\\\z([^ \t|>]\+\)" matchgroup=shHereDoc03 end="^\z1\s*$"
-ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc04 start="<<-\s*\\\z([^ \t|>]\+\)" matchgroup=shHereDoc04 end="^\s*\z1\s*$"
-ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc05 start="<<\s*'\z([^']\+\)'" matchgroup=shHereDoc05 end="^\z1\s*$"
-ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc06 start="<<-\s*'\z([^']\+\)'" matchgroup=shHereDoc06 end="^\s*\z1\s*$"
-ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc07 start="<<\s*\"\z([^"]\+\)\"" matchgroup=shHereDoc07 end="^\z1\s*$"
-ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc08 start="<<-\s*\"\z([^"]\+\)\"" matchgroup=shHereDoc08 end="^\s*\z1\s*$"
-ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc09 start="<<\s*\\\_$\_s*\z([^ \t|>]\+\)" matchgroup=shHereDoc09 end="^\z1\s*$" contains=@shDblQuoteList
-ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc10 start="<<-\s*\\\_$\_s*\z([^ \t|>]\+\)" matchgroup=shHereDoc10 end="^\s*\z1\s*$" contains=@shDblQuoteList
-ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc11 start="<<\s*\\\_$\_s*\\\z([^ \t|>]\+\)" matchgroup=shHereDoc11 end="^\z1\s*$"
-ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc12 start="<<-\s*\\\_$\_s*\\\z([^ \t|>]\+\)" matchgroup=shHereDoc12 end="^\s*\z1\s*$"
-ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc13 start="<<\s*\\\_$\_s*'\z([^']\+\)'" matchgroup=shHereDoc13 end="^\z1\s*$"
-ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc14 start="<<-\s*\\\_$\_s*'\z([^']\+\)'" matchgroup=shHereDoc14 end="^\s*\z1\s*$"
-ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc15 start="<<\s*\\\_$\_s*\"\z([^"]\+\)\"" matchgroup=shHereDoc15 end="^\z1\s*$"
-ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc16 start="<<-\s*\\\_$\_s*\"\z([^"]\+\)\"" matchgroup=shHereDoc16 end="^\s*\z1\s*$"
+ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc01 start="<<\s*\z([^ \t|>]\+\)" matchgroup=shHereDoc01 end="^\z1$" contains=@shDblQuoteList
+ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc02 start="<<-\s*\z([^ \t|>]\+\)" matchgroup=shHereDoc02 end="^\s*\z1$" contains=@shDblQuoteList
+ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc03 start="<<\s*\\\z([^ \t|>]\+\)" matchgroup=shHereDoc03 end="^\z1$"
+ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc04 start="<<-\s*\\\z([^ \t|>]\+\)" matchgroup=shHereDoc04 end="^\s*\z1$"
+ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc05 start="<<\s*'\z([^']\+\)'" matchgroup=shHereDoc05 end="^\z1$"
+ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc06 start="<<-\s*'\z([^']\+\)'" matchgroup=shHereDoc06 end="^\s*\z1$"
+ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc07 start="<<\s*\"\z([^"]\+\)\"" matchgroup=shHereDoc07 end="^\z1$"
+ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc08 start="<<-\s*\"\z([^"]\+\)\"" matchgroup=shHereDoc08 end="^\s*\z1$"
+ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc09 start="<<\s*\\\_$\_s*\z([^ \t|>]\+\)" matchgroup=shHereDoc09 end="^\z1$" contains=@shDblQuoteList
+ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc10 start="<<-\s*\\\_$\_s*\z([^ \t|>]\+\)" matchgroup=shHereDoc10 end="^\s*\z1$" contains=@shDblQuoteList
+ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc11 start="<<\s*\\\_$\_s*\\\z([^ \t|>]\+\)" matchgroup=shHereDoc11 end="^\z1$"
+ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc12 start="<<-\s*\\\_$\_s*\\\z([^ \t|>]\+\)" matchgroup=shHereDoc12 end="^\s*\z1$"
+ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc13 start="<<\s*\\\_$\_s*'\z([^']\+\)'" matchgroup=shHereDoc13 end="^\z1$"
+ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc14 start="<<-\s*\\\_$\_s*'\z([^']\+\)'" matchgroup=shHereDoc14 end="^\s*\z1$"
+ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc15 start="<<\s*\\\_$\_s*\"\z([^"]\+\)\"" matchgroup=shHereDoc15 end="^\z1$"
+ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc16 start="<<-\s*\\\_$\_s*\"\z([^"]\+\)\"" matchgroup=shHereDoc16 end="^\s*\z1$"
" Here Strings: {{{1
diff --git a/runtime/syntax/shared/hgcommitDiff.vim b/runtime/syntax/shared/hgcommitDiff.vim
new file mode 100644
index 0000000000..949cdf0b1c
--- /dev/null
+++ b/runtime/syntax/shared/hgcommitDiff.vim
@@ -0,0 +1,390 @@
+" Vim syntax file
+" Language: Sapling / Mecurial Diff (context or unified)
+" Maintainer: Max Coplan <mchcopl@gmail.com>
+" Translations by Jakson Alves de Aquino.
+" Last Change: 2022-12-08
+" Copied from: runtime/syntax/diff.vim
+
+" Quit when a (custom) syntax file was already loaded
+if exists("b:current_syntax")
+ finish
+endif
+scriptencoding utf-8
+
+syn match hgDiffOnly "^\%(SL\|HG\): Only in .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): Files .* and .* are identical$"
+syn match hgDiffDiffer "^\%(SL\|HG\): Files .* and .* differ$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Binary files .* and .* differ$"
+syn match hgDiffIsA "^\%(SL\|HG\): File .* is a .* while file .* is a .*"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ No newline at end of file .*"
+syn match hgDiffCommon "^\%(SL\|HG\): Common subdirectories: .*"
+
+" Disable the translations by setting diff_translations to zero.
+if !exists("diff_translations") || diff_translations
+
+" ca
+syn match hgDiffOnly "^\%(SL\|HG\): Només a .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): Els fitxers .* i .* són idèntics$"
+syn match hgDiffDiffer "^\%(SL\|HG\): Els fitxers .* i .* difereixen$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Els fitxers .* i .* difereixen$"
+syn match hgDiffIsA "^\%(SL\|HG\): El fitxer .* és un .* mentre que el fitxer .* és un .*"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ No hi ha cap caràcter de salt de línia al final del fitxer"
+syn match hgDiffCommon "^\%(SL\|HG\): Subdirectoris comuns: .* i .*"
+
+" cs
+syn match hgDiffOnly "^\%(SL\|HG\): Pouze v .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): Soubory .* a .* jsou identické$"
+syn match hgDiffDiffer "^\%(SL\|HG\): Soubory .* a .* jsou různé$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Binární soubory .* a .* jsou rozdílné$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Soubory .* a .* jsou různé$"
+syn match hgDiffIsA "^\%(SL\|HG\): Soubor .* je .* pokud soubor .* je .*"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ Chybí znak konce řádku na konci souboru"
+syn match hgDiffCommon "^\%(SL\|HG\): Společné podadresáře: .* a .*"
+
+" da
+syn match hgDiffOnly "^\%(SL\|HG\): Kun i .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): Filerne .* og .* er identiske$"
+syn match hgDiffDiffer "^\%(SL\|HG\): Filerne .* og .* er forskellige$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Binære filer .* og .* er forskellige$"
+syn match hgDiffIsA "^\%(SL\|HG\): Filen .* er en .* mens filen .* er en .*"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ Intet linjeskift ved filafslutning"
+syn match hgDiffCommon "^\%(SL\|HG\): Identiske underkataloger: .* og .*"
+
+" de
+syn match hgDiffOnly "^\%(SL\|HG\): Nur in .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): Dateien .* und .* sind identisch.$"
+syn match hgDiffDiffer "^\%(SL\|HG\): Dateien .* und .* sind verschieden.$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Binärdateien .* and .* sind verschieden.$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Binärdateien .* und .* sind verschieden.$"
+syn match hgDiffIsA "^\%(SL\|HG\): Datei .* ist ein .* während Datei .* ein .* ist.$"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ Kein Zeilenumbruch am Dateiende."
+syn match hgDiffCommon "^\%(SL\|HG\): Gemeinsame Unterverzeichnisse: .* und .*.$"
+
+" el
+syn match hgDiffOnly "^\%(SL\|HG\): Μόνο στο .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): Τα αρχεία .* καί .* είναι πανομοιότυπα$"
+syn match hgDiffDiffer "^\%(SL\|HG\): Τα αρχεία .* και .* διαφέρουν$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Τα αρχεία .* και .* διαφέρουν$"
+syn match hgDiffIsA "^\%(SL\|HG\): Το αρχείο .* είναι .* ενώ το αρχείο .* είναι .*"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ Δεν υπάρχει χαρακτήρας νέας γραμμής στο τέλος του αρχείου"
+syn match hgDiffCommon "^\%(SL\|HG\): Οι υποκατάλογοι .* και .* είναι ταυτόσημοι$"
+
+" eo
+syn match hgDiffOnly "^\%(SL\|HG\): Nur en .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): Dosieroj .* kaj .* estas samaj$"
+syn match hgDiffDiffer "^\%(SL\|HG\): Dosieroj .* kaj .* estas malsamaj$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Dosieroj .* kaj .* estas malsamaj$"
+syn match hgDiffIsA "^\%(SL\|HG\): Dosiero .* estas .*, dum dosiero .* estas .*"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ Mankas linifino ĉe fino de dosiero"
+syn match hgDiffCommon "^\%(SL\|HG\): Komunaj subdosierujoj: .* kaj .*"
+
+" es
+syn match hgDiffOnly "^\%(SL\|HG\): Sólo en .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): Los ficheros .* y .* son idénticos$"
+syn match hgDiffDiffer "^\%(SL\|HG\): Los ficheros .* y .* son distintos$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Los ficheros binarios .* y .* son distintos$"
+syn match hgDiffIsA "^\%(SL\|HG\): El fichero .* es un .* mientras que el .* es un .*"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ No hay ningún carácter de nueva línea al final del fichero"
+syn match hgDiffCommon "^\%(SL\|HG\): Subdirectorios comunes: .* y .*"
+
+" fi
+syn match hgDiffOnly "^\%(SL\|HG\): Vain hakemistossa .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): Tiedostot .* ja .* ovat identtiset$"
+syn match hgDiffDiffer "^\%(SL\|HG\): Tiedostot .* ja .* eroavat$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Binääritiedostot .* ja .* eroavat$"
+syn match hgDiffIsA "^\%(SL\|HG\): Tiedosto .* on .*, kun taas tiedosto .* on .*"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ Ei rivinvaihtoa tiedoston lopussa"
+syn match hgDiffCommon "^\%(SL\|HG\): Yhteiset alihakemistot: .* ja .*"
+
+" fr
+syn match hgDiffOnly "^\%(SL\|HG\): Seulement dans .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): Les fichiers .* et .* sont identiques.*"
+syn match hgDiffDiffer "^\%(SL\|HG\): Les fichiers .* et .* sont différents.*"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Les fichiers binaires .* et .* sont différents.*"
+syn match hgDiffIsA "^\%(SL\|HG\): Le fichier .* est un .* alors que le fichier .* est un .*"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ Pas de fin de ligne à la fin du fichier.*"
+syn match hgDiffCommon "^\%(SL\|HG\): Les sous-répertoires .* et .* sont identiques.*"
+
+" ga
+syn match hgDiffOnly "^\%(SL\|HG\): I .* amháin: .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): Is comhionann iad na comhaid .* agus .*"
+syn match hgDiffDiffer "^\%(SL\|HG\): Tá difríocht idir na comhaid .* agus .*"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Tá difríocht idir na comhaid .* agus .*"
+syn match hgDiffIsA "^\%(SL\|HG\): Tá comhad .* ina .* ach tá comhad .* ina .*"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ Gan líne nua ag an chomhadchríoch"
+syn match hgDiffCommon "^\%(SL\|HG\): Fochomhadlanna i gcoitianta: .* agus .*"
+
+" gl
+syn match hgDiffOnly "^\%(SL\|HG\): Só en .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): Os ficheiros .* e .* son idénticos$"
+syn match hgDiffDiffer "^\%(SL\|HG\): Os ficheiros .* e .* son diferentes$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Os ficheiros binarios .* e .* son diferentes$"
+syn match hgDiffIsA "^\%(SL\|HG\): O ficheiro .* é un .* mentres que o ficheiro .* é un .*"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ Non hai un salto de liña na fin da liña"
+syn match hgDiffCommon "^\%(SL\|HG\): Subdirectorios comúns: .* e .*"
+
+" he
+" ^\%(SL\|HG\): .* are expansive patterns for long lines, so disabled unless we can match
+" some specific hebrew chars
+if search('\%u05d5\|\%u05d1', 'nw', '', 100)
+ syn match hgDiffOnly "^\%(SL\|HG\): .*-ב קר אצמנ .*"
+ syn match hgDiffIdentical "^\%(SL\|HG\): םיהז םניה .*-ו .* םיצבקה$"
+ syn match hgDiffDiffer "^\%(SL\|HG\): הזמ הז םינוש `.*'-ו `.*' םיצבקה$"
+ syn match hgDiffBDiffer "^\%(SL\|HG\): הזמ הז םינוש `.*'-ו `.*' םיירניב םיצבק$"
+ syn match hgDiffIsA "^\%(SL\|HG\): .* .*-ל .* .* תוושהל ןתינ אל$"
+ syn match hgDiffNoEOL "^\%(SL\|HG\): \\ ץבוקה ףוסב השד.-הרוש ות רס."
+ syn match hgDiffCommon "^\%(SL\|HG\): .*-ו .* :תוהז תויקית-תת$"
+endif
+
+" hr
+syn match hgDiffOnly "^\%(SL\|HG\): Samo u .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): Datoteke .* i .* su identične$"
+syn match hgDiffDiffer "^\%(SL\|HG\): Datoteke .* i .* se razlikuju$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Binarne datoteke .* i .* se razlikuju$"
+syn match hgDiffIsA "^\%(SL\|HG\): Datoteka .* je .*, a datoteka .* je .*"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ Nema novog retka na kraju datoteke"
+syn match hgDiffCommon "^\%(SL\|HG\): Uobičajeni poddirektoriji: .* i .*"
+
+" hu
+syn match hgDiffOnly "^\%(SL\|HG\): Csak .* -ben: .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): .* és .* fájlok azonosak$"
+syn match hgDiffDiffer "^\%(SL\|HG\): A(z) .* és a(z) .* fájlok különböznek$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): A(z) .* és a(z) .* fájlok különböznek$"
+syn match hgDiffIsA "^\%(SL\|HG\): A(z) .* fájl egy .*, viszont a(z) .* fájl egy .*"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ Nincs újsor a fájl végén"
+syn match hgDiffCommon "^\%(SL\|HG\): Közös alkönyvtárak: .* és .*"
+
+" id
+syn match hgDiffOnly "^\%(SL\|HG\): Hanya dalam .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): File .* dan .* identik$"
+syn match hgDiffDiffer "^\%(SL\|HG\): Berkas .* dan .* berbeda$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): File biner .* dan .* berbeda$"
+syn match hgDiffIsA "^\%(SL\|HG\): File .* adalah .* sementara file .* adalah .*"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ Tidak ada baris-baru di akhir dari berkas"
+syn match hgDiffCommon "^\%(SL\|HG\): Subdirektori sama: .* dan .*"
+
+" it
+syn match hgDiffOnly "^\%(SL\|HG\): Solo in .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): I file .* e .* sono identici$"
+syn match hgDiffDiffer "^\%(SL\|HG\): I file .* e .* sono diversi$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): I file .* e .* sono diversi$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): I file binari .* e .* sono diversi$"
+syn match hgDiffIsA "^\%(SL\|HG\): File .* è un .* mentre file .* è un .*"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ Manca newline alla fine del file"
+syn match hgDiffCommon "^\%(SL\|HG\): Sottodirectory in comune: .* e .*"
+
+" ja
+syn match hgDiffOnly "^\%(SL\|HG\): .*だけに発見: .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): ファイル.*と.*は同一$"
+syn match hgDiffDiffer "^\%(SL\|HG\): ファイル.*と.*は違います$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): バイナリー・ファイル.*と.*は違います$"
+syn match hgDiffIsA "^\%(SL\|HG\): ファイル.*は.*、ファイル.*は.*"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ ファイル末尾に改行がありません"
+syn match hgDiffCommon "^\%(SL\|HG\): 共通の下位ディレクトリー: .*と.*"
+
+" ja DiffUtils 3.3
+syn match hgDiffOnly "^\%(SL\|HG\): .* のみに存在: .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): ファイル .* と .* は同一です$"
+syn match hgDiffDiffer "^\%(SL\|HG\): ファイル .* と .* は異なります$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): バイナリーファイル .* と.* は異なります$"
+syn match hgDiffIsA "^\%(SL\|HG\): ファイル .* は .* です。一方、ファイル .* は .* です$"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ ファイル末尾に改行がありません"
+syn match hgDiffCommon "^\%(SL\|HG\): 共通のサブディレクトリー: .* と .*"
+
+" lv
+syn match hgDiffOnly "^\%(SL\|HG\): Tikai iekš .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): Fails .* un .* ir identiski$"
+syn match hgDiffDiffer "^\%(SL\|HG\): Faili .* un .* atšķiras$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Faili .* un .* atšķiras$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Binārie faili .* un .* atšķiras$"
+syn match hgDiffIsA "^\%(SL\|HG\): Fails .* ir .* kamēr fails .* ir .*"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ Nav jaunu rindu faila beigās"
+syn match hgDiffCommon "^\%(SL\|HG\): Kopējās apakšdirektorijas: .* un .*"
+
+" ms
+syn match hgDiffOnly "^\%(SL\|HG\): Hanya dalam .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): Fail .* dan .* adalah serupa$"
+syn match hgDiffDiffer "^\%(SL\|HG\): Fail .* dan .* berbeza$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Fail .* dan .* berbeza$"
+syn match hgDiffIsA "^\%(SL\|HG\): Fail .* adalah .* manakala fail .* adalah .*"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ Tiada baris baru pada penghujung fail"
+syn match hgDiffCommon "^\%(SL\|HG\): Subdirektori umum: .* dan .*"
+
+" nl
+syn match hgDiffOnly "^\%(SL\|HG\): Alleen in .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): Bestanden .* en .* zijn identiek$"
+syn match hgDiffDiffer "^\%(SL\|HG\): Bestanden .* en .* zijn verschillend$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Bestanden .* en .* zijn verschillend$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Binaire bestanden .* en .* zijn verschillend$"
+syn match hgDiffIsA "^\%(SL\|HG\): Bestand .* is een .* terwijl bestand .* een .* is$"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ Geen regeleindeteken (LF) aan einde van bestand"
+syn match hgDiffCommon "^\%(SL\|HG\): Gemeenschappelijke submappen: .* en .*"
+
+" pl
+syn match hgDiffOnly "^\%(SL\|HG\): Tylko w .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): Pliki .* i .* są identyczne$"
+syn match hgDiffDiffer "^\%(SL\|HG\): Pliki .* i .* różnią się$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Pliki .* i .* różnią się$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Binarne pliki .* i .* różnią się$"
+syn match hgDiffIsA "^\%(SL\|HG\): Plik .* jest .*, podczas gdy plik .* jest .*"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ Brak znaku nowej linii na końcu pliku"
+syn match hgDiffCommon "^\%(SL\|HG\): Wspólne podkatalogi: .* i .*"
+
+" pt_BR
+syn match hgDiffOnly "^\%(SL\|HG\): Somente em .*"
+syn match hgDiffOnly "^\%(SL\|HG\): Apenas em .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): Os aquivos .* e .* são idênticos$"
+syn match hgDiffDiffer "^\%(SL\|HG\): Os arquivos .* e .* são diferentes$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Os arquivos binários .* e .* são diferentes$"
+syn match hgDiffIsA "^\%(SL\|HG\): O arquivo .* é .* enquanto o arquivo .* é .*"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ Falta o caracter nova linha no final do arquivo"
+syn match hgDiffCommon "^\%(SL\|HG\): Subdiretórios idênticos: .* e .*"
+
+" ro
+syn match hgDiffOnly "^\%(SL\|HG\): Doar în .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): Fişierele .* şi .* sunt identice$"
+syn match hgDiffDiffer "^\%(SL\|HG\): Fişierele .* şi .* diferă$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Fişierele binare .* şi .* diferă$"
+syn match hgDiffIsA "^\%(SL\|HG\): Fişierul .* este un .* pe când fişierul .* este un .*.$"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ Nici un element de linie nouă la sfârşitul fişierului"
+syn match hgDiffCommon "^\%(SL\|HG\): Subdirectoare comune: .* şi .*.$"
+
+" ru
+syn match hgDiffOnly "^\%(SL\|HG\): Только в .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): Файлы .* и .* идентичны$"
+syn match hgDiffDiffer "^\%(SL\|HG\): Файлы .* и .* различаются$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Файлы .* и .* различаются$"
+syn match hgDiffIsA "^\%(SL\|HG\): Файл .* это .*, тогда как файл .* -- .*"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ В конце файла нет новой строки"
+syn match hgDiffCommon "^\%(SL\|HG\): Общие подкаталоги: .* и .*"
+
+" sr
+syn match hgDiffOnly "^\%(SL\|HG\): Само у .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): Датотеке „.*“ и „.*“ се подударају$"
+syn match hgDiffDiffer "^\%(SL\|HG\): Датотеке .* и .* различите$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Бинарне датотеке .* и .* различите$"
+syn match hgDiffIsA "^\%(SL\|HG\): Датотека „.*“ је „.*“ док је датотека „.*“ „.*“$"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ Без новог реда на крају датотеке"
+syn match hgDiffCommon "^\%(SL\|HG\): Заједнички поддиректоријуми: .* и .*"
+
+" sv
+syn match hgDiffOnly "^\%(SL\|HG\): Endast i .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): Filerna .* och .* är lika$"
+syn match hgDiffDiffer "^\%(SL\|HG\): Filerna .* och .* skiljer$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Filerna .* och .* skiljer$"
+syn match hgDiffIsA "^\%(SL\|HG\): Fil .* är en .* medan fil .* är en .*"
+syn match hgDiffBDiffer "^\%(SL\|HG\): De binära filerna .* och .* skiljer$"
+syn match hgDiffIsA "^\%(SL\|HG\): Filen .* är .* medan filen .* är .*"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ Ingen nyrad vid filslut"
+syn match hgDiffCommon "^\%(SL\|HG\): Lika underkataloger: .* och .*"
+
+" tr
+syn match hgDiffOnly "^\%(SL\|HG\): Yalnızca .*'da: .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): .* ve .* dosyaları birbirinin aynı$"
+syn match hgDiffDiffer "^\%(SL\|HG\): .* ve .* dosyaları birbirinden farklı$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): .* ve .* dosyaları birbirinden farklı$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): İkili .* ve .* birbirinden farklı$"
+syn match hgDiffIsA "^\%(SL\|HG\): .* dosyası, bir .*, halbuki .* dosyası bir .*"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ Dosya sonunda yenisatır yok."
+syn match hgDiffCommon "^\%(SL\|HG\): Ortak alt dizinler: .* ve .*"
+
+" uk
+syn match hgDiffOnly "^\%(SL\|HG\): Лише у .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): Файли .* та .* ідентичні$"
+syn match hgDiffDiffer "^\%(SL\|HG\): Файли .* та .* відрізняються$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Файли .* та .* відрізняються$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Двійкові файли .* та .* відрізняються$"
+syn match hgDiffIsA "^\%(SL\|HG\): Файл .* це .*, тоді як файл .* -- .*"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ Наприкінці файлу немає нового рядка"
+syn match hgDiffCommon "^\%(SL\|HG\): Спільні підкаталоги: .* та .*"
+
+" vi
+syn match hgDiffOnly "^\%(SL\|HG\): Chỉ trong .*"
+syn match hgDiffIdentical "^\%(SL\|HG\): Hai tập tin .* và .* là bằng nhau.$"
+syn match hgDiffIdentical "^\%(SL\|HG\): Cả .* và .* là cùng một tập tin$"
+syn match hgDiffDiffer "^\%(SL\|HG\): Hai tập tin .* và .* là khác nhau.$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Hai tập tin nhị phân .* và .* khác nhau$"
+syn match hgDiffIsA "^\%(SL\|HG\): Tập tin .* là một .* trong khi tập tin .* là một .*.$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): Hai tập tin .* và .* là khác nhau.$"
+syn match hgDiffIsA "^\%(SL\|HG\): Tập tin .* là một .* còn tập tin .* là một .*.$"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ Không có ký tự dòng mới tại kêt thức tập tin."
+syn match hgDiffCommon "^\%(SL\|HG\): Thư mục con chung: .* và .*"
+
+" zh_CN
+syn match hgDiffOnly "^\%(SL\|HG\): 只在 .* 存在:.*"
+syn match hgDiffIdentical "^\%(SL\|HG\): 檔案 .* 和 .* 相同$"
+syn match hgDiffDiffer "^\%(SL\|HG\): 文件 .* 和 .* 不同$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): 文件 .* 和 .* 不同$"
+syn match hgDiffIsA "^\%(SL\|HG\): 文件 .* 是.*而文件 .* 是.*"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ 文件尾没有 newline 字符"
+syn match hgDiffCommon "^\%(SL\|HG\): .* 和 .* 有共同的子目录$"
+
+" zh_TW
+syn match hgDiffOnly "^\%(SL\|HG\): 只在 .* 存在:.*"
+syn match hgDiffIdentical "^\%(SL\|HG\): 檔案 .* 和 .* 相同$"
+syn match hgDiffDiffer "^\%(SL\|HG\): 檔案 .* 與 .* 不同$"
+syn match hgDiffBDiffer "^\%(SL\|HG\): 二元碼檔 .* 與 .* 不同$"
+syn match hgDiffIsA "^\%(SL\|HG\): 檔案 .* 是.*而檔案 .* 是.*"
+syn match hgDiffNoEOL "^\%(SL\|HG\): \\ 檔案末沒有 newline 字元"
+syn match hgDiffCommon "^\%(SL\|HG\): .* 和 .* 有共同的副目錄$"
+
+endif
+
+
+syn match hgDiffRemoved "^\%(SL\|HG\): -.*"
+syn match hgDiffRemoved "^\%(SL\|HG\): <.*"
+syn match hgDiffAdded "^\%(SL\|HG\): +.*"
+syn match hgDiffAdded "^\%(SL\|HG\): >.*"
+syn match hgDiffChanged "^\%(SL\|HG\): ! .*"
+
+syn match hgDiffSubname " @@..*"ms=s+3 contained
+syn match hgDiffLine "^\%(SL\|HG\): @.*" contains=hgDiffSubname
+syn match hgDiffLine "^\%(SL\|HG\): \<\d\+\>.*"
+syn match hgDiffLine "^\%(SL\|HG\): \*\*\*\*.*"
+syn match hgDiffLine "^\%(SL\|HG\): ---$"
+
+" Some versions of diff have lines like "#c#" and "#d#" (where # is a number)
+syn match hgDiffLine "^\%(SL\|HG\): \d\+\(,\d\+\)\=[cda]\d\+\>.*"
+
+syn match hgDiffFile "^\%(SL\|HG\): diff\>.*"
+syn match hgDiffFile "^\%(SL\|HG\): Index: .*"
+syn match hgDiffFile "^\%(SL\|HG\): ==== .*"
+
+if search('^\%(SL\|HG\): @@ -\S\+ +\S\+ @@', 'nw', '', 100)
+ " unified
+ syn match hgDiffOldFile "^\%(SL\|HG\): --- .*"
+ syn match hgDiffNewFile "^\%(SL\|HG\): +++ .*"
+else
+ " context / old style
+ syn match hgDiffOldFile "^\%(SL\|HG\): \*\*\* .*"
+ syn match hgDiffNewFile "^\%(SL\|HG\): --- .*"
+endif
+
+" Used by git
+syn match hgDiffIndexLine "^\%(SL\|HG\): index \x\x\x\x.*"
+
+syn match hgDiffComment "^\%(SL\|HG\): #.*"
+
+" Define the default highlighting.
+" Only used when an item doesn't have highlighting yet
+hi def link hgDiffOldFile hgDiffFile
+hi def link hgDiffNewFile hgDiffFile
+hi def link hgDiffIndexLine PreProc
+hi def link hgDiffFile Type
+hi def link hgDiffOnly Constant
+hi def link hgDiffIdentical Constant
+hi def link hgDiffDiffer Constant
+hi def link hgDiffBDiffer Constant
+hi def link hgDiffIsA Constant
+hi def link hgDiffNoEOL Constant
+hi def link hgDiffCommon Constant
+hi def link hgDiffRemoved Special
+hi def link hgDiffChanged PreProc
+hi def link hgDiffAdded Identifier
+hi def link hgDiffLine Statement
+hi def link hgDiffSubname PreProc
+hi def link hgDiffComment Comment
+
+let b:current_syntax = "hgcommitDiff"
+
+" vim: ts=8 sw=2
diff --git a/runtime/syntax/solidity.vim b/runtime/syntax/solidity.vim
new file mode 100644
index 0000000000..e552446e10
--- /dev/null
+++ b/runtime/syntax/solidity.vim
@@ -0,0 +1,173 @@
+" Vim syntax file
+" Language: Solidity
+" Maintainer: Cothi (jiungdev@gmail.com)
+" Original Author: tomlion (https://github.com/tomlion/vim-solidity/blob/master/syntax/solidity.vim)
+" Last Changed: 2022 Sep 27
+"
+" Additional contributors:
+" Modified by thesis (https://github.com/thesis/vim-solidity/blob/main/indent/solidity.vim)
+
+if exists("b:current_syntax")
+ finish
+endif
+
+" keyword
+syn keyword solKeyword abstract anonymous as break calldata case catch constant constructor continue default switch revert require
+syn keyword solKeyword ecrecover addmod mulmod keccak256
+syn keyword solKeyword delete do else emit enum external final for function if immutable import in indexed inline
+syn keyword solKeyword interface internal is let match memory modifier new of payable pragma private public pure override virtual
+syn keyword solKeyword relocatable return returns static storage struct throw try type typeof using
+syn keyword solKeyword var view while
+
+syn keyword solConstant true false wei szabo finney ether seconds minutes hours days weeks years now
+syn keyword solConstant abi block blockhash msg tx this super selfdestruct
+
+syn keyword solBuiltinType mapping address bool
+syn keyword solBuiltinType int int8 int16 int24 int32 int40 int48 int56 int64 int72 int80 int88 int96 int104 int112 int120 int128 int136 int144 int152 int160 int168 int178 int184 int192 int200 int208 int216 int224 int232 int240 int248 int256
+syn keyword solBuiltinType uint uint8 uint16 uint24 uint32 uint40 uint48 uint56 uint64 uint72 uint80 uint88 uint96 uint104 uint112 uint120 uint128 uint136 uint144 uint152 uint160 uint168 uint178 uint184 uint192 uint200 uint208 uint216 uint224 uint232 uint240 uint248 uint256
+syn keyword solBuiltinType fixed
+syn keyword solBuiltinType fixed0x8 fixed0x16 fixed0x24 fixed0x32 fixed0x40 fixed0x48 fixed0x56 fixed0x64 fixed0x72 fixed0x80 fixed0x88 fixed0x96 fixed0x104 fixed0x112 fixed0x120 fixed0x128 fixed0x136 fixed0x144 fixed0x152 fixed0x160 fixed0x168 fixed0x178 fixed0x184 fixed0x192 fixed0x200 fixed0x208 fixed0x216 fixed0x224 fixed0x232 fixed0x240 fixed0x248 fixed0x256
+syn keyword solBuiltinType fixed8x8 fixed8x16 fixed8x24 fixed8x32 fixed8x40 fixed8x48 fixed8x56 fixed8x64 fixed8x72 fixed8x80 fixed8x88 fixed8x96 fixed8x104 fixed8x112 fixed8x120 fixed8x128 fixed8x136 fixed8x144 fixed8x152 fixed8x160 fixed8x168 fixed8x178 fixed8x184 fixed8x192 fixed8x200 fixed8x208 fixed8x216 fixed8x224 fixed8x232 fixed8x240 fixed8x248
+syn keyword solBuiltinType fixed16x8 fixed16x16 fixed16x24 fixed16x32 fixed16x40 fixed16x48 fixed16x56 fixed16x64 fixed16x72 fixed16x80 fixed16x88 fixed16x96 fixed16x104 fixed16x112 fixed16x120 fixed16x128 fixed16x136 fixed16x144 fixed16x152 fixed16x160 fixed16x168 fixed16x178 fixed16x184 fixed16x192 fixed16x200 fixed16x208 fixed16x216 fixed16x224 fixed16x232 fixed16x240
+syn keyword solBuiltinType fixed24x8 fixed24x16 fixed24x24 fixed24x32 fixed24x40 fixed24x48 fixed24x56 fixed24x64 fixed24x72 fixed24x80 fixed24x88 fixed24x96 fixed24x104 fixed24x112 fixed24x120 fixed24x128 fixed24x136 fixed24x144 fixed24x152 fixed24x160 fixed24x168 fixed24x178 fixed24x184 fixed24x192 fixed24x200 fixed24x208 fixed24x216 fixed24x224 fixed24x232
+syn keyword solBuiltinType fixed32x8 fixed32x16 fixed32x24 fixed32x32 fixed32x40 fixed32x48 fixed32x56 fixed32x64 fixed32x72 fixed32x80 fixed32x88 fixed32x96 fixed32x104 fixed32x112 fixed32x120 fixed32x128 fixed32x136 fixed32x144 fixed32x152 fixed32x160 fixed32x168 fixed32x178 fixed32x184 fixed32x192 fixed32x200 fixed32x208 fixed32x216 fixed32x224
+syn keyword solBuiltinType fixed40x8 fixed40x16 fixed40x24 fixed40x32 fixed40x40 fixed40x48 fixed40x56 fixed40x64 fixed40x72 fixed40x80 fixed40x88 fixed40x96 fixed40x104 fixed40x112 fixed40x120 fixed40x128 fixed40x136 fixed40x144 fixed40x152 fixed40x160 fixed40x168 fixed40x178 fixed40x184 fixed40x192 fixed40x200 fixed40x208 fixed40x216
+syn keyword solBuiltinType fixed48x8 fixed48x16 fixed48x24 fixed48x32 fixed48x40 fixed48x48 fixed48x56 fixed48x64 fixed48x72 fixed48x80 fixed48x88 fixed48x96 fixed48x104 fixed48x112 fixed48x120 fixed48x128 fixed48x136 fixed48x144 fixed48x152 fixed48x160 fixed48x168 fixed48x178 fixed48x184 fixed48x192 fixed48x200 fixed48x208
+syn keyword solBuiltinType fixed56x8 fixed56x16 fixed56x24 fixed56x32 fixed56x40 fixed56x48 fixed56x56 fixed56x64 fixed56x72 fixed56x80 fixed56x88 fixed56x96 fixed56x104 fixed56x112 fixed56x120 fixed56x128 fixed56x136 fixed56x144 fixed56x152 fixed56x160 fixed56x168 fixed56x178 fixed56x184 fixed56x192 fixed56x200
+syn keyword solBuiltinType fixed64x8 fixed64x16 fixed64x24 fixed64x32 fixed64x40 fixed64x48 fixed64x56 fixed64x64 fixed64x72 fixed64x80 fixed64x88 fixed64x96 fixed64x104 fixed64x112 fixed64x120 fixed64x128 fixed64x136 fixed64x144 fixed64x152 fixed64x160 fixed64x168 fixed64x178 fixed64x184 fixed64x192
+syn keyword solBuiltinType fixed72x8 fixed72x16 fixed72x24 fixed72x32 fixed72x40 fixed72x48 fixed72x56 fixed72x64 fixed72x72 fixed72x80 fixed72x88 fixed72x96 fixed72x104 fixed72x112 fixed72x120 fixed72x128 fixed72x136 fixed72x144 fixed72x152 fixed72x160 fixed72x168 fixed72x178 fixed72x184
+syn keyword solBuiltinType fixed80x8 fixed80x16 fixed80x24 fixed80x32 fixed80x40 fixed80x48 fixed80x56 fixed80x64 fixed80x72 fixed80x80 fixed80x88 fixed80x96 fixed80x104 fixed80x112 fixed80x120 fixed80x128 fixed80x136 fixed80x144 fixed80x152 fixed80x160 fixed80x168 fixed80x178
+syn keyword solBuiltinType fixed88x8 fixed88x16 fixed88x24 fixed88x32 fixed88x40 fixed88x48 fixed88x56 fixed88x64 fixed88x72 fixed88x80 fixed88x88 fixed88x96 fixed88x104 fixed88x112 fixed88x120 fixed88x128 fixed88x136 fixed88x144 fixed88x152 fixed88x160 fixed88x168
+syn keyword solBuiltinType fixed96x8 fixed96x16 fixed96x24 fixed96x32 fixed96x40 fixed96x48 fixed96x56 fixed96x64 fixed96x72 fixed96x80 fixed96x88 fixed96x96 fixed96x104 fixed96x112 fixed96x120 fixed96x128 fixed96x136 fixed96x144 fixed96x152 fixed96x160
+syn keyword solBuiltinType fixed104x8 fixed104x16 fixed104x24 fixed104x32 fixed104x40 fixed104x48 fixed104x56 fixed104x64 fixed104x72 fixed104x80 fixed104x88 fixed104x96 fixed104x104 fixed104x112 fixed104x120 fixed104x128 fixed104x136 fixed104x144 fixed104x152
+syn keyword solBuiltinType fixed112x8 fixed112x16 fixed112x24 fixed112x32 fixed112x40 fixed112x48 fixed112x56 fixed112x64 fixed112x72 fixed112x80 fixed112x88 fixed112x96 fixed112x104 fixed112x112 fixed112x120 fixed112x128 fixed112x136 fixed112x144
+syn keyword solBuiltinType fixed120x8 fixed120x16 fixed120x24 fixed120x32 fixed120x40 fixed120x48 fixed120x56 fixed120x64 fixed120x72 fixed120x80 fixed120x88 fixed120x96 fixed120x104 fixed120x112 fixed120x120 fixed120x128 fixed120x136
+syn keyword solBuiltinType fixed128x8 fixed128x16 fixed128x24 fixed128x32 fixed128x40 fixed128x48 fixed128x56 fixed128x64 fixed128x72 fixed128x80 fixed128x88 fixed128x96 fixed128x104 fixed128x112 fixed128x120 fixed128x128
+syn keyword solBuiltinType fixed136x8 fixed136x16 fixed136x24 fixed136x32 fixed136x40 fixed136x48 fixed136x56 fixed136x64 fixed136x72 fixed136x80 fixed136x88 fixed136x96 fixed136x104 fixed136x112 fixed136x120
+syn keyword solBuiltinType fixed144x8 fixed144x16 fixed144x24 fixed144x32 fixed144x40 fixed144x48 fixed144x56 fixed144x64 fixed144x72 fixed144x80 fixed144x88 fixed144x96 fixed144x104 fixed144x112
+syn keyword solBuiltinType fixed152x8 fixed152x16 fixed152x24 fixed152x32 fixed152x40 fixed152x48 fixed152x56 fixed152x64 fixed152x72 fixed152x80 fixed152x88 fixed152x96 fixed152x104
+syn keyword solBuiltinType fixed160x8 fixed160x16 fixed160x24 fixed160x32 fixed160x40 fixed160x48 fixed160x56 fixed160x64 fixed160x72 fixed160x80 fixed160x88 fixed160x96
+syn keyword solBuiltinType fixed168x8 fixed168x16 fixed168x24 fixed168x32 fixed168x40 fixed168x48 fixed168x56 fixed168x64 fixed168x72 fixed168x80 fixed168x88
+syn keyword solBuiltinType fixed176x8 fixed176x16 fixed176x24 fixed176x32 fixed176x40 fixed176x48 fixed176x56 fixed176x64 fixed176x72 fixed176x80
+syn keyword solBuiltinType fixed184x8 fixed184x16 fixed184x24 fixed184x32 fixed184x40 fixed184x48 fixed184x56 fixed184x64 fixed184x72
+syn keyword solBuiltinType fixed192x8 fixed192x16 fixed192x24 fixed192x32 fixed192x40 fixed192x48 fixed192x56 fixed192x64
+syn keyword solBuiltinType fixed200x8 fixed200x16 fixed200x24 fixed200x32 fixed200x40 fixed200x48 fixed200x56
+syn keyword solBuiltinType fixed208x8 fixed208x16 fixed208x24 fixed208x32 fixed208x40 fixed208x48
+syn keyword solBuiltinType fixed216x8 fixed216x16 fixed216x24 fixed216x32 fixed216x40
+syn keyword solBuiltinType fixed224x8 fixed224x16 fixed224x24 fixed224x32
+syn keyword solBuiltinType fixed232x8 fixed232x16 fixed232x24
+syn keyword solBuiltinType fixed240x8 fixed240x16
+syn keyword solBuiltinType fixed248x8
+syn keyword solBuiltinType ufixed
+syn keyword solBuiltinType ufixed0x8 ufixed0x16 ufixed0x24 ufixed0x32 ufixed0x40 ufixed0x48 ufixed0x56 ufixed0x64 ufixed0x72 ufixed0x80 ufixed0x88 ufixed0x96 ufixed0x104 ufixed0x112 ufixed0x120 ufixed0x128 ufixed0x136 ufixed0x144 ufixed0x152 ufixed0x160 ufixed0x168 ufixed0x178 ufixed0x184 ufixed0x192 ufixed0x200 ufixed0x208 ufixed0x216 ufixed0x224 ufixed0x232 ufixed0x240 ufixed0x248 ufixed0x256
+syn keyword solBuiltinType ufixed8x8 ufixed8x16 ufixed8x24 ufixed8x32 ufixed8x40 ufixed8x48 ufixed8x56 ufixed8x64 ufixed8x72 ufixed8x80 ufixed8x88 ufixed8x96 ufixed8x104 ufixed8x112 ufixed8x120 ufixed8x128 ufixed8x136 ufixed8x144 ufixed8x152 ufixed8x160 ufixed8x168 ufixed8x178 ufixed8x184 ufixed8x192 ufixed8x200 ufixed8x208 ufixed8x216 ufixed8x224 ufixed8x232 ufixed8x240 ufixed8x248
+syn keyword solBuiltinType ufixed16x8 ufixed16x16 ufixed16x24 ufixed16x32 ufixed16x40 ufixed16x48 ufixed16x56 ufixed16x64 ufixed16x72 ufixed16x80 ufixed16x88 ufixed16x96 ufixed16x104 ufixed16x112 ufixed16x120 ufixed16x128 ufixed16x136 ufixed16x144 ufixed16x152 ufixed16x160 ufixed16x168 ufixed16x178 ufixed16x184 ufixed16x192 ufixed16x200 ufixed16x208 ufixed16x216 ufixed16x224 ufixed16x232 ufixed16x240
+syn keyword solBuiltinType ufixed24x8 ufixed24x16 ufixed24x24 ufixed24x32 ufixed24x40 ufixed24x48 ufixed24x56 ufixed24x64 ufixed24x72 ufixed24x80 ufixed24x88 ufixed24x96 ufixed24x104 ufixed24x112 ufixed24x120 ufixed24x128 ufixed24x136 ufixed24x144 ufixed24x152 ufixed24x160 ufixed24x168 ufixed24x178 ufixed24x184 ufixed24x192 ufixed24x200 ufixed24x208 ufixed24x216 ufixed24x224 ufixed24x232
+syn keyword solBuiltinType ufixed32x8 ufixed32x16 ufixed32x24 ufixed32x32 ufixed32x40 ufixed32x48 ufixed32x56 ufixed32x64 ufixed32x72 ufixed32x80 ufixed32x88 ufixed32x96 ufixed32x104 ufixed32x112 ufixed32x120 ufixed32x128 ufixed32x136 ufixed32x144 ufixed32x152 ufixed32x160 ufixed32x168 ufixed32x178 ufixed32x184 ufixed32x192 ufixed32x200 ufixed32x208 ufixed32x216 ufixed32x224
+syn keyword solBuiltinType ufixed40x8 ufixed40x16 ufixed40x24 ufixed40x32 ufixed40x40 ufixed40x48 ufixed40x56 ufixed40x64 ufixed40x72 ufixed40x80 ufixed40x88 ufixed40x96 ufixed40x104 ufixed40x112 ufixed40x120 ufixed40x128 ufixed40x136 ufixed40x144 ufixed40x152 ufixed40x160 ufixed40x168 ufixed40x178 ufixed40x184 ufixed40x192 ufixed40x200 ufixed40x208 ufixed40x216
+syn keyword solBuiltinType ufixed48x8 ufixed48x16 ufixed48x24 ufixed48x32 ufixed48x40 ufixed48x48 ufixed48x56 ufixed48x64 ufixed48x72 ufixed48x80 ufixed48x88 ufixed48x96 ufixed48x104 ufixed48x112 ufixed48x120 ufixed48x128 ufixed48x136 ufixed48x144 ufixed48x152 ufixed48x160 ufixed48x168 ufixed48x178 ufixed48x184 ufixed48x192 ufixed48x200 ufixed48x208
+syn keyword solBuiltinType ufixed56x8 ufixed56x16 ufixed56x24 ufixed56x32 ufixed56x40 ufixed56x48 ufixed56x56 ufixed56x64 ufixed56x72 ufixed56x80 ufixed56x88 ufixed56x96 ufixed56x104 ufixed56x112 ufixed56x120 ufixed56x128 ufixed56x136 ufixed56x144 ufixed56x152 ufixed56x160 ufixed56x168 ufixed56x178 ufixed56x184 ufixed56x192 ufixed56x200
+syn keyword solBuiltinType ufixed64x8 ufixed64x16 ufixed64x24 ufixed64x32 ufixed64x40 ufixed64x48 ufixed64x56 ufixed64x64 ufixed64x72 ufixed64x80 ufixed64x88 ufixed64x96 ufixed64x104 ufixed64x112 ufixed64x120 ufixed64x128 ufixed64x136 ufixed64x144 ufixed64x152 ufixed64x160 ufixed64x168 ufixed64x178 ufixed64x184 ufixed64x192
+syn keyword solBuiltinType ufixed72x8 ufixed72x16 ufixed72x24 ufixed72x32 ufixed72x40 ufixed72x48 ufixed72x56 ufixed72x64 ufixed72x72 ufixed72x80 ufixed72x88 ufixed72x96 ufixed72x104 ufixed72x112 ufixed72x120 ufixed72x128 ufixed72x136 ufixed72x144 ufixed72x152 ufixed72x160 ufixed72x168 ufixed72x178 ufixed72x184
+syn keyword solBuiltinType ufixed80x8 ufixed80x16 ufixed80x24 ufixed80x32 ufixed80x40 ufixed80x48 ufixed80x56 ufixed80x64 ufixed80x72 ufixed80x80 ufixed80x88 ufixed80x96 ufixed80x104 ufixed80x112 ufixed80x120 ufixed80x128 ufixed80x136 ufixed80x144 ufixed80x152 ufixed80x160 ufixed80x168 ufixed80x178
+syn keyword solBuiltinType ufixed88x8 ufixed88x16 ufixed88x24 ufixed88x32 ufixed88x40 ufixed88x48 ufixed88x56 ufixed88x64 ufixed88x72 ufixed88x80 ufixed88x88 ufixed88x96 ufixed88x104 ufixed88x112 ufixed88x120 ufixed88x128 ufixed88x136 ufixed88x144 ufixed88x152 ufixed88x160 ufixed88x168
+syn keyword solBuiltinType ufixed96x8 ufixed96x16 ufixed96x24 ufixed96x32 ufixed96x40 ufixed96x48 ufixed96x56 ufixed96x64 ufixed96x72 ufixed96x80 ufixed96x88 ufixed96x96 ufixed96x104 ufixed96x112 ufixed96x120 ufixed96x128 ufixed96x136 ufixed96x144 ufixed96x152 ufixed96x160
+syn keyword solBuiltinType ufixed104x8 ufixed104x16 ufixed104x24 ufixed104x32 ufixed104x40 ufixed104x48 ufixed104x56 ufixed104x64 ufixed104x72 ufixed104x80 ufixed104x88 ufixed104x96 ufixed104x104 ufixed104x112 ufixed104x120 ufixed104x128 ufixed104x136 ufixed104x144 ufixed104x152
+syn keyword solBuiltinType ufixed112x8 ufixed112x16 ufixed112x24 ufixed112x32 ufixed112x40 ufixed112x48 ufixed112x56 ufixed112x64 ufixed112x72 ufixed112x80 ufixed112x88 ufixed112x96 ufixed112x104 ufixed112x112 ufixed112x120 ufixed112x128 ufixed112x136 ufixed112x144
+syn keyword solBuiltinType ufixed120x8 ufixed120x16 ufixed120x24 ufixed120x32 ufixed120x40 ufixed120x48 ufixed120x56 ufixed120x64 ufixed120x72 ufixed120x80 ufixed120x88 ufixed120x96 ufixed120x104 ufixed120x112 ufixed120x120 ufixed120x128 ufixed120x136
+syn keyword solBuiltinType ufixed128x8 ufixed128x16 ufixed128x24 ufixed128x32 ufixed128x40 ufixed128x48 ufixed128x56 ufixed128x64 ufixed128x72 ufixed128x80 ufixed128x88 ufixed128x96 ufixed128x104 ufixed128x112 ufixed128x120 ufixed128x128
+syn keyword solBuiltinType ufixed136x8 ufixed136x16 ufixed136x24 ufixed136x32 ufixed136x40 ufixed136x48 ufixed136x56 ufixed136x64 ufixed136x72 ufixed136x80 ufixed136x88 ufixed136x96 ufixed136x104 ufixed136x112 ufixed136x120
+syn keyword solBuiltinType ufixed144x8 ufixed144x16 ufixed144x24 ufixed144x32 ufixed144x40 ufixed144x48 ufixed144x56 ufixed144x64 ufixed144x72 ufixed144x80 ufixed144x88 ufixed144x96 ufixed144x104 ufixed144x112
+syn keyword solBuiltinType ufixed152x8 ufixed152x16 ufixed152x24 ufixed152x32 ufixed152x40 ufixed152x48 ufixed152x56 ufixed152x64 ufixed152x72 ufixed152x80 ufixed152x88 ufixed152x96 ufixed152x104
+syn keyword solBuiltinType ufixed160x8 ufixed160x16 ufixed160x24 ufixed160x32 ufixed160x40 ufixed160x48 ufixed160x56 ufixed160x64 ufixed160x72 ufixed160x80 ufixed160x88 ufixed160x96
+syn keyword solBuiltinType ufixed168x8 ufixed168x16 ufixed168x24 ufixed168x32 ufixed168x40 ufixed168x48 ufixed168x56 ufixed168x64 ufixed168x72 ufixed168x80 ufixed168x88
+syn keyword solBuiltinType ufixed176x8 ufixed176x16 ufixed176x24 ufixed176x32 ufixed176x40 ufixed176x48 ufixed176x56 ufixed176x64 ufixed176x72 ufixed176x80
+syn keyword solBuiltinType ufixed184x8 ufixed184x16 ufixed184x24 ufixed184x32 ufixed184x40 ufixed184x48 ufixed184x56 ufixed184x64 ufixed184x72
+syn keyword solBuiltinType ufixed192x8 ufixed192x16 ufixed192x24 ufixed192x32 ufixed192x40 ufixed192x48 ufixed192x56 ufixed192x64
+syn keyword solBuiltinType ufixed200x8 ufixed200x16 ufixed200x24 ufixed200x32 ufixed200x40 ufixed200x48 ufixed200x56
+syn keyword solBuiltinType ufixed208x8 ufixed208x16 ufixed208x24 ufixed208x32 ufixed208x40 ufixed208x48
+syn keyword solBuiltinType ufixed216x8 ufixed216x16 ufixed216x24 ufixed216x32 ufixed216x40
+syn keyword solBuiltinType ufixed224x8 ufixed224x16 ufixed224x24 ufixed224x32
+syn keyword solBuiltinType ufixed232x8 ufixed232x16 ufixed232x24
+syn keyword solBuiltinType ufixed240x8 ufixed240x16
+syn keyword solBuiltinType ufixed248x8
+syn keyword solBuiltinType string string1 string2 string3 string4 string5 string6 string7 string8 string9 string10 string11 string12 string13 string14 string15 string16 string17 string18 string19 string20 string21 string22 string23 string24 string25 string26 string27 string28 string29 string30 string31 string32
+syn keyword solBuiltinType byte bytes bytes1 bytes2 bytes3 bytes4 bytes5 bytes6 bytes7 bytes8 bytes9 bytes10 bytes11 bytes12 bytes13 bytes14 bytes15 bytes16 bytes17 bytes18 bytes19 bytes20 bytes21 bytes22 bytes23 bytes24 bytes25 bytes26 bytes27 bytes28 bytes29 bytes30 bytes31 bytes32
+
+hi def link solKeyword Keyword
+hi def link solConstant Constant
+hi def link solBuiltinType Type
+hi def link solBuiltinFunction Keyword
+
+syn match solOperator /\(!\||\|&\|+\|-\|<\|>\|=\|%\|\/\|*\|\~\|\^\)/
+syn match solNumber /\<-\=\d\+L\=\>\|\<0[xX]\x\+\>/
+syn match solFloat /\<-\=\%(\d\+\.\d\+\|\d\+\.\|\.\d\+\)\%([eE][+-]\=\d\+\)\=\>/
+
+syn region solString start=+"+ skip=+\\\\\|\\$"\|\\"+ end=+"+
+syn region solString start=+'+ skip=+\\\\\|\\$'\|\\'+ end=+'+
+
+hi def link solOperator Operator
+hi def link solNumber Number
+hi def link solFloat Float
+hi def link solString String
+
+" Function
+syn match solFunction /\<function\>/ nextgroup=solFuncName,solFuncArgs skipwhite
+syn match solFuncName contained /\<[a-zA-Z_$][0-9a-zA-Z_$]*/ nextgroup=solFuncArgs skipwhite
+
+syn region solFuncArgs contained matchgroup=solFuncParens start='(' end=')' contains=solFuncArgCommas,solBuiltinType nextgroup=solModifierName,solFuncReturns,solFuncBody keepend skipwhite skipempty
+syn match solModifierName contained /\<[a-zA-Z_$][0-9a-zA-Z_$]*/ nextgroup=solModifierArgs,solModifierName skipwhite
+syn region solModifierArgs contained matchgroup=solFuncParens start='(' end=')' contains=solFuncArgCommas nextgroup=solModifierName,solFuncReturns,solFuncBody skipwhite
+syn region solFuncReturns contained matchgroup=solFuncParens nextgroup=solFuncBody start='(' end=')' contains=solFuncArgCommas,solBuiltinType skipwhite
+
+syn match solFuncArgCommas contained ','
+syn region solFuncBody start="{" end="}" fold transparent
+
+hi def link solFunction Type
+hi def link solFuncName Function
+hi def link solModifierName Function
+
+" Yul blocks
+syn match yul /\<assembly\>/ skipwhite skipempty nextgroup=yulBody
+syn region yulBody contained start='{' end='}' fold contains=yulAssemblyOp,solNumber,yulVarDeclaration,solLineComment,solComment skipwhite skipempty
+syn keyword yulAssemblyOp contained stop add sub mul div sdiv mod smod exp not lt gt slt sgt eq iszero and or xor byte shl shr sar addmod mulmod signextend keccak256 pc pop mload mstore mstore8 sload sstore msize gas address balance selfbalance caller callvalue calldataload calldatasize calldatacopy codesize codecopy extcodesize extcodecopy returndatasize returndatacopy extcodehash create create2 call callcode delegatecall staticcall return revert selfdestruct invalid log0 log1 log2 log3 log4 chainid basefee origin gasprice blockhash coinbase timestamp number difficulty gaslimit
+syn keyword yulVarDeclaration contained let
+
+hi def link yul Keyword
+hi def link yulVarDeclaration Keyword
+hi def link yulAssemblyOp Keyword
+
+" Contract
+syn match solContract /\<\%(contract\|library\|interface\)\>/ nextgroup=solContractName skipwhite
+syn match solContractName contained /\<[a-zA-Z_$][0-9a-zA-Z_$]*/ nextgroup=solContractParent skipwhite
+syn region solContractParent contained start='is' end='{' contains=solContractName,solContractNoise,solContractCommas skipwhite skipempty
+syn match solContractNoise contained 'is' containedin=solContractParent
+syn match solContractCommas contained ','
+
+hi def link solContract Type
+hi def link solContractName Function
+
+" Event
+syn match solEvent /\<event\>/ nextgroup=solEventName,solEventArgs skipwhite
+syn match solEventName contained /\<[a-zA-Z_$][0-9a-zA-Z_$]*/ nextgroup=solEventArgs skipwhite
+syn region solEventArgs contained matchgroup=solFuncParens start='(' end=')' contains=solEventArgCommas,solBuiltinType,solEventArgSpecial skipwhite skipempty
+syn match solEventArgCommas contained ','
+syn match solEventArgSpecial contained 'indexed'
+
+hi def link solEvent Type
+hi def link solEventName Function
+hi def link solEventArgSpecial Label
+
+" Comment
+syn keyword solCommentTodo TODO FIXME XXX TBD contained
+syn match solNatSpec contained /@title\|@author\|@notice\|@dev\|@param\|@inheritdoc\|@return/
+syn region solLineComment start=+\/\/+ end=+$+ contains=solCommentTodo,solNatSpec,@Spell
+syn region solLineComment start=+^\s*\/\/+ skip=+\n\s*\/\/+ end=+$+ contains=solCommentTodo,solNatSpec,@Spell fold
+syn region solComment start="/\*" end="\*/" contains=solCommentTodo,solNatSpec,@Spell fold
+
+hi def link solCommentTodo Todo
+hi def link solNatSpec Label
+hi def link solLineComment Comment
+hi def link solComment Comment
+
+let b:current_syntax = "solidity"
diff --git a/runtime/syntax/srt.vim b/runtime/syntax/srt.vim
new file mode 100644
index 0000000000..12fb264d8e
--- /dev/null
+++ b/runtime/syntax/srt.vim
@@ -0,0 +1,62 @@
+" Vim syntax file
+" Language: SubRip
+" Maintainer: ObserverOfTime <chronobserver@disroot.org>
+" Filenames: *.srt
+" Last Change: 2022 Sep 12
+
+if exists('b:current_syntax')
+ finish
+endif
+
+syn spell toplevel
+
+syn cluster srtSpecial contains=srtBold,srtItalics,srtStrikethrough,srtUnderline,srtFont,srtTag,srtEscape
+
+" Number
+syn match srtNumber /^\d\+$/ contains=@NoSpell
+
+" Range
+syn match srtRange /\d\d:\d\d:\d\d[,.]\d\d\d --> \d\d:\d\d:\d\d[,.]\d\d\d/ skipwhite contains=srtArrow,srtTime nextgroup=srtCoordinates
+syn match srtArrow /-->/ contained contains=@NoSpell
+syn match srtTime /\d\d:\d\d:\d\d[,.]\d\d\d/ contained contains=@NoSpell
+syn match srtCoordinates /X1:\d\+ X2:\d\+ Y1:\d\+ Y2:\d\+/ contained contains=@NoSpell
+
+" Bold
+syn region srtBold matchgroup=srtFormat start=+<b>+ end=+</b>+ contains=@srtSpecial
+syn region srtBold matchgroup=srtFormat start=+{b}+ end=+{/b}+ contains=@srtSpecial
+
+" Italics
+syn region srtItalics matchgroup=srtFormat start=+<i>+ end=+</i>+ contains=@srtSpecial
+syn region srtItalics matchgroup=srtFormat start=+{i}+ end=+{/i}+ contains=@srtSpecial
+
+" Strikethrough
+syn region srtStrikethrough matchgroup=srtFormat start=+<s>+ end=+</s>+ contains=@srtSpecial
+syn region srtStrikethrough matchgroup=srtFormat start=+{s}+ end=+{/s}+ contains=@srtSpecial
+
+" Underline
+syn region srtUnderline matchgroup=srtFormat start=+<u>+ end=+</u>+ contains=@srtSpecial
+syn region srtUnderline matchgroup=srtFormat start=+{u}+ end=+{/u}+ contains=@srtSpecial
+
+" Font
+syn region srtFont matchgroup=srtFormat start=+<font[^>]\{-}>+ end=+</font>+ contains=@srtSpecial
+
+" ASS tags
+syn match srtTag /{\\[^}]\{1,}}/ contains=@NoSpell
+
+" Special characters
+syn match srtEscape /\\[nNh]/ contains=@NoSpell
+
+hi def link srtArrow Delimiter
+hi def link srtCoordinates Label
+hi def link srtEscape SpecialChar
+hi def link srtFormat Special
+hi def link srtNumber Number
+hi def link srtTag PreProc
+hi def link srtTime String
+
+hi srtBold cterm=bold gui=bold
+hi srtItalics cterm=italic gui=italic
+hi srtStrikethrough cterm=strikethrough gui=strikethrough
+hi srtUnderline cterm=underline gui=underline
+
+let b:current_syntax = 'srt'
diff --git a/runtime/syntax/ssa.vim b/runtime/syntax/ssa.vim
new file mode 100644
index 0000000000..a5dbf37c30
--- /dev/null
+++ b/runtime/syntax/ssa.vim
@@ -0,0 +1,63 @@
+" Vim syntax file
+" Language: SubStation Alpha
+" Maintainer: ObserverOfTime <chronobserver@disroot.org>
+" Filenames: *.ass,*.ssa
+" Last Change: 2022 Oct 10
+
+if exists('b:current_syntax')
+ finish
+endif
+
+" Comments
+syn keyword ssaTodo TODO FIXME NOTE XXX contained
+syn match ssaComment /^\(;\|!:\).*$/ contains=ssaTodo,@Spell
+syn match ssaTextComment /{[^}]*}/ contained contains=@Spell
+
+" Sections
+syn match ssaSection /^\[[a-zA-Z0-9+ ]\+\]$/
+
+" Headers
+syn match ssaHeader /^[^;!:]\+:/ skipwhite nextgroup=ssaField
+
+" Fields
+syn match ssaField /[^,]*/ contained skipwhite nextgroup=ssaDelimiter
+
+" Time
+syn match ssaTime /\d:\d\d:\d\d\.\d\d/ contained skipwhite nextgroup=ssaDelimiter
+
+" Delimiter
+syn match ssaDelimiter /,/ contained skipwhite nextgroup=ssaField,ssaTime,ssaText
+
+" Text
+syn match ssaText /\(^Dialogue:\(.*,\)\{9}\)\@<=.*$/ contained contains=@ssaTags,@Spell
+syn cluster ssaTags contains=ssaOverrideTag,ssaEscapeChar,ssaTextComment,ssaItalics,ssaBold,ssaUnderline,ssaStrikeout
+
+" Override tags
+syn match ssaOverrideTag /{\\[^}]\+}/ contained contains=@NoSpell
+
+" Special characters
+syn match ssaEscapeChar /\\[nNh{}]/ contained contains=@NoSpell
+
+" Markup
+syn region ssaItalics start=/{\\i1}/ end=/{\\i0}/ matchgroup=ssaOverrideTag keepend oneline contained contains=@ssaTags,@Spell
+syn region ssaBold start=/{\\b1}/ end=/{\\b0}/ matchgroup=ssaOverrideTag keepend oneline contained contains=@ssaTags,@Spell
+syn region ssaUnderline start=/{\\u1}/ end=/{\\u0}/ matchgroup=ssaOverrideTag keepend oneline contained contains=@ssaTags,@Spell
+syn region ssaStrikeout start=/{\\s1}/ end=/{\\s0}/ matchgroup=ssaOverrideTag keepend oneline contained contains=@ssaTags,@Spell
+
+hi def link ssaDelimiter Delimiter
+hi def link ssaComment Comment
+hi def link ssaEscapeChar SpecialChar
+hi def link ssaField String
+hi def link ssaHeader Label
+hi def link ssaSection StorageClass
+hi def link ssaOverrideTag Special
+hi def link ssaTextComment Comment
+hi def link ssaTime Number
+hi def link ssaTodo Todo
+
+hi ssaBold cterm=bold gui=bold
+hi ssaItalics cterm=italic gui=italic
+hi ssaStrikeout cterm=strikethrough gui=strikethrough
+hi ssaUnderline cterm=underline gui=underline
+
+let b:current_syntax = 'srt'
diff --git a/runtime/syntax/sshconfig.vim b/runtime/syntax/sshconfig.vim
index ae3c7dd8cc..750289d83e 100644
--- a/runtime/syntax/sshconfig.vim
+++ b/runtime/syntax/sshconfig.vim
@@ -6,9 +6,10 @@
" Contributor: Leonard Ehrenfried <leonard.ehrenfried@web.de>
" Contributor: Karsten Hopp <karsten@redhat.com>
" Contributor: Dean, Adam Kenneth <adam.ken.dean@hpe.com>
-" Last Change: 2021 Mar 29
+" Last Change: 2022 Nov 10
" Added RemoteCommand from pull request #4809
" Included additional keywords from Martin.
+" Included PR #5753
" SSH Version: 8.5p1
"
@@ -57,12 +58,12 @@ syn match sshconfigCiphers "\<aes256-gcm@openssh\.com\>"
syn match sshconfigCiphers "\<chacha20-poly1305@openssh\.com\>"
syn keyword sshconfigMAC hmac-sha1
-syn keyword sshconfigMAC mac-sha1-96
-syn keyword sshconfigMAC mac-sha2-256
-syn keyword sshconfigMAC mac-sha2-512
-syn keyword sshconfigMAC mac-md5
-syn keyword sshconfigMAC mac-md5-96
-syn keyword sshconfigMAC mac-ripemd160
+syn keyword sshconfigMAC hmac-sha1-96
+syn keyword sshconfigMAC hmac-sha2-256
+syn keyword sshconfigMAC hmac-sha2-512
+syn keyword sshconfigMAC hmac-md5
+syn keyword sshconfigMAC hmac-md5-96
+syn keyword sshconfigMAC hmac-ripemd160
syn match sshconfigMAC "\<hmac-ripemd160@openssh\.com\>"
syn match sshconfigMAC "\<umac-64@openssh\.com\>"
syn match sshconfigMAC "\<umac-128@openssh\.com\>"
@@ -78,16 +79,24 @@ syn match sshconfigMAC "\<umac-128-etm@openssh\.com\>"
syn keyword sshconfigHostKeyAlgo ssh-ed25519
syn match sshconfigHostKeyAlgo "\<ssh-ed25519-cert-v01@openssh\.com\>"
+syn match sshconfigHostKeyAlgo "\<sk-ssh-ed25519@openssh\.com\>"
+syn match sshconfigHostKeyAlgo "\<sk-ssh-ed25519-cert-v01@openssh\.com\>"
syn keyword sshconfigHostKeyAlgo ssh-rsa
+syn keyword sshconfigHostKeyAlgo rsa-sha2-256
+syn keyword sshconfigHostKeyAlgo rsa-sha2-512
syn keyword sshconfigHostKeyAlgo ssh-dss
syn keyword sshconfigHostKeyAlgo ecdsa-sha2-nistp256
syn keyword sshconfigHostKeyAlgo ecdsa-sha2-nistp384
syn keyword sshconfigHostKeyAlgo ecdsa-sha2-nistp521
+syn match sshconfigHostKeyAlgo "\<sk-ecdsa-sha2-nistp256@openssh\.com\>"
syn match sshconfigHostKeyAlgo "\<ssh-rsa-cert-v01@openssh\.com\>"
+syn match sshconfigHostKeyAlgo "\<rsa-sha2-256-cert-v01@openssh\.com\>"
+syn match sshconfigHostKeyAlgo "\<rsa-sha2-512-cert-v01@openssh\.com\>"
syn match sshconfigHostKeyAlgo "\<ssh-dss-cert-v01@openssh\.com\>"
syn match sshconfigHostKeyAlgo "\<ecdsa-sha2-nistp256-cert-v01@openssh\.com\>"
syn match sshconfigHostKeyAlgo "\<ecdsa-sha2-nistp384-cert-v01@openssh\.com\>"
syn match sshconfigHostKeyAlgo "\<ecdsa-sha2-nistp521-cert-v01@openssh\.com\>"
+syn match sshconfigHostKeyAlgo "\<sk-ecdsa-sha2-nistp256-cert-v01@openssh\.com\>"
syn keyword sshconfigPreferredAuth hostbased publickey password gssapi-with-mic
syn keyword sshconfigPreferredAuth keyboard-interactive
@@ -162,6 +171,7 @@ syn keyword sshconfigKeyword EnableSSHKeysign
syn keyword sshconfigKeyword EscapeChar
syn keyword sshconfigKeyword ExitOnForwardFailure
syn keyword sshconfigKeyword FingerprintHash
+syn keyword sshconfigKeyword ForkAfterAuthentication
syn keyword sshconfigKeyword ForwardAgent
syn keyword sshconfigKeyword ForwardX11
syn keyword sshconfigKeyword ForwardX11Timeout
@@ -212,13 +222,16 @@ syn keyword sshconfigKeyword RekeyLimit
syn keyword sshconfigKeyword RemoteCommand
syn keyword sshconfigKeyword RemoteForward
syn keyword sshconfigKeyword RequestTTY
+syn keyword sshconfigKeyword RequiredRSASize
syn keyword sshconfigKeyword RevokedHostKeys
syn keyword sshconfigKeyword SecurityKeyProvider
syn keyword sshconfigKeyword SendEnv
syn keyword sshconfigKeyword ServerAliveCountMax
syn keyword sshconfigKeyword ServerAliveInterval
+syn keyword sshconfigKeyword SessionType
syn keyword sshconfigKeyword SmartcardDevice
syn keyword sshconfigKeyword SetEnv
+syn keyword sshconfigKeyword StdinNull
syn keyword sshconfigKeyword StreamLocalBindMask
syn keyword sshconfigKeyword StreamLocalBindUnlink
syn keyword sshconfigKeyword StrictHostKeyChecking
diff --git a/runtime/syntax/sshdconfig.vim b/runtime/syntax/sshdconfig.vim
index 6b0d2af4d1..c0d9c3f598 100644
--- a/runtime/syntax/sshdconfig.vim
+++ b/runtime/syntax/sshdconfig.vim
@@ -7,7 +7,7 @@
" Contributor: Leonard Ehrenfried <leonard.ehrenfried@web.de>
" Contributor: Karsten Hopp <karsten@redhat.com>
" Originally: 2009-07-09
-" Last Change: 2021-03-29
+" Last Change: 2022 Nov 10
" SSH Version: 8.5p1
"
@@ -59,12 +59,12 @@ syn match sshdconfigCiphers "\<aes256-gcm@openssh\.com\>"
syn match sshdconfigCiphers "\<chacha20-poly1305@openssh\.com\>"
syn keyword sshdconfigMAC hmac-sha1
-syn keyword sshdconfigMAC mac-sha1-96
-syn keyword sshdconfigMAC mac-sha2-256
-syn keyword sshdconfigMAC mac-sha2-512
-syn keyword sshdconfigMAC mac-md5
-syn keyword sshdconfigMAC mac-md5-96
-syn keyword sshdconfigMAC mac-ripemd160
+syn keyword sshdconfigMAC hmac-sha1-96
+syn keyword sshdconfigMAC hmac-sha2-256
+syn keyword sshdconfigMAC hmac-sha2-512
+syn keyword sshdconfigMAC hmac-md5
+syn keyword sshdconfigMAC hmac-md5-96
+syn keyword sshdconfigMAC hmac-ripemd160
syn match sshdconfigMAC "\<hmac-ripemd160@openssh\.com\>"
syn match sshdconfigMAC "\<umac-64@openssh\.com\>"
syn match sshdconfigMAC "\<umac-128@openssh\.com\>"
@@ -221,6 +221,7 @@ syn keyword sshdconfigKeyword Match
syn keyword sshdconfigKeyword MaxAuthTries
syn keyword sshdconfigKeyword MaxSessions
syn keyword sshdconfigKeyword MaxStartups
+syn keyword sshdconfigKeyword ModuliFile
syn keyword sshdconfigKeyword PasswordAuthentication
syn keyword sshdconfigKeyword PerSourceMaxStartups
syn keyword sshdconfigKeyword PerSourceNetBlockSize
@@ -244,6 +245,7 @@ syn keyword sshdconfigKeyword PubkeyAuthentication
syn keyword sshdconfigKeyword PubkeyAuthOptions
syn keyword sshdconfigKeyword RSAAuthentication
syn keyword sshdconfigKeyword RekeyLimit
+syn keyword sshdconfigKeyword RequiredRSASize
syn keyword sshdconfigKeyword RevokedKeys
syn keyword sshdconfigKeyword RDomain
syn keyword sshdconfigKeyword RhostsRSAAuthentication
@@ -258,6 +260,8 @@ syn keyword sshdconfigKeyword Subsystem
syn keyword sshdconfigKeyword SyslogFacility
syn keyword sshdconfigKeyword TCPKeepAlive
syn keyword sshdconfigKeyword TrustedUserCAKeys
+syn keyword sshdconfigKeyword UseBlacklist
+syn keyword sshdconfigKeyword UseBlocklist
syn keyword sshdconfigKeyword UseDNS
syn keyword sshdconfigKeyword UseLogin
syn keyword sshdconfigKeyword UsePAM
diff --git a/runtime/syntax/swayconfig.vim b/runtime/syntax/swayconfig.vim
index d9f31da47b..996b8f596c 100644
--- a/runtime/syntax/swayconfig.vim
+++ b/runtime/syntax/swayconfig.vim
@@ -2,7 +2,8 @@
" Language: sway window manager config
" Original Author: James Eapen <james.eapen@vai.org>
" Maintainer: James Eapen <james.eapen@vai.org>
-" Version: 0.11.1
+" Version: 0.1.6
+" Reference version (jamespeapen/swayconfig.vim): 0.11.6
" Last Change: 2022 Aug 08
" References:
@@ -23,10 +24,6 @@ scriptencoding utf-8
" Error
"syn match swayConfigError /.*/
-" Group mode/bar
-syn keyword swayConfigBlockKeyword set input contained
-syn region swayConfigBlock start=+.*s\?{$+ end=+^}$+ contains=i3ConfigBlockKeyword,swayConfigBlockKeyword,i3ConfigString,i3ConfigBind,i3ConfigComment,i3ConfigFont,i3ConfigFocusWrappingType,i3ConfigColor,i3ConfigVariable transparent keepend extend
-
" binding
syn keyword swayConfigBindKeyword bindswitch bindgesture contained
syn match swayConfigBind /^\s*\(bindswitch\)\s\+.*$/ contains=i3ConfigVariable,i3ConfigBindKeyword,swayConfigBindKeyword,i3ConfigVariableAndModifier,i3ConfigNumber,i3ConfigUnit,i3ConfigUnitOr,i3ConfigBindArgument,i3ConfigModifier,i3ConfigAction,i3ConfigString,i3ConfigGapStyleKeyword,i3ConfigBorderStyleKeyword
@@ -45,7 +42,7 @@ syn match swayConfigFloating /^\s*floating\s\+\(enable\|disable\|toggle\)\s*$/ c
syn clear i3ConfigFloatingModifier
syn keyword swayConfigFloatingModifier floating_modifier contained
-syn match swayConfigFloatingMouseAction /^\s\?.*floating_modifier\s.*\(normal\|inverted\)$/ contains=swayConfigFloatingModifier,i3ConfigVariable
+syn match swayConfigFloatingMouseAction /^\s\?.*floating_modifier\s\S\+\s\?\(normal\|inverted\|none\)\?$/ contains=swayConfigFloatingModifier,i3ConfigVariable
" Gaps
syn clear i3ConfigSmartBorderKeyword
@@ -57,6 +54,10 @@ syn match swayConfigSmartBorder /^\s*smart_borders\s\+\(on\|no_gaps\|off\)\s\?$/
syn keyword swayConfigClientColorKeyword focused_tab_title contained
syn match swayConfigClientColor /^\s*client.\w\+\s\+.*$/ contains=i3ConfigClientColorKeyword,i3ConfigColor,i3ConfigVariable,i3ConfigClientColorKeyword,swayConfigClientColorKeyword
+" Input config
+syn keyword swayConfigInputKeyword input contained
+syn match swayConfigInput /^\s*input\s\+.*$/ contains=swayConfigInputKeyword
+
" set display outputs
syn match swayConfigOutput /^\s*output\s\+.*$/ contains=i3ConfigOutput
@@ -65,21 +66,34 @@ syn keyword swayConfigFocusKeyword focus contained
syn keyword swayConfigFocusType output contained
syn match swayConfigFocus /^\s*focus\soutput\s.*$/ contains=swayConfigFocusKeyword,swayConfigFocusType
+" focus follows mouse
+syn clear i3ConfigFocusFollowsMouseType
+syn clear i3ConfigFocusFollowsMouse
+
+syn keyword swayConfigFocusFollowsMouseType yes no always contained
+syn match swayConfigFocusFollowsMouse /^\s*focus_follows_mouse\s\+\(yes\|no\|always\)\s\?$/ contains=i3ConfigFocusFollowsMouseKeyword,swayConfigFocusFollowsMouseType
+
+
" xwayland
syn keyword swayConfigXwaylandKeyword xwayland contained
syn match swayConfigXwaylandModifier /^\s*xwayland\s\+\(enable\|disable\|force\)\s\?$/ contains=swayConfigXwaylandKeyword
+" Group mode/bar
+syn clear i3ConfigBlock
+syn region swayConfigBlock start=+.*s\?{$+ end=+^}$+ contains=i3ConfigBlockKeyword,i3ConfigString,i3ConfigBind,i3ConfigInitializeKeyword,i3ConfigComment,i3ConfigFont,i3ConfigFocusWrappingType,i3ConfigColor,i3ConfigVariable,swayConfigInputKeyword,i3ConfigOutput transparent keepend extend
+
"hi def link swayConfigError Error
hi def link i3ConfigFloating Error
hi def link swayConfigFloating Type
hi def link swayConfigFloatingMouseAction Type
hi def link swayConfigFocusKeyword Type
hi def link swayConfigSmartBorderKeyword Type
+hi def link swayConfigInputKeyword Type
+hi def link swayConfigFocusFollowsMouseType Type
hi def link swayConfigBindGestureCommand Identifier
hi def link swayConfigBindGestureDirection Constant
hi def link swayConfigBindGesturePinchDirection Constant
hi def link swayConfigBindKeyword Identifier
-hi def link swayConfigBlockKeyword Identifier
hi def link swayConfigClientColorKeyword Identifier
hi def link swayConfigFloatingKeyword Identifier
hi def link swayConfigFloatingModifier Identifier
diff --git a/runtime/syntax/synload.vim b/runtime/syntax/synload.vim
index b88cd95103..056e38bf79 100644
--- a/runtime/syntax/synload.vim
+++ b/runtime/syntax/synload.vim
@@ -30,7 +30,7 @@ fun! s:SynSet()
unlet b:current_syntax
endif
- let s = expand("<amatch>")
+ 0verbose let s = expand("<amatch>")
if s == "ON"
" :set syntax=ON
if &filetype == ""
diff --git a/runtime/syntax/syntax.vim b/runtime/syntax/syntax.vim
index ac7dc314b9..5ec99c7e05 100644
--- a/runtime/syntax/syntax.vim
+++ b/runtime/syntax/syntax.vim
@@ -27,12 +27,13 @@ else
endif
" Set up the connection between FileType and Syntax autocommands.
-" This makes the syntax automatically set when the file type is detected.
+" This makes the syntax automatically set when the file type is detected
+" unless treesitter highlighting is enabled.
+" Avoid an error when 'verbose' is set and <amatch> expansion fails.
augroup syntaxset
- au! FileType * exe "set syntax=" . expand("<amatch>")
+ au! FileType * if !exists('b:ts_highlight') | 0verbose exe "set syntax=" . expand("<amatch>") | endif
augroup END
-
" Execute the syntax autocommands for the each buffer.
" If the filetype wasn't detected yet, do that now.
" Always do the syntaxset autocommands, for buffers where the 'filetype'
diff --git a/runtime/syntax/vdf.vim b/runtime/syntax/vdf.vim
new file mode 100644
index 0000000000..c690b706ea
--- /dev/null
+++ b/runtime/syntax/vdf.vim
@@ -0,0 +1,54 @@
+" Vim syntax file
+" Language: Valve Data Format
+" Maintainer: ObserverOfTime <chronobserver@disroot.org>
+" Filenames: *.vdf
+" Last Change: 2022 Sep 15
+
+if exists('b:current_syntax')
+ finish
+endif
+
+let s:cpo_save = &cpoptions
+set cpoptions&vim
+
+" Comment
+syn keyword vdfTodo contained TODO FIXME XXX
+syn match vdfComment +//.*+ contains=vdfTodo
+
+" Macro
+syn match vdfMacro /^\s*#.*/
+
+" Tag
+syn region vdfTag start=/"/ skip=/\\"/ end=/"/
+ \ nextgroup=vdfValue skipwhite oneline
+
+" Section
+syn region vdfSection matchgroup=vdfBrace
+ \ start=/{/ end=/}/ transparent fold
+ \ contains=vdfTag,vdfSection,vdfComment,vdfConditional
+
+" Conditional
+syn match vdfConditional /\[\$\w\{1,1021}\]/ nextgroup=vdfTag
+
+" Value
+syn region vdfValue start=/"/ skip=/\\"/ end=/"/
+ \ oneline contained contains=vdfVariable,vdfNumber,vdfEscape
+syn region vdfVariable start=/%/ skip=/\\%/ end=/%/ oneline contained
+syn match vdfEscape /\\[nt\\"]/ contained
+syn match vdfNumber /"-\?\d\+"/ contained
+
+hi def link vdfBrace Delimiter
+hi def link vdfComment Comment
+hi def link vdfConditional Constant
+hi def link vdfEscape SpecialChar
+hi def link vdfMacro Macro
+hi def link vdfNumber Number
+hi def link vdfTag Keyword
+hi def link vdfTodo Todo
+hi def link vdfValue String
+hi def link vdfVariable Identifier
+
+let b:current_syntax = 'vdf'
+
+let &cpoptions = s:cpo_save
+unlet s:cpo_save
diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim
index 2b1c58c449..a1c39e4dcf 100644
--- a/runtime/syntax/vim.vim
+++ b/runtime/syntax/vim.vim
@@ -53,7 +53,7 @@ syn case ignore
syn keyword vimGroup contained Comment Constant String Character Number Boolean Float Identifier Function Statement Conditional Repeat Label Operator Keyword Exception PreProc Include Define Macro PreCondit Type StorageClass Structure Typedef Special SpecialChar Tag Delimiter SpecialComment Debug Underlined Ignore Error Todo
" Default highlighting groups {{{2
-syn keyword vimHLGroup contained ColorColumn Cursor CursorColumn CursorIM CursorLine 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 MessageWindow 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
@@ -368,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\|eval\>\)\=\s*\z(\L\S*\)' matchgroup=vimLetHereDocStop end='^\s*\z1\s*$'
+VimFoldh syn region vimLetHereDoc matchgroup=vimLetHereDocStart start='=<<\s\+\%(trim\s\+\)\=\%(eval\s\+\)\=\s*\z(\L\S*\)' matchgroup=vimLetHereDocStop end='^\s*\z1\s*$'
" Abbreviations: {{{2
" =============
@@ -451,7 +451,7 @@ if !exists("g:vimsyn_noerror") && !exists("g:vimsyn_novimfunctionerror")
syn match vimBufnrWarn /\<bufnr\s*(\s*["']\.['"]\s*)/
endif
-syn match vimNotFunc "\<if\>\|\<el\%[seif]\>\|\<return\>\|\<while\>" skipwhite nextgroup=vimOper,vimOperParen,vimVar,vimFunc,vimNotation
+syn match vimNotFunc "\<if\>\|\<el\%[seif]\>\|\<retu\%[rn]\>\|\<while\>" skipwhite nextgroup=vimOper,vimOperParen,vimVar,vimFunc,vimNotation
" Norm: {{{2
" ====
diff --git a/runtime/syntax/wdl.vim b/runtime/syntax/wdl.vim
new file mode 100644
index 0000000000..3b8369e8bd
--- /dev/null
+++ b/runtime/syntax/wdl.vim
@@ -0,0 +1,41 @@
+" Vim syntax file
+" Language: wdl
+" Maintainer: Matt Dunford (zenmatic@gmail.com)
+" URL: https://github.com/zenmatic/vim-syntax-wdl
+" Last Change: 2022 Nov 24
+
+" https://github.com/openwdl/wdl
+
+" quit when a (custom) syntax file was already loaded
+if exists("b:current_syntax")
+ finish
+endif
+
+syn case match
+
+syn keyword wdlStatement alias task input command runtime input output workflow call scatter import as meta parameter_meta in version
+syn keyword wdlConditional if then else
+syn keyword wdlType struct Array String File Int Float Boolean Map Pair Object
+
+syn keyword wdlFunctions stdout stderr read_lines read_tsv read_map read_object read_objects read_json read_int read_string read_float read_boolean write_lines write_tsv write_map write_object write_objects write_json size sub range transpose zip cross length flatten prefix select_first defined basename floor ceil round
+
+syn region wdlCommandSection start="<<<" end=">>>"
+
+syn region wdlString start=+"+ skip=+\\\\\|\\"+ end=+"+
+syn region wdlString start=+'+ skip=+\\\\\|\\'+ end=+'+
+
+" Comments; their contents
+syn keyword wdlTodo contained TODO FIXME XXX BUG
+syn cluster wdlCommentGroup contains=wdlTodo
+syn region wdlComment start="#" end="$" contains=@wdlCommentGroup
+
+hi def link wdlStatement Statement
+hi def link wdlConditional Conditional
+hi def link wdlType Type
+hi def link wdlFunctions Function
+hi def link wdlString String
+hi def link wdlCommandSection String
+hi def link wdlComment Comment
+hi def link wdlTodo Todo
+
+let b:current_syntax = 'wdl'
diff --git a/runtime/syntax/zig.vim b/runtime/syntax/zig.vim
new file mode 100644
index 0000000000..e09b5e8815
--- /dev/null
+++ b/runtime/syntax/zig.vim
@@ -0,0 +1,292 @@
+" Vim syntax file
+" Language: Zig
+" Upstream: https://github.com/ziglang/zig.vim
+
+if exists("b:current_syntax")
+ finish
+endif
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+let s:zig_syntax_keywords = {
+ \ 'zigBoolean': ["true"
+ \ , "false"]
+ \ , 'zigNull': ["null"]
+ \ , 'zigType': ["bool"
+ \ , "f16"
+ \ , "f32"
+ \ , "f64"
+ \ , "f80"
+ \ , "f128"
+ \ , "void"
+ \ , "type"
+ \ , "anytype"
+ \ , "anyerror"
+ \ , "anyframe"
+ \ , "volatile"
+ \ , "linksection"
+ \ , "noreturn"
+ \ , "allowzero"
+ \ , "i0"
+ \ , "u0"
+ \ , "isize"
+ \ , "usize"
+ \ , "comptime_int"
+ \ , "comptime_float"
+ \ , "c_short"
+ \ , "c_ushort"
+ \ , "c_int"
+ \ , "c_uint"
+ \ , "c_long"
+ \ , "c_ulong"
+ \ , "c_longlong"
+ \ , "c_ulonglong"
+ \ , "c_longdouble"
+ \ , "anyopaque"]
+ \ , 'zigConstant': ["undefined"
+ \ , "unreachable"]
+ \ , 'zigConditional': ["if"
+ \ , "else"
+ \ , "switch"]
+ \ , 'zigRepeat': ["while"
+ \ , "for"]
+ \ , 'zigComparatorWord': ["and"
+ \ , "or"
+ \ , "orelse"]
+ \ , 'zigStructure': ["struct"
+ \ , "enum"
+ \ , "union"
+ \ , "error"
+ \ , "packed"
+ \ , "opaque"]
+ \ , 'zigException': ["error"]
+ \ , 'zigVarDecl': ["var"
+ \ , "const"
+ \ , "comptime"
+ \ , "threadlocal"]
+ \ , 'zigDummyVariable': ["_"]
+ \ , 'zigKeyword': ["fn"
+ \ , "try"
+ \ , "test"
+ \ , "pub"
+ \ , "usingnamespace"]
+ \ , 'zigExecution': ["return"
+ \ , "break"
+ \ , "continue"]
+ \ , 'zigMacro': ["defer"
+ \ , "errdefer"
+ \ , "async"
+ \ , "nosuspend"
+ \ , "await"
+ \ , "suspend"
+ \ , "resume"
+ \ , "export"
+ \ , "extern"]
+ \ , 'zigPreProc': ["catch"
+ \ , "inline"
+ \ , "noinline"
+ \ , "asm"
+ \ , "callconv"
+ \ , "noalias"]
+ \ , 'zigBuiltinFn': ["align"
+ \ , "@addWithOverflow"
+ \ , "@as"
+ \ , "@atomicLoad"
+ \ , "@atomicStore"
+ \ , "@bitCast"
+ \ , "@breakpoint"
+ \ , "@alignCast"
+ \ , "@alignOf"
+ \ , "@cDefine"
+ \ , "@cImport"
+ \ , "@cInclude"
+ \ , "@cUndef"
+ \ , "@clz"
+ \ , "@cmpxchgWeak"
+ \ , "@cmpxchgStrong"
+ \ , "@compileError"
+ \ , "@compileLog"
+ \ , "@ctz"
+ \ , "@popCount"
+ \ , "@divExact"
+ \ , "@divFloor"
+ \ , "@divTrunc"
+ \ , "@embedFile"
+ \ , "@export"
+ \ , "@extern"
+ \ , "@tagName"
+ \ , "@TagType"
+ \ , "@errorName"
+ \ , "@call"
+ \ , "@errorReturnTrace"
+ \ , "@fence"
+ \ , "@fieldParentPtr"
+ \ , "@field"
+ \ , "@unionInit"
+ \ , "@frameAddress"
+ \ , "@import"
+ \ , "@newStackCall"
+ \ , "@asyncCall"
+ \ , "@intToPtr"
+ \ , "@max"
+ \ , "@min"
+ \ , "@memcpy"
+ \ , "@memset"
+ \ , "@mod"
+ \ , "@mulAdd"
+ \ , "@mulWithOverflow"
+ \ , "@splat"
+ \ , "@src"
+ \ , "@bitOffsetOf"
+ \ , "@byteOffsetOf"
+ \ , "@offsetOf"
+ \ , "@OpaqueType"
+ \ , "@panic"
+ \ , "@prefetch"
+ \ , "@ptrCast"
+ \ , "@ptrToInt"
+ \ , "@rem"
+ \ , "@returnAddress"
+ \ , "@setCold"
+ \ , "@Type"
+ \ , "@shuffle"
+ \ , "@reduce"
+ \ , "@select"
+ \ , "@setRuntimeSafety"
+ \ , "@setEvalBranchQuota"
+ \ , "@setFloatMode"
+ \ , "@shlExact"
+ \ , "@This"
+ \ , "@hasDecl"
+ \ , "@hasField"
+ \ , "@shlWithOverflow"
+ \ , "@shrExact"
+ \ , "@sizeOf"
+ \ , "@bitSizeOf"
+ \ , "@sqrt"
+ \ , "@byteSwap"
+ \ , "@subWithOverflow"
+ \ , "@intCast"
+ \ , "@floatCast"
+ \ , "@intToFloat"
+ \ , "@floatToInt"
+ \ , "@boolToInt"
+ \ , "@errSetCast"
+ \ , "@truncate"
+ \ , "@typeInfo"
+ \ , "@typeName"
+ \ , "@TypeOf"
+ \ , "@atomicRmw"
+ \ , "@intToError"
+ \ , "@errorToInt"
+ \ , "@intToEnum"
+ \ , "@enumToInt"
+ \ , "@setAlignStack"
+ \ , "@frame"
+ \ , "@Frame"
+ \ , "@frameSize"
+ \ , "@bitReverse"
+ \ , "@Vector"
+ \ , "@sin"
+ \ , "@cos"
+ \ , "@tan"
+ \ , "@exp"
+ \ , "@exp2"
+ \ , "@log"
+ \ , "@log2"
+ \ , "@log10"
+ \ , "@fabs"
+ \ , "@floor"
+ \ , "@ceil"
+ \ , "@trunc"
+ \ , "@wasmMemorySize"
+ \ , "@wasmMemoryGrow"
+ \ , "@round"]
+ \ }
+
+function! s:syntax_keyword(dict)
+ for key in keys(a:dict)
+ execute 'syntax keyword' key join(a:dict[key], ' ')
+ endfor
+endfunction
+
+call s:syntax_keyword(s:zig_syntax_keywords)
+
+syntax match zigType "\v<[iu][1-9]\d*>"
+syntax match zigOperator display "\V\[-+/*=^&?|!><%~]"
+syntax match zigArrowCharacter display "\V->"
+
+" 12_34 (. but not ..)? (12_34)? (exponent 12_34)?
+syntax match zigDecNumber display "\v<\d%(_?\d)*%(\.\.@!)?%(\d%(_?\d)*)?%([eE][+-]?\d%(_?\d)*)?"
+syntax match zigHexNumber display "\v<0x\x%(_?\x)*%(\.\.@!)?%(\x%(_?\x)*)?%([pP][+-]?\d%(_?\d)*)?"
+syntax match zigOctNumber display "\v<0o\o%(_?\o)*"
+syntax match zigBinNumber display "\v<0b[01]%(_?[01])*"
+
+syntax match zigCharacterInvalid display contained /b\?'\zs[\n\r\t']\ze'/
+syntax match zigCharacterInvalidUnicode display contained /b'\zs[^[:cntrl:][:graph:][:alnum:][:space:]]\ze'/
+syntax match zigCharacter /b'\([^\\]\|\\\(.\|x\x\{2}\)\)'/ contains=zigEscape,zigEscapeError,zigCharacterInvalid,zigCharacterInvalidUnicode
+syntax match zigCharacter /'\([^\\]\|\\\(.\|x\x\{2}\|u\x\{4}\|U\x\{6}\)\)'/ contains=zigEscape,zigEscapeUnicode,zigEscapeError,zigCharacterInvalid
+
+syntax region zigBlock start="{" end="}" transparent fold
+
+syntax region zigCommentLine start="//" end="$" contains=zigTodo,@Spell
+syntax region zigCommentLineDoc start="//[/!]/\@!" end="$" contains=zigTodo,@Spell
+
+syntax match zigMultilineStringPrefix /c\?\\\\/ contained containedin=zigMultilineString
+syntax region zigMultilineString matchgroup=zigMultilineStringDelimiter start="c\?\\\\" end="$" contains=zigMultilineStringPrefix display
+
+syntax keyword zigTodo contained TODO
+
+syntax region zigString matchgroup=zigStringDelimiter start=+c\?"+ skip=+\\\\\|\\"+ end=+"+ oneline contains=zigEscape,zigEscapeUnicode,zigEscapeError,@Spell
+syntax match zigEscapeError display contained /\\./
+syntax match zigEscape display contained /\\\([nrt\\'"]\|x\x\{2}\)/
+syntax match zigEscapeUnicode display contained /\\\(u\x\{4}\|U\x\{6}\)/
+
+highlight default link zigDecNumber zigNumber
+highlight default link zigHexNumber zigNumber
+highlight default link zigOctNumber zigNumber
+highlight default link zigBinNumber zigNumber
+
+highlight default link zigBuiltinFn Statement
+highlight default link zigKeyword Keyword
+highlight default link zigType Type
+highlight default link zigCommentLine Comment
+highlight default link zigCommentLineDoc Comment
+highlight default link zigDummyVariable Comment
+highlight default link zigTodo Todo
+highlight default link zigString String
+highlight default link zigStringDelimiter String
+highlight default link zigMultilineString String
+highlight default link zigMultilineStringContent String
+highlight default link zigMultilineStringPrefix String
+highlight default link zigMultilineStringDelimiter Delimiter
+highlight default link zigCharacterInvalid Error
+highlight default link zigCharacterInvalidUnicode zigCharacterInvalid
+highlight default link zigCharacter Character
+highlight default link zigEscape Special
+highlight default link zigEscapeUnicode zigEscape
+highlight default link zigEscapeError Error
+highlight default link zigBoolean Boolean
+highlight default link zigNull Boolean
+highlight default link zigConstant Constant
+highlight default link zigNumber Number
+highlight default link zigArrowCharacter zigOperator
+highlight default link zigOperator Operator
+highlight default link zigStructure Structure
+highlight default link zigExecution Special
+highlight default link zigMacro Macro
+highlight default link zigConditional Conditional
+highlight default link zigComparatorWord Keyword
+highlight default link zigRepeat Repeat
+highlight default link zigSpecial Special
+highlight default link zigVarDecl Function
+highlight default link zigPreProc PreProc
+highlight default link zigException Exception
+
+delfunction s:syntax_keyword
+
+let b:current_syntax = "zig"
+
+let &cpo = s:cpo_save
+unlet! s:cpo_save
diff --git a/runtime/syntax/zir.vim b/runtime/syntax/zir.vim
new file mode 100644
index 0000000000..6553d322b7
--- /dev/null
+++ b/runtime/syntax/zir.vim
@@ -0,0 +1,49 @@
+" Vim syntax file
+" Language: Zir
+" Upstream: https://github.com/ziglang/zig.vim
+
+if exists("b:current_syntax")
+ finish
+endif
+let b:current_syntax = "zir"
+
+syn region zirCommentLine start=";" end="$" contains=zirTodo,@Spell
+
+syn region zirBlock start="{" end="}" transparent fold
+
+syn keyword zirKeyword primitive fntype int str as ptrtoint fieldptr deref asm unreachable export ref fn
+
+syn keyword zirTodo contained TODO
+
+syn region zirString start=+c\?"+ skip=+\\\\\|\\"+ end=+"+ oneline contains=zirEscape,zirEscapeUnicode,zirEscapeError,@Spell
+
+syn match zirEscapeError display contained /\\./
+syn match zirEscape display contained /\\\([nrt\\'"]\|x\x\{2}\)/
+syn match zirEscapeUnicode display contained /\\\(u\x\{4}\|U\x\{6}\)/
+
+syn match zirDecNumber display "\<[0-9]\+\%(.[0-9]\+\)\=\%([eE][+-]\?[0-9]\+\)\="
+syn match zirHexNumber display "\<0x[a-fA-F0-9]\+\%([a-fA-F0-9]\+\%([pP][+-]\?[0-9]\+\)\?\)\="
+syn match zirOctNumber display "\<0o[0-7]\+"
+syn match zirBinNumber display "\<0b[01]\+\%(.[01]\+\%([eE][+-]\?[0-9]\+\)\?\)\="
+
+syn match zirGlobal display "[^a-zA-Z0-9_]\?\zs@[a-zA-Z0-9_]\+"
+syn match zirLocal display "[^a-zA-Z0-9_]\?\zs%[a-zA-Z0-9_]\+"
+
+hi def link zirCommentLine Comment
+hi def link zirTodo Todo
+
+hi def link zirKeyword Keyword
+
+hi def link zirString Constant
+
+hi def link zirEscape Special
+hi def link zirEscapeUnicode zirEscape
+hi def link zirEscapeError Error
+
+hi def link zirDecNumber Constant
+hi def link zirHexNumber Constant
+hi def link zirOctNumber Constant
+hi def link zirBinNumber Constant
+
+hi def link zirGlobal Identifier
+hi def link zirLocal Identifier
diff --git a/runtime/syntax/zsh.vim b/runtime/syntax/zsh.vim
index bab89b916e..69671c59ca 100644
--- a/runtime/syntax/zsh.vim
+++ b/runtime/syntax/zsh.vim
@@ -2,7 +2,7 @@
" Language: Zsh shell script
" Maintainer: Christian Brabandt <cb@256bit.org>
" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
-" Latest Revision: 2020-11-21
+" Latest Revision: 2022-07-26
" License: Vim (see :h license)
" Repository: https://github.com/chrisbra/vim-zsh
@@ -19,9 +19,9 @@ function! s:ContainedGroup()
" vim-pandoc syntax defines the @langname cluster for embedded syntax languages
" However, if no syntax is defined yet, `syn list @zsh` will return
" "No syntax items defined", so make sure the result is actually a valid syn cluster
- for cluster in ['markdownHighlightzsh', 'zsh']
+ for cluster in ['markdownHighlight_zsh', 'zsh']
try
- " markdown syntax defines embedded clusters as @markdownhighlight<lang>,
+ " markdown syntax defines embedded clusters as @markdownhighlight_<lang>,
" pandoc just uses @<lang>, so check both for both clusters
let a=split(execute('syn list @'. cluster), "\n")
if len(a) == 2 && a[0] =~# '^---' && a[1] =~? cluster
@@ -48,17 +48,28 @@ syn match zshPOSIXQuoted '\\u[0-9a-fA-F]\{1,4}'
syn match zshPOSIXQuoted '\\U[1-9a-fA-F]\{1,8}'
syn region zshString matchgroup=zshStringDelimiter start=+"+ end=+"+
- \ contains=zshQuoted,@zshDerefs,@zshSubst fold
+ \ contains=zshQuoted,@zshDerefs,@zshSubstQuoted fold
syn region zshString matchgroup=zshStringDelimiter start=+'+ end=+'+ fold
syn region zshPOSIXString matchgroup=zshStringDelimiter start=+\$'+
\ skip=+\\[\\']+ end=+'+ contains=zshPOSIXQuoted,zshQuoted
syn match zshJobSpec '%\(\d\+\|?\=\w\+\|[%+-]\)'
+syn match zshNumber '[+-]\=\<\d\+\>'
+syn match zshNumber '[+-]\=\<0x\x\+\>'
+syn match zshNumber '[+-]\=\<0\o\+\>'
+syn match zshNumber '[+-]\=\d\+#[-+]\=\w\+\>'
+syn match zshNumber '[+-]\=\d\+\.\d\+\>'
+
syn keyword zshPrecommand noglob nocorrect exec command builtin - time
syn keyword zshDelimiter do done end
-syn keyword zshConditional if then elif else fi case in esac select
+syn keyword zshConditional if then elif else fi esac select
+
+syn keyword zshCase case nextgroup=zshCaseWord skipwhite
+syn match zshCaseWord /\S\+/ nextgroup=zshCaseIn skipwhite contained transparent
+syn keyword zshCaseIn in nextgroup=zshCasePattern skipwhite skipnl contained
+syn match zshCasePattern /\S[^)]*)/ contained
syn keyword zshRepeat while until repeat
@@ -73,9 +84,13 @@ syn match zshFunction '^\s*\k\+\ze\s*()'
syn match zshOperator '||\|&&\|;\|&!\='
-syn match zshRedir '\d\=\(<\|<>\|<<<\|<&\s*[0-9p-]\=\)'
-syn match zshRedir '\d\=\(>\|>>\|>&\s*[0-9p-]\=\|&>\|>>&\|&>>\)[|!]\='
-syn match zshRedir '|&\='
+ " <<<, <, <>, and variants.
+syn match zshRedir '\d\=\(<<<\|<&\s*[0-9p-]\=\|<>\?\)'
+ " >, >>, and variants.
+syn match zshRedir '\d\=\(>&\s*[0-9p-]\=\|&>>\?\|>>\?&\?\)[|!]\='
+ " | and |&, but only if it's not preceeded or
+ " followed by a | to avoid matching ||.
+syn match zshRedir '|\@1<!|&\=|\@!'
syn region zshHereDoc matchgroup=zshRedir
\ start='<\@<!<<\s*\z([^<]\S*\)'
@@ -125,7 +140,7 @@ syn keyword zshCommands alias autoload bg bindkey break bye cap cd
\ enable eval exec exit export false fc fg
\ functions getcap getln getopts hash history
\ jobs kill let limit log logout popd print
- \ printf pushd pushln pwd r read
+ \ printf prompt pushd pushln pwd r read
\ rehash return sched set setcap shift
\ source stat suspend test times trap true
\ ttyctl type ulimit umask unalias unfunction
@@ -139,10 +154,120 @@ syn case ignore
syn match zshOptStart
\ /\v^\s*%(%(un)?setopt|set\s+[-+]o)/
\ nextgroup=zshOption skipwhite
-syn match zshOption nextgroup=zshOption,zshComment skipwhite contained /\v
- \ <%(no_?)?%(
- \ auto_?cd|auto_?pushd|cdable_?vars|cd_?silent|chase_?dots|chase_?links|posix_?cd|pushd_?ignore_?dups|pushd_?minus|pushd_?silent|pushd_?to_?home|always_?last_?prompt|always_?to_?end|auto_?list|auto_?menu|auto_?name_?dirs|auto_?param_?keys|auto_?param_?slash|auto_?remove_?slash|bash_?auto_?list|complete_?aliases|complete_?in_?word|glob_?complete|hash_?list_?all|list_?ambiguous|list_?beep|list_?packed|list_?rows_?first|list_?types|menu_?complete|rec_?exact|bad_?pattern|bare_?glob_?qual|brace_?ccl|case_?glob|case_?match|case_?paths|csh_?null_?glob|equals|extended_?glob|force_?float|glob|glob_?assign|glob_?dots|glob_?star_?short|glob_?subst|hist_?subst_?pattern|ignore_?braces|ignore_?close_?braces|ksh_?glob|magic_?equal_?subst|mark_?dirs|multibyte|nomatch|null_?glob|numeric_?glob_?sort|rc_?expand_?param|rematch_?pcre|sh_?glob|unset|warn_?create_?global|warn_?nested_?var|warnnestedvar|append_?history|bang_?hist|extended_?history|hist_?allow_?clobber|hist_?beep|hist_?expire_?dups_?first|hist_?fcntl_?lock|hist_?find_?no_?dups|hist_?ignore_?all_?dups|hist_?ignore_?dups|hist_?ignore_?space|hist_?lex_?words|hist_?no_?functions|hist_?no_?store|hist_?reduce_?blanks|hist_?save_?by_?copy|hist_?save_?no_?dups|hist_?verify|inc_?append_?history|inc_?append_?history_?time|share_?history|all_?export|global_?export|global_?rcs|rcs|aliases|clobber|clobber_?empty|correct|correct_?all|dvorak|flow_?control|ignore_?eof|interactive_?comments|hash_?cmds|hash_?dirs|hash_?executables_?only|mail_?warning|path_?dirs|path_?script|print_?eight_?bit|print_?exit_?value|rc_?quotes|rm_?star_?silent|rm_?star_?wait|short_?loops|short_?repeat|sun_?keyboard_?hack|auto_?continue|auto_?resume|bg_?nice|check_?jobs|check_?running_?jobs|hup|long_?list_?jobs|monitor|notify|posix_?jobs|prompt_?bang|prompt_?cr|prompt_?sp|prompt_?percent|prompt_?subst|transient_?rprompt|alias_?func_?def|c_?bases|c_?precedences|debug_?before_?cmd|err_?exit|err_?return|eval_?lineno|exec|function_?argzero|local_?loops|local_?options|local_?patterns|local_?traps|multi_?func_?def|multios|octal_?zeroes|pipe_?fail|source_?trace|typeset_?silent|typeset_?to_?unset|verbose|xtrace|append_?create|bash_?rematch|bsd_?echo|continue_?on_?error|csh_?junkie_?history|csh_?junkie_?loops|csh_?junkie_?quotes|csh_?nullcmd|ksh_?arrays|ksh_?autoload|ksh_?option_?print|ksh_?typeset|ksh_?zero_?subscript|posix_?aliases|posix_?argzero|posix_?builtins|posix_?identifiers|posix_?strings|posix_?traps|sh_?file_?expansion|sh_?nullcmd|sh_?option_?letters|sh_?word_?split|traps_?async|interactive|login|privileged|restricted|shin_?stdin|single_?command|beep|combining_?chars|emacs|overstrike|single_?line_?zle|vi|zle|brace_?expand|dot_?glob|hash_?all|hist_?append|hist_?expand|log|mail_?warn|one_?cmd|physical|prompt_?vars|stdin|track_?all|no_?match
- \)>/
+syn keyword zshOption nextgroup=zshOption,zshComment skipwhite contained
+ \ auto_cd no_auto_cd autocd noautocd auto_pushd no_auto_pushd autopushd noautopushd cdable_vars
+ \ no_cdable_vars cdablevars nocdablevars cd_silent no_cd_silent cdsilent nocdsilent chase_dots
+ \ no_chase_dots chasedots nochasedots chase_links no_chase_links chaselinks nochaselinks posix_cd
+ \ posixcd no_posix_cd noposixcd pushd_ignore_dups no_pushd_ignore_dups pushdignoredups
+ \ nopushdignoredups pushd_minus no_pushd_minus pushdminus nopushdminus pushd_silent no_pushd_silent
+ \ pushdsilent nopushdsilent pushd_to_home no_pushd_to_home pushdtohome nopushdtohome
+ \ always_last_prompt no_always_last_prompt alwayslastprompt noalwayslastprompt always_to_end
+ \ no_always_to_end alwaystoend noalwaystoend auto_list no_auto_list autolist noautolist auto_menu
+ \ no_auto_menu automenu noautomenu auto_name_dirs no_auto_name_dirs autonamedirs noautonamedirs
+ \ auto_param_keys no_auto_param_keys autoparamkeys noautoparamkeys auto_param_slash
+ \ no_auto_param_slash autoparamslash noautoparamslash auto_remove_slash no_auto_remove_slash
+ \ autoremoveslash noautoremoveslash bash_auto_list no_bash_auto_list bashautolist nobashautolist
+ \ complete_aliases no_complete_aliases completealiases nocompletealiases complete_in_word
+ \ no_complete_in_word completeinword nocompleteinword glob_complete no_glob_complete globcomplete
+ \ noglobcomplete hash_list_all no_hash_list_all hashlistall nohashlistall list_ambiguous
+ \ no_list_ambiguous listambiguous nolistambiguous list_beep no_list_beep listbeep nolistbeep
+ \ list_packed no_list_packed listpacked nolistpacked list_rows_first no_list_rows_first listrowsfirst
+ \ nolistrowsfirst list_types no_list_types listtypes nolisttypes menu_complete no_menu_complete
+ \ menucomplete nomenucomplete rec_exact no_rec_exact recexact norecexact bad_pattern no_bad_pattern
+ \ badpattern nobadpattern bare_glob_qual no_bare_glob_qual bareglobqual nobareglobqual brace_ccl
+ \ no_brace_ccl braceccl nobraceccl case_glob no_case_glob caseglob nocaseglob case_match
+ \ no_case_match casematch nocasematch case_paths no_case_paths casepaths nocasepaths csh_null_glob
+ \ no_csh_null_glob cshnullglob nocshnullglob equals no_equals noequals extended_glob no_extended_glob
+ \ extendedglob noextendedglob force_float no_force_float forcefloat noforcefloat glob no_glob noglob
+ \ glob_assign no_glob_assign globassign noglobassign glob_dots no_glob_dots globdots noglobdots
+ \ glob_star_short no_glob_star_short globstarshort noglobstarshort glob_subst no_glob_subst globsubst
+ \ noglobsubst hist_subst_pattern no_hist_subst_pattern histsubstpattern nohistsubstpattern
+ \ ignore_braces no_ignore_braces ignorebraces noignorebraces ignore_close_braces
+ \ no_ignore_close_braces ignoreclosebraces noignoreclosebraces ksh_glob no_ksh_glob kshglob nokshglob
+ \ magic_equal_subst no_magic_equal_subst magicequalsubst nomagicequalsubst mark_dirs no_mark_dirs
+ \ markdirs nomarkdirs multibyte no_multibyte nomultibyte nomatch no_nomatch nonomatch null_glob
+ \ no_null_glob nullglob nonullglob numeric_glob_sort no_numeric_glob_sort numericglobsort
+ \ nonumericglobsort rc_expand_param no_rc_expand_param rcexpandparam norcexpandparam rematch_pcre
+ \ no_rematch_pcre rematchpcre norematchpcre sh_glob no_sh_glob shglob noshglob unset no_unset nounset
+ \ warn_create_global no_warn_create_global warncreateglobal nowarncreateglobal warn_nested_var
+ \ no_warn_nested_var warnnestedvar no_warnnestedvar append_history no_append_history appendhistory
+ \ noappendhistory bang_hist no_bang_hist banghist nobanghist extended_history no_extended_history
+ \ extendedhistory noextendedhistory hist_allow_clobber no_hist_allow_clobber histallowclobber
+ \ nohistallowclobber hist_beep no_hist_beep histbeep nohistbeep hist_expire_dups_first
+ \ no_hist_expire_dups_first histexpiredupsfirst nohistexpiredupsfirst hist_fcntl_lock
+ \ no_hist_fcntl_lock histfcntllock nohistfcntllock hist_find_no_dups no_hist_find_no_dups
+ \ histfindnodups nohistfindnodups hist_ignore_all_dups no_hist_ignore_all_dups histignorealldups
+ \ nohistignorealldups hist_ignore_dups no_hist_ignore_dups histignoredups nohistignoredups
+ \ hist_ignore_space no_hist_ignore_space histignorespace nohistignorespace hist_lex_words
+ \ no_hist_lex_words histlexwords nohistlexwords hist_no_functions no_hist_no_functions
+ \ histnofunctions nohistnofunctions hist_no_store no_hist_no_store histnostore nohistnostore
+ \ hist_reduce_blanks no_hist_reduce_blanks histreduceblanks nohistreduceblanks hist_save_by_copy
+ \ no_hist_save_by_copy histsavebycopy nohistsavebycopy hist_save_no_dups no_hist_save_no_dups
+ \ histsavenodups nohistsavenodups hist_verify no_hist_verify histverify nohistverify
+ \ inc_append_history no_inc_append_history incappendhistory noincappendhistory
+ \ inc_append_history_time no_inc_append_history_time incappendhistorytime noincappendhistorytime
+ \ share_history no_share_history sharehistory nosharehistory all_export no_all_export allexport
+ \ noallexport global_export no_global_export globalexport noglobalexport global_rcs no_global_rcs
+ \ globalrcs noglobalrcs rcs no_rcs norcs aliases no_aliases noaliases clobber no_clobber noclobber
+ \ clobber_empty no_clobber_empty clobberempty noclobberempty correct no_correct nocorrect correct_all
+ \ no_correct_all correctall nocorrectall dvorak no_dvorak nodvorak flow_control no_flow_control
+ \ flowcontrol noflowcontrol ignore_eof no_ignore_eof ignoreeof noignoreeof interactive_comments
+ \ no_interactive_comments interactivecomments nointeractivecomments hash_cmds no_hash_cmds hashcmds
+ \ nohashcmds hash_dirs no_hash_dirs hashdirs nohashdirs hash_executables_only
+ \ no_hash_executables_only hashexecutablesonly nohashexecutablesonly mail_warning no_mail_warning
+ \ mailwarning nomailwarning path_dirs no_path_dirs pathdirs nopathdirs path_script no_path_script
+ \ pathscript nopathscript print_eight_bit no_print_eight_bit printeightbit noprinteightbit
+ \ print_exit_value no_print_exit_value printexitvalue noprintexitvalue rc_quotes no_rc_quotes
+ \ rcquotes norcquotes rm_star_silent no_rm_star_silent rmstarsilent normstarsilent rm_star_wait
+ \ no_rm_star_wait rmstarwait normstarwait short_loops no_short_loops shortloops noshortloops
+ \ short_repeat no_short_repeat shortrepeat noshortrepeat sun_keyboard_hack no_sun_keyboard_hack
+ \ sunkeyboardhack nosunkeyboardhack auto_continue no_auto_continue autocontinue noautocontinue
+ \ auto_resume no_auto_resume autoresume noautoresume bg_nice no_bg_nice bgnice nobgnice check_jobs
+ \ no_check_jobs checkjobs nocheckjobs check_running_jobs no_check_running_jobs checkrunningjobs
+ \ nocheckrunningjobs hup no_hup nohup long_list_jobs no_long_list_jobs longlistjobs nolonglistjobs
+ \ monitor no_monitor nomonitor notify no_notify nonotify posix_jobs posixjobs no_posix_jobs
+ \ noposixjobs prompt_bang no_prompt_bang promptbang nopromptbang prompt_cr no_prompt_cr promptcr
+ \ nopromptcr prompt_sp no_prompt_sp promptsp nopromptsp prompt_percent no_prompt_percent
+ \ promptpercent nopromptpercent prompt_subst no_prompt_subst promptsubst nopromptsubst
+ \ transient_rprompt no_transient_rprompt transientrprompt notransientrprompt alias_func_def
+ \ no_alias_func_def aliasfuncdef noaliasfuncdef c_bases no_c_bases cbases nocbases c_precedences
+ \ no_c_precedences cprecedences nocprecedences debug_before_cmd no_debug_before_cmd debugbeforecmd
+ \ nodebugbeforecmd err_exit no_err_exit errexit noerrexit err_return no_err_return errreturn
+ \ noerrreturn eval_lineno no_eval_lineno evallineno noevallineno exec no_exec noexec function_argzero
+ \ no_function_argzero functionargzero nofunctionargzero local_loops no_local_loops localloops
+ \ nolocalloops local_options no_local_options localoptions nolocaloptions local_patterns
+ \ no_local_patterns localpatterns nolocalpatterns local_traps no_local_traps localtraps nolocaltraps
+ \ multi_func_def no_multi_func_def multifuncdef nomultifuncdef multios no_multios nomultios
+ \ octal_zeroes no_octal_zeroes octalzeroes nooctalzeroes pipe_fail no_pipe_fail pipefail nopipefail
+ \ source_trace no_source_trace sourcetrace nosourcetrace typeset_silent no_typeset_silent
+ \ typesetsilent notypesetsilent typeset_to_unset no_typeset_to_unset typesettounset notypesettounset
+ \ verbose no_verbose noverbose xtrace no_xtrace noxtrace append_create no_append_create appendcreate
+ \ noappendcreate bash_rematch no_bash_rematch bashrematch nobashrematch bsd_echo no_bsd_echo bsdecho
+ \ nobsdecho continue_on_error no_continue_on_error continueonerror nocontinueonerror
+ \ csh_junkie_history no_csh_junkie_history cshjunkiehistory nocshjunkiehistory csh_junkie_loops
+ \ no_csh_junkie_loops cshjunkieloops nocshjunkieloops csh_junkie_quotes no_csh_junkie_quotes
+ \ cshjunkiequotes nocshjunkiequotes csh_nullcmd no_csh_nullcmd cshnullcmd nocshnullcmd ksh_arrays
+ \ no_ksh_arrays ksharrays noksharrays ksh_autoload no_ksh_autoload kshautoload nokshautoload
+ \ ksh_option_print no_ksh_option_print kshoptionprint nokshoptionprint ksh_typeset no_ksh_typeset
+ \ kshtypeset nokshtypeset ksh_zero_subscript no_ksh_zero_subscript kshzerosubscript
+ \ nokshzerosubscript posix_aliases no_posix_aliases posixaliases noposixaliases posix_argzero
+ \ no_posix_argzero posixargzero noposixargzero posix_builtins no_posix_builtins posixbuiltins
+ \ noposixbuiltins posix_identifiers no_posix_identifiers posixidentifiers noposixidentifiers
+ \ posix_strings no_posix_strings posixstrings noposixstrings posix_traps no_posix_traps posixtraps
+ \ noposixtraps sh_file_expansion no_sh_file_expansion shfileexpansion noshfileexpansion sh_nullcmd
+ \ no_sh_nullcmd shnullcmd noshnullcmd sh_option_letters no_sh_option_letters shoptionletters
+ \ noshoptionletters sh_word_split no_sh_word_split shwordsplit noshwordsplit traps_async
+ \ no_traps_async trapsasync notrapsasync interactive no_interactive nointeractive login no_login
+ \ nologin privileged no_privileged noprivileged restricted no_restricted norestricted shin_stdin
+ \ no_shin_stdin shinstdin noshinstdin single_command no_single_command singlecommand nosinglecommand
+ \ beep no_beep nobeep combining_chars no_combining_chars combiningchars nocombiningchars emacs
+ \ no_emacs noemacs overstrike no_overstrike nooverstrike single_line_zle no_single_line_zle
+ \ singlelinezle nosinglelinezle vi no_vi novi zle no_zle nozle brace_expand no_brace_expand
+ \ braceexpand nobraceexpand dot_glob no_dot_glob dotglob nodotglob hash_all no_hash_all hashall
+ \ nohashall hist_append no_hist_append histappend nohistappend hist_expand no_hist_expand histexpand
+ \ nohistexpand log no_log nolog mail_warn no_mail_warn mailwarn nomailwarn one_cmd no_one_cmd onecmd
+ \ noonecmd physical no_physical nophysical prompt_vars no_prompt_vars promptvars nopromptvars stdin
+ \ no_stdin nostdin track_all no_track_all trackall notrackall
syn case match
syn keyword zshTypes float integer local typeset declare private readonly
@@ -150,15 +275,12 @@ syn keyword zshTypes float integer local typeset declare private read
" XXX: this may be too much
" syn match zshSwitches '\s\zs--\=[a-zA-Z0-9-]\+'
-syn match zshNumber '[+-]\=\<\d\+\>'
-syn match zshNumber '[+-]\=\<0x\x\+\>'
-syn match zshNumber '[+-]\=\<0\o\+\>'
-syn match zshNumber '[+-]\=\d\+#[-+]\=\w\+\>'
-syn match zshNumber '[+-]\=\d\+\.\d\+\>'
-
" TODO: $[...] is the same as $((...)), so add that as well.
syn cluster zshSubst contains=zshSubst,zshOldSubst,zshMathSubst
+syn cluster zshSubstQuoted contains=zshSubstQuoted,zshOldSubst,zshMathSubst
exe 'syn region zshSubst matchgroup=zshSubstDelim transparent start=/\$(/ skip=/\\)/ end=/)/ contains='.s:contained. ' fold'
+exe 'syn region zshSubstQuoted matchgroup=zshSubstDelim transparent start=/\$(/ skip=/\\)/ end=/)/ contains='.s:contained. ' fold'
+syn region zshSubstQuoted matchgroup=zshSubstDelim start='\${' skip='\\}' end='}' contains=@zshSubst,zshBrackets,zshQuoted fold
syn region zshParentheses transparent start='(' skip='\\)' end=')' fold
syn region zshGlob start='(#' end=')'
syn region zshMathSubst matchgroup=zshSubstDelim transparent
@@ -201,6 +323,8 @@ hi def link zshJobSpec Special
hi def link zshPrecommand Special
hi def link zshDelimiter Keyword
hi def link zshConditional Conditional
+hi def link zshCase zshConditional
+hi def link zshCaseIn zshCase
hi def link zshException Exception
hi def link zshRepeat Repeat
hi def link zshKeyword Keyword
@@ -223,6 +347,7 @@ hi def link zshTypes Type
hi def link zshSwitches Special
hi def link zshNumber Number
hi def link zshSubst PreProc
+hi def link zshSubstQuoted zshSubst
hi def link zshMathSubst zshSubst
hi def link zshOldSubst zshSubst
hi def link zshSubstDelim zshSubst
diff --git a/runtime/tutor/tutor.tutor.json b/runtime/tutor/tutor.tutor.json
index bf3eae8586..e8628e2f0e 100644
--- a/runtime/tutor/tutor.tutor.json
+++ b/runtime/tutor/tutor.tutor.json
@@ -2,8 +2,8 @@
"expect": {
"63": "This is text with **important information**",
"64": "This is text with **important information**",
- "71": "Document '&variable'",
- "72": "Document '&variable'",
+ "71": "TODO: Document '&variable'",
+ "72": "TODO: Document '&variable'",
"78": "# This is a level 1 header",
"79": "# This is a level 1 header",
"80": "### This is a level 3 header",
diff --git a/scripts/bump-deps.sh b/scripts/bump-deps.sh
deleted file mode 100755
index 85c7f72700..0000000000
--- a/scripts/bump-deps.sh
+++ /dev/null
@@ -1,104 +0,0 @@
-#!/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
index 17e3fd35d6..1873c3cd0d 100644..100755
--- a/scripts/bump_deps.lua
+++ b/scripts/bump_deps.lua
@@ -1,35 +1,24 @@
+#!/usr/bin/env -S nvim -l
+
-- 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()"
+-- ./scripts/bump_deps.lua -h
local M = {}
local _trace = false
-local required_branch_prefix = "bump-"
-local commit_prefix = "build(deps): "
+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")
+ 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")
+ p('')
+ vim.cmd('cquit 1')
end
-- Executes and returns the output of `cmd`, or nil on failure.
@@ -37,307 +26,421 @@ end
--
-- 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
+ 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, "")
+ 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)
+ 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")
+ 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 })
+ 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 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
+ 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")
+ 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
+ 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 }
+ 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 shacmd = (vim.fn.executable('sha256sum') == 1
+ and{ 'sha256sum', archive_path }
+ or { 'shasum', '-a', '256', archive_path })
+ local archive_sha = run(shacmd):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)
+ 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")
+ 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
+ require_executable('git')
+
+ local checked_out_branch = assert(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"
- )
+ 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")
+ 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")
+ 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")
+ 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)
+ 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
+ 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
+ require_executable('git')
+
+ local remotes = assert(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")
+ 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))
+ local dependency = assert(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)
+ vim.validate{
+ dependency_name={dependency_name,'s'},
+ version={version,'s'},
+ }
+ local dependency = assert(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))
+ local dependency = assert(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)
+ 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)
+ require_executable('git')
+
+ verify_branch('deps')
+
+ local nvim_remote = find_git_remote(nil)
+ local relevant_commit = assert(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
+
+local function usage()
+ local this_script = _G.arg[0]:match("[^/]*.lua$")
+ print(([=[
+ Bump Nvim dependencies
+
+ Usage: nvim -l %s [options]
+ Bump to HEAD, tagged version, commit, or branch:
+ nvim -l %s --dep Luv --head
+ nvim -l %s --dep Luv --version 1.43.0-0
+ nvim -l %s --dep Luv --commit abc123
+ nvim -l %s --dep Luv --branch
+ Create a PR:
+ nvim -l %s --pr
+
+ Options:
+ -h show this message and exit.
+ --pr submit pr for bumping deps.
+ --branch <dep> create a branch bump-<dep> from current branch.
+ --dep <dependency> bump to a specific release or tag.
+
+ Dependency Options:
+ --version <tag> bump to a specific release or tag.
+ --commit <hash> bump to a specific commit.
+ --HEAD bump to a current head.
+
+ <dependency> is one of:
+ "LuaJIT", "libuv", "Luv", "tree-sitter"
+ ]=]):format(this_script, this_script, this_script, this_script, this_script, this_script))
+end
+
+local function parseargs()
+ local args = {}
+ for i = 1, #_G.arg do
+ if _G.arg[i] == '-h' then
+ args.h = true
+ elseif _G.arg[i] == '--pr' then
+ args.pr = true
+ elseif _G.arg[i] == '--branch' then
+ args.branch = _G.arg[i+1]
+ elseif _G.arg[i] == '--dep' then
+ args.dep = _G.arg[i+1]
+ elseif _G.arg[i] == '--version' then
+ args.version = _G.arg[i+1]
+ elseif _G.arg[i] == '--commit' then
+ args.commit = _G.arg[i+1]
+ elseif _G.arg[i] == '--head' then
+ args.head = true
+ end
+ end
+ return args
end
-return M
+local is_main = _G.arg[0]:match('bump_deps.lua')
+
+if is_main then
+ local args = parseargs()
+ if args.h then
+ usage()
+ elseif args.pr then
+ M.submit_pr()
+ elseif args.head then
+ M.head(args.dep)
+ elseif args.branch then
+ M.create_branch(args.dep)
+ elseif args.version then
+ M.version(args.dep, args.version)
+ elseif args.commit then
+ M.commit(args.dep, args.commit)
+ elseif args.pr then
+ M.submit_pr()
+ else
+ print('missing required arg\n')
+ os.exit(1)
+ end
+else
+ return M
+end
diff --git a/scripts/check-includes.py b/scripts/check-includes.py
deleted file mode 100755
index ed1fe407c5..0000000000
--- a/scripts/check-includes.py
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/usr/bin/env python
-
-import sys
-import re
-import os
-
-from subprocess import Popen, PIPE
-from argparse import ArgumentParser
-
-
-GENERATED_INCLUDE_RE = re.compile(
- r'^\s*#\s*include\s*"([/a-z_0-9.]+\.generated\.h)"(\s+//.*)?$')
-
-
-def main(argv):
- argparser = ArgumentParser()
- argparser.add_argument('--generated-includes-dir', action='append',
- help='Directory where generated includes are located.')
- argparser.add_argument('--file', type=open, help='File to check.')
- argparser.add_argument('iwyu_args', nargs='*',
- help='IWYU arguments, must go after --.')
- args = argparser.parse_args(argv)
-
- with args.file:
- iwyu = Popen(['include-what-you-use', '-xc'] + args.iwyu_args + ['/dev/stdin'],
- stdin=PIPE, stdout=PIPE, stderr=PIPE)
-
- for line in args.file:
- match = GENERATED_INCLUDE_RE.match(line)
- if match:
- for d in args.generated_includes_dir:
- try:
- f = open(os.path.join(d, match.group(1)))
- except IOError:
- continue
- else:
- with f:
- for generated_line in f:
- iwyu.stdin.write(generated_line)
- break
- else:
- raise IOError('Failed to find {0}'.format(match.group(1)))
- else:
- iwyu.stdin.write(line)
-
- iwyu.stdin.close()
-
- out = iwyu.stdout.read()
- err = iwyu.stderr.read()
-
- ret = iwyu.wait()
-
- if ret != 2:
- print('IWYU failed with exit code {0}:'.format(ret))
- print('{0} stdout {0}'.format('=' * ((80 - len(' stdout ')) // 2)))
- print(out)
- print('{0} stderr {0}'.format('=' * ((80 - len(' stderr ')) // 2)))
- print(err)
- return 1
- return 0
-
-
-if __name__ == '__main__':
- raise SystemExit(main(sys.argv[1:]))
diff --git a/scripts/cliff.toml b/scripts/cliff.toml
new file mode 100644
index 0000000000..3fc10e5d16
--- /dev/null
+++ b/scripts/cliff.toml
@@ -0,0 +1,73 @@
+# configuration file for git-cliff (0.1.0)
+
+[changelog]
+# changelog header
+header = """
+# Changelog\n
+All notable changes to this project will be documented in this file.\n
+"""
+# template for the changelog body
+# https://tera.netlify.app/docs/#introduction
+body = """
+{% if version %}\
+ ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
+{% else %}\
+ ## [unreleased]
+{% endif %}\
+{% for group, commits in commits | group_by(attribute="group") %}
+ ### {{ group | upper_first }}
+ {% for commit in commits%}\
+ {% if not commit.scope %}\
+ - {{ commit.message | upper_first }}
+ {% endif %}\
+ {% endfor %}\
+ {% for group, commits in commits | group_by(attribute="scope") %}\
+ {% for commit in commits %}\
+ - **{{commit.scope}}**: {{ commit.message | upper_first }}
+ {% endfor %}\
+ {% endfor %}
+{% endfor %}\n
+"""
+# remove the leading and trailing whitespace from the template
+trim = true
+# changelog footer
+footer = """
+<!-- generated by git-cliff -->
+"""
+
+[git]
+# parse the commits based on https://www.conventionalcommits.org
+conventional_commits = true
+# filter out the commits that are not conventional
+filter_unconventional = true
+# process each line of a commit as an individual commit
+split_commits = false
+# regex for preprocessing the commit messages
+commit_preprocessors = [
+# { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](https://github.com/neovim/neovim/issues/${2}))"},
+]
+# regex for parsing and grouping commits
+commit_parsers = [
+ { message = "!:", group = "Breaking"},
+ { message = "^feat", group = "Features"},
+ { message = "^fix", group = "Bug Fixes"},
+ { message = "^doc", group = "Documentation"},
+ { message = "^perf", group = "Performance"},
+ { message = "^refactor", group = "Refactor"},
+ { message = "^test", group = "Testing"},
+ { message = "^chore", group = "Miscellaneous Tasks"},
+ { message = "^build", group = "Build System"},
+ { message = "^Revert", group = "Reverted Changes"},
+]
+# filter out the commits that are not matched by commit parsers
+filter_commits = true
+# glob pattern for matching git tags
+tag_pattern = "v[0-9]*"
+# regex for skipping tags
+skip_tags = "v0.1.0-beta.1"
+# regex for ignoring tags
+ignore_tags = ""
+# sort the tags chronologically
+date_order = false
+# sort the commits inside sections by oldest/newest order
+sort_commits = "oldest"
diff --git a/scripts/download-unicode-files.sh b/scripts/download-unicode-files.sh
index 4482cefa34..f0fd4c66ea 100755
--- a/scripts/download-unicode-files.sh
+++ b/scripts/download-unicode-files.sh
@@ -3,11 +3,12 @@
set -e
data_files="UnicodeData.txt CaseFolding.txt EastAsianWidth.txt"
emoji_files="emoji-data.txt"
+files="'$data_files $emoji_files'"
UNIDIR_DEFAULT=src/unicode
DOWNLOAD_URL_BASE_DEFAULT='http://unicode.org/Public'
-if test x$1 = 'x--help' ; then
+if test "$1" = '--help' ; then
echo 'Usage:'
echo " $0[ TARGET_DIRECTORY[ URL_BASE]]"
echo
@@ -16,6 +17,7 @@ if test x$1 = 'x--help' ; then
echo
echo "Default target directory is $PWD/${UNIDIR_DEFAULT}."
echo "Default URL base is ${DOWNLOAD_URL_BASE_DEFAULT}."
+ exit 0
fi
UNIDIR=${1:-$UNIDIR_DEFAULT}
@@ -23,21 +25,12 @@ DOWNLOAD_URL_BASE=${2:-$DOWNLOAD_URL_BASE_DEFAULT}
for filename in $data_files ; do
curl -L -o "$UNIDIR/$filename" "$DOWNLOAD_URL_BASE/UNIDATA/$filename"
- (
- cd "$UNIDIR"
- git add $filename
- )
+ git -C "$UNIDIR" add "$filename"
done
for filename in $emoji_files ; do
curl -L -o "$UNIDIR/$filename" "$DOWNLOAD_URL_BASE/UNIDATA/emoji/$filename"
- (
- cd "$UNIDIR"
- git add $filename
- )
+ git -C "$UNIDIR" add "$filename"
done
-(
- cd "$UNIDIR"
- git commit -m "feat: update unicode tables" -- $files
-)
+git -C "$UNIDIR" commit -m "feat: update unicode tables" .
diff --git a/scripts/gen_help_html.lua b/scripts/gen_help_html.lua
new file mode 100644
index 0000000000..2563f2f410
--- /dev/null
+++ b/scripts/gen_help_html.lua
@@ -0,0 +1,1110 @@
+-- Converts Vim :help files to HTML. Validates |tag| links and document syntax (parser errors).
+--
+-- NOTE: :helptags checks for duplicate tags, whereas this script checks _links_ (to tags).
+--
+-- USAGE (GENERATE HTML):
+-- 1. Run `make helptags` first; this script depends on vim.fn.taglist().
+-- 2. nvim -V1 -es --clean +"lua require('scripts.gen_help_html').gen('./build/runtime/doc/', 'target/dir/')"
+-- - Read the docstring at gen().
+-- 3. cd target/dir/ && jekyll serve --host 0.0.0.0
+-- 4. Visit http://localhost:4000/…/help.txt.html
+--
+-- USAGE (VALIDATE):
+-- 1. nvim -V1 -es +"lua require('scripts.gen_help_html').validate()"
+-- - validate() is 10x faster than gen(), so it is used in CI.
+--
+-- SELF-TEST MODE:
+-- 1. nvim -V1 -es +"lua require('scripts.gen_help_html')._test()"
+--
+-- NOTES:
+-- * gen() and validate() are the primary entrypoints. validate() only exists because gen() is too
+-- slow (~1 min) to run in per-commit CI.
+-- * visit_node() is the core function used by gen() to traverse the document tree and produce HTML.
+-- * visit_validate() is the core function used by validate().
+-- * Files in `new_layout` will be generated with a "flow" layout instead of preformatted/fixed-width layout.
+
+local tagmap = nil
+local helpfiles = nil
+local invalid_links = {}
+local invalid_urls = {}
+local invalid_spelling = {}
+local spell_dict = {
+ Neovim = 'Nvim',
+ NeoVim = 'Nvim',
+ neovim = 'Nvim',
+ lua = 'Lua',
+ VimL = 'Vimscript',
+}
+local language = nil
+
+local M = {}
+
+-- These files are generated with "flow" layout (non fixed-width, wrapped text paragraphs).
+-- All other files are "legacy" files which require fixed-width layout.
+local new_layout = {
+ ['api.txt'] = true,
+ ['channel.txt'] = true,
+ ['deprecated.txt'] = true,
+ ['develop.txt'] = true,
+ ['lua.txt'] = true,
+ ['luaref.txt'] = true,
+ ['news.txt'] = true,
+ ['nvim.txt'] = true,
+ ['pi_health.txt'] = true,
+ ['provider.txt'] = true,
+ ['ui.txt'] = true,
+}
+
+-- TODO: These known invalid |links| require an update to the relevant docs.
+local exclude_invalid = {
+ ["'previewpopup'"] = "quickref.txt",
+ ["'pvp'"] = "quickref.txt",
+ ["'string'"] = "eval.txt",
+ Query = 'treesitter.txt',
+ ['eq?'] = 'treesitter.txt',
+ ['lsp-request'] = 'lsp.txt',
+ matchit = 'vim_diff.txt',
+ ['matchit.txt'] = 'help.txt',
+ ["set!"] = "treesitter.txt",
+ ['v:_null_blob'] = 'builtin.txt',
+ ['v:_null_dict'] = 'builtin.txt',
+ ['v:_null_list'] = 'builtin.txt',
+ ['v:_null_string'] = 'builtin.txt',
+ ['vim.lsp.buf_request()'] = 'lsp.txt',
+ ['vim.lsp.util.get_progress_messages()'] = 'lsp.txt',
+}
+
+-- False-positive "invalid URLs".
+local exclude_invalid_urls = {
+ ["http://"] = "usr_23.txt",
+ ["http://."] = "usr_23.txt",
+ ["http://aspell.net/man-html/Affix-Compression.html"] = "spell.txt",
+ ["http://aspell.net/man-html/Phonetic-Code.html"] = "spell.txt",
+ ["http://canna.sourceforge.jp/"] = "mbyte.txt",
+ ["http://gnuada.sourceforge.net"] = "ft_ada.txt",
+ ["http://lua-users.org/wiki/StringLibraryTutorial"] = "lua.txt",
+ ["http://michael.toren.net/code/"] = "pi_tar.txt",
+ ["http://papp.plan9.de"] = "syntax.txt",
+ ["http://wiki.services.openoffice.org/wiki/Dictionaries"] = "spell.txt",
+ ["http://www.adapower.com"] = "ft_ada.txt",
+ ["http://www.jclark.com/"] = "quickfix.txt",
+}
+
+local function tofile(fname, text)
+ local f = io.open(fname, 'w')
+ if not f then
+ error(('failed to write: %s'):format(f))
+ else
+ f:write(text)
+ f:close()
+ end
+end
+
+local function html_esc(s)
+ return s:gsub(
+ '&', '&amp;'):gsub(
+ '<', '&lt;'):gsub(
+ '>', '&gt;')
+end
+
+local function url_encode(s)
+ -- Credit: tpope / vim-unimpaired
+ -- NOTE: these chars intentionally *not* escaped: ' ( )
+ return vim.fn.substitute(vim.fn.iconv(s, 'latin1', 'utf-8'),
+ [=[[^A-Za-z0-9()'_.~-]]=],
+ [=[\="%".printf("%02X",char2nr(submatch(0)))]=],
+ 'g')
+end
+
+local function expandtabs(s)
+ return s:gsub('\t', (' '):rep(8))
+end
+
+local function to_titlecase(s)
+ local text = ''
+ for w in vim.gsplit(s, '[ \t]+') do
+ text = ('%s %s%s'):format(text, vim.fn.toupper(w:sub(1, 1)), w:sub(2))
+ end
+ return text
+end
+
+local function to_heading_tag(text)
+ -- Prepend "_" to avoid conflicts with actual :help tags.
+ return text and string.format('_%s', vim.fn.tolower((text:gsub('%s+', '-')))) or 'unknown'
+end
+
+local function basename_noext(f)
+ return vim.fs.basename(f:gsub('%.txt', ''))
+end
+
+local function is_blank(s)
+ return not not s:find([[^[\t ]*$]])
+end
+
+local function trim(s, dir)
+ return vim.fn.trim(s, '\r\t\n ', dir or 0)
+end
+
+-- Remove common punctuation from URLs.
+--
+-- TODO: fix this in the parser instead... https://github.com/neovim/tree-sitter-vimdoc
+--
+-- @returns (fixed_url, removed_chars) where `removed_chars` is in the order found in the input.
+local function fix_url(url)
+ local removed_chars = ''
+ local fixed_url = url
+ -- Remove up to one of each char from end of the URL, in this order.
+ for _, c in ipairs({ '.', ')', }) do
+ if fixed_url:sub(-1) == c then
+ removed_chars = c .. removed_chars
+ fixed_url = fixed_url:sub(1, -2)
+ end
+ end
+ return fixed_url, removed_chars
+end
+
+-- Checks if a given line is a "noise" line that doesn't look good in HTML form.
+local function is_noise(line, noise_lines)
+ if (
+ -- First line is always noise.
+ (noise_lines ~= nil and vim.tbl_count(noise_lines) == 0)
+ or line:find('Type .*gO.* to see the table of contents')
+ -- Title line of traditional :help pages.
+ -- Example: "NVIM REFERENCE MANUAL by ..."
+ or line:find([[^%s*N?VIM[ \t]*REFERENCE[ \t]*MANUAL]])
+ -- First line of traditional :help pages.
+ -- Example: "*api.txt* Nvim"
+ or line:find('%s*%*?[a-zA-Z]+%.txt%*?%s+N?[vV]im%s*$')
+ -- modeline
+ -- Example: "vim:tw=78:ts=8:sw=4:sts=4:et:ft=help:norl:"
+ or line:find('^%s*vim?%:.*ft=help')
+ or line:find('^%s*vim?%:.*filetype=help')
+ or line:find('[*>]local%-additions[*<]')
+ ) then
+ -- table.insert(stats.noise_lines, getbuflinestr(root, opt.buf, 0))
+ table.insert(noise_lines or {}, line)
+ return true
+ end
+ return false
+end
+
+-- Creates a github issue URL at neovim/tree-sitter-vimdoc with prefilled content.
+local function get_bug_url_vimdoc(fname, to_fname, sample_text)
+ local this_url = string.format('https://neovim.io/doc/user/%s', vim.fs.basename(to_fname))
+ local bug_url = ('https://github.com/neovim/tree-sitter-vimdoc/issues/new?labels=bug&title=parse+error%3A+'
+ ..vim.fs.basename(fname)
+ ..'+&body=Found+%60tree-sitter-vimdoc%60+parse+error+at%3A+'
+ ..this_url
+ ..'%0D%0DContext%3A%0D%0D%60%60%60%0D'
+ ..url_encode(sample_text)
+ ..'%0D%60%60%60')
+ return bug_url
+end
+
+-- Creates a github issue URL at neovim/neovim with prefilled content.
+local function get_bug_url_nvim(fname, to_fname, sample_text, token_name)
+ local this_url = string.format('https://neovim.io/doc/user/%s', vim.fs.basename(to_fname))
+ local bug_url = ('https://github.com/neovim/neovim/issues/new?labels=bug&title=user+docs+HTML%3A+'
+ ..vim.fs.basename(fname)
+ ..'+&body=%60gen_help_html.lua%60+problem+at%3A+'
+ ..this_url
+ ..'%0D'
+ ..(token_name and '+unhandled+token%3A+%60'..token_name..'%60' or '')
+ ..'%0DContext%3A%0D%0D%60%60%60%0D'
+ ..url_encode(sample_text)
+ ..'%0D%60%60%60')
+ return bug_url
+end
+
+-- Gets a "foo.html" name from a "foo.txt" helpfile name.
+local function get_helppage(f)
+ if not f then
+ return nil
+ end
+ -- Special case: help.txt is the "main landing page" of :help files, not index.txt.
+ if f == 'index.txt' then
+ return 'vimindex.html'
+ elseif f == 'help.txt' then
+ return 'index.html'
+ end
+
+ return (f:gsub('%.txt$', '.html'))
+end
+
+-- Counts leading spaces (tab=8) to decide the indent size of multiline text.
+--
+-- Blank lines (empty or whitespace-only) are ignored.
+local function get_indent(s)
+ local min_indent = nil
+ for line in vim.gsplit(s, '\n') do
+ if line and not is_blank(line) then
+ local ws = expandtabs(line:match('^%s+') or '')
+ min_indent = (not min_indent or ws:len() < min_indent) and ws:len() or min_indent
+ end
+ end
+ return min_indent or 0
+end
+
+-- Removes the common indent level, after expanding tabs to 8 spaces.
+local function trim_indent(s)
+ local indent_size = get_indent(s)
+ local trimmed = ''
+ for line in vim.gsplit(s, '\n') do
+ line = expandtabs(line)
+ trimmed = ('%s%s\n'):format(trimmed, line:sub(indent_size + 1))
+ end
+ return trimmed:sub(1, -2)
+end
+
+-- Gets raw buffer text in the node's range (+/- an offset), as a newline-delimited string.
+local function getbuflinestr(node, bufnr, offset)
+ local line1, _, line2, _ = node:range()
+ line1 = line1 - offset
+ line2 = line2 + offset
+ local lines = vim.fn.getbufline(bufnr, line1 + 1, line2 + 1)
+ return table.concat(lines, '\n')
+end
+
+-- Gets the whitespace just before `node` from the raw buffer text.
+-- Needed for preformatted `old` lines.
+local function getws(node, bufnr)
+ local line1, c1, line2, _ = node:range()
+ local raw = vim.fn.getbufline(bufnr, line1 + 1, line2 + 1)[1]
+ local text_before = raw:sub(1, c1)
+ local leading_ws = text_before:match('%s+$') or ''
+ return leading_ws
+end
+
+local function get_tagname(node, bufnr)
+ local text = vim.treesitter.get_node_text(node, bufnr)
+ local tag = (node:type() == 'optionlink' or node:parent():type() == 'optionlink') and ("'%s'"):format(text) or text
+ local helpfile = vim.fs.basename(tagmap[tag]) or nil -- "api.txt"
+ local helppage = get_helppage(helpfile) -- "api.html"
+ return helppage, tag
+end
+
+-- Returns true if the given invalid tagname is a false positive.
+local function ignore_invalid(s)
+ return not not (
+ exclude_invalid[s]
+ -- Strings like |~/====| appear in various places and the parser thinks they are links, but they
+ -- are just table borders.
+ or s:find('===')
+ or s:find('---')
+ )
+end
+
+local function ignore_parse_error(s)
+ return (
+ -- Ignore parse errors for unclosed tag.
+ -- This is common in vimdocs and is treated as plaintext by :help.
+ s:find("^[`'|*]")
+ )
+end
+
+local function has_ancestor(node, ancestor_name)
+ local p = node
+ while true do
+ p = p:parent()
+ if not p or p:type() == 'help_file' then
+ break
+ elseif p:type() == ancestor_name then
+ return true
+ end
+ end
+ return false
+end
+
+-- Gets the first matching child node matching `name`.
+local function first(node, name)
+ for c, _ in node:iter_children() do
+ if c:named() and c:type() == name then
+ return c
+ end
+ end
+ return nil
+end
+
+local function validate_link(node, bufnr, fname)
+ local helppage, tagname = get_tagname(node:child(1), bufnr)
+ local ignored = false
+ if not tagmap[tagname] then
+ ignored = has_ancestor(node, 'column_heading') or node:has_error() or ignore_invalid(tagname)
+ if not ignored then
+ invalid_links[tagname] = vim.fs.basename(fname)
+ end
+ end
+ return helppage, tagname, ignored
+end
+
+-- TODO: port the logic from scripts/check_urls.vim
+local function validate_url(text, fname)
+ local ignored = false
+ if vim.fs.basename(fname) == 'pi_netrw.txt' then
+ ignored = true
+ elseif text:find('http%:') and not exclude_invalid_urls[text] then
+ invalid_urls[text] = vim.fs.basename(fname)
+ end
+ return ignored
+end
+
+-- Traverses the tree at `root` and checks that |tag| links point to valid helptags.
+local function visit_validate(root, level, lang_tree, opt, stats)
+ level = level or 0
+ local node_name = (root.named and root:named()) and root:type() or nil
+ local toplevel = level < 1
+ local function node_text(node)
+ return vim.treesitter.get_node_text(node or root, opt.buf)
+ end
+ local text = trim(node_text())
+
+ if root:child_count() > 0 then
+ for node, _ in root:iter_children() do
+ if node:named() then
+ visit_validate(node, level + 1, lang_tree, opt, stats)
+ end
+ end
+ end
+
+ if node_name == 'ERROR' then
+ if ignore_parse_error(text) then
+ return
+ end
+ -- Store the raw text to give context to the error report.
+ local sample_text = not toplevel and getbuflinestr(root, opt.buf, 3) or '[top level!]'
+ table.insert(stats.parse_errors, sample_text)
+ elseif node_name == 'word' or node_name == 'uppercase_name' then
+ if spell_dict[text] then
+ if not invalid_spelling[text] then
+ invalid_spelling[text] = { vim.fs.basename(opt.fname) }
+ else
+ table.insert(invalid_spelling[text], vim.fs.basename(opt.fname))
+ end
+ end
+ elseif node_name == 'url' then
+ local fixed_url, _ = fix_url(trim(text))
+ validate_url(fixed_url, opt.fname)
+ elseif node_name == 'taglink' or node_name == 'optionlink' then
+ local _, _, _ = validate_link(root, opt.buf, opt.fname)
+ end
+end
+
+-- Fix tab alignment issues caused by concealed characters like |, `, * in tags
+-- and code blocks.
+local function fix_tab_after_conceal(text, next_node_text)
+ -- Vim tabs take into account the two concealed characters even though they
+ -- are invisible, so we need to add back in the two spaces if this is
+ -- followed by a tab to make the tab alignment to match Vim's behavior.
+ if string.sub(next_node_text,1,1) == '\t' then
+ text = text .. ' '
+ end
+ return text
+end
+
+-- Generates HTML from node `root` recursively.
+local function visit_node(root, level, lang_tree, headings, opt, stats)
+ level = level or 0
+
+ local node_name = (root.named and root:named()) and root:type() or nil
+ -- Previous sibling kind (string).
+ local prev = root:prev_sibling() and (root:prev_sibling().named and root:prev_sibling():named()) and root:prev_sibling():type() or nil
+ -- Next sibling kind (string).
+ local next_ = root:next_sibling() and (root:next_sibling().named and root:next_sibling():named()) and root:next_sibling():type() or nil
+ -- Parent kind (string).
+ local parent = root:parent() and root:parent():type() or nil
+ local text = ''
+ local trimmed
+ -- Gets leading whitespace of `node`.
+ local function ws(node)
+ node = node or root
+ local ws_ = getws(node, opt.buf)
+ -- XXX: first node of a (line) includes whitespace, even after
+ -- https://github.com/neovim/tree-sitter-vimdoc/pull/31 ?
+ if ws_ == '' then
+ ws_ = vim.treesitter.get_node_text(node, opt.buf):match('^%s+') or ''
+ end
+ return ws_
+ end
+ local function node_text(node, ws_)
+ node = node or root
+ ws_ = (ws_ == nil or ws_ == true) and getws(node, opt.buf) or ''
+ return string.format('%s%s', ws_, vim.treesitter.get_node_text(node, opt.buf))
+ end
+
+ if root:named_child_count() == 0 or node_name == 'ERROR' then
+ text = node_text()
+ trimmed = html_esc(trim(text))
+ text = html_esc(text)
+ else
+ -- Process children and join them with whitespace.
+ for node, _ in root:iter_children() do
+ if node:named() then
+ local r = visit_node(node, level + 1, lang_tree, headings, opt, stats)
+ text = string.format('%s%s', text, r)
+ end
+ end
+ trimmed = trim(text)
+ end
+
+ if node_name == 'help_file' then -- root node
+ return text
+ elseif node_name == 'url' then
+ local fixed_url, removed_chars = fix_url(trimmed)
+ return ('%s<a href="%s">%s</a>%s'):format(ws(), fixed_url, fixed_url, removed_chars)
+ elseif node_name == 'word' or node_name == 'uppercase_name' then
+ return text
+ elseif node_name == 'h1' or node_name == 'h2' or node_name == 'h3' then
+ if is_noise(text, stats.noise_lines) then
+ return '' -- Discard common "noise" lines.
+ end
+ -- Remove "===" and tags from ToC text.
+ local hname = (node_text():gsub('%-%-%-%-+', ''):gsub('%=%=%=%=+', ''):gsub('%*.*%*', ''))
+ -- Use the first *tag* node as the heading anchor, if any.
+ local tagnode = first(root, 'tag')
+ local tagname = tagnode and url_encode(node_text(tagnode:child(1), false)) or to_heading_tag(hname)
+ if node_name == 'h1' or #headings == 0 then
+ table.insert(headings, { name = hname, subheadings = {}, tag = tagname })
+ else
+ table.insert(headings[#headings].subheadings, { name = hname, subheadings = {}, tag = tagname })
+ end
+ local el = node_name == 'h1' and 'h2' or 'h3'
+ -- If we are re-using the *tag*, this heading anchor is redundant.
+ local a = tagnode and '' or ('<a name="%s"></a>'):format(tagname)
+ return ('%s<%s class="help-heading">%s</%s>\n'):format(a, el, text, el)
+ elseif node_name == 'column_heading' or node_name == 'column_name' then
+ if root:has_error() then
+ return text
+ end
+ return ('<div class="help-column_heading">%s</div>'):format(text)
+ elseif node_name == 'block' then
+ if is_blank(text) then
+ return ''
+ end
+ if opt.old then
+ -- XXX: Treat "old" docs as preformatted: they use indentation for layout.
+ -- Trim trailing newlines to avoid too much whitespace between divs.
+ return ('<div class="old-help-para">%s</div>\n'):format(trim(text, 2))
+ end
+ return string.format('<div class="help-para">\n%s\n</div>\n', text)
+ elseif node_name == 'line' then
+ if (parent ~= 'codeblock' or parent ~= 'code') and (is_blank(text) or is_noise(text, stats.noise_lines)) then
+ return '' -- Discard common "noise" lines.
+ end
+ -- XXX: Avoid newlines (too much whitespace) after block elements in old (preformatted) layout.
+ local div = opt.old and root:child(0) and vim.tbl_contains({'column_heading', 'h1', 'h2', 'h3'}, root:child(0):type())
+ return string.format('%s%s', div and trim(text) or text, div and '' or '\n')
+ elseif node_name == 'line_li' then
+ local sib = root:prev_sibling()
+ local prev_li = sib and sib:type() == 'line_li'
+
+ if not prev_li then
+ opt.indent = 1
+ else
+ -- The previous listitem _sibling_ is _logically_ the _parent_ if it is indented less.
+ local parent_indent = get_indent(node_text(sib))
+ local this_indent = get_indent(node_text())
+ if this_indent > parent_indent then
+ opt.indent = opt.indent + 1
+ elseif this_indent < parent_indent then
+ opt.indent = math.max(1, opt.indent - 1)
+ end
+ end
+ local margin = opt.indent == 1 and '' or ('margin-left: %drem;'):format((1.5 * opt.indent))
+
+ return string.format('<div class="help-li" style="%s">%s</div>', margin, text)
+ elseif node_name == 'taglink' or node_name == 'optionlink' then
+ local helppage, tagname, ignored = validate_link(root, opt.buf, opt.fname)
+ if ignored then
+ return text
+ end
+ local s = ('%s<a href="%s#%s">%s</a>'):format(ws(), helppage, url_encode(tagname), html_esc(tagname))
+ if opt.old and node_name == 'taglink' then
+ s = fix_tab_after_conceal(s, node_text(root:next_sibling()))
+ end
+ return s
+ elseif vim.tbl_contains({'codespan', 'keycode'}, node_name) then
+ if root:has_error() then
+ return text
+ end
+ local s = ('%s<code>%s</code>'):format(ws(), trimmed)
+ if opt.old and node_name == 'codespan' then
+ s = fix_tab_after_conceal(s, node_text(root:next_sibling()))
+ end
+ return s
+ elseif node_name == 'argument' then
+ return ('%s<code>{%s}</code>'):format(ws(), text)
+ elseif node_name == 'codeblock' then
+ return text
+ elseif node_name == 'language' then
+ language = node_text(root)
+ return ''
+ elseif node_name == 'code' then
+ if is_blank(text) then
+ return ''
+ end
+ local code
+ if language then
+ code = ('<pre><code class="language-%s">%s</code></pre>'):format(language,trim(trim_indent(text), 2))
+ language = nil
+ else
+ code = ('<pre>%s</pre>'):format(trim(trim_indent(text), 2))
+ end
+ return code
+ elseif node_name == 'tag' then -- anchor
+ if root:has_error() then
+ return text
+ end
+ local in_heading = vim.tbl_contains({'h1', 'h2', 'h3'}, parent)
+ local cssclass = (not in_heading and get_indent(node_text()) > 8) and 'help-tag-right' or 'help-tag'
+ local tagname = node_text(root:child(1), false)
+ if vim.tbl_count(stats.first_tags) < 2 then
+ -- Force the first 2 tags in the doc to be anchored at the main heading.
+ table.insert(stats.first_tags, tagname)
+ return ''
+ end
+ local el = in_heading and 'span' or 'code'
+ local s = ('%s<a name="%s"></a><%s class="%s">%s</%s>'):format(ws(), url_encode(tagname), el, cssclass, trimmed, el)
+ if opt.old then
+ s = fix_tab_after_conceal(s, node_text(root:next_sibling()))
+ end
+
+ if in_heading and prev ~= 'tag' then
+ -- Start the <span> container for tags in a heading.
+ -- This makes "justify-content:space-between" right-align the tags.
+ -- <h2>foo bar<span>tag1 tag2</span></h2>
+ return string.format('<span class="help-heading-tags">%s', s)
+ elseif in_heading and next_ == nil then
+ -- End the <span> container for tags in a heading.
+ return string.format('%s</span>', s)
+ end
+ return s
+ elseif node_name == 'ERROR' then
+ if ignore_parse_error(trimmed) then
+ return text
+ end
+
+ -- Store the raw text to give context to the bug report.
+ local sample_text = level > 0 and getbuflinestr(root, opt.buf, 3) or '[top level!]'
+ table.insert(stats.parse_errors, sample_text)
+ return ('<a class="parse-error" target="_blank" title="Report bug... (parse error)" href="%s">%s</a>'):format(
+ get_bug_url_vimdoc(opt.fname, opt.to_fname, sample_text), trimmed)
+ else -- Unknown token.
+ local sample_text = level > 0 and getbuflinestr(root, opt.buf, 3) or '[top level!]'
+ return ('<a class="unknown-token" target="_blank" title="Report bug... (unhandled token "%s")" href="%s">%s</a>'):format(
+ node_name, get_bug_url_nvim(opt.fname, opt.to_fname, sample_text, node_name), trimmed), ('unknown-token:"%s"'):format(node_name)
+ end
+end
+
+local function get_helpfiles(include)
+ local dir = './build/runtime/doc'
+ local rv = {}
+ for f, type in vim.fs.dir(dir) do
+ if (vim.endswith(f, '.txt')
+ and type == 'file'
+ and (not include or vim.tbl_contains(include, f))) then
+ local fullpath = vim.fn.fnamemodify(('%s/%s'):format(dir, f), ':p')
+ table.insert(rv, fullpath)
+ end
+ end
+ return rv
+end
+
+-- Populates the helptags map.
+local function get_helptags(help_dir)
+ local m = {}
+ -- Load a random help file to convince taglist() to do its job.
+ vim.cmd(string.format('split %s/api.txt', help_dir))
+ vim.cmd('lcd %:p:h')
+ for _, item in ipairs(vim.fn.taglist('.*')) do
+ if vim.endswith(item.filename, '.txt') then
+ m[item.name] = item.filename
+ end
+ end
+ vim.cmd('q!')
+ return m
+end
+
+-- Use the help.so parser defined in the build, not whatever happens to be installed on the system.
+local function ensure_runtimepath()
+ if not vim.o.runtimepath:find('build/lib/nvim/') then
+ vim.cmd[[set runtimepath^=./build/lib/nvim/]]
+ end
+end
+
+-- Opens `fname` in a buffer and gets a treesitter parser for the buffer contents.
+--
+-- @returns lang_tree, bufnr
+local function parse_buf(fname)
+ local buf
+ if type(fname) == 'string' then
+ vim.cmd('split '..vim.fn.fnameescape(fname)) -- Filename.
+ buf = vim.api.nvim_get_current_buf()
+ else
+ buf = fname
+ vim.cmd('sbuffer '..tostring(fname)) -- Buffer number.
+ end
+ -- vim.treesitter.require_language('help', './build/lib/nvim/parser/help.so')
+ local lang_tree = vim.treesitter.get_parser(buf, 'help')
+ return lang_tree, buf
+end
+
+-- Validates one :help file `fname`:
+-- - checks that |tag| links point to valid helptags.
+-- - recursively counts parse errors ("ERROR" nodes)
+--
+-- @returns { invalid_links: number, parse_errors: number }
+local function validate_one(fname)
+ local stats = {
+ parse_errors = {},
+ }
+ local lang_tree, buf = parse_buf(fname)
+ for _, tree in ipairs(lang_tree:trees()) do
+ visit_validate(tree:root(), 0, tree, { buf = buf, fname = fname, }, stats)
+ end
+ lang_tree:destroy()
+ vim.cmd.close()
+ return stats
+end
+
+-- Generates HTML from one :help file `fname` and writes the result to `to_fname`.
+--
+-- @param fname Source :help file
+-- @param to_fname Destination .html file
+-- @param old boolean Preformat paragraphs (for old :help files which are full of arbitrary whitespace)
+--
+-- @returns html, stats
+local function gen_one(fname, to_fname, old, commit)
+ local stats = {
+ noise_lines = {},
+ parse_errors = {},
+ first_tags = {}, -- Track the first few tags in doc.
+ }
+ local lang_tree, buf = parse_buf(fname)
+ local headings = {} -- Headings (for ToC). 2-dimensional: h1 contains h2/h3.
+ local title = to_titlecase(basename_noext(fname))
+
+ local html = ([[
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta name="description" content="Neovim user documentation">
+ <link href="/css/normalize.min.css" rel="stylesheet">
+ <link href="/css/bootstrap.css" rel="stylesheet">
+ <link href="/css/main.css" rel="stylesheet">
+ <link href="help.css" rel="stylesheet">
+ <link href="/highlight/styles/neovim.min.css" rel="stylesheet">
+ <script src="/highlight/highlight.min.js"></script>
+ <script>hljs.highlightAll();</script>
+ <title>%s - Neovim docs</title>
+ </head>
+ <body>
+ ]]):format(title)
+
+ local logo_svg = [[
+ <svg xmlns="http://www.w3.org/2000/svg" role="img" width="173" height="50" viewBox="0 0 742 214" aria-label="Neovim">
+ <title>Neovim</title>
+ <defs>
+ <linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="a">
+ <stop stop-color="#16B0ED" stop-opacity=".8" offset="0%" />
+ <stop stop-color="#0F59B2" stop-opacity=".837" offset="100%" />
+ </linearGradient>
+ <linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="b">
+ <stop stop-color="#7DB643" offset="0%" />
+ <stop stop-color="#367533" offset="100%" />
+ </linearGradient>
+ <linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="c">
+ <stop stop-color="#88C649" stop-opacity=".8" offset="0%" />
+ <stop stop-color="#439240" stop-opacity=".84" offset="100%" />
+ </linearGradient>
+ </defs>
+ <g fill="none" fill-rule="evenodd">
+ <path
+ d="M.027 45.459L45.224-.173v212.171L.027 166.894V45.459z"
+ fill="url(#a)"
+ transform="translate(1 1)"
+ />
+ <path
+ d="M129.337 45.89L175.152-.149l-.928 212.146-45.197-45.104.31-121.005z"
+ fill="url(#b)"
+ transform="matrix(-1 0 0 1 305 1)"
+ />
+ <path
+ d="M45.194-.137L162.7 179.173l-32.882 32.881L12.25 33.141 45.194-.137z"
+ fill="url(#c)"
+ transform="translate(1 1)"
+ />
+ <path
+ d="M46.234 84.032l-.063 7.063-36.28-53.563 3.36-3.422 32.983 49.922z"
+ fill-opacity=".13"
+ fill="#000"
+ />
+ <g fill="#444">
+ <path
+ d="M227 154V64.44h4.655c1.55 0 2.445.75 2.685 2.25l.806 13.502c4.058-5.16 8.786-9.316 14.188-12.466 5.4-3.15 11.413-4.726 18.037-4.726 4.893 0 9.205.781 12.935 2.34 3.729 1.561 6.817 3.811 9.264 6.751 2.448 2.942 4.297 6.48 5.55 10.621 1.253 4.14 1.88 8.821 1.88 14.042V154h-8.504V96.754c0-8.402-1.91-14.987-5.729-19.757-3.82-4.771-9.667-7.156-17.544-7.156-5.851 0-11.28 1.516-16.292 4.545-5.013 3.032-9.489 7.187-13.427 12.467V154H227zM350.624 63c5.066 0 9.755.868 14.069 2.605 4.312 1.738 8.052 4.268 11.219 7.592s5.638 7.412 7.419 12.264C385.11 90.313 386 95.883 386 102.17c0 1.318-.195 2.216-.588 2.696-.393.48-1.01.719-1.851.719h-64.966v1.70c0 6.708.784 12.609 2.353 17.7 1.567 5.09 3.8 9.357 6.695 12.802 2.895 3.445 6.393 6.034 10.495 7.771 4.1 1.738 8.686 2.606 13.752 2.606 4.524 0 8.446-.494 11.762-1.483 3.317-.988 6.108-2.097 8.37-3.324 2.261-1.227 4.056-2.336 5.383-3.324 1.326-.988 2.292-1.482 2.895-1.482.784 0 1.388.3 1.81.898l2.352 2.875c-1.448 1.797-3.362 3.475-5.745 5.031-2.383 1.558-5.038 2.891-7.962 3.998-2.926 1.109-6.062 1.991-9.41 2.65a52.21 52.21 0 01-10.088.989c-6.152 0-11.762-1.064-16.828-3.19-5.067-2.125-9.415-5.225-13.043-9.298-3.63-4.074-6.435-9.06-8.415-14.96C310.99 121.655 310 114.9 310 107.294c0-6.408.92-12.323 2.76-17.744 1.84-5.421 4.493-10.093 7.961-14.016 3.467-3.922 7.72-6.991 12.758-9.209C338.513 64.11 344.229 63 350.624 63zm.573 6c-4.696 0-8.904.702-12.623 2.105-3.721 1.404-6.936 3.421-9.65 6.053-2.713 2.631-4.908 5.79-6.586 9.474S319.55 94.439 319 99h60c0-4.679-.672-8.874-2.013-12.588-1.343-3.712-3.232-6.856-5.67-9.43-2.44-2.571-5.367-4.545-8.782-5.92-3.413-1.374-7.192-2.062-11.338-2.062zM435.546 63c6.526 0 12.368 1.093 17.524 3.28 5.154 2.186 9.5 5.286 13.04 9.298 3.538 4.013 6.238 8.85 8.099 14.51 1.861 5.66 2.791 11.994 2.791 19.002 0 7.008-.932 13.327-2.791 18.957-1.861 5.631-4.561 10.452-8.099 14.465-3.54 4.012-7.886 7.097-13.04 9.254-5.156 2.156-10.998 3.234-17.524 3.234-6.529 0-12.369-1.078-17.525-3.234-5.155-2.157-9.517-5.242-13.085-9.254-3.57-4.013-6.285-8.836-8.145-14.465-1.861-5.63-2.791-11.95-2.791-18.957 0-7.008.93-13.342 2.791-19.002 1.861-5.66 4.576-10.496 8.145-14.51 3.568-4.012 7.93-7.112 13.085-9.299C423.177 64.094 429.017 63 435.546 63zm-.501 86c5.341 0 10.006-.918 13.997-2.757 3.99-1.838 7.32-4.474 9.992-7.909 2.67-3.435 4.664-7.576 5.986-12.428 1.317-4.85 1.98-10.288 1.98-16.316 0-5.965-.66-11.389-1.98-16.27-1.322-4.88-3.316-9.053-5.986-12.519-2.67-3.463-6-6.13-9.992-7.999-3.991-1.867-8.657-2.802-13.997-2.802s-10.008.935-13.997 2.802c-3.991 1.87-7.322 4.536-9.992 8-2.671 3.465-4.68 7.637-6.03 12.518-1.35 4.881-2.026 10.305-2.026 16.27 0 6.026.675 11.465 2.025 16.316 1.35 4.852 3.36 8.993 6.031 12.428 2.67 3.435 6 6.07 9.992 7.91 3.99 1.838 8.656 2.756 13.997 2.756z"
+ fill="currentColor"
+ />
+ <path
+ d="M530.57 152h-20.05L474 60h18.35c1.61 0 2.967.39 4.072 1.166 1.103.778 1.865 1.763 2.283 2.959l17.722 49.138a92.762 92.762 0 012.551 8.429c.686 2.751 1.298 5.5 1.835 8.25.537-2.75 1.148-5.499 1.835-8.25a77.713 77.713 0 012.64-8.429l18.171-49.138c.417-1.196 1.164-2.181 2.238-2.96 1.074-.776 2.356-1.165 3.849-1.165H567l-36.43 92zM572 61h23v92h-23zM610 153V60.443h13.624c2.887 0 4.78 1.354 5.682 4.06l1.443 6.856a52.7 52.7 0 015.097-4.962 32.732 32.732 0 015.683-3.879 30.731 30.731 0 016.496-2.57c2.314-.632 4.855-.948 7.624-.948 5.832 0 10.63 1.579 14.39 4.736 3.758 3.157 6.57 7.352 8.434 12.585 1.444-3.068 3.248-5.698 5.413-7.894 2.165-2.194 4.541-3.984 7.127-5.367a32.848 32.848 0 018.254-3.068 39.597 39.597 0 018.796-.992c5.111 0 9.653.783 13.622 2.345 3.97 1.565 7.307 3.849 10.014 6.857 2.706 3.007 4.766 6.675 6.18 11.005C739.29 83.537 740 88.5 740 94.092V153h-22.284V94.092c0-5.894-1.294-10.329-3.878-13.306-2.587-2.977-6.376-4.465-11.368-4.465-2.286 0-4.404.391-6.358 1.172a15.189 15.189 0 00-5.144 3.383c-1.473 1.474-2.631 3.324-3.474 5.548-.842 2.225-1.263 4.781-1.263 7.668V153h-22.37V94.092c0-6.194-1.249-10.704-3.744-13.532-2.497-2.825-6.18-4.24-11.051-4.24-3.19 0-6.18.798-8.976 2.391-2.799 1.593-5.399 3.775-7.804 6.54V153H610zM572 30h23v19h-23z"
+ fill="currentColor"
+ fill-opacity=".8"
+ />
+ </g>
+ </g>
+ </svg>
+ ]]
+
+ local main = ''
+ for _, tree in ipairs(lang_tree:trees()) do
+ main = main .. (visit_node(tree:root(), 0, tree, headings,
+ { buf = buf, old = old, fname = fname, to_fname = to_fname, indent = 1, },
+ stats))
+ end
+
+ main = ([[
+ <header class="container">
+ <nav class="navbar navbar-expand-lg">
+ <div>
+ <a href="/" class="navbar-brand" aria-label="logo">
+ <!--TODO: use <img src="….svg"> here instead. Need one that has green lettering instead of gray. -->
+ %s
+ <!--<img src="https://neovim.io/logos/neovim-logo.svg" width="173" height="50" alt="Neovim" />-->
+ </a>
+ </div>
+ </nav>
+ </header>
+
+ <div class="container golden-grid help-body">
+ <div class="col-wide">
+ <a name="%s"></a><a name="%s"></a><h1>%s</h1>
+ <p>
+ <i>
+ Nvim <code>:help</code> pages, <a href="https://github.com/neovim/neovim/blob/master/scripts/gen_help_html.lua">generated</a>
+ from <a href="https://github.com/neovim/neovim/blob/master/runtime/doc/%s">source</a>
+ using the <a href="https://github.com/neovim/tree-sitter-vimdoc">tree-sitter-vimdoc</a> parser.
+ </i>
+ </p>
+ <hr/>
+ %s
+ </div>
+ ]]):format(logo_svg, stats.first_tags[1] or '', stats.first_tags[2] or '', title, vim.fs.basename(fname), main)
+
+ local toc = [[
+ <div class="col-narrow toc">
+ <div><a href="index.html">Main</a></div>
+ <div><a href="vimindex.html">Commands index</a></div>
+ <div><a href="quickref.html">Quick reference</a></div>
+ <hr/>
+ ]]
+
+ local n = 0 -- Count of all headings + subheadings.
+ for _, h1 in ipairs(headings) do n = n + 1 + #h1.subheadings end
+ for _, h1 in ipairs(headings) do
+ toc = toc .. ('<div class="help-toc-h1"><a href="#%s">%s</a>\n'):format(h1.tag, h1.name)
+ if n < 30 or #headings < 10 then -- Show subheadings only if there aren't too many.
+ for _, h2 in ipairs(h1.subheadings) do
+ toc = toc .. ('<div class="help-toc-h2"><a href="#%s">%s</a></div>\n'):format(h2.tag, h2.name)
+ end
+ end
+ toc = toc .. '</div>'
+ end
+ toc = toc .. '</div>\n'
+
+ local bug_url = get_bug_url_nvim(fname, to_fname, 'TODO', nil)
+ local bug_link = string.format('(<a href="%s" target="_blank">report docs bug...</a>)', bug_url)
+
+ local footer = ([[
+ <footer>
+ <div class="container flex">
+ <div class="generator-stats">
+ Generated at %s from <code><a href="https://github.com/neovim/neovim/commit/%s">%s</a></code>
+ </div>
+ <div class="generator-stats">
+ parse_errors: %d %s | <span title="%s">noise_lines: %d</span>
+ </div>
+ <div>
+ </footer>
+ ]]):format(
+ os.date('%Y-%m-%d %H:%M'), commit, commit:sub(1, 7), #stats.parse_errors, bug_link,
+ html_esc(table.concat(stats.noise_lines, '\n')), #stats.noise_lines)
+
+ html = ('%s%s%s</div>\n%s</body>\n</html>\n'):format(
+ html, main, toc, footer)
+ vim.cmd('q!')
+ lang_tree:destroy()
+ return html, stats
+end
+
+local function gen_css(fname)
+ local css = [[
+ :root {
+ --code-color: #004b4b;
+ --tag-color: #095943;
+ }
+ @media (prefers-color-scheme: dark) {
+ :root {
+ --code-color: #00c243;
+ --tag-color: #00b7b7;
+ }
+ }
+ @media (min-width: 40em) {
+ .toc {
+ position: fixed;
+ left: 67%;
+ }
+ .golden-grid {
+ display: grid;
+ grid-template-columns: 65% auto;
+ grid-gap: 1em;
+ }
+ }
+ @media (max-width: 40em) {
+ .golden-grid {
+ /* Disable grid for narrow viewport (mobile phone). */
+ display: block;
+ }
+ }
+ .toc {
+ /* max-width: 12rem; */
+ height: 85%; /* Scroll if there are too many items. https://github.com/neovim/neovim.github.io/issues/297 */
+ overflow: auto; /* Scroll if there are too many items. https://github.com/neovim/neovim.github.io/issues/297 */
+ }
+ .toc > div {
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+ }
+ html {
+ scroll-behavior: auto;
+ }
+ body {
+ font-size: 18px;
+ line-height: 1.5;
+ }
+ h1, h2, h3, h4, h5 {
+ font-family: sans-serif;
+ border-bottom: 1px solid var(--tag-color); /*rgba(0, 0, 0, .9);*/
+ }
+ h3, h4, h5 {
+ border-bottom-style: dashed;
+ }
+ .help-column_heading {
+ color: var(--code-color);
+ }
+ .help-body {
+ padding-bottom: 2em;
+ }
+ .help-line {
+ /* font-family: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace; */
+ }
+ .help-li {
+ white-space: normal;
+ display: list-item;
+ margin-left: 1.5rem; /* padding-left: 1rem; */
+ }
+ .help-para {
+ padding-top: 10px;
+ padding-bottom: 10px;
+ }
+ .old-help-para {
+ padding-top: 10px;
+ padding-bottom: 10px;
+ /* Tabs are used for alignment in old docs, so we must match Vim's 8-char expectation. */
+ tab-size: 8;
+ white-space: pre;
+ font-size: 16px;
+ font-family: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;
+ }
+ a.help-tag, a.help-tag:focus, a.help-tag:hover {
+ color: inherit;
+ text-decoration: none;
+ }
+ .help-tag {
+ color: var(--tag-color);
+ }
+ /* Tag pseudo-header common in :help docs. */
+ .help-tag-right {
+ color: var(--tag-color);
+ }
+ h1 .help-tag, h2 .help-tag, h3 .help-tag {
+ font-size: smaller;
+ }
+ .help-heading {
+ overflow: hidden;
+ white-space: nowrap;
+ display: flex;
+ justify-content: space-between;
+ }
+ /* The (right-aligned) "tags" part of a section heading. */
+ .help-heading-tags {
+ margin-right: 10px;
+ }
+ .help-toc-h1 {
+ }
+ .help-toc-h2 {
+ margin-left: 1em;
+ }
+ .parse-error {
+ background-color: red;
+ }
+ .unknown-token {
+ color: black;
+ background-color: yellow;
+ }
+ code {
+ color: var(--code-color);
+ font-size: 16px;
+ }
+ pre {
+ /* Tabs are used in codeblocks only for indentation, not alignment, so we can aggressively shrink them. */
+ tab-size: 2;
+ white-space: pre-wrap;
+ line-height: 1.3; /* Important for ascii art. */
+ overflow: visible;
+ /* font-family: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace; */
+ font-size: 16px;
+ margin-top: 10px;
+ }
+ pre:hover,
+ .help-heading:hover {
+ overflow: visible;
+ }
+ .generator-stats {
+ color: gray;
+ font-size: smaller;
+ }
+ ]]
+ tofile(fname, css)
+end
+
+function M._test()
+ tagmap = get_helptags('./build/runtime/doc')
+ helpfiles = get_helpfiles()
+
+ local function ok(cond, expected, actual)
+ assert((not expected and not actual) or (expected and actual), 'if "expected" is given, "actual" is also required')
+ if expected then
+ return assert(cond, ('expected %s, got: %s'):format(vim.inspect(expected), vim.inspect(actual)))
+ else
+ return assert(cond)
+ end
+ end
+ local function eq(expected, actual)
+ return ok(expected == actual, expected, actual)
+ end
+
+ ok(vim.tbl_count(tagmap) > 3000, '>3000', vim.tbl_count(tagmap))
+ ok(vim.endswith(tagmap['vim.diagnostic.set()'], 'diagnostic.txt'), tagmap['vim.diagnostic.set()'], 'diagnostic.txt')
+ ok(vim.endswith(tagmap['%:s'], 'cmdline.txt'), tagmap['%:s'], 'cmdline.txt')
+ ok(is_noise([[vim:tw=78:isk=!-~,^*,^\|,^\":ts=8:noet:ft=help:norl:]]))
+ ok(is_noise([[ NVIM REFERENCE MANUAL by Thiago de Arruda ]]))
+ ok(not is_noise([[vim:tw=78]]))
+
+ eq(0, get_indent('a'))
+ eq(1, get_indent(' a'))
+ eq(2, get_indent(' a\n b\n c\n'))
+ eq(5, get_indent(' a\n \n b\n c\n d\n e\n'))
+ eq('a\n \n b\n c\n d\n e\n', trim_indent(' a\n \n b\n c\n d\n e\n'))
+
+ local fixed_url, removed_chars = fix_url('https://example.com).')
+ eq('https://example.com', fixed_url)
+ eq(').', removed_chars)
+ fixed_url, removed_chars = fix_url('https://example.com.)')
+ eq('https://example.com.', fixed_url)
+ eq(')', removed_chars)
+ fixed_url, removed_chars = fix_url('https://example.com.')
+ eq('https://example.com', fixed_url)
+ eq('.', removed_chars)
+ fixed_url, removed_chars = fix_url('https://example.com)')
+ eq('https://example.com', fixed_url)
+ eq(')', removed_chars)
+ fixed_url, removed_chars = fix_url('https://example.com')
+ eq('https://example.com', fixed_url)
+ eq('', removed_chars)
+
+ print('all tests passed')
+end
+
+--- Generates HTML from :help docs located in `help_dir` and writes the result in `to_dir`.
+---
+--- Example:
+---
+--- gen('./build/runtime/doc', '/path/to/neovim.github.io/_site/doc/', {'api.txt', 'autocmd.txt', 'channel.txt'}, nil)
+---
+--- @param help_dir string Source directory containing the :help files. Must run `make helptags` first.
+--- @param to_dir string Target directory where the .html files will be written.
+--- @param include table|nil Process only these filenames. Example: {'api.txt', 'autocmd.txt', 'channel.txt'}
+---
+--- @returns info dict
+function M.gen(help_dir, to_dir, include, commit)
+ vim.validate{
+ help_dir={help_dir, function(d) return vim.fn.isdirectory(d) == 1 end, 'valid directory'},
+ to_dir={to_dir, 's'},
+ include={include, 't', true},
+ commit={commit, 's', true},
+ }
+
+ local err_count = 0
+ ensure_runtimepath()
+ tagmap = get_helptags(help_dir)
+ helpfiles = get_helpfiles(include)
+
+ print(('output dir: %s'):format(to_dir))
+ vim.fn.mkdir(to_dir, 'p')
+ gen_css(('%s/help.css'):format(to_dir))
+
+ for _, f in ipairs(helpfiles) do
+ local helpfile = vim.fs.basename(f)
+ local to_fname = ('%s/%s'):format(to_dir, get_helppage(helpfile))
+ local html, stats = gen_one(f, to_fname, not new_layout[helpfile], commit or '?')
+ tofile(to_fname, html)
+ print(('generated (%-4s errors): %-15s => %s'):format(#stats.parse_errors, helpfile, vim.fs.basename(to_fname)))
+ err_count = err_count + #stats.parse_errors
+ end
+ print(('generated %d html pages'):format(#helpfiles))
+ print(('total errors: %d'):format(err_count))
+ print(('invalid tags:\n%s'):format(vim.inspect(invalid_links)))
+
+ return {
+ helpfiles = helpfiles,
+ err_count = err_count,
+ invalid_links = invalid_links,
+ }
+end
+
+-- Validates all :help files found in `help_dir`:
+-- - checks that |tag| links point to valid helptags.
+-- - recursively counts parse errors ("ERROR" nodes)
+--
+-- This is 10x faster than gen(), for use in CI.
+--
+-- @returns results dict
+function M.validate(help_dir, include)
+ vim.validate{
+ help_dir={help_dir, function(d) return vim.fn.isdirectory(d) == 1 end, 'valid directory'},
+ include={include, 't', true},
+ }
+ local err_count = 0
+ ensure_runtimepath()
+ tagmap = get_helptags(help_dir)
+ helpfiles = get_helpfiles(include)
+
+ for _, f in ipairs(helpfiles) do
+ local helpfile = vim.fs.basename(f)
+ local rv = validate_one(f)
+ print(('validated (%-4s errors): %s'):format(#rv.parse_errors, helpfile))
+ err_count = err_count + #rv.parse_errors
+ end
+
+ return {
+ helpfiles = #helpfiles,
+ err_count = err_count,
+ invalid_links = invalid_links,
+ invalid_urls = invalid_urls,
+ invalid_spelling = invalid_spelling,
+ }
+end
+
+return M
diff --git a/scripts/gen_help_html.py b/scripts/gen_help_html.py
deleted file mode 100644
index 0b8e77ac22..0000000000
--- a/scripts/gen_help_html.py
+++ /dev/null
@@ -1,389 +0,0 @@
-# Converts Vim/Nvim documentation to HTML.
-#
-# USAGE:
-# 1. python3 scripts/gen_help_html.py runtime/doc/ ~/neovim.github.io/t/
-# 3. cd ~/neovim.github.io/ && jekyll serve --host 0.0.0.0
-# 2. Visit http://localhost:4000/t/help.txt.html
-#
-# Adapted from https://github.com/c4rlo/vimhelp/
-# License: MIT
-#
-# Copyright (c) 2016 Carlo Teubner
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 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.
-
-import os
-import re
-import urllib.parse
-import datetime
-import sys
-from itertools import chain
-
-HEAD = """\
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
- "http://www.w3.org/TR/html4/loose.dtd">
-<html>
-<head>
-<meta http-equiv="Content-type" content="text/html; charset={encoding}"/>
-<style>
-.h {{
- font-weight: bold;
-}}
-h1 {{
- font-family: sans-serif;
-}}
-pre {{
- font-family: sans-serif;
-}}
-</style>
-<title>Nvim: {filename}</title>
-"""
-
-HEAD_END = '</head>\n<body>\n'
-
-INTRO = """
-<h1>Nvim help files</h1>
-<p>
-<a href="https://neovim.io/">Nvim</a> help pages{vers-note}.
-Updated <a href="https://github.com/neovim/bot-ci" class="d">automatically</a>
-from the <a href="https://github.com/neovim/neovim" class="d">Nvim source</a>.
-</p>
-"""
-
-VERSION_NOTE = ", current as of Nvim {version}"
-
-SITENAVI_LINKS = """
-<a href="quickref.txt.html">Quick reference</a> &middot;
-<a href="usr_toc.txt.html">User manual</a> &middot;
-<a href="{helptxt}#reference_toc">Reference manual</a> &middot;
-"""
-
-SITENAVI_LINKS_PLAIN = SITENAVI_LINKS.format(helptxt='help.txt.html')
-SITENAVI_LINKS_WEB = SITENAVI_LINKS.format(helptxt='/')
-
-SITENAVI_PLAIN = '<p>' + SITENAVI_LINKS_PLAIN + '</p>'
-SITENAVI_WEB = '<p>' + SITENAVI_LINKS_WEB + '</p>'
-
-SITENAVI_SEARCH = '<table width="100%"><tbody><tr><td>' + SITENAVI_LINKS_WEB + \
- '</td><td style="text-align: right; max-width: 25vw"><div class="gcse-searchbox">' \
- '</div></td></tr></tbody></table><div class="gcse-searchresults"></div>'
-
-TEXTSTART = """
-<div id="d1">
-<pre id="sp">""" + (" " * 80) + """</pre>
-<div id="d2">
-<pre>
-"""
-
-FOOTER = '</pre>'
-
-FOOTER2 = """
-<p id="footer">Generated {generated_date} from <code>{commit}</code></p>
-</div>
-</div>
-</body>
-</html>
-""".format(
- generated_date='{0:%Y-%m-%d %H:%M:%S}'.format(datetime.datetime.now()),
- commit='?')
-
-RE_TAGLINE = re.compile(r'(\S+)\s+(\S+)')
-
-PAT_WORDCHAR = '[!#-)+-{}~\xC0-\xFF]'
-
-PAT_HEADER = r'(^.*~$)'
-PAT_GRAPHIC = r'(^.* `$)'
-PAT_PIPEWORD = r'(?<!\\)\|([#-)!+-~]+)\|'
-PAT_STARWORD = r'\*([#-)!+-~]+)\*(?:(?=\s)|$)'
-PAT_COMMAND = r'`([^` ]+)`'
-PAT_OPTWORD = r"('(?:[a-z]{2,}|t_..)')"
-PAT_CTRL = r'(CTRL-(?:W_)?(?:\{char\}|<[A-Za-z]+?>|.)?)'
-PAT_SPECIAL = r'(<.+?>|\{.+?}|' \
- r'\[(?:range|line|count|offset|\+?cmd|[-+]?num|\+\+opt|' \
- r'arg|arguments|ident|addr|group)]|' \
- r'(?<=\s)\[[-a-z^A-Z0-9_]{2,}])'
-PAT_TITLE = r'(Vim version [0-9.a-z]+|VIM REFERENCE.*)'
-PAT_NOTE = r'((?<!' + PAT_WORDCHAR + r')(?:note|NOTE|Notes?):?' \
- r'(?!' + PAT_WORDCHAR + r'))'
-PAT_URL = r'((?:https?|ftp)://[^\'"<> \t]+[a-zA-Z0-9/])'
-PAT_WORD = r'((?<!' + PAT_WORDCHAR + r')' + PAT_WORDCHAR + r'+' \
- r'(?!' + PAT_WORDCHAR + r'))'
-
-RE_LINKWORD = re.compile(
- PAT_OPTWORD + '|' +
- PAT_CTRL + '|' +
- PAT_SPECIAL)
-RE_TAGWORD = re.compile(
- PAT_HEADER + '|' +
- PAT_GRAPHIC + '|' +
- PAT_PIPEWORD + '|' +
- PAT_STARWORD + '|' +
- PAT_COMMAND + '|' +
- PAT_OPTWORD + '|' +
- PAT_CTRL + '|' +
- PAT_SPECIAL + '|' +
- PAT_TITLE + '|' +
- PAT_NOTE + '|' +
- PAT_URL + '|' +
- PAT_WORD)
-RE_NEWLINE = re.compile(r'[\r\n]')
-# H1 header "=====…"
-# H2 header "-----…"
-RE_HRULE = re.compile(r'[-=]{3,}.*[-=]{3,3}$')
-RE_EG_START = re.compile(r'(?:.* )?>$')
-RE_EG_END = re.compile(r'\S')
-RE_SECTION = re.compile(r'[-A-Z .][-A-Z0-9 .()]*(?=\s+\*)')
-RE_STARTAG = re.compile(r'\s\*([^ \t|]+)\*(?:\s|$)')
-RE_LOCAL_ADD = re.compile(r'LOCAL ADDITIONS:\s+\*local-additions\*$')
-
-
-class Link(object):
- __slots__ = 'link_plain_same', 'link_pipe_same', \
- 'link_plain_foreign', 'link_pipe_foreign', \
- 'filename'
-
- def __init__(self, link_plain_same, link_plain_foreign,
- link_pipe_same, link_pipe_foreign, filename):
- self.link_plain_same = link_plain_same
- self.link_plain_foreign = link_plain_foreign
- self.link_pipe_same = link_pipe_same
- self.link_pipe_foreign = link_pipe_foreign
- self.filename = filename
-
-
-class VimH2H(object):
- def __init__(self, tags, version=None, is_web_version=True):
- self._urls = {}
- self._version = version
- self._is_web_version = is_web_version
- for line in RE_NEWLINE.split(tags):
- m = RE_TAGLINE.match(line)
- if m:
- tag, filename = m.group(1, 2)
- self.do_add_tag(filename, tag)
-
- def add_tags(self, filename, contents):
- for match in RE_STARTAG.finditer(contents):
- tag = match.group(1).replace('\\', '\\\\').replace('/', '\\/')
- self.do_add_tag(str(filename), tag)
-
- def do_add_tag(self, filename, tag):
- tag_quoted = urllib.parse.quote_plus(tag)
-
- def mkpart1(doc):
- return '<a href="' + doc + '#' + tag_quoted + '" class="'
- part1_same = mkpart1('')
- if self._is_web_version and filename == 'help.txt':
- doc = '/'
- else:
- doc = filename + '.html'
- part1_foreign = mkpart1(doc)
- part2 = '">' + html_escape[tag] + '</a>'
-
- def mklinks(cssclass):
- return (part1_same + cssclass + part2,
- part1_foreign + cssclass + part2)
- cssclass_plain = 'd'
- m = RE_LINKWORD.match(tag)
- if m:
- opt, ctrl, special = m.groups()
- if opt is not None:
- cssclass_plain = 'o'
- elif ctrl is not None:
- cssclass_plain = 'k'
- elif special is not None:
- cssclass_plain = 's'
- links_plain = mklinks(cssclass_plain)
- links_pipe = mklinks('l')
- self._urls[tag] = Link(
- links_plain[0], links_plain[1],
- links_pipe[0], links_pipe[1],
- filename)
-
- def maplink(self, tag, curr_filename, css_class=None):
- links = self._urls.get(tag)
- if links is not None:
- if links.filename == curr_filename:
- if css_class == 'l':
- return links.link_pipe_same
- else:
- return links.link_plain_same
- else:
- if css_class == 'l':
- return links.link_pipe_foreign
- else:
- return links.link_plain_foreign
- elif css_class is not None:
- return '<span class="' + css_class + '">' + html_escape[tag] + \
- '</span>'
- else:
- return html_escape[tag]
-
- def to_html(self, filename, contents, encoding):
- out = []
-
- inexample = 0
- filename = str(filename)
- is_help_txt = (filename == 'help.txt')
- last = ''
- for line in RE_NEWLINE.split(contents):
- line = line.rstrip('\r\n')
- line_tabs = line
- line = line.expandtabs()
- if last == 'h1':
- out.extend(('</pre>')) # XXX
- out.extend(('<h1>', line.rstrip(), '</h1>\n'))
- out.extend(('<pre>'))
- last = ''
- continue
- if RE_HRULE.match(line):
- # out.extend(('<span class="h">', line, '</span>\n'))
- last = 'h1'
- continue
- if inexample == 2:
- if RE_EG_END.match(line):
- inexample = 0
- if line[0] == '<':
- line = line[1:]
- else:
- out.extend(('<span class="e">', html_escape[line],
- '</span>\n'))
- continue
- if RE_EG_START.match(line_tabs):
- inexample = 1
- line = line[0:-1]
- if RE_SECTION.match(line_tabs):
- m = RE_SECTION.match(line)
- out.extend((r'<span class="c">', m.group(0), r'</span>'))
- line = line[m.end():]
- lastpos = 0
- for match in RE_TAGWORD.finditer(line):
- pos = match.start()
- if pos > lastpos:
- out.append(html_escape[line[lastpos:pos]])
- lastpos = match.end()
- header, graphic, pipeword, starword, command, opt, ctrl, \
- special, title, note, url, word = match.groups()
- if pipeword is not None:
- out.append(self.maplink(pipeword, filename, 'l'))
- elif starword is not None:
- out.extend(('<a name="', urllib.parse.quote_plus(starword),
- '" class="t">', html_escape[starword], '</a>'))
- elif command is not None:
- out.extend(('<span class="e">', html_escape[command],
- '</span>'))
- elif opt is not None:
- out.append(self.maplink(opt, filename, 'o'))
- elif ctrl is not None:
- out.append(self.maplink(ctrl, filename, 'k'))
- elif special is not None:
- out.append(self.maplink(special, filename, 's'))
- elif title is not None:
- out.extend(('<span class="i">', html_escape[title],
- '</span>'))
- elif note is not None:
- out.extend(('<span class="n">', html_escape[note],
- '</span>'))
- elif header is not None:
- out.extend(('<span class="h">', html_escape[header[:-1]],
- '</span>'))
- elif graphic is not None:
- out.append(html_escape[graphic[:-2]])
- elif url is not None:
- out.extend(('<a class="u" href="', url, '">' +
- html_escape[url], '</a>'))
- elif word is not None:
- out.append(self.maplink(word, filename))
- if lastpos < len(line):
- out.append(html_escape[line[lastpos:]])
- out.append('\n')
- if inexample == 1:
- inexample = 2
-
- header = []
- header.append(HEAD.format(encoding=encoding, filename=filename))
- header.append(HEAD_END)
- if self._is_web_version and is_help_txt:
- vers_note = VERSION_NOTE.replace('{version}', self._version) \
- if self._version else ''
- header.append(INTRO.replace('{vers-note}', vers_note))
- if self._is_web_version:
- header.append(SITENAVI_SEARCH)
- sitenavi_footer = SITENAVI_WEB
- else:
- header.append(SITENAVI_PLAIN)
- sitenavi_footer = SITENAVI_PLAIN
- header.append(TEXTSTART)
- return ''.join(chain(header, out, (FOOTER, sitenavi_footer, FOOTER2)))
-
-
-class HtmlEscCache(dict):
- def __missing__(self, key):
- r = key.replace('&', '&amp;') \
- .replace('<', '&lt;') \
- .replace('>', '&gt;')
- self[key] = r
- return r
-
-
-html_escape = HtmlEscCache()
-
-
-def slurp(filename):
- try:
- with open(filename, encoding='UTF-8') as f:
- return f.read(), 'UTF-8'
- except UnicodeError:
- # 'ISO-8859-1' ?
- with open(filename, encoding='latin-1') as f:
- return f.read(), 'latin-1'
-
-
-def usage():
- return "usage: " + sys.argv[0] + " IN_DIR OUT_DIR [BASENAMES...]"
-
-
-def main():
- if len(sys.argv) < 3:
- sys.exit(usage())
-
- in_dir = sys.argv[1]
- out_dir = sys.argv[2]
- basenames = sys.argv[3:]
-
- print("Processing tags...")
- h2h = VimH2H(slurp(os.path.join(in_dir, 'tags'))[0], is_web_version=False)
-
- if len(basenames) == 0:
- basenames = os.listdir(in_dir)
-
- for basename in basenames:
- if os.path.splitext(basename)[1] != '.txt' and basename != 'tags':
- print("Ignoring " + basename)
- continue
- print("Processing " + basename + "...")
- path = os.path.join(in_dir, basename)
- text, encoding = slurp(path)
- outpath = os.path.join(out_dir, basename + '.html')
- of = open(outpath, 'w')
- of.write(h2h.to_html(basename, text, encoding))
- of.close()
-
-
-main()
diff --git a/scripts/gen_vimdoc.py b/scripts/gen_vimdoc.py
index bca3dd816f..8e1d6ef80a 100755
--- a/scripts/gen_vimdoc.py
+++ b/scripts/gen_vimdoc.py
@@ -2,9 +2,7 @@
"""Generates Nvim :help docs from C/Lua docstrings, using Doxygen.
Also generates *.mpack files. To inspect the *.mpack structure:
-
- :new | put=v:lua.vim.inspect(msgpackparse(readfile('runtime/doc/api.mpack')))
-
+ :new | put=v:lua.vim.inspect(v:lua.vim.mpack.unpack(readfile('runtime/doc/api.mpack','B')))
Flow:
main
@@ -14,15 +12,10 @@ Flow:
update_params_map /
render_node
-This would be easier using lxml and XSLT, but:
-
- 1. This should avoid needing Python dependencies, especially ones that are
- C modules that have library dependencies (lxml requires libxml and
- libxslt).
- 2. I wouldn't know how to deal with nested indentation in <para> tags using
- XSLT.
+TODO: eliminate this script and use Lua+treesitter (requires parsers for C and
+Lua markdown-style docstrings).
-Each function :help block is formatted as follows:
+The generated :help text for each function is formatted as follows:
- Max width of 78 columns (`text_width`).
- Indent with spaces (not tabs).
@@ -62,11 +55,19 @@ if sys.version_info < MIN_PYTHON_VERSION:
doxygen_version = tuple((int(i) for i in subprocess.check_output(["doxygen", "-v"],
universal_newlines=True).split()[0].split('.')))
+# Until 0.9 is released, need this hacky way to check that "nvim -l foo.lua" works.
+nvim_version = list(line for line in subprocess.check_output(['nvim', '-h'], universal_newlines=True).split('\n')
+ if '-l ' in line)
+
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)
+if len(nvim_version) == 0:
+ print("\nRequires 'nvim -l' feature, see https://github.com/neovim/neovim/pull/18706")
+ sys.exit(1)
+
# DEBUG = ('DEBUG' in os.environ)
INCLUDE_C_DECL = ('INCLUDE_C_DECL' in os.environ)
INCLUDE_DEPRECATED = ('INCLUDE_DEPRECATED' in os.environ)
@@ -86,7 +87,7 @@ 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)
msgs = [] # Messages to show on exit.
-lua2dox_filter = os.path.join(base_dir, 'scripts', 'lua2dox_filter')
+lua2dox = os.path.join(base_dir, 'scripts', 'lua2dox.lua')
CONFIG = {
'api': {
@@ -132,12 +133,14 @@ CONFIG = {
'filename': 'lua.txt',
'section_order': [
'_editor.lua',
+ '_inspector.lua',
'shared.lua',
'uri.lua',
'ui.lua',
'filetype.lua',
'keymap.lua',
'fs.lua',
+ 'secure.lua',
],
'files': [
'runtime/lua/vim/_editor.lua',
@@ -147,11 +150,14 @@ CONFIG = {
'runtime/lua/vim/filetype.lua',
'runtime/lua/vim/keymap.lua',
'runtime/lua/vim/fs.lua',
+ 'runtime/lua/vim/secure.lua',
+ 'runtime/lua/vim/_inspector.lua',
],
'file_patterns': '*.lua',
'fn_name_prefix': '',
'section_name': {
'lsp.lua': 'core',
+ '_inspector.lua': 'inspector',
},
'section_fmt': lambda name: (
'Lua module: vim'
@@ -168,11 +174,13 @@ CONFIG = {
'module_override': {
# `shared` functions are exposed on the `vim` module.
'shared': 'vim',
+ '_inspector': 'vim',
'uri': 'vim',
'ui': 'vim.ui',
'filetype': 'vim.filetype',
'keymap': 'vim.keymap',
'fs': 'vim.fs',
+ 'secure': 'vim.secure',
},
'append_only': [
'shared.lua',
@@ -187,6 +195,7 @@ CONFIG = {
'diagnostic.lua',
'codelens.lua',
'tagfunc.lua',
+ 'semantic_tokens.lua',
'handlers.lua',
'util.lua',
'log.lua',
@@ -260,19 +269,13 @@ CONFIG = {
'helptag_fmt': lambda name: (
'*lua-treesitter-core*'
if name.lower() == 'treesitter'
- else f'*treesitter-{name.lower()}*'),
+ else f'*lua-treesitter-{name.lower()}*'),
'fn_helptag_fmt': lambda fstem, name: (
- f'*{name}()*'
- if name != 'new'
- else f'*{fstem}.{name}()*'),
- # 'fn_helptag_fmt': lambda fstem, name: (
- # f'*vim.treesitter.{name}()*'
- # if fstem == 'treesitter'
- # else (
- # '*vim.lsp.client*'
- # # HACK. TODO(justinmk): class/structure support in lua2dox
- # if 'lsp.client' == f'{fstem}.{name}'
- # else f'*vim.lsp.{fstem}.{name}()*')),
+ f'*vim.{fstem}.{name}()*'
+ if fstem == 'treesitter'
+ else f'*{name}()*'
+ if name[0].isupper()
+ else f'*vim.treesitter.{fstem}.{name}()*'),
'module_override': {},
'append_only': [],
}
@@ -287,7 +290,7 @@ annotation_map = {
'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',
+ 'FUNC_API_LUA_ONLY': 'Lua |vim.api| only',
}
@@ -295,14 +298,16 @@ annotation_map = {
# or if `cond()` is callable and returns True.
def debug_this(o, cond=True):
name = ''
+ if cond is False:
+ return
if not isinstance(o, str):
try:
name = o.nodeName
o = o.toprettyxml(indent=' ', newl='\n')
except Exception:
pass
- if ((callable(cond) and cond())
- or (not callable(cond) and cond)
+ if (cond is True
+ or (callable(cond) and cond())
or (not callable(cond) and cond in o)):
raise RuntimeError('xxx: {}\n{}'.format(name, o))
@@ -353,6 +358,17 @@ def self_or_child(n):
return n.childNodes[0]
+def align_tags(line):
+ tag_regex = r"\s(\*.+?\*)(?:\s|$)"
+ tags = re.findall(tag_regex, line)
+
+ if len(tags) > 0:
+ line = re.sub(tag_regex, "", line)
+ tags = " " + " ".join(tags)
+ line = line + (" " * (78 - len(line) - len(tags))) + tags
+ return line
+
+
def clean_lines(text):
"""Removes superfluous lines.
@@ -498,7 +514,12 @@ def render_node(n, text, prefix='', indent='', width=text_width - indentation,
if n.nodeName == 'preformatted':
o = get_text(n, preformatted=True)
ensure_nl = '' if o[-1] == '\n' else '\n'
- text += '>{}{}\n<'.format(ensure_nl, o)
+ if o[0:4] == 'lua\n':
+ text += '>lua{}{}\n<'.format(ensure_nl, o[3:-1])
+ elif o[0:4] == 'vim\n':
+ text += '>vim{}{}\n<'.format(ensure_nl, o[3:-1])
+ else:
+ text += '>{}{}\n<'.format(ensure_nl, o)
elif is_inline(n):
text = doc_wrap(get_text(n), indent=indent, width=width)
@@ -671,7 +692,7 @@ def fmt_node_as_vimhelp(parent, width=text_width - indentation, indent='',
max_name_len = max_name(m.keys()) + 4
out = ''
for name, desc in m.items():
- name = ' {}'.format('{{{}}}'.format(name).ljust(max_name_len))
+ name = ' • {}'.format('{{{}}}'.format(name).ljust(max_name_len))
out += '{}{}\n'.format(name, desc)
return out.rstrip()
@@ -801,7 +822,8 @@ def extract_from_xml(filename, target, width, fmt_vimhelp):
prefix = '%s(' % name
suffix = '%s)' % ', '.join('{%s}' % a[1] for a in params
- if a[0] not in ('void', 'Error'))
+ if a[0] not in ('void', 'Error', 'Arena',
+ 'lua_State'))
if not fmt_vimhelp:
c_decl = '%s %s(%s);' % (return_type, name, ', '.join(c_args))
@@ -887,7 +909,7 @@ def extract_from_xml(filename, target, width, fmt_vimhelp):
def fmt_doxygen_xml_as_vimhelp(filename, target):
"""Entrypoint for generating Vim :help from from Doxygen XML.
- Returns 3 items:
+ Returns 2 items:
1. Vim help text for functions found in `filename`.
2. Vim help text for deprecated functions.
"""
@@ -951,7 +973,7 @@ def fmt_doxygen_xml_as_vimhelp(filename, target):
start = end
- func_doc = "\n".join(split_lines)
+ func_doc = "\n".join(map(align_tags, split_lines))
if (name.startswith(CONFIG[target]['fn_name_prefix'])
and name != "nvim_error_event"):
@@ -979,7 +1001,7 @@ def delete_lines_below(filename, tokenstr):
fp.writelines(lines[0:i])
-def main(config, args):
+def main(doxygen_config, args):
"""Generates:
1. Vim :help docs
@@ -1007,7 +1029,7 @@ def main(config, args):
# runtime/lua/vim/lsp.lua:209: warning: argument 'foo' not found
stderr=(subprocess.STDOUT if debug else subprocess.DEVNULL))
p.communicate(
- config.format(
+ doxygen_config.format(
input=' '.join(
[f'"{file}"' for file in CONFIG[target]['files']]),
output=output_dir,
@@ -1094,7 +1116,7 @@ def main(config, args):
fn_map_full.update(fn_map)
if len(sections) == 0:
- fail(f'no sections for target: {target}')
+ fail(f'no sections for target: {target} (look for errors near "Preprocessing" log lines above)')
if len(sections) > len(CONFIG[target]['section_order']):
raise RuntimeError(
'found new modules "{}"; update the "section_order" map'.format(
@@ -1141,7 +1163,7 @@ def main(config, args):
def filter_source(filename):
name, extension = os.path.splitext(filename)
if extension == '.lua':
- p = subprocess.run([lua2dox_filter, filename], stdout=subprocess.PIPE)
+ p = subprocess.run(['nvim', '-l', lua2dox, filename], stdout=subprocess.PIPE)
op = ('?' if 0 != p.returncode else p.stdout.decode('utf-8'))
print(op)
else:
diff --git a/scripts/genappimage.sh b/scripts/genappimage.sh
index cc88ab5559..9944b5eb31 100755
--- a/scripts/genappimage.sh
+++ b/scripts/genappimage.sh
@@ -8,7 +8,8 @@
# App arch, used by generate_appimage.
if [ -z "$ARCH" ]; then
- export ARCH="$(arch)"
+ ARCH="$(arch)"
+ export ARCH
fi
TAG=$1
@@ -34,8 +35,9 @@ make install
# App version, used by generate_appimage.
VERSION=$("$ROOT_DIR"/build/bin/nvim --version | head -n 1 | grep -o 'v.*')
+export VERSION
-cd "$APP_BUILD_DIR"
+cd "$APP_BUILD_DIR" || exit
# Only downloads linuxdeploy if the remote file is different from local
if [ -e "$APP_BUILD_DIR"/linuxdeploy-x86_64.AppImage ]; then
@@ -53,7 +55,7 @@ chmod +x "$APP_BUILD_DIR"/linuxdeploy-x86_64.AppImage
mkdir "$APP_DIR/usr/share/metainfo/"
cp "$ROOT_DIR/runtime/nvim.appdata.xml" "$APP_DIR/usr/share/metainfo/"
-cd "$APP_DIR"
+cd "$APP_DIR" || exit
########################################################################
# AppDir complete. Now package it as an AppImage.
@@ -71,7 +73,7 @@ exec "$(dirname "$(readlink -f "${0}")")/usr/bin/nvim" ${@+"$@"}
EOF
chmod 755 AppRun
-cd "$APP_BUILD_DIR" # Get out of AppImage directory.
+cd "$APP_BUILD_DIR" || exit # Get out of AppImage directory.
# Set the name of the file generated by appimage
export OUTPUT=nvim.appimage
@@ -85,7 +87,7 @@ fi
# - Expects: $ARCH, $APP, $VERSION env vars
# - Expects: ./$APP.AppDir/ directory
# - Produces: ./nvim.appimage
-./linuxdeploy-x86_64.AppImage --appdir $APP.AppDir -d $ROOT_DIR/runtime/nvim.desktop -i \
+./linuxdeploy-x86_64.AppImage --appdir $APP.AppDir -d "$ROOT_DIR"/runtime/nvim.desktop -i \
"$ROOT_DIR/runtime/nvim.png" --output appimage
# Moving the final executable to a different folder so it isn't in the
diff --git a/scripts/genvimvim.lua b/scripts/genvimvim.lua
index 868084a583..3e9e7077be 100644
--- a/scripts/genvimvim.lua
+++ b/scripts/genvimvim.lua
@@ -11,6 +11,8 @@ local funcs_file = arg[3]
package.path = nvimsrcdir .. '/?.lua;' .. package.path
+_G.vim = loadfile(nvimsrcdir..'/../../runtime/lua/vim/shared.lua')()
+
local lld = {}
local syn_fd = io.open(syntax_file, 'w')
lld.line_length = 0
@@ -115,7 +117,7 @@ end
local nvimau_start = 'syn keyword nvimAutoEvent contained '
w('\n\n' .. nvimau_start)
-for au, _ in pairs(auevents.nvim_specific) do
+for au, _ in vim.spairs(auevents.nvim_specific) do
if lld.line_length > 850 then
w('\n' .. nvimau_start)
end
@@ -126,7 +128,7 @@ w('\n\nsyn case match')
local vimfun_start = 'syn keyword vimFuncName contained '
w('\n\n' .. vimfun_start)
local funcs = mpack.unpack(io.open(funcs_file, 'rb'):read("*all"))
-for name, _ in pairs(funcs) do
+for _, name in ipairs(funcs) do
if name then
if lld.line_length > 850 then
w('\n' .. vimfun_start)
diff --git a/scripts/git-log-pretty-since.sh b/scripts/git-log-pretty-since.sh
index a0aa4354b6..95dcee23f5 100755
--- a/scripts/git-log-pretty-since.sh
+++ b/scripts/git-log-pretty-since.sh
@@ -16,9 +16,9 @@ __SINCE=$1
__INVMATCH=$2
is_merge_commit() {
- git rev-parse $1 >/dev/null 2>&1 \
+ git rev-parse "$1" >/dev/null 2>&1 \
|| { echo "ERROR: invalid commit: $1"; exit 1; }
- git log $1^2 >/dev/null 2>&1 && return 0 || return 1
+ git log "$1"^2 >/dev/null 2>&1 && return 0 || return 1
}
# Removes parens from issue/ticket/PR numbers.
@@ -40,13 +40,13 @@ _format_ticketnums() {
}
for commit in $(git log --format='%H' --first-parent "$__SINCE"..HEAD); do
- if is_merge_commit ${commit} ; then
- if [ -z "$__INVMATCH" ] || ! git log --oneline ${commit}^1..${commit}^2 \
+ if is_merge_commit "${commit}" ; then
+ if [ -z "$__INVMATCH" ] || ! git log --oneline "${commit}^1..${commit}^2" \
| >/dev/null 2>&1 grep -E "$__INVMATCH" ; then
- git log -1 --oneline ${commit}
- git log --format=' %h %s' ${commit}^1..${commit}^2
+ git log -1 --oneline "${commit}"
+ git log --format=' %h %s' "${commit}^1..${commit}^2"
fi
else
- git log -1 --oneline ${commit}
+ git log -1 --oneline "${commit}"
fi
done | _format_ticketnums
diff --git a/scripts/lintcommit.lua b/scripts/lintcommit.lua
index 16326cfe66..87a8e62503 100644
--- a/scripts/lintcommit.lua
+++ b/scripts/lintcommit.lua
@@ -86,11 +86,28 @@ local function validate_commit(commit_message)
vim.inspect(allowed_types))
end
- -- Check if scope is empty
+ -- Check if scope is appropriate
if before_colon:match("%(") then
local scope = vim.trim(before_colon:match("%((.*)%)"))
+
if scope == '' then
- return [[Scope can't be empty.]]
+ return [[Scope can't be empty]]
+ end
+
+ if vim.startswith(scope, "nvim_") then
+ return [[Scope should be "api" instead of "nvim_..."]]
+ end
+
+ local alternative_scope = {
+ ['filetype.vim'] = 'filetype',
+ ['filetype.lua'] = 'filetype',
+ ['tree-sitter'] = 'treesitter',
+ ['ts'] = 'treesitter',
+ ['hl'] = 'highlight',
+ }
+
+ if alternative_scope[scope] then
+ return ('Scope should be "%s" instead of "%s"'):format(alternative_scope[scope], scope)
end
end
diff --git a/scripts/lua2dox.lua b/scripts/lua2dox.lua
index 6a206066b8..19f8f8141d 100644
--- a/scripts/lua2dox.lua
+++ b/scripts/lua2dox.lua
@@ -27,14 +27,13 @@ http://search.cpan.org/~alec/Doxygen-Lua-0.02/lib/Doxygen/Lua.pm
Running
-------
-This file "lua2dox.lua" gets called by "lua2dox_filter" (bash).
+This script "lua2dox.lua" gets called by "gen_vimdoc.py".
Doxygen must be on your system. You can experiment like so:
- Run "doxygen -g" to create a default Doxyfile.
-- Then alter it to let it recognise lua. Add the two following lines:
+- Then alter it to let it recognise lua. Add the following line:
FILE_PATTERNS = *.lua
- FILTER_PATTERNS = *.lua=lua2dox_filter
- Then run "doxygen".
The core function reads the input file (filename or stdin) and outputs some pseudo C-ish language.
@@ -50,108 +49,46 @@ However I have put in a hack that will insert the "missing" close paren.
The effect is that you will get the function documented, but not with the parameter list you might expect.
]]
-local function class(BaseClass, ClassInitialiser)
- local newClass = {} -- a new class newClass
- if not ClassInitialiser and type(BaseClass) == 'function' then
- ClassInitialiser = BaseClass
- BaseClass = nil
- elseif type(BaseClass) == 'table' then
- -- our new class is a shallow copy of the base class!
- for i,v in pairs(BaseClass) do
- newClass[i] = v
- end
- newClass._base = BaseClass
- end
+local function class()
+ local newClass = {} -- a new class newClass
-- the class will be the metatable for all its newInstanceects,
-- and they will look up their methods in it.
newClass.__index = newClass
-- expose a constructor which can be called by <classname>(<args>)
- local classMetatable = {}
- classMetatable.__call = function(class_tbl, ...)
- local newInstance = {}
- setmetatable(newInstance,newClass)
- --if init then
- -- init(newInstance,...)
- if class_tbl.init then
- class_tbl.init(newInstance,...)
- else
- -- make sure that any stuff from the base class is initialized!
- if BaseClass and BaseClass.init then
- BaseClass.init(newInstance, ...)
+ setmetatable(newClass, {
+ __call = function(class_tbl, ...)
+ local newInstance = {}
+ setmetatable(newInstance, newClass)
+ --if init then
+ -- init(newInstance,...)
+ if class_tbl.init then
+ class_tbl.init(newInstance, ...)
end
+ return newInstance
end
- return newInstance
- end
- newClass.init = ClassInitialiser
- newClass.is_a = function(this, klass)
- local thisMetatable = getmetatable(this)
- while thisMetatable do
- if thisMetatable == klass then
- return true
- end
- thisMetatable = thisMetatable._base
- end
- return false
- end
- setmetatable(newClass, classMetatable)
+ })
return newClass
end
---! \class TCore_Clock
---! \brief a clock
-local TCore_Clock = class()
-
---! \brief get the current time
-function TCore_Clock.GetTimeNow()
- local gettimeofday = os.gettimeofday -- luacheck: ignore 143 Accessing an undefined field of a global variable.
- if gettimeofday then
- return gettimeofday()
- else
- return os.time()
- end
-end
-
---! \brief constructor
-function TCore_Clock.init(this,T0)
- if T0 then
- this.t0 = T0
- else
- this.t0 = TCore_Clock.GetTimeNow()
- end
-end
-
---! \brief get time string
-function TCore_Clock.getTimeStamp(this,T0)
- local t0
- if T0 then
- t0 = T0
- else
- t0 = this.t0
- end
- return os.date('%c %Z',t0)
-end
-
-
--! \brief write to stdout
local function TCore_IO_write(Str)
- if (Str) then
+ if Str then
io.write(Str)
end
end
--! \brief write to stdout
local function TCore_IO_writeln(Str)
- if (Str) then
+ if Str then
io.write(Str)
end
- io.write("\n")
+ io.write('\n')
end
-
--! \brief trims a string
local function string_trim(Str)
- return Str:match("^%s*(.-)%s*$")
+ return Str:match('^%s*(.-)%s*$')
end
--! \brief split a string
@@ -159,46 +96,26 @@ end
--! \param Str
--! \param Pattern
--! \returns table of string fragments
+---@return string[]
local function string_split(Str, Pattern)
local splitStr = {}
- local fpat = "(.-)" .. Pattern
+ local fpat = '(.-)' .. Pattern
local last_end = 1
- local str, e, cap = string.find(Str,fpat, 1)
+ local str, e, cap = string.find(Str, fpat, 1)
while str do
- if str ~= 1 or cap ~= "" then
- table.insert(splitStr,cap)
+ if str ~= 1 or cap ~= '' then
+ table.insert(splitStr, cap)
end
- last_end = e+1
- str, e, cap = string.find(Str,fpat, last_end)
+ last_end = e + 1
+ str, e, cap = string.find(Str, fpat, last_end)
end
if last_end <= #Str then
- cap = string.sub(Str,last_end)
+ cap = string.sub(Str, last_end)
table.insert(splitStr, cap)
end
return splitStr
end
-
---! \class TCore_Commandline
---! \brief reads/parses commandline
-local TCore_Commandline = class()
-
---! \brief constructor
-function TCore_Commandline.init(this)
- this.argv = arg
- this.parsed = {}
- this.params = {}
-end
-
---! \brief get value
-function TCore_Commandline.getRaw(this,Key,Default)
- local val = this.argv[Key]
- if not val then
- val = Default
- end
- return val
-end
-
-------------------------------
--! \brief file buffer
--!
@@ -208,14 +125,13 @@ local TStream_Read = class()
--! \brief get contents of file
--!
--! \param Filename name of file to read (or nil == stdin)
-function TStream_Read.getContents(this,Filename)
- assert(Filename)
+function TStream_Read.getContents(this, Filename)
+ assert(Filename, ('invalid file: %s'):format(Filename))
-- get lines from file
-- syphon lines to our table
- --TCore_Debug_show_var('Filename',Filename)
- local filecontents={}
+ local filecontents = {}
for line in io.lines(Filename) do
- table.insert(filecontents,line)
+ table.insert(filecontents, line)
end
if filecontents then
@@ -240,7 +156,7 @@ function TStream_Read.getLine(this)
this.currentLine = nil
else
-- get line
- if this.currentLineNo<=this.contentsLen then
+ if this.currentLineNo <= this.contentsLen then
line = this.filecontents[this.currentLineNo]
this.currentLineNo = this.currentLineNo + 1
else
@@ -251,13 +167,13 @@ function TStream_Read.getLine(this)
end
--! \brief save line fragment
-function TStream_Read.ungetLine(this,LineFrag)
+function TStream_Read.ungetLine(this, LineFrag)
this.currentLine = LineFrag
end
--! \brief is it eof?
function TStream_Read.eof(this)
- if this.currentLine or this.currentLineNo<=this.contentsLen then
+ if this.currentLine or this.currentLineNo <= this.contentsLen then
return false
end
return true
@@ -272,32 +188,32 @@ function TStream_Write.init(this)
end
--! \brief write immediately
-function TStream_Write.write(_,Str)
+function TStream_Write.write(_, Str)
TCore_IO_write(Str)
end
--! \brief write immediately
-function TStream_Write.writeln(_,Str)
+function TStream_Write.writeln(_, Str)
TCore_IO_writeln(Str)
end
--! \brief write immediately
-function TStream_Write.writelnComment(_,Str)
+function TStream_Write.writelnComment(_, Str)
TCore_IO_write('// ZZ: ')
TCore_IO_writeln(Str)
end
--! \brief write to tail
-function TStream_Write.writelnTail(this,Line)
+function TStream_Write.writelnTail(this, Line)
if not Line then
Line = ''
end
- table.insert(this.tailLine,Line)
+ table.insert(this.tailLine, Line)
end
--! \brief output tail lines
function TStream_Write.write_tailLines(this)
- for _,line in ipairs(this.tailLine) do
+ for _, line in ipairs(this.tailLine) do
TCore_IO_writeln(line)
end
TCore_IO_write('// Lua2DoX new eof')
@@ -307,9 +223,9 @@ end
local TLua2DoX_filter = class()
--! \brief allow us to do errormessages
-function TLua2DoX_filter.warning(this,Line,LineNo,Legend)
+function TLua2DoX_filter.warning(this, Line, LineNo, Legend)
this.outStream:writelnTail(
- '//! \todo warning! ' .. Legend .. ' (@' .. LineNo .. ')"' .. Line .. '"'
+ '//! \todo warning! ' .. Legend .. ' (@' .. LineNo .. ')"' .. Line .. '"'
)
end
@@ -318,47 +234,47 @@ end
--! If the string has a comment on the end, this trims it off.
--!
local function TString_removeCommentFromLine(Line)
- local pos_comment = string.find(Line,'%-%-')
+ local pos_comment = string.find(Line, '%-%-')
local tailComment
if pos_comment then
- Line = string.sub(Line,1,pos_comment-1)
- tailComment = string.sub(Line,pos_comment)
+ Line = string.sub(Line, 1, pos_comment - 1)
+ tailComment = string.sub(Line, pos_comment)
end
- return Line,tailComment
+ return Line, tailComment
end
--! \brief get directive from magic
local function getMagicDirective(Line)
- local macro,tail
+ local macro, tail
local macroStr = '[\\@]'
- local pos_macro = string.find(Line,macroStr)
+ local pos_macro = string.find(Line, macroStr)
if pos_macro then
--! ....\\ macro...stuff
--! ....\@ macro...stuff
- local line = string.sub(Line,pos_macro+1)
- local space = string.find(line,'%s+')
+ local line = string.sub(Line, pos_macro + 1)
+ local space = string.find(line, '%s+')
if space then
- macro = string.sub(line,1,space-1)
- tail = string_trim(string.sub(line,space+1))
+ macro = string.sub(line, 1, space - 1)
+ tail = string_trim(string.sub(line, space + 1))
else
macro = line
- tail = ''
+ tail = ''
end
end
- return macro,tail
+ return macro, tail
end
--! \brief check comment for fn
-local function checkComment4fn(Fn_magic,MagicLines)
+local function checkComment4fn(Fn_magic, MagicLines)
local fn_magic = Fn_magic
-- TCore_IO_writeln('// checkComment4fn "' .. MagicLines .. '"')
- local magicLines = string_split(MagicLines,'\n')
+ local magicLines = string_split(MagicLines, '\n')
- local macro,tail
+ local macro, tail
for _, line in ipairs(magicLines) do
- macro,tail = getMagicDirective(line)
+ macro, tail = getMagicDirective(line)
if macro == 'fn' then
fn_magic = tail
-- TCore_IO_writeln('// found fn "' .. fn_magic .. '"')
@@ -369,13 +285,16 @@ local function checkComment4fn(Fn_magic,MagicLines)
return fn_magic
end
+
+local types = { 'number', 'string', 'table', 'list', 'boolean', 'function' }
+
--! \brief run the filter
-function TLua2DoX_filter.readfile(this,AppStamp,Filename)
+function TLua2DoX_filter.readfile(this, AppStamp, Filename)
local inStream = TStream_Read()
local outStream = TStream_Write()
this.outStream = outStream -- save to this obj
- if (inStream:getContents(Filename)) then
+ if inStream:getContents(Filename) then
-- output the file
local line
local fn_magic -- function name/def from magic comment
@@ -385,13 +304,14 @@ function TLua2DoX_filter.readfile(this,AppStamp,Filename)
outStream:writelnTail('// #######################')
outStream:writelnTail()
- local state = '' -- luacheck: ignore 231 variable is set but never accessed.
+ local state = '' -- luacheck: ignore 231 variable is set but never accessed.
local offset = 0
+ local generic = {}
+ local l = 0
while not (inStream:eof()) do
line = string_trim(inStream:getLine())
- -- TCore_Debug_show_var('inStream',inStream)
- -- TCore_Debug_show_var('line',line )
- if string.sub(line,1,2) == '--' then -- it's a comment
+ l = l + 1
+ if string.sub(line, 1, 2) == '--' then -- it's a comment
-- Allow people to write style similar to EmmyLua (since they are basically the same)
-- instead of silently skipping things that start with ---
if string.sub(line, 3, 3) == '@' then -- it's a magic comment
@@ -405,55 +325,87 @@ function TLua2DoX_filter.readfile(this,AppStamp,Filename)
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
+ for _, type in ipairs(types) do
+ magic = magic:gsub('^param%s+([a-zA-Z_?]+)%s+.*%((' .. type .. ')%)', 'param %1 %2')
+ magic =
+ magic:gsub('^param%s+([a-zA-Z_?]+)%s+.*%((' .. type .. '|nil)%)', 'param %1 %2')
+ end
+ magic_split = string_split(magic, ' ')
+ elseif magic_split[1] == 'return' then
+ for _, type in ipairs(types) do
+ magic = magic:gsub('^return%s+.*%((' .. type .. ')%)', 'return %1')
+ magic = magic:gsub('^return%s+.*%((' .. type .. '|nil)%)', 'return %1')
+ end
+ magic_split = string_split(magic, ' ')
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] .. ')'
+ if magic_split[1] == 'generic' then
+ local generic_name, generic_type = line:match('@generic%s*(%w+)%s*:?%s*(.*)')
+ if generic_type == '' then
+ generic_type = 'any'
+ end
+ generic[generic_name] = generic_type
+ else
+ local type_index = 2
+ if magic_split[1] == 'param' then
+ type_index = type_index + 1
+ end
+
+ if magic_split[type_index] then
+ -- fix optional parameters
+ if magic_split[type_index] and magic_split[2]:find('%?$') then
+ if not magic_split[type_index]:find('nil') then
+ magic_split[type_index] = magic_split[type_index] .. '|nil'
+ end
+ magic_split[2] = magic_split[2]:sub(1, -2)
+ end
+ -- replace generic types
+ if magic_split[type_index] then
+ for k, v in pairs(generic) do
+ magic_split[type_index] = magic_split[type_index]:gsub(k, v)
+ end
+ end
+ -- surround some types by ()
+ for _, type in ipairs(types) do
+ magic_split[type_index] =
+ magic_split[type_index]:gsub('^(' .. type .. '|nil):?$', '(%1)')
+ magic_split[type_index] =
+ magic_split[type_index]:gsub('^(' .. type .. '):?$', '(%1)')
+ end
+ end
+
+ magic = table.concat(magic_split, ' ')
+
+ outStream:writeln('/// @' .. magic)
+ fn_magic = checkComment4fn(fn_magic, magic)
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
- local comment = string.sub(line,4)
- outStream:writeln('/// '.. comment)
- elseif string.sub(line,3,4)=='[[' then -- it's a long comment
- line = string.sub(line,5) -- nibble head
+ elseif string.sub(line, 3, 3) == '-' then -- it's a nonmagic doc comment
+ local comment = string.sub(line, 4)
+ outStream:writeln('/// ' .. comment)
+ elseif string.sub(line, 3, 4) == '[[' then -- it's a long comment
+ line = string.sub(line, 5) -- nibble head
local comment = ''
- local closeSquare,hitend,thisComment
- while (not hitend) and (not inStream:eof()) do
- closeSquare = string.find(line,']]')
+ local closeSquare, hitend, thisComment
+ while not hitend and (not inStream:eof()) do
+ closeSquare = string.find(line, ']]')
if not closeSquare then -- need to look on another line
thisComment = line .. '\n'
line = inStream:getLine()
else
- thisComment = string.sub(line,1,closeSquare-1)
+ thisComment = string.sub(line, 1, closeSquare - 1)
hitend = true
-- unget the tail of the line
-- in most cases it's empty. This may make us less efficient but
-- easier to program
- inStream:ungetLine(string_trim(string.sub(line,closeSquare+2)))
+ inStream:ungetLine(string_trim(string.sub(line, closeSquare + 2)))
end
comment = comment .. thisComment
end
- if string.sub(comment,1,1)=='@' then -- it's a long magic comment
+ if string.sub(comment, 1, 1) == '@' then -- it's a long magic comment
outStream:write('/*' .. comment .. '*/ ')
- fn_magic = checkComment4fn(fn_magic,comment)
+ fn_magic = checkComment4fn(fn_magic, comment)
else -- discard
outStream:write('/* zz:' .. comment .. '*/ ')
fn_magic = nil
@@ -467,76 +419,74 @@ function TLua2DoX_filter.readfile(this,AppStamp,Filename)
fn_magic = nil
end
elseif string.find(line, '^function') or string.find(line, '^local%s+function') then
- state = 'in_function' -- it's a function
- local pos_fn = string.find(line,'function')
+ generic = {}
+ state = 'in_function' -- it's a function
+ local pos_fn = string.find(line, 'function')
-- function
-- ....v...
if pos_fn then
-- we've got a function
- local fn_type
- if string.find(line,'^local%s+') then
- fn_type = ''--'static ' -- static functions seem to be excluded
- else
- fn_type = ''
- end
- local fn = TString_removeCommentFromLine(string_trim(string.sub(line,pos_fn+8)))
+ local fn = TString_removeCommentFromLine(string_trim(string.sub(line, pos_fn + 8)))
if fn_magic then
fn = fn_magic
end
- if string.sub(fn,1,1)=='(' then
+ if string.sub(fn, 1, 1) == '(' then
-- it's an anonymous function
outStream:writelnComment(line)
else
-- fn has a name, so is interesting
-- want to fix for iffy declarations
- local open_paren = string.find(fn,'[%({]')
+ local open_paren = string.find(fn, '[%({]')
if open_paren then
-- we might have a missing close paren
- if not string.find(fn,'%)') then
+ if not string.find(fn, '%)') then
fn = fn .. ' ___MissingCloseParenHere___)'
end
end
-- Big hax
- if string.find(fn, ":") then
+ if string.find(fn, ':') then
-- TODO: We need to add a first parameter of "SELF" here
-- local colon_place = string.find(fn, ":")
-- local name = string.sub(fn, 1, colon_place)
- fn = fn:gsub(":", ".", 1)
- outStream:writeln("/// @param self")
+ fn = fn:gsub(':', '.', 1)
+ outStream:writeln('/// @param self')
- local paren_start = string.find(fn, "(", 1, true)
- local paren_finish = string.find(fn, ")", 1, true)
+ local paren_start = string.find(fn, '(', 1, true)
+ local paren_finish = string.find(fn, ')', 1, true)
-- Nothing in between the parens
local comma
if paren_finish == paren_start + 1 then
- comma = ""
+ comma = ''
else
- comma = ", "
+ comma = ', '
end
- fn = string.sub(fn, 1, paren_start) .. "self" .. comma .. string.sub(fn, paren_start + 1)
+ fn = string.sub(fn, 1, paren_start)
+ .. 'self'
+ .. comma
+ .. string.sub(fn, paren_start + 1)
end
-- add vanilla function
- outStream:writeln(fn_type .. 'function ' .. fn .. '{}')
+ outStream:writeln('function ' .. fn .. '{}')
end
else
- this:warning(inStream:getLineNo(),'something weird here')
+ this:warning(inStream:getLineNo(), 'something weird here')
end
- fn_magic = nil -- mustn't indavertently use it again
+ fn_magic = nil -- mustn't inadvertently use it again
-- TODO: If we can make this learn how to generate these, that would be helpful.
-- elseif string.find(line, "^M%['.*'%] = function") then
-- state = 'in_function' -- it's a function
-- outStream:writeln("function textDocument/publishDiagnostics(...){}")
- -- fn_magic = nil -- mustn't indavertently use it again
+ -- fn_magic = nil -- mustn't inadvertently use it again
else
- state = '' -- unknown
- if #line>0 then -- we don't know what this line means, so just comment it out
+ state = '' -- unknown
+ if #line > 0 then -- we don't know what this line means, so just comment it out
outStream:writeln('// zz: ' .. line)
else
outStream:writeln() -- keep this line blank
@@ -556,16 +506,14 @@ local TApp = class()
--! \brief constructor
function TApp.init(this)
- local t0 = TCore_Clock()
- this.timestamp = t0:getTimeStamp()
+ this.timestamp = os.date('%c %Z', os.time())
this.name = 'Lua2DoX'
this.version = '0.2 20130128'
this.copyright = 'Copyright (c) Simon Dales 2012-13'
end
function TApp.getRunStamp(this)
- return this.name .. ' (' .. this.version .. ') '
- .. this.timestamp
+ return this.name .. ' (' .. this.version .. ') ' .. this.timestamp
end
function TApp.getVersion(this)
@@ -579,15 +527,14 @@ end
local This_app = TApp()
--main
-local cl = TCore_Commandline()
-local argv1 = cl:getRaw(2)
+local argv1 = arg[1]
if argv1 == '--help' then
TCore_IO_writeln(This_app:getVersion())
TCore_IO_writeln(This_app:getCopyright())
TCore_IO_writeln([[
run as:
- lua2dox_filter <param>
+ nvim -l scripts/lua2dox.lua <param>
--------------
Param:
<filename> : interprets filename
@@ -602,8 +549,7 @@ else
local filename = argv1
local filter = TLua2DoX_filter()
- filter:readfile(appStamp,filename)
+ filter:readfile(appStamp, filename)
end
-
--eof
diff --git a/scripts/lua2dox_filter b/scripts/lua2dox_filter
deleted file mode 100755
index 22484a807f..0000000000
--- a/scripts/lua2dox_filter
+++ /dev/null
@@ -1,88 +0,0 @@
-#!/usr/bin/env bash
-
-###########################################################################
-# Copyright (C) 2012 by Simon Dales #
-# simon@purrsoft.co.uk #
-# #
-# This program is free software; you can redistribute it and/or modify #
-# it under the terms of the GNU General Public License as published by #
-# the Free Software Foundation; either version 2 of the License, or #
-# (at your option) any later version. #
-# #
-# This program is distributed in the hope that it will be useful, #
-# but WITHOUT ANY WARRANTY; without even the implied warranty of #
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
-# GNU General Public License for more details. #
-# #
-# You should have received a copy of the GNU General Public License #
-# along with this program; if not, write to the #
-# Free Software Foundation, Inc., #
-# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #
-###########################################################################
-LANG=""
-
-##! \brief test executable to see if it exists
-test_executable(){
- P_EXE="$1"
- #########
- WHICH=`which ${P_EXE}`
- if test -z "${WHICH}"
- then
- echo "not found \"${P_EXE}\""
- else
- EXE="${P_EXE}"
- fi
- }
-
-##! \brief sets the lua interpreter
-set_lua(){
- if test -z "${EXE}"; then
- test_executable 'luajit'
- fi
-
- if test -z "${EXE}"; then
- test_executable 'lua'
- fi
-}
-
-##! \brief makes canonical name of file
-##!
-##! Note that "readlink -f" doesn't work in MacOSX
-##!
-do_readlink(){
- pushd . > /dev/null
- TARGET_FILE=$1
-
- cd `dirname $TARGET_FILE`
- TARGET_FILE=`basename $TARGET_FILE`
-
- # Iterate down a (possible) chain of symlinks
- while [ -L "$TARGET_FILE" ]
- do
- TARGET_FILE=`readlink $TARGET_FILE`
- cd `dirname $TARGET_FILE`
- TARGET_FILE=`basename $TARGET_FILE`
- done
-
- PHYS_DIR=`pwd -P`
- RESULT=$PHYS_DIR
- popd > /dev/null
- }
-
-##main
-set_lua
-if test -z "${EXE}"
-then
- echo "no lua interpreter available"
-else
- BASENAME=`basename "$0"`
- do_readlink "$0"
- DIRNAME="${RESULT}"
-
- LUASCRIPT="${DIRNAME}/lua2dox.lua ${BASENAME}"
- #echo "lua[${LUASCRIPT}]"
-
- ${EXE} ${LUASCRIPT} $@
-fi
-#
-##eof
diff --git a/scripts/pvscheck.sh b/scripts/pvscheck.sh
index 610c20eb48..97757c0848 100755
--- a/scripts/pvscheck.sh
+++ b/scripts/pvscheck.sh
@@ -328,7 +328,7 @@ realdir() {(
patch_sources() {(
local tgt="$1" ; shift
- local only_bulid="${1}" ; shift
+ local only_build="${1}" ; shift
get_pvs_comment "$tgt"
diff --git a/scripts/release.sh b/scripts/release.sh
index 380503662d..4321d96f62 100755
--- a/scripts/release.sh
+++ b/scripts/release.sh
@@ -59,8 +59,8 @@ _do_release_commit() {
$__sed -i.bk 's/(NVIM_VERSION_PRERELEASE) "-dev"/\1 ""/' CMakeLists.txt
if grep '(NVIM_API_PRERELEASE true)' CMakeLists.txt > /dev/null; then
$__sed -i.bk 's/(NVIM_API_PRERELEASE) true/\1 false/' CMakeLists.txt
- build/bin/nvim --api-info > test/functional/fixtures/api_level_$__API_LEVEL.mpack
- git add test/functional/fixtures/api_level_$__API_LEVEL.mpack
+ build/bin/nvim --api-info > "test/functional/fixtures/api_level_$__API_LEVEL.mpack"
+ git add "test/functional/fixtures/api_level_${__API_LEVEL}.mpack"
fi
$__sed -i.bk 's,(<releases>),\1\
diff --git a/scripts/update_terminfo.sh b/scripts/update_terminfo.sh
index 8a0937cc8c..775048f246 100755
--- a/scripts/update_terminfo.sh
+++ b/scripts/update_terminfo.sh
@@ -35,7 +35,7 @@ readonly -A entries=(
db="$(mktemp -du)"
print_bold() {
- printf "\\e[1m$*\\e[0m"
+ printf "\\e[1m%b\\e[0m" "$*"
}
cd "$(git rev-parse --show-toplevel)"
diff --git a/scripts/vim-patch.sh b/scripts/vim-patch.sh
index ad1973603e..f9f7330097 100755
--- a/scripts/vim-patch.sh
+++ b/scripts/vim-patch.sh
@@ -35,7 +35,7 @@ usage() {
echo " -m {vim-revision} List previous (older) missing Vim patches."
echo " -M List all merged patch-numbers (at current v:version)."
echo " -p {vim-revision} Download and generate a Vim patch. vim-revision"
- echo " can be a Vim version (8.0.xxx) or a Git hash."
+ echo " can be a Vim version (8.1.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 [pr args] Create a vim-patch pull request."
@@ -120,9 +120,9 @@ get_vim_sources() {
commit_message() {
if [[ -n "$vim_tag" ]]; then
- printf '%s\n%s' "${vim_message}" "${vim_commit_url}"
+ printf '%s\n\n%s\n\n%s' "${vim_message}" "${vim_commit_url}" "${vim_coauthor}"
else
- printf 'vim-patch:%s\n\n%s\n%s' "$vim_version" "$vim_message" "$vim_commit_url"
+ printf 'vim-patch:%s\n\n%s\n\n%s\n\n%s' "$vim_version" "$vim_message" "$vim_commit_url" "$vim_coauthor"
fi
}
@@ -175,6 +175,7 @@ assign_commit_details() {
vim_commit_url="https://github.com/vim/vim/commit/${vim_commit}"
vim_message="$(git -C "${VIM_SOURCE_DIR}" log -1 --pretty='format:%B' "${vim_commit}" \
| sed -e 's/\(#[0-9]\{1,\}\)/vim\/vim\1/g')"
+ vim_coauthor="$(git -C "${VIM_SOURCE_DIR}" log -1 --pretty='format:Co-authored-by: %an <%ae>' "${vim_commit}")"
if [[ ${munge_commit_line} == "true" ]]; then
# Remove first line of commit message.
vim_message="$(echo "${vim_message}" | sed -e '1s/^patch /vim-patch:/')"
@@ -192,11 +193,15 @@ 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'
+ local na_src='auto\|configure.*\|GvimExt\|hardcopy.*\|libvterm\|proto\|tee\|VisVim\|xpm\|xxd\|Make.*\|INSTALL.*\|beval.*\|gui.*\|if_cscop\|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 runtime files ported to Lua.
+ local na_rt='filetype\.vim\|scripts\.vim\|autoload\/ft\/dist\.vim\|print\/.*'
+ 2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/runtime/\<\%('"${na_rt}"'\)\>@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'
+ local na_doc='channel\.txt\|if_cscop\.txt\|netbeans\.txt\|os_\w\+\.txt\|print\.txt\|term\.txt\|todo\.txt\|version\d\.txt\|vim9\.txt\|sponsor\.txt\|intro\.txt\|tags'
2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/runtime/doc/\<\%('"${na_doc}"'\)\>@norm! d/\v(^diff)|%$ ' +w +q "$file"
# Remove "Last change ..." changes in doc files.
@@ -207,7 +212,7 @@ preprocess_patch() {
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\|cscope\.vim\|gui.*\|hardcopy\.vim\|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
@@ -237,6 +242,14 @@ 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 evalbuffer.c to eval/buffer.c
+ LC_ALL=C sed -e 's/\( [ab]\/src\/nvim\)\/evalbuffer\.c/\1\/eval\/buffer\.c/g' \
+ "$file" > "$file".tmp && mv "$file".tmp "$file"
+
+ # Rename evalwindow.c to eval/window.c
+ LC_ALL=C sed -e 's/\( [ab]\/src\/nvim\)\/evalwindow\.c/\1\/eval\/window\.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"
@@ -274,6 +287,35 @@ preprocess_patch() {
"$file" > "$file".tmp && mv "$file".tmp "$file"
}
+uncrustify_patch() {
+ git diff --quiet || {
+ >&2 echo 'Vim source working tree dirty, aborting.'
+ exit 1
+ }
+
+ local patch_path=$NVIM_SOURCE_DIR/build/vim_patch
+ rm -rf "$patch_path"
+ mkdir -p "$patch_path"/{a,b}
+
+ local commit="$1"
+ for file in $(git diff-tree --name-only --no-commit-id -r --diff-filter=a "$commit"); do
+ git --work-tree="$patch_path"/a checkout --quiet "$commit"~ -- "$file"
+ done
+ for file in $(git diff-tree --name-only --no-commit-id -r --diff-filter=d "$commit"); do
+ git --work-tree="$patch_path"/b checkout --quiet "$commit" -- "$file"
+ done
+ git reset --quiet --hard HEAD
+
+ # If the difference are drastic enough uncrustify may need to be used more
+ # than once. This is obviously a bug that needs to be fixed on uncrustify's
+ # end, but in the meantime this workaround is sufficient.
+ for _ in {1..2}; do
+ uncrustify -c "$NVIM_SOURCE_DIR"/src/uncrustify.cfg -q --replace --no-backup "$patch_path"/{a,b}/src/*.[ch]
+ done
+
+ (cd "$patch_path" && (git --no-pager diff --no-index --no-prefix --patch --unified=5 --color=never a/ b/ || true))
+}
+
get_vimpatch() {
get_vim_sources
@@ -282,7 +324,11 @@ get_vimpatch() {
msg_ok "Found Vim revision '${vim_commit}'."
local patch_content
- patch_content="$(git --no-pager show --unified=5 --color=never -1 --pretty=medium "${vim_commit}")"
+ if check_executable uncrustify; then
+ patch_content="$(uncrustify_patch "${vim_commit}")"
+ else
+ patch_content="$(git --no-pager show --unified=5 --color=never -1 --pretty=medium "${vim_commit}")"
+ fi
cd "${NVIM_SOURCE_DIR}"
@@ -466,7 +512,7 @@ submit_pr() {
# Gets all Vim commits since the "start" commit.
list_vim_commits() { (
- cd "${VIM_SOURCE_DIR}" && git log --reverse v8.0.0000..HEAD "$@"
+ cd "${VIM_SOURCE_DIR}" && git log --reverse v8.1.0000..HEAD "$@"
) }
# Prints all (sorted) "vim-patch:xxx" tokens found in the Nvim git log.
@@ -484,7 +530,7 @@ list_vimpatch_tokens() {
list_vimpatch_numbers() {
# Transform "vim-patch:X.Y.ZZZZ" to "ZZZZ".
list_vimpatch_tokens | while read -r vimpatch_token; do
- echo "$vimpatch_token" | grep '8\.0\.' | sed 's/.*vim-patch:8\.0\.\([0-9a-z]\+\).*/\1/'
+ echo "$vimpatch_token" | grep '8\.1\.' | sed -E 's/.*vim-patch:8\.1\.([0-9a-z]+).*/\1/'
done
}
@@ -581,7 +627,7 @@ _set_missing_vimpatches() {
else
info=${line#* }
if [[ -n $info ]]; then
- # Remove any "patch 8.0.0902: " prefixes, and prefix with ": ".
+ # Remove any "patch 8.1.0902: " prefixes, and prefix with ": ".
info=": ${info#patch*: }"
fi
fi
@@ -624,7 +670,7 @@ show_vimpatches() {
Instructions:
To port one of the above patches to Neovim, execute this script with the patch revision as argument and follow the instructions, e.g.
- '${BASENAME} -p v8.0.1234', or '${BASENAME} -P v8.0.1234'
+ '${BASENAME} -p v8.1.1234', or '${BASENAME} -P v8.1.1234'
NOTE: Please port the _oldest_ patch if you possibly can.
You can use '${BASENAME} -l path/to/file' to see what patches are missing for a file.
diff --git a/scripts/vimpatch.lua b/scripts/vimpatch.lua
index 11eb285462..836f672f6e 100755
--- a/scripts/vimpatch.lua
+++ b/scripts/vimpatch.lua
@@ -1,7 +1,7 @@
-- Updates version.c list of applied Vim patches.
--
-- Usage:
--- VIM_SOURCE_DIR=~/neovim/.vim-src/ nvim -i NONE -u NONE --headless +'luafile ./scripts/vimpatch.lua' +q
+-- VIM_SOURCE_DIR=~/neovim/.vim-src/ nvim -V1 -es -i NONE +'luafile ./scripts/vimpatch.lua' +q
local nvim = vim.api
@@ -22,12 +22,13 @@ end
-- Generates the lines to be inserted into the src/version.c
-- `included_patches[]` definition.
local function gen_version_c_lines()
- -- Set of merged Vim 8.0.zzzz patch numbers.
+ -- Set of merged Vim 8.1.zzzz patch numbers.
local merged_patch_numbers = {}
local highest = 0
for _, n in ipairs(vimpatch_sh_list_numbers()) do
+ n = tonumber(n)
if n then
- merged_patch_numbers[tonumber(n)] = true
+ merged_patch_numbers[n] = true
highest = math.max(highest, n)
end
end
diff --git a/src/Doxyfile b/src/Doxyfile
deleted file mode 100644
index e085e4e198..0000000000
--- a/src/Doxyfile
+++ /dev/null
@@ -1,2566 +0,0 @@
-# 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 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 (\" \").
-
-#---------------------------------------------------------------------------
-# Project related configuration options
-#---------------------------------------------------------------------------
-
-# 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
-# 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 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
-
-# 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.
-
-PROJECT_BRIEF =
-
-# 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) 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 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.
-# 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
-
-# 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, 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 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
-# description.
-# The default value is: NO.
-
-ALWAYS_DETAILED_SEC = NO
-
-# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
-# 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, 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
-
-# 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 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 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-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 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 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
-
-# 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.
-# 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.
-# Minimum value: 1, maximum value: 16, default value: 4.
-
-TAB_SIZE = 4
-
-# 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 =
-
-# 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 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. 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.
-# 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, 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 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 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);
-# 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 (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 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
-# 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
-
-# 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).
-#
-# 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,
-# namespace, or group documentation), provided this scope is documented. If set
-# 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 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
-# 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 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.
-# 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.
-# The default value is: NO.
-
-EXTRACT_PRIVATE = NO
-
-# 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.
-# 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. 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. 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 namespace
-# are hidden.
-# The default value is: NO.
-
-EXTRACT_ANON_NSPACES = NO
-
-# 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, 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
-# 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, 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 then the documentation
-# will be excluded. Set it to YES to include the internal documentation.
-# The default value is: NO.
-
-INTERNAL_DOCS = 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 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 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 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 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 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
-# 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 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 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 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.
-# 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.
-# 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 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 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 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 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.
-
-ENABLED_SECTIONS =
-
-# 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
-# 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 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 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. 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.
-#
-# 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 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
-#---------------------------------------------------------------------------
-
-# 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 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 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 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
-
-# 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
-
-# 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 standard
-# error (stderr).
-
-WARN_LOGFILE =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the input files
-#---------------------------------------------------------------------------
-
-# 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. 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 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
-
-# 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.
-
-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/*
-
-EXCLUDE_PATTERNS =
-
-# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
-# (namespaces, classes, functions, etc.) that should be excluded from the
-# 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).
-
-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 =
-
-# 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.
-# 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 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.
-#
-# 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 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 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 tag requires that the tag FILTER_SOURCE_FILES is set to YES.
-
-FILTER_SOURCE_PATTERNS =
-
-# 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 to reuse the introduction page also for the doxygen output.
-
-USE_MDFILE_AS_MAINPAGE =
-
-#---------------------------------------------------------------------------
-# 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 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,
-# classes and enums directly into the documentation.
-# The default value is: NO.
-
-INLINE_SOURCES = NO
-
-# 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
-# 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.
-# The default value is: NO.
-
-REFERENCES_RELATION = NO
-
-# 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 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 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
-#---------------------------------------------------------------------------
-
-# 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
-
-# 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
-#---------------------------------------------------------------------------
-
-# 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.
-# 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).
-# 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 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 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.
-# 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 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
-
-# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
-# 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.
-# 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
-# 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.
-# 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.
-# 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 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.
-# 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 (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
-
-# 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"
-
-# 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
-
-# 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 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 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
-
-# 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 =
-
-# 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 =
-
-# 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
-
-# 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 =
-
-# 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 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.
-# 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.
-# 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 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 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 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_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. 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 =
-
-# 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, 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. 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
-
-# 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. 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 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.
-# Minimum value: 0, maximum value: 1500, default value: 250.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-TREEVIEW_WIDTH = 250
-
-# 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
-
-# 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_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
-
-# 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. 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 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 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. 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. 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 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 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:
-# 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 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
-# 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 = 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
-#---------------------------------------------------------------------------
-
-# 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.
-# 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.
-#
-# 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.
-# 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
-
-# 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 (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 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. 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. 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_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 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, 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.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_BATCHMODE = NO
-
-# 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 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. 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
-#---------------------------------------------------------------------------
-
-# 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.
-# 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.
-# 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 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 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 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
-#---------------------------------------------------------------------------
-
-# 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. 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. 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
-
-# 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
-#---------------------------------------------------------------------------
-
-# 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.
-# The default directory is: xml.
-# This tag requires that the tag GENERATE_XML is set to YES.
-
-XML_OUTPUT = xml
-
-# 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_PROGRAMLISTING = YES
-
-# 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_NS_MEMB_FILE_SCOPE = NO
-
-#---------------------------------------------------------------------------
-# Configuration options related to the DOCBOOK output
-#---------------------------------------------------------------------------
-
-# 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.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
-# 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
-#---------------------------------------------------------------------------
-
-# 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
-#---------------------------------------------------------------------------
-
-# 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.
-# 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.
-# 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.
-# This tag requires that the tag GENERATE_PERLMOD is set to YES.
-
-PERLMOD_MAKEVAR_PREFIX =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the preprocessor
-#---------------------------------------------------------------------------
-
-# 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, 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.
-# 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 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.
-# 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.
-# 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 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.
-# 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 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 options related to external references
-#---------------------------------------------------------------------------
-
-# 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. 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. 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 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.
-# 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.
-# The default value is: YES.
-
-EXTERNAL_PAGES = YES
-
-#---------------------------------------------------------------------------
-# Configuration options related to the dot tool
-#---------------------------------------------------------------------------
-
-# 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 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.
-
-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.
-# 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 (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 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
-
-# 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 (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 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 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 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 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
-# 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. 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 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 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 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 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 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 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 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. 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.
-# 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 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).
-# 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).
-
-MSCFILE_DIRS =
-
-# 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
-# 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).
-# 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 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.
-# 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 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, 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/clint.py b/src/clint.py
index 28f6031a57..155f5f2ce5 100755
--- a/src/clint.py
+++ b/src/clint.py
@@ -1,5 +1,7 @@
#!/usr/bin/env python3
#
+# https://github.com/cpplint/cpplint
+#
# Copyright (c) 2009 Google Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -29,15 +31,9 @@
"""Lints C files in the Neovim source tree.
-The goal of this script is to identify places in the code that *may*
-be in non-compliance with Neovim style. It does not attempt to fix
-up these problems -- the point is to educate. It does also not
-attempt to find all problems, or to ensure that everything it does
-find is legitimately a problem.
-
-In particular, we can get very confused by /* and // inside strings!
-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).
+This can get very confused by /* and // inside strings! 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).
"""
@@ -61,25 +57,10 @@ Syntax: clint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...]
<file> [file] ...
The style guidelines this tries to follow are those in
- http://neovim.io/develop/style-guide.xml
-
- Note: This is Google's cpplint.py modified for use with the Neovim project,
- which follows the Google C++ coding convention except with the following
- modifications:
-
- * Function names are lower_case.
- * Struct and enum names that are not typedef-ed are struct lower_case and
- enum lower_case.
- * The opening brace for functions appear on the next line.
- * All control structures must always use braces.
-
- Neovim is a C project. As a result, for .c and .h files, the following rules
- are suppressed:
+ https://neovim.io/doc/user/dev_style.html#dev-style
- * [whitespace/braces] { should almost always be at the end of the previous
- line
- * [build/include] Include the directory when naming .h files
- * [runtime/int] Use int16_t/int64_t/etc, rather than the C type.
+ Note: This is Google's https://github.com/cpplint/cpplint modified for use
+ with the Neovim project.
Every problem is given a confidence score from 1-5, with 5 meaning we are
certain of the problem, and 1 meaning it could be a legitimate construct.
@@ -176,7 +157,6 @@ _ERROR_CATEGORIES = [
'build/printf_format',
'build/storage_class',
'readability/bool',
- 'readability/braces',
'readability/multiline_comment',
'readability/multiline_string',
'readability/nul',
@@ -202,7 +182,7 @@ _ERROR_CATEGORIES = [
'whitespace/cast',
]
-# The default state of the category filter. This is overrided by the --filter=
+# The default state of the category filter. This is overridden by the --filter=
# flag. By default all errors are on, so only add here categories that should be
# off by default (i.e., categories that must be enabled by the --filter= flags).
# All entries here should start with a '-' or '+', as in the --filter= flag.
@@ -1941,13 +1921,6 @@ def CheckExpressionAlignment(filename, clean_lines, linenum, error, startpos=0):
error(filename, linenum, 'whitespace/indent', 2,
'End of the inner expression should have '
'the same indent as start')
- else:
- if (pos != depth_line_starts[depth][0] + 4
- and not (depth_line_starts[depth][1] == '{'
- and pos == depth_line_starts[depth][0] + 2)):
- if depth not in ignore_error_levels:
- error(filename, linenum, 'whitespace/indent', 2,
- 'Inner expression indentation should be 4')
else:
if (pos != level_starts[depth][0] + 1
+ (level_starts[depth][2] == '{')):
@@ -2331,213 +2304,36 @@ def CheckBraces(filename, clean_lines, linenum, error):
prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0]
if (not Search(r'[,;:}{(]\s*$', prevline) and
not Match(r'\s*#', prevline)):
- error(filename, linenum, 'whitespace/braces', 4,
- '{ should almost always be at the end'
- ' of the previous line')
+ return
# Brace must appear after function signature, but on the *next* line
if Match(r'^(?:\w+(?: ?\*+)? )+\w+\(', line):
pos = line.find('(')
- (endline, end_linenum, endpos) = CloseExpression(
- clean_lines, linenum, pos)
+ (endline, end_linenum, _) = CloseExpression(clean_lines, linenum, pos)
if endline.endswith('{'):
- error(filename, end_linenum, 'readability/braces', 5,
- 'Brace starting function body must be placed on its own line')
- else:
- func_start_linenum = end_linenum + 1
- while not clean_lines.lines[func_start_linenum] == "{":
- attrline = Match(
- r'^((?!# *define).*?)'
- r'(?:FUNC_ATTR|FUNC_API|REAL_FATTR)_\w+'
- r'(?:\(\d+(, \d+)*\))?',
- clean_lines.lines[func_start_linenum],
- )
- if attrline:
- if len(attrline.group(1)) != 2:
- error(filename, func_start_linenum,
- 'whitespace/indent', 5,
- 'Function attribute line should have 2-space '
- 'indent')
-
- func_start_linenum += 1
- else:
- func_start = clean_lines.lines[func_start_linenum]
- if not func_start.startswith('enum ') and func_start.endswith('{'):
- error(filename, func_start_linenum,
- 'readability/braces', 5,
- 'Brace starting function body must be placed '
- 'after the function signature')
- break
-
- # An else clause should be on the same line as the preceding closing brace.
- # If there is no preceding closing brace, there should be one.
- if Match(r'\s*else\s*', line):
- prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0]
- if Match(r'\s*}\s*$', prevline):
- error(filename, linenum, 'whitespace/newline', 4,
- 'An else should appear on the same line as the preceding }')
- else:
- error(filename, linenum, 'readability/braces', 5,
- 'An else should always have braces before it')
-
- # If should always have a brace
- for blockstart in ('if', 'while', 'for'):
- if Match(r'\s*{0}(?!\w)[^{{]*$'.format(blockstart), line):
- pos = line.find(blockstart)
- pos = line.find('(', pos)
- if pos > 0:
- (endline, _, endpos) = CloseExpression(
- clean_lines, linenum, pos)
- if endline[endpos:].find('{') == -1:
- error(filename, linenum, 'readability/braces', 5,
- '{} 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!
- if Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line):
- if Search(r'}\s*else if([^{]*)$', line): # could be multi-line if
- # find the ( after the if
- pos = line.find('else if')
- pos = line.find('(', pos)
- if pos > 0:
- (endline, _, endpos) = CloseExpression(
- clean_lines, linenum, pos)
- # must be brace after if
- if endline[endpos:].find('{') == -1:
- error(filename, linenum, 'readability/braces', 5,
- 'If an else has a brace on one side,'
- ' it should have it on both')
- else: # common case: else not followed by a multi-line if
- error(filename, linenum, 'readability/braces', 5,
- 'If an else has a brace on one side,'
- ' it should have it on both')
-
- # Likewise, an else should never have the else clause on the same line
- if Search(r'\belse [^\s{]', line) and not Search(r'\belse if\b', line):
- error(filename, linenum, 'whitespace/newline', 4,
- 'Else clause should never be on same line as else (use 2 lines)')
-
- # In the same way, a do/while should never be on one line
- if Match(r'\s*do [^\s{]', line):
- error(filename, linenum, 'whitespace/newline', 4,
- 'do/while clauses should not be on a single line')
-
- # Block bodies should not be followed by a semicolon. Due to C++11
- # brace initialization, there are more places where semicolons are
- # required than not, so we use a whitelist approach to check these
- # rather than a blacklist. These are the places where "};" should
- # be replaced by just "}":
- # 1. Some flavor of block following closing parenthesis:
- # for (;;) {};
- # while (...) {};
- # switch (...) {};
- # Function(...) {};
- # if (...) {};
- # if (...) else if (...) {};
- #
- # 2. else block:
- # if (...) else {};
- #
- # 3. const member function:
- # Function(...) const {};
- #
- # 4. Block following some statement:
- # x = 42;
- # {};
- #
- # 5. Block at the beginning of a function:
- # Function(...) {
- # {};
- # }
- #
- # Note that naively checking for the preceding "{" will also match
- # braces inside multi-dimensional arrays, but this is fine since
- # that expression will not contain semicolons.
- #
- # 6. Block following another block:
- # while (true) {}
- # {};
- #
- # 7. End of namespaces:
- # namespace {};
- #
- # These semicolons seems far more common than other kinds of
- # redundant semicolons, possibly due to people converting classes
- # to namespaces. For now we do not warn for this case.
- #
- # Try matching case 1 first.
- match = Match(r'^(.*\)\s*)\{', line)
- if match:
- # Matched closing parenthesis (case 1). Check the token before the
- # matching opening parenthesis, and don't warn if it looks like a
- # macro. This avoids these false positives:
- # - macro that defines a base class
- # - multi-line macro that defines a base class
- # - macro that defines the whole class-head
- #
- # But we still issue warnings for macros that we know are safe to
- # warn, specifically:
- # - TEST, TEST_F, TEST_P, MATCHER, MATCHER_P
- # - TYPED_TEST
- # - INTERFACE_DEF
- # - EXCLUSIVE_LOCKS_REQUIRED, SHARED_LOCKS_REQUIRED, LOCKS_EXCLUDED:
- #
- # We implement a whitelist of safe macros instead of a blacklist of
- # unsafe macros, even though the latter appears less frequently in
- # google code and would have been easier to implement. This is because
- # the downside for getting the whitelist wrong means some extra
- # semicolons, while the downside for getting the blacklist wrong
- # would result in compile errors.
- #
- # In addition to macros, we also don't want to warn on compound
- # literals.
- closing_brace_pos = match.group(1).rfind(')')
- opening_parenthesis = ReverseCloseExpression(
- clean_lines, linenum, closing_brace_pos)
- if opening_parenthesis[2] > -1:
- line_prefix = opening_parenthesis[0][0:opening_parenthesis[2]]
- macro = Search(r'\b([A-Z_]+)\s*$', line_prefix)
- if ((macro and
- macro.group(1) not in (
- 'TEST', 'TEST_F', 'MATCHER', 'MATCHER_P', 'TYPED_TEST',
- 'EXCLUSIVE_LOCKS_REQUIRED', 'SHARED_LOCKS_REQUIRED',
- 'LOCKS_EXCLUDED', 'INTERFACE_DEF')) or
- Search(r'\s+=\s*$', line_prefix) or
- Search(r'^\s*return\s*$', line_prefix)):
- match = None
-
- else:
- # Try matching cases 2-3.
- match = Match(r'^(.*(?:else|\)\s*const)\s*)\{', line)
- if not match:
- # Try matching cases 4-6. These are always matched on separate
- # lines.
- #
- # Note that we can't simply concatenate the previous line to the
- # current line and do a single match, otherwise we may output
- # duplicate warnings for the blank line case:
- # if (cond) {
- # // blank line
- # }
- prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0]
- if prevline and Search(r'[;{}]\s*$', prevline):
- match = Match(r'^(\s*)\{', line)
-
- # Check matching closing brace
- if match:
- (endline, endlinenum, endpos) = CloseExpression(
- clean_lines, linenum, len(match.group(1)))
- if endpos > -1 and Match(r'^\s*;', endline[endpos:]):
- # Current {} pair is eligible for semicolon check, and we have found
- # the redundant semicolon, output warning here.
- #
- # Note: because we are scanning forward for opening braces, and
- # outputting warnings for the matching closing brace, if there are
- # nested blocks with trailing semicolons, we will get the error
- # messages in reversed order.
- error(filename, endlinenum, 'readability/braces', 4,
- "You don't need a ; after a }")
+ return
+ func_start_linenum = end_linenum + 1
+ while not clean_lines.lines[func_start_linenum] == "{":
+ attrline = Match(
+ r'^((?!# *define).*?)'
+ r'(?:FUNC_ATTR|FUNC_API|REAL_FATTR)_\w+'
+ r'(?:\(\d+(, \d+)*\))?',
+ clean_lines.lines[func_start_linenum],
+ )
+ if attrline:
+ if len(attrline.group(1)) != 2:
+ error(filename, func_start_linenum,
+ 'whitespace/indent', 5,
+ 'Function attribute line should have 2-space '
+ 'indent')
+
+ func_start_linenum += 1
+ else:
+ func_start = clean_lines.lines[func_start_linenum]
+ if not func_start.startswith('enum ') and func_start.endswith('{'):
+ return
+ break
def CheckStyle(filename, clean_lines, linenum, error):
"""Checks rules from the 'C++ style rules' section of cppguide.html.
@@ -2573,23 +2369,10 @@ def CheckStyle(filename, clean_lines, linenum, error):
# if(match(prev, " +for \\(")) complain = 0;
# 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 (cleansed_line.count(';') > 1 and
- # for loops are allowed two ;'s (and may run over two lines).
- cleansed_line.find('for') == -1 and
- (GetPreviousNonBlankLine(clean_lines, linenum)[0].find('for') == -1 or
- GetPreviousNonBlankLine(clean_lines, linenum)[0].find(';') != -1) and
- # It's ok to have many commands in a switch case that fits in 1 line
- not ((cleansed_line.find('case ') != -1 or
- cleansed_line.find('default:') != -1) and
- cleansed_line.find('break;') != -1)):
- error(filename, linenum, 'whitespace/newline', 0,
- 'More than one command on the same line')
-
# Some more style checks
CheckBraces(filename, clean_lines, linenum, error)
CheckSpacing(filename, clean_lines, linenum, error)
@@ -2707,7 +2490,7 @@ def CheckLanguage(filename, clean_lines, linenum, error):
if match:
error(filename, linenum, 'runtime/printf', 4,
'Use xstrlcat or snprintf instead of %s' % match.group(1))
- if not Search(r'eval/typval\.[ch]$', filename):
+ if not Search(r'eval/typval\.[ch]$|eval/typval_defs\.h$', filename):
match = Search(r'(?:\.|->)'
r'(?:lv_(?:first|last|refcount|len|watch|idx(?:_item)?'
r'|copylist|lock)'
@@ -2720,8 +2503,7 @@ def CheckLanguage(filename, clean_lines, linenum, error):
# Check for suspicious usage of "if" like
# } if (a == b) {
if Search(r'\}\s*if\s*\(', line):
- error(filename, linenum, 'readability/braces', 4,
- 'Did you mean "else if"? If not, start a new line for "if".')
+ return
# Check for potential format string bugs like printf(foo).
# We constrain the pattern not to pick things like DocidForPrintf(foo).
diff --git a/src/nvim/lib/kbtree.h b/src/klib/kbtree.h
index 99f79952d7..99f79952d7 100644
--- a/src/nvim/lib/kbtree.h
+++ b/src/klib/kbtree.h
diff --git a/src/nvim/lib/khash.h b/src/klib/khash.h
index 57a41f9c13..57a41f9c13 100644
--- a/src/nvim/lib/khash.h
+++ b/src/klib/khash.h
diff --git a/src/nvim/lib/klist.h b/src/klib/klist.h
index a9abbc6dc2..a9abbc6dc2 100644
--- a/src/nvim/lib/klist.h
+++ b/src/klib/klist.h
diff --git a/src/nvim/lib/kvec.h b/src/klib/kvec.h
index b5b3adf7d2..f6674a0adf 100644
--- a/src/nvim/lib/kvec.h
+++ b/src/klib/kvec.h
@@ -111,7 +111,7 @@
(v).size = (v).size + len; \
} while (0)
-#define kv_concat(v, str) kv_concat_len(v, str, STRLEN(str))
+#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) \
diff --git a/src/man/nvim.1 b/src/man/nvim.1
index 9f35014ee8..c32bdeadc6 100644
--- a/src/man/nvim.1
+++ b/src/man/nvim.1
@@ -122,9 +122,6 @@ modifications.
.It Fl b
Binary mode.
.Ic ":help edit-binary"
-.It Fl l
-Lisp mode.
-Sets the 'lisp' and 'showmatch' options.
.It Fl A
Arabic mode.
Sets the 'arabic' option.
@@ -144,7 +141,7 @@ is specified, append messages to
instead of printing them.
.Ic ":help 'verbose'"
.It Fl D
-Debug mode for VimL (Vim script).
+Vimscript debug mode.
Started when executing the first command from a script.
:help debug-mode
.It Fl n
@@ -200,7 +197,8 @@ Skip loading plugins.
Implied by
.Cm -u NONE .
.It Fl -clean
-Mimic a fresh install of Nvim. Skip loading non-builtin plugins and shada (viminfo) file.
+Start Nvim with "factory defaults" (no user config and plugins, no shada).
+.Ic ":help --clean"
.It Fl o Ns Op Ar N
Open
.Ar N
@@ -267,10 +265,26 @@ but execute
before processing any vimrc.
Up to 10 instances of these can be used independently from instances of
.Fl c .
+.It Fl l Ar script Op Ar args
+Execute Lua
+.Ar script
+with optional
+.Op Ar args
+after processing any preceding Nvim startup arguments.
+All
+.Op Ar args
+are treated as script arguments and are passed literally to Lua, that is,
+.Fl l
+stops processing of Nvim arguments.
+.Ic ":help -l"
.It Fl S Op Ar session
-Source
+Execute
+.Ar session
+after the first file argument has been read. If
.Ar session
-after the first file argument has been read.
+filename ends with
+.Pa .lua
+it is executed as Lua instead of Vimscript.
Equivalent to
.Cm -c \(dqsource session\(dq .
.Ar session
diff --git a/src/mpack/mpack_core.c b/src/mpack/mpack_core.c
index 4ee67a032a..3424f444b9 100644
--- a/src/mpack/mpack_core.c
+++ b/src/mpack/mpack_core.c
@@ -173,6 +173,9 @@ MPACK_API int mpack_write(mpack_tokbuf_t *tokbuf, char **buf, size_t *buflen,
int mpack_rtoken(const char **buf, size_t *buflen, mpack_token_t *tok)
{
+ if (*buflen == 0) {
+ return MPACK_EOF;
+ }
unsigned char t = ADVANCE(buf, buflen);
if (t < 0x80) {
/* positive fixint */
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index 635833748d..2361210e59 100755
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -1,25 +1,337 @@
-option(USE_GCOV "Enable gcov support" OFF)
+add_library(main_lib INTERFACE)
+add_executable(nvim main.c)
+
+add_library(libuv_lib INTERFACE)
+find_package(LibUV 1.28.0 REQUIRED)
+target_include_directories(libuv_lib SYSTEM BEFORE INTERFACE ${LIBUV_INCLUDE_DIRS})
+target_link_libraries(libuv_lib INTERFACE ${LIBUV_LIBRARIES})
+
+find_package(Msgpack 1.0.0 REQUIRED)
+target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${MSGPACK_INCLUDE_DIRS})
+target_link_libraries(main_lib INTERFACE ${MSGPACK_LIBRARIES})
+
+find_package(LibLUV 1.43.0 REQUIRED)
+target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${LIBLUV_INCLUDE_DIRS})
+# Use "luv" as imported library, to work around CMake using "-lluv" for
+# "luv.so". #10407
+add_library(luv UNKNOWN IMPORTED)
+set_target_properties(luv PROPERTIES IMPORTED_LOCATION ${LIBLUV_LIBRARIES})
+target_link_libraries(main_lib INTERFACE luv)
+
+find_package(TreeSitter REQUIRED)
+target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${TreeSitter_INCLUDE_DIRS})
+target_link_libraries(main_lib INTERFACE ${TreeSitter_LIBRARIES})
+
+find_package(UNIBILIUM 2.0 REQUIRED)
+target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${UNIBILIUM_INCLUDE_DIRS})
+target_link_libraries(main_lib INTERFACE ${UNIBILIUM_LIBRARIES})
+
+find_package(LibTermkey 0.22 REQUIRED)
+target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${LIBTERMKEY_INCLUDE_DIRS})
+target_link_libraries(main_lib INTERFACE ${LIBTERMKEY_LIBRARIES})
+
+find_package(LIBVTERM 0.3 REQUIRED)
+target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${LIBVTERM_INCLUDE_DIRS})
+target_link_libraries(main_lib INTERFACE ${LIBVTERM_LIBRARIES})
+
+find_package(Iconv REQUIRED)
+target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${Iconv_INCLUDE_DIRS})
+target_link_libraries(main_lib INTERFACE ${Iconv_LIBRARIES})
+
+option(ENABLE_LIBINTL "enable libintl" ON)
+if(ENABLE_LIBINTL)
+ # LibIntl (not Intl) selects our FindLibIntl.cmake script. #8464
+ find_package(LibIntl REQUIRED)
+ target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${LibIntl_INCLUDE_DIRS})
+ if (LibIntl_FOUND)
+ target_link_libraries(main_lib INTERFACE ${LibIntl_LIBRARY})
+ endif()
+endif()
+
+# 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 EXACT REQUIRED)
+ target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${LUA_INCLUDE_DIR})
+ target_link_libraries(main_lib INTERFACE ${LUA_LIBRARIES})
+ # Passive (not REQUIRED): if LUAJIT_FOUND is not set, nvim-test is skipped.
+ find_package(LuaJit)
+else()
+ find_package(LuaJit REQUIRED)
+ target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${LUAJIT_INCLUDE_DIRS})
+ target_link_libraries(main_lib INTERFACE ${LUAJIT_LIBRARIES})
+endif()
+
+option(ENABLE_IWYU "Run include-what-you-use with the compiler." OFF)
+if(ENABLE_IWYU)
+ find_program(IWYU_PRG NAMES include-what-you-use iwyu)
+ if(NOT IWYU_PRG)
+ message(FATAL_ERROR "ENABLE_IWYU is ON but include-what-you-use is not found!")
+ endif()
+
+ set(iwyu_flags "${IWYU_PRG};")
+ string(APPEND iwyu_flags "-Xiwyu;--no_default_mappings;")
+ string(APPEND iwyu_flags "-Xiwyu;--mapping_file=${PROJECT_SOURCE_DIR}/cmake.config/iwyu/mapping.imp;")
+ string(APPEND iwyu_flags "-Xiwyu;--mapping_file=${PROJECT_SOURCE_DIR}/cmake.config/iwyu/gcc.libc.imp;")
+ string(APPEND iwyu_flags "-Xiwyu;--mapping_file=${PROJECT_SOURCE_DIR}/cmake.config/iwyu/gcc.symbols.imp")
+
+ set_target_properties(nvim PROPERTIES C_INCLUDE_WHAT_YOU_USE "${iwyu_flags}")
+ target_compile_definitions(main_lib INTERFACE EXITFREE)
+endif()
+
+if(MSVC)
+ # TODO(dundargoc): bump warning level
+ target_compile_options(main_lib INTERFACE -W2)
+
+ # Disable warnings that give too many false positives.
+ target_compile_options(main_lib INTERFACE -wd4311 -wd4146)
+ target_compile_definitions(main_lib INTERFACE _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_DEPRECATE)
+
+ target_sources(main_lib INTERFACE ${CMAKE_CURRENT_LIST_DIR}/os/nvim.manifest)
+else()
+ target_compile_options(main_lib INTERFACE -Wall -Wextra -pedantic -Wno-unused-parameter
+ -Wstrict-prototypes -std=gnu99 -Wshadow -Wconversion
+ -Wdouble-promotion
+ -Wmissing-noreturn
+ -Wmissing-format-attribute
+ -Wmissing-prototypes)
+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")
+ target_compile_options(main_lib INTERFACE -Wno-c11-extensions)
+endif()
+
+check_c_compiler_flag(-Wimplicit-fallthrough HAVE_WIMPLICIT_FALLTHROUGH_FLAG)
+if(HAVE_WIMPLICIT_FALLTHROUGH_FLAG)
+ target_compile_options(main_lib INTERFACE -Wimplicit-fallthrough)
+endif()
+
+option(ENABLE_COMPILER_SUGGESTIONS "Enable -Wsuggest compiler warnings" OFF)
+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)
+ target_compile_options(main_lib INTERFACE -Wsuggest-attribute=pure)
+ endif()
+
+ check_c_compiler_flag(-Wsuggest-attribute=const HAVE_WSUGGEST_ATTRIBUTE_CONST)
+ if(HAVE_WSUGGEST_ATTRIBUTE_CONST)
+ target_compile_options(main_lib INTERFACE -Wsuggest-attribute=const)
+ endif()
+
+ check_c_compiler_flag(-Wsuggest-attribute=malloc HAVE_WSUGGEST_ATTRIBUTE_MALLOC)
+ if(HAVE_WSUGGEST_ATTRIBUTE_MALLOC)
+ target_compile_options(main_lib INTERFACE -Wsuggest-attribute=malloc)
+ endif()
+
+ check_c_compiler_flag(-Wsuggest-attribute=cold HAVE_WSUGGEST_ATTRIBUTE_COLD)
+ if(HAVE_WSUGGEST_ATTRIBUTE_COLD)
+ target_compile_options(main_lib INTERFACE -Wsuggest-attribute=cold)
+ endif()
+endif()
+
+if(MINGW)
+ # Use POSIX compatible stdio in Mingw
+ target_compile_definitions(main_lib INTERFACE __USE_MINGW_ANSI_STDIO)
+endif()
+if(WIN32)
+ # Windows Vista is the minimum supported version
+ target_compile_definitions(main_lib INTERFACE _WIN32_WINNT=0x0600 MSWIN)
+endif()
+
+# OpenBSD's GCC (4.2.1) doesn't have -Wvla
+check_c_compiler_flag(-Wvla HAS_WVLA_FLAG)
+if(HAS_WVLA_FLAG)
+ target_compile_options(main_lib INTERFACE -Wvla)
+endif()
+
+check_c_compiler_flag(-fno-common HAVE_FNO_COMMON)
+if (HAVE_FNO_COMMON)
+ target_compile_options(main_lib INTERFACE -fno-common)
+endif()
+
+check_c_compiler_flag(-fdiagnostics-color=auto HAS_DIAG_COLOR_FLAG)
+if(HAS_DIAG_COLOR_FLAG)
+ if(CMAKE_GENERATOR MATCHES "Ninja")
+ target_compile_options(main_lib INTERFACE -fdiagnostics-color=always)
+ else()
+ target_compile_options(main_lib INTERFACE -fdiagnostics-color=auto)
+ endif()
+endif()
+
+option(CI_BUILD "CI, extra flags will be set" OFF)
+if(CI_BUILD)
+ message(STATUS "CI build enabled")
+ if(MSVC)
+ target_compile_options(main_lib INTERFACE -WX)
+ else()
+ target_compile_options(main_lib INTERFACE -Werror)
+ if(DEFINED ENV{BUILD_UCHAR})
+ # Get some test coverage for unsigned char
+ target_compile_options(main_lib INTERFACE -funsigned-char)
+ endif()
+ endif()
+endif()
+
+list(APPEND CMAKE_REQUIRED_INCLUDES "${UNIBILIUM_INCLUDE_DIRS}")
+list(APPEND CMAKE_REQUIRED_LIBRARIES "${UNIBILIUM_LIBRARIES}")
+check_c_source_compiles("
+#include <unibilium.h>
+
+int
+main(void)
+{
+ unibi_str_from_var(unibi_var_from_str(\"\"));
+ return unibi_num_from_var(unibi_var_from_num(0));
+}
+" UNIBI_HAS_VAR_FROM)
+list(REMOVE_ITEM CMAKE_REQUIRED_INCLUDES "${UNIBILIUM_INCLUDE_DIRS}")
+list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES "${UNIBILIUM_LIBRARIES}")
+if(UNIBI_HAS_VAR_FROM)
+ target_compile_definitions(main_lib INTERFACE NVIM_UNIBI_HAS_VAR_FROM)
+endif()
+
+list(APPEND CMAKE_REQUIRED_INCLUDES "${MSGPACK_INCLUDE_DIRS}")
+check_c_source_compiles("
+#include <msgpack.h>
+
+int
+main(void)
+{
+ return MSGPACK_OBJECT_FLOAT32;
+}
+" MSGPACK_HAS_FLOAT32)
+list(REMOVE_ITEM CMAKE_REQUIRED_INCLUDES "${MSGPACK_INCLUDE_DIRS}")
+if(MSGPACK_HAS_FLOAT32)
+ target_compile_definitions(main_lib INTERFACE NVIM_MSGPACK_HAS_FLOAT32)
+endif()
+
+list(APPEND CMAKE_REQUIRED_INCLUDES "${TreeSitter_INCLUDE_DIRS}")
+list(APPEND CMAKE_REQUIRED_LIBRARIES "${TreeSitter_LIBRARIES}")
+check_c_source_compiles("
+#include <tree_sitter/api.h>
+int
+main(void)
+{
+ TSQueryCursor *cursor = ts_query_cursor_new();
+ ts_query_cursor_set_match_limit(cursor, 32);
+ return 0;
+}
+" TS_HAS_SET_MATCH_LIMIT)
+if(TS_HAS_SET_MATCH_LIMIT)
+ target_compile_definitions(main_lib INTERFACE NVIM_TS_HAS_SET_MATCH_LIMIT)
+endif()
+check_c_source_compiles("
+#include <stdlib.h>
+#include <tree_sitter/api.h>
+int
+main(void)
+{
+ ts_set_allocator(malloc, calloc, realloc, free);
+ return 0;
+}
+" TS_HAS_SET_ALLOCATOR)
+if(TS_HAS_SET_ALLOCATOR)
+ target_compile_definitions(main_lib INTERFACE NVIM_TS_HAS_SET_ALLOCATOR)
+endif()
+list(REMOVE_ITEM CMAKE_REQUIRED_INCLUDES "${TreeSitter_INCLUDE_DIRS}")
+list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES "${TreeSitter_LIBRARIES}")
+
+# Include <string.h> because some toolchains define _FORTIFY_SOURCE=2 in
+# internal header files, which should in turn be #included by <string.h>.
+check_c_source_compiles("
+#include <string.h>
+
+#if defined(_FORTIFY_SOURCE) && _FORTIFY_SOURCE > 1
+#error \"_FORTIFY_SOURCE > 1\"
+#endif
+int
+main(void)
+{
+ return 0;
+}
+" HAS_ACCEPTABLE_FORTIFY)
+
+if(NOT HAS_ACCEPTABLE_FORTIFY)
+ message(STATUS "Unsupported _FORTIFY_SOURCE found, forcing _FORTIFY_SOURCE=1")
+ # Extract possible prefix to _FORTIFY_SOURCE (e.g. -Wp,-D_FORTIFY_SOURCE).
+ string(REGEX MATCH "[^\ ]+-D_FORTIFY_SOURCE" _FORTIFY_SOURCE_PREFIX "${CMAKE_C_FLAGS}")
+ string(REPLACE "-D_FORTIFY_SOURCE" "" _FORTIFY_SOURCE_PREFIX "${_FORTIFY_SOURCE_PREFIX}" )
+ if(NOT _FORTIFY_SOURCE_PREFIX STREQUAL "")
+ message(STATUS "Detected _FORTIFY_SOURCE Prefix=${_FORTIFY_SOURCE_PREFIX}")
+ endif()
+ # -U in add_definitions doesn't end up in the correct spot, so we add it to
+ # the flags variable instead.
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${_FORTIFY_SOURCE_PREFIX}-U_FORTIFY_SOURCE ${_FORTIFY_SOURCE_PREFIX}-D_FORTIFY_SOURCE=1")
+endif()
+
+target_compile_definitions(main_lib INTERFACE INCLUDE_GENERATED_DECLARATIONS)
+
+# Remove --sort-common from linker flags, as this seems to cause bugs (see #2641, #3374).
+# TODO: Figure out the root cause.
+if(CMAKE_EXE_LINKER_FLAGS MATCHES "--sort-common" OR
+ CMAKE_SHARED_LINKER_FLAGS MATCHES "--sort-common" OR
+ CMAKE_MODULE_LINKER_FLAGS MATCHES "--sort-common")
+ message(STATUS "Removing --sort-common from linker flags")
+ string(REGEX REPLACE ",--sort-common(=[^,]+)?" "" CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
+ string(REGEX REPLACE ",--sort-common(=[^,]+)?" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}")
+ string(REGEX REPLACE ",--sort-common(=[^,]+)?" "" CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS}")
+
+ # If no linker flags remain for a -Wl argument, remove it.
+ # '-Wl$' will match LDFLAGS="-Wl,--sort-common",
+ # '-Wl ' will match LDFLAGS="-Wl,--sort-common -Wl,..."
+ string(REGEX REPLACE "-Wl($| )" "" CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
+ string(REGEX REPLACE "-Wl($| )" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}")
+ string(REGEX REPLACE "-Wl($| )" "" CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS}")
+endif()
+
+if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
+ if(CMAKE_SYSTEM_NAME STREQUAL "SunOS")
+ target_link_libraries(nvim PRIVATE -Wl,--no-undefined -lsocket)
+ elseif(NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin")
+ target_link_libraries(nvim PRIVATE -Wl,--no-undefined)
+ endif()
+
+ # For O_CLOEXEC, O_DIRECTORY, and O_NOFOLLOW flags on older systems
+ # (pre POSIX.1-2008: glibc 2.11 and earlier). #4042
+ # For ptsname(). #6743
+ target_compile_definitions(main_lib INTERFACE _GNU_SOURCE)
+endif()
+option(USE_GCOV "Enable gcov support" OFF)
if(USE_GCOV)
if(CLANG_TSAN)
# GCOV and TSAN results in false data race reports
message(FATAL_ERROR "USE_GCOV cannot be used with CLANG_TSAN")
endif()
message(STATUS "Enabling gcov support")
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage")
- set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage")
- set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --coverage")
- add_definitions(-DUSE_GCOV)
+ target_compile_options(main_lib INTERFACE --coverage)
+ target_link_libraries(main_lib INTERFACE --coverage)
+ target_compile_definitions(main_lib INTERFACE USE_GCOV)
endif()
if(WIN32)
if(MINGW)
# Enable wmain
- set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -municode")
+ target_link_libraries(nvim PRIVATE -municode)
endif()
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
- set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework CoreServices")
- set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -framework CoreServices")
+ target_link_libraries(nvim PRIVATE "-framework CoreServices")
+endif()
+
+if(UNIX)
+ # -fstack-protector breaks non Unix builds even in Mingw-w64
+ check_c_compiler_flag(-fstack-protector-strong HAS_FSTACK_PROTECTOR_STRONG_FLAG)
+ check_c_compiler_flag(-fstack-protector HAS_FSTACK_PROTECTOR_FLAG)
+ if(HAS_FSTACK_PROTECTOR_STRONG_FLAG)
+ target_compile_options(main_lib INTERFACE -fstack-protector-strong)
+ target_link_libraries(main_lib INTERFACE -fstack-protector-strong)
+ elseif(HAS_FSTACK_PROTECTOR_FLAG)
+ target_compile_options(main_lib INTERFACE -fstack-protector --param ssp-buffer-size=4)
+ target_link_libraries(main_lib INTERFACE -fstack-protector --param ssp-buffer-size=4)
+ endif()
endif()
set(GENERATOR_DIR ${CMAKE_CURRENT_LIST_DIR}/generators)
@@ -36,10 +348,8 @@ set(HEADER_GENERATOR ${GENERATOR_DIR}/gen_declarations.lua)
set(GENERATED_INCLUDES_DIR ${PROJECT_BINARY_DIR}/include)
set(GENERATED_API_DISPATCH ${GENERATED_DIR}/api/private/dispatch_wrappers.generated.h)
set(GENERATED_FUNCS_METADATA ${GENERATED_DIR}/api/private/funcs_metadata.generated.h)
-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)
@@ -68,36 +378,26 @@ set(LUA_FILETYPE_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/filetype.lu
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")
-set(LINT_SUPPRESS_URL "${LINT_SUPPRESS_URL_BASE}/errors.json")
-set(LINT_PRG ${PROJECT_SOURCE_DIR}/src/clint.py)
-set(DOWNLOAD_SCRIPT ${PROJECT_SOURCE_DIR}/cmake/Download.cmake)
-set(LINT_SUPPRESSES_ROOT ${PROJECT_BINARY_DIR}/errors)
-set(LINT_SUPPRESSES_URL "${LINT_SUPPRESS_URL_BASE}/errors.tar.gz")
-set(LINT_SUPPRESSES_ARCHIVE ${LINT_SUPPRESSES_ROOT}/errors.tar.gz)
-set(LINT_SUPPRESSES_TOUCH_FILE "${TOUCHES_DIR}/unpacked-clint-errors-archive")
-set(CLINT_REPORT_PATH ${LINT_SUPPRESSES_ROOT}/src/home/runner/work/doc/doc/gh-pages/reports/clint)
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)
glob_wrapper(MSGPACK_RPC_HEADERS msgpack_rpc/*.h)
-include_directories(${GENERATED_DIR})
-include_directories(${CACHED_GENERATED_DIR})
-include_directories(${GENERATED_INCLUDES_DIR})
+target_include_directories(main_lib INTERFACE ${GENERATED_DIR})
+target_include_directories(main_lib INTERFACE ${CACHED_GENERATED_DIR})
+target_include_directories(main_lib INTERFACE ${GENERATED_INCLUDES_DIR})
+target_include_directories(main_lib INTERFACE "${PROJECT_BINARY_DIR}/cmake.config")
+target_include_directories(main_lib INTERFACE "${PROJECT_SOURCE_DIR}/src")
file(MAKE_DIRECTORY ${TOUCHES_DIR})
file(MAKE_DIRECTORY ${GENERATED_DIR})
file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR})
-file(MAKE_DIRECTORY ${LINT_SUPPRESSES_ROOT})
-file(MAKE_DIRECTORY ${LINT_SUPPRESSES_ROOT}/src)
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)
+glob_wrapper(EXTERNAL_SOURCES ../xdiff/*.c ../mpack/*.c ../cjson/*.c ../klib/*.c)
+glob_wrapper(EXTERNAL_HEADERS ../xdiff/*.h ../mpack/*.h ../cjson/*.h ../klib/*.h)
foreach(subdir
os
@@ -111,9 +411,6 @@ foreach(subdir
viml
viml/parser
)
- if(${subdir} MATCHES "tui" AND NOT FEAT_TUI)
- continue()
- endif()
file(MAKE_DIRECTORY ${GENERATED_DIR}/${subdir})
file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/${subdir})
@@ -123,8 +420,6 @@ foreach(subdir
list(APPEND NVIM_HEADERS ${headers})
endforeach()
-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.
list(SORT NVIM_SOURCES)
@@ -156,29 +451,32 @@ endforeach()
list(REMOVE_ITEM NVIM_SOURCES ${to_remove})
-if(NOT MSVC)
- # xdiff, mpack, lua-cjson: inlined external project, we don't maintain it. #9306
+# xdiff, mpack, lua-cjson: inlined external project, we don't maintain it. #9306
+if(MSVC)
set_source_files_properties(
- ${EXTERNAL_SOURCES} PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -Wno-conversion -Wno-missing-noreturn -Wno-missing-format-attribute -Wno-double-promotion")
+ ${EXTERNAL_SOURCES} PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} /wd4090 /wd4244")
+else()
+ set_source_files_properties(
+ ${EXTERNAL_SOURCES} PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -Wno-conversion -Wno-missing-noreturn -Wno-missing-format-attribute -Wno-double-promotion -Wno-strict-prototypes")
endif()
if(NOT "${MIN_LOG_LEVEL}" MATCHES "^$")
- add_definitions(-DMIN_LOG_LEVEL=${MIN_LOG_LEVEL})
+ target_compile_definitions(main_lib INTERFACE MIN_LOG_LEVEL=${MIN_LOG_LEVEL})
endif()
-if(DEBUG OR CLANG_ASAN_UBSAN OR CLANG_MSAN OR CLANG_TSAN)
- add_definitions(-DEXITFREE)
+if(CLANG_ASAN_UBSAN OR CLANG_MSAN OR CLANG_TSAN)
+ target_compile_definitions(main_lib INTERFACE EXITFREE)
endif()
-get_directory_property(gen_cdefs COMPILE_DEFINITIONS)
-foreach(gen_cdef ${gen_cdefs} DO_NOT_DEFINE_EMPTY_ATTRIBUTES)
+get_target_property(prop main_lib INTERFACE_COMPILE_DEFINITIONS)
+foreach(gen_cdef DO_NOT_DEFINE_EMPTY_ATTRIBUTES ${prop})
if(NOT ${gen_cdef} MATCHES "INCLUDE_GENERATED_DECLARATIONS")
list(APPEND gen_cflags "-D${gen_cdef}")
endif()
endforeach()
-get_directory_property(gen_includes INCLUDE_DIRECTORIES)
-foreach(gen_include ${gen_includes} ${LUA_PREFERRED_INCLUDE_DIRS})
+get_target_property(prop main_lib INTERFACE_INCLUDE_DIRECTORIES)
+foreach(gen_include ${prop})
list(APPEND gen_cflags "-I${gen_include}")
endforeach()
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_OSX_SYSROOT)
@@ -190,14 +488,6 @@ separate_arguments(C_FLAGS_ARRAY UNIX_COMMAND ${CMAKE_C_FLAGS})
separate_arguments(C_FLAGS_${build_type}_ARRAY UNIX_COMMAND ${CMAKE_C_FLAGS_${build_type}})
set(gen_cflags ${gen_cflags} ${C_FLAGS_${build_type}_ARRAY} ${C_FLAGS_ARRAY})
-function(get_preproc_output varname iname)
- if(MSVC)
- set(${varname} /P /Fi${iname} /nologo PARENT_SCOPE)
- else()
- set(${varname} -E -o ${iname} PARENT_SCOPE)
- endif()
-endfunction()
-
set(NVIM_VERSION_GIT_H ${PROJECT_BINARY_DIR}/cmake.config/auto/versiondef_git.h)
add_custom_target(update_version_stamp
COMMAND ${CMAKE_COMMAND}
@@ -206,9 +496,8 @@ add_custom_target(update_version_stamp
-DNVIM_VERSION_PATCH=${NVIM_VERSION_PATCH}
-DNVIM_VERSION_PRERELEASE=${NVIM_VERSION_PRERELEASE}
-DOUTPUT=${NVIM_VERSION_GIT_H}
- -DCMAKE_MESSAGE_LOG_LEVEL=${CMAKE_MESSAGE_LOG_LEVEL}
+ -DNVIM_SOURCE_DIR=${CMAKE_SOURCE_DIR}
-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
@@ -221,7 +510,6 @@ foreach(sfile ${NVIM_SOURCES}
${GENERATED_API_DISPATCH}
"${GENERATED_UI_EVENTS_CALL}"
"${GENERATED_UI_EVENTS_REMOTE}"
- "${GENERATED_UI_EVENTS_BRIDGE}"
"${GENERATED_KEYSETS}"
"${GENERATED_UI_EVENTS_CLIENT}"
)
@@ -240,7 +528,11 @@ foreach(sfile ${NVIM_SOURCES}
set(gf_h_h "${GENERATED_INCLUDES_DIR}/${r}.h.generated.h")
set(gf_i "${GENERATED_DIR}/${r}.i")
- get_preproc_output(PREPROC_OUTPUT ${gf_i})
+ if(MSVC)
+ set(PREPROC_OUTPUT /P /Fi${gf_i} /nologo)
+ else()
+ set(PREPROC_OUTPUT -E -o ${gf_i})
+ endif()
set(depends "${HEADER_GENERATOR}" "${sfile}")
if("${f}" STREQUAL "version.c")
@@ -271,11 +563,11 @@ add_custom_command(OUTPUT ${GENERATED_UNICODE_TABLES}
add_custom_command(
OUTPUT ${GENERATED_API_DISPATCH} ${GENERATED_FUNCS_METADATA}
${API_METADATA} ${LUA_API_C_BINDINGS}
- COMMAND ${LUA_PRG} ${API_DISPATCH_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}
- ${GENERATED_API_DISPATCH}
- ${GENERATED_FUNCS_METADATA} ${API_METADATA}
- ${LUA_API_C_BINDINGS}
- ${API_HEADERS}
+ COMMAND ${LUA_GEN_PRG} ${API_DISPATCH_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}
+ ${GENERATED_API_DISPATCH}
+ ${GENERATED_FUNCS_METADATA} ${API_METADATA}
+ ${LUA_API_C_BINDINGS}
+ ${API_HEADERS}
DEPENDS
${API_HEADERS}
${MSGPACK_RPC_HEADERS}
@@ -316,20 +608,16 @@ list(APPEND NVIM_GENERATED_SOURCES
)
add_custom_command(
- OUTPUT ${GENERATED_UI_EVENTS}
- ${GENERATED_UI_EVENTS_CALL}
+ OUTPUT ${GENERATED_UI_EVENTS_CALL}
${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}
- ${GENERATED_UI_EVENTS_CALL}
- ${GENERATED_UI_EVENTS_REMOTE}
- ${GENERATED_UI_EVENTS_BRIDGE}
- ${GENERATED_UI_EVENTS_METADATA}
- ${GENERATED_UI_EVENTS_CLIENT}
+ COMMAND ${LUA_GEN_PRG} ${API_UI_EVENTS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}
+ ${CMAKE_CURRENT_LIST_DIR}/api/ui_events.in.h
+ ${GENERATED_UI_EVENTS_CALL}
+ ${GENERATED_UI_EVENTS_REMOTE}
+ ${GENERATED_UI_EVENTS_METADATA}
+ ${GENERATED_UI_EVENTS_CLIENT}
DEPENDS
${API_UI_EVENTS_GENERATOR}
${GENERATOR_C_GRAMMAR}
@@ -396,76 +684,45 @@ foreach(hfile ${NVIM_GENERATED_FOR_HEADERS})
endif()
endforeach()
-# Our dependencies come first.
-
if (CMAKE_SYSTEM_NAME MATCHES "OpenBSD")
- list(APPEND NVIM_LINK_LIBRARIES pthread c++abi)
-endif()
-
-if (LibIntl_FOUND)
- list(APPEND NVIM_LINK_LIBRARIES ${LibIntl_LIBRARY})
-endif()
-
-if(Iconv_LIBRARIES)
- list(APPEND NVIM_LINK_LIBRARIES ${Iconv_LIBRARIES})
+ target_link_libraries(main_lib INTERFACE pthread c++abi)
endif()
if(WIN32)
- list(APPEND NVIM_LINK_LIBRARIES netapi32)
+ target_link_libraries(main_lib INTERFACE netapi32)
endif()
-# Use "luv" as imported library, to work around CMake using "-lluv" for
-# "luv.so". #10407
-add_library(luv UNKNOWN IMPORTED)
-set_property(TARGET luv PROPERTY IMPORTED_LOCATION ${LIBLUV_LIBRARIES})
-
-# Put these last on the link line, since multiple things may depend on them.
-list(APPEND NVIM_LINK_LIBRARIES
- luv
- ${LIBUV_LIBRARIES}
- ${MSGPACK_LIBRARIES}
- ${LIBVTERM_LIBRARIES}
- ${LIBTERMKEY_LIBRARIES}
- ${UNIBILIUM_LIBRARIES}
- ${UTF8PROC_LIBRARIES}
- ${TreeSitter_LIBRARIES}
- ${CMAKE_THREAD_LIBS_INIT}
-)
-
if(UNIX)
- list(APPEND NVIM_LINK_LIBRARIES
- m)
+ target_link_libraries(main_lib INTERFACE m)
if (NOT CMAKE_SYSTEM_NAME STREQUAL "SunOS")
- list(APPEND NVIM_LINK_LIBRARIES
- util)
+ target_link_libraries(main_lib INTERFACE util)
endif()
endif()
-set(NVIM_EXEC_LINK_LIBRARIES ${NVIM_LINK_LIBRARIES} ${LUA_PREFERRED_LIBRARIES})
+target_sources(nvim PRIVATE ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS}
+ ${NVIM_GENERATED_SOURCES} ${NVIM_SOURCES} ${NVIM_HEADERS}
+ ${EXTERNAL_SOURCES} ${EXTERNAL_HEADERS})
+
+set_target_properties(nvim
+ PROPERTIES
+ EXPORT_COMPILE_COMMANDS ON
+ ENABLE_EXPORTS TRUE)
-# Add IPO flags (for LTO), or error if CMake does not know the flags. #8654
-if(POLICY CMP0069)
- cmake_policy(SET CMP0069 NEW)
+if(${CMAKE_VERSION} VERSION_LESS 3.20)
+ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
endif()
-add_executable(nvim ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS}
- ${NVIM_GENERATED_SOURCES} ${NVIM_SOURCES} ${NVIM_HEADERS}
- ${EXTERNAL_SOURCES} ${EXTERNAL_HEADERS})
-target_link_libraries(nvim ${NVIM_EXEC_LINK_LIBRARIES})
+target_link_libraries(nvim PRIVATE main_lib PUBLIC libuv_lib)
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})
-set_property(TARGET nvim PROPERTY ENABLE_EXPORTS TRUE)
-
-if(ENABLE_LTO AND (POLICY CMP0069))
+if(ENABLE_LTO)
include(CheckIPOSupported)
check_ipo_supported(RESULT IPO_SUPPORTED)
if(IPO_SUPPORTED AND (NOT CMAKE_BUILD_TYPE MATCHES Debug))
- set_property(TARGET nvim PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
+ set_target_properties(nvim PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()
endif()
@@ -537,8 +794,8 @@ if(WIN32)
translations/qt_uk.qm
D3Dcompiler_47.dll
libEGL.dll
- libgcc_s_dw2-1.dll
- libGLESV2.dll
+ libgcc_s_seh-1.dll
+ libGLESv2.dll
libstdc++-6.dll
libwinpthread-1.dll
nvim-qt.exe
@@ -569,7 +826,10 @@ file(MAKE_DIRECTORY ${BINARY_LIB_DIR})
# install treesitter parser if bundled
if(EXISTS ${DEPS_PREFIX}/lib/nvim/parser)
- file(COPY ${DEPS_PREFIX}/lib/nvim/parser DESTINATION ${BINARY_LIB_DIR})
+ glob_wrapper(TREESITTER_PARSERS ${DEPS_PREFIX}/lib/nvim/parser/*)
+ foreach(parser_lib ${TREESITTER_PARSERS})
+ file(COPY ${parser_lib} DESTINATION ${BINARY_LIB_DIR}/parser)
+ endforeach()
endif()
install(DIRECTORY ${BINARY_LIB_DIR}
@@ -584,8 +844,6 @@ add_library(
${NVIM_HEADERS} ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS}
${EXTERNAL_SOURCES} ${EXTERNAL_HEADERS}
)
-set_property(TARGET libnvim APPEND PROPERTY
- INCLUDE_DIRECTORIES ${LUA_PREFERRED_INCLUDE_DIRS})
if(MSVC)
set(LIBNVIM_NAME libnvim)
else()
@@ -594,15 +852,15 @@ endif()
set_target_properties(
libnvim
PROPERTIES
- POSITION_INDEPENDENT_CODE ON
OUTPUT_NAME ${LIBNVIM_NAME}
)
-target_compile_options(libnvim PRIVATE -DMAKE_LIB)
+target_compile_definitions(libnvim PRIVATE MAKE_LIB)
+target_link_libraries(libnvim PRIVATE main_lib PUBLIC libuv_lib)
if(NOT LUAJIT_FOUND)
message(STATUS "luajit not found, skipping nvim-test (unit tests) target")
else()
- set(NVIM_TEST_LINK_LIBRARIES ${NVIM_LINK_LIBRARIES} ${LUAJIT_LIBRARIES})
+ glob_wrapper(UNIT_TEST_FIXTURES ${PROJECT_SOURCE_DIR}/test/unit/fixtures/*.c)
add_library(
nvim-test
MODULE
@@ -612,49 +870,41 @@ else()
${EXTERNAL_SOURCES} ${EXTERNAL_HEADERS}
${UNIT_TEST_FIXTURES}
)
- target_link_libraries(nvim-test ${NVIM_TEST_LINK_LIBRARIES})
- target_link_libraries(libnvim ${NVIM_TEST_LINK_LIBRARIES})
- set_property(
- TARGET nvim-test
- APPEND PROPERTY INCLUDE_DIRECTORIES ${LUAJIT_INCLUDE_DIRS}
- )
- set_target_properties(
- nvim-test
- PROPERTIES
- POSITION_INDEPENDENT_CODE ON
- )
- target_compile_options(nvim-test PRIVATE -DUNIT_TESTING)
+ target_link_libraries(nvim-test PRIVATE ${LUAJIT_LIBRARIES} main_lib PUBLIC libuv_lib)
+ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
+ target_link_libraries(nvim-test PRIVATE "-framework CoreServices")
+ endif()
+ target_include_directories(nvim-test PRIVATE ${LUAJIT_INCLUDE_DIRS})
+ target_compile_definitions(nvim-test PRIVATE UNIT_TESTING)
endif()
if(CLANG_ASAN_UBSAN)
message(STATUS "Enabling Clang address sanitizer and undefined behavior sanitizer for nvim.")
- check_c_compiler_flag(-fno-sanitize-recover=all SANITIZE_RECOVER_ALL)
- if(SANITIZE_RECOVER_ALL)
- if(CI_BUILD)
- # Try to recover from all sanitize issues so we get reports about all failures
- set(SANITIZE_RECOVER -fsanitize-recover=all) # Clang 3.6+
- else()
- set(SANITIZE_RECOVER -fno-sanitize-recover=all) # Clang 3.6+
- endif()
+ if(CI_BUILD)
+ # Try to recover from all sanitize issues so we get reports about all failures
+ target_compile_options(nvim PRIVATE -fsanitize-recover=all)
else()
- if(CI_BUILD)
- # Try to recover from all sanitize issues so we get reports about all failures
- set(SANITIZE_RECOVER -fsanitize-recover) # Clang 3.5-
- else()
- set(SANITIZE_RECOVER -fno-sanitize-recover) # Clang 3.5-
- endif()
+ target_compile_options(nvim PRIVATE -fno-sanitize-recover=all)
endif()
- 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 ")
+ target_compile_options(nvim PRIVATE
+ -fno-omit-frame-pointer
+ -fno-optimize-sibling-calls
+ -fsanitize=address
+ -fsanitize=undefined
+ -fsanitize-blacklist=${PROJECT_SOURCE_DIR}/src/.asan-blacklist)
+ target_link_libraries(nvim PRIVATE -fsanitize=address -fsanitize=undefined)
elseif(CLANG_MSAN)
message(STATUS "Enabling Clang memory sanitizer for nvim.")
- 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 ")
+ target_compile_options(nvim PRIVATE
+ -fsanitize=memory
+ -fsanitize-memory-track-origins
+ -fno-omit-frame-pointer
+ -fno-optimize-sibling-calls)
+ target_link_libraries(nvim PRIVATE -fsanitize=memory -fsanitize-memory-track-origins)
elseif(CLANG_TSAN)
message(STATUS "Enabling Clang thread sanitizer for nvim.")
- 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 ")
+ target_compile_options(nvim PRIVATE -fsanitize=thread -fPIE)
+ target_link_libraries(nvim PRIVATE -fsanitize=thread)
endif()
function(get_test_target prefix sfile relative_path_var target_var)
@@ -697,10 +947,7 @@ foreach(hfile ${NVIM_HEADERS})
${texe}
EXCLUDE_FROM_ALL
${tsource} ${NVIM_HEADERS} ${NVIM_GENERATED_FOR_HEADERS})
- set_property(
- TARGET ${texe}
- APPEND PROPERTY INCLUDE_DIRECTORIES ${LUA_PREFERRED_INCLUDE_DIRS}
- )
+ target_link_libraries(${texe} PRIVATE main_lib)
set_target_properties(${texe} PROPERTIES FOLDER test)
list(FIND NO_SINGLE_CHECK_HEADERS "${relative_path}" hfile_exclude_idx)
@@ -711,55 +958,19 @@ foreach(hfile ${NVIM_HEADERS})
endforeach()
add_custom_target(check-single-includes DEPENDS ${HEADER_CHECK_TARGETS})
-function(add_download output url allow_failure)
- add_custom_command(
- OUTPUT "${output}"
- COMMAND
- ${CMAKE_COMMAND}
- -DURL=${url} -DFILE=${output}
- -DALLOW_FAILURE=${allow_failure}
- -P ${DOWNLOAD_SCRIPT}
- DEPENDS ${DOWNLOAD_SCRIPT}
- )
-endfunction()
-
-add_download(${LINT_SUPPRESSES_ARCHIVE} ${LINT_SUPPRESSES_URL} off)
-
-add_custom_command(
- OUTPUT ${LINT_SUPPRESSES_TOUCH_FILE}
- WORKING_DIRECTORY ${LINT_SUPPRESSES_ROOT}/src
- COMMAND ${CMAKE_COMMAND} -E tar xfz ${LINT_SUPPRESSES_ARCHIVE}
- COMMAND ${CMAKE_COMMAND} -E copy_directory ${CLINT_REPORT_PATH} "${LINT_SUPPRESSES_ROOT}"
- COMMAND ${CMAKE_COMMAND} -E touch ${LINT_SUPPRESSES_TOUCH_FILE}
- DEPENDS ${LINT_SUPPRESSES_ARCHIVE}
-)
-
-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)
- set(suppress_file ${LINT_SUPPRESSES_ROOT}/${suffix}.json)
- set(suppress_url "${LINT_SUPPRESS_URL_BASE}/${suffix}.json")
- set(rsfile src/nvim/${r})
- set(touch_file "${TOUCHES_DIR}/ran-clint-${suffix}")
- add_custom_command(
- OUTPUT ${touch_file}
- 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}
- )
- list(APPEND LINT_TARGETS ${touch_file})
- list(APPEND LINT_NVIM_REL_SOURCES ${rsfile})
-endforeach()
-add_custom_target(lintc DEPENDS ${LINT_TARGETS})
+add_glob_target(
+ TARGET lintc-clint
+ COMMAND ${PROJECT_SOURCE_DIR}/src/clint.py
+ FLAGS --output=${LINT_OUTPUT_FORMAT}
+ FILES ${LINT_NVIM_SOURCES}
+ EXCLUDE
+ tui/terminfo_defs.h)
add_custom_target(uncrustify-version
COMMAND ${CMAKE_COMMAND}
@@ -767,13 +978,15 @@ add_custom_target(uncrustify-version
-D CONFIG_FILE=${PROJECT_SOURCE_DIR}/src/uncrustify.cfg
-P ${PROJECT_SOURCE_DIR}/cmake/CheckUncrustifyVersion.cmake)
-add_glob_targets(
- TARGET lintuncrustify
+add_glob_target(
+ TARGET lintc-uncrustify
COMMAND ${UNCRUSTIFY_PRG}
FLAGS -c "${PROJECT_SOURCE_DIR}/src/uncrustify.cfg" -q --check
- FILES ${LINT_NVIM_SOURCES}
- )
-add_dependencies(lintuncrustify uncrustify-version)
+ FILES ${LINT_NVIM_SOURCES})
+add_dependencies(lintc-uncrustify uncrustify-version)
+
+add_custom_target(lintc)
+add_dependencies(lintc lintc-clint lintc-uncrustify)
add_custom_target(formatc
COMMAND ${CMAKE_COMMAND}
@@ -783,14 +996,6 @@ add_custom_target(formatc
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
add_dependencies(formatc uncrustify-version)
-add_custom_target(
- lintcfull
- COMMAND
- ${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} lintuncrustify
-)
-
add_custom_target(generated-sources DEPENDS
${NVIM_GENERATED_FOR_SOURCES}
${NVIM_GENERATED_FOR_HEADERS}
diff --git a/src/nvim/README.md b/src/nvim/README.md
index 91fb3ca2f6..6876227e48 100644
--- a/src/nvim/README.md
+++ b/src/nvim/README.md
@@ -18,6 +18,12 @@ The source files use extensions to hint about their purpose.
- `*.h.generated.h` - exported functions’ declarations.
- `*.c.generated.h` - static functions’ declarations.
+Common structures
+-----------------
+
+- StringBuilder
+- kvec or garray.c for dynamic lists / vectors (use StringBuilder for strings)
+
Logs
----
@@ -391,8 +397,8 @@ implemented by libuv, the platform layer used by Nvim.
Since Nvim inherited its code from Vim, the states are not prepared to receive
"arbitrary events", so we use a special key to represent those (When a state
-receives an "arbitrary event", it normally doesn't do anything other update the
-screen).
+receives an "arbitrary event", it normally doesn't do anything other than
+update the screen).
Main loop
---------
diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c
index 79ae7994f7..931363e199 100644
--- a/src/nvim/api/autocmd.c
+++ b/src/nvim/api/autocmd.c
@@ -1,8 +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 <assert.h>
#include <stdbool.h>
+#include <stdint.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include "lauxlib.h"
#include "nvim/api/autocmd.h"
@@ -12,7 +16,12 @@
#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
+#include "nvim/globals.h"
#include "nvim/lua/executor.h"
+#include "nvim/memory.h"
+#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/autocmd.c.generated.h"
@@ -38,7 +47,7 @@ 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>
+/// <pre>lua
/// -- Matches all criteria
/// autocommands = vim.api.nvim_get_autocmds({
/// group = "MyGroup",
@@ -59,6 +68,9 @@ static int64_t next_autocmd_id = 1;
/// - 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|.
+/// Cannot be used with {buffer}
+/// - buffer: Buffer number or list of buffer numbers for buffer local autocommands
+/// |autocmd-buflocal|. Cannot be used with {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).
@@ -240,7 +252,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
assert(pattern_filters[i]);
char *pat = pattern_filters[i];
- int patlen = (int)STRLEN(pat);
+ int patlen = (int)strlen(pat);
if (aupat_is_buflocal(pat, patlen)) {
aupat_normalize_buflocal_pat(pattern_buflocal,
@@ -349,82 +361,49 @@ cleanup:
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
+/// Creates an |autocommand| event handler, defined by `callback` (Lua function or Vimscript
+/// function _name_ string) or `command` (Ex command string).
///
+/// Example using Lua callback:
+/// <pre>lua
/// vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, {
/// pattern = {"*.c", "*.h"},
-/// callback = function() myluafun() end,
+/// callback = function(ev)
+/// print(string.format('event fired: %s', vim.inspect(ev)))
+/// end
/// })
/// </pre>
///
-/// Example using command:
-/// <pre>
+/// Example using an Ex command as the handler:
+/// <pre>lua
/// 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"}
+/// Note: `pattern` is NOT automatically expanded (unlike with |:autocmd|), thus names like "$HOME"
+/// and "~" must be expanded explicitly:
+/// <pre>lua
+/// pattern = vim.fn.expand("~") .. "/some/path/*.py"
/// </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
+/// @param event (string|array) Event(s) that will trigger the handler (`callback` or `command`).
+/// @param opts Options dict:
+/// - group (string|integer) optional: autocommand group name or id to match against.
+/// - pattern (string|array) optional: pattern(s) to match literally |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>|
+/// - desc (string) optional: description (for documentation and troubleshooting).
+/// - callback (function|string) optional: Lua function (or Vimscript function name, if
+/// string) called when the event(s) is triggered. Lua callback can return true to
+/// delete the autocommand, and receives a table argument with these keys:
+/// - id: (number) autocommand id
+/// - event: (string) name of the triggered event |autocmd-events|
+/// - group: (number|nil) autocommand group id, if any
+/// - match: (string) expanded value of |<amatch>|
+/// - buf: (number) expanded value of |<abuf>|
+/// - file: (string) 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}
@@ -433,7 +412,7 @@ cleanup:
/// - nested (boolean) optional: defaults to false. Run nested
/// autocommands |autocmd-nested|.
///
-/// @return Integer id of the created autocommand.
+/// @return Autocommand id (number)
/// @see |autocommand|
/// @see |nvim_del_autocmd()|
Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autocmd) *opts,
@@ -454,8 +433,7 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
}
if (opts->callback.type != kObjectTypeNil && opts->command.type != kObjectTypeNil) {
- api_set_error(err, kErrorTypeValidation,
- "cannot pass both: 'callback' and 'command' for the same autocmd");
+ api_set_error(err, kErrorTypeValidation, "specify either 'callback' or 'command', not both");
goto cleanup;
} else if (opts->callback.type != kObjectTypeNil) {
// TODO(tjdevries): It's possible we could accept callable tables,
@@ -596,7 +574,7 @@ void nvim_del_autocmd(Integer id, Error *err)
}
/// Clear all autocommands that match the corresponding {opts}. To delete
-/// a particular autocmd, see |nvim_del_autocmd|.
+/// a particular autocmd, see |nvim_del_autocmd()|.
/// @param opts Parameters
/// - event: (string|table)
/// Examples:
@@ -684,7 +662,7 @@ cleanup:
/// Create or get an autocommand group |autocmd-groups|.
///
/// To get an existing group id, do:
-/// <pre>
+/// <pre>lua
/// local id = vim.api.nvim_create_augroup("MyGroup", {
/// clear = false
/// })
@@ -725,7 +703,7 @@ Integer nvim_create_augroup(uint64_t channel_id, String name, Dict(create_augrou
///
/// To get a group id one can use |nvim_get_autocmds()|.
///
-/// NOTE: behavior differs from |augroup-delete|. When deleting a group, autocommands contained in
+/// 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()|
@@ -743,10 +721,10 @@ void nvim_del_augroup_by_id(Integer id, Error *err)
/// Delete an autocommand group by name.
///
-/// NOTE: behavior differs from |augroup-delete|. When deleting a group, autocommands contained in
+/// 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|
+/// @see |autocmd-groups|
void nvim_del_augroup_by_name(String name, Error *err)
FUNC_API_SINCE(9)
{
@@ -887,12 +865,12 @@ static bool check_autocmd_string_array(Array arr, char *k, Error *err)
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));
+ ADD(*array, copy_object(*v, NULL));
} else if (v->type == kObjectTypeArray) {
if (!check_autocmd_string_array(v->data.array, k, err)) {
return false;
}
- *array = copy_array(v->data.array);
+ *array = copy_array(v->data.array, NULL);
} else {
if (required) {
api_set_error(err,
@@ -962,7 +940,7 @@ static bool get_patterns_from_pattern_or_buf(Array *patterns, Object pattern, Ob
patlen = aucmd_pattern_length(pat);
}
} else if (v->type == kObjectTypeArray) {
- if (!check_autocmd_string_array(*patterns, "pattern", err)) {
+ if (!check_autocmd_string_array(v->data.array, "pattern", err)) {
return false;
}
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 5e90e40dd3..fe9e6077d6 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -3,25 +3,30 @@
// Some of this code was adapted from 'if_py_both.h' from the original
// vim source
+
+#include <assert.h>
#include <lauxlib.h>
-#include <limits.h>
#include <stdbool.h>
+#include <stddef.h>
#include <stdint.h>
-#include <stdlib.h>
+#include <string.h>
+#include "klib/kvec.h"
+#include "lua.h"
#include "nvim/api/buffer.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
+#include "nvim/ascii.h"
#include "nvim/autocmd.h"
#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
#include "nvim/buffer_updates.h"
#include "nvim/change.h"
#include "nvim/cursor.h"
-#include "nvim/decoration.h"
#include "nvim/drawscreen.h"
#include "nvim/ex_cmds.h"
-#include "nvim/ex_docmd.h"
#include "nvim/extmark.h"
+#include "nvim/globals.h"
#include "nvim/lua/executor.h"
#include "nvim/mapping.h"
#include "nvim/mark.h"
@@ -29,9 +34,10 @@
#include "nvim/memory.h"
#include "nvim/move.h"
#include "nvim/ops.h"
+#include "nvim/pos.h"
+#include "nvim/types.h"
#include "nvim/undo.h"
#include "nvim/vim.h"
-#include "nvim/window.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/buffer.c.generated.h"
@@ -78,7 +84,7 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err)
///
/// Example (Lua): capture buffer updates in a global `events` variable
/// (use "print(vim.inspect(events))" to see its contents):
-/// <pre>
+/// <pre>lua
/// events = {}
/// vim.api.nvim_buf_attach(0, false, {
/// on_lines=function(...) table.insert(events, {...}) end})
@@ -271,6 +277,7 @@ ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id,
Integer start,
Integer end,
Boolean strict_indexing,
+ lua_State *lstate,
Error *err)
FUNC_API_SINCE(1)
{
@@ -300,21 +307,18 @@ ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id,
return rv;
}
- rv.size = (size_t)(end - start);
- rv.items = xcalloc(rv.size, sizeof(Object));
+ size_t size = (size_t)(end - start);
+
+ init_line_array(lstate, &rv, size);
- if (!buf_collect_lines(buf, rv.size, start,
- (channel_id != VIML_INTERNAL_CALL), &rv, err)) {
+ if (!buf_collect_lines(buf, size, (linenr_T)start, 0, (channel_id != VIML_INTERNAL_CALL), &rv,
+ lstate, err)) {
goto end;
}
end:
if (ERROR_SET(err)) {
- for (size_t i = 0; i < rv.size; i++) {
- xfree(rv.items[i].data.string.data);
- }
-
- xfree(rv.items);
+ api_free_array(rv);
rv.items = NULL;
}
@@ -355,6 +359,8 @@ static bool check_string_array(Array arr, bool disallow_nl, Error *err)
/// Out-of-bounds indices are clamped to the nearest valid value, unless
/// `strict_indexing` is set.
///
+/// @see |nvim_buf_set_text()|
+///
/// @param channel_id
/// @param buffer Buffer handle, or 0 for current buffer
/// @param start First line index
@@ -527,6 +533,8 @@ end:
///
/// Prefer |nvim_buf_set_lines()| if you are only adding or deleting entire lines.
///
+/// @see |nvim_buf_set_lines()|
+///
/// @param channel_id
/// @param buffer Buffer handle, or 0 for current buffer
/// @param start_row First line index
@@ -570,7 +578,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
char *str_at_end = NULL;
// Another call to ml_get_buf() may free the line, so make a copy.
- str_at_start = xstrdup((char *)ml_get_buf(buf, (linenr_T)start_row, false));
+ str_at_start = xstrdup(ml_get_buf(buf, (linenr_T)start_row, false));
size_t len_at_start = strlen(str_at_start);
if (start_col < 0 || (size_t)start_col > len_at_start) {
api_set_error(err, kErrorTypeValidation, "start_col out of bounds");
@@ -578,7 +586,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
}
// Another call to ml_get_buf() may free the line, so make a copy.
- str_at_end = xstrdup((char *)ml_get_buf(buf, (linenr_T)end_row, false));
+ str_at_end = xstrdup(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");
@@ -608,7 +616,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
for (int64_t i = 1; i < end_row - start_row; i++) {
int64_t lnum = start_row + i;
- const char *bufline = (char *)ml_get_buf(buf, (linenr_T)lnum, false);
+ const char *bufline = ml_get_buf(buf, (linenr_T)lnum, false);
old_byte += (bcount_t)(strlen(bufline)) + 1;
}
old_byte += (bcount_t)end_col + 1;
@@ -786,7 +794,8 @@ early_end:
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)
+ Dictionary opts, lua_State *lstate,
+ Error *err)
FUNC_API_SINCE(9)
{
Array rv = ARRAY_DICT_INIT;
@@ -826,33 +835,37 @@ ArrayOf(String) nvim_buf_get_text(uint64_t channel_id, Buffer buffer,
bool replace_nl = (channel_id != VIML_INTERNAL_CALL);
+ size_t size = (size_t)(end_row - start_row) + 1;
+
+ init_line_array(lstate, &rv, size);
+
if (start_row == end_row) {
- String line = buf_get_text(buf, start_row, start_col, end_col, replace_nl, err);
+ String line = buf_get_text(buf, start_row, start_col, end_col, err);
if (ERROR_SET(err)) {
- return rv;
+ goto end;
}
-
- ADD(rv, STRING_OBJ(line));
+ push_linestr(lstate, &rv, line.data, line.size, 0, replace_nl);
return rv;
}
- rv.size = (size_t)(end_row - start_row) + 1;
- rv.items = xcalloc(rv.size, sizeof(Object));
+ String str = buf_get_text(buf, start_row, start_col, MAXCOL - 1, err);
+
+ push_linestr(lstate, &rv, str.data, str.size, 0, replace_nl);
- 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)) {
+ if (size > 2) {
+ if (!buf_collect_lines(buf, size - 2, (linenr_T)start_row + 1, 1, replace_nl, &rv, lstate,
+ err)) {
goto end;
}
}
- rv.items[rv.size - 1] = STRING_OBJ(buf_get_text(buf, end_row, 0, end_col, replace_nl, err));
+ str = buf_get_text(buf, end_row, 0, end_col, err);
+ push_linestr(lstate, &rv, str.data, str.size, (int)(size - 1), replace_nl);
+
if (ERROR_SET(err)) {
goto end;
}
@@ -945,7 +958,7 @@ Integer nvim_buf_get_changedtick(Buffer buffer, Error *err)
/// @param[out] err Error details, if any
/// @returns Array of |maparg()|-like dictionaries describing mappings.
/// The "buffer" key holds the associated buffer handle.
-ArrayOf(Dictionary) nvim_buf_get_keymap(uint64_t channel_id, Buffer buffer, String mode, Error *err)
+ArrayOf(Dictionary) nvim_buf_get_keymap(Buffer buffer, String mode, Error *err)
FUNC_API_SINCE(3)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
@@ -954,7 +967,7 @@ ArrayOf(Dictionary) nvim_buf_get_keymap(uint64_t channel_id, Buffer buffer, Stri
return (Array)ARRAY_DICT_INIT;
}
- return keymap_array(mode, buf, channel_id == LUA_INTERNAL_CALL);
+ return keymap_array(mode, buf);
}
/// Sets a buffer-local |mapping| for the given mode.
@@ -1021,7 +1034,7 @@ void nvim_buf_del_var(Buffer buffer, String name, Error *err)
/// @param buffer Buffer handle, or 0 for current buffer
/// @param[out] err Error details, if any
/// @return Buffer name
-String nvim_buf_get_name(Buffer buffer, Error *err)
+String nvim_buf_get_name(Buffer buffer, Arena *arena, Error *err)
FUNC_API_SINCE(1)
{
String rv = STRING_INIT;
@@ -1031,7 +1044,7 @@ String nvim_buf_get_name(Buffer buffer, Error *err)
return rv;
}
- return cstr_to_string((char *)buf->b_ffname);
+ return cstr_as_string(buf->b_ffname);
}
/// Sets the full file name for a buffer
@@ -1386,3 +1399,91 @@ static int64_t normalize_index(buf_T *buf, int64_t index, bool end_exclusive, bo
index++;
return index;
}
+
+/// Initialise a string array either:
+/// - on the Lua stack (as a table) (if lstate is not NULL)
+/// - as an API array object (if lstate is NULL).
+///
+/// @param lstate Lua state. When NULL the Array is initialized instead.
+/// @param a Array to initialize
+/// @param size Size of array
+static inline void init_line_array(lua_State *lstate, Array *a, size_t size)
+{
+ if (lstate) {
+ lua_createtable(lstate, (int)size, 0);
+ } else {
+ a->size = size;
+ a->items = xcalloc(a->size, sizeof(Object));
+ }
+}
+
+/// Push a string onto either the Lua stack (as a table element) or an API array object.
+///
+/// For Lua, a table of the correct size must be created first.
+/// API array objects must be pre allocated.
+///
+/// @param lstate Lua state. When NULL the Array is pushed to instead.
+/// @param a Array to push onto when not using Lua
+/// @param s String to push
+/// @param len Size of string
+/// @param idx 0-based index to place s
+/// @param replace_nl Replace newlines ('\n') with null ('\0')
+static void push_linestr(lua_State *lstate, Array *a, const char *s, size_t len, int idx,
+ bool replace_nl)
+{
+ if (lstate) {
+ // Vim represents NULs as NLs
+ if (s && replace_nl && strchr(s, '\n')) {
+ char *tmp = xmemdupz(s, len);
+ strchrsub(tmp, '\n', '\0');
+ lua_pushlstring(lstate, tmp, len);
+ xfree(tmp);
+ } else {
+ lua_pushlstring(lstate, s, len);
+ }
+ lua_rawseti(lstate, -2, idx + 1);
+ } else {
+ String str = STRING_INIT;
+ if (s) {
+ str = cbuf_to_string(s, len);
+ if (replace_nl) {
+ // Vim represents NULs as NLs, but this may confuse clients.
+ strchrsub(str.data, '\n', '\0');
+ }
+ }
+
+ a->items[idx] = STRING_OBJ(str);
+ }
+}
+
+/// Collects `n` buffer lines into array `l` and/or lua_State `lstate`, optionally replacing
+/// newlines with NUL.
+///
+/// @param buf Buffer to get lines from
+/// @param n Number of lines to collect
+/// @param replace_nl Replace newlines ("\n") with NUL
+/// @param start Line number to start from
+/// @param start_idx First index to push to
+/// @param[out] l If not NULL, Lines are copied here
+/// @param[out] lstate If not NULL, Lines are pushed into a table onto the stack
+/// @param err[out] Error, if any
+/// @return true unless `err` was set
+bool buf_collect_lines(buf_T *buf, size_t n, linenr_T start, int start_idx, bool replace_nl,
+ Array *l, lua_State *lstate, Error *err)
+{
+ for (size_t i = 0; i < n; i++) {
+ linenr_T lnum = start + (linenr_T)i;
+
+ if (lnum >= MAXLNUM) {
+ if (err != NULL) {
+ api_set_error(err, kErrorTypeValidation, "Line index is too high");
+ }
+ return false;
+ }
+
+ char *bufstr = ml_get_buf(buf, lnum, false);
+ push_linestr(lstate, l, bufstr, strlen(bufstr), start_idx + (int)i, replace_nl);
+ }
+
+ return true;
+}
diff --git a/src/nvim/api/buffer.h b/src/nvim/api/buffer.h
index 1c4a93a587..0814da63cd 100644
--- a/src/nvim/api/buffer.h
+++ b/src/nvim/api/buffer.h
@@ -1,7 +1,10 @@
#ifndef NVIM_API_BUFFER_H
#define NVIM_API_BUFFER_H
+#include <lauxlib.h>
+
#include "nvim/api/private/defs.h"
+#include "nvim/buffer_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/buffer.h.generated.h"
diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c
index 1323fc347b..abd265f2cf 100644
--- a/src/nvim/api/command.c
+++ b/src/nvim/api/command.c
@@ -1,21 +1,36 @@
// 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 <inttypes.h>
#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "klib/kvec.h"
+#include "lauxlib.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/ascii.h"
#include "nvim/autocmd.h"
+#include "nvim/buffer_defs.h"
+#include "nvim/decoration.h"
+#include "nvim/ex_cmds.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
+#include "nvim/garray.h"
+#include "nvim/globals.h"
#include "nvim/lua/executor.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
+#include "nvim/memory.h"
#include "nvim/ops.h"
+#include "nvim/pos.h"
#include "nvim/regexp.h"
+#include "nvim/strings.h"
+#include "nvim/types.h"
#include "nvim/usercmd.h"
+#include "nvim/vim.h"
#include "nvim/window.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -31,17 +46,18 @@
/// @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.
+/// - range: (array) (optional) Command range (|<line1>| |<line2>|).
+/// Omitted if command doesn't accept a range.
+/// Otherwise, has no elements if no range was specified, one element if
+/// only a single range item was specified, or two elements if both range
+/// items were specified.
+/// - count: (number) (optional) Command |<count>|.
+/// Omitted if command cannot take a count.
+/// - reg: (string) (optional) Command |<register>|.
+/// Omitted 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.
+/// - addr: (string) Value of |:command-addr|. Uses short name or "line" for -addr=lines.
/// - 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.
@@ -62,13 +78,14 @@
/// - browse: (boolean) |:browse|.
/// - confirm: (boolean) |:confirm|.
/// - hide: (boolean) |:hide|.
+/// - horizontal: (boolean) |:horizontal|.
/// - keepalt: (boolean) |:keepalt|.
/// - keepjumps: (boolean) |:keepjumps|.
/// - keepmarks: (boolean) |:keepmarks|.
/// - keeppatterns: (boolean) |:keeppatterns|.
/// - lockmarks: (boolean) |:lockmarks|.
/// - noswapfile: (boolean) |:noswapfile|.
-/// - tab: (integer) |:tab|.
+/// - tab: (integer) |:tab|. -1 when omitted.
/// - verbose: (integer) |:verbose|. -1 when omitted.
/// - vertical: (boolean) |:vertical|.
/// - split: (string) Split modifier string, is an empty string when there's no split
@@ -104,7 +121,7 @@ Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err)
// Parse arguments
Array args = ARRAY_DICT_INIT;
- size_t length = STRLEN(ea.arg);
+ size_t length = strlen(ea.arg);
// For nargs = 1 or '?', pass the entire argument list as a single argument,
// otherwise split arguments by whitespace.
@@ -141,15 +158,15 @@ Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err)
PUT(result, "cmd", CSTR_TO_OBJ((char *)get_command_name(NULL, ea.cmdidx)));
}
- if ((ea.argt & EX_RANGE) && ea.addr_count > 0) {
+ if (ea.argt & EX_RANGE) {
Array range = ARRAY_DICT_INIT;
- if (ea.addr_count > 1) {
- ADD(range, INTEGER_OBJ(ea.line1));
+ if (ea.addr_count > 0) {
+ if (ea.addr_count > 1) {
+ ADD(range, INTEGER_OBJ(ea.line1));
+ }
+ ADD(range, INTEGER_OBJ(ea.line2));
}
- 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) {
@@ -160,14 +177,12 @@ Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err)
} 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));
+ if (ea.argt & EX_REGSTR) {
+ char reg[2] = { (char)ea.regname, NUL };
+ PUT(result, "reg", CSTR_TO_OBJ(reg));
+ }
PUT(result, "bang", BOOLEAN_OBJ(ea.forceit));
PUT(result, "args", ARRAY_OBJ(args));
@@ -238,7 +253,7 @@ Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err)
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, "tab", INTEGER_OBJ(cmdinfo.cmdmod.cmod_tab - 1));
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));
@@ -250,6 +265,7 @@ Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err)
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));
+ PUT(mods, "horizontal", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_split & WSP_HOR));
const char *split;
if (cmdinfo.cmdmod.cmod_split & WSP_BOT) {
@@ -283,7 +299,11 @@ end:
/// 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.
+/// doesn't expand filenames, etc. Command arguments may also be Number, Boolean or String.
+///
+/// The first argument may also be used instead of count for commands that support it in order to
+/// make their usage simpler with |vim.cmd()|. For example, instead of
+/// `vim.cmd.bdelete{ count = 2 }`, you may do `vim.cmd.bdelete(2)`.
///
/// On execution error: fails with VimL error, updates v:errmsg.
///
@@ -308,8 +328,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
char *cmdline = NULL;
char *cmdname = NULL;
- ArrayOf(String) args;
- size_t argc = 0;
+ ArrayOf(String) args = ARRAY_DICT_INIT;
String retv = (String)STRING_INIT;
@@ -381,48 +400,70 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
if (cmd->args.type != kObjectTypeArray) {
VALIDATION_ERROR("'args' must be an Array");
}
- // Check if every argument is valid
+
+ // Process all arguments. Convert non-String arguments to String and check if String arguments
+ // have non-whitespace characters.
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");
+ char *data_str;
+
+ switch (elem.type) {
+ case kObjectTypeBoolean:
+ data_str = xcalloc(2, sizeof(char));
+ data_str[0] = elem.data.boolean ? '1' : '0';
+ data_str[1] = '\0';
+ break;
+ case kObjectTypeBuffer:
+ case kObjectTypeWindow:
+ case kObjectTypeTabpage:
+ case kObjectTypeInteger:
+ data_str = xcalloc(NUMBUFLEN, sizeof(char));
+ snprintf(data_str, NUMBUFLEN, "%" PRId64, elem.data.integer);
+ break;
+ case kObjectTypeString:
+ if (string_iswhite(elem.data.string)) {
+ VALIDATION_ERROR("String command argument must have at least one non-whitespace "
+ "character");
+ }
+ data_str = xstrndup(elem.data.string.data, elem.data.string.size);
+ break;
+ default:
+ VALIDATION_ERROR("Invalid type for command argument");
+ break;
}
+
+ ADD(args, STRING_OBJ(cstr_as_string(data_str)));
}
- 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;
+ argc_valid = args.size == 1;
break;
case EX_EXTRA | EX_NOSPC:
- argc_valid = argc <= 1;
+ argc_valid = args.size <= 1;
break;
case EX_EXTRA | EX_NEEDARG:
- argc_valid = argc >= 1;
+ argc_valid = args.size >= 1;
break;
case EX_EXTRA:
argc_valid = true;
break;
default:
- argc_valid = argc == 0;
+ argc_valid = args.size == 0;
break;
}
if (!argc_valid) {
VALIDATION_ERROR("Incorrect number of arguments supplied");
}
-
- args = cmd->args.data.array;
}
// 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.items[0].data.string.data : NULL);
+ set_cmd_addr_type(&ea, args.size > 0 ? args.items[0].data.string.data : NULL);
if (HAS_KEY(cmd->range)) {
if (!(ea.argt & EX_RANGE)) {
@@ -557,15 +598,17 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
}
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");
+ if (mods.tab.type != kObjectTypeInteger) {
+ VALIDATION_ERROR("'mods.tab' must be an Integer");
+ } else if ((int)mods.tab.data.integer >= 0) {
+ // Silently ignore negative integers to allow mods.tab to be set to -1.
+ cmdinfo.cmdmod.cmod_tab = (int)mods.tab.data.integer + 1;
}
- 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");
+ VALIDATION_ERROR("'mods.verbose' must be an 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;
@@ -576,6 +619,10 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
OBJ_TO_BOOL(vertical, mods.vertical, false, "'mods.vertical'");
cmdinfo.cmdmod.cmod_split |= (vertical ? WSP_VERT : 0);
+ bool horizontal;
+ OBJ_TO_BOOL(horizontal, mods.horizontal, false, "'mods.horizontal'");
+ cmdinfo.cmdmod.cmod_split |= (horizontal ? WSP_HOR : 0);
+
if (HAS_KEY(mods.split)) {
if (mods.split.type != kObjectTypeString) {
VALIDATION_ERROR("'mods.split' must be a String");
@@ -583,15 +630,15 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
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) {
+ } 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) {
+ } 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) {
+ } 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) {
+ } else if (strcmp(mods.split.data.string.data, "botright") == 0) {
cmdinfo.cmdmod.cmod_split |= WSP_BOT;
} else {
VALIDATION_ERROR("Invalid value for 'mods.split'");
@@ -613,6 +660,12 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
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_ERRSILENT) {
+ // CMOD_ERRSILENT must imply CMOD_SILENT, otherwise apply_cmdmod() and undo_cmdmod() won't
+ // work properly.
+ cmdinfo.cmdmod.cmod_flags |= CMOD_SILENT;
+ }
+
if ((cmdinfo.cmdmod.cmod_flags & CMOD_SANDBOX) && !(ea.argt & EX_SBOXOK)) {
VALIDATION_ERROR("Command cannot be run in sandbox");
}
@@ -620,7 +673,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
// 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);
+ build_cmdline_str(&cmdline, &ea, &cmdinfo, args);
ea.cmdlinep = &cmdline;
garray_T capture_local;
@@ -676,6 +729,7 @@ clear_ga:
ga_clear(&capture_local);
}
end:
+ api_free_array(args);
xfree(cmdline);
xfree(cmdname);
xfree(ea.args);
@@ -705,8 +759,9 @@ static bool string_iswhite(String str)
/// Build cmdline string for command, used by `nvim_cmd()`.
static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdinfo,
- ArrayOf(String) args, size_t argc)
+ ArrayOf(String) args)
{
+ size_t argc = args.size;
StringBuilder cmdline = KV_INITIAL_VALUE;
kv_resize(cmdline, 32); // Make it big enough to handle most typical commands
@@ -753,6 +808,7 @@ static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdin
} while (0)
CMDLINE_APPEND_IF(cmdinfo->cmdmod.cmod_split & WSP_VERT, "vertical ");
+ CMDLINE_APPEND_IF(cmdinfo->cmdmod.cmod_split & WSP_HOR, "horizontal ");
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 ");
@@ -791,7 +847,7 @@ static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdin
}
eap->argc = argc;
- eap->arglens = xcalloc(argc, sizeof(size_t));
+ eap->arglens = eap->argc > 0 ? xcalloc(argc, sizeof(size_t)) : NULL;
size_t argstart_idx = cmdline.size;
for (size_t i = 0; i < argc; i++) {
String s = args.items[i].data.string;
@@ -806,7 +862,7 @@ static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdin
// 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 *));
+ eap->args = eap->argc > 0 ? xcalloc(argc, sizeof(char *)) : NULL;
size_t offset = argstart_idx;
for (size_t i = 0; i < argc; i++) {
offset++; // Account for space
@@ -823,13 +879,12 @@ static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdin
// 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.
+ // If replace_makeprg() modified the cmdline string, correct the eap->arg pointer.
eap->arg = p;
- // We can only know the position of the first argument because the argument list can be used
- // multiple times in makeprg / grepprg.
- if (argc >= 1) {
- eap->args[0] = p;
- }
+ // This cannot be a user command, so eap->args will not be used.
+ XFREE_CLEAR(eap->args);
+ XFREE_CLEAR(eap->arglens);
+ eap->argc = 0;
}
}
@@ -840,7 +895,7 @@ static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdin
/// {command} is the replacement text or Lua function to execute.
///
/// Example:
-/// <pre>
+/// <pre>vim
/// :call nvim_create_user_command('SayHello', 'echo "Hello world!"', {})
/// :SayHello
/// Hello world!
@@ -850,6 +905,7 @@ static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdin
/// @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:
+/// - name: (string) Command name
/// - 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>|
@@ -929,7 +985,7 @@ void nvim_buf_del_user_command(Buffer buffer, String name, Error *err)
for (int i = 0; i < gap->ga_len; i++) {
ucmd_T *cmd = USER_CMD_GA(gap, i);
- if (!STRCMP(name.data, cmd->uc_name)) {
+ if (!strcmp(name.data, cmd->uc_name)) {
free_ucmd(cmd);
gap->ga_len -= 1;
diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c
index abaac07755..332e2b5fc3 100644
--- a/src/nvim/api/deprecated.c
+++ b/src/nvim/api/deprecated.c
@@ -1,7 +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
-#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
@@ -11,10 +10,15 @@
#include "nvim/api/extmark.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
-#include "nvim/api/vim.h"
#include "nvim/api/vimscript.h"
+#include "nvim/buffer_defs.h"
+#include "nvim/decoration.h"
#include "nvim/extmark.h"
+#include "nvim/globals.h"
#include "nvim/lua/executor.h"
+#include "nvim/memory.h"
+#include "nvim/pos.h"
+#include "nvim/types.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/deprecated.c.generated.h"
@@ -190,7 +194,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, NULL, err);
if (!ERROR_SET(err) && slice.size) {
rv = slice.items[0].data.string;
@@ -263,7 +267,7 @@ ArrayOf(String) buffer_get_line_slice(Buffer buffer,
{
start = convert_index(start) + !include_start;
end = convert_index(end) + include_end;
- return nvim_buf_get_lines(0, buffer, start, end, false, err);
+ return nvim_buf_get_lines(0, buffer, start, end, false, NULL, err);
}
/// Replaces a line range on the buffer
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c
index 933aa85530..ab3b3485e4 100644
--- a/src/nvim/api/extmark.c
+++ b/src/nvim/api/extmark.c
@@ -1,20 +1,29 @@
// 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 <stdlib.h>
+#include <string.h>
+#include "klib/kvec.h"
+#include "lauxlib.h"
#include "nvim/api/extmark.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
+#include "nvim/decoration.h"
#include "nvim/decoration_provider.h"
#include "nvim/drawscreen.h"
#include "nvim/extmark.h"
#include "nvim/highlight_group.h"
-#include "nvim/lua/executor.h"
+#include "nvim/mbyte.h"
#include "nvim/memline.h"
+#include "nvim/memory.h"
+#include "nvim/pos.h"
+#include "nvim/strings.h"
+#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/extmark.c.generated.h"
@@ -31,7 +40,7 @@ void api_extmark_free_all_mem(void)
map_destroy(String, handle_T)(&namespace_ids);
}
-/// Creates a new \*namespace\* or gets an existing one.
+/// Creates a new namespace or gets an existing one. \*namespace\*
///
/// Namespaces are used for buffer highlights and virtual text, see
/// |nvim_buf_add_highlight()| and |nvim_buf_set_extmark()|.
@@ -51,13 +60,13 @@ Integer nvim_create_namespace(String name)
}
id = next_namespace_id++;
if (name.size > 0) {
- String name_alloc = copy_string(name);
+ String name_alloc = copy_string(name, NULL);
map_put(String, handle_T)(&namespace_ids, name_alloc, id);
}
return (Integer)id;
}
-/// Gets existing, non-anonymous namespaces.
+/// Gets existing, non-anonymous |namespace|s.
///
/// @return dict that maps from names to namespace ids.
Dictionary nvim_get_namespaces(void)
@@ -87,7 +96,7 @@ const char *describe_ns(NS ns_id)
}
// Is the Namespace in use?
-static bool ns_initialized(uint32_t ns)
+bool ns_initialized(uint32_t ns)
{
if (ns < 1) {
return false;
@@ -186,7 +195,7 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict
return rv;
}
-/// Gets the position (0-indexed) of an extmark.
+/// Gets the position (0-indexed) of an |extmark|.
///
/// @param buffer Buffer handle, or 0 for current buffer
/// @param ns_id Namespace id from |nvim_create_namespace()|
@@ -240,31 +249,29 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
return extmark_to_array(&extmark, false, details);
}
-/// Gets extmarks in "traversal order" from a |charwise| region defined by
+/// Gets |extmarks| in "traversal order" from a |charwise| region defined by
/// buffer positions (inclusive, 0-indexed |api-indexing|).
///
/// Region can be given as (row,col) tuples, or valid extmark ids (whose
/// positions define the bounds). 0 and -1 are understood as (0,0) and (-1,-1)
/// respectively, thus the following are equivalent:
-///
-/// <pre>
-/// nvim_buf_get_extmarks(0, my_ns, 0, -1, {})
-/// nvim_buf_get_extmarks(0, my_ns, [0,0], [-1,-1], {})
+/// <pre>lua
+/// vim.api.nvim_buf_get_extmarks(0, my_ns, 0, -1, {})
+/// vim.api.nvim_buf_get_extmarks(0, my_ns, {0,0}, {-1,-1}, {})
/// </pre>
///
/// If `end` is less than `start`, traversal works backwards. (Useful
/// with `limit`, to get the first marks prior to a given position.)
///
/// Example:
-///
-/// <pre>
+/// <pre>lua
/// local a = vim.api
/// local pos = a.nvim_win_get_cursor(0)
/// local ns = a.nvim_create_namespace('my-plugin')
/// -- Create new extmark at line 1, column 1.
/// local m1 = a.nvim_buf_set_extmark(0, ns, 0, 0, {})
/// -- Create new extmark at line 3, column 1.
-/// local m2 = a.nvim_buf_set_extmark(0, ns, 0, 2, {})
+/// local m2 = a.nvim_buf_set_extmark(0, ns, 2, 0, {})
/// -- Get extmarks only from line 3.
/// local ms = a.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {})
/// -- Get all marks in this buffer + namespace.
@@ -361,7 +368,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
return rv;
}
-/// Creates or updates an extmark.
+/// Creates or updates an |extmark|.
///
/// 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
@@ -389,11 +396,11 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// - virt_text : virtual text to link to this mark.
/// A list of [text, highlight] tuples, each representing a
/// text chunk with specified highlight. `highlight` element
-/// can either be a a single highlight group, or an array of
+/// can either be a single highlight group, or an array of
/// multiple highlight groups that will be stacked
/// (highest priority last). A highlight group can be supplied
/// either as a string or as an integer, the latter which
-/// can be obtained using |nvim_get_hl_id_by_name|.
+/// can be obtained using |nvim_get_hl_id_by_name()|.
/// - virt_text_pos : position of virtual text. Possible values:
/// - "eol": right after eol character (default)
/// - "overlay": display over the specified column, without
@@ -430,7 +437,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// column of the window, bypassing
/// sign and number columns.
///
-/// - ephemeral : for use with |nvim_set_decoration_provider|
+/// - ephemeral : for use with |nvim_set_decoration_provider()|
/// callbacks. The mark will only be used for the current
/// redraw cycle, and not be permantently stored in the
/// buffer.
@@ -473,6 +480,8 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// 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|.
+/// - spell: boolean indicating that spell checking should be
+/// performed within this extmark
/// - 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
@@ -689,7 +698,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
}
if (opts->sign_text.type == kObjectTypeString) {
- if (!init_sign_text((char **)&decor.sign_text,
+ if (!init_sign_text(&decor.sign_text,
opts->sign_text.data.string.data)) {
api_set_error(err, kErrorTypeValidation, "sign_text is not a valid value");
goto error;
@@ -719,6 +728,15 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
bool ephemeral = false;
OPTION_TO_BOOL(ephemeral, ephemeral, false);
+ if (opts->spell.type == kObjectTypeNil) {
+ decor.spell = kNone;
+ } else {
+ bool spell = false;
+ OPTION_TO_BOOL(spell, spell, false);
+ decor.spell = spell ? kTrue : kFalse;
+ has_decor = true;
+ }
+
OPTION_TO_BOOL(decor.ui_watched, ui_watched, false);
if (decor.ui_watched) {
has_decor = true;
@@ -735,7 +753,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
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) {
@@ -754,7 +772,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
if (col2 >= 0) {
if (line2 >= 0 && line2 < buf->b_ml.ml_line_count) {
- len = ephemeral ? MAXCOL : STRLEN(ml_get_buf(buf, (linenr_T)line2 + 1, false));
+ len = ephemeral ? MAXCOL : strlen(ml_get_buf(buf, (linenr_T)line2 + 1, false));
} else if (line2 == buf->b_ml.ml_line_count) {
// We are trying to add an extmark past final newline
len = 0;
@@ -796,7 +814,7 @@ error:
return 0;
}
-/// Removes an extmark.
+/// Removes an |extmark|.
///
/// @param buffer Buffer handle, or 0 for current buffer
/// @param ns_id Namespace id from |nvim_create_namespace()|
@@ -826,9 +844,8 @@ uint32_t src2ns(Integer *src_id)
}
if (*src_id < 0) {
return (((uint32_t)1) << 31) - 1;
- } else {
- return (uint32_t)(*src_id);
}
+ return (uint32_t)(*src_id);
}
/// Adds a highlight to buffer.
@@ -912,7 +929,7 @@ Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, In
return ns_id;
}
-/// Clears namespaced objects (highlights, extmarks, virtual text) from
+/// Clears |namespace|d objects (highlights, |extmarks|, virtual text) from
/// a region.
///
/// Lines are 0-indexed. |api-indexing| To clear the namespace in the entire
@@ -945,13 +962,13 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start,
(int)line_end - 1, MAXCOL);
}
-/// Set or change decoration provider for a namespace
+/// Set or change decoration provider for a |namespace|
///
/// This is a very general purpose interface for having lua callbacks
/// being triggered during the redraw code.
///
-/// The expected usage is to set extmarks for the currently
-/// redrawn buffer. |nvim_buf_set_extmark| can be called to add marks
+/// The expected usage is to set |extmarks| for the currently
+/// redrawn buffer. |nvim_buf_set_extmark()| can be called to add marks
/// on a per-window or per-lines basis. Use the `ephemeral` key to only
/// use the mark for the current screen redraw (the callback will be called
/// again for the next redraw ).
@@ -972,20 +989,21 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start,
/// for the moment.
///
/// @param ns_id Namespace id from |nvim_create_namespace()|
-/// @param opts Callbacks invoked during redraw:
+/// @param opts Table of callbacks:
/// - on_start: called first on each screen redraw
/// ["start", tick]
-/// - on_buf: called for each buffer being redrawn (before window
-/// callbacks)
+/// - on_buf: called for each buffer being redrawn (before
+/// window callbacks)
/// ["buf", bufnr, tick]
-/// - on_win: called when starting to redraw a specific window.
+/// - on_win: called when starting to redraw a
+/// specific window.
/// ["win", winid, bufnr, topline, botline_guess]
-/// - on_line: called for each buffer line being redrawn. (The
-/// interaction with fold lines is subject to change)
+/// - on_line: called for each buffer line being redrawn.
+/// (The interaction with fold lines is subject to change)
/// ["win", winid, bufnr, row]
/// - on_end: called at the end of a redraw cycle
/// ["end", tick]
-void nvim_set_decoration_provider(Integer ns_id, DictionaryOf(LuaRef) opts, Error *err)
+void nvim_set_decoration_provider(Integer ns_id, Dict(set_decoration_provider) *opts, Error *err)
FUNC_API_SINCE(7) FUNC_API_LUA_ONLY
{
DecorProvider *p = get_decor_provider((NS)ns_id, true);
@@ -993,41 +1011,36 @@ void nvim_set_decoration_provider(Integer ns_id, DictionaryOf(LuaRef) opts, Erro
decor_provider_clear(p);
// regardless of what happens, it seems good idea to redraw
- redraw_all_later(NOT_VALID); // TODO(bfredl): too soon?
+ redraw_all_later(UPD_NOT_VALID); // TODO(bfredl): too soon?
struct {
const char *name;
+ Object *source;
LuaRef *dest;
} cbs[] = {
- { "on_start", &p->redraw_start },
- { "on_buf", &p->redraw_buf },
- { "on_win", &p->redraw_win },
- { "on_line", &p->redraw_line },
- { "on_end", &p->redraw_end },
- { "_on_hl_def", &p->hl_def },
- { NULL, NULL },
+ { "on_start", &opts->on_start, &p->redraw_start },
+ { "on_buf", &opts->on_buf, &p->redraw_buf },
+ { "on_win", &opts->on_win, &p->redraw_win },
+ { "on_line", &opts->on_line, &p->redraw_line },
+ { "on_end", &opts->on_end, &p->redraw_end },
+ { "_on_hl_def", &opts->_on_hl_def, &p->hl_def },
+ { "_on_spell_nav", &opts->_on_spell_nav, &p->spell_nav },
+ { NULL, NULL, NULL },
};
- for (size_t i = 0; i < opts.size; i++) {
- String k = opts.items[i].key;
- Object *v = &opts.items[i].value;
- size_t j;
- for (j = 0; cbs[j].name && cbs[j].dest; j++) {
- if (strequal(cbs[j].name, k.data)) {
- if (v->type != kObjectTypeLuaRef) {
- api_set_error(err, kErrorTypeValidation,
- "%s is not a function", cbs[j].name);
- goto error;
- }
- *(cbs[j].dest) = v->data.luaref;
- v->data.luaref = LUA_NOREF;
- break;
- }
+ for (size_t i = 0; cbs[i].source && cbs[i].dest && cbs[i].name; i++) {
+ Object *v = cbs[i].source;
+ if (v->type == kObjectTypeNil) {
+ continue;
}
- if (!cbs[j].name) {
- api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data);
+
+ if (v->type != kObjectTypeLuaRef) {
+ api_set_error(err, kErrorTypeValidation,
+ "%s is not a function", cbs[i].name);
goto error;
}
+ *(cbs[i].dest) = v->data.luaref;
+ v->data.luaref = LUA_NOREF;
}
p->active = true;
@@ -1038,7 +1051,7 @@ error:
decor_provider_clear(p);
}
-/// Gets the line and column of an extmark.
+/// 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".
@@ -1103,7 +1116,7 @@ static int init_sign_text(char **sign_text, char *text)
{
char *s;
- char *endp = text + (int)STRLEN(text);
+ char *endp = text + (int)strlen(text);
// Count cells and check for non-printable chars
int cells = 0;
diff --git a/src/nvim/api/extmark.h b/src/nvim/api/extmark.h
index 74802c6efb..0a627a889c 100644
--- a/src/nvim/api/extmark.h
+++ b/src/nvim/api/extmark.h
@@ -3,7 +3,10 @@
#include "nvim/api/private/defs.h"
#include "nvim/decoration.h"
+#include "nvim/macros.h"
#include "nvim/map.h"
+#include "nvim/map_defs.h"
+#include "nvim/types.h"
EXTERN Map(String, handle_T) namespace_ids INIT(= MAP_INIT);
EXTERN handle_T next_namespace_id INIT(= 1);
diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua
index 6fad52ba75..30dcef6127 100644
--- a/src/nvim/api/keysets.lua
+++ b/src/nvim/api/keysets.lua
@@ -1,8 +1,17 @@
return {
- context = {
+ { 'context', {
"types";
- };
- set_extmark = {
+ }};
+ { 'set_decoration_provider', {
+ "on_start";
+ "on_buf";
+ "on_win";
+ "on_line";
+ "on_end";
+ "_on_hl_def";
+ "_on_spell_nav";
+ }};
+ { 'set_extmark', {
"id";
"end_line";
"end_row";
@@ -28,9 +37,10 @@ return {
"line_hl_group";
"cursorline_hl_group";
"conceal";
+ "spell";
"ui_watched";
- };
- keymap = {
+ }};
+ { 'keymap', {
"noremap";
"nowait";
"silent";
@@ -40,11 +50,11 @@ return {
"callback";
"desc";
"replace_keycodes";
- };
- get_commands = {
+ }};
+ { 'get_commands', {
"builtin";
- };
- user_command = {
+ }};
+ { 'user_command', {
"addr";
"bang";
"bar";
@@ -57,8 +67,8 @@ return {
"preview";
"range";
"register";
- };
- float_config = {
+ }};
+ { 'float_config', {
"row";
"col";
"width";
@@ -71,27 +81,29 @@ return {
"focusable";
"zindex";
"border";
+ "title";
+ "title_pos";
"style";
"noautocmd";
- };
- runtime = {
+ }};
+ { 'runtime', {
"is_lua";
"do_source";
- };
- eval_statusline = {
+ }};
+ { 'eval_statusline', {
"winid";
"maxwidth";
"fillchar";
"highlights";
"use_winbar";
"use_tabline";
- };
- option = {
+ }};
+ { 'option', {
"scope";
"win";
"buf";
- };
- highlight = {
+ }};
+ { 'highlight', {
"bold";
"standout";
"strikethrough";
@@ -102,6 +114,7 @@ return {
"underdashed";
"italic";
"reverse";
+ "altfont";
"nocombine";
"default";
"cterm";
@@ -114,8 +127,10 @@ return {
"global_link";
"fallback";
"blend";
- };
- highlight_cterm = {
+ "fg_indexed";
+ "bg_indexed";
+ }};
+ { 'highlight_cterm', {
"bold";
"standout";
"strikethrough";
@@ -126,16 +141,17 @@ return {
"underdashed";
"italic";
"reverse";
+ "altfont";
"nocombine";
- };
+ }};
-- Autocmds
- clear_autocmds = {
+ { 'clear_autocmds', {
"buffer";
"event";
"group";
"pattern";
- };
- create_autocmd = {
+ }};
+ { 'create_autocmd', {
"buffer";
"callback";
"command";
@@ -144,24 +160,24 @@ return {
"nested";
"once";
"pattern";
- };
- exec_autocmds = {
+ }};
+ { 'exec_autocmds', {
"buffer";
"group";
"modeline";
"pattern";
"data";
- };
- get_autocmds = {
+ }};
+ { 'get_autocmds', {
"event";
"group";
"pattern";
"buffer";
- };
- create_augroup = {
+ }};
+ { 'create_augroup', {
"clear";
- };
- cmd = {
+ }};
+ { 'cmd', {
"cmd";
"range";
"count";
@@ -173,12 +189,12 @@ return {
"nargs";
"addr";
"nextcmd";
- };
- cmd_magic = {
+ }};
+ { 'cmd_magic', {
"file";
"bar";
- };
- cmd_mods = {
+ }};
+ { 'cmd_mods', {
"silent";
"emsg_silent";
"unsilent";
@@ -188,6 +204,7 @@ return {
"browse";
"confirm";
"hide";
+ "horizontal";
"keepalt";
"keepjumps";
"keepmarks";
@@ -198,13 +215,15 @@ return {
"verbose";
"vertical";
"split";
- };
- cmd_mods_filter = {
+ }};
+ { 'cmd_mods_filter', {
"pattern";
"force";
- };
- cmd_opts = {
+ }};
+ { 'cmd_opts', {
"output";
- };
+ }};
+ { 'echo_opts', {
+ "verbose";
+ }};
}
-
diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c
index 867584dd71..bfcb99754f 100644
--- a/src/nvim/api/options.c
+++ b/src/nvim/api/options.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 <assert.h>
#include <inttypes.h>
+#include <limits.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/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/autocmd.h"
-#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
+#include "nvim/eval/window.h"
+#include "nvim/globals.h"
+#include "nvim/memory.h"
#include "nvim/option.h"
-#include "nvim/option_defs.h"
+#include "nvim/vim.h"
#include "nvim/window.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -147,7 +148,7 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
/// @param name Option name
/// @param value New option value
/// @param opts Optional parameters
-/// - scope: One of 'global' or 'local'. Analogous to
+/// - 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.
@@ -202,7 +203,7 @@ void nvim_set_option_value(String name, Object value, Dict(option) *opts, Error
/// 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|.
+/// dictionaries as detailed at |nvim_get_option_info()|.
///
/// @return dictionary of all options
Dictionary nvim_get_all_options_info(Error *err)
@@ -256,7 +257,7 @@ void nvim_set_option(uint64_t channel_id, String name, Object value, Error *err)
/// @param name Option name
/// @param[out] err Error details, if any
/// @return Option value (global)
-Object nvim_get_option(String name, Error *err)
+Object nvim_get_option(String name, Arena *arena, Error *err)
FUNC_API_SINCE(1)
{
return get_option_from(NULL, SREQ_GLOBAL, name, err);
@@ -268,7 +269,7 @@ Object nvim_get_option(String name, Error *err)
/// @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)
+Object nvim_buf_get_option(Buffer buffer, String name, Arena *arena, Error *err)
FUNC_API_SINCE(1)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
@@ -306,7 +307,7 @@ void nvim_buf_set_option(uint64_t channel_id, Buffer buffer, String name, Object
/// @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)
+Object nvim_win_get_option(Window window, String name, Arena *arena, Error *err)
FUNC_API_SINCE(1)
{
win_T *win = find_window_by_handle(window, err);
@@ -346,7 +347,7 @@ void nvim_win_set_option(uint64_t channel_id, Window window, String name, Object
/// @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)
+static Object get_option_from(void *from, int type, String name, Error *err)
{
Object rv = OBJECT_INIT;
@@ -358,8 +359,7 @@ Object get_option_from(void *from, int type, String name, Error *err)
// Return values
int64_t numval;
char *stringval = NULL;
- int flags = get_option_value_strict(name.data, &numval, &stringval,
- type, from);
+ int flags = get_option_value_strict(name.data, &numval, &stringval, type, from);
if (!flags) {
api_set_error(err, kErrorTypeValidation, "Invalid option name: '%s'",
@@ -487,7 +487,7 @@ static getoption_T access_option_value(char *key, long *numval, char **stringval
bool get, Error *err)
{
if (get) {
- return get_option_value(key, numval, stringval, opt_flags);
+ return get_option_value(key, numval, stringval, NULL, opt_flags);
} else {
char *errmsg;
if ((errmsg = set_option_value(key, *numval, *stringval, opt_flags))) {
diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c
index 8724ef4432..58ff552ab7 100644
--- a/src/nvim/api/private/converter.c
+++ b/src/nvim/api/private/converter.c
@@ -2,17 +2,24 @@
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include <assert.h>
+#include <stdbool.h>
#include <stddef.h>
+#include <stdint.h>
#include <stdlib.h>
+#include "klib/kvec.h"
#include "nvim/api/private/converter.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/assert.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/eval/userfunc.h"
-#include "nvim/lua/converter.h"
+#include "nvim/garray.h"
#include "nvim/lua/executor.h"
+#include "nvim/memory.h"
+#include "nvim/types.h"
+#include "nvim/vim.h"
/// Helper structure for vim_to_object
typedef struct {
@@ -65,8 +72,8 @@ typedef struct {
#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
do { \
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); \
+ if (fp != NULL && (fp->uf_flags & FC_LUAREF)) { \
+ LuaRef ref = api_new_luaref(fp->uf_luaref); \
kvi_push(edata->stack, LUAREF_OBJ(ref)); \
} else { \
TYPVAL_ENCODE_CONV_NIL(tv); \
@@ -351,10 +358,7 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err)
}
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);
+ char *name = register_luafunc(api_new_luaref(obj.data.luaref));
tv->v_type = VAR_FUNC;
tv->vval.v_string = xstrdup(name);
break;
diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h
index 9c7e59e4b3..8acbf0d9de 100644
--- a/src/nvim/api/private/defs.h
+++ b/src/nvim/api/private/defs.h
@@ -5,14 +5,14 @@
#include <stdint.h>
#include <string.h>
+#include "klib/kvec.h"
#include "nvim/func_attr.h"
-#include "nvim/lib/kvec.h"
#include "nvim/types.h"
#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 }
+#define ERROR_INIT ((Error) { .type = kErrorTypeNone, .msg = NULL })
#define REMOTE_TYPE(type) typedef handle_T type
#define ERROR_SET(e) ((e)->type != kErrorTypeNone)
@@ -48,7 +48,7 @@ typedef enum {
/// Internal call from lua code
#define LUA_INTERNAL_CALL (VIML_INTERNAL_CALL + 1)
-static inline bool is_internal_call(const uint64_t channel_id)
+static inline bool is_internal_call(uint64_t channel_id)
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_CONST;
/// Check whether call is internal
diff --git a/src/nvim/api/private/dispatch.c b/src/nvim/api/private/dispatch.c
index d6a6fc1219..f427bba00e 100644
--- a/src/nvim/api/private/dispatch.c
+++ b/src/nvim/api/private/dispatch.c
@@ -1,38 +1,11 @@
// 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 <msgpack.h>
-#include <stdbool.h>
+#include <stddef.h>
-#include "nvim/api/deprecated.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/ui_client.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/private/dispatch_wrappers.generated.h"
diff --git a/src/nvim/api/private/dispatch.h b/src/nvim/api/private/dispatch.h
index 4b7c394944..4ae61b2bfb 100644
--- a/src/nvim/api/private/dispatch.h
+++ b/src/nvim/api/private/dispatch.h
@@ -1,22 +1,29 @@
#ifndef NVIM_API_PRIVATE_DISPATCH_H
#define NVIM_API_PRIVATE_DISPATCH_H
+#include <stdbool.h>
+#include <stdint.h>
+
#include "nvim/api/private/defs.h"
+#include "nvim/memory.h"
+#include "nvim/types.h"
-typedef Object (*ApiDispatchWrapper)(uint64_t channel_id,
- Array args,
- Error *error);
+typedef Object (*ApiDispatchWrapper)(uint64_t channel_id, Array args, Arena *arena, Error *error);
/// The rpc_method_handlers table, used in msgpack_rpc_dispatch(), stores
/// functions of this type.
-typedef struct {
+struct MsgpackRpcRequestHandler {
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).
// If "fast" is false, the function is deferred, i e the call will
// be put in the event queue, for safe handling later.
-} MsgpackRpcRequestHandler;
+ bool arena_return; // return value is allocated in the arena (or statically)
+ // and should not be freed as such.
+};
+
+extern const MsgpackRpcRequestHandler method_handlers[];
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/private/dispatch.h.generated.h"
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index c466fc53e1..519f2cc5bf 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -3,37 +3,36 @@
#include <assert.h>
#include <inttypes.h>
+#include <limits.h>
+#include <msgpack/unpack.h>
+#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include "klib/kvec.h"
#include "nvim/api/private/converter.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
-#include "nvim/api/vim.h"
#include "nvim/ascii.h"
-#include "nvim/assert.h"
-#include "nvim/buffer.h"
-#include "nvim/charset.h"
-#include "nvim/eval.h"
+#include "nvim/buffer_defs.h"
#include "nvim/eval/typval.h"
-#include "nvim/ex_cmds_defs.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/ex_eval.h"
-#include "nvim/extmark.h"
+#include "nvim/garray.h"
#include "nvim/highlight_group.h"
-#include "nvim/lib/kvec.h"
#include "nvim/lua/executor.h"
#include "nvim/map.h"
-#include "nvim/map_defs.h"
#include "nvim/mark.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
+#include "nvim/message.h"
#include "nvim/msgpack_rpc/helpers.h"
+#include "nvim/pos.h"
#include "nvim/ui.h"
#include "nvim/version.h"
-#include "nvim/vim.h"
-#include "nvim/window.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/private/funcs_metadata.generated.h"
@@ -58,6 +57,7 @@ void try_enter(TryState *const tstate)
.private_msg_list = NULL,
.trylevel = trylevel,
.got_int = got_int,
+ .did_throw = did_throw,
.need_rethrow = need_rethrow,
.did_emsg = did_emsg,
};
@@ -65,6 +65,7 @@ void try_enter(TryState *const tstate)
current_exception = NULL;
trylevel = 1;
got_int = false;
+ did_throw = false;
need_rethrow = false;
did_emsg = false;
}
@@ -85,6 +86,7 @@ bool try_leave(const TryState *const tstate, Error *const err)
assert(trylevel == 0);
assert(!need_rethrow);
assert(!got_int);
+ assert(!did_throw);
assert(!did_emsg);
assert(msg_list == &tstate->private_msg_list);
assert(*msg_list == NULL);
@@ -93,6 +95,7 @@ bool try_leave(const TryState *const tstate, Error *const err)
current_exception = tstate->current_exception;
trylevel = tstate->trylevel;
got_int = tstate->got_int;
+ did_throw = tstate->did_throw;
need_rethrow = tstate->need_rethrow;
did_emsg = tstate->did_emsg;
return ret;
@@ -127,7 +130,7 @@ bool try_end(Error *err)
force_abort = false;
if (got_int) {
- if (current_exception) {
+ if (did_throw) {
// If we got an interrupt, discard the current exception
discard_current_exception();
}
@@ -146,8 +149,19 @@ bool try_end(Error *err)
if (should_free) {
xfree(msg);
}
- } else if (current_exception) {
- api_set_error(err, kErrorTypeException, "%s", current_exception->value);
+ } else if (did_throw) {
+ if (*current_exception->throw_name != NUL) {
+ if (current_exception->throw_lnum != 0) {
+ api_set_error(err, kErrorTypeException, "%s, line %" PRIdLINENR ": %s",
+ current_exception->throw_name, current_exception->throw_lnum,
+ current_exception->value);
+ } else {
+ api_set_error(err, kErrorTypeException, "%s: %s",
+ current_exception->throw_name, current_exception->value);
+ }
+ } else {
+ api_set_error(err, kErrorTypeException, "%s", current_exception->value);
+ }
discard_current_exception();
}
@@ -214,6 +228,8 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, bool retva
return rv;
}
+ bool watched = tv_dict_is_watched(dict);
+
if (del) {
// Delete the key
if (di == NULL) {
@@ -221,6 +237,10 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, bool retva
api_set_error(err, kErrorTypeValidation, "Key not found: %s",
key.data);
} else {
+ // Notify watchers
+ if (watched) {
+ tv_dict_watcher_notify(dict, key.data, NULL, &di->di_tv);
+ }
// Return the old value
if (retval) {
rv = vim_to_object(&di->di_tv);
@@ -237,11 +257,16 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, bool retva
return rv;
}
+ typval_T oldtv = TV_INITIAL_VALUE;
+
if (di == NULL) {
// Need to create an entry
di = tv_dict_item_alloc_len(key.data, key.size);
tv_dict_add(dict, di);
} else {
+ if (watched) {
+ tv_copy(&di->di_tv, &oldtv);
+ }
// Return the old value
if (retval) {
rv = vim_to_object(&di->di_tv);
@@ -251,6 +276,13 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, bool retva
// Update the value
tv_copy(&tv, &di->di_tv);
+
+ // Notify watchers
+ if (watched) {
+ tv_dict_watcher_notify(dict, key.data, &tv, &oldtv);
+ tv_clear(&oldtv);
+ }
+
// Clear the temporary variable
tv_clear(&tv);
}
@@ -369,7 +401,13 @@ String cbuf_to_string(const char *buf, size_t size)
String cstrn_to_string(const char *str, size_t maxsize)
FUNC_ATTR_NONNULL_ALL
{
- return cbuf_to_string(str, STRNLEN(str, maxsize));
+ return cbuf_to_string(str, strnlen(str, maxsize));
+}
+
+String cstrn_as_string(char *str, size_t maxsize)
+ FUNC_ATTR_NONNULL_ALL
+{
+ return cbuf_as_string(str, strnlen(str, maxsize));
}
/// Creates a String using the given C string. Unlike
@@ -440,53 +478,15 @@ Array string_to_array(const String input, bool crlf)
return ret;
}
-/// Collects `n` buffer lines into array `l`, optionally replacing newlines
-/// with NUL.
-///
-/// @param buf Buffer to get lines from
-/// @param n Number of lines to collect
-/// @param replace_nl Replace newlines ("\n") with NUL
-/// @param start Line number to start from
-/// @param[out] l Lines are copied here
-/// @param err[out] Error, if any
-/// @return true unless `err` was set
-bool buf_collect_lines(buf_T *buf, size_t n, int64_t start, bool replace_nl, Array *l, Error *err)
-{
- for (size_t i = 0; i < n; i++) {
- int64_t lnum = start + (int64_t)i;
-
- if (lnum >= MAXLNUM) {
- if (err != NULL) {
- api_set_error(err, kErrorTypeValidation, "Line index is too high");
- }
- return false;
- }
-
- const char *bufstr = (char *)ml_get_buf(buf, (linenr_T)lnum, false);
- Object str = STRING_OBJ(cstr_to_string(bufstr));
-
- if (replace_nl) {
- // Vim represents NULs as NLs, but this may confuse clients.
- strchrsub(str.data.string.data, '\n', '\0');
- }
-
- l->items[i] = str;
- }
-
- 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 buf_get_text(buf_T *buf, int64_t lnum, int64_t start_col, int64_t end_col, Error *err)
{
String rv = STRING_INIT;
@@ -495,7 +495,7 @@ String buf_get_text(buf_T *buf, int64_t lnum, int64_t start_col, int64_t end_col
return rv;
}
- const char *bufstr = (char *)ml_get_buf(buf, (linenr_T)lnum, false);
+ char *bufstr = 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;
@@ -515,12 +515,7 @@ String buf_get_text(buf_T *buf, int64_t lnum, int64_t start_col, int64_t end_col
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;
+ return cstrn_as_string(&bufstr[start_col], (size_t)(end_col - start_col));
}
void api_free_string(String value)
@@ -618,6 +613,7 @@ void api_clear_error(Error *value)
value->type = kErrorTypeNone;
}
+/// @returns a shared value. caller must not modify it!
Dictionary api_metadata(void)
{
static Dictionary metadata = ARRAY_DICT_INIT;
@@ -630,7 +626,7 @@ Dictionary api_metadata(void)
init_type_metadata(&metadata);
}
- return copy_object(DICTIONARY_OBJ(metadata)).data.dictionary;
+ return metadata;
}
static void init_function_metadata(Dictionary *metadata)
@@ -715,36 +711,40 @@ static void init_type_metadata(Dictionary *metadata)
PUT(*metadata, "types", DICTIONARY_OBJ(types));
}
-String copy_string(String str)
+// all the copy_[object] functions allow arena=NULL,
+// then global allocations are used, and the resulting object
+// should be freed with an api_free_[object] function
+
+String copy_string(String str, Arena *arena)
{
if (str.data != NULL) {
- return (String){ .data = xmemdupz(str.data, str.size), .size = str.size };
+ return (String){ .data = arena_memdupz(arena, str.data, str.size), .size = str.size };
} else {
return (String)STRING_INIT;
}
}
-Array copy_array(Array array)
+Array copy_array(Array array, Arena *arena)
{
- Array rv = ARRAY_DICT_INIT;
+ Array rv = arena_array(arena, array.size);
for (size_t i = 0; i < array.size; i++) {
- ADD(rv, copy_object(array.items[i]));
+ ADD(rv, copy_object(array.items[i], arena));
}
return rv;
}
-Dictionary copy_dictionary(Dictionary dict)
+Dictionary copy_dictionary(Dictionary dict, Arena *arena)
{
- Dictionary rv = ARRAY_DICT_INIT;
+ Dictionary rv = arena_dict(arena, dict.size);
for (size_t i = 0; i < dict.size; i++) {
KeyValuePair item = dict.items[i];
- PUT(rv, item.key.data, copy_object(item.value));
+ PUT_C(rv, copy_string(item.key, arena).data, copy_object(item.value, arena));
}
return rv;
}
/// Creates a deep clone of an object
-Object copy_object(Object obj)
+Object copy_object(Object obj, Arena *arena)
{
switch (obj.type) {
case kObjectTypeBuffer:
@@ -757,13 +757,13 @@ Object copy_object(Object obj)
return obj;
case kObjectTypeString:
- return STRING_OBJ(copy_string(obj.data.string));
+ return STRING_OBJ(copy_string(obj.data.string, arena));
case kObjectTypeArray:
- return ARRAY_OBJ(copy_array(obj.data.array));
+ return ARRAY_OBJ(copy_array(obj.data.array, arena));
case kObjectTypeDictionary:
- return DICTIONARY_OBJ(copy_dictionary(obj.data.dictionary));
+ return DICTIONARY_OBJ(copy_dictionary(obj.data.dictionary, arena));
case kObjectTypeLuaRef:
return LUAREF_OBJ(api_new_luaref(obj.data.luaref));
@@ -844,7 +844,7 @@ HlMessage parse_hl_msg(Array chunks, Error *err)
goto free_exit;
}
- String str = copy_string(chunk.items[0].data.string);
+ String str = copy_string(chunk.items[0].data.string, NULL);
int attr = 0;
if (chunk.size == 2) {
diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h
index 4608554448..ec97ba9ec6 100644
--- a/src/nvim/api/private/helpers.h
+++ b/src/nvim/api/private/helpers.h
@@ -1,11 +1,17 @@
#ifndef NVIM_API_PRIVATE_HELPERS_H
#define NVIM_API_PRIVATE_HELPERS_H
+#include <stdbool.h>
+#include <stddef.h>
+
+#include "klib/kvec.h"
#include "nvim/api/private/defs.h"
#include "nvim/decoration.h"
#include "nvim/ex_eval_defs.h"
#include "nvim/getchar.h"
-#include "nvim/lib/kvec.h"
+#include "nvim/globals.h"
+#include "nvim/macros.h"
+#include "nvim/map.h"
#include "nvim/memory.h"
#include "nvim/vim.h"
@@ -134,6 +140,7 @@ typedef struct {
const msglist_T *const *msg_list;
int trylevel;
int got_int;
+ bool did_throw;
int need_rethrow;
int did_emsg;
} TryState;
diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c
index b81fc3b7d7..21eb326c3b 100644
--- a/src/nvim/api/tabpage.c
+++ b/src/nvim/api/tabpage.c
@@ -2,13 +2,14 @@
// 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/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/tabpage.h"
#include "nvim/api/vim.h"
+#include "nvim/buffer_defs.h"
+#include "nvim/globals.h"
#include "nvim/memory.h"
#include "nvim/window.h"
@@ -110,15 +111,14 @@ Window nvim_tabpage_get_win(Tabpage tabpage, Error *err)
if (tab == curtab) {
return nvim_get_current_win();
- } else {
- FOR_ALL_WINDOWS_IN_TAB(wp, tab) {
- if (wp == tab->tp_curwin) {
- return wp->handle;
- }
+ }
+ FOR_ALL_WINDOWS_IN_TAB(wp, tab) {
+ if (wp == tab->tp_curwin) {
+ return wp->handle;
}
- // There should always be a current window for a tabpage
- abort();
}
+ // There should always be a current window for a tabpage
+ abort();
}
/// Gets the tabpage number
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index 6f7bfa244a..e67607a7e4 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -2,67 +2,41 @@
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include <assert.h>
+#include <inttypes.h>
+#include <msgpack/pack.h>
#include <stdbool.h>
-#include <stddef.h>
#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include "klib/kvec.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/ui.h"
+#include "nvim/autocmd.h"
#include "nvim/channel.h"
-#include "nvim/cursor_shape.h"
+#include "nvim/event/loop.h"
+#include "nvim/event/wstream.h"
+#include "nvim/globals.h"
#include "nvim/grid.h"
#include "nvim/highlight.h"
+#include "nvim/main.h"
#include "nvim/map.h"
+#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/msgpack_rpc/channel.h"
+#include "nvim/msgpack_rpc/channel_defs.h"
#include "nvim/msgpack_rpc/helpers.h"
#include "nvim/option.h"
-#include "nvim/popupmenu.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
-#include "nvim/window.h"
-
-typedef struct {
- uint64_t channel_id;
-
-#define UI_BUF_SIZE 4096 ///< total buffer size for pending msgpack data.
- /// guaranteed 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 specific 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.
-
- // Position of legacy cursor, used both for drawing and visible user cursor.
- Integer client_row, client_col;
- 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"
+# include "ui_events_remote.generated.h" // IWYU pragma: export
#endif
static PMap(uint64_t) connected_uis = MAP_INIT;
@@ -122,7 +96,7 @@ static char *mpack_array_dyn16(char **buf)
static void mpack_str(char **buf, const char *str)
{
assert(sizeof(schar_T) - 1 < 0x20);
- size_t len = STRLEN(str);
+ size_t len = strlen(str);
mpack_w(buf, 0xa0 | len);
memcpy(*buf, str, len);
*buf += len;
@@ -137,8 +111,6 @@ void remote_ui_disconnect(uint64_t channel_id)
UIData *data = ui->data;
kv_destroy(data->call_buf);
pmap_del(uint64_t)(&connected_uis, channel_id);
- xfree(data);
- ui->data = NULL; // Flag UI as "stopped".
ui_detach_impl(ui, channel_id);
xfree(ui);
}
@@ -198,31 +170,6 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictiona
ui->pum_col = -1.0;
ui->rgb = true;
ui->override = false;
- ui->grid_resize = remote_ui_grid_resize;
- ui->grid_clear = remote_ui_grid_clear;
- ui->grid_cursor_goto = remote_ui_grid_cursor_goto;
- ui->mode_info_set = remote_ui_mode_info_set;
- ui->update_menu = remote_ui_update_menu;
- ui->busy_start = remote_ui_busy_start;
- ui->busy_stop = remote_ui_busy_stop;
- ui->mouse_on = remote_ui_mouse_on;
- ui->mouse_off = remote_ui_mouse_off;
- ui->mode_change = remote_ui_mode_change;
- ui->grid_scroll = remote_ui_grid_scroll;
- ui->hl_attr_define = remote_ui_hl_attr_define;
- ui->hl_group_set = remote_ui_hl_group_set;
- ui->raw_line = remote_ui_raw_line;
- ui->bell = remote_ui_bell;
- ui->visual_bell = remote_ui_visual_bell;
- ui->default_colors_set = remote_ui_default_colors_set;
- ui->flush = remote_ui_flush;
- ui->suspend = remote_ui_suspend;
- ui->set_title = remote_ui_set_title;
- ui->set_icon = remote_ui_set_icon;
- ui->option_set = remote_ui_option_set;
- ui->msg_set_pos = remote_ui_msg_set_pos;
- ui->event = remote_ui_event;
- ui->inspect = remote_ui_inspect;
CLEAR_FIELD(ui->ui_ext);
@@ -245,7 +192,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictiona
ui->ui_ext[kUICmdline] = true;
}
- UIData *data = xmalloc(sizeof(UIData));
+ UIData *data = ui->data;
data->channel_id = channel_id;
data->cur_event = NULL;
data->hl_id = 0;
@@ -260,7 +207,6 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictiona
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);
ui_attach_impl(ui, channel_id);
@@ -276,6 +222,19 @@ void ui_attach(uint64_t channel_id, Integer width, Integer height, Boolean enabl
api_free_dictionary(opts);
}
+/// Tells the nvim server if focus was gained or lost by the GUI
+void nvim_ui_set_focus(uint64_t channel_id, Boolean gained, Error *error)
+ FUNC_API_SINCE(11) FUNC_API_REMOTE_ONLY
+{
+ if (!pmap_has(uint64_t)(&connected_uis, channel_id)) {
+ api_set_error(error, kErrorTypeException,
+ "UI not attached to channel: %" PRId64, channel_id);
+ return;
+ }
+
+ do_autocmd_focusgained((bool)gained);
+}
+
/// Deactivates UI events on the channel.
///
/// Removes the client from the list of UIs. |nvim_list_uis()|
@@ -293,6 +252,10 @@ void nvim_ui_detach(uint64_t channel_id, Error *err)
remote_ui_disconnect(channel_id);
}
+// TODO(bfredl): use me to detach a specifc ui from the server
+void remote_ui_stop(UI *ui)
+{}
+
void nvim_ui_try_resize(uint64_t channel_id, Integer width, Integer height, Error *err)
FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
{
@@ -395,6 +358,24 @@ static void ui_set_option(UI *ui, bool init, String name, Object value, Error *e
return;
}
+ if (strequal(name.data, "stdin_tty")) {
+ if (value.type != kObjectTypeBoolean) {
+ api_set_error(error, kErrorTypeValidation, "stdin_tty must be a Boolean");
+ return;
+ }
+ stdin_isatty = value.data.boolean;
+ return;
+ }
+
+ if (strequal(name.data, "stdout_tty")) {
+ if (value.type != kObjectTypeBoolean) {
+ api_set_error(error, kErrorTypeValidation, "stdout_tty must be a Boolean");
+ return;
+ }
+ stdout_isatty = value.data.boolean;
+ return;
+ }
+
// LEGACY: Deprecated option, use `ext_cmdline` instead.
bool is_popupmenu = strequal(name.data, "popupmenu_external");
@@ -452,7 +433,7 @@ void nvim_ui_try_resize_grid(uint64_t channel_id, Integer grid, Integer width, I
}
}
-/// Tells Nvim the number of elements displaying in the popumenu, to decide
+/// Tells Nvim the number of elements displaying in the popupmenu, to decide
/// <PageUp> and <PageDown> movement.
///
/// @param channel_id
@@ -482,7 +463,7 @@ void nvim_ui_pum_set_height(uint64_t channel_id, Integer height, Error *err)
ui->pum_nlines = (int)height;
}
-/// Tells Nvim the geometry of the popumenu, to align floating windows with an
+/// Tells Nvim the geometry of the popupmenu, to align floating windows with an
/// external popup menu.
///
/// Note that this method is not to be confused with |nvim_ui_pum_set_height()|,
@@ -646,7 +627,7 @@ static void push_call(UI *ui, const char *name, Array args)
data->ncalls++;
}
-static void remote_ui_grid_clear(UI *ui, Integer grid)
+void remote_ui_grid_clear(UI *ui, Integer grid)
{
UIData *data = ui->data;
Array args = data->call_buf;
@@ -657,7 +638,7 @@ static void remote_ui_grid_clear(UI *ui, Integer grid)
push_call(ui, name, args);
}
-static void remote_ui_grid_resize(UI *ui, Integer grid, Integer width, Integer height)
+void remote_ui_grid_resize(UI *ui, Integer grid, Integer width, Integer height)
{
UIData *data = ui->data;
Array args = data->call_buf;
@@ -670,8 +651,8 @@ static void remote_ui_grid_resize(UI *ui, Integer grid, Integer width, Integer h
push_call(ui, name, args);
}
-static void remote_ui_grid_scroll(UI *ui, Integer grid, Integer top, Integer bot, Integer left,
- Integer right, Integer rows, Integer cols)
+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]) {
@@ -707,8 +688,8 @@ static void remote_ui_grid_scroll(UI *ui, Integer grid, Integer top, Integer bot
}
}
-static void remote_ui_default_colors_set(UI *ui, Integer rgb_fg, Integer rgb_bg, Integer rgb_sp,
- Integer cterm_fg, Integer cterm_bg)
+void remote_ui_default_colors_set(UI *ui, Integer rgb_fg, Integer rgb_bg, Integer rgb_sp,
+ Integer cterm_fg, Integer cterm_bg)
{
if (!ui->ui_ext[kUITermColors]) {
HL_SET_DEFAULT_COLORS(rgb_fg, rgb_bg, rgb_sp);
@@ -738,8 +719,8 @@ static void remote_ui_default_colors_set(UI *ui, Integer rgb_fg, Integer rgb_bg,
}
}
-static void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs,
- Array info)
+void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs,
+ Array info)
{
if (!ui->ui_ext[kUILinegrid]) {
return;
@@ -748,10 +729,12 @@ static void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAt
UIData *data = ui->data;
Array args = data->call_buf;
ADD_C(args, INTEGER_OBJ(id));
- MAXSIZE_TEMP_DICT(rgb, 16);
- MAXSIZE_TEMP_DICT(cterm, 16);
- ADD_C(args, DICTIONARY_OBJ(hlattrs2dict(&rgb, rgb_attrs, true)));
- ADD_C(args, DICTIONARY_OBJ(hlattrs2dict(&cterm, cterm_attrs, false)));
+ MAXSIZE_TEMP_DICT(rgb, HLATTRS_DICT_SIZE);
+ MAXSIZE_TEMP_DICT(cterm, HLATTRS_DICT_SIZE);
+ hlattrs2dict(&rgb, rgb_attrs, true);
+ hlattrs2dict(&cterm, rgb_attrs, false);
+ ADD_C(args, DICTIONARY_OBJ(rgb));
+ ADD_C(args, DICTIONARY_OBJ(cterm));
if (ui->ui_ext[kUIHlState]) {
ADD_C(args, ARRAY_OBJ(info));
@@ -762,7 +745,7 @@ static void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAt
push_call(ui, "hl_attr_define", args);
}
-static void remote_ui_highlight_set(UI *ui, int id)
+void remote_ui_highlight_set(UI *ui, int id)
{
UIData *data = ui->data;
Array args = data->call_buf;
@@ -771,13 +754,14 @@ static void remote_ui_highlight_set(UI *ui, int id)
return;
}
data->hl_id = id;
- MAXSIZE_TEMP_DICT(dict, 16);
- ADD_C(args, DICTIONARY_OBJ(hlattrs2dict(&dict, syn_attr2entry(id), ui->rgb)));
+ MAXSIZE_TEMP_DICT(dict, HLATTRS_DICT_SIZE);
+ hlattrs2dict(&dict, syn_attr2entry(id), ui->rgb);
+ ADD_C(args, DICTIONARY_OBJ(dict));
push_call(ui, "highlight_set", args);
}
/// "true" cursor used only for input focus
-static void remote_ui_grid_cursor_goto(UI *ui, Integer grid, Integer row, Integer col)
+void remote_ui_grid_cursor_goto(UI *ui, Integer grid, Integer row, Integer col)
{
if (ui->ui_ext[kUILinegrid]) {
UIData *data = ui->data;
@@ -795,7 +779,7 @@ static void remote_ui_grid_cursor_goto(UI *ui, Integer grid, Integer row, Intege
}
/// emulated cursor used both for drawing and for input focus
-static void remote_ui_cursor_goto(UI *ui, Integer row, Integer col)
+void remote_ui_cursor_goto(UI *ui, Integer row, Integer col)
{
UIData *data = ui->data;
if (data->client_row == row && data->client_col == col) {
@@ -809,7 +793,7 @@ static void remote_ui_cursor_goto(UI *ui, Integer row, Integer col)
push_call(ui, "cursor_goto", args);
}
-static void remote_ui_put(UI *ui, const char *cell)
+void remote_ui_put(UI *ui, const char *cell)
{
UIData *data = ui->data;
data->client_col++;
@@ -818,9 +802,9 @@ static void remote_ui_put(UI *ui, const char *cell)
push_call(ui, "put", args);
}
-static void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Integer endcol,
- Integer clearcol, Integer clearattr, LineFlags flags,
- const schar_T *chunk, const sattr_T *attrs)
+void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Integer endcol,
+ Integer clearcol, Integer clearattr, LineFlags flags, const schar_T *chunk,
+ const sattr_T *attrs)
{
UIData *data = ui->data;
if (ui->ui_ext[kUILinegrid]) {
@@ -841,7 +825,7 @@ static void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startc
for (size_t i = 0; i < ncells; i++) {
repeat++;
if (i == ncells - 1 || attrs[i] != attrs[i + 1]
- || STRCMP(chunk[i], chunk[i + 1])) {
+ || strcmp(chunk[i], chunk[i + 1]) != 0) {
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.
@@ -912,7 +896,7 @@ static void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startc
///
/// 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)
+void remote_ui_flush_buf(UI *ui)
{
UIData *data = ui->data;
if (!data->nevents_pos) {
@@ -939,7 +923,7 @@ static void remote_ui_flush_buf(UI *ui)
///
/// Clients can know this happened by a final "flush" event at the end of the
/// "redraw" batch.
-static void remote_ui_flush(UI *ui)
+void remote_ui_flush(UI *ui)
{
UIData *data = ui->data;
if (data->nevents > 0 || data->flushed_events) {
@@ -952,65 +936,63 @@ static void remote_ui_flush(UI *ui)
}
}
-static Array translate_contents(UI *ui, Array contents)
+static Array translate_contents(UI *ui, Array contents, Arena *arena)
{
- Array new_contents = ARRAY_DICT_INIT;
+ Array new_contents = arena_array(arena, contents.size);
for (size_t i = 0; i < contents.size; i++) {
Array item = contents.items[i].data.array;
- Array new_item = ARRAY_DICT_INIT;
+ Array new_item = arena_array(arena, 2);
int attr = (int)item.items[0].data.integer;
if (attr) {
- Dictionary rgb_attrs = hlattrs2dict(NULL, syn_attr2entry(attr), ui->rgb);
+ Dictionary rgb_attrs = arena_dict(arena, HLATTRS_DICT_SIZE);
+ hlattrs2dict(&rgb_attrs, syn_attr2entry(attr), ui->rgb);
ADD(new_item, DICTIONARY_OBJ(rgb_attrs));
} else {
ADD(new_item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT));
}
- ADD(new_item, copy_object(item.items[1]));
+ ADD(new_item, item.items[1]);
ADD(new_contents, ARRAY_OBJ(new_item));
}
return new_contents;
}
-static Array translate_firstarg(UI *ui, Array args)
+static Array translate_firstarg(UI *ui, Array args, Arena *arena)
{
- Array new_args = ARRAY_DICT_INIT;
+ Array new_args = arena_array(arena, args.size);
Array contents = args.items[0].data.array;
- ADD(new_args, ARRAY_OBJ(translate_contents(ui, contents)));
+ ADD_C(new_args, ARRAY_OBJ(translate_contents(ui, contents, arena)));
for (size_t i = 1; i < args.size; i++) {
- ADD(new_args, copy_object(args.items[i]));
+ ADD(new_args, args.items[i]);
}
return new_args;
}
-static void remote_ui_event(UI *ui, char *name, Array args)
+void remote_ui_event(UI *ui, char *name, Array args)
{
+ Arena arena = ARENA_EMPTY;
UIData *data = ui->data;
if (!ui->ui_ext[kUILinegrid]) {
// the representation of highlights in cmdline changed, translate back
// never consumes args
if (strequal(name, "cmdline_show")) {
- Array new_args = translate_firstarg(ui, args);
+ Array new_args = translate_firstarg(ui, args, &arena);
push_call(ui, name, new_args);
- api_free_array(new_args);
- return;
+ goto free_ret;
} else if (strequal(name, "cmdline_block_show")) {
Array new_args = data->call_buf;
Array block = args.items[0].data.array;
- Array new_block = ARRAY_DICT_INIT;
+ Array new_block = arena_array(&arena, block.size);
for (size_t i = 0; i < block.size; i++) {
- ADD(new_block,
- ARRAY_OBJ(translate_contents(ui, block.items[i].data.array)));
+ ADD_C(new_block, ARRAY_OBJ(translate_contents(ui, block.items[i].data.array, &arena)));
}
ADD_C(new_args, ARRAY_OBJ(new_block));
push_call(ui, name, new_args);
- api_free_array(new_block);
- return;
+ goto free_ret;
} else if (strequal(name, "cmdline_block_append")) {
- Array new_args = translate_firstarg(ui, args);
+ Array new_args = translate_firstarg(ui, args, &arena);
push_call(ui, name, new_args);
- api_free_array(new_args);
- return;
+ goto free_ret;
}
}
@@ -1022,19 +1004,18 @@ static void remote_ui_event(UI *ui, char *name, Array args)
if (data->wildmenu_active) {
Array new_args = data->call_buf;
Array items = args.items[0].data.array;
- Array new_items = ARRAY_DICT_INIT;
+ Array new_items = arena_array(&arena, items.size);
for (size_t i = 0; i < items.size; i++) {
- ADD(new_items, copy_object(items.items[i].data.array.items[0]));
+ ADD_C(new_items, items.items[i].data.array.items[0]);
}
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 = data->call_buf;
ADD_C(new_args2, args.items[1]);
push_call(ui, "wildmenu_select", new_args2);
}
- return;
+ goto free_ret;
}
} else if (strequal(name, "popupmenu_select")) {
if (data->wildmenu_active) {
@@ -1048,9 +1029,13 @@ static void remote_ui_event(UI *ui, char *name, Array args)
}
push_call(ui, name, args);
+ return;
+
+free_ret:
+ arena_mem_free(arena_finish(&arena));
}
-static void remote_ui_inspect(UI *ui, Dictionary *info)
+void remote_ui_inspect(UI *ui, Dictionary *info)
{
UIData *data = ui->data;
PUT(*info, "chan", INTEGER_OBJ((Integer)data->channel_id));
diff --git a/src/nvim/api/ui.h b/src/nvim/api/ui.h
index bc70406acb..b3fe0fa2bb 100644
--- a/src/nvim/api/ui.h
+++ b/src/nvim/api/ui.h
@@ -5,8 +5,10 @@
#include "nvim/api/private/defs.h"
#include "nvim/map.h"
+#include "nvim/ui.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/ui.h.generated.h"
+# include "ui_events_remote.h.generated.h"
#endif
#endif // NVIM_API_UI_H
diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h
index 8b7e01e1c3..a08e8dbfeb 100644
--- a/src/nvim/api/ui_events.in.h
+++ b/src/nvim/api/ui_events.in.h
@@ -31,15 +31,15 @@ void visual_bell(void)
void flush(void)
FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL;
void suspend(void)
- FUNC_API_SINCE(3) FUNC_API_BRIDGE_IMPL;
+ FUNC_API_SINCE(3);
void set_title(String title)
FUNC_API_SINCE(3);
void set_icon(String icon)
FUNC_API_SINCE(3);
void screenshot(String path)
- FUNC_API_SINCE(7) FUNC_API_REMOTE_IMPL;
+ FUNC_API_SINCE(7);
void option_set(String name, Object value)
- FUNC_API_SINCE(4) FUNC_API_BRIDGE_IMPL;
+ FUNC_API_SINCE(4);
// Stop event is not exported as such, represented by EOF in the msgpack stream.
void stop(void)
FUNC_API_NOEXPORT;
@@ -69,14 +69,13 @@ void scroll(Integer count)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
// Second revision of the grid protocol, used with ext_linegrid ui option
-void default_colors_set(Integer rgb_fg, Integer rgb_bg, Integer rgb_sp,
- Integer cterm_fg, Integer cterm_bg)
+void default_colors_set(Integer rgb_fg, Integer rgb_bg, Integer rgb_sp, Integer cterm_fg,
+ Integer cterm_bg)
FUNC_API_SINCE(4) FUNC_API_REMOTE_IMPL;
-void hl_attr_define(Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs,
- Array info)
- FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_BRIDGE_IMPL;
+void hl_attr_define(Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs, Array info)
+ FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL;
void hl_group_set(String name, Integer id)
- FUNC_API_SINCE(6) FUNC_API_BRIDGE_IMPL;
+ FUNC_API_SINCE(6) FUNC_API_CLIENT_IGNORE;
void grid_resize(Integer grid, Integer width, Integer height)
FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL FUNC_API_CLIENT_IMPL;
void grid_clear(Integer grid)
@@ -85,8 +84,8 @@ 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_CLIENT_IMPL;
-void grid_scroll(Integer grid, Integer top, Integer bot,
- Integer left, Integer right, Integer rows, Integer cols)
+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;
void grid_destroy(Integer grid)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
@@ -94,20 +93,15 @@ void grid_destroy(Integer grid)
// For performance and simplicity, we use the dense screen representation
// in internal code, such as compositor and TUI. The remote_ui module will
// translate this in to the public grid_line format.
-void raw_line(Integer grid, Integer row, Integer startcol,
- Integer endcol, Integer clearcol, Integer clearattr,
- LineFlags flags, const schar_T *chunk, const sattr_T *attrs)
+void raw_line(Integer grid, Integer row, Integer startcol, Integer endcol, Integer clearcol,
+ Integer clearattr, LineFlags flags, const schar_T *chunk, const sattr_T *attrs)
FUNC_API_NOEXPORT FUNC_API_COMPOSITOR_IMPL;
-void event(char *name, Array args)
- FUNC_API_NOEXPORT;
-
-void win_pos(Integer grid, Window win, Integer startrow,
- Integer startcol, Integer width, Integer height)
+void win_pos(Integer grid, Window win, Integer startrow, Integer startcol, Integer width,
+ Integer height)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
-void win_float_pos(Integer grid, Window win, String anchor, Integer anchor_grid,
- Float anchor_row, Float anchor_col, Boolean focusable,
- Integer zindex)
+void win_float_pos(Integer grid, Window win, String anchor, Integer anchor_grid, Float anchor_row,
+ Float anchor_col, Boolean focusable, Integer zindex)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
void win_external_pos(Integer grid, Window win)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
@@ -115,32 +109,29 @@ void win_hide(Integer grid)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
void win_close(Integer grid)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
+
void msg_set_pos(Integer grid, Integer row, Boolean scrolled, String sep_char)
- FUNC_API_SINCE(6) FUNC_API_BRIDGE_IMPL FUNC_API_COMPOSITOR_IMPL;
+ FUNC_API_SINCE(6) FUNC_API_COMPOSITOR_IMPL FUNC_API_CLIENT_IGNORE;
-void win_viewport(Integer grid, Window win, Integer topline,
- Integer botline, Integer curline, Integer curcol,
- Integer line_count)
- FUNC_API_SINCE(7) FUNC_API_REMOTE_ONLY;
+void win_viewport(Integer grid, Window win, Integer topline, Integer botline, Integer curline,
+ Integer curcol, Integer line_count)
+ FUNC_API_SINCE(7) FUNC_API_CLIENT_IGNORE;
-void win_extmark(Integer grid, Window win, Integer ns_id, Integer mark_id,
- Integer row, Integer col)
+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)
+void popupmenu_show(Array items, Integer selected, Integer row, Integer col, Integer grid)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void popupmenu_hide(void)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void popupmenu_select(Integer selected)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
-void tabline_update(Tabpage current, Array tabs,
- Buffer current_buffer, Array buffers)
+void tabline_update(Tabpage current, Array tabs, Buffer current_buffer, Array buffers)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
-void cmdline_show(Array content, Integer pos, String firstc, String prompt,
- Integer indent, Integer level)
+void cmdline_show(Array content, Integer pos, String firstc, String prompt, Integer indent,
+ Integer level)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void cmdline_pos(Integer pos, Integer level)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
@@ -156,11 +147,11 @@ void cmdline_block_hide(void)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void wildmenu_show(Array items)
- FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL FUNC_API_BRIDGE_IMPL;
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void wildmenu_select(Integer selected)
- FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL FUNC_API_BRIDGE_IMPL;
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void wildmenu_hide(void)
- FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL FUNC_API_BRIDGE_IMPL;
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void msg_show(String kind, Array content, Boolean replace_last)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index ce91c1b4af..a53b30dd8a 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -5,9 +5,13 @@
#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include "klib/kvec.h"
+#include "lauxlib.h"
#include "nvim/api/buffer.h"
#include "nvim/api/deprecated.h"
#include "nvim/api/private/converter.h"
@@ -15,58 +19,55 @@
#include "nvim/api/private/dispatch.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/vim.h"
-#include "nvim/api/window.h"
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
-#include "nvim/buffer_defs.h"
-#include "nvim/charset.h"
+#include "nvim/channel.h"
#include "nvim/context.h"
-#include "nvim/decoration.h"
-#include "nvim/decoration_provider.h"
#include "nvim/drawscreen.h"
-#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
-#include "nvim/eval/userfunc.h"
-#include "nvim/ex_cmds_defs.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
-#include "nvim/file_search.h"
-#include "nvim/fileio.h"
#include "nvim/getchar.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
#include "nvim/highlight.h"
-#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
-#include "nvim/insexpand.h"
+#include "nvim/keycodes.h"
+#include "nvim/log.h"
#include "nvim/lua/executor.h"
+#include "nvim/macros.h"
#include "nvim/mapping.h"
#include "nvim/mark.h"
+#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/move.h"
#include "nvim/msgpack_rpc/channel.h"
-#include "nvim/msgpack_rpc/helpers.h"
+#include "nvim/msgpack_rpc/channel_defs.h"
#include "nvim/msgpack_rpc/unpacker.h"
#include "nvim/ops.h"
#include "nvim/option.h"
#include "nvim/optionstr.h"
#include "nvim/os/input.h"
+#include "nvim/os/os_defs.h"
#include "nvim/os/process.h"
#include "nvim/popupmenu.h"
+#include "nvim/pos.h"
#include "nvim/runtime.h"
#include "nvim/state.h"
#include "nvim/statusline.h"
+#include "nvim/strings.h"
+#include "nvim/terminal.h"
#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
-#include "nvim/viml/parser/expressions.h"
-#include "nvim/viml/parser/parser.h"
#include "nvim/window.h"
-#define LINE_BUFFER_SIZE 4096
+#define LINE_BUFFER_MIN_SIZE 4096
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/vim.c.generated.h"
@@ -79,19 +80,17 @@
/// @param[out] err Error details, if any
/// @return Highlight definition map
/// @see nvim_get_hl_by_id
-Dictionary nvim_get_hl_by_name(String name, Boolean rgb, Error *err)
+Dictionary nvim_get_hl_by_name(String name, Boolean rgb, Arena *arena, Error *err)
FUNC_API_SINCE(3)
{
Dictionary result = ARRAY_DICT_INIT;
int id = syn_name2id(name.data);
if (id == 0) {
- api_set_error(err, kErrorTypeException, "Invalid highlight name: %s",
- name.data);
+ api_set_error(err, kErrorTypeException, "Invalid highlight name: %s", name.data);
return result;
}
- result = nvim_get_hl_by_id(id, rgb, err);
- return result;
+ return nvim_get_hl_by_id(id, rgb, arena, err);
}
/// Gets a highlight definition by id. |hlID()|
@@ -100,17 +99,16 @@ Dictionary nvim_get_hl_by_name(String name, Boolean rgb, Error *err)
/// @param[out] err Error details, if any
/// @return Highlight definition map
/// @see nvim_get_hl_by_name
-Dictionary nvim_get_hl_by_id(Integer hl_id, Boolean rgb, Error *err)
+Dictionary nvim_get_hl_by_id(Integer hl_id, Boolean rgb, Arena *arena, Error *err)
FUNC_API_SINCE(3)
{
Dictionary dic = ARRAY_DICT_INIT;
if (syn_get_final_id((int)hl_id) == 0) {
- api_set_error(err, kErrorTypeException,
- "Invalid highlight id: %" PRId64, hl_id);
+ api_set_error(err, kErrorTypeException, "Invalid highlight id: %" PRId64, hl_id);
return dic;
}
int attrcode = syn_id2attr((int)hl_id);
- return hl_get_attr_by_id(attrcode, rgb, err);
+ return hl_get_attr_by_id(attrcode, rgb, arena, err);
}
/// Gets a highlight group by name
@@ -122,10 +120,10 @@ Integer nvim_get_hl_id_by_name(String name)
return syn_check_group(name.data, name.size);
}
-Dictionary nvim__get_hl_defs(Integer ns_id, Error *err)
+Dictionary nvim__get_hl_defs(Integer ns_id, Arena *arena, Error *err)
{
if (ns_id == 0) {
- return get_global_hl_defs();
+ return get_global_hl_defs(arena);
}
abort();
}
@@ -163,8 +161,8 @@ Dictionary nvim__get_hl_defs(Integer ns_id, Error *err)
/// - 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|
+/// - ctermfg: Sets foreground of cterm color |ctermfg|
+/// - ctermbg: Sets background of cterm color |ctermbg|
/// - cterm: cterm attribute map, like |highlight-args|. If not set,
/// cterm attributes will match those from the attribute map
/// documented above.
@@ -175,6 +173,10 @@ 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, name.size);
+ if (hl_id == 0) {
+ api_set_error(err, kErrorTypeException, "Invalid highlight name: %s", name.data);
+ return;
+ }
int link_id = -1;
HlAttrs attrs = dict2hlattrs(val, true, &link_id, err);
@@ -184,7 +186,7 @@ void nvim_set_hl(Integer ns_id, String name, Dict(highlight) *val, Error *err)
}
/// Set active namespace for highlights. This can be set for a single window,
-/// see |nvim_win_set_hl_ns|.
+/// see |nvim_win_set_hl_ns()|.
///
/// @param ns_id the namespace to use
/// @param[out] err Error details, if any
@@ -198,13 +200,13 @@ void nvim_set_hl_ns(Integer ns_id, Error *err)
ns_hl_global = (NS)ns_id;
hl_check_ns();
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
}
/// Set active namespace for highlights while redrawing.
///
/// This function meant to be called while redrawing, primarily from
-/// |nvim_set_decoration_provider| on_win and on_line callbacks, which
+/// |nvim_set_decoration_provider()| on_win and on_line callbacks, which
/// are allowed to change the namespace during a redraw cycle.
///
/// @param ns_id the namespace to activate
@@ -227,14 +229,14 @@ void nvim_set_hl_ns_fast(Integer ns_id, Error *err)
/// nvim_feedkeys().
///
/// Example:
-/// <pre>
+/// <pre>vim
/// :let key = nvim_replace_termcodes("<C-o>", v:true, v:false, v:true)
/// :call nvim_feedkeys(key, 'n', v:false)
/// </pre>
///
/// @param keys to be typed
/// @param mode behavior flags, see |feedkeys()|
-/// @param escape_ks If true, escape K_SPECIAL 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()
@@ -335,9 +337,9 @@ Integer nvim_input(String keys)
/// mouse input in a GUI. The deprecated pseudokey form
/// ("<LeftMouse><col,row>") of |nvim_input()| has the same limitation.
///
-/// @param button Mouse button: one of "left", "right", "middle", "wheel".
+/// @param button Mouse button: one of "left", "right", "middle", "wheel", "move".
/// @param action For ordinary buttons, one of "press", "drag", "release".
-/// For the wheel, one of "up", "down", "left", "right".
+/// For the wheel, one of "up", "down", "left", "right". Ignored for "move".
/// @param modifier String of modifiers each represented by a single char.
/// The same specifiers are used as for a key press, except
/// that the "-" separator is optional, so "C-A-", "c-a"
@@ -364,6 +366,8 @@ void nvim_input_mouse(String button, String action, String modifier, Integer gri
code = KE_RIGHTMOUSE;
} else if (strequal(button.data, "wheel")) {
code = KE_MOUSEDOWN;
+ } else if (strequal(button.data, "move")) {
+ code = KE_MOUSEMOVE;
} else {
goto error;
}
@@ -380,7 +384,7 @@ void nvim_input_mouse(String button, String action, String modifier, Integer gri
} else {
goto error;
}
- } else {
+ } else if (code != KE_MOUSEMOVE) {
if (strequal(action.data, "press")) {
// pass
} else if (strequal(action.data, "drag")) {
@@ -520,7 +524,7 @@ Array nvim__runtime_inspect(void)
/// Find files in runtime directories
///
-/// 'name' can contain wildcards. For example
+/// "name" can contain wildcards. For example
/// nvim_get_runtime_file("colors/*.vim", true) will return all color
/// scheme files. Always use forward slashes (/) in the search pattern for
/// subdirectories regardless of platform.
@@ -722,8 +726,11 @@ void nvim_set_vvar(String name, Object value, Error *err)
/// text chunk with specified highlight. `hl_group` element
/// can be omitted for no highlight.
/// @param history if true, add to |message-history|.
-/// @param opts Optional parameters. Reserved for future use.
-void nvim_echo(Array chunks, Boolean history, Dictionary opts, Error *err)
+/// @param opts Optional parameters.
+/// - verbose: Message was printed as a result of 'verbose' option
+/// if Nvim was invoked with -V3log_file, the message will be
+/// redirected to the log_file and suppressed from direct output.
+void nvim_echo(Array chunks, Boolean history, Dict(echo_opts) *opts, Error *err)
FUNC_API_SINCE(7)
{
HlMessage hl_msg = parse_hl_msg(chunks, err);
@@ -731,13 +738,19 @@ void nvim_echo(Array chunks, Boolean history, Dictionary opts, Error *err)
goto error;
}
- if (opts.size > 0) {
- api_set_error(err, kErrorTypeValidation, "opts dict isn't empty");
- goto error;
+ bool verbose = api_object_to_bool(opts->verbose, "verbose", false, err);
+
+ if (verbose) {
+ verbose_enter();
}
msg_multiattr(hl_msg, history ? "echomsg" : "echo", history);
+ if (verbose) {
+ verbose_leave();
+ verbose_stop(); // flush now
+ }
+
if (history) {
// history takes ownership
return;
@@ -961,7 +974,7 @@ fail:
/// mode. Note: keypresses are sent raw as they would be to the pty
/// master end. For instance, a carriage return is sent
/// as a "\r", not as a "\n". |textlock| applies. It is possible
-/// to call |nvim_chan_send| directly in the callback however.
+/// to call |nvim_chan_send()| directly in the callback however.
/// ["input", term, bufnr, data]
/// @param[out] err Error details, if any
/// @return Channel id, or 0 on error
@@ -1009,7 +1022,7 @@ Integer nvim_open_term(Buffer buffer, DictionaryOf(LuaRef) opts, Error *err)
return (Integer)chan->id;
}
-static void term_write(char *buf, size_t size, void *data)
+static void term_write(char *buf, size_t size, void *data) // NOLINT(readability-non-const-parameter)
{
Channel *chan = data;
LuaRef cb = chan->stream.internal.cb;
@@ -1291,7 +1304,7 @@ void nvim_unsubscribe(uint64_t channel_id, String event)
/// "#rrggbb" hexadecimal string.
///
/// Example:
-/// <pre>
+/// <pre>vim
/// :echo nvim_get_color_by_name("Pink")
/// :echo nvim_get_color_by_name("#cbcbcb")
/// </pre>
@@ -1419,10 +1432,10 @@ Dictionary nvim_get_mode(void)
/// @param mode Mode short-name ("n", "i", "v", ...)
/// @returns Array of |maparg()|-like dictionaries describing mappings.
/// The "buffer" key is always zero.
-ArrayOf(Dictionary) nvim_get_keymap(uint64_t channel_id, String mode)
+ArrayOf(Dictionary) nvim_get_keymap(String mode)
FUNC_API_SINCE(3)
{
- return keymap_array(mode, NULL, channel_id == LUA_INTERNAL_CALL);
+ return keymap_array(mode, NULL);
}
/// Sets a global |mapping| for the given mode.
@@ -1433,12 +1446,12 @@ ArrayOf(Dictionary) nvim_get_keymap(uint64_t channel_id, String mode)
/// Empty {rhs} is |<Nop>|. |keycodes| are replaced as usual.
///
/// Example:
-/// <pre>
+/// <pre>vim
/// call nvim_set_keymap('n', ' <NL>', '', {'nowait': v:true})
/// </pre>
///
/// is equivalent to:
-/// <pre>
+/// <pre>vim
/// nmap <nowait> <Space><NL> <Nop>
/// </pre>
///
@@ -1449,7 +1462,7 @@ ArrayOf(Dictionary) nvim_get_keymap(uint64_t channel_id, String mode)
/// @param rhs Right-hand-side |{rhs}| of the mapping.
/// @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.
+/// |: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.
@@ -1479,14 +1492,14 @@ void nvim_del_keymap(uint64_t channel_id, String mode, String lhs, Error *err)
/// 1 is the |api-metadata| map (Dictionary).
///
/// @returns 2-tuple [{channel-id}, {api-metadata}]
-Array nvim_get_api_info(uint64_t channel_id)
+Array nvim_get_api_info(uint64_t channel_id, Arena *arena)
FUNC_API_SINCE(1) FUNC_API_FAST FUNC_API_REMOTE_ONLY
{
- Array rv = ARRAY_DICT_INIT;
+ Array rv = arena_array(arena, 2);
assert(channel_id <= INT64_MAX);
- ADD(rv, INTEGER_OBJ((int64_t)channel_id));
- ADD(rv, DICTIONARY_OBJ(api_metadata()));
+ ADD_C(rv, INTEGER_OBJ((int64_t)channel_id));
+ ADD_C(rv, DICTIONARY_OBJ(api_metadata()));
return rv;
}
@@ -1545,9 +1558,9 @@ void nvim_set_client_info(uint64_t channel_id, String name, Dictionary version,
FUNC_API_SINCE(4) FUNC_API_REMOTE_ONLY
{
Dictionary info = ARRAY_DICT_INIT;
- PUT(info, "name", copy_object(STRING_OBJ(name)));
+ PUT(info, "name", copy_object(STRING_OBJ(name), NULL));
- version = copy_dictionary(version);
+ version = copy_dictionary(version, NULL);
bool has_major = false;
for (size_t i = 0; i < version.size; i++) {
if (strequal(version.items[i].key.data, "major")) {
@@ -1560,9 +1573,9 @@ void nvim_set_client_info(uint64_t channel_id, String name, Dictionary version,
}
PUT(info, "version", DICTIONARY_OBJ(version));
- PUT(info, "type", copy_object(STRING_OBJ(type)));
- PUT(info, "methods", DICTIONARY_OBJ(copy_dictionary(methods)));
- PUT(info, "attributes", DICTIONARY_OBJ(copy_dictionary(attributes)));
+ PUT(info, "type", copy_object(STRING_OBJ(type), NULL));
+ PUT(info, "methods", DICTIONARY_OBJ(copy_dictionary(methods, NULL)));
+ PUT(info, "attributes", DICTIONARY_OBJ(copy_dictionary(attributes, NULL)));
rpc_set_client_info(channel_id, info);
}
@@ -1629,11 +1642,11 @@ Array nvim_list_chans(void)
/// an error, it is a three-element array with the zero-based index of the call
/// which resulted in an error, the error type and the error message. If an
/// error occurred, the values from all preceding calls will still be returned.
-Array nvim_call_atomic(uint64_t channel_id, Array calls, Error *err)
+Array nvim_call_atomic(uint64_t channel_id, Array calls, Arena *arena, Error *err)
FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
{
- Array rv = ARRAY_DICT_INIT;
- Array results = ARRAY_DICT_INIT;
+ Array rv = arena_array(arena, 2);
+ Array results = arena_array(arena, calls.size);
Error nested_error = ERROR_INIT;
size_t i; // also used for freeing the variables
@@ -1642,21 +1655,21 @@ Array nvim_call_atomic(uint64_t channel_id, Array calls, Error *err)
api_set_error(err,
kErrorTypeValidation,
"Items in calls array must be arrays");
- goto validation_error;
+ goto theend;
}
Array call = calls.items[i].data.array;
if (call.size != 2) {
api_set_error(err,
kErrorTypeValidation,
"Items in calls array must be arrays of size 2");
- goto validation_error;
+ goto theend;
}
if (call.items[0].type != kObjectTypeString) {
api_set_error(err,
kErrorTypeValidation,
"Name must be String");
- goto validation_error;
+ goto theend;
}
String name = call.items[0].data.string;
@@ -1664,7 +1677,7 @@ Array nvim_call_atomic(uint64_t channel_id, Array calls, Error *err)
api_set_error(err,
kErrorTypeValidation,
"Args must be Array");
- goto validation_error;
+ goto theend;
}
Array args = call.items[1].data.array;
@@ -1676,29 +1689,32 @@ Array nvim_call_atomic(uint64_t channel_id, Array calls, Error *err)
if (ERROR_SET(&nested_error)) {
break;
}
- Object result = handler.fn(channel_id, args, &nested_error);
+
+ Object result = handler.fn(channel_id, args, arena, &nested_error);
if (ERROR_SET(&nested_error)) {
// error handled after loop
break;
}
-
- ADD(results, result);
+ // TODO(bfredl): wasteful copy. It could be avoided to encoding to msgpack
+ // directly here. But `result` might become invalid when next api function
+ // is called in the loop.
+ ADD_C(results, copy_object(result, arena));
+ if (!handler.arena_return) {
+ api_free_object(result);
+ }
}
- ADD(rv, ARRAY_OBJ(results));
+ ADD_C(rv, ARRAY_OBJ(results));
if (ERROR_SET(&nested_error)) {
- Array errval = ARRAY_DICT_INIT;
- ADD(errval, INTEGER_OBJ((Integer)i));
- ADD(errval, INTEGER_OBJ(nested_error.type));
- ADD(errval, STRING_OBJ(cstr_to_string(nested_error.msg)));
- ADD(rv, ARRAY_OBJ(errval));
+ Array errval = arena_array(arena, 3);
+ ADD_C(errval, INTEGER_OBJ((Integer)i));
+ ADD_C(errval, INTEGER_OBJ(nested_error.type));
+ ADD_C(errval, STRING_OBJ(copy_string(cstr_as_string(nested_error.msg), arena)));
+ ADD_C(rv, ARRAY_OBJ(errval));
} else {
- ADD(rv, NIL);
+ ADD_C(rv, NIL);
}
- goto theend;
-validation_error:
- api_free_array(results);
theend:
api_clear_error(&nested_error);
return rv;
@@ -1712,17 +1728,21 @@ theend:
/// @param to_err true: message is an error (uses `emsg` instead of `msg`)
static void write_msg(String message, bool to_err)
{
- static size_t out_pos = 0, err_pos = 0;
- static char out_line_buf[LINE_BUFFER_SIZE], err_line_buf[LINE_BUFFER_SIZE];
+ static StringBuilder out_line_buf = KV_INITIAL_VALUE;
+ static StringBuilder err_line_buf = KV_INITIAL_VALUE;
-#define PUSH_CHAR(i, pos, line_buf, msg) \
- if (message.data[i] == NL || (pos) == LINE_BUFFER_SIZE - 1) { \
- (line_buf)[pos] = NUL; \
- msg(line_buf); \
- (pos) = 0; \
+#define PUSH_CHAR(i, line_buf, msg) \
+ if (kv_max(line_buf) == 0) { \
+ kv_resize(line_buf, LINE_BUFFER_MIN_SIZE); \
+ } \
+ if (message.data[i] == NL) { \
+ kv_push(line_buf, NUL); \
+ msg(line_buf.items); \
+ kv_drop(line_buf, kv_size(line_buf)); \
+ kv_resize(line_buf, LINE_BUFFER_MIN_SIZE); \
continue; \
} \
- (line_buf)[(pos)++] = message.data[i];
+ kv_push(line_buf, message.data[i]);
no_wait_return++;
for (uint32_t i = 0; i < message.size; i++) {
@@ -1730,9 +1750,9 @@ static void write_msg(String message, bool to_err)
break;
}
if (to_err) {
- PUSH_CHAR(i, err_pos, err_line_buf, emsg);
+ PUSH_CHAR(i, err_line_buf, emsg);
} else {
- PUSH_CHAR(i, out_pos, out_line_buf, msg);
+ PUSH_CHAR(i, out_line_buf, msg);
}
}
no_wait_return--;
@@ -1751,7 +1771,7 @@ static void write_msg(String message, bool to_err)
/// @return its argument.
Object nvim__id(Object obj)
{
- return copy_object(obj);
+ return copy_object(obj, NULL);
}
/// Returns array given as argument.
@@ -1764,7 +1784,7 @@ Object nvim__id(Object obj)
/// @return its argument.
Array nvim__id_array(Array arr)
{
- return copy_object(ARRAY_OBJ(arr)).data.array;
+ return copy_array(arr, NULL);
}
/// Returns dictionary given as argument.
@@ -1777,7 +1797,7 @@ Array nvim__id_array(Array arr)
/// @return its argument.
Dictionary nvim__id_dictionary(Dictionary dct)
{
- return copy_object(DICTIONARY_OBJ(dct)).data.dictionary;
+ return copy_dictionary(dct, NULL);
}
/// Returns floating-point value given as argument.
@@ -1803,6 +1823,7 @@ Dictionary nvim__stats(void)
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, "arena_alloc_count", INTEGER_OBJ((Integer)arena_alloc_count));
return rv;
}
@@ -1813,7 +1834,7 @@ Dictionary nvim__stats(void)
/// - "width" Requested width of the UI
/// - "rgb" true if the UI uses RGB colors (false implies |cterm-colors|)
/// - "ext_..." Requested UI extensions, see |ui-option|
-/// - "chan" Channel id of remote UI (not present for TUI)
+/// - "chan" |channel-id| of remote UI
Array nvim_list_uis(void)
FUNC_API_SINCE(4)
{
@@ -1875,7 +1896,7 @@ Object nvim_get_proc(Integer pid, Error *err)
api_set_error(err, kErrorTypeException, "Invalid pid: %" PRId64, pid);
return NIL;
}
-#ifdef WIN32
+#ifdef MSWIN
rvobj.data.dictionary = os_proc_info((int)pid);
if (rvobj.data.dictionary.size == 0) { // Process not found.
return NIL;
@@ -1897,19 +1918,20 @@ Object nvim_get_proc(Integer pid, Error *err)
return rvobj;
}
-/// Selects an item in the completion popupmenu.
+/// Selects an item in the completion popup menu.
///
-/// If |ins-completion| is not active this API call is silently ignored.
-/// Useful for an external UI using |ui-popupmenu| to control the popupmenu
-/// with the mouse. Can also be used in a mapping; use <cmd> |:map-cmd| to
-/// ensure the mapping doesn't end completion mode.
+/// If neither |ins-completion| nor |cmdline-completion| popup menu is active
+/// this API call is silently ignored.
+/// Useful for an external UI using |ui-popupmenu| to control the popup menu with the mouse.
+/// Can also be used in a mapping; use <Cmd> |:map-cmd| or a Lua mapping to ensure the mapping
+/// doesn't end completion mode.
///
-/// @param item Index (zero-based) of the item to select. Value of -1 selects
-/// nothing and restores the original text.
-/// @param insert Whether the selection should be inserted in the buffer.
-/// @param finish Finish the completion and dismiss the popupmenu. Implies
-/// `insert`.
-/// @param opts Optional parameters. Reserved for future use.
+/// @param item Index (zero-based) of the item to select. Value of -1 selects nothing
+/// and restores the original text.
+/// @param insert For |ins-completion|, whether the selection should be inserted in the buffer.
+/// Ignored for |cmdline-completion|.
+/// @param finish Finish the completion and dismiss the popup menu. Implies {insert}.
+/// @param opts Optional parameters. Reserved for future use.
/// @param[out] err Error details, if any
void nvim_select_popupmenu_item(Integer item, Boolean insert, Boolean finish, Dictionary opts,
Error *err)
@@ -1928,7 +1950,7 @@ void nvim_select_popupmenu_item(Integer item, Boolean insert, Boolean finish, Di
}
/// NB: if your UI doesn't use hlstate, this will not return hlstate first time
-Array nvim__inspect_cell(Integer grid, Integer row, Integer col, Error *err)
+Array nvim__inspect_cell(Integer grid, Integer row, Integer col, Arena *arena, Error *err)
{
Array ret = ARRAY_DICT_INIT;
@@ -1952,13 +1974,14 @@ Array nvim__inspect_cell(Integer grid, Integer row, Integer col, Error *err)
|| col < 0 || col >= g->cols) {
return ret;
}
+ ret = arena_array(arena, 3);
size_t off = g->line_offset[(size_t)row] + (size_t)col;
- ADD(ret, STRING_OBJ(cstr_to_string((char *)g->chars[off])));
+ ADD_C(ret, STRING_OBJ(cstr_as_string((char *)g->chars[off])));
int attr = g->attrs[off];
- ADD(ret, DICTIONARY_OBJ(hl_get_attr_by_id(attr, true, err)));
+ ADD_C(ret, DICTIONARY_OBJ(hl_get_attr_by_id(attr, true, arena, err)));
// will not work first time
if (!highlight_use_hlstate()) {
- ADD(ret, ARRAY_OBJ(hl_inspect(attr)));
+ ADD_C(ret, ARRAY_OBJ(hl_inspect(attr)));
}
return ret;
}
@@ -2040,7 +2063,7 @@ Array nvim_get_mark(String name, Dictionary opts, Error *err)
// Marks are from an open buffer it fnum is non zero
if (mark->fmark.fnum != 0) {
bufnr = mark->fmark.fnum;
- filename = (char *)buflist_nr2name(bufnr, true, true);
+ filename = buflist_nr2name(bufnr, true, true);
allocated = true;
// Marks comes from shada
} else {
@@ -2088,7 +2111,7 @@ Array nvim_get_mark(String name, Dictionary opts, Error *err)
/// '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}
+/// - use_tabline: (boolean) Evaluate tabline instead of statusline. When true, {winid}
/// is ignored. Mutually exclusive with {use_winbar}.
///
/// @param[out] err Error details, if any.
@@ -2096,7 +2119,7 @@ Array nvim_get_mark(String name, Dictionary opts, Error *err)
/// - str: (string) Characters that will be displayed on the statusline.
/// - width: (number) Display width of the statusline.
/// - highlights: Array containing highlight information of the statusline. Only included when
-/// the "highlights" key in {opts} is |TRUE|. Each element of the array is a
+/// the "highlights" key in {opts} is true. Each element of the array is a
/// |Dictionary| with these keys:
/// - start: (number) Byte index (0-based) of first character that uses the highlight.
/// - group: (string) Name of highlight group.
@@ -2112,7 +2135,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
bool use_tabline = false;
bool highlights = false;
- if (str.size < 2 || memcmp(str.data, "%!", 2)) {
+ if (str.size < 2 || memcmp(str.data, "%!", 2) != 0) {
const char *const errmsg = check_stl_option(str.data);
if (errmsg) {
api_set_error(err, kErrorTypeValidation, "%s", errmsg);
@@ -2210,10 +2233,12 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
buf,
sizeof(buf),
str.data,
- false,
+ NULL,
+ 0,
fillchar,
maxwidth,
hltab_ptr,
+ NULL,
NULL);
PUT(result, "width", INTEGER_OBJ(width));
@@ -2228,7 +2253,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
// If first character doesn't have a defined highlight,
// add the default highlight at the beginning of the highlight list
- if (hltab->start == NULL || ((char *)hltab->start - buf) != 0) {
+ if (hltab->start == NULL || (hltab->start - buf) != 0) {
Dictionary hl_info = ARRAY_DICT_INIT;
grpname = get_default_stl_hl(wp, use_winbar);
@@ -2246,7 +2271,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
if (sp->userhl == 0) {
grpname = get_default_stl_hl(wp, use_winbar);
} else if (sp->userhl < 0) {
- grpname = (char *)syn_id2name(-sp->userhl);
+ grpname = syn_id2name(-sp->userhl);
} else {
snprintf(user_group, sizeof(user_group), "User%d", sp->userhl);
grpname = user_group;
diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c
index a28bfd2ab9..af1b23b712 100644
--- a/src/nvim/api/vimscript.c
+++ b/src/nvim/api/vimscript.c
@@ -2,26 +2,31 @@
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include <assert.h>
-#include <limits.h>
-#include <stdlib.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include "klib/kvec.h"
#include "nvim/api/private/converter.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/vimscript.h"
#include "nvim/ascii.h"
-#include "nvim/autocmd.h"
+#include "nvim/buffer_defs.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/eval/userfunc.h"
#include "nvim/ex_docmd.h"
-#include "nvim/ops.h"
+#include "nvim/garray.h"
+#include "nvim/globals.h"
+#include "nvim/memory.h"
+#include "nvim/pos.h"
#include "nvim/runtime.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"
@@ -137,7 +142,7 @@ Object nvim_eval(String expr, Error *err)
if (!recursive) {
force_abort = false;
suppress_errthrow = false;
- current_exception = NULL;
+ did_throw = false;
// `did_emsg` is set by emsg(), which cancels execution.
did_emsg = false;
}
@@ -196,7 +201,7 @@ static Object _call_function(String fn, Array args, dict_T *self, Error *err)
if (!recursive) {
force_abort = false;
suppress_errthrow = false;
- current_exception = NULL;
+ did_throw = false;
// `did_emsg` is set by emsg(), which cancels execution.
did_emsg = false;
}
@@ -204,10 +209,10 @@ static Object _call_function(String fn, Array args, dict_T *self, Error *err)
try_start();
typval_T rettv;
funcexe_T funcexe = FUNCEXE_INIT;
- funcexe.firstline = curwin->w_cursor.lnum;
- funcexe.lastline = curwin->w_cursor.lnum;
- funcexe.evaluate = true;
- funcexe.selfdict = self;
+ funcexe.fe_firstline = curwin->w_cursor.lnum;
+ funcexe.fe_lastline = curwin->w_cursor.lnum;
+ funcexe.fe_evaluate = true;
+ funcexe.fe_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(fn.data, (int)fn.size, &rettv, (int)args.size,
@@ -304,7 +309,7 @@ Object nvim_call_dict_function(Object dict, String fn, Array args, Error *err)
}
fn = (String) {
.data = di->di_tv.vval.v_string,
- .size = STRLEN(di->di_tv.vval.v_string),
+ .size = strlen(di->di_tv.vval.v_string),
};
}
@@ -532,7 +537,7 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, E
kv_drop(ast_conv_stack, 1);
} else {
if (cur_item.ret_node_p->type == kObjectTypeNil) {
- size_t items_size = (size_t)(3 // "type", "start" and "len"
+ size_t items_size = (size_t)(3 // "type", "start" and "len" // NOLINT(bugprone-misplaced-widening-cast)
+ (node->children != NULL) // "children"
+ (node->type == kExprNodeOption
|| node->type == kExprNodePlainIdentifier) // "scope"
diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c
index 6c37df6af8..0ffeac1bff 100644
--- a/src/nvim/api/win_config.c
+++ b/src/nvim/api/win_config.c
@@ -1,19 +1,27 @@
// 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 <stddef.h>
-#include <stdint.h>
+#include <string.h>
+#include "klib/kvec.h"
+#include "nvim/api/extmark.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/win_config.h"
#include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
+#include "nvim/decoration.h"
#include "nvim/drawscreen.h"
+#include "nvim/extmark_defs.h"
+#include "nvim/globals.h"
+#include "nvim/grid_defs.h"
#include "nvim/highlight_group.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
+#include "nvim/memory.h"
#include "nvim/option.h"
-#include "nvim/strings.h"
+#include "nvim/pos.h"
#include "nvim/syntax.h"
#include "nvim/ui.h"
#include "nvim/window.h"
@@ -47,13 +55,13 @@
/// this should not be used to specify arbitrary WM screen positions.
///
/// Example (Lua): window-relative float
-/// <pre>
+/// <pre>lua
/// vim.api.nvim_open_win(0, false,
/// {relative='win', row=3, col=3, width=12, height=3})
/// </pre>
///
/// Example (Lua): buffer-relative float (travels as buffer is scrolled)
-/// <pre>
+/// <pre>lua
/// vim.api.nvim_open_win(0, false,
/// {relative='win', width=12, height=3, bufpos={100,10}})
/// </pre>
@@ -66,6 +74,7 @@
/// - "editor" The global editor grid
/// - "win" Window given by the `win` field, or current window.
/// - "cursor" Cursor position in current window.
+/// - "mouse" Mouse position
/// - win: |window-ID| for relative="win".
/// - anchor: Decides which corner of the float to place at (row,col):
/// - "NW" northwest (default)
@@ -106,10 +115,11 @@
/// float where the text should not be edited. Disables
/// 'number', 'relativenumber', 'cursorline', 'cursorcolumn',
/// 'foldcolumn', 'spell' and 'list' options. 'signcolumn'
-/// is changed to `auto` and 'colorcolumn' is cleared. The
-/// end-of-buffer region is hidden by setting `eob` flag of
+/// is changed to `auto` and 'colorcolumn' is cleared.
+/// 'statuscolumn' is changed to empty. The end-of-buffer
+/// region is hidden by setting `eob` flag of
/// 'fillchars' to a space char, and clearing the
-/// |EndOfBuffer| region in 'winhighlight'.
+/// |hl-EndOfBuffer| region in 'winhighlight'.
/// - border: Style of (optional) window border. This can either be a string
/// or an array. The string values are
/// - "none": No border (default).
@@ -133,7 +143,12 @@
/// will only make vertical borders but not horizontal ones.
/// By default, `FloatBorder` highlight is used, which links to `WinSeparator`
/// when not defined. It could also be specified by character:
-/// [ {"+", "MyCorner"}, {"x", "MyBorder"} ].
+/// [ ["+", "MyCorner"], ["x", "MyBorder"] ].
+/// - title: Title (optional) in window border, String or list.
+/// List is [text, highlight] tuples. if is string the default
+/// highlight group is `FloatTitle`.
+/// - title_pos: Title position must set with title option.
+/// value can be of `left` `center` `right` default is left.
/// - noautocmd: If true then no buffer-related autocommand events such as
/// |BufEnter|, |BufLeave| or |BufWinEnter| may fire from
/// calling this function.
@@ -202,7 +217,7 @@ void nvim_win_set_config(Window window, Dict(float_config) *config, Error *err)
if (!win_new_float(win, false, fconfig, err)) {
return;
}
- redraw_later(win, NOT_VALID);
+ redraw_later(win, UPD_NOT_VALID);
} else {
win_config_float(win, fconfig);
win->w_pos_changed = true;
@@ -263,7 +278,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 *hi_name = (char *)syn_id2name(hi_id);
+ char *hi_name = syn_id2name(hi_id);
if (hi_name[0]) {
ADD(tuple, STRING_OBJ(s));
ADD(tuple, STRING_OBJ(cstr_to_string((const char *)hi_name)));
@@ -273,6 +288,29 @@ Dictionary nvim_win_get_config(Window window, Error *err)
}
}
PUT(rv, "border", ARRAY_OBJ(border));
+ if (config->title) {
+ Array titles = ARRAY_DICT_INIT;
+ VirtText title_datas = config->title_chunks;
+ for (size_t i = 0; i < title_datas.size; i++) {
+ Array tuple = ARRAY_DICT_INIT;
+ ADD(tuple, CSTR_TO_OBJ((const char *)title_datas.items[i].text));
+ if (title_datas.items[i].hl_id > 0) {
+ ADD(tuple,
+ STRING_OBJ(cstr_to_string((const char *)syn_id2name(title_datas.items[i].hl_id))));
+ }
+ ADD(titles, ARRAY_OBJ(tuple));
+ }
+ PUT(rv, "title", ARRAY_OBJ(titles));
+ char *title_pos;
+ if (config->title_pos == kAlignLeft) {
+ title_pos = "left";
+ } else if (config->title_pos == kAlignCenter) {
+ title_pos = "center";
+ } else {
+ title_pos = "right";
+ }
+ PUT(rv, "title_pos", CSTR_TO_OBJ(title_pos));
+ }
}
}
@@ -312,6 +350,8 @@ static bool parse_float_relative(String relative, FloatRelative *out)
*out = kFloatRelativeWindow;
} else if (striequal(str, "cursor")) {
*out = kFloatRelativeCursor;
+ } else if (striequal(str, "mouse")) {
+ *out = kFloatRelativeMouse;
} else {
return false;
}
@@ -330,7 +370,74 @@ static bool parse_float_bufpos(Array bufpos, lpos_T *out)
return true;
}
-static void parse_border_style(Object style, FloatConfig *fconfig, Error *err)
+static void parse_border_title(Object title, Object title_pos, FloatConfig *fconfig, Error *err)
+{
+ if (!parse_title_pos(title_pos, fconfig, err)) {
+ return;
+ }
+
+ if (title.type == kObjectTypeString) {
+ if (title.data.string.size == 0) {
+ fconfig->title = false;
+ return;
+ }
+ int hl_id = syn_check_group(S_LEN("FloatTitle"));
+ kv_push(fconfig->title_chunks, ((VirtTextChunk){ .text = xstrdup(title.data.string.data),
+ .hl_id = hl_id }));
+ fconfig->title_width = (int)mb_string2cells(title.data.string.data);
+ fconfig->title = true;
+ return;
+ }
+
+ if (title.type != kObjectTypeArray) {
+ api_set_error(err, kErrorTypeValidation, "title must be string or array");
+ return;
+ }
+
+ if (title.data.array.size == 0) {
+ api_set_error(err, kErrorTypeValidation, "title cannot be an empty array");
+ return;
+ }
+
+ fconfig->title_width = 0;
+ fconfig->title_chunks = parse_virt_text(title.data.array, err, &fconfig->title_width);
+
+ fconfig->title = true;
+}
+
+static bool parse_title_pos(Object title_pos, FloatConfig *fconfig, Error *err)
+{
+ if (!HAS_KEY(title_pos)) {
+ fconfig->title_pos = kAlignLeft;
+ return true;
+ }
+
+ if (title_pos.type != kObjectTypeString) {
+ api_set_error(err, kErrorTypeValidation, "title_pos must be string");
+ return false;
+ }
+
+ if (title_pos.data.string.size == 0) {
+ fconfig->title_pos = kAlignLeft;
+ return true;
+ }
+
+ char *pos = title_pos.data.string.data;
+
+ if (strequal(pos, "left")) {
+ fconfig->title_pos = kAlignLeft;
+ } else if (strequal(pos, "center")) {
+ fconfig->title_pos = kAlignCenter;
+ } else if (strequal(pos, "right")) {
+ fconfig->title_pos = kAlignRight;
+ } else {
+ api_set_error(err, kErrorTypeValidation, "invalid title_pos value");
+ return false;
+ }
+ return true;
+}
+
+static void parse_border_style(Object style, FloatConfig *fconfig, Error *err)
{
struct {
const char *name;
@@ -414,6 +521,8 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err)
String str = style.data.string;
if (str.size == 0 || strequal(str.data, "none")) {
fconfig->border = false;
+ // title does not work with border equal none
+ fconfig->title = false;
return;
}
for (size_t i = 0; defaults[i].name; i++) {
@@ -603,6 +712,29 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
return false;
}
+ if (HAS_KEY(config->title_pos)) {
+ if (!HAS_KEY(config->title)) {
+ api_set_error(err, kErrorTypeException, "title_pos requires title to be set");
+ return false;
+ }
+ }
+
+ if (HAS_KEY(config->title)) {
+ // title only work with border
+ if (!HAS_KEY(config->border) && !fconfig->border) {
+ api_set_error(err, kErrorTypeException, "title requires border to be set");
+ return false;
+ }
+
+ if (fconfig->title) {
+ clear_virttext(&fconfig->title_chunks);
+ }
+ parse_border_title(config->title, config->title_pos, fconfig, err);
+ if (ERROR_SET(err)) {
+ return false;
+ }
+ }
+
if (HAS_KEY(config->border)) {
parse_border_style(config->border, fconfig, err);
if (ERROR_SET(err)) {
diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c
index 580dfd8639..e2c234ab29 100644
--- a/src/nvim/api/window.c
+++ b/src/nvim/api/window.c
@@ -10,16 +10,18 @@
#include "nvim/api/private/helpers.h"
#include "nvim/api/window.h"
#include "nvim/ascii.h"
-#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
#include "nvim/cursor.h"
#include "nvim/drawscreen.h"
+#include "nvim/eval/window.h"
#include "nvim/ex_docmd.h"
+#include "nvim/gettext.h"
#include "nvim/globals.h"
#include "nvim/lua/executor.h"
+#include "nvim/memline_defs.h"
#include "nvim/move.h"
-#include "nvim/option.h"
-#include "nvim/syntax.h"
-#include "nvim/vim.h"
+#include "nvim/pos.h"
+#include "nvim/types.h"
#include "nvim/window.h"
/// Gets the current buffer in a window
@@ -51,7 +53,9 @@ void nvim_win_set_buf(Window window, Buffer buffer, Error *err)
win_set_buf(window, buffer, false, err);
}
-/// Gets the (1,0)-indexed cursor position in the window. |api-indexing|
+/// Gets the (1,0)-indexed, buffer-relative cursor position for a given window
+/// (different windows showing the same buffer have independent cursor
+/// positions). |api-indexing|
///
/// @param window Window handle, or 0 for current window
/// @param[out] err Error details, if any
@@ -115,10 +119,15 @@ void nvim_win_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err)
// Make sure we stick in this column.
win->w_set_curswant = true;
- // make sure cursor is in visible range even if win != curwin
- update_topline_win(win);
+ // make sure cursor is in visible range and
+ // cursorcolumn and cursorline are updated even if win != curwin
+ switchwin_T switchwin;
+ switch_win(&switchwin, win, NULL, true);
+ update_topline(curwin);
+ validate_cursor();
+ restore_win(&switchwin, true);
- redraw_later(win, VALID);
+ redraw_later(win, UPD_VALID);
win->w_redr_status = true;
}
@@ -160,9 +169,11 @@ void nvim_win_set_height(Window window, Integer height, Error *err)
win_T *savewin = curwin;
curwin = win;
+ curbuf = curwin->w_buffer;
try_start();
win_setheight((int)height);
curwin = savewin;
+ curbuf = curwin->w_buffer;
try_end(err);
}
@@ -205,9 +216,11 @@ void nvim_win_set_width(Window window, Integer width, Error *err)
win_T *savewin = curwin;
curwin = win;
+ curbuf = curwin->w_buffer;
try_start();
win_setwidth((int)width);
curwin = savewin;
+ curbuf = curwin->w_buffer;
try_end(err);
}
@@ -340,7 +353,7 @@ Boolean nvim_win_is_valid(Window window)
///
/// Like |:hide| the buffer becomes hidden unless another window is editing it,
/// or 'bufhidden' is `unload`, `delete` or `wipe` as opposed to |:close| or
-/// |nvim_win_close|, which will close the buffer.
+/// |nvim_win_close()|, which will close the buffer.
///
/// @param window Window handle, or 0 for current window
/// @param[out] err Error details, if any
@@ -356,11 +369,16 @@ void nvim_win_hide(Window window, Error *err)
tabpage_T *tabpage = win_find_tabpage(win);
TryState tstate;
try_enter(&tstate);
- if (tabpage == curtab) {
+
+ // Never close the autocommand window.
+ if (is_aucmd_win(win)) {
+ emsg(_(e_autocmd_close));
+ } else if (tabpage == curtab) {
win_close(win, false, false);
} else {
win_close_othertab(win, false, tabpage);
}
+
vim_ignored = try_leave(&tstate, err);
}
@@ -430,7 +448,7 @@ Object nvim_win_call(Window window, LuaRef fun, Error *err)
/// Set highlight namespace for a window. This will use highlights defined in
/// this namespace, but fall back to global highlights (ns=0) when missing.
///
-/// This takes predecence over the 'winhighlight' option.
+/// This takes precedence over the 'winhighlight' option.
///
/// @param ns_id the namespace to use
/// @param[out] err Error details, if any
@@ -449,5 +467,5 @@ void nvim_win_set_hl_ns(Window window, Integer ns_id, Error *err)
win->w_ns_hl = (NS)ns_id;
win->w_hl_needs_update = true;
- redraw_later(win, NOT_VALID);
+ redraw_later(win, UPD_NOT_VALID);
}
diff --git a/src/nvim/arabic.c b/src/nvim/arabic.c
index 06536e6e2b..41024cafda 100644
--- a/src/nvim/arabic.c
+++ b/src/nvim/arabic.c
@@ -21,76 +21,83 @@
/// Stand-Alone - unicode form-B isolated char denoted with a_s_* (NOT USED)
#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
#include "nvim/arabic.h"
#include "nvim/ascii.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
+#include "nvim/option_defs.h"
#include "nvim/vim.h"
// Unicode values for Arabic characters.
-#define a_HAMZA 0x0621
-#define a_ALEF_MADDA 0x0622
-#define a_ALEF_HAMZA_ABOVE 0x0623
-#define a_WAW_HAMZA 0x0624
-#define a_ALEF_HAMZA_BELOW 0x0625
-#define a_YEH_HAMZA 0x0626
-#define a_ALEF 0x0627
-#define a_BEH 0x0628
-#define a_TEH_MARBUTA 0x0629
-#define a_TEH 0x062a
-#define a_THEH 0x062b
-#define a_JEEM 0x062c
-#define a_HAH 0x062d
-#define a_KHAH 0x062e
-#define a_DAL 0x062f
-#define a_THAL 0x0630
-#define a_REH 0x0631
-#define a_ZAIN 0x0632
-#define a_SEEN 0x0633
-#define a_SHEEN 0x0634
-#define a_SAD 0x0635
-#define a_DAD 0x0636
-#define a_TAH 0x0637
-#define a_ZAH 0x0638
-#define a_AIN 0x0639
-#define a_GHAIN 0x063a
-#define a_TATWEEL 0x0640
-#define a_FEH 0x0641
-#define a_QAF 0x0642
-#define a_KAF 0x0643
-#define a_LAM 0x0644
-#define a_MEEM 0x0645
-#define a_NOON 0x0646
-#define a_HEH 0x0647
-#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
-#define a_FATHA 0x064e
-#define a_DAMMA 0x064f
-#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_PEH 0x067e
-#define a_TCHEH 0x0686
-#define a_JEH 0x0698
-#define a_FKAF 0x06a9
-#define a_GAF 0x06af
-#define a_FYEH 0x06cc
-
-#define a_s_LAM_ALEF_MADDA_ABOVE 0xfef5
-#define a_f_LAM_ALEF_MADDA_ABOVE 0xfef6
-#define a_s_LAM_ALEF_HAMZA_ABOVE 0xfef7
-#define a_f_LAM_ALEF_HAMZA_ABOVE 0xfef8
-#define a_s_LAM_ALEF_HAMZA_BELOW 0xfef9
-#define a_f_LAM_ALEF_HAMZA_BELOW 0xfefa
-#define a_s_LAM_ALEF 0xfefb
-#define a_f_LAM_ALEF 0xfefc
+enum {
+ a_HAMZA = 0x0621,
+ a_ALEF_MADDA = 0x0622,
+ a_ALEF_HAMZA_ABOVE = 0x0623,
+ a_WAW_HAMZA = 0x0624,
+ a_ALEF_HAMZA_BELOW = 0x0625,
+ a_YEH_HAMZA = 0x0626,
+ a_ALEF = 0x0627,
+ a_BEH = 0x0628,
+ a_TEH_MARBUTA = 0x0629,
+ a_TEH = 0x062a,
+ a_THEH = 0x062b,
+ a_JEEM = 0x062c,
+ a_HAH = 0x062d,
+ a_KHAH = 0x062e,
+ a_DAL = 0x062f,
+ a_THAL = 0x0630,
+ a_REH = 0x0631,
+ a_ZAIN = 0x0632,
+ a_SEEN = 0x0633,
+ a_SHEEN = 0x0634,
+ a_SAD = 0x0635,
+ a_DAD = 0x0636,
+ a_TAH = 0x0637,
+ a_ZAH = 0x0638,
+ a_AIN = 0x0639,
+ a_GHAIN = 0x063a,
+ a_TATWEEL = 0x0640,
+ a_FEH = 0x0641,
+ a_QAF = 0x0642,
+ a_KAF = 0x0643,
+ a_LAM = 0x0644,
+ a_MEEM = 0x0645,
+ a_NOON = 0x0646,
+ a_HEH = 0x0647,
+ a_WAW = 0x0648,
+ a_ALEF_MAKSURA = 0x0649,
+ a_YEH = 0x064a,
+ a_FATHATAN = 0x064b,
+ a_DAMMATAN = 0x064c,
+ a_KASRATAN = 0x064d,
+ a_FATHA = 0x064e,
+ a_DAMMA = 0x064f,
+ a_KASRA = 0x0650,
+ a_SHADDA = 0x0651,
+ a_SUKUN = 0x0652,
+ a_MADDA_ABOVE = 0x0653,
+ a_HAMZA_ABOVE = 0x0654,
+ a_HAMZA_BELOW = 0x0655,
+
+ a_PEH = 0x067e,
+ a_TCHEH = 0x0686,
+ a_JEH = 0x0698,
+ a_FKAF = 0x06a9,
+ a_GAF = 0x06af,
+ a_FYEH = 0x06cc,
+
+ a_s_LAM_ALEF_MADDA_ABOVE = 0xfef5,
+ a_f_LAM_ALEF_MADDA_ABOVE = 0xfef6,
+ a_s_LAM_ALEF_HAMZA_ABOVE = 0xfef7,
+ a_f_LAM_ALEF_HAMZA_ABOVE = 0xfef8,
+ a_s_LAM_ALEF_HAMZA_BELOW = 0xfef9,
+ a_f_LAM_ALEF_HAMZA_BELOW = 0xfefa,
+ a_s_LAM_ALEF = 0xfefb,
+ a_f_LAM_ALEF = 0xfefc,
+};
static struct achar {
unsigned c;
@@ -320,22 +327,22 @@ int arabic_shape(int c, int *ccp, int *c1p, int prev_c, int prev_c1, int next_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;
+ if (backward_combine) {
+ if (forward_combine) {
+ curr_c = (int)curr_a->medial;
+ } else {
+ curr_c = (int)curr_a->final;
+ }
+ } else {
+ if (forward_combine) {
+ curr_c = (int)curr_a->initial;
+ } else {
+ curr_c = (int)curr_a->isolated;
+ }
}
}
- // Sanity check -- curr_c should, in the future, never be 0.
- // We should, in the future, insert a fatal error here.
+ // Character missing from the table means using original character.
if (curr_c == NUL) {
curr_c = c;
}
diff --git a/src/nvim/arglist.c b/src/nvim/arglist.c
index 0be83f0c05..c6a4be7e13 100644
--- a/src/nvim/arglist.c
+++ b/src/nvim/arglist.c
@@ -5,41 +5,92 @@
#include <assert.h>
#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include "auto/config.h"
#include "nvim/arglist.h"
+#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
-#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/eval/window.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_cmds2.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
#include "nvim/garray.h"
+#include "nvim/gettext.h"
#include "nvim/globals.h"
+#include "nvim/macros.h"
#include "nvim/mark.h"
+#include "nvim/memline_defs.h"
#include "nvim/memory.h"
+#include "nvim/message.h"
+#include "nvim/option.h"
#include "nvim/os/input.h"
#include "nvim/path.h"
+#include "nvim/pos.h"
#include "nvim/regexp.h"
-#include "nvim/strings.h"
+#include "nvim/types.h"
#include "nvim/undo.h"
#include "nvim/version.h"
#include "nvim/vim.h"
#include "nvim/window.h"
+/// State used by the :all command to open all the files in the argument list in
+/// separate windows.
+typedef struct {
+ alist_T *alist; ///< argument list to be used
+ int had_tab;
+ bool keep_tabs;
+ bool forceit;
+
+ bool use_firstwin; ///< use first window for arglist
+ uint8_t *opened; ///< Array of weight for which args are open:
+ ///< 0: not opened
+ ///< 1: opened in other tab
+ ///< 2: opened in curtab
+ ///< 3: opened in curtab and curwin
+ int opened_len; ///< length of opened[]
+ win_T *new_curwin;
+ tabpage_T *new_curtab;
+} arg_all_state_T;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "arglist.c.generated.h"
#endif
+static char e_cannot_change_arglist_recursively[]
+ = N_("E1156: Cannot change the argument list recursively");
+
enum {
AL_SET = 1,
AL_ADD = 2,
AL_DEL = 3,
};
+/// This flag is set whenever the argument list is being changed and calling a
+/// function that might trigger an autocommand.
+static bool arglist_locked = false;
+
+static int check_arglist_locked(void)
+{
+ if (arglist_locked) {
+ emsg(_(e_cannot_change_arglist_recursively));
+ return FAIL;
+ }
+ return OK;
+}
+
/// Clear an argument list: free all file names and reset it to zero entries.
void alist_clear(alist_T *al)
{
+ if (check_arglist_locked() == FAIL) {
+ return;
+ }
#define FREE_AENTRY_FNAME(arg) xfree((arg)->ae_fname)
GA_DEEP_CLEAR(&al->al_ga, aentry_T, FREE_AENTRY_FNAME);
}
@@ -79,13 +130,14 @@ void alist_expand(int *fnum_list, int fnum_len)
{
char *save_p_su = p_su;
+ char **old_arg_files = xmalloc(sizeof(*old_arg_files) * GARGCOUNT);
+
// Don't use 'suffixes' here. This should work like the shell did the
// expansion. Also, the vimrc file isn't read yet, thus the user
// can't set the options.
p_su = empty_option;
- char **old_arg_files = xmalloc(sizeof(*old_arg_files) * GARGCOUNT);
for (int i = 0; i < GARGCOUNT; i++) {
- old_arg_files[i] = vim_strsave(GARGLIST[i].ae_fname);
+ old_arg_files[i] = xstrdup(GARGLIST[i].ae_fname);
}
int old_arg_count = GARGCOUNT;
char **new_arg_files;
@@ -106,13 +158,9 @@ void alist_expand(int *fnum_list, int fnum_len)
/// 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)
{
- static int recursive = 0;
-
- if (recursive) {
- emsg(_(e_au_recursive));
+ if (check_arglist_locked() == FAIL) {
return;
}
- recursive++;
alist_clear(al);
ga_grow(&al->al_ga, count);
@@ -130,7 +178,9 @@ void alist_set(alist_T *al, int count, char **files, int use_curbuf, int *fnum_l
// May set buffer name of a buffer previously used for the
// argument list, so that it's re-used by alist_add.
if (fnum_list != NULL && i < fnum_len) {
+ arglist_locked = true;
buf_set_name(fnum_list[i], files[i]);
+ arglist_locked = false;
}
alist_add(al, files[i], use_curbuf ? 2 : 1);
@@ -142,7 +192,6 @@ void alist_set(alist_T *al, int count, char **files, int use_curbuf, int *fnum_l
if (al == &global_alist) {
arg_had_last = false;
}
- recursive--;
}
/// Add file "fname" to argument list "al".
@@ -154,15 +203,22 @@ void alist_add(alist_T *al, char *fname, int set_fnum)
if (fname == NULL) { // don't add NULL file names
return;
}
+ if (check_arglist_locked() == FAIL) {
+ return;
+ }
+ arglist_locked = true;
+
#ifdef BACKSLASH_IN_FILENAME
slash_adjust(fname);
#endif
- AARGLIST(al)[al->al_ga.ga_len].ae_fname = (char_u *)fname;
+ AARGLIST(al)[al->al_ga.ga_len].ae_fname = 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));
}
al->al_ga.ga_len++;
+
+ arglist_locked = false;
}
#if defined(BACKSLASH_IN_FILENAME)
@@ -202,7 +258,7 @@ static char *do_one_arg(char *str)
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((char_u *)str)) {
+ if (rem_backslash(str)) {
*p++ = *str++;
*p++ = *str;
} else {
@@ -226,7 +282,7 @@ static char *do_one_arg(char *str)
/// growarray "gap".
static void get_arglist(garray_T *gap, char *str, int escaped)
{
- ga_init(gap, (int)sizeof(char_u *), 20);
+ ga_init(gap, (int)sizeof(char *), 20);
while (*str != NUL) {
GA_APPEND(char *, gap, str);
@@ -244,12 +300,12 @@ static void get_arglist(garray_T *gap, char *str, int escaped)
/// "fnames[fcountp]". When "wig" is true, removes files matching 'wildignore'.
///
/// @return FAIL or OK.
-int get_arglist_exp(char_u *str, int *fcountp, char ***fnamesp, bool wig)
+int get_arglist_exp(char *str, int *fcountp, char ***fnamesp, bool wig)
{
garray_T ga;
int i;
- get_arglist(&ga, (char *)str, true);
+ get_arglist(&ga, str, true);
if (wig) {
i = expand_wildcards(ga.ga_len, ga.ga_data,
@@ -284,7 +340,7 @@ static void alist_add_list(int count, char **files, int after, bool will_edit)
{
int old_argcount = ARGCOUNT;
ga_grow(&ALIST(curwin)->al_ga, count);
- {
+ if (check_arglist_locked() != FAIL) {
if (after < 0) {
after = 0;
}
@@ -295,11 +351,13 @@ static void alist_add_list(int count, char **files, int after, bool will_edit)
memmove(&(ARGLIST[after + count]), &(ARGLIST[after]),
(size_t)(ARGCOUNT - after) * sizeof(aentry_T));
}
+ arglist_locked = true;
for (int i = 0; i < count; i++) {
const int flags = BLN_LISTED | (will_edit ? BLN_CURBUF : 0);
- ARGLIST[after + i].ae_fname = (char_u *)files[i];
+ ARGLIST[after + i].ae_fname = files[i];
ARGLIST[after + i].ae_fnum = buflist_add(files[i], flags);
}
+ arglist_locked = false;
ALIST(curwin)->al_ga.ga_len += count;
if (old_argcount > 0 && curwin->w_arg_idx >= after) {
curwin->w_arg_idx += count;
@@ -308,6 +366,50 @@ static void alist_add_list(int count, char **files, int after, bool will_edit)
}
}
+/// Delete the file names in "alist_ga" from the argument list.
+static void arglist_del_files(garray_T *alist_ga)
+{
+ regmatch_T regmatch;
+
+ // Delete the items: use each item as a regexp and find a match in the
+ // argument list.
+ regmatch.rm_ic = p_fic; // ignore case when 'fileignorecase' is set
+ for (int i = 0; i < alist_ga->ga_len && !got_int; i++) {
+ char *p = ((char **)alist_ga->ga_data)[i];
+ p = file_pat_to_reg_pat(p, NULL, NULL, false);
+ if (p == NULL) {
+ break;
+ }
+ regmatch.regprog = vim_regcomp(p, magic_isset() ? RE_MAGIC : 0);
+ if (regmatch.regprog == NULL) {
+ xfree(p);
+ break;
+ }
+
+ bool didone = false;
+ for (int match = 0; match < ARGCOUNT; match++) {
+ if (vim_regexec(&regmatch, alist_name(&ARGLIST[match]), (colnr_T)0)) {
+ didone = true;
+ xfree(ARGLIST[match].ae_fname);
+ memmove(ARGLIST + match, ARGLIST + match + 1,
+ (size_t)(ARGCOUNT - match - 1) * sizeof(aentry_T));
+ ALIST(curwin)->al_ga.ga_len--;
+ if (curwin->w_arg_idx > match) {
+ curwin->w_arg_idx--;
+ }
+ match--;
+ }
+ }
+
+ vim_regfree(regmatch.regprog);
+ xfree(p);
+ if (!didone) {
+ semsg(_(e_nomatch2), ((char **)alist_ga->ga_data)[i]);
+ }
+ }
+ ga_clear(alist_ga);
+}
+
/// @param str
/// @param what
/// AL_SET: Redefine the argument list to 'str'.
@@ -324,10 +426,12 @@ static int do_arglist(char *str, int what, int after, bool will_edit)
garray_T new_ga;
int exp_count;
char **exp_files;
- char *p;
- int match;
int arg_escaped = true;
+ if (check_arglist_locked() == FAIL) {
+ return FAIL;
+ }
+
// Set default argument for ":argadd" command.
if (what == AL_ADD && *str == NUL) {
if (curbuf->b_ffname == NULL) {
@@ -341,46 +445,7 @@ static int do_arglist(char *str, int what, int after, bool will_edit)
get_arglist(&new_ga, str, arg_escaped);
if (what == AL_DEL) {
- regmatch_T regmatch;
- bool didone;
-
- // Delete the items: use each item as a regexp and find a match in the
- // 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 **)new_ga.ga_data)[i];
- p = file_pat_to_reg_pat(p, NULL, NULL, false);
- if (p == NULL) {
- break;
- }
- regmatch.regprog = vim_regcomp(p, p_magic ? RE_MAGIC : 0);
- if (regmatch.regprog == NULL) {
- xfree(p);
- break;
- }
-
- didone = false;
- for (match = 0; match < ARGCOUNT; match++) {
- if (vim_regexec(&regmatch, alist_name(&ARGLIST[match]), (colnr_T)0)) {
- didone = true;
- xfree(ARGLIST[match].ae_fname);
- memmove(ARGLIST + match, ARGLIST + match + 1,
- (size_t)(ARGCOUNT - match - 1) * sizeof(aentry_T));
- ALIST(curwin)->al_ga.ga_len--;
- if (curwin->w_arg_idx > match) {
- curwin->w_arg_idx--;
- }
- match--;
- }
- }
-
- vim_regfree(regmatch.regprog);
- xfree(p);
- if (!didone) {
- semsg(_(e_nomatch2), ((char_u **)new_ga.ga_data)[i]);
- }
- }
- ga_clear(&new_ga);
+ arglist_del_files(&new_ga);
} else {
int i = expand_wildcards(new_ga.ga_len, new_ga.ga_data,
&exp_count, &exp_files,
@@ -457,6 +522,9 @@ void check_arg_idx(win_T *win)
void ex_args(exarg_T *eap)
{
if (eap->cmdidx != CMD_args) {
+ if (check_arglist_locked() == FAIL) {
+ return;
+ }
alist_unlink(ALIST(curwin));
if (eap->cmdidx == CMD_argglobal) {
ALIST(curwin) = &global_alist;
@@ -465,34 +533,47 @@ void ex_args(exarg_T *eap)
}
}
+ // ":args file ..": define new argument list, handle like ":next"
+ // Also for ":argslocal file .." and ":argsglobal file ..".
if (*eap->arg != NUL) {
- // ":args file ..": define new argument list, handle like ":next"
- // Also for ":argslocal file .." and ":argsglobal file ..".
+ if (check_arglist_locked() == FAIL) {
+ return;
+ }
ex_next(eap);
- } else if (eap->cmdidx == CMD_args) {
- // ":args": list arguments.
- if (ARGCOUNT > 0) {
- 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);
- xfree(items);
+ return;
+ }
+
+ // ":args": list arguments.
+ if (eap->cmdidx == CMD_args) {
+ if (ARGCOUNT <= 0) {
+ return; // empty argument list
}
- } else if (eap->cmdidx == CMD_arglocal) {
+
+ char **items = xmalloc(sizeof(char *) * (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);
+ xfree(items);
+
+ return;
+ }
+
+ // ":argslocal": make a local copy of the global argument list.
+ if (eap->cmdidx == CMD_arglocal) {
garray_T *gap = &curwin->w_alist->al_ga;
- // ":argslocal": make a local copy of the global argument list.
ga_grow(gap, GARGCOUNT);
+
for (int i = 0; i < GARGCOUNT; i++) {
if (GARGLIST[i].ae_fname != NULL) {
- AARGLIST(curwin->w_alist)[gap->ga_len].ae_fname =
- vim_strsave(GARGLIST[i].ae_fname);
- AARGLIST(curwin->w_alist)[gap->ga_len].ae_fnum =
- GARGLIST[i].ae_fnum;
+ AARGLIST(curwin->w_alist)[gap->ga_len].ae_fname = xstrdup(GARGLIST[i].ae_fname);
+ AARGLIST(curwin->w_alist)[gap->ga_len].ae_fnum = GARGLIST[i].ae_fnum;
gap->ga_len++;
}
}
@@ -550,50 +631,52 @@ void do_argfile(exarg_T *eap, int argn)
} else {
emsg(_("E165: Cannot go beyond last file"));
}
- } else {
- setpcmark();
- // split window or create new tab page first
- if (*eap->cmd == 's' || cmdmod.cmod_tab != 0) {
- if (win_split(0, 0) == FAIL) {
- return;
- }
- RESET_BINDING(curwin);
- } else {
- // if 'hidden' set, only check for changed file when re-editing
- // the same buffer
- other = true;
- if (buf_hide(curbuf)) {
- p = fix_fname(alist_name(&ARGLIST[argn]));
- other = otherfile(p);
- xfree(p);
- }
- if ((!buf_hide(curbuf) || !other)
- && check_changed(curbuf, CCGD_AW
- | (other ? 0 : CCGD_MULTWIN)
- | (eap->forceit ? CCGD_FORCEIT : 0)
- | CCGD_EXCMD)) {
- return;
- }
- }
+ return;
+ }
- curwin->w_arg_idx = argn;
- if (argn == ARGCOUNT - 1 && curwin->w_alist == &global_alist) {
- arg_had_last = true;
- }
+ setpcmark();
- // Edit the file; always use the last known line number.
- // When it fails (e.g. Abort for already edited file) restore the
- // argument index.
- if (do_ecmd(0, alist_name(&ARGLIST[curwin->w_arg_idx]), NULL,
- eap, ECMD_LAST,
- (buf_hide(curwin->w_buffer) ? ECMD_HIDE : 0)
- + (eap->forceit ? ECMD_FORCEIT : 0), curwin) == FAIL) {
- curwin->w_arg_idx = old_arg_idx;
- } else if (eap->cmdidx != CMD_argdo) {
- // like Vi: set the mark where the cursor is in the file.
- setmark('\'');
+ // split window or create new tab page first
+ if (*eap->cmd == 's' || cmdmod.cmod_tab != 0) {
+ if (win_split(0, 0) == FAIL) {
+ return;
}
+ RESET_BINDING(curwin);
+ } else {
+ // if 'hidden' set, only check for changed file when re-editing
+ // the same buffer
+ other = true;
+ if (buf_hide(curbuf)) {
+ p = fix_fname(alist_name(&ARGLIST[argn]));
+ other = otherfile(p);
+ xfree(p);
+ }
+ if ((!buf_hide(curbuf) || !other)
+ && check_changed(curbuf, CCGD_AW
+ | (other ? 0 : CCGD_MULTWIN)
+ | (eap->forceit ? CCGD_FORCEIT : 0)
+ | CCGD_EXCMD)) {
+ return;
+ }
+ }
+
+ curwin->w_arg_idx = argn;
+ if (argn == ARGCOUNT - 1 && curwin->w_alist == &global_alist) {
+ arg_had_last = true;
+ }
+
+ // Edit the file; always use the last known line number.
+ // When it fails (e.g. Abort for already edited file) restore the
+ // argument index.
+ if (do_ecmd(0, alist_name(&ARGLIST[curwin->w_arg_idx]), NULL,
+ eap, ECMD_LAST,
+ (buf_hide(curwin->w_buffer) ? ECMD_HIDE : 0)
+ + (eap->forceit ? ECMD_FORCEIT : 0), curwin) == FAIL) {
+ curwin->w_arg_idx = old_arg_idx;
+ } else if (eap->cmdidx != CMD_argdo) {
+ // like Vi: set the mark where the cursor is in the file.
+ setmark('\'');
}
}
@@ -625,8 +708,17 @@ void ex_next(exarg_T *eap)
void ex_argdedupe(exarg_T *eap FUNC_ATTR_UNUSED)
{
for (int i = 0; i < ARGCOUNT; i++) {
+ // Expand each argument to a full path to catch different paths leading
+ // to the same file.
+ char *firstFullname = FullName_save(ARGLIST[i].ae_fname, false);
+
for (int j = i + 1; j < ARGCOUNT; j++) {
- if (FNAMECMP(ARGLIST[i].ae_fname, ARGLIST[j].ae_fname) == 0) {
+ char *secondFullname = FullName_save(ARGLIST[j].ae_fname, false);
+ bool areNamesDuplicate = path_fnamecmp(firstFullname, secondFullname) == 0;
+ xfree(secondFullname);
+
+ if (areNamesDuplicate) {
+ // remove one duplicate argument
xfree(ARGLIST[j].ae_fname);
memmove(ARGLIST + j, ARGLIST + j + 1,
(size_t)(ARGCOUNT - j - 1) * sizeof(aentry_T));
@@ -641,6 +733,8 @@ void ex_argdedupe(exarg_T *eap FUNC_ATTR_UNUSED)
j--;
}
}
+
+ xfree(firstFullname);
}
}
@@ -679,6 +773,10 @@ void ex_argadd(exarg_T *eap)
/// ":argdelete"
void ex_argdelete(exarg_T *eap)
{
+ if (check_arglist_locked() == FAIL) {
+ return;
+ }
+
if (eap->addr_count > 0 || *eap->arg == NUL) {
// ":argdel" works like ":.argdel"
if (eap->addr_count == 0) {
@@ -742,81 +840,37 @@ char *alist_name(aentry_T *aep)
// Use the name from the associated buffer if it exists.
bp = buflist_findnr(aep->ae_fnum);
if (bp == NULL || bp->b_fname == NULL) {
- return (char *)aep->ae_fname;
+ return aep->ae_fname;
}
return bp->b_fname;
}
-/// do_arg_all(): Open up to 'count' windows, one for each argument.
-///
-/// @param forceit hide buffers in current windows
-/// @param keep_tabs keep current tabs, for ":tab drop file"
-static void do_arg_all(int count, int forceit, int keep_tabs)
+/// Close all the windows containing files which are not in the argument list.
+/// Used by the ":all" command.
+static void arg_all_close_unused_windows(arg_all_state_T *aall)
{
- uint8_t *opened; // Array of weight for which args are open:
- // 0: not opened
- // 1: opened in other tab
- // 2: opened in curtab
- // 3: opened in curtab and curwin
-
- int opened_len; // length of opened[]
- int use_firstwin = false; // use first window for arglist
- bool tab_drop_empty_window = false;
- int split_ret = OK;
- bool p_ea_save;
- alist_T *alist; // argument list to be used
- buf_T *buf;
- tabpage_T *tpnext;
- int had_tab = cmdmod.cmod_tab;
- win_T *old_curwin, *last_curwin;
- tabpage_T *old_curtab, *last_curtab;
- win_T *new_curwin = NULL;
- tabpage_T *new_curtab = NULL;
-
- 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.
- return;
- }
- setpcmark();
-
- 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.
- alist = curwin->w_alist;
- alist->al_refcount++;
+ win_T *old_curwin = curwin;
+ tabpage_T *old_curtab = curtab;
- 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.
- if (had_tab > 0) {
+ if (aall->had_tab > 0) {
goto_tabpage_tp(first_tabpage, true, true);
}
for (;;) {
win_T *wpnext = NULL;
- tpnext = curtab->tp_next;
+ tabpage_T *tpnext = curtab->tp_next;
for (win_T *wp = firstwin; wp != NULL; wp = wpnext) {
int i;
wpnext = wp->w_next;
- buf = wp->w_buffer;
+ buf_T *buf = wp->w_buffer;
if (buf->b_ffname == NULL
- || (!keep_tabs && (buf->b_nwindows > 1 || wp->w_width != Columns))) {
- i = opened_len;
+ || (!aall->keep_tabs && (buf->b_nwindows > 1 || wp->w_width != Columns))) {
+ i = aall->opened_len;
} else {
// check if the buffer in this window is in the arglist
- for (i = 0; i < opened_len; i++) {
- if (i < alist->al_ga.ga_len
- && (AARGLIST(alist)[i].ae_fnum == buf->b_fnum
- || path_full_compare(alist_name(&AARGLIST(alist)[i]),
+ for (i = 0; i < aall->opened_len; i++) {
+ if (i < aall->alist->al_ga.ga_len
+ && (AARGLIST(aall->alist)[i].ae_fnum == buf->b_fnum
+ || path_full_compare(alist_name(&AARGLIST(aall->alist)[i]),
buf->b_ffname,
true, true) & kEqualFiles)) {
int weight = 1;
@@ -828,23 +882,24 @@ static void do_arg_all(int count, int forceit, int keep_tabs)
}
}
- if (weight > (int)opened[i]) {
- opened[i] = (uint8_t)weight;
+ if (weight > (int)aall->opened[i]) {
+ aall->opened[i] = (uint8_t)weight;
if (i == 0) {
- if (new_curwin != NULL) {
- new_curwin->w_arg_idx = opened_len;
+ if (aall->new_curwin != NULL) {
+ aall->new_curwin->w_arg_idx = aall->opened_len;
}
- new_curwin = wp;
- new_curtab = curtab;
+ aall->new_curwin = wp;
+ aall->new_curtab = curtab;
}
- } else if (keep_tabs) {
- i = opened_len;
+ } else if (aall->keep_tabs) {
+ i = aall->opened_len;
}
- if (wp->w_alist != alist) {
- // Use the current argument list for all windows containing a file from it.
+ if (wp->w_alist != aall->alist) {
+ // 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 = aall->alist;
wp->w_alist->al_refcount++;
}
break;
@@ -853,8 +908,8 @@ static void do_arg_all(int count, int forceit, int keep_tabs)
}
wp->w_arg_idx = i;
- if (i == opened_len && !keep_tabs) { // close this window
- if (buf_hide(buf) || forceit || buf->b_nwindows > 1
+ if (i == aall->opened_len && !aall->keep_tabs) { // close this window
+ if (buf_hide(buf) || aall->forceit || buf->b_nwindows > 1
|| !bufIsChanged(buf)) {
// If the buffer was changed, and we would like to hide it, try autowriting.
if (!buf_hide(buf) && buf->b_nwindows <= 1 && bufIsChanged(buf)) {
@@ -869,8 +924,8 @@ static void do_arg_all(int count, int forceit, int keep_tabs)
}
// don't close last window
if (ONE_WINDOW
- && (first_tabpage->tp_next == NULL || !had_tab)) {
- use_firstwin = true;
+ && (first_tabpage->tp_next == NULL || !aall->had_tab)) {
+ aall->use_firstwin = true;
} else {
win_close(wp, !buf_hide(buf) && !bufIsChanged(buf), false);
// check if autocommands removed the next window
@@ -884,7 +939,7 @@ static void do_arg_all(int count, int forceit, int keep_tabs)
}
// Without the ":tab" modifier only do the current tab page.
- if (had_tab == 0 || tpnext == NULL) {
+ if (aall->had_tab == 0 || tpnext == NULL) {
break;
}
@@ -894,39 +949,35 @@ static 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.
- if (count > opened_len || count <= 0) {
- count = opened_len;
- }
+/// Open up to "count" windows for the files in the argument list "aall->alist".
+static void arg_all_open_windows(arg_all_state_T *aall, int count)
+{
+ bool tab_drop_empty_window = false;
- // Don't execute Win/Buf Enter/Leave autocommands here.
- autocmd_no_enter++;
- autocmd_no_leave++;
- last_curwin = curwin;
- last_curtab = curtab;
- win_enter(lastwin, false);
// ":tab drop file" should re-use an empty window to avoid "--remote-tab"
// leaving an empty tab page when executed locally.
- if (keep_tabs && buf_is_empty(curbuf) && curbuf->b_nwindows == 1
+ if (aall->keep_tabs && buf_is_empty(curbuf) && curbuf->b_nwindows == 1
&& curbuf->b_ffname == NULL && !curbuf->b_changed) {
- use_firstwin = true;
+ aall->use_firstwin = true;
tab_drop_empty_window = true;
}
+ int split_ret = OK;
+
for (int i = 0; i < count && !got_int; i++) {
- if (alist == &global_alist && i == global_alist.al_ga.ga_len - 1) {
+ if (aall->alist == &global_alist && i == global_alist.al_ga.ga_len - 1) {
arg_had_last = true;
}
- if (opened[i] > 0) {
+ if (aall->opened[i] > 0) {
// Move the already present window to below the current window
if (curwin->w_arg_idx != i) {
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_arg_idx == i) {
- if (keep_tabs) {
- new_curwin = wp;
- new_curtab = curtab;
+ if (aall->keep_tabs) {
+ aall->new_curwin = wp;
+ aall->new_curtab = curtab;
} else if (wp->w_frame->fr_parent != curwin->w_frame->fr_parent) {
emsg(_("E249: window layout changed unexpectedly"));
i = count;
@@ -943,8 +994,8 @@ static void do_arg_all(int count, int forceit, int keep_tabs)
if (tab_drop_empty_window && i == count - 1) {
autocmd_no_enter--;
}
- if (!use_firstwin) { // split current window
- p_ea_save = p_ea;
+ if (!aall->use_firstwin) { // split current window
+ bool p_ea_save = p_ea;
p_ea = true; // use space from all windows
split_ret = win_split(0, WSP_ROOM | WSP_BELOW);
p_ea = p_ea_save;
@@ -958,36 +1009,102 @@ static void do_arg_all(int count, int forceit, int keep_tabs)
// edit file "i"
curwin->w_arg_idx = i;
if (i == 0) {
- new_curwin = curwin;
- new_curtab = curtab;
+ aall->new_curwin = curwin;
+ aall->new_curtab = curtab;
}
- (void)do_ecmd(0, alist_name(&AARGLIST(alist)[i]), NULL, NULL, ECMD_ONE,
+ (void)do_ecmd(0, alist_name(&AARGLIST(aall->alist)[i]), NULL, NULL, ECMD_ONE,
((buf_hide(curwin->w_buffer)
- || bufIsChanged(curwin->w_buffer))
- ? ECMD_HIDE : 0) + ECMD_OLDBUF,
+ || bufIsChanged(curwin->w_buffer)) ? ECMD_HIDE : 0) + ECMD_OLDBUF,
curwin);
if (tab_drop_empty_window && i == count - 1) {
autocmd_no_enter++;
}
- if (use_firstwin) {
+ if (aall->use_firstwin) {
autocmd_no_leave++;
}
- use_firstwin = false;
+ aall->use_firstwin = false;
}
os_breakcheck();
// When ":tab" was used open a new tab for a new window repeatedly.
- if (had_tab > 0 && tabpage_index(NULL) <= p_tpm) {
+ if (aall->had_tab > 0 && tabpage_index(NULL) <= p_tpm) {
cmdmod.cmod_tab = 9999;
}
}
+}
+
+/// do_arg_all(): Open up to 'count' windows, one for each argument.
+///
+/// @param forceit hide buffers in current windows
+/// @param keep_tabs keep current tabs, for ":tab drop file"
+static void do_arg_all(int count, int forceit, int keep_tabs)
+{
+ win_T *last_curwin;
+ tabpage_T *last_curtab;
+ bool prev_arglist_locked = arglist_locked;
+
+ assert(firstwin != NULL); // satisfy coverity
+
+ if (cmdwin_type != 0) {
+ emsg(_(e_cmdwin));
+ return;
+ }
+ if (ARGCOUNT <= 0) {
+ // Don't give an error message. We don't want it when the ":all"
+ // command is in the .vimrc.
+ return;
+ }
+ setpcmark();
+
+ arg_all_state_T aall = {
+ .use_firstwin = false,
+ .had_tab = cmdmod.cmod_tab,
+ .new_curwin = NULL,
+ .new_curtab = NULL,
+ .forceit = forceit,
+ .keep_tabs = keep_tabs,
+ .opened_len = ARGCOUNT,
+ .opened = xcalloc((size_t)ARGCOUNT, 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.
+ aall.alist = curwin->w_alist;
+ aall.alist->al_refcount++;
+ arglist_locked = true;
+
+ // 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.
+ arg_all_close_unused_windows(&aall);
+
+ // 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 > aall.opened_len || count <= 0) {
+ count = aall.opened_len;
+ }
+
+ // Don't execute Win/Buf Enter/Leave autocommands here.
+ autocmd_no_enter++;
+ autocmd_no_leave++;
+ last_curwin = curwin;
+ last_curtab = curtab;
+ win_enter(lastwin, false);
+
+ // Open up to "count" windows.
+ arg_all_open_windows(&aall, count);
// Remove the "lock" on the argument list.
- alist_unlink(alist);
+ alist_unlink(aall.alist);
+ arglist_locked = prev_arglist_locked;
autocmd_no_enter--;
+
// restore last referenced tabpage's curwin
- if (last_curtab != new_curtab) {
+ if (last_curtab != aall.new_curtab) {
if (valid_tabpage(last_curtab)) {
goto_tabpage_tp(last_curtab, true, true);
}
@@ -996,15 +1113,15 @@ static void do_arg_all(int count, int forceit, int keep_tabs)
}
}
// to window with first arg
- if (valid_tabpage(new_curtab)) {
- goto_tabpage_tp(new_curtab, true, true);
+ if (valid_tabpage(aall.new_curtab)) {
+ goto_tabpage_tp(aall.new_curtab, true, true);
}
- if (win_valid(new_curwin)) {
- win_enter(new_curwin, false);
+ if (win_valid(aall.new_curwin)) {
+ win_enter(aall.new_curwin, false);
}
autocmd_no_leave--;
- xfree(opened);
+ xfree(aall.opened);
}
/// ":all" and ":sall".
@@ -1074,7 +1191,7 @@ char *arg_all(void)
}
/// "argc([window id])" function
-void f_argc(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_argc(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[0].v_type == VAR_UNKNOWN) {
// use the current window
@@ -1095,13 +1212,13 @@ void f_argc(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "argidx()" function
-void f_argidx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_argidx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = curwin->w_arg_idx;
}
/// "arglistid()" function
-void f_arglistid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_arglistid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = -1;
win_T *wp = find_tabwin(&argvars[0], &argvars[1]);
@@ -1123,36 +1240,38 @@ static void get_arglist_as_rettv(aentry_T *arglist, int argcount, typval_T *rett
}
/// "argv(nr)" function
-void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_argv(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
aentry_T *arglist = NULL;
int argcount = -1;
- if (argvars[0].v_type != VAR_UNKNOWN) {
- if (argvars[1].v_type == VAR_UNKNOWN) {
- arglist = ARGLIST;
- argcount = ARGCOUNT;
- } else if (argvars[1].v_type == VAR_NUMBER
- && tv_get_number(&argvars[1]) == -1) {
- arglist = GARGLIST;
- argcount = GARGCOUNT;
- } else {
- win_T *wp = find_win_by_nr_or_id(&argvars[1]);
- if (wp != NULL) {
- // Use the argument list of the specified window
- arglist = WARGLIST(wp);
- argcount = WARGCOUNT(wp);
- }
- }
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = NULL;
- int idx = (int)tv_get_number_chk(&argvars[0], NULL);
- if (arglist != NULL && idx >= 0 && idx < argcount) {
- rettv->vval.v_string = xstrdup((const char *)alist_name(&arglist[idx]));
- } else if (idx == -1) {
- get_arglist_as_rettv(arglist, argcount, rettv);
- }
- } else {
+ if (argvars[0].v_type == VAR_UNKNOWN) {
get_arglist_as_rettv(ARGLIST, ARGCOUNT, rettv);
+ return;
+ }
+
+ if (argvars[1].v_type == VAR_UNKNOWN) {
+ arglist = ARGLIST;
+ argcount = ARGCOUNT;
+ } else if (argvars[1].v_type == VAR_NUMBER
+ && tv_get_number(&argvars[1]) == -1) {
+ arglist = GARGLIST;
+ argcount = GARGCOUNT;
+ } else {
+ win_T *wp = find_win_by_nr_or_id(&argvars[1]);
+ if (wp != NULL) {
+ // Use the argument list of the specified window
+ arglist = WARGLIST(wp);
+ argcount = WARGCOUNT(wp);
+ }
+ }
+
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+ int idx = (int)tv_get_number_chk(&argvars[0], NULL);
+ if (arglist != NULL && idx >= 0 && idx < argcount) {
+ rettv->vval.v_string = xstrdup((const char *)alist_name(&arglist[idx]));
+ } else if (idx == -1) {
+ get_arglist_as_rettv(arglist, argcount, rettv);
}
}
diff --git a/src/nvim/ascii.h b/src/nvim/ascii.h
index b1241166bf..96d6fe214a 100644
--- a/src/nvim/ascii.h
+++ b/src/nvim/ascii.h
@@ -84,31 +84,31 @@
# define PATHSEPSTR "/"
#endif
-static inline bool ascii_iswhite(int)
+static inline bool ascii_iswhite(int c)
REAL_FATTR_CONST
REAL_FATTR_ALWAYS_INLINE;
-static inline bool ascii_iswhite_or_nul(int)
+static inline bool ascii_iswhite_or_nul(int c)
REAL_FATTR_CONST
REAL_FATTR_ALWAYS_INLINE;
-static inline bool ascii_isdigit(int)
+static inline bool ascii_isdigit(int c)
REAL_FATTR_CONST
REAL_FATTR_ALWAYS_INLINE;
-static inline bool ascii_isxdigit(int)
+static inline bool ascii_isxdigit(int c)
REAL_FATTR_CONST
REAL_FATTR_ALWAYS_INLINE;
-static inline bool ascii_isident(int)
+static inline bool ascii_isident(int c)
REAL_FATTR_CONST
REAL_FATTR_ALWAYS_INLINE;
-static inline bool ascii_isbdigit(int)
+static inline bool ascii_isbdigit(int c)
REAL_FATTR_CONST
REAL_FATTR_ALWAYS_INLINE;
-static inline bool ascii_isspace(int)
+static inline bool ascii_isspace(int c)
REAL_FATTR_CONST
REAL_FATTR_ALWAYS_INLINE;
diff --git a/src/nvim/assert.h b/src/nvim/assert.h
index ad92d9a2af..1941f4c33c 100644
--- a/src/nvim/assert.h
+++ b/src/nvim/assert.h
@@ -61,7 +61,7 @@
# define STATIC_ASSERT_STATEMENT(cond, msg) _Static_assert(cond, msg)
// if we're dealing with gcc >= 4.6 in C99 mode, we can still use
// _Static_assert but we need to suppress warnings, this is pretty ugly.
-#elif (!defined(__clang__) && !defined(__INTEL_COMPILER)) && \
+#elif (!defined(__clang__) && !defined(__INTEL_COMPILER)) && /* NOLINT(whitespace/parens)*/ \
(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
# define STATIC_ASSERT_STATEMENT(cond, msg) _Static_assert(cond, msg)
diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua
index 93a870fe04..a75ee3bbd5 100644
--- a/src/nvim/auevents.lua
+++ b/src/nvim/auevents.lua
@@ -108,6 +108,7 @@ return {
'TextChanged', -- text was modified
'TextChangedI', -- text was modified in Insert mode(no popup)
'TextChangedP', -- text was modified in Insert mode(popup)
+ 'TextChangedT', -- text was modified in Terminal mode
'TextYankPost', -- after a yank or delete was done (y, d, c)
'UIEnter', -- after UI attaches
'UILeave', -- after UI detaches
@@ -122,13 +123,26 @@ return {
'WinEnter', -- after entering a window
'WinLeave', -- before leaving a window
'WinNew', -- when entering a new window
- 'WinScrolled', -- after scrolling a window
+ 'WinResized', -- after a window was resized
+ 'WinScrolled', -- after a window was scrolled or resized
},
aliases = {
- BufCreate = 'BufAdd',
- BufRead = 'BufReadPost',
- BufWrite = 'BufWritePre',
- FileEncoding = 'EncodingChanged',
+ {
+ 'BufCreate',
+ 'BufAdd'
+ },
+ {
+ 'BufRead',
+ 'BufReadPost'
+ },
+ {
+ 'BufWrite',
+ 'BufWritePre'
+ },
+ {
+ 'FileEncoding',
+ 'EncodingChanged'
+ },
},
-- List of nvim-specific events or aliases for the purpose of generating
-- syntax file
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index 579c6c029f..01ebdfdafe 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -2,9 +2,13 @@
// 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 <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
#include "nvim/autocmd.h"
@@ -12,26 +16,43 @@
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/drawscreen.h"
-#include "nvim/edit.h"
#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
#include "nvim/eval/userfunc.h"
#include "nvim/eval/vars.h"
+#include "nvim/event/defs.h"
+#include "nvim/event/loop.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
+#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
#include "nvim/grid.h"
+#include "nvim/hashtab.h"
+#include "nvim/highlight_defs.h"
#include "nvim/insexpand.h"
#include "nvim/lua/executor.h"
+#include "nvim/main.h"
#include "nvim/map.h"
+#include "nvim/memline_defs.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
+#include "nvim/option_defs.h"
#include "nvim/optionstr.h"
#include "nvim/os/input.h"
+#include "nvim/os/os.h"
+#include "nvim/os/time.h"
+#include "nvim/path.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
+#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/state.h"
+#include "nvim/strings.h"
+#include "nvim/ui.h"
#include "nvim/ui_compositor.h"
#include "nvim/vim.h"
#include "nvim/window.h"
@@ -266,7 +287,7 @@ static void au_show_for_event(int group, event_T event, char *pat)
// 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);
+ patlen = (int)strlen(buflocal_pat);
}
assert(*pat != NUL);
@@ -283,7 +304,7 @@ static void au_show_for_event(int group, event_T event, char *pat)
// all buffer-local patterns.
if ((group == AUGROUP_ALL || ap->group == group)
&& ap->patlen == patlen
- && STRNCMP(pat, ap->pat, patlen) == 0) {
+ && strncmp(pat, ap->pat, (size_t)patlen) == 0) {
// Show autocmd's for this autopat, or buflocals <buffer=X>
aupat_show(ap, event, previous_group);
previous_group = ap->group;
@@ -478,34 +499,37 @@ void augroup_del(char *name, bool stupid_legacy_mode)
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) {
+ return;
+ }
+ if (i == current_augroup) {
emsg(_("E936: Cannot delete the current group"));
- } else {
- 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;
- }
+ return;
+ }
+
+ 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;
}
}
- } else {
- FOR_ALL_AUEVENTS(event) {
- FOR_ALL_AUPATS_IN_EVENT(event, ap) {
- if (ap->group == i) {
- aupat_del(ap);
- }
+ }
+ } else {
+ 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();
}
+
+ // Remove the group because it's not currently in use.
+ augroup_map_del(i, name);
+ au_cleanup();
}
/// Find the ID of an autocmd group name.
@@ -635,9 +659,22 @@ void free_all_autocmds(void)
api_free_string(name);
})
map_destroy(int, String)(&map_augroup_id_to_name);
+
+ // aucmd_win[] is freed in win_free_all()
}
#endif
+/// Return true if "win" is an active entry in aucmd_win[].
+bool is_aucmd_win(win_T *win)
+{
+ for (int i = 0; i < AUCMD_WIN_COUNT; i++) {
+ if (aucmd_win[i].auc_win_used && aucmd_win[i].auc_win == win) {
+ return true;
+ }
+ }
+ return false;
+}
+
// 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".
@@ -686,7 +723,7 @@ const char *event_nr2name(event_T event)
static bool event_ignored(event_T event)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- char *p = (char *)p_ei;
+ char *p = p_ei;
while (*p != NUL) {
if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ',')) {
@@ -703,7 +740,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 *p = (char *)p_ei;
+ char *p = p_ei;
while (*p) {
if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ',')) {
@@ -724,8 +761,8 @@ int check_ei(void)
// Returns the old value of 'eventignore' in allocated memory.
char *au_event_disable(char *what)
{
- char *save_ei = (char *)vim_strsave(p_ei);
- char *new_ei = (char *)vim_strnsave(p_ei, STRLEN(p_ei) + STRLEN(what));
+ char *save_ei = xstrdup(p_ei);
+ char *new_ei = xstrnsave(p_ei, strlen(p_ei) + strlen(what));
if (*what == ',' && *p_ei == NUL) {
STRCPY(new_ei, what + 1);
} else {
@@ -733,7 +770,6 @@ char *au_event_disable(char *what)
}
set_string_option_direct("ei", -1, new_ei, OPT_FREE, SID_NONE);
xfree(new_ei);
-
return save_ei;
}
@@ -777,7 +813,7 @@ void au_event_restore(char *old_ei)
// :autocmd * *.c show all autocommands for *.c files.
//
// Mostly a {group} argument can optionally appear before <event>.
-void do_autocmd(char *arg_in, int forceit)
+void do_autocmd(exarg_T *eap, char *arg_in, int forceit)
{
char *arg = arg_in;
char *envpat = NULL;
@@ -788,6 +824,7 @@ void do_autocmd(char *arg_in, int forceit)
int group;
if (*arg == '|') {
+ eap->nextcmd = arg + 1;
arg = "";
group = AUGROUP_ALL; // no argument, use all groups
} else {
@@ -804,6 +841,7 @@ void do_autocmd(char *arg_in, int forceit)
pat = skipwhite(pat);
if (*pat == '|') {
+ eap->nextcmd = pat + 1;
pat = "";
cmd = "";
} else {
@@ -837,13 +875,15 @@ void do_autocmd(char *arg_in, int forceit)
bool invalid_flags = false;
for (size_t i = 0; i < 2; i++) {
- if (*cmd != NUL) {
- invalid_flags |= arg_autocmd_flag_get(&once, &cmd, "++once", 6);
- invalid_flags |= arg_autocmd_flag_get(&nested, &cmd, "++nested", 8);
-
- // Check the deprecated "nested" flag.
- invalid_flags |= arg_autocmd_flag_get(&nested, &cmd, "nested", 6);
+ if (*cmd == NUL) {
+ continue;
}
+
+ invalid_flags |= arg_autocmd_flag_get(&once, &cmd, "++once", 6);
+ invalid_flags |= arg_autocmd_flag_get(&nested, &cmd, "++nested", 8);
+
+ // Check the deprecated "nested" flag.
+ invalid_flags |= arg_autocmd_flag_get(&nested, &cmd, "nested", 6);
}
if (invalid_flags) {
@@ -877,7 +917,7 @@ void do_autocmd(char *arg_in, int forceit)
}
} else {
if (*arg == '*' || *arg == NUL || *arg == '|') {
- if (!forceit && *cmd != NUL) {
+ if (*cmd != NUL) {
emsg(_(e_cannot_define_autocommands_for_all_events));
} else {
do_all_autocmd_events(pat, once, nested, cmd, forceit, group);
@@ -957,7 +997,7 @@ int do_autocmd_event(event_T event, char *pat, bool once, int nested, char *cmd,
aupat_normalize_buflocal_pat(buflocal_pat, pat, patlen, buflocal_nr);
pat = buflocal_pat;
- patlen = (int)STRLEN(buflocal_pat);
+ patlen = (int)strlen(buflocal_pat);
}
if (delete) {
@@ -975,7 +1015,7 @@ int do_autocmd_event(event_T event, char *pat, bool once, int nested, char *cmd,
// all buffer-local patterns.
if (ap->group == findgroup
&& ap->patlen == patlen
- && STRNCMP(pat, ap->pat, patlen) == 0) {
+ && strncmp(pat, ap->pat, (size_t)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
@@ -1018,7 +1058,7 @@ int autocmd_register(int64_t id, event_T event, char *pat, int patlen, int group
int findgroup;
char buflocal_pat[BUFLOCAL_PAT_LEN]; // for "<buffer=X>"
- if (patlen > (int)STRLEN(pat)) {
+ if (patlen > (int)strlen(pat)) {
return FAIL;
}
@@ -1039,7 +1079,7 @@ int autocmd_register(int64_t id, event_T event, char *pat, int patlen, int group
aupat_normalize_buflocal_pat(buflocal_pat, pat, patlen, buflocal_nr);
pat = buflocal_pat;
- patlen = (int)STRLEN(buflocal_pat);
+ patlen = (int)strlen(buflocal_pat);
}
// always goes at or after the last one, so start at the end.
@@ -1059,7 +1099,7 @@ int autocmd_register(int64_t id, event_T event, char *pat, int patlen, int group
// all buffer-local patterns.
if (ap->group == findgroup
&& ap->patlen == patlen
- && STRNCMP(pat, ap->pat, patlen) == 0) {
+ && strncmp(pat, ap->pat, (size_t)patlen) == 0) {
if (ap->next == NULL) {
// Add autocmd to this autopat, if it's the last one.
break;
@@ -1114,13 +1154,19 @@ int autocmd_register(int64_t id, event_T event, char *pat, int patlen, int group
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;
+ // Initialize the fields checked by the WinScrolled and
+ // WinResized trigger to prevent them from firing right after
+ // the first autocmd is defined.
+ if ((event == EVENT_WINSCROLLED || event == EVENT_WINRESIZED)
+ && !(has_event(EVENT_WINSCROLLED) || has_event(EVENT_WINRESIZED))) {
+ tabpage_T *save_curtab = curtab;
+ FOR_ALL_TABS(tp) {
+ unuse_tabpage(curtab);
+ use_tabpage(tp);
+ snapshot_windows_scroll_size();
+ }
+ unuse_tabpage(curtab);
+ use_tabpage(save_curtab);
}
ap->cmds = NULL;
@@ -1192,7 +1238,7 @@ size_t aucmd_pattern_length(char *pat)
return (size_t)(endpat - pat);
}
- return STRLEN(pat);
+ return strlen(pat);
}
char *aucmd_next_pattern(char *pat, size_t patlen)
@@ -1274,6 +1320,7 @@ void ex_doautoall(exarg_T *eap)
if (buf->b_ml.ml_mfp == NULL || buf == curbuf) {
continue;
}
+
// Find a window for this buffer and save some values.
aucmd_prepbuf(&aco, buf);
set_bufref(&bufref, buf);
@@ -1285,7 +1332,7 @@ void ex_doautoall(exarg_T *eap)
// Execute the modeline settings, but don't set window-local
// options if we are using the current window for another
// buffer.
- do_modelines(curwin == aucmd_win ? OPT_NOWIN : 0);
+ do_modelines(is_aucmd_win(curwin) ? OPT_NOWIN : 0);
}
// restore the current window
@@ -1315,7 +1362,7 @@ void ex_doautoall(exarg_T *eap)
bool check_nomodeline(char **argp)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
- if (STRNCMP(*argp, "<nomodeline>", 12) == 0) {
+ if (strncmp(*argp, "<nomodeline>", 12) == 0) {
*argp = skipwhite(*argp + 12);
return false;
}
@@ -1324,7 +1371,7 @@ bool check_nomodeline(char **argp)
/// Prepare for executing autocommands for (hidden) buffer `buf`.
/// If the current buffer is not in any visible window, put it in a temporary
-/// floating window `aucmd_win`.
+/// floating window using an entry in `aucmd_win[]`.
/// Set `curbuf` and `curwin` to match `buf`.
///
/// @param aco structure to save values in
@@ -1347,15 +1394,29 @@ void aucmd_prepbuf(aco_save_T *aco, buf_T *buf)
}
}
- // Allocate the `aucmd_win` dummy floating window.
- if (win == NULL && aucmd_win == NULL) {
- win_alloc_aucmd_win();
- need_append = false;
- }
- if (win == NULL && aucmd_win_used) {
- // Strange recursive autocommand, fall back to using the current
- // window. Expect a few side effects...
- win = curwin;
+ // Allocate a window when needed.
+ win_T *auc_win = NULL;
+ int auc_idx = AUCMD_WIN_COUNT;
+ if (win == NULL) {
+ for (auc_idx = 0; auc_idx < AUCMD_WIN_COUNT; auc_idx++) {
+ if (!aucmd_win[auc_idx].auc_win_used) {
+ break;
+ }
+ }
+
+ if (auc_idx == AUCMD_WIN_COUNT) {
+ kv_push(aucmd_win_vec, ((aucmdwin_T){
+ .auc_win = NULL,
+ .auc_win_used = false,
+ }));
+ }
+
+ if (aucmd_win[auc_idx].auc_win == NULL) {
+ win_alloc_aucmd_win(auc_idx);
+ need_append = false;
+ }
+ auc_win = aucmd_win[auc_idx].auc_win;
+ aucmd_win[auc_idx].auc_win_used = true;
}
aco->save_curwin_handle = curwin->handle;
@@ -1365,42 +1426,41 @@ void aucmd_prepbuf(aco_save_T *aco, buf_T *buf)
// There is a window for "buf" in the current tab page, make it the
// curwin. This is preferred, it has the least side effects (esp. if
// "buf" is curbuf).
- aco->use_aucmd_win = false;
+ aco->use_aucmd_win_idx = -1;
curwin = win;
} else {
- // There is no window for "buf", use "aucmd_win". To minimize the side
+ // There is no window for "buf", use "auc_win". To minimize the side
// effects, insert it in the current tab page.
// Anything related to a window (e.g., setting folds) may have
// unexpected results.
- aco->use_aucmd_win = true;
- aucmd_win_used = true;
- aucmd_win->w_buffer = buf;
- aucmd_win->w_s = &buf->b_s;
+ aco->use_aucmd_win_idx = auc_idx;
+ auc_win->w_buffer = buf;
+ auc_win->w_s = &buf->b_s;
buf->b_nwindows++;
- win_init_empty(aucmd_win); // set cursor and topline to safe values
+ win_init_empty(auc_win); // set cursor and topline to safe values
// Make sure w_localdir and globaldir are NULL to avoid a chdir() in
// win_enter_ext().
- XFREE_CLEAR(aucmd_win->w_localdir);
+ XFREE_CLEAR(auc_win->w_localdir);
aco->globaldir = globaldir;
globaldir = NULL;
block_autocmds(); // We don't want BufEnter/WinEnter autocommands.
if (need_append) {
- win_append(lastwin, aucmd_win);
- pmap_put(handle_T)(&window_handles, aucmd_win->handle, aucmd_win);
- win_config_float(aucmd_win, aucmd_win->w_float_config);
+ win_append(lastwin, auc_win);
+ pmap_put(handle_T)(&window_handles, auc_win->handle, auc_win);
+ win_config_float(auc_win, auc_win->w_float_config);
}
// 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);
+ win_enter(auc_win, false);
RedrawingDisabled--;
p_acd = save_acd;
unblock_autocmds();
- curwin = aucmd_win;
+ curwin = auc_win;
}
curbuf = buf;
aco->new_curwin_handle = curwin->handle;
@@ -1417,18 +1477,20 @@ void aucmd_prepbuf(aco_save_T *aco, buf_T *buf)
/// @param aco structure holding saved values
void aucmd_restbuf(aco_save_T *aco)
{
- if (aco->use_aucmd_win) {
+ if (aco->use_aucmd_win_idx >= 0) {
+ win_T *awp = aucmd_win[aco->use_aucmd_win_idx].auc_win;
+
curbuf->b_nwindows--;
- // Find "aucmd_win", it can't be closed, but it may be in another tab page.
+ // Find "awp", it can't be closed, but it may be in another tab page.
// Do not trigger autocommands here.
block_autocmds();
- if (curwin != aucmd_win) {
+ if (curwin != awp) {
FOR_ALL_TAB_WINDOWS(tp, wp) {
- if (wp == aucmd_win) {
+ if (wp == awp) {
if (tp != curtab) {
goto_tabpage_tp(tp, true, true);
}
- win_goto(aucmd_win);
+ win_goto(awp);
goto win_found;
}
}
@@ -1443,8 +1505,9 @@ win_found:
grid_free(&curwin->w_grid_alloc);
}
- aucmd_win_used = false;
- last_status(false); // may need to remove last status line
+ // The window is marked as not used, but it is not freed, it can be
+ // used again.
+ aucmd_win[aco->use_aucmd_win_idx].auc_win_used = false;
if (!valid_tabpage_win(curtab)) {
// no valid window in current tabpage
@@ -1465,8 +1528,8 @@ win_found:
entering_window(curwin);
prevwin = win_find_by_handle(aco->save_prevwin_handle);
- vars_clear(&aucmd_win->w_vars->dv_hashtab); // free all w: variables
- hash_init(&aucmd_win->w_vars->dv_hashtab); // re-use the hashtab
+ vars_clear(&awp->w_vars->dv_hashtab); // free all w: variables
+ hash_init(&awp->w_vars->dv_hashtab); // re-use the hashtab
xfree(globaldir);
globaldir = aco->globaldir;
@@ -1720,9 +1783,9 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
fname = NULL;
} else {
if (event == EVENT_SYNTAX) {
- fname = (char *)buf->b_p_syn;
+ fname = buf->b_p_syn;
} else if (event == EVENT_FILETYPE) {
- fname = (char *)buf->b_p_ft;
+ fname = buf->b_p_ft;
} else {
if (buf->b_sfname != NULL) {
sfname = xstrdup(buf->b_sfname);
@@ -1738,17 +1801,18 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
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_CMDLINELEAVE || event == EVENT_CMDUNDEFINED
+ || event == EVENT_CMDWINENTER || event == EVENT_CMDWINLEAVE
|| event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
|| 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) {
+ || event == EVENT_MENUPOPUP || event == EVENT_MODECHANGED
+ || event == EVENT_OPTIONSET || event == EVENT_QUICKFIXCMDPOST
+ || event == EVENT_QUICKFIXCMDPRE || event == EVENT_REMOTEREPLY
+ || event == EVENT_SIGNAL || event == EVENT_SPELLFILEMISSING
+ || event == EVENT_SYNTAX || event == EVENT_TABCLOSED
+ || event == EVENT_USER || event == EVENT_WINCLOSED
+ || event == EVENT_WINRESIZED || event == EVENT_WINSCROLLED) {
fname = xstrdup(fname);
} else {
fname = FullName_save(fname, false);
@@ -1811,16 +1875,15 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
char *tail = path_tail(fname);
// Find first autocommand that matches
- AutoPatCmd patcmd;
- patcmd.curpat = first_autopat[(int)event];
- patcmd.nextcmd = NULL;
- patcmd.group = group;
- patcmd.fname = fname;
- patcmd.sfname = sfname;
- patcmd.tail = tail;
- patcmd.event = event;
- patcmd.arg_bufnr = autocmd_bufnr;
- patcmd.next = NULL;
+ AutoPatCmd patcmd = {
+ .curpat = first_autopat[(int)event],
+ .group = group,
+ .fname = fname,
+ .sfname = sfname,
+ .tail = tail,
+ .event = event,
+ .arg_bufnr = autocmd_bufnr,
+ };
auto_next_pat(&patcmd, false);
// found one, start executing the autocommands
@@ -1984,9 +2047,12 @@ void auto_next_pat(AutoPatCmd *apc, int stop_at_last)
AutoPat *ap;
AutoCmd *cp;
char *s;
- char **const sourcing_namep = &SOURCING_NAME;
- XFREE_CLEAR(*sourcing_namep);
+ estack_T *const entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
+
+ // Clear the exestack entry for this ETYPE_AUCMD entry.
+ XFREE_CLEAR(entry->es_name);
+ entry->es_info.aucmd = NULL;
for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next) {
apc->curpat = NULL;
@@ -2009,16 +2075,20 @@ void auto_next_pat(AutoPatCmd *apc, int stop_at_last)
s = _("%s Autocommands for \"%s\"");
const size_t sourcing_name_len
- = (STRLEN(s) + strlen(name) + (size_t)ap->patlen + 1);
+ = (strlen(s) + strlen(name) + (size_t)ap->patlen + 1);
- *sourcing_namep = xmalloc(sourcing_name_len);
- snprintf(*sourcing_namep, sourcing_name_len, s, name, ap->pat);
+ char *const namep = xmalloc(sourcing_name_len);
+ snprintf(namep, sourcing_name_len, s, name, ap->pat);
if (p_verbose >= 8) {
verbose_enter();
- smsg(_("Executing %s"), *sourcing_namep);
+ smsg(_("Executing %s"), namep);
verbose_leave();
}
+ // Update the exestack entry for this autocmd.
+ entry->es_name = namep;
+ entry->es_info.aucmd = apc;
+
apc->curpat = ap;
apc->nextcmd = ap->cmds;
// mark last command
@@ -2051,7 +2121,7 @@ static bool call_autocmd_callback(const AutoCmd *ac, const AutoPatCmd *apc)
PUT(data, "buf", INTEGER_OBJ(autocmd_bufnr));
if (apc->data) {
- PUT(data, "data", copy_object(*apc->data));
+ PUT(data, "data", copy_object(*apc->data, NULL));
}
int group = apc->curpat->group;
@@ -2150,6 +2220,7 @@ char *getnextac(int c, void *cookie, int indent, bool do_concat)
// lua code, so that it works properly
autocmd_nested = ac->nested;
current_sctx = ac->script_ctx;
+ acp->script_ctx = current_sctx;
if (ac->exec.type == CALLABLE_CB) {
if (call_autocmd_callback(ac, acp)) {
@@ -2206,7 +2277,7 @@ bool has_autocmd(event_T event, char *sfname, buf_T *buf)
#ifdef BACKSLASH_IN_FILENAME
// Replace all backslashes with forward slashes. This makes the
// autocommand patterns portable between Unix and Windows.
- sfname = vim_strsave(sfname);
+ sfname = xstrdup(sfname);
forward_slash(sfname);
forward_slash(fname);
#endif
@@ -2388,7 +2459,7 @@ bool au_exists(const char *const arg) FUNC_ATTR_WARN_UNUSED_RESULT
}
// if pattern is "<buffer>", special handling is needed which uses curbuf
- // for pattern "<buffer=N>, FNAMECMP() will work fine
+ // for pattern "<buffer=N>, path_fnamecmp() will work fine
if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0) {
buflocal_buf = curbuf;
}
@@ -2396,12 +2467,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, path_fnamecmp() works fine.
if (ap->pat != NULL && ap->cmds != NULL
&& (group == AUGROUP_ALL || ap->group == group)
&& (pattern == NULL
|| (buflocal_buf == NULL
- ? FNAMECMP(ap->pat, pattern) == 0
+ ? path_fnamecmp(ap->pat, pattern) == 0
: ap->buflocal_nr == buflocal_buf->b_fnum))) {
retval = true;
break;
@@ -2418,7 +2489,7 @@ bool aupat_is_buflocal(char *pat, int patlen)
FUNC_ATTR_PURE
{
return patlen >= 8
- && STRNCMP(pat, "<buffer", 7) == 0
+ && strncmp(pat, "<buffer", 7) == 0
&& (pat)[patlen - 1] == '>';
}
@@ -2605,26 +2676,27 @@ 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);
+ if (p <= arg) {
+ return AUGROUP_ALL;
+ }
+
+ char *group_name = xstrnsave(arg, (size_t)(p - arg));
+ int 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 (strncmp(*cmd_ptr, pattern, (size_t)len) == 0 && ascii_iswhite((*cmd_ptr)[len])) {
if (*flag) {
semsg(_(e_duparg2), pattern);
return true;
@@ -2661,22 +2733,7 @@ void do_autocmd_uienter(uint64_t chanid, bool attached)
// 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)
+void do_autocmd_focusgained(bool gained)
{
static bool recursive = false;
static Timestamp last_time = (time_t)0;
@@ -2708,7 +2765,7 @@ static void do_autocmd_focusgained(bool gained)
redrawcmdline();
} else if ((State & MODE_NORMAL) || (State & MODE_INSERT)) {
if (must_redraw != 0) {
- update_screen(0);
+ update_screen();
}
setcursor();
diff --git a/src/nvim/autocmd.h b/src/nvim/autocmd.h
index d559d8c3d2..791b589167 100644
--- a/src/nvim/autocmd.h
+++ b/src/nvim/autocmd.h
@@ -1,8 +1,20 @@
#ifndef NVIM_AUTOCMD_H
#define NVIM_AUTOCMD_H
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "nvim/api/private/defs.h"
#include "nvim/buffer_defs.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds_defs.h"
+#include "nvim/macros.h"
+#include "nvim/regexp_defs.h"
+#include "nvim/types.h"
+
+struct AutoCmd_S;
+struct AutoPatCmd_S;
+struct AutoPat_S;
// event_T definition
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -13,7 +25,7 @@
// not the current buffer.
typedef struct {
buf_T *save_curbuf; ///< saved curbuf
- bool use_aucmd_win; ///< using aucmd_win
+ int use_aucmd_win_idx; ///< index in aucmd_win[] if >= 0
handle_T save_curwin_handle; ///< ID of saved curwin
handle_T new_curwin_handle; ///< ID of new curwin
handle_T save_prevwin_handle; ///< ID of saved prevwin
@@ -29,7 +41,7 @@ struct AutoCmd_S {
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
+ sctx_T script_ctx; // script context where it is defined
char *desc; // Description for the autocmd.
AutoCmd *next; // Next AutoCmd in list
};
@@ -59,6 +71,7 @@ struct AutoPatCmd_S {
char *sfname; // sfname to match with
char *tail; // tail of fname
event_T event; // current event
+ sctx_T script_ctx; // script context where it is defined
int arg_bufnr; // initially equal to <abuf>, set to zero when buf is deleted
Object *data; // arbitrary data
AutoPatCmd *next; // chain of active apc-s for auto-invalidation
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index ab5b32bf3e..5dcb10751f 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -20,10 +20,16 @@
//
#include <assert.h>
+#include <ctype.h>
#include <inttypes.h>
#include <stdbool.h>
+#include <stdlib.h>
#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+#include "auto/config.h"
+#include "klib/kvec.h"
#include "nvim/api/private/helpers.h"
#include "nvim/arglist.h"
#include "nvim/ascii.h"
@@ -44,6 +50,7 @@
#include "nvim/eval/vars.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_cmds2.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
#include "nvim/ex_getln.h"
@@ -53,21 +60,25 @@
#include "nvim/fold.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
#include "nvim/hashtab.h"
#include "nvim/help.h"
-#include "nvim/highlight_group.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
#include "nvim/main.h"
+#include "nvim/map.h"
#include "nvim/mapping.h"
#include "nvim/mark.h"
-#include "nvim/mark_defs.h"
#include "nvim/mbyte.h"
+#include "nvim/memline_defs.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/move.h"
+#include "nvim/normal.h"
#include "nvim/option.h"
#include "nvim/optionstr.h"
+#include "nvim/os/fs_defs.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/os/time.h"
@@ -76,11 +87,15 @@
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
+#include "nvim/screen.h"
+#include "nvim/search.h"
#include "nvim/sign.h"
#include "nvim/spell.h"
#include "nvim/statusline.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
+#include "nvim/terminal.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/usercmd.h"
@@ -93,7 +108,8 @@
#endif
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");
+static char e_attempt_to_delete_buffer_that_is_in_use_str[]
+ = N_("E937: Attempt to delete a buffer that is in use: %s");
// Number of times free_buffer() was called.
static int buf_free_count = 0;
@@ -160,16 +176,33 @@ static int read_buffer(int read_stdin, exarg_T *eap, int flags)
return retval;
}
+/// Ensure buffer "buf" is loaded. Does not trigger the swap-exists action.
+void buffer_ensure_loaded(buf_T *buf)
+{
+ if (buf->b_ml.ml_mfp != NULL) {
+ return;
+ }
+
+ aco_save_T aco;
+
+ // Make sure the buffer is in a window.
+ aucmd_prepbuf(&aco, buf);
+ swap_exists_action = SEA_NONE;
+ open_buffer(false, NULL, 0);
+ aucmd_restbuf(&aco);
+}
+
/// Open current buffer, that is: open the memfile and read the file into
/// memory.
///
/// @param read_stdin read file from stdin
/// @param eap for forced 'ff' and 'fenc' or NULL
-/// @param flags extra flags for readfile()
+/// @param flags_arg extra flags for readfile()
///
/// @return FAIL for failure, OK otherwise.
-int open_buffer(int read_stdin, exarg_T *eap, int flags)
+int open_buffer(int read_stdin, exarg_T *eap, int flags_arg)
{
+ int flags = flags_arg;
int retval = OK;
bufref_T old_curbuf;
long old_tw = curbuf->b_p_tw;
@@ -224,6 +257,13 @@ int open_buffer(int read_stdin, exarg_T *eap, int flags)
// mark cursor position as being invalid
curwin->w_valid = 0;
+ // A buffer without an actual file should not use the buffer name to read a
+ // file.
+ if (bt_nofileread(curbuf)) {
+ flags |= READ_NOFILE;
+ }
+
+ // Read the file if there is one.
if (curbuf->b_ffname != NULL) {
#ifdef UNIX
int save_bin = curbuf->b_p_bin;
@@ -320,8 +360,9 @@ int open_buffer(int read_stdin, exarg_T *eap, int flags)
}
apply_autocmds_retval(EVENT_BUFENTER, NULL, NULL, false, curbuf, &retval);
+ // if (retval != OK) {
if (retval == FAIL) {
- return FAIL;
+ return retval;
}
// The autocommands may have changed the current buffer. Apply the
@@ -329,7 +370,7 @@ int open_buffer(int read_stdin, exarg_T *eap, int flags)
if (bufref_valid(&old_curbuf) && old_curbuf.br_buf->b_ml.ml_mfp != NULL) {
aco_save_T aco;
- // Go to the buffer that was opened.
+ // Go to the buffer that was opened, make sure it is in a window.
aucmd_prepbuf(&aco, old_curbuf.br_buf);
do_modelines(0);
curbuf->b_flags &= ~(BF_CHECK_RO | BF_NEVERLOADED);
@@ -393,6 +434,29 @@ bool buf_valid(buf_T *buf)
return false;
}
+/// Return true when buffer "buf" can be unloaded.
+/// Give an error message and return false when the buffer is locked or the
+/// screen is being redrawn and the buffer is in a window.
+static bool can_unload_buffer(buf_T *buf)
+{
+ bool can_unload = !buf->b_locked;
+
+ if (can_unload && updating_screen) {
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_buffer == buf) {
+ can_unload = false;
+ break;
+ }
+ }
+ }
+ if (!can_unload) {
+ char *fname = buf->b_fname != NULL ? buf->b_fname : buf->b_ffname;
+ semsg(_(e_attempt_to_delete_buffer_that_is_in_use_str),
+ fname != NULL ? fname : "[No Name]");
+ }
+ return can_unload;
+}
+
/// Close the link to a buffer.
///
/// @param win If not NULL, set b_last_cursor.
@@ -450,8 +514,7 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last, bool i
// 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(_(e_buflocked));
+ if ((del_buf || wipe_buf) && !can_unload_buffer(buf)) {
return false;
}
@@ -658,6 +721,8 @@ void buf_clear_file(buf_T *buf)
{
buf->b_ml.ml_line_count = 1;
unchanged(buf, true, true);
+ buf->b_p_eof = false;
+ buf->b_start_eof = false;
buf->b_p_eol = true;
buf->b_start_eol = true;
buf->b_p_bomb = false;
@@ -804,6 +869,18 @@ static void free_buffer(buf_T *buf)
}
}
+/// Free the b_wininfo list for buffer "buf".
+static void clear_wininfo(buf_T *buf)
+{
+ wininfo_T *wip;
+
+ while (buf->b_wininfo != NULL) {
+ wip = buf->b_wininfo;
+ buf->b_wininfo = wip->wi_next;
+ free_wininfo(wip, buf);
+ }
+}
+
/// Free stuff in the buffer for ":bdel" and when wiping out the buffer.
///
/// @param buf Buffer pointer
@@ -838,18 +915,6 @@ static void free_buffer_stuff(buf_T *buf, int free_flags)
buf_updates_unload(buf, false);
}
-/// Free the b_wininfo list for buffer "buf".
-static void clear_wininfo(buf_T *buf)
-{
- wininfo_T *wip;
-
- while (buf->b_wininfo != NULL) {
- wip = buf->b_wininfo;
- buf->b_wininfo = wip->wi_next;
- free_wininfo(wip, buf);
- }
-}
-
/// Go to another buffer. Handles the result of the ATTENTION dialog.
void goto_buffer(exarg_T *eap, int start, int dir, int count)
{
@@ -1026,16 +1091,16 @@ char *do_bufdel(int command, char *arg, int addr_count, int start_bnr, int end_b
} else {
STRCPY(IObuff, _("E517: No buffers were wiped out"));
}
- errormsg = (char *)IObuff;
+ errormsg = IObuff;
} else if (deleted >= p_report) {
if (command == DOBUF_UNLOAD) {
- smsg(NGETTEXT("%d buffer unloaded", "%d buffers unloaded", (unsigned long)deleted),
+ smsg(NGETTEXT("%d buffer unloaded", "%d buffers unloaded", deleted),
deleted);
} else if (command == DOBUF_DEL) {
- smsg(NGETTEXT("%d buffer deleted", "%d buffers deleted", (unsigned long)deleted),
+ smsg(NGETTEXT("%d buffer deleted", "%d buffers deleted", deleted),
deleted);
} else {
- smsg(NGETTEXT("%d buffer wiped out", "%d buffers wiped out", (unsigned long)deleted),
+ smsg(NGETTEXT("%d buffer wiped out", "%d buffers wiped out", deleted),
deleted);
}
}
@@ -1193,13 +1258,17 @@ int do_buffer(int action, int start, int dir, int count, int forceit)
}
return FAIL;
}
+ if ((action == DOBUF_GOTO || action == DOBUF_SPLIT) && (buf->b_flags & BF_DUMMY)) {
+ // disallow navigating to the dummy buffer
+ semsg(_(e_nobufnr), count);
+ 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));
+ if (!can_unload_buffer(buf)) {
return FAIL;
}
set_bufref(&bufref, buf);
@@ -1265,7 +1334,7 @@ int do_buffer(int action, int start, int dir, int count, int forceit)
// 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)
- && (lastwin == aucmd_win || !last_window(curwin))) {
+ && (is_aucmd_win(lastwin) || !last_window(curwin))) {
if (win_close(curwin, false, false) == FAIL) {
break;
}
@@ -1623,7 +1692,7 @@ void enter_buffer(buf_T *buf)
}
curbuf->b_last_used = time(NULL);
- redraw_later(curwin, NOT_VALID);
+ redraw_later(curwin, UPD_NOT_VALID);
}
/// Change to the directory of the current buffer.
@@ -1805,7 +1874,7 @@ buf_T *buflist_new(char *ffname_arg, char *sfname_arg, linenr_T lnum, int flags)
pmap_put(handle_T)(&buffer_handles, buf->b_fnum, buf);
if (top_file_num < 0) { // wrap around (may cause duplicates)
emsg(_("W14: Warning: List of file names overflow"));
- if (emsg_silent == 0) {
+ if (emsg_silent == 0 && !in_assert_fails) {
ui_flush();
os_delay(3001L, true); // make sure it is noticed
}
@@ -1929,12 +1998,16 @@ void free_buf_options(buf_T *buf, int free_p_ff)
clear_string_option(&buf->b_p_ft);
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_lop);
clear_string_option(&buf->b_p_cinsd);
+ clear_string_option(&buf->b_p_cinw);
clear_string_option(&buf->b_p_cpt);
clear_string_option(&buf->b_p_cfu);
+ callback_free(&buf->b_cfu_cb);
clear_string_option(&buf->b_p_ofu);
+ callback_free(&buf->b_ofu_cb);
clear_string_option(&buf->b_p_tsrfu);
+ callback_free(&buf->b_tsrfu_cb);
clear_string_option(&buf->b_p_gp);
clear_string_option(&buf->b_p_mp);
clear_string_option(&buf->b_p_efm);
@@ -1943,6 +2016,7 @@ void free_buf_options(buf_T *buf, int free_p_ff)
clear_string_option(&buf->b_p_tags);
clear_string_option(&buf->b_p_tc);
clear_string_option(&buf->b_p_tfu);
+ callback_free(&buf->b_tfu_cb);
clear_string_option(&buf->b_p_dict);
clear_string_option(&buf->b_p_tsr);
clear_string_option(&buf->b_p_qe);
@@ -2162,7 +2236,7 @@ int buflist_findpat(const char *pattern, const char *pattern_end, bool unlisted,
if (pat == NULL) {
return -1;
}
- patend = pat + STRLEN(pat) - 1;
+ patend = pat + strlen(pat) - 1;
toggledollar = (patend > pat && *patend == '$');
// First try finding a listed buffer. If not found and "unlisted"
@@ -2180,13 +2254,14 @@ int buflist_findpat(const char *pattern, const char *pattern_end, bool unlisted,
}
regmatch_T regmatch;
- regmatch.regprog = vim_regcomp(p, p_magic ? RE_MAGIC : 0);
- if (regmatch.regprog == NULL) {
- xfree(pat);
- return -1;
- }
+ regmatch.regprog = vim_regcomp(p, magic_isset() ? RE_MAGIC : 0);
FOR_ALL_BUFFERS_BACKWARDS(buf) {
+ if (regmatch.regprog == NULL) {
+ // invalid pattern, possibly after switching engine
+ xfree(pat);
+ return -1;
+ }
if (buf->b_p_bl == find_listed
&& (!diffmode || diff_mode_buf(buf))
&& buflist_match(&regmatch, buf, false) != NULL) {
@@ -2264,7 +2339,6 @@ int ExpandBufnames(char *pat, int *num_file, char ***file, int options)
int round;
char *p;
int attempt;
- char *patc;
bufmatch_T *matches = NULL;
*num_file = 0; // return values in case of FAIL
@@ -2274,31 +2348,34 @@ int ExpandBufnames(char *pat, int *num_file, char ***file, int options)
return FAIL;
}
- // Make a copy of "pat" and change "^" to "\(^\|[\/]\)".
- if (*pat == '^') {
- patc = xmalloc(STRLEN(pat) + 11);
- STRCPY(patc, "\\(^\\|[\\/]\\)");
- STRCPY(patc + 11, pat + 1);
- } else {
- patc = pat;
+ const bool fuzzy = cmdline_fuzzy_complete(pat);
+
+ char *patc = NULL;
+ // Make a copy of "pat" and change "^" to "\(^\|[\/]\)" (if doing regular
+ // expression matching)
+ if (!fuzzy) {
+ if (*pat == '^') {
+ patc = xmalloc(strlen(pat) + 11);
+ STRCPY(patc, "\\(^\\|[\\/]\\)");
+ STRCPY(patc + 11, pat + 1);
+ } else {
+ patc = pat;
+ }
}
+ fuzmatch_str_T *fuzmatch = NULL;
// attempt == 0: try match with '\<', match at start of word
// attempt == 1: try match without '\<', match anywhere
- for (attempt = 0; attempt <= 1; attempt++) {
- if (attempt > 0 && patc == pat) {
- break; // there was no anchor, no need to try again
- }
-
+ for (attempt = 0; attempt <= (fuzzy ? 0 : 1); attempt++) {
regmatch_T regmatch;
- regmatch.regprog = vim_regcomp(patc + attempt * 11, RE_MAGIC);
- if (regmatch.regprog == NULL) {
- if (patc != pat) {
- xfree(patc);
+ if (!fuzzy) {
+ if (attempt > 0 && patc == pat) {
+ break; // there was no anchor, no need to try again
}
- return FAIL;
+ regmatch.regprog = vim_regcomp(patc + attempt * 11, RE_MAGIC);
}
+ int score = 0;
// round == 1: Count the matches.
// round == 2: Build the array to keep the matches.
for (round = 1; round <= 2; round++) {
@@ -2314,64 +2391,108 @@ int ExpandBufnames(char *pat, int *num_file, char ***file, int options)
continue;
}
}
- p = buflist_match(&regmatch, buf, p_wic);
- if (p != NULL) {
- if (round == 1) {
- count++;
- } else {
- if (options & WILD_HOME_REPLACE) {
- p = home_replace_save(buf, p);
- } else {
- p = xstrdup(p);
+
+ if (!fuzzy) {
+ if (regmatch.regprog == NULL) {
+ // invalid pattern, possibly after recompiling
+ if (patc != pat) {
+ xfree(patc);
}
- if (matches != NULL) {
- matches[count].buf = buf;
- matches[count].match = p;
- count++;
- } else {
- (*file)[count++] = p;
+ return FAIL;
+ }
+ p = buflist_match(&regmatch, buf, p_wic);
+ } else {
+ p = NULL;
+ // first try matching with the short file name
+ if ((score = fuzzy_match_str(buf->b_sfname, pat)) != 0) {
+ p = buf->b_sfname;
+ }
+ if (p == NULL) {
+ // next try matching with the full path file name
+ if ((score = fuzzy_match_str(buf->b_ffname, pat)) != 0) {
+ p = buf->b_ffname;
}
}
}
+
+ if (p == NULL) {
+ continue;
+ }
+
+ if (round == 1) {
+ count++;
+ continue;
+ }
+
+ if (options & WILD_HOME_REPLACE) {
+ p = home_replace_save(buf, p);
+ } else {
+ p = xstrdup(p);
+ }
+
+ if (!fuzzy) {
+ if (matches != NULL) {
+ matches[count].buf = buf;
+ matches[count].match = p;
+ count++;
+ } else {
+ (*file)[count++] = p;
+ }
+ } else {
+ fuzmatch[count].idx = count;
+ fuzmatch[count].str = p;
+ fuzmatch[count].score = score;
+ count++;
+ }
}
if (count == 0) { // no match found, break here
break;
}
if (round == 1) {
- *file = xmalloc((size_t)count * sizeof(**file));
-
- if (options & WILD_BUFLASTUSED) {
- matches = xmalloc((size_t)count * sizeof(*matches));
+ if (!fuzzy) {
+ *file = xmalloc((size_t)count * sizeof(**file));
+ if (options & WILD_BUFLASTUSED) {
+ matches = xmalloc((size_t)count * sizeof(*matches));
+ }
+ } else {
+ fuzmatch = xmalloc((size_t)count * sizeof(fuzmatch_str_T));
}
}
}
- vim_regfree(regmatch.regprog);
- if (count) { // match(es) found, break here
- break;
+
+ if (!fuzzy) {
+ vim_regfree(regmatch.regprog);
+ if (count) { // match(es) found, break here
+ break;
+ }
}
}
- if (patc != pat) {
+ if (!fuzzy && patc != pat) {
xfree(patc);
}
- if (matches != NULL) {
- if (count > 1) {
- qsort(matches, (size_t)count, sizeof(bufmatch_T), buf_time_compare);
- }
-
- // 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;
+ if (!fuzzy) {
+ if (matches != NULL) {
+ if (count > 1) {
+ qsort(matches, (size_t)count, sizeof(bufmatch_T), buf_time_compare);
}
- (*file)[count - 1] = matches[0].match;
- } else {
- for (int i = 0; i < count; i++) {
- (*file)[i] = matches[i].match;
+
+ // 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)[count - 1] = matches[0].match;
+ } else {
+ for (int i = 0; i < count; i++) {
+ (*file)[i] = matches[i].match;
+ }
}
+ xfree(matches);
}
- xfree(matches);
+ } else {
+ fuzzymatches_to_strmatches(fuzmatch, file, count, false);
}
*num_file = count;
@@ -2379,6 +2500,7 @@ int ExpandBufnames(char *pat, int *num_file, char ***file, int options)
}
/// Check for a match on the file name for buffer "buf" with regprog "prog".
+/// Note that rmp->regprog may become NULL when switching regexp engine.
///
/// @param ignore_case When true, ignore case. Use 'fic' otherwise.
static char *buflist_match(regmatch_T *rmp, buf_T *buf, bool ignore_case)
@@ -2391,7 +2513,8 @@ static char *buflist_match(regmatch_T *rmp, buf_T *buf, bool ignore_case)
return match;
}
-/// Try matching the regexp in "prog" with file name "name".
+/// Try matching the regexp in "rmp->regprog" with file name "name".
+/// Note that rmp->regprog may become NULL when switching regexp engine.
///
/// @param ignore_case When true, ignore case. Use 'fileignorecase' otherwise.
///
@@ -2401,19 +2524,22 @@ static char *fname_match(regmatch_T *rmp, char *name, bool ignore_case)
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)) {
+ // extra check for valid arguments
+ if (name == NULL || rmp->regprog == NULL) {
+ return 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 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)) {
match = name;
- } 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)) {
- match = name;
- }
- xfree(p);
}
+ xfree(p);
}
return match;
@@ -2520,17 +2646,18 @@ void buflist_setfpos(buf_T *const buf, win_T *const win, linenr_T lnum, colnr_T
static bool wininfo_other_tab_diff(wininfo_T *wip)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
- if (wip->wi_opt.wo_diff) {
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- // return false when it's a window in the current tab page, thus
- // the buffer was in diff mode here
- if (wip->wi_win == wp) {
- return false;
- }
+ if (!wip->wi_opt.wo_diff) {
+ return false;
+ }
+
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ // return false when it's a window in the current tab page, thus
+ // the buffer was in diff mode here
+ if (wip->wi_win == wp) {
+ return false;
}
- return true;
}
- return false;
+ return true;
}
/// Find info for the current window in buffer "buf".
@@ -2553,25 +2680,27 @@ static wininfo_T *find_wininfo(buf_T *buf, bool need_options, bool skip_diff_buf
}
}
+ if (wip != NULL) {
+ return wip;
+ }
+
// If no wininfo for curwin, use the first in the list (that doesn't have
// 'diff' set and is in another tab page).
// If "need_options" is true skip entries that don't have options set,
// unless the window is editing "buf", so we can copy from the window
// itself.
- if (wip == NULL) {
- if (skip_diff_buffer) {
- for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) {
- if (!wininfo_other_tab_diff(wip)
- && (!need_options
- || wip->wi_optset
- || (wip->wi_win != NULL
- && wip->wi_win->w_buffer == buf))) {
- break;
- }
+ if (skip_diff_buffer) {
+ for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) {
+ if (!wininfo_other_tab_diff(wip)
+ && (!need_options
+ || wip->wi_optset
+ || (wip->wi_win != NULL
+ && wip->wi_win->w_buffer == buf))) {
+ break;
}
- } else {
- wip = buf->b_wininfo;
}
+ } else {
+ wip = buf->b_wininfo;
}
return wip;
}
@@ -2688,9 +2817,9 @@ void buflist_list(exarg_T *eap)
continue;
}
if (buf_spname(buf) != NULL) {
- STRLCPY(NameBuff, buf_spname(buf), MAXPATHL);
+ xstrlcpy(NameBuff, buf_spname(buf), MAXPATHL);
} else {
- home_replace(buf, buf->b_fname, (char *)NameBuff, MAXPATHL, true);
+ home_replace(buf, buf->b_fname, NameBuff, MAXPATHL, true);
}
if (message_filtered(NameBuff)) {
@@ -2706,7 +2835,7 @@ void buflist_list(exarg_T *eap)
}
msg_putchar('\n');
- len = vim_snprintf((char *)IObuff, IOSIZE - 20, "%3d%c%c%c%c%c \"%s\"",
+ len = vim_snprintf(IObuff, IOSIZE - 20, "%3d%c%c%c%c%c \"%s\"",
buf->b_fnum,
buf->b_p_bl ? ' ' : 'u',
buf == curbuf ? '%' : (curwin->w_alt_fnum == buf->b_fnum ? '#' : ' '),
@@ -2720,18 +2849,18 @@ void buflist_list(exarg_T *eap)
}
// put "line 999" in column 40 or after the file name
- i = 40 - vim_strsize((char *)IObuff);
+ i = 40 - vim_strsize(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),
+ vim_snprintf(IObuff + len, (size_t)(IOSIZE - len), _("line %" PRId64),
buf == curbuf ? (int64_t)curwin->w_cursor.lnum : (int64_t)buflist_findlnum(buf));
}
- msg_outtrans((char *)IObuff);
+ msg_outtrans(IObuff);
line_breakcheck();
}
@@ -2833,18 +2962,20 @@ int setfname(buf_T *buf, char *ffname_arg, char *sfname_arg, bool message)
void buf_set_name(int fnum, char *name)
{
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 = xstrdup(name);
- buf->b_sfname = NULL;
- // Allocate ffname and expand into full path. Also resolves .lnk
- // files on Win32.
- fname_expand(buf, &buf->b_ffname, &buf->b_sfname);
- buf->b_fname = buf->b_sfname;
+ if (buf == NULL) {
+ return;
+ }
+
+ if (buf->b_sfname != buf->b_ffname) {
+ xfree(buf->b_sfname);
}
+ xfree(buf->b_ffname);
+ buf->b_ffname = xstrdup(name);
+ buf->b_sfname = NULL;
+ // Allocate ffname and expand into full path. Also resolves .lnk
+ // files on Win32.
+ fname_expand(buf, &buf->b_ffname, &buf->b_sfname);
+ buf->b_fname = buf->b_sfname;
}
/// Take care of what needs to be done when the name of buffer "buf" has changed.
@@ -2957,7 +3088,7 @@ static bool otherfile_buf(buf_T *buf, char *ffname, FileID *file_id_p, bool file
if (ffname == NULL || *ffname == NUL || buf->b_ffname == NULL) {
return true;
}
- if (FNAMECMP(ffname, buf->b_ffname) == 0) {
+ if (path_fnamecmp(ffname, buf->b_ffname) == 0) {
return false;
}
{
@@ -3029,14 +3160,14 @@ void fileinfo(int fullname, int shorthelp, int dont_truncate)
if (fullname > 1) { // 2 CTRL-G: include buffer number
vim_snprintf(buffer, IOSIZE, "buf %d: ", curbuf->b_fnum);
- p = buffer + STRLEN(buffer);
+ p = buffer + strlen(buffer);
} else {
p = buffer;
}
*p++ = '"';
if (buf_spname(curbuf) != NULL) {
- STRLCPY(p, buf_spname(curbuf), IOSIZE - (p - buffer));
+ xstrlcpy(p, buf_spname(curbuf), (size_t)(IOSIZE - (p - buffer)));
} else {
if (!fullname && curbuf->b_fname != NULL) {
name = curbuf->b_fname;
@@ -3079,7 +3210,7 @@ void fileinfo(int fullname, int shorthelp, int dont_truncate)
vim_snprintf_add(buffer, IOSIZE,
NGETTEXT("%" PRId64 " line --%d%%--",
"%" PRId64 " lines --%d%%--",
- (unsigned long)curbuf->b_ml.ml_line_count),
+ curbuf->b_ml.ml_line_count),
(int64_t)curbuf->b_ml.ml_line_count, n);
} else {
vim_snprintf_add(buffer, IOSIZE,
@@ -3088,7 +3219,7 @@ void fileinfo(int fullname, int shorthelp, int dont_truncate)
(int64_t)curbuf->b_ml.ml_line_count,
n);
validate_virtcol();
- len = STRLEN(buffer);
+ len = strlen(buffer);
col_print(buffer + len, IOSIZE - len,
(int)curwin->w_cursor.col + 1, (int)curwin->w_virtcol + 1);
}
@@ -3161,19 +3292,11 @@ void maketitle(void)
if (*p_titlestring != NUL) {
if (stl_syntax & STL_IN_TITLE) {
- int use_sandbox = false;
- const int called_emsg_before = called_emsg;
-
- use_sandbox = was_set_insecurely(curwin, "titlestring", 0);
- build_stl_str_hl(curwin, buf, sizeof(buf),
- (char *)p_titlestring, use_sandbox,
- 0, maxlen, NULL, NULL);
+ build_stl_str_hl(curwin, buf, sizeof(buf), p_titlestring,
+ "titlestring", 0, 0, maxlen, NULL, NULL, NULL);
title_str = buf;
- if (called_emsg > called_emsg_before) {
- set_string_option_direct("titlestring", -1, "", OPT_FREE, SID_ERROR);
- }
} else {
- title_str = (char *)p_titlestring;
+ title_str = p_titlestring;
}
} else {
// Format: "fname + (path) (1 of 2) - VIM".
@@ -3220,7 +3343,7 @@ void maketitle(void)
(SPACE_FOR_DIR - (size_t)(buf_p - buf)), true);
#ifdef BACKSLASH_IN_FILENAME
// Avoid "c:/name" to be reduced to "c".
- if (isalpha((uint8_t)buf_p) && *(buf_p + 1) == ':') {
+ if (isalpha((uint8_t)(*buf_p)) && *(buf_p + 1) == ':') {
buf_p += 2;
}
#endif
@@ -3275,18 +3398,10 @@ void maketitle(void)
icon_str = buf;
if (*p_iconstring != NUL) {
if (stl_syntax & STL_IN_ICON) {
- int use_sandbox = false;
- const int called_emsg_before = called_emsg;
-
- use_sandbox = was_set_insecurely(curwin, "iconstring", 0);
- build_stl_str_hl(curwin, icon_str, sizeof(buf),
- (char *)p_iconstring, use_sandbox,
- 0, 0, NULL, NULL);
- if (called_emsg > called_emsg_before) {
- set_string_option_direct("iconstring", -1, "", OPT_FREE, SID_ERROR);
- }
+ build_stl_str_hl(curwin, icon_str, sizeof(buf), p_iconstring,
+ "iconstring", 0, 0, 0, NULL, NULL, NULL);
} else {
- icon_str = (char *)p_iconstring;
+ icon_str = p_iconstring;
}
} else {
char *buf_p;
@@ -3297,7 +3412,7 @@ void maketitle(void)
}
*icon_str = NUL;
// Truncate name at 100 bytes.
- len = (int)STRLEN(buf_p);
+ len = (int)strlen(buf_p);
if (len > 100) {
len -= 100;
len += utf_cp_tail_off(buf_p, buf_p + len) + 1;
@@ -3327,7 +3442,7 @@ static bool value_change(char *str, char **last)
FUNC_ATTR_WARN_UNUSED_RESULT
{
if ((str == NULL) != (*last == NULL)
- || (str != NULL && *last != NULL && STRCMP(str, *last) != 0)) {
+ || (str != NULL && *last != NULL && strcmp(str, *last) != 0)) {
xfree(*last);
if (str == NULL) {
*last = NULL;
@@ -3378,9 +3493,9 @@ void get_rel_pos(win_T *wp, char *buf, int buflen)
}
below = wp->w_buffer->b_ml.ml_line_count - wp->w_botline + 1;
if (below <= 0) {
- STRLCPY(buf, (above == 0 ? _("All") : _("Bot")), buflen);
+ xstrlcpy(buf, (above == 0 ? _("All") : _("Bot")), (size_t)buflen);
} else if (above <= 0) {
- STRLCPY(buf, _("Top"), buflen);
+ xstrlcpy(buf, _("Top"), (size_t)buflen);
} else {
vim_snprintf(buf, (size_t)buflen, "%2d%%", above > 1000000L
? (int)(above / ((above + below) / 100L))
@@ -3404,7 +3519,7 @@ bool append_arg_number(win_T *wp, char *buf, int buflen, bool add_file)
return false;
}
- char *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) {
@@ -3440,7 +3555,7 @@ void fname_expand(buf_T *buf, char **ffname, char **sfname)
}
*ffname = fix_fname((*ffname)); // expand to full path
-#ifdef WIN32
+#ifdef MSWIN
if (!buf->b_p_bin) {
// If the file name is a shortcut file, use the file it links to.
char *rfname = os_resolve_shortcut((const char *)(*ffname));
@@ -3693,10 +3808,10 @@ static int chk_modeline(linenr_T lnum, int flags)
int retval = OK;
prev = -1;
- for (s = (char *)ml_get(lnum); *s != NUL; s++) {
+ for (s = 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) {
+ if ((prev != -1 && strncmp(s, "ex:", (size_t)3) == 0)
+ || strncmp(s, "vi:", (size_t)3) == 0) {
break;
}
// Accept both "vim" and "Vim".
@@ -3712,9 +3827,9 @@ static int chk_modeline(linenr_T lnum, int flags)
if (*e == ':'
&& (s[0] != 'V'
- || STRNCMP(skipwhite((char *)e + 1), "set", 3) == 0)
+ || strncmp(skipwhite(e + 1), "set", 3) == 0)
&& (s[3] == ':'
- || (VIM_VERSION_100 >= vers && isdigit(s[3]))
+ || (VIM_VERSION_100 >= vers && isdigit((uint8_t)s[3]))
|| (VIM_VERSION_100 < vers && s[3] == '<')
|| (VIM_VERSION_100 > vers && s[3] == '>')
|| (VIM_VERSION_100 == vers && s[3] == '='))) {
@@ -3761,8 +3876,8 @@ static int chk_modeline(linenr_T lnum, int flags)
// "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 (strncmp(s, "set ", (size_t)4) == 0
+ || strncmp(s, "se ", (size_t)3) == 0) {
if (*e != ':') { // no terminating ':'?
break;
}
@@ -3826,7 +3941,8 @@ bool bt_terminal(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.
+/// buffer. This means the buffer name may not be a file name,
+/// at least not for writing the buffer.
bool bt_nofilename(const buf_T *const buf)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
@@ -3836,6 +3952,17 @@ bool bt_nofilename(const buf_T *const buf)
|| buf->b_p_bt[0] == 'p');
}
+/// @return true if "buf" is a "nofile", "quickfix", "terminal" or "prompt"
+/// buffer. This means the buffer is not to be read from a file.
+static bool bt_nofileread(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')
+ || buf->b_p_bt[0] == 't'
+ || buf->b_p_bt[0] == 'q'
+ || buf->b_p_bt[0] == 'p');
+}
+
/// @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
@@ -3909,30 +4036,6 @@ char *buf_spname(buf_T *buf)
return NULL;
}
-/// Find a window for buffer "buf".
-/// If found true is returned and "wp" and "tp" are set to
-/// the window and tabpage.
-/// If not found, false is returned.
-///
-/// @param buf buffer to find a window for
-/// @param[out] wp stores the found window
-/// @param[out] tp stores the found tabpage
-///
-/// @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;
- *tp = NULL;
- FOR_ALL_TAB_WINDOWS(tp2, wp2) {
- if (wp2->w_buffer == buf) {
- *tp = tp2;
- *wp = wp2;
- return true;
- }
- }
- return false;
-}
-
static int buf_signcols_inner(buf_T *buf, int maximum)
{
sign_entry_T *sign; // a sign in the sign list
@@ -4034,7 +4137,7 @@ void buf_signcols_add_check(buf_T *buf, sign_entry_T *added)
buf->b_signcols.max++;
}
buf->b_signcols.size++;
- redraw_buf_later(buf, NOT_VALID);
+ redraw_buf_later(buf, UPD_NOT_VALID);
return;
}
@@ -4055,7 +4158,7 @@ void buf_signcols_add_check(buf_T *buf, sign_entry_T *added)
buf->b_signcols.size = linesum;
buf->b_signcols.max = linesum;
buf->b_signcols.sentinel = added->se_lnum;
- redraw_buf_later(buf, NOT_VALID);
+ redraw_buf_later(buf, UPD_NOT_VALID);
}
}
@@ -4074,7 +4177,7 @@ int buf_signcols(buf_T *buf, int maximum)
if (signcols != buf->b_signcols.size) {
buf->b_signcols.size = signcols;
buf->b_signcols.max = maximum;
- redraw_buf_later(buf, NOT_VALID);
+ redraw_buf_later(buf, UPD_NOT_VALID);
}
buf->b_signcols.valid = true;
@@ -4096,13 +4199,15 @@ char *buf_get_fname(const buf_T *buf)
/// Set 'buflisted' for curbuf to "on" and trigger autocommands if it changed.
void set_buflisted(int on)
{
- if (on != curbuf->b_p_bl) {
- curbuf->b_p_bl = on;
- if (on) {
- apply_autocmds(EVENT_BUFADD, NULL, NULL, false, curbuf);
- } else {
- apply_autocmds(EVENT_BUFDELETE, NULL, NULL, false, curbuf);
- }
+ if (on == curbuf->b_p_bl) {
+ return;
+ }
+
+ curbuf->b_p_bl = on;
+ if (on) {
+ apply_autocmds(EVENT_BUFADD, NULL, NULL, false, curbuf);
+ } else {
+ apply_autocmds(EVENT_BUFDELETE, NULL, NULL, false, curbuf);
}
}
@@ -4127,7 +4232,7 @@ bool buf_contents_changed(buf_T *buf)
exarg_T ea;
prep_exarg(&ea, buf);
- // set curwin/curbuf to buf and save a few things
+ // Set curwin/curbuf to buf and save a few things.
aco_save_T aco;
aucmd_prepbuf(&aco, newbuf);
@@ -4139,7 +4244,7 @@ bool buf_contents_changed(buf_T *buf)
if (buf->b_ml.ml_line_count == curbuf->b_ml.ml_line_count) {
differ = false;
for (linenr_T lnum = 1; lnum <= curbuf->b_ml.ml_line_count; lnum++) {
- if (STRCMP(ml_get_buf(buf, lnum, false), ml_get(lnum)) != 0) {
+ if (strcmp(ml_get_buf(buf, lnum, false), ml_get(lnum)) != 0) {
differ = true;
break;
}
@@ -4183,14 +4288,19 @@ void wipe_buffer(buf_T *buf, bool aucmd)
/// @param bufnr Buffer to switch to, or 0 to create a new buffer.
///
/// @see curbufIsChanged()
-void buf_open_scratch(handle_T bufnr, char *bufname)
+///
+/// @return FAIL for failure, OK otherwise
+int buf_open_scratch(handle_T bufnr, char *bufname)
{
- (void)do_ecmd((int)bufnr, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, NULL);
+ if (do_ecmd((int)bufnr, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, NULL) == FAIL) {
+ return FAIL;
+ }
apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, false, curbuf);
(void)setfname(curbuf, bufname, NULL, true);
apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, false, curbuf);
- set_option_value("bh", 0L, "hide", OPT_LOCAL);
- set_option_value("bt", 0L, "nofile", OPT_LOCAL);
- set_option_value("swf", 0L, NULL, OPT_LOCAL);
+ set_option_value_give_err("bh", 0L, "hide", OPT_LOCAL);
+ set_option_value_give_err("bt", 0L, "nofile", OPT_LOCAL);
+ set_option_value_give_err("swf", 0L, NULL, OPT_LOCAL);
RESET_BINDING(curwin);
+ return OK;
}
diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h
index d56a70dc7e..610d9e37ec 100644
--- a/src/nvim/buffer.h
+++ b/src/nvim/buffer.h
@@ -1,13 +1,20 @@
#ifndef NVIM_BUFFER_H
#define NVIM_BUFFER_H
+#include <assert.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#include "nvim/buffer_defs.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/func_attr.h"
#include "nvim/grid_defs.h" // for StlClickRecord
#include "nvim/macros.h"
#include "nvim/memline.h"
-#include "nvim/pos.h" // for linenr_T
+#include "nvim/memline_defs.h"
+#include "nvim/pos.h"
// Values for buflist_getfile()
enum getf_values {
@@ -69,8 +76,7 @@ EXTERN char *msg_qflist INIT(= N_("[Quickfix List]"));
# include "buffer.h.generated.h"
#endif
-static inline void buf_set_changedtick(buf_T *const buf,
- const varnumber_T changedtick)
+static inline void buf_set_changedtick(buf_T *buf, varnumber_T changedtick)
REAL_FATTR_NONNULL_ALL REAL_FATTR_ALWAYS_INLINE;
/// Set b:changedtick, also checking b: for consistency in debug build
@@ -102,7 +108,7 @@ static inline void buf_set_changedtick(buf_T *const buf, const varnumber_T chang
}
}
-static inline varnumber_T buf_get_changedtick(const buf_T *const buf)
+static inline varnumber_T buf_get_changedtick(const buf_T *buf)
REAL_FATTR_NONNULL_ALL REAL_FATTR_ALWAYS_INLINE REAL_FATTR_PURE
REAL_FATTR_WARN_UNUSED_RESULT;
@@ -116,7 +122,7 @@ static inline varnumber_T buf_get_changedtick(const buf_T *const buf)
return buf->changedtick_di.di_tv.vval.v_number;
}
-static inline void buf_inc_changedtick(buf_T *const buf)
+static inline void buf_inc_changedtick(buf_T *buf)
REAL_FATTR_NONNULL_ALL REAL_FATTR_ALWAYS_INLINE;
/// Increment b:changedtick value
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 12ebd13439..fa76377c4e 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -3,11 +3,8 @@
#include <stdbool.h>
#include <stdint.h>
-// 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.
@@ -18,55 +15,44 @@ typedef struct {
int br_buf_free_count;
} bufref_T;
-// for garray_T
+#include "klib/kvec.h"
+#include "nvim/api/private/defs.h"
+#include "nvim/eval/typval.h"
#include "nvim/garray.h"
-// for ScreenGrid
#include "nvim/grid_defs.h"
-// for HLF_COUNT
-#include "nvim/highlight_defs.h"
-// for pos_T, lpos_T and linenr_T
-#include "nvim/pos.h"
-// for the number window-local and buffer-local options
-#include "nvim/option_defs.h"
-// for jump list and tag stack sizes in a buffer and mark types
-#include "nvim/mark_defs.h"
-// for u_header_T
-#include "nvim/undo_defs.h"
-// for hashtab_T
#include "nvim/hashtab.h"
-// for dict_T
-#include "nvim/eval/typval.h"
-// for String
-#include "nvim/api/private/defs.h"
-// for Map(K, V)
+#include "nvim/highlight_defs.h"
#include "nvim/map.h"
-// for kvec
-#include "nvim/lib/kvec.h"
-// for marktree
+#include "nvim/mark_defs.h"
#include "nvim/marktree.h"
+// for float window title
+#include "nvim/extmark_defs.h"
+// for click definitions
+#include "nvim/option_defs.h"
+#include "nvim/pos.h"
+#include "nvim/statusline_defs.h"
+#include "nvim/undo_defs.h"
#define GETFILE_SUCCESS(x) ((x) <= 0)
#define MODIFIABLE(buf) (buf->b_p_ma)
-/*
- * Flags for w_valid.
- * These are set when something in a window structure becomes invalid, except
- * when the cursor is moved. Call check_cursor_moved() before testing one of
- * the flags.
- * These are reset when that thing has been updated and is valid again.
- *
- * Every function that invalidates one of these must call one of the
- * invalidate_* functions.
- *
- * w_valid is supposed to be used only in screen.c. From other files, use the
- * functions that set or reset the flags.
- *
- * VALID_BOTLINE VALID_BOTLINE_AP
- * on on w_botline valid
- * off on w_botline approximated
- * off off w_botline not valid
- * on off not possible
- */
+// Flags for w_valid.
+// These are set when something in a window structure becomes invalid, except
+// when the cursor is moved. Call check_cursor_moved() before testing one of
+// the flags.
+// These are reset when that thing has been updated and is valid again.
+//
+// Every function that invalidates one of these must call one of the
+// invalidate_* functions.
+//
+// w_valid is supposed to be used only in screen.c. From other files, use the
+// functions that set or reset the flags.
+//
+// VALID_BOTLINE VALID_BOTLINE_AP
+// on on w_botline valid
+// off on w_botline approximated
+// off off w_botline not valid
+// on off not possible
#define VALID_WROW 0x01 // w_wrow (window row) is valid
#define VALID_WCOL 0x02 // w_wcol (window col) is valid
#define VALID_VIRTCOL 0x04 // w_virtcol (file col) is valid
@@ -100,43 +86,32 @@ typedef struct wininfo_S wininfo_T;
typedef struct frame_S frame_T;
typedef uint64_t disptick_T; // display tick type
-// for struct memline (it needs memfile_T)
#include "nvim/memline_defs.h"
-
-// for regprog_T. Needs win_T and buf_T.
+#include "nvim/os/fs_defs.h"
#include "nvim/regexp_defs.h"
-// for synstate_T (needs reg_extmatch_T, win_T, buf_T)
-#include "nvim/syntax_defs.h"
-// for sign_entry_T
-#include "nvim/os/fs_defs.h" // for FileID
#include "nvim/sign_defs.h"
-#include "nvim/terminal.h" // for Terminal
+#include "nvim/syntax_defs.h"
+#include "nvim/terminal.h"
-/*
- * The taggy struct is used to store the information about a :tag command.
- */
+// The taggy struct is used to store the information about a :tag command.
typedef struct taggy {
- char_u *tagname; // tag name
+ char *tagname; // tag name
fmark_T fmark; // cursor position BEFORE ":tag"
int cur_match; // match number
int cur_fnum; // buffer number used for cur_match
- char_u *user_data; // used with tagfunc
+ char *user_data; // used with tagfunc
} taggy_T;
typedef struct buffblock buffblock_T;
typedef struct buffheader buffheader_T;
-/*
- * structure used to store one block of the stuff/redo/recording buffers
- */
+// structure used to store one block of the stuff/redo/recording buffers
struct buffblock {
buffblock_T *b_next; // pointer to next buffblock
- char_u b_str[1]; // contents (actually longer)
+ char b_str[1]; // contents (actually longer)
};
-/*
- * header used for the stuff buffer and the redo buffer
- */
+// header used for the stuff buffer and the redo buffer
struct buffheader {
buffblock_T bh_first; // first (dummy) block of list
buffblock_T *bh_curr; // buffblock for appending
@@ -149,49 +124,47 @@ typedef struct {
buffheader_T sr_old_redobuff;
} save_redo_T;
-/*
- * Structure that contains all options that are local to a window.
- * Used twice in a window: for the current buffer and for all buffers.
- * Also used in wininfo_T.
- */
+// Structure that contains all options that are local to a window.
+// Used twice in a window: for the current buffer and for all buffers.
+// Also used in wininfo_T.
typedef struct {
int wo_arab;
#define w_p_arab w_onebuf_opt.wo_arab // 'arabic'
int wo_bri;
#define w_p_bri w_onebuf_opt.wo_bri // 'breakindent'
- char_u *wo_briopt;
+ char *wo_briopt;
#define w_p_briopt w_onebuf_opt.wo_briopt // 'breakindentopt'
int wo_diff;
#define w_p_diff w_onebuf_opt.wo_diff // 'diff'
- char_u *wo_fdc;
+ char *wo_fdc;
#define w_p_fdc w_onebuf_opt.wo_fdc // 'foldcolumn'
- char_u *wo_fdc_save;
+ char *wo_fdc_save;
#define w_p_fdc_save w_onebuf_opt.wo_fdc_save // 'fdc' saved for diff mode
int wo_fen;
#define w_p_fen w_onebuf_opt.wo_fen // 'foldenable'
int wo_fen_save;
// 'foldenable' saved for diff mode
#define w_p_fen_save w_onebuf_opt.wo_fen_save
- char_u *wo_fdi;
+ char *wo_fdi;
#define w_p_fdi w_onebuf_opt.wo_fdi // 'foldignore'
long wo_fdl;
#define w_p_fdl w_onebuf_opt.wo_fdl // 'foldlevel'
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;
+ char *wo_fdm;
#define w_p_fdm w_onebuf_opt.wo_fdm // 'foldmethod'
- char_u *wo_fdm_save;
+ char *wo_fdm_save;
#define w_p_fdm_save w_onebuf_opt.wo_fdm_save // 'fdm' saved for diff mode
long wo_fml;
#define w_p_fml w_onebuf_opt.wo_fml // 'foldminlines'
long wo_fdn;
#define w_p_fdn w_onebuf_opt.wo_fdn // 'foldnestmax'
- char_u *wo_fde;
+ char *wo_fde;
#define w_p_fde w_onebuf_opt.wo_fde // 'foldexpr'
- char_u *wo_fdt;
+ char *wo_fdt;
#define w_p_fdt w_onebuf_opt.wo_fdt // 'foldtext'
- char_u *wo_fmr;
+ char *wo_fmr;
#define w_p_fmr w_onebuf_opt.wo_fmr // 'foldmarker'
int wo_lbr;
#define w_p_lbr w_onebuf_opt.wo_lbr // 'linebreak'
@@ -201,7 +174,7 @@ 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;
+ char *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'
@@ -215,7 +188,7 @@ typedef struct {
#define w_p_pvw w_onebuf_opt.wo_pvw // 'previewwindow'
int wo_rl;
#define w_p_rl w_onebuf_opt.wo_rl // 'rightleft'
- char_u *wo_rlc;
+ char *wo_rlc;
#define w_p_rlc w_onebuf_opt.wo_rlc // 'rightleftcmd'
long wo_scr;
#define w_p_scr w_onebuf_opt.wo_scr // 'scroll'
@@ -225,13 +198,15 @@ typedef struct {
#define w_p_cuc w_onebuf_opt.wo_cuc // 'cursorcolumn'
int wo_cul;
#define w_p_cul w_onebuf_opt.wo_cul // 'cursorline'
- char_u *wo_culopt;
+ char *wo_culopt;
#define w_p_culopt w_onebuf_opt.wo_culopt // 'cursorlineopt'
- char_u *wo_cc;
+ char *wo_cc;
#define w_p_cc w_onebuf_opt.wo_cc // 'colorcolumn'
- char_u *wo_sbr;
+ char *wo_sbr;
#define w_p_sbr w_onebuf_opt.wo_sbr // 'showbreak'
- char_u *wo_stl;
+ char *wo_stc;
+#define w_p_stc w_onebuf_opt.wo_stc // 'statuscolumn'
+ char *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'
@@ -245,7 +220,7 @@ typedef struct {
#define w_p_wrap w_onebuf_opt.wo_wrap // 'wrap'
int wo_wrap_save; // 'wrap' state saved for diff mode
#define w_p_wrap_save w_onebuf_opt.wo_wrap_save
- char_u *wo_cocu; // 'concealcursor'
+ char *wo_cocu; // 'concealcursor'
#define w_p_cocu w_onebuf_opt.wo_cocu
long wo_cole; // 'conceallevel'
#define w_p_cole w_onebuf_opt.wo_cole
@@ -253,14 +228,14 @@ typedef struct {
#define w_p_crb w_onebuf_opt.wo_crb // 'cursorbind'
int wo_crb_save; // 'cursorbind' state saved for diff mode
#define w_p_crb_save w_onebuf_opt.wo_crb_save
- char_u *wo_scl;
+ char *wo_scl;
#define w_p_scl w_onebuf_opt.wo_scl // 'signcolumn'
- char_u *wo_winhl;
+ char *wo_winhl;
#define w_p_winhl w_onebuf_opt.wo_winhl // 'winhighlight'
- char_u *wo_fcs;
-#define w_p_fcs w_onebuf_opt.wo_fcs // 'fillchars'
- char_u *wo_lcs;
+ char *wo_lcs;
#define w_p_lcs w_onebuf_opt.wo_lcs // 'listchars'
+ char *wo_fcs;
+#define w_p_fcs w_onebuf_opt.wo_fcs // 'fillchars'
long wo_winbl;
#define w_p_winbl w_onebuf_opt.wo_winbl // 'winblend'
@@ -268,16 +243,14 @@ typedef struct {
#define w_p_script_ctx w_onebuf_opt.wo_script_ctx
} winopt_T;
-/*
- * Window info stored with a buffer.
- *
- * Two types of info are kept for a buffer which are associated with a
- * specific window:
- * 1. Each window can have a different line number associated with a buffer.
- * 2. The window-local options for a buffer work in a similar way.
- * The window-info is kept in a list at b_wininfo. It is kept in
- * most-recently-used order.
- */
+// Window info stored with a buffer.
+//
+// Two types of info are kept for a buffer which are associated with a
+// specific window:
+// 1. Each window can have a different line number associated with a buffer.
+// 2. The window-local options for a buffer work in a similar way.
+// The window-info is kept in a list at b_wininfo. It is kept in
+// most-recently-used order.
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
@@ -290,12 +263,10 @@ struct wininfo_S {
int wi_changelistidx; // copy of w_changelistidx
};
-/*
- * Argument list: Array of file names.
- * Used for the global argument list and the argument lists local to a window.
- *
- * TODO: move struct arglist to another header
- */
+// Argument list: Array of file names.
+// Used for the global argument list and the argument lists local to a window.
+//
+// TODO(neovim): move struct arglist to another header
typedef struct arglist {
garray_T al_ga; // growarray with the array of file names
int al_refcount; // number of windows using this arglist
@@ -308,8 +279,8 @@ typedef struct arglist {
//
// TODO(Felipe): move aentry_T to another header
typedef struct argentry {
- char_u *ae_fname; // file name as specified
- int ae_fnum; // buffer number with expanded file name
+ char *ae_fname; // file name as specified
+ int ae_fnum; // buffer number with expanded file name
} aentry_T;
#define ALIST(win) (win)->w_alist
@@ -321,12 +292,10 @@ typedef struct argentry {
#define ARGCOUNT (ALIST(curwin)->al_ga.ga_len)
#define WARGCOUNT(wp) (ALIST(wp)->al_ga.ga_len)
-/*
- * Used for the typeahead buffer: typebuf.
- */
+// Used for the typeahead buffer: typebuf.
typedef struct {
- char_u *tb_buf; // buffer for typed characters
- char_u *tb_noremap; // mapping flags for characters in tb_buf[]
+ uint8_t *tb_buf; // buffer for typed characters
+ uint8_t *tb_noremap; // mapping flags for characters in tb_buf[]
int tb_buflen; // size of tb_buf[]
int tb_off; // current position in tb_buf[]
int tb_len; // number of valid bytes in tb_buf[]
@@ -347,16 +316,14 @@ typedef struct {
String save_inputbuf;
} tasave_T;
-/*
- * Structure used for mappings and abbreviations.
- */
+// Structure used for mappings and abbreviations.
typedef struct mapblock mapblock_T;
struct mapblock {
- mapblock_T *m_next; // next mapblock in list
- char_u *m_keys; // mapped from, lhs
- char_u *m_str; // mapped to, rhs
- char_u *m_orig_str; // rhs as entered by the user
- LuaRef m_luaref; // lua function reference as rhs
+ mapblock_T *m_next; // next mapblock in list
+ char *m_keys; // mapped from, lhs
+ char *m_str; // mapped to, rhs
+ char *m_orig_str; // rhs as entered by the user
+ 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
@@ -366,8 +333,8 @@ struct mapblock {
char m_nowait; // <nowait> used
char m_expr; // <expr> used, m_str is an expression
sctx_T m_script_ctx; // SCTX where map was defined
- char *m_desc; // description of keymap
- bool m_replace_keycodes; // replace termcodes in lua function
+ char *m_desc; // description of mapping
+ bool m_replace_keycodes; // replace keycodes in result of expression
};
/// Used for highlighting in the status line.
@@ -414,9 +381,7 @@ struct stl_item {
typedef struct qf_info_S qf_info_T;
-/*
- * Used for :syntime: timing of executing a syntax pattern.
- */
+// Used for :syntime: timing of executing a syntax pattern.
typedef struct {
proftime_T total; // total time used
proftime_T slowest; // time of slowest call
@@ -424,10 +389,8 @@ typedef struct {
long match; // nr of times matched
} syn_time_T;
-/*
- * These are items normally related to a buffer. But when using ":ownsyntax"
- * a window may have its own instance.
- */
+// These are items normally related to a buffer. But when using ":ownsyntax"
+// a window may have its own instance.
typedef struct {
hashtab_T b_keywtab; // syntax keywords hash table
hashtab_T b_keywtab_ic; // idem, ignore case
@@ -440,14 +403,14 @@ typedef struct {
garray_T b_syn_clusters; // table for syntax clusters
int b_spell_cluster_id; // @Spell cluster ID or 0
int b_nospell_cluster_id; // @NoSpell cluster ID or 0
- int b_syn_containedin; // TRUE when there is an item with a
+ int b_syn_containedin; // true when there is an item with a
// "containedin" argument
int b_syn_sync_flags; // flags about how to sync
int16_t b_syn_sync_id; // group to sync on
linenr_T b_syn_sync_minlines; // minimal sync lines offset
linenr_T b_syn_sync_maxlines; // maximal sync lines offset
linenr_T b_syn_sync_linebreaks; // offset for multi-line pattern
- char_u *b_syn_linecont_pat; // line continuation pattern
+ char *b_syn_linecont_pat; // line continuation pattern
regprog_T *b_syn_linecont_prog; // line continuation program
syn_time_T b_syn_linecont_time;
int b_syn_linecont_ic; // ignore-case flag for above
@@ -476,17 +439,20 @@ typedef struct {
disptick_T b_sst_lasttick; // last display tick
// for spell checking
- garray_T b_langp; // list of pointers to slang_T, see spell.c
- bool b_spell_ismw[256]; // flags: is midword char
- char_u *b_spell_ismw_mb; // multi-byte midword chars
- char_u *b_p_spc; // 'spellcapcheck'
+ garray_T b_langp; // list of pointers to slang_T, see spell.c
+ bool b_spell_ismw[256]; // flags: is midword char
+ char *b_spell_ismw_mb; // multi-byte midword chars
+ char *b_p_spc; // 'spellcapcheck'
regprog_T *b_cap_prog; // program for 'spellcapcheck'
- char_u *b_p_spf; // 'spellfile'
- char_u *b_p_spl; // 'spelllang'
- char_u *b_p_spo; // 'spelloptions'
- int b_cjk; // all CJK letters as OK
- char_u b_syn_chartab[32]; // syntax iskeyword option
- char_u *b_syn_isk; // iskeyword option
+ char *b_p_spf; // 'spellfile'
+ char *b_p_spl; // 'spelllang'
+ char *b_p_spo; // 'spelloptions'
+#define SPO_CAMEL 0x1
+#define SPO_NPBUFFER 0x2
+ unsigned b_p_spo_flags; // 'spelloptions' flags
+ int b_cjk; // all CJK letters as OK
+ uint8_t b_syn_chartab[32]; // syntax iskeyword option
+ char *b_syn_isk; // iskeyword option
} synblock_T;
/// Type used for changedtick_di member in buf_T
@@ -514,13 +480,11 @@ EXTERN int curbuf_splice_pending INIT(= 0);
// Maximum number of maphash blocks we will have
#define MAX_MAPHASH 256
-/*
- * buffer: structure that holds information about one file
- *
- * Several windows can share a single Buffer
- * A buffer is unallocated if there is no memfile for it.
- * A buffer is new if the associated file has never been loaded yet.
- */
+// buffer: structure that holds information about one file
+//
+// Several windows can share a single Buffer
+// A buffer is unallocated if there is no memfile for it.
+// A buffer is new if the associated file has never been loaded yet.
struct file_buffer {
handle_T handle; // unique id for the buffer (buffer number)
@@ -570,15 +534,13 @@ struct file_buffer {
varnumber_T b_last_changedtick_pum; // b:changedtick when TextChangedP was
// last triggered.
- bool b_saving; /* Set to true if we are in the middle of
- saving the buffer. */
+ bool b_saving; // Set to true if we are in the middle of
+ // saving the buffer.
- /*
- * Changes to a buffer require updating of the display. To minimize the
- * work, remember changes made and update everything at once.
- */
- bool b_mod_set; /* true when there are changes since the last
- time the display was updated */
+ // Changes to a buffer require updating of the display. To minimize the
+ // work, remember changes made and update everything at once.
+ bool b_mod_set; // true when there are changes since the last
+ // time the display was updated
linenr_T b_mod_top; // topmost lnum that was changed
linenr_T b_mod_bot; // lnum below last changed line, AFTER the
// change
@@ -600,7 +562,7 @@ struct file_buffer {
fmark_T b_namedm[NMARKS]; // current named marks (mark.c)
- // These variables are set when VIsual_active becomes FALSE
+ // These variables are set when VIsual_active becomes false
visualinfo_T b_visual;
int b_visual_mode_eval; // b_visual.vi_mode for visualmode()
@@ -609,17 +571,13 @@ struct file_buffer {
fmark_T b_last_insert; // where Insert mode was left
fmark_T b_last_change; // position of last change: '. mark
- /*
- * the changelist contains old change positions
- */
+ // the changelist contains old change positions
fmark_T b_changelist[JUMPLISTSIZE];
int b_changelistlen; // number of active entries
bool b_new_change; // set by u_savecommon()
- /*
- * Character table, only used in charset.c for 'iskeyword'
- * bitset with 4*64=256 bits: 1 bit per character 0-255.
- */
+ // Character table, only used in charset.c for 'iskeyword'
+ // bitset with 4*64=256 bits: 1 bit per character 0-255.
uint64_t b_chartab[4];
// Table used for mappings local to a buffer.
@@ -629,18 +587,14 @@ struct file_buffer {
mapblock_T *b_first_abbr;
// User commands local to the buffer.
garray_T b_ucmds;
- /*
- * start and end of an operator, also used for '[ and ']
- */
+ // start and end of an operator, also used for '[ and ']
pos_T b_op_start;
pos_T b_op_start_orig; // used for Insstart_orig
pos_T b_op_end;
bool b_marks_read; // Have we read ShaDa marks yet?
- /*
- * The following only used in undo.c.
- */
+ // The following only used in undo.c.
u_header_T *b_u_oldhead; // pointer to oldest header
u_header_T *b_u_newhead; // pointer to newest header; may not be valid
// if b_u_curhead is not NULL
@@ -649,14 +603,12 @@ struct file_buffer {
bool b_u_synced; // entry lists are synced
long b_u_seq_last; // last used undo sequence number
long b_u_save_nr_last; // counter for last file write
- long b_u_seq_cur; // hu_seq of header below which we are now
+ long b_u_seq_cur; // uh_seq of header below which we are now
time_t b_u_time_cur; // uh_time of header below which we are now
long b_u_save_nr_cur; // file write nr after which we are now
- /*
- * variables for "U" command in undo.c
- */
- char_u *b_u_line_ptr; // saved line for "U" command
+ // variables for "U" command in undo.c
+ char *b_u_line_ptr; // saved line for "U" command
linenr_T b_u_line_lnum; // line number of line in u_line
colnr_T b_u_line_colnr; // optional column number
@@ -675,84 +627,87 @@ struct file_buffer {
#define KEYMAP_LOADED 2 // 'keymap' mappings have been loaded
garray_T b_kmap_ga; // the keymap table
- /*
- * Options local to a buffer.
- * They are here because their value depends on the type of file
- * or contents of the file being edited.
- */
+ // Options local to a buffer.
+ // They are here because their value depends on the type of file
+ // or contents of the file being edited.
bool b_p_initialized; // set when options initialized
LastSet b_p_script_ctx[BV_COUNT]; // SCTXs for buffer-local options
int b_p_ai; ///< 'autoindent'
int b_p_ai_nopaste; ///< b_p_ai saved for paste mode
- char_u *b_p_bkc; ///< 'backupco
+ char *b_p_bkc; ///< 'backupco
unsigned int b_bkc_flags; ///< flags for 'backupco
int b_p_ci; ///< 'copyindent'
int b_p_bin; ///< 'binary'
int b_p_bomb; ///< 'bomb'
- char_u *b_p_bh; ///< 'bufhidden'
- char_u *b_p_bt; ///< 'buftype'
+ char *b_p_bh; ///< 'bufhidden'
+ char *b_p_bt; ///< 'buftype'
int b_has_qf_entry; ///< quickfix exists for buffer
int b_p_bl; ///< 'buflisted'
long b_p_channel; ///< 'channel'
int b_p_cin; ///< 'cindent'
- 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'
+ char *b_p_cino; ///< 'cinoptions'
+ char *b_p_cink; ///< 'cinkeys'
+ char *b_p_cinw; ///< 'cinwords'
+ char *b_p_cinsd; ///< 'cinscopedecls'
+ char *b_p_com; ///< 'comments'
+ char *b_p_cms; ///< 'commentstring'
+ char *b_p_cpt; ///< 'complete'
#ifdef BACKSLASH_IN_FILENAME
- char_u *b_p_csl; ///< 'completeslash'
+ char *b_p_csl; ///< 'completeslash'
#endif
- char_u *b_p_cfu; ///< 'completefunc'
- char_u *b_p_ofu; ///< 'omnifunc'
- char_u *b_p_tfu; ///< 'tagfunc'
- char_u *b_p_umf; ///< 'usermarkfunc'
+ char *b_p_umf; ///< 'usermarkfunc'
+ char *b_p_cfu; ///< 'completefunc'
+ Callback b_cfu_cb; ///< 'completefunc' callback
+ char *b_p_ofu; ///< 'omnifunc'
+ Callback b_ofu_cb; ///< 'omnifunc' callback
+ char *b_p_tfu; ///< 'tagfunc'
+ Callback b_tfu_cb; ///< 'tagfunc' callback
+ int b_p_eof; ///< 'endoffile'
int b_p_eol; ///< 'endofline'
int b_p_fixeol; ///< 'fixendofline'
int b_p_et; ///< 'expandtab'
int b_p_et_nobin; ///< b_p_et saved for binary mode
int b_p_et_nopaste; ///< b_p_et saved for paste mode
- char_u *b_p_fenc; ///< 'fileencoding'
- char_u *b_p_ff; ///< 'fileformat'
- char_u *b_p_ft; ///< 'filetype'
- char_u *b_p_fo; ///< 'formatoptions'
- char_u *b_p_flp; ///< 'formatlistpat'
+ char *b_p_fenc; ///< 'fileencoding'
+ char *b_p_ff; ///< 'fileformat'
+ char *b_p_ft; ///< 'filetype'
+ char *b_p_fo; ///< 'formatoptions'
+ char *b_p_flp; ///< 'formatlistpat'
int b_p_inf; ///< 'infercase'
- char_u *b_p_isk; ///< 'iskeyword'
- char_u *b_p_def; ///< 'define' local value
- char_u *b_p_inc; ///< 'include'
- char_u *b_p_inex; ///< 'includeexpr'
+ char *b_p_isk; ///< 'iskeyword'
+ char *b_p_def; ///< 'define' local value
+ char *b_p_inc; ///< 'include'
+ char *b_p_inex; ///< 'includeexpr'
uint32_t b_p_inex_flags; ///< flags for 'includeexpr'
- char_u *b_p_inde; ///< 'indentexpr'
+ char *b_p_inde; ///< 'indentexpr'
uint32_t b_p_inde_flags; ///< flags for 'indentexpr'
- char_u *b_p_indk; ///< 'indentkeys'
- char_u *b_p_fp; ///< 'formatprg'
- char_u *b_p_fex; ///< 'formatexpr'
+ char *b_p_indk; ///< 'indentkeys'
+ char *b_p_fp; ///< 'formatprg'
+ char *b_p_fex; ///< 'formatexpr'
uint32_t b_p_fex_flags; ///< flags for 'formatexpr'
- char_u *b_p_kp; ///< 'keywordprg'
+ char *b_p_kp; ///< 'keywordprg'
int b_p_lisp; ///< 'lisp'
- char_u *b_p_menc; ///< 'makeencoding'
- char_u *b_p_mps; ///< 'matchpairs'
+ char *b_p_lop; ///< 'lispoptions'
+ char *b_p_menc; ///< 'makeencoding'
+ char *b_p_mps; ///< 'matchpairs'
int b_p_ml; ///< 'modeline'
int b_p_ml_nobin; ///< b_p_ml saved for binary mode
int b_p_ma; ///< 'modifiable'
- char_u *b_p_nf; ///< 'nrformats'
+ char *b_p_nf; ///< 'nrformats'
int b_p_pi; ///< 'preserveindent'
- char_u *b_p_qe; ///< 'quoteescape'
+ char *b_p_qe; ///< 'quoteescape'
int b_p_ro; ///< 'readonly'
long b_p_sw; ///< 'shiftwidth'
long b_p_scbk; ///< 'scrollback'
int b_p_si; ///< 'smartindent'
long b_p_sts; ///< 'softtabstop'
long b_p_sts_nopaste; ///< b_p_sts saved for paste mode
- char_u *b_p_sua; ///< 'suffixesadd'
+ char *b_p_sua; ///< 'suffixesadd'
int b_p_swf; ///< 'swapfile'
long b_p_smc; ///< 'synmaxcol'
- char_u *b_p_syn; ///< 'syntax'
+ char *b_p_syn; ///< 'syntax'
long b_p_ts; ///< 'tabstop'
long b_p_tw; ///< 'textwidth'
long b_p_tw_nobin; ///< b_p_tw saved for binary mode
@@ -760,29 +715,30 @@ struct file_buffer {
long b_p_wm; ///< 'wrapmargin'
long b_p_wm_nobin; ///< b_p_wm saved for binary mode
long b_p_wm_nopaste; ///< b_p_wm saved for paste mode
- char_u *b_p_vsts; ///< 'varsofttabstop'
- long *b_p_vsts_array; ///< 'varsofttabstop' in internal format
- char_u *b_p_vsts_nopaste; ///< b_p_vsts saved for paste mode
- char_u *b_p_vts; ///< 'vartabstop'
- long *b_p_vts_array; ///< 'vartabstop' in internal format
- char_u *b_p_keymap; ///< 'keymap'
+ char *b_p_vsts; ///< 'varsofttabstop'
+ long *b_p_vsts_array; ///< 'varsofttabstop' in internal format
+ char *b_p_vsts_nopaste; ///< b_p_vsts saved for paste mode
+ char *b_p_vts; ///< 'vartabstop'
+ long *b_p_vts_array; ///< 'vartabstop' in internal format
+ char *b_p_keymap; ///< 'keymap'
// local values for options which are normally global
- char_u *b_p_gp; ///< 'grepprg' local value
- char_u *b_p_mp; ///< 'makeprg' local value
- char_u *b_p_efm; ///< 'errorformat' local value
- char_u *b_p_ep; ///< 'equalprg' local value
- char_u *b_p_path; ///< 'path' local value
+ char *b_p_gp; ///< 'grepprg' local value
+ char *b_p_mp; ///< 'makeprg' local value
+ char *b_p_efm; ///< 'errorformat' local value
+ char *b_p_ep; ///< 'equalprg' local value
+ char *b_p_path; ///< 'path' local value
int b_p_ar; ///< 'autoread' local value
- char_u *b_p_tags; ///< 'tags' local value
- char_u *b_p_tc; ///< 'tagcase' local value
+ char *b_p_tags; ///< 'tags' local value
+ char *b_p_tc; ///< 'tagcase' local value
unsigned b_tc_flags; ///< flags for 'tagcase'
- char_u *b_p_dict; ///< 'dictionary' local value
- char_u *b_p_tsr; ///< 'thesaurus' local value
- char_u *b_p_tsrfu; ///< 'thesaurusfunc' local value
+ char *b_p_dict; ///< 'dictionary' local value
+ char *b_p_tsr; ///< 'thesaurus' local value
+ char *b_p_tsrfu; ///< 'thesaurusfunc' local value
+ Callback b_tsrfu_cb; ///< 'thesaurusfunc' callback
long b_p_ul; ///< 'undolevels' local value
int b_p_udf; ///< 'undofile'
- char_u *b_p_lw; ///< 'lispwords' local value
+ char *b_p_lw; ///< 'lispwords' local value
// end of buffer options
@@ -825,9 +781,10 @@ struct file_buffer {
int b_ind_cpp_extern_c;
int b_ind_pragma;
- linenr_T b_no_eol_lnum; /* non-zero lnum when last line of next binary
- * write should not have an end-of-line */
+ linenr_T b_no_eol_lnum; // non-zero lnum when last line of next binary
+ // write should not have an end-of-line
+ int b_start_eof; // last line had eof (CTRL-Z) when it was read
int b_start_eol; // last line had eol when it was read
int b_start_ffc; // first char of 'ff' when edit started
char *b_start_fenc; // 'fileencoding' when edit started or NULL
@@ -837,20 +794,18 @@ struct file_buffer {
ScopeDictDictItem b_bufvar; ///< Variable for "b:" Dictionary.
dict_T *b_vars; ///< b: scope dictionary.
- /* When a buffer is created, it starts without a swap file. b_may_swap is
- * then set to indicate that a swap file may be opened later. It is reset
- * if a swap file could not be opened.
- */
+ // When a buffer is created, it starts without a swap file. b_may_swap is
+ // then set to indicate that a swap file may be opened later. It is reset
+ // if a swap file could not be opened.
bool b_may_swap;
- bool b_did_warn; /* Set to true if user has been warned on first
- change of a read-only file */
-
- /* Two special kinds of buffers:
- * help buffer - used for help files, won't use a swap file.
- * spell buffer - used for spell info, never displayed and doesn't have a
- * file name.
- */
- bool b_help; // TRUE for help file buffer (when set b_p_bt
+ bool b_did_warn; // Set to true if user has been warned on first
+ // change of a read-only file
+
+ // Two special kinds of buffers:
+ // help buffer - used for help files, won't use a swap file.
+ // spell buffer - used for spell info, never displayed and doesn't have a
+ // file name.
+ bool b_help; // true for help file buffer (when set b_p_bt
// is "help")
bool b_spell; // True for a spell file buffer, most fields
// are not used! Use the B_SPELL macro to
@@ -909,30 +864,28 @@ struct file_buffer {
int b_diff_failed; // internal diff failed for this buffer
};
-/*
- * Stuff for diff mode.
- */
+// Stuff for diff mode.
#define DB_COUNT 8 // up to four buffers can be diff'ed
-/*
- * Each diffblock defines where a block of lines starts in each of the buffers
- * and how many lines it occupies in that buffer. When the lines are missing
- * in the buffer the df_count[] is zero. This is all counted in
- * buffer lines.
- * There is always at least one unchanged line in between the diffs.
- * Otherwise it would have been included in the diff above or below it.
- * df_lnum[] + df_count[] is the lnum below the change. When in one buffer
- * lines have been inserted, in the other buffer df_lnum[] is the line below
- * the insertion and df_count[] is zero. When appending lines at the end of
- * the buffer, df_lnum[] is one beyond the end!
- * This is using a linked list, because the number of differences is expected
- * to be reasonable small. The list is sorted on lnum.
- */
+// Each diffblock defines where a block of lines starts in each of the buffers
+// and how many lines it occupies in that buffer. When the lines are missing
+// in the buffer the df_count[] is zero. This is all counted in
+// buffer lines.
+// There is always at least one unchanged line in between the diffs.
+// Otherwise it would have been included in the diff above or below it.
+// df_lnum[] + df_count[] is the lnum below the change. When in one buffer
+// lines have been inserted, in the other buffer df_lnum[] is the line below
+// the insertion and df_count[] is zero. When appending lines at the end of
+// the buffer, df_lnum[] is one beyond the end!
+// This is using a linked list, because the number of differences is expected
+// to be reasonable small. The list is sorted on lnum.
typedef struct diffblock_S diff_T;
struct diffblock_S {
diff_T *df_next;
linenr_T df_lnum[DB_COUNT]; // line number in buffer
linenr_T df_count[DB_COUNT]; // nr of inserted/changed lines
+ bool is_linematched; // has the linematch algorithm ran on this diff hunk to divide it into
+ // smaller diff hunks?
};
#define SNAP_HELP_IDX 0
@@ -953,7 +906,8 @@ struct tabpage_S {
win_T *tp_firstwin; ///< first window in this Tab page
win_T *tp_lastwin; ///< last window in this Tab page
long tp_old_Rows_avail; ///< ROWS_AVAIL when Tab page was left
- long tp_old_Columns; ///< Columns when Tab page was left
+ long tp_old_Columns; ///< Columns when Tab page was left, -1 when
+ ///< calling win_new_screen_cols() postponed
long tp_ch_used; ///< value of 'cmdheight' when frame size was set
diff_T *tp_first_diff;
@@ -967,30 +921,26 @@ struct tabpage_S {
char *tp_prevdir; ///< Previous directory.
};
-/*
- * Structure to cache info for displayed lines in w_lines[].
- * Each logical line has one entry.
- * The entry tells how the logical line is currently displayed in the window.
- * This is updated when displaying the window.
- * When the display is changed (e.g., when clearing the screen) w_lines_valid
- * is changed to exclude invalid entries.
- * When making changes to the buffer, wl_valid is reset to indicate wl_size
- * may not reflect what is actually in the buffer. When wl_valid is FALSE,
- * the entries can only be used to count the number of displayed lines used.
- * wl_lnum and wl_lastlnum are invalid too.
- */
+// Structure to cache info for displayed lines in w_lines[].
+// Each logical line has one entry.
+// The entry tells how the logical line is currently displayed in the window.
+// This is updated when displaying the window.
+// When the display is changed (e.g., when clearing the screen) w_lines_valid
+// is changed to exclude invalid entries.
+// When making changes to the buffer, wl_valid is reset to indicate wl_size
+// may not reflect what is actually in the buffer. When wl_valid is false,
+// the entries can only be used to count the number of displayed lines used.
+// wl_lnum and wl_lastlnum are invalid too.
typedef struct w_line {
linenr_T wl_lnum; // buffer line number for logical line
uint16_t wl_size; // height in screen lines
- char wl_valid; // TRUE values are valid for text in buffer
- char wl_folded; // TRUE when this is a range of folded lines
+ char wl_valid; // true values are valid for text in buffer
+ char wl_folded; // true when this is a range of folded lines
linenr_T wl_lastlnum; // last buffer line number for logical line
} wline_T;
-/*
- * Windows are kept in a tree of frames. Each frame has a column (FR_COL)
- * or row (FR_ROW) layout or is a leaf, which has a window.
- */
+// Windows are kept in a tree of frames. Each frame has a column (FR_COL)
+// or row (FR_ROW) layout or is a leaf, which has a window.
struct frame_S {
char fr_layout; // FR_LEAF, FR_COL or FR_ROW
int fr_width;
@@ -1004,19 +954,18 @@ struct frame_S {
// for first
// fr_child and fr_win are mutually exclusive
frame_T *fr_child; // first contained frame
- win_T *fr_win; // window that fills this frame
+ win_T *fr_win; // window that fills this frame; for a snapshot
+ // set to the current window
};
#define FR_LEAF 0 // frame is a leaf
#define FR_ROW 1 // frame with a row of windows
#define FR_COL 2 // frame with a column of windows
-/*
- * Struct used for highlighting 'hlsearch' matches, matches defined by
- * ":match" and matches defined by match functions.
- * For 'hlsearch' there is one pattern for all windows. For ":match" and the
- * match functions there is a different pattern for each window.
- */
+// Struct used for highlighting 'hlsearch' matches, matches defined by
+// ":match" and matches defined by match functions.
+// For 'hlsearch' there is one pattern for all windows. For ":match" and the
+// match functions there is a different pattern for each window.
typedef struct {
regmmatch_T rm; // points to the regexp program; contains last found
// match (may continue in next line)
@@ -1032,9 +981,6 @@ typedef struct {
proftime_T tm; // for a time limit
} match_T;
-/// number of positions supported by matchaddpos()
-#define MAXPOSMATCH 8
-
/// Same as lpos_T, but with additional field len.
typedef struct {
linenr_T lnum; ///< line number
@@ -1042,31 +988,28 @@ typedef struct {
int len; ///< length: 0 - to the end of line
} llpos_T;
-/// posmatch_T provides an array for storing match items for matchaddpos()
-/// function.
-typedef struct posmatch posmatch_T;
-struct posmatch {
- llpos_T pos[MAXPOSMATCH]; ///< array of positions
- int cur; ///< internal position counter
- linenr_T toplnum; ///< top buffer line
- linenr_T botlnum; ///< bottom buffer line
-};
-
-/*
- * matchitem_T provides a linked list for storing match items for ":match" and
- * the match functions.
- */
+/// matchitem_T provides a linked list for storing match items for ":match",
+/// matchadd() and matchaddpos().
typedef struct matchitem matchitem_T;
struct matchitem {
- matchitem_T *next;
- int id; ///< match ID
- int priority; ///< match priority
- 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
- int hlg_id; ///< highlight group ID
- int conceal_char; ///< cchar for Conceal highlighting
+ matchitem_T *mit_next;
+ int mit_id; ///< match ID
+ int mit_priority; ///< match priority
+
+ // Either a pattern is defined (mit_pattern is not NUL) or a list of
+ // positions is given (mit_pos is not NULL and mit_pos_count > 0).
+ char *mit_pattern; ///< pattern to highlight
+ regmmatch_T mit_match; ///< regexp program for pattern
+
+ llpos_T *mit_pos_array; ///< array of positions
+ int mit_pos_count; ///< nr of entries in mit_pos
+ int mit_pos_cur; ///< internal position counter
+ linenr_T mit_toplnum; ///< top buffer line
+ linenr_T mit_botlnum; ///< bottom buffer line
+
+ match_T mit_hl; ///< struct for doing the actual highlighting
+ int mit_hlg_id; ///< highlight group ID
+ int mit_conceal_char; ///< cchar for Conceal highlighting
};
typedef int FloatAnchor;
@@ -1086,16 +1029,23 @@ typedef enum {
kFloatRelativeEditor = 0,
kFloatRelativeWindow = 1,
kFloatRelativeCursor = 2,
+ kFloatRelativeMouse = 3,
} FloatRelative;
EXTERN const char *const float_relative_str[] INIT(= { "editor", "win",
- "cursor" });
+ "cursor", "mouse" });
typedef enum {
kWinStyleUnused = 0,
kWinStyleMinimal, /// Minimal UI: no number column, eob markers, etc
} WinStyle;
+typedef enum {
+ kAlignLeft = 0,
+ kAlignCenter = 1,
+ kAlignRight = 2,
+} AlignTextPos;
+
typedef struct {
Window window;
lpos_T bufpos;
@@ -1108,10 +1058,14 @@ typedef struct {
int zindex;
WinStyle style;
bool border;
+ bool title;
bool shadow;
schar_T border_chars[8];
int border_hl_ids[8];
int border_attr[8];
+ AlignTextPos title_pos;
+ VirtText title_chunks;
+ int title_width;
bool noautocmd;
} FloatConfig;
@@ -1222,16 +1176,15 @@ struct window_S {
int diff;
int msgsep;
int eob;
+ int lastline;
} w_p_fcs_chars;
- /*
- * "w_topline", "w_leftcol" and "w_skipcol" specify the offsets for
- * displaying the buffer.
- */
- linenr_T w_topline; /* buffer line number of the line at the
- top of the window */
- char w_topline_was_set; /* flag set to TRUE when topline is set,
- e.g. by winrestview() */
+ // "w_topline", "w_leftcol" and "w_skipcol" specify the offsets for
+ // displaying the buffer.
+ linenr_T w_topline; // buffer line number of the line at the
+ // top of the window
+ char w_topline_was_set; // flag set to true when topline is set,
+ // e.g. by winrestview()
int w_topfill; // number of filler lines above w_topline
int w_old_topfill; // w_topfill at last redraw
bool w_botfill; // true when filler lines are actually
@@ -1243,9 +1196,11 @@ struct window_S {
colnr_T w_skipcol; // starting column when a single line
// doesn't fit in the window
- // four fields that are only used when there is a WinScrolled autocommand
+ // six fields that are only used when there is a WinScrolled autocommand
linenr_T w_last_topline; ///< last known value for w_topline
+ int w_last_topfill; ///< last known value for w_topfill
colnr_T w_last_leftcol; ///< last known value for w_leftcol
+ colnr_T w_last_skipcol; ///< last known value for w_skipcol
int w_last_width; ///< last known value for w_width
int w_last_height; ///< last known value for w_height
@@ -1256,6 +1211,8 @@ struct window_S {
int w_winrow; // first row of window in screen
int w_height; // number of rows in window, excluding
// status/command line(s)
+ int w_prev_winrow; // previous winrow used for 'splitkeep'
+ int w_prev_height; // previous height used for 'splitkeep'
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.
@@ -1281,26 +1238,20 @@ struct window_S {
int w_height_outer;
int w_width_outer;
- /*
- * === start of cached values ====
- */
- /*
- * Recomputing is minimized by storing the result of computations.
- * Use functions in screen.c to check if they are valid and to update.
- * w_valid is a bitfield of flags, which indicate if specific values are
- * valid or need to be recomputed.
- */
+ // === start of cached values ====
+
+ // Recomputing is minimized by storing the result of computations.
+ // Use functions in screen.c to check if they are valid and to update.
+ // w_valid is a bitfield of flags, which indicate if specific values are
+ // valid or need to be recomputed.
int w_valid;
- pos_T w_valid_cursor; /* last known position of w_cursor, used
- to adjust w_valid */
+ pos_T w_valid_cursor; // last known position of w_cursor, used to adjust w_valid
colnr_T w_valid_leftcol; // last known w_leftcol
bool w_viewport_invalid;
- /*
- * w_cline_height is the number of physical lines taken by the buffer line
- * that the cursor is on. We use this to avoid extra calls to plines_win().
- */
+ // w_cline_height is the number of physical lines taken by the buffer line
+ // that the cursor is on. We use this to avoid extra calls to plines_win().
int w_cline_height; // current size of cursor line
bool w_cline_folded; // cursor line is folded
@@ -1313,11 +1264,9 @@ struct window_S {
// more than one screen line or when
// w_leftcol is non-zero
- /*
- * w_wrow and w_wcol specify the cursor position in the window.
- * This is related to positions in the window, not in the display or
- * buffer, thus w_wrow is relative to w_winrow.
- */
+ // w_wrow and w_wcol specify the cursor position in the window.
+ // This is related to positions in the window, not in the display or
+ // buffer, thus w_wrow is relative to w_winrow.
int w_wrow, w_wcol; // cursor position in window
linenr_T w_botline; // number of the line below the bottom of
@@ -1326,16 +1275,14 @@ struct window_S {
int w_filler_rows; // number of filler rows at the end of the
// window
- /*
- * Info about the lines currently in the window is remembered to avoid
- * recomputing it every time. The allocated size of w_lines[] is Rows.
- * Only the w_lines_valid entries are actually valid.
- * When the display is up-to-date w_lines[0].wl_lnum is equal to w_topline
- * and w_lines[w_lines_valid - 1].wl_lnum is equal to w_botline.
- * Between changing text and updating the display w_lines[] represents
- * what is currently displayed. wl_valid is reset to indicated this.
- * This is used for efficient redrawing.
- */
+ // Info about the lines currently in the window is remembered to avoid
+ // recomputing it every time. The allocated size of w_lines[] is Rows.
+ // Only the w_lines_valid entries are actually valid.
+ // When the display is up-to-date w_lines[0].wl_lnum is equal to w_topline
+ // and w_lines[w_lines_valid - 1].wl_lnum is equal to w_botline.
+ // Between changing text and updating the display w_lines[] represents
+ // what is currently displayed. wl_valid is reset to indicated this.
+ // This is used for efficient redrawing.
int w_lines_valid; // number of valid entries
wline_T *w_lines;
@@ -1348,17 +1295,16 @@ struct window_S {
// column being used
int w_scwidth; // width of 'signcolumn'
- /*
- * === end of cached values ===
- */
+ // === end of cached values ===
int w_redr_type; // type of redraw to be performed on win
int w_upd_rows; // number of window lines to update when
- // w_redr_type is REDRAW_TOP
+ // w_redr_type is UPD_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 statusline/winbar must be redrawn
bool w_redr_border; // if true border must be redrawn
+ bool w_redr_statuscol; // if true 'statuscolumn' must be redrawn
// remember what is shown in the ruler for this window (if 'ruler' set)
pos_T w_ru_cursor; // cursor position shown in ruler
@@ -1366,7 +1312,7 @@ struct window_S {
linenr_T w_ru_topline; // topline shown in ruler
linenr_T w_ru_line_count; // line count used for ruler
int w_ru_topfill; // topfill shown in ruler
- char w_ru_empty; // TRUE if ruler shows 0-1 (empty line)
+ char w_ru_empty; // true if ruler shows 0-1 (empty line)
int w_alt_fnum; // alternate file (for # and CTRL-^)
@@ -1399,6 +1345,7 @@ struct window_S {
int w_briopt_shift; // additional shift for breakindent
bool w_briopt_sbr; // sbr in 'briopt'
int w_briopt_list; // additional indent for lists
+ int w_briopt_vcol; // indent for specific column
// transform a pointer to a "onebuf" option into a "allbuf" option
#define GLOBAL_WO(p) ((char *)(p) + sizeof(winopt_T))
@@ -1408,17 +1355,13 @@ struct window_S {
ScopeDictDictItem w_winvar; ///< Variable for "w:" dictionary.
dict_T *w_vars; ///< Dictionary with w: variables.
- /*
- * The w_prev_pcmark field is used to check whether we really did jump to
- * a new line after setting the w_pcmark. If not, then we revert to
- * using the previous w_pcmark.
- */
+ // The w_prev_pcmark field is used to check whether we really did jump to
+ // a new line after setting the w_pcmark. If not, then we revert to
+ // using the previous w_pcmark.
pos_T w_pcmark; // previous context mark
pos_T w_prev_pcmark; // previous w_pcmark
- /*
- * the jumplist contains old cursor positions
- */
+ // the jumplist contains old cursor positions
xfmark_T w_jumplist[JUMPLISTSIZE];
int w_jumplistlen; // number of active entries
int w_jumplistidx; // current position
@@ -1428,12 +1371,10 @@ struct window_S {
matchitem_T *w_match_head; // head of match list
int w_next_match_id; // next match ID
- /*
- * the tagstack grows from 0 upwards:
- * entry 0: older
- * entry 1: newer
- * entry 2: newest
- */
+ // the tagstack grows from 0 upwards:
+ // entry 0: older
+ // entry 1: newer
+ // entry 2: newest
taggy_T w_tagstack[TAGSTACKSIZE]; // the tag stack
int w_tagstackidx; // idx just below active entry
int w_tagstacklen; // number of tags on stack
@@ -1444,17 +1385,14 @@ struct window_S {
bool w_floating; ///< whether the window is floating
FloatConfig w_float_config;
- /*
- * w_fraction is the fractional row of the cursor within the window, from
- * 0 at the top row to FRACTION_MULT at the last row.
- * w_prev_fraction_row was the actual cursor row when w_fraction was last
- * calculated.
- */
+ // w_fraction is the fractional row of the cursor within the window, from
+ // 0 at the top row to FRACTION_MULT at the last row.
+ // w_prev_fraction_row was the actual cursor row when w_fraction was last
+ // calculated.
int w_fraction;
int w_prev_fraction_row;
- linenr_T w_nrwidth_line_count; /* line count when ml_nrwidth_width
- * was computed. */
+ linenr_T w_nrwidth_line_count; // line count when ml_nrwidth_width was computed.
int w_nrwidth_width; // nr of chars to print line count.
qf_info_T *w_llist; // Location list for this window
@@ -1471,10 +1409,37 @@ struct window_S {
StlClickDefinition *w_winbar_click_defs;
// Size of the w_winbar_click_defs array
size_t w_winbar_click_defs_size;
+
+ // Status column click definitions
+ StlClickDefinition *w_statuscol_click_defs;
+ // Size of the w_statuscol_click_defs array
+ size_t w_statuscol_click_defs_size;
+};
+
+/// Struct to hold info for 'statuscolumn'
+typedef struct statuscol statuscol_T;
+
+struct statuscol {
+ int width; ///< width of the status column
+ int cur_attr; ///< current attributes in text
+ int num_attr; ///< attributes used for line number
+ int fold_attr; ///< attributes used for fold column
+ int sign_attr[SIGN_SHOW_MAX + 1]; ///< attributes used for signs
+ int truncate; ///< truncated width
+ bool draw; ///< draw statuscolumn or not
+ char fold_text[9 * 4 + 1]; ///< text in fold column (%C)
+ char *sign_text[SIGN_SHOW_MAX + 1]; ///< text in sign column (%s)
+ char text[MAXPATHL]; ///< text in status column
+ char *textp; ///< current position in text
+ char *text_end; ///< end of text (the NUL byte)
+ stl_hlrec_t *hlrec; ///< highlight groups
+ stl_hlrec_t *hlrecp; ///< current highlight group
};
/// Macros defined in Vim, but not in Neovim
+// uncrustify:off
#define CHANGEDTICK(buf) \
(=== Include buffer.h & use buf_(get|set|inc) _changedtick ===)
+// uncrustify:on
-#endif // NVIM_BUFFER_DEFS_H
+#endif // NVIM_BUFFER_DEFS_H
diff --git a/src/nvim/buffer_updates.c b/src/nvim/buffer_updates.c
index 14973502ab..075ac2adbf 100644
--- a/src/nvim/buffer_updates.c
+++ b/src/nvim/buffer_updates.c
@@ -1,17 +1,31 @@
// 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 <inttypes.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#include "klib/kvec.h"
+#include "lauxlib.h"
+#include "nvim/api/buffer.h"
+#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/assert.h"
#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
#include "nvim/buffer_updates.h"
#include "nvim/extmark.h"
+#include "nvim/globals.h"
+#include "nvim/log.h"
#include "nvim/lua/executor.h"
#include "nvim/memline.h"
+#include "nvim/memory.h"
#include "nvim/msgpack_rpc/channel.h"
+#include "nvim/pos.h"
+#include "nvim/types.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "buffer_updates.c.generated.h"
+# include "buffer_updates.c.generated.h" // IWYU pragma: export
#endif
// Register a channel. Return True if the channel was added, or already added.
@@ -34,12 +48,10 @@ bool buf_updates_register(buf_T *buf, uint64_t channel_id, BufUpdateCallbacks cb
// count how many channels are currently watching the buffer
size_t size = kv_size(buf->update_channels);
- if (size) {
- for (size_t i = 0; i < size; i++) {
- if (kv_A(buf->update_channels, i) == channel_id) {
- // buffer is already registered ... nothing to do
- return true;
- }
+ for (size_t i = 0; i < size; i++) {
+ if (kv_A(buf->update_channels, i) == channel_id) {
+ // buffer is already registered ... nothing to do
+ return true;
}
}
@@ -69,13 +81,14 @@ bool buf_updates_register(buf_T *buf, uint64_t channel_id, BufUpdateCallbacks cb
linedata.size = line_count;
linedata.items = xcalloc(line_count, sizeof(Object));
- buf_collect_lines(buf, line_count, 1, true, &linedata, NULL);
+ buf_collect_lines(buf, line_count, 1, 0, true, &linedata, NULL, NULL);
}
args.items[4] = ARRAY_OBJ(linedata);
args.items[5] = BOOLEAN_OBJ(false);
rpc_send_event(channel_id, "nvim_buf_lines_event", args);
+ api_free_array(args); // TODO(bfredl): no
} else {
buf_updates_changedtick_single(buf, channel_id);
}
@@ -91,10 +104,8 @@ bool buf_updates_active(buf_T *buf)
void buf_updates_send_end(buf_T *buf, uint64_t channelid)
{
- Array args = ARRAY_DICT_INIT;
- args.size = 1;
- args.items = xcalloc(args.size, sizeof(Object));
- args.items[0] = BUFFER_OBJ(buf->handle);
+ MAXSIZE_TEMP_ARRAY(args, 1);
+ ADD_C(args, BUFFER_OBJ(buf->handle));
rpc_send_event(channelid, "nvim_buf_detach_event", args);
}
@@ -135,6 +146,15 @@ void buf_updates_unregister(buf_T *buf, uint64_t channelid)
}
}
+void buf_free_callbacks(buf_T *buf)
+{
+ kv_destroy(buf->update_channels);
+ for (size_t i = 0; i < kv_size(buf->update_callbacks); i++) {
+ buffer_update_callbacks_free(kv_A(buf->update_callbacks, i));
+ }
+ kv_destroy(buf->update_callbacks);
+}
+
void buf_updates_unload(buf_T *buf, bool can_reload)
{
size_t size = kv_size(buf->update_channels);
@@ -230,8 +250,8 @@ void buf_updates_send_changes(buf_T *buf, linenr_T firstline, int64_t num_added,
STATIC_ASSERT(SIZE_MAX >= MAXLNUM, "size_t smaller than MAXLNUM");
linedata.size = (size_t)num_added;
linedata.items = xcalloc((size_t)num_added, sizeof(Object));
- buf_collect_lines(buf, (size_t)num_added, firstline, true, &linedata,
- NULL);
+ buf_collect_lines(buf, (size_t)num_added, firstline, 0, true, &linedata,
+ NULL, NULL);
}
args.items[4] = ARRAY_OBJ(linedata);
args.items[5] = BOOLEAN_OBJ(false);
@@ -241,6 +261,7 @@ void buf_updates_send_changes(buf_T *buf, linenr_T firstline, int64_t num_added,
// the end.
badchannelid = channelid;
}
+ api_free_array(args); // TODO(bfredl): no
}
// We can only ever remove one dead channel at a time. This is OK because the
@@ -285,14 +306,13 @@ void buf_updates_send_changes(buf_T *buf, linenr_T firstline, int64_t num_added,
args.items[7] = INTEGER_OBJ((Integer)deleted_codeunits);
}
textlock++;
- Object res = nlua_call_ref(cb.on_lines, "lines", args, true, NULL);
+ Object res = nlua_call_ref(cb.on_lines, "lines", args, false, NULL);
textlock--;
if (res.type == kObjectTypeBoolean && res.data.boolean == true) {
buffer_update_callbacks_free(cb);
keep = false;
}
- api_free_object(res);
}
if (keep) {
kv_A(buf->update_callbacks, j++) = kv_A(buf->update_callbacks, i);
@@ -335,7 +355,7 @@ void buf_updates_send_splice(buf_T *buf, int start_row, colnr_T start_col, bcoun
ADD_C(args, INTEGER_OBJ(new_byte));
textlock++;
- Object res = nlua_call_ref(cb.on_bytes, "bytes", args, true, NULL);
+ Object res = nlua_call_ref(cb.on_bytes, "bytes", args, false, NULL);
textlock--;
if (res.type == kObjectTypeBoolean && res.data.boolean == true) {
@@ -371,14 +391,13 @@ void buf_updates_changedtick(buf_T *buf)
textlock++;
Object res = nlua_call_ref(cb.on_changedtick, "changedtick",
- args, true, NULL);
+ args, false, NULL);
textlock--;
if (res.type == kObjectTypeBoolean && res.data.boolean == true) {
buffer_update_callbacks_free(cb);
keep = false;
}
- api_free_object(res);
}
if (keep) {
kv_A(buf->update_callbacks, j++) = kv_A(buf->update_callbacks, i);
@@ -389,15 +408,13 @@ void buf_updates_changedtick(buf_T *buf)
void buf_updates_changedtick_single(buf_T *buf, uint64_t channel_id)
{
- Array args = ARRAY_DICT_INIT;
- args.size = 2;
- args.items = xcalloc(args.size, sizeof(Object));
+ MAXSIZE_TEMP_ARRAY(args, 2);
// the first argument is always the buffer handle
- args.items[0] = BUFFER_OBJ(buf->handle);
+ ADD_C(args, BUFFER_OBJ(buf->handle));
// next argument is b:changedtick
- args.items[1] = INTEGER_OBJ(buf_get_changedtick(buf));
+ ADD_C(args, INTEGER_OBJ(buf_get_changedtick(buf)));
// don't try and clean up dead channels here
rpc_send_event(channel_id, "nvim_buf_changedtick_event", args);
diff --git a/src/nvim/buffer_updates.h b/src/nvim/buffer_updates.h
index 3c2635be71..961fec879b 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" // for buf_T
-#include "nvim/extmark.h" // for bcount_t
+#include "nvim/buffer_defs.h"
+#include "nvim/extmark.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "buffer_updates.h.generated.h"
diff --git a/src/nvim/change.c b/src/nvim/change.c
index 5184dc0689..06696610b0 100644
--- a/src/nvim/change.c
+++ b/src/nvim/change.c
@@ -3,8 +3,16 @@
/// change.c: functions related to changing text
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "nvim/ascii.h"
#include "nvim/assert.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
#include "nvim/buffer_updates.h"
#include "nvim/change.h"
#include "nvim/charset.h"
@@ -13,21 +21,35 @@
#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/extmark.h"
-#include "nvim/fileio.h"
#include "nvim/fold.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/grid_defs.h"
+#include "nvim/highlight_defs.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
#include "nvim/insexpand.h"
+#include "nvim/macros.h"
#include "nvim/mark.h"
+#include "nvim/mbyte.h"
#include "nvim/memline.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
#include "nvim/move.h"
#include "nvim/option.h"
+#include "nvim/os/time.h"
#include "nvim/plines.h"
+#include "nvim/pos.h"
+#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/state.h"
+#include "nvim/strings.h"
+#include "nvim/textformat.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
+#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "change.c.generated.h"
@@ -96,8 +118,7 @@ void changed(void)
// Create a swap file if that is wanted.
// Don't do this for "nofile" and "nowrite" buffer types.
- if (curbuf->b_may_swap
- && !bt_dontwrite(curbuf)) {
+ if (curbuf->b_may_swap && !bt_dontwrite(curbuf)) {
bool save_need_wait_return = need_wait_return;
need_wait_return = false;
@@ -107,7 +128,7 @@ void changed(void)
// Wait two seconds, to make sure the user reads this unexpected
// message. Since we could be anywhere, call wait_return() now,
// and don't let the emsg() set msg_scroll.
- if (need_wait_return && emsg_silent == 0) {
+ if (need_wait_return && emsg_silent == 0 && !in_assert_fails) {
ui_flush();
os_delay(2002L, true);
wait_return(true);
@@ -223,8 +244,8 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, linenr_T
FOR_ALL_TAB_WINDOWS(tp, wp) {
if (wp->w_buffer == curbuf) {
// Mark this window to be redrawn later.
- if (wp->w_redr_type < VALID) {
- wp->w_redr_type = VALID;
+ if (wp->w_redr_type < UPD_VALID) {
+ wp->w_redr_type = UPD_VALID;
}
// Check if a change in the buffer has invalidated the cached
@@ -301,17 +322,17 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, linenr_T
// requires a redraw.
if (wp->w_p_rnu && xtra != 0) {
wp->w_last_cursor_lnum_rnu = 0;
- redraw_later(wp, VALID);
+ redraw_later(wp, UPD_VALID);
}
// Cursor line highlighting probably need to be updated with
- // "VALID" if it's below the change.
+ // "UPD_VALID" if it's below the change.
// If the cursor line is inside the change we need to redraw more.
if (wp->w_p_cul) {
if (xtra == 0) {
- redraw_later(wp, VALID);
+ redraw_later(wp, UPD_VALID);
} else if (lnum <= wp->w_last_cursorline) {
- redraw_later(wp, SOME_VALID);
+ redraw_later(wp, UPD_SOME_VALID);
}
}
}
@@ -319,8 +340,8 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, linenr_T
// Call update_screen() later, which checks out what needs to be redrawn,
// since it notices b_mod_set and then uses b_mod_*.
- if (must_redraw < VALID) {
- must_redraw = VALID;
+ if (must_redraw < UPD_VALID) {
+ must_redraw = UPD_VALID;
}
// when the cursor line is changed always trigger CursorMoved
@@ -364,7 +385,7 @@ void changed_bytes(linenr_T lnum, colnr_T col)
if (curwin->w_p_diff) {
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_p_diff && wp != curwin) {
- redraw_later(wp, VALID);
+ redraw_later(wp, UPD_VALID);
linenr_T wlnum = diff_lnum_win(lnum, wp);
if (wlnum > 0) {
changedOneline(wp->w_buffer, wlnum);
@@ -397,14 +418,7 @@ void appended_lines(linenr_T lnum, linenr_T count)
/// Like appended_lines(), but adjust marks first.
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, (linenr_T)count, 0L, kExtmarkUndo);
- } else {
- extmark_adjust(curbuf, lnum + 1, (linenr_T)MAXLNUM, (linenr_T)count, 0L,
- kExtmarkUndo);
- }
+ mark_adjust(lnum + 1, (linenr_T)MAXLNUM, (linenr_T)count, 0L, kExtmarkUndo);
changed_lines(lnum + 1, 0, lnum + 1, (linenr_T)count, true);
}
@@ -493,7 +507,7 @@ void changed_lines(linenr_T lnum, colnr_T col, linenr_T lnume, linenr_T xtra, bo
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_p_diff && wp != curwin) {
- redraw_later(wp, VALID);
+ redraw_later(wp, UPD_VALID);
wlnum = diff_lnum_win(lnum, wp);
if (wlnum > 0) {
changed_lines_buf(wp->w_buffer, wlnum,
@@ -534,16 +548,69 @@ void unchanged(buf_T *buf, int ff, bool always_inc_changedtick)
}
}
+/// Save the current values of 'fileformat' and 'fileencoding', so that we know
+/// the file must be considered changed when the value is different.
+void save_file_ff(buf_T *buf)
+{
+ buf->b_start_ffc = (unsigned char)(*buf->b_p_ff);
+ buf->b_start_eof = buf->b_p_eof;
+ buf->b_start_eol = buf->b_p_eol;
+ buf->b_start_bomb = buf->b_p_bomb;
+
+ // Only use free/alloc when necessary, they take time.
+ 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 = xstrdup(buf->b_p_fenc);
+ }
+}
+
+/// Return true if 'fileformat' and/or 'fileencoding' has a different value
+/// from when editing started (save_file_ff() called).
+/// Also when 'endofline' was changed and 'binary' is set, or when 'bomb' was
+/// changed and 'binary' is not set.
+/// Also when 'endofline' was changed and 'fixeol' is not set.
+/// When "ignore_empty" is true don't consider a new, empty buffer to be
+/// changed.
+bool file_ff_differs(buf_T *buf, bool ignore_empty)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ // In a buffer that was never loaded the options are not valid.
+ if (buf->b_flags & BF_NEVERLOADED) {
+ return false;
+ }
+ if (ignore_empty
+ && (buf->b_flags & BF_NEW)
+ && buf->b_ml.ml_line_count == 1
+ && *ml_get_buf(buf, (linenr_T)1, false) == NUL) {
+ return false;
+ }
+ if (buf->b_start_ffc != *buf->b_p_ff) {
+ return true;
+ }
+ if ((buf->b_p_bin || !buf->b_p_fixeol)
+ && (buf->b_start_eof != buf->b_p_eof || buf->b_start_eol != buf->b_p_eol)) {
+ return true;
+ }
+ if (!buf->b_p_bin && buf->b_start_bomb != buf->b_p_bomb) {
+ return true;
+ }
+ if (buf->b_start_fenc == NULL) {
+ return *buf->b_p_fenc != NUL;
+ }
+ return strcmp(buf->b_start_fenc, buf->b_p_fenc) != 0;
+}
+
/// Insert string "p" at the cursor position. Stops at a NUL byte.
/// Handles Replace mode and multi-byte characters.
-void ins_bytes(char_u *p)
+void ins_bytes(char *p)
{
- ins_bytes_len(p, STRLEN(p));
+ ins_bytes_len(p, strlen(p));
}
/// Insert string "p" with length "len" at the cursor position.
/// Handles Replace mode and multi-byte characters.
-void ins_bytes_len(char_u *p, size_t len)
+void ins_bytes_len(char *p, size_t len)
{
size_t n;
for (size_t i = 0; i < len; i += n) {
@@ -560,18 +627,18 @@ void ins_bytes_len(char_u *p, size_t len)
/// convert bytes to a character.
void ins_char(int c)
{
- char_u buf[MB_MAXBYTES + 1];
- size_t n = (size_t)utf_char2bytes(c, (char *)buf);
+ char buf[MB_MAXBYTES + 1];
+ size_t n = (size_t)utf_char2bytes(c, 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((char_u *)buf, n);
+ ins_char_bytes(buf, n);
}
-void ins_char_bytes(char_u *buf, size_t charlen)
+void ins_char_bytes(char *buf, size_t charlen)
{
// Break tabs if needed.
if (virtual_active() && curwin->w_cursor.coladd > 0) {
@@ -580,8 +647,8 @@ void ins_char_bytes(char_u *buf, size_t charlen)
size_t col = (size_t)curwin->w_cursor.col;
linenr_T lnum = curwin->w_cursor.lnum;
- char_u *oldp = ml_get(lnum);
- size_t linelen = STRLEN(oldp) + 1; // length of old line including NUL
+ char *oldp = ml_get(lnum);
+ size_t linelen = strlen(oldp) + 1; // length of old line including NUL
// The lengths default to the values for when not replacing.
size_t oldlen = 0; // nr of bytes inserted
@@ -610,7 +677,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((char *)oldp + col + oldlen);
+ oldlen += (size_t)utfc_ptr2len(oldp + col + oldlen);
// Deleted a bit too much, insert spaces.
if (vcol > new_vcol) {
newlen += (size_t)(vcol - new_vcol);
@@ -619,7 +686,7 @@ 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((char *)oldp + col);
+ oldlen = (size_t)utfc_ptr2len(oldp + col);
}
// Push the replaced bytes onto the replace stack, so that they can be
@@ -632,7 +699,7 @@ void ins_char_bytes(char_u *buf, size_t charlen)
}
}
- char_u *newp = xmalloc(linelen + newlen - oldlen);
+ char *newp = xmalloc(linelen + newlen - oldlen);
// Copy bytes before the cursor.
if (col > 0) {
@@ -640,7 +707,7 @@ void ins_char_bytes(char_u *buf, size_t charlen)
}
// Copy bytes after the changed character(s).
- char_u *p = newp + col;
+ char *p = newp + col;
if (linelen > col + oldlen) {
memmove(p + newlen, oldp + col + oldlen,
(size_t)(linelen - col - oldlen));
@@ -655,7 +722,7 @@ void ins_char_bytes(char_u *buf, size_t charlen)
}
// Replace the line in the buffer.
- ml_replace(lnum, (char *)newp, false);
+ ml_replace(lnum, newp, false);
// mark the buffer as changed and prepare for displaying
inserted_bytes(lnum, (colnr_T)col, (int)oldlen, (int)newlen);
@@ -665,7 +732,7 @@ void ins_char_bytes(char_u *buf, size_t charlen)
if (p_sm && (State & MODE_INSERT)
&& msg_silent == 0
&& !ins_compl_active()) {
- showmatch(utf_ptr2char((char *)buf));
+ showmatch(utf_ptr2char(buf));
}
if (!p_ri || (State & REPLACE_FLAG)) {
@@ -678,9 +745,9 @@ void ins_char_bytes(char_u *buf, size_t charlen)
/// Insert a string at the cursor position.
/// Note: Does NOT handle Replace mode.
/// Caller must have prepared for undo.
-void ins_str(char_u *s)
+void ins_str(char *s)
{
- int newlen = (int)STRLEN(s);
+ int newlen = (int)strlen(s);
linenr_T lnum = curwin->w_cursor.lnum;
if (virtual_active() && curwin->w_cursor.coladd > 0) {
@@ -688,10 +755,10 @@ void ins_str(char_u *s)
}
colnr_T col = curwin->w_cursor.col;
- char_u *oldp = ml_get(lnum);
- int oldlen = (int)STRLEN(oldp);
+ char *oldp = ml_get(lnum);
+ int oldlen = (int)strlen(oldp);
- char_u *newp = (char_u *)xmalloc((size_t)oldlen + (size_t)newlen + 1);
+ char *newp = xmalloc((size_t)oldlen + (size_t)newlen + 1);
if (col > 0) {
memmove(newp, oldp, (size_t)col);
}
@@ -699,7 +766,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, (char *)newp, false);
+ ml_replace(lnum, newp, false);
inserted_bytes(lnum, col, 0, newlen);
curwin->w_cursor.col += newlen;
}
@@ -723,9 +790,9 @@ int del_char(bool fixpos)
int del_chars(long count, int fixpos)
{
int bytes = 0;
- char_u *p = get_cursor_pos_ptr();
+ char *p = get_cursor_pos_ptr();
for (long i = 0; i < count && *p != NUL; i++) {
- int l = utfc_ptr2len((char *)p);
+ int l = utfc_ptr2len(p);
bytes += l;
p += l;
}
@@ -746,8 +813,8 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine)
linenr_T lnum = curwin->w_cursor.lnum;
colnr_T col = curwin->w_cursor.col;
bool fixpos = fixpos_arg;
- char_u *oldp = ml_get(lnum);
- colnr_T oldlen = (colnr_T)STRLEN(oldp);
+ char *oldp = ml_get(lnum);
+ colnr_T oldlen = (colnr_T)strlen(oldp);
// Can't do anything when the cursor is on the NUL after the line.
if (col >= oldlen) {
@@ -766,7 +833,7 @@ 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((char *)oldp + col) >= count) {
+ && utfc_ptr2len(oldp + col) >= count) {
int cc[MAX_MCO];
(void)utfc_ptr2char(oldp + col, cc);
@@ -775,7 +842,7 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine)
int n = col;
do {
col = n;
- count = utf_ptr2len((char *)oldp + n);
+ count = utf_ptr2len(oldp + n);
n += count;
} while (utf_composinglike(oldp + col, oldp + n));
fixpos = false;
@@ -801,7 +868,7 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine)
// If the old line has been allocated the deletion can be done in the
// existing line. Otherwise a new line has to be allocated.
bool was_alloced = ml_line_alloced(); // check if oldp was allocated
- char_u *newp;
+ char *newp;
if (was_alloced) {
ml_add_deleted_len(curbuf->b_ml.ml_line_ptr, oldlen);
newp = oldp; // use same allocated memory
@@ -811,7 +878,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, (char *)newp, false);
+ ml_replace(lnum, newp, false);
}
// mark the buffer as changed and prepare for displaying
@@ -823,10 +890,10 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine)
/// Copy the indent from ptr to the current line (and fill to size).
/// Leaves the cursor on the first non-blank in the line.
/// @return true if the line was changed.
-int copy_indent(int size, char_u *src)
+int copy_indent(int size, char *src)
{
- char_u *p = NULL;
- char_u *line = NULL;
+ char *p = NULL;
+ char *line = NULL;
int ind_len;
int line_len = 0;
int tab_pad;
@@ -838,7 +905,7 @@ int copy_indent(int size, char_u *src)
ind_len = 0;
int ind_done = 0;
int ind_col = 0;
- char_u *s = src;
+ char *s = src;
// Count/copy the usable portion of the source line.
while (todo > 0 && ascii_iswhite(*s)) {
@@ -911,7 +978,7 @@ int copy_indent(int size, char_u *src)
if (p == NULL) {
// Allocate memory for the result: the copied indent, new indent
// and the rest of the line.
- line_len = (int)STRLEN(get_cursor_line_ptr()) + 1;
+ line_len = (int)strlen(get_cursor_line_ptr()) + 1;
assert(ind_len + line_len >= 0);
size_t line_size;
STRICT_ADD(ind_len, line_len, &line_size, size_t);
@@ -924,7 +991,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, (char *)line, false);
+ ml_replace(curwin->w_cursor.lnum, line, false);
// Put the cursor after the indent.
curwin->w_cursor.col = ind_len;
@@ -948,15 +1015,15 @@ 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.
+/// leader in front 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, bool *did_do_comment)
{
- char_u *next_line = NULL; // copy of the next line
- char_u *p_extra = NULL; // what goes to next line
+ char *next_line = NULL; // copy of the next line
+ char *p_extra = NULL; // what goes to next line
colnr_T less_cols = 0; // less columns for mark in new line
colnr_T less_cols_off = 0; // columns to skip for mark adjust
pos_T old_cursor; // old cursor position
@@ -969,9 +1036,9 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
int comment_start = 0; // start index of the comment leader
char *lead_flags; // position in 'comments' for comment leader
char *leader = NULL; // copy of comment leader
- char_u *allocated = NULL; // allocated memory
+ char *allocated = NULL; // allocated memory
char *p;
- char_u saved_char = NUL; // init for GCC
+ char saved_char = NUL; // init for GCC
pos_T *pos;
bool do_si = may_do_si();
bool do_cindent;
@@ -985,7 +1052,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
colnr_T mincol = curwin->w_cursor.col + 1;
// make a copy of the current line so we can mess with it
- char_u *saved_line = vim_strsave(get_cursor_line_ptr());
+ char *saved_line = xstrdup(get_cursor_line_ptr());
if (State & VREPLACE_FLAG) {
// With MODE_VREPLACE we make a copy of the next line, which we will be
@@ -996,9 +1063,9 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
// the line, replacing what was there before and pushing the right
// stuff onto the replace stack. -- webb.
if (curwin->w_cursor.lnum < orig_line_count) {
- next_line = vim_strsave(ml_get(curwin->w_cursor.lnum + 1));
+ next_line = xstrdup(ml_get(curwin->w_cursor.lnum + 1));
} else {
- next_line = vim_strsave((char_u *)"");
+ next_line = xstrdup("");
}
// In MODE_VREPLACE state, a NL replaces the rest of the line, and
@@ -1008,9 +1075,9 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
// autoindent etc) a bit later.
replace_push(NUL); // Call twice because BS over NL expects it
replace_push(NUL);
- p = (char *)saved_line + curwin->w_cursor.col;
+ p = saved_line + curwin->w_cursor.col;
while (*p != NUL) {
- p += replace_push_mb((char_u *)p);
+ p += replace_push_mb(p);
}
saved_line[curwin->w_cursor.col] = NUL;
}
@@ -1018,10 +1085,10 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
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((char *)p_extra);
+ p = skipwhite(p_extra);
first_char = (unsigned char)(*p);
}
- extra_len = (int)STRLEN(p_extra);
+ extra_len = (int)strlen(p_extra);
saved_char = *p_extra;
*p_extra = NUL;
}
@@ -1058,7 +1125,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
char *ptr;
old_cursor = curwin->w_cursor;
- ptr = (char *)saved_line;
+ ptr = saved_line;
if (flags & OPENLINE_DO_COM) {
lead_len = get_leader_len(ptr, NULL, false, true);
} else {
@@ -1068,7 +1135,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
// Skip preprocessor directives, unless they are recognised as comments.
if (lead_len == 0 && ptr[0] == '#') {
while (ptr[0] == '#' && curwin->w_cursor.lnum > 1) {
- ptr = (char *)ml_get(--curwin->w_cursor.lnum);
+ ptr = ml_get(--curwin->w_cursor.lnum);
}
newindent = get_indent();
}
@@ -1104,7 +1171,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
}
} else { // Not a comment line
// Find last non-blank in line
- p = ptr + STRLEN(ptr) - 1;
+ p = ptr + strlen(ptr) - 1;
while (p > ptr && ascii_iswhite(*p)) {
p--;
}
@@ -1130,7 +1197,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
if ((pos = findmatch(NULL, '(')) != NULL) {
curwin->w_cursor.lnum = pos->lnum;
newindent = get_indent();
- ptr = (char *)get_cursor_line_ptr();
+ ptr = get_cursor_line_ptr();
}
}
// If last character is '{' do indent, without
@@ -1142,7 +1209,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
// Don't do this if the previous line ended in ';' or
// '}'.
} else if (last_char != ';' && last_char != '}'
- && cin_is_cinword((char_u *)ptr)) {
+ && cin_is_cinword(ptr)) {
did_si = true;
}
}
@@ -1154,12 +1221,12 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
while ((ptr[0] == '#' || was_backslashed)
&& curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
- if (*ptr && ptr[STRLEN(ptr) - 1] == '\\') {
+ if (*ptr && ptr[strlen(ptr) - 1] == '\\') {
was_backslashed = true;
} else {
was_backslashed = false;
}
- ptr = (char *)ml_get(++curwin->w_cursor.lnum);
+ ptr = ml_get(++curwin->w_cursor.lnum);
}
if (was_backslashed) {
newindent = 0; // Got to end of file
@@ -1192,13 +1259,13 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
// 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((char *)saved_line, &lead_flags, dir == BACKWARD, true);
+ lead_len = get_leader_len(saved_line, &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, &lead_flags, false, true);
+ lead_len = get_leader_len(saved_line + comment_start, &lead_flags, false, true);
if (lead_len != 0) {
lead_len += comment_start;
if (did_do_comment != NULL) {
@@ -1213,13 +1280,13 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
if (lead_len > 0) {
char *lead_repl = NULL; // replaces comment leader
int lead_repl_len = 0; // length of *lead_repl
- char_u lead_middle[COM_MAX_LEN]; // middle-comment string
- char_u lead_end[COM_MAX_LEN]; // end-comment string
- char_u *comment_end = NULL; // where lead_end has been found
+ char lead_middle[COM_MAX_LEN]; // middle-comment string
+ char lead_end[COM_MAX_LEN]; // end-comment string
+ char *comment_end = NULL; // where lead_end has been found
int extra_space = false; // append extra space
int current_flag;
int require_blank = false; // requires blank after middle
- char_u *p2;
+ char *p2;
// If the comment leader has the start, middle or end flag, it may not
// be used or may be replaced with the middle leader.
@@ -1261,15 +1328,15 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
size_t n = copy_option_part(&p, (char *)lead_end, COM_MAX_LEN, ",");
if (end_comment_pending == -1) { // we can set it now
- end_comment_pending = lead_end[n - 1];
+ end_comment_pending = (unsigned char)lead_end[n - 1];
}
// If the end of the comment is in the same line, don't use
// the comment leader.
if (dir == FORWARD) {
- for (p = (char *)saved_line + lead_len; *p; p++) {
- if (STRNCMP(p, lead_end, n) == 0) {
- comment_end = (char_u *)p;
+ for (p = saved_line + lead_len; *p; p++) {
+ if (strncmp(p, lead_end, n) == 0) {
+ comment_end = p;
lead_len = 0;
break;
}
@@ -1280,7 +1347,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
if (lead_len > 0) {
if (current_flag == COM_START) {
lead_repl = (char *)lead_middle;
- lead_repl_len = (int)STRLEN(lead_middle);
+ lead_repl_len = (int)strlen(lead_middle);
}
// If we have hit RETURN immediately after the start
@@ -1302,17 +1369,17 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
// Remember where the end is, might want to use it to find the
// start (for C-comments).
if (dir == FORWARD) {
- comment_end = (char_u *)skipwhite((char *)saved_line);
+ comment_end = skipwhite(saved_line);
lead_len = 0;
break;
}
// Doing "O" on the end of a comment inserts the middle leader.
// Find the string for the middle leader, searching backwards.
- while (p > (char *)curbuf->b_p_com && *p != ',') {
+ while (p > curbuf->b_p_com && *p != ',') {
p--;
}
- for (lead_repl = p; lead_repl > (char *)curbuf->b_p_com
+ for (lead_repl = p; lead_repl > curbuf->b_p_com
&& lead_repl[-1] != ':'; lead_repl--) {}
lead_repl_len = (int)(p - lead_repl);
@@ -1321,7 +1388,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
extra_space = true;
// Check whether we allow automatic ending of comments
- for (p2 = (char_u *)p; *p2 && *p2 != ':'; p2++) {
+ for (p2 = p; *p2 && *p2 != ':'; p2++) {
if (*p2 == COM_AUTO_END) {
end_comment_pending = -1; // means we want to set it
}
@@ -1331,7 +1398,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
while (*p2 && *p2 != ',') {
p2++;
}
- end_comment_pending = p2[-1];
+ end_comment_pending = (unsigned char)p2[-1];
}
break;
}
@@ -1357,9 +1424,9 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
+ 1;
assert(bytes >= 0);
leader = xmalloc((size_t)bytes);
- allocated = (char_u *)leader; // remember to free it later
+ allocated = leader; // remember to free it later
- STRLCPY(leader, saved_line, lead_len + 1);
+ xstrlcpy(leader, saved_line, (size_t)lead_len + 1);
// TODO(vim): handle multi-byte and double width chars
for (int li = 0; li < comment_start; li++) {
@@ -1391,7 +1458,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
// Compute the length of the replaced characters in
// screen characters, not bytes.
{
- int repl_size = vim_strnsize((char_u *)lead_repl, lead_repl_len);
+ int repl_size = vim_strnsize(lead_repl, lead_repl_len);
int old_size = 0;
char *endp = p;
int l;
@@ -1414,7 +1481,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
// blank-out any other chars from the old leader.
while (--p >= leader) {
- int l = utf_head_off((char_u *)leader, (char_u *)p);
+ int l = utf_head_off(leader, p);
if (l > 1) {
p -= l;
@@ -1436,13 +1503,13 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
// screen characters, not bytes. Move the part that is
// not to be overwritten.
{
- int repl_size = vim_strnsize((char_u *)lead_repl, lead_repl_len);
+ int repl_size = vim_strnsize(lead_repl, lead_repl_len);
int i;
int l;
for (i = 0; i < lead_len && p[i] != NUL; i += l) {
l = utfc_ptr2len(p + i);
- if (vim_strnsize((char_u *)p, i + l) > repl_size) {
+ if (vim_strnsize(p, i + l) > repl_size) {
break;
}
}
@@ -1485,7 +1552,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
// Recompute the indent, it may have changed.
if (curbuf->b_p_ai || do_si) {
- newindent = get_indent_str_vtab((char_u *)leader,
+ newindent = get_indent_str_vtab(leader,
curbuf->b_p_ts,
curbuf->b_p_vts_array, false);
}
@@ -1568,7 +1635,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
}
if (curbuf->b_p_ai || (flags & OPENLINE_DELSPACES)) {
while ((*p_extra == ' ' || *p_extra == '\t')
- && !utf_iscomposing(utf_ptr2char((char *)p_extra + 1))) {
+ && !utf_iscomposing(utf_ptr2char(p_extra + 1))) {
if (REPLACE_NORMAL(State)) {
replace_push(*p_extra);
}
@@ -1582,7 +1649,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
}
if (p_extra == NULL) {
- p_extra = (char_u *)""; // append empty line
+ p_extra = ""; // append empty line
}
// concatenate leader and p_extra, if there is a leader
@@ -1590,7 +1657,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
if (flags & OPENLINE_COM_LIST && second_line_indent > 0) {
int i;
int padding = second_line_indent
- - (newindent + (int)STRLEN(leader));
+ - (newindent + (int)strlen(leader));
// Here whitespace is inserted after the comment char.
// Below, set_indent(newindent, SIN_INSERT) will insert the
@@ -1602,7 +1669,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
}
}
STRCAT(leader, p_extra);
- p_extra = (char_u *)leader;
+ p_extra = leader;
did_ai = true; // So truncating blanks works with comments
less_cols -= lead_len;
} else {
@@ -1615,18 +1682,12 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
curwin->w_cursor.lnum--;
}
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) {
+ if (ml_append(curwin->w_cursor.lnum, p_extra, (colnr_T)0, false) == FAIL) {
goto theend;
}
// Postpone calling changed_lines(), because it would mess up folding
// with markers.
- // Skip mark_adjust when adding a line after the last one, there can't
- // be marks there. But still needed in diff mode.
- if (curwin->w_cursor.lnum + 1 < curbuf->b_ml.ml_line_count
- || curwin->w_p_diff) {
- mark_adjust(curwin->w_cursor.lnum + 1, (linenr_T)MAXLNUM, 1L, 0L,
- kExtmarkNOOP);
- }
+ mark_adjust(curwin->w_cursor.lnum + 1, (linenr_T)MAXLNUM, 1L, 0L, kExtmarkNOOP);
did_append = true;
} else {
// In MODE_VREPLACE state we are starting to replace the next line.
@@ -1637,7 +1698,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
(void)u_save_cursor(); // errors are ignored!
vr_lines_changed++;
}
- ml_replace(curwin->w_cursor.lnum, (char *)p_extra, true);
+ ml_replace(curwin->w_cursor.lnum, p_extra, true);
changed_bytes(curwin->w_cursor.lnum, 0);
// TODO(vigoux): extmark_splice_cols here??
curwin->w_cursor.lnum--;
@@ -1702,9 +1763,9 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
if (trunc_line && !(flags & OPENLINE_KEEPTRAIL)) {
truncate_spaces(saved_line);
}
- ml_replace(curwin->w_cursor.lnum, (char *)saved_line, false);
+ ml_replace(curwin->w_cursor.lnum, saved_line, false);
- int new_len = (int)STRLEN(saved_line);
+ int new_len = (int)strlen(saved_line);
// TODO(vigoux): maybe there is issues there with expandtabs ?
int cols_spliced = 0;
@@ -1744,7 +1805,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
if (did_append) {
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));
+ 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);
}
@@ -1763,19 +1824,19 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
vreplace_mode = 0;
}
- // May do lisp indenting.
- if (!p_paste
- && leader == NULL
- && curbuf->b_p_lisp
- && curbuf->b_p_ai) {
- fixthisline(get_lisp_indent);
- ai_col = (colnr_T)getwhitecols_curline();
- }
-
- // May do indenting after opening a new line.
- if (do_cindent) {
- do_c_expr_indent();
- ai_col = (colnr_T)getwhitecols_curline();
+ if (!p_paste) {
+ if (leader == NULL
+ && !use_indentexpr_for_lisp()
+ && curbuf->b_p_lisp
+ && curbuf->b_p_ai) {
+ // do lisp indenting
+ fixthisline(get_lisp_indent);
+ ai_col = (colnr_T)getwhitecols_curline();
+ } else if (do_cindent || (curbuf->b_p_ai && use_indentexpr_for_lisp())) {
+ // do 'cindent' or 'indentexpr' indenting
+ do_c_expr_indent();
+ ai_col = (colnr_T)getwhitecols_curline();
+ }
}
if (vreplace_mode != 0) {
@@ -1787,10 +1848,10 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
// 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());
+ p_extra = xstrdup(get_cursor_line_ptr());
// Put back original line
- ml_replace(curwin->w_cursor.lnum, (char *)next_line, false);
+ ml_replace(curwin->w_cursor.lnum, next_line, false);
// Insert new stuff into line again
curwin->w_cursor.col = 0;
@@ -1814,16 +1875,16 @@ theend:
/// If "fixpos" is true fix the cursor position when done.
void truncate_line(int fixpos)
{
- char_u *newp;
+ char *newp;
linenr_T lnum = curwin->w_cursor.lnum;
colnr_T col = curwin->w_cursor.col;
if (col == 0) {
- newp = vim_strsave((char_u *)"");
+ newp = xstrdup("");
} else {
- newp = vim_strnsave(ml_get(lnum), (size_t)col);
+ newp = xstrnsave(ml_get(lnum), (size_t)col);
}
- ml_replace(lnum, (char *)newp, false);
+ ml_replace(lnum, newp, false);
// mark the buffer as changed and prepare for displaying
changed_bytes(lnum, curwin->w_cursor.col);
@@ -1900,7 +1961,7 @@ int get_leader_len(char *line, char **flags, bool backward, bool include_space)
while (line[i] != NUL) {
// scan through the 'comments' option for a match
int found_one = false;
- for (list = (char *)curbuf->b_p_com; *list;) {
+ for (list = curbuf->b_p_com; *list;) {
// Get one option part into part_buf[]. Advance "list" to next
// one. Put "string" at start of string.
if (!got_com && flags != NULL) {
@@ -2033,11 +2094,11 @@ int get_last_leader_offset(char *line, char **flags)
char part_buf[COM_MAX_LEN]; // buffer for one option part
// Repeat to match several nested comment strings.
- int i = (int)STRLEN(line);
+ int i = (int)strlen(line);
while (--i >= lower_check_bound) {
// scan through the 'comments' option for a match
int found_one = false;
- for (list = (char *)curbuf->b_p_com; *list;) {
+ for (list = curbuf->b_p_com; *list;) {
char *flags_save = list;
// Get one option part into part_buf[]. Advance list to next one.
@@ -2120,9 +2181,9 @@ int get_last_leader_offset(char *line, char **flags)
while (ascii_iswhite(*com_leader)) {
com_leader++;
}
- len1 = (int)STRLEN(com_leader);
+ len1 = (int)strlen(com_leader);
- for (list = (char *)curbuf->b_p_com; *list;) {
+ for (list = curbuf->b_p_com; *list;) {
char *flags_save = list;
(void)copy_option_part(&list, part_buf2, COM_MAX_LEN, ",");
@@ -2134,7 +2195,7 @@ int get_last_leader_offset(char *line, char **flags)
while (ascii_iswhite(*string)) {
string++;
}
- len2 = (int)STRLEN(string);
+ len2 = (int)strlen(string);
if (len2 == 0) {
continue;
}
@@ -2143,7 +2204,7 @@ int get_last_leader_offset(char *line, char **flags)
// beginning the com_leader.
for (off = (len2 > i ? i : len2); off > 0 && off + len1 > len2;) {
off--;
- if (!STRNCMP(string + off, com_leader, len2 - off)) {
+ if (!strncmp(string + off, com_leader, (size_t)(len2 - off))) {
if (i - off < lower_check_bound) {
lower_check_bound = i - off;
}
diff --git a/src/nvim/change.h b/src/nvim/change.h
index fdfa8a29ec..d1d016c630 100644
--- a/src/nvim/change.h
+++ b/src/nvim/change.h
@@ -1,8 +1,8 @@
#ifndef NVIM_CHANGE_H
#define NVIM_CHANGE_H
-#include "nvim/buffer_defs.h" // for buf_T
-#include "nvim/pos.h" // for linenr_T
+#include "nvim/buffer_defs.h"
+#include "nvim/pos.h"
// flags for open_line()
#define OPENLINE_DELSPACES 0x01 // delete spaces after cursor
diff --git a/src/nvim/channel.c b/src/nvim/channel.c
index 5910053025..65bb87bc2c 100644
--- a/src/nvim/channel.c
+++ b/src/nvim/channel.c
@@ -1,24 +1,42 @@
// 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 <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "lauxlib.h"
#include "nvim/api/private/converter.h"
+#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
-#include "nvim/api/ui.h"
#include "nvim/autocmd.h"
+#include "nvim/buffer_defs.h"
#include "nvim/channel.h"
#include "nvim/eval.h"
#include "nvim/eval/encode.h"
+#include "nvim/eval/typval.h"
+#include "nvim/event/loop.h"
+#include "nvim/event/rstream.h"
#include "nvim/event/socket.h"
+#include "nvim/event/wstream.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/log.h"
#include "nvim/lua/executor.h"
+#include "nvim/main.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/server.h"
-#include "nvim/os/fs.h"
+#include "nvim/os/os_defs.h"
#include "nvim/os/shell.h"
-#ifdef WIN32
+#include "nvim/rbuffer.h"
+#ifdef MSWIN
# include "nvim/os/os_win_console.h"
# include "nvim/os/pty_conpty_win.h"
#endif
-#include "nvim/ascii.h"
#include "nvim/path.h"
static bool did_stdio = false;
@@ -197,7 +215,7 @@ void channel_create_event(Channel *chan, const char *ext_source)
// external events should be included.
source = ext_source;
} else {
- eval_fmt_source_name_line((char *)IObuff, sizeof(IObuff));
+ eval_fmt_source_name_line(IObuff, sizeof(IObuff));
source = (const char *)IObuff;
}
@@ -359,6 +377,7 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout, CallbackReader
} else {
has_out = rpc || callback_reader_set(chan->on_data);
has_err = callback_reader_set(chan->on_stderr);
+ proc->fwd_err = chan->on_stderr.fwd_err;
}
switch (stdin_mode) {
@@ -492,7 +511,7 @@ uint64_t channel_from_stdio(bool rpc, CallbackReader on_output, const char **err
int stdin_dup_fd = STDIN_FILENO;
int stdout_dup_fd = STDOUT_FILENO;
-#ifdef WIN32
+#ifdef MSWIN
// Strangely, ConPTY doesn't work if stdin and stdout are pipes. So replace
// stdin and stdout with CONIN$ and CONOUT$, respectively.
if (embedded_mode && os_has_conpty_working()) {
@@ -501,6 +520,13 @@ uint64_t channel_from_stdio(bool rpc, CallbackReader on_output, const char **err
stdout_dup_fd = os_dup(STDOUT_FILENO);
os_replace_stdout_and_stderr_to_conout();
}
+#else
+ if (embedded_mode) {
+ stdin_dup_fd = dup(STDIN_FILENO);
+ stdout_dup_fd = dup(STDOUT_FILENO);
+ dup2(STDERR_FILENO, STDOUT_FILENO);
+ dup2(STDERR_FILENO, STDIN_FILENO);
+ }
#endif
rstream_init_fd(&main_loop, &channel->stream.stdio.in, stdin_dup_fd, 0);
wstream_init_fd(&main_loop, &channel->stream.stdio.out, stdout_dup_fd, 0);
diff --git a/src/nvim/channel.h b/src/nvim/channel.h
index 0f1b481792..5743eaead5 100644
--- a/src/nvim/channel.h
+++ b/src/nvim/channel.h
@@ -1,13 +1,26 @@
#ifndef NVIM_CHANNEL_H
#define NVIM_CHANNEL_H
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/event/libuv_process.h"
+#include "nvim/event/multiqueue.h"
#include "nvim/event/process.h"
#include "nvim/event/socket.h"
+#include "nvim/event/stream.h"
+#include "nvim/garray.h"
+#include "nvim/macros.h"
#include "nvim/main.h"
+#include "nvim/map.h"
+#include "nvim/map_defs.h"
#include "nvim/msgpack_rpc/channel_defs.h"
#include "nvim/os/pty_process.h"
+#include "nvim/terminal.h"
+#include "nvim/types.h"
#define CHAN_STDIO 1
#define CHAN_STDERR 2
@@ -53,6 +66,7 @@ typedef struct {
garray_T buffer;
bool eof;
bool buffered;
+ bool fwd_err;
const char *type;
} CallbackReader;
@@ -60,6 +74,7 @@ typedef struct {
.self = NULL, \
.buffer = GA_EMPTY_INIT_VALUE, \
.buffered = false, \
+ .fwd_err = false, \
.type = NULL })
static inline bool callback_reader_set(CallbackReader reader)
{
diff --git a/src/nvim/charset.c b/src/nvim/charset.c
index 6238d85b3a..5aec9ccf9d 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -6,26 +6,35 @@
/// Code related to character sets.
#include <assert.h>
+#include <errno.h>
#include <inttypes.h>
+#include <limits.h>
+#include <stdlib.h>
#include <string.h>
-#include <wctype.h>
+#include "auto/config.h"
+#include "klib/kvec.h"
#include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
-#include "nvim/func_attr.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/garray.h"
+#include "nvim/globals.h"
+#include "nvim/grid_defs.h"
#include "nvim/indent.h"
-#include "nvim/main.h"
+#include "nvim/keycodes.h"
+#include "nvim/macros.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/move.h"
#include "nvim/option.h"
-#include "nvim/os_unix.h"
#include "nvim/path.h"
#include "nvim/plines.h"
+#include "nvim/pos.h"
#include "nvim/state.h"
#include "nvim/strings.h"
#include "nvim/vim.h"
@@ -137,7 +146,7 @@ int buf_init_chartab(buf_T *buf, int global)
// options Each option is a list of characters, character numbers or
// ranges, separated by commas, e.g.: "200-210,x,#-178,-"
for (i = global ? 0 : 3; i <= 3; i++) {
- const char_u *p;
+ const char *p;
if (i == 0) {
// first round: 'isident'
p = p_isi;
@@ -245,7 +254,7 @@ int buf_init_chartab(buf_T *buf, int global)
c++;
}
- c = *p;
+ c = (uint8_t)(*p);
p = skip_to_option_part(p);
if ((c == ',') && (*p == NUL)) {
@@ -267,8 +276,8 @@ int buf_init_chartab(buf_T *buf, int global)
/// @param bufsize
void trans_characters(char *buf, int bufsize)
{
- char_u *trs; // translated character
- int len = (int)STRLEN(buf); // length of string needing translation
+ char *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) {
@@ -277,8 +286,8 @@ void trans_characters(char *buf, int bufsize)
if ((trs_len = utfc_ptr2len(buf)) > 1) {
len -= trs_len;
} else {
- trs = transchar_byte((uint8_t)(*buf));
- trs_len = (int)STRLEN(trs);
+ trs = (char *)transchar_byte((uint8_t)(*buf));
+ trs_len = (int)strlen(trs);
if (trs_len > 1) {
room -= trs_len - 1;
@@ -313,7 +322,7 @@ size_t transstr_len(const char *const s, bool untab)
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]);
+ pcc[0] = utfc_ptr2char(p, &pcc[1]);
if (vim_isprintc(pcc[0])) {
len += l;
@@ -359,7 +368,7 @@ size_t transstr_buf(const char *const s, char *const buf, const size_t len, bool
break; // Exceeded `buf` size.
}
int pcc[MAX_MCO + 1];
- pcc[0] = utfc_ptr2char((const char_u *)p, &pcc[1]);
+ pcc[0] = utfc_ptr2char(p, &pcc[1]);
if (vim_isprintc(pcc[0])) {
memmove(buf_p, p, l);
@@ -411,12 +420,28 @@ char *transstr(const char *const s, bool untab)
return buf;
}
+size_t kv_transstr(StringBuilder *str, const char *const s, bool untab)
+ FUNC_ATTR_NONNULL_ARG(1)
+{
+ if (!s) {
+ return 0;
+ }
+
+ // Compute the length of the result, taking account of unprintable
+ // multi-byte characters.
+ const size_t len = transstr_len(s, untab);
+ kv_ensure_space(*str, len + 1);
+ transstr_buf(s, str->items + str->size, len + 1, untab);
+ str->size += len; // do not include NUL byte
+ return len;
+}
+
/// Convert the string "str[orglen]" to do ignore-case comparing.
/// Use the current locale.
///
/// When "buf" is NULL, return an allocated string.
/// Otherwise, put the result in buf, limited by buflen, and return buf.
-char_u *str_foldcase(char_u *str, int orglen, char_u *buf, int buflen)
+char *str_foldcase(char *str, int orglen, char *buf, int buflen)
FUNC_ATTR_NONNULL_RET
{
garray_T ga;
@@ -424,7 +449,7 @@ char_u *str_foldcase(char_u *str, int orglen, char_u *buf, int buflen)
int len = orglen;
#define GA_CHAR(i) ((char *)ga.ga_data)[i]
-#define GA_PTR(i) ((char_u *)ga.ga_data + (i))
+#define GA_PTR(i) ((char *)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))
@@ -452,8 +477,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((char *)STR_PTR(i));
- int olen = utf_ptr2len((char *)STR_PTR(i));
+ int c = utf_ptr2char(STR_PTR(i));
+ int olen = utf_ptr2len(STR_PTR(i));
int lc = mb_tolower(c);
// Only replace the character when it is not an invalid
@@ -487,15 +512,15 @@ char_u *str_foldcase(char_u *str, int orglen, char_u *buf, int buflen)
}
}
}
- (void)utf_char2bytes(lc, (char *)STR_PTR(i));
+ (void)utf_char2bytes(lc, STR_PTR(i));
}
// skip to next multi-byte char
- i += utfc_ptr2len((char *)STR_PTR(i));
+ i += utfc_ptr2len(STR_PTR(i));
}
if (buf == NULL) {
- return (char_u *)ga.ga_data;
+ return ga.ga_data;
}
return buf;
}
@@ -515,9 +540,9 @@ static char_u transchar_charbuf[11];
/// @param[in] c Character to translate.
///
/// @return translated character into a static buffer.
-char_u *transchar(int c)
+char *transchar(int c)
{
- return transchar_buf(curbuf, c);
+ return (char *)transchar_buf(curbuf, c);
}
char_u *transchar_buf(const buf_T *buf, int c)
@@ -559,7 +584,7 @@ char_u *transchar_byte(const int c)
transchar_nonprint(curbuf, transchar_charbuf, c);
return transchar_charbuf;
}
- return transchar(c);
+ return (char_u *)transchar(c);
}
/// Convert non-printable characters to 2..4 printable ones
@@ -650,7 +675,7 @@ static inline unsigned nr2hex(unsigned n)
///
/// @param b
///
-/// @reeturn Number of display cells.
+/// @return Number of display cells.
int byte2cells(int b)
FUNC_ATTR_PURE
{
@@ -709,7 +734,7 @@ int ptr2cells(const char *p_in)
/// @return number of character cells.
int vim_strsize(char *s)
{
- return vim_strnsize((char_u *)s, MAXCOL);
+ return vim_strnsize(s, MAXCOL);
}
/// Return the number of character cells string "s[len]" will take on the
@@ -721,13 +746,13 @@ int vim_strsize(char *s)
/// @param len
///
/// @return Number of character cells.
-int vim_strnsize(char_u *s, int len)
+int vim_strnsize(char *s, int len)
{
assert(s != NULL);
int size = 0;
while (*s != NUL && --len >= 0) {
- int l = utfc_ptr2len((char *)s);
- size += ptr2cells((char *)s);
+ int l = utfc_ptr2len(s);
+ size += ptr2cells(s);
s += l;
len -= l - 1;
}
@@ -786,7 +811,7 @@ bool vim_iswordc_buf(const int c, buf_T *const buf)
/// @param p pointer to the multi-byte character
///
/// @return true if "p" points to a keyword character.
-bool vim_iswordp(const char_u *const p)
+bool vim_iswordp(const char *const p)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
return vim_iswordp_buf(p, curbuf);
@@ -799,13 +824,13 @@ bool vim_iswordp(const char_u *const p)
/// @param buf buffer whose keywords to use
///
/// @return true if "p" points to a keyword character.
-bool vim_iswordp_buf(const char_u *const p, buf_T *const buf)
+bool vim_iswordp_buf(const char *const p, buf_T *const buf)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
- int c = *p;
+ int c = (uint8_t)(*p);
if (MB_BYTE2LEN(c) > 1) {
- c = utf_ptr2char((char *)p);
+ c = utf_ptr2char(p);
}
return vim_iswordc_buf(c, buf);
}
@@ -829,8 +854,8 @@ bool vim_isfilec(int c)
bool vim_isfilec_or_wc(int c)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- char_u buf[2];
- buf[0] = (char_u)c;
+ char buf[2];
+ buf[0] = (char)c;
buf[1] = NUL;
return vim_isfilec(c) || c == ']' || path_has_wildcard(buf);
}
@@ -905,15 +930,15 @@ 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)
{
- char_u *ptr; // points to current char
- char_u *posptr; // points to char at pos->col
+ char *ptr; // points to current char
+ char *posptr; // points to char at pos->col
int incr;
int head;
long *vts = wp->w_buffer->b_p_vts_array;
int ts = (int)wp->w_buffer->b_p_ts;
colnr_T vcol = 0;
- char_u *line = ptr = ml_get_buf(wp->w_buffer, pos->lnum, false); // start of the line
+ char *line = ptr = ml_get_buf(wp->w_buffer, pos->lnum, false); // start of the line
if (pos->col == MAXCOL) {
// continue until the NUL
@@ -930,17 +955,21 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en
posptr -= utf_head_off(line, posptr);
}
+ chartabsize_T cts;
+ init_chartabsize_arg(&cts, wp, pos->lnum, 0, line, line);
+
// This function is used very often, do some speed optimizations.
// When 'list', 'linebreak', 'showbreak' and 'breakindent' are not set
- // use a simple loop.
+ // and there are no virtual text use a simple loop.
// Also use this when 'list' is set but tabs take their normal size.
if ((!wp->w_p_list || (wp->w_p_lcs_chars.tab1 != NUL))
&& !wp->w_p_lbr
&& *get_showbreak_value(wp) == NUL
- && !wp->w_p_bri) {
+ && !wp->w_p_bri
+ && !cts.cts_has_virt_text) {
for (;;) {
head = 0;
- int c = *ptr;
+ int c = (uint8_t)(*ptr);
// make sure we don't go past the end of the line
if (c == NUL) {
@@ -956,7 +985,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((char *)ptr);
+ incr = utf_ptr2cells(ptr);
} else {
incr = g_chartab[c] & CT_CELL_MASK;
}
@@ -966,7 +995,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en
// cells wide.
if ((incr == 2)
&& wp->w_p_wrap
- && (MB_BYTE2LEN(*ptr) > 1)
+ && (MB_BYTE2LEN((uint8_t)(*ptr)) > 1)
&& in_win_border(wp, vcol)) {
incr++;
head = 1;
@@ -984,25 +1013,29 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en
} else {
for (;;) {
// A tab gets expanded, depending on the current column
+ // Other things also take up space.
head = 0;
- incr = win_lbr_chartabsize(wp, line, ptr, vcol, &head);
+ incr = win_lbr_chartabsize(&cts, &head);
// make sure we don't go past the end of the line
- if (*ptr == NUL) {
+ if (*cts.cts_ptr == NUL) {
// NUL at end of line only takes one column
incr = 1;
break;
}
- if ((posptr != NULL) && (ptr >= posptr)) {
+ if ((posptr != NULL) && (cts.cts_ptr >= posptr)) {
// character at pos->col
break;
}
- vcol += incr;
- MB_PTR_ADV(ptr);
+ cts.cts_vcol += incr;
+ MB_PTR_ADV(cts.cts_ptr);
}
+ vcol = cts.cts_vcol;
+ ptr = cts.cts_ptr;
}
+ clear_chartabsize_arg(&cts);
if (start != NULL) {
*start = vcol + head;
@@ -1013,6 +1046,8 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en
}
if (cursor != NULL) {
+ // cursor is after inserted text
+ vcol += cts.cts_cur_text_width;
if ((*ptr == TAB)
&& (State & MODE_NORMAL)
&& !wp->w_p_list
@@ -1066,10 +1101,10 @@ void getvvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *e
colnr_T endadd = 0;
// Cannot put the cursor on part of a wide character.
- char_u *ptr = ml_get_buf(wp->w_buffer, pos->lnum, false);
+ char *ptr = ml_get_buf(wp->w_buffer, pos->lnum, false);
- if (pos->col < (colnr_T)STRLEN(ptr)) {
- int c = utf_ptr2char((char *)ptr + pos->col);
+ if (pos->col < (colnr_T)strlen(ptr)) {
+ int c = utf_ptr2char(ptr + pos->col);
if ((c != TAB) && vim_isprintc(c)) {
endadd = (colnr_T)(char2cells(c) - 1);
if (coladd > endadd) {
@@ -1147,7 +1182,7 @@ char *skipwhite(const char *const p)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
FUNC_ATTR_NONNULL_RET
{
- return (char *)skipwhite_len((char_u *)p, STRLEN(p));
+ return skipwhite_len(p, strlen(p));
}
/// Like `skipwhite`, but skip up to `len` characters.
@@ -1158,14 +1193,14 @@ char *skipwhite(const char *const p)
///
/// @return Pointer to character after the skipped whitespace, or the `len`-th
/// character in the string.
-char_u *skipwhite_len(const char_u *p, size_t len)
+char *skipwhite_len(const char *p, size_t len)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
FUNC_ATTR_NONNULL_RET
{
for (; len > 0 && ascii_iswhite(*p); len--) {
p++;
}
- return (char_u *)p;
+ return (char *)p;
}
// getwhitecols: return the number of whitespace
@@ -1175,10 +1210,10 @@ intptr_t getwhitecols_curline(void)
return getwhitecols(get_cursor_line_ptr());
}
-intptr_t getwhitecols(const char_u *p)
+intptr_t getwhitecols(const char *p)
FUNC_ATTR_PURE
{
- return (char_u *)skipwhite((char *)p) - p;
+ return skipwhite(p) - p;
}
/// Skip over digits
@@ -1222,10 +1257,10 @@ const char *skipbin(const char *q)
///
/// @return Pointer to the character after the skipped digits and hex
/// characters.
-char_u *skiphex(char_u *q)
+char *skiphex(char *q)
FUNC_ATTR_PURE
{
- char_u *p = q;
+ char *p = q;
while (ascii_isxdigit(*p)) {
// skip to next non-digit
p++;
@@ -1238,10 +1273,10 @@ char_u *skiphex(char_u *q)
/// @param q
///
/// @return Pointer to the digit or (NUL after the string).
-char_u *skiptodigit(char_u *q)
+char *skiptodigit(char *q)
FUNC_ATTR_PURE
{
- char_u *p = q;
+ char *p = q;
while (*p != NUL && !ascii_isdigit(*p)) {
// skip to next digit
p++;
@@ -1272,10 +1307,10 @@ const char *skiptobin(const char *q)
/// @param q
///
/// @return Pointer to the hex character or (NUL after the string).
-char_u *skiptohex(char_u *q)
+char *skiptohex(char *q)
FUNC_ATTR_PURE
{
- char_u *p = q;
+ char *p = q;
while (*p != NUL && !ascii_isxdigit(*p)) {
// skip to next digit
p++;
@@ -1288,13 +1323,13 @@ char_u *skiptohex(char_u *q)
/// @param[in] p Text to skip over.
///
/// @return Pointer to the next whitespace or NUL character.
-char_u *skiptowhite(const char_u *p)
+char *skiptowhite(const char *p)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE
{
while (*p != ' ' && *p != '\t' && *p != NUL) {
p++;
}
- return (char_u *)p;
+ return (char *)p;
}
/// skiptowhite_esc: Like skiptowhite(), but also skip escaped chars
@@ -1319,16 +1354,16 @@ char *skiptowhite_esc(char *p)
/// @param[in] p Text to skip over.
///
/// @return Pointer to the next '\n' or NUL character.
-char_u *skip_to_newline(const char_u *const p)
+char *skip_to_newline(const char *const p)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
FUNC_ATTR_NONNULL_RET
{
- return (char_u *)xstrchrnul((const char *)p, NL);
+ return xstrchrnul(p, NL);
}
/// Gets a number from a string and skips over it, signalling overflow.
///
-/// @param[out] pp A pointer to a pointer to char_u.
+/// @param[out] pp A pointer to a pointer to char.
/// It will be advanced past the read number.
/// @param[out] nr Number read from the string.
///
@@ -1345,7 +1380,7 @@ bool try_getdigits(char **pp, intmax_t *nr)
/// Gets a number from a string and skips over it.
///
-/// @param[out] pp Pointer to a pointer to char_u.
+/// @param[out] pp Pointer to a pointer to char.
/// It will be advanced past the read number.
/// @param strict Abort on overflow.
/// @param def Default value, if parsing fails or overflow occurs.
@@ -1412,10 +1447,10 @@ int32_t getdigits_int32(char **pp, bool strict, long def)
/// Check that "lbuf" is empty or only contains blanks.
///
/// @param lbuf line buffer to check
-bool vim_isblankline(char_u *lbuf)
+bool vim_isblankline(char *lbuf)
FUNC_ATTR_PURE
{
- char_u *p = (char_u *)skipwhite((char *)lbuf);
+ char *p = skipwhite(lbuf);
return *p == NUL || *p == '\r' || *p == '\n';
}
@@ -1453,14 +1488,14 @@ bool vim_isblankline(char_u *lbuf)
/// @param strict If true, fail if the number has unexpected trailing
/// 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,
+void vim_str2nr(const char *const start, int *const prep, int *const len, const int what,
varnumber_T *const nptr, uvarnumber_T *const unptr, const int maxlen,
const bool strict)
FUNC_ATTR_NONNULL_ARG(1)
{
- const char *ptr = (const char *)start;
+ const char *ptr = start;
#define STRING_ENDED(ptr) \
- (!(maxlen == 0 || (int)((ptr) - (const char *)start) < maxlen))
+ (!(maxlen == 0 || (int)((ptr) - start) < maxlen))
int pre = 0; // default is decimal
const bool negative = (ptr[0] == '-');
uvarnumber_T un = 0;
@@ -1512,7 +1547,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 = (char_u)ptr[1];
+ pre = (uint8_t)ptr[1];
// Detect hexadecimal: 0x or 0X followed by hex digit.
if ((what & STR2NR_HEX)
&& !STRING_ENDED(ptr + 2)
@@ -1600,7 +1635,7 @@ vim_str2nr_hex:
vim_str2nr_proceed:
// Check for an alphanumeric character immediately following, that is
// most likely a typo.
- if (strict && ptr - (const char *)start != maxlen && ASCII_ISALNUM(*ptr)) {
+ if (strict && ptr - start != maxlen && ASCII_ISALNUM(*ptr)) {
return;
}
@@ -1609,7 +1644,7 @@ vim_str2nr_proceed:
}
if (len != NULL) {
- *len = (int)(ptr - (const char *)start);
+ *len = (int)(ptr - start);
}
if (nptr != NULL) {
@@ -1654,8 +1689,9 @@ 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)
+///
+/// @return -1 if one of the characters is not hex.
+int hexhex2nr(const char *p)
FUNC_ATTR_PURE
{
if (!ascii_isxdigit(p[0]) || !ascii_isxdigit(p[1])) {
@@ -1677,17 +1713,17 @@ int hexhex2nr(char_u *p)
/// characters.
///
/// @param str file path string to check
-bool rem_backslash(const char_u *str)
+bool rem_backslash(const char *str)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
#ifdef BACKSLASH_IN_FILENAME
return str[0] == '\\'
- && str[1] < 0x80
+ && (uint8_t)str[1] < 0x80
&& (str[1] == ' '
|| (str[1] != NUL
&& str[1] != '*'
&& str[1] != '?'
- && !vim_isfilec(str[1])));
+ && !vim_isfilec((uint8_t)str[1])));
#else // ifdef BACKSLASH_IN_FILENAME
return str[0] == '\\' && str[1] != NUL;
@@ -1697,7 +1733,7 @@ bool rem_backslash(const char_u *str)
/// Halve the number of backslashes in a file name argument.
///
/// @param p
-void backslash_halve(char_u *p)
+void backslash_halve(char *p)
{
for (; *p; p++) {
if (rem_backslash(p)) {
@@ -1711,11 +1747,11 @@ void backslash_halve(char_u *p)
/// @param p
///
/// @return String with the number of backslashes halved.
-char_u *backslash_halve_save(const char_u *p)
+char *backslash_halve_save(const char *p)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
{
// TODO(philix): simplify and improve backslash_halve_save algorithm
- char_u *res = vim_strsave(p);
+ char *res = xstrdup(p);
backslash_halve(res);
return res;
}
diff --git a/src/nvim/charset.h b/src/nvim/charset.h
index c4e5d9522b..e1ef06ef1d 100644
--- a/src/nvim/charset.h
+++ b/src/nvim/charset.h
@@ -1,10 +1,13 @@
#ifndef NVIM_CHARSET_H
#define NVIM_CHARSET_H
+#include <stdbool.h>
+
#include "nvim/buffer_defs.h"
#include "nvim/eval/typval.h"
#include "nvim/option_defs.h"
#include "nvim/pos.h"
+#include "nvim/strings.h"
#include "nvim/types.h"
/// Return the folded-case equivalent of the given character
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
index e82e98ba4e..be815151ef 100644
--- a/src/nvim/cmdexpand.c
+++ b/src/nvim/cmdexpand.c
@@ -3,9 +3,20 @@
// cmdexpand.c: functions for command-line completion
+#include <assert.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "auto/config.h"
+#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/arglist.h"
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
@@ -13,27 +24,42 @@
#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/eval/funcs.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/eval/userfunc.h"
#include "nvim/ex_cmds.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/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/grid.h"
+#include "nvim/hashtab.h"
#include "nvim/help.h"
+#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
-#include "nvim/if_cscope.h"
+#include "nvim/keycodes.h"
+#include "nvim/locale.h"
+#include "nvim/log.h"
#include "nvim/lua/executor.h"
+#include "nvim/macros.h"
#include "nvim/mapping.h"
+#include "nvim/mbyte.h"
+#include "nvim/memory.h"
#include "nvim/menu.h"
+#include "nvim/message.h"
#include "nvim/option.h"
#include "nvim/os/os.h"
+#include "nvim/path.h"
#include "nvim/popupmenu.h"
+#include "nvim/pos.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
+#include "nvim/runtime.h"
#include "nvim/search.h"
#include "nvim/sign.h"
+#include "nvim/statusline.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/tag.h"
@@ -47,7 +73,7 @@
typedef char *(*CompleteListItemGetter)(expand_T *, int);
/// Type used by call_user_expand_func
-typedef void *(*user_expand_func_T)(const char_u *, int, typval_T *);
+typedef void *(*user_expand_func_T)(const char *, int, typval_T *);
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "cmdexpand.c.generated.h"
@@ -63,10 +89,48 @@ static int compl_match_arraysize;
static int compl_startcol;
static int compl_selected;
+#define SHOW_MATCH(m) (showtail ? showmatches_gettail(matches[m], false) : matches[m])
+
+/// Returns true if fuzzy completion is supported for a given cmdline completion
+/// context.
+static bool cmdline_fuzzy_completion_supported(const expand_T *const xp)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE
+{
+ return (wop_flags & WOP_FUZZY)
+ && xp->xp_context != EXPAND_BOOL_SETTINGS
+ && xp->xp_context != EXPAND_COLORS
+ && xp->xp_context != EXPAND_COMPILER
+ && xp->xp_context != EXPAND_DIRECTORIES
+ && xp->xp_context != EXPAND_FILES
+ && xp->xp_context != EXPAND_FILES_IN_PATH
+ && xp->xp_context != EXPAND_FILETYPE
+ && xp->xp_context != EXPAND_HELP
+ && xp->xp_context != EXPAND_LUA
+ && xp->xp_context != EXPAND_OLD_SETTING
+ && xp->xp_context != EXPAND_OWNSYNTAX
+ && xp->xp_context != EXPAND_PACKADD
+ && xp->xp_context != EXPAND_SHELLCMD
+ && xp->xp_context != EXPAND_TAGS
+ && xp->xp_context != EXPAND_TAGS_LISTFILES
+ && xp->xp_context != EXPAND_USER_LIST
+ && xp->xp_context != EXPAND_USER_LUA;
+}
+
+/// Returns true if fuzzy completion for cmdline completion is enabled and
+/// "fuzzystr" is not empty. If search pattern is empty, then don't use fuzzy
+/// matching.
+bool cmdline_fuzzy_complete(const char *const fuzzystr)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE
+{
+ return (wop_flags & WOP_FUZZY) && *fuzzystr != NUL;
+}
+
+/// Sort function for the completion matches.
+/// <SNR> functions should be sorted to the end.
static int sort_func_compare(const void *s1, const void *s2)
{
- char_u *p1 = *(char_u **)s1;
- char_u *p2 = *(char_u **)s2;
+ char *p1 = *(char **)s1;
+ char *p2 = *(char **)s2;
if (*p1 != '<' && *p2 == '<') {
return -1;
@@ -74,74 +138,79 @@ static int sort_func_compare(const void *s1, const void *s2)
if (*p1 == '<' && *p2 != '<') {
return 1;
}
- return STRCMP(p1, p2);
+ return strcmp(p1, p2);
}
-static void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char **files, int options)
+/// Escape special characters in the cmdline completion matches.
+static void wildescape(expand_T *xp, const char *str, int numfiles, char **files)
{
- int i;
- char_u *p;
+ char *p;
const int vse_what = xp->xp_context == EXPAND_BUFFERS ? VSE_BUFFER : VSE_NONE;
- // May change home directory back to "~"
- if (options & WILD_HOME_REPLACE) {
- tilde_replace(str, numfiles, files);
- }
-
- if (options & WILD_ESCAPE) {
- if (xp->xp_context == EXPAND_FILES
- || xp->xp_context == EXPAND_FILES_IN_PATH
- || xp->xp_context == EXPAND_SHELLCMD
- || xp->xp_context == EXPAND_BUFFERS
- || xp->xp_context == EXPAND_DIRECTORIES) {
- // Insert a backslash into a file name before a space, \, %, #
- // and wildmatch characters, except '~'.
- for (i = 0; i < numfiles; i++) {
- // for ":set path=" we need to escape spaces twice
- if (xp->xp_backslash == XP_BS_THREE) {
- p = vim_strsave_escaped((char_u *)files[i], (char_u *)" ");
- xfree(files[i]);
- files[i] = (char *)p;
+ if (xp->xp_context == EXPAND_FILES
+ || xp->xp_context == EXPAND_FILES_IN_PATH
+ || xp->xp_context == EXPAND_SHELLCMD
+ || xp->xp_context == EXPAND_BUFFERS
+ || xp->xp_context == EXPAND_DIRECTORIES) {
+ // Insert a backslash into a file name before a space, \, %, #
+ // and wildmatch characters, except '~'.
+ for (int i = 0; i < numfiles; i++) {
+ // for ":set path=" we need to escape spaces twice
+ if (xp->xp_backslash == XP_BS_THREE) {
+ p = vim_strsave_escaped(files[i], " ");
+ xfree(files[i]);
+ files[i] = p;
#if defined(BACKSLASH_IN_FILENAME)
- p = vim_strsave_escaped(files[i], (char_u *)" ");
- xfree(files[i]);
- files[i] = p;
+ p = vim_strsave_escaped(files[i], " ");
+ xfree(files[i]);
+ files[i] = p;
#endif
- }
+ }
#ifdef BACKSLASH_IN_FILENAME
- p = (char_u *)vim_strsave_fnameescape((const char *)files[i], vse_what);
+ p = vim_strsave_fnameescape(files[i], vse_what);
#else
- p = (char_u *)vim_strsave_fnameescape((const char *)files[i],
- xp->xp_shell ? VSE_SHELL : vse_what);
+ p = vim_strsave_fnameescape(files[i], xp->xp_shell ? VSE_SHELL : vse_what);
#endif
- xfree(files[i]);
- files[i] = (char *)p;
+ xfree(files[i]);
+ files[i] = p;
- // If 'str' starts with "\~", replace "~" at start of
- // files[i] with "\~".
- if (str[0] == '\\' && str[1] == '~' && files[i][0] == '~') {
- escape_fname(&files[i]);
- }
+ // If 'str' starts with "\~", replace "~" at start of
+ // files[i] with "\~".
+ if (str[0] == '\\' && str[1] == '~' && files[i][0] == '~') {
+ escape_fname(&files[i]);
}
- xp->xp_backslash = XP_BS_NONE;
+ }
+ xp->xp_backslash = XP_BS_NONE;
- // If the first file starts with a '+' escape it. Otherwise it
- // could be seen as "+cmd".
- if (*files[0] == '+') {
- escape_fname(&files[0]);
- }
- } else if (xp->xp_context == EXPAND_TAGS) {
- // Insert a backslash before characters in a tag name that
- // would terminate the ":tag" command.
- for (i = 0; i < numfiles; i++) {
- p = vim_strsave_escaped((char_u *)files[i], (char_u *)"\\|\"");
- xfree(files[i]);
- files[i] = (char *)p;
- }
+ // If the first file starts with a '+' escape it. Otherwise it
+ // could be seen as "+cmd".
+ if (*files[0] == '+') {
+ escape_fname(&files[0]);
+ }
+ } else if (xp->xp_context == EXPAND_TAGS) {
+ // Insert a backslash before characters in a tag name that
+ // would terminate the ":tag" command.
+ for (int i = 0; i < numfiles; i++) {
+ p = vim_strsave_escaped(files[i], "\\|\"");
+ xfree(files[i]);
+ files[i] = p;
}
}
}
+/// Escape special characters in the cmdline completion matches.
+static void ExpandEscape(expand_T *xp, char *str, int numfiles, char **files, int options)
+{
+ // May change home directory back to "~"
+ if (options & WILD_HOME_REPLACE) {
+ tilde_replace(str, numfiles, files);
+ }
+
+ if (options & WILD_ESCAPE) {
+ wildescape(xp, str, numfiles, files);
+ }
+}
+
/// Return FAIL if this is not an appropriate context in which to do
/// completion of anything, return OK if it is (even if there are no matches).
/// For the caller, this means that the character is just passed through like a
@@ -153,8 +222,8 @@ int nextwild(expand_T *xp, int type, int options, bool escape)
{
CmdlineInfo *const ccline = get_cmdline_info();
int i, j;
- char_u *p1;
- char_u *p2;
+ char *p1;
+ char *p2;
int difflen;
if (xp->xp_numfiles == -1) {
@@ -171,34 +240,43 @@ int nextwild(expand_T *xp, int type, int options, bool escape)
return FAIL;
}
- if (!(ui_has(kUICmdline) || ui_has(kUIWildmenu))) {
+ // If cmd_silent is set then don't show the dots, because redrawcmd() below
+ // won't remove them.
+ if (!cmd_silent && !(ui_has(kUICmdline) || ui_has(kUIWildmenu))) {
msg_puts("..."); // show that we are busy
ui_flush();
}
- i = (int)((char_u *)xp->xp_pattern - ccline->cmdbuff);
+ i = (int)(xp->xp_pattern - ccline->cmdbuff);
assert(ccline->cmdpos >= i);
xp->xp_pattern_len = (size_t)ccline->cmdpos - (size_t)i;
- if (type == WILD_NEXT || type == WILD_PREV) {
+ if (type == WILD_NEXT || type == WILD_PREV
+ || type == WILD_PAGEUP || type == WILD_PAGEDOWN
+ || type == WILD_PUM_WANT) {
// Get next/previous match for a previous expanded pattern.
p2 = ExpandOne(xp, NULL, NULL, 0, type);
} else {
+ if (cmdline_fuzzy_completion_supported(xp)) {
+ // If fuzzy matching, don't modify the search string
+ p1 = xstrdup(xp->xp_pattern);
+ } else {
+ p1 = addstar(xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
+ }
// Translate string into pattern and expand it.
- p1 = addstar((char_u *)xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
const int use_options = (options
| WILD_HOME_REPLACE
| WILD_ADD_SLASH
| WILD_SILENT
| (escape ? WILD_ESCAPE : 0)
| (p_wic ? WILD_ICASE : 0));
- p2 = ExpandOne(xp, p1, vim_strnsave(&ccline->cmdbuff[i], xp->xp_pattern_len),
+ p2 = ExpandOne(xp, p1, xstrnsave(&ccline->cmdbuff[i], xp->xp_pattern_len),
use_options, type);
xfree(p1);
// xp->xp_pattern might have been modified by ExpandOne (for example,
// in lua completion), so recompute the pattern index and length
- i = (int)((char_u *)xp->xp_pattern - ccline->cmdbuff);
+ i = (int)(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.
@@ -209,23 +287,23 @@ int nextwild(expand_T *xp, int type, int options, bool escape)
break;
}
}
- if ((int)STRLEN(p2) < j) {
+ if ((int)strlen(p2) < j) {
XFREE_CLEAR(p2);
}
}
}
if (p2 != NULL && !got_int) {
- difflen = (int)STRLEN(p2) - (int)(xp->xp_pattern_len);
+ 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 = (char *)ccline->cmdbuff + i;
+ xp->xp_pattern = ccline->cmdbuff + i;
}
assert(ccline->cmdpos <= ccline->cmdlen);
memmove(&ccline->cmdbuff[ccline->cmdpos + difflen],
&ccline->cmdbuff[ccline->cmdpos],
(size_t)ccline->cmdlen - (size_t)ccline->cmdpos + 1);
- memmove(&ccline->cmdbuff[i], p2, STRLEN(p2));
+ memmove(&ccline->cmdbuff[i], p2, strlen(p2));
ccline->cmdlen += difflen;
ccline->cmdpos += difflen;
}
@@ -250,19 +328,54 @@ int nextwild(expand_T *xp, int type, int options, bool escape)
return OK;
}
+/// Create and display a cmdline completion popup menu with items from
+/// "matches".
+static int cmdline_pum_create(CmdlineInfo *ccline, expand_T *xp, char **matches, int numMatches,
+ int showtail)
+{
+ assert(numMatches >= 0);
+ // Add all the completion matches
+ compl_match_arraysize = numMatches;
+ compl_match_array = xmalloc(sizeof(pumitem_T) * (size_t)compl_match_arraysize);
+ for (int i = 0; i < numMatches; i++) {
+ compl_match_array[i] = (pumitem_T){
+ .pum_text = SHOW_MATCH(i),
+ .pum_info = NULL,
+ .pum_extra = NULL,
+ .pum_kind = NULL,
+ };
+ }
+
+ // Compute the popup menu starting column
+ char *endpos = showtail ? showmatches_gettail(xp->xp_pattern, true) : xp->xp_pattern;
+ if (ui_has(kUICmdline)) {
+ compl_startcol = (int)(endpos - ccline->cmdbuff);
+ } else {
+ compl_startcol = cmd_screencol((int)(endpos - ccline->cmdbuff));
+ }
+
+ // no default selection
+ compl_selected = -1;
+
+ cmdline_pum_display(true);
+
+ return EXPAND_OK;
+}
+
void cmdline_pum_display(bool changed_array)
{
pum_display(compl_match_array, compl_match_arraysize, compl_selected,
changed_array, compl_startcol);
}
+/// Returns true if the cmdline completion popup menu is being displayed.
bool cmdline_pum_active(void)
{
// compl_match_array != NULL should already imply pum_visible() in Nvim.
return compl_match_array != NULL;
}
-/// Remove the cmdline completion popup menu
+/// Remove the cmdline completion popup menu (if present), free the list of items.
void cmdline_pum_remove(void)
{
pum_undisplay(true);
@@ -275,7 +388,420 @@ void cmdline_pum_cleanup(CmdlineInfo *cclp)
wildmenu_cleanup(cclp);
}
-/// Do wildcard expansion on the string 'str'.
+/// Return the number of characters that should be skipped in the wildmenu
+/// These are backslashes used for escaping. Do show backslashes in help tags.
+static int skip_wildmenu_char(expand_T *xp, char *s)
+{
+ if ((rem_backslash(s) && xp->xp_context != EXPAND_HELP)
+ || ((xp->xp_context == EXPAND_MENUS || xp->xp_context == EXPAND_MENUNAMES)
+ && (s[0] == '\t' || (s[0] == '\\' && s[1] != NUL)))) {
+#ifndef BACKSLASH_IN_FILENAME
+ // TODO(bfredl): Why in the actual fuck are we special casing the
+ // shell variety deep in the redraw logic? Shell special snowflakiness
+ // should already be eliminated multiple layers before reaching the
+ // screen infracstructure.
+ if (xp->xp_shell && csh_like_shell() && s[1] == '\\' && s[2] == '!') {
+ return 2;
+ }
+#endif
+ return 1;
+ }
+ return 0;
+}
+
+/// Get the length of an item as it will be shown in the status line.
+static int wildmenu_match_len(expand_T *xp, char *s)
+{
+ int len = 0;
+
+ int emenu = (xp->xp_context == EXPAND_MENUS
+ || xp->xp_context == EXPAND_MENUNAMES);
+
+ // Check for menu separators - replace with '|'.
+ if (emenu && menu_is_separator(s)) {
+ return 1;
+ }
+
+ while (*s != NUL) {
+ s += skip_wildmenu_char(xp, s);
+ len += ptr2cells(s);
+ MB_PTR_ADV(s);
+ }
+
+ return len;
+}
+
+/// Show wildchar matches in the status line.
+/// Show at least the "match" item.
+/// We start at item "first_match" in the list and show all matches that fit.
+///
+/// If inversion is possible we use it. Else '=' characters are used.
+///
+/// @param matches list of matches
+static void redraw_wildmenu(expand_T *xp, int num_matches, char **matches, int match, int showtail)
+{
+ int row;
+ char *buf;
+ int len;
+ int clen; // length in screen cells
+ int fillchar;
+ int attr;
+ int i;
+ bool highlight = true;
+ char *selstart = NULL;
+ int selstart_col = 0;
+ char *selend = NULL;
+ static int first_match = 0;
+ bool add_left = false;
+ char *s;
+ int emenu;
+ int l;
+
+ if (matches == NULL) { // interrupted completion?
+ return;
+ }
+
+ buf = xmalloc((size_t)Columns * MB_MAXBYTES + 1);
+
+ if (match == -1) { // don't show match but original text
+ match = 0;
+ highlight = false;
+ }
+ // count 1 for the ending ">"
+ clen = wildmenu_match_len(xp, SHOW_MATCH(match)) + 3;
+ if (match == 0) {
+ first_match = 0;
+ } else if (match < first_match) {
+ // jumping left, as far as we can go
+ first_match = match;
+ add_left = true;
+ } else {
+ // check if match fits on the screen
+ for (i = first_match; i < match; i++) {
+ clen += wildmenu_match_len(xp, SHOW_MATCH(i)) + 2;
+ }
+ if (first_match > 0) {
+ clen += 2;
+ }
+ // jumping right, put match at the left
+ if ((long)clen > Columns) {
+ first_match = match;
+ // if showing the last match, we can add some on the left
+ clen = 2;
+ for (i = match; i < num_matches; i++) {
+ clen += wildmenu_match_len(xp, SHOW_MATCH(i)) + 2;
+ if ((long)clen >= Columns) {
+ break;
+ }
+ }
+ if (i == num_matches) {
+ add_left = true;
+ }
+ }
+ }
+ if (add_left) {
+ while (first_match > 0) {
+ clen += wildmenu_match_len(xp, SHOW_MATCH(first_match - 1)) + 2;
+ if ((long)clen >= Columns) {
+ break;
+ }
+ first_match--;
+ }
+ }
+
+ fillchar = fillchar_status(&attr, curwin);
+
+ if (first_match == 0) {
+ *buf = NUL;
+ len = 0;
+ } else {
+ STRCPY(buf, "< ");
+ len = 2;
+ }
+ clen = len;
+
+ i = first_match;
+ while (clen + wildmenu_match_len(xp, SHOW_MATCH(i)) + 2 < Columns) {
+ if (i == match) {
+ selstart = buf + len;
+ selstart_col = clen;
+ }
+
+ s = SHOW_MATCH(i);
+ // Check for menu separators - replace with '|'
+ emenu = (xp->xp_context == EXPAND_MENUS
+ || xp->xp_context == EXPAND_MENUNAMES);
+ if (emenu && menu_is_separator(s)) {
+ STRCPY(buf + len, transchar('|'));
+ l = (int)strlen(buf + len);
+ len += l;
+ clen += l;
+ } else {
+ for (; *s != NUL; s++) {
+ s += skip_wildmenu_char(xp, s);
+ clen += ptr2cells(s);
+ if ((l = utfc_ptr2len(s)) > 1) {
+ strncpy(buf + len, s, (size_t)l); // NOLINT(runtime/printf)
+ s += l - 1;
+ len += l;
+ } else {
+ STRCPY(buf + len, transchar_byte((uint8_t)(*s)));
+ len += (int)strlen(buf + len);
+ }
+ }
+ }
+ if (i == match) {
+ selend = buf + len;
+ }
+
+ *(buf + len++) = ' ';
+ *(buf + len++) = ' ';
+ clen += 2;
+ if (++i == num_matches) {
+ break;
+ }
+ }
+
+ if (i != num_matches) {
+ *(buf + len++) = '>';
+ clen++;
+ }
+
+ buf[len] = NUL;
+
+ row = cmdline_row - 1;
+ if (row >= 0) {
+ if (wild_menu_showing == 0 || wild_menu_showing == WM_LIST) {
+ if (msg_scrolled > 0) {
+ // Put the wildmenu just above the command line. If there is
+ // no room, scroll the screen one line up.
+ if (cmdline_row == Rows - 1) {
+ msg_scroll_up(false, false);
+ msg_scrolled++;
+ } else {
+ cmdline_row++;
+ row++;
+ }
+ wild_menu_showing = WM_SCROLLED;
+ } else {
+ // 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 && global_stl_height() == 0) {
+ save_p_ls = (int)p_ls;
+ save_p_wmh = (int)p_wmh;
+ p_ls = 2;
+ p_wmh = 0;
+ last_status(false);
+ }
+ wild_menu_showing = WM_SHOWN;
+ }
+ }
+
+ // Tricky: wildmenu can be drawn either over a status line, or at empty
+ // scrolled space in the message output
+ ScreenGrid *grid = (wild_menu_showing == WM_SCROLLED)
+ ? &msg_grid_adj : &default_grid;
+
+ grid_puts(grid, buf, row, 0, attr);
+ if (selstart != NULL && highlight) {
+ *selend = NUL;
+ grid_puts(grid, selstart, row, selstart_col, HL_ATTR(HLF_WM));
+ }
+
+ grid_fill(grid, row, row + 1, clen, Columns,
+ fillchar, fillchar, attr);
+ }
+
+ win_redraw_last_status(topframe);
+ xfree(buf);
+}
+
+/// Get the next or prev cmdline completion match. The index of the match is set
+/// in "p_findex"
+static char *get_next_or_prev_match(int mode, expand_T *xp, int *p_findex, char *orig_save)
+{
+ if (xp->xp_numfiles <= 0) {
+ return NULL;
+ }
+
+ int findex = *p_findex;
+
+ if (mode == WILD_PREV) {
+ if (findex == -1) {
+ findex = xp->xp_numfiles;
+ }
+ findex--;
+ } else if (mode == WILD_NEXT) {
+ findex++;
+ } else if (mode == WILD_PAGEUP) {
+ if (findex == 0) {
+ // at the first entry, don't select any entries
+ findex = -1;
+ } else if (findex == -1) {
+ // no entry is selected. select the last entry
+ findex = xp->xp_numfiles - 1;
+ } else {
+ // go up by the pum height
+ int ht = pum_get_height();
+ if (ht > 3) {
+ ht -= 2;
+ }
+ findex -= ht;
+ if (findex < 0) {
+ // few entries left, select the first entry
+ findex = 0;
+ }
+ }
+ } else if (mode == WILD_PAGEDOWN) {
+ if (findex == xp->xp_numfiles - 1) {
+ // at the last entry, don't select any entries
+ findex = -1;
+ } else if (findex == -1) {
+ // no entry is selected. select the first entry
+ findex = 0;
+ } else {
+ // go down by the pum height
+ int ht = pum_get_height();
+ if (ht > 3) {
+ ht -= 2;
+ }
+ findex += ht;
+ if (findex >= xp->xp_numfiles) {
+ // few entries left, select the last entry
+ findex = xp->xp_numfiles - 1;
+ }
+ }
+ } else { // mode == WILD_PUM_WANT
+ assert(pum_want.active);
+ findex = pum_want.item;
+ }
+
+ // When wrapping around, return the original string, set findex to -1.
+ if (findex < 0) {
+ if (orig_save == NULL) {
+ findex = xp->xp_numfiles - 1;
+ } else {
+ findex = -1;
+ }
+ }
+ if (findex >= xp->xp_numfiles) {
+ if (orig_save == NULL) {
+ findex = 0;
+ } else {
+ findex = -1;
+ }
+ }
+ if (compl_match_array) {
+ compl_selected = findex;
+ cmdline_pum_display(false);
+ } else if (p_wmnu) {
+ redraw_wildmenu(xp, xp->xp_numfiles, xp->xp_files, findex, cmd_showtail);
+ }
+ *p_findex = findex;
+
+ return xstrdup(findex == -1 ? orig_save : xp->xp_files[findex]);
+}
+
+/// Start the command-line expansion and get the matches.
+static char *ExpandOne_start(int mode, expand_T *xp, char *str, int options)
+{
+ int non_suf_match; // number without matching suffix
+ char *ss = NULL;
+
+ // Do the expansion.
+ if (ExpandFromContext(xp, str, &xp->xp_files, &xp->xp_numfiles, options) == FAIL) {
+#ifdef FNAME_ILLEGAL
+ // Illegal file name has been silently skipped. But when there
+ // are wildcards, the real problem is that there was no match,
+ // causing the pattern to be added, which has illegal characters.
+ if (!(options & WILD_SILENT) && (options & WILD_LIST_NOTFOUND)) {
+ semsg(_(e_nomatch2), str);
+ }
+#endif
+ } else if (xp->xp_numfiles == 0) {
+ if (!(options & WILD_SILENT)) {
+ semsg(_(e_nomatch2), str);
+ }
+ } else {
+ // Escape the matches for use on the command line.
+ ExpandEscape(xp, str, xp->xp_numfiles, xp->xp_files, options);
+
+ // Check for matching suffixes in file names.
+ if (mode != WILD_ALL && mode != WILD_ALL_KEEP && mode != WILD_LONGEST) {
+ if (xp->xp_numfiles) {
+ non_suf_match = xp->xp_numfiles;
+ } else {
+ non_suf_match = 1;
+ }
+ if ((xp->xp_context == EXPAND_FILES
+ || xp->xp_context == EXPAND_DIRECTORIES)
+ && xp->xp_numfiles > 1) {
+ // More than one match; check suffix.
+ // The files will have been sorted on matching suffix in
+ // expand_wildcards, only need to check the first two.
+ non_suf_match = 0;
+ for (int i = 0; i < 2; i++) {
+ if (match_suffix(xp->xp_files[i])) {
+ non_suf_match++;
+ }
+ }
+ }
+ if (non_suf_match != 1) {
+ // Can we ever get here unless it's while expanding
+ // interactively? If not, we can get rid of this all
+ // together. Don't really want to wait for this message
+ // (and possibly have to hit return to continue!).
+ if (!(options & WILD_SILENT)) {
+ emsg(_(e_toomany));
+ } else if (!(options & WILD_NO_BEEP)) {
+ beep_flush();
+ }
+ }
+ if (!(non_suf_match != 1 && mode == WILD_EXPAND_FREE)) {
+ ss = xstrdup(xp->xp_files[0]);
+ }
+ }
+ }
+
+ return ss;
+}
+
+/// Return the longest common part in the list of cmdline completion matches.
+static char *find_longest_match(expand_T *xp, int options)
+{
+ size_t len = 0;
+
+ for (size_t mb_len; xp->xp_files[0][len]; len += mb_len) {
+ mb_len = (size_t)utfc_ptr2len(&xp->xp_files[0][len]);
+ int c0 = utf_ptr2char(&xp->xp_files[0][len]);
+ int i;
+ for (i = 1; i < xp->xp_numfiles; i++) {
+ int ci = utf_ptr2char(&xp->xp_files[i][len]);
+
+ if (p_fic && (xp->xp_context == EXPAND_DIRECTORIES
+ || xp->xp_context == EXPAND_FILES
+ || xp->xp_context == EXPAND_SHELLCMD
+ || xp->xp_context == EXPAND_BUFFERS)) {
+ if (mb_tolower(c0) != mb_tolower(ci)) {
+ break;
+ }
+ } else if (c0 != ci) {
+ break;
+ }
+ }
+ if (i < xp->xp_numfiles) {
+ if (!(options & WILD_NO_BEEP)) {
+ vim_beep(BO_WILD);
+ }
+ break;
+ }
+ }
+
+ return xstrndup(xp->xp_files[0], len);
+}
+
+/// Do wildcard expansion on the string "str".
/// Chars that should not be expanded must be preceded with a backslash.
/// Return a pointer to allocated memory containing the new string.
/// Return NULL for failure.
@@ -295,6 +821,11 @@ void cmdline_pum_cleanup(CmdlineInfo *cclp)
/// mode = WILD_ALL: return all matches concatenated
/// mode = WILD_LONGEST: return longest matched part
/// mode = WILD_ALL_KEEP: get all matches, keep matches
+/// mode = WILD_APPLY: apply the item selected in the cmdline completion
+/// popup menu and close the menu.
+/// mode = WILD_CANCEL: cancel and close the cmdline completion popup and
+/// use the original text.
+/// mode = WILD_PUM_WANT: use the match at index pum_want.item
///
/// options = WILD_LIST_NOTFOUND: list entries without a match
/// options = WILD_HOME_REPLACE: do home_replace() for buffer names
@@ -309,63 +840,27 @@ void cmdline_pum_cleanup(CmdlineInfo *cclp)
/// The variables xp->xp_context and xp->xp_backslash must have been set!
///
/// @param orig allocated copy of original of expanded string
-char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode)
+char *ExpandOne(expand_T *xp, char *str, char *orig, int options, int mode)
{
- char_u *ss = NULL;
+ char *ss = NULL;
static int findex;
- static char_u *orig_save = NULL; // kept value of orig
+ static char *orig_save = NULL; // kept value of orig
int orig_saved = false;
int i;
- int non_suf_match; // number without matching suffix
// first handle the case of using an old match
- if (mode == WILD_NEXT || mode == WILD_PREV) {
- if (xp->xp_numfiles > 0) {
- if (mode == WILD_PREV) {
- if (findex == -1) {
- findex = xp->xp_numfiles;
- }
- findex--;
- } else { // mode == WILD_NEXT
- findex++;
- }
-
- // When wrapping around, return the original string, set findex to
- // -1.
- if (findex < 0) {
- if (orig_save == NULL) {
- findex = xp->xp_numfiles - 1;
- } else {
- findex = -1;
- }
- }
- if (findex >= xp->xp_numfiles) {
- if (orig_save == NULL) {
- findex = 0;
- } else {
- findex = -1;
- }
- }
- if (compl_match_array) {
- compl_selected = findex;
- cmdline_pum_display(false);
- } else if (p_wmnu) {
- redraw_wildmenu(xp, xp->xp_numfiles, xp->xp_files, findex, cmd_showtail);
- }
- if (findex == -1) {
- return vim_strsave(orig_save);
- }
- return vim_strsave((char_u *)xp->xp_files[findex]);
- } else {
- return NULL;
- }
+ if (mode == WILD_NEXT || mode == WILD_PREV
+ || mode == WILD_PAGEUP || mode == WILD_PAGEDOWN
+ || mode == WILD_PUM_WANT) {
+ return get_next_or_prev_match(mode, xp, &findex, orig_save);
}
if (mode == WILD_CANCEL) {
- ss = vim_strsave(orig_save ? orig_save : (char_u *)"");
+ ss = xstrdup(orig_save ? orig_save : "");
} else if (mode == WILD_APPLY) {
- ss = vim_strsave(findex == -1 ? (orig_save ? orig_save : (char_u *)"") :
- (char_u *)xp->xp_files[findex]);
+ ss = xstrdup(findex == -1
+ ? (orig_save ? orig_save : "")
+ : xp->xp_files[findex]);
}
// free old names
@@ -373,6 +868,11 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode
FreeWild(xp->xp_numfiles, xp->xp_files);
xp->xp_numfiles = -1;
XFREE_CLEAR(orig_save);
+
+ // The entries from xp_files may be used in the PUM, remove it.
+ if (compl_match_array != NULL) {
+ cmdline_pum_remove();
+ }
}
findex = 0;
@@ -385,93 +885,12 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode
orig_save = orig;
orig_saved = true;
- // Do the expansion.
- if (ExpandFromContext(xp, str, &xp->xp_numfiles, &xp->xp_files, options) == FAIL) {
-#ifdef FNAME_ILLEGAL
- // Illegal file name has been silently skipped. But when there
- // are wildcards, the real problem is that there was no match,
- // causing the pattern to be added, which has illegal characters.
- if (!(options & WILD_SILENT) && (options & WILD_LIST_NOTFOUND)) {
- semsg(_(e_nomatch2), str);
- }
-#endif
- } else if (xp->xp_numfiles == 0) {
- if (!(options & WILD_SILENT)) {
- semsg(_(e_nomatch2), str);
- }
- } else {
- // Escape the matches for use on the command line.
- ExpandEscape(xp, str, xp->xp_numfiles, xp->xp_files, options);
-
- // Check for matching suffixes in file names.
- if (mode != WILD_ALL && mode != WILD_ALL_KEEP
- && mode != WILD_LONGEST) {
- if (xp->xp_numfiles) {
- non_suf_match = xp->xp_numfiles;
- } else {
- non_suf_match = 1;
- }
- if ((xp->xp_context == EXPAND_FILES
- || xp->xp_context == EXPAND_DIRECTORIES)
- && xp->xp_numfiles > 1) {
- // More than one match; check suffix.
- // The files will have been sorted on matching suffix in
- // expand_wildcards, only need to check the first two.
- non_suf_match = 0;
- for (i = 0; i < 2; i++) {
- if (match_suffix((char_u *)xp->xp_files[i])) {
- non_suf_match++;
- }
- }
- }
- if (non_suf_match != 1) {
- // Can we ever get here unless it's while expanding
- // interactively? If not, we can get rid of this all
- // together. Don't really want to wait for this message
- // (and possibly have to hit return to continue!).
- if (!(options & WILD_SILENT)) {
- emsg(_(e_toomany));
- } else if (!(options & WILD_NO_BEEP)) {
- beep_flush();
- }
- }
- if (!(non_suf_match != 1 && mode == WILD_EXPAND_FREE)) {
- ss = vim_strsave((char_u *)xp->xp_files[0]);
- }
- }
- }
+ ss = ExpandOne_start(mode, xp, str, options);
}
// Find longest common part
if (mode == WILD_LONGEST && xp->xp_numfiles > 0) {
- size_t len = 0;
-
- for (size_t mb_len; xp->xp_files[0][len]; len += mb_len) {
- mb_len = (size_t)utfc_ptr2len(&xp->xp_files[0][len]);
- int c0 = utf_ptr2char(&xp->xp_files[0][len]);
- for (i = 1; i < xp->xp_numfiles; i++) {
- int ci = utf_ptr2char(&xp->xp_files[i][len]);
-
- if (p_fic && (xp->xp_context == EXPAND_DIRECTORIES
- || xp->xp_context == EXPAND_FILES
- || xp->xp_context == EXPAND_SHELLCMD
- || xp->xp_context == EXPAND_BUFFERS)) {
- if (mb_tolower(c0) != mb_tolower(ci)) {
- break;
- }
- } else if (c0 != ci) {
- break;
- }
- }
- if (i < xp->xp_numfiles) {
- if (!(options & WILD_NO_BEEP)) {
- vim_beep(BO_WILD);
- }
- break;
- }
- }
-
- ss = (char_u *)xstrndup(xp->xp_files[0], len);
+ ss = find_longest_match(xp, options);
findex = -1; // next p_wc gets first one
}
@@ -481,7 +900,7 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode
if (mode == WILD_ALL && xp->xp_numfiles > 0 && !got_int) {
size_t len = 0;
for (i = 0; i < xp->xp_numfiles; i++) {
- len += STRLEN(xp->xp_files[i]) + 1;
+ len += strlen(xp->xp_files[i]) + 1;
}
ss = xmalloc(len);
*ss = NUL;
@@ -523,36 +942,99 @@ void ExpandCleanup(expand_T *xp)
}
}
+/// Display one line of completion matches. Multiple matches are displayed in
+/// each line (used by wildmode=list and CTRL-D)
+///
+/// @param matches list of completion match names
+/// @param numMatches number of completion matches in "matches"
+/// @param lines number of output lines
+/// @param linenr line number of matches to display
+/// @param maxlen maximum number of characters in each line
+/// @param showtail display only the tail of the full path of a file name
+/// @param dir_attr highlight attribute to use for directory names
+static void showmatches_oneline(expand_T *xp, char **matches, int numMatches, int lines, int linenr,
+ int maxlen, int showtail, int dir_attr)
+{
+ char *p;
+ int lastlen = 999;
+ for (int j = linenr; j < numMatches; j += lines) {
+ if (xp->xp_context == EXPAND_TAGS_LISTFILES) {
+ msg_outtrans_attr(matches[j], HL_ATTR(HLF_D));
+ p = matches[j] + strlen(matches[j]) + 1;
+ msg_advance(maxlen + 1);
+ msg_puts((const char *)p);
+ msg_advance(maxlen + 3);
+ msg_outtrans_long_attr(p + 2, HL_ATTR(HLF_D));
+ break;
+ }
+ for (int i = maxlen - lastlen; --i >= 0;) {
+ msg_putchar(' ');
+ }
+ bool isdir;
+ if (xp->xp_context == EXPAND_FILES
+ || xp->xp_context == EXPAND_SHELLCMD
+ || xp->xp_context == EXPAND_BUFFERS) {
+ // highlight directories
+ if (xp->xp_numfiles != -1) {
+ // Expansion was done before and special characters
+ // were escaped, need to halve backslashes. Also
+ // $HOME has been replaced with ~/.
+ char *exp_path = expand_env_save_opt(matches[j], true);
+ char *path = exp_path != NULL ? exp_path : matches[j];
+ char *halved_slash = backslash_halve_save(path);
+ isdir = os_isdir(halved_slash);
+ xfree(exp_path);
+ if (halved_slash != path) {
+ xfree(halved_slash);
+ }
+ } else {
+ // Expansion was done here, file names are literal.
+ isdir = os_isdir(matches[j]);
+ }
+ if (showtail) {
+ p = SHOW_MATCH(j);
+ } else {
+ home_replace(NULL, matches[j], NameBuff, MAXPATHL, true);
+ p = NameBuff;
+ }
+ } else {
+ isdir = false;
+ p = SHOW_MATCH(j);
+ }
+ lastlen = msg_outtrans_attr(p, isdir ? dir_attr : 0);
+ }
+ if (msg_col > 0) { // when not wrapped around
+ msg_clr_eos();
+ msg_putchar('\n');
+ }
+}
+
/// Show all matches for completion on the command line.
/// Returns EXPAND_NOTHING when the character that triggered expansion should
/// be inserted like a normal character.
int showmatches(expand_T *xp, int wildmenu)
{
CmdlineInfo *const ccline = get_cmdline_info();
-#define L_SHOWFILE(m) (showtail \
- ? sm_gettail(files_found[m], false) : files_found[m])
- int num_files;
- char **files_found;
- int i, j, k;
+ int numMatches;
+ char **matches;
+ int i, j;
int maxlen;
int lines;
int columns;
- char_u *p;
- int lastlen;
int attr;
int showtail;
if (xp->xp_numfiles == -1) {
set_expand_context(xp);
i = expand_cmdline(xp, ccline->cmdbuff, ccline->cmdpos,
- &num_files, &files_found);
+ &numMatches, &matches);
showtail = expand_showtail(xp);
if (i != EXPAND_OK) {
return i;
}
} else {
- num_files = xp->xp_numfiles;
- files_found = xp->xp_files;
+ numMatches = xp->xp_numfiles;
+ matches = xp->xp_files;
showtail = cmd_showtail;
}
@@ -562,26 +1044,8 @@ int showmatches(expand_T *xp, int wildmenu)
|| ui_has(kUIWildmenu);
if (compl_use_pum) {
- assert(num_files >= 0);
- compl_match_arraysize = num_files;
- compl_match_array = xmalloc(sizeof(pumitem_T) * (size_t)compl_match_arraysize);
- for (i = 0; i < num_files; i++) {
- compl_match_array[i] = (pumitem_T){
- .pum_text = (char_u *)L_SHOWFILE(i),
- .pum_info = NULL,
- .pum_extra = NULL,
- .pum_kind = NULL,
- };
- }
- char_u *endpos = (char_u *)(showtail ? sm_gettail(xp->xp_pattern, true) : xp->xp_pattern);
- if (ui_has(kUICmdline)) {
- compl_startcol = (int)(endpos - ccline->cmdbuff);
- } else {
- compl_startcol = cmd_screencol((int)(endpos - ccline->cmdbuff));
- }
- compl_selected = -1;
- cmdline_pum_display(true);
- return EXPAND_OK;
+ // cmdline completion popup menu (with wildoptions=pum)
+ return cmdline_pum_create(ccline, xp, matches, numMatches, showtail);
}
if (!wildmenu) {
@@ -597,18 +1061,18 @@ int showmatches(expand_T *xp, int wildmenu)
if (got_int) {
got_int = false; // only int. the completion, not the cmd line
} else if (wildmenu) {
- redraw_wildmenu(xp, num_files, files_found, -1, showtail);
+ redraw_wildmenu(xp, numMatches, matches, -1, showtail);
} else {
// find the length of the longest file name
maxlen = 0;
- for (i = 0; i < num_files; i++) {
+ for (i = 0; i < numMatches; i++) {
if (!showtail && (xp->xp_context == EXPAND_FILES
|| xp->xp_context == EXPAND_SHELLCMD
|| xp->xp_context == EXPAND_BUFFERS)) {
- home_replace(NULL, files_found[i], (char *)NameBuff, MAXPATHL, true);
- j = vim_strsize((char *)NameBuff);
+ home_replace(NULL, matches[i], NameBuff, MAXPATHL, true);
+ j = vim_strsize(NameBuff);
} else {
- j = vim_strsize(L_SHOWFILE(i));
+ j = vim_strsize(SHOW_MATCH(i));
}
if (j > maxlen) {
maxlen = j;
@@ -616,7 +1080,7 @@ int showmatches(expand_T *xp, int wildmenu)
}
if (xp->xp_context == EXPAND_TAGS_LISTFILES) {
- lines = num_files;
+ lines = numMatches;
} else {
// compute the number of columns and lines for the listing
maxlen += 2; // two spaces between file names
@@ -624,7 +1088,7 @@ int showmatches(expand_T *xp, int wildmenu)
if (columns < 1) {
columns = 1;
}
- lines = (num_files + columns - 1) / columns;
+ lines = (numMatches + columns - 1) / columns;
}
attr = HL_ATTR(HLF_D); // find out highlighting for directories
@@ -638,57 +1102,7 @@ int showmatches(expand_T *xp, int wildmenu)
// list the files line by line
for (i = 0; i < lines; i++) {
- lastlen = 999;
- for (k = i; k < num_files; k += lines) {
- if (xp->xp_context == EXPAND_TAGS_LISTFILES) {
- msg_outtrans_attr((char_u *)files_found[k], HL_ATTR(HLF_D));
- p = (char_u *)files_found[k] + STRLEN(files_found[k]) + 1;
- msg_advance(maxlen + 1);
- msg_puts((const char *)p);
- msg_advance(maxlen + 3);
- msg_outtrans_long_attr(p + 2, HL_ATTR(HLF_D));
- break;
- }
- for (j = maxlen - lastlen; --j >= 0;) {
- msg_putchar(' ');
- }
- if (xp->xp_context == EXPAND_FILES
- || xp->xp_context == EXPAND_SHELLCMD
- || xp->xp_context == EXPAND_BUFFERS) {
- // highlight directories
- if (xp->xp_numfiles != -1) {
- // Expansion was done before and special characters
- // were escaped, need to halve backslashes. Also
- // $HOME has been replaced with ~/.
- char_u *exp_path = expand_env_save_opt((char_u *)files_found[k], true);
- char_u *path = exp_path != NULL ? exp_path : (char_u *)files_found[k];
- char_u *halved_slash = backslash_halve_save(path);
- j = os_isdir(halved_slash);
- xfree(exp_path);
- if (halved_slash != path) {
- xfree(halved_slash);
- }
- } else {
- // Expansion was done here, file names are literal.
- j = os_isdir((char_u *)files_found[k]);
- }
- if (showtail) {
- p = (char_u *)L_SHOWFILE(k);
- } else {
- home_replace(NULL, files_found[k], (char *)NameBuff, MAXPATHL, true);
- p = NameBuff;
- }
- } else {
- j = false;
- p = (char_u *)L_SHOWFILE(k);
- }
- lastlen = msg_outtrans_attr(p, j ? attr : 0);
- }
- if (msg_col > 0) { // when not wrapped around
- msg_clr_eos();
- msg_putchar('\n');
- }
- ui_flush(); // show one line at a time
+ showmatches_oneline(xp, matches, numMatches, lines, i, maxlen, showtail, attr);
if (got_int) {
got_int = false;
break;
@@ -701,21 +1115,21 @@ int showmatches(expand_T *xp, int wildmenu)
}
if (xp->xp_numfiles == -1) {
- FreeWild(num_files, files_found);
+ FreeWild(numMatches, matches);
}
return EXPAND_OK;
}
-/// Private path_tail for showmatches() (and redraw_wildmenu()):
-/// Find tail of file name path, but ignore trailing "/".
-char *sm_gettail(char *s, bool eager)
+/// path_tail() version for showmatches() and redraw_wildmenu():
+/// Return the tail of file name path "s", ignoring a trailing "/".
+static char *showmatches_gettail(char *s, bool eager)
{
- char_u *p;
- char_u *t = (char_u *)s;
+ char *p;
+ char *t = s;
bool had_sep = false;
- for (p = (char_u *)s; *p != NUL;) {
+ for (p = s; *p != NUL;) {
if (vim_ispathsep(*p)
#ifdef BACKSLASH_IN_FILENAME
&& !rem_backslash(p)
@@ -732,7 +1146,7 @@ char *sm_gettail(char *s, bool eager)
}
MB_PTR_ADV(p);
}
- return (char *)t;
+ return t;
}
/// Return true if we only need to show the tail of completion matches.
@@ -740,8 +1154,8 @@ char *sm_gettail(char *s, bool eager)
/// returned.
static bool expand_showtail(expand_T *xp)
{
- char_u *s;
- char_u *end;
+ char *s;
+ char *end;
// When not completing file names a "/" may mean something different.
if (xp->xp_context != EXPAND_FILES
@@ -750,17 +1164,17 @@ static bool expand_showtail(expand_T *xp)
return false;
}
- end = (char_u *)path_tail(xp->xp_pattern);
- if (end == (char_u *)xp->xp_pattern) { // there is no path separator
+ end = path_tail(xp->xp_pattern);
+ if (end == xp->xp_pattern) { // there is no path separator
return false;
}
- for (s = (char_u *)xp->xp_pattern; s < end; s++) {
+ 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.
if (rem_backslash(s)) {
s++;
- } else if (vim_strchr("*?[", *s) != NULL) {
+ } else if (vim_strchr("*?[", (uint8_t)(*s)) != NULL) {
return false;
}
}
@@ -775,13 +1189,13 @@ static bool expand_showtail(expand_T *xp)
/// the name into allocated memory and prepend "^".
///
/// @param context EXPAND_FILES etc.
-char_u *addstar(char_u *fname, size_t len, int context)
+char *addstar(char *fname, size_t len, int context)
FUNC_ATTR_NONNULL_RET
{
- char_u *retval;
+ char *retval;
size_t i, j;
size_t new_len;
- char_u *tail;
+ char *tail;
int ends_in_star;
if (context != EXPAND_FILES
@@ -803,7 +1217,7 @@ char_u *addstar(char_u *fname, size_t len, int context)
|| context == EXPAND_PACKADD
|| ((context == EXPAND_TAGS_LISTFILES || context == EXPAND_TAGS)
&& fname[0] == '/')) {
- retval = vim_strnsave(fname, len);
+ retval = xstrnsave(fname, len);
} else {
new_len = len + 2; // +2 for '^' at start, NUL at end
for (i = 0; i < len; i++) {
@@ -865,7 +1279,7 @@ char_u *addstar(char_u *fname, size_t len, int context)
}
} else {
retval = xmalloc(len + 4);
- STRLCPY(retval, fname, len + 1);
+ xstrlcpy(retval, fname, len + 1);
// Don't add a star to *, ~, ~user, $var or `cmd`.
// * would become **, which walks the whole tree.
@@ -873,7 +1287,7 @@ char_u *addstar(char_u *fname, size_t len, int context)
// $ could be anywhere in the tail.
// ` could be anywhere in the file name.
// When the name ends in '$' don't add a star, remove the '$'.
- tail = (char_u *)path_tail((char *)retval);
+ tail = path_tail(retval);
ends_in_star = (len > 0 && retval[len - 1] == '*');
#ifndef BACKSLASH_IN_FILENAME
for (ssize_t k = (ssize_t)len - 2; k >= 0; k--) {
@@ -885,8 +1299,8 @@ char_u *addstar(char_u *fname, size_t len, int context)
#endif
if ((*retval != '~' || tail != retval)
&& !ends_in_star
- && vim_strchr((char *)tail, '$') == NULL
- && vim_strchr((char *)retval, '`') == NULL) {
+ && vim_strchr(tail, '$') == NULL
+ && vim_strchr(retval, '`') == NULL) {
retval[len++] = '*';
} else if (len > 0 && retval[len - 1] == '$') {
len--;
@@ -951,65 +1365,26 @@ void set_expand_context(expand_T *xp)
set_cmd_context(xp, ccline->cmdbuff, ccline->cmdlen, ccline->cmdpos, true);
}
-/// This is all pretty much copied from do_one_cmd(), with all the extra stuff
-/// we don't need/want deleted. Maybe this could be done better if we didn't
-/// repeat all this stuff. The only problem is that they may not stay
-/// perfectly compatible with each other, but then the command line syntax
-/// probably won't change that much -- webb.
+/// Sets the index of a built-in or user defined command "cmd" in eap->cmdidx.
+/// For user defined commands, the completion context is set in "xp" and the
+/// completion flags in "complp".
///
-/// @param buff buffer for command string
-static const char *set_one_cmd_context(expand_T *xp, const char *buff)
+/// @return a pointer to the text after the command or NULL for failure.
+static const char *set_cmd_index(const char *cmd, exarg_T *eap, expand_T *xp, int *complp)
{
+ const char *p = NULL;
size_t len = 0;
- exarg_T ea;
- int context = EXPAND_NOTHING;
- bool forceit = false;
- bool usefilter = false; // Filter instead of file name.
-
- ExpandInit(xp);
- 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(" \t:|", *cmd) != NULL; cmd++) {}
- xp->xp_pattern = (char *)cmd;
-
- if (*cmd == NUL) {
- return NULL;
- }
- if (*cmd == '"') { // ignore comment lines
- xp->xp_context = EXPAND_NOTHING;
- return NULL;
- }
-
- // 3. parse a range specifier of the form: addr [,addr] [;addr] ..
- cmd = (const char *)skip_range(cmd, &xp->xp_context);
-
- // 4. parse command
- xp->xp_pattern = (char *)cmd;
- if (*cmd == NUL) {
- return NULL;
- }
- if (*cmd == '"') {
- xp->xp_context = EXPAND_NOTHING;
- return NULL;
- }
-
- if (*cmd == '|' || *cmd == '\n') {
- return cmd + 1; // There's another command
- }
+ const bool fuzzy = cmdline_fuzzy_complete(cmd);
// Isolate the command and search for it in the command table.
// Exceptions:
- // - the 'k' command can directly be followed by any character, but
- // do accept "keepmarks", "keepalt" and "keepjumps".
+ // - the 'k' command can directly be followed by any character, but do
+ // accept "keepmarks", "keepalt" and "keepjumps". As fuzzy matching can
+ // find matches anywhere in the command name, do this only for command
+ // expansion based on regular expression and not for fuzzy matching.
// - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r'
- const char *p;
- if (*cmd == 'k' && cmd[1] != 'e') {
- ea.cmdidx = CMD_k;
+ if (!fuzzy && (*cmd == 'k' && cmd[1] != 'e')) {
+ eap->cmdidx = CMD_k;
p = cmd + 1;
} else {
p = cmd;
@@ -1030,7 +1405,7 @@ static const char *set_one_cmd_context(expand_T *xp, const char *buff)
}
}
// check for non-alpha command
- if (p == cmd && vim_strchr("@*!=><&~#", *p) != NULL) {
+ if (p == cmd && vim_strchr("@*!=><&~#", (uint8_t)(*p)) != NULL) {
p++;
}
len = (size_t)(p - cmd);
@@ -1040,9 +1415,13 @@ static const char *set_one_cmd_context(expand_T *xp, const char *buff)
return NULL;
}
- ea.cmdidx = excmd_get_cmdidx(cmd, len);
+ eap->cmdidx = excmd_get_cmdidx(cmd, len);
- if (cmd[0] >= 'A' && cmd[0] <= 'Z') {
+ // User defined commands support alphanumeric characters.
+ // Also when doing fuzzy expansion for non-shell commands, support
+ // alphanumeric characters.
+ if ((cmd[0] >= 'A' && cmd[0] <= 'Z')
+ || (fuzzy && eap->cmdidx != CMD_bang && *p != NUL)) {
while (ASCII_ISALNUM(*p) || *p == '*') { // Allow * wild card
p++;
}
@@ -1055,227 +1434,358 @@ static const char *set_one_cmd_context(expand_T *xp, const char *buff)
return NULL;
}
- if (ea.cmdidx == CMD_SIZE) {
- if (*cmd == 's' && vim_strchr("cgriI", cmd[1]) != NULL) {
- ea.cmdidx = CMD_substitute;
+ if (eap->cmdidx == CMD_SIZE) {
+ if (*cmd == 's' && vim_strchr("cgriI", (uint8_t)cmd[1]) != NULL) {
+ eap->cmdidx = CMD_substitute;
p = cmd + 1;
} else if (cmd[0] >= 'A' && cmd[0] <= 'Z') {
- ea.cmd = (char *)cmd;
- p = (const char *)find_ucmd(&ea, (char *)p, NULL, xp, &context);
+ eap->cmd = (char *)cmd;
+ p = (const char *)find_ucmd(eap, (char *)p, NULL, xp, complp);
if (p == NULL) {
- ea.cmdidx = CMD_SIZE; // Ambiguous user command.
+ eap->cmdidx = CMD_SIZE; // Ambiguous user command.
}
}
}
- if (ea.cmdidx == CMD_SIZE) {
+ if (eap->cmdidx == CMD_SIZE) {
// Not still touching the command and it was an illegal one
xp->xp_context = EXPAND_UNSUCCESSFUL;
return NULL;
}
- xp->xp_context = EXPAND_NOTHING; // Default now that we're past command
+ return p;
+}
- if (*p == '!') { // forced commands
- forceit = true;
- p++;
- }
+/// Set the completion context for a command argument with wild card characters.
+static void set_context_for_wildcard_arg(exarg_T *eap, const char *arg, bool usefilter,
+ expand_T *xp, int *complp)
+{
+ bool in_quote = false;
+ const char *bow = NULL; // Beginning of word.
+ size_t len = 0;
- // 5. parse arguments
- if (!IS_USER_CMDIDX(ea.cmdidx)) {
- ea.argt = excmd_get_argt(ea.cmdidx);
+ // Allow spaces within back-quotes to count as part of the argument
+ // being expanded.
+ xp->xp_pattern = skipwhite(arg);
+ const char *p = (const char *)xp->xp_pattern;
+ while (*p != NUL) {
+ int c = utf_ptr2char(p);
+ if (c == '\\' && p[1] != NUL) {
+ p++;
+ } else if (c == '`') {
+ if (!in_quote) {
+ xp->xp_pattern = (char *)p;
+ bow = p + 1;
+ }
+ in_quote = !in_quote;
+ // An argument can contain just about everything, except
+ // characters that end the command and white space.
+ } else if (c == '|' || c == '\n' || c == '"' || ascii_iswhite(c)) {
+ len = 0; // avoid getting stuck when space is in 'isfname'
+ while (*p != NUL) {
+ c = utf_ptr2char(p);
+ if (c == '`' || vim_isfilec_or_wc(c)) {
+ break;
+ }
+ len = (size_t)utfc_ptr2len(p);
+ MB_PTR_ADV(p);
+ }
+ if (in_quote) {
+ bow = p;
+ } else {
+ xp->xp_pattern = (char *)p;
+ }
+ p -= len;
+ }
+ MB_PTR_ADV(p);
}
- const char *arg = (const char *)skipwhite(p);
+ // If we are still inside the quotes, and we passed a space, just
+ // expand from there.
+ if (bow != NULL && in_quote) {
+ xp->xp_pattern = (char *)bow;
+ }
+ xp->xp_context = EXPAND_FILES;
- // Skip over ++argopt argument
- if ((ea.argt & EX_ARGOPT) && *arg != NUL && strncmp(arg, "++", 2) == 0) {
- p = arg;
- while (*p && !ascii_isspace(*p)) {
- MB_PTR_ADV(p);
+ // For a shell command more chars need to be escaped.
+ if (usefilter || eap->cmdidx == CMD_bang || eap->cmdidx == CMD_terminal) {
+#ifndef BACKSLASH_IN_FILENAME
+ xp->xp_shell = true;
+#endif
+ // When still after the command name expand executables.
+ if (xp->xp_pattern == skipwhite(arg)) {
+ xp->xp_context = EXPAND_SHELLCMD;
}
- arg = (const char *)skipwhite(p);
}
- if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update) {
- if (*arg == '>') { // Append.
- if (*++arg == '>') {
- arg++;
+ // Check for environment variable.
+ if (*xp->xp_pattern == '$') {
+ for (p = (const char *)xp->xp_pattern + 1; *p != NUL; p++) {
+ if (!vim_isIDc((uint8_t)(*p))) {
+ break;
}
- arg = (const char *)skipwhite(arg);
- } else if (*arg == '!' && ea.cmdidx == CMD_write) { // :w !filter
- arg++;
- usefilter = true;
}
- }
-
- if (ea.cmdidx == CMD_read) {
- usefilter = forceit; // :r! filter if forced
- if (*arg == '!') { // :r !filter
- arg++;
- usefilter = true;
+ if (*p == NUL) {
+ xp->xp_context = EXPAND_ENV_VARS;
+ xp->xp_pattern++;
+ // Avoid that the assignment uses EXPAND_FILES again.
+ if (*complp != EXPAND_USER_DEFINED && *complp != EXPAND_USER_LIST) {
+ *complp = EXPAND_ENV_VARS;
+ }
}
}
-
- if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift) {
- while (*arg == *cmd) { // allow any number of '>' or '<'
- arg++;
+ // Check for user names.
+ if (*xp->xp_pattern == '~') {
+ 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) {
+ xp->xp_context = EXPAND_USER;
+ xp->xp_pattern++;
}
- 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 *)arg, false);
+/// Set the completion context for the :filter command. Returns a pointer to the
+/// next command after the :filter command.
+static const char *set_context_in_filter_cmd(expand_T *xp, const char *arg)
+{
+ if (*arg != NUL) {
+ 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(arg);
+}
- // Still touching the command after '+'?
- if (*arg == NUL) {
- return p;
+/// Set the completion context for the :match command. Returns a pointer to the
+/// next command after the :match command.
+static const char *set_context_in_match_cmd(expand_T *xp, const char *arg)
+{
+ if (*arg == NUL || !ends_excmd(*arg)) {
+ // also complete "None"
+ set_context_in_echohl_cmd(xp, arg);
+ arg = (const char *)skipwhite(skiptowhite(arg));
+ if (*arg != NUL) {
+ xp->xp_context = EXPAND_NOTHING;
+ arg = (const char *)skip_regexp((char *)arg + 1, (uint8_t)(*arg), magic_isset());
}
+ }
+ return (const char *)find_nextcmd(arg);
+}
- // Skip space(s) after +command to get to the real argument.
- arg = (const char *)skipwhite(arg);
+/// Returns a pointer to the next command after a :global or a :v command.
+/// Returns NULL if there is no next command.
+static const char *find_cmd_after_global_cmd(const char *arg)
+{
+ const int delim = (uint8_t)(*arg); // Get the delimiter.
+ if (delim) {
+ arg++; // Skip delimiter if there is one.
}
- // Check for '|' to separate commands and '"' to start comments.
- // Don't do this for ":read !cmd" and ":write !cmd".
- if ((ea.argt & EX_TRLBAR) && !usefilter) {
- p = arg;
- // ":redir @" is not the start of a comment
- if (ea.cmdidx == CMD_redir && p[0] == '@' && p[1] == '"') {
- p += 2;
+ while (arg[0] != NUL && (uint8_t)arg[0] != delim) {
+ if (arg[0] == '\\' && arg[1] != NUL) {
+ arg++;
}
- while (*p) {
- if (*p == Ctrl_V) {
- if (p[1] != NUL) {
- p++;
- }
- } else if ((*p == '"' && !(ea.argt & EX_NOTRLCOM))
- || *p == '|'
- || *p == '\n') {
- if (*(p - 1) != '\\') {
- if (*p == '|' || *p == '\n') {
- return p + 1;
- }
- return NULL; // It's a comment
+ arg++;
+ }
+ if (arg[0] != NUL) {
+ return arg + 1;
+ }
+
+ return NULL;
+}
+
+/// Returns a pointer to the next command after a :substitute or a :& command.
+/// Returns NULL if there is no next command.
+static const char *find_cmd_after_substitute_cmd(const char *arg)
+{
+ const int delim = (uint8_t)(*arg);
+ if (delim) {
+ // Skip "from" part.
+ arg++;
+ arg = (const char *)skip_regexp((char *)arg, delim, magic_isset());
+
+ if (arg[0] != NUL && arg[0] == delim) {
+ // Skip "to" part.
+ arg++;
+ while (arg[0] != NUL && (uint8_t)arg[0] != delim) {
+ if (arg[0] == '\\' && arg[1] != NUL) {
+ arg++;
}
+ arg++;
+ }
+ if (arg[0] != NUL) { // Skip delimiter.
+ arg++;
}
- MB_PTR_ADV(p);
}
}
+ while (arg[0] && strchr("|\"#", arg[0]) == NULL) {
+ arg++;
+ }
+ if (arg[0] != NUL) {
+ return arg;
+ }
- if (!(ea.argt & EX_EXTRA) && *arg != NUL && strchr("|\"", *arg) == NULL) {
- // no arguments allowed but there is something
+ return NULL;
+}
+
+/// Returns a pointer to the next command after a :isearch/:dsearch/:ilist
+/// :dlist/:ijump/:psearch/:djump/:isplit/:dsplit command.
+/// Returns NULL if there is no next command.
+static const char *find_cmd_after_isearch_cmd(expand_T *xp, const char *arg)
+{
+ // Skip count.
+ arg = (const char *)skipwhite(skipdigits(arg));
+ if (*arg != '/') {
return NULL;
}
- // Find start of last argument (argument just before cursor):
- p = buff;
- 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 *)++p;
- } else {
- if (*p == '\\' && *(p + 1) != NUL) {
- p++; // skip over escaped character
- }
- MB_PTR_ADV(p);
+ // Match regexp, not just whole words.
+ for (++arg; *arg && *arg != '/'; arg++) {
+ if (*arg == '\\' && arg[1] != NUL) {
+ arg++;
}
}
+ if (*arg) {
+ arg = (const char *)skipwhite(arg + 1);
- if (ea.argt & EX_XFILE) {
- int in_quote = false;
- const char *bow = NULL; // Beginning of word.
-
- // Allow spaces within back-quotes to count as part of the argument
- // being expanded.
- xp->xp_pattern = skipwhite(arg);
- p = (const char *)xp->xp_pattern;
- while (*p != NUL) {
- int c = utf_ptr2char(p);
- if (c == '\\' && p[1] != NUL) {
- p++;
- } else if (c == '`') {
- if (!in_quote) {
- xp->xp_pattern = (char *)p;
- bow = p + 1;
- }
- in_quote = !in_quote;
- // An argument can contain just about everything, except
- // characters that end the command and white space.
- } else if (c == '|' || c == '\n' || c == '"' || ascii_iswhite(c)) {
- len = 0; // avoid getting stuck when space is in 'isfname'
- while (*p != NUL) {
- c = utf_ptr2char(p);
- if (c == '`' || vim_isfilec_or_wc(c)) {
- break;
- }
- len = (size_t)utfc_ptr2len(p);
- MB_PTR_ADV(p);
- }
- if (in_quote) {
- bow = p;
- } else {
- xp->xp_pattern = (char *)p;
- }
- p -= len;
- }
- MB_PTR_ADV(p);
+ // Check for trailing illegal characters.
+ if (*arg == NUL || strchr("|\"\n", *arg) == NULL) {
+ xp->xp_context = EXPAND_NOTHING;
+ } else {
+ return arg;
}
+ }
- // If we are still inside the quotes, and we passed a space, just
- // expand from there.
- if (bow != NULL && in_quote) {
- xp->xp_pattern = (char *)bow;
- }
- xp->xp_context = EXPAND_FILES;
+ return NULL;
+}
- // For a shell command more chars need to be escaped.
- if (usefilter || ea.cmdidx == CMD_bang || ea.cmdidx == CMD_terminal) {
-#ifndef BACKSLASH_IN_FILENAME
- xp->xp_shell = true;
-#endif
- // When still after the command name expand executables.
- if (xp->xp_pattern == skipwhite(arg)) {
- xp->xp_context = EXPAND_SHELLCMD;
- }
+/// Set the completion context for the :unlet command. Always returns NULL.
+static const char *set_context_in_unlet_cmd(expand_T *xp, const char *arg)
+{
+ while ((xp->xp_pattern = strchr(arg, ' ')) != NULL) {
+ arg = (const char *)xp->xp_pattern + 1;
+ }
+
+ xp->xp_context = EXPAND_USER_VARS;
+ xp->xp_pattern = (char *)arg;
+
+ if (*xp->xp_pattern == '$') {
+ xp->xp_context = EXPAND_ENV_VARS;
+ xp->xp_pattern++;
+ }
+
+ return NULL;
+}
+
+/// Set the completion context for the :language command. Always returns NULL.
+static const char *set_context_in_lang_cmd(expand_T *xp, const char *arg)
+{
+ const char *p = (const char *)skiptowhite(arg);
+ if (*p == NUL) {
+ xp->xp_context = EXPAND_LANGUAGE;
+ xp->xp_pattern = (char *)arg;
+ } else {
+ 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(p);
+ } else {
+ xp->xp_context = EXPAND_NOTHING;
}
+ }
- // Check for environment variable.
- if (*xp->xp_pattern == '$') {
- for (p = (const char *)xp->xp_pattern + 1; *p != NUL; p++) {
- if (!vim_isIDc((uint8_t)(*p))) {
- break;
- }
- }
- if (*p == NUL) {
- xp->xp_context = EXPAND_ENV_VARS;
- xp->xp_pattern++;
- // Avoid that the assignment uses EXPAND_FILES again.
- if (context != EXPAND_USER_DEFINED && context != EXPAND_USER_LIST) {
- context = EXPAND_ENV_VARS;
- }
+ return NULL;
+}
+
+static enum {
+ EXP_BREAKPT_ADD, ///< expand ":breakadd" sub-commands
+ EXP_BREAKPT_DEL, ///< expand ":breakdel" sub-commands
+ EXP_PROFDEL, ///< expand ":profdel" sub-commands
+} breakpt_expand_what;
+
+/// Set the completion context for the :breakadd command. Always returns NULL.
+static const char *set_context_in_breakadd_cmd(expand_T *xp, const char *arg, cmdidx_T cmdidx)
+{
+ xp->xp_context = EXPAND_BREAKPOINT;
+ xp->xp_pattern = (char *)arg;
+
+ if (cmdidx == CMD_breakadd) {
+ breakpt_expand_what = EXP_BREAKPT_ADD;
+ } else if (cmdidx == CMD_breakdel) {
+ breakpt_expand_what = EXP_BREAKPT_DEL;
+ } else {
+ breakpt_expand_what = EXP_PROFDEL;
+ }
+
+ const char *p = skipwhite(arg);
+ if (*p == NUL) {
+ return NULL;
+ }
+ const char *subcmd_start = p;
+
+ if (strncmp("file ", p, 5) == 0 || strncmp("func ", p, 5) == 0) {
+ // :breakadd file [lnum] <filename>
+ // :breakadd func [lnum] <funcname>
+ p += 4;
+ p = skipwhite(p);
+
+ // skip line number (if specified)
+ if (ascii_isdigit(*p)) {
+ p = skipdigits(p);
+ if (*p != ' ') {
+ xp->xp_context = EXPAND_NOTHING;
+ return NULL;
}
+ p = skipwhite(p);
}
- // Check for user names.
- if (*xp->xp_pattern == '~') {
- 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((char_u *)xp->xp_pattern + 1) >= 1) {
- xp->xp_context = EXPAND_USER;
- xp->xp_pattern++;
- }
+ if (strncmp("file", subcmd_start, 4) == 0) {
+ xp->xp_context = EXPAND_FILES;
+ } else {
+ xp->xp_context = EXPAND_USER_FUNC;
}
+ xp->xp_pattern = (char *)p;
+ } else if (strncmp("expr ", p, 5) == 0) {
+ // :breakadd expr <expression>
+ xp->xp_context = EXPAND_EXPRESSION;
+ xp->xp_pattern = skipwhite(p + 5);
+ }
+
+ return NULL;
+}
+
+static const char *set_context_in_scriptnames_cmd(expand_T *xp, const char *arg)
+{
+ xp->xp_context = EXPAND_NOTHING;
+ xp->xp_pattern = NULL;
+
+ char *p = skipwhite(arg);
+ if (ascii_isdigit(*p)) {
+ return NULL;
}
- // 6. switch on command name
- switch (ea.cmdidx) {
+ xp->xp_context = EXPAND_SCRIPTNAMES;
+ xp->xp_pattern = p;
+
+ return NULL;
+}
+
+/// Set the completion context in "xp" for command "cmd" with index "cmdidx".
+/// The argument to the command is "arg" and the argument flags is "argt".
+/// For user-defined commands and for environment variables, "context" has the
+/// completion type.
+///
+/// @return a pointer to the next command, or NULL if there is no next command.
+static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, expand_T *xp,
+ const char *arg, uint32_t argt, int context, bool forceit)
+{
+ switch (cmdidx) {
case CMD_find:
case CMD_sfind:
case CMD_tabfind:
@@ -1313,6 +1823,7 @@ static const char *set_one_cmd_context(expand_T *xp, const char *buff)
case CMD_folddoclosed:
case CMD_folddoopen:
case CMD_hide:
+ case CMD_horizontal:
case CMD_keepalt:
case CMD_keepjumps:
case CMD_keepmarks:
@@ -1335,27 +1846,10 @@ static const char *set_one_cmd_context(expand_T *xp, const char *buff)
return arg;
case CMD_filter:
- if (*arg != NUL) {
- 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(arg);
+ return set_context_in_filter_cmd(xp, arg);
case CMD_match:
- if (*arg == NUL || !ends_excmd(*arg)) {
- // also complete "None"
- set_context_in_echohl_cmd(xp, 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),
- p_magic, NULL);
- }
- }
- return (const char *)find_nextcmd((char_u *)arg);
+ return set_context_in_match_cmd(xp, arg);
// All completion for the +cmdline_compl feature goes here.
@@ -1368,49 +1862,11 @@ static const char *set_one_cmd_context(expand_T *xp, const char *buff)
break;
case CMD_global:
- case CMD_vglobal: {
- const int delim = (uint8_t)(*arg); // Get the delimiter.
- if (delim) {
- arg++; // Skip delimiter if there is one.
- }
-
- while (arg[0] != NUL && (uint8_t)arg[0] != delim) {
- if (arg[0] == '\\' && arg[1] != NUL) {
- arg++;
- }
- arg++;
- }
- if (arg[0] != NUL) {
- return arg + 1;
- }
- break;
- }
+ case CMD_vglobal:
+ return find_cmd_after_global_cmd(arg);
case CMD_and:
- case CMD_substitute: {
- const int delim = (uint8_t)(*arg);
- if (delim) {
- // Skip "from" part.
- arg++;
- arg = (const char *)skip_regexp((char_u *)arg, delim, p_magic, NULL);
- }
- // Skip "to" part.
- while (arg[0] != NUL && (uint8_t)arg[0] != delim) {
- if (arg[0] == '\\' && arg[1] != NUL) {
- arg++;
- }
- arg++;
- }
- if (arg[0] != NUL) { // Skip delimiter.
- arg++;
- }
- while (arg[0] && strchr("|\"#", arg[0]) == NULL) {
- arg++;
- }
- if (arg[0] != NUL) {
- return arg;
- }
- break;
- }
+ case CMD_substitute:
+ return find_cmd_after_substitute_cmd(arg);
case CMD_isearch:
case CMD_dsearch:
case CMD_ilist:
@@ -1420,26 +1876,7 @@ static const char *set_one_cmd_context(expand_T *xp, const char *buff)
case CMD_djump:
case CMD_isplit:
case CMD_dsplit:
- // Skip count.
- arg = (const char *)skipwhite(skipdigits(arg));
- if (*arg == '/') { // Match regexp, not just whole words.
- for (++arg; *arg && *arg != '/'; arg++) {
- if (*arg == '\\' && arg[1] != NUL) {
- arg++;
- }
- }
- if (*arg) {
- arg = (const char *)skipwhite(arg + 1);
-
- // Check for trailing illegal characters.
- if (*arg && strchr("|\"\n", *arg) == NULL) {
- xp->xp_context = EXPAND_NOTHING;
- } else {
- return arg;
- }
- }
- }
- break;
+ return find_cmd_after_isearch_cmd(xp, arg);
case CMD_autocmd:
return (const char *)set_context_in_autocmd(xp, (char *)arg, false);
@@ -1447,13 +1884,13 @@ static const char *set_one_cmd_context(expand_T *xp, const char *buff)
case CMD_doautoall:
return (const char *)set_context_in_autocmd(xp, (char *)arg, true);
case CMD_set:
- set_context_in_set_cmd(xp, (char_u *)arg, 0);
+ set_context_in_set_cmd(xp, (char *)arg, 0);
break;
case CMD_setglobal:
- set_context_in_set_cmd(xp, (char_u *)arg, OPT_GLOBAL);
+ set_context_in_set_cmd(xp, (char *)arg, OPT_GLOBAL);
break;
case CMD_setlocal:
- set_context_in_set_cmd(xp, (char_u *)arg, OPT_LOCAL);
+ set_context_in_set_cmd(xp, (char *)arg, OPT_LOCAL);
break;
case CMD_tag:
case CMD_stag:
@@ -1498,24 +1935,11 @@ static 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 *)arg, ea.cmdidx);
+ set_context_for_expression(xp, (char *)arg, cmdidx);
break;
case CMD_unlet:
- while ((xp->xp_pattern = strchr(arg, ' ')) != NULL) {
- arg = (const char *)xp->xp_pattern + 1;
- }
-
- xp->xp_context = EXPAND_USER_VARS;
- xp->xp_pattern = (char *)arg;
-
- if (*xp->xp_pattern == '$') {
- xp->xp_context = EXPAND_ENV_VARS;
- xp->xp_pattern++;
- }
-
- break;
-
+ return set_context_in_unlet_cmd(xp, arg);
case CMD_function:
case CMD_delfunction:
xp->xp_context = EXPAND_USER_FUNC;
@@ -1528,13 +1952,8 @@ static const char *set_one_cmd_context(expand_T *xp, const char *buff)
case CMD_highlight:
set_context_in_highlight_cmd(xp, arg);
break;
- case CMD_cscope:
- case CMD_lcscope:
- case CMD_scscope:
- set_context_in_cscope_cmd(xp, arg, ea.cmdidx);
- break;
case CMD_sign:
- set_context_in_sign_cmd(xp, (char_u *)arg);
+ set_context_in_sign_cmd(xp, (char *)arg);
break;
case CMD_bdelete:
case CMD_bwipeout:
@@ -1559,34 +1978,7 @@ static const char *set_one_cmd_context(expand_T *xp, const char *buff)
case CMD_USER:
case CMD_USER_BUF:
- if (context != EXPAND_NOTHING) {
- // 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 *)arg, forceit);
- } else if (context == EXPAND_COMMANDS) {
- return arg;
- } else if (context == EXPAND_MAPPINGS) {
- return (const char *)set_context_in_map_cmd(xp, "map", (char_u *)arg, forceit,
- false, false,
- CMD_map);
- }
- // Find start of last argument.
- p = arg;
- while (*p) {
- if (*p == ' ') {
- // argument starts after a space
- arg = p + 1;
- } else if (*p == '\\' && *(p + 1) != NUL) {
- p++; // skip over escaped character
- }
- MB_PTR_ADV(p);
- }
- xp->xp_pattern = (char *)arg;
- }
- xp->xp_context = context;
- }
- break;
+ return set_context_in_user_cmdarg(cmd, arg, argt, context, xp, forceit);
case CMD_map:
case CMD_noremap:
@@ -1606,8 +1998,8 @@ static const char *set_one_cmd_context(expand_T *xp, const char *buff)
case CMD_snoremap:
case CMD_xmap:
case CMD_xnoremap:
- return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char_u *)arg, forceit, false,
- false, ea.cmdidx);
+ return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char *)arg, forceit, false,
+ false, cmdidx);
case CMD_unmap:
case CMD_nunmap:
case CMD_vunmap:
@@ -1617,8 +2009,8 @@ static const char *set_one_cmd_context(expand_T *xp, const char *buff)
case CMD_lunmap:
case CMD_sunmap:
case CMD_xunmap:
- return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char_u *)arg, forceit, false,
- true, ea.cmdidx);
+ return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char *)arg, forceit, false,
+ true, cmdidx);
case CMD_mapclear:
case CMD_nmapclear:
case CMD_vmapclear:
@@ -1638,13 +2030,13 @@ static const char *set_one_cmd_context(expand_T *xp, const char *buff)
case CMD_cnoreabbrev:
case CMD_iabbrev:
case CMD_inoreabbrev:
- return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char_u *)arg, forceit, true,
- false, ea.cmdidx);
+ return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char *)arg, forceit, true,
+ false, cmdidx);
case CMD_unabbreviate:
case CMD_cunabbrev:
case CMD_iunabbrev:
- return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char_u *)arg, forceit, true,
- true, ea.cmdidx);
+ return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char *)arg, forceit, true,
+ true, cmdidx);
case CMD_menu:
case CMD_noremenu:
case CMD_unmenu:
@@ -1702,22 +2094,7 @@ static const char *set_one_cmd_context(expand_T *xp, const char *buff)
#ifdef HAVE_WORKING_LIBINTL
case CMD_language:
- p = (const char *)skiptowhite((const char_u *)arg);
- if (*p == NUL) {
- xp->xp_context = EXPAND_LANGUAGE;
- xp->xp_pattern = (char *)arg;
- } else {
- 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(p);
- } else {
- xp->xp_context = EXPAND_NOTHING;
- }
- }
- break;
+ return set_context_in_lang_cmd(xp, arg);
#endif
case CMD_profile:
set_context_in_profile_cmd(xp, arg);
@@ -1753,6 +2130,14 @@ static const char *set_one_cmd_context(expand_T *xp, const char *buff)
xp->xp_pattern = (char *)arg;
break;
+ case CMD_breakadd:
+ case CMD_profdel:
+ case CMD_breakdel:
+ return set_context_in_breakadd_cmd(xp, arg, cmdidx);
+
+ case CMD_scriptnames:
+ return set_context_in_scriptnames_cmd(xp, arg);
+
case CMD_lua:
xp->xp_context = EXPAND_LUA;
break;
@@ -1763,14 +2148,192 @@ static const char *set_one_cmd_context(expand_T *xp, const char *buff)
return NULL;
}
+/// This is all pretty much copied from do_one_cmd(), with all the extra stuff
+/// we don't need/want deleted. Maybe this could be done better if we didn't
+/// repeat all this stuff. The only problem is that they may not stay
+/// perfectly compatible with each other, but then the command line syntax
+/// probably won't change that much -- webb.
+///
+/// @param buff buffer for command string
+static const char *set_one_cmd_context(expand_T *xp, const char *buff)
+{
+ size_t len = 0;
+ exarg_T ea;
+ int context = EXPAND_NOTHING;
+ bool forceit = false;
+ bool usefilter = false; // Filter instead of file name.
+
+ ExpandInit(xp);
+ xp->xp_pattern = (char *)buff;
+ xp->xp_line = (char *)buff;
+ xp->xp_context = EXPAND_COMMANDS; // Default until we get past command
+ ea.argt = 0;
+
+ // 1. skip comment lines and leading space, colons or bars
+ const char *cmd;
+ for (cmd = buff; vim_strchr(" \t:|", (uint8_t)(*cmd)) != NULL; cmd++) {}
+ xp->xp_pattern = (char *)cmd;
+
+ if (*cmd == NUL) {
+ return NULL;
+ }
+ if (*cmd == '"') { // ignore comment lines
+ xp->xp_context = EXPAND_NOTHING;
+ return NULL;
+ }
+
+ // 3. skip over a range specifier of the form: addr [,addr] [;addr] ..
+ cmd = (const char *)skip_range(cmd, &xp->xp_context);
+ xp->xp_pattern = (char *)cmd;
+ if (*cmd == NUL) {
+ return NULL;
+ }
+ if (*cmd == '"') {
+ xp->xp_context = EXPAND_NOTHING;
+ return NULL;
+ }
+
+ if (*cmd == '|' || *cmd == '\n') {
+ return cmd + 1; // There's another command
+ }
+
+ // Get the command index.
+ const char *p = set_cmd_index(cmd, &ea, xp, &context);
+ if (p == NULL) {
+ return NULL;
+ }
+
+ xp->xp_context = EXPAND_NOTHING; // Default now that we're past command
+
+ if (*p == '!') { // forced commands
+ forceit = true;
+ p++;
+ }
+
+ // 6. parse arguments
+ if (!IS_USER_CMDIDX(ea.cmdidx)) {
+ ea.argt = excmd_get_argt(ea.cmdidx);
+ }
+
+ const char *arg = (const char *)skipwhite(p);
+
+ // Skip over ++argopt argument
+ if ((ea.argt & EX_ARGOPT) && *arg != NUL && strncmp(arg, "++", 2) == 0) {
+ p = arg;
+ while (*p && !ascii_isspace(*p)) {
+ MB_PTR_ADV(p);
+ }
+ arg = (const char *)skipwhite(p);
+ }
+
+ if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update) {
+ if (*arg == '>') { // append
+ if (*++arg == '>') {
+ arg++;
+ }
+ arg = (const char *)skipwhite(arg);
+ } else if (*arg == '!' && ea.cmdidx == CMD_write) { // :w !filter
+ arg++;
+ usefilter = true;
+ }
+ }
+
+ if (ea.cmdidx == CMD_read) {
+ usefilter = forceit; // :r! filter if forced
+ if (*arg == '!') { // :r !filter
+ arg++;
+ usefilter = true;
+ }
+ }
+
+ if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift) {
+ while (*arg == *cmd) { // allow any number of '>' or '<'
+ 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 *)arg, false);
+
+ // Still touching the command after '+'?
+ if (*arg == NUL) {
+ return p;
+ }
+
+ // Skip space(s) after +command to get to the real argument.
+ arg = (const char *)skipwhite(arg);
+ }
+
+ // Check for '|' to separate commands and '"' to start comments.
+ // Don't do this for ":read !cmd" and ":write !cmd".
+ if ((ea.argt & EX_TRLBAR) && !usefilter) {
+ p = arg;
+ // ":redir @" is not the start of a comment
+ if (ea.cmdidx == CMD_redir && p[0] == '@' && p[1] == '"') {
+ p += 2;
+ }
+ while (*p) {
+ if (*p == Ctrl_V) {
+ if (p[1] != NUL) {
+ p++;
+ }
+ } else if ((*p == '"' && !(ea.argt & EX_NOTRLCOM))
+ || *p == '|'
+ || *p == '\n') {
+ if (*(p - 1) != '\\') {
+ if (*p == '|' || *p == '\n') {
+ return p + 1;
+ }
+ return NULL; // It's a comment
+ }
+ }
+ MB_PTR_ADV(p);
+ }
+ }
+
+ if (!(ea.argt & EX_EXTRA) && *arg != NUL && strchr("|\"", *arg) == NULL) {
+ // no arguments allowed but there is something
+ return NULL;
+ }
+
+ // Find start of last argument (argument just before cursor):
+ p = buff;
+ 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 *)++p;
+ } else {
+ if (*p == '\\' && *(p + 1) != NUL) {
+ p++; // skip over escaped character
+ }
+ MB_PTR_ADV(p);
+ }
+ }
+
+ if (ea.argt & EX_XFILE) {
+ set_context_for_wildcard_arg(&ea, arg, usefilter, xp, &context);
+ }
+
+ // Switch on command name.
+ return set_context_by_cmdname(cmd, ea.cmdidx, xp, arg, ea.argt, context, forceit);
+}
+
+/// Set the completion context in "xp" for command "str"
+///
/// @param str start of command line
/// @param len length of command line (excl. NUL)
/// @param col position of cursor
/// @param use_ccline use ccline for info
-void set_cmd_context(expand_T *xp, char_u *str, int len, int col, int use_ccline)
+void set_cmd_context(expand_T *xp, char *str, int len, int col, int use_ccline)
{
CmdlineInfo *const ccline = get_cmdline_info();
- char_u old_char = NUL;
+ char old_char = NUL;
// Avoid a UMR warning from Purify, only save the character if it has been
// written before.
@@ -1782,11 +2345,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, (char *)str, CMD_SIZE);
+ set_context_for_expression(xp, str, CMD_SIZE);
} else if (use_ccline && ccline->input_fn) {
xp->xp_context = ccline->xp_context;
- xp->xp_pattern = (char *)ccline->cmdbuff;
- xp->xp_arg = (char *)ccline->xp_arg;
+ xp->xp_pattern = ccline->cmdbuff;
+ xp->xp_arg = ccline->xp_arg;
} else {
while (nextcomm != NULL) {
nextcomm = set_one_cmd_context(xp, nextcomm);
@@ -1795,7 +2358,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 = (char *)str;
+ xp->xp_line = str;
xp->xp_col = col;
str[col] = old_char;
@@ -1815,9 +2378,9 @@ void set_cmd_context(expand_T *xp, char_u *str, int len, int col, int use_ccline
/// @param col position of cursor
/// @param matchcount return: nr of matches
/// @param matches return: array of pointers to matches
-int expand_cmdline(expand_T *xp, char_u *str, int col, int *matchcount, char ***matches)
+int expand_cmdline(expand_T *xp, const char *str, int col, int *matchcount, char ***matches)
{
- char_u *file_str = NULL;
+ char *file_str = NULL;
int options = WILD_ADD_SLASH|WILD_SILENT;
if (xp->xp_context == EXPAND_UNSUCCESSFUL) {
@@ -1830,16 +2393,21 @@ int expand_cmdline(expand_T *xp, char_u *str, int col, int *matchcount, char ***
}
// add star to file name, or convert to regexp if not exp. files.
- 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);
+ assert((str + col) - xp->xp_pattern >= 0);
+ xp->xp_pattern_len = (size_t)((str + col) - xp->xp_pattern);
+ if (cmdline_fuzzy_completion_supported(xp)) {
+ // If fuzzy matching, don't modify the search string
+ file_str = xstrdup(xp->xp_pattern);
+ } else {
+ file_str = addstar(xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
+ }
if (p_wic) {
options += WILD_ICASE;
}
// find all files that match the description
- if (ExpandFromContext(xp, file_str, matchcount, matches, options) == FAIL) {
+ if (ExpandFromContext(xp, file_str, matches, matchcount, options) == FAIL) {
*matchcount = 0;
*matches = NULL;
}
@@ -1848,6 +2416,66 @@ int expand_cmdline(expand_T *xp, char_u *str, int col, int *matchcount, char ***
return EXPAND_OK;
}
+/// Expand file or directory names.
+static int expand_files_and_dirs(expand_T *xp, char *pat, char ***matches, int *numMatches,
+ int flags, int options)
+{
+ bool free_pat = false;
+
+ // for ":set path=" and ":set tags=" halve backslashes for escaped space
+ if (xp->xp_backslash != XP_BS_NONE) {
+ free_pat = true;
+ pat = xstrdup(pat);
+ for (int i = 0; pat[i]; i++) {
+ if (pat[i] == '\\') {
+ if (xp->xp_backslash == XP_BS_THREE
+ && pat[i + 1] == '\\'
+ && pat[i + 2] == '\\'
+ && pat[i + 3] == ' ') {
+ STRMOVE(pat + i, pat + i + 3);
+ }
+ if (xp->xp_backslash == XP_BS_ONE
+ && pat[i + 1] == ' ') {
+ STRMOVE(pat + i, pat + i + 1);
+ }
+ }
+ }
+ }
+
+ if (xp->xp_context == EXPAND_FILES) {
+ flags |= EW_FILE;
+ } else if (xp->xp_context == EXPAND_FILES_IN_PATH) {
+ flags |= (EW_FILE | EW_PATH);
+ } else {
+ flags = (flags | EW_DIR) & ~EW_FILE;
+ }
+ if (options & WILD_ICASE) {
+ flags |= EW_ICASE;
+ }
+
+ // Expand wildcards, supporting %:h and the like.
+ int ret = expand_wildcards_eval(&pat, numMatches, matches, flags);
+ if (free_pat) {
+ xfree(pat);
+ }
+#ifdef BACKSLASH_IN_FILENAME
+ if (p_csl[0] != NUL && (options & WILD_IGNORE_COMPLETESLASH) == 0) {
+ for (int j = 0; j < *numMatches; j++) {
+ char *ptr = (*matches)[j];
+ while (*ptr != NUL) {
+ if (p_csl[0] == 's' && *ptr == '\\') {
+ *ptr = '/';
+ } else if (p_csl[0] == 'b' && *ptr == '/') {
+ *ptr = '\\';
+ }
+ ptr += utfc_ptr2len(ptr);
+ }
+ }
+ }
+#endif
+ return ret;
+}
+
/// Function given to ExpandGeneric() to obtain the possible arguments of the
/// ":behave {mswin,xterm}" command.
static char *get_behave_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
@@ -1862,6 +2490,45 @@ static char *get_behave_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
}
/// Function given to ExpandGeneric() to obtain the possible arguments of the
+/// ":breakadd {expr, file, func, here}" command.
+/// ":breakdel {func, file, here}" command.
+static char *get_breakadd_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
+{
+ char *opts[] = { "expr", "file", "func", "here" };
+
+ if (idx >= 0 && idx <= 3) {
+ // breakadd {expr, file, func, here}
+ if (breakpt_expand_what == EXP_BREAKPT_ADD) {
+ return opts[idx];
+ } else if (breakpt_expand_what == EXP_BREAKPT_DEL) {
+ // breakdel {func, file, here}
+ if (idx <= 2) {
+ return opts[idx + 1];
+ }
+ } else {
+ // profdel {func, file}
+ if (idx <= 1) {
+ return opts[idx + 1];
+ }
+ }
+ }
+ return NULL;
+}
+
+/// Function given to ExpandGeneric() to obtain the possible arguments for the
+/// ":scriptnames" command.
+static char *get_scriptnames_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
+{
+ if (!SCRIPT_ID_VALID(idx + 1)) {
+ return NULL;
+ }
+
+ scriptitem_T *si = &SCRIPT_ITEM(idx + 1);
+ home_replace(NULL, si->sn_name, NameBuff, MAXPATHL, true);
+ return NameBuff;
+}
+
+/// Function given to ExpandGeneric() to obtain the possible arguments of the
/// ":messages {clear}" command.
static char *get_messages_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
{
@@ -1905,16 +2572,72 @@ static char *get_healthcheck_names(expand_T *xp FUNC_ATTR_UNUSED, int idx)
return NULL;
}
-/// Do the expansion based on xp->xp_context and "pat".
-///
-/// @param options WILD_ flags
-static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char ***file, int options)
+/// Do the expansion based on xp->xp_context and "rmp".
+static int ExpandOther(char *pat, expand_T *xp, regmatch_T *rmp, char ***matches, int *numMatches)
{
- regmatch_T regmatch;
- int ret;
- int flags;
+ typedef CompleteListItemGetter ExpandFunc;
+ static struct expgen {
+ int context;
+ ExpandFunc func;
+ int ic;
+ int escaped;
+ } tab[] = {
+ { EXPAND_COMMANDS, get_command_name, false, true },
+ { EXPAND_BEHAVE, get_behave_arg, true, true },
+ { EXPAND_MAPCLEAR, get_mapclear_arg, true, true },
+ { EXPAND_MESSAGES, get_messages_arg, true, true },
+ { EXPAND_HISTORY, get_history_arg, true, true },
+ { EXPAND_USER_COMMANDS, get_user_commands, false, true },
+ { EXPAND_USER_ADDR_TYPE, get_user_cmd_addr_type, false, true },
+ { EXPAND_USER_CMD_FLAGS, get_user_cmd_flags, false, true },
+ { EXPAND_USER_NARGS, get_user_cmd_nargs, false, true },
+ { EXPAND_USER_COMPLETE, get_user_cmd_complete, false, true },
+ { EXPAND_USER_VARS, get_user_var_name, false, true },
+ { EXPAND_FUNCTIONS, get_function_name, false, true },
+ { EXPAND_USER_FUNC, get_user_func_name, false, true },
+ { EXPAND_EXPRESSION, get_expr_name, false, true },
+ { EXPAND_MENUS, get_menu_name, false, true },
+ { EXPAND_MENUNAMES, get_menu_names, false, true },
+ { EXPAND_SYNTAX, get_syntax_name, true, true },
+ { EXPAND_SYNTIME, get_syntime_arg, true, true },
+ { EXPAND_HIGHLIGHT, (ExpandFunc)get_highlight_name, true, false },
+ { EXPAND_EVENTS, expand_get_event_name, true, false },
+ { EXPAND_AUGROUP, expand_get_augroup_name, true, false },
+ { EXPAND_SIGN, get_sign_name, true, true },
+ { EXPAND_PROFILE, get_profile_name, true, true },
+#ifdef HAVE_WORKING_LIBINTL
+ { EXPAND_LANGUAGE, get_lang_arg, true, false },
+ { EXPAND_LOCALES, get_locales, true, false },
+#endif
+ { EXPAND_ENV_VARS, get_env_name, true, true },
+ { EXPAND_USER, get_users, true, false },
+ { EXPAND_ARGLIST, get_arglist_name, true, false },
+ { EXPAND_BREAKPOINT, get_breakadd_arg, true, true },
+ { EXPAND_SCRIPTNAMES, get_scriptnames_arg, true, false },
+ { EXPAND_CHECKHEALTH, get_healthcheck_names, true, false },
+ };
+ int ret = FAIL;
+
+ // Find a context in the table and call the ExpandGeneric() with the
+ // right function to do the expansion.
+ for (int i = 0; i < (int)ARRAY_SIZE(tab); i++) {
+ if (xp->xp_context == tab[i].context) {
+ if (tab[i].ic) {
+ rmp->rm_ic = true;
+ }
+ ExpandGeneric(pat, xp, rmp, matches, numMatches, tab[i].func, tab[i].escaped);
+ ret = OK;
+ break;
+ }
+ }
+
+ return ret;
+}
- flags = EW_DIR; // include directories
+/// Map wild expand options to flags for expand_wildcards()
+static int map_wildopts_to_ewflags(int options)
+{
+ int flags = EW_DIR; // include directories
if (options & WILD_LIST_NOTFOUND) {
flags |= EW_NOTFOUND;
}
@@ -1934,214 +2657,123 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char ***f
flags |= EW_ALLLINKS;
}
+ return flags;
+}
+
+/// Do the expansion based on xp->xp_context and "pat".
+///
+/// @param options WILD_ flags
+static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numMatches, int options)
+{
+ regmatch_T regmatch = { .rm_ic = false };
+ int ret;
+ int flags = map_wildopts_to_ewflags(options);
+ const bool fuzzy = cmdline_fuzzy_complete(pat)
+ && cmdline_fuzzy_completion_supported(xp);
+
if (xp->xp_context == EXPAND_FILES
|| xp->xp_context == EXPAND_DIRECTORIES
|| xp->xp_context == EXPAND_FILES_IN_PATH) {
- // Expand file or directory names.
- bool free_pat = false;
- int i;
-
- // for ":set path=" and ":set tags=" halve backslashes for escaped space
- if (xp->xp_backslash != XP_BS_NONE) {
- free_pat = true;
- pat = vim_strsave(pat);
- for (i = 0; pat[i]; i++) {
- if (pat[i] == '\\') {
- if (xp->xp_backslash == XP_BS_THREE
- && pat[i + 1] == '\\'
- && pat[i + 2] == '\\'
- && pat[i + 3] == ' ') {
- STRMOVE(pat + i, pat + i + 3);
- }
- if (xp->xp_backslash == XP_BS_ONE
- && pat[i + 1] == ' ') {
- STRMOVE(pat + i, pat + i + 1);
- }
- }
- }
- }
-
- if (xp->xp_context == EXPAND_FILES) {
- flags |= EW_FILE;
- } else if (xp->xp_context == EXPAND_FILES_IN_PATH) {
- flags |= (EW_FILE | EW_PATH);
- } else {
- flags = (flags | EW_DIR) & ~EW_FILE;
- }
- if (options & WILD_ICASE) {
- flags |= EW_ICASE;
- }
-
- // Expand wildcards, supporting %:h and the like.
- ret = expand_wildcards_eval(&pat, num_file, file, flags);
- if (free_pat) {
- xfree(pat);
- }
-#ifdef BACKSLASH_IN_FILENAME
- if (p_csl[0] != NUL && (options & WILD_IGNORE_COMPLETESLASH) == 0) {
- for (int i = 0; i < *num_file; i++) {
- char_u *ptr = (*file)[i];
- while (*ptr != NUL) {
- if (p_csl[0] == 's' && *ptr == '\\') {
- *ptr = '/';
- } else if (p_csl[0] == 'b' && *ptr == '/') {
- *ptr = '\\';
- }
- ptr += utfc_ptr2len(ptr);
- }
- }
- }
-#endif
- return ret;
+ return expand_files_and_dirs(xp, pat, matches, numMatches, flags, options);
}
- *file = NULL;
- *num_file = 0;
+ *matches = NULL;
+ *numMatches = 0;
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 ? "help" : (char *)pat,
- num_file, file, false) == OK) {
- cleanup_help_tags(*num_file, *file);
+ if (find_help_tags(*pat == NUL ? "help" : pat,
+ numMatches, matches, false) == OK) {
+ cleanup_help_tags(*numMatches, *matches);
return OK;
}
return FAIL;
}
if (xp->xp_context == EXPAND_SHELLCMD) {
- *file = NULL;
- expand_shellcmd(pat, num_file, file, flags);
+ expand_shellcmd(pat, matches, numMatches, flags);
return OK;
}
if (xp->xp_context == EXPAND_OLD_SETTING) {
- ExpandOldSetting(num_file, file);
+ ExpandOldSetting(numMatches, matches);
return OK;
}
if (xp->xp_context == EXPAND_BUFFERS) {
- return ExpandBufnames((char *)pat, num_file, file, options);
+ return ExpandBufnames(pat, numMatches, matches, options);
}
if (xp->xp_context == EXPAND_DIFF_BUFFERS) {
- return ExpandBufnames((char *)pat, num_file, file, options | BUF_DIFF_FILTER);
+ return ExpandBufnames(pat, numMatches, matches, options | BUF_DIFF_FILTER);
}
if (xp->xp_context == EXPAND_TAGS
|| xp->xp_context == EXPAND_TAGS_LISTFILES) {
- return expand_tags(xp->xp_context == EXPAND_TAGS, pat, num_file, file);
+ return expand_tags(xp->xp_context == EXPAND_TAGS, pat, numMatches, matches);
}
if (xp->xp_context == EXPAND_COLORS) {
char *directories[] = { "colors", NULL };
- return ExpandRTDir(pat, DIP_START + DIP_OPT + DIP_LUA, num_file, file, directories);
+ return ExpandRTDir(pat, DIP_START + DIP_OPT, numMatches, matches, directories);
}
if (xp->xp_context == EXPAND_COMPILER) {
char *directories[] = { "compiler", NULL };
- return ExpandRTDir(pat, DIP_LUA, num_file, file, directories);
+ return ExpandRTDir(pat, 0, numMatches, matches, directories);
}
if (xp->xp_context == EXPAND_OWNSYNTAX) {
char *directories[] = { "syntax", NULL };
- return ExpandRTDir(pat, 0, num_file, file, directories);
+ return ExpandRTDir(pat, 0, numMatches, matches, directories);
}
if (xp->xp_context == EXPAND_FILETYPE) {
char *directories[] = { "syntax", "indent", "ftplugin", NULL };
- return ExpandRTDir(pat, DIP_LUA, num_file, file, directories);
+ return ExpandRTDir(pat, 0, numMatches, matches, directories);
}
if (xp->xp_context == EXPAND_USER_LIST) {
- return ExpandUserList(xp, num_file, file);
+ return ExpandUserList(xp, matches, numMatches);
}
if (xp->xp_context == EXPAND_USER_LUA) {
- return ExpandUserLua(xp, num_file, file);
+ return ExpandUserLua(xp, numMatches, matches);
}
if (xp->xp_context == EXPAND_PACKADD) {
- return ExpandPackAddDir(pat, num_file, file);
+ return ExpandPackAddDir(pat, numMatches, matches);
}
// When expanding a function name starting with s:, match the <SNR>nr_
// prefix.
char *tofree = NULL;
- if (xp->xp_context == EXPAND_USER_FUNC && STRNCMP(pat, "^s:", 3) == 0) {
- const size_t len = STRLEN(pat) + 20;
+ if (xp->xp_context == EXPAND_USER_FUNC && strncmp(pat, "^s:", 3) == 0) {
+ const size_t len = strlen(pat) + 20;
tofree = xmalloc(len);
snprintf(tofree, len, "^<SNR>\\d\\+_%s", pat + 3);
- pat = (char_u *)tofree;
+ pat = tofree;
}
if (xp->xp_context == EXPAND_LUA) {
ILOG("PAT %s", pat);
- return nlua_expand_pat(xp, pat, num_file, file);
+ return nlua_expand_pat(xp, pat, numMatches, matches);
}
- regmatch.regprog = vim_regcomp((char *)pat, p_magic ? RE_MAGIC : 0);
- if (regmatch.regprog == NULL) {
- return FAIL;
- }
+ if (!fuzzy) {
+ regmatch.regprog = vim_regcomp(pat, magic_isset() ? RE_MAGIC : 0);
+ if (regmatch.regprog == NULL) {
+ return FAIL;
+ }
- // set ignore-case according to p_ic, p_scs and pat
- regmatch.rm_ic = ignorecase(pat);
+ // set ignore-case according to p_ic, p_scs and pat
+ regmatch.rm_ic = ignorecase(pat);
+ }
if (xp->xp_context == EXPAND_SETTINGS
|| xp->xp_context == EXPAND_BOOL_SETTINGS) {
- ret = ExpandSettings(xp, &regmatch, num_file, file);
+ ret = ExpandSettings(xp, &regmatch, pat, numMatches, matches, fuzzy);
} else if (xp->xp_context == EXPAND_MAPPINGS) {
- ret = ExpandMappings(&regmatch, num_file, file);
+ ret = ExpandMappings(pat, &regmatch, numMatches, matches);
} else if (xp->xp_context == EXPAND_USER_DEFINED) {
- ret = ExpandUserDefined(xp, &regmatch, num_file, file);
+ ret = ExpandUserDefined(pat, xp, &regmatch, matches, numMatches);
} else {
- typedef CompleteListItemGetter ExpandFunc;
- static struct expgen {
- int context;
- ExpandFunc func;
- int ic;
- int escaped;
- } tab[] = {
- { EXPAND_COMMANDS, get_command_name, false, true },
- { EXPAND_BEHAVE, get_behave_arg, true, true },
- { EXPAND_MAPCLEAR, get_mapclear_arg, true, true },
- { EXPAND_MESSAGES, get_messages_arg, true, true },
- { EXPAND_HISTORY, get_history_arg, true, true },
- { EXPAND_USER_COMMANDS, get_user_commands, false, true },
- { EXPAND_USER_ADDR_TYPE, get_user_cmd_addr_type, false, true },
- { EXPAND_USER_CMD_FLAGS, get_user_cmd_flags, false, true },
- { EXPAND_USER_NARGS, get_user_cmd_nargs, false, true },
- { EXPAND_USER_COMPLETE, get_user_cmd_complete, false, true },
- { EXPAND_USER_VARS, get_user_var_name, false, true },
- { EXPAND_FUNCTIONS, get_function_name, false, true },
- { EXPAND_USER_FUNC, get_user_func_name, false, true },
- { EXPAND_EXPRESSION, get_expr_name, false, true },
- { EXPAND_MENUS, get_menu_name, false, true },
- { EXPAND_MENUNAMES, get_menu_names, false, true },
- { EXPAND_SYNTAX, get_syntax_name, true, true },
- { EXPAND_SYNTIME, get_syntime_arg, true, true },
- { EXPAND_HIGHLIGHT, (ExpandFunc)get_highlight_name, true, true },
- { EXPAND_EVENTS, expand_get_event_name, true, false },
- { EXPAND_AUGROUP, expand_get_augroup_name, true, false },
- { EXPAND_CSCOPE, get_cscope_name, true, true },
- { EXPAND_SIGN, get_sign_name, true, true },
- { EXPAND_PROFILE, get_profile_name, true, true },
-#ifdef HAVE_WORKING_LIBINTL
- { EXPAND_LANGUAGE, get_lang_arg, true, false },
- { EXPAND_LOCALES, get_locales, true, false },
-#endif
- { EXPAND_ENV_VARS, get_env_name, true, true },
- { EXPAND_USER, get_users, true, false },
- { EXPAND_ARGLIST, get_arglist_name, true, false },
- { EXPAND_CHECKHEALTH, get_healthcheck_names, true, false },
- };
-
- // Find a context in the table and call the ExpandGeneric() with the
- // right function to do the expansion.
- ret = FAIL;
- for (int i = 0; i < (int)ARRAY_SIZE(tab); i++) {
- if (xp->xp_context == tab[i].context) {
- if (tab[i].ic) {
- regmatch.rm_ic = true;
- }
- ExpandGeneric(xp, &regmatch, num_file, file, tab[i].func, tab[i].escaped);
- ret = OK;
- break;
- }
- }
+ ret = ExpandOther(pat, xp, &regmatch, matches, numMatches);
}
- vim_regfree(regmatch.regprog);
+ if (!fuzzy) {
+ vim_regfree(regmatch.regprog);
+ }
xfree(tofree);
return ret;
@@ -2154,102 +2786,173 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char ***f
/// program. Matching strings are copied into an array, which is returned.
///
/// @param func returns a string from the list
-static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, char ***file,
- CompleteListItemGetter func, int escaped)
+static void ExpandGeneric(const char *const pat, expand_T *xp, regmatch_T *regmatch,
+ char ***matches, int *numMatches, CompleteListItemGetter func,
+ int escaped)
{
- int i;
- size_t count = 0;
- char_u *str;
+ const bool fuzzy = cmdline_fuzzy_complete(pat);
+ *matches = NULL;
+ *numMatches = 0;
- // count the number of matching names
- 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, (char *)str, (colnr_T)0)) {
- count++;
- }
- }
- if (count == 0) {
- return;
+ garray_T ga;
+ if (!fuzzy) {
+ ga_init(&ga, sizeof(char *), 30);
+ } else {
+ ga_init(&ga, sizeof(fuzmatch_str_T), 30);
}
- assert(count < INT_MAX);
- *num_file = (int)count;
- *file = xmalloc(count * sizeof(char_u *));
- // copy the matching names into allocated memory
- count = 0;
- for (i = 0;; i++) {
- str = (char_u *)(*func)(xp, i);
+ for (int i = 0;; i++) {
+ char *str = (*func)(xp, i);
if (str == NULL) { // End of list.
break;
}
if (*str == NUL) { // Skip empty strings.
continue;
}
- if (vim_regexec(regmatch, (char *)str, (colnr_T)0)) {
- if (escaped) {
- str = vim_strsave_escaped(str, (char_u *)" \t\\.");
+
+ bool match;
+ int score = 0;
+ if (xp->xp_pattern[0] != NUL) {
+ if (!fuzzy) {
+ match = vim_regexec(regmatch, str, (colnr_T)0);
} else {
- str = vim_strsave(str);
+ score = fuzzy_match_str(str, pat);
+ match = (score != 0);
}
- (*file)[count++] = (char *)str;
- if (func == get_menu_names) {
- // Test for separator added by get_menu_names().
- str += STRLEN(str) - 1;
- if (*str == '\001') {
- *str = '.';
- }
+ } else {
+ match = true;
+ }
+
+ if (!match) {
+ continue;
+ }
+
+ if (escaped) {
+ str = vim_strsave_escaped(str, " \t\\.");
+ } else {
+ str = xstrdup(str);
+ }
+
+ if (fuzzy) {
+ GA_APPEND(fuzmatch_str_T, &ga, ((fuzmatch_str_T){
+ .idx = ga.ga_len,
+ .str = str,
+ .score = score,
+ }));
+ } else {
+ GA_APPEND(char *, &ga, str);
+ }
+
+ if (func == get_menu_names) {
+ // Test for separator added by get_menu_names().
+ str += strlen(str) - 1;
+ if (*str == '\001') {
+ *str = '.';
}
}
}
- // Sort the results. Keep menu's in the specified order.
- if (xp->xp_context != EXPAND_MENUNAMES && xp->xp_context != EXPAND_MENUS) {
- if (xp->xp_context == EXPAND_EXPRESSION
- || xp->xp_context == EXPAND_FUNCTIONS
- || xp->xp_context == EXPAND_USER_FUNC) {
+ if (ga.ga_len == 0) {
+ return;
+ }
+
+ // Sort the matches when using regular expression matching and sorting
+ // applies to the completion context. Menus and scriptnames should be kept
+ // in the specified order.
+ const bool sort_matches = !fuzzy
+ && xp->xp_context != EXPAND_MENUNAMES
+ && xp->xp_context != EXPAND_MENUS
+ && xp->xp_context != EXPAND_SCRIPTNAMES;
+
+ // <SNR> functions should be sorted to the end.
+ const bool funcsort = xp->xp_context == EXPAND_EXPRESSION
+ || xp->xp_context == EXPAND_FUNCTIONS
+ || xp->xp_context == EXPAND_USER_FUNC;
+
+ // Sort the matches.
+ if (sort_matches) {
+ if (funcsort) {
// <SNR> functions should be sorted to the end.
- qsort((void *)(*file), (size_t)(*num_file), sizeof(char_u *),
- sort_func_compare);
+ qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(char *), sort_func_compare);
} else {
- sort_strings(*file, *num_file);
+ sort_strings(ga.ga_data, ga.ga_len);
}
}
+ if (!fuzzy) {
+ *matches = ga.ga_data;
+ *numMatches = ga.ga_len;
+ } else {
+ fuzzymatches_to_strmatches(ga.ga_data, matches, ga.ga_len, funcsort);
+ *numMatches = ga.ga_len;
+ }
+
// Reset the variables used for special highlight names expansion, so that
// they don't show up when getting normal highlight names by ID.
reset_expand_highlight();
}
+/// Expand shell command matches in one directory of $PATH.
+static void expand_shellcmd_onedir(char *buf, char *s, size_t l, char *pat, char ***matches,
+ int *numMatches, int flags, hashtab_T *ht, garray_T *gap)
+{
+ xstrlcpy(buf, s, l + 1);
+ add_pathsep(buf);
+ l = strlen(buf);
+ xstrlcpy(buf + l, pat, MAXPATHL - l);
+
+ // Expand matches in one directory of $PATH.
+ int ret = expand_wildcards(1, &buf, numMatches, matches, flags);
+ if (ret != OK) {
+ return;
+ }
+
+ ga_grow(gap, *numMatches);
+
+ for (int i = 0; i < *numMatches; i++) {
+ char *name = (*matches)[i];
+
+ if (strlen(name) > l) {
+ // Check if this name was already found.
+ hash_T hash = hash_hash(name + l);
+ hashitem_T *hi =
+ hash_lookup(ht, (const char *)(name + l), strlen(name + l), hash);
+ if (HASHITEM_EMPTY(hi)) {
+ // Remove the path that was prepended.
+ STRMOVE(name, name + l);
+ ((char **)gap->ga_data)[gap->ga_len++] = name;
+ hash_add_item(ht, hi, name, hash);
+ name = NULL;
+ }
+ }
+ xfree(name);
+ }
+ xfree(*matches);
+}
+
/// Complete a shell command.
///
-/// @param filepat is a pattern to match with command names.
-/// @param[out] num_file is pointer to number of matches.
-/// @param[out] file is pointer to array of pointers to matches.
-/// *file will either be set to NULL or point to
-/// allocated memory.
-/// @param flagsarg is a combination of EW_* flags.
-static void expand_shellcmd(char_u *filepat, int *num_file, char ***file, int flagsarg)
+/// @param filepat is a pattern to match with command names.
+/// @param[out] matches is pointer to array of pointers to matches.
+/// *matches will either be set to NULL or point to
+/// allocated memory.
+/// @param[out] numMatches is pointer to number of matches.
+/// @param flagsarg is a combination of EW_* flags.
+static void expand_shellcmd(char *filepat, char ***matches, int *numMatches, int flagsarg)
FUNC_ATTR_NONNULL_ALL
{
- char_u *pat;
+ char *pat;
int i;
- char_u *path = NULL;
+ char *path = NULL;
garray_T ga;
char *buf = xmalloc(MAXPATHL);
size_t l;
- char_u *s, *e;
+ char *s, *e;
int flags = flagsarg;
- int ret;
bool did_curdir = false;
// for ":set path=" and ":set tags=" halve backslashes for escaped space
- pat = vim_strsave(filepat);
+ pat = xstrdup(filepat);
for (i = 0; pat[i]; i++) {
if (pat[i] == '\\' && pat[i + 1] == ' ') {
STRMOVE(pat + i, pat + i + 1);
@@ -2261,29 +2964,29 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char ***file, int fl
bool mustfree = false; // Track memory allocation for *path.
if (pat[0] == '.' && (vim_ispathsep(pat[1])
|| (pat[1] == '.' && vim_ispathsep(pat[2])))) {
- path = (char_u *)".";
+ path = ".";
} else {
// For an absolute name we don't use $PATH.
if (!path_is_absolute(pat)) {
- path = (char_u *)vim_getenv("PATH");
+ path = vim_getenv("PATH");
}
if (path == NULL) {
- path = (char_u *)"";
+ path = "";
} else {
mustfree = true;
}
}
// Go over all directories in $PATH. Expand matches in that directory and
- // collect them in "ga". When "." is not in $PATH also expaned for the
+ // collect them in "ga". When "." is not in $PATH also expand for the
// current directory, to find "subdir/cmd".
ga_init(&ga, (int)sizeof(char *), 10);
hashtab_T found_ht;
hash_init(&found_ht);
for (s = path;; s = e) {
- e = (char_u *)vim_strchr((char *)s, ENV_SEPCHAR);
+ e = vim_strchr(s, ENV_SEPCHAR);
if (e == NULL) {
- e = s + STRLEN(s);
+ e = s + strlen(s);
}
if (*s == NUL) {
@@ -2293,7 +2996,7 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char ***file, int fl
// Find directories in the current directory, path is empty.
did_curdir = true;
flags |= EW_DIR;
- } else if (STRNCMP(s, ".", e - s) == 0) {
+ } else if (strncmp(s, ".", (size_t)(e - s)) == 0) {
did_curdir = true;
flags |= EW_DIR;
} else {
@@ -2305,44 +3008,13 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char ***file, int fl
if (l > MAXPATHL - 5) {
break;
}
- STRLCPY(buf, s, l + 1);
- add_pathsep(buf);
- l = STRLEN(buf);
- STRLCPY(buf + l, pat, MAXPATHL - l);
-
- // Expand matches in one directory of $PATH.
- ret = expand_wildcards(1, &buf, num_file, file, flags);
- if (ret == OK) {
- ga_grow(&ga, *num_file);
- {
- for (i = 0; i < *num_file; i++) {
- char_u *name = (char_u *)(*file)[i];
-
- if (STRLEN(name) > l) {
- // Check if this name was already found.
- hash_T hash = hash_hash(name + l);
- hashitem_T *hi =
- hash_lookup(&found_ht, (const char *)(name + l),
- STRLEN(name + l), hash);
- if (HASHITEM_EMPTY(hi)) {
- // Remove the path that was prepended.
- STRMOVE(name, name + l);
- ((char_u **)ga.ga_data)[ga.ga_len++] = name;
- hash_add_item(&found_ht, hi, name, hash);
- name = NULL;
- }
- }
- xfree(name);
- }
- xfree(*file);
- }
- }
+ expand_shellcmd_onedir(buf, s, l, pat, matches, numMatches, flags, &found_ht, &ga);
if (*e != NUL) {
e++;
}
}
- *file = ga.ga_data;
- *num_file = ga.ga_len;
+ *matches = ga.ga_data;
+ *numMatches = ga.ga_len;
xfree(buf);
xfree(pat);
@@ -2354,39 +3026,36 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char ***file, int fl
/// Call "user_expand_func()" to invoke a user defined Vim script function and
/// return the result (either a string, a List or NULL).
-static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T *xp, int *num_file,
- char ***file)
+static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T *xp)
FUNC_ATTR_NONNULL_ALL
{
CmdlineInfo *const ccline = get_cmdline_info();
- char_u keep = 0;
+ char keep = 0;
typval_T args[4];
- char_u *pat = NULL;
+ char *pat = NULL;
const sctx_T save_current_sctx = current_sctx;
if (xp->xp_arg == NULL || xp->xp_arg[0] == '\0' || xp->xp_line == NULL) {
return NULL;
}
- *num_file = 0;
- *file = NULL;
if (ccline->cmdbuff != NULL) {
keep = ccline->cmdbuff[ccline->cmdlen];
ccline->cmdbuff[ccline->cmdlen] = 0;
}
- pat = vim_strnsave((char_u *)xp->xp_pattern, xp->xp_pattern_len);
+ pat = xstrnsave(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 = (char *)pat;
+ args[0].vval.v_string = pat;
args[1].vval.v_string = xp->xp_line;
args[2].vval.v_number = xp->xp_col;
current_sctx = xp->xp_script_ctx;
- void *const ret = user_expand_func((char_u *)xp->xp_arg, 3, args);
+ void *const ret = user_expand_func(xp->xp_arg, 3, args);
current_sctx = save_current_sctx;
if (ccline->cmdbuff != NULL) {
@@ -2397,33 +3066,60 @@ static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T
return ret;
}
-/// Expand names with a function defined by the user.
-static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, char ***file)
+/// Expand names with a function defined by the user (EXPAND_USER_DEFINED and
+/// EXPAND_USER_LIST).
+static int ExpandUserDefined(const char *const pat, expand_T *xp, regmatch_T *regmatch,
+ char ***matches, int *numMatches)
{
- char_u *e;
- garray_T ga;
-
- char_u *const retstr = call_user_expand_func((user_expand_func_T)call_func_retstr, xp, num_file,
- file);
+ const bool fuzzy = cmdline_fuzzy_complete(pat);
+ *matches = NULL;
+ *numMatches = 0;
+ char *const retstr = call_user_expand_func((user_expand_func_T)call_func_retstr, xp);
if (retstr == NULL) {
return FAIL;
}
- ga_init(&ga, (int)sizeof(char *), 3);
- for (char_u *s = retstr; *s != NUL; s = e) {
- e = (char_u *)vim_strchr((char *)s, '\n');
+ garray_T ga;
+ if (!fuzzy) {
+ ga_init(&ga, (int)sizeof(char *), 3);
+ } else {
+ ga_init(&ga, (int)sizeof(fuzmatch_str_T), 3);
+ }
+
+ for (char *s = retstr, *e; *s != NUL; s = e) {
+ e = vim_strchr(s, '\n');
if (e == NULL) {
- e = s + STRLEN(s);
+ e = s + strlen(s);
}
- const char_u keep = *e;
+ const char keep = *e;
*e = NUL;
- const bool skip = xp->xp_pattern[0]
- && vim_regexec(regmatch, (char *)s, (colnr_T)0) == 0;
+ bool match;
+ int score = 0;
+ if (xp->xp_pattern[0] != NUL) {
+ if (!fuzzy) {
+ match = vim_regexec(regmatch, s, (colnr_T)0);
+ } else {
+ score = fuzzy_match_str(s, pat);
+ match = (score != 0);
+ }
+ } else {
+ match = true; // match everything
+ }
+
*e = keep;
- if (!skip) {
- GA_APPEND(char_u *, &ga, vim_strnsave(s, (size_t)(e - s)));
+
+ if (match) {
+ if (!fuzzy) {
+ GA_APPEND(char *, &ga, xstrnsave(s, (size_t)(e - s)));
+ } else {
+ GA_APPEND(fuzmatch_str_T, &ga, ((fuzmatch_str_T){
+ .idx = ga.ga_len,
+ .str = xstrnsave(s, (size_t)(e - s)),
+ .score = score,
+ }));
+ }
}
if (*e != NUL) {
@@ -2431,16 +3127,27 @@ static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file,
}
}
xfree(retstr);
- *file = ga.ga_data;
- *num_file = ga.ga_len;
+
+ if (ga.ga_len == 0) {
+ return OK;
+ }
+
+ if (!fuzzy) {
+ *matches = ga.ga_data;
+ *numMatches = ga.ga_len;
+ } else {
+ fuzzymatches_to_strmatches(ga.ga_data, matches, ga.ga_len, false);
+ *numMatches = ga.ga_len;
+ }
return OK;
}
/// Expand names with a list returned by a function defined by the user.
-static int ExpandUserList(expand_T *xp, int *num_file, char ***file)
+static int ExpandUserList(expand_T *xp, char ***matches, int *numMatches)
{
- list_T *const retlist = call_user_expand_func((user_expand_func_T)call_func_retlist, xp, num_file,
- file);
+ *matches = NULL;
+ *numMatches = 0;
+ list_T *const retlist = call_user_expand_func((user_expand_func_T)call_func_retlist, xp);
if (retlist == NULL) {
return FAIL;
}
@@ -2458,8 +3165,8 @@ static int ExpandUserList(expand_T *xp, int *num_file, char ***file)
});
tv_list_unref(retlist);
- *file = ga.ga_data;
- *num_file = ga.ga_len;
+ *matches = ga.ga_data;
+ *numMatches = ga.ga_len;
return OK;
}
@@ -2494,25 +3201,25 @@ static int ExpandUserLua(expand_T *xp, int *num_file, char ***file)
/// Expand `file` for all comma-separated directories in `path`.
/// Adds matches to `ga`.
-void globpath(char *path, char_u *file, garray_T *ga, int expand_options)
+void globpath(char *path, char *file, garray_T *ga, int expand_options)
{
expand_T xpc;
ExpandInit(&xpc);
xpc.xp_context = EXPAND_FILES;
- char_u *buf = xmalloc(MAXPATHL);
+ char *buf = xmalloc(MAXPATHL);
// 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, (char *)buf, MAXPATHL, ",");
- if (STRLEN(buf) + STRLEN(file) + 2 < MAXPATHL) {
- add_pathsep((char *)buf);
+ copy_option_part(&path, buf, MAXPATHL, ",");
+ if (strlen(buf) + strlen(file) + 2 < MAXPATHL) {
+ add_pathsep(buf);
STRCAT(buf, file); // NOLINT
char **p;
int num_p = 0;
- (void)ExpandFromContext(&xpc, buf, &num_p, &p,
+ (void)ExpandFromContext(&xpc, buf, &p, &num_p,
WILD_SILENT | expand_options);
if (num_p > 0) {
ExpandEscape(&xpc, buf, num_p, p, WILD_SILENT | expand_options);
@@ -2567,145 +3274,158 @@ static void cmdline_del(CmdlineInfo *cclp, int from)
cclp->cmdpos = from;
}
-/// Handle a key pressed when wild menu is displayed
-int wildmenu_process_key(CmdlineInfo *cclp, int key, expand_T *xp)
+/// Handle a key pressed when the wild menu for the menu names
+/// (EXPAND_MENUNAMES) is displayed.
+static int wildmenu_process_key_menunames(CmdlineInfo *cclp, int key, expand_T *xp)
{
- int c = key;
-
- // Special translations for 'wildmenu'
- if (xp->xp_context == EXPAND_MENUNAMES) {
- // Hitting <Down> after "emenu Name.": complete submenu
- if (c == K_DOWN && cclp->cmdpos > 0
- && cclp->cmdbuff[cclp->cmdpos - 1] == '.') {
- c = (int)p_wc;
- KeyTyped = true; // in case the key was mapped
- } else if (c == K_UP) {
- // Hitting <Up>: Remove one submenu name in front of the
- // cursor
- int found = false;
-
- int j = (int)((char_u *)xp->xp_pattern - cclp->cmdbuff);
- int i = 0;
- while (--j > 0) {
- // check for start of menu name
- if (cclp->cmdbuff[j] == ' '
- && cclp->cmdbuff[j - 1] != '\\') {
+ // Hitting <Down> after "emenu Name.": complete submenu
+ if (key == K_DOWN && cclp->cmdpos > 0
+ && cclp->cmdbuff[cclp->cmdpos - 1] == '.') {
+ key = (int)p_wc;
+ KeyTyped = true; // in case the key was mapped
+ } else if (key == K_UP) {
+ // Hitting <Up>: Remove one submenu name in front of the
+ // cursor
+ int found = false;
+
+ int j = (int)(xp->xp_pattern - cclp->cmdbuff);
+ int i = 0;
+ while (--j > 0) {
+ // check for start of menu name
+ if (cclp->cmdbuff[j] == ' '
+ && cclp->cmdbuff[j - 1] != '\\') {
+ i = j + 1;
+ break;
+ }
+ // check for start of submenu name
+ if (cclp->cmdbuff[j] == '.'
+ && cclp->cmdbuff[j - 1] != '\\') {
+ if (found) {
i = j + 1;
break;
+ } else {
+ found = true;
}
-
- // check for start of submenu name
- if (cclp->cmdbuff[j] == '.'
- && cclp->cmdbuff[j - 1] != '\\') {
- if (found) {
- i = j + 1;
- break;
- } else {
- found = true;
- }
- }
- }
- if (i > 0) {
- cmdline_del(cclp, i);
}
- c = (int)p_wc;
- KeyTyped = true; // in case the key was mapped
- xp->xp_context = EXPAND_NOTHING;
}
+ if (i > 0) {
+ cmdline_del(cclp, i);
+ }
+ key = (int)p_wc;
+ KeyTyped = true; // in case the key was mapped
+ xp->xp_context = EXPAND_NOTHING;
}
- if (xp->xp_context == EXPAND_FILES
- || xp->xp_context == EXPAND_DIRECTORIES
- || xp->xp_context == EXPAND_SHELLCMD) {
- char_u upseg[5];
-
- upseg[0] = PATHSEP;
- upseg[1] = '.';
- upseg[2] = '.';
- upseg[3] = PATHSEP;
- upseg[4] = NUL;
-
- if (c == K_DOWN
- && cclp->cmdpos > 0
- && cclp->cmdbuff[cclp->cmdpos - 1] == PATHSEP
- && (cclp->cmdpos < 3
- || cclp->cmdbuff[cclp->cmdpos - 2] != '.'
- || cclp->cmdbuff[cclp->cmdpos - 3] != '.')) {
- // go down a directory
- c = (int)p_wc;
- KeyTyped = true; // in case the key was mapped
- } else if (STRNCMP(xp->xp_pattern, upseg + 1, 3) == 0
- && c == K_DOWN) {
- // If in a direct ancestor, strip off one ../ to go down
- int found = false;
-
- int j = cclp->cmdpos;
- int i = (int)((char_u *)xp->xp_pattern - cclp->cmdbuff);
- while (--j > i) {
- j -= utf_head_off(cclp->cmdbuff, cclp->cmdbuff + j);
- if (vim_ispathsep(cclp->cmdbuff[j])) {
- found = true;
- break;
- }
- }
- if (found
- && cclp->cmdbuff[j - 1] == '.'
- && cclp->cmdbuff[j - 2] == '.'
- && (vim_ispathsep(cclp->cmdbuff[j - 3]) || j == i + 2)) {
- cmdline_del(cclp, j - 2);
- c = (int)p_wc;
- KeyTyped = true; // in case the key was mapped
+
+ return key;
+}
+
+/// Handle a key pressed when the wild menu for file names (EXPAND_FILES) or
+/// directory names (EXPAND_DIRECTORIES) or shell command names
+/// (EXPAND_SHELLCMD) is displayed.
+static int wildmenu_process_key_filenames(CmdlineInfo *cclp, int key, expand_T *xp)
+{
+ char upseg[5];
+ upseg[0] = PATHSEP;
+ upseg[1] = '.';
+ upseg[2] = '.';
+ upseg[3] = PATHSEP;
+ upseg[4] = NUL;
+
+ if (key == K_DOWN
+ && cclp->cmdpos > 0
+ && cclp->cmdbuff[cclp->cmdpos - 1] == PATHSEP
+ && (cclp->cmdpos < 3
+ || cclp->cmdbuff[cclp->cmdpos - 2] != '.'
+ || cclp->cmdbuff[cclp->cmdpos - 3] != '.')) {
+ // go down a directory
+ key = (int)p_wc;
+ KeyTyped = true; // in case the key was mapped
+ } else if (strncmp(xp->xp_pattern, upseg + 1, 3) == 0 && key == K_DOWN) {
+ // If in a direct ancestor, strip off one ../ to go down
+ int found = false;
+
+ int j = cclp->cmdpos;
+ int i = (int)(xp->xp_pattern - cclp->cmdbuff);
+ while (--j > i) {
+ j -= utf_head_off(cclp->cmdbuff, cclp->cmdbuff + j);
+ if (vim_ispathsep(cclp->cmdbuff[j])) {
+ found = true;
+ break;
}
- } else if (c == K_UP) {
- // go up a directory
- int found = false;
-
- int j = cclp->cmdpos - 1;
- int i = (int)((char_u *)xp->xp_pattern - cclp->cmdbuff);
- while (--j > i) {
- j -= utf_head_off(cclp->cmdbuff, cclp->cmdbuff + j);
- if (vim_ispathsep(cclp->cmdbuff[j])
+ }
+ if (found
+ && cclp->cmdbuff[j - 1] == '.'
+ && cclp->cmdbuff[j - 2] == '.'
+ && (vim_ispathsep(cclp->cmdbuff[j - 3]) || j == i + 2)) {
+ cmdline_del(cclp, j - 2);
+ key = (int)p_wc;
+ KeyTyped = true; // in case the key was mapped
+ }
+ } else if (key == K_UP) {
+ // go up a directory
+ int found = false;
+
+ int j = cclp->cmdpos - 1;
+ int i = (int)(xp->xp_pattern - cclp->cmdbuff);
+ while (--j > i) {
+ j -= utf_head_off(cclp->cmdbuff, cclp->cmdbuff + j);
+ if (vim_ispathsep(cclp->cmdbuff[j])
#ifdef BACKSLASH_IN_FILENAME
- && vim_strchr((const char_u *)" *?[{`$%#", cclp->cmdbuff[j + 1])
- == NULL
+ && vim_strchr(" *?[{`$%#", (uint8_t)cclp->cmdbuff[j + 1]) == NULL
#endif
- ) {
- if (found) {
- i = j + 1;
- break;
- } else {
- found = true;
- }
+ ) {
+ if (found) {
+ i = j + 1;
+ break;
+ } else {
+ found = true;
}
}
+ }
- if (!found) {
- j = i;
- } else if (STRNCMP(cclp->cmdbuff + j, upseg, 4) == 0) {
- j += 4;
- } else if (STRNCMP(cclp->cmdbuff + j, upseg + 1, 3) == 0
- && j == i) {
- j += 3;
- } else {
- j = 0;
- }
-
- if (j > 0) {
- // TODO(tarruda): this is only for DOS/Unix systems - need to put in
- // machine-specific stuff here and in upseg init
- cmdline_del(cclp, j);
- put_on_cmdline(upseg + 1, 3, false);
- } else if (cclp->cmdpos > i) {
- cmdline_del(cclp, i);
- }
+ if (!found) {
+ j = i;
+ } else if (strncmp(cclp->cmdbuff + j, upseg, 4) == 0) {
+ j += 4;
+ } else if (strncmp(cclp->cmdbuff + j, upseg + 1, 3) == 0
+ && j == i) {
+ j += 3;
+ } else {
+ j = 0;
+ }
- // Now complete in the new directory. Set KeyTyped in case the
- // Up key came from a mapping.
- c = (int)p_wc;
- KeyTyped = true;
+ if (j > 0) {
+ // TODO(tarruda): this is only for DOS/Unix systems - need to put in
+ // machine-specific stuff here and in upseg init
+ cmdline_del(cclp, j);
+ put_on_cmdline(upseg + 1, 3, false);
+ } else if (cclp->cmdpos > i) {
+ cmdline_del(cclp, i);
}
+
+ // Now complete in the new directory. Set KeyTyped in case the
+ // Up key came from a mapping.
+ key = (int)p_wc;
+ KeyTyped = true;
}
- return c;
+ return key;
+}
+
+/// Handle a key pressed when wild menu is displayed
+int wildmenu_process_key(CmdlineInfo *cclp, int key, expand_T *xp)
+{
+ // Special translations for 'wildmenu'
+ if (xp->xp_context == EXPAND_MENUNAMES) {
+ return wildmenu_process_key_menunames(cclp, key, xp);
+ }
+ if (xp->xp_context == EXPAND_FILES
+ || xp->xp_context == EXPAND_DIRECTORIES
+ || xp->xp_context == EXPAND_SHELLCMD) {
+ return wildmenu_process_key_filenames(cclp, key, xp);
+ }
+
+ return key;
}
/// Free expanded names when finished walking through the matches
@@ -2732,7 +3452,7 @@ void wildmenu_cleanup(CmdlineInfo *cclp)
p_ls = save_p_ls;
p_wmh = save_p_wmh;
last_status(false);
- update_screen(VALID); // redraw the screen NOW
+ update_screen(); // redraw the screen NOW
redrawcmd();
save_p_ls = -1;
wild_menu_showing = 0;
@@ -2751,9 +3471,9 @@ void wildmenu_cleanup(CmdlineInfo *cclp)
}
/// "getcompletion()" function
-void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_getcompletion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- char_u *pat;
+ char *pat;
expand_T xpc;
bool filtered = false;
int options = WILD_SILENT | WILD_USE_NL | WILD_ADD_SLASH
@@ -2786,14 +3506,14 @@ void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (strcmp(type, "cmdline") == 0) {
set_one_cmd_context(&xpc, pattern);
- xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
- xpc.xp_col = (int)STRLEN(pattern);
+ xpc.xp_pattern_len = strlen(xpc.xp_pattern);
+ xpc.xp_col = (int)strlen(pattern);
goto theend;
}
ExpandInit(&xpc);
xpc.xp_pattern = (char *)pattern;
- xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
+ xpc.xp_pattern_len = strlen(xpc.xp_pattern);
xpc.xp_context = cmdcomplete_str_to_type(type);
if (xpc.xp_context == EXPAND_NOTHING) {
semsg(_(e_invarg2), type);
@@ -2802,21 +3522,22 @@ void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (xpc.xp_context == EXPAND_MENUS) {
set_context_in_menu_cmd(&xpc, "menu", xpc.xp_pattern, false);
- xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
- }
-
- if (xpc.xp_context == EXPAND_CSCOPE) {
- set_context_in_cscope_cmd(&xpc, (const char *)xpc.xp_pattern, CMD_cscope);
- xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
+ xpc.xp_pattern_len = strlen(xpc.xp_pattern);
}
if (xpc.xp_context == EXPAND_SIGN) {
- set_context_in_sign_cmd(&xpc, (char_u *)xpc.xp_pattern);
- xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
+ set_context_in_sign_cmd(&xpc, xpc.xp_pattern);
+ xpc.xp_pattern_len = strlen(xpc.xp_pattern);
}
theend:
- pat = addstar((char_u *)xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context);
+ if (cmdline_fuzzy_completion_supported(&xpc)) {
+ // when fuzzy matching, don't modify the search string
+ pat = xstrdup(xpc.xp_pattern);
+ } else {
+ pat = addstar(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);
diff --git a/src/nvim/cmdexpand.h b/src/nvim/cmdexpand.h
index 93e91af169..810e289f7c 100644
--- a/src/nvim/cmdexpand.h
+++ b/src/nvim/cmdexpand.h
@@ -19,6 +19,9 @@ enum {
WILD_ALL_KEEP = 8,
WILD_CANCEL = 9,
WILD_APPLY = 10,
+ WILD_PAGEUP = 11,
+ WILD_PAGEDOWN = 12,
+ WILD_PUM_WANT = 13,
};
enum {
diff --git a/src/nvim/cmdhist.c b/src/nvim/cmdhist.c
index 13f97ba1e8..2df82d9355 100644
--- a/src/nvim/cmdhist.c
+++ b/src/nvim/cmdhist.c
@@ -3,14 +3,30 @@
// cmdhist.c: Functions for the history of the command-line.
+#include <assert.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
#include "nvim/ascii.h"
#include "nvim/charset.h"
#include "nvim/cmdhist.h"
+#include "nvim/eval/typval.h"
#include "nvim/ex_cmds.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_getln.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/macros.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
+#include "nvim/option_defs.h"
+#include "nvim/pos.h"
#include "nvim/regexp.h"
#include "nvim/strings.h"
-#include "nvim/ui.h"
+#include "nvim/types.h"
#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -90,14 +106,14 @@ static char *(history_names[]) = {
/// arguments of the ":history command.
char *get_history_arg(expand_T *xp, int idx)
{
- static char_u compl[2] = { NUL, NUL };
- char *short_names = ":=@>?/";
- int short_names_count = (int)STRLEN(short_names);
- int history_name_count = ARRAY_SIZE(history_names) - 1;
+ const char *short_names = ":=@>?/";
+ const int short_names_count = (int)strlen(short_names);
+ const int history_name_count = ARRAY_SIZE(history_names) - 1;
if (idx < short_names_count) {
- compl[0] = (char_u)short_names[idx];
- return (char *)compl;
+ xp->xp_buf[0] = short_names[idx];
+ xp->xp_buf[1] = NUL;
+ return xp->xp_buf;
}
if (idx < short_names_count + history_name_count) {
return history_names[idx - short_names_count];
@@ -116,56 +132,58 @@ void init_history(void)
int newlen = (int)p_hi;
int oldlen = hislen;
+ if (newlen == oldlen) { // history length didn't change
+ return;
+ }
+
// If history tables size changed, reallocate them.
// Tables are circular arrays (current position marked by hisidx[type]).
// On copying them to the new arrays, we take the chance to reorder them.
- if (newlen != oldlen) {
- for (int type = 0; type < HIST_COUNT; type++) {
- histentry_T *temp = (newlen
- ? xmalloc((size_t)newlen * sizeof(*temp))
- : NULL);
-
- int j = hisidx[type];
- if (j >= 0) {
- // old array gets partitioned this way:
- // [0 , i1 ) --> newest entries to be deleted
- // [i1 , i1 + l1) --> newest entries to be copied
- // [i1 + l1 , i2 ) --> oldest entries to be deleted
- // [i2 , i2 + l2) --> oldest entries to be copied
- int l1 = MIN(j + 1, newlen); // how many newest to copy
- int l2 = MIN(newlen, oldlen) - l1; // how many oldest to copy
- int i1 = j + 1 - l1; // copy newest from here
- int i2 = MAX(l1, oldlen - newlen + l1); // copy oldest from here
-
- // copy as much entries as they fit to new table, reordering them
- if (newlen) {
- // copy oldest entries
- memcpy(&temp[0], &history[type][i2], (size_t)l2 * sizeof(*temp));
- // copy newest entries
- memcpy(&temp[l2], &history[type][i1], (size_t)l1 * sizeof(*temp));
- }
-
- // delete entries that don't fit in newlen, if any
- for (int i = 0; i < i1; i++) {
- hist_free_entry(history[type] + i);
- }
- for (int i = i1 + l1; i < i2; i++) {
- hist_free_entry(history[type] + i);
- }
+ for (int type = 0; type < HIST_COUNT; type++) {
+ histentry_T *temp = (newlen > 0
+ ? xmalloc((size_t)newlen * sizeof(*temp))
+ : NULL);
+
+ int j = hisidx[type];
+ if (j >= 0) {
+ // old array gets partitioned this way:
+ // [0 , i1 ) --> newest entries to be deleted
+ // [i1 , i1 + l1) --> newest entries to be copied
+ // [i1 + l1 , i2 ) --> oldest entries to be deleted
+ // [i2 , i2 + l2) --> oldest entries to be copied
+ int l1 = MIN(j + 1, newlen); // how many newest to copy
+ int l2 = MIN(newlen, oldlen) - l1; // how many oldest to copy
+ int i1 = j + 1 - l1; // copy newest from here
+ int i2 = MAX(l1, oldlen - newlen + l1); // copy oldest from here
+
+ // copy as much entries as they fit to new table, reordering them
+ if (newlen) {
+ // copy oldest entries
+ memcpy(&temp[0], &history[type][i2], (size_t)l2 * sizeof(*temp));
+ // copy newest entries
+ memcpy(&temp[l2], &history[type][i1], (size_t)l1 * sizeof(*temp));
}
- // clear remaining space, if any
- int l3 = j < 0 ? 0 : MIN(newlen, oldlen); // number of copied entries
- if (newlen) {
- memset(temp + l3, 0, (size_t)(newlen - l3) * sizeof(*temp));
+ // delete entries that don't fit in newlen, if any
+ for (int i = 0; i < i1; i++) {
+ hist_free_entry(history[type] + i);
+ }
+ for (int i = i1 + l1; i < i2; i++) {
+ hist_free_entry(history[type] + i);
}
+ }
- hisidx[type] = l3 - 1;
- xfree(history[type]);
- history[type] = temp;
+ // clear remaining space, if any
+ int l3 = j < 0 ? 0 : MIN(newlen, oldlen); // number of copied entries
+ if (newlen > 0) {
+ memset(temp + l3, 0, (size_t)(newlen - l3) * sizeof(*temp));
}
- hislen = newlen;
+
+ hisidx[type] = l3 - 1;
+ xfree(history[type]);
+ history[type] = temp;
}
+ hislen = newlen;
}
static inline void hist_free_entry(histentry_T *hisptr)
@@ -186,7 +204,7 @@ static inline void clear_hist_entry(histentry_T *hisptr)
/// If 'move_to_front' is true, matching entry is moved to end of history.
///
/// @param move_to_front Move the entry to the front if it exists
-static int in_history(int type, char_u *str, int move_to_front, int sep)
+static int in_history(int type, char *str, int move_to_front, int sep)
{
int last_i = -1;
@@ -201,9 +219,9 @@ static int in_history(int type, char_u *str, int move_to_front, int sep)
// For search history, check that the separator character matches as
// well.
- char_u *p = history[type][i].hisstr;
- if (STRCMP(str, p) == 0
- && (type != HIST_SEARCH || sep == p[STRLEN(p) + 1])) {
+ char *p = history[type][i].hisstr;
+ if (strcmp(str, p) == 0
+ && (type != HIST_SEARCH || sep == p[strlen(p) + 1])) {
if (!move_to_front) {
return true;
}
@@ -215,24 +233,25 @@ static int in_history(int type, char_u *str, int move_to_front, int sep)
}
} while (i != hisidx[type]);
- if (last_i >= 0) {
- list_T *const list = history[type][i].additional_elements;
- str = history[type][i].hisstr;
- while (i != hisidx[type]) {
- if (++i >= hislen) {
- i = 0;
- }
- history[type][last_i] = history[type][i];
- last_i = i;
+ if (last_i < 0) {
+ return false;
+ }
+
+ list_T *const list = history[type][i].additional_elements;
+ str = history[type][i].hisstr;
+ while (i != hisidx[type]) {
+ if (++i >= hislen) {
+ i = 0;
}
- tv_list_unref(list);
- history[type][i].hisnum = ++hisnum[type];
- history[type][i].hisstr = str;
- history[type][i].timestamp = os_time();
- history[type][i].additional_elements = NULL;
- return true;
- }
- return false;
+ history[type][last_i] = history[type][i];
+ last_i = i;
+ }
+ tv_list_unref(list);
+ history[type][i].hisnum = ++hisnum[type];
+ history[type][i].hisstr = str;
+ history[type][i].timestamp = os_time();
+ history[type][i].additional_elements = NULL;
+ return true;
}
/// Convert history name to its HIST_ equivalent
@@ -261,7 +280,7 @@ static HistoryType get_histtype(const char *const name, const size_t len, const
}
}
- if (vim_strchr(":=@>?/", name[0]) != NULL && len == 1) {
+ if (vim_strchr(":=@>?/", (uint8_t)name[0]) != NULL && len == 1) {
return hist_char2type(name[0]);
}
@@ -276,7 +295,7 @@ static int last_maptick = -1; // last seen maptick
/// @param histype may be one of the HIST_ values.
/// @param in_map consider maptick when inside a mapping
/// @param sep separator character used (search hist)
-void add_to_history(int histype, char_u *new_entry, int in_map, int sep)
+void add_to_history(int histype, char *new_entry, int in_map, int sep)
{
histentry_T *hisptr;
@@ -304,24 +323,27 @@ void add_to_history(int histype, char_u *new_entry, int in_map, int sep)
}
last_maptick = -1;
}
- if (!in_history(histype, new_entry, true, sep)) {
- if (++hisidx[histype] == hislen) {
- hisidx[histype] = 0;
- }
- hisptr = &history[histype][hisidx[histype]];
- hist_free_entry(hisptr);
-
- // Store the separator after the NUL of the string.
- size_t len = STRLEN(new_entry);
- hisptr->hisstr = vim_strnsave(new_entry, len + 2);
- hisptr->timestamp = os_time();
- hisptr->additional_elements = NULL;
- hisptr->hisstr[len + 1] = (char_u)sep;
-
- hisptr->hisnum = ++hisnum[histype];
- if (histype == HIST_SEARCH && in_map) {
- last_maptick = maptick;
- }
+
+ if (in_history(histype, new_entry, true, sep)) {
+ return;
+ }
+
+ if (++hisidx[histype] == hislen) {
+ hisidx[histype] = 0;
+ }
+ hisptr = &history[histype][hisidx[histype]];
+ hist_free_entry(hisptr);
+
+ // Store the separator after the NUL of the string.
+ size_t len = strlen(new_entry);
+ hisptr->hisstr = xstrnsave(new_entry, len + 2);
+ hisptr->timestamp = os_time();
+ hisptr->additional_elements = NULL;
+ hisptr->hisstr[len + 1] = (char)sep;
+
+ hisptr->hisnum = ++hisnum[histype];
+ if (histype == HIST_SEARCH && in_map) {
+ last_maptick = maptick;
}
}
@@ -382,13 +404,13 @@ static int calc_hist_idx(int histype, int num)
/// Get a history entry by its index.
///
/// @param histype may be one of the HIST_ values.
-static char_u *get_history_entry(int histype, int idx)
+static char *get_history_entry(int histype, int idx)
{
idx = calc_hist_idx(histype, idx);
if (idx >= 0) {
return history[histype][idx].hisstr;
} else {
- return (char_u *)"";
+ return "";
}
}
@@ -415,49 +437,50 @@ int clr_history(const int histype)
/// Remove all entries matching {str} from a history.
///
/// @param histype may be one of the HIST_ values.
-static int del_history_entry(int histype, char_u *str)
+static int del_history_entry(int histype, char *str)
{
+ if (hislen == 0 || histype < 0 || histype >= HIST_COUNT || *str == NUL
+ || hisidx[histype] < 0) {
+ return false;
+ }
+
+ const int idx = hisidx[histype];
regmatch_T regmatch;
- histentry_T *hisptr;
- int idx;
- int i;
- int last;
- bool found = false;
+ regmatch.regprog = vim_regcomp(str, RE_MAGIC + RE_STRING);
+ if (regmatch.regprog == NULL) {
+ return false;
+ }
- regmatch.regprog = NULL;
regmatch.rm_ic = false; // always match case
- if (hislen != 0
- && histype >= 0
- && histype < HIST_COUNT
- && *str != NUL
- && (idx = hisidx[histype]) >= 0
- && (regmatch.regprog = vim_regcomp((char *)str, RE_MAGIC + RE_STRING)) != NULL) {
- i = last = idx;
- do {
- hisptr = &history[histype][i];
- if (hisptr->hisstr == NULL) {
- break;
- }
- if (vim_regexec(&regmatch, (char *)hisptr->hisstr, (colnr_T)0)) {
- found = true;
- hist_free_entry(hisptr);
- } else {
- if (i != last) {
- history[histype][last] = *hisptr;
- clear_hist_entry(hisptr);
- }
- if (--last < 0) {
- last += hislen;
- }
+
+ bool found = false;
+ int i = idx, last = idx;
+ do {
+ histentry_T *hisptr = &history[histype][i];
+ if (hisptr->hisstr == NULL) {
+ break;
+ }
+ if (vim_regexec(&regmatch, hisptr->hisstr, (colnr_T)0)) {
+ found = true;
+ hist_free_entry(hisptr);
+ } else {
+ if (i != last) {
+ history[histype][last] = *hisptr;
+ clear_hist_entry(hisptr);
}
- if (--i < 0) {
- i += hislen;
+ if (--last < 0) {
+ last += hislen;
}
- } while (i != idx);
- if (history[histype][idx].hisstr == NULL) {
- hisidx[histype] = -1;
}
+ if (--i < 0) {
+ i += hislen;
+ }
+ } while (i != idx);
+
+ if (history[histype][idx].hisstr == NULL) {
+ hisidx[histype] = -1;
}
+
vim_regfree(regmatch.regprog);
return found;
}
@@ -494,7 +517,7 @@ static int del_history_idx(int histype, int idx)
}
/// "histadd()" function
-void f_histadd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_histadd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
HistoryType histype;
@@ -504,20 +527,23 @@ void f_histadd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
const char *str = tv_get_string_chk(&argvars[0]); // NULL on type error
histype = str != NULL ? get_histtype(str, strlen(str), false) : HIST_INVALID;
- if (histype != HIST_INVALID) {
- char buf[NUMBUFLEN];
- str = tv_get_string_buf(&argvars[1], buf);
- if (*str != NUL) {
- init_history();
- add_to_history(histype, (char_u *)str, false, NUL);
- rettv->vval.v_number = true;
- return;
- }
+ if (histype == HIST_INVALID) {
+ return;
+ }
+
+ char buf[NUMBUFLEN];
+ str = tv_get_string_buf(&argvars[1], buf);
+ if (*str == NUL) {
+ return;
}
+
+ init_history();
+ add_to_history(histype, (char *)str, false, NUL);
+ rettv->vval.v_number = true;
}
/// "histdel()" function
-void f_histdel(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_histdel(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int n;
const char *const str = tv_get_string_chk(&argvars[0]); // NULL on type error
@@ -534,13 +560,13 @@ void f_histdel(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// string given: remove all matching entries
char buf[NUMBUFLEN];
n = del_history_entry(get_histtype(str, strlen(str), false),
- (char_u *)tv_get_string_buf(&argvars[1], buf));
+ (char *)tv_get_string_buf(&argvars[1], buf));
}
rettv->vval.v_number = n;
}
/// "histget()" function
-void f_histget(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_histget(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
HistoryType type;
int idx;
@@ -556,13 +582,13 @@ 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 = (char *)vim_strsave(get_history_entry(type, idx));
+ rettv->vval.v_string = xstrdup(get_history_entry(type, idx));
}
rettv->v_type = VAR_STRING;
}
/// "histnr()" function
-void f_histnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_histnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *const histname = tv_get_string_chk(&argvars[0]);
HistoryType i = histname == NULL
@@ -585,7 +611,7 @@ void ex_history(exarg_T *eap)
int idx;
int i, j, k;
char *end;
- char_u *arg = (char_u *)eap->arg;
+ char *arg = eap->arg;
if (hislen == 0) {
msg(_("'history' option is zero"));
@@ -593,12 +619,12 @@ void ex_history(exarg_T *eap)
}
if (!(ascii_isdigit(*arg) || *arg == '-' || *arg == ',')) {
- end = (char *)arg;
+ end = arg;
while (ASCII_ISALPHA(*end)
- || vim_strchr(":=@>/?", *end) != NULL) {
+ || vim_strchr(":=@>/?", (uint8_t)(*end)) != NULL) {
end++;
}
- histype1 = get_histtype((const char *)arg, (size_t)(end - (char *)arg), false);
+ histype1 = get_histtype(arg, (size_t)(end - arg), false);
if (histype1 == HIST_INVALID) {
if (STRNICMP(arg, "all", end - (char *)arg) == 0) {
histype1 = 0;
@@ -611,7 +637,7 @@ void ex_history(exarg_T *eap)
histype2 = histype1;
}
} else {
- end = (char *)arg;
+ end = arg;
}
if (!get_list_range(&end, &hisidx1, &hisidx2) || *end != NUL) {
semsg(_(e_trailing_arg), end);
@@ -622,7 +648,7 @@ void ex_history(exarg_T *eap)
STRCPY(IObuff, "\n # ");
assert(history_names[histype1] != NULL);
STRCAT(STRCAT(IObuff, history_names[histype1]), " history");
- msg_puts_title((char *)IObuff);
+ msg_puts_title(IObuff);
idx = hisidx[histype1];
hist = history[histype1];
j = hisidx1;
@@ -641,16 +667,15 @@ void ex_history(exarg_T *eap)
if (hist[i].hisstr != NULL
&& hist[i].hisnum >= j && hist[i].hisnum <= k) {
msg_putchar('\n');
- snprintf((char *)IObuff, IOSIZE, "%c%6d ", i == idx ? '>' : ' ',
+ snprintf(IObuff, IOSIZE, "%c%6d ", i == idx ? '>' : ' ',
hist[i].hisnum);
- 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));
+ if (vim_strsize(hist[i].hisstr) > Columns - 10) {
+ trunc_string(hist[i].hisstr, IObuff + strlen(IObuff),
+ Columns - 10, IOSIZE - (int)strlen(IObuff));
} else {
STRCAT(IObuff, hist[i].hisstr);
}
- msg_outtrans((char *)IObuff);
- ui_flush();
+ msg_outtrans(IObuff);
}
if (i == idx) {
break;
diff --git a/src/nvim/cmdhist.h b/src/nvim/cmdhist.h
index 797b79a5f0..f86a2f855c 100644
--- a/src/nvim/cmdhist.h
+++ b/src/nvim/cmdhist.h
@@ -2,6 +2,7 @@
#define NVIM_CMDHIST_H
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/os/time.h"
@@ -22,7 +23,7 @@ typedef enum {
/// History entry definition
typedef struct hist_entry {
int hisnum; ///< Entry identifier number.
- char_u *hisstr; ///< Actual entry, separator char after the NUL.
+ char *hisstr; ///< Actual entry, separator char after the NUL.
Timestamp timestamp; ///< Time when entry was added.
list_T *additional_elements; ///< Additional entries from ShaDa file.
} histentry_T;
diff --git a/src/nvim/context.c b/src/nvim/context.c
index e3ae9355bf..9de6c16536 100644
--- a/src/nvim/context.c
+++ b/src/nvim/context.c
@@ -3,14 +3,26 @@
// Context: snapshot of the entire editor state as one big object/map
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
#include "nvim/api/private/converter.h"
#include "nvim/api/private/helpers.h"
-#include "nvim/api/vim.h"
#include "nvim/api/vimscript.h"
#include "nvim/context.h"
#include "nvim/eval/encode.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/eval/userfunc.h"
#include "nvim/ex_docmd.h"
+#include "nvim/gettext.h"
+#include "nvim/hashtab.h"
+#include "nvim/keycodes.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
#include "nvim/option.h"
#include "nvim/shada.h"
@@ -131,7 +143,7 @@ bool ctx_restore(Context *ctx, const int flags)
}
char *op_shada;
- get_option_value("shada", NULL, &op_shada, OPT_GLOBAL);
+ get_option_value("shada", NULL, &op_shada, NULL, OPT_GLOBAL);
set_option_value("shada", 0L, "!,'100,%", OPT_GLOBAL);
if (flags & kCtxRegs) {
@@ -251,12 +263,12 @@ static inline void ctx_save_funcs(Context *ctx, bool scriptonly)
Error err = ERROR_INIT;
HASHTAB_ITER(func_tbl_get(), hi, {
- const char_u *const name = hi->hi_key;
- bool islambda = (STRNCMP(name, "<lambda>", 8) == 0);
- bool isscript = (name[0] == K_SPECIAL);
+ const char *const name = hi->hi_key;
+ bool islambda = (strncmp(name, "<lambda>", 8) == 0);
+ bool isscript = ((uint8_t)name[0] == K_SPECIAL);
if (!islambda && (!scriptonly || isscript)) {
- size_t cmd_len = sizeof("func! ") + STRLEN(name);
+ 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(VIML_INTERNAL_CALL, cstr_as_string(cmd),
@@ -345,7 +357,7 @@ Dictionary ctx_to_dict(Context *ctx)
PUT(rv, "jumps", ARRAY_OBJ(sbuf_to_array(ctx->jumps)));
PUT(rv, "bufs", ARRAY_OBJ(sbuf_to_array(ctx->bufs)));
PUT(rv, "gvars", ARRAY_OBJ(sbuf_to_array(ctx->gvars)));
- PUT(rv, "funcs", ARRAY_OBJ(copy_array(ctx->funcs)));
+ PUT(rv, "funcs", ARRAY_OBJ(copy_array(ctx->funcs, NULL)));
return rv;
}
@@ -381,7 +393,7 @@ int ctx_from_dict(Dictionary dict, Context *ctx)
ctx->gvars = array_to_sbuf(item.value.data.array);
} else if (strequal(item.key.data, "funcs")) {
types |= kCtxFuncs;
- ctx->funcs = copy_object(item.value).data.array;
+ ctx->funcs = copy_object(item.value, NULL).data.array;
}
}
diff --git a/src/nvim/context.h b/src/nvim/context.h
index c2deca12c9..7a1224d876 100644
--- a/src/nvim/context.h
+++ b/src/nvim/context.h
@@ -2,9 +2,11 @@
#define NVIM_CONTEXT_H
#include <msgpack.h>
+#include <msgpack/sbuffer.h>
+#include <stddef.h>
+#include "klib/kvec.h"
#include "nvim/api/private/defs.h"
-#include "nvim/lib/kvec.h"
typedef struct {
msgpack_sbuffer regs; ///< Registers.
diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c
index d4670dedc7..b1dbc68ea3 100644
--- a/src/nvim/cursor.c
+++ b/src/nvim/cursor.c
@@ -1,24 +1,31 @@
// 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 <string.h>
#include "nvim/ascii.h"
#include "nvim/assert.h"
+#include "nvim/buffer_defs.h"
#include "nvim/change.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/drawscreen.h"
-#include "nvim/extmark.h"
#include "nvim/fold.h"
+#include "nvim/globals.h"
+#include "nvim/macros.h"
#include "nvim/mark.h"
+#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/plines.h"
+#include "nvim/pos.h"
#include "nvim/state.h"
+#include "nvim/types.h"
#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -67,7 +74,7 @@ int coladvance_force(colnr_T wcol)
/// 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)),
+/// 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
@@ -101,10 +108,10 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a
|| (VIsual_active && *p_sel != 'o')
|| ((get_ve_flags() & VE_ONEMORE) && wcol < MAXCOL);
- char_u *line = ml_get_buf(curbuf, pos->lnum, false);
+ char *line = ml_get_buf(curbuf, pos->lnum, false);
if (wcol >= MAXCOL) {
- idx = (int)STRLEN(line) - 1 + one_more;
+ idx = (int)strlen(line) - 1 + one_more;
col = wcol;
if ((addspaces || finetune) && !VIsual_active) {
@@ -137,14 +144,18 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a
}
}
- char_u *ptr = line;
- while (col <= wcol && *ptr != NUL) {
+ chartabsize_T cts;
+ init_chartabsize_arg(&cts, curwin, pos->lnum, 0, line, line);
+ while (cts.cts_vcol <= wcol && *cts.cts_ptr != NUL) {
// Count a tab for what it's worth (if list mode not on)
- csize = win_lbr_chartabsize(curwin, line, ptr, col, &head);
- MB_PTR_ADV(ptr);
- col += csize;
+ csize = win_lbr_chartabsize(&cts, &head);
+ MB_PTR_ADV(cts.cts_ptr);
+ cts.cts_vcol += csize;
}
- idx = (int)(ptr - line);
+ col = cts.cts_vcol;
+ idx = (int)(cts.cts_ptr - line);
+ clear_chartabsize_arg(&cts);
+
// 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
@@ -167,19 +178,19 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a
int correct = wcol - col;
size_t newline_size;
STRICT_ADD(idx, correct, &newline_size, size_t);
- char_u *newline = xmallocz(newline_size);
+ char *newline = xmallocz(newline_size);
memcpy(newline, line, (size_t)idx);
memset(newline + idx, ' ', (size_t)correct);
- ml_replace(pos->lnum, (char *)newline, false);
+ ml_replace(pos->lnum, newline, false);
inserted_bytes(pos->lnum, (colnr_T)idx, 0, correct);
idx += correct;
col = wcol;
} else {
// Break a tab
- int linelen = (int)STRLEN(line);
+ int linelen = (int)strlen(line);
int correct = wcol - col - csize + 1; // negative!!
- char_u *newline;
+ char *newline;
if (-correct > csize) {
return FAIL;
@@ -197,7 +208,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, (char *)newline, false);
+ ml_replace(pos->lnum, newline, false);
inserted_bytes(pos->lnum, idx, 1, csize);
idx += (csize - 1 + correct);
col += correct;
@@ -304,8 +315,8 @@ void check_pos(buf_T *buf, pos_T *pos)
}
if (pos->col > 0) {
- char_u *line = ml_get_buf(buf, pos->lnum, false);
- colnr_T len = (colnr_T)STRLEN(line);
+ char *line = ml_get_buf(buf, pos->lnum, false);
+ colnr_T len = (colnr_T)strlen(line);
if (pos->col > len) {
pos->col = len;
}
@@ -342,7 +353,7 @@ void check_cursor_col_win(win_T *win)
colnr_T oldcoladd = win->w_cursor.col + win->w_cursor.coladd;
unsigned int cur_ve_flags = get_ve_flags();
- colnr_T 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) {
@@ -408,7 +419,7 @@ void check_visual_pos(void)
VIsual.col = 0;
VIsual.coladd = 0;
} else {
- int len = (int)STRLEN(ml_get(VIsual.lnum));
+ int len = (int)strlen(ml_get(VIsual.lnum));
if (VIsual.col > len) {
VIsual.col = len;
@@ -445,12 +456,13 @@ bool leftcol_changed(void)
// 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)) {
+ long siso = get_sidescrolloff_value(curwin);
+ if (curwin->w_virtcol > (colnr_T)(lastcol - siso)) {
retval = true;
- coladvance((colnr_T)(lastcol - p_siso));
- } else if (curwin->w_virtcol < curwin->w_leftcol + p_siso) {
+ coladvance((colnr_T)(lastcol - siso));
+ } else if (curwin->w_virtcol < curwin->w_leftcol + siso) {
retval = true;
- coladvance((colnr_T)(curwin->w_leftcol + p_siso));
+ coladvance((colnr_T)(curwin->w_leftcol + siso));
}
// If the start of the character under the cursor is not on the screen,
@@ -471,13 +483,13 @@ bool leftcol_changed(void)
if (retval) {
curwin->w_set_curswant = true;
}
- redraw_later(curwin, NOT_VALID);
+ redraw_later(curwin, UPD_NOT_VALID);
return retval;
}
int gchar_cursor(void)
{
- return utf_ptr2char((char *)get_cursor_pos_ptr());
+ return utf_ptr2char(get_cursor_pos_ptr());
}
/// Write a character at the current cursor position.
@@ -485,18 +497,17 @@ int gchar_cursor(void)
void pchar_cursor(char_u c)
{
*(ml_get_buf(curbuf, curwin->w_cursor.lnum, true)
- + curwin->w_cursor.col) = c;
+ + curwin->w_cursor.col) = (char)c;
}
/// @return pointer to cursor line.
-char_u *get_cursor_line_ptr(void)
+char *get_cursor_line_ptr(void)
{
return ml_get_buf(curbuf, curwin->w_cursor.lnum, false);
}
/// @return pointer to cursor position.
-char_u *get_cursor_pos_ptr(void)
+char *get_cursor_pos_ptr(void)
{
- return ml_get_buf(curbuf, curwin->w_cursor.lnum, false) +
- curwin->w_cursor.col;
+ return ml_get_buf(curbuf, curwin->w_cursor.lnum, false) + curwin->w_cursor.col;
}
diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c
index 9c33b1a806..f21e632036 100644
--- a/src/nvim/cursor_shape.c
+++ b/src/nvim/cursor_shape.c
@@ -1,15 +1,23 @@
// 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 <string.h>
+#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
#include "nvim/charset.h"
#include "nvim/cursor_shape.h"
#include "nvim/ex_getln.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
#include "nvim/highlight_group.h"
+#include "nvim/log.h"
+#include "nvim/macros.h"
+#include "nvim/memory.h"
+#include "nvim/option_defs.h"
#include "nvim/strings.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
@@ -19,8 +27,7 @@
#endif
/// Handling of cursor and mouse pointer shapes in various modes.
-cursorentry_T shape_table[SHAPE_IDX_COUNT] =
-{
+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 },
@@ -120,7 +127,7 @@ char *parse_shape_opt(int what)
}
}
// Repeat for all comma separated parts.
- char *modep = (char *)p_guicursor;
+ char *modep = p_guicursor;
while (modep != NULL && *modep != NUL) {
colonp = vim_strchr(modep, ':');
commap = vim_strchr(modep, ',');
@@ -220,7 +227,7 @@ char *parse_shape_opt(int what)
endp = vim_strchr(p, '-');
if (commap == NULL) { // last part
if (endp == NULL) {
- endp = p + STRLEN(p); // find end of part
+ endp = p + strlen(p); // find end of part
}
} else if (endp > commap || endp == NULL) {
endp = commap;
diff --git a/src/nvim/debugger.c b/src/nvim/debugger.c
index a061bd961b..f7e70a78ce 100644
--- a/src/nvim/debugger.c
+++ b/src/nvim/debugger.c
@@ -5,17 +5,33 @@
///
/// Vim script debugger functions
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
#include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/debugger.h"
#include "nvim/drawscreen.h"
#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
+#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
#include "nvim/globals.h"
+#include "nvim/keycodes.h"
+#include "nvim/macros.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
#include "nvim/os/os.h"
+#include "nvim/path.h"
#include "nvim/pos.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
@@ -33,7 +49,7 @@ static char *debug_newval = NULL;
struct debuggy {
int dbg_nr; ///< breakpoint number
int dbg_type; ///< DBG_FUNC or DBG_FILE or DBG_EXPR
- char_u *dbg_name; ///< function, expression or file name
+ char *dbg_name; ///< function, expression or file name
regprog_T *dbg_prog; ///< regexp program
linenr_T dbg_lnum; ///< line number in function or file
int dbg_forceit; ///< ! used
@@ -47,7 +63,7 @@ struct debuggy {
/// Debug mode. Repeatedly get Ex commands, until told to continue normal
/// execution.
-void do_debug(char_u *cmd)
+void do_debug(char *cmd)
{
int save_msg_scroll = msg_scroll;
int save_State = State;
@@ -60,7 +76,7 @@ void do_debug(char_u *cmd)
bool typeahead_saved = false;
int save_ignore_script = 0;
int n;
- char_u *cmdline = NULL;
+ char *cmdline = NULL;
char *p;
char *tail = NULL;
static int last_cmd = 0;
@@ -114,6 +130,7 @@ void do_debug(char_u *cmd)
for (;;) {
msg_scroll = true;
need_wait_return = false;
+
// Save the current typeahead buffer and replace it with an empty one.
// This makes sure we get input from the user here and don't interfere
// with the commands being executed. Reset "ex_normal_busy" to avoid
@@ -128,10 +145,15 @@ void do_debug(char_u *cmd)
ignore_script = true;
}
+ // don't debug any function call, e.g. from an expression mapping
+ n = debug_break_level;
+ debug_break_level = -1;
+
xfree(cmdline);
- cmdline = (char_u *)getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL,
- CALLBACK_NONE);
+ cmdline = getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL,
+ CALLBACK_NONE);
+ debug_break_level = n;
if (typeahead_saved) {
restore_typeahead(&typeaheadbuf);
ignore_script = save_ignore_script;
@@ -144,7 +166,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((char *)cmdline);
+ p = skipwhite(cmdline);
if (*p != NUL) {
switch (*p) {
case 'c':
@@ -246,7 +268,7 @@ void do_debug(char_u *cmd)
do_showbacktrace(cmd);
} else {
p = skipwhite(p);
- do_setdebugtracelevel((char_u *)p);
+ do_setdebugtracelevel(p);
}
continue;
case CMD_UP:
@@ -266,7 +288,7 @@ void do_debug(char_u *cmd)
// don't debug this command
n = debug_break_level;
debug_break_level = -1;
- (void)do_cmdline((char *)cmdline, getexline, NULL, DOCMD_VERBOSE|DOCMD_EXCRESET);
+ (void)do_cmdline(cmdline, getexline, NULL, DOCMD_VERBOSE|DOCMD_EXCRESET);
debug_break_level = n;
}
lines_left = Rows - 1;
@@ -275,7 +297,7 @@ void do_debug(char_u *cmd)
RedrawingDisabled--;
no_wait_return--;
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
need_wait_return = false;
msg_scroll = save_msg_scroll;
lines_left = Rows - 1;
@@ -295,20 +317,22 @@ static int get_maxbacktrace_level(char *sname)
{
int maxbacktrace = 0;
- if (sname != NULL) {
- char *p = sname;
- char *q;
- while ((q = strstr(p, "..")) != NULL) {
- p = q + 2;
- maxbacktrace++;
- }
+ if (sname == NULL) {
+ return 0;
+ }
+
+ char *p = sname;
+ char *q;
+ while ((q = strstr(p, "..")) != NULL) {
+ p = q + 2;
+ maxbacktrace++;
}
return maxbacktrace;
}
-static void do_setdebugtracelevel(char_u *arg)
+static void do_setdebugtracelevel(char *arg)
{
- int level = atoi((char *)arg);
+ int level = atoi(arg);
if (*arg == '+' || level < 0) {
debug_backtrace_level += level;
} else {
@@ -335,7 +359,7 @@ static void do_checkbacktracelevel(void)
}
}
-static void do_showbacktrace(char_u *cmd)
+static void do_showbacktrace(char *cmd)
{
char *sname = estack_sfile(ESTACK_NONE);
int max = get_maxbacktrace_level(sname);
@@ -379,7 +403,7 @@ void ex_debug(exarg_T *eap)
debug_break_level = debug_break_level_save;
}
-static char_u *debug_breakpoint_name = NULL;
+static char *debug_breakpoint_name = NULL;
static linenr_T debug_breakpoint_lnum;
/// When debugging or a breakpoint is set on a skipped command, no debug prompt
@@ -388,7 +412,7 @@ static linenr_T debug_breakpoint_lnum;
/// a skipped command decides itself that a debug prompt should be displayed, it
/// can do so by calling dbg_check_skipped().
static int debug_skipped;
-static char_u *debug_skipped_name;
+static char *debug_skipped_name;
/// Go to debug mode when a breakpoint was encountered or "ex_nesting_level" is
/// at or below the break level. But only when the line is actually
@@ -402,8 +426,8 @@ void dbg_check_breakpoint(exarg_T *eap)
if (!eap->skip) {
char *p;
// replace K_SNR with "<SNR>"
- if (debug_breakpoint_name[0] == K_SPECIAL
- && debug_breakpoint_name[1] == KS_EXTRA
+ if ((uint8_t)debug_breakpoint_name[0] == K_SPECIAL
+ && (uint8_t)debug_breakpoint_name[1] == KS_EXTRA
&& debug_breakpoint_name[2] == KE_SNR) {
p = "<SNR>";
} else {
@@ -414,7 +438,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((char_u *)eap->cmd);
+ do_debug(eap->cmd);
} else {
debug_skipped = true;
debug_skipped_name = debug_breakpoint_name;
@@ -422,7 +446,7 @@ void dbg_check_breakpoint(exarg_T *eap)
}
} else if (ex_nesting_level <= debug_break_level) {
if (!eap->skip) {
- do_debug((char_u *)eap->cmd);
+ do_debug(eap->cmd);
} else {
debug_skipped = true;
debug_skipped_name = NULL;
@@ -436,20 +460,21 @@ void dbg_check_breakpoint(exarg_T *eap)
/// @return true when the debug mode is entered this time.
bool dbg_check_skipped(exarg_T *eap)
{
- if (debug_skipped) {
- // Save the value of got_int and reset it. We don't want a previous
- // interruption cause flushing the input buffer.
- int prev_got_int = got_int;
- got_int = false;
- debug_breakpoint_name = debug_skipped_name;
- // eap->skip is true
- eap->skip = false;
- dbg_check_breakpoint(eap);
- eap->skip = true;
- got_int |= prev_got_int;
- return true;
- }
- return false;
+ if (!debug_skipped) {
+ return false;
+ }
+
+ // Save the value of got_int and reset it. We don't want a previous
+ // interruption cause flushing the input buffer.
+ int prev_got_int = got_int;
+ got_int = false;
+ debug_breakpoint_name = debug_skipped_name;
+ // eap->skip is true
+ eap->skip = false;
+ dbg_check_breakpoint(eap);
+ eap->skip = true;
+ got_int |= prev_got_int;
+ return true;
}
static garray_T dbg_breakp = { 0, 0, sizeof(struct debuggy), 4, NULL };
@@ -470,7 +495,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((char *)bp->dbg_name);
+ typval_T *const tv = eval_expr(bp->dbg_name);
emsg_off--;
return tv;
}
@@ -482,9 +507,9 @@ static typval_T *eval_expr_no_emsg(struct debuggy *const bp)
///
/// @param arg
/// @param gap either &dbg_breakp or &prof_ga
-static int dbg_parsearg(char_u *arg, garray_T *gap)
+static int dbg_parsearg(char *arg, garray_T *gap)
{
- char *p = (char *)arg;
+ char *p = arg;
char *q;
bool here = false;
@@ -493,18 +518,18 @@ static int dbg_parsearg(char_u *arg, garray_T *gap)
struct debuggy *bp = &DEBUGGY(gap, gap->ga_len);
// Find "func" or "file".
- if (STRNCMP(p, "func", 4) == 0) {
+ if (strncmp(p, "func", 4) == 0) {
bp->dbg_type = DBG_FUNC;
- } else if (STRNCMP(p, "file", 4) == 0) {
+ } else if (strncmp(p, "file", 4) == 0) {
bp->dbg_type = DBG_FILE;
- } else if (gap != &prof_ga && STRNCMP(p, "here", 4) == 0) {
+ } else if (gap != &prof_ga && strncmp(p, "here", 4) == 0) {
if (curbuf->b_ffname == NULL) {
emsg(_(e_noname));
return FAIL;
}
bp->dbg_type = DBG_FILE;
here = true;
- } else if (gap != &prof_ga && STRNCMP(p, "expr", 4) == 0) {
+ } else if (gap != &prof_ga && strncmp(p, "expr", 4) == 0) {
bp->dbg_type = DBG_EXPR;
} else {
semsg(_(e_invarg2), p);
@@ -531,11 +556,11 @@ static int dbg_parsearg(char_u *arg, garray_T *gap)
}
if (bp->dbg_type == DBG_FUNC) {
- bp->dbg_name = vim_strsave((char_u *)p);
+ bp->dbg_name = xstrdup(p);
} else if (here) {
- bp->dbg_name = vim_strsave((char_u *)curbuf->b_ffname);
+ bp->dbg_name = xstrdup(curbuf->b_ffname);
} else if (bp->dbg_type == DBG_EXPR) {
- bp->dbg_name = vim_strsave((char_u *)p);
+ bp->dbg_name = xstrdup(p);
bp->dbg_val = eval_expr_no_emsg(bp);
} else {
// Expand the file name in the same way as do_source(). This means
@@ -551,10 +576,10 @@ static int dbg_parsearg(char_u *arg, garray_T *gap)
return FAIL;
}
if (*p != '*') {
- bp->dbg_name = (char_u *)fix_fname(p);
+ bp->dbg_name = fix_fname(p);
xfree(p);
} else {
- bp->dbg_name = (char_u *)p;
+ bp->dbg_name = p;
}
}
@@ -572,33 +597,35 @@ void ex_breakadd(exarg_T *eap)
gap = &prof_ga;
}
- if (dbg_parsearg((char_u *)eap->arg, gap) == OK) {
- struct debuggy *bp = &DEBUGGY(gap, gap->ga_len);
- bp->dbg_forceit = eap->forceit;
+ if (dbg_parsearg(eap->arg, gap) != OK) {
+ return;
+ }
- if (bp->dbg_type != DBG_EXPR) {
- 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);
+ struct debuggy *bp = &DEBUGGY(gap, gap->ga_len);
+ bp->dbg_forceit = eap->forceit;
+
+ if (bp->dbg_type != DBG_EXPR) {
+ char *pat = file_pat_to_reg_pat(bp->dbg_name, NULL, NULL, false);
+ if (pat != NULL) {
+ bp->dbg_prog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
+ xfree(pat);
+ }
+ if (pat == NULL || bp->dbg_prog == NULL) {
+ xfree(bp->dbg_name);
+ } else {
+ if (bp->dbg_lnum == 0) { // default line number is 1
+ bp->dbg_lnum = 1;
}
- if (pat == NULL || bp->dbg_prog == NULL) {
- xfree(bp->dbg_name);
- } else {
- if (bp->dbg_lnum == 0) { // default line number is 1
- bp->dbg_lnum = 1;
- }
- if (eap->cmdidx != CMD_profile) {
- DEBUGGY(gap, gap->ga_len).dbg_nr = ++last_breakp;
- debug_tick++;
- }
- gap->ga_len++;
+ if (eap->cmdidx != CMD_profile) {
+ DEBUGGY(gap, gap->ga_len).dbg_nr = ++last_breakp;
+ debug_tick++;
}
- } else {
- // DBG_EXPR
- DEBUGGY(gap, gap->ga_len++).dbg_nr = ++last_breakp;
- debug_tick++;
+ gap->ga_len++;
}
+ } else {
+ // DBG_EXPR
+ DEBUGGY(gap, gap->ga_len++).dbg_nr = ++last_breakp;
+ debug_tick++;
}
}
@@ -639,14 +666,14 @@ void ex_breakdel(exarg_T *eap)
del_all = true;
} else {
// ":breakdel {func|file|expr} [lnum] {name}"
- if (dbg_parsearg((char_u *)eap->arg, gap) == FAIL) {
+ if (dbg_parsearg(eap->arg, gap) == FAIL) {
return;
}
bp = &DEBUGGY(gap, gap->ga_len);
for (int i = 0; i < gap->ga_len; i++) {
bpi = &DEBUGGY(gap, i);
if (bp->dbg_type == bpi->dbg_type
- && STRCMP(bp->dbg_name, bpi->dbg_name) == 0
+ && strcmp(bp->dbg_name, bpi->dbg_name) == 0
&& (bp->dbg_lnum == bpi->dbg_lnum
|| (bp->dbg_lnum == 0
&& (best_lnum == 0
@@ -660,32 +687,33 @@ void ex_breakdel(exarg_T *eap)
if (todel < 0) {
semsg(_("E161: Breakpoint not found: %s"), eap->arg);
- } else {
- while (!GA_EMPTY(gap)) {
- xfree(DEBUGGY(gap, todel).dbg_name);
- if (DEBUGGY(gap, todel).dbg_type == DBG_EXPR
- && DEBUGGY(gap, todel).dbg_val != NULL) {
- tv_free(DEBUGGY(gap, todel).dbg_val);
- }
- vim_regfree(DEBUGGY(gap, todel).dbg_prog);
- gap->ga_len--;
- if (todel < gap->ga_len) {
- memmove(&DEBUGGY(gap, todel), &DEBUGGY(gap, todel + 1),
- (size_t)(gap->ga_len - todel) * sizeof(struct debuggy));
- }
- if (eap->cmdidx == CMD_breakdel) {
- debug_tick++;
- }
- if (!del_all) {
- break;
- }
- }
+ return;
+ }
- // If all breakpoints were removed clear the array.
- if (GA_EMPTY(gap)) {
- ga_clear(gap);
+ while (!GA_EMPTY(gap)) {
+ xfree(DEBUGGY(gap, todel).dbg_name);
+ if (DEBUGGY(gap, todel).dbg_type == DBG_EXPR
+ && DEBUGGY(gap, todel).dbg_val != NULL) {
+ tv_free(DEBUGGY(gap, todel).dbg_val);
+ }
+ vim_regfree(DEBUGGY(gap, todel).dbg_prog);
+ gap->ga_len--;
+ if (todel < gap->ga_len) {
+ memmove(&DEBUGGY(gap, todel), &DEBUGGY(gap, todel + 1),
+ (size_t)(gap->ga_len - todel) * sizeof(struct debuggy));
+ }
+ if (eap->cmdidx == CMD_breakdel) {
+ debug_tick++;
+ }
+ if (!del_all) {
+ break;
}
}
+
+ // If all breakpoints were removed clear the array.
+ if (GA_EMPTY(gap)) {
+ ga_clear(gap);
+ }
}
/// ":breaklist".
@@ -693,21 +721,22 @@ void ex_breaklist(exarg_T *eap)
{
if (GA_EMPTY(&dbg_breakp)) {
msg(_("No breakpoints defined"));
- } else {
- for (int i = 0; i < dbg_breakp.ga_len; i++) {
- struct debuggy *bp = &BREAKP(i);
- if (bp->dbg_type == DBG_FILE) {
- home_replace(NULL, (char *)bp->dbg_name, (char *)NameBuff, MAXPATHL, true);
- }
- if (bp->dbg_type != DBG_EXPR) {
- smsg(_("%3d %s %s line %" PRId64),
- bp->dbg_nr,
- bp->dbg_type == DBG_FUNC ? "func" : "file",
- bp->dbg_type == DBG_FUNC ? bp->dbg_name : NameBuff,
- (int64_t)bp->dbg_lnum);
- } else {
- smsg(_("%3d expr %s"), bp->dbg_nr, bp->dbg_name);
- }
+ return;
+ }
+
+ for (int i = 0; i < dbg_breakp.ga_len; i++) {
+ struct debuggy *bp = &BREAKP(i);
+ if (bp->dbg_type == DBG_FILE) {
+ home_replace(NULL, bp->dbg_name, (char *)NameBuff, MAXPATHL, true);
+ }
+ if (bp->dbg_type != DBG_EXPR) {
+ smsg(_("%3d %s %s line %" PRId64),
+ bp->dbg_nr,
+ bp->dbg_type == DBG_FUNC ? "func" : "file",
+ bp->dbg_type == DBG_FUNC ? bp->dbg_name : NameBuff,
+ (int64_t)bp->dbg_lnum);
+ } else {
+ smsg(_("%3d expr %s"), bp->dbg_nr, bp->dbg_name);
}
}
}
@@ -718,7 +747,7 @@ void ex_breaklist(exarg_T *eap)
/// @param file true for a file, false for a function
/// @param fname file or function name
/// @param after after this line number
-linenr_T dbg_find_breakpoint(bool file, char_u *fname, linenr_T after)
+linenr_T dbg_find_breakpoint(bool file, char *fname, linenr_T after)
{
return debuggy_find(file, fname, after, &dbg_breakp, NULL);
}
@@ -728,7 +757,7 @@ linenr_T dbg_find_breakpoint(bool file, char_u *fname, linenr_T after)
/// @param fp[out] forceit
///
/// @returns true if profiling is on for a function or sourced file.
-bool has_profiling(bool file, char_u *fname, bool *fp)
+bool has_profiling(bool file, char *fname, bool *fp)
{
return debuggy_find(file, fname, (linenr_T)0, &prof_ga, fp)
!= (linenr_T)0;
@@ -741,11 +770,11 @@ bool has_profiling(bool file, char_u *fname, bool *fp)
/// @param after after this line number
/// @param gap either &dbg_breakp or &prof_ga
/// @param fp if not NULL: return forceit
-static linenr_T debuggy_find(bool file, char_u *fname, linenr_T after, garray_T *gap, bool *fp)
+static linenr_T debuggy_find(bool file, char *fname, linenr_T after, garray_T *gap, bool *fp)
{
struct debuggy *bp;
linenr_T lnum = 0;
- char_u *name = fname;
+ char *name = fname;
int prev_got_int;
// Return quickly when there are no breakpoints.
@@ -754,8 +783,8 @@ static linenr_T debuggy_find(bool file, char_u *fname, linenr_T after, garray_T
}
// Replace K_SNR in function name with "<SNR>".
- if (!file && fname[0] == K_SPECIAL) {
- name = xmalloc(STRLEN(fname) + 3);
+ if (!file && (uint8_t)fname[0] == K_SPECIAL) {
+ name = xmalloc(strlen(fname) + 3);
STRCPY(name, "<SNR>");
STRCPY(name + 5, fname + 3);
}
@@ -786,26 +815,26 @@ static linenr_T debuggy_find(bool file, char_u *fname, linenr_T after, garray_T
typval_T *const tv = eval_expr_no_emsg(bp);
if (tv != NULL) {
if (bp->dbg_val == NULL) {
- debug_oldval = typval_tostring(NULL);
+ debug_oldval = typval_tostring(NULL, true);
bp->dbg_val = tv;
- debug_newval = typval_tostring(bp->dbg_val);
+ debug_newval = typval_tostring(bp->dbg_val, true);
line = true;
} else {
if (typval_compare(tv, bp->dbg_val, EXPR_IS, false) == OK
&& tv->vval.v_number == false) {
line = true;
- debug_oldval = typval_tostring(bp->dbg_val);
+ debug_oldval = typval_tostring(bp->dbg_val, true);
// Need to evaluate again, typval_compare() overwrites "tv".
typval_T *const v = eval_expr_no_emsg(bp);
- debug_newval = typval_tostring(v);
+ debug_newval = typval_tostring(v, true);
tv_free(bp->dbg_val);
bp->dbg_val = v;
}
tv_free(tv);
}
} else if (bp->dbg_val != NULL) {
- debug_oldval = typval_tostring(bp->dbg_val);
- debug_newval = typval_tostring(NULL);
+ debug_oldval = typval_tostring(bp->dbg_val, true);
+ debug_newval = typval_tostring(NULL, true);
tv_free(bp->dbg_val);
bp->dbg_val = NULL;
line = true;
@@ -825,7 +854,7 @@ static linenr_T debuggy_find(bool file, char_u *fname, linenr_T after, garray_T
}
/// Called when a breakpoint was encountered.
-void dbg_breakpoint(char_u *name, linenr_T lnum)
+void dbg_breakpoint(char *name, linenr_T lnum)
{
// We need to check if this line is actually executed in do_one_cmd()
debug_breakpoint_name = name;
diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c
index a93fb599c4..63c55ec602 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -1,16 +1,18 @@
// 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 <assert.h>
+
#include "nvim/buffer.h"
#include "nvim/decoration.h"
#include "nvim/drawscreen.h"
#include "nvim/extmark.h"
+#include "nvim/fold.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
-#include "nvim/lua/executor.h"
-#include "nvim/move.h"
-#include "nvim/vim.h"
+#include "nvim/memory.h"
+#include "nvim/pos.h"
+#include "nvim/sign_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "decoration.c.generated.h"
@@ -69,18 +71,21 @@ void bufhl_add_hl_pos_offset(buf_T *buf, int src_id, int hl_id, lpos_T pos_start
void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor)
{
if (row2 >= row1) {
- if (!decor || decor->hl_id || decor_has_sign(decor) || decor->conceal) {
+ if (!decor
+ || decor->hl_id
+ || decor_has_sign(decor)
+ || decor->conceal
+ || decor->spell != kNone) {
redraw_buf_range_later(buf, row1 + 1, row2 + 1);
}
}
if (decor && decor_virt_pos(*decor)) {
- redraw_buf_line_later(buf, row1 + 1);
+ redraw_buf_line_later(buf, row1 + 1, false);
}
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)));
+ redraw_buf_line_later(buf, row1 + 1 + (decor->virt_lines_above?0:1), true);
}
}
@@ -116,6 +121,11 @@ void decor_free(Decoration *decor)
}
}
+void decor_state_free(DecorState *state)
+{
+ xfree(state->active.items);
+}
+
void clear_virttext(VirtText *text)
{
for (size_t i = 0; i < kv_size(*text); i++) {
@@ -165,13 +175,12 @@ 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;
}
+ 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)
@@ -306,6 +315,7 @@ next_mark:
bool conceal = 0;
int conceal_char = 0;
int conceal_attr = 0;
+ TriState spell = kNone;
for (size_t i = 0; i < kv_size(state->active); i++) {
DecorRange item = kv_A(state->active, i);
@@ -339,6 +349,9 @@ next_mark:
conceal_attr = item.attr_id;
}
}
+ if (active && item.decor.spell != kNone) {
+ spell = item.decor.spell;
+ }
if ((item.start_row == state->row && item.start_col <= col)
&& decor_virt_pos(item.decor)
&& item.decor.virt_text_pos == kVTOverlay && item.win_col == -1) {
@@ -355,6 +368,7 @@ next_mark:
state->conceal = conceal;
state->conceal_char = conceal_char;
state->conceal_attr = conceal_attr;
+ state->spell = spell;
return attr;
}
@@ -537,7 +551,8 @@ void decor_add_ephemeral(int start_row, int start_col, int end_row, int end_col,
decor_add(&decor_state, start_row, start_col, end_row, end_col, decor, true, ns_id, mark_id);
}
-int decor_virt_lines(win_T *wp, linenr_T lnum, VirtLines *lines)
+/// @param has_fold whether line "lnum" has a fold, or kNone when not calculated yet
+int decor_virt_lines(win_T *wp, linenr_T lnum, VirtLines *lines, TriState has_fold)
{
buf_T *buf = wp->w_buffer;
if (!buf->b_virt_line_blocks) {
@@ -547,10 +562,14 @@ int decor_virt_lines(win_T *wp, linenr_T lnum, VirtLines *lines)
}
int virt_lines = 0;
- int row = (int)MAX(lnum - 2, 0);
+ int row = MAX(lnum - 2, 0);
int end_row = (int)lnum;
MarkTreeIter itr[1] = { 0 };
marktree_itr_get(buf->b_marktree, row, 0, itr);
+ bool below_fold = lnum > 1 && hasFoldingWin(wp, lnum - 1, NULL, NULL, true, NULL);
+ if (has_fold == kNone) {
+ has_fold = hasFoldingWin(wp, lnum, NULL, NULL, true, NULL);
+ }
while (true) {
mtkey_t mark = marktree_itr_current(itr);
if (mark.pos.row < 0 || mark.pos.row >= end_row) {
@@ -558,9 +577,10 @@ int decor_virt_lines(win_T *wp, linenr_T lnum, VirtLines *lines)
} else if (marktree_decor_level(mark) < kDecorLevelVirtLine) {
goto next_mark;
}
- bool above = mark.pos.row > (int)(lnum - 2);
+ bool above = mark.pos.row > (lnum - 2);
+ bool has_fold_cur = above ? has_fold : below_fold;
Decoration *decor = mark.decor_full;
- if (decor && decor->virt_lines_above == above) {
+ if (!has_fold_cur && decor && decor->virt_lines_above == above) {
virt_lines += (int)kv_size(decor->virt_lines);
if (lines) {
kv_splice(*lines, decor->virt_lines);
diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h
index 8f28442d41..c9ec8ede7f 100644
--- a/src/nvim/decoration.h
+++ b/src/nvim/decoration.h
@@ -1,9 +1,17 @@
#ifndef NVIM_DECORATION_H
#define NVIM_DECORATION_H
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "klib/kvec.h"
#include "nvim/buffer_defs.h"
#include "nvim/extmark_defs.h"
+#include "nvim/macros.h"
+#include "nvim/marktree.h"
#include "nvim/pos.h"
+#include "nvim/types.h"
// actual Decoration data is in extmark_defs.h
@@ -28,7 +36,6 @@ typedef enum {
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;
@@ -46,11 +53,12 @@ struct Decoration {
bool hl_eol;
bool virt_lines_above;
bool conceal;
+ TriState spell;
// 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;
+ char *sign_text;
int sign_hl_id;
int number_hl_id;
int line_hl_id;
@@ -61,8 +69,8 @@ struct Decoration {
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, NULL, 0, 0, 0, 0, 0, false }
+ kHlModeUnknown, false, false, false, false, kNone, \
+ DECOR_PRIORITY_BASE, 0, 0, NULL, 0, 0, 0, 0, 0, false }
typedef struct {
int start_row;
@@ -90,6 +98,8 @@ typedef struct {
bool conceal;
int conceal_char;
int conceal_attr;
+
+ TriState spell;
} DecorState;
EXTERN DecorState decor_state INIT(= { 0 });
diff --git a/src/nvim/decoration_provider.c b/src/nvim/decoration_provider.c
index 14c1238fa4..ed21474935 100644
--- a/src/nvim/decoration_provider.c
+++ b/src/nvim/decoration_provider.c
@@ -1,20 +1,30 @@
// 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 <stdio.h>
+#include <string.h>
+
+#include "klib/kvec.h"
+#include "lauxlib.h"
#include "nvim/api/extmark.h"
+#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
-#include "nvim/buffer.h"
-#include "nvim/decoration.h"
+#include "nvim/buffer_defs.h"
#include "nvim/decoration_provider.h"
+#include "nvim/globals.h"
#include "nvim/highlight.h"
+#include "nvim/log.h"
#include "nvim/lua/executor.h"
+#include "nvim/memory.h"
+#include "nvim/pos.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, false }
+ LUA_NOREF, -1, false, false }
static bool decor_provider_invoke(NS ns_id, const char *name, LuaRef ref, Array args,
bool default_true, char **perr)
@@ -47,11 +57,33 @@ static bool decor_provider_invoke(NS ns_id, const char *name, LuaRef ref, Array
return false;
}
+void decor_providers_invoke_spell(win_T *wp, int start_row, int start_col, int end_row, int end_col,
+ char **err)
+{
+ for (size_t i = 0; i < kv_size(decor_providers); i++) {
+ DecorProvider *p = &kv_A(decor_providers, i);
+ if (!p->active) {
+ continue;
+ }
+
+ if (p->spell_nav != LUA_NOREF) {
+ MAXSIZE_TEMP_ARRAY(args, 6);
+ ADD_C(args, INTEGER_OBJ(wp->handle));
+ ADD_C(args, INTEGER_OBJ(wp->w_buffer->handle));
+ ADD_C(args, INTEGER_OBJ(start_row));
+ ADD_C(args, INTEGER_OBJ(start_col));
+ ADD_C(args, INTEGER_OBJ(end_row));
+ ADD_C(args, INTEGER_OBJ(end_col));
+ decor_provider_invoke(p->ns_id, "spell", p->spell_nav, args, true, err);
+ }
+ }
+}
+
/// 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)
+void decor_providers_start(DecorProviders *providers, char **err)
{
kvi_init(*providers);
@@ -65,7 +97,6 @@ void decor_providers_start(DecorProviders *providers, int type, char **err)
if (p->redraw_start != LUA_NOREF) {
MAXSIZE_TEMP_ARRAY(args, 2);
ADD_C(args, INTEGER_OBJ((int)display_tick));
- ADD_C(args, INTEGER_OBJ(type));
active = decor_provider_invoke(p->ns_id, "start", p->redraw_start, args, true, err);
} else {
active = true;
@@ -116,8 +147,8 @@ void decor_providers_invoke_win(win_T *wp, DecorProviders *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)
+void decor_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);
@@ -172,6 +203,25 @@ void decor_providers_invoke_end(DecorProviders *providers, char **err)
}
}
+/// Mark all cached state of per-namespace highlights as invalid. Revalidate
+/// current namespace.
+///
+/// Expensive! Should on be called by an already throttled validity check
+/// like highlight_changed() (throttled to the next redraw or mode change)
+void decor_provider_invalidate_hl(void)
+{
+ size_t len = kv_size(decor_providers);
+ for (size_t i = 0; i < len; i++) {
+ DecorProvider *item = &kv_A(decor_providers, i);
+ item->hl_cached = false;
+ }
+
+ if (ns_hl_active) {
+ ns_hl_active = -1;
+ hl_check_ns();
+ }
+}
+
DecorProvider *get_decor_provider(NS ns_id, bool force)
{
assert(ns_id > 0);
@@ -215,6 +265,7 @@ void decor_provider_clear(DecorProvider *p)
NLUA_CLEAR_REF(p->redraw_win);
NLUA_CLEAR_REF(p->redraw_line);
NLUA_CLEAR_REF(p->redraw_end);
+ NLUA_CLEAR_REF(p->spell_nav);
p->active = false;
}
diff --git a/src/nvim/decoration_provider.h b/src/nvim/decoration_provider.h
index dd1ed6c581..b91ddabdfd 100644
--- a/src/nvim/decoration_provider.h
+++ b/src/nvim/decoration_provider.h
@@ -1,7 +1,12 @@
#ifndef NVIM_DECORATION_PROVIDER_H
#define NVIM_DECORATION_PROVIDER_H
+#include <stdbool.h>
+
+#include "klib/kvec.h"
#include "nvim/buffer_defs.h"
+#include "nvim/macros.h"
+#include "nvim/types.h"
typedef struct {
NS ns_id;
@@ -12,6 +17,7 @@ typedef struct {
LuaRef redraw_line;
LuaRef redraw_end;
LuaRef hl_def;
+ LuaRef spell_nav;
int hl_valid;
bool hl_cached;
} DecorProvider;
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index 5485d528f7..032de561b3 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -10,9 +10,15 @@
/// - Use the compiled-in xdiff library.
/// - Let 'diffexpr' do the work, using files.
+#include <assert.h>
+#include <ctype.h>
#include <inttypes.h>
#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "auto/config.h"
#include "nvim/ascii.h"
#include "nvim/autocmd.h"
#include "nvim/buffer.h"
@@ -23,9 +29,15 @@
#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/ex_cmds.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
+#include "nvim/extmark_defs.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
+#include "nvim/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/linematch.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
@@ -35,10 +47,13 @@
#include "nvim/normal.h"
#include "nvim/option.h"
#include "nvim/optionstr.h"
+#include "nvim/os/fs_defs.h"
#include "nvim/os/os.h"
#include "nvim/os/shell.h"
#include "nvim/path.h"
+#include "nvim/pos.h"
#include "nvim/strings.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/vim.h"
@@ -61,10 +76,12 @@ static bool diff_need_update = false; // ex_diffupdate needs to be called
#define DIFF_INTERNAL 0x200 // use internal xdiff algorithm
#define DIFF_CLOSE_OFF 0x400 // diffoff when closing window
#define DIFF_FOLLOWWRAP 0x800 // follow the wrap option
+#define DIFF_LINEMATCH 0x1000 // match most similar lines within diff
#define ALL_WHITE_DIFF (DIFF_IWHITE | DIFF_IWHITEALL | DIFF_IWHITEEOL)
static int diff_flags = DIFF_INTERNAL | DIFF_FILLER | DIFF_CLOSE_OFF;
static long diff_algorithm = 0;
+static int linematch_lines = 0;
#define LBUFLEN 50 // length of line in diff file
@@ -74,13 +91,13 @@ static TriState diff_a_works = kNone;
// used for diff input
typedef struct {
- char_u *din_fname; // used for external diff
+ char *din_fname; // used for external diff
mmfile_t din_mmfile; // used for internal diff
} diffin_T;
// used for diff result
typedef struct {
- char_u *dout_fname; // used for external diff
+ char *dout_fname; // used for external diff
garray_T dout_ga; // used for internal diff
} diffout_T;
@@ -100,6 +117,12 @@ typedef struct {
int dio_internal; // using internal diff
} diffio_T;
+typedef enum {
+ DIFF_ED,
+ DIFF_UNIFIED,
+ DIFF_NONE,
+} diffstyle_T;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "diff.c.generated.h"
#endif
@@ -120,7 +143,7 @@ void diff_buf_delete(buf_T *buf)
// don't redraw right away, more might change or buffer state
// is invalid right now
need_diff_redraw = true;
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
}
}
}
@@ -204,13 +227,7 @@ static void diff_buf_clear(void)
/// @return Its index or DB_COUNT if not found.
static int diff_buf_idx(buf_T *buf)
{
- int idx;
- for (idx = 0; idx < DB_COUNT; idx++) {
- if (curtab->tp_diffbuf[idx] == buf) {
- break;
- }
- }
- return idx;
+ return diff_buf_idx_tp(buf, curtab);
}
/// Find buffer "buf" in the list of diff buffers for tab page "tp".
@@ -309,8 +326,6 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T
diff_T *dp = tp->tp_first_diff;
linenr_T lnum_deleted = line1; // lnum of remaining deletion
- 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
@@ -363,7 +378,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T
if (last >= line1 - 1) {
// 6. change below line2: only adjust for amount_after; also when
// "deleted" became zero when deleted all lines between two diffs.
- if (dp->df_lnum[idx] - (deleted + inserted != 0) > line2) {
+ if (dp->df_lnum[idx] - (deleted + inserted != 0) > line2 - dp->is_linematched) {
if (amount_after == 0) {
// nothing left to change
break;
@@ -374,7 +389,8 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T
// 2. 3. 4. 5.: inserted/deleted lines touching this diff.
if (deleted > 0) {
- off = 0;
+ linenr_T n;
+ linenr_T off = 0;
if (dp->df_lnum[idx] >= line1) {
if (last <= line2) {
// 4. delete all lines of diff
@@ -454,7 +470,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T
}
// check if this block touches the previous one, may merge them.
- if ((dprev != NULL)
+ if ((dprev != NULL) && !dp->is_linematched
&& (dprev->df_lnum[idx] + dprev->df_count[idx] == dp->df_lnum[idx])) {
int i;
for (i = 0; i < DB_COUNT; i++) {
@@ -462,9 +478,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T
dprev->df_count[i] += dp->df_count[i];
}
}
- dprev->df_next = dp->df_next;
- xfree(dp);
- dp = dprev->df_next;
+ dp = diff_free(tp, dprev, dp);
} else {
// Advance to next entry.
dprev = dp;
@@ -485,15 +499,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T
}
if (i == DB_COUNT) {
- diff_T *dnext = dp->df_next;
- xfree(dp);
- dp = dnext;
-
- if (dprev == NULL) {
- tp->tp_first_diff = dnext;
- } else {
- dprev->df_next = dnext;
- }
+ dp = diff_free(tp, dprev, dp);
} else {
// Advance to next entry.
dprev = dp;
@@ -523,6 +529,7 @@ static diff_T *diff_alloc_new(tabpage_T *tp, diff_T *dprev, diff_T *dp)
{
diff_T *dnew = xmalloc(sizeof(*dnew));
+ dnew->is_linematched = false;
dnew->df_next = dp;
if (dprev == NULL) {
tp->tp_first_diff = dnew;
@@ -533,6 +540,20 @@ static diff_T *diff_alloc_new(tabpage_T *tp, diff_T *dprev, diff_T *dp)
return dnew;
}
+static diff_T *diff_free(tabpage_T *tp, diff_T *dprev, diff_T *dp)
+{
+ diff_T *ret = dp->df_next;
+ xfree(dp);
+
+ if (dprev == NULL) {
+ tp->tp_first_diff = ret;
+ } else {
+ dprev->df_next = ret;
+ }
+
+ return ret;
+}
+
/// Check if the diff block "dp" can be made smaller for lines at the start and
/// end that are equal. Called after inserting lines.
///
@@ -573,9 +594,9 @@ static void diff_check_unchanged(tabpage_T *tp, diff_T *dp)
if (dir == BACKWARD) {
off_org = dp->df_count[i_org] - 1;
}
- char_u *line_org = vim_strsave(ml_get_buf(tp->tp_diffbuf[i_org],
- dp->df_lnum[i_org] + off_org,
- false));
+ char *line_org = xstrdup(ml_get_buf(tp->tp_diffbuf[i_org],
+ dp->df_lnum[i_org] + off_org,
+ false));
int i_new;
for (i_new = i_org + 1; i_new < DB_COUNT; i_new++) {
@@ -659,7 +680,7 @@ void diff_redraw(bool dofold)
continue;
}
- redraw_later(wp, SOME_VALID);
+ redraw_later(wp, UPD_SOME_VALID);
if (wp != curwin) {
wp_other = wp;
}
@@ -704,16 +725,16 @@ static void clear_diffin(diffin_T *din)
if (din->din_fname == NULL) {
XFREE_CLEAR(din->din_mmfile.ptr);
} else {
- os_remove((char *)din->din_fname);
+ os_remove(din->din_fname);
}
}
static void clear_diffout(diffout_T *dout)
{
if (dout->dout_fname == NULL) {
- ga_clear_strings(&dout->dout_ga);
+ ga_clear(&dout->dout_ga);
} else {
- os_remove((char *)dout->dout_fname);
+ os_remove(dout->dout_fname);
}
}
@@ -723,15 +744,19 @@ static void clear_diffout(diffout_T *dout)
/// @param din
///
/// @return FAIL for failure.
-static int diff_write_buffer(buf_T *buf, diffin_T *din)
+static int diff_write_buffer(buf_T *buf, mmfile_t *m, linenr_T start, linenr_T end)
{
- long len = 0;
+ size_t len = 0;
+
+ if (end < 0) {
+ end = buf->b_ml.ml_line_count;
+ }
// xdiff requires one big block of memory with all the text.
- for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
- len += (long)STRLEN(ml_get_buf(buf, lnum, false)) + 1;
+ for (linenr_T lnum = start; lnum <= end; lnum++) {
+ len += strlen(ml_get_buf(buf, lnum, false)) + 1;
}
- char_u *ptr = try_malloc((size_t)len);
+ char *ptr = try_malloc(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
@@ -745,37 +770,32 @@ static int diff_write_buffer(buf_T *buf, diffin_T *din)
}
return FAIL;
}
- din->din_mmfile.ptr = (char *)ptr;
- din->din_mmfile.size = len;
+ m->ptr = ptr;
+ m->size = (long)len;
len = 0;
- 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) {
- int c;
+ for (linenr_T lnum = start; lnum <= end; lnum++) {
+ char *s = ml_get_buf(buf, lnum, false);
+ if (diff_flags & DIFF_ICASE) {
+ while (*s != NUL) {
char cbuf[MB_MAXBYTES + 1];
- 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, (size_t)orig_len);
- } else {
- memmove(ptr + len, cbuf, (size_t)orig_len);
- }
+ // xdiff doesn't support ignoring case, fold-case the text.
+ int c = *s == NL ? NUL : utf_fold(utf_ptr2char(s));
+ const int orig_len = utfc_ptr2len(s);
+ // TODO(Bram): handle byte length difference
+ char *s1 = (utf_char2bytes(c, cbuf) != orig_len) ? s : cbuf;
+ memmove(ptr + len, s1, (size_t)orig_len);
s += orig_len;
- len += orig_len;
- } else {
- ptr[len++] = *s == NL ? NUL : *s;
- s++;
+ len += (size_t)orig_len;
}
+ } else {
+ size_t slen = strlen(s);
+ memmove(ptr + len, s, slen);
+ // NUL is represented as NL; convert
+ memchrsub(ptr + len, NL, NUL, slen);
+ len += slen;
}
ptr[len++] = NL;
}
@@ -793,17 +813,17 @@ static int diff_write_buffer(buf_T *buf, diffin_T *din)
static int diff_write(buf_T *buf, diffin_T *din)
{
if (din->din_fname == NULL) {
- return diff_write_buffer(buf, din);
+ return diff_write_buffer(buf, &din->din_mmfile, 1, -1);
}
// Always use 'fileformat' set to "unix".
- char_u *save_ff = buf->b_p_ff;
- buf->b_p_ff = vim_strsave((char_u *)FF_UNIX);
+ char *save_ff = buf->b_p_ff;
+ buf->b_p_ff = xstrdup(FF_UNIX);
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,
+ int r = buf_write(buf, din->din_fname, NULL,
(linenr_T)1, buf->b_ml.ml_line_count,
NULL, false, false, false, true);
cmdmod.cmod_flags = save_cmod_flags;
@@ -821,7 +841,7 @@ static int diff_write(buf_T *buf, diffin_T *din)
static void diff_try_update(diffio_T *dio, int idx_orig, exarg_T *eap)
{
if (dio->dio_internal) {
- ga_init(&dio->dio_diff.dout_ga, sizeof(char *), 1000);
+ ga_init(&dio->dio_diff.dout_ga, sizeof(diffhunk_T), 100);
} else {
// We need three temp file names.
dio->dio_orig.din_fname = vim_tempname();
@@ -992,7 +1012,7 @@ static int check_external_diff(diffio_T *diffio)
TriState ok = kFalse;
for (;;) {
ok = kFalse;
- FILE *fd = os_fopen((char *)diffio->dio_orig.din_fname, "w");
+ FILE *fd = os_fopen(diffio->dio_orig.din_fname, "w");
if (fd == NULL) {
io_error = true;
@@ -1001,7 +1021,7 @@ static int check_external_diff(diffio_T *diffio)
io_error = true;
}
fclose(fd);
- fd = os_fopen((char *)diffio->dio_new.din_fname, "w");
+ fd = os_fopen(diffio->dio_new.din_fname, "w");
if (fd == NULL) {
io_error = true;
@@ -1012,13 +1032,13 @@ static int check_external_diff(diffio_T *diffio)
fclose(fd);
fd = NULL;
if (diff_file(diffio) == OK) {
- fd = os_fopen((char *)diffio->dio_diff.dout_fname, "r");
+ fd = os_fopen(diffio->dio_diff.dout_fname, "r");
}
if (fd == NULL) {
io_error = true;
} else {
- char_u linebuf[LBUFLEN];
+ char linebuf[LBUFLEN];
for (;;) {
// For normal diff there must be a line that contains
@@ -1027,17 +1047,17 @@ static int check_external_diff(diffio_T *diffio)
break;
}
- if (STRNCMP(linebuf, "1c1", 3) == 0
- || STRNCMP(linebuf, "@@ -1 +1 @@", 11) == 0) {
+ if (strncmp(linebuf, "1c1", 3) == 0
+ || strncmp(linebuf, "@@ -1 +1 @@", 11) == 0) {
ok = kTrue;
}
}
fclose(fd);
}
- os_remove((char *)diffio->dio_diff.dout_fname);
- os_remove((char *)diffio->dio_new.din_fname);
+ os_remove(diffio->dio_diff.dout_fname);
+ os_remove(diffio->dio_new.din_fname);
}
- os_remove((char *)diffio->dio_orig.din_fname);
+ os_remove(diffio->dio_orig.din_fname);
}
// When using 'diffexpr' break here.
@@ -1115,9 +1135,9 @@ static int diff_file_internal(diffio_T *diffio)
/// @return OK or FAIL
static int diff_file(diffio_T *dio)
{
- char *tmp_orig = (char *)dio->dio_orig.din_fname;
- char *tmp_new = (char *)dio->dio_new.din_fname;
- char *tmp_diff = (char *)dio->dio_diff.dout_fname;
+ char *tmp_orig = dio->dio_orig.din_fname;
+ char *tmp_new = dio->dio_new.din_fname;
+ char *tmp_diff = dio->dio_diff.dout_fname;
if (*p_dex != NUL) {
// Use 'diffexpr' to generate the diff file.
eval_diff(tmp_orig, tmp_new, tmp_diff);
@@ -1126,37 +1146,37 @@ static int diff_file(diffio_T *dio)
// Use xdiff for generating the diff.
if (dio->dio_internal) {
return diff_file_internal(dio);
- } else {
- const size_t len = (strlen(tmp_orig) + strlen(tmp_new) + strlen(tmp_diff)
- + STRLEN(p_srr) + 27);
- char *const cmd = xmalloc(len);
-
- // We don't want $DIFF_OPTIONS to get in the way.
- if (os_getenv("DIFF_OPTIONS")) {
- os_unsetenv("DIFF_OPTIONS");
- }
-
- // Build the diff command and execute it. Always use -a, binary
- // differences are of no use. Ignore errors, diff returns
- // non-zero when differences have been found.
- vim_snprintf(cmd, len, "diff %s%s%s%s%s%s%s%s %s",
- diff_a_works == kFalse ? "" : "-a ",
- "",
- (diff_flags & DIFF_IWHITE) ? "-b " : "",
- (diff_flags & DIFF_IWHITEALL) ? "-w " : "",
- (diff_flags & DIFF_IWHITEEOL) ? "-Z " : "",
- (diff_flags & DIFF_IBLANK) ? "-B " : "",
- (diff_flags & DIFF_ICASE) ? "-i " : "",
- tmp_orig, tmp_new);
- append_redir(cmd, len, (char *)p_srr, tmp_diff);
- block_autocmds(); // Avoid ShellCmdPost stuff
- (void)call_shell((char_u *)cmd,
- kShellOptFilter | kShellOptSilent | kShellOptDoOut,
- NULL);
- unblock_autocmds();
- xfree(cmd);
- return OK;
}
+
+ const size_t len = (strlen(tmp_orig) + strlen(tmp_new) + strlen(tmp_diff)
+ + strlen(p_srr) + 27);
+ char *const cmd = xmalloc(len);
+
+ // We don't want $DIFF_OPTIONS to get in the way.
+ if (os_getenv("DIFF_OPTIONS")) {
+ os_unsetenv("DIFF_OPTIONS");
+ }
+
+ // Build the diff command and execute it. Always use -a, binary
+ // differences are of no use. Ignore errors, diff returns
+ // non-zero when differences have been found.
+ vim_snprintf(cmd, len, "diff %s%s%s%s%s%s%s%s %s",
+ diff_a_works == kFalse ? "" : "-a ",
+ "",
+ (diff_flags & DIFF_IWHITE) ? "-b " : "",
+ (diff_flags & DIFF_IWHITEALL) ? "-w " : "",
+ (diff_flags & DIFF_IWHITEEOL) ? "-Z " : "",
+ (diff_flags & DIFF_IBLANK) ? "-B " : "",
+ (diff_flags & DIFF_ICASE) ? "-i " : "",
+ tmp_orig, tmp_new);
+ append_redir(cmd, len, p_srr, tmp_diff);
+ block_autocmds(); // Avoid ShellCmdPost stuff
+ (void)call_shell(cmd,
+ kShellOptFilter | kShellOptSilent | kShellOptDoOut,
+ NULL);
+ unblock_autocmds();
+ xfree(cmd);
+ return OK;
}
/// Create a new version of a file from the current buffer and a diff file.
@@ -1167,10 +1187,10 @@ static int diff_file(diffio_T *dio)
/// @param eap
void ex_diffpatch(exarg_T *eap)
{
- char_u *buf = NULL;
+ char *buf = NULL;
win_T *old_curwin = curwin;
char *newname = NULL; // name of patched file buffer
- char_u *esc_name = NULL;
+ char *esc_name = NULL;
#ifdef UNIX
char *fullname = NULL;
@@ -1178,16 +1198,16 @@ void ex_diffpatch(exarg_T *eap)
// We need two temp file names.
// Name of original temp file.
- char_u *tmp_orig = vim_tempname();
+ char *tmp_orig = vim_tempname();
// Name of patched temp file.
- char_u *tmp_new = vim_tempname();
+ char *tmp_new = vim_tempname();
if ((tmp_orig == NULL) || (tmp_new == NULL)) {
goto theend;
}
// Write the current buffer to "tmp_orig".
- if (buf_write(curbuf, (char *)tmp_orig, NULL,
+ if (buf_write(curbuf, tmp_orig, NULL,
(linenr_T)1, curbuf->b_ml.ml_line_count,
NULL, false, false, false, true) == FAIL) {
goto theend;
@@ -1196,23 +1216,22 @@ void ex_diffpatch(exarg_T *eap)
#ifdef UNIX
// Get the absolute path of the patchfile, changing directory below.
fullname = FullName_save(eap->arg, false);
- esc_name =
- vim_strsave_shellescape((char_u *)(fullname != NULL ? fullname : eap->arg), true, true);
+ esc_name = vim_strsave_shellescape(fullname != NULL ? fullname : eap->arg, true, true);
#else
esc_name = vim_strsave_shellescape(eap->arg, true, true);
#endif
- size_t buflen = STRLEN(tmp_orig) + STRLEN(esc_name) + STRLEN(tmp_new) + 16;
+ size_t buflen = strlen(tmp_orig) + strlen(esc_name) + strlen(tmp_new) + 16;
buf = xmalloc(buflen);
#ifdef UNIX
- char_u dirbuf[MAXPATHL];
+ char dirbuf[MAXPATHL];
// Temporarily chdir to /tmp, to avoid patching files in the current
// directory when the patch file contains more than one patch. When we
// have our own temp dir use that instead, it will be cleaned up when we
// exit (any .rej files created). Don't change directory if we can't
// return to the current.
if ((os_dirname(dirbuf, MAXPATHL) != OK)
- || (os_chdir((char *)dirbuf) != 0)) {
+ || (os_chdir(dirbuf) != 0)) {
dirbuf[0] = NUL;
} else {
char *tempdir = vim_gettempdir();
@@ -1227,15 +1246,13 @@ void ex_diffpatch(exarg_T *eap)
if (*p_pex != NUL) {
// Use 'patchexpr' to generate the new file.
#ifdef UNIX
- eval_patch((char *)tmp_orig,
- (fullname != NULL ? fullname : eap->arg),
- (char *)tmp_new);
+ eval_patch(tmp_orig, (fullname != NULL ? fullname : eap->arg), tmp_new);
#else
- eval_patch((char *)tmp_orig, (char *)eap->arg, (char *)tmp_new);
+ eval_patch(tmp_orig, eap->arg, tmp_new);
#endif
} else {
// Build the patch command and execute it. Ignore errors.
- vim_snprintf((char *)buf, buflen, "patch -o %s %s < %s",
+ vim_snprintf(buf, buflen, "patch -o %s %s < %s",
tmp_new, tmp_orig, esc_name);
block_autocmds(); // Avoid ShellCmdPost stuff
(void)call_shell(buf, kShellOptFilter, NULL);
@@ -1244,7 +1261,7 @@ void ex_diffpatch(exarg_T *eap)
#ifdef UNIX
if (dirbuf[0] != NUL) {
- if (os_chdir((char *)dirbuf) != 0) {
+ if (os_chdir(dirbuf) != 0) {
emsg(_(e_prev_dir));
}
shorten_fnames(true);
@@ -1254,20 +1271,20 @@ void ex_diffpatch(exarg_T *eap)
// Delete any .orig or .rej file created.
STRCPY(buf, tmp_new);
STRCAT(buf, ".orig");
- os_remove((char *)buf);
+ os_remove(buf);
STRCPY(buf, tmp_new);
STRCAT(buf, ".rej");
- os_remove((char *)buf);
+ os_remove(buf);
// Only continue if the output file was created.
FileInfo file_info;
- bool info_ok = os_fileinfo((char *)tmp_new, &file_info);
+ bool info_ok = os_fileinfo(tmp_new, &file_info);
uint64_t filesize = os_fileinfo_size(&file_info);
if (!info_ok || filesize == 0) {
emsg(_("E816: Cannot read patch output"));
} else {
if (curbuf->b_fname != NULL) {
- newname = xstrnsave(curbuf->b_fname, STRLEN(curbuf->b_fname) + 4);
+ newname = xstrnsave(curbuf->b_fname, strlen(curbuf->b_fname) + 4);
STRCAT(newname, ".new");
}
@@ -1277,7 +1294,7 @@ void ex_diffpatch(exarg_T *eap)
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 = (char *)tmp_new;
+ eap->arg = tmp_new;
do_exedit(eap, old_curwin);
// check that split worked and editing tmp_new
@@ -1302,12 +1319,12 @@ void ex_diffpatch(exarg_T *eap)
theend:
if (tmp_orig != NULL) {
- os_remove((char *)tmp_orig);
+ os_remove(tmp_orig);
}
xfree(tmp_orig);
if (tmp_new != NULL) {
- os_remove((char *)tmp_new);
+ os_remove(tmp_new);
}
xfree(tmp_new);
xfree(newname);
@@ -1334,30 +1351,33 @@ void ex_diffsplit(exarg_T *eap)
// don't use a new tab page, each tab page has its own diffs
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;
- curwin->w_p_diff = true;
- do_exedit(eap, old_curwin);
-
- // split must have worked
- if (curwin != old_curwin) {
- // Set 'diff', 'scrollbind' on and 'wrap' off.
- diff_win_options(curwin, true);
- if (win_valid(old_curwin)) {
- diff_win_options(old_curwin, true);
+ if (win_split(0, (diff_flags & DIFF_VERTICAL) ? WSP_VERT : 0) == FAIL) {
+ return;
+ }
- if (bufref_valid(&old_curbuf)) {
- // Move the cursor position to that of the old window.
- curwin->w_cursor.lnum = diff_get_corresponding_line(old_curbuf.br_buf,
- old_curwin->w_cursor.lnum);
- }
- }
- // Now that lines are folded scroll to show the cursor at the same
- // relative position.
- scroll_to_fraction(curwin, curwin->w_height);
+ // Pretend it was a ":split fname" command
+ eap->cmdidx = CMD_split;
+ curwin->w_p_diff = true;
+ do_exedit(eap, old_curwin);
+
+ if (curwin == old_curwin) { // split didn't work
+ return;
+ }
+
+ // Set 'diff', 'scrollbind' on and 'wrap' off.
+ diff_win_options(curwin, true);
+ if (win_valid(old_curwin)) {
+ diff_win_options(old_curwin, true);
+
+ if (bufref_valid(&old_curbuf)) {
+ // Move the cursor position to that of the old window.
+ curwin->w_cursor.lnum = diff_get_corresponding_line(old_curbuf.br_buf,
+ old_curwin->w_cursor.lnum);
}
}
+ // Now that lines are folded scroll to show the cursor at the same
+ // relative position.
+ scroll_to_fraction(curwin, curwin->w_height);
}
// Set options to show diffs for the current window.
@@ -1374,7 +1394,7 @@ static void set_diff_option(win_T *wp, int value)
curwin = wp;
curbuf = curwin->w_buffer;
curbuf->b_ro_locked++;
- set_option_value("diff", (long)value, NULL, OPT_LOCAL);
+ set_option_value_give_err("diff", (long)value, NULL, OPT_LOCAL);
curbuf->b_ro_locked--;
curwin = old_curwin;
curbuf = curwin->w_buffer;
@@ -1413,7 +1433,7 @@ void diff_win_options(win_T *wp, int addbuf)
if (wp->w_p_diff_saved) {
free_string_option(wp->w_p_fdm_save);
}
- wp->w_p_fdm_save = vim_strsave(wp->w_p_fdm);
+ wp->w_p_fdm_save = xstrdup(wp->w_p_fdm);
}
set_string_option_direct_in_win(wp, "fdm", -1, "diff", OPT_LOCAL | OPT_FREE, 0);
@@ -1424,12 +1444,12 @@ void diff_win_options(win_T *wp, int addbuf)
if (wp->w_p_diff_saved) {
free_string_option(wp->w_p_fdc_save);
}
- wp->w_p_fdc_save = vim_strsave(wp->w_p_fdc);
+ wp->w_p_fdc_save = xstrdup(wp->w_p_fdc);
}
free_string_option(wp->w_p_fdc);
- wp->w_p_fdc = (char_u *)xstrdup("2");
+ wp->w_p_fdc = xstrdup("2");
assert(diff_foldcolumn >= 0 && diff_foldcolumn <= 9);
- snprintf((char *)wp->w_p_fdc, STRLEN(wp->w_p_fdc) + 1, "%d", diff_foldcolumn);
+ snprintf(wp->w_p_fdc, strlen(wp->w_p_fdc) + 1, "%d", diff_foldcolumn);
wp->w_p_fen = true;
wp->w_p_fdl = 0;
foldUpdateAll(wp);
@@ -1448,7 +1468,7 @@ void diff_win_options(win_T *wp, int addbuf)
if (addbuf) {
diff_buf_add(wp->w_buffer);
}
- redraw_later(wp, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
}
/// Set options not to show diffs. For the current window or all windows.
@@ -1480,11 +1500,9 @@ void ex_diffoff(exarg_T *eap)
}
}
free_string_option(wp->w_p_fdm);
- wp->w_p_fdm = vim_strsave(*wp->w_p_fdm_save
- ? wp->w_p_fdm_save
- : (char_u *)"manual");
+ wp->w_p_fdm = xstrdup(*wp->w_p_fdm_save ? wp->w_p_fdm_save : "manual");
free_string_option(wp->w_p_fdc);
- wp->w_p_fdc = vim_strsave(wp->w_p_fdc_save);
+ wp->w_p_fdc = xstrdup(*wp->w_p_fdc_save ? wp->w_p_fdc_save : "0");
if (wp->w_p_fdl == 0) {
wp->w_p_fdl = wp->w_p_fdl_save;
@@ -1528,210 +1546,219 @@ void ex_diffoff(exarg_T *eap)
}
}
-/// Read the diff output and add each entry to the diff list.
-///
-/// @param idx_orig idx of original file
-/// @param idx_new idx of new file
-/// @dout diff output
-static void diff_read(int idx_orig, int idx_new, diffio_T *dio)
+static bool extract_hunk_internal(diffout_T *dout, diffhunk_T *hunk, int *line_idx)
{
- FILE *fd = NULL;
- int line_idx = 0;
- diff_T *dprev = NULL;
- diff_T *dp = curtab->tp_first_diff;
- diff_T *dn, *dpl;
- diffout_T *dout = &dio->dio_diff;
- char_u linebuf[LBUFLEN]; // only need to hold the diff line
- char_u *line;
- linenr_T off;
- int i;
- int notset = true; // block "*dp" not set yet
- diffhunk_T *hunk = NULL; // init to avoid gcc warning
- enum {
- DIFF_ED,
- DIFF_UNIFIED,
- DIFF_NONE,
- } diffstyle = DIFF_NONE;
-
- if (dout->dout_fname == NULL) {
- diffstyle = DIFF_UNIFIED;
- } else {
- fd = os_fopen((char *)dout->dout_fname, "r");
- if (fd == NULL) {
- emsg(_("E98: Cannot read diff output"));
- return;
- }
- }
-
- if (!dio->dio_internal) {
- hunk = xmalloc(sizeof(*hunk));
+ bool eof = *line_idx >= dout->dout_ga.ga_len;
+ if (!eof) {
+ *hunk = ((diffhunk_T *)dout->dout_ga.ga_data)[(*line_idx)++];
}
+ return eof;
+}
+// Extract hunk by parsing the diff output from file and calculate the diffstyle.
+static bool extract_hunk(FILE *fd, diffhunk_T *hunk, diffstyle_T *diffstyle)
+{
for (;;) {
- if (dio->dio_internal) {
- if (line_idx >= dout->dout_ga.ga_len) {
- break; // did last line
- }
- hunk = ((diffhunk_T **)dout->dout_ga.ga_data)[line_idx++];
- } else {
- if (fd == NULL) {
- if (line_idx >= dout->dout_ga.ga_len) {
- break; // did last line
- }
- line = ((char_u **)dout->dout_ga.ga_data)[line_idx++];
+ char line[LBUFLEN]; // only need to hold the diff line
+ if (vim_fgets(line, LBUFLEN, fd)) {
+ return true; // end of file
+ }
+
+ if (*diffstyle == DIFF_NONE) {
+ // Determine diff style.
+ // ed like diff looks like this:
+ // {first}[,{last}]c{first}[,{last}]
+ // {first}a{first}[,{last}]
+ // {first}[,{last}]d{first}
+ //
+ // unified diff looks like this:
+ // --- file1 2018-03-20 13:23:35.783153140 +0100
+ // +++ file2 2018-03-20 13:23:41.183156066 +0100
+ // @@ -1,3 +1,5 @@
+ if (isdigit((uint8_t)(*line))) {
+ *diffstyle = DIFF_ED;
+ } else if ((strncmp(line, "@@ ", 3) == 0)) {
+ *diffstyle = DIFF_UNIFIED;
+ } else if ((strncmp(line, "--- ", 4) == 0) // -V501
+ && (vim_fgets(line, LBUFLEN, fd) == 0) // -V501
+ && (strncmp(line, "+++ ", 4) == 0)
+ && (vim_fgets(line, LBUFLEN, fd) == 0) // -V501
+ && (strncmp(line, "@@ ", 3) == 0)) {
+ *diffstyle = DIFF_UNIFIED;
} else {
- if (vim_fgets(linebuf, LBUFLEN, fd)) {
- break; // end of file
- }
- line = linebuf;
- }
-
- if (diffstyle == DIFF_NONE) {
- // Determine diff style.
- // ed like diff looks like this:
- // {first}[,{last}]c{first}[,{last}]
- // {first}a{first}[,{last}]
- // {first}[,{last}]d{first}
- //
- // unified diff looks like this:
- // --- file1 2018-03-20 13:23:35.783153140 +0100
- // +++ file2 2018-03-20 13:23:41.183156066 +0100
- // @@ -1,3 +1,5 @@
- if (isdigit(*line)) {
- diffstyle = DIFF_ED;
- } else if ((STRNCMP(line, "@@ ", 3) == 0)) {
- diffstyle = DIFF_UNIFIED;
- } else if ((STRNCMP(line, "--- ", 4) == 0) // -V501
- && (vim_fgets(linebuf, LBUFLEN, fd) == 0) // -V501
- && (STRNCMP(line, "+++ ", 4) == 0)
- && (vim_fgets(linebuf, LBUFLEN, fd) == 0) // -V501
- && (STRNCMP(line, "@@ ", 3) == 0)) {
- diffstyle = DIFF_UNIFIED;
- } else {
- // Format not recognized yet, skip over this line. Cygwin diff
- // may put a warning at the start of the file.
- continue;
- }
+ // Format not recognized yet, skip over this line. Cygwin diff
+ // may put a warning at the start of the file.
+ continue;
}
+ }
- if (diffstyle == DIFF_ED) {
- if (!isdigit(*line)) {
- continue; // not the start of a diff block
- }
- if (parse_diff_ed(line, hunk) == FAIL) {
- continue;
- }
- } else {
- assert(diffstyle == DIFF_UNIFIED);
- if (STRNCMP(line, "@@ ", 3) != 0) {
- continue; // not the start of a diff block
- }
- if (parse_diff_unified(line, hunk) == FAIL) {
- continue;
- }
+ if (*diffstyle == DIFF_ED) {
+ if (!isdigit((uint8_t)(*line))) {
+ continue; // not the start of a diff block
+ }
+ if (parse_diff_ed(line, hunk) == FAIL) {
+ continue;
+ }
+ } else {
+ assert(*diffstyle == DIFF_UNIFIED);
+ if (strncmp(line, "@@ ", 3) != 0) {
+ continue; // not the start of a diff block
+ }
+ if (parse_diff_unified(line, hunk) == FAIL) {
+ continue;
}
}
- // Go over blocks before the change, for which orig and new are equal.
- // Copy blocks from orig to new.
- while (dp != NULL
- && hunk->lnum_orig > dp->df_lnum[idx_orig] + dp->df_count[idx_orig]) {
- if (notset) {
- diff_copy_entry(dprev, dp, idx_orig, idx_new);
- }
- dprev = dp;
- dp = dp->df_next;
- notset = true;
+ // Successfully parsed diff output, can return
+ return false;
+ }
+}
+
+static void process_hunk(diff_T **dpp, diff_T **dprevp, int idx_orig, int idx_new, diffhunk_T *hunk,
+ bool *notsetp)
+{
+ diff_T *dp = *dpp;
+ diff_T *dprev = *dprevp;
+
+ // Go over blocks before the change, for which orig and new are equal.
+ // Copy blocks from orig to new.
+ while (dp != NULL
+ && hunk->lnum_orig > dp->df_lnum[idx_orig] + dp->df_count[idx_orig]) {
+ if (*notsetp) {
+ diff_copy_entry(dprev, dp, idx_orig, idx_new);
}
+ dprev = dp;
+ dp = dp->df_next;
+ *notsetp = true;
+ }
- if ((dp != NULL)
- && (hunk->lnum_orig <= dp->df_lnum[idx_orig] + dp->df_count[idx_orig])
- && (hunk->lnum_orig + hunk->count_orig >= dp->df_lnum[idx_orig])) {
- // New block overlaps with existing block(s).
- // First find last block that overlaps.
- for (dpl = dp; dpl->df_next != NULL; dpl = dpl->df_next) {
- if (hunk->lnum_orig + hunk->count_orig < dpl->df_next->df_lnum[idx_orig]) {
- break;
- }
+ if ((dp != NULL)
+ && (hunk->lnum_orig <= dp->df_lnum[idx_orig] + dp->df_count[idx_orig])
+ && (hunk->lnum_orig + hunk->count_orig >= dp->df_lnum[idx_orig])) {
+ // New block overlaps with existing block(s).
+ // First find last block that overlaps.
+ diff_T *dpl;
+ for (dpl = dp; dpl->df_next != NULL; dpl = dpl->df_next) {
+ if (hunk->lnum_orig + hunk->count_orig < dpl->df_next->df_lnum[idx_orig]) {
+ break;
}
+ }
- // If the newly found block starts before the old one, set the
- // start back a number of lines.
- off = dp->df_lnum[idx_orig] - hunk->lnum_orig;
+ // If the newly found block starts before the old one, set the
+ // start back a number of lines.
+ linenr_T off = dp->df_lnum[idx_orig] - hunk->lnum_orig;
- if (off > 0) {
- 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] = (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] = (linenr_T)hunk->count_new - off;
- } else {
- // second overlap of new block with existing block
- 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] +
- dp->df_count[idx_orig]);
- }
-
- // 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 + (linenr_T)hunk->count_orig)
- - (dpl->df_lnum[idx_orig] + dpl->df_count[idx_orig]);
-
- if (off < 0) {
- // new change ends in existing block, adjust the end if not
- // done already
- if (notset) {
- dp->df_count[idx_new] += -off;
+ if (off > 0) {
+ for (int i = idx_orig; i < idx_new; i++) {
+ if (curtab->tp_diffbuf[i] != NULL) {
+ dp->df_lnum[i] -= off;
}
- off = 0;
}
+ dp->df_lnum[idx_new] = hunk->lnum_new;
+ dp->df_count[idx_new] = (linenr_T)hunk->count_new;
+ } else if (*notsetp) {
+ // new block inside existing one, adjust new block
+ dp->df_lnum[idx_new] = hunk->lnum_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] += (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] +
+ dp->df_count[idx_orig]);
+ }
+
+ // 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 + (linenr_T)hunk->count_orig)
+ - (dpl->df_lnum[idx_orig] + dpl->df_count[idx_orig]);
+
+ if (off < 0) {
+ // new change ends in existing block, adjust the end if not
+ // done already
+ if (*notsetp) {
+ dp->df_count[idx_new] += -off;
+ }
+ off = 0;
+ }
- 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;
- }
+ for (int 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;
}
+ }
- // Delete the diff blocks that have been merged into one.
- dn = dp->df_next;
- dp->df_next = dpl->df_next;
+ // Delete the diff blocks that have been merged into one.
+ diff_T *dn = dp->df_next;
+ dp->df_next = dpl->df_next;
- while (dn != dp->df_next) {
- dpl = dn->df_next;
- xfree(dn);
- dn = dpl;
+ while (dn != dp->df_next) {
+ dpl = dn->df_next;
+ xfree(dn);
+ dn = dpl;
+ }
+ } else {
+ // Allocate a new diffblock.
+ dp = diff_alloc_new(curtab, dprev, dp);
+
+ dp->df_lnum[idx_orig] = hunk->lnum_orig;
+ dp->df_count[idx_orig] = (linenr_T)hunk->count_orig;
+ dp->df_lnum[idx_new] = hunk->lnum_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 (int i = idx_orig + 1; i < idx_new; i++) {
+ if (curtab->tp_diffbuf[i] != NULL) {
+ diff_copy_entry(dprev, dp, idx_orig, i);
}
- } else {
- // Allocate a new diffblock.
- dp = diff_alloc_new(curtab, dprev, dp);
+ }
+ }
+ *notsetp = false; // "*dp" has been set
+ *dpp = dp;
+ *dprevp = dprev;
+}
- dp->df_lnum[idx_orig] = hunk->lnum_orig;
- dp->df_count[idx_orig] = (linenr_T)hunk->count_orig;
- dp->df_lnum[idx_new] = hunk->lnum_new;
- dp->df_count[idx_new] = (linenr_T)hunk->count_new;
+/// Read the diff output and add each entry to the diff list.
+///
+/// @param idx_orig idx of original file
+/// @param idx_new idx of new file
+/// @dout diff output
+static void diff_read(int idx_orig, int idx_new, diffio_T *dio)
+{
+ FILE *fd = NULL;
+ int line_idx = 0;
+ diff_T *dprev = NULL;
+ diff_T *dp = curtab->tp_first_diff;
+ diffout_T *dout = &dio->dio_diff;
+ bool notset = true; // block "*dp" not set yet
+ diffstyle_T diffstyle = DIFF_NONE;
- // 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++) {
- if (curtab->tp_diffbuf[i] != NULL) {
- diff_copy_entry(dprev, dp, idx_orig, i);
- }
- }
+ if (!dio->dio_internal) {
+ fd = os_fopen(dout->dout_fname, "r");
+ if (fd == NULL) {
+ emsg(_("E98: Cannot read diff output"));
+ return;
+ }
+ }
+
+ for (;;) {
+ diffhunk_T hunk = { 0 };
+ bool eof = dio->dio_internal
+ ? extract_hunk_internal(dout, &hunk, &line_idx)
+ : extract_hunk(fd, &hunk, &diffstyle);
+
+ if (eof) {
+ break;
}
- notset = false; // "*dp" has been set
+
+ process_hunk(&dp, &dprev, idx_orig, idx_new, &hunk, &notset);
}
-// for remaining diff blocks orig and new are equal
+ // for remaining diff blocks orig and new are equal
while (dp != NULL) {
if (notset) {
diff_copy_entry(dprev, dp, idx_orig, idx_new);
@@ -1741,10 +1768,6 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio)
notset = true;
}
- if (!dio->dio_internal) {
- xfree(hunk);
- }
-
if (fd != NULL) {
fclose(fd);
}
@@ -1784,6 +1807,293 @@ void diff_clear(tabpage_T *tp)
tp->tp_first_diff = NULL;
}
+///
+/// return true if the options are set to use diff linematch
+///
+bool diff_linematch(diff_T *dp)
+{
+ if (!(diff_flags & DIFF_LINEMATCH)) {
+ return false;
+ }
+ // are there more than three diff buffers?
+ int tsize = 0;
+ for (int i = 0; i < DB_COUNT; i++) {
+ if (curtab->tp_diffbuf[i] != NULL) {
+ // for the rare case (bug?) that the count of a diff block is negative, do
+ // not run the algorithm because this will try to allocate a negative
+ // amount of space and crash
+ if (dp->df_count[i] < 0) {
+ return false;
+ }
+ tsize += dp->df_count[i];
+ }
+ }
+ // avoid allocating a huge array because it will lag
+ return tsize <= linematch_lines;
+}
+
+static int get_max_diff_length(const diff_T *dp)
+{
+ int maxlength = 0;
+ for (int k = 0; k < DB_COUNT; k++) {
+ if (curtab->tp_diffbuf[k] != NULL) {
+ if (dp->df_count[k] > maxlength) {
+ maxlength = dp->df_count[k];
+ }
+ }
+ }
+ return maxlength;
+}
+
+static void find_top_diff_block(diff_T **thistopdiff, diff_T **nextblockblock, int fromidx,
+ int topline)
+{
+ diff_T *topdiff = NULL;
+ diff_T *localtopdiff = NULL;
+ int topdiffchange = 0;
+
+ for (topdiff = curtab->tp_first_diff; topdiff != NULL; topdiff = topdiff->df_next) {
+ // set the top of the current overlapping diff block set as we
+ // iterate through all of the sets of overlapping diff blocks
+ if (!localtopdiff || topdiffchange) {
+ localtopdiff = topdiff;
+ topdiffchange = 0;
+ }
+
+ // check if the fromwin topline is matched by the current diff. if so, set it to the top of the diff block
+ if (topline >= topdiff->df_lnum[fromidx] && topline <=
+ (topdiff->df_lnum[fromidx] + topdiff->df_count[fromidx])) {
+ // this line is inside the current diff block, so we will save the
+ // top block of the set of blocks to refer to later
+ if ((*thistopdiff) == NULL) {
+ (*thistopdiff) = localtopdiff;
+ }
+ }
+
+ // check if the next set of overlapping diff blocks is next
+ if (!(topdiff->df_next && (topdiff->df_next->df_lnum[fromidx] ==
+ (topdiff->df_lnum[fromidx] + topdiff->df_count[fromidx])))) {
+ // mark that the next diff block is belongs to a different set of
+ // overlapping diff blocks
+ topdiffchange = 1;
+
+ // if we already have found that the line number is inside a diff block,
+ // set the marker of the next block and finish the iteration
+ if (*thistopdiff) {
+ (*nextblockblock) = topdiff->df_next;
+ break;
+ }
+ }
+ }
+}
+
+static void count_filler_lines_and_topline(int *curlinenum_to, int *linesfiller,
+ const diff_T *thistopdiff, const int toidx,
+ int virtual_lines_passed)
+{
+ const diff_T *curdif = thistopdiff;
+ int ch_virtual_lines = 0;
+ int isfiller = 0;
+ while (virtual_lines_passed > 0) {
+ if (ch_virtual_lines) {
+ virtual_lines_passed--;
+ ch_virtual_lines--;
+ if (!isfiller) {
+ (*curlinenum_to)++;
+ } else {
+ (*linesfiller)++;
+ }
+ } else {
+ (*linesfiller) = 0;
+ ch_virtual_lines = get_max_diff_length(curdif);
+ isfiller = (curdif->df_count[toidx] ? 0 : 1);
+ if (isfiller) {
+ while (curdif && curdif->df_next && curdif->df_lnum[toidx] ==
+ curdif->df_next->df_lnum[toidx]
+ && curdif->df_next->df_count[toidx] == 0) {
+ curdif = curdif->df_next;
+ ch_virtual_lines += get_max_diff_length(curdif);
+ }
+ }
+ if (curdif) {
+ curdif = curdif->df_next;
+ }
+ }
+ }
+}
+
+static void calculate_topfill_and_topline(const int fromidx, const int toidx, const
+ int from_topline, const int from_topfill, int *topfill,
+ linenr_T *topline)
+{
+ // 1. find the position from the top of the diff block, and the start
+ // of the next diff block
+ diff_T *thistopdiff = NULL;
+ diff_T *nextblockblock = NULL;
+ int virtual_lines_passed = 0;
+
+ find_top_diff_block(&thistopdiff, &nextblockblock, fromidx, from_topline);
+
+ // count the virtual lines that have been passed
+
+ diff_T *curdif = thistopdiff;
+ while (curdif && (curdif->df_lnum[fromidx] + curdif->df_count[fromidx])
+ <= from_topline) {
+ virtual_lines_passed += get_max_diff_length(curdif);
+
+ curdif = curdif->df_next;
+ }
+
+ if (curdif != nextblockblock) {
+ virtual_lines_passed += from_topline - curdif->df_lnum[fromidx];
+ }
+ virtual_lines_passed -= from_topfill;
+
+ // count the same amount of virtual lines in the toidx buffer
+ int curlinenum_to = thistopdiff->df_lnum[toidx];
+ int linesfiller = 0;
+ count_filler_lines_and_topline(&curlinenum_to, &linesfiller,
+ thistopdiff, toidx, virtual_lines_passed);
+
+ // count the number of filler lines that would normally be above this line
+ int maxfiller = 0;
+ for (diff_T *dpfillertest = thistopdiff; dpfillertest != NULL;
+ dpfillertest = dpfillertest->df_next) {
+ if (dpfillertest->df_lnum[toidx] == curlinenum_to) {
+ while (dpfillertest && dpfillertest->df_lnum[toidx] == curlinenum_to) {
+ maxfiller += dpfillertest->df_count[toidx] ? 0 : get_max_diff_length(dpfillertest);
+ dpfillertest = dpfillertest->df_next;
+ }
+ break;
+ }
+ }
+ (*topfill) = maxfiller - linesfiller;
+ (*topline) = curlinenum_to;
+}
+
+static int linematched_filler_lines(diff_T *dp, int idx, linenr_T lnum, int *linestatus)
+{
+ int filler_lines_d1 = 0;
+ while (dp && dp->df_next
+ && lnum == (dp->df_lnum[idx] + dp->df_count[idx])
+ && dp->df_next->df_lnum[idx] == lnum) {
+ if (dp->df_count[idx] == 0) {
+ filler_lines_d1 += get_max_diff_length(dp);
+ }
+ dp = dp->df_next;
+ }
+
+ if (dp->df_count[idx] == 0) {
+ filler_lines_d1 += get_max_diff_length(dp);
+ }
+
+ if (lnum < dp->df_lnum[idx] + dp->df_count[idx]) {
+ int j = 0;
+ for (int i = 0; i < DB_COUNT; i++) {
+ if (curtab->tp_diffbuf[i] != NULL) {
+ if (dp->df_count[i]) {
+ j++;
+ }
+ }
+ // is this an added line or a changed line?
+ if (linestatus) {
+ (*linestatus) = (j == 1) ? -2 : -1;
+ }
+ }
+ }
+ return filler_lines_d1;
+}
+
+// Apply results from the linematch algorithm and apply to 'dp' by splitting it into multiple
+// adjacent diff blocks.
+static void apply_linematch_results(diff_T *dp, size_t decisions_length, const int *decisions)
+{
+ // get the start line number here in each diff buffer, and then increment
+ int line_numbers[DB_COUNT];
+ int outputmap[DB_COUNT];
+ size_t ndiffs = 0;
+ for (int i = 0; i < DB_COUNT; i++) {
+ if (curtab->tp_diffbuf[i] != NULL) {
+ line_numbers[i] = dp->df_lnum[i];
+ dp->df_count[i] = 0;
+
+ // Keep track of the index of the diff buffer we are using here.
+ // We will use this to write the output of the algorithm to
+ // diff_T structs at the correct indexes
+ outputmap[ndiffs] = i;
+ ndiffs++;
+ }
+ }
+
+ // write the diffs starting with the current diff block
+ diff_T *dp_s = dp;
+ for (size_t i = 0; i < decisions_length; i++) {
+ // Don't allocate on first iter since we can reuse the initial diffblock
+ if (i != 0 && (decisions[i - 1] != decisions[i])) {
+ // create new sub diff blocks to segment the original diff block which we
+ // further divided by running the linematch algorithm
+ dp_s = diff_alloc_new(curtab, dp_s, dp_s->df_next);
+ dp_s->is_linematched = true;
+ for (int j = 0; j < DB_COUNT; j++) {
+ if (curtab->tp_diffbuf[j] != NULL) {
+ dp_s->df_lnum[j] = line_numbers[j];
+ dp_s->df_count[j] = 0;
+ }
+ }
+ }
+ for (size_t j = 0; j < ndiffs; j++) {
+ if (decisions[i] & (1 << j)) {
+ // will need to use the map here
+ dp_s->df_count[outputmap[j]]++;
+ line_numbers[outputmap[j]]++;
+ }
+ }
+ }
+ dp->is_linematched = true;
+}
+
+static void run_linematch_algorithm(diff_T *dp)
+{
+ // define buffers for diff algorithm
+ mmfile_t diffbufs_mm[DB_COUNT];
+ const char *diffbufs[DB_COUNT];
+ int diff_length[DB_COUNT];
+ size_t ndiffs = 0;
+ for (int i = 0; i < DB_COUNT; i++) {
+ if (curtab->tp_diffbuf[i] != NULL) {
+ // write the contents of the entire buffer to
+ // diffbufs_mm[diffbuffers_count]
+ diff_write_buffer(curtab->tp_diffbuf[i], &diffbufs_mm[ndiffs],
+ dp->df_lnum[i], dp->df_lnum[i] + dp->df_count[i] - 1);
+
+ // we want to get the char* to the diff buffer that was just written
+ // we add it to the array of char*, diffbufs
+ diffbufs[ndiffs] = diffbufs_mm[ndiffs].ptr;
+
+ // keep track of the length of this diff block to pass it to the linematch
+ // algorithm
+ diff_length[ndiffs] = dp->df_count[i];
+
+ // increment the amount of diff buffers we are passing to the algorithm
+ ndiffs++;
+ }
+ }
+
+ // we will get the output of the linematch algorithm in the format of an array
+ // of integers (*decisions) and the length of that array (decisions_length)
+ int *decisions = NULL;
+ const bool iwhite = (diff_flags & (DIFF_IWHITEALL | DIFF_IWHITE)) > 0;
+ size_t decisions_length = linematch_nbuffers(diffbufs, diff_length, ndiffs, &decisions, iwhite);
+
+ for (size_t i = 0; i < ndiffs; i++) {
+ XFREE_CLEAR(diffbufs_mm[i].ptr);
+ }
+
+ apply_linematch_results(dp, decisions_length, decisions);
+
+ xfree(decisions);
+}
+
/// Check diff status for line "lnum" in buffer "buf":
///
/// Returns 0 for nothing special
@@ -1795,11 +2105,11 @@ void diff_clear(tabpage_T *tp)
///
/// @param wp
/// @param lnum
+/// @param[out] linestatus
///
/// @return diff status.
-int diff_check(win_T *wp, linenr_T lnum)
+int diff_check_with_linestatus(win_T *wp, linenr_T lnum, int *linestatus)
{
- diff_T *dp;
buf_T *buf = wp->w_buffer;
if (curtab->tp_diff_invalid) {
@@ -1830,6 +2140,7 @@ int diff_check(win_T *wp, linenr_T lnum)
}
// search for a change that includes "lnum" in the list of diffblocks.
+ diff_T *dp;
for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) {
if (lnum <= dp->df_lnum[idx] + dp->df_count[idx]) {
break;
@@ -1840,6 +2151,14 @@ int diff_check(win_T *wp, linenr_T lnum)
return 0;
}
+ if (!dp->is_linematched && diff_linematch(dp)) {
+ run_linematch_algorithm(dp);
+ }
+
+ if (dp->is_linematched) {
+ return linematched_filler_lines(dp, idx, lnum, linestatus);
+ }
+
if (lnum < dp->df_lnum[idx] + dp->df_count[idx]) {
int zero = false;
@@ -1894,15 +2213,16 @@ 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.
- 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];
- }
- }
+ int maxcount = get_max_diff_length(dp);
return maxcount - dp->df_count[idx];
}
+/// See diff_check_with_linestatus
+int diff_check(win_T *wp, linenr_T lnum)
+{
+ return diff_check_with_linestatus(wp, lnum, NULL);
+}
+
/// Compare two entries in diff "dp" and return true if they are equal.
///
/// @param dp diff
@@ -1922,8 +2242,8 @@ static bool diff_equal_entry(diff_T *dp, int idx1, int idx2)
}
for (int i = 0; i < dp->df_count[idx1]; i++) {
- char_u *line = vim_strsave(ml_get_buf(curtab->tp_diffbuf[idx1],
- dp->df_lnum[idx1] + i, false));
+ char *line = xstrdup(ml_get_buf(curtab->tp_diffbuf[idx1],
+ dp->df_lnum[idx1] + i, false));
int cmp = diff_cmp(line, ml_get_buf(curtab->tp_diffbuf[idx2],
dp->df_lnum[idx2] + i, false));
@@ -1938,24 +2258,24 @@ static bool diff_equal_entry(diff_T *dp, int idx1, int idx2)
// Compare the characters at "p1" and "p2". If they are equal (possibly
// 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)
+static bool diff_equal_char(const char *const p1, const char *const p2, int *const len)
{
- const int l = utfc_ptr2len((char *)p1);
+ const int l = utfc_ptr2len(p1);
- if (l != utfc_ptr2len((char *)p2)) {
+ if (l != utfc_ptr2len(p2)) {
return false;
}
if (l > 1) {
- if (STRNCMP(p1, p2, l) != 0
+ if (strncmp(p1, p2, (size_t)l) != 0
&& (!(diff_flags & DIFF_ICASE)
- || utf_fold(utf_ptr2char((char *)p1)) != utf_fold(utf_ptr2char((char *)p2)))) {
+ || utf_fold(utf_ptr2char(p1)) != utf_fold(utf_ptr2char(p2)))) {
return false;
}
*len = l;
} else {
if ((*p1 != *p2)
&& (!(diff_flags & DIFF_ICASE)
- || TOLOWER_LOC(*p1) != TOLOWER_LOC(*p2))) {
+ || TOLOWER_LOC((uint8_t)(*p1)) != TOLOWER_LOC((uint8_t)(*p2)))) {
return false;
}
*len = 1;
@@ -1970,23 +2290,23 @@ static bool diff_equal_char(const char_u *const p1, const char_u *const p2, int
/// @param s2 The second string
///
/// @return on-zero if the two strings are different.
-static int diff_cmp(char_u *s1, char_u *s2)
+static int diff_cmp(char *s1, char *s2)
{
if ((diff_flags & DIFF_IBLANK)
- && (*(char_u *)skipwhite((char *)s1) == NUL || *skipwhite((char *)s2) == NUL)) {
+ && (*skipwhite(s1) == NUL || *skipwhite(s2) == NUL)) {
return 0;
}
if ((diff_flags & (DIFF_ICASE | ALL_WHITE_DIFF)) == 0) {
- return STRCMP(s1, s2);
+ return strcmp(s1, s2);
}
if ((diff_flags & DIFF_ICASE) && !(diff_flags & ALL_WHITE_DIFF)) {
- return mb_stricmp((const char *)s1, (const char *)s2);
+ return mb_stricmp(s1, s2);
}
- char *p1 = (char *)s1;
- char *p2 = (char *)s2;
+ char *p1 = s1;
+ char *p2 = s2;
// Ignore white space changes and possibly ignore case.
while (*p1 != NUL && *p2 != NUL) {
@@ -1998,7 +2318,7 @@ static int diff_cmp(char_u *s1, char_u *s2)
p2 = skipwhite(p2);
} else {
int l;
- if (!diff_equal_char((char_u *)p1, (char_u *)p2, &l)) {
+ if (!diff_equal_char(p1, p2, &l)) {
break;
}
p1 += l;
@@ -2025,7 +2345,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 fromidx = diff_buf_idx(frombuf);
if (fromidx == DB_COUNT) {
@@ -2040,6 +2359,7 @@ 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.
+ diff_T *dp;
for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) {
if (lnum <= dp->df_lnum[fromidx] + dp->df_count[fromidx]) {
break;
@@ -2062,46 +2382,51 @@ void diff_set_topline(win_T *fromwin, win_T *towin)
towin->w_topline = lnum + (dp->df_lnum[toidx] - dp->df_lnum[fromidx]);
if (lnum >= dp->df_lnum[fromidx]) {
- // Inside a change: compute filler lines. With three or more
- // buffers we need to know the largest count.
- linenr_T max_count = 0;
-
- 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];
- }
- }
+ if (diff_flags & DIFF_LINEMATCH) {
+ calculate_topfill_and_topline(fromidx, toidx, fromwin->w_topline,
+ fromwin->w_topfill, &towin->w_topfill, &towin->w_topline);
+ } else {
+ // Inside a change: compute filler lines. With three or more
+ // buffers we need to know the largest count.
+ linenr_T max_count = 0;
- if (dp->df_count[toidx] == dp->df_count[fromidx]) {
- // same number of lines: use same filler count
- towin->w_topfill = fromwin->w_topfill;
- } else if (dp->df_count[toidx] > dp->df_count[fromidx]) {
- if (lnum == dp->df_lnum[fromidx] + dp->df_count[fromidx]) {
- // more lines in towin and fromwin doesn't show diff
- // lines, only filler lines
- if (max_count - fromwin->w_topfill >= dp->df_count[toidx]) {
- // towin also only shows filler lines
- towin->w_topline = dp->df_lnum[toidx] + dp->df_count[toidx];
- towin->w_topfill = fromwin->w_topfill;
- } else {
- // towin still has some diff lines to show
- towin->w_topline = dp->df_lnum[toidx]
- + max_count - fromwin->w_topfill;
+ 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];
}
}
- } else if (towin->w_topline >= dp->df_lnum[toidx]
- + dp->df_count[toidx]) {
- // less lines in towin and no diff lines to show: compute
- // filler lines
- towin->w_topline = dp->df_lnum[toidx] + dp->df_count[toidx];
- if (diff_flags & DIFF_FILLER) {
+ if (dp->df_count[toidx] == dp->df_count[fromidx]) {
+ // same number of lines: use same filler count
+ towin->w_topfill = fromwin->w_topfill;
+ } else if (dp->df_count[toidx] > dp->df_count[fromidx]) {
if (lnum == dp->df_lnum[fromidx] + dp->df_count[fromidx]) {
- // fromwin is also out of diff lines
- towin->w_topfill = fromwin->w_topfill;
- } else {
- // fromwin has some diff lines
- towin->w_topfill = dp->df_lnum[fromidx] + max_count - lnum;
+ // more lines in towin and fromwin doesn't show diff
+ // lines, only filler lines
+ if (max_count - fromwin->w_topfill >= dp->df_count[toidx]) {
+ // towin also only shows filler lines
+ towin->w_topline = dp->df_lnum[toidx] + dp->df_count[toidx];
+ towin->w_topfill = fromwin->w_topfill;
+ } else {
+ // towin still has some diff lines to show
+ towin->w_topline = dp->df_lnum[toidx]
+ + max_count - fromwin->w_topfill;
+ }
+ }
+ } else if (towin->w_topline >= dp->df_lnum[toidx]
+ + dp->df_count[toidx]) {
+ // less lines in towin and no diff lines to show: compute
+ // filler lines
+ towin->w_topline = dp->df_lnum[toidx] + dp->df_count[toidx];
+
+ if (diff_flags & DIFF_FILLER) {
+ if (lnum == dp->df_lnum[fromidx] + dp->df_count[fromidx]) {
+ // fromwin is also out of diff lines
+ towin->w_topfill = fromwin->w_topfill;
+ } else {
+ // fromwin has some diff lines
+ towin->w_topfill = dp->df_lnum[fromidx] + max_count - lnum;
+ }
}
}
}
@@ -2136,75 +2461,80 @@ void diff_set_topline(win_T *fromwin, win_T *towin)
int diffopt_changed(void)
{
int diff_context_new = 6;
+ int linematch_lines_new = 0;
int diff_flags_new = 0;
int diff_foldcolumn_new = 2;
long diff_algorithm_new = 0;
long diff_indent_heuristic = 0;
- char *p = (char *)p_dip;
+ char *p = p_dip;
while (*p != NUL) {
- if (STRNCMP(p, "filler", 6) == 0) {
+ if (strncmp(p, "filler", 6) == 0) {
p += 6;
diff_flags_new |= DIFF_FILLER;
- } else if ((STRNCMP(p, "context:", 8) == 0) && ascii_isdigit(p[8])) {
+ } else if ((strncmp(p, "context:", 8) == 0) && ascii_isdigit(p[8])) {
p += 8;
diff_context_new = getdigits_int(&p, false, diff_context_new);
- } else if (STRNCMP(p, "iblank", 6) == 0) {
+ } else if (strncmp(p, "iblank", 6) == 0) {
p += 6;
diff_flags_new |= DIFF_IBLANK;
- } else if (STRNCMP(p, "icase", 5) == 0) {
+ } else if (strncmp(p, "icase", 5) == 0) {
p += 5;
diff_flags_new |= DIFF_ICASE;
- } else if (STRNCMP(p, "iwhiteall", 9) == 0) {
+ } else if (strncmp(p, "iwhiteall", 9) == 0) {
p += 9;
diff_flags_new |= DIFF_IWHITEALL;
- } else if (STRNCMP(p, "iwhiteeol", 9) == 0) {
+ } else if (strncmp(p, "iwhiteeol", 9) == 0) {
p += 9;
diff_flags_new |= DIFF_IWHITEEOL;
- } else if (STRNCMP(p, "iwhite", 6) == 0) {
+ } else if (strncmp(p, "iwhite", 6) == 0) {
p += 6;
diff_flags_new |= DIFF_IWHITE;
- } else if (STRNCMP(p, "horizontal", 10) == 0) {
+ } else if (strncmp(p, "horizontal", 10) == 0) {
p += 10;
diff_flags_new |= DIFF_HORIZONTAL;
- } else if (STRNCMP(p, "vertical", 8) == 0) {
+ } else if (strncmp(p, "vertical", 8) == 0) {
p += 8;
diff_flags_new |= DIFF_VERTICAL;
- } else if ((STRNCMP(p, "foldcolumn:", 11) == 0) && ascii_isdigit(p[11])) {
+ } else if ((strncmp(p, "foldcolumn:", 11) == 0) && ascii_isdigit(p[11])) {
p += 11;
diff_foldcolumn_new = getdigits_int(&p, false, diff_foldcolumn_new);
- } else if (STRNCMP(p, "hiddenoff", 9) == 0) {
+ } else if (strncmp(p, "hiddenoff", 9) == 0) {
p += 9;
diff_flags_new |= DIFF_HIDDEN_OFF;
- } else if (STRNCMP(p, "closeoff", 8) == 0) {
+ } else if (strncmp(p, "closeoff", 8) == 0) {
p += 8;
diff_flags_new |= DIFF_CLOSE_OFF;
- } else if (STRNCMP(p, "followwrap", 10) == 0) {
+ } else if (strncmp(p, "followwrap", 10) == 0) {
p += 10;
diff_flags_new |= DIFF_FOLLOWWRAP;
- } else if (STRNCMP(p, "indent-heuristic", 16) == 0) {
+ } else if (strncmp(p, "indent-heuristic", 16) == 0) {
p += 16;
diff_indent_heuristic = XDF_INDENT_HEURISTIC;
- } else if (STRNCMP(p, "internal", 8) == 0) {
+ } else if (strncmp(p, "internal", 8) == 0) {
p += 8;
diff_flags_new |= DIFF_INTERNAL;
- } else if (STRNCMP(p, "algorithm:", 10) == 0) {
+ } else if (strncmp(p, "algorithm:", 10) == 0) {
p += 10;
- if (STRNCMP(p, "myers", 5) == 0) {
+ if (strncmp(p, "myers", 5) == 0) {
p += 5;
diff_algorithm_new = 0;
- } else if (STRNCMP(p, "minimal", 7) == 0) {
+ } else if (strncmp(p, "minimal", 7) == 0) {
p += 7;
diff_algorithm_new = XDF_NEED_MINIMAL;
- } else if (STRNCMP(p, "patience", 8) == 0) {
+ } else if (strncmp(p, "patience", 8) == 0) {
p += 8;
diff_algorithm_new = XDF_PATIENCE_DIFF;
- } else if (STRNCMP(p, "histogram", 9) == 0) {
+ } else if (strncmp(p, "histogram", 9) == 0) {
p += 9;
diff_algorithm_new = XDF_HISTOGRAM_DIFF;
} else {
return FAIL;
}
+ } else if ((strncmp(p, "linematch:", 10) == 0) && ascii_isdigit(p[10])) {
+ p += 10;
+ linematch_lines_new = getdigits_int(&p, false, linematch_lines_new);
+ diff_flags_new |= DIFF_LINEMATCH;
}
if ((*p != ',') && (*p != NUL)) {
@@ -2233,6 +2563,7 @@ int diffopt_changed(void)
diff_flags = diff_flags_new;
diff_context = diff_context_new == 0 ? 1 : diff_context_new;
+ linematch_lines = linematch_lines_new;
diff_foldcolumn = diff_foldcolumn_new;
diff_algorithm = diff_algorithm_new;
@@ -2283,16 +2614,8 @@ bool diffopt_filler(void)
bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
- char_u *line_new;
- int si_org;
- int si_new;
- int ei_org;
- int ei_new;
- bool added = true;
- int l;
-
// Make a copy of the line, the next ml_get() will invalidate it.
- char_u *line_org = vim_strsave(ml_get_buf(wp->w_buffer, lnum, false));
+ char *line_org = xstrdup(ml_get_buf(wp->w_buffer, lnum, false));
int idx = diff_buf_idx(wp->w_buffer);
if (idx == DB_COUNT) {
@@ -2308,6 +2631,13 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
break;
}
}
+ if (dp != NULL && dp->is_linematched) {
+ while (dp && dp->df_next
+ && lnum == dp->df_count[idx] + dp->df_lnum[idx]
+ && dp->df_next->df_lnum[idx] == lnum) {
+ dp = dp->df_next;
+ }
+ }
if ((dp == NULL) || (diff_check_sanity(curtab, dp) == FAIL)) {
xfree(line_org);
@@ -2315,6 +2645,12 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
return false;
}
+ int si_org;
+ int si_new;
+ int ei_org;
+ int ei_new;
+ bool added = true;
+
linenr_T off = lnum - dp->df_lnum[idx];
int i;
for (i = 0; i < DB_COUNT; i++) {
@@ -2324,8 +2660,7 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
continue;
}
added = false;
- line_new = ml_get_buf(curtab->tp_diffbuf[i],
- dp->df_lnum[i] + off, false);
+ char *line_new = ml_get_buf(curtab->tp_diffbuf[i], dp->df_lnum[i] + off, false);
// Search for start of difference
si_org = si_new = 0;
@@ -2337,9 +2672,10 @@ 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)((char_u *)skipwhite((char *)line_org + si_org) - line_org);
- si_new = (int)((char_u *)skipwhite((char *)line_new + si_new) - line_new);
+ si_org = (int)(skipwhite(line_org + si_org) - line_org);
+ si_new = (int)(skipwhite(line_new + si_new) - line_new);
} else {
+ int l;
if (!diff_equal_char(line_org + si_org, line_new + si_new, &l)) {
break;
}
@@ -2359,8 +2695,8 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
// Search for end of difference, if any.
if ((line_org[si_org] != NUL) || (line_new[si_new] != NUL)) {
- ei_org = (int)STRLEN(line_org);
- ei_new = (int)STRLEN(line_new);
+ ei_org = (int)strlen(line_org);
+ ei_new = (int)strlen(line_new);
while (ei_org >= *startp
&& ei_new >= si_new
@@ -2380,12 +2716,13 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
ei_new--;
}
} else {
- const char_u *p1 = line_org + ei_org;
- const char_u *p2 = line_new + ei_new;
+ const char *p1 = line_org + ei_org;
+ const char *p2 = line_new + ei_new;
p1 -= utf_head_off(line_org, p1);
p2 -= utf_head_off(line_new, p2);
+ int l;
if (!diff_equal_char(p1, p2, &l)) {
break;
}
@@ -2415,16 +2752,14 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
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;
-
// Return if 'diff' isn't set.
if (!wp->w_p_diff) {
return false;
}
int idx = -1;
- int i;
- for (i = 0; i < DB_COUNT; i++) {
+ bool other = false;
+ for (int i = 0; i < DB_COUNT; i++) {
if (curtab->tp_diffbuf[i] == wp->w_buffer) {
idx = i;
} else if (curtab->tp_diffbuf[i] != NULL) {
@@ -2464,13 +2799,13 @@ bool diff_infold(win_T *wp, linenr_T lnum)
/// "dp" and "do" commands.
void nv_diffgetput(bool put, size_t count)
{
- exarg_T ea;
- char buf[30];
-
if (bt_prompt(curbuf)) {
vim_beep(BO_OPER);
return;
}
+
+ exarg_T ea;
+ char buf[30];
if (count == 0) {
ea.arg = "";
} else {
@@ -2506,24 +2841,7 @@ static bool valid_diff(diff_T *diff)
/// @param eap
void ex_diffgetput(exarg_T *eap)
{
- linenr_T lnum;
- linenr_T count;
- linenr_T off = 0;
- diff_T *dp;
- diff_T *dfree;
- int i;
- int added;
- char_u *p;
- aco_save_T aco;
- buf_T *buf;
- linenr_T start_skip;
- linenr_T end_skip;
- linenr_T new_count;
- int buf_empty;
- int found_not_ma = false;
int idx_other;
- int idx_from;
- int idx_to;
// Find the current buffer in the list of diff buffers.
int idx_cur = diff_buf_idx(curbuf);
@@ -2533,6 +2851,7 @@ void ex_diffgetput(exarg_T *eap)
}
if (*eap->arg == NUL) {
+ int found_not_ma = false;
// No argument: Find the other buffer in the list of diff buffers.
for (idx_other = 0; idx_other < DB_COUNT; idx_other++) {
if ((curtab->tp_diffbuf[idx_other] != curbuf)
@@ -2555,7 +2874,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 (int i = idx_other + 1; i < DB_COUNT; i++) {
if ((curtab->tp_diffbuf[i] != curbuf)
&& (curtab->tp_diffbuf[i] != NULL)
&& ((eap->cmdidx != CMD_diffput)
@@ -2567,25 +2886,26 @@ void ex_diffgetput(exarg_T *eap)
}
} else {
// Buffer number or pattern given. Ignore trailing white space.
- p = (char_u *)eap->arg + STRLEN(eap->arg);
- while (p > (char_u *)eap->arg && ascii_iswhite(p[-1])) {
+ char *p = eap->arg + strlen(eap->arg);
+ while (p > eap->arg && ascii_iswhite(p[-1])) {
p--;
}
- for (i = 0; ascii_isdigit(eap->arg[i]) && (char_u *)eap->arg + i < p; i++) {}
+ int i;
+ for (i = 0; ascii_isdigit(eap->arg[i]) && eap->arg + i < p; i++) {}
- if ((char_u *)eap->arg + i == p) {
+ if (eap->arg + i == p) {
// digits only
i = (int)atol(eap->arg);
} else {
- i = buflist_findpat(eap->arg, (char *)p, false, true, false);
+ i = buflist_findpat(eap->arg, p, false, true, false);
if (i < 0) {
// error message already given
return;
}
}
- buf = buflist_findnr(i);
+ buf_T *buf = buflist_findnr(i);
if (buf == NULL) {
semsg(_("E102: Can't find buffer \"%s\""), eap->arg);
@@ -2620,20 +2940,19 @@ void ex_diffgetput(exarg_T *eap)
}
}
- if (eap->cmdidx == CMD_diffget) {
- idx_from = idx_other;
- idx_to = idx_cur;
- } else {
- idx_from = idx_cur;
- idx_to = idx_other;
+ aco_save_T aco;
+ if (eap->cmdidx != CMD_diffget) {
// Need to make the other buffer the current buffer to be able to make
// changes in it.
- // set curwin/curbuf to buf and save a few things
+ // Set curwin/curbuf to buf and save a few things.
aucmd_prepbuf(&aco, curtab->tp_diffbuf[idx_other]);
}
+ const int idx_from = eap->cmdidx == CMD_diffget ? idx_other : idx_cur;
+ const int idx_to = eap->cmdidx == CMD_diffget ? idx_cur : idx_other;
+
// May give the warning for a changed buffer here, which can trigger the
// FileChangedRO autocommand, which may do nasty things and mess
// everything up.
@@ -2645,26 +2964,83 @@ void ex_diffgetput(exarg_T *eap)
}
}
+ diffgetput(eap->addr_count, idx_cur, idx_from, idx_to, eap->line1, eap->line2);
+
+ // restore curwin/curbuf and a few other things
+ if (eap->cmdidx != CMD_diffget) {
+ // Syncing undo only works for the current buffer, but we change
+ // another buffer. Sync undo if the command was typed. This isn't
+ // 100% right when ":diffput" is used in a function or mapping.
+ if (KeyTyped) {
+ u_sync(false);
+ }
+ aucmd_restbuf(&aco);
+ }
+
+theend:
+ diff_busy = false;
+
+ if (diff_need_update) {
+ ex_diffupdate(NULL);
+ }
+
+ // Check that the cursor is on a valid character and update its
+ // position. When there were filler lines the topline has become
+ // invalid.
+ check_cursor();
+ changed_line_abv_curs();
+
+ if (diff_need_update) {
+ // redraw already done by ex_diffupdate()
+ diff_need_update = false;
+ } else {
+ // Also need to redraw the other buffers.
+ diff_redraw(false);
+ apply_autocmds(EVENT_DIFFUPDATED, NULL, NULL, false, curbuf);
+ }
+}
+
+/// Apply diffget/diffput to buffers and diffblocks
+///
+/// @param idx_cur index of "curbuf" before aucmd_prepbuf() in the list of diff buffers
+/// @param idx_from index of the buffer to read from in the list of diff buffers
+/// @param idx_to index of the buffer to modify in the list of diff buffers
+static void diffgetput(const int addr_count, const int idx_cur, const int idx_from,
+ const int idx_to, const linenr_T line1, const linenr_T line2)
+{
+ linenr_T off = 0;
diff_T *dprev = NULL;
- for (dp = curtab->tp_first_diff; dp != NULL;) {
- if (dp->df_lnum[idx_cur] > eap->line2 + off) {
+ for (diff_T *dp = curtab->tp_first_diff; dp != NULL;) {
+ if (!addr_count) {
+ // handle the case with adjacent diff blocks
+ while (dp->is_linematched
+ && dp->df_next
+ && dp->df_next->df_lnum[idx_cur] == dp->df_lnum[idx_cur] + dp->df_count[idx_cur]
+ && dp->df_next->df_lnum[idx_cur] == line1 + off + 1) {
+ dprev = dp;
+ dp = dp->df_next;
+ }
+ }
+
+ if (dp->df_lnum[idx_cur] > line2 + off) {
// past the range that was specified
break;
}
- dfree = NULL;
- lnum = dp->df_lnum[idx_to];
- count = dp->df_count[idx_to];
+ diff_T dfree = { 0 };
+ bool did_free = false;
+ linenr_T lnum = dp->df_lnum[idx_to];
+ linenr_T count = dp->df_count[idx_to];
- if ((dp->df_lnum[idx_cur] + dp->df_count[idx_cur] > eap->line1 + off)
+ if ((dp->df_lnum[idx_cur] + dp->df_count[idx_cur] > line1 + off)
&& (u_save(lnum - 1, lnum + count) != FAIL)) {
// Inside the specified range and saving for undo worked.
- start_skip = 0;
- end_skip = 0;
+ linenr_T start_skip = 0;
+ linenr_T end_skip = 0;
- if (eap->addr_count > 0) {
+ if (addr_count > 0) {
// A range was specified: check if lines need to be skipped.
- start_skip = eap->line1 + off - dp->df_lnum[idx_cur];
+ start_skip = line1 + off - dp->df_lnum[idx_cur];
if (start_skip > 0) {
// range starts below start of current diff block
if (start_skip > count) {
@@ -2679,13 +3055,13 @@ void ex_diffgetput(exarg_T *eap)
}
end_skip = dp->df_lnum[idx_cur] + dp->df_count[idx_cur] - 1
- - (eap->line2 + off);
+ - (line2 + off);
if (end_skip > 0) {
// range ends above end of current/from diff block
if (idx_cur == idx_from) {
// :diffput
- i = dp->df_count[idx_cur] - start_skip - end_skip;
+ int i = dp->df_count[idx_cur] - start_skip - end_skip;
if (count > i) {
count = i;
@@ -2704,10 +3080,10 @@ void ex_diffgetput(exarg_T *eap)
}
}
- buf_empty = buf_is_empty(curbuf);
- added = 0;
+ bool buf_empty = buf_is_empty(curbuf);
+ int added = 0;
- for (i = 0; i < count; i++) {
+ for (int i = 0; i < count; i++) {
// remember deleting the last line of the buffer
buf_empty = curbuf->b_ml.ml_line_count == 1;
if (ml_delete(lnum, false) == OK) {
@@ -2715,13 +3091,13 @@ void ex_diffgetput(exarg_T *eap)
}
}
- for (i = 0; i < dp->df_count[idx_from] - start_skip - end_skip; i++) {
+ for (int 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, (char *)p, 0, false);
+ char *p = xstrdup(ml_get_buf(curtab->tp_diffbuf[idx_from], nr, false));
+ ml_append(lnum + i - 1, p, 0, false);
xfree(p);
added++;
if (buf_empty && (curbuf->b_ml.ml_line_count == 2)) {
@@ -2731,12 +3107,13 @@ void ex_diffgetput(exarg_T *eap)
ml_delete((linenr_T)2, false);
}
}
- new_count = dp->df_count[idx_to] + added;
+ linenr_T new_count = dp->df_count[idx_to] + added;
dp->df_count[idx_to] = new_count;
if ((start_skip == 0) && (end_skip == 0)) {
// Check if there are any other buffers and if the diff is
// equal in them.
+ int i;
for (i = 0; i < DB_COUNT; i++) {
if ((curtab->tp_diffbuf[i] != NULL)
&& (i != idx_from)
@@ -2748,14 +3125,9 @@ void ex_diffgetput(exarg_T *eap)
if (i == DB_COUNT) {
// delete the diff entry, the buffers are now equal here
- dfree = dp;
- dp = dp->df_next;
-
- if (dprev == NULL) {
- curtab->tp_first_diff = dp;
- } else {
- dprev->df_next = dp;
- }
+ dfree = *dp;
+ did_free = true;
+ dp = diff_free(curtab, dprev, dp);
}
}
@@ -2774,10 +3146,9 @@ void ex_diffgetput(exarg_T *eap)
}
changed_lines(lnum, 0, lnum + count, added, true);
- if (dfree != NULL) {
+ if (did_free) {
// Diff is deleted, update folds in other windows.
- diff_fold_update(dfree, idx_to);
- xfree(dfree);
+ diff_fold_update(&dfree, idx_to);
}
// mark_adjust() may have made "dp" invalid. We don't know where
@@ -2786,7 +3157,7 @@ void ex_diffgetput(exarg_T *eap)
break;
}
- if (dfree == NULL) {
+ if (!did_free) {
// mark_adjust() may have changed the count in a wrong way
dp->df_count[idx_to] = new_count;
}
@@ -2798,43 +3169,11 @@ void ex_diffgetput(exarg_T *eap)
}
// If before the range or not deleted, go to next diff.
- if (dfree == NULL) {
+ if (!did_free) {
dprev = dp;
dp = dp->df_next;
}
}
-
- // restore curwin/curbuf and a few other things
- if (eap->cmdidx != CMD_diffget) {
- // Syncing undo only works for the current buffer, but we change
- // another buffer. Sync undo if the command was typed. This isn't
- // 100% right when ":diffput" is used in a function or mapping.
- if (KeyTyped) {
- u_sync(false);
- }
- aucmd_restbuf(&aco);
- }
-
-theend:
- diff_busy = false;
- if (diff_need_update) {
- ex_diffupdate(NULL);
- }
-
- // Check that the cursor is on a valid character and update its
- // position. When there were filler lines the topline has become
- // invalid.
- check_cursor();
- changed_line_abv_curs();
-
- if (diff_need_update) {
- // redraw already done by ex_diffupdate()
- diff_need_update = false;
- } else {
- // Also need to redraw the other buffers.
- diff_redraw(false);
- apply_autocmds(EVENT_DIFFUPDATED, NULL, NULL, false, curbuf);
- }
}
/// Update folds for all diff buffers for entry "dp".
@@ -3058,7 +3397,7 @@ linenr_T diff_lnum_win(linenr_T lnum, win_T *wp)
/// Handle an ED style diff line.
/// Return FAIL if the line does not contain diff info.
///
-static int parse_diff_ed(char_u *line, diffhunk_T *hunk)
+static int parse_diff_ed(char *line, diffhunk_T *hunk)
{
long l1, l2;
@@ -3066,7 +3405,7 @@ static int parse_diff_ed(char_u *line, diffhunk_T *hunk)
// change: {first}[,{last}]c{first}[,{last}]
// append: {first}a{first}[,{last}]
// delete: {first}[,{last}]d{first}
- char *p = (char *)line;
+ char *p = line;
linenr_T f1 = getdigits_int32(&p, true, 0);
if (*p == ',') {
p++;
@@ -3110,11 +3449,11 @@ static int parse_diff_ed(char_u *line, diffhunk_T *hunk)
/// Parses unified diff with zero(!) context lines.
/// Return FAIL if there is no diff information in "line".
///
-static int parse_diff_unified(char_u *line, diffhunk_T *hunk)
+static int parse_diff_unified(char *line, diffhunk_T *hunk)
{
// Parse unified diff hunk header:
// @@ -oldline,oldcount +newline,newcount @@
- char *p = (char *)line;
+ char *p = line;
if (*p++ == '@' && *p++ == '@' && *p++ == ' ' && *p++ == '-') {
long oldcount;
long newline;
@@ -3166,13 +3505,11 @@ static int parse_diff_unified(char_u *line, diffhunk_T *hunk)
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 = (linenr_T)start_a + 1;
- p->count_orig = count_a;
- 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;
+ GA_APPEND(diffhunk_T, &(dout->dout_ga), ((diffhunk_T){
+ .lnum_orig = (linenr_T)start_a + 1,
+ .count_orig = count_a,
+ .lnum_new = (linenr_T)start_b + 1,
+ .count_new = count_b,
+ }));
return 0;
}
diff --git a/src/nvim/diff.h b/src/nvim/diff.h
index 53fc5aa077..1f64465336 100644
--- a/src/nvim/diff.h
+++ b/src/nvim/diff.h
@@ -1,7 +1,10 @@
#ifndef NVIM_DIFF_H
#define NVIM_DIFF_H
+#include <stdbool.h>
+
#include "nvim/ex_cmds_defs.h"
+#include "nvim/macros.h"
#include "nvim/pos.h"
// Value set from 'diffopt'.
diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c
index 0f511bd37c..a057978a5e 100644
--- a/src/nvim/digraph.c
+++ b/src/nvim/digraph.c
@@ -8,24 +8,34 @@
#include <assert.h>
#include <inttypes.h>
#include <stdbool.h>
+#include <string.h>
#include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/digraph.h"
#include "nvim/drawscreen.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/highlight_defs.h"
+#include "nvim/keycodes.h"
#include "nvim/mapping.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/normal.h"
+#include "nvim/option_defs.h"
#include "nvim/os/input.h"
#include "nvim/runtime.h"
#include "nvim/strings.h"
+#include "nvim/types.h"
#include "nvim/vim.h"
typedef int result_T;
@@ -52,7 +62,6 @@ static garray_T user_digraphs = { 0, 0, (int)sizeof(digr_T), 10, NULL };
/// Note: Characters marked with XX are not included literally, because some
/// compilers cannot handle them (Amiga SAS/C is the most picky one).
static digr_T digraphdefault[] =
-
// digraphs for Unicode from RFC1345
// (also work for ISO-8859-1 aka latin1)
{
@@ -1480,7 +1489,7 @@ int do_digraph(int c)
/// Find a digraph for "val". If found return the string to display it.
/// If not found return NULL.
-char_u *get_digraph_for_char(int val_arg)
+char *get_digraph_for_char(int val_arg)
{
const int val = val_arg;
const digr_T *dp;
@@ -1497,7 +1506,7 @@ char_u *get_digraph_for_char(int val_arg)
r[0] = dp->char1;
r[1] = dp->char2;
r[2] = NUL;
- return r;
+ return (char *)r;
}
dp++;
}
@@ -1519,30 +1528,31 @@ int get_digraph(bool cmdline)
no_mapping--;
allow_keys--;
- if (c != ESC) {
- // ESC cancels CTRL-K
- if (IS_SPECIAL(c)) {
- // insert special key code
- return c;
- }
+ if (c == ESC) { // ESC cancels CTRL-K
+ return NUL;
+ }
- if (cmdline) {
- if ((char2cells(c) == 1) && c < 128 && (cmdline_star == 0)) {
- putcmdline((char)c, true);
- }
- } else {
- add_to_showcmd(c);
- }
- no_mapping++;
- allow_keys++;
- int cc = plain_vgetc();
- no_mapping--;
- allow_keys--;
-
- if (cc != ESC) {
- // ESC cancels CTRL-K
- return digraph_get(c, cc, true);
+ if (IS_SPECIAL(c)) {
+ // insert special key code
+ return c;
+ }
+
+ if (cmdline) {
+ if ((char2cells(c) == 1) && c < 128 && (cmdline_star == 0)) {
+ putcmdline((char)c, true);
}
+ } else {
+ add_to_showcmd(c);
+ }
+ no_mapping++;
+ allow_keys++;
+ int cc = plain_vgetc();
+ no_mapping--;
+ allow_keys--;
+
+ if (cc != ESC) {
+ // ESC cancels CTRL-K
+ return digraph_get(c, cc, true);
}
return NUL;
}
@@ -1578,7 +1588,7 @@ static int getexactdigraph(int char1, int char2, bool meta_char)
if (retval == 0) {
dp = digraphdefault;
- for (int i = 0; dp->char1 != 0; i++) {
+ while (dp->char1 != 0) {
if (((int)dp->char1 == char1) && ((int)dp->char2 == char2)) {
retval = dp->result;
break;
@@ -1646,8 +1656,8 @@ static void registerdigraph(int char1, int char2, int n)
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;
+ char msg[MB_MAXBYTES + 1];
+ msg[utf_char2bytes(char1, msg)] = NUL;
semsg(_(e_digraph_must_be_just_two_characters_str), msg);
return false;
}
@@ -1695,7 +1705,7 @@ static void digraph_header(const char *msg)
if (msg_col > 0) {
msg_putchar('\n');
}
- msg_outtrans_attr((const char_u *)msg, HL_ATTR(HLF_CM));
+ msg_outtrans_attr(msg, HL_ATTR(HLF_CM));
msg_putchar('\n');
}
@@ -1707,7 +1717,7 @@ void listdigraphs(bool use_headers)
const digr_T *dp = digraphdefault;
- for (int i = 0; dp->char1 != NUL && !got_int; i++) {
+ while (dp->char1 != NUL && !got_int) {
digr_T tmp;
// May need to convert the result to 'encoding'.
@@ -1739,16 +1749,16 @@ 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;
+ char buf[30];
+ buf[0] = (char)dp->char1;
+ buf[1] = (char)dp->char2;
buf[2] = NUL;
- tv_list_append_string(l2, (char *)buf, -1);
+ tv_list_append_string(l2, buf, -1);
- char_u *p = buf;
- p += utf_char2bytes(dp->result, (char *)p);
+ char *p = buf;
+ p += utf_char2bytes(dp->result, p);
*p = NUL;
- tv_list_append_string(l2, (char *)buf, -1);
+ tv_list_append_string(l2, buf, -1);
}
void digraph_getlist_common(bool list_all, typval_T *rettv)
@@ -1759,7 +1769,7 @@ void digraph_getlist_common(bool list_all, typval_T *rettv)
if (list_all) {
dp = digraphdefault;
- for (int i = 0; dp->char1 != NUL && !got_int; i++) {
+ while (dp->char1 != NUL && !got_int) {
digr_T tmp;
tmp.char1 = dp->char1;
tmp.char2 = dp->char2;
@@ -1814,57 +1824,59 @@ struct dg_header_entry {
static void printdigraph(const digr_T *dp, result_T *previous)
FUNC_ATTR_NONNULL_ARG(1)
{
- char_u buf[30];
+ char buf[30];
int list_width = 13;
- if (dp->result != 0) {
- if (previous != NULL) {
- for (int i = 0; header_table[i].dg_header != NULL; i++) {
- if (*previous < header_table[i].dg_start
- && dp->result >= header_table[i].dg_start
- && dp->result < header_table[i + 1].dg_start) {
- digraph_header(_(header_table[i].dg_header));
- break;
- }
+ if (dp->result == 0) {
+ return;
+ }
+
+ if (previous != NULL) {
+ for (int i = 0; header_table[i].dg_header != NULL; i++) {
+ if (*previous < header_table[i].dg_start
+ && dp->result >= header_table[i].dg_start
+ && dp->result < header_table[i + 1].dg_start) {
+ digraph_header(_(header_table[i].dg_header));
+ break;
}
- *previous = dp->result;
- }
- if (msg_col > Columns - list_width) {
- msg_putchar('\n');
}
+ *previous = dp->result;
+ }
+ if (msg_col > Columns - list_width) {
+ 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;
- while (spaces--) {
- msg_putchar(' ');
- }
+ // 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;
+ while (spaces--) {
+ msg_putchar(' ');
}
+ }
- char_u *p = &buf[0];
- *p++ = dp->char1;
- *p++ = dp->char2;
- *p++ = ' ';
- *p = NUL;
- msg_outtrans((char *)buf);
- p = buf;
+ char *p = &buf[0];
+ *p++ = (char)dp->char1;
+ *p++ = (char)dp->char2;
+ *p++ = ' ';
+ *p = NUL;
+ msg_outtrans(buf);
+ p = buf;
- // add a space to draw a composing char on
- if (utf_iscomposing(dp->result)) {
- *p++ = ' ';
- }
- p += utf_char2bytes(dp->result, (char *)p);
+ // add a space to draw a composing char on
+ if (utf_iscomposing(dp->result)) {
+ *p++ = ' ';
+ }
+ p += utf_char2bytes(dp->result, p);
- *p = NUL;
- msg_outtrans_attr(buf, HL_ATTR(HLF_8));
- p = buf;
- if (char2cells(dp->result) == 1) {
- *p++ = ' ';
- }
- assert(p >= buf);
- vim_snprintf((char *)p, sizeof(buf) - (size_t)(p - buf), " %3d", dp->result);
- msg_outtrans((char *)buf);
+ *p = NUL;
+ msg_outtrans_attr(buf, HL_ATTR(HLF_8));
+ p = buf;
+ if (char2cells(dp->result) == 1) {
+ *p++ = ' ';
}
+ assert(p >= buf);
+ vim_snprintf(p, sizeof(buf) - (size_t)(p - buf), " %3d", dp->result);
+ msg_outtrans(buf);
}
/// Get the two digraph characters from a typval.
@@ -1873,7 +1885,7 @@ 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;
+ const char *p = chars;
if (p != NULL) {
if (*p != NUL) {
@@ -1905,7 +1917,7 @@ static bool digraph_set_common(const typval_T *argchars, const typval_T *argdigr
if (digraph == NULL) {
return false;
}
- const char_u *p = (const char_u *)digraph;
+ const char *p = digraph;
int n = mb_cptr2char_adv(&p);
if (*p != NUL) {
semsg(_(e_digraph_argument_must_be_one_character_str), digraph);
@@ -1917,7 +1929,7 @@ static bool digraph_set_common(const typval_T *argchars, const typval_T *argdigr
}
/// "digraph_get()" function
-void f_digraph_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_digraph_get(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL; // Return empty string for failure
@@ -1926,19 +1938,19 @@ void f_digraph_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (digraphs == NULL) {
return;
}
- if (STRLEN(digraphs) != 2) {
+ 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);
+ char buf[NUMBUFLEN];
+ buf[utf_char2bytes(code, buf)] = NUL;
+ rettv->vval.v_string = xstrdup(buf);
}
/// "digraph_getlist()" function
-void f_digraph_getlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_digraph_getlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
bool flag_list_all;
@@ -1957,7 +1969,7 @@ void f_digraph_getlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "digraph_set()" function
-void f_digraph_set(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_digraph_set(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_BOOL;
rettv->vval.v_bool = kBoolVarFalse;
@@ -1970,7 +1982,7 @@ void f_digraph_set(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "digraph_setlist()" function
-void f_digraph_setlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_digraph_setlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_BOOL;
rettv->vval.v_bool = kBoolVarFalse;
@@ -2010,8 +2022,8 @@ void f_digraph_setlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// structure used for b_kmap_ga.ga_data
typedef struct {
- char_u *from;
- char_u *to;
+ char *from;
+ char *to;
} kmap_T;
#define KMAP_MAXLEN 20 // maximum length of "from" or "to"
@@ -2036,7 +2048,7 @@ char *keymap_init(void)
// Source the keymap file. It will contain a ":loadkeymap" command
// which will call ex_loadkeymap() below.
- buflen = STRLEN(curbuf->b_p_keymap) + STRLEN(p_enc) + 14;
+ buflen = strlen(curbuf->b_p_keymap) + strlen(p_enc) + 14;
buf = xmalloc(buflen);
// try finding "keymap/'keymap'_'encoding'.vim" in 'runtimepath'
@@ -2064,10 +2076,10 @@ char *keymap_init(void)
/// @param eap
void ex_loadkeymap(exarg_T *eap)
{
- char_u *s;
+ char *s;
#define KMAP_LLEN 200 // max length of "to" and "from" together
- char_u buf[KMAP_LLEN + 11];
+ char buf[KMAP_LLEN + 11];
char *save_cpo = p_cpo;
if (!getline_equal(eap->getline, eap->cookie, getsourceline)) {
@@ -2092,17 +2104,17 @@ void ex_loadkeymap(exarg_T *eap)
break;
}
- char_u *p = (char_u *)skipwhite(line);
+ char *p = 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 = (char_u *)skipwhite((char *)s);
+ kp->from = xstrnsave(p, (size_t)(s - p));
+ p = skipwhite(s);
s = skiptowhite(p);
- kp->to = vim_strnsave(p, (size_t)(s - p));
+ kp->to = xstrnsave(p, (size_t)(s - p));
- if ((STRLEN(kp->from) + STRLEN(kp->to) >= KMAP_LLEN)
+ if ((strlen(kp->from) + strlen(kp->to) >= KMAP_LLEN)
|| (*kp->from == NUL)
|| (*kp->to == NUL)) {
if (*kp->to == NUL) {
@@ -2118,7 +2130,7 @@ void ex_loadkeymap(exarg_T *eap)
// setup ":lmap" to map the keys
for (int i = 0; i < curbuf->b_kmap_ga.ga_len; i++) {
- vim_snprintf((char *)buf, sizeof(buf), "<buffer> %s %s",
+ vim_snprintf(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(MAPTYPE_MAP, buf, MODE_LANGMAP, false);
@@ -2158,7 +2170,7 @@ static void keymap_unload(void)
for (int i = 0; i < curbuf->b_kmap_ga.ga_len; i++) {
vim_snprintf(buf, sizeof(buf), "<buffer> %s", kp[i].from);
- (void)do_map(MAPTYPE_UNMAP, (char_u *)buf, MODE_LANGMAP, false);
+ (void)do_map(MAPTYPE_UNMAP, buf, MODE_LANGMAP, false);
}
keymap_ga_clear(&curbuf->b_kmap_ga);
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c
index 2b1b2607fb..e24d86b353 100644
--- a/src/nvim/drawline.c
+++ b/src/nvim/drawline.c
@@ -5,38 +5,69 @@
// This is the middle level, drawscreen.c is the top and grid.c/screen.c the lower level.
#include <assert.h>
-#include <inttypes.h>
+#include <limits.h>
#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include "nvim/arabic.h"
+#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/cursor_shape.h"
+#include "nvim/decoration.h"
+#include "nvim/decoration_provider.h"
#include "nvim/diff.h"
#include "nvim/drawline.h"
+#include "nvim/eval.h"
+#include "nvim/extmark_defs.h"
#include "nvim/fold.h"
+#include "nvim/garray.h"
+#include "nvim/globals.h"
#include "nvim/grid.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
#include "nvim/indent.h"
+#include "nvim/mark.h"
#include "nvim/match.h"
+#include "nvim/mbyte.h"
+#include "nvim/memline.h"
+#include "nvim/memory.h"
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/plines.h"
+#include "nvim/pos.h"
#include "nvim/quickfix.h"
-#include "nvim/search.h"
+#include "nvim/screen.h"
#include "nvim/sign.h"
#include "nvim/spell.h"
#include "nvim/state.h"
+#include "nvim/statusline.h"
+#include "nvim/strings.h"
#include "nvim/syntax.h"
-#include "nvim/undo.h"
-#include "nvim/window.h"
+#include "nvim/terminal.h"
+#include "nvim/types.h"
+#include "nvim/ui.h"
+#include "nvim/vim.h"
#define MB_FILLER_CHAR '<' // character used when a double-width character
// doesn't fit.
+/// possible draw states in win_line(), drawn in sequence.
+typedef enum {
+ WL_START = 0, // nothing done yet
+ WL_CMDLINE, // cmdline window column
+ WL_FOLD, // 'foldcolumn'
+ WL_SIGN, // column for signs
+ WL_NR, // line number
+ WL_STC, // 'statuscolumn'
+ WL_BRI, // 'breakindent'
+ WL_SBR, // 'showbreak' or 'diff'
+ WL_LINE, // text in the line
+} LineDrawState;
+
/// for line_putchar. Contains the state that needs to be remembered from
/// putting one character to the next.
typedef struct {
@@ -109,9 +140,9 @@ static void margin_columns_win(win_T *wp, int *left_col, int *right_col)
/// Handles composing chars and arabic shaping state.
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((char *)p);
- int c_len = utfc_ptr2len((char *)p);
+ const char *p = s->p;
+ int cells = utf_ptr2cells(p);
+ int c_len = utfc_ptr2len(p);
int u8c, u8cc[MAX_MCO];
if (cells > maxcells) {
return -1;
@@ -123,22 +154,22 @@ static int line_putchar(buf_T *buf, LineState *s, schar_T *dest, int maxcells, b
schar_from_ascii(dest[c], ' ');
}
goto done;
- } else if (*p < 0x80 && u8cc[0] == 0) {
- schar_from_ascii(dest[0], (char)(*p));
+ } else if ((uint8_t)(*p) < 0x80 && u8cc[0] == 0) {
+ schar_from_ascii(dest[0], *p);
s->prev_c = u8c;
} else {
if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) {
// Do Arabic shaping.
int pc, pc1, nc;
int pcc[MAX_MCO];
- int firstbyte = *p;
+ int firstbyte = (uint8_t)(*p);
// The idea of what is the previous and next
// character depends on 'rightleft'.
if (rl) {
pc = s->prev_c;
pc1 = s->prev_c1;
- nc = utf_ptr2char((char *)p + c_len);
+ nc = utf_ptr2char(p + c_len);
s->prev_c1 = u8cc[0];
} else {
pc = utfc_ptr2char(p + c_len, pcc);
@@ -277,7 +308,7 @@ static bool use_cursor_line_sign(win_T *wp, linenr_T lnum)
}
// 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.
+// 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
@@ -285,8 +316,8 @@ static bool use_cursor_line_sign(win_T *wp, linenr_T lnum)
// @param sign_idxp Index of the displayed sign
static void get_sign_display_info(bool nrcol, win_T *wp, linenr_T lnum, SignTextAttrs sattrs[],
int row, int startrow, int filler_lines, int filler_todo,
- 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,
+ int *c_extrap, int *c_finalp, char *extra, size_t extra_size,
+ char **pp_extra, int *n_extrap, int *char_attrp, int sign_idx,
int cul_attr)
{
// Draw cells with the sign value or blank.
@@ -320,16 +351,16 @@ static void get_sign_display_info(bool nrcol, win_T *wp, linenr_T lnum, SignText
STRCAT(extra, *pp_extra);
STRCAT(extra, " ");
*pp_extra = extra;
- *n_extrap = (int)STRLEN(*pp_extra);
+ *n_extrap = (int)strlen(*pp_extra);
} else {
- size_t symbol_blen = STRLEN(*pp_extra);
+ size_t symbol_blen = strlen(*pp_extra);
// TODO(oni-link): Is sign text already extended to
// full cell width?
assert((size_t)win_signcol_width(wp) >= mb_string2cells((char *)(*pp_extra)));
// symbol(s) bytes + (filling spaces) (one byte each)
*n_extrap = (int)symbol_blen + win_signcol_width(wp) -
- (int)mb_string2cells((char *)(*pp_extra));
+ (int)mb_string2cells(*pp_extra);
assert(extra_size > symbol_blen);
memset(extra, ' ', extra_size);
@@ -367,6 +398,102 @@ static int get_sign_attrs(buf_T *buf, linenr_T lnum, SignTextAttrs *sattrs, int
return num_signs;
}
+/// Prepare and build the 'statuscolumn' string for line "lnum" in window "wp".
+/// Fill "stcp" with the built status column string and attributes.
+/// This can be called three times per win_line(), once for virt_lines, once for
+/// the start of the buffer line "lnum" and once for the wrapped lines.
+///
+/// @param[out] stcp Status column attributes
+static void get_statuscol_str(win_T *wp, linenr_T lnum, int row, int startrow, int filler_lines,
+ int cul_attr, int sign_num_attr, int sign_cul_attr, statuscol_T *stcp,
+ foldinfo_T foldinfo, SignTextAttrs *sattrs)
+{
+ long relnum = -1;
+ bool use_cul = use_cursor_line_sign(wp, lnum);
+ int virtnum = row - startrow - filler_lines;
+
+ set_vim_var_nr(VV_VIRTNUM, virtnum);
+ // When called the first time for line "lnum" set num_attr
+ if (stcp->num_attr == 0) {
+ stcp->num_attr = sign_num_attr ? sign_num_attr
+ : get_line_number_attr(wp, lnum, row, startrow, filler_lines);
+ }
+ // When called for the first non-filler row of line "lnum" set num v:vars and fold column
+ if (virtnum == 0) {
+ relnum = labs(get_cursor_rel_lnum(wp, lnum));
+ if (compute_foldcolumn(wp, 0)) {
+ size_t n = fill_foldcolumn(stcp->fold_text, wp, foldinfo, lnum);
+ stcp->fold_text[n] = NUL;
+ stcp->fold_attr = win_hl_attr(wp, use_cul ? HLF_CLF : HLF_FC);
+ }
+ }
+ // Make sure to clear->set->clear sign column for filler->first->wrapped lines
+ int i = 0;
+ for (; i < wp->w_scwidth; i++) {
+ SignTextAttrs *sattr = virtnum ? NULL : sign_get_attr(i, sattrs, wp->w_scwidth);
+ stcp->sign_text[i] = sattr && sattr->text ? sattr->text : " ";
+ stcp->sign_attr[i] = sattr ? (use_cul && sign_cul_attr ? sign_cul_attr : sattr->hl_attr_id)
+ : win_hl_attr(wp, use_cul ? HLF_CLS : HLF_SC);
+ }
+ stcp->sign_text[i] = NULL;
+
+ int width = build_statuscol_str(wp, lnum, relnum, stcp->width,
+ ' ', stcp->text, &stcp->hlrec, stcp);
+ // Force a redraw in case of error or when truncated
+ if (*wp->w_p_stc == NUL || (stcp->truncate > 0 && wp->w_nrwidth < MAX_NUMBERWIDTH)) {
+ if (stcp->truncate) { // Avoid truncating 'statuscolumn'
+ wp->w_nrwidth = MIN(MAX_NUMBERWIDTH, wp->w_nrwidth + stcp->truncate);
+ wp->w_nrwidth_width = wp->w_nrwidth;
+ } else { // 'statuscolumn' reset due to error
+ wp->w_nrwidth_line_count = 0;
+ wp->w_nrwidth = (wp->w_p_nu || wp->w_p_rnu) * number_width(wp);
+ }
+ wp->w_redr_statuscol = true;
+ return;
+ }
+
+ // Reset text/highlight pointer and current attr for new line
+ stcp->textp = stcp->text;
+ stcp->hlrecp = stcp->hlrec;
+ stcp->cur_attr = stcp->num_attr;
+ stcp->text_end = stcp->text + strlen(stcp->text);
+
+ int fill = stcp->width - width;
+ if (fill > 0) {
+ // Fill up with ' '
+ memset(stcp->text_end, ' ', (size_t)fill);
+ *(stcp->text_end += fill) = NUL;
+ }
+}
+
+/// Get information needed to display the next segment in the 'statuscolumn'.
+/// If not yet at the end, prepare for next segment and decrement "draw_state".
+///
+/// @param stcp Status column attributes
+/// @param[out] draw_state Current draw state in win_line()
+static void get_statuscol_display_info(statuscol_T *stcp, LineDrawState *draw_state, int *char_attr,
+ int *n_extrap, int *c_extrap, int *c_finalp, char **pp_extra)
+{
+ *c_extrap = NUL;
+ *c_finalp = NUL;
+ do {
+ *draw_state = WL_STC;
+ *char_attr = stcp->cur_attr;
+ *pp_extra = stcp->textp;
+ *n_extrap = (int)((stcp->hlrecp->start ? stcp->hlrecp->start : stcp->text_end) - stcp->textp);
+ // Prepare for next highlight section if not yet at the end
+ if (stcp->textp + *n_extrap < stcp->text_end) {
+ int hl = stcp->hlrecp->userhl;
+ stcp->textp = stcp->hlrecp->start;
+ stcp->cur_attr = hl < 0 ? syn_id2attr(-stcp->hlrecp->userhl)
+ : hl > 0 ? hl : stcp->num_attr;
+ stcp->hlrecp++;
+ *draw_state = WL_STC - 1;
+ }
+ // Skip over empty highlight sections
+ } while (*n_extrap == 0 && stcp->textp < stcp->text_end);
+}
+
/// Return true if CursorLineNr highlight is to be used for the number column.
///
/// - 'cursorline' must be set
@@ -386,7 +513,7 @@ static bool use_cursor_line_nr(win_T *wp, linenr_T lnum, int row, int startrow,
&& (wp->w_p_culopt_flags & CULOPT_LINE)));
}
-static inline void get_line_number_str(win_T *wp, linenr_T lnum, char_u *buf, size_t buf_len)
+static inline void get_line_number_str(win_T *wp, linenr_T lnum, char *buf, size_t buf_len)
{
long num;
char *fmt = "%*ld ";
@@ -404,7 +531,7 @@ static inline void get_line_number_str(win_T *wp, linenr_T lnum, char_u *buf, si
}
}
- snprintf((char *)buf, buf_len, fmt, number_width(wp), num);
+ snprintf(buf, buf_len, fmt, number_width(wp), num);
}
static int get_line_number_attr(win_T *wp, linenr_T lnum, int row, int startrow, int filler_lines)
@@ -480,29 +607,28 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
long vcol = 0; // virtual column (for tabs)
long vcol_sbr = -1; // virtual column after showbreak
long vcol_prev = -1; // "vcol" of previous character
- char_u *line; // current line
- char_u *ptr; // current position in "line"
+ char *line; // current line
+ char *ptr; // current position in "line"
int row; // row in the window, excl w_winrow
ScreenGrid *grid = &wp->w_grid; // grid specific to the window
- char_u extra[57]; // sign, line number and 'fdc' must
+ char extra[57]; // sign, line number and 'fdc' must
// fit in here
int n_extra = 0; // number of extra chars
- char_u *p_extra = NULL; // string of extra chars, plus NUL
- char_u *p_extra_free = NULL; // p_extra needs to be freed
+ char *p_extra = NULL; // string of extra chars, plus NUL
+ char *p_extra_free = NULL; // p_extra needs to be freed
int c_extra = NUL; // extra chars, all the same
int c_final = NUL; // final char, mandatory if set
int extra_attr = 0; // attributes when n_extra != 0
- static char_u *at_end_str = (char_u *)""; // used for p_extra when displaying
- // curwin->w_p_lcs_chars.eol at
- // end-of-line
+ static char *at_end_str = ""; // used for p_extra when displaying curwin->w_p_lcs_chars.eol
+ // at end-of-line
int lcs_eol_one = wp->w_p_lcs_chars.eol; // 'eol' until it's been used
int lcs_prec_todo = wp->w_p_lcs_chars.prec; // 'prec' until it's been used
bool has_fold = foldinfo.fi_level != 0 && foldinfo.fi_lines > 0;
// saved "extra" items for when draw_state becomes WL_LINE (again)
int saved_n_extra = 0;
- char_u *saved_p_extra = NULL;
+ char *saved_p_extra = NULL;
int saved_c_extra = 0;
int saved_c_final = 0;
int saved_char_attr = 0;
@@ -525,7 +651,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
int char_attr = 0; // attributes for next character
bool attr_pri = false; // char_attr has priority
bool area_highlighting = false; // Visual or incsearch highlighting in this line
- int attr = 0; // attributes for area highlighting
+ int vi_attr = 0; // attributes for Visual and incsearch highlighting
int area_attr = 0; // attributes desired by highlighting
int search_attr = 0; // attributes desired by 'hlsearch'
int vcol_save_attr = 0; // saved attr for 'cursorcolumn'
@@ -537,7 +663,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
int *color_cols = NULL; // pointer to according columns array
bool has_spell = false; // this buffer has spell checking
#define SPWORDLEN 150
- char_u nextline[SPWORDLEN * 2]; // text with start of the next line
+ char nextline[SPWORDLEN * 2]; // text with start of the next line
int nextlinecol = 0; // column where nextline[] starts
int nextline_idx = 0; // index in nextline[] where next line
// starts
@@ -576,7 +702,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
bool has_decor = false; // this buffer has decoration
int win_col_offset = 0; // offset for window columns
- char_u buf_fold[FOLD_TEXT_LEN]; // Hold value returned by get_foldtext
+ char buf_fold[FOLD_TEXT_LEN]; // Hold value returned by get_foldtext
bool area_active = false;
@@ -588,17 +714,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
int left_curline_col = 0;
int right_curline_col = 0;
- // 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
- int draw_state = WL_START; // what to draw next
+ LineDrawState draw_state = WL_START; // what to draw next
+ int match_conc = 0; ///< cchar for match functions
+ bool on_last_col = false;
int syntax_flags = 0;
int syntax_seqnr = 0;
int prev_syntax_id = 0;
@@ -608,7 +727,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
///< force wrapping
int vcol_off = 0; ///< offset for concealed characters
int did_wcol = false;
- int match_conc = 0; ///< cchar for match functions
int old_boguscols = 0;
#define VCOL_HLC (vcol - vcol_off)
#define FIX_FOR_BOGUSCOLS \
@@ -654,7 +772,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
has_decor = decor_redraw_line(buf, lnum - 1, &decor_state);
- providers_invoke_line(wp, providers, lnum - 1, &has_decor, provider_err);
+ decor_providers_invoke_line(wp, providers, lnum - 1, &has_decor, provider_err);
if (*provider_err) {
provider_err_virt_text(lnum, *provider_err);
@@ -772,7 +890,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
// if inverting in this line set area_highlighting
if (fromcol >= 0) {
area_highlighting = true;
- attr = win_hl_attr(wp, HLF_V);
+ vi_attr = win_hl_attr(wp, HLF_V);
}
// handle 'incsearch' and ":s///c" highlighting
} else if (highlight_match
@@ -796,15 +914,16 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
tocol = fromcol + 1;
}
area_highlighting = true;
- attr = win_hl_attr(wp, HLF_I);
+ vi_attr = win_hl_attr(wp, HLF_I);
}
}
int bg_attr = win_bg_attr(wp);
- filler_lines = diff_check(wp, lnum);
- if (filler_lines < 0) {
- if (filler_lines == -1) {
+ int linestatus = 0;
+ filler_lines = diff_check_with_linestatus(wp, lnum, &linestatus);
+ if (filler_lines < 0 || linestatus < 0) {
+ if (filler_lines == -1 || linestatus == -1) {
if (diff_find_change(wp, lnum, &change_start, &change_end)) {
diff_hlf = HLF_ADD; // added line
} else if (change_start == 0) {
@@ -815,11 +934,13 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
} else {
diff_hlf = HLF_ADD; // added line
}
- filler_lines = 0;
+ if (linestatus == 0) {
+ filler_lines = 0;
+ }
area_highlighting = true;
}
VirtLines virt_lines = KV_INITIAL_VALUE;
- int n_virt_lines = decor_virt_lines(wp, lnum, &virt_lines);
+ int n_virt_lines = decor_virt_lines(wp, lnum, &virt_lines, has_fold);
filler_lines += n_virt_lines;
if (lnum == wp->w_topline) {
filler_lines = wp->w_topfill;
@@ -864,7 +985,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
line_attr_lowprio_save = line_attr_lowprio;
}
- line = end_fill ? (char_u *)"" : ml_get_buf(wp->w_buffer, lnum, false);
+ line = end_fill ? "" : ml_get_buf(wp->w_buffer, lnum, false);
ptr = line;
if (has_spell && !number_only) {
@@ -881,7 +1002,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
nextlinecol = MAXCOL;
nextline_idx = 0;
} else {
- v = (long)STRLEN(line);
+ v = (long)strlen(line);
if (v < SPWORDLEN) {
// Short line, use it completely and append the start of the
// next line.
@@ -892,7 +1013,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
} else {
// Long line, use only the last SPWORDLEN bytes.
nextlinecol = (int)v - SPWORDLEN;
- memmove(nextline, line + nextlinecol, SPWORDLEN); // -V512
+ memmove(nextline, line + nextlinecol, SPWORDLEN); // -V1086
nextline_idx = SPWORDLEN + 1;
}
}
@@ -909,7 +1030,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
}
// find start of trailing whitespace
if (wp->w_p_lcs_chars.trail) {
- trailcol = (colnr_T)STRLEN(ptr);
+ trailcol = (colnr_T)strlen(ptr);
while (trailcol > (colnr_T)0 && ascii_iswhite(ptr[trailcol - 1])) {
trailcol--;
}
@@ -939,13 +1060,20 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
v = wp->w_leftcol;
}
if (v > 0 && !number_only) {
- char_u *prev_ptr = ptr;
- while (vcol < v && *ptr != NUL) {
- c = win_lbr_chartabsize(wp, line, ptr, (colnr_T)vcol, NULL);
- vcol += c;
- prev_ptr = ptr;
- MB_PTR_ADV(ptr);
+ char *prev_ptr = ptr;
+ chartabsize_T cts;
+ int charsize;
+
+ init_chartabsize_arg(&cts, wp, lnum, (colnr_T)vcol, line, ptr);
+ while (cts.cts_vcol < v && *cts.cts_ptr != NUL) {
+ charsize = win_lbr_chartabsize(&cts, NULL);
+ cts.cts_vcol += charsize;
+ prev_ptr = cts.cts_ptr;
+ MB_PTR_ADV(cts.cts_ptr);
}
+ vcol = cts.cts_vcol;
+ ptr = cts.cts_ptr;
+ clear_chartabsize_arg(&cts);
// When:
// - 'cuc' is set, or
@@ -963,11 +1091,11 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
// Handle a character that's not completely on the screen: Put ptr at
// that character but skip the first few screen characters.
if (vcol > v) {
- vcol -= c;
+ vcol -= charsize;
ptr = prev_ptr;
// If the character fits on the screen, don't need to skip it.
// Except for a TAB.
- if (utf_ptr2cells((char *)ptr) >= c || *ptr == TAB) {
+ if (utf_ptr2cells(ptr) >= charsize || *ptr == TAB) {
n_skip = (int)(v - vcol);
}
}
@@ -1068,7 +1196,16 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
extra_check = true;
}
+ statuscol_T statuscol = { 0 };
+ if (*wp->w_p_stc != NUL) {
+ // Draw the 'statuscolumn' if option is set.
+ statuscol.draw = true;
+ statuscol.width = win_col_off(wp);
+ }
+
int sign_idx = 0;
+ int virt_line_index;
+ int virt_line_offset = -1;
// Repeat for the whole displayed line.
for (;;) {
int has_match_conc = 0; ///< match wants to conceal
@@ -1096,8 +1233,25 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
}
if (draw_state == WL_FOLD - 1 && n_extra == 0) {
- int fdc = compute_foldcolumn(wp, 0);
+ if (filler_todo > 0) {
+ int index = filler_todo - (filler_lines - n_virt_lines);
+ if (index > 0) {
+ virt_line_index = (int)kv_size(virt_lines) - index;
+ assert(virt_line_index >= 0);
+ virt_line_offset = kv_A(virt_lines, virt_line_index).left_col ? 0 : win_col_off(wp);
+ }
+ }
+ if (!virt_line_offset) {
+ // Skip the column states if there is a "virt_left_col" line.
+ draw_state = WL_BRI - 1;
+ } else if (statuscol.draw) {
+ // Skip fold, sign and number states if 'statuscolumn' is set.
+ draw_state = WL_STC - 1;
+ }
+ }
+ if (draw_state == WL_FOLD - 1 && n_extra == 0) {
+ int fdc = compute_foldcolumn(wp, 0);
draw_state = WL_FOLD;
if (fdc > 0) {
// Draw the 'foldcolumn'. Allocate a buffer, "extra" may
@@ -1155,7 +1309,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
} else {
// Draw the line number (empty space after wrapping).
if (row == startrow + filler_lines) {
- get_line_number_str(wp, lnum, (char_u *)extra, sizeof(extra));
+ get_line_number_str(wp, lnum, extra, sizeof(extra));
if (wp->w_skipcol > 0) {
for (p_extra = extra; *p_extra == ' '; p_extra++) {
*p_extra = '-';
@@ -1163,10 +1317,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
}
if (wp->w_p_rl) { // reverse line numbers
// like rl_mirror(), but keep the space at the end
- char_u *p2 = (char_u *)skipwhite((char *)extra);
+ char *p2 = skipwhite(extra);
p2 = skiptowhite(p2) - 1;
- for (char_u *p1 = (char_u *)skipwhite((char *)extra); p1 < p2; p1++, p2--) {
- const char_u t = *p1;
+ for (char *p1 = skipwhite(extra); p1 < p2; p1++, p2--) {
+ const char t = *p1;
*p1 = *p2;
*p2 = t;
}
@@ -1187,7 +1341,23 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
}
}
- if (draw_state == WL_NR && n_extra == 0) {
+ if (draw_state == WL_STC - 1 && n_extra == 0) {
+ draw_state = WL_STC;
+ // Draw the 'statuscolumn' if option is set.
+ if (statuscol.draw) {
+ if (statuscol.textp == NULL) {
+ get_statuscol_str(wp, lnum, row, startrow, filler_lines, cul_attr,
+ sign_num_attr, sign_cul_attr, &statuscol, foldinfo, sattrs);
+ if (wp->w_redr_statuscol) {
+ break;
+ }
+ }
+ get_statuscol_display_info(&statuscol, &draw_state, &char_attr,
+ &n_extra, &c_extra, &c_final, &p_extra);
+ }
+ }
+
+ if (draw_state == WL_STC && n_extra == 0) {
win_col_offset = off;
}
@@ -1247,7 +1417,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
}
char_attr = 0;
} else if (filler_todo > 0) {
- // draw "deleted" diff line(s)
+ // Draw "deleted" diff line(s)
if (char2cells(wp->w_p_fcs_chars.diff) > 1) {
c_extra = '-';
c_final = NUL;
@@ -1262,13 +1432,13 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
}
char_attr = win_hl_attr(wp, HLF_DED);
}
- char_u *const sbr = get_showbreak_value(wp);
+ char *const sbr = get_showbreak_value(wp);
if (*sbr != NUL && need_showbreak) {
// Draw 'showbreak' at the start of each broken line.
p_extra = sbr;
c_extra = NUL;
c_final = NUL;
- n_extra = (int)STRLEN(sbr);
+ n_extra = (int)strlen(sbr);
char_attr = win_hl_attr(wp, HLF_AT);
if (wp->w_skipcol == 0 || !wp->w_p_wrap) {
need_showbreak = false;
@@ -1319,7 +1489,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
&& wp == curwin
&& lnum == wp->w_cursor.lnum
&& vcol >= (long)wp->w_virtcol)
- || (number_only && draw_state > WL_NR))
+ || (number_only && draw_state > WL_STC))
&& filler_todo <= 0) {
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, bg_attr, false);
@@ -1337,13 +1507,13 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
&& has_fold
&& col == win_col_offset
&& n_extra == 0
- && row == startrow) {
+ && row == startrow + filler_lines) {
char_attr = win_hl_attr(wp, HLF_FL);
linenr_T lnume = lnum + foldinfo.fi_lines - 1;
memset(buf_fold, ' ', FOLD_TEXT_LEN);
p_extra = get_foldtext(wp, lnum, lnume, foldinfo, buf_fold);
- n_extra = (int)STRLEN(p_extra);
+ n_extra = (int)strlen(p_extra);
if (p_extra != buf_fold) {
xfree(p_extra_free);
@@ -1358,7 +1528,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
&& has_fold
&& col < grid->cols
&& n_extra == 0
- && row == startrow) {
+ && row == startrow + filler_lines) {
// fill rest of line with 'fold'
c_extra = wp->w_p_fcs_chars.fold;
c_final = NUL;
@@ -1370,7 +1540,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
&& has_fold
&& col >= grid->cols
&& n_extra != 0
- && row == startrow) {
+ && row == startrow + filler_lines) {
// Truncate the folding.
n_extra = 0;
}
@@ -1379,11 +1549,11 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
// handle Visual or match highlighting in this line
if (vcol == fromcol
|| (vcol + 1 == fromcol && n_extra == 0
- && utf_ptr2cells((char *)ptr) > 1)
+ && utf_ptr2cells(ptr) > 1)
|| ((int)vcol_prev == fromcol_prev
&& vcol_prev < vcol // not at margin
&& vcol < tocol)) {
- area_attr = attr; // start highlighting
+ area_attr = vi_attr; // start highlighting
if (area_highlighting) {
area_active = true;
}
@@ -1400,8 +1570,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
// When another match, have to check for start again.
v = (ptr - line);
search_attr = update_search_hl(wp, lnum, (colnr_T)v, &line, &screen_search_hl,
- &has_match_conc,
- &match_conc, lcs_eol_one, &search_attr_from_match);
+ &has_match_conc, &match_conc, lcs_eol_one,
+ &on_last_col, &search_attr_from_match);
ptr = line + v; // "line" may have been changed
// Do not allow a conceal over EOL otherwise EOL will be missed
@@ -1473,11 +1643,11 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
mb_utf8 = check_mb_utf8(&c, u8cc);
} else {
assert(p_extra != NULL);
- c = *p_extra;
+ c = (uint8_t)(*p_extra);
mb_c = c;
// If the UTF-8 character is more than one byte:
// Decode it into "mb_c".
- mb_l = utfc_ptr2len((char *)p_extra);
+ mb_l = utfc_ptr2len(p_extra);
mb_utf8 = false;
if (mb_l > n_extra) {
mb_l = 1;
@@ -1526,11 +1696,11 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
XFREE_CLEAR(p_extra_free);
// Get a character from the line itself.
- c0 = c = *ptr;
+ c0 = c = (uint8_t)(*ptr);
mb_c = c;
// If the UTF-8 character is more than one byte: Decode it
// into "mb_c".
- mb_l = utfc_ptr2len((char *)ptr);
+ mb_l = utfc_ptr2len(ptr);
mb_utf8 = false;
if (mb_l > 1) {
mb_c = utfc_ptr2char(ptr, u8cc);
@@ -1559,16 +1729,16 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
|| (mb_l > 1 && (!vim_isprintc(mb_c)))) {
// Illegal UTF-8 byte: display as <xx>.
// Non-BMP character : display as ? or fullwidth ?.
- transchar_hex((char *)extra, mb_c);
+ transchar_hex(extra, mb_c);
if (wp->w_p_rl) { // reverse
rl_mirror(extra);
}
p_extra = extra;
- c = *p_extra;
- mb_c = mb_ptr2char_adv((const char_u **)&p_extra);
+ c = (uint8_t)(*p_extra);
+ mb_c = mb_ptr2char_adv((const char **)&p_extra);
mb_utf8 = (c >= 0x80);
- n_extra = (int)STRLEN(p_extra);
+ n_extra = (int)strlen(p_extra);
c_extra = NUL;
c_final = NUL;
if (area_attr == 0 && search_attr == 0) {
@@ -1588,7 +1758,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
if (wp->w_p_rl) {
pc = prev_c;
pc1 = prev_c1;
- nc = utf_ptr2char((char *)ptr + mb_l);
+ nc = utf_ptr2char(ptr + mb_l);
prev_c1 = u8cc[0];
} else {
pc = utfc_ptr2char(ptr + mb_l, pcc);
@@ -1639,7 +1809,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
ptr++;
if (extra_check) {
- bool can_spell = true;
+ bool no_plain_buffer = (wp->w_s->b_p_spo_flags & SPO_NPBUFFER) != 0;
+ bool can_spell = !no_plain_buffer;
// Get syntax attribute, unless still at the start of the line
// (double-wide char that doesn't fit).
@@ -1653,7 +1824,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
syntax_attr = get_syntax_attr((colnr_T)v - 1,
has_spell ? &can_spell : NULL, false);
- if (did_emsg) {
+ if (did_emsg) { // -V547
wp->w_s->b_syn_error = true;
has_syntax = false;
} else {
@@ -1691,6 +1862,27 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
char_attr = 0;
}
+ 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,
+ selected, &decor_state);
+ if (extmark_attr != 0) {
+ if (!attr_pri) {
+ char_attr = hl_combine_attr(char_attr, extmark_attr);
+ } else {
+ 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??
+ }
+
+ can_spell = TRISTATE_TO_BOOL(decor_state.spell, can_spell);
+ }
+
// Check spelling (unless at the end of the line).
// Only do this when there is no syntax highlighting, the
// @Spell cluster is not used or the current syntax item
@@ -1699,11 +1891,11 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
if (has_spell && v >= word_end && v > cur_checked_col) {
spell_attr = 0;
if (!attr_pri) {
- char_attr = syntax_attr;
+ char_attr = hl_combine_attr(char_attr, syntax_attr);
}
- if (c != 0 && (!has_syntax || can_spell)) {
- char_u *prev_ptr;
- char_u *p;
+ if (c != 0 && ((!has_syntax && !no_plain_buffer) || can_spell)) {
+ char *prev_ptr;
+ char *p;
int len;
hlf_T spell_hlf = HLF_COUNT;
prev_ptr = ptr - mb_l;
@@ -1774,32 +1966,15 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
char_attr = hl_combine_attr(term_attrs[vcol], char_attr);
}
- 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,
- selected, &decor_state);
- if (extmark_attr != 0) {
- if (!attr_pri) {
- char_attr = hl_combine_attr(char_attr, extmark_attr);
- } else {
- 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.
if (wp->w_p_lbr && c0 == c && vim_isbreak(c)
&& !vim_isbreak((int)(*ptr))) {
int mb_off = utf_head_off(line, ptr - 1);
- char_u *p = ptr - (mb_off + 1);
- // TODO(neovim): is passing p for start of the line OK?
- n_extra = win_lbr_chartabsize(wp, line, p, (colnr_T)vcol, NULL) - 1;
+ char *p = ptr - (mb_off + 1);
+ chartabsize_T cts;
+
+ init_chartabsize_arg(&cts, wp, lnum, (colnr_T)vcol, line, p);
+ n_extra = win_lbr_chartabsize(&cts, NULL) - 1;
// We have just drawn the showbreak value, no need to add
// space for it again.
@@ -1809,6 +1984,12 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
n_extra = 0;
}
}
+ if (on_last_col && c != TAB) {
+ // Do not continue search/match highlighting over the
+ // line break, but for TABs the highlighting should
+ // include the complete width of the character
+ search_attr = 0;
+ }
if (c == TAB && n_extra + col > grid->cols) {
n_extra = tabstop_padding((colnr_T)vcol, wp->w_buffer->b_p_ts,
@@ -1825,6 +2006,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
c = ' ';
}
}
+ clear_chartabsize_arg(&cts);
}
in_multispace = c == ' ' && ((ptr > line + 1 && ptr[-2] == ' ') || *ptr == ' ');
@@ -1893,7 +2075,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
if (c == TAB && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
int tab_len = 0;
long vcol_adjusted = vcol; // removed showbreak length
- char_u *const sbr = get_showbreak_value(wp);
+ char *const sbr = get_showbreak_value(wp);
// Only adjust the tab_len, when at the first column after the
// showbreak value was drawn.
@@ -1908,7 +2090,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
if (!wp->w_p_lbr || !wp->w_p_list) {
n_extra = tab_len;
} else {
- char_u *p;
+ char *p;
int i;
int saved_nextra = n_extra;
@@ -1949,7 +2131,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
if (wp->w_p_lcs_chars.tab3 && i == tab_len - 1) {
lcs = wp->w_p_lcs_chars.tab3;
}
- p += utf_char2bytes(lcs, (char *)p);
+ p += utf_char2bytes(lcs, p);
n_extra += utf_char2len(lcs) - (saved_nextra > 0 ? 1 : 0);
}
p_extra = p_extra_free;
@@ -2041,7 +2223,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
mb_c = c;
mb_utf8 = check_mb_utf8(&c, u8cc);
} else if (c != NUL) {
- p_extra = transchar_buf(wp->w_buffer, c);
+ p_extra = (char *)transchar_buf(wp->w_buffer, c);
if (n_extra == 0) {
n_extra = byte2cells(c) - 1;
}
@@ -2051,18 +2233,20 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
c_extra = NUL;
c_final = NUL;
if (wp->w_p_lbr) {
- char_u *p;
+ char *p;
- c = *p_extra;
+ c = (uint8_t)(*p_extra);
p = xmalloc((size_t)n_extra + 1);
memset(p, ' ', (size_t)n_extra);
- STRNCPY(p, p_extra + 1, STRLEN(p_extra) - 1); // NOLINT(runtime/printf)
+ strncpy(p, // NOLINT(runtime/printf)
+ p_extra + 1,
+ (size_t)strlen(p_extra) - 1);
p[n_extra] = NUL;
xfree(p_extra_free);
p_extra_free = p_extra = p;
} else {
n_extra = byte2cells(c) - 1;
- c = *p_extra++;
+ c = (uint8_t)(*p_extra++);
}
n_attr = n_extra + 1;
extra_attr = win_hl_attr(wp, HLF_8);
@@ -2082,7 +2266,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
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 || decor_conceal > 0)
- && !(lnum_in_visual_area && vim_strchr((char *)wp->w_p_cocu, 'v') == NULL)) {
+ && !(lnum_in_visual_area && vim_strchr(wp->w_p_cocu, 'v') == NULL)) {
char_attr = conceal_attr;
if (((prev_syntax_id != syntax_seqnr && (syntax_flags & HL_CONCEAL) != 0)
|| has_match_conc > 1 || decor_conceal > 1)
@@ -2170,7 +2354,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
&& wp->w_p_list
&& (wp->w_p_wrap ? (wp->w_skipcol > 0 && row == 0) : wp->w_leftcol > 0)
&& filler_todo <= 0
- && draw_state > WL_NR
+ && draw_state > WL_STC
&& c != NUL) {
c = wp->w_p_lcs_chars.prec;
lcs_prec_todo = NUL;
@@ -2195,7 +2379,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
// flag to indicate whether prevcol equals startcol of search_hl or
// one of the matches
bool prevcol_hl_flag = get_prevcol_hl_flag(wp, &screen_search_hl,
- (long)(ptr - line) - 1);
+ (long)(ptr - line) - 1); // NOLINT(google-readability-casting)
// Invert at least one char, used for Visual and empty line or
// highlight match at end of line. If it's beyond the last
@@ -2231,7 +2415,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
if (area_attr == 0 && !has_fold) {
// Use attributes from match with highest priority among
// 'search_hl' and the match list.
- get_search_match_hl(wp, &screen_search_hl, (long)(ptr - line), &char_attr);
+ get_search_match_hl(wp,
+ &screen_search_hl,
+ (long)(ptr - line), // NOLINT(google-readability-casting)
+ &char_attr);
}
int eol_attr = char_attr;
@@ -2461,7 +2648,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
col++;
// UTF-8: Put a 0 in the second screen char.
linebuf_char[off][0] = 0;
- if (draw_state > WL_NR && filler_todo <= 0) {
+ if (draw_state > WL_STC && filler_todo <= 0) {
vcol++;
}
// When "tocol" is halfway through a character, set it to the end of
@@ -2544,7 +2731,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
// Only advance the "vcol" when after the 'number' or 'relativenumber'
// column.
- if (draw_state > WL_NR
+ if (draw_state > WL_STC
&& filler_todo <= 0) {
vcol++;
}
@@ -2554,7 +2741,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
}
// restore attributes after "predeces" in 'listchars'
- if (draw_state > WL_NR && n_attr3 > 0 && --n_attr3 == 0) {
+ if (draw_state > WL_STC && n_attr3 > 0 && --n_attr3 == 0) {
char_attr = saved_attr3;
}
@@ -2566,7 +2753,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
// 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->cols))
- && foldinfo.fi_lines == 0
+ && (!has_fold || virt_line_offset >= 0)
&& (draw_state != WL_LINE
|| *ptr != NUL
|| filler_todo > 0
@@ -2583,15 +2770,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
&& !wp->w_p_rl; // Not right-to-left.
int draw_col = col - boguscols;
- if (filler_todo > 0) {
- int index = filler_todo - (filler_lines - n_virt_lines);
- if (index > 0) {
- int i = (int)kv_size(virt_lines) - index;
- 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->cols, offset);
- }
+ if (virt_line_offset >= 0) {
+ draw_virt_text_item(buf, virt_line_offset, kv_A(virt_lines, virt_line_index).line,
+ kHlModeReplace, grid->cols, 0);
} else {
draw_virt_text(wp, buf, win_col_offset, &draw_col, grid->cols, row);
}
@@ -2621,6 +2802,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
// 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);
+ set_empty_rows(wp, row);
row = endrow;
}
@@ -2649,7 +2831,19 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
if (filler_todo <= 0) {
need_showbreak = true;
}
+ if (statuscol.draw) {
+ if (row == startrow + filler_lines + 1 || row == startrow + filler_lines) {
+ // Re-evaluate 'statuscolumn' for the first wrapped row and non filler line
+ statuscol.textp = NULL;
+ } else if (statuscol.textp) {
+ // Draw the already built 'statuscolumn' on the next wrapped or filler line
+ statuscol.textp = statuscol.text;
+ statuscol.hlrecp = statuscol.hlrec;
+ } // Fall back to default columns if the 'n' flag isn't in 'cpo'
+ statuscol.draw = vim_strchr(p_cpo, CPO_NUMCOL) == NULL;
+ }
filler_todo--;
+ virt_line_offset = -1;
// When the filler lines are actually below the last line of the
// file, don't draw the line itself, break here.
if (filler_todo == 0 && (wp->w_botfill || end_fill)) {
@@ -2659,7 +2853,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
} // for every character in the line
// After an empty line check first word for capital.
- if (*skipwhite((char *)line) == NUL) {
+ if (*skipwhite(line) == NUL) {
capcol_lnum = lnum + 1;
cap_col = 0;
}
diff --git a/src/nvim/drawline.h b/src/nvim/drawline.h
index e50969983e..9f60b46e1b 100644
--- a/src/nvim/drawline.h
+++ b/src/nvim/drawline.h
@@ -1,9 +1,15 @@
#ifndef NVIM_DRAWLINE_H
#define NVIM_DRAWLINE_H
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "klib/kvec.h"
#include "nvim/decoration_provider.h"
#include "nvim/fold.h"
+#include "nvim/macros.h"
#include "nvim/screen.h"
+#include "nvim/types.h"
// Maximum columns for terminal highlight attributes
#define TERM_ATTRS_MAX 1024
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index 2abff6c894..04c342e068 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -20,7 +20,7 @@
//
// Commands that scroll a window change w_topline and must call
// check_cursor() to move the cursor into the visible part of the window, and
-// call redraw_later(wp, VALID) to have the window displayed by update_screen()
+// call redraw_later(wp, UPD_VALID) to have the window displayed by update_screen()
// later.
//
// Commands that change text in the buffer must call changed_bytes() or
@@ -32,23 +32,23 @@
//
// Commands that change how a window is displayed (e.g., setting 'list') or
// invalidate the contents of a window in another way (e.g., change fold
-// settings), must call redraw_later(wp, NOT_VALID) to have the whole window
+// settings), must call redraw_later(wp, UPD_NOT_VALID) to have the whole window
// redisplayed by update_screen() later.
//
// Commands that change how a buffer is displayed (e.g., setting 'tabstop')
-// must call redraw_curbuf_later(NOT_VALID) to have all the windows for the
+// must call redraw_curbuf_later(UPD_NOT_VALID) to have all the windows for the
// buffer redisplayed by update_screen() later.
//
// Commands that change highlighting and possibly cause a scroll too must call
-// redraw_later(wp, SOME_VALID) to update the whole window but still use
+// redraw_later(wp, UPD_SOME_VALID) to update the whole window but still use
// scrolling to avoid redrawing everything. But the length of displayed lines
-// must not change, use NOT_VALID then.
+// must not change, use UPD_NOT_VALID then.
//
-// Commands that move the window position must call redraw_later(wp, NOT_VALID).
+// Commands that move the window position must call redraw_later(wp, UPD_NOT_VALID).
// TODO(neovim): should minimize redrawing by scrolling when possible.
//
// Commands that change everything (e.g., resizing the screen) must call
-// redraw_all_later(NOT_VALID) or redraw_all_later(CLEAR).
+// redraw_all_later(UPD_NOT_VALID) or redraw_all_later(UPD_CLEAR).
//
// Things that are handled indirectly:
// - When messages scroll the screen up, msg_scrolled will be set and
@@ -59,28 +59,47 @@
#include <stdbool.h>
#include <string.h>
+#include "klib/kvec.h"
+#include "nvim/api/private/defs.h"
+#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
+#include "nvim/decoration.h"
+#include "nvim/decoration_provider.h"
#include "nvim/diff.h"
+#include "nvim/drawline.h"
#include "nvim/drawscreen.h"
#include "nvim/ex_getln.h"
+#include "nvim/extmark_defs.h"
+#include "nvim/fold.h"
+#include "nvim/globals.h"
#include "nvim/grid.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
#include "nvim/insexpand.h"
#include "nvim/match.h"
+#include "nvim/mbyte.h"
+#include "nvim/memline.h"
+#include "nvim/message.h"
#include "nvim/move.h"
+#include "nvim/normal.h"
#include "nvim/option.h"
#include "nvim/plines.h"
#include "nvim/popupmenu.h"
+#include "nvim/pos.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
+#include "nvim/screen.h"
#include "nvim/statusline.h"
#include "nvim/syntax.h"
+#include "nvim/terminal.h"
+#include "nvim/types.h"
+#include "nvim/ui.h"
#include "nvim/ui_compositor.h"
-#include "nvim/undo.h"
#include "nvim/version.h"
+#include "nvim/vim.h"
#include "nvim/window.h"
/// corner value flags for hsep_connected and vsep_connected
@@ -97,7 +116,7 @@ typedef enum {
static bool redraw_popupmenu = false;
static bool msg_grid_invalid = false;
-static bool resizing = false;
+static bool resizing_autocmd = false;
static char *provider_err = NULL;
@@ -107,15 +126,17 @@ static char *provider_err = NULL;
void conceal_check_cursor_line(void)
{
bool should_conceal = conceal_cursor_line(curwin);
- if (curwin->w_p_cole > 0 && (conceal_cursor_used != should_conceal)) {
- redrawWinline(curwin, curwin->w_cursor.lnum);
- // Need to recompute cursor column, e.g., when starting Visual mode
- // without concealing.
- curs_columns(curwin, true);
+ if (curwin->w_p_cole <= 0 || conceal_cursor_used == should_conceal) {
+ return;
}
+
+ redrawWinline(curwin, curwin->w_cursor.lnum);
+ // Need to recompute cursor column, e.g., when starting Visual mode
+ // without concealing.
+ curs_columns(curwin, true);
}
-/// Resize the screen to Rows and Columns.
+/// Resize default_grid to Rows and Columns.
///
/// Allocate default_grid.chars[] and other grid arrays.
///
@@ -125,19 +146,20 @@ void conceal_check_cursor_line(void)
/// default_grid.Columns to access items in default_grid.chars[]. Use Rows
/// and Columns for positioning text etc. where the final size of the screen is
/// needed.
-void screenalloc(void)
+///
+/// @return whether resizing has been done
+bool default_grid_alloc(void)
{
+ static bool resizing = false;
+
// It's possible that we produce an out-of-memory message below, which
// will cause this function to be called again. To break the loop, just
// return here.
if (resizing) {
- return;
+ return false;
}
resizing = true;
- int retry_count = 0;
-
-retry:
// Allocation of the screen buffers is done only when the size changes and
// when Rows and Columns have been set and we have started doing full
// screen stuff.
@@ -148,24 +170,9 @@ retry:
|| Columns == 0
|| (!full_screen && default_grid.chars == NULL)) {
resizing = false;
- return;
- }
-
- // Note that the window sizes are updated before reallocating the arrays,
- // thus we must not redraw here!
- RedrawingDisabled++;
-
- // 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;
+ return false;
}
- win_new_screensize(); // fit the windows in the new sized screen
-
- comp_col(); // recompute columns for shown command and ruler
-
// We're changing the size of the screen.
// - Allocate new arrays for default_grid
// - Move lines from the old arrays into the new arrays, clear extra
@@ -177,14 +184,10 @@ retry:
// size is wrong.
grid_alloc(&default_grid, Rows, Columns, true, true);
- StlClickDefinition *new_tab_page_click_defs =
- xcalloc((size_t)Columns, sizeof(*new_tab_page_click_defs));
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;
- tab_page_click_defs_size = Columns;
+ tab_page_click_defs = stl_alloc_click_defs(tab_page_click_defs, Columns,
+ &tab_page_click_defs_size);
default_grid.comp_height = Rows;
default_grid.comp_width = Columns;
@@ -193,35 +196,20 @@ retry:
default_grid.col_offset = 0;
default_grid.handle = DEFAULT_GRID_HANDLE;
- must_redraw = CLEAR; // need to clear the screen later
-
- RedrawingDisabled--;
-
- // Do not apply autocommands more than 3 times to avoid an endless loop
- // in case applying autocommands always changes Rows or Columns.
- if (starting == 0 && ++retry_count <= 3) {
- apply_autocmds(EVENT_VIMRESIZED, NULL, NULL, false, curbuf);
- // In rare cases, autocommands may have altered Rows or Columns,
- // jump back to check if we need to allocate the screen again.
- goto retry;
- }
-
resizing = false;
+ return true;
}
void screenclear(void)
{
check_for_delay(false);
- screenalloc(); // allocate screen buffers if size changed
-
- int i;
if (starting == NO_SCREEN || default_grid.chars == NULL) {
return;
}
// blank out the default grid
- for (i = 0; i < default_grid.rows; i++) {
+ for (int i = 0; i < default_grid.rows; i++) {
grid_clear_line(&default_grid, default_grid.line_offset[i],
default_grid.cols, true);
default_grid.line_wraps[i] = false;
@@ -235,18 +223,18 @@ void screenclear(void)
clear_cmdline = false;
mode_displayed = false;
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
redraw_cmdline = true;
redraw_tabline = true;
redraw_popupmenu = true;
pum_invalidate();
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_floating) {
- wp->w_redr_type = CLEAR;
+ wp->w_redr_type = UPD_CLEAR;
}
}
- if (must_redraw == CLEAR) {
- must_redraw = NOT_VALID; // no need to clear again
+ if (must_redraw == UPD_CLEAR) {
+ must_redraw = UPD_NOT_VALID; // no need to clear again
}
compute_cmdrow();
msg_row = cmdline_row; // put cursor on last line for messages
@@ -281,13 +269,6 @@ void screen_resize(int width, int height)
return;
}
- // curwin->w_buffer can be NULL when we are closing a window and the
- // buffer has already been closed and removing a scrollbar causes a resize
- // event. Don't resize then, it will happen after entering another buffer.
- if (curwin->w_buffer == NULL) {
- return;
- }
-
resizing_screen = true;
Rows = height;
@@ -301,16 +282,42 @@ void screen_resize(int width, int height)
width = Columns;
p_lines = Rows;
p_columns = Columns;
+
ui_call_grid_resize(1, width, height);
- /// 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().
+ int retry_count = 0;
+ resizing_autocmd = true;
+
+ // In rare cases, autocommands may have altered Rows or Columns,
+ // so retry to check if we need to allocate the screen again.
+ while (default_grid_alloc()) {
+ // 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;
+ }
+
+ RedrawingDisabled++;
+
+ win_new_screensize(); // fit the windows in the new sized screen
+
+ comp_col(); // recompute columns for shown command and ruler
+
+ RedrawingDisabled--;
+
+ // Do not apply autocommands more than 3 times to avoid an endless loop
+ // in case applying autocommands always changes Rows or Columns.
+ if (++retry_count > 3) {
+ break;
+ }
- if (State != MODE_ASKMORE && State != MODE_EXTERNCMD && State != MODE_CONFIRM) {
- screenclear();
+ apply_autocmds(EVENT_VIMRESIZED, NULL, NULL, false, curbuf);
}
+ resizing_autocmd = false;
+ redraw_all_later(UPD_CLEAR);
+
if (starting != NO_SCREEN) {
maketitle();
@@ -320,14 +327,11 @@ void screen_resize(int width, int height)
// We only redraw when it's needed:
// - While at the more prompt or executing an external command, don't
// redraw, but position the cursor.
- // - While editing the command line, only redraw that.
+ // - While editing the command line, only redraw that. TODO: lies
// - in Ex mode, don't redraw anything.
// - Otherwise, redraw right now, and position the cursor.
- // Always need to call update_screen() or screenalloc(), to make
- // sure Rows/Columns and the size of the screen is correct!
if (State == MODE_ASKMORE || State == MODE_EXTERNCMD || State == MODE_CONFIRM
|| exmode_active) {
- screenalloc();
if (msg_grid.chars) {
msg_grid_validate();
}
@@ -341,7 +345,7 @@ void screen_resize(int width, int height)
}
if (State & MODE_CMDLINE) {
redraw_popupmenu = false;
- update_screen(NOT_VALID);
+ update_screen();
redrawcmdline();
if (pum_drawn()) {
cmdline_pum_display(false);
@@ -350,12 +354,12 @@ void screen_resize(int width, int height)
update_topline(curwin);
if (pum_drawn()) {
// TODO(bfredl): ins_compl_show_pum wants to redraw the screen first.
- // For now make sure the nested update_screen(0) won't redraw the
+ // For now make sure the nested update_screen() won't redraw the
// pum at the old position. Try to untangle this later.
redraw_popupmenu = false;
ins_compl_show_pum();
}
- update_screen(NOT_VALID);
+ update_screen();
if (redrawing()) {
setcursor();
}
@@ -370,9 +374,7 @@ void screen_resize(int width, int height)
///
/// Most code shouldn't call this directly, rather use redraw_later() and
/// and redraw_all_later() to mark parts of the screen as needing a redraw.
-///
-/// @param type set to a NOT_VALID to force redraw of entire screen
-int update_screen(int type)
+int update_screen(void)
{
static bool did_intro = false;
bool is_stl_global = global_stl_height() > 0;
@@ -380,7 +382,7 @@ int update_screen(int type)
// Don't do anything if the screen structures are (not yet) valid.
// A VimResized autocmd can invoke redrawing in the middle of a resize,
// which would bypass the checks in screen_resize for popupmenu etc.
- if (!default_grid.chars || resizing) {
+ if (resizing_autocmd || !default_grid.chars) {
return FAIL;
}
@@ -389,37 +391,25 @@ int update_screen(int type)
diff_redraw(true);
}
- if (must_redraw) {
- if (type < must_redraw) { // use maximal 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, or a decoration provider requires a redraw, the screen
- // will be redrawn later or in win_update().
- must_redraw = 0;
- }
-
- // Need to update w_lines[].
- if (curwin->w_lines_valid == 0 && type < NOT_VALID) {
- type = NOT_VALID;
- }
-
// Postpone the redrawing when it's not needed and when being called
// recursively.
if (!redrawing() || updating_screen) {
- must_redraw = type;
- if (type > INVERTED_ALL) {
- curwin->w_lines_valid = 0; // don't use w_lines[].wl_size now
- }
return FAIL;
}
+
+ int 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, or a decoration provider requires a redraw, the screen
+ // will be redrawn later or in win_update().
+ must_redraw = 0;
+
updating_screen = 1;
- display_tick++; // let syntax code know we're in a next round of
- // display updating
+ display_tick++; // let syntax code know we're in a next round of
+ // display updating
// Tricky: vim code can reset msg_scrolled behind our back, so need
// separate bookkeeping for now.
@@ -428,7 +418,7 @@ int update_screen(int type)
msg_scrolled_at_flush = 0;
}
- if (type >= CLEAR || !default_grid.valid) {
+ if (type >= UPD_CLEAR || !default_grid.valid) {
ui_comp_set_screen_valid(false);
}
@@ -443,68 +433,48 @@ int update_screen(int type)
msg_grid.cols, false);
}
}
- if (msg_use_msgsep()) {
- msg_grid.throttled = false;
- // 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++) {
- grid_clear_line(&default_grid, default_grid.line_offset[i],
- Columns, false);
- }
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_floating) {
- continue;
- }
- if (W_ENDROW(wp) > valid) {
- wp->w_redr_type = MAX(wp->w_redr_type, NOT_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.throttled = false;
+ bool was_invalidated = false;
+
+ // UPD_CLEAR is already handled
+ if (type == UPD_NOT_VALID && !ui_has(kUIMultigrid) && msg_scrolled) {
+ was_invalidated = ui_comp_set_screen_valid(false);
+ for (int i = valid; i < Rows - p_ch; i++) {
+ grid_clear_line(&default_grid, default_grid.line_offset[i],
+ Columns, false);
}
- msg_grid_set_pos(Rows - (int)p_ch, false);
- msg_grid_invalid = false;
- } else if (msg_scrolled > Rows - 5) { // clearing is faster
- type = CLEAR;
- } else if (type != CLEAR) {
- check_for_delay(false);
- grid_ins_lines(&default_grid, 0, msg_scrolled, Rows, 0, Columns);
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_floating) {
continue;
}
- if (wp->w_winrow < msg_scrolled) {
- if (W_ENDROW(wp) > msg_scrolled
- && wp->w_redr_type < REDRAW_TOP
- && wp->w_lines_valid > 0
- && wp->w_topline == wp->w_lines[0].wl_lnum) {
- wp->w_upd_rows = msg_scrolled - wp->w_winrow;
- wp->w_redr_type = REDRAW_TOP;
- } else {
- wp->w_redr_type = NOT_VALID;
- if (wp->w_winrow + wp->w_winbar_height <= msg_scrolled) {
- wp->w_redr_status = true;
- }
- }
+ if (W_ENDROW(wp) > valid) {
+ // TODO(bfredl): too pessimistic. type could be UPD_NOT_VALID
+ // only because windows that are above the separator.
+ wp->w_redr_type = MAX(wp->w_redr_type, UPD_NOT_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 <= msg_scrolled) {
+ if (is_stl_global && Rows - p_ch - 1 > valid) {
curwin->w_redr_status = true;
}
- redraw_cmdline = true;
- redraw_tabline = true;
+ }
+ msg_grid_set_pos(Rows - (int)p_ch, false);
+ msg_grid_invalid = false;
+ if (was_invalidated) {
+ // screen was only invalid for the msgarea part.
+ // @TODO(bfredl): using the same "valid" flag
+ // for both messages and floats moving is bit of a mess.
+ ui_comp_set_screen_valid(true);
}
msg_scrolled = 0;
msg_scrolled_at_flush = 0;
+ msg_grid_scroll_discount = 0;
need_wait_return = false;
}
- win_ui_flush();
+ win_ui_flush(true);
msg_ext_check_clear();
// reset cmdline_row now (may have been changed temporarily)
@@ -517,10 +487,11 @@ int update_screen(int type)
hl_changed = true;
}
- if (type == CLEAR) { // first clear screen
- screenclear(); // will reset clear_cmdline
+ if (type == UPD_CLEAR) { // first clear screen
+ screenclear(); // will reset clear_cmdline
+ // and set UPD_NOT_VALID for each window
cmdline_screen_cleared(); // clear external cmdline state
- type = NOT_VALID;
+ type = UPD_NOT_VALID;
// must_redraw may be set indirectly, avoid another redraw later
must_redraw = 0;
} else if (!default_grid.valid) {
@@ -528,16 +499,15 @@ int update_screen(int type)
default_grid.valid = true;
}
- // 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)) {
+ // might need to clear space on default_grid for the message area.
+ if (type == UPD_NOT_VALID && clear_cmdline && !ui_has(kUIMessages)) {
grid_fill(&default_grid, Rows - (int)p_ch, Rows, 0, Columns, ' ', ' ', 0);
}
ui_comp_set_screen_valid(true);
DecorProviders providers;
- decor_providers_start(&providers, type, &provider_err);
+ decor_providers_start(&providers, &provider_err);
// "start" callback could have changed highlights for global elements
if (win_check_ns_hl(NULL)) {
@@ -551,37 +521,20 @@ int update_screen(int type)
// Force redraw when width of 'number' or 'relativenumber' column
// changes.
- if (curwin->w_redr_type < NOT_VALID
- && curwin->w_nrwidth != ((curwin->w_p_nu || curwin->w_p_rnu)
+ // TODO(bfredl): special casing curwin here is SÅ JÄVLA BULL.
+ // Either this should be done for all windows or not at all.
+ if (curwin->w_redr_type < UPD_NOT_VALID
+ && curwin->w_nrwidth != ((curwin->w_p_nu || curwin->w_p_rnu || *curwin->w_p_stc)
? number_width(curwin) : 0)) {
- curwin->w_redr_type = NOT_VALID;
- }
-
- // Only start redrawing if there is really something to do.
- if (type == INVERTED) {
- update_curswant();
- }
- if (curwin->w_redr_type < type
- && !((type == VALID
- && curwin->w_lines[0].wl_valid
- && curwin->w_topfill == curwin->w_old_topfill
- && curwin->w_botfill == curwin->w_old_botfill
- && curwin->w_topline == curwin->w_lines[0].wl_lnum)
- || (type == INVERTED
- && VIsual_active
- && curwin->w_old_cursor_lnum == curwin->w_cursor.lnum
- && curwin->w_old_visual_mode == VIsual_mode
- && (curwin->w_valid & VALID_VIRTCOL)
- && curwin->w_old_curswant == curwin->w_curswant))) {
- curwin->w_redr_type = type;
+ curwin->w_redr_type = UPD_NOT_VALID;
}
// Redraw the tab pages line if needed.
- if (redraw_tabline || type >= NOT_VALID) {
- update_window_hl(curwin, type >= NOT_VALID);
+ if (redraw_tabline || type >= UPD_NOT_VALID) {
+ update_window_hl(curwin, type >= UPD_NOT_VALID);
FOR_ALL_TABS(tp) {
if (tp != curtab) {
- update_window_hl(tp->tp_curwin, type >= NOT_VALID);
+ update_window_hl(tp->tp_curwin, type >= UPD_NOT_VALID);
}
}
draw_tabline();
@@ -590,7 +543,7 @@ int update_screen(int type)
// Correct stored syntax highlighting info for changes in each displayed
// buffer. Each buffer must only be done once.
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- update_window_hl(wp, type >= NOT_VALID || hl_changed);
+ update_window_hl(wp, type >= UPD_NOT_VALID || hl_changed);
buf_T *buf = wp->w_buffer;
if (buf->b_mod_set) {
@@ -612,9 +565,9 @@ int update_screen(int type)
screen_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) {
+ if (wp->w_redr_type == UPD_CLEAR && wp->w_floating && wp->w_grid_alloc.chars) {
grid_invalidate(&wp->w_grid_alloc);
- wp->w_redr_type = NOT_VALID;
+ wp->w_redr_type = UPD_NOT_VALID;
}
win_check_ns_hl(wp);
@@ -622,7 +575,7 @@ int update_screen(int type)
// reallocate grid if needed.
win_grid_alloc(wp);
- if (wp->w_redr_border || wp->w_redr_type >= NOT_VALID) {
+ if (wp->w_redr_border || wp->w_redr_type >= UPD_NOT_VALID) {
win_redr_border(wp);
}
@@ -679,6 +632,20 @@ int update_screen(int type)
return OK;
}
+static void win_border_redr_title(win_T *wp, ScreenGrid *grid, int col)
+{
+ VirtText title_chunks = wp->w_float_config.title_chunks;
+
+ for (size_t i = 0; i < title_chunks.size; i++) {
+ char *text = title_chunks.items[i].text;
+ int cell = (int)mb_string2cells(text);
+ int hl_id = title_chunks.items[i].hl_id;
+ int attr = hl_id ? syn_id2attr(hl_id) : 0;
+ grid_puts(grid, text, 0, col, attr);
+ col += cell;
+ }
+}
+
static void win_redr_border(win_T *wp)
{
wp->w_redr_border = false;
@@ -699,9 +666,24 @@ static void win_redr_border(win_T *wp)
if (adj[3]) {
grid_put_schar(grid, 0, 0, chars[0], attrs[0]);
}
+
for (int i = 0; i < icol; i++) {
grid_put_schar(grid, 0, i + adj[3], chars[1], attrs[1]);
}
+
+ if (wp->w_float_config.title) {
+ int title_col = 0;
+ int title_width = wp->w_float_config.title_width;
+ AlignTextPos title_pos = wp->w_float_config.title_pos;
+
+ if (title_pos == kAlignCenter) {
+ title_col = (icol - title_width) / 2 + 1;
+ } else {
+ title_col = title_pos == kAlignLeft ? 1 : icol - title_width + 1;
+ }
+
+ win_border_redr_title(wp, grid, title_col);
+ }
if (adj[1]) {
grid_put_schar(grid, 0, icol + adj[3], chars[2], attrs[2]);
}
@@ -746,6 +728,8 @@ void show_cursor_info(bool always)
if (!always && !redrawing()) {
return;
}
+
+ win_check_ns_hl(curwin);
if ((*p_stl != NUL || *curwin->w_p_stl != NUL)
&& (curwin->w_status_height || global_stl_height())) {
redraw_custom_statusline(curwin);
@@ -762,6 +746,7 @@ void show_cursor_info(bool always)
maketitle();
}
+ win_check_ns_hl(NULL);
// Redraw the tab pages line if needed.
if (redraw_tabline) {
draw_tabline();
@@ -854,29 +839,29 @@ static bool vsep_connected(win_T *wp, WindowCorner corner)
/// Draw the vertical separator right of window "wp"
static void draw_vsep_win(win_T *wp)
{
- int hl;
- int c;
-
- 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, W_ENDROW(wp),
- W_ENDCOL(wp), W_ENDCOL(wp) + 1, c, ' ', hl);
+ if (!wp->w_vsep_width) {
+ return;
}
+
+ // draw the vertical separator right of this window
+ int hl;
+ int c = fillchar_vsep(wp, &hl);
+ 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);
+ if (!wp->w_hsep_height) {
+ return;
}
+
+ // draw the horizontal separator below this window
+ int hl;
+ int 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"
@@ -957,31 +942,26 @@ static void draw_sep_connectors_win(win_T *wp)
///
/// How the window is redrawn depends on wp->w_redr_type. Each type also
/// implies the one below it.
-/// NOT_VALID redraw the whole window
-/// SOME_VALID redraw the whole window but do scroll when possible
-/// REDRAW_TOP redraw the top w_upd_rows window lines, otherwise like VALID
-/// INVERTED redraw the changed part of the Visual area
-/// INVERTED_ALL redraw the whole Visual area
-/// VALID 1. scroll up/down to adjust for a changed w_topline
-/// 2. update lines at the top when scrolled down
-/// 3. redraw changed text:
-/// - if wp->w_buffer->b_mod_set set, update lines between
-/// b_mod_top and b_mod_bot.
-/// - if wp->w_redraw_top non-zero, redraw lines between
-/// wp->w_redraw_top and wp->w_redr_bot.
-/// - continue redrawing when syntax status is invalid.
-/// 4. if scrolled up, update lines at the bottom.
+/// UPD_NOT_VALID redraw the whole window
+/// UPD_SOME_VALID redraw the whole window but do scroll when possible
+/// UPD_REDRAW_TOP redraw the top w_upd_rows window lines, otherwise like UPD_VALID
+/// UPD_INVERTED redraw the changed part of the Visual area
+/// UPD_INVERTED_ALL redraw the whole Visual area
+/// UPD_VALID 1. scroll up/down to adjust for a changed w_topline
+/// 2. update lines at the top when scrolled down
+/// 3. redraw changed text:
+/// - if wp->w_buffer->b_mod_set set, update lines between
+/// b_mod_top and b_mod_bot.
+/// - if wp->w_redraw_top non-zero, redraw lines between
+/// wp->w_redraw_top and wp->w_redr_bot.
+/// - continue redrawing when syntax status is invalid.
+/// 4. if scrolled up, update lines at the bottom.
/// This results in three areas that may need updating:
/// top: from first row to top_end (when scrolled down)
/// 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, 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
// updating. 0 when no top area updating.
int mid_start = 999; // first row of the mid area that needs
@@ -993,31 +973,28 @@ win_update_start:
bool scrolled_down = false; // true when scrolled down when w_topline got smaller a bit
bool top_to_mod = false; // redraw above mod_top
- int row; // current window row to display
- linenr_T lnum; // current buffer lnum to display
- int idx; // current index in w_lines[]
- int srow; // starting row of the current line
+ int bot_scroll_start = 999; // first line that needs to be redrawn due to
+ // scrolling. only used for EOB
- bool eof = false; // if true, we hit the end of the file
- bool didline = false; // if true, we finished the last line
- int i;
- long j;
static bool recursive = false; // being called recursively
- const linenr_T old_botline = wp->w_botline;
+
// Remember what happened to the previous line.
-#define DID_NONE 1 // didn't update a line
-#define DID_LINE 2 // updated a normal line
-#define DID_FOLD 3 // updated a folded line
- int did_update = DID_NONE;
+ enum {
+ DID_NONE = 1, // didn't update a line
+ DID_LINE = 2, // updated a normal line
+ DID_FOLD = 3, // updated a folded line
+ } did_update = DID_NONE;
+
linenr_T syntax_last_parsed = 0; // last parsed text line
linenr_T mod_top = 0;
linenr_T mod_bot = 0;
- int save_got_int;
- type = wp->w_redr_type;
+ int type = wp->w_redr_type;
- if (type >= NOT_VALID) {
+ if (type >= UPD_NOT_VALID) {
+ // TODO(bfredl): should only be implied for CLEAR, not NOT_VALID!
wp->w_redr_status = true;
+
wp->w_lines_valid = 0;
}
@@ -1039,16 +1016,32 @@ win_update_start:
return;
}
+ buf_T *buf = wp->w_buffer;
+
+ // reset got_int, otherwise regexp won't work
+ int save_got_int = got_int;
+ got_int = 0;
+ // Set the time limit to 'redrawtime'.
+ proftime_T syntax_tm = profile_setlimit(p_rdt);
+ syn_set_timeout(&syntax_tm);
+
+ win_extmark_arr.size = 0;
+
+ decor_redraw_reset(buf, &decor_state);
+
+ DecorProviders line_providers;
+ decor_providers_invoke_win(wp, providers, &line_providers, &provider_err);
+
redraw_win_signcol(wp);
init_search_hl(wp, &screen_search_hl);
// Force redraw when width of 'number' or 'relativenumber' column
// changes.
- i = (wp->w_p_nu || wp->w_p_rnu) ? number_width(wp) : 0;
- if (wp->w_nrwidth != i) {
- type = NOT_VALID;
- wp->w_nrwidth = i;
+ int nrwidth = (wp->w_p_nu || wp->w_p_rnu || *wp->w_p_stc) ? number_width(wp) : 0;
+ if (wp->w_nrwidth != nrwidth) {
+ type = UPD_NOT_VALID;
+ wp->w_nrwidth = nrwidth;
if (buf->terminal) {
terminal_check_size(buf->terminal);
@@ -1059,7 +1052,7 @@ win_update_start:
// When there are both inserted/deleted lines and specific lines to be
// redrawn, w_redraw_top and w_redraw_bot may be invalid, just redraw
// everything (only happens when redrawing is off for while).
- type = NOT_VALID;
+ type = UPD_NOT_VALID;
} else {
// Set mod_top to the first line that needs displaying because of
// changes. Set mod_bot to the first line after the changes.
@@ -1096,12 +1089,12 @@ win_update_start:
} else {
const matchitem_T *cur = wp->w_match_head;
while (cur != NULL) {
- if (cur->match.regprog != NULL
- && re_multiline(cur->match.regprog)) {
+ if (cur->mit_match.regprog != NULL
+ && re_multiline(cur->mit_match.regprog)) {
top_to_mod = true;
break;
}
- cur = cur->next;
+ cur = cur->mit_next;
}
}
}
@@ -1120,7 +1113,7 @@ win_update_start:
// to this line. If there is no valid entry, use MAXLNUM.
lnumt = wp->w_topline;
lnumb = MAXLNUM;
- 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_lastlnum < mod_top) {
lnumt = wp->w_lines[i].wl_lastlnum + 1;
@@ -1168,14 +1161,15 @@ win_update_start:
mod_bot = MAXLNUM;
}
}
+
wp->w_redraw_top = 0; // reset for next time
wp->w_redraw_bot = 0;
// When only displaying the lines at the top, set top_end. Used when
// window has scrolled down for msg_scrolled.
- if (type == REDRAW_TOP) {
- j = 0;
- for (i = 0; i < wp->w_lines_valid; i++) {
+ if (type == UPD_REDRAW_TOP) {
+ long j = 0;
+ for (int i = 0; i < wp->w_lines_valid; i++) {
j += wp->w_lines[i].wl_size;
if (j >= wp->w_upd_rows) {
top_end = (int)j;
@@ -1184,10 +1178,10 @@ win_update_start:
}
if (top_end == 0) {
// not found (cannot happen?): redraw everything
- type = NOT_VALID;
+ type = UPD_NOT_VALID;
} else {
- // top area defined, the rest is VALID
- type = VALID;
+ // top area defined, the rest is UPD_VALID
+ type = UPD_VALID;
}
}
@@ -1197,8 +1191,8 @@ win_update_start:
// 2: wp->w_topline is below wp->w_lines[0].wl_lnum: may scroll up
// 3: wp->w_topline is wp->w_lines[0].wl_lnum: find first entry in
// w_lines[] that needs updating.
- if ((type == VALID || type == SOME_VALID
- || type == INVERTED || type == INVERTED_ALL)
+ if ((type == UPD_VALID || type == UPD_SOME_VALID
+ || type == UPD_INVERTED || type == UPD_INVERTED_ALL)
&& !wp->w_botfill && !wp->w_old_botfill) {
if (mod_top != 0
&& wp->w_topline == mod_top
@@ -1211,6 +1205,7 @@ win_update_start:
|| (wp->w_topline == wp->w_lines[0].wl_lnum
&& wp->w_topfill > wp->w_old_topfill))) {
// New topline is above old topline: May scroll down.
+ long j;
if (hasAnyFolding(wp)) {
linenr_T ln;
@@ -1228,7 +1223,7 @@ win_update_start:
j = wp->w_lines[0].wl_lnum - wp->w_topline;
}
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);
+ int 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;
@@ -1238,6 +1233,7 @@ win_update_start:
// If not the last window, delete the lines at the bottom.
// win_ins_lines may fail when the terminal can't do it.
win_scroll_lines(wp, 0, i);
+ bot_scroll_start = 0;
if (wp->w_lines_valid != 0) {
// Need to update rows that are new, stop at the
// first one that scrolled down.
@@ -1249,6 +1245,7 @@ win_update_start:
if ((wp->w_lines_valid += (linenr_T)j) > wp->w_grid.rows) {
wp->w_lines_valid = wp->w_grid.rows;
}
+ int idx;
for (idx = wp->w_lines_valid; idx - j >= 0; idx--) {
wp->w_lines[idx] = wp->w_lines[idx - j];
}
@@ -1268,9 +1265,9 @@ win_update_start:
// needs updating.
// try to find wp->w_topline in wp->w_lines[].wl_lnum
- j = -1;
- row = 0;
- for (i = 0; i < wp->w_lines_valid; i++) {
+ long j = -1;
+ int row = 0;
+ for (int i = 0; i < wp->w_lines_valid; i++) {
if (wp->w_lines[i].wl_valid
&& wp->w_lines[i].wl_lnum == wp->w_topline) {
j = i;
@@ -1298,6 +1295,7 @@ win_update_start:
if (row > 0) {
win_scroll_lines(wp, 0, -row);
bot_start = wp->w_grid.rows - row;
+ bot_scroll_start = bot_start;
}
if ((row == 0 || bot_start < 999) && wp->w_lines_valid != 0) {
// Skip the lines (below the deleted lines) that are still
@@ -1305,7 +1303,7 @@ win_update_start:
// upwards, to compensate for the deleted lines. Set
// bot_start to the first row that needs redrawing.
bot_start = 0;
- idx = 0;
+ int idx = 0;
for (;;) {
wp->w_lines[idx] = wp->w_lines[j];
// stop at line that didn't fit, unless it is still
@@ -1339,25 +1337,25 @@ win_update_start:
mid_end = wp->w_grid.rows;
}
} else {
- // Not VALID or INVERTED: redraw all lines.
+ // Not UPD_VALID or UPD_INVERTED: redraw all lines.
mid_start = 0;
mid_end = wp->w_grid.rows;
}
- if (type == SOME_VALID) {
- // SOME_VALID: redraw all lines.
+ if (type == UPD_SOME_VALID) {
+ // UPD_SOME_VALID: redraw all lines.
mid_start = 0;
mid_end = wp->w_grid.rows;
- type = NOT_VALID;
+ type = UPD_NOT_VALID;
}
// check if we are updating or removing the inverted part
if ((VIsual_active && buf == curwin->w_buffer)
- || (wp->w_old_cursor_lnum != 0 && type != NOT_VALID)) {
+ || (wp->w_old_cursor_lnum != 0 && type != UPD_NOT_VALID)) {
linenr_T from, to;
if (VIsual_active) {
- if (VIsual_mode != wp->w_old_visual_mode || type == INVERTED_ALL) {
+ if (VIsual_mode != wp->w_old_visual_mode || type == UPD_INVERTED_ALL) {
// If the type of Visual selection changed, redraw the whole
// selection. Also when the ownership of the X selection is
// gained or lost.
@@ -1443,7 +1441,7 @@ win_update_start:
pos.lnum += cursor_above ? 1 : -1) {
colnr_T t;
- pos.col = (colnr_T)STRLEN(ml_get_buf(wp->w_buffer, pos.lnum, false));
+ pos.col = (colnr_T)strlen(ml_get_buf(wp->w_buffer, pos.lnum, false));
getvvcol(wp, &pos, NULL, NULL, &t);
if (toc < t) {
toc = t;
@@ -1502,9 +1500,9 @@ win_update_start:
// above the Visual area and reset wl_valid, do count these for
// mid_end (in srow).
if (mid_start > 0) {
- lnum = wp->w_topline;
- idx = 0;
- srow = 0;
+ linenr_T lnum = wp->w_topline;
+ int idx = 0;
+ int srow = 0;
if (scrolled_down) {
mid_start = top_end;
} else {
@@ -1550,38 +1548,18 @@ win_update_start:
wp->w_old_visual_col = 0;
}
- // reset got_int, otherwise regexp won't work
- save_got_int = got_int;
- got_int = 0;
- // Set the time limit to 'redrawtime'.
- proftime_T syntax_tm = profile_setlimit(p_rdt);
- syn_set_timeout(&syntax_tm);
-
- // Update all the window rows.
- idx = 0; // first entry in w_lines[].wl_size
- row = 0;
- srow = 0;
- lnum = wp->w_topline; // first line shown in window
-
- win_extmark_arr.size = 0;
-
- decor_redraw_reset(buf, &decor_state);
-
- 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;
- }
- }
-
bool cursorline_standout = win_cursorline_standout(wp);
win_check_ns_hl(wp);
+ // Update all the window rows.
+ int idx = 0; // first entry in w_lines[].wl_size
+ int row = 0; // current window row to display
+ int srow = 0; // starting row of the current line
+ linenr_T lnum = wp->w_topline; // first line shown in window
+
+ bool eof = false; // if true, we hit the end of the file
+ bool didline = false; // if true, we finished the last line
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)
@@ -1645,6 +1623,7 @@ win_update_start:
int new_rows = 0;
int xtra_rows;
linenr_T l;
+ int i;
// Count the old number of window rows, using w_lines[], which
// should still contain the sizes for the lines as they are
@@ -1675,10 +1654,11 @@ win_update_start:
// need to redraw until the end of the window.
// Inserting/deleting lines has no use.
bot_start = 0;
+ bot_scroll_start = 0;
} else {
// Able to count old number of rows: Count new window
// rows, and may insert/delete lines
- j = idx;
+ long j = idx;
for (l = lnum; l < mod_bot; l++) {
if (hasFoldingWin(wp, l, NULL, &l, true, NULL)) {
new_rows++;
@@ -1705,6 +1685,7 @@ win_update_start:
} else {
win_scroll_lines(wp, row, xtra_rows);
bot_start = wp->w_grid.rows + xtra_rows;
+ bot_scroll_start = bot_start;
}
} else if (xtra_rows > 0) {
// May scroll text down. If there is not enough
@@ -1714,6 +1695,7 @@ win_update_start:
mod_bot = MAXLNUM;
} else {
win_scroll_lines(wp, row + old_rows, xtra_rows);
+ bot_scroll_start = 0;
if (top_end > row + old_rows) {
// Scrolled the part at the top that requires
// updating down.
@@ -1792,7 +1774,7 @@ win_update_start:
// Let the syntax stuff know we skipped a few lines.
if (syntax_last_parsed != 0 && syntax_last_parsed + 1 < lnum
&& syntax_present(wp)) {
- syntax_end_parsing(syntax_last_parsed + 1);
+ syntax_end_parsing(wp, syntax_last_parsed + 1);
}
// Display one line
@@ -1847,6 +1829,18 @@ win_update_start:
did_update = DID_NONE;
}
+ // 'statuscolumn' width has changed or errored, start from the top.
+ if (wp->w_redr_statuscol) {
+ wp->w_redr_statuscol = false;
+ idx = 0;
+ row = 0;
+ lnum = wp->w_topline;
+ wp->w_lines_valid = 0;
+ wp->w_valid &= ~VALID_WCOL;
+ decor_providers_invoke_win(wp, providers, &line_providers, &provider_err);
+ continue;
+ }
+
if (lnum > buf->b_ml.ml_line_count) {
eof = true;
break;
@@ -1866,9 +1860,11 @@ win_update_start:
// Let the syntax stuff know we stop parsing here.
if (syntax_last_parsed != 0 && syntax_present(wp)) {
- syntax_end_parsing(syntax_last_parsed + 1);
+ syntax_end_parsing(wp, syntax_last_parsed + 1);
}
+ const linenr_T old_botline = wp->w_botline;
+
// If we didn't hit the end of the file, and we didn't finish the last
// line we were working on, then the line didn't fit.
wp->w_empty_rows = 0;
@@ -1885,34 +1881,38 @@ win_update_start:
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 symbol = wp->w_p_fcs_chars.lastline;
+ char fillbuf[12]; // 2 characters of 6 bytes
+ int charlen = utf_char2bytes(symbol, &fillbuf[0]);
+ utf_char2bytes(symbol, &fillbuf[charlen]);
// Last line isn't finished: Display "@@@" in the last screen line.
- 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.cols,
- '@', ' ', at_attr);
+ grid_puts_len(&wp->w_grid, fillbuf, MIN(wp->w_grid.cols, 2) * charlen, scr_row, 0, at_attr);
+ grid_fill(&wp->w_grid, scr_row, scr_row + 1, 2, wp->w_grid.cols, symbol, ' ', 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;
+ int symbol = wp->w_p_fcs_chars.lastline;
// Last line isn't finished: Display "@@@" at the end.
grid_fill(&wp->w_grid, wp->w_grid.rows - 1, wp->w_grid.rows,
- MAX(start_col, 0), wp->w_grid.cols, '@', '@', at_attr);
+ MAX(start_col, 0), wp->w_grid.cols, symbol, symbol, 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, wp->w_p_fcs_chars.lastline, ' ', true, srow, wp->w_grid.rows, HLF_AT);
+ set_empty_rows(wp, srow);
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);
+ long j = win_get_fill(wp, wp->w_botline);
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;
+ foldinfo_T info = { 0 };
row = win_line(wp, wp->w_botline, row, wp->w_grid.rows,
false, false, info, &line_providers, &provider_err);
}
@@ -1920,15 +1920,27 @@ win_update_start:
wp->w_botline = lnum;
}
- // 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,
+ // Make sure the rest of the screen is blank.
+ // write the "eob" character from 'fillchars' to rows that aren't part
+ // of the file.
+ // TODO(bfredl): just keep track of the valid EOB area from last redraw?
+ int lastline = bot_scroll_start;
+ if (mid_end >= row) {
+ lastline = MIN(lastline, mid_start);
+ }
+ // if (mod_bot > buf->b_ml.ml_line_count + 1) {
+ if (mod_bot > buf->b_ml.ml_line_count) {
+ lastline = 0;
+ }
+
+ win_draw_end(wp, wp->w_p_fcs_chars.eob, ' ', false, MAX(lastline, row), wp->w_grid.rows,
HLF_EOB);
+ set_empty_rows(wp, row);
}
kvi_destroy(line_providers);
- if (wp->w_redr_type >= REDRAW_TOP) {
+ if (wp->w_redr_type >= UPD_REDRAW_TOP) {
draw_vsep_win(wp);
draw_hsep_win(wp);
draw_sep_connectors_win(wp);
@@ -1966,11 +1978,11 @@ win_update_start:
update_topline(curwin); // may invalidate w_botline again
if (must_redraw != 0) {
// Don't update for changes in buffer again.
- i = curbuf->b_mod_set;
+ int mod_set = curbuf->b_mod_set;
curbuf->b_mod_set = false;
win_update(curwin, providers);
must_redraw = 0;
- curbuf->b_mod_set = i;
+ curbuf->b_mod_set = mod_set;
}
recursive = false;
}
@@ -1982,16 +1994,16 @@ win_update_start:
}
}
-/// Redraw a window later, with update_screen(type).
+/// Redraw a window later, with wp->w_redr_type >= type.
///
/// Set must_redraw only if not already set to a higher value.
-/// e.g. if must_redraw is CLEAR, type NOT_VALID will do nothing.
+/// e.g. if must_redraw is UPD_CLEAR, type UPD_NOT_VALID will do nothing.
void redraw_later(win_T *wp, int type)
FUNC_ATTR_NONNULL_ALL
{
if (!exiting && wp->w_redr_type < type) {
wp->w_redr_type = type;
- if (type >= NOT_VALID) {
+ if (type >= UPD_NOT_VALID) {
wp->w_lines_valid = 0;
}
if (must_redraw < type) { // must_redraw is the maximum of all windows
@@ -2015,7 +2027,7 @@ void redraw_all_later(int type)
void screen_invalidate_highlights(void)
{
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- redraw_later(wp, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
wp->w_grid_alloc.valid = false;
}
}
@@ -2035,12 +2047,14 @@ void redraw_buf_later(buf_T *buf, int type)
}
}
-void redraw_buf_line_later(buf_T *buf, linenr_T line)
+void redraw_buf_line_later(buf_T *buf, linenr_T line, bool force)
{
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer == buf
- && line >= wp->w_topline && line < wp->w_botline) {
- redrawWinline(wp, line);
+ if (wp->w_buffer == buf) {
+ redrawWinline(wp, MIN(line, buf->b_ml.ml_line_count));
+ if (force && line > buf->b_ml.ml_line_count) {
+ wp->w_redraw_bot = line;
+ }
}
}
}
@@ -2056,7 +2070,7 @@ void redraw_buf_range_later(buf_T *buf, linenr_T firstline, linenr_T lastline)
if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lastline) {
wp->w_redraw_bot = lastline;
}
- redraw_later(wp, VALID);
+ redraw_later(wp, UPD_VALID);
}
}
}
@@ -2070,8 +2084,8 @@ void redraw_buf_status_later(buf_T *buf)
|| (wp == curwin && global_stl_height())
|| wp->w_winbar_height)) {
wp->w_redr_status = true;
- if (must_redraw < VALID) {
- must_redraw = VALID;
+ if (must_redraw < UPD_VALID) {
+ must_redraw = UPD_VALID;
}
}
}
@@ -2086,7 +2100,7 @@ void status_redraw_all(void)
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);
+ redraw_later(wp, UPD_VALID);
}
}
}
@@ -2106,7 +2120,7 @@ void status_redraw_buf(buf_T *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);
+ redraw_later(wp, UPD_VALID);
}
}
}
@@ -2116,10 +2130,13 @@ void redraw_statuslines(void)
{
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_redr_status) {
+ win_check_ns_hl(wp);
win_redr_winbar(wp);
win_redr_status(wp);
}
}
+
+ win_check_ns_hl(NULL);
if (redraw_tabline) {
draw_tabline();
}
@@ -2162,6 +2179,6 @@ void redrawWinline(win_T *wp, linenr_T lnum)
if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lnum) {
wp->w_redraw_bot = lnum;
}
- redraw_later(wp, VALID);
+ redraw_later(wp, UPD_VALID);
}
}
diff --git a/src/nvim/drawscreen.h b/src/nvim/drawscreen.h
index 3eac1caaa1..c14703dfa9 100644
--- a/src/nvim/drawscreen.h
+++ b/src/nvim/drawscreen.h
@@ -1,18 +1,21 @@
#ifndef NVIM_DRAWSCREEN_H
#define NVIM_DRAWSCREEN_H
+#include <stdbool.h>
+
#include "nvim/drawline.h"
+#include "nvim/macros.h"
/// flags for update_screen()
/// The higher the value, the higher the priority
enum {
- VALID = 10, ///< buffer not changed, or changes marked with b_mod_*
- INVERTED = 20, ///< redisplay inverted part that changed
- INVERTED_ALL = 25, ///< redisplay whole inverted part
- REDRAW_TOP = 30, ///< display first w_upd_rows screen lines
- SOME_VALID = 35, ///< like NOT_VALID but may scroll
- NOT_VALID = 40, ///< buffer needs complete redraw
- CLEAR = 50, ///< screen messed up, clear it
+ UPD_VALID = 10, ///< buffer not changed, or changes marked with b_mod_*
+ UPD_INVERTED = 20, ///< redisplay inverted part that changed
+ UPD_INVERTED_ALL = 25, ///< redisplay whole inverted part
+ UPD_REDRAW_TOP = 30, ///< display first w_upd_rows screen lines
+ UPD_SOME_VALID = 35, ///< like UPD_NOT_VALID but may scroll
+ UPD_NOT_VALID = 40, ///< buffer needs complete redraw
+ UPD_CLEAR = 50, ///< screen messed up, clear it
};
/// While redrawing the screen this flag is set. It means the screen size
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 6583ac8584..095d73f53f 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -1,16 +1,17 @@
// 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
-/*
- * edit.c: functions for Insert mode
- */
+// edit.c: functions for Insert mode
#include <assert.h>
+#include <ctype.h>
#include <inttypes.h>
#include <stdbool.h>
#include <string.h>
+#include <sys/types.h>
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/change.h"
#include "nvim/charset.h"
@@ -19,20 +20,23 @@
#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
-#include "nvim/event/loop.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
-#include "nvim/ex_getln.h"
#include "nvim/extmark.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
#include "nvim/grid.h"
+#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
#include "nvim/insexpand.h"
#include "nvim/keycodes.h"
-#include "nvim/main.h"
+#include "nvim/macros.h"
#include "nvim/mapping.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
@@ -45,17 +49,18 @@
#include "nvim/ops.h"
#include "nvim/option.h"
#include "nvim/os/input.h"
-#include "nvim/os/time.h"
-#include "nvim/path.h"
#include "nvim/plines.h"
#include "nvim/popupmenu.h"
-#include "nvim/quickfix.h"
+#include "nvim/pos.h"
+#include "nvim/screen.h"
#include "nvim/search.h"
-#include "nvim/spell.h"
#include "nvim/state.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/terminal.h"
+#include "nvim/textformat.h"
+#include "nvim/textobject.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/vim.h"
@@ -81,31 +86,34 @@ typedef struct insert_state {
int did_restart_edit; // remember if insert mode was restarted
// after a ctrl+o
bool nomove;
- char_u *ptr;
+ char *ptr;
} InsertState;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "edit.c.generated.h"
#endif
-#define BACKSPACE_CHAR 1
-#define BACKSPACE_WORD 2
-#define BACKSPACE_WORD_NOT_SPACE 3
-#define BACKSPACE_LINE 4
+enum {
+ BACKSPACE_CHAR = 1,
+ BACKSPACE_WORD = 2,
+ BACKSPACE_WORD_NOT_SPACE = 3,
+ BACKSPACE_LINE = 4,
+};
+
+/// Set when doing something for completion that may call edit() recursively,
+/// which is not allowed.
+static bool compl_busy = false;
static colnr_T Insstart_textlen; // length of line when insert started
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 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 char *last_insert = NULL; // the text of the previous insert, 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()
static bool can_cindent; // may do cindenting on this line
-static int old_indent = 0; // for ^^D command in insert mode
-
static int revins_on; // reverse insert mode on
static int revins_chars; // how much to skip after edit
static int revins_legal; // was the last char 'legal'?
@@ -115,8 +123,6 @@ static bool ins_need_undo; // call u_save() before inserting a
// char. Set when edit() is called.
// after that arrow_used is used.
-static bool did_add_space = false; // auto_format() added an extra space
- // under the cursor
static TriState dont_sync_undo = kFalse; // CTRL-G U prevents syncing undo
// for the next left/right cursor key
@@ -143,14 +149,14 @@ static void insert_enter(InsertState *s)
pos_T save_cursor = curwin->w_cursor;
if (s->cmdchar == 'R') {
- s->ptr = (char_u *)"r";
+ s->ptr = "r";
} else if (s->cmdchar == 'V') {
- s->ptr = (char_u *)"v";
+ s->ptr = "v";
} else {
- s->ptr = (char_u *)"i";
+ s->ptr = "i";
}
- set_vim_var_string(VV_INSERTMODE, (char *)s->ptr, 1);
+ set_vim_var_string(VV_INSERTMODE, s->ptr, 1);
set_vim_var_string(VV_CHAR, NULL, -1);
ins_apply_autocmds(EVENT_INSERTENTER);
@@ -276,7 +282,7 @@ static void insert_enter(InsertState *s)
if (s->ptr[1] == NUL) {
curwin->w_cursor.col++;
} else {
- s->i = utfc_ptr2len((char *)s->ptr);
+ s->i = utfc_ptr2len(s->ptr);
if (s->ptr[s->i] == NUL) {
curwin->w_cursor.col += s->i;
}
@@ -321,7 +327,7 @@ static void insert_enter(InsertState *s)
if (s->ptr == NULL) {
new_insert_skip = 0;
} else {
- new_insert_skip = (int)STRLEN(s->ptr);
+ new_insert_skip = (int)strlen(s->ptr);
xfree(s->ptr);
}
@@ -439,8 +445,7 @@ static int insert_check(VimState *state)
s->mincol = curwin->w_wcol;
validate_cursor_col();
- if (
- curwin->w_wcol < s->mincol - tabstop_at(get_nolist_virtcol(),
+ if (curwin->w_wcol < s->mincol - tabstop_at(get_nolist_virtcol(),
curbuf->b_p_ts,
curbuf->b_p_vts_array)
&& curwin->w_wrow == curwin->w_winrow
@@ -448,7 +453,7 @@ static int insert_check(VimState *state)
&& (curwin->w_cursor.lnum != curwin->w_topline
|| curwin->w_topfill > 0)) {
if (curwin->w_topfill > 0) {
- --curwin->w_topfill;
+ curwin->w_topfill--;
} else if (hasFolding(curwin->w_topline, NULL, &s->old_topline)) {
set_topline(curwin, s->old_topline + 1);
} else {
@@ -552,12 +557,12 @@ static int insert_execute(VimState *state, int key)
// completion: Add to "compl_leader".
if (ins_compl_accept_char(s->c)) {
// Trigger InsertCharPre.
- char_u *str = do_insert_char_pre(s->c);
- char_u *p;
+ char *str = do_insert_char_pre(s->c);
+ char *p;
if (str != NULL) {
for (p = str; *p != NUL; MB_PTR_ADV(p)) {
- ins_compl_addleader(utf_ptr2char((char *)p));
+ ins_compl_addleader(utf_ptr2char(p));
}
xfree(str);
} else {
@@ -624,16 +629,17 @@ static int insert_execute(VimState *state, int key)
}
if (cindent_on() && ctrl_x_mode_none()) {
+ s->line_is_white = inindent(0);
// A key name preceded by a bang means this key is not to be
// inserted. Skip ahead to the re-indenting below.
- // A key name preceded by a star means that indenting has to be
- // done before inserting the key.
- s->line_is_white = inindent(0);
- if (in_cinkeys(s->c, '!', s->line_is_white)) {
- insert_do_cindent(s);
+ if (in_cinkeys(s->c, '!', s->line_is_white)
+ && stop_arrow() == OK) {
+ do_c_expr_indent();
return 1; // continue
}
+ // A key name preceded by a star means that indenting has to be
+ // done before inserting the key.
if (can_cindent && in_cinkeys(s->c, '*', s->line_is_white)
&& stop_arrow() == OK) {
do_c_expr_indent();
@@ -901,6 +907,12 @@ check_pum:
}
pum_want.active = false;
}
+
+ if (curbuf->b_u_synced) {
+ // The K_EVENT, K_COMMAND, or K_LUA caused undo to be synced.
+ // Need to save the line for undo before inserting the next char.
+ ins_need_undo = true;
+ }
break;
case K_HOME: // <Home>
@@ -1090,7 +1102,7 @@ check_pum:
// but it is under other ^X modes
if (*curbuf->b_p_cpt == NUL
&& (ctrl_x_mode_normal() || ctrl_x_mode_whole_line())
- && !(compl_cont_status & CONT_LOCAL)) {
+ && !compl_status_local()) {
goto normalchar;
}
@@ -1109,7 +1121,7 @@ normalchar:
if (!p_paste) {
// Trigger InsertCharPre.
- char *str = (char *)do_insert_char_pre(s->c);
+ char *str = do_insert_char_pre(s->c);
char *p;
if (str != NULL) {
@@ -1176,9 +1188,11 @@ normalchar:
static void insert_do_complete(InsertState *s)
{
compl_busy = true;
+ disable_fold_update++; // don't redraw folds here
if (ins_complete(s->c, true) == FAIL) {
- compl_cont_status = 0;
+ compl_status_clear();
}
+ disable_fold_update--;
compl_busy = false;
can_si = may_do_si(); // allow smartindenting
}
@@ -1225,9 +1239,8 @@ bool edit(int cmdchar, bool startln, long count)
restart_edit = 'i';
force_restart_edit = true;
return false;
- } else {
- return terminal_enter();
}
+ return terminal_enter();
}
// Don't allow inserting in the sandbox.
@@ -1283,7 +1296,7 @@ void ins_redraw(bool ready)
// a "(". The autocommand may also require a redraw, so it's done
// again below, unfortunately.
if (syntax_present(curwin) && must_redraw) {
- update_screen(0);
+ update_screen();
}
// Make sure curswant is correct, an autocommand may call
// getcurpos()
@@ -1331,8 +1344,7 @@ void ins_redraw(bool ready)
}
if (ready) {
- // Trigger Scroll if viewport changed.
- may_trigger_winscrolled();
+ may_trigger_win_scrolled_resized();
}
// Trigger BufModified if b_changed_invalid is set.
@@ -1345,7 +1357,7 @@ void ins_redraw(bool ready)
pum_check_clear();
if (must_redraw) {
- update_screen(0);
+ update_screen();
} else if (clear_cmdline || redraw_cmdline) {
showmode(); // clear cmdline and show mode
}
@@ -1354,9 +1366,7 @@ void ins_redraw(bool ready)
emsg_on_display = false; // may remove error message now
}
-/*
- * Handle a CTRL-V or CTRL-Q typed in Insert mode.
- */
+// Handle a CTRL-V or CTRL-Q typed in Insert mode.
static void ins_ctrl_v(void)
{
int c;
@@ -1386,10 +1396,8 @@ static void ins_ctrl_v(void)
revins_legal++;
}
-/*
- * Put a character directly onto the screen. It's not stored in a buffer.
- * Used while handling CTRL-K, CTRL-V, etc. in Insert mode.
- */
+// Put a character directly onto the screen. It's not stored in a buffer.
+// Used while handling CTRL-K, CTRL-V, etc. in Insert mode.
static int pc_status;
#define PC_STATUS_UNSET 0 // pc_bytes was not set
#define PC_STATUS_RIGHT 1 // right half of double-wide char
@@ -1433,25 +1441,25 @@ void edit_putchar(int c, bool highlight)
// save the character to be able to put it back
if (pc_status == PC_STATUS_UNSET) {
- grid_getbytes(&curwin->w_grid, pc_row, pc_col, pc_bytes, &pc_attr);
+ grid_getbytes(&curwin->w_grid, pc_row, pc_col, (char *)pc_bytes, &pc_attr);
pc_status = PC_STATUS_SET;
}
grid_putchar(&curwin->w_grid, c, pc_row, pc_col, attr);
}
}
-/// Return the effective prompt for the specified buffer.
-char_u *buf_prompt_text(const buf_T *const buf)
+/// @return the effective prompt for the specified buffer.
+char *buf_prompt_text(const buf_T *const buf)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
{
if (buf->b_prompt_text == NULL) {
- return (char_u *)"% ";
+ return "% ";
}
- return (char_u *)buf->b_prompt_text;
+ return buf->b_prompt_text;
}
-// Return the effective prompt for the current buffer.
-char_u *prompt_text(void)
+/// @return the effective prompt for the current buffer.
+char *prompt_text(void)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
{
return buf_prompt_text(curbuf);
@@ -1461,17 +1469,17 @@ char_u *prompt_text(void)
// Move the cursor to this line.
static void init_prompt(int cmdchar_todo)
{
- char_u *prompt = prompt_text();
- char_u *text;
+ char *prompt = prompt_text();
+ char *text;
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
text = get_cursor_line_ptr();
- if (STRNCMP(text, prompt, STRLEN(prompt)) != 0) {
+ 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, (char *)prompt, true);
+ ml_replace(curbuf->b_ml.ml_line_count, prompt, true);
} else {
- ml_append(curbuf->b_ml.ml_line_count, (char *)prompt, 0, false);
+ ml_append(curbuf->b_ml.ml_line_count, prompt, 0, false);
}
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
coladvance(MAXCOL);
@@ -1479,9 +1487,9 @@ static void init_prompt(int cmdchar_todo)
}
// Insert always starts after the prompt, allow editing text after it.
- if (Insstart_orig.lnum != curwin->w_cursor.lnum || Insstart_orig.col != (colnr_T)STRLEN(prompt)) {
+ if (Insstart_orig.lnum != curwin->w_cursor.lnum || Insstart_orig.col != (colnr_T)strlen(prompt)) {
Insstart.lnum = curwin->w_cursor.lnum;
- Insstart.col = (colnr_T)STRLEN(prompt);
+ Insstart.col = (colnr_T)strlen(prompt);
Insstart_orig = Insstart;
Insstart_textlen = Insstart.col;
Insstart_blank_vcol = MAXCOL;
@@ -1491,8 +1499,8 @@ static void init_prompt(int cmdchar_todo)
if (cmdchar_todo == 'A') {
coladvance(MAXCOL);
}
- if (curwin->w_cursor.col < (colnr_T)STRLEN(prompt)) {
- curwin->w_cursor.col = (colnr_T)STRLEN(prompt);
+ if (curwin->w_cursor.col < (colnr_T)strlen(prompt)) {
+ curwin->w_cursor.col = (colnr_T)strlen(prompt);
}
// Make sure the cursor is in a valid position.
check_cursor();
@@ -1503,12 +1511,10 @@ 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());
+ && curwin->w_cursor.col >= (int)strlen(prompt_text());
}
-/*
- * Undo the previous edit_putchar().
- */
+// Undo the previous edit_putchar().
void edit_unputchar(void)
{
if (pc_status != PC_STATUS_UNSET && pc_row >= msg_scrolled) {
@@ -1518,16 +1524,13 @@ void edit_unputchar(void)
if (pc_status == PC_STATUS_RIGHT || pc_status == PC_STATUS_LEFT) {
redrawWinline(curwin, curwin->w_cursor.lnum);
} else {
- grid_puts(&curwin->w_grid, pc_bytes, pc_row - msg_scrolled, pc_col,
- pc_attr);
+ grid_puts(&curwin->w_grid, (char *)pc_bytes, pc_row - msg_scrolled, pc_col, pc_attr);
}
}
}
-/*
- * Called when p_dollar is set: display a '$' at the end of the changed text
- * Only works when cursor is in the line that changes.
- */
+// Called when p_dollar is set: display a '$' at the end of the changed text
+// Only works when cursor is in the line that changes.
void display_dollar(colnr_T col)
{
colnr_T save_col;
@@ -1540,7 +1543,7 @@ void display_dollar(colnr_T col)
curwin->w_cursor.col = col;
// If on the last byte of a multi-byte move to the first byte.
- char_u *p = get_cursor_line_ptr();
+ char *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.cols) {
@@ -1550,11 +1553,9 @@ void display_dollar(colnr_T col)
curwin->w_cursor.col = save_col;
}
-/*
- * Call this function before moving the cursor from the normal insert position
- * in insert mode.
- */
-static void undisplay_dollar(void)
+// Call this function before moving the cursor from the normal insert position
+// in insert mode.
+void undisplay_dollar(void)
{
if (dollar_vcol >= 0) {
dollar_vcol = -1;
@@ -1568,7 +1569,7 @@ static void undisplay_dollar(void)
/// type == INDENT_DEC decrease indent (for CTRL-D)
/// type == INDENT_SET set indent to "amount"
///
-/// @param round if TRUE, round the indent to 'shiftwidth' (only with _INC and _Dec).
+/// @param round if true, round the indent to 'shiftwidth' (only with _INC and _Dec).
/// @param replaced replaced character, put on replace stack
/// @param call_changed_bytes call changed_bytes()
void change_indent(int type, int amount, int round, int replaced, int call_changed_bytes)
@@ -1577,30 +1578,28 @@ 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;
- char_u *ptr;
+ char *ptr;
int save_p_list;
int start_col;
colnr_T vc;
colnr_T orig_col = 0; // init for GCC
- char_u *new_line, *orig_line = NULL; // init for GCC
+ char *new_line, *orig_line = NULL; // init for GCC
// 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_line = xstrdup(get_cursor_line_ptr()); // Deal with NULL below
orig_col = curwin->w_cursor.col;
}
// for the following tricks we don't want list mode
save_p_list = curwin->w_p_list;
- curwin->w_p_list = FALSE;
+ curwin->w_p_list = false;
vc = getvcol_nolist(&curwin->w_cursor);
vcol = vc;
- /*
- * For Replace mode we need to fix the replace stack later, which is only
- * possible when the cursor is in the indent. Remember the number of
- * characters before the cursor if it's possible.
- */
+ // For Replace mode we need to fix the replace stack later, which is only
+ // possible when the cursor is in the indent. Remember the number of
+ // characters before the cursor if it's possible.
start_col = curwin->w_cursor.col;
// determine offset from first non-blank
@@ -1610,10 +1609,8 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
insstart_less = curwin->w_cursor.col;
- /*
- * If the cursor is in the indent, compute how many screen columns the
- * cursor is to the left of the first non-blank.
- */
+ // If the cursor is in the indent, compute how many screen columns the
+ // cursor is to the left of the first non-blank.
if (new_cursor_col < 0) {
vcol = get_indent() - vcol;
}
@@ -1622,9 +1619,7 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
start_col = -1;
}
- /*
- * Set the new indent. The cursor will be put on the first non-blank.
- */
+ // Set the new indent. The cursor will be put on the first non-blank.
if (type == INDENT_SET) {
(void)set_indent(amount, call_changed_bytes ? SIN_CHANGED : 0);
} else {
@@ -1639,20 +1634,16 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
}
insstart_less -= curwin->w_cursor.col;
- /*
- * Try to put cursor on same character.
- * If the cursor is at or after the first non-blank in the line,
- * compute the cursor column relative to the column of the first
- * non-blank character.
- * If we are not in insert mode, leave the cursor on the first non-blank.
- * If the cursor is before the first non-blank, position it relative
- * to the first non-blank, counted in screen columns.
- */
+ // Try to put cursor on same character.
+ // If the cursor is at or after the first non-blank in the line,
+ // compute the cursor column relative to the column of the first
+ // non-blank character.
+ // If we are not in insert mode, leave the cursor on the first non-blank.
+ // If the cursor is before the first non-blank, position it relative
+ // to the first non-blank, counted in screen columns.
if (new_cursor_col >= 0) {
- /*
- * When changing the indent while the cursor is touching it, reset
- * Insstart_col to 0.
- */
+ // When changing the indent while the cursor is touching it, reset
+ // Insstart_col to 0.
if (new_cursor_col == 0) {
insstart_less = MAXCOL;
}
@@ -1660,33 +1651,31 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
} else if (!(State & MODE_INSERT)) {
new_cursor_col = curwin->w_cursor.col;
} else {
- /*
- * Compute the screen column where the cursor should be.
- */
+ // Compute the screen column where the cursor should be.
vcol = get_indent() - vcol;
curwin->w_virtcol = (colnr_T)((vcol < 0) ? 0 : vcol);
- /*
- * Advance the cursor until we reach the right screen column.
- */
- vcol = last_vcol = 0;
- new_cursor_col = -1;
+ // Advance the cursor until we reach the right screen column.
+ last_vcol = 0;
ptr = get_cursor_line_ptr();
- while (vcol <= (int)curwin->w_virtcol) {
- last_vcol = vcol;
- if (new_cursor_col >= 0) {
- new_cursor_col += utfc_ptr2len((char *)ptr + new_cursor_col);
- } else {
- new_cursor_col++;
+ chartabsize_T cts;
+ init_chartabsize_arg(&cts, curwin, 0, 0, ptr, ptr);
+ while (cts.cts_vcol <= (int)curwin->w_virtcol) {
+ last_vcol = cts.cts_vcol;
+ if (cts.cts_vcol > 0) {
+ MB_PTR_ADV(cts.cts_ptr);
+ }
+ if (*cts.cts_ptr == NUL) {
+ break;
}
- vcol += lbr_chartabsize(ptr, ptr + new_cursor_col, (colnr_T)vcol);
+ cts.cts_vcol += lbr_chartabsize(&cts);
}
vcol = last_vcol;
+ new_cursor_col = (int)(cts.cts_ptr - cts.cts_line);
+ clear_chartabsize_arg(&cts);
- /*
- * May need to insert spaces to be able to position the cursor on
- * the right screen column.
- */
+ // May need to insert spaces to be able to position the cursor on
+ // the right screen column.
if (vcol != (int)curwin->w_virtcol) {
curwin->w_cursor.col = (colnr_T)new_cursor_col;
size_t i = (size_t)(curwin->w_virtcol - vcol);
@@ -1697,10 +1686,8 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
xfree(ptr);
}
- /*
- * When changing the indent while the cursor is in it, reset
- * Insstart_col to 0.
- */
+ // When changing the indent while the cursor is in it, reset
+ // Insstart_col to 0.
insstart_less = MAXCOL;
}
@@ -1711,12 +1698,10 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
} else {
curwin->w_cursor.col = (colnr_T)new_cursor_col;
}
- curwin->w_set_curswant = TRUE;
+ curwin->w_set_curswant = true;
changed_cline_bef_curs();
- /*
- * May have to adjust the start of the insert.
- */
+ // May have to adjust the start of the insert.
if (State & MODE_INSERT) {
if (curwin->w_cursor.lnum == Insstart.lnum && Insstart.col != 0) {
if ((int)Insstart.col <= insstart_less) {
@@ -1757,14 +1742,14 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
// 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());
+ new_line = xstrdup(get_cursor_line_ptr());
// We only put back the new line up to the cursor
new_line[curwin->w_cursor.col] = NUL;
int new_col = curwin->w_cursor.col;
// Put back original line
- ml_replace(curwin->w_cursor.lnum, (char *)orig_line, false);
+ ml_replace(curwin->w_cursor.lnum, orig_line, false);
curwin->w_cursor.col = orig_col;
curbuf_splice_pending++;
@@ -1792,12 +1777,12 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
/// 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)
+void truncate_spaces(char *line)
{
int i;
// find start of trailing white space
- for (i = (int)STRLEN(line) - 1; i >= 0 && ascii_iswhite(line[i]); i--) {
+ for (i = (int)strlen(line) - 1; i >= 0 && ascii_iswhite(line[i]); i--) {
if (State & REPLACE_FLAG) {
replace_join(0); // remove a NUL from the replace stack
}
@@ -1837,7 +1822,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((char *)get_cursor_pos_ptr());
+ int l = utf_ptr2len(get_cursor_pos_ptr());
if (l == 0) { // end of line
break;
@@ -1964,28 +1949,28 @@ int get_literal(bool no_simplify)
/// @param ctrlv `c` was typed after CTRL-V
static void insert_special(int c, int allow_modmask, int ctrlv)
{
- char_u *p;
+ char *p;
int len;
// Special function key, translate into "<Key>". Up to the last '>' is
// inserted with ins_str(), so as not to replace characters in replace
// mode.
// Only use mod_mask for special keys, to avoid things like <S-Space>,
- // unless 'allow_modmask' is TRUE.
+ // unless 'allow_modmask' is true.
if (mod_mask & MOD_MASK_CMD) { // Command-key never produces a normal key.
allow_modmask = true;
}
if (IS_SPECIAL(c) || (mod_mask && allow_modmask)) {
- p = get_special_key_name(c, mod_mask);
- len = (int)STRLEN(p);
- c = p[len - 1];
+ p = (char *)get_special_key_name(c, mod_mask);
+ len = (int)strlen(p);
+ c = (uint8_t)p[len - 1];
if (len > 2) {
if (stop_arrow() == FAIL) {
return;
}
p[len - 1] = NUL;
ins_str(p);
- AppendToRedobuffLit((char *)p, -1);
+ AppendToRedobuffLit(p, -1);
ctrlv = false;
}
}
@@ -1994,20 +1979,15 @@ static void insert_special(int c, int allow_modmask, int ctrlv)
}
}
-/*
- * Special characters in this context are those that need processing other
- * than the simple insertion that can be performed here. This includes ESC
- * which terminates the insert, and CR/NL which need special processing to
- * open up a new line. This routine tries to optimize insertions performed by
- * the "redo", "undo" or "put" commands, so it needs to know when it should
- * stop and defer processing to the "normal" mechanism.
- * '0' and '^' are special, because they can be followed by CTRL-D.
- */
+// Special characters in this context are those that need processing other
+// than the simple insertion that can be performed here. This includes ESC
+// which terminates the insert, and CR/NL which need special processing to
+// open up a new line. This routine tries to optimize insertions performed by
+// the "redo", "undo" or "put" commands, so it needs to know when it should
+// stop and defer processing to the "normal" mechanism.
+// '0' and '^' are special, because they can be followed by CTRL-D.
#define ISSPECIAL(c) ((c) < ' ' || (c) >= DEL || (c) == '0' || (c) == '^')
-#define WHITECHAR(cc) (ascii_iswhite(cc) \
- && !utf_iscomposing(utf_ptr2char((char *)get_cursor_pos_ptr() + 1)))
-
///
/// "flags": INSCHAR_FORMAT - force formatting
/// INSCHAR_CTRLV - char typed just after CTRL-V
@@ -2081,8 +2061,8 @@ void insertchar(int c, int flags, int second_indent)
// 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, &p, false, true);
+ char *line = get_cursor_line_ptr();
+ int i = get_leader_len(line, &p, false, true);
if (i > 0 && vim_strchr(p, COM_MIDDLE) != NULL) { // Just checking
// Skip middle-comment string
while (*p && p[-1] != ':') { // find end of middle flags
@@ -2115,7 +2095,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, (size_t)(end_len - 1));
+ ins_bytes_len((char *)lead_end, (size_t)(end_len - 1));
}
}
}
@@ -2143,11 +2123,11 @@ void insertchar(int c, int flags, int second_indent)
&& !cindent_on()
&& !p_ri) {
#define INPUT_BUFLEN 100
- char_u buf[INPUT_BUFLEN + 1];
+ char buf[INPUT_BUFLEN + 1];
int i;
colnr_T virtcol = 0;
- buf[0] = (char_u)c;
+ buf[0] = (char)c;
i = 1;
if (textwidth > 0) {
virtcol = get_nolist_virtcol();
@@ -2163,27 +2143,27 @@ void insertchar(int c, int flags, int second_indent)
&& MB_BYTE2LEN(c) == 1
&& i < INPUT_BUFLEN
&& (textwidth == 0
- || (virtcol += byte2cells(buf[i - 1])) < (colnr_T)textwidth)
- && !(!no_abbr && !vim_iswordc(c) && vim_iswordc(buf[i - 1]))) {
+ || (virtcol += byte2cells((uint8_t)buf[i - 1])) < (colnr_T)textwidth)
+ && !(!no_abbr && !vim_iswordc(c) && vim_iswordc((uint8_t)buf[i - 1]))) {
c = vgetc();
if (p_hkmap && KeyTyped) {
c = hkmap(c); // Hebrew mode mapping
}
- buf[i++] = (char_u)c;
+ buf[i++] = (char)c;
}
do_digraph(-1); // clear digraphs
- do_digraph(buf[i - 1]); // may be the start of a digraph
+ do_digraph((uint8_t)buf[i - 1]); // may be the start of a digraph
buf[i] = NUL;
ins_str(buf);
if (flags & INSCHAR_CTRLV) {
- redo_literal(*buf);
+ redo_literal((uint8_t)(*buf));
i = 1;
} else {
i = 0;
}
if (buf[i] != NUL) {
- AppendToRedobuffLit((char *)buf + i, -1);
+ AppendToRedobuffLit(buf + i, -1);
}
} else {
int cc;
@@ -2191,9 +2171,9 @@ void insertchar(int c, int flags, int second_indent)
if ((cc = utf_char2len(c)) > 1) {
char buf[MB_MAXBYTES + 1];
- utf_char2bytes(c, (char *)buf);
+ utf_char2bytes(c, buf);
buf[cc] = NUL;
- ins_char_bytes((char_u *)buf, (size_t)cc);
+ ins_char_bytes(buf, (size_t)cc);
AppendCharToRedobuff(c);
} else {
ins_char(c);
@@ -2206,600 +2186,7 @@ void insertchar(int c, int flags, int second_indent)
}
}
-/// Format text at the current insert position.
-///
-/// If the INSCHAR_COM_LIST flag is present, then the value of second_indent
-/// will be the comment leader length sent to open_line().
-///
-/// @param c character to be inserted (can be NUL)
-static void internal_format(int textwidth, int second_indent, int flags, int format_only, int c)
-{
- int cc;
- int save_char = NUL;
- bool haveto_redraw = false;
- const bool fo_ins_blank = has_format_option(FO_INS_BLANK);
- const bool fo_multibyte = has_format_option(FO_MBYTE_BREAK);
- const bool fo_rigor_tw = has_format_option(FO_RIGOROUS_TW);
- const bool fo_white_par = has_format_option(FO_WHITE_PAR);
- bool first_line = true;
- colnr_T leader_len;
- bool no_leader = false;
- int do_comments = (flags & INSCHAR_DO_COM);
- int has_lbr = curwin->w_p_lbr;
-
- // make sure win_lbr_chartabsize() counts correctly
- curwin->w_p_lbr = false;
-
- /*
- * When 'ai' is off we don't want a space under the cursor to be
- * deleted. Replace it with an 'x' temporarily.
- */
- if (!curbuf->b_p_ai
- && !(State & VREPLACE_FLAG)) {
- cc = gchar_cursor();
- if (ascii_iswhite(cc)) {
- save_char = cc;
- pchar_cursor('x');
- }
- }
-
- /*
- * Repeat breaking lines, until the current line is not too long.
- */
- while (!got_int) {
- int startcol; // Cursor column at entry
- int wantcol; // column at textwidth border
- int foundcol; // column for start of spaces
- int end_foundcol = 0; // column for start of word
- colnr_T len;
- colnr_T virtcol;
- int orig_col = 0;
- 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());
- if (virtcol <= (colnr_T)textwidth) {
- break;
- }
-
- if (no_leader) {
- do_comments = false;
- } else if (!(flags & INSCHAR_FORMAT)
- && has_format_option(FO_WRAP_COMS)) {
- do_comments = true;
- }
-
- // Don't break until after the comment leader
- if (do_comments) {
- 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;
- }
-
- // If the line doesn't start with a comment leader, then don't
- // start one in a following broken line. Avoids that a %word
- // moved to the start of the next line causes all following lines
- // to start with %.
- if (leader_len == 0) {
- no_leader = true;
- }
- if (!(flags & INSCHAR_FORMAT)
- && leader_len == 0
- && !has_format_option(FO_WRAP)) {
- break;
- }
- if ((startcol = curwin->w_cursor.col) == 0) {
- break;
- }
-
- // find column of textwidth border
- coladvance((colnr_T)textwidth);
- wantcol = curwin->w_cursor.col;
-
- curwin->w_cursor.col = startcol;
- foundcol = 0;
- int skip_pos = 0;
-
- /*
- * Find position to break at.
- * Stop at first entered white when 'formatoptions' has 'v'
- */
- while ((!fo_ins_blank && !has_format_option(FO_INS_VI))
- || (flags & INSCHAR_FORMAT)
- || curwin->w_cursor.lnum != Insstart.lnum
- || curwin->w_cursor.col >= Insstart.col) {
- if (curwin->w_cursor.col == startcol && c != NUL) {
- cc = c;
- } else {
- cc = gchar_cursor();
- }
- if (WHITECHAR(cc)) {
- // remember position of blank just before text
- end_col = curwin->w_cursor.col;
-
- // find start of sequence of blanks
- int wcc = 0; // counter for whitespace chars
- while (curwin->w_cursor.col > 0 && WHITECHAR(cc)) {
- dec_cursor();
- cc = gchar_cursor();
-
- // Increment count of how many whitespace chars in this
- // group; we only need to know if it's more than one.
- if (wcc < 2) {
- wcc++;
- }
- }
- if (curwin->w_cursor.col == 0 && WHITECHAR(cc)) {
- break; // only spaces in front of text
- }
-
- // Don't break after a period when 'formatoptions' has 'p' and
- // there are less than two spaces.
- if (has_format_option(FO_PERIOD_ABBR) && cc == '.' && wcc < 2) {
- continue;
- }
-
- // Don't break until after the comment leader
- if (curwin->w_cursor.col < leader_len) {
- break;
- }
-
- if (has_format_option(FO_ONE_LETTER)) {
- // do not break after one-letter words
- if (curwin->w_cursor.col == 0) {
- break; // one-letter word at begin
- }
- // do not break "#a b" when 'tw' is 2
- if (curwin->w_cursor.col <= leader_len) {
- break;
- }
- col = curwin->w_cursor.col;
- dec_cursor();
- cc = gchar_cursor();
-
- if (WHITECHAR(cc)) {
- continue; // one-letter, continue
- }
- curwin->w_cursor.col = col;
- }
-
- inc_cursor();
-
- end_foundcol = end_col + 1;
- foundcol = curwin->w_cursor.col;
- if (curwin->w_cursor.col <= (colnr_T)wantcol) {
- break;
- }
- } else if ((cc >= 0x100 || !utf_allow_break_before(cc)) && fo_multibyte) {
- int ncc;
- bool allow_break;
-
- // Break after or before a multi-byte character.
- if (curwin->w_cursor.col != startcol) {
- // Don't break until after the comment leader
- if (curwin->w_cursor.col < leader_len) {
- break;
- }
- col = curwin->w_cursor.col;
- inc_cursor();
- ncc = gchar_cursor();
- allow_break = utf_allow_break(cc, ncc);
-
- // If we have already checked this position, skip!
- if (curwin->w_cursor.col != skip_pos && allow_break) {
- foundcol = curwin->w_cursor.col;
- end_foundcol = foundcol;
- if (curwin->w_cursor.col <= (colnr_T)wantcol) {
- break;
- }
- }
- curwin->w_cursor.col = col;
- }
-
- if (curwin->w_cursor.col == 0) {
- break;
- }
-
- ncc = cc;
- col = curwin->w_cursor.col;
-
- dec_cursor();
- cc = gchar_cursor();
-
- if (WHITECHAR(cc)) {
- continue; // break with space
- }
- // Don't break until after the comment leader.
- if (curwin->w_cursor.col < leader_len) {
- break;
- }
-
- curwin->w_cursor.col = col;
- skip_pos = curwin->w_cursor.col;
-
- allow_break = utf_allow_break(cc, ncc);
-
- // Must handle this to respect line break prohibition.
- if (allow_break) {
- foundcol = curwin->w_cursor.col;
- end_foundcol = foundcol;
- }
- if (curwin->w_cursor.col <= (colnr_T)wantcol) {
- const bool ncc_allow_break = utf_allow_break_before(ncc);
-
- if (allow_break) {
- break;
- }
- if (!ncc_allow_break && !fo_rigor_tw) {
- // Enable at most 1 punct hang outside of textwidth.
- if (curwin->w_cursor.col == startcol) {
- // We are inserting a non-breakable char, postpone
- // line break check to next insert.
- end_foundcol = foundcol = 0;
- break;
- }
-
- // Neither cc nor ncc is NUL if we are here, so
- // it's safe to inc_cursor.
- col = curwin->w_cursor.col;
-
- inc_cursor();
- cc = ncc;
- ncc = gchar_cursor();
- // handle insert
- ncc = (ncc != NUL) ? ncc : c;
-
- allow_break = utf_allow_break(cc, ncc);
-
- if (allow_break) {
- // Break only when we are not at end of line.
- end_foundcol = foundcol = ncc == NUL? 0 : curwin->w_cursor.col;
- break;
- }
- curwin->w_cursor.col = col;
- }
- }
- }
- if (curwin->w_cursor.col == 0) {
- break;
- }
- dec_cursor();
- }
-
- if (foundcol == 0) { // no spaces, cannot break line
- curwin->w_cursor.col = startcol;
- break;
- }
-
- // Going to break the line, remove any "$" now.
- undisplay_dollar();
-
- // 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 {
- replace_offset = startcol - end_foundcol;
- }
-
- /*
- * adjust startcol for spaces that will be deleted and
- * characters that will remain on top line
- */
- curwin->w_cursor.col = foundcol;
- while ((cc = gchar_cursor(), WHITECHAR(cc))
- && (!fo_white_par || curwin->w_cursor.col < startcol)) {
- inc_cursor();
- }
- startcol -= curwin->w_cursor.col;
- if (startcol < 0) {
- startcol = 0;
- }
-
- if (State & VREPLACE_FLAG) {
- // 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;
-
- // Backspace over characters that will move to the next line
- if (!fo_white_par) {
- backspace_until_column(foundcol);
- }
- } else {
- // put cursor after pos. to break line
- if (!fo_white_par) {
- curwin->w_cursor.col = foundcol;
- }
- }
-
- /*
- * Split the line just before the margin.
- * Only insert/delete lines, but don't really redraw the window.
- */
- 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),
- &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)) {
- // This section is for auto-wrap of numeric lists. When not
- // in insert mode (i.e. format_lines()), the INSCHAR_COM_LIST
- // flag will be set and open_line() will handle it (as seen
- // above). The code here (and in get_number_indent()) will
- // recognize comments if needed...
- if (second_indent < 0 && has_format_option(FO_Q_NUMBER)) {
- second_indent = get_number_indent(curwin->w_cursor.lnum - 1);
- }
- if (second_indent >= 0) {
- if (State & VREPLACE_FLAG) {
- change_indent(INDENT_SET, second_indent, false, NUL, true);
- } else if (leader_len > 0 && second_indent - leader_len > 0) {
- int padding = second_indent - leader_len;
-
- // We started at the first_line of a numbered list
- // that has a comment. the open_line() function has
- // inserted the proper comment leader and positioned
- // the cursor at the end of the split line. Now we
- // add the additional whitespace needed after the
- // comment leader for the numbered list.
- for (int i = 0; i < padding; i++) {
- ins_str((char_u *)" ");
- }
- changed_bytes(curwin->w_cursor.lnum, leader_len);
- } else {
- (void)set_indent(second_indent, SIN_CHANGED);
- }
- }
- }
- first_line = false;
- }
-
- if (State & VREPLACE_FLAG) {
- // 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 {
- /*
- * Check if cursor is not past the NUL off the line, cindent
- * may have added or removed indent.
- */
- curwin->w_cursor.col += startcol;
- len = (colnr_T)STRLEN(get_cursor_line_ptr());
- if (curwin->w_cursor.col > len) {
- curwin->w_cursor.col = len;
- }
- }
-
- haveto_redraw = true;
- can_cindent = true;
- // moved the cursor, don't autoindent or cindent now
- did_ai = false;
- did_si = false;
- can_si = false;
- can_si_back = false;
- line_breakcheck();
- }
-
- if (save_char != NUL) { // put back space after cursor
- pchar_cursor((char_u)save_char);
- }
-
- curwin->w_p_lbr = has_lbr;
-
- if (!format_only && haveto_redraw) {
- update_topline(curwin);
- redraw_curbuf_later(VALID);
- }
-}
-
-/// Called after inserting or deleting text: When 'formatoptions' includes the
-/// 'a' flag format from the current line until the end of the paragraph.
-/// Keep the cursor at the same position relative to the text.
-/// The caller must have saved the cursor line for undo, following ones will be
-/// saved here.
-///
-/// @param trailblank when true also format with trailing blank
-/// @param prev_line may start in previous line
-void auto_format(bool trailblank, bool prev_line)
-{
- pos_T pos;
- colnr_T len;
- char_u *old;
- char_u *new, *pnew;
- int wasatend;
- int cc;
-
- if (!has_format_option(FO_AUTO)) {
- return;
- }
-
- pos = curwin->w_cursor;
- old = get_cursor_line_ptr();
-
- // may remove added space
- check_auto_format(false);
-
- // Don't format in Insert mode when the cursor is on a trailing blank, the
- // user might insert normal text next. Also skip formatting when "1" is
- // in 'formatoptions' and there is a single character before the cursor.
- // Otherwise the line would be broken and when typing another non-white
- // next they are not joined back together.
- wasatend = (pos.col == (colnr_T)STRLEN(old));
- if (*old != NUL && !trailblank && wasatend) {
- dec_cursor();
- cc = gchar_cursor();
- if (!WHITECHAR(cc) && curwin->w_cursor.col > 0
- && has_format_option(FO_ONE_LETTER)) {
- dec_cursor();
- }
- cc = gchar_cursor();
- if (WHITECHAR(cc)) {
- curwin->w_cursor = pos;
- return;
- }
- curwin->w_cursor = pos;
- }
-
- // 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((char *)old, NULL, false, true) == 0) {
- return;
- }
-
- /*
- * May start formatting in a previous line, so that after "x" a word is
- * moved to the previous line if it fits there now. Only when this is not
- * the start of a paragraph.
- */
- if (prev_line && !paragraph_start(curwin->w_cursor.lnum)) {
- --curwin->w_cursor.lnum;
- if (u_save_cursor() == FAIL) {
- return;
- }
- }
-
- /*
- * Do the formatting and restore the cursor position. "saved_cursor" will
- * be adjusted for the text formatting.
- */
- saved_cursor = pos;
- format_lines((linenr_T) - 1, false);
- curwin->w_cursor = saved_cursor;
- saved_cursor.lnum = 0;
-
- if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
- // "cannot happen"
- curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
- coladvance(MAXCOL);
- } else {
- check_cursor_col();
- }
-
- // Insert mode: If the cursor is now after the end of the line while it
- // previously wasn't, the line was broken. Because of the rule above we
- // need to add a space when 'w' is in 'formatoptions' to keep a paragraph
- // formatted.
- if (!wasatend && has_format_option(FO_WHITE_PAR)) {
- new = get_cursor_line_ptr();
- len = (colnr_T)STRLEN(new);
- if (curwin->w_cursor.col == len) {
- pnew = vim_strnsave(new, (size_t)len + 2);
- pnew[len] = ' ';
- pnew[len + 1] = NUL;
- ml_replace(curwin->w_cursor.lnum, (char *)pnew, false);
- // remove the space later
- did_add_space = true;
- } else {
- // may remove added space
- check_auto_format(false);
- }
- }
-
- check_cursor();
-}
-
-/// When an extra space was added to continue a paragraph for auto-formatting,
-/// delete it now. The space must be under the cursor, just after the insert
-/// position.
-///
-/// @param end_insert true when ending Insert mode
-static void check_auto_format(bool end_insert)
-{
- int c = ' ';
- int cc;
-
- if (did_add_space) {
- cc = gchar_cursor();
- if (!WHITECHAR(cc)) {
- // Somehow the space was removed already.
- did_add_space = false;
- } else {
- if (!end_insert) {
- inc_cursor();
- c = gchar_cursor();
- dec_cursor();
- }
- if (c != NUL) {
- // The space is no longer at the end of the line, delete it.
- del_char(false);
- did_add_space = false;
- }
- }
- }
-}
-
-/// Find out textwidth to be used for formatting:
-/// if 'textwidth' option is set, use it
-/// else if 'wrapmargin' option is set, use curwin->w_width_inner-'wrapmargin'
-/// if invalid value, use 0.
-/// Set default to window width (maximum 79) for "gq" operator.
-///
-/// @param ff force formatting (for "gq" command)
-int comp_textwidth(bool ff)
-{
- 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 - (int)curbuf->b_p_wm;
- if (cmdwin_type != 0) {
- textwidth -= 1;
- }
- textwidth -= win_fdccol_count(curwin);
- textwidth -= win_signcol_count(curwin);
-
- if (curwin->w_p_nu || curwin->w_p_rnu) {
- textwidth -= 8;
- }
- }
- if (textwidth < 0) {
- textwidth = 0;
- }
- if (ff && textwidth == 0) {
- textwidth = curwin->w_width_inner - 1;
- if (textwidth > 79) {
- textwidth = 79;
- }
- }
- return textwidth;
-}
-
-/*
- * Put a character in the redo buffer, for when just after a CTRL-V.
- */
+// Put a character in the redo buffer, for when just after a CTRL-V.
static void redo_literal(int c)
{
char buf[10];
@@ -2849,10 +2236,8 @@ static void start_arrow_common(pos_T *end_insert_pos, bool end_change)
check_spell_redraw();
}
-/*
- * If we skipped highlighting word at cursor, do it now.
- * It may be skipped again, thus reset spell_redraw_lnum first.
- */
+// If we skipped highlighting word at cursor, do it now.
+// It may be skipped again, thus reset spell_redraw_lnum first.
static void check_spell_redraw(void)
{
if (spell_redraw_lnum != 0) {
@@ -2863,11 +2248,9 @@ static void check_spell_redraw(void)
}
}
-/*
- * stop_arrow() is called before a change is made in insert mode.
- * If an arrow key has been used, start a new insertion.
- * Returns FAIL if undo is impossible, shouldn't insert then.
- */
+// stop_arrow() is called before a change is made in insert mode.
+// If an arrow key has been used, start a new insertion.
+// Returns FAIL if undo is impossible, shouldn't insert then.
int stop_arrow(void)
{
if (arrow_used) {
@@ -2912,19 +2295,17 @@ int stop_arrow(void)
static void stop_insert(pos_T *end_insert_pos, int esc, int nomove)
{
int cc;
- char_u *ptr;
+ char *ptr;
stop_redo_ins();
replace_flush(); // abandon replace stack
- /*
- * Save the inserted text for later redo with ^@ and CTRL-A.
- * Don't do it when "restart_edit" was set and nothing was inserted,
- * otherwise CTRL-O w and then <Left> will clear "last_insert".
- */
+ // Save the inserted text for later redo with ^@ and CTRL-A.
+ // Don't do it when "restart_edit" was set and nothing was inserted,
+ // otherwise CTRL-O w and then <Left> will clear "last_insert".
ptr = get_inserted();
if (did_restart_edit == 0 || (ptr != NULL
- && (int)STRLEN(ptr) > new_insert_skip)) {
+ && (int)strlen(ptr) > new_insert_skip)) {
xfree(last_insert);
last_insert = ptr;
last_insert_skip = new_insert_skip;
@@ -2958,8 +2339,8 @@ static void stop_insert(pos_T *end_insert_pos, int esc, int nomove)
if (gchar_cursor() != NUL) {
inc_cursor();
}
- /* If the cursor is still at the same character, also keep
- * the "coladd". */
+ // If the cursor is still at the same character, also keep
+ // the "coladd".
if (gchar_cursor() == NUL
&& curwin->w_cursor.lnum == tpos.lnum
&& curwin->w_cursor.col == tpos.col) {
@@ -2986,7 +2367,7 @@ static void stop_insert(pos_T *end_insert_pos, int esc, int nomove)
check_cursor_col(); // make sure it is not past the line
for (;;) {
if (gchar_cursor() == NUL && curwin->w_cursor.col > 0) {
- --curwin->w_cursor.col;
+ curwin->w_cursor.col--;
}
cc = gchar_cursor();
if (!ascii_iswhite(cc)) {
@@ -3003,7 +2384,7 @@ static void stop_insert(pos_T *end_insert_pos, int esc, int nomove)
tpos = curwin->w_cursor;
tpos.col++;
if (cc != NUL && gchar_pos(&tpos) == NUL) {
- ++curwin->w_cursor.col; // put cursor back on the NUL
+ curwin->w_cursor.col++; // put cursor back on the NUL
}
}
@@ -3028,13 +2409,11 @@ static void stop_insert(pos_T *end_insert_pos, int esc, int nomove)
}
}
-/*
- * Set the last inserted text to a single character.
- * Used for the replace command.
- */
+// Set the last inserted text to a single character.
+// Used for the replace command.
void set_last_insert(int c)
{
- char_u *s;
+ char *s;
xfree(last_insert);
last_insert = xmalloc(MB_MAXBYTES * 3 + 5);
@@ -3043,7 +2422,7 @@ void set_last_insert(int c)
if (c < ' ' || c == DEL) {
*s++ = Ctrl_V;
}
- s = add_char2buf(c, s);
+ s = (char *)add_char2buf(c, (char_u *)s);
*s++ = ESC;
*s++ = NUL;
last_insert_skip = 0;
@@ -3056,13 +2435,11 @@ void free_last_insert(void)
}
#endif
-/*
- * move cursor to start of line
- * if flags & BL_WHITE move to first non-white
- * if flags & BL_SOL move to first non-white if startofline is set,
- * otherwise keep "curswant" column
- * if flags & BL_FIX don't leave the cursor on a NUL.
- */
+// move cursor to start of line
+// if flags & BL_WHITE move to first non-white
+// if flags & BL_SOL move to first non-white if startofline is set,
+// otherwise keep "curswant" column
+// if flags & BL_FIX don't leave the cursor on a NUL.
void beginline(int flags)
{
if ((flags & BL_SOL) && !p_sol) {
@@ -3074,22 +2451,20 @@ void beginline(int flags)
if (flags & (BL_WHITE | BL_SOL)) {
char_u *ptr;
- for (ptr = get_cursor_line_ptr(); ascii_iswhite(*ptr)
- && !((flags & BL_FIX) && ptr[1] == NUL); ++ptr) {
- ++curwin->w_cursor.col;
+ for (ptr = (char_u *)get_cursor_line_ptr(); ascii_iswhite(*ptr)
+ && !((flags & BL_FIX) && ptr[1] == NUL); ptr++) {
+ curwin->w_cursor.col++;
}
}
- curwin->w_set_curswant = TRUE;
+ curwin->w_set_curswant = true;
}
}
-/*
- * oneright oneleft cursor_down cursor_up
- *
- * Move one char {right,left,down,up}.
- * Doesn't move onto the NUL past the end of the line, unless it is allowed.
- * Return OK when successful, FAIL when we hit a line of file boundary.
- */
+// oneright oneleft cursor_down cursor_up
+//
+// Move one char {right,left,down,up}.
+// Doesn't move onto the NUL past the end of the line, unless it is allowed.
+// Return OK when successful, FAIL when we hit a line of file boundary.
int oneright(void)
{
@@ -3100,7 +2475,7 @@ int oneright(void)
pos_T prevpos = curwin->w_cursor;
// Adjust for multi-wide char (excluding TAB)
- ptr = (char *)get_cursor_pos_ptr();
+ ptr = get_cursor_pos_ptr();
coladvance(getviscol() + ((*ptr != TAB && vim_isprintc(utf_ptr2char(ptr))) ?
ptr2cells(ptr) : 1));
curwin->w_set_curswant = true;
@@ -3109,7 +2484,7 @@ int oneright(void)
|| prevpos.coladd != curwin->w_cursor.coladd) ? OK : FAIL;
}
- ptr = (char *)get_cursor_pos_ptr();
+ ptr = get_cursor_pos_ptr();
if (*ptr == NUL) {
return FAIL; // already at the very end
}
@@ -3123,7 +2498,7 @@ int oneright(void)
}
curwin->w_cursor.col += l;
- curwin->w_set_curswant = TRUE;
+ curwin->w_set_curswant = true;
return OK;
}
@@ -3152,13 +2527,13 @@ int oneleft(void)
if (curwin->w_cursor.coladd == 1) {
// Adjust for multi-wide char (not a TAB)
- char *ptr = (char *)get_cursor_pos_ptr();
+ char *ptr = get_cursor_pos_ptr();
if (*ptr != TAB && vim_isprintc(utf_ptr2char(ptr)) && ptr2cells(ptr) > 1) {
curwin->w_cursor.coladd = 0;
}
}
- curwin->w_set_curswant = TRUE;
+ curwin->w_set_curswant = true;
return OK;
}
@@ -3166,8 +2541,8 @@ int oneleft(void)
return FAIL;
}
- curwin->w_set_curswant = TRUE;
- --curwin->w_cursor.col;
+ curwin->w_set_curswant = true;
+ curwin->w_cursor.col--;
// if the character on the left of the current cursor is a multi-byte
// character, move to its first byte
@@ -3175,98 +2550,112 @@ int oneleft(void)
return OK;
}
-/// @oaram upd_topline When TRUE: update topline
-int cursor_up(long n, int upd_topline)
+/// Move the cursor up "n" lines in window "wp".
+/// Takes care of closed folds.
+/// Returns the new cursor line or zero for failure.
+linenr_T cursor_up_inner(win_T *wp, long n)
{
- linenr_T lnum;
+ linenr_T lnum = wp->w_cursor.lnum;
- if (n > 0) {
- lnum = curwin->w_cursor.lnum;
+ // This fails if the cursor is already in the first line.
+ if (lnum <= 1) {
+ return 0;
+ }
+ if (n >= lnum) {
+ lnum = 1;
+ } else if (hasAnyFolding(wp)) {
+ // Count each sequence of folded lines as one logical line.
- // This fails if the cursor is already in the first line.
- if (lnum <= 1) {
- return FAIL;
- }
- if (n >= lnum) {
- lnum = 1;
- } else if (hasAnyFolding(curwin)) {
- /*
- * Count each sequence of folded lines as one logical line.
- */
- // go to the start of the current fold
- (void)hasFolding(lnum, &lnum, NULL);
-
- while (n--) {
- // move up one line
- lnum--;
- if (lnum <= 1) {
- break;
- }
- // 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 & MODE_INSERT) || (fdo_flags & FDO_ALL))) {
- (void)hasFolding(lnum, &lnum, NULL);
- }
+ // go to the start of the current fold
+ (void)hasFoldingWin(wp, lnum, &lnum, NULL, true, NULL);
+
+ while (n--) {
+ // move up one line
+ lnum--;
+ if (lnum <= 1) {
+ break;
}
- if (lnum < 1) {
- lnum = 1;
+ // 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 & MODE_INSERT) || (fdo_flags & FDO_ALL))) {
+ (void)hasFoldingWin(wp, lnum, &lnum, NULL, true, NULL);
}
- } else {
- lnum -= (linenr_T)n;
}
- curwin->w_cursor.lnum = lnum;
+ if (lnum < 1) {
+ lnum = 1;
+ }
+ } else {
+ lnum -= (linenr_T)n;
+ }
+
+ wp->w_cursor.lnum = lnum;
+ return lnum;
+}
+
+/// @param upd_topline When true: update topline
+int cursor_up(long n, int upd_topline)
+{
+ if (n > 0 && cursor_up_inner(curwin, n) == 0) {
+ return FAIL;
}
// try to advance to the column we want to be at
coladvance(curwin->w_curswant);
if (upd_topline) {
- update_topline(curwin); // make sure curwin->w_topline is valid
+ update_topline(curwin); // make sure curwin->w_topline is valid
}
return OK;
}
-/// Cursor down a number of logical lines.
-///
-/// @param upd_topline When TRUE: update topline
-int cursor_down(long n, int upd_topline)
+/// Move the cursor down "n" lines in window "wp".
+/// Takes care of closed folds.
+/// Returns the new cursor line or zero for failure.
+linenr_T cursor_down_inner(win_T *wp, long n)
{
- linenr_T lnum;
-
- if (n > 0) {
- lnum = curwin->w_cursor.lnum;
- // Move to last line of fold, will fail if it's the end-of-file.
- (void)hasFolding(lnum, NULL, &lnum);
+ linenr_T lnum = wp->w_cursor.lnum;
+ linenr_T line_count = wp->w_buffer->b_ml.ml_line_count;
- // This fails if the cursor is already in the last line.
- if (lnum >= curbuf->b_ml.ml_line_count) {
- return FAIL;
- }
- if (lnum + n >= curbuf->b_ml.ml_line_count) {
- lnum = curbuf->b_ml.ml_line_count;
- } else if (hasAnyFolding(curwin)) {
- linenr_T last;
+ // Move to last line of fold, will fail if it's the end-of-file.
+ (void)hasFoldingWin(wp, lnum, NULL, &lnum, true, NULL);
+ // This fails if the cursor is already in the last line.
+ if (lnum >= line_count) {
+ return FAIL;
+ }
+ if (lnum + n >= line_count) {
+ lnum = line_count;
+ } else if (hasAnyFolding(wp)) {
+ linenr_T last;
- // count each sequence of folded lines as one logical line
- while (n--) {
- if (hasFolding(lnum, NULL, &last)) {
- lnum = last + 1;
- } else {
- lnum++;
- }
- if (lnum >= curbuf->b_ml.ml_line_count) {
- break;
- }
+ // count each sequence of folded lines as one logical line
+ while (n--) {
+ if (hasFoldingWin(wp, lnum, NULL, &last, true, NULL)) {
+ lnum = last + 1;
+ } else {
+ lnum++;
}
- if (lnum > curbuf->b_ml.ml_line_count) {
- lnum = curbuf->b_ml.ml_line_count;
+ if (lnum >= line_count) {
+ break;
}
- } else {
- lnum += (linenr_T)n;
}
- curwin->w_cursor.lnum = lnum;
+ if (lnum > line_count) {
+ lnum = line_count;
+ }
+ } else {
+ lnum += (linenr_T)n;
+ }
+
+ wp->w_cursor.lnum = lnum;
+ return lnum;
+}
+
+/// @param upd_topline When true: update topline
+int cursor_down(long n, int upd_topline)
+{
+ if (n > 0 && cursor_down_inner(curwin, n) == 0) {
+ return FAIL;
}
// try to advance to the column we want to be at
@@ -3288,12 +2677,12 @@ int cursor_down(long n, int upd_topline)
/// @param no_esc Don't add an ESC at the end
int stuff_inserted(int c, long count, int no_esc)
{
- char_u *esc_ptr;
- char_u *ptr;
- char_u *last_ptr;
- char_u last = NUL;
+ char *esc_ptr;
+ char *ptr;
+ char *last_ptr;
+ char last = NUL;
- ptr = get_last_insert();
+ ptr = (char *)get_last_insert();
if (ptr == NULL) {
emsg(_(e_noinstext));
return FAIL;
@@ -3303,7 +2692,7 @@ int stuff_inserted(int c, long count, int no_esc)
if (c != NUL) {
stuffcharReadbuff(c);
}
- if ((esc_ptr = STRRCHR(ptr, ESC)) != NULL) {
+ if ((esc_ptr = strrchr(ptr, ESC)) != NULL) {
// remove the ESC.
*esc_ptr = NUL;
}
@@ -3311,7 +2700,7 @@ int stuff_inserted(int c, long count, int no_esc)
// when the last char is either "0" or "^" it will be quoted if no ESC
// comes after it OR if it will inserted more than once and "ptr"
// starts with ^D. -- Acevedo
- last_ptr = (esc_ptr ? esc_ptr : ptr + STRLEN(ptr)) - 1;
+ last_ptr = (esc_ptr ? esc_ptr : ptr + strlen(ptr)) - 1;
if (last_ptr >= ptr && (*last_ptr == '0' || *last_ptr == '^')
&& (no_esc || (*ptr == Ctrl_D && count > 1))) {
last = *last_ptr;
@@ -3348,23 +2737,21 @@ char_u *get_last_insert(void)
if (last_insert == NULL) {
return NULL;
}
- return last_insert + last_insert_skip;
+ return (char_u *)last_insert + last_insert_skip;
}
-/*
- * Get last inserted string, and remove trailing <Esc>.
- * Returns pointer to allocated memory (must be freed) or NULL.
- */
-char_u *get_last_insert_save(void)
+// Get last inserted string, and remove trailing <Esc>.
+// Returns pointer to allocated memory (must be freed) or NULL.
+char *get_last_insert_save(void)
{
- char_u *s;
+ char *s;
int len;
if (last_insert == NULL) {
return NULL;
}
- s = vim_strsave(last_insert + last_insert_skip);
- len = (int)STRLEN(s);
+ s = xstrdup(last_insert + last_insert_skip);
+ len = (int)strlen(s);
if (len > 0 && s[len - 1] == ESC) { // remove trailing ESC
s[len - 1] = NUL;
}
@@ -3393,20 +2780,18 @@ static bool echeck_abbr(int c)
curwin->w_cursor.lnum == Insstart.lnum ? Insstart.col : 0);
}
-/*
- * replace-stack functions
- *
- * When replacing characters, the replaced characters are remembered for each
- * new character. This is used to re-insert the old text when backspacing.
- *
- * There is a NUL headed list of characters for each character that is
- * currently in the file after the insertion point. When BS is used, one NUL
- * headed list is put back for the deleted character.
- *
- * For a newline, there are two NUL headed lists. One contains the characters
- * that the NL replaced. The extra one stores the characters after the cursor
- * that were deleted (always white space).
- */
+// replace-stack functions
+//
+// When replacing characters, the replaced characters are remembered for each
+// new character. This is used to re-insert the old text when backspacing.
+//
+// There is a NUL headed list of characters for each character that is
+// currently in the file after the insertion point. When BS is used, one NUL
+// headed list is put back for the deleted character.
+//
+// For a newline, there are two NUL headed lists. One contains the characters
+// that the NL replaced. The extra one stores the characters after the cursor
+// that were deleted (always white space).
static char_u *replace_stack = NULL;
static ssize_t replace_stack_nr = 0; // next entry in replace stack
@@ -3437,17 +2822,16 @@ void replace_push(int c)
replace_stack_nr++;
}
-/*
- * Push a character onto the replace stack. Handles a multi-byte character in
- * reverse byte order, so that the first byte is popped off first.
- * Return the number of bytes done (includes composing characters).
- */
-int replace_push_mb(char_u *p)
+/// Push a character onto the replace stack. Handles a multi-byte character in
+/// reverse byte order, so that the first byte is popped off first.
+///
+/// @return the number of bytes done (includes composing characters).
+int replace_push_mb(char *p)
{
- int l = utfc_ptr2len((char *)p);
+ int l = utfc_ptr2len(p);
int j;
- for (j = l - 1; j >= 0; --j) {
+ for (j = l - 1; j >= 0; j--) {
replace_push(p[j]);
}
return l;
@@ -3492,10 +2876,8 @@ static void replace_pop_ins(void)
State = oldState;
}
-/*
- * Insert bytes popped from the replace stack. "cc" is the first byte. If it
- * indicates a multi-byte char, pop the other bytes too.
- */
+// Insert bytes popped from the replace stack. "cc" is the first byte. If it
+// indicates a multi-byte char, pop the other bytes too.
static void mb_replace_pop_ins(int cc)
{
int n;
@@ -3508,7 +2890,7 @@ static void mb_replace_pop_ins(int cc)
for (i = 1; i < n; i++) {
buf[i] = (char_u)replace_pop();
}
- ins_bytes_len(buf, (size_t)n);
+ ins_bytes_len((char *)buf, (size_t)n);
} else {
ins_char(cc);
}
@@ -3531,7 +2913,7 @@ static void mb_replace_pop_ins(int cc)
buf[i] = (char_u)replace_pop();
}
if (utf_iscomposing(utf_ptr2char((char *)buf))) {
- ins_bytes_len(buf, (size_t)n);
+ ins_bytes_len((char *)buf, (size_t)n);
} else {
// Not a composing char, put it back.
for (i = n - 1; i >= 0; i--) {
@@ -3542,10 +2924,8 @@ static void mb_replace_pop_ins(int cc)
}
}
-/*
- * make the replace stack empty
- * (called when exiting replace mode)
- */
+// make the replace stack empty
+// (called when exiting replace mode)
static void replace_flush(void)
{
XFREE_CLEAR(replace_stack);
@@ -3553,15 +2933,13 @@ static void replace_flush(void)
replace_stack_nr = 0;
}
-/*
- * Handle doing a BS for one character.
- * cc < 0: replace stack empty, just move cursor
- * cc == 0: character was inserted, delete it
- * cc > 0: character was replaced, put cc (first byte of original char) back
- * and check for more characters to be put back
- * When "limit_col" is >= 0, don't delete before this column. Matters when
- * using composing characters, use del_char_after_col() instead of del_char().
- */
+// Handle doing a BS for one character.
+// cc < 0: replace stack empty, just move cursor
+// cc == 0: character was inserted, delete it
+// cc > 0: character was replaced, put cc (first byte of original char) back
+// and check for more characters to be put back
+// When "limit_col" is >= 0, don't delete before this column. Matters when
+// using composing characters, use del_char_after_col() instead of del_char().
static void replace_do_bs(int limit_col)
{
int cc;
@@ -3569,7 +2947,7 @@ static void replace_do_bs(int limit_col)
int ins_len;
int orig_vcols = 0;
colnr_T start_vcol;
- char_u *p;
+ char *p;
int i;
int vcol;
const int l_State = State;
@@ -3584,7 +2962,7 @@ static void replace_do_bs(int limit_col)
}
(void)del_char_after_col(limit_col);
if (l_State & VREPLACE_FLAG) {
- orig_len = (int)STRLEN(get_cursor_pos_ptr());
+ orig_len = (int)strlen(get_cursor_pos_ptr());
}
replace_push(cc);
replace_pop_ins();
@@ -3592,11 +2970,11 @@ static void replace_do_bs(int limit_col)
if (l_State & VREPLACE_FLAG) {
// Get the number of screen cells used by the inserted characters
p = get_cursor_pos_ptr();
- ins_len = (int)STRLEN(p) - orig_len;
+ ins_len = (int)strlen(p) - orig_len;
vcol = start_vcol;
for (i = 0; i < ins_len; i++) {
vcol += win_chartabsize(curwin, p + i, vcol);
- i += utfc_ptr2len((char *)p) - 1;
+ i += utfc_ptr2len(p) - 1;
}
vcol -= start_vcol;
@@ -3624,40 +3002,10 @@ bool cindent_on(void)
return !p_paste && (curbuf->b_p_cin || *curbuf->b_p_inde != NUL);
}
-/*
- * Re-indent the current line, based on the current contents of it and the
- * surrounding lines. Fixing the cursor position seems really easy -- I'm very
- * confused what all the part that handles Control-T is doing that I'm not.
- * "get_the_indent" should be get_c_indent, get_expr_indent or get_lisp_indent.
- */
-void fixthisline(IndentGetter get_the_indent)
-{
- int amount = get_the_indent();
-
- if (amount >= 0) {
- change_indent(INDENT_SET, amount, false, 0, true);
- if (linewhite(curwin->w_cursor.lnum)) {
- did_ai = true; // delete the indent if the line stays empty
- }
- }
-}
-
-void fix_indent(void)
-{
- if (p_paste) {
- return;
- }
- if (curbuf->b_p_lisp && curbuf->b_p_ai) {
- fixthisline(get_lisp_indent);
- } else if (cindent_on()) {
- do_c_expr_indent();
- }
-}
-
/// Check that "cinkeys" contains the key "keytyped",
/// when == '*': Only if key is preceded with '*' (indent before insert)
/// when == '!': Only if key is preceded with '!' (don't insert)
-/// when == ' ': Only if key is not preceded with '*' (indent afterwards)
+/// when == ' ': Only if key is not preceded with '*' or '!' (indent afterwards)
///
/// "keytyped" can have a few special values:
/// KEY_OPEN_FORW :
@@ -3669,11 +3017,11 @@ void fix_indent(void)
/// @param line_is_empty when true, accept keys with '0' before them.
bool in_cinkeys(int keytyped, int when, bool line_is_empty)
{
- char_u *look;
+ char *look;
int try_match;
int try_match_word;
- char_u *p;
- char_u *line;
+ char *p;
+ char *line;
bool icase;
if (keytyped == NUL) {
@@ -3687,17 +3035,15 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty)
look = curbuf->b_p_cink; // 'indentexpr' empty: use 'cinkeys'
}
while (*look) {
- /*
- * Find out if we want to try a match with this key, depending on
- * 'when' and a '*' or '!' before the key.
- */
+ // Find out if we want to try a match with this key, depending on
+ // 'when' and a '*' or '!' before the key.
switch (when) {
case '*':
try_match = (*look == '*'); break;
case '!':
try_match = (*look == '!'); break;
default:
- try_match = (*look != '*'); break;
+ try_match = (*look != '*') && (*look != '!'); break;
}
if (*look == '*' || *look == '!') {
look++;
@@ -3740,8 +3086,8 @@ 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 ((char_u *)skipwhite((char *)p) == p + curwin->w_cursor.col - 4
- && STRNCMP(p + curwin->w_cursor.col - 4, "else", 4) == 0) {
+ if (skipwhite(p) == p + curwin->w_cursor.col - 4
+ && strncmp(p + curwin->w_cursor.col - 4, "else", 4) == 0) {
return true;
}
}
@@ -3780,12 +3126,12 @@ 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("<>!*oOe0:", look[1]) != NULL
+ if (vim_strchr("<>!*oOe0:", (uint8_t)look[1]) != NULL
&& keytyped == look[1]) {
return true;
}
- if (keytyped == get_special_key_code(look + 1)) {
+ if (keytyped == get_special_key_code((char_u *)look + 1)) {
return true;
}
}
@@ -3804,19 +3150,19 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty)
} else {
icase = false;
}
- p = (char_u *)vim_strchr((char *)look, ',');
+ p = vim_strchr(look, ',');
if (p == NULL) {
- p = look + STRLEN(look);
+ p = look + strlen(look);
}
if ((try_match || try_match_word)
&& curwin->w_cursor.col >= (colnr_T)(p - look)) {
bool match = false;
if (keytyped == KEY_COMPLETE) {
- char_u *n, *s;
+ char *n, *s;
- /* Just completed a word, check if it starts with "look".
- * search back for the start of a word. */
+ // Just completed a word, check if it starts with "look".
+ // search back for the start of a word.
line = get_cursor_line_ptr();
for (s = line + curwin->w_cursor.col; s > line; s = n) {
n = mb_prevptr(line, s);
@@ -3828,28 +3174,28 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty)
if (s + (p - look) <= line + curwin->w_cursor.col
&& (icase
? mb_strnicmp(s, look, (size_t)(p - look))
- : STRNCMP(s, look, p - look)) == 0) {
+ : strncmp(s, look, (size_t)(p - look))) == 0) {
match = true;
}
} else {
// TODO(@brammool): multi-byte
- if (keytyped == (int)p[-1]
+ if (keytyped == (int)(uint8_t)p[-1]
|| (icase && keytyped < 256
- && TOLOWER_LOC(keytyped) == TOLOWER_LOC((int)p[-1]))) {
+ && TOLOWER_LOC(keytyped) == TOLOWER_LOC((uint8_t)p[-1]))) {
line = get_cursor_pos_ptr();
assert(p >= look && (uintmax_t)(p - look) <= SIZE_MAX);
if ((curwin->w_cursor.col == (colnr_T)(p - look)
- || !vim_iswordc(line[-(p - look) - 1]))
+ || !vim_iswordc((uint8_t)line[-(p - look) - 1]))
&& (icase
? mb_strnicmp(line - (p - look), look, (size_t)(p - look))
- : STRNCMP(line - (p - look), look, p - look)) == 0) {
+ : strncmp(line - (p - look), look, (size_t)(p - look))) == 0) {
match = true;
}
}
}
if (match && try_match_word && !try_match) {
- /* "0=word": Check if there are only blanks before the
- * word. */
+ // "0=word": Check if there are only blanks before the
+ // word.
if (getwhitecols_curline() !=
(int)(curwin->w_cursor.col - (p - look))) {
match = false;
@@ -3863,7 +3209,7 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty)
// Ok, it's a boring generic character.
} else {
- if (try_match && *look == keytyped) {
+ if (try_match && (uint8_t)(*look) == keytyped) {
return true;
}
if (*look != NUL) {
@@ -3871,17 +3217,13 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty)
}
}
- /*
- * Skip over ", ".
- */
+ // Skip over ", ".
look = skip_to_option_part(look);
}
return false;
}
-/*
- * Map Hebrew keyboard when in hkmap mode.
- */
+// Map Hebrew keyboard when in hkmap mode.
int hkmap(int c)
FUNC_ATTR_PURE
{
@@ -3983,9 +3325,7 @@ static void ins_reg(void)
int literally = 0;
int vis_active = VIsual_active;
- /*
- * If we are going to wait for a character, show a '"'.
- */
+ // If we are going to wait for a character, show a '"'.
pc_status = PC_STATUS_UNSET;
if (redrawing() && !char_avail()) {
// may need to redraw when no more chars available now
@@ -4000,13 +3340,13 @@ static void ins_reg(void)
no_mapping++;
allow_keys++;
regname = plain_vgetc();
- LANGMAP_ADJUST(regname, TRUE);
+ LANGMAP_ADJUST(regname, true);
if (regname == Ctrl_R || regname == Ctrl_O || regname == Ctrl_P) {
// Get a third key for literal register insertion
literally = regname;
add_to_showcmd_c(literally);
regname = plain_vgetc();
- LANGMAP_ADJUST(regname, TRUE);
+ LANGMAP_ADJUST(regname, true);
}
no_mapping--;
allow_keys--;
@@ -4044,7 +3384,7 @@ static void ins_reg(void)
need_redraw = true; // remove the '"'
} else if (stop_insert_mode) {
// When the '=' register was used and a function was invoked that
- // did ":stopinsert" then stuff_empty() returns FALSE but we won't
+ // did ":stopinsert" then stuff_empty() returns false but we won't
// insert anything, need to remove the '"'
need_redraw = true;
}
@@ -4067,9 +3407,7 @@ static void ins_reg(void)
}
}
-/*
- * CTRL-G commands in Insert mode.
- */
+// CTRL-G commands in Insert mode.
static void ins_ctrl_g(void)
{
int c;
@@ -4123,9 +3461,7 @@ static void ins_ctrl_g(void)
}
}
-/*
- * CTRL-^ in Insert mode.
- */
+// CTRL-^ in Insert mode.
static void ins_ctrl_hat(void)
{
if (map_to_exists_mode("", MODE_LANGMAP, false)) {
@@ -4138,7 +3474,7 @@ static void ins_ctrl_hat(void)
State |= MODE_LANGMAP;
}
}
- set_iminsert_global();
+ set_iminsert_global(curbuf);
showmode();
// Show/unshow value of 'keymap' in status lines.
status_redraw_curbuf();
@@ -4169,10 +3505,8 @@ static bool ins_esc(long *count, int cmdchar, bool nomove)
AppendToRedobuff(ESC_STR);
}
- /*
- * Repeating insert may take a long time. Check for
- * interrupt now and then.
- */
+ // Repeating insert may take a long time. Check for
+ // interrupt now and then.
if (*count > 0) {
line_breakcheck();
if (got_int) {
@@ -4195,7 +3529,7 @@ static bool ins_esc(long *count, int cmdchar, bool nomove)
// Repeat the insert
return false;
}
- stop_insert(&curwin->w_cursor, TRUE, nomove);
+ stop_insert(&curwin->w_cursor, true, nomove);
undisplay_dollar();
}
@@ -4215,10 +3549,8 @@ static bool ins_esc(long *count, int cmdchar, bool nomove)
RESET_FMARK(&curbuf->b_last_insert, curwin->w_cursor, curbuf->b_fnum, view);
}
- /*
- * The cursor should end up on the last inserted character.
- * Don't do it for CTRL-O, unless past the end of the line.
- */
+ // The cursor should end up on the last inserted character.
+ // 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))
@@ -4256,15 +3588,13 @@ static bool ins_esc(long *count, int cmdchar, bool nomove)
return true;
}
-/*
- * Toggle language: hkmap and revins_on.
- * Move to end of reverse inserted text.
- */
+// Toggle language: hkmap and revins_on.
+// Move to end of reverse inserted text.
static void ins_ctrl_(void)
{
if (revins_on && revins_chars && revins_scol >= 0) {
while (gchar_cursor() != NUL && revins_chars--) {
- ++curwin->w_cursor.col;
+ curwin->w_cursor.col++;
}
}
p_ri = !p_ri;
@@ -4326,14 +3656,11 @@ static bool ins_start_select(int c)
return false;
}
-/*
- * <Insert> key in Insert mode: toggle insert/replace mode.
- */
+// <Insert> key in Insert mode: toggle insert/replace mode.
static void ins_insert(int replaceState)
{
set_vim_var_string(VV_INSERTMODE, ((State & REPLACE_FLAG) ? "i" :
- replaceState == MODE_VREPLACE ? "v" :
- "r"), 1);
+ replaceState == MODE_VREPLACE ? "v" : "r"), 1);
ins_apply_autocmds(EVENT_INSERTCHANGE);
if (State & REPLACE_FLAG) {
State = MODE_INSERT | (State & MODE_LANGMAP);
@@ -4346,9 +3673,7 @@ static void ins_insert(int replaceState)
ui_cursor_shape(); // may show different cursor shape
}
-/*
- * Pressed CTRL-O in Insert mode.
- */
+// Pressed CTRL-O in Insert mode.
static void ins_ctrl_o(void)
{
if (State & VREPLACE_FLAG) {
@@ -4365,13 +3690,11 @@ static void ins_ctrl_o(void)
}
}
-/*
- * If the cursor is on an indent, ^T/^D insert/delete one
- * shiftwidth. Otherwise ^T/^D behave like a "<<" or ">>".
- * Always round the indent to 'shiftwidth', this is compatible
- * with vi. But vi only supports ^T and ^D after an
- * autoindent, we support it everywhere.
- */
+// If the cursor is on an indent, ^T/^D insert/delete one
+// shiftwidth. Otherwise ^T/^D behave like a "<<" or ">>".
+// Always round the indent to 'shiftwidth', this is compatible
+// with vi. But vi only supports ^T and ^D after an
+// autoindent, we support it everywhere.
static void ins_shift(int c, int lastc)
{
if (stop_arrow() == FAIL) {
@@ -4379,9 +3702,7 @@ static void ins_shift(int c, int lastc)
}
AppendCharToRedobuff(c);
- /*
- * 0^D and ^^D: remove all indent.
- */
+ // 0^D and ^^D: remove all indent.
if (c == Ctrl_D && (lastc == '0' || lastc == '^')
&& curwin->w_cursor.col > 0) {
curwin->w_cursor.col--;
@@ -4393,12 +3714,12 @@ static void ins_shift(int c, int lastc)
if (lastc == '^') {
old_indent = get_indent(); // remember curr. indent
}
- change_indent(INDENT_SET, 0, TRUE, 0, TRUE);
+ change_indent(INDENT_SET, 0, true, 0, true);
} else {
- change_indent(c == Ctrl_D ? INDENT_DEC : INDENT_INC, 0, TRUE, 0, TRUE);
+ change_indent(c == Ctrl_D ? INDENT_DEC : INDENT_INC, 0, true, 0, true);
}
- if (did_ai && *skipwhite((char *)get_cursor_line_ptr()) != NUL) {
+ if (did_ai && *skipwhite(get_cursor_line_ptr()) != NUL) {
did_ai = false;
}
did_si = false;
@@ -4437,9 +3758,7 @@ static void ins_del(void)
AppendCharToRedobuff(K_DEL);
}
-/*
- * Delete one character for ins_bs().
- */
+// Delete one character for ins_bs().
static void ins_bs_one(colnr_T *vcolp)
{
dec_cursor();
@@ -4532,13 +3851,11 @@ static bool ins_bs(int c, int mode, int *inserted_space_p)
return false;
}
Insstart.lnum--;
- Insstart.col = (colnr_T)STRLEN(ml_get(Insstart.lnum));
+ Insstart.col = (colnr_T)strlen(ml_get(Insstart.lnum));
}
- /*
- * In replace mode:
- * cc < 0: NL was inserted, delete it
- * cc >= 0: NL was replaced, put original characters back
- */
+ // In replace mode:
+ // cc < 0: NL was inserted, delete it
+ // cc >= 0: NL was replaced, put original characters back
cc = -1;
if (State & REPLACE_FLAG) {
cc = replace_pop(); // returns -1 if NL was inserted
@@ -4558,16 +3875,16 @@ static bool ins_bs(int c, int mode, int *inserted_space_p)
// again when auto-formatting.
if (has_format_option(FO_AUTO)
&& has_format_option(FO_WHITE_PAR)) {
- char_u *ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, true);
+ char *ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, true);
int len;
- len = (int)STRLEN(ptr);
+ len = (int)strlen(ptr);
if (len > 0 && ptr[len - 1] == ' ') {
ptr[len - 1] = NUL;
}
}
- do_join(2, FALSE, FALSE, FALSE, false);
+ do_join(2, false, false, false, false);
if (temp == NUL && gchar_cursor() != NUL) {
inc_cursor();
}
@@ -4618,9 +3935,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p)
curwin->w_cursor.col = save_col;
}
- /*
- * Handle deleting one 'shiftwidth' or 'softtabstop'.
- */
+ // Handle deleting one 'shiftwidth' or 'softtabstop'.
if (mode == BACKSPACE_CHAR
&& ((p_sta && in_indent)
|| ((get_sts_value() != 0
@@ -4654,7 +3969,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 && curwin->w_cursor.col > 0
- && (cc = *(get_cursor_pos_ptr() - 1), ascii_iswhite(cc))) {
+ && (cc = (uint8_t)(*(get_cursor_pos_ptr() - 1)), ascii_iswhite(cc))) {
ins_bs_one(&vcol);
}
@@ -4669,7 +3984,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p)
if (State & VREPLACE_FLAG) {
ins_char(' ');
} else {
- ins_str((char_u *)" ");
+ ins_str(" ");
if ((State & REPLACE_FLAG)) {
replace_push(NUL);
}
@@ -4841,7 +4156,7 @@ static void ins_mousescroll(int dir)
if (dir == MSCR_DOWN || dir == MSCR_UP) {
if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
scroll_redraw(dir, (long)(curwin->w_botline - curwin->w_topline));
- } else {
+ } else if (p_mousescroll_vert > 0) {
scroll_redraw(dir, p_mousescroll_vert);
}
} else {
@@ -4849,7 +4164,7 @@ static void ins_mousescroll(int dir)
}
}
- curwin->w_redr_status = TRUE;
+ curwin->w_redr_status = true;
curwin = old_curwin;
curbuf = curwin->w_buffer;
@@ -4880,7 +4195,7 @@ static void ins_left(void)
revins_legal++;
}
revins_chars++;
- } else if (vim_strchr((char *)p_ww, '[') != NULL && curwin->w_cursor.lnum > 1) {
+ } else if (vim_strchr(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);
@@ -4966,14 +4281,14 @@ static void ins_right(void)
if (virtual_active()) {
oneright();
} else {
- curwin->w_cursor.col += utfc_ptr2len((char *)get_cursor_pos_ptr());
+ curwin->w_cursor.col += utfc_ptr2len(get_cursor_pos_ptr());
}
revins_legal++;
if (revins_chars) {
revins_chars--;
}
- } else if (vim_strchr((char *)p_ww, ']') != NULL
+ } else if (vim_strchr(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
@@ -5017,13 +4332,13 @@ static void ins_up(bool startcol)
undisplay_dollar();
tpos = curwin->w_cursor;
- if (cursor_up(1L, TRUE) == OK) {
+ if (cursor_up(1L, true) == OK) {
if (startcol) {
coladvance(getvcol_nolist(&Insstart));
}
if (old_topline != curwin->w_topline
|| old_topfill != curwin->w_topfill) {
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
}
start_arrow(&tpos);
can_cindent = true;
@@ -5065,13 +4380,13 @@ static void ins_down(bool startcol)
undisplay_dollar();
tpos = curwin->w_cursor;
- if (cursor_down(1L, TRUE) == OK) {
+ if (cursor_down(1L, true) == OK) {
if (startcol) {
coladvance(getvcol_nolist(&Insstart));
}
if (old_topline != curwin->w_topline
|| old_topfill != curwin->w_topfill) {
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
}
start_arrow(&tpos);
can_cindent = true;
@@ -5175,21 +4490,19 @@ static bool ins_tab(void)
if (State & VREPLACE_FLAG) {
ins_char(' ');
} else {
- ins_str((char_u *)" ");
+ ins_str(" ");
if (State & REPLACE_FLAG) { // no char replaced
replace_push(NUL);
}
}
}
- /*
- * When 'expandtab' not set: Replace spaces by TABs where possible.
- */
+ // When 'expandtab' not set: Replace spaces by TABs where possible.
if (!curbuf->b_p_et && (tabstop_count(curbuf->b_p_vsts_array) > 0
|| get_sts_value() > 0
|| (p_sta && ind))) {
- char_u *ptr;
- char_u *saved_line = NULL; // init for GCC
+ char *ptr;
+ char *saved_line = NULL; // init for GCC
pos_T pos;
pos_T fpos;
pos_T *cursor;
@@ -5202,7 +4515,7 @@ static bool ins_tab(void)
if (State & VREPLACE_FLAG) {
pos = curwin->w_cursor;
cursor = &pos;
- saved_line = vim_strsave(get_cursor_line_ptr());
+ saved_line = xstrdup(get_cursor_line_ptr());
ptr = saved_line + pos.col;
} else {
ptr = get_cursor_pos_ptr();
@@ -5233,11 +4546,15 @@ static bool ins_tab(void)
getvcol(curwin, &fpos, &vcol, NULL, NULL);
getvcol(curwin, cursor, &want_vcol, NULL, NULL);
+ char *tab = "\t";
+ chartabsize_T cts;
+ init_chartabsize_arg(&cts, curwin, 0, vcol, tab, tab);
+
// Use as many TABs as possible. Beware of 'breakindent', 'showbreak'
// and 'linebreak' adding extra virtual columns.
while (ascii_iswhite(*ptr)) {
- i = lbr_chartabsize(NULL, (char_u *)"\t", vcol);
- if (vcol + i > want_vcol) {
+ i = lbr_chartabsize(&cts);
+ if (cts.cts_vcol + i > want_vcol) {
break;
}
if (*ptr != TAB) {
@@ -5252,19 +4569,24 @@ static bool ins_tab(void)
}
fpos.col++;
ptr++;
- vcol += i;
+ cts.cts_vcol += i;
}
+ vcol = cts.cts_vcol;
+ clear_chartabsize_arg(&cts);
if (change_col >= 0) {
int repl_off = 0;
- char_u *line = ptr;
-
// Skip over the spaces we need.
- while (vcol < want_vcol && *ptr == ' ') {
- vcol += lbr_chartabsize(line, ptr, vcol);
- ptr++;
+ init_chartabsize_arg(&cts, curwin, 0, vcol, ptr, ptr);
+ while (cts.cts_vcol < want_vcol && *cts.cts_ptr == ' ') {
+ cts.cts_vcol += lbr_chartabsize(&cts);
+ cts.cts_ptr++;
repl_off++;
}
+ ptr = cts.cts_ptr;
+ vcol = cts.cts_vcol;
+ clear_chartabsize_arg(&cts);
+
if (vcol > want_vcol) {
// Must have a char with 'showbreak' just before it.
ptr--;
@@ -5275,7 +4597,7 @@ static bool ins_tab(void)
// Delete following spaces.
i = cursor->col - fpos.col;
if (i > 0) {
- STRMOVE(ptr, ptr + i);
+ STRMOVE(ptr, (char *)ptr + i);
// correct replace stack.
if ((State & REPLACE_FLAG)
&& !(State & VREPLACE_FLAG)) {
@@ -5326,11 +4648,9 @@ bool ins_eol(int c)
}
undisplay_dollar();
- /*
- * Strange Vi behaviour: In Replace mode, typing a NL will not delete the
- * character under the cursor. Only push a NUL on the replace stack,
- * nothing to put back when the NL is deleted.
- */
+ // Strange Vi behaviour: In Replace mode, typing a NL will not delete the
+ // character under the cursor. Only push a NUL on the replace stack,
+ // nothing to put back when the NL is deleted.
if ((State & REPLACE_FLAG)
&& !(State & VREPLACE_FLAG)) {
replace_push(NUL);
@@ -5349,7 +4669,7 @@ bool ins_eol(int c)
// NL in reverse insert will always start in the end of current line.
if (revins_on) {
- curwin->w_cursor.col += (colnr_T)STRLEN(get_cursor_pos_ptr());
+ curwin->w_cursor.col += (colnr_T)strlen(get_cursor_pos_ptr());
}
AppendToRedobuff(NL_STR);
@@ -5364,11 +4684,9 @@ bool ins_eol(int c)
return i;
}
-/*
- * Handle digraph in insert mode.
- * Returns character still to be inserted, or NUL when nothing remaining to be
- * done.
- */
+// Handle digraph in insert mode.
+// Returns character still to be inserted, or NUL when nothing remaining to be
+// done.
static int ins_digraph(void)
{
int c;
@@ -5400,7 +4718,7 @@ static int ins_digraph(void)
if (IS_SPECIAL(c) || mod_mask) { // special key
clear_showcmd();
- insert_special(c, TRUE, FALSE);
+ insert_special(c, true, false);
return NUL;
}
if (c != ESC) {
@@ -5437,16 +4755,13 @@ static int ins_digraph(void)
return NUL;
}
-/*
- * Handle CTRL-E and CTRL-Y in Insert mode: copy char from other line.
- * Returns the char to be inserted, or NUL if none found.
- */
+// Handle CTRL-E and CTRL-Y in Insert mode: copy char from other line.
+// Returns the char to be inserted, or NUL if none found.
int ins_copychar(linenr_T lnum)
{
int c;
- int temp;
- char_u *ptr, *prev_ptr;
- char_u *line;
+ char *ptr, *prev_ptr;
+ char *line;
if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) {
vim_beep(BO_COPY);
@@ -5454,28 +4769,32 @@ int ins_copychar(linenr_T lnum)
}
// try to advance to the cursor column
- temp = 0;
- line = ptr = ml_get(lnum);
- prev_ptr = ptr;
+ line = ml_get(lnum);
+ prev_ptr = line;
validate_virtcol();
- while ((colnr_T)temp < curwin->w_virtcol && *ptr != NUL) {
- prev_ptr = ptr;
- temp += lbr_chartabsize_adv(line, &ptr, (colnr_T)temp);
+
+ chartabsize_T cts;
+ init_chartabsize_arg(&cts, curwin, lnum, 0, line, line);
+ while (cts.cts_vcol < curwin->w_virtcol && *cts.cts_ptr != NUL) {
+ prev_ptr = cts.cts_ptr;
+ cts.cts_vcol += lbr_chartabsize_adv(&cts);
}
- if ((colnr_T)temp > curwin->w_virtcol) {
+
+ if (cts.cts_vcol > curwin->w_virtcol) {
ptr = prev_ptr;
+ } else {
+ ptr = cts.cts_ptr;
}
+ clear_chartabsize_arg(&cts);
- c = utf_ptr2char((char *)ptr);
+ c = utf_ptr2char(ptr);
if (c == NUL) {
vim_beep(BO_COPY);
}
return c;
}
-/*
- * CTRL-Y or CTRL-E typed in Insert mode.
- */
+// CTRL-Y or CTRL-E typed in Insert mode.
static int ins_ctrl_ey(int tc)
{
int c = tc;
@@ -5486,7 +4805,7 @@ static int ins_ctrl_ey(int tc)
} else {
scrollup_clamp();
}
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
} else {
c = ins_copychar(curwin->w_cursor.lnum + (c == Ctrl_Y ? -1 : 1));
if (c != NUL) {
@@ -5501,7 +4820,7 @@ static int ins_ctrl_ey(int tc)
}
tw_save = curbuf->b_p_tw;
curbuf->b_p_tw = -1;
- insert_special(c, TRUE, FALSE);
+ insert_special(c, true, false);
curbuf->b_p_tw = tw_save;
revins_chars++;
revins_legal++;
@@ -5512,31 +4831,25 @@ static int ins_ctrl_ey(int tc)
return c;
}
-/*
- * Try to do some very smart auto-indenting.
- * Used when inserting a "normal" character.
- */
+// Try to do some very smart auto-indenting.
+// Used when inserting a "normal" character.
static void ins_try_si(int c)
{
pos_T *pos, old_pos;
- char_u *ptr;
+ char *ptr;
int i;
bool temp;
- /*
- * do some very smart indenting when entering '{' or '}'
- */
+ // do some very smart indenting when entering '{' or '}'
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;
- /*
- * If the matching '{' has a ')' immediately before it (ignoring
- * white-space), then line up with the start of the line
- * containing the matching '(' if there is one. This handles the
- * case where an "if (..\n..) {" statement continues over multiple
- * lines -- webb
- */
+ // If the matching '{' has a ')' immediately before it (ignoring
+ // white-space), then line up with the start of the line
+ // containing the matching '(' if there is one. This handles the
+ // case where an "if (..\n..) {" statement continues over multiple
+ // lines -- webb
ptr = ml_get(pos->lnum);
i = pos->col;
if (i > 0) { // skip blanks before '{'
@@ -5550,7 +4863,7 @@ static void ins_try_si(int c)
i = get_indent();
curwin->w_cursor = old_pos;
if (State & VREPLACE_FLAG) {
- change_indent(INDENT_SET, i, FALSE, NUL, TRUE);
+ change_indent(INDENT_SET, i, false, NUL, true);
} else {
(void)set_indent(i, SIN_CHANGED);
}
@@ -5562,7 +4875,7 @@ static void ins_try_si(int c)
old_pos = curwin->w_cursor;
i = get_indent();
while (curwin->w_cursor.lnum > 1) {
- ptr = (char_u *)skipwhite((char *)ml_get(--(curwin->w_cursor.lnum)));
+ ptr = skipwhite(ml_get(--(curwin->w_cursor.lnum)));
// ignore empty lines and lines starting with '#'.
if (*ptr != '#' && *ptr != NUL) {
@@ -5575,14 +4888,12 @@ static void ins_try_si(int c)
curwin->w_cursor = old_pos;
}
if (temp) {
- shift_line(TRUE, FALSE, 1, TRUE);
+ shift_line(true, false, 1, true);
}
}
}
- /*
- * set indent of '#' always to 0
- */
+ // set indent of '#' always to 0
if (curwin->w_cursor.col > 0 && can_si && c == '#' && inindent(0)) {
// remember current indent for next line
old_indent = get_indent();
@@ -5595,10 +4906,8 @@ static void ins_try_si(int c)
}
}
-/*
- * Get the value that w_virtcol would have when 'list' is off.
- * Unless 'cpo' contains the 'L' flag.
- */
+// Get the value that w_virtcol would have when 'list' is off.
+// Unless 'cpo' contains the 'L' flag.
colnr_T get_nolist_virtcol(void)
{
// check validity of cursor in current buffer
@@ -5613,13 +4922,11 @@ colnr_T get_nolist_virtcol(void)
return curwin->w_virtcol;
}
-/*
- * Handle the InsertCharPre autocommand.
- * "c" is the character that was typed.
- * Return a pointer to allocated memory with the replacement string.
- * Return NULL to continue inserting "c".
- */
-static char_u *do_insert_char_pre(int c)
+// Handle the InsertCharPre autocommand.
+// "c" is the character that was typed.
+// Return a pointer to allocated memory with the replacement string.
+// Return NULL to continue inserting "c".
+static char *do_insert_char_pre(int c)
{
char buf[MB_MAXBYTES + 1];
const int save_State = State;
@@ -5634,13 +4941,13 @@ static char_u *do_insert_char_pre(int c)
textlock++;
set_vim_var_string(VV_CHAR, buf, -1);
- char_u *res = NULL;
+ char *res = NULL;
if (ins_apply_autocmds(EVENT_INSERTCHARPRE)) {
// Get the value of v:char. It may be empty or more than one
// 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((char_u *)get_vim_var_str(VV_CHAR));
+ if (strcmp(buf, get_vim_var_str(VV_CHAR)) != 0) {
+ res = xstrdup(get_vim_var_str(VV_CHAR));
}
}
@@ -5653,11 +4960,16 @@ static char_u *do_insert_char_pre(int c)
return res;
}
-bool can_cindent_get(void)
+bool get_can_cindent(void)
{
return can_cindent;
}
+void set_can_cindent(bool val)
+{
+ can_cindent = val;
+}
+
/// Trigger "event" and take care of fixing undo.
int ins_apply_autocmds(event_T event)
{
diff --git a/src/nvim/edit.h b/src/nvim/edit.h
index eda6d8c9db..91c519f015 100644
--- a/src/nvim/edit.h
+++ b/src/nvim/edit.h
@@ -4,8 +4,6 @@
#include "nvim/autocmd.h"
#include "nvim/vim.h"
-typedef int (*IndentGetter)(void);
-
// Values for in_cinkeys()
#define KEY_OPEN_FORW 0x101
#define KEY_OPEN_BACK 0x102
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index cb46e26f82..4392ea306f 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -3,19 +3,19 @@
// eval.c: Expression evaluation.
+#include <assert.h>
+#include <ctype.h>
+#include <inttypes.h>
#include <math.h>
+#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include "auto/config.h"
-
-#ifdef HAVE_LOCALE_H
-# include <locale.h>
-#endif
-
+#include "nvim/api/private/defs.h"
#include "nvim/ascii.h"
-#include "nvim/autocmd.h"
#include "nvim/buffer.h"
-#include "nvim/change.h"
+#include "nvim/buffer_defs.h"
#include "nvim/channel.h"
#include "nvim/charset.h"
#include "nvim/cmdhist.h"
@@ -28,36 +28,58 @@
#include "nvim/eval/typval.h"
#include "nvim/eval/userfunc.h"
#include "nvim/eval/vars.h"
+#include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
+#include "nvim/event/process.h"
#include "nvim/ex_cmds.h"
-#include "nvim/ex_cmds2.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
#include "nvim/ex_getln.h"
#include "nvim/ex_session.h"
+#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/grid_defs.h"
#include "nvim/highlight_group.h"
+#include "nvim/insexpand.h"
+#include "nvim/keycodes.h"
+#include "nvim/lib/queue.h"
+#include "nvim/locale.h"
#include "nvim/lua/executor.h"
+#include "nvim/macros.h"
+#include "nvim/main.h"
+#include "nvim/map.h"
#include "nvim/mark.h"
+#include "nvim/mbyte.h"
#include "nvim/memline.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
#include "nvim/move.h"
+#include "nvim/msgpack_rpc/channel_defs.h"
#include "nvim/ops.h"
#include "nvim/option.h"
#include "nvim/optionstr.h"
-#include "nvim/os/input.h"
+#include "nvim/os/fileio.h"
+#include "nvim/os/fs_defs.h"
+#include "nvim/os/os.h"
#include "nvim/os/shell.h"
+#include "nvim/os/stdpaths_defs.h"
#include "nvim/path.h"
+#include "nvim/pos.h"
#include "nvim/profile.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
-#include "nvim/sign.h"
-#include "nvim/syntax.h"
+#include "nvim/strings.h"
+#include "nvim/tag.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/ui_compositor.h"
-#include "nvim/undo.h"
+#include "nvim/usercmd.h"
#include "nvim/version.h"
+#include "nvim/vim.h"
#include "nvim/window.h"
// TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead
@@ -65,11 +87,15 @@
#define DICT_MAXNEST 100 // maximum nesting of lists and dicts
static char *e_missbrac = N_("E111: Missing ']'");
+static char *e_list_end = N_("E697: Missing end of List ']': %s");
static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary");
static char *e_nowhitespace
= N_("E274: No white space allowed before parenthesis");
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");
+static char e_expression_too_recursive_str[] = N_("E1169: Expression too recursive: %s");
+static char e_dot_can_only_be_used_on_dictionary_str[]
+ = N_("E1203: Dot can only be used on a dictionary: %s");
static char * const namespace_char = "abglstvw";
@@ -101,7 +127,7 @@ static garray_T ga_loaded = { 0, 0, sizeof(char *), 4, NULL };
/// Info used by a ":for" loop.
typedef struct {
- int fi_semicolon; // TRUE if ending in '; var]'
+ 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
@@ -137,8 +163,7 @@ static struct vimvar {
char *vv_name; ///< Name of the variable, without v:.
TV_DICTITEM_STRUCT(VIMVAR_KEY_LEN + 1) vv_di; ///< Value and name for key (max 16 chars).
char vv_flags; ///< Flags: #VV_COMPAT, #VV_RO, #VV_RO_SBX.
-} vimvars[] =
-{
+} vimvars[] = {
// VV_ tails differing from upcased string literals:
// VV_CC_FROM "charconvert_from"
// VV_CC_TO "charconvert_to"
@@ -242,6 +267,8 @@ static struct vimvar {
VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO),
VV(VV__NULL_BLOB, "_null_blob", VAR_BLOB, VV_RO),
VV(VV_LUA, "lua", VAR_PARTIAL, VV_RO),
+ VV(VV_RELNUM, "relnum", VAR_NUMBER, VV_RO),
+ VV(VV_VIRTNUM, "virtnum", VAR_NUMBER, VV_RO),
};
#undef VV
@@ -335,6 +362,10 @@ varnumber_T num_divide(varnumber_T n1, varnumber_T n2)
} else {
result = VARNUMBER_MAX;
}
+ } else if (n1 == VARNUMBER_MIN && n2 == -1) {
+ // specific case: trying to do VARNUMBAR_MIN / -1 results in a positive
+ // number that doesn't fit in varnumber_T and causes an FPE
+ result = VARNUMBER_MAX;
} else {
result = n1 / n2;
}
@@ -363,7 +394,7 @@ void eval_init(void)
for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) {
struct vimvar *p = &vimvars[i];
- assert(STRLEN(p->vv_name) <= VIMVAR_KEY_LEN);
+ assert(strlen(p->vv_name) <= VIMVAR_KEY_LEN);
STRCPY(p->vv_di.di_key, p->vv_name);
if (p->vv_flags & VV_RO) {
p->vv_di.di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
@@ -375,11 +406,11 @@ void eval_init(void)
// add to v: scope dict, unless the value is not always available
if (p->vv_type != VAR_UNKNOWN) {
- hash_add(&vimvarht, p->vv_di.di_key);
+ hash_add(&vimvarht, (char *)p->vv_di.di_key);
}
if (p->vv_flags & VV_COMPAT) {
// add to compat scope dict
- hash_add(&compat_hashtab, p->vv_di.di_key);
+ hash_add(&compat_hashtab, (char *)p->vv_di.di_key);
}
}
vimvars[VV_VERSION].vv_nr = VIM_VERSION_100;
@@ -490,7 +521,7 @@ void eval_clear(void)
/// 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)
+void set_internal_string_var(const char *name, char *value) // NOLINT(readability-non-const-parameter)
FUNC_ATTR_NONNULL_ARG(1)
{
typval_T tv = {
@@ -546,7 +577,7 @@ int var_redir_start(char *name, int append)
// check if we can write to the variable: set it to or append an empty
// string
- int save_emsg = did_emsg;
+ const int called_emsg_before = called_emsg;
did_emsg = false;
typval_T tv;
tv.v_type = VAR_STRING;
@@ -557,9 +588,7 @@ int var_redir_start(char *name, int append)
set_var_lval(redir_lval, redir_endp, &tv, true, false, "=");
}
clear_lval(redir_lval);
- int err = did_emsg;
- did_emsg |= save_emsg;
- if (err) {
+ if (called_emsg > called_emsg_before) {
redir_endp = NULL; // don't store a value, only cleanup
var_redir_stop();
return FAIL;
@@ -583,7 +612,7 @@ void var_redir_str(char *value, int value_len)
int len;
if (value_len == -1) {
- len = (int)STRLEN(value); // Append the entire string
+ len = (int)strlen(value); // Append the entire string
} else {
len = value_len; // Append only "value_len" characters
}
@@ -631,7 +660,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((char *)p_ccv, &err, NULL, false)) {
+ if (eval_to_bool(p_ccv, &err, NULL, false)) {
err = true;
}
set_vim_var_string(VV_CC_FROM, NULL, -1);
@@ -645,25 +674,6 @@ int eval_charconvert(const char *const enc_from, const char *const enc_to,
return OK;
}
-int eval_printexpr(const char *const fname, const char *const args)
-{
- bool err = false;
-
- set_vim_var_string(VV_FNAME_IN, fname, -1);
- set_vim_var_string(VV_CMDARG, args, -1);
- if (eval_to_bool((char *)p_pexpr, &err, NULL, false)) {
- err = true;
- }
- set_vim_var_string(VV_FNAME_IN, NULL, -1);
- set_vim_var_string(VV_CMDARG, NULL, -1);
-
- if (err) {
- os_remove(fname);
- return FAIL;
- }
- return OK;
-}
-
void eval_diff(const char *const origfile, const char *const newfile, const char *const outfile)
{
bool err = false;
@@ -671,7 +681,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((char *)p_dex, &err, NULL, false);
+ (void)eval_to_bool(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);
@@ -684,18 +694,18 @@ 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((char *)p_pex, &err, NULL, false);
+ (void)eval_to_bool(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);
}
/// Top level evaluation function, returning a boolean.
-/// Sets "error" to TRUE if there was an error.
+/// Sets "error" to true if there was an error.
///
/// @param skip only parse, don't execute
///
-/// @return TRUE or FALSE.
+/// @return true or false.
int eval_to_bool(char *arg, bool *error, char **nextcmd, int skip)
{
typval_T tv;
@@ -762,7 +772,7 @@ int eval_expr_typval(const typval_T *expr, typval_T *argv, int argc, typval_T *r
if (s == NULL || *s == NUL) {
return FAIL;
}
- funcexe.evaluate = true;
+ funcexe.fe_evaluate = true;
if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) {
return FAIL;
}
@@ -772,8 +782,8 @@ int eval_expr_typval(const typval_T *expr, typval_T *argv, int argc, typval_T *r
if (s == NULL || *s == NUL) {
return FAIL;
}
- funcexe.evaluate = true;
- funcexe.partial = partial;
+ funcexe.fe_evaluate = true;
+ funcexe.fe_partial = partial;
if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) {
return FAIL;
}
@@ -895,7 +905,7 @@ char *eval_to_string(char *arg, char **nextcmd, bool convert)
/// Call eval_to_string() without using current local variables and using
/// textlock.
///
-/// @param use_sandbox when TRUE, use the sandbox.
+/// @param use_sandbox when true, use the sandbox.
char *eval_to_string_safe(char *arg, char **nextcmd, int use_sandbox)
{
char *retval;
@@ -982,7 +992,7 @@ void prepare_vimvar(int idx, typval_T *save_tv)
{
*save_tv = vimvars[idx].vv_tv;
if (vimvars[idx].vv_type == VAR_UNKNOWN) {
- hash_add(&vimvarht, vimvars[idx].vv_di.di_key);
+ hash_add(&vimvarht, (char *)vimvars[idx].vv_di.di_key);
}
}
@@ -991,24 +1001,15 @@ void prepare_vimvar(int idx, typval_T *save_tv)
void restore_vimvar(int idx, typval_T *save_tv)
{
vimvars[idx].vv_tv = *save_tv;
- if (vimvars[idx].vv_type == VAR_UNKNOWN) {
- hashitem_T *hi = hash_find(&vimvarht, (char *)vimvars[idx].vv_di.di_key);
- if (HASHITEM_EMPTY(hi)) {
- internal_error("restore_vimvar()");
- } else {
- hash_remove(&vimvarht, hi);
- }
+ if (vimvars[idx].vv_type != VAR_UNKNOWN) {
+ return;
}
-}
-/// If there is a window for "curbuf", make it the current window.
-void find_win_for_curbuf(void)
-{
- for (wininfo_T *wip = curbuf->b_wininfo; wip != NULL; wip = wip->wi_next) {
- if (wip->wi_win != NULL) {
- curwin = wip->wi_win;
- break;
- }
+ hashitem_T *hi = hash_find(&vimvarht, (char *)vimvars[idx].vv_di.di_key);
+ if (HASHITEM_EMPTY(hi)) {
+ internal_error("restore_vimvar()");
+ } else {
+ hash_remove(&vimvarht, hi);
}
}
@@ -1074,16 +1075,16 @@ int get_spellword(list_T *const list, const char **ret_word)
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.
+/// 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 *func, int argc, typval_T *argv, typval_T *rettv)
FUNC_ATTR_NONNULL_ALL
{
int ret;
- int len = (int)STRLEN(func);
+ int len = (int)strlen(func);
partial_T *pt = NULL;
if (len >= 6 && !memcmp(func, "v:lua.", 6)) {
@@ -1098,10 +1099,10 @@ int call_vim_function(const char *func, int argc, typval_T *argv, typval_T *rett
rettv->v_type = VAR_UNKNOWN; // tv_clear() uses this.
funcexe_T funcexe = FUNCEXE_INIT;
- funcexe.firstline = curwin->w_cursor.lnum;
- funcexe.lastline = curwin->w_cursor.lnum;
- funcexe.evaluate = true;
- funcexe.partial = pt;
+ funcexe.fe_firstline = curwin->w_cursor.lnum;
+ funcexe.fe_lastline = curwin->w_cursor.lnum;
+ funcexe.fe_evaluate = true;
+ funcexe.fe_partial = pt;
ret = call_func(func, len, rettv, argc, argv, &funcexe);
fail:
@@ -1111,26 +1112,10 @@ fail:
return ret;
}
-/// Call Vim script function and return the result as a number
-///
-/// @param[in] func Function name.
-/// @param[in] argc Number of arguments.
-/// @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 *func, int argc, typval_T *argv)
- FUNC_ATTR_NONNULL_ALL
-{
- typval_T rettv;
- if (call_vim_function((char *)func, argc, argv, &rettv) == FAIL) {
- return -1;
- }
- varnumber_T retval = tv_get_number_chk(&rettv, NULL);
- tv_clear(&rettv);
- return retval;
-}
-/// Call Vim script function and return the result as a string
+/// Call Vim script function and return the result as a string.
+/// Uses "argv[0]" to "argv[argc - 1]" for the function arguments. "argv[argc]"
+/// should have type VAR_UNKNOWN.
///
/// @param[in] func Function name.
/// @param[in] argc Number of arguments.
@@ -1152,7 +1137,9 @@ char *call_func_retstr(const char *const func, int argc, typval_T *argv)
tv_clear(&rettv);
return retval;
}
-/// Call Vim script function and return the result as a List
+
+/// Call Vim script function and return the result as a List.
+/// Uses "argv" and "argc" as call_func_retstr().
///
/// @param[in] func Function name.
/// @param[in] argc Number of arguments.
@@ -1220,8 +1207,6 @@ int eval_foldexpr(char *arg, int *cp)
return (int)retval;
}
-// TODO(ZyX-I): move to eval/executor
-
/// Get an lvalue
///
/// Lvalue may be
@@ -1316,13 +1301,26 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
return NULL;
}
- // Loop until no more [idx] or .key is following.
lp->ll_tv = &v->di_tv;
+
+ if (tv_is_luafunc(lp->ll_tv)) {
+ // For v:lua just return a pointer to the "." after the "v:lua".
+ // If the caller is trans_function_name() it will check for a Lua function name.
+ return p;
+ }
+
+ // Loop until no more [idx] or .key is following.
typval_T var1;
var1.v_type = VAR_UNKNOWN;
typval_T var2;
var2.v_type = VAR_UNKNOWN;
- while (*p == '[' || (*p == '.' && lp->ll_tv->v_type == VAR_DICT)) {
+ while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.')) {
+ if (*p == '.' && lp->ll_tv->v_type != VAR_DICT) {
+ if (!quiet) {
+ semsg(_(e_dot_can_only_be_used_on_dictionary_str), name);
+ }
+ return NULL;
+ }
if (!(lp->ll_tv->v_type == VAR_LIST && lp->ll_tv->vval.v_list != NULL)
&& !(lp->ll_tv->v_type == VAR_DICT && lp->ll_tv->vval.v_dict != NULL)
&& !(lp->ll_tv->v_type == VAR_BLOB && lp->ll_tv->vval.v_blob != NULL)) {
@@ -1444,12 +1442,13 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
}
wrong = ((lp->ll_dict->dv_scope == VAR_DEF_SCOPE
&& tv_is_func(*rettv)
- && !var_check_func_name((const char *)key, lp->ll_di == NULL))
+ && var_wrong_func_name((const char *)key, lp->ll_di == NULL))
|| !valid_varname((const char *)key));
if (len != -1) {
key[len] = prevval;
}
if (wrong) {
+ tv_clear(&var1);
return NULL;
}
}
@@ -1486,9 +1485,9 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
tv_clear(&var1);
break;
// existing variable, need to check if it can be changed
- } else if (!(flags & GLV_READ_ONLY) && var_check_ro(lp->ll_di->di_flags,
- (const char *)name,
- (size_t)(p - name))) {
+ } else if (!(flags & GLV_READ_ONLY)
+ && (var_check_ro(lp->ll_di->di_flags, name, (size_t)(p - name))
+ || var_check_lock(lp->ll_di->di_flags, name, (size_t)(p - name)))) {
tv_clear(&var1);
return NULL;
}
@@ -1592,8 +1591,6 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
return p;
}
-// TODO(ZyX-I): move to eval/executor
-
/// Clear lval "lp" that was filled by get_lval().
void clear_lval(lval_T *lp)
{
@@ -1601,8 +1598,6 @@ void clear_lval(lval_T *lp)
xfree(lp->ll_newkey);
}
-// TODO(ZyX-I): move to eval/executor
-
/// Set a variable that was parsed by get_lval() to "rettv".
///
/// @param endp points to just after the parsed name.
@@ -1623,7 +1618,7 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool
semsg(_(e_letwrong), op);
return;
}
- if (var_check_lock(lp->ll_blob->bv_lock, lp->ll_name, TV_CSTRING)) {
+ if (value_check_lock(lp->ll_blob->bv_lock, lp->ll_name, TV_CSTRING)) {
return;
}
@@ -1653,7 +1648,7 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool
// 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, (int)lp->ll_n1, (char_u)val);
+ tv_blob_set(lp->ll_blob, (int)lp->ll_n1, (uint8_t)val);
if (lp->ll_n1 == gap->ga_len) {
gap->ga_len++;
}
@@ -1672,7 +1667,7 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool
// handle +=, -=, *=, /=, %= and .=
di = NULL;
- if (get_var_tv(lp->ll_name, (int)STRLEN(lp->ll_name),
+ if (get_var_tv(lp->ll_name, (int)strlen(lp->ll_name),
&tv, &di, true, false) == OK) {
if ((di == NULL
|| (!var_check_ro(di->di_flags, lp->ll_name, TV_CSTRING)
@@ -1686,10 +1681,10 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool
set_var_const(lp->ll_name, lp->ll_name_len, rettv, copy, is_const);
}
*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)) {
+ } else if (value_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;
@@ -1703,8 +1698,8 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool
// Check whether any of the list items is locked
for (ri = tv_list_first(rettv->vval.v_list);
ri != NULL && ll_li != NULL;) {
- if (var_check_lock(TV_LIST_ITEM_TV(ll_li)->v_lock, lp->ll_name,
- TV_CSTRING)) {
+ if (value_check_lock(TV_LIST_ITEM_TV(ll_li)->v_lock, lp->ll_name,
+ TV_CSTRING)) {
return;
}
ri = TV_LIST_ITEM_NEXT(rettv->vval.v_list, ri);
@@ -1759,7 +1754,10 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool
// Assign to a List or Dictionary item.
if (lp->ll_newkey != NULL) {
if (op != NULL && *op != '=') {
- semsg(_(e_letwrong), op);
+ semsg(_(e_dictkey), lp->ll_newkey);
+ return;
+ }
+ if (tv_dict_wrong_func_name(lp->ll_tv->vval.v_dict, rettv, lp->ll_newkey)) {
return;
}
@@ -1807,12 +1805,10 @@ notify:
}
}
-// TODO(ZyX-I): move to eval/ex_cmds
-
/// 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;
+/// @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)
@@ -1884,8 +1880,6 @@ void *eval_for_line(const char *arg, bool *errp, char **nextcmdp, int skip)
return fi;
}
-// 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.
///
@@ -1926,15 +1920,12 @@ bool next_for_item(void *fi_void, char *arg)
listitem_T *item = fi->fi_lw.lw_item;
if (item == NULL) {
return false;
- } else {
- fi->fi_lw.lw_item = TV_LIST_ITEM_NEXT(fi->fi_list, item);
- return (ex_let_vars(arg, TV_LIST_ITEM_TV(item), true,
- fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK);
}
+ fi->fi_lw.lw_item = TV_LIST_ITEM_NEXT(fi->fi_list, item);
+ return (ex_let_vars(arg, TV_LIST_ITEM_TV(item), true,
+ fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK);
}
-// TODO(ZyX-I): move to eval/ex_cmds
-
/// Free the structure used to store info used by ":for".
void free_for_info(void *fi_void)
{
@@ -1965,7 +1956,7 @@ void set_context_for_expression(expand_T *xp, char *arg, cmdidx_T cmdidx)
xp->xp_context = EXPAND_USER_VARS;
if (strpbrk(arg, "\"'+-*/%.=!?~|&$([<>,#") == NULL) {
// ":let var1 var2 ...": find last space.
- for (p = arg + STRLEN(arg); p >= arg;) {
+ for (p = arg + strlen(arg); p >= arg;) {
xp->xp_pattern = p;
MB_PTR_BACK(arg, p);
if (ascii_iswhite(*p)) {
@@ -2047,7 +2038,7 @@ void set_context_for_expression(expand_T *xp, char *arg, cmdidx_T cmdidx)
|| cmdidx == CMD_echomsg)
&& xp->xp_context == EXPAND_EXPRESSION) {
for (;;) {
- char *const n = (char *)skiptowhite((char_u *)arg);
+ char *const n = skiptowhite(arg);
if (n == arg || ascii_iswhite_or_nul(*skipwhite(n))) {
break;
@@ -2064,7 +2055,7 @@ void del_menutrans_vars(void)
{
hash_lock(&globvarht);
HASHTAB_ITER(&globvarht, hi, {
- if (STRNCMP(hi->hi_key, "menutrans_", 10) == 0) {
+ if (strncmp(hi->hi_key, "menutrans_", 10) == 0) {
delete_var(&globvarht, hi);
}
});
@@ -2082,7 +2073,7 @@ static size_t varnamebuflen = 0;
char *cat_prefix_varname(int prefix, const char *name)
FUNC_ATTR_NONNULL_ALL
{
- size_t len = STRLEN(name) + 3;
+ size_t len = strlen(name) + 3;
if (len > varnamebuflen) {
xfree(varnamebuf);
@@ -2122,10 +2113,10 @@ char *get_user_var_name(expand_T *xp, int idx)
while (HASHITEM_EMPTY(hi)) {
hi++;
}
- if (STRNCMP("g:", xp->xp_pattern, 2) == 0) {
- return cat_prefix_varname('g', (char *)hi->hi_key);
+ if (strncmp("g:", xp->xp_pattern, 2) == 0) {
+ return cat_prefix_varname('g', hi->hi_key);
}
- return (char *)hi->hi_key;
+ return hi->hi_key;
}
// b: variables
@@ -2139,7 +2130,7 @@ char *get_user_var_name(expand_T *xp, int idx)
while (HASHITEM_EMPTY(hi)) {
hi++;
}
- return cat_prefix_varname('b', (char *)hi->hi_key);
+ return cat_prefix_varname('b', hi->hi_key);
}
// w: variables
@@ -2153,7 +2144,7 @@ char *get_user_var_name(expand_T *xp, int idx)
while (HASHITEM_EMPTY(hi)) {
hi++;
}
- return cat_prefix_varname('w', (char *)hi->hi_key);
+ return cat_prefix_varname('w', hi->hi_key);
}
// t: variables
@@ -2167,7 +2158,7 @@ char *get_user_var_name(expand_T *xp, int idx)
while (HASHITEM_EMPTY(hi)) {
hi++;
}
- return cat_prefix_varname('t', (char *)hi->hi_key);
+ return cat_prefix_varname('t', hi->hi_key);
}
// v: variables
@@ -2180,23 +2171,21 @@ char *get_user_var_name(expand_T *xp, int idx)
return NULL;
}
-// TODO(ZyX-I): move to eval/expressions
-
/// Does not use 'cpo' and always uses 'magic'.
///
-/// @return TRUE if "pat" matches "text".
-int pattern_match(char *pat, char *text, bool ic)
+/// @return true if "pat" matches "text".
+int pattern_match(const char *pat, const char *text, bool ic)
{
int matches = 0;
regmatch_T regmatch;
// avoid 'l' flag in 'cpoptions'
char *save_cpo = p_cpo;
- p_cpo = "";
- regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
+ p_cpo = empty_option;
+ regmatch.regprog = vim_regcomp((char *)pat, RE_MAGIC + RE_STRING);
if (regmatch.regprog != NULL) {
regmatch.rm_ic = ic;
- matches = vim_regexec_nl(&regmatch, (char_u *)text, (colnr_T)0);
+ matches = vim_regexec_nl(&regmatch, (char *)text, (colnr_T)0);
vim_regfree(regmatch.regprog);
}
p_cpo = save_cpo;
@@ -2224,7 +2213,7 @@ static int eval_func(char **const arg, char *const name, const int name_len, typ
// If "s" is the name of a variable of type VAR_FUNC
// use its contents.
partial_T *partial;
- s = (char *)deref_func_name((const char *)s, &len, &partial, !evaluate);
+ s = deref_func_name((const char *)s, &len, &partial, !evaluate);
// Need to make a copy, in case evaluating the arguments makes
// the name invalid.
@@ -2232,12 +2221,12 @@ static int eval_func(char **const arg, char *const name, const int name_len, typ
// Invoke the function.
funcexe_T funcexe = FUNCEXE_INIT;
- funcexe.firstline = curwin->w_cursor.lnum;
- funcexe.lastline = curwin->w_cursor.lnum;
- funcexe.evaluate = evaluate;
- funcexe.partial = partial;
- funcexe.basetv = basetv;
- int ret = get_func_tv((char_u *)s, len, rettv, arg, &funcexe);
+ funcexe.fe_firstline = curwin->w_cursor.lnum;
+ funcexe.fe_lastline = curwin->w_cursor.lnum;
+ funcexe.fe_evaluate = evaluate;
+ funcexe.fe_partial = partial;
+ funcexe.fe_basetv = basetv;
+ int ret = get_func_tv(s, len, rettv, arg, &funcexe);
xfree(s);
@@ -2261,15 +2250,13 @@ static int eval_func(char **const arg, char *const name, const int name_len, typ
return ret;
}
-// TODO(ZyX-I): move to eval/expressions
-
-/// The "evaluate" argument: When FALSE, the argument is only parsed but not
+/// The "evaluate" argument: When false, the argument is only parsed but not
/// executed. The function may return OK, but the rettv will be of type
/// 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.
+/// Put the result in "rettv" when returning OK and "evaluate" is true.
/// Note: "rettv.v_lock" is not set.
///
/// @return OK or FAIL.
@@ -2279,10 +2266,15 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate)
char *p;
const int did_emsg_before = did_emsg;
const int called_emsg_before = called_emsg;
+ bool end_error = false;
p = skipwhite(arg);
ret = eval1(&p, rettv, evaluate);
- if (ret == FAIL || !ends_excmd(*p)) {
+
+ if (ret != FAIL) {
+ end_error = !ends_excmd(*p);
+ }
+ if (ret == FAIL || end_error) {
if (ret != FAIL) {
tv_clear(rettv);
}
@@ -2292,19 +2284,21 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate)
// Also check called_emsg for when using assert_fails().
if (!aborting() && did_emsg == did_emsg_before
&& called_emsg == called_emsg_before) {
- semsg(_(e_invexpr2), arg);
+ if (end_error) {
+ semsg(_(e_trailing_arg), p);
+ } else {
+ semsg(_(e_invexpr2), arg);
+ }
}
ret = FAIL;
}
if (nextcmd != NULL) {
- *nextcmd = (char *)check_nextcmd((char_u *)p);
+ *nextcmd = check_nextcmd(p);
}
return ret;
}
-// TODO(ZyX-I): move to eval/expressions
-
/// Handle top level expression:
/// expr2 ? expr1 : expr1
///
@@ -2369,8 +2363,6 @@ int eval1(char **arg, typval_T *rettv, int evaluate)
return OK;
}
-// TODO(ZyX-I): move to eval/expressions
-
/// Handle first level expression:
/// expr2 || expr2 || expr2 logical OR
///
@@ -2428,8 +2420,6 @@ static int eval2(char **arg, typval_T *rettv, int evaluate)
return OK;
}
-// TODO(ZyX-I): move to eval/expressions
-
/// Handle second level expression:
/// expr3 && expr3 && expr3 logical AND
///
@@ -2487,8 +2477,6 @@ static int eval3(char **arg, typval_T *rettv, int evaluate)
return OK;
}
-// TODO(ZyX-I): move to eval/expressions
-
/// Handle third level expression:
/// var1 == var2
/// var1 =~ var2
@@ -2555,7 +2543,7 @@ static int eval4(char **arg, typval_T *rettv, int evaluate)
if (p[2] == 'n' && p[3] == 'o' && p[4] == 't') {
len = 5;
}
- if (!isalnum(p[len]) && p[len] != '_') {
+ if (!isalnum((uint8_t)p[len]) && p[len] != '_') {
type = len == 2 ? EXPR_IS : EXPR_ISNOT;
}
}
@@ -2592,8 +2580,6 @@ static int eval4(char **arg, typval_T *rettv, int evaluate)
return OK;
}
-// TODO(ZyX-I): move to eval/expressions
-
/// Handle fourth level expression:
/// + number addition
/// - number subtraction
@@ -2663,7 +2649,7 @@ static int eval5(char **arg, typval_T *rettv, int evaluate)
tv_clear(&var2);
return FAIL;
}
- p = (char *)concat_str((const char_u *)s1, (const char_u *)s2);
+ p = concat_str(s1, s2);
tv_clear(rettv);
rettv->v_type = VAR_STRING;
rettv->vval.v_string = p;
@@ -2674,10 +2660,10 @@ static int eval5(char **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, (char)tv_blob_get(b1, i));
+ ga_append(&b->bv_ga, tv_blob_get(b1, i));
}
for (int i = 0; i < tv_blob_len(b2); i++) {
- ga_append(&b->bv_ga, (char)tv_blob_get(b2, i));
+ ga_append(&b->bv_ga, tv_blob_get(b2, i));
}
tv_clear(rettv);
@@ -2755,8 +2741,6 @@ static int eval5(char **arg, typval_T *rettv, int evaluate)
return OK;
}
-// TODO(ZyX-I): move to eval/expressions
-
/// Handle fifth level expression:
/// - * number multiplication
/// - / number division
@@ -2872,8 +2856,6 @@ static int eval6(char **arg, typval_T *rettv, int evaluate, int want_string)
return OK;
}
-// TODO(ZyX-I): move to eval/expressions
-
/// Handle sixth level expression:
/// number number constant
/// 0zFFFFFFFF Blob constant
@@ -2905,23 +2887,33 @@ static int eval6(char **arg, typval_T *rettv, int evaluate, int want_string)
/// @return OK or FAIL.
static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
{
- varnumber_T n;
- int len;
- char *s;
- const char *start_leader, *end_leader;
int ret = OK;
- char *alias;
+ static int recurse = 0;
// Initialise variable so that tv_clear() can't mistake this for a
// string and free a string that isn't there.
rettv->v_type = VAR_UNKNOWN;
// Skip '!', '-' and '+' characters. They are handled later.
- start_leader = *arg;
+ const char *start_leader = *arg;
while (**arg == '!' || **arg == '-' || **arg == '+') {
*arg = skipwhite(*arg + 1);
}
- end_leader = *arg;
+ const char *end_leader = *arg;
+
+ // Limit recursion to 1000 levels. At least at 10000 we run out of stack
+ // and crash. With MSVC the stack is smaller.
+ if (recurse ==
+#ifdef _MSC_VER
+ 300
+#else
+ 1000
+#endif
+ ) {
+ semsg(_(e_expression_too_recursive_str), *arg);
+ return FAIL;
+ }
+ recurse++;
switch (**arg) {
// Number constant.
@@ -2934,86 +2926,9 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
case '6':
case '7':
case '8':
- case '9': {
- char *p = skipdigits(*arg + 1);
- int get_float = false;
-
- // We accept a float when the format matches
- // "[0-9]\+\.[0-9]\+\([eE][+-]\?[0-9]\+\)\?". This is very
- // strict to avoid backwards compatibility problems.
- // Don't look for a float after the "." operator, so that
- // ":let vers = 1.2.3" doesn't fail.
- if (!want_string && p[0] == '.' && ascii_isdigit(p[1])) {
- get_float = true;
- p = skipdigits(p + 2);
- if (*p == 'e' || *p == 'E') {
- p++;
- if (*p == '-' || *p == '+') {
- p++;
- }
- if (!ascii_isdigit(*p)) {
- get_float = false;
- } else {
- p = skipdigits(p + 1);
- }
- }
- if (ASCII_ISALPHA(*p) || *p == '.') {
- get_float = false;
- }
- }
- if (get_float) {
- float_T f;
-
- *arg += string2float(*arg, &f);
- if (evaluate) {
- rettv->v_type = VAR_FLOAT;
- rettv->vval.v_float = f;
- }
- } else if (**arg == '0' && ((*arg)[1] == 'z' || (*arg)[1] == 'Z')) {
- blob_T *blob = NULL;
- // Blob constant: 0z0123456789abcdef
- if (evaluate) {
- blob = tv_blob_alloc();
- }
- char *bp;
- for (bp = *arg + 2; ascii_isxdigit(bp[0]); bp += 2) {
- if (!ascii_isxdigit(bp[1])) {
- if (blob != NULL) {
- emsg(_("E973: Blob literal should have an even number of hex "
- "characters"));
- ga_clear(&blob->bv_ga);
- XFREE_CLEAR(blob);
- }
- ret = FAIL;
- break;
- }
- if (blob != NULL) {
- ga_append(&blob->bv_ga, (char)((hex2nr(*bp) << 4) + hex2nr(*(bp + 1))));
- }
- if (bp[2] == '.' && ascii_isxdigit(bp[3])) {
- bp++;
- }
- }
- if (blob != NULL) {
- tv_blob_set_ret(rettv, blob);
- }
- *arg = bp;
- } else {
- // decimal, hex or octal number
- vim_str2nr((char_u *)(*arg), NULL, &len, STR2NR_ALL, &n, NULL, 0, true);
- if (len == 0) {
- semsg(_(e_invexpr2), *arg);
- ret = FAIL;
- break;
- }
- *arg += len;
- if (evaluate) {
- rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = n;
- }
- }
+ case '9':
+ ret = get_number_tv(arg, rettv, evaluate, want_string);
break;
- }
// String constant: "string".
case '"':
@@ -3034,7 +2949,7 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
case '#':
if ((*arg)[1] == '{') {
(*arg)++;
- ret = dict_get_tv(arg, rettv, evaluate, true);
+ ret = eval_dict(arg, rettv, evaluate, true);
} else {
ret = NOTDONE;
}
@@ -3045,7 +2960,7 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
case '{':
ret = get_lambda_tv(arg, rettv, evaluate);
if (ret == NOTDONE) {
- ret = dict_get_tv(arg, rettv, evaluate, false);
+ ret = eval_dict(arg, rettv, evaluate, false);
}
break;
@@ -3091,8 +3006,9 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
if (ret == NOTDONE) {
// 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, &alias, evaluate, true);
+ char *s = *arg;
+ char *alias;
+ int len = get_name_len((const char **)arg, &alias, evaluate, true);
if (alias != NULL) {
s = alias;
}
@@ -3125,6 +3041,8 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
if (ret == OK && evaluate && end_leader > start_leader) {
ret = eval7_leader(rettv, (char *)start_leader, &end_leader);
}
+
+ recurse--;
return ret;
}
@@ -3214,13 +3132,13 @@ static int call_func_rettv(char **const arg, typval_T *const rettv, const bool e
}
funcexe_T funcexe = FUNCEXE_INIT;
- funcexe.firstline = curwin->w_cursor.lnum;
- funcexe.lastline = curwin->w_cursor.lnum;
- funcexe.evaluate = evaluate;
- funcexe.partial = pt;
- funcexe.selfdict = selfdict;
- funcexe.basetv = basetv;
- const int ret = get_func_tv((char_u *)funcname, is_lua ? (int)(*arg - funcname) : -1, rettv,
+ funcexe.fe_firstline = curwin->w_cursor.lnum;
+ funcexe.fe_lastline = curwin->w_cursor.lnum;
+ funcexe.fe_evaluate = evaluate;
+ funcexe.fe_partial = pt;
+ funcexe.fe_selfdict = selfdict;
+ funcexe.fe_basetv = basetv;
+ const int ret = get_func_tv(funcname, is_lua ? (int)(*arg - funcname) : -1, rettv,
arg, &funcexe);
// Clear the funcref afterwards, so that deleting it while
@@ -3293,7 +3211,7 @@ static int eval_method(char **const arg, typval_T *const rettv, const bool evalu
int len;
char *name = *arg;
char *lua_funcname = NULL;
- if (STRNCMP(name, "v:lua.", 6) == 0) {
+ if (strncmp(name, "v:lua.", 6) == 0) {
lua_funcname = name + 6;
*arg = (char *)skip_luafunc_name((const char *)lua_funcname);
*arg = skipwhite(*arg); // to detect trailing whitespace later
@@ -3348,8 +3266,6 @@ static int eval_method(char **const arg, typval_T *const rettv, const bool evalu
return ret;
}
-// TODO(ZyX-I): move to eval/expressions
-
/// Evaluate an "[expr]" or "[expr:expr]" index. Also "dict.key".
/// "*arg" points to the '[' or '.'.
///
@@ -3401,7 +3317,7 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose)
if (**arg == '.') {
// dict.name
key = *arg + 1;
- for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; len++) {}
+ for (len = 0; eval_isdictc(key[len]); len++) {}
if (len == 0) {
return FAIL;
}
@@ -3649,8 +3565,6 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose)
return OK;
}
-// TODO(ZyX-I): move to eval/executor
-
/// Get an option value
///
/// @param[in,out] arg Points to the '&' or '+' before the option name. Is
@@ -3662,11 +3576,11 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose)
int get_option_tv(const char **const arg, typval_T *const rettv, const bool evaluate)
FUNC_ATTR_NONNULL_ARG(1)
{
- bool working = (**arg == '+'); // has("+option")
- int opt_flags;
+ const bool working = (**arg == '+'); // has("+option")
+ int scope;
// Isolate the option name and find its value.
- char *option_end = (char *)find_option_end(arg, &opt_flags);
+ char *option_end = (char *)find_option_end(arg, &scope);
if (option_end == NULL) {
if (rettv != NULL) {
semsg(_("E112: Option name missing: %s"), *arg);
@@ -3686,7 +3600,7 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval
char c = *option_end;
*option_end = NUL;
getoption_T opt_type = get_option_value(*arg, &numval,
- rettv == NULL ? NULL : &stringval, opt_flags);
+ rettv == NULL ? NULL : &stringval, NULL, scope);
if (opt_type == gov_unknown) {
if (rettv != NULL) {
@@ -3719,6 +3633,91 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval
return ret;
}
+/// Allocate a variable for a number constant. Also deals with "0z" for blob.
+///
+/// @return OK or FAIL.
+static int get_number_tv(char **arg, typval_T *rettv, bool evaluate, bool want_string)
+{
+ char *p = skipdigits(*arg + 1);
+ bool get_float = false;
+
+ // We accept a float when the format matches
+ // "[0-9]\+\.[0-9]\+\([eE][+-]\?[0-9]\+\)\?". This is very
+ // strict to avoid backwards compatibility problems.
+ // Don't look for a float after the "." operator, so that
+ // ":let vers = 1.2.3" doesn't fail.
+ if (!want_string && p[0] == '.' && ascii_isdigit(p[1])) {
+ get_float = true;
+ p = skipdigits(p + 2);
+ if (*p == 'e' || *p == 'E') {
+ p++;
+ if (*p == '-' || *p == '+') {
+ p++;
+ }
+ if (!ascii_isdigit(*p)) {
+ get_float = false;
+ } else {
+ p = skipdigits(p + 1);
+ }
+ }
+ if (ASCII_ISALPHA(*p) || *p == '.') {
+ get_float = false;
+ }
+ }
+ if (get_float) {
+ float_T f;
+ *arg += string2float(*arg, &f);
+ if (evaluate) {
+ rettv->v_type = VAR_FLOAT;
+ rettv->vval.v_float = f;
+ }
+ } else if (**arg == '0' && ((*arg)[1] == 'z' || (*arg)[1] == 'Z')) {
+ // Blob constant: 0z0123456789abcdef
+ blob_T *blob = NULL;
+ if (evaluate) {
+ blob = tv_blob_alloc();
+ }
+ char *bp;
+ for (bp = *arg + 2; ascii_isxdigit(bp[0]); bp += 2) {
+ if (!ascii_isxdigit(bp[1])) {
+ if (blob != NULL) {
+ emsg(_("E973: Blob literal should have an even number of hex characters"));
+ ga_clear(&blob->bv_ga);
+ XFREE_CLEAR(blob);
+ }
+ return FAIL;
+ }
+ if (blob != NULL) {
+ ga_append(&blob->bv_ga, (uint8_t)((hex2nr(*bp) << 4) + hex2nr(*(bp + 1))));
+ }
+ if (bp[2] == '.' && ascii_isxdigit(bp[3])) {
+ bp++;
+ }
+ }
+ if (blob != NULL) {
+ tv_blob_set_ret(rettv, blob);
+ }
+ *arg = bp;
+ } else {
+ // decimal, hex or octal number
+ int len;
+ varnumber_T n;
+ vim_str2nr(*arg, NULL, &len, STR2NR_ALL, &n, NULL, 0, true);
+ if (len == 0) {
+ if (evaluate) {
+ semsg(_(e_invexpr2), *arg);
+ }
+ return FAIL;
+ }
+ *arg += len;
+ if (evaluate) {
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = n;
+ }
+ }
+ return OK;
+}
+
/// Allocate a variable for a string constant.
///
/// @return OK or FAIL.
@@ -3780,7 +3779,7 @@ static int get_string_tv(char **arg, typval_T *rettv, int evaluate)
case 'U':
if (ascii_isxdigit(p[1])) {
int n, nr;
- int c = toupper(*p);
+ int c = toupper((uint8_t)(*p));
if (c == 'X') {
n = 2;
@@ -3831,7 +3830,7 @@ static int get_string_tv(char **arg, typval_T *rettv, int evaluate)
if (p[1] != '*') {
flags |= FSK_SIMPLIFY;
}
- extra = trans_special((const char_u **)&p, STRLEN(p), (char_u *)name, flags, false, NULL);
+ extra = trans_special((const char **)&p, strlen(p), name, flags, false, NULL);
if (extra != 0) {
name += extra;
if (name >= rettv->vval.v_string + len) {
@@ -3914,13 +3913,11 @@ char *partial_name(partial_T *pt)
FUNC_ATTR_PURE
{
if (pt->pt_name != NULL) {
- return (char *)pt->pt_name;
+ return pt->pt_name;
}
return (char *)pt->pt_func->uf_name;
}
-// TODO(ZyX-I): Move to eval/typval.h
-
static void partial_free(partial_T *pt)
{
for (int i = 0; i < pt->pt_argc; i++) {
@@ -3937,8 +3934,6 @@ static void partial_free(partial_T *pt)
xfree(pt);
}
-// TODO(ZyX-I): Move to eval/typval.h
-
/// Unreference a closure: decrement the reference count and free it when it
/// becomes zero.
void partial_unref(partial_T *pt)
@@ -3981,7 +3976,7 @@ static int get_list_tv(char **arg, typval_T *rettv, int evaluate)
}
if (**arg != ']') {
- semsg(_("E697: Missing end of List ']': %s"), *arg);
+ semsg(_(e_list_end), *arg);
failret:
if (evaluate) {
tv_list_free(l);
@@ -4001,13 +3996,11 @@ failret:
bool func_equal(typval_T *tv1, typval_T *tv2, bool ic)
{
// empty and NULL function name considered the same
- char_u *s1 =
- (char_u *)(tv1->v_type == VAR_FUNC ? tv1->vval.v_string : partial_name(tv1->vval.v_partial));
+ char *s1 = tv1->v_type == VAR_FUNC ? tv1->vval.v_string : partial_name(tv1->vval.v_partial);
if (s1 != NULL && *s1 == NUL) {
s1 = NULL;
}
- char_u *s2 =
- (char_u *)(tv2->v_type == VAR_FUNC ? tv2->vval.v_string : partial_name(tv2->vval.v_partial));
+ char *s2 = tv2->v_type == VAR_FUNC ? tv2->vval.v_string : partial_name(tv2->vval.v_partial);
if (s2 != NULL && *s2 == NUL) {
s2 = NULL;
}
@@ -4015,7 +4008,7 @@ bool func_equal(typval_T *tv1, typval_T *tv2, bool ic)
if (s1 != s2) {
return false;
}
- } else if (STRCMP(s1, s2) != 0) {
+ } else if (strcmp(s1, s2) != 0) {
return false;
}
@@ -4148,10 +4141,23 @@ bool garbage_collect(bool testing)
ABORTING(set_ref_dict)(buf->additional_data, copyID);
// buffer callback functions
- set_ref_in_callback(&buf->b_prompt_callback, copyID, NULL, NULL);
- set_ref_in_callback(&buf->b_prompt_interrupt, copyID, NULL, NULL);
+ ABORTING(set_ref_in_callback)(&buf->b_prompt_callback, copyID, NULL, NULL);
+ ABORTING(set_ref_in_callback)(&buf->b_prompt_interrupt, copyID, NULL, NULL);
+ ABORTING(set_ref_in_callback)(&buf->b_cfu_cb, copyID, NULL, NULL);
+ ABORTING(set_ref_in_callback)(&buf->b_ofu_cb, copyID, NULL, NULL);
+ ABORTING(set_ref_in_callback)(&buf->b_tsrfu_cb, copyID, NULL, NULL);
+ ABORTING(set_ref_in_callback)(&buf->b_tfu_cb, copyID, NULL, NULL);
}
+ // 'completefunc', 'omnifunc' and 'thesaurusfunc' callbacks
+ ABORTING(set_ref_in_insexpand_funcs)(copyID);
+
+ // 'operatorfunc' callback
+ ABORTING(set_ref_in_opfunc)(copyID);
+
+ // 'tagfunc' callback
+ ABORTING(set_ref_in_tagfunc)(copyID);
+
FOR_ALL_TAB_WINDOWS(tp, wp) {
// window-local variables
ABORTING(set_ref_in_item)(&wp->w_winvar.di_tv, copyID, NULL, NULL);
@@ -4160,8 +4166,11 @@ bool garbage_collect(bool testing)
ABORTING(set_ref_in_fmark)(wp->w_jumplist[i].fmark, copyID);
}
}
- if (aucmd_win != NULL) {
- ABORTING(set_ref_in_item)(&aucmd_win->w_winvar.di_tv, copyID, NULL, NULL);
+ // window-local variables in autocmd windows
+ for (int i = 0; i < AUCMD_WIN_COUNT; i++) {
+ if (aucmd_win[i].auc_win != NULL) {
+ ABORTING(set_ref_in_item)(&aucmd_win[i].auc_win->w_winvar.di_tv, copyID, NULL, NULL);
+ }
}
// registers (ShaDa additional data)
@@ -4231,11 +4240,11 @@ bool garbage_collect(bool testing)
// history items (ShaDa additional elements)
if (p_hi) {
- for (uint8_t i = 0; i < HIST_COUNT; i++) {
+ for (HistoryType i = 0; i < HIST_COUNT; i++) {
const void *iter = NULL;
do {
histentry_T hist;
- iter = hist_iter(iter, i, false, &hist);
+ iter = hist_iter(iter, (uint8_t)i, false, &hist);
if (hist.hisstr != NULL) {
ABORTING(set_ref_list)(hist.additional_elements, copyID);
}
@@ -4500,7 +4509,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((char_u *)tv->vval.v_string, NULL, copyID);
+ abort = set_ref_in_func(tv->vval.v_string, NULL, copyID);
break;
case VAR_UNKNOWN:
case VAR_BOOL:
@@ -4583,25 +4592,24 @@ static int get_literal_key(char **arg, typval_T *tv)
/// "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)
+static int eval_dict(char **arg, typval_T *rettv, int evaluate, bool literal)
{
typval_T tv;
char *key = NULL;
- char *start = skipwhite(*arg + 1);
+ char *curly_expr = skipwhite(*arg + 1);
char buf[NUMBUFLEN];
- // First check if it's not a curly-braces thing: {expr}.
+ // First check if it's not a curly-braces expression: {expr}.
// Must do this without evaluating, otherwise a function may be called
// twice. Unfortunately this means we need to call eval1() twice for the
// first item.
- // But {} is an empty Dictionary.
- if (*start != '}') {
- if (eval1(&start, &tv, false) == FAIL) { // recursive!
- return FAIL;
- }
- if (*skipwhite(start) == '}') {
- return NOTDONE;
- }
+ // "{}" is an empty Dictionary.
+ // "#{abc}" is never a curly-braces expression.
+ if (*curly_expr != '}'
+ && !literal
+ && eval1(&curly_expr, &tv, false) == OK
+ && *skipwhite(curly_expr) == '}') {
+ return NOTDONE;
}
dict_T *d = NULL;
@@ -4765,19 +4773,6 @@ void assert_error(garray_T *gap)
(const char *)gap->ga_data, (ptrdiff_t)gap->ga_len);
}
-/// 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)
-{
- int nr = (int)tv_get_number_chk(vp, NULL);
-
- if (nr >= LOWEST_WIN_ID) {
- return win_id2wp((int)tv_get_number(vp));
- }
-
- return find_win_by_nr(vp, NULL);
-}
-
/// Implementation of map() and filter().
void filter_map(typval_T *argvars, typval_T *rettv, int map)
{
@@ -4792,22 +4787,22 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
int save_did_emsg;
int idx = 0;
+ // Always return the first argument, also on failure.
+ tv_copy(&argvars[0], rettv);
+
if (argvars[0].v_type == VAR_BLOB) {
- tv_copy(&argvars[0], rettv);
if ((b = argvars[0].vval.v_blob) == NULL) {
return;
}
} else if (argvars[0].v_type == VAR_LIST) {
- tv_copy(&argvars[0], rettv);
if ((l = argvars[0].vval.v_list) == NULL
|| (!map
- && var_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE))) {
+ && value_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE))) {
return;
}
} else if (argvars[0].v_type == VAR_DICT) {
- tv_copy(&argvars[0], rettv);
if ((d = argvars[0].vval.v_dict) == NULL
- || (!map && var_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) {
+ || (!map && value_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) {
return;
}
} else {
@@ -4846,12 +4841,12 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
dictitem_T *di = TV_DICT_HI2DI(hi);
if (map
- && (var_check_lock(di->di_tv.v_lock, arg_errmsg, TV_TRANSLATE)
+ && (value_check_lock(di->di_tv.v_lock, arg_errmsg, TV_TRANSLATE)
|| var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE))) {
break;
}
- vimvars[VV_KEY].vv_str = (char *)vim_strsave(di->di_key);
+ vimvars[VV_KEY].vv_str = xstrdup((char *)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) {
@@ -4886,7 +4881,7 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
}
if (map) {
if (tv.vval.v_number != val) {
- tv_blob_set(b, i, (char_u)tv.vval.v_number);
+ tv_blob_set(b, i, (uint8_t)tv.vval.v_number);
}
} else if (rem) {
char *const p = argvars[0].vval.v_blob->bv_ga.ga_data;
@@ -4906,8 +4901,8 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
}
for (listitem_T *li = tv_list_first(l); li != NULL;) {
if (map
- && var_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg,
- TV_TRANSLATE)) {
+ && value_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg,
+ TV_TRANSLATE)) {
break;
}
vimvars[VV_KEY].vv_nr = idx;
@@ -4968,7 +4963,7 @@ theend:
return retval;
}
-void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref, FunPtr fptr)
+void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref)
{
char *s;
char *name;
@@ -4993,9 +4988,8 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref, FunPtr
if ((use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL) || is_funcref) {
name = s;
- trans_name = (char *)trans_function_name(&name, false,
- TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD
- | TFN_NO_DEREF, NULL, NULL);
+ trans_name = save_function_name(&name, false,
+ TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DEREF, NULL);
if (*name != NUL) {
s = NULL;
}
@@ -5008,26 +5002,19 @@ 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((char_u *)trans_name) == NULL
+ ? find_func(trans_name) == NULL
: !translated_function_exists((const char *)trans_name))) {
semsg(_("E700: Unknown function: %s"), s);
} else {
int dict_idx = 0;
int arg_idx = 0;
list_T *list = NULL;
- if (STRNCMP(s, "s:", 2) == 0 || STRNCMP(s, "<SID>", 5) == 0) {
- char sid_buf[25];
- int off = *s == 's' ? 2 : 5;
-
+ if (strncmp(s, "s:", 2) == 0 || strncmp(s, "<SID>", 5) == 0) {
// Expand s: and <SID> into <SNR>nr_, so that the function can
// also be called from another script. Using trans_function_name()
// would also work, but some plugins depend on the name being
// printable text.
- snprintf(sid_buf, sizeof(sid_buf), "<SNR>%" PRId64 "_",
- (int64_t)current_sctx.sc_sid);
- name = xmalloc(STRLEN(sid_buf) + STRLEN(s + off) + 1);
- STRCPY(name, sid_buf);
- STRCAT(name, s + off);
+ name = get_scriptlocal_funcname(s);
} else {
name = xstrdup(s);
}
@@ -5065,7 +5052,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, (char_u *)s);
+ emsg_funcname((char *)e_toomanyarg, s);
xfree(name);
goto theend;
}
@@ -5114,12 +5101,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((char_u *)trans_name);
+ pt->pt_func = find_func(trans_name);
func_ptr_ref(pt->pt_func);
xfree(name);
} else {
- pt->pt_name = (char_u *)name;
- func_ref((char_u *)name);
+ pt->pt_name = name;
+ func_ref(name);
}
rettv->v_type = VAR_PARTIAL;
@@ -5128,53 +5115,13 @@ 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((char_u *)name);
+ func_ref(name);
}
}
theend:
xfree(trans_name);
}
-/// @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();
-
- tv_dict_add_nr(dict, S_LEN("bufnr"), buf->b_fnum);
- tv_dict_add_str(dict, S_LEN("name"),
- buf->b_ffname != NULL ? (const char *)buf->b_ffname : "");
- tv_dict_add_nr(dict, S_LEN("lnum"),
- buf == curbuf ? curwin->w_cursor.lnum : buflist_findlnum(buf));
- tv_dict_add_nr(dict, S_LEN("linecount"), buf->b_ml.ml_line_count);
- tv_dict_add_nr(dict, S_LEN("loaded"), buf->b_ml.ml_mfp != NULL);
- tv_dict_add_nr(dict, S_LEN("listed"), buf->b_p_bl);
- tv_dict_add_nr(dict, S_LEN("changed"), bufIsChanged(buf));
- tv_dict_add_nr(dict, S_LEN("changedtick"), buf_get_changedtick(buf));
- tv_dict_add_nr(dict, S_LEN("hidden"),
- buf->b_ml.ml_mfp != NULL && buf->b_nwindows == 0);
-
- // Get a reference to buffer variables
- tv_dict_add_dict(dict, S_LEN("variables"), buf->b_vars);
-
- // List of windows displaying this buffer
- list_T *const windows = tv_list_alloc(kListLenMayKnow);
- FOR_ALL_TAB_WINDOWS(tp, wp) {
- if (wp->w_buffer == buf) {
- tv_list_append_number(windows, (varnumber_T)wp->handle);
- }
- }
- tv_dict_add_list(dict, S_LEN("windows"), windows);
-
- if (buf->b_signlist != NULL) {
- // List of signs placed in this buffer
- tv_dict_add_list(dict, S_LEN("signs"), get_buffer_signs(buf));
- }
-
- tv_dict_add_nr(dict, S_LEN("lastused"), buf->b_last_used);
-
- return dict;
-}
-
/// Get the line number from VimL object
///
/// @note Unlike tv_get_lnum(), this one supports only "$" special string.
@@ -5191,121 +5138,13 @@ linenr_T tv_get_lnum_buf(const typval_T *const tv, const buf_T *const buf)
if (tv->v_type == VAR_STRING
&& tv->vval.v_string != NULL
&& tv->vval.v_string[0] == '$'
+ && tv->vval.v_string[1] == NUL
&& buf != NULL) {
return buf->b_ml.ml_line_count;
}
return (linenr_T)tv_get_number_chk(tv, NULL);
}
-/// @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();
-
- tv_dict_add_nr(dict, S_LEN("tabnr"), tp_idx);
-
- list_T *const l = tv_list_alloc(kListLenMayKnow);
- FOR_ALL_WINDOWS_IN_TAB(wp, tp) {
- tv_list_append_number(l, (varnumber_T)wp->handle);
- }
- tv_dict_add_list(dict, S_LEN("windows"), l);
-
- // Make a reference to tabpage variables
- tv_dict_add_dict(dict, S_LEN("variables"), tp->tp_vars);
-
- return dict;
-}
-
-/// @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);
- tv_dict_add_nr(dict, S_LEN("height"), wp->w_height);
- 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"), 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);
- tv_dict_add_nr(dict, S_LEN("textoff"), win_col_off(wp));
- tv_dict_add_nr(dict, S_LEN("terminal"), bt_terminal(wp->w_buffer));
- tv_dict_add_nr(dict, S_LEN("quickfix"), bt_quickfix(wp->w_buffer));
- tv_dict_add_nr(dict, S_LEN("loclist"),
- (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL));
-
- // Add a reference to window variables
- tv_dict_add_dict(dict, S_LEN("variables"), wp->w_vars);
-
- return dict;
-}
-
-/// Find window specified by "vp" in tabpage "tp".
-///
-/// @param tp NULL for current tab page
-win_T *find_win_by_nr(typval_T *vp, tabpage_T *tp)
-{
- int nr = (int)tv_get_number_chk(vp, NULL);
-
- if (nr < 0) {
- return NULL;
- }
-
- if (nr == 0) {
- return curwin;
- }
-
- // This method accepts NULL as an alias for curtab.
- if (tp == NULL) {
- tp = curtab;
- }
-
- FOR_ALL_WINDOWS_IN_TAB(wp, tp) {
- if (nr >= LOWEST_WIN_ID) {
- if (wp->handle == nr) {
- return wp;
- }
- } else if (--nr <= 0) {
- return wp;
- }
- }
- return NULL;
-}
-
-/// Find window specified by "wvp" in tabpage "tvp".
-win_T *find_tabwin(typval_T *wvp, typval_T *tvp)
-{
- win_T *wp = NULL;
- tabpage_T *tp = NULL;
-
- if (wvp->v_type != VAR_UNKNOWN) {
- if (tvp->v_type != VAR_UNKNOWN) {
- long n = tv_get_number(tvp);
- if (n >= 0) {
- tp = find_tabpage((int)n);
- }
- } else {
- tp = curtab;
- }
-
- if (tp != NULL) {
- wp = find_win_by_nr(wvp, tp);
- }
- } else {
- wp = curwin;
- }
-
- return wp;
-}
-
/// 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
@@ -5527,126 +5366,6 @@ void screenchar_adjust(ScreenGrid **grid, int *row, int *col)
*col -= (*grid)->comp_col;
}
-/// Set line or list of lines in buffer "buf".
-void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, const typval_T *lines,
- typval_T *rettv)
- FUNC_ATTR_NONNULL_ARG(4, 5)
-{
- linenr_T lnum = lnum_arg + (append ? 1 : 0);
- long added = 0;
- 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
- // loaded.
- if (buf == NULL || (!is_curbuf && buf->b_ml.ml_mfp == NULL) || lnum < 1) {
- rettv->vval.v_number = 1; // FAIL
- return;
- }
-
- if (!is_curbuf) {
- VIsual_active = false;
- curbuf_save = curbuf;
- curwin_save = curwin;
- curbuf = buf;
- find_win_for_curbuf();
- }
-
- linenr_T append_lnum;
- if (append) {
- // appendbufline() uses the line number below which we insert
- append_lnum = lnum - 1;
- } else {
- // setbufline() uses the line number above which we insert, we only
- // append if it's below the last line
- append_lnum = curbuf->b_ml.ml_line_count;
- }
-
- list_T *l = NULL;
- listitem_T *li = NULL;
- const char *line = NULL;
- if (lines->v_type == VAR_LIST) {
- l = lines->vval.v_list;
- li = tv_list_first(l);
- } else {
- line = tv_get_string_chk(lines);
- }
-
- // Default result is zero == OK.
- for (;;) {
- if (lines->v_type == VAR_LIST) {
- // List argument, get next string.
- if (li == NULL) {
- break;
- }
- line = tv_get_string_chk(TV_LIST_ITEM_TV(li));
- li = TV_LIST_ITEM_NEXT(l, li);
- }
-
- rettv->vval.v_number = 1; // FAIL
- if (line == NULL || lnum > curbuf->b_ml.ml_line_count + 1) {
- break;
- }
-
- // When coming here from Insert mode, sync undo, so that this can be
- // undone separately from what was previously inserted.
- if (u_sync_once == 2) {
- u_sync_once = 1; // notify that u_sync() was called
- u_sync(true);
- }
-
- if (!append && lnum <= curbuf->b_ml.ml_line_count) {
- // Existing line, replace it.
- int old_len = (int)STRLEN(ml_get(lnum));
- if (u_savesub(lnum) == OK
- && 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();
- }
- rettv->vval.v_number = 0; // OK
- }
- } else if (added > 0 || u_save(lnum - 1, lnum) == OK) {
- // append the line.
- added++;
- if (ml_append(lnum - 1, (char *)line, 0, false) == OK) {
- rettv->vval.v_number = 0; // OK
- }
- }
-
- if (l == NULL) { // only one string argument
- break;
- }
- lnum++;
- }
-
- if (added > 0) {
- appended_lines_mark(append_lnum, added);
-
- // Only adjust the cursor for buffers other than the current, unless it
- // is the current window. For curbuf and other windows it has been done
- // in mark_adjust_internal().
- FOR_ALL_TAB_WINDOWS(tp, wp) {
- if (wp->w_buffer == buf
- && (wp->w_buffer != curbuf || wp == curwin)
- && wp->w_cursor.lnum > append_lnum) {
- wp->w_cursor.lnum += (linenr_T)added;
- }
- }
- check_cursor_col();
- update_topline(curwin);
- }
-
- if (!is_curbuf) {
- curbuf = curbuf_save;
- curwin = curwin_save;
- VIsual_active = save_VIsual_active;
- }
-}
-
/// "stdpath()" helper for list results
void get_xdg_var_list(const XDGVarType xdg, typval_T *rettv)
FUNC_ATTR_NONNULL_ALL
@@ -5699,7 +5418,7 @@ void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, bool retlist
// get input to the shell command (if any), and its length
ptrdiff_t input_len;
- char *input = save_tv_as_string(&argvars[1], &input_len, false);
+ char *input = save_tv_as_string(&argvars[1], &input_len, false, false);
if (input_len < 0) {
assert(input == NULL);
return;
@@ -5783,7 +5502,8 @@ void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, bool retlist
}
}
-bool callback_from_typval(Callback *const callback, typval_T *const arg)
+/// Get a callback from "arg". It can be a Funcref or a function name.
+bool callback_from_typval(Callback *const callback, const typval_T *const arg)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
int r = OK;
@@ -5804,13 +5524,19 @@ bool callback_from_typval(Callback *const callback, typval_T *const arg)
callback->type = kCallbackNone;
callback->data.funcref = NULL;
} else {
- func_ref((char_u *)name);
- callback->data.funcref = xstrdup(name);
+ callback->data.funcref = NULL;
+ if (arg->v_type == VAR_STRING) {
+ callback->data.funcref = get_scriptlocal_funcname(name);
+ }
+ if (callback->data.funcref == NULL) {
+ callback->data.funcref = xstrdup(name);
+ }
+ func_ref(callback->data.funcref);
callback->type = kCallbackFuncref;
}
} else if (nlua_is_table_from_lua(arg)) {
// TODO(tjdvries): UnifiedCallback
- char *name = (char *)nlua_register_table_as_callable(arg);
+ char *name = nlua_register_table_as_callable(arg);
if (name != NULL) {
callback->data.funcref = xstrdup(name);
@@ -5833,6 +5559,7 @@ bool callback_from_typval(Callback *const callback, typval_T *const arg)
return true;
}
+/// @return whether the callback could be called.
bool callback_call(Callback *const callback, const int argcount_in, typval_T *const argvars_in,
typval_T *const rettv)
FUNC_ATTR_NONNULL_ALL
@@ -5844,7 +5571,7 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co
switch (callback->type) {
case kCallbackFuncref:
name = callback->data.funcref;
- int len = (int)STRLEN(name);
+ int len = (int)strlen(name);
if (len >= 6 && !memcmp(name, "v:lua.", 6)) {
name += 6;
len = check_luafunc_name(name, false);
@@ -5863,13 +5590,8 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co
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;
- }
+ rv = nlua_call_ref(callback->data.luaref, NULL, args, false, NULL);
+ return (rv.type == kObjectTypeBoolean && rv.data.boolean == true);
case kCallbackNone:
return false;
@@ -5880,15 +5602,15 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co
}
funcexe_T funcexe = FUNCEXE_INIT;
- funcexe.firstline = curwin->w_cursor.lnum;
- funcexe.lastline = curwin->w_cursor.lnum;
- funcexe.evaluate = true;
- funcexe.partial = partial;
+ funcexe.fe_firstline = curwin->w_cursor.lnum;
+ funcexe.fe_lastline = curwin->w_cursor.lnum;
+ funcexe.fe_evaluate = true;
+ funcexe.fe_partial = partial;
return call_func(name, -1, rettv, argcount_in, argvars_in, &funcexe);
}
-static bool set_ref_in_callback(Callback *callback, int copyID, ht_stack_T **ht_stack,
- list_stack_T **list_stack)
+bool set_ref_in_callback(Callback *callback, int copyID, ht_stack_T **ht_stack,
+ list_stack_T **list_stack)
{
typval_T tv;
switch (callback->type) {
@@ -5990,7 +5712,7 @@ void timer_due_cb(TimeWatcher *tw, void *data)
// Handle error message
if (called_emsg > called_emsg_before && did_emsg) {
timer->emsg_count++;
- if (current_exception != NULL) {
+ if (did_throw) {
discard_current_exception();
}
}
@@ -6191,9 +5913,10 @@ bool read_blob(FILE *const fd, blob_T *const blob)
/// @param[in] tv Value to store as a string.
/// @param[out] len Length of the resulting string or -1 on error.
/// @param[in] endnl If true, the output will end in a newline (if a list).
+/// @param[in] crlf If true, list items will be joined with CRLF (if a list).
/// @returns an allocated string if `tv` represents a VimL string, list, or
/// number; NULL otherwise.
-char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl)
+char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl, bool crlf)
FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
{
*len = 0;
@@ -6218,7 +5941,7 @@ char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl)
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 *p = (char *)ml_get_buf(buf, lnum, false); *p != NUL; p++) {
+ for (char *p = ml_get_buf(buf, lnum, false); *p != NUL; p++) {
*len += 1;
}
*len += 1;
@@ -6236,7 +5959,7 @@ char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl)
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 *p = (char *)ml_get_buf(buf, lnum, false); *p != NUL; p++) {
+ for (char *p = ml_get_buf(buf, lnum, false); *p != NUL; p++) {
*end++ = (*p == '\n') ? NUL : *p;
}
*end++ = '\n';
@@ -6250,20 +5973,23 @@ 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 += (ptrdiff_t)strlen(tv_get_string(TV_LIST_ITEM_TV(li))) + 1;
+ *len += (ptrdiff_t)strlen(tv_get_string(TV_LIST_ITEM_TV(li))) + (crlf ? 2 : 1);
});
if (*len == 0) {
return NULL;
}
- char *ret = xmalloc((size_t)(*len) + endnl);
+ char *ret = xmalloc((size_t)(*len) + (endnl ? (crlf ? 2 : 1) : 0));
char *end = ret;
TV_LIST_ITER_CONST(list, li, {
for (const char *s = tv_get_string(TV_LIST_ITEM_TV(li)); *s != NUL; s++) {
*end++ = (*s == '\n') ? NUL : *s;
}
if (endnl || TV_LIST_ITEM_NEXT(list, li) != NULL) {
+ if (crlf) {
+ *end++ = '\r';
+ }
*end++ = '\n';
}
});
@@ -6285,7 +6011,7 @@ int buf_byteidx_to_charidx(buf_T *buf, linenr_T lnum, int byteidx)
lnum = buf->b_ml.ml_line_count;
}
- char *str = (char *)ml_get_buf(buf, lnum, false);
+ char *str = ml_get_buf(buf, lnum, false);
if (*str == NUL) {
return 0;
@@ -6323,7 +6049,7 @@ int buf_charidx_to_byteidx(buf_T *buf, linenr_T lnum, int charidx)
lnum = buf->b_ml.ml_line_count;
}
- char *str = (char *)ml_get_buf(buf, lnum, false);
+ char *str = ml_get_buf(buf, lnum, false);
// Convert the character offset to a byte offset
char *t = str;
@@ -6376,14 +6102,14 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
if (charcol) {
len = mb_charlen(ml_get(pos.lnum));
} else {
- len = (int)STRLEN(ml_get(pos.lnum));
+ len = (int)strlen(ml_get(pos.lnum));
}
// We accept "$" for the column number: last column.
listitem_T *li = tv_list_find(l, 1L);
if (li != NULL && TV_LIST_ITEM_TV(li)->v_type == VAR_STRING
&& TV_LIST_ITEM_TV(li)->vval.v_string != NULL
- && STRCMP(TV_LIST_ITEM_TV(li)->vval.v_string, "$") == 0) {
+ && strcmp(TV_LIST_ITEM_TV(li)->vval.v_string, "$") == 0) {
pos.col = len + 1;
}
@@ -6462,7 +6188,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
if (charcol) {
pos.col = (colnr_T)mb_charlen(get_cursor_line_ptr());
} else {
- pos.col = (colnr_T)STRLEN(get_cursor_line_ptr());
+ pos.col = (colnr_T)strlen(get_cursor_line_ptr());
}
}
return &pos;
@@ -6470,11 +6196,14 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
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.
+/// Convert list in "arg" into position "posp" and optional file number "fnump".
+/// When "fnump" is NULL there is no file number, only 3 items: [lnum, col, off]
/// Note that the column is passed on as-is, the caller may want to decrement
/// it to use 1 for the first column.
///
+/// @param charcol if true, use the column as the character index instead of the
+/// byte index.
+///
/// @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)
@@ -6514,13 +6243,16 @@ int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, bool c
return FAIL;
}
// If character position is specified, then convert to byte position
+ // If the line number is zero use the cursor line.
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;
+ n = buf_charidx_to_byteidx(buf,
+ posp->lnum == 0 ? curwin->w_cursor.lnum : posp->lnum,
+ (int)n) + 1;
}
posp->col = (colnr_T)n;
@@ -6545,7 +6277,7 @@ int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, bool c
int get_env_len(const char **arg)
{
const char *p;
- for (p = *arg; vim_isIDc(*p); p++) {}
+ for (p = *arg; vim_isIDc((uint8_t)(*p)); p++) {}
if (p == *arg) { // No name found.
return 0;
}
@@ -6572,7 +6304,7 @@ int get_id_len(const char **const arg)
// slice "[n:]". Also "xx:" is not a namespace.
len = (int)(p - *arg);
if (len > 1
- || (len == 1 && vim_strchr(namespace_char, **arg) == NULL)) {
+ || (len == 1 && vim_strchr(namespace_char, (uint8_t)(**arg)) == NULL)) {
break;
}
}
@@ -6632,7 +6364,7 @@ int get_name_len(const char **const arg, char **alias, bool evaluate, bool verbo
}
*alias = temp_string;
*arg = (const char *)skipwhite(p);
- return (int)STRLEN(temp_string);
+ return (int)strlen(temp_string);
}
len += get_id_len(arg);
@@ -6675,7 +6407,8 @@ const char *find_name_end(const char *arg, const char **expr_start, const char *
for (p = arg; *p != NUL
&& (eval_isnamec(*p)
|| *p == '{'
- || ((flags & FNE_INCL_BR) && (*p == '[' || *p == '.'))
+ || ((flags & FNE_INCL_BR) && (*p == '['
+ || (*p == '.' && eval_isdictc(p[1]))))
|| mb_nest != 0
|| br_nest != 0); MB_PTR_ADV(p)) {
if (*p == '\'') {
@@ -6699,7 +6432,7 @@ const char *find_name_end(const char *arg, const char **expr_start, const char *
// slice "[n:]". Also "xx:" is not a namespace. But {ns}: is.
len = (int)(p - arg);
if ((len > 1 && p[-1] != '}')
- || (len == 1 && vim_strchr(namespace_char, *arg) == NULL)) {
+ || (len == 1 && vim_strchr(namespace_char, (uint8_t)(*arg)) == NULL)) {
break;
}
}
@@ -6758,7 +6491,7 @@ static char *make_expanded_name(const char *in_start, char *expr_start, char *ex
char *temp_result = eval_to_string(expr_start + 1, &nextcmd, false);
if (temp_result != NULL && nextcmd == NULL) {
- retval = xmalloc(STRLEN(temp_result) + (size_t)(expr_start - in_start)
+ 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);
@@ -6786,20 +6519,27 @@ static char *make_expanded_name(const char *in_start, char *expr_start, char *ex
return retval;
}
-/// @return TRUE if character "c" can be used in a variable or function name.
+/// @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)
+bool 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
+/// @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)
+bool eval_isnamec1(int c)
{
return ASCII_ISALPHA(c) || c == '_';
}
+/// @return true if character "c" can be used as the first character of a
+/// dictionary key.
+bool eval_isdictc(int c)
+{
+ return ASCII_ISALNUM(c) || c == '_';
+}
+
/// Get typval_T v: variable value.
typval_T *get_vim_var_tv(int idx)
{
@@ -6846,7 +6586,7 @@ void set_vim_var_char(int c)
/// Set v:count to "count" and v:count1 to "count1".
///
-/// @param set_prevcount if TRUE, first set v:prevcount from v:count.
+/// @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) {
@@ -6932,12 +6672,13 @@ void set_vim_var_dict(const VimVarIndex idx, dict_T *const val)
tv_clear(&vimvars[idx].vv_di.di_tv);
vimvars[idx].vv_type = VAR_DICT;
vimvars[idx].vv_dict = val;
-
- if (val != NULL) {
- val->dv_refcount++;
- // Set readonly
- tv_dict_set_keys_readonly(val);
+ if (val == NULL) {
+ return;
}
+
+ val->dv_refcount++;
+ // Set readonly
+ tv_dict_set_keys_readonly(val);
}
/// Set the v:argv list.
@@ -7026,11 +6767,14 @@ char *set_cmdarg(exarg_T *eap, char *oldarg)
len += 10; // " ++ff=unix"
}
if (eap->force_enc != 0) {
- len += STRLEN(eap->cmd + eap->force_enc) + 7;
+ len += strlen(eap->cmd + eap->force_enc) + 7;
}
if (eap->bad_char != 0) {
len += 7 + 4; // " ++bad=" + "keep" or "drop"
}
+ if (eap->mkdir_p != 0) {
+ len += 4;
+ }
const size_t newval_len = len + 1;
char *newval = xmalloc(newval_len);
@@ -7048,22 +6792,27 @@ char *set_cmdarg(exarg_T *eap, char *oldarg)
}
if (eap->force_ff != 0) {
- snprintf(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(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) {
- STRCPY(newval + STRLEN(newval), " ++bad=keep");
+ STRCPY(newval + strlen(newval), " ++bad=keep");
} else if (eap->bad_char == BAD_DROP) {
- STRCPY(newval + STRLEN(newval), " ++bad=drop");
+ STRCPY(newval + strlen(newval), " ++bad=drop");
} else if (eap->bad_char != 0) {
- snprintf(newval + STRLEN(newval), newval_len, " ++bad=%c",
+ snprintf(newval + strlen(newval), newval_len, " ++bad=%c",
eap->bad_char);
}
+
+ if (eap->mkdir_p) {
+ snprintf(newval, newval_len, " ++p");
+ }
+
vimvars[VV_CMDARG].vv_str = newval;
return oldval;
}
@@ -7118,9 +6867,8 @@ int check_luafunc_name(const char *const str, const bool paren)
const char *const p = skip_luafunc_name(str);
if (*p != (paren ? '(' : NUL)) {
return 0;
- } else {
- return (int)(p - str);
}
+ return (int)(p - str);
}
/// Handle:
@@ -7405,7 +7153,7 @@ hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, const char
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);
+ char *sc_name = get_scriptname(last_set, &should_free);
new_script_item(sc_name, &current_sctx.sc_sid);
}
}
@@ -7532,9 +7280,9 @@ 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 = (char *)string_convert((vimconv_T *)conv,
- (char_u *)from->vval.v_string,
- NULL))
+ if ((to->vval.v_string = string_convert((vimconv_T *)conv,
+ from->vval.v_string,
+ NULL))
== NULL) {
to->vval.v_string = xstrdup(from->vval.v_string);
}
@@ -7644,7 +7392,7 @@ void ex_echo(exarg_T *eap)
tv_clear(&rettv);
arg = skipwhite(arg);
}
- eap->nextcmd = (char *)check_nextcmd((char_u *)arg);
+ eap->nextcmd = check_nextcmd(arg);
if (eap->skip) {
emsg_skip--;
@@ -7662,7 +7410,7 @@ void ex_echo(exarg_T *eap)
/// ":echohl {name}".
void ex_echohl(exarg_T *eap)
{
- echo_attr = syn_name2attr((char_u *)eap->arg);
+ echo_attr = syn_name2attr(eap->arg);
}
/// ":execute expr1 ..." execute the result of an expression.
@@ -7741,7 +7489,7 @@ void ex_execute(exarg_T *eap)
emsg_skip--;
}
- eap->nextcmd = (char *)check_nextcmd((char_u *)arg);
+ eap->nextcmd = check_nextcmd(arg);
}
/// Skip over the name of an option: "&option", "&g:option" or "&l:option".
@@ -7750,19 +7498,19 @@ void ex_execute(exarg_T *eap)
///
/// @return NULL when no option name found. Otherwise pointer to the char
/// after the option name.
-const char *find_option_end(const char **const arg, int *const opt_flags)
+const char *find_option_end(const char **const arg, int *const scope)
{
const char *p = *arg;
p++;
if (*p == 'g' && p[1] == ':') {
- *opt_flags = OPT_GLOBAL;
+ *scope = OPT_GLOBAL;
p += 2;
} else if (*p == 'l' && p[1] == ':') {
- *opt_flags = OPT_LOCAL;
+ *scope = OPT_LOCAL;
p += 2;
} else {
- *opt_flags = 0;
+ *scope = 0;
}
if (!ASCII_ISALPHA(*p)) {
@@ -7831,7 +7579,7 @@ bool script_autoload(const char *const name, const size_t name_len, const bool r
// "autoload/", it's always the same.
int i = 0;
for (; i < ga_loaded.ga_len; i++) {
- if (STRCMP(((char **)ga_loaded.ga_data)[i] + 9, scriptname + 9) == 0) {
+ if (strcmp(((char **)ga_loaded.ga_data)[i] + 9, scriptname + 9) == 0) {
break;
}
}
@@ -7866,9 +7614,8 @@ static var_flavour_T var_flavour(char *varname)
}
}
return VAR_FLAVOUR_SHADA;
- } else {
- return VAR_FLAVOUR_DEFAULT;
}
+ return VAR_FLAVOUR_DEFAULT;
}
/// Iterate over global variables
@@ -7894,7 +7641,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((char *)hi->hi_key) & flavour))) {
+ || !(var_flavour(hi->hi_key) & flavour))) {
hi++;
}
if ((size_t)(hi - hifirst) == hinum) {
@@ -7906,7 +7653,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((char *)hi->hi_key) & flavour)) {
+ if (!HASHITEM_EMPTY(hi) && (var_flavour(hi->hi_key) & flavour)) {
return hi;
}
}
@@ -7930,8 +7677,7 @@ int store_session_globals(FILE *fd)
&& 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 *const p = (char *)vim_strsave_escaped((const char_u *)tv_get_string(&this_var->di_tv),
- (const char_u *)"\\\"\n\r");
+ char *const p = (char *)vim_strsave_escaped(tv_get_string(&this_var->di_tv), "\\\"\n\r");
for (char *t = p; *t != NUL; t++) {
if (*t == '\n') {
*t = 'n';
@@ -7985,7 +7731,7 @@ void option_last_set_msg(LastSet last_set)
{
if (last_set.script_ctx.sc_sid != 0) {
bool should_free;
- char *p = (char *)get_scriptname(last_set, &should_free);
+ char *p = get_scriptname(last_set, &should_free);
verbose_enter();
msg_puts(_("\n\tLast set from "));
msg_puts(p);
@@ -8072,8 +7818,8 @@ repeat:
}
// FullName_save() is slow, don't use it when not needed.
- if (*p != NUL || !vim_isAbsName((char_u *)(*fnamep))) {
- *fnamep = FullName_save((*fnamep), *p != NUL);
+ if (*p != NUL || !vim_isAbsName(*fnamep)) {
+ *fnamep = FullName_save(*fnamep, *p != NUL);
xfree(*bufp); // free any allocated file name
*bufp = *fnamep;
if (*fnamep == NULL) {
@@ -8082,9 +7828,9 @@ repeat:
}
// Append a path separator to a directory.
- if (os_isdir((char_u *)(*fnamep))) {
+ if (os_isdir(*fnamep)) {
// Make room for one or two extra characters.
- *fnamep = xstrnsave(*fnamep, STRLEN(*fnamep) + 2);
+ *fnamep = xstrnsave(*fnamep, strlen(*fnamep) + 2);
xfree(*bufp); // free any allocated file name
*bufp = *fnamep;
add_pathsep(*fnamep);
@@ -8118,17 +7864,17 @@ repeat:
if (p != NULL) {
if (c == '.') {
- os_dirname((char_u *)dirname, MAXPATHL);
+ os_dirname(dirname, MAXPATHL);
if (has_homerelative) {
s = xstrdup(dirname);
home_replace(NULL, s, dirname, MAXPATHL, true);
xfree(s);
}
- size_t namelen = STRLEN(dirname);
+ 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) {
+ if (path_fnamencmp(p, dirname, namelen) == 0) {
p += namelen;
if (vim_ispathsep(*p)) {
while (*p && vim_ispathsep(*p)) {
@@ -8148,6 +7894,7 @@ repeat:
// Only replace it when it starts with '~'
if (*dirname == '~') {
s = xstrdup(dirname);
+ assert(s != NULL); // suppress clang "Argument with 'nonnull' attribute passed null"
*fnamep = s;
xfree(*bufp);
*bufp = s;
@@ -8159,14 +7906,14 @@ repeat:
}
char *tail = path_tail(*fnamep);
- *fnamelen = STRLEN(*fnamep);
+ *fnamelen = strlen(*fnamep);
// ":h" - head, remove "/file_name", can be repeated
// Don't remove the first "/" or "c:\"
while (src[*usedlen] == ':' && src[*usedlen + 1] == 'h') {
valid |= VALID_HEAD;
*usedlen += 2;
- s = (char *)get_past_head((char_u *)(*fnamep));
+ s = get_past_head(*fnamep);
while (tail > s && after_pathsep(s, tail)) {
MB_PTR_BACK(*fnamep, tail);
}
@@ -8261,7 +8008,7 @@ repeat:
s++;
}
- int sep = (char_u)(*s++);
+ int sep = (uint8_t)(*s++);
if (sep) {
// find end of pattern
p = vim_strchr(s, sep);
@@ -8276,7 +8023,7 @@ repeat:
*usedlen = (size_t)(p + 1 - src);
s = do_string_sub(str, pat, sub, NULL, flags);
*fnamep = s;
- *fnamelen = STRLEN(s);
+ *fnamelen = strlen(s);
xfree(*bufp);
*bufp = s;
didit = true;
@@ -8294,17 +8041,17 @@ repeat:
if (src[*usedlen] == ':' && src[*usedlen + 1] == 'S') {
// vim_strsave_shellescape() needs a NUL terminated string.
- c = (char_u)(*fnamep)[*fnamelen];
+ c = (uint8_t)(*fnamep)[*fnamelen];
if (c != NUL) {
(*fnamep)[*fnamelen] = NUL;
}
- p = (char *)vim_strsave_shellescape((char_u *)(*fnamep), false, false);
+ p = vim_strsave_shellescape(*fnamep, false, false);
if (c != NUL) {
(*fnamep)[*fnamelen] = (char)c;
}
xfree(*bufp);
*bufp = *fnamep = p;
- *fnamelen = STRLEN(p);
+ *fnamelen = strlen(p);
*usedlen += 2;
}
@@ -8316,7 +8063,7 @@ repeat:
/// "flags" can be "g" to do a global substitute.
///
/// @return an allocated string, NULL for error.
-char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, char *flags)
+char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char *flags)
{
int sublen;
regmatch_T regmatch;
@@ -8325,7 +8072,7 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, char *flags
// Make 'cpoptions' empty, so that the 'l' flag doesn't work here
char *save_cpo = p_cpo;
- p_cpo = (char *)empty_option;
+ p_cpo = empty_option;
ga_init(&ga, 1, 200);
@@ -8335,11 +8082,11 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, char *flags
regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
if (regmatch.regprog != NULL) {
char *tail = str;
- char *end = str + STRLEN(str);
- while (vim_regexec_nl(&regmatch, (char_u *)str, (colnr_T)(tail - str))) {
+ char *end = str + strlen(str);
+ while (vim_regexec_nl(&regmatch, str, (colnr_T)(tail - str))) {
// Skip empty match except for first match.
if (regmatch.startp[0] == regmatch.endp[0]) {
- if ((char_u *)zero_width == regmatch.startp[0]) {
+ if (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);
@@ -8347,7 +8094,7 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, char *flags
tail += i;
continue;
}
- zero_width = (char *)regmatch.startp[0];
+ zero_width = regmatch.startp[0];
}
// Get some space for a temporary buffer to do the substitution
@@ -8355,19 +8102,19 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, char *flags
// - The text up to where the match is.
// - The substituted text.
// - The text after the match.
- sublen = vim_regsub(&regmatch, (char_u *)sub, expr, (char_u *)tail, 0, REGSUB_MAGIC);
+ sublen = vim_regsub(&regmatch, sub, expr, 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] - (char_u *)tail);
+ int i = (int)(regmatch.startp[0] - tail);
memmove((char_u *)ga.ga_data + ga.ga_len, tail, (size_t)i);
// add the substituted text
- (void)vim_regsub(&regmatch, (char_u *)sub, expr,
- (char_u *)ga.ga_data + ga.ga_len + i, sublen,
+ (void)vim_regsub(&regmatch, sub, expr,
+ (char *)ga.ga_data + ga.ga_len + i, sublen,
REGSUB_COPY | REGSUB_MAGIC);
ga.ga_len += i + sublen - 1;
- tail = (char *)regmatch.endp[0];
+ tail = regmatch.endp[0];
if (*tail == NUL) {
break;
}
@@ -8385,11 +8132,16 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, char *flags
char *ret = xstrdup(ga.ga_data == NULL ? str : ga.ga_data);
ga_clear(&ga);
- if ((char_u *)p_cpo == empty_option) {
+ if (p_cpo == empty_option) {
p_cpo = save_cpo;
} else {
// Darn, evaluating {sub} expression or {expr} changed the value.
- free_string_option((char_u *)save_cpo);
+ // If it's still empty it was changed and restored, need to restore in
+ // the complicated way.
+ if (*p_cpo == NUL) {
+ set_option_value_give_err("cpo", 0L, save_cpo, 0);
+ }
+ free_string_option(save_cpo);
}
return ret;
@@ -8496,9 +8248,9 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments, boo
tv_list_ref(arguments);
funcexe_T funcexe = FUNCEXE_INIT;
- funcexe.firstline = curwin->w_cursor.lnum;
- funcexe.lastline = curwin->w_cursor.lnum;
- funcexe.evaluate = true;
+ funcexe.fe_firstline = curwin->w_cursor.lnum;
+ funcexe.fe_lastline = curwin->w_cursor.lnum;
+ funcexe.fe_evaluate = true;
(void)call_func(func, name_len, &rettv, 2, argvars, &funcexe);
tv_list_unref(arguments);
@@ -8547,7 +8299,7 @@ bool eval_has_provider(const char *feat)
if (get_var_tv(buf, len, &tv, NULL, false, true) == FAIL) {
// Show a hint if Call() is defined but g:loaded_xx_provider is missing.
snprintf(buf, sizeof(buf), "provider#%s#Call", name);
- if (!!find_func((char_u *)buf) && p_lpl) {
+ if (!!find_func(buf) && p_lpl) {
semsg("provider: %s: missing required variable g:loaded_%s_provider",
name, name);
}
@@ -8562,7 +8314,7 @@ bool eval_has_provider(const char *feat)
if (ok) {
// Call() must be defined if provider claims to be working.
snprintf(buf, sizeof(buf), "provider#%s#Call", name);
- if (!find_func((char_u *)buf)) {
+ if (!find_func(buf)) {
semsg("provider: %s: g:loaded_%s_provider=2 but %s is not defined",
name, name, buf);
ok = false;
@@ -8585,17 +8337,17 @@ void eval_fmt_source_name_line(char *buf, size_t bufsize)
/// ":checkhealth [plugins]"
void ex_checkhealth(exarg_T *eap)
{
- bool found = !!find_func((char_u *)"health#check");
+ bool found = !!find_func("health#check");
if (!found
&& script_autoload("health#check", sizeof("health#check") - 1, false)) {
- found = !!find_func((char_u *)"health#check");
+ found = !!find_func("health#check");
}
if (!found) {
const char *vimruntime_env = os_getenv("VIMRUNTIME");
if (vimruntime_env == NULL) {
emsg(_("E5009: $VIMRUNTIME is empty or unset"));
} else {
- bool rtp_ok = NULL != strstr((char *)p_rtp, vimruntime_env);
+ bool rtp_ok = NULL != strstr(p_rtp, vimruntime_env);
if (rtp_ok) {
semsg(_("E5009: Invalid $VIMRUNTIME: %s"), vimruntime_env);
} else {
@@ -8605,7 +8357,7 @@ void ex_checkhealth(exarg_T *eap)
return;
}
- size_t bufsize = STRLEN(eap->arg) + sizeof("call health#check('')");
+ size_t bufsize = strlen(eap->arg) + sizeof("call health#check('')");
char *buf = xmalloc(bufsize);
snprintf(buf, bufsize, "call health#check('%s')", eap->arg);
@@ -8629,10 +8381,10 @@ void invoke_prompt_callback(void)
if (curbuf->b_prompt_callback.type == kCallbackNone) {
return;
}
- char *text = (char *)ml_get(lnum);
- char *prompt = (char *)prompt_text();
- if (STRLEN(text) >= STRLEN(prompt)) {
- text += STRLEN(prompt);
+ char *text = ml_get(lnum);
+ char *prompt = prompt_text();
+ if (strlen(text) >= strlen(prompt)) {
+ text += strlen(prompt);
}
argv[0].v_type = VAR_STRING;
argv[0].vval.v_string = xstrdup(text);
@@ -8655,9 +8407,9 @@ bool invoke_prompt_interrupt(void)
argv[0].v_type = VAR_UNKNOWN;
got_int = false; // don't skip executing commands
- callback_call(&curbuf->b_prompt_interrupt, 0, argv, &rettv);
+ int ret = callback_call(&curbuf->b_prompt_interrupt, 0, argv, &rettv);
tv_clear(&rettv);
- return true;
+ return ret != FAIL;
}
/// Compare "typ1" and "typ2". Put the result in "typ1".
@@ -8855,7 +8607,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 *)s2, (char *)s1, ic);
+ n1 = pattern_match(s2, s1, ic);
if (type == EXPR_NOMATCH) {
n1 = !n1;
}
@@ -8870,10 +8622,16 @@ int typval_compare(typval_T *typ1, typval_T *typ2, exprtype_T type, bool ic)
return OK;
}
-char *typval_tostring(typval_T *arg)
+/// Convert any type to a string, never give an error.
+/// When "quotes" is true add quotes to a string.
+/// Returns an allocated string.
+char *typval_tostring(typval_T *arg, bool quotes)
{
if (arg == NULL) {
return xstrdup("(does not exist)");
}
+ if (!quotes && arg->v_type == VAR_STRING) {
+ return xstrdup(arg->vval.v_string == NULL ? "" : arg->vval.v_string);
+ }
return encode_tv2string(arg, NULL);
}
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
index b0cb5fd8c1..86bc76e793 100644
--- a/src/nvim/eval.h
+++ b/src/nvim/eval.h
@@ -1,43 +1,46 @@
#ifndef NVIM_EVAL_H
#define NVIM_EVAL_H
+#include <stdbool.h>
+#include <stddef.h>
+
#include "nvim/buffer_defs.h"
#include "nvim/channel.h"
-#include "nvim/event/time.h" // For TimeWatcher
-#include "nvim/ex_cmds_defs.h" // For exarg_T
-#include "nvim/os/fileio.h" // For FileDescriptor
-#include "nvim/os/stdpaths_defs.h" // For XDGVarType
+#include "nvim/eval/typval_defs.h"
+#include "nvim/event/time.h"
+#include "nvim/ex_cmds_defs.h"
+#include "nvim/hashtab.h"
+#include "nvim/os/fileio.h"
+#include "nvim/os/stdpaths_defs.h"
#define COPYID_INC 2
#define COPYID_MASK (~0x1)
-/*
- * Structure returned by get_lval() and used by set_var_lval().
- * For a plain name:
- * "name" points to the variable name.
- * "exp_name" is NULL.
- * "tv" is NULL
- * For a magic braces name:
- * "name" points to the expanded variable name.
- * "exp_name" is non-NULL, to be freed later.
- * "tv" is NULL
- * For an index in a list:
- * "name" points to the (expanded) variable name.
- * "exp_name" NULL or non-NULL, to be freed later.
- * "tv" points to the (first) list item value
- * "li" points to the (first) list item
- * "range", "n1", "n2" and "empty2" indicate what items are used.
- * For an existing Dict item:
- * "name" points to the (expanded) variable name.
- * "exp_name" NULL or non-NULL, to be freed later.
- * "tv" points to the dict item value
- * "newkey" is NULL
- * For a non-existing Dict item:
- * "name" points to the (expanded) variable name.
- * "exp_name" NULL or non-NULL, to be freed later.
- * "tv" points to the Dictionary typval_T
- * "newkey" is the key for the new item.
- */
+// Structure returned by get_lval() and used by set_var_lval().
+// For a plain name:
+// "name" points to the variable name.
+// "exp_name" is NULL.
+// "tv" is NULL
+// For a magic braces name:
+// "name" points to the expanded variable name.
+// "exp_name" is non-NULL, to be freed later.
+// "tv" is NULL
+// For an index in a list:
+// "name" points to the (expanded) variable name.
+// "exp_name" NULL or non-NULL, to be freed later.
+// "tv" points to the (first) list item value
+// "li" points to the (first) list item
+// "range", "n1", "n2" and "empty2" indicate what items are used.
+// For an existing Dict item:
+// "name" points to the (expanded) variable name.
+// "exp_name" NULL or non-NULL, to be freed later.
+// "tv" points to the dict item value
+// "newkey" is NULL
+// For a non-existing Dict item:
+// "name" points to the (expanded) variable name.
+// "exp_name" NULL or non-NULL, to be freed later.
+// "tv" points to the Dictionary typval_T
+// "newkey" is the key for the new item.
typedef struct lval_S {
const char *ll_name; ///< Start of variable name (can be NULL).
size_t ll_name_len; ///< Length of the .ll_name.
@@ -162,6 +165,8 @@ typedef enum {
VV__NULL_DICT, // Dictionary with NULL value. For test purposes only.
VV__NULL_BLOB, // Blob with NULL value. For test purposes only.
VV_LUA,
+ VV_RELNUM,
+ VV_VIRTNUM,
} VimVarIndex;
/// All recognized msgpack types
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index e4e9b34ec6..c17a44b990 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -23,7 +23,7 @@ end
return {
funcs={
abs={args=1, base=1},
- acos={args=1, base=1, func="float_op_wrapper", data="&acos"}, -- WJMc
+ acos={args=1, base=1, float_func="acos"}, -- WJMc
add={args=2, base=1},
['and']={args=2, base=1},
api_info={},
@@ -33,12 +33,12 @@ return {
argidx={},
arglistid={args={0, 2}},
argv={args={0, 2}},
- asin={args=1, base=1, func="float_op_wrapper", data="&asin"}, -- WJMc
+ asin={args=1, base=1, float_func="asin"}, -- WJMc
assert_beeps={args=1, base=1},
assert_equal={args={2, 3}, base=2},
assert_equalfile={args={2, 3}, base=1},
assert_exception={args={1, 2}},
- assert_fails={args={1, 3}, base=1},
+ assert_fails={args={1, 5}, base=1},
assert_false={args={1, 2}, base=1},
assert_inrange={args={3, 4}, base=3},
assert_match={args={2, 3}, base=2},
@@ -47,7 +47,7 @@ return {
assert_notmatch={args={2, 3}, base=2},
assert_report={args=1, base=1},
assert_true={args={1, 2}, base=1},
- atan={args=1, base=1, func="float_op_wrapper", data="&atan"},
+ atan={args=1, base=1, float_func="atan"},
atan2={args=2, base=1},
browse={args=4},
browsedir={args=2},
@@ -67,28 +67,27 @@ return {
byteidx={args=2, base=1},
byteidxcomp={args=2, base=1},
call={args={2, 3}, base=1},
- ceil={args=1, base=1, func="float_op_wrapper", data="&ceil"},
+ ceil={args=1, base=1, float_func="ceil"},
changenr={},
chanclose={args={1, 2}},
chansend={args=2},
char2nr={args={1, 2}, base=1},
charclass={args=1, base=1},
- charcol={args=1, base=1},
+ charcol={args={1, 2}, base=1},
charidx={args={2, 3}, base=1},
chdir={args=1, base=1},
cindent={args=1, base=1},
clearmatches={args={0, 1}, base=1},
- col={args=1, base=1},
+ col={args={1, 2}, base=1},
complete={args=2, base=2},
complete_add={args=1, base=1},
complete_check={},
complete_info={args={0, 1}, base=1},
confirm={args={1, 4}, base=1},
copy={args=1, base=1},
- cos={args=1, base=1, func="float_op_wrapper", data="&cos"},
- cosh={args=1, base=1, func="float_op_wrapper", data="&cosh"},
+ cos={args=1, base=1, float_func="cos"},
+ cosh={args=1, base=1, float_func="cosh"},
count={args={2, 4}, base=1},
- cscope_connection={args={0, 3}},
ctxget={args={0, 1}},
ctxpop={},
ctxpush={args={0, 1}},
@@ -117,9 +116,9 @@ return {
execute={args={1, 2}, base=1},
exepath={args=1, base=1},
exists={args=1, base=1},
- exp={args=1, base=1, func="float_op_wrapper", data="&exp"},
+ exp={args=1, base=1, float_func="exp"},
expand={args={1, 3}, base=1},
- expandcmd={args=1, base=1},
+ expandcmd={args={1, 2}, base=1},
extend={args={2, 3}, base=1},
feedkeys={args={1, 2}, base=1},
file_readable={args=1, base=1, func='f_filereadable'}, -- obsolete
@@ -130,7 +129,7 @@ return {
findfile={args={1, 3}, base=1},
flatten={args={1, 2}, base=1},
float2nr={args=1, base=1},
- floor={args=1, base=1, func="float_op_wrapper", data="&floor"},
+ floor={args=1, base=1, float_func="floor"},
fmod={args=2, base=1},
fnameescape={args=1, base=1},
fnamemodify={args=2, base=1},
@@ -147,7 +146,9 @@ return {
get={args={2, 3}, base=1},
getbufinfo={args={0, 1}, base=1},
getbufline={args={2, 3}, base=1},
+ getbufoneline={args=2, base=1},
getbufvar={args={2, 3}, base=1},
+ getcellwidths={},
getchangelist={args={0, 1}, base=1},
getchar={args={0, 1}},
getcharmod={},
@@ -186,6 +187,7 @@ return {
gettabvar={args={2, 3}, base=1},
gettabwinvar={args={3, 4}, base=1},
gettagstack={args={0, 1}, base=1},
+ gettext={args=1, base=1},
getwininfo={args={0, 1}, base=1},
getwinpos={args={0, 1}, base=1},
getwinposx={},
@@ -236,6 +238,7 @@ return {
json_decode={args=1, base=1},
json_encode={args=1, base=1},
keys={args=1, base=1},
+ keytrans={args=1, base=1},
last_buffer_nr={}, -- obsolete
len={args=1, base=1},
libcall={args=3, base=3},
@@ -245,8 +248,8 @@ return {
lispindent={args=1, base=1},
list2str={args={1, 2}, base=1},
localtime={},
- log={args=1, base=1, func="float_op_wrapper", data="&log"},
- log10={args=1, base=1, func="float_op_wrapper", data="&log10"},
+ log={args=1, base=1, float_func="log"},
+ log10={args=1, base=1, float_func="log10"},
luaeval={args={1, 2}, base=1},
map={args=2, base=1},
maparg={args={1, 4}, base=1},
@@ -290,6 +293,7 @@ return {
perleval={args=1, base=1},
rand={args={0, 1}, base=1},
range={args={1, 3}, base=1},
+ readblob={args=1, base=1},
readdir={args={1, 2}, base=1},
readfile={args={1, 3}, base=1},
reduce={args={2, 3}, base=1},
@@ -304,7 +308,7 @@ return {
['repeat']={args=2, base=1},
resolve={args=1, base=1},
reverse={args=1, base=1},
- round={args=1, base=1, func="float_op_wrapper", data="&round"},
+ round={args=1, base=1, float_func="round"},
rpcnotify={args=varargs(2)},
rpcrequest={args=varargs(2)},
rpcstart={args={1, 2}},
@@ -332,6 +336,7 @@ return {
setcharpos={args=2, base=2},
setcharsearch={args=1, base=1},
setcmdpos={args=1, base=1},
+ setcmdline={args={1, 2}, base=1},
setcursorcharpos={args={1, 3}, base=1},
setenv={args=2, base=2},
setfperm={args=2, base=1},
@@ -358,8 +363,8 @@ return {
sign_unplace={args={1, 2}, base=1},
sign_unplacelist={args=1, base=1},
simplify={args=1, base=1},
- sin={args=1, base=1, func="float_op_wrapper", data="&sin"},
- sinh={args=1, base=1, func="float_op_wrapper", data="&sinh"},
+ sin={args=1, base=1, float_func="sin"},
+ sinh={args=1, base=1, float_func="sinh"},
sockconnect={args={2,3}},
sort={args={1, 3}, base=1},
soundfold={args=1, base=1},
@@ -367,12 +372,13 @@ return {
spellbadword={args={0, 1}, base=1},
spellsuggest={args={1, 3}, base=1},
split={args={1, 3}, base=1},
- sqrt={args=1, base=1, func="float_op_wrapper", data="&sqrt"},
+ sqrt={args=1, base=1, float_func="sqrt"},
srand={args={0, 1}, base=1},
stdpath={args=1},
str2float={args=1, base=1},
str2list={args={1, 2}, base=1},
str2nr={args={1, 3}, base=1},
+ strcharlen={args=1, base=1},
strcharpart={args={2, 3}, base=1},
strchars={args={1, 2}, base=1},
strdisplaywidth={args={1, 2}, base=1},
@@ -402,8 +408,8 @@ return {
tabpagewinnr={args={1, 2}, base=1},
tagfiles={},
taglist={args={1, 2}, base=1},
- tan={args=1, base=1, func="float_op_wrapper", data="&tan"},
- tanh={args=1, base=1, func="float_op_wrapper", data="&tanh"},
+ tan={args=1, base=1, float_func="tan"},
+ tanh={args=1, base=1, float_func="tanh"},
tempname={},
termopen={args={1, 2}},
test_garbagecollect_now={},
@@ -417,13 +423,14 @@ return {
toupper={args=1, base=1},
tr={args=3, base=1},
trim={args={1, 3}, base=1},
- trunc={args=1, base=1, func="float_op_wrapper", data="&trunc"},
+ trunc={args=1, base=1, float_func="trunc"},
type={args=1, base=1},
undofile={args=1, base=1},
undotree={},
uniq={args={1, 3}, base=1},
values={args=1, base=1},
virtcol={args=1, base=1},
+ virtcol2col={args=3, base=1},
visualmode={args={0, 1}},
wait={args={2,3}},
wildmenumode={},
diff --git a/src/nvim/eval/buffer.c b/src/nvim/eval/buffer.c
new file mode 100644
index 0000000000..2f37d1ba2e
--- /dev/null
+++ b/src/nvim/eval/buffer.c
@@ -0,0 +1,734 @@
+// 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
+
+// eval/buffer.c: Buffer related builtin functions
+
+#include <stdbool.h>
+#include <string.h>
+
+#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
+#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
+#include "nvim/change.h"
+#include "nvim/cursor.h"
+#include "nvim/eval.h"
+#include "nvim/eval/buffer.h"
+#include "nvim/eval/funcs.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/globals.h"
+#include "nvim/macros.h"
+#include "nvim/memline.h"
+#include "nvim/memory.h"
+#include "nvim/move.h"
+#include "nvim/path.h"
+#include "nvim/pos.h"
+#include "nvim/sign.h"
+#include "nvim/types.h"
+#include "nvim/undo.h"
+#include "nvim/vim.h"
+
+typedef struct {
+ win_T *cob_curwin_save;
+ aco_save_T cob_aco;
+ int cob_using_aco;
+ int cob_save_VIsual_active;
+} cob_T;
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "eval/buffer.c.generated.h"
+#endif
+
+/// Find a buffer by number or exact name.
+buf_T *find_buffer(typval_T *avar)
+{
+ buf_T *buf = NULL;
+
+ if (avar->v_type == VAR_NUMBER) {
+ buf = buflist_findnr((int)avar->vval.v_number);
+ } else if (avar->v_type == VAR_STRING && avar->vval.v_string != NULL) {
+ buf = buflist_findname_exp(avar->vval.v_string);
+ if (buf == NULL) {
+ // No full path name match, try a match with a URL or a "nofile"
+ // buffer, these don't use the full path.
+ FOR_ALL_BUFFERS(bp) {
+ if (bp->b_fname != NULL
+ && (path_with_url(bp->b_fname) || bt_nofilename(bp))
+ && strcmp(bp->b_fname, avar->vval.v_string) == 0) {
+ buf = bp;
+ break;
+ }
+ }
+ }
+ }
+ return buf;
+}
+
+/// If there is a window for "curbuf", make it the current window.
+static void find_win_for_curbuf(void)
+{
+ wininfo_T *wip;
+
+ // The b_wininfo list should have the windows that recently contained the
+ // buffer, going over this is faster than going over all the windows.
+ // Do check the buffer is still there.
+ FOR_ALL_BUF_WININFO(curbuf, wip) {
+ if (wip->wi_win != NULL && wip->wi_win->w_buffer == curbuf) {
+ curwin = wip->wi_win;
+ break;
+ }
+ }
+}
+
+/// Used before making a change in "buf", which is not the current one: Make
+/// "buf" the current buffer and find a window for this buffer, so that side
+/// effects are done correctly (e.g., adjusting marks).
+///
+/// Information is saved in "cob" and MUST be restored by calling
+/// change_other_buffer_restore().
+static void change_other_buffer_prepare(cob_T *cob, buf_T *buf)
+{
+ CLEAR_POINTER(cob);
+
+ // Set "curbuf" to the buffer being changed. Then make sure there is a
+ // window for it to handle any side effects.
+ cob->cob_save_VIsual_active = VIsual_active;
+ VIsual_active = false;
+ cob->cob_curwin_save = curwin;
+ curbuf = buf;
+ find_win_for_curbuf(); // simplest: find existing window for "buf"
+
+ if (curwin->w_buffer != buf) {
+ // No existing window for this buffer. It is dangerous to have
+ // curwin->w_buffer differ from "curbuf", use the autocmd window.
+ curbuf = curwin->w_buffer;
+ aucmd_prepbuf(&cob->cob_aco, buf);
+ cob->cob_using_aco = true;
+ }
+}
+
+static void change_other_buffer_restore(cob_T *cob)
+{
+ if (cob->cob_using_aco) {
+ aucmd_restbuf(&cob->cob_aco);
+ } else {
+ curwin = cob->cob_curwin_save;
+ curbuf = curwin->w_buffer;
+ }
+ VIsual_active = cob->cob_save_VIsual_active;
+}
+
+/// Set line or list of lines in buffer "buf" to "lines".
+/// Any type is allowed and converted to a string.
+static void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, typval_T *lines,
+ typval_T *rettv)
+ FUNC_ATTR_NONNULL_ARG(4, 5)
+{
+ linenr_T lnum = lnum_arg + (append ? 1 : 0);
+ long added = 0;
+
+ // 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
+ // loaded.
+ const bool is_curbuf = buf == curbuf;
+ if (buf == NULL || (!is_curbuf && buf->b_ml.ml_mfp == NULL) || lnum < 1) {
+ rettv->vval.v_number = 1; // FAIL
+ return;
+ }
+
+ // After this don't use "return", goto "cleanup"!
+ cob_T cob;
+ if (!is_curbuf) {
+ // set "curbuf" to "buf" and find a window for this buffer
+ change_other_buffer_prepare(&cob, buf);
+ }
+
+ linenr_T append_lnum;
+ if (append) {
+ // appendbufline() uses the line number below which we insert
+ append_lnum = lnum - 1;
+ } else {
+ // setbufline() uses the line number above which we insert, we only
+ // append if it's below the last line
+ append_lnum = curbuf->b_ml.ml_line_count;
+ }
+
+ list_T *l = NULL;
+ listitem_T *li = NULL;
+ char *line = NULL;
+ if (lines->v_type == VAR_LIST) {
+ l = lines->vval.v_list;
+ if (l == NULL || tv_list_len(l) == 0) {
+ // set proper return code
+ if (lnum > curbuf->b_ml.ml_line_count) {
+ rettv->vval.v_number = 1; // FAIL
+ }
+ goto cleanup;
+ }
+ li = tv_list_first(l);
+ } else {
+ line = typval_tostring(lines, false);
+ }
+
+ // Default result is zero == OK.
+ for (;;) {
+ if (lines->v_type == VAR_LIST) {
+ // List argument, get next string.
+ if (li == NULL) {
+ break;
+ }
+ xfree(line);
+ line = typval_tostring(TV_LIST_ITEM_TV(li), false);
+ li = TV_LIST_ITEM_NEXT(l, li);
+ }
+
+ rettv->vval.v_number = 1; // FAIL
+ if (line == NULL || lnum > curbuf->b_ml.ml_line_count + 1) {
+ break;
+ }
+
+ // When coming here from Insert mode, sync undo, so that this can be
+ // undone separately from what was previously inserted.
+ if (u_sync_once == 2) {
+ u_sync_once = 1; // notify that u_sync() was called
+ u_sync(true);
+ }
+
+ if (!append && lnum <= curbuf->b_ml.ml_line_count) {
+ // Existing line, replace it.
+ int old_len = (int)strlen(ml_get(lnum));
+ if (u_savesub(lnum) == OK
+ && ml_replace(lnum, line, true) == OK) {
+ inserted_bytes(lnum, 0, old_len, (int)strlen(line));
+ if (is_curbuf && lnum == curwin->w_cursor.lnum) {
+ check_cursor_col();
+ }
+ rettv->vval.v_number = 0; // OK
+ }
+ } else if (added > 0 || u_save(lnum - 1, lnum) == OK) {
+ // append the line.
+ added++;
+ if (ml_append(lnum - 1, line, 0, false) == OK) {
+ rettv->vval.v_number = 0; // OK
+ }
+ }
+
+ if (l == NULL) { // only one string argument
+ break;
+ }
+ lnum++;
+ }
+ xfree(line);
+
+ if (added > 0) {
+ appended_lines_mark(append_lnum, added);
+
+ // Only adjust the cursor for buffers other than the current, unless it
+ // is the current window. For curbuf and other windows it has been done
+ // in mark_adjust_internal().
+ FOR_ALL_TAB_WINDOWS(tp, wp) {
+ if (wp->w_buffer == buf
+ && (wp->w_buffer != curbuf || wp == curwin)
+ && wp->w_cursor.lnum > append_lnum) {
+ wp->w_cursor.lnum += (linenr_T)added;
+ }
+ }
+ check_cursor_col();
+ update_topline(curwin);
+ }
+
+cleanup:
+ if (!is_curbuf) {
+ change_other_buffer_restore(&cob);
+ }
+}
+
+/// "append(lnum, string/list)" function
+void f_append(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ const int did_emsg_before = did_emsg;
+ const linenr_T lnum = tv_get_lnum(&argvars[0]);
+ if (did_emsg == did_emsg_before) {
+ set_buffer_lines(curbuf, lnum, true, &argvars[1], rettv);
+ }
+}
+
+/// Set or append lines to a buffer.
+static void buf_set_append_line(typval_T *argvars, typval_T *rettv, bool append)
+{
+ const int did_emsg_before = did_emsg;
+ buf_T *const buf = tv_get_buf(&argvars[0], false);
+ if (buf == NULL) {
+ rettv->vval.v_number = 1; // FAIL
+ } else {
+ const linenr_T lnum = tv_get_lnum_buf(&argvars[1], buf);
+ if (did_emsg == did_emsg_before) {
+ set_buffer_lines(buf, lnum, append, &argvars[2], rettv);
+ }
+ }
+}
+
+/// "appendbufline(buf, lnum, string/list)" function
+void f_appendbufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ buf_set_append_line(argvars, rettv, true);
+}
+
+/// "bufadd(expr)" function
+void f_bufadd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ char *name = (char *)tv_get_string(&argvars[0]);
+
+ rettv->vval.v_number = buflist_add(*name == NUL ? NULL : name, 0);
+}
+
+/// "bufexists(expr)" function
+void f_bufexists(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->vval.v_number = (find_buffer(&argvars[0]) != NULL);
+}
+
+/// "buflisted(expr)" function
+void f_buflisted(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ buf_T *buf;
+
+ buf = find_buffer(&argvars[0]);
+ rettv->vval.v_number = (buf != NULL && buf->b_p_bl);
+}
+
+/// "bufload(expr)" function
+void f_bufload(typval_T *argvars, typval_T *unused, EvalFuncData fptr)
+{
+ buf_T *buf = get_buf_arg(&argvars[0]);
+
+ if (buf != NULL) {
+ buffer_ensure_loaded(buf);
+ }
+}
+
+/// "bufloaded(expr)" function
+void f_bufloaded(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ buf_T *buf;
+
+ buf = find_buffer(&argvars[0]);
+ rettv->vval.v_number = (buf != NULL && buf->b_ml.ml_mfp != NULL);
+}
+
+/// "bufname(expr)" function
+void f_bufname(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ const buf_T *buf;
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+ if (argvars[0].v_type == VAR_UNKNOWN) {
+ buf = curbuf;
+ } else {
+ buf = tv_get_buf_from_arg(&argvars[0]);
+ }
+ if (buf != NULL && buf->b_fname != NULL) {
+ rettv->vval.v_string = xstrdup(buf->b_fname);
+ }
+}
+
+/// "bufnr(expr)" function
+void f_bufnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ const buf_T *buf;
+ bool error = false;
+
+ rettv->vval.v_number = -1;
+
+ if (argvars[0].v_type == VAR_UNKNOWN) {
+ buf = curbuf;
+ } else {
+ // Don't use tv_get_buf_from_arg(); we continue if the buffer wasn't found
+ // and the second argument isn't zero, but we want to return early if the
+ // first argument isn't a string or number so only one error is shown.
+ if (!tv_check_str_or_nr(&argvars[0])) {
+ return;
+ }
+ emsg_off++;
+ buf = tv_get_buf(&argvars[0], false);
+ emsg_off--;
+ }
+
+ // If the buffer isn't found and the second argument is not zero create a
+ // new buffer.
+ const char *name;
+ if (buf == NULL
+ && argvars[1].v_type != VAR_UNKNOWN
+ && tv_get_number_chk(&argvars[1], &error) != 0
+ && !error
+ && (name = tv_get_string_chk(&argvars[0])) != NULL) {
+ buf = buflist_new((char *)name, NULL, 1, 0);
+ }
+
+ if (buf != NULL) {
+ rettv->vval.v_number = buf->b_fnum;
+ }
+}
+
+static void buf_win_common(typval_T *argvars, typval_T *rettv, bool get_nr)
+{
+ const buf_T *const buf = tv_get_buf_from_arg(&argvars[0]);
+ if (buf == NULL) { // no need to search if invalid arg or buffer not found
+ rettv->vval.v_number = -1;
+ return;
+ }
+
+ int winnr = 0;
+ int winid;
+ bool found_buf = false;
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ winnr++;
+ if (wp->w_buffer == buf) {
+ found_buf = true;
+ winid = wp->handle;
+ break;
+ }
+ }
+ rettv->vval.v_number = (found_buf ? (get_nr ? winnr : winid) : -1);
+}
+
+/// "bufwinid(nr)" function
+void f_bufwinid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ buf_win_common(argvars, rettv, false);
+}
+
+/// "bufwinnr(nr)" function
+void f_bufwinnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ buf_win_common(argvars, rettv, true);
+}
+
+/// "deletebufline()" function
+void f_deletebufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ const int did_emsg_before = did_emsg;
+ rettv->vval.v_number = 1; // FAIL by default
+ buf_T *const buf = tv_get_buf(&argvars[0], false);
+ if (buf == NULL) {
+ return;
+ }
+
+ linenr_T last;
+ const linenr_T first = tv_get_lnum_buf(&argvars[1], buf);
+ if (did_emsg > did_emsg_before) {
+ return;
+ }
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ last = tv_get_lnum_buf(&argvars[2], buf);
+ } else {
+ last = first;
+ }
+
+ if (buf->b_ml.ml_mfp == NULL || first < 1
+ || first > buf->b_ml.ml_line_count || last < first) {
+ return;
+ }
+
+ // After this don't use "return", goto "cleanup"!
+ const bool is_curbuf = buf == curbuf;
+ cob_T cob;
+ if (!is_curbuf) {
+ // set "curbuf" to "buf" and find a window for this buffer
+ change_other_buffer_prepare(&cob, buf);
+ }
+
+ if (last > curbuf->b_ml.ml_line_count) {
+ last = curbuf->b_ml.ml_line_count;
+ }
+ const long count = last - first + 1;
+
+ // When coming here from Insert mode, sync undo, so that this can be
+ // undone separately from what was previously inserted.
+ if (u_sync_once == 2) {
+ u_sync_once = 1; // notify that u_sync() was called
+ u_sync(true);
+ }
+
+ if (u_save(first - 1, last + 1) == FAIL) {
+ goto cleanup;
+ }
+
+ for (linenr_T lnum = first; lnum <= last; lnum++) {
+ ml_delete(first, true);
+ }
+
+ FOR_ALL_TAB_WINDOWS(tp, wp) {
+ if (wp->w_buffer == buf) {
+ if (wp->w_cursor.lnum > last) {
+ wp->w_cursor.lnum -= (linenr_T)count;
+ } else if (wp->w_cursor.lnum > first) {
+ wp->w_cursor.lnum = first;
+ }
+ if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count) {
+ wp->w_cursor.lnum = wp->w_buffer->b_ml.ml_line_count;
+ }
+ }
+ }
+ check_cursor_col();
+ deleted_lines_mark(first, count);
+ rettv->vval.v_number = 0; // OK
+
+cleanup:
+ if (!is_curbuf) {
+ change_other_buffer_restore(&cob);
+ }
+}
+
+/// @return buffer options, variables and other attributes in a dictionary.
+static dict_T *get_buffer_info(buf_T *buf)
+{
+ dict_T *const dict = tv_dict_alloc();
+
+ tv_dict_add_nr(dict, S_LEN("bufnr"), buf->b_fnum);
+ tv_dict_add_str(dict, S_LEN("name"),
+ buf->b_ffname != NULL ? (const char *)buf->b_ffname : "");
+ tv_dict_add_nr(dict, S_LEN("lnum"),
+ buf == curbuf ? curwin->w_cursor.lnum : buflist_findlnum(buf));
+ tv_dict_add_nr(dict, S_LEN("linecount"), buf->b_ml.ml_line_count);
+ tv_dict_add_nr(dict, S_LEN("loaded"), buf->b_ml.ml_mfp != NULL);
+ tv_dict_add_nr(dict, S_LEN("listed"), buf->b_p_bl);
+ tv_dict_add_nr(dict, S_LEN("changed"), bufIsChanged(buf));
+ tv_dict_add_nr(dict, S_LEN("changedtick"), buf_get_changedtick(buf));
+ tv_dict_add_nr(dict, S_LEN("hidden"),
+ buf->b_ml.ml_mfp != NULL && buf->b_nwindows == 0);
+
+ // Get a reference to buffer variables
+ tv_dict_add_dict(dict, S_LEN("variables"), buf->b_vars);
+
+ // List of windows displaying this buffer
+ list_T *const windows = tv_list_alloc(kListLenMayKnow);
+ FOR_ALL_TAB_WINDOWS(tp, wp) {
+ if (wp->w_buffer == buf) {
+ tv_list_append_number(windows, (varnumber_T)wp->handle);
+ }
+ }
+ tv_dict_add_list(dict, S_LEN("windows"), windows);
+
+ if (buf->b_signlist != NULL) {
+ // List of signs placed in this buffer
+ tv_dict_add_list(dict, S_LEN("signs"), get_buffer_signs(buf));
+ }
+
+ tv_dict_add_nr(dict, S_LEN("lastused"), buf->b_last_used);
+
+ return dict;
+}
+
+/// "getbufinfo()" function
+void f_getbufinfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ buf_T *argbuf = NULL;
+ bool filtered = false;
+ bool sel_buflisted = false;
+ bool sel_bufloaded = false;
+ bool sel_bufmodified = false;
+
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
+
+ // List of all the buffers or selected buffers
+ if (argvars[0].v_type == VAR_DICT) {
+ dict_T *sel_d = argvars[0].vval.v_dict;
+
+ if (sel_d != NULL) {
+ dictitem_T *di;
+
+ filtered = true;
+
+ di = tv_dict_find(sel_d, S_LEN("buflisted"));
+ if (di != NULL && tv_get_number(&di->di_tv)) {
+ sel_buflisted = true;
+ }
+
+ di = tv_dict_find(sel_d, S_LEN("bufloaded"));
+ if (di != NULL && tv_get_number(&di->di_tv)) {
+ sel_bufloaded = true;
+ }
+ di = tv_dict_find(sel_d, S_LEN("bufmodified"));
+ if (di != NULL && tv_get_number(&di->di_tv)) {
+ sel_bufmodified = true;
+ }
+ }
+ } else if (argvars[0].v_type != VAR_UNKNOWN) {
+ // Information about one buffer. Argument specifies the buffer
+ argbuf = tv_get_buf_from_arg(&argvars[0]);
+ if (argbuf == NULL) {
+ return;
+ }
+ }
+
+ // Return information about all the buffers or a specified buffer
+ FOR_ALL_BUFFERS(buf) {
+ if (argbuf != NULL && argbuf != buf) {
+ continue;
+ }
+ if (filtered && ((sel_bufloaded && buf->b_ml.ml_mfp == NULL)
+ || (sel_buflisted && !buf->b_p_bl)
+ || (sel_bufmodified && !buf->b_changed))) {
+ continue;
+ }
+
+ dict_T *const d = get_buffer_info(buf);
+ tv_list_append_dict(rettv->vval.v_list, d);
+ if (argbuf != NULL) {
+ return;
+ }
+ }
+}
+
+/// 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);
+ rettv->vval.v_string = NULL;
+
+ if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0 || end < start) {
+ if (retlist) {
+ tv_list_alloc_ret(rettv, 0);
+ }
+ return;
+ }
+
+ if (retlist) {
+ if (start < 1) {
+ start = 1;
+ }
+ if (end > buf->b_ml.ml_line_count) {
+ end = buf->b_ml.ml_line_count;
+ }
+ tv_list_alloc_ret(rettv, end - start + 1);
+ while (start <= end) {
+ tv_list_append_string(rettv->vval.v_list,
+ (const char *)ml_get_buf(buf, start++, false), -1);
+ }
+ } else {
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = ((start >= 1 && start <= buf->b_ml.ml_line_count)
+ ? xstrdup(ml_get_buf(buf, start, false)) : NULL);
+ }
+}
+
+/// @param retlist true: "getbufline()" function
+/// false: "getbufoneline()" function
+static void getbufline(typval_T *argvars, typval_T *rettv, bool retlist)
+{
+ const int did_emsg_before = did_emsg;
+ buf_T *const buf = tv_get_buf_from_arg(&argvars[0]);
+ const linenr_T lnum = tv_get_lnum_buf(&argvars[1], buf);
+ if (did_emsg > did_emsg_before) {
+ return;
+ }
+ const linenr_T end = (argvars[2].v_type == VAR_UNKNOWN
+ ? lnum
+ : tv_get_lnum_buf(&argvars[2], buf));
+
+ get_buffer_lines(buf, lnum, end, retlist, rettv);
+}
+
+/// "getbufline()" function
+void f_getbufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ getbufline(argvars, rettv, true);
+}
+
+/// "getbufoneline()" function
+void f_getbufoneline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ getbufline(argvars, rettv, false);
+}
+
+/// "getline(lnum, [end])" function
+void f_getline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ linenr_T end;
+ bool retlist;
+
+ const linenr_T lnum = tv_get_lnum(argvars);
+ if (argvars[1].v_type == VAR_UNKNOWN) {
+ end = lnum;
+ retlist = false;
+ } else {
+ end = tv_get_lnum(&argvars[1]);
+ retlist = true;
+ }
+
+ get_buffer_lines(curbuf, lnum, end, retlist, rettv);
+}
+
+/// "setbufline()" function
+void f_setbufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ buf_set_append_line(argvars, rettv, false);
+}
+
+/// "setline()" function
+void f_setline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ const int did_emsg_before = did_emsg;
+ linenr_T lnum = tv_get_lnum(&argvars[0]);
+ if (did_emsg == did_emsg_before) {
+ set_buffer_lines(curbuf, lnum, false, &argvars[1], rettv);
+ }
+}
+
+/// Make "buf" the current buffer.
+///
+/// restore_buffer() MUST be called to undo.
+/// No autocommands will be executed. Use aucmd_prepbuf() if there are any.
+void switch_buffer(bufref_T *save_curbuf, buf_T *buf)
+{
+ block_autocmds();
+ set_bufref(save_curbuf, curbuf);
+ curbuf->b_nwindows--;
+ curbuf = buf;
+ curwin->w_buffer = buf;
+ curbuf->b_nwindows++;
+}
+
+/// Restore the current buffer after using switch_buffer().
+void restore_buffer(bufref_T *save_curbuf)
+{
+ unblock_autocmds();
+ // Check for valid buffer, just in case.
+ if (bufref_valid(save_curbuf)) {
+ curbuf->b_nwindows--;
+ curwin->w_buffer = save_curbuf->br_buf;
+ curbuf = save_curbuf->br_buf;
+ curbuf->b_nwindows++;
+ }
+}
+
+/// Find a window for buffer "buf".
+/// If found true is returned and "wp" and "tp" are set to
+/// the window and tabpage.
+/// If not found, false is returned.
+///
+/// @param buf buffer to find a window for
+/// @param[out] wp stores the found window
+/// @param[out] tp stores the found tabpage
+///
+/// @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;
+ *tp = NULL;
+ FOR_ALL_TAB_WINDOWS(tp2, wp2) {
+ if (wp2->w_buffer == buf) {
+ *tp = tp2;
+ *wp = wp2;
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/src/nvim/eval/buffer.h b/src/nvim/eval/buffer.h
new file mode 100644
index 0000000000..4a2f8f9e94
--- /dev/null
+++ b/src/nvim/eval/buffer.h
@@ -0,0 +1,10 @@
+#ifndef NVIM_EVAL_BUFFER_H
+#define NVIM_EVAL_BUFFER_H
+
+#include "nvim/buffer_defs.h"
+#include "nvim/eval/typval_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "eval/buffer.h.generated.h"
+#endif
+#endif // NVIM_EVAL_BUFFER_H
diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c
index 7b975ce775..cd1479f150 100644
--- a/src/nvim/eval/decode.c
+++ b/src/nvim/eval/decode.c
@@ -1,20 +1,31 @@
// 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 <msgpack.h>
+#include <assert.h>
+#include <msgpack/object.h>
+#include <stdbool.h>
#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include "klib/kvec.h"
#include "nvim/ascii.h"
-#include "nvim/charset.h" // vim_str2nr
+#include "nvim/charset.h"
#include "nvim/eval.h"
#include "nvim/eval/decode.h"
#include "nvim/eval/encode.h"
#include "nvim/eval/typval.h"
-#include "nvim/globals.h"
-#include "nvim/lib/kvec.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/hashtab.h"
#include "nvim/macros.h"
+#include "nvim/mbyte.h"
+#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/vim.h" // OK, FAIL
+#include "nvim/types.h"
+#include "nvim/vim.h"
/// Helper structure for container_struct
typedef struct {
@@ -271,11 +282,9 @@ typval_T decode_string(const char *const s, const size_t len, const TriState has
list_T *const list = tv_list_alloc(kListLenMayKnow);
tv_list_ref(list);
create_special_dict(&tv, kMPString,
- ((typval_T){
- .v_type = VAR_LIST,
- .v_lock = VAR_UNLOCKED,
- .vval = { .v_list = list },
- }));
+ (typval_T){ .v_type = VAR_LIST,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_list = list } });
const int elw_ret = encode_list_write((void *)list, s, len);
if (s_allocated) {
xfree((void *)s);
@@ -286,13 +295,12 @@ typval_T decode_string(const char *const s, const size_t len, const TriState has
}
}
return tv;
- } else {
- return (typval_T) {
- .v_type = VAR_STRING,
- .v_lock = VAR_UNLOCKED,
- .vval = { .v_string = ((s == NULL || s_allocated) ? (char *)s : xmemdupz(s, len)) },
- };
}
+ return (typval_T) {
+ .v_type = VAR_STRING,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_string = ((s == NULL || s_allocated) ? (char *)s : xmemdupz(s, len)) },
+ };
}
/// Parse JSON double-quoted string
@@ -368,7 +376,7 @@ static inline int parse_json_string(const char *const buf, const size_t buf_len,
goto parse_json_string_fail;
}
} else {
- uint8_t p_byte = (uint8_t)*p;
+ uint8_t p_byte = (uint8_t)(*p);
// unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
if (p_byte < 0x20) {
semsg(_("E474: ASCII control characters cannot be present "
@@ -430,7 +438,7 @@ static inline int parse_json_string(const char *const buf, const size_t buf_len,
const char ubuf[] = { t[1], t[2], t[3], t[4] };
t += 4;
uvarnumber_T ch;
- vim_str2nr((char_u *)ubuf, NULL, NULL,
+ vim_str2nr(ubuf, NULL, NULL,
STR2NR_HEX | STR2NR_FORCE, NULL, &ch, 4, true);
if (ch == 0) {
hasnul = true;
@@ -469,7 +477,7 @@ static inline int parse_json_string(const char *const buf, const size_t buf_len,
['r'] = CAR,
['f'] = FF,
};
- *str_end++ = escapes[(int)*t];
+ *str_end++ = escapes[(int)(*t)];
break;
}
default:
@@ -600,7 +608,7 @@ parse_json_number_check:
// Convert integer
varnumber_T nr;
int num_len;
- vim_str2nr((char_u *)s, NULL, &num_len, 0, &nr, NULL, (int)(p - s), true);
+ vim_str2nr(s, NULL, &num_len, 0, &nr, NULL, (int)(p - s), true);
if ((int)exp_num_len != num_len) {
semsg(_("E685: internal error: while converting number \"%.*s\" "
"to integer vim_str2nr consumed %i bytes in place of %zu"),
@@ -838,12 +846,10 @@ json_decode_string_cycle_start:
.v_lock = VAR_UNLOCKED,
.vval = { .v_list = list },
};
- kv_push(container_stack, ((ContainerStackItem) {
- .stack_index = kv_size(stack),
- .s = p,
- .container = tv,
- .special_val = NULL,
- }));
+ kv_push(container_stack, ((ContainerStackItem) { .stack_index = kv_size(stack),
+ .s = p,
+ .container = tv,
+ .special_val = NULL }));
kv_push(stack, OBJ(tv, false, didcomma, didcolon));
break;
}
@@ -862,12 +868,10 @@ json_decode_string_cycle_start:
.vval = { .v_dict = dict },
};
}
- kv_push(container_stack, ((ContainerStackItem) {
- .stack_index = kv_size(stack),
- .s = p,
- .container = tv,
- .special_val = val_list,
- }));
+ kv_push(container_stack, ((ContainerStackItem) { .stack_index = kv_size(stack),
+ .s = p,
+ .container = tv,
+ .special_val = val_list }));
kv_push(stack, OBJ(tv, false, didcomma, didcolon));
break;
}
@@ -1089,11 +1093,9 @@ msgpack_to_vim_generic_map: {}
tv_list_append_number(list, mobj.via.ext.type);
list_T *const ext_val_list = tv_list_alloc(kListLenMayKnow);
tv_list_append_list(list, ext_val_list);
- create_special_dict(rettv, kMPExt, ((typval_T) {
- .v_type = VAR_LIST,
- .v_lock = VAR_UNLOCKED,
- .vval = { .v_list = list },
- }));
+ create_special_dict(rettv, kMPExt, ((typval_T) { .v_type = VAR_LIST,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_list = list } }));
if (encode_list_write((void *)ext_val_list, mobj.via.ext.ptr,
mobj.via.ext.size) == -1) {
return FAIL;
diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c
index bb514fba47..c2f1eae8af 100644
--- a/src/nvim/eval/encode.c
+++ b/src/nvim/eval/encode.c
@@ -10,23 +10,28 @@
#include <assert.h>
#include <inttypes.h>
#include <math.h>
-#include <msgpack.h>
+#include <stdbool.h>
#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include "klib/kvec.h"
+#include "msgpack/pack.h"
#include "nvim/ascii.h"
-#include "nvim/buffer_defs.h"
-#include "nvim/charset.h" // vim_isprintc()
#include "nvim/eval.h"
#include "nvim/eval/encode.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/typval_encode.h"
#include "nvim/garray.h"
-#include "nvim/lib/kvec.h"
+#include "nvim/gettext.h"
+#include "nvim/hashtab.h"
#include "nvim/macros.h"
#include "nvim/math.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
+#include "nvim/strings.h"
+#include "nvim/types.h"
#include "nvim/vim.h" // For _()
const char *const encode_bool_var_names[] = {
@@ -131,35 +136,35 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack,
typval_T key_tv = {
.v_type = VAR_STRING,
.vval = { .v_string =
- (char *)(v.data.d.hi ==
- NULL ? v.data.d.dict->dv_hashtab.ht_array : (v.data.d.hi -
- 1))->hi_key },
+ (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);
+ vim_snprintf(IObuff, IOSIZE, key_msg, key);
xfree(key);
- ga_concat(&msg_ga, (char *)IObuff);
+ ga_concat(&msg_ga, IObuff);
break;
}
case kMPConvPairs:
case kMPConvList: {
const int idx = (v.data.l.li == tv_list_first(v.data.l.list)
- ? 0
- : (v.data.l.li == NULL
- ? tv_list_len(v.data.l.list) - 1
- : (int)tv_list_idx_of_item(v.data.l.list,
- TV_LIST_ITEM_PREV(v.data.l.list,
- v.data.l.li))));
+ ? 0
+ : (v.data.l.li == NULL
+ ? tv_list_len(v.data.l.list) - 1
+ : (int)tv_list_idx_of_item(v.data.l.list,
+ TV_LIST_ITEM_PREV(v.data.l.list,
+ v.data.l.li))));
const listitem_T *const li = (v.data.l.li == NULL
- ? tv_list_last(v.data.l.list)
- : TV_LIST_ITEM_PREV(v.data.l.list,
- v.data.l.li));
+ ? tv_list_last(v.data.l.list)
+ : TV_LIST_ITEM_PREV(v.data.l.list,
+ v.data.l.li));
if (v.type == kMPConvList
|| li == NULL
|| (TV_LIST_ITEM_TV(li)->v_type != VAR_LIST
&& tv_list_len(TV_LIST_ITEM_TV(li)->vval.v_list) <= 0)) {
- vim_snprintf((char *)IObuff, IOSIZE, idx_msg, idx);
- ga_concat(&msg_ga, (char *)IObuff);
+ vim_snprintf(IObuff, IOSIZE, idx_msg, idx);
+ ga_concat(&msg_ga, IObuff);
} else {
assert(li != NULL);
listitem_T *const first_item =
@@ -167,9 +172,9 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack,
assert(first_item != NULL);
typval_T key_tv = *TV_LIST_ITEM_TV(first_item);
char *const key = encode_tv2echo(&key_tv, NULL);
- vim_snprintf((char *)IObuff, IOSIZE, key_pair_msg, key, idx);
+ vim_snprintf(IObuff, IOSIZE, key_pair_msg, key, idx);
xfree(key);
- ga_concat(&msg_ga, (char *)IObuff);
+ ga_concat(&msg_ga, IObuff);
}
break;
}
@@ -188,8 +193,8 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack,
break;
case kMPConvPartialList: {
const int idx = (int)(v.data.a.arg - v.data.a.argv) - 1;
- vim_snprintf((char *)IObuff, IOSIZE, partial_arg_i_msg, idx);
- ga_concat(&msg_ga, (char *)IObuff);
+ vim_snprintf(IObuff, IOSIZE, partial_arg_i_msg, idx);
+ ga_concat(&msg_ga, IObuff);
break;
}
}
@@ -219,7 +224,7 @@ bool encode_vim_list_to_buf(const list_T *const list, size_t *const ret_len, cha
}
len++;
if (TV_LIST_ITEM_TV(li)->vval.v_string != NULL) {
- len += STRLEN(TV_LIST_ITEM_TV(li)->vval.v_string);
+ len += strlen(TV_LIST_ITEM_TV(li)->vval.v_string);
}
});
if (len) {
@@ -281,7 +286,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s
state->offset = 0;
state->li_length = (TV_LIST_ITEM_TV(state->li)->vval.v_string == NULL
? 0
- : STRLEN(TV_LIST_ITEM_TV(state->li)->vval.v_string));
+ : strlen(TV_LIST_ITEM_TV(state->li)->vval.v_string));
}
}
*read_bytes = nbuf;
@@ -304,7 +309,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s
if (buf_[i_] == '\'') { \
ga_append(gap, '\''); \
} \
- ga_append(gap, buf_[i_]); \
+ ga_append(gap, (uint8_t)buf_[i_]); \
} \
ga_append(gap, '\''); \
} \
diff --git a/src/nvim/eval/encode.h b/src/nvim/eval/encode.h
index 8755ff48ac..41e7614fc0 100644
--- a/src/nvim/eval/encode.h
+++ b/src/nvim/eval/encode.h
@@ -2,11 +2,15 @@
#define NVIM_EVAL_ENCODE_H
#include <msgpack.h>
+#include <msgpack/pack.h>
#include <stddef.h>
+#include <string.h>
#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/garray.h"
-#include "nvim/vim.h" // For STRLEN
+#include "nvim/vim.h"
/// Convert VimL value to msgpack string
///
@@ -15,9 +19,7 @@
/// @param[in] objname Object name, used for error message.
///
/// @return OK in case of success, FAIL otherwise.
-int encode_vim_to_msgpack(msgpack_packer *const packer,
- typval_T *const tv,
- const char *const objname);
+int encode_vim_to_msgpack(msgpack_packer *packer, typval_T *tv, const char *objname);
/// Convert VimL value to :echo output
///
@@ -26,9 +28,7 @@ int encode_vim_to_msgpack(msgpack_packer *const packer,
/// @param[in] objname Object name, used for error message.
///
/// @return OK in case of success, FAIL otherwise.
-int encode_vim_to_echo(garray_T *const packer,
- typval_T *const tv,
- const char *const objname);
+int encode_vim_to_echo(garray_T *packer, typval_T *tv, const char *objname);
/// Structure defining state for read_from_list()
typedef struct {
@@ -48,7 +48,7 @@ static inline ListReaderState encode_init_lrstate(const list_T *const list)
.offset = 0,
.li_length = (TV_LIST_ITEM_TV(tv_list_first(list))->vval.v_string == NULL
? 0
- : STRLEN(TV_LIST_ITEM_TV(tv_list_first(list))->vval.v_string)),
+ : strlen(TV_LIST_ITEM_TV(tv_list_first(list))->vval.v_string)),
};
}
diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c
index b461456a3a..9caea2fef1 100644
--- a/src/nvim/eval/executor.c
+++ b/src/nvim/eval/executor.c
@@ -1,15 +1,23 @@
// 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 <inttypes.h>
+#include <stdlib.h>
+
#include "nvim/eval.h"
#include "nvim/eval/executor.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/garray.h"
+#include "nvim/gettext.h"
#include "nvim/globals.h"
#include "nvim/message.h"
+#include "nvim/strings.h"
+#include "nvim/types.h"
#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "eval/executor.c.generated.h"
+# include "eval/executor.c.generated.h" // IWYU pragma: export
#endif
char *e_listidx = N_("E684: list index out of range: %" PRId64);
@@ -43,7 +51,7 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, const char *cons
blob_T *const b1 = tv1->vval.v_blob;
blob_T *const b2 = tv2->vval.v_blob;
for (int i = 0; i < tv_blob_len(b2); i++) {
- ga_append(&b1->bv_ga, (char)tv_blob_get(b2, i));
+ ga_append(&b1->bv_ga, tv_blob_get(b2, i));
}
}
return OK;
@@ -61,7 +69,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("+-*/%", *op) != NULL) {
+ if (vim_strchr("+-*/%", (uint8_t)(*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) {
@@ -108,8 +116,7 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, const char *cons
const char *tvs = tv_get_string(tv1);
char numbuf[NUMBUFLEN];
char *const s =
- (char *)concat_str((const char_u *)tvs, (const char_u *)tv_get_string_buf(tv2,
- numbuf));
+ concat_str(tvs, tv_get_string_buf(tv2, numbuf));
tv_clear(tv1);
tv1->v_type = VAR_STRING;
tv1->vval.v_string = s;
@@ -123,8 +130,8 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, const char *cons
break;
}
const float_T f = (tv2->v_type == VAR_FLOAT
- ? tv2->vval.v_float
- : (float_T)tv_get_number(tv2));
+ ? tv2->vval.v_float
+ : (float_T)tv_get_number(tv2));
switch (*op) {
case '+':
tv1->vval.v_float += f; break;
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 9f851653eb..a6dc41d0f3 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -1,27 +1,44 @@
// 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 <fcntl.h>
#include <float.h>
+#include <inttypes.h>
+#include <limits.h>
#include <math.h>
-
+#include <msgpack/object.h>
+#include <msgpack/pack.h>
+#include <msgpack/unpack.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <uv.h>
+
+#include "auto/config.h"
#include "nvim/api/private/converter.h"
+#include "nvim/api/private/defs.h"
+#include "nvim/api/private/dispatch.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/vim.h"
-#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/assert.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
-#include "nvim/change.h"
+#include "nvim/buffer_defs.h"
#include "nvim/channel.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
-#include "nvim/cmdhist.h"
#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/buffer.h"
#include "nvim/eval/decode.h"
#include "nvim/eval/encode.h"
#include "nvim/eval/executor.h"
@@ -29,55 +46,72 @@
#include "nvim/eval/typval.h"
#include "nvim/eval/userfunc.h"
#include "nvim/eval/vars.h"
+#include "nvim/eval/window.h"
+#include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
+#include "nvim/event/process.h"
+#include "nvim/event/time.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
#include "nvim/ex_getln.h"
#include "nvim/file_search.h"
#include "nvim/fileio.h"
-#include "nvim/fold.h"
+#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
#include "nvim/globals.h"
+#include "nvim/grid_defs.h"
+#include "nvim/hashtab.h"
+#include "nvim/highlight_defs.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/insexpand.h"
+#include "nvim/keycodes.h"
#include "nvim/lua/executor.h"
#include "nvim/macros.h"
-#include "nvim/mapping.h"
+#include "nvim/main.h"
#include "nvim/mark.h"
-#include "nvim/match.h"
#include "nvim/math.h"
+#include "nvim/mbyte.h"
+#include "nvim/memfile_defs.h"
#include "nvim/memline.h"
+#include "nvim/memory.h"
#include "nvim/menu.h"
+#include "nvim/message.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/msgpack_rpc/channel.h"
+#include "nvim/msgpack_rpc/channel_defs.h"
#include "nvim/msgpack_rpc/server.h"
+#include "nvim/normal.h"
#include "nvim/ops.h"
#include "nvim/option.h"
#include "nvim/optionstr.h"
#include "nvim/os/dl.h"
+#include "nvim/os/fileio.h"
+#include "nvim/os/fs_defs.h"
+#include "nvim/os/os.h"
+#include "nvim/os/pty_process.h"
#include "nvim/os/shell.h"
+#include "nvim/os/stdpaths_defs.h"
+#include "nvim/os/time.h"
#include "nvim/path.h"
#include "nvim/plines.h"
#include "nvim/popupmenu.h"
+#include "nvim/pos.h"
#include "nvim/profile.h"
-#include "nvim/quickfix.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/sha256.h"
-#include "nvim/sign.h"
#include "nvim/spell.h"
#include "nvim/spellsuggest.h"
#include "nvim/state.h"
+#include "nvim/strings.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"
@@ -106,6 +140,7 @@ typedef enum {
PRAGMA_DIAG_PUSH_IGNORE_MISSING_PROTOTYPES
PRAGMA_DIAG_PUSH_IGNORE_IMPLICIT_FALLTHROUGH
# include "funcs.generated.h"
+
PRAGMA_DIAG_POP
PRAGMA_DIAG_POP
#endif
@@ -113,6 +148,8 @@ PRAGMA_DIAG_POP
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");
+static char e_using_number_as_bool_nr[]
+ = N_("E1023: Using a Number as a Bool: %d");
/// Dummy va_list for passing to vim_snprintf
///
@@ -132,13 +169,13 @@ char *get_function_name(expand_T *xp, int idx)
intidx = -1;
}
if (intidx < 0) {
- char_u *name = (char_u *)get_user_func_name(xp, idx);
+ char *name = 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', (char *)name);
+ && strncmp("g:", xp->xp_pattern, 2) == 0) {
+ return cat_prefix_varname('g', name);
}
- return (char *)name;
+ return name;
}
}
@@ -155,7 +192,7 @@ char *get_function_name(expand_T *xp, int idx)
} else {
IObuff[key_len + 1] = NUL;
}
- return (char *)IObuff;
+ return IObuff;
}
/// Function given to ExpandGeneric() to obtain the list of internal or
@@ -168,9 +205,9 @@ char *get_expr_name(expand_T *xp, int idx)
intidx = -1;
}
if (intidx < 0) {
- char_u *name = (char_u *)get_function_name(xp, idx);
+ char *name = get_function_name(xp, idx);
if (name != NULL) {
- return (char *)name;
+ return name;
}
}
return get_user_var_name(xp, ++intidx);
@@ -189,37 +226,37 @@ const EvalFuncDef *find_internal_func(const char *const name)
return index >= 0 ? &functions[index] : NULL;
}
-int call_internal_func(const char_u *const fname, const int argcount, typval_T *const argvars,
+int call_internal_func(const char *const fname, const int argcount, typval_T *const argvars,
typval_T *const rettv)
FUNC_ATTR_NONNULL_ALL
{
- const EvalFuncDef *const fdef = find_internal_func((const char *)fname);
+ const EvalFuncDef *const fdef = find_internal_func(fname);
if (fdef == NULL) {
- return ERROR_UNKNOWN;
+ return FCERR_UNKNOWN;
} else if (argcount < fdef->min_argc) {
- return ERROR_TOOFEW;
+ return FCERR_TOOFEW;
} else if (argcount > fdef->max_argc) {
- return ERROR_TOOMANY;
+ return FCERR_TOOMANY;
}
argvars[argcount].v_type = VAR_UNKNOWN;
fdef->func(argvars, rettv, fdef->data);
- return ERROR_NONE;
+ return FCERR_NONE;
}
/// Invoke a method for base->method().
-int call_internal_method(const char_u *const fname, const int argcount, typval_T *const argvars,
+int call_internal_method(const char *const fname, const int argcount, typval_T *const argvars,
typval_T *const rettv, typval_T *const basetv)
FUNC_ATTR_NONNULL_ALL
{
- const EvalFuncDef *const fdef = find_internal_func((const char *)fname);
+ const EvalFuncDef *const fdef = find_internal_func(fname);
if (fdef == NULL) {
- return ERROR_UNKNOWN;
+ return FCERR_UNKNOWN;
} else if (fdef->base_arg == BASE_NONE) {
- return ERROR_NOTMETHOD;
+ return FCERR_NOTMETHOD;
} else if (argcount + 1 < fdef->min_argc) {
- return ERROR_TOOFEW;
+ return FCERR_TOOFEW;
} else if (argcount + 1 > fdef->max_argc) {
- return ERROR_TOOMANY;
+ return FCERR_TOOMANY;
}
typval_T argv[MAX_FUNC_ARGS + 1];
@@ -231,10 +268,10 @@ int call_internal_method(const char_u *const fname, const int argcount, typval_T
argv[argcount + 1].v_type = VAR_UNKNOWN;
fdef->func(argv, rettv, fdef->data);
- return ERROR_NONE;
+ return FCERR_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
@@ -251,26 +288,25 @@ static int non_zero_arg(typval_T *argvars)
/// 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)
+static void float_op_wrapper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
float_T f;
- float_T (*function)(float_T) = (float_T (*)(float_T)) fptr;
rettv->v_type = VAR_FLOAT;
if (tv_get_float_chk(argvars, &f)) {
- rettv->vval.v_float = function(f);
+ rettv->vval.v_float = fptr.float_func(f);
} else {
rettv->vval.v_float = 0.0;
}
}
-static void api_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void api_wrapper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (check_secure()) {
return;
}
- ApiDispatchWrapper fn = (ApiDispatchWrapper)fptr;
+ MsgpackRpcRequestHandler handler = *fptr.api_handler;
Array args = ARRAY_DICT_INIT;
@@ -279,7 +315,8 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
Error err = ERROR_INIT;
- Object result = fn(VIML_INTERNAL_CALL, args, &err);
+ Arena res_arena = ARENA_EMPTY;
+ Object result = handler.fn(VIML_INTERNAL_CALL, args, &res_arena, &err);
if (ERROR_SET(&err)) {
semsg_multiline((const char *)e_api_error, err.msg);
@@ -292,15 +329,19 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr)
end:
api_free_array(args);
- api_free_object(result);
+ if (handler.arena_return) {
+ arena_mem_free(arena_finish(&res_arena));
+ } else {
+ api_free_object(result);
+ }
api_clear_error(&err);
}
/// "abs(expr)" function
-static void f_abs(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_abs(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[0].v_type == VAR_FLOAT) {
- float_op_wrapper(argvars, rettv, (FunPtr)&fabs);
+ float_op_wrapper(argvars, rettv, (EvalFuncData){ .float_func = &fabs });
} else {
bool error = false;
@@ -316,25 +357,25 @@ static void f_abs(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "add(list, item)" function
-static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_add(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = 1; // Default: failed.
if (argvars[0].v_type == VAR_LIST) {
list_T *const l = argvars[0].vval.v_list;
- if (!var_check_lock(tv_list_locked(l), N_("add() argument"),
- TV_TRANSLATE)) {
+ if (!value_check_lock(tv_list_locked(l), N_("add() argument"),
+ TV_TRANSLATE)) {
tv_list_append_tv(l, &argvars[1]);
tv_copy(&argvars[0], rettv);
}
} else if (argvars[0].v_type == VAR_BLOB) {
blob_T *const b = argvars[0].vval.v_blob;
if (b != NULL
- && !var_check_lock(b->bv_lock, N_("add() argument"), TV_TRANSLATE)) {
+ && !value_check_lock(b->bv_lock, N_("add() argument"), TV_TRANSLATE)) {
bool error = false;
const varnumber_T n = tv_get_number_chk(&argvars[1], &error);
if (!error) {
- ga_append(&b->bv_ga, (char)n);
+ ga_append(&b->bv_ga, (uint8_t)n);
tv_copy(&argvars[0], rettv);
}
}
@@ -344,42 +385,21 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "and(expr, expr)" function
-static void f_and(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_and(typval_T *argvars, typval_T *rettv, EvalFuncData 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)
+static void f_api_info(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
Dictionary metadata = api_metadata();
(void)object_to_vim(DICTIONARY_OBJ(metadata), rettv, NULL);
- api_free_dictionary(metadata);
-}
-
-/// "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]);
-
- set_buffer_lines(curbuf, lnum, true, &argvars[1], rettv);
-}
-
-/// "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);
- if (buf == NULL) {
- rettv->vval.v_number = 1; // FAIL
- } else {
- const linenr_T lnum = tv_get_lnum_buf(&argvars[1], buf);
- set_buffer_lines(buf, lnum, true, &argvars[2], rettv);
- }
}
/// "atan2()" function
-static void f_atan2(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_atan2(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
float_T fx;
float_T fy;
@@ -393,176 +413,16 @@ static void f_atan2(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "browse(save, title, initdir, default)" function
-static void f_browse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_browse(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_string = NULL;
rettv->v_type = VAR_STRING;
}
/// "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.
-static buf_T *find_buffer(typval_T *avar)
-{
- buf_T *buf = NULL;
-
- if (avar->v_type == VAR_NUMBER) {
- buf = buflist_findnr((int)avar->vval.v_number);
- } else if (avar->v_type == VAR_STRING && avar->vval.v_string != NULL) {
- buf = buflist_findname_exp(avar->vval.v_string);
- if (buf == NULL) {
- // No full path name match, try a match with a URL or a "nofile"
- // buffer, these don't use the full path.
- FOR_ALL_BUFFERS(bp) {
- if (bp->b_fname != NULL
- && (path_with_url(bp->b_fname) || bt_nofilename(bp))
- && STRCMP(bp->b_fname, avar->vval.v_string) == 0) {
- buf = bp;
- break;
- }
- }
- }
- }
- return buf;
-}
-
-/// "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 : (char *)name, 0);
-}
-
-/// "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
-static void f_buflisted(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- buf_T *buf;
-
- buf = find_buffer(&argvars[0]);
- rettv->vval.v_number = (buf != NULL && buf->b_p_bl);
-}
-
-/// "bufload(expr)" function
-static void f_bufload(typval_T *argvars, typval_T *unused, FunPtr fptr)
-{
- buf_T *buf = get_buf_arg(&argvars[0]);
-
- if (buf != NULL && buf->b_ml.ml_mfp == NULL) {
- aco_save_T aco;
-
- aucmd_prepbuf(&aco, buf);
- swap_exists_action = SEA_NONE;
- open_buffer(false, NULL, 0);
- aucmd_restbuf(&aco);
- }
-}
-
-/// "bufloaded(expr)" function
-static void f_bufloaded(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_browsedir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- buf_T *buf;
-
- buf = find_buffer(&argvars[0]);
- rettv->vval.v_number = (buf != NULL && buf->b_ml.ml_mfp != NULL);
-}
-
-/// "bufname(expr)" function
-static void f_bufname(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- const buf_T *buf;
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = NULL;
- if (argvars[0].v_type == VAR_UNKNOWN) {
- buf = curbuf;
- } else {
- buf = tv_get_buf_from_arg(&argvars[0]);
- }
- if (buf != NULL && buf->b_fname != NULL) {
- rettv->vval.v_string = xstrdup(buf->b_fname);
- }
-}
-
-/// "bufnr(expr)" function
-static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- const buf_T *buf;
- bool error = false;
-
- rettv->vval.v_number = -1;
-
- if (argvars[0].v_type == VAR_UNKNOWN) {
- buf = curbuf;
- } else {
- // Don't use tv_get_buf_from_arg(); we continue if the buffer wasn't found
- // and the second argument isn't zero, but we want to return early if the
- // first argument isn't a string or number so only one error is shown.
- if (!tv_check_str_or_nr(&argvars[0])) {
- return;
- }
- emsg_off++;
- buf = tv_get_buf(&argvars[0], false);
- emsg_off--;
- }
-
- // If the buffer isn't found and the second argument is not zero create a
- // new buffer.
- const char *name;
- if (buf == NULL
- && argvars[1].v_type != VAR_UNKNOWN
- && tv_get_number_chk(&argvars[1], &error) != 0
- && !error
- && (name = tv_get_string_chk(&argvars[0])) != NULL) {
- buf = buflist_new((char *)name, NULL, 1, 0);
- }
-
- if (buf != NULL) {
- rettv->vval.v_number = buf->b_fnum;
- }
-}
-
-static void buf_win_common(typval_T *argvars, typval_T *rettv, bool get_nr)
-{
- const buf_T *const buf = tv_get_buf_from_arg(&argvars[0]);
- if (buf == NULL) { // no need to search if invalid arg or buffer not found
- rettv->vval.v_number = -1;
- return;
- }
-
- int winnr = 0;
- int winid;
- bool found_buf = false;
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- winnr++;
- if (wp->w_buffer == buf) {
- found_buf = true;
- winid = wp->handle;
- break;
- }
- }
- rettv->vval.v_number = (found_buf ? (get_nr ? winnr : winid) : -1);
-}
-
-/// "bufwinid(nr)" function
-static void f_bufwinid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- buf_win_common(argvars, rettv, false);
-}
-
-/// "bufwinnr(nr)" function
-static void f_bufwinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- buf_win_common(argvars, rettv, true);
+ f_browse(argvars, rettv, fptr);
}
/// Get buffer by number or pattern.
@@ -575,7 +435,7 @@ buf_T *tv_get_buf(typval_T *tv, int curtab_only)
return NULL;
}
- char_u *name = (char_u *)tv->vval.v_string;
+ char *name = tv->vval.v_string;
if (name == NULL || *name == NUL) {
return curbuf;
@@ -588,9 +448,9 @@ buf_T *tv_get_buf(typval_T *tv, int curtab_only)
int save_magic = p_magic;
p_magic = true;
char *save_cpo = p_cpo;
- p_cpo = "";
+ p_cpo = empty_option;
- buf_T *buf = buflist_findnr(buflist_findpat((char *)name, (char *)name + STRLEN(name),
+ buf_T *buf = buflist_findnr(buflist_findpat(name, name + strlen(name),
true, false, curtab_only));
p_magic = save_magic;
@@ -630,7 +490,7 @@ buf_T *get_buf_arg(typval_T *arg)
}
/// "byte2line(byte)" function
-static void f_byte2line(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_byte2line(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
long boff = tv_get_number(&argvars[0]) - 1;
if (boff < 0) {
@@ -665,19 +525,19 @@ static void byteidx(typval_T *argvars, typval_T *rettv, int comp)
}
/// "byteidx()" function
-static void f_byteidx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_byteidx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
byteidx(argvars, rettv, false);
}
/// "byteidxcomp()" function
-static void f_byteidxcomp(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_byteidxcomp(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
byteidx(argvars, rettv, true);
}
/// "call(func, arglist [, dict])" function
-static void f_call(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_call(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[1].v_type != VAR_LIST) {
emsg(_(e_listreq));
@@ -688,19 +548,19 @@ static void f_call(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
bool owned = false;
- char_u *func;
+ char *func;
partial_T *partial = NULL;
if (argvars[0].v_type == VAR_FUNC) {
- func = (char_u *)argvars[0].vval.v_string;
+ func = argvars[0].vval.v_string;
} else if (argvars[0].v_type == VAR_PARTIAL) {
partial = argvars[0].vval.v_partial;
- func = (char_u *)partial_name(partial);
+ func = 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 {
- func = (char_u *)tv_get_string(&argvars[0]);
+ func = (char *)tv_get_string(&argvars[0]);
}
if (*func == NUL) {
@@ -726,13 +586,13 @@ static void f_call(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "changenr()" function
-static void f_changenr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_changenr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = curbuf->b_u_seq_cur;
}
/// "chanclose(id[, stream])" function
-static void f_chanclose(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_chanclose(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
@@ -771,7 +631,7 @@ static void f_chanclose(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "chansend(id, data)" function
-static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_chansend(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
@@ -788,6 +648,14 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
ptrdiff_t input_len = 0;
char *input = NULL;
+ uint64_t id = (uint64_t)argvars[0].vval.v_number;
+#ifdef UNIX
+ bool crlf = false;
+#else
+ Channel *chan = find_channel(id);
+ bool crlf = (chan != NULL && chan->term) ? true: false;
+#endif
+
if (argvars[1].v_type == VAR_BLOB) {
const blob_T *const b = argvars[1].vval.v_blob;
input_len = tv_blob_len(b);
@@ -795,7 +663,7 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
input = xmemdup(b->bv_ga.ga_data, (size_t)input_len);
}
} else {
- input = save_tv_as_string(&argvars[1], &input_len, false);
+ input = save_tv_as_string(&argvars[1], &input_len, false, crlf);
}
if (!input) {
@@ -803,7 +671,6 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// or there is no input to send.
return;
}
- uint64_t id = (uint64_t)argvars[0].vval.v_number;
const char *error = NULL;
rettv->vval.v_number = (varnumber_T)channel_send(id, input, (size_t)input_len, true, &error);
if (error) {
@@ -812,7 +679,7 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "char2nr(string)" function
-static void f_char2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_char2nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[1].v_type != VAR_UNKNOWN) {
if (!tv_check_num(&argvars[1])) {
@@ -829,15 +696,38 @@ static void f_char2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// otherwise the byte index of the column.
static void get_col(typval_T *argvars, typval_T *rettv, bool charcol)
{
+ if (tv_check_for_string_or_list_arg(argvars, 0) == FAIL
+ || tv_check_for_opt_number_arg(argvars, 1) == FAIL) {
+ return;
+ }
+
+ switchwin_T switchwin;
+ bool winchanged = false;
+
+ if (argvars[1].v_type != VAR_UNKNOWN) {
+ // use the window specified in the second argument
+ tabpage_T *tp;
+ win_T *wp = win_id2wp_tp((int)tv_get_number(&argvars[1]), &tp);
+ if (wp == NULL || tp == NULL) {
+ return;
+ }
+
+ if (switch_win_noblock(&switchwin, wp, tp, true) != OK) {
+ return;
+ }
+
+ check_cursor();
+ winchanged = true;
+ }
+
colnr_T col = 0;
int fnum = curbuf->b_fnum;
-
pos_T *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;
+ col = (colnr_T)strlen(ml_get(fp->lnum)) + 1;
} else {
col = MAXCOL;
}
@@ -846,11 +736,12 @@ static void get_col(typval_T *argvars, typval_T *rettv, bool charcol)
// 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();
+ char *p = get_cursor_pos_ptr();
if (curwin->w_cursor.coladd >=
- (colnr_T)win_chartabsize(curwin, p, curwin->w_virtcol - 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) {
+ if (*p != NUL && p[(l = utfc_ptr2len(p))] == NUL) {
col += l;
}
}
@@ -858,23 +749,28 @@ static void get_col(typval_T *argvars, typval_T *rettv, bool charcol)
}
}
rettv->vval.v_number = col;
+
+ if (winchanged) {
+ restore_win_noblock(&switchwin, true);
+ }
}
/// "charcol()" function
-static void f_charcol(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_charcol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
get_col(argvars, rettv, true);
}
/// "charidx()" function
-static void f_charidx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_charidx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = -1;
if (argvars[0].v_type != VAR_STRING
|| argvars[1].v_type != VAR_NUMBER
|| (argvars[2].v_type != VAR_UNKNOWN
- && argvars[2].v_type != VAR_NUMBER)) {
+ && argvars[2].v_type != VAR_NUMBER
+ && argvars[2].v_type != VAR_BOOL)) {
emsg(_(e_invarg));
return;
}
@@ -889,7 +785,7 @@ static void f_charidx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
countcc = (int)tv_get_number(&argvars[2]);
}
if (countcc < 0 || countcc > 1) {
- emsg(_(e_invarg));
+ semsg(_(e_using_number_as_bool_nr), countcc);
return;
}
@@ -913,7 +809,7 @@ static void f_charidx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "chdir(dir)" function
-static void f_chdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_chdir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
@@ -925,12 +821,12 @@ static void f_chdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
// Return the current directory
- char_u *cwd = xmalloc(MAXPATHL);
+ char *cwd = xmalloc(MAXPATHL);
if (os_dirname(cwd, MAXPATHL) != FAIL) {
#ifdef BACKSLASH_IN_FILENAME
slash_adjust(cwd);
#endif
- rettv->vval.v_string = (char *)vim_strsave(cwd);
+ rettv->vval.v_string = xstrdup(cwd);
}
xfree(cwd);
@@ -948,7 +844,7 @@ static void f_chdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "cindent(lnum)" function
-static void f_cindent(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_cindent(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
pos_T pos = curwin->w_cursor;
linenr_T lnum = tv_get_lnum(argvars);
@@ -963,26 +859,26 @@ static void f_cindent(typval_T *argvars, typval_T *rettv, FunPtr fptr)
win_T *get_optional_window(typval_T *argvars, int idx)
{
- win_T *win = curwin;
+ if (argvars[idx].v_type == VAR_UNKNOWN) {
+ return curwin;
+ }
- if (argvars[idx].v_type != VAR_UNKNOWN) {
- win = find_win_by_nr_or_id(&argvars[idx]);
- if (win == NULL) {
- emsg(_(e_invalwindow));
- return NULL;
- }
+ win_T *win = find_win_by_nr_or_id(&argvars[idx]);
+ if (win == NULL) {
+ emsg(_(e_invalwindow));
+ return NULL;
}
return win;
}
/// "col(string)" function
-static void f_col(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_col(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
get_col(argvars, rettv, false);
}
/// "confirm(message, buttons[, default [, type]])" function
-static void f_confirm(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_confirm(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char buf[NUMBUFLEN];
char buf2[NUMBUFLEN];
@@ -1029,19 +925,19 @@ static void f_confirm(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
if (!error) {
- rettv->vval.v_number = do_dialog(type, NULL, (char_u *)message, (char_u *)buttons, def, NULL,
+ rettv->vval.v_number = do_dialog(type, NULL, (char *)message, (char *)buttons, def, NULL,
false);
}
}
/// "copy()" function
-static void f_copy(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_copy(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
var_item_copy(NULL, &argvars[0], rettv, false, 0);
}
/// "count()" function
-static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_count(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
long n = 0;
int ic = 0;
@@ -1052,15 +948,15 @@ 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 = (char_u *)argvars[0].vval.v_string;
+ const char *expr = tv_get_string_chk(&argvars[1]);
+ const char *p = argvars[0].vval.v_string;
if (!error && expr != NULL && *expr != NUL && p != NULL) {
if (ic) {
- const size_t len = STRLEN(expr);
+ const size_t len = strlen(expr);
while (*p != NUL) {
- if (mb_strnicmp(p, expr, len) == 0) {
+ if (mb_strnicmp((char *)p, (char *)expr, len) == 0) {
n++;
p += len;
} else {
@@ -1068,10 +964,10 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
} else {
- char_u *next;
- while ((next = (char_u *)strstr((char *)p, (char *)expr)) != NULL) {
+ char *next;
+ while ((next = strstr((char *)p, (char *)expr)) != NULL) {
n++;
- p = next + STRLEN(expr);
+ p = next + strlen(expr);
}
}
}
@@ -1127,31 +1023,8 @@ 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.
-static void f_cscope_connection(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- int num = 0;
- const char *dbpath = NULL;
- const char *prepend = NULL;
- char buf[NUMBUFLEN];
-
- if (argvars[0].v_type != VAR_UNKNOWN
- && argvars[1].v_type != VAR_UNKNOWN) {
- num = (int)tv_get_number(&argvars[0]);
- dbpath = tv_get_string(&argvars[1]);
- if (argvars[2].v_type != VAR_UNKNOWN) {
- prepend = tv_get_string_buf(&argvars[2], buf);
- }
- }
-
- rettv->vval.v_number = cs_connection(num, (char_u *)dbpath,
- (char_u *)prepend);
-}
-
/// "ctxget([{index}])" function
-static void f_ctxget(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_ctxget(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
size_t index = 0;
if (argvars[0].v_type == VAR_NUMBER) {
@@ -1175,7 +1048,7 @@ static void f_ctxget(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "ctxpop()" function
-static void f_ctxpop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_ctxpop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (!ctx_restore(NULL, kCtxAll)) {
emsg(_("Context stack is empty"));
@@ -1183,7 +1056,7 @@ static void f_ctxpop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "ctxpush([{types}])" function
-static void f_ctxpush(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_ctxpush(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int types = kCtxAll;
if (argvars[0].v_type == VAR_LIST) {
@@ -1214,7 +1087,7 @@ static void f_ctxpush(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "ctxset({context}[, {index}])" function
-static void f_ctxset(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_ctxset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[0].v_type != VAR_DICT) {
semsg(_(e_invarg2), "expected dictionary as first argument");
@@ -1254,7 +1127,7 @@ static void f_ctxset(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "ctxsize()" function
-static void f_ctxsize(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_ctxsize(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = (varnumber_T)ctx_size();
@@ -1265,7 +1138,7 @@ static void f_ctxsize(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// 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 lnum, col;
long coladd = 0;
bool set_curswant = true;
@@ -1279,7 +1152,7 @@ static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol)
return;
}
- line = pos.lnum;
+ lnum = pos.lnum;
col = pos.col;
coladd = pos.coladd;
if (curswant >= 0) {
@@ -1288,10 +1161,15 @@ static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol)
}
} 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);
+ lnum = tv_get_lnum(argvars);
+ if (lnum < 0) {
+ semsg(_(e_invarg2), tv_get_string(&argvars[0]));
+ } else if (lnum == 0) {
+ lnum = curwin->w_cursor.lnum;
+ }
col = (long)tv_get_number_chk(&argvars[1], NULL);
if (charcol) {
- col = buf_charidx_to_byteidx(curbuf, (linenr_T)line, (int)col) + 1;
+ col = buf_charidx_to_byteidx(curbuf, (linenr_T)lnum, (int)col) + 1;
}
if (argvars[2].v_type != VAR_UNKNOWN) {
coladd = (long)tv_get_number_chk(&argvars[2], NULL);
@@ -1300,11 +1178,11 @@ static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol)
emsg(_(e_invarg));
return;
}
- if (line < 0 || col < 0 || coladd < 0) {
+ if (lnum < 0 || col < 0 || coladd < 0) {
return; // type error; errmsg already given
}
- if (line > 0) {
- curwin->w_cursor.lnum = (linenr_T)line;
+ if (lnum > 0) {
+ curwin->w_cursor.lnum = (linenr_T)lnum;
}
if (col > 0) {
curwin->w_cursor.col = (colnr_T)col - 1;
@@ -1326,43 +1204,45 @@ static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol)
/// 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)
+static void f_cursor(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
set_cursorpos(argvars, rettv, false);
}
/// "debugbreak()" function
-static void f_debugbreak(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_debugbreak(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = FAIL;
int pid = (int)tv_get_number(&argvars[0]);
if (pid == 0) {
emsg(_(e_invarg));
- } else {
-#ifdef WIN32
- HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
+ return;
+ }
- if (hProcess != NULL) {
- DebugBreakProcess(hProcess);
- CloseHandle(hProcess);
- rettv->vval.v_number = OK;
- }
+#ifdef MSWIN
+ HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
+ if (hProcess == NULL) {
+ return;
+ }
+
+ DebugBreakProcess(hProcess);
+ CloseHandle(hProcess);
+ rettv->vval.v_number = OK;
#else
- uv_kill(pid, SIGINT);
+ uv_kill(pid, SIGINT);
#endif
- }
}
/// "deepcopy()" function
-static void f_deepcopy(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_deepcopy(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int noref = 0;
if (argvars[1].v_type != VAR_UNKNOWN) {
- noref = (int)tv_get_number_chk(&argvars[1], NULL);
+ noref = (int)tv_get_bool_chk(&argvars[1], NULL);
}
if (noref < 0 || noref > 1) {
- emsg(_(e_invarg));
+ semsg(_(e_using_number_as_bool_nr), noref);
} else {
var_item_copy(NULL, &argvars[0], rettv, true, (noref == 0
? get_copyID()
@@ -1371,7 +1251,7 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "delete()" function
-static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_delete(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = -1;
if (check_secure()) {
@@ -1407,7 +1287,7 @@ static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// dictwatcheradd(dict, key, funcref) function
-static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (check_secure()) {
return;
@@ -1445,7 +1325,7 @@ static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// dictwatcherdel(dict, key, funcref) function
-static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (check_secure()) {
return;
@@ -1479,96 +1359,20 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, FunPtr fptr)
callback_free(&callback);
}
-/// "deletebufline()" function
-static void f_deletebufline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- buf_T *const buf = tv_get_buf(&argvars[0], false);
- if (buf == NULL) {
- rettv->vval.v_number = 1; // FAIL
- return;
- }
- const bool is_curbuf = buf == curbuf;
- const bool save_VIsual_active = VIsual_active;
-
- linenr_T last;
- const linenr_T first = tv_get_lnum_buf(&argvars[1], buf);
- if (argvars[2].v_type != VAR_UNKNOWN) {
- last = tv_get_lnum_buf(&argvars[2], buf);
- } else {
- last = first;
- }
-
- if (buf->b_ml.ml_mfp == NULL || first < 1
- || first > buf->b_ml.ml_line_count || last < first) {
- rettv->vval.v_number = 1; // FAIL
- return;
- }
-
- buf_T *curbuf_save = NULL;
- win_T *curwin_save = NULL;
- if (!is_curbuf) {
- VIsual_active = false;
- curbuf_save = curbuf;
- curwin_save = curwin;
- curbuf = buf;
- find_win_for_curbuf();
- }
- if (last > curbuf->b_ml.ml_line_count) {
- last = curbuf->b_ml.ml_line_count;
- }
- const long count = last - first + 1;
-
- // When coming here from Insert mode, sync undo, so that this can be
- // undone separately from what was previously inserted.
- if (u_sync_once == 2) {
- u_sync_once = 1; // notify that u_sync() was called
- u_sync(true);
- }
-
- if (u_save(first - 1, last + 1) == FAIL) {
- rettv->vval.v_number = 1; // FAIL
- } else {
- for (linenr_T lnum = first; lnum <= last; lnum++) {
- ml_delete(first, true);
- }
-
- FOR_ALL_TAB_WINDOWS(tp, wp) {
- if (wp->w_buffer == buf) {
- if (wp->w_cursor.lnum > last) {
- wp->w_cursor.lnum -= (linenr_T)count;
- } else if (wp->w_cursor.lnum > first) {
- wp->w_cursor.lnum = first;
- }
- if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count) {
- wp->w_cursor.lnum = wp->w_buffer->b_ml.ml_line_count;
- }
- }
- }
- check_cursor_col();
- deleted_lines_mark(first, count);
- }
-
- if (!is_curbuf) {
- curbuf = curbuf_save;
- curwin = curwin_save;
- VIsual_active = save_VIsual_active;
- }
-}
-
/// "did_filetype()" function
-static void f_did_filetype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_did_filetype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = did_filetype;
}
/// "diff_filler()" function
-static void f_diff_filler(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_diff_filler(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = MAX(0, diff_check(curwin, tv_get_lnum(argvars)));
}
/// "diff_hlID()" function
-static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_diff_hlID(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
linenr_T lnum = tv_get_lnum(argvars);
static linenr_T prev_lnum = 0;
@@ -1585,9 +1389,10 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|| changedtick != buf_get_changedtick(curbuf)
|| fnum != curbuf->b_fnum) {
// New line, buffer, change: need to get the values.
- int filler_lines = diff_check(curwin, lnum);
- if (filler_lines < 0) {
- if (filler_lines == -1) {
+ int linestatus = 0;
+ int filler_lines = diff_check_with_linestatus(curwin, lnum, &linestatus);
+ if (filler_lines < 0 || linestatus < 0) {
+ if (filler_lines == -1 || linestatus == -1) {
change_start = MAXCOL;
change_end = -1;
if (diff_find_change(curwin, lnum, &change_start, &change_end)) {
@@ -1614,11 +1419,11 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
hlID = HLF_CHD; // Changed line.
}
}
- rettv->vval.v_number = hlID == (hlf_T)0 ? 0 : (int)(hlID + 1);
+ rettv->vval.v_number = hlID == (hlf_T)0 ? 0 : (hlID + 1);
}
/// "empty({expr})" function
-static void f_empty(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_empty(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
bool n = true;
@@ -1668,7 +1473,7 @@ static void f_empty(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "environ()" function
-static void f_environ(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_environ(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_dict_alloc_ret(rettv);
@@ -1690,7 +1495,7 @@ static void f_environ(typval_T *argvars, typval_T *rettv, FunPtr fptr)
char c = env[i][len];
env[i][len] = NUL;
-#ifdef WIN32
+#ifdef MSWIN
// Upper-case all the keys for Windows so we can detect duplicates
char *const key = strcase_save(str, true);
#else
@@ -1713,32 +1518,31 @@ static void f_environ(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "escape({string}, {chars})" function
-static void f_escape(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_escape(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char buf[NUMBUFLEN];
- 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->vval.v_string = vim_strsave_escaped(tv_get_string(&argvars[0]),
+ tv_get_string_buf(&argvars[1], buf));
rettv->v_type = VAR_STRING;
}
/// "getenv()" function
-static void f_getenv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getenv(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- char_u *p = (char_u *)vim_getenv(tv_get_string(&argvars[0]));
+ char *p = vim_getenv(tv_get_string(&argvars[0]));
if (p == NULL) {
rettv->v_type = VAR_SPECIAL;
rettv->vval.v_special = kSpecialVarNull;
return;
}
- rettv->vval.v_string = (char *)p;
+ rettv->vval.v_string = p;
rettv->v_type = VAR_STRING;
}
/// "eval()" function
-static void f_eval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_eval(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *s = tv_get_string_chk(&argvars[0]);
if (s != NULL) {
@@ -1759,15 +1563,15 @@ static void f_eval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "eventhandler()" function
-static void f_eventhandler(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_eventhandler(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = vgetc_busy;
}
/// "executable()" function
-static void f_executable(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_executable(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- if (tv_check_for_string(&argvars[0]) == FAIL) {
+ if (tv_check_for_string_arg(argvars, 0) == FAIL) {
return;
}
@@ -1794,7 +1598,7 @@ static char *get_list_line(int c, void *cookie, int indent, bool do_concat)
return s == NULL ? NULL : xstrdup(s);
}
-static void execute_common(typval_T *argvars, typval_T *rettv, FunPtr fptr, int arg_off)
+void execute_common(typval_T *argvars, typval_T *rettv, int arg_off)
{
const int save_msg_silent = msg_silent;
const int save_emsg_silent = emsg_silent;
@@ -1873,30 +1677,15 @@ static void execute_common(typval_T *argvars, typval_T *rettv, FunPtr fptr, int
}
/// "execute(command)" function
-static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_execute(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- execute_common(argvars, rettv, fptr, 0);
-}
-
-/// "win_execute(win_id, command)" function
-static void f_win_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- // Return an empty string if something fails.
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = NULL;
-
- int id = (int)tv_get_number(argvars);
- tabpage_T *tp;
- win_T *wp = win_id2wp_tp(id, &tp);
- if (wp != NULL && tp != NULL) {
- WIN_EXECUTE(wp, tp, execute_common(argvars, rettv, fptr, 1));
- }
+ execute_common(argvars, rettv, 0);
}
/// "exepath()" function
-static void f_exepath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_exepath(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- if (tv_check_for_nonempty_string(&argvars[0]) == FAIL) {
+ if (tv_check_for_nonempty_string_arg(argvars, 0) == FAIL) {
return;
}
@@ -1906,7 +1695,7 @@ static void f_exepath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
#ifdef BACKSLASH_IN_FILENAME
if (path != NULL) {
- slash_adjust((char_u *)path);
+ slash_adjust(path);
}
#endif
@@ -1915,7 +1704,7 @@ static void f_exepath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "exists()" function
-static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_exists(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int n = false;
@@ -1955,13 +1744,12 @@ static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "expand()" function
-static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_expand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- char *errormsg;
int options = WILD_SILENT|WILD_USE_NL|WILD_LIST_NOTFOUND;
bool error = false;
#ifdef BACKSLASH_IN_FILENAME
- char_u *p_csl_save = p_csl;
+ char *p_csl_save = p_csl;
// avoid using 'completeslash' here
p_csl = empty_option;
@@ -1977,10 +1765,17 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
const char *s = tv_get_string(&argvars[0]);
if (*s == '%' || *s == '#' || *s == '<') {
- emsg_off++;
+ if (p_verbose == 0) {
+ emsg_off++;
+ }
size_t len;
- char_u *result = eval_vars((char_u *)s, (char_u *)s, &len, NULL, &errormsg, NULL);
- emsg_off--;
+ char *errormsg = NULL;
+ char *result = eval_vars((char *)s, s, &len, NULL, &errormsg, NULL, false);
+ if (p_verbose == 0) {
+ emsg_off--;
+ } else if (errormsg != NULL) {
+ emsg(errormsg);
+ }
if (rettv->v_type == VAR_LIST) {
tv_list_alloc_ret(rettv, (result != NULL));
if (result != NULL) {
@@ -1988,7 +1783,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
XFREE_CLEAR(result);
} else {
- rettv->vval.v_string = (char *)result;
+ rettv->vval.v_string = result;
}
} else {
// When the optional second argument is non-zero, don't remove matches
@@ -2005,10 +1800,9 @@ 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 = (char *)ExpandOne(&xpc, (char_u *)s, NULL, options,
- WILD_ALL);
+ rettv->vval.v_string = ExpandOne(&xpc, (char *)s, NULL, options, WILD_ALL);
} else {
- ExpandOne(&xpc, (char_u *)s, NULL, options, WILD_ALL_KEEP);
+ ExpandOne(&xpc, (char *)s, NULL, options, WILD_ALL_KEEP);
tv_list_alloc_ret(rettv, xpc.xp_numfiles);
for (int i = 0; i < xpc.xp_numfiles; i++) {
tv_list_append_string(rettv->vval.v_list,
@@ -2026,7 +1820,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "menu_get(path [, modes])" function
-static void f_menu_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_menu_get(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_list_alloc_ret(rettv, kListLenMayKnow);
int modes = MENU_ALL_MODES;
@@ -2039,9 +1833,15 @@ static void f_menu_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "expandcmd()" function
/// Expand all the special characters in a command string.
-static void f_expandcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_expandcmd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char *errormsg = NULL;
+ bool emsgoff = true;
+
+ if (argvars[1].v_type == VAR_DICT
+ && tv_dict_get_bool(argvars[1].vval.v_dict, "errmsg", kBoolVarFalse)) {
+ emsgoff = false;
+ }
rettv->v_type = VAR_STRING;
char *cmdstr = xstrdup(tv_get_string(&argvars[0]));
@@ -2055,15 +1855,23 @@ static void f_expandcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
};
eap.argt |= EX_NOSPC;
- emsg_off++;
- expand_filename(&eap, &cmdstr, &errormsg);
- emsg_off--;
+ if (emsgoff) {
+ emsg_off++;
+ }
+ if (expand_filename(&eap, &cmdstr, &errormsg) == FAIL) {
+ if (!emsgoff && errormsg != NULL && *errormsg != NUL) {
+ emsg(errormsg);
+ }
+ }
+ if (emsgoff) {
+ emsg_off--;
+ }
rettv->vval.v_string = cmdstr;
}
/// "flatten(list[, {maxdepth}])" function
-static void f_flatten(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_flatten(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
bool error = false;
@@ -2088,9 +1896,9 @@ static void f_flatten(typval_T *argvars, typval_T *rettv, FunPtr fptr)
list_T *list = argvars[0].vval.v_list;
if (list != NULL
- && !var_check_lock(tv_list_locked(list),
- N_("flatten() argument"),
- TV_TRANSLATE)
+ && !value_check_lock(tv_list_locked(list),
+ N_("flatten() argument"),
+ TV_TRANSLATE)
&& tv_list_flatten(list, maxdepth) == OK) {
tv_copy(&argvars[0], rettv);
}
@@ -2098,7 +1906,7 @@ static void f_flatten(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "extend(list, list [, idx])" function
/// "extend(dict, dict [, action])" function
-static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_extend(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *const arg_errmsg = N_("extend() argument");
@@ -2107,7 +1915,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
list_T *const l1 = argvars[0].vval.v_list;
list_T *const l2 = argvars[1].vval.v_list;
- if (!var_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) {
+ if (!value_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) {
listitem_T *item;
if (argvars[2].v_type != VAR_UNKNOWN) {
long before = (long)tv_get_number_chk(&argvars[2], &error);
@@ -2136,13 +1944,13 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
dict_T *const d1 = argvars[0].vval.v_dict;
dict_T *const d2 = argvars[1].vval.v_dict;
if (d1 == NULL) {
- const bool locked = var_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE);
+ const bool locked = value_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE);
(void)locked;
assert(locked == true);
} else if (d2 == NULL) {
// Do nothing
tv_copy(&argvars[0], rettv);
- } else if (!var_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) {
+ } else if (!value_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) {
const char *action = "force";
// Check the third argument.
if (argvars[2].v_type != VAR_UNKNOWN) {
@@ -2174,7 +1982,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "feedkeys()" function
-static void f_feedkeys(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_feedkeys(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
// This is not allowed in the sandbox. If the commands would still be
// executed in the sandbox it would be OK, but it probably happens later,
@@ -2195,17 +2003,17 @@ static void f_feedkeys(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "filereadable()" function
-static void f_filereadable(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_filereadable(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *const p = tv_get_string(&argvars[0]);
rettv->vval.v_number =
- (*p && !os_isdir((const char_u *)p) && os_file_is_readable(p));
+ (*p && !os_isdir(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.
-static void f_filewritable(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_filewritable(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *filename = tv_get_string(&argvars[0]);
rettv->vval.v_number = os_file_is_writable(filename);
@@ -2213,8 +2021,8 @@ static void f_filewritable(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what)
{
- char_u *fresult = NULL;
- char_u *path = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path;
+ char *fresult = NULL;
+ char *path = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path;
int count = 1;
bool first = true;
bool error = false;
@@ -2231,7 +2039,7 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what)
error = true;
} else {
if (*p != NUL) {
- path = (char_u *)p;
+ path = (char *)p;
}
if (argvars[2].v_type != VAR_UNKNOWN) {
@@ -2249,12 +2057,12 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what)
if (rettv->v_type == VAR_STRING || rettv->v_type == VAR_LIST) {
xfree(fresult);
}
- fresult = find_file_in_path_option(first ? (char_u *)fname : NULL,
+ fresult = find_file_in_path_option(first ? (char *)fname : NULL,
first ? strlen(fname) : 0,
0, first, path,
- find_what, (char_u *)curbuf->b_ffname,
+ find_what, curbuf->b_ffname,
(find_what == FINDFILE_DIR
- ? (char_u *)""
+ ? ""
: curbuf->b_p_sua));
first = false;
@@ -2265,46 +2073,48 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what)
}
if (rettv->v_type == VAR_STRING) {
- rettv->vval.v_string = (char *)fresult;
+ rettv->vval.v_string = fresult;
}
}
/// "filter()" function
-static void f_filter(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_filter(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
filter_map(argvars, rettv, false);
}
/// "finddir({fname}[, {path}[, {count}]])" function
-static void f_finddir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_finddir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
findfilendir(argvars, rettv, FINDFILE_DIR);
}
/// "findfile({fname}[, {path}[, {count}]])" function
-static void f_findfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_findfile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
findfilendir(argvars, rettv, FINDFILE_FILE);
}
/// "float2nr({float})" function
-static void f_float2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_float2nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
float_T f;
- if (tv_get_float_chk(argvars, &f)) {
- 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;
- } else {
- rettv->vval.v_number = (varnumber_T)f;
- }
+ if (!tv_get_float_chk(argvars, &f)) {
+ return;
+ }
+
+ 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;
+ } else {
+ rettv->vval.v_number = (varnumber_T)f;
}
}
/// "fmod()" function
-static void f_fmod(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_fmod(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
float_T fx;
float_T fy;
@@ -2318,16 +2128,16 @@ static void f_fmod(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "fnameescape({string})" function
-static void f_fnameescape(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_fnameescape(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_string = vim_strsave_fnameescape(tv_get_string(&argvars[0]), VSE_NONE);
rettv->v_type = VAR_STRING;
}
/// "fnamemodify({fname}, {mods})" function
-static void f_fnamemodify(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_fnamemodify(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- char_u *fbuf = NULL;
+ char *fbuf = NULL;
size_t len = 0;
char buf[NUMBUFLEN];
const char *fname = tv_get_string_chk(&argvars[0]);
@@ -2339,7 +2149,7 @@ static void f_fnamemodify(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (*mods != NUL) {
size_t usedlen = 0;
(void)modify_fname((char *)mods, false, &usedlen,
- (char **)&fname, (char **)&fbuf, &len);
+ (char **)&fname, &fbuf, &len);
}
}
@@ -2353,21 +2163,21 @@ static void f_fnamemodify(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "foreground()" function
-static void f_foreground(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_foreground(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{}
-static void f_funcref(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_funcref(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- common_function(argvars, rettv, true, fptr);
+ common_function(argvars, rettv, true);
}
-static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_function(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- common_function(argvars, rettv, false, fptr);
+ common_function(argvars, rettv, false);
}
/// "garbagecollect()" function
-static void f_garbagecollect(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_garbagecollect(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
// This is postponed until we are back at the toplevel, because we may be
// using Lists and Dicts internally. E.g.: ":echo [garbagecollect()]".
@@ -2379,7 +2189,7 @@ static void f_garbagecollect(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "get()" function
-static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_get(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
typval_T *tv = NULL;
bool what_is_dict = false;
@@ -2426,7 +2236,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
pt = argvars[0].vval.v_partial;
} else {
CLEAR_FIELD(fref_pt);
- fref_pt.pt_name = (char_u *)argvars[0].vval.v_string;
+ fref_pt.pt_name = argvars[0].vval.v_string;
pt = &fref_pt;
}
@@ -2434,13 +2244,17 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
const char *const what = tv_get_string(&argvars[1]);
if (strcmp(what, "func") == 0 || strcmp(what, "name") == 0) {
+ const char *name = partial_name(pt);
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 = xstrdup(n);
+ assert(name != NULL);
if (rettv->v_type == VAR_FUNC) {
- func_ref((char_u *)rettv->vval.v_string);
+ func_ref((char *)name);
+ }
+ if (*what == 'n' && pt->pt_name == NULL && pt->pt_func != NULL) {
+ // use <SNR> instead of the byte code
+ name = printable_func_name(pt->pt_func);
}
+ rettv->vval.v_string = xstrdup(name);
} else if (strcmp(what, "dict") == 0) {
what_is_dict = true;
if (pt->pt_dict != NULL) {
@@ -2475,120 +2289,8 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/// "getbufinfo()" function
-static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- buf_T *argbuf = NULL;
- bool filtered = false;
- bool sel_buflisted = false;
- bool sel_bufloaded = false;
- bool sel_bufmodified = false;
-
- tv_list_alloc_ret(rettv, kListLenMayKnow);
-
- // List of all the buffers or selected buffers
- if (argvars[0].v_type == VAR_DICT) {
- dict_T *sel_d = argvars[0].vval.v_dict;
-
- if (sel_d != NULL) {
- dictitem_T *di;
-
- filtered = true;
-
- di = tv_dict_find(sel_d, S_LEN("buflisted"));
- if (di != NULL && tv_get_number(&di->di_tv)) {
- sel_buflisted = true;
- }
-
- di = tv_dict_find(sel_d, S_LEN("bufloaded"));
- if (di != NULL && tv_get_number(&di->di_tv)) {
- sel_bufloaded = true;
- }
- di = tv_dict_find(sel_d, S_LEN("bufmodified"));
- if (di != NULL && tv_get_number(&di->di_tv)) {
- sel_bufmodified = true;
- }
- }
- } else if (argvars[0].v_type != VAR_UNKNOWN) {
- // Information about one buffer. Argument specifies the buffer
- argbuf = tv_get_buf_from_arg(&argvars[0]);
- if (argbuf == NULL) {
- return;
- }
- }
-
- // Return information about all the buffers or a specified buffer
- FOR_ALL_BUFFERS(buf) {
- if (argbuf != NULL && argbuf != buf) {
- continue;
- }
- if (filtered && ((sel_bufloaded && buf->b_ml.ml_mfp == NULL)
- || (sel_buflisted && !buf->b_p_bl)
- || (sel_bufmodified && !buf->b_changed))) {
- continue;
- }
-
- dict_T *const d = get_buffer_info(buf);
- tv_list_append_dict(rettv->vval.v_list, d);
- if (argbuf != NULL) {
- return;
- }
- }
-}
-
-/// 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);
- rettv->vval.v_string = NULL;
-
- if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0 || end < start) {
- if (retlist) {
- tv_list_alloc_ret(rettv, 0);
- }
- return;
- }
-
- if (retlist) {
- if (start < 1) {
- start = 1;
- }
- if (end > buf->b_ml.ml_line_count) {
- end = buf->b_ml.ml_line_count;
- }
- tv_list_alloc_ret(rettv, end - start + 1);
- while (start <= end) {
- tv_list_append_string(rettv->vval.v_list,
- (const char *)ml_get_buf(buf, start++, false), -1);
- }
- } else {
- rettv->v_type = VAR_STRING;
- 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
-static void f_getbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- buf_T *const buf = tv_get_buf_from_arg(&argvars[0]);
-
- const linenr_T lnum = tv_get_lnum_buf(&argvars[1], buf);
- const linenr_T end = (argvars[2].v_type == VAR_UNKNOWN
- ? lnum
- : tv_get_lnum_buf(&argvars[2], buf));
-
- get_buffer_lines(buf, lnum, end, true, rettv);
-}
-
/// "getchangelist()" function
-static void f_getchangelist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getchangelist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_list_alloc_ret(rettv, 2);
@@ -2693,13 +2395,13 @@ static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos, bool
}
/// "getcharpos()" function
-static void f_getcharpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getcharpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
getpos_both(argvars, rettv, false, true);
}
/// "getcharsearch()" function
-static void f_getcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getcharsearch(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_dict_alloc_ret(rettv);
@@ -2710,49 +2412,6 @@ static void f_getcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
tv_dict_add_nr(dict, S_LEN("until"), last_csearch_until());
}
-/// "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 = (char *)get_cmdline_str();
-}
-
-/// "getcmdpos()" function
-static void f_getcmdpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->vval.v_number = get_cmdline_pos() + 1;
-}
-
-/// "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;
- rettv->vval.v_string = xmallocz(1);
- rettv->vval.v_string[0] = (char)get_cmdline_type();
-}
-
-/// "getcmdwintype()" function
-static void f_getcmdwintype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = NULL;
- rettv->vval.v_string = xmallocz(1);
- rettv->vval.v_string[0] = (char)cmdwin_type;
-}
-
/// `getcwd([{win}[, {tab}]])` function
///
/// Every scope not specified implies the currently selected scope object.
@@ -2762,7 +2421,7 @@ static void f_getcmdwintype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// @pre An argument may not be -1 if preceding arguments are not all -1.
///
/// @post The return value will be a string.
-static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getcwd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
// Possible scope of working directory to return.
CdScope scope = kCdScopeInvalid;
@@ -2857,13 +2516,13 @@ 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((char_u *)cwd, MAXPATHL) == FAIL) {
+ if (os_dirname(cwd, MAXPATHL) == FAIL) {
from = ""; // Return empty string on failure.
}
}
if (from) {
- STRLCPY(cwd, from, MAXPATHL);
+ xstrlcpy(cwd, from, MAXPATHL);
}
rettv->vval.v_string = xstrdup(cwd);
@@ -2875,17 +2534,17 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "getfontname()" function
-static void f_getfontname(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getfontname(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
}
/// "getfperm({fname})" function
-static void f_getfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getfperm(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char *perm = NULL;
- char_u flags[] = "rwx";
+ char flags[] = "rwx";
const char *filename = tv_get_string(&argvars[0]);
int32_t file_perm = os_getperm(filename);
@@ -2893,7 +2552,7 @@ static void f_getfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr)
perm = xstrdup("---------");
for (int i = 0; i < 9; i++) {
if (file_perm & (1 << (8 - i))) {
- perm[i] = (char)flags[i % 3];
+ perm[i] = flags[i % 3];
}
}
}
@@ -2902,7 +2561,7 @@ static void f_getfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "getfsize({fname})" function
-static void f_getfsize(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getfsize(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *fname = tv_get_string(&argvars[0]);
@@ -2911,7 +2570,7 @@ static void f_getfsize(typval_T *argvars, typval_T *rettv, FunPtr fptr)
FileInfo file_info;
if (os_fileinfo(fname, &file_info)) {
uint64_t filesize = os_fileinfo_size(&file_info);
- if (os_isdir((const char_u *)fname)) {
+ if (os_isdir(fname)) {
rettv->vval.v_number = 0;
} else {
rettv->vval.v_number = (varnumber_T)filesize;
@@ -2927,7 +2586,7 @@ static void f_getfsize(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "getftime({fname})" function
-static void f_getftime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getftime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *fname = tv_get_string(&argvars[0]);
@@ -2940,9 +2599,9 @@ static void f_getftime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "getftype({fname})" function
-static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getftype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- char_u *type = NULL;
+ char *type = NULL;
char *t;
const char *fname = tv_get_string(&argvars[0]);
@@ -2968,13 +2627,13 @@ static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
} else {
t = "other";
}
- type = vim_strsave((char_u *)t);
+ type = xstrdup(t);
}
- rettv->vval.v_string = (char *)type;
+ rettv->vval.v_string = type;
}
/// "getjumplist()" function
-static void f_getjumplist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getjumplist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_list_alloc_ret(rettv, kListLenMayKnow);
win_T *const wp = find_tabwin(&argvars[0], &argvars[1]);
@@ -3004,26 +2663,8 @@ static void f_getjumplist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/// "getline(lnum, [end])" function
-static void f_getline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- linenr_T end;
- bool retlist;
-
- const linenr_T lnum = tv_get_lnum(argvars);
- if (argvars[1].v_type == VAR_UNKNOWN) {
- end = lnum;
- retlist = false;
- } else {
- end = tv_get_lnum(&argvars[1]);
- retlist = true;
- }
-
- get_buffer_lines(curbuf, lnum, end, retlist, rettv);
-}
-
/// "getmarklist()" function
-static void f_getmarklist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getmarklist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_list_alloc_ret(rettv, kListLenMayKnow);
@@ -3041,7 +2682,7 @@ static void f_getmarklist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "getmousepos()" function
-static void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getmousepos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int row = mouse_row;
int col = mouse_col;
@@ -3082,24 +2723,24 @@ static void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "getpid()" function
-static void f_getpid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getpid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = os_get_pid();
}
/// "getcurpos(string)" function
-static void f_getcurpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getcurpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
getpos_both(argvars, rettv, true, false);
}
-static void f_getcursorcharpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getcursorcharpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
getpos_both(argvars, rettv, true, true);
}
/// "getpos(string)" function
-static void f_getpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
getpos_both(argvars, rettv, false, false);
}
@@ -3109,23 +2750,23 @@ static void f_getpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// Returns zero on error.
static int getreg_get_regname(typval_T *argvars)
{
- const char_u *strregname;
+ const char *strregname;
if (argvars[0].v_type != VAR_UNKNOWN) {
- strregname = (const char_u *)tv_get_string_chk(&argvars[0]);
+ strregname = tv_get_string_chk(&argvars[0]);
if (strregname == NULL) { // type error; errmsg already given
return 0;
}
} else {
// Default to v:register
- strregname = (char_u *)get_vim_var_str(VV_REG);
+ strregname = get_vim_var_str(VV_REG);
}
- return *strregname == 0 ? '"' : *strregname;
+ return *strregname == 0 ? '"' : (uint8_t)(*strregname);
}
/// "getreg()" function
-static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getreg(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int arg2 = false;
bool return_list = false;
@@ -3161,7 +2802,7 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "getregtype()" function
-static void f_getregtype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getregtype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
// on error return an empty string
rettv->v_type = VAR_STRING;
@@ -3180,40 +2821,8 @@ static void f_getregtype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_string = xstrdup(buf);
}
-/// "gettabinfo()" function
-static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- tabpage_T *tparg = NULL;
-
- tv_list_alloc_ret(rettv, (argvars[0].v_type == VAR_UNKNOWN
- ? 1
- : kListLenMayKnow));
-
- if (argvars[0].v_type != VAR_UNKNOWN) {
- // Information about one tab page
- tparg = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL));
- if (tparg == NULL) {
- return;
- }
- }
-
- // Get information about a specific tab page or all tab pages
- int tpnr = 0;
- FOR_ALL_TABS(tp) {
- tpnr++;
- if (tparg != NULL && tp != tparg) {
- continue;
- }
- dict_T *const d = get_tabpage_info(tp, tpnr);
- tv_list_append_dict(rettv->vval.v_list, d);
- if (tparg != NULL) {
- return;
- }
- }
-}
-
/// "gettagstack()" function
-static void f_gettagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_gettagstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
win_T *wp = curwin; // default is current window
@@ -3229,41 +2838,6 @@ static void f_gettagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
get_tagstack(wp, rettv->vval.v_dict);
}
-/// "getwininfo()" function
-static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- win_T *wparg = NULL;
-
- tv_list_alloc_ret(rettv, kListLenMayKnow);
-
- if (argvars[0].v_type != VAR_UNKNOWN) {
- wparg = win_id2wp((int)tv_get_number(&argvars[0]));
- if (wparg == NULL) {
- return;
- }
- }
-
- // Collect information about either all the windows across all the tab
- // pages or one particular window.
- int16_t tabnr = 0;
- FOR_ALL_TABS(tp) {
- tabnr++;
- int16_t winnr = 0;
- FOR_ALL_WINDOWS_IN_TAB(wp, tp) {
- winnr++;
- if (wparg != NULL && wp != wparg) {
- continue;
- }
- dict_T *const d = get_win_info(wp, tabnr, winnr);
- tv_list_append_dict(rettv->vval.v_list, d);
- if (wparg != NULL) {
- // found information about a specific window
- return;
- }
- }
- }
-}
-
/// Dummy timer callback. Used by f_wait().
static void dummy_timer_due_cb(TimeWatcher *tw, void *data)
{}
@@ -3275,7 +2849,7 @@ static void dummy_timer_close_cb(TimeWatcher *tw, void *data)
}
/// "wait(timeout, condition[, interval])" function
-static void f_wait(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_wait(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = -1;
@@ -3328,113 +2902,8 @@ static void f_wait(typval_T *argvars, typval_T *rettv, FunPtr fptr)
time_watcher_close(tw, dummy_timer_close_cb);
}
-/// "win_screenpos()" function
-static void f_win_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- tv_list_alloc_ret(rettv, 2);
- const win_T *const wp = find_win_by_nr_or_id(&argvars[0]);
- tv_list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_winrow + 1);
- 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
-static void win_move_into_split(win_T *wp, win_T *targetwin, int size, int flags)
-{
- int height = wp->w_height;
- win_T *oldwin = curwin;
-
- if (wp == targetwin || wp == aucmd_win) {
- return;
- }
-
- // Jump to the target window
- if (curwin != targetwin) {
- win_goto(targetwin);
- }
-
- // Remove the old window and frame from the tree of frames
- int dir;
- (void)winframe_remove(wp, &dir, NULL);
- win_remove(wp, NULL);
- last_status(false); // may need to remove last status line
- (void)win_comp_pos(); // recompute window positions
-
- // Split a window on the desired side and put the old window there
- (void)win_split_ins(size, flags, wp, dir);
-
- // If splitting horizontally, try to preserve height
- if (size == 0 && !(flags & WSP_VERT)) {
- win_setheight_win(height, wp);
- if (p_ea) {
- win_equal(wp, true, 'v');
- }
- }
-
- if (oldwin != curwin) {
- win_goto(oldwin);
- }
-}
-
-/// "win_splitmove()" function
-static void f_win_splitmove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- win_T *wp = find_win_by_nr_or_id(&argvars[0]);
- win_T *targetwin = find_win_by_nr_or_id(&argvars[1]);
-
- if (wp == NULL || targetwin == NULL || wp == targetwin
- || !win_valid(wp) || !win_valid(targetwin)
- || win_valid_floating(wp) || win_valid_floating(targetwin)) {
- emsg(_(e_invalwindow));
- rettv->vval.v_number = -1;
- return;
- }
-
- int flags = 0, size = 0;
-
- if (argvars[2].v_type != VAR_UNKNOWN) {
- dict_T *d;
- dictitem_T *di;
-
- if (argvars[2].v_type != VAR_DICT || argvars[2].vval.v_dict == NULL) {
- emsg(_(e_invarg));
- return;
- }
-
- d = argvars[2].vval.v_dict;
- if (tv_dict_get_number(d, "vertical")) {
- flags |= WSP_VERT;
- }
- if ((di = tv_dict_find(d, "rightbelow", -1)) != NULL) {
- flags |= tv_get_number(&di->di_tv) ? WSP_BELOW : WSP_ABOVE;
- }
- size = (int)tv_dict_get_number(d, "size");
- }
-
- win_move_into_split(wp, targetwin, size, flags);
-}
-
-/// "getwinpos({timeout})" function
-static void f_getwinpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- tv_list_alloc_ret(rettv, 2);
- tv_list_append_number(rettv->vval.v_list, -1);
- tv_list_append_number(rettv->vval.v_list, -1);
-}
-
-/// "getwinposx()" function
-static void f_getwinposx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->vval.v_number = -1;
-}
-
-/// "getwinposy()" function
-static void f_getwinposy(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->vval.v_number = -1;
-}
-
/// "glob()" function
-static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_glob(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int options = WILD_SILENT|WILD_USE_NL;
expand_T xpc;
@@ -3464,11 +2933,11 @@ 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 = (char *)ExpandOne(&xpc, (char_u *)
- tv_get_string(&argvars[0]), NULL, options,
- WILD_ALL);
+ rettv->vval.v_string = ExpandOne(&xpc, (char *)
+ tv_get_string(&argvars[0]), NULL, options,
+ WILD_ALL);
} else {
- ExpandOne(&xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options,
+ ExpandOne(&xpc, (char *)tv_get_string(&argvars[0]), NULL, options,
WILD_ALL_KEEP);
tv_list_alloc_ret(rettv, xpc.xp_numfiles);
for (int i = 0; i < xpc.xp_numfiles; i++) {
@@ -3483,7 +2952,7 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "globpath()" function
-static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_globpath(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int flags = WILD_IGNORE_COMPLETESLASH; // Flags for globpath.
bool error = false;
@@ -3513,8 +2982,8 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
const char *const file = tv_get_string_buf_chk(&argvars[1], buf1);
if (file != NULL && !error) {
garray_T ga;
- ga_init(&ga, (int)sizeof(char_u *), 10);
- globpath((char *)tv_get_string(&argvars[0]), (char_u *)file, &ga, flags);
+ ga_init(&ga, (int)sizeof(char *), 10);
+ globpath((char *)tv_get_string(&argvars[0]), (char *)file, &ga, flags);
if (rettv->v_type == VAR_STRING) {
rettv->vval.v_string = ga_concat_strings_sep(&ga, "\n");
@@ -3533,7 +3002,7 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "glob2regpat()" function
-static void f_glob2regpat(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_glob2regpat(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *const pat = tv_get_string_chk(&argvars[0]); // NULL on type error
@@ -3541,8 +3010,21 @@ static void f_glob2regpat(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_string = (pat == NULL) ? NULL : file_pat_to_reg_pat(pat, NULL, NULL, false);
}
+/// "gettext()" function
+static void f_gettext(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ if (argvars[0].v_type != VAR_STRING
+ || argvars[0].vval.v_string == NULL
+ || *argvars[0].vval.v_string == NUL) {
+ semsg(_(e_invarg2), tv_get_string(&argvars[0]));
+ } else {
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = xstrdup(_(argvars[0].vval.v_string));
+ }
+}
+
/// "has()" function
-static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
static const char *const has_list[] = {
#if defined(BSD) && !defined(__APPLE__)
@@ -3557,7 +3039,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
#ifdef UNIX
"unix",
#endif
-#if defined(WIN32)
+#ifdef MSWIN
"win32",
#endif
#ifdef _WIN64
@@ -3580,7 +3062,6 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
"cmdwin",
"comments",
"conceal",
- "cscope",
"cursorbind",
"cursorshape",
#ifdef DEBUG
@@ -3601,9 +3082,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
"fork",
#endif
"gettext",
-#if defined(HAVE_ICONV)
"iconv",
-#endif
"insert_expand",
"jumplist",
"keymap",
@@ -3631,8 +3110,6 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
"packages",
"path_extra",
"persistent_undo",
- "postscript",
- "printer",
"profile",
"pythonx",
"reltime",
@@ -3649,7 +3126,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
"spell",
"syntax",
#if !defined(UNIX)
- "system", // TODO(SplinterOfChaos): This IS defined for UNIX!
+ "system",
#endif
"tablineat",
"tag_binary",
@@ -3765,7 +3242,7 @@ static bool has_wsl(void)
/// @pre An argument may not be -1 if preceding arguments are not all -1.
///
/// @post The return value will be either the number `1` or `0`.
-static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_haslocaldir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
// Possible scope of working directory to return.
CdScope scope = kCdScopeInvalid;
@@ -3855,29 +3332,29 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "highlightID(name)" function
-static void f_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_hlID(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = syn_name2id(tv_get_string(&argvars[0]));
}
/// "highlight_exists()" function
-static void f_hlexists(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_hlexists(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = highlight_exists(tv_get_string(&argvars[0]));
}
/// "hostname()" function
-static void f_hostname(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_hostname(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char hostname[256];
os_get_hostname(hostname, 256);
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = (char *)vim_strsave((char_u *)hostname);
+ rettv->vval.v_string = xstrdup(hostname);
}
/// iconv() function
-static void f_iconv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_iconv(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
vimconv_T vimconv;
@@ -3886,9 +3363,9 @@ static void f_iconv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
const char *const str = tv_get_string(&argvars[0]);
char buf1[NUMBUFLEN];
- char_u *const from = enc_canonize(enc_skip((char_u *)tv_get_string_buf(&argvars[1], buf1)));
+ char *const from = enc_canonize(enc_skip((char *)tv_get_string_buf(&argvars[1], buf1)));
char buf2[NUMBUFLEN];
- char_u *const to = enc_canonize(enc_skip((char_u *)tv_get_string_buf(&argvars[2], buf2)));
+ char *const to = enc_canonize(enc_skip((char *)tv_get_string_buf(&argvars[2], buf2)));
vimconv.vc_type = CONV_NONE;
convert_setup(&vimconv, from, to);
@@ -3896,7 +3373,7 @@ static void f_iconv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (vimconv.vc_type == CONV_NONE) {
rettv->vval.v_string = xstrdup(str);
} else {
- rettv->vval.v_string = (char *)string_convert(&vimconv, (char_u *)str, NULL);
+ rettv->vval.v_string = string_convert(&vimconv, (char *)str, NULL);
}
convert_setup(&vimconv, NULL, NULL);
@@ -3905,7 +3382,7 @@ static void f_iconv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "indent()" function
-static void f_indent(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_indent(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const linenr_T lnum = tv_get_lnum(argvars);
if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) {
@@ -3916,7 +3393,7 @@ static void f_indent(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "index()" function
-static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_index(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
long idx = 0;
bool ic = false;
@@ -3956,33 +3433,36 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr)
emsg(_(e_listblobreq));
return;
}
+
list_T *const l = argvars[0].vval.v_list;
- if (l != NULL) {
- listitem_T *item = tv_list_first(l);
- if (argvars[2].v_type != VAR_UNKNOWN) {
- bool error = false;
+ if (l == NULL) {
+ return;
+ }
- // Start at specified item.
- idx = tv_list_uidx(l, (int)tv_get_number_chk(&argvars[2], &error));
- if (error || idx == -1) {
+ listitem_T *item = tv_list_first(l);
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ bool error = false;
+
+ // Start at specified item.
+ idx = tv_list_uidx(l, (int)tv_get_number_chk(&argvars[2], &error));
+ if (error || idx == -1) {
+ item = NULL;
+ } else {
+ item = tv_list_find(l, (int)idx);
+ assert(item != NULL);
+ }
+ if (argvars[3].v_type != VAR_UNKNOWN) {
+ ic = !!tv_get_number_chk(&argvars[3], &error);
+ if (error) {
item = NULL;
- } else {
- item = tv_list_find(l, (int)idx);
- assert(item != NULL);
- }
- if (argvars[3].v_type != VAR_UNKNOWN) {
- ic = !!tv_get_number_chk(&argvars[3], &error);
- if (error) {
- item = NULL;
- }
}
}
+ }
- for (; item != NULL; item = TV_LIST_ITEM_NEXT(l, item), idx++) {
- if (tv_equal(TV_LIST_ITEM_TV(item), &argvars[1], ic, false)) {
- rettv->vval.v_number = idx;
- break;
- }
+ for (; item != NULL; item = TV_LIST_ITEM_NEXT(l, item), idx++) {
+ if (tv_equal(TV_LIST_ITEM_TV(item), &argvars[1], ic, false)) {
+ rettv->vval.v_number = idx;
+ break;
}
}
}
@@ -3991,19 +3471,19 @@ static bool inputsecret_flag = false;
/// "input()" function
/// Also handles inputsecret() when inputsecret is set.
-static void f_input(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_input(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
get_user_input(argvars, rettv, false, inputsecret_flag);
}
/// "inputdialog()" function
-static void f_inputdialog(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_inputdialog(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
get_user_input(argvars, rettv, true, inputsecret_flag);
}
/// "inputlist()" function
-static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_inputlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[0].v_type != VAR_LIST) {
semsg(_(e_listarg), "inputlist()");
@@ -4034,7 +3514,7 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static garray_T ga_userinput = { 0, 0, sizeof(tasave_T), 4, NULL };
/// "inputrestore()" function
-static void f_inputrestore(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_inputrestore(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (!GA_EMPTY(&ga_userinput)) {
ga_userinput.ga_len--;
@@ -4048,7 +3528,7 @@ static void f_inputrestore(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "inputsave()" function
-static void f_inputsave(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_inputsave(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
// Add an entry to the stack of typeahead storage.
tasave_T *p = GA_APPEND_VIA_PTR(tasave_T, &ga_userinput);
@@ -4056,17 +3536,17 @@ static void f_inputsave(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "inputsecret()" function
-static void f_inputsecret(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_inputsecret(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
cmdline_star++;
inputsecret_flag = true;
- f_input(argvars, rettv, NULL);
+ f_input(argvars, rettv, fptr);
cmdline_star--;
inputsecret_flag = false;
}
/// "insert()" function
-static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_insert(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
list_T *l;
bool error = false;
@@ -4075,8 +3555,8 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr)
blob_T *const b = argvars[0].vval.v_blob;
if (b == NULL
- || var_check_lock(b->bv_lock, N_("insert() argument"),
- TV_TRANSLATE)) {
+ || value_check_lock(b->bv_lock, N_("insert() argument"),
+ TV_TRANSLATE)) {
return;
}
@@ -4103,16 +3583,16 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
ga_grow(&b->bv_ga, 1);
- char_u *const p = (char_u *)b->bv_ga.ga_data;
+ uint8_t *const p = (uint8_t *)b->bv_ga.ga_data;
memmove(p + before + 1, p + before, (size_t)(len - before));
- *(p + before) = (char_u)val;
+ *(p + before) = (uint8_t)val;
b->bv_ga.ga_len++;
tv_copy(&argvars[0], rettv);
} else if (argvars[0].v_type != VAR_LIST) {
semsg(_(e_listblobarg), "insert()");
- } else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)),
- N_("insert() argument"), TV_TRANSLATE)) {
+ } else if (!value_check_lock(tv_list_locked((l = argvars[0].vval.v_list)),
+ N_("insert() argument"), TV_TRANSLATE)) {
long before = 0;
if (argvars[2].v_type != VAR_UNKNOWN) {
before = tv_get_number_chk(&argvars[2], &error);
@@ -4139,34 +3619,34 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "interrupt()" function
static void f_interrupt(typval_T *argvars FUNC_ATTR_UNUSED, typval_T *rettv FUNC_ATTR_UNUSED,
- FunPtr fptr FUNC_ATTR_UNUSED)
+ EvalFuncData fptr FUNC_ATTR_UNUSED)
{
got_int = true;
}
/// "invert(expr)" function
-static void f_invert(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_invert(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = ~tv_get_number_chk(&argvars[0], NULL);
}
/// "isdirectory()" function
-static void f_isdirectory(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_isdirectory(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- rettv->vval.v_number = os_isdir((const char_u *)tv_get_string(&argvars[0]));
+ rettv->vval.v_number = os_isdir(tv_get_string(&argvars[0]));
}
/// "islocked()" function
-static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_islocked(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
lval_T lv;
rettv->vval.v_number = -1;
- 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);
+ const char *const end = 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) {
semsg(_(e_trailing_arg), end);
@@ -4199,7 +3679,7 @@ static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "isinf()" function
-static void f_isinf(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_isinf(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[0].v_type == VAR_FLOAT
&& xisinf(argvars[0].vval.v_float)) {
@@ -4208,14 +3688,14 @@ static void f_isinf(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "isnan()" function
-static void f_isnan(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_isnan(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = argvars[0].v_type == VAR_FLOAT
&& xisnan(argvars[0].vval.v_float);
}
/// "id()" function
-static void f_id(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_id(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
FUNC_ATTR_NONNULL_ALL
{
const int len = vim_vsnprintf_typval(NULL, 0, "%p", dummy_ap, argvars);
@@ -4225,7 +3705,7 @@ static void f_id(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "jobpid(id)" function
-static void f_jobpid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_jobpid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
@@ -4249,7 +3729,7 @@ static void f_jobpid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "jobresize(job, width, height)" function
-static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_jobresize(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
@@ -4281,7 +3761,7 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
static const char *ignored_env_vars[] = {
-#ifndef WIN32
+#ifndef MSWIN
"COLUMNS",
"LINES",
"TERMCAP",
@@ -4293,7 +3773,7 @@ static const char *ignored_env_vars[] = {
/// According to comments in src/win/process.c of libuv, Windows has a few
/// "essential" environment variables.
static const char *required_env_vars[] = {
-#ifdef WIN32
+#ifdef MSWIN
"HOMEDRIVE",
"HOMEPATH",
"LOGONSERVER",
@@ -4316,7 +3796,7 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en
if (!clear_env) {
typval_T temp_env = TV_INITIAL_VALUE;
- f_environ(NULL, &temp_env, NULL);
+ f_environ(NULL, &temp_env, (EvalFuncData){ .nullptr = NULL });
tv_dict_extend(env, temp_env.vval.v_dict, "force");
tv_dict_free(temp_env.vval.v_dict);
@@ -4332,7 +3812,7 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en
tv_dict_item_remove(env, dv);
}
}
-#ifndef WIN32
+#ifndef MSWIN
// Set COLORTERM to "truecolor" if termguicolors is set and 256
// otherwise, but only if it was set in the parent terminal at all
dictitem_T *dv = tv_dict_find(env, S_LEN("COLORTERM"));
@@ -4367,7 +3847,7 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en
}
if (job_env) {
-#ifdef WIN32
+#ifdef MSWIN
TV_DICT_ITER(job_env->di_tv.vval.v_dict, var, {
// Always use upper-case keys for Windows so we detect duplicate keys
char *const key = strcase_save((const char *)var->di_key, true);
@@ -4405,7 +3885,7 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en
}
/// "jobstart()" function
-static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
@@ -4467,7 +3947,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
-#ifdef WIN32
+#ifdef MSWIN
if (pty && overlapped) {
semsg(_(e_invarg2),
"job cannot have both 'pty' and 'overlapped' options set");
@@ -4480,7 +3960,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (new_cwd && *new_cwd != NUL) {
cwd = new_cwd;
// The new cwd must be a directory.
- if (!os_isdir((const char_u *)cwd)) {
+ if (!os_isdir(cwd)) {
semsg(_(e_invarg2), "expected valid directory");
shell_free_argv(argv);
return;
@@ -4525,7 +4005,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "jobstop()" function
-static void f_jobstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_jobstop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
@@ -4558,7 +4038,7 @@ static void f_jobstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "jobwait(ids[, timeout])" function
-static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_jobwait(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
@@ -4657,7 +4137,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// json_decode() function
-static void f_json_decode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_json_decode(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char numbuf[NUMBUFLEN];
const char *s = NULL;
@@ -4691,14 +4171,28 @@ static void f_json_decode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// json_encode() function
-static void f_json_encode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_json_encode(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = encode_tv2json(&argvars[0], NULL);
}
+/// "keytrans()" function
+static void f_keytrans(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->v_type = VAR_STRING;
+ if (tv_check_for_string_arg(argvars, 0) == FAIL
+ || argvars[0].vval.v_string == NULL) {
+ return;
+ }
+ // Need to escape K_SPECIAL for mb_unescape().
+ char *escaped = vim_strsave_escape_ks(argvars[0].vval.v_string);
+ rettv->vval.v_string = str2special_save(escaped, true, true);
+ xfree(escaped);
+}
+
/// "last_buffer_nr()" function.
-static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int n = 0;
@@ -4712,7 +4206,7 @@ static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "len()" function
-static void f_len(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_len(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
switch (argvars[0].v_type) {
case VAR_STRING:
@@ -4783,19 +4277,19 @@ static void libcall_common(typval_T *argvars, typval_T *rettv, int out_type)
}
/// "libcall()" function
-static void f_libcall(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_libcall(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
libcall_common(argvars, rettv, VAR_STRING);
}
/// "libcallnr()" function
-static void f_libcallnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_libcallnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
libcall_common(argvars, rettv, VAR_NUMBER);
}
/// "line(string, [winid])" function
-static void f_line(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_line(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
linenr_T lnum = 0;
pos_T *fp = NULL;
@@ -4826,7 +4320,7 @@ static void f_line(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "line2byte(lnum)" function
-static void f_line2byte(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_line2byte(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const linenr_T lnum = tv_get_lnum(argvars);
if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1) {
@@ -4840,7 +4334,7 @@ static void f_line2byte(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "lispindent(lnum)" function
-static void f_lispindent(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_lispindent(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const pos_T pos = curwin->w_cursor;
const linenr_T lnum = tv_get_lnum(argvars);
@@ -4854,13 +4348,13 @@ static void f_lispindent(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "localtime()" function
-static void f_localtime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_localtime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = (varnumber_T)time(NULL);
}
/// luaeval() function implementation
-static void f_luaeval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_luaeval(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
FUNC_ATTR_NONNULL_ALL
{
const char *const str = tv_get_string_chk(&argvars[0]);
@@ -4872,7 +4366,7 @@ static void f_luaeval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "map()" function
-static void f_map(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_map(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
filter_map(argvars, rettv, true);
}
@@ -4880,9 +4374,9 @@ static void f_map(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void find_some_match(typval_T *const argvars, typval_T *const rettv,
const SomeMatchType type)
{
- char_u *str = NULL;
+ char *str = NULL;
long len = 0;
- char_u *expr = NULL;
+ char *expr = NULL;
regmatch_T regmatch;
long start = 0;
long nth = 1;
@@ -4890,11 +4384,11 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
bool match = false;
list_T *l = NULL;
long idx = 0;
- char_u *tofree = NULL;
+ char *tofree = NULL;
// Make 'cpoptions' empty, the 'l' flag should not be used here.
char *save_cpo = p_cpo;
- p_cpo = "";
+ p_cpo = empty_option;
rettv->vval.v_number = -1;
switch (type) {
@@ -4927,8 +4421,8 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
}
li = tv_list_first(l);
} else {
- expr = str = (char_u *)tv_get_string(&argvars[0]);
- len = (long)STRLEN(str);
+ expr = str = (char *)tv_get_string(&argvars[0]);
+ len = (long)strlen(str);
}
char patbuf[NUMBUFLEN];
@@ -4987,8 +4481,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
break;
}
xfree(tofree);
- tofree = expr = str = (char_u *)encode_tv2echo(TV_LIST_ITEM_TV(li),
- NULL);
+ tofree = expr = str = encode_tv2echo(TV_LIST_ITEM_TV(li), NULL);
if (str == NULL) {
break;
}
@@ -5009,7 +4502,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
idx++;
} else {
startcol = (colnr_T)(regmatch.startp[0]
- + utfc_ptr2len((char *)regmatch.startp[0]) - str);
+ + utfc_ptr2len(regmatch.startp[0]) - str);
if (startcol > (colnr_T)len || str + startcol <= regmatch.startp[0]) {
match = false;
break;
@@ -5028,7 +4521,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
xfree(TV_LIST_ITEM_TV(li1)->vval.v_string);
const size_t rd = (size_t)(regmatch.endp[0] - regmatch.startp[0]);
- TV_LIST_ITEM_TV(li1)->vval.v_string = xmemdupz((const char *)regmatch.startp[0], rd);
+ TV_LIST_ITEM_TV(li1)->vval.v_string = xmemdupz(regmatch.startp[0], rd);
TV_LIST_ITEM_TV(li3)->vval.v_number = (varnumber_T)(regmatch.startp[0] - expr);
TV_LIST_ITEM_TV(li4)->vval.v_number = (varnumber_T)(regmatch.endp[0] - expr);
if (l != NULL) {
@@ -5064,11 +4557,9 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
rettv->vval.v_number = idx;
} else {
if (type == kSomeMatch) {
- rettv->vval.v_number =
- (varnumber_T)(regmatch.startp[0] - str);
+ rettv->vval.v_number = (varnumber_T)(regmatch.startp[0] - str);
} else {
- rettv->vval.v_number =
- (varnumber_T)(regmatch.endp[0] - str);
+ rettv->vval.v_number = (varnumber_T)(regmatch.endp[0] - str);
}
rettv->vval.v_number += (varnumber_T)(str - expr);
}
@@ -5090,31 +4581,31 @@ theend:
}
/// "match()" function
-static void f_match(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_match(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
find_some_match(argvars, rettv, kSomeMatch);
}
/// "matchend()" function
-static void f_matchend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_matchend(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
find_some_match(argvars, rettv, kSomeMatchEnd);
}
/// "matchlist()" function
-static void f_matchlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_matchlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
find_some_match(argvars, rettv, kSomeMatchList);
}
/// "matchstr()" function
-static void f_matchstr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_matchstr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
find_some_match(argvars, rettv, kSomeMatchStr);
}
/// "matchstrpos()" function
-static void f_matchstrpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_matchstrpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
find_some_match(argvars, rettv, kSomeMatchStrPos);
}
@@ -5142,7 +4633,7 @@ static void max_min(const typval_T *const tv, typval_T *const rettv, const bool
TV_LIST_ITER_CONST(tv->vval.v_list, li, {
const varnumber_T i = tv_get_number_chk(TV_LIST_ITEM_TV(li), &error);
if (error) {
- return;
+ return; // type error; errmsg already given
}
if (domax ? i > n : i < n) {
n = i;
@@ -5155,7 +4646,7 @@ static void max_min(const typval_T *const tv, typval_T *const rettv, const bool
TV_DICT_ITER(tv->vval.v_dict, di, {
const varnumber_T i = tv_get_number_chk(&di->di_tv, &error);
if (error) {
- return;
+ return; // type error; errmsg already given
}
if (domax ? i > n : i < n) {
n = i;
@@ -5165,23 +4656,24 @@ static void max_min(const typval_T *const tv, typval_T *const rettv, const bool
semsg(_(e_listdictarg), domax ? "max()" : "min()");
return;
}
+
rettv->vval.v_number = n;
}
/// "max()" function
-static void f_max(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_max(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
max_min(argvars, rettv, true);
}
/// "min()" function
-static void f_min(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_min(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
max_min(argvars, rettv, false);
}
/// "mkdir()" function
-static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_mkdir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int prot = 0755; // -V536
@@ -5216,17 +4708,16 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
xfree(failed_dir);
rettv->vval.v_number = FAIL;
return;
- } else {
- rettv->vval.v_number = OK;
- return;
}
+ rettv->vval.v_number = OK;
+ return;
}
}
rettv->vval.v_number = vim_mkdir_emsg(dir, prot);
}
/// "mode()" function
-static void f_mode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_mode(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char buf[MODE_MAX_LENGTH];
@@ -5243,7 +4734,7 @@ static void f_mode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "msgpackdump()" function
-static void f_msgpackdump(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_msgpackdump(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
FUNC_ATTR_NONNULL_ALL
{
if (argvars[0].v_type != VAR_LIST) {
@@ -5382,7 +4873,7 @@ static void msgpackparse_unpack_blob(const blob_T *const blob, list_T *const ret
}
/// "msgpackparse" function
-static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_msgpackparse(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
FUNC_ATTR_NONNULL_ALL
{
if (argvars[0].v_type != VAR_LIST && argvars[0].v_type != VAR_BLOB) {
@@ -5398,7 +4889,7 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "nextnonblank()" function
-static void f_nextnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_nextnonblank(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
linenr_T lnum;
@@ -5407,7 +4898,7 @@ static void f_nextnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr)
lnum = 0;
break;
}
- if (*skipwhite((char *)ml_get(lnum)) != NUL) {
+ if (*skipwhite(ml_get(lnum)) != NUL) {
break;
}
}
@@ -5415,7 +4906,7 @@ static void f_nextnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "nr2char()" function
-static void f_nr2char(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_nr2char(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[1].v_type != VAR_UNKNOWN) {
if (!tv_check_num(&argvars[1])) {
@@ -5446,14 +4937,14 @@ static void f_nr2char(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "or(expr, expr)" function
-static void f_or(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_or(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL)
| tv_get_number_chk(&argvars[1], NULL);
}
/// "pathshorten()" function
-static void f_pathshorten(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_pathshorten(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int trim_len = 1;
@@ -5465,17 +4956,17 @@ static void f_pathshorten(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
rettv->v_type = VAR_STRING;
- const char_u *p = (char_u *)tv_get_string_chk(&argvars[0]);
+ const char *p = 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 = xstrdup(p);
+ shorten_dir_len(rettv->vval.v_string, trim_len);
}
}
/// "pow()" function
-static void f_pow(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_pow(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
float_T fx;
float_T fy;
@@ -5489,13 +4980,13 @@ static void f_pow(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "prevnonblank()" function
-static void f_prevnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_prevnonblank(typval_T *argvars, typval_T *rettv, EvalFuncData 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((char *)ml_get(lnum)) == NUL) {
+ while (lnum >= 1 && *skipwhite(ml_get(lnum)) == NUL) {
lnum--;
}
}
@@ -5503,7 +4994,7 @@ static void f_prevnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "printf()" function
-static void f_printf(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_printf(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
@@ -5525,7 +5016,7 @@ static void f_printf(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "prompt_setcallback({buffer}, {callback})" function
-static void f_prompt_setcallback(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_prompt_setcallback(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
Callback prompt_callback = { .type = kCallbackNone };
@@ -5548,7 +5039,7 @@ static void f_prompt_setcallback(typval_T *argvars, typval_T *rettv, FunPtr fptr
}
/// "prompt_setinterrupt({buffer}, {callback})" function
-static void f_prompt_setinterrupt(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_prompt_setinterrupt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
Callback interrupt_callback = { .type = kCallbackNone };
@@ -5571,7 +5062,7 @@ static void f_prompt_setinterrupt(typval_T *argvars, typval_T *rettv, FunPtr fpt
}
/// "prompt_getprompt({buffer})" function
-static void f_prompt_getprompt(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_prompt_getprompt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
FUNC_ATTR_NONNULL_ALL
{
// return an empty string by default, e.g. it's not a prompt buffer
@@ -5587,11 +5078,11 @@ static void f_prompt_getprompt(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
- rettv->vval.v_string = (char *)vim_strsave(buf_prompt_text(buf));
+ rettv->vval.v_string = xstrdup(buf_prompt_text(buf));
}
/// "prompt_setprompt({buffer}, {text})" function
-static void f_prompt_setprompt(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_prompt_setprompt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (check_secure()) {
return;
@@ -5607,14 +5098,14 @@ static void f_prompt_setprompt(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "pum_getpos()" function
-static void f_pum_getpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_pum_getpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_dict_alloc_ret(rettv);
pum_set_event_info(rettv->vval.v_dict);
}
/// "pumvisible()" function
-static void f_pumvisible(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_pumvisible(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (pum_visible()) {
rettv->vval.v_number = 1;
@@ -5622,7 +5113,7 @@ static void f_pumvisible(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)
+static void f_py3eval(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
script_host_eval("python3", argvars, rettv);
}
@@ -5657,10 +5148,11 @@ static void init_srand(uint32_t *const x)
}
}
if (dev_urandom_state != OK) {
- // Reading /dev/urandom doesn't work, fall back to time().
+ // Reading /dev/urandom doesn't work, fall back to os_hrtime() XOR with process ID
#endif
// uncrustify:off
- *x = (uint32_t)time(NULL);
+ *x = (uint32_t)os_hrtime();
+ *x ^= (uint32_t)os_get_pid();
#ifndef MSWIN
}
#endif
@@ -5694,7 +5186,7 @@ static inline uint32_t shuffle_xoshiro128starstar(uint32_t *const x, uint32_t *c
}
/// "rand()" function
-static void f_rand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_rand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
uint32_t result;
@@ -5705,7 +5197,7 @@ static void f_rand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// When no argument is given use the global seed list.
if (!initialized) {
// Initialize the global seed list.
- uint32_t x;
+ uint32_t x = 0;
init_srand(&x);
gx = splitmix32(&x);
@@ -5764,7 +5256,7 @@ theend:
}
/// "srand()" function
-static void f_srand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_srand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
uint32_t x = 0;
@@ -5786,19 +5278,19 @@ static void f_srand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "perleval()" function
-static void f_perleval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_perleval(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
script_host_eval("perl", argvars, rettv);
}
/// "rubyeval()" function
-static void f_rubyeval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_rubyeval(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
script_host_eval("ruby", argvars, rettv);
}
/// "range()" function
-static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_range(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
varnumber_T end;
varnumber_T stride = 1;
@@ -5820,13 +5312,16 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
if (stride == 0) {
emsg(_("E726: Stride is zero"));
- } else if (stride > 0 ? end + 1 < start : end - 1 > start) {
+ return;
+ }
+ if (stride > 0 ? end + 1 < start : end - 1 > start) {
emsg(_("E727: Start past end"));
- } else {
- tv_list_alloc_ret(rettv, (end - start) / stride);
- for (varnumber_T i = start; stride > 0 ? i <= end : i >= end; i += stride) {
- tv_list_append_number(rettv->vval.v_list, i);
- }
+ return;
+ }
+
+ tv_list_alloc_ret(rettv, (end - start) / stride);
+ for (varnumber_T i = start; stride > 0 ? i <= end : i >= end; i += stride) {
+ tv_list_append_number(rettv->vval.v_list, i);
}
}
@@ -5868,7 +5363,7 @@ theend:
}
/// "readdir()" function
-static void f_readdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_readdir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_list_alloc_ret(rettv, kListLenUnknown);
@@ -5885,17 +5380,17 @@ static void f_readdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
ga_clear_strings(&ga);
}
-/// "readfile()" function
-static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+/// "readfile()" or "readblob()" function
+static void read_file_or_blob(typval_T *argvars, typval_T *rettv, bool always_blob)
{
bool binary = false;
- bool blob = false;
+ bool blob = always_blob;
FILE *fd;
- char_u buf[(IOSIZE/256) * 256]; // rounded to avoid odd + 1
+ char buf[(IOSIZE/256) * 256]; // rounded to avoid odd + 1
int io_size = sizeof(buf);
- char_u *prev = NULL; // previously read bytes, if any
- long prevlen = 0; // length of data in prev
- long prevsize = 0; // size of prev buffer
+ char *prev = NULL; // previously read bytes, if any
+ ptrdiff_t prevlen = 0; // length of data in prev
+ ptrdiff_t prevsize = 0; // size of prev buffer
long maxline = MAXLNUM;
if (argvars[1].v_type != VAR_UNKNOWN) {
@@ -5913,7 +5408,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// their own about CR-LF conversion.
const char *const fname = tv_get_string(&argvars[0]);
- if (os_isdir((const char_u *)fname)) {
+ if (os_isdir(fname)) {
semsg(_(e_isadir2), fname);
return;
}
@@ -5944,13 +5439,13 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// - an incomplete line gets written
// - a "binary" file gets an empty line at the end if it ends in a
// newline.
- char_u *p; // Position in buf.
- char_u *start; // Start of current line.
+ char *p; // Position in buf.
+ char *start; // Start of current line.
for (p = buf, start = buf;
p < buf + readlen || (readlen <= 0 && (prevlen > 0 || binary));
p++) {
- if (*p == '\n' || readlen <= 0) {
- char_u *s = NULL;
+ if (readlen <= 0 || *p == '\n') {
+ char *s = NULL;
size_t len = (size_t)(p - start);
// Finished a line. Remove CRs before NL.
@@ -5967,7 +5462,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
if (prevlen == 0) {
assert(len < INT_MAX);
- s = vim_strnsave(start, len);
+ s = xstrnsave(start, len);
} else {
// Change "prev" buffer to be the right size. This way
// the bytes are only copied once, and very long lines are
@@ -5982,7 +5477,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 = (char *)s,
+ .vval.v_string = s,
});
start = p + 1; // Step over newline.
@@ -6002,18 +5497,18 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*p = '\n';
// Check for utf8 "bom"; U+FEFF is encoded as EF BB BF. Do this
// when finding the BF and check the previous two bytes.
- } else if (*p == 0xbf && !binary) {
+ } else if ((uint8_t)(*p) == 0xbf && !binary) {
// Find the two bytes before the 0xbf. If p is at buf, or buf + 1,
// these may be in the "prev" string.
- char_u back1 = p >= buf + 1 ? p[-1]
+ char back1 = p >= buf + 1 ? p[-1]
: prevlen >= 1 ? prev[prevlen - 1] : NUL;
- char_u back2 = p >= buf + 2 ? p[-2]
+ char back2 = p >= buf + 2 ? p[-2]
: p == buf + 1 && prevlen >= 1 ? prev[prevlen - 1]
: prevlen >=
- 2 ? prev[prevlen - 2] : NUL;
+ 2 ? prev[prevlen - 2] : NUL;
- if (back2 == 0xef && back1 == 0xbb) {
- char_u *dest = p - 2;
+ if ((uint8_t)back2 == 0xef && (uint8_t)back1 == 0xbb) {
+ char *dest = p - 2;
// Usually a BOM is at the beginning of a file, and so at
// the beginning of a line; then we can just step over it.
@@ -6024,8 +5519,8 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
int adjust_prevlen = 0;
if (dest < buf) { // -V782
- adjust_prevlen = (int)(buf - dest); // -V782
// adjust_prevlen must be 1 or 2.
+ adjust_prevlen = (int)(buf - dest); // -V782
dest = buf;
}
if (readlen > p - buf + 1) {
@@ -6050,17 +5545,17 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// small, to avoid repeatedly 'allocing' large and
// 'reallocing' small.
if (prevsize == 0) {
- prevsize = (long)(p - start);
+ prevsize = p - start;
} else {
- long grow50pc = (prevsize * 3) / 2;
- long growmin = (long)((p - start) * 2 + prevlen);
+ ptrdiff_t grow50pc = (prevsize * 3) / 2;
+ ptrdiff_t growmin = (p - start) * 2 + prevlen;
prevsize = grow50pc > growmin ? grow50pc : growmin;
}
prev = xrealloc(prev, (size_t)prevsize);
}
// Add the line part to end of "prev".
memmove(prev + prevlen, start, (size_t)(p - start));
- prevlen += (long)(p - start);
+ prevlen += p - start;
}
} // while
@@ -6068,8 +5563,20 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
fclose(fd);
}
+/// "readblob()" function
+static void f_readblob(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ read_file_or_blob(argvars, rettv, true);
+}
+
+/// "readfile()" function
+static void f_readfile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ read_file_or_blob(argvars, rettv, false);
+}
+
/// "getreginfo()" function
-static void f_getreginfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getreginfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int regname = getreg_get_regname(argvars);
if (regname == 0) {
@@ -6119,18 +5626,18 @@ static void f_getreginfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "reg_executing()" function
-static void f_reg_executing(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_reg_executing(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
return_register(reg_executing, rettv);
}
/// "reg_recording()" function
-static void f_reg_recording(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_reg_recording(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
return_register(reg_recording, rettv);
}
-static void f_reg_recorded(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_reg_recorded(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
return_register(reg_recorded, rettv);
}
@@ -6172,7 +5679,7 @@ static int list2proftime(typval_T *arg, proftime_T *tm) FUNC_ATTR_NONNULL_ALL
/// one argument it returns the time passed since the argument.
/// With two arguments it returns the time passed between
/// the two arguments.
-static void f_reltime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_reltime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
proftime_T res;
proftime_T start;
@@ -6214,7 +5721,7 @@ static void f_reltime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "reltimestr()" function
-static void f_reltimestr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_reltimestr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
FUNC_ATTR_NONNULL_ALL
{
proftime_T tm;
@@ -6227,7 +5734,7 @@ static void f_reltimestr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "remove()" function
-static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_remove(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *const arg_errmsg = N_("remove() argument");
@@ -6243,19 +5750,19 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "rename({from}, {to})" function
-static void f_rename(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_rename(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (check_secure()) {
rettv->vval.v_number = -1;
} else {
char buf[NUMBUFLEN];
- rettv->vval.v_number = vim_rename((const char_u *)tv_get_string(&argvars[0]),
- (const char_u *)tv_get_string_buf(&argvars[1], buf));
+ rettv->vval.v_number = vim_rename(tv_get_string(&argvars[0]),
+ tv_get_string_buf(&argvars[1], buf));
}
}
/// "repeat()" function
-static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_repeat(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
varnumber_T n = tv_get_number(&argvars[1]);
if (argvars[0].v_type == VAR_LIST) {
@@ -6292,18 +5799,18 @@ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "resolve()" function
-static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_resolve(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
const char *fname = tv_get_string(&argvars[0]);
-#ifdef WIN32
+#ifdef MSWIN
char *v = os_resolve_shortcut(fname);
if (v == NULL) {
if (os_is_reparse_point_include(fname)) {
v = os_realpath(fname, v);
}
}
- rettv->vval.v_string = (char_u *)(v == NULL ? xstrdup(fname) : v);
+ rettv->vval.v_string = (v == NULL ? xstrdup(fname) : v);
#else
# ifdef HAVE_READLINK
{
@@ -6365,7 +5872,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (*q != NUL) {
cpy = remain;
remain = (remain
- ? (char *)concat_str((char_u *)q - 1, (char_u *)remain)
+ ? concat_str(q - 1, remain)
: xstrdup(q - 1));
xfree(cpy);
q[-1] = NUL;
@@ -6377,7 +5884,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr)
q[-1] = NUL;
q = path_tail(p);
}
- if (q > p && !path_is_absolute((const char_u *)buf)) {
+ if (q > p && !path_is_absolute(buf)) {
// Symlink is relative to directory of argument. Replace the
// symlink with the resolved name in the same directory.
const size_t p_len = strlen(p);
@@ -6424,7 +5931,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr)
&& (p[2] == NUL
|| vim_ispathsep(p[2])))))) {
// Prepend "./".
- cpy = (char *)concat_str((const char_u *)"./", (const char_u *)p);
+ cpy = concat_str("./", p);
xfree(p);
p = cpy;
} else if (!is_relative_to_current) {
@@ -6457,18 +5964,18 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr)
# endif
#endif
- simplify_filename((char_u *)rettv->vval.v_string);
+ simplify_filename(rettv->vval.v_string);
}
/// "reverse({list})" function
-static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_reverse(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[0].v_type == VAR_BLOB) {
blob_T *const b = argvars[0].vval.v_blob;
const int len = tv_blob_len(b);
for (int i = 0; i < len / 2; i++) {
- const char_u tmp = tv_blob_get(b, i);
+ const uint8_t tmp = tv_blob_get(b, i);
tv_blob_set(b, i, tv_blob_get(b, len - i - 1));
tv_blob_set(b, len - i - 1, tmp);
}
@@ -6477,8 +5984,8 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
semsg(_(e_listblobarg), "reverse()");
} else {
list_T *const l = argvars[0].vval.v_list;
- if (!var_check_lock(tv_list_locked(l), N_("reverse() argument"),
- TV_TRANSLATE)) {
+ if (!value_check_lock(tv_list_locked(l), N_("reverse() argument"),
+ TV_TRANSLATE)) {
tv_list_reverse(l);
tv_list_set_ret(rettv, l);
}
@@ -6486,7 +5993,7 @@ 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)
+static void f_reduce(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[0].v_type != VAR_LIST && argvars[0].v_type != VAR_BLOB) {
emsg(_(e_listblobreq));
@@ -6508,8 +6015,8 @@ static void f_reduce(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
funcexe_T funcexe = FUNCEXE_INIT;
- funcexe.evaluate = true;
- funcexe.partial = partial;
+ funcexe.fe_evaluate = true;
+ funcexe.fe_partial = partial;
typval_T initial;
typval_T argv[3];
@@ -6598,55 +6105,57 @@ static int get_search_arg(typval_T *varp, int *flagsp)
{
int dir = FORWARD;
- if (varp->v_type != VAR_UNKNOWN) {
- char nbuf[NUMBUFLEN];
- const char *flags = tv_get_string_buf_chk(varp, nbuf);
- if (flags == NULL) {
- return 0; // Type error; errmsg already given.
- }
- int mask;
- while (*flags != NUL) {
- switch (*flags) {
- case 'b':
- dir = BACKWARD; break;
- case 'w':
- p_ws = true; break;
- case 'W':
- p_ws = false; break;
- default:
- mask = 0;
- if (flagsp != NULL) {
- switch (*flags) {
- case 'c':
- mask = SP_START; break;
- case 'e':
- mask = SP_END; break;
- case 'm':
- mask = SP_RETCOUNT; break;
- case 'n':
- mask = SP_NOMOVE; break;
- case 'p':
- mask = SP_SUBPAT; break;
- case 'r':
- mask = SP_REPEAT; break;
- case 's':
- mask = SP_SETPCMARK; break;
- case 'z':
- mask = SP_COLUMN; break;
- }
- }
- if (mask == 0) {
- semsg(_(e_invarg2), flags);
- dir = 0;
- } else {
- *flagsp |= mask;
+ if (varp->v_type == VAR_UNKNOWN) {
+ return FORWARD;
+ }
+
+ char nbuf[NUMBUFLEN];
+ const char *flags = tv_get_string_buf_chk(varp, nbuf);
+ if (flags == NULL) {
+ return 0; // Type error; errmsg already given.
+ }
+ int mask;
+ while (*flags != NUL) {
+ switch (*flags) {
+ case 'b':
+ dir = BACKWARD; break;
+ case 'w':
+ p_ws = true; break;
+ case 'W':
+ p_ws = false; break;
+ default:
+ mask = 0;
+ if (flagsp != NULL) {
+ switch (*flags) {
+ case 'c':
+ mask = SP_START; break;
+ case 'e':
+ mask = SP_END; break;
+ case 'm':
+ mask = SP_RETCOUNT; break;
+ case 'n':
+ mask = SP_NOMOVE; break;
+ case 'p':
+ mask = SP_SUBPAT; break;
+ case 'r':
+ mask = SP_REPEAT; break;
+ case 's':
+ mask = SP_SETPCMARK; break;
+ case 'z':
+ mask = SP_COLUMN; break;
}
}
- if (dir == 0) {
- break;
+ if (mask == 0) {
+ semsg(_(e_invarg2), flags);
+ dir = 0;
+ } else {
+ *flagsp |= mask;
}
- flags++;
}
+ if (dir == 0) {
+ break;
+ }
+ flags++;
}
return dir;
}
@@ -6718,7 +6227,7 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
// Repeat until {skip} returns false.
for (;;) {
subpatnum
- = searchit(curwin, curbuf, &pos, NULL, dir, (char_u *)pat, 1, options, RE_SEARCH, &sia);
+ = searchit(curwin, curbuf, &pos, NULL, dir, (char *)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)) {
@@ -6729,7 +6238,9 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
// didn't find it or no skip argument
break;
}
- firstpos = pos;
+ if (firstpos.lnum == 0) {
+ firstpos = pos;
+ }
// If the skip expression matches, ignore this match.
{
@@ -6786,7 +6297,7 @@ theend:
}
/// "rpcnotify()" function
-static void f_rpcnotify(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_rpcnotify(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
@@ -6811,17 +6322,20 @@ static void f_rpcnotify(typval_T *argvars, typval_T *rettv, FunPtr fptr)
ADD(args, vim_to_object(tv));
}
- if (!rpc_send_event((uint64_t)argvars[0].vval.v_number,
- tv_get_string(&argvars[1]), args)) {
+ bool ok = rpc_send_event((uint64_t)argvars[0].vval.v_number,
+ tv_get_string(&argvars[1]), args);
+
+ api_free_array(args);
+
+ if (!ok) {
semsg(_(e_invarg2), "Channel doesn't exist");
return;
}
-
rettv->vval.v_number = 1;
}
/// "rpcrequest()" function
-static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
@@ -6910,12 +6424,12 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
end:
- arena_mem_free(res_mem, NULL);
+ arena_mem_free(res_mem);
api_clear_error(&err);
}
/// "rpcstart()" function (DEPRECATED)
-static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_rpcstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
@@ -6955,7 +6469,7 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// Allocate extra memory for the argument vector and the NULL pointer
int argvl = argsl + 2;
- char **argv = xmalloc(sizeof(char_u *) * (size_t)argvl);
+ char **argv = xmalloc(sizeof(char *) * (size_t)argvl);
// Copy program name
argv[0] = xstrdup(argvars[0].vval.v_string);
@@ -6982,7 +6496,7 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "rpcstop()" function
-static void f_rpcstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_rpcstop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
@@ -7000,7 +6514,7 @@ static void f_rpcstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// if called with a job, stop it, else closes the channel
uint64_t id = (uint64_t)argvars[0].vval.v_number;
if (find_job(id, false)) {
- f_jobstop(argvars, rettv, NULL);
+ f_jobstop(argvars, rettv, fptr);
} else {
const char *error;
rettv->vval.v_number =
@@ -7012,7 +6526,7 @@ static void f_rpcstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "screenattr()" function
-static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_screenattr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1;
int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1;
@@ -7030,7 +6544,7 @@ static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "screenchar()" function
-static void f_screenchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_screenchar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1;
int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1;
@@ -7048,7 +6562,7 @@ static void f_screenchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "screenchars()" function
-static void f_screenchars(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_screenchars(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1;
int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1;
@@ -7061,7 +6575,7 @@ static void f_screenchars(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
int pcc[MAX_MCO];
- int c = utfc_ptr2char(grid->chars[grid->line_offset[row] + (size_t)col], pcc);
+ int c = utfc_ptr2char((char *)grid->chars[grid->line_offset[row] + (size_t)col], pcc);
int composing_len = 0;
while (pcc[composing_len] != 0) {
composing_len++;
@@ -7076,45 +6590,19 @@ static void f_screenchars(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "screencol()" function
///
/// First column is 1 to be consistent with virtcol().
-static void f_screencol(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_screencol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = ui_current_col() + 1;
}
-/// "screenpos({winid}, {lnum}, {col})" function
-static void f_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- tv_dict_alloc_ret(rettv);
- dict_T *dict = rettv->vval.v_dict;
-
- win_T *wp = find_win_by_nr_or_id(&argvars[0]);
- if (wp == NULL) {
- return;
- }
-
- pos_T pos = {
- .lnum = (linenr_T)tv_get_number(&argvars[1]),
- .col = (colnr_T)tv_get_number(&argvars[2]) - 1,
- .coladd = 0
- };
- int row = 0;
- int scol = 0, ccol = 0, ecol = 0;
- textpos2screenpos(wp, &pos, &row, &scol, &ccol, &ecol, false);
-
- tv_dict_add_nr(dict, S_LEN("row"), row);
- tv_dict_add_nr(dict, S_LEN("col"), scol);
- tv_dict_add_nr(dict, S_LEN("curscol"), ccol);
- tv_dict_add_nr(dict, S_LEN("endcol"), ecol);
-}
-
/// "screenrow()" function
-static void f_screenrow(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_screenrow(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = ui_current_row() + 1;
}
/// "screenstring()" function
-static void f_screenstring(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_screenstring(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_string = NULL;
rettv->v_type = VAR_STRING;
@@ -7129,11 +6617,11 @@ static void f_screenstring(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
- rettv->vval.v_string = (char *)vim_strsave(grid->chars[grid->line_offset[row] + (size_t)col]);
+ rettv->vval.v_string = xstrdup((char *)grid->chars[grid->line_offset[row] + (size_t)col]);
}
/// "search()" function
-static void f_search(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_search(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int flags = 0;
@@ -7141,7 +6629,7 @@ static void f_search(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "searchdecl()" function
-static void f_searchdecl(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_searchdecl(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int locally = 1;
int thisblock = 0;
@@ -7157,7 +6645,7 @@ static void f_searchdecl(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
if (!error && name != NULL) {
- rettv->vval.v_number = find_decl((char_u *)name, strlen(name), locally,
+ rettv->vval.v_number = find_decl((char *)name, strlen(name), locally,
thisblock, SEARCH_KEEP) == FAIL;
}
}
@@ -7236,13 +6724,13 @@ theend:
}
/// "searchpair()" function
-static void f_searchpair(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_searchpair(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = searchpair_cmn(argvars, NULL);
}
/// "searchpairpos()" function
-static void f_searchpairpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_searchpairpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
pos_T match_pos;
int lnum = 0;
@@ -7284,7 +6772,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.
char *save_cpo = p_cpo;
- p_cpo = (char *)empty_option;
+ p_cpo = empty_option;
// Set the time limit, if there is one.
proftime_T tm = profile_setlimit(time_limit);
@@ -7292,14 +6780,14 @@ long do_searchpair(const char *spat, const char *mpat, const char *epat, int dir
// Make two search patterns: start/end (pat2, for in nested pairs) and
// start/middle/end (pat3, for the top pair).
const size_t pat2_len = strlen(spat) + strlen(epat) + 17;
- char_u *pat2 = xmalloc(pat2_len);
+ char *pat2 = xmalloc(pat2_len);
const size_t pat3_len = strlen(spat) + strlen(mpat) + strlen(epat) + 25;
- char_u *pat3 = xmalloc(pat3_len);
- snprintf((char *)pat2, pat2_len, "\\m\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat);
+ char *pat3 = xmalloc(pat3_len);
+ snprintf(pat2, pat2_len, "\\m\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat);
if (*mpat == NUL) {
STRCPY(pat3, pat2);
} else {
- snprintf((char *)pat3, pat3_len,
+ snprintf(pat3, pat3_len,
"\\m\\(%s\\m\\)\\|\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat, mpat);
}
if (flags & SP_START) {
@@ -7316,7 +6804,7 @@ long do_searchpair(const char *spat, const char *mpat, const char *epat, int dir
clearpos(&firstpos);
pos_T foundpos;
clearpos(&foundpos);
- char_u *pat = pat3;
+ char *pat = pat3;
for (;;) {
searchit_arg_T sia = {
.sa_stop_lnum = lnum_stop,
@@ -7410,18 +6898,23 @@ long do_searchpair(const char *spat, const char *mpat, const char *epat, int dir
xfree(pat2);
xfree(pat3);
- if ((char_u *)p_cpo == empty_option) {
+ if (p_cpo == empty_option) {
p_cpo = save_cpo;
} else {
// Darn, evaluating the {skip} expression changed the value.
- free_string_option((char_u *)save_cpo);
+ // If it's still empty it was changed and restored, need to restore in
+ // the complicated way.
+ if (*p_cpo == NUL) {
+ set_option_value_give_err("cpo", 0L, save_cpo, 0);
+ }
+ free_string_option(save_cpo);
}
return retval;
}
/// "searchpos()" function
-static void f_searchpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_searchpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
pos_T match_pos;
int flags = 0;
@@ -7441,7 +6934,7 @@ static void f_searchpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "serverlist()" function
-static void f_serverlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_serverlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
size_t n;
char **addrs = server_address_list(&n);
@@ -7455,7 +6948,7 @@ static void f_serverlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "serverstart()" function
-static void f_serverstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_serverstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL; // Address of the new server
@@ -7470,9 +6963,8 @@ static void f_serverstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[0].v_type != VAR_STRING) {
emsg(_(e_invarg));
return;
- } else {
- address = xstrdup(tv_get_string(argvars));
}
+ address = xstrdup(tv_get_string(argvars));
} else {
address = server_address_new(NULL);
}
@@ -7500,7 +6992,7 @@ static void f_serverstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "serverstop()" function
-static void f_serverstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_serverstop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (check_secure()) {
return;
@@ -7519,23 +7011,8 @@ static void f_serverstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/// "setbufline()" function
-static void f_setbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- linenr_T lnum;
- buf_T *buf;
-
- buf = tv_get_buf(&argvars[0], false);
- if (buf == NULL) {
- rettv->vval.v_number = 1; // FAIL
- } else {
- lnum = tv_get_lnum_buf(&argvars[1], buf);
- set_buffer_lines(buf, lnum, false, &argvars[2], rettv);
- }
-}
-
/// Set the cursor or mark position.
-/// If 'charpos' is TRUE, then use the column number as a character offset.
+/// 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)
{
@@ -7543,41 +7020,45 @@ static void set_position(typval_T *argvars, typval_T *rettv, bool charpos)
rettv->vval.v_number = -1;
const char *const name = tv_get_string_chk(argvars);
- if (name != NULL) {
- pos_T pos;
- int fnum;
- 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));
- }
+ if (name == NULL) {
+ return;
+ }
+
+ pos_T pos;
+ int fnum;
+ if (list2fpos(&argvars[1], &pos, &fnum, &curswant, charpos) != OK) {
+ return;
+ }
+
+ 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)
+static void f_setcharpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
set_position(argvars, rettv, true);
}
-static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_setcharsearch(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[0].v_type != VAR_DICT) {
emsg(_(e_dictreq));
@@ -7585,44 +7066,36 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
dict_T *d = argvars[0].vval.v_dict;
- if (d != NULL) {
- char_u *const csearch = (char_u *)tv_dict_get_string(d, "char", false);
- if (csearch != NULL) {
- int pcc[MAX_MCO];
- const int c = utfc_ptr2char(csearch, pcc);
- set_last_csearch(c, csearch, utfc_ptr2len((char *)csearch));
- }
-
- dictitem_T *di = tv_dict_find(d, S_LEN("forward"));
- if (di != NULL) {
- set_csearch_direction(tv_get_number(&di->di_tv) ? FORWARD : BACKWARD);
- }
+ if (d == NULL) {
+ return;
+ }
- di = tv_dict_find(d, S_LEN("until"));
- if (di != NULL) {
- set_csearch_until(!!tv_get_number(&di->di_tv));
- }
+ char *const csearch = tv_dict_get_string(d, "char", false);
+ if (csearch != NULL) {
+ int pcc[MAX_MCO];
+ const int c = utfc_ptr2char(csearch, pcc);
+ set_last_csearch(c, csearch, utfc_ptr2len(csearch));
}
-}
-/// "setcmdpos()" function
-static void f_setcmdpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- const int pos = (int)tv_get_number(&argvars[0]) - 1;
+ dictitem_T *di = tv_dict_find(d, S_LEN("forward"));
+ if (di != NULL) {
+ set_csearch_direction(tv_get_number(&di->di_tv) ? FORWARD : BACKWARD);
+ }
- if (pos >= 0) {
- rettv->vval.v_number = set_cmdline_pos(pos);
+ di = tv_dict_find(d, S_LEN("until"));
+ if (di != NULL) {
+ set_csearch_until(!!tv_get_number(&di->di_tv));
}
}
/// "setcursorcharpos" function
-static void f_setcursorcharpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_setcursorcharpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
set_cursorpos(argvars, rettv, true);
}
/// "setenv()" function
-static void f_setenv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_setenv(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char namebuf[NUMBUFLEN];
char valbuf[NUMBUFLEN];
@@ -7637,7 +7110,7 @@ static void f_setenv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "setfperm({fname}, {mode})" function
-static void f_setfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_setfperm(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = 0;
@@ -7667,15 +7140,8 @@ static void f_setfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = os_setperm(fname, mode) == OK;
}
-/// "setline()" function
-static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- linenr_T lnum = tv_get_lnum(&argvars[0]);
- set_buffer_lines(curbuf, lnum, false, &argvars[1], rettv);
-}
-
/// "setpos()" function
-static void f_setpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_setpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
set_position(argvars, rettv, false);
}
@@ -7711,7 +7177,7 @@ static int get_yank_type(char **const pp, MotionType *const yank_type, long *con
}
/// "setreg()" function
-static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_setreg(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
bool append = false;
@@ -7837,7 +7303,7 @@ free_lstval:
if (strval == NULL) {
return;
}
- write_reg_contents_ex(regname, (const char_u *)strval, (ssize_t)STRLEN(strval),
+ write_reg_contents_ex(regname, strval, (ssize_t)strlen(strval),
append, yank_type, (colnr_T)block_len);
}
if (pointreg != 0) {
@@ -7852,7 +7318,7 @@ free_lstval:
}
/// "settagstack()" function
-static void f_settagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_settagstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
static char *e_invact2 = N_("E962: Invalid action: '%s'");
char action = 'r';
@@ -7903,7 +7369,7 @@ static void f_settagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// f_sha256 - sha256({string}) function
-static void f_sha256(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_sha256(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *p = tv_get_string(&argvars[0]);
const char *hash = sha256_bytes((const uint8_t *)p, strlen(p), NULL, 0);
@@ -7914,18 +7380,17 @@ static void f_sha256(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "shellescape({string})" function
-static void f_shellescape(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_shellescape(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const bool do_special = non_zero_arg(&argvars[1]);
rettv->vval.v_string =
- (char *)vim_strsave_shellescape((const char_u *)tv_get_string(&argvars[0]), do_special,
- do_special);
+ vim_strsave_shellescape(tv_get_string(&argvars[0]), do_special, do_special);
rettv->v_type = VAR_STRING;
}
/// shiftwidth() function
-static void f_shiftwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_shiftwidth(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = 0;
@@ -7941,16 +7406,16 @@ static void f_shiftwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "simplify()" function
-static void f_simplify(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_simplify(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *const p = tv_get_string(&argvars[0]);
rettv->vval.v_string = xstrdup(p);
- simplify_filename((char_u *)rettv->vval.v_string); // Simplify in place.
+ simplify_filename(rettv->vval.v_string); // Simplify in place.
rettv->v_type = VAR_STRING;
}
/// "sockconnect()" function
-static void f_sockconnect(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_sockconnect(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[0].v_type != VAR_STRING || argvars[1].v_type != VAR_STRING) {
emsg(_(e_invarg));
@@ -8002,7 +7467,7 @@ static void f_sockconnect(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "stdioopen()" function
-static void f_stdioopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_stdioopen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[0].v_type != VAR_DICT) {
emsg(_(e_invarg));
@@ -8036,7 +7501,7 @@ static void f_stdioopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "reltimefloat()" function
-static void f_reltimefloat(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_reltimefloat(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
FUNC_ATTR_NONNULL_ALL
{
proftime_T tm;
@@ -8049,7 +7514,7 @@ static void f_reltimefloat(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "soundfold({word})" function
-static void f_soundfold(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_soundfold(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
const char *const s = tv_get_string(&argvars[0]);
@@ -8057,7 +7522,7 @@ static void f_soundfold(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "spellbadword()" function
-static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_spellbadword(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const int wo_spell_save = curwin->w_p_spell;
@@ -8079,7 +7544,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// Find the start and length of the badly spelled word.
len = spell_move_to(curwin, FORWARD, true, true, &attr);
if (len != 0) {
- word = (char *)get_cursor_pos_ptr();
+ word = get_cursor_pos_ptr();
curwin->w_set_curswant = true;
}
} else if (*curbuf->b_s.b_p_spl != NUL) {
@@ -8089,7 +7554,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (str != NULL) {
// Check the argument for spelling.
while (*str != NUL) {
- len = spell_check(curwin, (char_u *)str, &attr, &capcol, false);
+ len = spell_check(curwin, (char *)str, &attr, &capcol, false);
if (attr != HLF_COUNT) {
word = str;
break;
@@ -8113,7 +7578,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "spellsuggest()" function
-static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_spellsuggest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
garray_T ga = GA_EMPTY_INIT_VALUE;
const int wo_spell_save = curwin->w_p_spell;
@@ -8148,7 +7613,7 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
maxcount = 25;
}
- spell_suggest_list(&ga, (char_u *)str, maxcount, need_capital, false);
+ spell_suggest_list(&ga, (char *)str, maxcount, need_capital, false);
f_spellsuggest_return:
tv_list_alloc_ret(rettv, (ptrdiff_t)ga.ga_len);
@@ -8160,7 +7625,7 @@ f_spellsuggest_return:
curwin->w_p_spell = wo_spell_save;
}
-static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_split(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
colnr_T col = 0;
bool keepempty = false;
@@ -8168,7 +7633,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// Make 'cpoptions' empty, the 'l' flag should not be used here.
char *save_cpo = p_cpo;
- p_cpo = "";
+ p_cpo = empty_option;
const char *str = tv_get_string(&argvars[0]);
const char *pat = NULL;
@@ -8179,7 +7644,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr)
typeerr = true;
}
if (argvars[2].v_type != VAR_UNKNOWN) {
- keepempty = (bool)tv_get_number_chk(&argvars[2], &typeerr);
+ keepempty = (bool)tv_get_bool_chk(&argvars[2], &typeerr);
}
}
if (pat == NULL || *pat == NUL) {
@@ -8204,7 +7669,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (*str == NUL) {
match = false; // Empty item at the end.
} else {
- match = vim_regexec_nl(&regmatch, (char_u *)str, col);
+ match = vim_regexec_nl(&regmatch, (char *)str, col);
}
const char *end;
if (match) {
@@ -8222,11 +7687,11 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr)
break;
}
// Advance to just after the match.
- if (regmatch.endp[0] > (char_u *)str) {
+ if (regmatch.endp[0] > str) {
col = 0;
} else {
// Don't get stuck at the same match.
- col = utfc_ptr2len((char *)regmatch.endp[0]);
+ col = utfc_ptr2len(regmatch.endp[0]);
}
str = (const char *)regmatch.endp[0];
}
@@ -8239,7 +7704,7 @@ theend:
}
/// "stdpath(type)" function
-static void f_stdpath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_stdpath(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
@@ -8271,7 +7736,7 @@ static void f_stdpath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "str2float()" function
-static void f_str2float(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_str2float(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char *p = skipwhite(tv_get_string(&argvars[0]));
bool isneg = (*p == '-');
@@ -8287,18 +7752,18 @@ static void f_str2float(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "str2list()" function
-static void f_str2list(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_str2list(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_list_alloc_ret(rettv, kListLenUnknown);
- const char_u *p = (const char_u *)tv_get_string(&argvars[0]);
+ const char *p = tv_get_string(&argvars[0]);
- for (; *p != NUL; p += utf_ptr2len((char *)p)) {
- tv_list_append_number(rettv->vval.v_list, utf_ptr2char((char *)p));
+ for (; *p != NUL; p += utf_ptr2len(p)) {
+ tv_list_append_number(rettv->vval.v_list, utf_ptr2char(p));
}
}
/// "str2nr()" function
-static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_str2nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int base = 10;
int what = 0;
@@ -8309,15 +7774,15 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
emsg(_(e_invarg));
return;
}
- if (argvars[2].v_type != VAR_UNKNOWN && tv_get_number(&argvars[2])) {
+ if (argvars[2].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[2])) {
what |= STR2NR_QUOTE;
}
}
- char_u *p = (char_u *)skipwhite(tv_get_string(&argvars[0]));
+ char *p = skipwhite(tv_get_string(&argvars[0]));
bool isneg = (*p == '-');
if (*p == '+' || *p == '-') {
- p = (char_u *)skipwhite((char *)p + 1);
+ p = skipwhite(p + 1);
}
switch (base) {
case 2:
@@ -8341,7 +7806,7 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "strftime({format}[, {time}])" function
-static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_strftime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
time_t seconds;
@@ -8361,18 +7826,15 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_string = xstrdup(_("(Invalid)"));
} else {
vimconv_T conv;
- char_u *enc;
conv.vc_type = CONV_NONE;
- enc = enc_locale();
+ char *enc = enc_locale();
convert_setup(&conv, p_enc, enc);
if (conv.vc_type != CONV_NONE) {
- p = (char *)string_convert(&conv, (char_u *)p, NULL);
+ p = string_convert(&conv, p, NULL);
}
char result_buf[256];
- if (p != NULL) {
- (void)strftime(result_buf, sizeof(result_buf), p, curtime_ptr);
- } else {
+ if (p == NULL || strftime(result_buf, sizeof(result_buf), p, curtime_ptr) == 0) {
result_buf[0] = NUL;
}
@@ -8381,7 +7843,7 @@ 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 = (char *)string_convert(&conv, (char_u *)result_buf, NULL);
+ rettv->vval.v_string = string_convert(&conv, result_buf, NULL);
} else {
rettv->vval.v_string = xstrdup(result_buf);
}
@@ -8393,7 +7855,7 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "strgetchar()" function
-static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_strgetchar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = -1;
@@ -8407,7 +7869,7 @@ static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
- const size_t len = STRLEN(str);
+ const size_t len = strlen(str);
size_t byteidx = 0;
while (charidx >= 0 && byteidx < len) {
@@ -8421,7 +7883,7 @@ static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "stridx()" function
-static void f_stridx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_stridx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = -1;
@@ -8453,43 +7915,55 @@ static void f_stridx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "string()" function
-static void f_string(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_string(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = encode_tv2string(&argvars[0], NULL);
}
/// "strlen()" function
-static void f_strlen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_strlen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = (varnumber_T)strlen(tv_get_string(&argvars[0]));
}
-/// "strchars()" function
-static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void strchar_common(typval_T *argvars, typval_T *rettv, bool skipcc)
{
const char *s = tv_get_string(&argvars[0]);
- int skipcc = 0;
varnumber_T len = 0;
- int (*func_mb_ptr2char_adv)(const char_u **pp);
+ int (*func_mb_ptr2char_adv)(const char **pp);
+
+ func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv;
+ while (*s != NUL) {
+ func_mb_ptr2char_adv(&s);
+ len++;
+ }
+ rettv->vval.v_number = len;
+}
+
+/// "strcharlen()" function
+static void f_strcharlen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ strchar_common(argvars, rettv, true);
+}
+
+/// "strchars()" function
+static void f_strchars(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ int skipcc = false;
if (argvars[1].v_type != VAR_UNKNOWN) {
- skipcc = (int)tv_get_number_chk(&argvars[1], NULL);
+ skipcc = (int)tv_get_bool(&argvars[1]);
}
if (skipcc < 0 || skipcc > 1) {
- emsg(_(e_invarg));
+ semsg(_(e_using_number_as_bool_nr), skipcc);
} else {
- func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv;
- while (*s != NUL) {
- func_mb_ptr2char_adv((const char_u **)&s);
- len++;
- }
- rettv->vval.v_number = len;
+ strchar_common(argvars, rettv, skipcc);
}
}
/// "strdisplaywidth()" function
-static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *const s = tv_get_string(&argvars[0]);
int col = 0;
@@ -8498,11 +7972,11 @@ static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
col = (int)tv_get_number(&argvars[1]);
}
- rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, (char_u *)s) - col);
+ rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, (char *)s) - col);
}
/// "strwidth()" function
-static void f_strwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_strwidth(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *const s = tv_get_string(&argvars[0]);
@@ -8510,10 +7984,10 @@ static void f_strwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "strcharpart()" function
-static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_strcharpart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *const p = tv_get_string(&argvars[0]);
- const size_t slen = STRLEN(p);
+ const size_t slen = strlen(p);
int nbyte = 0;
bool error = false;
@@ -8564,7 +8038,7 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "strpart()" function
-static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_strpart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
bool error = false;
@@ -8610,7 +8084,7 @@ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "strptime({format}, {timestring})" function
-static void f_strptime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_strptime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char fmt_buf[NUMBUFLEN];
char str_buf[NUMBUFLEN];
@@ -8624,10 +8098,10 @@ static void f_strptime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
vimconv_T conv = {
.vc_type = CONV_NONE,
};
- char_u *enc = enc_locale();
+ char *enc = enc_locale();
convert_setup(&conv, p_enc, enc);
if (conv.vc_type != CONV_NONE) {
- fmt = (char *)string_convert(&conv, (char_u *)fmt, NULL);
+ fmt = string_convert(&conv, fmt, NULL);
}
if (fmt == NULL
|| os_strptime(str, fmt, &tmval) == NULL
@@ -8642,7 +8116,7 @@ static void f_strptime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "strridx()" function
-static void f_strridx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_strridx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char buf[NUMBUFLEN];
const char *const needle = tv_get_string_chk(&argvars[1]);
@@ -8653,7 +8127,7 @@ static void f_strridx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return; // Type error; errmsg already given.
}
- const size_t haystack_len = STRLEN(haystack);
+ const size_t haystack_len = strlen(haystack);
ptrdiff_t end_idx;
if (argvars[2].v_type != VAR_UNKNOWN) {
// Third argument: upper limit for index.
@@ -8685,14 +8159,14 @@ static void f_strridx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "strtrans()" function
-static void f_strtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_strtrans(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = transstr(tv_get_string(&argvars[0]), true);
}
/// "submatch()" function
-static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_submatch(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
bool error = false;
int no = (int)tv_get_number_chk(&argvars[0], &error);
@@ -8715,7 +8189,7 @@ static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (retList == 0) {
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = (char *)reg_submatch(no);
+ rettv->vval.v_string = reg_submatch(no);
} else {
rettv->v_type = VAR_LIST;
rettv->vval.v_list = reg_submatch_list(no);
@@ -8723,7 +8197,7 @@ static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "substitute()" function
-static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_substitute(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char patbuf[NUMBUFLEN];
char subbuf[NUMBUFLEN];
@@ -8752,14 +8226,14 @@ static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "swapinfo(swap_filename)" function
-static void f_swapinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_swapinfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_dict_alloc_ret(rettv);
get_b0_dict(tv_get_string(argvars), rettv->vval.v_dict);
}
/// "swapname(expr)" function
-static void f_swapname(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_swapname(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
buf_T *buf = tv_get_buf(&argvars[0], false);
@@ -8768,12 +8242,12 @@ 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 = (char *)vim_strsave(buf->b_ml.ml_mfp->mf_fname);
+ rettv->vval.v_string = xstrdup(buf->b_ml.ml_mfp->mf_fname);
}
}
/// "synID(lnum, col, trans)" function
-static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_synID(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
// -1 on type error (both)
const linenr_T lnum = tv_get_lnum(argvars);
@@ -8784,7 +8258,7 @@ static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
int id = 0;
if (!transerr && lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count
- && col >= 0 && (size_t)col < STRLEN(ml_get(lnum))) {
+ && col >= 0 && (size_t)col < strlen(ml_get(lnum))) {
id = syn_get_id(curwin, lnum, col, trans, NULL, false);
}
@@ -8792,7 +8266,7 @@ static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "synIDattr(id, what [, mode])" function
-static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_synIDattr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const int id = (int)tv_get_number(&argvars[0]);
const char *const what = tv_get_string(&argvars[1]);
@@ -8850,7 +8324,7 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
break;
case 'u':
- if (STRLEN(what) >= 9) {
+ if (strlen(what) >= 9) {
if (TOLOWER_ASC(what[5]) == 'l') {
// underline
p = highlight_has_attr(id, HL_UNDERLINE, modec);
@@ -8879,7 +8353,7 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "synIDtrans(id)" function
-static void f_synIDtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_synIDtrans(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int id = (int)tv_get_number(&argvars[0]);
@@ -8893,12 +8367,12 @@ static void f_synIDtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "synconcealed(lnum, col)" function
-static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_synconcealed(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int syntax_flags = 0;
int cchar;
int matchid = 0;
- char_u str[NUMBUFLEN];
+ char str[NUMBUFLEN];
tv_list_set_ret(rettv, NULL);
@@ -8909,7 +8383,7 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr)
CLEAR_FIELD(str);
if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0
- && (size_t)col <= STRLEN(ml_get(lnum)) && curwin->w_p_cole > 0) {
+ && (size_t)col <= strlen(ml_get(lnum)) && curwin->w_p_cole > 0) {
(void)syn_get_id(curwin, lnum, col, false, NULL, false);
syntax_flags = get_syntax_info(&matchid);
@@ -8922,7 +8396,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, (char *)str);
+ utf_char2bytes(cchar, str);
}
}
}
@@ -8930,12 +8404,12 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr)
tv_list_alloc_ret(rettv, 3);
tv_list_append_number(rettv->vval.v_list, (syntax_flags & HL_CONCEAL) != 0);
// -1 to auto-determine strlen
- tv_list_append_string(rettv->vval.v_list, (const char *)str, -1);
+ tv_list_append_string(rettv->vval.v_list, str, -1);
tv_list_append_number(rettv->vval.v_list, matchid);
}
/// "synstack(lnum, col)" function
-static void f_synstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_synstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_list_set_ret(rettv, NULL);
@@ -8946,7 +8420,7 @@ static void f_synstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (lnum >= 1
&& lnum <= curbuf->b_ml.ml_line_count
&& col >= 0
- && (size_t)col <= STRLEN(ml_get(lnum))) {
+ && (size_t)col <= strlen(ml_get(lnum))) {
tv_list_alloc_ret(rettv, kListLenMayKnow);
(void)syn_get_id(curwin, lnum, col, false, NULL, true);
@@ -8959,18 +8433,18 @@ static void f_synstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// f_system - the VimL system() function
-static void f_system(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_system(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
get_system_output_as_rettv(argvars, rettv, false);
}
-static void f_systemlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_systemlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
get_system_output_as_rettv(argvars, rettv, true);
}
/// "tabpagebuflist()" function
-static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
win_T *wp = NULL;
@@ -8991,114 +8465,15 @@ static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/// "tabpagenr()" function
-static void f_tabpagenr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- int nr = 1;
-
- if (argvars[0].v_type != VAR_UNKNOWN) {
- const char *const arg = tv_get_string_chk(&argvars[0]);
- nr = 0;
- if (arg != NULL) {
- if (strcmp(arg, "$") == 0) {
- nr = tabpage_index(NULL) - 1;
- } else if (strcmp(arg, "#") == 0) {
- nr = valid_tabpage(lastused_tabpage) ? tabpage_index(lastused_tabpage) : 0;
- } else {
- semsg(_(e_invexpr2), arg);
- }
- }
- } else {
- nr = tabpage_index(curtab);
- }
- rettv->vval.v_number = nr;
-}
-
-/// Common code for tabpagewinnr() and winnr().
-static int get_winnr(tabpage_T *tp, typval_T *argvar)
-{
- int nr = 1;
-
- win_T *twin = (tp == curtab) ? curwin : tp->tp_curwin;
- if (argvar->v_type != VAR_UNKNOWN) {
- bool invalid_arg = false;
- const char *const arg = tv_get_string_chk(argvar);
- if (arg == NULL) {
- nr = 0; // Type error; errmsg already given.
- } else if (strcmp(arg, "$") == 0) {
- twin = (tp == curtab) ? lastwin : tp->tp_lastwin;
- } else if (strcmp(arg, "#") == 0) {
- twin = (tp == curtab) ? prevwin : tp->tp_prevwin;
- if (twin == NULL) {
- nr = 0;
- }
- } else {
- // Extract the window count (if specified). e.g. winnr('3j')
- char *endp;
- long count = strtol((char *)arg, &endp, 10);
- if (count <= 0) {
- // if count is not specified, default to 1
- count = 1;
- }
- if (endp != NULL && *endp != '\0') {
- if (strequal(endp, "j")) {
- twin = win_vert_neighbor(tp, twin, false, count);
- } else if (strequal(endp, "k")) {
- twin = win_vert_neighbor(tp, twin, true, count);
- } else if (strequal(endp, "h")) {
- twin = win_horz_neighbor(tp, twin, true, count);
- } else if (strequal(endp, "l")) {
- twin = win_horz_neighbor(tp, twin, false, count);
- } else {
- invalid_arg = true;
- }
- } else {
- invalid_arg = true;
- }
- }
-
- if (invalid_arg) {
- semsg(_(e_invexpr2), arg);
- nr = 0;
- }
- }
-
- if (nr > 0) {
- for (win_T *wp = (tp == curtab) ? firstwin : tp->tp_firstwin;
- wp != twin; wp = wp->w_next) {
- if (wp == NULL) {
- // didn't find it in this tabpage
- nr = 0;
- break;
- }
- nr++;
- }
- }
- return nr;
-}
-
-/// "tabpagewinnr()" function
-static void f_tabpagewinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- int nr = 1;
- tabpage_T *const tp = find_tabpage((int)tv_get_number(&argvars[0]));
- if (tp == NULL) {
- nr = 0;
- } else {
- nr = get_winnr(tp, &argvars[1]);
- }
- rettv->vval.v_number = nr;
-}
-
/// "tagfiles()" function
-static void f_tagfiles(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_tagfiles(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_list_alloc_ret(rettv, kListLenUnknown);
char *fname = xmalloc(MAXPATHL);
bool first = true;
tagname_T tn;
- while (get_tagfname(&tn, first, (char_u *)fname) == OK) {
+ while (get_tagfname(&tn, first, fname) == OK) {
tv_list_append_string(rettv->vval.v_list, fname, -1);
first = false;
}
@@ -9108,7 +8483,7 @@ static void f_tagfiles(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "taglist()" function
-static void f_taglist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_taglist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *const tag_pattern = tv_get_string(&argvars[0]);
@@ -9122,18 +8497,18 @@ static void f_taglist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
fname = tv_get_string(&argvars[1]);
}
(void)get_tags(tv_list_alloc_ret(rettv, kListLenUnknown),
- (char_u *)tag_pattern, (char_u *)fname);
+ (char *)tag_pattern, (char *)fname);
}
/// "tempname()" function
-static void f_tempname(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_tempname(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = (char *)vim_tempname();
+ rettv->vval.v_string = vim_tempname();
}
/// "termopen(cmd[, cwd])" function
-static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_termopen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (check_secure()) {
return;
@@ -9176,7 +8551,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (new_cwd && *new_cwd != NUL) {
cwd = new_cwd;
// The new cwd must be a directory.
- if (!os_isdir((const char_u *)cwd)) {
+ if (!os_isdir(cwd)) {
semsg(_(e_invarg2), "expected valid directory");
shell_free_argv(argv);
return;
@@ -9216,9 +8591,9 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
int pid = chan->stream.pty.process.pid;
// "./…" => "/home/foo/…"
- vim_FullName(cwd, (char *)NameBuff, sizeof(NameBuff), false);
+ vim_FullName(cwd, NameBuff, sizeof(NameBuff), false);
// "/home/foo/…" => "~/…"
- size_t len = home_replace(NULL, (char *)NameBuff, (char *)IObuff, sizeof(IObuff), true);
+ size_t len = home_replace(NULL, NameBuff, IObuff, sizeof(IObuff), true);
// Trim slash.
if (len != 1 && (IObuff[len - 1] == '\\' || IObuff[len - 1] == '/')) {
IObuff[len - 1] = '\0';
@@ -9231,14 +8606,14 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
// Terminal URI: "term://$CWD//$PID:$CMD"
- snprintf((char *)NameBuff, sizeof(NameBuff), "term://%s//%d:%s",
- (char *)IObuff, pid, cmd);
+ snprintf(NameBuff, sizeof(NameBuff), "term://%s//%d:%s",
+ 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;
apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, false, curbuf);
- (void)setfname(curbuf, (char *)NameBuff, NULL, true);
+ (void)setfname(curbuf, NameBuff, NULL, true);
apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, false, curbuf);
// Save the job id and pid in b:terminal_job_{id,pid}
@@ -9256,7 +8631,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "timer_info([timer])" function
-static void f_timer_info(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_timer_info(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[0].v_type != VAR_UNKNOWN) {
if (argvars[0].v_type != VAR_NUMBER) {
@@ -9274,7 +8649,7 @@ static void f_timer_info(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "timer_pause(timer, paused)" function
-static void f_timer_pause(typval_T *argvars, typval_T *unused, FunPtr fptr)
+static void f_timer_pause(typval_T *argvars, typval_T *unused, EvalFuncData fptr)
{
if (argvars[0].v_type != VAR_NUMBER) {
emsg(_(e_number_exp));
@@ -9294,7 +8669,7 @@ static void f_timer_pause(typval_T *argvars, typval_T *unused, FunPtr fptr)
}
/// "timer_start(timeout, callback, opts)" function
-static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_timer_start(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int repeat = 1;
@@ -9326,7 +8701,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "timer_stop(timerid)" function
-static void f_timer_stop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_timer_stop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[0].v_type != VAR_NUMBER) {
emsg(_(e_number_exp));
@@ -9341,27 +8716,27 @@ static void f_timer_stop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
timer_stop(timer);
}
-static void f_timer_stopall(typval_T *argvars, typval_T *unused, FunPtr fptr)
+static void f_timer_stopall(typval_T *argvars, typval_T *unused, EvalFuncData fptr)
{
timer_stop_all();
}
/// "tolower(string)" function
-static void f_tolower(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_tolower(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = strcase_save(tv_get_string(&argvars[0]), false);
}
/// "toupper(string)" function
-static void f_toupper(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_toupper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = strcase_save(tv_get_string(&argvars[0]), true);
}
/// "tr(string, fromstr, tostr)" function
-static void f_tr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_tr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char buf[NUMBUFLEN];
char buf2[NUMBUFLEN];
@@ -9389,7 +8764,7 @@ static void f_tr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
int fromlen;
for (const char *p = fromstr; *p != NUL; p += fromlen) {
fromlen = utfc_ptr2len(p);
- if (fromlen == inlen && STRNCMP(in_str, p, inlen) == 0) {
+ if (fromlen == inlen && strncmp(in_str, p, (size_t)inlen) == 0) {
int tolen;
for (p = tostr; *p != NUL; p += tolen) {
tolen = utfc_ptr2len(p);
@@ -9440,14 +8815,14 @@ error:
}
/// "trim({expr})" function
-static void f_trim(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_trim(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char buf1[NUMBUFLEN];
char buf2[NUMBUFLEN];
- const char_u *head = (const char_u *)tv_get_string_buf_chk(&argvars[0], buf1);
- const char_u *mask = NULL;
- const char_u *prev;
- const char_u *p;
+ const char *head = tv_get_string_buf_chk(&argvars[0], buf1);
+ const char *mask = NULL;
+ const char *prev;
+ const char *p;
int dir = 0;
rettv->v_type = VAR_STRING;
@@ -9456,8 +8831,13 @@ static void f_trim(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
+ if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_STRING) {
+ semsg(_(e_invarg2), tv_get_string(&argvars[1]));
+ return;
+ }
+
if (argvars[1].v_type == VAR_STRING) {
- mask = (const char_u *)tv_get_string_buf_chk(&argvars[1], buf2);
+ mask = tv_get_string_buf_chk(&argvars[1], buf2);
if (argvars[2].v_type != VAR_UNKNOWN) {
bool error = false;
// leading or trailing characters to trim
@@ -9495,7 +8875,7 @@ static void f_trim(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
- const char_u *tail = head + STRLEN(head);
+ const char *tail = head + strlen(head);
if (dir == 0 || dir == 2) {
// Trim trailing characters
for (; tail > head; tail = prev) {
@@ -9518,11 +8898,11 @@ static void f_trim(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
}
- rettv->vval.v_string = (char *)vim_strnsave(head, (size_t)(tail - head));
+ rettv->vval.v_string = xstrnsave(head, (size_t)(tail - head));
}
/// "type(expr)" function
-static void f_type(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_type(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int n = -1;
@@ -9554,7 +8934,7 @@ static void f_type(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "undofile(name)" function
-static void f_undofile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_undofile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
const char *const fname = tv_get_string(&argvars[0]);
@@ -9573,7 +8953,7 @@ static void f_undofile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "undotree()" function
-static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_undotree(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_dict_alloc_ret(rettv);
@@ -9591,7 +8971,7 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "virtcol(string)" function
-static void f_virtcol(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_virtcol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
colnr_T vcol = 0;
int fnum = curbuf->b_fnum;
@@ -9603,7 +8983,7 @@ static void f_virtcol(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (fp->col < 0) {
fp->col = 0;
} else {
- const size_t len = STRLEN(ml_get(fp->lnum));
+ const size_t len = strlen(ml_get(fp->lnum));
if (fp->col > (colnr_T)len) {
fp->col = (colnr_T)len;
}
@@ -9616,14 +8996,14 @@ static void f_virtcol(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "visualmode()" function
-static void f_visualmode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_visualmode(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- char_u str[2];
+ char str[2];
rettv->v_type = VAR_STRING;
- str[0] = (char_u)curbuf->b_visual_mode_eval;
+ str[0] = (char)curbuf->b_visual_mode_eval;
str[1] = NUL;
- rettv->vval.v_string = (char *)vim_strsave(str);
+ rettv->vval.v_string = xstrdup(str);
// A non-zero number or non-empty string argument: reset mode.
if (non_zero_arg(&argvars[0])) {
@@ -9632,287 +9012,29 @@ static void f_visualmode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "wildmenumode()" function
-static void f_wildmenumode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_wildmenumode(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (wild_menu_showing || ((State & MODE_CMDLINE) && cmdline_pum_active())) {
rettv->vval.v_number = 1;
}
}
-/// "win_findbuf()" function
-static void f_win_findbuf(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- tv_list_alloc_ret(rettv, kListLenMayKnow);
- win_findbuf(argvars, rettv->vval.v_list);
-}
-
-/// "win_getid()" function
-static void f_win_getid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->vval.v_number = win_getid(argvars);
-}
-
-/// "win_gettype(nr)" function
-static void f_win_gettype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- win_T *wp = curwin;
-
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = NULL;
- if (argvars[0].v_type != VAR_UNKNOWN) {
- wp = find_win_by_nr_or_id(&argvars[0]);
- if (wp == NULL) {
- rettv->vval.v_string = (char *)vim_strsave((char_u *)"unknown");
- return;
- }
- }
- if (wp == aucmd_win) {
- rettv->vval.v_string = xstrdup("autocmd");
- } else if (wp->w_p_pvw) {
- rettv->vval.v_string = xstrdup("preview");
- } else if (wp->w_floating) {
- rettv->vval.v_string = xstrdup("popup");
- } else if (wp == curwin && cmdwin_type != 0) {
- rettv->vval.v_string = xstrdup("command");
- } else if (bt_quickfix(wp->w_buffer)) {
- rettv->vval.v_string = xstrdup((wp->w_llist_ref != NULL ? "loclist" : "quickfix"));
- }
-}
-
-/// "win_gotoid()" function
-static void f_win_gotoid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->vval.v_number = win_gotoid(argvars);
-}
-
-/// "win_id2tabwin()" function
-static void f_win_id2tabwin(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- win_id2tabwin(argvars, rettv);
-}
-
-/// "win_id2win()" function
-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)
-{
- rettv->vval.v_number = false;
-
- win_T *wp = find_win_by_nr_or_id(&argvars[0]);
- if (wp == NULL || wp->w_floating) {
- return;
- }
-
- int 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)
-{
- win_T *wp = find_win_by_nr_or_id(&argvars[0]);
- if (wp == NULL) {
- rettv->vval.v_number = -1;
- } else {
- rettv->vval.v_number = wp->w_buffer->b_fnum;
- }
-}
-
-/// "wincol()" function
-static void f_wincol(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- validate_cursor();
- rettv->vval.v_number = curwin->w_wcol + 1;
-}
-
-/// "winheight(nr)" function
-static void f_winheight(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- win_T *wp = find_win_by_nr_or_id(&argvars[0]);
- if (wp == NULL) {
- rettv->vval.v_number = -1;
- } else {
- rettv->vval.v_number = wp->w_height;
- }
-}
-
-/// "winlayout()" function
-static void f_winlayout(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- tabpage_T *tp;
-
- tv_list_alloc_ret(rettv, 2);
-
- if (argvars[0].v_type == VAR_UNKNOWN) {
- tp = curtab;
- } else {
- tp = find_tabpage((int)tv_get_number(&argvars[0]));
- if (tp == NULL) {
- return;
- }
- }
-
- get_framelayout(tp->tp_topframe, rettv->vval.v_list, true);
-}
-
-/// "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
-static void f_winnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->vval.v_number = get_winnr(curtab, &argvars[0]);
-}
-
-/// "winrestcmd()" function
-static void f_winrestcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- char_u buf[50];
-
- garray_T ga;
- ga_init(&ga, (int)sizeof(char), 70);
-
- // Do this twice to handle some window layouts properly.
- for (int i = 0; i < 2; i++) {
- int winnr = 1;
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- snprintf((char *)buf, sizeof(buf), "%dresize %d|", winnr,
- wp->w_height);
- ga_concat(&ga, (char *)buf);
- snprintf((char *)buf, sizeof(buf), "vert %dresize %d|", winnr,
- wp->w_width);
- ga_concat(&ga, (char *)buf);
- winnr++;
- }
- }
- ga_append(&ga, NUL);
-
- rettv->vval.v_string = ga.ga_data;
- rettv->v_type = VAR_STRING;
-}
-
-/// "winrestview()" function
-static void f_winrestview(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- dict_T *dict = argvars[0].vval.v_dict;
-
- if (argvars[0].v_type != VAR_DICT || dict == NULL) {
- emsg(_(e_invarg));
- } else {
- dictitem_T *di;
- if ((di = tv_dict_find(dict, S_LEN("lnum"))) != NULL) {
- curwin->w_cursor.lnum = (linenr_T)tv_get_number(&di->di_tv);
- }
- if ((di = tv_dict_find(dict, S_LEN("col"))) != NULL) {
- curwin->w_cursor.col = (colnr_T)tv_get_number(&di->di_tv);
- }
- if ((di = tv_dict_find(dict, S_LEN("coladd"))) != NULL) {
- curwin->w_cursor.coladd = (colnr_T)tv_get_number(&di->di_tv);
- }
- if ((di = tv_dict_find(dict, S_LEN("curswant"))) != NULL) {
- curwin->w_curswant = (colnr_T)tv_get_number(&di->di_tv);
- curwin->w_set_curswant = false;
- }
- if ((di = tv_dict_find(dict, S_LEN("topline"))) != NULL) {
- set_topline(curwin, (linenr_T)tv_get_number(&di->di_tv));
- }
- if ((di = tv_dict_find(dict, S_LEN("topfill"))) != NULL) {
- curwin->w_topfill = (int)tv_get_number(&di->di_tv);
- }
- if ((di = tv_dict_find(dict, S_LEN("leftcol"))) != NULL) {
- curwin->w_leftcol = (colnr_T)tv_get_number(&di->di_tv);
- }
- if ((di = tv_dict_find(dict, S_LEN("skipcol"))) != NULL) {
- curwin->w_skipcol = (colnr_T)tv_get_number(&di->di_tv);
- }
-
- check_cursor();
- win_new_height(curwin, curwin->w_height);
- win_new_width(curwin, curwin->w_width);
- changed_window_setting();
-
- if (curwin->w_topline <= 0) {
- curwin->w_topline = 1;
- }
- if (curwin->w_topline > curbuf->b_ml.ml_line_count) {
- curwin->w_topline = curbuf->b_ml.ml_line_count;
- }
- check_topfill(curwin, true);
- }
-}
-
-/// "winsaveview()" function
-static void f_winsaveview(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- tv_dict_alloc_ret(rettv);
- dict_T *dict = rettv->vval.v_dict;
-
- tv_dict_add_nr(dict, S_LEN("lnum"), (varnumber_T)curwin->w_cursor.lnum);
- tv_dict_add_nr(dict, S_LEN("col"), (varnumber_T)curwin->w_cursor.col);
- tv_dict_add_nr(dict, S_LEN("coladd"), (varnumber_T)curwin->w_cursor.coladd);
- update_curswant();
- tv_dict_add_nr(dict, S_LEN("curswant"), (varnumber_T)curwin->w_curswant);
-
- tv_dict_add_nr(dict, S_LEN("topline"), (varnumber_T)curwin->w_topline);
- tv_dict_add_nr(dict, S_LEN("topfill"), (varnumber_T)curwin->w_topfill);
- tv_dict_add_nr(dict, S_LEN("leftcol"), (varnumber_T)curwin->w_leftcol);
- tv_dict_add_nr(dict, S_LEN("skipcol"), (varnumber_T)curwin->w_skipcol);
-}
-
-/// "winwidth(nr)" function
-static void f_winwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- win_T *wp = find_win_by_nr_or_id(&argvars[0]);
- if (wp == NULL) {
- rettv->vval.v_number = -1;
- } else {
- rettv->vval.v_number = wp->w_width;
- }
-}
-
/// "windowsversion()" function
-static void f_windowsversion(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_windowsversion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = xstrdup(windowsVersion);
}
/// "wordcount()" function
-static void f_wordcount(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_wordcount(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_dict_alloc_ret(rettv);
cursor_pos_info(rettv->vval.v_dict);
}
/// "writefile()" function
-static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_writefile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = -1;
@@ -9935,6 +9057,7 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
bool binary = false;
bool append = false;
bool do_fsync = !!p_fs;
+ bool mkdir_p = false;
if (argvars[2].v_type != VAR_UNKNOWN) {
const char *const flags = tv_get_string_chk(&argvars[2]);
if (flags == NULL) {
@@ -9950,6 +9073,8 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
do_fsync = true; break;
case 'S':
do_fsync = false; break;
+ case 'p':
+ mkdir_p = true; break;
default:
// Using %s, p and not %c, *p to preserve multibyte characters
semsg(_("E5060: Unknown flag: %s"), p);
@@ -9969,6 +9094,7 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
emsg(_("E482: Can't open file with an empty name"));
} else if ((error = file_open(&fp, fname,
((append ? kFileAppend : kFileTruncate)
+ | (mkdir_p ? kFileMkDir : kFileCreate)
| kFileCreate), 0666)) != 0) {
semsg(_("E482: Can't open file %s for writing: %s"),
fname, os_strerror(error));
@@ -9990,7 +9116,7 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "xor(expr, expr)" function
-static void f_xor(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_xor(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL)
^ tv_get_number_chk(&argvars[1], NULL);
diff --git a/src/nvim/eval/funcs.h b/src/nvim/eval/funcs.h
index 583ee0e75e..1ae031a952 100644
--- a/src/nvim/eval/funcs.h
+++ b/src/nvim/eval/funcs.h
@@ -1,11 +1,17 @@
#ifndef NVIM_EVAL_FUNCS_H
#define NVIM_EVAL_FUNCS_H
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "nvim/api/private/dispatch.h"
#include "nvim/buffer_defs.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/types.h"
/// Prototype of C function that implements VimL function
-typedef void (*VimLFunc)(typval_T *args, typval_T *rvar, FunPtr data);
+typedef void (*VimLFunc)(typval_T *args, typval_T *rvar, EvalFuncData data);
/// Special flags for base_arg @see EvalFuncDef
#define BASE_NONE 0 ///< Not a method (no base argument).
@@ -13,13 +19,13 @@ typedef void (*VimLFunc)(typval_T *args, typval_T *rvar, FunPtr data);
/// Structure holding VimL function definition
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.
+ 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.
+ EvalFuncData data; ///< Userdata for function implementation.
} EvalFuncDef;
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/eval/gc.c b/src/nvim/eval/gc.c
index 633e6abacf..6a54c4ddc1 100644
--- a/src/nvim/eval/gc.c
+++ b/src/nvim/eval/gc.c
@@ -1,11 +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 <stddef.h>
+
#include "nvim/eval/gc.h"
-#include "nvim/eval/typval.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "eval/gc.c.generated.h"
+# include "eval/gc.c.generated.h" // IWYU pragma: export
#endif
/// Head of list of all dictionaries
diff --git a/src/nvim/eval/gc.h b/src/nvim/eval/gc.h
index c2e862e469..3185750c3b 100644
--- a/src/nvim/eval/gc.h
+++ b/src/nvim/eval/gc.h
@@ -2,6 +2,7 @@
#define NVIM_EVAL_GC_H
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
extern dict_T *gc_first_dict;
extern list_T *gc_first_list;
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 8822bb0491..05b4737206 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -28,9 +28,9 @@
#include "nvim/lua/executor.h"
#include "nvim/macros.h"
#include "nvim/mbyte.h"
+#include "nvim/mbyte_defs.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"
@@ -40,6 +40,15 @@
# include "eval/typval.c.generated.h"
#endif
+static char e_string_required_for_argument_nr[]
+ = N_("E1174: String required for argument %d");
+static char e_non_empty_string_required_for_argument_nr[]
+ = N_("E1175: Non-empty string required for argument %d");
+static char e_number_required_for_argument_nr[]
+ = N_("E1210: Number required for argument %d");
+static char e_string_or_list_required_for_argument_nr[]
+ = N_("E1222: String or List required for argument %d");
+
bool tv_in_free_unref_items = false;
// TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead
@@ -317,10 +326,12 @@ void tv_list_free_list(list_T *const l)
void tv_list_free(list_T *const l)
FUNC_ATTR_NONNULL_ALL
{
- if (!tv_in_free_unref_items) {
- tv_list_free_contents(l);
- tv_list_free_list(l);
+ if (tv_in_free_unref_items) {
+ return;
}
+
+ tv_list_free_contents(l);
+ tv_list_free_list(l);
}
/// Unreference a list
@@ -831,7 +842,7 @@ int tv_list_join(garray_T *const gap, list_T *const l, const char *const sep)
}
/// "join()" function
-void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_join(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[0].v_type != VAR_LIST) {
emsg(_(e_listreq));
@@ -855,7 +866,7 @@ void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "list2str()" function
-void f_list2str(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_list2str(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
garray_T ga;
@@ -889,8 +900,8 @@ void tv_list_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg)
list_T *l;
bool error = false;
- if (var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)),
- arg_errmsg, TV_TRANSLATE)) {
+ if (value_check_lock(tv_list_locked((l = argvars[0].vval.v_list)),
+ arg_errmsg, TV_TRANSLATE)) {
return;
}
@@ -1016,7 +1027,7 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero)
if (sortinfo->item_compare_lc) {
res = strcoll(p1, p2);
} else {
- res = sortinfo->item_compare_ic ? STRICMP(p1, p2): STRCMP(p1, p2);
+ res = sortinfo->item_compare_ic ? STRICMP(p1, p2): strcmp(p1, p2);
}
} else {
double n1, n2;
@@ -1079,15 +1090,17 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero)
rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this
funcexe_T funcexe = FUNCEXE_INIT;
- funcexe.evaluate = true;
- funcexe.partial = partial;
- funcexe.selfdict = sortinfo->item_compare_selfdict;
+ funcexe.fe_evaluate = true;
+ funcexe.fe_partial = partial;
+ funcexe.fe_selfdict = sortinfo->item_compare_selfdict;
res = call_func(func_name, -1, &rettv, 2, argv, &funcexe);
tv_clear(&argv[0]);
tv_clear(&argv[1]);
if (res == FAIL) {
+ // XXX: ITEM_COMPARE_FAIL is unused
res = ITEM_COMPARE_FAIL;
+ sortinfo->item_compare_func_err = true;
} else {
res = (int)tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err);
if (res > 0) {
@@ -1143,7 +1156,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
semsg(_(e_listarg), sort ? "sort()" : "uniq()");
} else {
list_T *const l = argvars[0].vval.v_list;
- if (var_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) {
+ if (value_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) {
goto theend;
}
tv_list_set_ret(rettv, l);
@@ -1242,20 +1255,18 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
item_compare_func_ptr = item_compare_keeping_zero;
}
- int idx = 0;
for (listitem_T *li = TV_LIST_ITEM_NEXT(l, tv_list_first(l))
; li != NULL;) {
listitem_T *const prev_li = TV_LIST_ITEM_PREV(l, li);
if (item_compare_func_ptr(&prev_li, &li) == 0) {
- if (info.item_compare_func_err) { // -V547
- emsg(_("E882: Uniq compare function failed"));
- break;
- }
li = tv_list_item_remove(l, li);
} else {
- idx++;
li = TV_LIST_ITEM_NEXT(l, li);
}
+ if (info.item_compare_func_err) {
+ emsg(_("E882: Uniq compare function failed"));
+ break;
+ }
}
}
@@ -1267,13 +1278,13 @@ theend:
}
/// "sort"({list})" function
-void f_sort(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_sort(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
do_sort_uniq(argvars, rettv, true);
}
/// "uniq({list})" function
-void f_uniq(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_uniq(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
do_sort_uniq(argvars, rettv, false);
}
@@ -1356,7 +1367,7 @@ void tv_list_reverse(list_T *const l)
/// true list will not be modified. Must be initialized to false
/// by the caller.
void tv_list_item_sort(list_T *const l, ListSortItem *const ptrs,
- const ListSorter item_compare_func, bool *errp)
+ const ListSorter item_compare_func, const bool *errp)
FUNC_ATTR_NONNULL_ARG(3, 4)
{
const int len = tv_list_len(l);
@@ -1568,7 +1579,7 @@ bool tv_callback_equal(const Callback *cb1, const Callback *cb2)
}
switch (cb1->type) {
case kCallbackFuncref:
- return STRCMP(cb1->data.funcref, cb2->data.funcref) == 0;
+ return strcmp(cb1->data.funcref, cb2->data.funcref) == 0;
case kCallbackPartial:
// FIXME: this is inconsistent with tv_equal but is needed for precision
// maybe change dictwatcheradd to return a watcher id instead?
@@ -1588,7 +1599,7 @@ void callback_free(Callback *callback)
{
switch (callback->type) {
case kCallbackFuncref:
- func_unref((char_u *)callback->data.funcref);
+ func_unref(callback->data.funcref);
xfree(callback->data.funcref);
break;
case kCallbackPartial:
@@ -1617,7 +1628,7 @@ void callback_put(Callback *cb, typval_T *tv)
case kCallbackFuncref:
tv->v_type = VAR_FUNC;
tv->vval.v_string = xstrdup(cb->data.funcref);
- func_ref((char_u *)cb->data.funcref);
+ func_ref(cb->data.funcref);
break;
case kCallbackLua:
// TODO(tjdevries): Unified Callback.
@@ -1643,7 +1654,7 @@ void callback_copy(Callback *dest, Callback *src)
break;
case kCallbackFuncref:
dest->data.funcref = xstrdup(src->data.funcref);
- func_ref((char_u *)src->data.funcref);
+ func_ref(src->data.funcref);
break;
case kCallbackLua:
dest->data.luaref = api_new_luaref(src->data.luaref);
@@ -1740,9 +1751,8 @@ static bool tv_dict_watcher_matches(DictWatcher *watcher, const char *const key)
const size_t len = watcher->key_pattern_len;
if (len && watcher->key_pattern[len - 1] == '*') {
return strncmp(key, watcher->key_pattern, len - 1) == 0;
- } else {
- return strcmp(key, watcher->key_pattern) == 0;
}
+ return strcmp(key, watcher->key_pattern) == 0;
}
/// Send a change notification to all dictionary watchers that match given key
@@ -1774,7 +1784,7 @@ void tv_dict_watcher_notify(dict_T *const dict, const char *const key, typval_T
tv_dict_add(argv[2].vval.v_dict, v);
}
- if (oldtv) {
+ if (oldtv && oldtv->v_type != VAR_UNKNOWN) {
dictitem_T *const v = tv_dict_item_alloc_len(S_LEN("old"));
tv_copy(oldtv, &v->di_tv);
tv_dict_add(argv[2].vval.v_dict, v);
@@ -1834,6 +1844,7 @@ dictitem_T *tv_dict_item_alloc_len(const char *const key, const size_t key_len)
di->di_key[key_len] = NUL;
di->di_flags = DI_FLAGS_ALLOC;
di->di_tv.v_lock = VAR_UNLOCKED;
+ di->di_tv.v_type = VAR_UNKNOWN;
return di;
}
@@ -2053,9 +2064,24 @@ int tv_dict_get_tv(dict_T *d, const char *const key, typval_T *rettv)
varnumber_T tv_dict_get_number(const dict_T *const d, const char *const key)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
+ return tv_dict_get_number_def(d, key, 0);
+}
+
+/// Get a number item from a dictionary.
+///
+/// Returns "def" if the entry doesn't exist.
+///
+/// @param[in] d Dictionary to get item from.
+/// @param[in] key Key to find in dictionary.
+/// @param[in] def Default value.
+///
+/// @return Dictionary item.
+varnumber_T tv_dict_get_number_def(const dict_T *const d, const char *const key, const int def)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
dictitem_T *const di = tv_dict_find(d, key, -1);
if (di == NULL) {
- return 0;
+ return def;
}
return tv_get_number(&di->di_tv);
}
@@ -2074,7 +2100,7 @@ char **tv_dict_to_env(dict_T *denv)
TV_DICT_ITER(denv, var, {
const char *str = tv_get_string(&var->di_tv);
assert(str);
- size_t len = STRLEN(var->di_key) + strlen(str) + strlen("=") + 1;
+ size_t len = strlen((char *)var->di_key) + strlen(str) + strlen("=") + 1;
env[i] = xmalloc(len);
snprintf(env[i], len, "%s=%s", (char *)var->di_key, str);
i++;
@@ -2183,6 +2209,15 @@ bool tv_dict_get_callback(dict_T *const d, const char *const key, const ptrdiff_
return res;
}
+/// Check for adding a function to g: or l:.
+/// If the name is wrong give an error message and return true.
+int tv_dict_wrong_func_name(dict_T *d, typval_T *tv, const char *name)
+{
+ return (d == &globvardict || &d->dv_hashtab == get_funccal_local_ht())
+ && tv_is_func(*tv)
+ && var_wrong_func_name(name, true);
+}
+
//{{{2 dict_add*
/// Add item to dictionary
@@ -2194,7 +2229,10 @@ bool tv_dict_get_callback(dict_T *const d, const char *const key, const ptrdiff_
int tv_dict_add(dict_T *const d, dictitem_T *const item)
FUNC_ATTR_NONNULL_ALL
{
- return hash_add(&d->dv_hashtab, item->di_key);
+ if (tv_dict_wrong_func_name(d, &item->di_tv, (const char *)item->di_key)) {
+ return FAIL;
+ }
+ return hash_add(&d->dv_hashtab, (char *)item->di_key);
}
/// Add a list entry to dictionary
@@ -2410,10 +2448,10 @@ void tv_dict_clear(dict_T *const d)
///
/// @param d1 Dictionary to extend.
/// @param[in] d2 Dictionary to extend with.
-/// @param[in] action "error", "force", "keep":
-///
+/// @param[in] action "error", "force", "move", "keep":
/// e*, including "error": duplicate key gives an error.
/// f*, including "force": duplicate d2 keys override d1.
+/// m*, including "move": move items instead of copying.
/// other, including "keep": duplicate d2 keys ignored.
void tv_dict_extend(dict_T *const d1, dict_T *const d2, const char *const action)
FUNC_ATTR_NONNULL_ALL
@@ -2422,27 +2460,33 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2, const char *const action
const char *const arg_errmsg = _("extend() argument");
const size_t arg_errmsg_len = strlen(arg_errmsg);
- TV_DICT_ITER(d2, di2, {
+ if (*action == 'm') {
+ hash_lock(&d2->dv_hashtab); // don't rehash on hash_remove()
+ }
+
+ HASHTAB_ITER(&d2->dv_hashtab, hi2, {
+ dictitem_T *const di2 = TV_DICT_HI2DI(hi2);
dictitem_T *const di1 = tv_dict_find(d1, (const char *)di2->di_key, -1);
- if (d1->dv_scope != VAR_NO_SCOPE) {
- // Disallow replacing a builtin function in l: and g:.
- // Check the key to be valid when adding to any scope.
- if (d1->dv_scope == VAR_DEF_SCOPE
- && tv_is_func(di2->di_tv)
- && !var_check_func_name((const char *)di2->di_key, di1 == NULL)) {
- break;
- }
- if (!valid_varname((const char *)di2->di_key)) {
- break;
- }
+ // Check the key to be valid when adding to any scope.
+ if (d1->dv_scope != VAR_NO_SCOPE && !valid_varname((const char *)di2->di_key)) {
+ break;
}
if (di1 == NULL) {
- dictitem_T *const new_di = tv_dict_item_copy(di2);
- if (tv_dict_add(d1, new_di) == FAIL) {
- tv_dict_item_free(new_di);
- } else if (watched) {
- tv_dict_watcher_notify(d1, (const char *)new_di->di_key, &new_di->di_tv,
- NULL);
+ if (*action == 'm') {
+ // Cheap way to move a dict item from "d2" to "d1".
+ // If dict_add() fails then "d2" won't be empty.
+ dictitem_T *const new_di = di2;
+ if (tv_dict_add(d1, new_di) == OK) {
+ hash_remove(&d2->dv_hashtab, hi2);
+ tv_dict_watcher_notify(d1, (const char *)new_di->di_key, &new_di->di_tv, NULL);
+ }
+ } else {
+ dictitem_T *const new_di = tv_dict_item_copy(di2);
+ if (tv_dict_add(d1, new_di) == FAIL) {
+ tv_dict_item_free(new_di);
+ } else if (watched) {
+ tv_dict_watcher_notify(d1, (const char *)new_di->di_key, &new_di->di_tv, NULL);
+ }
}
} else if (*action == 'e') {
semsg(_("E737: Key already exists: %s"), di2->di_key);
@@ -2450,10 +2494,14 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2, const char *const action
} else if (*action == 'f' && di2 != di1) {
typval_T oldtv;
- if (var_check_lock(di1->di_tv.v_lock, arg_errmsg, arg_errmsg_len)
+ if (value_check_lock(di1->di_tv.v_lock, arg_errmsg, arg_errmsg_len)
|| var_check_ro(di1->di_flags, arg_errmsg, arg_errmsg_len)) {
break;
}
+ // Disallow replacing a builtin function.
+ if (tv_dict_wrong_func_name(d1, &di2->di_tv, (const char *)di2->di_key)) {
+ break;
+ }
if (watched) {
tv_copy(&di1->di_tv, &oldtv);
@@ -2469,6 +2517,10 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2, const char *const action
}
}
});
+
+ if (*action == 'm') {
+ hash_unlock(&d2->dv_hashtab);
+ }
}
/// Compare two dictionaries
@@ -2483,10 +2535,14 @@ bool tv_dict_equal(dict_T *const d1, dict_T *const d2, const bool ic, const bool
if (d1 == d2) {
return true;
}
- if (d1 == NULL || d2 == NULL) {
+ if (tv_dict_len(d1) != tv_dict_len(d2)) {
return false;
}
- if (tv_dict_len(d1) != tv_dict_len(d2)) {
+ if (tv_dict_len(d1) == 0) {
+ // empty and NULL dicts are considered equal
+ return true;
+ }
+ if (d1 == NULL || d2 == NULL) {
return false;
}
@@ -2532,8 +2588,8 @@ dict_T *tv_dict_copy(const vimconv_T *const conv, dict_T *const orig, const bool
if (conv == NULL || conv->vc_type == CONV_NONE) {
new_di = tv_dict_item_alloc((const char *)di->di_key);
} else {
- size_t len = STRLEN(di->di_key);
- char *const key = (char *)string_convert(conv, di->di_key, &len);
+ size_t len = strlen((char *)di->di_key);
+ char *const key = (char *)string_convert(conv, (char *)di->di_key, &len);
if (key == NULL) {
new_di = tv_dict_item_alloc_len((const char *)di->di_key, len);
} else {
@@ -2654,7 +2710,7 @@ void tv_blob_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg)
{
blob_T *const b = argvars[0].vval.v_blob;
- if (b != NULL && var_check_lock(b->bv_lock, arg_errmsg, TV_TRANSLATE)) {
+ if (b != NULL && value_check_lock(b->bv_lock, arg_errmsg, TV_TRANSLATE)) {
return;
}
@@ -2674,7 +2730,7 @@ void tv_blob_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg)
}
if (argvars[2].v_type == VAR_UNKNOWN) {
// Remove one item, return its value.
- char_u *const p = (char_u *)b->bv_ga.ga_data;
+ uint8_t *const p = (uint8_t *)b->bv_ga.ga_data;
rettv->vval.v_number = (varnumber_T)(*(p + idx));
memmove(p + idx, p + idx + 1, (size_t)(len - idx - 1));
b->bv_ga.ga_len--;
@@ -2696,9 +2752,8 @@ void tv_blob_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg)
blob->bv_ga.ga_len = (int)(end - idx + 1);
ga_grow(&blob->bv_ga, (int)(end - idx + 1));
- char_u *const p = (char_u *)b->bv_ga.ga_data;
- memmove((char_u *)blob->bv_ga.ga_data, p + idx,
- (size_t)(end - idx + 1));
+ uint8_t *const p = (uint8_t *)b->bv_ga.ga_data;
+ memmove(blob->bv_ga.ga_data, p + idx, (size_t)(end - idx + 1));
tv_blob_set_ret(rettv, blob);
if (len - end - 1 > 0) {
@@ -2777,7 +2832,7 @@ static void tv_dict_list(typval_T *const tv, typval_T *const rettv, const DictLi
switch (what) {
case kDictListKeys:
tv_item.v_type = VAR_STRING;
- tv_item.vval.v_string = (char *)vim_strsave(di->di_key);
+ tv_item.vval.v_string = xstrdup((char *)di->di_key);
break;
case kDictListValues:
tv_copy(&di->di_tv, &tv_item);
@@ -2806,25 +2861,25 @@ static void tv_dict_list(typval_T *const tv, typval_T *const rettv, const DictLi
}
/// "items(dict)" function
-void f_items(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_items(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_dict_list(argvars, rettv, 2);
}
/// "keys()" function
-void f_keys(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_keys(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_dict_list(argvars, rettv, 0);
}
/// "values(dict)" function
-void f_values(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_values(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_dict_list(argvars, rettv, 1);
}
/// "has_key()" function
-void f_has_key(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_has_key(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[0].v_type != VAR_DICT) {
emsg(_(e_dictreq));
@@ -2846,7 +2901,7 @@ void tv_dict_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg)
if (argvars[2].v_type != VAR_UNKNOWN) {
semsg(_(e_toomanyarg), "remove()");
} else if ((d = argvars[0].vval.v_dict) != NULL
- && !var_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE)) {
+ && !value_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE)) {
const char *key = tv_get_string_chk(&argvars[1]);
if (key != NULL) {
dictitem_T *di = tv_dict_find(d, key, -1);
@@ -2951,7 +3006,7 @@ void tv_blob_copy(typval_T *const from, typval_T *const to)
(tv)->v_lock = VAR_UNLOCKED; \
} while (0)
-static inline int _nothing_conv_func_start(typval_T *const tv, char_u *const fun)
+static inline int _nothing_conv_func_start(typval_T *const tv, char *const fun)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ARG(1)
{
tv->v_lock = VAR_UNLOCKED;
@@ -3114,6 +3169,7 @@ static inline void _nothing_conv_dict_end(typval_T *const tv, dict_T **const dic
#define TYPVAL_ENCODE_FIRST_ARG_TYPE const void *const
#define TYPVAL_ENCODE_FIRST_ARG_NAME ignored
#include "nvim/eval/typval_encode.c.h"
+
#undef TYPVAL_ENCODE_SCOPE
#undef TYPVAL_ENCODE_NAME
#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
@@ -3178,7 +3234,7 @@ void tv_free(typval_T *tv)
partial_unref(tv->vval.v_partial);
break;
case VAR_FUNC:
- func_unref((char_u *)tv->vval.v_string);
+ func_unref(tv->vval.v_string);
FALLTHROUGH;
case VAR_STRING:
xfree(tv->vval.v_string);
@@ -3231,7 +3287,7 @@ void tv_copy(const typval_T *const from, typval_T *const to)
if (from->vval.v_string != NULL) {
to->vval.v_string = xstrdup(from->vval.v_string);
if (from->v_type == VAR_FUNC) {
- func_ref((char_u *)to->vval.v_string);
+ func_ref(to->vval.v_string);
}
}
break;
@@ -3403,12 +3459,12 @@ bool tv_check_lock(const typval_T *tv, const char *name, size_t name_len)
default:
break;
}
- return var_check_lock(tv->v_lock, name, name_len)
- || (lock != VAR_UNLOCKED && var_check_lock(lock, name, name_len));
+ return value_check_lock(tv->v_lock, name, name_len)
+ || (lock != VAR_UNLOCKED && value_check_lock(lock, name, name_len));
}
-/// @return true if variable "name" is locked (immutable)
-bool var_check_lock(VarLockStatus lock, const char *name, size_t name_len)
+/// @return true if variable "name" has a locked (immutable) value
+bool value_check_lock(VarLockStatus lock, const char *name, size_t name_len)
{
const char *error_message = NULL;
switch (lock) {
@@ -3583,13 +3639,13 @@ bool tv_check_str_or_nr(const typval_T *const tv)
#define FUNC_ERROR "E703: Using a Funcref as a Number"
static const char *const num_errors[] = {
- [VAR_PARTIAL]=N_(FUNC_ERROR),
- [VAR_FUNC]=N_(FUNC_ERROR),
- [VAR_LIST]=N_("E745: Using a List as a Number"),
- [VAR_DICT]=N_("E728: Using a Dictionary as a Number"),
- [VAR_FLOAT]=N_("E805: Using a Float as a Number"),
- [VAR_BLOB]=N_("E974: Using a Blob as a Number"),
- [VAR_UNKNOWN]=N_("E685: using an invalid value as a Number"),
+ [VAR_PARTIAL]= N_(FUNC_ERROR),
+ [VAR_FUNC]= N_(FUNC_ERROR),
+ [VAR_LIST]= N_("E745: Using a List as a Number"),
+ [VAR_DICT]= N_("E728: Using a Dictionary as a Number"),
+ [VAR_FLOAT]= N_("E805: Using a Float as a Number"),
+ [VAR_BLOB]= N_("E974: Using a Blob as a Number"),
+ [VAR_UNKNOWN]= N_("E685: using an invalid value as a Number"),
};
#undef FUNC_ERROR
@@ -3628,13 +3684,13 @@ bool tv_check_num(const typval_T *const tv)
#define FUNC_ERROR "E729: using Funcref as a String"
static const char *const str_errors[] = {
- [VAR_PARTIAL]=N_(FUNC_ERROR),
- [VAR_FUNC]=N_(FUNC_ERROR),
- [VAR_LIST]=N_("E730: using List as a String"),
- [VAR_DICT]=N_("E731: using Dictionary as a String"),
- [VAR_FLOAT]=((const char *)e_float_as_string),
- [VAR_BLOB]=N_("E976: using Blob as a String"),
- [VAR_UNKNOWN]=N_("E908: using an invalid value as a String"),
+ [VAR_PARTIAL]= N_(FUNC_ERROR),
+ [VAR_FUNC]= N_(FUNC_ERROR),
+ [VAR_LIST]= N_("E730: using List as a String"),
+ [VAR_DICT]= N_("E731: using Dictionary as a String"),
+ [VAR_FLOAT]= ((const char *)e_float_as_string),
+ [VAR_BLOB]= N_("E976: using Blob as a String"),
+ [VAR_UNKNOWN]= N_("E908: using an invalid value as a String"),
};
#undef FUNC_ERROR
@@ -3717,8 +3773,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((char_u *)tv->vval.v_string, NULL, NULL, STR2NR_ALL, &n, NULL, 0,
- false);
+ vim_str2nr(tv->vval.v_string, NULL, NULL, STR2NR_ALL, &n, NULL, 0, false);
}
return n;
}
@@ -3746,9 +3801,11 @@ varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error)
linenr_T tv_get_lnum(const typval_T *const tv)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
+ const int did_emsg_before = did_emsg;
linenr_T lnum = (linenr_T)tv_get_number_chk(tv, NULL);
- if (lnum == 0) { // No valid number, try using same function as line() does.
+ if (lnum <= 0 && did_emsg_before == did_emsg && tv->v_type != VAR_NUMBER) {
int fnum;
+ // No valid number, try using same function as line() does.
pos_T *const fp = var2fpos(tv, true, &fnum, false);
if (fp != NULL) {
lnum = fp->lnum;
@@ -3801,26 +3858,56 @@ float_T tv_get_float(const typval_T *const tv)
return 0;
}
-// Give an error and return FAIL unless "tv" is a string.
-int tv_check_for_string(const typval_T *const tv)
+/// Give an error and return FAIL unless "args[idx]" is a string.
+int tv_check_for_string_arg(const typval_T *const args, const int idx)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+{
+ if (args[idx].v_type != VAR_STRING) {
+ semsg(_(e_string_required_for_argument_nr), idx + 1);
+ return FAIL;
+ }
+ return OK;
+}
+
+/// Give an error and return FAIL unless "args[idx]" is a non-empty string.
+int tv_check_for_nonempty_string_arg(const typval_T *const args, const int idx)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
{
- if (tv->v_type != VAR_STRING) {
- emsg(_(e_stringreq));
+ if (tv_check_for_string_arg(args, idx) == FAIL) {
+ return FAIL;
+ }
+ if (args[idx].vval.v_string == NULL || *args[idx].vval.v_string == NUL) {
+ semsg(_(e_non_empty_string_required_for_argument_nr), idx + 1);
return FAIL;
}
return OK;
}
-// Give an error and return FAIL unless "tv" is a non-empty string.
-int tv_check_for_nonempty_string(const typval_T *const tv)
+/// Give an error and return FAIL unless "args[idx]" is a number.
+int tv_check_for_number_arg(const typval_T *const args, const int idx)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
{
- if (tv_check_for_string(tv) == FAIL) {
+ if (args[idx].v_type != VAR_NUMBER) {
+ semsg(_(e_number_required_for_argument_nr), idx + 1);
return FAIL;
}
- if (tv->vval.v_string == NULL || *tv->vval.v_string == NUL) {
- emsg(_(e_non_empty_string_required));
+ return OK;
+}
+
+/// Check for an optional number argument at "idx"
+int tv_check_for_opt_number_arg(const typval_T *const args, const int idx)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+{
+ return (args[idx].v_type == VAR_UNKNOWN
+ || tv_check_for_number_arg(args, idx) != FAIL) ? OK : FAIL;
+}
+
+/// Give an error and return FAIL unless "args[idx]" is a string or a list.
+int tv_check_for_string_or_list_arg(const typval_T *const args, const int idx)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+{
+ if (args[idx].v_type != VAR_STRING && args[idx].v_type != VAR_LIST) {
+ semsg(_(e_string_or_list_required_for_argument_nr), idx + 1);
return FAIL;
}
return OK;
diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h
index c4bc9f603b..3f59cd3547 100644
--- a/src/nvim/eval/typval.h
+++ b/src/nvim/eval/typval.h
@@ -2,11 +2,12 @@
#define NVIM_EVAL_TYPVAL_H
#include <assert.h>
-#include <inttypes.h>
#include <stdbool.h>
#include <stddef.h>
+#include <stdint.h>
#include <string.h>
+#include "nvim/eval/typval_defs.h"
#include "nvim/func_attr.h"
#include "nvim/garray.h"
#include "nvim/gettext.h"
@@ -15,405 +16,10 @@
#include "nvim/macros.h"
#include "nvim/mbyte_defs.h"
#include "nvim/message.h"
-#include "nvim/pos.h" // for linenr_T
#include "nvim/types.h"
-#ifdef LOG_LIST_ACTIONS
-# include "nvim/memory.h"
-#endif
-
-/// Type used for VimL VAR_NUMBER values
-typedef int64_t varnumber_T;
-typedef uint64_t uvarnumber_T;
-
-/// Type used for VimL VAR_FLOAT values
-typedef double float_T;
-
-/// Refcount for dict or list that should not be freed
-enum { DO_NOT_FREE_CNT = (INT_MAX / 2), };
-
-/// Additional values for tv_list_alloc() len argument
-enum ListLenSpecials {
- /// List length is not known in advance
- ///
- /// To be used when there is neither a way to know how many elements will be
- /// needed nor are any educated guesses.
- kListLenUnknown = -1,
- /// List length *should* be known, but is actually not
- ///
- /// All occurrences of this value should be eventually removed. This is for
- /// the case when the only reason why list length is not known is that it
- /// would be hard to code without refactoring, but refactoring is needed.
- kListLenShouldKnow = -2,
- /// List length may be known in advance, but it requires too much effort
- ///
- /// To be used when it looks impractical to determine list length.
- kListLenMayKnow = -3,
-};
-
-/// Maximal possible value of varnumber_T variable
-#define VARNUMBER_MAX INT64_MAX
-#define UVARNUMBER_MAX UINT64_MAX
-
-/// Minimal possible value of varnumber_T variable
-#define VARNUMBER_MIN INT64_MIN
-
-/// %d printf format specifier for varnumber_T
-#define PRIdVARNUMBER PRId64
-
-typedef struct listvar_S list_T;
-typedef struct dictvar_S dict_T;
-typedef struct partial_S partial_T;
-typedef struct blobvar_S blob_T;
-
-typedef struct ufunc ufunc_T;
-
-typedef enum {
- kCallbackNone = 0,
- kCallbackFuncref,
- kCallbackPartial,
- kCallbackLua,
-} CallbackType;
-
-typedef struct {
- union {
- char *funcref;
- partial_T *partial;
- LuaRef luaref;
- } data;
- CallbackType type;
-} Callback;
-
-#define CALLBACK_INIT { .type = kCallbackNone }
-#define CALLBACK_NONE ((Callback)CALLBACK_INIT)
-
-/// Structure holding dictionary watcher
-typedef struct dict_watcher {
- Callback callback;
- char *key_pattern;
- size_t key_pattern_len;
- QUEUE node;
- bool busy; // prevent recursion if the dict is changed in the callback
- bool needs_free;
-} DictWatcher;
-
-/// Bool variable values
-typedef enum {
- kBoolVarFalse, ///< v:false
- kBoolVarTrue, ///< v:true
-} BoolVarValue;
-
-/// Special variable values
-typedef enum {
- kSpecialVarNull, ///< v:null
-} SpecialVarValue;
-
-/// Variable lock status for typval_T.v_lock
-typedef enum {
- VAR_UNLOCKED = 0, ///< Not locked.
- VAR_LOCKED = 1, ///< User lock, can be unlocked.
- VAR_FIXED = 2, ///< Locked forever.
-} VarLockStatus;
-
-/// VimL variable types, for use in typval_T.v_type
-typedef enum {
- VAR_UNKNOWN = 0, ///< Unknown (unspecified) value.
- VAR_NUMBER, ///< Number, .v_number is used.
- VAR_STRING, ///< String, .v_string is used.
- VAR_FUNC, ///< Function reference, .v_string is used as function name.
- VAR_LIST, ///< List, .v_list is used.
- VAR_DICT, ///< Dictionary, .v_dict is used.
- VAR_FLOAT, ///< Floating-point value, .v_float is used.
- VAR_BOOL, ///< true, false
- VAR_SPECIAL, ///< Special value (null), .v_special
- ///< is used.
- VAR_PARTIAL, ///< Partial, .v_partial is used.
- VAR_BLOB, ///< Blob, .v_blob is used.
-} VarType;
-
-/// Structure that holds an internal variable value
-typedef struct {
- VarType v_type; ///< Variable type.
- VarLockStatus v_lock; ///< Variable lock status.
- union typval_vval_union {
- 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 *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
-typedef enum {
- VAR_NO_SCOPE = 0, ///< Not a scope dictionary.
- VAR_SCOPE = 1, ///< Scope dictionary which requires prefix (a:, v:, …).
- VAR_DEF_SCOPE = 2, ///< Scope dictionary which may be accessed without prefix
- ///< (l:, g:).
-} ScopeType;
-
-/// Structure to hold an item of a list
-typedef struct listitem_S listitem_T;
-
-struct listitem_S {
- listitem_T *li_next; ///< Next item in list.
- listitem_T *li_prev; ///< Previous item in list.
- typval_T li_tv; ///< Item value.
-};
-
-/// Structure used by those that are using an item in a list
-typedef struct listwatch_S listwatch_T;
-
-struct listwatch_S {
- listitem_T *lw_item; ///< Item being watched.
- listwatch_T *lw_next; ///< Next watcher.
-};
-
-/// Structure to hold info about a list
-/// Order of members is optimized to reduce padding.
-struct listvar_S {
- listitem_T *lv_first; ///< First item, NULL if none.
- listitem_T *lv_last; ///< Last item, NULL if none.
- listwatch_T *lv_watch; ///< First watcher, NULL if none.
- listitem_T *lv_idx_item; ///< When not NULL item at index "lv_idx".
- list_T *lv_copylist; ///< Copied list used by deepcopy().
- list_T *lv_used_next; ///< next list in used lists list.
- list_T *lv_used_prev; ///< Previous list in used lists list.
- int lv_refcount; ///< Reference count.
- int lv_len; ///< Number of items.
- int lv_idx; ///< Index of a cached item, used for optimising repeated l[idx].
- int lv_copyID; ///< ID used by deepcopy().
- VarLockStatus lv_lock; ///< Zero, VAR_LOCKED, VAR_FIXED.
-
- LuaRef lua_table_ref;
-};
-
-// Static list with 10 items. Use tv_list_init_static10() to initialize.
-typedef struct {
- list_T sl_list; // must be first
- listitem_T sl_items[10];
-} staticList10_T;
-
-#define TV_LIST_STATIC10_INIT { \
- .sl_list = { \
- .lv_first = NULL, \
- .lv_last = NULL, \
- .lv_refcount = 0, \
- .lv_len = 0, \
- .lv_watch = NULL, \
- .lv_idx_item = NULL, \
- .lv_lock = VAR_FIXED, \
- .lv_used_next = NULL, \
- .lv_used_prev = NULL, \
- }, \
-}
-
-#define TV_DICTITEM_STRUCT(...) \
- struct { \
- typval_T di_tv; /* Structure that holds scope dictionary itself. */ \
- uint8_t di_flags; /* Flags. */ \
- char_u di_key[__VA_ARGS__]; /* Key value. */ \
- }
-
-/// Structure to hold a scope dictionary
-///
-/// @warning Must be compatible with dictitem_T.
-///
-/// For use in find_var_in_ht to pretend that it found dictionary item when it
-/// finds scope dictionary.
-typedef TV_DICTITEM_STRUCT(1) ScopeDictDictItem;
-
-/// Structure to hold an item of a Dictionary
-///
-/// @warning Must be compatible with ScopeDictDictItem.
-///
-/// Also used for a variable.
-typedef TV_DICTITEM_STRUCT() dictitem_T;
-
-/// Flags for dictitem_T.di_flags
-typedef enum {
- DI_FLAGS_RO = 1, ///< Read-only value
- DI_FLAGS_RO_SBX = 2, ///< Value, read-only in the sandbox
- DI_FLAGS_FIX = 4, ///< Fixed value: cannot be :unlet or remove()d.
- DI_FLAGS_LOCK = 8, ///< Locked value.
- DI_FLAGS_ALLOC = 16, ///< Separately allocated.
-} DictItemFlags;
-
-/// Structure representing a Dictionary
-struct dictvar_S {
- VarLockStatus dv_lock; ///< Whole dictionary lock status.
- ScopeType dv_scope; ///< Non-zero (#VAR_SCOPE, #VAR_DEF_SCOPE) if
- ///< dictionary represents a scope (i.e. g:, l: …).
- int dv_refcount; ///< Reference count.
- int dv_copyID; ///< ID used when recursivery traversing a value.
- hashtab_T dv_hashtab; ///< Hashtab containing all items.
- dict_T *dv_copydict; ///< Copied dict used by deepcopy().
- dict_T *dv_used_next; ///< Next dictionary in used dictionaries list.
- dict_T *dv_used_prev; ///< Previous dictionary in used dictionaries list.
- QUEUE watchers; ///< Dictionary key watchers set by user code.
-
- LuaRef lua_table_ref;
-};
-
-/// Structure to hold info about a Blob
-struct blobvar_S {
- garray_T bv_ga; ///< Growarray with the data.
- int bv_refcount; ///< Reference count.
- VarLockStatus bv_lock; ///< VAR_UNLOCKED, VAR_LOCKED, VAR_FIXED.
-};
-
-/// Type used for script ID
-typedef int scid_T;
-/// Format argument for scid_T
-#define PRIdSCID "d"
-
-// SCript ConteXt (SCTX): identifies a script line.
-// When sourcing a script "sc_lnum" is zero, "sourcing_lnum" is the current
-// line number. When executing a user function "sc_lnum" is the line where the
-// function was defined, "sourcing_lnum" is the line number inside the
-// function. When stored with a function, mapping, option, etc. "sc_lnum" is
-// the line number in the script "sc_sid".
-typedef struct {
- scid_T sc_sid; // script ID
- int sc_seq; // sourcing sequence number
- linenr_T sc_lnum; // line number
-} sctx_T;
-
-/// Maximum number of function arguments
-#define MAX_FUNC_ARGS 20
-/// Short variable name length
-#define VAR_SHORT_LEN 20
-/// Number of fixed variables used for arguments
-#define FIXVAR_CNT 12
-
-/// Callback interface for C function reference>
-/// Used for managing functions that were registered with |register_cfunc|
-typedef int (*cfunc_T)(int argcount, typval_T *argvars, typval_T *rettv, void *state); // NOLINT
-/// Callback to clear cfunc_T and any associated state.
-typedef void (*cfunc_free_T)(void *state);
-
-// Structure to hold info for a function that is currently being executed.
-typedef struct funccall_S funccall_T;
-
-struct funccall_S {
- ufunc_T *func; ///< Function being called.
- int linenr; ///< Next line to be executed.
- int returned; ///< ":return" used.
- /// Fixed variables for arguments.
- TV_DICTITEM_STRUCT(VAR_SHORT_LEN + 1) fixvar[FIXVAR_CNT];
- dict_T l_vars; ///< l: local function variables.
- ScopeDictDictItem l_vars_var; ///< Variable for l: scope.
- dict_T l_avars; ///< a: argument variables.
- ScopeDictDictItem l_avars_var; ///< Variable for a: scope.
- list_T l_varlist; ///< List for a:000.
- listitem_T l_listitems[MAX_FUNC_ARGS]; ///< List items for a:000.
- typval_T *rettv; ///< Return value.
- linenr_T breakpoint; ///< Next line with breakpoint or zero.
- int dbg_tick; ///< Debug_tick when breakpoint was set.
- int level; ///< Top nesting level of executed function.
- proftime_T prof_child; ///< Time spent in a child.
- funccall_T *caller; ///< Calling function or NULL; or next funccal in
- ///< list pointed to by previous_funccal.
- int fc_refcount; ///< Number of user functions that reference this funccall.
- int fc_copyID; ///< CopyID used for garbage collection.
- garray_T fc_funcs; ///< List of ufunc_T* which keep a reference to "func".
-};
-
-/// Structure to hold info for a user function.
-struct ufunc {
- int uf_varargs; ///< variable nr of arguments
- int uf_flags;
- int uf_calls; ///< nr of active calls
- bool uf_cleared; ///< func_clear() was already called
- garray_T uf_args; ///< arguments
- garray_T uf_def_args; ///< default argument expressions
- garray_T uf_lines; ///< function lines
- int uf_profiling; ///< true when func is being profiled
- int uf_prof_initialized;
- // Managing cfuncs
- cfunc_T uf_cb; ///< C function extension callback
- cfunc_free_T uf_cb_free; ///< C function extension free callback
- void *uf_cb_state; ///< State of C function extension.
- // Profiling the function as a whole.
- int uf_tm_count; ///< nr of calls
- proftime_T uf_tm_total; ///< time spent in function + children
- proftime_T uf_tm_self; ///< time spent in function itself
- proftime_T uf_tm_children; ///< time spent in children this call
- // Profiling the function per line.
- int *uf_tml_count; ///< nr of times line was executed
- proftime_T *uf_tml_total; ///< time spent in a line + children
- proftime_T *uf_tml_self; ///< time spent in a line itself
- proftime_T uf_tml_start; ///< start time for current line
- proftime_T uf_tml_children; ///< time spent in children for this line
- proftime_T uf_tml_wait; ///< start wait time for current line
- int uf_tml_idx; ///< index of line being timed; -1 if none
- int uf_tml_execed; ///< line being timed was executed
- sctx_T uf_script_ctx; ///< SCTX where function was defined,
- ///< used for s: variables
- int uf_refcount; ///< reference count, see func_name_refcount()
- funccall_T *uf_scoped; ///< l: local variables for closure
- char_u *uf_name_exp; ///< if "uf_name[]" starts with SNR the name with
- ///< "<SNR>" as a string, otherwise NULL
- char_u uf_name[]; ///< Name of function (actual size equals name);
- ///< can start with <SNR>123_
- ///< (<SNR> is K_SPECIAL KS_EXTRA KE_SNR)
-};
-
-struct partial_S {
- int pt_refcount; ///< Reference count.
- char_u *pt_name; ///< Function name; when NULL use pt_func->name.
- ufunc_T *pt_func; ///< Function pointer; when NULL lookup function with
- ///< pt_name.
- bool pt_auto; ///< When true the partial was created by using dict.member
- ///< in handle_subscript().
- int pt_argc; ///< Number of arguments.
- typval_T *pt_argv; ///< Arguments in allocated array.
- dict_T *pt_dict; ///< Dict for "self".
-};
-
-/// Structure used for explicit stack while garbage collecting hash tables
-typedef struct ht_stack_S {
- hashtab_T *ht;
- struct ht_stack_S *prev;
-} ht_stack_T;
-
-/// Structure used for explicit stack while garbage collecting lists
-typedef struct list_stack_S {
- list_T *list;
- struct list_stack_S *prev;
-} list_stack_T;
-
-/// Structure representing one list item, used for sort array.
-typedef struct {
- listitem_T *item; ///< Sorted list item.
- int idx; ///< Sorted list item index.
-} ListSortItem;
-
-typedef int (*ListSorter)(const void *, const void *);
#ifdef LOG_LIST_ACTIONS
-
-/// List actions log entry
-typedef struct {
- uintptr_t l; ///< List log entry belongs to.
- uintptr_t li1; ///< First list item log entry belongs to, if applicable.
- uintptr_t li2; ///< Second list item log entry belongs to, if applicable.
- int len; ///< List length when log entry was created.
- const char *action; ///< Logged action.
-} ListLogEntry;
-
-typedef struct list_log ListLog;
-
-/// List actions log
-struct list_log {
- ListLog *next; ///< Next chunk or NULL.
- size_t capacity; ///< Number of entries in current chunk.
- size_t size; ///< Current chunk size.
- ListLogEntry entries[]; ///< Actual log entries.
-};
+# include "nvim/memory.h"
extern ListLog *list_log_first; ///< First list log chunk, NULL if missing
extern ListLog *list_log_last; ///< Last list log chunk
@@ -442,10 +48,8 @@ static inline ListLog *list_log_new(const size_t size)
return ret;
}
-static inline void list_log(const list_T *const l,
- const listitem_T *const li1,
- const listitem_T *const li2,
- const char *const action)
+static inline void list_log(const list_T *const l, const listitem_T *const li1,
+ const listitem_T *const li2, const char *const action)
REAL_FATTR_ALWAYS_INLINE;
/// Add new entry to log
@@ -489,7 +93,7 @@ static inline void list_log(const list_T *const l, const listitem_T *const li1,
#define TV_DICT_HI2DI(hi) \
((dictitem_T *)((hi)->hi_key - offsetof(dictitem_T, di_key)))
-static inline void tv_list_ref(list_T *const l)
+static inline void tv_list_ref(list_T *l)
REAL_FATTR_ALWAYS_INLINE;
/// Increase reference count for a given list
@@ -505,7 +109,7 @@ static inline void tv_list_ref(list_T *const l)
l->lv_refcount++;
}
-static inline void tv_list_set_ret(typval_T *const tv, list_T *const l)
+static inline void tv_list_set_ret(typval_T *tv, list_T *l)
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ARG(1);
/// Set a list as the return value. Increments the reference count.
@@ -519,7 +123,7 @@ static inline void tv_list_set_ret(typval_T *const tv, list_T *const l)
tv_list_ref(l);
}
-static inline VarLockStatus tv_list_locked(const list_T *const l)
+static inline VarLockStatus tv_list_locked(const list_T *l)
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT;
/// Get list lock status
@@ -562,7 +166,7 @@ static inline void tv_list_set_copyid(list_T *const l, const int copyid)
l->lv_copyID = copyid;
}
-static inline int tv_list_len(const list_T *const l)
+static inline int tv_list_len(const list_T *l)
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT;
/// Get the number of items in a list
@@ -577,7 +181,7 @@ static inline int tv_list_len(const list_T *const l)
return l->lv_len;
}
-static inline int tv_list_copyid(const list_T *const l)
+static inline int tv_list_copyid(const list_T *l)
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL;
/// Get list copyID
@@ -590,7 +194,7 @@ static inline int tv_list_copyid(const list_T *const l)
return l->lv_copyID;
}
-static inline list_T *tv_list_latest_copy(const list_T *const l)
+static inline list_T *tv_list_latest_copy(const list_T *l)
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL;
/// Get latest list copy
@@ -605,7 +209,7 @@ static inline list_T *tv_list_latest_copy(const list_T *const l)
return l->lv_copylist;
}
-static inline int tv_list_uidx(const list_T *const l, int n)
+static inline int tv_list_uidx(const list_T *l, int n)
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT;
/// Normalize index: that is, return either -1 or non-negative index
@@ -628,7 +232,7 @@ static inline int tv_list_uidx(const list_T *const l, int n)
return n;
}
-static inline bool tv_list_has_watchers(const list_T *const l)
+static inline bool tv_list_has_watchers(const list_T *l)
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT;
/// Check whether list has watchers
@@ -643,7 +247,7 @@ static inline bool tv_list_has_watchers(const list_T *const l)
return l && l->lv_watch;
}
-static inline listitem_T *tv_list_first(const list_T *const l)
+static inline listitem_T *tv_list_first(const list_T *l)
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT;
/// Get first list item
@@ -661,7 +265,7 @@ static inline listitem_T *tv_list_first(const list_T *const l)
return l->lv_first;
}
-static inline listitem_T *tv_list_last(const list_T *const l)
+static inline listitem_T *tv_list_last(const list_T *l)
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT;
/// Get last list item
@@ -679,7 +283,7 @@ static inline listitem_T *tv_list_last(const list_T *const l)
return l->lv_last;
}
-static inline void tv_dict_set_ret(typval_T *const tv, dict_T *const d)
+static inline void tv_dict_set_ret(typval_T *tv, dict_T *d)
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ARG(1);
/// Set a dictionary as the return value
@@ -695,7 +299,7 @@ static inline void tv_dict_set_ret(typval_T *const tv, dict_T *const d)
}
}
-static inline long tv_dict_len(const dict_T *const d)
+static inline long tv_dict_len(const dict_T *d)
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT;
/// Get the number of items in a Dictionary
@@ -709,7 +313,7 @@ static inline long tv_dict_len(const dict_T *const d)
return (long)d->dv_hashtab.ht_used;
}
-static inline bool tv_dict_is_watched(const dict_T *const d)
+static inline bool tv_dict_is_watched(const dict_T *d)
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT;
/// Check if dictionary is watched
@@ -722,7 +326,7 @@ static inline bool tv_dict_is_watched(const dict_T *const d)
return d && !QUEUE_EMPTY(&d->watchers);
}
-static inline void tv_blob_set_ret(typval_T *const tv, blob_T *const b)
+static inline void tv_blob_set_ret(typval_T *tv, blob_T *b)
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ARG(1);
/// Set a blob as the return value.
@@ -740,7 +344,7 @@ static inline void tv_blob_set_ret(typval_T *const tv, blob_T *const b)
}
}
-static inline int tv_blob_len(const blob_T *const b)
+static inline int tv_blob_len(const blob_T *b)
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT;
/// Get the length of the data in the blob, in bytes.
@@ -754,7 +358,7 @@ static inline int tv_blob_len(const blob_T *const b)
return b->bv_ga.ga_len;
}
-static inline char_u tv_blob_get(const blob_T *const b, int idx)
+static inline uint8_t tv_blob_get(const blob_T *b, int idx)
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT;
/// Get the byte at index `idx` in the blob.
@@ -763,12 +367,12 @@ static inline char_u tv_blob_get(const blob_T *const b, int idx)
/// @param[in] idx Index in a blob. Must be valid.
///
/// @return Byte value at the given index.
-static inline char_u tv_blob_get(const blob_T *const b, int idx)
+static inline uint8_t tv_blob_get(const blob_T *const b, int idx)
{
- return ((char_u *)b->bv_ga.ga_data)[idx];
+ return ((uint8_t *)b->bv_ga.ga_data)[idx];
}
-static inline void tv_blob_set(blob_T *const b, int idx, char_u c)
+static inline void tv_blob_set(blob_T *b, int idx, uint8_t c)
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL;
/// Store the byte `c` at index `idx` in the blob.
@@ -776,9 +380,9 @@ static inline void tv_blob_set(blob_T *const b, int idx, char_u c)
/// @param[in] b Blob to index. Cannot be NULL.
/// @param[in] idx Index in a blob. Must be valid.
/// @param[in] c Value to store.
-static inline void tv_blob_set(blob_T *const b, int idx, char_u c)
+static inline void tv_blob_set(blob_T *const b, int idx, uint8_t c)
{
- ((char_u *)b->bv_ga.ga_data)[idx] = c;
+ ((uint8_t *)b->bv_ga.ga_data)[idx] = c;
}
/// Initialize VimL object
@@ -793,12 +397,6 @@ static inline void tv_init(typval_T *const tv)
}
}
-#define TV_INITIAL_VALUE \
- ((typval_T) { \
- .v_type = VAR_UNKNOWN, \
- .v_lock = VAR_UNLOCKED, \
- })
-
/// Empty string
///
/// Needed for hack which allows not allocating empty string and still not
@@ -836,7 +434,7 @@ extern bool tv_in_free_unref_items;
/// @param li Name of the variable with current listitem_T entry.
/// @param code Cycle body.
#define TV_LIST_ITER(l, li, code) \
- _TV_LIST_ITER_MOD( , l, li, code)
+ _TV_LIST_ITER_MOD( , l, li, code) // NOLINT(whitespace/parens)
/// Iterate over a list
///
@@ -893,13 +491,9 @@ extern bool tv_in_free_unref_items;
} \
})
-static inline bool tv_get_float_chk(const typval_T *const tv,
- float_T *const ret_f)
+static inline bool tv_get_float_chk(const typval_T *tv, float_T *ret_f)
REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT;
-// FIXME circular dependency, cannot import message.h.
-bool semsg(const char *const fmt, ...);
-
/// Get the float value
///
/// Raises an error if object is not number or floating-point.
@@ -935,7 +529,7 @@ static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q)
return QUEUE_DATA(q, DictWatcher, node);
}
-static inline bool tv_is_func(const typval_T tv)
+static inline bool tv_is_func(typval_T tv)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_CONST;
/// Check whether given typval_T contains a function
@@ -970,4 +564,9 @@ EXTERN const size_t kTVTranslate INIT(= TV_TRANSLATE);
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/typval.h.generated.h"
#endif
+
+#define tv_get_bool tv_get_number
+#define tv_get_bool_chk tv_get_number_chk
+#define tv_dict_get_bool tv_dict_get_number_def
+
#endif // NVIM_EVAL_TYPVAL_H
diff --git a/src/nvim/eval/typval_defs.h b/src/nvim/eval/typval_defs.h
new file mode 100644
index 0000000000..4615198441
--- /dev/null
+++ b/src/nvim/eval/typval_defs.h
@@ -0,0 +1,399 @@
+#ifndef NVIM_EVAL_TYPVAL_DEFS_H
+#define NVIM_EVAL_TYPVAL_DEFS_H
+
+#include <inttypes.h>
+#include <limits.h>
+
+#include "nvim/garray.h"
+#include "nvim/hashtab.h"
+#include "nvim/lib/queue.h"
+#include "nvim/pos.h"
+#include "nvim/types.h"
+
+/// Type used for VimL VAR_NUMBER values
+typedef int64_t varnumber_T;
+typedef uint64_t uvarnumber_T;
+
+/// Refcount for dict or list that should not be freed
+enum { DO_NOT_FREE_CNT = (INT_MAX / 2), };
+
+/// Additional values for tv_list_alloc() len argument
+enum ListLenSpecials {
+ /// List length is not known in advance
+ ///
+ /// To be used when there is neither a way to know how many elements will be
+ /// needed nor are any educated guesses.
+ kListLenUnknown = -1,
+ /// List length *should* be known, but is actually not
+ ///
+ /// All occurrences of this value should be eventually removed. This is for
+ /// the case when the only reason why list length is not known is that it
+ /// would be hard to code without refactoring, but refactoring is needed.
+ kListLenShouldKnow = -2,
+ /// List length may be known in advance, but it requires too much effort
+ ///
+ /// To be used when it looks impractical to determine list length.
+ kListLenMayKnow = -3,
+};
+
+/// Maximal possible value of varnumber_T variable
+#define VARNUMBER_MAX INT64_MAX
+#define UVARNUMBER_MAX UINT64_MAX
+
+/// Minimal possible value of varnumber_T variable
+#define VARNUMBER_MIN INT64_MIN
+
+/// %d printf format specifier for varnumber_T
+#define PRIdVARNUMBER PRId64
+
+typedef struct listvar_S list_T;
+typedef struct dictvar_S dict_T;
+typedef struct partial_S partial_T;
+typedef struct blobvar_S blob_T;
+
+typedef struct ufunc ufunc_T;
+
+typedef enum {
+ kCallbackNone = 0,
+ kCallbackFuncref,
+ kCallbackPartial,
+ kCallbackLua,
+} CallbackType;
+
+typedef struct {
+ union {
+ char *funcref;
+ partial_T *partial;
+ LuaRef luaref;
+ } data;
+ CallbackType type;
+} Callback;
+
+#define CALLBACK_INIT { .type = kCallbackNone }
+#define CALLBACK_NONE ((Callback)CALLBACK_INIT)
+
+/// Structure holding dictionary watcher
+typedef struct dict_watcher {
+ Callback callback;
+ char *key_pattern;
+ size_t key_pattern_len;
+ QUEUE node;
+ bool busy; // prevent recursion if the dict is changed in the callback
+ bool needs_free;
+} DictWatcher;
+
+/// Bool variable values
+typedef enum {
+ kBoolVarFalse, ///< v:false
+ kBoolVarTrue, ///< v:true
+} BoolVarValue;
+
+/// Special variable values
+typedef enum {
+ kSpecialVarNull, ///< v:null
+} SpecialVarValue;
+
+/// Variable lock status for typval_T.v_lock
+typedef enum {
+ VAR_UNLOCKED = 0, ///< Not locked.
+ VAR_LOCKED = 1, ///< User lock, can be unlocked.
+ VAR_FIXED = 2, ///< Locked forever.
+} VarLockStatus;
+
+/// VimL variable types, for use in typval_T.v_type
+typedef enum {
+ VAR_UNKNOWN = 0, ///< Unknown (unspecified) value.
+ VAR_NUMBER, ///< Number, .v_number is used.
+ VAR_STRING, ///< String, .v_string is used.
+ VAR_FUNC, ///< Function reference, .v_string is used as function name.
+ VAR_LIST, ///< List, .v_list is used.
+ VAR_DICT, ///< Dictionary, .v_dict is used.
+ VAR_FLOAT, ///< Floating-point value, .v_float is used.
+ VAR_BOOL, ///< true, false
+ VAR_SPECIAL, ///< Special value (null), .v_special is used.
+ VAR_PARTIAL, ///< Partial, .v_partial is used.
+ VAR_BLOB, ///< Blob, .v_blob is used.
+} VarType;
+
+/// Structure that holds an internal variable value
+typedef struct {
+ VarType v_type; ///< Variable type.
+ VarLockStatus v_lock; ///< Variable lock status.
+ union typval_vval_union {
+ 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 *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;
+
+#define TV_INITIAL_VALUE \
+ ((typval_T) { \
+ .v_type = VAR_UNKNOWN, \
+ .v_lock = VAR_UNLOCKED, \
+ })
+
+/// Values for (struct dictvar_S).dv_scope
+typedef enum {
+ VAR_NO_SCOPE = 0, ///< Not a scope dictionary.
+ VAR_SCOPE = 1, ///< Scope dictionary which requires prefix (a:, v:, …).
+ VAR_DEF_SCOPE = 2, ///< Scope dictionary which may be accessed without prefix
+ ///< (l:, g:).
+} ScopeType;
+
+/// Structure to hold an item of a list
+typedef struct listitem_S listitem_T;
+
+struct listitem_S {
+ listitem_T *li_next; ///< Next item in list.
+ listitem_T *li_prev; ///< Previous item in list.
+ typval_T li_tv; ///< Item value.
+};
+
+/// Structure used by those that are using an item in a list
+typedef struct listwatch_S listwatch_T;
+
+struct listwatch_S {
+ listitem_T *lw_item; ///< Item being watched.
+ listwatch_T *lw_next; ///< Next watcher.
+};
+
+/// Structure to hold info about a list
+/// Order of members is optimized to reduce padding.
+struct listvar_S {
+ listitem_T *lv_first; ///< First item, NULL if none.
+ listitem_T *lv_last; ///< Last item, NULL if none.
+ listwatch_T *lv_watch; ///< First watcher, NULL if none.
+ listitem_T *lv_idx_item; ///< When not NULL item at index "lv_idx".
+ list_T *lv_copylist; ///< Copied list used by deepcopy().
+ list_T *lv_used_next; ///< next list in used lists list.
+ list_T *lv_used_prev; ///< Previous list in used lists list.
+ int lv_refcount; ///< Reference count.
+ int lv_len; ///< Number of items.
+ int lv_idx; ///< Index of a cached item, used for optimising repeated l[idx].
+ int lv_copyID; ///< ID used by deepcopy().
+ VarLockStatus lv_lock; ///< Zero, VAR_LOCKED, VAR_FIXED.
+
+ LuaRef lua_table_ref;
+};
+
+/// Static list with 10 items. Use tv_list_init_static10() to initialize.
+typedef struct {
+ list_T sl_list; // must be first
+ listitem_T sl_items[10];
+} staticList10_T;
+
+#define TV_LIST_STATIC10_INIT { \
+ .sl_list = { \
+ .lv_first = NULL, \
+ .lv_last = NULL, \
+ .lv_refcount = 0, \
+ .lv_len = 0, \
+ .lv_watch = NULL, \
+ .lv_idx_item = NULL, \
+ .lv_lock = VAR_FIXED, \
+ .lv_used_next = NULL, \
+ .lv_used_prev = NULL, \
+ }, \
+}
+
+#define TV_DICTITEM_STRUCT(...) \
+ struct { \
+ typval_T di_tv; /* Structure that holds scope dictionary itself. */ \
+ uint8_t di_flags; /* Flags. */ \
+ char_u di_key[__VA_ARGS__]; /* Key value. */ /* NOLINT(runtime/arrays)*/ \
+ }
+
+/// Structure to hold a scope dictionary
+///
+/// @warning Must be compatible with dictitem_T.
+///
+/// For use in find_var_in_ht to pretend that it found dictionary item when it
+/// finds scope dictionary.
+typedef TV_DICTITEM_STRUCT(1) ScopeDictDictItem;
+
+/// Structure to hold an item of a Dictionary
+///
+/// @warning Must be compatible with ScopeDictDictItem.
+///
+/// Also used for a variable.
+typedef TV_DICTITEM_STRUCT() dictitem_T;
+
+/// Flags for dictitem_T.di_flags
+typedef enum {
+ DI_FLAGS_RO = 1, ///< Read-only value
+ DI_FLAGS_RO_SBX = 2, ///< Value, read-only in the sandbox
+ DI_FLAGS_FIX = 4, ///< Fixed value: cannot be :unlet or remove()d.
+ DI_FLAGS_LOCK = 8, ///< Locked value.
+ DI_FLAGS_ALLOC = 16, ///< Separately allocated.
+} DictItemFlags;
+
+/// Structure representing a Dictionary
+struct dictvar_S {
+ VarLockStatus dv_lock; ///< Whole dictionary lock status.
+ ScopeType dv_scope; ///< Non-zero (#VAR_SCOPE, #VAR_DEF_SCOPE) if
+ ///< dictionary represents a scope (i.e. g:, l: …).
+ int dv_refcount; ///< Reference count.
+ int dv_copyID; ///< ID used when recursivery traversing a value.
+ hashtab_T dv_hashtab; ///< Hashtab containing all items.
+ dict_T *dv_copydict; ///< Copied dict used by deepcopy().
+ dict_T *dv_used_next; ///< Next dictionary in used dictionaries list.
+ dict_T *dv_used_prev; ///< Previous dictionary in used dictionaries list.
+ QUEUE watchers; ///< Dictionary key watchers set by user code.
+
+ LuaRef lua_table_ref;
+};
+
+/// Structure to hold info about a Blob
+struct blobvar_S {
+ garray_T bv_ga; ///< Growarray with the data.
+ int bv_refcount; ///< Reference count.
+ VarLockStatus bv_lock; ///< VAR_UNLOCKED, VAR_LOCKED, VAR_FIXED.
+};
+
+/// Type used for script ID
+typedef int scid_T;
+/// Format argument for scid_T
+#define PRIdSCID "d"
+
+/// SCript ConteXt (SCTX): identifies a script line.
+/// When sourcing a script "sc_lnum" is zero, "sourcing_lnum" is the current
+/// line number. When executing a user function "sc_lnum" is the line where the
+/// function was defined, "sourcing_lnum" is the line number inside the
+/// function. When stored with a function, mapping, option, etc. "sc_lnum" is
+/// the line number in the script "sc_sid".
+typedef struct {
+ scid_T sc_sid; ///< script ID
+ int sc_seq; ///< sourcing sequence number
+ linenr_T sc_lnum; ///< line number
+} sctx_T;
+
+/// Maximum number of function arguments
+enum { MAX_FUNC_ARGS = 20, };
+/// Short variable name length
+enum { VAR_SHORT_LEN = 20, };
+/// Number of fixed variables used for arguments
+enum { FIXVAR_CNT = 12, };
+
+/// Structure to hold info for a function that is currently being executed.
+typedef struct funccall_S funccall_T;
+
+struct funccall_S {
+ ufunc_T *func; ///< Function being called.
+ int linenr; ///< Next line to be executed.
+ int returned; ///< ":return" used.
+ /// Fixed variables for arguments.
+ TV_DICTITEM_STRUCT(VAR_SHORT_LEN + 1) fixvar[FIXVAR_CNT];
+ dict_T l_vars; ///< l: local function variables.
+ ScopeDictDictItem l_vars_var; ///< Variable for l: scope.
+ dict_T l_avars; ///< a: argument variables.
+ ScopeDictDictItem l_avars_var; ///< Variable for a: scope.
+ list_T l_varlist; ///< List for a:000.
+ listitem_T l_listitems[MAX_FUNC_ARGS]; ///< List items for a:000.
+ typval_T *rettv; ///< Return value.
+ linenr_T breakpoint; ///< Next line with breakpoint or zero.
+ int dbg_tick; ///< debug_tick when breakpoint was set.
+ int level; ///< Top nesting level of executed function.
+ proftime_T prof_child; ///< Time spent in a child.
+ funccall_T *caller; ///< Calling function or NULL; or next funccal in
+ ///< list pointed to by previous_funccal.
+ int fc_refcount; ///< Number of user functions that reference this funccall.
+ int fc_copyID; ///< CopyID used for garbage collection.
+ garray_T fc_funcs; ///< List of ufunc_T* which keep a reference to "func".
+};
+
+/// Structure to hold info for a user function.
+struct ufunc {
+ int uf_varargs; ///< variable nr of arguments
+ int uf_flags;
+ int uf_calls; ///< nr of active calls
+ bool uf_cleared; ///< func_clear() was already called
+ garray_T uf_args; ///< arguments
+ garray_T uf_def_args; ///< default argument expressions
+ garray_T uf_lines; ///< function lines
+ int uf_profiling; ///< true when func is being profiled
+ int uf_prof_initialized;
+ LuaRef uf_luaref; ///< lua callback, used if (uf_flags & FC_LUAREF)
+ // Profiling the function as a whole.
+ int uf_tm_count; ///< nr of calls
+ proftime_T uf_tm_total; ///< time spent in function + children
+ proftime_T uf_tm_self; ///< time spent in function itself
+ proftime_T uf_tm_children; ///< time spent in children this call
+ // Profiling the function per line.
+ int *uf_tml_count; ///< nr of times line was executed
+ proftime_T *uf_tml_total; ///< time spent in a line + children
+ proftime_T *uf_tml_self; ///< time spent in a line itself
+ proftime_T uf_tml_start; ///< start time for current line
+ proftime_T uf_tml_children; ///< time spent in children for this line
+ proftime_T uf_tml_wait; ///< start wait time for current line
+ int uf_tml_idx; ///< index of line being timed; -1 if none
+ int uf_tml_execed; ///< line being timed was executed
+ sctx_T uf_script_ctx; ///< SCTX where function was defined,
+ ///< used for s: variables
+ int uf_refcount; ///< reference count, see func_name_refcount()
+ funccall_T *uf_scoped; ///< l: local variables for closure
+ char *uf_name_exp; ///< if "uf_name[]" starts with SNR the name with
+ ///< "<SNR>" as a string, otherwise NULL
+ char uf_name[]; ///< Name of function (actual size equals name);
+ ///< can start with <SNR>123_
+ ///< (<SNR> is K_SPECIAL KS_EXTRA KE_SNR)
+};
+
+struct partial_S {
+ int pt_refcount; ///< Reference count.
+ char *pt_name; ///< Function name; when NULL use pt_func->name.
+ ufunc_T *pt_func; ///< Function pointer; when NULL lookup function with pt_name.
+ bool pt_auto; ///< When true the partial was created by using dict.member
+ ///< in handle_subscript().
+ int pt_argc; ///< Number of arguments.
+ typval_T *pt_argv; ///< Arguments in allocated array.
+ dict_T *pt_dict; ///< Dict for "self".
+};
+
+/// Structure used for explicit stack while garbage collecting hash tables
+typedef struct ht_stack_S {
+ hashtab_T *ht;
+ struct ht_stack_S *prev;
+} ht_stack_T;
+
+/// Structure used for explicit stack while garbage collecting lists
+typedef struct list_stack_S {
+ list_T *list;
+ struct list_stack_S *prev;
+} list_stack_T;
+
+/// Structure representing one list item, used for sort array.
+typedef struct {
+ listitem_T *item; ///< Sorted list item.
+ int idx; ///< Sorted list item index.
+} ListSortItem;
+
+typedef int (*ListSorter)(const void *, const void *);
+
+#ifdef LOG_LIST_ACTIONS
+/// List actions log entry
+typedef struct {
+ uintptr_t l; ///< List log entry belongs to.
+ uintptr_t li1; ///< First list item log entry belongs to, if applicable.
+ uintptr_t li2; ///< Second list item log entry belongs to, if applicable.
+ int len; ///< List length when log entry was created.
+ const char *action; ///< Logged action.
+} ListLogEntry;
+
+typedef struct list_log ListLog;
+
+/// List actions log
+struct list_log {
+ ListLog *next; ///< Next chunk or NULL.
+ size_t capacity; ///< Number of entries in current chunk.
+ size_t size; ///< Current chunk size.
+ ListLogEntry entries[]; ///< Actual log entries.
+};
+#endif
+
+#endif // NVIM_EVAL_TYPVAL_DEFS_H
diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h
index 73b36b8611..6d29286a58 100644
--- a/src/nvim/eval/typval_encode.c.h
+++ b/src/nvim/eval/typval_encode.c.h
@@ -250,7 +250,7 @@
#include "nvim/eval/typval.h"
#include "nvim/eval/typval_encode.h"
#include "nvim/func_attr.h"
-#include "nvim/lib/kvec.h"
+#include "klib/kvec.h"
// -V::1063
@@ -266,7 +266,7 @@ static inline int _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(
const MPConvStack *const mpstack, const int copyID,
const MPConvStackValType conv_type,
const char *const objname)
-REAL_FATTR_NONNULL_ARG(2, 3, 4, 7) REAL_FATTR_WARN_UNUSED_RESULT
+ REAL_FATTR_NONNULL_ARG(2, 3, 4, 7) REAL_FATTR_WARN_UNUSED_RESULT
REAL_FATTR_ALWAYS_INLINE;
/// Function for checking whether container references itself
@@ -301,7 +301,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
MPConvStack *const mpstack, MPConvStackVal *const cur_mpsv,
typval_T *const tv, const int copyID,
const char *const objname)
-REAL_FATTR_NONNULL_ARG(2, 4, 6) REAL_FATTR_WARN_UNUSED_RESULT;
+ REAL_FATTR_NONNULL_ARG(2, 4, 6) REAL_FATTR_WARN_UNUSED_RESULT;
/// Convert single value
///
@@ -339,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, (char_u *)tv->vval.v_string);
+ TYPVAL_ENCODE_CONV_FUNC_START(tv, 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);
@@ -347,7 +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(tv, (pt == NULL ? NULL : (char_u *)partial_name(pt))); // -V547
+ TYPVAL_ENCODE_CONV_FUNC_START(tv, (pt == NULL ? NULL : partial_name(pt))); // -V547
_mp_push(*mpstack, ((MPConvStackVal) { // -V779
.type = kMPConvPartial,
.tv = tv,
@@ -358,7 +358,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
.pt = tv->vval.v_partial,
},
},
- }));
+ }));
break;
}
case VAR_LIST: {
@@ -381,7 +381,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
.li = tv_list_first(tv->vval.v_list),
},
},
- }));
+ }));
TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, _mp_last(*mpstack));
break;
}
@@ -459,8 +459,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
const listitem_T *const highest_bits_li = (
TV_LIST_ITEM_NEXT(val_list, sign_li));
if (TV_LIST_ITEM_TV(highest_bits_li)->v_type != VAR_NUMBER
- || ((highest_bits
- = TV_LIST_ITEM_TV(highest_bits_li)->vval.v_number)
+ || ((highest_bits = TV_LIST_ITEM_TV(highest_bits_li)->vval.v_number)
< 0)) {
goto _convert_one_value_regular_dict;
}
@@ -536,7 +535,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
.li = tv_list_first(val_di->di_tv.vval.v_list),
},
},
- }));
+ }));
break;
}
case kMPMap: {
@@ -571,7 +570,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
.li = tv_list_first(val_list),
},
},
- }));
+ }));
break;
}
case kMPExt: {
@@ -581,8 +580,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
|| tv_list_len((val_list = val_di->di_tv.vval.v_list)) != 2
|| (TV_LIST_ITEM_TV(tv_list_first(val_list))->v_type
!= VAR_NUMBER)
- || ((type
- = TV_LIST_ITEM_TV(tv_list_first(val_list))->vval.v_number)
+ || ((type = TV_LIST_ITEM_TV(tv_list_first(val_list))->vval.v_number)
> INT8_MAX)
|| type < INT8_MIN
|| (TV_LIST_ITEM_TV(tv_list_last(val_list))->v_type
@@ -622,7 +620,7 @@ _convert_one_value_regular_dict: {}
.todo = tv->vval.v_dict->dv_hashtab.ht_used,
},
},
- }));
+ }));
TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, tv->vval.v_dict,
_mp_last(*mpstack));
break;
@@ -640,7 +638,7 @@ typval_encode_stop_converting_one_item:
TYPVAL_ENCODE_SCOPE int _TYPVAL_ENCODE_ENCODE(
TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
typval_T *const tv, const char *const objname)
-REAL_FATTR_NONNULL_ARG(2, 3) REAL_FATTR_WARN_UNUSED_RESULT;
+ REAL_FATTR_NONNULL_ARG(2, 3) REAL_FATTR_WARN_UNUSED_RESULT;
/// Convert the whole typval
///
@@ -758,7 +756,7 @@ typval_encode_stop_converting_one_item:
.todo = (size_t)pt->pt_argc,
},
},
- }));
+ }));
}
break;
case kMPConvPartialSelf: {
@@ -797,7 +795,7 @@ typval_encode_stop_converting_one_item:
.todo = dict->dv_hashtab.ht_used,
},
},
- }));
+ }));
TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(NULL, pt->pt_dict,
_mp_last(mpstack));
} else {
diff --git a/src/nvim/eval/typval_encode.h b/src/nvim/eval/typval_encode.h
index ed70ba87ec..2f19144da3 100644
--- a/src/nvim/eval/typval_encode.h
+++ b/src/nvim/eval/typval_encode.h
@@ -10,9 +10,9 @@
#include <stddef.h>
#include <string.h>
+#include "klib/kvec.h"
#include "nvim/eval/typval.h"
#include "nvim/func_attr.h"
-#include "nvim/lib/kvec.h"
/// Type of the stack entry
typedef enum {
@@ -71,7 +71,7 @@ typedef kvec_withinit_t(MPConvStackVal, 8) MPConvStack;
#define _mp_pop kv_pop
#define _mp_last kv_last
-static inline size_t tv_strlen(const typval_T *const tv)
+static inline size_t tv_strlen(const typval_T *tv)
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT
REAL_FATTR_NONNULL_ALL;
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index c46cb6ba5d..22c5b1954d 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -3,45 +3,52 @@
// User defined function support
+#include <assert.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lauxlib.h"
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/debugger.h"
-#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/encode.h"
#include "nvim/eval/funcs.h"
+#include "nvim/eval/typval.h"
#include "nvim/eval/userfunc.h"
#include "nvim/eval/vars.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
#include "nvim/ex_getln.h"
-#include "nvim/fileio.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
#include "nvim/globals.h"
#include "nvim/insexpand.h"
+#include "nvim/keycodes.h"
#include "nvim/lua/executor.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
+#include "nvim/memline_defs.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
+#include "nvim/option_defs.h"
#include "nvim/os/input.h"
+#include "nvim/path.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
#include "nvim/search.h"
+#include "nvim/strings.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
-// flags used in uf_flags
-#define FC_ABORT 0x01 // abort function on error
-#define FC_RANGE 0x02 // function accepts range
-#define FC_DICT 0x04 // Dict function, uses "self"
-#define FC_CLOSURE 0x08 // closure, uses outer scope variables
-#define FC_DELETED 0x10 // :delfunction used while uf_refcount > 0
-#define FC_REMOVED 0x20 // function redefined while uf_refcount > 0
-#define FC_SANDBOX 0x40 // function defined in the sandbox
-#define FC_DEAD 0x80 // function kept only for reference to dfunc
-#define FC_EXPORT 0x100 // "export def Func()"
-#define FC_NOARGS 0x200 // no a: variables in lambda
-#define FC_VIM9 0x400 // defined in vim9 script file
-#define FC_CFUNC 0x800 // C function extension
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/userfunc.c.generated.h"
#endif
@@ -62,6 +69,8 @@ static char *e_funcexts = N_("E122: Function %s already exists, add ! to replace
static char *e_funcdict = N_("E717: Dictionary entry already exists");
static char *e_funcref = N_("E718: Funcref required");
static char *e_nofunc = N_("E130: Unknown function: %s");
+static char e_no_white_space_allowed_before_str_str[]
+ = N_("E1068: No white space allowed before '%s': %s");
void func_init(void)
{
@@ -75,7 +84,7 @@ hashtab_T *func_tbl_get(void)
}
/// Get function arguments.
-static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int *varargs,
+static int get_function_args(char **argp, char endchar, garray_T *newargs, int *varargs,
garray_T *default_args, bool skip)
{
bool mustend = false;
@@ -85,10 +94,10 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int
int i;
if (newargs != NULL) {
- ga_init(newargs, (int)sizeof(char_u *), 3);
+ ga_init(newargs, (int)sizeof(char *), 3);
}
if (default_args != NULL) {
- ga_init(default_args, (int)sizeof(char_u *), 3);
+ ga_init(default_args, (int)sizeof(char *), 3);
}
if (varargs != NULL) {
@@ -97,7 +106,7 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int
// Isolate the arguments: "arg1, arg2, ...)"
bool any_default = false;
- while (*p != (char)endchar) {
+ while (*p != endchar) {
if (p[0] == '.' && p[1] == '.' && p[2] == '.') {
if (varargs != NULL) {
*varargs = true;
@@ -109,9 +118,9 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int
while (ASCII_ISALNUM(*p) || *p == '_') {
p++;
}
- if (arg == p || isdigit(*arg)
- || (p - arg == 9 && STRNCMP(arg, "firstline", 9) == 0)
- || (p - arg == 8 && STRNCMP(arg, "lastline", 8) == 0)) {
+ if (arg == p || isdigit((uint8_t)(*arg))
+ || (p - arg == 9 && strncmp(arg, "firstline", 9) == 0)
+ || (p - arg == 8 && strncmp(arg, "lastline", 8) == 0)) {
if (!skip) {
semsg(_("E125: Illegal argument: %s"), arg);
}
@@ -125,7 +134,7 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int
// Check for duplicate argument name.
for (i = 0; i < newargs->ga_len; i++) {
- if (STRCMP(((char **)(newargs->ga_data))[i], arg) == 0) {
+ if (strcmp(((char **)(newargs->ga_data))[i], arg) == 0) {
semsg(_("E853: Duplicate argument name: %s"), arg);
xfree(arg);
goto err_ret;
@@ -142,18 +151,18 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int
any_default = true;
p = skipwhite(p) + 1;
p = skipwhite(p);
- char_u *expr = (char_u *)p;
+ char *expr = p;
if (eval1(&p, &rettv, false) != FAIL) {
ga_grow(default_args, 1);
// trim trailing whitespace
- while (p > (char *)expr && ascii_iswhite(p[-1])) {
+ while (p > expr && ascii_iswhite(p[-1])) {
p--;
}
c = (char_u)(*p);
*p = NUL;
- expr = vim_strsave(expr);
- ((char **)(default_args->ga_data))[default_args->ga_len] = (char *)expr;
+ expr = xstrdup(expr);
+ ((char **)(default_args->ga_data))[default_args->ga_len] = expr;
default_args->ga_len++;
*p = (char)c;
} else {
@@ -163,6 +172,15 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int
emsg(_("E989: Non-default argument follows default argument"));
mustend = true;
}
+
+ if (ascii_iswhite(*p) && *skipwhite(p) == ',') {
+ // Be tolerant when skipping
+ if (!skip) {
+ semsg(_(e_no_white_space_allowed_before_str_str), ",", p);
+ goto err_ret;
+ }
+ p = skipwhite(p);
+ }
if (*p == ',') {
p++;
} else {
@@ -170,14 +188,14 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int
}
}
p = skipwhite(p);
- if (mustend && *p != (char)endchar) {
+ if (mustend && *p != endchar) {
if (!skip) {
semsg(_(e_invarg2), *argp);
}
break;
}
}
- if (*p != (char)endchar) {
+ if (*p != endchar) {
goto err_ret;
}
p++; // skip "endchar"
@@ -211,21 +229,21 @@ static void register_closure(ufunc_T *fp)
}
/// @return a name for a lambda. Returned in static memory.
-char_u *get_lambda_name(void)
+char *get_lambda_name(void)
{
- static char_u name[30];
+ static char name[30];
static int lambda_no = 0;
- snprintf((char *)name, sizeof(name), "<lambda>%d", ++lambda_no);
+ snprintf(name, sizeof(name), "<lambda>%d", ++lambda_no);
return name;
}
-static void set_ufunc_name(ufunc_T *fp, char_u *name)
+static void set_ufunc_name(ufunc_T *fp, char *name)
{
STRCPY(fp->uf_name, name);
- if (name[0] == K_SPECIAL) {
- fp->uf_name_exp = xmalloc(STRLEN(name) + 3);
+ if ((uint8_t)name[0] == K_SPECIAL) {
+ fp->uf_name_exp = xmalloc(strlen(name) + 3);
STRCPY(fp->uf_name_exp, "<SNR>");
STRCAT(fp->uf_name_exp, fp->uf_name + 3);
}
@@ -242,13 +260,13 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
partial_T *pt = NULL;
int varargs;
int ret;
- char_u *start = (char_u *)skipwhite(*arg + 1);
- char_u *s, *e;
+ char *start = skipwhite(*arg + 1);
+ char *s, *e;
bool *old_eval_lavars = eval_lavars_used;
bool eval_lavars = false;
// First, check if this is a lambda expression. "->" must exists.
- ret = get_function_args((char **)&start, '-', NULL, NULL, NULL, true);
+ ret = get_function_args(&start, '-', NULL, NULL, NULL, true);
if (ret == FAIL || *start != '>') {
return NOTDONE;
}
@@ -272,38 +290,39 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
// Get the start and the end of the expression.
*arg = skipwhite((*arg) + 1);
- s = (char_u *)(*arg);
+ s = *arg;
ret = skip_expr(arg);
if (ret == FAIL) {
goto errret;
}
- e = (char_u *)(*arg);
+ e = *arg;
*arg = skipwhite(*arg);
if (**arg != '}') {
+ semsg(_("E451: Expected }: %s"), *arg);
goto errret;
}
(*arg)++;
if (evaluate) {
int flags = 0;
- char_u *p;
+ char *p;
garray_T newlines;
- char_u *name = get_lambda_name();
+ char *name = get_lambda_name();
- fp = xcalloc(1, offsetof(ufunc_T, uf_name) + STRLEN(name) + 1);
+ fp = xcalloc(1, offsetof(ufunc_T, uf_name) + strlen(name) + 1);
pt = xcalloc(1, sizeof(partial_T));
- ga_init(&newlines, (int)sizeof(char_u *), 1);
+ ga_init(&newlines, (int)sizeof(char *), 1);
ga_grow(&newlines, 1);
// Add "return " before the expression.
size_t len = (size_t)(7 + e - s + 1);
- p = (char_u *)xmalloc(len);
- ((char **)(newlines.ga_data))[newlines.ga_len++] = (char *)p;
+ p = xmalloc(len);
+ ((char **)(newlines.ga_data))[newlines.ga_len++] = p;
STRCPY(p, "return ");
- STRLCPY(p + 7, s, e - s + 1);
- if (strstr((char *)p + 7, "a:") == NULL) {
+ xstrlcpy(p + 7, s, (size_t)(e - s) + 1);
+ if (strstr(p + 7, "a:") == NULL) {
// No a: variables are used for sure.
flags |= FC_NOARGS;
}
@@ -312,7 +331,7 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
set_ufunc_name(fp, name);
hash_add(&func_hashtab, UF2HIKEY(fp));
fp->uf_args = newargs;
- ga_init(&fp->uf_def_args, (int)sizeof(char_u *), 1);
+ ga_init(&fp->uf_def_args, (int)sizeof(char *), 1);
fp->uf_lines = newlines;
if (current_funccal != NULL && eval_lavars) {
flags |= FC_CLOSURE;
@@ -365,7 +384,7 @@ errret:
/// was not found.
///
/// @return name of the function.
-char_u *deref_func_name(const char *name, int *lenp, partial_T **const partialp, bool no_autoload)
+char *deref_func_name(const char *name, int *lenp, partial_T **const partialp, bool no_autoload)
FUNC_ATTR_NONNULL_ARG(1, 2)
{
if (partialp != NULL) {
@@ -376,10 +395,10 @@ char_u *deref_func_name(const char *name, int *lenp, partial_T **const partialp,
if (v != NULL && v->di_tv.v_type == VAR_FUNC) {
if (v->di_tv.vval.v_string == NULL) { // just in case
*lenp = 0;
- return (char_u *)"";
+ return "";
}
- *lenp = (int)STRLEN(v->di_tv.vval.v_string);
- return (char_u *)v->di_tv.vval.v_string;
+ *lenp = (int)strlen(v->di_tv.vval.v_string);
+ return v->di_tv.vval.v_string;
}
if (v != NULL && v->di_tv.v_type == VAR_PARTIAL) {
@@ -387,31 +406,31 @@ char_u *deref_func_name(const char *name, int *lenp, partial_T **const partialp,
if (pt == NULL) { // just in case
*lenp = 0;
- return (char_u *)"";
+ return "";
}
if (partialp != NULL) {
*partialp = pt;
}
- char_u *s = (char_u *)partial_name(pt);
- *lenp = (int)STRLEN(s);
+ char *s = partial_name(pt);
+ *lenp = (int)strlen(s);
return s;
}
- return (char_u *)name;
+ return (char *)name;
}
/// Give an error message with a function name. Handle <SNR> things.
///
/// @param ermsg must be passed without translation (use N_() instead of _()).
/// @param name function name
-void emsg_funcname(char *ermsg, const char_u *name)
+void emsg_funcname(char *ermsg, const char *name)
{
- char_u *p;
+ char *p;
- if (*name == K_SPECIAL) {
- p = concat_str((char_u *)"<SNR>", name + 3);
+ if ((uint8_t)(*name) == K_SPECIAL) {
+ p = concat_str("<SNR>", name + 3);
} else {
- p = (char_u *)name;
+ p = (char *)name;
}
semsg(_(ermsg), p);
@@ -429,7 +448,7 @@ void emsg_funcname(char *ermsg, const char_u *name)
/// @param funcexe various values
///
/// @return OK or FAIL.
-int get_func_tv(const char_u *name, int len, typval_T *rettv, char **arg, funcexe_T *funcexe)
+int get_func_tv(const char *name, int len, typval_T *rettv, char **arg, funcexe_T *funcexe)
{
char *argp;
int ret = OK;
@@ -439,12 +458,12 @@ int get_func_tv(const char_u *name, int len, typval_T *rettv, char **arg, funcex
// Get the arguments.
argp = *arg;
while (argcount < MAX_FUNC_ARGS
- - (funcexe->partial == NULL ? 0 : funcexe->partial->pt_argc)) {
+ - (funcexe->fe_partial == NULL ? 0 : funcexe->fe_partial->pt_argc)) {
argp = skipwhite(argp + 1); // skip the '(' or ','
if (*argp == ')' || *argp == ',' || *argp == NUL) {
break;
}
- if (eval1(&argp, &argvars[argcount], funcexe->evaluate) == FAIL) {
+ if (eval1(&argp, &argvars[argcount], funcexe->fe_evaluate) == FAIL) {
ret = FAIL;
break;
}
@@ -463,7 +482,7 @@ int get_func_tv(const char_u *name, int len, typval_T *rettv, char **arg, funcex
int i = 0;
if (get_vim_var_nr(VV_TESTING)) {
- // Prepare for calling garbagecollect_for_testing(), need to know
+ // Prepare for calling test_garbagecollect_now(), need to know
// what variables are used on the call stack.
if (funcargs.ga_itemsize == 0) {
ga_init(&funcargs, (int)sizeof(typval_T *), 50);
@@ -473,7 +492,7 @@ int get_func_tv(const char_u *name, int len, typval_T *rettv, char **arg, funcex
((typval_T **)funcargs.ga_data)[funcargs.ga_len++] = &argvars[i];
}
}
- ret = call_func((char *)name, len, rettv, argcount, argvars, funcexe);
+ ret = call_func(name, len, rettv, argcount, argvars, funcexe);
funcargs.ga_len -= i;
} else if (!aborting()) {
@@ -525,48 +544,47 @@ static inline bool eval_fname_sid(const char *const name)
///
/// @return transformed name: either `fname_buf` or a pointer to an allocated
/// memory.
-static char_u *fname_trans_sid(const char_u *const name, char_u *const fname_buf,
- char_u **const tofree, int *const error)
+static char *fname_trans_sid(const char *const name, char *const fname_buf, char **const tofree,
+ int *const error)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
- char_u *fname;
- const int llen = eval_fname_script((const char *)name);
- if (llen > 0) {
- fname_buf[0] = K_SPECIAL;
- fname_buf[1] = KS_EXTRA;
- 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, (size_t)(FLEN_FIXED + 1 - i), "%" PRId64 "_",
- (int64_t)current_sctx.sc_sid);
- i = (int)STRLEN(fname_buf);
- }
- }
- if ((size_t)i + STRLEN(name + llen) < FLEN_FIXED) {
- STRCPY(fname_buf + i, name + llen);
- fname = fname_buf;
+ const int llen = eval_fname_script(name);
+ if (llen == 0) {
+ return (char *)name; // no prefix
+ }
+
+ fname_buf[0] = (char)K_SPECIAL;
+ fname_buf[1] = (char)KS_EXTRA;
+ fname_buf[2] = KE_SNR;
+ int i = 3;
+ if (eval_fname_sid(name)) { // "<SID>" or "s:"
+ if (current_sctx.sc_sid <= 0) {
+ *error = FCERR_SCRIPT;
} else {
- fname = xmalloc((size_t)i + STRLEN(name + llen) + 1);
- *tofree = fname;
- memmove(fname, fname_buf, (size_t)i);
- STRCPY(fname + i, name + llen);
+ snprintf(fname_buf + i, (size_t)(FLEN_FIXED + 1 - i), "%" PRId64 "_",
+ (int64_t)current_sctx.sc_sid);
+ i = (int)strlen(fname_buf);
}
+ }
+ char *fname;
+ if ((size_t)i + strlen(name + llen) < FLEN_FIXED) {
+ STRCPY(fname_buf + i, name + llen);
+ fname = fname_buf;
} else {
- fname = (char_u *)name;
+ fname = xmalloc((size_t)i + strlen(name + llen) + 1);
+ *tofree = fname;
+ memmove(fname, fname_buf, (size_t)i);
+ STRCPY(fname + i, name + llen);
}
-
return fname;
}
/// Find a function by name, return pointer to it in ufuncs.
///
/// @return NULL for unknown function.
-ufunc_T *find_func(const char_u *name)
+ufunc_T *find_func(const char *name)
{
- hashitem_T *hi = hash_find(&func_hashtab, (char *)name);
+ hashitem_T *hi = hash_find(&func_hashtab, name);
if (!HASHITEM_EMPTY(hi)) {
return HI2UF(hi);
}
@@ -576,9 +594,9 @@ ufunc_T *find_func(const char_u *name)
/// 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)
+static void cat_func_name(char *buf, ufunc_T *fp)
{
- if (fp->uf_name[0] == K_SPECIAL) {
+ if ((uint8_t)fp->uf_name[0] == K_SPECIAL) {
STRCPY(buf, "<SNR>");
STRCAT(buf, fp->uf_name + 3);
} else {
@@ -593,7 +611,7 @@ static void add_nr_var(dict_T *dp, dictitem_T *v, char *name, varnumber_T nr)
STRCPY(v->di_key, name);
#endif
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
- tv_dict_add(dp, v);
+ hash_add(&dp->dv_hashtab, (char *)v->di_key);
v->di_tv.v_type = VAR_NUMBER;
v->di_tv.v_lock = VAR_FIXED;
v->di_tv.vval.v_number = nr;
@@ -758,9 +776,9 @@ static void func_clear_items(ufunc_T *fp)
ga_clear_strings(&(fp->uf_lines));
XFREE_CLEAR(fp->uf_name_exp);
- if (fp->uf_cb_free != NULL) {
- fp->uf_cb_free(fp->uf_cb_state);
- fp->uf_cb_free = NULL;
+ if (fp->uf_flags & FC_LUAREF) {
+ api_free_luaref(fp->uf_luaref);
+ fp->uf_luaref = LUA_NOREF;
}
XFREE_CLEAR(fp->uf_tml_count);
@@ -828,7 +846,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
int fixvar_idx = 0; // index in fixvar[]
int ai;
bool islambda = false;
- char_u numbuf[NUMBUFLEN];
+ char numbuf[NUMBUFLEN];
char *name;
typval_T *tv_to_free[MAX_FUNC_ARGS];
int tv_to_free_len = 0;
@@ -852,7 +870,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
saveRedobuff(&save_redo);
did_save_redo = true;
}
- ++fp->uf_calls;
+ fp->uf_calls++;
// check for CTRL-C hit
line_breakcheck();
// prepare the funccall_T structure
@@ -863,14 +881,14 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
fc->rettv = rettv;
fc->level = ex_nesting_level;
// Check if this function has a breakpoint.
- fc->breakpoint = dbg_find_breakpoint(false, fp->uf_name, (linenr_T)0);
+ fc->breakpoint = dbg_find_breakpoint(false, (char *)fp->uf_name, (linenr_T)0);
fc->dbg_tick = debug_tick;
// Set up fields for closure.
ga_init(&fc->fc_funcs, sizeof(ufunc_T *), 1);
func_ptr_ref(fp);
- if (STRNCMP(fp->uf_name, "<lambda>", 8) == 0) {
+ if (strncmp(fp->uf_name, "<lambda>", 8) == 0) {
islambda = true;
}
@@ -889,11 +907,11 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
STRCPY(name, "self");
#endif
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
- tv_dict_add(&fc->l_vars, v);
+ hash_add(&fc->l_vars.dv_hashtab, (char *)v->di_key);
v->di_tv.v_type = VAR_DICT;
v->di_tv.v_lock = VAR_UNLOCKED;
v->di_tv.vval.v_dict = selfdict;
- ++selfdict->dv_refcount;
+ selfdict->dv_refcount++;
}
// Init a: variables, unless none found (in lambda).
@@ -915,7 +933,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
STRCPY(name, "000");
#endif
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
- tv_dict_add(&fc->l_avars, v);
+ hash_add(&fc->l_avars.dv_hashtab, (char *)v->di_key);
v->di_tv.v_type = VAR_LIST;
v->di_tv.v_lock = VAR_FIXED;
v->di_tv.vval.v_list = &fc->l_varlist;
@@ -967,14 +985,14 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
break;
}
// "..." argument a:1, a:2, etc.
- snprintf((char *)numbuf, sizeof(numbuf), "%d", ai + 1);
- name = (char *)numbuf;
+ snprintf(numbuf, sizeof(numbuf), "%d", ai + 1);
+ name = numbuf;
}
- if (fixvar_idx < FIXVAR_CNT && STRLEN(name) <= VAR_SHORT_LEN) {
+ if (fixvar_idx < FIXVAR_CNT && strlen(name) <= VAR_SHORT_LEN) {
v = (dictitem_T *)&fc->fixvar[fixvar_idx++];
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
} else {
- v = xmalloc(sizeof(dictitem_T) + STRLEN(name));
+ v = xmalloc(sizeof(dictitem_T) + strlen(name));
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC;
}
STRCPY(v->di_key, name);
@@ -993,16 +1011,16 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
// Named arguments can be accessed without the "a:" prefix in lambda
// expressions. Add to the l: dict.
tv_copy(&v->di_tv, &v->di_tv);
- tv_dict_add(&fc->l_vars, v);
+ hash_add(&fc->l_vars.dv_hashtab, (char *)v->di_key);
} else {
- tv_dict_add(&fc->l_avars, v);
+ hash_add(&fc->l_avars.dv_hashtab, (char *)v->di_key);
}
if (ai >= 0 && ai < MAX_FUNC_ARGS) {
listitem_T *li = &fc->l_listitems[ai];
*TV_LIST_ITEM_TV(li) = argvars[i];
- TV_LIST_ITEM_TV(li)->v_lock = VAR_FIXED;
+ TV_LIST_ITEM_TV(li)->v_lock = VAR_FIXED;
tv_list_append(&fc->l_varlist, li);
}
}
@@ -1058,7 +1076,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
bool func_not_yet_profiling_but_should =
do_profiling_yes
- && !fp->uf_profiling && has_profiling(false, fp->uf_name, NULL);
+ && !fp->uf_profiling && has_profiling(false, (char *)fp->uf_name, NULL);
if (func_not_yet_profiling_but_should) {
started_profiling = true;
@@ -1071,7 +1089,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
|| (fc->caller != NULL && fc->caller->func->uf_profiling));
if (func_or_func_caller_profiling) {
- ++fp->uf_tm_count;
+ fp->uf_tm_count++;
call_start = profile_start();
fp->uf_tm_children = profile_zero();
}
@@ -1083,7 +1101,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
const sctx_T save_current_sctx = current_sctx;
current_sctx = fp->uf_script_ctx;
save_did_emsg = did_emsg;
- did_emsg = FALSE;
+ did_emsg = false;
if (default_arg_err && (fp->uf_flags & FC_ABORT)) {
did_emsg = true;
@@ -1208,9 +1226,37 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
/// For the first we only count the name stored in func_hashtab as a reference,
/// using function() does not count as a reference, because the function is
/// looked up by name.
-static bool func_name_refcount(char_u *name)
+static bool func_name_refcount(const char *name)
+{
+ return isdigit((uint8_t)(*name)) || *name == '<';
+}
+
+/// Call a user function after checking the arguments.
+static int call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv,
+ funcexe_T *funcexe, dict_T *selfdict)
+ FUNC_ATTR_NONNULL_ARG(1, 3, 4, 5)
{
- return isdigit(*name) || *name == '<';
+ if (fp->uf_flags & FC_LUAREF) {
+ return typval_exec_lua_callable(fp->uf_luaref, argcount, argvars, rettv);
+ }
+
+ if ((fp->uf_flags & FC_RANGE) && funcexe->fe_doesrange != NULL) {
+ *funcexe->fe_doesrange = true;
+ }
+ int error;
+ if (argcount < fp->uf_args.ga_len - fp->uf_def_args.ga_len) {
+ error = FCERR_TOOFEW;
+ } else if (!fp->uf_varargs && argcount > fp->uf_args.ga_len) {
+ error = FCERR_TOOMANY;
+ } else if ((fp->uf_flags & FC_DICT) && selfdict == NULL) {
+ error = FCERR_DICT;
+ } else {
+ // Call the user function.
+ call_user_func(fp, argcount, argvars, rettv, funcexe->fe_firstline, funcexe->fe_lastline,
+ (fp->uf_flags & FC_DICT) ? selfdict : NULL);
+ error = FCERR_NONE;
+ }
+ return error;
}
static funccal_entry_T *funccal_stack = NULL;
@@ -1252,7 +1298,7 @@ void free_all_functions(void)
ufunc_T *fp;
uint64_t skipped = 0;
uint64_t todo = 1;
- uint64_t used;
+ int changed;
// Clean up the current_funccal chain and the funccal stack.
while (current_funccal != NULL) {
@@ -1276,9 +1322,9 @@ void free_all_functions(void)
if (func_name_refcount(fp->uf_name)) {
skipped++;
} else {
- used = func_hashtab.ht_used;
+ changed = func_hashtab.ht_changed;
func_clear(fp, true);
- if (used != func_hashtab.ht_used) {
+ if (changed != func_hashtab.ht_changed) {
skipped = 0;
break;
}
@@ -1322,10 +1368,10 @@ void free_all_functions(void)
/// @param[in] len length of "name", or -1 for NUL terminated.
///
/// @return true if "name" looks like a builtin function name: starts with a
-/// lower case letter and doesn't contain AUTOLOAD_CHAR.
+/// lower case letter and doesn't contain AUTOLOAD_CHAR or ':'.
static bool builtin_function(const char *name, int len)
{
- if (!ASCII_ISLOWER(name[0])) {
+ if (!ASCII_ISLOWER(name[0]) || name[1] == ':') {
return false;
}
@@ -1336,7 +1382,7 @@ static bool builtin_function(const char *name, int len)
return p == NULL;
}
-int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv)
+int func_call(char *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv)
{
typval_T argv[MAX_FUNC_ARGS + 1];
int argc = 0;
@@ -1353,12 +1399,12 @@ int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict
});
funcexe_T funcexe = FUNCEXE_INIT;
- funcexe.firstline = curwin->w_cursor.lnum;
- funcexe.lastline = curwin->w_cursor.lnum;
- funcexe.evaluate = true;
- funcexe.partial = partial;
- funcexe.selfdict = selfdict;
- r = call_func((char *)name, -1, rettv, argc, argv, &funcexe);
+ funcexe.fe_firstline = curwin->w_cursor.lnum;
+ funcexe.fe_lastline = curwin->w_cursor.lnum;
+ funcexe.fe_evaluate = true;
+ funcexe.fe_partial = partial;
+ funcexe.fe_selfdict = selfdict;
+ r = call_func(name, -1, rettv, argc, argv, &funcexe);
func_call_skip_call:
// Free the arguments.
@@ -1369,35 +1415,50 @@ func_call_skip_call:
return r;
}
+/// call the 'callback' function and return the result as a number.
+/// Returns -2 when calling the function fails. Uses argv[0] to argv[argc - 1]
+/// for the function arguments. argv[argc] should have type VAR_UNKNOWN.
+///
+/// @param argcount number of "argvars"
+/// @param argvars vars for arguments, must have "argcount" PLUS ONE elements!
+varnumber_T callback_call_retnr(Callback *callback, int argcount, typval_T *argvars)
+{
+ typval_T rettv;
+ if (!callback_call(callback, argcount, argvars, &rettv)) {
+ return -2;
+ }
+
+ varnumber_T retval = tv_get_number_chk(&rettv, NULL);
+ tv_clear(&rettv);
+ return retval;
+}
+
/// 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)
+static void user_func_error(int error, const char *name)
FUNC_ATTR_NONNULL_ALL
{
switch (error) {
- case ERROR_UNKNOWN:
+ case FCERR_UNKNOWN:
emsg_funcname(N_("E117: Unknown function: %s"), name);
break;
- case ERROR_NOTMETHOD:
+ case FCERR_NOTMETHOD:
emsg_funcname(N_("E276: Cannot use function as a method: %s"), name);
break;
- case ERROR_DELETED:
+ case FCERR_DELETED:
emsg_funcname(N_("E933: Function was deleted: %s"), name);
break;
- case ERROR_TOOMANY:
+ case FCERR_TOOMANY:
emsg_funcname(_(e_toomanyarg), name);
break;
- case ERROR_TOOFEW:
- emsg_funcname(N_("E119: Not enough arguments for function: %s"),
- name);
+ case FCERR_TOOFEW:
+ emsg_funcname(N_("E119: Not enough arguments for function: %s"), name);
break;
- case ERROR_SCRIPT:
- emsg_funcname(N_("E120: Using <SID> not in a script context: %s"),
- name);
+ case FCERR_SCRIPT:
+ emsg_funcname(N_("E120: Using <SID> not in a script context: %s"), name);
break;
- case ERROR_DICT:
- emsg_funcname(N_("E725: Calling dict function without Dictionary: %s"),
- name);
+ case FCERR_DICT:
+ emsg_funcname(N_("E725: Calling dict function without Dictionary: %s"), name);
break;
}
}
@@ -1435,27 +1496,27 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t
FUNC_ATTR_NONNULL_ARG(1, 3, 5, 6)
{
int ret = FAIL;
- int error = ERROR_NONE;
+ int error = FCERR_NONE;
ufunc_T *fp = NULL;
- char_u fname_buf[FLEN_FIXED + 1];
- char_u *tofree = NULL;
- char_u *fname = NULL;
- char_u *name = NULL;
+ char fname_buf[FLEN_FIXED + 1];
+ char *tofree = NULL;
+ char *fname = NULL;
+ char *name = NULL;
int argcount = argcount_in;
typval_T *argvars = argvars_in;
- dict_T *selfdict = funcexe->selfdict;
+ dict_T *selfdict = funcexe->fe_selfdict;
typval_T argv[MAX_FUNC_ARGS + 1]; // used when "partial" or
- // "funcexe->basetv" is not NULL
+ // "funcexe->fe_basetv" is not NULL
int argv_clear = 0;
int argv_base = 0;
- partial_T *partial = funcexe->partial;
+ partial_T *partial = funcexe->fe_partial;
// Initialize rettv so that it is safe for caller to invoke clear_tv(rettv)
// even when call_func() returns FAIL.
rettv->v_type = VAR_UNKNOWN;
if (len <= 0) {
- len = (int)STRLEN(funcname);
+ len = (int)strlen(funcname);
}
if (partial != NULL) {
fp = partial->pt_func;
@@ -1463,12 +1524,12 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t
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((char_u *)funcname, (size_t)len);
+ name = xstrnsave(funcname, (size_t)len);
fname = fname_trans_sid(name, fname_buf, &tofree, &error);
}
- if (funcexe->doesrange != NULL) {
- *funcexe->doesrange = false;
+ if (funcexe->fe_doesrange != NULL) {
+ *funcexe->fe_doesrange = false;
}
if (partial != NULL) {
@@ -1478,10 +1539,10 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t
if (partial->pt_dict != NULL && (selfdict == NULL || !partial->pt_auto)) {
selfdict = partial->pt_dict;
}
- if (error == ERROR_NONE && partial->pt_argc > 0) {
+ if (error == FCERR_NONE && partial->pt_argc > 0) {
for (argv_clear = 0; argv_clear < partial->pt_argc; argv_clear++) {
if (argv_clear + argcount_in >= MAX_FUNC_ARGS) {
- error = ERROR_TOOMANY;
+ error = FCERR_TOOMANY;
goto theend;
}
tv_copy(&partial->pt_argv[argv_clear], &argv[argv_clear]);
@@ -1494,8 +1555,8 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t
}
}
- if (error == ERROR_NONE && funcexe->evaluate) {
- char_u *rfname = fname;
+ if (error == FCERR_NONE && funcexe->fe_evaluate) {
+ char *rfname = fname;
// Ignore "g:" before a function name.
if (fp == NULL && fname[0] == 'g' && fname[1] == ':') {
@@ -1504,12 +1565,12 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t
rettv->v_type = VAR_NUMBER; // default rettv is number zero
rettv->vval.v_number = 0;
- error = ERROR_UNKNOWN;
+ error = FCERR_UNKNOWN;
if (is_luafunc(partial)) {
if (len > 0) {
- error = ERROR_NONE;
- argv_add_base(funcexe->basetv, &argvars, &argcount, argv, &argv_base);
+ error = FCERR_NONE;
+ argv_add_base(funcexe->fe_basetv, &argvars, &argcount, argv, &argv_base);
nlua_typval_call(funcname, (size_t)len, argvars, argcount, rettv);
} else {
// v:lua was called directly; show its name in the emsg
@@ -1524,71 +1585,50 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t
// Trigger FuncUndefined event, may load the function.
if (fp == NULL
- && apply_autocmds(EVENT_FUNCUNDEFINED, (char *)rfname, (char *)rfname, true, NULL)
+ && apply_autocmds(EVENT_FUNCUNDEFINED, rfname, rfname, true, NULL)
&& !aborting()) {
// executed an autocommand, search for the function again
fp = find_func(rfname);
}
// Try loading a package.
- if (fp == NULL && script_autoload((const char *)rfname, STRLEN(rfname),
+ if (fp == NULL && script_autoload((const char *)rfname, strlen(rfname),
true) && !aborting()) {
// Loaded a package, search for the function again.
fp = find_func(rfname);
}
if (fp != NULL && (fp->uf_flags & FC_DELETED)) {
- error = ERROR_DELETED;
- } else if (fp != NULL && (fp->uf_flags & FC_CFUNC)) {
- cfunc_T cb = fp->uf_cb;
- error = (*cb)(argcount, argvars, rettv, fp->uf_cb_state);
+ error = FCERR_DELETED;
} else if (fp != NULL) {
- if (funcexe->argv_func != NULL) {
+ if (funcexe->fe_argv_func != NULL) {
// postponed filling in the arguments, do it now
- argcount = funcexe->argv_func(argcount, argvars, argv_clear,
- fp->uf_args.ga_len);
+ argcount = funcexe->fe_argv_func(argcount, argvars, argv_clear, fp);
}
- argv_add_base(funcexe->basetv, &argvars, &argcount, argv, &argv_base);
+ argv_add_base(funcexe->fe_basetv, &argvars, &argcount, argv, &argv_base);
- if (fp->uf_flags & FC_RANGE && funcexe->doesrange != NULL) {
- *funcexe->doesrange = true;
- }
- if (argcount < fp->uf_args.ga_len - fp->uf_def_args.ga_len) {
- error = ERROR_TOOFEW;
- } else if (!fp->uf_varargs && argcount > fp->uf_args.ga_len) {
- error = ERROR_TOOMANY;
- } else if ((fp->uf_flags & FC_DICT) && selfdict == NULL) {
- error = ERROR_DICT;
- } else {
- // Call the user function.
- call_user_func(fp, argcount, argvars, rettv, funcexe->firstline,
- funcexe->lastline,
- (fp->uf_flags & FC_DICT) ? selfdict : NULL);
- error = ERROR_NONE;
- }
+ error = call_user_func_check(fp, argcount, argvars, rettv, funcexe, selfdict);
}
- } else if (funcexe->basetv != NULL) {
+ } else if (funcexe->fe_basetv != NULL) {
// expr->method(): Find the method name in the table, call its
// implementation with the base as one of the arguments.
error = call_internal_method(fname, argcount, argvars, rettv,
- funcexe->basetv);
+ funcexe->fe_basetv);
} else {
// Find the function name in the table, call its implementation.
error = call_internal_func(fname, argcount, argvars, rettv);
}
- /*
- * The function call (or "FuncUndefined" autocommand sequence) might
- * have been aborted by an error, an interrupt, or an explicitly thrown
- * exception that has not been caught so far. This situation can be
- * tested for by calling aborting(). For an error in an internal
- * function or for the "E132" error in call_user_func(), however, the
- * throw point at which the "force_abort" flag (temporarily reset by
- * emsg()) is normally updated has not been reached yet. We need to
- * update that flag first to make aborting() reliable.
- */
+ // The function call (or "FuncUndefined" autocommand sequence) might
+ // have been aborted by an error, an interrupt, or an explicitly thrown
+ // exception that has not been caught so far. This situation can be
+ // tested for by calling aborting(). For an error in an internal
+ // function or for the "E132" error in call_user_func(), however, the
+ // throw point at which the "force_abort" flag (temporarily reset by
+ // emsg()) is normally updated has not been reached yet. We need to
+ // update that flag first to make aborting() reliable.
update_force_abort();
}
- if (error == ERROR_NONE) {
+ if (error == FCERR_NONE) {
ret = OK;
}
@@ -1596,7 +1636,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 : (char_u *)funcname);
+ user_func_error(error, (name != NULL) ? name : funcname);
}
// clear the copies made from the partial
@@ -1610,6 +1650,11 @@ theend:
return ret;
}
+char *printable_func_name(ufunc_T *fp)
+{
+ return fp->uf_name_exp != NULL ? fp->uf_name_exp : fp->uf_name;
+}
+
/// List the head of the function: "name(arg1, arg2)".
///
/// @param[in] fp Function pointer.
@@ -1679,12 +1724,12 @@ static void list_func_head(ufunc_T *fp, int indent, bool force)
/// @param partial return: partial of a FuncRef
///
/// @return the function name in allocated memory, or NULL for failure.
-char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, partial_T **partial)
+char *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, partial_T **partial)
FUNC_ATTR_NONNULL_ARG(1)
{
- char_u *name = NULL;
- const char_u *start;
- const char_u *end;
+ char *name = NULL;
+ const char *start;
+ const char *end;
int lead;
int len;
lval_T lv;
@@ -1692,7 +1737,7 @@ char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, pa
if (fdp != NULL) {
CLEAR_POINTER(fdp);
}
- start = (char_u *)(*pp);
+ start = *pp;
// Check for hard coded <SNR>: already translated function ID (from a user
// command).
@@ -1700,19 +1745,19 @@ char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, pa
&& (*pp)[2] == KE_SNR) {
*pp += 3;
len = get_id_len((const char **)pp) + 3;
- return (char_u *)xmemdupz(start, (size_t)len);
+ return xmemdupz(start, (size_t)len);
}
// A name starting with "<SID>" or "<SNR>" is local to a script. But
// don't skip over "s:", get_lval() needs it for "s:dict.func".
- lead = eval_fname_script((const char *)start);
+ lead = eval_fname_script(start);
if (lead > 2) {
start += lead;
}
// Note that TFN_ flags use the same values as GLV_ flags.
- end = (char_u *)get_lval((char *)start, NULL, &lv, false, skip, flags | GLV_READ_ONLY,
- lead > 2 ? 0 : FNE_CHECK_START);
+ end = 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"));
@@ -1720,11 +1765,9 @@ char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, pa
goto theend;
}
if (end == NULL || (lv.ll_tv != NULL && (lead > 2 || lv.ll_range))) {
- /*
- * Report an invalid expression in braces, unless the expression
- * evaluation has been cancelled due to an aborting error, an
- * interrupt, or an exception.
- */
+ // Report an invalid expression in braces, unless the expression
+ // evaluation has been cancelled due to an aborting error, an
+ // interrupt, or an exception.
if (!aborting()) {
if (end != NULL) {
semsg(_(e_invarg2), start);
@@ -1738,17 +1781,17 @@ char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, pa
if (lv.ll_tv != NULL) {
if (fdp != NULL) {
fdp->fd_dict = lv.ll_dict;
- fdp->fd_newkey = (char_u *)lv.ll_newkey;
+ fdp->fd_newkey = 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((char_u *)lv.ll_tv->vval.v_string);
+ name = xstrdup(lv.ll_tv->vval.v_string);
*pp = (char *)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(end + 1, true);
if (len == 0) {
semsg(e_invexpr2, "v:lua");
goto theend;
@@ -1757,7 +1800,7 @@ char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, pa
memcpy(name, end + 1, (size_t)len);
*pp = (char *)end + 1 + len;
} else {
- name = vim_strsave((char_u *)partial_name(lv.ll_tv->vval.v_partial));
+ name = xstrdup(partial_name(lv.ll_tv->vval.v_partial));
*pp = (char *)end;
}
if (partial != NULL) {
@@ -1791,22 +1834,21 @@ char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, pa
name = NULL;
}
} else if (!(flags & TFN_NO_DEREF)) {
- len = (int)(end - (char_u *)(*pp));
- name = deref_func_name((const char *)(*pp), &len, partial,
- flags & TFN_NO_AUTOLOAD);
- if (name == (char_u *)(*pp)) {
+ len = (int)(end - *pp);
+ name = deref_func_name(*pp, &len, partial, flags & TFN_NO_AUTOLOAD);
+ if (name == *pp) {
name = NULL;
}
}
if (name != NULL) {
- name = vim_strsave(name);
+ name = xstrdup(name);
*pp = (char *)end;
- if (STRNCMP(name, "<SNR>", 5) == 0) {
+ if (strncmp(name, "<SNR>", 5) == 0) {
// Change "<SNR>" to the byte sequence.
- name[0] = K_SPECIAL;
- name[1] = KS_EXTRA;
+ name[0] = (char)K_SPECIAL;
+ name[1] = (char)KS_EXTRA;
name[2] = KE_SNR;
- memmove(name + 3, name + 5, strlen((char *)name + 5) + 1);
+ memmove(name + 3, name + 5, strlen(name + 5) + 1);
}
goto theend;
}
@@ -1828,7 +1870,7 @@ char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, pa
lv.ll_name += 2;
lv.ll_name_len -= 2;
}
- len = (int)((const char *)end - lv.ll_name);
+ len = (int)(end - lv.ll_name);
}
size_t sid_buf_len = 0;
@@ -1859,7 +1901,7 @@ char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, pa
}
if (!skip && !(flags & TFN_QUIET) && !(flags & TFN_NO_DEREF)) {
- char_u *cp = xmemrchr(lv.ll_name, ':', lv.ll_name_len);
+ char *cp = xmemrchr(lv.ll_name, ':', lv.ll_name_len);
if (cp != NULL && cp < end) {
semsg(_("E884: Function name cannot contain a colon: %s"), start);
@@ -1869,8 +1911,8 @@ char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, pa
name = xmalloc((size_t)len + (size_t)lead + 1);
if (!skip && lead > 0) {
- name[0] = K_SPECIAL;
- name[1] = KS_EXTRA;
+ name[0] = (char)K_SPECIAL;
+ name[1] = (char)KS_EXTRA;
name[2] = KE_SNR;
if (sid_buf_len > 0) { // If it's "<SID>"
memcpy(name + 3, sid_buf, sid_buf_len);
@@ -1885,18 +1927,105 @@ theend:
return name;
}
+/// If the "funcname" starts with "s:" or "<SID>", then expands it to the
+/// current script ID and returns the expanded function name. The caller should
+/// free the returned name. If not called from a script context or the function
+/// name doesn't start with these prefixes, then returns NULL.
+/// This doesn't check whether the script-local function exists or not.
+char *get_scriptlocal_funcname(char *funcname)
+{
+ if (funcname == NULL) {
+ return NULL;
+ }
+
+ if (strncmp(funcname, "s:", 2) != 0
+ && strncmp(funcname, "<SID>", 5) != 0) {
+ // The function name is not a script-local function name
+ return NULL;
+ }
+
+ if (!SCRIPT_ID_VALID(current_sctx.sc_sid)) {
+ emsg(_(e_usingsid));
+ return NULL;
+ }
+
+ char sid_buf[25];
+ // Expand s: and <SID> prefix into <SNR>nr_<name>
+ snprintf(sid_buf, sizeof(sid_buf), "<SNR>%" PRId64 "_",
+ (int64_t)current_sctx.sc_sid);
+ const int off = *funcname == 's' ? 2 : 5;
+ char *newname = xmalloc(strlen(sid_buf) + strlen(funcname + off) + 1);
+ STRCPY(newname, sid_buf);
+ STRCAT(newname, funcname + off);
+
+ return newname;
+}
+
+/// Call trans_function_name(), except that a lambda is returned as-is.
+/// Returns the name in allocated memory.
+char *save_function_name(char **name, bool skip, int flags, funcdict_T *fudi)
+{
+ char *p = *name;
+ char *saved;
+
+ if (strncmp(p, "<lambda>", 8) == 0) {
+ p += 8;
+ (void)getdigits(&p, false, 0);
+ saved = xstrndup(*name, (size_t)(p - *name));
+ if (fudi != NULL) {
+ CLEAR_POINTER(fudi);
+ }
+ } else {
+ saved = trans_function_name(&p, skip, flags, fudi, NULL);
+ }
+ *name = p;
+ return saved;
+}
+
+#define MAX_FUNC_NESTING 50
+
+/// List functions.
+///
+/// @param regmatch When NULL, all of them.
+/// Otherwise functions matching "regmatch".
+static void list_functions(regmatch_T *regmatch)
+{
+ const int changed = func_hashtab.ht_changed;
+ size_t todo = func_hashtab.ht_used;
+ const hashitem_T *const ht_array = func_hashtab.ht_array;
+
+ for (const hashitem_T *hi = ht_array; todo > 0 && !got_int; hi++) {
+ if (!HASHITEM_EMPTY(hi)) {
+ ufunc_T *fp = HI2UF(hi);
+ todo--;
+ if ((fp->uf_flags & FC_DEAD) == 0
+ && (regmatch == NULL
+ ? (!message_filtered((char *)fp->uf_name)
+ && !func_name_refcount(fp->uf_name))
+ : (!isdigit((uint8_t)(*fp->uf_name))
+ && vim_regexec(regmatch, (char *)fp->uf_name, 0)))) {
+ list_func_head(fp, false, false);
+ if (changed != func_hashtab.ht_changed) {
+ emsg(_("E454: function list was modified"));
+ return;
+ }
+ }
+ }
+ }
+}
+
/// ":function"
void ex_function(exarg_T *eap)
{
- char_u *theline;
- char_u *line_to_free = NULL;
- char_u c;
+ char *theline;
+ char *line_to_free = NULL;
+ char c;
int saved_did_emsg;
bool saved_wait_return = need_wait_return;
- char_u *name = NULL;
- char_u *p;
- char_u *arg;
- char_u *line_arg = NULL;
+ char *name = NULL;
+ char *p;
+ char *arg;
+ char *line_arg = NULL;
garray_T newargs;
garray_T default_args;
garray_T newlines;
@@ -1911,44 +2040,27 @@ void ex_function(exarg_T *eap)
static int func_nr = 0; // number for nameless function
int paren;
hashtab_T *ht;
- int todo;
hashitem_T *hi;
linenr_T sourcing_lnum_off;
linenr_T sourcing_lnum_top;
bool is_heredoc = false;
- char_u *skip_until = NULL;
- char_u *heredoc_trimmed = NULL;
+ char *skip_until = NULL;
+ char *heredoc_trimmed = NULL;
bool show_block = false;
bool do_concat = true;
- /*
- * ":function" without argument: list functions.
- */
+ // ":function" without argument: list functions.
if (ends_excmd(*eap->arg)) {
if (!eap->skip) {
- todo = (int)func_hashtab.ht_used;
- for (hi = func_hashtab.ht_array; todo > 0 && !got_int; ++hi) {
- if (!HASHITEM_EMPTY(hi)) {
- todo--;
- fp = HI2UF(hi);
- if (message_filtered(fp->uf_name)) {
- continue;
- }
- if (!func_name_refcount(fp->uf_name)) {
- list_func_head(fp, false, false);
- }
- }
- }
+ list_functions(NULL);
}
- eap->nextcmd = (char *)check_nextcmd((char_u *)eap->arg);
+ eap->nextcmd = check_nextcmd(eap->arg);
return;
}
- /*
- * ":function /pat": list functions matching pattern.
- */
+ // ":function /pat": list functions matching pattern.
if (*eap->arg == '/') {
- p = skip_regexp((char_u *)eap->arg + 1, '/', true, NULL);
+ p = skip_regexp(eap->arg + 1, '/', true);
if (!eap->skip) {
regmatch_T regmatch;
@@ -1958,25 +2070,14 @@ void ex_function(exarg_T *eap)
*p = c;
if (regmatch.regprog != NULL) {
regmatch.rm_ic = p_ic;
-
- todo = (int)func_hashtab.ht_used;
- for (hi = func_hashtab.ht_array; todo > 0 && !got_int; ++hi) {
- if (!HASHITEM_EMPTY(hi)) {
- todo--;
- fp = HI2UF(hi);
- if (!isdigit(*fp->uf_name)
- && vim_regexec(&regmatch, (char *)fp->uf_name, 0)) {
- list_func_head(fp, false, false);
- }
- }
- }
+ list_functions(&regmatch);
vim_regfree(regmatch.regprog);
}
}
if (*p == '/') {
p++;
}
- eap->nextcmd = (char *)check_nextcmd(p);
+ eap->nextcmd = check_nextcmd(p);
return;
}
@@ -1994,30 +2095,27 @@ 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 = (char_u *)eap->arg;
- name = trans_function_name((char **)&p, eap->skip, TFN_NO_AUTOLOAD, &fudi, NULL);
- paren = (vim_strchr((char *)p, '(') != NULL);
+ p = eap->arg;
+ name = save_function_name(&p, eap->skip, TFN_NO_AUTOLOAD, &fudi);
+ paren = (vim_strchr(p, '(') != NULL);
if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip) {
- /*
- * Return on an invalid expression in braces, unless the expression
- * evaluation has been cancelled due to an aborting error, an
- * interrupt, or an exception.
- */
+ // Return on an invalid expression in braces, unless the expression
+ // evaluation has been cancelled due to an aborting error, an
+ // interrupt, or an exception.
if (!aborting()) {
if (fudi.fd_newkey != NULL) {
semsg(_(e_dictkey), fudi.fd_newkey);
}
xfree(fudi.fd_newkey);
return;
- } else {
- eap->skip = TRUE;
}
+ eap->skip = true;
}
// An error in a function call during evaluation of an expression in magic
// braces should not cause the function not to be defined.
saved_did_emsg = did_emsg;
- did_emsg = FALSE;
+ did_emsg = false;
//
// ":function func" with only function name: list function.
@@ -2026,11 +2124,11 @@ void ex_function(exarg_T *eap)
// - exclude line numbers from function body
//
if (!paren) {
- if (!ends_excmd(*skipwhite((char *)p))) {
+ if (!ends_excmd(*skipwhite(p))) {
semsg(_(e_trailing_arg), p);
goto ret_free;
}
- eap->nextcmd = (char *)check_nextcmd(p);
+ eap->nextcmd = check_nextcmd(p);
if (eap->nextcmd != NULL) {
*p = NUL;
}
@@ -2052,7 +2150,7 @@ void ex_function(exarg_T *eap)
msg_putchar(' ');
}
}
- msg_prt_line((char_u *)FUNCLINE(fp, j), false);
+ msg_prt_line(FUNCLINE(fp, j), false);
ui_flush(); // show a line at a time
os_breakcheck();
}
@@ -2067,24 +2165,22 @@ void ex_function(exarg_T *eap)
goto ret_free;
}
- /*
- * ":function name(arg1, arg2)" Define function.
- */
- p = (char_u *)skipwhite((char *)p);
+ // ":function name(arg1, arg2)" Define function.
+ p = skipwhite(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((char *)p, '(') != NULL) {
- p = (char_u *)vim_strchr((char *)p, '(');
+ if (vim_strchr(p, '(') != NULL) {
+ p = vim_strchr(p, '(');
}
}
- p = (char_u *)skipwhite((char *)p + 1);
+ p = skipwhite(p + 1);
- ga_init(&newargs, (int)sizeof(char_u *), 3);
- ga_init(&newlines, (int)sizeof(char_u *), 3);
+ ga_init(&newargs, (int)sizeof(char *), 3);
+ ga_init(&newlines, (int)sizeof(char *), 3);
if (!eap->skip) {
// Check the name of the function. Unless it's a dictionary function
@@ -2095,7 +2191,7 @@ void ex_function(exarg_T *eap)
arg = fudi.fd_newkey;
}
if (arg != NULL && (fudi.fd_di == NULL || !tv_is_func(fudi.fd_di->di_tv))) {
- int j = (*arg == K_SPECIAL) ? 3 : 0;
+ int j = ((uint8_t)(*arg) == K_SPECIAL) ? 3 : 0;
while (arg[j] != NUL && (j == 0 ? eval_isnamec1(arg[j]) : eval_isnamec(arg[j]))) {
j++;
}
@@ -2109,7 +2205,7 @@ void ex_function(exarg_T *eap)
}
}
- if (get_function_args((char **)&p, ')', &newargs, &varargs,
+ if (get_function_args(&p, ')', &newargs, &varargs,
&default_args, eap->skip) == FAIL) {
goto errret_2;
}
@@ -2121,23 +2217,22 @@ void ex_function(exarg_T *eap)
// find extra arguments "range", "dict", "abort" and "closure"
for (;;) {
- p = (char_u *)skipwhite((char *)p);
- if (STRNCMP(p, "range", 5) == 0) {
+ p = skipwhite(p);
+ if (strncmp(p, "range", 5) == 0) {
flags |= FC_RANGE;
p += 5;
- } else if (STRNCMP(p, "dict", 4) == 0) {
+ } else if (strncmp(p, "dict", 4) == 0) {
flags |= FC_DICT;
p += 4;
- } else if (STRNCMP(p, "abort", 5) == 0) {
+ } else if (strncmp(p, "abort", 5) == 0) {
flags |= FC_ABORT;
p += 5;
- } else if (STRNCMP(p, "closure", 7) == 0) {
+ } else if (strncmp(p, "closure", 7) == 0) {
flags |= FC_CLOSURE;
p += 7;
if (current_funccal == NULL) {
- emsg_funcname(N_
- ("E932: Closure function should not be at top level: %s"),
- name == NULL ? (char_u *)"" : name);
+ emsg_funcname(N_("E932: Closure function should not be at top level: %s"),
+ name == NULL ? "" : name);
goto erret;
}
} else {
@@ -2153,9 +2248,7 @@ void ex_function(exarg_T *eap)
semsg(_(e_trailing_arg), p);
}
- /*
- * Read the body of the function, until ":endfunction" is found.
- */
+ // Read the body of the function, until ":endfunction" is found.
if (KeyTyped) {
// Check if the function already exists, don't let the user type the
// whole function before telling him it doesn't work! For a script we
@@ -2193,9 +2286,9 @@ void ex_function(exarg_T *eap)
if (line_arg != NULL) {
// Use eap->arg, split up in parts by line breaks.
theline = line_arg;
- p = (char_u *)vim_strchr((char *)theline, '\n');
+ p = vim_strchr(theline, '\n');
if (p == NULL) {
- line_arg += STRLEN(line_arg);
+ line_arg += strlen(line_arg);
} else {
*p = NUL;
line_arg = p + 1;
@@ -2205,7 +2298,7 @@ void ex_function(exarg_T *eap)
if (eap->getline == NULL) {
theline = getcmdline(':', 0L, indent, do_concat);
} else {
- theline = (char_u *)eap->getline(':', eap->cookie, indent, do_concat);
+ theline = eap->getline(':', eap->cookie, indent, do_concat);
}
line_to_free = theline;
}
@@ -2235,18 +2328,18 @@ void ex_function(exarg_T *eap)
// * ":python <<EOF" and "EOF"
// * ":let {var-name} =<< [trim] {marker}" and "{marker}"
if (heredoc_trimmed == NULL
- || (is_heredoc && (char_u *)skipwhite((char *)theline) == theline)
- || STRNCMP(theline, heredoc_trimmed,
- STRLEN(heredoc_trimmed)) == 0) {
+ || (is_heredoc && skipwhite(theline) == theline)
+ || strncmp(theline, heredoc_trimmed,
+ strlen(heredoc_trimmed)) == 0) {
if (heredoc_trimmed == NULL) {
p = theline;
} else if (is_heredoc) {
- p = (char_u *)skipwhite((char *)theline) == theline
- ? theline : theline + STRLEN(heredoc_trimmed);
+ p = skipwhite(theline) == theline
+ ? theline : theline + strlen(heredoc_trimmed);
} else {
- p = theline + STRLEN(heredoc_trimmed);
+ p = theline + strlen(heredoc_trimmed);
}
- if (STRCMP(p, skip_until) == 0) {
+ if (strcmp(p, skip_until) == 0) {
XFREE_CLEAR(skip_until);
XFREE_CLEAR(heredoc_trimmed);
do_concat = true;
@@ -2258,27 +2351,26 @@ void ex_function(exarg_T *eap)
for (p = theline; ascii_iswhite(*p) || *p == ':'; p++) {}
// Check for "endfunction".
- if (checkforcmd((char **)&p, "endfunction", 4) && nesting-- == 0) {
+ if (checkforcmd(&p, "endfunction", 4) && nesting-- == 0) {
if (*p == '!') {
p++;
}
- char_u *nextcmd = NULL;
+ char *nextcmd = NULL;
if (*p == '|') {
nextcmd = p + 1;
- } else if (line_arg != NULL && *skipwhite((char *)line_arg) != NUL) {
+ } else if (line_arg != NULL && *skipwhite(line_arg) != NUL) {
nextcmd = line_arg;
} else if (*p != NUL && *p != '"' && p_verbose > 0) {
- give_warning2((char_u *)_("W22: Text found after :endfunction: %s"),
- p, true);
+ give_warning2(_("W22: Text found after :endfunction: %s"), p, true);
}
if (nextcmd != NULL) {
// Another command follows. If the line came from "eap" we
// can simply point into it, otherwise we need to change
// "eap->cmdlinep".
- eap->nextcmd = (char *)nextcmd;
+ eap->nextcmd = nextcmd;
if (line_to_free != NULL) {
xfree(*eap->cmdlinep);
- *eap->cmdlinep = (char *)line_to_free;
+ *eap->cmdlinep = line_to_free;
line_to_free = NULL;
}
}
@@ -2287,46 +2379,50 @@ void ex_function(exarg_T *eap)
// Increase indent inside "if", "while", "for" and "try", decrease
// at "end".
- if (indent > 2 && STRNCMP(p, "end", 3) == 0) {
+ if (indent > 2 && strncmp(p, "end", 3) == 0) {
indent -= 2;
- } else if (STRNCMP(p, "if", 2) == 0
- || STRNCMP(p, "wh", 2) == 0
- || STRNCMP(p, "for", 3) == 0
- || STRNCMP(p, "try", 3) == 0) {
+ } else if (strncmp(p, "if", 2) == 0
+ || strncmp(p, "wh", 2) == 0
+ || strncmp(p, "for", 3) == 0
+ || strncmp(p, "try", 3) == 0) {
indent += 2;
}
// Check for defining a function inside this function.
- if (checkforcmd((char **)&p, "function", 2)) {
+ if (checkforcmd(&p, "function", 2)) {
if (*p == '!') {
- p = (char_u *)skipwhite((char *)p + 1);
+ p = skipwhite(p + 1);
}
p += eval_fname_script((const char *)p);
- xfree(trans_function_name((char **)&p, true, 0, NULL, NULL));
- if (*skipwhite((char *)p) == '(') {
- nesting++;
- indent += 2;
+ xfree(trans_function_name(&p, true, 0, NULL, NULL));
+ if (*skipwhite(p) == '(') {
+ if (nesting == MAX_FUNC_NESTING - 1) {
+ emsg(_("E1058: function nesting too deep"));
+ } else {
+ nesting++;
+ indent += 2;
+ }
}
}
// Check for ":append", ":change", ":insert".
- p = (char_u *)skip_range((char *)p, NULL);
+ p = skip_range(p, NULL);
if ((p[0] == 'a' && (!ASCII_ISALPHA(p[1]) || p[1] == 'p'))
|| (p[0] == 'c'
&& (!ASCII_ISALPHA(p[1])
|| (p[1] == 'h' && (!ASCII_ISALPHA(p[2])
|| (p[2] == 'a'
- && (STRNCMP(&p[3], "nge", 3) != 0
+ && (strncmp(&p[3], "nge", 3) != 0
|| !ASCII_ISALPHA(p[6])))))))
|| (p[0] == 'i'
&& (!ASCII_ISALPHA(p[1]) || (p[1] == 'n'
&& (!ASCII_ISALPHA(p[2])
|| (p[2] == 's')))))) {
- skip_until = vim_strsave((char_u *)".");
+ skip_until = xstrdup(".");
}
// heredoc: Check for ":python <<EOF", ":lua <<EOF", etc.
- arg = (char_u *)skipwhite((char *)skiptowhite(p));
+ arg = skipwhite(skiptowhite(p));
if (arg[0] == '<' && arg[1] == '<'
&& ((p[0] == 'p' && p[1] == 'y'
&& (!ASCII_ISALNUM(p[2]) || p[2] == 't'
@@ -2343,22 +2439,22 @@ 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 = (char_u *)skipwhite((char *)arg + 2);
+ p = skipwhite(arg + 2);
if (*p == NUL) {
- skip_until = vim_strsave((char_u *)".");
+ skip_until = xstrdup(".");
} else {
- skip_until = vim_strsave(p);
+ skip_until = xstrdup(p);
}
}
// Check for ":let v =<< [trim] EOF"
// and ":let [a, b] =<< [trim] EOF"
- arg = (char_u *)skipwhite((char *)skiptowhite(p));
+ arg = skipwhite(skiptowhite(p));
if (*arg == '[') {
- arg = (char_u *)vim_strchr((char *)arg, ']');
+ arg = vim_strchr(arg, ']');
}
if (arg != NULL) {
- arg = (char_u *)skipwhite((char *)skiptowhite(arg));
+ arg = skipwhite(skiptowhite(arg));
if (arg[0] == '='
&& arg[1] == '<'
&& arg[2] == '<'
@@ -2366,14 +2462,13 @@ void ex_function(exarg_T *eap)
&& p[1] == 'e'
&& (!ASCII_ISALNUM(p[2])
|| (p[2] == 't' && !ASCII_ISALNUM(p[3]))))) {
- p = (char_u *)skipwhite((char *)arg + 3);
- if (STRNCMP(p, "trim", 4) == 0) {
+ p = skipwhite(arg + 3);
+ if (strncmp(p, "trim", 4) == 0) {
// Ignore leading white space.
- p = (char_u *)skipwhite((char *)p + 4);
- heredoc_trimmed =
- vim_strnsave(theline, (size_t)((char_u *)skipwhite((char *)theline) - theline));
+ p = skipwhite(p + 4);
+ heredoc_trimmed = xstrnsave(theline, (size_t)(skipwhite(theline) - theline));
}
- skip_until = vim_strnsave(p, (size_t)(skiptowhite(p) - p));
+ skip_until = xstrnsave(p, (size_t)(skiptowhite(p) - p));
do_concat = false;
is_heredoc = true;
}
@@ -2386,8 +2481,8 @@ void ex_function(exarg_T *eap)
// Copy the line to newly allocated memory. get_one_sourceline()
// allocates 250 bytes per line, this saves 80% on average. The cost
// is an extra alloc/free.
- p = vim_strsave(theline);
- ((char **)(newlines.ga_data))[newlines.ga_len++] = (char *)p;
+ p = xstrdup(theline);
+ ((char **)(newlines.ga_data))[newlines.ga_len++] = p;
// Add NULL lines for continuation lines, so that the line count is
// equal to the index in the growarray.
@@ -2407,14 +2502,11 @@ void ex_function(exarg_T *eap)
goto erret;
}
- /*
- * If there are no errors, add the function
- */
+ // If there are no errors, add the function
if (fudi.fd_dict == NULL) {
- v = find_var((const char *)name, STRLEN(name), &ht, false);
+ v = find_var((const char *)name, strlen(name), &ht, false);
if (v != NULL && v->di_tv.v_type == VAR_FUNC) {
- emsg_funcname(N_("E707: Function name conflicts with variable: %s"),
- name);
+ emsg_funcname(N_("E707: Function name conflicts with variable: %s"), name);
goto erret;
}
@@ -2429,8 +2521,7 @@ void ex_function(exarg_T *eap)
goto erret;
}
if (fp->uf_calls > 0) {
- emsg_funcname(N_("E127: Cannot redefine function %s: It is in use"),
- name);
+ emsg_funcname(N_("E127: Cannot redefine function %s: It is in use"), name);
goto erret;
}
if (fp->uf_refcount > 1) {
@@ -2441,7 +2532,7 @@ void ex_function(exarg_T *eap)
fp = NULL;
overwrite = true;
} else {
- char_u *exp_name = fp->uf_name_exp;
+ char *exp_name = fp->uf_name_exp;
// redefine existing function, keep the expanded name
XFREE_CLEAR(name);
fp->uf_name_exp = NULL;
@@ -2460,13 +2551,13 @@ void ex_function(exarg_T *eap)
goto erret;
}
if (fudi.fd_di == NULL) {
- if (var_check_lock(fudi.fd_dict->dv_lock, (const char *)eap->arg,
- TV_CSTRING)) {
+ if (value_check_lock(fudi.fd_dict->dv_lock, (const char *)eap->arg,
+ TV_CSTRING)) {
// Can't add a function to a locked dictionary
goto erret;
}
- } else if (var_check_lock(fudi.fd_di->di_tv.v_lock, (const char *)eap->arg,
- TV_CSTRING)) {
+ } else if (value_check_lock(fudi.fd_di->di_tv.v_lock, (const char *)eap->arg,
+ TV_CSTRING)) {
// Can't change an existing function if it is locked
goto erret;
}
@@ -2474,23 +2565,23 @@ void ex_function(exarg_T *eap)
// Give the function a sequential number. Can only be used with a
// Funcref!
xfree(name);
- sprintf(numbuf, "%d", ++func_nr);
- name = vim_strsave((char_u *)numbuf);
+ sprintf(numbuf, "%d", ++func_nr); // NOLINT(runtime/printf)
+ name = xstrdup(numbuf);
}
if (fp == NULL) {
- if (fudi.fd_dict == NULL && vim_strchr((char *)name, AUTOLOAD_CHAR) != NULL) {
+ if (fudi.fd_dict == NULL && vim_strchr(name, AUTOLOAD_CHAR) != NULL) {
int slen, plen;
- char_u *scriptname;
+ char *scriptname;
// Check that the autoload name matches the script name.
int j = FAIL;
if (SOURCING_NAME != NULL) {
- scriptname = (char_u *)autoload_name((const char *)name, STRLEN(name));
- p = (char_u *)vim_strchr((char *)scriptname, '/');
- plen = (int)STRLEN(p);
- slen = (int)STRLEN(SOURCING_NAME);
- if (slen > plen && FNAMECMP(p, SOURCING_NAME + slen - plen) == 0) {
+ scriptname = autoload_name(name, strlen(name));
+ p = vim_strchr(scriptname, '/');
+ plen = (int)strlen(p);
+ slen = (int)strlen(SOURCING_NAME);
+ if (slen > plen && path_fnamecmp(p, SOURCING_NAME + slen - plen) == 0) {
j = OK;
}
xfree(scriptname);
@@ -2502,7 +2593,7 @@ void ex_function(exarg_T *eap)
}
}
- fp = xcalloc(1, offsetof(ufunc_T, uf_name) + STRLEN(name) + 1);
+ fp = xcalloc(1, offsetof(ufunc_T, uf_name) + strlen(name) + 1);
if (fudi.fd_dict != NULL) {
if (fudi.fd_di == NULL) {
@@ -2518,7 +2609,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 = (char *)vim_strsave(name);
+ fudi.fd_di->di_tv.vval.v_string = xstrdup(name);
// behave like "dict" was used
flags |= FC_DICT;
@@ -2527,8 +2618,8 @@ void ex_function(exarg_T *eap)
// insert the new function in the function list
set_ufunc_name(fp, name);
if (overwrite) {
- hi = hash_find(&func_hashtab, (char *)name);
- hi->hi_key = UF2HIKEY(fp);
+ hi = hash_find(&func_hashtab, name);
+ hi->hi_key = (char *)UF2HIKEY(fp);
} else if (hash_add(&func_hashtab, UF2HIKEY(fp)) == FAIL) {
xfree(fp);
goto erret;
@@ -2584,8 +2675,8 @@ int eval_fname_script(const char *const p)
// Use mb_strnicmp() because in Turkish comparing the "I" may not work with
// the standard library function.
if (p[0] == '<'
- && (mb_strnicmp((char_u *)p + 1, (char_u *)"SID>", 4) == 0
- || mb_strnicmp((char_u *)p + 1, (char_u *)"SNR>", 4) == 0)) {
+ && (mb_strnicmp(p + 1, "SID>", 4) == 0
+ || mb_strnicmp(p + 1, "SNR>", 4) == 0)) {
return 5;
}
if (p[0] == 's' && p[1] == ':') {
@@ -2599,7 +2690,7 @@ bool translated_function_exists(const char *name)
if (builtin_function(name, -1)) {
return find_internal_func((char *)name) != NULL;
}
- return find_func((const char_u *)name) != NULL;
+ return find_func(name) != NULL;
}
/// Check whether function with the given name exists
@@ -2610,15 +2701,15 @@ bool translated_function_exists(const char *name)
/// @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;
+ const char *nm = name;
bool n = false;
int flag = TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD;
if (no_deref) {
flag |= TFN_NO_DEREF;
}
- char *const p = (char *)trans_function_name((char **)&nm, false, flag, NULL, NULL);
- nm = (char_u *)skipwhite((char *)nm);
+ char *const p = trans_function_name((char **)&nm, false, flag, NULL, NULL);
+ nm = skipwhite(nm);
// Only accept "funcname", "funcname ", "funcname (..." and
// "funcname(...", not "funcname!...".
@@ -2634,15 +2725,17 @@ bool function_exists(const char *const name, bool no_deref)
char *get_user_func_name(expand_T *xp, int idx)
{
static size_t done;
+ static int changed;
static hashitem_T *hi;
ufunc_T *fp;
if (idx == 0) {
done = 0;
hi = func_hashtab.ht_array;
+ changed = func_hashtab.ht_changed;
}
assert(hi);
- if (done < func_hashtab.ht_used) {
+ if (changed == func_hashtab.ht_changed && done < func_hashtab.ht_used) {
if (done++ > 0) {
hi++;
}
@@ -2652,11 +2745,11 @@ char *get_user_func_name(expand_T *xp, int idx)
fp = HI2UF(hi);
if ((fp->uf_flags & FC_DICT)
- || STRNCMP(fp->uf_name, "<lambda>", 8) == 0) {
+ || strncmp(fp->uf_name, "<lambda>", 8) == 0) {
return ""; // don't show dict and lambda functions
}
- if (STRLEN(fp->uf_name) + 4 >= IOSIZE) {
+ if (strlen(fp->uf_name) + 4 >= IOSIZE) {
return (char *)fp->uf_name; // Prevent overflow.
}
@@ -2667,7 +2760,7 @@ char *get_user_func_name(expand_T *xp, int idx)
STRCAT(IObuff, ")");
}
}
- return (char *)IObuff;
+ return IObuff;
}
return NULL;
}
@@ -2676,12 +2769,12 @@ char *get_user_func_name(expand_T *xp, int idx)
void ex_delfunction(exarg_T *eap)
{
ufunc_T *fp = NULL;
- char_u *p;
- char_u *name;
+ char *p;
+ char *name;
funcdict_T fudi;
- p = (char_u *)eap->arg;
- name = trans_function_name((char **)&p, eap->skip, 0, &fudi, NULL);
+ p = eap->arg;
+ name = trans_function_name(&p, eap->skip, 0, &fudi, NULL);
xfree(fudi.fd_newkey);
if (name == NULL) {
if (fudi.fd_dict != NULL && !eap->skip) {
@@ -2689,16 +2782,23 @@ void ex_delfunction(exarg_T *eap)
}
return;
}
- if (!ends_excmd(*skipwhite((char *)p))) {
+ if (!ends_excmd(*skipwhite(p))) {
xfree(name);
semsg(_(e_trailing_arg), p);
return;
}
- eap->nextcmd = (char *)check_nextcmd(p);
+ eap->nextcmd = check_nextcmd(p);
if (eap->nextcmd != NULL) {
*p = NUL;
}
+ if (isdigit((uint8_t)(*name)) && fudi.fd_dict == NULL) {
+ if (!eap->skip) {
+ semsg(_(e_invarg2), eap->arg);
+ }
+ xfree(name);
+ return;
+ }
if (!eap->skip) {
fp = find_func(name);
}
@@ -2749,7 +2849,7 @@ void ex_delfunction(exarg_T *eap)
/// Unreference a Function: decrement the reference count and free it when it
/// becomes zero.
-void func_unref(char_u *name)
+void func_unref(char *name)
{
ufunc_T *fp = NULL;
@@ -2758,7 +2858,7 @@ void func_unref(char_u *name)
}
fp = find_func(name);
- if (fp == NULL && isdigit(*name)) {
+ if (fp == NULL && isdigit((uint8_t)(*name))) {
#ifdef EXITFREE
if (!entered_free_all_mem) {
internal_error("func_unref()");
@@ -2791,7 +2891,7 @@ void func_ptr_unref(ufunc_T *fp)
}
/// Count a reference to a Function.
-void func_ref(char_u *name)
+void func_ref(char *name)
{
ufunc_T *fp;
@@ -2801,7 +2901,7 @@ void func_ref(char_u *name)
fp = find_func(name);
if (fp != NULL) {
(fp->uf_refcount)++;
- } else if (isdigit(*name)) {
+ } else if (isdigit((uint8_t)(*name))) {
// Only give an error for a numbered function.
// Fail silently, when named or lambda function isn't found.
internal_error("func_ref()");
@@ -2845,9 +2945,9 @@ static int can_free_funccal(funccall_T *fc, int copyID)
/// ":return [expr]"
void ex_return(exarg_T *eap)
{
- char_u *arg = (char_u *)eap->arg;
+ char *arg = eap->arg;
typval_T rettv;
- int returning = FALSE;
+ int returning = false;
if (current_funccal == NULL) {
emsg(_("E133: :return not inside a function"));
@@ -2860,7 +2960,7 @@ void ex_return(exarg_T *eap)
eap->nextcmd = NULL;
if ((*arg != NUL && *arg != '|' && *arg != '\n')
- && eval0((char *)arg, &rettv, &eap->nextcmd, !eap->skip) != FAIL) {
+ && eval0(arg, &rettv, &eap->nextcmd, !eap->skip) != FAIL) {
if (!eap->skip) {
returning = do_return(eap, false, true, &rettv);
} else {
@@ -2883,7 +2983,7 @@ void ex_return(exarg_T *eap)
if (returning) {
eap->nextcmd = NULL;
} else if (eap->nextcmd == NULL) { // no argument
- eap->nextcmd = (char *)check_nextcmd(arg);
+ eap->nextcmd = check_nextcmd(arg);
}
if (eap->skip) {
@@ -2891,15 +2991,13 @@ void ex_return(exarg_T *eap)
}
}
-// TODO(ZyX-I): move to eval/ex_cmds
-
/// ":1,25call func(arg1, arg2)" function call.
void ex_call(exarg_T *eap)
{
- char_u *arg = (char_u *)eap->arg;
- char_u *startarg;
- char_u *name;
- char_u *tofree;
+ char *arg = eap->arg;
+ char *startarg;
+ char *name;
+ char *tofree;
int len;
typval_T rettv;
linenr_T lnum;
@@ -2920,7 +3018,7 @@ void ex_call(exarg_T *eap)
return;
}
- tofree = trans_function_name((char **)&arg, false, TFN_INT, &fudi, &partial);
+ tofree = trans_function_name(&arg, false, TFN_INT, &fudi, &partial);
if (fudi.fd_newkey != NULL) {
// Still need to give an error message for missing key.
semsg(_(e_dictkey), fudi.fd_newkey);
@@ -2939,13 +3037,12 @@ void ex_call(exarg_T *eap)
// If it is the name of a variable of type VAR_FUNC or VAR_PARTIAL use its
// contents. For VAR_PARTIAL get its partial, unless we already have one
// from trans_function_name().
- len = (int)STRLEN(tofree);
- name = deref_func_name((const char *)tofree, &len,
- partial != NULL ? NULL : &partial, false);
+ len = (int)strlen(tofree);
+ name = deref_func_name(tofree, &len, partial != NULL ? NULL : &partial, false);
// Skip white space to allow ":call func ()". Not good, but required for
// backward compatibility.
- startarg = (char_u *)skipwhite((char *)arg);
+ startarg = skipwhite(arg);
rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this.
if (*startarg != '(') {
@@ -2969,13 +3066,13 @@ void ex_call(exarg_T *eap)
arg = startarg;
funcexe_T funcexe = FUNCEXE_INIT;
- funcexe.firstline = eap->line1;
- funcexe.lastline = eap->line2;
- funcexe.doesrange = &doesrange;
- funcexe.evaluate = true;
- funcexe.partial = partial;
- funcexe.selfdict = fudi.fd_dict;
- if (get_func_tv(name, -1, &rettv, (char **)&arg, &funcexe) == FAIL) {
+ funcexe.fe_firstline = eap->line1;
+ funcexe.fe_lastline = eap->line2;
+ funcexe.fe_doesrange = &doesrange;
+ funcexe.fe_evaluate = true;
+ funcexe.fe_partial = partial;
+ funcexe.fe_selfdict = fudi.fd_dict;
+ if (get_func_tv(name, -1, &rettv, &arg, &funcexe) == FAIL) {
failed = true;
break;
}
@@ -3004,7 +3101,7 @@ void ex_call(exarg_T *eap)
// 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)) {
+ if ((!aborting() || did_throw) && (!failed || eap->cstack->cs_trylevel > 0)) {
// Check for trailing illegal characters and a following command.
if (!ends_excmd(*arg)) {
if (!failed && !aborting()) {
@@ -3012,7 +3109,7 @@ void ex_call(exarg_T *eap)
semsg(_(e_trailing_arg), arg);
}
} else {
- eap->nextcmd = (char *)check_nextcmd(arg);
+ eap->nextcmd = check_nextcmd(arg);
}
}
@@ -3029,8 +3126,8 @@ end:
/// @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.
+/// @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;
@@ -3080,7 +3177,7 @@ int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv)
}
report_make_pending(CSTP_RETURN, rettv);
} else {
- current_funccal->returned = TRUE;
+ current_funccal->returned = true;
// If the return is carried out now, store the return value. For
// a return immediately after reanimation, the value is already
@@ -3099,25 +3196,25 @@ int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv)
/// 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 *get_return_cmd(void *rettv)
{
- char_u *s = NULL;
- char_u *tofree = NULL;
+ char *s = NULL;
+ char *tofree = NULL;
if (rettv != NULL) {
- tofree = s = (char_u *)encode_tv2echo((typval_T *)rettv, NULL);
+ tofree = s = encode_tv2echo((typval_T *)rettv, NULL);
}
if (s == NULL) {
- s = (char_u *)"";
+ s = "";
}
STRCPY(IObuff, ":return ");
- STRLCPY(IObuff + 8, s, IOSIZE - 8);
- if (STRLEN(s) + 8 >= IOSIZE) {
+ xstrlcpy(IObuff + 8, s, IOSIZE - 8);
+ if (strlen(s) + 8 >= IOSIZE) {
STRCPY(IObuff + IOSIZE - 4, "...");
}
xfree(tofree);
- return vim_strsave(IObuff);
+ return xstrdup(IObuff);
}
/// Get next function line.
@@ -3128,12 +3225,12 @@ char *get_func_line(int c, void *cookie, int indent, bool do_concat)
{
funccall_T *fcp = (funccall_T *)cookie;
ufunc_T *fp = fcp->func;
- char_u *retval;
+ char *retval;
garray_T *gap; // growarray with function lines
// If breakpoints have been added/deleted need to check for it.
if (fcp->dbg_tick != debug_tick) {
- fcp->breakpoint = dbg_find_breakpoint(false, fp->uf_name, SOURCING_LNUM);
+ fcp->breakpoint = dbg_find_breakpoint(false, (char *)fp->uf_name, SOURCING_LNUM);
fcp->dbg_tick = debug_tick;
}
if (do_profiling == PROF_YES) {
@@ -3153,7 +3250,7 @@ char *get_func_line(int c, void *cookie, int indent, bool do_concat)
if (fcp->linenr >= gap->ga_len) {
retval = NULL;
} else {
- retval = (char_u *)xstrdup(((char **)(gap->ga_data))[fcp->linenr++]);
+ retval = xstrdup(((char **)(gap->ga_data))[fcp->linenr++]);
SOURCING_LNUM = fcp->linenr;
if (do_profiling == PROF_YES) {
func_line_start(cookie);
@@ -3163,16 +3260,16 @@ char *get_func_line(int c, void *cookie, int indent, bool do_concat)
// Did we encounter a breakpoint?
if (fcp->breakpoint != 0 && fcp->breakpoint <= SOURCING_LNUM) {
- dbg_breakpoint(fp->uf_name, SOURCING_LNUM);
+ dbg_breakpoint((char *)fp->uf_name, SOURCING_LNUM);
// Find next breakpoint.
- fcp->breakpoint = dbg_find_breakpoint(false, fp->uf_name, SOURCING_LNUM);
+ fcp->breakpoint = dbg_find_breakpoint(false, (char *)fp->uf_name, SOURCING_LNUM);
fcp->dbg_tick = debug_tick;
}
- return (char *)retval;
+ return retval;
}
-/// @return TRUE if the currently active function should be ended, because a
+/// @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)
{
@@ -3184,7 +3281,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;
@@ -3194,17 +3291,17 @@ int func_has_abort(void *cookie)
/// Changes "rettv" in-place.
void make_partial(dict_T *const selfdict, typval_T *const rettv)
{
- char_u *fname;
- char_u *tofree = NULL;
+ char *fname;
+ char *tofree = NULL;
ufunc_T *fp;
- char_u fname_buf[FLEN_FIXED + 1];
+ char fname_buf[FLEN_FIXED + 1];
int error;
if (rettv->v_type == VAR_PARTIAL && rettv->vval.v_partial->pt_func != NULL) {
fp = rettv->vval.v_partial->pt_func;
} else {
fname = rettv->v_type == VAR_FUNC || rettv->v_type == VAR_STRING
- ? (char_u *)rettv->vval.v_string
+ ? 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);
@@ -3221,7 +3318,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 = (char_u *)rettv->vval.v_string;
+ pt->pt_name = rettv->vval.v_string;
} else {
partial_T *ret_pt = rettv->vval.v_partial;
int i;
@@ -3230,7 +3327,7 @@ void make_partial(dict_T *const selfdict, typval_T *const rettv)
// args. Can't take over name or args, the partial might
// be referenced elsewhere.
if (ret_pt->pt_name != NULL) {
- pt->pt_name = vim_strsave(ret_pt->pt_name);
+ pt->pt_name = xstrdup(ret_pt->pt_name);
func_ref(pt->pt_name);
} else {
pt->pt_func = ret_pt->pt_func;
@@ -3252,7 +3349,7 @@ void make_partial(dict_T *const selfdict, typval_T *const rettv)
}
/// @return the name of the executed function.
-char_u *func_name(void *cookie)
+char *func_name(void *cookie)
{
return ((funccall_T *)cookie)->func->uf_name;
}
@@ -3275,7 +3372,7 @@ 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;
@@ -3539,14 +3636,14 @@ bool set_ref_in_func_args(int copyID)
/// "ht_stack" is used to add hashtabs to be marked. Can be NULL.
///
/// @return true if setting references failed somehow.
-bool set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID)
+bool set_ref_in_func(char *name, ufunc_T *fp_in, int copyID)
{
ufunc_T *fp = fp_in;
funccall_T *fc;
- int error = ERROR_NONE;
- char_u fname_buf[FLEN_FIXED + 1];
- char_u *tofree = NULL;
- char_u *fname;
+ int error = FCERR_NONE;
+ char fname_buf[FLEN_FIXED + 1];
+ char *tofree = NULL;
+ char *fname;
bool abort = false;
if (name == NULL && fp_in == NULL) {
return false;
@@ -3565,20 +3662,18 @@ bool set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID)
return abort;
}
-/// Registers a C extension user function.
-char_u *register_cfunc(cfunc_T cb, cfunc_free_T cb_free, void *state)
+/// Registers a luaref as a lambda.
+char *register_luafunc(LuaRef ref)
{
- char_u *name = get_lambda_name();
- ufunc_T *fp = xcalloc(1, offsetof(ufunc_T, uf_name) + STRLEN(name) + 1);
+ char *name = get_lambda_name();
+ ufunc_T *fp = xcalloc(1, offsetof(ufunc_T, uf_name) + strlen(name) + 1);
fp->uf_refcount = 1;
fp->uf_varargs = true;
- fp->uf_flags = FC_CFUNC;
+ fp->uf_flags = FC_LUAREF;
fp->uf_calls = 0;
fp->uf_script_ctx = current_sctx;
- fp->uf_cb = cb;
- fp->uf_cb_free = cb_free;
- fp->uf_cb_state = state;
+ fp->uf_luaref = ref;
STRCPY(fp->uf_name, name);
hash_add(&func_hashtab, UF2HIKEY(fp));
diff --git a/src/nvim/eval/userfunc.h b/src/nvim/eval/userfunc.h
index 4b7007aae9..c8583f232c 100644
--- a/src/nvim/eval/userfunc.h
+++ b/src/nvim/eval/userfunc.h
@@ -1,18 +1,42 @@
#ifndef NVIM_EVAL_USERFUNC_H
#define NVIM_EVAL_USERFUNC_H
+#include <stdbool.h>
+#include <stddef.h>
+
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds_defs.h"
+#include "nvim/garray.h"
+#include "nvim/hashtab.h"
+#include "nvim/pos.h"
+#include "nvim/types.h"
+
+struct funccal_entry;
// From user function to hashitem and back.
#define UF2HIKEY(fp) ((fp)->uf_name)
#define HIKEY2UF(p) ((ufunc_T *)(p - offsetof(ufunc_T, uf_name)))
#define HI2UF(hi) HIKEY2UF((hi)->hi_key)
-///< Structure used by trans_function_name()
+// flags used in uf_flags
+#define FC_ABORT 0x01 // abort function on error
+#define FC_RANGE 0x02 // function accepts range
+#define FC_DICT 0x04 // Dict function, uses "self"
+#define FC_CLOSURE 0x08 // closure, uses outer scope variables
+#define FC_DELETED 0x10 // :delfunction used while uf_refcount > 0
+#define FC_REMOVED 0x20 // function redefined while uf_refcount > 0
+#define FC_SANDBOX 0x40 // function defined in the sandbox
+#define FC_DEAD 0x80 // function kept only for reference to dfunc
+#define FC_EXPORT 0x100 // "export def Func()"
+#define FC_NOARGS 0x200 // no a: variables in lambda
+#define FC_VIM9 0x400 // defined in vim9 script file
+#define FC_LUAREF 0x800 // luaref callback
+
+/// Structure used by trans_function_name()
typedef struct {
dict_T *fd_dict; ///< Dictionary used.
- char_u *fd_newkey; ///< New key in "dict" in allocated memory.
+ char *fd_newkey; ///< New key in "dict" in allocated memory.
dictitem_T *fd_di; ///< Dictionary item used.
} funcdict_T;
@@ -24,44 +48,43 @@ struct funccal_entry {
/// errors for when calling a function
typedef enum {
- ERROR_UNKNOWN = 0,
- ERROR_TOOMANY,
- ERROR_TOOFEW,
- ERROR_SCRIPT,
- ERROR_DICT,
- ERROR_NONE,
- ERROR_OTHER,
- ERROR_BOTH,
- ERROR_DELETED,
- ERROR_NOTMETHOD,
+ FCERR_UNKNOWN = 0,
+ FCERR_TOOMANY = 1,
+ FCERR_TOOFEW = 2,
+ FCERR_SCRIPT = 3,
+ FCERR_DICT = 4,
+ FCERR_NONE = 5,
+ FCERR_OTHER = 6,
+ FCERR_DELETED = 7,
+ FCERR_NOTMETHOD = 8, ///< function cannot be used as a method
} FnameTransError;
/// Used in funcexe_T. Returns the new argcount.
-typedef int (*ArgvFunc)(int current_argcount, typval_T *argv, int argskip,
- int called_func_argcount);
+typedef int (*ArgvFunc)(int current_argcount, typval_T *argv, int partial_argcount,
+ ufunc_T *called_func);
/// Structure passed between functions dealing with function call execution.
typedef struct {
- ArgvFunc argv_func; ///< when not NULL, can be used to fill in arguments only
- ///< when the invoked function uses them
- linenr_T firstline; ///< first line of range
- linenr_T lastline; ///< last line of range
- bool *doesrange; ///< [out] if not NULL: function handled range
- bool evaluate; ///< actually evaluate expressions
- partial_T *partial; ///< for extra arguments
- dict_T *selfdict; ///< Dictionary for "self"
- typval_T *basetv; ///< base for base->method()
+ ArgvFunc fe_argv_func; ///< when not NULL, can be used to fill in arguments only
+ ///< when the invoked function uses them
+ linenr_T fe_firstline; ///< first line of range
+ linenr_T fe_lastline; ///< last line of range
+ bool *fe_doesrange; ///< [out] if not NULL: function handled range
+ bool fe_evaluate; ///< actually evaluate expressions
+ partial_T *fe_partial; ///< for extra arguments
+ dict_T *fe_selfdict; ///< Dictionary for "self"
+ typval_T *fe_basetv; ///< base for base->method()
} funcexe_T;
#define FUNCEXE_INIT (funcexe_T) { \
- .argv_func = NULL, \
- .firstline = 0, \
- .lastline = 0, \
- .doesrange = NULL, \
- .evaluate = false, \
- .partial = NULL, \
- .selfdict = NULL, \
- .basetv = NULL, \
+ .fe_argv_func = NULL, \
+ .fe_firstline = 0, \
+ .fe_lastline = 0, \
+ .fe_doesrange = NULL, \
+ .fe_evaluate = false, \
+ .fe_partial = NULL, \
+ .fe_selfdict = NULL, \
+ .fe_basetv = NULL, \
}
#define FUNCARG(fp, j) ((char **)(fp->uf_args.ga_data))[j]
diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c
index b38849730a..3e593151fc 100644
--- a/src/nvim/eval/vars.c
+++ b/src/nvim/eval/vars.c
@@ -3,24 +3,43 @@
// eval/vars.c: functions for dealing with variables
+#include <assert.h>
+#include <ctype.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
#include "nvim/ascii.h"
#include "nvim/autocmd.h"
-#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/eval/encode.h"
#include "nvim/eval/funcs.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/eval/userfunc.h"
#include "nvim/eval/vars.h"
+#include "nvim/eval/window.h"
#include "nvim/ex_cmds.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/hashtab.h"
+#include "nvim/macros.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
#include "nvim/ops.h"
#include "nvim/option.h"
-#include "nvim/screen.h"
+#include "nvim/os/os.h"
#include "nvim/search.h"
+#include "nvim/strings.h"
+#include "nvim/types.h"
+#include "nvim/vim.h"
#include "nvim/window.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -62,7 +81,7 @@ static list_T *heredoc_get(exarg_T *eap, char *cmd)
// Check for the optional 'trim' word before the marker
cmd = skipwhite(cmd);
- if (STRNCMP(cmd, "trim", 4) == 0
+ if (strncmp(cmd, "trim", 4) == 0
&& (cmd[4] == NUL || ascii_iswhite(cmd[4]))) {
cmd = skipwhite(cmd + 4);
@@ -81,13 +100,13 @@ static list_T *heredoc_get(exarg_T *eap, char *cmd)
// The marker is the next word.
if (*cmd != NUL && *cmd != '"') {
marker = skipwhite(cmd);
- p = (char *)skiptowhite((char_u *)marker);
+ p = skiptowhite(marker);
if (*skipwhite(p) != NUL && *skipwhite(p) != '"') {
semsg(_(e_trailing_arg), p);
return NULL;
}
*p = NUL;
- if (islower(*marker)) {
+ if (islower((uint8_t)(*marker))) {
emsg(_("E221: Marker cannot start with lower case letter"));
return NULL;
}
@@ -110,10 +129,10 @@ static list_T *heredoc_get(exarg_T *eap, char *cmd)
// with "trim": skip the indent matching the :let line to find the
// marker
if (marker_indent_len > 0
- && STRNCMP(theline, *eap->cmdlinep, marker_indent_len) == 0) {
+ && strncmp(theline, *eap->cmdlinep, (size_t)marker_indent_len) == 0) {
mi = marker_indent_len;
}
- if (STRCMP(marker, theline + mi) == 0) {
+ if (strcmp(marker, theline + mi) == 0) {
xfree(theline);
break;
}
@@ -189,8 +208,8 @@ static void ex_let_const(exarg_T *eap, const bool is_const)
argend--;
}
expr = skipwhite(argend);
- if (*expr != '=' && !((vim_strchr("+-*/%.", *expr) != NULL
- && expr[1] == '=') || STRNCMP(expr, "..=", 3) == 0)) {
+ if (*expr != '=' && !((vim_strchr("+-*/%.", (uint8_t)(*expr)) != NULL
+ && expr[1] == '=') || strncmp(expr, "..=", 3) == 0)) {
// ":let" without "=": list variables
if (*arg == '[') {
emsg(_(e_invarg));
@@ -207,7 +226,7 @@ static void ex_let_const(exarg_T *eap, const bool is_const)
list_func_vars(&first);
list_vim_vars(&first);
}
- eap->nextcmd = (char *)check_nextcmd((char_u *)arg);
+ eap->nextcmd = check_nextcmd(arg);
} else if (expr[0] == '=' && expr[1] == '<' && expr[2] == '<') {
// HERE document
list_T *l = heredoc_get(eap, expr + 3);
@@ -225,17 +244,19 @@ static void ex_let_const(exarg_T *eap, const bool is_const)
op[0] = '=';
op[1] = NUL;
if (*expr != '=') {
- if (vim_strchr("+-*/%.", *expr) != NULL) {
+ if (vim_strchr("+-*/%.", (uint8_t)(*expr)) != NULL) {
op[0] = *expr; // +=, -=, *=, /=, %= or .=
if (expr[0] == '.' && expr[1] == '.') { // ..=
expr++;
}
}
- expr = skipwhite(expr + 2);
+ expr += 2;
} else {
- expr = skipwhite(expr + 1);
+ expr += 1;
}
+ expr = skipwhite(expr);
+
if (eap->skip) {
emsg_skip++;
}
@@ -378,9 +399,8 @@ const char *skip_var_list(const char *arg, int *var_count, int *semicolon)
}
}
return p + 1;
- } else {
- return skip_var_one((char *)arg);
}
+ return skip_var_one((char *)arg);
}
/// Skip one (assignable) variable name, including @r, $VAR, &option, d.key,
@@ -413,7 +433,7 @@ void list_hashtable_vars(hashtab_T *ht, const char *prefix, int empty, int *firs
// apply :filter /pat/ to variable name
xstrlcpy(buf, prefix, IOSIZE);
xstrlcat(buf, (char *)di->di_key, IOSIZE);
- if (message_filtered((char_u *)buf)) {
+ if (message_filtered(buf)) {
continue;
}
@@ -539,8 +559,6 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first)
return arg;
}
-// TODO(ZyX-I): move to eval/ex_cmds
-
/// Set one item of `:let var = expr` or `:let [v1, v2] = list` to its value
///
/// @param[in] arg Start of the variable name.
@@ -558,8 +576,6 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
{
char *arg_end = NULL;
int len;
- int opt_flags;
- char *tofree = NULL;
// ":let $VAR = expr": Set environment variable.
if (*arg == '$') {
@@ -574,20 +590,20 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
if (len == 0) {
semsg(_(e_invarg2), name - 1);
} else {
- if (op != NULL && vim_strchr("+-*/%", *op) != NULL) {
+ if (op != NULL && vim_strchr("+-*/%", (uint8_t)(*op)) != NULL) {
semsg(_(e_letwrong), op);
} else if (endchars != NULL
- && vim_strchr(endchars, *skipwhite(arg)) == NULL) {
+ && vim_strchr(endchars, (uint8_t)(*skipwhite(arg))) == NULL) {
emsg(_(e_letunexp));
} else if (!check_secure()) {
+ char *tofree = NULL;
const char c1 = name[len];
name[len] = NUL;
const char *p = tv_get_string_chk(tv);
if (p != NULL && op != NULL && *op == '.') {
char *s = vim_getenv(name);
-
if (s != NULL) {
- tofree = (char *)concat_str((const char_u *)s, (const char_u *)p);
+ tofree = concat_str(s, p);
p = (const char *)tofree;
xfree(s);
}
@@ -609,10 +625,11 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
return NULL;
}
// Find the end of the name.
- char *const p = (char *)find_option_end((const char **)&arg, &opt_flags);
+ int scope;
+ char *const p = (char *)find_option_end((const char **)&arg, &scope);
if (p == NULL
|| (endchars != NULL
- && vim_strchr(endchars, *skipwhite(p)) == NULL)) {
+ && vim_strchr(endchars, (uint8_t)(*skipwhite(p))) == NULL)) {
emsg(_(e_letunexp));
} else {
varnumber_T n = 0;
@@ -621,11 +638,13 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
char *stringval = NULL;
const char *s = NULL;
bool failed = false;
+ uint32_t opt_p_flags;
+ char *tofree = NULL;
const char c1 = *p;
*p = NUL;
- opt_type = get_option_value(arg, &numval, &stringval, opt_flags);
+ opt_type = get_option_value(arg, &numval, &stringval, &opt_p_flags, scope);
if (opt_type == gov_bool
|| opt_type == gov_number
|| opt_type == gov_hidden_bool
@@ -634,8 +653,13 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
n = (long)tv_get_number(tv);
}
- // Avoid setting a string option to the text "v:false" or similar.
- if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) {
+ if ((opt_p_flags & P_FUNC) && tv_is_func(*tv)) {
+ // If the option can be set to a function reference or a lambda
+ // and the passed value is a function reference, then convert it to
+ // the name (string) of the function reference.
+ s = tofree = encode_tv2string(tv, NULL);
+ } else if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) {
+ // Avoid setting a string option to the text "v:false" or similar.
s = tv_get_string_chk(tv);
}
@@ -663,8 +687,7 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
} else if (opt_type == gov_string && stringval != NULL && s != NULL) {
// string
char *const oldstringval = stringval;
- stringval = (char *)concat_str((const char_u *)stringval,
- (const char_u *)s);
+ stringval = concat_str(stringval, s);
xfree(oldstringval);
s = stringval;
}
@@ -673,7 +696,7 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
if (!failed) {
if (opt_type != gov_string || s != NULL) {
- char *err = set_option_value(arg, n, s, opt_flags);
+ char *err = set_option_value(arg, n, s, scope);
arg_end = p;
if (err != NULL) {
emsg(_(err));
@@ -684,6 +707,7 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
}
*p = c1;
xfree(stringval);
+ xfree(tofree);
}
// ":let @r = expr": Set register contents.
} else if (*arg == '@') {
@@ -692,10 +716,10 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
return NULL;
}
arg++;
- if (op != NULL && vim_strchr("+-*/%", *op) != NULL) {
+ if (op != NULL && vim_strchr("+-*/%", (uint8_t)(*op)) != NULL) {
semsg(_(e_letwrong), op);
} else if (endchars != NULL
- && vim_strchr(endchars, *skipwhite(arg + 1)) == NULL) {
+ && vim_strchr(endchars, (uint8_t)(*skipwhite(arg + 1))) == NULL) {
emsg(_(e_letunexp));
} else {
char *s;
@@ -705,14 +729,13 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
if (p != NULL && op != NULL && *op == '.') {
s = get_reg_contents(*arg == '@' ? '"' : *arg, kGRegExprSrc);
if (s != NULL) {
- ptofree = (char *)concat_str((char_u *)s, (const char_u *)p);
+ ptofree = concat_str(s, p);
p = (const char *)ptofree;
xfree(s);
}
}
if (p != NULL) {
- write_reg_contents(*arg == '@' ? '"' : *arg,
- (const char_u *)p, (ssize_t)STRLEN(p), false);
+ write_reg_contents(*arg == '@' ? '"' : *arg, p, (ssize_t)strlen(p), false);
arg_end = arg + 1;
}
xfree(ptofree);
@@ -724,7 +747,7 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
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) {
+ if (endchars != NULL && vim_strchr(endchars, (uint8_t)(*skipwhite(p))) == NULL) {
emsg(_(e_letunexp));
} else {
set_var_lval(&lv, p, tv, copy, is_const, op);
@@ -745,8 +768,6 @@ void ex_unlet(exarg_T *eap)
ex_unletlock(eap, eap->arg, 0, do_unlet_var);
}
-// TODO(ZyX-I): move to eval/ex_cmds
-
/// ":lockvar" and ":unlockvar" commands
void ex_lockvar(exarg_T *eap)
{
@@ -763,8 +784,6 @@ void ex_lockvar(exarg_T *eap)
ex_unletlock(eap, arg, deep, do_lock_var);
}
-// TODO(ZyX-I): move to eval/ex_cmds
-
/// Common parsing logic for :unlet, :lockvar and :unlockvar.
///
/// Invokes `callback` afterwards if successful and `eap->skip == false`.
@@ -791,6 +810,7 @@ static void ex_unletlock(exarg_T *eap, char *argstart, int deep, ex_unletlock_ca
semsg(_(e_invarg2), arg - 1);
return;
}
+ assert(*lv.ll_name == '$'); // suppress clang "Uninitialized argument value"
if (!error && !eap->skip && callback(&lv, arg, eap, deep) == FAIL) {
error = true;
}
@@ -825,11 +845,9 @@ static void ex_unletlock(exarg_T *eap, char *argstart, int deep, ex_unletlock_ca
arg = skipwhite(name_end);
} while (!ends_excmd(*arg));
- eap->nextcmd = (char *)check_nextcmd((char_u *)arg);
+ eap->nextcmd = check_nextcmd(arg);
}
-// TODO(ZyX-I): move to eval/ex_cmds
-
/// Unlet a variable indicated by `lp`.
///
/// @param[in] lp The lvalue.
@@ -846,7 +864,7 @@ static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_
int cc;
if (lp->ll_tv == NULL) {
- cc = (char_u)(*name_end);
+ cc = (uint8_t)(*name_end);
*name_end = NUL;
// Environment variable, normal name or expanded name.
@@ -859,13 +877,13 @@ static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_
} else if ((lp->ll_list != NULL
// ll_list is not NULL when lvalue is not in a list, NULL lists
// yield E689.
- && var_check_lock(tv_list_locked(lp->ll_list),
- lp->ll_name,
- lp->ll_name_len))
+ && value_check_lock(tv_list_locked(lp->ll_list),
+ lp->ll_name,
+ lp->ll_name_len))
|| (lp->ll_dict != NULL
- && var_check_lock(lp->ll_dict->dv_lock,
- lp->ll_name,
- lp->ll_name_len))) {
+ && value_check_lock(lp->ll_dict->dv_lock,
+ lp->ll_name,
+ lp->ll_name_len))) {
return FAIL;
} else if (lp->ll_range) {
assert(lp->ll_list != NULL);
@@ -874,18 +892,17 @@ static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_
listitem_T *last_li = first_li;
for (;;) {
listitem_T *const li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li);
- if (var_check_lock(TV_LIST_ITEM_TV(lp->ll_li)->v_lock,
- lp->ll_name,
- lp->ll_name_len)) {
+ if (value_check_lock(TV_LIST_ITEM_TV(lp->ll_li)->v_lock,
+ lp->ll_name,
+ lp->ll_name_len)) {
return false;
}
lp->ll_li = li;
lp->ll_n1++;
if (lp->ll_li == NULL || (!lp->ll_empty2 && lp->ll_n2 < lp->ll_n1)) {
break;
- } else {
- last_li = lp->ll_li;
}
+ last_li = lp->ll_li;
}
tv_list_remove_items(lp->ll_list, first_li, last_li);
} else {
@@ -920,8 +937,6 @@ static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_
return ret;
}
-// TODO(ZyX-I): move to eval/ex_cmds
-
/// unlet a variable
///
/// @param[in] name Variable name to unlet.
@@ -961,11 +976,11 @@ int do_unlet(const char *const name, const size_t name_len, const bool forceit)
dictitem_T *const di = TV_DICT_HI2DI(hi);
if (var_check_fixed(di->di_flags, name, TV_CSTRING)
|| var_check_ro(di->di_flags, name, TV_CSTRING)
- || var_check_lock(d->dv_lock, name, TV_CSTRING)) {
+ || value_check_lock(d->dv_lock, name, TV_CSTRING)) {
return FAIL;
}
- if (var_check_lock(d->dv_lock, name, TV_CSTRING)) {
+ if (value_check_lock(d->dv_lock, name, TV_CSTRING)) {
return FAIL;
}
@@ -992,8 +1007,6 @@ int do_unlet(const char *const name, const size_t name_len, const bool forceit)
return FAIL;
}
-// TODO(ZyX-I): move to eval/ex_cmds
-
/// Lock or unlock variable indicated by `lp`.
///
/// Locks if `eap->cmdidx == CMD_lockvar`, unlocks otherwise.
@@ -1010,10 +1023,6 @@ static int do_lock_var(lval_T *lp, char *name_end FUNC_ATTR_UNUSED, exarg_T *eap
bool lock = eap->cmdidx == CMD_lockvar;
int ret = OK;
- if (deep == 0) { // Nothing to do.
- return OK;
- }
-
if (lp->ll_tv == NULL) {
if (*lp->ll_name == '$') {
semsg(_(e_lock_unlock), lp->ll_name);
@@ -1037,9 +1046,13 @@ static int do_lock_var(lval_T *lp, char *name_end FUNC_ATTR_UNUSED, exarg_T *eap
} else {
di->di_flags &= (uint8_t)(~DI_FLAGS_LOCK);
}
- tv_item_lock(&di->di_tv, deep, lock, false);
+ if (deep != 0) {
+ tv_item_lock(&di->di_tv, deep, lock, false);
+ }
}
}
+ } else if (deep == 0) {
+ // nothing to do
} else if (lp->ll_range) {
listitem_T *li = lp->ll_li;
@@ -1099,7 +1112,7 @@ int get_var_tv(const char *name, int len, typval_T *rettv, dictitem_T **dip, boo
/// 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)
+char *get_var_value(const char *const name)
{
dictitem_T *v;
@@ -1107,7 +1120,7 @@ char_u *get_var_value(const char *const name)
if (v == NULL) {
return NULL;
}
- return (char_u *)tv_get_string(&v->di_tv);
+ return (char *)tv_get_string(&v->di_tv);
}
/// Clean up a list of internal variables.
@@ -1118,7 +1131,7 @@ 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;
@@ -1162,7 +1175,7 @@ void delete_var(hashtab_T *ht, hashitem_T *hi)
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, (ptrdiff_t)STRLEN(v->di_key),
+ list_one_var_a(prefix, (const char *)v->di_key, (ptrdiff_t)strlen((char *)v->di_key),
v->di_tv.v_type, (s == NULL ? "" : s), first);
xfree(s);
}
@@ -1258,7 +1271,7 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv,
v = find_var_in_scoped_ht(name, name_len, true);
}
- if (tv_is_func(*tv) && !var_check_func_name(name, v == NULL)) {
+ if (tv_is_func(*tv) && var_wrong_func_name(name, v == NULL)) {
return;
}
@@ -1269,12 +1282,18 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv,
return;
}
- // existing variable, need to clear the value
+ // Check in this order for backwards compatibility:
+ // - Whether the variable is read-only
+ // - Whether the variable value is locked
+ // - Whether the variable is locked
if (var_check_ro(v->di_flags, name, name_len)
- || var_check_lock(v->di_tv.v_lock, name, name_len)) {
+ || value_check_lock(v->di_tv.v_lock, name, name_len)
+ || var_check_lock(v->di_flags, name, name_len)) {
return;
}
+ // existing variable, need to clear the value
+
// Handle setting internal v: variables separately where needed to
// prevent changing the type.
if (is_vimvarht(ht)) {
@@ -1300,7 +1319,7 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv,
set_search_direction(v->di_tv.vval.v_number ? '/' : '?');
} else if (strcmp(varname, "hlsearch") == 0) {
no_hlsearch = !v->di_tv.vval.v_number;
- redraw_all_later(SOME_VALID);
+ redraw_all_later(UPD_SOME_VALID);
}
return;
} else if (v->di_tv.v_type != tv->v_type) {
@@ -1330,7 +1349,7 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv,
v = xmalloc(sizeof(dictitem_T) + strlen(varname));
STRCPY(v->di_key, varname);
- if (tv_dict_add(dict, v) == FAIL) {
+ if (hash_add(ht, (char *)v->di_key) == FAIL) {
xfree(v);
return;
}
@@ -1349,12 +1368,8 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv,
}
if (watched) {
- if (oldtv.v_type == VAR_UNKNOWN) {
- tv_dict_watcher_notify(dict, (char *)v->di_key, &v->di_tv, NULL);
- } else {
- tv_dict_watcher_notify(dict, (char *)v->di_key, &v->di_tv, &oldtv);
- tv_clear(&oldtv);
- }
+ tv_dict_watcher_notify(dict, (char *)v->di_key, &v->di_tv, &oldtv);
+ tv_clear(&oldtv);
}
if (is_const) {
@@ -1409,6 +1424,26 @@ bool var_check_ro(const int flags, const char *name, size_t name_len)
return true;
}
+/// Return true if di_flags "flags" indicates variable "name" is locked.
+/// Also give an error message.
+bool var_check_lock(const int flags, const char *name, size_t name_len)
+{
+ if (!(flags & DI_FLAGS_LOCK)) {
+ return false;
+ }
+
+ if (name_len == TV_TRANSLATE) {
+ name = _(name);
+ name_len = strlen(name);
+ } else if (name_len == TV_CSTRING) {
+ name_len = strlen(name);
+ }
+
+ semsg(_("E1122: Variable is locked: %*s"), (int)name_len, name);
+
+ return true;
+}
+
/// Check whether variable is fixed (DI_FLAGS_FIX)
///
/// Also gives an error message.
@@ -1443,37 +1478,34 @@ bool var_check_fixed(const int flags, const char *name, size_t name_len)
return false;
}
-// TODO(ZyX-I): move to eval/expressions
-
/// Check if name is a valid name to assign funcref to
///
/// @param[in] name Possible function/funcref name.
/// @param[in] new_var True if it is a name for a variable.
///
-/// @return false in case of error, true in case of success. Also gives an
+/// @return false in case of success, true in case of failure. Also gives an
/// error message if appropriate.
-bool var_check_func_name(const char *const name, const bool new_var)
+bool var_wrong_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("wbst", name[0]) != NULL && name[1] == ':')
- && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':')
- ? name[2] : name[0])) {
+ // Allow autoload variable.
+ if (!(vim_strchr("wbst", (uint8_t)name[0]) != NULL && name[1] == ':')
+ && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':') ? name[2] : name[0])
+ && vim_strchr(name, '#') == NULL) {
semsg(_("E704: Funcref variable name must start with a capital: %s"), name);
- return false;
+ return true;
}
// Don't allow hiding a function. When "v" is not NULL we might be
// assigning another function to the same var, the type is checked
// below.
if (new_var && function_exists(name, false)) {
semsg(_("E705: Variable name conflicts with existing function: %s"), name);
- return false;
+ return true;
}
- return true;
+ return false;
}
-// TODO(ZyX-I): move to eval/expressions
-
/// Check if a variable name is valid
///
/// @param[in] varname Variable name to check.
@@ -1627,7 +1659,7 @@ static void set_option_from_tv(const char *varname, typval_T *varp)
strval = tv_get_string_buf_chk(varp, nbuf);
}
if (!error && strval != NULL) {
- set_option_value(varname, numval, strval, OPT_LOCAL);
+ set_option_value_give_err(varname, numval, strval, OPT_LOCAL);
}
}
@@ -1648,25 +1680,27 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off)
const char *varname = tv_get_string_chk(&argvars[off + 1]);
typval_T *varp = &argvars[off + 2];
- if (win != NULL && varname != NULL && varp != NULL) {
- bool need_switch_win = !(tp == curtab && win == curwin);
- switchwin_T switchwin;
- if (!need_switch_win || switch_win(&switchwin, win, tp, true) == OK) {
- if (*varname == '&') {
- set_option_from_tv(varname + 1, varp);
- } else {
- const size_t varname_len = strlen(varname);
- char *const winvarname = xmalloc(varname_len + 3);
- memcpy(winvarname, "w:", 2);
- memcpy(winvarname + 2, varname, varname_len + 1);
- set_var(winvarname, varname_len + 2, varp, true);
- xfree(winvarname);
- }
- }
- if (need_switch_win) {
- restore_win(&switchwin, true);
+ if (win == NULL || varname == NULL) {
+ return;
+ }
+
+ bool need_switch_win = !(tp == curtab && win == curwin);
+ switchwin_T switchwin;
+ if (!need_switch_win || switch_win(&switchwin, win, tp, true) == OK) {
+ if (*varname == '&') {
+ set_option_from_tv(varname + 1, varp);
+ } else {
+ const size_t varname_len = strlen(varname);
+ char *const winvarname = xmalloc(varname_len + 3);
+ memcpy(winvarname, "w:", 2);
+ memcpy(winvarname + 2, varname, varname_len + 1);
+ set_var(winvarname, varname_len + 2, varp, true);
+ xfree(winvarname);
}
}
+ if (need_switch_win) {
+ restore_win(&switchwin, true);
+ }
}
bool var_exists(const char *var)
@@ -1702,7 +1736,7 @@ bool var_exists(const char *var)
}
/// "gettabvar()" function
-void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_gettabvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *const varname = tv_get_string_chk(&argvars[1]);
tabpage_T *const tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL));
@@ -1716,19 +1750,19 @@ void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "gettabwinvar()" function
-void f_gettabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_gettabwinvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
getwinvar(argvars, rettv, 1);
}
/// "getwinvar()" function
-void f_getwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_getwinvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
getwinvar(argvars, rettv, 0);
}
/// "getbufvar()" function
-void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_getbufvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *const varname = tv_get_string_chk(&argvars[1]);
buf_T *const buf = tv_get_buf_from_arg(&argvars[0]);
@@ -1737,7 +1771,7 @@ void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "settabvar()" function
-void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_settabvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = 0;
@@ -1749,38 +1783,40 @@ void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
const char *const varname = tv_get_string_chk(&argvars[1]);
typval_T *const varp = &argvars[2];
- if (varname != NULL && tp != NULL) {
- tabpage_T *const save_curtab = curtab;
- goto_tabpage_tp(tp, false, false);
+ if (varname == NULL || tp == NULL) {
+ return;
+ }
- const size_t varname_len = strlen(varname);
- char *const tabvarname = xmalloc(varname_len + 3);
- memcpy(tabvarname, "t:", 2);
- memcpy(tabvarname + 2, varname, varname_len + 1);
- set_var(tabvarname, varname_len + 2, varp, true);
- xfree(tabvarname);
-
- // Restore current tabpage.
- if (valid_tabpage(save_curtab)) {
- goto_tabpage_tp(save_curtab, false, false);
- }
+ tabpage_T *const save_curtab = curtab;
+ goto_tabpage_tp(tp, false, false);
+
+ const size_t varname_len = strlen(varname);
+ char *const tabvarname = xmalloc(varname_len + 3);
+ memcpy(tabvarname, "t:", 2);
+ memcpy(tabvarname + 2, varname, varname_len + 1);
+ set_var(tabvarname, varname_len + 2, varp, true);
+ xfree(tabvarname);
+
+ // Restore current tabpage.
+ if (valid_tabpage(save_curtab)) {
+ goto_tabpage_tp(save_curtab, false, false);
}
}
/// "settabwinvar()" function
-void f_settabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_settabwinvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
setwinvar(argvars, rettv, 1);
}
/// "setwinvar()" function
-void f_setwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_setwinvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
setwinvar(argvars, rettv, 0);
}
/// "setbufvar()" function
-void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_setbufvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (check_secure()
|| !tv_check_str_or_nr(&argvars[0])) {
@@ -1790,27 +1826,29 @@ void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
buf_T *const buf = tv_get_buf(&argvars[0], false);
typval_T *varp = &argvars[2];
- if (buf != NULL && varname != NULL) {
- if (*varname == '&') {
- aco_save_T aco;
+ if (buf == NULL || varname == NULL) {
+ return;
+ }
- // set curbuf to be our buf, temporarily
- aucmd_prepbuf(&aco, buf);
+ if (*varname == '&') {
+ aco_save_T aco;
- set_option_from_tv(varname + 1, varp);
+ // Set curbuf to be our buf, temporarily.
+ aucmd_prepbuf(&aco, buf);
- // reset notion of buffer
- aucmd_restbuf(&aco);
- } else {
- const size_t varname_len = STRLEN(varname);
- char *const bufvarname = xmalloc(varname_len + 3);
- buf_T *const save_curbuf = curbuf;
- curbuf = buf;
- memcpy(bufvarname, "b:", 2);
- memcpy(bufvarname + 2, varname, varname_len + 1);
- set_var(bufvarname, varname_len + 2, varp, true);
- xfree(bufvarname);
- curbuf = save_curbuf;
- }
+ set_option_from_tv(varname + 1, varp);
+
+ // reset notion of buffer
+ aucmd_restbuf(&aco);
+ } else {
+ const size_t varname_len = strlen(varname);
+ char *const bufvarname = xmalloc(varname_len + 3);
+ buf_T *const save_curbuf = curbuf;
+ curbuf = buf;
+ memcpy(bufvarname, "b:", 2);
+ memcpy(bufvarname + 2, varname, varname_len + 1);
+ set_var(bufvarname, varname_len + 2, varp, true);
+ xfree(bufvarname);
+ curbuf = save_curbuf;
}
}
diff --git a/src/nvim/eval/vars.h b/src/nvim/eval/vars.h
index 73efc4938a..b87c9d62cb 100644
--- a/src/nvim/eval/vars.h
+++ b/src/nvim/eval/vars.h
@@ -1,7 +1,7 @@
#ifndef NVIM_EVAL_VARS_H
#define NVIM_EVAL_VARS_H
-#include "nvim/ex_cmds_defs.h" // For exarg_T
+#include "nvim/ex_cmds_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/vars.h.generated.h"
diff --git a/src/nvim/eval/window.c b/src/nvim/eval/window.c
new file mode 100644
index 0000000000..f58a0c488a
--- /dev/null
+++ b/src/nvim/eval/window.c
@@ -0,0 +1,954 @@
+// 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
+
+// eval/window.c: Window related builtin functions
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
+#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
+#include "nvim/cursor.h"
+#include "nvim/eval/funcs.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/eval/window.h"
+#include "nvim/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/macros.h"
+#include "nvim/memline_defs.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
+#include "nvim/move.h"
+#include "nvim/option_defs.h"
+#include "nvim/pos.h"
+#include "nvim/types.h"
+#include "nvim/vim.h"
+#include "nvim/window.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "eval/window.c.generated.h"
+#endif
+
+static char *e_invalwindow = N_("E957: Invalid window number");
+static char e_cannot_resize_window_in_another_tab_page[]
+ = N_("E1308: Cannot resize a window in another tab page");
+
+static int win_getid(typval_T *argvars)
+{
+ if (argvars[0].v_type == VAR_UNKNOWN) {
+ return curwin->handle;
+ }
+ int winnr = (int)tv_get_number(&argvars[0]);
+ win_T *wp;
+ if (winnr <= 0) {
+ return 0;
+ }
+
+ if (argvars[1].v_type == VAR_UNKNOWN) {
+ wp = firstwin;
+ } else {
+ tabpage_T *tp = NULL;
+ int tabnr = (int)tv_get_number(&argvars[1]);
+ FOR_ALL_TABS(tp2) {
+ if (--tabnr == 0) {
+ tp = tp2;
+ break;
+ }
+ }
+ if (tp == NULL) {
+ return -1;
+ }
+ if (tp == curtab) {
+ wp = firstwin;
+ } else {
+ wp = tp->tp_firstwin;
+ }
+ }
+ for (; wp != NULL; wp = wp->w_next) {
+ if (--winnr == 0) {
+ return wp->handle;
+ }
+ }
+ return 0;
+}
+
+static void win_id2tabwin(typval_T *const argvars, typval_T *const rettv)
+{
+ handle_T id = (handle_T)tv_get_number(&argvars[0]);
+
+ int winnr = 1;
+ int tabnr = 1;
+ win_get_tabwin(id, &tabnr, &winnr);
+
+ list_T *const list = tv_list_alloc_ret(rettv, 2);
+ tv_list_append_number(list, tabnr);
+ tv_list_append_number(list, winnr);
+}
+
+win_T *win_id2wp(int id)
+{
+ return win_id2wp_tp(id, NULL);
+}
+
+/// Return the window and tab pointer of window "id".
+win_T *win_id2wp_tp(int id, tabpage_T **tpp)
+{
+ FOR_ALL_TAB_WINDOWS(tp, wp) {
+ if (wp->handle == id) {
+ if (tpp != NULL) {
+ *tpp = tp;
+ }
+ return wp;
+ }
+ }
+
+ return NULL;
+}
+
+static int win_id2win(typval_T *argvars)
+{
+ int nr = 1;
+ int id = (int)tv_get_number(&argvars[0]);
+
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->handle == id) {
+ return nr;
+ }
+ nr++;
+ }
+ return 0;
+}
+
+void win_findbuf(typval_T *argvars, list_T *list)
+{
+ int bufnr = (int)tv_get_number(&argvars[0]);
+
+ FOR_ALL_TAB_WINDOWS(tp, wp) {
+ if (wp->w_buffer->b_fnum == bufnr) {
+ tv_list_append_number(list, wp->handle);
+ }
+ }
+}
+
+/// Find window specified by "vp" in tabpage "tp".
+///
+/// @param tp NULL for current tab page
+/// @return current window if "vp" is number zero.
+/// NULL if not found.
+win_T *find_win_by_nr(typval_T *vp, tabpage_T *tp)
+{
+ int nr = (int)tv_get_number_chk(vp, NULL);
+
+ if (nr < 0) {
+ return NULL;
+ }
+
+ if (nr == 0) {
+ return curwin;
+ }
+
+ // This method accepts NULL as an alias for curtab.
+ if (tp == NULL) {
+ tp = curtab;
+ }
+
+ FOR_ALL_WINDOWS_IN_TAB(wp, tp) {
+ if (nr >= LOWEST_WIN_ID) {
+ if (wp->handle == nr) {
+ return wp;
+ }
+ } else if (--nr <= 0) {
+ return wp;
+ }
+ }
+ return NULL;
+}
+
+/// 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)
+{
+ int nr = (int)tv_get_number_chk(vp, NULL);
+
+ if (nr >= LOWEST_WIN_ID) {
+ return win_id2wp((int)tv_get_number(vp));
+ }
+
+ return find_win_by_nr(vp, NULL);
+}
+
+/// Find window specified by "wvp" in tabpage "tvp".
+win_T *find_tabwin(typval_T *wvp, typval_T *tvp)
+{
+ win_T *wp = NULL;
+ tabpage_T *tp = NULL;
+
+ if (wvp->v_type != VAR_UNKNOWN) {
+ if (tvp->v_type != VAR_UNKNOWN) {
+ long n = tv_get_number(tvp);
+ if (n >= 0) {
+ tp = find_tabpage((int)n);
+ }
+ } else {
+ tp = curtab;
+ }
+
+ if (tp != NULL) {
+ wp = find_win_by_nr(wvp, tp);
+ }
+ } else {
+ wp = curwin;
+ }
+
+ return wp;
+}
+
+/// Get the layout of the given tab page for winlayout().
+static void get_framelayout(const frame_T *fr, list_T *l, bool outer)
+{
+ if (fr == NULL) {
+ return;
+ }
+
+ list_T *fr_list;
+ if (outer) {
+ // outermost call from f_winlayout()
+ fr_list = l;
+ } else {
+ fr_list = tv_list_alloc(2);
+ tv_list_append_list(l, fr_list);
+ }
+
+ if (fr->fr_layout == FR_LEAF) {
+ if (fr->fr_win != NULL) {
+ tv_list_append_string(fr_list, "leaf", -1);
+ tv_list_append_number(fr_list, fr->fr_win->handle);
+ }
+ } else {
+ tv_list_append_string(fr_list, fr->fr_layout == FR_ROW ? "row" : "col", -1);
+
+ list_T *const win_list = tv_list_alloc(kListLenUnknown);
+ tv_list_append_list(fr_list, win_list);
+ const frame_T *child = fr->fr_child;
+ while (child != NULL) {
+ get_framelayout(child, win_list, false);
+ child = child->fr_next;
+ }
+ }
+}
+
+/// Common code for tabpagewinnr() and winnr().
+static int get_winnr(tabpage_T *tp, typval_T *argvar)
+{
+ int nr = 1;
+
+ win_T *twin = (tp == curtab) ? curwin : tp->tp_curwin;
+ if (argvar->v_type != VAR_UNKNOWN) {
+ bool invalid_arg = false;
+ const char *const arg = tv_get_string_chk(argvar);
+ if (arg == NULL) {
+ nr = 0; // Type error; errmsg already given.
+ } else if (strcmp(arg, "$") == 0) {
+ twin = (tp == curtab) ? lastwin : tp->tp_lastwin;
+ } else if (strcmp(arg, "#") == 0) {
+ twin = (tp == curtab) ? prevwin : tp->tp_prevwin;
+ if (twin == NULL) {
+ nr = 0;
+ }
+ } else {
+ // Extract the window count (if specified). e.g. winnr('3j')
+ char *endp;
+ long count = strtol((char *)arg, &endp, 10);
+ if (count <= 0) {
+ // if count is not specified, default to 1
+ count = 1;
+ }
+ if (endp != NULL && *endp != '\0') {
+ if (strequal(endp, "j")) {
+ twin = win_vert_neighbor(tp, twin, false, count);
+ } else if (strequal(endp, "k")) {
+ twin = win_vert_neighbor(tp, twin, true, count);
+ } else if (strequal(endp, "h")) {
+ twin = win_horz_neighbor(tp, twin, true, count);
+ } else if (strequal(endp, "l")) {
+ twin = win_horz_neighbor(tp, twin, false, count);
+ } else {
+ invalid_arg = true;
+ }
+ } else {
+ invalid_arg = true;
+ }
+ }
+
+ if (invalid_arg) {
+ semsg(_(e_invexpr2), arg);
+ nr = 0;
+ }
+ }
+
+ if (nr <= 0) {
+ return 0;
+ }
+
+ for (win_T *wp = (tp == curtab) ? firstwin : tp->tp_firstwin;
+ wp != twin; wp = wp->w_next) {
+ if (wp == NULL) {
+ // didn't find it in this tabpage
+ nr = 0;
+ break;
+ }
+ nr++;
+ }
+ return nr;
+}
+
+/// @return information about a window as a dictionary.
+static 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);
+ tv_dict_add_nr(dict, S_LEN("height"), wp->w_height_inner);
+ 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"), wp->w_winbar_height);
+ tv_dict_add_nr(dict, S_LEN("width"), wp->w_width_inner);
+ tv_dict_add_nr(dict, S_LEN("bufnr"), wp->w_buffer->b_fnum);
+ tv_dict_add_nr(dict, S_LEN("wincol"), wp->w_wincol + 1);
+ tv_dict_add_nr(dict, S_LEN("textoff"), win_col_off(wp));
+ tv_dict_add_nr(dict, S_LEN("terminal"), bt_terminal(wp->w_buffer));
+ tv_dict_add_nr(dict, S_LEN("quickfix"), bt_quickfix(wp->w_buffer));
+ tv_dict_add_nr(dict, S_LEN("loclist"),
+ (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL));
+
+ // Add a reference to window variables
+ tv_dict_add_dict(dict, S_LEN("variables"), wp->w_vars);
+
+ return dict;
+}
+
+/// @return information (variables, options, etc.) about a tab page
+/// as a dictionary.
+static dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx)
+{
+ dict_T *const dict = tv_dict_alloc();
+
+ tv_dict_add_nr(dict, S_LEN("tabnr"), tp_idx);
+
+ list_T *const l = tv_list_alloc(kListLenMayKnow);
+ FOR_ALL_WINDOWS_IN_TAB(wp, tp) {
+ tv_list_append_number(l, (varnumber_T)wp->handle);
+ }
+ tv_dict_add_list(dict, S_LEN("windows"), l);
+
+ // Make a reference to tabpage variables
+ tv_dict_add_dict(dict, S_LEN("variables"), tp->tp_vars);
+
+ return dict;
+}
+
+/// "gettabinfo()" function
+void f_gettabinfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ tabpage_T *tparg = NULL;
+
+ tv_list_alloc_ret(rettv, (argvars[0].v_type == VAR_UNKNOWN
+ ? 1
+ : kListLenMayKnow));
+
+ if (argvars[0].v_type != VAR_UNKNOWN) {
+ // Information about one tab page
+ tparg = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL));
+ if (tparg == NULL) {
+ return;
+ }
+ }
+
+ // Get information about a specific tab page or all tab pages
+ int tpnr = 0;
+ FOR_ALL_TABS(tp) {
+ tpnr++;
+ if (tparg != NULL && tp != tparg) {
+ continue;
+ }
+ dict_T *const d = get_tabpage_info(tp, tpnr);
+ tv_list_append_dict(rettv->vval.v_list, d);
+ if (tparg != NULL) {
+ return;
+ }
+ }
+}
+
+/// "getwininfo()" function
+void f_getwininfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ win_T *wparg = NULL;
+
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
+
+ if (argvars[0].v_type != VAR_UNKNOWN) {
+ wparg = win_id2wp((int)tv_get_number(&argvars[0]));
+ if (wparg == NULL) {
+ return;
+ }
+ }
+
+ // Collect information about either all the windows across all the tab
+ // pages or one particular window.
+ int16_t tabnr = 0;
+ FOR_ALL_TABS(tp) {
+ tabnr++;
+ int16_t winnr = 0;
+ FOR_ALL_WINDOWS_IN_TAB(wp, tp) {
+ winnr++;
+ if (wparg != NULL && wp != wparg) {
+ continue;
+ }
+ dict_T *const d = get_win_info(wp, tabnr, winnr);
+ tv_list_append_dict(rettv->vval.v_list, d);
+ if (wparg != NULL) {
+ // found information about a specific window
+ return;
+ }
+ }
+ }
+}
+
+/// "getwinpos({timeout})" function
+void f_getwinpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ tv_list_alloc_ret(rettv, 2);
+ tv_list_append_number(rettv->vval.v_list, -1);
+ tv_list_append_number(rettv->vval.v_list, -1);
+}
+
+/// "getwinposx()" function
+void f_getwinposx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->vval.v_number = -1;
+}
+
+/// "getwinposy()" function
+void f_getwinposy(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->vval.v_number = -1;
+}
+
+/// "tabpagenr()" function
+void f_tabpagenr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ int nr = 1;
+
+ if (argvars[0].v_type != VAR_UNKNOWN) {
+ const char *const arg = tv_get_string_chk(&argvars[0]);
+ nr = 0;
+ if (arg != NULL) {
+ if (strcmp(arg, "$") == 0) {
+ nr = tabpage_index(NULL) - 1;
+ } else if (strcmp(arg, "#") == 0) {
+ nr = valid_tabpage(lastused_tabpage) ? tabpage_index(lastused_tabpage) : 0;
+ } else {
+ semsg(_(e_invexpr2), arg);
+ }
+ }
+ } else {
+ nr = tabpage_index(curtab);
+ }
+ rettv->vval.v_number = nr;
+}
+
+/// "tabpagewinnr()" function
+void f_tabpagewinnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ int nr = 1;
+ tabpage_T *const tp = find_tabpage((int)tv_get_number(&argvars[0]));
+ if (tp == NULL) {
+ nr = 0;
+ } else {
+ nr = get_winnr(tp, &argvars[1]);
+ }
+ rettv->vval.v_number = nr;
+}
+
+/// "win_execute(win_id, command)" function
+void f_win_execute(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ // Return an empty string if something fails.
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+
+ int id = (int)tv_get_number(argvars);
+ tabpage_T *tp;
+ win_T *wp = win_id2wp_tp(id, &tp);
+ if (wp == NULL || tp == NULL) {
+ return;
+ }
+
+ WIN_EXECUTE(wp, tp, execute_common(argvars, rettv, 1));
+}
+
+/// "win_findbuf()" function
+void f_win_findbuf(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
+ win_findbuf(argvars, rettv->vval.v_list);
+}
+
+/// "win_getid()" function
+void f_win_getid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->vval.v_number = win_getid(argvars);
+}
+
+/// "win_gotoid()" function
+void f_win_gotoid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ int id = (int)tv_get_number(&argvars[0]);
+
+ if (cmdwin_type != 0) {
+ emsg(_(e_cmdwin));
+ return;
+ }
+ FOR_ALL_TAB_WINDOWS(tp, wp) {
+ if (wp->handle == id) {
+ goto_tabpage_win(tp, wp);
+ rettv->vval.v_number = 1;
+ return;
+ }
+ }
+}
+
+/// "win_id2tabwin()" function
+void f_win_id2tabwin(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ win_id2tabwin(argvars, rettv);
+}
+
+/// "win_id2win()" function
+void f_win_id2win(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->vval.v_number = win_id2win(argvars);
+}
+
+/// "win_move_separator()" function
+void f_win_move_separator(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->vval.v_number = false;
+
+ win_T *wp = find_win_by_nr_or_id(&argvars[0]);
+ if (wp == NULL || wp->w_floating) {
+ return;
+ }
+ if (!win_valid(wp)) {
+ emsg(_(e_cannot_resize_window_in_another_tab_page));
+ return;
+ }
+
+ int offset = (int)tv_get_number(&argvars[1]);
+ win_drag_vsep_line(wp, offset);
+ rettv->vval.v_number = true;
+}
+
+/// "win_move_statusline()" function
+void f_win_move_statusline(typval_T *argvars, typval_T *rettv, EvalFuncData 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;
+ }
+ if (!win_valid(wp)) {
+ emsg(_(e_cannot_resize_window_in_another_tab_page));
+ return;
+ }
+
+ offset = (int)tv_get_number(&argvars[1]);
+ win_drag_status_line(wp, offset);
+ rettv->vval.v_number = true;
+}
+
+/// "win_screenpos()" function
+void f_win_screenpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ tv_list_alloc_ret(rettv, 2);
+ const win_T *const wp = find_win_by_nr_or_id(&argvars[0]);
+ tv_list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_winrow + 1);
+ 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
+static void win_move_into_split(win_T *wp, win_T *targetwin, int size, int flags)
+{
+ int height = wp->w_height;
+ win_T *oldwin = curwin;
+
+ if (wp == targetwin || is_aucmd_win(wp)) {
+ return;
+ }
+
+ // Jump to the target window
+ if (curwin != targetwin) {
+ win_goto(targetwin);
+ }
+
+ // Remove the old window and frame from the tree of frames
+ int dir;
+ (void)winframe_remove(wp, &dir, NULL);
+ win_remove(wp, NULL);
+ last_status(false); // may need to remove last status line
+ (void)win_comp_pos(); // recompute window positions
+
+ // Split a window on the desired side and put the old window there
+ (void)win_split_ins(size, flags, wp, dir);
+
+ // If splitting horizontally, try to preserve height
+ if (size == 0 && !(flags & WSP_VERT)) {
+ win_setheight_win(height, wp);
+ if (p_ea) {
+ win_equal(wp, true, 'v');
+ }
+ }
+
+ if (oldwin != curwin) {
+ win_goto(oldwin);
+ }
+}
+
+/// "win_splitmove()" function
+void f_win_splitmove(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ win_T *wp = find_win_by_nr_or_id(&argvars[0]);
+ win_T *targetwin = find_win_by_nr_or_id(&argvars[1]);
+
+ if (wp == NULL || targetwin == NULL || wp == targetwin
+ || !win_valid(wp) || !win_valid(targetwin)
+ || win_valid_floating(wp) || win_valid_floating(targetwin)) {
+ emsg(_(e_invalwindow));
+ rettv->vval.v_number = -1;
+ return;
+ }
+
+ int flags = 0, size = 0;
+
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ dict_T *d;
+ dictitem_T *di;
+
+ if (argvars[2].v_type != VAR_DICT || argvars[2].vval.v_dict == NULL) {
+ emsg(_(e_invarg));
+ return;
+ }
+
+ d = argvars[2].vval.v_dict;
+ if (tv_dict_get_number(d, "vertical")) {
+ flags |= WSP_VERT;
+ }
+ if ((di = tv_dict_find(d, "rightbelow", -1)) != NULL) {
+ flags |= tv_get_number(&di->di_tv) ? WSP_BELOW : WSP_ABOVE;
+ }
+ size = (int)tv_dict_get_number(d, "size");
+ }
+
+ win_move_into_split(wp, targetwin, size, flags);
+}
+
+/// "win_gettype(nr)" function
+void f_win_gettype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ win_T *wp = curwin;
+
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+ if (argvars[0].v_type != VAR_UNKNOWN) {
+ wp = find_win_by_nr_or_id(&argvars[0]);
+ if (wp == NULL) {
+ rettv->vval.v_string = xstrdup("unknown");
+ return;
+ }
+ }
+ if (is_aucmd_win(wp)) {
+ rettv->vval.v_string = xstrdup("autocmd");
+ } else if (wp->w_p_pvw) {
+ rettv->vval.v_string = xstrdup("preview");
+ } else if (wp->w_floating) {
+ rettv->vval.v_string = xstrdup("popup");
+ } else if (wp == curwin && cmdwin_type != 0) {
+ rettv->vval.v_string = xstrdup("command");
+ } else if (bt_quickfix(wp->w_buffer)) {
+ rettv->vval.v_string = xstrdup((wp->w_llist_ref != NULL ? "loclist" : "quickfix"));
+ }
+}
+
+/// "getcmdwintype()" function
+void f_getcmdwintype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+ rettv->vval.v_string = xmallocz(1);
+ rettv->vval.v_string[0] = (char)cmdwin_type;
+}
+
+/// "winbufnr(nr)" function
+void f_winbufnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ win_T *wp = find_win_by_nr_or_id(&argvars[0]);
+ if (wp == NULL) {
+ rettv->vval.v_number = -1;
+ } else {
+ rettv->vval.v_number = wp->w_buffer->b_fnum;
+ }
+}
+
+/// "wincol()" function
+void f_wincol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ validate_cursor();
+ rettv->vval.v_number = curwin->w_wcol + 1;
+}
+
+/// "winheight(nr)" function
+void f_winheight(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ win_T *wp = find_win_by_nr_or_id(&argvars[0]);
+ if (wp == NULL) {
+ rettv->vval.v_number = -1;
+ } else {
+ rettv->vval.v_number = wp->w_height_inner;
+ }
+}
+
+/// "winlayout()" function
+void f_winlayout(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ tabpage_T *tp;
+
+ tv_list_alloc_ret(rettv, 2);
+
+ if (argvars[0].v_type == VAR_UNKNOWN) {
+ tp = curtab;
+ } else {
+ tp = find_tabpage((int)tv_get_number(&argvars[0]));
+ if (tp == NULL) {
+ return;
+ }
+ }
+
+ get_framelayout(tp->tp_topframe, rettv->vval.v_list, true);
+}
+
+/// "winline()" function
+void f_winline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ validate_cursor();
+ rettv->vval.v_number = curwin->w_wrow + 1;
+}
+
+/// "winnr()" function
+void f_winnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->vval.v_number = get_winnr(curtab, &argvars[0]);
+}
+
+/// "winrestcmd()" function
+void f_winrestcmd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ char buf[50];
+
+ garray_T ga;
+ ga_init(&ga, (int)sizeof(char), 70);
+
+ // Do this twice to handle some window layouts properly.
+ for (int i = 0; i < 2; i++) {
+ int winnr = 1;
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ snprintf(buf, sizeof(buf), "%dresize %d|", winnr,
+ wp->w_height);
+ ga_concat(&ga, buf);
+ snprintf(buf, sizeof(buf), "vert %dresize %d|", winnr,
+ wp->w_width);
+ ga_concat(&ga, buf);
+ winnr++;
+ }
+ }
+ ga_append(&ga, NUL);
+
+ rettv->vval.v_string = ga.ga_data;
+ rettv->v_type = VAR_STRING;
+}
+
+/// "winrestview()" function
+void f_winrestview(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ dict_T *dict = argvars[0].vval.v_dict;
+
+ if (argvars[0].v_type != VAR_DICT || dict == NULL) {
+ emsg(_(e_invarg));
+ } else {
+ dictitem_T *di;
+ if ((di = tv_dict_find(dict, S_LEN("lnum"))) != NULL) {
+ curwin->w_cursor.lnum = (linenr_T)tv_get_number(&di->di_tv);
+ }
+ if ((di = tv_dict_find(dict, S_LEN("col"))) != NULL) {
+ curwin->w_cursor.col = (colnr_T)tv_get_number(&di->di_tv);
+ }
+ if ((di = tv_dict_find(dict, S_LEN("coladd"))) != NULL) {
+ curwin->w_cursor.coladd = (colnr_T)tv_get_number(&di->di_tv);
+ }
+ if ((di = tv_dict_find(dict, S_LEN("curswant"))) != NULL) {
+ curwin->w_curswant = (colnr_T)tv_get_number(&di->di_tv);
+ curwin->w_set_curswant = false;
+ }
+ if ((di = tv_dict_find(dict, S_LEN("topline"))) != NULL) {
+ set_topline(curwin, (linenr_T)tv_get_number(&di->di_tv));
+ }
+ if ((di = tv_dict_find(dict, S_LEN("topfill"))) != NULL) {
+ curwin->w_topfill = (int)tv_get_number(&di->di_tv);
+ }
+ if ((di = tv_dict_find(dict, S_LEN("leftcol"))) != NULL) {
+ curwin->w_leftcol = (colnr_T)tv_get_number(&di->di_tv);
+ }
+ if ((di = tv_dict_find(dict, S_LEN("skipcol"))) != NULL) {
+ curwin->w_skipcol = (colnr_T)tv_get_number(&di->di_tv);
+ }
+
+ check_cursor();
+ win_new_height(curwin, curwin->w_height);
+ win_new_width(curwin, curwin->w_width);
+ changed_window_setting();
+
+ if (curwin->w_topline <= 0) {
+ curwin->w_topline = 1;
+ }
+ if (curwin->w_topline > curbuf->b_ml.ml_line_count) {
+ curwin->w_topline = curbuf->b_ml.ml_line_count;
+ }
+ check_topfill(curwin, true);
+ }
+}
+
+/// "winsaveview()" function
+void f_winsaveview(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ tv_dict_alloc_ret(rettv);
+ dict_T *dict = rettv->vval.v_dict;
+
+ tv_dict_add_nr(dict, S_LEN("lnum"), (varnumber_T)curwin->w_cursor.lnum);
+ tv_dict_add_nr(dict, S_LEN("col"), (varnumber_T)curwin->w_cursor.col);
+ tv_dict_add_nr(dict, S_LEN("coladd"), (varnumber_T)curwin->w_cursor.coladd);
+ update_curswant();
+ tv_dict_add_nr(dict, S_LEN("curswant"), (varnumber_T)curwin->w_curswant);
+
+ tv_dict_add_nr(dict, S_LEN("topline"), (varnumber_T)curwin->w_topline);
+ tv_dict_add_nr(dict, S_LEN("topfill"), (varnumber_T)curwin->w_topfill);
+ tv_dict_add_nr(dict, S_LEN("leftcol"), (varnumber_T)curwin->w_leftcol);
+ tv_dict_add_nr(dict, S_LEN("skipcol"), (varnumber_T)curwin->w_skipcol);
+}
+
+/// "winwidth(nr)" function
+void f_winwidth(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ win_T *wp = find_win_by_nr_or_id(&argvars[0]);
+ if (wp == NULL) {
+ rettv->vval.v_number = -1;
+ } else {
+ rettv->vval.v_number = wp->w_width_inner;
+ }
+}
+
+/// 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.
+///
+/// @param no_display if true the display won't be affected, no redraw is
+/// triggered, another tabpage access is limited.
+///
+/// @return FAIL if switching to "win" failed.
+int switch_win(switchwin_T *switchwin, win_T *win, tabpage_T *tp, bool no_display)
+{
+ block_autocmds();
+ return switch_win_noblock(switchwin, win, tp, no_display);
+}
+
+// As switch_win() but without blocking autocommands.
+int switch_win_noblock(switchwin_T *switchwin, win_T *win, tabpage_T *tp, bool no_display)
+{
+ CLEAR_POINTER(switchwin);
+ 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) {
+ switchwin->sw_curtab = curtab;
+ if (no_display) {
+ curtab->tp_firstwin = firstwin;
+ curtab->tp_lastwin = lastwin;
+ curtab = tp;
+ firstwin = curtab->tp_firstwin;
+ lastwin = curtab->tp_lastwin;
+ } else {
+ goto_tabpage_tp(tp, false, false);
+ }
+ }
+ if (!win_valid(win)) {
+ return FAIL;
+ }
+ curwin = win;
+ curbuf = curwin->w_buffer;
+ return OK;
+}
+
+// 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(switchwin_T *switchwin, bool no_display)
+{
+ restore_win_noblock(switchwin, no_display);
+ unblock_autocmds();
+}
+
+// As restore_win() but without unblocking autocommands.
+void restore_win_noblock(switchwin_T *switchwin, bool no_display)
+{
+ if (switchwin->sw_curtab != NULL && valid_tabpage(switchwin->sw_curtab)) {
+ if (no_display) {
+ curtab->tp_firstwin = firstwin;
+ curtab->tp_lastwin = lastwin;
+ curtab = switchwin->sw_curtab;
+ firstwin = curtab->tp_firstwin;
+ lastwin = curtab->tp_lastwin;
+ } else {
+ goto_tabpage_tp(switchwin->sw_curtab, false, false);
+ }
+ }
+
+ 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;
+ }
+}
diff --git a/src/nvim/eval/window.h b/src/nvim/eval/window.h
new file mode 100644
index 0000000000..995f0a55a9
--- /dev/null
+++ b/src/nvim/eval/window.h
@@ -0,0 +1,78 @@
+#ifndef NVIM_EVAL_WINDOW_H
+#define NVIM_EVAL_WINDOW_H
+
+#include <stdbool.h>
+#include <string.h>
+
+#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
+#include "nvim/cursor.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/globals.h"
+#include "nvim/mark.h"
+#include "nvim/option_defs.h"
+#include "nvim/os/os.h"
+#include "nvim/pos.h"
+#include "nvim/vim.h"
+#include "nvim/window.h"
+
+/// 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 cwd_[MAXPATHL]; \
+ char 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(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 "eval/window.h.generated.h"
+#endif
+#endif // NVIM_EVAL_WINDOW_H
diff --git a/src/nvim/event/libuv_process.c b/src/nvim/event/libuv_process.c
index 0251ea9957..e528d21a71 100644
--- a/src/nvim/event/libuv_process.c
+++ b/src/nvim/event/libuv_process.c
@@ -2,16 +2,18 @@
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include <assert.h>
+#include <stdint.h>
#include <uv.h>
+#include "nvim/eval/typval.h"
#include "nvim/event/libuv_process.h"
#include "nvim/event/loop.h"
#include "nvim/event/process.h"
-#include "nvim/event/rstream.h"
-#include "nvim/event/wstream.h"
+#include "nvim/event/stream.h"
#include "nvim/log.h"
#include "nvim/macros.h"
#include "nvim/os/os.h"
+#include "nvim/ui_client.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "event/libuv_process.c.generated.h"
@@ -25,7 +27,7 @@ int libuv_process_spawn(LibuvProcess *uvproc)
uvproc->uvopts.file = proc->argv[0];
uvproc->uvopts.args = proc->argv;
uvproc->uvopts.flags = UV_PROCESS_WINDOWS_HIDE;
-#ifdef WIN32
+#ifdef MSWIN
// libuv collapses the argv to a CommandLineToArgvW()-style string. cmd.exe
// expects a different syntax (must be prepared by the caller before now).
if (os_shell_is_cmdexe(proc->argv[0])) {
@@ -40,11 +42,19 @@ int libuv_process_spawn(LibuvProcess *uvproc)
#endif
uvproc->uvopts.exit_cb = exit_cb;
uvproc->uvopts.cwd = proc->cwd;
+
uvproc->uvopts.stdio = uvproc->uvstdio;
uvproc->uvopts.stdio_count = 3;
uvproc->uvstdio[0].flags = UV_IGNORE;
uvproc->uvstdio[1].flags = UV_IGNORE;
uvproc->uvstdio[2].flags = UV_IGNORE;
+
+ if (ui_client_forward_stdin) {
+ assert(UI_CLIENT_STDIN_FD == 3);
+ uvproc->uvopts.stdio_count = 4;
+ uvproc->uvstdio[3].data.fd = 0;
+ uvproc->uvstdio[3].flags = UV_INHERIT_FD;
+ }
uvproc->uv.data = proc;
if (proc->env) {
@@ -55,7 +65,7 @@ int libuv_process_spawn(LibuvProcess *uvproc)
if (!proc->in.closed) {
uvproc->uvstdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE;
-#ifdef WIN32
+#ifdef MSWIN
uvproc->uvstdio[0].flags |= proc->overlapped ? UV_OVERLAPPED_PIPE : 0;
#endif
uvproc->uvstdio[0].data.stream = STRUCT_CAST(uv_stream_t,
@@ -64,7 +74,7 @@ int libuv_process_spawn(LibuvProcess *uvproc)
if (!proc->out.closed) {
uvproc->uvstdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
-#ifdef WIN32
+#ifdef MSWIN
// pipe must be readable for IOCP to work on Windows.
uvproc->uvstdio[1].flags |= proc->overlapped ?
(UV_READABLE_PIPE | UV_OVERLAPPED_PIPE) : 0;
@@ -77,6 +87,9 @@ int libuv_process_spawn(LibuvProcess *uvproc)
uvproc->uvstdio[2].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
uvproc->uvstdio[2].data.stream = STRUCT_CAST(uv_stream_t,
&proc->err.uv.pipe);
+ } else if (proc->fwd_err) {
+ uvproc->uvstdio[2].flags = UV_INHERIT_FD;
+ uvproc->uvstdio[2].data.fd = STDERR_FILENO;
}
int status;
@@ -113,7 +126,7 @@ static void close_cb(uv_handle_t *handle)
static void exit_cb(uv_process_t *handle, int64_t status, int term_signal)
{
Process *proc = handle->data;
-#if defined(WIN32)
+#if defined(MSWIN)
// Use stored/expected signal.
term_signal = proc->exit_signal;
#endif
diff --git a/src/nvim/event/libuv_process.h b/src/nvim/event/libuv_process.h
index 1132ce79ca..4472839944 100644
--- a/src/nvim/event/libuv_process.h
+++ b/src/nvim/event/libuv_process.h
@@ -3,13 +3,14 @@
#include <uv.h>
+#include "nvim/event/loop.h"
#include "nvim/event/process.h"
typedef struct libuv_process {
Process process;
uv_process_t uv;
uv_process_options_t uvopts;
- uv_stdio_container_t uvstdio[3];
+ uv_stdio_container_t uvstdio[4];
} LibuvProcess;
static inline LibuvProcess libuv_process_init(Loop *loop, void *data)
diff --git a/src/nvim/event/loop.c b/src/nvim/event/loop.c
index 1b5cc23b09..ab2524c1a9 100644
--- a/src/nvim/event/loop.c
+++ b/src/nvim/event/loop.c
@@ -1,13 +1,16 @@
// 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 <stdarg.h>
+#include <stdbool.h>
#include <stdint.h>
+#include <stdlib.h>
#include <uv.h>
+#include "nvim/event/defs.h"
#include "nvim/event/loop.h"
-#include "nvim/event/process.h"
#include "nvim/log.h"
+#include "nvim/memory.h"
+#include "nvim/os/time.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "event/loop.c.generated.h"
@@ -31,41 +34,57 @@ void loop_init(Loop *loop, void *data)
loop->poll_timer.data = xmalloc(sizeof(bool)); // "timeout expired" flag
}
-/// Processes one `Loop.uv` event (at most).
-/// Processes all `Loop.fast_events` events.
-/// Does NOT process `Loop.events`, that is an application-specific decision.
+/// Process `Loop.uv` events with a timeout.
///
/// @param loop
-/// @param ms 0: non-blocking poll.
-/// >0: timeout after `ms`.
-/// <0: wait forever.
-/// @returns true if `ms` timeout was reached
-bool loop_poll_events(Loop *loop, int ms)
+/// @param ms 0: non-blocking poll.
+/// > 0: timeout after `ms`.
+/// < 0: wait forever.
+/// @param once true: process at most one `Loop.uv` event.
+/// false: process until `ms` timeout (only has effect if `ms` > 0).
+/// @return true if `ms` > 0 and was reached
+bool loop_uv_run(Loop *loop, int64_t ms, bool once)
{
if (loop->recursive++) {
abort(); // Should not re-enter uv_run
}
uv_run_mode mode = UV_RUN_ONCE;
- bool timeout_expired = false;
+ bool *timeout_expired = loop->poll_timer.data;
+ *timeout_expired = false;
if (ms > 0) {
- *((bool *)loop->poll_timer.data) = false; // reset "timeout expired" flag
- // Dummy timer to ensure UV_RUN_ONCE does not block indefinitely for I/O.
+ // This timer ensures UV_RUN_ONCE does not block indefinitely for I/O.
uv_timer_start(&loop->poll_timer, timer_cb, (uint64_t)ms, (uint64_t)ms);
} else if (ms == 0) {
// For ms == 0, do a non-blocking event poll.
mode = UV_RUN_NOWAIT;
}
- uv_run(&loop->uv, mode);
+ do { // -V1044
+ uv_run(&loop->uv, mode);
+ } while (ms > 0 && !once && !*timeout_expired); // -V560
if (ms > 0) {
- timeout_expired = *((bool *)loop->poll_timer.data);
uv_timer_stop(&loop->poll_timer);
}
loop->recursive--; // Can re-enter uv_run now
+ return *timeout_expired;
+}
+
+/// Processes one `Loop.uv` event (at most).
+/// Processes all `Loop.fast_events` events.
+/// Does NOT process `Loop.events`, that is an application-specific decision.
+///
+/// @param loop
+/// @param ms 0: non-blocking poll.
+/// > 0: timeout after `ms`.
+/// < 0: wait forever.
+/// @return true if `ms` > 0 and was reached
+bool loop_poll_events(Loop *loop, int64_t ms)
+{
+ bool timeout_expired = loop_uv_run(loop, ms, true);
multiqueue_process_events(loop->fast_events);
return timeout_expired;
}
diff --git a/src/nvim/event/loop.h b/src/nvim/event/loop.h
index 65980c6c05..b2265a726d 100644
--- a/src/nvim/event/loop.h
+++ b/src/nvim/event/loop.h
@@ -4,8 +4,8 @@
#include <stdint.h>
#include <uv.h>
+#include "klib/klist.h"
#include "nvim/event/multiqueue.h"
-#include "nvim/lib/klist.h"
#include "nvim/os/time.h"
typedef void *WatcherPtr;
@@ -58,7 +58,7 @@ typedef struct loop {
// Poll for events until a condition or timeout
#define LOOP_PROCESS_EVENTS_UNTIL(loop, multiqueue, timeout, condition) \
do { \
- int remaining = timeout; \
+ int64_t remaining = timeout; \
uint64_t before = (remaining > 0) ? os_hrtime() : 0; \
while (!(condition)) { \
LOOP_PROCESS_EVENTS(loop, multiqueue, remaining); \
@@ -66,7 +66,7 @@ typedef struct loop {
break; \
} else if (remaining > 0) { \
uint64_t now = os_hrtime(); \
- remaining -= (int)((now - before) / 1000000); \
+ remaining -= (int64_t)((now - before) / 1000000); \
before = now; \
if (remaining <= 0) { \
break; \
diff --git a/src/nvim/event/multiqueue.c b/src/nvim/event/multiqueue.c
index 40d20033e0..e05084b656 100644
--- a/src/nvim/event/multiqueue.c
+++ b/src/nvim/event/multiqueue.c
@@ -46,14 +46,14 @@
// other sources and focus on a specific channel.
#include <assert.h>
-#include <stdarg.h>
#include <stdbool.h>
-#include <stdint.h>
+#include <stddef.h>
#include <uv.h>
+#include "nvim/event/defs.h"
#include "nvim/event/multiqueue.h"
+#include "nvim/lib/queue.h"
#include "nvim/memory.h"
-#include "nvim/os/time.h"
typedef struct multiqueue_item MultiQueueItem;
struct multiqueue_item {
diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c
index e029f778f6..1a524a56ca 100644
--- a/src/nvim/event/process.c
+++ b/src/nvim/event/process.c
@@ -2,20 +2,25 @@
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include <assert.h>
+#include <inttypes.h>
+#include <signal.h>
#include <stdlib.h>
#include <uv.h>
+#include "klib/klist.h"
#include "nvim/event/libuv_process.h"
#include "nvim/event/loop.h"
#include "nvim/event/process.h"
-#include "nvim/event/rstream.h"
-#include "nvim/event/wstream.h"
#include "nvim/globals.h"
#include "nvim/log.h"
#include "nvim/macros.h"
+#include "nvim/main.h"
#include "nvim/os/process.h"
#include "nvim/os/pty_process.h"
#include "nvim/os/shell.h"
+#include "nvim/os/time.h"
+#include "nvim/rbuffer.h"
+#include "nvim/ui_client.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "event/process.c.generated.h"
@@ -32,10 +37,16 @@ void __gcov_flush(void);
static bool process_is_tearing_down = false;
+// Delay exit until handles are closed, to avoid deadlocks
+static int exit_need_delay = 0;
+
/// @returns zero on success, or negative error code
int process_spawn(Process *proc, bool in, bool out, bool err)
FUNC_ATTR_NONNULL_ALL
{
+ // forwarding stderr contradicts with processing it internally
+ assert(!(err && proc->fwd_err));
+
if (in) {
uv_pipe_init(&proc->loop->uv, &proc->in.uv.pipe, 0);
} else {
@@ -395,12 +406,41 @@ static void process_close_handles(void **argv)
exit_need_delay--;
}
+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, 1, main_loop.exit_delay_timer.data);
+}
+
+static void exit_event(void **argv)
+{
+ int status = (int)(intptr_t)argv[0];
+ if (exit_need_delay) {
+ main_loop.exit_delay_timer.data = argv[0];
+ uv_timer_start(&main_loop.exit_delay_timer, exit_delay_cb, 0, 0);
+ return;
+ }
+
+ if (!exiting) {
+ os_exit(status);
+ }
+}
+
+void exit_from_channel(int status)
+{
+ multiqueue_put(main_loop.fast_events, exit_event, 1, status);
+}
+
static void on_process_exit(Process *proc)
{
Loop *loop = proc->loop;
ILOG("exited: pid=%d status=%d stoptime=%" PRIu64, proc->pid, proc->status,
proc->stopped_time);
+ if (ui_client_channel_id) {
+ exit_from_channel(proc->status);
+ }
+
// Process has terminated, but there could still be data to be read from the
// OS. We are still in the libuv loop, so we cannot call code that polls for
// more data directly. Instead delay the reading after the libuv loop by
diff --git a/src/nvim/event/process.h b/src/nvim/event/process.h
index 30254bfe07..e0057faffb 100644
--- a/src/nvim/event/process.h
+++ b/src/nvim/event/process.h
@@ -1,11 +1,20 @@
#ifndef NVIM_EVENT_PROCESS_H
#define NVIM_EVENT_PROCESS_H
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
#include "nvim/event/rstream.h"
+#include "nvim/event/stream.h"
#include "nvim/event/wstream.h"
+struct process;
+
typedef enum {
kProcessTypeUv,
kProcessTypePty,
@@ -29,7 +38,7 @@ struct process {
/// Exit handler. If set, user must call process_free().
process_exit_cb cb;
internal_process_cb internal_exit_cb, internal_close_cb;
- bool closed, detach, overlapped;
+ bool closed, detach, overlapped, fwd_err;
MultiQueue *events;
};
@@ -53,7 +62,8 @@ static inline Process process_init(Loop *loop, ProcessType type, void *data)
.closed = false,
.internal_close_cb = NULL,
.internal_exit_cb = NULL,
- .detach = false
+ .detach = false,
+ .fwd_err = false,
};
}
diff --git a/src/nvim/event/rstream.c b/src/nvim/event/rstream.c
index 2847788ef8..a88d62fd6b 100644
--- a/src/nvim/event/rstream.c
+++ b/src/nvim/event/rstream.c
@@ -3,17 +3,18 @@
#include <assert.h>
#include <stdbool.h>
+#include <stddef.h>
#include <stdint.h>
-#include <stdlib.h>
#include <uv.h>
-#include "nvim/ascii.h"
#include "nvim/event/loop.h"
#include "nvim/event/rstream.h"
+#include "nvim/event/stream.h"
#include "nvim/log.h"
+#include "nvim/macros.h"
#include "nvim/main.h"
-#include "nvim/memory.h"
-#include "nvim/vim.h"
+#include "nvim/os/os_defs.h"
+#include "nvim/rbuffer.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "event/rstream.c.generated.h"
diff --git a/src/nvim/event/signal.c b/src/nvim/event/signal.c
index 4a45a2ec2f..8256ca2091 100644
--- a/src/nvim/event/signal.c
+++ b/src/nvim/event/signal.c
@@ -1,6 +1,7 @@
// 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 <stddef.h>
#include <uv.h>
#include "nvim/event/loop.h"
diff --git a/src/nvim/event/signal.h b/src/nvim/event/signal.h
index 7fe352edef..f9adf62c20 100644
--- a/src/nvim/event/signal.h
+++ b/src/nvim/event/signal.h
@@ -4,6 +4,9 @@
#include <uv.h>
#include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
+
+struct signal_watcher;
typedef struct signal_watcher SignalWatcher;
typedef void (*signal_cb)(SignalWatcher *watcher, int signum, void *data);
diff --git a/src/nvim/event/socket.c b/src/nvim/event/socket.c
index 9496a568b9..10756015ad 100644
--- a/src/nvim/event/socket.c
+++ b/src/nvim/event/socket.c
@@ -2,23 +2,24 @@
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include <assert.h>
-#include <stdint.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
#include <uv.h>
#include "nvim/ascii.h"
#include "nvim/charset.h"
#include "nvim/event/loop.h"
-#include "nvim/event/rstream.h"
#include "nvim/event/socket.h"
-#include "nvim/event/wstream.h"
+#include "nvim/event/stream.h"
+#include "nvim/gettext.h"
#include "nvim/log.h"
#include "nvim/macros.h"
#include "nvim/main.h"
#include "nvim/memory.h"
#include "nvim/os/os.h"
#include "nvim/path.h"
-#include "nvim/strings.h"
-#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "event/socket.c.generated.h"
@@ -125,7 +126,7 @@ int socket_watcher_start(SocketWatcher *watcher, int backlog, socket_cb cb)
// Libuv converts ENOENT to EACCES for Windows compatibility, but if
// the parent directory does not exist, ENOENT would be more accurate.
*path_tail(watcher->addr) = NUL;
- if (!os_path_exists((char_u *)watcher->addr)) {
+ if (!os_path_exists(watcher->addr)) {
result = UV_ENOENT;
}
}
diff --git a/src/nvim/event/socket.h b/src/nvim/event/socket.h
index d30ae45502..c6fcdec4bb 100644
--- a/src/nvim/event/socket.h
+++ b/src/nvim/event/socket.h
@@ -4,9 +4,12 @@
#include <uv.h>
#include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
#include "nvim/event/rstream.h"
#include "nvim/event/wstream.h"
+struct socket_watcher;
+
#define ADDRESS_MAX_SIZE 256
typedef struct socket_watcher SocketWatcher;
diff --git a/src/nvim/event/stream.c b/src/nvim/event/stream.c
index b34fd73d52..0a4918636a 100644
--- a/src/nvim/event/stream.c
+++ b/src/nvim/event/stream.c
@@ -3,14 +3,16 @@
#include <assert.h>
#include <stdbool.h>
-#include <stdio.h>
+#include <stddef.h>
#include <uv.h>
+#include <uv/version.h>
+#include "nvim/event/loop.h"
#include "nvim/event/stream.h"
#include "nvim/log.h"
#include "nvim/macros.h"
#include "nvim/rbuffer.h"
-#ifdef WIN32
+#ifdef MSWIN
# include "nvim/os/os_win_console.h"
#endif
@@ -60,7 +62,7 @@ void stream_init(Loop *loop, Stream *stream, int fd, uv_stream_t *uvstream)
stream->uv.idle.data = stream;
} else {
assert(type == UV_NAMED_PIPE || type == UV_TTY);
-#ifdef WIN32
+#ifdef MSWIN
if (type == UV_TTY) {
uv_tty_init(&loop->uv, &stream->uv.tty, fd, 0);
uv_tty_set_mode(&stream->uv.tty, UV_TTY_MODE_RAW);
@@ -75,7 +77,7 @@ void stream_init(Loop *loop, Stream *stream, int fd, uv_stream_t *uvstream)
uv_pipe_init(&loop->uv, &stream->uv.pipe, 0);
uv_pipe_open(&stream->uv.pipe, fd);
stream->uvstream = STRUCT_CAST(uv_stream_t, &stream->uv.pipe);
-#ifdef WIN32
+#ifdef MSWIN
}
#endif
}
@@ -109,7 +111,7 @@ void stream_close(Stream *stream, stream_close_cb on_stream_close, void *data)
stream->close_cb = on_stream_close;
stream->close_cb_data = data;
-#ifdef WIN32
+#ifdef MSWIN
if (UV_TTY == uv_guess_handle(stream->fd)) {
// Undo UV_TTY_MODE_RAW from stream_init(). #10801
uv_tty_set_mode(&stream->uv.tty, UV_TTY_MODE_NORMAL);
diff --git a/src/nvim/event/stream.h b/src/nvim/event/stream.h
index 02e816b4be..33d2d6e775 100644
--- a/src/nvim/event/stream.h
+++ b/src/nvim/event/stream.h
@@ -6,8 +6,11 @@
#include <uv.h>
#include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
#include "nvim/rbuffer.h"
+struct stream;
+
typedef struct stream Stream;
/// Type of function called when the Stream buffer is filled with data
///
@@ -16,8 +19,7 @@ typedef struct stream Stream;
/// @param count Number of bytes that was read.
/// @param data User-defined data
/// @param eof If the stream reached EOF.
-typedef void (*stream_read_cb)(Stream *stream, RBuffer *buf, size_t count,
- void *data, bool eof);
+typedef void (*stream_read_cb)(Stream *stream, RBuffer *buf, size_t count, void *data, bool eof);
/// Type of function called when the Stream has information about a write
/// request.
@@ -35,7 +37,7 @@ struct stream {
uv_pipe_t pipe;
uv_tcp_t tcp;
uv_idle_t idle;
-#ifdef WIN32
+#ifdef MSWIN
uv_tty_t tty;
#endif
} uv;
diff --git a/src/nvim/event/time.h b/src/nvim/event/time.h
index a6de89ad6e..e84488fdd6 100644
--- a/src/nvim/event/time.h
+++ b/src/nvim/event/time.h
@@ -1,9 +1,13 @@
#ifndef NVIM_EVENT_TIME_H
#define NVIM_EVENT_TIME_H
+#include <stdbool.h>
#include <uv.h>
#include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
+
+struct time_watcher;
typedef struct time_watcher TimeWatcher;
typedef void (*time_cb)(TimeWatcher *watcher, void *data);
diff --git a/src/nvim/event/wstream.c b/src/nvim/event/wstream.c
index d81ffa5c15..65391ba5cf 100644
--- a/src/nvim/event/wstream.c
+++ b/src/nvim/event/wstream.c
@@ -3,15 +3,13 @@
#include <assert.h>
#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
#include <uv.h>
#include "nvim/event/loop.h"
+#include "nvim/event/stream.h"
#include "nvim/event/wstream.h"
-#include "nvim/log.h"
+#include "nvim/macros.h"
#include "nvim/memory.h"
-#include "nvim/vim.h"
#define DEFAULT_MAXMEM 1024 * 1024 * 2000
diff --git a/src/nvim/event/wstream.h b/src/nvim/event/wstream.h
index d599d29913..ef1311c619 100644
--- a/src/nvim/event/wstream.h
+++ b/src/nvim/event/wstream.h
@@ -2,12 +2,15 @@
#define NVIM_EVENT_WSTREAM_H
#include <stdbool.h>
+#include <stddef.h>
#include <stdint.h>
#include <uv.h>
#include "nvim/event/loop.h"
#include "nvim/event/stream.h"
+struct wbuffer;
+
typedef struct wbuffer WBuffer;
typedef void (*wbuffer_data_finalizer)(void *data);
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 06a372bb93..437a05f61d 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -1,25 +1,30 @@
// 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
-/*
- * ex_cmds.c: some functions for command line commands
- */
+// ex_cmds.c: some functions for command line commands
#include <assert.h>
+#include <ctype.h>
#include <float.h>
#include <inttypes.h>
#include <math.h>
#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
-#include "nvim/api/buffer.h"
-#include "nvim/api/private/defs.h"
+#include "auto/config.h"
+#include "klib/kvec.h"
#include "nvim/arglist.h"
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
#include "nvim/buffer_updates.h"
#include "nvim/change.h"
+#include "nvim/channel.h"
#include "nvim/charset.h"
#include "nvim/cmdhist.h"
#include "nvim/cursor.h"
@@ -29,22 +34,27 @@
#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_cmds2.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
#include "nvim/ex_getln.h"
#include "nvim/extmark.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
-#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/grid_defs.h"
#include "nvim/help.h"
-#include "nvim/highlight.h"
+#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/indent.h"
#include "nvim/input.h"
-#include "nvim/log.h"
+#include "nvim/lua/executor.h"
+#include "nvim/macros.h"
#include "nvim/main.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
@@ -57,21 +67,22 @@
#include "nvim/ops.h"
#include "nvim/option.h"
#include "nvim/optionstr.h"
+#include "nvim/os/fs_defs.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/os/shell.h"
#include "nvim/os/time.h"
-#include "nvim/os_unix.h"
#include "nvim/path.h"
#include "nvim/plines.h"
#include "nvim/profile.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
+#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/spell.h"
#include "nvim/strings.h"
-#include "nvim/syntax.h"
-#include "nvim/tag.h"
+#include "nvim/terminal.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/vim.h"
@@ -147,14 +158,14 @@ void do_ascii(const exarg_T *const eap)
char buf2[20];
buf2[0] = NUL;
- dig = (char *)get_digraph_for_char(cval);
+ dig = get_digraph_for_char(cval);
if (dig != NULL) {
- iobuff_len += (size_t)vim_snprintf((char *)IObuff + iobuff_len,
+ iobuff_len += (size_t)vim_snprintf(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 += (size_t)vim_snprintf((char *)IObuff + iobuff_len,
+ iobuff_len += (size_t)vim_snprintf(IObuff + iobuff_len,
sizeof(IObuff) - iobuff_len,
_("<%s>%s%s %d, Hex %02x, Octal %03o"),
transchar(c), buf1, buf2, cval, cval, cval);
@@ -193,18 +204,18 @@ 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 += (size_t)utf_char2bytes(c, (char *)IObuff + iobuff_len);
+ iobuff_len += (size_t)utf_char2bytes(c, IObuff + iobuff_len);
- dig = (char *)get_digraph_for_char(c);
+ dig = get_digraph_for_char(c);
if (dig != NULL) {
- iobuff_len += (size_t)vim_snprintf((char *)IObuff + iobuff_len,
+ iobuff_len += (size_t)vim_snprintf(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 += (size_t)vim_snprintf((char *)IObuff + iobuff_len,
+ iobuff_len += (size_t)vim_snprintf(IObuff + iobuff_len,
sizeof(IObuff) - iobuff_len,
(c < 0x10000
? _("> %d, Hex %04x, Octal %o")
@@ -217,10 +228,10 @@ void do_ascii(const exarg_T *const eap)
c = cc[ci++];
}
if (ci != MAX_MCO && c != 0) {
- xstrlcpy((char *)IObuff + iobuff_len, " ...", sizeof(IObuff) - iobuff_len);
+ xstrlcpy(IObuff + iobuff_len, " ...", sizeof(IObuff) - iobuff_len);
}
- msg((char *)IObuff);
+ msg(IObuff);
}
/// ":left", ":center" and ":right": align text.
@@ -249,11 +260,9 @@ void ex_align(exarg_T *eap)
indent = width;
}
} else {
- /*
- * if 'textwidth' set, use it
- * else if 'wrapmargin' set, use it
- * if invalid value, use 80
- */
+ // if 'textwidth' set, use it
+ // else if 'wrapmargin' set, use it
+ // if invalid value, use 80
if (width <= 0) {
width = (int)curbuf->b_p_tw;
}
@@ -270,13 +279,12 @@ void ex_align(exarg_T *eap)
}
for (curwin->w_cursor.lnum = eap->line1;
- curwin->w_cursor.lnum <= eap->line2; ++curwin->w_cursor.lnum) {
+ curwin->w_cursor.lnum <= eap->line2; curwin->w_cursor.lnum++) {
if (eap->cmdidx == CMD_left) { // left align
new_indent = indent;
} else {
- has_tab = FALSE; // avoid uninit warnings
- len = linelen(eap->cmdidx == CMD_right ? &has_tab
- : NULL) - get_indent();
+ has_tab = false; // avoid uninit warnings
+ len = linelen(eap->cmdidx == CMD_right ? &has_tab : NULL) - get_indent();
if (len <= 0) { // skip blank lines
continue;
@@ -287,18 +295,14 @@ void ex_align(exarg_T *eap)
} else {
new_indent = width - len; // right align
- /*
- * Make sure that embedded TABs don't make the text go too far
- * to the right.
- */
+ // Make sure that embedded TABs don't make the text go too far
+ // to the right.
if (has_tab) {
while (new_indent > 0) {
(void)set_indent(new_indent, 0);
if (linelen(NULL) <= width) {
- /*
- * Now try to move the line as much as possible to
- * the right. Stop when it moves too far.
- */
+ // Now try to move the line as much as possible to
+ // the right. Stop when it moves too far.
do {
(void)set_indent(++new_indent, 0);
} while (linelen(NULL) <= width);
@@ -330,7 +334,7 @@ static int linelen(int *has_tab)
// Get the line. If it's empty bail out early (could be the empty string
// for an unloaded buffer).
- line = (char *)get_cursor_line_ptr();
+ line = get_cursor_line_ptr();
if (*line == NUL) {
return 0;
}
@@ -338,12 +342,12 @@ static int linelen(int *has_tab)
first = skipwhite(line);
// find the character after the last non-blank character
- for (last = first + STRLEN(first);
+ for (last = first + strlen(first);
last > first && ascii_iswhite(last[-1]); last--) {}
char save = *last;
*last = NUL;
// Get line length.
- len = linetabsize((char_u *)line);
+ len = linetabsize(line);
// Check for embedded TAB.
if (has_tab != NULL) {
*has_tab = vim_strchr(first, TAB) != NULL;
@@ -387,7 +391,7 @@ static int string_compare(const void *s1, const void *s2) FUNC_ATTR_NONNULL_ALL
if (sort_lc) {
return strcoll((char *)s1, (char *)s2);
}
- return sort_ic ? STRICMP(s1, s2) : STRCMP(s1, s2);
+ return sort_ic ? STRICMP(s1, s2) : strcmp(s1, s2);
}
static int sort_compare(const void *s1, const void *s2)
@@ -404,7 +408,7 @@ static int sort_compare(const void *s1, const void *s2)
}
fast_breakcheck();
if (got_int) {
- sort_abort = TRUE;
+ sort_abort = true;
}
// When sorting numbers "start_col_nr" is the number, not the column
@@ -509,13 +513,12 @@ void ex_sort(exarg_T *eap)
} else if (*p == '"') {
// comment start
break;
- } else if (check_nextcmd((char_u *)p) != NULL) {
- eap->nextcmd = (char *)check_nextcmd((char_u *)p);
+ } else if (check_nextcmd(p) != NULL) {
+ eap->nextcmd = check_nextcmd(p);
break;
} else if (!ASCII_ISALPHA(*p) && regmatch.regprog == NULL) {
- s = (char *)skip_regexp((char_u *)p + 1, *p, true, NULL);
- if (*s != *p) {
- emsg(_(e_invalpat));
+ s = skip_regexp_err(p + 1, *p, true);
+ if (s == NULL) {
goto sortend;
}
*s = NUL;
@@ -525,7 +528,7 @@ void ex_sort(exarg_T *eap)
emsg(_(e_noprevre));
goto sortend;
}
- regmatch.regprog = vim_regcomp((char *)last_search_pat(), RE_MAGIC);
+ regmatch.regprog = vim_regcomp(last_search_pat(), RE_MAGIC);
} else {
regmatch.regprog = vim_regcomp(p + 1, RE_MAGIC);
}
@@ -557,8 +560,8 @@ void ex_sort(exarg_T *eap)
// 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 = (char *)ml_get(lnum);
- len = (int)STRLEN(s);
+ s = ml_get(lnum);
+ len = (int)strlen(s);
if (maxlen < len) {
maxlen = len;
}
@@ -567,10 +570,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] - (char_u *)s);
- end_col = (colnr_T)(regmatch.endp[0] - (char_u *)s);
+ start_col = (colnr_T)(regmatch.startp[0] - s);
+ end_col = (colnr_T)(regmatch.endp[0] - s);
} else {
- start_col = (colnr_T)(regmatch.endp[0] - (char_u *)s);
+ start_col = (colnr_T)(regmatch.endp[0] - s);
}
} else if (regmatch.regprog != NULL) {
end_col = 0;
@@ -586,11 +589,11 @@ void ex_sort(exarg_T *eap)
p = s + start_col;
if (sort_nr) {
if (sort_what & STR2NR_HEX) {
- s = (char *)skiptohex((char_u *)p);
+ s = skiptohex(p);
} else if (sort_what & STR2NR_BIN) {
s = (char *)skiptobin(p);
} else {
- s = (char *)skiptodigit((char_u *)p);
+ s = skiptodigit(p);
}
if (s > p && s[-1] == '-') {
s--; // include preceding negative sign
@@ -601,7 +604,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((char_u *)s, NULL, NULL, sort_what,
+ vim_str2nr(s, NULL, NULL, sort_what,
&nrs[lnum - eap->line1].st_u.num.value, NULL, 0, false);
}
} else {
@@ -658,8 +661,8 @@ void ex_sort(exarg_T *eap)
change_occurred = true;
}
- s = (char *)ml_get(get_lnum);
- size_t bytelen = STRLEN(s) + 1; // include EOL in bytelen
+ s = ml_get(get_lnum);
+ size_t bytelen = strlen(s) + 1; // include EOL in 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
@@ -678,7 +681,7 @@ void ex_sort(exarg_T *eap)
// delete the original lines if appending worked
if (i == count) {
- for (i = 0; i < count; ++i) {
+ for (i = 0; i < count; i++) {
ml_delete(eap->line1, false);
}
} else {
@@ -716,187 +719,6 @@ sortend:
}
}
-/// ":retab".
-void ex_retab(exarg_T *eap)
-{
- linenr_T lnum;
- bool got_tab = false;
- long num_spaces = 0;
- long num_tabs;
- long len;
- long col;
- long vcol;
- long start_col = 0; // For start of white-space string
- long start_vcol = 0; // For start of white-space string
- long old_len;
- 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 *new_ts_str; // string value of tab argument
-
- int save_list;
- linenr_T first_line = 0; // first changed line
- linenr_T last_line = 0; // last changed line
-
- save_list = curwin->w_p_list;
- curwin->w_p_list = 0; // don't want list mode here
-
- new_ts_str = eap->arg;
- if (!tabstop_set((char_u *)eap->arg, &new_vts_array)) {
- return;
- }
- while (ascii_isdigit(*(eap->arg)) || *(eap->arg) == ',') {
- (eap->arg)++;
- }
-
- // This ensures that either new_vts_array and new_ts_str are freshly
- // allocated, or new_vts_array points to an existing array and new_ts_str
- // is null.
- if (new_vts_array == NULL) {
- new_vts_array = curbuf->b_p_vts_array;
- new_ts_str = NULL;
- } else {
- 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 = (char *)ml_get(lnum);
- col = 0;
- vcol = 0;
- did_undo = false;
- for (;;) {
- if (ascii_iswhite(ptr[col])) {
- if (!got_tab && num_spaces == 0) {
- // First consecutive white-space
- start_vcol = vcol;
- start_col = col;
- }
- if (ptr[col] == ' ') {
- num_spaces++;
- } else {
- got_tab = true;
- }
- } else {
- if (got_tab || (eap->forceit && num_spaces > 1)) {
- // Retabulate this string of white-space
-
- // len is virtual length of white string
- len = num_spaces = vcol - start_vcol;
- num_tabs = 0;
- if (!curbuf->b_p_et) {
- int t, s;
-
- tabstop_fromto((colnr_T)start_vcol, (colnr_T)vcol,
- curbuf->b_p_ts, new_vts_array, &t, &s);
- num_tabs = t;
- num_spaces = s;
- }
- if (curbuf->b_p_et || got_tab
- || (num_spaces + num_tabs < len)) {
- if (did_undo == false) {
- did_undo = true;
- if (u_save((linenr_T)(lnum - 1),
- (linenr_T)(lnum + 1)) == FAIL) {
- new_line = NULL; // flag out-of-memory
- break;
- }
- }
-
- // len is actual number of white characters used
- len = num_spaces + num_tabs;
- old_len = (long)STRLEN(ptr);
- 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);
- }
- memmove(new_line + start_col + len,
- ptr + col, (size_t)(old_len - col + 1));
- ptr = new_line + start_col;
- for (col = 0; col < len; col++) {
- ptr[col] = (col < num_tabs) ? '\t' : ' ';
- }
- if (ml_replace(lnum, new_line, false) == OK) {
- // "new_line" may have been copied
- 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);
- }
- if (first_line == 0) {
- first_line = lnum;
- }
- last_line = lnum;
- ptr = new_line;
- col = start_col + len;
- }
- }
- got_tab = false;
- num_spaces = 0;
- }
- if (ptr[col] == NUL) {
- break;
- }
- 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
- break;
- }
- line_breakcheck();
- }
- if (got_int) {
- emsg(_(e_interr));
- }
-
- // If a single value was given then it can be considered equal to
- // either the value of 'tabstop' or the value of 'vartabstop'.
- if (tabstop_count(curbuf->b_p_vts_array) == 0
- && tabstop_count(new_vts_array) == 1
- && curbuf->b_p_ts == tabstop_first(new_vts_array)) {
- // not changed
- } else if (tabstop_count(curbuf->b_p_vts_array) > 0
- && tabstop_eq(curbuf->b_p_vts_array, new_vts_array)) {
- // not changed
- } else {
- redraw_curbuf_later(NOT_VALID);
- }
- if (first_line != 0) {
- changed_lines(first_line, 0, last_line + 1, 0L, true);
- }
-
- curwin->w_p_list = save_list; // restore 'list'
-
- if (new_ts_str != NULL) { // set the new tabstop
- // If 'vartabstop' is in use or if the value given to retab has more
- // than one tabstop then update 'vartabstop'.
- 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);
- curbuf->b_p_vts_array = new_vts_array;
- xfree(old_vts_ary);
- } else {
- // 'vartabstop' wasn't in use and a single value was given to
- // retab then update 'tabstop'.
- curbuf->b_p_ts = tabstop_first(new_vts_array);
- xfree(new_vts_array);
- }
- xfree(new_ts_str);
- }
- coladvance(curwin->w_curswant);
-
- u_clearline();
-}
-
/// :move command - move lines line1-line2 to line dest
///
/// @return FAIL for failure, OK otherwise
@@ -933,15 +755,13 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
num_lines = line2 - line1 + 1;
- /*
- * First we copy the old text to its new location -- webb
- * Also copy the flag that ":global" command uses.
- */
+ // First we copy the old text to its new location -- webb
+ // Also copy the flag that ":global" command uses.
if (u_save(dest, dest + 1) == FAIL) {
return FAIL;
}
for (extra = 0, l = line1; l <= line2; l++) {
- str = (char *)vim_strsave(ml_get(l + extra));
+ str = xstrdup(ml_get(l + extra));
ml_append(dest + l - line1, str, (colnr_T)0, false);
xfree(str);
if (dest < line1) {
@@ -949,21 +769,19 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
}
}
- /*
- * Now we must be careful adjusting our marks so that we don't overlap our
- * mark_adjust() calls.
- *
- * We adjust the marks within the old text so that they refer to the
- * last lines of the file (temporarily), because we know no other marks
- * will be set there since these line numbers did not exist until we added
- * our new lines.
- *
- * Then we adjust the marks on lines between the old and new text positions
- * (either forwards or backwards).
- *
- * And Finally we adjust the marks we put at the end of the file back to
- * their final destination at the new text position -- webb
- */
+ // Now we must be careful adjusting our marks so that we don't overlap our
+ // mark_adjust() calls.
+ //
+ // We adjust the marks within the old text so that they refer to the
+ // last lines of the file (temporarily), because we know no other marks
+ // will be set there since these line numbers did not exist until we added
+ // our new lines.
+ //
+ // Then we adjust the marks on lines between the old and new text positions
+ // (either forwards or backwards).
+ //
+ // And Finally we adjust the marks we put at the end of the file back to
+ // their final destination at the new text position -- webb
last_line = curbuf->b_ml.ml_line_count;
mark_adjust_nofold(line1, line2, last_line - line2, 0L, kExtmarkNOOP);
@@ -1011,9 +829,7 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
// send update regarding the new lines that were added
buf_updates_send_changes(curbuf, dest + 1, num_lines, 0);
- /*
- * Now we delete the original text -- webb
- */
+ // Now we delete the original text -- webb
if (u_save(line1 + extra - 1, line2 + extra + 1) == FAIL) {
return FAIL;
}
@@ -1022,7 +838,9 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
ml_delete(line1 + extra, true);
}
if (!global_busy && num_lines > p_report) {
- smsg(NGETTEXT("1 line moved", "%" PRId64 " lines moved", num_lines), (int64_t)num_lines);
+ smsg(NGETTEXT("%" PRId64 " line moved",
+ "%" PRId64 " lines moved", num_lines),
+ (int64_t)num_lines);
}
extmark_move_region(curbuf, line1 - 1, 0, start_byte,
@@ -1030,9 +848,7 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
dest + line_off, 0, dest_byte + byte_off,
kExtmarkUndo);
- /*
- * Leave the cursor on the last of the moved lines.
- */
+ // Leave the cursor on the last of the moved lines.
if (dest >= line1) {
curwin->w_cursor.lnum = dest;
} else {
@@ -1069,26 +885,24 @@ void ex_copy(linenr_T line1, linenr_T line2, linenr_T n)
curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
}
- /*
- * there are three situations:
- * 1. destination is above line1
- * 2. destination is between line1 and line2
- * 3. destination is below line2
- *
- * n = destination (when starting)
- * curwin->w_cursor.lnum = destination (while copying)
- * line1 = start of source (while copying)
- * line2 = end of source (while copying)
- */
+ // there are three situations:
+ // 1. destination is above line1
+ // 2. destination is between line1 and line2
+ // 3. destination is below line2
+ //
+ // n = destination (when starting)
+ // curwin->w_cursor.lnum = destination (while copying)
+ // line1 = start of source (while copying)
+ // line2 = end of source (while copying)
if (u_save(n, n + 1) == FAIL) {
return;
}
curwin->w_cursor.lnum = n;
while (line1 <= line2) {
- // need to use vim_strsave() because the line will be unlocked within
+ // need to use xstrdup() because the line will be unlocked within
// ml_append()
- p = (char *)vim_strsave(ml_get(line1));
+ p = xstrdup(ml_get(line1));
ml_append(curwin->w_cursor.lnum, p, (colnr_T)0, false);
xfree(p);
@@ -1103,7 +917,7 @@ void ex_copy(linenr_T line1, linenr_T line2, linenr_T n)
if (curwin->w_cursor.lnum < line2) {
line2++;
}
- ++curwin->w_cursor.lnum;
+ curwin->w_cursor.lnum++;
}
appended_lines_mark(n, count);
@@ -1142,29 +956,28 @@ void do_bang(int addr_count, exarg_T *eap, bool forceit, bool do_in, bool do_out
int scroll_save = msg_scroll;
//
- // Disallow shell commands from .exrc and .vimrc in current directory for
- // security reasons.
+ // Disallow shell commands in secure mode
//
if (check_secure()) {
return;
}
if (addr_count == 0) { // :!
- msg_scroll = FALSE; // don't scroll here
+ msg_scroll = false; // don't scroll here
autowrite_all();
msg_scroll = scroll_save;
}
- /*
- * Try to find an embedded bang, like in :!<cmd> ! [args]
- * (:!! is indicated by the 'forceit' variable)
- */
+ // Try to find an embedded bang, like in ":!<cmd> ! [args]"
+ // ":!!" is indicated by the 'forceit' variable.
bool ins_prevcmd = forceit;
- trailarg = arg;
+
+ // Skip leading white space to avoid a strange error with some shells.
+ trailarg = skipwhite(arg);
do {
- len = STRLEN(trailarg) + 1;
+ len = strlen(trailarg) + 1;
if (newcmd != NULL) {
- len += STRLEN(newcmd);
+ len += strlen(newcmd);
}
if (ins_prevcmd) {
if (prevcmd == NULL) {
@@ -1172,7 +985,7 @@ void do_bang(int addr_count, exarg_T *eap, bool forceit, bool do_in, bool do_out
xfree(newcmd);
return;
}
- len += STRLEN(prevcmd);
+ len += strlen(prevcmd);
}
t = xmalloc(len);
*t = NUL;
@@ -1182,15 +995,13 @@ void do_bang(int addr_count, exarg_T *eap, bool forceit, bool do_in, bool do_out
if (ins_prevcmd) {
STRCAT(t, prevcmd);
}
- p = t + STRLEN(t);
+ p = t + strlen(t);
STRCAT(t, trailarg);
xfree(newcmd);
newcmd = t;
- /*
- * Scan the rest of the argument for '!', which is replaced by the
- * previous command. "\!" is replaced by "!" (this is vi compatible).
- */
+ // Scan the rest of the argument for '!', which is replaced by the
+ // previous command. "\!" is replaced by "!" (this is vi compatible).
trailarg = NULL;
while (*p) {
if (*p == '!') {
@@ -1214,18 +1025,16 @@ 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 *cmd = (char *)vim_strsave_escaped((char_u *)prevcmd, (char_u *)"%#");
+ char *cmd = vim_strsave_escaped(prevcmd, "%#");
AppendToRedobuffLit(cmd, -1);
xfree(cmd);
AppendToRedobuff("\n");
bangredo = false;
}
- /*
- * Add quotes around the command, for shells that need them.
- */
+ // Add quotes around the command, for shells that need them.
if (*p_shq != NUL) {
- newcmd = xmalloc(STRLEN(prevcmd) + 2 * STRLEN(p_shq) + 1);
+ newcmd = xmalloc(strlen(prevcmd) + 2 * strlen(p_shq) + 1);
STRCPY(newcmd, p_shq);
STRCAT(newcmd, prevcmd);
STRCAT(newcmd, p_shq);
@@ -1298,18 +1107,16 @@ static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char *cmd, b
changed_line_abv_curs();
invalidate_botline();
- /*
- * When using temp files:
- * 1. * Form temp file names
- * 2. * Write the lines to a temp file
- * 3. Run the filter command on the temp file
- * 4. * Read the output of the command into the buffer
- * 5. * Delete the original lines to be filtered
- * 6. * Remove the temp files
- *
- * When writing the input with a pipe or when catching the output with a
- * pipe only need to do 3.
- */
+ // When using temp files:
+ // 1. * Form temp file names
+ // 2. * Write the lines to a temp file
+ // 3. Run the filter command on the temp file
+ // 4. * Read the output of the command into the buffer
+ // 5. * Delete the original lines to be filtered
+ // 6. * Remove the temp files
+ //
+ // When writing the input with a pipe or when catching the output with a
+ // pipe only need to do 3.
if (do_out) {
shell_flags |= kShellOptDoOut;
@@ -1331,23 +1138,22 @@ static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char *cmd, b
curbuf->b_op_start.lnum = line1;
curbuf->b_op_end.lnum = line2;
curwin->w_cursor.lnum = line2;
- } else if ((do_in && (itmp = (char *)vim_tempname()) == NULL)
- || (do_out && (otmp = (char *)vim_tempname()) == NULL)) {
+ } else if ((do_in && (itmp = vim_tempname()) == NULL)
+ || (do_out && (otmp = vim_tempname()) == NULL)) {
emsg(_(e_notmp));
goto filterend;
}
- /*
- * The writing and reading of temp files will not be shown.
- * Vi also doesn't do this and the messages are not very informative.
- */
+ // 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
if (itmp != NULL && buf_write(curbuf, itmp, NULL, line1, line2, eap,
false, false, false, true) == FAIL) {
msg_putchar('\n'); // Keep message from buf_write().
no_wait_return--;
if (!aborting()) {
- semsg(_("E482: Can't create file %s"), itmp); // Will call wait_return.
+ // will call wait_return()
+ semsg(_("E482: Can't create file %s"), itmp);
}
goto filterend;
}
@@ -1368,22 +1174,22 @@ static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char *cmd, b
xfree(cmd_buf);
goto error;
}
- redraw_curbuf_later(VALID);
+ redraw_curbuf_later(UPD_VALID);
}
read_linecount = curbuf->b_ml.ml_line_count;
// Pass on the kShellOptDoOut flag when the output is being redirected.
- call_shell((char_u *)cmd_buf, (ShellOpts)(kShellOptFilter | shell_flags), NULL);
+ call_shell(cmd_buf, (ShellOpts)(kShellOptFilter | shell_flags), NULL);
xfree(cmd_buf);
- did_check_timestamps = FALSE;
- need_check_timestamps = TRUE;
+ did_check_timestamps = false;
+ need_check_timestamps = true;
// When interrupting the shell command, it may still have produced some
// useful output. Reset got_int here, so that readfile() won't cancel
// reading.
os_breakcheck();
- got_int = FALSE;
+ got_int = false;
if (do_out) {
if (otmp != NULL) {
@@ -1427,10 +1233,8 @@ static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char *cmd, b
}
}
- /*
- * Put cursor on first filtered line for ":range!cmd".
- * Adjust '[ and '] (set by buf_write()).
- */
+ // Put cursor on first filtered line for ":range!cmd".
+ // Adjust '[ and '] (set by buf_write()).
curwin->w_cursor.lnum = line1;
del_lines(linecount, true);
curbuf->b_op_start.lnum -= linecount; // adjust '[
@@ -1439,9 +1243,7 @@ static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char *cmd, b
// for next write
foldUpdate(curwin, curbuf->b_op_start.lnum, curbuf->b_op_end.lnum);
} else {
- /*
- * Put cursor on last new line for ":r !cmd".
- */
+ // Put cursor on last new line for ":r !cmd".
linecount = curbuf->b_op_end.lnum - curbuf->b_op_start.lnum + 1;
curwin->w_cursor.lnum = curbuf->b_op_end.lnum;
}
@@ -1496,17 +1298,14 @@ filterend:
/// @param flags may be SHELL_DOOUT when output is redirected
void do_shell(char *cmd, int flags)
{
- // Disallow shell commands from .exrc and .vimrc in current directory for
- // security reasons.
+ // Disallow shell commands in secure mode
if (check_secure()) {
msg_end();
return;
}
- /*
- * For autocommands we want to get the output on the current screen, to
- * avoid having to type return below.
- */
+ // For autocommands we want to get the output on the current screen, to
+ // avoid having to type return below.
msg_putchar('\r'); // put cursor at start of line
msg_putchar('\n'); // may shift screen one line up
@@ -1525,7 +1324,7 @@ void do_shell(char *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((char_u *)cmd, (ShellOpts)flags, NULL);
+ (void)call_shell(cmd, (ShellOpts)flags, NULL);
msg_didout = true;
did_check_timestamps = false;
need_check_timestamps = true;
@@ -1545,11 +1344,11 @@ static char *find_pipe(const char *cmd)
for (const char *p = cmd; *p != NUL; p++) {
if (!inquote && *p == '|') {
- return p;
+ return (char *)p;
}
if (*p == '"') {
inquote = !inquote;
- } else if (rem_backslash((const char_u *)p)) {
+ } else if (rem_backslash(p)) {
p++;
}
}
@@ -1568,95 +1367,84 @@ char *make_filter_cmd(char *cmd, char *itmp, char *otmp)
{
bool is_fish_shell =
#if defined(UNIX)
- STRNCMP(invocation_path_tail(p_sh, NULL), "fish", 4) == 0;
+ strncmp((char *)invocation_path_tail(p_sh, NULL), "fish", 4) == 0;
#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;
+ bool is_pwsh = strncmp((char *)invocation_path_tail(p_sh, NULL), "pwsh", 4) == 0
+ || strncmp((char *)invocation_path_tail(p_sh, NULL), "powershell",
+ 10) == 0;
- size_t len = STRLEN(cmd) + 1; // At least enough space for cmd + NULL.
+ size_t len = strlen(cmd) + 1; // At least enough space for cmd + NULL.
- len += is_fish_shell ? sizeof("begin; " "; end") - 1
- : is_pwsh ? sizeof("Start-Process ")
- : sizeof("(" ")") - 1;
+ len += is_fish_shell ? sizeof("begin; " "; end") - 1
+ : !is_pwsh ? sizeof("(" ")") - 1
+ : 0;
if (itmp != NULL) {
- len += is_pwsh ? STRLEN(itmp) + sizeof(" -RedirectStandardInput ")
- : STRLEN(itmp) + sizeof(" { " " < " " } ") - 1;
+ len += is_pwsh ? strlen(itmp) + sizeof("& { Get-Content " " | & " " }") - 1 + 6 // +6: #20530
+ : strlen(itmp) + sizeof(" { " " < " " } ") - 1;
}
if (otmp != NULL) {
- len += STRLEN(otmp) + STRLEN(p_srr) + 2; // two extra spaces (" "),
+ len += strlen(otmp) + strlen(p_srr) + 2; // two extra spaces (" "),
}
- const char *const cmd_args = strchr(cmd, ' ');
- len += (is_pwsh && cmd_args)
- ? STRLEN(" -ArgumentList ") + 2 // two extra quotes
- : 0;
-
char *const buf = xmalloc(len);
if (is_pwsh) {
- xstrlcpy(buf, "Start-Process ", len);
- if (cmd_args == NULL) {
- xstrlcat(buf, cmd, len);
+ if (itmp != NULL) {
+ xstrlcpy(buf, "& { Get-Content ", len - 1); // FIXME: should we add "-Encoding utf8"?
+ xstrlcat(buf, (const char *)itmp, len - 1);
+ xstrlcat(buf, " | & ", len - 1); // FIXME: add `&` ourself or leave to user?
+ xstrlcat(buf, cmd, len - 1);
+ xstrlcat(buf, " }", len - 1);
} else {
- xstrlcpy(buf + STRLEN(buf), cmd, (size_t)(cmd_args - cmd + 1));
- xstrlcat(buf, " -ArgumentList \"", len);
- xstrlcat(buf, cmd_args + 1, len); // +1 to skip the leading space.
- xstrlcat(buf, "\"", len);
+ xstrlcpy(buf, cmd, len - 1);
}
+ } else {
#if defined(UNIX)
// Put delimiters around the command (for concatenated commands) when
// redirecting input and/or output.
- } else if (itmp != NULL || otmp != NULL) {
- char *fmt = is_fish_shell ? "begin; %s; end"
- : "(%s)";
- vim_snprintf(buf, len, fmt, cmd);
-#endif
- // For shells that don't understand braces around commands, at least allow
- // the use of commands in a pipe.
- } else {
- xstrlcpy(buf, cmd, len);
- }
-
-#if defined(UNIX)
- if (itmp != NULL) {
- if (is_pwsh) {
- xstrlcat(buf, " -RedirectStandardInput ", len - 1);
+ if (itmp != NULL || otmp != NULL) {
+ char *fmt = is_fish_shell ? "begin; %s; end"
+ : "(%s)";
+ vim_snprintf(buf, len, fmt, cmd);
} else {
+ xstrlcpy(buf, cmd, len);
+ }
+
+ if (itmp != NULL) {
xstrlcat(buf, " < ", len - 1);
+ xstrlcat(buf, (const char *)itmp, len - 1);
}
- xstrlcat(buf, itmp, len - 1);
- }
#else
- 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
- // redirection would be inside the quotes.
- if (*p_shq == NUL) {
- char *const p = find_pipe(buf);
- if (p != NULL) {
- *p = NUL;
+ // For shells that don't understand braces around commands, at least allow
+ // the use of commands in a pipe.
+ 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
+ // redirection would be inside the quotes.
+ if (*p_shq == NUL) {
+ char *const p = find_pipe(buf);
+ if (p != NULL) {
+ *p = NUL;
+ }
}
- }
- 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(cmd);
- if (p != NULL) {
- xstrlcat(buf, " ", len - 1); // Insert a space before the '|' for DOS
- xstrlcat(buf, p, len - 1);
+ xstrlcat(buf, (const char *)itmp, len);
+ if (*p_shq == NUL) {
+ const char *const p = find_pipe((const char *)cmd);
+ if (p != NULL) {
+ xstrlcat(buf, " ", len - 1); // Insert a space before the '|' for DOS
+ xstrlcat(buf, p, len - 1);
+ }
}
}
- }
#endif
+ }
if (otmp != NULL) {
- append_redir(buf, len, (char *)p_srr, otmp);
+ append_redir(buf, len, p_srr, otmp);
}
return buf;
}
@@ -1714,8 +1502,8 @@ void print_line(linenr_T lnum, int use_number, int list)
}
msg_start();
- silent_mode = FALSE;
- info_message = true; // use mch_msg(), not mch_errmsg()
+ silent_mode = false;
+ info_message = true; // use os_msg(), not os_errmsg()
print_line_no_prefix(lnum, use_number, list);
if (save_silent) {
msg_putchar('\n');
@@ -1739,13 +1527,11 @@ int rename_buffer(char *new_fname)
if (aborting()) { // autocmds may abort script processing
return FAIL;
}
- /*
- * The name of the current buffer will be changed.
- * A new (unlisted) buffer entry needs to be made to hold the old file
- * name, which will become the alternate file name.
- * But don't set the alternate file name if the buffer didn't have a
- * name.
- */
+ // The name of the current buffer will be changed.
+ // A new (unlisted) buffer entry needs to be made to hold the old file
+ // name, which will become the alternate file name.
+ // But don't set the alternate file name if the buffer didn't have a
+ // name.
fname = curbuf->b_ffname;
sfname = curbuf->b_sfname;
xfname = curbuf->b_fname;
@@ -1821,12 +1607,23 @@ void ex_write(exarg_T *eap)
}
}
-/// write current buffer to file 'eap->arg'
-/// if 'eap->append' is TRUE, append to the file
+#ifdef UNIX
+static int check_writable(const char *fname)
+{
+ if (os_nodetype(fname) == NODE_OTHER) {
+ semsg(_("E503: \"%s\" is not a file or writable device"), fname);
+ return FAIL;
+ }
+ return OK;
+}
+#endif
+
+/// Write current buffer to file "eap->arg".
+/// If "eap->append" is true, append to the file.
///
-/// if *eap->arg == NUL write to current file
+/// If "*eap->arg == NUL" write to current file.
///
-/// @return FAIL for failure, OK otherwise
+/// @return FAIL for failure, OK otherwise.
int do_write(exarg_T *eap)
{
int other;
@@ -1847,7 +1644,7 @@ int do_write(exarg_T *eap)
emsg(_(e_argreq));
goto theend;
}
- other = FALSE;
+ other = false;
} else {
fname = ffname;
free_fname = fix_fname(ffname);
@@ -1859,9 +1656,7 @@ int do_write(exarg_T *eap)
other = otherfile(ffname);
}
- /*
- * If we have a new file, put its name in the list of alternate file names.
- */
+ // If we have a new file, put its name in the list of alternate file names.
if (other) {
if (vim_strchr(p_cpo, CPO_ALTWRITE) != NULL
|| eap->cmdidx == CMD_saveas) {
@@ -1882,6 +1677,9 @@ int do_write(exarg_T *eap)
// "nofile" and "nowrite" buffers cannot be written implicitly either.
if (!other && (bt_dontwrite_msg(curbuf)
|| check_fname() == FAIL
+#ifdef UNIX
+ || check_writable(curbuf->b_ffname) == FAIL
+#endif
|| check_readonly(&eap->forceit, curbuf))) {
goto theend;
}
@@ -1897,10 +1695,10 @@ int do_write(exarg_T *eap)
&& !p_wa) {
if (p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) {
if (vim_dialog_yesno(VIM_QUESTION, NULL,
- (char_u *)_("Write partial file?"), 2) != VIM_YES) {
+ _("Write partial file?"), 2) != VIM_YES) {
goto theend;
}
- eap->forceit = TRUE;
+ eap->forceit = true;
} else {
emsg(_("E140: Use ! to write partial buffer"));
goto theend;
@@ -1937,8 +1735,8 @@ int do_write(exarg_T *eap)
apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, false, curbuf);
apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, false, alt_buf);
if (!alt_buf->b_p_bl) {
- alt_buf->b_p_bl = TRUE;
- apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, alt_buf);
+ alt_buf->b_p_bl = true;
+ apply_autocmds(EVENT_BUFADD, NULL, NULL, false, alt_buf);
}
if (curbuf != was_curbuf || aborting()) {
// buffer changed, don't write the file
@@ -1959,6 +1757,13 @@ int do_write(exarg_T *eap)
fname = curbuf->b_sfname;
}
+ if (eap->mkdir_p) {
+ if (os_file_mkdir(fname, 0755) < 0) {
+ retval = FAIL;
+ goto theend;
+ }
+ }
+
name_was_missing = curbuf->b_ffname == NULL;
retval = buf_write(curbuf, ffname, fname, eap->line1, eap->line2,
eap, eap->append, eap->forceit, true, false);
@@ -1966,7 +1771,7 @@ int do_write(exarg_T *eap)
// After ":saveas fname" reset 'readonly'.
if (eap->cmdidx == CMD_saveas) {
if (retval == OK) {
- curbuf->b_p_ro = FALSE;
+ curbuf->b_p_ro = false;
redraw_tabline = true;
}
}
@@ -1996,17 +1801,22 @@ int check_overwrite(exarg_T *eap, buf_T *buf, char *fname, char *ffname, int oth
{
// Write to another file or b_flags set or not writing the whole file:
// overwriting only allowed with '!'
+ // If "other" is false and bt_nofilename(buf) is true, this must be
+ // writing an "acwrite" buffer to the same file as its b_ffname, and
+ // buf_write() will only allow writing with BufWriteCmd autocommands,
+ // so there is no need for an overwrite check.
if ((other
- || (buf->b_flags & BF_NOTEDITED)
- || ((buf->b_flags & BF_NEW)
- && vim_strchr(p_cpo, CPO_OVERNEW) == NULL)
- || (buf->b_flags & BF_READERR))
+ || (!bt_nofilename(buf)
+ && ((buf->b_flags & BF_NOTEDITED)
+ || ((buf->b_flags & BF_NEW)
+ && vim_strchr(p_cpo, CPO_OVERNEW) == NULL)
+ || (buf->b_flags & BF_READERR))))
&& !p_wa
- && os_path_exists((char_u *)ffname)) {
+ && os_path_exists(ffname)) {
if (!eap->forceit && !eap->append) {
#ifdef UNIX
// It is possible to open a directory on Unix.
- if (os_isdir((char_u *)ffname)) {
+ if (os_isdir(ffname)) {
semsg(_(e_isadir2), ffname);
return FAIL;
}
@@ -2015,10 +1825,10 @@ int check_overwrite(exarg_T *eap, buf_T *buf, char *fname, char *ffname, int oth
char buff[DIALOG_MSG_SIZE];
dialog_msg((char *)buff, _("Overwrite existing file \"%s\"?"), fname);
- if (vim_dialog_yesno(VIM_QUESTION, NULL, (char_u *)buff, 2) != VIM_YES) {
+ if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 2) != VIM_YES) {
return FAIL;
}
- eap->forceit = TRUE;
+ eap->forceit = true;
} else {
emsg(_(e_exists));
return FAIL;
@@ -2041,24 +1851,24 @@ int check_overwrite(exarg_T *eap, buf_T *buf, char *fname, char *ffname, int oth
STRCPY(dir, ".");
} else {
dir = xmalloc(MAXPATHL);
- p = (char *)p_dir;
+ p = p_dir;
copy_option_part(&p, dir, MAXPATHL, ",");
}
- swapname = (char *)makeswapname((char_u *)fname, (char_u *)ffname, curbuf, (char_u *)dir);
+ swapname = makeswapname(fname, ffname, curbuf, dir);
xfree(dir);
- if (os_path_exists((char_u *)swapname)) {
+ if (os_path_exists(swapname)) {
if (p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) {
char buff[DIALOG_MSG_SIZE];
dialog_msg((char *)buff,
_("Swap file \"%s\" exists, overwrite anyway?"),
swapname);
- if (vim_dialog_yesno(VIM_QUESTION, NULL, (char_u *)buff, 2)
+ if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 2)
!= VIM_YES) {
xfree(swapname);
return FAIL;
}
- eap->forceit = TRUE;
+ eap->forceit = true;
} else {
semsg(_("E768: Swap file exists: %s (:silent! overrides)"),
swapname);
@@ -2108,13 +1918,11 @@ void do_wqall(exarg_T *eap)
} else if (!bufIsChanged(buf) || bt_dontwrite(buf)) {
continue;
}
- /*
- * Check if there is a reason the buffer cannot be written:
- * 1. if the 'write' option is set
- * 2. if there is no file name (even after browsing)
- * 3. if the 'readonly' is set (even after a dialog)
- * 4. if overwriting is allowed (even after a dialog)
- */
+ // Check if there is a reason the buffer cannot be written:
+ // 1. if the 'write' option is set
+ // 2. if there is no file name (even after browsing)
+ // 3. if the 'readonly' is set (even after a dialog)
+ // 4. if overwriting is allowed (even after a dialog)
if (not_writing()) {
error++;
break;
@@ -2159,14 +1967,14 @@ bool not_writing(void)
}
/// 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
+/// 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((char_u *)buf->b_ffname)
+ || (os_path_exists(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];
@@ -2182,23 +1990,22 @@ static int check_readonly(int *forceit, buf_T *buf)
buf->b_fname);
}
- if (vim_dialog_yesno(VIM_QUESTION, NULL, (char_u *)buff, 2) == VIM_YES) {
+ if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 2) == VIM_YES) {
// Set forceit, to force the writing of a readonly file
- *forceit = TRUE;
- return FALSE;
- } else {
- return TRUE;
+ *forceit = true;
+ return false;
}
+ return true;
} else if (buf->b_p_ro) {
emsg(_(e_readonly));
} else {
semsg(_("E505: \"%s\" is read-only (add ! to override)"),
buf->b_fname);
}
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
/// Try to abandon the current file and edit a new or existing file.
@@ -2292,7 +2099,7 @@ theend:
/// ECMD_LASTL: use last position in loaded file
/// ECMD_LAST: use last position in all files
/// ECMD_ONE: use first line
-/// @param flags ECMD_HIDE: if TRUE don't free the current buffer
+/// @param flags ECMD_HIDE: if true don't free the current buffer
/// ECMD_SET_HELP: set b_help flag of (new) buffer before
/// opening file
/// ECMD_OLDBUF: use existing buffer if it exists
@@ -2310,7 +2117,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
win_T *oldwin)
{
bool other_file; // true if editing another file
- int oldbuf; // TRUE if using existing buffer
+ int oldbuf; // true if using existing buffer
bool auto_buf = false; // true if autocommands brought us
// into the buffer unexpectedly
char *new_name = NULL;
@@ -2386,7 +2193,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
// If the file was changed we may not be allowed to abandon it:
// - if we are going to re-edit the same file
- // - or if we are the only window on this file and if ECMD_HIDE is FALSE
+ // - or if we are the only window on this file and if ECMD_HIDE is false
if (((!other_file && !(flags & ECMD_OLDBUF))
|| (curbuf->b_nwindows == 1
&& !(flags & (ECMD_HIDE | ECMD_ADDBUF | ECMD_ALTBUF))))
@@ -2400,16 +2207,14 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
goto theend;
}
- /*
- * End Visual mode before switching to another buffer, so the text can be
- * copied into the GUI selection buffer.
- */
+ // End Visual mode before switching to another buffer, so the text can be
+ // copied into the GUI selection buffer.
reset_VIsual();
if ((command != NULL || newlnum > (linenr_T)0)
&& *get_vim_var_str(VV_SWAPCOMMAND) == NUL) {
// Set v:swapcommand for the SwapExists autocommands.
- const size_t len = (command != NULL) ? STRLEN(command) + 3 : 30;
+ const size_t len = (command != NULL) ? strlen(command) + 3 : 30;
char *const p = xmalloc(len);
if (command != NULL) {
vim_snprintf(p, len, ":%s\r", command);
@@ -2421,10 +2226,8 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
xfree(p);
}
- /*
- * If we are starting to edit another file, open a (new) buffer.
- * Otherwise we re-use the current buffer.
- */
+ // If we are starting to edit another file, open a (new) buffer.
+ // Otherwise we re-use the current buffer.
if (other_file) {
const int prev_alt_fnum = curwin->w_alt_fnum;
@@ -2502,12 +2305,10 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
solcol = pos->col;
}
- /*
- * Make the (new) buffer the one used by the current window.
- * If the old buffer becomes unused, free it if ECMD_HIDE is FALSE.
- * If the current buffer was empty and has no file name, curbuf
- * is returned by buflist_new(), nothing to do here.
- */
+ // Make the (new) buffer the one used by the current window.
+ // If the old buffer becomes unused, free it if ECMD_HIDE is false.
+ // If the current buffer was empty and has no file name, curbuf
+ // is returned by buflist_new(), nothing to do here.
if (buf != curbuf) {
const int save_cmdwin_type = cmdwin_type;
@@ -2601,11 +2402,11 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
curwin->w_buffer = buf;
curbuf = buf;
- ++curbuf->b_nwindows;
+ curbuf->b_nwindows++;
// Set 'fileformat', 'binary' and 'fenc' when forced.
if (!oldbuf && eap != NULL) {
- set_file_options(TRUE, eap);
+ set_file_options(true, eap);
set_forced_fenc(eap);
}
}
@@ -2641,7 +2442,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
} else if (!curbuf->b_help) {
// Don't make a buffer listed if it's a help buffer. Useful when using
// CTRL-O to go back to a help file.
- set_buflisted(TRUE);
+ set_buflisted(true);
}
// If autocommands change buffers under our fingers, forget about
@@ -2658,13 +2459,11 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
// highlighting to work in the other file.
did_filetype = false;
- /*
- * other_file oldbuf
- * FALSE FALSE re-edit same file, buffer is re-used
- * FALSE TRUE re-edit same file, nothing changes
- * TRUE FALSE start editing new file, new buffer
- * TRUE TRUE start editing in existing buffer (nothing to do)
- */
+ // other_file oldbuf
+ // false false re-edit same file, buffer is re-used
+ // false true re-edit same file, nothing changes
+ // true false start editing new file, new buffer
+ // true true start editing in existing buffer (nothing to do)
if (!other_file && !oldbuf) { // re-use the buffer
set_last_cursor(curwin); // may set b_last_cursor
if (newlnum == ECMD_LAST || newlnum == ECMD_LASTL) {
@@ -2673,7 +2472,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
}
buf = curbuf;
if (buf->b_fname != NULL) {
- new_name = (char *)vim_strsave((char_u *)buf->b_fname);
+ new_name = xstrdup(buf->b_fname);
} else {
new_name = NULL;
}
@@ -2723,9 +2522,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
curbuf->b_op_end.lnum = 0;
}
- /*
- * If we get here we are sure to start editing
- */
+ // If we get here we are sure to start editing
// Assume success now
retval = OK;
@@ -2736,17 +2533,13 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
curbuf->b_flags &= ~BF_NOTEDITED;
}
- /*
- * Check if we are editing the w_arg_idx file in the argument list.
- */
+ // Check if we are editing the w_arg_idx file in the argument list.
check_arg_idx(curwin);
if (!auto_buf) {
- /*
- * Set cursor and init window before reading the file and executing
- * autocommands. This allows for the autocommands to position the
- * cursor.
- */
+ // Set cursor and init window before reading the file and executing
+ // autocommands. This allows for the autocommands to position the
+ // cursor.
curwin_init();
// It's possible that all lines in the buffer changed. Need to update
@@ -2760,19 +2553,15 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
// Change directories when the 'acd' option is set.
do_autochdir();
- /*
- * Careful: open_buffer() and apply_autocmds() may change the current
- * buffer and window.
- */
+ // Careful: open_buffer() and apply_autocmds() may change the current
+ // buffer and window.
orig_pos = curwin->w_cursor;
topline = curwin->w_topline;
if (!oldbuf) { // need to read the file
swap_exists_action = SEA_DIALOG;
curbuf->b_flags |= BF_CHECK_RO; // set/reset 'ro' flag
- /*
- * Open the buffer and read the file.
- */
+ // Open the buffer and read the file.
if (flags & ECMD_NOWINENTER) {
readfile_flags |= READ_NOWINENTER;
}
@@ -2790,7 +2579,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
// changed by the user.
do_modelines(OPT_WINONLY);
- apply_autocmds_retval(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf,
+ apply_autocmds_retval(EVENT_BUFENTER, NULL, NULL, false, curbuf,
&retval);
if ((flags & ECMD_NOWINENTER) == 0) {
apply_autocmds_retval(EVENT_BUFWINENTER, NULL, NULL, false, curbuf,
@@ -2803,7 +2592,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
// 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 *text = (char *)get_cursor_line_ptr();
+ const char *text = get_cursor_line_ptr();
if (curwin->w_cursor.lnum != orig_pos.lnum
|| curwin->w_cursor.col != (int)(skipwhite(text) - text)) {
@@ -2848,7 +2637,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
curwin->w_cursor.col = solcol;
check_cursor_col();
curwin->w_cursor.coladd = 0;
- curwin->w_set_curswant = TRUE;
+ curwin->w_set_curswant = true;
} else {
beginline(BL_SOL | BL_FIX);
}
@@ -2863,10 +2652,8 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
// Check if cursors in other windows on the same buffer are still valid
check_lnums(false);
- /*
- * Did not read the file, need to show some info about the file.
- * Do this after setting the cursor.
- */
+ // Did not read the file, need to show some info about the file.
+ // Do this after setting the cursor.
if (oldbuf
&& !auto_buf) {
int msg_scroll_save = msg_scroll;
@@ -2874,7 +2661,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
// Obey the 'O' flag in 'cpoptions': overwrite any previous file
// message.
if (shortmess(SHM_OVERALL) && !exiting && p_verbose == 0) {
- msg_scroll = FALSE;
+ msg_scroll = false;
}
if (!msg_scroll) { // wait a bit when overwriting an error msg
check_for_delay(false);
@@ -2910,7 +2697,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
update_topline(curwin);
curwin->w_scbind_pos = curwin->w_topline;
*so_ptr = n;
- redraw_curbuf_later(NOT_VALID); // redraw this buffer later
+ redraw_curbuf_later(UPD_NOT_VALID); // redraw this buffer later
}
// Change directories when the 'acd' option is set.
@@ -2996,7 +2783,7 @@ void ex_append(exarg_T *eap)
}
p = vim_strchr(eap->nextcmd, NL);
if (p == NULL) {
- p = eap->nextcmd + STRLEN(eap->nextcmd);
+ p = eap->nextcmd + strlen(eap->nextcmd);
}
theline = xstrnsave(eap->nextcmd, (size_t)(p - eap->nextcmd));
if (*p != NUL) {
@@ -3018,7 +2805,7 @@ void ex_append(exarg_T *eap)
// Look for the "." after automatic indent.
vcol = 0;
- for (p = theline; indent > vcol; ++p) {
+ for (p = theline; indent > vcol; p++) {
if (*p == ' ') {
vcol++;
} else if (*p == TAB) {
@@ -3098,7 +2885,7 @@ void ex_change(exarg_T *eap)
append_indent = get_indent_lnum(eap->line1);
}
- for (lnum = eap->line2; lnum >= eap->line1; --lnum) {
+ for (lnum = eap->line2; lnum >= eap->line1; lnum--) {
if (curbuf->b_ml.ml_flags & ML_EMPTY) { // nothing to delete
break;
}
@@ -3249,8 +3036,7 @@ void ex_z(exarg_T *eap)
ex_no_reprint = true;
}
-/// @return true if the secure flag is set (.exrc or .vimrc in current directory)
-/// and also give an error message.
+/// @return true if the secure flag is set and also give an error message.
/// Otherwise, return false.
bool check_secure(void)
{
@@ -3307,13 +3093,13 @@ 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 *pat, char *sub, char *cmd, bool save)
+static bool sub_joining_lines(exarg_T *eap, char *pat, const char *sub, const char *cmd, bool save)
FUNC_ATTR_NONNULL_ARG(1, 3, 4)
{
// TODO(vim): find a generic solution to make line-joining operations more
// efficient, avoid allocating a string that grows in size.
if (pat != NULL
- && STRCMP(pat, "\\n") == 0
+ && strcmp(pat, "\\n") == 0
&& *sub == NUL
&& (*cmd == NUL || (cmd[1] == NUL
&& (*cmd == 'g'
@@ -3346,10 +3132,10 @@ static bool sub_joining_lines(exarg_T *eap, char *pat, char *sub, char *cmd, boo
if (save) {
if ((cmdmod.cmod_flags & CMOD_KEEPPATTERNS) == 0) {
- save_re_pat(RE_SUBST, (char_u *)pat, p_magic);
+ save_re_pat(RE_SUBST, pat, magic_isset());
}
// put pattern in history
- add_to_history(HIST_SEARCH, (char_u *)pat, true, NUL);
+ add_to_history(HIST_SEARCH, pat, true, NUL);
}
return true;
@@ -3384,7 +3170,7 @@ static char *sub_grow_buf(char **new_start, int needed_len)
// Check if the temporary buffer is long enough to do the
// 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);
+ size_t len = strlen(*new_start);
needed_len += (int)len;
if (needed_len > new_start_len) {
new_start_len = needed_len + 50;
@@ -3477,6 +3263,30 @@ static int check_regexp_delim(int c)
/// @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)
{
+#define ADJUST_SUB_FIRSTLNUM() \
+ do { \
+ /* For a multi-line match, make a copy of the last matched */ \
+ /* line and continue in that one. */ \
+ if (nmatch > 1) { \
+ sub_firstlnum += (linenr_T)nmatch - 1; \
+ xfree(sub_firstline); \
+ sub_firstline = xstrdup(ml_get(sub_firstlnum)); \
+ /* When going beyond the last line, stop substituting. */ \
+ if (sub_firstlnum <= line2) { \
+ do_again = true; \
+ } else { \
+ subflags.do_all = false; \
+ } \
+ } \
+ if (skip_match) { \
+ /* Already hit end of the buffer, sub_firstlnum is one */ \
+ /* less than what it ought to be. */ \
+ xfree(sub_firstline); \
+ sub_firstline = xstrdup(""); \
+ copycol = 0; \
+ } \
+ } while (0)
+
long i = 0;
regmmatch_T regmatch;
static subflags_T subflags = {
@@ -3522,7 +3332,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
}
// new pattern and substitution
if (eap->cmd[0] == 's' && *cmd != NUL && !ascii_iswhite(*cmd)
- && vim_strchr("0123456789cegriIp|\"", *cmd) == NULL) {
+ && vim_strchr("0123456789cegriIp|\"", (uint8_t)(*cmd)) == NULL) {
// don't accept alphanumeric for separator
if (check_regexp_delim(*cmd) == FAIL) {
return 0;
@@ -3533,7 +3343,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
// //sub/r). "\&sub&" use last substitute pattern (like //sub/).
if (*cmd == '\\') {
cmd++;
- if (vim_strchr("/?&", *cmd) == NULL) {
+ if (vim_strchr("/?&", (uint8_t)(*cmd)) == NULL) {
emsg(_(e_backslash));
return 0;
}
@@ -3541,23 +3351,21 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
which_pat = RE_SEARCH; // use last '/' pattern
}
pat = ""; // empty search pattern
- delimiter = (char_u)(*cmd++); // remember delimiter character
+ delimiter = (uint8_t)(*cmd++); // remember delimiter character
has_second_delim = true;
} else { // find the end of the regexp
which_pat = RE_LAST; // use last used regexp
- delimiter = (char_u)(*cmd++); // remember delimiter character
+ delimiter = (uint8_t)(*cmd++); // remember delimiter character
pat = cmd; // remember start of search pat
- cmd = (char *)skip_regexp((char_u *)cmd, delimiter, p_magic, &eap->arg);
+ cmd = skip_regexp_ex(cmd, delimiter, magic_isset(), &eap->arg, NULL, NULL);
if (cmd[0] == delimiter) { // end delimiter found
*cmd++ = NUL; // replace it with a NUL
has_second_delim = true;
}
}
- /*
- * Small incompatibility: vi sees '\n' as end of the command, but in
- * Vim we want to use '\n' to find/substitute a NUL.
- */
+ // Small incompatibility: vi sees '\n' as end of the command, but in
+ // Vim we want to use '\n' to find/substitute a NUL.
sub = cmd; // remember the start of the substitution
while (cmd[0]) {
@@ -3615,12 +3423,10 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
}
}
- /*
- * check for trailing command or garbage
- */
+ // check for trailing command or garbage
cmd = skipwhite(cmd);
if (*cmd && *cmd != '"') { // if not end-of-line or comment
- eap->nextcmd = (char *)check_nextcmd((char_u *)cmd);
+ eap->nextcmd = check_nextcmd(cmd);
if (eap->nextcmd == NULL) {
semsg(_(e_trailing_arg), cmd);
return 0;
@@ -3637,8 +3443,8 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
return 0;
}
- if (search_regcomp((char_u *)pat, RE_SUBST, which_pat, (cmdpreview ? 0 : SEARCH_HIS),
- &regmatch) == FAIL) {
+ if (search_regcomp(pat, NULL, RE_SUBST, which_pat,
+ (cmdpreview ? 0 : SEARCH_HIS), &regmatch) == FAIL) {
if (subflags.do_error) {
emsg(_(e_invcmd));
}
@@ -3667,7 +3473,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
sub = xstrdup(sub);
sub_copy = sub;
} else {
- char *newsub = (char *)regtilde((char_u *)sub, p_magic, cmdpreview);
+ char *newsub = regtilde(sub, magic_isset(), cmdpreview);
if (newsub != sub) {
// newsub was allocated, free it later.
sub_copy = newsub;
@@ -3675,13 +3481,6 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
}
}
- const bool cmdheight0 = !ui_has_messages();
- 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.
// If preview: limit to max('cmdwinheight', viewport).
linenr_T line2 = eap->line2;
@@ -3706,50 +3505,48 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
bool skip_match = false;
linenr_T sub_firstlnum; // nr of first sub line
- /*
- * The new text is build up step by step, to avoid too much
- * copying. There are these pieces:
- * sub_firstline The old text, unmodified.
- * copycol Column in the old text where we started
- * looking for a match; from here old text still
- * needs to be copied to the new text.
- * matchcol Column number of the old text where to look
- * for the next match. It's just after the
- * previous match or one further.
- * prev_matchcol Column just after the previous match (if any).
- * Mostly equal to matchcol, except for the first
- * match and after skipping an empty match.
- * regmatch.*pos Where the pattern matched in the old text.
- * new_start The new text, all that has been produced so
- * far.
- * new_end The new text, where to append new text.
- *
- * lnum The line number where we found the start of
- * the match. Can be below the line we searched
- * when there is a \n before a \zs in the
- * pattern.
- * sub_firstlnum The line number in the buffer where to look
- * for a match. Can be different from "lnum"
- * when the pattern or substitute string contains
- * line breaks.
- *
- * Special situations:
- * - When the substitute string contains a line break, the part up
- * to the line break is inserted in the text, but the copy of
- * the original line is kept. "sub_firstlnum" is adjusted for
- * the inserted lines.
- * - When the matched pattern contains a line break, the old line
- * is taken from the line at the end of the pattern. The lines
- * in the match are deleted later, "sub_firstlnum" is adjusted
- * accordingly.
- *
- * The new text is built up in new_start[]. It has some extra
- * room to avoid using xmalloc()/free() too often.
- *
- * Make a copy of the old line, so it won't be taken away when
- * updating the screen or handling a multi-line match. The "old_"
- * pointers point into this copy.
- */
+ // The new text is build up step by step, to avoid too much
+ // copying. There are these pieces:
+ // sub_firstline The old text, unmodified.
+ // copycol Column in the old text where we started
+ // looking for a match; from here old text still
+ // needs to be copied to the new text.
+ // matchcol Column number of the old text where to look
+ // for the next match. It's just after the
+ // previous match or one further.
+ // prev_matchcol Column just after the previous match (if any).
+ // Mostly equal to matchcol, except for the first
+ // match and after skipping an empty match.
+ // regmatch.*pos Where the pattern matched in the old text.
+ // new_start The new text, all that has been produced so
+ // far.
+ // new_end The new text, where to append new text.
+ //
+ // lnum The line number where we found the start of
+ // the match. Can be below the line we searched
+ // when there is a \n before a \zs in the
+ // pattern.
+ // sub_firstlnum The line number in the buffer where to look
+ // for a match. Can be different from "lnum"
+ // when the pattern or substitute string contains
+ // line breaks.
+ //
+ // Special situations:
+ // - When the substitute string contains a line break, the part up
+ // to the line break is inserted in the text, but the copy of
+ // the original line is kept. "sub_firstlnum" is adjusted for
+ // the inserted lines.
+ // - When the matched pattern contains a line break, the old line
+ // is taken from the line at the end of the pattern. The lines
+ // in the match are deleted later, "sub_firstlnum" is adjusted
+ // accordingly.
+ //
+ // The new text is built up in new_start[]. It has some extra
+ // room to avoid using xmalloc()/free() too often.
+ //
+ // Make a copy of the old line, so it won't be taken away when
+ // updating the screen or handling a multi-line match. The "old_"
+ // pointers point into this copy.
sub_firstlnum = lnum;
copycol = 0;
matchcol = 0;
@@ -3760,14 +3557,12 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
got_match = true;
}
- /*
- * Loop until nothing more to replace in this line.
- * 1. Handle match with empty string.
- * 2. If subflags.do_ask is set, ask for confirmation.
- * 3. substitute the string.
- * 4. if subflags.do_all is set, find next match
- * 5. break if there isn't another match in this line
- */
+ // Loop until nothing more to replace in this line.
+ // 1. Handle match with empty string.
+ // 2. If subflags.do_ask is set, ask for confirmation.
+ // 3. substitute the string.
+ // 4. if subflags.do_all is set, find next match
+ // 5. break if there isn't another match in this line
for (;;) {
SubResult current_match = {
.start = { 0, 0 },
@@ -3798,19 +3593,17 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
break;
}
if (sub_firstline == NULL) {
- sub_firstline = (char *)vim_strsave(ml_get(sub_firstlnum));
+ sub_firstline = xstrdup(ml_get(sub_firstlnum));
}
// Save the line number of the last change for the final
// cursor position (just like Vi).
curwin->w_cursor.lnum = lnum;
- do_again = FALSE;
+ do_again = false;
- /*
- * 1. Match empty string does not count, except for first
- * match. This reproduces the strange vi behaviour.
- * This also catches endless loops.
- */
+ // 1. Match empty string does not count, except for first
+ // match. This reproduces the strange vi behaviour.
+ // This also catches endless loops.
if (matchcol == prev_matchcol
&& regmatch.endpos[0].lnum == 0
&& matchcol == regmatch.endpos[0].col) {
@@ -3842,7 +3635,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
// we continue looking for a match on the next line.
// Avoids that ":s/\nB\@=//gc" get stuck.
if (nmatch > 1) {
- matchcol = (colnr_T)STRLEN(sub_firstline);
+ matchcol = (colnr_T)strlen(sub_firstline);
nmatch = 1;
skip_match = true;
}
@@ -3875,9 +3668,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
no_u_sync++;
}
- /*
- * Loop until 'y', 'n', 'q', CTRL-E or CTRL-Y typed.
- */
+ // Loop until 'y', 'n', 'q', CTRL-E or CTRL-Y typed.
while (subflags.do_ask) {
if (exmode_active) {
char *prompt;
@@ -3906,7 +3697,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
msg_putchar('\n');
xfree(prompt);
if (resp != NULL) {
- typed = (char_u)(*resp);
+ typed = (uint8_t)(*resp);
xfree(resp);
} else {
// getcmdline_prompt() returns NULL if there is no command line to return.
@@ -3939,14 +3730,13 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
// really update the line, it would change
// what matches. Temporarily replace the line
// and change it back afterwards.
- orig_line = (char *)vim_strsave(ml_get(lnum));
- char *new_line = (char *)concat_str((char_u *)new_start,
- (char_u *)sub_firstline + copycol);
+ orig_line = xstrdup(ml_get(lnum));
+ char *new_line = concat_str(new_start, sub_firstline + copycol);
// Position the cursor relative to the end of the line, the
// previous substitute may have inserted or deleted characters
// before the cursor.
- len_change = (int)STRLEN(new_line) - (int)STRLEN(orig_line);
+ len_change = (int)strlen(new_line) - (int)strlen(orig_line);
curwin->w_cursor.col += len_change;
ml_replace(lnum, new_line, false);
}
@@ -3955,13 +3745,18 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
- regmatch.startpos[0].lnum;
search_match_endcol = regmatch.endpos[0].col
+ len_change;
- highlight_match = TRUE;
+ if (search_match_lines == 0 && search_match_endcol == 0) {
+ // highlight at least one character for /^/
+ search_match_endcol = 1;
+ }
+ highlight_match = true;
update_topline(curwin);
validate_cursor();
- update_screen(SOME_VALID);
+ redraw_later(curwin, UPD_SOME_VALID);
+ update_screen();
highlight_match = false;
- redraw_later(curwin, SOME_VALID);
+ redraw_later(curwin, UPD_SOME_VALID);
curwin->w_p_fen = save_p_fen;
if (msg_row == Rows - 1) {
@@ -3978,7 +3773,9 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
msg_no_more = false;
msg_scroll = (int)i;
show_cursor_info(true);
- ui_cursor_goto(msg_row, msg_col);
+ if (!ui_has(kUIMessages)) {
+ ui_cursor_goto(msg_row, msg_col);
+ }
RedrawingDisabled = temp;
no_mapping++; // don't map this key
@@ -4039,7 +3836,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
// Avoids that ":%s/\nB\@=//gc" and ":%s/\n/,\r/gc"
// get stuck when pressing 'n'.
if (nmatch > 1) {
- matchcol = (colnr_T)STRLEN(sub_firstline);
+ matchcol = (colnr_T)strlen(sub_firstline);
skip_match = true;
}
goto skip;
@@ -4061,30 +3858,6 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
skip_match = true;
}
-#define ADJUST_SUB_FIRSTLNUM() \
- do { \
- /* For a multi-line match, make a copy of the last matched */ \
- /* line and continue in that one. */ \
- if (nmatch > 1) { \
- sub_firstlnum += (linenr_T)nmatch - 1; \
- xfree(sub_firstline); \
- sub_firstline = (char *)vim_strsave(ml_get(sub_firstlnum)); \
- /* When going beyond the last line, stop substituting. */ \
- if (sub_firstlnum <= line2) { \
- do_again = true; \
- } else { \
- subflags.do_all = false; \
- } \
- } \
- if (skip_match) { \
- /* Already hit end of the buffer, sub_firstlnum is one */ \
- /* less than what it ought to be. */ \
- xfree(sub_firstline); \
- sub_firstline = xstrdup(""); \
- copycol = 0; \
- } \
- } while (0)
-
// 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.
@@ -4122,8 +3895,9 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
// When it fails sublen is zero.
sublen = vim_regsub_multi(&regmatch,
sub_firstlnum - regmatch.startpos[0].lnum,
- (char_u *)sub, (char_u *)sub_firstline, 0,
- REGSUB_BACKSLASH | (p_magic ? REGSUB_MAGIC : 0));
+ sub, sub_firstline, 0,
+ REGSUB_BACKSLASH
+ | (magic_isset() ? REGSUB_MAGIC : 0));
textlock--;
// If getting the substitute string caused an error, don't do
@@ -4144,12 +3918,12 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
if (nmatch == 1) {
p1 = sub_firstline;
} else {
- p1 = (char *)ml_get(sub_firstlnum + (linenr_T)nmatch - 1);
+ p1 = ml_get(sub_firstlnum + (linenr_T)nmatch - 1);
nmatch_tl += nmatch - 1;
}
size_t copy_len = (size_t)(regmatch.startpos[0].col - copycol);
new_end = sub_grow_buf(&new_start,
- (colnr_T)STRLEN(p1) - regmatch.endpos[0].col
+ (colnr_T)strlen(p1) - regmatch.endpos[0].col
+ (colnr_T)copy_len + sublen + 1);
// copy the text up to the part that matched
@@ -4164,8 +3938,9 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
textlock++;
(void)vim_regsub_multi(&regmatch,
sub_firstlnum - regmatch.startpos[0].lnum,
- (char_u *)sub, (char_u *)new_end, sublen,
- REGSUB_COPY | REGSUB_BACKSLASH | (p_magic ? REGSUB_MAGIC : 0));
+ sub, new_end, sublen,
+ REGSUB_COPY | REGSUB_BACKSLASH
+ | (magic_isset() ? REGSUB_MAGIC : 0));
textlock--;
sub_nsubs++;
did_sub = true;
@@ -4183,7 +3958,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
bcount_t replaced_bytes = 0;
lpos_T start = regmatch.startpos[0], end = regmatch.endpos[0];
for (i = 0; i < nmatch - 1; i++) {
- replaced_bytes += (bcount_t)STRLEN(ml_get((linenr_T)(lnum_start + i))) + 1;
+ replaced_bytes += (bcount_t)strlen(ml_get((linenr_T)(lnum_start + i))) + 1;
}
replaced_bytes += end.col - start.col;
@@ -4226,7 +4001,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
p1 += utfc_ptr2len(p1) - 1;
}
}
- colnr_T new_endcol = (colnr_T)STRLEN(new_start);
+ colnr_T new_endcol = (colnr_T)strlen(new_start);
current_match.end.col = new_endcol;
current_match.end.lnum = lnum;
@@ -4262,15 +4037,13 @@ skip:
&& !re_multiline(regmatch.regprog)));
nmatch = -1;
- /*
- * Replace the line in the buffer when needed. This is
- * skipped when there are more matches.
- * The check for nmatch_tl is needed for when multi-line
- * matching must replace the lines before trying to do another
- * match, otherwise "\@<=" won't work.
- * When the match starts below where we start searching also
- * need to replace the line first (using \zs after \n).
- */
+ // Replace the line in the buffer when needed. This is
+ // skipped when there are more matches.
+ // The check for nmatch_tl is needed for when multi-line
+ // matching must replace the lines before trying to do another
+ // match, otherwise "\@<=" won't work.
+ // When the match starts below where we start searching also
+ // need to replace the line first (using \zs after \n).
if (lastone
|| nmatch_tl > 0
|| (nmatch = vim_regexec_multi(&regmatch, curwin,
@@ -4278,16 +4051,14 @@ skip:
matchcol, NULL, NULL)) == 0
|| regmatch.startpos[0].lnum > 0) {
if (new_start != NULL) {
- /*
- * Copy the rest of the line, that didn't match.
- * "matchcol" has to be adjusted, we use the end of
- * the line as reference, because the substitute may
- * have changed the number of characters. Same for
- * "prev_matchcol".
- */
+ // Copy the rest of the line, that didn't match.
+ // "matchcol" has to be adjusted, we use the end of
+ // the line as reference, because the substitute may
+ // have changed the number of characters. Same for
+ // "prev_matchcol".
STRCAT(new_start, sub_firstline + copycol);
- matchcol = (colnr_T)STRLEN(sub_firstline) - matchcol;
- prev_matchcol = (colnr_T)STRLEN(sub_firstline)
+ matchcol = (colnr_T)strlen(sub_firstline) - matchcol;
+ prev_matchcol = (colnr_T)strlen(sub_firstline)
- prev_matchcol;
if (u_savesub(lnum) != OK) {
@@ -4296,12 +4067,10 @@ skip:
ml_replace(lnum, new_start, true);
if (nmatch_tl > 0) {
- /*
- * Matched lines have now been substituted and are
- * useless, delete them. The part after the match
- * has been appended to new_start, we don't need
- * it in the buffer.
- */
+ // Matched lines have now been substituted and are
+ // useless, delete them. The part after the match
+ // has been appended to new_start, we don't need
+ // it in the buffer.
lnum++;
if (u_savedel(lnum, nmatch_tl) != OK) {
break;
@@ -4334,8 +4103,8 @@ skip:
xfree(sub_firstline); // free the temp buffer
sub_firstline = new_start;
new_start = NULL;
- matchcol = (colnr_T)STRLEN(sub_firstline) - matchcol;
- prev_matchcol = (colnr_T)STRLEN(sub_firstline)
+ matchcol = (colnr_T)strlen(sub_firstline) - matchcol;
+ prev_matchcol = (colnr_T)strlen(sub_firstline)
- prev_matchcol;
copycol = 0;
}
@@ -4344,9 +4113,7 @@ skip:
sub_firstlnum, matchcol, NULL, NULL);
}
- /*
- * 5. break if there isn't another match in this line
- */
+ // 5. break if there isn't another match in this line
if (nmatch <= 0) {
// If the match found didn't start where we were
// searching, do the next search in the line where we
@@ -4442,7 +4209,7 @@ skip:
beginline(BL_WHITE | BL_FIX);
}
}
- if (!cmdpreview && !do_sub_msg(subflags.do_count) && subflags.do_ask) {
+ if (!cmdpreview && !do_sub_msg(subflags.do_count) && subflags.do_ask && p_ch > 0) {
msg("");
}
} else {
@@ -4457,7 +4224,9 @@ skip:
emsg(_(e_interr));
} else if (got_match) {
// did find something but nothing substituted
- msg("");
+ if (p_ch > 0) {
+ msg("");
+ }
} else if (subflags.do_error) {
// nothing found
semsg(_(e_patnotf2), get_search_pat());
@@ -4490,11 +4259,6 @@ skip:
}
}
- if (cmdheight0) {
- // Restore cmdheight
- set_option_value("ch", 0L, NULL, 0);
- }
-
kv_destroy(preview_lines.subresults);
return retv;
#undef ADJUST_SUB_FIRSTLNUM
@@ -4509,12 +4273,10 @@ skip:
/// @return true if a message was given.
bool do_sub_msg(bool count_only)
{
- /*
- * Only report substitutions when:
- * - more than 'report' substitutions
- * - command was typed by user, or number of changed lines > 'report'
- * - giving messages is not disabled by 'lazyredraw'
- */
+ // Only report substitutions when:
+ // - more than 'report' substitutions
+ // - command was typed by user, or number of changed lines > 'report'
+ // - giving messages is not disabled by 'lazyredraw'
if (((sub_nsubs > p_report && (KeyTyped || sub_nlines > 1 || p_report < 1))
|| count_only)
&& messaging()) {
@@ -4605,14 +4367,12 @@ void ex_global(exarg_T *eap)
cmd = eap->arg;
which_pat = RE_LAST; // default: use last used regexp
- /*
- * undocumented vi feature:
- * "\/" and "\?": use previous search pattern.
- * "\&": use previous substitute pattern.
- */
+ // undocumented vi feature:
+ // "\/" and "\?": use previous search pattern.
+ // "\&": use previous substitute pattern.
if (*cmd == '\\') {
cmd++;
- if (vim_strchr("/?&", *cmd) == NULL) {
+ if (vim_strchr("/?&", (uint8_t)(*cmd)) == NULL) {
emsg(_(e_backslash));
return;
}
@@ -4632,13 +4392,15 @@ void ex_global(exarg_T *eap)
delim = *cmd; // get the delimiter
cmd++; // skip delimiter if there is one
pat = cmd; // remember start of pattern
- cmd = (char *)skip_regexp((char_u *)cmd, delim, p_magic, &eap->arg);
+ cmd = skip_regexp_ex(cmd, delim, magic_isset(), &eap->arg, NULL, NULL);
if (cmd[0] == delim) { // end delimiter found
*cmd++ = NUL; // replace it with a NUL
}
}
- if (search_regcomp((char_u *)pat, RE_BOTH, which_pat, SEARCH_HIS, &regmatch) == FAIL) {
+ char *used_pat;
+ if (search_regcomp(pat, &used_pat, RE_BOTH, which_pat,
+ SEARCH_HIS, &regmatch) == FAIL) {
emsg(_(e_invcmd));
return;
}
@@ -4669,9 +4431,9 @@ void ex_global(exarg_T *eap)
msg(_(e_interr));
} else if (ndone == 0) {
if (type == 'v') {
- smsg(_("Pattern found in every line: %s"), pat);
+ smsg(_("Pattern found in every line: %s"), used_pat);
} else {
- smsg(_("Pattern not found: %s"), pat);
+ smsg(_("Pattern not found: %s"), used_pat);
}
} else {
global_exe(cmd);
@@ -4748,37 +4510,30 @@ void free_old_sub(void)
/// @return true when it was created.
bool prepare_tagpreview(bool undo_sync)
{
- /*
- * If there is already a preview window open, use that one.
- */
- if (!curwin->w_p_pvw) {
- bool found_win = false;
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_p_pvw) {
- win_enter(wp, undo_sync);
- found_win = true;
- break;
- }
- }
- if (!found_win) {
- /*
- * There is no preview window open yet. Create one.
- */
- if (win_split(g_do_tagpreview > 0 ? g_do_tagpreview : 0, 0)
- == FAIL) {
- return false;
- }
- curwin->w_p_pvw = TRUE;
- curwin->w_p_wfh = TRUE;
- RESET_BINDING(curwin); /* don't take over 'scrollbind'
- and 'cursorbind' */
- curwin->w_p_diff = false; // no 'diff'
- set_string_option_direct("fdc", -1, // no 'foldcolumn'
- "0", OPT_FREE, SID_NONE);
- return true;
+ if (curwin->w_p_pvw) {
+ return false;
+ }
+
+ // If there is already a preview window open, use that one.
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_p_pvw) {
+ win_enter(wp, undo_sync);
+ return false;
}
}
- return false;
+
+ // There is no preview window open yet. Create one.
+ if (win_split(g_do_tagpreview > 0 ? g_do_tagpreview : 0, 0)
+ == FAIL) {
+ return false;
+ }
+ curwin->w_p_pvw = true;
+ curwin->w_p_wfh = true;
+ RESET_BINDING(curwin); // don't take over 'scrollbind' and 'cursorbind'
+ curwin->w_p_diff = false; // no 'diff'
+ set_string_option_direct("fdc", -1, // no 'foldcolumn'
+ "0", OPT_FREE, SID_NONE);
+ return true;
}
/// Shows the effects of the :substitute command being typed ('inccommand').
@@ -4789,7 +4544,7 @@ static int show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, i
long cmdpreview_ns, handle_T cmdpreview_bufnr)
FUNC_ATTR_NONNULL_ALL
{
- char *save_shm_p = (char *)vim_strsave(p_shm);
+ char *save_shm_p = xstrdup(p_shm);
PreviewLines lines = *preview_lines;
buf_T *orig_buf = curbuf;
// We keep a special-purpose buffer around, but don't assume it exists.
@@ -4867,7 +4622,7 @@ static int show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, i
if (next_linenr == orig_buf->b_ml.ml_line_count + 1) {
line = "";
} else {
- line = (char *)ml_get_buf(orig_buf, next_linenr, false);
+ line = ml_get_buf(orig_buf, next_linenr, false);
line_size = strlen(line) + (size_t)col_width + 1;
// Reallocate if line not long enough
@@ -4935,12 +4690,12 @@ char *skip_vimgrep_pat(char *p, char **s, int *flags)
{
int c;
- if (vim_isIDc(*p)) {
+ if (vim_isIDc((uint8_t)(*p))) {
// ":vimgrep pattern fname"
if (s != NULL) {
*s = p;
}
- p = (char *)skiptowhite((char_u *)p);
+ p = skiptowhite(p);
if (s != NULL && *p != NUL) {
*p++ = NUL;
}
@@ -4949,8 +4704,8 @@ char *skip_vimgrep_pat(char *p, char **s, int *flags)
if (s != NULL) {
*s = p + 1;
}
- c = (char_u)(*p);
- p = (char *)skip_regexp((char_u *)p + 1, c, true, NULL);
+ c = (uint8_t)(*p);
+ p = skip_regexp(p + 1, c, true);
if (*p != c) {
return NULL;
}
@@ -4986,46 +4741,72 @@ void ex_oldfiles(exarg_T *eap)
if (l == NULL) {
msg(_("No old files"));
- } else {
- msg_start();
- msg_scroll = true;
- TV_LIST_ITER(l, li, {
- if (got_int) {
- break;
- }
- nr++;
- const char *fname = tv_get_string(TV_LIST_ITEM_TV(li));
- if (!message_filtered((char_u *)fname)) {
- msg_outnum(nr);
- msg_puts(": ");
- 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
- os_breakcheck();
- }
- });
-
- // Assume "got_int" was set to truncate the listing.
- got_int = false;
-
- // File selection prompt on ":browse oldfiles"
- 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, (int)nr - 1);
- if (p == NULL) {
- return;
- }
- char *const s = expand_env_save((char *)p);
- eap->arg = s;
- eap->cmdidx = CMD_edit;
- cmdmod.cmod_flags &= ~CMOD_BROWSE;
- do_exedit(eap, NULL);
- xfree(s);
+ return;
+ }
+
+ msg_start();
+ msg_scroll = true;
+ TV_LIST_ITER(l, li, {
+ if (got_int) {
+ break;
+ }
+ nr++;
+ const char *fname = tv_get_string(TV_LIST_ITEM_TV(li));
+ if (!message_filtered((char *)fname)) {
+ msg_outnum(nr);
+ msg_puts(": ");
+ msg_outtrans((char *)tv_get_string(TV_LIST_ITEM_TV(li)));
+ msg_clr_eos();
+ msg_putchar('\n');
+ os_breakcheck();
+ }
+ });
+
+ // Assume "got_int" was set to truncate the listing.
+ got_int = false;
+
+ // File selection prompt on ":browse oldfiles"
+ 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, (int)nr - 1);
+ if (p == NULL) {
+ return;
}
+ char *const s = expand_env_save((char *)p);
+ eap->arg = s;
+ eap->cmdidx = CMD_edit;
+ cmdmod.cmod_flags &= ~CMOD_BROWSE;
+ do_exedit(eap, NULL);
+ xfree(s);
}
}
}
+
+void ex_trust(exarg_T *eap)
+{
+ const char *const p = skiptowhite(eap->arg);
+ char *arg1 = xmemdupz(eap->arg, (size_t)(p - eap->arg));
+ const char *action = "allow";
+ const char *path = skipwhite(p);
+
+ if (strcmp(arg1, "++deny") == 0) {
+ action = "deny";
+ } else if (strcmp(arg1, "++remove") == 0) {
+ action = "remove";
+ } else if (*arg1 != '\0') {
+ semsg(e_invarg2, arg1);
+ goto theend;
+ }
+
+ if (path[0] == '\0') {
+ path = NULL;
+ }
+
+ nlua_trust(action, path);
+
+theend:
+ xfree(arg1);
+}
diff --git a/src/nvim/ex_cmds.h b/src/nvim/ex_cmds.h
index 3aaba9ce42..39bff3e35d 100644
--- a/src/nvim/ex_cmds.h
+++ b/src/nvim/ex_cmds.h
@@ -5,6 +5,7 @@
#include "nvim/buffer_defs.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/os/time.h"
#include "nvim/pos.h"
diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua
index 4bed1e94b9..c8b6ceab69 100644
--- a/src/nvim/ex_cmds.lua
+++ b/src/nvim/ex_cmds.lua
@@ -673,18 +673,6 @@ module.cmds = {
func='ex_cc',
},
{
- command='cscope',
- flags=bit.bor(EXTRA, NOTRLCOM, XFILE),
- addr_type='ADDR_NONE',
- func='ex_cscope',
- },
- {
- command='cstag',
- flags=bit.bor(BANG, TRLBAR, WORD1),
- addr_type='ADDR_NONE',
- func='ex_cstag',
- },
- {
command='cunmap',
flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
@@ -1117,12 +1105,6 @@ module.cmds = {
func='ex_helptags',
},
{
- command='hardcopy',
- flags=bit.bor(RANGE, COUNT, EXTRA, TRLBAR, DFLALL, BANG),
- addr_type='ADDR_LINES',
- func='ex_hardcopy',
- },
- {
command='highlight',
flags=bit.bor(BANG, EXTRA, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
@@ -1141,6 +1123,12 @@ module.cmds = {
func='ex_history',
},
{
+ command='horizontal',
+ flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM),
+ addr_type='ADDR_NONE',
+ func='ex_wrongmodifier',
+ },
+ {
command='insert',
flags=bit.bor(BANG, RANGE, TRLBAR, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
@@ -1399,12 +1387,6 @@ module.cmds = {
func='ex_cclose',
},
{
- command='lcscope',
- flags=bit.bor(EXTRA, NOTRLCOM, XFILE),
- addr_type='ADDR_NONE',
- func='ex_cscope',
- },
- {
command='ldo',
flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL),
addr_type='ADDR_QUICKFIX_VALID',
@@ -2405,7 +2387,7 @@ module.cmds = {
},
{
command='scriptnames',
- flags=bit.bor(BANG, RANGE, COUNT, TRLBAR, CMDWIN, LOCK_OK),
+ flags=bit.bor(BANG, FILES, RANGE, COUNT, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_scriptnames',
},
@@ -2416,12 +2398,6 @@ module.cmds = {
func='ex_scriptencoding',
},
{
- command='scscope',
- flags=bit.bor(EXTRA, NOTRLCOM),
- addr_type='ADDR_NONE',
- func='ex_scscope',
- },
- {
command='set',
flags=bit.bor(BANG, TRLBAR, EXTRA, CMDWIN, LOCK_OK, SBOXOK),
addr_type='ADDR_NONE',
@@ -2952,6 +2928,12 @@ module.cmds = {
func='ex_tag',
},
{
+ command='trust',
+ flags=bit.bor(EXTRA, FILE1, TRLBAR, LOCK_OK),
+ addr_type='ADDR_NONE',
+ func='ex_trust',
+ },
+ {
command='try',
flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 54315a6417..d08823bc30 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -6,46 +6,45 @@
/// Some more functions for command line commands
#include <assert.h>
-#include <fcntl.h>
#include <inttypes.h>
#include <stdbool.h>
+#include <stdio.h>
#include <string.h>
#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/autocmd.h"
-#include "nvim/globals.h"
-#include "nvim/vim.h"
-#ifdef HAVE_LOCALE_H
-# include <locale.h>
-#endif
#include "nvim/buffer.h"
#include "nvim/change.h"
-#include "nvim/charset.h"
+#include "nvim/channel.h"
#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/eval/vars.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_cmds2.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
-#include "nvim/ex_eval.h"
#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/highlight_defs.h"
+#include "nvim/macros.h"
#include "nvim/mark.h"
-#include "nvim/mbyte.h"
+#include "nvim/memline_defs.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/move.h"
#include "nvim/normal.h"
-#include "nvim/ops.h"
#include "nvim/option.h"
-#include "nvim/os/fs_defs.h"
-#include "nvim/os/shell.h"
-#include "nvim/os_unix.h"
+#include "nvim/os/os_defs.h"
#include "nvim/path.h"
+#include "nvim/pos.h"
#include "nvim/quickfix.h"
#include "nvim/runtime.h"
-#include "nvim/strings.h"
#include "nvim/undo.h"
+#include "nvim/vim.h"
#include "nvim/window.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -204,9 +203,9 @@ void dialog_changed(buf_T *buf, bool checkall)
dialog_msg((char *)buff, _("Save changes to \"%s\"?"), buf->b_fname);
if (checkall) {
- ret = vim_dialog_yesnoallcancel(VIM_QUESTION, NULL, (char_u *)buff, 1);
+ ret = vim_dialog_yesnoallcancel(VIM_QUESTION, NULL, buff, 1);
} else {
- ret = vim_dialog_yesnocancel(VIM_QUESTION, NULL, (char_u *)buff, 1);
+ ret = vim_dialog_yesnocancel(VIM_QUESTION, NULL, buff, 1);
}
if (ret == VIM_YES) {
@@ -256,7 +255,7 @@ bool dialog_close_terminal(buf_T *buf)
dialog_msg(buff, _("Close \"%s\"?"),
(buf->b_fname != NULL) ? buf->b_fname : "?");
- int ret = vim_dialog_yesnocancel(VIM_QUESTION, NULL, (char_u *)buff, 1);
+ int ret = vim_dialog_yesnocancel(VIM_QUESTION, NULL, buff, 1);
return ret == VIM_YES;
}
@@ -368,7 +367,7 @@ bool check_changed_any(bool hidden, bool unload)
exiting = false;
// When ":confirm" used, don't give an error message.
if (!(p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM))) {
- // There must be a wait_return for this message, do_buffer()
+ // 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
// cause a scroll up.
@@ -543,10 +542,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 = (char *)vim_strsave(p_shm);
- set_option_value("shm", 0L, "", 0);
+ p_shm_save = xstrdup(p_shm);
+ set_option_value_give_err("shm", 0L, "", 0);
do_argfile(eap, i);
- set_option_value("shm", 0L, p_shm_save, 0);
+ set_option_value_give_err("shm", 0L, p_shm_save, 0);
xfree(p_shm_save);
}
if (curwin->w_arg_idx != i) {
@@ -612,10 +611,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 = (char *)vim_strsave(p_shm);
- set_option_value("shm", 0L, "", 0);
+ p_shm_save = xstrdup(p_shm);
+ set_option_value_give_err("shm", 0L, "", 0);
goto_buffer(eap, DOBUF_FIRST, FORWARD, next_fnum);
- set_option_value("shm", 0L, p_shm_save, 0);
+ set_option_value_give_err("shm", 0L, p_shm_save, 0);
xfree(p_shm_save);
// If autocommands took us elsewhere, quit here.
@@ -635,10 +634,10 @@ void ex_listdo(exarg_T *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);
+ p_shm_save = xstrdup(p_shm);
+ set_option_value_give_err("shm", 0L, "", 0);
ex_cnext(eap);
- set_option_value("shm", 0L, p_shm_save, 0);
+ set_option_value_give_err("shm", 0L, p_shm_save, 0);
xfree(p_shm_save);
// If jumping to the next quickfix entry fails, quit here.
@@ -680,11 +679,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, (char *)curbuf->b_p_syn, curbuf->b_fname, true,
+ apply_autocmds(EVENT_SYNTAX, curbuf->b_p_syn, curbuf->b_fname, true,
curbuf);
} else {
aucmd_prepbuf(&aco, buf);
- apply_autocmds(EVENT_SYNTAX, (char *)buf->b_p_syn, buf->b_fname, true, buf);
+ apply_autocmds(EVENT_SYNTAX, buf->b_p_syn, buf->b_fname, true, buf);
aucmd_restbuf(&aco);
}
@@ -706,54 +705,56 @@ void ex_compiler(exarg_T *eap)
// List all compiler scripts.
do_cmdline_cmd("echo globpath(&rtp, 'compiler/*.vim')"); // NOLINT
do_cmdline_cmd("echo globpath(&rtp, 'compiler/*.lua')"); // NOLINT
- } else {
- size_t bufsize = STRLEN(eap->arg) + 14;
- buf = xmalloc(bufsize);
- if (eap->forceit) {
- // ":compiler! {name}" sets global options
- do_cmdline_cmd("command -nargs=* CompilerSet set <args>");
- } else {
- // ":compiler! {name}" sets local options.
- // To remain backwards compatible "current_compiler" is always
- // used. A user's compiler plugin may set it, the distributed
- // 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 = (char *)get_var_value("g:current_compiler");
- if (old_cur_comp != NULL) {
- old_cur_comp = xstrdup(old_cur_comp);
- }
- 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);
+ return;
+ }
- snprintf(buf, bufsize, "compiler/%s.vim", eap->arg);
+ size_t bufsize = strlen(eap->arg) + 14;
+ buf = xmalloc(bufsize);
+
+ if (eap->forceit) {
+ // ":compiler! {name}" sets global options
+ do_cmdline_cmd("command -nargs=* CompilerSet set <args>");
+ } else {
+ // ":compiler! {name}" sets local options.
+ // To remain backwards compatible "current_compiler" is always
+ // used. A user's compiler plugin may set it, the distributed
+ // 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");
+ if (old_cur_comp != NULL) {
+ old_cur_comp = xstrdup(old_cur_comp);
+ }
+ 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(buf, bufsize, "compiler/%s.vim", eap->arg);
+ if (source_runtime(buf, DIP_ALL) == FAIL) {
+ // Try lua compiler
+ snprintf(buf, bufsize, "compiler/%s.lua", eap->arg);
if (source_runtime(buf, DIP_ALL) == FAIL) {
- // Try lua compiler
- snprintf(buf, bufsize, "compiler/%s.lua", eap->arg);
- if (source_runtime(buf, DIP_ALL) == FAIL) {
- semsg(_("E666: compiler not supported: %s"), eap->arg);
- }
+ semsg(_("E666: compiler not supported: %s"), eap->arg);
}
- xfree(buf);
+ }
+ xfree(buf);
- do_cmdline_cmd(":delcommand CompilerSet");
+ do_cmdline_cmd(":delcommand CompilerSet");
- // Set "b:current_compiler" from "current_compiler".
- p = (char *)get_var_value("g:current_compiler");
- if (p != NULL) {
- set_internal_string_var("b:current_compiler", p);
- }
+ // Set "b:current_compiler" from "current_compiler".
+ p = get_var_value("g:current_compiler");
+ if (p != NULL) {
+ set_internal_string_var("b:current_compiler", p);
+ }
- // Restore "current_compiler" for ":compiler {name}".
- if (!eap->forceit) {
- if (old_cur_comp != NULL) {
- set_internal_string_var("g:current_compiler", old_cur_comp);
- xfree(old_cur_comp);
- } else {
- do_unlet(S_LEN("g:current_compiler"), true);
- }
+ // Restore "current_compiler" for ":compiler {name}".
+ if (!eap->forceit) {
+ if (old_cur_comp != NULL) {
+ set_internal_string_var("g:current_compiler", old_cur_comp);
+ xfree(old_cur_comp);
+ } else {
+ do_unlet(S_LEN("g:current_compiler"), true);
}
}
}
@@ -776,324 +777,6 @@ void ex_checktime(exarg_T *eap)
no_check_timestamps = save_no_check_timestamps;
}
-#if defined(HAVE_LOCALE_H)
-# define HAVE_GET_LOCALE_VAL
-
-static char *get_locale_val(int what)
-{
- // Obtain the locale value from the libraries.
- char *loc = setlocale(what, NULL);
-
- return loc;
-}
-#endif
-
-/// @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]);
-}
-
-/// Obtain the current messages language. Used to set the default for
-/// 'helplang'. May return NULL or an empty string.
-char *get_mess_lang(void)
-{
- char *p;
-
-#ifdef HAVE_GET_LOCALE_VAL
-# if defined(LC_MESSAGES)
- p = get_locale_val(LC_MESSAGES);
-# else
- // This is necessary for Win32, where LC_MESSAGES is not defined and $LANG
- // may be set to the LCID number. LC_COLLATE is the best guess, LC_TIME
- // and LC_MONETARY may be set differently for a Japanese working in the
- // US.
- p = get_locale_val(LC_COLLATE);
-# endif
-#else
- p = os_getenv("LC_ALL");
- if (!is_valid_mess_lang(p)) {
- p = os_getenv("LC_MESSAGES");
- if (!is_valid_mess_lang(p)) {
- p = os_getenv("LANG");
- }
- }
-#endif
- return is_valid_mess_lang(p) ? p : NULL;
-}
-
-// 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 *get_mess_env(void)
-{
- char *p;
-
- p = (char *)os_getenv("LC_ALL");
- if (p == NULL) {
- p = (char *)os_getenv("LC_MESSAGES");
- if (p == NULL) {
- 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 = get_locale_val(LC_CTYPE);
- }
-# endif
- }
- }
- return p;
-}
-
-#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)
-{
- const char *loc;
-
-#ifdef HAVE_GET_LOCALE_VAL
- loc = get_locale_val(LC_CTYPE);
-#else
- // setlocale() not supported: use the default value
- loc = "C";
-#endif
- set_vim_var_string(VV_CTYPE, loc, -1);
-
- // 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 = get_mess_env();
-#elif defined(LC_MESSAGES)
- loc = get_locale_val(LC_MESSAGES);
-#else
- // In Windows LC_MESSAGES is not defined fallback to LC_CTYPE
- loc = get_locale_val(LC_CTYPE);
-#endif
- set_vim_var_string(VV_LANG, loc, -1);
-
-#ifdef HAVE_GET_LOCALE_VAL
- loc = get_locale_val(LC_TIME);
-#endif
- set_vim_var_string(VV_LC_TIME, loc, -1);
-
-#ifdef HAVE_GET_LOCALE_VAL
- loc = get_locale_val(LC_COLLATE);
-#else
- // setlocale() not supported: use the default value
- loc = "C";
-#endif
- set_vim_var_string(VV_COLLATE, loc, -1);
-}
-
-#ifdef HAVE_WORKING_LIBINTL
-
-/// ":language": Set the language (locale).
-///
-/// @param eap
-void ex_language(exarg_T *eap)
-{
- char *loc;
- char *p;
- char *name;
- int what = LC_ALL;
- char *whatstr = "";
-# ifdef LC_MESSAGES
-# define VIM_LC_MESSAGES LC_MESSAGES
-# else
-# define VIM_LC_MESSAGES 6789
-# endif
-
- name = eap->arg;
-
- // 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 = (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;
- name = skipwhite(p);
- whatstr = "messages ";
- } else if (STRNICMP(eap->arg, "ctype", p - eap->arg) == 0) {
- what = LC_CTYPE;
- name = skipwhite(p);
- whatstr = "ctype ";
- } else if (STRNICMP(eap->arg, "time", p - eap->arg) == 0) {
- what = LC_TIME;
- name = skipwhite(p);
- whatstr = "time ";
- } else if (STRNICMP(eap->arg, "collate", p - eap->arg) == 0) {
- what = LC_COLLATE;
- name = skipwhite(p);
- whatstr = "collate ";
- }
- }
-
- if (*name == NUL) {
- if (what == VIM_LC_MESSAGES) {
- p = get_mess_env();
- } else {
- p = setlocale(what, NULL);
- }
- if (p == NULL || *p == NUL) {
- p = "Unknown";
- }
- smsg(_("Current %slanguage: \"%s\""), whatstr, p);
- } else {
-# ifndef LC_MESSAGES
- if (what == VIM_LC_MESSAGES) {
- loc = "";
- } else {
-# endif
- loc = setlocale(what, name);
-# ifdef LC_NUMERIC
- // Make sure strtod() uses a decimal point, not a comma.
- setlocale(LC_NUMERIC, "C");
-# endif
-# ifndef LC_MESSAGES
- }
-# endif
- if (loc == NULL) {
- semsg(_("E197: Cannot set language to \"%s\""), name);
- } else {
-# ifdef HAVE_NL_MSG_CAT_CNTR
- // Need to do this for GNU gettext, otherwise cached translations
- // will be used again.
- extern int _nl_msg_cat_cntr;
-
- _nl_msg_cat_cntr++;
-# endif
- // Reset $LC_ALL, otherwise it would overrule everything.
- os_setenv("LC_ALL", "", 1);
-
- if (what != LC_TIME && what != LC_COLLATE) {
- // Tell gettext() what to translate to. It apparently doesn't
- // use the currently effective locale.
- if (what == LC_ALL) {
- os_setenv("LANG", name, 1);
-
- // Clear $LANGUAGE because GNU gettext uses it.
- os_setenv("LANGUAGE", "", 1);
- }
- if (what != LC_CTYPE) {
- os_setenv("LC_MESSAGES", name, 1);
- set_helplang_default(name);
- }
- }
-
- // Set v:lang, v:lc_time, v:collate and v:ctype to the final result.
- set_lang_var();
- maketitle();
- }
- }
-}
-
-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 or,
-/// NULL in case of error.
-static char **find_locales(void)
-{
- garray_T locales_ga;
- 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 *locale_a = (char *)get_cmd_output((char_u *)"locale -a", NULL,
- kShellOptSilent, NULL);
- if (locale_a == NULL) {
- return NULL;
- }
- ga_init(&locales_ga, sizeof(char_u *), 20);
-
- // Transform locale_a string where each locale is separated by "\n"
- // into an array of locale strings.
- loc = os_strtok(locale_a, "\n", &saveptr);
-
- while (loc != NULL) {
- 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 locales_ga.ga_data;
-}
-# endif
-
-/// Lazy initialization of all available locales.
-static void init_locales(void)
-{
-# ifndef WIN32
- if (!did_init_locales) {
- did_init_locales = true;
- locales = find_locales();
- }
-# endif
-}
-
-# if defined(EXITFREE)
-void free_locales(void)
-{
- int i;
- if (locales != NULL) {
- for (i = 0; locales[i] != NULL; i++) {
- xfree(locales[i]);
- }
- XFREE_CLEAR(locales);
- }
-}
-
-# endif
-
-/// Function given to ExpandGeneric() to obtain the possible arguments of the
-/// ":language" command.
-char *get_lang_arg(expand_T *xp, int idx)
-{
- if (idx == 0) {
- return "messages";
- }
- if (idx == 1) {
- return "ctype";
- }
- if (idx == 2) {
- return "time";
- }
- if (idx == 3) {
- return "collate";
- }
-
- init_locales();
- if (locales == NULL) {
- return NULL;
- }
- return locales[idx - 4];
-}
-
-/// Function given to ExpandGeneric() to obtain the available locales.
-char *get_locales(expand_T *xp, int idx)
-{
- init_locales();
- if (locales == NULL) {
- return NULL;
- }
- return locales[idx];
-}
-
-#endif
-
static void script_host_execute(char *name, exarg_T *eap)
{
size_t len;
@@ -1166,45 +849,46 @@ void ex_drop(exarg_T *eap)
// edited in a window yet. It's like ":tab all" but without closing
// windows or tabs.
ex_all(eap);
- } else {
- // ":drop file ...": Edit the first argument. Jump to an existing
- // window if possible, edit in current window if the current buffer
- // can be abandoned, otherwise open a new window.
- buf = buflist_findnr(ARGLIST[0].ae_fnum);
+ return;
+ }
- FOR_ALL_TAB_WINDOWS(tp, wp) {
- if (wp->w_buffer == buf) {
- goto_tabpage_win(tp, wp);
- curwin->w_arg_idx = 0;
- if (!bufIsChanged(curbuf)) {
- const int save_ar = curbuf->b_p_ar;
-
- // reload the file if it is newer
- curbuf->b_p_ar = 1;
- buf_check_timestamp(curbuf);
- curbuf->b_p_ar = save_ar;
- }
- return;
+ // ":drop file ...": Edit the first argument. Jump to an existing
+ // window if possible, edit in current window if the current buffer
+ // can be abandoned, otherwise open a new window.
+ buf = buflist_findnr(ARGLIST[0].ae_fnum);
+
+ FOR_ALL_TAB_WINDOWS(tp, wp) {
+ if (wp->w_buffer == buf) {
+ goto_tabpage_win(tp, wp);
+ curwin->w_arg_idx = 0;
+ if (!bufIsChanged(curbuf)) {
+ const int save_ar = curbuf->b_p_ar;
+
+ // reload the file if it is newer
+ curbuf->b_p_ar = 1;
+ buf_check_timestamp(curbuf);
+ curbuf->b_p_ar = save_ar;
}
+ return;
}
+ }
- // Check whether the current buffer is changed. If so, we will need
- // to split the current window or data could be lost.
- // Skip the check if the 'hidden' option is set, as in this case the
- // buffer won't be lost.
- if (!buf_hide(curbuf)) {
- emsg_off++;
- split = check_changed(curbuf, CCGD_AW | CCGD_EXCMD);
- emsg_off--;
- }
+ // Check whether the current buffer is changed. If so, we will need
+ // to split the current window or data could be lost.
+ // Skip the check if the 'hidden' option is set, as in this case the
+ // buffer won't be lost.
+ if (!buf_hide(curbuf)) {
+ emsg_off++;
+ split = check_changed(curbuf, CCGD_AW | CCGD_EXCMD);
+ emsg_off--;
+ }
- // Fake a ":sfirst" or ":first" command edit the first argument.
- if (split) {
- eap->cmdidx = CMD_sfirst;
- eap->cmd[0] = 's';
- } else {
- eap->cmdidx = CMD_first;
- }
- ex_rewind(eap);
+ // Fake a ":sfirst" or ":first" command edit the first argument.
+ if (split) {
+ eap->cmdidx = CMD_sfirst;
+ eap->cmd[0] = 's';
+ } else {
+ eap->cmdidx = CMD_first;
}
+ ex_rewind(eap);
}
diff --git a/src/nvim/ex_cmds2.h b/src/nvim/ex_cmds2.h
index e454a30028..3a41e105f3 100644
--- a/src/nvim/ex_cmds2.h
+++ b/src/nvim/ex_cmds2.h
@@ -1,8 +1,6 @@
#ifndef NVIM_EX_CMDS2_H
#define NVIM_EX_CMDS2_H
-#include <stdbool.h>
-
#include "nvim/ex_cmds_defs.h"
//
diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h
index e80e47bcff..629aaf14cf 100644
--- a/src/nvim/ex_cmds_defs.h
+++ b/src/nvim/ex_cmds_defs.h
@@ -6,7 +6,7 @@
#include "nvim/eval/typval.h"
#include "nvim/normal.h"
-#include "nvim/pos.h" // for linenr_T
+#include "nvim/pos.h"
#include "nvim/regexp_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -62,7 +62,7 @@
#define EX_FLAGS 0x200000u // allow flags after count in argument
#define EX_LOCK_OK 0x1000000u // command can be executed when textlock is
// set; when missing disallows editing another
- // buffer when current buffer is locked
+ // buffer when curbuf->b_ro_locked is set
#define EX_KEEPSCRIPT 0x4000000u // keep sctx of where command was invoked
#define EX_PREVIEW 0x8000000u // allow incremental command preview
#define EX_FILES (EX_XFILE | EX_EXTRA) // multiple extra files allowed
@@ -188,7 +188,7 @@ struct exarg {
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
- int forceit; ///< TRUE if ! present
+ int forceit; ///< true if ! present
int addr_count; ///< the number of addresses given
linenr_T line1; ///< the first line number
linenr_T line2; ///< the second line number or count
@@ -196,12 +196,13 @@ struct exarg {
int flags; ///< extra flags after count: EXFLAG_
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"
+ int append; ///< true with ":w >>file" command
+ int usefilter; ///< true with ":w !command" and ":r!command"
int amount; ///< number of '>' or '<' for shift command
int regname; ///< register name (NUL if none)
int force_bin; ///< 0, FORCE_BIN or FORCE_NOBIN
int read_edit; ///< ++edit argument
+ int mkdir_p; ///< ++p argument
int force_ff; ///< ++ff= argument (first char of argument)
int force_enc; ///< ++enc= argument (index in cmd[])
int bad_char; ///< BAD_KEEP, BAD_DROP or replacement byte
@@ -230,13 +231,15 @@ struct expand {
sctx_T xp_script_ctx; // SCTX for completion function
int xp_backslash; // one of the XP_BS_ values
#ifndef BACKSLASH_IN_FILENAME
- int xp_shell; // TRUE for a shell command, more
+ int xp_shell; // true for a shell command, more
// characters need to be escaped
#endif
int xp_numfiles; // number of files found by file name completion
int xp_col; // cursor position in line
char **xp_files; // list of files
char *xp_line; // text being completed
+#define EXPAND_BUF_LEN 256
+ char xp_buf[EXPAND_BUF_LEN]; // buffer for returned match
};
// values for xp_backslash
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index c2e7b96a53..5d271e4ef6 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -4,58 +4,57 @@
// ex_docmd.c: functions for executing an Ex command line.
#include <assert.h>
+#include <ctype.h>
#include <inttypes.h>
+#include <limits.h>
#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include "auto/config.h"
#include "nvim/arglist.h"
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/change.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
-#include "nvim/cmdhist.h"
#include "nvim/cursor.h"
#include "nvim/debugger.h"
-#include "nvim/diff.h"
#include "nvim/digraph.h"
#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/eval/userfunc.h"
-#include "nvim/eval/vars.h"
-#include "nvim/event/rstream.h"
-#include "nvim/event/wstream.h"
+#include "nvim/event/loop.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_cmds2.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
#include "nvim/ex_getln.h"
-#include "nvim/ex_session.h"
#include "nvim/file_search.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
-#include "nvim/func_attr.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
#include "nvim/globals.h"
-#include "nvim/hardcopy.h"
-#include "nvim/help.h"
+#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
-#include "nvim/if_cscope.h"
#include "nvim/input.h"
#include "nvim/keycodes.h"
-#include "nvim/lua/executor.h"
+#include "nvim/macros.h"
#include "nvim/main.h"
-#include "nvim/mapping.h"
#include "nvim/mark.h"
-#include "nvim/match.h"
#include "nvim/mbyte.h"
+#include "nvim/memfile_defs.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
-#include "nvim/menu.h"
#include "nvim/message.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
@@ -65,28 +64,25 @@
#include "nvim/optionstr.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
-#include "nvim/os/time.h"
-#include "nvim/os_unix.h"
+#include "nvim/os/shell.h"
#include "nvim/path.h"
#include "nvim/popupmenu.h"
+#include "nvim/pos.h"
#include "nvim/profile.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
+#include "nvim/runtime.h"
+#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/shada.h"
-#include "nvim/sign.h"
-#include "nvim/spell.h"
-#include "nvim/spellfile.h"
#include "nvim/state.h"
+#include "nvim/statusline.h"
#include "nvim/strings.h"
-#include "nvim/syntax.h"
#include "nvim/tag.h"
-#include "nvim/terminal.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
-#include "nvim/undo_defs.h"
#include "nvim/usercmd.h"
-#include "nvim/version.h"
#include "nvim/vim.h"
#include "nvim/window.h"
@@ -94,6 +90,12 @@ 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_source_file_name_to_substitute_for_sfile[]
+ = N_("E498: no :source file name to substitute for \"<sfile>\"");
+static char e_no_call_stack_to_substitute_for_stack[]
+ = N_("E489: no call stack to substitute for \"<stack>\"");
+static char e_no_script_file_name_to_substitute_for_script[]
+ = N_("E1274: No script file name to substitute for \"<script>\"");
static int quitmore = 0;
static bool ex_pressedreturn = false;
@@ -127,6 +129,7 @@ struct dbg_stuff {
char *vv_throwpoint;
int did_emsg;
int got_int;
+ bool did_throw;
int need_rethrow;
int check_cstack;
except_T *current_exception;
@@ -158,6 +161,7 @@ static void save_dbg_stuff(struct dbg_stuff *dsp)
// Necessary for debugging an inactive ":catch", ":finally", ":endtry".
dsp->did_emsg = did_emsg; did_emsg = false;
dsp->got_int = got_int; got_int = false;
+ dsp->did_throw = did_throw; did_throw = false;
dsp->need_rethrow = need_rethrow; need_rethrow = false;
dsp->check_cstack = check_cstack; check_cstack = false;
dsp->current_exception = current_exception; current_exception = NULL;
@@ -173,6 +177,7 @@ static void restore_dbg_stuff(struct dbg_stuff *dsp)
(void)v_throwpoint(dsp->vv_throwpoint);
did_emsg = dsp->did_emsg;
got_int = dsp->got_int;
+ did_throw = dsp->did_throw;
need_rethrow = dsp->need_rethrow;
check_cstack = dsp->check_cstack;
current_exception = dsp->current_exception;
@@ -243,8 +248,8 @@ void do_exmode(void)
RedrawingDisabled--;
no_wait_return--;
- redraw_all_later(NOT_VALID);
- update_screen(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
+ update_screen();
need_wait_return = false;
msg_scroll = save_msg_scroll;
}
@@ -364,7 +369,7 @@ int do_cmdline(char *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 = (char *)func_name(real_cookie);
+ fname = func_name(real_cookie);
breakpoint = func_breakpoint(real_cookie);
dbg_tick = func_dbg_tick(real_cookie);
} else if (getline_equal(fgetline, cookie, getsourceline)) {
@@ -390,7 +395,8 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
initial_trylevel = trylevel;
- current_exception = NULL;
+ // "did_throw" will be set to true when an exception is being thrown.
+ did_throw = false;
// "did_emsg" will be set to true when emsg() is used, in which case we
// cancel the whole command line, and any if/endif or loop.
// If force_abort is set, we cancel everything.
@@ -455,7 +461,7 @@ int do_cmdline(char *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),
- (char_u *)fname, SOURCING_LNUM);
+ fname, SOURCING_LNUM);
*dbg_tick = debug_tick;
}
@@ -464,10 +470,10 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
// Did we encounter a breakpoint?
if (breakpoint != NULL && *breakpoint != 0 && *breakpoint <= SOURCING_LNUM) {
- dbg_breakpoint((char_u *)fname, SOURCING_LNUM);
+ dbg_breakpoint(fname, SOURCING_LNUM);
// Find next breakpoint.
*breakpoint = dbg_find_breakpoint(getline_equal(fgetline, cookie, getsourceline),
- (char_u *)fname, SOURCING_LNUM);
+ fname, SOURCING_LNUM);
*dbg_tick = debug_tick;
}
if (do_profiling == PROF_YES) {
@@ -509,7 +515,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
cstack.cs_idx <
0 ? 0 : (cstack.cs_idx + 1) * 2,
true)) == NULL) {
- // Don't call wait_return for aborted command line. The NULL
+ // Don't call wait_return() for aborted command line. The NULL
// returned for the end of a sourced file or executed function
// doesn't do this.
if (KeyTyped && !(flags & DOCMD_REPEAT)) {
@@ -524,7 +530,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
if (flags & DOCMD_KEEPLINE) {
xfree(repeat_cmdline);
if (count == 0) {
- repeat_cmdline = vim_strsave((char_u *)next_cmdline);
+ repeat_cmdline = xstrdup(next_cmdline);
} else {
repeat_cmdline = NULL;
}
@@ -618,7 +624,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
// not to use a cs_line[] from an entry that isn't a ":while"
// or ":for": It would make "current_line" invalid and can
// cause a crash.
- if (!did_emsg && !got_int && !current_exception
+ if (!did_emsg && !got_int && !did_throw
&& cstack.cs_idx >= 0
&& (cstack.cs_flags[cstack.cs_idx]
& (CSF_WHILE | CSF_FOR))
@@ -631,9 +637,8 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
// Check for the next breakpoint at or after the ":while"
// or ":for".
- if (breakpoint != NULL) {
- *breakpoint = dbg_find_breakpoint(getline_equal(fgetline, cookie, getsourceline),
- (char_u *)fname,
+ if (breakpoint != NULL && lines_ga.ga_len > current_line) {
+ *breakpoint = dbg_find_breakpoint(getline_equal(fgetline, cookie, getsourceline), fname,
((wcmd_T *)lines_ga.ga_data)[current_line].lnum - 1);
*dbg_tick = debug_tick;
}
@@ -660,7 +665,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
current_line = 0;
}
- // A ":finally" makes did_emsg, got_int and current_exception pending for
+ // A ":finally" makes did_emsg, got_int and did_throw pending for
// being restored at the ":endtry". Reset them here and set the
// ACTIVE and FINALLY flags, so that the finally clause gets executed.
// This includes the case where a missing ":endif", ":endwhile" or
@@ -669,9 +674,8 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
cstack.cs_lflags &= ~CSL_HAD_FINA;
report_make_pending((cstack.cs_pending[cstack.cs_idx]
& (CSTP_ERROR | CSTP_INTERRUPT | CSTP_THROW)),
- current_exception);
- did_emsg = got_int = false;
- current_exception = NULL;
+ did_throw ? current_exception : NULL);
+ did_emsg = got_int = did_throw = false;
cstack.cs_flags[cstack.cs_idx] |= CSF_ACTIVE | CSF_FINALLY;
}
@@ -684,7 +688,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
// exception, cancel everything. If it is left normally, reset
// force_abort to get the non-EH compatible abortion behavior for
// the rest of the script.
- if (trylevel == 0 && !did_emsg && !got_int && !current_exception) {
+ if (trylevel == 0 && !did_emsg && !got_int && !did_throw) {
force_abort = false;
}
@@ -698,7 +702,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
// - didn't get an error message or lines are not typed
// - there is a command after '|', inside a :if, :while, :for or :try, or
// looping for ":source" command or function call.
- } while (!((got_int || (did_emsg && force_abort) || current_exception)
+ } while (!((got_int || (did_emsg && force_abort) || did_throw)
&& cstack.cs_trylevel == 0)
&& !(did_emsg
// Keep going when inside try/catch, so that the error can be
@@ -718,7 +722,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
if (cstack.cs_idx >= 0) {
// If a sourced file or executed function ran to its end, report the
// unclosed conditional.
- if (!got_int && !current_exception
+ if (!got_int && !did_throw && !aborting()
&& ((getline_equal(fgetline, cookie, getsourceline)
&& !source_finished(fgetline, cookie))
|| (getline_equal(fgetline, cookie, get_func_line)
@@ -761,52 +765,8 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
// conditional, discard the uncaught exception, disable the conversion
// of interrupts or errors to exceptions, and ensure that no more
// commands are executed.
- if (current_exception) {
- char *p = NULL;
- msglist_T *messages = NULL;
- msglist_T *next;
-
- // If the uncaught exception is a user exception, report it as an
- // error. If it is an error exception, display the saved error
- // message now. For an interrupt exception, do nothing; the
- // interrupt message is given elsewhere.
- switch (current_exception->type) {
- case ET_USER:
- vim_snprintf((char *)IObuff, IOSIZE,
- _("E605: Exception not caught: %s"),
- current_exception->value);
- p = (char *)vim_strsave(IObuff);
- break;
- case ET_ERROR:
- messages = current_exception->messages;
- current_exception->messages = NULL;
- break;
- case ET_INTERRUPT:
- break;
- }
-
- estack_push(ETYPE_EXCEPT, current_exception->throw_name, current_exception->throw_lnum);
- current_exception->throw_name = NULL;
-
- discard_current_exception(); // uses IObuff if 'verbose'
- suppress_errthrow = true;
- force_abort = true;
- msg_ext_set_kind("emsg"); // kind=emsg for :throw, exceptions. #9993
-
- if (messages != NULL) {
- do {
- next = messages->next;
- emsg(messages->msg);
- xfree(messages->msg);
- xfree(messages);
- messages = next;
- } while (messages != NULL);
- } else if (p != NULL) {
- emsg(p);
- xfree(p);
- }
- xfree(SOURCING_NAME);
- estack_pop();
+ if (did_throw) {
+ handle_did_throw();
} else if (got_int || (did_emsg && force_abort)) {
// On an interrupt or an aborting error not converted to an exception,
// disable the conversion of errors to exceptions. (Interrupts are not
@@ -823,14 +783,14 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
// cstack belongs to the same function or, respectively, script file, it
// will have to be checked for finally clauses to be executed due to the
// ":return" or ":finish". This is done in do_one_cmd().
- if (current_exception) {
+ if (did_throw) {
need_rethrow = true;
}
if ((getline_equal(fgetline, cookie, getsourceline)
&& ex_nesting_level > source_level(real_cookie))
|| (getline_equal(fgetline, cookie, get_func_line)
&& ex_nesting_level > func_level(real_cookie) + 1)) {
- if (!current_exception) {
+ if (!did_throw) {
check_cstack = true;
}
} else {
@@ -844,8 +804,8 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
|| getline_equal(fgetline, cookie, get_func_line))
&& ex_nesting_level + 1 <= debug_break_level) {
do_debug(getline_equal(fgetline, cookie, getsourceline)
- ? (char_u *)_("End of sourced file")
- : (char_u *)_("End of function"));
+ ? _("End of sourced file")
+ : _("End of function"));
}
}
@@ -881,7 +841,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
need_wait_return = false;
msg_didany = false; // don't wait when restarting edit
} else if (need_wait_return) {
- // The msg_start() above clears msg_didout. The wait_return we do
+ // The msg_start() above clears msg_didout. The wait_return() we do
// here should not overwrite the command that may be shown before
// doing that.
msg_didout |= msg_didout_before_start;
@@ -896,6 +856,57 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
return retval;
}
+/// Handle when "did_throw" is set after executing commands.
+void handle_did_throw(void)
+{
+ assert(current_exception != NULL);
+ char *p = NULL;
+ msglist_T *messages = NULL;
+
+ // If the uncaught exception is a user exception, report it as an
+ // error. If it is an error exception, display the saved error
+ // message now. For an interrupt exception, do nothing; the
+ // interrupt message is given elsewhere.
+ switch (current_exception->type) {
+ case ET_USER:
+ vim_snprintf(IObuff, IOSIZE,
+ _("E605: Exception not caught: %s"),
+ current_exception->value);
+ p = xstrdup(IObuff);
+ break;
+ case ET_ERROR:
+ messages = current_exception->messages;
+ current_exception->messages = NULL;
+ break;
+ case ET_INTERRUPT:
+ break;
+ }
+
+ estack_push(ETYPE_EXCEPT, current_exception->throw_name, current_exception->throw_lnum);
+ current_exception->throw_name = NULL;
+
+ discard_current_exception(); // uses IObuff if 'verbose'
+ suppress_errthrow = true;
+ force_abort = true;
+ msg_ext_set_kind("emsg"); // kind=emsg for :throw, exceptions. #9993
+
+ if (messages != NULL) {
+ do {
+ msglist_T *next = messages->next;
+ emsg(messages->msg);
+ xfree(messages->msg);
+ xfree(messages->sfile);
+ xfree(messages);
+ messages = next;
+ } while (messages != NULL);
+ } else if (p != NULL) {
+ emsg(p);
+ xfree(p);
+ }
+ xfree(SOURCING_NAME);
+ estack_pop();
+}
+
/// Obtain a line when inside a ":while" or ":for" loop.
static char *get_loop_line(int c, void *cookie, int indent, bool do_concat)
{
@@ -908,7 +919,7 @@ static char *get_loop_line(int c, void *cookie, int indent, bool do_concat)
char *line;
// First time inside the ":while"/":for": get line normally.
if (cp->getline == NULL) {
- line = (char *)getcmdline(c, 0L, indent, do_concat);
+ line = getcmdline(c, 0L, indent, do_concat);
} else {
line = cp->getline(c, cp->cookie, indent, do_concat);
}
@@ -1050,7 +1061,7 @@ static int current_tab_nr(tabpage_T *tab)
#define LAST_TAB_NR current_tab_nr(NULL)
/// Figure out the address type for ":wincmd".
-static void get_wincmd_addr_type(char *arg, exarg_T *eap)
+static void get_wincmd_addr_type(const char *arg, exarg_T *eap)
{
switch (*arg) {
case 'S':
@@ -1290,9 +1301,9 @@ static void parse_register(exarg_T *eap)
// 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));
+ set_expr_line(xstrdup(eap->arg));
}
- eap->arg += STRLEN(eap->arg);
+ eap->arg += strlen(eap->arg);
}
eap->arg = skipwhite(eap->arg);
}
@@ -1329,6 +1340,20 @@ static int parse_count(exarg_T *eap, char **errormsg, bool validate)
|| ascii_iswhite(*p))) {
long n = getdigits_long(&eap->arg, false, -1);
eap->arg = skipwhite(eap->arg);
+
+ if (eap->args != NULL) {
+ assert(eap->argc > 0 && eap->arg >= eap->args[0]);
+ // If eap->arg is still pointing to the first argument, just make eap->args[0] point to the
+ // same location. This is needed for usecases like vim.cmd.sleep('10m'). If eap->arg is
+ // pointing outside the first argument, shift arguments by 1.
+ if (eap->arg < eap->args[0] + eap->arglens[0]) {
+ eap->arglens[0] -= (size_t)(eap->arg - eap->args[0]);
+ eap->args[0] = eap->arg;
+ } else {
+ shift_cmd_args(eap);
+ }
+ }
+
if (n <= 0 && (eap->argt & EX_ZEROR) == 0) {
if (errormsg != NULL) {
*errormsg = _(e_zerocount);
@@ -1361,6 +1386,13 @@ bool is_cmd_ni(cmdidx_T cmdidx)
bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **errormsg)
{
char *after_modifier = NULL;
+ bool retval = false;
+ // parsing the command modifiers may set ex_pressedreturn
+ const bool save_ex_pressedreturn = ex_pressedreturn;
+ // parsing the command range may require moving the cursor
+ const pos_T save_cursor = curwin->w_cursor;
+ // parsing the command range may set the last search pattern
+ save_last_search_pattern();
// Initialize cmdinfo
CLEAR_POINTER(cmdinfo);
@@ -1375,13 +1407,10 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er
.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;
+ goto end;
}
- ex_pressedreturn = save_ex_pressedreturn;
after_modifier = eap->cmd;
// Save location after command modifiers
@@ -1394,21 +1423,21 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er
char *p = find_ex_command(eap, NULL);
if (p == NULL) {
*errormsg = _(e_ambiguous_use_of_user_defined_command);
- goto err;
+ goto end;
}
// 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;
+ if (parse_cmd_address(eap, errormsg, true) == FAIL) {
+ goto end;
}
// 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;
+ goto end;
}
// Fail if command is invalid
if (eap->cmdidx == CMD_SIZE) {
@@ -1416,8 +1445,8 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er
// 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;
+ *errormsg = IObuff;
+ goto end;
}
// Correctly set 'forceit' for commands
@@ -1456,12 +1485,12 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er
// 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;
+ goto end;
}
// 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;
+ goto end;
}
// Set default range for command if required
if ((eap->argt & EX_DFLALL) && eap->addr_count == 0) {
@@ -1471,7 +1500,7 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er
// Parse register and count
parse_register(eap);
if (parse_count(eap, errormsg, false) == FAIL) {
- goto err;
+ goto end;
}
// Remove leading whitespace and colon from next command
@@ -1487,10 +1516,39 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er
cmdinfo->magic.bar = true;
}
- return true;
-err:
- undo_cmdmod(&cmdinfo->cmdmod);
- return false;
+ retval = true;
+end:
+ if (!retval) {
+ undo_cmdmod(&cmdinfo->cmdmod);
+ }
+ ex_pressedreturn = save_ex_pressedreturn;
+ curwin->w_cursor = save_cursor;
+ restore_last_search_pattern();
+ return retval;
+}
+
+// Shift Ex-command arguments to the right.
+static void shift_cmd_args(exarg_T *eap)
+{
+ assert(eap->args != NULL && eap->argc > 0);
+
+ char **oldargs = eap->args;
+ size_t *oldarglens = eap->arglens;
+
+ eap->argc--;
+ eap->args = eap->argc > 0 ? xcalloc(eap->argc, sizeof(char *)) : NULL;
+ eap->arglens = eap->argc > 0 ? xcalloc(eap->argc, sizeof(size_t)) : NULL;
+
+ for (size_t i = 0; i < eap->argc; i++) {
+ eap->args[i] = oldargs[i + 1];
+ eap->arglens[i] = oldarglens[i + 1];
+ }
+
+ // If there are no arguments, make eap->arg point to the end of string.
+ eap->arg = (eap->argc > 0 ? eap->args[0] : (oldargs[0] + oldarglens[0]));
+
+ xfree(oldargs);
+ xfree(oldarglens);
}
static int execute_cmd0(int *retv, exarg_T *eap, char **errormsg, bool preview)
@@ -1517,7 +1575,7 @@ static int execute_cmd0(int *retv, exarg_T *eap, char **errormsg, bool preview)
|| eap->cmdidx == CMD_bunload) {
p = skiptowhite_esc(eap->arg);
} else {
- p = eap->arg + STRLEN(eap->arg);
+ p = eap->arg + strlen(eap->arg);
while (p > eap->arg && ascii_iswhite(p[-1])) {
p--;
}
@@ -1532,15 +1590,7 @@ static int execute_cmd0(int *retv, exarg_T *eap, char **errormsg, bool preview)
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];
+ shift_cmd_args(eap);
}
if (eap->line2 < 0) { // failed
return FAIL;
@@ -1589,6 +1639,7 @@ int execute_cmd(exarg_T *eap, CmdParseInfo *cmdinfo, bool preview)
char *errormsg = NULL;
int retv = 0;
+#undef ERROR
#define ERROR(msg) \
do { \
errormsg = msg; \
@@ -1639,6 +1690,11 @@ int execute_cmd(exarg_T *eap, CmdParseInfo *cmdinfo, bool preview)
(void)hasFolding(eap->line2, NULL, &eap->line2);
}
+ // Use first argument as count when possible
+ if (parse_count(eap, &errormsg, true) == FAIL) {
+ goto end;
+ }
+
// Execute the command
execute_cmd0(&retv, eap, &errormsg, preview);
@@ -1660,7 +1716,7 @@ static void profile_cmd(const exarg_T *eap, cstack_T *cstack, LineGetter fgetlin
&& (!eap->skip || cstack->cs_idx == 0
|| (cstack->cs_idx > 0
&& (cstack->cs_flags[cstack->cs_idx - 1] & CSF_ACTIVE)))) {
- int skip = did_emsg || got_int || current_exception;
+ bool skip = did_emsg || got_int || did_throw;
if (eap->cmdidx == CMD_catch) {
skip = !skip && !(cstack->cs_idx >= 0
@@ -1738,6 +1794,7 @@ static bool skip_cmd(const exarg_T *eap)
case CMD_filter:
case CMD_help:
case CMD_hide:
+ case CMD_horizontal:
case CMD_ijump:
case CMD_ilist:
case CMD_isearch:
@@ -1775,6 +1832,7 @@ static bool skip_cmd(const exarg_T *eap)
case CMD_throw:
case CMD_tilde:
case CMD_topleft:
+ case CMD_trust:
case CMD_unlet:
case CMD_unlockvar:
case CMD_verbose:
@@ -1857,7 +1915,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter
ea.skip = (did_emsg
|| got_int
- || current_exception
+ || did_throw
|| (cstack->cs_idx >= 0
&& !(cstack->cs_flags[cstack->cs_idx] & CSF_ACTIVE)));
@@ -1873,9 +1931,11 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter
profile_cmd(&ea, cstack, fgetline, cookie);
- // May go to debug mode. If this happens and the ">quit" debug command is
- // used, throw an interrupt exception and skip the next command.
- dbg_check_breakpoint(&ea);
+ if (!exiting) {
+ // May go to debug mode. If this happens and the ">quit" debug command is
+ // used, throw an interrupt exception and skip the next command.
+ dbg_check_breakpoint(&ea);
+ }
if (!ea.skip && got_int) {
ea.skip = true;
(void)do_intthrow(cstack);
@@ -1910,7 +1970,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter
// If we got a line, but no command, then go to the line.
// If we find a '|' or '\n' we set ea.nextcmd.
if (*ea.cmd == NUL || *ea.cmd == '"'
- || (ea.nextcmd = (char *)check_nextcmd((char_u *)ea.cmd)) != NULL) {
+ || (ea.nextcmd = check_nextcmd(ea.cmd)) != NULL) {
// strange vi behaviour:
// ":3" jumps to line 3
// ":3|..." prints line 3
@@ -1978,7 +2038,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter
if (!(flags & DOCMD_VERBOSE)) {
append_command(cmdname);
}
- errormsg = (char *)IObuff;
+ errormsg = IObuff;
did_emsg_syntax = true;
verify_command(cmdname);
}
@@ -2247,9 +2307,9 @@ doend:
if (errormsg != NULL && *errormsg != NUL && !did_emsg) {
if (flags & DOCMD_VERBOSE) {
- if (errormsg != (char *)IObuff) {
+ if (errormsg != IObuff) {
STRCPY(IObuff, errormsg);
- errormsg = (char *)IObuff;
+ errormsg = IObuff;
}
append_command(*ea.cmdlinep);
}
@@ -2419,8 +2479,12 @@ int parse_command_modifiers(exarg_T *eap, char **errormsg, cmdmod_T *cmod, bool
continue;
}
- // ":hide" and ":hide | cmd" are not modifiers
case 'h':
+ if (checkforcmd(&eap->cmd, "horizontal", 3)) {
+ cmod->cmod_split |= WSP_HOR;
+ continue;
+ }
+ // ":hide" and ":hide | cmd" are not modifiers
if (p != eap->cmd || !checkforcmd(&p, "hide", 3)
|| *p == NUL || ends_excmd(*p)) {
break;
@@ -2565,7 +2629,7 @@ static void apply_cmdmod(cmdmod_T *cmod)
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);
+ cmod->cmod_save_ei = xstrdup(p_ei);
set_string_option_direct("ei", -1, "all", OPT_FREE, SID_NONE);
}
}
@@ -2587,7 +2651,7 @@ void undo_cmdmod(cmdmod_T *cmod)
if (cmod->cmod_save_ei != NULL) {
// Restore 'eventignore' to the value before ":noautocmd".
set_string_option_direct("ei", -1, cmod->cmod_save_ei, OPT_FREE, SID_NONE);
- free_string_option((char_u *)cmod->cmod_save_ei);
+ free_string_option(cmod->cmod_save_ei);
cmod->cmod_save_ei = NULL;
}
@@ -2779,7 +2843,7 @@ theend:
/// @param pp start of command
/// @param cmd name of command
/// @param len required length
-bool checkforcmd(char **pp, char *cmd, int len)
+bool checkforcmd(char **pp, const char *cmd, int len)
{
int i;
@@ -2788,7 +2852,7 @@ bool checkforcmd(char **pp, char *cmd, int len)
break;
}
}
- if (i >= len && !isalpha((*pp)[i])) {
+ if (i >= len && !ASCII_ISALPHA((*pp)[i])) {
*pp = skipwhite(*pp + i);
return true;
}
@@ -2800,24 +2864,24 @@ bool checkforcmd(char **pp, char *cmd, int len)
/// invisible otherwise.
static void append_command(char *cmd)
{
- size_t len = STRLEN(IObuff);
+ 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);
+ d = IObuff + IOSIZE - 100;
+ d -= utf_head_off(IObuff, d);
STRCPY(d, "...");
}
STRCAT(IObuff, ": ");
- 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) {
+ d = IObuff + strlen(IObuff);
+ while (*s != NUL && d - IObuff + 5 < IOSIZE) {
+ if ((uint8_t)s[0] == 0xc2 && (uint8_t)s[1] == 0xa0) {
s += 2;
STRCPY(d, "<a0>");
d += 4;
- } else if ((char_u *)d - IObuff + utfc_ptr2len(s) + 1 >= IOSIZE) {
+ } else if (d - IObuff + utfc_ptr2len(s) + 1 >= IOSIZE) {
break;
} else {
mb_copy_char((const char **)&s, &d);
@@ -2826,6 +2890,33 @@ static void append_command(char *cmd)
*d = NUL;
}
+/// Return true and set "*idx" if "p" points to a one letter command.
+/// - The 'k' command can directly be followed by any character.
+/// - The 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r'
+/// but :sre[wind] is another command, as are :scr[iptnames],
+/// :scs[cope], :sim[alt], :sig[ns] and :sil[ent].
+static int one_letter_cmd(const char *p, cmdidx_T *idx)
+{
+ if (*p == 'k') {
+ *idx = CMD_k;
+ return true;
+ }
+ if (p[0] == 's'
+ && ((p[1] == 'c'
+ && (p[2] == NUL
+ || (p[2] != 's' && p[2] != 'r'
+ && (p[3] == NUL
+ || (p[3] != 'i' && p[4] != 'p')))))
+ || p[1] == 'g'
+ || (p[1] == 'i' && p[2] != 'm' && p[2] != 'l' && p[2] != 'g')
+ || p[1] == 'I'
+ || (p[1] == 'r' && p[2] != 'e'))) {
+ *idx = CMD_substitute;
+ return true;
+ }
+ return false;
+}
+
/// 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.
@@ -2836,27 +2927,8 @@ char *find_ex_command(exarg_T *eap, int *full)
FUNC_ATTR_NONNULL_ARG(1)
{
// Isolate the command and search for it in the command table.
- // Exceptions:
- // - the 'k' command can directly be followed by any character.
- // - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r'
- // but :sre[wind] is another command, as are :scr[iptnames],
- // :scs[cope], :sim[alt], :sig[ns] and :sil[ent].
- // - the "d" command can directly be followed by 'l' or 'p' flag.
char *p = eap->cmd;
- if (*p == 'k') {
- eap->cmdidx = CMD_k;
- p++;
- } else if (p[0] == 's'
- && ((p[1] == 'c'
- && (p[2] == NUL
- || (p[2] != 's' && p[2] != 'r'
- && (p[3] == NUL
- || (p[3] != 'i' && p[4] != 'p')))))
- || p[1] == 'g'
- || (p[1] == 'i' && p[2] != 'm' && p[2] != 'l' && p[2] != 'g')
- || p[1] == 'I'
- || (p[1] == 'r' && p[2] != 'e'))) {
- eap->cmdidx = CMD_substitute;
+ if (one_letter_cmd(p, &eap->cmdidx)) {
p++;
} else {
while (ASCII_ISALPHA(*p)) {
@@ -2870,10 +2942,11 @@ char *find_ex_command(exarg_T *eap, int *full)
}
// check for non-alpha command
- if (p == eap->cmd && vim_strchr("@!=><&~#", *p) != NULL) {
+ if (p == eap->cmd && vim_strchr("@!=><&~#", (uint8_t)(*p)) != NULL) {
p++;
}
int len = (int)(p - eap->cmd);
+ // The "d" command can directly be followed by 'l' or 'p' flag.
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'.
@@ -2894,7 +2967,7 @@ char *find_ex_command(exarg_T *eap, int *full)
}
if (ASCII_ISLOWER(eap->cmd[0])) {
- const int c1 = (char_u)eap->cmd[0];
+ const int c1 = (uint8_t)eap->cmd[0];
const int c2 = len == 1 ? NUL : eap->cmd[1];
if (command_count != CMD_SIZE) {
@@ -2917,7 +2990,7 @@ char *find_ex_command(exarg_T *eap, int *full)
for (; (int)eap->cmdidx < CMD_SIZE;
eap->cmdidx = (cmdidx_T)((int)eap->cmdidx + 1)) {
- if (STRNCMP(cmdnames[(int)eap->cmdidx].cmd_name, 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) {
@@ -3038,7 +3111,7 @@ int cmd_exists(const char *const name)
}
/// "fullcommand" function
-void f_fullcommand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_fullcommand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char *name = argvars[0].vval.v_string;
@@ -3062,19 +3135,20 @@ void f_fullcommand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
- 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);
+ rettv->vval.v_string = xstrdup(IS_USER_CMDIDX(ea.cmdidx)
+ ? get_user_command_name(ea.useridx, ea.cmdidx)
+ : cmdnames[ea.cmdidx].cmd_name);
}
cmdidx_T excmd_get_cmdidx(const char *cmd, size_t len)
{
cmdidx_T idx;
- for (idx = (cmdidx_T)0; (int)idx < (int)CMD_SIZE; idx = (cmdidx_T)((int)idx + 1)) {
- if (strncmp(cmdnames[(int)idx].cmd_name, cmd, len) == 0) {
- break;
+ if (!one_letter_cmd(cmd, &idx)) {
+ for (idx = (cmdidx_T)0; (int)idx < CMD_SIZE; idx = (cmdidx_T)((int)idx + 1)) {
+ if (strncmp(cmdnames[(int)idx].cmd_name, cmd, len) == 0) {
+ break;
+ }
}
}
@@ -3098,7 +3172,7 @@ uint32_t excmd_get_argt(cmdidx_T idx)
/// @return the "cmd" pointer advanced to beyond the range.
char *skip_range(const char *cmd, int *ctx)
{
- while (vim_strchr(" \t0123456789.$%'/?-+,;\\", *cmd) != NULL) {
+ while (vim_strchr(" \t0123456789.$%'/?-+,;\\", (uint8_t)(*cmd)) != NULL) {
if (*cmd == '\\') {
if (cmd[1] == '?' || cmd[1] == '/' || cmd[1] == '&') {
cmd++;
@@ -3287,14 +3361,14 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int
case '/':
case '?': // '/' or '?' - search
- c = (char_u)(*cmd++);
+ c = (uint8_t)(*cmd++);
if (addr_type != ADDR_LINES) {
addr_error(addr_type);
cmd = NULL;
goto error;
}
if (skip) { // skip "/pat/"
- cmd = (char *)skip_regexp((char_u *)cmd, c, p_magic, NULL);
+ cmd = skip_regexp(cmd, c, magic_isset());
if (*cmd == c) {
cmd++;
}
@@ -3323,7 +3397,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int
}
searchcmdlen = 0;
flags = silent ? 0 : SEARCH_HIS | SEARCH_MSG;
- if (!do_search(NULL, c, c, (char_u *)cmd, 1L, flags, NULL)) {
+ if (!do_search(NULL, c, c, cmd, 1L, flags, NULL)) {
curwin->w_cursor = pos;
cmd = NULL;
goto error;
@@ -3360,7 +3434,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int
pos.coladd = 0;
if (searchit(curwin, curbuf, &pos, NULL,
*cmd == '?' ? BACKWARD : FORWARD,
- (char_u *)"", 1L, SEARCH_MSG, i, NULL) != FAIL) {
+ "", 1L, SEARCH_MSG, i, NULL) != FAIL) {
lnum = pos.lnum;
} else {
cmd = NULL;
@@ -3421,11 +3495,12 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int
if (ascii_isdigit(*cmd)) {
i = '+'; // "number" is same as "+number"
} else {
- i = (char_u)(*cmd++);
+ i = (uint8_t)(*cmd++);
}
- if (!ascii_isdigit(*cmd)) { // '+' is '+1', but '+0' is not '+1'
+ if (!ascii_isdigit(*cmd)) { // '+' is '+1'
n = 1;
} else {
+ // "number", "+number" or "-number"
n = getdigits_int32(&cmd, false, MAXLNUM);
if (n == MAXLNUM) {
emsg(_(e_line_number_out_of_range));
@@ -3440,8 +3515,8 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int
} else if (addr_type == ADDR_LOADED_BUFFERS || addr_type == ADDR_BUFFERS) {
lnum = compute_buffer_local_count(addr_type, lnum, (i == '-') ? -1 * n : n);
} else {
- // Relative line addressing, need to adjust for folded lines
- // now, but only do it after the first address.
+ // Relative line addressing: need to adjust for lines in a
+ // closed fold after the first address.
if (addr_type == ADDR_LINES && (i == '-' || i == '+')
&& address_count >= 2) {
(void)hasFolding(lnum, NULL, &lnum);
@@ -3467,7 +3542,7 @@ error:
/// Get flags from an Ex command argument.
static void get_flags(exarg_T *eap)
{
- while (vim_strchr("lp#", *eap->arg) != NULL) {
+ while (vim_strchr("lp#", (uint8_t)(*eap->arg)) != NULL) {
if (*eap->arg == 'l') {
eap->flags |= EXFLAG_LIST;
} else if (*eap->arg == 'p') {
@@ -3570,6 +3645,9 @@ char *invalid_range(exarg_T *eap)
assert(eap->line2 >= 0);
// No error for value that is too big, will use the last entry.
if (eap->line2 <= 0) {
+ if (eap->addr_count == 0) {
+ return _(e_no_errors);
+ }
return _(e_invrange);
}
break;
@@ -3631,8 +3709,8 @@ char *replace_makeprg(exarg_T *eap, char *arg, char **cmdlinep)
// Don't do it when ":vimgrep" is used for ":grep".
if ((eap->cmdidx == CMD_make || eap->cmdidx == CMD_lmake || isgrep)
&& !grep_internal(eap->cmdidx)) {
- const char *program = isgrep ? (*curbuf->b_p_gp == NUL ? (char *)p_gp : (char *)curbuf->b_p_gp)
- : (*curbuf->b_p_mp == NUL ? (char *)p_mp : (char *)curbuf->b_p_mp);
+ const char *program = isgrep ? (*curbuf->b_p_gp == NUL ? p_gp : curbuf->b_p_gp)
+ : (*curbuf->b_p_mp == NUL ? p_mp : curbuf->b_p_mp);
arg = skipwhite(arg);
@@ -3640,13 +3718,13 @@ char *replace_makeprg(exarg_T *eap, char *arg, char **cmdlinep)
// Replace $* by given arguments
if ((new_cmdline = strrep(program, "$*", arg)) == NULL) {
// No $* in arg, build "<makeprg> <arg>" instead
- new_cmdline = xmalloc(STRLEN(program) + STRLEN(arg) + 2);
+ new_cmdline = xmalloc(strlen(program) + strlen(arg) + 2);
STRCPY(new_cmdline, program);
STRCAT(new_cmdline, " ");
STRCAT(new_cmdline, arg);
}
- msg_make((char_u *)arg);
+ msg_make(arg);
// 'eap->cmd' is not set here, because it is not used at CMD_make
xfree(*cmdlinep);
@@ -3668,7 +3746,7 @@ int expand_filename(exarg_T *eap, char **cmdlinep, char **errormsgp)
// Decide to expand wildcards *before* replacing '%', '#', etc. If
// the file name contains a wildcard it should not cause expanding.
// (it will be expanded anyway if there is a wildcard before replacing).
- int has_wildcards = path_has_wildcard((char_u *)p);
+ int has_wildcards = path_has_wildcard(p);
while (*p != NUL) {
// Skip over `=expr`, wildcards in it are not expanded.
if (p[0] == '`' && p[1] == '=') {
@@ -3681,7 +3759,7 @@ int expand_filename(exarg_T *eap, char **cmdlinep, char **errormsgp)
}
// Quick check if this cannot be the start of a special string.
// Also removes backslash before '%', '#' and '<'.
- if (vim_strchr("%#<", *p) == NULL) {
+ if (vim_strchr("%#<", (uint8_t)(*p)) == NULL) {
p++;
continue;
}
@@ -3689,8 +3767,8 @@ int expand_filename(exarg_T *eap, char **cmdlinep, char **errormsgp)
// Try to find a match at this position.
size_t srclen;
int escaped;
- char *repl = (char *)eval_vars((char_u *)p, (char_u *)eap->arg, &srclen, &(eap->do_ecmd_lnum),
- errormsgp, &escaped);
+ char *repl = eval_vars(p, eap->arg, &srclen, &(eap->do_ecmd_lnum),
+ errormsgp, &escaped, true);
if (*errormsgp != NULL) { // error detected
return FAIL;
}
@@ -3717,7 +3795,6 @@ int expand_filename(exarg_T *eap, char **cmdlinep, char **errormsgp)
&& eap->cmdidx != CMD_bang
&& eap->cmdidx != CMD_grep
&& eap->cmdidx != CMD_grepadd
- && eap->cmdidx != CMD_hardcopy
&& eap->cmdidx != CMD_lgrep
&& eap->cmdidx != CMD_lgrepadd
&& eap->cmdidx != CMD_lmake
@@ -3735,8 +3812,8 @@ int expand_filename(exarg_T *eap, char **cmdlinep, char **errormsgp)
#endif
for (l = repl; *l; l++) {
- if (vim_strchr((char *)ESCAPE_CHARS, *l) != NULL) {
- l = (char *)vim_strsave_escaped((char_u *)repl, ESCAPE_CHARS);
+ if (vim_strchr(ESCAPE_CHARS, (uint8_t)(*l)) != NULL) {
+ l = vim_strsave_escaped(repl, ESCAPE_CHARS);
xfree(repl);
repl = l;
break;
@@ -3751,7 +3828,7 @@ int expand_filename(exarg_T *eap, char **cmdlinep, char **errormsgp)
&& strpbrk(repl, "!") != NULL) {
char *l;
- l = (char *)vim_strsave_escaped((char_u *)repl, (char_u *)"!");
+ l = vim_strsave_escaped(repl, "!");
xfree(repl);
repl = l;
}
@@ -3772,14 +3849,14 @@ int expand_filename(exarg_T *eap, char **cmdlinep, char **errormsgp)
// if there are still wildcards present.
if (vim_strchr(eap->arg, '$') != NULL
|| vim_strchr(eap->arg, '~') != NULL) {
- expand_env_esc((char_u *)eap->arg, NameBuff, MAXPATHL, true, true, NULL);
+ expand_env_esc(eap->arg, NameBuff, MAXPATHL, true, true, NULL);
has_wildcards = path_has_wildcard(NameBuff);
- p = (char *)NameBuff;
+ p = 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, cmdlinep);
}
}
@@ -3788,10 +3865,10 @@ int expand_filename(exarg_T *eap, char **cmdlinep, char **errormsgp)
// done by ExpandOne() below.
#ifdef UNIX
if (!has_wildcards) {
- backslash_halve((char_u *)eap->arg);
+ backslash_halve(eap->arg);
}
#else
- backslash_halve((char_u *)eap->arg);
+ backslash_halve(eap->arg);
#endif
if (has_wildcards) {
@@ -3803,11 +3880,11 @@ int expand_filename(exarg_T *eap, char **cmdlinep, char **errormsgp)
if (p_wic) {
options += WILD_ICASE;
}
- p = (char *)ExpandOne(&xpc, (char_u *)eap->arg, NULL, options, WILD_EXPAND_FREE);
+ p = ExpandOne(&xpc, 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, cmdlinep);
xfree(p);
}
}
@@ -3825,10 +3902,10 @@ static char *repl_cmdline(exarg_T *eap, char *src, size_t srclen, char *repl, ch
// The new command line is build in new_cmdline[].
// First allocate it.
// Careful: a "+cmd" argument may have been NUL terminated.
- size_t len = STRLEN(repl);
- size_t i = (size_t)(src - *cmdlinep) + STRLEN(src + srclen) + len + 3;
+ size_t len = strlen(repl);
+ size_t i = (size_t)(src - *cmdlinep) + strlen(src + srclen) + len + 3;
if (eap->nextcmd != NULL) {
- i += STRLEN(eap->nextcmd); // add space for next command
+ i += strlen(eap->nextcmd); // add space for next command
}
char *new_cmdline = xmalloc(i);
size_t offset = (size_t)(src - *cmdlinep);
@@ -3846,7 +3923,7 @@ static char *repl_cmdline(exarg_T *eap, char *src, size_t srclen, char *repl, ch
src = new_cmdline + i; // remember where to continue
if (eap->nextcmd != NULL) { // append next command
- i = STRLEN(new_cmdline) + 1;
+ i = strlen(new_cmdline) + 1;
STRCPY(new_cmdline + i, eap->nextcmd);
eap->nextcmd = new_cmdline + i;
}
@@ -3861,7 +3938,7 @@ static char *repl_cmdline(exarg_T *eap, char *src, size_t srclen, char *repl, ch
} 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);
+ eap->args[j] = new_cmdline + ((eap->args[j] - *cmdlinep) + (ptrdiff_t)(len - srclen));
}
}
@@ -3913,7 +3990,7 @@ void separate_nextcmd(exarg_T *eap)
STRMOVE(p - 1, p); // remove the '\'
p--;
} else {
- eap->nextcmd = (char *)check_nextcmd((char_u *)p);
+ eap->nextcmd = check_nextcmd(p);
*p = NUL;
break;
}
@@ -3921,7 +3998,7 @@ void separate_nextcmd(exarg_T *eap)
}
if (!(eap->argt & EX_NOTRLCOM)) { // remove trailing spaces
- del_trailing_spaces((char_u *)eap->arg);
+ del_trailing_spaces(eap->arg);
}
}
@@ -3967,15 +4044,15 @@ char *skip_cmd_arg(char *p, int rembs)
return p;
}
-int get_bad_opt(const char_u *p, exarg_T *eap)
+int get_bad_opt(const char *p, exarg_T *eap)
FUNC_ATTR_NONNULL_ALL
{
if (STRICMP(p, "keep") == 0) {
eap->bad_char = BAD_KEEP;
} else if (STRICMP(p, "drop") == 0) {
eap->bad_char = BAD_DROP;
- } else if (MB_BYTE2LEN(*p) == 1 && p[1] == NUL) {
- eap->bad_char = *p;
+ } else if (MB_BYTE2LEN((uint8_t)(*p)) == 1 && p[1] == NUL) {
+ eap->bad_char = (uint8_t)(*p);
} else {
return FAIL;
}
@@ -3992,7 +4069,7 @@ static int getargopt(exarg_T *eap)
int bad_char_idx;
// ":edit ++[no]bin[ary] file"
- if (STRNCMP(arg, "bin", 3) == 0 || STRNCMP(arg, "nobin", 5) == 0) {
+ if (strncmp(arg, "bin", 3) == 0 || strncmp(arg, "nobin", 5) == 0) {
if (*arg == 'n') {
arg += 2;
eap->force_bin = FORCE_NOBIN;
@@ -4007,26 +4084,33 @@ static int getargopt(exarg_T *eap)
}
// ":read ++edit file"
- if (STRNCMP(arg, "edit", 4) == 0) {
+ if (strncmp(arg, "edit", 4) == 0) {
eap->read_edit = true;
eap->arg = skipwhite(arg + 4);
return OK;
}
- if (STRNCMP(arg, "ff", 2) == 0) {
+ // ":write ++p foo/bar/file
+ if (strncmp(arg, "p", 1) == 0) {
+ eap->mkdir_p = true;
+ eap->arg = skipwhite(arg + 1);
+ return OK;
+ }
+
+ if (strncmp(arg, "ff", 2) == 0) {
arg += 2;
pp = &eap->force_ff;
- } else if (STRNCMP(arg, "fileformat", 10) == 0) {
+ } else if (strncmp(arg, "fileformat", 10) == 0) {
arg += 10;
pp = &eap->force_ff;
- } else if (STRNCMP(arg, "enc", 3) == 0) {
- if (STRNCMP(arg, "encoding", 8) == 0) {
+ } else if (strncmp(arg, "enc", 3) == 0) {
+ if (strncmp(arg, "encoding", 8) == 0) {
arg += 8;
} else {
arg += 3;
}
pp = &eap->force_enc;
- } else if (STRNCMP(arg, "bad", 3) == 0) {
+ } else if (strncmp(arg, "bad", 3) == 0) {
arg += 3;
pp = &bad_char_idx;
}
@@ -4042,10 +4126,10 @@ static int getargopt(exarg_T *eap)
*arg = NUL;
if (pp == &eap->force_ff) {
- if (check_ff_value((char_u *)eap->cmd + eap->force_ff) == FAIL) {
+ if (check_ff_value(eap->cmd + eap->force_ff) == FAIL) {
return FAIL;
}
- eap->force_ff = (char_u)eap->cmd[eap->force_ff];
+ eap->force_ff = (uint8_t)eap->cmd[eap->force_ff];
} else if (pp == &eap->force_enc) {
// Make 'fileencoding' lower case.
for (char *p = eap->cmd + eap->force_enc; *p != NUL; p++) {
@@ -4054,7 +4138,7 @@ static int getargopt(exarg_T *eap)
} else {
// Check ++bad= argument. Must be a single-byte character, "keep" or
// "drop".
- if (get_bad_opt((char_u *)eap->cmd + bad_char_idx, eap) == FAIL) {
+ if (get_bad_opt(eap->cmd + bad_char_idx, eap) == FAIL) {
return FAIL;
}
}
@@ -4088,9 +4172,9 @@ static int get_tabpage_arg(exarg_T *eap)
tab_number = (int)getdigits(&p, false, tab_number);
if (relative == 0) {
- if (STRCMP(p, "$") == 0) {
+ if (strcmp(p, "$") == 0) {
tab_number = LAST_TAB_NR;
- } else if (STRCMP(p, "#") == 0) {
+ } else if (strcmp(p, "#") == 0) {
if (valid_tabpage(lastused_tabpage)) {
tab_number = tabpage_index(lastused_tabpage);
} else {
@@ -4157,13 +4241,12 @@ theend:
static void ex_autocmd(exarg_T *eap)
{
- // Disallow autocommands from .exrc and .vimrc in current
- // directory for security reasons.
+ // Disallow autocommands in secure mode.
if (secure) {
secure = 2;
eap->errmsg = _(e_curdir);
} else if (eap->cmdidx == CMD_autocmd) {
- do_autocmd(eap->arg, eap->forceit);
+ do_autocmd(eap, eap->arg, eap->forceit);
} else {
do_augroup(eap->arg, eap->forceit);
}
@@ -4276,7 +4359,7 @@ int ends_excmd(int c) FUNC_ATTR_CONST
/// @return the next command, after the first '|' or '\n' or,
/// NULL if not found.
-char_u *find_nextcmd(const char_u *p)
+char *find_nextcmd(const char *p)
{
while (*p != '|' && *p != '\n') {
if (*p == NUL) {
@@ -4284,21 +4367,20 @@ char_u *find_nextcmd(const char_u *p)
}
p++;
}
- return (char_u *)p + 1;
+ return (char *)p + 1;
}
/// 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.
-char_u *check_nextcmd(char_u *p)
+char *check_nextcmd(char *p)
{
- char *s = skipwhite((char *)p);
+ char *s = skipwhite(p);
if (*s == '|' || *s == '\n') {
- return (char_u *)(s + 1);
- } else {
- return NULL;
+ return s + 1;
}
+ return NULL;
}
/// - if there are more files to edit
@@ -4321,14 +4403,14 @@ static int check_more(int message, bool forceit)
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, (char_u *)buff, 1) == VIM_YES) {
+ "%d more files to edit. Quit anyway?", n), n);
+ if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 1) == VIM_YES) {
return OK;
}
return FAIL;
}
semsg(NGETTEXT("E173: %" PRId64 " more file to edit",
- "E173: %" PRId64 " more files to edit", (unsigned long)n), (int64_t)n);
+ "E173: %" PRId64 " more files to edit", n), (int64_t)n);
quitmore = 2; // next try to quit is allowed
}
return FAIL;
@@ -4361,7 +4443,7 @@ static void ex_colorscheme(exarg_T *eap)
} else {
msg("default");
}
- } else if (load_colors((char_u *)eap->arg) == FAIL) {
+ } else if (load_colors(eap->arg) == FAIL) {
semsg(_("E185: Cannot find color scheme '%s'"), eap->arg);
}
}
@@ -4556,7 +4638,7 @@ static void ex_pclose(exarg_T *eap)
void ex_win_close(int forceit, win_T *win, tabpage_T *tp)
{
// Never close the autocommand window.
- if (win == aucmd_win) {
+ if (is_aucmd_win(win)) {
emsg(_(e_autocmd_close));
return;
}
@@ -4593,23 +4675,29 @@ static void ex_tabclose(exarg_T *eap)
{
if (cmdwin_type != 0) {
cmdwin_result = K_IGNORE;
- } else if (first_tabpage->tp_next == NULL) {
+ return;
+ }
+
+ if (first_tabpage->tp_next == NULL) {
emsg(_("E784: Cannot close last tab page"));
- } else {
- int tab_number = get_tabpage_arg(eap);
- if (eap->errmsg == NULL) {
- tabpage_T *tp = find_tabpage(tab_number);
- if (tp == NULL) {
- beep_flush();
- return;
- }
- if (tp != curtab) {
- tabpage_close_other(tp, eap->forceit);
- return;
- } else if (!text_locked() && !curbuf_locked()) {
- tabpage_close(eap->forceit);
- }
- }
+ return;
+ }
+
+ int tab_number = get_tabpage_arg(eap);
+ if (eap->errmsg != NULL) {
+ return;
+ }
+
+ tabpage_T *tp = find_tabpage(tab_number);
+ if (tp == NULL) {
+ beep_flush();
+ return;
+ }
+ if (tp != curtab) {
+ tabpage_close_other(tp, eap->forceit);
+ return;
+ } else if (!text_locked() && !curbuf_locked()) {
+ tabpage_close(eap->forceit);
}
}
@@ -4618,32 +4706,38 @@ static void ex_tabonly(exarg_T *eap)
{
if (cmdwin_type != 0) {
cmdwin_result = K_IGNORE;
- } else if (first_tabpage->tp_next == NULL) {
+ return;
+ }
+
+ if (first_tabpage->tp_next == NULL) {
msg(_("Already only one tab page"));
- } else {
- int tab_number = get_tabpage_arg(eap);
- if (eap->errmsg == NULL) {
- goto_tabpage(tab_number);
- // Repeat this up to a 1000 times, because autocommands may
- // mess up the lists.
- for (int done = 0; done < 1000; done++) {
- FOR_ALL_TABS(tp) {
- if (tp->tp_topframe != topframe) {
- tabpage_close_other(tp, eap->forceit);
- // if we failed to close it quit
- if (valid_tabpage(tp)) {
- done = 1000;
- }
- // start over, "tp" is now invalid
- break;
- }
- }
- assert(first_tabpage);
- if (first_tabpage->tp_next == NULL) {
- break;
+ return;
+ }
+
+ int tab_number = get_tabpage_arg(eap);
+ if (eap->errmsg != NULL) {
+ return;
+ }
+
+ goto_tabpage(tab_number);
+ // Repeat this up to a 1000 times, because autocommands may
+ // mess up the lists.
+ for (int done = 0; done < 1000; done++) {
+ FOR_ALL_TABS(tp) {
+ if (tp->tp_topframe != topframe) {
+ tabpage_close_other(tp, eap->forceit);
+ // if we failed to close it quit
+ if (valid_tabpage(tp)) {
+ done = 1000;
}
+ // start over, "tp" is now invalid
+ break;
}
}
+ assert(first_tabpage);
+ if (first_tabpage->tp_next == NULL) {
+ break;
+ }
}
}
@@ -4670,7 +4764,6 @@ void tabpage_close(int forceit)
void tabpage_close_other(tabpage_T *tp, int forceit)
{
int done = 0;
- int h = tabline_height();
char prev_idx[NUMBUFLEN];
// Limit to 1000 windows, autocommands may add a window while we close
@@ -4686,11 +4779,6 @@ void tabpage_close_other(tabpage_T *tp, int forceit)
break;
}
}
-
- redraw_tabline = true;
- if (h != tabline_height()) {
- win_new_screen_rows();
- }
}
/// ":only".
@@ -4703,9 +4791,8 @@ static void ex_only(exarg_T *eap)
for (wp = firstwin; --wnr > 0;) {
if (wp->w_next == NULL) {
break;
- } else {
- wp = wp->w_next;
}
+ wp = wp->w_next;
}
} else {
wp = curwin;
@@ -4719,25 +4806,27 @@ static void ex_only(exarg_T *eap)
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, eap->forceit); // don't free buffer
- } else {
- int winnr = 0;
- win_T *win = NULL;
+ if (eap->skip) {
+ return;
+ }
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- winnr++;
- if (winnr == eap->line2) {
- win = wp;
- break;
- }
- }
- if (win == NULL) {
- win = lastwin;
+ if (eap->addr_count == 0) {
+ win_close(curwin, false, eap->forceit); // don't free buffer
+ } else {
+ int winnr = 0;
+ win_T *win = NULL;
+
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ winnr++;
+ if (winnr == eap->line2) {
+ win = wp;
+ break;
}
- win_close(win, false, eap->forceit);
}
+ if (win == NULL) {
+ win = lastwin;
+ }
+ win_close(win, false, eap->forceit);
}
}
@@ -4812,7 +4901,6 @@ static void ex_print(exarg_T *eap)
if (++eap->line1 > eap->line2) {
break;
}
- ui_flush(); // show one line at a time
}
setpcmark();
// put cursor at last line
@@ -4888,8 +4976,8 @@ void ex_splitview(exarg_T *eap)
}
if (eap->cmdidx == CMD_sfind || eap->cmdidx == CMD_tabfind) {
- fname = (char *)find_file_in_path((char_u *)eap->arg, STRLEN(eap->arg),
- FNAME_MESS, true, (char_u *)curbuf->b_ffname);
+ fname = find_file_in_path(eap->arg, strlen(eap->arg),
+ FNAME_MESS, true, curbuf->b_ffname);
if (fname == NULL) {
goto theend;
}
@@ -4899,7 +4987,7 @@ void ex_splitview(exarg_T *eap)
// Either open new tab page or split the window.
if (use_tab) {
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) {
+ ? 0 : (int)eap->line2 + 1, eap->arg) != FAIL) {
do_exedit(eap, old_curwin);
apply_autocmds(EVENT_TABNEWENTERED, NULL, NULL, false, curbuf);
@@ -5012,9 +5100,8 @@ static void ex_tabs(exarg_T *eap)
}
msg_putchar('\n');
- vim_snprintf((char *)IObuff, IOSIZE, _("Tab page %d"), tabcount++);
+ vim_snprintf(IObuff, IOSIZE, _("Tab page %d"), tabcount++);
msg_outtrans_attr(IObuff, HL_ATTR(HLF_T));
- ui_flush(); // output one line at a time
os_breakcheck();
FOR_ALL_WINDOWS_IN_TAB(wp, tp) {
@@ -5028,12 +5115,11 @@ static void ex_tabs(exarg_T *eap)
msg_putchar(bufIsChanged(wp->w_buffer) ? '+' : ' ');
msg_putchar(' ');
if (buf_spname(wp->w_buffer) != NULL) {
- STRLCPY(IObuff, buf_spname(wp->w_buffer), IOSIZE);
+ xstrlcpy(IObuff, buf_spname(wp->w_buffer), IOSIZE);
} else {
- home_replace(wp->w_buffer, wp->w_buffer->b_fname, (char *)IObuff, IOSIZE, true);
+ home_replace(wp->w_buffer, wp->w_buffer->b_fname, IObuff, IOSIZE, true);
}
- msg_outtrans((char *)IObuff);
- ui_flush(); // output one line at a time
+ msg_outtrans(IObuff);
os_breakcheck();
}
}
@@ -5044,7 +5130,7 @@ static void ex_tabs(exarg_T *eap)
static void ex_mode(exarg_T *eap)
{
if (*eap->arg == NUL) {
- must_redraw = CLEAR;
+ must_redraw = UPD_CLEAR;
ex_redraw(eap);
} else {
emsg(_(e_screenmode));
@@ -5083,23 +5169,25 @@ static void ex_resize(exarg_T *eap)
/// ":find [+command] <file>" command.
static void ex_find(exarg_T *eap)
{
- char *fname = (char *)find_file_in_path((char_u *)eap->arg, STRLEN(eap->arg),
- FNAME_MESS, true, (char_u *)curbuf->b_ffname);
+ char *fname = find_file_in_path(eap->arg, strlen(eap->arg),
+ FNAME_MESS, true, curbuf->b_ffname);
if (eap->addr_count > 0) {
// Repeat finding the file "count" times. This matters when it
// appears several times in the path.
linenr_T count = eap->line2;
while (fname != NULL && --count > 0) {
xfree(fname);
- fname = (char *)find_file_in_path(NULL, 0, FNAME_MESS, false, (char_u *)curbuf->b_ffname);
+ fname = find_file_in_path(NULL, 0, FNAME_MESS, false, curbuf->b_ffname);
}
}
- if (fname != NULL) {
- eap->arg = fname;
- do_exedit(eap, NULL);
- xfree(fname);
+ if (fname == NULL) {
+ return;
}
+
+ eap->arg = fname;
+ do_exedit(eap, NULL);
+ xfree(fname);
}
/// ":edit", ":badd", ":balt", ":visual".
@@ -5136,7 +5224,7 @@ void do_exedit(exarg_T *eap, win_T *old_curwin)
no_wait_return = 0;
need_wait_return = false;
msg_scroll = 0;
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
pending_exmode_active = true;
normal_enter(false, true);
@@ -5161,9 +5249,9 @@ void do_exedit(exarg_T *eap, win_T *old_curwin)
old_curwin == NULL ? curwin : NULL);
} else if ((eap->cmdidx != CMD_split && eap->cmdidx != CMD_vsplit)
|| *eap->arg != NUL) {
- // Can't edit another file when "curbuf->b_ro_lockec" is set. Only ":edit"
- // can bring us here, others are stopped earlier.
- if (*eap->arg != NUL && curbuf_locked()) {
+ // Can't edit another file when "textlock" or "curbuf->b_ro_locked" is set.
+ // Only ":edit" or ":script" can bring us here, others are stopped earlier.
+ if (*eap->arg != NUL && text_or_buf_locked()) {
return;
}
n = readonlymode;
@@ -5251,7 +5339,7 @@ static void ex_swapname(exarg_T *eap)
if (curbuf->b_ml.ml_mfp == NULL || curbuf->b_ml.ml_mfp->mf_fname == NULL) {
msg(_("No swap file"));
} else {
- msg((char *)curbuf->b_ml.ml_mfp->mf_fname);
+ msg(curbuf->b_ml.ml_mfp->mf_fname);
}
}
@@ -5298,7 +5386,7 @@ static void ex_syncbind(exarg_T *eap)
scrolldown(-y, true);
}
curwin->w_scbind_pos = topline;
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
cursor_correct();
curwin->w_redr_status = true;
}
@@ -5324,50 +5412,51 @@ static void ex_read(exarg_T *eap)
if (eap->usefilter) { // :r!cmd
do_bang(1, eap, false, false, true);
- } else {
- if (u_save(eap->line2, (linenr_T)(eap->line2 + 1)) == FAIL) {
+ return;
+ }
+
+ if (u_save(eap->line2, (linenr_T)(eap->line2 + 1)) == FAIL) {
+ return;
+ }
+
+ int i;
+ if (*eap->arg == NUL) {
+ if (check_fname() == FAIL) { // check for no file name
return;
}
- int i;
-
- if (*eap->arg == NUL) {
- if (check_fname() == FAIL) { // check for no file name
- return;
- }
- i = readfile(curbuf->b_ffname, curbuf->b_fname,
- 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, false);
+ i = readfile(curbuf->b_ffname, curbuf->b_fname,
+ 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, false);
+ }
+ if (i != OK) {
+ if (!aborting()) {
+ semsg(_(e_notopen), eap->arg);
}
- if (i != OK) {
- if (!aborting()) {
- semsg(_(e_notopen), eap->arg);
+ } else {
+ if (empty && exmode_active) {
+ // Delete the empty line that remains. Historically ex does
+ // this but vi doesn't.
+ linenr_T lnum;
+ if (eap->line2 == 0) {
+ lnum = curbuf->b_ml.ml_line_count;
+ } else {
+ lnum = 1;
}
- } else {
- if (empty && exmode_active) {
- // Delete the empty line that remains. Historically ex does
- // this but vi doesn't.
- linenr_T lnum;
- if (eap->line2 == 0) {
- lnum = curbuf->b_ml.ml_line_count;
- } else {
- lnum = 1;
- }
- if (*ml_get(lnum) == NUL && u_savedel(lnum, 1L) == OK) {
- ml_delete(lnum, false);
- if (curwin->w_cursor.lnum > 1
- && curwin->w_cursor.lnum >= lnum) {
- curwin->w_cursor.lnum--;
- }
- deleted_lines_mark(lnum, 1L);
+ if (*ml_get(lnum) == NUL && u_savedel(lnum, 1L) == OK) {
+ ml_delete(lnum, false);
+ if (curwin->w_cursor.lnum > 1
+ && curwin->w_cursor.lnum >= lnum) {
+ curwin->w_cursor.lnum--;
}
+ deleted_lines_mark(lnum, 1L);
}
- redraw_curbuf_later(VALID);
}
+ redraw_curbuf_later(UPD_VALID);
}
}
@@ -5419,7 +5508,7 @@ static void post_chdir(CdScope scope, bool trigger_dirchanged)
}
char cwd[MAXPATHL];
- if (os_dirname((char_u *)cwd, MAXPATHL) != OK) {
+ if (os_dirname(cwd, MAXPATHL) != OK) {
return;
}
switch (scope) {
@@ -5457,7 +5546,7 @@ bool changedir_func(char *new_dir, CdScope scope)
char *pdir = NULL;
// ":cd -": Change to previous directory
- if (STRCMP(new_dir, "-") == 0) {
+ if (strcmp(new_dir, "-") == 0) {
pdir = get_prevdir(scope);
if (pdir == NULL) {
emsg(_("E186: No previous directory"));
@@ -5467,7 +5556,7 @@ bool changedir_func(char *new_dir, CdScope scope)
}
if (os_dirname(NameBuff, MAXPATHL) == OK) {
- pdir = (char *)vim_strsave(NameBuff);
+ pdir = xstrdup(NameBuff);
} else {
pdir = NULL;
}
@@ -5480,14 +5569,14 @@ bool changedir_func(char *new_dir, CdScope scope)
if (*new_dir == NUL && p_cdh) {
#endif
// Use NameBuff for home directory name.
- expand_env((char_u *)"$HOME", NameBuff, MAXPATHL);
- new_dir = (char *)NameBuff;
+ expand_env("$HOME", NameBuff, MAXPATHL);
+ new_dir = NameBuff;
}
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) {
+ if (vim_chdir(new_dir) != 0) {
emsg(_(e_failed));
xfree(pdir);
return false;
@@ -5521,27 +5610,27 @@ void ex_cd(exarg_T *eap)
// for non-UNIX ":cd" means: print current directory unless 'cdhome' is set
if (*new_dir == NUL && !p_cdh) {
ex_pwd(NULL);
- } else
+ return;
+ }
#endif
- {
- CdScope scope = kCdScopeGlobal;
- switch (eap->cmdidx) {
- case CMD_tcd:
- case CMD_tchdir:
- scope = kCdScopeTabpage;
- break;
- case CMD_lcd:
- case CMD_lchdir:
- scope = kCdScopeWindow;
- break;
- default:
- break;
- }
- if (changedir_func(new_dir, scope)) {
- // Echo the new current directory if the command was typed.
- if (KeyTyped || p_verbose >= 5) {
- ex_pwd(eap);
- }
+
+ CdScope scope = kCdScopeGlobal;
+ switch (eap->cmdidx) {
+ case CMD_tcd:
+ case CMD_tchdir:
+ scope = kCdScopeTabpage;
+ break;
+ case CMD_lcd:
+ case CMD_lchdir:
+ scope = kCdScopeWindow;
+ break;
+ default:
+ break;
+ }
+ if (changedir_func(new_dir, scope)) {
+ // Echo the new current directory if the command was typed.
+ if (KeyTyped || p_verbose >= 5) {
+ ex_pwd(eap);
}
}
}
@@ -5562,9 +5651,9 @@ static void ex_pwd(exarg_T *eap)
} else if (curtab->tp_localdir != NULL) {
context = "tabpage";
}
- smsg("[%s] %s", context, (char *)NameBuff);
+ smsg("[%s] %s", context, NameBuff);
} else {
- msg((char *)NameBuff);
+ msg(NameBuff);
}
} else {
emsg(_("E187: Unknown"));
@@ -5599,15 +5688,11 @@ 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 return early on CTRL-C.
void do_sleep(long msec)
{
ui_flush(); // flush before waiting
- for (long left = msec; !got_int && left > 0; left -= 1000L) {
- int next = left > 1000l ? 1000 : (int)left;
- LOOP_PROCESS_EVENTS_UNTIL(&main_loop, main_loop.events, (int)next, got_int);
- os_breakcheck();
- }
+ LOOP_PROCESS_EVENTS_UNTIL(&main_loop, main_loop.events, msec, got_int);
// If CTRL-C was typed to interrupt the sleep, drop the CTRL-C from the
// input buffer, otherwise a following call to input() fails.
@@ -5653,7 +5738,7 @@ static void ex_wincmd(exarg_T *eap)
p = eap->arg + 1;
}
- eap->nextcmd = (char *)check_nextcmd((char_u *)p);
+ eap->nextcmd = check_nextcmd(p);
p = skipwhite(p);
if (*p != NUL && *p != '"' && eap->nextcmd == NULL) {
emsg(_(e_invarg));
@@ -5769,21 +5854,21 @@ void ex_may_print(exarg_T *eap)
/// ":smagic" and ":snomagic".
static void ex_submagic(exarg_T *eap)
{
- int magic_save = p_magic;
+ const optmagic_T saved = magic_overruled;
- p_magic = (eap->cmdidx == CMD_smagic);
+ magic_overruled = eap->cmdidx == CMD_smagic ? OPTION_MAGIC_ON : OPTION_MAGIC_OFF;
ex_substitute(eap);
- p_magic = magic_save;
+ magic_overruled = saved;
}
/// ":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;
+ const optmagic_T saved = magic_overruled;
- p_magic = (eap->cmdidx == CMD_smagic);
+ magic_overruled = eap->cmdidx == CMD_smagic ? OPTION_MAGIC_ON : OPTION_MAGIC_OFF;
int retv = ex_substitute_preview(eap, cmdpreview_ns, cmdpreview_bufnr);
- p_magic = magic_save;
+ magic_overruled = saved;
return retv;
}
@@ -5824,20 +5909,21 @@ static void ex_at(exarg_T *eap)
// 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) {
beep_flush();
- } else {
- bool save_efr = exec_from_reg;
+ return;
+ }
- exec_from_reg = true;
+ const bool save_efr = exec_from_reg;
- // Execute from the typeahead buffer.
- // Continue until the stuff buffer is empty and all added characters
- // have been consumed.
- while (!stuff_empty() || typebuf.tb_len > prev_len) {
- (void)do_cmdline(NULL, getexline, NULL, DOCMD_NOWAIT|DOCMD_VERBOSE);
- }
+ exec_from_reg = true;
- exec_from_reg = save_efr;
+ // Execute from the typeahead buffer.
+ // Continue until the stuff buffer is empty and all added characters
+ // have been consumed.
+ while (!stuff_empty() || typebuf.tb_len > prev_len) {
+ (void)do_cmdline(NULL, getexline, NULL, DOCMD_NOWAIT|DOCMD_VERBOSE);
}
+
+ exec_from_reg = save_efr;
}
/// ":!".
@@ -5851,7 +5937,7 @@ static void ex_undo(exarg_T *eap)
{
if (eap->addr_count != 1) {
if (eap->forceit) {
- u_undo_and_forget(1); // :undo!
+ u_undo_and_forget(1, true); // :undo!
} else {
u_undo(1); // :undo
}
@@ -5878,7 +5964,7 @@ static void ex_undo(exarg_T *eap)
emsg(_(e_undobang_cannot_redo_or_move_branch));
return;
}
- u_undo_and_forget(count);
+ u_undo_and_forget(count, true);
} else { // :undo 123
undo_time(step, false, false, true);
}
@@ -5886,18 +5972,18 @@ static void ex_undo(exarg_T *eap)
static void ex_wundo(exarg_T *eap)
{
- char hash[UNDO_HASH_SIZE];
+ uint8_t hash[UNDO_HASH_SIZE];
- u_compute_hash(curbuf, (char_u *)hash);
- u_write_undo(eap->arg, eap->forceit, curbuf, (char_u *)hash);
+ u_compute_hash(curbuf, hash);
+ u_write_undo(eap->arg, eap->forceit, curbuf, hash);
}
static void ex_rundo(exarg_T *eap)
{
- char hash[UNDO_HASH_SIZE];
+ uint8_t hash[UNDO_HASH_SIZE];
- u_compute_hash(curbuf, (char_u *)hash);
- u_read_undo(eap->arg, (char_u *)hash, NULL);
+ u_compute_hash(curbuf, hash);
+ u_read_undo(eap->arg, hash, NULL);
}
/// ":redo".
@@ -5916,7 +6002,7 @@ static void ex_later(exarg_T *eap)
if (*p == NUL) {
count = 1;
- } else if (isdigit(*p)) {
+ } else if (isdigit((uint8_t)(*p))) {
count = getdigits_long(&p, false, 0);
switch (*p) {
case 's':
@@ -5967,14 +6053,14 @@ static void ex_redir(exarg_T *eap)
return;
}
- redir_fd = open_exfile((char_u *)fname, eap->forceit, mode);
+ redir_fd = open_exfile(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 = (char_u)(*arg++);
+ redir_reg = (uint8_t)(*arg++);
if (*arg == '>' && arg[1] == '>') { // append
arg += 2;
} else {
@@ -5985,7 +6071,7 @@ static void ex_redir(exarg_T *eap)
// Make register empty when not using @A-@Z and the
// command is valid.
if (*arg == NUL && !isupper(redir_reg)) {
- write_reg_contents(redir_reg, (char_u *)"", 0, false);
+ write_reg_contents(redir_reg, "", 0, false);
}
}
}
@@ -6037,11 +6123,12 @@ static void ex_redraw(exarg_T *eap)
validate_cursor();
update_topline(curwin);
if (eap->forceit) {
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
redraw_cmdline = true;
+ } else if (VIsual_active) {
+ redraw_curbuf_later(UPD_INVERTED);
}
- update_screen(eap->forceit ? NOT_VALID
- : VIsual_active ? INVERTED : 0);
+ update_screen();
if (need_maketitle) {
maketitle();
}
@@ -6067,14 +6154,22 @@ static void ex_redrawstatus(exarg_T *eap)
int r = RedrawingDisabled;
int p = p_lz;
- RedrawingDisabled = 0;
- p_lz = false;
if (eap->forceit) {
status_redraw_all();
} else {
status_redraw_curbuf();
}
- update_screen(VIsual_active ? INVERTED : 0);
+
+ RedrawingDisabled = 0;
+ p_lz = false;
+ if (State & MODE_CMDLINE) {
+ redraw_statuslines();
+ } else {
+ if (VIsual_active) {
+ redraw_curbuf_later(UPD_INVERTED);
+ }
+ update_screen();
+ }
RedrawingDisabled = r;
p_lz = p;
ui_flush();
@@ -6131,7 +6226,7 @@ int vim_mkdir_emsg(const char *const name, const int prot)
/// @param mode "w" for create new file or "a" for append
///
/// @return file descriptor, or NULL on failure.
-FILE *open_exfile(char_u *fname, int forceit, char *mode)
+FILE *open_exfile(char *fname, int forceit, char *mode)
{
#ifdef UNIX
// with Unix it is possible to open a directory
@@ -6146,7 +6241,7 @@ FILE *open_exfile(char_u *fname, int forceit, char *mode)
}
FILE *fd;
- if ((fd = os_fopen((char *)fname, mode)) == NULL) {
+ if ((fd = os_fopen(fname, mode)) == NULL) {
semsg(_("E190: Cannot open \"%s\" for writing"), fname);
}
@@ -6158,17 +6253,21 @@ static void ex_mark(exarg_T *eap)
{
if (*eap->arg == NUL) { // No argument?
emsg(_(e_argreq));
- } else if (eap->arg[1] != NUL) { // more than one character?
+ return;
+ }
+
+ if (eap->arg[1] != NUL) { // more than one character?
semsg(_(e_trailing_arg), eap->arg);
- } else {
- pos_T pos = curwin->w_cursor; // save curwin->w_cursor
- curwin->w_cursor.lnum = eap->line2;
- beginline(BL_WHITE | BL_FIX);
- if (setmark(*eap->arg) == FAIL) { // set mark
- emsg(_("E191: Argument must be a letter or forward/backward quote"));
- }
- curwin->w_cursor = pos; // restore curwin->w_cursor
+ return;
+ }
+
+ pos_T pos = curwin->w_cursor; // save curwin->w_cursor
+ curwin->w_cursor.lnum = eap->line2;
+ beginline(BL_WHITE | BL_FIX);
+ if (setmark(*eap->arg) == FAIL) { // set mark
+ emsg(_("E191: Argument must be a letter or forward/backward quote"));
}
+ curwin->w_cursor = pos; // restore curwin->w_cursor
}
/// Update w_topline, w_leftcol and the cursor position.
@@ -6270,7 +6369,7 @@ static void ex_normal(exarg_T *eap)
}
}
if (len > 0) {
- arg = xmalloc(STRLEN(eap->arg) + (size_t)len + 1);
+ arg = xmalloc(strlen(eap->arg) + (size_t)len + 1);
len = 0;
for (p = eap->arg; *p != NUL; p++) {
arg[len++] = *p;
@@ -6299,7 +6398,7 @@ static void ex_normal(exarg_T *eap)
check_cursor_moved(curwin);
}
- exec_normal_cmd((char_u *)(arg != NULL ? arg : eap->arg),
+ exec_normal_cmd((arg != NULL ? arg : eap->arg),
eap->forceit ? REMAP_NONE : REMAP_YES, false);
} while (eap->addr_count > 0 && eap->line1 <= eap->line2 && !got_int);
}
@@ -6363,10 +6462,10 @@ static void ex_stopinsert(exarg_T *eap)
/// Execute normal mode command "cmd".
/// "remap" can be REMAP_NONE or REMAP_YES.
-void exec_normal_cmd(char_u *cmd, int remap, bool silent)
+void exec_normal_cmd(char *cmd, int remap, bool silent)
{
// Stuff the argument into the typeahead buffer.
- ins_typebuf((char *)cmd, remap, 0, true, silent);
+ ins_typebuf(cmd, remap, 0, true, silent);
exec_normal(false);
}
@@ -6435,7 +6534,7 @@ static void ex_findpat(exarg_T *eap)
if (*eap->arg == '/') { // Match regexp, not just whole words
whole = false;
eap->arg++;
- char *p = (char *)skip_regexp((char_u *)eap->arg, '/', p_magic, NULL);
+ char *p = skip_regexp(eap->arg, '/', magic_isset());
if (*p) {
*p++ = NUL;
p = skipwhite(p);
@@ -6444,12 +6543,12 @@ static void ex_findpat(exarg_T *eap)
if (!ends_excmd(*p)) {
eap->errmsg = ex_errmsg(e_trailing_arg, p);
} else {
- eap->nextcmd = (char *)check_nextcmd((char_u *)p);
+ eap->nextcmd = check_nextcmd(p);
}
}
}
if (!eap->skip) {
- find_pattern_in_path((char_u *)eap->arg, 0, STRLEN(eap->arg), whole, !eap->forceit,
+ find_pattern_in_path(eap->arg, 0, strlen(eap->arg), whole, !eap->forceit,
*eap->cmd == 'd' ? FIND_DEFINE : FIND_ANY,
n, action, eap->line1, eap->line2);
}
@@ -6477,7 +6576,7 @@ static void ex_pedit(exarg_T *eap)
if (curwin != curwin_save && win_valid(curwin_save)) {
// Return cursor to where we were
validate_cursor();
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
win_enter(curwin_save, true);
}
g_do_tagpreview = 0;
@@ -6500,7 +6599,7 @@ 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 *name)
+static void ex_tag_cmd(exarg_T *eap, const char *name)
{
int cmd;
@@ -6529,10 +6628,6 @@ static void ex_tag_cmd(exarg_T *eap, char *name)
cmd = DT_LAST; // ":tlast"
break;
default: // ":tag"
- if (p_cst && *eap->arg != NUL) {
- ex_cstag(eap);
- return;
- }
cmd = DT_TAG;
break;
}
@@ -6541,7 +6636,7 @@ static void ex_tag_cmd(exarg_T *eap, char *name)
cmd = DT_LTAG;
}
- do_tag((char_u *)eap->arg, cmd, eap->addr_count > 0 ? (int)eap->line2 : 1,
+ do_tag(eap->arg, cmd, eap->addr_count > 0 ? (int)eap->line2 : 1,
eap->forceit, true);
}
@@ -6555,6 +6650,7 @@ enum {
SPEC_SFILE,
SPEC_SLNUM,
SPEC_STACK,
+ SPEC_SCRIPT,
SPEC_AFILE,
SPEC_ABUF,
SPEC_AMATCH,
@@ -6566,7 +6662,7 @@ enum {
/// 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)
+ssize_t find_cmdline_var(const char *src, size_t *usedlen)
FUNC_ATTR_NONNULL_ALL
{
static char *(spec_str[]) = {
@@ -6579,6 +6675,7 @@ ssize_t find_cmdline_var(const char_u *src, size_t *usedlen)
[SPEC_SFILE] = "<sfile>", // ":so" file name
[SPEC_SLNUM] = "<slnum>", // ":so" file line number
[SPEC_STACK] = "<stack>", // call stack
+ [SPEC_SCRIPT] = "<script>", // script file name
[SPEC_AFILE] = "<afile>", // autocommand file name
[SPEC_ABUF] = "<abuf>", // autocommand buffer number
[SPEC_AMATCH] = "<amatch>", // autocommand match name
@@ -6588,8 +6685,8 @@ ssize_t find_cmdline_var(const char_u *src, size_t *usedlen)
};
for (size_t i = 0; i < ARRAY_SIZE(spec_str); i++) {
- size_t len = STRLEN(spec_str[i]);
- if (STRNCMP(src, spec_str[i], len) == 0) {
+ size_t len = strlen(spec_str[i]);
+ if (strncmp(src, spec_str[i], len) == 0) {
*usedlen = len;
assert(i <= SSIZE_MAX);
return (ssize_t)i;
@@ -6600,34 +6697,36 @@ ssize_t find_cmdline_var(const char_u *src, size_t *usedlen)
/// Evaluate cmdline variables.
///
-/// change '%' to curbuf->b_ffname
-/// '#' to curwin->w_alt_fnum
-/// '<cword>' to word under the cursor
-/// '<cWORD>' to WORD under the cursor
-/// '<cexpr>' to C-expression under the cursor
-/// '<cfile>' to path name under the cursor
-/// '<sfile>' to sourced file name
-/// '<stack>' to call stack
-/// '<slnum>' to sourced file line number
-/// '<afile>' to file name for autocommand
-/// '<abuf>' to buffer number for autocommand
-/// '<amatch>' to matching name for autocommand
+/// change "%" to curbuf->b_ffname
+/// "#" to curwin->w_alt_fnum
+/// "<cword>" to word under the cursor
+/// "<cWORD>" to WORD under the cursor
+/// "<cexpr>" to C-expression under the cursor
+/// "<cfile>" to path name under the cursor
+/// "<sfile>" to sourced file name
+/// "<stack>" to call stack
+/// "<script>" to current script name
+/// "<slnum>" to sourced file line number
+/// "<afile>" to file name for autocommand
+/// "<abuf>" to buffer number for autocommand
+/// "<amatch>" to matching name for autocommand
///
/// When an error is detected, "errormsg" is set to a non-NULL pointer (may be
/// "" for error without a message) and NULL is returned.
///
-/// @param src pointer into commandline
-/// @param srcstart beginning of valid memory for src
-/// @param usedlen characters after src that are used
-/// @param lnump line number for :e command, or NULL
-/// @param errormsg pointer to error message
-/// @param escaped return value has escaped white space (can be NULL)
+/// @param src pointer into commandline
+/// @param srcstart beginning of valid memory for src
+/// @param usedlen characters after src that are used
+/// @param lnump line number for :e command, or NULL
+/// @param errormsg pointer to error message
+/// @param escaped return value has escaped white space (can be NULL)
+/// @param empty_is_error empty result is considered an error
///
/// @return an allocated string if a valid match was found.
/// Returns NULL if no match was found. "usedlen" then still contains the
/// number of characters to skip.
-char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnump, char **errormsg,
- int *escaped)
+char *eval_vars(char *src, const char *srcstart, size_t *usedlen, linenr_T *lnump, char **errormsg,
+ int *escaped, bool empty_is_error)
{
char *result;
char *resultbuf = NULL;
@@ -6653,7 +6752,7 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
// Note: In "\\%" the % is also not recognized!
if (src > srcstart && src[-1] == '\\') {
*usedlen = 0;
- STRMOVE(src - 1, src); // remove backslash
+ STRMOVE(src - 1, (char *)src); // remove backslash
return NULL;
}
@@ -6686,7 +6785,7 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
valid = 0; // Must have ":p:h" to be valid
} else {
result = curbuf->b_fname;
- tilde_file = STRCMP(result, "~") == 0;
+ tilde_file = strcmp(result, "~") == 0;
}
break;
@@ -6701,16 +6800,16 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
skip_mod = true;
break;
}
- char *s = (char *)src + 1;
+ char *s = src + 1;
if (*s == '<') { // "#<99" uses v:oldfiles.
s++;
}
int i = getdigits_int(&s, false, 0);
- if ((char_u *)s == src + 2 && src[1] == '-') {
+ if (s == src + 2 && src[1] == '-') {
// just a minus sign, don't skip over it
s--;
}
- *usedlen = (size_t)((char_u *)s - src); // length of what we expand
+ *usedlen = (size_t)(s - src); // length of what we expand
if (src[1] == '<' && i != 0) {
if (*usedlen < 2) {
@@ -6740,13 +6839,13 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
valid = 0; // Must have ":p:h" to be valid
} else {
result = buf->b_fname;
- tilde_file = STRCMP(result, "~") == 0;
+ tilde_file = strcmp(result, "~") == 0;
}
}
break;
case SPEC_CFILE: // file name under cursor
- result = (char *)file_name_at_cursor(FNAME_MESS|FNAME_HYP, 1L, NULL);
+ result = file_name_at_cursor(FNAME_MESS|FNAME_HYP, 1L, NULL);
if (result == NULL) {
*errormsg = "";
return NULL;
@@ -6756,14 +6855,14 @@ 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((char_u *)autocmd_fname)
+ && !path_is_absolute(autocmd_fname)
// For CmdlineEnter and related events, <afile> is not a path! #9348
&& !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 = FullName_save(autocmd_fname, false);
// Copy into `autocmd_fname`, don't reassign it. #8165
- STRLCPY(autocmd_fname, result, MAXPATHL);
+ xstrlcpy(autocmd_fname, result, MAXPATHL);
xfree(result);
}
result = autocmd_fname;
@@ -6771,7 +6870,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 = (char *)path_try_shorten_fname((char_u *)result);
+ result = path_try_shorten_fname(result);
break;
case SPEC_ABUF: // buffer number for autocommand
@@ -6792,12 +6891,25 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
break;
case SPEC_SFILE: // file name for ":so" command
+ result = estack_sfile(ESTACK_SFILE);
+ if (result == NULL) {
+ *errormsg = _(e_no_source_file_name_to_substitute_for_sfile);
+ return NULL;
+ }
+ resultbuf = result; // remember allocated string
+ break;
case SPEC_STACK: // call stack
- result = estack_sfile(spec_idx == SPEC_SFILE ? ESTACK_SFILE : ESTACK_STACK);
+ result = estack_sfile(ESTACK_STACK);
if (result == NULL) {
- *errormsg = spec_idx == SPEC_SFILE
- ? _("E498: no :source file name to substitute for \"<sfile>\"")
- : _("E489: no call stack to substitute for \"<stack>\"");
+ *errormsg = _(e_no_call_stack_to_substitute_for_stack);
+ return NULL;
+ }
+ resultbuf = result; // remember allocated string
+ break;
+ case SPEC_SCRIPT: // script file name
+ result = estack_sfile(ESTACK_SCRIPT);
+ if (result == NULL) {
+ *errormsg = _(e_no_script_file_name_to_substitute_for_script);
return NULL;
}
resultbuf = result; // remember allocated string
@@ -6840,17 +6952,17 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
}
// Length of new string.
- resultlen = STRLEN(result);
+ resultlen = strlen(result);
// Remove the file name extension.
if (src[*usedlen] == '<') {
(*usedlen)++;
char *s;
- if ((s = (char *)STRRCHR(result, '.')) != NULL
+ if ((s = strrchr(result, '.')) != NULL
&& s >= path_tail(result)) {
resultlen = (size_t)(s - result);
}
} else if (!skip_mod) {
- valid |= modify_fname((char *)src, tilde_file, usedlen, &result,
+ valid |= modify_fname(src, tilde_file, usedlen, &result,
&resultbuf, &resultlen);
if (result == NULL) {
*errormsg = "";
@@ -6860,18 +6972,20 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
}
if (resultlen == 0 || valid != VALID_HEAD + VALID_PATH) {
- if (valid != VALID_HEAD + VALID_PATH) {
- // xgettext:no-c-format
- *errormsg = _("E499: Empty file name for '%' or '#', only works with \":p:h\"");
- } else {
- *errormsg = _("E500: Evaluates to an empty string");
+ if (empty_is_error) {
+ if (valid != VALID_HEAD + VALID_PATH) {
+ // xgettext:no-c-format
+ *errormsg = _("E499: Empty file name for '%' or '#', only works with \":p:h\"");
+ } else {
+ *errormsg = _("E500: Evaluates to an empty string");
+ }
}
result = NULL;
} else {
result = xstrnsave(result, resultlen);
}
xfree(resultbuf);
- return (char_u *)result;
+ return result;
}
/// Expand the <sfile> string in "arg".
@@ -6882,13 +6996,13 @@ char *expand_sfile(char *arg)
char *result = xstrdup(arg);
for (char *p = result; *p;) {
- if (STRNCMP(p, "<sfile>", 7) != 0) {
+ if (strncmp(p, "<sfile>", 7) != 0) {
p++;
} else {
// replace "<sfile>" with the sourced file name, and do ":" stuff
size_t srclen;
char *errormsg;
- char *repl = (char *)eval_vars((char_u *)p, (char_u *)result, &srclen, NULL, &errormsg, NULL);
+ char *repl = eval_vars(p, result, &srclen, NULL, &errormsg, NULL, true);
if (errormsg != NULL) {
if (*errormsg) {
emsg(errormsg);
@@ -6900,11 +7014,11 @@ char *expand_sfile(char *arg)
p += srclen;
continue;
}
- size_t len = STRLEN(result) - srclen + STRLEN(repl) + 1;
+ size_t len = strlen(result) - srclen + strlen(repl) + 1;
char *newres = xmalloc(len);
memmove(newres, result, (size_t)(p - result));
STRCPY(newres + (p - result), repl);
- len = STRLEN(newres);
+ len = strlen(newres);
STRCAT(newres, p + srclen);
xfree(repl);
xfree(result);
@@ -6919,16 +7033,16 @@ char *expand_sfile(char *arg)
/// ":rshada" and ":wshada".
static void ex_shada(exarg_T *eap)
{
- char *save_shada = (char *)p_shada;
+ char *save_shada = p_shada;
if (*p_shada == NUL) {
- p_shada = (char_u *)"'100";
+ p_shada = "'100";
}
if (eap->cmdidx == CMD_rviminfo || eap->cmdidx == CMD_rshada) {
(void)shada_read_everything(eap->arg, eap->forceit, false);
} else {
shada_write_file(eap->arg, eap->forceit);
}
- p_shada = (char_u *)save_shada;
+ p_shada = save_shada;
}
/// Make a dialog message in "buff[DIALOG_MSG_SIZE]".
@@ -6944,16 +7058,16 @@ void dialog_msg(char *buff, char *format, char *fname)
/// ":behave {mswin,xterm}"
static void ex_behave(exarg_T *eap)
{
- if (STRCMP(eap->arg, "mswin") == 0) {
- set_option_value("selection", 0L, "exclusive", 0);
- set_option_value("selectmode", 0L, "mouse,key", 0);
- set_option_value("mousemodel", 0L, "popup", 0);
- set_option_value("keymodel", 0L, "startsel,stopsel", 0);
- } else if (STRCMP(eap->arg, "xterm") == 0) {
- set_option_value("selection", 0L, "inclusive", 0);
- set_option_value("selectmode", 0L, "", 0);
- set_option_value("mousemodel", 0L, "extend", 0);
- set_option_value("keymodel", 0L, "", 0);
+ if (strcmp(eap->arg, "mswin") == 0) {
+ set_option_value_give_err("selection", 0L, "exclusive", 0);
+ set_option_value_give_err("selectmode", 0L, "mouse,key", 0);
+ set_option_value_give_err("mousemodel", 0L, "popup", 0);
+ set_option_value_give_err("keymodel", 0L, "startsel,stopsel", 0);
+ } else if (strcmp(eap->arg, "xterm") == 0) {
+ set_option_value_give_err("selection", 0L, "inclusive", 0);
+ set_option_value_give_err("selectmode", 0L, "", 0);
+ set_option_value_give_err("mousemodel", 0L, "extend", 0);
+ set_option_value_give_err("keymodel", 0L, "", 0);
} else {
semsg(_(e_invarg2), eap->arg);
}
@@ -6987,19 +7101,19 @@ static void ex_filetype(exarg_T *eap)
// Accept "plugin" and "indent" in any order.
for (;;) {
- if (STRNCMP(arg, "plugin", 6) == 0) {
+ if (strncmp(arg, "plugin", 6) == 0) {
plugin = true;
arg = skipwhite(arg + 6);
continue;
}
- if (STRNCMP(arg, "indent", 6) == 0) {
+ if (strncmp(arg, "indent", 6) == 0) {
indent = true;
arg = skipwhite(arg + 6);
continue;
}
break;
}
- if (STRCMP(arg, "on") == 0 || STRCMP(arg, "detect") == 0) {
+ if (strcmp(arg, "on") == 0 || strcmp(arg, "detect") == 0) {
if (*arg == 'o' || !filetype_detect) {
source_runtime(FILETYPE_FILE, DIP_ALL);
filetype_detect = kTrue;
@@ -7016,7 +7130,7 @@ static void ex_filetype(exarg_T *eap)
(void)do_doautocmd("filetypedetect BufRead", true, NULL);
do_modelines(0);
}
- } else if (STRCMP(arg, "off") == 0) {
+ } else if (strcmp(arg, "off") == 0) {
if (plugin || indent) {
if (plugin) {
source_runtime(FTPLUGOF_FILE, DIP_ALL);
@@ -7066,17 +7180,18 @@ void filetype_maybe_enable(void)
/// ":setfiletype [FALLBACK] {name}"
static void ex_setfiletype(exarg_T *eap)
{
- if (!did_filetype) {
- char *arg = eap->arg;
+ if (did_filetype) {
+ return;
+ }
- if (STRNCMP(arg, "FALLBACK ", 9) == 0) {
- arg += 9;
- }
+ char *arg = eap->arg;
+ if (strncmp(arg, "FALLBACK ", 9) == 0) {
+ arg += 9;
+ }
- set_option_value("filetype", 0L, arg, OPT_LOCAL);
- if (arg != eap->arg) {
- did_filetype = false;
- }
+ set_option_value_give_err("filetype", 0L, arg, OPT_LOCAL);
+ if (arg != eap->arg) {
+ did_filetype = false;
}
}
@@ -7099,7 +7214,7 @@ void set_no_hlsearch(bool flag)
static void ex_nohlsearch(exarg_T *eap)
{
set_no_hlsearch(true);
- redraw_all_later(SOME_VALID);
+ redraw_all_later(UPD_SOME_VALID);
}
static void ex_fold(exarg_T *eap)
@@ -7158,7 +7273,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((char_u *)eap->arg, (char_u *)"\"\\");
+ char *name = vim_strsave_escaped(eap->arg, "\"\\");
snprintf(ex_cmd, sizeof(ex_cmd),
":enew%s | call termopen(\"%s\")",
eap->forceit ? "!" : "", name);
@@ -7191,7 +7306,7 @@ static void ex_terminal(exarg_T *eap)
void verify_command(char *cmd)
{
- if (strcmp("smile", cmd)) {
+ if (strcmp("smile", cmd) != 0) {
return; // acceptable non-existing command
}
msg(" #xxn` #xnxx` ,+x@##@Mz;` .xxx"
diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h
index 283501cf76..19dd9e96ca 100644
--- a/src/nvim/ex_docmd.h
+++ b/src/nvim/ex_docmd.h
@@ -1,6 +1,9 @@
#ifndef NVIM_EX_DOCMD_H
#define NVIM_EX_DOCMD_H
+#include <stdbool.h>
+
+#include "nvim/buffer_defs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/globals.h"
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index 69d509abb7..f76e60f6c5 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.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
-// TODO(ZyX-I): move to eval/executor
-
/// @file ex_eval.c
///
/// Functions for Ex command line for the +eval feature.
@@ -10,19 +8,30 @@
#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
#include "nvim/ascii.h"
#include "nvim/charset.h"
#include "nvim/debugger.h"
#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/eval/userfunc.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
+#include "nvim/ex_eval_defs.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
#include "nvim/memory.h"
#include "nvim/message.h"
+#include "nvim/option_defs.h"
+#include "nvim/pos.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
#include "nvim/strings.h"
+#include "nvim/types.h"
#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -45,20 +54,19 @@
// there or even outside the try conditional. Try conditionals may be nested.
// Configuration whether an exception is thrown on error or interrupt. When
-// the preprocessor macros below evaluate to FALSE, an error (did_emsg) or
+// the preprocessor macros below evaluate to false, an error (did_emsg) or
// interrupt (got_int) under an active try conditional terminates the script
// after the non-active finally clauses of all active try conditionals have been
// executed. Otherwise, errors and/or interrupts are converted into catchable
-// exceptions, which terminate the script only if not caught. For user
-// exceptions, only current_exception is set. (Note: got_int can be set
-// asynchronously afterwards by a SIGINT, so current_exception && got_int is not
+// exceptions (did_throw additionally set), which terminate the script only if
+// not caught. For user exceptions, only did_throw is set. (Note: got_int can
+// be set asynchronously afterwards by a SIGINT, so did_throw && got_int is not
// a reliant test that the exception currently being thrown is an interrupt
// exception. Similarly, did_emsg can be set afterwards on an error in an
-// (unskipped) conditional command inside an inactive conditional, so
-// current_exception && did_emsg is not a reliant test that the exception
-// currently being thrown is an error exception.) - The macros can be defined
-// as expressions checking for a variable that is allowed to be changed during
-// execution of a script.
+// (unskipped) conditional command inside an inactive conditional, so did_throw
+// && did_emsg is not a reliant test that the exception currently being thrown
+// is an error exception.) - The macros can be defined as expressions checking
+// for a variable that is allowed to be changed during execution of a script.
// Values used for the Vim release.
#define THROW_ON_ERROR true
@@ -71,7 +79,7 @@
#define CHECK_SKIP \
(did_emsg \
|| got_int \
- || current_exception \
+ || did_throw \
|| (cstack->cs_idx > 0 \
&& !(cstack->cs_flags[cstack->cs_idx - 1] & CSF_ACTIVE)))
@@ -80,16 +88,14 @@ static void discard_pending_return(typval_T *p)
tv_free(p);
}
-/*
- * When several errors appear in a row, setting "force_abort" is delayed until
- * the failing command returned. "cause_abort" is set to TRUE meanwhile, in
- * order to indicate that situation. This is useful when "force_abort" was set
- * during execution of a function call from an expression: the aborting of the
- * expression evaluation is done without producing any error messages, but all
- * error messages on parsing errors during the expression evaluation are given
- * (even if a try conditional is active).
- */
-static int cause_abort = FALSE;
+// When several errors appear in a row, setting "force_abort" is delayed until
+// the failing command returned. "cause_abort" is set to true meanwhile, in
+// order to indicate that situation. This is useful when "force_abort" was set
+// during execution of a function call from an expression: the aborting of the
+// expression evaluation is done without producing any error messages, but all
+// error messages on parsing errors during the expression evaluation are given
+// (even if a try conditional is active).
+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.
@@ -104,7 +110,7 @@ static int cause_abort = FALSE;
/// value. "got_int" is also set by calling interrupt().
int aborting(void)
{
- return (did_emsg && force_abort) || got_int || current_exception;
+ return (did_emsg && force_abort) || got_int || did_throw;
}
/// The value of "force_abort" is temporarily reset by the first emsg() call
@@ -114,11 +120,11 @@ int aborting(void)
void update_force_abort(void)
{
if (cause_abort) {
- force_abort = TRUE;
+ force_abort = true;
}
}
-/// @return TRUE if a command with a subcommand resulting in "retcode" should
+/// @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.
@@ -127,7 +133,7 @@ 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
+/// @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.
@@ -154,70 +160,58 @@ bool cause_errthrow(const char *mesg, bool severe, bool *ignore)
msglist_T *elem;
msglist_T **plist;
- /*
- * Do nothing when displaying the interrupt message or reporting an
- * uncaught exception (which has already been discarded then) at the top
- * level. Also when no exception can be thrown. The message will be
- * displayed by emsg().
- */
+ // Do nothing when displaying the interrupt message or reporting an
+ // uncaught exception (which has already been discarded then) at the top
+ // level. Also when no exception can be thrown. The message will be
+ // displayed by emsg().
if (suppress_errthrow) {
return false;
}
- /*
- * If emsg() has not been called previously, temporarily reset
- * "force_abort" until the throw point for error messages has been
- * reached. This ensures that aborting() returns the same value for all
- * errors that appear in the same command. This means particularly that
- * for parsing errors during expression evaluation emsg() will be called
- * multiply, even when the expression is evaluated from a finally clause
- * that was activated due to an aborting error, interrupt, or exception.
- */
+ // If emsg() has not been called previously, temporarily reset
+ // "force_abort" until the throw point for error messages has been
+ // reached. This ensures that aborting() returns the same value for all
+ // errors that appear in the same command. This means particularly that
+ // for parsing errors during expression evaluation emsg() will be called
+ // multiply, even when the expression is evaluated from a finally clause
+ // that was activated due to an aborting error, interrupt, or exception.
if (!did_emsg) {
cause_abort = force_abort;
- force_abort = FALSE;
- }
-
- /*
- * If no try conditional is active and no exception is being thrown and
- * there has not been an error in a try conditional or a throw so far, do
- * nothing (for compatibility of non-EH scripts). The message will then
- * be displayed by emsg(). When ":silent!" was used and we are not
- * currently throwing an exception, do nothing. The message text will
- * then be stored to v:errmsg by emsg() without displaying it.
- */
- if (((trylevel == 0 && !cause_abort) || emsg_silent) && !current_exception) {
+ force_abort = false;
+ }
+
+ // If no try conditional is active and no exception is being thrown and
+ // there has not been an error in a try conditional or a throw so far, do
+ // nothing (for compatibility of non-EH scripts). The message will then
+ // be displayed by emsg(). When ":silent!" was used and we are not
+ // currently throwing an exception, do nothing. The message text will
+ // then be stored to v:errmsg by emsg() without displaying it.
+ if (((trylevel == 0 && !cause_abort) || emsg_silent) && !did_throw) {
return false;
}
- /*
- * Ignore an interrupt message when inside a try conditional or when an
- * exception is being thrown or when an error in a try conditional or
- * throw has been detected previously. This is important in order that an
- * interrupt exception is catchable by the innermost try conditional and
- * not replaced by an interrupt message error exception.
- */
+ // Ignore an interrupt message when inside a try conditional or when an
+ // exception is being thrown or when an error in a try conditional or
+ // throw has been detected previously. This is important in order that an
+ // interrupt exception is catchable by the innermost try conditional and
+ // not replaced by an interrupt message error exception.
if (mesg == _(e_interr)) {
*ignore = true;
return true;
}
- /*
- * Ensure that all commands in nested function calls and sourced files
- * are aborted immediately.
- */
- cause_abort = TRUE;
-
- /*
- * When an exception is being thrown, some commands (like conditionals) are
- * not skipped. Errors in those commands may affect what of the subsequent
- * commands are regarded part of catch and finally clauses. Catching the
- * exception would then cause execution of commands not intended by the
- * user, who wouldn't even get aware of the problem. Therefore, discard the
- * exception currently being thrown to prevent it from being caught. Just
- * execute finally clauses and terminate.
- */
- if (current_exception) {
+ // Ensure that all commands in nested function calls and sourced files
+ // are aborted immediately.
+ cause_abort = true;
+
+ // When an exception is being thrown, some commands (like conditionals) are
+ // not skipped. Errors in those commands may affect what of the subsequent
+ // commands are regarded part of catch and finally clauses. Catching the
+ // exception would then cause execution of commands not intended by the
+ // user, who wouldn't even get aware of the problem. Therefore, discard the
+ // exception currently being thrown to prevent it from being caught. Just
+ // execute finally clauses and terminate.
+ if (did_throw) {
// When discarding an interrupt exception, reset got_int to prevent the
// same interrupt being converted to an exception again and discarding
// the error exception we are about to throw here.
@@ -229,25 +223,21 @@ bool cause_errthrow(const char *mesg, bool severe, bool *ignore)
#ifdef THROW_TEST
if (!THROW_ON_ERROR) {
- /*
- * Print error message immediately without searching for a matching
- * catch clause; just finally clauses are executed before the script
- * is terminated.
- */
+ // Print error message immediately without searching for a matching
+ // catch clause; just finally clauses are executed before the script
+ // is terminated.
return false;
} else // NOLINT(readability/braces)
#endif
{
- /*
- * Prepare the throw of an error exception, so that everything will
- * be aborted (except for executing finally clauses), until the error
- * exception is caught; if still uncaught at the top level, the error
- * message will be displayed and the script processing terminated
- * then. - This function has no access to the conditional stack.
- * Thus, the actual throw is made after the failing command has
- * returned. - Throw only the first of several errors in a row, except
- * a severe error is following.
- */
+ // Prepare the throw of an error exception, so that everything will
+ // be aborted (except for executing finally clauses), until the error
+ // exception is caught; if still uncaught at the top level, the error
+ // message will be displayed and the script processing terminated
+ // then. - This function has no access to the conditional stack.
+ // Thus, the actual throw is made after the failing command has
+ // returned. - Throw only the first of several errors in a row, except
+ // a severe error is following.
if (msg_list != NULL) {
plist = msg_list;
while (*plist != NULL) {
@@ -264,7 +254,7 @@ bool cause_errthrow(const char *mesg, bool severe, bool *ignore)
// Skip the extra "Vim " prefix for message "E458".
tmsg = elem->msg;
- if (STRNCMP(tmsg, "Vim E", 5) == 0
+ if (strncmp(tmsg, "Vim E", 5) == 0
&& ascii_isdigit(tmsg[5])
&& ascii_isdigit(tmsg[6])
&& ascii_isdigit(tmsg[7])
@@ -313,13 +303,11 @@ void free_global_msglist(void)
/// 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
- * are aborted immediately.
- */
+ // Ensure that all commands in nested function calls and sourced files
+ // are aborted immediately.
if (cause_abort) {
- cause_abort = FALSE;
- force_abort = TRUE;
+ cause_abort = false;
+ force_abort = true;
}
// If no exception is to be thrown or the conversion should be done after
@@ -334,7 +322,7 @@ void do_errthrow(cstack_T *cstack, char *cmdname)
if (cstack != NULL) {
do_throw(cstack);
} else {
- need_rethrow = TRUE;
+ need_rethrow = true;
}
}
*msg_list = NULL;
@@ -343,13 +331,13 @@ void do_errthrow(cstack_T *cstack, char *cmdname)
/// 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.
+/// @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
// is being thrown, do nothing (for compatibility of non-EH scripts).
- if (!got_int || (trylevel == 0 && !current_exception)) {
+ if (!got_int || (trylevel == 0 && !did_throw)) {
return false;
}
@@ -358,7 +346,7 @@ int do_intthrow(cstack_T *cstack)
// The interrupt aborts everything except for executing finally clauses.
// Discard any user or error or interrupt exception currently being
// thrown.
- if (current_exception) {
+ if (did_throw) {
discard_current_exception();
}
} else {
@@ -369,7 +357,7 @@ int do_intthrow(cstack_T *cstack)
// will be terminated then. - If an interrupt exception is already
// being thrown, do nothing.
- if (current_exception) {
+ if (did_throw) {
if (current_exception->type == ET_INTERRUPT) {
return false;
}
@@ -397,13 +385,13 @@ char *get_exception_string(void *value, except_type_T type, char *cmdname, int *
*should_free = true;
mesg = ((msglist_T *)value)->throw_msg;
if (cmdname != NULL && *cmdname != NUL) {
- size_t cmdlen = STRLEN(cmdname);
- ret = xstrnsave("Vim(", 4 + cmdlen + 2 + STRLEN(mesg));
+ size_t cmdlen = strlen(cmdname);
+ ret = xstrnsave("Vim(", 4 + cmdlen + 2 + strlen(mesg));
STRCPY(&ret[4], cmdname);
STRCPY(&ret[4 + cmdlen], "):");
val = ret + 4 + cmdlen + 2;
} else {
- ret = xstrnsave("Vim:", 4 + STRLEN(mesg));
+ ret = xstrnsave("Vim:", 4 + strlen(mesg));
val = ret + 4;
}
@@ -431,7 +419,7 @@ char *get_exception_string(void *value, except_type_T type, char *cmdname, int *
STRCAT(val, p);
p[-2] = NUL;
- snprintf(val + STRLEN(p), strlen(" (%s)"), " (%s)", &mesg[1]);
+ snprintf(val + strlen(p), strlen(" (%s)"), " (%s)", &mesg[1]);
p[-2] = '"';
}
break;
@@ -456,15 +444,13 @@ static int throw_exception(void *value, except_type_T type, char *cmdname)
except_T *excp;
int should_free;
- /*
- * Disallow faking Interrupt or error exceptions as user exceptions. They
- * would be treated differently from real interrupt or error exceptions
- * when no active try block is found, see do_cmdline().
- */
+ // Disallow faking Interrupt or error exceptions as user exceptions. They
+ // would be treated differently from real interrupt or error exceptions
+ // when no active try block is found, see do_cmdline().
if (type == ET_USER) {
- if (STRNCMP((char_u *)value, "Vim", 3) == 0
- && (((char_u *)value)[3] == NUL || ((char_u *)value)[3] == ':'
- || ((char_u *)value)[3] == '(')) {
+ if (strncmp(value, "Vim", 3) == 0
+ && (((char *)value)[3] == NUL || ((char *)value)[3] == ':'
+ || ((char *)value)[3] == '(')) {
emsg(_("E608: Cannot :throw exceptions with 'Vim' prefix"));
goto fail;
}
@@ -501,13 +487,13 @@ static int throw_exception(void *value, except_type_T type, char *cmdname)
int save_msg_silent = msg_silent;
if (debug_break_level > 0) {
- msg_silent = FALSE; // display messages
+ msg_silent = false; // display messages
} else {
verbose_enter();
}
no_wait_return++;
if (debug_break_level > 0 || *p_vfile == NUL) {
- msg_scroll = TRUE; // always scroll up, don't overwrite
+ msg_scroll = true; // always scroll up, don't overwrite
}
smsg(_("Exception thrown: %s"), excp->value);
msg_puts("\n"); // don't overwrite this either
@@ -552,19 +538,17 @@ 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 = (char *)vim_strsave(IObuff);
+ saved_IObuff = xstrdup(IObuff);
if (debug_break_level > 0) {
- msg_silent = FALSE; // display messages
+ msg_silent = false; // display messages
} else {
verbose_enter();
}
no_wait_return++;
if (debug_break_level > 0 || *p_vfile == NUL) {
- msg_scroll = TRUE; // always scroll up, don't overwrite
+ msg_scroll = true; // always scroll up, don't overwrite
}
- smsg(was_finished ? _("Exception finished: %s")
- : _("Exception discarded: %s"),
- excp->value);
+ smsg(was_finished ? _("Exception finished: %s") : _("Exception discarded: %s"), excp->value);
msg_puts("\n"); // don't overwrite this either
if (debug_break_level > 0 || *p_vfile == NUL) {
cmdline_row = msg_row;
@@ -575,7 +559,7 @@ static void discard_exception(except_T *excp, bool was_finished)
} else {
verbose_leave();
}
- STRLCPY(IObuff, saved_IObuff, IOSIZE);
+ xstrlcpy(IObuff, saved_IObuff, IOSIZE);
xfree(saved_IObuff);
}
if (excp->type != ET_INTERRUPT) {
@@ -596,6 +580,7 @@ void discard_current_exception(void)
}
// Note: all globals manipulated here should be saved/restored in
// try_enter/try_leave.
+ did_throw = false;
need_rethrow = false;
}
@@ -607,12 +592,12 @@ static void catch_exception(except_T *excp)
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),
+ vim_snprintf(IObuff, IOSIZE, _("%s, line %" PRId64),
excp->throw_name, (int64_t)excp->throw_lnum);
} else {
- vim_snprintf((char *)IObuff, IOSIZE, "%s", excp->throw_name);
+ vim_snprintf(IObuff, IOSIZE, "%s", excp->throw_name);
}
- set_vim_var_string(VV_THROWPOINT, (char *)IObuff, -1);
+ set_vim_var_string(VV_THROWPOINT, IObuff, -1);
} else {
// throw_name not set on an exception from a command that was typed.
set_vim_var_string(VV_THROWPOINT, NULL, -1);
@@ -622,13 +607,13 @@ static void catch_exception(except_T *excp)
int save_msg_silent = msg_silent;
if (debug_break_level > 0) {
- msg_silent = FALSE; // display messages
+ msg_silent = false; // display messages
} else {
verbose_enter();
}
no_wait_return++;
if (debug_break_level > 0 || *p_vfile == NUL) {
- msg_scroll = TRUE; // always scroll up, don't overwrite
+ msg_scroll = true; // always scroll up, don't overwrite
}
smsg(_("Exception caught: %s"), excp->value);
msg_puts("\n"); // don't overwrite this either
@@ -656,14 +641,14 @@ static void finish_exception(except_T *excp)
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,
+ vim_snprintf(IObuff, IOSIZE,
_("%s, line %" PRId64), caught_stack->throw_name,
(int64_t)caught_stack->throw_lnum);
} else {
- vim_snprintf((char *)IObuff, IOSIZE, "%s",
+ vim_snprintf(IObuff, IOSIZE, "%s",
caught_stack->throw_name);
}
- set_vim_var_string(VV_THROWPOINT, (char *)IObuff, -1);
+ set_vim_var_string(VV_THROWPOINT, IObuff, -1);
} else {
// throw_name not set on an exception from a command that was
// typed.
@@ -678,9 +663,7 @@ static void finish_exception(except_T *excp)
discard_exception(excp, true);
}
-/*
- * Flags specifying the message displayed by report_pending.
- */
+// Flags specifying the message displayed by report_pending.
#define RP_MAKE 0
#define RP_RESUME 1
#define RP_DISCARD 2
@@ -726,14 +709,14 @@ static void report_pending(int action, int pending, void *value)
break;
case CSTP_RETURN:
// ":return" command producing value, allocated
- s = (char *)get_return_cmd(value);
+ s = get_return_cmd(value);
break;
default:
if (pending & CSTP_THROW) {
- vim_snprintf((char *)IObuff, IOSIZE,
+ vim_snprintf(IObuff, IOSIZE,
mesg, _("Exception"));
- mesg = (char *)concat_str(IObuff, (char_u *)": %s");
+ mesg = concat_str(IObuff, ": %s");
s = ((except_T *)value)->value;
} else if ((pending & CSTP_ERROR) && (pending & CSTP_INTERRUPT)) {
s = _("Error and interrupt");
@@ -746,7 +729,7 @@ static void report_pending(int action, int pending, void *value)
save_msg_silent = msg_silent;
if (debug_break_level > 0) {
- msg_silent = FALSE; // display messages
+ msg_silent = false; // display messages
}
no_wait_return++;
msg_scroll = true; // always scroll up, don't overwrite
@@ -830,7 +813,7 @@ void ex_if(exarg_T *eap)
if (cstack->cs_idx == CSTACK_LEN - 1) {
eap->errmsg = _("E579: :if nesting too deep");
} else {
- ++cstack->cs_idx;
+ cstack->cs_idx++;
cstack->cs_flags[cstack->cs_idx] = 0;
skip = CHECK_SKIP;
@@ -870,14 +853,14 @@ void ex_endif(exarg_T *eap)
(void)do_intthrow(eap->cstack);
}
- --eap->cstack->cs_idx;
+ eap->cstack->cs_idx--;
}
}
/// Handle ":else" and ":elseif".
void ex_else(exarg_T *eap)
{
- int result;
+ bool result = false;
cstack_T *const cstack = eap->cstack;
bool skip = CHECK_SKIP;
@@ -925,13 +908,20 @@ void ex_else(exarg_T *eap)
if (eap->cmdidx == CMD_elseif) {
bool error;
- result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip);
+ // When skipping we ignore most errors, but a missing expression is
+ // wrong, perhaps it should have been "else".
+ // A double quote here is the start of a string, not a comment.
+ if (skip && *eap->arg != '"' && ends_excmd(*eap->arg)) {
+ semsg(_(e_invexpr2), eap->arg);
+ } else {
+ result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip);
+ }
+
// When throwing error exceptions, we want to throw always the first
// of several errors in a row. This is what actually happens when
// a conditional error was detected above and there is another failure
// when parsing the expression. Since the skip flag is set in this
// case, the parsing error will be ignored by emsg().
-
if (!skip && !error) {
if (result) {
cstack->cs_flags[cstack->cs_idx] = CSF_ACTIVE | CSF_TRUE;
@@ -958,14 +948,12 @@ void ex_while(exarg_T *eap)
if (cstack->cs_idx == CSTACK_LEN - 1) {
eap->errmsg = _("E585: :while/:for nesting too deep");
} else {
- /*
- * The loop flag is set when we have jumped back from the matching
- * ":endwhile" or ":endfor". When not set, need to initialise this
- * cstack entry.
- */
+ // The loop flag is set when we have jumped back from the matching
+ // ":endwhile" or ":endfor". When not set, need to initialise this
+ // cstack entry.
if ((cstack->cs_lflags & CSL_HAD_LOOP) == 0) {
- ++cstack->cs_idx;
- ++cstack->cs_looplevel;
+ cstack->cs_idx++;
+ cstack->cs_looplevel++;
cstack->cs_line[cstack->cs_idx] = -1;
}
cstack->cs_flags[cstack->cs_idx] =
@@ -973,21 +961,17 @@ void ex_while(exarg_T *eap)
skip = CHECK_SKIP;
if (eap->cmdidx == CMD_while) {
- /*
- * ":while bool-expr"
- */
+ // ":while bool-expr"
result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip);
} else {
void *fi;
- /*
- * ":for var in list-expr"
- */
+ // ":for var in list-expr"
if ((cstack->cs_lflags & CSL_HAD_LOOP) != 0) {
// Jumping here from a ":continue" or ":endfor": use the
// previously evaluated list.
fi = cstack->cs_forinfo[cstack->cs_idx];
- error = FALSE;
+ error = false;
} else {
// Evaluate the argument and get the info in a structure.
fi = eval_for_line(eap->arg, &error, &eap->nextcmd, skip);
@@ -998,7 +982,7 @@ void ex_while(exarg_T *eap)
if (!error && fi != NULL && !skip) {
result = next_for_item(fi, eap->arg);
} else {
- result = FALSE;
+ result = false;
}
if (!result) {
@@ -1007,11 +991,9 @@ void ex_while(exarg_T *eap)
}
}
- /*
- * If this cstack entry was just initialised and is active, set the
- * loop flag, so do_cmdline() will set the line number in cs_line[].
- * If executing the command a second time, clear the loop flag.
- */
+ // If this cstack entry was just initialised and is active, set the
+ // loop flag, so do_cmdline() will set the line number in cs_line[].
+ // If executing the command a second time, clear the loop flag.
if (!skip && !error && result) {
cstack->cs_flags[cstack->cs_idx] |= (CSF_ACTIVE | CSF_TRUE);
cstack->cs_lflags ^= CSL_HAD_LOOP;
@@ -1046,10 +1028,8 @@ void ex_continue(exarg_T *eap)
if (cstack->cs_flags[idx] & (CSF_WHILE | CSF_FOR)) {
rewind_conditionals(cstack, idx, CSF_TRY, &cstack->cs_trylevel);
- /*
- * Set CSL_HAD_CONT, so do_cmdline() will jump back to the
- * matching ":while".
- */
+ // Set CSL_HAD_CONT, so do_cmdline() will jump back to the
+ // matching ":while".
cstack->cs_lflags |= CSL_HAD_CONT; // let do_cmdline() handle it
} else {
// If a try conditional not in its finally clause is reached first,
@@ -1118,8 +1098,8 @@ void ex_endwhile(exarg_T *eap)
eap->errmsg = _(e_endtry);
}
// Try to find the matching ":while" and report what's missing.
- for (idx = cstack->cs_idx; idx > 0; --idx) {
- fl = cstack->cs_flags[idx];
+ for (idx = cstack->cs_idx; idx > 0; idx--) {
+ fl = cstack->cs_flags[idx];
if ((fl & CSF_TRY) && !(fl & CSF_FINALLY)) {
// Give up at a try conditional not in its finally clause.
// Ignore the ":endwhile"/":endfor".
@@ -1131,7 +1111,7 @@ void ex_endwhile(exarg_T *eap)
}
}
// Cleanup and rewind all contained (and unclosed) conditionals.
- (void)cleanup_conditionals(cstack, CSF_WHILE | CSF_FOR, FALSE);
+ (void)cleanup_conditionals(cstack, CSF_WHILE | CSF_FOR, false);
rewind_conditionals(cstack, idx, CSF_TRY, &cstack->cs_trylevel);
} else if (cstack->cs_flags[cstack->cs_idx] & CSF_TRUE
&& !(cstack->cs_flags[cstack->cs_idx] & CSF_ACTIVE)
@@ -1147,10 +1127,8 @@ void ex_endwhile(exarg_T *eap)
(void)do_intthrow(cstack);
}
- /*
- * Set loop flag, so do_cmdline() will jump back to the matching
- * ":while" or ":for".
- */
+ // Set loop flag, so do_cmdline() will jump back to the matching
+ // ":while" or ":for".
cstack->cs_lflags |= CSL_HAD_ENDLOOP;
}
}
@@ -1172,7 +1150,7 @@ void ex_throw(exarg_T *eap)
// On error or when an exception is thrown during argument evaluation, do
// not throw.
if (!eap->skip && value != NULL) {
- if (throw_exception((char_u *)value, ET_USER, NULL) == FAIL) {
+ if (throw_exception(value, ET_USER, NULL) == FAIL) {
xfree(value);
} else {
do_throw(eap->cstack);
@@ -1186,7 +1164,7 @@ void ex_throw(exarg_T *eap)
void do_throw(cstack_T *cstack)
{
int idx;
- int inactivate_try = FALSE;
+ int inactivate_try = false;
//
// Cleanup and deactivate up to the next surrounding try conditional that
@@ -1199,31 +1177,29 @@ void do_throw(cstack_T *cstack)
//
#ifndef THROW_ON_ERROR_TRUE
if (did_emsg && !THROW_ON_ERROR) {
- inactivate_try = TRUE;
- did_emsg = FALSE;
+ inactivate_try = true;
+ did_emsg = false;
}
#endif
#ifndef THROW_ON_INTERRUPT_TRUE
if (got_int && !THROW_ON_INTERRUPT) {
- inactivate_try = TRUE;
- got_int = FALSE;
+ inactivate_try = true;
+ got_int = false;
}
#endif
idx = cleanup_conditionals(cstack, 0, inactivate_try);
if (idx >= 0) {
- /*
- * If this try conditional is active and we are before its first
- * ":catch", set THROWN so that the ":catch" commands will check
- * whether the exception matches. When the exception came from any of
- * the catch clauses, it will be made pending at the ":finally" (if
- * present) and rethrown at the ":endtry". This will also happen if
- * the try conditional is inactive. This is the case when we are
- * throwing an exception due to an error or interrupt on the way from
- * a preceding ":continue", ":break", ":return", ":finish", error or
- * interrupt (not converted to an exception) to the finally clause or
- * from a preceding throw of a user or error or interrupt exception to
- * the matching catch clause or the finally clause.
- */
+ // If this try conditional is active and we are before its first
+ // ":catch", set THROWN so that the ":catch" commands will check
+ // whether the exception matches. When the exception came from any of
+ // the catch clauses, it will be made pending at the ":finally" (if
+ // present) and rethrown at the ":endtry". This will also happen if
+ // the try conditional is inactive. This is the case when we are
+ // throwing an exception due to an error or interrupt on the way from
+ // a preceding ":continue", ":break", ":return", ":finish", error or
+ // interrupt (not converted to an exception) to the finally clause or
+ // from a preceding throw of a user or error or interrupt exception to
+ // the matching catch clause or the finally clause.
if (!(cstack->cs_flags[idx] & CSF_CAUGHT)) {
if (cstack->cs_flags[idx] & CSF_ACTIVE) {
cstack->cs_flags[idx] |= CSF_THROWN;
@@ -1237,6 +1213,8 @@ void do_throw(cstack_T *cstack)
cstack->cs_flags[idx] &= ~CSF_ACTIVE;
cstack->cs_exception[idx] = current_exception;
}
+
+ did_throw = true;
}
/// Handle ":try"
@@ -1248,8 +1226,8 @@ void ex_try(exarg_T *eap)
if (cstack->cs_idx == CSTACK_LEN - 1) {
eap->errmsg = _("E601: :try nesting too deep");
} else {
- ++cstack->cs_idx;
- ++cstack->cs_trylevel;
+ cstack->cs_idx++;
+ cstack->cs_trylevel++;
cstack->cs_flags[cstack->cs_idx] = CSF_TRY;
cstack->cs_pending[cstack->cs_idx] = CSTP_NONE;
@@ -1261,22 +1239,20 @@ void ex_try(exarg_T *eap)
// that the finally clause needs to be executed.
cstack->cs_flags[cstack->cs_idx] |= CSF_ACTIVE | CSF_TRUE;
- /*
- * ":silent!", even when used in a try conditional, disables
- * displaying of error messages and conversion of errors to
- * exceptions. When the silent commands again open a try
- * conditional, save "emsg_silent" and reset it so that errors are
- * again converted to exceptions. The value is restored when that
- * try conditional is left. If it is left normally, the commands
- * following the ":endtry" are again silent. If it is left by
- * a ":continue", ":break", ":return", or ":finish", the commands
- * executed next are again silent. If it is left due to an
- * aborting error, an interrupt, or an exception, restoring
- * "emsg_silent" does not matter since we are already in the
- * aborting state and/or the exception has already been thrown.
- * The effect is then just freeing the memory that was allocated
- * to save the value.
- */
+ // ":silent!", even when used in a try conditional, disables
+ // displaying of error messages and conversion of errors to
+ // exceptions. When the silent commands again open a try
+ // conditional, save "emsg_silent" and reset it so that errors are
+ // again converted to exceptions. The value is restored when that
+ // try conditional is left. If it is left normally, the commands
+ // following the ":endtry" are again silent. If it is left by
+ // a ":continue", ":break", ":return", or ":finish", the commands
+ // executed next are again silent. If it is left due to an
+ // aborting error, an interrupt, or an exception, restoring
+ // "emsg_silent" does not matter since we are already in the
+ // aborting state and/or the exception has already been thrown.
+ // The effect is then just freeing the memory that was allocated
+ // to save the value.
if (emsg_silent) {
eslist_T *elem = xmalloc(sizeof(*elem));
elem->saved_emsg_silent = emsg_silent;
@@ -1314,7 +1290,7 @@ void ex_catch(exarg_T *eap)
eap->errmsg = get_end_emsg(cstack);
skip = true;
}
- for (idx = cstack->cs_idx; idx > 0; --idx) {
+ for (idx = cstack->cs_idx; idx > 0; idx--) {
if (cstack->cs_flags[idx] & CSF_TRY) {
break;
}
@@ -1333,27 +1309,26 @@ void ex_catch(exarg_T *eap)
if (ends_excmd(*eap->arg)) { // no argument, catch all errors
pat = ".*";
end = NULL;
- eap->nextcmd = (char *)find_nextcmd((char_u *)eap->arg);
+ eap->nextcmd = find_nextcmd(eap->arg);
} else {
pat = eap->arg + 1;
- end = (char *)skip_regexp((char_u *)pat, *eap->arg, true, NULL);
+ end = skip_regexp_err(pat, *eap->arg, true);
+ if (end == NULL) {
+ give_up = true;
+ }
}
if (!give_up) {
- /*
- * Don't do something when no exception has been thrown or when the
- * corresponding try block never got active (because of an inactive
- * surrounding conditional or after an error or interrupt or throw).
- */
- if (!current_exception || !(cstack->cs_flags[idx] & CSF_TRUE)) {
+ // Don't do something when no exception has been thrown or when the
+ // corresponding try block never got active (because of an inactive
+ // surrounding conditional or after an error or interrupt or throw).
+ if (!did_throw || !(cstack->cs_flags[idx] & CSF_TRUE)) {
skip = true;
}
- /*
- * Check for a match only if an exception is thrown but not caught by
- * a previous ":catch". An exception that has replaced a discarded
- * exception is not checked (THROWN is not set then).
- */
+ // Check for a match only if an exception is thrown but not caught by
+ // a previous ":catch". An exception that has replaced a discarded
+ // exception is not checked (THROWN is not set then).
if (!skip && (cstack->cs_flags[idx] & CSF_THROWN)
&& !(cstack->cs_flags[idx] & CSF_CAUGHT)) {
if (end != NULL && *end != NUL && !ends_excmd(*skipwhite(end + 1))) {
@@ -1376,7 +1351,7 @@ void ex_catch(exarg_T *eap)
*end = NUL;
}
save_cpo = p_cpo;
- p_cpo = "";
+ p_cpo = empty_option;
// Disable error messages, it will make current exception
// invalid
emsg_off++;
@@ -1397,8 +1372,7 @@ void ex_catch(exarg_T *eap)
//
prev_got_int = got_int;
got_int = false;
- caught = vim_regexec_nl(&regmatch, (char_u *)current_exception->value,
- (colnr_T)0);
+ caught = vim_regexec_nl(&regmatch, current_exception->value, (colnr_T)0);
got_int |= prev_got_int;
vim_regfree(regmatch.regprog);
}
@@ -1406,10 +1380,10 @@ void ex_catch(exarg_T *eap)
}
if (caught) {
- // Make this ":catch" clause active and reset did_emsg and got_int.
- // Put the exception on the caught stack.
+ // Make this ":catch" clause active and reset did_emsg, got_int,
+ // and did_throw. Put the exception on the caught stack.
cstack->cs_flags[idx] |= CSF_ACTIVE | CSF_CAUGHT;
- did_emsg = got_int = false;
+ did_emsg = got_int = did_throw = false;
catch_exception((except_T *)cstack->cs_exception[idx]);
// It's mandatory that the current exception is stored in the cstack
// so that it can be discarded at the next ":catch", ":finally", or
@@ -1419,27 +1393,21 @@ void ex_catch(exarg_T *eap)
if (cstack->cs_exception[cstack->cs_idx] != current_exception) {
internal_error("ex_catch()");
}
- // Discarding current_exceptions happens based on what is stored in
- // cstack->cs_exception, *all* calls to discard_current_exception() are
- // (and must be) guarded by current_exception check.
- current_exception = NULL;
} else {
- /*
- * If there is a preceding catch clause and it caught the exception,
- * finish the exception now. This happens also after errors except
- * when this ":catch" was after the ":finally" or not within
- * a ":try". Make the try conditional inactive so that the
- * following catch clauses are skipped. On an error or interrupt
- * after the preceding try block or catch clause was left by
- * a ":continue", ":break", ":return", or ":finish", discard the
- * pending action.
- */
- cleanup_conditionals(cstack, CSF_TRY, TRUE);
+ // If there is a preceding catch clause and it caught the exception,
+ // finish the exception now. This happens also after errors except
+ // when this ":catch" was after the ":finally" or not within
+ // a ":try". Make the try conditional inactive so that the
+ // following catch clauses are skipped. On an error or interrupt
+ // after the preceding try block or catch clause was left by
+ // a ":continue", ":break", ":return", or ":finish", discard the
+ // pending action.
+ cleanup_conditionals(cstack, CSF_TRY, true);
}
}
if (end != NULL) {
- eap->nextcmd = (char *)find_nextcmd((char_u *)end);
+ eap->nextcmd = find_nextcmd(end);
}
}
@@ -1447,121 +1415,111 @@ void ex_catch(exarg_T *eap)
void ex_finally(exarg_T *eap)
{
int idx;
- int skip = FALSE;
+ int skip = false;
int pending = CSTP_NONE;
cstack_T *const cstack = eap->cstack;
- if (cstack->cs_trylevel <= 0 || cstack->cs_idx < 0) {
- eap->errmsg = _("E606: :finally without :try");
- } else {
- if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) {
- eap->errmsg = get_end_emsg(cstack);
- for (idx = cstack->cs_idx - 1; idx > 0; --idx) {
- if (cstack->cs_flags[idx] & CSF_TRY) {
- break;
- }
- }
- // Make this error pending, so that the commands in the following
- // finally clause can be executed. This overrules also a pending
- // ":continue", ":break", ":return", or ":finish".
- pending = CSTP_ERROR;
- } else {
- idx = cstack->cs_idx;
+ for (idx = cstack->cs_idx; idx >= 0; idx--) {
+ if (cstack->cs_flags[idx] & CSF_TRY) {
+ break;
}
+ }
+ if (cstack->cs_trylevel <= 0 || idx < 0) {
+ eap->errmsg = _("E606: :finally without :try");
+ return;
+ }
- if (cstack->cs_flags[idx] & CSF_FINALLY) {
- // Give up for a multiple ":finally" and ignore it.
- eap->errmsg = _("E607: multiple :finally");
- return;
- }
- rewind_conditionals(cstack, idx, CSF_WHILE | CSF_FOR,
- &cstack->cs_looplevel);
+ if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) {
+ eap->errmsg = get_end_emsg(cstack);
+ // Make this error pending, so that the commands in the following
+ // finally clause can be executed. This overrules also a pending
+ // ":continue", ":break", ":return", or ":finish".
+ pending = CSTP_ERROR;
+ }
- /*
- * Don't do something when the corresponding try block never got active
- * (because of an inactive surrounding conditional or after an error or
- * interrupt or throw) or for a ":finally" without ":try" or a multiple
- * ":finally". After every other error (did_emsg or the conditional
- * errors detected above) or after an interrupt (got_int) or an
- * exception (current_exception), the finally clause must be executed.
- */
- skip = !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE);
+ if (cstack->cs_flags[idx] & CSF_FINALLY) {
+ // Give up for a multiple ":finally" and ignore it.
+ eap->errmsg = _("E607: multiple :finally");
+ return;
+ }
+ rewind_conditionals(cstack, idx, CSF_WHILE | CSF_FOR,
+ &cstack->cs_looplevel);
+
+ // Don't do something when the corresponding try block never got active
+ // (because of an inactive surrounding conditional or after an error or
+ // interrupt or throw) or for a ":finally" without ":try" or a multiple
+ // ":finally". After every other error (did_emsg or the conditional
+ // errors detected above) or after an interrupt (got_int) or an
+ // exception (did_throw), the finally clause must be executed.
+ skip = !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE);
+
+ if (!skip) {
+ // When debugging or a breakpoint was encountered, display the
+ // debug prompt (if not already done). The user then knows that the
+ // finally clause is executed.
+ if (dbg_check_skipped(eap)) {
+ // Handle a ">quit" debug command as if an interrupt had
+ // occurred before the ":finally". That is, discard the
+ // original exception and replace it by an interrupt
+ // exception.
+ (void)do_intthrow(cstack);
+ }
- if (!skip) {
- // When debugging or a breakpoint was encountered, display the
- // debug prompt (if not already done). The user then knows that the
- // finally clause is executed.
- if (dbg_check_skipped(eap)) {
- // Handle a ">quit" debug command as if an interrupt had
- // occurred before the ":finally". That is, discard the
- // original exception and replace it by an interrupt
- // exception.
- (void)do_intthrow(cstack);
+ // If there is a preceding catch clause and it caught the exception,
+ // finish the exception now. This happens also after errors except
+ // when this is a multiple ":finally" or one not within a ":try".
+ // After an error or interrupt, this also discards a pending
+ // ":continue", ":break", ":finish", or ":return" from the preceding
+ // try block or catch clause.
+ cleanup_conditionals(cstack, CSF_TRY, false);
+
+ // Make did_emsg, got_int, did_throw pending. If set, they overrule
+ // a pending ":continue", ":break", ":return", or ":finish". Then
+ // we have particularly to discard a pending return value (as done
+ // by the call to cleanup_conditionals() above when did_emsg or
+ // got_int is set). The pending values are restored by the
+ // ":endtry", except if there is a new error, interrupt, exception,
+ // ":continue", ":break", ":return", or ":finish" in the following
+ // finally clause. A missing ":endwhile", ":endfor" or ":endif"
+ // detected here is treated as if did_emsg and did_throw had
+ // already been set, respectively in case that the error is not
+ // converted to an exception, did_throw had already been unset.
+ // We must not set did_emsg here since that would suppress the
+ // error message.
+ if (pending == CSTP_ERROR || did_emsg || got_int || did_throw) {
+ if (cstack->cs_pending[cstack->cs_idx] == CSTP_RETURN) {
+ report_discard_pending(CSTP_RETURN,
+ cstack->cs_rettv[cstack->cs_idx]);
+ discard_pending_return(cstack->cs_rettv[cstack->cs_idx]);
}
-
- /*
- * If there is a preceding catch clause and it caught the exception,
- * finish the exception now. This happens also after errors except
- * when this is a multiple ":finally" or one not within a ":try".
- * After an error or interrupt, this also discards a pending
- * ":continue", ":break", ":finish", or ":return" from the preceding
- * try block or catch clause.
- */
- cleanup_conditionals(cstack, CSF_TRY, FALSE);
-
- /*
- * Make did_emsg, got_int, current_exception pending. If set, they
- * overrule a pending ":continue", ":break", ":return", or ":finish".
- * Then we have particularly to discard a pending return value (as done
- * by the call to cleanup_conditionals() above when did_emsg or
- * got_int is set). The pending values are restored by the
- * ":endtry", except if there is a new error, interrupt, exception,
- * ":continue", ":break", ":return", or ":finish" in the following
- * finally clause. A missing ":endwhile", ":endfor" or ":endif"
- * detected here is treated as if did_emsg and current_exception had
- * already been set, respectively in case that the error is not
- * converted to an exception, current_exception had already been unset.
- * We must not set did_emsg here since that would suppress the
- * error message.
- */
- if (pending == CSTP_ERROR || did_emsg || got_int || current_exception) {
- if (cstack->cs_pending[cstack->cs_idx] == CSTP_RETURN) {
- report_discard_pending(CSTP_RETURN,
- cstack->cs_rettv[cstack->cs_idx]);
- discard_pending_return(cstack->cs_rettv[cstack->cs_idx]);
- }
- if (pending == CSTP_ERROR && !did_emsg) {
- pending |= (THROW_ON_ERROR ? CSTP_THROW : 0);
- } else {
- pending |= (current_exception ? CSTP_THROW : 0);
- }
- pending |= did_emsg ? CSTP_ERROR : 0;
- pending |= got_int ? CSTP_INTERRUPT : 0;
- assert(pending >= CHAR_MIN && pending <= CHAR_MAX);
- cstack->cs_pending[cstack->cs_idx] = (char)pending;
-
- // It's mandatory that the current exception is stored in the
- // cstack so that it can be rethrown at the ":endtry" or be
- // discarded if the finally clause is left by a ":continue",
- // ":break", ":return", ":finish", error, interrupt, or another
- // exception. When emsg() is called for a missing ":endif" or
- // a missing ":endwhile"/":endfor" detected here, the
- // exception will be discarded.
- if (current_exception
- && cstack->cs_exception[cstack->cs_idx] != current_exception) {
- internal_error("ex_finally()");
- }
+ if (pending == CSTP_ERROR && !did_emsg) {
+ pending |= (THROW_ON_ERROR ? CSTP_THROW : 0);
+ } else {
+ pending |= (did_throw ? CSTP_THROW : 0);
+ }
+ pending |= did_emsg ? CSTP_ERROR : 0;
+ pending |= got_int ? CSTP_INTERRUPT : 0;
+ assert(pending >= CHAR_MIN && pending <= CHAR_MAX);
+ cstack->cs_pending[cstack->cs_idx] = (char)pending;
+
+ // It's mandatory that the current exception is stored in the
+ // cstack so that it can be rethrown at the ":endtry" or be
+ // discarded if the finally clause is left by a ":continue",
+ // ":break", ":return", ":finish", error, interrupt, or another
+ // exception. When emsg() is called for a missing ":endif" or
+ // a missing ":endwhile"/":endfor" detected here, the
+ // exception will be discarded.
+ if (did_throw && cstack->cs_exception[cstack->cs_idx] != current_exception) {
+ internal_error("ex_finally()");
}
-
- /*
- * Set CSL_HAD_FINA, so do_cmdline() will reset did_emsg,
- * got_int, and current_exception and make the finally clause active.
- * This will happen after emsg() has been called for a missing
- * ":endif" or a missing ":endwhile"/":endfor" detected here, so
- * that the following finally clause will be executed even then.
- */
- cstack->cs_lflags |= CSL_HAD_FINA;
}
+
+ // Set CSL_HAD_FINA, so do_cmdline() will reset did_emsg,
+ // got_int, and did_throw and make the finally clause active.
+ // This will happen after emsg() has been called for a missing
+ // ":endif" or a missing ":endwhile"/":endfor" detected here, so
+ // that the following finally clause will be executed even then.
+ cstack->cs_lflags |= CSL_HAD_FINA;
}
}
@@ -1574,189 +1532,180 @@ void ex_endtry(exarg_T *eap)
void *rettv = NULL;
cstack_T *const cstack = eap->cstack;
- if (cstack->cs_trylevel <= 0 || cstack->cs_idx < 0) {
+ for (idx = cstack->cs_idx; idx >= 0; idx--) {
+ if (cstack->cs_flags[idx] & CSF_TRY) {
+ break;
+ }
+ }
+ if (cstack->cs_trylevel <= 0 || idx < 0) {
eap->errmsg = _("E602: :endtry without :try");
- } else {
- // Don't do something after an error, interrupt or throw in the try
- // block, catch clause, or finally clause preceding this ":endtry" or
- // when an error or interrupt occurred after a ":continue", ":break",
- // ":return", or ":finish" in a try block or catch clause preceding this
- // ":endtry" or when the try block never got active (because of an
- // inactive surrounding conditional or after an error or interrupt or
- // throw) or when there is a surrounding conditional and it has been
- // made inactive by a ":continue", ":break", ":return", or ":finish" in
- // the finally clause. The latter case need not be tested since then
- // anything pending has already been discarded.
- bool skip = did_emsg || got_int || current_exception
- || !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE);
+ return;
+ }
- if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) {
- eap->errmsg = get_end_emsg(cstack);
+ // Don't do something after an error, interrupt or throw in the try
+ // block, catch clause, or finally clause preceding this ":endtry" or
+ // when an error or interrupt occurred after a ":continue", ":break",
+ // ":return", or ":finish" in a try block or catch clause preceding this
+ // ":endtry" or when the try block never got active (because of an
+ // inactive surrounding conditional or after an error or interrupt or
+ // throw) or when there is a surrounding conditional and it has been
+ // made inactive by a ":continue", ":break", ":return", or ":finish" in
+ // the finally clause. The latter case need not be tested since then
+ // anything pending has already been discarded.
+ bool skip = did_emsg || got_int || did_throw || !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE);
- // Find the matching ":try" and report what's missing.
- idx = cstack->cs_idx;
- do {
- idx--;
- } while (idx > 0 && !(cstack->cs_flags[idx] & CSF_TRY));
- rewind_conditionals(cstack, idx, CSF_WHILE | CSF_FOR,
- &cstack->cs_looplevel);
- skip = true;
+ if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) {
+ eap->errmsg = get_end_emsg(cstack);
- /*
- * If an exception is being thrown, discard it to prevent it from
- * being rethrown at the end of this function. It would be
- * discarded by the error message, anyway. Resets current_exception.
- * This does not affect the script termination due to the error
- * since "trylevel" is decremented after emsg() has been called.
- */
- if (current_exception) {
- discard_current_exception();
- }
+ // Find the matching ":try" and report what's missing.
+ rewind_conditionals(cstack, idx, CSF_WHILE | CSF_FOR,
+ &cstack->cs_looplevel);
+ skip = true;
- // report eap->errmsg, also when there already was an error
- did_emsg = false;
- } else {
- idx = cstack->cs_idx;
-
- /*
- * If we stopped with the exception currently being thrown at this
- * try conditional since we didn't know that it doesn't have
- * a finally clause, we need to rethrow it after closing the try
- * conditional.
- */
- if (current_exception
- && (cstack->cs_flags[idx] & CSF_TRUE)
- && !(cstack->cs_flags[idx] & CSF_FINALLY)) {
- rethrow = true;
- }
+ // If an exception is being thrown, discard it to prevent it from
+ // being rethrown at the end of this function. It would be
+ // discarded by the error message, anyway. Resets did_throw.
+ // This does not affect the script termination due to the error
+ // since "trylevel" is decremented after emsg() has been called.
+ if (did_throw) {
+ discard_current_exception();
}
- // If there was no finally clause, show the user when debugging or
- // a breakpoint was encountered that the end of the try conditional has
- // been reached: display the debug prompt (if not already done). Do
- // this on normal control flow or when an exception was thrown, but not
- // on an interrupt or error not converted to an exception or when
- // a ":break", ":continue", ":return", or ":finish" is pending. These
- // actions are carried out immediately.
- if ((rethrow || (!skip
- && !(cstack->cs_flags[idx] & CSF_FINALLY)
- && !cstack->cs_pending[idx]))
- && dbg_check_skipped(eap)) {
- // Handle a ">quit" debug command as if an interrupt had occurred
- // before the ":endtry". That is, throw an interrupt exception and
- // set "skip" and "rethrow".
- if (got_int) {
- skip = true;
- (void)do_intthrow(cstack);
- // The do_intthrow() call may have reset current_exception or
- // cstack->cs_pending[idx].
- rethrow = false;
- if (current_exception && !(cstack->cs_flags[idx] & CSF_FINALLY)) {
- rethrow = true;
- }
+ // report eap->errmsg, also when there already was an error
+ did_emsg = false;
+ } else {
+ idx = cstack->cs_idx;
+
+ // If we stopped with the exception currently being thrown at this
+ // try conditional since we didn't know that it doesn't have
+ // a finally clause, we need to rethrow it after closing the try
+ // conditional.
+ if (did_throw
+ && (cstack->cs_flags[idx] & CSF_TRUE)
+ && !(cstack->cs_flags[idx] & CSF_FINALLY)) {
+ rethrow = true;
+ }
+ }
+
+ // If there was no finally clause, show the user when debugging or
+ // a breakpoint was encountered that the end of the try conditional has
+ // been reached: display the debug prompt (if not already done). Do
+ // this on normal control flow or when an exception was thrown, but not
+ // on an interrupt or error not converted to an exception or when
+ // a ":break", ":continue", ":return", or ":finish" is pending. These
+ // actions are carried out immediately.
+ if ((rethrow || (!skip
+ && !(cstack->cs_flags[idx] & CSF_FINALLY)
+ && !cstack->cs_pending[idx]))
+ && dbg_check_skipped(eap)) {
+ // Handle a ">quit" debug command as if an interrupt had occurred
+ // before the ":endtry". That is, throw an interrupt exception and
+ // set "skip" and "rethrow".
+ if (got_int) {
+ skip = true;
+ (void)do_intthrow(cstack);
+ // The do_intthrow() call may have reset did_throw or
+ // cstack->cs_pending[idx].
+ rethrow = false;
+ if (did_throw && !(cstack->cs_flags[idx] & CSF_FINALLY)) {
+ rethrow = true;
}
}
+ }
- /*
- * If a ":return" is pending, we need to resume it after closing the
- * try conditional; remember the return value. If there was a finally
- * clause making an exception pending, we need to rethrow it. Make it
- * the exception currently being thrown.
- */
- if (!skip) {
- pending = cstack->cs_pending[idx];
- cstack->cs_pending[idx] = CSTP_NONE;
- if (pending == CSTP_RETURN) {
- rettv = cstack->cs_rettv[idx];
- } else if (pending & CSTP_THROW) {
- current_exception = cstack->cs_exception[idx];
- }
+ // If a ":return" is pending, we need to resume it after closing the
+ // try conditional; remember the return value. If there was a finally
+ // clause making an exception pending, we need to rethrow it. Make it
+ // the exception currently being thrown.
+ if (!skip) {
+ pending = cstack->cs_pending[idx];
+ cstack->cs_pending[idx] = CSTP_NONE;
+ if (pending == CSTP_RETURN) {
+ rettv = cstack->cs_rettv[idx];
+ } else if (pending & CSTP_THROW) {
+ current_exception = cstack->cs_exception[idx];
}
+ }
- /*
- * Discard anything pending on an error, interrupt, or throw in the
- * finally clause. If there was no ":finally", discard a pending
- * ":continue", ":break", ":return", or ":finish" if an error or
- * interrupt occurred afterwards, but before the ":endtry" was reached.
- * If an exception was caught by the last of the catch clauses and there
- * was no finally clause, finish the exception now. This happens also
- * after errors except when this ":endtry" is not within a ":try".
- * Restore "emsg_silent" if it has been reset by this try conditional.
- */
- (void)cleanup_conditionals(cstack, CSF_TRY | CSF_SILENT, TRUE);
+ // Discard anything pending on an error, interrupt, or throw in the
+ // finally clause. If there was no ":finally", discard a pending
+ // ":continue", ":break", ":return", or ":finish" if an error or
+ // interrupt occurred afterwards, but before the ":endtry" was reached.
+ // If an exception was caught by the last of the catch clauses and there
+ // was no finally clause, finish the exception now. This happens also
+ // after errors except when this ":endtry" is not within a ":try".
+ // Restore "emsg_silent" if it has been reset by this try conditional.
+ (void)cleanup_conditionals(cstack, CSF_TRY | CSF_SILENT, true);
- if (cstack->cs_idx >= 0 && (cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) {
- 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,
- (pending == CSTP_RETURN) ? rettv :
- (pending & CSTP_THROW) ? (void *)current_exception : NULL);
- switch (pending) {
- case CSTP_NONE:
- break;
+ if (!skip) {
+ report_resume_pending(pending,
+ (pending == CSTP_RETURN) ? rettv :
+ (pending & CSTP_THROW) ? (void *)current_exception : NULL);
+ switch (pending) {
+ case CSTP_NONE:
+ break;
- // Reactivate a pending ":continue", ":break", ":return",
- // ":finish" from the try block or a catch clause of this try
- // conditional. This is skipped, if there was an error in an
- // (unskipped) conditional command or an interrupt afterwards
- // or if the finally clause is present and executed a new error,
- // interrupt, throw, ":continue", ":break", ":return", or
- // ":finish".
- case CSTP_CONTINUE:
- ex_continue(eap);
- break;
- case CSTP_BREAK:
- ex_break(eap);
- break;
- case CSTP_RETURN:
- do_return(eap, FALSE, FALSE, rettv);
- break;
- case CSTP_FINISH:
- do_finish(eap, FALSE);
- break;
+ // Reactivate a pending ":continue", ":break", ":return",
+ // ":finish" from the try block or a catch clause of this try
+ // conditional. This is skipped, if there was an error in an
+ // (unskipped) conditional command or an interrupt afterwards
+ // or if the finally clause is present and executed a new error,
+ // interrupt, throw, ":continue", ":break", ":return", or
+ // ":finish".
+ case CSTP_CONTINUE:
+ ex_continue(eap);
+ break;
+ case CSTP_BREAK:
+ ex_break(eap);
+ break;
+ case CSTP_RETURN:
+ do_return(eap, false, false, rettv);
+ break;
+ case CSTP_FINISH:
+ do_finish(eap, false);
+ break;
- // When the finally clause was entered due to an error,
- // interrupt or throw (as opposed to a ":continue", ":break",
- // ":return", or ":finish"), restore the pending values of
- // did_emsg, got_int, and current_exception. This is skipped, if there
- // was a new error, interrupt, throw, ":continue", ":break",
- // ":return", or ":finish". in the finally clause.
- default:
- if (pending & CSTP_ERROR) {
- did_emsg = true;
- }
- if (pending & CSTP_INTERRUPT) {
- got_int = true;
- }
- if (pending & CSTP_THROW) {
- rethrow = true;
- }
- break;
+ // When the finally clause was entered due to an error,
+ // interrupt or throw (as opposed to a ":continue", ":break",
+ // ":return", or ":finish"), restore the pending values of
+ // did_emsg, got_int, and did_throw. This is skipped, if there
+ // was a new error, interrupt, throw, ":continue", ":break",
+ // ":return", or ":finish". in the finally clause.
+ default:
+ if (pending & CSTP_ERROR) {
+ did_emsg = true;
+ }
+ if (pending & CSTP_INTERRUPT) {
+ got_int = true;
}
+ if (pending & CSTP_THROW) {
+ rethrow = true;
+ }
+ break;
}
+ }
- if (rethrow) {
- // Rethrow the current exception (within this cstack).
- do_throw(cstack);
- }
+ if (rethrow) {
+ // Rethrow the current exception (within this cstack).
+ do_throw(cstack);
}
}
-/*
- * enter_cleanup() and leave_cleanup()
- *
- * Functions to be called before/after invoking a sequence of autocommands for
- * cleanup for a failed command. (Failure means here that a call to emsg()
- * has been made, an interrupt occurred, or there is an uncaught exception
- * from a previous autocommand execution of the same command.)
- *
- * Call enter_cleanup() with a pointer to a cleanup_T and pass the same
- * pointer to leave_cleanup(). The cleanup_T structure stores the pending
- * error/interrupt/exception state.
- */
+// enter_cleanup() and leave_cleanup()
+//
+// Functions to be called before/after invoking a sequence of autocommands for
+// cleanup for a failed command. (Failure means here that a call to emsg()
+// has been made, an interrupt occurred, or there is an uncaught exception
+// from a previous autocommand execution of the same command.)
+//
+// Call enter_cleanup() with a pointer to a cleanup_T and pass the same
+// pointer to leave_cleanup(). The cleanup_T structure stores the pending
+// 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
@@ -1768,35 +1717,32 @@ void enter_cleanup(cleanup_T *csp)
{
int pending = CSTP_NONE;
- /*
- * Postpone did_emsg, got_int, current_exception. The pending values will be
- * restored by leave_cleanup() except if there was an aborting error,
- * interrupt, or uncaught exception after this function ends.
- */
- if (did_emsg || got_int || current_exception || need_rethrow) {
+ // Postpone did_emsg, got_int, did_throw. The pending values will be
+ // restored by leave_cleanup() except if there was an aborting error,
+ // interrupt, or uncaught exception after this function ends.
+ if (did_emsg || got_int || did_throw || need_rethrow) {
csp->pending = (did_emsg ? CSTP_ERROR : 0)
| (got_int ? CSTP_INTERRUPT : 0)
- | (current_exception ? CSTP_THROW : 0)
+ | (did_throw ? CSTP_THROW : 0)
| (need_rethrow ? CSTP_THROW : 0);
- // If we are currently throwing an exception, save it as well. On an error
- // not yet converted to an exception, update "force_abort" and reset
- // "cause_abort" (as do_errthrow() would do). This is needed for the
- // do_cmdline() call that is going to be made for autocommand execution. We
- // need not save *msg_list because there is an extra instance for every call
- // of do_cmdline(), anyway.
- if (current_exception || need_rethrow) {
+ // If we are currently throwing an exception (did_throw), save it as
+ // well. On an error not yet converted to an exception, update
+ // "force_abort" and reset "cause_abort" (as do_errthrow() would do).
+ // This is needed for the do_cmdline() call that is going to be made
+ // for autocommand execution. We need not save *msg_list because
+ // there is an extra instance for every call of do_cmdline(), anyway.
+ if (did_throw || need_rethrow) {
csp->exception = current_exception;
current_exception = NULL;
} else {
csp->exception = NULL;
if (did_emsg) {
force_abort |= cause_abort;
- cause_abort = FALSE;
+ cause_abort = false;
}
}
- did_emsg = got_int = need_rethrow = false;
- current_exception = NULL;
+ did_emsg = got_int = did_throw = need_rethrow = false;
// Report if required by the 'verbose' option or when debugging.
report_make_pending(pending, csp->exception);
@@ -1844,18 +1790,14 @@ void leave_cleanup(cleanup_T *csp)
if (msg_list != NULL) {
free_global_msglist();
}
- }
- /*
- * If there was no new error, interrupt, or throw between the calls
- * to enter_cleanup() and leave_cleanup(), restore the pending
- * error/interrupt/exception state.
- */
- else {
- /*
- * If there was an exception being thrown when enter_cleanup() was
- * called, we need to rethrow it. Make it the exception currently
- * being thrown.
- */
+ } else {
+ // If there was no new error, interrupt, or throw between the calls
+ // to enter_cleanup() and leave_cleanup(), restore the pending
+ // error/interrupt/exception state.
+
+ // If there was an exception being thrown when enter_cleanup() was
+ // called, we need to rethrow it. Make it the exception currently
+ // being thrown.
if (pending & CSTP_THROW) {
current_exception = csp->exception;
} else if (pending & CSTP_ERROR) {
@@ -1863,10 +1805,10 @@ void leave_cleanup(cleanup_T *csp)
// enter_cleanup() was called, let "cause_abort" take the part of
// "force_abort" (as done by cause_errthrow()).
cause_abort = force_abort;
- force_abort = FALSE;
+ force_abort = false;
}
- // Restore the pending values of did_emsg, got_int, and current_exception.
+ // Restore the pending values of did_emsg, got_int, and did_throw.
if (pending & CSTP_ERROR) {
did_emsg = true;
}
@@ -1874,7 +1816,7 @@ void leave_cleanup(cleanup_T *csp)
got_int = true;
}
if (pending & CSTP_THROW) {
- need_rethrow = true; // current_exception will be set by do_one_cmd()
+ need_rethrow = true; // did_throw will be set by do_one_cmd()
}
// Report if required by the 'verbose' option or when debugging.
@@ -1895,7 +1837,7 @@ void leave_cleanup(cleanup_T *csp)
/// 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
+/// 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.
@@ -1904,16 +1846,14 @@ void leave_cleanup(cleanup_T *csp)
int cleanup_conditionals(cstack_T *cstack, int searched_cond, int inclusive)
{
int idx;
- int stop = FALSE;
+ int stop = false;
- for (idx = cstack->cs_idx; idx >= 0; --idx) {
+ for (idx = cstack->cs_idx; idx >= 0; idx--) {
if (cstack->cs_flags[idx] & CSF_TRY) {
- /*
- * Discard anything pending in a finally clause and continue the
- * search. There may also be a pending ":continue", ":break",
- * ":return", or ":finish" before the finally clause. We must not
- * discard it, unless an error or interrupt occurred afterwards.
- */
+ // Discard anything pending in a finally clause and continue the
+ // search. There may also be a pending ":continue", ":break",
+ // ":return", or ":finish" before the finally clause. We must not
+ // discard it, unless an error or interrupt occurred afterwards.
if (did_emsg || got_int || (cstack->cs_flags[idx] & CSF_FINALLY)) {
switch (cstack->cs_pending[idx]) {
case CSTP_NONE:
@@ -1949,11 +1889,9 @@ int cleanup_conditionals(cstack_T *cstack, int searched_cond, int inclusive)
}
}
- /*
- * Stop at a try conditional not in its finally clause. If this try
- * conditional is in an active catch clause, finish the caught
- * exception.
- */
+ // Stop at a try conditional not in its finally clause. If this try
+ // conditional is in an active catch clause, finish the caught
+ // exception.
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_FINISHED)) {
@@ -1968,31 +1906,29 @@ int cleanup_conditionals(cstack_T *cstack, int searched_cond, int inclusive)
if (searched_cond == 0 && !inclusive) {
break;
}
- stop = TRUE;
+ stop = true;
}
}
}
// Stop on the searched conditional type (even when the surrounding
// conditional is not active or something has been made pending).
- // If "inclusive" is TRUE and "searched_cond" is CSF_TRY|CSF_SILENT,
+ // If "inclusive" is true and "searched_cond" is CSF_TRY|CSF_SILENT,
// check first whether "emsg_silent" needs to be restored.
if (cstack->cs_flags[idx] & searched_cond) {
if (!inclusive) {
break;
}
- stop = TRUE;
+ stop = true;
}
cstack->cs_flags[idx] &= ~CSF_ACTIVE;
if (stop && searched_cond != (CSF_TRY | CSF_SILENT)) {
break;
}
- /*
- * When leaving a try conditional that reset "emsg_silent" on its
- * entry after saving the original value, restore that value here and
- * free the memory used to store it.
- */
+ // When leaving a try conditional that reset "emsg_silent" on its
+ // entry after saving the original value, restore that value here and
+ // free the memory used to store it.
if ((cstack->cs_flags[idx] & CSF_TRY)
&& (cstack->cs_flags[idx] & CSF_SILENT)) {
eslist_T *elem;
@@ -2031,12 +1967,12 @@ void rewind_conditionals(cstack_T *cstack, int idx, int cond_type, int *cond_lev
{
while (cstack->cs_idx > idx) {
if (cstack->cs_flags[cstack->cs_idx] & cond_type) {
- --*cond_level;
+ (*cond_level)--;
}
if (cstack->cs_flags[cstack->cs_idx] & CSF_FOR) {
free_for_info(cstack->cs_forinfo[cstack->cs_idx]);
}
- --cstack->cs_idx;
+ cstack->cs_idx--;
}
}
@@ -2046,7 +1982,7 @@ 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.
+/// @return true if the string "p" looks like a ":while" or ":for" command.
int has_loop_cmd(char *p)
{
int len;
@@ -2064,7 +2000,7 @@ int has_loop_cmd(char *p)
}
if ((p[0] == 'w' && p[1] == 'h')
|| (p[0] == 'f' && p[1] == 'o' && p[2] == 'r')) {
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
diff --git a/src/nvim/ex_eval.h b/src/nvim/ex_eval.h
index 9e3ac5e9c1..d3053ae0d4 100644
--- a/src/nvim/ex_eval.h
+++ b/src/nvim/ex_eval.h
@@ -1,7 +1,7 @@
#ifndef NVIM_EX_EVAL_H
#define NVIM_EX_EVAL_H
-#include "nvim/ex_cmds_defs.h" // for exarg_T
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_eval_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/ex_eval_defs.h b/src/nvim/ex_eval_defs.h
index 9da0c9ad12..6b3c426722 100644
--- a/src/nvim/ex_eval_defs.h
+++ b/src/nvim/ex_eval_defs.h
@@ -1,7 +1,7 @@
#ifndef NVIM_EX_EVAL_DEFS_H
#define NVIM_EX_EVAL_DEFS_H
-#include "nvim/pos.h" // for linenr_T
+#include "nvim/pos.h"
/// There is no CSF_IF, the lack of CSF_WHILE, CSF_FOR and CSF_TRY means ":if"
/// was used.
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index a0bcccb5be..76c3680742 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -1,50 +1,49 @@
// 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
-/*
- * ex_getln.c: Functions for entering and editing an Ex command line.
- */
+// ex_getln.c: Functions for entering and editing an Ex command line.
#include <assert.h>
#include <inttypes.h>
+#include <limits.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
+#include "klib/kvec.h"
#include "nvim/api/extmark.h"
+#include "nvim/api/private/defs.h"
+#include "nvim/api/private/helpers.h"
#include "nvim/api/vim.h"
#include "nvim/arabic.h"
#include "nvim/ascii.h"
-#include "nvim/assert.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
#include "nvim/cmdhist.h"
#include "nvim/cursor.h"
-#include "nvim/cursor_shape.h"
#include "nvim/digraph.h"
#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
-#include "nvim/event/loop.h"
+#include "nvim/eval/typval.h"
#include "nvim/ex_cmds.h"
+#include "nvim/ex_cmds_defs.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/extmark.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
-#include "nvim/indent.h"
#include "nvim/keycodes.h"
-#include "nvim/lib/kvec.h"
-#include "nvim/log.h"
-#include "nvim/main.h"
+#include "nvim/macros.h"
#include "nvim/mapping.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
@@ -53,15 +52,18 @@
#include "nvim/message.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
+#include "nvim/normal.h"
#include "nvim/ops.h"
#include "nvim/option.h"
#include "nvim/optionstr.h"
#include "nvim/os/input.h"
-#include "nvim/os/time.h"
+#include "nvim/os/os.h"
#include "nvim/path.h"
#include "nvim/popupmenu.h"
+#include "nvim/pos.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
+#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/state.h"
#include "nvim/strings.h"
@@ -96,7 +98,7 @@ typedef struct {
pos_T match_end;
bool did_incsearch;
bool incsearch_postponed;
- int magic_save;
+ optmagic_T magic_overruled_save;
} incsearch_state_T;
typedef struct command_line_state {
@@ -105,9 +107,9 @@ typedef struct command_line_state {
long count;
int indent;
int c;
- int gotesc; // TRUE when <ESC> just typed
- int do_abbr; // when TRUE check for abbr.
- char_u *lookfor; // string to match
+ int gotesc; // true when <ESC> just typed
+ int do_abbr; // when true check for abbr.
+ char *lookfor; // string to match
int hiscnt; // current history line in use
int save_hiscnt; // history line before attempting
// to jump to next match
@@ -115,10 +117,9 @@ typedef struct command_line_state {
incsearch_state_T is_state;
int did_wild_list; // did wild_list() recently
int wim_index; // index in wim_flags[]
- int res;
int save_msg_scroll;
int save_State; // remember State when called
- char_u *save_p_icm;
+ char *save_p_icm;
int some_key_typed; // one of the keys was typed
// mouse drag and release events are ignored, unless they are
// preceded with a mouse down event
@@ -138,6 +139,7 @@ typedef struct cmdpreview_win_info {
typedef struct cmdpreview_buf_info {
buf_T *buf;
+ bool save_b_u_synced;
time_t save_b_u_time_cur;
long save_b_u_seq_cur;
u_header_T *save_b_u_newhead;
@@ -154,6 +156,14 @@ typedef struct cmdpreview_info {
garray_T save_view;
} CpInfo;
+/// Return value when handling keys in command-line mode.
+enum {
+ CMDLINE_NOT_CHANGED = 1,
+ CMDLINE_CHANGED = 2,
+ GOTO_NORMAL_MODE = 3,
+ PROCESS_NEXT_KEY = 4,
+};
+
/// 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().
@@ -170,6 +180,8 @@ static Array cmdline_block = ARRAY_DICT_INIT;
/// user interrupting highlight function to not interrupt command-line.
static bool getln_interrupted_highlight = false;
+static int cedit_key = -1; ///< key value of 'cedit' option
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ex_getln.c.generated.h"
#endif
@@ -206,7 +218,7 @@ static void init_incsearch_state(incsearch_state_T *s)
s->match_start = curwin->w_cursor;
s->did_incsearch = false;
s->incsearch_postponed = false;
- s->magic_save = p_magic;
+ s->magic_overruled_save = magic_overruled;
clearpos(&s->match_end);
s->save_cursor = curwin->w_cursor; // may be restored later
s->search_start = curwin->w_cursor;
@@ -229,6 +241,7 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s
pos_T save_cursor;
bool use_last_pat;
bool retval = false;
+ magic_T magic = 0;
*skiplen = 0;
*patlen = ccline.cmdlen;
@@ -253,7 +266,7 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s
exarg_T ea = {
.line1 = 1,
.line2 = 1,
- .cmd = (char *)ccline.cmdbuff,
+ .cmd = ccline.cmdbuff,
.addr_type = ADDR_LINES,
};
@@ -261,7 +274,7 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s
parse_command_modifiers(&ea, &dummy, &dummy_cmdmod, true);
cmd = skip_range(ea.cmd, NULL);
- if (vim_strchr("sgvl", *cmd) == NULL) {
+ if (vim_strchr("sgvl", (uint8_t)(*cmd)) == NULL) {
goto theend;
}
@@ -271,16 +284,16 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s
goto theend;
}
- if (STRNCMP(cmd, "substitute", p - cmd) == 0
- || STRNCMP(cmd, "smagic", p - cmd) == 0
- || STRNCMP(cmd, "snomagic", MAX(p - cmd, 3)) == 0
- || STRNCMP(cmd, "vglobal", p - cmd) == 0) {
+ if (strncmp(cmd, "substitute", (size_t)(p - cmd)) == 0
+ || strncmp(cmd, "smagic", (size_t)(p - cmd)) == 0
+ || strncmp(cmd, "snomagic", (size_t)MAX(p - cmd, 3)) == 0
+ || strncmp(cmd, "vglobal", (size_t)(p - cmd)) == 0) {
if (*cmd == 's' && cmd[1] == 'm') {
- p_magic = true;
+ magic_overruled = OPTION_MAGIC_ON;
} else if (*cmd == 's' && cmd[1] == 'n') {
- p_magic = false;
+ magic_overruled = OPTION_MAGIC_OFF;
}
- } else if (STRNCMP(cmd, "sort", MAX(p - cmd, 3)) == 0) {
+ } else if (strncmp(cmd, "sort", (size_t)MAX(p - cmd, 3)) == 0) {
// skip over ! and flags
if (*p == '!') {
p = skipwhite(p + 1);
@@ -291,11 +304,11 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s
if (*p == NUL) {
goto theend;
}
- } else if (STRNCMP(cmd, "vimgrep", MAX(p - cmd, 3)) == 0
- || STRNCMP(cmd, "vimgrepadd", MAX(p - cmd, 8)) == 0
- || STRNCMP(cmd, "lvimgrep", MAX(p - cmd, 2)) == 0
- || STRNCMP(cmd, "lvimgrepadd", MAX(p - cmd, 9)) == 0
- || STRNCMP(cmd, "global", p - cmd) == 0) {
+ } else if (strncmp(cmd, "vimgrep", (size_t)MAX(p - cmd, 3)) == 0
+ || strncmp(cmd, "vimgrepadd", (size_t)MAX(p - cmd, 8)) == 0
+ || strncmp(cmd, "lvimgrep", (size_t)MAX(p - cmd, 2)) == 0
+ || strncmp(cmd, "lvimgrepadd", (size_t)MAX(p - cmd, 9)) == 0
+ || strncmp(cmd, "global", (size_t)(p - cmd)) == 0) {
// skip over "!/".
if (*p == '!') {
p++;
@@ -311,9 +324,9 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s
}
p = skipwhite(p);
- delim = (delim_optional && vim_isIDc(*p)) ? ' ' : *p++;
+ delim = (delim_optional && vim_isIDc((uint8_t)(*p))) ? ' ' : *p++;
*search_delim = delim;
- end = (char *)skip_regexp((char_u *)p, delim, p_magic, NULL);
+ end = skip_regexp_ex(p, delim, magic_isset(), NULL, NULL, &magic);
use_last_pat = end == p && *end == delim;
if (end == p && !use_last_pat) {
@@ -323,10 +336,8 @@ 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 c = *end;
- int empty;
-
*end = NUL;
- empty = empty_pattern((char_u *)p);
+ bool empty = empty_pattern_magic(p, strlen(p), magic);
*end = c;
if (empty) {
goto theend;
@@ -334,7 +345,7 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s
}
// found a non-empty pattern or //
- *skiplen = (int)((char_u *)p - ccline.cmdbuff);
+ *skiplen = (int)(p - ccline.cmdbuff);
*patlen = (int)(end - p);
// parse the address range
@@ -369,8 +380,8 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat
pos_T end_pos;
proftime_T tm;
int skiplen, patlen;
- char_u next_char;
- char_u use_last_pat;
+ char next_char;
+ bool use_last_pat;
int search_delim;
// Parsing range may already set the last search pattern.
@@ -415,7 +426,7 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat
if (patlen == 0 && !use_last_pat) {
found = 0;
set_no_hlsearch(true); // turn off previous highlight
- redraw_all_later(SOME_VALID);
+ redraw_all_later(UPD_SOME_VALID);
} else {
int search_flags = SEARCH_OPT + SEARCH_NOOF + SEARCH_PEEK;
ui_busy_start();
@@ -481,14 +492,14 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat
} else {
end_pos = curwin->w_cursor; // shutup gcc 4
}
- //
+
// Disable 'hlsearch' highlighting if the pattern matches
// everything. Avoids a flash when typing "foo\|".
if (!use_last_pat) {
next_char = ccline.cmdbuff[skiplen + patlen];
ccline.cmdbuff[skiplen + patlen] = NUL;
- if (empty_pattern(ccline.cmdbuff) && !no_hlsearch) {
- redraw_all_later(SOME_VALID);
+ if (empty_pattern(ccline.cmdbuff + skiplen, search_delim) && !no_hlsearch) {
+ redraw_all_later(UPD_SOME_VALID);
set_no_hlsearch(true);
}
ccline.cmdbuff[skiplen + patlen] = next_char;
@@ -500,7 +511,8 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat
curwin->w_redr_status = true;
}
- update_screen(SOME_VALID);
+ redraw_later(curwin, UPD_SOME_VALID);
+ update_screen();
highlight_match = false;
restore_last_search_pattern();
@@ -550,7 +562,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((p_magic ? "\\~^$.*[" : "\\^$"), *c) != NULL) {
+ || vim_strchr((magic_isset() ? "\\~^$.*[" : "\\^$"), *c) != NULL) {
// put a backslash before special characters
stuffcharReadbuff(*c);
*c = '\\';
@@ -563,60 +575,74 @@ static int may_add_char_to_search(int firstc, int *c, incsearch_state_T *s)
static void finish_incsearch_highlighting(int gotesc, incsearch_state_T *s, bool call_update_screen)
{
- if (s->did_incsearch) {
- s->did_incsearch = false;
- if (gotesc) {
+ if (!s->did_incsearch) {
+ return;
+ }
+
+ s->did_incsearch = false;
+ if (gotesc) {
+ curwin->w_cursor = s->save_cursor;
+ } else {
+ if (!equalpos(s->save_cursor, s->search_start)) {
+ // put the '" mark at the original position
curwin->w_cursor = s->save_cursor;
- } else {
- if (!equalpos(s->save_cursor, s->search_start)) {
- // put the '" mark at the original position
- curwin->w_cursor = s->save_cursor;
- setpcmark();
- }
- curwin->w_cursor = s->search_start; // -V519
+ setpcmark();
}
- restore_viewstate(curwin, &s->old_viewstate);
- highlight_match = false;
+ curwin->w_cursor = s->search_start; // -V519
+ }
+ restore_viewstate(curwin, &s->old_viewstate);
+ highlight_match = false;
- // by default search all lines
- search_first_line = 0;
- search_last_line = MAXLNUM;
+ // by default search all lines
+ search_first_line = 0;
+ search_last_line = MAXLNUM;
- p_magic = s->magic_save;
+ magic_overruled = s->magic_overruled_save;
- validate_cursor(); // needed for TAB
- redraw_all_later(SOME_VALID);
- if (call_update_screen) {
- update_screen(SOME_VALID);
- }
+ validate_cursor(); // needed for TAB
+ redraw_all_later(UPD_SOME_VALID);
+ if (call_update_screen) {
+ update_screen();
}
}
-/// Internal entry point for cmdline mode.
-///
-/// @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)
+/// Initialize the current command-line info.
+static void init_ccline(int firstc, int indent)
{
- const bool cmdheight0 = !ui_has_messages();
+ ccline.overstrike = false; // always start in insert mode
- if (cmdheight0) {
- const long save_so = lastwin->w_p_so;
+ assert(indent >= 0);
- // If cmdheight is 0, cmdheight must be set to 1 when we enter the
- // command line. Set "made_cmdheight_nonzero" and reset 'scrolloff' to
- // avoid scrolling the last window.
- made_cmdheight_nonzero = true;
+ // set some variables for redrawcmd()
+ ccline.cmdfirstc = (firstc == '@' ? 0 : firstc);
+ ccline.cmdindent = (firstc > 0 ? indent : 0);
- lastwin->w_p_so = 0;
- set_option_value("ch", 1L, NULL, 0);
- update_screen(VALID); // redraw the screen NOW
+ // alloc initial ccline.cmdbuff
+ alloc_cmdbuff(indent + 50);
+ ccline.cmdlen = ccline.cmdpos = 0;
+ ccline.cmdbuff[0] = NUL;
- made_cmdheight_nonzero = false;
- lastwin->w_p_so = save_so;
+ ccline.last_colors = (ColoredCmdline){ .cmdbuff = NULL,
+ .colors = KV_INITIAL_VALUE };
+ sb_text_start_cmdline();
+
+ // autoindent for :insert and :append
+ if (firstc <= 0) {
+ memset(ccline.cmdbuff, ' ', (size_t)indent);
+ ccline.cmdbuff[indent] = NUL;
+ ccline.cmdpos = indent;
+ ccline.cmdspos = indent;
+ ccline.cmdlen = indent;
}
+}
+/// Internal entry point for cmdline mode.
+///
+/// @param count only used for incremental search
+/// @param indent indent for inside conditionals
+/// @param clear_ccline clear ccline first
+static uint8_t *command_line_enter(int firstc, long count, int indent, bool clear_ccline)
+{
// can be invoked recursively, identify each level
static int cmdline_level = 0;
cmdline_level++;
@@ -632,20 +658,20 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init
.ignore_drag_release = true,
};
CommandLineState *s = &state;
- s->save_p_icm = vim_strsave(p_icm);
+ s->save_p_icm = xstrdup(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.
+ // Currently ccline can never be in use if clear_ccline is false.
// Some changes will be needed if this is no longer the case.
- assert(init_ccline);
+ assert(clear_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) {
+ } else if (clear_ccline) {
CLEAR_FIELD(ccline);
}
@@ -659,33 +685,9 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init
cmd_hkmap = 0;
}
+ init_ccline(s->firstc, s->indent);
ccline.prompt_id = last_prompt_id++;
ccline.level = cmdline_level;
- ccline.overstrike = false; // always start in insert mode
-
- assert(indent >= 0);
-
- // set some variables for redrawcmd()
- ccline.cmdfirstc = (s->firstc == '@' ? 0 : s->firstc);
- ccline.cmdindent = (s->firstc > 0 ? s->indent : 0);
-
- // alloc initial ccline.cmdbuff
- alloc_cmdbuff(indent + 50);
- ccline.cmdlen = ccline.cmdpos = 0;
- ccline.cmdbuff[0] = NUL;
-
- ccline.last_colors = (ColoredCmdline){ .cmdbuff = NULL,
- .colors = KV_INITIAL_VALUE };
- sb_text_start_cmdline();
-
- // autoindent for :insert and :append
- if (s->firstc <= 0) {
- memset(ccline.cmdbuff, ' ', (size_t)s->indent);
- ccline.cmdbuff[s->indent] = NUL;
- ccline.cmdpos = s->indent;
- ccline.cmdspos = s->indent;
- ccline.cmdlen = s->indent;
- }
if (cmdline_level == 50) {
// Somehow got into a loop recursively calling getcmdline(), bail out.
@@ -722,8 +724,8 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init
if (ccline.input_fn) {
s->xpc.xp_context = ccline.xp_context;
- s->xpc.xp_pattern = (char *)ccline.cmdbuff;
- s->xpc.xp_arg = (char *)ccline.xp_arg;
+ s->xpc.xp_pattern = ccline.cmdbuff;
+ s->xpc.xp_arg = ccline.xp_arg;
}
// Avoid scrolling when called by a recursive do_cmdline(), e.g. when
@@ -751,13 +753,14 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init
TryState tstate;
Error err = ERROR_INIT;
bool tl_ret = true;
- save_v_event_T save_v_event;
- dict_T *dict = get_v_event(&save_v_event);
char firstcbuf[2];
firstcbuf[0] = (char)(firstc > 0 ? firstc : '-');
firstcbuf[1] = 0;
if (has_event(EVENT_CMDLINEENTER)) {
+ save_v_event_T save_v_event;
+ dict_T *dict = get_v_event(&save_v_event);
+
// set v:event to a dictionary with information about the commandline
tv_dict_add_str(dict, S_LEN("cmdtype"), firstcbuf);
tv_dict_add_nr(dict, S_LEN("cmdlevel"), ccline.level);
@@ -770,7 +773,8 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init
tl_ret = try_leave(&tstate, &err);
if (!tl_ret && ERROR_SET(&err)) {
msg_putchar('\n');
- msg_printf_attr(HL_ATTR(HLF_E)|MSG_HIST, (char *)e_autocmd_err, err.msg);
+ msg_scroll = true;
+ msg_puts_attr(err.msg, HL_ATTR(HLF_E)|MSG_HIST);
api_clear_error(&err);
redrawcmd();
}
@@ -791,11 +795,11 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init
// Redraw the statusline in case it uses the current mode using the mode()
// function.
- if (!cmd_silent && msg_scrolled == 0) {
+ if (!cmd_silent) {
bool found_one = false;
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (*p_stl != NUL || *wp->w_p_stl != NUL) {
+ if (*p_stl != NUL || *wp->w_p_stl != NUL || *p_wbr != NUL || *wp->w_p_wbr != NUL) {
wp->w_redr_status = true;
found_one = true;
}
@@ -819,6 +823,9 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init
state_enter(&s->state);
if (has_event(EVENT_CMDLINELEAVE)) {
+ save_v_event_T save_v_event;
+ dict_T *dict = get_v_event(&save_v_event);
+
tv_dict_add_str(dict, S_LEN("cmdtype"), firstcbuf);
tv_dict_add_nr(dict, S_LEN("cmdlevel"), ccline.level);
tv_dict_set_keys_readonly(dict);
@@ -852,7 +859,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init
s->histype == HIST_SEARCH ? s->firstc : NUL);
if (s->firstc == ':') {
xfree(new_last_cmdline);
- new_last_cmdline = vim_strsave(ccline.cmdbuff);
+ new_last_cmdline = xstrdup(ccline.cmdbuff);
}
}
@@ -865,12 +872,18 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init
// If the line is too long, clear it, so ruler and shown command do
// not get printed in the middle of it.
msg_check();
+ if (p_ch == 0 && !ui_has(kUIMessages)) {
+ if (must_redraw < UPD_VALID) {
+ must_redraw = UPD_VALID;
+ }
+ }
msg_scroll = s->save_msg_scroll;
redir_off = false;
if (!tl_ret && ERROR_SET(&err)) {
msg_putchar('\n');
- msg_printf_attr(HL_ATTR(HLF_E)|MSG_HIST, (char *)e_autocmd_err, err.msg);
+ emsg(err.msg);
+ did_emsg = false;
api_clear_error(&err);
}
@@ -879,11 +892,11 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init
need_wait_return = false;
}
- set_string_option_direct("icm", -1, (char *)s->save_p_icm, OPT_FREE, SID_NONE);
+ set_string_option_direct("icm", -1, 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);
+ redraw_all_later(UPD_SOME_VALID);
}
may_trigger_modechanged();
setmouse();
@@ -895,7 +908,7 @@ theend:
xfree(ccline.last_colors.cmdbuff);
kv_destroy(ccline.last_colors.colors);
- char_u *p = ccline.cmdbuff;
+ char *p = ccline.cmdbuff;
if (ui_has(kUICmdline)) {
ui_call_cmdline_hide(ccline.level);
@@ -910,18 +923,7 @@ theend:
ccline.cmdbuff = NULL;
}
- if (cmdheight0) {
- made_cmdheight_nonzero = true;
-
- // Restore cmdheight
- set_option_value("ch", 0L, NULL, 0);
- // Redraw is needed for command line completion
- redraw_all_later(CLEAR);
-
- made_cmdheight_nonzero = false;
- }
-
- return p;
+ return (uint8_t *)p;
}
static int command_line_check(VimState *state)
@@ -940,6 +942,191 @@ static int command_line_check(VimState *state)
return 1;
}
+/// Handle CTRL-\ pressed in Command-line mode:
+/// - CTRL-\ CTRL-N or CTRL-\ CTRL-G goes to Normal mode.
+/// - CTRL-\ e prompts for an expression.
+static int command_line_handle_ctrl_bsl(CommandLineState *s)
+{
+ 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
+ && s->c != Ctrl_G
+ && (s->c != 'e'
+ || (ccline.cmdfirstc == '=' && KeyTyped)
+ || cmdline_star > 0)) {
+ vungetc(s->c);
+ return PROCESS_NEXT_KEY;
+ }
+
+ if (s->c == 'e') {
+ // Replace the command line with the result of an expression.
+ // This will call getcmdline() recursively in get_expr_register().
+ if (ccline.cmdpos == ccline.cmdlen) {
+ new_cmdpos = 99999; // keep it at the end
+ } else {
+ new_cmdpos = ccline.cmdpos;
+ }
+
+ s->c = get_expr_register();
+ if (s->c == '=') {
+ // Evaluate the expression. Set "textlock" to avoid nasty things
+ // like going to another buffer.
+ textlock++;
+ char *p = get_expr_line();
+ textlock--;
+
+ if (p != NULL) {
+ int len = (int)strlen(p);
+ realloc_cmdbuff(len + 1);
+ ccline.cmdlen = len;
+ STRCPY(ccline.cmdbuff, p);
+ xfree(p);
+
+ // Restore the cursor or use the position set with
+ // set_cmdline_pos().
+ if (new_cmdpos > ccline.cmdlen) {
+ ccline.cmdpos = ccline.cmdlen;
+ } else {
+ ccline.cmdpos = new_cmdpos;
+ }
+
+ KeyTyped = false; // Don't do p_wc completion.
+ redrawcmd();
+ return CMDLINE_CHANGED;
+ }
+ }
+ beep_flush();
+ got_int = false; // don't abandon the command line
+ did_emsg = false;
+ emsg_on_display = false;
+ redrawcmd();
+ return CMDLINE_NOT_CHANGED;
+ }
+
+ s->gotesc = true; // will free ccline.cmdbuff after putting it in history
+ return GOTO_NORMAL_MODE;
+}
+
+/// Completion for 'wildchar' or 'wildcharm' key.
+/// - hitting <ESC> twice means: abandon command line.
+/// - wildcard expansion is only done when the 'wildchar' key is really
+/// typed, not when it comes from a macro
+/// @return CMDLINE_CHANGED if command line is changed or CMDLINE_NOT_CHANGED.
+static int command_line_wildchar_complete(CommandLineState *s)
+{
+ int res;
+ int options = WILD_NO_BEEP;
+ if (wim_flags[s->wim_index] & WIM_BUFLASTUSED) {
+ options |= WILD_BUFLASTUSED;
+ }
+ if (s->xpc.xp_numfiles > 0) { // typed p_wc at least twice
+ // if 'wildmode' contains "list" may still need to list
+ if (s->xpc.xp_numfiles > 1
+ && !s->did_wild_list
+ && ((wim_flags[s->wim_index] & WIM_LIST)
+ || (p_wmnu && (wim_flags[s->wim_index] & WIM_FULL) != 0))) {
+ (void)showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & WIM_LIST) == 0));
+ redrawcmd();
+ s->did_wild_list = true;
+ }
+
+ if (wim_flags[s->wim_index] & WIM_LONGEST) {
+ res = nextwild(&s->xpc, WILD_LONGEST, options, s->firstc != '@');
+ } else if (wim_flags[s->wim_index] & WIM_FULL) {
+ res = nextwild(&s->xpc, WILD_NEXT, options, s->firstc != '@');
+ } else {
+ res = OK; // don't insert 'wildchar' now
+ }
+ } else { // typed p_wc first time
+ s->wim_index = 0;
+ int j = ccline.cmdpos;
+
+ // if 'wildmode' first contains "longest", get longest
+ // common part
+ if (wim_flags[0] & WIM_LONGEST) {
+ res = nextwild(&s->xpc, WILD_LONGEST, options, s->firstc != '@');
+ } else {
+ res = nextwild(&s->xpc, WILD_EXPAND_KEEP, options, s->firstc != '@');
+ }
+
+ // if interrupted while completing, behave like it failed
+ if (got_int) {
+ (void)vpeekc(); // remove <C-C> from input stream
+ got_int = false; // don't abandon the command line
+ (void)ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE);
+ s->xpc.xp_context = EXPAND_NOTHING;
+ return CMDLINE_CHANGED;
+ }
+
+ // when more than one match, and 'wildmode' first contains
+ // "list", or no change and 'wildmode' contains "longest,list",
+ // list all matches
+ if (res == OK && s->xpc.xp_numfiles > 1) {
+ // a "longest" that didn't do anything is skipped (but not
+ // "list:longest")
+ if (wim_flags[0] == WIM_LONGEST && ccline.cmdpos == j) {
+ s->wim_index = 1;
+ }
+ if ((wim_flags[s->wim_index] & WIM_LIST)
+ || (p_wmnu && (wim_flags[s->wim_index] & WIM_FULL) != 0)) {
+ if (!(wim_flags[0] & WIM_LONGEST)) {
+ int p_wmnu_save = p_wmnu;
+ p_wmnu = 0;
+ // remove match
+ nextwild(&s->xpc, WILD_PREV, 0, s->firstc != '@');
+ p_wmnu = p_wmnu_save;
+ }
+
+ (void)showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & WIM_LIST) == 0));
+ redrawcmd();
+ s->did_wild_list = true;
+
+ if (wim_flags[s->wim_index] & WIM_LONGEST) {
+ nextwild(&s->xpc, WILD_LONGEST, options, s->firstc != '@');
+ } else if (wim_flags[s->wim_index] & WIM_FULL) {
+ nextwild(&s->xpc, WILD_NEXT, options, s->firstc != '@');
+ }
+ } else {
+ vim_beep(BO_WILD);
+ }
+ } else if (s->xpc.xp_numfiles == -1) {
+ s->xpc.xp_context = EXPAND_NOTHING;
+ }
+ }
+
+ if (s->wim_index < 3) {
+ s->wim_index++;
+ }
+
+ if (s->c == ESC) {
+ s->gotesc = true;
+ }
+
+ return (res == OK) ? CMDLINE_CHANGED : CMDLINE_NOT_CHANGED;
+}
+
+static void command_line_end_wildmenu(CommandLineState *s)
+{
+ if (cmdline_pum_active()) {
+ cmdline_pum_remove();
+ }
+ if (s->xpc.xp_numfiles != -1) {
+ (void)ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE);
+ }
+ s->did_wild_list = false;
+ if (!p_wmnu || (s->c != K_UP && s->c != K_DOWN)) {
+ s->xpc.xp_context = EXPAND_NOTHING;
+ }
+ s->wim_index = 0;
+ wildmenu_cleanup(&ccline);
+}
+
static int command_line_execute(VimState *state, int key)
{
if (key == K_IGNORE || key == K_NOP) {
@@ -958,6 +1145,19 @@ static int command_line_execute(VimState *state, int key)
map_execute_lua();
}
+ // nvim_select_popupmenu_item() can be called from the handling of
+ // K_EVENT, K_COMMAND, or K_LUA.
+ if (pum_want.active) {
+ if (cmdline_pum_active()) {
+ nextwild(&s->xpc, WILD_PUM_WANT, 0, s->firstc != '@');
+ if (pum_want.finish) {
+ nextwild(&s->xpc, WILD_APPLY, WILD_NO_BEEP, s->firstc != '@');
+ command_line_end_wildmenu(s);
+ }
+ }
+ pum_want.active = false;
+ }
+
if (!cmdline_was_last_drawn) {
redrawcmdline();
}
@@ -1026,100 +1226,49 @@ static int command_line_execute(VimState *state, int key)
}
if (cmdline_pum_active() || s->did_wild_list) {
+ // Ctrl-Y: Accept the current selection and close the popup menu.
+ // Ctrl-E: cancel the cmdline popup menu and return the original text.
if (s->c == Ctrl_E || s->c == Ctrl_Y) {
const int wild_type = (s->c == Ctrl_E) ? WILD_CANCEL : WILD_APPLY;
- s->res = nextwild(&s->xpc, wild_type, WILD_NO_BEEP, s->firstc != '@');
+ (void)nextwild(&s->xpc, wild_type, WILD_NO_BEEP, s->firstc != '@');
s->c = Ctrl_E;
}
}
+ // The wildmenu is cleared if the pressed key is not used for
+ // navigating the wild menu (i.e. the key is not 'wildchar' or
+ // 'wildcharm' or Ctrl-N or Ctrl-P or Ctrl-A or Ctrl-L).
+ // If the popup menu is displayed, then PageDown and PageUp keys are
+ // also used to navigate the menu.
+ bool end_wildmenu = (!(s->c == p_wc && KeyTyped) && s->c != p_wcm && s->c != Ctrl_Z
+ && s->c != Ctrl_N && s->c != Ctrl_P && s->c != Ctrl_A
+ && s->c != Ctrl_L);
+ end_wildmenu = end_wildmenu && (!cmdline_pum_active()
+ || (s->c != K_PAGEDOWN && s->c != K_PAGEUP
+ && s->c != K_KPAGEDOWN && s->c != K_KPAGEUP));
+
// free expanded names when finished walking through matches
- if (!(s->c == p_wc && KeyTyped) && s->c != p_wcm && s->c != Ctrl_Z
- && s->c != Ctrl_N && s->c != Ctrl_P && s->c != Ctrl_A
- && s->c != Ctrl_L) {
- if (cmdline_pum_active()) {
- cmdline_pum_remove();
- }
- if (s->xpc.xp_numfiles != -1) {
- (void)ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE);
- }
- s->did_wild_list = false;
- if (!p_wmnu || (s->c != K_UP && s->c != K_DOWN)) {
- s->xpc.xp_context = EXPAND_NOTHING;
- }
- s->wim_index = 0;
- wildmenu_cleanup(&ccline);
+ if (end_wildmenu) {
+ command_line_end_wildmenu(s);
}
if (p_wmnu) {
s->c = wildmenu_process_key(&ccline, s->c, &s->xpc);
}
- // CTRL-\ CTRL-N goes to Normal mode, CTRL-\ e prompts for an expression.
+ // CTRL-\ CTRL-N or CTRL-\ CTRL-G 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
- && s->c != Ctrl_G
- && (s->c != 'e'
- || (ccline.cmdfirstc == '=' && KeyTyped)
- || cmdline_star > 0)) {
- vungetc(s->c);
- s->c = Ctrl_BSL;
- } else if (s->c == 'e') {
- char_u *p = NULL;
- int len;
-
- // Replace the command line with the result of an expression.
- // Need to save and restore the current command line, to be
- // able to enter a new one...
- if (ccline.cmdpos == ccline.cmdlen) {
- new_cmdpos = 99999; // keep it at the end
- } else {
- new_cmdpos = ccline.cmdpos;
- }
-
- s->c = get_expr_register();
- if (s->c == '=') {
- textlock++;
- p = get_expr_line();
- textlock--;
-
- if (p != NULL) {
- len = (int)STRLEN(p);
- realloc_cmdbuff(len + 1);
- ccline.cmdlen = len;
- STRCPY(ccline.cmdbuff, p);
- xfree(p);
-
- // Restore the cursor or use the position set with
- // set_cmdline_pos().
- if (new_cmdpos > ccline.cmdlen) {
- ccline.cmdpos = ccline.cmdlen;
- } else {
- ccline.cmdpos = new_cmdpos;
- }
-
- KeyTyped = false; // Don't do p_wc completion.
- redrawcmd();
- return command_line_changed(s);
- }
- }
- beep_flush();
- got_int = false; // don't abandon the command line
- did_emsg = false;
- emsg_on_display = false;
- redrawcmd();
+ switch (command_line_handle_ctrl_bsl(s)) {
+ case CMDLINE_CHANGED:
+ return command_line_changed(s);
+ case CMDLINE_NOT_CHANGED:
return command_line_not_changed(s);
- } else {
- s->gotesc = true; // will free ccline.cmdbuff after putting it
- // in history
- return 0; // back to Normal mode
+ case GOTO_NORMAL_MODE:
+ return 0; // back to cmd mode
+ default:
+ s->c = Ctrl_BSL; // backslash key not processed by
+ // command_line_handle_ctrl_bsl()
}
}
@@ -1167,107 +1316,8 @@ static int command_line_execute(VimState *state, int key)
}
// Completion for 'wildchar' or 'wildcharm' key.
- // - hitting <ESC> twice means: abandon command line.
- // - wildcard expansion is only done when the 'wildchar' key is really
- // typed, not when it comes from a macro
- if ((s->c == p_wc && !s->gotesc && KeyTyped) || s->c == p_wcm
- || s->c == Ctrl_Z) {
- int options = WILD_NO_BEEP;
- if (wim_flags[s->wim_index] & WIM_BUFLASTUSED) {
- options |= WILD_BUFLASTUSED;
- }
- if (s->xpc.xp_numfiles > 0) { // typed p_wc at least twice
- // if 'wildmode' contains "list" may still need to list
- if (s->xpc.xp_numfiles > 1
- && !s->did_wild_list
- && ((wim_flags[s->wim_index] & WIM_LIST)
- || (p_wmnu && (wim_flags[s->wim_index] & WIM_FULL) != 0))) {
- (void)showmatches(&s->xpc, p_wmnu
- && ((wim_flags[s->wim_index] & WIM_LIST) == 0));
- redrawcmd();
- s->did_wild_list = true;
- }
-
- if (wim_flags[s->wim_index] & WIM_LONGEST) {
- s->res = nextwild(&s->xpc, WILD_LONGEST, options,
- s->firstc != '@');
- } else if (wim_flags[s->wim_index] & WIM_FULL) {
- s->res = nextwild(&s->xpc, WILD_NEXT, options,
- s->firstc != '@');
- } else {
- s->res = OK; // don't insert 'wildchar' now
- }
- } else { // typed p_wc first time
- s->wim_index = 0;
- int j = ccline.cmdpos;
-
- // if 'wildmode' first contains "longest", get longest
- // common part
- if (wim_flags[0] & WIM_LONGEST) {
- s->res = nextwild(&s->xpc, WILD_LONGEST, options,
- s->firstc != '@');
- } else {
- s->res = nextwild(&s->xpc, WILD_EXPAND_KEEP, options,
- s->firstc != '@');
- }
-
- // if interrupted while completing, behave like it failed
- if (got_int) {
- (void)vpeekc(); // remove <C-C> from input stream
- got_int = false; // don't abandon the command line
- (void)ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE);
- s->xpc.xp_context = EXPAND_NOTHING;
- return command_line_changed(s);
- }
-
- // when more than one match, and 'wildmode' first contains
- // "list", or no change and 'wildmode' contains "longest,list",
- // list all matches
- if (s->res == OK && s->xpc.xp_numfiles > 1) {
- // a "longest" that didn't do anything is skipped (but not
- // "list:longest")
- if (wim_flags[0] == WIM_LONGEST && ccline.cmdpos == j) {
- s->wim_index = 1;
- }
- if ((wim_flags[s->wim_index] & WIM_LIST)
- || (p_wmnu && (wim_flags[s->wim_index] & WIM_FULL) != 0)) {
- if (!(wim_flags[0] & WIM_LONGEST)) {
- int p_wmnu_save = p_wmnu;
- p_wmnu = 0;
- // remove match
- nextwild(&s->xpc, WILD_PREV, 0, s->firstc != '@');
- p_wmnu = p_wmnu_save;
- }
-
- (void)showmatches(&s->xpc, p_wmnu
- && ((wim_flags[s->wim_index] & WIM_LIST) == 0));
- redrawcmd();
- s->did_wild_list = true;
-
- if (wim_flags[s->wim_index] & WIM_LONGEST) {
- nextwild(&s->xpc, WILD_LONGEST, options,
- s->firstc != '@');
- } else if (wim_flags[s->wim_index] & WIM_FULL) {
- nextwild(&s->xpc, WILD_NEXT, options,
- s->firstc != '@');
- }
- } else {
- vim_beep(BO_WILD);
- }
- } else if (s->xpc.xp_numfiles == -1) {
- s->xpc.xp_context = EXPAND_NOTHING;
- }
- }
-
- if (s->wim_index < 3) {
- ++s->wim_index;
- }
-
- if (s->c == ESC) {
- s->gotesc = true;
- }
-
- if (s->res == OK) {
+ if ((s->c == p_wc && !s->gotesc && KeyTyped) || s->c == p_wcm || s->c == Ctrl_Z) {
+ if (command_line_wildchar_complete(s) == CMDLINE_CHANGED) {
return command_line_changed(s);
}
}
@@ -1277,8 +1327,11 @@ static int command_line_execute(VimState *state, int key)
// <S-Tab> goes to last match, in a clumsy way
if (s->c == K_S_TAB && KeyTyped) {
if (nextwild(&s->xpc, WILD_EXPAND_KEEP, 0, s->firstc != '@') == OK) {
- // Trigger the popup menu when wildoptions=pum
- showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & WIM_LIST) == 0));
+ if (s->xpc.xp_numfiles > 1
+ && ((!s->did_wild_list && (wim_flags[s->wim_index] & WIM_LIST)) || p_wmnu)) {
+ // Trigger the popup menu when wildoptions=pum
+ showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & WIM_LIST) == 0));
+ }
nextwild(&s->xpc, WILD_PREV, 0, s->firstc != '@');
nextwild(&s->xpc, WILD_PREV, 0, s->firstc != '@');
return command_line_changed(s);
@@ -1321,9 +1374,9 @@ static int may_do_command_line_next_incsearch(int firstc, long count, incsearch_
ui_flush();
pos_T t;
- char_u *pat;
+ char *pat;
int search_flags = SEARCH_NOOF;
- char_u save;
+ char save;
if (search_delim == ccline.cmdbuff[skiplen]) {
pat = last_search_pattern();
@@ -1332,7 +1385,7 @@ static int may_do_command_line_next_incsearch(int firstc, long count, incsearch_
return FAIL;
}
skiplen = 0;
- patlen = (int)STRLEN(pat);
+ patlen = (int)strlen(pat);
} else {
pat = ccline.cmdbuff + skiplen;
}
@@ -1395,7 +1448,8 @@ static int may_do_command_line_next_incsearch(int firstc, long count, incsearch_
validate_cursor();
highlight_match = true;
save_viewstate(curwin, &s->old_viewstate);
- update_screen(NOT_VALID);
+ redraw_later(curwin, UPD_NOT_VALID);
+ update_screen();
highlight_match = false;
redrawcmdline();
curwin->w_cursor = s->match_end;
@@ -1406,9 +1460,202 @@ static int may_do_command_line_next_incsearch(int firstc, long count, incsearch_
return FAIL;
}
+/// Handle backspace, delete and CTRL-W keys in the command-line mode.
+static int command_line_erase_chars(CommandLineState *s)
+{
+ if (s->c == K_KDEL) {
+ s->c = K_DEL;
+ }
+
+ // Delete current character is the same as backspace on next
+ // character, except at end of line
+ if (s->c == K_DEL && ccline.cmdpos != ccline.cmdlen) {
+ ccline.cmdpos++;
+ }
+
+ if (s->c == K_DEL) {
+ ccline.cmdpos += mb_off_next(ccline.cmdbuff,
+ ccline.cmdbuff + ccline.cmdpos);
+ }
+
+ if (ccline.cmdpos > 0) {
+ char *p;
+
+ int j = ccline.cmdpos;
+ p = mb_prevptr(ccline.cmdbuff, ccline.cmdbuff + j);
+
+ if (s->c == Ctrl_W) {
+ while (p > ccline.cmdbuff && ascii_isspace(*p)) {
+ p = mb_prevptr(ccline.cmdbuff, p);
+ }
+
+ int i = mb_get_class(p);
+ while (p > ccline.cmdbuff && mb_get_class(p) == i) {
+ p = mb_prevptr(ccline.cmdbuff, p);
+ }
+
+ if (mb_get_class(p) != i) {
+ p += utfc_ptr2len(p);
+ }
+ }
+
+ ccline.cmdpos = (int)(p - ccline.cmdbuff);
+ ccline.cmdlen -= j - ccline.cmdpos;
+ int i = ccline.cmdpos;
+
+ while (i < ccline.cmdlen) {
+ ccline.cmdbuff[i++] = ccline.cmdbuff[j++];
+ }
+
+ // Truncate at the end, required for multi-byte chars.
+ ccline.cmdbuff[ccline.cmdlen] = NUL;
+ if (ccline.cmdlen == 0) {
+ s->is_state.search_start = s->is_state.save_cursor;
+ // save view settings, so that the screen won't be restored at the
+ // wrong position
+ s->is_state.old_viewstate = s->is_state.init_viewstate;
+ }
+ redrawcmd();
+ } else if (ccline.cmdlen == 0 && s->c != Ctrl_W
+ && ccline.cmdprompt == NULL && s->indent == 0) {
+ // In ex and debug mode it doesn't make sense to return.
+ if (exmode_active || ccline.cmdfirstc == '>') {
+ return CMDLINE_NOT_CHANGED;
+ }
+
+ XFREE_CLEAR(ccline.cmdbuff); // no commandline to return
+ if (!cmd_silent && !ui_has(kUICmdline)) {
+ if (cmdmsg_rl) {
+ msg_col = Columns;
+ } else {
+ msg_col = 0;
+ }
+ msg_putchar(' '); // delete ':'
+ }
+ s->is_state.search_start = s->is_state.save_cursor;
+ redraw_cmdline = true;
+ return GOTO_NORMAL_MODE;
+ }
+ return CMDLINE_CHANGED;
+}
+
+/// Handle the CTRL-^ key in the command-line mode and toggle the use of the
+/// language :lmap mappings and/or Input Method.
+static void command_line_toggle_langmap(CommandLineState *s)
+{
+ if (map_to_exists_mode("", MODE_LANGMAP, false)) {
+ // ":lmap" mappings exists, toggle use of mappings.
+ State ^= MODE_LANGMAP;
+ if (s->b_im_ptr != NULL) {
+ if (State & MODE_LANGMAP) {
+ *s->b_im_ptr = B_IMODE_LMAP;
+ } else {
+ *s->b_im_ptr = B_IMODE_NONE;
+ }
+ }
+ }
+
+ if (s->b_im_ptr != NULL) {
+ if (s->b_im_ptr == &curbuf->b_p_iminsert) {
+ set_iminsert_global(curbuf);
+ } else {
+ set_imsearch_global(curbuf);
+ }
+ }
+ ui_cursor_shape(); // may show different cursor shape
+ // Show/unshow value of 'keymap' in status lines later.
+ status_redraw_curbuf();
+}
+
+/// Handle the CTRL-R key in the command-line mode and insert the contents of a
+/// numbered or named register.
+static int command_line_insert_reg(CommandLineState *s)
+{
+ const int save_new_cmdpos = new_cmdpos;
+
+ 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
+ }
+
+ if (i == Ctrl_R) {
+ s->c = plain_vgetc(); // CTRL-R CTRL-R <char>
+ }
+ no_mapping--;
+ allow_keys--;
+ // Insert the result of an expression.
+ new_cmdpos = -1;
+ if (s->c == '=') {
+ if (ccline.cmdfirstc == '=' // can't do this recursively
+ || cmdline_star > 0) { // or when typing a password
+ beep_flush();
+ s->c = ESC;
+ } else {
+ s->c = get_expr_register();
+ }
+ }
+
+ if (s->c != ESC) { // use ESC to cancel inserting register
+ cmdline_paste(s->c, i == Ctrl_R, false);
+
+ // When there was a serious error abort getting the
+ // command line.
+ if (aborting()) {
+ s->gotesc = true; // will free ccline.cmdbuff after
+ // putting it in history
+ return GOTO_NORMAL_MODE;
+ }
+ KeyTyped = false; // Don't do p_wc completion.
+ if (new_cmdpos >= 0) {
+ // set_cmdline_pos() was used
+ if (new_cmdpos > ccline.cmdlen) {
+ ccline.cmdpos = ccline.cmdlen;
+ } else {
+ ccline.cmdpos = new_cmdpos;
+ }
+ }
+ }
+ new_cmdpos = save_new_cmdpos;
+
+ // remove the double quote
+ ccline.special_char = NUL;
+ redrawcmd();
+
+ // The text has been stuffed, the command line didn't change yet.
+ return CMDLINE_NOT_CHANGED;
+}
+
+/// Handle the Left and Right mouse clicks in the command-line mode.
+static void command_line_left_right_mouse(CommandLineState *s)
+{
+ if (s->c == K_LEFTRELEASE || s->c == K_RIGHTRELEASE) {
+ s->ignore_drag_release = true;
+ } else {
+ s->ignore_drag_release = false;
+ }
+
+ ccline.cmdspos = cmd_startcol();
+ for (ccline.cmdpos = 0; ccline.cmdpos < ccline.cmdlen;
+ ccline.cmdpos++) {
+ int cells = cmdline_charsize(ccline.cmdpos);
+ if (mouse_row <= cmdline_row + ccline.cmdspos / Columns
+ && mouse_col < ccline.cmdspos % Columns + cells) {
+ break;
+ }
+
+ // 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.cmdspos += cells;
+ }
+}
+
static void command_line_next_histidx(CommandLineState *s, bool next_match)
{
- int j = (int)STRLEN(s->lookfor);
+ int j = (int)strlen(s->lookfor);
for (;;) {
// one step backwards
if (!next_match) {
@@ -1451,96 +1698,118 @@ static void command_line_next_histidx(CommandLineState *s, bool next_match)
if ((s->c != K_UP && s->c != K_DOWN)
|| s->hiscnt == s->save_hiscnt
- || STRNCMP(get_histentry(s->histype)[s->hiscnt].hisstr,
+ || strncmp(get_histentry(s->histype)[s->hiscnt].hisstr,
s->lookfor, (size_t)j) == 0) {
break;
}
}
}
-static int command_line_handle_key(CommandLineState *s)
+/// Handle the Up, Down, Page Up, Page down, CTRL-N and CTRL-P key in the
+/// command-line mode.
+static int command_line_browse_history(CommandLineState *s)
{
- // Big switch for a typed command line character.
- switch (s->c) {
- case K_BS:
- case Ctrl_H:
- case K_DEL:
- case K_KDEL:
- case Ctrl_W:
- if (s->c == K_KDEL) {
- s->c = K_DEL;
- }
+ if (s->histype == HIST_INVALID || get_hislen() == 0 || s->firstc == NUL) {
+ // no history
+ return CMDLINE_NOT_CHANGED;
+ }
- // delete current character is the same as backspace on next
- // character, except at end of line
- if (s->c == K_DEL && ccline.cmdpos != ccline.cmdlen) {
- ++ccline.cmdpos;
- }
+ s->save_hiscnt = s->hiscnt;
- if (s->c == K_DEL) {
- ccline.cmdpos += mb_off_next(ccline.cmdbuff,
- ccline.cmdbuff + ccline.cmdpos);
- }
+ // save current command string so it can be restored later
+ if (s->lookfor == NULL) {
+ s->lookfor = xstrdup(ccline.cmdbuff);
+ s->lookfor[ccline.cmdpos] = NUL;
+ }
- if (ccline.cmdpos > 0) {
- char_u *p;
+ bool next_match = (s->c == K_DOWN || s->c == K_S_DOWN || s->c == Ctrl_N
+ || s->c == K_PAGEDOWN || s->c == K_KPAGEDOWN);
+ command_line_next_histidx(s, next_match);
- int j = ccline.cmdpos;
- p = mb_prevptr(ccline.cmdbuff, ccline.cmdbuff + j);
+ if (s->hiscnt != s->save_hiscnt) {
+ // jumped to other entry
+ char *p;
+ int len = 0;
+ int old_firstc;
- if (s->c == Ctrl_W) {
- while (p > ccline.cmdbuff && ascii_isspace(*p)) {
- p = mb_prevptr(ccline.cmdbuff, p);
- }
+ XFREE_CLEAR(ccline.cmdbuff);
+ s->xpc.xp_context = EXPAND_NOTHING;
+ if (s->hiscnt == get_hislen()) {
+ p = s->lookfor; // back to the old one
+ } else {
+ p = get_histentry(s->histype)[s->hiscnt].hisstr;
+ }
+
+ if (s->histype == HIST_SEARCH
+ && p != s->lookfor
+ && (old_firstc = (uint8_t)p[strlen(p) + 1]) != s->firstc) {
+ // Correct for the separator character used when
+ // adding the history entry vs the one used now.
+ // First loop: count length.
+ // Second loop: copy the characters.
+ for (int i = 0; i <= 1; i++) {
+ len = 0;
+ for (int j = 0; p[j] != NUL; j++) {
+ // Replace old sep with new sep, unless it is
+ // escaped.
+ if (p[j] == old_firstc
+ && (j == 0 || p[j - 1] != '\\')) {
+ if (i > 0) {
+ ccline.cmdbuff[len] = (char)s->firstc;
+ }
+ } else {
+ // Escape new sep, unless it is already
+ // escaped.
+ if (p[j] == s->firstc
+ && (j == 0 || p[j - 1] != '\\')) {
+ if (i > 0) {
+ ccline.cmdbuff[len] = '\\';
+ }
+ len++;
+ }
- int i = mb_get_class(p);
- while (p > ccline.cmdbuff && mb_get_class(p) == i) {
- p = mb_prevptr(ccline.cmdbuff, p);
+ if (i > 0) {
+ ccline.cmdbuff[len] = p[j];
+ }
+ }
+ len++;
}
- if (mb_get_class(p) != i) {
- p += utfc_ptr2len((char *)p);
+ if (i == 0) {
+ alloc_cmdbuff(len);
}
}
+ ccline.cmdbuff[len] = NUL;
+ } else {
+ alloc_cmdbuff((int)strlen(p));
+ STRCPY(ccline.cmdbuff, p);
+ }
- ccline.cmdpos = (int)(p - ccline.cmdbuff);
- ccline.cmdlen -= j - ccline.cmdpos;
- int i = ccline.cmdpos;
-
- while (i < ccline.cmdlen) {
- ccline.cmdbuff[i++] = ccline.cmdbuff[j++];
- }
-
- // Truncate at the end, required for multi-byte chars.
- ccline.cmdbuff[ccline.cmdlen] = NUL;
- if (ccline.cmdlen == 0) {
- s->is_state.search_start = s->is_state.save_cursor;
- // save view settings, so that the screen won't be restored at the
- // wrong position
- s->is_state.old_viewstate = s->is_state.init_viewstate;
- }
- redrawcmd();
- } else if (ccline.cmdlen == 0 && s->c != Ctrl_W
- && ccline.cmdprompt == NULL && s->indent == 0) {
- // In ex and debug mode it doesn't make sense to return.
- if (exmode_active || ccline.cmdfirstc == '>') {
- return command_line_not_changed(s);
- }
+ ccline.cmdpos = ccline.cmdlen = (int)strlen(ccline.cmdbuff);
+ redrawcmd();
+ return CMDLINE_CHANGED;
+ }
+ beep_flush();
+ return CMDLINE_NOT_CHANGED;
+}
- XFREE_CLEAR(ccline.cmdbuff); // no commandline to return
- if (!cmd_silent && !ui_has(kUICmdline)) {
- if (cmdmsg_rl) {
- msg_col = Columns;
- } else {
- msg_col = 0;
- }
- msg_putchar(' '); // delete ':'
- }
- s->is_state.search_start = s->is_state.save_cursor;
- redraw_cmdline = true;
- return 0; // back to cmd mode
+static int command_line_handle_key(CommandLineState *s)
+{
+ // Big switch for a typed command line character.
+ switch (s->c) {
+ case K_BS:
+ case Ctrl_H:
+ case K_DEL:
+ case K_KDEL:
+ case Ctrl_W:
+ switch (command_line_erase_chars(s)) {
+ case CMDLINE_NOT_CHANGED:
+ return command_line_not_changed(s);
+ case GOTO_NORMAL_MODE:
+ return 0; // back to cmd mode
+ default:
+ return command_line_changed(s);
}
- return command_line_changed(s);
case K_INS:
case K_KINS:
@@ -1550,28 +1819,7 @@ static int command_line_handle_key(CommandLineState *s)
return command_line_not_changed(s);
case Ctrl_HAT:
- if (map_to_exists_mode("", MODE_LANGMAP, false)) {
- // ":lmap" mappings exists, toggle use of mappings.
- State ^= MODE_LANGMAP;
- if (s->b_im_ptr != NULL) {
- if (State & MODE_LANGMAP) {
- *s->b_im_ptr = B_IMODE_LMAP;
- } else {
- *s->b_im_ptr = B_IMODE_NONE;
- }
- }
- }
-
- if (s->b_im_ptr != NULL) {
- if (s->b_im_ptr == &curbuf->b_p_iminsert) {
- set_iminsert_global();
- } else {
- set_imsearch_global();
- }
- }
- ui_cursor_shape(); // may show different cursor shape
- // Show/unshow value of 'keymap' in status lines later.
- status_redraw_curbuf();
+ command_line_toggle_langmap(s);
return command_line_not_changed(s);
case Ctrl_U: {
@@ -1607,58 +1855,15 @@ static int command_line_handle_key(CommandLineState *s)
// putting it in history
return 0; // back to cmd mode
- 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
- }
-
- if (i == Ctrl_R) {
- s->c = plain_vgetc(); // CTRL-R CTRL-R <char>
- }
- 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...
- new_cmdpos = -1;
- if (s->c == '=') {
- if (ccline.cmdfirstc == '=' // can't do this recursively
- || cmdline_star > 0) { // or when typing a password
- beep_flush();
- s->c = ESC;
- } else {
- s->c = get_expr_register();
- }
- }
-
- if (s->c != ESC) { // use ESC to cancel inserting register
- cmdline_paste(s->c, i == Ctrl_R, false);
-
- // When there was a serious error abort getting the
- // command line.
- if (aborting()) {
- s->gotesc = true; // will free ccline.cmdbuff after
- // putting it in history
- return 0; // back to cmd mode
- }
- KeyTyped = false; // Don't do p_wc completion.
- if (new_cmdpos >= 0) {
- // set_cmdline_pos() was used
- if (new_cmdpos > ccline.cmdlen) {
- ccline.cmdpos = ccline.cmdlen;
- } else {
- ccline.cmdpos = new_cmdpos;
- }
- }
+ case Ctrl_R: // insert register
+ switch (command_line_insert_reg(s)) {
+ case CMDLINE_NOT_CHANGED:
+ return command_line_not_changed(s);
+ case GOTO_NORMAL_MODE:
+ return 0; // back to cmd mode
+ default:
+ return command_line_changed(s);
}
- ccline.special_char = NUL;
- redrawcmd();
- return command_line_changed(s);
- }
case Ctrl_D:
if (showmatches(&s->xpc, false) == EXPAND_NOTHING) {
@@ -1683,7 +1888,7 @@ static int command_line_handle_key(CommandLineState *s)
}
ccline.cmdspos += cells;
- ccline.cmdpos += utfc_ptr2len((char *)ccline.cmdbuff + ccline.cmdpos);
+ ccline.cmdpos += utfc_ptr2len(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] != ' ');
@@ -1739,26 +1944,7 @@ static int command_line_handle_key(CommandLineState *s)
FALLTHROUGH;
case K_LEFTMOUSE:
case K_RIGHTMOUSE:
- if (s->c == K_LEFTRELEASE || s->c == K_RIGHTRELEASE) {
- s->ignore_drag_release = true;
- } else {
- s->ignore_drag_release = false;
- }
-
- ccline.cmdspos = cmd_startcol();
- for (ccline.cmdpos = 0; ccline.cmdpos < ccline.cmdlen;
- ccline.cmdpos++) {
- int cells = cmdline_charsize(ccline.cmdpos);
- if (mouse_row <= cmdline_row + ccline.cmdspos / Columns
- && mouse_col < ccline.cmdspos % Columns + cells) {
- break;
- }
-
- // Count ">" for double-wide char that doesn't fit.
- correct_screencol(ccline.cmdpos, cells, &ccline.cmdspos);
- ccline.cmdpos += utfc_ptr2len((char *)ccline.cmdbuff + ccline.cmdpos) - 1;
- ccline.cmdspos += cells;
- }
+ command_line_left_right_mouse(s);
return command_line_not_changed(s);
// Mouse scroll wheel: ignored here
@@ -1798,13 +1984,17 @@ static int command_line_handle_key(CommandLineState *s)
return command_line_not_changed(s);
case Ctrl_A: // all matches
- if (nextwild(&s->xpc, WILD_ALL, 0, s->firstc != '@') == FAIL) {
- break;
- }
if (cmdline_pum_active()) {
+ // As Ctrl-A completes all the matches, close the popup
+ // menu (if present)
cmdline_pum_cleanup(&ccline);
- s->xpc.xp_context = EXPAND_NOTHING;
}
+
+ if (nextwild(&s->xpc, WILD_ALL, 0, s->firstc != '@') == FAIL) {
+ break;
+ }
+ s->xpc.xp_context = EXPAND_NOTHING;
+ s->did_wild_list = false;
return command_line_changed(s);
case Ctrl_L:
@@ -1821,8 +2011,8 @@ static int command_line_handle_key(CommandLineState *s)
case Ctrl_N: // next match
case Ctrl_P: // previous match
if (s->xpc.xp_numfiles > 0) {
- if (nextwild(&s->xpc, (s->c == Ctrl_P) ? WILD_PREV : WILD_NEXT,
- 0, s->firstc != '@') == FAIL) {
+ const int wild_type = (s->c == Ctrl_P) ? WILD_PREV : WILD_NEXT;
+ if (nextwild(&s->xpc, wild_type, 0, s->firstc != '@') == FAIL) {
break;
}
return command_line_not_changed(s);
@@ -1837,88 +2027,27 @@ static int command_line_handle_key(CommandLineState *s)
case K_KPAGEUP:
case K_PAGEDOWN:
case K_KPAGEDOWN:
- if (s->histype == HIST_INVALID || get_hislen() == 0 || s->firstc == NUL) {
- // no history
- return command_line_not_changed(s);
- }
-
- s->save_hiscnt = s->hiscnt;
-
- // save current command string so it can be restored later
- if (s->lookfor == NULL) {
- s->lookfor = vim_strsave(ccline.cmdbuff);
- s->lookfor[ccline.cmdpos] = NUL;
- }
-
- bool next_match = (s->c == K_DOWN || s->c == K_S_DOWN || s->c == Ctrl_N
- || s->c == K_PAGEDOWN || s->c == K_KPAGEDOWN);
- command_line_next_histidx(s, next_match);
-
- if (s->hiscnt != s->save_hiscnt) {
- // jumped to other entry
- char_u *p;
- int len = 0;
- int old_firstc;
-
- XFREE_CLEAR(ccline.cmdbuff);
- s->xpc.xp_context = EXPAND_NOTHING;
- if (s->hiscnt == get_hislen()) {
- p = s->lookfor; // back to the old one
- } else {
- p = get_histentry(s->histype)[s->hiscnt].hisstr;
+ if (cmdline_pum_active()
+ && (s->c == K_PAGEUP || s->c == K_PAGEDOWN
+ || s->c == K_KPAGEUP || s->c == K_KPAGEDOWN)) {
+ // If the popup menu is displayed, then PageUp and PageDown
+ // are used to scroll the menu.
+ const int wild_type =
+ (s->c == K_PAGEDOWN || s->c == K_KPAGEDOWN) ? WILD_PAGEDOWN : WILD_PAGEUP;
+ if (nextwild(&s->xpc, wild_type, 0, s->firstc != '@') == FAIL) {
+ break;
}
-
- if (s->histype == HIST_SEARCH
- && p != s->lookfor
- && (old_firstc = p[STRLEN(p) + 1]) != s->firstc) {
- // Correct for the separator character used when
- // adding the history entry vs the one used now.
- // First loop: count length.
- // Second loop: copy the characters.
- for (int i = 0; i <= 1; i++) {
- len = 0;
- for (int j = 0; p[j] != NUL; j++) {
- // Replace old sep with new sep, unless it is
- // escaped.
- if (p[j] == old_firstc
- && (j == 0 || p[j - 1] != '\\')) {
- if (i > 0) {
- ccline.cmdbuff[len] = (char_u)s->firstc;
- }
- } else {
- // Escape new sep, unless it is already
- // escaped.
- if (p[j] == s->firstc
- && (j == 0 || p[j - 1] != '\\')) {
- if (i > 0) {
- ccline.cmdbuff[len] = '\\';
- }
- len++;
- }
-
- if (i > 0) {
- ccline.cmdbuff[len] = p[j];
- }
- }
- len++;
- }
-
- if (i == 0) {
- alloc_cmdbuff(len);
- }
- }
- ccline.cmdbuff[len] = NUL;
- } else {
- alloc_cmdbuff((int)STRLEN(p));
- STRCPY(ccline.cmdbuff, p);
+ return command_line_not_changed(s);
+ } else {
+ switch (command_line_browse_history(s)) {
+ case CMDLINE_CHANGED:
+ return command_line_changed(s);
+ case GOTO_NORMAL_MODE:
+ return 0;
+ default:
+ return command_line_not_changed(s);
}
-
- ccline.cmdpos = ccline.cmdlen = (int)STRLEN(ccline.cmdbuff);
- redrawcmd();
- return command_line_changed(s);
}
- beep_flush();
- return command_line_not_changed(s);
case Ctrl_G: // next match
case Ctrl_T: // previous match
@@ -1994,9 +2123,9 @@ static int command_line_handle_key(CommandLineState *s)
// put the character in the command line
if (IS_SPECIAL(s->c) || mod_mask != 0) {
- put_on_cmdline(get_special_key_name(s->c, mod_mask), -1, true);
+ put_on_cmdline((char *)get_special_key_name(s->c, mod_mask), -1, true);
} else {
- int j = utf_char2bytes(s->c, (char *)IObuff);
+ int j = utf_char2bytes(s->c, IObuff);
IObuff[j] = NUL; // exclude composing chars
put_on_cmdline(IObuff, j, true);
}
@@ -2018,16 +2147,35 @@ static int command_line_not_changed(CommandLineState *s)
/// Guess that the pattern matches everything. Only finds specific cases, such
/// as a trailing \|, which can happen while typing a pattern.
-static int empty_pattern(char_u *p)
+static bool empty_pattern(char *p, int delim)
{
- size_t n = STRLEN(p);
+ size_t n = strlen(p);
+ magic_T magic_val = MAGIC_ON;
- // remove trailing \v and the like
- while (n >= 2 && p[n - 2] == '\\'
- && vim_strchr("mMvVcCZ", p[n - 1]) != NULL) {
- n -= 2;
+ if (n > 0) {
+ (void)skip_regexp_ex(p, delim, magic_isset(), NULL, NULL, &magic_val);
+ } else {
+ return true;
}
- return n == 0 || (n >= 2 && p[n - 2] == '\\' && p[n - 1] == '|');
+
+ return empty_pattern_magic(p, n, magic_val);
+}
+
+static bool empty_pattern_magic(char *p, size_t len, magic_T magic_val)
+{
+ // remove trailing \v and the like
+ while (len >= 2 && p[len - 2] == '\\'
+ && vim_strchr("mMvVcCZ", (uint8_t)p[len - 1]) != NULL) {
+ len -= 2;
+ }
+
+ // true, if the pattern is empty, or the pattern ends with \| and magic is
+ // set (or it ends with '|' and very magic is set)
+ return len == 0 || (len > 1
+ && ((p[len - 2] == '\\'
+ && p[len - 1] == '|' && magic_val == MAGIC_ON)
+ || (p[len - 2] != '\\'
+ && p[len - 1] == '|' && magic_val == MAGIC_ALL)));
}
handle_T cmdpreview_get_bufnr(void)
@@ -2147,6 +2295,7 @@ static void cmdpreview_prepare(CpInfo *cpinfo)
CpBufInfo cp_bufinfo;
cp_bufinfo.buf = buf;
+ cp_bufinfo.save_b_u_synced = buf->b_u_synced;
cp_bufinfo.save_b_u_time_cur = buf->b_u_time_cur;
cp_bufinfo.save_b_u_seq_cur = buf->b_u_seq_cur;
cp_bufinfo.save_b_u_newhead = buf->b_u_newhead;
@@ -2184,6 +2333,8 @@ static void cmdpreview_prepare(CpInfo *cpinfo)
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
+
+ u_sync(true);
}
// Restore the state of buffers and windows before command preview.
@@ -2206,7 +2357,7 @@ static void cmdpreview_restore_state(CpInfo *cpinfo)
aco_save_T aco;
aucmd_prepbuf(&aco, buf);
// Undo invisibly. This also moves the cursor!
- if (!u_undo_and_forget(count)) {
+ if (!u_undo_and_forget(count, false)) {
abort();
}
aucmd_restbuf(&aco);
@@ -2216,6 +2367,11 @@ static void cmdpreview_restore_state(CpInfo *cpinfo)
buf->b_u_newhead = cp_bufinfo.save_b_u_newhead;
buf->b_u_time_cur = cp_bufinfo.save_b_u_time_cur;
}
+
+ if (buf->b_u_curhead == NULL) {
+ buf->b_u_synced = cp_bufinfo.save_b_u_synced;
+ }
+
if (cp_bufinfo.save_changedtick != buf_get_changedtick(buf)) {
buf_set_changedtick(buf, cp_bufinfo.save_changedtick);
}
@@ -2272,7 +2428,7 @@ static bool cmdpreview_may_show(CommandLineState *s)
CmdParseInfo cmdinfo;
// Copy the command line so we can modify it.
int cmdpreview_type = 0;
- char *cmdline = xstrdup((char *)ccline.cmdbuff);
+ char *cmdline = xstrdup(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)) {
@@ -2343,7 +2499,7 @@ static bool cmdpreview_may_show(CommandLineState *s)
if (cmdpreview_type != 0) {
int save_rd = RedrawingDisabled;
RedrawingDisabled = 0;
- update_screen(SOME_VALID);
+ update_screen();
RedrawingDisabled = save_rd;
}
@@ -2364,9 +2520,9 @@ end:
return cmdpreview_type != 0;
}
-static int command_line_changed(CommandLineState *s)
+/// Trigger CmdlineChanged autocommands.
+static void do_autocmd_cmdlinechanged(int firstc)
{
- // Trigger CmdlineChanged autocommands.
if (has_event(EVENT_CMDLINECHANGED)) {
TryState tstate;
Error err = ERROR_INIT;
@@ -2374,7 +2530,7 @@ static int command_line_changed(CommandLineState *s)
dict_T *dict = get_v_event(&save_v_event);
char firstcbuf[2];
- firstcbuf[0] = (char)(s->firstc > 0 ? s->firstc : '-');
+ firstcbuf[0] = (char)firstc;
firstcbuf[1] = 0;
// set v:event to a dictionary with information about the commandline
@@ -2389,12 +2545,20 @@ static int command_line_changed(CommandLineState *s)
bool tl_ret = try_leave(&tstate, &err);
if (!tl_ret && ERROR_SET(&err)) {
msg_putchar('\n');
- msg_printf_attr(HL_ATTR(HLF_E)|MSG_HIST, (char *)e_autocmd_err, err.msg);
+ msg_scroll = true;
+ msg_puts_attr(err.msg, HL_ATTR(HLF_E)|MSG_HIST);
api_clear_error(&err);
redrawcmd();
}
}
+}
+
+static int command_line_changed(CommandLineState *s)
+{
+ // Trigger CmdlineChanged autocommands.
+ do_autocmd_cmdlinechanged(s->firstc > 0 ? s->firstc : '-');
+ const bool prev_cmdpreview = cmdpreview;
if (s->firstc == ':'
&& current_sctx.sc_sid == 0 // only if interactive
&& *p_icm != NUL // 'inccommand' is set
@@ -2403,10 +2567,13 @@ static int command_line_changed(CommandLineState *s)
&& !vpeekc_any()
&& cmdpreview_may_show(s)) {
// 'inccommand' preview has been shown.
- } else if (cmdpreview) {
- cmdpreview = false;
- update_screen(SOME_VALID); // Clear 'inccommand' preview.
} else {
+ cmdpreview = false;
+ if (prev_cmdpreview) {
+ // TODO(bfredl): add an immediate redraw flag for cmdline mode which will trigger
+ // at next wait-for-input
+ update_screen(); // Clear 'inccommand' preview.
+ }
if (s->xpc.xp_context == EXPAND_NOTHING && (KeyTyped || vpeekc() == NUL)) {
may_do_incsearch_highlighting(s->firstc, s->count, &s->is_state);
}
@@ -2458,9 +2625,9 @@ static void abandon_cmdline(void)
///
/// @param count only used for incremental search
/// @param indent indent for inside conditionals
-char_u *getcmdline(int firstc, long count, int indent, bool do_concat FUNC_ATTR_UNUSED)
+char *getcmdline(int firstc, long count, int indent, bool do_concat FUNC_ATTR_UNUSED)
{
- return command_line_enter(firstc, count, indent, true);
+ return (char *)command_line_enter(firstc, count, indent, true);
}
/// Get a command line with a prompt
@@ -2493,10 +2660,10 @@ char *getcmdline_prompt(const int firstc, const char *const prompt, const int at
CLEAR_FIELD(ccline);
}
ccline.prompt_id = last_prompt_id++;
- ccline.cmdprompt = (char_u *)prompt;
+ ccline.cmdprompt = (char *)prompt;
ccline.cmdattr = attr;
ccline.xp_context = xp_context;
- ccline.xp_arg = (char_u *)xp_arg;
+ ccline.xp_arg = (char *)xp_arg;
ccline.input_fn = (firstc == '@');
ccline.highlight_callback = highlight_callback;
@@ -2520,10 +2687,56 @@ char *getcmdline_prompt(const int firstc, const char *const prompt, const int at
return ret;
}
-// Return current cmdline prompt
-char_u *get_cmdprompt(void)
+/// Read the 'wildmode' option, fill wim_flags[].
+int check_opt_wim(void)
{
- return ccline.cmdprompt;
+ char_u new_wim_flags[4];
+ int i;
+ int idx = 0;
+
+ for (i = 0; i < 4; i++) {
+ new_wim_flags[i] = 0;
+ }
+
+ for (char *p = p_wim; *p; p++) {
+ for (i = 0; ASCII_ISALPHA(p[i]); i++) {}
+ if (p[i] != NUL && p[i] != ',' && p[i] != ':') {
+ return FAIL;
+ }
+ if (i == 7 && strncmp(p, "longest", 7) == 0) {
+ new_wim_flags[idx] |= WIM_LONGEST;
+ } else if (i == 4 && strncmp(p, "full", 4) == 0) {
+ new_wim_flags[idx] |= WIM_FULL;
+ } else if (i == 4 && strncmp(p, "list", 4) == 0) {
+ new_wim_flags[idx] |= WIM_LIST;
+ } else if (i == 8 && strncmp(p, "lastused", 8) == 0) {
+ new_wim_flags[idx] |= WIM_BUFLASTUSED;
+ } else {
+ return FAIL;
+ }
+ p += i;
+ if (*p == NUL) {
+ break;
+ }
+ if (*p == ',') {
+ if (idx == 3) {
+ return FAIL;
+ }
+ idx++;
+ }
+ }
+
+ // fill remaining entries with last flag
+ while (idx < 3) {
+ new_wim_flags[idx + 1] = new_wim_flags[idx];
+ idx++;
+ }
+
+ // only when there are no errors, wim_flags[] is changed
+ for (i = 0; i < 4; i++) {
+ wim_flags[i] = new_wim_flags[i];
+ }
+ return OK;
}
/// Return true when the text must not be changed and we can't switch to
@@ -2536,10 +2749,8 @@ bool text_locked(void)
return textlock != 0;
}
-/*
- * Give an error message for a command that isn't allowed while the cmdline
- * window is open or editing the cmdline in another way.
- */
+// Give an error message for a command that isn't allowed while the cmdline
+// window is open or editing the cmdline in another way.
void text_locked_msg(void)
{
emsg(_(get_text_locked_msg()));
@@ -2592,7 +2803,7 @@ static int cmdline_charsize(int idx)
if (cmdline_star > 0) { // showing '*', always 1 position
return 1;
}
- return ptr2cells((char *)ccline.cmdbuff + idx);
+ return ptr2cells(ccline.cmdbuff + idx);
}
/// Compute the offset of the cursor on the command line for the prompt and
@@ -2618,7 +2829,7 @@ int cmd_screencol(int bytepos)
}
for (int i = 0; i < ccline.cmdlen && i < bytepos;
- i += utfc_ptr2len((char *)ccline.cmdbuff + i)) {
+ i += utfc_ptr2len(ccline.cmdbuff + i)) {
int c = cmdline_charsize(i);
// Count ">" for double-wide multi-byte char that doesn't fit.
correct_screencol(i, c, &col);
@@ -2637,8 +2848,8 @@ 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((char *)ccline.cmdbuff + idx) > 1
- && utf_ptr2cells((char *)ccline.cmdbuff + idx) > 1
+ if (utfc_ptr2len(ccline.cmdbuff + idx) > 1
+ && utf_ptr2cells(ccline.cmdbuff + idx) > 1
&& (*col) % Columns + cells > Columns) {
(*col)++;
}
@@ -2655,7 +2866,7 @@ char *getexline(int c, void *cookie, int indent, bool do_concat)
(void)vgetc();
}
- return (char *)getcmdline(c, 1L, indent, do_concat);
+ return getcmdline(c, 1L, indent, do_concat);
}
bool cmdline_overstrike(void)
@@ -2671,15 +2882,11 @@ bool cmdline_at_end(void)
return (ccline.cmdpos >= ccline.cmdlen);
}
-/*
- * Allocate a new command line buffer.
- * Assigns the new buffer to ccline.cmdbuff and ccline.cmdbufflen.
- */
+// Allocate a new command line buffer.
+// Assigns the new buffer to ccline.cmdbuff and ccline.cmdbufflen.
static void alloc_cmdbuff(int len)
{
- /*
- * give some extra space to avoid having to allocate all the time
- */
+ // give some extra space to avoid having to allocate all the time
if (len < 80) {
len = 100;
} else {
@@ -2697,7 +2904,7 @@ void realloc_cmdbuff(int len)
return; // no need to resize
}
- char_u *p = ccline.cmdbuff;
+ char *p = ccline.cmdbuff;
alloc_cmdbuff(len); // will get some more
// There isn't always a NUL after the command, but it may need to be
// there, thus copy up to the NUL and add a NUL.
@@ -2709,12 +2916,12 @@ 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)((char_u *)ccline.xpc->xp_pattern - p);
+ int i = (int)(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 = (char *)ccline.cmdbuff + i;
+ ccline.xpc->xp_pattern = ccline.cmdbuff + i;
}
}
}
@@ -2744,7 +2951,7 @@ static void color_expr_cmdline(const CmdlineInfo *const colored_ccline,
ParserLine parser_lines[] = {
{
.data = (const char *)colored_ccline->cmdbuff,
- .size = STRLEN(colored_ccline->cmdbuff),
+ .size = strlen(colored_ccline->cmdbuff),
.allocated = false,
},
{ NULL, 0, false },
@@ -2822,7 +3029,7 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
// Check whether result of the previous call is still valid.
if (ccline_colors->prompt_id == colored_ccline->prompt_id
&& ccline_colors->cmdbuff != NULL
- && STRCMP(ccline_colors->cmdbuff, colored_ccline->cmdbuff) == 0) {
+ && strcmp(ccline_colors->cmdbuff, colored_ccline->cmdbuff) == 0) {
return ret;
}
@@ -2837,7 +3044,7 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
bool arg_allocated = false;
typval_T arg = {
.v_type = VAR_STRING,
- .vval.v_string = (char *)colored_ccline->cmdbuff,
+ .vval.v_string = colored_ccline->cmdbuff,
};
typval_T tv = { .v_type = VAR_UNKNOWN };
@@ -3021,10 +3228,8 @@ color_cmdline_error:
#undef PRINT_ERRMSG
}
-/*
- * Draw part of the cmdline at the current cursor position. But draw stars
- * when cmdline_star is TRUE.
- */
+// Draw part of the cmdline at the current cursor position. But draw stars
+// when cmdline_star is true.
static void draw_cmdline(int start, int len)
{
if (!color_cmdline(&ccline)) {
@@ -3040,13 +3245,13 @@ static void draw_cmdline(int start, int len)
if (cmdline_star > 0) {
for (int i = 0; i < len; i++) {
msg_putchar('*');
- i += utfc_ptr2len((char *)ccline.cmdbuff + start + i) - 1;
+ i += utfc_ptr2len(ccline.cmdbuff + start + i) - 1;
}
} else if (p_arshape && !p_tbidi && len > 0) {
bool do_arabicshape = false;
int mb_l;
for (int i = start; i < start + len; i += mb_l) {
- char_u *p = ccline.cmdbuff + i;
+ char *p = ccline.cmdbuff + i;
int u8cc[MAX_MCO];
int u8c = utfc_ptr2char_len(p, u8cc, start + len - i);
mb_l = utfc_ptr2len_len(p, start + len - i);
@@ -3073,7 +3278,7 @@ static void draw_cmdline(int start, int len)
}
int newlen = 0;
- if (utf_iscomposing(utf_ptr2char((char *)ccline.cmdbuff + start))) {
+ if (utf_iscomposing(utf_ptr2char(ccline.cmdbuff + start))) {
// Prepend a space to draw the leading composing char on.
arshape_buf[0] = ' ';
newlen = 1;
@@ -3082,7 +3287,7 @@ static void draw_cmdline(int start, int len)
int prev_c = 0;
int prev_c1 = 0;
for (int i = start; i < start + len; i += mb_l) {
- char_u *p = ccline.cmdbuff + i;
+ char *p = ccline.cmdbuff + i;
int u8cc[MAX_MCO];
int u8c = utfc_ptr2char_len(p, u8cc, start + len - i);
mb_l = utfc_ptr2len_len(p, start + len - i);
@@ -3099,7 +3304,7 @@ static void draw_cmdline(int start, int len)
if (i + mb_l >= start + len) {
nc = NUL;
} else {
- nc = utf_ptr2char((char *)p + mb_l);
+ nc = utf_ptr2char(p + mb_l);
}
} else {
// Displaying from left to right.
@@ -3131,7 +3336,7 @@ static void draw_cmdline(int start, int len)
}
}
- msg_outtrans_len((char_u *)arshape_buf, newlen);
+ msg_outtrans_len(arshape_buf, newlen);
} else {
draw_cmdline_no_arabicshape:
if (kv_size(ccline.last_colors.colors)) {
@@ -3154,12 +3359,11 @@ draw_cmdline_no_arabicshape:
static void ui_ext_cmdline_show(CmdlineInfo *line)
{
Arena arena = ARENA_EMPTY;
- arena_start(&arena, &ui_ext_fixblk);
Array content;
if (cmdline_star) {
content = arena_array(&arena, 1);
size_t len = 0;
- for (char_u *p = ccline.cmdbuff; *p; MB_PTR_ADV(p)) {
+ for (char *p = ccline.cmdbuff; *p; MB_PTR_ADV(p)) {
len++;
}
char *buf = arena_alloc(&arena, len, false);
@@ -3190,7 +3394,7 @@ static void ui_ext_cmdline_show(CmdlineInfo *line)
char charbuf[2] = { (char)line->cmdfirstc, 0 };
ui_call_cmdline_show(content, line->cmdpos,
cstr_as_string(charbuf),
- cstr_as_string((char *)(line->cmdprompt)),
+ cstr_as_string((line->cmdprompt)),
line->cmdindent,
line->level);
if (line->special_char) {
@@ -3199,7 +3403,7 @@ static void ui_ext_cmdline_show(CmdlineInfo *line)
line->special_shift,
line->level);
}
- arena_mem_free(arena_finish(&arena), &ui_ext_fixblk);
+ arena_mem_free(arena_finish(&arena));
}
void ui_ext_cmdline_block_append(size_t indent, const char *line)
@@ -3276,11 +3480,9 @@ void cmdline_ui_flush(void)
}
}
-/*
- * Put a character on the command line. Shifts the following text to the
- * right when "shift" is TRUE. Used for CTRL-V, CTRL-K, etc.
- * "c" must be printable (fit in one display cell)!
- */
+// Put a character on the command line. Shifts the following text to the
+// right when "shift" is true. Used for CTRL-V, CTRL-K, etc.
+// "c" must be printable (fit in one display cell)!
void putcmdline(char c, int shift)
{
if (cmd_silent) {
@@ -3304,7 +3506,7 @@ void putcmdline(char c, int shift)
ui_cursor_shape();
}
-/// Undo a putcmdline(c, FALSE).
+/// Undo a putcmdline(c, false).
void unputcmdline(void)
{
if (cmd_silent) {
@@ -3314,7 +3516,7 @@ void unputcmdline(void)
if (ccline.cmdlen == ccline.cmdpos && !ui_has(kUICmdline)) {
msg_putchar(' ');
} else {
- draw_cmdline(ccline.cmdpos, utfc_ptr2len((char *)ccline.cmdbuff + ccline.cmdpos));
+ draw_cmdline(ccline.cmdpos, utfc_ptr2len(ccline.cmdbuff + ccline.cmdpos));
}
msg_no_more = false;
cursorcmd();
@@ -3322,22 +3524,20 @@ void unputcmdline(void)
ui_cursor_shape();
}
-/*
- * Put the given string, of the given length, onto the command line.
- * If len is -1, then STRLEN() is used to calculate the length.
- * If 'redraw' is TRUE then the new part of the command line, and the remaining
- * part will be redrawn, otherwise it will not. If this function is called
- * twice in a row, then 'redraw' should be FALSE and redrawcmd() should be
- * called afterwards.
- */
-void put_on_cmdline(char_u *str, int len, int redraw)
+// Put the given string, of the given length, onto the command line.
+// If len is -1, then strlen() is used to calculate the length.
+// If 'redraw' is true then the new part of the command line, and the remaining
+// part will be redrawn, otherwise it will not. If this function is called
+// twice in a row, then 'redraw' should be false and redrawcmd() should be
+// called afterwards.
+void put_on_cmdline(char *str, int len, int redraw)
{
int i;
int m;
int c;
if (len < 0) {
- len = (int)STRLEN(str);
+ len = (int)strlen(str);
}
realloc_cmdbuff(ccline.cmdlen + len + 1);
@@ -3350,13 +3550,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((char *)str + i)) {
+ for (i = 0; i < len; i += utfc_ptr2len(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((char *)ccline.cmdbuff + i)) {
+ i += utfc_ptr2len(ccline.cmdbuff + i)) {
m--;
}
if (i < ccline.cmdlen) {
@@ -3374,17 +3574,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((char *)ccline.cmdbuff + ccline.cmdpos);
+ c = utf_ptr2char(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((char *)ccline.cmdbuff + ccline.cmdpos);
+ c = utf_ptr2char(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((char *)ccline.cmdbuff + ccline.cmdpos - i), c)) {
+ if (arabic_combine(utf_ptr2char(ccline.cmdbuff + ccline.cmdpos - i), c)) {
ccline.cmdpos -= i;
len += i;
} else {
@@ -3393,7 +3593,7 @@ void put_on_cmdline(char_u *str, int len, int redraw)
}
if (i != 0) {
// Also backup the cursor position.
- i = ptr2cells((char *)ccline.cmdbuff + ccline.cmdpos);
+ i = ptr2cells(ccline.cmdbuff + ccline.cmdpos);
ccline.cmdspos -= i;
msg_col -= i;
if (msg_col < 0) {
@@ -3404,7 +3604,7 @@ void put_on_cmdline(char_u *str, int len, int redraw)
}
if (redraw && !cmd_silent) {
- msg_no_more = TRUE;
+ msg_no_more = true;
i = cmdline_row;
cursorcmd();
draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos);
@@ -3412,7 +3612,7 @@ void put_on_cmdline(char_u *str, int len, int redraw)
if (cmdline_row != i || ccline.overstrike) {
msg_clr_eos();
}
- msg_no_more = FALSE;
+ msg_no_more = false;
}
if (KeyTyped) {
m = Columns * Rows;
@@ -3432,7 +3632,7 @@ void put_on_cmdline(char_u *str, int len, int redraw)
if (ccline.cmdspos + c < m) {
ccline.cmdspos += c;
}
- c = utfc_ptr2len((char *)ccline.cmdbuff + ccline.cmdpos) - 1;
+ c = utfc_ptr2len(ccline.cmdbuff + ccline.cmdpos) - 1;
if (c > len - i - 1) {
c = len - i - 1;
}
@@ -3476,7 +3676,7 @@ static void restore_cmdline(CmdlineInfo *ccp)
static bool cmdline_paste(int regname, bool literally, bool remcr)
{
char *arg;
- char_u *p;
+ char *p;
bool allocated;
// check for valid regname; also accept special characters for CTRL-R in
@@ -3508,21 +3708,21 @@ static bool cmdline_paste(int regname, bool literally, bool remcr)
// When 'incsearch' is set and CTRL-R CTRL-W used: skip the duplicate
// part of the word.
- p = (char_u *)arg;
+ p = arg;
if (p_is && regname == Ctrl_W) {
- char_u *w;
+ char *w;
int len;
// 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((char *)w - len))) {
+ if (!vim_iswordc(utf_ptr2char(w - len))) {
break;
}
w -= len;
}
len = (int)((ccline.cmdbuff + ccline.cmdpos) - w);
- if (p_ic ? STRNICMP(w, arg, len) == 0 : STRNCMP(w, arg, len) == 0) {
+ if (p_ic ? STRNICMP(w, arg, len) == 0 : strncmp(w, arg, (size_t)len) == 0) {
p += len;
}
}
@@ -3537,25 +3737,23 @@ static bool cmdline_paste(int regname, bool literally, bool remcr)
return cmdline_paste_reg(regname, literally, remcr);
}
-/*
- * Put a string on the command line.
- * When "literally" is TRUE, insert literally.
- * When "literally" is FALSE, insert as typed, but don't leave the command
- * line.
- */
-void cmdline_paste_str(char_u *s, int literally)
+// Put a string on the command line.
+// When "literally" is true, insert literally.
+// When "literally" is false, insert as typed, but don't leave the command
+// line.
+void cmdline_paste_str(char *s, int literally)
{
int c, cv;
if (literally) {
- put_on_cmdline(s, -1, TRUE);
+ put_on_cmdline(s, -1, true);
} else {
while (*s != NUL) {
- cv = *s;
+ cv = (uint8_t)(*s);
if (cv == Ctrl_V && s[1]) {
s++;
}
- c = mb_cptr2char_adv((const char_u **)&s);
+ c = mb_cptr2char_adv((const char **)&s);
if (cv == Ctrl_V || c == ESC || c == Ctrl_C
|| c == CAR || c == NL || c == Ctrl_L
|| (c == Ctrl_BSL && *s == Ctrl_N)) {
@@ -3596,7 +3794,7 @@ static void redrawcmdprompt(void)
msg_putchar(ccline.cmdfirstc);
}
if (ccline.cmdprompt != NULL) {
- msg_puts_attr((const char *)ccline.cmdprompt, ccline.cmdattr);
+ msg_puts_attr(ccline.cmdprompt, ccline.cmdattr);
ccline.cmdindent = msg_col + (msg_row - cmdline_row) * Columns;
// do the reverse of cmd_startcol()
if (ccline.cmdfirstc != NUL) {
@@ -3609,9 +3807,7 @@ static void redrawcmdprompt(void)
}
}
-/*
- * Redraw what is currently on the command line.
- */
+// Redraw what is currently on the command line.
void redrawcmd(void)
{
if (cmd_silent) {
@@ -3637,7 +3833,7 @@ void redrawcmd(void)
redrawcmdprompt();
// Don't use more prompt, truncate the cmdline if it doesn't fit.
- msg_no_more = TRUE;
+ msg_no_more = true;
draw_cmdline(0, ccline.cmdlen);
msg_clr_eos();
msg_no_more = false;
@@ -3648,11 +3844,9 @@ void redrawcmd(void)
putcmdline(ccline.special_char, ccline.special_shift);
}
- /*
- * An emsg() before may have set msg_scroll. This is used in normal mode,
- * in cmdline mode we can reset them now.
- */
- msg_scroll = FALSE; // next message overwrites cmdline
+ // An emsg() before may have set msg_scroll. This is used in normal mode,
+ // in cmdline mode we can reset them now.
+ msg_scroll = false; // next message overwrites cmdline
// Typing ':' at the more prompt may set skip_redraw. We don't want this
// in cmdline mode.
@@ -3670,6 +3864,9 @@ void compute_cmdrow(void)
cmdline_row = wp->w_winrow + wp->w_height
+ wp->w_hsep_height + wp->w_status_height + global_stl_height();
}
+ if (cmdline_row == Rows && p_ch > 0) {
+ cmdline_row--;
+ }
lines_left = cmdline_row;
}
@@ -3728,12 +3925,10 @@ void gotocmdline(bool clr)
cmd_cursor_goto(cmdline_row, 0);
}
-/*
- * Check the word in front of the cursor for an abbreviation.
- * Called when the non-id character "c" has been entered.
- * When an abbreviation is recognized it is removed from the text with
- * backspaces and the replacement string is inserted, followed by "c".
- */
+// Check the word in front of the cursor for an abbreviation.
+// Called when the non-id character "c" has been entered.
+// When an abbreviation is recognized it is removed from the text with
+// backspaces and the replacement string is inserted, followed by "c".
static int ccheck_abbr(int c)
{
int spos = 0;
@@ -3774,34 +3969,31 @@ char *vim_strsave_fnameescape(const char *const fname, const int what)
{
#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)];
+# define BUFFER_ESC_CHARS (" \t\n*?[`%#'\"|!<")
+ char buf[sizeof(PATH_ESC_CHARS)];
int j = 0;
// 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)) {
+ if ((*p != '[' && *p != '{' && *p != '!') || !vim_isfilec((uint8_t)(*p))) {
buf[j++] = *p;
}
}
buf[j] = NUL;
- char *p = (char *)vim_strsave_escaped((const char_u *)fname,
- (const char_u *)buf);
+ char *p = vim_strsave_escaped(fname, buf);
#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,
- what == VSE_SHELL ? SHELL_ESC_CHARS
- : what == VSE_BUFFER ? BUFFER_ESC_CHARS : PATH_ESC_CHARS);
+# define PATH_ESC_CHARS " \t\n*?[{`$\\%#'\"|!<"
+# define SHELL_ESC_CHARS " \t\n*?[{`$\\%#'\"|!<>();&"
+# define BUFFER_ESC_CHARS " \t\n*?[`$\\%#'\"|!<"
+ char *p = vim_strsave_escaped(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,
- (const char_u *)"!");
+ char *s = vim_strsave_escaped(p, "!");
xfree(p);
p = s;
}
@@ -3819,16 +4011,16 @@ char *vim_strsave_fnameescape(const char *const fname, const int what)
/// Put a backslash before the file name in "pp", which is in allocated memory.
void escape_fname(char **pp)
{
- char_u *p = xmalloc(STRLEN(*pp) + 2);
+ char *p = xmalloc(strlen(*pp) + 2);
p[0] = '\\';
STRCPY(p + 1, *pp);
xfree(*pp);
- *pp = (char *)p;
+ *pp = p;
}
/// For each file name in files[num_files]:
/// If 'orig_pat' starts with "~/", replace the home directory with "~".
-void tilde_replace(char_u *orig_pat, int num_files, char **files)
+void tilde_replace(char *orig_pat, int num_files, char **files)
{
char *p;
@@ -3867,76 +4059,127 @@ static CmdlineInfo *get_ccline_ptr(void)
}
}
-/// Get the current command-line completion type.
-char_u *get_cmdline_completion(void)
+/// Get the current command-line type.
+/// Returns ':' or '/' or '?' or '@' or '>' or '-'
+/// Only works when the command line is being edited.
+/// Returns NUL when something is wrong.
+static int get_cmdline_type(void)
+{
+ CmdlineInfo *p = get_ccline_ptr();
+
+ if (p == NULL) {
+ return NUL;
+ }
+ if (p->cmdfirstc == NUL) {
+ return (p->input_fn) ? '@' : '-';
+ }
+ return p->cmdfirstc;
+}
+
+/// Get the current command line in allocated memory.
+/// Only works when the command line is being edited.
+///
+/// @return NULL when something is wrong.
+static char *get_cmdline_str(void)
{
if (cmdline_star > 0) {
return NULL;
}
CmdlineInfo *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);
- }
+ if (p == NULL) {
+ return NULL;
}
-
- return NULL;
+ return xstrnsave(p->cmdbuff, (size_t)p->cmdlen);
}
-/*
- * Get the current command line in allocated memory.
- * Only works when the command line is being edited.
- * Returns NULL when something is wrong.
- */
-char_u *get_cmdline_str(void)
+/// Get the current command-line completion type.
+static char *get_cmdline_completion(void)
{
if (cmdline_star > 0) {
return NULL;
}
CmdlineInfo *p = get_ccline_ptr();
- if (p == NULL) {
+ if (p == NULL || p->xpc == NULL) {
return NULL;
}
- return vim_strnsave(p->cmdbuff, (size_t)p->cmdlen);
+
+ set_expand_context(p->xpc);
+ char *cmd_compl = get_user_cmd_complete(p->xpc, p->xpc->xp_context);
+ if (cmd_compl != NULL) {
+ return xstrdup(cmd_compl);
+ }
+
+ return NULL;
+}
+
+/// "getcmdcompltype()" function
+void f_getcmdcompltype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = get_cmdline_completion();
+}
+
+/// "getcmdline()" function
+void f_getcmdline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = get_cmdline_str();
}
-/*
- * Get the current command line position, counted in bytes.
- * Zero is the first position.
- * Only works when the command line is being edited.
- * Returns -1 when something is wrong.
- */
-int get_cmdline_pos(void)
+/// "getcmdpos()" function
+void f_getcmdpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
CmdlineInfo *p = get_ccline_ptr();
+ rettv->vval.v_number = p != NULL ? p->cmdpos + 1 : 0;
+}
- if (p == NULL) {
- return -1;
- }
- return p->cmdpos;
+/// "getcmdscreenpos()" function
+void f_getcmdscreenpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ CmdlineInfo *p = get_ccline_ptr();
+ rettv->vval.v_number = p != NULL ? p->cmdspos + 1 : 0;
}
-/// Get the command line cursor screen position.
-int get_cmdline_screen_pos(void)
+/// "getcmdtype()" function
+void f_getcmdtype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = xmallocz(1);
+ rettv->vval.v_string[0] = (char)get_cmdline_type();
+}
+
+/// Set the command line str to "str".
+/// @return 1 when failed, 0 when OK.
+static int set_cmdline_str(const char *str, int pos)
{
CmdlineInfo *p = get_ccline_ptr();
if (p == NULL) {
- return -1;
+ return 1;
}
- return p->cmdspos;
+
+ int len = (int)strlen(str);
+ realloc_cmdbuff(len + 1);
+ p->cmdlen = len;
+ STRCPY(p->cmdbuff, str);
+
+ p->cmdpos = pos < 0 || pos > p->cmdlen ? p->cmdlen : pos;
+ new_cmdpos = p->cmdpos;
+
+ redrawcmd();
+
+ // Trigger CmdlineChanged autocommands.
+ do_autocmd_cmdlinechanged(get_cmdline_type());
+
+ return 0;
}
-/*
- * Set the command line byte position to "pos". Zero is the first position.
- * Only works when the command line is being edited.
- * Returns 1 when failed, 0 when OK.
- */
-int set_cmdline_pos(int pos)
+/// Set the command line byte position to "pos". Zero is the first position.
+/// Only works when the command line is being edited.
+/// @return 1 when failed, 0 when OK.
+static int set_cmdline_pos(int pos)
{
CmdlineInfo *p = get_ccline_ptr();
@@ -3954,23 +4197,39 @@ int set_cmdline_pos(int pos)
return 0;
}
-/*
- * Get the current command-line type.
- * Returns ':' or '/' or '?' or '@' or '>' or '-'
- * Only works when the command line is being edited.
- * Returns NUL when something is wrong.
- */
-int get_cmdline_type(void)
+/// "setcmdline()" function
+void f_setcmdline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- CmdlineInfo *p = get_ccline_ptr();
+ if (tv_check_for_string_arg(argvars, 0) == FAIL
+ || tv_check_for_opt_number_arg(argvars, 1) == FAIL) {
+ return;
+ }
- if (p == NULL) {
- return NUL;
+ int pos = -1;
+ if (argvars[1].v_type != VAR_UNKNOWN) {
+ bool error = false;
+
+ pos = (int)tv_get_number_chk(&argvars[1], &error) - 1;
+ if (error) {
+ return;
+ }
+ if (pos < 0) {
+ emsg(_(e_positive));
+ return;
+ }
}
- if (p->cmdfirstc == NUL) {
- return (p->input_fn) ? '@' : '-';
+
+ rettv->vval.v_number = set_cmdline_str(argvars[0].vval.v_string, pos);
+}
+
+/// "setcmdpos()" function
+void f_setcmdpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ const int pos = (int)tv_get_number(&argvars[0]) - 1;
+
+ if (pos >= 0) {
+ rettv->vval.v_number = set_cmdline_pos(pos);
}
- return p->cmdfirstc;
}
/// Return the first character of the current command line.
@@ -3995,7 +4254,7 @@ int get_list_range(char **str, int *num1, int *num2)
*str = skipwhite((*str));
if (**str == '-' || ascii_isdigit(**str)) { // parse "from" part of range
- vim_str2nr((char_u *)(*str), NULL, &len, 0, &num, NULL, 0, false);
+ vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0, false);
*str += len;
*num1 = (int)num;
first = true;
@@ -4003,7 +4262,7 @@ int get_list_range(char **str, int *num1, int *num2)
*str = skipwhite((*str));
if (**str == ',') { // parse "to" part of range
*str = skipwhite((*str) + 1);
- vim_str2nr((char_u *)(*str), NULL, &len, 0, &num, NULL, 0, false);
+ vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0, false);
if (len > 0) {
*num2 = (int)num;
*str = skipwhite((*str) + len);
@@ -4021,6 +4280,24 @@ void cmdline_init(void)
CLEAR_FIELD(ccline);
}
+/// Check value of 'cedit' and set cedit_key.
+/// Returns NULL if value is OK, error message otherwise.
+char *check_cedit(void)
+{
+ int n;
+
+ if (*p_cedit == NUL) {
+ cedit_key = -1;
+ } else {
+ n = string_to_key(p_cedit);
+ if (vim_isprintc(n)) {
+ return e_invarg;
+ }
+ cedit_key = n;
+ }
+ return NULL;
+}
+
/// Open a window on the current command line and history. Allow editing in
/// the window. Returns when the window is closed.
/// Returns:
@@ -4068,17 +4345,27 @@ static int open_cmdwin(void)
ga_clear(&winsizes);
return K_IGNORE;
}
+ // Don't let quitting the More prompt make this fail.
+ got_int = false;
+
+ // Set "cmdwin_type" before any autocommands may mess things up.
cmdwin_type = get_cmdline_type();
cmdwin_level = ccline.level;
// Create empty command-line buffer.
- buf_open_scratch(0, _("[Command Line]"));
+ if (buf_open_scratch(0, _("[Command Line]")) == FAIL) {
+ // Some autocommand messed it up?
+ win_close(curwin, true, false);
+ ga_clear(&winsizes);
+ cmdwin_type = 0;
+ return Ctrl_C;
+ }
// Command-line buffer has bufhidden=wipe, unlike a true "scratch" buffer.
- set_option_value("bh", 0L, "wipe", OPT_LOCAL);
- curwin->w_p_rl = cmdmsg_rl;
- cmdmsg_rl = false;
+ set_option_value_give_err("bh", 0L, "wipe", OPT_LOCAL);
curbuf->b_p_ma = true;
curwin->w_p_fen = false;
+ curwin->w_p_rl = cmdmsg_rl;
+ cmdmsg_rl = false;
// Don't allow switching to another buffer.
curbuf->b_ro_locked++;
@@ -4092,7 +4379,7 @@ static int open_cmdwin(void)
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);
+ set_option_value_give_err("ft", 0L, "vim", OPT_LOCAL);
}
curbuf->b_ro_locked--;
@@ -4111,7 +4398,7 @@ static int open_cmdwin(void)
i = 0;
}
if (get_histentry(histtype)[i].hisstr != NULL) {
- ml_append(lnum++, (char *)get_histentry(histtype)[i].hisstr, (colnr_T)0, false);
+ ml_append(lnum++, get_histentry(histtype)[i].hisstr, (colnr_T)0, false);
}
} while (i != *get_hisidx(histtype));
}
@@ -4119,7 +4406,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, (char *)ccline.cmdbuff, true);
+ ml_replace(curbuf->b_ml.ml_line_count, ccline.cmdbuff, true);
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
curwin->w_cursor.col = ccline.cmdpos;
changed_line_abv_curs();
@@ -4128,7 +4415,7 @@ static int open_cmdwin(void)
ccline.redraw_state = kCmdRedrawNone;
ui_call_cmdline_hide(ccline.level);
}
- redraw_later(curwin, SOME_VALID);
+ redraw_later(curwin, UPD_SOME_VALID);
// No Ex mode here!
exmode_active = false;
@@ -4151,9 +4438,7 @@ static int open_cmdwin(void)
RedrawingDisabled = 0;
int save_count = save_batch_count();
- /*
- * Call the main loop until <CR> or CTRL-C is typed.
- */
+ // Call the main loop until <CR> or CTRL-C is typed.
normal_enter(true, false);
RedrawingDisabled = i;
@@ -4189,7 +4474,7 @@ static int open_cmdwin(void)
if (histtype == HIST_CMD) {
// Execute the command directly.
- ccline.cmdbuff = (char_u *)xstrdup(p);
+ ccline.cmdbuff = xstrdup(p);
cmdwin_result = CAR;
} else {
// First need to cancel what we were doing.
@@ -4203,16 +4488,16 @@ static int open_cmdwin(void)
// and don't modify the cmd window.
ccline.cmdbuff = NULL;
} else {
- ccline.cmdbuff = vim_strsave(get_cursor_line_ptr());
+ ccline.cmdbuff = xstrdup(get_cursor_line_ptr());
}
if (ccline.cmdbuff == NULL) {
- ccline.cmdbuff = vim_strsave((char_u *)"");
+ ccline.cmdbuff = xstrdup("");
ccline.cmdlen = 0;
ccline.cmdbufflen = 1;
ccline.cmdpos = 0;
cmdwin_result = Ctrl_C;
} else {
- ccline.cmdlen = (int)STRLEN(ccline.cmdbuff);
+ ccline.cmdlen = (int)strlen(ccline.cmdbuff);
ccline.cmdbufflen = ccline.cmdlen + 1;
ccline.cmdpos = curwin->w_cursor.col;
if (ccline.cmdpos > ccline.cmdlen) {
@@ -4229,6 +4514,7 @@ static int open_cmdwin(void)
// First go back to the original window.
wp = curwin;
set_bufref(&bufref, curbuf);
+ skip_win_fix_cursor = true;
win_goto(old_curwin);
// win_goto() may trigger an autocommand that already closes the
@@ -4245,6 +4531,7 @@ static int open_cmdwin(void)
// Restore window sizes.
win_size_restore(&winsizes);
+ skip_win_fix_cursor = false;
}
ga_clear(&winsizes);
@@ -4285,7 +4572,7 @@ char *script_get(exarg_T *const eap, size_t *const lenp)
const char *const cmd = (const char *)eap->arg;
if (cmd[0] != '<' || cmd[1] != '<' || eap->getline == NULL) {
- *lenp = STRLEN(eap->arg);
+ *lenp = strlen(eap->arg);
return eap->skip ? NULL : xmemdupz(eap->arg, *lenp);
}
diff --git a/src/nvim/ex_getln.h b/src/nvim/ex_getln.h
index 5e3ad20a31..61ac4b69c5 100644
--- a/src/nvim/ex_getln.h
+++ b/src/nvim/ex_getln.h
@@ -1,10 +1,16 @@
#ifndef NVIM_EX_GETLN_H
#define NVIM_EX_GETLN_H
+#include <stdbool.h>
+
+#include "klib/kvec.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/types.h"
+struct cmdline_info;
+
/// Command-line colors: one chunk
///
/// Defines a region which has the same highlighting.
@@ -41,21 +47,21 @@ typedef enum {
/// structure.
typedef struct cmdline_info CmdlineInfo;
struct cmdline_info {
- char_u *cmdbuff; ///< pointer to command line buffer
+ char *cmdbuff; ///< pointer to command line buffer
int cmdbufflen; ///< length of cmdbuff
int cmdlen; ///< number of chars in command line
int cmdpos; ///< current cursor position
int cmdspos; ///< cursor column on screen
int cmdfirstc; ///< ':', '/', '?', '=', '>' or NUL
int cmdindent; ///< number of spaces before cmdline
- char_u *cmdprompt; ///< message in front of cmdline
+ char *cmdprompt; ///< message in front of cmdline
int cmdattr; ///< attributes for prompt
int overstrike; ///< Typing mode on the command line. Shared by
///< getcmdline() and put_on_cmdline().
expand_T *xpc; ///< struct being used for expansion, xp_pattern
///< may point into cmdbuff
int xp_context; ///< type of expansion
- char_u *xp_arg; ///< user-defined expansion arg
+ char *xp_arg; ///< user-defined expansion arg
int input_fn; ///< when true Invoked for input() function
unsigned prompt_id; ///< Prompt number, used to disable coloring on errors.
Callback highlight_callback; ///< Callback used for coloring user input.
diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c
index 9495ae1c4b..3de5e1db52 100644
--- a/src/nvim/ex_session.c
+++ b/src/nvim/ex_session.c
@@ -9,31 +9,34 @@
#include <assert.h>
#include <inttypes.h>
+#include <limits.h>
#include <stdbool.h>
-#include <stdlib.h>
+#include <stdio.h>
#include <string.h>
#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/buffer.h"
-#include "nvim/cursor.h"
-#include "nvim/edit.h"
#include "nvim/eval.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
#include "nvim/ex_session.h"
#include "nvim/file_search.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
+#include "nvim/garray.h"
+#include "nvim/gettext.h"
#include "nvim/globals.h"
-#include "nvim/keycodes.h"
+#include "nvim/macros.h"
#include "nvim/mapping.h"
-#include "nvim/move.h"
+#include "nvim/mark_defs.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
#include "nvim/option.h"
-#include "nvim/os/input.h"
#include "nvim/os/os.h"
-#include "nvim/os/time.h"
#include "nvim/path.h"
+#include "nvim/pos.h"
#include "nvim/runtime.h"
#include "nvim/vim.h"
#include "nvim/window.h"
@@ -109,40 +112,43 @@ static int ses_win_rec(FILE *fd, frame_T *fr)
frame_T *frc;
int count = 0;
- if (fr->fr_layout != FR_LEAF) {
- // Find first frame that's not skipped and then create a window for
- // each following one (first frame is already there).
- frc = ses_skipframe(fr->fr_child);
- if (frc != NULL) {
- while ((frc = ses_skipframe(frc->fr_next)) != NULL) {
- // Make window as big as possible so that we have lots of room
- // to split.
- if (fprintf(fd, "%s%s",
- "wincmd _ | wincmd |\n",
- (fr->fr_layout == FR_COL ? "split\n" : "vsplit\n")) < 0) {
- return FAIL;
- }
- count++;
+ if (fr->fr_layout == FR_LEAF) {
+ return OK;
+ }
+
+ // Find first frame that's not skipped and then create a window for
+ // each following one (first frame is already there).
+ frc = ses_skipframe(fr->fr_child);
+ if (frc != NULL) {
+ while ((frc = ses_skipframe(frc->fr_next)) != NULL) {
+ // Make window as big as possible so that we have lots of room
+ // to split.
+ if (fprintf(fd, "%s%s",
+ "wincmd _ | wincmd |\n",
+ (fr->fr_layout == FR_COL ? "split\n" : "vsplit\n")) < 0) {
+ return FAIL;
}
+ count++;
}
+ }
- // Go back to the first window.
- if (count > 0 && (fprintf(fd, fr->fr_layout == FR_COL
- ? "%dwincmd k\n" : "%dwincmd h\n", count) < 0)) {
- return FAIL;
- }
+ // Go back to the first window.
+ if (count > 0 && (fprintf(fd, fr->fr_layout == FR_COL
+ ? "%dwincmd k\n" : "%dwincmd h\n", count) < 0)) {
+ return FAIL;
+ }
- // Recursively create frames/windows in each window of this column or row.
- frc = ses_skipframe(fr->fr_child);
- while (frc != NULL) {
- ses_win_rec(fd, frc);
- frc = ses_skipframe(frc->fr_next);
- // Go to next window.
- if (frc != NULL && put_line(fd, "wincmd w") == FAIL) {
- return FAIL;
- }
+ // Recursively create frames/windows in each window of this column or row.
+ frc = ses_skipframe(fr->fr_child);
+ while (frc != NULL) {
+ ses_win_rec(fd, frc);
+ frc = ses_skipframe(frc->fr_next);
+ // Go to next window.
+ if (frc != NULL && put_line(fd, "wincmd w") == FAIL) {
+ return FAIL;
}
}
+
return OK;
}
@@ -211,22 +217,22 @@ static int ses_do_win(win_T *wp)
/// @returns FAIL if writing fails.
static int ses_arglist(FILE *fd, char *cmd, garray_T *gap, int fullname, unsigned *flagp)
{
- char_u *buf = NULL;
- char_u *s;
+ char *buf = NULL;
+ char *s;
if (fprintf(fd, "%s\n%s\n", cmd, "%argdel") < 0) {
return FAIL;
}
for (int i = 0; i < gap->ga_len; i++) {
// NULL file names are skipped (only happens when out of memory).
- s = (char_u *)alist_name(&((aentry_T *)gap->ga_data)[i]);
+ s = alist_name(&((aentry_T *)gap->ga_data)[i]);
if (s != NULL) {
if (fullname) {
buf = xmalloc(MAXPATHL);
- (void)vim_FullName((char *)s, (char *)buf, MAXPATHL, false);
+ (void)vim_FullName(s, buf, MAXPATHL, false);
s = buf;
}
- char *fname_esc = ses_escape_fname((char *)s, flagp);
+ char *fname_esc = ses_escape_fname(s, flagp);
if (fprintf(fd, "$argadd %s\n", fname_esc) < 0) {
xfree(fname_esc);
xfree(buf);
@@ -240,7 +246,7 @@ static int ses_arglist(FILE *fd, char *cmd, garray_T *gap, int fullname, unsigne
}
/// @return the buffer name for `buf`.
-static char *ses_get_fname(buf_T *buf, unsigned *flagp)
+static char *ses_get_fname(buf_T *buf, const unsigned *flagp)
{
// Use the short file name if the current directory is known at the time
// the session file will be sourced.
@@ -264,7 +270,7 @@ static char *ses_get_fname(buf_T *buf, unsigned *flagp)
static int ses_fname(FILE *fd, buf_T *buf, unsigned *flagp, bool add_eol)
{
char *name = ses_get_fname(buf, flagp);
- if (ses_put_fname(fd, (char_u *)name, flagp) == FAIL
+ if (ses_put_fname(fd, name, flagp) == FAIL
|| (add_eol && fprintf(fd, "\n") < 0)) {
return FAIL;
}
@@ -299,9 +305,9 @@ static char *ses_escape_fname(char *name, unsigned *flagp)
/// characters.
///
/// @return FAIL if writing fails.
-static int ses_put_fname(FILE *fd, char_u *name, unsigned *flagp)
+static int ses_put_fname(FILE *fd, char *name, unsigned *flagp)
{
- char *p = ses_escape_fname((char *)name, flagp);
+ char *p = ses_escape_fname(name, flagp);
bool retval = fputs(p, fd) < 0 ? FAIL : OK;
xfree(p);
return retval;
@@ -359,7 +365,7 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr
// 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;
+ curtag = wp->w_tagstack[wp->w_tagstackidx - 1].tagname;
}
if (put_line(fd, "enew | setl bt=help") == FAIL
@@ -523,7 +529,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, (char_u *)wp->w_localdir, flagp) == FAIL
+ || ses_put_fname(fd, wp->w_localdir, flagp) == FAIL
|| fprintf(fd, "\n") < 0) {
return FAIL;
}
@@ -542,7 +548,7 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr
/// @param fd File descriptor to write to
///
/// @return FAIL on error, OK otherwise.
-static int makeopens(FILE *fd, char_u *dirnow)
+static int makeopens(FILE *fd, char *dirnow)
{
int only_save_windows = true;
int nr;
@@ -581,7 +587,7 @@ 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 : (char *)dirnow);
+ sname = home_replace_save(NULL, globaldir != NULL ? globaldir : dirnow);
char *fname_esc = ses_escape_fname(sname, &ssop_flags);
if (fprintf(fd, "cd %s\n", fname_esc) < 0) {
xfree(fname_esc);
@@ -828,7 +834,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, (char_u *)tp->tp_localdir, &ssop_flags) == FAIL
+ || ses_put_fname(fd, tp->tp_localdir, &ssop_flags) == FAIL
|| fputs(" | endif\n", fd) < 0) {
return FAIL;
}
@@ -903,12 +909,14 @@ static int makeopens(FILE *fd, char_u *dirnow)
void ex_loadview(exarg_T *eap)
{
char *fname = get_view_file(*eap->arg);
- if (fname != NULL) {
- if (do_source(fname, false, DOSO_NONE) == FAIL) {
- semsg(_(e_notopen), fname);
- }
- xfree(fname);
+ if (fname == NULL) {
+ return;
+ }
+
+ if (do_source(fname, false, DOSO_NONE) == FAIL) {
+ semsg(_(e_notopen), fname);
}
+ xfree(fname);
}
/// ":mkexrc", ":mkvimrc", ":mkview", ":mksession".
@@ -960,7 +968,7 @@ void ex_mkrc(exarg_T *eap)
vim_mkdir_emsg((const char *)p_vdir, 0755);
}
- fd = open_exfile((char_u *)fname, eap->forceit, WRITEBIN);
+ fd = open_exfile(fname, eap->forceit, WRITEBIN);
if (fd != NULL) {
if (eap->cmdidx == CMD_mkview) {
flagp = &vop_flags;
@@ -997,14 +1005,14 @@ void ex_mkrc(exarg_T *eap)
failed = true;
}
if (eap->cmdidx == CMD_mksession) {
- char_u *dirnow; // current directory
+ char *dirnow; // current directory
dirnow = xmalloc(MAXPATHL);
//
// Change to session file's dir.
//
if (os_dirname(dirnow, MAXPATHL) == FAIL
- || os_chdir((char *)dirnow) != 0) {
+ || os_chdir(dirnow) != 0) {
*dirnow = NUL;
}
if (*dirnow != NUL && (ssop_flags & SSOP_SESDIR)) {
@@ -1024,7 +1032,7 @@ void ex_mkrc(exarg_T *eap)
if (*dirnow != NUL && ((ssop_flags & SSOP_SESDIR)
|| ((ssop_flags & SSOP_CURDIR) && globaldir !=
NULL))) {
- if (os_chdir((char *)dirnow) != 0) {
+ if (os_chdir(dirnow) != 0) {
emsg(_(e_prev_dir));
}
shorten_fnames(true);
@@ -1095,7 +1103,7 @@ static char *get_view_file(int c)
len++;
}
}
- char *retval = xmalloc(strlen(sname) + len + STRLEN(p_vdir) + 9);
+ char *retval = xmalloc(strlen(sname) + len + strlen(p_vdir) + 9);
STRCPY(retval, p_vdir);
add_pathsep(retval);
char *s = retval + strlen(retval);
diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c
index 1639f72990..3e059bcc6c 100644
--- a/src/nvim/extmark.c
+++ b/src/nvim/extmark.c
@@ -29,20 +29,21 @@
// code for redrawing the line with the deleted decoration.
#include <assert.h>
+#include <sys/types.h>
-#include "nvim/api/extmark.h"
#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
#include "nvim/buffer_updates.h"
-#include "nvim/charset.h"
#include "nvim/decoration.h"
#include "nvim/extmark.h"
+#include "nvim/extmark_defs.h"
#include "nvim/globals.h"
-#include "nvim/lib/kbtree.h"
#include "nvim/map.h"
+#include "nvim/marktree.h"
#include "nvim/memline.h"
+#include "nvim/memory.h"
#include "nvim/pos.h"
#include "nvim/undo.h"
-#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "extmark.c.generated.h"
@@ -70,7 +71,8 @@ void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col
|| kv_size(decor->virt_lines)
|| decor->conceal
|| decor_has_sign(decor)
- || decor->ui_watched) {
+ || decor->ui_watched
+ || decor->spell != kNone) {
decor_full = true;
decor = xmemdup(decor, sizeof *decor);
}
@@ -111,6 +113,7 @@ void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col
marktree_revise(buf->b_marktree, itr, decor_level, old_mark);
goto revised;
}
+ decor_remove(buf, old_mark.pos.row, old_mark.pos.row, old_mark.decor_full);
marktree_del_itr(buf->b_marktree, itr, false);
}
} else {
@@ -510,11 +513,11 @@ void extmark_adjust(buf_T *buf, linenr_T line1, linenr_T line2, linenr_T amount,
bcount_t old_byte = 0, new_byte = 0;
int old_row, new_row;
if (amount == MAXLNUM) {
- old_row = (int)(line2 - line1 + 1);
+ old_row = line2 - line1 + 1;
// TODO(bfredl): ej kasta?
old_byte = (bcount_t)buf->deleted_bytes2;
- new_row = (int)(amount_after + old_row);
+ new_row = amount_after + old_row;
} else {
// A region is either deleted (amount == MAXLNUM) or
// added (line2 == MAXLNUM). The only other case is :move
diff --git a/src/nvim/extmark.h b/src/nvim/extmark.h
index c144504076..657e99a938 100644
--- a/src/nvim/extmark.h
+++ b/src/nvim/extmark.h
@@ -1,11 +1,18 @@
#ifndef NVIM_EXTMARK_H
#define NVIM_EXTMARK_H
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "klib/kvec.h"
#include "nvim/buffer_defs.h"
#include "nvim/decoration.h"
#include "nvim/extmark_defs.h"
+#include "nvim/macros.h"
#include "nvim/marktree.h"
#include "nvim/pos.h"
+#include "nvim/types.h"
EXTERN int extmark_splice_pending INIT(= 0);
diff --git a/src/nvim/extmark_defs.h b/src/nvim/extmark_defs.h
index 5570b5c71e..51b60dcf6d 100644
--- a/src/nvim/extmark_defs.h
+++ b/src/nvim/extmark_defs.h
@@ -1,7 +1,7 @@
#ifndef NVIM_EXTMARK_DEFS_H
#define NVIM_EXTMARK_DEFS_H
-#include "nvim/lib/kvec.h"
+#include "klib/kvec.h"
#include "nvim/types.h"
typedef struct {
@@ -9,6 +9,8 @@ typedef struct {
int hl_id;
} VirtTextChunk;
+typedef kvec_t(VirtTextChunk) VirtText;
+
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 2d09e7aa71..e236f23895 100644
--- a/src/nvim/file_search.c
+++ b/src/nvim/file_search.c
@@ -47,41 +47,43 @@
#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include "nvim/ascii.h"
#include "nvim/autocmd.h"
-#include "nvim/charset.h"
+#include "nvim/buffer_defs.h"
#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/file_search.h"
-#include "nvim/fileio.h"
+#include "nvim/gettext.h"
#include "nvim/globals.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/option.h"
#include "nvim/os/fs_defs.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
-#include "nvim/os_unix.h"
#include "nvim/path.h"
#include "nvim/strings.h"
-#include "nvim/tag.h"
+#include "nvim/types.h"
#include "nvim/vim.h"
#include "nvim/window.h"
-static char_u *ff_expand_buffer = NULL; // used for expanding filenames
+static char *ff_expand_buffer = NULL; // used for expanding filenames
-/*
- * type for the directory search stack
- */
+// type for the directory search stack
typedef struct ff_stack {
struct ff_stack *ffs_prev;
- /* the fix part (no wildcards) and the part containing the wildcards
- * of the search path
- */
- char_u *ffs_fix_path;
- char_u *ffs_wc_path;
+ // the fix part (no wildcards) and the part containing the wildcards
+ // of the search path
+ char *ffs_fix_path;
+ char *ffs_wc_path;
// files/dirs found in the above directory, matched by the first wildcard
// of wc_part
@@ -89,98 +91,87 @@ typedef struct ff_stack {
int ffs_filearray_size;
int ffs_filearray_cur; // needed for partly handled dirs
- /* to store status of partly handled directories
- * 0: we work on this directory for the first time
- * 1: this directory was partly searched in an earlier step
- */
+ // to store status of partly handled directories
+ // 0: we work on this directory for the first time
+ // 1: this directory was partly searched in an earlier step
int ffs_stage;
- /* How deep are we in the directory tree?
- * Counts backward from value of level parameter to vim_findfile_init
- */
+ // How deep are we in the directory tree?
+ // Counts backward from value of level parameter to vim_findfile_init
int ffs_level;
// Did we already expand '**' to an empty string?
int ffs_star_star_empty;
} ff_stack_T;
-/*
- * type for already visited directories or files.
- */
+// type for already visited directories or files.
typedef struct ff_visited {
struct ff_visited *ffv_next;
- /* Visited directories are different if the wildcard string are
- * different. So we have to save it.
- */
- char_u *ffv_wc_path;
+ // Visited directories are different if the wildcard string are
+ // different. So we have to save it.
+ char *ffv_wc_path;
+
// use FileID for comparison (needed because of links), else use filename.
bool file_id_valid;
FileID file_id;
- /* The memory for this struct is allocated according to the length of
- * ffv_fname.
- */
- char_u ffv_fname[1]; // actually longer
+ // The memory for this struct is allocated according to the length of
+ // ffv_fname.
+ char ffv_fname[1]; // actually longer
} ff_visited_T;
-/*
- * We might have to manage several visited lists during a search.
- * This is especially needed for the tags option. If tags is set to:
- * "./++/tags,./++/TAGS,++/tags" (replace + with *)
- * So we have to do 3 searches:
- * 1) search from the current files directory downward for the file "tags"
- * 2) search from the current files directory downward for the file "TAGS"
- * 3) search from Vims current directory downwards for the file "tags"
- * As you can see, the first and the third search are for the same file, so for
- * the third search we can use the visited list of the first search. For the
- * second search we must start from an empty visited list.
- * The struct ff_visited_list_hdr is used to manage a linked list of already
- * visited lists.
- */
+// We might have to manage several visited lists during a search.
+// This is especially needed for the tags option. If tags is set to:
+// "./++/tags,./++/TAGS,++/tags" (replace + with *)
+// So we have to do 3 searches:
+// 1) search from the current files directory downward for the file "tags"
+// 2) search from the current files directory downward for the file "TAGS"
+// 3) search from Vims current directory downwards for the file "tags"
+// As you can see, the first and the third search are for the same file, so for
+// the third search we can use the visited list of the first search. For the
+// second search we must start from an empty visited list.
+// The struct ff_visited_list_hdr is used to manage a linked list of already
+// visited lists.
typedef struct ff_visited_list_hdr {
struct ff_visited_list_hdr *ffvl_next;
// the filename the attached visited list is for
- char_u *ffvl_filename;
+ char *ffvl_filename;
ff_visited_T *ffvl_visited_list;
} ff_visited_list_hdr_T;
-/*
- * '**' can be expanded to several directory levels.
- * Set the default maximum depth.
- */
-#define FF_MAX_STAR_STAR_EXPAND ((char_u)30)
-
-/*
- * The search context:
- * ffsc_stack_ptr: the stack for the dirs to search
- * ffsc_visited_list: the currently active visited list
- * ffsc_dir_visited_list: the currently active visited list for search dirs
- * ffsc_visited_lists_list: the list of all visited lists
- * ffsc_dir_visited_lists_list: the list of all visited lists for search dirs
- * ffsc_file_to_search: the file to search for
- * ffsc_start_dir: the starting directory, if search path was relative
- * ffsc_fix_path: the fix part of the given path (without wildcards)
- * Needed for upward search.
- * ffsc_wc_path: the part of the given path containing wildcards
- * ffsc_level: how many levels of dirs to search downwards
- * ffsc_stopdirs_v: array of stop directories for upward search
- * ffsc_find_what: FINDFILE_BOTH, FINDFILE_DIR or FINDFILE_FILE
- * ffsc_tagfile: searching for tags file, don't use 'suffixesadd'
- */
+// '**' can be expanded to several directory levels.
+// Set the default maximum depth.
+#define FF_MAX_STAR_STAR_EXPAND 30
+
+// The search context:
+// ffsc_stack_ptr: the stack for the dirs to search
+// ffsc_visited_list: the currently active visited list
+// ffsc_dir_visited_list: the currently active visited list for search dirs
+// ffsc_visited_lists_list: the list of all visited lists
+// ffsc_dir_visited_lists_list: the list of all visited lists for search dirs
+// ffsc_file_to_search: the file to search for
+// ffsc_start_dir: the starting directory, if search path was relative
+// ffsc_fix_path: the fix part of the given path (without wildcards)
+// Needed for upward search.
+// ffsc_wc_path: the part of the given path containing wildcards
+// ffsc_level: how many levels of dirs to search downwards
+// ffsc_stopdirs_v: array of stop directories for upward search
+// ffsc_find_what: FINDFILE_BOTH, FINDFILE_DIR or FINDFILE_FILE
+// ffsc_tagfile: searching for tags file, don't use 'suffixesadd'
typedef struct ff_search_ctx_T {
ff_stack_T *ffsc_stack_ptr;
ff_visited_list_hdr_T *ffsc_visited_list;
ff_visited_list_hdr_T *ffsc_dir_visited_list;
ff_visited_list_hdr_T *ffsc_visited_lists_list;
ff_visited_list_hdr_T *ffsc_dir_visited_lists_list;
- char_u *ffsc_file_to_search;
- char_u *ffsc_start_dir;
- char_u *ffsc_fix_path;
- char_u *ffsc_wc_path;
+ char *ffsc_file_to_search;
+ char *ffsc_start_dir;
+ char *ffsc_fix_path;
+ char *ffsc_wc_path;
int ffsc_level;
- char_u **ffsc_stopdirs_v;
+ char **ffsc_stopdirs_v;
int ffsc_find_what;
int ffsc_tagfile;
} ff_search_ctx_T;
@@ -191,7 +182,7 @@ typedef struct ff_search_ctx_T {
# include "file_search.c.generated.h"
#endif
-static char_u e_pathtoolong[] = N_("E854: path too long for completion");
+static char e_pathtoolong[] = N_("E854: path too long for completion");
/// Initialization routine for vim_findfile().
///
@@ -221,8 +212,8 @@ static char_u e_pathtoolong[] = N_("E854: path too long for completion");
///
/// Upward search is only done on the starting dir.
///
-/// If 'free_visited' is TRUE the list of already visited files/directories is
-/// cleared. Set this to FALSE if you just want to search from another
+/// If 'free_visited' is true the list of already visited files/directories is
+/// cleared. Set this to false if you just want to search from another
/// directory, but want to be sure that no directory from a previous search is
/// searched again. This is useful if you search for a file at different places.
/// The list of visited files/dirs can also be cleared with the function
@@ -246,17 +237,15 @@ static char_u e_pathtoolong[] = N_("E854: path too long for completion");
///
/// @param tagfile expanding names of tags files
/// @param rel_fname file name to use for "."
-void *vim_findfile_init(char_u *path, char_u *filename, char_u *stopdirs, int level,
- int free_visited, int find_what, void *search_ctx_arg, int tagfile,
- char_u *rel_fname)
+void *vim_findfile_init(char *path, char *filename, char *stopdirs, int level, int free_visited,
+ int find_what, void *search_ctx_arg, int tagfile, char *rel_fname)
{
- char_u *wc_part;
+ char *wc_part;
ff_stack_T *sptr;
ff_search_ctx_T *search_ctx;
- /* If a search context is given by the caller, reuse it, else allocate a
- * new one.
- */
+ // If a search context is given by the caller, reuse it, else allocate a
+ // new one.
if (search_ctx_arg != NULL) {
search_ctx = search_ctx_arg;
} else {
@@ -269,12 +258,12 @@ void *vim_findfile_init(char_u *path, char_u *filename, char_u *stopdirs, int le
ff_clear(search_ctx);
// clear visited list if wanted
- if (free_visited == TRUE) {
+ if (free_visited == true) {
vim_findfile_free_visited(search_ctx);
} else {
- /* Reuse old visited lists. Get the visited list for the given
- * filename. If no list for the current filename exists, creates a new
- * one. */
+ // Reuse old visited lists. Get the visited list for the given
+ // filename. If no list for the current filename exists, creates a new
+ // one.
search_ctx->ffsc_visited_list = ff_get_visited_list(filename,
&search_ctx->ffsc_visited_lists_list);
if (search_ctx->ffsc_visited_list == NULL) {
@@ -291,20 +280,20 @@ void *vim_findfile_init(char_u *path, char_u *filename, char_u *stopdirs, int le
ff_expand_buffer = xmalloc(MAXPATHL);
}
- /* Store information on starting dir now if path is relative.
- * If path is absolute, we do that later. */
+ // Store information on starting dir now if path is relative.
+ // If path is absolute, we do that later.
if (path[0] == '.'
&& (vim_ispathsep(path[1]) || path[1] == NUL)
&& (!tagfile || vim_strchr(p_cpo, CPO_DOTTAG) == NULL)
&& rel_fname != NULL) {
- size_t len = (size_t)((char_u *)path_tail((char *)rel_fname) - rel_fname);
+ size_t len = (size_t)(path_tail(rel_fname) - rel_fname);
if (!vim_isAbsName(rel_fname) && len + 1 < MAXPATHL) {
// Make the start dir an absolute path name.
- STRLCPY(ff_expand_buffer, rel_fname, len + 1);
- search_ctx->ffsc_start_dir = (char_u *)FullName_save((char *)ff_expand_buffer, FALSE);
+ xstrlcpy(ff_expand_buffer, rel_fname, len + 1);
+ search_ctx->ffsc_start_dir = FullName_save(ff_expand_buffer, false);
} else {
- search_ctx->ffsc_start_dir = vim_strnsave(rel_fname, len);
+ search_ctx->ffsc_start_dir = xstrnsave(rel_fname, len);
}
if (*++path != NUL) {
path++;
@@ -313,28 +302,26 @@ void *vim_findfile_init(char_u *path, char_u *filename, char_u *stopdirs, int le
#ifdef BACKSLASH_IN_FILENAME
// "c:dir" needs "c:" to be expanded, otherwise use current dir
if (*path != NUL && path[1] == ':') {
- char_u drive[3];
+ char drive[3];
drive[0] = path[0];
drive[1] = ':';
drive[2] = NUL;
- if (vim_FullName((const char *)drive, (char *)ff_expand_buffer, MAXPATHL,
- true)
- == FAIL) {
+ if (vim_FullName(drive, ff_expand_buffer, MAXPATHL, true) == FAIL) {
goto error_return;
}
path += 2;
- } else
+ } else // NOLINT(readability/braces)
#endif
if (os_dirname(ff_expand_buffer, MAXPATHL) == FAIL) {
goto error_return;
}
- search_ctx->ffsc_start_dir = vim_strsave(ff_expand_buffer);
+ search_ctx->ffsc_start_dir = xstrdup(ff_expand_buffer);
#ifdef BACKSLASH_IN_FILENAME
- /* A path that starts with "/dir" is relative to the drive, not to the
- * directory (but not for "//machine/dir"). Only use the drive name. */
+ // A path that starts with "/dir" is relative to the drive, not to the
+ // directory (but not for "//machine/dir"). Only use the drive name.
if ((*path == '/' || *path == '\\')
&& path[1] != path[0]
&& search_ctx->ffsc_start_dir[1] == ':') {
@@ -343,45 +330,39 @@ void *vim_findfile_init(char_u *path, char_u *filename, char_u *stopdirs, int le
#endif
}
- /*
- * If stopdirs are given, split them into an array of pointers.
- * If this fails (mem allocation), there is no upward search at all or a
- * stop directory is not recognized -> continue silently.
- * If stopdirs just contains a ";" or is empty,
- * search_ctx->ffsc_stopdirs_v will only contain a NULL pointer. This
- * is handled as unlimited upward search. See function
- * ff_path_in_stoplist() for details.
- */
+ // If stopdirs are given, split them into an array of pointers.
+ // If this fails (mem allocation), there is no upward search at all or a
+ // stop directory is not recognized -> continue silently.
+ // If stopdirs just contains a ";" or is empty,
+ // search_ctx->ffsc_stopdirs_v will only contain a NULL pointer. This
+ // is handled as unlimited upward search. See function
+ // ff_path_in_stoplist() for details.
if (stopdirs != NULL) {
- char_u *walker = stopdirs;
+ char *walker = stopdirs;
while (*walker == ';') {
walker++;
}
size_t dircount = 1;
- search_ctx->ffsc_stopdirs_v = xmalloc(sizeof(char_u *));
+ search_ctx->ffsc_stopdirs_v = xmalloc(sizeof(char *));
do {
- char_u *helper;
+ char *helper;
void *ptr;
helper = walker;
ptr = xrealloc(search_ctx->ffsc_stopdirs_v,
- (dircount + 1) * sizeof(char_u *));
+ (dircount + 1) * sizeof(char *));
search_ctx->ffsc_stopdirs_v = ptr;
- walker = (char_u *)vim_strchr((char *)walker, ';');
+ walker = vim_strchr(walker, ';');
if (walker) {
assert(walker - helper >= 0);
- search_ctx->ffsc_stopdirs_v[dircount - 1] =
- vim_strnsave(helper, (size_t)(walker - helper));
+ search_ctx->ffsc_stopdirs_v[dircount - 1] = xstrnsave(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] =
- vim_strsave(helper);
+ // this might be "", which means ascent till top of directory tree.
+ search_ctx->ffsc_stopdirs_v[dircount - 1] = xstrdup(helper);
}
dircount++;
@@ -391,11 +372,10 @@ void *vim_findfile_init(char_u *path, char_u *filename, char_u *stopdirs, int le
search_ctx->ffsc_level = level;
- /* split into:
- * -fix path
- * -wildcard_stuff (might be NULL)
- */
- wc_part = (char_u *)vim_strchr((char *)path, '*');
+ // split into:
+ // -fix path
+ // -wildcard_stuff (might be NULL)
+ wc_part = vim_strchr(path, '*');
if (wc_part != NULL) {
int64_t llevel;
int len;
@@ -403,37 +383,35 @@ void *vim_findfile_init(char_u *path, char_u *filename, char_u *stopdirs, int le
// save the fix part of the path
assert(wc_part - path >= 0);
- search_ctx->ffsc_fix_path = vim_strnsave(path, (size_t)(wc_part - path));
-
- /*
- * copy wc_path and add restricts to the '**' wildcard.
- * The octet after a '**' is used as a (binary) counter.
- * So '**3' is transposed to '**^C' ('^C' is ASCII value 3)
- * or '**76' is transposed to '**N'( 'N' is ASCII value 76).
- * If no restrict is given after '**' the default is used.
- * Due to this technique the path looks awful if you print it as a
- * string.
- */
+ search_ctx->ffsc_fix_path = xstrnsave(path, (size_t)(wc_part - path));
+
+ // copy wc_path and add restricts to the '**' wildcard.
+ // The octet after a '**' is used as a (binary) counter.
+ // So '**3' is transposed to '**^C' ('^C' is ASCII value 3)
+ // or '**76' is transposed to '**N'( 'N' is ASCII value 76).
+ // If no restrict is given after '**' the default is used.
+ // Due to this technique the path looks awful if you print it as a
+ // string.
len = 0;
while (*wc_part != NUL) {
if (len + 5 >= MAXPATHL) {
emsg(_(e_pathtoolong));
break;
}
- if (STRNCMP(wc_part, "**", 2) == 0) {
+ if (strncmp(wc_part, "**", 2) == 0) {
ff_expand_buffer[len++] = *wc_part++;
ff_expand_buffer[len++] = *wc_part++;
- llevel = strtol((char *)wc_part, &errpt, 10);
- if ((char_u *)errpt != wc_part && llevel > 0 && llevel < 255) {
- ff_expand_buffer[len++] = (char_u)llevel;
- } else if ((char_u *)errpt != wc_part && llevel == 0) {
+ llevel = strtol(wc_part, &errpt, 10);
+ if (errpt != wc_part && llevel > 0 && llevel < 255) {
+ ff_expand_buffer[len++] = (char)llevel;
+ } else if (errpt != wc_part && llevel == 0) {
// restrict is 0 -> remove already added '**'
len -= 2;
} else {
ff_expand_buffer[len++] = FF_MAX_STAR_STAR_EXPAND;
}
- wc_part = (char_u *)errpt;
+ wc_part = errpt;
if (*wc_part != NUL && !vim_ispathsep(*wc_part)) {
semsg(_(
"E343: Invalid path: '**[number]' must be at the end of the path or be followed by '%s'."),
@@ -445,60 +423,59 @@ void *vim_findfile_init(char_u *path, char_u *filename, char_u *stopdirs, int le
}
}
ff_expand_buffer[len] = NUL;
- search_ctx->ffsc_wc_path = vim_strsave(ff_expand_buffer);
+ search_ctx->ffsc_wc_path = xstrdup(ff_expand_buffer);
} else {
- search_ctx->ffsc_fix_path = vim_strsave(path);
+ search_ctx->ffsc_fix_path = xstrdup(path);
}
if (search_ctx->ffsc_start_dir == NULL) {
- /* store the fix part as startdir.
- * This is needed if the parameter path is fully qualified.
- */
- search_ctx->ffsc_start_dir = vim_strsave(search_ctx->ffsc_fix_path);
+ // store the fix part as startdir.
+ // This is needed if the parameter path is fully qualified.
+ search_ctx->ffsc_start_dir = xstrdup(search_ctx->ffsc_fix_path);
search_ctx->ffsc_fix_path[0] = NUL;
}
// create an absolute path
- if (STRLEN(search_ctx->ffsc_start_dir)
- + STRLEN(search_ctx->ffsc_fix_path) + 3 >= MAXPATHL) {
+ if (strlen(search_ctx->ffsc_start_dir)
+ + strlen(search_ctx->ffsc_fix_path) + 3 >= MAXPATHL) {
emsg(_(e_pathtoolong));
goto error_return;
}
STRCPY(ff_expand_buffer, search_ctx->ffsc_start_dir);
- add_pathsep((char *)ff_expand_buffer);
+ add_pathsep(ff_expand_buffer);
{
- size_t eb_len = STRLEN(ff_expand_buffer);
- char_u *buf = xmalloc(eb_len + STRLEN(search_ctx->ffsc_fix_path) + 1);
+ size_t eb_len = strlen(ff_expand_buffer);
+ char *buf = xmalloc(eb_len + strlen(search_ctx->ffsc_fix_path) + 1);
STRCPY(buf, ff_expand_buffer);
STRCPY(buf + eb_len, search_ctx->ffsc_fix_path);
if (os_isdir(buf)) {
STRCAT(ff_expand_buffer, search_ctx->ffsc_fix_path);
- add_pathsep((char *)ff_expand_buffer);
+ add_pathsep(ff_expand_buffer);
} else {
- char_u *p = (char_u *)path_tail((char *)search_ctx->ffsc_fix_path);
- char_u *wc_path = NULL;
- char_u *temp = NULL;
+ char *p = path_tail(search_ctx->ffsc_fix_path);
+ char *wc_path = NULL;
+ char *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;
- if ((len >= 2 && STRNCMP(search_ctx->ffsc_fix_path, "..", 2) == 0)
+ 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);
+ xstrlcat(ff_expand_buffer, search_ctx->ffsc_fix_path, eb_len + (size_t)len + 1);
+ add_pathsep(ff_expand_buffer);
} else {
- len = (int)STRLEN(search_ctx->ffsc_fix_path);
+ len = (int)strlen(search_ctx->ffsc_fix_path);
}
if (search_ctx->ffsc_wc_path != NULL) {
- wc_path = vim_strsave(search_ctx->ffsc_wc_path);
- temp = xmalloc(STRLEN(search_ctx->ffsc_wc_path)
- + STRLEN(search_ctx->ffsc_fix_path + len)
+ wc_path = xstrdup(search_ctx->ffsc_wc_path);
+ temp = xmalloc(strlen(search_ctx->ffsc_wc_path)
+ + strlen(search_ctx->ffsc_fix_path + len)
+ 1);
STRCPY(temp, search_ctx->ffsc_fix_path + len);
STRCAT(temp, search_ctx->ffsc_wc_path);
@@ -510,33 +487,29 @@ void *vim_findfile_init(char_u *path, char_u *filename, char_u *stopdirs, int le
xfree(buf);
}
- sptr = ff_create_stack_element(ff_expand_buffer,
- search_ctx->ffsc_wc_path,
- level, 0);
+ sptr = ff_create_stack_element(ff_expand_buffer, search_ctx->ffsc_wc_path, level, 0);
ff_push(search_ctx, sptr);
- search_ctx->ffsc_file_to_search = vim_strsave(filename);
+ search_ctx->ffsc_file_to_search = xstrdup(filename);
return search_ctx;
error_return:
- /*
- * We clear the search context now!
- * Even when the caller gave us a (perhaps valid) context we free it here,
- * as we might have already destroyed it.
- */
+ // We clear the search context now!
+ // Even when the caller gave us a (perhaps valid) context we free it here,
+ // as we might have already destroyed it.
vim_findfile_cleanup(search_ctx);
return NULL;
}
/// @return the stopdir string. Check that ';' is not escaped.
-char_u *vim_findfile_stopdir(char_u *buf)
+char *vim_findfile_stopdir(char *buf)
{
- char_u *r_ptr = buf;
+ char *r_ptr = buf;
while (*r_ptr != NUL && *r_ptr != ';') {
if (r_ptr[0] == '\\' && r_ptr[1] == ';') {
- /* Overwrite the escape char,
- * use STRLEN(r_ptr) to move the trailing '\0'. */
+ // Overwrite the escape char,
+ // use strlen(r_ptr) to move the trailing '\0'.
STRMOVE(r_ptr, r_ptr + 1);
r_ptr++;
}
@@ -576,14 +549,14 @@ void vim_findfile_cleanup(void *ctx)
///
/// @return a pointer to an allocated file name or,
/// NULL if nothing found.
-char_u *vim_findfile(void *search_ctx_arg)
+char *vim_findfile(void *search_ctx_arg)
{
- char_u *file_path;
- char_u *rest_of_wildcards;
- char_u *path_end = NULL;
+ char *file_path;
+ char *rest_of_wildcards;
+ char *path_end = NULL;
ff_stack_T *stackp = NULL;
size_t len;
- char_u *p;
+ char *p;
char *suf;
ff_search_ctx_T *search_ctx;
@@ -593,16 +566,13 @@ char_u *vim_findfile(void *search_ctx_arg)
search_ctx = (ff_search_ctx_T *)search_ctx_arg;
- /*
- * filepath is used as buffer for various actions and as the storage to
- * return a found filename.
- */
+ // filepath is used as buffer for various actions and as the storage to
+ // return a found filename.
file_path = xmalloc(MAXPATHL);
// store the end of the start dir -- needed for upward search
if (search_ctx->ffsc_start_dir != NULL) {
- path_end = &search_ctx->ffsc_start_dir[
- STRLEN(search_ctx->ffsc_start_dir)];
+ path_end = &search_ctx->ffsc_start_dir[strlen(search_ctx->ffsc_start_dir)];
}
// upward search loop
@@ -621,31 +591,26 @@ char_u *vim_findfile(void *search_ctx_arg)
break;
}
- /*
- * TODO: decide if we leave this test in
- *
- * GOOD: don't search a directory(-tree) twice.
- * BAD: - check linked list for every new directory entered.
- * - check for double files also done below
- *
- * Here we check if we already searched this directory.
- * We already searched a directory if:
- * 1) The directory is the same.
- * 2) We would use the same wildcard string.
- *
- * Good if you have links on same directory via several ways
- * or you have selfreferences in directories (e.g. SuSE Linux 6.3:
- * /etc/rc.d/init.d is linked to /etc/rc.d -> endless loop)
- *
- * This check is only needed for directories we work on for the
- * first time (hence stackp->ff_filearray == NULL)
- */
+ // TODO(vim): decide if we leave this test in
+ //
+ // GOOD: don't search a directory(-tree) twice.
+ // BAD: - check linked list for every new directory entered.
+ // - check for double files also done below
+ //
+ // Here we check if we already searched this directory.
+ // We already searched a directory if:
+ // 1) The directory is the same.
+ // 2) We would use the same wildcard string.
+ //
+ // Good if you have links on same directory via several ways
+ // or you have selfreferences in directories (e.g. SuSE Linux 6.3:
+ // /etc/rc.d/init.d is linked to /etc/rc.d -> endless loop)
+ //
+ // This check is only needed for directories we work on for the
+ // first time (hence stackp->ff_filearray == NULL)
if (stackp->ffs_filearray == NULL
- && ff_check_visited(&search_ctx->ffsc_dir_visited_list
- ->ffvl_visited_list,
- stackp->ffs_fix_path,
- stackp->ffs_wc_path
- ) == FAIL) {
+ && ff_check_visited(&search_ctx->ffsc_dir_visited_list->ffvl_visited_list,
+ stackp->ffs_fix_path, stackp->ffs_wc_path) == FAIL) {
#ifdef FF_VERBOSE
if (p_verbose >= 5) {
verbose_enter_scroll();
@@ -657,16 +622,15 @@ char_u *vim_findfile(void *search_ctx_arg)
#endif
ff_free_stack_element(stackp);
continue;
- }
#ifdef FF_VERBOSE
- else if (p_verbose >= 5) {
+ } else if (p_verbose >= 5) {
verbose_enter_scroll();
smsg("Searching: %s (%s)",
stackp->ffs_fix_path, stackp->ffs_wc_path);
msg_puts("\n"); // don't overwrite this either
verbose_leave_scroll();
- }
#endif
+ }
// check depth
if (stackp->ffs_level <= 0) {
@@ -676,50 +640,46 @@ char_u *vim_findfile(void *search_ctx_arg)
file_path[0] = NUL;
- /*
- * If no filearray till now expand wildcards
- * The function expand_wildcards() can handle an array of paths
- * and all possible expands are returned in one array. We use this
- * to handle the expansion of '**' into an empty string.
- */
+ // If no filearray till now expand wildcards
+ // The function expand_wildcards() can handle an array of paths
+ // and all possible expands are returned in one array. We use this
+ // to handle the expansion of '**' into an empty string.
if (stackp->ffs_filearray == NULL) {
char *dirptrs[2];
- /* we use filepath to build the path expand_wildcards() should
- * expand.
- */
- dirptrs[0] = (char *)file_path;
+ // we use filepath to build the path expand_wildcards() should expand.
+ dirptrs[0] = file_path;
dirptrs[1] = NULL;
// if we have a start dir copy it in
if (!vim_isAbsName(stackp->ffs_fix_path)
&& search_ctx->ffsc_start_dir) {
- if (STRLEN(search_ctx->ffsc_start_dir) + 1 >= MAXPATHL) {
+ if (strlen(search_ctx->ffsc_start_dir) + 1 >= MAXPATHL) {
ff_free_stack_element(stackp);
goto fail;
}
STRCPY(file_path, search_ctx->ffsc_start_dir);
- if (!add_pathsep((char *)file_path)) {
+ if (!add_pathsep(file_path)) {
ff_free_stack_element(stackp);
goto fail;
}
}
// append the fix part of the search path
- if (STRLEN(file_path) + STRLEN(stackp->ffs_fix_path) + 1 >= MAXPATHL) {
+ if (strlen(file_path) + strlen(stackp->ffs_fix_path) + 1 >= MAXPATHL) {
ff_free_stack_element(stackp);
goto fail;
}
STRCAT(file_path, stackp->ffs_fix_path);
- if (!add_pathsep((char *)file_path)) {
+ if (!add_pathsep(file_path)) {
ff_free_stack_element(stackp);
goto fail;
}
rest_of_wildcards = stackp->ffs_wc_path;
if (*rest_of_wildcards != NUL) {
- len = STRLEN(file_path);
- if (STRNCMP(rest_of_wildcards, "**", 2) == 0) {
+ len = strlen(file_path);
+ if (strncmp(rest_of_wildcards, "**", 2) == 0) {
// pointer to the restrict byte
// The restrict byte is not a character!
p = rest_of_wildcards + 2;
@@ -743,17 +703,15 @@ char_u *vim_findfile(void *search_ctx_arg)
if (stackp->ffs_star_star_empty == 0) {
// if not done before, expand '**' to empty
stackp->ffs_star_star_empty = 1;
- dirptrs[1] = (char *)stackp->ffs_fix_path;
+ dirptrs[1] = stackp->ffs_fix_path;
}
}
- /*
- * Here we copy until the next path separator or the end of
- * the path. If we stop at a path separator, there is
- * still something else left. This is handled below by
- * pushing every directory returned from expand_wildcards()
- * on the stack again for further search.
- */
+ // Here we copy until the next path separator or the end of
+ // the path. If we stop at a path separator, there is
+ // still something else left. This is handled below by
+ // pushing every directory returned from expand_wildcards()
+ // on the stack again for further search.
while (*rest_of_wildcards
&& !vim_ispathsep(*rest_of_wildcards)) {
if (len + 1 >= MAXPATHL) {
@@ -769,18 +727,16 @@ char_u *vim_findfile(void *search_ctx_arg)
}
}
- /*
- * Expand wildcards like "*" and "$VAR".
- * If the path is a URL don't try this.
- */
+ // Expand wildcards like "*" and "$VAR".
+ // If the path is a URL don't try this.
if (path_with_url(dirptrs[0])) {
stackp->ffs_filearray = xmalloc(sizeof(char *));
stackp->ffs_filearray[0] = xstrdup(dirptrs[0]);
stackp->ffs_filearray_size = 1;
} else {
- /* Add EW_NOTWILD because the expanded path may contain
- * wildcard characters that are to be taken literally.
- * This is a bit of a hack. */
+ // Add EW_NOTWILD because the expanded path may contain
+ // wildcard characters that are to be taken literally.
+ // This is a bit of a hack.
expand_wildcards((dirptrs[1] == NULL) ? 1 : 2, dirptrs,
&stackp->ffs_filearray_size,
&stackp->ffs_filearray,
@@ -790,66 +746,55 @@ char_u *vim_findfile(void *search_ctx_arg)
stackp->ffs_filearray_cur = 0;
stackp->ffs_stage = 0;
} else {
- rest_of_wildcards = &stackp->ffs_wc_path[
- STRLEN(stackp->ffs_wc_path)];
+ rest_of_wildcards = &stackp->ffs_wc_path[strlen(stackp->ffs_wc_path)];
}
if (stackp->ffs_stage == 0) {
// this is the first time we work on this directory
if (*rest_of_wildcards == NUL) {
- /*
- * We don't have further wildcards to expand, so we have to
- * check for the final file now.
- */
+ // We don't have further wildcards to expand, so we have to
+ // check for the final file now.
for (int i = stackp->ffs_filearray_cur; i < stackp->ffs_filearray_size; i++) {
if (!path_with_url(stackp->ffs_filearray[i])
- && !os_isdir((char_u *)stackp->ffs_filearray[i])) {
+ && !os_isdir(stackp->ffs_filearray[i])) {
continue; // not a directory
}
// prepare the filename to be checked for existence below
- if (STRLEN(stackp->ffs_filearray[i]) + 1
- + STRLEN(search_ctx->ffsc_file_to_search) >= MAXPATHL) {
+ if (strlen(stackp->ffs_filearray[i]) + 1
+ + strlen(search_ctx->ffsc_file_to_search) >= MAXPATHL) {
ff_free_stack_element(stackp);
goto fail;
}
STRCPY(file_path, stackp->ffs_filearray[i]);
- if (!add_pathsep((char *)file_path)) {
+ if (!add_pathsep(file_path)) {
ff_free_stack_element(stackp);
goto fail;
}
STRCAT(file_path, search_ctx->ffsc_file_to_search);
- /*
- * Try without extra suffix and then with suffixes
- * from 'suffixesadd'.
- */
- len = STRLEN(file_path);
+ // Try without extra suffix and then with suffixes
+ // from 'suffixesadd'.
+ len = strlen(file_path);
if (search_ctx->ffsc_tagfile) {
suf = "";
} else {
- suf = (char *)curbuf->b_p_sua;
+ suf = curbuf->b_p_sua;
}
for (;;) {
// if file exists and we didn't already find it
- if ((path_with_url((char *)file_path)
+ if ((path_with_url(file_path)
|| (os_path_exists(file_path)
- && (search_ctx->ffsc_find_what
- == FINDFILE_BOTH
- || ((search_ctx->ffsc_find_what
- == FINDFILE_DIR)
+ && (search_ctx->ffsc_find_what == FINDFILE_BOTH
+ || ((search_ctx->ffsc_find_what == FINDFILE_DIR)
== os_isdir(file_path)))))
#ifndef FF_VERBOSE
&& (ff_check_visited(&search_ctx->ffsc_visited_list->ffvl_visited_list,
- file_path,
- (char_u *)""
- ) == OK)
+ file_path, "") == OK)
#endif
) {
#ifdef FF_VERBOSE
if (ff_check_visited(&search_ctx->ffsc_visited_list->ffvl_visited_list,
- file_path,
- (char_u *)""
- ) == FAIL) {
+ file_path, (char_u *)"") == FAIL) {
if (p_verbose >= 5) {
verbose_enter_scroll();
smsg("Already: %s", file_path);
@@ -865,13 +810,11 @@ char_u *vim_findfile(void *search_ctx_arg)
stackp->ffs_filearray_cur = i + 1;
ff_push(search_ctx, stackp);
- if (!path_with_url((char *)file_path)) {
+ if (!path_with_url(file_path)) {
simplify_filename(file_path);
}
- if (os_dirname(ff_expand_buffer, MAXPATHL)
- == OK) {
- p = path_shorten_fname(file_path,
- ff_expand_buffer);
+ if (os_dirname(ff_expand_buffer, MAXPATHL) == OK) {
+ p = path_shorten_fname(file_path, ff_expand_buffer);
if (p != NULL) {
STRMOVE(file_path, p);
}
@@ -892,17 +835,17 @@ char_u *vim_findfile(void *search_ctx_arg)
break;
}
assert(MAXPATHL >= len);
- copy_option_part(&suf, (char *)file_path + len, MAXPATHL - len, ",");
+ copy_option_part(&suf, file_path + len, MAXPATHL - len, ",");
}
}
} else {
// still wildcards left, push the directories for further search
for (int i = stackp->ffs_filearray_cur; i < stackp->ffs_filearray_size; i++) {
- if (!os_isdir((char_u *)stackp->ffs_filearray[i])) {
+ if (!os_isdir(stackp->ffs_filearray[i])) {
continue; // not a directory
}
ff_push(search_ctx,
- ff_create_stack_element((char_u *)stackp->ffs_filearray[i],
+ ff_create_stack_element(stackp->ffs_filearray[i],
rest_of_wildcards,
stackp->ffs_level - 1, 0));
}
@@ -911,22 +854,20 @@ char_u *vim_findfile(void *search_ctx_arg)
stackp->ffs_stage = 1;
}
- /*
- * if wildcards contains '**' we have to descent till we reach the
- * leaves of the directory tree.
- */
- if (STRNCMP(stackp->ffs_wc_path, "**", 2) == 0) {
+ // if wildcards contains '**' we have to descent till we reach the
+ // leaves of the directory tree.
+ 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],
- stackp->ffs_fix_path) == 0) {
+ if (path_fnamecmp(stackp->ffs_filearray[i],
+ stackp->ffs_fix_path) == 0) {
continue; // don't repush same directory
}
- if (!os_isdir((char_u *)stackp->ffs_filearray[i])) {
+ if (!os_isdir(stackp->ffs_filearray[i])) {
continue; // not a directory
}
ff_push(search_ctx,
- ff_create_stack_element((char_u *)stackp->ffs_filearray[i],
+ ff_create_stack_element(stackp->ffs_filearray[i],
stackp->ffs_wc_path, stackp->ffs_level - 1, 1));
}
}
@@ -935,9 +876,8 @@ char_u *vim_findfile(void *search_ctx_arg)
ff_free_stack_element(stackp);
}
- /* If we reached this, we didn't find anything downwards.
- * Let's check if we should do an upward search.
- */
+ // If we reached this, we didn't find anything downwards.
+ // Let's check if we should do an upward search.
if (search_ctx->ffsc_start_dir
&& search_ctx->ffsc_stopdirs_v != NULL && !got_int) {
ff_stack_T *sptr;
@@ -945,17 +885,15 @@ char_u *vim_findfile(void *search_ctx_arg)
// is the last starting directory in the stop list?
if (ff_path_in_stoplist(search_ctx->ffsc_start_dir,
(int)(path_end - search_ctx->ffsc_start_dir),
- search_ctx->ffsc_stopdirs_v) == TRUE) {
+ search_ctx->ffsc_stopdirs_v) == true) {
break;
}
// cut of last dir
- while (path_end > search_ctx->ffsc_start_dir
- && vim_ispathsep(*path_end)) {
+ while (path_end > search_ctx->ffsc_start_dir && vim_ispathsep(*path_end)) {
path_end--;
}
- while (path_end > search_ctx->ffsc_start_dir
- && !vim_ispathsep(path_end[-1])) {
+ while (path_end > search_ctx->ffsc_start_dir && !vim_ispathsep(path_end[-1])) {
path_end--;
}
*path_end = 0;
@@ -965,12 +903,12 @@ char_u *vim_findfile(void *search_ctx_arg)
break;
}
- if (STRLEN(search_ctx->ffsc_start_dir) + 1
- + STRLEN(search_ctx->ffsc_fix_path) >= MAXPATHL) {
+ if (strlen(search_ctx->ffsc_start_dir) + 1
+ + strlen(search_ctx->ffsc_fix_path) >= MAXPATHL) {
goto fail;
}
STRCPY(file_path, search_ctx->ffsc_start_dir);
- if (!add_pathsep((char *)file_path)) {
+ if (!add_pathsep(file_path)) {
goto fail;
}
STRCAT(file_path, search_ctx->ffsc_fix_path);
@@ -1034,7 +972,7 @@ static void ff_free_visited_list(ff_visited_T *vl)
/// @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,
+static ff_visited_list_hdr_T *ff_get_visited_list(char *filename,
ff_visited_list_hdr_T **list_headp)
{
ff_visited_list_hdr_T *retptr = NULL;
@@ -1043,7 +981,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 (path_fnamecmp(filename, retptr->ffvl_filename) == 0) {
#ifdef FF_VERBOSE
if (p_verbose >= 5) {
verbose_enter_scroll();
@@ -1067,13 +1005,11 @@ static ff_visited_list_hdr_T *ff_get_visited_list(char_u *filename,
}
#endif
- /*
- * if we reach this we didn't find a list and we have to allocate new list
- */
+ // if we reach this we didn't find a list and we have to allocate new list
retptr = xmalloc(sizeof(*retptr));
retptr->ffvl_visited_list = NULL;
- retptr->ffvl_filename = vim_strsave(filename);
+ retptr->ffvl_filename = xstrdup(filename);
retptr->ffvl_next = *list_headp;
*list_headp = retptr;
@@ -1087,7 +1023,7 @@ static ff_visited_list_hdr_T *ff_get_visited_list(char_u *filename,
/// - 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)
+static bool ff_wc_equal(char *s1, char *s2)
{
int i, j;
int c1 = NUL;
@@ -1104,8 +1040,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((char *)s1 + i);
- c2 = utf_ptr2char((char *)s2 + j);
+ c1 = utf_ptr2char(s1 + i);
+ c2 = utf_ptr2char(s2 + j);
if ((p_fic ? mb_tolower(c1) != mb_tolower(c2) : c1 != c2)
&& (prev1 != '*' || prev2 != '*')) {
@@ -1114,8 +1050,8 @@ static bool ff_wc_equal(char_u *s1, char_u *s2)
prev2 = prev1;
prev1 = c1;
- i += utfc_ptr2len((char *)s1 + i);
- j += utfc_ptr2len((char *)s2 + j);
+ i += utfc_ptr2len(s1 + i);
+ j += utfc_ptr2len(s2 + j);
}
return s1[i] == s2[j];
}
@@ -1124,7 +1060,7 @@ static bool ff_wc_equal(char_u *s1, char_u *s2)
///
/// @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)
+static int ff_check_visited(ff_visited_T **visited_list, char *fname, char *wc_path)
{
ff_visited_T *vp;
bool url = false;
@@ -1132,19 +1068,19 @@ static int ff_check_visited(ff_visited_T **visited_list, char_u *fname, char_u *
FileID file_id;
// For a URL we only compare the name, otherwise we compare the
// device/inode.
- if (path_with_url((char *)fname)) {
- STRLCPY(ff_expand_buffer, fname, MAXPATHL);
+ if (path_with_url(fname)) {
+ xstrlcpy(ff_expand_buffer, fname, MAXPATHL);
url = true;
} else {
ff_expand_buffer[0] = NUL;
- if (!os_fileid((char *)fname, &file_id)) {
+ if (!os_fileid(fname, &file_id)) {
return FAIL;
}
}
// 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 && path_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
@@ -1155,10 +1091,8 @@ static int ff_check_visited(ff_visited_T **visited_list, char_u *fname, char_u *
}
}
- /*
- * New file/dir. Add it to the list of visited files/dirs.
- */
- vp = xmalloc(sizeof(ff_visited_T) + STRLEN(ff_expand_buffer));
+ // New file/dir. Add it to the list of visited files/dirs.
+ vp = xmalloc(sizeof(ff_visited_T) + strlen(ff_expand_buffer));
if (!url) {
vp->file_id_valid = true;
@@ -1170,7 +1104,7 @@ static int ff_check_visited(ff_visited_T **visited_list, char_u *fname, char_u *
}
if (wc_path != NULL) {
- vp->ffv_wc_path = vim_strsave(wc_path);
+ vp->ffv_wc_path = xstrdup(wc_path);
} else {
vp->ffv_wc_path = NULL;
}
@@ -1182,7 +1116,7 @@ static int ff_check_visited(ff_visited_T **visited_list, char_u *fname, char_u *
}
/// 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,
+static ff_stack_T *ff_create_stack_element(char *fix_part, char *wc_part, int level,
int star_star_empty)
{
ff_stack_T *new = xmalloc(sizeof(ff_stack_T));
@@ -1197,14 +1131,14 @@ static ff_stack_T *ff_create_stack_element(char_u *fix_part, char_u *wc_part, in
// the following saves NULL pointer checks in vim_findfile
if (fix_part == NULL) {
- fix_part = (char_u *)"";
+ fix_part = "";
}
- new->ffs_fix_path = vim_strsave(fix_part);
+ new->ffs_fix_path = xstrdup(fix_part);
if (wc_part == NULL) {
- wc_part = (char_u *)"";
+ wc_part = "";
}
- new->ffs_wc_path = vim_strsave(wc_part);
+ new->ffs_wc_path = xstrdup(wc_part);
return new;
}
@@ -1212,12 +1146,14 @@ static ff_stack_T *ff_create_stack_element(char_u *fix_part, char_u *wc_part, in
/// 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
- * to prevent a crash */
- if (stack_ptr != NULL) {
- stack_ptr->ffs_prev = search_ctx->ffsc_stack_ptr;
- search_ctx->ffsc_stack_ptr = stack_ptr;
+ // check for NULL pointer, not to return an error to the user, but
+ // to prevent a crash
+ if (stack_ptr == NULL) {
+ return;
}
+
+ stack_ptr->ffs_prev = search_ctx->ffsc_stack_ptr;
+ search_ctx->ffsc_stack_ptr = stack_ptr;
}
/// Pop a dir from the directory stack.
@@ -1289,8 +1225,8 @@ static void ff_clear(ff_search_ctx_T *search_ctx)
/// 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)
+/// @return true if yes else false
+static int ff_path_in_stoplist(char *path, int path_len, char **stopdirs_v)
{
int i = 0;
@@ -1301,32 +1237,31 @@ static int ff_path_in_stoplist(char_u *path, int path_len, char_u **stopdirs_v)
// if no path consider it as match
if (path_len == 0) {
- return TRUE;
+ return true;
}
for (i = 0; stopdirs_v[i] != NULL; i++) {
- if ((int)STRLEN(stopdirs_v[i]) > path_len) {
- /* match for parent directory. So '/home' also matches
- * '/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 ((int)strlen(stopdirs_v[i]) > path_len) {
+ // match for parent directory. So '/home' also matches
+ // '/home/rks'. Check for PATHSEP in stopdirs_v[i], else
+ // '/home/r' would also match '/home/rks'
+ if (path_fnamencmp(stopdirs_v[i], path, (size_t)path_len) == 0
&& vim_ispathsep(stopdirs_v[i][path_len])) {
- return TRUE;
+ return true;
}
} else {
- if (FNAMECMP(stopdirs_v[i], path) == 0) {
+ if (path_fnamecmp(stopdirs_v[i], path) == 0) {
return true;
}
}
}
- return FALSE;
+ return false;
}
/// Find the file name "ptr[len]" in the path. Also finds directory names.
///
-/// On the first call set the parameter 'first' to TRUE to initialize
-/// the search. For repeating calls to FALSE.
+/// On the first call set the parameter 'first' to true to initialize
+/// the search. For repeating calls to false.
///
/// Repeating calls will return other files called 'ptr[len]' from the path.
///
@@ -1350,7 +1285,7 @@ static int ff_path_in_stoplist(char_u *path, int path_len, char_u **stopdirs_v)
/// @param rel_fname file name searching relative to
///
/// @return an allocated string for the file name. NULL for error.
-char_u *find_file_in_path(char_u *ptr, size_t len, int options, int first, char_u *rel_fname)
+char *find_file_in_path(char *ptr, size_t len, int options, int first, char *rel_fname)
{
return find_file_in_path_option(ptr, len, options, first,
(*curbuf->b_p_path == NUL
@@ -1359,7 +1294,7 @@ char_u *find_file_in_path(char_u *ptr, size_t len, int options, int first, char_
FINDFILE_BOTH, rel_fname, curbuf->b_p_sua);
}
-static char_u *ff_file_to_find = NULL;
+static char *ff_file_to_find = NULL;
static void *fdip_search_ctx = NULL;
#if defined(EXITFREE)
@@ -1385,10 +1320,10 @@ void free_findfile(void)
/// @param rel_fname file name searching relative to
///
/// @return an allocated string for the file name. NULL for error.
-char_u *find_directory_in_path(char_u *ptr, size_t len, int options, char_u *rel_fname)
+char *find_directory_in_path(char *ptr, size_t len, int options, char *rel_fname)
{
- return find_file_in_path_option(ptr, len, options, TRUE, p_cdpath,
- FINDFILE_DIR, rel_fname, (char_u *)"");
+ return find_file_in_path_option(ptr, len, options, true, p_cdpath,
+ FINDFILE_DIR, rel_fname, "");
}
/// @param ptr file name
@@ -1398,14 +1333,13 @@ char_u *find_directory_in_path(char_u *ptr, size_t len, int options, char_u *rel
/// @param find_what FINDFILE_FILE, _DIR or _BOTH
/// @param rel_fname file name we are looking relative to.
/// @param suffixes list of suffixes, 'suffixesadd' option
-char_u *find_file_in_path_option(char_u *ptr, size_t len, int options, int first,
- char_u *path_option, int find_what, char_u *rel_fname,
- char_u *suffixes)
+char *find_file_in_path_option(char *ptr, size_t len, int options, int first, char *path_option,
+ int find_what, char *rel_fname, char *suffixes)
{
static char *dir;
static int did_findfile_init = false;
- char_u save_char;
- char_u *file_name = NULL;
+ char save_char;
+ char *file_name = NULL;
char *buf = NULL;
int rel_to_curdir;
@@ -1426,12 +1360,12 @@ char_u *find_file_in_path_option(char_u *ptr, size_t len, int options, int first
ptr[len] = save_char;
xfree(ff_file_to_find);
- ff_file_to_find = vim_strsave(NameBuff);
+ ff_file_to_find = xstrdup(NameBuff);
if (options & FNAME_UNESC) {
// Change all "\ " to " ".
- for (ptr = ff_file_to_find; *ptr != NUL; ++ptr) {
+ for (ptr = ff_file_to_find; *ptr != NUL; ptr++) {
if (ptr[0] == '\\' && ptr[1] == ' ') {
- memmove(ptr, ptr + 1, STRLEN(ptr));
+ memmove(ptr, ptr + 1, strlen(ptr));
}
}
}
@@ -1446,71 +1380,65 @@ char_u *find_file_in_path_option(char_u *ptr, size_t len, int options, int first
if (vim_isAbsName(ff_file_to_find)
// "..", "../path", "." and "./path": don't use the path_option
|| rel_to_curdir
-#if defined(WIN32)
+#if defined(MSWIN)
// handle "\tmp" as absolute path
|| vim_ispathsep(ff_file_to_find[0])
// handle "c:name" as absolute path
|| (ff_file_to_find[0] != NUL && ff_file_to_find[1] == ':')
#endif
) {
- /*
- * Absolute path, no need to use "path_option".
- * If this is not a first call, return NULL. We already returned a
- * filename on the first call.
- */
- if (first == TRUE) {
- if (path_with_url((char *)ff_file_to_find)) {
- file_name = vim_strsave(ff_file_to_find);
+ // Absolute path, no need to use "path_option".
+ // If this is not a first call, return NULL. We already returned a
+ // filename on the first call.
+ if (first == true) {
+ if (path_with_url(ff_file_to_find)) {
+ file_name = xstrdup(ff_file_to_find);
goto theend;
}
- /* When FNAME_REL flag given first use the directory of the file.
- * Otherwise or when this fails use the current directory. */
- for (int run = 1; run <= 2; ++run) {
- size_t l = STRLEN(ff_file_to_find);
+ // When FNAME_REL flag given first use the directory of the file.
+ // Otherwise or when this fails use the current directory.
+ for (int run = 1; run <= 2; run++) {
+ size_t l = strlen(ff_file_to_find);
if (run == 1
&& rel_to_curdir
&& (options & FNAME_REL)
&& rel_fname != NULL
- && STRLEN(rel_fname) + l < MAXPATHL) {
+ && strlen(rel_fname) + l < MAXPATHL) {
STRCPY(NameBuff, rel_fname);
- STRCPY(path_tail((char *)NameBuff), ff_file_to_find);
- l = STRLEN(NameBuff);
+ STRCPY(path_tail(NameBuff), ff_file_to_find);
+ l = strlen(NameBuff);
} else {
STRCPY(NameBuff, ff_file_to_find);
run = 2;
}
- /* When the file doesn't exist, try adding parts of
- * 'suffixesadd'. */
- buf = (char *)suffixes;
+ // When the file doesn't exist, try adding parts of 'suffixesadd'.
+ buf = suffixes;
for (;;) {
- if (
- (os_path_exists(NameBuff)
+ if ((os_path_exists(NameBuff)
&& (find_what == FINDFILE_BOTH
|| ((find_what == FINDFILE_DIR)
== os_isdir(NameBuff))))) {
- file_name = vim_strsave(NameBuff);
+ file_name = xstrdup(NameBuff);
goto theend;
}
if (*buf == NUL) {
break;
}
assert(MAXPATHL >= l);
- copy_option_part(&buf, (char *)NameBuff + l, MAXPATHL - l, ",");
+ copy_option_part(&buf, NameBuff + l, MAXPATHL - l, ",");
}
}
}
} else {
- /*
- * Loop over all paths in the 'path' or 'cdpath' option.
- * When "first" is set, first setup to the start of the option.
- * Otherwise continue to find the next match.
- */
- if (first == TRUE) {
+ // Loop over all paths in the 'path' or 'cdpath' option.
+ // When "first" is set, first setup to the start of the option.
+ // Otherwise continue to find the next match.
+ if (first == true) {
// vim_findfile_free_visited can handle a possible NULL pointer
vim_findfile_free_visited(fdip_search_ctx);
- dir = (char *)path_option;
+ dir = path_option;
did_findfile_init = false;
}
@@ -1521,13 +1449,12 @@ char_u *find_file_in_path_option(char_u *ptr, size_t len, int options, int first
break;
}
- did_findfile_init = FALSE;
+ did_findfile_init = false;
} else {
- char_u *r_ptr;
+ char *r_ptr;
if (dir == NULL || *dir == NUL) {
- /* We searched all paths of the option, now we can
- * free the search context. */
+ // We searched all paths of the option, now we can free the search context.
vim_findfile_cleanup(fdip_search_ctx);
fdip_search_ctx = NULL;
break;
@@ -1540,19 +1467,19 @@ char_u *find_file_in_path_option(char_u *ptr, size_t len, int options, int first
copy_option_part(&dir, buf, MAXPATHL, " ,");
// get the stopdir string
- r_ptr = vim_findfile_stopdir((char_u *)buf);
- fdip_search_ctx = vim_findfile_init((char_u *)buf, ff_file_to_find,
+ r_ptr = vim_findfile_stopdir(buf);
+ fdip_search_ctx = vim_findfile_init(buf, ff_file_to_find,
r_ptr, 100, false, find_what,
fdip_search_ctx, false, rel_fname);
if (fdip_search_ctx != NULL) {
- did_findfile_init = TRUE;
+ did_findfile_init = true;
}
xfree(buf);
}
}
}
if (file_name == NULL && (options & FNAME_MESS)) {
- if (first == TRUE) {
+ if (first == true) {
if (find_what == FINDFILE_DIR) {
semsg(_("E344: Can't find directory \"%s\" in cdpath"),
ff_file_to_find);
@@ -1651,14 +1578,14 @@ int vim_chdirfile(char *fname, CdCause cause)
{
char dir[MAXPATHL];
- STRLCPY(dir, fname, MAXPATHL);
+ xstrlcpy(dir, fname, MAXPATHL);
*path_tail_with_sep(dir) = NUL;
if (os_dirname(NameBuff, sizeof(NameBuff)) != OK) {
NameBuff[0] = NUL;
}
- if (pathcmp(dir, (char *)NameBuff, -1) == 0) {
+ if (pathcmp(dir, NameBuff, -1) == 0) {
// nothing to do
return OK;
}
@@ -1679,10 +1606,10 @@ int vim_chdirfile(char *fname, CdCause cause)
}
/// Change directory to "new_dir". Search 'cdpath' for relative directory names.
-int vim_chdir(char_u *new_dir)
+int vim_chdir(char *new_dir)
{
- char *dir_name = (char *)find_directory_in_path(new_dir, STRLEN(new_dir),
- FNAME_MESS, (char_u *)curbuf->b_ffname);
+ char *dir_name = find_directory_in_path(new_dir, strlen(new_dir),
+ FNAME_MESS, curbuf->b_ffname);
if (dir_name == NULL) {
return -1;
}
diff --git a/src/nvim/file_search.h b/src/nvim/file_search.h
index 4d4e723922..b69a6fa154 100644
--- a/src/nvim/file_search.h
+++ b/src/nvim/file_search.h
@@ -1,10 +1,10 @@
#ifndef NVIM_FILE_SEARCH_H
#define NVIM_FILE_SEARCH_H
-#include <stdlib.h> // for size_t
+#include <stdlib.h>
-#include "nvim/globals.h" // for CdScope
-#include "nvim/types.h" // for char_u
+#include "nvim/globals.h"
+#include "nvim/types.h"
// Flags for find_file_*() functions.
#define FINDFILE_FILE 0 // only files
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 5dfcbb0668..9da9d6199e 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -6,62 +6,72 @@
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
+#include <iconv.h>
#include <inttypes.h>
+#include <limits.h>
#include <stdbool.h>
+#include <stdio.h>
#include <string.h>
+#include <sys/stat.h>
+#include <uv.h>
-#include "nvim/api/private/helpers.h"
+#include "auto/config.h"
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
#include "nvim/buffer_updates.h"
#include "nvim/change.h"
-#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/diff.h"
#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
-#include "nvim/eval/typval.h"
-#include "nvim/eval/userfunc.h"
#include "nvim/ex_cmds.h"
-#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
-#include "nvim/func_attr.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
-#include "nvim/hashtab.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/highlight_defs.h"
#include "nvim/iconv.h"
#include "nvim/input.h"
+#include "nvim/log.h"
+#include "nvim/macros.h"
#include "nvim/mbyte.h"
#include "nvim/memfile.h"
#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/optionstr.h"
+#include "nvim/os/fs_defs.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
-#include "nvim/os/os_defs.h"
#include "nvim/os/time.h"
-#include "nvim/os_unix.h"
#include "nvim/path.h"
-#include "nvim/quickfix.h"
+#include "nvim/pos.h"
#include "nvim/regexp.h"
-#include "nvim/search.h"
+#include "nvim/screen.h"
#include "nvim/sha256.h"
#include "nvim/shada.h"
-#include "nvim/state.h"
#include "nvim/strings.h"
#include "nvim/types.h"
#include "nvim/ui.h"
-#include "nvim/ui_compositor.h"
#include "nvim/undo.h"
#include "nvim/vim.h"
-#include "nvim/window.h"
+
+#if defined(HAVE_FLOCK) && defined(HAVE_DIRFD)
+# include <dirent.h>
+# include <sys/file.h>
+#endif
+
+#ifdef OPEN_CHR_FILES
+# include "nvim/charset.h"
+#endif
#define BUFSIZE 8192 // size of normal write buffer
#define SMBUFSIZE 256 // size of emergency write buffer
@@ -72,30 +82,30 @@
#endif
#define HAS_BW_FLAGS
-#define FIO_LATIN1 0x01 // convert Latin1
-#define FIO_UTF8 0x02 // convert UTF-8
-#define FIO_UCS2 0x04 // convert UCS-2
-#define FIO_UCS4 0x08 // convert UCS-4
-#define FIO_UTF16 0x10 // convert UTF-16
-#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
-
-/* When converting, a read() or write() may leave some bytes to be converted
- * for the next call. The value is guessed... */
+enum {
+ FIO_LATIN1 = 0x01, // convert Latin1
+ FIO_UTF8 = 0x02, // convert UTF-8
+ FIO_UCS2 = 0x04, // convert UCS-2
+ FIO_UCS4 = 0x08, // convert UCS-4
+ FIO_UTF16 = 0x10, // convert UTF-16
+ FIO_ENDIAN_L = 0x80, // little endian
+ FIO_NOCONVERT = 0x2000, // skip encoding conversion
+ FIO_UCSBOM = 0x4000, // check for BOM at start of file
+ 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...
#define CONV_RESTLEN 30
-/* We have to guess how much a sequence of bytes may expand when converting
- * with iconv() to be able to allocate a buffer. */
+// We have to guess how much a sequence of bytes may expand when converting
+// with iconv() to be able to allocate a buffer.
#define ICONV_MULT 8
-/*
- * Structure to pass arguments from buf_write() to buf_write_bytes().
- */
+// Structure to pass arguments from buf_write() to buf_write_bytes().
struct bw_info {
int bw_fd; // file descriptor
- char_u *bw_buf; // buffer with data to be written
+ char *bw_buf; // buffer with data to be written
int bw_len; // length of data
#ifdef HAS_BW_FLAGS
int bw_flags; // FIO_ flags
@@ -103,14 +113,12 @@ struct bw_info {
char_u bw_rest[CONV_RESTLEN]; // not converted bytes
int bw_restlen; // nr of bytes in bw_rest[]
int bw_first; // first write call
- char_u *bw_conv_buf; // buffer for writing converted chars
+ char *bw_conv_buf; // buffer for writing converted chars
size_t bw_conv_buflen; // size of bw_conv_buf
int bw_conv_error; // set for conversion error
linenr_T bw_conv_error_lnum; // first line with error or zero
linenr_T bw_start_lnum; // line number at start of buffer
-#ifdef HAVE_ICONV
iconv_t bw_iconv_fd; // descriptor for iconv() or -1
-#endif
};
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -118,23 +126,25 @@ struct bw_info {
#endif
static char *e_auchangedbuf = N_("E812: Autocommands changed buffer or buffer name");
+static char e_no_matching_autocommands_for_buftype_str_buffer[]
+ = N_("E676: No matching autocommands for buftype=%s buffer");
-void filemess(buf_T *buf, char_u *name, char_u *s, int attr)
+void filemess(buf_T *buf, char *name, char *s, int attr)
{
int msg_scroll_save;
if (msg_silent != 0) {
return;
}
- add_quoted_fname((char *)IObuff, IOSIZE - 100, buf, (const char *)name);
+ add_quoted_fname(IObuff, IOSIZE - 100, buf, (const char *)name);
// Avoid an over-long translation to cause trouble.
- STRLCAT(IObuff, s, IOSIZE);
+ xstrlcat(IObuff, s, IOSIZE);
// For the first message may have to start a new line.
// For further ones overwrite the previous one, reset msg_scroll before
// calling filemess().
msg_scroll_save = msg_scroll;
if (shortmess(SHM_OVERALL) && !exiting && p_verbose == 0) {
- msg_scroll = FALSE;
+ msg_scroll = false;
}
if (!msg_scroll) { // wait a bit when overwriting an error msg
check_for_delay(false);
@@ -143,7 +153,7 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr)
msg_scroll = msg_scroll_save;
msg_scrolled_ign = true;
// may truncate the message to avoid a hit-return prompt
- msg_outtrans_attr(msg_may_trunc(FALSE, IObuff), attr);
+ msg_outtrans_attr(msg_may_trunc(false, IObuff), attr);
msg_clr_eos();
ui_flush();
msg_scrolled_ign = false;
@@ -167,6 +177,7 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr)
/// READ_STDIN read from stdin instead of a file
/// READ_BUFFER read from curbuf instead of a file (converting after reading
/// stdin)
+/// READ_NOFILE do not read a file, only trigger BufReadCmd
/// READ_DUMMY read into a dummy buffer (to check if file contents changed)
/// READ_KEEP_UNDO don't clear undo info or read it from a file
/// READ_FIFO read from fifo/socket instead of a file
@@ -235,11 +246,9 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
char *fenc_next = NULL; // next item in 'fencs' or NULL
bool advance_fenc = false;
long real_size = 0;
-#ifdef HAVE_ICONV
iconv_t iconv_fd = (iconv_t)-1; // descriptor for iconv() or -1
bool did_iconv = false; // true when iconv() failed and trying
// 'charconvert' next
-#endif
bool converted = false; // true if conversion done
bool notconverted = false; // true if conversion wanted but it wasn't possible
char conv_rest[CONV_RESTLEN];
@@ -256,18 +265,16 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
curbuf->b_no_eol_lnum = 0; // in case it was set by the previous read
- /*
- * If there is no file name yet, use the one for the read file.
- * BF_NOTEDITED is set to reflect this.
- * Don't do this for a read from a filter.
- * Only do this when 'cpoptions' contains the 'f' flag.
- */
+ // If there is no file name yet, use the one for the read file.
+ // BF_NOTEDITED is set to reflect this.
+ // Don't do this for a read from a filter.
+ // Only do this when 'cpoptions' contains the 'f' flag.
if (curbuf->b_ffname == NULL
&& !filtering
&& fname != NULL
&& vim_strchr(p_cpo, CPO_FNAMER) != NULL
&& !(flags & READ_DUMMY)) {
- if (set_rw_fname((char_u *)fname, (char_u *)sfname) == FAIL) {
+ if (set_rw_fname(fname, sfname) == FAIL) {
return FAIL;
}
}
@@ -334,20 +341,26 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
}
curbuf->b_op_start = orig_start;
+
+ if (flags & READ_NOFILE) {
+ // Return NOTDONE instead of FAIL so that BufEnter can be triggered
+ // and other operations don't fail.
+ return NOTDONE;
+ }
}
if ((shortmess(SHM_OVER) || curbuf->b_help) && p_verbose == 0) {
- msg_scroll = FALSE; // overwrite previous file message
+ msg_scroll = false; // overwrite previous file message
} else {
- msg_scroll = TRUE; // don't overwrite previous file message
+ msg_scroll = true; // don't overwrite previous file message
}
// If the name is too long we might crash further on, quit here.
if (fname != NULL && *fname != NUL) {
- size_t namelen = STRLEN(fname);
+ size_t namelen = strlen(fname);
// If the name is too long we might crash further on, quit here.
if (namelen >= MAXPATHL) {
- filemess(curbuf, (char_u *)fname, (char_u *)_("Illegal file name"), 0);
+ filemess(curbuf, fname, _("Illegal file name"), 0);
msg_end();
msg_scroll = msg_save;
return FAIL;
@@ -358,7 +371,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
// swap file may destroy it! Reported on MS-DOS and Win 95.
if (after_pathsep(fname, fname + namelen)) {
if (!silent) {
- filemess(curbuf, (char_u *)fname, (char_u *)_(msg_is_a_directory), 0);
+ filemess(curbuf, fname, _(msg_is_a_directory), 0);
}
msg_end();
msg_scroll = msg_save;
@@ -380,10 +393,10 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
) {
if (S_ISDIR(perm)) {
if (!silent) {
- filemess(curbuf, (char_u *)fname, (char_u *)_(msg_is_a_directory), 0);
+ filemess(curbuf, fname, _(msg_is_a_directory), 0);
}
} else {
- filemess(curbuf, (char_u *)fname, (char_u *)_("is not a file"), 0);
+ filemess(curbuf, fname, _("is not a file"), 0);
}
msg_end();
msg_scroll = msg_save;
@@ -394,15 +407,13 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
// Set default or forced 'fileformat' and 'binary'.
set_file_options(set_options, eap);
- /*
- * When opening a new file we take the readonly flag from the file.
- * Default is r/w, can be set to r/o below.
- * Don't reset it when in readonly mode
- * Only set/reset b_p_ro when BF_CHECK_RO is set.
- */
+ // When opening a new file we take the readonly flag from the file.
+ // Default is r/w, can be set to r/o below.
+ // Don't reset it when in readonly mode
+ // Only set/reset b_p_ro when BF_CHECK_RO is set.
check_readonly = (newfile && (curbuf->b_flags & BF_CHECK_RO));
if (check_readonly && !readonlymode) {
- curbuf->b_p_ro = FALSE;
+ curbuf->b_p_ro = false;
}
if (newfile && !read_stdin && !read_buffer && !read_fifo) {
@@ -412,17 +423,15 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
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.
- * This makes it possible for others to read the name of the
- * edited file from the swapfile, but only if they can read the
- * edited file.
- * Remove the "write" and "execute" bits for group and others
- * (they must not write the swapfile).
- * Add the "read" and "write" bits for the user, otherwise we may
- * not be able to write to the file ourselves.
- * Setting the bits is done below, after creating the swap file.
- */
+ // Use the protection bits of the original file for the swap file.
+ // This makes it possible for others to read the name of the
+ // edited file from the swapfile, but only if they can read the
+ // edited file.
+ // Remove the "write" and "execute" bits for group and others
+ // (they must not write the swapfile).
+ // Add the "read" and "write" bits for the user, otherwise we may
+ // not be able to write to the file ourselves.
+ // Setting the bits is done below, after creating the swap file.
swap_mode = ((int)file_info.stat.st_mode & 0644) | 0600;
#endif
} else {
@@ -475,10 +484,10 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
}
}
if (!silent) {
- if (dir_of_file_exists((char_u *)fname)) {
- filemess(curbuf, (char_u *)sfname, (char_u *)new_file_message(), 0);
+ if (dir_of_file_exists(fname)) {
+ filemess(curbuf, sfname, new_file_message(), 0);
} else {
- filemess(curbuf, (char_u *)sfname, (char_u *)_("[New DIRECTORY]"), 0);
+ filemess(curbuf, sfname, _("[New DIRECTORY]"), 0);
}
}
// Even though this is a new file, it might have been
@@ -497,39 +506,38 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
return FAIL;
}
return OK; // a new file is not an error
- } else {
- filemess(curbuf, (char_u *)sfname, (char_u *)((fd == UV_EFBIG) ? _("[File too big]") :
+ }
+ filemess(curbuf, sfname, ((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;
}
- /*
- * Only set the 'ro' flag for readonly files the first time they are
- * loaded. Help files always get readonly mode
- */
+ // Only set the 'ro' flag for readonly files the first time they are
+ // loaded. Help files always get readonly mode
if ((check_readonly && file_readonly) || curbuf->b_help) {
- curbuf->b_p_ro = TRUE;
+ curbuf->b_p_ro = true;
}
if (set_options) {
// Don't change 'eol' if reading from buffer as it will already be
// correctly set when reading stdin.
if (!read_buffer) {
- curbuf->b_p_eol = TRUE;
- curbuf->b_start_eol = TRUE;
+ curbuf->b_p_eof = false;
+ curbuf->b_start_eof = false;
+ curbuf->b_p_eol = true;
+ curbuf->b_start_eol = true;
}
- curbuf->b_p_bomb = FALSE;
- curbuf->b_start_bomb = FALSE;
+ curbuf->b_p_bomb = false;
+ curbuf->b_start_bomb = false;
}
// Create a swap file now, so that other Vims are warned that we are
@@ -583,7 +591,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
return FAIL;
}
- ++no_wait_return; // don't wait for return yet
+ no_wait_return++; // don't wait for return yet
// Set '[ mark to the line above where the lines go (line 1 if zero).
orig_start = curbuf->b_op_start;
@@ -635,16 +643,14 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
if (aborting()) { // autocmds may abort script processing
no_wait_return--;
msg_scroll = msg_save;
- curbuf->b_p_ro = TRUE; // must use "w!" now
+ curbuf->b_p_ro = true; // must use "w!" now
return FAIL;
}
- /*
- * Don't allow the autocommands to change the current buffer.
- * Try to re-open the file.
- *
- * Don't allow the autocommands to change the buffer name either
- * (cd for example) if it invalidates fname or sfname.
- */
+ // Don't allow the autocommands to change the current buffer.
+ // Try to re-open the file.
+ //
+ // Don't allow the autocommands to change the buffer name either
+ // (cd for example) if it invalidates fname or sfname.
if (!read_stdin && (curbuf != old_curbuf
|| (using_b_ffname && (old_b_ffname != curbuf->b_ffname))
|| (using_b_fname && (old_b_fname != curbuf->b_fname))
@@ -656,7 +662,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
} else {
emsg(_("E201: *ReadPre autocommands must not change current buffer"));
}
- curbuf->b_p_ro = TRUE; // must use "w!" now
+ curbuf->b_p_ro = true; // must use "w!" now
return FAIL;
}
}
@@ -666,16 +672,14 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
if (!recoverymode && !filtering && !(flags & READ_DUMMY) && !silent) {
if (!read_stdin && !read_buffer) {
- filemess(curbuf, (char_u *)sfname, (char_u *)"", 0);
+ filemess(curbuf, sfname, "", 0);
}
}
- msg_scroll = FALSE; // overwrite the file message
+ msg_scroll = false; // overwrite the file message
- /*
- * Set linecnt now, before the "retry" caused by a wrong guess for
- * fileformat, and after the autocommands, which may change them.
- */
+ // Set linecnt now, before the "retry" caused by a wrong guess for
+ // fileformat, and after the autocommands, which may change them.
linecnt = curbuf->b_ml.ml_line_count;
// "++bad=" argument.
@@ -688,11 +692,9 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
curbuf->b_bad_char = 0;
}
- /*
- * Decide which 'encoding' to use or use first.
- */
+ // Decide which 'encoding' to use or use first.
if (eap != NULL && eap->force_enc != 0) {
- fenc = (char *)enc_canonize((char_u *)eap->cmd + eap->force_enc);
+ fenc = enc_canonize(eap->cmd + eap->force_enc);
fenc_alloced = true;
keep_dest_enc = true;
} else if (curbuf->b_p_bin) {
@@ -708,32 +710,30 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
fenc_alloced = false;
} else if (*p_fencs == NUL) {
- fenc = (char *)curbuf->b_p_fenc; // use format from buffer
+ fenc = curbuf->b_p_fenc; // use format from buffer
fenc_alloced = false;
} else {
- fenc_next = (char *)p_fencs; // try items in 'fileencodings'
- fenc = (char *)next_fenc(&fenc_next, &fenc_alloced);
- }
-
- /*
- * Jump back here to retry reading the file in different ways.
- * Reasons to retry:
- * - encoding conversion failed: try another one from "fenc_next"
- * - BOM detected and fenc was set, need to setup conversion
- * - "fileformat" check failed: try another
- *
- * Variables set for special retry actions:
- * "file_rewind" Rewind the file to start reading it again.
- * "advance_fenc" Advance "fenc" using "fenc_next".
- * "skip_read" Re-use already read bytes (BOM detected).
- * "did_iconv" iconv() conversion failed, try 'charconvert'.
- * "keep_fileformat" Don't reset "fileformat".
- *
- * Other status indicators:
- * "tmpname" When != NULL did conversion with 'charconvert'.
- * Output file has to be deleted afterwards.
- * "iconv_fd" When != -1 did conversion with iconv().
- */
+ fenc_next = p_fencs; // try items in 'fileencodings'
+ fenc = next_fenc(&fenc_next, &fenc_alloced);
+ }
+
+ // Jump back here to retry reading the file in different ways.
+ // Reasons to retry:
+ // - encoding conversion failed: try another one from "fenc_next"
+ // - BOM detected and fenc was set, need to setup conversion
+ // - "fileformat" check failed: try another
+ //
+ // Variables set for special retry actions:
+ // "file_rewind" Rewind the file to start reading it again.
+ // "advance_fenc" Advance "fenc" using "fenc_next".
+ // "skip_read" Re-use already read bytes (BOM detected).
+ // "did_iconv" iconv() conversion failed, try 'charconvert'.
+ // "keep_fileformat" Don't reset "fileformat".
+ //
+ // Other status indicators:
+ // "tmpname" When != NULL did conversion with 'charconvert'.
+ // Output file has to be deleted afterwards.
+ // "iconv_fd" When != -1 did conversion with iconv().
retry:
if (file_rewind) {
@@ -751,22 +751,20 @@ retry:
}
file_rewind = false;
if (set_options) {
- curbuf->b_p_bomb = FALSE;
- curbuf->b_start_bomb = FALSE;
+ curbuf->b_p_bomb = false;
+ curbuf->b_start_bomb = false;
}
conv_error = 0;
}
- /*
- * When retrying with another "fenc" and the first time "fileformat"
- * will be reset.
- */
+ // When retrying with another "fenc" and the first time "fileformat"
+ // will be reset.
if (keep_fileformat) {
keep_fileformat = false;
} else {
if (eap != NULL && eap->force_ff != 0) {
fileformat = get_fileformat_force(curbuf, eap);
- try_unix = try_dos = try_mac = FALSE;
+ try_unix = try_dos = try_mac = false;
} else if (curbuf->b_p_bin) {
fileformat = EOL_UNIX; // binary: use Unix format
} else if (*p_ffs ==
@@ -777,18 +775,14 @@ retry:
}
}
-#ifdef HAVE_ICONV
if (iconv_fd != (iconv_t)-1) {
// aborted conversion with iconv(), close the descriptor
iconv_close(iconv_fd);
iconv_fd = (iconv_t)-1;
}
-#endif
if (advance_fenc) {
- /*
- * Try the next entry in 'fileencodings'.
- */
+ // Try the next entry in 'fileencodings'.
advance_fenc = false;
if (eap != NULL && eap->force_enc != 0) {
@@ -806,7 +800,7 @@ retry:
xfree(fenc);
}
if (fenc_next != NULL) {
- fenc = (char *)next_fenc(&fenc_next, &fenc_alloced);
+ fenc = next_fenc(&fenc_next, &fenc_alloced);
} else {
fenc = "";
fenc_alloced = false;
@@ -818,16 +812,14 @@ retry:
}
}
- /*
- * Conversion may be required when the encoding of the file is different
- * from 'encoding' or 'encoding' is UTF-16, UCS-2 or UCS-4.
- */
+ // Conversion may be required when the encoding of the file is different
+ // from 'encoding' or 'encoding' is UTF-16, UCS-2 or UCS-4.
fio_flags = 0;
- converted = need_conversion((char_u *)fenc);
+ converted = need_conversion(fenc);
if (converted) {
// "ucs-bom" means we need to check the first bytes of the file
// for a BOM.
- if (STRCMP(fenc, ENC_UCSBOM) == 0) {
+ if (strcmp(fenc, ENC_UCSBOM) == 0) {
fio_flags = FIO_UCSBOM;
} else {
// Check if UCS-2/4 or Latin1 to UTF-8 conversion needs to be
@@ -837,34 +829,24 @@ 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((char_u *)fenc);
+ fio_flags = get_fio_flags(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", (char_u *)fenc);
+ iconv_fd = (iconv_t)my_iconv_open("utf-8", fenc);
}
-#endif
- /*
- * Use the 'charconvert' expression when conversion is required
- * and we can't do it internally or with iconv().
- */
+ // Use the 'charconvert' expression when conversion is required
+ // and we can't do it internally or with iconv().
if (fio_flags == 0 && !read_stdin && !read_buffer && *p_ccv != NUL
- && !read_fifo
-#ifdef HAVE_ICONV
- && iconv_fd == (iconv_t)-1
-#endif
- ) {
-#ifdef HAVE_ICONV
+ && !read_fifo && iconv_fd == (iconv_t)-1) {
did_iconv = false;
-#endif
// Skip conversion when it's already done (retry for wrong
// "fileformat").
if (tmpname == NULL) {
- tmpname = (char *)readfile_charconvert((char_u *)fname, (char_u *)fenc, &fd);
+ tmpname = readfile_charconvert(fname, fenc, &fd);
if (tmpname == NULL) {
// Conversion failed. Try another one.
advance_fenc = true;
@@ -878,11 +860,7 @@ retry:
}
}
} else {
- if (fio_flags == 0
-#ifdef HAVE_ICONV
- && iconv_fd == (iconv_t)-1
-#endif
- ) {
+ if (fio_flags == 0 && iconv_fd == (iconv_t)-1) {
// Conversion wanted but we can't.
// Try the next conversion in 'fileencodings'
advance_fenc = true;
@@ -892,7 +870,7 @@ retry:
}
// Set "can_retry" when it's possible to rewind the file and try with
- // another "fenc" value. It's FALSE when no other "fenc" to try, reading
+ // another "fenc" value. It's false when no other "fenc" to try, reading
// stdin or fixed at a specific encoding.
can_retry = (*fenc != NUL && !read_stdin && !keep_dest_enc && !read_fifo);
@@ -915,12 +893,10 @@ retry:
}
while (!error && !got_int) {
- /*
- * We allocate as much space for the file as we can get, plus
- * space for the old line plus room for one terminating NUL.
- * The amount is limited by the fact that read() only can read
- * up to max_unsigned characters (and other things).
- */
+ // We allocate as much space for the file as we can get, plus
+ // space for the old line plus room for one terminating NUL.
+ // The amount is limited by the fact that read() only can read
+ // up to max_unsigned characters (and other things).
{
if (!skip_read) {
// Use buffer >= 64K. Add linerest to double the size if the
@@ -967,12 +943,9 @@ retry:
// ucs-4 to utf-8: 4 bytes become up to 6 bytes, size must be
// multiple of 4
real_size = (int)size;
-#ifdef HAVE_ICONV
if (iconv_fd != (iconv_t)-1) {
size = size / ICONV_MULT;
- } else {
-#endif
- if (fio_flags & FIO_LATIN1) {
+ } else if (fio_flags & FIO_LATIN1) {
size = size / 2;
} else if (fio_flags & (FIO_UCS2 | FIO_UTF16)) {
size = (size * 2 / 3) & ~1;
@@ -981,9 +954,7 @@ retry:
} else if (fio_flags == FIO_UCSBOM) {
size = size / ICONV_MULT; // worst case
}
-#ifdef HAVE_ICONV
- }
-#endif
+
if (conv_restlen > 0) {
// Insert unconverted bytes from previous line.
memmove(ptr, conv_rest, (size_t)conv_restlen); // -V614
@@ -992,10 +963,8 @@ retry:
}
if (read_buffer) {
- /*
- * Read bytes from curbuf. Used for converting text read
- * from stdin.
- */
+ // Read bytes from curbuf. Used for converting text read
+ // from stdin.
if (read_buf_lnum > from) {
size = 0;
} else {
@@ -1004,14 +973,14 @@ retry:
tlen = 0;
for (;;) {
- p = ml_get(read_buf_lnum) + read_buf_col;
- n = (int)STRLEN(p);
+ p = (char_u *)ml_get(read_buf_lnum) + read_buf_col;
+ n = (int)strlen((char *)p);
if ((int)tlen + n + 1 > size) {
// Filled up to "size", append partial line.
// Change NL to NUL to reverse the effect done
// below.
n = (int)(size - tlen);
- for (ni = 0; ni < n; ++ni) {
+ for (ni = 0; ni < n; ni++) {
if (p[ni] == NL) {
ptr[tlen++] = NUL;
} else {
@@ -1045,9 +1014,7 @@ retry:
}
}
} else {
- /*
- * Read bytes from the file.
- */
+ // Read bytes from the file.
size = read_eintr(fd, ptr, (size_t)size);
}
@@ -1055,17 +1022,11 @@ retry:
if (size < 0) { // read error
error = true;
} else if (conv_restlen > 0) {
- /*
- * Reached end-of-file but some trailing bytes could
- * not be converted. Truncated file?
- */
+ // Reached end-of-file but some trailing bytes could
+ // not be converted. Truncated file?
// When we did a conversion report an error.
- if (fio_flags != 0
-#ifdef HAVE_ICONV
- || iconv_fd != (iconv_t)-1
-#endif
- ) {
+ if (fio_flags != 0 || iconv_fd != (iconv_t)-1) {
if (can_retry) {
goto rewind_retry;
}
@@ -1073,9 +1034,8 @@ retry:
conv_error = curbuf->b_ml.ml_line_count
- linecnt + 1;
}
- }
- // Remember the first linenr with an illegal byte
- else if (illegal_byte == 0) {
+ } else if (illegal_byte == 0) {
+ // Remember the first linenr with an illegal byte
illegal_byte = curbuf->b_ml.ml_line_count
- linecnt + 1;
}
@@ -1087,23 +1047,17 @@ retry:
// character if we were converting; if we weren't,
// leave the UTF8 checking code to do it, as it
// works slightly differently.
- if (bad_char_behavior != BAD_KEEP && (fio_flags != 0
-#ifdef HAVE_ICONV
- || iconv_fd != (iconv_t)-1
-#endif
- )) {
+ if (bad_char_behavior != BAD_KEEP && (fio_flags != 0 || iconv_fd != (iconv_t)-1)) {
while (conv_restlen > 0) {
*(--ptr) = (char)bad_char_behavior;
conv_restlen--;
}
}
fio_flags = 0; // don't convert this
-#ifdef HAVE_ICONV
if (iconv_fd != (iconv_t)-1) {
iconv_close(iconv_fd);
iconv_fd = (iconv_t)-1;
}
-#endif
}
}
}
@@ -1111,26 +1065,24 @@ retry:
skip_read = false;
- /*
- * At start of file: Check for BOM.
- * Also check for a BOM for other Unicode encodings, but not after
- * converting with 'charconvert' or when a BOM has already been
- * found.
- */
+ // At start of file: Check for BOM.
+ // Also check for a BOM for other Unicode encodings, but not after
+ // converting with 'charconvert' or when a BOM has already been
+ // found.
if ((filesize == 0)
&& (fio_flags == FIO_UCSBOM
|| (!curbuf->b_p_bomb
&& tmpname == NULL
&& (*fenc == 'u' || *fenc == NUL)))) {
- char_u *ccname;
+ char *ccname;
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((char_u *)ptr, size, &blen,
- fio_flags == FIO_UCSBOM ? FIO_ALL : get_fio_flags((char_u *)fenc));
+ ccname = check_for_bom(ptr, size, &blen,
+ fio_flags == FIO_UCSBOM ? FIO_ALL : get_fio_flags(fenc));
}
if (ccname != NULL) {
// Remove BOM from the text
@@ -1138,8 +1090,8 @@ retry:
size -= blen;
memmove(ptr, ptr + blen, (size_t)size);
if (set_options) {
- curbuf->b_p_bomb = TRUE;
- curbuf->b_start_bomb = TRUE;
+ curbuf->b_p_bomb = true;
+ curbuf->b_start_bomb = true;
}
}
@@ -1152,7 +1104,7 @@ retry:
if (fenc_alloced) {
xfree(fenc);
}
- fenc = (char *)ccname;
+ fenc = ccname;
fenc_alloced = false;
}
// retry reading without getting new bytes or rewinding
@@ -1165,14 +1117,11 @@ retry:
ptr -= conv_restlen;
size += conv_restlen;
conv_restlen = 0;
- /*
- * Break here for a read error or end-of-file.
- */
+ // Break here for a read error or end-of-file.
if (size <= 0) {
break;
}
-#ifdef HAVE_ICONV
if (iconv_fd != (iconv_t)-1) {
// Attempt conversion of the read bytes to 'encoding' using iconv().
const char *fromp = ptr;
@@ -1181,11 +1130,9 @@ retry:
char *top = ptr;
size_t to_size = (size_t)(real_size - size);
- /*
- * If there is conversion error or not enough room try using
- * another conversion. Except for when there is no
- * alternative (help files).
- */
+ // If there is conversion error or not enough room try using
+ // another conversion. Except for when there is no
+ // alternative (help files).
while ((iconv(iconv_fd, (void *)&fromp, &from_size,
&top, &to_size)
== (size_t)-1 && ICONV_ERRNO != ICONV_EINVAL)
@@ -1194,7 +1141,7 @@ retry:
goto rewind_retry;
}
if (conv_error == 0) {
- conv_error = readfile_linenr(linecnt, (char_u *)ptr, (char_u *)top);
+ conv_error = readfile_linenr(linecnt, ptr, top);
}
// Deal with a bad byte and continue with the next.
@@ -1212,7 +1159,7 @@ retry:
if (from_size > 0) {
// Some remaining characters, keep them for the next
// round.
- memmove(conv_rest, (char_u *)fromp, from_size);
+ memmove(conv_rest, fromp, from_size);
conv_restlen = (int)from_size;
}
@@ -1221,7 +1168,6 @@ retry:
memmove(line_start, buffer, (size_t)linerest);
size = (top - ptr);
}
-#endif
if (fio_flags != 0) {
unsigned int u8c;
@@ -1306,7 +1252,7 @@ retry:
goto rewind_retry;
}
if (conv_error == 0) {
- conv_error = readfile_linenr(linecnt, (char_u *)ptr, p);
+ conv_error = readfile_linenr(linecnt, ptr, (char *)p);
}
if (bad_char_behavior == BAD_DROP) {
continue;
@@ -1334,7 +1280,7 @@ retry:
goto rewind_retry;
}
if (conv_error == 0) {
- conv_error = readfile_linenr(linecnt, (char_u *)ptr, p);
+ conv_error = readfile_linenr(linecnt, ptr, (char *)p);
}
if (bad_char_behavior == BAD_DROP) {
continue;
@@ -1364,7 +1310,7 @@ retry:
if (*--p < 0x80) {
u8c = *p;
} else {
- len = utf_head_off((char_u *)ptr, p);
+ len = utf_head_off(ptr, (char *)p);
p -= len;
u8c = (unsigned)utf_ptr2char((char *)p);
if (len == 0) {
@@ -1375,7 +1321,7 @@ retry:
goto rewind_retry;
}
if (conv_error == 0) {
- conv_error = readfile_linenr(linecnt, (char_u *)ptr, p);
+ conv_error = readfile_linenr(linecnt, ptr, (char *)p);
}
if (bad_char_behavior == BAD_DROP) {
continue;
@@ -1413,7 +1359,7 @@ retry:
// an incomplete character at the end though, the next
// read() will get the next bytes, we'll check it
// then.
- l = utf_ptr2len_len(p, todo);
+ l = utf_ptr2len_len((char *)p, todo);
if (l > todo && !incomplete_tail) {
// Avoid retrying with a different encoding when
// a truncated file is more likely, or attempting
@@ -1439,15 +1385,15 @@ retry:
if (can_retry && !incomplete_tail) {
break;
}
-#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, (char_u *)ptr, p);
+ conv_error = readfile_linenr(linecnt, ptr, (char *)p);
}
-#endif
+
// Remember the first linenr with an illegal byte
if (conv_error == 0 && illegal_byte == 0) {
- illegal_byte = readfile_linenr(linecnt, (char_u *)ptr, p);
+ illegal_byte = readfile_linenr(linecnt, ptr, (char *)p);
}
// Drop, keep or replace the bad byte.
@@ -1467,17 +1413,13 @@ retry:
// Detected a UTF-8 error.
rewind_retry:
// Retry reading with another conversion.
-#ifdef HAVE_ICONV
if (*p_ccv != NUL && iconv_fd != (iconv_t)-1) {
// iconv() failed, try 'charconvert'
did_iconv = true;
} else {
-#endif
- // use next item from 'fileencodings'
- advance_fenc = true;
-#ifdef HAVE_ICONV
- }
-#endif
+ // use next item from 'fileencodings'
+ advance_fenc = true;
+ }
file_rewind = true;
goto retry;
}
@@ -1486,9 +1428,7 @@ rewind_retry:
// count the number of characters (after conversion!)
filesize += size;
- /*
- * when reading the first part of a file: guess EOL type
- */
+ // when reading the first part of a file: guess EOL type
if (fileformat == EOL_UNKNOWN) {
// First try finding a NL, for Dos and Unix
if (try_dos || try_unix) {
@@ -1553,10 +1493,8 @@ rewind_retry:
}
}
- /*
- * This loop is executed once for every character read.
- * Keep it fast!
- */
+ // This loop is executed once for every character read.
+ // Keep it fast!
if (fileformat == EOL_MAC) {
ptr--;
while (++ptr, --size >= 0) {
@@ -1577,7 +1515,7 @@ rewind_retry:
break;
}
if (read_undo_file) {
- sha256_update(&sha_ctx, (char_u *)line_start, (size_t)len);
+ sha256_update(&sha_ctx, (uint8_t *)line_start, (size_t)len);
}
lnum++;
if (--read_count == 0) {
@@ -1633,7 +1571,7 @@ rewind_retry:
break;
}
if (read_undo_file) {
- sha256_update(&sha_ctx, (char_u *)line_start, (size_t)len);
+ sha256_update(&sha_ctx, (uint8_t *)line_start, (size_t)len);
}
lnum++;
if (--read_count == 0) {
@@ -1658,21 +1596,31 @@ failed:
error = false;
}
- /*
- * If we get EOF in the middle of a line, note the fact and
- * complete the line ourselves.
- * In Dos format ignore a trailing CTRL-Z, unless 'binary' set.
- */
+ // In Dos format ignore a trailing CTRL-Z, unless 'binary' is set.
+ // In old days the file length was in sector count and the CTRL-Z the
+ // marker where the file really ended. Assuming we write it to a file
+ // system that keeps file length properly the CTRL-Z should be dropped.
+ // Set the 'endoffile' option so the user can decide what to write later.
+ // In Unix format the CTRL-Z is just another character.
+ if (linerest != 0
+ && !curbuf->b_p_bin
+ && fileformat == EOL_DOS
+ && ptr[-1] == Ctrl_Z) {
+ ptr--;
+ linerest--;
+ if (set_options) {
+ curbuf->b_p_eof = true;
+ }
+ }
+
+ // If we get EOF in the middle of a line, note the fact and
+ // complete the line ourselves.
if (!error
&& !got_int
- && linerest != 0
- && !(!curbuf->b_p_bin
- && fileformat == EOL_DOS
- && *line_start == Ctrl_Z
- && ptr == line_start + 1)) {
+ && linerest != 0) {
// remember for when writing
if (set_options) {
- curbuf->b_p_eol = FALSE;
+ curbuf->b_p_eol = false;
}
*ptr = NUL;
len = (colnr_T)(ptr - line_start + 1);
@@ -1680,7 +1628,7 @@ failed:
error = true;
} else {
if (read_undo_file) {
- sha256_update(&sha_ctx, (char_u *)line_start, (size_t)len);
+ sha256_update(&sha_ctx, (uint8_t *)line_start, (size_t)len);
}
read_no_eol_lnum = ++lnum;
}
@@ -1696,11 +1644,9 @@ failed:
if (fenc_alloced) {
xfree(fenc);
}
-#ifdef HAVE_ICONV
if (iconv_fd != (iconv_t)-1) {
iconv_close(iconv_fd);
}
-#endif
if (!read_buffer && !read_stdin) {
close(fd); // errors are ignored
@@ -1712,7 +1658,7 @@ failed:
if (read_stdin) {
close(fd);
if (stdin_fd < 0) {
-#ifndef WIN32
+#ifndef MSWIN
// On Unix, use stderr for stdin, makes shell commands work.
vim_ignored = dup(2);
#else
@@ -1720,7 +1666,7 @@ failed:
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);
+ vim_ignored = _open_osfhandle((intptr_t)conin, _O_RDONLY);
#endif
}
}
@@ -1729,11 +1675,9 @@ failed:
os_remove(tmpname); // delete converted file
xfree(tmpname);
}
- --no_wait_return; // may wait for return now
+ no_wait_return--; // may wait for return now
- /*
- * In recovery mode everything but autocommands is skipped.
- */
+ // In recovery mode everything but autocommands is skipped.
if (!recoverymode) {
// need to delete the last line, which comes from the empty buffer
if (newfile && wasempty && !(curbuf->b_ml.ml_flags & ML_EMPTY)) {
@@ -1749,7 +1693,7 @@ failed:
linecnt = 0;
}
if (newfile || read_buffer) {
- redraw_curbuf_later(NOT_VALID);
+ redraw_curbuf_later(UPD_NOT_VALID);
// After reading the text into the buffer the diff info needs to
// be updated.
diff_invalidate(curbuf);
@@ -1760,20 +1704,11 @@ failed:
appended_lines_mark(from, linecnt);
}
- /*
- * If we were reading from the same terminal as where messages go,
- * the screen will have been messed up.
- * Switch on raw mode now and clear the screen.
- */
- if (read_stdin) {
- screenclear();
- }
-
if (got_int) {
if (!(flags & READ_DUMMY)) {
- filemess(curbuf, (char_u *)sfname, (char_u *)_(e_interr), 0);
+ filemess(curbuf, sfname, _(e_interr), 0);
if (newfile) {
- curbuf->b_p_ro = TRUE; // must use "w!" now
+ curbuf->b_p_ro = true; // must use "w!" now
}
}
msg_scroll = msg_save;
@@ -1782,36 +1717,36 @@ failed:
}
if (!filtering && !(flags & READ_DUMMY) && !silent) {
- add_quoted_fname((char *)IObuff, IOSIZE, curbuf, (const char *)sfname);
+ add_quoted_fname(IObuff, IOSIZE, curbuf, (const char *)sfname);
c = false;
#ifdef UNIX
if (S_ISFIFO(perm)) { // fifo
STRCAT(IObuff, _("[fifo]"));
- c = TRUE;
+ c = true;
}
if (S_ISSOCK(perm)) { // or socket
STRCAT(IObuff, _("[socket]"));
- c = TRUE;
+ c = true;
}
# ifdef OPEN_CHR_FILES
if (S_ISCHR(perm)) { // or character special
STRCAT(IObuff, _("[character special]"));
- c = TRUE;
+ c = true;
}
# endif
#endif
if (curbuf->b_p_ro) {
STRCAT(IObuff, shortmess(SHM_RO) ? _("[RO]") : _("[readonly]"));
- c = TRUE;
+ c = true;
}
if (read_no_eol_lnum) {
msg_add_eol();
- c = TRUE;
+ c = true;
}
if (ff_error == EOL_DOS) {
STRCAT(IObuff, _("[CR missing]"));
- c = TRUE;
+ c = true;
}
if (split) {
STRCAT(IObuff, _("[long lines split]"));
@@ -1819,25 +1754,25 @@ failed:
}
if (notconverted) {
STRCAT(IObuff, _("[NOT converted]"));
- c = TRUE;
+ c = true;
} else if (converted) {
STRCAT(IObuff, _("[converted]"));
- c = TRUE;
+ c = true;
}
if (conv_error != 0) {
- sprintf((char *)IObuff + STRLEN(IObuff),
- _("[CONVERSION ERROR in line %" PRId64 "]"), (int64_t)conv_error);
- c = TRUE;
+ snprintf(IObuff + strlen(IObuff), IOSIZE - strlen(IObuff),
+ _("[CONVERSION ERROR in line %" PRId64 "]"), (int64_t)conv_error);
+ c = true;
} else if (illegal_byte > 0) {
- sprintf((char *)IObuff + STRLEN(IObuff),
- _("[ILLEGAL BYTE in line %" PRId64 "]"), (int64_t)illegal_byte);
- c = TRUE;
+ snprintf(IObuff + strlen(IObuff), IOSIZE - strlen(IObuff),
+ _("[ILLEGAL BYTE in line %" PRId64 "]"), (int64_t)illegal_byte);
+ c = true;
} else if (error) {
STRCAT(IObuff, _("[READ ERRORS]"));
- c = TRUE;
+ c = true;
}
if (msg_add_fileformat(fileformat)) {
- c = TRUE;
+ c = true;
}
msg_add_lines(c, (long)linecnt, filesize);
@@ -1847,7 +1782,7 @@ failed:
msg_scrolled_ign = true;
if (!read_stdin && !read_buffer) {
- p = (char_u *)msg_trunc_attr((char *)IObuff, false, 0);
+ p = (char_u *)msg_trunc_attr(IObuff, false, 0);
}
if (read_stdin || read_buffer || restart_edit != 0
@@ -1865,17 +1800,14 @@ failed:
// with errors writing the file requires ":w!"
if (newfile && (error
|| conv_error != 0
- || (illegal_byte > 0 && bad_char_behavior != BAD_KEEP)
- )) {
- curbuf->b_p_ro = TRUE;
+ || (illegal_byte > 0 && bad_char_behavior != BAD_KEEP))) {
+ curbuf->b_p_ro = true;
}
u_clearline(); // cannot use "U" command after adding lines
- /*
- * In Ex mode: cursor at last new line.
- * Otherwise: cursor at first new line.
- */
+ // In Ex mode: cursor at last new line.
+ // Otherwise: cursor at first new line.
if (exmode_active) {
curwin->w_cursor.lnum = from + linecnt;
} else {
@@ -1894,17 +1826,13 @@ failed:
}
msg_scroll = msg_save;
- /*
- * Get the marks before executing autocommands, so they can be used there.
- */
+ // Get the marks before executing autocommands, so they can be used there.
check_marks_read();
- /*
- * We remember if the last line of the read didn't have
- * an eol even when 'binary' is off, to support turning 'fixeol' off,
- * or writing the read again with 'binary' on. The latter is required
- * for ":autocmd FileReadPost *.gz set bin|'[,']!gunzip" to work.
- */
+ // We remember if the last line of the read didn't have
+ // an eol even when 'binary' is off, to support turning 'fixeol' off,
+ // or writing the read again with 'binary' on. The latter is required
+ // for ":autocmd FileReadPost *.gz set bin|'[,']!gunzip" to work.
curbuf->b_no_eol_lnum = read_no_eol_lnum;
// When reloading a buffer put the cursor at the first line that is
@@ -1913,14 +1841,12 @@ failed:
u_find_first_changed();
}
- /*
- * When opening a new file locate undo info and read it.
- */
+ // When opening a new file locate undo info and read it.
if (read_undo_file) {
char_u hash[UNDO_HASH_SIZE];
sha256_finish(&sha_ctx, hash);
- u_read_undo(NULL, hash, (char_u *)fname);
+ u_read_undo(NULL, hash, fname);
}
if (!read_stdin && !read_fifo && (!read_buffer || sfname != NULL)) {
@@ -1933,11 +1859,9 @@ failed:
save_file_ff(curbuf);
}
- /*
- * The output from the autocommands should not overwrite anything and
- * should not be overwritten: Set msg_scroll, restore its value if no
- * output was done.
- */
+ // The output from the autocommands should not overwrite anything and
+ // should not be overwritten: Set msg_scroll, restore its value if no
+ // output was done.
msg_scroll = true;
if (filtering) {
apply_autocmds_exarg(EVENT_FILTERREADPOST, NULL, sfname,
@@ -1948,7 +1872,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, (char *)curbuf->b_p_ft, curbuf->b_fname, true, curbuf);
+ apply_autocmds(EVENT_FILETYPE, curbuf->b_p_ft, curbuf->b_fname, true, curbuf);
}
} else {
apply_autocmds_exarg(EVENT_FILEREADPOST, sfname, sfname,
@@ -1978,7 +1902,7 @@ failed:
bool is_dev_fd_file(char *fname)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
- return STRNCMP(fname, "/dev/fd/", 8) == 0
+ return strncmp(fname, "/dev/fd/", 8) == 0
&& ascii_isdigit((uint8_t)fname[8])
&& *skipdigits(fname + 9) == NUL
&& (fname[9] != NUL
@@ -1993,13 +1917,13 @@ bool is_dev_fd_file(char *fname)
/// @param linecnt line count before reading more bytes
/// @param p start of more bytes read
/// @param endp end of more bytes read
-static linenr_T readfile_linenr(linenr_T linecnt, char_u *p, char_u *endp)
+static linenr_T readfile_linenr(linenr_T linecnt, char *p, const char *endp)
{
- char_u *s;
+ char *s;
linenr_T lnum;
lnum = curbuf->b_ml.ml_line_count - linecnt + 1;
- for (s = p; s < endp; ++s) {
+ for (s = p; s < endp; s++) {
if (*s == '\n') {
lnum++;
}
@@ -2012,17 +1936,17 @@ static linenr_T readfile_linenr(linenr_T linecnt, char_u *p, char_u *endp)
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);
+ const size_t cmd_len = 15 + strlen(buf->b_p_fenc);
eap->cmd = xmalloc(cmd_len);
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;
+ eap->force_ff = (unsigned char)(*buf->b_p_ff);
eap->force_bin = buf->b_p_bin ? FORCE_BIN : FORCE_NOBIN;
- eap->read_edit = FALSE;
- eap->forceit = FALSE;
+ eap->read_edit = false;
+ eap->forceit = false;
}
/// Set default or forced 'fileformat' and 'binary'.
@@ -2049,11 +1973,13 @@ void set_file_options(int set_options, exarg_T *eap)
/// Set forced 'fileencoding'.
void set_forced_fenc(exarg_T *eap)
{
- if (eap->force_enc != 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);
+ if (eap->force_enc == 0) {
+ return;
}
+
+ char *fenc = enc_canonize(eap->cmd + eap->force_enc);
+ set_string_option_direct("fenc", -1, fenc, OPT_FREE|OPT_LOCAL, 0);
+ xfree(fenc);
}
/// Find next fileencoding to use from 'fileencodings'.
@@ -2062,24 +1988,24 @@ void set_forced_fenc(exarg_T *eap)
/// 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 **pp, bool *alloced)
+static char *next_fenc(char **pp, bool *alloced)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
{
- char_u *p;
- char_u *r;
+ char *p;
+ char *r;
*alloced = false;
if (**pp == NUL) {
*pp = NULL;
- return (char_u *)"";
+ return "";
}
- p = (char_u *)vim_strchr((*pp), ',');
+ p = vim_strchr(*pp, ',');
if (p == NULL) {
- r = enc_canonize((char_u *)(*pp));
- *pp += STRLEN(*pp);
+ r = enc_canonize(*pp);
+ *pp += strlen(*pp);
} else {
- r = vim_strnsave((char_u *)(*pp), (size_t)(p - (char_u *)(*pp)));
- *pp = (char *)p + 1;
+ r = xstrnsave(*pp, (size_t)(p - *pp));
+ *pp = p + 1;
p = enc_canonize(r);
xfree(r);
r = p;
@@ -2098,9 +2024,9 @@ static char_u *next_fenc(char **pp, bool *alloced)
///
/// @return name of the resulting converted file (the caller should delete it after reading it).
/// Returns NULL if the conversion failed ("*fdp" is not set) .
-static char_u *readfile_charconvert(char_u *fname, char_u *fenc, int *fdp)
+static char *readfile_charconvert(char *fname, char *fenc, int *fdp)
{
- char_u *tmpname;
+ char *tmpname;
char *errmsg = NULL;
tmpname = vim_tempname();
@@ -2109,11 +2035,11 @@ static char_u *readfile_charconvert(char_u *fname, char_u *fenc, int *fdp)
} else {
close(*fdp); // close the input file, ignore errors
*fdp = -1;
- if (eval_charconvert((char *)fenc, "utf-8",
- (char *)fname, (char *)tmpname) == FAIL) {
+ if (eval_charconvert(fenc, "utf-8",
+ fname, tmpname) == FAIL) {
errmsg = _("Conversion with 'charconvert' failed");
}
- if (errmsg == NULL && (*fdp = os_open((char *)tmpname, O_RDONLY, 0)) < 0) {
+ if (errmsg == NULL && (*fdp = os_open(tmpname, O_RDONLY, 0)) < 0) {
errmsg = _("can't read output of 'charconvert'");
}
}
@@ -2123,14 +2049,14 @@ static char_u *readfile_charconvert(char_u *fname, char_u *fenc, int *fdp)
// another type of conversion might still work.
msg(errmsg);
if (tmpname != NULL) {
- os_remove((char *)tmpname); // delete converted file
+ os_remove(tmpname); // delete converted file
XFREE_CLEAR(tmpname);
}
}
// If the input file is closed, open it (caller should check for error).
if (*fdp < 0) {
- *fdp = os_open((char *)fname, O_RDONLY, 0);
+ *fdp = os_open(fname, O_RDONLY, 0);
}
return tmpname;
@@ -2145,8 +2071,8 @@ static void check_marks_read(void)
shada_read_marks();
}
- /* Always set b_marks_read; needed when 'shada' is changed to include
- * the ' parameter after opening a buffer. */
+ // Always set b_marks_read; needed when 'shada' is changed to include
+ // the ' parameter after opening a buffer.
curbuf->b_marks_read = true;
}
@@ -2161,9 +2087,9 @@ char *new_file_message(void)
///
/// If "forceit" is true, we don't care for errors when attempting backups.
/// In case of an error everything possible is done to restore the original
-/// file. But when "forceit" is TRUE, we risk losing it.
+/// file. But when "forceit" is true, we risk losing it.
///
-/// When "reset_changed" is TRUE and "append" == FALSE and "start" == 1 and
+/// When "reset_changed" is true and "append" == false and "start" == 1 and
/// "end" == curbuf->b_ml.ml_line_count, reset curbuf->b_changed.
///
/// This function must NOT use NameBuff (because it's called by autowrite()).
@@ -2204,9 +2130,9 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
int bufsize;
long perm; // file permissions
int retval = OK;
- int newfile = false; // TRUE if file doesn't exist yet
+ int newfile = false; // true if file doesn't exist yet
int msg_save = msg_scroll;
- int overwriting; // TRUE if writing over original
+ int overwriting; // true if writing over original
int no_eol = false; // no end-of-line written
int device = false; // writing to a device
int prev_got_int = got_int;
@@ -2215,7 +2141,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
static char *err_readonly =
"is read-only (cannot override: \"W\" in 'cpoptions')";
#if defined(UNIX)
- int made_writable = FALSE; // 'w' bit has been set
+ int made_writable = false; // 'w' bit has been set
#endif
// writing everything
int whole = (start == 1 && end == buf->b_ml.ml_line_count);
@@ -2231,10 +2157,10 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
int wb_flags = 0;
#endif
#ifdef HAVE_ACL
- vim_acl_T acl = NULL; /* ACL copied from original file to
- backup or new file */
+ vim_acl_T acl = NULL; // ACL copied from original file to
+ // backup or new file
#endif
- int write_undo_file = FALSE;
+ 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;
@@ -2244,46 +2170,39 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
return FAIL;
}
if (buf->b_ml.ml_mfp == NULL) {
- /* This can happen during startup when there is a stray "w" in the
- * vimrc file. */
+ // This can happen during startup when there is a stray "w" in the
+ // vimrc file.
emsg(_(e_emptybuf));
return FAIL;
}
- /*
- * Disallow writing from .exrc and .vimrc in current directory for
- * security reasons.
- */
+ // Disallow writing in secure mode.
if (check_secure()) {
return FAIL;
}
// Avoid a crash for a long name.
- if (STRLEN(fname) >= MAXPATHL) {
+ if (strlen(fname) >= MAXPATHL) {
emsg(_(e_longname));
return FAIL;
}
// must init bw_conv_buf and bw_iconv_fd before jumping to "fail"
write_info.bw_conv_buf = NULL;
- write_info.bw_conv_error = FALSE;
+ write_info.bw_conv_error = false;
write_info.bw_conv_error_lnum = 0;
write_info.bw_restlen = 0;
-#ifdef HAVE_ICONV
write_info.bw_iconv_fd = (iconv_t)-1;
-#endif
- /* After writing a file changedtick changes but we don't want to display
- * the line. */
+ // After writing a file changedtick changes but we don't want to display
+ // the line.
ex_no_reprint = true;
- /*
- * If there is no file name yet, use the one for the written file.
- * BF_NOTEDITED is set to reflect this (in case the write fails).
- * Don't do this when the write is for a filter command.
- * Don't do this when appending.
- * Only do this when 'cpoptions' contains the 'F' flag.
- */
+ // If there is no file name yet, use the one for the written file.
+ // BF_NOTEDITED is set to reflect this (in case the write fails).
+ // Don't do this when the write is for a filter command.
+ // Don't do this when appending.
+ // Only do this when 'cpoptions' contains the 'F' flag.
if (buf->b_ffname == NULL
&& reset_changed
&& whole
@@ -2292,7 +2211,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
&& !filtering
&& (!append || vim_strchr(p_cpo, CPO_FNAMEAPP) != NULL)
&& vim_strchr(p_cpo, CPO_FNAMEW) != NULL) {
- if (set_rw_fname((char_u *)fname, (char_u *)sfname) == FAIL) {
+ if (set_rw_fname(fname, sfname) == FAIL) {
return FAIL;
}
buf = curbuf; // just in case autocmds made "buf" invalid
@@ -2311,17 +2230,15 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
fname = sfname;
#endif
- if (buf->b_ffname != NULL && FNAMECMP(ffname, buf->b_ffname) == 0) {
+ if (buf->b_ffname != NULL && path_fnamecmp(ffname, buf->b_ffname) == 0) {
overwriting = true;
} else {
- overwriting = FALSE;
+ overwriting = false;
}
- ++no_wait_return; // don't wait for return yet
+ no_wait_return++; // don't wait for return yet
- /*
- * Set '[ and '] marks to the lines to be written.
- */
+ // Set '[ and '] marks to the lines to be written.
buf->b_op_start.lnum = start;
buf->b_op_start.col = 0;
buf->b_op_end.lnum = end;
@@ -2329,20 +2246,18 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
{
aco_save_T aco;
- int buf_ffname = FALSE;
- int buf_sfname = FALSE;
- int buf_fname_f = FALSE;
- int buf_fname_s = FALSE;
- int did_cmd = FALSE;
- int nofile_err = FALSE;
+ int buf_ffname = false;
+ int buf_sfname = false;
+ int buf_fname_f = false;
+ int buf_fname_s = false;
+ int did_cmd = false;
+ int nofile_err = false;
int empty_memline = (buf->b_ml.ml_mfp == NULL);
bufref_T bufref;
- /*
- * Apply PRE autocommands.
- * Set curbuf to the buffer to be written.
- * Careful: The autocommands may call buf_write() recursively!
- */
+ // Apply PRE autocommands.
+ // Set curbuf to the buffer to be written.
+ // Careful: The autocommands may call buf_write() recursively!
if (ffname == buf->b_ffname) {
buf_ffname = true;
}
@@ -2380,9 +2295,9 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
sfname, sfname, false, curbuf, eap);
if (did_cmd) {
if (was_changed && !curbufIsChanged()) {
- /* Written everything correctly and BufWriteCmd has reset
- * 'modified': Correct the undo information so that an
- * undo now sets 'modified'. */
+ // Written everything correctly and BufWriteCmd has reset
+ // 'modified': Correct the undo information so that an
+ // undo now sets 'modified'.
u_unchanged(curbuf);
u_update_save_nr(curbuf);
}
@@ -2428,19 +2343,19 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
no_wait_return--;
msg_scroll = msg_save;
if (nofile_err) {
- emsg(_("E676: No matching autocommands for acwrite buffer"));
+ semsg(_(e_no_matching_autocommands_for_buftype_str_buffer), curbuf->b_p_bt);
}
if (nofile_err
|| aborting()) {
- /* An aborting error, interrupt or exception in the
- * autocommands. */
+ // An aborting error, interrupt or exception in the
+ // autocommands.
return FAIL;
}
if (did_cmd) {
if (buf == NULL) {
- /* The buffer was deleted. We assume it was written
- * (can't retry anyway). */
+ // The buffer was deleted. We assume it was written
+ // (can't retry anyway).
return OK;
}
if (overwriting) {
@@ -2465,12 +2380,10 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
return FAIL;
}
- /*
- * The autocommands may have changed the number of lines in the file.
- * When writing the whole file, adjust the end.
- * When writing part of the file, assume that the autocommands only
- * changed the number of lines that are to be written (tricky!).
- */
+ // The autocommands may have changed the number of lines in the file.
+ // When writing the whole file, adjust the end.
+ // When writing part of the file, assume that the autocommands only
+ // changed the number of lines that are to be written (tricky!).
if (buf->b_ml.ml_line_count != old_line_count) {
if (whole) { // write all
end = buf->b_ml.ml_line_count;
@@ -2487,10 +2400,8 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
}
}
- /*
- * The autocommands may have changed the name of the buffer, which may
- * be kept in fname, ffname and sfname.
- */
+ // The autocommands may have changed the name of the buffer, which may
+ // be kept in fname, ffname and sfname.
if (buf_ffname) {
ffname = buf->b_ffname;
}
@@ -2512,20 +2423,20 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
}
if (shortmess(SHM_OVER) && !exiting) {
- msg_scroll = FALSE; // overwrite previous file message
+ msg_scroll = false; // overwrite previous file message
} else {
- msg_scroll = TRUE; // don't overwrite previous file message
+ msg_scroll = true; // don't overwrite previous file message
}
if (!filtering) {
filemess(buf,
#ifndef UNIX
- (char_u *)sfname,
+ sfname,
#else
- (char_u *)fname,
+ fname,
#endif
- (char_u *)"", 0); // show that we are busy
+ "", 0); // show that we are busy
}
- msg_scroll = FALSE; // always overwrite the file message now
+ msg_scroll = false; // always overwrite the file message now
buffer = verbose_try_malloc(BUFSIZE);
// can't allocate big buffer, use small one (to be able to write when out of
@@ -2537,9 +2448,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
bufsize = BUFSIZE;
}
- /*
- * Get information about original file (if there is one).
- */
+ // Get information about original file (if there is one).
FileInfo file_info_old;
#if defined(UNIX)
perm = -1;
@@ -2556,23 +2465,23 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
SET_ERRMSG_NUM("E503", _("is not a file or writable device"));
goto fail;
}
- /* It's a device of some kind (or a fifo) which we can write to
- * but for which we can't make a backup. */
- device = TRUE;
- newfile = TRUE;
+ // It's a device of some kind (or a fifo) which we can write to
+ // but for which we can't make a backup.
+ device = true;
+ newfile = true;
perm = -1;
}
}
#else // win32
// Check for a writable device name.
- c = fname == NULL ? NODE_OTHER : os_nodetype((char *)fname);
+ c = fname == NULL ? NODE_OTHER : os_nodetype(fname);
if (c == NODE_OTHER) {
SET_ERRMSG_NUM("E503", _("is not a file or writable device"));
goto fail;
}
if (c == NODE_WRITABLE) {
- device = TRUE;
- newfile = TRUE;
+ device = true;
+ newfile = true;
perm = -1;
} else {
perm = os_getperm((const char *)fname);
@@ -2583,16 +2492,14 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
goto fail;
}
if (overwriting) {
- os_fileinfo((char *)fname, &file_info_old);
+ os_fileinfo(fname, &file_info_old);
}
}
#endif // !UNIX
if (!device && !newfile) {
- /*
- * Check if the file is really writable (when renaming the file to
- * make a backup we won't discover it later).
- */
+ // 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(fname);
if (!forceit && file_readonly) {
@@ -2604,10 +2511,8 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
goto fail;
}
- /*
- * Check if the timestamp hasn't changed since reading the file.
- */
- if (overwriting) {
+ // If 'forceit' is false, check if the timestamp hasn't changed since reading the file.
+ if (overwriting && !forceit) {
retval = check_mtime(buf, &file_info_old);
if (retval == FAIL) {
goto fail;
@@ -2616,101 +2521,87 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
}
#ifdef HAVE_ACL
- /*
- * For systems that support ACL: get the ACL from the original file.
- */
+ // For systems that support ACL: get the ACL from the original file.
if (!newfile) {
- acl = mch_get_acl((char_u *)fname);
+ acl = os_get_acl(fname);
}
#endif
- /*
- * If 'backupskip' is not empty, don't make a backup for some files.
- */
+ // 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, (char_u *)sfname, (char_u *)ffname)) {
+ if (dobackup && *p_bsk != NUL && match_file_list(p_bsk, sfname, ffname)) {
dobackup = false;
}
- /*
- * Save the value of got_int and reset it. We don't want a previous
- * interruption cancel writing, only hitting CTRL-C while writing should
- * abort it.
- */
+ // Save the value of got_int and reset it. We don't want a previous
+ // interruption cancel writing, only hitting CTRL-C while writing should
+ // abort it.
prev_got_int = got_int;
- got_int = FALSE;
+ got_int = false;
// Mark the buffer as 'being saved' to prevent changed buffer warnings
buf->b_saving = true;
- /*
- * If we are not appending or filtering, the file exists, and the
- * 'writebackup', 'backup' or 'patchmode' option is set, need a backup.
- * When 'patchmode' is set also make a backup when appending.
- *
- * Do not make any backup, if 'writebackup' and 'backup' are both switched
- * off. This helps when editing large files on almost-full disks.
- */
+ // If we are not appending or filtering, the file exists, and the
+ // 'writebackup', 'backup' or 'patchmode' option is set, need a backup.
+ // When 'patchmode' is set also make a backup when appending.
+ //
+ // Do not make any backup, if 'writebackup' and 'backup' are both switched
+ // off. This helps when editing large files on almost-full disks.
if (!(append && *p_pm == NUL) && !filtering && perm >= 0 && dobackup) {
FileInfo file_info;
const bool no_prepend_dot = false;
if ((bkc & BKC_YES) || append) { // "yes"
- backup_copy = TRUE;
+ backup_copy = true;
} else if ((bkc & BKC_AUTO)) { // "auto"
int i;
- /*
- * Don't rename the file when:
- * - it's a hard link
- * - it's a symbolic link
- * - we don't have write permission in the directory
- */
+ // Don't rename the file when:
+ // - it's a hard link
+ // - it's a symbolic link
+ // - we don't have write permission in the directory
if (os_fileinfo_hardlinks(&file_info_old) > 1
|| !os_fileinfo_link(fname, &file_info)
|| !os_fileinfo_id_equal(&file_info, &file_info_old)) {
- backup_copy = TRUE;
+ backup_copy = true;
} else {
- /*
- * Check if we can create a file and set the owner/group to
- * the ones from the original file.
- * First find a file name that doesn't exist yet (use some
- * arbitrary numbers).
- */
+ // Check if we can create a file and set the owner/group to
+ // the ones from the original file.
+ // First find a file name that doesn't exist yet (use some
+ // arbitrary numbers).
STRCPY(IObuff, fname);
for (i = 4913;; i += 123) {
- char *tail = path_tail((char *)IObuff);
- size_t size = (size_t)((char_u *)tail - IObuff);
+ char *tail = path_tail(IObuff);
+ size_t size = (size_t)(tail - IObuff);
snprintf(tail, IOSIZE - size, "%d", i);
- if (!os_fileinfo_link((char *)IObuff, &file_info)) {
+ if (!os_fileinfo_link(IObuff, &file_info)) {
break;
}
}
- fd = os_open((char *)IObuff,
+ fd = os_open(IObuff,
O_CREAT|O_WRONLY|O_EXCL|O_NOFOLLOW, (int)perm);
if (fd < 0) { // can't write in directory
- backup_copy = TRUE;
+ backup_copy = true;
} else {
#ifdef UNIX
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)
+ if (!os_fileinfo(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
|| (long)file_info.stat.st_mode != perm) {
- backup_copy = TRUE;
+ backup_copy = true;
}
#endif
- /* Close the file before removing it, on MS-Windows we
- * can't delete an open file. */
+ // Close the file before removing it, on MS-Windows we
+ // can't delete an open file.
close(fd);
- os_remove((char *)IObuff);
+ os_remove(IObuff);
}
}
}
- /*
- * Break symlinks and/or hardlinks if we've been asked to.
- */
+ // Break symlinks and/or hardlinks if we've been asked to.
if ((bkc & BKC_BREAKSYMLINK) || (bkc & BKC_BREAKHARDLINK)) {
#ifdef UNIX
bool file_info_link_ok = os_fileinfo_link(fname, &file_info);
@@ -2719,7 +2610,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
if ((bkc & BKC_BREAKSYMLINK)
&& file_info_link_ok
&& !os_fileinfo_id_equal(&file_info, &file_info_old)) {
- backup_copy = FALSE;
+ backup_copy = false;
}
// Hardlinks.
@@ -2727,7 +2618,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
&& os_fileinfo_hardlinks(&file_info_old) > 1
&& (!file_info_link_ok
|| os_fileinfo_id_equal(&file_info, &file_info_old))) {
- backup_copy = FALSE;
+ backup_copy = false;
}
#endif
}
@@ -2736,7 +2627,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
if (*p_bex == NUL) {
backup_ext = ".bak";
} else {
- backup_ext = (char *)p_bex;
+ backup_ext = p_bex;
}
if (backup_copy) {
@@ -2746,33 +2637,29 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
char *rootname;
char *p;
- /*
- * Try to make the backup in each directory in the 'bdir' option.
- *
- * Unix semantics has it, that we may have a writable file,
- * that cannot be recreated with a simple open(..., O_CREAT, ) e.g:
- * - the directory is not writable,
- * - the file may be a symbolic link,
- * - the file may belong to another user/group, etc.
- *
- * For these reasons, the existing writable file must be truncated
- * and reused. Creation of a backup COPY will be attempted.
- */
- dirp = (char *)p_bdir;
+ // Try to make the backup in each directory in the 'bdir' option.
+ //
+ // Unix semantics has it, that we may have a writable file,
+ // that cannot be recreated with a simple open(..., O_CREAT, ) e.g:
+ // - the directory is not writable,
+ // - the file may be a symbolic link,
+ // - the file may belong to another user/group, etc.
+ //
+ // For these reasons, the existing writable file must be truncated
+ // and reused. Creation of a backup COPY will be attempted.
+ dirp = p_bdir;
while (*dirp) {
- /*
- * Isolate one directory name, using an entry in 'bdir'.
- */
- 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];
+ // 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(IObuff, p) && p[-1] == p[-2];
if (trailing_pathseps) {
IObuff[dir_len - 2] = NUL;
}
if (*dirp == NUL && !os_isdir(IObuff)) {
int ret;
char *failed_dir;
- if ((ret = os_mkdir_recurse((char *)IObuff, 0755, &failed_dir)) != 0) {
+ if ((ret = os_mkdir_recurse(IObuff, 0755, &failed_dir)) != 0) {
semsg(_("E303: Unable to create directory \"%s\" for backup file: %s"),
failed_dir, os_strerror(ret));
xfree(failed_dir);
@@ -2780,16 +2667,16 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
}
if (trailing_pathseps) {
// Ends with '//', Use Full path
- if ((p = make_percent_swname((char *)IObuff, fname))
+ if ((p = make_percent_swname(IObuff, fname))
!= NULL) {
backup = modname(p, backup_ext, no_prepend_dot);
xfree(p);
}
}
- rootname = (char *)get_file_in_dir((char_u *)fname, IObuff);
+ rootname = get_file_in_dir(fname, IObuff);
if (rootname == NULL) {
- some_error = TRUE; // out of memory
+ some_error = true; // out of memory
goto nobackup;
}
@@ -2804,13 +2691,11 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
if (backup == NULL) {
xfree(rootname);
- some_error = TRUE; // out of memory
+ some_error = true; // out of memory
goto nobackup;
}
- /*
- * Check if backup file already exists.
- */
+ // Check if backup file already exists.
if (os_fileinfo(backup, &file_info_new)) {
if (os_fileinfo_id_equal(&file_info_new, &file_info_old)) {
//
@@ -2825,7 +2710,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
// delete an existing one, and try to use another name instead.
// Change one character, just before the extension.
//
- wp = backup + STRLEN(backup) - 1 - STRLEN(backup_ext);
+ wp = backup + strlen(backup) - 1 - strlen(backup_ext);
if (wp < backup) { // empty file name ???
wp = backup;
}
@@ -2842,9 +2727,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
}
xfree(rootname);
- /*
- * Try to create the backup file
- */
+ // Try to create the backup file
if (backup != NULL) {
// remove old backup, if present
os_remove(backup);
@@ -2867,10 +2750,11 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
#endif
// copy the file
- if (os_copy(fname, backup, UV_FS_COPYFILE_FICLONE)
- != 0) {
- SET_ERRMSG(_("E506: Can't write to backup file "
- "(add ! to override)"));
+ if (os_copy(fname, backup, UV_FS_COPYFILE_FICLONE) != 0) {
+ SET_ERRMSG(_("E509: Cannot create backup file (add ! to override)"));
+ XFREE_CLEAR(backup);
+ backup = NULL;
+ continue;
}
#ifdef UNIX
@@ -2879,8 +2763,9 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
(double)file_info_old.stat.st_mtim.tv_sec);
#endif
#ifdef HAVE_ACL
- mch_set_acl((char_u *)backup, acl);
+ os_set_acl(backup, acl);
#endif
+ SET_ERRMSG(NULL);
break;
}
}
@@ -2889,7 +2774,7 @@ nobackup:
if (backup == NULL && errmsg == NULL) {
SET_ERRMSG(_("E509: Cannot create backup file (add ! to override)"));
}
- // Ignore errors when forceit is TRUE.
+ // Ignore errors when forceit is true.
if ((some_error || errmsg != NULL) && !forceit) {
retval = FAIL;
goto fail;
@@ -2900,40 +2785,32 @@ nobackup:
char *p;
char *rootname;
- /*
- * Make a backup by renaming the original file.
- */
- /*
- * If 'cpoptions' includes the "W" flag, we don't want to
- * overwrite a read-only file. But rename may be possible
- * anyway, thus we need an extra check here.
- */
+ // Make a backup by renaming the original file.
+
+ // If 'cpoptions' includes the "W" flag, we don't want to
+ // overwrite a read-only file. But rename may be possible
+ // anyway, thus we need an extra check here.
if (file_readonly && vim_strchr(p_cpo, CPO_FWRITE) != NULL) {
SET_ERRMSG_NUM("E504", _(err_readonly));
goto fail;
}
- /*
- *
- * Form the backup file name - change path/fo.o.h to
- * path/fo.o.h.bak Try all directories in 'backupdir', first one
- * that works is used.
- */
- dirp = (char *)p_bdir;
+ // Form the backup file name - change path/fo.o.h to
+ // path/fo.o.h.bak Try all directories in 'backupdir', first one
+ // that works is used.
+ dirp = p_bdir;
while (*dirp) {
- /*
- * Isolate one directory name and make the backup file name.
- */
- 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];
+ // 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(IObuff, p) && p[-1] == p[-2];
if (trailing_pathseps) {
IObuff[dir_len - 2] = NUL;
}
if (*dirp == NUL && !os_isdir(IObuff)) {
int ret;
char *failed_dir;
- if ((ret = os_mkdir_recurse((char *)IObuff, 0755, &failed_dir)) != 0) {
+ if ((ret = os_mkdir_recurse(IObuff, 0755, &failed_dir)) != 0) {
semsg(_("E303: Unable to create directory \"%s\" for backup file: %s"),
failed_dir, os_strerror(ret));
xfree(failed_dir);
@@ -2941,7 +2818,7 @@ nobackup:
}
if (trailing_pathseps) {
// path ends with '//', use full path
- if ((p = make_percent_swname((char *)IObuff, fname))
+ if ((p = make_percent_swname(IObuff, fname))
!= NULL) {
backup = modname(p, backup_ext, no_prepend_dot);
xfree(p);
@@ -2949,7 +2826,7 @@ nobackup:
}
if (backup == NULL) {
- rootname = (char *)get_file_in_dir((char_u *)fname, IObuff);
+ rootname = get_file_in_dir(fname, IObuff);
if (rootname == NULL) {
backup = NULL;
} else {
@@ -2959,18 +2836,16 @@ nobackup:
}
if (backup != NULL) {
- /*
- * If we are not going to keep the backup file, don't
- * delete an existing one, try to use another name.
- * Change one character, just before the extension.
- */
- if (!p_bk && os_path_exists((char_u *)backup)) {
- p = backup + STRLEN(backup) - 1 - STRLEN(backup_ext);
+ // If we are not going to keep the backup file, don't
+ // delete an existing one, try to use another name.
+ // Change one character, just before the extension.
+ if (!p_bk && os_path_exists(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((char_u *)backup)) {
+ while (*p > 'a' && os_path_exists(backup)) {
(*p)--;
}
// They all exist??? Must be something wrong!
@@ -2988,7 +2863,7 @@ nobackup:
// If the renaming of the original file to the backup file
// works, quit here.
///
- if (vim_rename((char_u *)fname, (char_u *)backup) == 0) {
+ if (vim_rename(fname, backup) == 0) {
break;
}
@@ -3049,20 +2924,20 @@ nobackup:
// Check for forced 'fileencoding' from "++opt=val" argument.
if (eap != NULL && eap->force_enc != 0) {
fenc = eap->cmd + eap->force_enc;
- fenc = (char *)enc_canonize((char_u *)fenc);
+ fenc = enc_canonize(fenc);
fenc_tofree = fenc;
} else {
- fenc = (char *)buf->b_p_fenc;
+ fenc = buf->b_p_fenc;
}
// Check if the file needs to be converted.
- converted = need_conversion((char_u *)fenc);
+ converted = need_conversion(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((char_u *)fenc);
+ wb_flags = get_fio_flags(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)) {
@@ -3078,10 +2953,9 @@ 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((char_u *)fenc, (char_u *)"utf-8");
+ write_info.bw_iconv_fd = (iconv_t)my_iconv_open(fenc, "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 = (size_t)bufsize * ICONV_MULT;
@@ -3089,33 +2963,29 @@ nobackup:
if (!write_info.bw_conv_buf) {
end = 0;
}
- write_info.bw_first = TRUE;
- } else
-#endif
-
- /*
- * When the file needs to be converted with 'charconvert' after
- * writing, write to a temp file instead and let the conversion
- * overwrite the original file.
- */
- if (*p_ccv != NUL) {
- 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;
+ write_info.bw_first = true;
+ } else {
+ // When the file needs to be converted with 'charconvert' after
+ // writing, write to a temp file instead and let the conversion
+ // overwrite the original file.
+ if (*p_ccv != NUL) {
+ wfname = vim_tempname();
+ if (wfname == NULL) { // Can't write without a tempfile!
+ SET_ERRMSG(_("E214: Can't find temp file for writing"));
+ goto restore_backup;
+ }
}
}
}
+
if (converted && wb_flags == 0
-#ifdef HAVE_ICONV
&& write_info.bw_iconv_fd == (iconv_t)-1
-#endif
&& wfname == fname) {
if (!forceit) {
SET_ERRMSG(_("E213: Cannot convert (add ! to write without conversion)"));
goto restore_backup;
}
- notconverted = TRUE;
+ notconverted = true;
}
// If conversion is taking place, we may first pretend to write and check
@@ -3137,17 +3007,19 @@ nobackup:
} else {
// Open the file "wfname" for writing.
// We may try to open the file twice: If we can't write to the file
- // and forceit is TRUE we delete the existing file and try to
+ // and forceit is true we delete the existing file and try to
// create a new one. If this still fails we may have lost the
// original file! (this may happen when the user reached his
// quotum for number of files).
// Appending will fail if the file does not exist and forceit is
- // FALSE.
+ // false.
while ((fd = os_open(wfname,
O_WRONLY |
- (append ?
- (forceit ? (O_APPEND | O_CREAT) : O_APPEND)
- : (O_CREAT | O_TRUNC)),
+ (append
+ ? (forceit
+ ? (O_APPEND | O_CREAT)
+ : O_APPEND)
+ : (O_CREAT | O_TRUNC)),
perm < 0 ? 0666 : (perm & 0777))) < 0) {
// A forced write will try to create a new file if the old one
// is still readonly. This may also happen when the directory
@@ -3200,21 +3072,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((char_u *)fname)) {
- vim_rename((char_u *)backup, (char_u *)fname);
+ if (!os_path_exists(fname)) {
+ vim_rename(backup, fname);
}
// if original file does exist throw away the copy
- if (os_path_exists((char_u *)fname)) {
+ if (os_path_exists(fname)) {
os_remove(backup);
}
} else {
// try to put the original file back
- vim_rename((char_u *)backup, (char_u *)fname);
+ vim_rename(backup, fname);
}
}
// if original file no longer exists give an extra warning
- if (!newfile && !os_path_exists((char_u *)fname)) {
+ if (!newfile && !os_path_exists(fname)) {
end = 0;
}
}
@@ -3228,7 +3100,7 @@ restore_backup:
}
SET_ERRMSG(NULL);
- write_info.bw_buf = (char_u *)buffer;
+ write_info.bw_buf = buffer;
nchars = 0;
// use "++bin", "++nobin" or 'binary'
@@ -3241,7 +3113,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((char_u *)buffer, (char_u *)fenc);
+ write_info.bw_len = make_bom((char_u *)buffer, fenc);
if (write_info.bw_len > 0) {
// don't convert
write_info.bw_flags = FIO_NOCONVERT | wb_flags;
@@ -3271,9 +3143,9 @@ restore_backup:
for (lnum = start; lnum <= end; lnum++) {
// The next while loop is done once for each character written.
// Keep it fast!
- ptr = (char *)ml_get_buf(buf, lnum, false) - 1;
+ ptr = ml_get_buf(buf, lnum, false) - 1;
if (write_undo_file) {
- sha256_update(&sha_ctx, (char_u *)ptr + 1, (uint32_t)(STRLEN(ptr + 1) + 1));
+ sha256_update(&sha_ctx, (uint8_t *)ptr + 1, (uint32_t)(strlen(ptr + 1) + 1));
}
while ((c = *++ptr) != NUL) {
if (c == NL) {
@@ -3347,6 +3219,11 @@ restore_backup:
nchars += len;
}
+ if (!buf->b_p_fixeol && buf->b_p_eof) {
+ // write trailing CTRL-Z
+ (void)write_eintr(write_info.bw_fd, "\x1a", 1);
+ }
+
// Stop when writing done or an error was encountered.
if (!checking_conversion || end == 0) {
break;
@@ -3366,7 +3243,7 @@ restore_backup:
// been written to disk and we don't lose it.
// For a device do try the fsync() but don't complain if it does not work
// (could be a pipe).
- // If the 'fsync' option is FALSE, don't fsync(). Useful for laptops.
+ // If the 'fsync' option is false, don't fsync(). Useful for laptops.
int error;
if (p_fs && (error = os_fsync(fd)) != 0 && !device
// fsync not supported on this storage.
@@ -3414,7 +3291,7 @@ restore_backup:
// 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((char_u *)wfname, acl);
+ os_set_acl(wfname, acl);
}
#endif
@@ -3442,7 +3319,7 @@ restore_backup:
} else {
errmsg_allocated = true;
SET_ERRMSG(xmalloc(300));
- vim_snprintf(errmsg, 300,
+ vim_snprintf(errmsg, 300, // NOLINT(runtime/printf)
_("E513: write error, conversion failed in line %" PRIdLINENR
" (make 'fenc' empty to override)"),
write_info.bw_conv_error_lnum);
@@ -3476,7 +3353,7 @@ restore_backup:
end = 1; // success
}
} else {
- if (vim_rename((char_u *)backup, (char_u *)fname) == 0) {
+ if (vim_rename(backup, fname) == 0) {
end = 1;
}
}
@@ -3485,42 +3362,42 @@ restore_backup:
}
lnum -= start; // compute number of written lines
- --no_wait_return; // may wait for return now
+ no_wait_return--; // may wait for return now
#if !defined(UNIX)
fname = sfname; // use shortname now, for the messages
#endif
if (!filtering) {
- add_quoted_fname((char *)IObuff, IOSIZE, buf, (const char *)fname);
+ add_quoted_fname(IObuff, IOSIZE, buf, (const char *)fname);
c = false;
if (write_info.bw_conv_error) {
STRCAT(IObuff, _(" CONVERSION ERROR"));
- c = TRUE;
+ c = true;
if (write_info.bw_conv_error_lnum != 0) {
- vim_snprintf_add((char *)IObuff, IOSIZE, _(" in line %" PRId64 ";"),
+ vim_snprintf_add(IObuff, IOSIZE, _(" in line %" PRId64 ";"),
(int64_t)write_info.bw_conv_error_lnum);
}
} else if (notconverted) {
STRCAT(IObuff, _("[NOT converted]"));
- c = TRUE;
+ c = true;
} else if (converted) {
STRCAT(IObuff, _("[converted]"));
- c = TRUE;
+ c = true;
}
if (device) {
STRCAT(IObuff, _("[Device]"));
- c = TRUE;
+ c = true;
} else if (newfile) {
STRCAT(IObuff, new_file_message());
c = true;
}
if (no_eol) {
msg_add_eol();
- c = TRUE;
+ c = true;
}
// may add [unix/dos/mac]
if (msg_add_fileformat(fileformat)) {
- c = TRUE;
+ c = true;
}
msg_add_lines(c, (long)lnum, nchars); // add line/char count
if (!shortmess(SHM_WRITE)) {
@@ -3531,11 +3408,11 @@ restore_backup:
}
}
- set_keep_msg(msg_trunc_attr((char *)IObuff, false, 0), 0);
+ set_keep_msg(msg_trunc_attr(IObuff, false, 0), 0);
}
- /* When written everything correctly: reset 'modified'. Unless not
- * writing to the original file and '+' is not in 'cpoptions'. */
+ // When written everything correctly: reset 'modified'. Unless not
+ // writing to the original file and '+' is not in 'cpoptions'.
if (reset_changed && whole && !append
&& !write_info.bw_conv_error
&& (overwriting || vim_strchr(p_cpo, CPO_PLUS) != NULL)) {
@@ -3550,10 +3427,8 @@ restore_backup:
u_update_save_nr(buf);
}
- /*
- * If written to the current file, update the timestamp of the swap file
- * and reset the BF_WRITE_MASK flags. Also sets buf->b_mtime.
- */
+ // If written to the current file, update the timestamp of the swap file
+ // and reset the BF_WRITE_MASK flags. Also sets buf->b_mtime.
if (overwriting) {
ml_timestamp(buf);
if (append) {
@@ -3563,22 +3438,18 @@ restore_backup:
}
}
- /*
- * If we kept a backup until now, and we are in patch mode, then we make
- * the backup file our 'original' file.
- */
+ // If we kept a backup until now, and we are in patch mode, then we make
+ // the backup file our 'original' file.
if (*p_pm && dobackup) {
- char *const org = modname(fname, (char *)p_pm, false);
+ char *const org = modname(fname, p_pm, false);
if (backup != NULL) {
- /*
- * If the original file does not exist yet
- * the current backup file becomes the original file
- */
+ // If the original file does not exist yet
+ // the current backup file becomes the original file
if (org == NULL) {
emsg(_("E205: Patchmode: can't save original file"));
- } else if (!os_path_exists((char_u *)org)) {
- vim_rename((char_u *)backup, (char_u *)org);
+ } else if (!os_path_exists(org)) {
+ vim_rename(backup, org);
XFREE_CLEAR(backup); // don't delete the file
#ifdef UNIX
os_file_settime(org,
@@ -3586,12 +3457,9 @@ restore_backup:
(double)file_info_old.stat.st_mtim.tv_sec);
#endif
}
- }
- /*
- * If there is no backup file, remember that a (new) file was
- * created.
- */
- else {
+ } else {
+ // If there is no backup file, remember that a (new) file was
+ // created.
int empty_fd;
if (org == NULL
@@ -3609,9 +3477,7 @@ restore_backup:
}
}
- /*
- * Remove the backup unless 'backup' option is set
- */
+ // Remove the backup unless 'backup' option is set
if (!p_bk && backup != NULL
&& !write_info.bw_conv_error
&& os_remove(backup) != 0) {
@@ -3620,11 +3486,9 @@ restore_backup:
goto nofail;
- /*
- * Finish up. We get here either after failure or success.
- */
+ // Finish up. We get here either after failure or success.
fail:
- --no_wait_return; // may wait for return now
+ no_wait_return--; // may wait for return now
nofail:
// Done saving, we accept changed buffer warnings again
@@ -3636,22 +3500,20 @@ nofail:
}
xfree(fenc_tofree);
xfree(write_info.bw_conv_buf);
-#ifdef HAVE_ICONV
if (write_info.bw_iconv_fd != (iconv_t)-1) {
iconv_close(write_info.bw_iconv_fd);
write_info.bw_iconv_fd = (iconv_t)-1;
}
-#endif
#ifdef HAVE_ACL
- mch_free_acl(acl);
+ os_free_acl(acl);
#endif
if (errmsg != NULL) {
// - 100 to save some space for further error message
#ifndef UNIX
- add_quoted_fname((char *)IObuff, IOSIZE - 100, buf, (const char *)sfname);
+ add_quoted_fname(IObuff, IOSIZE - 100, buf, (const char *)sfname);
#else
- add_quoted_fname((char *)IObuff, IOSIZE - 100, buf, (const char *)fname);
+ add_quoted_fname(IObuff, IOSIZE - 100, buf, (const char *)fname);
#endif
if (errnum != NULL) {
if (errmsgarg != 0) {
@@ -3687,15 +3549,13 @@ nofail:
}
msg_scroll = msg_save;
- /*
- * When writing the whole file and 'undofile' is set, also write the undo
- * file.
- */
+ // When writing the whole file and 'undofile' is set, also write the undo
+ // file.
if (retval == OK && write_undo_file) {
- char hash[UNDO_HASH_SIZE];
+ uint8_t hash[UNDO_HASH_SIZE];
- sha256_finish(&sha_ctx, (char_u *)hash);
- u_write_undo(NULL, false, buf, (char_u *)hash);
+ sha256_finish(&sha_ctx, hash);
+ u_write_undo(NULL, false, buf, hash);
}
if (!should_abort(retval)) {
@@ -3703,31 +3563,29 @@ nofail:
curbuf->b_no_eol_lnum = 0; // in case it was set by the previous read
- /*
- * Apply POST autocommands.
- * Careful: The autocommands may call buf_write() recursively!
- */
+ // Apply POST autocommands.
+ // Careful: The autocommands may call buf_write() recursively!
aucmd_prepbuf(&aco, buf);
if (append) {
apply_autocmds_exarg(EVENT_FILEAPPENDPOST, fname, fname,
- FALSE, curbuf, eap);
+ false, curbuf, eap);
} else if (filtering) {
apply_autocmds_exarg(EVENT_FILTERWRITEPOST, NULL, fname,
- FALSE, curbuf, eap);
+ false, curbuf, eap);
} else if (reset_changed && whole) {
apply_autocmds_exarg(EVENT_BUFWRITEPOST, fname, fname,
- FALSE, curbuf, eap);
+ false, curbuf, eap);
} else {
apply_autocmds_exarg(EVENT_FILEWRITEPOST, fname, fname,
- FALSE, curbuf, eap);
+ false, curbuf, eap);
}
// restore curwin/curbuf and a few other things
aucmd_restbuf(&aco);
if (aborting()) { // autocmds may abort script processing
- retval = FALSE;
+ retval = false;
}
}
@@ -3741,7 +3599,7 @@ nofail:
/// 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)
+static int set_rw_fname(char *fname, char *sfname)
{
buf_T *buf = curbuf;
@@ -3759,7 +3617,7 @@ static int set_rw_fname(char_u *fname, char_u *sfname)
return FAIL;
}
- if (setfname(curbuf, (char *)fname, (char *)sfname, false) == OK) {
+ if (setfname(curbuf, fname, sfname, false) == OK) {
curbuf->b_flags |= BF_NOTEDITED;
}
@@ -3832,22 +3690,22 @@ static bool msg_add_fileformat(int eol_type)
/// Append line and character count to IObuff.
void msg_add_lines(int insert_space, long lnum, off_T nchars)
{
- char_u *p;
+ char *p;
- p = IObuff + STRLEN(IObuff);
+ p = IObuff + strlen(IObuff);
if (insert_space) {
*p++ = ' ';
}
if (shortmess(SHM_LINES)) {
- vim_snprintf((char *)p, (size_t)(IOSIZE - (p - IObuff)), "%" PRId64 "L, %" PRId64 "B",
+ vim_snprintf(p, (size_t)(IOSIZE - (p - IObuff)), "%" PRId64 "L, %" PRId64 "B",
(int64_t)lnum, (int64_t)nchars);
} else {
- vim_snprintf((char *)p, (size_t)(IOSIZE - (p - IObuff)),
+ vim_snprintf(p, (size_t)(IOSIZE - (p - IObuff)),
NGETTEXT("%" PRId64 " line, ", "%" PRId64 " lines, ", lnum),
(int64_t)lnum);
- p += STRLEN(p);
- vim_snprintf((char *)p, (size_t)(IOSIZE - (p - IObuff)),
+ p += strlen(p);
+ vim_snprintf(p, (size_t)(IOSIZE - (p - IObuff)),
NGETTEXT("%" PRId64 " byte", "%" PRId64 " bytes", nchars),
(int64_t)nchars);
}
@@ -3890,7 +3748,7 @@ static bool time_differs(const FileInfo *file_info, long mtime, long mtime_ns) F
|| file_info->stat.st_mtim.tv_sec - mtime > 1
|| mtime - file_info->stat.st_mtim.tv_sec > 1;
#else
- || (long)file_info->stat.st_mtim.tv_sec != mtime;
+ || file_info->stat.st_mtim.tv_sec != mtime;
#endif
}
@@ -3901,35 +3759,29 @@ static bool time_differs(const FileInfo *file_info, long mtime, long mtime_ns) F
static int buf_write_bytes(struct bw_info *ip)
{
int wlen;
- char_u *buf = ip->bw_buf; // data to write
+ char *buf = ip->bw_buf; // data to write
int len = ip->bw_len; // length of data
#ifdef HAS_BW_FLAGS
int flags = ip->bw_flags; // extra flags
#endif
- /*
- * Skip conversion when writing the BOM.
- */
+ // Skip conversion when writing the BOM.
if (!(flags & FIO_NOCONVERT)) {
- char_u *p;
+ char *p;
unsigned c;
int n;
if (flags & FIO_UTF8) {
- /*
- * Convert latin1 in the buffer to UTF-8 in the file.
- */
+ // 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], (char *)p);
+ p += utf_char2bytes((uint8_t)buf[wlen], p);
}
buf = ip->bw_conv_buf;
len = (int)(p - ip->bw_conv_buf);
} else if (flags & (FIO_UCS4 | FIO_UTF16 | FIO_UCS2 | FIO_LATIN1)) {
- /*
- * Convert UTF-8 bytes in the buffer to UCS-2, UCS-4, UTF-16 or
- * Latin1 chars in the file.
- */
+ // Convert UTF-8 bytes in the buffer to UCS-2, UCS-4, UTF-16 or
+ // Latin1 chars in the file.
if (flags & FIO_LATIN1) {
p = buf; // translate in-place (can only get shorter)
} else {
@@ -3939,19 +3791,19 @@ static int buf_write_bytes(struct bw_info *ip)
if (wlen == 0 && ip->bw_restlen != 0) {
int l;
- /* Use remainder of previous call. Append the start of
- * buf[] to get a full sequence. Might still be too
- * short! */
+ // Use remainder of previous call. Append the start of
+ // buf[] to get a full sequence. Might still be too
+ // short!
l = CONV_RESTLEN - ip->bw_restlen;
if (l > len) {
l = len;
}
memmove(ip->bw_rest + ip->bw_restlen, buf, (size_t)l);
- n = utf_ptr2len_len(ip->bw_rest, ip->bw_restlen + l);
+ n = utf_ptr2len_len((char *)ip->bw_rest, ip->bw_restlen + l);
if (n > ip->bw_restlen + len) {
- /* We have an incomplete byte sequence at the end to
- * be written. We can't convert it without the
- * remaining bytes. Keep them for the next call. */
+ // We have an incomplete byte sequence at the end to
+ // be written. We can't convert it without the
+ // remaining bytes. Keep them for the next call.
if (ip->bw_restlen + len > CONV_RESTLEN) {
return FAIL;
}
@@ -3975,9 +3827,9 @@ static int buf_write_bytes(struct bw_info *ip)
} else {
n = utf_ptr2len_len(buf + wlen, len - wlen);
if (n > len - wlen) {
- /* We have an incomplete byte sequence at the end to
- * be written. We can't convert it without the
- * remaining bytes. Keep them for the next call. */
+ // We have an incomplete byte sequence at the end to
+ // be written. We can't convert it without the
+ // remaining bytes. Keep them for the next call.
if (len - wlen > CONV_RESTLEN) {
return FAIL;
}
@@ -3987,18 +3839,18 @@ static int buf_write_bytes(struct bw_info *ip)
break;
}
if (n > 1) {
- c = (unsigned)utf_ptr2char((char *)buf + wlen);
+ c = (unsigned)utf_ptr2char(buf + wlen);
} else {
- c = buf[wlen];
+ c = (uint8_t)buf[wlen];
}
}
if (ucs2bytes(c, &p, flags) && !ip->bw_conv_error) {
- ip->bw_conv_error = TRUE;
+ ip->bw_conv_error = true;
ip->bw_conv_error_lnum = ip->bw_start_lnum;
}
if (c == NL) {
- ++ip->bw_start_lnum;
+ ip->bw_start_lnum++;
}
}
if (flags & FIO_LATIN1) {
@@ -4009,7 +3861,6 @@ static int buf_write_bytes(struct bw_info *ip)
}
}
-#ifdef HAVE_ICONV
if (ip->bw_iconv_fd != (iconv_t)-1) {
const char *from;
size_t fromlen;
@@ -4020,21 +3871,21 @@ static int buf_write_bytes(struct bw_info *ip)
if (ip->bw_restlen > 0) {
char *fp;
- /* 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. */
+ // 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 = (size_t)len + (size_t)ip->bw_restlen;
- fp = (char *)ip->bw_conv_buf + ip->bw_conv_buflen - fromlen;
+ fp = 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);
from = fp;
tolen = ip->bw_conv_buflen - fromlen;
} else {
- from = (const char *)buf;
+ from = buf;
fromlen = (size_t)len;
tolen = ip->bw_conv_buflen;
}
- to = (char *)ip->bw_conv_buf;
+ to = ip->bw_conv_buf;
if (ip->bw_first) {
size_t save_len = tolen;
@@ -4042,23 +3893,20 @@ static int buf_write_bytes(struct bw_info *ip)
// output the initial shift state sequence
(void)iconv(ip->bw_iconv_fd, NULL, NULL, &to, &tolen);
- /* There is a bug in iconv() on Linux (which appears to be
- * wide-spread) which sets "to" to NULL and messes up "tolen".
- */
+ // There is a bug in iconv() on Linux (which appears to be
+ // wide-spread) which sets "to" to NULL and messes up "tolen".
if (to == NULL) {
- to = (char *)ip->bw_conv_buf;
+ to = ip->bw_conv_buf;
tolen = save_len;
}
- ip->bw_first = FALSE;
+ ip->bw_first = false;
}
- /*
- * If iconv() has an error or there is not enough room, fail.
- */
+ // If iconv() has an error or there is not enough room, fail.
if ((iconv(ip->bw_iconv_fd, (void *)&from, &fromlen, &to, &tolen)
== (size_t)-1 && ICONV_ERRNO != ICONV_EINVAL)
|| fromlen > CONV_RESTLEN) {
- ip->bw_conv_error = TRUE;
+ ip->bw_conv_error = true;
return FAIL;
}
@@ -4069,9 +3917,8 @@ static int buf_write_bytes(struct bw_info *ip)
ip->bw_restlen = (int)fromlen;
buf = ip->bw_conv_buf;
- len = (int)((char_u *)to - ip->bw_conv_buf);
+ len = (int)(to - ip->bw_conv_buf);
}
-#endif
}
if (ip->bw_fd < 0) {
@@ -4089,9 +3936,9 @@ static int buf_write_bytes(struct bw_info *ip)
/// @param flags FIO_ flags that specify which encoding to use
///
/// @return true for an error, false when it's OK.
-static bool ucs2bytes(unsigned c, char_u **pp, int flags) FUNC_ATTR_NONNULL_ALL
+static bool ucs2bytes(unsigned c, char **pp, int flags) FUNC_ATTR_NONNULL_ALL
{
- char_u *p = *pp;
+ char_u *p = (char_u *)(*pp);
bool error = false;
int cc;
@@ -4110,8 +3957,8 @@ static bool ucs2bytes(unsigned c, char_u **pp, int flags) FUNC_ATTR_NONNULL_ALL
} else if (flags & (FIO_UCS2 | FIO_UTF16)) {
if (c >= 0x10000) {
if (flags & FIO_UTF16) {
- /* Make two words, ten bits of the character in each. First
- * word is 0xd800 - 0xdbff, second one 0xdc00 - 0xdfff */
+ // Make two words, ten bits of the character in each. First
+ // word is 0xd800 - 0xdbff, second one 0xdc00 - 0xdfff
c -= 0x10000;
if (c >= 0x100000) {
error = true;
@@ -4145,7 +3992,7 @@ static bool ucs2bytes(unsigned c, char_u **pp, int flags) FUNC_ATTR_NONNULL_ALL
}
}
- *pp = p;
+ *pp = (char *)p;
return error;
}
@@ -4155,15 +4002,15 @@ static bool ucs2bytes(unsigned c, char_u **pp, int flags) FUNC_ATTR_NONNULL_ALL
/// @param fenc file encoding to check
///
/// @return true if conversion is required
-static bool need_conversion(const char_u *fenc)
+static bool need_conversion(const char *fenc)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
int same_encoding;
int enc_flags;
int fenc_flags;
- if (*fenc == NUL || STRCMP(p_enc, fenc) == 0) {
- same_encoding = TRUE;
+ if (*fenc == NUL || strcmp(p_enc, fenc) == 0) {
+ same_encoding = true;
fenc_flags = 0;
} else {
// Ignore difference between "ansi" and "latin1", "ucs-4" and
@@ -4187,7 +4034,7 @@ static bool need_conversion(const char_u *fenc)
/// use 'encoding'.
///
/// @param name string to check for encoding
-static int get_fio_flags(const char_u *name)
+static int get_fio_flags(const char *name)
{
int prop;
@@ -4228,8 +4075,9 @@ static int get_fio_flags(const char_u *name)
///
/// @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)
+static char *check_for_bom(const char *p_in, long size, int *lenp, int flags)
{
+ const uint8_t *p = (const uint8_t *)p_in;
char *name = NULL;
int len = 2;
@@ -4265,16 +4113,16 @@ static char_u *check_for_bom(char_u *p, long size, int *lenp, int flags)
}
*lenp = len;
- return (char_u *)name;
+ return name;
}
/// 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)
+static int make_bom(char_u *buf, char *name)
{
int flags;
- char_u *p;
+ char *p;
flags = get_fio_flags(name);
@@ -4289,21 +4137,21 @@ static int make_bom(char_u *buf, char_u *name)
buf[2] = 0xbf;
return 3;
}
- p = buf;
+ p = (char *)buf;
(void)ucs2bytes(0xfeff, &p, flags);
- return (int)(p - buf);
+ return (int)((char_u *)p - buf);
}
/// Shorten filename of a buffer.
///
-/// @param force when TRUE: Use full path from now on for files currently being
+/// @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.
+/// 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)
+void shorten_buf_fname(buf_T *buf, char *dirname, int force)
{
char *p;
@@ -4312,11 +4160,11 @@ void shorten_buf_fname(buf_T *buf, char_u *dirname, int force)
&& !path_with_url(buf->b_fname)
&& (force
|| buf->b_sfname == NULL
- || path_is_absolute((char_u *)buf->b_sfname))) {
+ || path_is_absolute(buf->b_sfname))) {
if (buf->b_sfname != buf->b_ffname) {
XFREE_CLEAR(buf->b_sfname);
}
- p = (char *)path_shorten_fname((char_u *)buf->b_ffname, dirname);
+ p = path_shorten_fname(buf->b_ffname, dirname);
if (p != NULL) {
buf->b_sfname = xstrdup(p);
buf->b_fname = buf->b_sfname;
@@ -4330,11 +4178,11 @@ void shorten_buf_fname(buf_T *buf, char_u *dirname, int force)
/// Shorten filenames for all buffers.
void shorten_fnames(int force)
{
- char_u dirname[MAXPATHL];
+ char dirname[MAXPATHL];
os_dirname(dirname, MAXPATHL);
FOR_ALL_BUFFERS(buf) {
- shorten_buf_fname(buf, dirname, force);
+ shorten_buf_fname(buf, (char *)dirname, force);
// Always make the swap file name a full path, a "nofile" buffer may
// also have a swap file.
@@ -4379,18 +4227,18 @@ char *modname(const char *fname, const char *ext, bool prepend_dot)
// (we need the full path in case :cd is used).
if (fname == NULL || *fname == NUL) {
retval = xmalloc(MAXPATHL + extlen + 3); // +3 for PATHSEP, "_" (Win), NUL
- if (os_dirname((char_u *)retval, MAXPATHL) == FAIL
+ if (os_dirname(retval, MAXPATHL) == FAIL
|| strlen(retval) == 0) {
xfree(retval);
return NULL;
}
add_pathsep(retval);
fnamelen = strlen(retval);
- prepend_dot = FALSE; // nothing to prepend a dot to
+ prepend_dot = false; // nothing to prepend a dot to
} else {
fnamelen = strlen(fname);
retval = xmalloc(fnamelen + extlen + 3);
- strcpy(retval, fname);
+ strcpy(retval, fname); // NOLINT(runtime/printf)
}
// Search backwards until we hit a '/', '\' or ':'.
@@ -4408,12 +4256,11 @@ char *modname(const char *fname, const char *ext, bool prepend_dot)
ptr[BASENAMELEN] = '\0';
}
- char *s;
- s = ptr + strlen(ptr);
+ char *s = ptr + strlen(ptr);
// Append the extension.
// ext can start with '.' and cannot exceed 3 more characters.
- strcpy(s, ext);
+ strcpy(s, ext); // NOLINT(runtime/printf)
char *e;
// Prepend the dot if needed.
@@ -4447,7 +4294,8 @@ char *modname(const char *fname, const char *ext, bool prepend_dot)
/// @param fp file to read from
///
/// @return true for EOF or error
-bool vim_fgets(char_u *buf, int size, FILE *fp) FUNC_ATTR_NONNULL_ALL
+bool vim_fgets(char *buf, int size, FILE *fp)
+ FUNC_ATTR_NONNULL_ALL
{
char *retval;
@@ -4456,7 +4304,7 @@ bool vim_fgets(char_u *buf, int size, FILE *fp) FUNC_ATTR_NONNULL_ALL
do {
errno = 0;
- retval = fgets((char *)buf, size, fp);
+ retval = fgets(buf, size, fp);
} while (retval == NULL && errno == EINTR && ferror(fp));
if (buf[size - 2] != NUL && buf[size - 2] != '\n') {
@@ -4608,7 +4456,7 @@ int put_time(FILE *fd, time_t time_)
/// function will (attempts to?) copy the file across if rename fails -- webb
///
/// @return -1 for failure, 0 for success
-int vim_rename(const char_u *from, const char_u *to)
+int vim_rename(const char *from, const char *to)
FUNC_ATTR_NONNULL_ALL
{
int fd_in;
@@ -4622,13 +4470,11 @@ int vim_rename(const char_u *from, const char_u *to)
#endif
bool use_tmp_file = false;
- /*
- * When the names are identical, there is nothing to do. When they refer
- * 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 *)from), path_tail((char *)to))
+ // When the names are identical, there is nothing to do. When they refer
+ // to the same file (ignoring case and slash/backslash differences) but
+ // the file name differs we need to go through a temp file.
+ if (path_fnamecmp(from, to) == 0) {
+ if (p_fic && (strcmp(path_tail((char *)from), path_tail((char *)to))
!= 0)) {
use_tmp_file = true;
} else {
@@ -4652,19 +4498,17 @@ int vim_rename(const char_u *from, const char_u *to)
}
if (use_tmp_file) {
- char_u tempname[MAXPATHL + 1];
+ char tempname[MAXPATHL + 1];
- /*
- * Find a name that doesn't exist and is in the same directory.
- * Rename "from" to "tempname" and then rename "tempname" to "to".
- */
- if (STRLEN(from) >= MAXPATHL - 5) {
+ // Find a name that doesn't exist and is in the same directory.
+ // Rename "from" to "tempname" and then rename "tempname" to "to".
+ if (strlen(from) >= MAXPATHL - 5) {
return -1;
}
STRCPY(tempname, from);
for (n = 123; n < 99999; n++) {
- char *tail = path_tail((char *)tempname);
- snprintf(tail, (size_t)((MAXPATHL + 1) - (tail - (char *)tempname - 1)), "%d", n);
+ char *tail = path_tail(tempname);
+ snprintf(tail, (size_t)((MAXPATHL + 1) - (tail - tempname - 1)), "%d", n);
if (!os_path_exists(tempname)) {
if (os_rename(from, tempname) == OK) {
@@ -4684,33 +4528,27 @@ int vim_rename(const char_u *from, const char_u *to)
return -1;
}
- /*
- * Delete the "to" file, this is required on some systems to make the
- * os_rename() work, on other systems it makes sure that we don't have
- * two files when the os_rename() fails.
- */
+ // Delete the "to" file, this is required on some systems to make the
+ // os_rename() work, on other systems it makes sure that we don't have
+ // two files when the os_rename() fails.
os_remove((char *)to);
- /*
- * First try a normal rename, return if it works.
- */
+ // First try a normal rename, return if it works.
if (os_rename(from, to) == OK) {
return 0;
}
- /*
- * Rename() failed, try copying the file.
- */
- perm = os_getperm((const char *)from);
+ // Rename() failed, try copying the file.
+ perm = os_getperm(from);
#ifdef HAVE_ACL
// For systems that support ACL: get the ACL from the original file.
- acl = mch_get_acl(from);
+ acl = os_get_acl(from);
#endif
fd_in = os_open((char *)from, O_RDONLY, 0);
if (fd_in < 0) {
#ifdef HAVE_ACL
- mch_free_acl(acl);
+ os_free_acl(acl);
#endif
return -1;
}
@@ -4721,7 +4559,7 @@ int vim_rename(const char_u *from, const char_u *to)
if (fd_out < 0) {
close(fd_in);
#ifdef HAVE_ACL
- mch_free_acl(acl);
+ os_free_acl(acl);
#endif
return -1;
}
@@ -4733,7 +4571,7 @@ int vim_rename(const char_u *from, const char_u *to)
close(fd_out);
close(fd_in);
#ifdef HAVE_ACL
- mch_free_acl(acl);
+ os_free_acl(acl);
#endif
return -1;
}
@@ -4755,11 +4593,11 @@ int vim_rename(const char_u *from, const char_u *to)
to = from;
}
#ifndef UNIX // For Unix os_open() already set the permission.
- os_setperm((const char *)to, perm);
+ os_setperm(to, perm);
#endif
#ifdef HAVE_ACL
- mch_set_acl(to, acl);
- mch_free_acl(acl);
+ os_set_acl(to, acl);
+ os_free_acl(acl);
#endif
if (errmsg != NULL) {
semsg(errmsg, to);
@@ -4769,7 +4607,7 @@ int vim_rename(const char_u *from, const char_u *to)
return 0;
}
-static int already_warned = FALSE;
+static int already_warned = false;
/// Check if any not hidden buffer has been changed.
/// Postpone the check if there are characters in the stuff buffer, a global
@@ -4778,7 +4616,7 @@ static int already_warned = FALSE;
///
/// @param focus called for GUI focus event
///
-/// @return TRUE if some message was written (screen should be redrawn and cursor positioned).
+/// @return true if some message was written (screen should be redrawn and cursor positioned).
int check_timestamps(int focus)
{
int didit = 0;
@@ -4786,15 +4624,15 @@ int check_timestamps(int focus)
// Don't check timestamps while system() or another low-level function may
// cause us to lose and gain focus.
if (no_check_timestamps > 0) {
- return FALSE;
+ return false;
}
// Avoid doing a check twice. The OK/Reload dialog can cause a focus
// event and we would keep on checking if the file is steadily growing.
// Do check again after typing something.
if (focus && did_check_timestamps) {
- need_check_timestamps = TRUE;
- return FALSE;
+ need_check_timestamps = true;
+ return false;
}
if (!stuff_empty() || global_busy || !typebuf_typed()
@@ -4841,13 +4679,13 @@ static int move_lines(buf_T *frombuf, buf_T *tobuf)
buf_T *tbuf = curbuf;
int retval = OK;
linenr_T lnum;
- char_u *p;
+ char *p;
// Copy the lines in "frombuf" to "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, (char *)p, 0, false) == FAIL) {
+ p = xstrdup(ml_get_buf(frombuf, lnum, false));
+ if (ml_append(lnum - 1, p, 0, false) == FAIL) {
xfree(p);
retval = FAIL;
break;
@@ -4882,7 +4720,7 @@ int buf_check_timestamp(buf_T *buf)
FUNC_ATTR_NONNULL_ALL
{
int retval = 0;
- char_u *path;
+ char *path;
char *mesg = NULL;
char *mesg2 = "";
bool helpmesg = false;
@@ -4897,7 +4735,7 @@ int buf_check_timestamp(buf_T *buf)
uint64_t orig_size = buf->b_orig_size;
int orig_mode = buf->b_orig_mode;
static bool busy = false;
- char_u *s;
+ char *s;
char *reason;
bufref_T bufref;
@@ -4937,7 +4775,7 @@ int buf_check_timestamp(buf_T *buf)
buf_store_file_info(buf, &file_info);
}
- if (os_isdir((char_u *)buf->b_fname)) {
+ if (os_isdir(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) {
@@ -4972,12 +4810,12 @@ int buf_check_timestamp(buf_T *buf)
if (!bufref_valid(&bufref)) {
emsg(_("E246: FileChangedShell autocommand deleted buffer"));
}
- s = (char_u *)get_vim_var_str(VV_FCS_CHOICE);
- if (STRCMP(s, "reload") == 0 && *reason != 'd') {
+ s = get_vim_var_str(VV_FCS_CHOICE);
+ if (strcmp(s, "reload") == 0 && *reason != 'd') {
reload = RELOAD_NORMAL;
- } else if (STRCMP(s, "edit") == 0) {
+ } else if (strcmp(s, "edit") == 0) {
reload = RELOAD_DETECT;
- } else if (STRCMP(s, "ask") == 0) {
+ } else if (strcmp(s, "ask") == 0) {
n = false;
} else {
return 2;
@@ -5017,7 +4855,7 @@ int buf_check_timestamp(buf_T *buf)
}
}
} else if ((buf->b_flags & BF_NEW) && !(buf->b_flags & BF_NEW_W)
- && os_path_exists((char_u *)buf->b_ffname)) {
+ && os_path_exists(buf->b_ffname)) {
retval = 1;
mesg = _("W13: Warning: File \"%s\" has been created after editing started");
buf->b_flags |= BF_NEW_W;
@@ -5025,11 +4863,11 @@ int buf_check_timestamp(buf_T *buf)
}
if (mesg != NULL) {
- path = (char_u *)home_replace_save(buf, buf->b_fname);
+ path = home_replace_save(buf, buf->b_fname);
if (!helpmesg) {
mesg2 = "";
}
- const size_t tbuf_len = STRLEN(path) + STRLEN(mesg) + STRLEN(mesg2) + 2;
+ const size_t tbuf_len = strlen(path) + strlen(mesg) + strlen(mesg2) + 2;
char *const tbuf = xmalloc(tbuf_len);
snprintf(tbuf, tbuf_len, mesg, path);
// Set warningmsg here, before the unimportant and output-specific
@@ -5040,8 +4878,8 @@ int buf_check_timestamp(buf_T *buf)
xstrlcat(tbuf, "\n", tbuf_len - 1);
xstrlcat(tbuf, mesg2, tbuf_len - 1);
}
- switch (do_dialog(VIM_WARNING, (char_u *)_("Warning"), (char_u *)tbuf,
- (char_u *)_("&OK\n&Load File\nLoad File &and Options"),
+ switch (do_dialog(VIM_WARNING, _("Warning"), tbuf,
+ _("&OK\n&Load File\nLoad File &and Options"),
1, NULL, true)) {
case 2:
reload = RELOAD_NORMAL;
@@ -5066,7 +4904,7 @@ int buf_check_timestamp(buf_T *buf)
}
msg_clr_eos();
(void)msg_end();
- if (emsg_silent == 0) {
+ if (emsg_silent == 0 && !in_assert_fails) {
ui_flush();
// give the user some time to think about it
os_delay(1004L, true);
@@ -5075,7 +4913,7 @@ int buf_check_timestamp(buf_T *buf)
redraw_cmdline = false;
}
}
- already_warned = TRUE;
+ already_warned = true;
}
xfree(path);
@@ -5117,7 +4955,7 @@ void buf_reload(buf_T *buf, int orig_mode, bool reload_options)
aco_save_T aco;
int flags = READ_NEW;
- // set curwin/curbuf for "buf" and save some things
+ // Set curwin/curbuf for "buf" and save some things.
aucmd_prepbuf(&aco, buf);
// Unless reload_options is set, we only want to read the text from the
@@ -5264,11 +5102,11 @@ void write_lnum_adjust(linenr_T offset)
#if defined(BACKSLASH_IN_FILENAME)
/// Convert all backslashes in fname to forward slashes in-place,
/// unless when it looks like a URL.
-void forward_slash(char_u *fname)
+void forward_slash(char *fname)
{
- char_u *p;
+ char *p;
- if (path_with_url((const char *)fname)) {
+ if (path_with_url(fname)) {
return;
}
for (p = fname; *p != NUL; p++) {
@@ -5281,6 +5119,9 @@ void forward_slash(char_u *fname)
/// Path to Nvim's own temp dir. Ends in a slash.
static char *vim_tempdir = NULL;
+#if defined(HAVE_FLOCK) && defined(HAVE_DIRFD)
+DIR *vim_tempdir_dp = NULL; ///< File descriptor of temp dir
+#endif
/// Creates a directory for private use by this instance of Nvim, trying each of
/// `TEMP_DIR_NAMES` until one succeeds.
@@ -5306,8 +5147,8 @@ static void vim_mktempdir(void)
mode_t umask_save = umask(0077);
for (size_t i = 0; i < ARRAY_SIZE(temp_dirs); i++) {
// 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)) {
+ expand_env((char *)temp_dirs[i], tmp, TEMP_FILE_PATH_MAXLEN - 64);
+ if (!os_isdir(tmp)) {
continue;
}
@@ -5317,7 +5158,7 @@ static void vim_mktempdir(void)
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);
+ bool isdir = os_isdir(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);
@@ -5353,10 +5194,9 @@ static void vim_mktempdir(void)
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(path);
}
+ // Couldn't set `vim_tempdir` to `path` so remove created directory.
+ os_rmdir(path);
}
(void)umask(umask_save);
}
@@ -5423,7 +5263,7 @@ int delete_recursive(const char *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]);
+ vim_snprintf(NameBuff, MAXPATHL, "%s/%s", exp, ((char **)ga.ga_data)[i]);
if (delete_recursive((const char *)NameBuff) != 0) {
// Remember the failure but continue deleting any further
// entries.
@@ -5446,15 +5286,50 @@ int delete_recursive(const char *name)
return result;
}
+#if defined(HAVE_FLOCK) && defined(HAVE_DIRFD)
+/// Open temporary directory and take file lock to prevent
+/// to be auto-cleaned.
+static void vim_opentempdir(void)
+{
+ if (vim_tempdir_dp != NULL) {
+ return;
+ }
+
+ DIR *dp = opendir(vim_tempdir);
+ if (dp == NULL) {
+ return;
+ }
+
+ vim_tempdir_dp = dp;
+ flock(dirfd(vim_tempdir_dp), LOCK_SH);
+}
+
+/// Close temporary directory - it automatically release file lock.
+static void vim_closetempdir(void)
+{
+ if (vim_tempdir_dp == NULL) {
+ return;
+ }
+
+ closedir(vim_tempdir_dp);
+ vim_tempdir_dp = NULL;
+}
+#endif
+
/// Delete the temp directory and all files it contains.
void vim_deltempdir(void)
{
- if (vim_tempdir != NULL) {
- // remove the trailing path separator
- path_tail(vim_tempdir)[-1] = NUL;
- delete_recursive(vim_tempdir);
- XFREE_CLEAR(vim_tempdir);
+ if (vim_tempdir == NULL) {
+ return;
}
+
+#if defined(HAVE_FLOCK) && defined(HAVE_DIRFD)
+ vim_closetempdir();
+#endif
+ // remove the trailing path separator
+ path_tail(vim_tempdir)[-1] = NUL;
+ delete_recursive(vim_tempdir);
+ XFREE_CLEAR(vim_tempdir);
}
/// Gets path to Nvim's own temp dir (ending with slash).
@@ -5479,12 +5354,16 @@ char *vim_gettempdir(void)
static bool vim_settempdir(char *tempdir)
{
char *buf = verbose_try_malloc(MAXPATHL + 2);
- if (!buf) {
+ if (buf == NULL) {
return false;
}
+
vim_FullName(tempdir, buf, MAXPATHL, false);
add_pathsep(buf);
vim_tempdir = xstrdup(buf);
+#if defined(HAVE_FLOCK) && defined(HAVE_DIRFD)
+ vim_opentempdir();
+#endif
xfree(buf);
return true;
}
@@ -5495,7 +5374,7 @@ static bool vim_settempdir(char *tempdir)
///
/// @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)
+char *vim_tempname(void)
{
// Temp filename counter.
static uint64_t temp_count;
@@ -5507,10 +5386,10 @@ char_u *vim_tempname(void)
// There is no need to check if the file exists, because we own the directory
// and nobody else creates a file in it.
- char_u template[TEMP_FILE_PATH_MAXLEN];
- snprintf((char *)template, TEMP_FILE_PATH_MAXLEN,
+ char template[TEMP_FILE_PATH_MAXLEN];
+ snprintf(template, TEMP_FILE_PATH_MAXLEN,
"%s%" PRIu64, tempdir, temp_count++);
- return vim_strsave(template);
+ return xstrdup(template);
}
/// Tries matching a filename with a "pattern" ("prog" is NULL), or use the
@@ -5542,12 +5421,10 @@ bool match_file_pat(char *pattern, regprog_T **prog, char *fname, char *sfname,
}
}
- /*
- * Try for a match with the pattern with:
- * 1. the full file name, when the pattern has a '/'.
- * 2. the short file name, when the pattern has a '/'.
- * 3. the tail of the file name, when the pattern has no '/'.
- */
+ // Try for a match with the pattern with:
+ // 1. the full file name, when the pattern has a '/'.
+ // 2. the short file name, when the pattern has a '/'.
+ // 3. the tail of the file name, when the pattern has no '/'.
if (regmatch.regprog != NULL
&& ((allow_dirs
&& (vim_regexec(&regmatch, fname, (colnr_T)0)
@@ -5574,28 +5451,27 @@ bool match_file_pat(char *pattern, regprog_T **prog, char *fname, char *sfname,
/// @param ffname full file name
///
/// @return true if there was a match
-bool match_file_list(char_u *list, char_u *sfname, char_u *ffname)
+bool match_file_list(char *list, char *sfname, char *ffname)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1, 3)
{
- char_u buf[100];
- char_u *tail;
- char_u *regpat;
+ char buf[100];
+ char *tail;
+ char *regpat;
char allow_dirs;
bool match;
char *p;
- tail = (char_u *)path_tail((char *)sfname);
+ tail = path_tail(sfname);
// try all patterns in 'wildignore'
- p = (char *)list;
+ p = list;
while (*p) {
- copy_option_part(&p, (char *)buf, ARRAY_SIZE(buf), ",");
- regpat = (char_u *)file_pat_to_reg_pat((char *)buf, NULL, &allow_dirs, false);
+ copy_option_part(&p, buf, ARRAY_SIZE(buf), ",");
+ regpat = file_pat_to_reg_pat(buf, NULL, &allow_dirs, false);
if (regpat == NULL) {
break;
}
- match = match_file_pat((char *)regpat, NULL, (char *)ffname, (char *)sfname, (char *)tail,
- (int)allow_dirs);
+ match = match_file_pat(regpat, NULL, ffname, sfname, tail, (int)allow_dirs);
xfree(regpat);
if (match) {
return true;
@@ -5606,8 +5482,8 @@ bool match_file_list(char_u *list, char_u *sfname, char_u *ffname)
/// Convert the given pattern "pat" which has shell style wildcards in it, into
/// a regular expression, and return the result in allocated memory. If there
-/// is a directory path separator to be matched, then TRUE is put in
-/// allow_dirs, otherwise FALSE is put there -- webb.
+/// is a directory path separator to be matched, then true is put in
+/// allow_dirs, otherwise false is put there -- webb.
/// Handle backslashes before special characters, like "\*" and "\ ".
///
/// @param pat_end first char after pattern or NULL
@@ -5625,10 +5501,10 @@ char *file_pat_to_reg_pat(const char *pat, const char *pat_end, char *allow_dirs
bool add_dollar = true;
if (allow_dirs != NULL) {
- *allow_dirs = FALSE;
+ *allow_dirs = false;
}
if (pat_end == NULL) {
- pat_end = pat + STRLEN(pat);
+ pat_end = pat + strlen(pat);
}
if (pat_end == pat) {
@@ -5704,14 +5580,14 @@ char *file_pat_to_reg_pat(const char *pat, const char *pat_end, char *allow_dirs
// "\*" to "\\.*" e.g., "dir\*.c"
// "\?" to "\\." e.g., "dir\??.c"
// "\+" to "\+" e.g., "fileX\+.c"
- if ((vim_isfilec(p[1]) || p[1] == '*' || p[1] == '?')
+ if ((vim_isfilec((uint8_t)p[1]) || p[1] == '*' || p[1] == '?')
&& p[1] != '+') {
reg_pat[i++] = '[';
reg_pat[i++] = '\\';
reg_pat[i++] = '/';
reg_pat[i++] = ']';
if (allow_dirs != NULL) {
- *allow_dirs = TRUE;
+ *allow_dirs = true;
}
break;
}
@@ -5745,7 +5621,7 @@ char *file_pat_to_reg_pat(const char *pat, const char *pat_end, char *allow_dirs
&& (!no_bslash || *p != '\\')
#endif
) {
- *allow_dirs = TRUE;
+ *allow_dirs = true;
}
reg_pat[i++] = '\\';
reg_pat[i++] = *p;
@@ -5758,7 +5634,7 @@ char *file_pat_to_reg_pat(const char *pat, const char *pat_end, char *allow_dirs
reg_pat[i++] = '/';
reg_pat[i++] = ']';
if (allow_dirs != NULL) {
- *allow_dirs = TRUE;
+ *allow_dirs = true;
}
break;
#endif
diff --git a/src/nvim/fileio.h b/src/nvim/fileio.h
index 650977deac..dabcda5bf2 100644
--- a/src/nvim/fileio.h
+++ b/src/nvim/fileio.h
@@ -3,6 +3,7 @@
#include "nvim/buffer_defs.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/garray.h"
#include "nvim/os/os.h"
@@ -15,6 +16,7 @@
#define READ_KEEP_UNDO 0x20 // keep undo info
#define READ_FIFO 0x40 // read from fifo or socket
#define READ_NOWINENTER 0x80 // do not trigger BufWinEnter
+#define READ_NOFILE 0x100 // do not read a file, do trigger BufReadCmd
#define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y))
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index 8ce24fd378..7306131574 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -5,10 +5,15 @@
// fold.c: code for folding
+#include <assert.h>
#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
#include "nvim/buffer_updates.h"
#include "nvim/change.h"
#include "nvim/charset.h"
@@ -16,14 +21,17 @@
#include "nvim/diff.h"
#include "nvim/drawscreen.h"
#include "nvim/eval.h"
-#include "nvim/ex_docmd.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/ex_session.h"
#include "nvim/extmark.h"
#include "nvim/fold.h"
-#include "nvim/func_attr.h"
#include "nvim/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
#include "nvim/indent.h"
#include "nvim/mark.h"
+#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
@@ -35,6 +43,7 @@
#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
+#include "nvim/types.h"
#include "nvim/undo.h"
#include "nvim/vim.h"
@@ -57,9 +66,11 @@ typedef struct {
// folds too
} fold_T;
-#define FD_OPEN 0 // fold is open (nested ones can be closed)
-#define FD_CLOSED 1 // fold is closed
-#define FD_LEVEL 2 // depends on 'foldlevel' (nested folds too)
+enum {
+ FD_OPEN = 0, // fold is open (nested ones can be closed)
+ FD_CLOSED = 1, // fold is closed
+ FD_LEVEL = 2, // depends on 'foldlevel' (nested folds too)
+};
#define MAX_LEVEL 20 // maximum fold depth
@@ -111,7 +122,7 @@ static int prev_lnum_lvl = -1;
#define DONE_FOLD 2 // did find a fold
static size_t foldstartmarkerlen;
-static char_u *foldendmarker;
+static char *foldendmarker;
static size_t foldendmarkerlen;
// Exported folding functions. {{{1
@@ -394,7 +405,7 @@ void opFoldRange(pos_T firstpos, pos_T lastpos, int opening, int recurse, int ha
}
// Force a redraw to remove the Visual highlighting.
if (had_visual) {
- redraw_curbuf_later(INVERTED);
+ redraw_curbuf_later(UPD_INVERTED);
}
}
@@ -466,12 +477,15 @@ static void newFoldLevelWin(win_T *wp)
/// Apply 'foldlevel' to all folds that don't contain the cursor.
void foldCheckClose(void)
{
- if (*p_fcl != NUL) { // can only be "all" right now
- checkupdate(curwin);
- if (checkCloseRec(&curwin->w_folds, curwin->w_cursor.lnum,
- (int)curwin->w_p_fdl)) {
- changed_window_setting();
- }
+ if (*p_fcl == NUL) {
+ return;
+ }
+
+ // 'foldclose' can only be "all" right now
+ checkupdate(curwin);
+ if (checkCloseRec(&curwin->w_folds, curwin->w_cursor.lnum,
+ (int)curwin->w_p_fdl)) {
+ changed_window_setting();
}
}
@@ -721,7 +735,7 @@ void deleteFold(win_T *const wp, const linenr_T start, const linenr_T end, const
emsg(_(e_nofold));
// Force a redraw to remove the Visual highlighting.
if (had_visual) {
- redraw_buf_later(wp->w_buffer, INVERTED);
+ redraw_buf_later(wp->w_buffer, UPD_INVERTED);
}
} else {
// Deleting markers may make cursor column invalid
@@ -757,7 +771,7 @@ void clearFolding(win_T *win)
/// The changes in lines from top to bot (inclusive).
void foldUpdate(win_T *wp, linenr_T top, linenr_T bot)
{
- if (disable_fold_update || compl_busy || State & MODE_INSERT) {
+ if (disable_fold_update || State & MODE_INSERT) {
return;
}
@@ -819,7 +833,7 @@ void foldUpdateAfterInsert(void)
void foldUpdateAll(win_T *win)
{
win->w_foldinvalid = true;
- redraw_later(win, NOT_VALID);
+ redraw_later(win, UPD_NOT_VALID);
}
// foldMoveTo() {{{2
@@ -982,7 +996,7 @@ void foldAdjustVisual(void)
}
pos_T *start, *end;
- char_u *ptr;
+ char *ptr;
if (ltoreq(VIsual, curwin->w_cursor)) {
start = &VIsual;
@@ -994,15 +1008,18 @@ void foldAdjustVisual(void)
if (hasFolding(start->lnum, &start->lnum, NULL)) {
start->col = 0;
}
- if (hasFolding(end->lnum, NULL, &end->lnum)) {
- ptr = ml_get(end->lnum);
- end->col = (colnr_T)STRLEN(ptr);
- if (end->col > 0 && *p_sel == 'o') {
- end->col--;
- }
- // prevent cursor from moving on the trail byte
- mb_adjust_cursor();
+
+ if (!hasFolding(end->lnum, NULL, &end->lnum)) {
+ return;
+ }
+
+ ptr = ml_get(end->lnum);
+ end->col = (colnr_T)strlen(ptr);
+ if (end->col > 0 && *p_sel == 'o') {
+ end->col--;
}
+ // prevent cursor from moving on the trail byte
+ mb_adjust_cursor();
}
// cursor_foldstart() {{{2
@@ -1083,7 +1100,7 @@ static int foldLevelWin(win_T *wp, linenr_T lnum)
{
fold_T *fp;
linenr_T lnum_rel = lnum;
- int level = 0;
+ int level = 0;
// Recursively search for a fold that contains "lnum".
garray_T *gap = &wp->w_folds;
@@ -1104,10 +1121,12 @@ static int foldLevelWin(win_T *wp, linenr_T lnum)
/// Check if the folds in window "wp" are invalid and update them if needed.
static void checkupdate(win_T *wp)
{
- if (wp->w_foldinvalid) {
- foldUpdate(wp, (linenr_T)1, (linenr_T)MAXLNUM); // will update all
- wp->w_foldinvalid = false;
+ if (!wp->w_foldinvalid) {
+ return;
}
+
+ foldUpdate(wp, (linenr_T)1, (linenr_T)MAXLNUM); // will update all
+ wp->w_foldinvalid = false;
}
// setFoldRepeat() {{{2
@@ -1425,15 +1444,13 @@ static void foldMarkAdjustRecurse(win_T *wp, garray_T *gap, linenr_T line1, line
// 5. fold is below line1 and contains line2; need to
// correct nested folds too
if (amount == MAXLNUM) {
- foldMarkAdjustRecurse(wp, &fp->fd_nested, line1 - fp->fd_top,
- line2 - fp->fd_top, amount,
- amount_after + (fp->fd_top - top));
+ foldMarkAdjustRecurse(wp, &fp->fd_nested, 0, line2 - fp->fd_top,
+ amount, amount_after + (fp->fd_top - top));
fp->fd_len -= line2 - fp->fd_top + 1;
fp->fd_top = line1;
} else {
- foldMarkAdjustRecurse(wp, &fp->fd_nested, line1 - fp->fd_top,
- line2 - fp->fd_top, amount,
- amount_after - amount);
+ foldMarkAdjustRecurse(wp, &fp->fd_nested, 0, line2 - fp->fd_top,
+ amount, amount_after - amount);
fp->fd_len += amount_after - amount;
fp->fd_top += amount;
}
@@ -1514,23 +1531,25 @@ static bool check_closed(win_T *const wp, fold_T *const fp, bool *const use_leve
/// @param lnum_off offset for fp->fd_top
static void checkSmall(win_T *const wp, fold_T *const fp, const linenr_T lnum_off)
{
- if (fp->fd_small == kNone) {
- // Mark any nested folds to maybe-small
- setSmallMaybe(&fp->fd_nested);
+ if (fp->fd_small != kNone) {
+ return;
+ }
- if (fp->fd_len > wp->w_p_fml) {
- fp->fd_small = kFalse;
- } else {
- int count = 0;
- for (int n = 0; n < fp->fd_len; n++) {
- count += plines_win_nofold(wp, fp->fd_top + lnum_off + n);
- if (count > wp->w_p_fml) {
- fp->fd_small = kFalse;
- return;
- }
+ // Mark any nested folds to maybe-small
+ setSmallMaybe(&fp->fd_nested);
+
+ if (fp->fd_len > wp->w_p_fml) {
+ fp->fd_small = kFalse;
+ } else {
+ int count = 0;
+ for (int n = 0; n < fp->fd_len; n++) {
+ count += plines_win_nofold(wp, fp->fd_top + lnum_off + n);
+ if (count > wp->w_p_fml) {
+ fp->fd_small = kFalse;
+ return;
}
- fp->fd_small = kTrue;
}
+ fp->fd_small = kTrue;
}
}
@@ -1573,39 +1592,41 @@ static void foldCreateMarkers(win_T *wp, pos_T start, pos_T end)
// foldAddMarker() {{{2
/// Add "marker[markerlen]" in 'commentstring' to position `pos`.
-static void foldAddMarker(buf_T *buf, pos_T pos, const char_u *marker, size_t markerlen)
+static void foldAddMarker(buf_T *buf, pos_T pos, const char *marker, size_t markerlen)
{
- char_u *cms = buf->b_p_cms;
- char_u *newline;
- char_u *p = (char_u *)strstr((char *)buf->b_p_cms, "%s");
+ char *cms = buf->b_p_cms;
+ char *newline;
+ char *p = strstr(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
- char_u *line = ml_get_buf(buf, lnum, false);
- size_t line_len = STRLEN(line);
+ char *line = ml_get_buf(buf, lnum, false);
+ size_t line_len = strlen(line);
size_t added = 0;
- if (u_save(lnum - 1, lnum + 1) == OK) {
- // Check if the line ends with an unclosed comment
- skip_comment(line, false, false, &line_is_comment);
- newline = xmalloc(line_len + markerlen + STRLEN(cms) + 1);
- STRCPY(newline, line);
- // Append the marker to the end of the line
- if (p == NULL || line_is_comment) {
- STRLCPY(newline + line_len, marker, markerlen + 1);
- added = markerlen;
- } else {
- 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;
- }
- ml_replace_buf(buf, lnum, newline, false);
- if (added) {
- extmark_splice_cols(buf, (int)lnum - 1, (int)line_len,
- 0, (int)added, kExtmarkUndo);
- }
+ if (u_save(lnum - 1, lnum + 1) != OK) {
+ return;
+ }
+
+ // Check if the line ends with an unclosed comment
+ skip_comment(line, false, false, &line_is_comment);
+ newline = xmalloc(line_len + markerlen + strlen(cms) + 1);
+ STRCPY(newline, line);
+ // Append the marker to the end of the line
+ if (p == NULL || line_is_comment) {
+ xstrlcpy(newline + line_len, marker, markerlen + 1);
+ added = markerlen;
+ } else {
+ 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;
+ }
+ ml_replace_buf(buf, lnum, newline, false);
+ if (added) {
+ extmark_splice_cols(buf, (int)lnum - 1, (int)line_len,
+ 0, (int)added, kExtmarkUndo);
}
}
@@ -1632,17 +1653,17 @@ static void deleteFoldMarkers(win_T *wp, fold_T *fp, int recursive, linenr_T lnu
/// 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)
+static void foldDelMarker(buf_T *buf, linenr_T lnum, char *marker, size_t markerlen)
{
// 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) {
+ char *cms = buf->b_p_cms;
+ char *line = ml_get_buf(buf, lnum, false);
+ for (char *p = line; *p != NUL; p++) {
+ if (strncmp(p, marker, markerlen) != 0) {
continue;
}
// Found the marker, include a digit if it's there.
@@ -1652,17 +1673,17 @@ static void foldDelMarker(buf_T *buf, linenr_T lnum, char_u *marker, size_t mark
}
if (*cms != NUL) {
// Also delete 'commentstring' if it matches.
- char_u *cms2 = (char_u *)strstr((char *)cms, "%s");
+ char *cms2 = strstr(cms, "%s");
if (p - line >= cms2 - cms
- && STRNCMP(p - (cms2 - cms), cms, cms2 - cms) == 0
- && STRNCMP(p + len, cms2 + 2, STRLEN(cms2 + 2)) == 0) {
+ && strncmp(p - (cms2 - cms), cms, (size_t)(cms2 - cms)) == 0
+ && strncmp(p + len, cms2 + 2, strlen(cms2 + 2)) == 0) {
p -= cms2 - cms;
- len += STRLEN(cms) - 2;
+ len += strlen(cms) - 2;
}
}
if (u_save(lnum - 1, lnum + 1) == OK) {
// Make new line: text-before-marker + text-after-marker
- char_u *newline = xmalloc(STRLEN(line) - len + 1);
+ char *newline = xmalloc(strlen(line) - len + 1);
assert(p >= line);
memcpy(newline, line, (size_t)(p - line));
STRCPY(newline + (p - line), p + len);
@@ -1683,10 +1704,10 @@ static void foldDelMarker(buf_T *buf, linenr_T lnum, char_u *marker, size_t mark
/// @return the text for a closed fold
///
/// Otherwise the result is in allocated memory.
-char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldinfo, char_u *buf)
+char *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldinfo, char *buf)
FUNC_ATTR_NONNULL_ARG(1)
{
- char_u *text = NULL;
+ char *text = NULL;
// an error occurred when evaluating 'fdt' setting
static bool got_fdt_error = false;
int save_did_emsg = did_emsg;
@@ -1728,9 +1749,7 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldin
curbuf = wp->w_buffer;
emsg_silent++; // handle exceptions, but don't display errors
- text =
- (char_u *)eval_to_string_safe((char *)wp->w_p_fdt, NULL,
- was_set_insecurely(wp, "foldtext", OPT_LOCAL));
+ text = eval_to_string_safe(wp->w_p_fdt, NULL, was_set_insecurely(wp, "foldtext", OPT_LOCAL));
emsg_silent--;
if (text == NULL || did_emsg) {
@@ -1751,32 +1770,32 @@ 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;
+ char *p;
for (p = text; *p != NUL; p++) {
- int len = utfc_ptr2len((char *)p);
+ int len = utfc_ptr2len(p);
if (len > 1) {
- if (!vim_isprintc(utf_ptr2char((char *)p))) {
+ if (!vim_isprintc(utf_ptr2char(p))) {
break;
}
p += len - 1;
} else if (*p == TAB) {
*p = ' ';
- } else if (ptr2cells((char *)p) > 1) {
+ } else if (ptr2cells(p) > 1) {
break;
}
}
if (*p != NUL) {
- p = (char_u *)transstr((const char *)text, true);
+ p = transstr((const char *)text, true);
xfree(text);
text = p;
}
}
}
if (text == NULL) {
- unsigned long count = (unsigned long)(lnume - lnum + 1);
+ long count = lnume - lnum + 1;
- vim_snprintf((char *)buf, FOLD_TEXT_LEN,
+ vim_snprintf(buf, FOLD_TEXT_LEN,
NGETTEXT("+--%3ld line folded",
"+--%3ld lines folded ", count),
count);
@@ -1787,17 +1806,17 @@ 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).
-static void foldtext_cleanup(char_u *str)
+static void foldtext_cleanup(char *str)
{
// Ignore leading and trailing white space in 'commentstring'.
- char_u *cms_start = (char_u *)skipwhite((char *)curbuf->b_p_cms);
- size_t cms_slen = STRLEN(cms_start);
+ char *cms_start = skipwhite(curbuf->b_p_cms);
+ size_t cms_slen = strlen(cms_start);
while (cms_slen > 0 && ascii_iswhite(cms_start[cms_slen - 1])) {
cms_slen--;
}
// locate "%s" in 'commentstring', use the part before and after it.
- char_u *cms_end = (char_u *)strstr((char *)cms_start, "%s");
+ char *cms_end = strstr(cms_start, "%s");
size_t cms_elen = 0;
if (cms_end != NULL) {
cms_elen = cms_slen - (size_t)(cms_end - cms_start);
@@ -1809,7 +1828,7 @@ static void foldtext_cleanup(char_u *str)
}
// skip "%s" and white space after it
- char_u *s = (char_u *)skipwhite((char *)cms_end + 2);
+ char *s = skipwhite(cms_end + 2);
cms_elen -= (size_t)(s - cms_end);
cms_end = s;
}
@@ -1818,11 +1837,11 @@ static void foldtext_cleanup(char_u *str)
bool did1 = false;
bool did2 = false;
- for (char_u *s = str; *s != NUL;) {
+ for (char *s = str; *s != NUL;) {
size_t len = 0;
- if (STRNCMP(s, curwin->w_p_fmr, foldstartmarkerlen) == 0) {
+ if (strncmp(s, curwin->w_p_fmr, foldstartmarkerlen) == 0) {
len = foldstartmarkerlen;
- } else if (STRNCMP(s, foldendmarker, foldendmarkerlen) == 0) {
+ } else if (strncmp(s, foldendmarker, foldendmarkerlen) == 0) {
len = foldendmarkerlen;
}
if (len > 0) {
@@ -1832,19 +1851,19 @@ static void foldtext_cleanup(char_u *str)
// May remove 'commentstring' start. Useful when it's a double
// quote and we already removed a double quote.
- char_u *p;
+ char *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) {
+ && strncmp(p - cms_slen, cms_start, cms_slen) == 0) {
len += (size_t)(s - p) + cms_slen;
s = p - cms_slen;
}
} else if (cms_end != NULL) {
- if (!did1 && cms_slen > 0 && STRNCMP(s, cms_start, cms_slen) == 0) {
+ if (!did1 && cms_slen > 0 && strncmp(s, cms_start, cms_slen) == 0) {
len = cms_slen;
did1 = true;
} else if (!did2 && cms_elen > 0
- && STRNCMP(s, cms_end, cms_elen) == 0) {
+ && strncmp(s, cms_end, cms_elen) == 0) {
len = cms_elen;
did2 = true;
}
@@ -2850,11 +2869,11 @@ static void foldlevelIndent(fline_T *flp)
linenr_T lnum = flp->lnum + flp->off;
buf_T *buf = flp->wp->w_buffer;
- char_u *s = (char_u *)skipwhite((char *)ml_get_buf(buf, lnum, false));
+ char *s = skipwhite(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((char *)flp->wp->w_p_fdi, *s) != NULL) {
+ if (*s == NUL || vim_strchr(flp->wp->w_p_fdi, (uint8_t)(*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;
@@ -2907,7 +2926,7 @@ static void foldlevelExpr(fline_T *flp)
const bool save_keytyped = KeyTyped;
int c;
- const int n = eval_foldexpr((char *)flp->wp->w_p_fde, &c);
+ const int n = eval_foldexpr(flp->wp->w_p_fde, &c);
KeyTyped = save_keytyped;
switch (c) {
@@ -2985,9 +3004,9 @@ static void foldlevelExpr(fline_T *flp)
/// Relies on the option value to have been checked for correctness already.
static void parseMarker(win_T *wp)
{
- foldendmarker = (char_u *)vim_strchr((char *)wp->w_p_fmr, ',');
+ foldendmarker = vim_strchr(wp->w_p_fmr, ',');
foldstartmarkerlen = (size_t)(foldendmarker++ - wp->w_p_fmr);
- foldendmarkerlen = STRLEN(foldendmarker);
+ foldendmarkerlen = strlen(foldendmarker);
}
// foldlevelMarker() {{{2
@@ -3003,23 +3022,23 @@ static void foldlevelMarker(fline_T *flp)
int start_lvl = flp->lvl;
// cache a few values for speed
- char_u *startmarker = flp->wp->w_p_fmr;
- int cstart = *startmarker;
+ char *startmarker = flp->wp->w_p_fmr;
+ char cstart = *startmarker;
startmarker++;
- int cend = *foldendmarker;
+ char cend = *foldendmarker;
// Default: no start found, next level is same as current level
flp->start = 0;
flp->lvl_next = flp->lvl;
- char_u *s = ml_get_buf(flp->wp->w_buffer, flp->lnum + flp->off, false);
+ char *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) {
+ && strncmp(s + 1, startmarker, foldstartmarkerlen - 1) == 0) {
// found startmarker: set flp->lvl
s += foldstartmarkerlen;
if (ascii_isdigit(*s)) {
- int n = atoi((char *)s);
+ int n = atoi(s);
if (n > 0) {
flp->lvl = n;
flp->lvl_next = n;
@@ -3034,12 +3053,12 @@ static void foldlevelMarker(fline_T *flp)
flp->lvl_next++;
flp->start++;
}
- } else if (*s == cend && STRNCMP(s + 1, foldendmarker + 1,
- foldendmarkerlen - 1) == 0) {
+ } else if (*s == cend
+ && strncmp(s + 1, foldendmarker + 1, foldendmarkerlen - 1) == 0) {
// found endmarker: set flp->lvl_next
s += foldendmarkerlen;
if (ascii_isdigit(*s)) {
- int n = atoi((char *)s);
+ int n = atoi(s);
if (n > 0) {
flp->lvl = n;
flp->lvl_next = n - 1;
@@ -3117,7 +3136,7 @@ static int put_folds_recurse(FILE *fd, garray_T *gap, linenr_T off)
return FAIL;
}
if (fprintf(fd, "%" PRId64 ",%" PRId64 "fold",
- (int64_t)(fp->fd_top + off),
+ (int64_t)fp->fd_top + off,
(int64_t)(fp->fd_top + off + fp->fd_len - 1)) < 0
|| put_eol(fd) == FAIL) {
return FAIL;
@@ -3138,7 +3157,7 @@ static int put_foldopen_recurse(FILE *fd, win_T *wp, garray_T *gap, linenr_T off
if (fp->fd_flags != FD_LEVEL) {
if (!GA_EMPTY(&fp->fd_nested)) {
// open nested folds while this fold is open
- if (fprintf(fd, "%" PRId64, (int64_t)(fp->fd_top + off)) < 0
+ if (fprintf(fd, "%" PRId64, (int64_t)fp->fd_top + off) < 0
|| put_eol(fd) == FAIL
|| put_line(fd, "normal! zo") == FAIL) {
return FAIL;
@@ -3212,19 +3231,19 @@ static void foldclosed_both(typval_T *argvars, typval_T *rettv, int end)
}
/// "foldclosed()" function
-void f_foldclosed(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_foldclosed(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
foldclosed_both(argvars, rettv, false);
}
/// "foldclosedend()" function
-void f_foldclosedend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_foldclosedend(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
foldclosed_both(argvars, rettv, true);
}
/// "foldlevel()" function
-void f_foldlevel(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_foldlevel(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const linenr_T lnum = tv_get_lnum(argvars);
if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) {
@@ -3233,14 +3252,14 @@ void f_foldlevel(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "foldtext()" function
-void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_foldtext(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
linenr_T foldstart = (linenr_T)get_vim_var_nr(VV_FOLDSTART);
linenr_T foldend = (linenr_T)get_vim_var_nr(VV_FOLDEND);
- char_u *dashes = (char_u *)get_vim_var_str(VV_FOLDDASHES);
+ char *dashes = get_vim_var_str(VV_FOLDDASHES);
if (foldstart > 0 && foldend <= curbuf->b_ml.ml_line_count) {
// Find first non-empty line in the fold.
linenr_T lnum;
@@ -3251,37 +3270,37 @@ void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
// Find interesting text in this line.
- char_u *s = (char_u *)skipwhite((char *)ml_get(lnum));
+ char *s = skipwhite(ml_get(lnum));
// skip C comment-start
if (s[0] == '/' && (s[1] == '*' || s[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));
+ s = skipwhite(s + 2);
+ if (*skipwhite(s) == NUL && lnum + 1 < foldend) {
+ s = skipwhite(ml_get(lnum + 1));
if (*s == '*') {
- s = (char_u *)skipwhite((char *)s + 1);
+ s = skipwhite(s + 1);
}
}
}
- int count = foldend - foldstart + 1;
+ long count = foldend - foldstart + 1;
char *txt = NGETTEXT("+-%s%3ld line: ", "+-%s%3ld lines: ", count);
- size_t len = STRLEN(txt)
- + STRLEN(dashes) // for %s
+ size_t len = strlen(txt)
+ + strlen(dashes) // for %s
+ 20 // for %3ld
- + STRLEN(s); // concatenated
- char_u *r = xmalloc(len);
- snprintf((char *)r, len, txt, dashes, count);
- len = STRLEN(r);
+ + strlen(s); // concatenated
+ char *r = xmalloc(len);
+ snprintf(r, len, txt, dashes, count);
+ len = strlen(r);
STRCAT(r, s);
// remove 'foldmarker' and 'commentstring'
foldtext_cleanup(r + len);
- rettv->vval.v_string = (char *)r;
+ rettv->vval.v_string = r;
}
}
/// "foldtextresult(lnum)" function
-void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_foldtextresult(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- char_u buf[FOLD_TEXT_LEN];
+ char buf[FOLD_TEXT_LEN];
static bool entered = false;
rettv->v_type = VAR_STRING;
@@ -3298,11 +3317,11 @@ void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr)
foldinfo_T info = fold_info(curwin, lnum);
if (info.fi_lines > 0) {
- char_u *text = get_foldtext(curwin, lnum, lnum + info.fi_lines - 1, info, buf);
+ char *text = get_foldtext(curwin, lnum, lnum + info.fi_lines - 1, info, buf);
if (text == buf) {
- text = vim_strsave(text);
+ text = xstrdup(text);
}
- rettv->vval.v_string = (char *)text;
+ rettv->vval.v_string = text;
}
entered = false;
diff --git a/src/nvim/fold.h b/src/nvim/fold.h
index f34e6d43c3..ac1e8c9419 100644
--- a/src/nvim/fold.h
+++ b/src/nvim/fold.h
@@ -5,13 +5,12 @@
#include "nvim/buffer_defs.h"
#include "nvim/garray.h"
+#include "nvim/macros.h"
#include "nvim/pos.h"
#include "nvim/types.h"
-/*
- * Info used to pass info about a fold from the fold-detection code to the
- * code that displays the foldcolumn.
- */
+// Info used to pass info about a fold from the fold-detection code to the
+// code that displays the foldcolumn.
typedef struct foldinfo {
linenr_T fi_lnum; // line number where fold starts
int fi_level; // level of the fold; when this is zero the
@@ -21,8 +20,6 @@ typedef struct foldinfo {
linenr_T fi_lines;
} foldinfo_T;
-#define FOLDINFO_INIT { 0, 0, 0, 0 }
-
EXTERN int disable_fold_update INIT(= 0);
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/garray.c b/src/nvim/garray.c
index 7a3c14b1bb..aa9a44d410 100644
--- a/src/nvim/garray.c
+++ b/src/nvim/garray.c
@@ -5,22 +5,17 @@
///
/// Functions for handling growing arrays.
-#include <inttypes.h>
#include <string.h>
-#include "nvim/ascii.h"
#include "nvim/garray.h"
#include "nvim/log.h"
#include "nvim/memory.h"
#include "nvim/path.h"
#include "nvim/strings.h"
-#include "nvim/vim.h"
-
-// #include "nvim/globals.h"
-#include "nvim/memline.h"
+#include "nvim/types.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "garray.c.generated.h"
+# include "garray.c.generated.h" // IWYU pragma: export
#endif
/// Clear an allocated growing array.
@@ -123,7 +118,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 (path_fnamecmp(fnames[i - 1], fnames[i]) == 0) {
xfree(fnames[i]);
// close the gap (move all strings one slot lower)
@@ -131,7 +126,7 @@ void ga_remove_duplicate_strings(garray_T *gap)
fnames[j - 1] = fnames[j];
}
- --gap->ga_len;
+ gap->ga_len--;
}
}
}
@@ -167,7 +162,7 @@ char *ga_concat_strings_sep(const garray_T *gap, const char *sep)
s = xstpcpy(s, strings[i]);
s = xstpcpy(s, sep);
}
- strcpy(s, strings[nelem - 1]);
+ strcpy(s, strings[nelem - 1]); // NOLINT(runtime/printf)
return ret;
}
@@ -178,9 +173,9 @@ char *ga_concat_strings_sep(const garray_T *gap, const char *sep)
/// @param gap
///
/// @returns the concatenated strings
-char_u *ga_concat_strings(const garray_T *gap) FUNC_ATTR_NONNULL_RET
+char *ga_concat_strings(const garray_T *gap) FUNC_ATTR_NONNULL_RET
{
- return (char_u *)ga_concat_strings_sep(gap, ",");
+ return ga_concat_strings_sep(gap, ",");
}
/// Concatenate a string to a growarray which contains characters.
@@ -198,7 +193,7 @@ void ga_concat(garray_T *gap, const char *restrict s)
return;
}
- ga_concat_len(gap, s, STRLEN(s));
+ ga_concat_len(gap, s, strlen(s));
}
/// Concatenate a string to a growarray which contains characters
@@ -221,7 +216,7 @@ void ga_concat_len(garray_T *const gap, const char *restrict s, const size_t len
///
/// @param gap
/// @param c
-void ga_append(garray_T *gap, char c)
+void ga_append(garray_T *gap, uint8_t c)
{
- GA_APPEND(char, gap, c);
+ GA_APPEND(uint8_t, gap, c);
}
diff --git a/src/nvim/garray.h b/src/nvim/garray.h
index 56bd5c9130..1623c4db7b 100644
--- a/src/nvim/garray.h
+++ b/src/nvim/garray.h
@@ -1,10 +1,12 @@
#ifndef NVIM_GARRAY_H
#define NVIM_GARRAY_H
-#include <stddef.h> // for size_t
+#include <stdbool.h>
+#include <stddef.h>
#include "nvim/log.h"
-#include "nvim/types.h" // for char_u
+#include "nvim/memory.h"
+#include "nvim/types.h"
/// Structure used for growing arrays.
/// This is used to store information that only grows, is deleted all at
@@ -25,7 +27,8 @@ typedef struct growarray {
#define GA_APPEND(item_type, gap, item) \
do { \
ga_grow(gap, 1); \
- ((item_type *)(gap)->ga_data)[(gap)->ga_len++] = (item); \
+ ((item_type *)(gap)->ga_data)[(gap)->ga_len] = (item); \
+ (gap)->ga_len++; \
} while (0)
#define GA_APPEND_VIA_PTR(item_type, gap) \
diff --git a/src/nvim/generators/c_grammar.lua b/src/nvim/generators/c_grammar.lua
index 70a7be86b5..3e89b60b4a 100644
--- a/src/nvim/generators/c_grammar.lua
+++ b/src/nvim/generators/c_grammar.lua
@@ -26,6 +26,8 @@ local c_id = (
local c_void = P('void')
local c_param_type = (
((P('Error') * fill * P('*') * fill) * Cc('error')) +
+ ((P('Arena') * fill * P('*') * fill) * Cc('arena')) +
+ ((P('lua_State') * fill * P('*') * fill) * Cc('lstate')) +
C((P('const ') ^ -1) * (c_id) * (ws ^ 1) * P('*')) +
(C(c_id) * (ws ^ 1))
)
@@ -47,9 +49,9 @@ local c_proto = Ct(
(fill * Cg((P('FUNC_API_LUA_ONLY') * Cc(true)), 'lua_only') ^ -1) *
(fill * Cg((P('FUNC_API_CHECK_TEXTLOCK') * Cc(true)), 'check_textlock') ^ -1) *
(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 * Cg((P('FUNC_API_CLIENT_IGNORE') * Cc(true)), 'client_ignore') ^ -1) *
fill * P(';')
)
diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua
index b167767f7a..35f6bf8455 100644
--- a/src/nvim/generators/gen_api_dispatch.lua
+++ b/src/nvim/generators/gen_api_dispatch.lua
@@ -17,6 +17,7 @@ local nvimdir = arg[1]
package.path = nvimdir .. '/?.lua;' .. package.path
_G.vim = loadfile(nvimdir..'/../../runtime/lua/vim/shared.lua')()
+_G.vim.inspect = loadfile(nvimdir..'/../../runtime/lua/vim/inspect.lua')()
local hashy = require'generators.hashy'
@@ -59,6 +60,12 @@ for i = 6, #arg do
if public and not fn.noexport then
functions[#functions + 1] = tmp[j]
function_names[fn.name] = true
+ if #fn.parameters >= 2 and fn.parameters[2][1] == 'Array' and fn.parameters[2][2] == 'uidata' then
+ -- function receives the "args" as a parameter
+ fn.receives_array_args = true
+ -- remove the args parameter
+ table.remove(fn.parameters, 2)
+ end
if #fn.parameters ~= 0 and fn.parameters[1][2] == 'channel_id' then
-- this function should receive the channel id
fn.receives_channel_id = true
@@ -72,6 +79,15 @@ for i = 6, #arg do
-- for specifying errors
fn.parameters[#fn.parameters] = nil
end
+ if #fn.parameters ~= 0 and fn.parameters[#fn.parameters][1] == 'arena' then
+ -- return value is allocated in an arena
+ fn.arena_return = true
+ fn.parameters[#fn.parameters] = nil
+ end
+ if #fn.parameters ~= 0 and fn.parameters[#fn.parameters][1] == 'lstate' then
+ fn.has_lua_imp = true
+ fn.parameters[#fn.parameters] = nil
+ end
end
end
input:close()
@@ -149,7 +165,7 @@ local exported_attributes = {'name', 'return_type', 'method',
'since', 'deprecated_since'}
local exported_functions = {}
for _,f in ipairs(functions) do
- if not (startswith(f.name, "nvim__") or f.name == "nvim_error_event") then
+ if not (startswith(f.name, "nvim__") or f.name == "nvim_error_event" or f.name == "redraw") then
local f_exported = {}
for _,attr in ipairs(exported_attributes) do
f_exported[attr] = f[attr]
@@ -179,6 +195,35 @@ funcs_metadata_output:close()
-- start building the dispatch wrapper output
local output = io.open(dispatch_outputf, 'wb')
+
+-- ===========================================================================
+-- 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!
+-- ===========================================================================
+output:write([[
+#include "nvim/log.h"
+#include "nvim/map.h"
+#include "nvim/msgpack_rpc/helpers.h"
+#include "nvim/vim.h"
+
+#include "nvim/api/autocmd.h"
+#include "nvim/api/buffer.h"
+#include "nvim/api/command.h"
+#include "nvim/api/deprecated.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/ui_client.h"
+
+]])
+
local function real_type(type)
local rv = type
local rmatch = string.match(type, "Dict%(([_%w]+)%)")
@@ -210,7 +255,7 @@ for i = 1, #functions do
if fn.impl_name == nil and fn.remote then
local args = {}
- output:write('Object handle_'..fn.name..'(uint64_t channel_id, Array args, Error *error)')
+ output:write('Object handle_'..fn.name..'(uint64_t channel_id, Array args, Arena* arena, Error *error)')
output:write('\n{')
output:write('\n#if MIN_LOG_LEVEL <= LOGLVL_DBG')
output:write('\n logmsg(LOGLVL_DBG, "RPC: ", NULL, -1, true, "ch %" PRIu64 ": invoke '
@@ -225,11 +270,13 @@ for i = 1, #functions do
output:write('\n '..rt..' '..converted..';')
end
output:write('\n')
- output:write('\n if (args.size != '..#fn.parameters..') {')
- output:write('\n api_set_error(error, kErrorTypeException, \
- "Wrong number of arguments: expecting '..#fn.parameters..' but got %zu", args.size);')
- output:write('\n goto cleanup;')
- output:write('\n }\n')
+ if not fn.receives_array_args then
+ output:write('\n if (args.size != '..#fn.parameters..') {')
+ output:write('\n api_set_error(error, kErrorTypeException, \
+ "Wrong number of arguments: expecting '..#fn.parameters..' but got %zu", args.size);')
+ output:write('\n goto cleanup;')
+ output:write('\n }\n')
+ end
-- Validation/conversion for each argument
for j = 1, #fn.parameters do
@@ -311,12 +358,40 @@ for i = 1, #functions do
if fn.receives_channel_id then
-- if the function receives the channel id, pass it as first argument
if #args > 0 or fn.can_fail then
- output:write('channel_id, '..call_args)
+ output:write('channel_id, ')
+ if fn.receives_array_args then
+ -- if the function receives the array args, pass it the second argument
+ output:write('args, ')
+ end
+ output:write(call_args)
else
output:write('channel_id')
+ if fn.receives_array_args then
+ output:write(', args')
+ end
end
else
- output:write(call_args)
+ if fn.receives_array_args then
+ if #args > 0 or fn.call_fail then
+ output:write('args, '..call_args)
+ else
+ output:write('args')
+ end
+ else
+ output:write(call_args)
+ end
+ end
+
+ if fn.arena_return then
+ output:write(', arena')
+ end
+
+ if fn.has_lua_imp then
+ if #args > 0 then
+ output:write(', NULL')
+ else
+ output:write('NULL')
+ end
end
if fn.can_fail then
@@ -351,15 +426,18 @@ for _,fn in ipairs(functions) do
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)
+local names = vim.tbl_keys(remote_fns)
+table.sort(names)
+local hashorder, hashfun = hashy.hashy_hash("msgpack_rpc_get_handler_for", names, function (idx)
return "method_handlers["..idx.."].name"
end)
-output:write("static const MsgpackRpcRequestHandler method_handlers[] = {\n")
-for _, name in ipairs(hashorder) do
+output:write("const MsgpackRpcRequestHandler method_handlers[] = {\n")
+for n, name in ipairs(hashorder) do
local fn = remote_fns[name]
+ fn.handler_id = n-1
output:write(' { .name = "'..name..'", .fn = handle_'.. (fn.impl_name or fn.name)..
- ', .fast = '..tostring(fn.fast)..'},\n')
+ ', .fast = '..tostring(fn.fast)..', .arena_return = '..tostring(not not fn.arena_return)..'},\n')
end
output:write("};\n\n")
output:write(hashfun)
@@ -400,6 +478,8 @@ output:write([[
#include "nvim/api/private/helpers.h"
#include "nvim/lua/converter.h"
#include "nvim/lua/executor.h"
+#include "nvim/memory.h"
+
]])
include_headers(output, headers)
output:write('\n')
@@ -477,6 +557,17 @@ local function process_function(fn)
if fn.receives_channel_id then
cparams = 'LUA_INTERNAL_CALL, ' .. cparams
end
+ if fn.arena_return then
+ cparams = cparams .. '&arena, '
+ write_shifted_output(output, [[
+ Arena arena = ARENA_EMPTY;
+ ]])
+ end
+
+ if fn.has_lua_imp then
+ cparams = cparams .. 'lstate, '
+ end
+
if fn.can_fail then
cparams = cparams .. '&err'
else
@@ -511,15 +602,35 @@ local function process_function(fn)
else
return_type = fn.return_type
end
+ local free_retval
+ if fn.arena_return then
+ free_retval = "arena_mem_free(arena_finish(&arena));"
+ else
+ free_retval = "api_free_"..return_type:lower().."(ret);"
+ end
write_shifted_output(output, string.format([[
const %s ret = %s(%s);
+ ]], fn.return_type, fn.name, cparams))
+
+ if fn.has_lua_imp then
+ -- only push onto the Lua stack if we haven't already
+ write_shifted_output(output, string.format([[
+ if (lua_gettop(lstate) == 0) {
+ nlua_push_%s(lstate, ret, true);
+ }
+ ]], return_type))
+ else
+ write_shifted_output(output, string.format([[
nlua_push_%s(lstate, ret, true);
- api_free_%s(ret);
+ ]], return_type))
+ end
+
+ write_shifted_output(output, string.format([[
+ %s
%s
%s
return 1;
- ]], fn.return_type, fn.name, cparams, return_type, return_type:lower(),
- free_at_exit_code, err_throw_code))
+ ]], free_retval, free_at_exit_code, err_throw_code))
else
write_shifted_output(output, string.format([[
%s(%s);
diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua
index 93bbaab74c..827097f69d 100755
--- a/src/nvim/generators/gen_api_ui_events.lua
+++ b/src/nvim/generators/gen_api_ui_events.lua
@@ -3,14 +3,12 @@ local mpack = require('mpack')
local nvimdir = arg[1]
package.path = nvimdir .. '/?.lua;' .. package.path
-assert(#arg == 8)
+assert(#arg == 6)
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 call_output = io.open(arg[3], 'wb')
+local remote_output = io.open(arg[4], 'wb')
+local metadata_output = io.open(arg[5], 'wb')
+local client_output = io.open(arg[6], 'wb')
local c_grammar = require('generators.c_grammar')
local events = c_grammar.grammar:match(input:read('*all'))
@@ -75,17 +73,16 @@ local function call_ui_event_method(output, ev)
hlattrs_args_count = hlattrs_args_count + 1
elseif kind == 'Object' then
output:write('args.items['..(j-1)..'];\n')
+ elseif kind == 'Window' then
+ output:write('(Window)args.items['..(j-1)..'].data.integer;\n')
else
output:write('args.items['..(j-1)..'].data.'..string.lower(kind)..';\n')
end
end
- output:write(' ui_call_'..ev.name..'(')
+ output:write(' tui_'..ev.name..'(tui')
for j = 1, #ev.parameters do
- output:write('arg_'..j)
- if j ~= #ev.parameters then
- output:write(', ')
- end
+ output:write(', arg_'..j)
end
output:write(');\n')
@@ -103,12 +100,9 @@ for i = 1, #events do
ev.since = tonumber(ev.since)
if not ev.remote_only then
- proto_output:write(' void (*'..ev.name..')')
- write_signature(proto_output, ev, 'UI *ui')
- proto_output:write(';\n')
if not ev.remote_impl and not ev.noexport then
- remote_output:write('static void remote_ui_'..ev.name)
+ remote_output:write('void remote_ui_'..ev.name)
write_signature(remote_output, ev, 'UI *ui')
remote_output:write('\n{\n')
remote_output:write(' UIData *data = ui->data;\n')
@@ -117,62 +111,6 @@ for i = 1, #events do
remote_output:write(' push_call(ui, "'..ev.name..'", args);\n')
remote_output:write('}\n\n')
end
-
- if not ev.bridge_impl and not ev.noexport then
- local send, argv, recv, recv_argv, recv_cleanup = '', '', '', '', ''
- local argc = 1
- for j = 1, #ev.parameters do
- local param = ev.parameters[j]
- local copy = 'copy_'..param[2]
- if param[1] == 'String' then
- send = send..' String copy_'..param[2]..' = copy_string('..param[2]..');\n'
- argv = argv..', '..copy..'.data, INT2PTR('..copy..'.size)'
- recv = (recv..' String '..param[2]..
- ' = (String){.data = argv['..argc..'],'..
- '.size = (size_t)argv['..(argc+1)..']};\n')
- recv_argv = recv_argv..', '..param[2]
- recv_cleanup = recv_cleanup..' api_free_string('..param[2]..');\n'
- argc = argc+2
- elseif param[1] == 'Array' then
- send = send..' Array '..copy..' = copy_array('..param[2]..');\n'
- argv = argv..', '..copy..'.items, INT2PTR('..copy..'.size)'
- recv = (recv..' Array '..param[2]..
- ' = (Array){.items = argv['..argc..'],'..
- '.size = (size_t)argv['..(argc+1)..']};\n')
- recv_argv = recv_argv..', '..param[2]
- recv_cleanup = recv_cleanup..' api_free_array('..param[2]..');\n'
- argc = argc+2
- elseif param[1] == 'Object' then
- send = send..' Object *'..copy..' = xmalloc(sizeof(Object));\n'
- send = send..' *'..copy..' = copy_object('..param[2]..');\n'
- argv = argv..', '..copy
- recv = recv..' Object '..param[2]..' = *(Object *)argv['..argc..'];\n'
- recv_argv = recv_argv..', '..param[2]
- recv_cleanup = (recv_cleanup..' api_free_object('..param[2]..');\n'..
- ' xfree(argv['..argc..']);\n')
- argc = argc+1
- elseif param[1] == 'Integer' or param[1] == 'Boolean' then
- argv = argv..', INT2PTR('..param[2]..')'
- recv_argv = recv_argv..', PTR2INT(argv['..argc..'])'
- argc = argc+1
- else
- assert(false)
- end
- end
- bridge_output:write('static void ui_bridge_'..ev.name..
- '_event(void **argv)\n{\n')
- bridge_output:write(' UI *ui = UI(argv[0]);\n')
- bridge_output:write(recv)
- bridge_output:write(' ui->'..ev.name..'(ui'..recv_argv..');\n')
- bridge_output:write(recv_cleanup)
- bridge_output:write('}\n\n')
-
- bridge_output:write('static void ui_bridge_'..ev.name)
- write_signature(bridge_output, ev, 'UI *ui')
- bridge_output:write('\n{\n')
- bridge_output:write(send)
- bridge_output:write(' UI_BRIDGE_CALL(ui, '..ev.name..', '..argc..', ui'..argv..');\n}\n\n')
- end
end
if not (ev.remote_only and ev.remote_impl) then
@@ -185,6 +123,9 @@ for i = 1, #events do
call_output:write(' UI_LOG('..ev.name..');\n')
call_output:write(' ui_call_event("'..ev.name..'", args);\n')
elseif ev.compositor_impl then
+ call_output:write(' ui_comp_'..ev.name)
+ write_signature(call_output, ev, '', true)
+ call_output:write(";\n")
call_output:write(' UI_CALL')
write_signature(call_output, ev, '!ui->composed, '..ev.name..', ui', true)
call_output:write(";\n")
@@ -206,14 +147,14 @@ for i = 1, #events do
call_output:write("}\n\n")
end
- if (not ev.remote_only) and (not ev.noexport) and (not ev.client_impl) then
+ if (not ev.remote_only) and (not ev.noexport) and (not ev.client_impl) and (not ev.client_ignore) 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
+ if (not ev.noexport) and ((not ev.remote_only) or ev.client_impl) and (not ev.client_ignore) then
client_events[ev.name] = ev
end
end
@@ -231,7 +172,6 @@ end
client_output:write('\n};\n\n')
client_output:write(hashfun)
-proto_output:close()
call_output:close()
remote_output:close()
client_output:close()
diff --git a/src/nvim/generators/gen_declarations.lua b/src/nvim/generators/gen_declarations.lua
index c7d5a1a191..4097ff7dc5 100755
--- a/src/nvim/generators/gen_declarations.lua
+++ b/src/nvim/generators/gen_declarations.lua
@@ -182,8 +182,7 @@ Additionally uses the following environment variables:
If set to 1 then all generated declarations receive a comment with file
name and line number after the declaration. This may be useful for
debugging gen_declarations script, but not much beyond that with
- configured development environment (i.e. with ctags/cscope/finding
- definitions with clang/etc).
+ configured development environment (i.e. with with clang/etc).
WARNING: setting this to 1 will cause extensive rebuilds: declarations
generator script will not regenerate non-static.h file if its
@@ -218,7 +217,7 @@ local footer = [[
local non_static = header .. [[
#ifndef DLLEXPORT
-# ifdef WIN32
+# ifdef MSWIN
# define DLLEXPORT __declspec(dllexport)
# else
# define DLLEXPORT
diff --git a/src/nvim/generators/gen_eval.lua b/src/nvim/generators/gen_eval.lua
index c72249161b..baed6a74c2 100644
--- a/src/nvim/generators/gen_eval.lua
+++ b/src/nvim/generators/gen_eval.lua
@@ -27,26 +27,59 @@ local hashy = require'generators.hashy'
local hashpipe = io.open(funcsfname, 'wb')
+hashpipe:write([[
+#include "nvim/arglist.h"
+#include "nvim/cmdexpand.h"
+#include "nvim/cmdhist.h"
+#include "nvim/digraph.h"
+#include "nvim/eval/buffer.h"
+#include "nvim/eval/funcs.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/vars.h"
+#include "nvim/eval/window.h"
+#include "nvim/ex_docmd.h"
+#include "nvim/ex_getln.h"
+#include "nvim/fold.h"
+#include "nvim/getchar.h"
+#include "nvim/insexpand.h"
+#include "nvim/mapping.h"
+#include "nvim/match.h"
+#include "nvim/mbyte.h"
+#include "nvim/menu.h"
+#include "nvim/move.h"
+#include "nvim/quickfix.h"
+#include "nvim/search.h"
+#include "nvim/sign.h"
+#include "nvim/testing.h"
+
+]])
+
local funcs = require('eval').funcs
+for _, func in pairs(funcs) do
+ if func.float_func then
+ func.func = "float_op_wrapper"
+ func.data = "{ .float_func = &"..func.float_func.." }"
+ end
+end
+
local metadata = mpack.unpack(io.open(metadata_file, 'rb'):read("*all"))
for _,fun in ipairs(metadata) do
if fun.eval then
funcs[fun.name] = {
args=#fun.parameters,
func='api_wrapper',
- data='&handle_'..fun.name,
+ data='{ .api_handler = &method_handlers['..fun.handler_id..'] }'
}
end
end
+local func_names = vim.tbl_keys(funcs)
+table.sort(func_names)
local funcsdata = io.open(funcs_file, 'w')
-funcsdata:write(mpack.pack(funcs))
+funcsdata:write(mpack.pack(func_names))
funcsdata:close()
-
-local names = vim.tbl_keys(funcs)
-
-local neworder, hashfun = hashy.hashy_hash("find_internal_func", names, function (idx)
+local neworder, hashfun = hashy.hashy_hash("find_internal_func", func_names, function (idx)
return "functions["..idx.."].name"
end)
hashpipe:write("static const EvalFuncDef functions[] = {\n")
@@ -60,12 +93,12 @@ for _, name in ipairs(neworder) do
end
local base = def.base or "BASE_NONE"
local func = def.func or ('f_' .. name)
- local data = def.data or "NULL"
+ local data = def.data or "{ .nullptr = NULL }"
local fast = def.fast and 'true' or 'false'
- hashpipe:write((' { "%s", %s, %s, %s, %s, &%s, (FunPtr)%s },\n')
+ hashpipe:write((' { "%s", %s, %s, %s, %s, &%s, %s },\n')
:format(name, args[1], args[2], base, fast, func, data))
end
-hashpipe:write(' { NULL, 0, 0, BASE_NONE, false, NULL, NULL },\n')
+hashpipe:write(' { NULL, 0, 0, BASE_NONE, false, NULL, { .nullptr = NULL } },\n')
hashpipe:write("};\n\n")
hashpipe:write(hashfun)
hashpipe:close()
diff --git a/src/nvim/generators/gen_events.lua b/src/nvim/generators/gen_events.lua
index 6ee45a14af..8db7f22452 100644
--- a/src/nvim/generators/gen_events.lua
+++ b/src/nvim/generators/gen_events.lua
@@ -32,7 +32,9 @@ for i, event in ipairs(events) do
end
end
-for alias, event in pairs(aliases) do
+for _, v in ipairs(aliases) do
+ local alias = v[1]
+ local event = v[2]
names_tgt:write(('\n {%u, "%s", EVENT_%s},'):format(#alias, alias, event:upper()))
end
diff --git a/src/nvim/generators/gen_ex_cmds.lua b/src/nvim/generators/gen_ex_cmds.lua
index 255c415a4d..0c1051b04e 100644
--- a/src/nvim/generators/gen_ex_cmds.lua
+++ b/src/nvim/generators/gen_ex_cmds.lua
@@ -49,6 +49,43 @@ enumfile:write([[
typedef enum CMD_index {
]])
defsfile:write(string.format([[
+#include "nvim/arglist.h"
+#include "nvim/autocmd.h"
+#include "nvim/buffer.h"
+#include "nvim/cmdhist.h"
+#include "nvim/debugger.h"
+#include "nvim/diff.h"
+#include "nvim/digraph.h"
+#include "nvim/eval.h"
+#include "nvim/eval/userfunc.h"
+#include "nvim/eval/vars.h"
+#include "nvim/ex_cmds.h"
+#include "nvim/ex_cmds2.h"
+#include "nvim/ex_docmd.h"
+#include "nvim/ex_eval.h"
+#include "nvim/ex_session.h"
+#include "nvim/help.h"
+#include "nvim/indent.h"
+#include "nvim/locale.h"
+#include "nvim/lua/executor.h"
+#include "nvim/mapping.h"
+#include "nvim/mark.h"
+#include "nvim/match.h"
+#include "nvim/menu.h"
+#include "nvim/message.h"
+#include "nvim/ops.h"
+#include "nvim/option.h"
+#include "nvim/profile.h"
+#include "nvim/quickfix.h"
+#include "nvim/runtime.h"
+#include "nvim/sign.h"
+#include "nvim/spell.h"
+#include "nvim/spellfile.h"
+#include "nvim/syntax.h"
+#include "nvim/undo.h"
+#include "nvim/usercmd.h"
+#include "nvim/version.h"
+
static const int command_count = %u;
static CommandDefinition cmdnames[%u] = {
]], #defs, #defs))
diff --git a/src/nvim/generators/gen_keysets.lua b/src/nvim/generators/gen_keysets.lua
index 633c5da184..b1c1f3e2d8 100644
--- a/src/nvim/generators/gen_keysets.lua
+++ b/src/nvim/generators/gen_keysets.lua
@@ -1,4 +1,3 @@
-
local nvimsrcdir = arg[1]
local shared_file = arg[2]
local funcs_file = arg[3]
@@ -38,7 +37,9 @@ local function sanitize(key)
return key
end
-for name, keys in pairs(keysets) do
+for _, v in ipairs(keysets) do
+ local name = v[1]
+ local keys = v[2]
local neworder, hashfun = hashy.hashy_hash(name, keys, function (idx)
return name.."_table["..idx.."].str"
end)
diff --git a/src/nvim/generators/gen_options.lua b/src/nvim/generators/gen_options.lua
index 0454c54faf..edb7dae159 100644
--- a/src/nvim/generators/gen_options.lua
+++ b/src/nvim/generators/gen_options.lua
@@ -29,14 +29,14 @@ local type_flags={
}
local redraw_flags={
+ ui_option='P_UI_OPTION',
+ tabline='P_RTABL',
statuslines='P_RSTAT',
current_window='P_RWIN',
current_window_only='P_RWINONLY',
current_buffer='P_RBUF',
all_windows='P_RALL',
- everything='P_RCLR',
curswant='P_CURSWANT',
- ui_option='P_UI_OPTION',
}
local list_flags={
@@ -78,6 +78,7 @@ local get_flags = function(o)
{'deny_in_modelines', 'P_NO_ML'},
{'deny_duplicates', 'P_NODUP'},
{'modelineexpr', 'P_MLE'},
+ {'func'}
}) do
local key_name = flag_desc[1]
local def_name = flag_desc[2] or ('P_' .. key_name:upper())
@@ -115,7 +116,7 @@ local value_dumpers = {
}
local get_value = function(v)
- return '(char_u *) ' .. value_dumpers[type(v)](v)
+ return '(char *) ' .. value_dumpers[type(v)](v)
end
local get_defaults = function(d,n)
@@ -158,7 +159,7 @@ local dump_option = function(i, o)
if #o.scope == 2 then
pv_name = 'OPT_BOTH(' .. pv_name .. ')'
end
- defines['PV_' .. varname:sub(3):upper()] = pv_name
+ table.insert(defines, { 'PV_' .. varname:sub(3):upper() , pv_name})
w(' .indir=' .. pv_name)
end
if o.enable_if then
@@ -191,7 +192,7 @@ w(' [' .. ('%u'):format(#options.options) .. ']={.fullname=NULL}')
w('};')
w('')
-for k, v in pairs(defines) do
- w('#define ' .. k .. ' ' .. v)
+for _, v in ipairs(defines) do
+ w('#define ' .. v[1] .. ' ' .. v[2])
end
opt_fd:close()
diff --git a/src/nvim/generators/gen_unicode_tables.lua b/src/nvim/generators/gen_unicode_tables.lua
index 36553f4649..9ad99c8029 100644
--- a/src/nvim/generators/gen_unicode_tables.lua
+++ b/src/nvim/generators/gen_unicode_tables.lua
@@ -153,7 +153,8 @@ local build_combining_table = function(ut_fp, dataprops)
local start = -1
local end_ = -1
for _, p in ipairs(dataprops) do
- if (({Mn=true, Mc=true, Me=true})[p[3]]) then
+ -- The 'Mc' property was removed, it does take up space.
+ if (({Mn=true, Me=true})[p[3]]) then
local n = tonumber(p[1], 16)
if start >= 0 and end_ + 1 == n then
-- Continue with the same range.
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 0f55158733..9c5364e1b1 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -7,11 +7,15 @@
#include <assert.h>
#include <inttypes.h>
#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
+#include "lauxlib.h"
+#include "nvim/api/private/defs.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/cursor.h"
@@ -19,16 +23,21 @@
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
#include "nvim/input.h"
#include "nvim/insexpand.h"
#include "nvim/keycodes.h"
#include "nvim/lua/executor.h"
+#include "nvim/macros.h"
#include "nvim/main.h"
#include "nvim/mapping.h"
#include "nvim/mbyte.h"
@@ -44,8 +53,11 @@
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/plines.h"
+#include "nvim/pos.h"
+#include "nvim/screen.h"
#include "nvim/state.h"
#include "nvim/strings.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/vim.h"
@@ -81,33 +93,29 @@ static buffheader_T readbuf2 = { { NULL, { NUL } }, NULL, 0, 0 };
static int typeahead_char = 0; // typeahead char that's not flushed
-/*
- * when block_redo is TRUE redo buffer will not be changed
- * used by edit() to repeat insertions and 'V' command for redoing
- */
-static int block_redo = FALSE;
+// when block_redo is true redo buffer will not be changed
+// used by edit() to repeat insertions and 'V' command for redoing
+static int block_redo = false;
static int KeyNoremap = 0; // remapping flags
-/*
- * Variables used by vgetorpeek() and flush_buffers()
- *
- * typebuf.tb_buf[] contains all characters that are not consumed yet.
- * typebuf.tb_buf[typebuf.tb_off] is the first valid character.
- * typebuf.tb_buf[typebuf.tb_off + typebuf.tb_len - 1] is the last valid char.
- * typebuf.tb_buf[typebuf.tb_off + typebuf.tb_len] must be NUL.
- * The head of the buffer may contain the result of mappings, abbreviations
- * and @a commands. The length of this part is typebuf.tb_maplen.
- * typebuf.tb_silent is the part where <silent> applies.
- * After the head are characters that come from the terminal.
- * typebuf.tb_no_abbr_cnt is the number of characters in typebuf.tb_buf that
- * should not be considered for abbreviations.
- * Some parts of typebuf.tb_buf may not be mapped. These parts are remembered
- * in typebuf.tb_noremap[], which is the same length as typebuf.tb_buf and
- * contains RM_NONE for the characters that are not to be remapped.
- * typebuf.tb_noremap[typebuf.tb_off] is the first valid flag.
- * (typebuf has been put in globals.h, because check_termcode() needs it).
- */
+// Variables used by vgetorpeek() and flush_buffers()
+//
+// typebuf.tb_buf[] contains all characters that are not consumed yet.
+// typebuf.tb_buf[typebuf.tb_off] is the first valid character.
+// typebuf.tb_buf[typebuf.tb_off + typebuf.tb_len - 1] is the last valid char.
+// typebuf.tb_buf[typebuf.tb_off + typebuf.tb_len] must be NUL.
+// The head of the buffer may contain the result of mappings, abbreviations
+// and @a commands. The length of this part is typebuf.tb_maplen.
+// typebuf.tb_silent is the part where <silent> applies.
+// After the head are characters that come from the terminal.
+// typebuf.tb_no_abbr_cnt is the number of characters in typebuf.tb_buf that
+// should not be considered for abbreviations.
+// Some parts of typebuf.tb_buf may not be mapped. These parts are remembered
+// in typebuf.tb_noremap[], which is the same length as typebuf.tb_buf and
+// contains RM_NONE for the characters that are not to be remapped.
+// typebuf.tb_noremap[typebuf.tb_off] is the first valid flag.
+// (typebuf has been put in globals.h, because check_termcode() needs it).
#define RM_YES 0 // tb_noremap: remap
#define RM_NONE 1 // tb_noremap: don't remap
#define RM_SCRIPT 2 // tb_noremap: remap local script mappings
@@ -126,9 +134,7 @@ static size_t last_recorded_len = 0; // number of last recorded chars
# include "getchar.c.generated.h"
#endif
-/*
- * Free and clear a buffer.
- */
+// Free and clear a buffer.
void free_buff(buffheader_T *buf)
{
buffblock_T *p, *np;
@@ -145,16 +151,16 @@ void free_buff(buffheader_T *buf)
/// 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)
+static char *get_buffcont(buffheader_T *buffer, int dozero)
{
size_t count = 0;
- char_u *p = NULL;
- char_u *p2;
+ char *p = NULL;
+ char *p2;
// compute the total length of the string
for (const buffblock_T *bp = buffer->bh_first.b_next;
bp != NULL; bp = bp->b_next) {
- count += STRLEN(bp->b_str);
+ count += strlen(bp->b_str);
}
if (count || dozero) {
@@ -162,7 +168,7 @@ static char_u *get_buffcont(buffheader_T *buffer, int dozero)
p2 = p;
for (const buffblock_T *bp = buffer->bh_first.b_next;
bp != NULL; bp = bp->b_next) {
- for (const char_u *str = bp->b_str; *str;) {
+ for (const char *str = bp->b_str; *str;) {
*p2++ = *str++;
}
}
@@ -176,38 +182,34 @@ static char_u *get_buffcont(buffheader_T *buffer, int dozero)
/// K_SPECIAL in the returned string is escaped.
char_u *get_recorded(void)
{
- char_u *p;
+ char *p;
size_t len;
- p = get_buffcont(&recordbuff, TRUE);
+ p = get_buffcont(&recordbuff, true);
free_buff(&recordbuff);
- /*
- * Remove the characters that were added the last time, these must be the
- * (possibly mapped) characters that stopped the recording.
- */
- len = STRLEN(p);
+ // Remove the characters that were added the last time, these must be the
+ // (possibly mapped) characters that stopped the recording.
+ len = strlen(p);
if (len >= last_recorded_len) {
len -= last_recorded_len;
p[len] = NUL;
}
- /*
- * When stopping recording from Insert mode with CTRL-O q, also remove the
- * CTRL-O.
- */
+ // When stopping recording from Insert mode with CTRL-O q, also remove the
+ // CTRL-O.
if (len > 0 && restart_edit != 0 && p[len - 1] == Ctrl_O) {
p[len - 1] = NUL;
}
- return p;
+ return (char_u *)p;
}
/// Return the contents of the redo buffer as a single string.
/// K_SPECIAL in the returned string is escaped.
-char_u *get_inserted(void)
+char *get_inserted(void)
{
- return get_buffcont(&redobuff, FALSE);
+ return get_buffcont(&redobuff, false);
}
/// Add string after the current block of the given buffer
@@ -235,14 +237,14 @@ static void add_buff(buffheader_T *const buf, const char *const s, ptrdiff_t sle
} else if (buf->bh_index != 0) {
memmove(buf->bh_first.b_next->b_str,
buf->bh_first.b_next->b_str + buf->bh_index,
- STRLEN(buf->bh_first.b_next->b_str + buf->bh_index) + 1);
+ strlen(buf->bh_first.b_next->b_str + buf->bh_index) + 1);
}
buf->bh_index = 0;
size_t len;
if (buf->bh_space >= (size_t)slen) {
- len = STRLEN(buf->bh_curr->b_str);
- STRLCPY(buf->bh_curr->b_str + len, s, slen + 1);
+ len = strlen(buf->bh_curr->b_str);
+ xstrlcpy(buf->bh_curr->b_str + len, s, (size_t)slen + 1);
buf->bh_space -= (size_t)slen;
} else {
if (slen < MINIMAL_SIZE) {
@@ -252,7 +254,7 @@ static void add_buff(buffheader_T *const buf, const char *const s, ptrdiff_t sle
}
buffblock_T *p = xmalloc(sizeof(buffblock_T) + len);
buf->bh_space = len - (size_t)slen;
- STRLCPY(p->b_str, s, slen + 1);
+ xstrlcpy(p->b_str, s, (size_t)slen + 1);
p->b_next = buf->bh_curr->b_next;
buf->bh_curr->b_next = p;
@@ -269,11 +271,13 @@ static void delete_buff_tail(buffheader_T *buf, int slen)
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;
+ len = (int)strlen(buf->bh_curr->b_str);
+ if (len < slen) {
+ return;
}
+
+ buf->bh_curr->b_str[len - slen] = NUL;
+ buf->bh_space += (size_t)slen;
}
/// Add number "n" to buffer "buf".
@@ -319,7 +323,7 @@ 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.
+/// If advance == true go to the next char.
/// No translation is done K_SPECIAL is escaped.
static int read_readbuffers(int advance)
{
@@ -341,7 +345,7 @@ static int read_readbuf(buffheader_T *buf, int advance)
}
buffblock_T *const curr = buf->bh_first.b_next;
- c = curr->b_str[buf->bh_index];
+ c = (char_u)curr->b_str[buf->bh_index];
if (advance) {
if (curr->b_str[++buf->bh_index] == NUL) {
@@ -353,9 +357,7 @@ static int read_readbuf(buffheader_T *buf, int advance)
return c;
}
-/*
- * Prepare the read buffers for reading (if they contain something).
- */
+// Prepare the read buffers for reading (if they contain something).
static void start_stuff(void)
{
if (readbuf1.bh_first.b_next != NULL) {
@@ -368,38 +370,30 @@ static void start_stuff(void)
}
}
-/*
- * Return TRUE if the stuff buffer is empty.
- */
+/// 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);
}
-/*
- * Return TRUE if readbuf1 is empty. There may still be redo characters in
- * redbuf2.
- */
+/// Return true if readbuf1 is empty. There may still be redo characters in
+/// redbuf2.
int readbuf1_empty(void)
FUNC_ATTR_PURE
{
return (readbuf1.bh_first.b_next == NULL);
}
-/*
- * Set a typeahead character that won't be flushed.
- */
+// Set a typeahead character that won't be flushed.
void typeahead_noflush(int c)
{
typeahead_char = c;
}
-/*
- * Remove the contents of the stuff buffer and the mapped characters in the
- * typeahead buffer (used in case of an error). If "flush_typeahead" is true,
- * flush all typeahead characters (used when interrupted by a CTRL-C).
- */
+// Remove the contents of the stuff buffer and the mapped characters in the
+// typeahead buffer (used in case of an error). If "flush_typeahead" is true,
+// flush all typeahead characters (used when interrupted by a CTRL-C).
void flush_buffers(flush_buffers_T flush_typeahead)
{
init_typebuf();
@@ -443,32 +437,32 @@ void beep_flush(void)
}
}
-/*
- * The previous contents of the redo buffer is kept in old_redobuffer.
- * This is used for the CTRL-O <.> command in insert mode.
- */
+// The previous contents of the redo buffer is kept in old_redobuffer.
+// This is used for the CTRL-O <.> command in insert mode.
void ResetRedobuff(void)
{
- if (!block_redo) {
- free_buff(&old_redobuff);
- old_redobuff = redobuff;
- redobuff.bh_first.b_next = NULL;
+ if (block_redo) {
+ return;
}
+
+ free_buff(&old_redobuff);
+ old_redobuff = redobuff;
+ redobuff.bh_first.b_next = NULL;
}
-/*
- * Discard the contents of the redo buffer and restore the previous redo
- * buffer.
- */
+// Discard the contents of the redo buffer and restore the previous redo
+// buffer.
void CancelRedo(void)
{
- if (!block_redo) {
- free_buff(&redobuff);
- redobuff = old_redobuff;
- old_redobuff.bh_first.b_next = NULL;
- start_stuff();
- while (read_readbuffers(true) != NUL) {}
+ if (block_redo) {
+ return;
}
+
+ free_buff(&redobuff);
+ redobuff = old_redobuff;
+ old_redobuff.bh_first.b_next = NULL;
+ start_stuff();
+ while (read_readbuffers(true) != NUL) {}
}
/// Save redobuff and old_redobuff to save_redobuff and save_old_redobuff.
@@ -481,11 +475,13 @@ void saveRedobuff(save_redo_T *save_redo)
old_redobuff.bh_first.b_next = NULL;
// Make a copy, so that ":normal ." in a function works.
- char *const s = (char *)get_buffcont(&save_redo->sr_redobuff, false);
- if (s != NULL) {
- add_buff(&redobuff, s, -1L);
- xfree(s);
+ char *const s = get_buffcont(&save_redo->sr_redobuff, false);
+ if (s == NULL) {
+ return;
}
+
+ add_buff(&redobuff, s, -1L);
+ xfree(s);
}
/// Restore redobuff and old_redobuff from save_redobuff and save_old_redobuff.
@@ -533,7 +529,7 @@ void AppendToRedobuffLit(const char *str, int len)
s--;
}
if (s > start) {
- add_buff(&redobuff, start, (long)(s - start));
+ add_buff(&redobuff, start, s - start);
}
if (*s == NUL || (len >= 0 && s - str >= len)) {
@@ -542,7 +538,7 @@ void AppendToRedobuffLit(const char *str, int len)
// Handle a special or multibyte character.
// Composing chars separately are handled separately.
- const int c = mb_cptr2char_adv((const char_u **)&s);
+ const int c = mb_cptr2char_adv(&s);
if (c < ' ' || c == DEL || (*s == NUL && (c == '0' || c == '^'))) {
add_char_buff(&redobuff, Ctrl_V);
}
@@ -565,9 +561,7 @@ void AppendCharToRedobuff(int c)
}
}
-/*
- * Append a number to the redo buffer.
- */
+// Append a number to the redo buffer.
void AppendNumberToRedobuff(long n)
{
if (!block_redo) {
@@ -605,7 +599,7 @@ void stuffReadbuffSpec(const char *s)
stuffReadbuffLen(s, 3);
s += 3;
} else {
- int c = mb_cptr2char_adv((const char_u **)&s);
+ int c = mb_cptr2char_adv(&s);
if (c == CAR || c == NL || c == ESC) {
c = ' ';
}
@@ -621,14 +615,40 @@ void stuffcharReadbuff(int c)
add_char_buff(&readbuf1, c);
}
-/*
- * Append a number to the stuff buffer.
- */
+// Append a number to the stuff buffer.
void stuffnumReadbuff(long n)
{
add_num_buff(&readbuf1, n);
}
+/// Stuff a string into the typeahead buffer, such that edit() will insert it
+/// literally ("literally" true) or interpret is as typed characters.
+void stuffescaped(const char *arg, bool literally)
+{
+ while (*arg != NUL) {
+ // Stuff a sequence of normal ASCII characters, that's fast. Also
+ // stuff K_SPECIAL to get the effect of a special key when "literally"
+ // is true.
+ const char *const start = arg;
+ while ((*arg >= ' ' && *arg < DEL) || ((uint8_t)(*arg) == K_SPECIAL
+ && !literally)) {
+ arg++;
+ }
+ if (arg > start) {
+ stuffReadbuffLen(start, (arg - start));
+ }
+
+ // stuff a single special character
+ if (*arg != NUL) {
+ const int c = mb_cptr2char_adv(&arg);
+ if (literally && ((c < ' ' && c != TAB) || c == DEL)) {
+ stuffcharReadbuff(Ctrl_V);
+ }
+ stuffcharReadbuff(c);
+ }
+ }
+}
+
/// Read a character from the redo buffer. Translates K_SPECIAL and
/// multibyte characters.
/// The redo buffer is left as it is.
@@ -649,7 +669,7 @@ static int read_redo(bool init, bool old_redo)
if (bp == NULL) {
return FAIL;
}
- p = bp->b_str;
+ p = (char_u *)bp->b_str;
return OK;
}
if ((c = *p) == NUL) {
@@ -670,7 +690,7 @@ static int read_redo(bool init, bool old_redo)
}
if (*++p == NUL && bp->b_next != NULL) {
bp = bp->b_next;
- p = bp->b_str;
+ p = (char_u *)bp->b_str;
}
buf[i] = (char_u)c;
if (i == n - 1) { // last byte of a character
@@ -761,11 +781,9 @@ int start_redo(long count, bool old_redo)
return OK;
}
-/*
- * Repeat the last insert (R, o, O, a, A, i or I command) by stuffing
- * the redo buffer into readbuf2.
- * return FAIL for failure, OK otherwise
- */
+// Repeat the last insert (R, o, O, a, A, i or I command) by stuffing
+// the redo buffer into readbuf2.
+// return FAIL for failure, OK otherwise
int start_redo_ins(void)
{
int c;
@@ -793,24 +811,24 @@ int start_redo_ins(void)
void stop_redo_ins(void)
{
- block_redo = FALSE;
+ block_redo = false;
}
-/*
- * Initialize typebuf.tb_buf to point to typebuf_init.
- * alloc() cannot be used here: In out-of-memory situations it would
- * be impossible to type anything.
- */
+// Initialize typebuf.tb_buf to point to typebuf_init.
+// alloc() cannot be used here: In out-of-memory situations it would
+// be impossible to type anything.
static void init_typebuf(void)
{
- if (typebuf.tb_buf == NULL) {
- typebuf.tb_buf = typebuf_init;
- typebuf.tb_noremap = noremapbuf_init;
- typebuf.tb_buflen = TYPELEN_INIT;
- typebuf.tb_len = 0;
- typebuf.tb_off = MAXMAPLEN + 4;
- typebuf.tb_change_cnt = 1;
+ if (typebuf.tb_buf != NULL) {
+ return;
}
+
+ typebuf.tb_buf = typebuf_init;
+ typebuf.tb_noremap = noremapbuf_init;
+ typebuf.tb_buflen = TYPELEN_INIT;
+ typebuf.tb_len = 0;
+ typebuf.tb_off = MAXMAPLEN + 4;
+ typebuf.tb_change_cnt = 1;
}
/// @return true when keys cannot be remapped.
@@ -849,7 +867,7 @@ int ins_typebuf(char *str, int noremap, int offset, bool nottyped, bool silent)
typebuf.tb_change_cnt = 1;
}
- addlen = (int)STRLEN(str);
+ addlen = (int)strlen(str);
if (offset == 0 && addlen <= typebuf.tb_off) {
// Easy case: there is room in front of typebuf.tb_buf[typebuf.tb_off]
@@ -868,7 +886,7 @@ int ins_typebuf(char *str, int noremap, int offset, bool nottyped, bool silent)
// often.
int newoff = MAXMAPLEN + 4;
int extra = addlen + newoff + 4 * (MAXMAPLEN + 4);
- if (typebuf.tb_len > 2147483674 - extra) {
+ if (typebuf.tb_len > INT_MAX - extra) {
// string is getting too long for 32 bit int
emsg(_(e_toocompl)); // also calls flush_buffers
setcursor();
@@ -917,14 +935,12 @@ int ins_typebuf(char *str, int noremap, int offset, bool nottyped, bool silent)
val = RM_NONE;
}
- /*
- * Adjust typebuf.tb_noremap[] for the new characters:
- * If noremap == REMAP_NONE or REMAP_SCRIPT: new characters are
- * (sometimes) not remappable
- * If noremap == REMAP_YES: all the new characters are mappable
- * If noremap > 0: "noremap" characters are not remappable, the rest
- * mappable
- */
+ // Adjust typebuf.tb_noremap[] for the new characters:
+ // If noremap == REMAP_NONE or REMAP_SCRIPT: new characters are
+ // (sometimes) not remappable
+ // If noremap == REMAP_YES: all the new characters are mappable
+ // If noremap > 0: "noremap" characters are not remappable, the rest
+ // mappable
if (noremap == REMAP_SKIP) {
nrm = 1;
} else if (noremap < 0) {
@@ -932,9 +948,9 @@ int ins_typebuf(char *str, int noremap, int offset, bool nottyped, bool silent)
} else {
nrm = noremap;
}
- for (i = 0; i < addlen; ++i) {
+ for (i = 0; i < addlen; i++) {
typebuf.tb_noremap[typebuf.tb_off + i + offset] =
- (char_u)((--nrm >= 0) ? val : RM_YES);
+ (uint8_t)((--nrm >= 0) ? val : RM_YES);
}
// tb_maplen and tb_silent only remember the length of mapped and/or
@@ -970,7 +986,7 @@ int ins_char_typebuf(int c, int modifiers)
return (int)len;
}
-/// Return TRUE if the typeahead buffer was changed (while waiting for a
+/// Return true if the typeahead buffer was changed (while waiting for a
/// character to arrive). Happens when a message was received from a client or
/// from feedkeys().
/// But check in a more generic way to avoid trouble: When "typebuf.tb_buf"
@@ -986,28 +1002,22 @@ bool typebuf_changed(int tb_change_cnt)
|| typebuf_was_filled);
}
-/*
- * Return TRUE if there are no characters in the typeahead buffer that have
- * not been typed (result from a mapping or come from ":normal").
- */
+/// Return true if there are no characters in the typeahead buffer that have
+/// not been typed (result from a mapping or come from ":normal").
int typebuf_typed(void)
FUNC_ATTR_PURE
{
return typebuf.tb_maplen == 0;
}
-/*
- * Return the number of characters that are mapped (or not typed).
- */
+// Return the number of characters that are mapped (or not typed).
int typebuf_maplen(void)
FUNC_ATTR_PURE
{
return typebuf.tb_maplen;
}
-/*
- * remove "len" characters from typebuf.tb_buf[typebuf.tb_off + offset]
- */
+// remove "len" characters from typebuf.tb_buf[typebuf.tb_off + offset]
void del_typebuf(int len, int offset)
{
int i;
@@ -1018,21 +1028,14 @@ void del_typebuf(int len, int offset)
typebuf.tb_len -= len;
- /*
- * Easy case: Just increase typebuf.tb_off.
- */
+ // Easy case: Just increase typebuf.tb_off.
if (offset == 0 && typebuf.tb_buflen - (typebuf.tb_off + len)
>= 3 * MAXMAPLEN + 3) {
typebuf.tb_off += len;
- }
- /*
- * Have to move the characters in typebuf.tb_buf[] and typebuf.tb_noremap[]
- */
- else {
+ } else {
+ // Have to move the characters in typebuf.tb_buf[] and typebuf.tb_noremap[]
i = typebuf.tb_off + offset;
- /*
- * Leave some extra room at the end to avoid reallocation.
- */
+ // Leave some extra room at the end to avoid reallocation.
if (typebuf.tb_off > MAXMAPLEN) {
memmove(typebuf.tb_buf + MAXMAPLEN,
typebuf.tb_buf + typebuf.tb_off, (size_t)offset);
@@ -1081,10 +1084,8 @@ void del_typebuf(int len, int offset)
}
}
-/*
- * Write typed characters to script file.
- * If recording is on put the character in the recordbuffer.
- */
+// Write typed characters to script file.
+// If recording is on put the character in the recordbuffer.
static void gotchars(const char_u *chars, size_t len)
FUNC_ATTR_NONNULL_ALL
{
@@ -1135,20 +1136,20 @@ static void gotchars(const char_u *chars, size_t len)
/// Only affects recorded characters.
void ungetchars(int len)
{
- if (reg_recording != 0) {
- delete_buff_tail(&recordbuff, len);
- last_recorded_len -= (size_t)len;
+ if (reg_recording == 0) {
+ return;
}
+
+ 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.
- * Do not sync:
- * - In Insert mode, unless cursor key has been used.
- * - While reading a script file.
- * - When no_u_sync is non-zero.
- */
+// Sync undo. Called when typed characters are obtained from the typeahead
+// buffer, or when a menu is used.
+// Do not sync:
+// - In Insert mode, unless cursor key has been used.
+// - While reading a script file.
+// - When no_u_sync is non-zero.
void may_sync_undo(void)
{
if ((!(State & (MODE_INSERT | MODE_CMDLINE)) || arrow_used)
@@ -1157,9 +1158,7 @@ void may_sync_undo(void)
}
}
-/*
- * Make "typebuf" empty and allocate new buffers.
- */
+// Make "typebuf" empty and allocate new buffers.
void alloc_typebuf(void)
{
typebuf.tb_buf = xmalloc(TYPELEN_INIT);
@@ -1175,9 +1174,7 @@ void alloc_typebuf(void)
}
}
-/*
- * Free the buffers of "typebuf".
- */
+// Free the buffers of "typebuf".
void free_typebuf(void)
{
if (typebuf.tb_buf == typebuf_init) {
@@ -1192,10 +1189,8 @@ void free_typebuf(void)
}
}
-/*
- * When doing ":so! file", the current typeahead needs to be saved, and
- * restored when "file" has been read completely.
- */
+// When doing ":so! file", the current typeahead needs to be saved, and
+// restored when "file" has been read completely.
static typebuf_T saved_typebuf[NSCRIPT];
void save_typebuf(void)
@@ -1219,9 +1214,7 @@ static bool can_get_old_char(void)
return old_char != -1 && (old_KeyStuffed || stuff_empty());
}
-/*
- * Save all three kinds of typeahead, so that the user must type at a prompt.
- */
+// Save all three kinds of typeahead, so that the user must type at a prompt.
void save_typeahead(tasave_T *tp)
{
tp->save_typebuf = typebuf;
@@ -1237,10 +1230,8 @@ void save_typeahead(tasave_T *tp)
readbuf2.bh_first.b_next = NULL;
}
-/*
- * Restore the typeahead to what it was before calling save_typeahead().
- * The allocated memory is freed, can only be called once!
- */
+// Restore the typeahead to what it was before calling save_typeahead().
+// The allocated memory is freed, can only be called once!
void restore_typeahead(tasave_T *tp)
{
if (tp->typebuf_valid) {
@@ -1260,7 +1251,7 @@ void restore_typeahead(tasave_T *tp)
/// Open a new script file for the ":source!" command.
///
/// @param directly when true execute directly
-void openscript(char_u *name, bool directly)
+void openscript(char *name, bool directly)
{
if (curscript + 1 == NSCRIPT) {
emsg(_(e_nesting));
@@ -1284,7 +1275,7 @@ void openscript(char_u *name, bool directly)
// use NameBuff for expanded name
expand_env(name, NameBuff, MAXPATHL);
int error;
- if ((scriptin[curscript] = file_open_new(&error, (char *)NameBuff,
+ if ((scriptin[curscript] = file_open_new(&error, NameBuff,
kFileReadOnly, 0)) == NULL) {
semsg(_(e_notopen_2), name, os_strerror(error));
if (curscript) {
@@ -1294,12 +1285,10 @@ void openscript(char_u *name, bool directly)
}
save_typebuf();
- /*
- * Execute the commands from the file right now when using ":source!"
- * after ":global" or ":argdo" or in a loop. Also when another command
- * follows. This means the display won't be updated. Don't do this
- * always, "make test" would fail.
- */
+ // Execute the commands from the file right now when using ":source!"
+ // after ":global" or ":argdo" or in a loop. Also when another command
+ // follows. This means the display won't be updated. Don't do this
+ // always, "make test" would fail.
if (directly) {
oparg_T oa;
int oldcurscript;
@@ -1328,9 +1317,7 @@ void openscript(char_u *name, bool directly)
}
}
-/*
- * Close the currently active input script.
- */
+// Close the currently active input script.
static void closescript(void)
{
free_typebuf();
@@ -1353,9 +1340,7 @@ void close_all_scripts(void)
#endif
-/*
- * Return TRUE when reading keys from a script file.
- */
+/// Return true when reading keys from a script file.
int using_script(void)
FUNC_ATTR_PURE
{
@@ -1434,10 +1419,8 @@ int vgetc(void)
garbage_collect(false);
}
- /*
- * If a character was put back with vungetc, it was already processed.
- * Return it directly.
- */
+ // If a character was put back with vungetc, it was already processed.
+ // Return it directly.
if (can_get_old_char()) {
c = old_char;
old_char = -1;
@@ -1610,11 +1593,9 @@ int vgetc(void)
last_vgetc_recorded_len = last_recorded_len;
}
- /*
- * In the main loop "may_garbage_collect" can be set to do garbage
- * collection in the first next vgetc(). It's disabled after that to
- * avoid internally used Lists and Dicts to be freed.
- */
+ // In the main loop "may_garbage_collect" can be set to do garbage
+ // collection in the first next vgetc(). It's disabled after that to
+ // avoid internally used Lists and Dicts to be freed.
may_garbage_collect = false;
// Execute Lua on_key callbacks.
@@ -1623,10 +1604,8 @@ int vgetc(void)
return c;
}
-/*
- * Like vgetc(), but never return a NUL when called recursively, get a key
- * directly from the user (ignoring typeahead).
- */
+// Like vgetc(), but never return a NUL when called recursively, get a key
+// directly from the user (ignoring typeahead).
int safe_vgetc(void)
{
int c;
@@ -1638,10 +1617,8 @@ int safe_vgetc(void)
return c;
}
-/*
- * Like safe_vgetc(), but loop to handle K_IGNORE.
- * Also ignore scrollbar events.
- */
+// Like safe_vgetc(), but loop to handle K_IGNORE.
+// Also ignore scrollbar events.
int plain_vgetc(void)
{
int c;
@@ -1654,12 +1631,10 @@ int plain_vgetc(void)
return c;
}
-/*
- * Check if a character is available, such that vgetc() will not block.
- * If the next character is a special character or multi-byte, the returned
- * character is not valid!.
- * Returns NUL if no character is available.
- */
+// Check if a character is available, such that vgetc() will not block.
+// If the next character is a special character or multi-byte, the returned
+// character is not valid!.
+// Returns NUL if no character is available.
int vpeekc(void)
{
if (can_get_old_char()) {
@@ -1668,11 +1643,9 @@ int vpeekc(void)
return vgetorpeek(false);
}
-/*
- * Check if any character is available, also half an escape sequence.
- * Trick: when no typeahead found, but there is something in the typeahead
- * buffer, it must be an ESC that is recognized as the start of a key code.
- */
+// Check if any character is available, also half an escape sequence.
+// Trick: when no typeahead found, but there is something in the typeahead
+// buffer, it must be an ESC that is recognized as the start of a key code.
int vpeekc_any(void)
{
int c;
@@ -1684,10 +1657,8 @@ int vpeekc_any(void)
return c;
}
-/*
- * Call vpeekc() without causing anything to be mapped.
- * Return TRUE if a character is available, FALSE otherwise.
- */
+// Call vpeekc() without causing anything to be mapped.
+// Return true if a character is available, false otherwise.
int char_avail(void)
{
int retval;
@@ -1708,9 +1679,10 @@ static void getchar_common(typval_T *argvars, typval_T *rettv)
no_mapping++;
allow_keys++;
for (;;) {
- // Position the cursor. Needed after a message that ends in a space,
- // or if event processing caused a redraw.
- ui_cursor_goto(msg_row, msg_col);
+ if (msg_col > 0) {
+ // Position the cursor. Needed after a message that ends in a space.
+ ui_cursor_goto(msg_row, msg_col);
+ }
if (argvars[0].v_type == VAR_UNKNOWN) {
// getchar(): blocking wait.
@@ -1718,7 +1690,7 @@ static void getchar_common(typval_T *argvars, typval_T *rettv)
if (!char_avail()) {
// flush output before waiting
ui_flush();
- (void)os_inchar(NULL, 0, -1, 0, main_loop.events);
+ (void)os_inchar(NULL, 0, -1, typebuf.tb_change_cnt, main_loop.events);
if (!multiqueue_empty(main_loop.events)) {
state_handle_k_event();
continue;
@@ -1748,11 +1720,6 @@ static void getchar_common(typval_T *argvars, typval_T *rettv)
no_mapping--;
allow_keys--;
- if (!ui_has_messages()) {
- // redraw the screen after getchar()
- update_screen(CLEAR);
- }
-
set_vim_var_nr(VV_MOUSE_WIN, 0);
set_vim_var_nr(VV_MOUSE_WINID, 0);
set_vim_var_nr(VV_MOUSE_LNUM, 0);
@@ -1760,26 +1727,26 @@ static void getchar_common(typval_T *argvars, typval_T *rettv)
rettv->vval.v_number = n;
if (n != 0 && (IS_SPECIAL(n) || mod_mask != 0)) {
- char_u temp[10]; // modifier: 3, mbyte-char: 6, NUL: 1
+ char temp[10]; // modifier: 3, mbyte-char: 6, NUL: 1
int i = 0;
// Turn a special key into three bytes, plus modifier.
if (mod_mask != 0) {
- temp[i++] = K_SPECIAL;
- temp[i++] = KS_MODIFIER;
- temp[i++] = (char_u)mod_mask;
+ temp[i++] = (char)K_SPECIAL;
+ temp[i++] = (char)KS_MODIFIER;
+ temp[i++] = (char)mod_mask;
}
if (IS_SPECIAL(n)) {
- temp[i++] = K_SPECIAL;
- temp[i++] = (char_u)K_SECOND(n);
- temp[i++] = K_THIRD(n);
+ temp[i++] = (char)K_SPECIAL;
+ temp[i++] = (char)K_SECOND(n);
+ temp[i++] = (char)K_THIRD(n);
} else {
- i += utf_char2bytes((int)n, (char *)temp + i);
+ i += utf_char2bytes((int)n, temp + i);
}
assert(i < 10);
temp[i++] = NUL;
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = (char *)vim_strsave(temp);
+ rettv->vval.v_string = xstrdup(temp);
if (is_mouse_key((int)n)) {
int row = mouse_row;
@@ -1810,33 +1777,35 @@ static void getchar_common(typval_T *argvars, typval_T *rettv)
}
/// "getchar()" function
-void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_getchar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
getchar_common(argvars, rettv);
}
/// "getcharstr()" function
-void f_getcharstr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_getcharstr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
getchar_common(argvars, rettv);
- if (rettv->v_type == VAR_NUMBER) {
- char temp[7]; // mbyte-char: 6, NUL: 1
- const varnumber_T n = rettv->vval.v_number;
- int i = 0;
+ if (rettv->v_type != VAR_NUMBER) {
+ return;
+ }
- if (n != 0) {
- i += utf_char2bytes((int)n, (char *)temp);
- }
- assert(i < 7);
- temp[i++] = NUL;
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = xstrdup(temp);
+ 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((int)n, (char *)temp);
}
+ assert(i < 7);
+ temp[i++] = NUL;
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = xstrdup(temp);
}
/// "getcharmod()" function
-void f_getcharmod(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_getcharmod(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = mod_mask;
}
@@ -1881,7 +1850,7 @@ static bool at_ins_compl_key(void)
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));
+ || (compl_status_local() && (c == Ctrl_N || c == Ctrl_P));
}
/// Check if typebuf.tb_buf[] contains a modifier plus key that can be changed
@@ -1943,7 +1912,7 @@ static int check_simplify_modifier(int max_offset)
/// - 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)
+static int handle_mapping(int *keylenp, const bool *timedout, int *mapdepth)
{
mapblock_T *mp = NULL;
mapblock_T *mp2;
@@ -2013,21 +1982,33 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
// Only consider an entry if the first character matches and it is
// for the current state.
// Skip ":lmap" mappings if keys were mapped.
- if (mp->m_keys[0] == tb_c1 && (mp->m_mode & local_State)
+ if ((uint8_t)mp->m_keys[0] == tb_c1 && (mp->m_mode & local_State)
&& ((mp->m_mode & MODE_LANGMAP) == 0 || typebuf.tb_maplen == 0)) {
int nomap = nolmaplen;
- int c2;
+ int modifiers = 0;
// find the match length of this mapping
for (mlen = 1; mlen < typebuf.tb_len; mlen++) {
- c2 = typebuf.tb_buf[typebuf.tb_off + mlen];
+ int c2 = typebuf.tb_buf[typebuf.tb_off + mlen];
if (nomap > 0) {
+ if (nomap == 2 && c2 == KS_MODIFIER) {
+ modifiers = 1;
+ } else if (nomap == 1 && modifiers == 1) {
+ modifiers = c2;
+ }
nomap--;
- } else if (c2 == K_SPECIAL) {
- nomap = 2;
} else {
- LANGMAP_ADJUST(c2, true);
+ if (c2 == K_SPECIAL) {
+ nomap = 2;
+ } else if (merge_modifiers(c2, &modifiers) == c2) {
+ // Only apply 'langmap' if merging modifiers into
+ // the key will not result in another character,
+ // so that 'langmap' behaves consistently in
+ // different terminals and GUIs.
+ LANGMAP_ADJUST(c2, true);
+ }
+ modifiers = 0;
}
- if (mp->m_keys[mlen] != c2) {
+ if ((uint8_t)mp->m_keys[mlen] != c2) {
break;
}
}
@@ -2035,7 +2016,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
// Don't allow mapping the first byte(s) of a multi-byte char.
// Happens when mapping <M-a> and then changing 'encoding'.
// Beware that 0x80 is escaped.
- char_u *p1 = mp->m_keys;
+ char_u *p1 = (char_u *)mp->m_keys;
char_u *p2 = (char_u *)mb_unescape((const char **)&p1);
if (p2 != NULL && MB_BYTE2LEN(tb_c1) > utfc_ptr2len((char *)p2)) {
@@ -2047,15 +2028,14 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
// - Partly match: mlen == typebuf.tb_len
keylen = mp->m_keylen;
if (mlen == keylen || (mlen == typebuf.tb_len && typebuf.tb_len < keylen)) {
- char_u *s;
int n;
// If only script-local mappings are allowed, check if the
// mapping starts with K_SNR.
- s = typebuf.tb_noremap + typebuf.tb_off;
+ uint8_t *s = typebuf.tb_noremap + typebuf.tb_off;
if (*s == RM_SCRIPT
- && (mp->m_keys[0] != K_SPECIAL
- || mp->m_keys[1] != KS_EXTRA
+ && ((uint8_t)mp->m_keys[0] != K_SPECIAL
+ || (uint8_t)mp->m_keys[1] != KS_EXTRA
|| mp->m_keys[2] != KE_SNR)) {
continue;
}
@@ -2103,7 +2083,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
// Check for match with 'pastetoggle'
if (*p_pt != NUL && mp == NULL && (State & (MODE_INSERT | MODE_NORMAL))) {
- bool match = typebuf_match_len(p_pt, &mlen);
+ bool match = typebuf_match_len((char_u *)p_pt, &mlen);
if (match) {
// write chars to script file(s)
if (mlen > typebuf.tb_maplen) {
@@ -2112,7 +2092,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);
+ set_option_value_give_err("paste", !p_paste, NULL, 0);
if (!(State & MODE_INSERT)) {
msg_col = 0;
msg_row = Rows - 1;
@@ -2162,7 +2142,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
} else {
keylen = 0;
}
- if (keylen == 0) { // no simplication has been done
+ if (keylen == 0) { // no simplification has been done
// If there was no mapping at all use the character from the
// typeahead buffer right here.
if (mp == NULL) {
@@ -2188,7 +2168,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
// complete match
if (keylen >= 0 && keylen <= typebuf.tb_len) {
- char_u *map_str = NULL;
+ char *map_str = NULL;
// Write chars to script file(s).
// Note: :lmap mappings are written *after* being applied. #5658
@@ -2225,12 +2205,12 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
// Copy the values from *mp that are used, because evaluating the
// expression may invoke a function that redefines the mapping, thereby
// making *mp invalid.
- 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;
+ const bool save_m_expr = mp->m_expr;
+ const int save_m_noremap = mp->m_noremap;
+ const bool save_m_silent = mp->m_silent;
+ char *save_m_keys = NULL; // only saved when needed
+ char *save_m_str = NULL; // only saved when needed
+ const LuaRef save_m_luaref = mp->m_luaref;
// Handle ":map <expr>": evaluate the {rhs} as an
// expression. Also save and restore the command line
@@ -2246,9 +2226,9 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
vgetc_busy = 0;
may_garbage_collect = false;
- save_m_keys = vim_strsave(mp->m_keys);
+ save_m_keys = xstrdup(mp->m_keys);
if (save_m_luaref == LUA_NOREF) {
- save_m_str = vim_strsave(mp->m_str);
+ save_m_str = xstrdup(mp->m_str);
}
map_str = eval_map_expr(mp, NUL);
@@ -2260,13 +2240,13 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
// 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];
+ char buf[4];
xfree(map_str);
- buf[0] = K_SPECIAL;
- buf[1] = KS_EXTRA;
+ buf[0] = (char)K_SPECIAL;
+ buf[1] = (char)KS_EXTRA;
buf[2] = KE_IGNORE;
buf[3] = NUL;
- map_str = vim_strsave(buf);
+ map_str = xstrdup(buf);
if (State & MODE_CMDLINE) {
// redraw the command below the error
msg_didout = true;
@@ -2295,18 +2275,18 @@ 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 & MODE_LANGMAP) != 0) {
- gotchars(map_str, STRLEN(map_str));
+ gotchars((char_u *)map_str, strlen(map_str));
}
if (save_m_noremap != REMAP_YES) {
noremap = save_m_noremap;
- } else if (STRNCMP(map_str, save_m_keys != NULL ? save_m_keys : mp->m_keys,
+ } else if (strncmp(map_str, save_m_keys != NULL ? save_m_keys : mp->m_keys,
(size_t)keylen) != 0) {
noremap = REMAP_YES;
} else {
noremap = REMAP_SKIP;
}
- i = ins_typebuf((char *)map_str, noremap, 0, true, cmd_silent || save_m_silent);
+ i = ins_typebuf(map_str, noremap, 0, true, cmd_silent || save_m_silent);
if (save_m_expr) {
xfree(map_str);
}
@@ -2364,8 +2344,8 @@ void check_end_reg_executing(bool advance)
///
/// if "advance" is true (vgetc()):
/// Really get the character.
-/// KeyTyped is set to TRUE in the case the user typed the key.
-/// KeyStuffed is TRUE if the character comes from the stuff buffer.
+/// KeyTyped is set to true in the case the user typed the key.
+/// KeyStuffed is true if the character comes from the stuff buffer.
/// if "advance" is false (vpeekc()):
/// Just look whether there is a character available.
/// Return NUL if not.
@@ -2376,7 +2356,9 @@ void check_end_reg_executing(bool advance)
static int vgetorpeek(bool advance)
{
int c, c1;
- bool timedout = false; // waited for more than 1 second for mapping to complete
+ bool timedout = false; // waited for more than 'timeoutlen'
+ // for mapping to complete or
+ // 'ttimeoutlen' for complete key code
int mapdepth = 0; // check for recursive mapping
bool mode_deleted = false; // set when mode has been deleted
int new_wcol, new_wrow;
@@ -2452,7 +2434,7 @@ static int vgetorpeek(bool advance)
// flush all input
c = inchar(typebuf.tb_buf, typebuf.tb_buflen - 1, 0L);
- // If inchar() returns TRUE (script file was active) or we
+ // If inchar() returns true (script file was active) or we
// are inside a mapping, get out of Insert mode.
// Otherwise we behave like having gotten a CTRL-C.
// As a result typing CTRL-C in insert mode will
@@ -2501,7 +2483,7 @@ static int vgetorpeek(bool advance)
// write char to script file(s)
gotchars(typebuf.tb_buf + typebuf.tb_off, 1);
}
- KeyNoremap = typebuf.tb_noremap[typebuf.tb_off];
+ KeyNoremap = (unsigned char)typebuf.tb_noremap[typebuf.tb_off];
del_typebuf(1, 0);
}
break; // got character, break the for loop
@@ -2530,7 +2512,7 @@ static int vgetorpeek(bool advance)
&& (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;
+ colnr_T col = 0;
char_u *ptr;
if (mode_displayed) {
@@ -2548,22 +2530,27 @@ static int vgetorpeek(bool advance)
// We are expecting to truncate the trailing
// white-space, so find the last non-white
// character -- webb
- col = vcol = curwin->w_wcol = 0;
- ptr = get_cursor_line_ptr();
- while (col < curwin->w_cursor.col) {
- if (!ascii_iswhite(ptr[col])) {
- curwin->w_wcol = vcol;
+ curwin->w_wcol = 0;
+ ptr = (char_u *)get_cursor_line_ptr();
+ chartabsize_T cts;
+ init_chartabsize_arg(&cts, curwin,
+ curwin->w_cursor.lnum, 0, (char *)ptr, (char *)ptr);
+ while ((char_u *)cts.cts_ptr < ptr + curwin->w_cursor.col) {
+ if (!ascii_iswhite(*cts.cts_ptr)) {
+ curwin->w_wcol = cts.cts_vcol;
}
- vcol += lbr_chartabsize(ptr, ptr + col, vcol);
- col += utfc_ptr2len((char *)ptr + col);
+ cts.cts_vcol += lbr_chartabsize(&cts);
+ cts.cts_ptr += utfc_ptr2len(cts.cts_ptr);
}
+ clear_chartabsize_arg(&cts);
+
curwin->w_wrow = curwin->w_cline_row
+ curwin->w_wcol / curwin->w_width_inner;
curwin->w_wcol %= curwin->w_width_inner;
curwin->w_wcol += curwin_col_off();
col = 0; // no correction needed
} else {
- --curwin->w_wcol;
+ curwin->w_wcol--;
col = curwin->w_cursor.col - 1;
}
} else if (curwin->w_p_wrap && curwin->w_wrow) {
@@ -2574,8 +2561,8 @@ static int vgetorpeek(bool advance)
if (col > 0 && curwin->w_wcol > 0) {
// Correct when the cursor is on the right halve
// of a double-wide character.
- ptr = get_cursor_line_ptr();
- col -= utf_head_off(ptr, ptr + col);
+ ptr = (char_u *)get_cursor_line_ptr();
+ col -= utf_head_off((char *)ptr, (char *)ptr + col);
if (utf_ptr2cells((char *)ptr + col) > 1) {
curwin->w_wcol--;
}
@@ -2654,7 +2641,7 @@ static int vgetorpeek(bool advance)
// input buffer (e.g., termresponse).
if (((State & MODE_INSERT) != 0 || p_lz) && (State & MODE_CMDLINE) == 0
&& advance && must_redraw != 0 && !need_wait_return) {
- update_screen(0);
+ update_screen();
setcursor(); // put cursor back where it belongs
}
@@ -2829,11 +2816,9 @@ int inchar(char_u *buf, int maxlen, long wait_time)
ui_flush();
}
- /*
- * Don't reset these when at the hit-return prompt, otherwise an endless
- * recursive loop may result (write error in swapfile, hit-return, timeout
- * on char wait, flush swapfile, write error....).
- */
+ // Don't reset these when at the hit-return prompt, otherwise an endless
+ // recursive loop may result (write error in swapfile, hit-return, timeout
+ // on char wait, flush swapfile, write error....).
if (State != MODE_HITRETURN) {
did_outofmem_msg = false; // display out of memory message (again)
did_swapwrite_msg = false; // display swap file write error again
@@ -2866,7 +2851,7 @@ int inchar(char_u *buf, int maxlen, long wait_time)
if (read_size <= 0) { // Did not get a character from script.
// If we got an interrupt, skip all previously typed characters and
- // return TRUE if quit reading script file.
+ // return true if quit reading script file.
// Stop reading typeahead when a single CTRL-C was read,
// fill_input_buf() returns this when not able to read from stdin.
// Don't use buf[] here, closescript() may have freed typebuf.tb_buf[]
@@ -2929,7 +2914,7 @@ int fix_input_buffer(char_u *buf, int len)
// Two characters are special: NUL and K_SPECIAL.
// Replace NUL by K_SPECIAL KS_ZERO KE_FILLER
// Replace K_SPECIAL by K_SPECIAL KS_SPECIAL KE_FILLER
- for (i = len; --i >= 0; ++p) {
+ for (i = len; --i >= 0; p++) {
if (p[0] == NUL
|| (p[0] == K_SPECIAL
&& (i < 2 || p[1] != KS_EXTRA))) {
@@ -3008,16 +2993,16 @@ char *getcmdkeycmd(int promptc, void *cookie, int indent, bool do_concat)
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);
+ ga_append(&line_ga, K_SPECIAL);
+ ga_append(&line_ga, KS_MODIFIER);
+ ga_append(&line_ga, (uint8_t)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));
+ ga_append(&line_ga, K_SPECIAL);
+ ga_append(&line_ga, (uint8_t)K_SECOND(c1));
+ ga_append(&line_ga, (uint8_t)K_THIRD(c1));
} else {
- ga_append(&line_ga, (char)c1);
+ ga_append(&line_ga, (uint8_t)c1);
}
}
@@ -3053,7 +3038,7 @@ bool map_execute_lua(void)
} else if (c1 == '\r' || c1 == '\n') {
c1 = NUL; // end the line
} else {
- ga_append(&line_ga, (char)c1);
+ ga_append(&line_ga, (uint8_t)c1);
}
}
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 231220c319..df4ae05522 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -23,7 +23,7 @@
#define MSG_BUF_CLEN (MSG_BUF_LEN / 6) // cell length (worst case: utf-8
// takes 6 bytes for one cell)
-#ifdef WIN32
+#ifdef MSWIN
# define _PATHSEPSTR "\\"
#else
# define _PATHSEPSTR "/"
@@ -75,6 +75,10 @@
# define VIMRC_FILE ".nvimrc"
#endif
+#ifndef VIMRC_LUA_FILE
+# define VIMRC_LUA_FILE ".nvim.lua"
+#endif
+
EXTERN struct nvim_stats_s {
int64_t fsync;
int64_t redraw;
@@ -159,31 +163,10 @@ EXTERN colnr_T dollar_vcol INIT(= -1);
// Variables for Insert mode completion.
-// Length in bytes of the text being completed (this is deleted to be replaced
-// by the match.)
-EXTERN int compl_length INIT(= 0);
-
-// Set when doing something for completion that may call edit() recursively,
-// which is not allowed. Also used to disable folding during completion
-EXTERN bool compl_busy INIT(= false);
-
-// List of flags for method of completion.
-EXTERN int compl_cont_status INIT(= 0);
-#define CONT_ADDING 1 // "normal" or "adding" expansion
-#define CONT_INTRPT (2 + 4) // a ^X interrupted the current expansion
- // it's set only iff N_ADDS is set
-#define CONT_N_ADDS 4 // next ^X<> will add-new or expand-current
-#define CONT_S_IPOS 8 // next ^X<> will set initial_pos?
- // if so, word-wise-expansion will set SOL
-#define CONT_SOL 16 // pattern includes start of line, just for
- // word-wise expansion, not set for ^X^L
-#define CONT_LOCAL 32 // for ctrl_x_mode 0, ^X^P/^X^N do a local
- // expansion, (eg use complete=.)
-
-EXTERN char_u *edit_submode INIT(= NULL); // msg for CTRL-X submode
-EXTERN char_u *edit_submode_pre INIT(= NULL); // prepended to edit_submode
-EXTERN char_u *edit_submode_extra INIT(= NULL); // appended to edit_submode
-EXTERN hlf_T edit_submode_highl; // highl. method for extra info
+EXTERN char *edit_submode INIT(= NULL); // msg for CTRL-X submode
+EXTERN char *edit_submode_pre INIT(= NULL); // prepended to edit_submode
+EXTERN char *edit_submode_extra INIT(= NULL); // appended to edit_submode
+EXTERN hlf_T edit_submode_highl; // highl. method for extra info
// state for putting characters in the message area
EXTERN int cmdmsg_rl INIT(= false); // cmdline is drawn right to left
@@ -198,7 +181,7 @@ 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 char *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
EXTERN int msg_scroll INIT(= false); // msg_start() will scroll
@@ -215,13 +198,18 @@ EXTERN int emsg_skip INIT(= 0); // don't display errors for
// expression that is skipped
EXTERN bool emsg_severe INIT(= false); // use message of next of several
// emsg() calls for throw
+// used by assert_fails()
+EXTERN char *emsg_assert_fails_msg INIT(= NULL);
+EXTERN long emsg_assert_fails_lnum INIT(= 0);
+EXTERN char *emsg_assert_fails_context INIT(= NULL);
+
EXTERN bool did_endif INIT(= false); // just had ":endif"
EXTERN dict_T vimvardict; // Dictionary with v: variables
EXTERN dict_T globvardict; // Dictionary with g: variables
/// g: value
#define globvarht globvardict.dv_hashtab
-EXTERN bool did_emsg; // set by emsg() when the message
- // is displayed or thrown
+EXTERN int did_emsg; // incremented by emsg() when a
+ // 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
@@ -262,9 +250,13 @@ EXTERN int do_profiling INIT(= PROF_NONE); ///< PROF_ values
/// Exception currently being thrown. Used to pass an exception to a different
/// cstack. Also used for discarding an exception before it is caught or made
-/// pending.
+/// pending. Only valid when did_throw is true.
EXTERN except_T *current_exception;
+/// An exception is being thrown. Reset when the exception is caught or as
+/// long as it is pending in a finally clause.
+EXTERN bool did_throw INIT(= false);
+
/// Set when a throw that cannot be handled in do_cmdline() must be propagated
/// to the cstack of the previously called do_cmdline().
EXTERN bool need_rethrow INIT(= false);
@@ -332,13 +324,10 @@ EXTERN int garbage_collect_at_exit INIT(= false);
#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 });
+EXTERN sctx_T current_sctx INIT(= { 0, 0, 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
@@ -430,12 +419,22 @@ EXTERN win_T *prevwin INIT(= NULL); // previous window
// -V:FOR_ALL_WINDOWS_IN_TAB:501
#define FOR_ALL_WINDOWS_IN_TAB(wp, tp) \
for (win_T *wp = ((tp) == curtab) \
- ? firstwin : (tp)->tp_firstwin; wp != NULL; wp = wp->w_next)
+ ? firstwin : (tp)->tp_firstwin; wp != NULL; wp = wp->w_next)
EXTERN win_T *curwin; // currently active window
-EXTERN win_T *aucmd_win; // window used in aucmd_prepbuf()
-EXTERN int aucmd_win_used INIT(= false); // aucmd_win is being used
+typedef struct {
+ win_T *auc_win; ///< Window used in aucmd_prepbuf(). When not NULL the
+ ///< window has been allocated.
+ bool auc_win_used; ///< This auc_win is being used.
+} aucmdwin_T;
+
+/// When executing autocommands for a buffer that is not in any window, a
+/// special window is created to handle the side effects. When autocommands
+/// nest we may need more than one.
+EXTERN kvec_t(aucmdwin_T) aucmd_win_vec INIT(= KV_INITIAL_VALUE);
+#define aucmd_win (aucmd_win_vec.items)
+#define AUCMD_WIN_COUNT ((int)aucmd_win_vec.size)
// The window layout is kept in a tree of frames. topframe points to the top
// of the tree.
@@ -492,17 +491,19 @@ EXTERN bool exiting INIT(= false);
// internal value of v:dying
EXTERN int v_dying INIT(= 0);
// is stdin a terminal?
-EXTERN int stdin_isatty INIT(= true);
+EXTERN bool stdin_isatty INIT(= true);
// is stdout a terminal?
-EXTERN int stdout_isatty INIT(= true);
+EXTERN bool stdout_isatty INIT(= true);
+// is stderr a terminal?
+EXTERN bool stderr_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.
EXTERN int full_screen INIT(= false);
-/// Non-zero when only "safe" commands are allowed, e.g. when sourcing .exrc or
-/// .vimrc in current directory.
+/// Non-zero when only "safe" commands are allowed
EXTERN int secure INIT(= 0);
/// Non-zero when changing text and jumping to another window or editing another buffer is not
@@ -517,7 +518,7 @@ EXTERN int allbuf_lock INIT(= 0);
/// not allowed then.
EXTERN int sandbox INIT(= 0);
-/// Batch-mode: "-es" or "-Es" commandline argument was given.
+/// Batch-mode: "-es", "-Es", "-l" commandline argument was given.
EXTERN int silent_mode INIT(= false);
/// Start position of active Visual selection.
@@ -580,6 +581,8 @@ EXTERN bool can_si INIT(= false);
// one indent will be removed.
EXTERN bool can_si_back INIT(= false);
+EXTERN int old_indent INIT(= 0); ///< for ^^D command in insert mode
+
// w_cursor before formatting text.
EXTERN pos_T saved_cursor INIT(= { 0, 0, 0 });
@@ -614,7 +617,7 @@ EXTERN int inhibit_delete_count INIT(= 0);
#define DBCS_DEBUG (-1)
/// Encoding used when 'fencs' is set to "default"
-EXTERN char_u *fenc_default INIT(= NULL);
+EXTERN char *fenc_default INIT(= NULL);
/// "State" is the main state of Vim.
/// There are other variables that modify the state:
@@ -628,7 +631,7 @@ 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
-EXTERN int motion_force INIT(=0); // motion force for pending operator
+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
@@ -636,7 +639,7 @@ 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.
+EXTERN bool ex_no_reprint INIT(= false); // No need to print after z or p.
// 'inccommand' command preview state
EXTERN bool cmdpreview INIT(= false);
@@ -676,6 +679,8 @@ EXTERN int emsg_silent INIT(= 0); // don't print error messages
EXTERN bool emsg_noredir INIT(= false); // don't redirect error messages
EXTERN bool cmd_silent INIT(= false); // don't echo the command line
+EXTERN bool in_assert_fails INIT(= false); // assert_fails() active
+
// Values for swap_exists_action: what to do when swap file already exists
#define SEA_NONE 0 // don't use dialog
#define SEA_DIALOG 1 // use dialog when possible
@@ -685,8 +690,8 @@ EXTERN bool cmd_silent INIT(= false); // don't echo the command line
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
+EXTERN char IObuff[IOSIZE]; ///< Buffer for sprintf, I/O, etc.
+EXTERN char NameBuff[MAXPATHL]; ///< Buffer for expanding file names
EXTERN char msg_buf[MSG_BUF_LEN]; ///< Small buffer for messages
EXTERN char os_buf[ ///< Buffer for the os/ layer
#if MAXPATHL > IOSIZE
@@ -750,9 +755,9 @@ EXTERN bool need_start_insertmode INIT(= false); ///< start insert mode soon
// 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 *last_cmdline INIT(= NULL); // last command line (for ":)
+EXTERN char *repeat_cmdline INIT(= NULL); // command line for "."
+EXTERN char *new_last_cmdline INIT(= NULL); // new value for last_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 *autocmd_match INIT(= NULL); // name for <amatch> on cmdline
@@ -769,14 +774,14 @@ 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 *escape_chars INIT(= " \t\\\"|"); // need backslash in cmd line
EXTERN int keep_help_flag INIT(= false); // doing :ta from help file
// When a string option is NULL (which only happens in out-of-memory
// situations), it is set to empty_option, to avoid having to check for NULL
// everywhere.
-EXTERN char_u *empty_option INIT(= (char_u *)"");
+EXTERN char *empty_option INIT(= "");
EXTERN bool redir_off INIT(= false); // no redirection for a moment
EXTERN FILE *redir_fd INIT(= NULL); // message redirection file
@@ -801,8 +806,8 @@ enum {
extern char *default_vim_dir;
extern char *default_vimruntime_dir;
extern char *default_lib_dir;
-extern char_u *compiled_user;
-extern char_u *compiled_sys;
+extern char *compiled_user;
+extern char *compiled_sys;
#endif
// When a window has a local directory, the absolute path of the global
@@ -816,12 +821,11 @@ EXTERN char *last_chdir_reason INIT(= NULL);
EXTERN bool km_stopsel INIT(= false);
EXTERN bool km_startsel INIT(= false);
-EXTERN int cedit_key INIT(= -1); ///< key value of 'cedit' option
EXTERN int cmdwin_type INIT(= 0); ///< type of cmdline window or 0
EXTERN int cmdwin_result INIT(= 0); ///< result of cmdline window or 0
EXTERN int cmdwin_level INIT(= 0); ///< cmdline recursion level
-EXTERN char_u no_lines_msg[] INIT(= N_("--No lines in buffer--"));
+EXTERN char no_lines_msg[] INIT(= N_("--No lines in buffer--"));
// When ":global" is used to number of substitutions and changed lines is
// accumulated until it's finished.
@@ -840,9 +844,6 @@ EXTERN int stl_syntax INIT(= 0);
// don't use 'hlsearch' temporarily
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()
@@ -873,7 +874,7 @@ 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 in secure mode 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"));
@@ -940,7 +941,7 @@ EXTERN char e_patnotf2[] INIT(= N_("E486: Pattern not found: %s"));
EXTERN char e_positive[] INIT(= N_("E487: Argument must be positive"));
EXTERN char e_prev_dir[] INIT(= N_("E459: Cannot go back to previous directory"));
-EXTERN char e_quickfix[] INIT(= N_("E42: No Errors"));
+EXTERN char e_no_errors[] INIT(= N_("E42: No Errors"));
EXTERN char e_loclist[] INIT(= N_("E776: No location list"));
EXTERN char e_re_damg[] INIT(= N_("E43: Damaged match string"));
EXTERN char e_re_corr[] INIT(= N_("E44: Corrupted regexp program"));
@@ -1001,7 +1002,6 @@ 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>"));
@@ -1013,27 +1013,28 @@ EXTERN char e_luv_api_disabled[] INIT(= N_("E5560: %s must not be called in a lu
EXTERN char e_floatonly[] INIT(= N_("E5601: Cannot close window, only floating window would remain"));
EXTERN char e_floatexchange[] INIT(= N_("E5602: Cannot exchange or rotate float"));
-EXTERN char e_non_empty_string_required[] INIT(= N_("E1142: Non-empty string required"));
-
EXTERN char e_cannot_define_autocommands_for_all_events[] INIT(= N_("E1155: Cannot define autocommands for ALL events"));
EXTERN char e_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_invalid_char[] INIT(= N_("E5248: Invalid character in group name"));
+
EXTERN char e_highlight_group_name_too_long[] INIT(= N_("E1249: Highlight group name too long"));
+EXTERN char e_invalid_line_number_nr[] INIT(= N_("E966: Invalid line number: %ld"));
+
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 e_trustfile[] INIT(= N_("E5570: Cannot update trust file: %s"));
+
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"));
EXTERN char line_msg[] INIT(= N_(" line "));
-// For undo we need to know the lowest time possible.
-EXTERN time_t starttime;
-
EXTERN FILE *time_fd INIT(= NULL); // where to write startup timing
// Some compilers warn for not using a return value, but in some situations we
@@ -1043,7 +1044,7 @@ EXTERN int vim_ignored;
// stdio is an RPC channel (--embed).
EXTERN bool embedded_mode INIT(= false);
-// Do not start a UI nor read/write to stdio (unless embedding).
+// Do not start UI (--headless, -l) nor read/write to stdio (unless embedding).
EXTERN bool headless_mode INIT(= false);
// uncrustify:on
@@ -1083,9 +1084,15 @@ typedef enum {
// Only filled for Win32.
EXTERN char windowsVersion[20] INIT(= { 0 });
-EXTERN int exit_need_delay INIT(= 0);
+/// While executing a regexp and set to OPTION_MAGIC_ON or OPTION_MAGIC_OFF this
+/// overrules p_magic. Otherwise set to OPTION_MAGIC_NOT_SET.
+EXTERN optmagic_T magic_overruled INIT(= OPTION_MAGIC_NOT_SET);
-// Set when 'cmdheight' is changed from zero to one temporarily.
-EXTERN bool made_cmdheight_nonzero INIT(= false);
+/// Skip win_fix_cursor() call for 'splitkeep' when cmdwin is closed.
+EXTERN bool skip_win_fix_cursor INIT(= false);
+/// Skip win_fix_scroll() call for 'splitkeep' when closing tab page.
+EXTERN bool skip_win_fix_scroll INIT(= false);
+/// Skip update_topline() call while executing win_fix_scroll().
+EXTERN bool skip_update_topline INIT(= false);
#endif // NVIM_GLOBALS_H
diff --git a/src/nvim/grid.c b/src/nvim/grid.c
index f95ef3e705..46f8a59710 100644
--- a/src/nvim/grid.c
+++ b/src/nvim/grid.c
@@ -11,9 +11,19 @@
//
// The grid_*() functions write to the screen and handle updating grid->lines[].
+#include <assert.h>
+#include <limits.h>
+#include <stdlib.h>
+
#include "nvim/arabic.h"
+#include "nvim/buffer_defs.h"
+#include "nvim/globals.h"
#include "nvim/grid.h"
#include "nvim/highlight.h"
+#include "nvim/log.h"
+#include "nvim/message.h"
+#include "nvim/option_defs.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
@@ -46,14 +56,14 @@ void grid_adjust(ScreenGrid **grid, int *row_off, int *col_off)
}
/// 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 schar_from_cc(char *p, int c, int u8cc[MAX_MCO])
{
- int len = utf_char2bytes(c, (char *)p);
+ int len = utf_char2bytes(c, p);
for (int i = 0; i < MAX_MCO; i++) {
if (u8cc[i] == 0) {
break;
}
- len += utf_char2bytes(u8cc[i], (char *)p + len);
+ len += utf_char2bytes(u8cc[i], p + len);
}
p[len] = 0;
return len;
@@ -125,30 +135,30 @@ 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);
+ 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)
+void grid_getbytes(ScreenGrid *grid, int row, int col, char *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]);
+ if (grid->chars == NULL || row >= grid->rows || col >= grid->cols) {
+ return;
}
+
+ size_t 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)
+void grid_puts(ScreenGrid *grid, char *text, int row, int col, int attr)
{
grid_puts_len(grid, text, -1, row, col, attr);
}
@@ -171,11 +181,11 @@ void grid_puts_line_start(ScreenGrid *grid, int row)
put_dirty_grid = grid;
}
-void grid_put_schar(ScreenGrid *grid, int row, int col, char_u *schar, int attr)
+void grid_put_schar(ScreenGrid *grid, int row, int col, char *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)) {
+ if (grid->attrs[off] != attr || schar_cmp(grid->chars[off], schar) || rdb_flags & RDB_NODELTA) {
schar_copy(grid->chars[off], schar);
grid->attrs[off] = attr;
@@ -187,10 +197,10 @@ void grid_put_schar(ScreenGrid *grid, int row, int col, char_u *schar, int attr)
/// 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)
+void grid_puts_len(ScreenGrid *grid, char *text, int textlen, int row, int col, int attr)
{
size_t off;
- char_u *ptr = text;
+ char *ptr = text;
int len = textlen;
int c;
size_t max_off;
@@ -237,11 +247,11 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col
while (col < grid->cols
&& (len < 0 || (int)(ptr - text) < len)
&& *ptr != NUL) {
- c = *ptr;
+ c = (unsigned char)(*ptr);
// check if this is the first byte of a multibyte
mbyte_blen = len > 0
? utfc_ptr2len_len(ptr, (int)((text + len) - ptr))
- : utfc_ptr2len((char *)ptr);
+ : utfc_ptr2len(ptr);
u8c = len >= 0
? utfc_ptr2char_len(ptr, u8cc, (int)((text + len) - ptr))
: utfc_ptr2char(ptr, u8cc);
@@ -254,7 +264,8 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col
nc1 = NUL;
} else {
nc = len >= 0
- ? utfc_ptr2char_len(ptr + mbyte_blen, pcc, (int)((text + len) - ptr - mbyte_blen))
+ ? utfc_ptr2char_len(ptr + mbyte_blen, pcc,
+ (int)((text + len) - ptr - mbyte_blen))
: utfc_ptr2char(ptr + mbyte_blen, pcc);
nc1 = pcc[0];
}
@@ -279,7 +290,8 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col
need_redraw = schar_cmp(grid->chars[off], buf)
|| (mbyte_cells == 2 && grid->chars[off + 1][0] != 0)
|| grid->attrs[off] != attr
- || exmode_active;
+ || exmode_active
+ || rdb_flags & RDB_NODELTA;
if (need_redraw) {
// When at the end of the text and overwriting a two-cell
@@ -320,7 +332,7 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col
ptr += mbyte_blen;
if (clear_next_cell) {
// This only happens at the end, display one space next.
- ptr = (char_u *)" ";
+ ptr = " ";
len = -1;
}
}
@@ -393,11 +405,11 @@ void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int
// 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);
+ grid_puts_len(grid, " ", 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);
+ grid_puts_len(grid, " ", 1, row, end_col, 0);
}
// if grid was resized (in ext_multigrid mode), the UI has no redraw updates
@@ -411,8 +423,7 @@ void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int
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) {
+ if (schar_cmp(grid->chars[off], sc) || grid->attrs[off] != attr || rdb_flags & RDB_NODELTA) {
schar_copy(grid->chars[off], sc);
grid->attrs[off] = attr;
if (dirty_first == INT_MAX) {
@@ -475,9 +486,9 @@ static int grid_char_needs_redraw(ScreenGrid *grid, size_t off_from, size_t off_
/// "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"
+/// "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,
@@ -604,7 +615,8 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int cle
while (col < clear_width) {
if (grid->chars[off_to][0] != ' '
|| grid->chars[off_to][1] != NUL
- || grid->attrs[off_to] != bg_attr) {
+ || grid->attrs[off_to] != bg_attr
+ || rdb_flags & RDB_NODELTA) {
grid->chars[off_to][0] = ' ';
grid->chars[off_to][1] = NUL;
grid->attrs[off_to] = bg_attr;
diff --git a/src/nvim/grid.h b/src/nvim/grid.h
index 6a93bf3d90..deb3d3785d 100644
--- a/src/nvim/grid.h
+++ b/src/nvim/grid.h
@@ -2,11 +2,14 @@
#define NVIM_GRID_H
#include <stdbool.h>
+#include <string.h>
#include "nvim/ascii.h"
#include "nvim/buffer_defs.h"
#include "nvim/grid_defs.h"
+#include "nvim/macros.h"
#include "nvim/mbyte.h"
+#include "nvim/memory.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
@@ -29,30 +32,30 @@ EXTERN sattr_T *linebuf_attr INIT(= NULL);
// screen grid.
/// Put a ASCII character in a screen cell.
-static inline void schar_from_ascii(char_u *p, const char c)
+static inline void schar_from_ascii(char *p, const char c)
{
- p[0] = (char_u)c;
+ p[0] = c;
p[1] = 0;
}
/// Put a unicode character in a screen cell.
-static inline int schar_from_char(char_u *p, int c)
+static inline int schar_from_char(char *p, int c)
{
- int len = utf_char2bytes(c, (char *)p);
+ int len = utf_char2bytes(c, p);
p[len] = NUL;
return len;
}
/// compare the contents of two screen cells.
-static inline int schar_cmp(char_u *sc1, char_u *sc2)
+static inline int schar_cmp(char *sc1, char *sc2)
{
- return strncmp((char *)sc1, (char *)sc2, sizeof(schar_T));
+ return strncmp(sc1, 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)
+static inline void schar_copy(char *sc1, char *sc2)
{
- xstrlcpy((char *)sc1, (char *)sc2, sizeof(schar_T));
+ xstrlcpy(sc1, sc2, sizeof(schar_T));
}
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/grid_defs.h b/src/nvim/grid_defs.h
index 9252b8a371..db31f7b984 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 schar_T[(MAX_MCO + 1) * 4 + 1];
typedef int sattr_T;
enum {
@@ -110,24 +110,6 @@ 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;
-
typedef struct {
int args[3];
int icell;
diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c
deleted file mode 100644
index e8410d1ee7..0000000000
--- a/src/nvim/hardcopy.c
+++ /dev/null
@@ -1,3192 +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
-
-/*
- * hardcopy.c: printing to paper
- */
-
-#include <assert.h>
-#include <inttypes.h>
-#include <string.h>
-
-#include "nvim/ascii.h"
-#include "nvim/vim.h"
-#ifdef HAVE_LOCALE_H
-# include <locale.h>
-#endif
-#include "nvim/buffer.h"
-#include "nvim/charset.h"
-#include "nvim/eval.h"
-#include "nvim/ex_docmd.h"
-#include "nvim/fileio.h"
-#include "nvim/garray.h"
-#include "nvim/grid.h"
-#include "nvim/hardcopy.h"
-#include "nvim/highlight_group.h"
-#include "nvim/indent.h"
-#include "nvim/mbyte.h"
-#include "nvim/memline.h"
-#include "nvim/memory.h"
-#include "nvim/message.h"
-#include "nvim/option.h"
-#include "nvim/os/input.h"
-#include "nvim/os/os.h"
-#include "nvim/path.h"
-#include "nvim/runtime.h"
-#include "nvim/statusline.h"
-#include "nvim/strings.h"
-#include "nvim/syntax.h"
-#include "nvim/ui.h"
-#include "nvim/version.h"
-
-/*
- * To implement printing on a platform, the following functions must be
- * defined:
- *
- * int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
- * Called once. Code should display printer dialogue (if appropriate) and
- * determine printer font and margin settings. Reset has_color if the printer
- * doesn't support colors at all.
- * Returns FAIL to abort.
- *
- * int mch_print_begin(prt_settings_T *settings)
- * Called to start the print job.
- * Return FALSE to abort.
- *
- * int mch_print_begin_page(char_u *msg)
- * Called at the start of each page.
- * "msg" indicates the progress of the print job, can be NULL.
- * Return FALSE to abort.
- *
- * int mch_print_end_page()
- * Called at the end of each page.
- * Return FALSE to abort.
- *
- * int mch_print_blank_page()
- * Called to generate a blank page for collated, duplex, multiple copy
- * document. Return FALSE to abort.
- *
- * void mch_print_end(prt_settings_T *psettings)
- * Called at normal end of print job.
- *
- * void mch_print_cleanup()
- * Called if print job ends normally or is abandoned. Free any memory, close
- * devices and handles. Also called when mch_print_begin() fails, but not
- * when mch_print_init() fails.
- *
- * void mch_print_set_font(int Bold, int Italic, int Underline);
- * Called whenever the font style changes.
- *
- * void mch_print_set_bg(uint32_t bgcol);
- * Called to set the background color for the following text. Parameter is an
- * RGB value.
- *
- * void mch_print_set_fg(uint32_t fgcol);
- * Called to set the foreground color for the following text. Parameter is an
- * RGB value.
- *
- * mch_print_start_line(int margin, int page_line)
- * Sets the current position at the start of line "page_line".
- * If margin is TRUE start in the left margin (for header and line number).
- *
- * int mch_print_text_out(char_u *p, size_t len);
- * Output one character of text p[len] at the current position.
- * Return TRUE if there is no room for another character in the same line.
- *
- * Note that the generic code has no idea of margins. The machine code should
- * simply make the page look smaller! The header and the line numbers are
- * printed in the margin.
- */
-
-static option_table_T printer_opts[OPT_PRINT_NUM_OPTIONS]
- =
- {
- { "top", TRUE, 0, NULL, 0, FALSE },
- { "bottom", TRUE, 0, NULL, 0, FALSE },
- { "left", TRUE, 0, NULL, 0, FALSE },
- { "right", TRUE, 0, NULL, 0, FALSE },
- { "header", TRUE, 0, NULL, 0, FALSE },
- { "syntax", FALSE, 0, NULL, 0, FALSE },
- { "number", FALSE, 0, NULL, 0, FALSE },
- { "wrap", FALSE, 0, NULL, 0, FALSE },
- { "duplex", FALSE, 0, NULL, 0, FALSE },
- { "portrait", FALSE, 0, NULL, 0, FALSE },
- { "paper", FALSE, 0, NULL, 0, FALSE },
- { "collate", FALSE, 0, NULL, 0, FALSE },
- { "jobsplit", FALSE, 0, NULL, 0, FALSE },
- { "formfeed", FALSE, 0, NULL, 0, FALSE },
- }
-;
-
-static const uint32_t cterm_color_8[8] = {
- 0x000000, 0xff0000, 0x00ff00, 0xffff00,
- 0x0000ff, 0xff00ff, 0x00ffff, 0xffffff
-};
-
-static const uint32_t cterm_color_16[16] = {
- 0x000000, 0x0000c0, 0x008000, 0x004080,
- 0xc00000, 0xc000c0, 0x808000, 0xc0c0c0,
- 0x808080, 0x6060ff, 0x00ff00, 0x00ffff,
- 0xff8080, 0xff40ff, 0xffff00, 0xffffff
-};
-
-static int current_syn_id;
-
-#define PRCOLOR_BLACK 0
-#define PRCOLOR_WHITE 0xffffff
-
-static TriState curr_italic;
-static TriState curr_bold;
-static TriState curr_underline;
-static uint32_t curr_bg;
-static uint32_t curr_fg;
-static int page_count;
-
-#define OPT_MBFONT_USECOURIER 0
-#define OPT_MBFONT_ASCII 1
-#define OPT_MBFONT_REGULAR 2
-#define OPT_MBFONT_BOLD 3
-#define OPT_MBFONT_OBLIQUE 4
-#define OPT_MBFONT_BOLDOBLIQUE 5
-#define OPT_MBFONT_NUM_OPTIONS 6
-
-static option_table_T mbfont_opts[OPT_MBFONT_NUM_OPTIONS] =
-{
- { "c", FALSE, 0, NULL, 0, FALSE },
- { "a", FALSE, 0, NULL, 0, FALSE },
- { "r", FALSE, 0, NULL, 0, FALSE },
- { "b", FALSE, 0, NULL, 0, FALSE },
- { "i", FALSE, 0, NULL, 0, FALSE },
- { "o", FALSE, 0, NULL, 0, FALSE },
-};
-
-/*
- * These values determine the print position on a page.
- */
-typedef struct {
- int lead_spaces; // remaining spaces for a TAB
- int print_pos; // virtual column for computing TABs
- colnr_T column; // byte column
- linenr_T file_line; // line nr in the buffer
- size_t bytes_printed; // bytes printed so far
- int ff; // seen form feed character
-} prt_pos_T;
-
-struct prt_mediasize_S {
- char *name;
- double width; // width and height in points for portrait
- double height;
-};
-
-// PS font names, must be in Roman, Bold, Italic, Bold-Italic order
-struct prt_ps_font_S {
- int wx;
- int uline_offset;
- int uline_width;
- int bbox_min_y;
- int bbox_max_y;
- char *(ps_fontname[4]);
-};
-
-/* Structures to map user named encoding and mapping to PS equivalents for
- * building CID font name */
-struct prt_ps_encoding_S {
- char *encoding;
- char *cmap_encoding;
- int needs_charset;
-};
-
-struct prt_ps_charset_S {
- char *charset;
- char *cmap_charset;
- int has_charset;
-};
-
-// Collections of encodings and charsets for multi-byte printing
-struct prt_ps_mbfont_S {
- int num_encodings;
- struct prt_ps_encoding_S *encodings;
- int num_charsets;
- struct prt_ps_charset_S *charsets;
- char *ascii_enc;
- char *defcs;
-};
-
-// Types of PS resource file currently used
-typedef enum {
- PRT_RESOURCE_TYPE_PROCSET = 0,
- PRT_RESOURCE_TYPE_ENCODING = 1,
- PRT_RESOURCE_TYPE_CMAP = 2,
-} PrtResourceType;
-
-// String versions of PS resource types
-static const char *const prt_resource_types[] =
-{
- [PRT_RESOURCE_TYPE_PROCSET] = "procset",
- [PRT_RESOURCE_TYPE_ENCODING] = "encoding",
- [PRT_RESOURCE_TYPE_CMAP] = "cmap",
-};
-
-struct prt_ps_resource_S {
- char_u name[64];
- char_u filename[MAXPATHL + 1];
- PrtResourceType type;
- char_u title[256];
- char_u version[256];
-};
-
-struct prt_dsc_comment_S {
- char *string;
- int len;
- int type;
-};
-
-struct prt_dsc_line_S {
- int type;
- char_u *string;
- int len;
-};
-
-/* Static buffer to read initial comments in a resource file, some can have a
- * couple of KB of comments! */
-#define PRT_FILE_BUFFER_LEN (2048)
-struct prt_resfile_buffer_S {
- char_u buffer[PRT_FILE_BUFFER_LEN];
- int len;
- int line_start;
- int line_end;
-};
-
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "hardcopy.c.generated.h"
-#endif
-
-/*
- * Parse 'printoptions' and set the flags in "printer_opts".
- * Returns an error message or NULL;
- */
-char *parse_printoptions(void)
-{
- return parse_list_options(p_popt, printer_opts, OPT_PRINT_NUM_OPTIONS);
-}
-
-/*
- * Parse 'printoptions' and set the flags in "printer_opts".
- * Returns an error message or NULL;
- */
-char *parse_printmbfont(void)
-{
- return parse_list_options(p_pmfn, mbfont_opts, OPT_MBFONT_NUM_OPTIONS);
-}
-
-/*
- * Parse a list of options in the form
- * option:value,option:value,option:value
- *
- * "value" can start with a number which is parsed out, e.g. margin:12mm
- *
- * Returns an error message for an illegal option, NULL otherwise.
- * Only used for the printer at the moment...
- */
-static char *parse_list_options(char_u *option_str, option_table_T *table, size_t table_size)
-{
- option_table_T *old_opts;
- char *ret = NULL;
- char_u *stringp;
- char_u *colonp;
- char *commap;
- char *p;
- size_t idx = 0; // init for GCC
- int len;
-
- // Save the old values, so that they can be restored in case of an error.
- old_opts = (option_table_T *)xmalloc(sizeof(option_table_T) * table_size);
-
- for (idx = 0; idx < table_size; idx++) {
- old_opts[idx] = table[idx];
- table[idx].present = false;
- }
-
- /*
- * Repeat for all comma separated parts.
- */
- stringp = option_str;
- while (*stringp) {
- colonp = (char_u *)vim_strchr((char *)stringp, ':');
- if (colonp == NULL) {
- ret = N_("E550: Missing colon");
- break;
- }
- commap = vim_strchr((char *)stringp, ',');
- if (commap == NULL) {
- commap = (char *)option_str + STRLEN(option_str);
- }
-
- len = (int)(colonp - stringp);
-
- for (idx = 0; idx < table_size; ++idx) {
- if (STRNICMP(stringp, table[idx].name, len) == 0) {
- break;
- }
- }
-
- if (idx == table_size) {
- ret = N_("E551: Illegal component");
- break;
- }
-
- p = (char *)colonp + 1;
- table[idx].present = true;
-
- if (table[idx].hasnum) {
- if (!ascii_isdigit(*p)) {
- ret = N_("E552: digit expected");
- break;
- }
-
- table[idx].number = getdigits_int(&p, false, 0);
- }
-
- table[idx].string = (char_u *)p;
- table[idx].strlen = (int)(commap - p);
-
- stringp = (char_u *)commap;
- if (*stringp == ',') {
- stringp++;
- }
- }
-
- if (ret != NULL) {
- // Restore old options in case of error
- for (idx = 0; idx < table_size; idx++) {
- table[idx] = old_opts[idx];
- }
- }
-
- xfree(old_opts);
- 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.
- */
-static uint32_t darken_rgb(uint32_t rgb)
-{
- return ((rgb >> 17) << 16)
- + (((rgb & 0xff00) >> 9) << 8)
- + ((rgb & 0xff) >> 1);
-}
-
-static uint32_t prt_get_term_color(int colorindex)
-{
- // TODO(vim): Should check for xterm with 88 or 256 colors.
- if (t_colors > 8) {
- return cterm_color_16[colorindex % 16];
- }
- return cterm_color_8[colorindex % 8];
-}
-
-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);
-
- uint32_t fg_color = prt_get_color(hl_id, modec);
-
- if (fg_color == PRCOLOR_WHITE) {
- fg_color = PRCOLOR_BLACK;
- } else if (*p_bg == 'd') {
- fg_color = darken_rgb(fg_color);
- }
-
- pattr->fg_color = fg_color;
- pattr->bg_color = PRCOLOR_WHITE;
-}
-
-static void prt_set_fg(uint32_t fg)
-{
- if (fg != curr_fg) {
- curr_fg = fg;
- mch_print_set_fg(fg);
- }
-}
-
-static void prt_set_bg(uint32_t bg)
-{
- if (bg != curr_bg) {
- curr_bg = bg;
- mch_print_set_bg(bg);
- }
-}
-
-static void prt_set_font(const TriState bold, const TriState italic, const TriState underline)
-{
- if (curr_bold != bold
- || curr_italic != italic
- || curr_underline != underline) {
- curr_underline = underline;
- curr_italic = italic;
- curr_bold = bold;
- mch_print_set_font(bold, italic, underline);
- }
-}
-
-// Print the line number in the left margin.
-static void prt_line_number(prt_settings_T *const psettings, const int page_line,
- const linenr_T lnum)
-{
- prt_set_fg(psettings->number.fg_color);
- prt_set_bg(psettings->number.bg_color);
- prt_set_font(psettings->number.bold, psettings->number.italic,
- psettings->number.underline);
- mch_print_start_line(true, page_line);
-
- // Leave two spaces between the number and the text; depends on
- // PRINT_NUMBER_WIDTH.
- char tbuf[20];
- snprintf(tbuf, sizeof(tbuf), "%6ld", (long)lnum);
- for (int i = 0; i < 6; i++) {
- (void)mch_print_text_out((char_u *)(&tbuf[i]), 1);
- }
-
- if (psettings->do_syntax) {
- // Set colors for next character.
- current_syn_id = -1;
- } else {
- // Set colors and font back to normal.
- prt_set_fg(PRCOLOR_BLACK);
- prt_set_bg(PRCOLOR_WHITE);
- prt_set_font(kFalse, kFalse, kFalse);
- }
-}
-
-/*
- * Get the currently effective header height.
- */
-int prt_header_height(void)
-{
- if (printer_opts[OPT_PRINT_HEADERHEIGHT].present) {
- return printer_opts[OPT_PRINT_HEADERHEIGHT].number;
- }
- return 2;
-}
-
-/*
- * Return TRUE if using a line number for printing.
- */
-int prt_use_number(void)
-{
- return printer_opts[OPT_PRINT_NUMBER].present
- && TOLOWER_ASC(printer_opts[OPT_PRINT_NUMBER].string[0]) == 'y';
-}
-
-/*
- * Return the unit used in a margin item in 'printoptions'.
- * Returns PRT_UNIT_NONE if not recognized.
- */
-int prt_get_unit(int idx)
-{
- int u = PRT_UNIT_NONE;
- int i;
- static char *(units[4]) = PRT_UNIT_NAMES;
-
- if (printer_opts[idx].present) {
- for (i = 0; i < 4; ++i) {
- if (STRNICMP(printer_opts[idx].string, units[i], 2) == 0) {
- u = i;
- break;
- }
- }
- }
- return u;
-}
-
-// Print the page header.
-static void prt_header(prt_settings_T *const psettings, const int pagenum, const linenr_T lnum)
-{
- int width = psettings->chars_per_line;
-
- // Also use the space for the line number.
- if (prt_use_number()) {
- width += PRINT_NUMBER_WIDTH;
- }
-
- assert(width >= 0);
- const size_t tbuf_size = (size_t)width + IOSIZE;
- char_u *tbuf = xmalloc(tbuf_size);
-
- if (*p_header != NUL) {
- linenr_T tmp_lnum, tmp_topline, tmp_botline;
- int use_sandbox = FALSE;
-
- /*
- * Need to (temporarily) set current line number and first/last line
- * number on the 'window'. Since we don't know how long the page is,
- * set the first and current line number to the top line, and guess
- * that the page length is 64.
- */
- tmp_lnum = curwin->w_cursor.lnum;
- tmp_topline = curwin->w_topline;
- tmp_botline = curwin->w_botline;
- curwin->w_cursor.lnum = lnum;
- curwin->w_topline = lnum;
- curwin->w_botline = lnum + 63;
- printer_page_num = pagenum;
-
- use_sandbox = was_set_insecurely(curwin, "printheader", 0);
- build_stl_str_hl(curwin, (char *)tbuf, (size_t)width + IOSIZE,
- (char *)p_header, use_sandbox,
- ' ', width, NULL, NULL);
-
- // Reset line numbers
- curwin->w_cursor.lnum = tmp_lnum;
- curwin->w_topline = tmp_topline;
- curwin->w_botline = tmp_botline;
- } else {
- snprintf((char *)tbuf, tbuf_size, _("Page %d"), pagenum);
- }
-
- prt_set_fg(PRCOLOR_BLACK);
- prt_set_bg(PRCOLOR_WHITE);
- prt_set_font(kTrue, kFalse, kFalse);
-
- // Use a negative line number to indicate printing in the top margin.
- int page_line = 0 - prt_header_height();
- mch_print_start_line(true, page_line);
- for (char_u *p = tbuf; *p != NUL;) {
- const int l = utfc_ptr2len((char *)p);
- assert(l >= 0);
- if (mch_print_text_out(p, (size_t)l)) {
- page_line++;
- if (page_line >= 0) { // out of room in header
- break;
- }
- mch_print_start_line(true, page_line);
- }
- p += l;
- }
-
- xfree(tbuf);
-
- if (psettings->do_syntax) {
- // Set colors for next character.
- current_syn_id = -1;
- } else {
- // Set colors and font back to normal.
- prt_set_fg(PRCOLOR_BLACK);
- prt_set_bg(PRCOLOR_WHITE);
- prt_set_font(kFalse, kFalse, kFalse);
- }
-}
-
-/*
- * Display a print status message.
- */
-static void prt_message(char_u *s)
-{
- // TODO(bfredl): delete this
- grid_fill(&default_grid, Rows - 1, Rows, 0, Columns, ' ', ' ', 0);
- grid_puts(&default_grid, s, Rows - 1, 0, HL_ATTR(HLF_R));
- ui_flush();
-}
-
-void ex_hardcopy(exarg_T *eap)
-{
- linenr_T lnum;
- int collated_copies, uncollated_copies;
- prt_settings_T settings;
- size_t bytes_to_print = 0;
- int page_line;
- int jobsplit;
-
- CLEAR_FIELD(settings);
- settings.has_color = true;
-
- if (*eap->arg == '>') {
- char *errormsg = NULL;
-
- // Expand things like "%.ps".
- if (expand_filename(eap, eap->cmdlinep, &errormsg) == FAIL) {
- if (errormsg != NULL) {
- emsg(errormsg);
- }
- return;
- }
- settings.outfile = (char_u *)skipwhite(eap->arg + 1);
- } else if (*eap->arg != NUL) {
- settings.arguments = (char_u *)eap->arg;
- }
-
- /*
- * Initialise for printing. Ask the user for settings, unless forceit is
- * set.
- * The mch_print_init() code should set up margins if applicable. (It may
- * not be a real printer - for example the engine might generate HTML or
- * PS.)
- */
- if (mch_print_init(&settings,
- 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;
- }
-
- settings.modec = 'c';
-
- if (!syntax_present(curwin)) {
- settings.do_syntax = FALSE;
- } else if (printer_opts[OPT_PRINT_SYNTAX].present
- && TOLOWER_ASC(printer_opts[OPT_PRINT_SYNTAX].string[0]) != 'a') {
- settings.do_syntax =
- (TOLOWER_ASC(printer_opts[OPT_PRINT_SYNTAX].string[0]) == 'y');
- } else {
- settings.do_syntax = settings.has_color;
- }
-
- // Set up printing attributes for line numbers
- settings.number.fg_color = PRCOLOR_BLACK;
- settings.number.bg_color = PRCOLOR_WHITE;
- settings.number.bold = kFalse;
- settings.number.italic = kTrue;
- settings.number.underline = kFalse;
-
- // Syntax highlighting of line numbers.
- if (prt_use_number() && settings.do_syntax) {
- int id = syn_name2id("LineNr");
- if (id > 0) {
- id = syn_get_final_id(id);
- }
-
- prt_get_attr(id, &settings.number, settings.modec);
- }
-
- /*
- * Estimate the total lines to be printed
- */
- for (lnum = eap->line1; lnum <= eap->line2; lnum++) {
- bytes_to_print += STRLEN(skipwhite((char *)ml_get(lnum)));
- }
- if (bytes_to_print == 0) {
- msg(_("No text to be printed"));
- goto print_fail_no_begin;
- }
-
- // Set colors and font to normal.
- curr_bg = 0xffffffff;
- curr_fg = 0xffffffff;
- curr_italic = kNone;
- curr_bold = kNone;
- curr_underline = kNone;
-
- prt_set_fg(PRCOLOR_BLACK);
- prt_set_bg(PRCOLOR_WHITE);
- prt_set_font(kFalse, kFalse, kFalse);
- current_syn_id = -1;
-
- jobsplit = (printer_opts[OPT_PRINT_JOBSPLIT].present
- && TOLOWER_ASC(printer_opts[OPT_PRINT_JOBSPLIT].string[0]) == 'y');
-
- if (!mch_print_begin(&settings)) {
- goto print_fail_no_begin;
- }
-
- /*
- * Loop over collated copies: 1 2 3, 1 2 3, ...
- */
- page_count = 0;
- for (collated_copies = 0;
- collated_copies < settings.n_collated_copies;
- collated_copies++) {
- prt_pos_T prtpos; // current print position
- prt_pos_T page_prtpos; // print position at page start
- int side;
-
- CLEAR_FIELD(page_prtpos);
- page_prtpos.file_line = eap->line1;
- prtpos = page_prtpos;
-
- if (jobsplit && collated_copies > 0) {
- // Splitting jobs: Stop a previous job and start a new one.
- mch_print_end(&settings);
- if (!mch_print_begin(&settings)) {
- goto print_fail_no_begin;
- }
- }
-
- /*
- * Loop over all pages in the print job: 1 2 3 ...
- */
- for (page_count = 0; prtpos.file_line <= eap->line2; ++page_count) {
- /*
- * Loop over uncollated copies: 1 1 1, 2 2 2, 3 3 3, ...
- * For duplex: 12 12 12 34 34 34, ...
- */
- for (uncollated_copies = 0;
- uncollated_copies < settings.n_uncollated_copies;
- uncollated_copies++) {
- // Set the print position to the start of this page.
- prtpos = page_prtpos;
-
- /*
- * Do front and rear side of a page.
- */
- for (side = 0; side <= settings.duplex; ++side) {
- /*
- * Print one page.
- */
-
- // Check for interrupt character every page.
- os_breakcheck();
- if (got_int || settings.user_abort) {
- goto print_fail;
- }
-
- assert(prtpos.bytes_printed <= SIZE_MAX / 100);
- sprintf((char *)IObuff, _("Printing page %d (%zu%%)"),
- page_count + 1 + side,
- prtpos.bytes_printed * 100 / bytes_to_print);
- if (!mch_print_begin_page(IObuff)) {
- goto print_fail;
- }
-
- if (settings.n_collated_copies > 1) {
- sprintf((char *)IObuff + STRLEN(IObuff),
- _(" Copy %d of %d"),
- collated_copies + 1,
- settings.n_collated_copies);
- }
- prt_message(IObuff);
-
- /*
- * Output header if required
- */
- if (prt_header_height() > 0) {
- prt_header(&settings, page_count + 1 + side,
- prtpos.file_line);
- }
-
- for (page_line = 0; page_line < settings.lines_per_page;
- ++page_line) {
- prtpos.column = hardcopy_line(&settings,
- page_line, &prtpos);
- if (prtpos.column == 0) {
- // finished a file line
- prtpos.bytes_printed +=
- STRLEN(skipwhite((char *)ml_get(prtpos.file_line)));
- if (++prtpos.file_line > eap->line2) {
- break; // reached the end
- }
- } else if (prtpos.ff) {
- // Line had a formfeed in it - start new page but
- // stay on the current line
- break;
- }
- }
-
- if (!mch_print_end_page()) {
- goto print_fail;
- }
- if (prtpos.file_line > eap->line2) {
- break; // reached the end
- }
- }
-
- /*
- * Extra blank page for duplexing with odd number of pages and
- * more copies to come.
- */
- if (prtpos.file_line > eap->line2 && settings.duplex
- && side == 0
- && uncollated_copies + 1 < settings.n_uncollated_copies) {
- if (!mch_print_blank_page()) {
- goto print_fail;
- }
- }
- }
- if (settings.duplex && prtpos.file_line <= eap->line2) {
- page_count++;
- }
-
- // Remember the position where the next page starts.
- page_prtpos = prtpos;
- }
-
- vim_snprintf((char *)IObuff, IOSIZE, _("Printed: %s"),
- settings.jobname);
- prt_message(IObuff);
- }
-
-print_fail:
- if (got_int || settings.user_abort) {
- sprintf((char *)IObuff, "%s", _("Printing aborted"));
- prt_message(IObuff);
- }
- mch_print_end(&settings);
-
-print_fail_no_begin:
- mch_print_cleanup();
-}
-
-/*
- * Print one page line.
- * Return the next column to print, or zero if the line is finished.
- */
-static colnr_T hardcopy_line(prt_settings_T *psettings, int page_line, prt_pos_T *ppos)
-{
- colnr_T col;
- char_u *line;
- int need_break = FALSE;
- int outputlen;
- int tab_spaces;
- int print_pos;
- prt_text_attr_T attr;
- int id;
-
- if (ppos->column == 0 || ppos->ff) {
- print_pos = 0;
- tab_spaces = 0;
- if (!ppos->ff && prt_use_number()) {
- prt_line_number(psettings, page_line, ppos->file_line);
- }
- ppos->ff = FALSE;
- } else {
- // left over from wrap halfway through a tab
- print_pos = ppos->print_pos;
- tab_spaces = ppos->lead_spaces;
- }
-
- mch_print_start_line(false, page_line);
- line = ml_get(ppos->file_line);
-
- /*
- * 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((char *)line + col)) < 1) {
- outputlen = 1;
- }
- // syntax highlighting stuff.
- if (psettings->do_syntax) {
- id = syn_get_id(curwin, ppos->file_line, col, 1, NULL, false);
- if (id > 0) {
- id = syn_get_final_id(id);
- } else {
- id = 0;
- }
- // Get the line again, a multi-line regexp may invalidate it.
- line = ml_get(ppos->file_line);
-
- if (id != current_syn_id) {
- current_syn_id = id;
- prt_get_attr(id, &attr, psettings->modec);
- prt_set_font(attr.bold, attr.italic, attr.underline);
- prt_set_fg(attr.fg_color);
- prt_set_bg(attr.bg_color);
- }
- }
-
- /*
- * Appropriately expand any tabs to spaces.
- */
- if (line[col] == TAB || tab_spaces != 0) {
- if (tab_spaces == 0) {
- tab_spaces = tabstop_padding(print_pos,
- curbuf->b_p_ts,
- curbuf->b_p_vts_array);
- }
-
- while (tab_spaces > 0) {
- need_break = mch_print_text_out((char_u *)" ", 1);
- print_pos++;
- tab_spaces--;
- if (need_break) {
- break;
- }
- }
- // Keep the TAB if we didn't finish it.
- if (need_break && tab_spaces > 0) {
- break;
- }
- } else if (line[col] == FF
- && printer_opts[OPT_PRINT_FORMFEED].present
- && TOLOWER_ASC(printer_opts[OPT_PRINT_FORMFEED].string[0])
- == 'y') {
- ppos->ff = TRUE;
- need_break = 1;
- } else {
- need_break = mch_print_text_out(line + col, (size_t)outputlen);
- print_pos += utf_ptr2cells((char *)line + col);
- }
- }
-
- ppos->lead_spaces = tab_spaces;
- ppos->print_pos = print_pos;
-
- /*
- * Start next line of file if we clip lines, or have reached end of the
- * line, unless we are doing a formfeed.
- */
- if (!ppos->ff
- && (line[col] == NUL
- || (printer_opts[OPT_PRINT_WRAP].present
- && TOLOWER_ASC(printer_opts[OPT_PRINT_WRAP].string[0])
- == 'n'))) {
- return 0;
- }
- return col;
-}
-
-/*
- * PS printer stuff.
- *
- * Sources of information to help maintain the PS printing code:
- *
- * 1. PostScript Language Reference, 3rd Edition,
- * Addison-Wesley, 1999, ISBN 0-201-37922-8
- * 2. PostScript Language Program Design,
- * Addison-Wesley, 1988, ISBN 0-201-14396-8
- * 3. PostScript Tutorial and Cookbook,
- * Addison Wesley, 1985, ISBN 0-201-10179-3
- * 4. PostScript Language Document Structuring Conventions Specification,
- * version 3.0,
- * Adobe Technote 5001, 25th September 1992
- * 5. PostScript Printer Description File Format Specification, Version 4.3,
- * Adobe technote 5003, 9th February 1996
- * 6. Adobe Font Metrics File Format Specification, Version 4.1,
- * Adobe Technote 5007, 7th October 1998
- * 7. Adobe CMap and CIDFont Files Specification, Version 1.0,
- * Adobe Technote 5014, 8th October 1996
- * 8. Adobe CJKV Character Collections and CMaps for CID-Keyed Fonts,
- * Adoboe Technote 5094, 8th September, 2001
- * 9. CJKV Information Processing, 2nd Edition,
- * O'Reilly, 2002, ISBN 1-56592-224-7
- *
- * Some of these documents can be found in PDF form on Adobe's web site -
- * http://www.adobe.com
- */
-
-#define PRT_PS_DEFAULT_DPI (72) // Default user space resolution
-#define PRT_PS_DEFAULT_FONTSIZE (10)
-
-#define PRT_MEDIASIZE_LEN ARRAY_SIZE(prt_mediasize)
-
-static struct prt_mediasize_S prt_mediasize[] =
-{
- { "A4", 595.0, 842.0 },
- { "letter", 612.0, 792.0 },
- { "10x14", 720.0, 1008.0 },
- { "A3", 842.0, 1191.0 },
- { "A5", 420.0, 595.0 },
- { "B4", 729.0, 1032.0 },
- { "B5", 516.0, 729.0 },
- { "executive", 522.0, 756.0 },
- { "folio", 595.0, 935.0 },
- { "ledger", 1224.0, 792.0 }, // Yes, it is wider than taller!
- { "legal", 612.0, 1008.0 },
- { "quarto", 610.0, 780.0 },
- { "statement", 396.0, 612.0 },
- { "tabloid", 792.0, 1224.0 }
-};
-
-#define PRT_PS_FONT_ROMAN (0)
-#define PRT_PS_FONT_BOLD (1)
-#define PRT_PS_FONT_OBLIQUE (2)
-#define PRT_PS_FONT_BOLDOBLIQUE (3)
-
-// Standard font metrics for Courier family
-static struct prt_ps_font_S prt_ps_courier_font =
-{
- 600,
- -100, 50,
- -250, 805,
- { "Courier", "Courier-Bold", "Courier-Oblique", "Courier-BoldOblique" }
-};
-
-// Generic font metrics for multi-byte fonts
-static struct prt_ps_font_S prt_ps_mb_font =
-{
- 1000,
- -100, 50,
- -250, 805,
- { NULL, NULL, NULL, NULL }
-};
-
-// Pointer to current font set being used
-static struct prt_ps_font_S *prt_ps_font;
-
-#define CS_JIS_C_1978 (0x01)
-#define CS_JIS_X_1983 (0x02)
-#define CS_JIS_X_1990 (0x04)
-#define CS_NEC (0x08)
-#define CS_MSWINDOWS (0x10)
-#define CS_CP932 (0x20)
-#define CS_KANJITALK6 (0x40)
-#define CS_KANJITALK7 (0x80)
-
-// Japanese encodings and charsets
-static struct prt_ps_encoding_S j_encodings[] =
-{
- { "iso-2022-jp", NULL, (CS_JIS_C_1978|CS_JIS_X_1983|CS_JIS_X_1990|
- CS_NEC) },
- { "euc-jp", "EUC", (CS_JIS_C_1978|CS_JIS_X_1983|CS_JIS_X_1990) },
- { "sjis", "RKSJ", (CS_JIS_C_1978|CS_JIS_X_1983|CS_MSWINDOWS|
- CS_KANJITALK6|CS_KANJITALK7) },
- { "cp932", "RKSJ", CS_JIS_X_1983 },
- { "ucs-2", "UCS2", CS_JIS_X_1990 },
- { "utf-8", "UTF8", CS_JIS_X_1990 }
-};
-static struct prt_ps_charset_S j_charsets[] =
-{
- { "JIS_C_1978", "78", CS_JIS_C_1978 },
- { "JIS_X_1983", NULL, CS_JIS_X_1983 },
- { "JIS_X_1990", "Hojo", CS_JIS_X_1990 },
- { "NEC", "Ext", CS_NEC },
- { "MSWINDOWS", "90ms", CS_MSWINDOWS },
- { "CP932", "90ms", CS_JIS_X_1983 },
- { "KANJITALK6", "83pv", CS_KANJITALK6 },
- { "KANJITALK7", "90pv", CS_KANJITALK7 }
-};
-
-#define CS_GB_2312_80 (0x01)
-#define CS_GBT_12345_90 (0x02)
-#define CS_GBK2K (0x04)
-#define CS_SC_MAC (0x08)
-#define CS_GBT_90_MAC (0x10)
-#define CS_GBK (0x20)
-#define CS_SC_ISO10646 (0x40)
-
-// Simplified Chinese encodings and charsets
-static struct prt_ps_encoding_S sc_encodings[] =
-{
- { "iso-2022", NULL, (CS_GB_2312_80|CS_GBT_12345_90) },
- { "gb18030", NULL, CS_GBK2K },
- { "euc-cn", "EUC", (CS_GB_2312_80|CS_GBT_12345_90|CS_SC_MAC|
- CS_GBT_90_MAC) },
- { "gbk", "EUC", CS_GBK },
- { "ucs-2", "UCS2", CS_SC_ISO10646 },
- { "utf-8", "UTF8", CS_SC_ISO10646 }
-};
-static struct prt_ps_charset_S sc_charsets[] =
-{
- { "GB_2312-80", "GB", CS_GB_2312_80 },
- { "GBT_12345-90", "GBT", CS_GBT_12345_90 },
- { "MAC", "GBpc", CS_SC_MAC },
- { "GBT-90_MAC", "GBTpc", CS_GBT_90_MAC },
- { "GBK", "GBK", CS_GBK },
- { "GB18030", "GBK2K", CS_GBK2K },
- { "ISO10646", "UniGB", CS_SC_ISO10646 }
-};
-
-#define CS_CNS_PLANE_1 (0x01)
-#define CS_CNS_PLANE_2 (0x02)
-#define CS_CNS_PLANE_1_2 (0x04)
-#define CS_B5 (0x08)
-#define CS_ETEN (0x10)
-#define CS_HK_GCCS (0x20)
-#define CS_HK_SCS (0x40)
-#define CS_HK_SCS_ETEN (0x80)
-#define CS_MTHKL (0x100)
-#define CS_MTHKS (0x200)
-#define CS_DLHKL (0x400)
-#define CS_DLHKS (0x800)
-#define CS_TC_ISO10646 (0x1000)
-
-// Traditional Chinese encodings and charsets
-static struct prt_ps_encoding_S tc_encodings[] =
-{
- { "iso-2022", NULL, (CS_CNS_PLANE_1|CS_CNS_PLANE_2) },
- { "euc-tw", "EUC", CS_CNS_PLANE_1_2 },
- { "big5", "B5", (CS_B5|CS_ETEN|CS_HK_GCCS|CS_HK_SCS|
- CS_HK_SCS_ETEN|CS_MTHKL|CS_MTHKS|CS_DLHKL|
- CS_DLHKS) },
- { "cp950", "B5", CS_B5 },
- { "ucs-2", "UCS2", CS_TC_ISO10646 },
- { "utf-8", "UTF8", CS_TC_ISO10646 },
- { "utf-16", "UTF16", CS_TC_ISO10646 },
- { "utf-32", "UTF32", CS_TC_ISO10646 }
-};
-static struct prt_ps_charset_S tc_charsets[] =
-{
- { "CNS_1992_1", "CNS1", CS_CNS_PLANE_1 },
- { "CNS_1992_2", "CNS2", CS_CNS_PLANE_2 },
- { "CNS_1993", "CNS", CS_CNS_PLANE_1_2 },
- { "BIG5", NULL, CS_B5 },
- { "CP950", NULL, CS_B5 },
- { "ETEN", "ETen", CS_ETEN },
- { "HK_GCCS", "HKgccs", CS_HK_GCCS },
- { "SCS", "HKscs", CS_HK_SCS },
- { "SCS_ETEN", "ETHK", CS_HK_SCS_ETEN },
- { "MTHKL", "HKm471", CS_MTHKL },
- { "MTHKS", "HKm314", CS_MTHKS },
- { "DLHKL", "HKdla", CS_DLHKL },
- { "DLHKS", "HKdlb", CS_DLHKS },
- { "ISO10646", "UniCNS", CS_TC_ISO10646 }
-};
-
-#define CS_KR_X_1992 (0x01)
-#define CS_KR_MAC (0x02)
-#define CS_KR_X_1992_MS (0x04)
-#define CS_KR_ISO10646 (0x08)
-
-// Korean encodings and charsets
-static struct prt_ps_encoding_S k_encodings[] =
-{
- { "iso-2022-kr", NULL, CS_KR_X_1992 },
- { "euc-kr", "EUC", (CS_KR_X_1992|CS_KR_MAC) },
- { "johab", "Johab", CS_KR_X_1992 },
- { "cp1361", "Johab", CS_KR_X_1992 },
- { "uhc", "UHC", CS_KR_X_1992_MS },
- { "cp949", "UHC", CS_KR_X_1992_MS },
- { "ucs-2", "UCS2", CS_KR_ISO10646 },
- { "utf-8", "UTF8", CS_KR_ISO10646 }
-};
-static struct prt_ps_charset_S k_charsets[] =
-{
- { "KS_X_1992", "KSC", CS_KR_X_1992 },
- { "CP1361", "KSC", CS_KR_X_1992 },
- { "MAC", "KSCpc", CS_KR_MAC },
- { "MSWINDOWS", "KSCms", CS_KR_X_1992_MS },
- { "CP949", "KSCms", CS_KR_X_1992_MS },
- { "WANSUNG", "KSCms", CS_KR_X_1992_MS },
- { "ISO10646", "UniKS", CS_KR_ISO10646 }
-};
-
-static struct prt_ps_mbfont_S prt_ps_mbfonts[] =
-{
- {
- ARRAY_SIZE(j_encodings),
- j_encodings,
- ARRAY_SIZE(j_charsets),
- j_charsets,
- "jis_roman",
- "JIS_X_1983"
- },
- {
- ARRAY_SIZE(sc_encodings),
- sc_encodings,
- ARRAY_SIZE(sc_charsets),
- sc_charsets,
- "gb_roman",
- "GB_2312-80"
- },
- {
- ARRAY_SIZE(tc_encodings),
- tc_encodings,
- ARRAY_SIZE(tc_charsets),
- tc_charsets,
- "cns_roman",
- "BIG5"
- },
- {
- ARRAY_SIZE(k_encodings),
- k_encodings,
- ARRAY_SIZE(k_charsets),
- k_charsets,
- "ks_roman",
- "KS_X_1992"
- }
-};
-
-/* The PS prolog file version number has to match - if the prolog file is
- * updated, increment the number in the file and here. Version checking was
- * added as of VIM 6.2.
- * The CID prolog file version number behaves as per PS prolog.
- * Table of VIM and prolog versions:
- *
- * VIM Prolog CIDProlog
- * 6.2 1.3
- * 7.0 1.4 1.0
- */
-#define PRT_PROLOG_VERSION ((char_u *)"1.4")
-#define PRT_CID_PROLOG_VERSION ((char_u *)"1.0")
-
-// Strings to look for in a PS resource file
-#define PRT_RESOURCE_HEADER "%!PS-Adobe-"
-#define PRT_RESOURCE_RESOURCE "Resource-"
-#define PRT_RESOURCE_PROCSET "ProcSet"
-#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)
-#define PRT_DSC_TITLE_TYPE (1)
-#define PRT_DSC_VERSION_TYPE (2)
-#define PRT_DSC_ENDCOMMENTS_TYPE (3)
-
-#define PRT_DSC_TITLE "%%Title:"
-#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[] =
-{
- { PRT_DSC_TITLE, SIZEOF_CSTR(PRT_DSC_TITLE), PRT_DSC_TITLE_TYPE },
- { PRT_DSC_VERSION, SIZEOF_CSTR(PRT_DSC_VERSION),
- PRT_DSC_VERSION_TYPE },
- { PRT_DSC_ENDCOMMENTS, SIZEOF_CSTR(PRT_DSC_ENDCOMMENTS),
- PRT_DSC_ENDCOMMENTS_TYPE }
-};
-
-/*
- * Variables for the output PostScript file.
- */
-static FILE *prt_ps_fd;
-static bool prt_file_error;
-static char_u *prt_ps_file_name = NULL;
-
-/*
- * Various offsets and dimensions in default PostScript user space (points).
- * Used for text positioning calculations
- */
-static double prt_page_width;
-static double prt_page_height;
-static double prt_left_margin;
-static double prt_right_margin;
-static double prt_top_margin;
-static double prt_bottom_margin;
-static double prt_line_height;
-static double prt_first_line_height;
-static double prt_char_width;
-static double prt_number_width;
-static double prt_bgcol_offset;
-static double prt_pos_x_moveto = 0.0;
-static double prt_pos_y_moveto = 0.0;
-
-/*
- * Various control variables used to decide when and how to change the
- * PostScript graphics state.
- */
-static bool prt_need_moveto;
-static bool prt_do_moveto;
-static bool prt_need_font;
-static int prt_font;
-static bool prt_need_underline;
-static TriState prt_underline;
-static TriState prt_do_underline;
-static bool prt_need_fgcol;
-static uint32_t prt_fgcol;
-static bool prt_need_bgcol;
-static bool prt_do_bgcol;
-static uint32_t prt_bgcol;
-static uint32_t prt_new_bgcol;
-static bool prt_attribute_change;
-static double prt_text_run;
-static int prt_page_num;
-static int prt_bufsiz;
-
-/*
- * Variables controlling physical printing.
- */
-static int prt_media;
-static int prt_portrait;
-static int prt_num_copies;
-static int prt_duplex;
-static int prt_tumble;
-static int prt_collate;
-
-/*
- * Buffers used when generating PostScript output
- */
-static char prt_line_buffer[257];
-static garray_T prt_ps_buffer = GA_EMPTY_INIT_VALUE;
-
-static int prt_do_conv;
-static vimconv_T prt_conv;
-
-static int prt_out_mbyte;
-static int prt_custom_cmap;
-static char prt_cmap[80];
-static int prt_use_courier;
-static bool prt_in_ascii;
-static bool prt_half_width;
-static char *prt_ascii_encoding;
-static char_u prt_hexchar[] = "0123456789abcdef";
-
-static void prt_write_file_raw_len(char_u *buffer, size_t bytes)
-{
- if (!prt_file_error
- && fwrite(buffer, sizeof(char_u), bytes, prt_ps_fd) != bytes) {
- emsg(_("E455: Error writing to PostScript output file"));
- prt_file_error = true;
- }
-}
-
-static void prt_write_file(char *buffer)
-{
- prt_write_file_len((char_u *)buffer, STRLEN(buffer));
-}
-
-static void prt_write_file_len(char_u *buffer, size_t bytes)
-{
- prt_write_file_raw_len(buffer, bytes);
-}
-
-/*
- * Write a string.
- */
-static void prt_write_string(char *s)
-{
- vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer), "%s", s);
- prt_write_file(prt_line_buffer);
-}
-
-/*
- * Write an int and a space.
- */
-static void prt_write_int(int i)
-{
- snprintf(prt_line_buffer, sizeof(prt_line_buffer), "%d ", i);
- prt_write_file(prt_line_buffer);
-}
-
-/*
- * Write a boolean and a space.
- */
-static void prt_write_boolean(int b)
-{
- snprintf(prt_line_buffer, sizeof(prt_line_buffer), "%s ", (b ? "T" : "F"));
- prt_write_file(prt_line_buffer);
-}
-
-/*
- * Write PostScript to re-encode and define the font.
- */
-static void prt_def_font(char *new_name, char *encoding, int height, char *font)
-{
- vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer),
- "/_%s /VIM-%s /%s ref\n", new_name, encoding, font);
- prt_write_file(prt_line_buffer);
- if (prt_out_mbyte) {
- snprintf(prt_line_buffer, sizeof(prt_line_buffer), "/%s %d %f /_%s sffs\n",
- new_name, height, 500./prt_ps_courier_font.wx, new_name);
- } else {
- vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer),
- "/%s %d /_%s ffs\n", new_name, height, new_name);
- }
- prt_write_file(prt_line_buffer);
-}
-
-/*
- * Write a line to define the CID font.
- */
-static void prt_def_cidfont(char *new_name, int height, char *cidfont)
-{
- vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer),
- "/_%s /%s[/%s] vim_composefont\n", new_name, prt_cmap, cidfont);
- prt_write_file(prt_line_buffer);
- vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer),
- "/%s %d /_%s ffs\n", new_name, height, new_name);
- prt_write_file(prt_line_buffer);
-}
-
-/*
- * Write a line to define a duplicate of a CID font
- */
-static void prt_dup_cidfont(char *original_name, char *new_name)
-{
- vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer),
- "/%s %s d\n", new_name, original_name);
- prt_write_file(prt_line_buffer);
-}
-
-/*
- * Convert a real value into an integer and fractional part as integers, with
- * the fractional part being in the range [0,10^precision). The fractional part
- * is also rounded based on the precision + 1'th fractional digit.
- */
-static void prt_real_bits(double real, int precision, int *pinteger, int *pfraction)
-{
- int integer = (int)real;
- double fraction = real - integer;
- if (real < integer) {
- fraction = -fraction;
- }
- for (int i = 0; i < precision; i++) {
- fraction *= 10.0;
- }
-
- *pinteger = integer;
- *pfraction = (int)(fraction + 0.5);
-}
-
-/*
- * Write a real and a space. Save bytes if real value has no fractional part!
- * We use prt_real_bits() as %f in sprintf uses the locale setting to decide
- * what decimal point character to use, but PS always requires a '.'.
- */
-static void prt_write_real(double val, int prec)
-{
- int integer;
- int fraction;
-
- prt_real_bits(val, prec, &integer, &fraction);
- // Emit integer part
- snprintf(prt_line_buffer, sizeof(prt_line_buffer), "%d", integer);
- prt_write_file(prt_line_buffer);
- // Only emit fraction if necessary
- if (fraction != 0) {
- // Remove any trailing zeros
- while ((fraction % 10) == 0) {
- prec--;
- fraction /= 10;
- }
- // Emit fraction left padded with zeros
- snprintf(prt_line_buffer, sizeof(prt_line_buffer), ".%0*d",
- prec, fraction);
- prt_write_file(prt_line_buffer);
- }
- snprintf(prt_line_buffer, sizeof(prt_line_buffer), " ");
- prt_write_file(prt_line_buffer);
-}
-
-/*
- * Write a line to define a numeric variable.
- */
-static void prt_def_var(char *name, double value, int prec)
-{
- vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer),
- "/%s ", name);
- prt_write_file(prt_line_buffer);
- prt_write_real(value, prec);
- snprintf(prt_line_buffer, sizeof(prt_line_buffer), "d\n");
- prt_write_file(prt_line_buffer);
-}
-
-// Convert size from font space to user space at current font scale
-#define PRT_PS_FONT_TO_USER(scale, size) ((size) * ((scale)/1000.0))
-
-static void prt_flush_buffer(void)
-{
- if (!GA_EMPTY(&prt_ps_buffer)) {
- // Any background color must be drawn first
- if (prt_do_bgcol && (prt_new_bgcol != PRCOLOR_WHITE)) {
- unsigned int r, g, b;
-
- if (prt_do_moveto) {
- prt_write_real(prt_pos_x_moveto, 2);
- prt_write_real(prt_pos_y_moveto, 2);
- prt_write_string("m\n");
- prt_do_moveto = false;
- }
-
- // Size of rect of background color on which text is printed
- prt_write_real(prt_text_run, 2);
- prt_write_real(prt_line_height, 2);
-
- // Lastly add the color of the background
- r = (prt_new_bgcol & 0xff0000) >> 16;
- g = (prt_new_bgcol & 0xff00) >> 8;
- b = prt_new_bgcol & 0xff;
- prt_write_real(r / 255.0, 3);
- prt_write_real(g / 255.0, 3);
- prt_write_real(b / 255.0, 3);
- prt_write_string("bg\n");
- }
- // Draw underlines before the text as it makes it slightly easier to
- // find the starting point.
- if (prt_do_underline) {
- if (prt_do_moveto) {
- prt_write_real(prt_pos_x_moveto, 2);
- prt_write_real(prt_pos_y_moveto, 2);
- prt_write_string("m\n");
- prt_do_moveto = false;
- }
-
- // Underline length of text run
- prt_write_real(prt_text_run, 2);
- prt_write_string("ul\n");
- }
- // Draw the text
- if (prt_out_mbyte) {
- prt_write_string("<");
- } else {
- prt_write_string("(");
- }
- assert(prt_ps_buffer.ga_len >= 0);
- prt_write_file_raw_len(prt_ps_buffer.ga_data, (size_t)prt_ps_buffer.ga_len);
- if (prt_out_mbyte) {
- prt_write_string(">");
- } else {
- prt_write_string(")");
- }
- // Add a moveto if need be and use the appropriate show procedure
- if (prt_do_moveto) {
- prt_write_real(prt_pos_x_moveto, 2);
- prt_write_real(prt_pos_y_moveto, 2);
- // moveto and a show
- prt_write_string("ms\n");
- prt_do_moveto = false;
- } else { // Simple show
- prt_write_string("s\n");
- }
- ga_clear(&prt_ps_buffer);
- ga_init(&prt_ps_buffer, (int)sizeof(char), prt_bufsiz);
- }
-}
-
-static void prt_resource_name(char *filename, void *cookie)
-{
- char_u *resource_filename = cookie;
-
- if (STRLEN(filename) >= MAXPATHL) {
- *resource_filename = NUL;
- } else {
- STRCPY(resource_filename, filename);
- }
-}
-
-static int prt_find_resource(char *name, struct prt_ps_resource_S *resource)
-{
- char *buffer;
- int retval;
-
- buffer = xmallocz(MAXPATHL);
-
- STRLCPY(resource->name, name, 64);
- // Look for named resource file in runtimepath
- STRCPY(buffer, "print");
- add_pathsep(buffer);
- STRLCAT(buffer, name, MAXPATHL);
- STRLCAT(buffer, ".ps", MAXPATHL);
- resource->filename[0] = NUL;
- retval = (do_in_runtimepath(buffer, 0, prt_resource_name, resource->filename)
- && resource->filename[0] != NUL);
- xfree(buffer);
- return retval;
-}
-
-// PS CR and LF characters have platform independent values
-#define PSLF (0x0a)
-#define PSCR (0x0d)
-
-static struct prt_resfile_buffer_S prt_resfile;
-
-static int prt_resfile_next_line(void)
-{
- int idx;
-
- // Move to start of next line and then find end of line
- idx = prt_resfile.line_end + 1;
- while (idx < prt_resfile.len) {
- if (prt_resfile.buffer[idx] != PSLF && prt_resfile.buffer[idx] != PSCR) {
- break;
- }
- idx++;
- }
- prt_resfile.line_start = idx;
-
- while (idx < prt_resfile.len) {
- if (prt_resfile.buffer[idx] == PSLF || prt_resfile.buffer[idx] == PSCR) {
- break;
- }
- idx++;
- }
- prt_resfile.line_end = idx;
-
- return idx < prt_resfile.len;
-}
-
-static int prt_resfile_strncmp(int offset, const char *string, int len)
- FUNC_ATTR_NONNULL_ALL
-{
- // Force not equal if string is longer than remainder of line
- if (len > (prt_resfile.line_end - (prt_resfile.line_start + offset))) {
- return 1;
- }
- return STRNCMP(&prt_resfile.buffer[prt_resfile.line_start + offset],
- string, len);
-}
-
-static int prt_resfile_skip_nonws(int offset)
-{
- int idx;
-
- idx = prt_resfile.line_start + offset;
- while (idx < prt_resfile.line_end) {
- if (isspace(prt_resfile.buffer[idx])) {
- return idx - prt_resfile.line_start;
- }
- idx++;
- }
- return -1;
-}
-
-static int prt_resfile_skip_ws(int offset)
-{
- int idx;
-
- idx = prt_resfile.line_start + offset;
- while (idx < prt_resfile.line_end) {
- if (!isspace(prt_resfile.buffer[idx])) {
- return idx - prt_resfile.line_start;
- }
- idx++;
- }
- return -1;
-}
-
-/* prt_next_dsc() - returns detail on next DSC comment line found. Returns true
- * if a DSC comment is found, else false */
-static bool prt_next_dsc(struct prt_dsc_line_S *p_dsc_line)
- FUNC_ATTR_NONNULL_ALL
-{
- int comment;
- int offset;
-
- // Move to start of next line
- if (!prt_resfile_next_line()) {
- return false;
- }
- // DSC comments always start %%
- if (prt_resfile_strncmp(0, "%%", 2) != 0) {
- return false;
- }
- // Find type of DSC comment
- for (comment = 0; comment < (int)ARRAY_SIZE(prt_dsc_table); comment++) {
- if (prt_resfile_strncmp(0, prt_dsc_table[comment].string,
- prt_dsc_table[comment].len) == 0) {
- break;
- }
- }
- if (comment != ARRAY_SIZE(prt_dsc_table)) {
- // Return type of comment
- p_dsc_line->type = prt_dsc_table[comment].type;
- offset = prt_dsc_table[comment].len;
- } else {
- // Unrecognised DSC comment, skip to ws after comment leader
- p_dsc_line->type = PRT_DSC_MISC_TYPE;
- offset = prt_resfile_skip_nonws(0);
- if (offset == -1) {
- return false;
- }
- }
-
- // Skip ws to comment value
- offset = prt_resfile_skip_ws(offset);
- if (offset == -1) {
- return false;
- }
- p_dsc_line->string = &prt_resfile.buffer[prt_resfile.line_start + offset];
- p_dsc_line->len = prt_resfile.line_end - (prt_resfile.line_start + offset);
-
- return true;
-}
-
-/// Improved hand crafted parser to get the type, title, and version number of a
-/// PS resource file so the file details can be added to the DSC header comments.
-static bool prt_open_resource(struct prt_ps_resource_S *resource)
- FUNC_ATTR_NONNULL_ALL
-{
- struct prt_dsc_line_S dsc_line;
-
- FILE *fd_resource = os_fopen((char *)resource->filename, READBIN);
- if (fd_resource == NULL) {
- semsg(_("E624: Can't open file \"%s\""), resource->filename);
- return false;
- }
- CLEAR_FIELD(prt_resfile.buffer);
-
- // Parse first line to ensure valid resource file
- prt_resfile.len = (int)fread((char *)prt_resfile.buffer, sizeof(char_u),
- PRT_FILE_BUFFER_LEN, fd_resource);
- if (ferror(fd_resource)) {
- semsg(_("E457: Can't read PostScript resource file \"%s\""),
- resource->filename);
- fclose(fd_resource);
- return false;
- }
- fclose(fd_resource);
-
- prt_resfile.line_end = -1;
- prt_resfile.line_start = 0;
- if (!prt_resfile_next_line()) {
- return false;
- }
- int offset = 0;
-
- if (prt_resfile_strncmp(offset, PRT_RESOURCE_HEADER,
- (int)STRLEN(PRT_RESOURCE_HEADER)) != 0) {
- semsg(_("E618: file \"%s\" is not a PostScript resource file"),
- resource->filename);
- return false;
- }
-
- // Skip over any version numbers and following ws
- offset += (int)STRLEN(PRT_RESOURCE_HEADER);
- offset = prt_resfile_skip_nonws(offset);
- if (offset == -1) {
- return false;
- }
- offset = prt_resfile_skip_ws(offset);
- if (offset == -1) {
- return false;
- }
- if (prt_resfile_strncmp(offset, PRT_RESOURCE_RESOURCE,
- (int)STRLEN(PRT_RESOURCE_RESOURCE)) != 0) {
- semsg(_("E619: file \"%s\" is not a supported PostScript resource file"),
- resource->filename);
- return false;
- }
- offset += (int)STRLEN(PRT_RESOURCE_RESOURCE);
-
- // Decide type of resource in the file
- if (prt_resfile_strncmp(offset, PRT_RESOURCE_PROCSET,
- (int)STRLEN(PRT_RESOURCE_PROCSET)) == 0) {
- resource->type = PRT_RESOURCE_TYPE_PROCSET;
- } else if (prt_resfile_strncmp(offset, PRT_RESOURCE_ENCODING,
- (int)STRLEN(PRT_RESOURCE_ENCODING)) == 0) {
- resource->type = PRT_RESOURCE_TYPE_ENCODING;
- } else if (prt_resfile_strncmp(offset, PRT_RESOURCE_CMAP,
- (int)STRLEN(PRT_RESOURCE_CMAP)) == 0) {
- resource->type = PRT_RESOURCE_TYPE_CMAP;
- } else {
- semsg(_("E619: file \"%s\" is not a supported PostScript resource file"),
- resource->filename);
- return false;
- }
-
- // Look for title and version of resource
- resource->title[0] = '\0';
- resource->version[0] = '\0';
- bool seen_title = false;
- bool seen_version = false;
- bool seen_all = false;
- while (!seen_all && prt_next_dsc(&dsc_line)) {
- switch (dsc_line.type) {
- case PRT_DSC_TITLE_TYPE:
- STRLCPY(resource->title, dsc_line.string, dsc_line.len + 1);
- seen_title = true;
- if (seen_version) {
- seen_all = true;
- }
- break;
-
- case PRT_DSC_VERSION_TYPE:
- STRLCPY(resource->version, dsc_line.string, dsc_line.len + 1);
- seen_version = true;
- if (seen_title) {
- seen_all = true;
- }
- break;
-
- case PRT_DSC_ENDCOMMENTS_TYPE:
- // Won't find title or resource after this comment, stop searching
- seen_all = true;
- break;
-
- case PRT_DSC_MISC_TYPE:
- // Not interested in whatever comment this line had
- break;
- }
- }
-
- if (!seen_title || !seen_version) {
- semsg(_("E619: file \"%s\" is not a supported PostScript resource file"),
- resource->filename);
- return false;
- }
-
- return true;
-}
-
-static bool prt_check_resource(const struct prt_ps_resource_S *resource, const char_u *version)
- FUNC_ATTR_NONNULL_ALL
-{
- // Version number m.n should match, the revision number does not matter
- if (STRNCMP(resource->version, version, STRLEN(version))) {
- semsg(_("E621: \"%s\" resource file has wrong version"),
- resource->name);
- return false;
- }
-
- // Other checks to be added as needed
- return true;
-}
-
-static void prt_dsc_start(void)
-{
- prt_write_string("%!PS-Adobe-3.0\n");
-}
-
-static void prt_dsc_noarg(char *comment)
-{
- vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer),
- "%%%%%s\n", comment);
- prt_write_file(prt_line_buffer);
-}
-
-static void prt_dsc_textline(char *comment, char *text)
-{
- vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer),
- "%%%%%s: %s\n", comment, text);
- prt_write_file(prt_line_buffer);
-}
-
-static void prt_dsc_text(char *comment, char *text)
-{
- // TODO(vim): - should scan 'text' for any chars needing escaping!
- vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer),
- "%%%%%s: (%s)\n", comment, text);
- prt_write_file(prt_line_buffer);
-}
-
-static void prt_dsc_ints(char *comment, int count, int *ints)
-{
- int i;
-
- vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer),
- "%%%%%s:", comment);
- prt_write_file(prt_line_buffer);
-
- for (i = 0; i < count; i++) {
- snprintf(prt_line_buffer, sizeof(prt_line_buffer), " %d", ints[i]);
- prt_write_file(prt_line_buffer);
- }
-
- prt_write_string("\n");
-}
-
-/// @param comment if NULL add to previous
-static void prt_dsc_resources(const char *comment, const char *type, const char *string)
- FUNC_ATTR_NONNULL_ARG(2, 3)
-{
- if (comment != NULL) {
- vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer),
- "%%%%%s: %s", comment, type);
- } else {
- vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer),
- "%%%%+ %s", type);
- }
- prt_write_file(prt_line_buffer);
-
- vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer),
- " %s\n", string);
- prt_write_file(prt_line_buffer);
-}
-
-static void prt_dsc_font_resource(char *resource, struct prt_ps_font_S *ps_font)
-{
- int i;
-
- prt_dsc_resources(resource, "font",
- ps_font->ps_fontname[PRT_PS_FONT_ROMAN]);
- for (i = PRT_PS_FONT_BOLD; i <= PRT_PS_FONT_BOLDOBLIQUE; i++) {
- if (ps_font->ps_fontname[i] != NULL) {
- prt_dsc_resources(NULL, "font", ps_font->ps_fontname[i]);
- }
- }
-}
-
-static void prt_dsc_requirements(int duplex, int tumble, int collate, int color, int num_copies)
-{
- // Only output the comment if we need to.
- // Note: tumble is ignored if we are not duplexing
- if (!(duplex || collate || color || (num_copies > 1))) {
- return;
- }
-
- snprintf(prt_line_buffer, sizeof(prt_line_buffer), "%%%%Requirements:");
- prt_write_file(prt_line_buffer);
-
- if (duplex) {
- prt_write_string(" duplex");
- if (tumble) {
- prt_write_string("(tumble)");
- }
- }
- if (collate) {
- prt_write_string(" collate");
- }
- if (color) {
- prt_write_string(" color");
- }
- if (num_copies > 1) {
- prt_write_string(" numcopies(");
- // Note: no space wanted so don't use prt_write_int()
- snprintf(prt_line_buffer, sizeof(prt_line_buffer), "%d",
- num_copies);
- prt_write_file(prt_line_buffer);
- prt_write_string(")");
- }
- prt_write_string("\n");
-}
-
-static void prt_dsc_docmedia(char *paper_name, double width, double height, double weight,
- char *colour, char *type)
-{
- vim_snprintf(prt_line_buffer, sizeof(prt_line_buffer),
- "%%%%DocumentMedia: %s ", paper_name);
- prt_write_file(prt_line_buffer);
- prt_write_real(width, 2);
- prt_write_real(height, 2);
- prt_write_real(weight, 2);
- if (colour == NULL) {
- prt_write_string("()");
- } else {
- prt_write_string(colour);
- }
- prt_write_string(" ");
- if (type == NULL) {
- prt_write_string("()");
- } else {
- prt_write_string(type);
- }
- prt_write_string("\n");
-}
-
-void mch_print_cleanup(void)
-{
- if (prt_out_mbyte) {
- int i;
-
- // Free off all CID font names created, but first clear duplicate
- // pointers to the same string (when the same font is used for more than
- // one style).
- for (i = PRT_PS_FONT_ROMAN; i <= PRT_PS_FONT_BOLDOBLIQUE; i++) {
- if (prt_ps_mb_font.ps_fontname[i] != NULL) {
- xfree(prt_ps_mb_font.ps_fontname[i]);
- }
- prt_ps_mb_font.ps_fontname[i] = NULL;
- }
- }
-
- if (prt_do_conv) {
- convert_setup(&prt_conv, NULL, NULL);
- prt_do_conv = FALSE;
- }
- if (prt_ps_fd != NULL) {
- fclose(prt_ps_fd);
- prt_ps_fd = NULL;
- prt_file_error = false;
- }
- if (prt_ps_file_name != NULL) {
- XFREE_CLEAR(prt_ps_file_name);
- }
-}
-
-static double to_device_units(int idx, double physsize, int def_number)
-{
- double ret;
- int nr;
-
- int u = prt_get_unit(idx);
- if (u == PRT_UNIT_NONE) {
- u = PRT_UNIT_PERC;
- nr = def_number;
- } else {
- nr = printer_opts[idx].number;
- }
-
- switch (u) {
- case PRT_UNIT_INCH:
- ret = nr * PRT_PS_DEFAULT_DPI;
- break;
- case PRT_UNIT_MM:
- ret = nr * PRT_PS_DEFAULT_DPI / 25.4;
- break;
- case PRT_UNIT_POINT:
- ret = nr;
- break;
- case PRT_UNIT_PERC:
- default:
- ret = physsize * nr / 100;
- break;
- }
-
- return ret;
-}
-
-/*
- * Calculate margins for given width and height from printoptions settings.
- */
-static void prt_page_margins(double width, double height, double *left, double *right, double *top,
- double *bottom)
-{
- *left = to_device_units(OPT_PRINT_LEFT, width, 10);
- *right = width - to_device_units(OPT_PRINT_RIGHT, width, 5);
- *top = height - to_device_units(OPT_PRINT_TOP, height, 5);
- *bottom = to_device_units(OPT_PRINT_BOT, height, 5);
-}
-
-static void prt_font_metrics(int font_scale)
-{
- prt_line_height = (double)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()) {
- prt_number_width = PRINT_NUMBER_WIDTH * prt_char_width;
- // If we are outputting multi-byte characters then line numbers will be
- // printed with half width characters
- if (prt_out_mbyte) {
- prt_number_width /= 2;
- }
- prt_left_margin += prt_number_width;
- } else {
- prt_number_width = 0.0;
- }
-
- return (int)((prt_right_margin - prt_left_margin) / prt_char_width);
-}
-
-static void prt_build_cid_fontname(int font, char_u *name, int name_len)
-{
- assert(name_len >= 0);
- char *fontname = xstrndup((char *)name, (size_t)name_len);
- prt_ps_mb_font.ps_fontname[font] = fontname;
-}
-
-/*
- * Get number of lines of text that fit on a page (excluding the header).
- */
-static int prt_get_lpp(void)
-{
- int lpp;
-
- /*
- * Calculate offset to lower left corner of background rect based on actual
- * font height (based on its bounding box) and the line height, handling the
- * case where the font height can exceed the line height.
- */
- prt_bgcol_offset = PRT_PS_FONT_TO_USER(prt_line_height,
- prt_ps_font->bbox_min_y);
- if ((prt_ps_font->bbox_max_y - prt_ps_font->bbox_min_y) < 1000.0) {
- prt_bgcol_offset -= PRT_PS_FONT_TO_USER(prt_line_height,
- (1000.0 - (prt_ps_font->bbox_max_y -
- prt_ps_font->bbox_min_y)) / 2);
- }
-
- // Get height for topmost line based on background rect offset.
- prt_first_line_height = prt_line_height + prt_bgcol_offset;
-
- // Calculate lpp
- lpp = (int)((prt_top_margin - prt_bottom_margin) / prt_line_height);
-
- // Adjust top margin if there is a header
- prt_top_margin -= prt_line_height * prt_header_height();
-
- return lpp - prt_header_height();
-}
-
-static int prt_match_encoding(char *p_encoding, struct prt_ps_mbfont_S *p_cmap,
- struct prt_ps_encoding_S **pp_mbenc)
-{
- int mbenc;
- int enc_len;
- struct prt_ps_encoding_S *p_mbenc;
-
- *pp_mbenc = NULL;
- // Look for recognised encoding
- enc_len = (int)STRLEN(p_encoding);
- p_mbenc = p_cmap->encodings;
- for (mbenc = 0; mbenc < p_cmap->num_encodings; mbenc++) {
- if (STRNICMP(p_mbenc->encoding, p_encoding, enc_len) == 0) {
- *pp_mbenc = p_mbenc;
- return TRUE;
- }
- p_mbenc++;
- }
- return FALSE;
-}
-
-static int prt_match_charset(char *p_charset, struct prt_ps_mbfont_S *p_cmap,
- struct prt_ps_charset_S **pp_mbchar)
-{
- int mbchar;
- int char_len;
- struct prt_ps_charset_S *p_mbchar;
-
- // Look for recognised character set, using default if one is not given
- if (*p_charset == NUL) {
- p_charset = p_cmap->defcs;
- }
- char_len = (int)STRLEN(p_charset);
- p_mbchar = p_cmap->charsets;
- for (mbchar = 0; mbchar < p_cmap->num_charsets; mbchar++) {
- if (STRNICMP(p_mbchar->charset, p_charset, char_len) == 0) {
- *pp_mbchar = p_mbchar;
- return TRUE;
- }
- p_mbchar++;
- }
- return FALSE;
-}
-
-int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
-{
- int i;
- char *paper_name;
- int paper_strlen;
- int fontsize;
- char_u *p;
- int props;
- int cmap = 0;
- char_u *p_encoding;
- struct prt_ps_encoding_S *p_mbenc;
- struct prt_ps_encoding_S *p_mbenc_first;
- struct prt_ps_charset_S *p_mbchar = NULL;
-
- /*
- * Set up font and encoding.
- */
- p_encoding = enc_skip(p_penc);
- if (*p_encoding == NUL) {
- p_encoding = enc_skip(p_enc);
- }
-
- // Look for a multi-byte font that matches the encoding and character set.
- // Only look if multi-byte character set is defined, or using multi-byte
- // encoding other than Unicode. This is because a Unicode encoding does not
- // uniquely identify a CJK character set to use.
- p_mbenc = NULL;
- props = enc_canon_props(p_encoding);
- if (!(props & ENC_8BIT) && ((*p_pmcs != NUL) || !(props & ENC_UNICODE))) {
- p_mbenc_first = NULL;
- int effective_cmap = 0;
- for (cmap = 0; cmap < (int)ARRAY_SIZE(prt_ps_mbfonts); cmap++) {
- if (prt_match_encoding((char *)p_encoding, &prt_ps_mbfonts[cmap],
- &p_mbenc)) {
- if (p_mbenc_first == NULL) {
- p_mbenc_first = p_mbenc;
- effective_cmap = cmap;
- }
- if (prt_match_charset((char *)p_pmcs, &prt_ps_mbfonts[cmap], &p_mbchar)) {
- break;
- }
- }
- }
-
- // Use first encoding matched if no charset matched
- if (p_mbenc_first != NULL && p_mbchar == NULL) {
- p_mbenc = p_mbenc_first;
- cmap = effective_cmap;
- }
-
- assert(p_mbenc == NULL || cmap < (int)ARRAY_SIZE(prt_ps_mbfonts));
- }
-
- prt_out_mbyte = (p_mbenc != NULL);
- if (prt_out_mbyte) {
- // Build CMap name - will be same for all multi-byte fonts used
- prt_cmap[0] = NUL;
-
- prt_custom_cmap = (p_mbchar == NULL);
- if (!prt_custom_cmap) {
- // Check encoding and character set are compatible
- if ((p_mbenc->needs_charset & p_mbchar->has_charset) == 0) {
- emsg(_("E673: Incompatible multi-byte encoding and character set."));
- return false;
- }
-
- // Add charset name if not empty
- if (p_mbchar->cmap_charset != NULL) {
- STRLCPY(prt_cmap, p_mbchar->cmap_charset, sizeof(prt_cmap) - 2);
- STRCAT(prt_cmap, "-");
- }
- } else {
- // Add custom CMap character set name
- if (*p_pmcs == NUL) {
- emsg(_("E674: printmbcharset cannot be empty with multi-byte encoding."));
- return false;
- }
- STRLCPY(prt_cmap, p_pmcs, sizeof(prt_cmap) - 2);
- STRCAT(prt_cmap, "-");
- }
-
- // CMap name ends with (optional) encoding name and -H for horizontal
- if (p_mbenc->cmap_encoding != NULL && STRLEN(prt_cmap)
- + STRLEN(p_mbenc->cmap_encoding) + 3 < sizeof(prt_cmap)) {
- STRCAT(prt_cmap, p_mbenc->cmap_encoding);
- STRCAT(prt_cmap, "-");
- }
- STRCAT(prt_cmap, "H");
-
- if (!mbfont_opts[OPT_MBFONT_REGULAR].present) {
- emsg(_("E675: No default font specified for multi-byte printing."));
- return false;
- }
-
- // Derive CID font names with fallbacks if not defined
- prt_build_cid_fontname(PRT_PS_FONT_ROMAN,
- mbfont_opts[OPT_MBFONT_REGULAR].string,
- mbfont_opts[OPT_MBFONT_REGULAR].strlen);
- if (mbfont_opts[OPT_MBFONT_BOLD].present) {
- prt_build_cid_fontname(PRT_PS_FONT_BOLD,
- mbfont_opts[OPT_MBFONT_BOLD].string,
- mbfont_opts[OPT_MBFONT_BOLD].strlen);
- }
- if (mbfont_opts[OPT_MBFONT_OBLIQUE].present) {
- prt_build_cid_fontname(PRT_PS_FONT_OBLIQUE,
- mbfont_opts[OPT_MBFONT_OBLIQUE].string,
- mbfont_opts[OPT_MBFONT_OBLIQUE].strlen);
- }
- if (mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].present) {
- prt_build_cid_fontname(PRT_PS_FONT_BOLDOBLIQUE,
- mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].string,
- mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].strlen);
- }
-
- // Check if need to use Courier for ASCII code range, and if so pick up
- // the encoding to use
- prt_use_courier = (
- mbfont_opts[OPT_MBFONT_USECOURIER].present
- && (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_USECOURIER].string[0]) == 'y'));
- if (prt_use_courier) {
- // Use national ASCII variant unless ASCII wanted
- if (mbfont_opts[OPT_MBFONT_ASCII].present
- && (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_ASCII].string[0]) == 'y')) {
- prt_ascii_encoding = "ascii";
- } else {
- prt_ascii_encoding = prt_ps_mbfonts[cmap].ascii_enc;
- }
- }
-
- prt_ps_font = &prt_ps_mb_font;
- } else {
- prt_use_courier = FALSE;
- prt_ps_font = &prt_ps_courier_font;
- }
-
- /*
- * Find the size of the paper and set the margins.
- */
- prt_portrait = (!printer_opts[OPT_PRINT_PORTRAIT].present
- || TOLOWER_ASC(printer_opts[OPT_PRINT_PORTRAIT].string[0]) ==
- 'y');
- if (printer_opts[OPT_PRINT_PAPER].present) {
- paper_name = (char *)printer_opts[OPT_PRINT_PAPER].string;
- paper_strlen = printer_opts[OPT_PRINT_PAPER].strlen;
- } else {
- paper_name = "A4";
- paper_strlen = 2;
- }
- for (i = 0; i < (int)PRT_MEDIASIZE_LEN; ++i) {
- if (STRLEN(prt_mediasize[i].name) == (unsigned)paper_strlen
- && STRNICMP(prt_mediasize[i].name, paper_name,
- paper_strlen) == 0) {
- break;
- }
- }
- if (i == PRT_MEDIASIZE_LEN) {
- i = 0;
- }
- prt_media = i;
-
- /*
- * Set PS pagesize based on media dimensions and print orientation.
- * Note: Media and page sizes have defined meanings in PostScript and should
- * be kept distinct. Media is the paper (or transparency, or ...) that is
- * printed on, whereas the page size is the area that the PostScript
- * interpreter renders into.
- */
- if (prt_portrait) {
- prt_page_width = prt_mediasize[i].width;
- prt_page_height = prt_mediasize[i].height;
- } else {
- prt_page_width = prt_mediasize[i].height;
- prt_page_height = prt_mediasize[i].width;
- }
-
- // Set PS page margins based on the PS pagesize, not the mediasize - this
- // needs to be done before the cpl and lpp are calculated.
- double left, right, top, bottom;
- prt_page_margins(prt_page_width, prt_page_height, &left, &right, &top,
- &bottom);
- prt_left_margin = left;
- prt_right_margin = right;
- prt_top_margin = top;
- prt_bottom_margin = bottom;
-
- /*
- * Set up the font size.
- */
- fontsize = PRT_PS_DEFAULT_FONTSIZE;
- 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);
- }
- }
- prt_font_metrics(fontsize);
-
- /*
- * Return the number of characters per line, and lines per page for the
- * generic print code.
- */
- psettings->chars_per_line = prt_get_cpl();
- psettings->lines_per_page = prt_get_lpp();
-
- // Catch margin settings that leave no space for output!
- if (psettings->chars_per_line <= 0 || psettings->lines_per_page <= 0) {
- return FAIL;
- }
-
- /*
- * Sort out the number of copies to be printed. PS by default will do
- * uncollated copies for you, so once we know how many uncollated copies are
- * wanted cache it away and lie to the generic code that we only want one
- * uncollated copy.
- */
- psettings->n_collated_copies = 1;
- psettings->n_uncollated_copies = 1;
- prt_num_copies = 1;
- prt_collate = (!printer_opts[OPT_PRINT_COLLATE].present
- || TOLOWER_ASC(printer_opts[OPT_PRINT_COLLATE].string[0]) ==
- 'y');
- if (prt_collate) {
- // TODO(vim): Get number of collated copies wanted.
- } else {
- // TODO(vim): Get number of uncollated copies wanted and update the cached
- // count.
- }
-
- psettings->jobname = jobname;
-
- /*
- * Set up printer duplex and tumble based on Duplex option setting - default
- * is long sided duplex printing (i.e. no tumble).
- */
- prt_duplex = TRUE;
- prt_tumble = FALSE;
- psettings->duplex = 1;
- if (printer_opts[OPT_PRINT_DUPLEX].present) {
- if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "off", 3) == 0) {
- prt_duplex = FALSE;
- psettings->duplex = 0;
- } else if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "short", 5)
- == 0) {
- prt_tumble = TRUE;
- }
- }
-
- // For now user abort not supported
- psettings->user_abort = 0;
-
- // If the user didn't specify a file name, use a temp file.
- if (psettings->outfile == NULL) {
- prt_ps_file_name = vim_tempname();
- if (prt_ps_file_name == NULL) {
- emsg(_(e_notmp));
- return FAIL;
- }
- prt_ps_fd = os_fopen((char *)prt_ps_file_name, WRITEBIN);
- } else {
- p = (char_u *)expand_env_save((char *)psettings->outfile);
- if (p != NULL) {
- prt_ps_fd = os_fopen((char *)p, WRITEBIN);
- xfree(p);
- }
- }
- if (prt_ps_fd == NULL) {
- emsg(_("E324: Can't open PostScript output file"));
- mch_print_cleanup();
- return FAIL;
- }
-
- prt_bufsiz = psettings->chars_per_line;
- if (prt_out_mbyte) {
- prt_bufsiz *= 2;
- }
- ga_init(&prt_ps_buffer, (int)sizeof(char), prt_bufsiz);
-
- prt_page_num = 0;
-
- prt_attribute_change = false;
- prt_need_moveto = false;
- prt_need_font = false;
- prt_need_fgcol = false;
- prt_need_bgcol = false;
- prt_need_underline = false;
-
- prt_file_error = false;
-
- return OK;
-}
-
-static bool prt_add_resource(struct prt_ps_resource_S *resource)
-{
- FILE *fd_resource;
- char_u resource_buffer[512];
- size_t bytes_read;
-
- fd_resource = os_fopen((char *)resource->filename, READBIN);
- if (fd_resource == NULL) {
- semsg(_("E456: Can't open file \"%s\""), resource->filename);
- return false;
- }
- switch (resource->type) {
- case PRT_RESOURCE_TYPE_PROCSET:
- case PRT_RESOURCE_TYPE_ENCODING:
- case PRT_RESOURCE_TYPE_CMAP:
- prt_dsc_resources("BeginResource", prt_resource_types[resource->type],
- (char *)resource->title);
- break;
- default:
- return false;
- }
-
- prt_dsc_textline("BeginDocument", (char *)resource->filename);
-
- for (;;) {
- bytes_read = fread((char *)resource_buffer, sizeof(char_u),
- sizeof(resource_buffer), fd_resource);
- if (ferror(fd_resource)) {
- semsg(_("E457: Can't read PostScript resource file \"%s\""),
- resource->filename);
- fclose(fd_resource);
- return false;
- }
- if (bytes_read == 0) {
- break;
- }
- prt_write_file_raw_len(resource_buffer, bytes_read);
- if (prt_file_error) {
- fclose(fd_resource);
- return false;
- }
- }
- fclose(fd_resource);
-
- prt_dsc_noarg("EndDocument");
-
- prt_dsc_noarg("EndResource");
-
- return true;
-}
-
-bool mch_print_begin(prt_settings_T *psettings)
-{
- int bbox[4];
- double left;
- double right;
- double top;
- double bottom;
- struct prt_ps_resource_S res_prolog;
- struct prt_ps_resource_S res_encoding;
- char buffer[256];
- char *p_encoding;
- char_u *p;
- struct prt_ps_resource_S res_cidfont;
- struct prt_ps_resource_S res_cmap;
-
- /*
- * PS DSC Header comments - no PS code!
- */
- prt_dsc_start();
- prt_dsc_textline("Title", (char *)psettings->jobname);
- if (os_get_username(buffer, 256) == FAIL) {
- STRCPY(buffer, "Unknown");
- }
- prt_dsc_textline("For", buffer);
- prt_dsc_textline("Creator", longVersion);
- // Note: to ensure Clean8bit I don't think we can use LC_TIME
- 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 = (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_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
- // coordinates based on the font metrics for the bbox to be accurate.
- prt_page_margins(prt_mediasize[prt_media].width,
- prt_mediasize[prt_media].height,
- &left, &right, &top, &bottom);
- bbox[0] = (int)left;
- if (prt_portrait) {
- // In portrait printing the fixed point is the top left corner so we
- // derive the bbox from that point. We have the expected cpl chars
- // across the media and lpp lines down the media.
- bbox[1] = (int)(top - (psettings->lines_per_page + prt_header_height())
- * prt_line_height);
- bbox[2] = (int)(left + psettings->chars_per_line * prt_char_width
- + 0.5);
- bbox[3] = (int)(top + 0.5);
- } else {
- // In landscape printing the fixed point is the bottom left corner so we
- // derive the bbox from that point. We have lpp chars across the media
- // and cpl lines up the media.
- bbox[1] = (int)bottom;
- bbox[2] = (int)(left + ((psettings->lines_per_page
- + prt_header_height()) * prt_line_height) + 0.5);
- bbox[3] = (int)(bottom + psettings->chars_per_line * prt_char_width
- + 0.5);
- }
- prt_dsc_ints("BoundingBox", 4, bbox);
- // The media width and height does not change with landscape printing!
- prt_dsc_docmedia(prt_mediasize[prt_media].name,
- prt_mediasize[prt_media].width,
- prt_mediasize[prt_media].height,
- (double)0, NULL, NULL);
- // Define fonts needed
- if (!prt_out_mbyte || prt_use_courier) {
- prt_dsc_font_resource("DocumentNeededResources", &prt_ps_courier_font);
- }
- if (prt_out_mbyte) {
- prt_dsc_font_resource((prt_use_courier ? NULL
- : "DocumentNeededResources"), &prt_ps_mb_font);
- if (!prt_custom_cmap) {
- prt_dsc_resources(NULL, "cmap", prt_cmap);
- }
- }
-
- // Search for external resources VIM supplies
- if (!prt_find_resource("prolog", &res_prolog)) {
- emsg(_("E456: Can't find PostScript resource file \"prolog.ps\""));
- return false;
- }
- if (!prt_open_resource(&res_prolog)) {
- return false;
- }
- if (!prt_check_resource(&res_prolog, PRT_PROLOG_VERSION)) {
- return false;
- }
- if (prt_out_mbyte) {
- // Look for required version of multi-byte printing procset
- if (!prt_find_resource("cidfont", &res_cidfont)) {
- emsg(_("E456: Can't find PostScript resource file \"cidfont.ps\""));
- return false;
- }
- if (!prt_open_resource(&res_cidfont)) {
- return false;
- }
- if (!prt_check_resource(&res_cidfont, PRT_CID_PROLOG_VERSION)) {
- return false;
- }
- }
-
- // Find an encoding to use for printing.
- // Check 'printencoding'. If not set or not found, then use 'encoding'. If
- // that cannot be found then default to "latin1".
- // Note: VIM specific encoding header is always skipped.
- if (!prt_out_mbyte) {
- p_encoding = (char *)enc_skip(p_penc);
- if (*p_encoding == NUL
- || !prt_find_resource(p_encoding, &res_encoding)) {
- // 'printencoding' not set or not supported - find alternate
- int props;
-
- p_encoding = (char *)enc_skip(p_enc);
- props = enc_canon_props((char_u *)p_encoding);
- if (!(props & ENC_8BIT)
- || !prt_find_resource(p_encoding, &res_encoding)) {
- // 8-bit 'encoding' is not supported
- // Use latin1 as default printing encoding
- p_encoding = "latin1";
- if (!prt_find_resource(p_encoding, &res_encoding)) {
- semsg(_("E456: Can't find PostScript resource file \"%s.ps\""),
- p_encoding);
- return false;
- }
- }
- }
- if (!prt_open_resource(&res_encoding)) {
- return false;
- }
- // For the moment there are no checks on encoding resource files to
- // perform
- } else {
- p_encoding = (char *)enc_skip(p_penc);
- if (*p_encoding == NUL) {
- p_encoding = (char *)enc_skip(p_enc);
- }
- if (prt_use_courier) {
- // Include ASCII range encoding vector
- if (!prt_find_resource(prt_ascii_encoding, &res_encoding)) {
- semsg(_("E456: Can't find PostScript resource file \"%s.ps\""),
- prt_ascii_encoding);
- return false;
- }
- if (!prt_open_resource(&res_encoding)) {
- return false;
- }
- // For the moment there are no checks on encoding resource files to
- // perform
- }
- }
-
- prt_conv.vc_type = CONV_NONE;
- if (!(enc_canon_props(p_enc) & enc_canon_props((char_u *)p_encoding) & ENC_8BIT)) {
- // Set up encoding conversion if required
- if (convert_setup(&prt_conv, p_enc, (char_u *)p_encoding) == FAIL) {
- semsg(_("E620: Unable to convert to print encoding \"%s\""),
- p_encoding);
- return false;
- }
- }
- prt_do_conv = prt_conv.vc_type != CONV_NONE;
-
- if (prt_out_mbyte && prt_custom_cmap) {
- // Find user supplied CMap
- if (!prt_find_resource(prt_cmap, &res_cmap)) {
- semsg(_("E456: Can't find PostScript resource file \"%s.ps\""),
- prt_cmap);
- return false;
- }
- if (!prt_open_resource(&res_cmap)) {
- return false;
- }
- }
-
- // List resources supplied
- STRCPY(buffer, res_prolog.title);
- STRCAT(buffer, " ");
- STRCAT(buffer, res_prolog.version);
- prt_dsc_resources("DocumentSuppliedResources", "procset", buffer);
- if (prt_out_mbyte) {
- STRCPY(buffer, res_cidfont.title);
- STRCAT(buffer, " ");
- STRCAT(buffer, res_cidfont.version);
- prt_dsc_resources(NULL, "procset", buffer);
-
- if (prt_custom_cmap) {
- STRCPY(buffer, res_cmap.title);
- STRCAT(buffer, " ");
- STRCAT(buffer, res_cmap.version);
- prt_dsc_resources(NULL, "cmap", buffer);
- }
- }
- if (!prt_out_mbyte || prt_use_courier) {
- STRCPY(buffer, res_encoding.title);
- STRCAT(buffer, " ");
- STRCAT(buffer, res_encoding.version);
- prt_dsc_resources(NULL, "encoding", buffer);
- }
- prt_dsc_requirements(prt_duplex, prt_tumble, prt_collate,
- psettings->do_syntax,
- prt_num_copies);
- prt_dsc_noarg("EndComments");
-
- /*
- * PS Document page defaults
- */
- prt_dsc_noarg("BeginDefaults");
-
- // List font resources most likely common to all pages
- if (!prt_out_mbyte || prt_use_courier) {
- prt_dsc_font_resource("PageResources", &prt_ps_courier_font);
- }
- if (prt_out_mbyte) {
- prt_dsc_font_resource((prt_use_courier ? NULL : "PageResources"),
- &prt_ps_mb_font);
- if (!prt_custom_cmap) {
- prt_dsc_resources(NULL, "cmap", prt_cmap);
- }
- }
-
- // Paper will be used for all pages
- prt_dsc_textline("PageMedia", prt_mediasize[prt_media].name);
-
- prt_dsc_noarg("EndDefaults");
-
- /*
- * PS Document prolog inclusion - all required procsets.
- */
- prt_dsc_noarg("BeginProlog");
-
- // Add required procsets - NOTE: order is important!
- if (!prt_add_resource(&res_prolog)) {
- return false;
- }
- if (prt_out_mbyte) {
- // Add CID font procset, and any user supplied CMap
- if (!prt_add_resource(&res_cidfont)) {
- return false;
- }
- if (prt_custom_cmap && !prt_add_resource(&res_cmap)) {
- return false;
- }
- }
-
- if (!prt_out_mbyte || prt_use_courier) {
- // There will be only one Roman font encoding to be included in the PS
- // file.
- if (!prt_add_resource(&res_encoding)) {
- return false;
- }
- }
-
- prt_dsc_noarg("EndProlog");
-
- /*
- * PS Document setup - must appear after the prolog
- */
- prt_dsc_noarg("BeginSetup");
-
- // Device setup - page size and number of uncollated copies
- prt_write_int((int)prt_mediasize[prt_media].width);
- prt_write_int((int)prt_mediasize[prt_media].height);
- prt_write_int(0);
- prt_write_string("sps\n");
- prt_write_int(prt_num_copies);
- prt_write_string("nc\n");
- prt_write_boolean(prt_duplex);
- prt_write_boolean(prt_tumble);
- prt_write_string("dt\n");
- prt_write_boolean(prt_collate);
- prt_write_string("c\n");
-
- // Font resource inclusion and definition
- if (!prt_out_mbyte || prt_use_courier) {
- // When using Courier for ASCII range when printing multi-byte, need to
- // pick up ASCII encoding to use with it.
- if (prt_use_courier) {
- p_encoding = prt_ascii_encoding;
- }
- prt_dsc_resources("IncludeResource", "font",
- prt_ps_courier_font.ps_fontname[PRT_PS_FONT_ROMAN]);
- prt_def_font("F0", p_encoding, (int)prt_line_height,
- prt_ps_courier_font.ps_fontname[PRT_PS_FONT_ROMAN]);
- prt_dsc_resources("IncludeResource", "font",
- prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLD]);
- prt_def_font("F1", p_encoding, (int)prt_line_height,
- prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLD]);
- prt_dsc_resources("IncludeResource", "font",
- prt_ps_courier_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
- prt_def_font("F2", p_encoding, (int)prt_line_height,
- prt_ps_courier_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
- prt_dsc_resources("IncludeResource", "font",
- prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
- prt_def_font("F3", p_encoding, (int)prt_line_height,
- prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
- }
- if (prt_out_mbyte) {
- // Define the CID fonts to be used in the job. Typically CJKV fonts do
- // not have an italic form being a western style, so where no font is
- // defined for these faces VIM falls back to an existing face.
- // Note: if using Courier for the ASCII range then the printout will
- // have bold/italic/bolditalic regardless of the setting of printmbfont.
- prt_dsc_resources("IncludeResource", "font",
- prt_ps_mb_font.ps_fontname[PRT_PS_FONT_ROMAN]);
- if (!prt_custom_cmap) {
- prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
- }
- prt_def_cidfont("CF0", (int)prt_line_height,
- prt_ps_mb_font.ps_fontname[PRT_PS_FONT_ROMAN]);
-
- if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD] != NULL) {
- prt_dsc_resources("IncludeResource", "font",
- prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD]);
- if (!prt_custom_cmap) {
- prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
- }
- prt_def_cidfont("CF1", (int)prt_line_height,
- prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD]);
- } else {
- // Use ROMAN for BOLD
- prt_dup_cidfont("CF0", "CF1");
- }
- if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE] != NULL) {
- prt_dsc_resources("IncludeResource", "font",
- prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
- if (!prt_custom_cmap) {
- prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
- }
- prt_def_cidfont("CF2", (int)prt_line_height,
- prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
- } else {
- // Use ROMAN for OBLIQUE
- prt_dup_cidfont("CF0", "CF2");
- }
- if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE] != NULL) {
- prt_dsc_resources("IncludeResource", "font",
- prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
- if (!prt_custom_cmap) {
- prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
- }
- prt_def_cidfont("CF3", (int)prt_line_height,
- prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
- } else {
- // Use BOLD for BOLDOBLIQUE
- prt_dup_cidfont("CF1", "CF3");
- }
- }
-
- // Misc constant vars used for underlining and background rects
- prt_def_var("UO", PRT_PS_FONT_TO_USER(prt_line_height,
- prt_ps_font->uline_offset), 2);
- prt_def_var("UW", PRT_PS_FONT_TO_USER(prt_line_height,
- prt_ps_font->uline_width), 2);
- prt_def_var("BO", prt_bgcol_offset, 2);
-
- prt_dsc_noarg("EndSetup");
-
- // Fail if any problems writing out to the PS file
- return !prt_file_error;
-}
-
-void mch_print_end(prt_settings_T *psettings)
-{
- prt_dsc_noarg("Trailer");
-
- /*
- * Output any info we don't know in toto until we finish
- */
- prt_dsc_ints("Pages", 1, &prt_page_num);
-
- prt_dsc_noarg("EOF");
-
- // Write CTRL-D to close serial communication link if used.
- // NOTHING MUST BE WRITTEN AFTER THIS!
- prt_write_file("\004");
-
- if (!prt_file_error && psettings->outfile == NULL
- && !got_int && !psettings->user_abort) {
- // Close the file first.
- if (prt_ps_fd != NULL) {
- fclose(prt_ps_fd);
- prt_ps_fd = NULL;
- }
- prt_message((char_u *)_("Sending to printer..."));
-
- // Not printing to a file: use 'printexpr' to print the file.
- if (eval_printexpr((char *)prt_ps_file_name, (char *)psettings->arguments)
- == FAIL) {
- emsg(_("E365: Failed to print PostScript file"));
- } else {
- prt_message((char_u *)_("Print job sent."));
- }
- }
-
- mch_print_cleanup();
-}
-
-int mch_print_end_page(void)
-{
- prt_flush_buffer();
-
- prt_write_string("re sp\n");
-
- prt_dsc_noarg("PageTrailer");
-
- return !prt_file_error;
-}
-
-int mch_print_begin_page(char_u *str)
-{
- int page_num[2];
-
- prt_page_num++;
-
- page_num[0] = page_num[1] = prt_page_num;
- prt_dsc_ints("Page", 2, page_num);
-
- prt_dsc_noarg("BeginPageSetup");
-
- prt_write_string("sv\n0 g\n");
- prt_in_ascii = !prt_out_mbyte;
- if (prt_out_mbyte) {
- prt_write_string("CF0 sf\n");
- } else {
- prt_write_string("F0 sf\n");
- }
- prt_fgcol = PRCOLOR_BLACK;
- prt_bgcol = PRCOLOR_WHITE;
- prt_font = PRT_PS_FONT_ROMAN;
-
- // Set up page transformation for landscape printing.
- if (!prt_portrait) {
- prt_write_int(-((int)prt_mediasize[prt_media].width));
- prt_write_string("sl\n");
- }
-
- prt_dsc_noarg("EndPageSetup");
-
- // We have reset the font attributes, force setting them again.
- curr_bg = 0xffffffff;
- curr_fg = 0xffffffff;
- curr_bold = kNone;
-
- return !prt_file_error;
-}
-
-int mch_print_blank_page(void)
-{
- return mch_print_begin_page(NULL) ? (mch_print_end_page()) : FALSE;
-}
-
-static double prt_pos_x = 0;
-static double prt_pos_y = 0;
-
-void mch_print_start_line(const bool margin, const int page_line)
-{
- prt_pos_x = prt_left_margin;
- if (margin) {
- prt_pos_x -= prt_number_width;
- }
-
- prt_pos_y = prt_top_margin - prt_first_line_height -
- page_line * prt_line_height;
-
- prt_attribute_change = true;
- prt_need_moveto = true;
- prt_half_width = false;
-}
-
-int mch_print_text_out(char_u *const textp, size_t len)
-{
- char_u *p = textp;
- char_u ch;
- char_u ch_buff[8];
- char_u *tofree = NULL;
- double char_width = prt_char_width;
-
- // Ideally VIM would create a rearranged CID font to combine a Roman and
- // CJKV font to do what VIM is doing here - use a Roman font for characters
- // in the ASCII range, and the original CID font for everything else.
- // The problem is that GhostScript still (as of 8.13) does not support
- // rearranged fonts even though they have been documented by Adobe for 7
- // years! If they ever do, a lot of this code will disappear.
- if (prt_use_courier) {
- const bool in_ascii = (len == 1 && *p < 0x80);
- if (prt_in_ascii) {
- if (!in_ascii) {
- // No longer in ASCII range - need to switch font
- prt_in_ascii = false;
- prt_need_font = true;
- prt_attribute_change = true;
- }
- } else if (in_ascii) {
- // Now in ASCII range - need to switch font
- prt_in_ascii = true;
- prt_need_font = true;
- prt_attribute_change = true;
- }
- }
- if (prt_out_mbyte) {
- const bool half_width = (utf_ptr2cells((char *)p) == 1);
- if (half_width) {
- char_width /= 2;
- }
- if (prt_half_width) {
- if (!half_width) {
- prt_half_width = false;
- prt_pos_x += prt_char_width/4;
- prt_need_moveto = true;
- prt_attribute_change = true;
- }
- } else if (half_width) {
- prt_half_width = true;
- prt_pos_x += prt_char_width/4;
- prt_need_moveto = true;
- prt_attribute_change = true;
- }
- }
-
- // Output any required changes to the graphics state, after flushing any
- // text buffered so far.
- if (prt_attribute_change) {
- prt_flush_buffer();
- // Reset count of number of chars that will be printed
- prt_text_run = 0;
-
- if (prt_need_moveto) {
- prt_pos_x_moveto = prt_pos_x;
- prt_pos_y_moveto = prt_pos_y;
- prt_do_moveto = true;
-
- prt_need_moveto = false;
- }
- if (prt_need_font) {
- if (!prt_in_ascii) {
- prt_write_string("CF");
- } else {
- prt_write_string("F");
- }
- prt_write_int(prt_font);
- prt_write_string("sf\n");
- prt_need_font = false;
- }
- if (prt_need_fgcol) {
- unsigned int r, g, b;
- r = (prt_fgcol & 0xff0000) >> 16;
- g = (prt_fgcol & 0xff00) >> 8;
- b = prt_fgcol & 0xff;
-
- prt_write_real(r / 255.0, 3);
- if (r == g && g == b) {
- prt_write_string("g\n");
- } else {
- prt_write_real(g / 255.0, 3);
- prt_write_real(b / 255.0, 3);
- prt_write_string("r\n");
- }
- prt_need_fgcol = false;
- }
-
- if (prt_bgcol != PRCOLOR_WHITE) {
- prt_new_bgcol = prt_bgcol;
- if (prt_need_bgcol) {
- prt_do_bgcol = true;
- }
- } else {
- prt_do_bgcol = false;
- }
- prt_need_bgcol = false;
-
- if (prt_need_underline) {
- prt_do_underline = prt_underline;
- }
- prt_need_underline = false;
-
- prt_attribute_change = false;
- }
-
- if (prt_do_conv) {
- // Convert from multi-byte to 8-bit encoding
- tofree = p = string_convert(&prt_conv, p, &len);
- if (p == NULL) {
- p = (char_u *)"";
- len = 0;
- }
- }
-
- if (prt_out_mbyte) {
- // Multi-byte character strings are represented more efficiently as hex
- // strings when outputting clean 8 bit PS.
- while (len-- > 0) {
- ch = prt_hexchar[(unsigned)(*p) >> 4];
- ga_append(&prt_ps_buffer, (char)ch);
- ch = prt_hexchar[(*p) & 0xf];
- ga_append(&prt_ps_buffer, (char)ch);
- p++;
- }
- } else {
- // Add next character to buffer of characters to output.
- // Note: One printed character may require several PS characters to
- // represent it, but we only count them as one printed character.
- ch = *p;
- if (ch < 32 || ch == '(' || ch == ')' || ch == '\\') {
- // Convert non-printing characters to either their escape or octal
- // sequence, ensures PS sent over a serial line does not interfere
- // with the comms protocol.
- ga_append(&prt_ps_buffer, '\\');
- switch (ch) {
- case BS:
- ga_append(&prt_ps_buffer, 'b'); break;
- case TAB:
- ga_append(&prt_ps_buffer, 't'); break;
- case NL:
- ga_append(&prt_ps_buffer, 'n'); break;
- case FF:
- ga_append(&prt_ps_buffer, 'f'); break;
- case CAR:
- ga_append(&prt_ps_buffer, 'r'); break;
- case '(':
- ga_append(&prt_ps_buffer, '('); break;
- case ')':
- ga_append(&prt_ps_buffer, ')'); break;
- case '\\':
- ga_append(&prt_ps_buffer, '\\'); break;
-
- default:
- sprintf((char *)ch_buff, "%03o", (unsigned int)ch);
- ga_append(&prt_ps_buffer, (char)ch_buff[0]);
- ga_append(&prt_ps_buffer, (char)ch_buff[1]);
- ga_append(&prt_ps_buffer, (char)ch_buff[2]);
- break;
- }
- } else {
- ga_append(&prt_ps_buffer, (char)ch);
- }
- }
-
- // Need to free any translated characters
- xfree(tofree);
-
- prt_text_run += char_width;
- prt_pos_x += char_width;
-
- // The downside of fp - use relative error on right margin check
- const double next_pos = prt_pos_x + prt_char_width;
- const bool need_break = (next_pos > prt_right_margin)
- && ((next_pos - prt_right_margin) > (prt_right_margin * 1e-5));
-
- if (need_break) {
- prt_flush_buffer();
- }
-
- return need_break;
-}
-
-void mch_print_set_font(const TriState iBold, const TriState iItalic, const TriState iUnderline)
-{
- int font = 0;
-
- if (iBold) {
- font |= 0x01;
- }
- if (iItalic) {
- font |= 0x02;
- }
-
- if (font != prt_font) {
- prt_font = font;
- prt_attribute_change = true;
- prt_need_font = true;
- }
- if (prt_underline != iUnderline) {
- prt_underline = iUnderline;
- prt_attribute_change = true;
- prt_need_underline = true;
- }
-}
-
-void mch_print_set_bg(uint32_t bgcol)
-{
- prt_bgcol = bgcol;
- prt_attribute_change = true;
- prt_need_bgcol = true;
-}
-
-void mch_print_set_fg(uint32_t fgcol)
-{
- if (fgcol != prt_fgcol) {
- prt_fgcol = fgcol;
- prt_attribute_change = true;
- prt_need_fgcol = true;
- }
-}
diff --git a/src/nvim/hardcopy.h b/src/nvim/hardcopy.h
deleted file mode 100644
index 9ef4eb0074..0000000000
--- a/src/nvim/hardcopy.h
+++ /dev/null
@@ -1,86 +0,0 @@
-#ifndef NVIM_HARDCOPY_H
-#define NVIM_HARDCOPY_H
-
-#include <stdint.h>
-#include <stdlib.h> // for size_t
-
-#include "nvim/ex_cmds_defs.h" // for exarg_T
-#include "nvim/globals.h" // for TriState
-#include "nvim/types.h" // for char_u
-
-/*
- * Structure to hold printing color and font attributes.
- */
-typedef struct {
- uint32_t fg_color;
- uint32_t bg_color;
- TriState bold;
- TriState italic;
- TriState underline;
- int undercurl;
- int underdouble;
- int underdotted;
- int underdashed;
-} prt_text_attr_T;
-
-/*
- * Structure passed back to the generic printer code.
- */
-typedef struct {
- int n_collated_copies;
- int n_uncollated_copies;
- int duplex;
- int chars_per_line;
- int lines_per_page;
- int has_color;
- prt_text_attr_T number;
- int modec;
- int do_syntax;
- int user_abort;
- char_u *jobname;
- char_u *outfile;
- char_u *arguments;
-} prt_settings_T;
-
-/*
- * Generic option table item, only used for printer at the moment.
- */
-typedef struct {
- const char *name;
- int hasnum;
- int number;
- char_u *string; // points into option string
- int strlen;
- int present;
-} option_table_T;
-
-#define OPT_PRINT_TOP 0
-#define OPT_PRINT_BOT 1
-#define OPT_PRINT_LEFT 2
-#define OPT_PRINT_RIGHT 3
-#define OPT_PRINT_HEADERHEIGHT 4
-#define OPT_PRINT_SYNTAX 5
-#define OPT_PRINT_NUMBER 6
-#define OPT_PRINT_WRAP 7
-#define OPT_PRINT_DUPLEX 8
-#define OPT_PRINT_PORTRAIT 9
-#define OPT_PRINT_PAPER 10
-#define OPT_PRINT_COLLATE 11
-#define OPT_PRINT_JOBSPLIT 12
-#define OPT_PRINT_FORMFEED 13
-#define OPT_PRINT_NUM_OPTIONS 14
-
-// For prt_get_unit().
-#define PRT_UNIT_NONE -1
-#define PRT_UNIT_PERC 0
-#define PRT_UNIT_INCH 1
-#define PRT_UNIT_MM 2
-#define PRT_UNIT_POINT 3
-#define PRT_UNIT_NAMES { "pc", "in", "mm", "pt" }
-
-#define PRINT_NUMBER_WIDTH 8
-
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "hardcopy.h.generated.h"
-#endif
-#endif // NVIM_HARDCOPY_H
diff --git a/src/nvim/hashtab.c b/src/nvim/hashtab.c
index 951e72ea52..851e70caca 100644
--- a/src/nvim/hashtab.c
+++ b/src/nvim/hashtab.c
@@ -30,6 +30,7 @@
#include "nvim/hashtab.h"
#include "nvim/memory.h"
#include "nvim/message.h"
+#include "nvim/types.h"
#include "nvim/vim.h"
// Magic value for algorithm that walks through the array.
@@ -67,7 +68,7 @@ void hash_clear(hashtab_T *ht)
void hash_clear_all(hashtab_T *ht, unsigned int off)
{
size_t todo = ht->ht_used;
- for (hashitem_T *hi = ht->ht_array; todo > 0; ++hi) {
+ for (hashitem_T *hi = ht->ht_array; todo > 0; hi++) {
if (!HASHITEM_EMPTY(hi)) {
xfree(hi->hi_key - off);
todo--;
@@ -87,7 +88,7 @@ void hash_clear_all(hashtab_T *ht, unsigned int off)
/// is changed in any way.
hashitem_T *hash_find(const hashtab_T *const ht, const char *const key)
{
- return hash_lookup(ht, key, STRLEN(key), hash_hash((char_u *)key));
+ return hash_lookup(ht, key, strlen(key), hash_hash(key));
}
/// Like hash_find, but key is not NUL-terminated
@@ -140,7 +141,7 @@ hashitem_T *hash_lookup(const hashtab_T *const ht, const char *const key, const
if (hi->hi_key == HI_KEY_REMOVED) {
freeitem = hi;
} else if ((hi->hi_hash == hash)
- && (STRNCMP(hi->hi_key, key, key_len) == 0)
+ && (strncmp(hi->hi_key, key, key_len) == 0)
&& hi->hi_key[key_len] == NUL) {
return hi;
}
@@ -166,7 +167,7 @@ hashitem_T *hash_lookup(const hashtab_T *const ht, const char *const key, const
if ((hi->hi_hash == hash)
&& (hi->hi_key != HI_KEY_REMOVED)
- && (STRNCMP(hi->hi_key, key, key_len) == 0)
+ && (strncmp(hi->hi_key, key, key_len) == 0)
&& hi->hi_key[key_len] == NUL) {
return hi;
}
@@ -194,17 +195,17 @@ void hash_debug_results(void)
#endif // ifdef HT_DEBUG
}
-/// Add item for key "key" to hashtable "ht".
+/// Add (empty) item for key `key` to hashtable `ht`.
///
/// @param key Pointer to the key for the new item. The key has to be contained
/// in the new item (@see hashitem_T). Must not be NULL.
///
/// @return OK if success.
/// FAIL if key already present
-int hash_add(hashtab_T *ht, char_u *key)
+int hash_add(hashtab_T *ht, char *key)
{
hash_T hash = hash_hash(key);
- hashitem_T *hi = hash_lookup(ht, (const char *)key, STRLEN(key), hash);
+ hashitem_T *hi = hash_lookup(ht, key, strlen(key), hash);
if (!HASHITEM_EMPTY(hi)) {
internal_error("hash_add()");
return FAIL;
@@ -220,9 +221,10 @@ int hash_add(hashtab_T *ht, char_u *key)
/// @param key Pointer to the key for the new item. The key has to be contained
/// in the new item (@see hashitem_T). Must not be NULL.
/// @param hash The precomputed hash value for the key.
-void hash_add_item(hashtab_T *ht, hashitem_T *hi, char_u *key, hash_T hash)
+void hash_add_item(hashtab_T *ht, hashitem_T *hi, char *key, hash_T hash)
{
ht->ht_used++;
+ ht->ht_changed++;
if (hi->hi_key == NULL) {
ht->ht_filled++;
}
@@ -242,6 +244,7 @@ void hash_add_item(hashtab_T *ht, hashitem_T *hi, char_u *key, hash_T hash)
void hash_remove(hashtab_T *ht, hashitem_T *hi)
{
ht->ht_used--;
+ ht->ht_changed++;
hi->hi_key = HI_KEY_REMOVED;
hash_may_resize(ht, 0);
}
@@ -265,7 +268,7 @@ void hash_unlock(hashtab_T *ht)
hash_may_resize(ht, 0);
}
-/// Resize hastable (new size can be given or automatically computed).
+/// Resize hashtable (new size can be given or automatically computed).
///
/// @param minitems Minimum number of items the new table should hold.
/// If zero, new size will depend on currently used items:
@@ -290,6 +293,7 @@ static void hash_may_resize(hashtab_T *ht, size_t minitems)
#endif // ifdef HT_DEBUG
size_t minsize;
+ const size_t oldsize = ht->ht_mask + 1;
if (minitems == 0) {
// Return quickly for small tables with at least two NULL items.
// items are required for the lookup to decide a key isn't there.
@@ -302,7 +306,6 @@ static void hash_may_resize(hashtab_T *ht, size_t minitems)
// removed items, so that they get cleaned up).
// Shrink the array when it's less than 1/5 full. When growing it is
// at least 1/4 full (avoids repeated grow-shrink operations)
- size_t oldsize = ht->ht_mask + 1;
if ((ht->ht_filled * 3 < oldsize * 2) && (ht->ht_used > oldsize / 5)) {
return;
}
@@ -333,6 +336,13 @@ static void hash_may_resize(hashtab_T *ht, size_t minitems)
}
bool newarray_is_small = newsize == HT_INIT_SIZE;
+
+ if (!newarray_is_small && newsize == oldsize && ht->ht_filled * 3 < oldsize * 2) {
+ // The hashtab is already at the desired size, and there are not too
+ // many removed items, bail out.
+ return;
+ }
+
bool keep_smallarray = newarray_is_small
&& ht->ht_array == ht->ht_smallarray;
@@ -356,7 +366,7 @@ static void hash_may_resize(hashtab_T *ht, size_t minitems)
hash_T newmask = newsize - 1;
size_t todo = ht->ht_used;
- for (hashitem_T *olditem = oldarray; todo > 0; ++olditem) {
+ for (hashitem_T *olditem = oldarray; todo > 0; olditem++) {
if (HASHITEM_EMPTY(olditem)) {
continue;
}
@@ -384,6 +394,7 @@ static void hash_may_resize(hashtab_T *ht, size_t minitems)
ht->ht_array = newarray;
ht->ht_mask = newmask;
ht->ht_filled = ht->ht_used;
+ ht->ht_changed++;
}
#define HASH_CYCLE_BODY(hash, p) \
@@ -395,9 +406,9 @@ static void hash_may_resize(hashtab_T *ht, size_t minitems)
/// run a script that uses hashtables a lot. Vim will then print statistics
/// when exiting. Try that with the current hash algorithm and yours. The
/// lower the percentage the better.
-hash_T hash_hash(const char_u *key)
+hash_T hash_hash(const char *key)
{
- hash_T hash = *key;
+ hash_T hash = (uint8_t)(*key);
if (hash == 0) {
return (hash_T)0;
@@ -405,7 +416,7 @@ hash_T hash_hash(const char_u *key)
// A simplistic algorithm that appears to do very well.
// Suggested by George Reilly.
- const uint8_t *p = key + 1;
+ const uint8_t *p = (uint8_t *)key + 1;
while (*p != NUL) {
HASH_CYCLE_BODY(hash, p);
}
@@ -449,5 +460,5 @@ hash_T hash_hash_len(const char *key, const size_t len)
const char_u *_hash_key_removed(void)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- return HI_KEY_REMOVED;
+ return (char_u *)HI_KEY_REMOVED;
}
diff --git a/src/nvim/hashtab.h b/src/nvim/hashtab.h
index 7740a3e431..0a50fb2ef8 100644
--- a/src/nvim/hashtab.h
+++ b/src/nvim/hashtab.h
@@ -15,9 +15,9 @@ typedef size_t hash_T;
/// The address of "hash_removed" is used as a magic number
/// for hi_key to indicate a removed item.
-#define HI_KEY_REMOVED ((char_u *)&hash_removed)
+#define HI_KEY_REMOVED (&hash_removed)
#define HASHITEM_EMPTY(hi) ((hi)->hi_key == NULL \
- || (hi)->hi_key == (char_u *)&hash_removed)
+ || (hi)->hi_key == &hash_removed)
/// Hashtable item.
///
@@ -45,7 +45,7 @@ typedef struct hashitem_S {
/// NULL : Item was never used.
/// HI_KEY_REMOVED : Item was removed.
/// (Any other pointer value) : Item is currently being used.
- char_u *hi_key;
+ char *hi_key;
} hashitem_T;
/// Initial size for a hashtable.
@@ -61,14 +61,15 @@ typedef struct hashitem_S {
///
/// The hashtable grows to accommodate more entries when needed.
typedef struct hashtable_S {
- hash_T ht_mask; /// mask used for hash value
- /// (nr of items in array is "ht_mask" + 1)
- size_t ht_used; /// number of items used
- size_t ht_filled; /// number of items used or removed
- int ht_locked; /// counter for hash_lock()
- hashitem_T *ht_array; /// points to the array, allocated when it's
- /// not "ht_smallarray"
- hashitem_T ht_smallarray[HT_INIT_SIZE]; /// initial array
+ hash_T ht_mask; ///< mask used for hash value
+ ///< (nr of items in array is "ht_mask" + 1)
+ size_t ht_used; ///< number of items used
+ size_t ht_filled; ///< number of items used or removed
+ int ht_changed; ///< incremented when adding or removing an item
+ int ht_locked; ///< counter for hash_lock()
+ hashitem_T *ht_array; ///< points to the array, allocated when it's
+ ///< not "ht_smallarray"
+ hashitem_T ht_smallarray[HT_INIT_SIZE]; ///< initial array
} hashtab_T;
/// Iterate over a hashtab
diff --git a/src/nvim/help.c b/src/nvim/help.c
index 442f2e0b7b..bbc552fa4c 100644
--- a/src/nvim/help.c
+++ b/src/nvim/help.c
@@ -3,26 +3,39 @@
// help.c: functions for Vim help
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
+#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
#include "nvim/ex_cmds.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/fileio.h"
#include "nvim/garray.h"
+#include "nvim/gettext.h"
#include "nvim/globals.h"
#include "nvim/help.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
+#include "nvim/memline.h"
#include "nvim/memory.h"
+#include "nvim/message.h"
#include "nvim/option.h"
#include "nvim/optionstr.h"
#include "nvim/os/input.h"
+#include "nvim/os/os.h"
#include "nvim/path.h"
+#include "nvim/pos.h"
+#include "nvim/runtime.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/tag.h"
+#include "nvim/types.h"
#include "nvim/vim.h"
#include "nvim/window.h"
@@ -75,7 +88,7 @@ void ex_help(exarg_T *eap)
}
// remove trailing blanks
- p = arg + STRLEN(arg) - 1;
+ p = arg + strlen(arg) - 1;
while (p > arg && ascii_iswhite(*p) && p[-1] != '\\') {
*p-- = NUL;
}
@@ -95,7 +108,7 @@ void ex_help(exarg_T *eap)
if (n != FAIL && lang != NULL) {
// Find first item with the requested language.
for (i = 0; i < num_matches; i++) {
- len = (int)STRLEN(matches[i]);
+ len = (int)strlen(matches[i]);
if (len > 3 && matches[i][len - 3] == '@'
&& STRICMP(matches[i] + len - 2, lang) == 0) {
break;
@@ -137,7 +150,7 @@ void ex_help(exarg_T *eap)
} else {
// There is no help window yet.
// Try to open the file specified by the "helpfile" option.
- if ((helpfd = os_fopen((char *)p_hf, READBIN)) == NULL) {
+ if ((helpfd = os_fopen(p_hf, READBIN)) == NULL) {
smsg(_("Sorry, help file \"%s\" not found"), p_hf);
goto erret;
}
@@ -149,7 +162,7 @@ void ex_help(exarg_T *eap)
n = WSP_HELP;
if (cmdmod.cmod_split == 0 && curwin->w_width != Columns
&& curwin->w_width < 80) {
- n |= WSP_TOP;
+ n |= p_sb ? WSP_BOT : WSP_TOP;
}
if (win_split(0, n) == FAIL) {
goto erret;
@@ -180,7 +193,7 @@ void ex_help(exarg_T *eap)
// It is needed for do_tag top open folds under the cursor.
KeyTyped = old_KeyTyped;
- do_tag((char_u *)tag, DT_HELP, 1, false, true);
+ do_tag(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
@@ -219,7 +232,7 @@ void ex_helpclose(exarg_T *eap)
/// @return NULL if not found.
char *check_help_lang(char *arg)
{
- int len = (int)STRLEN(arg);
+ int len = (int)strlen(arg);
if (len >= 3 && arg[len - 3] == '@' && ASCII_ISALPHA(arg[len - 2])
&& ASCII_ISALPHA(arg[len - 1])) {
@@ -278,7 +291,7 @@ int help_heuristic(char *matched_string, int offset, int wrong_case)
if (matched_string[0] == '+' && matched_string[1] != NUL) {
offset += 100;
}
- return 100 * num_letters + (int)STRLEN(matched_string) + offset;
+ return 100 * num_letters + (int)strlen(matched_string) + offset;
}
/// Compare functions for qsort() below, that checks the help heuristics number
@@ -359,7 +372,7 @@ int find_help_tags(const char *arg, int *num_matches, char ***matches, bool keep
"!=?", "!~?", "<=?", "<?", "==?", "=~?",
">=?", ">?", "is?", "isnot?"
};
- char *d = (char *)IObuff; // assume IObuff is long enough!
+ char *d = IObuff; // assume IObuff is long enough!
d[0] = NUL;
if (STRNICMP(arg, "expr-", 5) == 0) {
@@ -367,7 +380,7 @@ int find_help_tags(const char *arg, int *num_matches, char ***matches, bool keep
// the table, it is taken literally (but ~ is escaped). Otherwise '?'
// is recognized as a wildcard.
for (i = (int)ARRAY_SIZE(expr_table); --i >= 0;) {
- if (STRCMP(arg + 5, expr_table[i]) == 0) {
+ if (strcmp(arg + 5, expr_table[i]) == 0) {
for (int si = 0, di = 0;; si++) {
if (arg[si] == '~') {
d[di++] = '\\';
@@ -384,7 +397,7 @@ int find_help_tags(const char *arg, int *num_matches, char ***matches, bool keep
// Recognize a few exceptions to the rule. Some strings that contain
// '*'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) {
+ if (strcmp(arg, except_tbl[i][0]) == 0) {
STRCPY(d, except_tbl[i][1]);
break;
}
@@ -399,7 +412,7 @@ int find_help_tags(const char *arg, int *num_matches, char ***matches, bool keep
// And also "\_$" and "\_^".
if (arg[0] == '\\'
&& ((arg[1] != NUL && arg[2] == NUL)
- || (vim_strchr("%_z@", arg[1]) != NULL
+ || (vim_strchr("%_z@", (uint8_t)arg[1]) != NULL
&& arg[2] != NUL))) {
vim_snprintf(d, IOSIZE, "/\\\\%s", arg + 1);
// Check for "/\\_$", should be "/\\_\$"
@@ -428,7 +441,7 @@ int find_help_tags(const char *arg, int *num_matches, char ***matches, bool keep
// completion.
// Insert a backslash before '~', '$' and '.' to avoid their
// special meaning.
- if ((char_u *)d - IObuff > IOSIZE - 10) { // getting too long!?
+ if (d - IObuff > IOSIZE - 10) { // getting too long!?
break;
}
switch (*s) {
@@ -458,8 +471,8 @@ int find_help_tags(const char *arg, int *num_matches, char ***matches, bool keep
// Insert '-' before and after "CTRL-X" when applicable.
if (*s < ' '
|| (*s == '^' && s[1]
- && (ASCII_ISALPHA(s[1]) || vim_strchr("?@[\\]^", s[1]) != NULL))) {
- if ((char_u *)d > IObuff && d[-1] != '_' && d[-1] != '\\') {
+ && (ASCII_ISALPHA(s[1]) || vim_strchr("?@[\\]^", (uint8_t)s[1]) != NULL))) {
+ if (d > IObuff && d[-1] != '_' && d[-1] != '\\') {
*d++ = '_'; // prepend a '_' to make x_CTRL-x
}
STRCPY(d, "CTRL-");
@@ -513,18 +526,18 @@ int find_help_tags(const char *arg, int *num_matches, char ***matches, bool keep
*d = NUL;
if (*IObuff == '`') {
- if ((char_u *)d > IObuff + 2 && d[-1] == '`') {
+ if (d > IObuff + 2 && d[-1] == '`') {
// remove the backticks from `command`
- memmove(IObuff, IObuff + 1, STRLEN(IObuff));
+ memmove(IObuff, IObuff + 1, strlen(IObuff));
d[-2] = NUL;
- } else if ((char_u *)d > IObuff + 3 && d[-2] == '`' && d[-1] == ',') {
+ } else if (d > IObuff + 3 && d[-2] == '`' && d[-1] == ',') {
// remove the backticks and comma from `command`,
- memmove(IObuff, IObuff + 1, STRLEN(IObuff));
+ memmove(IObuff, IObuff + 1, strlen(IObuff));
d[-3] = NUL;
- } else if ((char_u *)d > IObuff + 4 && d[-3] == '`'
+ } else if (d > IObuff + 4 && d[-3] == '`'
&& d[-2] == '\\' && d[-1] == '.') {
// remove the backticks and dot from `command`\.
- memmove(IObuff, IObuff + 1, STRLEN(IObuff));
+ memmove(IObuff, IObuff + 1, strlen(IObuff));
d[-4] = NUL;
}
}
@@ -542,7 +555,7 @@ int find_help_tags(const char *arg, int *num_matches, char ***matches, bool keep
// Sort the matches found on the heuristic number that is after the
// tag name.
qsort((void *)(*matches), (size_t)(*num_matches),
- sizeof(char_u *), help_compare);
+ sizeof(char *), help_compare);
// Delete more than TAG_MANY to reduce the size of the listing.
while (*num_matches > TAG_MANY) {
xfree((*matches)[--*num_matches]);
@@ -556,8 +569,8 @@ int find_help_tags(const char *arg, int *num_matches, char ***matches, bool keep
/// tag matches it. Otherwise remove "@en" if "en" is the only language.
void cleanup_help_tags(int num_file, char **file)
{
- char_u buf[4];
- char_u *p = buf;
+ char buf[4];
+ char *p = buf;
if (p_hlg[0] != NUL && (p_hlg[0] != 'e' || p_hlg[1] != 'n')) {
*p++ = '@';
@@ -567,18 +580,18 @@ void cleanup_help_tags(int num_file, char **file)
*p = NUL;
for (int i = 0; i < num_file; i++) {
- int len = (int)STRLEN(file[i]) - 3;
+ int len = (int)strlen(file[i]) - 3;
if (len <= 0) {
continue;
}
- if (STRCMP(file[i] + len, "@en") == 0) {
+ if (strcmp(file[i] + len, "@en") == 0) {
// Sorting on priority means the same item in another language may
// be anywhere. Search all items for a match up to the "@en".
int j;
for (j = 0; j < num_file; j++) {
if (j != i
- && (int)STRLEN(file[j]) == len + 3
- && STRNCMP(file[i], file[j], len + 1) == 0) {
+ && (int)strlen(file[j]) == len + 3
+ && strncmp(file[i], file[j], (size_t)len + 1) == 0) {
break;
}
}
@@ -591,11 +604,11 @@ void cleanup_help_tags(int num_file, char **file)
if (*buf != NUL) {
for (int i = 0; i < num_file; i++) {
- int len = (int)STRLEN(file[i]) - 3;
+ int len = (int)strlen(file[i]) - 3;
if (len <= 0) {
continue;
}
- if (STRCMP(file[i] + len, buf) == 0) {
+ if (strcmp(file[i] + len, buf) == 0) {
// remove the default language
file[i][len] = NUL;
}
@@ -615,7 +628,7 @@ void prepare_help_buffer(void)
// latin1 word characters (for translated help files).
// Only set it when needed, buf_init_chartab() is some work.
char *p = "!-~,^*,^|,^\",192-255";
- if (STRCMP(curbuf->b_p_isk, p) != 0) {
+ if (strcmp(curbuf->b_p_isk, p) != 0) {
set_string_option_direct("isk", -1, p, OPT_FREE|OPT_LOCAL, 0);
check_buf_options(curbuf);
(void)buf_init_chartab(curbuf, false);
@@ -650,21 +663,21 @@ void fix_help_buffer(void)
bool in_example = false;
// Set filetype to "help".
- if (STRCMP(curbuf->b_p_ft, "help") != 0) {
+ if (strcmp(curbuf->b_p_ft, "help") != 0) {
curbuf->b_ro_locked++;
- set_option_value("ft", 0L, "help", OPT_LOCAL);
+ set_option_value_give_err("ft", 0L, "help", OPT_LOCAL);
curbuf->b_ro_locked--;
}
if (!syntax_present(curwin)) {
for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; lnum++) {
- line = (char *)ml_get_buf(curbuf, lnum, false);
- const size_t len = STRLEN(line);
+ line = 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 = (char *)ml_get_buf(curbuf, lnum, true);
+ line = ml_get_buf(curbuf, lnum, true);
line[0] = ' ';
}
in_example = false;
@@ -672,12 +685,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 = (char *)ml_get_buf(curbuf, lnum, true);
+ line = 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 = (char *)ml_get_buf(curbuf, lnum, true);
+ line = ml_get_buf(curbuf, lnum, true);
line[len - 1] = ' ';
}
}
@@ -687,26 +700,26 @@ 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 *const fname = path_tail(curbuf->b_fname);
- if (FNAMECMP(fname, "help.txt") == 0
- || (FNAMENCMP(fname, "help.", 5) == 0
+ if (path_fnamecmp(fname, "help.txt") == 0
+ || (path_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 = (char *)ml_get_buf(curbuf, lnum, false);
+ line = ml_get_buf(curbuf, lnum, false);
if (strstr(line, "*local-additions*") == NULL) {
continue;
}
// Go through all directories in 'runtimepath', skipping
// $VIMRUNTIME.
- char *p = (char *)p_rtp;
+ char *p = p_rtp;
while (*p != NUL) {
- copy_option_part(&p, (char *)NameBuff, MAXPATHL, ",");
+ copy_option_part(&p, NameBuff, MAXPATHL, ",");
char *const rt = vim_getenv("VIMRUNTIME");
if (rt != NULL
- && path_full_compare(rt, (char *)NameBuff, false, true) != kEqualFiles) {
+ && path_full_compare(rt, NameBuff, false, true) != kEqualFiles) {
int fcount;
char **fnames;
char *s;
@@ -714,50 +727,48 @@ void fix_help_buffer(void)
char *cp;
// Find all "doc/ *.txt" files in this directory.
- if (!add_pathsep((char *)NameBuff)
- || STRLCAT(NameBuff, "doc/*.??[tx]", // NOLINT
- sizeof(NameBuff)) >= MAXPATHL) {
+ if (!add_pathsep(NameBuff)
+ || xstrlcat(NameBuff, "doc/*.??[tx]", // NOLINT
+ sizeof(NameBuff)) >= MAXPATHL) {
emsg(_(e_fnametoolong));
continue;
}
// Note: We cannot just do `&NameBuff` because it is a statically sized array
// so `NameBuff == &NameBuff` according to C semantics.
- char *buff_list[1] = { (char *)NameBuff };
+ char *buff_list[1] = { NameBuff };
if (gen_expand_wildcards(1, buff_list, &fcount,
&fnames, EW_FILE|EW_SILENT) == OK
&& fcount > 0) {
// If foo.abx is found use it instead of foo.txt in
// the same directory.
for (int i1 = 0; i1 < fcount; i1++) {
- for (int i2 = 0; i2 < fcount; i2++) {
- if (i1 == i2) {
- continue;
- }
- if (fnames[i1] == NULL || fnames[i2] == NULL) {
+ const char *const f1 = fnames[i1];
+ const char *const t1 = path_tail(f1);
+ const char *const e1 = strrchr(t1, '.');
+ if (path_fnamecmp(e1, ".txt") != 0
+ && path_fnamecmp(e1, fname + 4) != 0) {
+ // Not .txt and not .abx, remove it.
+ XFREE_CLEAR(fnames[i1]);
+ continue;
+ }
+
+ for (int i2 = i1 + 1; i2 < fcount; i2++) {
+ const char *const f2 = fnames[i2];
+ if (f2 == NULL) {
continue;
}
- 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, '.');
+ const char *const e2 = strrchr(t2, '.');
if (e1 == NULL || e2 == NULL) {
continue;
}
- 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) {
+ || path_fnamencmp(f1, f2, (size_t)(e1 - f1)) != 0) {
continue;
}
- if (FNAMECMP(e1, ".txt") == 0
- && FNAMECMP(e2, fname + 4) == 0) {
+ if (path_fnamecmp(e1, ".txt") == 0
+ && path_fnamecmp(e2, fname + 4) == 0) {
// use .abx instead of .txt
XFREE_CLEAR(fnames[i1]);
}
@@ -774,7 +785,7 @@ void fix_help_buffer(void)
}
vim_fgets(IObuff, IOSIZE, fd);
if (IObuff[0] == '*'
- && (s = vim_strchr((char *)IObuff + 1, '*'))
+ && (s = vim_strchr(IObuff + 1, '*'))
!= NULL) {
TriState this_utf = kNone;
// Change tag definition to a
@@ -788,7 +799,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 ((char_u)(*s) >= 0x80 && this_utf != kFalse) {
+ if ((uint8_t)(*s) >= 0x80 && this_utf != kFalse) {
this_utf = kTrue;
const int l = utf_ptr2len(s);
if (l == 1) {
@@ -803,23 +814,23 @@ void fix_help_buffer(void)
// 'encoding' may be required.
vc.vc_type = CONV_NONE;
convert_setup(&vc,
- (char_u *)(this_utf == kTrue ? "utf-8" : "latin1"),
+ (this_utf == kTrue ? "utf-8" : "latin1"),
p_enc);
if (vc.vc_type == CONV_NONE) {
// No conversion needed.
- cp = (char *)IObuff;
+ cp = IObuff;
} else {
// Do the conversion. If it fails
// use the unconverted text.
- cp = (char *)string_convert(&vc, IObuff, NULL);
+ cp = string_convert(&vc, IObuff, NULL);
if (cp == NULL) {
- cp = (char *)IObuff;
+ cp = IObuff;
}
}
convert_setup(&vc, NULL, NULL);
ml_append(lnum, cp, (colnr_T)0, false);
- if ((char_u *)cp != IObuff) {
+ if (cp != IObuff) {
xfree(cp);
}
lnum++;
@@ -869,17 +880,17 @@ static void helptags_one(char *dir, const char *ext, const char *tagfname, bool
bool mix = false; // detected mixed encodings
// Find all *.txt files.
- size_t dirlen = STRLCPY(NameBuff, dir, sizeof(NameBuff));
+ size_t dirlen = xstrlcpy(NameBuff, dir, sizeof(NameBuff));
if (dirlen >= MAXPATHL
- || STRLCAT(NameBuff, "/**/*", sizeof(NameBuff)) >= MAXPATHL // NOLINT
- || STRLCAT(NameBuff, ext, sizeof(NameBuff)) >= MAXPATHL) {
+ || xstrlcat(NameBuff, "/**/*", sizeof(NameBuff)) >= MAXPATHL // NOLINT
+ || xstrlcat(NameBuff, ext, sizeof(NameBuff)) >= MAXPATHL) {
emsg(_(e_fnametoolong));
return;
}
// Note: We cannot just do `&NameBuff` because it is a statically sized array
// so `NameBuff == &NameBuff` according to C semantics.
- char *buff_list[1] = { (char *)NameBuff };
+ char *buff_list[1] = { NameBuff };
const int res = gen_expand_wildcards(1, buff_list, &filecount, &files,
EW_FILE|EW_SILENT);
if (res == FAIL || filecount == 0) {
@@ -895,13 +906,13 @@ static void helptags_one(char *dir, const char *ext, const char *tagfname, bool
// Open the tags file for writing.
// Do this before scanning through all the files.
memcpy(NameBuff, dir, dirlen + 1);
- if (!add_pathsep((char *)NameBuff)
- || STRLCAT(NameBuff, tagfname, sizeof(NameBuff)) >= MAXPATHL) {
+ if (!add_pathsep(NameBuff)
+ || xstrlcat(NameBuff, tagfname, sizeof(NameBuff)) >= MAXPATHL) {
emsg(_(e_fnametoolong));
return;
}
- FILE *const fd_tags = os_fopen((char *)NameBuff, "w");
+ FILE *const fd_tags = os_fopen(NameBuff, "w");
if (fd_tags == NULL) {
if (!ignore_writeerr) {
semsg(_("E152: Cannot open %s for writing"), NameBuff);
@@ -912,10 +923,10 @@ static void helptags_one(char *dir, const char *ext, const char *tagfname, bool
// If using the "++t" argument or generating tags for "$VIMRUNTIME/doc"
// add the "help-tags" tag.
- ga_init(&ga, (int)sizeof(char_u *), 100);
+ ga_init(&ga, (int)sizeof(char *), 100);
if (add_help_tags
|| path_full_compare("$VIMRUNTIME/doc", dir, false, true) == kEqualFiles) {
- size_t s_len = 18 + STRLEN(tagfname);
+ size_t s_len = 18 + strlen(tagfname);
s = xmalloc(s_len);
snprintf(s, s_len, "help-tags\t%s\t1\n", tagfname);
GA_APPEND(char *, &ga, s);
@@ -930,13 +941,14 @@ static void helptags_one(char *dir, const char *ext, const char *tagfname, bool
}
const char *const fname = files[fi] + dirlen + 1;
+ bool in_example = false;
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 = (char *)IObuff; *s != NUL; s++) {
- if ((char_u)(*s) >= 0x80) {
+ for (s = IObuff; *s != NUL; s++) {
+ if ((uint8_t)(*s) >= 0x80) {
this_utf8 = kTrue;
const int l = utf_ptr2len(s);
if (l == 1) {
@@ -960,7 +972,14 @@ static void helptags_one(char *dir, const char *ext, const char *tagfname, bool
}
firstline = false;
}
- p1 = vim_strchr((char *)IObuff, '*'); // find first '*'
+ if (in_example) {
+ // skip over example; a non-white in the first column ends it
+ if (vim_strchr(" \t\n\r", (uint8_t)IObuff[0])) {
+ continue;
+ }
+ in_example = false;
+ }
+ p1 = vim_strchr(IObuff, '*'); // find first '*'
while (p1 != NULL) {
p2 = strchr((const char *)p1 + 1, '*'); // Find second '*'.
if (p2 != NULL && p2 > p1 + 1) { // Skip "*" and "**".
@@ -974,12 +993,12 @@ static void helptags_one(char *dir, const char *ext, const char *tagfname, bool
// characters, there is white space before it and is
// followed by a white character or end-of-line.
if (s == p2
- && ((char_u *)p1 == IObuff || p1[-1] == ' ' || p1[-1] == '\t')
- && (vim_strchr(" \t\n\r", s[1]) != NULL
+ && (p1 == IObuff || p1[-1] == ' ' || p1[-1] == '\t')
+ && (vim_strchr(" \t\n\r", (uint8_t)s[1]) != NULL
|| s[1] == '\0')) {
*p2 = '\0';
p1++;
- size_t s_len= (size_t)(p2 - p1) + STRLEN(fname) + 2;
+ size_t s_len = (size_t)(p2 - p1) + strlen(fname) + 2;
s = xmalloc(s_len);
GA_APPEND(char *, &ga, s);
snprintf(s, s_len, "%s\t%s", p1, fname);
@@ -990,6 +1009,11 @@ static void helptags_one(char *dir, const char *ext, const char *tagfname, bool
}
p1 = p2;
}
+ size_t len = strlen(IObuff);
+ if ((len == 2 && strcmp(&IObuff[len - 2], ">\n") == 0)
+ || (len >= 3 && strcmp(&IObuff[len - 3], " >\n") == 0)) {
+ in_example = true;
+ }
line_breakcheck();
}
@@ -1009,10 +1033,10 @@ static void helptags_one(char *dir, const char *ext, const char *tagfname, bool
while (*p1 == *p2) {
if (*p2 == '\t') {
*p2 = NUL;
- vim_snprintf((char *)NameBuff, MAXPATHL,
+ vim_snprintf(NameBuff, MAXPATHL,
_("E154: Duplicate tag \"%s\" in file %s/%s"),
((char_u **)ga.ga_data)[i], dir, p2 + 1);
- emsg((char *)NameBuff);
+ emsg(NameBuff);
*p2 = '\t';
break;
}
@@ -1028,7 +1052,7 @@ static void helptags_one(char *dir, const char *ext, const char *tagfname, bool
// Write the tags into the file.
for (int i = 0; i < ga.ga_len; i++) {
s = ((char **)ga.ga_data)[i];
- if (STRNCMP(s, "help-tags\t", 10) == 0) {
+ if (strncmp(s, "help-tags\t", 10) == 0) {
// help-tags entry was added in formatted form
fputs(s, fd_tags);
} else {
@@ -1065,16 +1089,16 @@ static void do_helptags(char *dirname, bool add_help_tags, bool ignore_writeerr)
char **files;
// Get a list of all files in the help directory and in subdirectories.
- STRLCPY(NameBuff, dirname, sizeof(NameBuff));
+ xstrlcpy(NameBuff, dirname, sizeof(NameBuff));
if (!add_pathsep((char *)NameBuff)
- || STRLCAT(NameBuff, "**", sizeof(NameBuff)) >= MAXPATHL) {
+ || xstrlcat(NameBuff, "**", sizeof(NameBuff)) >= MAXPATHL) {
emsg(_(e_fnametoolong));
return;
}
// Note: We cannot just do `&NameBuff` because it is a statically sized array
// so `NameBuff == &NameBuff` according to C semantics.
- char *buff_list[1] = { (char *)NameBuff };
+ char *buff_list[1] = { NameBuff };
if (gen_expand_wildcards(1, buff_list, &filecount, &files,
EW_FILE|EW_SILENT) == FAIL
|| filecount == 0) {
@@ -1087,7 +1111,7 @@ static void do_helptags(char *dirname, bool add_help_tags, bool ignore_writeerr)
int j;
ga_init(&ga, 1, 10);
for (int i = 0; i < filecount; i++) {
- len = (int)STRLEN(files[i]);
+ len = (int)strlen(files[i]);
if (len <= 4) {
continue;
}
@@ -1109,7 +1133,7 @@ static void do_helptags(char *dirname, bool add_help_tags, bool ignore_writeerr)
// Did we find this language already?
for (j = 0; j < ga.ga_len; j += 2) {
- if (STRNCMP(lang, ((char_u *)ga.ga_data) + j, 2) == 0) {
+ if (strncmp(lang, ((char *)ga.ga_data) + j, 2) == 0) {
break;
}
}
@@ -1157,19 +1181,18 @@ void ex_helptags(exarg_T *eap)
bool add_help_tags = false;
// Check for ":helptags ++t {dir}".
- if (STRNCMP(eap->arg, "++t", 3) == 0 && ascii_iswhite(eap->arg[3])) {
+ if (strncmp(eap->arg, "++t", 3) == 0 && ascii_iswhite(eap->arg[3])) {
add_help_tags = true;
eap->arg = skipwhite(eap->arg + 3);
}
- if (STRCMP(eap->arg, "ALL") == 0) {
+ if (strcmp(eap->arg, "ALL") == 0) {
do_in_path(p_rtp, "doc", DIP_ALL + DIP_DIR, helptags_cb, &add_help_tags);
} else {
ExpandInit(&xpc);
xpc.xp_context = EXPAND_DIRECTORIES;
- 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)) {
+ dirname = ExpandOne(&xpc, eap->arg, NULL, WILD_LIST_NOTFOUND|WILD_SILENT, WILD_EXPAND_FREE);
+ if (dirname == NULL || !os_isdir(dirname)) {
semsg(_("E150: Not a directory: %s"), eap->arg);
} else {
do_helptags(dirname, add_help_tags, false);
diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c
index d6a18fcf8e..9dab91cc2b 100644
--- a/src/nvim/highlight.c
+++ b/src/nvim/highlight.c
@@ -3,18 +3,32 @@
// highlight.c: low level code for UI and syntax highlighting
+#include <assert.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <string.h>
+
+#include "klib/kvec.h"
+#include "lauxlib.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
+#include "nvim/api/ui.h"
#include "nvim/decoration_provider.h"
#include "nvim/drawscreen.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
+#include "nvim/log.h"
#include "nvim/lua/executor.h"
+#include "nvim/macros.h"
#include "nvim/map.h"
+#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/option.h"
#include "nvim/popupmenu.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
@@ -115,19 +129,15 @@ static int get_attr_entry(HlEntry entry)
/// When a UI connects, we need to send it the table of highlights used so far.
void ui_send_all_hls(UI *ui)
{
- if (ui->hl_attr_define) {
- for (size_t i = 1; i < kv_size(attr_entries); i++) {
- Array inspect = hl_inspect((int)i);
- ui->hl_attr_define(ui, (Integer)i, kv_A(attr_entries, i).attr,
- kv_A(attr_entries, i).attr, inspect);
- api_free_array(inspect);
- }
+ for (size_t i = 1; i < kv_size(attr_entries); i++) {
+ Array inspect = hl_inspect((int)i);
+ remote_ui_hl_attr_define(ui, (Integer)i, kv_A(attr_entries, i).attr,
+ kv_A(attr_entries, i).attr, inspect);
+ api_free_array(inspect);
}
- if (ui->hl_group_set) {
- for (size_t hlf = 0; hlf < HLF_COUNT; hlf++) {
- ui->hl_group_set(ui, cstr_as_string((char *)hlf_names[hlf]),
- highlight_attr[hlf]);
- }
+ for (size_t hlf = 0; hlf < HLF_COUNT; hlf++) {
+ remote_ui_hl_group_set(ui, cstr_as_string((char *)hlf_names[hlf]),
+ highlight_attr[hlf]);
}
}
@@ -141,10 +151,9 @@ int hl_get_syn_attr(int ns_id, int idx, HlAttrs at_en)
|| at_en.rgb_ae_attr != 0 || ns_id != 0) {
return get_attr_entry((HlEntry){ .attr = at_en, .kind = kHlSyntax,
.id1 = idx, .id2 = ns_id });
- } else {
- // If all the fields are cleared, clear the attr field back to default value
- return 0;
}
+ // If all the fields are cleared, clear the attr field back to default value
+ return 0;
}
void ns_hl_def(NS ns_id, int hl_id, HlAttrs attrs, int link_id, Dict(highlight) *dict)
@@ -223,7 +232,7 @@ int ns_get_hl(NS *ns_hl, int hl_id, bool link, bool nodefault)
}
}
- it.attr_id = fallback ? -1 : hl_get_syn_attr((int)ns_id, hl_id, attrs);
+ it.attr_id = fallback ? -1 : hl_get_syn_attr(ns_id, hl_id, attrs);
it.version = p->hl_valid - tmp;
it.is_default = attrs.rgb_ae_attr & HL_DEFAULT;
it.link_global = attrs.rgb_ae_attr & HL_GLOBAL;
@@ -238,12 +247,11 @@ int ns_get_hl(NS *ns_hl, int hl_id, bool link, bool nodefault)
if (link) {
if (it.attr_id >= 0) {
return 0;
- } else {
- if (it.link_global) {
- *ns_hl = 0;
- }
- return it.link_id;
}
+ if (it.link_global) {
+ *ns_hl = 0;
+ }
+ return it.link_id;
} else {
return it.attr_id;
}
@@ -406,8 +414,8 @@ void update_ns_hl(int ns_id)
}
int *hl_attrs = **alloc;
- for (int hlf = 0; hlf < (int)HLF_COUNT; hlf++) {
- int id = syn_check_group(hlf_names[hlf], STRLEN(hlf_names[hlf]));
+ for (int hlf = 0; hlf < HLF_COUNT; hlf++) {
+ int id = syn_check_group(hlf_names[hlf], strlen(hlf_names[hlf]));
bool optional = (hlf == HLF_INACTIVE || hlf == HLF_NFLOAT);
hl_attrs[hlf] = hl_get_ui_attr(ns_id, hlf, id, optional);
}
@@ -641,7 +649,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_ANY_UNDERLINE)) {
+ if (cattrs.rgb_ae_attr & (HL_UNDERLINE_MASK)) {
cattrs.rgb_sp_color = rgb_blend(ratio, battrs.rgb_sp_color,
fattrs.rgb_bg_color);
} else {
@@ -659,7 +667,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_ANY_UNDERLINE)) {
+ if (cattrs.rgb_ae_attr & (HL_UNDERLINE_MASK)) {
cattrs.rgb_sp_color = rgb_blend(ratio/2, battrs.rgb_bg_color,
fattrs.rgb_sp_color);
} else {
@@ -729,7 +737,7 @@ static int hl_cterm2rgb_color(int nr)
0x08, 0x12, 0x1C, 0x26, 0x30, 0x3A, 0x44, 0x4E, 0x58, 0x62, 0x6C, 0x76,
0x80, 0x8A, 0x94, 0x9E, 0xA8, 0xB2, 0xBC, 0xC6, 0xD0, 0xDA, 0xE4, 0xEE
};
- static char_u ansi_table[16][4] = {
+ static uint8_t ansi_table[16][4] = {
// R G B idx
{ 0, 0, 0, 1 }, // black
{ 224, 0, 0, 2 }, // dark red
@@ -788,7 +796,7 @@ HlAttrs syn_attr2entry(int attr)
}
/// Gets highlight description for id `attr_id` as a map.
-Dictionary hl_get_attr_by_id(Integer attr_id, Boolean rgb, Error *err)
+Dictionary hl_get_attr_by_id(Integer attr_id, Boolean rgb, Arena *arena, Error *err)
{
Dictionary dic = ARRAY_DICT_INIT;
@@ -801,66 +809,68 @@ Dictionary hl_get_attr_by_id(Integer attr_id, Boolean rgb, Error *err)
"Invalid attribute id: %" PRId64, attr_id);
return dic;
}
-
- return hlattrs2dict(NULL, syn_attr2entry((int)attr_id), rgb);
+ Dictionary retval = arena_dict(arena, HLATTRS_DICT_SIZE);
+ hlattrs2dict(&retval, syn_attr2entry((int)attr_id), rgb);
+ return retval;
}
/// Converts an HlAttrs into Dictionary
///
-/// @param[out] hl optional pre-allocated dictionary for return value
-/// if present, must be allocated with at least 16 elements!
+/// @param[in/out] hl Dictionary with pre-allocated space for HLATTRS_DICT_SIZE elements
/// @param[in] aep data to convert
/// @param use_rgb use 'gui*' settings if true, else resorts to 'cterm*'
-Dictionary hlattrs2dict(Dictionary *hl_alloc, HlAttrs ae, bool use_rgb)
+void hlattrs2dict(Dictionary *dict, HlAttrs ae, bool use_rgb)
{
+ assert(dict->capacity >= HLATTRS_DICT_SIZE); // at most 16 items
+ Dictionary hl = *dict;
int mask = use_rgb ? ae.rgb_ae_attr : ae.cterm_ae_attr;
- Dictionary hl = ARRAY_DICT_INIT;
- if (hl_alloc) {
- hl = *hl_alloc;
- } else {
- kv_ensure_space(hl, 16);
+
+ if (mask & HL_INVERSE) {
+ PUT_C(hl, "reverse", BOOLEAN_OBJ(true));
}
if (mask & HL_BOLD) {
PUT_C(hl, "bold", BOOLEAN_OBJ(true));
}
- if (mask & HL_STANDOUT) {
- PUT_C(hl, "standout", BOOLEAN_OBJ(true));
+ if (mask & HL_ITALIC) {
+ PUT_C(hl, "italic", BOOLEAN_OBJ(true));
}
- if (mask & HL_UNDERLINE) {
+ switch (mask & HL_UNDERLINE_MASK) {
+ case HL_UNDERLINE:
PUT_C(hl, "underline", BOOLEAN_OBJ(true));
- }
-
- if (mask & HL_UNDERCURL) {
- PUT_C(hl, "undercurl", BOOLEAN_OBJ(true));
- }
+ break;
- if (mask & HL_UNDERDOUBLE) {
+ case HL_UNDERDOUBLE:
PUT_C(hl, "underdouble", BOOLEAN_OBJ(true));
- }
+ break;
- if (mask & HL_UNDERDOTTED) {
+ case HL_UNDERCURL:
+ PUT_C(hl, "undercurl", BOOLEAN_OBJ(true));
+ break;
+
+ case HL_UNDERDOTTED:
PUT_C(hl, "underdotted", BOOLEAN_OBJ(true));
- }
+ break;
- if (mask & HL_UNDERDASHED) {
+ case HL_UNDERDASHED:
PUT_C(hl, "underdashed", BOOLEAN_OBJ(true));
+ break;
}
- if (mask & HL_ITALIC) {
- PUT_C(hl, "italic", BOOLEAN_OBJ(true));
- }
-
- if (mask & HL_INVERSE) {
- PUT_C(hl, "reverse", BOOLEAN_OBJ(true));
+ if (mask & HL_STANDOUT) {
+ PUT_C(hl, "standout", BOOLEAN_OBJ(true));
}
if (mask & HL_STRIKETHROUGH) {
PUT_C(hl, "strikethrough", BOOLEAN_OBJ(true));
}
+ if (mask & HL_ALTFONT) {
+ PUT_C(hl, "altfont", BOOLEAN_OBJ(true));
+ }
+
if (mask & HL_NOCOMBINE) {
PUT_C(hl, "nocombine", BOOLEAN_OBJ(true));
}
@@ -899,14 +909,7 @@ Dictionary hlattrs2dict(Dictionary *hl_alloc, HlAttrs ae, bool use_rgb)
PUT_C(hl, "blend", INTEGER_OBJ(ae.hl_blend));
}
- if (hl_alloc) {
- *hl_alloc = hl;
- return hl;
- } else {
- Dictionary allocated = copy_dictionary(hl);
- kv_destroy(hl);
- return allocated;
- }
+ *dict = hl;
}
HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *err)
@@ -923,32 +926,37 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e
m = m | flag; \
}
+ CHECK_FLAG(dict, mask, reverse, , HL_INVERSE);
CHECK_FLAG(dict, mask, bold, , HL_BOLD);
- CHECK_FLAG(dict, mask, standout, , HL_STANDOUT);
+ CHECK_FLAG(dict, mask, italic, , HL_ITALIC);
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, undercurl, , HL_UNDERCURL);
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, standout, , HL_STANDOUT);
CHECK_FLAG(dict, mask, strikethrough, , HL_STRIKETHROUGH);
+ CHECK_FLAG(dict, mask, altfont, , HL_ALTFONT);
+ if (use_rgb) {
+ CHECK_FLAG(dict, mask, fg_indexed, , HL_FG_INDEXED);
+ CHECK_FLAG(dict, mask, bg_indexed, , HL_BG_INDEXED);
+ }
CHECK_FLAG(dict, mask, nocombine, , HL_NOCOMBINE);
CHECK_FLAG(dict, mask, default, _, HL_DEFAULT);
if (HAS_KEY(dict->fg)) {
- fg = object_to_color(dict->fg, "fg", true, err);
+ fg = object_to_color(dict->fg, "fg", use_rgb, err);
} else if (HAS_KEY(dict->foreground)) {
- fg = object_to_color(dict->foreground, "foreground", true, err);
+ fg = object_to_color(dict->foreground, "foreground", use_rgb, err);
}
if (ERROR_SET(err)) {
return hlattrs;
}
if (HAS_KEY(dict->bg)) {
- bg = object_to_color(dict->bg, "bg", true, err);
+ bg = object_to_color(dict->bg, "bg", use_rgb, err);
} else if (HAS_KEY(dict->background)) {
- bg = object_to_color(dict->background, "background", true, err);
+ bg = object_to_color(dict->background, "background", use_rgb, err);
}
if (ERROR_SET(err)) {
return hlattrs;
@@ -1004,13 +1012,14 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e
}
cterm_mask_provided = true;
+ CHECK_FLAG(cterm, cterm_mask, reverse, , HL_INVERSE);
CHECK_FLAG(cterm, cterm_mask, bold, , HL_BOLD);
- CHECK_FLAG(cterm, cterm_mask, standout, , HL_STANDOUT);
+ CHECK_FLAG(cterm, cterm_mask, italic, , HL_ITALIC);
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, standout, , HL_STANDOUT);
CHECK_FLAG(cterm, cterm_mask, strikethrough, , HL_STRIKETHROUGH);
+ CHECK_FLAG(cterm, cterm_mask, altfont, , HL_ALTFONT);
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
@@ -1035,11 +1044,11 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e
}
}
- // apply gui mask as default for cterm mask
- if (!cterm_mask_provided) {
- cterm_mask = mask;
- }
if (use_rgb) {
+ // apply gui mask as default for cterm mask
+ if (!cterm_mask_provided) {
+ cterm_mask = mask;
+ }
hlattrs.rgb_ae_attr = mask;
hlattrs.rgb_bg_color = bg;
hlattrs.rgb_fg_color = fg;
@@ -1049,9 +1058,9 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e
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 ? 0 : bg + 1;
+ hlattrs.cterm_fg_color = fg == -1 ? 0 : fg + 1;
+ hlattrs.cterm_ae_attr = mask;
}
return hlattrs;
@@ -1086,6 +1095,7 @@ int object_to_color(Object val, char *key, bool rgb, Error *err)
Array hl_inspect(int attr)
{
+ // TODO(bfredl): use arena allocation
Array ret = ARRAY_DICT_INIT;
if (hlstate_active) {
hl_inspect_impl(&ret, attr);
diff --git a/src/nvim/highlight.h b/src/nvim/highlight.h
index 50299bb91c..4da7dd65bb 100644
--- a/src/nvim/highlight.h
+++ b/src/nvim/highlight.h
@@ -6,6 +6,7 @@
#include "nvim/api/private/defs.h"
#include "nvim/buffer_defs.h"
#include "nvim/highlight_defs.h"
+#include "nvim/option_defs.h"
#include "nvim/ui.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -19,6 +20,8 @@ static inline int win_hl_attr(win_T *wp, int hlf)
return ((wp->w_ns_hl_attr && ns_hl_fast < 0) ? wp->w_ns_hl_attr : hl_attr_active)[hlf];
}
+#define HLATTRS_DICT_SIZE 16
+
#define HL_SET_DEFAULT_COLORS(rgb_fg, rgb_bg, rgb_sp) \
do { \
bool dark_ = (*p_bg == 'd'); \
diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h
index ffcb0f3f22..a4dcf6eb60 100644
--- a/src/nvim/highlight_defs.h
+++ b/src/nvim/highlight_defs.h
@@ -15,19 +15,23 @@ typedef enum {
HL_INVERSE = 0x01,
HL_BOLD = 0x02,
HL_ITALIC = 0x04,
+ // The next three bits are all underline styles
+ HL_UNDERLINE_MASK = 0x38,
HL_UNDERLINE = 0x08,
- HL_UNDERCURL = 0x10,
- HL_UNDERDOUBLE = 0x20,
- HL_UNDERDOTTED = 0x40,
- HL_UNDERDASHED = 0x80,
- HL_STANDOUT = 0x0100,
- HL_NOCOMBINE = 0x0200,
- HL_STRIKETHROUGH = 0x0400,
+ HL_UNDERDOUBLE = 0x10,
+ HL_UNDERCURL = 0x18,
+ HL_UNDERDOTTED = 0x20,
+ HL_UNDERDASHED = 0x28,
+ // 0x30 and 0x38 spare for underline styles
+ HL_STANDOUT = 0x0040,
+ HL_STRIKETHROUGH = 0x0080,
+ HL_ALTFONT = 0x0100,
+ // 0x0200 spare
+ HL_NOCOMBINE = 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
@@ -114,6 +118,7 @@ typedef enum {
HLF_WBR, // Window bars
HLF_WBRNC, // Window bars of not-current windows
HLF_CU, // Cursor
+ HLF_BTITLE, // Float Border Title
HLF_COUNT, // MUST be the last one
} hlf_T;
@@ -178,6 +183,7 @@ EXTERN const char *hlf_names[] INIT(= {
[HLF_WBR] = "WinBar",
[HLF_WBRNC] = "WinBarNC",
[HLF_CU] = "Cursor",
+ [HLF_BTITLE] = "FloatTitle",
});
EXTERN int highlight_attr[HLF_COUNT + 1]; // Highl. attr for each context.
diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c
index 77424de3b8..5b1ea9967d 100644
--- a/src/nvim/highlight_group.c
+++ b/src/nvim/highlight_group.c
@@ -3,29 +3,53 @@
// highlight_group.c: code for managing highlight groups
+#include <ctype.h>
#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
+#include "nvim/ascii.h"
#include "nvim/autocmd.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/cursor_shape.h"
+#include "nvim/decoration_provider.h"
#include "nvim/drawscreen.h"
#include "nvim/eval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/eval/vars.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
-#include "nvim/fold.h"
+#include "nvim/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/grid_defs.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
#include "nvim/lua/executor.h"
-#include "nvim/match.h"
+#include "nvim/macros.h"
+#include "nvim/map.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
#include "nvim/option.h"
+#include "nvim/os/time.h"
#include "nvim/runtime.h"
+#include "nvim/strings.h"
+#include "nvim/types.h"
+#include "nvim/ui.h"
+#include "nvim/vim.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
+enum {
+ SG_CTERM = 2, // cterm has been set
+ SG_GUI = 4, // gui has been set
+ SG_LINK = 8, // link has been set
+};
/// @}
#define MAX_SYN_NAME 200
@@ -43,32 +67,32 @@ Map(cstr_t, int) highlight_unames = MAP_INIT;
static char *(hl_name_table[]) =
{ "bold", "standout", "underline",
"undercurl", "underdouble", "underdotted", "underdashed",
- "italic", "reverse", "inverse", "strikethrough", "nocombine", "NONE" };
+ "italic", "reverse", "inverse", "strikethrough", "altfont",
+ "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 };
+ HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_STRIKETHROUGH, HL_ALTFONT,
+ 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
+ char *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
+ 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
+ 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
@@ -79,6 +103,8 @@ typedef struct {
int sg_rgb_sp_idx; ///< RGB special color index
int sg_blend; ///< blend level (0-100 inclusive), -1 if unset
+
+ int sg_parent; ///< parent of @nested.group
} HlGroup;
enum {
@@ -96,8 +122,6 @@ enum {
// 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",
@@ -131,6 +155,7 @@ static const char *highlight_init_both[] = {
"default link MsgSeparator StatusLine",
"default link NormalFloat Pmenu",
"default link FloatBorder WinSeparator",
+ "default link FloatTitle Title",
"default FloatShadow blend=80 guibg=Black",
"default FloatShadowThrough blend=100 guibg=Black",
"RedrawDebugNormal cterm=reverse gui=reverse",
@@ -167,22 +192,94 @@ static const char *highlight_init_both[] = {
"default DiagnosticWarn ctermfg=3 guifg=Orange",
"default DiagnosticInfo ctermfg=4 guifg=LightBlue",
"default DiagnosticHint ctermfg=7 guifg=LightGrey",
+ "default DiagnosticOk ctermfg=10 guifg=LightGreen",
"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 DiagnosticUnderlineOk cterm=underline gui=underline guisp=LightGreen",
"default link DiagnosticVirtualTextError DiagnosticError",
"default link DiagnosticVirtualTextWarn DiagnosticWarn",
"default link DiagnosticVirtualTextInfo DiagnosticInfo",
"default link DiagnosticVirtualTextHint DiagnosticHint",
+ "default link DiagnosticVirtualTextOk DiagnosticOk",
"default link DiagnosticFloatingError DiagnosticError",
"default link DiagnosticFloatingWarn DiagnosticWarn",
"default link DiagnosticFloatingInfo DiagnosticInfo",
"default link DiagnosticFloatingHint DiagnosticHint",
+ "default link DiagnosticFloatingOk DiagnosticOk",
"default link DiagnosticSignError DiagnosticError",
"default link DiagnosticSignWarn DiagnosticWarn",
"default link DiagnosticSignInfo DiagnosticInfo",
"default link DiagnosticSignHint DiagnosticHint",
+ "default link DiagnosticSignOk DiagnosticOk",
+
+ // Text
+ "default link @text.literal Comment",
+ "default link @text.reference Identifier",
+ "default link @text.title Title",
+ "default link @text.uri Underlined",
+ "default link @text.underline Underlined",
+ "default link @text.todo Todo",
+
+ // Miscs
+ "default link @comment Comment",
+ "default link @punctuation Delimiter",
+
+ // Constants
+ "default link @constant Constant",
+ "default link @constant.builtin Special",
+ "default link @constant.macro Define",
+ "default link @define Define",
+ "default link @macro Macro",
+ "default link @string String",
+ "default link @string.escape SpecialChar",
+ "default link @string.special SpecialChar",
+ "default link @character Character",
+ "default link @character.special SpecialChar",
+ "default link @number Number",
+ "default link @boolean Boolean",
+ "default link @float Float",
+
+ // Functions
+ "default link @function Function",
+ "default link @function.builtin Special",
+ "default link @function.macro Macro",
+ "default link @parameter Identifier",
+ "default link @method Function",
+ "default link @field Identifier",
+ "default link @property Identifier",
+ "default link @constructor Special",
+
+ // Keywords
+ "default link @conditional Conditional",
+ "default link @repeat Repeat",
+ "default link @label Label",
+ "default link @operator Operator",
+ "default link @keyword Keyword",
+ "default link @exception Exception",
+
+ "default link @variable Identifier",
+ "default link @type Type",
+ "default link @type.definition Typedef",
+ "default link @storageclass StorageClass",
+ "default link @namespace Identifier",
+ "default link @include Include",
+ "default link @preproc PreProc",
+ "default link @debug Debug",
+ "default link @tag Tag",
+
+ // LSP semantic tokens
+ "default link @class Structure",
+ "default link @struct Structure",
+ "default link @enum Type",
+ "default link @enumMember Constant",
+ "default link @event Identifier",
+ "default link @interface Identifier",
+ "default link @modifier Identifier",
+ "default link @regexp SpecialChar",
+ "default link @typeParameter Type",
+ "default link @decorator Identifier",
NULL
};
@@ -474,7 +571,7 @@ int highlight_num_groups(void)
}
/// Returns the name of a highlight group.
-char_u *highlight_group_name(int id)
+char *highlight_group_name(int id)
{
return hl_table[id].sg_name;
}
@@ -504,11 +601,11 @@ void init_highlight(bool both, bool reset)
// 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");
+ char *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);
+ char *copy_p = xstrdup(p);
bool okay = load_colors(copy_p);
xfree(copy_p);
if (okay) {
@@ -557,11 +654,10 @@ void init_highlight(bool both, bool reset)
}
/// Load color file "name".
-/// Return OK for success, FAIL for failure.
-int load_colors(char_u *name)
+///
+/// @return OK for success, FAIL for failure.
+int load_colors(char *name)
{
- char_u *buf;
- int retval = FAIL;
static bool recursive = false;
// When being called recursively, this is probably because setting
@@ -572,17 +668,19 @@ int load_colors(char_u *name)
}
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);
+ size_t buflen = strlen(name) + 12;
+ char *buf = xmalloc(buflen);
+ apply_autocmds(EVENT_COLORSCHEMEPRE, name, curbuf->b_fname, false, curbuf);
+ snprintf(buf, buflen, "colors/%s.vim", name);
+ int retval = source_runtime(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);
+ snprintf(buf, buflen, "colors/%s.lua", name);
+ retval = source_runtime(buf, DIP_START + DIP_OPT);
}
xfree(buf);
- apply_autocmds(EVENT_COLORSCHEME, (char *)name, curbuf->b_fname, false, curbuf);
+ if (retval == OK) {
+ apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, false, curbuf);
+ }
recursive = false;
@@ -640,7 +738,7 @@ static int color_numbers_8[28] = { 0, 4, 2, 6,
// 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
+// "boldp" will be set to kTrue or kFalse 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)
{
@@ -740,7 +838,7 @@ void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id)
g->sg_attr = hl_get_syn_attr(0, id, attrs);
// 'Normal' is special
- if (STRCMP(g->sg_name_u, "NORMAL") == 0) {
+ 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;
@@ -756,7 +854,7 @@ void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id)
update:
if (!updating_screen) {
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
}
need_highlight_changed = true;
}
@@ -773,27 +871,8 @@ update:
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))) {
+ if (!init && 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);
@@ -801,18 +880,23 @@ void do_highlight(const char *line, const bool forceit, const bool init)
return;
}
+ bool dodefault = false;
+
// Isolate the name.
- name_end = (const char *)skiptowhite((const char_u *)line);
- linep = (const char *)skipwhite(name_end);
+ const char *name_end = (const char *)skiptowhite(line);
+ const char *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);
+ name_end = (const char *)skiptowhite(line);
linep = (const char *)skipwhite(name_end);
}
+ bool doclear = false;
+ bool dolink = false;
+
// Check for "clear" or "link" argument.
if (strncmp(line, "clear", (size_t)(name_end - line)) == 0) {
doclear = true;
@@ -822,7 +906,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
// ":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));
+ int id = syn_name2id_len(line, (size_t)(name_end - line));
if (id == 0) {
semsg(_("E411: highlight group not found: %s"), line);
} else {
@@ -841,9 +925,9 @@ void do_highlight(const char *line, const bool forceit, const bool init)
int to_id;
HlGroup *hlgroup = NULL;
- from_end = (const char *)skiptowhite((const char_u *)from_start);
+ from_end = (const char *)skiptowhite(from_start);
to_start = (const char *)skipwhite(from_end);
- to_end = (const char *)skiptowhite((const char_u *)to_start);
+ to_end = (const char *)skiptowhite(to_start);
if (ends_excmd((uint8_t)(*from_start))
|| ends_excmd((uint8_t)(*to_start))) {
@@ -893,7 +977,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
hlgroup->sg_script_ctx.sc_lnum += SOURCING_LNUM;
nlua_set_sctx(&hlgroup->sg_script_ctx);
hlgroup->sg_cleared = false;
- redraw_all_later(SOME_VALID);
+ redraw_all_later(UPD_SOME_VALID);
// Only call highlight changed() once after multiple changes
need_highlight_changed = true;
@@ -916,19 +1000,19 @@ void do_highlight(const char *line, const bool forceit, const bool init)
}
init_highlight(true, true);
highlight_changed();
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
return;
}
- name_end = (const char *)skiptowhite((const char_u *)line);
+ name_end = (const char *)skiptowhite(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));
+ int 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.
+ int 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)) {
@@ -936,8 +1020,8 @@ void do_highlight(const char *line, const bool forceit, const bool init)
}
// 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);
+ HlGroup item_before = hl_table[idx];
+ bool 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)) {
@@ -947,11 +1031,16 @@ void do_highlight(const char *line, const bool forceit, const bool init)
}
}
+ bool did_change = false;
+ bool error = false;
+
char key[64];
char arg[512];
if (!doclear) {
+ const char *arg_start;
+
while (!ends_excmd((uint8_t)(*linep))) {
- key_start = linep;
+ const char *key_start = linep;
if (*linep == '=') {
semsg(_("E415: unexpected equal sign: %s"), key_start);
error = true;
@@ -971,7 +1060,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
}
memcpy(key, key_start, key_len);
key[key_len] = NUL;
- vim_strup((char_u *)key);
+ vim_strup(key);
linep = (const char *)skipwhite(linep);
if (strcmp(key, "NONE") == 0) {
@@ -1004,7 +1093,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
}
} else {
arg_start = linep;
- linep = (const char *)skiptowhite((const char_u *)linep);
+ linep = (const char *)skiptowhite(linep);
}
if (linep == arg_start) {
semsg(_("E417: missing argument: %s"), key_start);
@@ -1028,12 +1117,12 @@ void do_highlight(const char *line, const bool forceit, const bool init)
if (strcmp(key, "TERM") == 0
|| strcmp(key, "CTERM") == 0
|| strcmp(key, "GUI") == 0) {
- attr = 0;
- off = 0;
+ int attr = 0;
+ int off = 0;
int i;
while (arg[off] != NUL) {
for (i = ARRAY_SIZE(hl_attr_table); --i >= 0;) {
- len = (int)STRLEN(hl_name_table[i]);
+ int len = (int)strlen(hl_name_table[i]);
if (STRNICMP(arg + off, hl_name_table[i], len) == 0) {
attr |= hl_attr_table[i];
off += len;
@@ -1068,9 +1157,9 @@ void do_highlight(const char *line, const bool forceit, const bool init)
hl_table[idx].sg_gui = attr;
}
}
- } else if (STRCMP(key, "FONT") == 0) {
+ } else if (strcmp(key, "FONT") == 0) {
// in non-GUI fonts are simply ignored
- } else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0) {
+ } 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;
@@ -1083,6 +1172,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
hl_table[idx].sg_cterm_bold = false;
}
+ int color;
if (ascii_isdigit(*arg)) {
color = atoi(arg);
} else if (STRICMP(arg, "fg") == 0) {
@@ -1103,7 +1193,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
}
} else {
// Reduce calls to STRICMP a bit, it can be slow.
- off = TOUPPER_ASC(*arg);
+ int off = TOUPPER_ASC(*arg);
int i;
for (i = ARRAY_SIZE(color_names); --i >= 0;) {
if (off == color_names[i][0]
@@ -1156,7 +1246,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
if (dark != -1
&& dark != (*p_bg == 'd')
&& !option_was_set("bg")) {
- set_option_value("bg", 0L, (dark ? "dark" : "light"), 0);
+ set_option_value_give_err("bg", 0L, (dark ? "dark" : "light"), 0);
reset_option_was_set("bg");
}
}
@@ -1188,7 +1278,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
if (is_normal_group) {
normal_fg = hl_table[idx].sg_rgb_fg;
}
- } else if (STRCMP(key, "GUIBG") == 0) {
+ } else if (strcmp(key, "GUIBG") == 0) {
int *indexp = &hl_table[idx].sg_rgb_bg_idx;
if (!init || !(hl_table[idx].sg_set & SG_GUI)) {
@@ -1199,7 +1289,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
RgbValue old_color = hl_table[idx].sg_rgb_bg;
int old_idx = hl_table[idx].sg_rgb_bg_idx;
- if (STRCMP(arg, "NONE") != 0) {
+ if (strcmp(arg, "NONE") != 0) {
hl_table[idx].sg_rgb_bg = name_to_color(arg, indexp);
} else {
hl_table[idx].sg_rgb_bg = -1;
@@ -1260,6 +1350,8 @@ void do_highlight(const char *line, const bool forceit, const bool init)
}
}
+ bool did_highlight_changed = false;
+
if (!error && is_normal_group) {
// Need to update all groups, because they might be using "bg" and/or
// "fg", which have been changed now.
@@ -1270,12 +1362,12 @@ void do_highlight(const char *line, const bool forceit, const bool init)
// changed
ui_refresh();
} else {
- // TUI and newer UIs will repaint the screen themselves. NOT_VALID
+ // TUI and newer UIs will repaint the screen themselves. UPD_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);
+ redraw_all_later(UPD_NOT_VALID);
} else {
set_hl_attr(idx);
}
@@ -1292,7 +1384,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
// redrawing. This may happen when evaluating 'statusline' changes the
// StatusLine group.
if (!updating_screen) {
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
}
need_highlight_changed = true;
}
@@ -1303,7 +1395,7 @@ void free_highlight(void)
{
ga_clear(&highlight_ga);
map_destroy(cstr_t, int)(&highlight_unames);
- arena_mem_free(arena_finish(&highlight_arena), NULL);
+ arena_mem_free(arena_finish(&highlight_arena));
}
#endif
@@ -1321,7 +1413,7 @@ void restore_cterm_colors(void)
/// @param check_link if true also check for an existing link.
///
-/// @return TRUE if highlight group "idx" has any settings.
+/// @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
@@ -1375,6 +1467,11 @@ static void highlight_list_one(const int id)
return;
}
+ // don't list specialized groups if a parent is used instead
+ if (sgp->sg_parent && sgp->sg_cleared) {
+ return;
+ }
+
didh = highlight_list_arg(id, didh, LIST_ATTR,
sgp->sg_cterm, NULL, "cterm");
didh = highlight_list_arg(id, didh, LIST_INT,
@@ -1400,7 +1497,7 @@ static void highlight_list_one(const int id)
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);
+ msg_outtrans(hl_table[hl_table[id - 1].sg_link - 1].sg_name);
}
if (!didh) {
@@ -1411,19 +1508,21 @@ static void highlight_list_one(const int id)
}
}
-Dictionary get_global_hl_defs(void)
+Dictionary get_global_hl_defs(Arena *arena)
{
- Dictionary rv = ARRAY_DICT_INIT;
- for (int i = 1; i <= highlight_ga.ga_len && !got_int; i++) {
+ Dictionary rv = arena_dict(arena, (size_t)highlight_ga.ga_len);
+ for (int i = 1; i <= highlight_ga.ga_len; i++) {
Dictionary attrs = ARRAY_DICT_INIT;
HlGroup *h = &hl_table[i - 1];
if (h->sg_attr > 0) {
- attrs = hlattrs2dict(NULL, syn_attr2entry(h->sg_attr), true);
+ attrs = arena_dict(arena, HLATTRS_DICT_SIZE);
+ hlattrs2dict(&attrs, 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)));
+ attrs = arena_dict(arena, 1);
+ char *link = hl_table[h->sg_link - 1].sg_name;
+ PUT_C(attrs, "link", STRING_OBJ(cstr_as_string(link)));
}
- PUT(rv, (const char *)h->sg_name, DICTIONARY_OBJ(attrs));
+ PUT_C(rv, (char *)h->sg_name, DICTIONARY_OBJ(attrs));
}
return rv;
@@ -1437,39 +1536,41 @@ Dictionary get_global_hl_defs(void)
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"
+
+ if (type == LIST_STRING ? (sarg == NULL) : (iarg == 0)) {
+ return didh;
+ }
+
+ char buf[100];
+ 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);
+ (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;
}
@@ -1484,19 +1585,24 @@ static bool highlight_list_arg(const int id, bool didh, const int type, int iarg
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;
}
+ int attr;
+
if (modec == 'g') {
attr = hl_table[id - 1].sg_gui;
} else {
attr = hl_table[id - 1].sg_cterm;
}
- return (attr & flag) ? "1" : NULL;
+ if (flag & HL_UNDERLINE_MASK) {
+ int ul = attr & HL_UNDERLINE_MASK;
+ return ul == flag ? "1" : NULL;
+ } else {
+ return (attr & flag) ? "1" : NULL;
+ }
}
/// Return color name of the given highlight group
@@ -1512,7 +1618,6 @@ const char *highlight_color(const int id, const char *const what, const int mode
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
static char name[20];
- int n;
bool fg = false;
bool sp = false;
bool font = false;
@@ -1531,6 +1636,9 @@ const char *highlight_color(const int id, const char *const what, const int mode
} else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g')) {
return NULL;
}
+
+ int n;
+
if (modec == 'g') {
if (what[2] == '#' && ui_rgb_attached()) {
if (fg) {
@@ -1592,7 +1700,7 @@ bool syn_list_header(const bool did_header, const int outlen, const int id, bool
if (got_int) {
return true;
}
- msg_outtrans((char *)hl_table[id - 1].sg_name);
+ msg_outtrans(hl_table[id - 1].sg_name);
name_col = msg_col;
endcol = 15;
} else if ((ui_has(kUIMessages) || msg_silent) && !force_newline) {
@@ -1661,7 +1769,11 @@ static void set_hl_attr(int idx)
int syn_name2id(const char *name)
FUNC_ATTR_NONNULL_ALL
{
- return syn_name2id_len(name, STRLEN(name));
+ if (name[0] == '@') {
+ // if we look up @aaa.bbb, we have to consider @aaa as well
+ return syn_check_group(name, strlen(name));
+ }
+ return syn_name2id_len(name, strlen(name));
}
/// Lookup a highlight group name and return its ID.
@@ -1681,7 +1793,7 @@ int syn_name2id_len(const char *name, size_t len)
// Avoid alloc()/free(), these are slow too.
memcpy(name_u, name, len);
name_u[len] = '\0';
- vim_strup((char_u *)name_u);
+ vim_strup(name_u);
// map_get(..., int) returns 0 when no key is present, which is
// the expected value for missing highlight group.
@@ -1690,10 +1802,10 @@ int syn_name2id_len(const char *name, size_t len)
/// Lookup a highlight group name and return its attributes.
/// Return zero if not found.
-int syn_name2attr(const char_u *name)
+int syn_name2attr(const char *name)
FUNC_ATTR_NONNULL_ALL
{
- int id = syn_name2id((char *)name);
+ int id = syn_name2id(name);
if (id != 0) {
return syn_id2attr(id);
@@ -1701,7 +1813,7 @@ int syn_name2attr(const char_u *name)
return 0;
}
-/// Return TRUE if highlight group "name" exists.
+/// Return true if highlight group "name" exists.
int highlight_exists(const char *name)
{
return syn_name2id(name) > 0;
@@ -1709,10 +1821,10 @@ int highlight_exists(const char *name)
/// Return the name of highlight group "id".
/// When not a valid ID return an empty string.
-char_u *syn_id2name(int id)
+char *syn_id2name(int id)
{
if (id <= 0 || id > highlight_ga.ga_len) {
- return (char_u *)"";
+ return "";
}
return hl_table[id - 1].sg_name;
}
@@ -1750,11 +1862,19 @@ static int syn_add_group(const char *name, size_t len)
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.
+ } else if (!ASCII_ISALNUM(c) && c != '_' && c != '.' && c != '@') {
+ // '.' and '@' are allowed characters for use with treesitter capture names.
msg_source(HL_ATTR(HLF_W));
- msg(_("W18: Invalid character in group name"));
- break;
+ emsg(_(e_highlight_group_name_invalid_char));
+ return 0;
+ }
+ }
+
+ int scoped_parent = 0;
+ if (len > 1 && name[0] == '@') {
+ char *delim = xmemrchr(name, '.', len);
+ if (delim) {
+ scoped_parent = syn_check_group(name, (size_t)(delim - name));
}
}
@@ -1774,7 +1894,7 @@ static int syn_add_group(const char *name, size_t len)
// Append another syntax_highlight entry.
HlGroup *hlgp = GA_APPEND_VIA_PTR(HlGroup, &highlight_ga);
CLEAR_POINTER(hlgp);
- hlgp->sg_name = (char_u *)arena_memdupz(&highlight_arena, name, len);
+ hlgp->sg_name = arena_memdupz(&highlight_arena, name, len);
hlgp->sg_rgb_bg = -1;
hlgp->sg_rgb_fg = -1;
hlgp->sg_rgb_sp = -1;
@@ -1783,7 +1903,10 @@ static int syn_add_group(const char *name, size_t len)
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);
+ hlgp->sg_parent = scoped_parent;
+ // will get set to false by caller if settings are added
+ hlgp->sg_cleared = true;
+ vim_strup(hlgp->sg_name_u);
int id = highlight_ga.ga_len; // ID is index plus one
@@ -1844,10 +1967,13 @@ int syn_ns_get_final_id(int *ns_id, int hl_id)
continue;
}
- if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len) {
+ if (sgp->sg_link > 0 && sgp->sg_link <= highlight_ga.ga_len) {
+ hl_id = sgp->sg_link;
+ } else if (sgp->sg_cleared && sgp->sg_parent > 0) {
+ hl_id = sgp->sg_parent;
+ } else {
break;
}
- hl_id = sgp->sg_link;
}
return hl_id;
@@ -1920,17 +2046,15 @@ static void combine_stl_hlt(int id, int id_S, int id_alt, int hlcnt, int i, int
/// 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]));
+ int id = syn_check_group(hlf_names[hlf], strlen(hlf_names[hlf]));
if (id == 0) {
abort();
}
@@ -1957,7 +2081,7 @@ void highlight_changed(void)
}
}
- // sentinel value. used when no hightlight namespace is active
+ // sentinel value. used when no highlight namespace is active
highlight_attr[HLF_COUNT] = 0;
//
@@ -1969,7 +2093,7 @@ void highlight_changed(void)
// 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;
+ int hlcnt = highlight_ga.ga_len;
if (id_S == -1) {
// Make sure id_S is always valid to simplify code below. Use the last entry
CLEAR_POINTER(&hl_table[hlcnt + 9]);
@@ -1977,7 +2101,7 @@ void highlight_changed(void)
}
for (int i = 0; i < 9; i++) {
snprintf(userhl, sizeof(userhl), "User%d", i + 1);
- id = syn_name2id(userhl);
+ int id = syn_name2id(userhl);
if (id == 0) {
highlight_user[i] = 0;
highlight_stlnc[i] = 0;
@@ -1987,6 +2111,8 @@ void highlight_changed(void)
}
}
highlight_ga.ga_len = hlcnt;
+
+ decor_provider_invalidate_hl();
}
/// Handle command line completion for :highlight command.
@@ -1998,47 +2124,53 @@ void set_context_in_highlight_cmd(expand_T *xp, const char *arg)
include_link = 2;
include_default = 1;
+ if (*arg == NUL) {
+ return;
+ }
+
// (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;
- }
- }
+ const char *p = (const char *)skiptowhite(arg);
+ if (*p == NUL) {
+ return;
+ }
+
+ // 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(arg);
+ }
+ if (*p == NUL) {
+ return;
+ }
+
+ // 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(xp->xp_pattern);
+ if (*p != NUL) { // past first group name
+ xp->xp_pattern = skipwhite(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;) {
+ for (int i = 10; --i >= 0;) {
highlight_list_two(i, HL_ATTR(HLF_D));
}
- for (i = 40; --i >= 0;) {
+ for (int i = 40; --i >= 0;) {
highlight_list_two(99, 0);
}
}
@@ -2783,9 +2915,9 @@ color_name_table_T color_name_table[] = {
/// 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) {
+ if (name[0] == '#' && isxdigit((uint8_t)name[1]) && isxdigit((uint8_t)name[2])
+ && isxdigit((uint8_t)name[3]) && isxdigit((uint8_t)name[4]) && isxdigit((uint8_t)name[5])
+ && isxdigit((uint8_t)name[6]) && name[7] == NUL) {
// rgb hex string
*idx = kColorIdxHex;
return (RgbValue)strtol((char *)(name + 1), NULL, 16);
@@ -2809,7 +2941,6 @@ RgbValue name_to_color(const char *name, int *idx)
} else { // found match
*idx = m;
return color_name_table[m].color;
- break;
}
}
diff --git a/src/nvim/iconv.h b/src/nvim/iconv.h
index 509f83c415..f5f3f25786 100644
--- a/src/nvim/iconv.h
+++ b/src/nvim/iconv.h
@@ -1,20 +1,18 @@
#ifndef NVIM_ICONV_H
#define NVIM_ICONV_H
-#include "auto/config.h"
+#include <errno.h>
+#include <iconv.h>
-#ifdef HAVE_ICONV
-# include <errno.h>
-# include <iconv.h>
+#include "auto/config.h"
// define some missing constants if necessary
-# ifndef EILSEQ
-# define EILSEQ 123
-# endif
-# define ICONV_ERRNO errno
-# define ICONV_E2BIG E2BIG
-# define ICONV_EINVAL EINVAL
-# define ICONV_EILSEQ EILSEQ
+#ifndef EILSEQ
+# define EILSEQ 123
#endif
+#define ICONV_ERRNO errno
+#define ICONV_E2BIG E2BIG
+#define ICONV_EINVAL EINVAL
+#define ICONV_EILSEQ EILSEQ
#endif // NVIM_ICONV_H
diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c
deleted file mode 100644
index 689d1fce0d..0000000000
--- a/src/nvim/if_cscope.c
+++ /dev/null
@@ -1,2052 +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
-
-/*
- * CSCOPE support for Vim added by Andy Kahn <kahn@zk3.dec.com>
- * Ported to Win32 by Sergey Khorev <sergey.khorev@gmail.com>
- *
- * The basic idea/structure of cscope for Vim was borrowed from Nvi. There
- * might be a few lines of code that look similar to what Nvi has.
- */
-
-#include <assert.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdbool.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include "nvim/ascii.h"
-#include "nvim/autocmd.h"
-#include "nvim/buffer.h"
-#include "nvim/charset.h"
-#include "nvim/eval.h"
-#include "nvim/event/stream.h"
-#include "nvim/ex_eval.h"
-#include "nvim/fileio.h"
-#include "nvim/if_cscope.h"
-#include "nvim/memory.h"
-#include "nvim/message.h"
-#include "nvim/os/input.h"
-#include "nvim/os/os.h"
-#include "nvim/os/time.h"
-#include "nvim/path.h"
-#include "nvim/quickfix.h"
-#include "nvim/strings.h"
-#include "nvim/tag.h"
-#include "nvim/window.h"
-#if defined(UNIX)
-# include <sys/wait.h>
-#endif
-#include "nvim/if_cscope_defs.h"
-
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "if_cscope.c.generated.h"
-#endif
-
-static csinfo_T *csinfo = NULL;
-static size_t csinfo_size = 0; // number of items allocated in csinfo[]
-
-static int eap_arg_len; // length of eap->arg, set in cs_lookup_cmd()
-static cscmd_T cs_cmds[] =
-{
- { "add", cs_add,
- N_("Add a new database"), "add file|dir [pre-path] [flags]", 0 },
- { "find", cs_find,
- N_("Query for a pattern"), "find a|c|d|e|f|g|i|s|t name", 1 },
- { "help", cs_help,
- N_("Show this message"), "help", 0 },
- { "kill", cs_kill,
- N_("Kill a connection"), "kill #", 0 },
- { "reset", cs_reset,
- N_("Reinit all connections"), "reset", 0 },
- { "show", cs_show,
- N_("Show connections"), "show", 0 },
- { NULL, NULL, NULL, NULL, 0 }
-};
-
-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
- EXP_CSCOPE_FIND, // expand ":cscope find" arguments
- EXP_CSCOPE_KILL, // expand ":cscope kill" arguments
-} expand_what;
-
-/*
- * Function given to ExpandGeneric() to obtain the cscope command
- * expansion.
- */
-char *get_cscope_name(expand_T *xp, int idx)
-{
- int current_idx;
-
- switch (expand_what) {
- case EXP_CSCOPE_SUBCMD:
- // Complete with sub-commands of ":cscope":
- // add, find, help, kill, reset, show
- 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
- int i;
- for (i = 0, current_idx = 0; cs_cmds[i].name != NULL; i++) {
- if (cs_cmds[i].cansplit) {
- if (current_idx++ == idx) {
- break;
- }
- }
- }
- return cs_cmds[i].name;
- }
- case EXP_CSCOPE_FIND: {
- const char *query_type[] =
- {
- "a", "c", "d", "e", "f", "g", "i", "s", "t", NULL
- };
-
- // Complete with query type of ":cscope find {query_type}".
- // {query_type} can be letters (c, d, ... a) or numbers (0, 1,
- // ..., 9) but only complete with letters, since numbers are
- // redundant.
- return (char *)query_type[idx];
- }
- case EXP_CSCOPE_KILL: {
- static char connection[5];
-
- // ":cscope kill" accepts connection numbers or partial names of
- // the pathname of the cscope database as argument. Only complete
- // with connection numbers. -1 can also be used to kill all
- // connections.
- size_t i;
- for (i = 0, current_idx = 0; i < csinfo_size; i++) {
- if (csinfo[i].fname == NULL) {
- continue;
- }
- if (current_idx++ == idx) {
- vim_snprintf(connection, sizeof(connection), "%zu", i);
- return connection;
- }
- }
- return (current_idx == idx && idx > 0) ? "-1" : NULL;
- }
- default:
- return NULL;
- }
-}
-
-/*
- * Handle command line completion for :cscope command.
- */
-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 *)arg;
- expand_what = ((cmdidx == CMD_scscope)
- ? EXP_SCSCOPE_SUBCMD : EXP_CSCOPE_SUBCMD);
-
- // (part of) subcommand already typed
- if (*arg != NUL) {
- const char *p = (const char *)skiptowhite((const char_u *)arg);
- if (*p != NUL) { // Past first word.
- 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;
- } else if (STRNICMP(arg, "kill", p - arg) == 0) {
- expand_what = EXP_CSCOPE_KILL;
- } else if (STRNICMP(arg, "find", p - arg) == 0) {
- expand_what = EXP_CSCOPE_FIND;
- } else {
- xp->xp_context = EXPAND_NOTHING;
- }
- }
- }
-}
-
-/// Find the command, print help if invalid, and then call the corresponding
-/// command function.
-///
-/// @param make_split whether to split window
-static void do_cscope_general(exarg_T *eap, int make_split)
-{
- cscmd_T *cmdp;
-
- if ((cmdp = cs_lookup_cmd(eap)) == NULL) {
- cs_help(eap);
- return;
- }
-
- if (make_split) {
- if (!cmdp->cansplit) {
- (void)msg_puts(_("This cscope command does not support splitting the window.\n"));
- return;
- }
- postponed_split = -1;
- postponed_split_flags = cmdmod.cmod_split;
- postponed_split_tab = cmdmod.cmod_tab;
- }
-
- cmdp->func(eap);
-
- postponed_split_flags = 0;
- postponed_split_tab = 0;
-}
-
-/// Implementation of ":cscope" and ":lcscope"
-void ex_cscope(exarg_T *eap)
-{
- do_cscope_general(eap, FALSE);
-}
-
-/// Implementation of ":scscope". Same as ex_cscope(), but splits window, too.
-void ex_scscope(exarg_T *eap)
-{
- do_cscope_general(eap, TRUE);
-}
-
-/// Implementation of ":cstag"
-void ex_cstag(exarg_T *eap)
-{
- int ret = FALSE;
-
- if (*eap->arg == NUL) {
- (void)emsg(_("E562: Usage: cstag <ident>"));
- return;
- }
-
- switch (p_csto) {
- case 0:
- if (cs_check_for_connections()) {
- ret = cs_find_common("g", eap->arg, eap->forceit, false,
- false, (char_u *)(*eap->cmdlinep));
- if (ret == false) {
- cs_free_tags();
- if (msg_col) {
- msg_putchar('\n');
- }
-
- if (cs_check_for_tags()) {
- ret = do_tag((char_u *)eap->arg, DT_JUMP, 0, eap->forceit, false);
- }
- }
- } else if (cs_check_for_tags()) {
- ret = do_tag((char_u *)eap->arg, DT_JUMP, 0, eap->forceit, false);
- }
- break;
- case 1:
- if (cs_check_for_tags()) {
- 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", 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", eap->arg, eap->forceit, false,
- false, (char_u *)(*eap->cmdlinep));
- if (ret == false) {
- cs_free_tags();
- }
- }
- break;
- default:
- break;
- }
-
- if (!ret) {
- (void)emsg(_("E257: cstag: tag not found"));
- g_do_tagpreview = 0;
- }
-}
-
-/// This simulates a vim_fgets(), but for cscope, returns the next line
-/// from the cscope output. should only be called from find_tags()
-///
-/// @return true if eof, FALSE otherwise
-bool cs_fgets(char_u *buf, int size)
- FUNC_ATTR_NONNULL_ALL
-{
- char *p;
-
- if ((p = cs_manage_matches(NULL, NULL, 0, Get)) == NULL) {
- return true;
- }
- STRLCPY(buf, p, size);
-
- return false;
-}
-
-/// Called only from do_tag(), when popping the tag stack.
-void cs_free_tags(void)
-{
- cs_manage_matches(NULL, NULL, 0, Free);
-}
-
-/// Called from do_tag().
-void cs_print_tags(void)
-{
- cs_manage_matches(NULL, NULL, 0, Print);
-}
-
-/*
- * "cscope_connection([{num} , {dbpath} [, {prepend}]])" function
- *
- * Checks for the existence of a |cscope| connection. If no
- * parameters are specified, then the function returns:
- *
- * 0, if cscope was not available (not compiled in), or if there
- * are no cscope connections; or
- * 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!
- */
-bool cs_connection(int num, char_u *dbpath, char_u *ppath)
-{
- if (num < 0 || num > 4 || (num > 0 && !dbpath)) {
- return false;
- }
-
- for (size_t i = 0; i < csinfo_size; i++) {
- if (!csinfo[i].fname) {
- continue;
- }
- if (num == 0) {
- return true;
- }
- switch (num) {
- case 1:
- if (strstr(csinfo[i].fname, (char *)dbpath)) {
- return true;
- }
- break;
- case 2:
- if (strcmp(csinfo[i].fname, (char *)dbpath) == 0) {
- return true;
- }
- break;
- case 3:
- if (strstr(csinfo[i].fname, (char *)dbpath)
- && ((!ppath && !csinfo[i].ppath)
- || (ppath
- && csinfo[i].ppath
- && strstr(csinfo[i].ppath, (char *)ppath)))) {
- return true;
- }
- break;
- case 4:
- if ((strcmp(csinfo[i].fname, (char *)dbpath) == 0)
- && ((!ppath && !csinfo[i].ppath)
- || (ppath
- && csinfo[i].ppath
- && (strcmp(csinfo[i].ppath, (char *)ppath) == 0)))) {
- return true;
- }
- break;
- }
- }
-
- return false;
-} // cs_connection
-
-/*
- * PRIVATE functions
- ****************************************************************************/
-
-/// Add cscope database or a directory name (to look for cscope.out)
-/// to the cscope connection list.
-static int cs_add(exarg_T *eap)
-{
- char *fname, *ppath, *flags = NULL;
-
- if ((fname = strtok((char *)NULL, (const char *)" ")) == NULL) {
- cs_usage_msg(Add);
- return CSCOPE_FAILURE;
- }
- if ((ppath = strtok((char *)NULL, (const char *)" ")) != NULL) {
- flags = strtok((char *)NULL, (const char *)" ");
- }
-
- return cs_add_common(fname, ppath, flags);
-}
-
-static void cs_stat_emsg(char *fname)
-{
- int err = errno;
- (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.
-///
-/// @param arg1 filename - may contain environment variables
-/// @param arg2 prepend path - may contain environment variables
-static int cs_add_common(char *arg1, char *arg2, char *flags)
-{
- char *fname = NULL;
- char *fname2 = NULL;
- char *ppath = NULL;
- size_t usedlen = 0;
- char *fbuf = NULL;
-
- // get the filename (arg1), expand it, and try to stat it
- fname = xmalloc(MAXPATHL + 1);
-
- expand_env((char_u *)arg1, (char_u *)fname, MAXPATHL);
- size_t len = STRLEN(fname);
- fbuf = fname;
- (void)modify_fname(":p", false, &usedlen, &fname, &fbuf, &len);
- if (fname == NULL) {
- goto add_err;
- }
- fname = xstrnsave(fname, len);
- xfree(fbuf);
- FileInfo file_info;
- bool file_info_ok = os_fileinfo(fname, &file_info);
- if (!file_info_ok) {
-staterr:
- if (p_csverbose) {
- cs_stat_emsg(fname);
- }
- goto add_err;
- }
-
- // get the prepend path (arg2), expand it, and see if it exists
- if (arg2 != NULL) {
- ppath = xmalloc(MAXPATHL + 1);
- expand_env((char_u *)arg2, (char_u *)ppath, MAXPATHL);
- if (!os_path_exists((char_u *)ppath)) {
- goto staterr;
- }
- }
-
- int i;
- // if filename is a directory, append the cscope database name to it
- if (S_ISDIR(file_info.stat.st_mode)) {
- fname2 = (char *)xmalloc(strlen(CSCOPE_DBFILE) + strlen(fname) + 2);
-
- while (fname[strlen(fname) - 1] == '/'
- ) {
- fname[strlen(fname) - 1] = '\0';
- if (fname[0] == '\0') {
- break;
- }
- }
- if (fname[0] == '\0') {
- (void)sprintf(fname2, "/%s", CSCOPE_DBFILE);
- } else {
- (void)sprintf(fname2, "%s/%s", fname, CSCOPE_DBFILE);
- }
-
- file_info_ok = os_fileinfo(fname2, &file_info);
- if (!file_info_ok) {
- if (p_csverbose) {
- cs_stat_emsg(fname2);
- }
- goto add_err;
- }
-
- i = cs_insert_filelist(fname2, ppath, flags, &file_info);
- } else if (S_ISREG(file_info.stat.st_mode) || S_ISLNK(file_info.stat.st_mode)) {
- i = cs_insert_filelist(fname, ppath, flags, &file_info);
- } else {
- if (p_csverbose) {
- (void)semsg(_("E564: %s is not a directory or a valid cscope database"),
- fname);
- }
- goto add_err;
- }
-
- if (i != -1) {
- assert(i >= 0);
- if (cs_create_connection((size_t)i) == CSCOPE_FAILURE
- || cs_read_prompt((size_t)i) == CSCOPE_FAILURE) {
- cs_release_csp((size_t)i, true);
- goto add_err;
- }
-
- if (p_csverbose) {
- msg_clr_eos();
- (void)smsg_attr(HL_ATTR(HLF_R),
- _("Added cscope database %s"),
- csinfo[i].fname);
- }
- }
-
- xfree(fname);
- xfree(fname2);
- xfree(ppath);
- return CSCOPE_SUCCESS;
-
-add_err:
- xfree(fname2);
- xfree(fname);
- xfree(ppath);
- return CSCOPE_FAILURE;
-}
-
-static bool cs_check_for_connections(void)
-{
- return cs_cnt_connections() > 0;
-}
-
-static int cs_check_for_tags(void)
-{
- return p_tags[0] != NUL && curbuf->b_p_tags != NULL;
-}
-
-/// Count the number of cscope connections.
-static size_t cs_cnt_connections(void)
-{
- size_t cnt = 0;
-
- for (size_t i = 0; i < csinfo_size; i++) {
- if (csinfo[i].fname != NULL) {
- cnt++;
- }
- }
- return cnt;
-}
-
-/// @param idx connection index
-static void cs_reading_emsg(size_t idx)
-{
- semsg(_("E262: error reading cscope connection %" PRIu64), (uint64_t)idx);
-}
-
-#define CSREAD_BUFSIZE 2048
-/// Count the number of matches for a given cscope connection.
-static int cs_cnt_matches(size_t idx)
-{
- char *stok;
- int nlines = 0;
-
- char *buf = xmalloc(CSREAD_BUFSIZE);
- for (;;) {
- errno = 0;
- if (!fgets(buf, CSREAD_BUFSIZE, csinfo[idx].fr_fp)) {
- if (errno == EINTR) {
- continue;
- }
-
- if (feof(csinfo[idx].fr_fp)) {
- errno = EIO;
- }
-
- cs_reading_emsg(idx);
-
- xfree(buf);
- return CSCOPE_FAILURE;
- }
-
- // If the database is out of date, or there's some other problem,
- // cscope will output error messages before the number-of-lines output.
- // Display/discard any output that doesn't match what we want.
- // Accept "\S*cscope: X lines", also matches "mlcscope".
- // Bail out for the "Unable to search" error.
- if (strstr((const char *)buf, "Unable to search database") != NULL) {
- break;
- }
- if ((stok = strtok(buf, (const char *)" ")) == NULL) {
- continue;
- }
- if (strstr((const char *)stok, "cscope:") == NULL) {
- continue;
- }
-
- if ((stok = strtok(NULL, (const char *)" ")) == NULL) {
- continue;
- }
- nlines = atoi(stok);
- if (nlines < 0) {
- nlines = 0;
- break;
- }
-
- if ((stok = strtok(NULL, (const char *)" ")) == NULL) {
- continue;
- }
- if (strncmp(stok, "lines", 5)) {
- continue;
- }
-
- break;
- }
-
- xfree(buf);
- return nlines;
-}
-
-/// Creates the actual cscope command query from what the user entered.
-static char *cs_create_cmd(char *csoption, char *pattern)
-{
- char *cmd;
- short search;
- char *pat;
-
- switch (csoption[0]) {
- case '0':
- case 's':
- search = 0;
- break;
- case '1':
- case 'g':
- search = 1;
- break;
- case '2':
- case 'd':
- search = 2;
- break;
- case '3':
- case 'c':
- search = 3;
- break;
- case '4':
- case 't':
- search = 4;
- break;
- case '6':
- case 'e':
- search = 6;
- break;
- case '7':
- case 'f':
- search = 7;
- break;
- case '8':
- case 'i':
- search = 8;
- break;
- case '9':
- case 'a':
- search = 9;
- break;
- default:
- (void)emsg(_("E561: unknown cscope search type"));
- cs_usage_msg(Find);
- return NULL;
- }
-
- // Skip white space before the patter, except for text and pattern search,
- // they may want to use the leading white space.
- pat = pattern;
- if (search != 4 && search != 6) {
- while (ascii_iswhite(*pat)) {
- pat++;
- }
- }
-
- cmd = xmalloc(strlen(pat) + 2);
-
- (void)sprintf(cmd, "%d%s", search, pat);
-
- 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)
-{
-#ifdef UNIX
- int to_cs[2], from_cs[2];
-#endif
- char *prog, *cmd, *ppath = NULL;
-
-#if defined(UNIX)
- /*
- * Cscope reads from to_cs[0] and writes to from_cs[1]; vi reads from
- * from_cs[0] and writes to to_cs[1].
- */
- to_cs[0] = to_cs[1] = from_cs[0] = from_cs[1] = -1;
- if (pipe(to_cs) < 0 || pipe(from_cs) < 0) {
- (void)emsg(_("E566: Could not create cscope pipes"));
-err_closing:
- if (to_cs[0] != -1) {
- (void)close(to_cs[0]);
- }
- if (to_cs[1] != -1) {
- (void)close(to_cs[1]);
- }
- if (from_cs[0] != -1) {
- (void)close(from_cs[0]);
- }
- if (from_cs[1] != -1) {
- (void)close(from_cs[1]);
- }
- return CSCOPE_FAILURE;
- }
-
- switch (csinfo[i].pid = fork()) {
- case -1:
- (void)emsg(_("E622: Could not fork for cscope"));
- goto err_closing;
- case 0: // child: run cscope.
- if (dup2(to_cs[0], STDIN_FILENO) == -1) {
- PERROR("cs_create_connection 1");
- }
- if (dup2(from_cs[1], STDOUT_FILENO) == -1) {
- PERROR("cs_create_connection 2");
- }
- if (dup2(from_cs[1], STDERR_FILENO) == -1) {
- PERROR("cs_create_connection 3");
- }
-
- // close unused
- (void)close(to_cs[1]);
- (void)close(from_cs[0]);
-#else
- // Create pipes to communicate with cscope
- int fd;
- SECURITY_ATTRIBUTES sa;
- PROCESS_INFORMATION pi;
- BOOL pipe_stdin = FALSE, pipe_stdout = FALSE; // NOLINT(readability/bool)
- STARTUPINFO si;
- HANDLE stdin_rd, stdout_rd;
- HANDLE stdout_wr, stdin_wr;
- BOOL created;
-
- sa.nLength = sizeof(SECURITY_ATTRIBUTES);
- sa.bInheritHandle = TRUE;
- sa.lpSecurityDescriptor = NULL;
-
- if (!(pipe_stdin = CreatePipe(&stdin_rd, &stdin_wr, &sa, 0))
- || !(pipe_stdout = CreatePipe(&stdout_rd, &stdout_wr, &sa, 0))) {
- (void)emsg(_("E566: Could not create cscope pipes"));
-err_closing:
- if (pipe_stdin) {
- CloseHandle(stdin_rd);
- CloseHandle(stdin_wr);
- }
- if (pipe_stdout) {
- CloseHandle(stdout_rd);
- CloseHandle(stdout_wr);
- }
- return CSCOPE_FAILURE;
- }
-#endif
- // expand the cscope exec for env var's
- prog = xmalloc(MAXPATHL + 1);
- expand_env(p_csprg, (char_u *)prog, MAXPATHL);
-
- // alloc space to hold the cscope command
- size_t len = strlen(prog) + strlen(csinfo[i].fname) + 32;
- if (csinfo[i].ppath) {
- // expand the prepend path for env var's
- ppath = xmalloc(MAXPATHL + 1);
- expand_env((char_u *)csinfo[i].ppath, (char_u *)ppath, MAXPATHL);
-
- len += strlen(ppath);
- }
-
- if (csinfo[i].flags) {
- len += strlen(csinfo[i].flags);
- }
-
- cmd = xmalloc(len);
-
- // run the cscope command; is there execl for non-unix systems?
-#if defined(UNIX)
- (void)snprintf(cmd, len, "exec %s -dl -f %s", prog, csinfo[i].fname);
-#else
- // WIN32
- (void)snprintf(cmd, len, "%s -dl -f %s", prog, csinfo[i].fname);
-#endif
- if (csinfo[i].ppath != NULL) {
- (void)strcat(cmd, " -P");
- (void)strcat(cmd, csinfo[i].ppath);
- }
- if (csinfo[i].flags != NULL) {
- (void)strcat(cmd, " ");
- (void)strcat(cmd, csinfo[i].flags);
- }
-#ifdef UNIX
- // on Win32 we still need prog
- xfree(prog);
-#endif
- xfree(ppath);
-
-#if defined(UNIX)
-# if defined(HAVE_SETSID) || defined(HAVE_SETPGID)
- // Change our process group to avoid cscope receiving SIGWINCH.
-# if defined(HAVE_SETSID)
- (void)setsid();
-# else
- if (setpgid(0, 0) == -1) {
- PERROR(_("cs_create_connection setpgid failed"));
- }
-# endif
-# endif
- if (execl("/bin/sh", "sh", "-c", cmd, (char *)NULL) == -1) {
- PERROR(_("cs_create_connection exec failed"));
- }
-
- exit(127);
- // NOTREACHED
- default: // parent.
- // Save the file descriptors for later duplication, and
- // reopen as streams.
- if ((csinfo[i].to_fp = fdopen(to_cs[1], "w")) == NULL) {
- PERROR(_("cs_create_connection: fdopen for to_fp failed"));
- }
- if ((csinfo[i].fr_fp = fdopen(from_cs[0], "r")) == NULL) {
- PERROR(_("cs_create_connection: fdopen for fr_fp failed"));
- }
-
- // close unused
- (void)close(to_cs[0]);
- (void)close(from_cs[1]);
-
- break;
- }
-
-#else
- // WIN32
- // Create a new process to run cscope and use pipes to talk with it
- GetStartupInfo(&si);
- si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
- si.wShowWindow = SW_HIDE; // Hide child application window
- si.hStdOutput = stdout_wr;
- si.hStdError = stdout_wr;
- si.hStdInput = stdin_rd;
- created = CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NEW_CONSOLE,
- NULL, NULL, &si, &pi);
- xfree(prog);
- xfree(cmd);
-
- if (!created) {
- PERROR(_("cs_create_connection exec failed"));
- (void)emsg(_("E623: Could not spawn cscope process"));
- goto err_closing;
- }
- // else
- csinfo[i].pid = pi.dwProcessId;
- csinfo[i].hProc = pi.hProcess;
- CloseHandle(pi.hThread);
-
- // TODO(neovim): tidy up after failure to create files on pipe handles.
- if (((fd = _open_osfhandle((intptr_t)stdin_wr, _O_TEXT|_O_APPEND)) < 0)
- || ((csinfo[i].to_fp = _fdopen(fd, "w")) == NULL)) {
- PERROR(_("cs_create_connection: fdopen for to_fp failed"));
- }
- if (((fd = _open_osfhandle((intptr_t)stdout_rd, _O_TEXT|_O_RDONLY)) < 0)
- || ((csinfo[i].fr_fp = _fdopen(fd, "r")) == NULL)) {
- PERROR(_("cs_create_connection: fdopen for fr_fp failed"));
- }
- // Close handles for file descriptors inherited by the cscope process.
- CloseHandle(stdin_rd);
- CloseHandle(stdout_wr);
-
-#endif // !UNIX
-
- 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.
-///
-/// @return TRUE if we jump to a tag or abort, FALSE if not.
-static int cs_find(exarg_T *eap)
-{
- char *opt, *pat;
-
- if (cs_check_for_connections() == false) {
- (void)emsg(_("E567: no cscope connections"));
- return false;
- }
-
- if ((opt = strtok((char *)NULL, (const char *)" ")) == NULL) {
- cs_usage_msg(Find);
- return false;
- }
-
- pat = opt + strlen(opt) + 1;
- if (pat >= eap->arg + eap_arg_len) {
- cs_usage_msg(Find);
- return false;
- }
-
- /*
- * Let's replace the NULs written by strtok() with spaces - we need the
- * spaces to correctly display the quickfix/location list window's title.
- */
- for (int i = 0; i < eap_arg_len; ++i) {
- if (NUL == eap->arg[i]) {
- eap->arg[i] = ' ';
- }
- }
-
- return cs_find_common(opt, pat, eap->forceit, true,
- 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)
-{
- char *cmd;
- int *nummatches;
- size_t totmatches;
- char cmdletter;
- char *qfpos;
-
- // get cmd letter
- switch (opt[0]) {
- case '0':
- cmdletter = 's';
- break;
- case '1':
- cmdletter = 'g';
- break;
- case '2':
- cmdletter = 'd';
- break;
- case '3':
- cmdletter = 'c';
- break;
- case '4':
- cmdletter = 't';
- break;
- case '6':
- cmdletter = 'e';
- break;
- case '7':
- cmdletter = 'f';
- break;
- case '8':
- cmdletter = 'i';
- break;
- case '9':
- cmdletter = 'a';
- break;
- default:
- cmdletter = opt[0];
- }
-
- qfpos = vim_strchr((char *)p_csqf, cmdletter);
- if (qfpos != NULL) {
- qfpos++;
- // next symbol must be + or -
- if (strchr(CSQF_FLAGS, *qfpos) == NULL) {
- (void)semsg(_("E469: invalid cscopequickfix flag %c for %c"), *qfpos, *(qfpos - 1));
- return false;
- }
-
- if (*qfpos != '0'
- && apply_autocmds(EVENT_QUICKFIXCMDPRE, "cscope", curbuf->b_fname, true, curbuf)) {
- if (aborting()) {
- return false;
- }
- }
- }
-
- // create the actual command to send to cscope
- cmd = cs_create_cmd(opt, pat);
- if (cmd == NULL) {
- return false;
- }
-
- nummatches = xmalloc(sizeof(int) * csinfo_size);
-
- // Send query to all open connections, then count the total number
- // of matches so we can alloc all in one swell foop.
- for (size_t i = 0; i < csinfo_size; i++) {
- nummatches[i] = 0;
- }
- totmatches = 0;
- for (size_t i = 0; i < csinfo_size; i++) {
- if (csinfo[i].fname == NULL || csinfo[i].to_fp == NULL) {
- continue;
- }
-
- // send cmd to cscope
- (void)fprintf(csinfo[i].to_fp, "%s\n", cmd);
- (void)fflush(csinfo[i].to_fp);
-
- nummatches[i] = cs_cnt_matches(i);
-
- if (nummatches[i] > -1) {
- totmatches += (size_t)nummatches[i];
- }
-
- if (nummatches[i] == 0) {
- (void)cs_read_prompt(i);
- }
- }
- xfree(cmd);
-
- if (totmatches == 0) {
- if (verbose) {
- (void)semsg(_("E259: no matches found for cscope query %s of %s"), opt, pat);
- }
- xfree(nummatches);
- return false;
- }
-
- if (qfpos != NULL && *qfpos != '0') {
- // Fill error list.
- FILE *f;
- char_u *tmp = vim_tempname();
- qf_info_T *qi = NULL;
- win_T *wp = NULL;
-
- f = os_fopen((char *)tmp, "w");
- if (f == NULL) {
- semsg(_(e_notopen), tmp);
- } else {
- cs_file_results(f, nummatches);
- fclose(f);
- if (use_ll) { // Use location list
- wp = curwin;
- }
- // '-' starts a new error list
- 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);
- RESET_BINDING(curwin);
- postponed_split = 0;
- }
-
- apply_autocmds(EVENT_QUICKFIXCMDPOST, "cscope", curbuf->b_fname, true, curbuf);
- if (use_ll) {
- /*
- * In the location list window, use the displayed location
- * list. Otherwise, use the location list for the window.
- */
- qi = (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL)
- ? wp->w_llist_ref : wp->w_llist;
- }
- qf_jump(qi, 0, 0, forceit);
- }
- }
- os_remove((char *)tmp);
- xfree(tmp);
- xfree(nummatches);
- return true;
- } else {
- char **matches = NULL, **contexts = NULL;
- size_t matched = 0;
-
- // read output
- cs_fill_results(pat, totmatches, nummatches, &matches, &contexts, &matched);
- xfree(nummatches);
- if (matches == NULL) {
- return false;
- }
-
- (void)cs_manage_matches(matches, contexts, matched, Store);
-
- return do_tag((char_u *)pat, DT_CSCOPE, 0, forceit, verbose);
- }
-}
-
-/// Print help.
-static int cs_help(exarg_T *eap)
-{
- cscmd_T *cmdp = cs_cmds;
-
- (void)msg_puts(_("cscope commands:\n"));
- while (cmdp->name != NULL) {
- char *help = _(cmdp->help);
- int space_cnt = 30 - vim_strsize(help);
-
- // Use %*s rather than %30s to ensure proper alignment in utf-8
- if (space_cnt < 0) {
- space_cnt = 0;
- }
- (void)smsg(_("%-5s: %s%*s (Usage: %s)"),
- cmdp->name,
- help, space_cnt, " ",
- cmdp->usage);
- if (strcmp(cmdp->name, "find") == 0) {
- msg_puts(_("\n"
- " a: Find assignments to this symbol\n"
- " c: Find functions calling this function\n"
- " d: Find functions called by this function\n"
- " e: Find this egrep pattern\n"
- " f: Find this file\n"
- " g: Find this definition\n"
- " i: Find files #including this file\n"
- " s: Find this C symbol\n"
- " t: Find this text string\n"));
- }
-
- cmdp++;
- }
-
- wait_return(TRUE);
- return CSCOPE_SUCCESS;
-}
-
-static void clear_csinfo(size_t i)
-{
- csinfo[i].fname = NULL;
- csinfo[i].ppath = NULL;
- csinfo[i].flags = NULL;
- csinfo[i].file_id = FILE_ID_EMPTY;
- csinfo[i].pid = 0;
- csinfo[i].fr_fp = NULL;
- csinfo[i].to_fp = NULL;
-}
-
-/// Insert a new cscope database filename into the filelist.
-static int cs_insert_filelist(char *fname, char *ppath, char *flags, FileInfo *file_info)
-{
- size_t i = 0;
- bool empty_found = false;
-
- for (size_t j = 0; j < csinfo_size; j++) {
- if (csinfo[j].fname != NULL
- && os_fileid_equal_fileinfo(&(csinfo[j].file_id), file_info)) {
- if (p_csverbose) {
- (void)emsg(_("E568: duplicate cscope database not added"));
- }
- return CSCOPE_FAILURE;
- }
-
- if (csinfo[j].fname == NULL && !empty_found) {
- i = j; // remember first empty entry
- empty_found = true;
- }
- }
-
- if (!empty_found) {
- i = csinfo_size;
- if (csinfo_size == 0) {
- // First time allocation: allocate only 1 connection. It should
- // be enough for most users. If more is needed, csinfo will be
- // reallocated.
- csinfo_size = 1;
- csinfo = xcalloc(1, sizeof(csinfo_T));
- } else {
- // Reallocate space for more connections.
- csinfo_size *= 2;
- csinfo = xrealloc(csinfo, sizeof(csinfo_T)*csinfo_size);
- }
- for (size_t j = csinfo_size/2; j < csinfo_size; j++) {
- clear_csinfo(j);
- }
- }
-
- csinfo[i].fname = xmalloc(strlen(fname) + 1);
-
- (void)strcpy(csinfo[i].fname, (const char *)fname);
-
- if (ppath != NULL) {
- csinfo[i].ppath = xmalloc(strlen(ppath) + 1);
- (void)strcpy(csinfo[i].ppath, (const char *)ppath);
- } else {
- csinfo[i].ppath = NULL;
- }
-
- if (flags != NULL) {
- csinfo[i].flags = xmalloc(strlen(flags) + 1);
- (void)strcpy(csinfo[i].flags, (const char *)flags);
- } else {
- csinfo[i].flags = NULL;
- }
-
- os_fileinfo_id(file_info, &(csinfo[i].file_id));
- assert(i <= INT_MAX);
- return (int)i;
-}
-
-/// Find cscope command in command table.
-static cscmd_T *cs_lookup_cmd(exarg_T *eap)
-{
- cscmd_T *cmdp;
- char *stok;
- size_t len;
-
- if (eap->arg == NULL) {
- return NULL;
- }
-
- // Store length of eap->arg before it gets modified by strtok().
- eap_arg_len = (int)STRLEN(eap->arg);
-
- if ((stok = strtok(eap->arg, (const char *)" ")) == NULL) { // NOLINT(runtime/threadsafe_fn)
- return NULL;
- }
-
- len = strlen(stok);
- for (cmdp = cs_cmds; cmdp->name != NULL; cmdp++) {
- if (strncmp(stok, cmdp->name, len) == 0) {
- return cmdp;
- }
- }
- return NULL;
-}
-
-/// Nuke em.
-static int cs_kill(exarg_T *eap)
-{
- char *stok;
- int num;
- size_t i = 0;
- bool killall = false;
-
- if ((stok = strtok((char *)NULL, (const char *)" ")) == NULL) {
- cs_usage_msg(Kill);
- return CSCOPE_FAILURE;
- }
-
- // Check if string is a number, only single digit
- // positive and negative integers are allowed
- if ((strlen(stok) < 2 && ascii_isdigit((int)(stok[0])))
- || (strlen(stok) < 3 && stok[0] == '-'
- && ascii_isdigit((int)(stok[1])))) {
- num = atoi(stok);
- if (num == -1) {
- killall = true;
- } else if (num >= 0) {
- i = (size_t)num;
- } else { // All negative values besides -1 are invalid.
- if (p_csverbose) {
- (void)semsg(_("E261: cscope connection %s not found"), stok);
- }
- return CSCOPE_FAILURE;
- }
- } else {
- // Else it must be part of a name. We will try to find a match
- // within all the names in the csinfo data structure
- for (i = 0; i < csinfo_size; i++) {
- if (csinfo[i].fname != NULL && strstr(csinfo[i].fname, stok)) {
- break;
- }
- }
- }
-
- if (!killall && (i >= csinfo_size || csinfo[i].fname == NULL)) {
- if (p_csverbose) {
- (void)semsg(_("E261: cscope connection %s not found"), stok);
- }
- return CSCOPE_FAILURE;
- } else {
- if (killall) {
- for (i = 0; i < csinfo_size; i++) {
- if (csinfo[i].fname) {
- cs_kill_execute(i, csinfo[i].fname);
- }
- }
- } else {
- cs_kill_execute(i, stok);
- }
- }
-
- return CSCOPE_SUCCESS;
-}
-
-/// Actually kills a specific cscope connection.
-///
-/// @param i cscope table index
-/// @param cname cscope database name
-static void cs_kill_execute(size_t i, char *cname)
-{
- if (p_csverbose) {
- msg_clr_eos();
- (void)smsg_attr(HL_ATTR(HLF_R) | MSG_HIST,
- _("cscope connection %s closed"), 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
-/// ctags (the one that comes with vim), #define's use a line number to find the
-/// tag in a file while function definitions use a regexp search pattern.
-///
-/// I'm going to always use the line number because cscope does something
-/// quirky (and probably other things i don't know about):
-///
-/// if you have "# define" in your source file, which is
-/// perfectly legal, cscope thinks you have "#define". this
-/// will result in a failed regexp search. :(
-///
-/// Besides, even if this particular case didn't happen, the search pattern
-/// would still have to be modified to escape all the special regular expression
-/// characters to comply with ctags formatting.
-static char *cs_make_vim_style_matches(char *fname, char *slno, char *search, char *tagstr)
-{
- // vim style is ctags:
- //
- // <tagstr>\t<filename>\t<linenum_or_search>"\t<extra>
- //
- // but as mentioned above, we'll always use the line number and
- // put the search pattern (if one exists) as "extra"
- //
- // buf is used as part of vim's method of handling tags, and
- // (i think) vim frees it when you pop your tags and get replaced
- // by new ones on the tag stack.
- char *buf;
- size_t amt;
-
- if (search != NULL) {
- amt = strlen(fname) + strlen(slno) + strlen(tagstr) + strlen(search) + 6;
- buf = xmalloc(amt);
-
- (void)sprintf(buf, "%s\t%s\t%s;\"\t%s", tagstr, fname, slno, search);
- } else {
- amt = strlen(fname) + strlen(slno) + strlen(tagstr) + 5;
- buf = xmalloc(amt);
-
- (void)sprintf(buf, "%s\t%s\t%s;\"", tagstr, fname, slno);
- }
-
- 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
-/// generated from cs_find(). the matches are originally lines directly
-/// from cscope output, but transformed to look like something out of a
-/// ctags. see cs_make_vim_style_matches for more details.
-///
-/// Get: used only from cs_fgets(), this simulates a vim_fgets() to return
-/// the next line from the cscope output. it basically keeps track of which
-/// lines have been "used" and returns the next one.
-///
-/// Free: frees up everything and resets
-///
-/// Print: prints the tags
-static char *cs_manage_matches(char **matches, char **contexts, size_t totmatches, mcmd_e cmd)
-{
- static char **mp = NULL;
- static char **cp = NULL;
- static size_t cnt = 0;
- static size_t next = 0;
- char *p = NULL;
-
- switch (cmd) {
- case Store:
- assert(matches != NULL);
- assert(totmatches > 0);
- if (mp != NULL || cp != NULL) {
- (void)cs_manage_matches(NULL, NULL, 0, Free);
- }
- mp = matches;
- cp = contexts;
- cnt = totmatches;
- next = 0;
- break;
- case Get:
- if (next >= cnt) {
- return NULL;
- }
-
- p = mp[next];
- next++;
- break;
- case Free:
- if (mp != NULL) {
- while (cnt--) {
- xfree(mp[cnt]);
- if (cp != NULL) {
- xfree(cp[cnt]);
- }
- }
- xfree(mp);
- xfree(cp);
- }
- mp = NULL;
- cp = NULL;
- cnt = 0;
- next = 0;
- break;
- case Print:
- assert(mp != NULL);
- assert(cp != NULL);
- cs_print_tags_priv(mp, cp, cnt);
- break;
- default: // should not reach here
- iemsg(_("E570: fatal error in cs_manage_matches"));
- return NULL;
- }
-
- return p;
-}
-
-/// Parse cscope output.
-static char *cs_parse_results(size_t cnumber, char *buf, int bufsize, char **context,
- char **linenumber, char **search)
-{
- int ch;
- char *p;
- char *name;
-
-retry:
- errno = 0;
- if (fgets(buf, bufsize, csinfo[cnumber].fr_fp) == NULL) {
- if (errno == EINTR) {
- goto retry;
- }
-
- if (feof(csinfo[cnumber].fr_fp)) {
- errno = EIO;
- }
-
- cs_reading_emsg(cnumber);
-
- return NULL;
- }
-
- // 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') {}
- return NULL;
- }
- *p = '\0';
-
- /*
- * cscope output is in the following format:
- *
- * <filename> <context> <line number> <pattern>
- */
- char *saveptr = NULL;
- if ((name = os_strtok(buf, (const char *)" ", &saveptr)) == NULL) {
- return NULL;
- }
- if ((*context = os_strtok(NULL, (const char *)" ", &saveptr)) == NULL) {
- return NULL;
- }
- if ((*linenumber = os_strtok(NULL, (const char *)" ", &saveptr)) == NULL) {
- return NULL;
- }
- *search = *linenumber + strlen(*linenumber) + 1; // +1 to skip \0
-
- // --- nvi ---
- // If the file is older than the cscope database, that is,
- // the database was built since the file was last modified,
- // or there wasn't a search string, use the line number.
- if (strcmp(*search, "<unknown>") == 0) {
- *search = NULL;
- }
-
- name = cs_resolve_file(cnumber, name);
- return name;
-}
-
-/// Write cscope find results to file.
-static void cs_file_results(FILE *f, int *nummatches_a)
-{
- char *search, *slno;
- char *fullname;
- char *cntx;
- char *context;
-
- char *buf = xmalloc(CSREAD_BUFSIZE);
-
- for (size_t i = 0; i < csinfo_size; i++) {
- if (nummatches_a[i] < 1) {
- continue;
- }
-
- for (int j = 0; j < nummatches_a[i]; j++) {
- if ((fullname = cs_parse_results(i, buf, CSREAD_BUFSIZE, &cntx,
- &slno, &search)) == NULL) {
- continue;
- }
-
- size_t context_len = strlen(cntx) + 5;
- context = xmalloc(context_len);
-
- if (strcmp(cntx, "<global>") == 0) {
- xstrlcpy(context, "<<global>>", context_len);
- } else {
- snprintf(context, context_len, "<<%s>>", cntx);
- }
-
- if (search == NULL) {
- fprintf(f, "%s\t%s\t%s\n", fullname, slno, context);
- } else {
- fprintf(f, "%s\t%s\t%s %s\n", fullname, slno, context, search);
- }
-
- xfree(context);
- xfree(fullname);
- } // for all matches
-
- (void)cs_read_prompt(i);
- } // for all cscope connections
- xfree(buf);
-}
-
-/// Get parsed cscope output and calls cs_make_vim_style_matches to convert
-/// into ctags format.
-/// When there are no matches sets "*matches_p" to NULL.
-static void cs_fill_results(char *tagstr, size_t totmatches, int *nummatches_a, char ***matches_p,
- char ***cntxts_p, size_t *matched)
-{
- char *buf;
- char *search, *slno;
- size_t totsofar = 0;
- char **matches = NULL;
- char **cntxts = NULL;
- char *fullname;
- char *cntx;
-
- assert(totmatches > 0);
-
- buf = xmalloc(CSREAD_BUFSIZE);
- matches = xmalloc(sizeof(char *) * totmatches);
- cntxts = xmalloc(sizeof(char *) * totmatches);
-
- for (size_t i = 0; i < csinfo_size; i++) {
- if (nummatches_a[i] < 1) {
- continue;
- }
-
- for (int j = 0; j < nummatches_a[i]; j++) {
- if ((fullname = cs_parse_results(i, buf, CSREAD_BUFSIZE, &cntx,
- &slno, &search)) == NULL) {
- continue;
- }
-
- matches[totsofar] = cs_make_vim_style_matches(fullname, slno, search,
- tagstr);
-
- xfree(fullname);
-
- if (strcmp(cntx, "<global>") == 0) {
- cntxts[totsofar] = NULL;
- } else {
- cntxts[totsofar] = xstrdup(cntx);
- }
-
- totsofar++;
- } // for all matches
-
- (void)cs_read_prompt(i);
- } // for all cscope connections
-
- if (totsofar == 0) {
- // No matches, free the arrays and return NULL in "*matches_p".
- XFREE_CLEAR(matches);
- XFREE_CLEAR(cntxts);
- }
- *matched = totsofar;
- *matches_p = matches;
- *cntxts_p = cntxts;
-
- xfree(buf);
-}
-
-// get the requested path components
-static char *cs_pathcomponents(char *path)
-{
- if (p_cspc == 0) {
- return path;
- }
-
- char *s = path + strlen(path) - 1;
- for (int i = 0; i < p_cspc; i++) {
- while (s > path && *--s != '/') {}
- }
- if ((s > path && *s == '/')) {
- s++;
- }
- return s;
-}
-
-/// Print cscope output that was converted into ctags style entries.
-///
-/// Only called from cs_manage_matches().
-///
-/// @param matches Array of cscope lines in ctags style. Every entry was
-// produced with a format string of the form
-// "%s\t%s\t%s;\"\t%s" or
-// "%s\t%s\t%s;\""
-// by cs_make_vim_style_matches().
-/// @param cntxts Context for matches.
-/// @param num_matches Number of entries in matches/cntxts, always greater 0.
-static void cs_print_tags_priv(char **matches, char **cntxts,
- size_t num_matches) FUNC_ATTR_NONNULL_ALL
-{
- char *globalcntx = "GLOBAL";
- char *cstag_msg = _("Cscope tag: %s");
-
- assert(num_matches > 0);
- assert(strcnt(matches[0], '\t') >= 2);
-
- char *ptag = matches[0];
- char *ptag_end = strchr(ptag, '\t');
- assert(ptag_end >= ptag);
- // NUL terminate tag string in matches[0].
- *ptag_end = NUL;
-
- // The "%s" in cstag_msg won't appear in the result string, so we don't need
- // extra memory for terminating NUL.
- size_t newsize = strlen(cstag_msg) + (size_t)(ptag_end - ptag);
- char *buf = xmalloc(newsize);
- size_t bufsize = newsize; // Track available bufsize
- (void)snprintf(buf, bufsize, cstag_msg, ptag);
- msg_puts_attr(buf, HL_ATTR(HLF_T));
- msg_clr_eos();
-
- // restore matches[0]
- *ptag_end = '\t';
-
- // Column headers for match number, line number and filename.
- msg_puts_attr(_("\n # line"), HL_ATTR(HLF_T));
- msg_advance(msg_col + 2);
- msg_puts_attr(_("filename / context / line\n"), HL_ATTR(HLF_T));
-
- for (size_t i = 0; i < num_matches; i++) {
- assert(strcnt(matches[i], '\t') >= 2);
-
- // Parse filename, line number and optional part.
- char *fname = strchr(matches[i], '\t') + 1;
- char *fname_end = strchr(fname, '\t');
- // Replace second '\t' in matches[i] with NUL to terminate fname.
- *fname_end = NUL;
-
- char *lno = fname_end + 1;
- char *extra = xstrchrnul(lno, '\t');
- // Ignore ;" at the end of lno.
- char *lno_end = extra - 2;
- *lno_end = NUL;
- // Do we have an optional part?
- extra = *extra ? extra + 1 : NULL;
-
- const char *csfmt_str = "%4zu %6s ";
- // hopefully num_matches will be less than 10^16
- newsize = strlen(csfmt_str) + 16 + (size_t)(lno_end - lno);
- if (bufsize < newsize) {
- buf = xrealloc(buf, newsize);
- bufsize = newsize;
- }
- (void)snprintf(buf, bufsize, csfmt_str, i + 1, lno);
- msg_puts_attr(buf, HL_ATTR(HLF_CM));
- msg_outtrans_long_attr((char_u *)cs_pathcomponents(fname), HL_ATTR(HLF_CM));
-
- // compute the required space for the context
- char *context = cntxts[i] ? cntxts[i] : globalcntx;
-
- const char *cntxformat = " <<%s>>";
- // '%s' won't appear in result string, so:
- // newsize = len(cntxformat) - 2 + len(context) + 1 (for NUL).
- newsize = strlen(context) + strlen(cntxformat) - 1;
-
- if (bufsize < newsize) {
- buf = xrealloc(buf, newsize);
- bufsize = newsize;
- }
- int buf_len = snprintf(buf, bufsize, cntxformat, context);
- assert(buf_len >= 0);
-
- // Print the context only if it fits on the same line.
- if (msg_col + buf_len >= Columns) {
- msg_putchar('\n');
- }
- msg_advance(12);
- msg_outtrans_long_attr((char_u *)buf, 0);
- msg_putchar('\n');
- if (extra != NULL) {
- msg_advance(13);
- msg_outtrans_long_attr((char_u *)extra, 0);
- }
-
- // restore matches[i]
- *fname_end = '\t';
- *lno_end = ';';
-
- if (msg_col) {
- msg_putchar('\n');
- }
-
- os_breakcheck();
- if (got_int) {
- got_int = false; // don't print any more matches
- break;
- }
- }
-
- xfree(buf);
-}
-
-/// Read a cscope prompt (basically, skip over the ">> ").
-static int cs_read_prompt(size_t i)
-{
- int ch;
- char *buf = NULL; // buffer for possible error message from cscope
- size_t bufpos = 0;
- char *cs_emsg = _("E609: Cscope error: %s");
- size_t cs_emsg_len = strlen(cs_emsg);
- static char *eprompt = "Press the RETURN key to continue:";
- size_t epromptlen = strlen(eprompt);
-
- // compute maximum allowed len for Cscope error message
- assert(IOSIZE >= cs_emsg_len);
- size_t maxlen = IOSIZE - cs_emsg_len;
-
- while (1) {
- while (1) {
- do {
- errno = 0;
- ch = fgetc(csinfo[i].fr_fp);
- } while (ch == EOF && errno == EINTR && ferror(csinfo[i].fr_fp));
- if (ch == EOF || ch == CSCOPE_PROMPT[0]) {
- break;
- }
- // if there is room and char is printable
- if (bufpos < maxlen - 1 && vim_isprintc(ch)) {
- // lazy buffer allocation
- if (buf == NULL) {
- buf = xmalloc(maxlen);
- }
- // append character to the message
- buf[bufpos++] = (char)ch;
- buf[bufpos] = NUL;
- if (bufpos >= epromptlen
- && strcmp(&buf[bufpos - epromptlen], eprompt) == 0) {
- // remove eprompt from buf
- buf[bufpos - epromptlen] = NUL;
-
- // print message to user
- (void)semsg(cs_emsg, buf);
-
- // send RETURN to cscope
- (void)putc('\n', csinfo[i].to_fp);
- (void)fflush(csinfo[i].to_fp);
-
- // clear buf
- bufpos = 0;
- buf[bufpos] = NUL;
- }
- }
- }
-
- for (size_t n = 0; n < strlen(CSCOPE_PROMPT); n++) {
- if (n > 0) {
- do {
- errno = 0;
- ch = fgetc(csinfo[i].fr_fp);
- } while (ch == EOF && errno == EINTR && ferror(csinfo[i].fr_fp));
- }
- if (ch == EOF) {
- PERROR("cs_read_prompt EOF");
- if (buf != NULL && buf[0] != NUL) {
- (void)semsg(cs_emsg, buf);
- } else if (p_csverbose) {
- cs_reading_emsg(i); // don't have additional information
- }
- cs_release_csp(i, true);
- xfree(buf);
- return CSCOPE_FAILURE;
- }
-
- if (ch != CSCOPE_PROMPT[n]) {
- ch = EOF;
- break;
- }
- }
-
- if (ch == EOF) {
- continue; // didn't find the prompt
- }
- break; // did find the prompt
- }
-
- xfree(buf);
- return CSCOPE_SUCCESS;
-}
-
-#if defined(UNIX) && defined(SIGALRM)
-/*
- * Used to catch and ignore SIGALRM below.
- */
-static void sig_handler(int s)
-{
- // do nothing
-}
-
-#endif
-
-/// Does the actual free'ing for the cs ptr with an optional flag of whether
-/// or not to free the filename. Called by cs_kill and cs_reset.
-static void cs_release_csp(size_t i, bool freefnpp)
-{
- // Trying to exit normally (not sure whether it is fit to Unix cscope)
- if (csinfo[i].to_fp != NULL) {
- (void)fputs("q\n", csinfo[i].to_fp);
- (void)fflush(csinfo[i].to_fp);
- }
-#if defined(UNIX)
- {
- int waitpid_errno;
- int pstat;
- pid_t pid;
-
-# if defined(HAVE_SIGACTION)
- struct sigaction sa, old;
-
- // Use sigaction() to limit the waiting time to two seconds.
- sigemptyset(&sa.sa_mask);
- sa.sa_handler = sig_handler;
-# ifdef SA_NODEFER
- sa.sa_flags = SA_NODEFER;
-# else
- sa.sa_flags = 0;
-# endif
- sigaction(SIGALRM, &sa, &old);
- alarm(2); // 2 sec timeout
-
- // Block until cscope exits or until timer expires
- pid = waitpid(csinfo[i].pid, &pstat, 0);
- waitpid_errno = errno;
-
- // cancel pending alarm if still there and restore signal
- alarm(0);
- sigaction(SIGALRM, &old, NULL);
-# else
- int waited;
-
- // Can't use sigaction(), loop for two seconds. First yield the CPU
- // to give cscope a chance to exit quickly.
- sleep(0);
- for (waited = 0; waited < 40; ++waited) {
- pid = waitpid(csinfo[i].pid, &pstat, WNOHANG);
- waitpid_errno = errno;
- if (pid != 0) {
- break; // break unless the process is still running
- }
- os_delay(50L, false); // sleep 50 ms
- }
-# endif
- /*
- * If the cscope process is still running: kill it.
- * Safety check: If the PID would be zero here, the entire X session
- * would be killed. -1 and 1 are dangerous as well.
- */
- if (pid < 0 && csinfo[i].pid > 1) {
-# ifdef ECHILD
- bool alive = true;
-
- if (waitpid_errno == ECHILD) {
- /*
- * When using 'vim -g', vim is forked and cscope process is
- * no longer a child process but a sibling. So waitpid()
- * fails with errno being ECHILD (No child processes).
- * Don't send SIGKILL to cscope immediately but wait
- * (polling) for it to exit normally as result of sending
- * the "q" command, hence giving it a chance to clean up
- * its temporary files.
- */
- int waited;
-
- sleep(0);
- for (waited = 0; waited < 40; waited++) {
- // Check whether cscope process is still alive
- if (kill(csinfo[i].pid, 0) != 0) {
- alive = false; // cscope process no longer exists
- break;
- }
- os_delay(50L, false); // sleep 50 ms
- }
- }
- if (alive)
-# endif
- {
- kill(csinfo[i].pid, SIGKILL);
- (void)waitpid(csinfo[i].pid, &pstat, 0);
- }
- }
- }
-#else // !UNIX
- if (csinfo[i].hProc != NULL) {
- // Give cscope a chance to exit normally
- if (WaitForSingleObject(csinfo[i].hProc, 1000) == WAIT_TIMEOUT) {
- TerminateProcess(csinfo[i].hProc, 0);
- }
- CloseHandle(csinfo[i].hProc);
- }
-#endif
-
- if (csinfo[i].fr_fp != NULL) {
- (void)fclose(csinfo[i].fr_fp);
- }
- if (csinfo[i].to_fp != NULL) {
- (void)fclose(csinfo[i].to_fp);
- }
-
- if (freefnpp) {
- xfree(csinfo[i].fname);
- xfree(csinfo[i].ppath);
- xfree(csinfo[i].flags);
- }
-
- clear_csinfo(i);
-}
-
-/// Calls cs_kill on all cscope connections then reinits.
-static int cs_reset(exarg_T *eap)
-{
- char **dblist = NULL, **pplist = NULL, **fllist = NULL;
- char buf[25]; // for snprintf " (#%zu)"
-
- if (csinfo_size == 0) {
- return CSCOPE_SUCCESS;
- }
-
- // malloc our db and ppath list
- dblist = xmalloc(csinfo_size * sizeof(char *));
- pplist = xmalloc(csinfo_size * sizeof(char *));
- fllist = xmalloc(csinfo_size * sizeof(char *));
-
- for (size_t i = 0; i < csinfo_size; i++) {
- dblist[i] = csinfo[i].fname;
- pplist[i] = csinfo[i].ppath;
- fllist[i] = csinfo[i].flags;
- if (csinfo[i].fname != NULL) {
- cs_release_csp(i, FALSE);
- }
- }
-
- // rebuild the cscope connection list
- for (size_t i = 0; i < csinfo_size; i++) {
- if (dblist[i] != NULL) {
- cs_add_common(dblist[i], pplist[i], fllist[i]);
- if (p_csverbose) {
- // don't use smsg_attr() because we want to display the
- // connection number in the same line as
- // "Added cscope database..."
- snprintf(buf, ARRAY_SIZE(buf), " (#%zu)", i);
- msg_puts_attr(buf, HL_ATTR(HLF_R));
- }
- }
- xfree(dblist[i]);
- xfree(pplist[i]);
- xfree(fllist[i]);
- }
- xfree(dblist);
- xfree(pplist);
- xfree(fllist);
-
- if (p_csverbose) {
- msg_attr(_("All cscope databases reset"), HL_ATTR(HLF_R) | MSG_HIST);
- }
- 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.)
-///
-/// We need to prepend the prefix because on some cscope's (e.g., the one that
-/// ships with Solaris 2.6), the output never has the prefix prepended.
-/// Contrast this with my development system (Digital Unix), which does.
-static char *cs_resolve_file(size_t i, char *name)
-{
- char *fullname;
- char_u *csdir = NULL;
-
- /*
- * Ppath is freed when we destroy the cscope connection.
- * Fullname is freed after cs_make_vim_style_matches, after it's been
- * copied into the tag buffer used by Vim.
- */
- size_t len = strlen(name) + 2;
- if (csinfo[i].ppath != NULL) {
- len += strlen(csinfo[i].ppath);
- } else if (p_csre && csinfo[i].fname != NULL) {
- // If 'cscoperelative' is set and ppath is not set, use cscope.out
- // path in path resolution.
- csdir = xmalloc(MAXPATHL);
- STRLCPY(csdir, csinfo[i].fname,
- path_tail(csinfo[i].fname)
- - csinfo[i].fname + 1);
- len += STRLEN(csdir);
- }
-
- // Note/example: this won't work if the cscope output already starts
- // "../.." and the prefix path is also "../..". if something like this
- // happens, you are screwed up and need to fix how you're using cscope.
- if (csinfo[i].ppath != NULL
- && (strncmp(name, csinfo[i].ppath, strlen(csinfo[i].ppath)) != 0)
- && (name[0] != '/')) {
- fullname = xmalloc(len);
- (void)sprintf(fullname, "%s/%s", csinfo[i].ppath, name);
- } else if (csdir != NULL && csinfo[i].fname != NULL && *csdir != NUL) {
- // Check for csdir to be non empty to avoid empty path concatenated to
- // cscope output.
- fullname = concat_fnames((char *)csdir, name, true);
- } else {
- fullname = xstrdup(name);
- }
-
- xfree(csdir);
- return fullname;
-}
-
-/// Show all cscope connections.
-static int cs_show(exarg_T *eap)
-{
- if (cs_cnt_connections() == 0) {
- msg_puts(_("no cscope connections\n"));
- } else {
- msg_puts_attr(_(" # pid database name prepend path\n"),
- HL_ATTR(HLF_T));
- for (size_t i = 0; i < csinfo_size; i++) {
- if (csinfo[i].fname == NULL) {
- continue;
- }
-
- if (csinfo[i].ppath != NULL) {
- (void)smsg("%2zu %-5" PRId64 " %-34s %-32s", i,
- (int64_t)csinfo[i].pid, csinfo[i].fname, csinfo[i].ppath);
- } else {
- (void)smsg("%2zu %-5" PRId64 " %-34s <none>", i,
- (int64_t)csinfo[i].pid, csinfo[i].fname);
- }
- }
- }
-
- wait_return(TRUE);
- return CSCOPE_SUCCESS;
-}
-
-/// Only called when VIM exits to quit any cscope sessions.
-void cs_end(void)
-{
- for (size_t i = 0; i < csinfo_size; i++) {
- cs_release_csp(i, true);
- }
- xfree(csinfo);
- csinfo_size = 0;
-}
-
-// the end
diff --git a/src/nvim/if_cscope.h b/src/nvim/if_cscope.h
deleted file mode 100644
index 8dbc78943f..0000000000
--- a/src/nvim/if_cscope.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef NVIM_IF_CSCOPE_H
-#define NVIM_IF_CSCOPE_H
-
-#include "nvim/ex_cmds_defs.h" // for exarg_T
-#include "nvim/types.h" // for char_u and expand_T
-
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "if_cscope.h.generated.h"
-#endif
-#endif // NVIM_IF_CSCOPE_H
diff --git a/src/nvim/if_cscope_defs.h b/src/nvim/if_cscope_defs.h
deleted file mode 100644
index 6ded89fa0b..0000000000
--- a/src/nvim/if_cscope_defs.h
+++ /dev/null
@@ -1,64 +0,0 @@
-#ifndef NVIM_IF_CSCOPE_DEFS_H
-#define NVIM_IF_CSCOPE_DEFS_H
-
-// CSCOPE support for Vim added by Andy Kahn <kahn@zk3.dec.com>
-// Ported to Win32 by Sergey Khorev <sergey.khorev@gmail.com>
-//
-// The basic idea/structure of cscope for Vim was borrowed from Nvi.
-// There might be a few lines of code that look similar to what Nvi
-// has. If this is a problem and requires inclusion of the annoying
-// BSD license, then sue me; I'm not worth much anyway.
-
-#if defined(UNIX)
-# include <sys/types.h> // pid_t
-#endif
-
-#include "nvim/ex_cmds_defs.h"
-#include "nvim/os/fs_defs.h"
-#include "nvim/os/os_defs.h"
-
-#define CSCOPE_SUCCESS 0
-#define CSCOPE_FAILURE -1
-
-#define CSCOPE_DBFILE "cscope.out"
-#define CSCOPE_PROMPT ">> "
-
-// See ":help cscope-find" for the possible queries.
-
-typedef struct {
- char *name;
- int (*func)(exarg_T *eap);
- char *help;
- char *usage;
- int cansplit; // if supports splitting window
-} cscmd_T;
-
-typedef struct csi {
- char *fname; // cscope db name
- char *ppath; // path to prepend (the -P option)
- char *flags; // additional cscope flags/options (e.g, -p2)
-#if defined(UNIX)
- pid_t pid; // PID of the connected cscope process
-#else
- DWORD pid; // PID of the connected cscope process
- HANDLE hProc; // cscope process handle
- DWORD nVolume; // Volume serial number, instead of st_dev
- DWORD nIndexHigh; // st_ino has no meaning on Windows
- DWORD nIndexLow;
-#endif
- FileID file_id;
-
- FILE *fr_fp; // from cscope: FILE.
- FILE *to_fp; // to cscope: FILE.
-} csinfo_T;
-
-typedef enum { Add, Find, Help, Kill, Reset, Show, } csid_e;
-
-typedef enum {
- Store,
- Get,
- Free,
- Print,
-} mcmd_e;
-
-#endif // NVIM_IF_CSCOPE_DEFS_H
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index f18a6d7b32..ec6c72da6d 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -2,8 +2,10 @@
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include <assert.h>
-#include <inttypes.h>
+#include <limits.h>
#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
#include "nvim/ascii.h"
#include "nvim/assert.h"
@@ -11,21 +13,36 @@
#include "nvim/change.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
+#include "nvim/ex_docmd.h"
#include "nvim/extmark.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
#include "nvim/indent.h"
+#include "nvim/indent_c.h"
#include "nvim/mark.h"
+#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
+#include "nvim/message.h"
#include "nvim/move.h"
#include "nvim/option.h"
+#include "nvim/optionstr.h"
+#include "nvim/os/input.h"
#include "nvim/plines.h"
+#include "nvim/pos.h"
#include "nvim/regexp.h"
#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/strings.h"
+#include "nvim/textformat.h"
+#include "nvim/types.h"
#include "nvim/undo.h"
+#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "indent.c.generated.h"
@@ -33,12 +50,13 @@
/// 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)
+///
+/// @return false for an error.
+bool tabstop_set(char *var, long **array)
{
long valcount = 1;
int t;
- char_u *cp;
+ char *cp;
if (var[0] == NUL || (var[0] == '0' && var[1] == NUL)) {
*array = NULL;
@@ -49,8 +67,8 @@ bool tabstop_set(char_u *var, long **array)
if (cp == var || cp[-1] == ',') {
char *end;
- if (strtol((char *)cp, &end, 10) <= 0) {
- if (cp != (char_u *)end) {
+ if (strtol(cp, &end, 10) <= 0) {
+ if (cp != end) {
emsg(_(e_positive));
} else {
semsg(_(e_invarg2), cp);
@@ -75,7 +93,7 @@ bool tabstop_set(char_u *var, long **array)
t = 1;
for (cp = var; *cp != NUL;) {
- int n = atoi((char *)cp);
+ int n = atoi(cp);
// Catch negative values, overflow and ridiculous big values.
if (n <= 0 || n > TABSTOP_MAX) {
@@ -98,7 +116,7 @@ bool tabstop_set(char_u *var, long **array)
/// Calculate the number of screen spaces a tab will occupy.
/// If "vts" is set then the tab widths are taken from that array,
/// otherwise the value of ts is used.
-int tabstop_padding(colnr_T col, long ts_arg, long *vts)
+int tabstop_padding(colnr_T col, long ts_arg, const long *vts)
{
long ts = ts_arg == 0 ? 8 : ts_arg;
colnr_T tabcol = 0;
@@ -126,7 +144,7 @@ int tabstop_padding(colnr_T col, long ts_arg, long *vts)
}
/// Find the size of the tab that covers a particular column.
-int tabstop_at(colnr_T col, long ts, long *vts)
+int tabstop_at(colnr_T col, long ts, const long *vts)
{
colnr_T tabcol = 0;
int t;
@@ -175,7 +193,7 @@ colnr_T tabstop_start(colnr_T col, long ts, long *vts)
/// Find the number of tabs and spaces necessary to get from one column
/// to another.
-void tabstop_fromto(colnr_T start_col, colnr_T end_col, long ts_arg, long *vts, int *ntabs,
+void tabstop_fromto(colnr_T start_col, colnr_T end_col, long ts_arg, const long *vts, int *ntabs,
int *nspcs)
{
int spaces = end_col - start_col;
@@ -183,6 +201,7 @@ void tabstop_fromto(colnr_T start_col, colnr_T end_col, long ts_arg, long *vts,
long padding = 0;
int t;
long ts = ts_arg == 0 ? curbuf->b_p_ts : ts_arg;
+ assert(ts != 0); // suppress clang "Division by zero"
if (vts == NULL || vts[0] == 0) {
int tabs = 0;
@@ -239,7 +258,7 @@ void tabstop_fromto(colnr_T start_col, colnr_T end_col, long ts_arg, long *vts,
}
/// See if two tabstop arrays contain the same values.
-bool tabstop_eq(long *ts1, long *ts2)
+bool tabstop_eq(const long *ts1, const long *ts2)
{
int t;
@@ -263,7 +282,7 @@ bool tabstop_eq(long *ts1, long *ts2)
}
/// Copy a tabstop array, allocating space for the new array.
-int *tabstop_copy(long *oldts)
+int *tabstop_copy(const long *oldts)
{
long *newts;
int t;
@@ -361,7 +380,7 @@ int get_indent_lnum(linenr_T lnum)
int get_indent_buf(buf_T *buf, linenr_T lnum)
{
return get_indent_str_vtab(ml_get_buf(buf, lnum, false),
- curbuf->b_p_ts,
+ buf->b_p_ts,
buf->b_p_vts_array,
false);
}
@@ -369,7 +388,7 @@ int get_indent_buf(buf_T *buf, linenr_T lnum)
/// 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.
-int get_indent_str(const char_u *ptr, int ts, bool list)
+int get_indent_str(const char *ptr, int ts, bool list)
FUNC_ATTR_NONNULL_ALL
{
int count = 0;
@@ -383,7 +402,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((char *)ptr);
+ count += ptr2cells(ptr);
}
} else if (*ptr == ' ') {
// Count a space for one.
@@ -398,7 +417,7 @@ int get_indent_str(const char_u *ptr, int ts, bool list)
/// Count the size (in window cells) of the indent in line "ptr", using
/// variable tabstops.
/// if "list" is true, count only screen size for tabs.
-int get_indent_str_vtab(const char_u *ptr, long ts, long *vts, bool list)
+int get_indent_str_vtab(const char *ptr, long ts, long *vts, bool list)
{
int count = 0;
@@ -432,10 +451,10 @@ int get_indent_str_vtab(const char_u *ptr, long ts, long *vts, bool list)
// Returns true if the line was changed.
int set_indent(int size, int flags)
{
- char_u *p;
- char_u *newline;
- char_u *oldline;
- char_u *s;
+ char *p;
+ char *newline;
+ char *oldline;
+ char *s;
int todo;
int ind_len; // Measured in characters.
int line_len;
@@ -547,9 +566,9 @@ int set_indent(int size, int flags)
if (flags & SIN_INSERT) {
p = oldline;
} else {
- p = (char_u *)skipwhite((char *)p);
+ p = skipwhite(p);
}
- line_len = (int)STRLEN(p) + 1;
+ line_len = (int)strlen(p) + 1;
// If 'preserveindent' and 'expandtab' are both set keep the original
// characters and allocate accordingly. We will fill the rest with spaces
@@ -629,7 +648,7 @@ int set_indent(int size, int flags)
todo -= tab_pad;
ind_done += tab_pad;
}
- p = (char_u *)skipwhite((char *)p);
+ p = skipwhite(p);
}
for (;;) {
@@ -657,7 +676,7 @@ 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, (char *)newline, false);
+ ml_replace(curwin->w_cursor.lnum, newline, false);
if (!(flags & SIN_NOMARK)) {
extmark_splice_cols(curbuf,
(int)curwin->w_cursor.lnum - 1,
@@ -708,16 +727,16 @@ int get_number_indent(linenr_T lnum)
// In format_lines() (i.e. not insert mode), fo+=q is needed too...
if ((State & MODE_INSERT) || has_format_option(FO_Q_COMS)) {
- lead_len = get_leader_len((char *)ml_get(lnum), NULL, false, true);
+ lead_len = get_leader_len(ml_get(lnum), NULL, false, true);
}
- regmatch.regprog = vim_regcomp((char *)curbuf->b_p_flp, RE_MAGIC);
+ regmatch.regprog = vim_regcomp(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, (char *)ml_get(lnum) + lead_len, (colnr_T)0)) {
+ if (vim_regexec(&regmatch, ml_get(lnum) + lead_len, (colnr_T)0)) {
pos.lnum = lnum;
pos.col = (colnr_T)(*regmatch.endp - ml_get(lnum));
pos.coladd = 0;
@@ -732,64 +751,133 @@ int get_number_indent(linenr_T lnum)
return (int)col;
}
+/// This is called when 'breakindentopt' is changed and when a window is
+/// initialized
+bool briopt_check(win_T *wp)
+{
+ int bri_shift = 0;
+ int bri_min = 20;
+ bool bri_sbr = false;
+ int bri_list = 0;
+ int bri_vcol = 0;
+
+ char *p = wp->w_p_briopt;
+ while (*p != NUL) {
+ 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);
+ } else if (strncmp(p, "min:", 4) == 0 && ascii_isdigit(p[4])) {
+ p += 4;
+ bri_min = getdigits_int(&p, true, 0);
+ } else if (strncmp(p, "sbr", 3) == 0) {
+ p += 3;
+ bri_sbr = true;
+ } else if (strncmp(p, "list:", 5) == 0) {
+ p += 5;
+ bri_list = (int)getdigits(&p, false, 0);
+ } else if (strncmp(p, "column:", 7) == 0) {
+ p += 7;
+ bri_vcol = (int)getdigits(&p, false, 0);
+ }
+ if (*p != ',' && *p != NUL) {
+ return false;
+ }
+ if (*p == ',') {
+ p++;
+ }
+ }
+
+ wp->w_briopt_shift = bri_shift;
+ wp->w_briopt_min = bri_min;
+ wp->w_briopt_sbr = bri_sbr;
+ wp->w_briopt_list = bri_list;
+ wp->w_briopt_vcol = bri_vcol;
+
+ return true;
+}
+
// Return appropriate space number for breakindent, taking influencing
// parameters into account. Window must be specified, since it is not
// necessarily always the current one.
-int get_breakindent_win(win_T *wp, char_u *line)
+int get_breakindent_win(win_T *wp, char *line)
FUNC_ATTR_NONNULL_ALL
{
static int prev_indent = 0; // Cached indent value.
- static long prev_ts = 0; // Cached tabstop value.
- static const char_u *prev_line = NULL; // cached pointer to line.
+ static long prev_ts = 0L; // Cached tabstop value.
+ static const char *prev_line = NULL; // cached pointer to line.
static varnumber_T prev_tick = 0; // Changedtick of cached value.
- static long *prev_vts = NULL; // Cached vartabs values.
+ static long *prev_vts = NULL; // Cached vartabs values.
+ static int prev_list = 0; // cached list value
+ static int prev_listopt = 0; // cached w_p_briopt_list value
+ static char *prev_flp = NULL; // cached formatlistpat value
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);
- // used cached indent, unless pointer or 'tabstop' changed
+ // used cached indent, unless
+ // - line pointer changed
+ // - 'tabstop' changed
+ // - 'briopt_list changed' changed or
+ // - 'formatlistpattern' changed
if (prev_line != line || prev_ts != wp->w_buffer->b_p_ts
|| prev_tick != buf_get_changedtick(wp->w_buffer)
+ || prev_listopt != wp->w_briopt_list
+ || (prev_flp == NULL || (strcmp(prev_flp, get_flp_value(wp->w_buffer)) != 0))
|| prev_vts != wp->w_buffer->b_p_vts_array) {
prev_line = line;
prev_ts = wp->w_buffer->b_p_ts;
prev_tick = buf_get_changedtick(wp->w_buffer);
prev_vts = wp->w_buffer->b_p_vts_array;
- prev_indent = get_indent_str_vtab(line,
- wp->w_buffer->b_p_ts,
- wp->w_buffer->b_p_vts_array,
- wp->w_p_list);
+ if (wp->w_briopt_vcol == 0) {
+ prev_indent = get_indent_str_vtab(line,
+ wp->w_buffer->b_p_ts,
+ wp->w_buffer->b_p_vts_array,
+ wp->w_p_list);
+ }
+ prev_listopt = wp->w_briopt_list;
+ prev_list = 0;
+ xfree(prev_flp);
+ prev_flp = xstrdup(get_flp_value(wp->w_buffer));
+ // add additional indent for numbered lists
+ if (wp->w_briopt_list != 0 && wp->w_briopt_vcol == 0) {
+ regmatch_T regmatch = {
+ .regprog = vim_regcomp(prev_flp, RE_MAGIC + RE_STRING + RE_AUTO + RE_STRICT),
+ };
+ if (regmatch.regprog != NULL) {
+ regmatch.rm_ic = false;
+ if (vim_regexec(&regmatch, line, 0)) {
+ if (wp->w_briopt_list > 0) {
+ prev_list += wp->w_briopt_list;
+ } else {
+ prev_indent = (int)(*regmatch.endp - *regmatch.startp);
+ }
+ }
+ vim_regfree(regmatch.regprog);
+ }
+ }
+ }
+ if (wp->w_briopt_vcol != 0) {
+ // column value has priority
+ bri = wp->w_briopt_vcol;
+ prev_list = 0;
+ } else {
+ bri = prev_indent + wp->w_briopt_shift;
}
- bri = prev_indent + wp->w_briopt_shift;
// Add offset for number column, if 'n' is in 'cpoptions'
bri += win_col_off2(wp);
// add additional indent for numbered lists
- if (wp->w_briopt_list != 0) {
- regmatch_T regmatch = {
- .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, (char *)line, 0)) {
- if (wp->w_briopt_list > 0) {
- bri += wp->w_briopt_list;
- } else {
- bri = (int)(*regmatch.endp - *regmatch.startp);
- }
- }
- vim_regfree(regmatch.regprog);
- }
+ if (wp->w_briopt_list > 0) {
+ bri += prev_list;
}
// indent minus the length of the showbreak string
if (wp->w_briopt_sbr) {
- bri -= vim_strsize((char *)get_showbreak_value(wp));
+ bri -= vim_strsize(get_showbreak_value(wp));
}
// never indent past left window margin
@@ -811,7 +899,7 @@ int get_breakindent_win(win_T *wp, char_u *line)
// the line.
int inindent(int extra)
{
- char_u *ptr;
+ char *ptr;
colnr_T col;
for (col = 0, ptr = get_cursor_line_ptr(); ascii_iswhite(*ptr); col++) {
@@ -820,9 +908,8 @@ int inindent(int extra)
if (col >= curwin->w_cursor.col + extra) {
return true;
- } else {
- return false;
}
+ return false;
}
/// @return true if the conditions are OK for smart indenting.
@@ -831,7 +918,198 @@ 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'.
+/// Give a "resulting text too long" error and maybe set got_int.
+static void emsg_text_too_long(void)
+{
+ emsg(_(e_resulting_text_too_long));
+ // when not inside a try/catch set got_int to break out of any loop
+ if (trylevel == 0) {
+ got_int = true;
+ }
+}
+
+/// ":retab".
+void ex_retab(exarg_T *eap)
+{
+ linenr_T lnum;
+ bool got_tab = false;
+ long num_spaces = 0;
+ long num_tabs;
+ long len;
+ long col;
+ long vcol;
+ long start_col = 0; // For start of white-space string
+ long start_vcol = 0; // For start of white-space string
+ long old_len;
+ 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 *new_ts_str; // string value of tab argument
+
+ int save_list;
+ linenr_T first_line = 0; // first changed line
+ linenr_T last_line = 0; // last changed line
+
+ save_list = curwin->w_p_list;
+ curwin->w_p_list = 0; // don't want list mode here
+
+ new_ts_str = eap->arg;
+ if (!tabstop_set(eap->arg, &new_vts_array)) {
+ return;
+ }
+ while (ascii_isdigit(*(eap->arg)) || *(eap->arg) == ',') {
+ (eap->arg)++;
+ }
+
+ // This ensures that either new_vts_array and new_ts_str are freshly
+ // allocated, or new_vts_array points to an existing array and new_ts_str
+ // is null.
+ if (new_vts_array == NULL) {
+ new_vts_array = curbuf->b_p_vts_array;
+ new_ts_str = NULL;
+ } else {
+ 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);
+ col = 0;
+ vcol = 0;
+ did_undo = false;
+ for (;;) {
+ if (ascii_iswhite(ptr[col])) {
+ if (!got_tab && num_spaces == 0) {
+ // First consecutive white-space
+ start_vcol = vcol;
+ start_col = col;
+ }
+ if (ptr[col] == ' ') {
+ num_spaces++;
+ } else {
+ got_tab = true;
+ }
+ } else {
+ if (got_tab || (eap->forceit && num_spaces > 1)) {
+ // Retabulate this string of white-space
+
+ // len is virtual length of white string
+ len = num_spaces = vcol - start_vcol;
+ num_tabs = 0;
+ if (!curbuf->b_p_et) {
+ int t, s;
+
+ tabstop_fromto((colnr_T)start_vcol, (colnr_T)vcol,
+ curbuf->b_p_ts, new_vts_array, &t, &s);
+ num_tabs = t;
+ num_spaces = s;
+ }
+ if (curbuf->b_p_et || got_tab
+ || (num_spaces + num_tabs < len)) {
+ if (did_undo == false) {
+ did_undo = true;
+ if (u_save((linenr_T)(lnum - 1),
+ (linenr_T)(lnum + 1)) == FAIL) {
+ new_line = NULL; // flag out-of-memory
+ break;
+ }
+ }
+
+ // len is actual number of white characters used
+ len = num_spaces + num_tabs;
+ old_len = (long)strlen(ptr);
+ const long new_len = old_len - col + start_col + len + 1;
+ if (new_len <= 0 || new_len >= MAXCOL) {
+ emsg_text_too_long();
+ break;
+ }
+ new_line = xmalloc((size_t)new_len);
+
+ if (start_col > 0) {
+ memmove(new_line, ptr, (size_t)start_col);
+ }
+ memmove(new_line + start_col + len,
+ ptr + col, (size_t)(old_len - col + 1));
+ ptr = new_line + start_col;
+ for (col = 0; col < len; col++) {
+ ptr[col] = (col < num_tabs) ? '\t' : ' ';
+ }
+ if (ml_replace(lnum, new_line, false) == OK) {
+ // "new_line" may have been copied
+ new_line = curbuf->b_ml.ml_line_ptr;
+ extmark_splice_cols(curbuf, lnum - 1, 0, (colnr_T)old_len,
+ (colnr_T)new_len - 1, kExtmarkUndo);
+ }
+ if (first_line == 0) {
+ first_line = lnum;
+ }
+ last_line = lnum;
+ ptr = new_line;
+ col = start_col + len;
+ }
+ }
+ got_tab = false;
+ num_spaces = 0;
+ }
+ if (ptr[col] == NUL) {
+ break;
+ }
+ vcol += win_chartabsize(curwin, ptr + col, (colnr_T)vcol);
+ if (vcol >= MAXCOL) {
+ emsg_text_too_long();
+ break;
+ }
+ col += utfc_ptr2len(ptr + col);
+ }
+ if (new_line == NULL) { // out of memory
+ break;
+ }
+ line_breakcheck();
+ }
+ if (got_int) {
+ emsg(_(e_interr));
+ }
+
+ // If a single value was given then it can be considered equal to
+ // either the value of 'tabstop' or the value of 'vartabstop'.
+ if (tabstop_count(curbuf->b_p_vts_array) == 0
+ && tabstop_count(new_vts_array) == 1
+ && curbuf->b_p_ts == tabstop_first(new_vts_array)) {
+ // not changed
+ } else if (tabstop_count(curbuf->b_p_vts_array) > 0
+ && tabstop_eq(curbuf->b_p_vts_array, new_vts_array)) {
+ // not changed
+ } else {
+ redraw_curbuf_later(UPD_NOT_VALID);
+ }
+ if (first_line != 0) {
+ changed_lines(first_line, 0, last_line + 1, 0L, true);
+ }
+
+ curwin->w_p_list = save_list; // restore 'list'
+
+ if (new_ts_str != NULL) { // set the new tabstop
+ // If 'vartabstop' is in use or if the value given to retab has more
+ // than one tabstop then update 'vartabstop'.
+ 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);
+ curbuf->b_p_vts_array = new_vts_array;
+ xfree(old_vts_ary);
+ } else {
+ // 'vartabstop' wasn't in use and a single value was given to
+ // retab then update 'tabstop'.
+ curbuf->b_p_ts = tabstop_first(new_vts_array);
+ xfree(new_vts_array);
+ }
+ xfree(new_ts_str);
+ }
+ coladvance(curwin->w_curswant);
+
+ u_clearline();
+}
+
+/// Get indent level from 'indentexpr'.
int get_expr_indent(void)
{
int indent = -1;
@@ -855,8 +1133,8 @@ 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((char *)inde_copy);
+ char *inde_copy = xstrdup(curbuf->b_p_inde);
+ indent = (int)eval_to_number(inde_copy);
xfree(inde_copy);
if (use_sandbox) {
@@ -875,6 +1153,12 @@ int get_expr_indent(void)
check_cursor();
State = save_State;
+ // Reset did_throw, unless 'debug' has "throw" and inside a try/catch.
+ if (did_throw && (vim_strchr(p_debug, 't') == NULL || trylevel == 0)) {
+ handle_did_throw();
+ did_throw = false;
+ }
+
// If there is an error, just keep the current indent.
if (indent < 0) {
indent = get_indent();
@@ -901,7 +1185,7 @@ int get_lisp_indent(void)
{
pos_T *pos, realpos, paren;
int amount;
- char_u *that;
+ char *that;
colnr_T col;
colnr_T firsttry;
int parencount;
@@ -991,14 +1275,16 @@ int get_lisp_indent(void)
if (vi_lisp && (get_indent() == 0)) {
amount = 2;
} else {
- char_u *line = that;
-
- amount = 0;
-
- while (*that && col) {
- amount += lbr_chartabsize_adv(line, &that, (colnr_T)amount);
+ char *line = that;
+ chartabsize_T cts;
+ init_chartabsize_arg(&cts, curwin, pos->lnum, 0, line, line);
+ while (*cts.cts_ptr != NUL && col > 0) {
+ cts.cts_vcol += lbr_chartabsize_adv(&cts);
col--;
}
+ amount = cts.cts_vcol;
+ that = cts.cts_ptr;
+ clear_chartabsize_arg(&cts);
// Some keywords require "body" indenting rules (the
// non-standard-lisp ones are Scheme special forms):
@@ -1014,10 +1300,15 @@ int get_lisp_indent(void)
}
firsttry = amount;
- while (ascii_iswhite(*that)) {
- amount += lbr_chartabsize(line, that, (colnr_T)amount);
- that++;
+ init_chartabsize_arg(&cts, curwin, (colnr_T)(that - line),
+ amount, line, that);
+ while (ascii_iswhite(*cts.cts_ptr)) {
+ cts.cts_vcol += lbr_chartabsize(&cts);
+ cts.cts_ptr++;
}
+ that = cts.cts_ptr;
+ amount = cts.cts_vcol;
+ clear_chartabsize_arg(&cts);
if (*that && (*that != ';')) {
// Not a comment line.
@@ -1030,33 +1321,39 @@ int get_lisp_indent(void)
parencount = 0;
quotecount = 0;
+ init_chartabsize_arg(&cts, curwin,
+ (colnr_T)(that - line), amount, line, that);
if (vi_lisp || ((*that != '"') && (*that != '\'')
- && (*that != '#') && ((*that < '0') || (*that > '9')))) {
- while (*that
- && (!ascii_iswhite(*that) || quotecount || parencount)
- && (!((*that == '(' || *that == '[')
+ && (*that != '#')
+ && (((uint8_t)(*that) < '0') || ((uint8_t)(*that) > '9')))) {
+ while (*cts.cts_ptr
+ && (!ascii_iswhite(*cts.cts_ptr) || quotecount || parencount)
+ && (!((*cts.cts_ptr == '(' || *cts.cts_ptr == '[')
&& !quotecount && !parencount && vi_lisp))) {
- if (*that == '"') {
+ if (*cts.cts_ptr == '"') {
quotecount = !quotecount;
}
- if (((*that == '(') || (*that == '[')) && !quotecount) {
+ if (((*cts.cts_ptr == '(') || (*cts.cts_ptr == '[')) && !quotecount) {
parencount++;
}
- if (((*that == ')') || (*that == ']')) && !quotecount) {
+ if (((*cts.cts_ptr == ')') || (*cts.cts_ptr == ']')) && !quotecount) {
parencount--;
}
- if ((*that == '\\') && (*(that + 1) != NUL)) {
- amount += lbr_chartabsize_adv(line, &that, (colnr_T)amount);
+ if ((*cts.cts_ptr == '\\') && (*(cts.cts_ptr + 1) != NUL)) {
+ cts.cts_vcol += lbr_chartabsize_adv(&cts);
}
- amount += lbr_chartabsize_adv(line, &that, (colnr_T)amount);
+ cts.cts_vcol += lbr_chartabsize_adv(&cts);
}
}
- while (ascii_iswhite(*that)) {
- amount += lbr_chartabsize(line, that, (colnr_T)amount);
- that++;
+ while (ascii_iswhite(*cts.cts_ptr)) {
+ cts.cts_vcol += lbr_chartabsize(&cts);
+ cts.cts_ptr++;
}
+ that = cts.cts_ptr;
+ amount = cts.cts_vcol;
+ clear_chartabsize_arg(&cts);
if (!*that || (*that == ';')) {
amount = firsttry;
@@ -1073,19 +1370,63 @@ int get_lisp_indent(void)
return amount;
}
-static int lisp_match(char_u *p)
+static int lisp_match(char *p)
{
- char_u buf[LSIZE];
+ char buf[LSIZE];
int len;
- char *word = (char *)(*curbuf->b_p_lw != NUL ? curbuf->b_p_lw : p_lispwords);
+ char *word = *curbuf->b_p_lw != NUL ? curbuf->b_p_lw : p_lispwords;
while (*word != NUL) {
- (void)copy_option_part(&word, (char *)buf, LSIZE, ",");
- len = (int)STRLEN(buf);
+ (void)copy_option_part(&word, buf, LSIZE, ",");
+ len = (int)strlen(buf);
- if ((STRNCMP(buf, p, len) == 0) && (p[len] == ' ')) {
+ if ((strncmp(buf, p, (size_t)len) == 0) && ascii_iswhite_or_nul(p[len])) {
return true;
}
}
return false;
}
+
+/// Re-indent the current line, based on the current contents of it and the
+/// surrounding lines. Fixing the cursor position seems really easy -- I'm very
+/// confused what all the part that handles Control-T is doing that I'm not.
+/// "get_the_indent" should be get_c_indent, get_expr_indent or get_lisp_indent.
+void fixthisline(IndentGetter get_the_indent)
+{
+ int amount = get_the_indent();
+
+ if (amount < 0) {
+ return;
+ }
+
+ change_indent(INDENT_SET, amount, false, 0, true);
+ if (linewhite(curwin->w_cursor.lnum)) {
+ did_ai = true; // delete the indent if the line stays empty
+ }
+}
+
+/// Return true if 'indentexpr' should be used for Lisp indenting.
+/// Caller may want to check 'autoindent'.
+bool use_indentexpr_for_lisp(void)
+{
+ return curbuf->b_p_lisp
+ && *curbuf->b_p_inde != NUL
+ && strcmp(curbuf->b_p_lop, "expr:1") == 0;
+}
+
+/// Fix indent for 'lisp' and 'cindent'.
+void fix_indent(void)
+{
+ if (p_paste) {
+ return; // no auto-indenting when 'paste' is set
+ }
+ if (curbuf->b_p_lisp && curbuf->b_p_ai) {
+ if (use_indentexpr_for_lisp()) {
+ do_c_expr_indent();
+ } else {
+ fixthisline(get_lisp_indent);
+ }
+ } else if (cindent_on()) {
+ do_c_expr_indent();
+ }
+}
diff --git a/src/nvim/indent.h b/src/nvim/indent.h
index f96732bf1c..f807bbb42b 100644
--- a/src/nvim/indent.h
+++ b/src/nvim/indent.h
@@ -3,6 +3,8 @@
#include "nvim/vim.h"
+typedef int (*IndentGetter)(void);
+
// flags for set_indent()
#define SIN_CHANGED 1 // call changed_bytes() when line changed
#define SIN_INSERT 2 // insert indent before existing text
diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c
index 34a3de4f78..1c771073b2 100644
--- a/src/nvim/indent_c.c
+++ b/src/nvim/indent_c.c
@@ -1,19 +1,26 @@
// 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/ascii.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/edit.h"
+#include "nvim/globals.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
+#include "nvim/macros.h"
#include "nvim/mark.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/option.h"
+#include "nvim/pos.h"
#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/vim.h"
@@ -27,11 +34,9 @@ typedef struct {
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "indent_c.c.generated.h"
#endif
-/*
- * Find the start of a comment, not knowing if we are in a comment right now.
- * Search starts at w_cursor.lnum and goes backwards.
- * Return NULL when not inside a comment.
- */
+// Find the start of a comment, not knowing if we are in a comment right now.
+// Search starts at w_cursor.lnum and goes backwards.
+// Return NULL when not inside a comment.
static pos_T *ind_find_start_comment(void) // XXX
{
return find_start_comment(curbuf->b_ind_maxcomment);
@@ -48,10 +53,8 @@ pos_T *find_start_comment(int ind_maxcomment) // XXX
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.
- */
+ // 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.
if (!is_pos_in_string(ml_get(pos->lnum), pos->col)) {
break;
}
@@ -97,11 +100,9 @@ static pos_T *ind_find_start_CORS(linenr_T *is_raw)
return comment_pos;
}
-/*
- * Find the start of a raw string, not knowing if we are in one right now.
- * Search starts at w_cursor.lnum and goes backwards.
- * Return NULL when not inside a raw string.
- */
+// Find the start of a raw string, not knowing if we are in one right now.
+// Search starts at w_cursor.lnum and goes backwards.
+// Return NULL when not inside a raw string.
static pos_T *find_start_rawstring(int ind_maxcomment) // XXX
{
pos_T *pos;
@@ -127,17 +128,13 @@ static pos_T *find_start_rawstring(int ind_maxcomment) // XXX
return pos;
}
-/*
- * Skip to the end of a "string" and a 'c' character.
- * If there is no string or character, return argument unmodified.
- */
-static const char_u *skip_string(const char_u *p)
+// Skip to the end of a "string" and a 'c' character.
+// If there is no string or character, return argument unmodified.
+static const char *skip_string(const char *p)
{
int i;
- /*
- * We loop, because strings may be concatenated: "date""time".
- */
+ // We loop, because strings may be concatenated: "date""time".
for (;; p++) {
if (p[0] == '\'') { // 'c' or '\n' or '\000'
if (p[1] == NUL) { // ' at end of line
@@ -167,14 +164,14 @@ static const char_u *skip_string(const char_u *p)
}
} else if (p[0] == 'R' && p[1] == '"') {
// Raw string: R"[delim](...)[delim]"
- const char *delim = (char *)p + 2;
- const char *paren = vim_strchr((char *)delim, '(');
+ const char *delim = p + 2;
+ const char *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
+ if (p[0] == ')' && strncmp(p + 1, delim, (size_t)delim_len) == 0
&& p[delim_len + 1] == '"') {
p += delim_len + 1;
break;
@@ -194,9 +191,9 @@ static const char_u *skip_string(const char_u *p)
}
/// @returns true if "line[col]" is inside a C string.
-int is_pos_in_string(const char_u *line, colnr_T col)
+int is_pos_in_string(const char *line, colnr_T col)
{
- const char_u *p;
+ const char *p;
for (p = line; *p && (colnr_T)(p - line) < col; p++) {
p = skip_string(p);
@@ -204,29 +201,24 @@ int is_pos_in_string(const char_u *line, colnr_T col)
return !((colnr_T)(p - line) <= col);
}
-/*
- * Functions for C-indenting.
- * Most of this originally comes from Eric Fischer.
- */
-/*
- * 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(const char_u *line)
+// Functions for C-indenting.
+// Most of this originally comes from Eric Fischer.
+
+// 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(const char *line)
{
bool retval = false;
- size_t cinw_len = STRLEN(curbuf->b_p_cinw) + 1;
- char_u *cinw_buf = xmalloc(cinw_len);
- line = (char_u *)skipwhite((char *)line);
+ size_t cinw_len = strlen(curbuf->b_p_cinw) + 1;
+ char *cinw_buf = xmalloc(cinw_len);
+ line = skipwhite(line);
- for (char *cinw = (char *)curbuf->b_p_cinw; *cinw;) {
- size_t len = copy_option_part(&cinw, (char *)cinw_buf, cinw_len, ",");
- if (STRNCMP(line, cinw_buf, len) == 0
- && (!vim_iswordc(line[len]) || !vim_iswordc(line[len - 1]))) {
+ for (char *cinw = curbuf->b_p_cinw; *cinw;) {
+ size_t len = copy_option_part(&cinw, cinw_buf, cinw_len, ",");
+ if (strncmp(line, cinw_buf, len) == 0
+ && (!vim_iswordc((uint8_t)line[len]) || !vim_iswordc((uint8_t)line[len - 1]))) {
retval = true;
break;
}
@@ -237,21 +229,19 @@ bool cin_is_cinword(const char_u *line)
return retval;
}
-/*
- * Skip over white space and C comments within the line.
- * Also skip over Perl/shell comments if desired.
- */
-static const char_u *cin_skipcomment(const char_u *s)
+// Skip over white space and C comments within the line.
+// Also skip over Perl/shell comments if desired.
+static const char *cin_skipcomment(const char *s)
{
while (*s) {
- const char_u *prev_s = s;
+ const char *prev_s = s;
- s = (char_u *)skipwhite((char *)s);
+ s = skipwhite(s);
- /* Perl/shell # comment comment continues until eol. Require a space
- * before # to avoid recognizing $#array. */
+ // Perl/shell # comment comment continues until eol. Require a space
+ // before # to avoid recognizing $#array.
if (curbuf->b_ind_hash_comment != 0 && s != prev_s && *s == '#') {
- s += STRLEN(s);
+ s += strlen(s);
break;
}
if (*s != '/') {
@@ -259,7 +249,7 @@ static const char_u *cin_skipcomment(const char_u *s)
}
s++;
if (*s == '/') { // slash-slash comment continues till eol
- s += STRLEN(s);
+ s += strlen(s);
break;
}
if (*s != '*') {
@@ -275,28 +265,24 @@ static const char_u *cin_skipcomment(const char_u *s)
return s;
}
-/*
- * Return TRUE if there is no code at *s. White space and comments are
- * not considered code.
- */
-static int cin_nocode(const char_u *s)
+/// Return true if there is no code at *s. White space and comments are
+/// not considered code.
+static int cin_nocode(const char *s)
{
return *cin_skipcomment(s) == NUL;
}
-/*
- * Check previous lines for a "//" line comment, skipping over blank lines.
- */
+// Check previous lines for a "//" line comment, skipping over blank lines.
static pos_T *find_line_comment(void) // XXX
{
static pos_T pos;
- char_u *line;
- char_u *p;
+ char *line;
+ char *p;
pos = curwin->w_cursor;
while (--pos.lnum > 0) {
line = ml_get(pos.lnum);
- p = (char_u *)skipwhite((char *)line);
+ p = skipwhite(line);
if (cin_islinecomment(p)) {
pos.col = (int)(p - line);
return &pos;
@@ -309,21 +295,21 @@ static pos_T *find_line_comment(void) // XXX
}
/// Checks if `text` starts with "key:".
-static bool cin_has_js_key(const char_u *text)
+static bool cin_has_js_key(const char *text)
{
- const char_u *s = (char_u *)skipwhite((char *)text);
+ const char *s = skipwhite(text);
- char_u quote = 0;
+ char quote = 0;
if (*s == '\'' || *s == '"') {
// can be 'key': or "key":
quote = *s;
s++;
}
- if (!vim_isIDc(*s)) { // need at least one ID character
+ if (!vim_isIDc((uint8_t)(*s))) { // need at least one ID character
return false;
}
- while (vim_isIDc(*s)) {
+ while (vim_isIDc((uint8_t)(*s))) {
s++;
}
if (*s && *s == quote) {
@@ -338,14 +324,14 @@ static bool cin_has_js_key(const 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(const char_u **s)
+static bool cin_islabel_skip(const char **s)
FUNC_ATTR_NONNULL_ALL
{
- if (!vim_isIDc(**s)) { // need at least one ID character
+ if (!vim_isIDc((uint8_t)(**s))) { // need at least one ID character
return false;
}
- while (vim_isIDc(**s)) {
+ while (vim_isIDc((uint8_t)(**s))) {
(*s)++;
}
@@ -359,7 +345,7 @@ static bool cin_islabel_skip(const char_u **s)
// Note: curwin->w_cursor must be where we are looking for the label.
bool cin_islabel(void) // XXX
{
- const char_u *s = cin_skipcomment(get_cursor_line_ptr());
+ const char *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.
@@ -369,26 +355,23 @@ bool cin_islabel(void) // XXX
if (cin_isscopedecl(s)) {
return false;
}
+
if (!cin_islabel_skip(&s)) {
return false;
}
- /*
- * Only accept a label if the previous line is terminated or is a case
- * label.
- */
+ // Only accept a label if the previous line is terminated or is a case
+ // label.
pos_T cursor_save;
pos_T *trypos;
- const char_u *line;
+ const char *line;
cursor_save = curwin->w_cursor;
while (curwin->w_cursor.lnum > 1) {
- --curwin->w_cursor.lnum;
+ curwin->w_cursor.lnum--;
- /*
- * If we're in a comment or raw string now, skip to the start of
- * it.
- */
+ // If we're in a comment or raw string now, skip to the start of
+ // it.
curwin->w_cursor.col = 0;
if ((trypos = ind_find_start_CORS(NULL)) != NULL) { // XXX
curwin->w_cursor = *trypos;
@@ -403,7 +386,7 @@ bool cin_islabel(void) // XXX
}
curwin->w_cursor = cursor_save;
- if (cin_isterminated(line, TRUE, FALSE)
+ if (cin_isterminated(line, true, false)
|| cin_isscopedecl(line)
|| cin_iscase(line, true)
|| (cin_islabel_skip(&line) && cin_nocode(line))) {
@@ -415,14 +398,12 @@ bool cin_islabel(void) // XXX
return true; // label at start of file???
}
-/*
- * Recognize structure initialization and enumerations:
- * "[typedef] [static|public|protected|private] enum"
- * "[typedef] [static|public|protected|private] = {"
- */
+// Recognize structure initialization and enumerations:
+// "[typedef] [static|public|protected|private] enum"
+// "[typedef] [static|public|protected|private] = {"
static int cin_isinit(void)
{
- const char_u *s;
+ const char *s;
static char *skip[] = { "static", "public", "protected", "private" };
s = cin_skipcomment(get_cursor_line_ptr());
@@ -434,7 +415,7 @@ static int cin_isinit(void)
for (;;) {
int i, l;
- for (i = 0; i < (int)ARRAY_SIZE(skip); ++i) {
+ for (i = 0; i < (int)ARRAY_SIZE(skip); i++) {
l = (int)strlen(skip[i]);
if (cin_starts_with(s, skip[i])) {
s = cin_skipcomment(s + l);
@@ -451,21 +432,21 @@ static int cin_isinit(void)
return true;
}
- if (cin_ends_in(s, (char_u *)"=", (char_u *)"{")) {
+ if (cin_ends_in(s, "=", "{")) {
return true;
}
- return FALSE;
+ return false;
}
/// 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)
+bool cin_iscase(const char *s, bool strict)
{
s = cin_skipcomment(s);
if (cin_starts_with(s, "case")) {
- for (s += 4; *s; ++s) {
+ for (s += 4; *s; s++) {
s = cin_skipcomment(s);
if (*s == NUL) {
break;
@@ -485,9 +466,8 @@ bool cin_iscase(const char_u *s, bool strict)
// JS etc.
if (strict) {
return false; // stop at string
- } else {
- return true;
}
+ return true;
}
}
return false;
@@ -499,30 +479,28 @@ bool cin_iscase(const char_u *s, bool strict)
return false;
}
-/*
- * Recognize a "default" switch label.
- */
-static int cin_isdefault(const char_u *s)
+// Recognize a "default" switch label.
+static int cin_isdefault(const char *s)
{
- return STRNCMP(s, "default", 7) == 0
+ return strncmp(s, "default", 7) == 0
&& *(s = cin_skipcomment(s + 7)) == ':'
&& s[1] != ':';
}
/// Recognize a scope declaration label set in 'cinscopedecls'.
-bool cin_isscopedecl(const char_u *p)
+bool cin_isscopedecl(const char *p)
{
- const char_u *s = cin_skipcomment(p);
+ const char *s = cin_skipcomment(p);
- const size_t cinsd_len = STRLEN(curbuf->b_p_cinsd) + 1;
- char_u *cinsd_buf = xmalloc(cinsd_len);
+ const size_t cinsd_len = strlen(curbuf->b_p_cinsd) + 1;
+ char *cinsd_buf = xmalloc(cinsd_len);
bool found = false;
- for (char *cinsd = (char *)curbuf->b_p_cinsd; *cinsd;) {
- const size_t len = copy_option_part(&cinsd, (char *)cinsd_buf, cinsd_len, ",");
- if (STRNCMP(s, cinsd_buf, len) == 0) {
- const char_u *skip = cin_skipcomment(s + len);
+ for (char *cinsd = curbuf->b_p_cinsd; *cinsd;) {
+ const size_t len = copy_option_part(&cinsd, cinsd_buf, cinsd_len, ",");
+ if (strncmp(s, cinsd_buf, len) == 0) {
+ const char *skip = cin_skipcomment(s + len);
if (*skip == ':' && skip[1] != ':') {
found = true;
break;
@@ -539,33 +517,33 @@ bool cin_isscopedecl(const char_u *p)
#define FIND_NAMESPACE_LIM 20
// Recognize a "namespace" scope declaration.
-static bool cin_is_cpp_namespace(const char_u *s)
+static bool cin_is_cpp_namespace(const char *s)
{
- const char_u *p;
+ const char *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, "inline", 6) == 0 && (s[6] == NUL || !vim_iswordc((uint8_t)s[6]))) {
+ s = cin_skipcomment(skipwhite(s + 6));
}
- if (STRNCMP(s, "namespace", 9) == 0 && (s[9] == NUL || !vim_iswordc(s[9]))) {
- p = cin_skipcomment((char_u *)skipwhite((char *)s + 9));
+ if (strncmp(s, "namespace", 9) == 0 && (s[9] == NUL || !vim_iswordc((uint8_t)s[9]))) {
+ p = cin_skipcomment(skipwhite(s + 9));
while (*p != NUL) {
if (ascii_iswhite(*p)) {
has_name = true; // found end of a name
- p = cin_skipcomment((char_u *)skipwhite((char *)p));
+ p = cin_skipcomment(skipwhite(p));
} else if (*p == '{') {
break;
- } else if (vim_iswordc(*p)) {
+ } else if (vim_iswordc((uint8_t)(*p))) {
has_name_start = true;
if (has_name) {
return false; // word character after skipping past name
}
p++;
- } else if (p[0] == ':' && p[1] == ':' && vim_iswordc(p[2])) {
+ } else if (p[0] == ':' && p[1] == ':' && vim_iswordc((uint8_t)p[2])) {
if (!has_name_start || has_name) {
return false;
}
@@ -580,15 +558,13 @@ static bool cin_is_cpp_namespace(const char_u *s)
return false;
}
-/*
- * Return a pointer to the first non-empty non-comment character after a ':'.
- * Return NULL if not found.
- * case 234: a = b;
- * ^
- */
-static const char_u *after_label(const char_u *l)
+// Return a pointer to the first non-empty non-comment character after a ':'.
+// Return NULL if not found.
+// case 234: a = b;
+// ^
+static const char *after_label(const char *l)
{
- for (; *l; ++l) {
+ for (; *l; l++) {
if (*l == ':') {
if (l[1] == ':') { // skip over "::" for C++
l++;
@@ -609,16 +585,14 @@ static const char_u *after_label(const char_u *l)
return l;
}
-/*
- * Get indent of line "lnum", skipping a label.
- * Return 0 if there is nothing after the label.
- */
+// Get indent of line "lnum", skipping a label.
+// Return 0 if there is nothing after the label.
static int get_indent_nolabel(linenr_T lnum) // XXX
{
- const char_u *l;
+ const char *l;
pos_T fp;
colnr_T col;
- const char_u *p;
+ const char *p;
l = ml_get(lnum);
p = after_label(l);
@@ -632,15 +606,13 @@ static int get_indent_nolabel(linenr_T lnum) // XXX
return (int)col;
}
-/*
- * 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)
- * ^
- */
-static int skip_label(linenr_T lnum, const char_u **pp)
+// 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)
+// ^
+static int skip_label(linenr_T lnum, const char **pp)
{
- const char_u *l;
+ const char *l;
int amount;
pos_T cursor_save;
@@ -664,78 +636,75 @@ static int skip_label(linenr_T lnum, const char_u **pp)
return amount;
}
-/*
- * 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"
- * Returns zero when it doesn't look like a declaration.
- */
+// 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"
+// Returns zero when it doesn't look like a declaration.
static int cin_first_id_amount(void)
{
- char_u *line, *p, *s;
+ char *line, *p, *s;
int len;
pos_T fp;
colnr_T col;
line = get_cursor_line_ptr();
- p = (char_u *)skipwhite((char *)line);
+ p = skipwhite(line);
len = (int)(skiptowhite(p) - p);
- if (len == 6 && STRNCMP(p, "static", 6) == 0) {
- p = (char_u *)skipwhite((char *)p + 6);
+ if (len == 6 && strncmp(p, "static", 6) == 0) {
+ p = skipwhite(p + 6);
len = (int)(skiptowhite(p) - p);
}
- 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]))) {
+ 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 ((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]))) {
p = s;
}
}
- for (len = 0; vim_isIDc(p[len]); len++) {}
+ for (len = 0; vim_isIDc((uint8_t)p[len]); len++) {}
if (len == 0 || !ascii_iswhite(p[len]) || cin_nocode(p)) {
return 0;
}
- p = (char_u *)skipwhite((char *)p + len);
+ p = skipwhite(p + len);
fp.lnum = curwin->w_cursor.lnum;
fp.col = (colnr_T)(p - line);
getvcol(curwin, &fp, &col, NULL, NULL);
return (int)col;
}
-/*
- * Return the indent of the first non-blank after an equal sign.
- * char *foo = "here";
- * Return zero if no (useful) equal sign found.
- * Return -1 if the line above "lnum" ends in a backslash.
- * foo = "asdf\
- * asdf\
- * here";
- */
+// Return the indent of the first non-blank after an equal sign.
+// char *foo = "here";
+// Return zero if no (useful) equal sign found.
+// Return -1 if the line above "lnum" ends in a backslash.
+// foo = "asdf{backslash}
+// asdf{backslash}
+// here";
static int cin_get_equal_amount(linenr_T lnum)
{
- const char_u *line;
- const char_u *s;
+ const char *line;
+ const char *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("=;{}\"'", *s) == NULL) {
+ s = ml_get(lnum);
+ line = s;
+ while (*s != NUL && vim_strchr("=;{}\"'", (uint8_t)(*s)) == NULL) {
if (cin_iscomment(s)) { // ignore comments
s = cin_skipcomment(s);
} else {
@@ -746,7 +715,7 @@ static int cin_get_equal_amount(linenr_T lnum)
return 0;
}
- s = (char_u *)skipwhite((char *)s + 1);
+ s = skipwhite(s + 1);
if (cin_nocode(s)) {
return 0;
}
@@ -761,35 +730,33 @@ static int cin_get_equal_amount(linenr_T lnum)
return (int)col;
}
-/*
- * Recognize a preprocessor statement: Any line that starts with '#'.
- */
-static int cin_ispreproc(const char_u *s)
+// Recognize a preprocessor statement: Any line that starts with '#'.
+static int cin_ispreproc(const char *s)
{
- if (*skipwhite((char *)s) == '#') {
+ if (*skipwhite(s) == '#') {
return true;
}
- return FALSE;
+ return false;
}
-/// Return TRUE if line "*pp" at "*lnump" is a preprocessor statement or a
+/// Return true if line "*pp" at "*lnump" is a preprocessor statement or a
/// 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(const char_u **pp, linenr_T *lnump, int *amount)
+static int cin_ispreproc_cont(const char **pp, linenr_T *lnump, int *amount)
{
- const char_u *line = *pp;
+ const char *line = *pp;
linenr_T lnum = *lnump;
int retval = false;
int candidate_amount = *amount;
- if (*line != NUL && line[STRLEN(line) - 1] == '\\') {
+ if (*line != NUL && line[strlen(line) - 1] == '\\') {
candidate_amount = get_indent_lnum(lnum);
}
for (;;) {
if (cin_ispreproc(line)) {
- retval = TRUE;
+ retval = true;
*lnump = lnum;
break;
}
@@ -797,7 +764,7 @@ static int cin_ispreproc_cont(const char_u **pp, linenr_T *lnump, int *amount)
break;
}
line = ml_get(--lnum);
- if (*line == NUL || line[STRLEN(line) - 1] != '\\') {
+ if (*line == NUL || line[strlen(line) - 1] != '\\') {
break;
}
}
@@ -811,18 +778,14 @@ static int cin_ispreproc_cont(const char_u **pp, linenr_T *lnump, int *amount)
return retval;
}
-/*
- * Recognize the start of a C or C++ comment.
- */
-static int cin_iscomment(const char_u *p)
+// Recognize the start of a C or C++ comment.
+static int cin_iscomment(const char *p)
{
return p[0] == '/' && (p[1] == '*' || p[1] == '/');
}
-/*
- * Recognize the start of a "//" comment.
- */
-static int cin_islinecomment(const char_u *p)
+// Recognize the start of a "//" comment.
+static int cin_islinecomment(const char *p)
{
return p[0] == '/' && p[1] == '/';
}
@@ -838,11 +801,11 @@ static int cin_islinecomment(const char_u *p)
///
/// @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)
+static char cin_isterminated(const char *s, int incl_open, int incl_comma)
{
- char_u found_start = 0;
+ char found_start = 0;
unsigned n_open = 0;
- int is_else = FALSE;
+ int is_else = false;
s = cin_skipcomment(s);
@@ -889,9 +852,9 @@ static char_u cin_isterminated(const char_u *s, int incl_open, int incl_comma)
/// 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(const char_u **sp, linenr_T first_lnum, linenr_T min_lnum)
+static int cin_isfuncdecl(const char **sp, linenr_T first_lnum, linenr_T min_lnum)
{
- const char_u *s;
+ const char *s;
linenr_T lnum = first_lnum;
linenr_T save_lnum = curwin->w_cursor.lnum;
int retval = false;
@@ -952,7 +915,7 @@ static int cin_isfuncdecl(const char_u **sp, linenr_T first_lnum, linenr_T min_l
// defined(y)
lnum = first_lnum - 1;
s = ml_get(lnum);
- if (*s == NUL || s[STRLEN(s) - 1] != '\\') {
+ if (*s == NUL || s[strlen(s) - 1] != '\\') {
retval = true;
}
goto done;
@@ -960,10 +923,10 @@ static int cin_isfuncdecl(const char_u **sp, linenr_T first_lnum, linenr_T min_l
if ((*s == ',' && cin_nocode(s + 1)) || s[1] == NUL || cin_nocode(s)) {
int comma = (*s == ',');
- /* ',' at the end: continue looking in the next line.
- * At the end: check for ',' in the next line, for this style:
- * func(arg1
- * , arg2) */
+ // ',' at the end: continue looking in the next line.
+ // At the end: check for ',' in the next line, for this style:
+ // func(arg1
+ // , arg2)
for (;;) {
if (lnum >= curbuf->b_ml.ml_line_count) {
break;
@@ -978,7 +941,7 @@ static int cin_isfuncdecl(const char_u **sp, linenr_T first_lnum, linenr_T min_l
}
// Require a comma at end of the line or a comma or ')' at the
// start of next line.
- s = (char_u *)skipwhite((char *)s);
+ s = skipwhite(s);
if (!just_started && (!comma && *s != ',' && *s != ')')) {
break;
}
@@ -999,30 +962,28 @@ done:
return retval;
}
-static int cin_isif(const char_u *p)
+static int cin_isif(const char *p)
{
- return STRNCMP(p, "if", 2) == 0 && !vim_isIDc(p[2]);
+ return strncmp(p, "if", 2) == 0 && !vim_isIDc((uint8_t)p[2]);
}
-static int cin_iselse(const char_u *p)
+static int cin_iselse(const char *p)
{
if (*p == '}') { // accept "} else"
p = cin_skipcomment(p + 1);
}
- return STRNCMP(p, "else", 4) == 0 && !vim_isIDc(p[4]);
+ return strncmp(p, "else", 4) == 0 && !vim_isIDc((uint8_t)p[4]);
}
-static int cin_isdo(const char_u *p)
+static int cin_isdo(const char *p)
{
- return STRNCMP(p, "do", 2) == 0 && !vim_isIDc(p[2]);
+ return strncmp(p, "do", 2) == 0 && !vim_isIDc((uint8_t)p[2]);
}
-/*
- * Check if this is a "while" that should have a matching "do".
- * 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(const char_u *p, linenr_T lnum) // XXX
+// Check if this is a "while" that should have a matching "do".
+// 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(const char *p, linenr_T lnum) // XXX
{
pos_T cursor_save;
pos_T *trypos;
@@ -1050,13 +1011,11 @@ static int cin_iswhileofdo(const char_u *p, linenr_T lnum) // XXX
return retval;
}
-/*
- * Check whether in "p" there is an "if", "for" or "while" before "*poffset".
- * Return 0 if there is none.
- * 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(const char_u *line, int *poffset)
+// Check whether in "p" there is an "if", "for" or "while" before "*poffset".
+// Return 0 if there is none.
+// 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(const char *line, int *poffset)
{
int offset = *poffset;
@@ -1068,19 +1027,19 @@ static int cin_is_if_for_while_before_offset(const char_u *line, int *poffset)
}
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;
}
}
@@ -1088,26 +1047,24 @@ static int cin_is_if_for_while_before_offset(const char_u *line, int *poffset)
return 0;
probablyFound:
- if (!offset || !vim_isIDc(line[offset - 1])) {
+ if (!offset || !vim_isIDc((uint8_t)line[offset - 1])) {
*poffset = offset;
return 1;
}
return 0;
}
-/*
- * Return TRUE if we are at the end of a do-while.
- * do
- * nothing;
- * while (foo
- * && bar); <-- here
- * Adjust the cursor to the line with "while".
- */
+/// Return true if we are at the end of a do-while.
+/// do
+/// nothing;
+/// while (foo
+/// && bar); <-- here
+/// Adjust the cursor to the line with "while".
static int cin_iswhileofdo_end(int terminated)
{
- const char_u *line;
- const char_u *p;
- const char_u *s;
+ const char *line;
+ const char *p;
+ const char *s;
pos_T *trypos;
int i;
@@ -1119,10 +1076,10 @@ static int cin_iswhileofdo_end(int terminated)
while (*p != NUL) {
p = cin_skipcomment(p);
if (*p == ')') {
- s = (char_u *)skipwhite((char *)p + 1);
+ s = skipwhite(p + 1);
if (*s == ';' && cin_nocode(s + 1)) {
- /* Found ");" at end of the line, now check there is "while"
- * before the matching '('. XXX */
+ // Found ");" at end of the line, now check there is "while"
+ // before the matching '('. XXX
i = (int)(p - line);
curwin->w_cursor.col = i;
trypos = find_match_paren(curbuf->b_ind_maxparen);
@@ -1133,7 +1090,7 @@ static int cin_iswhileofdo_end(int terminated)
}
if (cin_starts_with(s, "while")) {
curwin->w_cursor.lnum = trypos->lnum;
- return TRUE;
+ return true;
}
}
@@ -1146,34 +1103,32 @@ static int cin_iswhileofdo_end(int terminated)
p++;
}
}
- return FALSE;
+ return false;
}
-static int cin_isbreak(const char_u *p)
+static int cin_isbreak(const char *p)
{
- return STRNCMP(p, "break", 5) == 0 && !vim_isIDc(p[5]);
+ return strncmp(p, "break", 5) == 0 && !vim_isIDc((uint8_t)p[5]);
}
-/*
- * Find the position of a C++ base-class declaration or
- * constructor-initialization. eg:
- *
- * class MyClass :
- * baseClass <-- here
- * class MyClass : public baseClass,
- * anotherBaseClass <-- here (should probably lineup ??)
- * MyClass::MyClass(...) :
- * baseClass(...) <-- here (constructor-initialization)
- *
- * This is a lot of guessing. Watch out for "cond ? func() : foo".
- */
+// Find the position of a C++ base-class declaration or
+// constructor-initialization. eg:
+//
+// class MyClass :
+// baseClass <-- here
+// class MyClass : public baseClass,
+// anotherBaseClass <-- here (should probably lineup ??)
+// MyClass::MyClass(...) :
+// 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)
{
lpos_T *pos = &cached->lpos; // find position
- const char_u *s;
+ const char *s;
int class_or_struct, lookfor_ctor_init, cpp_base_class;
linenr_T lnum = curwin->w_cursor.lnum;
- const char_u *line = get_cursor_line_ptr();
+ const char *line = get_cursor_line_ptr();
if (pos->lnum <= lnum) {
return cached->found; // Use the cached result
@@ -1181,7 +1136,7 @@ static int cin_is_cpp_baseclass(cpp_baseclass_cache_T *cached)
pos->col = 0;
- s = (char_u *)skipwhite((char *)line);
+ s = skipwhite(line);
if (*s == '#') { // skip #define FOO x ? (x) : x
return false;
}
@@ -1190,24 +1145,23 @@ static int cin_is_cpp_baseclass(cpp_baseclass_cache_T *cached)
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)
- * {}
- */
+ 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)
+ // {}
while (lnum > 1) {
line = ml_get(lnum - 1);
- s = (char_u *)skipwhite((char *)line);
+ s = skipwhite(line);
if (*s == '#' || *s == NUL) {
break;
}
@@ -1254,13 +1208,13 @@ static int cin_is_cpp_baseclass(cpp_baseclass_cache_T *cached)
s = skip_string(s) + 1;
} else if (s[0] == ':') {
if (s[1] == ':') {
- /* skip double colon. It can't be a constructor
- * initialization any more */
- lookfor_ctor_init = FALSE;
+ // skip double colon. It can't be a constructor
+ // initialization any more
+ lookfor_ctor_init = false;
s = cin_skipcomment(s + 2);
} else if (lookfor_ctor_init || class_or_struct) {
- /* we have something found, that looks like the start of
- * cpp-base-class-declaration or constructor-initialization */
+ // we have something found, that looks like the start of
+ // cpp-base-class-declaration or constructor-initialization
cpp_base_class = true;
lookfor_ctor_init = class_or_struct = false;
pos->col = 0;
@@ -1268,10 +1222,10 @@ static int cin_is_cpp_baseclass(cpp_baseclass_cache_T *cached)
} 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;
+ } else if ((strncmp(s, "class", 5) == 0 && !vim_isIDc((uint8_t)s[5]))
+ || (strncmp(s, "struct", 6) == 0 && !vim_isIDc((uint8_t)s[6]))) {
+ class_or_struct = true;
+ lookfor_ctor_init = false;
if (*s == 'c') {
s = cin_skipcomment(s + 5);
@@ -1280,16 +1234,16 @@ static int cin_is_cpp_baseclass(cpp_baseclass_cache_T *cached)
}
} else {
if (s[0] == '{' || s[0] == '}' || s[0] == ';') {
- cpp_base_class = lookfor_ctor_init = class_or_struct = FALSE;
+ cpp_base_class = lookfor_ctor_init = class_or_struct = false;
} else if (s[0] == ')') {
- /* Constructor-initialization is assumed if we come across
- * something like "):" */
- class_or_struct = FALSE;
- lookfor_ctor_init = TRUE;
+ // Constructor-initialization is assumed if we come across
+ // something like "):"
+ class_or_struct = false;
+ lookfor_ctor_init = true;
} else if (s[0] == '?') {
// Avoid seeing '() :' after '?' as constructor init.
return false;
- } else if (!vim_isIDc(s[0])) {
+ } else if (!vim_isIDc((uint8_t)s[0])) {
// if it is not an identifier, we are wrong
class_or_struct = false;
lookfor_ctor_init = false;
@@ -1331,7 +1285,7 @@ static int get_baseclass_amount(int col)
&& (trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL) {
amount = get_indent_lnum(trypos->lnum); // XXX
}
- if (!cin_ends_in(get_cursor_line_ptr(), (char_u *)",", NULL)) {
+ if (!cin_ends_in(get_cursor_line_ptr(), ",", NULL)) {
amount += curbuf->b_ind_cpp_baseclass;
}
} else {
@@ -1345,23 +1299,21 @@ static int get_baseclass_amount(int col)
return amount;
}
-/*
- * Return TRUE if string "s" ends with the string "find", possibly followed by
- * white space and comments. Skip strings and comments.
- * Ignore "ignore" after "find" if it's not NULL.
- */
-static int cin_ends_in(const char_u *s, const char_u *find, const char_u *ignore)
+/// Return true if string "s" ends with the string "find", possibly followed by
+/// white space and comments. Skip strings and comments.
+/// Ignore "ignore" after "find" if it's not NULL.
+static int cin_ends_in(const char *s, const char *find, const char *ignore)
{
- const char_u *p = s;
- const char_u *r;
- int len = (int)STRLEN(find);
+ const char *p = s;
+ const char *r;
+ int len = (int)strlen(find);
while (*p != NUL) {
p = cin_skipcomment(p);
- if (STRNCMP(p, find, len) == 0) {
- 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 (strncmp(p, find, (size_t)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;
@@ -1371,31 +1323,29 @@ static int cin_ends_in(const char_u *s, const char_u *find, const char_u *ignore
p++;
}
}
- return FALSE;
+ return false;
}
-/*
- * Return TRUE when "s" starts with "word" and then a non-ID character.
- */
-static int cin_starts_with(const char_u *s, const char *word)
+/// Return true when "s" starts with "word" and then a non-ID character.
+static int cin_starts_with(const char *s, const char *word)
{
- int l = (int)STRLEN(word);
+ size_t l = strlen(word);
- return STRNCMP(s, word, l) == 0 && !vim_isIDc(s[l]);
+ return strncmp(s, word, l) == 0 && !vim_isIDc((uint8_t)s[l]);
}
/// Recognize a `extern "C"` or `extern "C++"` linkage specifications.
-static int cin_is_cpp_extern_c(const char_u *s)
+static int cin_is_cpp_extern_c(const char *s)
{
- const char_u *p;
+ const char *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((char_u *)skipwhite((char *)s + 6));
+ if (strncmp(s, "extern", 6) == 0 && (s[6] == NUL || !vim_iswordc((uint8_t)s[6]))) {
+ p = cin_skipcomment(skipwhite(s + 6));
while (*p != NUL) {
if (ascii_iswhite(*p)) {
- p = cin_skipcomment((char_u *)skipwhite((char *)p));
+ p = cin_skipcomment(skipwhite(p));
} else if (*p == '{') {
break;
} else if (p[0] == '"' && p[1] == 'C' && p[2] == '"') {
@@ -1420,17 +1370,16 @@ static int cin_is_cpp_extern_c(const char_u *s)
return false;
}
-/*
- * Skip strings, chars and comments until at or past "trypos".
- * Return the column found.
- */
+// Skip strings, chars and comments until at or past "trypos".
+// Return the column found.
static int cin_skip2pos(pos_T *trypos)
{
- const char_u *line;
- const char_u *p;
- const char_u *new_p;
+ const char *line;
+ const char *p;
+ const char *new_p;
- p = line = ml_get(trypos->lnum);
+ line = ml_get(trypos->lnum);
+ p = line;
while (*p && (colnr_T)(p - line) < trypos->col) {
if (cin_iscomment(p)) {
p = cin_skipcomment(p);
@@ -1446,11 +1395,10 @@ static int cin_skip2pos(pos_T *trypos)
return (int)(p - line);
}
-/*
- * Find the '{' at the start of the block we are in.
- * Return NULL if no match found.
- * Ignore a '{' that is in a comment, makes indenting the next three lines
- * work. */
+// Find the '{' at the start of the block we are in.
+// Return NULL if no match found.
+// Ignore a '{' that is in a comment, makes indenting the next three lines
+// work.
// foo()
// {
// }
@@ -1488,7 +1436,7 @@ 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 c, int ind_maxparen)
{
pos_T cursor_save;
pos_T *trypos;
@@ -1498,7 +1446,7 @@ static pos_T *find_match_char(char_u c, int ind_maxparen)
cursor_save = curwin->w_cursor;
ind_maxp_wk = ind_maxparen;
retry:
- if ((trypos = findmatchlimit(NULL, c, 0, ind_maxp_wk)) != NULL) {
+ if ((trypos = findmatchlimit(NULL, (uint8_t)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 - (cursor_save.lnum - trypos->lnum);
@@ -1550,12 +1498,10 @@ 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
- * matching paren above the cursor line doesn't find a match because of
- * looking a few lines further.
- */
+// Return ind_maxparen corrected for the difference in line number between the
+// cursor position and "startpos". This makes sure that searching for a
+// matching paren above the cursor line doesn't find a match because of
+// looking a few lines further.
static int corr_ind_maxparen(pos_T *startpos)
{
long n = (long)startpos->lnum - (long)curwin->w_cursor.lnum;
@@ -1566,14 +1512,12 @@ static int corr_ind_maxparen(pos_T *startpos)
return curbuf->b_ind_maxparen;
}
-/*
- * 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(const char_u *l, int start, int end)
+// 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(const char *l, char start, char end)
{
int i;
- int retval = FALSE;
+ int retval = false;
int open_count = 0;
curwin->w_cursor.col = 0; // default is start of line
@@ -1588,17 +1532,15 @@ static int find_last_paren(const char_u *l, int start, int end)
open_count--;
} else {
curwin->w_cursor.col = i;
- retval = TRUE;
+ retval = true;
}
}
}
return retval;
}
-/*
- * Parse 'cinoptions' and set the values in "curbuf".
- * Must be called when 'cinoptions', 'shiftwidth' and/or 'tabstop' changes.
- */
+// Parse 'cinoptions' and set the values in "curbuf".
+// Must be called when 'cinoptions', 'shiftwidth' and/or 'tabstop' changes.
void parse_cino(buf_T *buf)
{
char *p;
@@ -1607,39 +1549,37 @@ void parse_cino(buf_T *buf)
int fraction = 0;
int sw = get_sw_value(buf);
- /*
- * Set the default values.
- */
- /* Spaces from a block's opening brace the prevailing indent for that
- * block should be. */
+ // Set the default values.
+ // Spaces from a block's opening brace the prevailing indent for that
+ // block should be.
buf->b_ind_level = sw;
- /* Spaces from the edge of the line an open brace that's at the end of a
- * line is imagined to be. */
+ // Spaces from the edge of the line an open brace that's at the end of a
+ // line is imagined to be.
buf->b_ind_open_imag = 0;
- /* Spaces from the prevailing indent for a line that is not preceded by
- * an opening brace. */
+ // Spaces from the prevailing indent for a line that is not preceded by
+ // an opening brace.
buf->b_ind_no_brace = 0;
// Column where the first { of a function should be located }.
buf->b_ind_first_open = 0;
- /* Spaces from the prevailing indent a leftmost open brace should be
- * located. */
+ // Spaces from the prevailing indent a leftmost open brace should be
+ // located.
buf->b_ind_open_extra = 0;
- /* Spaces from the matching open brace (real location for one at the left
- * edge; imaginary location from one that ends a line) the matching close
- * brace should be located. */
+ // Spaces from the matching open brace (real location for one at the left
+ // edge; imaginary location from one that ends a line) the matching close
+ // brace should be located.
buf->b_ind_close_extra = 0;
- /* Spaces from the edge of the line an open brace sitting in the leftmost
- * column is imagined to be. */
+ // Spaces from the edge of the line an open brace sitting in the leftmost
+ // column is imagined to be.
buf->b_ind_open_left_imag = 0;
- /* Spaces jump labels should be shifted to the left if N is non-negative,
- * otherwise the jump label will be put to column 1. */
+ // Spaces jump labels should be shifted to the left if N is non-negative,
+ // otherwise the jump label will be put to column 1.
buf->b_ind_jump_label = -1;
// Spaces from the switch() indent a "case xx" label should be located.
@@ -1651,8 +1591,8 @@ void parse_cino(buf_T *buf)
// Lineup break at end of case in switch() with case label.
buf->b_ind_case_break = 0;
- /* Spaces from the class declaration indent a scope declaration label
- * should be located. */
+ // Spaces from the class declaration indent a scope declaration label
+ // should be located.
buf->b_ind_scopedecl = sw;
// Spaces from the scope declaration label code should be located.
@@ -1664,32 +1604,32 @@ void parse_cino(buf_T *buf)
// Amount a function type spec should be indented.
buf->b_ind_func_type = sw;
- /* Amount a cpp base class declaration or constructor initialization
- * should be indented. */
+ // Amount a cpp base class declaration or constructor initialization
+ // should be indented.
buf->b_ind_cpp_baseclass = sw;
- /* additional spaces beyond the prevailing indent a continuation line
- * should be located. */
+ // additional spaces beyond the prevailing indent a continuation line
+ // should be located.
buf->b_ind_continuation = sw;
// Spaces from the indent of the line with an unclosed parentheses.
buf->b_ind_unclosed = sw * 2;
- /* Spaces from the indent of the line with an unclosed parentheses, which
- * itself is also unclosed. */
+ // Spaces from the indent of the line with an unclosed parentheses, which
+ // itself is also unclosed.
buf->b_ind_unclosed2 = sw;
// Suppress ignoring spaces from the indent of a line starting with an
// unclosed parenthesis.
buf->b_ind_unclosed_noignore = 0;
- /* If the opening paren is the last nonwhite character on the line, and
- * b_ind_unclosed_wrapped is nonzero, use this indent relative to the outer
- * context (for very long lines). */
+ // If the opening paren is the last nonwhite character on the line, and
+ // b_ind_unclosed_wrapped is nonzero, use this indent relative to the outer
+ // context (for very long lines).
buf->b_ind_unclosed_wrapped = 0;
- /* Suppress ignoring white space when lining up with the character after
- * an unclosed parentheses. */
+ // Suppress ignoring white space when lining up with the character after
+ // an unclosed parentheses.
buf->b_ind_unclosed_whiteok = 0;
// Indent a closing parenthesis under the line start of the matching
@@ -1705,8 +1645,8 @@ void parse_cino(buf_T *buf)
// Spaces from the comment opener when there is nothing after it.
buf->b_ind_in_comment = 3;
- /* Boolean: if non-zero, use b_ind_in_comment even if there is something
- * after the comment opener. */
+ // Boolean: if non-zero, use b_ind_in_comment even if there is something
+ // after the comment opener.
buf->b_ind_in_comment2 = 0;
// Max lines to search for an open paren.
@@ -1727,8 +1667,8 @@ void parse_cino(buf_T *buf)
// Handle C++ namespace.
buf->b_ind_cpp_namespace = 0;
- /* Handle continuation lines containing conditions of if(), for() and
- * while(). */
+ // Handle continuation lines containing conditions of if(), for() and
+ // while().
buf->b_ind_if_for_while = 0;
// indentation for # comments
@@ -1740,7 +1680,7 @@ void parse_cino(buf_T *buf)
// Handle C #pragma directives
buf->b_ind_pragma = 0;
- for (p = (char *)buf->b_p_cino; *p;) {
+ for (p = buf->b_p_cino; *p;) {
l = p++;
if (*p == '-') {
p++;
@@ -1774,8 +1714,8 @@ void parse_cino(buf_T *buf)
n = -n;
}
- /* When adding an entry here, also update the default 'cinoptions' in
- * doc/indent.txt, and add explanation for it! */
+ // 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;
@@ -1895,10 +1835,8 @@ void parse_cino(buf_T *buf)
}
}
-/*
- * Return the desired indent for C code.
- * Return -1 if the indent should be left alone (inside a raw string).
- */
+// Return the desired indent for C code.
+// Return -1 if the indent should be left alone (inside a raw string).
int get_c_indent(void)
{
pos_T cur_curpos;
@@ -1906,22 +1844,22 @@ int get_c_indent(void)
int scope_amount;
int cur_amount = MAXCOL;
colnr_T col;
- char_u *theline;
- char_u *linecopy;
+ char *theline;
+ char *linecopy;
pos_T *trypos;
pos_T *comment_pos;
pos_T *tryposBrace = NULL;
pos_T tryposCopy;
pos_T our_paren_pos;
- char_u *start;
+ char *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;
- const char_u *l;
- const char_u *look;
- char_u terminated;
+ const char *l;
+ const char *look;
+ char terminated;
int lookfor;
#define LOOKFOR_INITIAL 0
#define LOOKFOR_IF 1
@@ -1959,25 +1897,23 @@ int get_c_indent(void)
return 0;
}
- /* Get a copy of the current contents of the line.
- * This is required, because only the most recent line obtained with
- * ml_get is valid! */
- linecopy = vim_strsave(ml_get(cur_curpos.lnum));
+ // Get a copy of the current contents of the line.
+ // This is required, because only the most recent line obtained with
+ // ml_get is valid!
+ linecopy = xstrdup(ml_get(cur_curpos.lnum));
- /*
- * In insert mode and the cursor is on a ')' truncate the line at the
- * cursor position. We don't want to line up with the matching '(' when
- * inserting new stuff.
- * For unknown reasons the cursor might be past the end of the line, thus
- * check for that.
- */
+ // In insert mode and the cursor is on a ')' truncate the line at the
+ // cursor position. We don't want to line up with the matching '(' when
+ // inserting new stuff.
+ // For unknown reasons the cursor might be past the end of the line, thus
+ // check for that.
if ((State & MODE_INSERT)
- && curwin->w_cursor.col < (colnr_T)STRLEN(linecopy)
+ && curwin->w_cursor.col < (colnr_T)strlen(linecopy)
&& linecopy[curwin->w_cursor.col] == ')') {
linecopy[curwin->w_cursor.col] = NUL;
}
- theline = (char_u *)skipwhite((char *)linecopy);
+ theline = skipwhite(linecopy);
// move the cursor to the start of the line
@@ -1985,10 +1921,8 @@ int get_c_indent(void)
original_line_islabel = cin_islabel(); // XXX
- /*
- * If we are inside a raw string don't change the indent.
- * Ignore a raw string inside a comment.
- */
+ // If we are inside a raw string don't change the indent.
+ // Ignore a raw string inside a comment.
comment_pos = ind_find_start_comment();
if (comment_pos != NULL) {
// findmatchlimit() static pos is overwritten, make a copy
@@ -2004,8 +1938,8 @@ int get_c_indent(void)
// #defines and so on go at the left when included in 'cinkeys',
// excluding pragmas when customized in 'cinoptions'
if (*theline == '#' && (*linecopy == '#' || in_cinkeys('#', ' ', true))) {
- const char_u *const directive = (char_u *)skipwhite((char *)theline + 1);
- if (curbuf->b_ind_pragma == 0 || STRNCMP(directive, "pragma", 6) != 0) {
+ const char *const directive = skipwhite(theline + 1);
+ if (curbuf->b_ind_pragma == 0 || strncmp(directive, "pragma", 6) != 0) {
amount = curbuf->b_ind_hash_comment;
goto theend;
}
@@ -2018,10 +1952,8 @@ int get_c_indent(void)
amount = 0;
goto theend;
}
- /*
- * If we're inside a "//" comment and there is a "//" comment in a
- * previous line, lineup with that one.
- */
+ // If we're inside a "//" comment and there is a "//" comment in a
+ // previous line, lineup with that one.
if (cin_islinecomment(theline)) {
pos_T linecomment_pos;
@@ -2042,10 +1974,8 @@ int get_c_indent(void)
goto theend;
}
}
- /*
- * If we're inside a comment and not looking at the start of the
- * comment, try using the 'comments' option.
- */
+ // If we're inside a comment and not looking at the start of the
+ // comment, try using the 'comments' option.
if (!cin_iscomment(theline) && comment_pos != NULL) { // XXX
int lead_start_len = 2;
int lead_middle_len = 1;
@@ -2055,7 +1985,7 @@ int get_c_indent(void)
char *p;
int start_align = 0;
int start_off = 0;
- int done = FALSE;
+ int done = false;
// find how indented the line beginning the comment is
getvcol(curwin, comment_pos, &col, NULL, NULL);
@@ -2063,7 +1993,7 @@ int get_c_indent(void)
*lead_start = NUL;
*lead_middle = NUL;
- p = (char *)curbuf->b_p_com;
+ p = curbuf->b_p_com;
while (*p != NUL) {
int align = 0;
int off = 0;
@@ -2087,31 +2017,31 @@ int get_c_indent(void)
(void)copy_option_part(&p, lead_end, COM_MAX_LEN, ",");
if (what == COM_START) {
STRCPY(lead_start, lead_end);
- lead_start_len = (int)STRLEN(lead_start);
+ lead_start_len = (int)strlen(lead_start);
start_off = off;
start_align = align;
} else if (what == COM_MIDDLE) {
STRCPY(lead_middle, lead_end);
- lead_middle_len = (int)STRLEN(lead_middle);
+ lead_middle_len = (int)strlen(lead_middle);
} else if (what == COM_END) {
- /* If our line starts with the middle comment string, line it
- * up with the comment opener per the 'comments' option. */
- if (STRNCMP(theline, lead_middle, lead_middle_len) == 0
- && STRNCMP(theline, lead_end, STRLEN(lead_end)) != 0) {
- done = TRUE;
+ // If our line starts with the middle comment string, line it
+ // up with the comment opener per the 'comments' option.
+ if (strncmp(theline, lead_middle, (size_t)lead_middle_len) == 0
+ && strncmp(theline, lead_end, strlen(lead_end)) != 0) {
+ done = true;
if (curwin->w_cursor.lnum > 1) {
- /* If the start comment string matches in the previous
- * 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 = (char_u *)skipwhite((char *)ml_get(curwin->w_cursor.lnum - 1));
- if (STRNCMP(look, lead_start, lead_start_len) == 0) {
+ // If the start comment string matches in the previous
+ // 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, (size_t)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, (size_t)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) {
+ } else if (strncmp(ml_get(comment_pos->lnum) + comment_pos->col,
+ lead_start, (size_t)lead_start_len) != 0) {
// If the start comment string doesn't match with the
// start of the comment, skip this entry. XXX
continue;
@@ -2125,10 +2055,10 @@ int get_c_indent(void)
break;
}
- /* If our line starts with the end comment string, line it up
- * with the middle comment */
- if (STRNCMP(theline, lead_middle, lead_middle_len) != 0
- && STRNCMP(theline, lead_end, STRLEN(lead_end)) == 0) {
+ // If our line starts with the end comment string, line it up
+ // with the middle comment
+ if (strncmp(theline, lead_middle, (size_t)lead_middle_len) != 0
+ && strncmp(theline, lead_end, strlen(lead_end)) == 0) {
amount = get_indent_lnum(curwin->w_cursor.lnum - 1);
// XXX
if (off != 0) {
@@ -2142,10 +2072,9 @@ int get_c_indent(void)
}
}
- /* If our line starts with an asterisk, line up with the
- * asterisk in the comment opener; otherwise, line up
- * with the first character of the comment text.
- */
+ // If our line starts with an asterisk, line up with the
+ // asterisk in the comment opener; otherwise, line up
+ // with the first character of the comment text.
if (done) {
// skip
} else if (theline[0] == '*') {
@@ -2169,7 +2098,7 @@ 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)((char_u *)skipwhite((char *)look) - start);
+ comment_pos->col = (colnr_T)(skipwhite(look) - start);
}
}
getvcol(curwin, comment_pos, &col, NULL, NULL);
@@ -2182,7 +2111,7 @@ int get_c_indent(void)
goto theend;
}
// Are we looking at a ']' that has a match?
- if (*skipwhite((char *)theline) == ']'
+ if (*skipwhite(theline) == ']'
&& (trypos = find_match_char('[', curbuf->b_ind_maxparen)) != NULL) {
// align with the line containing the '['.
amount = get_indent_lnum(trypos->lnum);
@@ -2195,8 +2124,8 @@ int get_c_indent(void)
|| (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. */
+ // 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) {
@@ -2208,17 +2137,15 @@ int get_c_indent(void)
if (trypos != NULL) {
our_paren_pos = *trypos;
- /*
- * If the matching paren is more than one line away, use the indent of
- * a previous non-empty line that matches the same paren.
- */
+ // If the matching paren is more than one line away, use the indent of
+ // a previous non-empty line that matches the same paren.
if (theline[0] == ')' && curbuf->b_ind_paren_prev) {
// Line up with the start of the matching paren line.
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 = (char_u *)skipwhite((char *)ml_get(lnum));
+ l = skipwhite(ml_get(lnum));
if (cin_nocode(l)) { // skip comment lines
continue;
}
@@ -2251,22 +2178,20 @@ int get_c_indent(void)
}
}
- /*
- * Line up with line where the matching paren is. XXX
- * If the line starts with a '(' or the indent for unclosed
- * parentheses is zero, line up with the unclosed parentheses.
- */
+ // Line up with line where the matching paren is. XXX
+ // If the line starts with a '(' or the indent for unclosed
+ // parentheses is zero, line up with the unclosed parentheses.
if (amount == -1) {
int ignore_paren_col = 0;
int is_if_for_while = 0;
if (curbuf->b_ind_if_for_while) {
- /* Look for the outermost opening parenthesis on this line
- * and check whether it belongs to an "if", "for" or "while". */
+ // Look for the outermost opening parenthesis on this line
+ // and check whether it belongs to an "if", "for" or "while".
pos_T cursor_save = curwin->w_cursor;
pos_T outermost;
- char_u *line;
+ char *line;
trypos = &our_paren_pos;
do {
@@ -2286,14 +2211,14 @@ int get_c_indent(void)
}
amount = skip_label(our_paren_pos.lnum, &look);
- look = (char_u *)skipwhite((char *)look);
+ look = skipwhite(look);
if (*look == '(') {
linenr_T save_lnum = curwin->w_cursor.lnum;
- char_u *line;
+ char *line;
int look_col;
- /* Ignore a '(' in front of the line that has a match before
- * our matching '('. */
+ // Ignore a '(' in front of the line that has a match before
+ // our matching '('.
curwin->w_cursor.lnum = our_paren_pos.lnum;
line = get_cursor_line_ptr();
look_col = (int)(look - line);
@@ -2313,24 +2238,22 @@ int get_c_indent(void)
&& is_if_for_while == 0)
|| (!curbuf->b_ind_unclosed_noignore && *look == '('
&& ignore_paren_col == 0)) {
- /*
- * If we're looking at a close paren, line up right there;
- * otherwise, line up with the next (non-white) character.
- * When b_ind_unclosed_wrapped is set and the matching paren is
- * the last nonwhite character of the line, use either the
- * indent of the current line or the indentation of the next
- * outer paren and add b_ind_unclosed_wrapped (for very long
- * lines).
- */
+ // If we're looking at a close paren, line up right there;
+ // otherwise, line up with the next (non-white) character.
+ // When b_ind_unclosed_wrapped is set and the matching paren is
+ // the last nonwhite character of the line, use either the
+ // indent of the current line or the indentation of the next
+ // outer paren and add b_ind_unclosed_wrapped (for very long
+ // lines).
if (theline[0] != ')') {
cur_amount = MAXCOL;
l = ml_get(our_paren_pos.lnum);
if (curbuf->b_ind_unclosed_wrapped
- && cin_ends_in(l, (char_u *)"(", NULL)) {
- /* look for opening unmatched paren, indent one level
- * for each additional level */
+ && cin_ends_in(l, "(", NULL)) {
+ // look for opening unmatched paren, indent one level
+ // for each additional level
n = 1;
- for (col = 0; col < our_paren_pos.col; ++col) {
+ for (col = 0; col < our_paren_pos.col; col++) {
switch (l[col]) {
case '(':
case '{':
@@ -2363,10 +2286,8 @@ int get_c_indent(void)
}
}
- /*
- * Find how indented the paren is, or the character after it
- * if we did the above "if".
- */
+ // Find how indented the paren is, or the character after it
+ // if we did the above "if".
if (our_paren_pos.col > 0) {
getvcol(curwin, &our_paren_pos, &col, NULL, NULL);
if (cur_amount > (int)col) {
@@ -2384,11 +2305,11 @@ int get_c_indent(void)
amount = cur_amount;
}
} else {
- /* Add b_ind_unclosed2 for each '(' before our matching one,
- * but ignore (void) before the line (ignore_paren_col). */
+ // Add b_ind_unclosed2 for each '(' before our matching one,
+ // but ignore (void) before the line (ignore_paren_col).
col = our_paren_pos.col;
while ((int)our_paren_pos.col > ignore_paren_col) {
- --our_paren_pos.col;
+ our_paren_pos.col--;
switch (*ml_get_pos(&our_paren_pos)) {
case '(':
amount += curbuf->b_ind_unclosed2;
@@ -2401,8 +2322,8 @@ int get_c_indent(void)
}
}
- /* Use b_ind_unclosed once, when the first '(' is not inside
- * braces */
+ // Use b_ind_unclosed once, when the first '(' is not inside
+ // braces
if (col == MAXCOL) {
amount += curbuf->b_ind_unclosed;
} else {
@@ -2418,14 +2339,12 @@ 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
- */
+ // 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
if (cur_amount < amount) {
amount = cur_amount;
}
@@ -2447,13 +2366,11 @@ int get_c_indent(void)
ourscope = trypos->lnum;
start = ml_get(ourscope);
- /*
- * Now figure out how indented the line is in general.
- * If the brace was at the start of the line, we use that;
- * otherwise, check out the indentation of the line as
- * a whole and then add the "imaginary indent" to that.
- */
- look = (char_u *)skipwhite((char *)start);
+ // Now figure out how indented the line is in general.
+ // If the brace was at the start of the line, we use that;
+ // otherwise, check out the indentation of the line as
+ // a whole and then add the "imaginary indent" to that.
+ look = skipwhite(start);
if (*look == '{') {
getvcol(curwin, trypos, &col, NULL, NULL);
amount = col;
@@ -2480,7 +2397,7 @@ int get_c_indent(void)
// ldfd) {
// }
if ((curbuf->b_ind_js || curbuf->b_ind_keep_case_label)
- && cin_iscase((char_u *)skipwhite((char *)get_cursor_line_ptr()), false)) {
+ && cin_iscase(skipwhite(get_cursor_line_ptr()), false)) {
amount = get_indent();
} else if (curbuf->b_ind_js) {
amount = get_indent_lnum(lnum);
@@ -2498,18 +2415,14 @@ int get_c_indent(void)
// we want to be. Otherwise, add the amount of room
// that an indent is supposed to be.
if (theline[0] == '}') {
- /*
- * they may want closing braces to line up with something
- * other than the open brace. indulge them, if so.
- */
+ // they may want closing braces to line up with something
+ // other than the open brace. indulge them, if so.
amount += curbuf->b_ind_close_extra;
} else {
- /*
- * If we're looking at an "else", try to find an "if"
- * to match it with.
- * If we're looking at a "while", try to find a "do"
- * to match it with.
- */
+ // If we're looking at an "else", try to find an "if"
+ // to match it with.
+ // If we're looking at a "while", try to find a "do"
+ // to match it with.
lookfor = LOOKFOR_INITIAL;
if (cin_iselse(theline)) {
lookfor = LOOKFOR_IF;
@@ -2524,18 +2437,14 @@ int get_c_indent(void)
}
}
- /*
- * We get here if we are not on an "while-of-do" or "else" (or
- * failed to find a matching "if").
- * Search backwards for something to line up with.
- * First set amount for when we don't find anything.
- */
+ // We get here if we are not on an "while-of-do" or "else" (or
+ // failed to find a matching "if").
+ // Search backwards for something to line up with.
+ // First set amount for when we don't find anything.
- /*
- * if the '{' is _really_ at the left margin, use the imaginary
- * location of a left-margin brace. Otherwise, correct the
- * location for b_ind_open_extra.
- */
+ // if the '{' is _really_ at the left margin, use the imaginary
+ // location of a left-margin brace. Otherwise, correct the
+ // location for b_ind_open_extra.
if (start_brace == BRACE_IN_COL0) { // '{' is in column 0
amount = curbuf->b_ind_open_left_imag;
@@ -2547,7 +2456,7 @@ int get_c_indent(void)
if (start_brace == BRACE_AT_END) { // '{' is at end of line
amount += curbuf->b_ind_open_imag;
- l = (char_u *)skipwhite((char *)get_cursor_line_ptr());
+ l = skipwhite(get_cursor_line_ptr());
if (cin_is_cpp_namespace(l)) {
amount += curbuf->b_ind_cpp_namespace;
} else if (cin_is_cpp_extern_c(l)) {
@@ -2562,7 +2471,7 @@ int get_c_indent(void)
}
}
- lookfor_break = FALSE;
+ lookfor_break = false;
if (cin_iscase(theline, false)) { // it's a switch() label
lookfor = LOOKFOR_CASE; // find a previous switch() label
@@ -2594,10 +2503,8 @@ int get_c_indent(void)
curwin->w_cursor.lnum--;
curwin->w_cursor.col = 0;
- /*
- * If we went all the way back to the start of our scope, line
- * up with it.
- */
+ // If we went all the way back to the start of our scope, line
+ // up with it.
if (curwin->w_cursor.lnum <= ourscope) {
// We reached end of scope:
// If looking for a enum or structure initialization
@@ -2611,9 +2518,9 @@ int get_c_indent(void)
if (curwin->w_cursor.lnum == 0
|| curwin->w_cursor.lnum
< ourscope - curbuf->b_ind_maxparen) {
- /* nothing found (abuse curbuf->b_ind_maxparen as
- * limit) assume terminated line (i.e. a variable
- * initialization) */
+ // nothing found (abuse curbuf->b_ind_maxparen as
+ // limit) assume terminated line (i.e. a variable
+ // initialization)
if (cont_amount > 0) {
amount = cont_amount;
} else if (!curbuf->b_ind_js) {
@@ -2624,10 +2531,8 @@ int get_c_indent(void)
l = get_cursor_line_ptr();
- /*
- * If we're in a comment or raw string now, skip to
- * the start of it.
- */
+ // If we're in a comment or raw string now, skip to
+ // the start of it.
trypos = ind_find_start_CORS(NULL);
if (trypos != NULL) {
curwin->w_cursor.lnum = trypos->lnum + 1;
@@ -2646,29 +2551,25 @@ int get_c_indent(void)
continue;
}
- terminated = cin_isterminated(l, FALSE, TRUE);
+ terminated = cin_isterminated(l, false, true);
- /*
- * If we are at top level and the line looks like a
- * function declaration, we are done
- * (it's a variable declaration).
- */
+ // If we are at top level and the line looks like a
+ // function declaration, we are done
+ // (it's a variable declaration).
if (start_brace != BRACE_IN_COL0
|| !cin_isfuncdecl(&l, curwin->w_cursor.lnum, 0)) {
- /* if the line is terminated with another ','
- * it is a continued variable initialization.
- * don't add extra indent.
- * TODO: does not work, if a function
- * declaration is split over multiple lines:
- * cin_isfuncdecl returns FALSE then.
- */
+ // if the line is terminated with another ','
+ // it is a continued variable initialization.
+ // don't add extra indent.
+ // TODO(vim): does not work, if a function
+ // declaration is split over multiple lines:
+ // cin_isfuncdecl returns false then.
if (terminated == ',') {
break;
}
- /* if it is an enum declaration or an assignment,
- * we are done.
- */
+ // if it is an enum declaration or an assignment,
+ // we are done.
if (terminated != ';' && cin_isinit()) {
break;
}
@@ -2700,11 +2601,10 @@ int get_c_indent(void)
}
}
- /* it's a variable declaration, add indentation
- * like in
- * int a,
- * b;
- */
+ // it's a variable declaration, add indentation
+ // like in
+ // int a,
+ // b;
if (cont_amount > 0) {
amount = cont_amount;
} else {
@@ -2728,10 +2628,8 @@ int get_c_indent(void)
}
if (lookfor_cpp_namespace) {
- /*
- * Looking for C++ namespace, need to look further
- * back.
- */
+ // Looking for C++ namespace, need to look further
+ // back.
if (curwin->w_cursor.lnum == ourscope) {
continue;
}
@@ -2744,8 +2642,8 @@ int get_c_indent(void)
l = get_cursor_line_ptr();
- /* If we're in a comment or raw string now, skip
- * to the start of it. */
+ // If we're in a comment or raw string now, skip
+ // to the start of it.
trypos = ind_find_start_CORS(NULL);
if (trypos != NULL) {
curwin->w_cursor.lnum = trypos->lnum + 1;
@@ -2787,20 +2685,18 @@ int get_c_indent(void)
l = get_cursor_line_ptr();
- /*
- * If this is a switch() label, may line up relative to that.
- * If this is a C++ scope declaration, do the same.
- */
+ // If this is a switch() label, may line up relative to that.
+ // If this is a C++ scope declaration, do the same.
bool iscase = cin_iscase(l, false);
if (iscase || cin_isscopedecl(l)) {
- /* we are only looking for cpp base class
- * declaration/initialization any longer */
+ // we are only looking for cpp base class
+ // declaration/initialization any longer
if (lookfor == LOOKFOR_CPP_BASECLASS) {
break;
}
- /* When looking for a "do" we are not interested in
- * labels. */
+ // When looking for a "do" we are not interested in
+ // labels.
if (whilelevel > 0) {
continue;
}
@@ -2873,14 +2769,12 @@ int get_c_indent(void)
break;
}
- /*
- * 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:
- * -> y = 1;
- */
+ // 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:
+ // -> y = 1;
scope_amount = get_indent() + (iscase // XXX
? curbuf->b_ind_case_code
: curbuf->b_ind_scopedecl_code);
@@ -2889,10 +2783,8 @@ int get_c_indent(void)
continue;
}
- /*
- * Looking for a switch() label or C++ scope declaration,
- * ignore other lines, skip {}-blocks.
- */
+ // Looking for a switch() label or C++ scope declaration,
+ // ignore other lines, skip {}-blocks.
if (lookfor == LOOKFOR_CASE || lookfor == LOOKFOR_SCOPEDECL) {
if (find_last_paren(l, '{', '}')
&& (trypos = find_start_brace()) != NULL) {
@@ -2902,9 +2794,7 @@ int get_c_indent(void)
continue;
}
- /*
- * Ignore jump labels with nothing after them.
- */
+ // Ignore jump labels with nothing after them.
if (!curbuf->b_ind_js && cin_islabel()) {
l = after_label(get_cursor_line_ptr());
if (l == NULL || cin_nocode(l)) {
@@ -2912,12 +2802,10 @@ int get_c_indent(void)
}
}
- /*
- * Ignore #defines, #if, etc.
- * Ignore comment and empty lines.
- * (need to get the line again, cin_islabel() may have
- * unlocked it)
- */
+ // Ignore #defines, #if, etc.
+ // Ignore comment and empty lines.
+ // (need to get the line again, cin_islabel() may have
+ // unlocked it)
l = get_cursor_line_ptr();
if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum, &amount)
|| cin_nocode(l)) {
@@ -2950,9 +2838,8 @@ int get_c_indent(void)
}
break;
} else if (lookfor == LOOKFOR_CPP_BASECLASS) {
- /* only look, whether there is a cpp base class
- * declaration or initialization before the opening brace.
- */
+ // only look, whether there is a cpp base class
+ // declaration or initialization before the opening brace.
if (cin_isterminated(l, true, false)) {
break;
} else {
@@ -2960,18 +2847,16 @@ int get_c_indent(void)
}
}
- /*
- * What happens next depends on the line being terminated.
- * If terminated with a ',' only consider it terminating if
- * there is another unterminated statement behind, eg:
- * 123,
- * sizeof
- * here
- * Otherwise check whether it is an enumeration or structure
- * initialisation (not indented) or a variable declaration
- * (indented).
- */
- terminated = cin_isterminated(l, FALSE, TRUE);
+ // What happens next depends on the line being terminated.
+ // If terminated with a ',' only consider it terminating if
+ // there is another unterminated statement behind, eg:
+ // 123,
+ // sizeof
+ // here
+ // Otherwise check whether it is an enumeration or structure
+ // initialisation (not indented) or a variable declaration
+ // (indented).
+ terminated = cin_isterminated(l, false, true);
if (js_cur_has_key) {
js_cur_has_key = false; // only check the first line
@@ -2999,21 +2884,20 @@ int get_c_indent(void)
// Line below current line is the one that starts a
// (possibly broken) line ending in a comma.
break;
- } else {
- amount = get_indent();
- if (curwin->w_cursor.lnum - 1 == ourscope) {
- // line above is start of the scope, thus current
- // line is the one that stars a (possibly broken)
- // line ending in a comma.
- break;
- }
+ }
+ amount = get_indent();
+ if (curwin->w_cursor.lnum - 1 == ourscope) {
+ // line above is start of the scope, thus current
+ // line is the one that stars a (possibly broken)
+ // line ending in a comma.
+ break;
}
}
if (terminated == 0 || (lookfor != LOOKFOR_UNTERM
&& terminated == ',')) {
if (lookfor != LOOKFOR_ENUM_OR_INIT
- && (*skipwhite((char *)l) == '[' || l[STRLEN(l) - 1] == '[')) {
+ && (*skipwhite(l) == '[' || l[strlen(l) - 1] == '[')) {
amount += ind_continuation;
}
// If we're in the middle of a paren thing, Go back to the line
@@ -3040,12 +2924,10 @@ int get_c_indent(void)
}
if (trypos != NULL) {
- /*
- * Check if we are on a case label now. This is
- * handled above.
- * case xx: if ( asdf &&
- * asdf)
- */
+ // 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)) {
@@ -3055,17 +2937,15 @@ int get_c_indent(void)
}
}
- /*
- * Skip over continuation lines to find the one to get the
- * indent from
- * char *usethis = "bla\
- * bla",
- * here;
- */
+ // Skip over continuation lines to find the one to get the
+ // indent from
+ // char *usethis = "bla{backslash}
+ // 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--;
@@ -3073,50 +2953,42 @@ int get_c_indent(void)
}
}
- /*
- * Get indent and pointer to text for current line,
- * ignoring any jump label. XXX
- */
+ // Get indent and pointer to text for current line,
+ // ignoring any jump label. XXX
if (curbuf->b_ind_js) {
cur_amount = get_indent();
} else {
cur_amount = skip_label(curwin->w_cursor.lnum, &l);
}
- /*
- * If this is just above the line we are indenting, and it
- * starts with a '{', line it up with this line.
- * while (not)
- * -> {
- * }
- */
+ // If this is just above the line we are indenting, and it
+ // starts with a '{', line it up with this line.
+ // while (not)
+ // -> {
+ // }
if (terminated != ',' && lookfor != LOOKFOR_TERM
&& theline[0] == '{') {
amount = cur_amount;
- /*
- * 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 }
- */
- if (*skipwhite((char *)l) != '{') {
+ // 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 }
+ if (*skipwhite(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
- * class declaration or initialization */
+ // have to look back, whether it is a cpp base
+ // class declaration or initialization
lookfor = LOOKFOR_CPP_BASECLASS;
continue;
}
break;
}
- /*
- * Check if we are after an "if", "while", etc.
- * Also allow " } else".
- */
- if (cin_is_cinword(l) || cin_iselse((char_u *)skipwhite((char *)l))) {
+ // 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)
@@ -3132,19 +3004,17 @@ int get_c_indent(void)
break;
}
- /*
- * If this is just above the line we are indenting, we
- * are finished.
- * while (not)
- * -> here;
- * Otherwise this indent can be used when the line
- * before this is terminated.
- * yyy;
- * if (stat)
- * while (not)
- * xxx;
- * -> here;
- */
+ // If this is just above the line we are indenting, we
+ // are finished.
+ // while (not)
+ // -> here;
+ // Otherwise this indent can be used when the line
+ // before this is terminated.
+ // yyy;
+ // if (stat)
+ // while (not)
+ // xxx;
+ // -> here;
amount = cur_amount;
if (theline[0] == '{') {
amount += curbuf->b_ind_open_extra;
@@ -3155,14 +3025,12 @@ int get_c_indent(void)
break;
}
- /*
- * Special trick: when expecting the while () after a
- * do, line up with the while()
- * do
- * x = 1;
- * -> here
- */
- l = (char_u *)skipwhite((char *)get_cursor_line_ptr());
+ // Special trick: when expecting the while () after a
+ // do, line up with the while()
+ // do
+ // x = 1;
+ // -> here
+ l = skipwhite(get_cursor_line_ptr());
if (cin_isdo(l)) {
if (whilelevel == 0) {
break;
@@ -3170,16 +3038,14 @@ int get_c_indent(void)
whilelevel--;
}
- /*
- * When searching for a terminated line, don't use the
- * one between the "if" and the matching "else".
- * Need to use the scope of this "else". XXX
- * If whilelevel != 0 continue looking for a "do {".
- */
+ // When searching for a terminated line, don't use the
+ // one between the "if" and the matching "else".
+ // Need to use the scope of this "else". XXX
+ // If whilelevel != 0 continue looking for a "do {".
if (cin_iselse(l) && whilelevel == 0) {
- /* 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 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 == '}') {
curwin->w_cursor.col =
(colnr_T)(l - get_cursor_line_ptr()) + 1;
@@ -3191,21 +3057,17 @@ int get_c_indent(void)
break;
}
}
- }
- /*
- * If we're below an unterminated line that is not an
- * "if" or something, we may line up with this line or
- * add something for a continuation line, depending on
- * the line before this one.
- */
- else {
- /*
- * Found two unterminated lines on a row, line up with
- * the last one.
- * c = 99 +
- * 100 +
- * -> here;
- */
+ } else {
+ // If we're below an unterminated line that is not an
+ // "if" or something, we may line up with this line or
+ // add something for a continuation line, depending on
+ // the line before this one.
+
+ // Found two unterminated lines on a row, line up with
+ // the last one.
+ // c = 99 +
+ // 100 +
+ // -> here;
if (lookfor == LOOKFOR_UNTERM) {
// When line ends in a comma add extra indent
if (terminated == ',') {
@@ -3215,11 +3077,11 @@ int get_c_indent(void)
}
if (lookfor == LOOKFOR_ENUM_OR_INIT) {
- /* Found two lines ending in ',', lineup with the
- * lowest one, but check for cpp base class
- * declaration/initialization, if it is an
- * opening brace or we are looking just for
- * enumerations/initializations. */
+ // Found two lines ending in ',', lineup with the
+ // lowest one, but check for cpp base class
+ // declaration/initialization, if it is an
+ // opening brace or we are looking just for
+ // enumerations/initializations.
if (terminated == ',') {
if (curbuf->b_ind_cpp_baseclass == 0) {
break;
@@ -3242,9 +3104,9 @@ int get_c_indent(void)
l = get_cursor_line_ptr();
amount = cur_amount;
- n = (int)STRLEN(l);
+ n = (int)strlen(l);
if (terminated == ','
- && (*skipwhite((char *)l) == ']'
+ && (*skipwhite(l) == ']'
|| (n >= 2 && l[n - 2] == ']'))) {
break;
}
@@ -3271,7 +3133,7 @@ int get_c_indent(void)
// 4 *
// 5,
// 6,
- if (cin_iscomment((char_u *)skipwhite((char *)l))) {
+ if (cin_iscomment(skipwhite(l))) {
break;
}
lookfor = LOOKFOR_COMMA;
@@ -3291,7 +3153,7 @@ int get_c_indent(void)
} else {
if (lookfor == LOOKFOR_INITIAL
&& *l != NUL
- && l[STRLEN(l) - 1] == '\\') {
+ && l[strlen(l) - 1] == '\\') {
// XXX
cont_amount = cin_get_equal_amount(curwin->w_cursor.lnum);
}
@@ -3304,19 +3166,14 @@ int get_c_indent(void)
}
}
}
- }
- /*
- * Check if we are after a while (cond);
- * If so: Ignore until the matching "do".
- */
- else if (cin_iswhileofdo_end(terminated)) { // XXX
- /*
- * Found an unterminated line after a while ();, line up
- * with the last one.
- * while (cond);
- * 100 + <- line up with this one
- * -> here;
- */
+ // Check if we are after a while (cond);
+ // If so: Ignore until the matching "do".
+ } else if (cin_iswhileofdo_end((uint8_t)terminated)) { // XXX
+ // Found an unterminated line after a while ();, line up
+ // with the last one.
+ // while (cond);
+ // 100 + <- line up with this one
+ // -> here;
if (lookfor == LOOKFOR_UNTERM
|| lookfor == LOOKFOR_ENUM_OR_INIT) {
if (cont_amount > 0) {
@@ -3335,28 +3192,22 @@ int get_c_indent(void)
}
}
whilelevel++;
- }
- /*
- * We are after a "normal" statement.
- * If we had another statement we can stop now and use the
- * indent of that other statement.
- * Otherwise the indent of the current statement may be used,
- * search backwards for the next "normal" statement.
- */
- else {
- /*
- * Skip single break line, if before a switch label. It
- * may be lined up with the case label.
- */
+ } else {
+ // We are after a "normal" statement.
+ // If we had another statement we can stop now and use the
+ // indent of that other statement.
+ // Otherwise the indent of the current statement may be used,
+ // search backwards for the next "normal" statement.
+
+ // Skip single break line, if before a switch label. It
+ // may be lined up with the case label.
if (lookfor == LOOKFOR_NOBREAK
- && cin_isbreak((char_u *)skipwhite((char *)get_cursor_line_ptr()))) {
+ && cin_isbreak(skipwhite(get_cursor_line_ptr()))) {
lookfor = LOOKFOR_ANY;
continue;
}
- /*
- * Handle "do {" line.
- */
+ // Handle "do {" line.
if (whilelevel > 0) {
l = cin_skipcomment(get_cursor_line_ptr());
if (cin_isdo(l)) {
@@ -3366,17 +3217,15 @@ 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;
- * or
- * int x = 1;
- * int foo,
- * -> here;
- */
+ // Found a terminated line above an unterminated line. Add
+ // the amount for a continuation line.
+ // x = 1;
+ // y = foo +
+ // -> here;
+ // or
+ // int x = 1;
+ // int foo,
+ // -> here;
if (lookfor == LOOKFOR_UNTERM
|| lookfor == LOOKFOR_ENUM_OR_INIT) {
if (cont_amount > 0) {
@@ -3387,34 +3236,28 @@ int get_c_indent(void)
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;
- * ->foo;
- */
+ // 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;
+ // ->foo;
if (lookfor == LOOKFOR_TERM) {
if (!lookfor_break && whilelevel == 0) {
break;
}
- }
- /*
- * First line above the one we're indenting is terminated.
- * To know what needs to be done look further backward for
- * a terminated line.
- */
- else {
- /*
- * position the cursor over the rightmost paren, so
- * that matching it will take us back to the start of
- * the line. Helps for:
- * func(asdr,
- * asdfasdf);
- * here;
- */
+ } else {
+ // First line above the one we're indenting is terminated.
+ // To know what needs to be done look further backward for
+ // a terminated line.
+
+ // position the cursor over the rightmost paren, so
+ // that matching it will take us back to the start of
+ // the line. Helps for:
+ // func(asdr,
+ // asdfasdf);
+ // here;
term_again:
l = get_cursor_line_ptr();
if (find_last_paren(l, '(', ')')
@@ -3432,41 +3275,36 @@ term_again:
}
}
- /* When aligning with the case statement, don't align
- * with a statement after it.
- * case 1: { <-- don't use this { position
- * stat;
- * }
- * case 2:
- * stat;
- * }
- */
+ // When aligning with the case statement, don't align
+ // with a statement after it.
+ // case 1: { <-- don't use this { position
+ // stat;
+ // }
+ // case 2:
+ // stat;
+ // }
iscase = curbuf->b_ind_keep_case_label && cin_iscase(l, false);
- /*
- * Get indent and pointer to text for current line,
- * ignoring any jump label.
- */
+ // Get indent and pointer to text for current line,
+ // ignoring any jump label.
amount = skip_label(curwin->w_cursor.lnum, &l);
if (theline[0] == '{') {
amount += curbuf->b_ind_open_extra;
}
// See remark above: "Only add b_ind_open_extra.."
- l = (char_u *)skipwhite((char *)l);
+ l = skipwhite(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;
- * Need to use the scope of this "else". XXX
- * If whilelevel != 0 continue looking for a "do {".
- */
+ // When a terminated line starts with "else" skip to
+ // the matching "if":
+ // else 3;
+ // indent this;
+ // Need to use the scope of this "else". XXX
+ // If whilelevel != 0 continue looking for a "do {".
if (lookfor == LOOKFOR_TERM
&& *l != '}'
&& cin_iselse(l)
@@ -3479,10 +3317,8 @@ term_again:
continue;
}
- /*
- * If we're at the end of a block, skip to the start of
- * that block.
- */
+ // If we're at the end of a block, skip to the start of
+ // that block.
l = get_cursor_line_ptr();
if (find_last_paren(l, '{', '}') // XXX
&& (trypos = find_start_brace()) != NULL) {
@@ -3529,19 +3365,17 @@ term_again:
amount = curbuf->b_ind_first_open;
goto theend;
}
- /*
- * If the NEXT line is a function declaration, the current
- * line needs to be indented as a function type spec.
- * Don't do this if the current line looks like a comment or if the
- * current line is terminated, ie. ends in ';', or if the current line
- * contains { or }: "void f() {\n if (1)"
- */
+ // If the NEXT line is a function declaration, the current
+ // line needs to be indented as a function type spec.
+ // Don't do this if the current line looks like a comment or if the
+ // current line is terminated, ie. ends in ';', or if the current line
+ // contains { or }: "void f() {\n if (1)"
if (cur_curpos.lnum < curbuf->b_ml.ml_line_count
&& !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)
+ && vim_strchr(theline, '{') == NULL
+ && vim_strchr(theline, '}') == NULL
+ && !cin_ends_in(theline, ":", NULL)
+ && !cin_ends_in(theline, ",", NULL)
&& cin_isfuncdecl(NULL, cur_curpos.lnum + 1, cur_curpos.lnum + 1)
&& !cin_isterminated(theline, false, true)) {
amount = curbuf->b_ind_func_type;
@@ -3590,35 +3424,32 @@ term_again:
continue;
}
- /*
- * If the previous line ends in ',', use one level of
- * indentation:
- * int foo,
- * bar;
- * do this before checking for '}' in case of eg.
- * enum foobar
- * {
- * ...
- * } foo,
- * bar;
- */
- if (cin_ends_in(l, (char_u *)",", NULL)
- || (*l != NUL && (n = l[STRLEN(l) - 1]) == '\\')) {
+ // If the previous line ends in ',', use one level of
+ // indentation:
+ // int foo,
+ // bar;
+ // do this before checking for '}' in case of eg.
+ // enum foobar
+ // {
+ // ...
+ // } foo,
+ // bar;
+ if (cin_ends_in(l, ",", NULL)
+ || (*l != NUL && (n = (uint8_t)l[strlen(l) - 1]) == '\\')) {
// take us back to opening paren
if (find_last_paren(l, '(', ')')
&& (trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL) {
curwin->w_cursor = *trypos;
}
- /* For a line ending in ',' that is a continuation line go
- * back to the first line with a backslash:
- * char *foo = "bla\
- * bla",
- * here;
- */
+ // For a line ending in ',' that is a continuation line go
+ // back to the first line with a backslash:
+ // char *foo = "bla{backslash}
+ // 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--;
@@ -3636,20 +3467,16 @@ term_again:
break;
}
- /*
- * If the line looks like a function declaration, and we're
- * not in a comment, put it the left margin.
- */
+ // If the line looks like a function declaration, and we're
+ // not in a comment, put it the left margin.
if (cin_isfuncdecl(NULL, cur_curpos.lnum, 0)) { // XXX
break;
}
l = get_cursor_line_ptr();
- /*
- * Finding the closing '}' of a previous function. Put
- * current line at the left margin. For when 'cino' has "fs".
- */
- if (*skipwhite((char *)l) == '}') {
+ // Finding the closing '}' of a previous function. Put
+ // current line at the left margin. For when 'cino' has "fs".
+ if (*skipwhite(l) == '}') {
break;
}
@@ -3658,7 +3485,7 @@ term_again:
// comments) align at column 0. For example:
// char *string_array[] = { "foo",
// / * x * / "b};ar" }; / * foobar * /
- if (cin_ends_in(l, (char_u *)"};", NULL)) {
+ if (cin_ends_in(l, "};", NULL)) {
break;
}
@@ -3666,17 +3493,15 @@ term_again:
// array constant:
// something = [
// 234, <- extra indent
- if (cin_ends_in(l, (char_u *)"[", NULL)) {
+ if (cin_ends_in(l, "[", NULL)) {
amount = get_indent() + ind_continuation;
break;
}
- /*
- * Find a line only has a semicolon that belongs to a previous
- * line ending in '}', e.g. before an #endif. Don't increase
- * indent then.
- */
- if (*(look = (char_u *)skipwhite((char *)l)) == ';' && cin_nocode(look + 1)) {
+ // Find a line only has a semicolon that belongs to a previous
+ // line ending in '}', e.g. before an #endif. Don't increase
+ // indent then.
+ if (*(look = skipwhite(l)) == ';' && cin_nocode(look + 1)) {
pos_T curpos_save = curwin->w_cursor;
while (curwin->w_cursor.lnum > 1) {
@@ -3687,46 +3512,40 @@ term_again:
}
}
if (curwin->w_cursor.lnum > 0
- && cin_ends_in(look, (char_u *)"}", NULL)) {
+ && cin_ends_in(look, "}", NULL)) {
break;
}
curwin->w_cursor = curpos_save;
}
- /*
- * If the PREVIOUS line is a function declaration, the current
- * line (and the ones that follow) needs to be indented as
- * parameters.
- */
+ // If the PREVIOUS line is a function declaration, the current
+ // line (and the ones that follow) needs to be indented as
+ // parameters.
if (cin_isfuncdecl(&l, curwin->w_cursor.lnum, 0)) {
amount = curbuf->b_ind_param;
break;
}
- /*
- * If the previous line ends in ';' and the line before the
- * previous line ends in ',' or '\', ident to column zero:
- * int foo,
- * bar;
- * indent_to_0 here;
- */
- if (cin_ends_in(l, (char_u *)";", NULL)) {
+ // If the previous line ends in ';' and the line before the
+ // previous line ends in ',' or '\', ident to column zero:
+ // int foo,
+ // bar;
+ // indent_to_0 here;
+ if (cin_ends_in(l, ";", NULL)) {
l = ml_get(curwin->w_cursor.lnum - 1);
- if (cin_ends_in(l, (char_u *)",", NULL)
- || (*l != NUL && l[STRLEN(l) - 1] == '\\')) {
+ if (cin_ends_in(l, ",", NULL)
+ || (*l != NUL && l[strlen(l) - 1] == '\\')) {
break;
}
l = get_cursor_line_ptr();
}
- /*
- * Doesn't look like anything interesting -- so just
- * use the indent of this line.
- *
- * Position the cursor over the rightmost paren, so that
- * matching it will take us back to the start of the line.
- */
+ // Doesn't look like anything interesting -- so just
+ // use the indent of this line.
+ //
+ // Position the cursor over the rightmost paren, so that
+ // matching it will take us back to the start of the line.
(void)find_last_paren(l, '(', ')');
if ((trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL) {
@@ -3748,7 +3567,7 @@ term_again:
// here";
if (cur_curpos.lnum > 1) {
l = ml_get(cur_curpos.lnum - 1);
- if (*l != NUL && l[STRLEN(l) - 1] == '\\') {
+ if (*l != NUL && l[strlen(l) - 1] == '\\') {
cur_amount = cin_get_equal_amount(cur_curpos.lnum - 1);
if (cur_amount > 0) {
amount = cur_amount;
@@ -3774,9 +3593,9 @@ laterend:
static int find_match(int lookfor, linenr_T ourscope)
{
- const char_u *look;
+ const char *look;
pos_T *theirscope;
- const char_u *mightbeif;
+ const char *mightbeif;
int elselevel;
int whilelevel;
@@ -3802,38 +3621,30 @@ static int find_match(int lookfor, linenr_T ourscope)
continue;
}
- /*
- * if we've gone outside the braces entirely,
- * we must be out of scope...
- */
+ // if we've gone outside the braces entirely,
+ // we must be out of scope...
theirscope = find_start_brace(); // XXX
if (theirscope == NULL) {
break;
}
- /*
- * and if the brace enclosing this is further
- * back than the one enclosing the else, we're
- * out of luck too.
- */
+ // and if the brace enclosing this is further
+ // back than the one enclosing the else, we're
+ // out of luck too.
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...
- */
+ // 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) {
continue;
}
- /*
- * if it was an "else" (that's not an "else if")
- * then we need to go back to another if, so
- * increment elselevel
- */
+ // if it was an "else" (that's not an "else if")
+ // then we need to go back to another if, so
+ // increment elselevel
look = cin_skipcomment(get_cursor_line_ptr());
if (cin_iselse(look)) {
mightbeif = cin_skipcomment(look + 4);
@@ -3843,10 +3654,8 @@ static int find_match(int lookfor, linenr_T ourscope)
continue;
}
- /*
- * if it was a "while" then we need to go back to
- * another "do", so increment whilelevel. XXX
- */
+ // if it was a "while" then we need to go back to
+ // another "do", so increment whilelevel. XXX
if (cin_iswhileofdo(look, curwin->w_cursor.lnum)) {
whilelevel++;
continue;
@@ -3855,11 +3664,9 @@ static int find_match(int lookfor, linenr_T ourscope)
// If it's an "if" decrement elselevel
look = cin_skipcomment(get_cursor_line_ptr());
if (cin_isif(look)) {
- elselevel--;
- /*
- * When looking for an "if" ignore "while"s that
- * get in the way.
- */
+ elselevel--; // NOLINT(readability/braces)
+ // When looking for an "if" ignore "while"s that
+ // get in the way.
if (elselevel == 0 && lookfor == LOOKFOR_IF) {
whilelevel = 0;
}
@@ -3870,11 +3677,9 @@ static int find_match(int lookfor, linenr_T ourscope)
whilelevel--;
}
- /*
- * if we've used up all the elses, then
- * this must be the if that we want!
- * match the indent level of that if.
- */
+ // if we've used up all the elses, then
+ // this must be the if that we want!
+ // match the indent level of that if.
if (elselevel <= 0 && whilelevel <= 0) {
return OK;
}
@@ -3882,9 +3687,7 @@ static int find_match(int lookfor, linenr_T ourscope)
return FAIL;
}
-/*
- * Do C or expression indenting on the current line.
- */
+// Do C or expression indenting on the current line.
void do_c_expr_indent(void)
{
if (*curbuf->b_p_inde != NUL) {
diff --git a/src/nvim/input.c b/src/nvim/input.c
index 37c2903cfd..96214d45c2 100644
--- a/src/nvim/input.c
+++ b/src/nvim/input.c
@@ -4,24 +4,33 @@
// input.c: high level functions for prompting the user or input
// like yes/no or number prompts.
-#include <inttypes.h>
#include <stdbool.h>
+#include <string.h>
+#include "nvim/ascii.h"
+#include "nvim/event/multiqueue.h"
#include "nvim/func_attr.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/highlight_defs.h"
#include "nvim/input.h"
+#include "nvim/keycodes.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
+#include "nvim/message.h"
#include "nvim/mouse.h"
#include "nvim/os/input.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "input.c.generated.h"
+# include "input.c.generated.h" // IWYU pragma: export
#endif
-/// Ask for a reply from the user, 'y' or 'n'
+/// Ask for a reply from the user, a 'y' or a 'n', with prompt "str" (which
+/// should have been translated already).
///
/// No other characters are accepted, the message is repeated until a valid
/// reply is entered or <C-c> is hit.
@@ -46,7 +55,7 @@ int ask_yesno(const char *const str, const bool direct)
int r = ' ';
while (r != 'y' && r != 'n') {
- // Same highlighting as for wait_return.
+ // same highlighting as for wait_return()
smsg_attr(HL_ATTR(HLF_R), "%s (y/n)?", str);
if (direct) {
r = get_keystroke(NULL);
@@ -83,7 +92,6 @@ int get_keystroke(MultiQueue *events)
int len = 0;
int n;
int save_mapped_ctrl_c = mapped_ctrl_c;
- int waited = 0;
mapped_ctrl_c = 0; // mappings are not used here
for (;;) {
@@ -110,10 +118,8 @@ int get_keystroke(MultiQueue *events)
// Replace zero and K_SPECIAL by a special key code.
n = fix_input_buffer(buf + len, n);
len += n;
- waited = 0;
- } else if (len > 0) {
- waited++; // keep track of the waiting time
}
+
if (n > 0) { // found a termcode: adjust length
len = n;
}
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c
index 938189f343..6de3b0a9d0 100644
--- a/src/nvim/insexpand.c
+++ b/src/nvim/insexpand.c
@@ -4,11 +4,16 @@
// insexpand.c: functions for Insert mode completion
#include <assert.h>
-#include <inttypes.h>
+#include <limits.h>
#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/change.h"
#include "nvim/charset.h"
@@ -18,35 +23,48 @@
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/eval/userfunc.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
+#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/highlight_defs.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
#include "nvim/insexpand.h"
#include "nvim/keycodes.h"
+#include "nvim/macros.h"
+#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/move.h"
#include "nvim/option.h"
+#include "nvim/os/fs.h"
#include "nvim/os/input.h"
#include "nvim/os/time.h"
#include "nvim/path.h"
#include "nvim/popupmenu.h"
+#include "nvim/pos.h"
#include "nvim/regexp.h"
+#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/spell.h"
#include "nvim/state.h"
#include "nvim/strings.h"
#include "nvim/tag.h"
+#include "nvim/textformat.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/vim.h"
-#include "nvim/window.h"
// Definitions used for CTRL-X submode.
// Note: If you change CTRL-X submode, you must also maintain ctrl_x_msgs[]
@@ -54,30 +72,31 @@
#define CTRL_X_WANT_IDENT 0x100
-#define CTRL_X_NORMAL 0 ///< CTRL-N CTRL-P completion, default
-#define CTRL_X_NOT_DEFINED_YET 1
-#define CTRL_X_SCROLL 2
-#define CTRL_X_WHOLE_LINE 3
-#define CTRL_X_FILES 4
-#define CTRL_X_TAGS (5 + CTRL_X_WANT_IDENT)
-#define CTRL_X_PATH_PATTERNS (6 + CTRL_X_WANT_IDENT)
-#define CTRL_X_PATH_DEFINES (7 + CTRL_X_WANT_IDENT)
-#define CTRL_X_FINISHED 8
-#define CTRL_X_DICTIONARY (9 + CTRL_X_WANT_IDENT)
-#define CTRL_X_THESAURUS (10 + CTRL_X_WANT_IDENT)
-#define CTRL_X_CMDLINE 11
-#define CTRL_X_FUNCTION 12
-#define CTRL_X_OMNI 13
-#define CTRL_X_SPELL 14
-#define CTRL_X_LOCAL_MSG 15 ///< only used in "ctrl_x_msgs"
-#define CTRL_X_EVAL 16 ///< for builtin function complete()
-#define CTRL_X_CMDLINE_CTRL_X 17 ///< CTRL-X typed in CTRL_X_CMDLINE
+enum {
+ CTRL_X_NORMAL = 0, ///< CTRL-N CTRL-P completion, default
+ CTRL_X_NOT_DEFINED_YET = 1,
+ CTRL_X_SCROLL = 2,
+ CTRL_X_WHOLE_LINE = 3,
+ CTRL_X_FILES = 4,
+ CTRL_X_TAGS = (5 + CTRL_X_WANT_IDENT),
+ CTRL_X_PATH_PATTERNS = (6 + CTRL_X_WANT_IDENT),
+ CTRL_X_PATH_DEFINES = (7 + CTRL_X_WANT_IDENT),
+ CTRL_X_FINISHED = 8,
+ CTRL_X_DICTIONARY = (9 + CTRL_X_WANT_IDENT),
+ CTRL_X_THESAURUS = (10 + CTRL_X_WANT_IDENT),
+ CTRL_X_CMDLINE = 11,
+ CTRL_X_FUNCTION = 12,
+ CTRL_X_OMNI = 13,
+ CTRL_X_SPELL = 14,
+ CTRL_X_LOCAL_MSG = 15, ///< only used in "ctrl_x_msgs"
+ CTRL_X_EVAL = 16, ///< for builtin function complete()
+ CTRL_X_CMDLINE_CTRL_X = 17, ///< CTRL-X typed in CTRL_X_CMDLINE
+};
#define CTRL_X_MSG(i) ctrl_x_msgs[(i) & ~CTRL_X_WANT_IDENT]
/// Message for CTRL-X mode, index is ctrl_x_mode.
-static char *ctrl_x_msgs[] =
-{
+static char *ctrl_x_msgs[] = {
N_(" Keyword completion (^N^P)"), // CTRL_X_NORMAL, ^P/^N compl.
N_(" ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"),
NULL, // CTRL_X_SCROLL: depends on state
@@ -131,15 +150,30 @@ typedef struct compl_S compl_T;
struct compl_S {
compl_T *cp_next;
compl_T *cp_prev;
- char_u *cp_str; ///< matched text
- char_u *(cp_text[CPT_COUNT]); ///< text for the menu
+ char *cp_str; ///< matched text
+ char *(cp_text[CPT_COUNT]); ///< text for the menu
typval_T cp_user_data;
- char_u *cp_fname; ///< file containing the match, allocated when
+ char *cp_fname; ///< file containing the match, allocated when
///< cp_flags has CP_FREE_FNAME
int cp_flags; ///< CP_ values
int cp_number; ///< sequence number
};
+/// state information used for getting the next set of insert completion
+/// matches.
+typedef struct {
+ char *e_cpt; ///< current entry in 'complete'
+ buf_T *ins_buf; ///< buffer being scanned
+ pos_T *cur_match_pos; ///< current match position
+ pos_T prev_match_pos; ///< previous match position
+ bool set_match_pos; ///< save first_match_pos/last_match_pos
+ pos_T first_match_pos; ///< first match position
+ pos_T last_match_pos; ///< last match position
+ bool found_all; ///< found all matches of a certain type.
+ char *dict; ///< dictionary file to search
+ int dict_f; ///< "dict" is an exact file name or not
+} ins_compl_next_state_T;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "insexpand.c.generated.h"
#endif
@@ -162,6 +196,7 @@ static char e_compldel[] = N_("E840: Completion function deleted text");
// "compl_curr_match" points to the currently selected entry.
// "compl_shown_match" is different from compl_curr_match during
// ins_compl_get_exp().
+// "compl_old_match" points to previous "compl_curr_match".
static compl_T *compl_first_match = NULL;
static compl_T *compl_curr_match = NULL;
@@ -174,7 +209,7 @@ static bool compl_enter_selects = false;
/// When "compl_leader" is not NULL only matches that start with this string
/// are used.
-static char_u *compl_leader = NULL;
+static char *compl_leader = NULL;
static bool compl_get_longest = false; ///< put longest common string in compl_leader
@@ -182,6 +217,8 @@ static bool compl_no_insert = false; ///< false: select & insert
///< true: noinsert
static bool compl_no_select = false; ///< false: select & insert
///< true: noselect
+static bool compl_longest = false; ///< false: insert full match
+ ///< true: insert longest prefix
/// Selected one of the matches. When false the match was edited or using the
/// longest common string.
@@ -196,26 +233,42 @@ static bool compl_interrupted = false;
static bool compl_restarting = false; ///< don't insert match
-///< When the first completion is done "compl_started" is set. When it's
-///< false the word to be completed must be located.
+/// When the first completion is done "compl_started" is set. When it's
+/// false the word to be completed must be located.
static bool compl_started = false;
-///< Which Ctrl-X mode are we in?
+/// Which Ctrl-X mode are we in?
static int ctrl_x_mode = CTRL_X_NORMAL;
-static int compl_matches = 0;
+static int compl_matches = 0; ///< number of completion matches
static char *compl_pattern = NULL;
static Direction compl_direction = FORWARD;
static Direction compl_shows_dir = FORWARD;
static int compl_pending = 0; ///< > 1 for postponed CTRL-N
static pos_T compl_startpos;
+/// Length in bytes of the text being completed (this is deleted to be replaced
+/// by the match.)
+static int compl_length = 0;
static colnr_T compl_col = 0; ///< column where the text starts
///< that is being completed
-static char_u *compl_orig_text = NULL; ///< text as it was before
+static char *compl_orig_text = NULL; ///< text as it was before
///< completion started
static int compl_cont_mode = 0;
static expand_T compl_xp;
+// List of flags for method of completion.
+static int compl_cont_status = 0;
+#define CONT_ADDING 1 ///< "normal" or "adding" expansion
+#define CONT_INTRPT (2 + 4) ///< a ^X interrupted the current expansion
+ ///< it's set only iff N_ADDS is set
+#define CONT_N_ADDS 4 ///< next ^X<> will add-new or expand-current
+#define CONT_S_IPOS 8 ///< next ^X<> will set initial_pos?
+ ///< if so, word-wise-expansion will set SOL
+#define CONT_SOL 16 ///< pattern includes start of line, just for
+ ///< word-wise expansion, not set for ^X^L
+#define CONT_LOCAL 32 ///< for ctrl_x_mode 0, ^X^P/^X^N do a local
+ ///< expansion, (eg use complete=.)
+
static bool compl_opt_refresh_always = false;
static size_t spell_bad_len = 0; // length of located bad word
@@ -234,7 +287,7 @@ void ins_ctrl_x(void)
}
// We're not sure which CTRL-X mode it will be yet
ctrl_x_mode = CTRL_X_NOT_DEFINED_YET;
- edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode));
+ edit_submode = _(CTRL_X_MSG(ctrl_x_mode));
edit_submode_pre = NULL;
showmode();
} else {
@@ -359,9 +412,52 @@ bool ctrl_x_mode_not_defined_yet(void)
return ctrl_x_mode == CTRL_X_NOT_DEFINED_YET;
}
-/// Check that the "dict" or "tsr" option can be used.
+/// @return true if currently in "normal" or "adding" insert completion matches state
+bool compl_status_adding(void)
+{
+ return compl_cont_status & CONT_ADDING;
+}
+
+/// @return true if the completion pattern includes start of line, just for
+/// word-wise expansion.
+bool compl_status_sol(void)
+{
+ return compl_cont_status & CONT_SOL;
+}
+
+/// @return true if ^X^P/^X^N will do a local completion (i.e. use complete=.)
+bool compl_status_local(void)
+{
+ return compl_cont_status & CONT_LOCAL;
+}
+
+/// Clear the completion status flags
+void compl_status_clear(void)
+{
+ compl_cont_status = 0;
+}
+
+/// @return true if completion is using the forward direction matches
+static bool compl_dir_forward(void)
+{
+ return compl_direction == FORWARD;
+}
+
+/// @return true if currently showing forward completion matches
+static bool compl_shows_dir_forward(void)
+{
+ return compl_shows_dir == FORWARD;
+}
+
+/// @return true if currently showing backward completion matches
+static bool compl_shows_dir_backward(void)
+{
+ return compl_shows_dir == BACKWARD;
+}
+
+/// Check that the 'dictionary' or 'thesaurus' option can be used.
///
-/// @param dict_opt check "dict" when true, "tsr" when false.
+/// @param dict_opt check 'dictionary' when true, 'thesaurus' when false.
bool check_compl_option(bool dict_opt)
{
if (dict_opt
@@ -373,7 +469,7 @@ bool check_compl_option(bool dict_opt)
msg_attr((dict_opt
? _("'dictionary' option is empty")
: _("'thesaurus' option is empty")), HL_ATTR(HLF_E));
- if (emsg_silent == 0) {
+ if (emsg_silent == 0 && !in_assert_fails) {
vim_beep(BO_COMPL);
setcursor();
ui_flush();
@@ -445,6 +541,18 @@ bool vim_is_ctrl_x_key(int c)
return false;
}
+/// @return true if "match" is the original text when the completion began.
+static bool match_at_original_text(const compl_T *const match)
+{
+ return match->cp_flags & CP_ORIGINAL_TEXT;
+}
+
+/// @return true if "match" is the first match in the completion list.
+static bool is_first_match(const compl_T *const match)
+{
+ return match == compl_first_match;
+}
+
/// Check that character "c" is part of the item currently being
/// completed. Used to decide whether to abandon complete mode when the menu
/// is visible.
@@ -479,126 +587,150 @@ bool ins_compl_accept_char(int c)
return vim_iswordc(c);
}
+/// Get the completed text by inferring the case of the originally typed text.
+/// If the result is in allocated memory "tofree" is set to it.
+static char *ins_compl_infercase_gettext(const char *str, int char_len, int compl_char_len,
+ int min_len, char **tofree)
+{
+ bool has_lower = false;
+ bool was_letter = false;
+
+ // Allocate wide character array for the completion and fill it.
+ int *const wca = xmalloc((size_t)char_len * sizeof(*wca));
+ {
+ const char *p = str;
+ for (int i = 0; i < char_len; i++) {
+ wca[i] = mb_ptr2char_adv(&p);
+ }
+ }
+
+ // Rule 1: Were any chars converted to lower?
+ {
+ const char *p = compl_orig_text;
+ for (int i = 0; i < min_len; i++) {
+ const int c = mb_ptr2char_adv(&p);
+ if (mb_islower(c)) {
+ has_lower = true;
+ if (mb_isupper(wca[i])) {
+ // Rule 1 is satisfied.
+ for (i = compl_char_len; i < char_len; i++) {
+ wca[i] = mb_tolower(wca[i]);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ // Rule 2: No lower case, 2nd consecutive letter converted to
+ // upper case.
+ if (!has_lower) {
+ const char *p = compl_orig_text;
+ for (int i = 0; i < min_len; i++) {
+ const int c = mb_ptr2char_adv(&p);
+ if (was_letter && mb_isupper(c) && mb_islower(wca[i])) {
+ // Rule 2 is satisfied.
+ for (i = compl_char_len; i < char_len; i++) {
+ wca[i] = mb_toupper(wca[i]);
+ }
+ break;
+ }
+ was_letter = mb_islower(c) || mb_isupper(c);
+ }
+ }
+
+ // Copy the original case of the part we typed.
+ {
+ const char *p = compl_orig_text;
+ for (int i = 0; i < min_len; i++) {
+ const int c = mb_ptr2char_adv(&p);
+ if (mb_islower(c)) {
+ wca[i] = mb_tolower(wca[i]);
+ } else if (mb_isupper(c)) {
+ wca[i] = mb_toupper(wca[i]);
+ }
+ }
+ }
+
+ // Generate encoding specific output from wide character array.
+ garray_T gap;
+ char *p = IObuff;
+ int i = 0;
+ ga_init(&gap, 1, 500);
+ while (i < char_len) {
+ if (gap.ga_data != NULL) {
+ ga_grow(&gap, 10);
+ assert(gap.ga_data != NULL); // suppress clang "Dereference of NULL pointer"
+ p = (char *)gap.ga_data + gap.ga_len;
+ gap.ga_len += utf_char2bytes(wca[i++], p);
+ } else if ((p - IObuff) + 6 >= IOSIZE) {
+ // Multi-byte characters can occupy up to five bytes more than
+ // ASCII characters, and we also need one byte for NUL, so when
+ // getting to six bytes from the edge of IObuff switch to using a
+ // growarray. Add the character in the next round.
+ ga_grow(&gap, IOSIZE);
+ *p = NUL;
+ STRCPY(gap.ga_data, IObuff);
+ gap.ga_len = (int)strlen(IObuff);
+ } else {
+ p += utf_char2bytes(wca[i++], p);
+ }
+ }
+ xfree(wca);
+
+ if (gap.ga_data != NULL) {
+ *tofree = gap.ga_data;
+ return gap.ga_data;
+ }
+
+ *p = NUL;
+ return IObuff;
+}
+
/// This is like ins_compl_add(), but if 'ic' and 'inf' are set, then the
/// case of the originally typed text is used, and the case of the completed
/// text is inferred, ie this tries to work out what case you probably wanted
/// the rest of the word to be in -- webb
///
/// @param[in] cont_s_ipos next ^X<> will set initial_pos
-int ins_compl_add_infercase(char_u *str_arg, int len, bool icase, char_u *fname, Direction dir,
+int ins_compl_add_infercase(char *str_arg, int len, bool icase, char *fname, Direction dir,
bool cont_s_ipos)
FUNC_ATTR_NONNULL_ARG(1)
{
- char_u *str = str_arg;
- int i, c;
- int actual_len; // Take multi-byte characters
- int actual_compl_length; // into account.
- int min_len;
- bool has_lower = false;
- bool was_letter = false;
+ char *str = str_arg;
+ int char_len; // count multi-byte characters
+ int compl_char_len;
int flags = 0;
+ char *tofree = NULL;
if (p_ic && curbuf->b_p_inf && len > 0) {
// Infer case of completed part.
// Find actual length of completion.
{
- const char_u *p = str;
- actual_len = 0;
+ const char *p = str;
+ char_len = 0;
while (*p != NUL) {
MB_PTR_ADV(p);
- actual_len++;
+ char_len++;
}
}
// Find actual length of original text.
{
- const char_u *p = compl_orig_text;
- actual_compl_length = 0;
+ const char *p = compl_orig_text;
+ compl_char_len = 0;
while (*p != NUL) {
MB_PTR_ADV(p);
- actual_compl_length++;
+ compl_char_len++;
}
}
- // "actual_len" may be smaller than "actual_compl_length" when using
+ // "char_len" may be smaller than "compl_char_len" when using
// thesaurus, only use the minimum when comparing.
- min_len = actual_len < actual_compl_length
- ? actual_len : actual_compl_length;
-
- // Allocate wide character array for the completion and fill it.
- int *const wca = xmalloc((size_t)actual_len * sizeof(*wca));
- {
- const char_u *p = str;
- for (i = 0; i < actual_len; i++) {
- wca[i] = mb_ptr2char_adv(&p);
- }
- }
-
- // Rule 1: Were any chars converted to lower?
- {
- const char_u *p = compl_orig_text;
- for (i = 0; i < min_len; i++) {
- c = mb_ptr2char_adv(&p);
- if (mb_islower(c)) {
- has_lower = true;
- if (mb_isupper(wca[i])) {
- // Rule 1 is satisfied.
- for (i = actual_compl_length; i < actual_len; i++) {
- wca[i] = mb_tolower(wca[i]);
- }
- break;
- }
- }
- }
- }
-
- // Rule 2: No lower case, 2nd consecutive letter converted to
- // upper case.
- if (!has_lower) {
- const char_u *p = compl_orig_text;
- for (i = 0; i < min_len; i++) {
- c = mb_ptr2char_adv(&p);
- if (was_letter && mb_isupper(c) && mb_islower(wca[i])) {
- // Rule 2 is satisfied.
- for (i = actual_compl_length; i < actual_len; i++) {
- wca[i] = mb_toupper(wca[i]);
- }
- break;
- }
- was_letter = mb_islower(c) || mb_isupper(c);
- }
- }
-
- // Copy the original case of the part we typed.
- {
- const char_u *p = compl_orig_text;
- for (i = 0; i < min_len; i++) {
- c = mb_ptr2char_adv(&p);
- if (mb_islower(c)) {
- wca[i] = mb_tolower(wca[i]);
- } else if (mb_isupper(c)) {
- wca[i] = mb_toupper(wca[i]);
- }
- }
- }
+ int min_len = char_len < compl_char_len ? char_len : compl_char_len;
- // Generate encoding specific output from wide character array.
- // Multi-byte characters can occupy up to five bytes more than
- // ASCII characters, and we also need one byte for NUL, so stay
- // six bytes away from the edge of IObuff.
- {
- char_u *p = IObuff;
- i = 0;
- while (i < actual_len && (p - IObuff + 6) < IOSIZE) {
- p += utf_char2bytes(wca[i++], (char *)p);
- }
- *p = NUL;
- }
-
- xfree(wca);
-
- str = IObuff;
+ str = ins_compl_infercase_gettext(str, char_len, compl_char_len, min_len, &tofree);
}
if (cont_s_ipos) {
flags |= CP_CONT_S_IPOS;
@@ -607,29 +739,37 @@ int ins_compl_add_infercase(char_u *str_arg, int len, bool icase, char_u *fname,
flags |= CP_ICASE;
}
- return ins_compl_add(str, len, fname, NULL, false, NULL, dir, flags, false);
+ int res = ins_compl_add(str, len, fname, NULL, false, NULL, dir, flags, false);
+ xfree(tofree);
+ return res;
}
/// Add a match to the list of matches
///
-/// @param[in] str Match to add.
-/// @param[in] len Match length, -1 to use #STRLEN.
-/// @param[in] fname File name match comes from. May be NULL.
-/// @param[in] cptext Extra text for popup menu. May be NULL. If not NULL,
-/// must have exactly #CPT_COUNT items.
+/// @param[in] str text of the match to add
+/// @param[in] len length of "str". If -1, then the length of "str" is computed.
+/// @param[in] fname file name to associate with this match. May be NULL.
+/// @param[in] cptext list of strings to use with this match (for abbr, menu, info
+/// and kind). May be NULL.
+/// If not NULL, must have exactly #CPT_COUNT items.
/// @param[in] cptext_allocated If true, will not copy cptext strings.
///
/// @note Will free strings in case of error.
/// cptext itself will not be freed.
-/// @param[in] cdir Completion direction.
-/// @param[in] adup True if duplicate matches are to be accepted.
+/// @param[in] user_data user supplied data (any vim type) for this match
+/// @param[in] cdir match direction. If 0, use "compl_direction".
+/// @param[in] flags_arg match flags (cp_flags)
+/// @param[in] adup accept this match even if it is already present.
+///
+/// If "cdir" is FORWARD, then the match is added after the current match.
+/// Otherwise, it is added before the current match.
///
/// @return NOTDONE if the given string is already in the list of completions,
/// otherwise it is added to the list and OK is returned. FAIL will be
/// returned in case of error.
-static int ins_compl_add(char_u *const str, int len, char_u *const fname,
- char_u *const *const cptext, const bool cptext_allocated,
- typval_T *user_data, const Direction cdir, int flags_arg, const bool adup)
+static int ins_compl_add(char *const str, int len, char *const fname, char *const *const cptext,
+ const bool cptext_allocated, typval_T *user_data, const Direction cdir,
+ int flags_arg, const bool adup)
FUNC_ATTR_NONNULL_ARG(1)
{
compl_T *match;
@@ -654,21 +794,21 @@ static int ins_compl_add(char_u *const str, int len, char_u *const fname,
return FAIL;
}
if (len < 0) {
- len = (int)STRLEN(str);
+ len = (int)strlen(str);
}
// If the same match is already present, don't add it.
if (compl_first_match != NULL && !adup) {
match = compl_first_match;
do {
- if (!(match->cp_flags & CP_ORIGINAL_TEXT)
- && STRNCMP(match->cp_str, str, len) == 0
- && match->cp_str[len] == NUL) {
+ if (!match_at_original_text(match)
+ && strncmp(match->cp_str, str, (size_t)len) == 0
+ && ((int)strlen(match->cp_str) <= len || match->cp_str[len] == NUL)) {
FREE_CPTEXT(cptext, cptext_allocated);
return NOTDONE;
}
match = match->cp_next;
- } while (match != NULL && match != compl_first_match);
+ } while (match != NULL && !is_first_match(match));
}
// Remove any popup menu before changing the list of matches.
@@ -681,7 +821,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, (size_t)len);
+ match->cp_str = xstrnsave(str, (size_t)len);
// match-fname is:
// - compl_curr_match->cp_fname if it is a string equal to fname.
@@ -690,10 +830,10 @@ static int ins_compl_add(char_u *const str, int len, char_u *const fname,
if (fname != NULL
&& compl_curr_match != NULL
&& compl_curr_match->cp_fname != NULL
- && STRCMP(fname, compl_curr_match->cp_fname) == 0) {
+ && strcmp(fname, compl_curr_match->cp_fname) == 0) {
match->cp_fname = compl_curr_match->cp_fname;
} else if (fname != NULL) {
- match->cp_fname = vim_strsave(fname);
+ match->cp_fname = xstrdup(fname);
flags |= CP_FREE_FNAME;
} else {
match->cp_fname = NULL;
@@ -708,9 +848,7 @@ static int ins_compl_add(char_u *const str, int len, char_u *const fname,
continue;
}
if (*cptext[i] != NUL) {
- match->cp_text[i] = (cptext_allocated
- ? cptext[i]
- : (char_u *)xstrdup((char *)cptext[i]));
+ match->cp_text[i] = (cptext_allocated ? cptext[i] : xstrdup(cptext[i]));
} else if (cptext_allocated) {
xfree(cptext[i]);
}
@@ -721,7 +859,8 @@ static int ins_compl_add(char_u *const str, int len, char_u *const fname,
match->cp_user_data = *user_data;
}
- // Link the new match structure in the list of matches.
+ // Link the new match structure after (FORWARD) or before (BACKWARD) the
+ // current match in the list of matches .
if (compl_first_match == NULL) {
match->cp_next = match->cp_prev = NULL;
} else if (dir == FORWARD) {
@@ -755,7 +894,7 @@ static int ins_compl_add(char_u *const str, int len, char_u *const fname,
/// @param match completion match
/// @param str character string to check
/// @param len length of "str"
-static bool ins_compl_equal(compl_T *match, char_u *str, size_t len)
+static bool ins_compl_equal(compl_T *match, char *str, size_t len)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
if (match->cp_flags & CP_EQUAL) {
@@ -764,19 +903,20 @@ static bool ins_compl_equal(compl_T *match, char_u *str, size_t len)
if (match->cp_flags & CP_ICASE) {
return STRNICMP(match->cp_str, str, len) == 0;
}
- return STRNCMP(match->cp_str, str, len) == 0;
+ return strncmp(match->cp_str, str, len) == 0;
}
/// Reduce the longest common string for match "match".
static void ins_compl_longest_match(compl_T *match)
{
- char_u *p, *s;
+ char *p, *s;
int c1, c2;
int had_match;
if (compl_leader == NULL) {
// First match, use it as a whole.
- compl_leader = vim_strsave(match->cp_str);
+ compl_leader = xstrdup(match->cp_str);
+
had_match = (curwin->w_cursor.col > compl_col);
ins_compl_delete();
ins_bytes(compl_leader + get_compl_len());
@@ -788,40 +928,42 @@ static void ins_compl_longest_match(compl_T *match)
ins_compl_delete();
}
compl_used_match = false;
- } else {
- // Reduce the text if this match differs from compl_leader.
- p = compl_leader;
- s = match->cp_str;
- while (*p != NUL) {
- c1 = utf_ptr2char((char *)p);
- c2 = utf_ptr2char((char *)s);
-
- if ((match->cp_flags & CP_ICASE)
- ? (mb_tolower(c1) != mb_tolower(c2))
- : (c1 != c2)) {
- break;
- }
- MB_PTR_ADV(p);
- MB_PTR_ADV(s);
- }
- if (*p != NUL) {
- // Leader was shortened, need to change the inserted text.
- *p = NUL;
- had_match = (curwin->w_cursor.col > compl_col);
- ins_compl_delete();
- ins_bytes(compl_leader + get_compl_len());
- ins_redraw(false);
+ return;
+ }
- // When the match isn't there (to avoid matching itself) remove it
- // again after redrawing.
- if (!had_match) {
- ins_compl_delete();
- }
+ // Reduce the text if this match differs from compl_leader.
+ p = compl_leader;
+ s = match->cp_str;
+ while (*p != NUL) {
+ c1 = utf_ptr2char(p);
+ c2 = utf_ptr2char(s);
+
+ if ((match->cp_flags & CP_ICASE)
+ ? (mb_tolower(c1) != mb_tolower(c2))
+ : (c1 != c2)) {
+ break;
}
+ MB_PTR_ADV(p);
+ MB_PTR_ADV(s);
+ }
- compl_used_match = false;
+ if (*p != NUL) {
+ // Leader was shortened, need to change the inserted text.
+ *p = NUL;
+ had_match = (curwin->w_cursor.col > compl_col);
+ ins_compl_delete();
+ ins_bytes(compl_leader + get_compl_len());
+ ins_redraw(false);
+
+ // When the match isn't there (to avoid matching itself) remove it
+ // again after redrawing.
+ if (!had_match) {
+ ins_compl_delete();
+ }
}
+
+ compl_used_match = false;
}
/// Add an array of matches to the list of matches.
@@ -832,7 +974,7 @@ static void ins_compl_add_matches(int num_matches, char **matches, int icase)
Direction dir = compl_direction;
for (int i = 0; i < num_matches && add_r != FAIL; i++) {
- if ((add_r = ins_compl_add((char_u *)matches[i], -1, NULL, NULL, false, NULL, dir,
+ if ((add_r = ins_compl_add(matches[i], -1, NULL, NULL, false, NULL, dir,
CP_FAST | (icase ? CP_ICASE : 0),
false)) == OK) {
// If dir was BACKWARD then honor it just once.
@@ -846,20 +988,21 @@ static void ins_compl_add_matches(int num_matches, char **matches, int icase)
/// Return the number of matches (excluding the original).
static int ins_compl_make_cyclic(void)
{
- compl_T *match;
- int count = 0;
+ if (compl_first_match == NULL) {
+ return 0;
+ }
- if (compl_first_match != NULL) {
- // Find the end of the list.
- match = compl_first_match;
- // there's always an entry for the compl_orig_text, it doesn't count.
- while (match->cp_next != NULL && match->cp_next != compl_first_match) {
- match = match->cp_next;
- count++;
- }
- match->cp_next = compl_first_match;
- compl_first_match->cp_prev = match;
+ // Find the end of the list.
+ compl_T *match = compl_first_match;
+ int count = 0;
+ // there's always an entry for the compl_orig_text, it doesn't count.
+ while (match->cp_next != NULL && !is_first_match(match->cp_next)) {
+ match = match->cp_next;
+ count++;
}
+ match->cp_next = compl_first_match;
+ compl_first_match->cp_prev = match;
+
return count;
}
@@ -873,7 +1016,7 @@ bool ins_compl_has_shown_match(void)
bool ins_compl_long_shown_match(void)
{
return compl_shown_match != NULL && compl_shown_match->cp_str != NULL
- && (colnr_T)STRLEN(compl_shown_match->cp_str) > curwin->w_cursor.col - compl_col;
+ && (colnr_T)strlen(compl_shown_match->cp_str) > curwin->w_cursor.col - compl_col;
}
/// Set variables that store noselect and noinsert behavior from the
@@ -882,12 +1025,16 @@ void completeopt_was_set(void)
{
compl_no_insert = false;
compl_no_select = false;
- if (strstr((char *)p_cot, "noselect") != NULL) {
+ compl_longest = false;
+ if (strstr(p_cot, "noselect") != NULL) {
compl_no_select = true;
}
- if (strstr((char *)p_cot, "noinsert") != NULL) {
+ if (strstr(p_cot, "noinsert") != NULL) {
compl_no_insert = true;
}
+ if (strstr(p_cot, "longest") != NULL) {
+ compl_longest = true;
+ }
}
/// "compl_match_array" points the currently displayed list of entries in the
@@ -898,10 +1045,12 @@ static int compl_match_arraysize;
/// Remove any popup menu.
static void ins_compl_del_pum(void)
{
- if (compl_match_array != NULL) {
- pum_undisplay(false);
- XFREE_CLEAR(compl_match_array);
+ if (compl_match_array == NULL) {
+ return;
}
+
+ pum_undisplay(false);
+ XFREE_CLEAR(compl_match_array);
}
/// Check if the popup menu should be displayed.
@@ -909,7 +1058,7 @@ bool pum_wanted(void)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
// "completeopt" must contain "menu" or "menuone"
- return vim_strchr((char *)p_cot, 'm') != NULL;
+ return vim_strchr(p_cot, 'm') != NULL;
}
/// Check that there are two or more matches to be shown in the popup menu.
@@ -919,17 +1068,16 @@ static bool pum_enough_matches(void)
{
// Don't display the popup menu if there are no matches or there is only
// one (ignoring the original text).
- compl_T *comp = compl_first_match;
+ compl_T *compl = compl_first_match;
int i = 0;
do {
- if (comp == NULL
- || ((comp->cp_flags & CP_ORIGINAL_TEXT) == 0 && ++i == 2)) {
+ if (compl == NULL || (!match_at_original_text(compl) && ++i == 2)) {
break;
}
- comp = comp->cp_next;
- } while (comp != compl_first_match);
+ compl = compl->cp_next;
+ } while (!is_first_match(compl));
- if (strstr((char *)p_cot, "menuone") != NULL) {
+ if (strstr(p_cot, "menuone") != NULL) {
return i >= 1;
}
return i >= 2;
@@ -953,6 +1101,8 @@ static dict_T *ins_compl_dict_alloc(compl_T *match)
return dict;
}
+/// Trigger the CompleteChanged autocmd event. Invoked each time the Insert mode
+/// completion menu is changed.
static void trigger_complete_changed_event(int cur)
{
static bool recursive = false;
@@ -981,141 +1131,151 @@ static void trigger_complete_changed_event(int cur)
restore_v_event(v_event, &save_v_event);
}
-/// Show the popup menu for the list of matches.
-/// Also adjusts "compl_shown_match" to an entry that is actually displayed.
-void ins_compl_show_pum(void)
+/// Build a popup menu to show the completion matches.
+///
+/// @return the popup menu entry that should be selected,
+/// -1 if nothing should be selected.
+static int ins_compl_build_pum(void)
{
- compl_T *compl;
- compl_T *shown_compl = NULL;
- bool did_find_shown_match = false;
- bool shown_match_ok = false;
- int i;
- int cur = -1;
- colnr_T col;
- int lead_len = 0;
- bool array_changed = false;
+ // Need to build the popup menu list.
+ compl_match_arraysize = 0;
+ compl_T *compl = compl_first_match;
- if (!pum_wanted() || !pum_enough_matches()) {
- return;
+ // If it's user complete function and refresh_always,
+ // do not use "compl_leader" as prefix filter.
+ if (ins_compl_need_restart()) {
+ XFREE_CLEAR(compl_leader);
}
- // Dirty hard-coded hack: remove any matchparen highlighting.
- do_cmdline_cmd("if exists('g:loaded_matchparen')|3match none|endif");
-
- // Update the screen before drawing the popup menu over it.
- update_screen(0);
+ const int lead_len = compl_leader != NULL ? (int)strlen(compl_leader) : 0;
- if (compl_match_array == NULL) {
- array_changed = true;
- // Need to build the popup menu list.
- compl_match_arraysize = 0;
- compl = compl_first_match;
- // If it's user complete function and refresh_always,
- // do not use "compl_leader" as prefix filter.
- if (ins_compl_need_restart()) {
- XFREE_CLEAR(compl_leader);
- }
- if (compl_leader != NULL) {
- lead_len = (int)STRLEN(compl_leader);
- }
- do {
- if ((compl->cp_flags & CP_ORIGINAL_TEXT) == 0
- && (compl_leader == NULL
- || ins_compl_equal(compl, compl_leader, (size_t)lead_len))) {
- compl_match_arraysize++;
- }
- compl = compl->cp_next;
- } while (compl != NULL && compl != compl_first_match);
- if (compl_match_arraysize == 0) {
- return;
+ do {
+ if (!match_at_original_text(compl)
+ && (compl_leader == NULL
+ || ins_compl_equal(compl, compl_leader, (size_t)lead_len))) {
+ compl_match_arraysize++;
}
+ compl = compl->cp_next;
+ } while (compl != NULL && !is_first_match(compl));
- assert(compl_match_arraysize >= 0);
- 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;
- }
+ if (compl_match_arraysize == 0) {
+ return -1;
+ }
- i = 0;
- compl = compl_first_match;
- do {
- if ((compl->cp_flags & CP_ORIGINAL_TEXT) == 0
- && (compl_leader == NULL
- || 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
- // first displayed item after the shown match.
- compl_shown_match = compl;
- did_find_shown_match = true;
- shown_match_ok = true;
- } else {
- // Remember this displayed match for when the
- // shown match is just below it.
- shown_compl = compl;
- }
- cur = i;
- }
+ assert(compl_match_arraysize >= 0);
+ compl_match_array = xcalloc((size_t)compl_match_arraysize, sizeof(pumitem_T));
- if (compl->cp_text[CPT_ABBR] != NULL) {
- compl_match_array[i].pum_text =
- compl->cp_text[CPT_ABBR];
- } else {
- compl_match_array[i].pum_text = compl->cp_str;
- }
- compl_match_array[i].pum_kind = compl->cp_text[CPT_KIND];
- compl_match_array[i].pum_info = compl->cp_text[CPT_INFO];
- if (compl->cp_text[CPT_MENU] != NULL) {
- compl_match_array[i++].pum_extra =
- compl->cp_text[CPT_MENU];
+ // If the current match is the original text don't find the first
+ // match after it, don't highlight anything.
+ bool shown_match_ok = match_at_original_text(compl_shown_match);
+
+ compl_T *shown_compl = NULL;
+ bool did_find_shown_match = false;
+ int cur = -1;
+ int i = 0;
+ compl = compl_first_match;
+ do {
+ if (!match_at_original_text(compl)
+ && (compl_leader == NULL
+ || 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
+ // first displayed item after the shown match.
+ compl_shown_match = compl;
+ did_find_shown_match = true;
+ shown_match_ok = true;
} else {
- compl_match_array[i++].pum_extra = compl->cp_fname;
+ // Remember this displayed match for when the
+ // shown match is just below it.
+ shown_compl = compl;
}
+ cur = i;
}
- if (compl == compl_shown_match) {
- did_find_shown_match = true;
+ if (compl->cp_text[CPT_ABBR] != NULL) {
+ compl_match_array[i].pum_text = compl->cp_text[CPT_ABBR];
+ } else {
+ compl_match_array[i].pum_text = compl->cp_str;
+ }
+ compl_match_array[i].pum_kind = compl->cp_text[CPT_KIND];
+ compl_match_array[i].pum_info = compl->cp_text[CPT_INFO];
+ if (compl->cp_text[CPT_MENU] != NULL) {
+ compl_match_array[i++].pum_extra = compl->cp_text[CPT_MENU];
+ } else {
+ compl_match_array[i++].pum_extra = compl->cp_fname;
+ }
+ }
- // When the original text is the shown match don't set
- // compl_shown_match.
- if (compl->cp_flags & CP_ORIGINAL_TEXT) {
- shown_match_ok = true;
- }
+ if (compl == compl_shown_match) {
+ did_find_shown_match = true;
- if (!shown_match_ok && shown_compl != NULL) {
- // The shown match isn't displayed, set it to the
- // previously displayed match.
- compl_shown_match = shown_compl;
- shown_match_ok = true;
- }
+ // When the original text is the shown match don't set
+ // compl_shown_match.
+ if (match_at_original_text(compl)) {
+ shown_match_ok = true;
}
- compl = compl->cp_next;
- } while (compl != NULL && compl != compl_first_match);
- if (!shown_match_ok) { // no displayed match at all
- cur = -1;
+ if (!shown_match_ok && shown_compl != NULL) {
+ // The shown match isn't displayed, set it to the
+ // previously displayed match.
+ compl_shown_match = shown_compl;
+ shown_match_ok = true;
+ }
}
+ compl = compl->cp_next;
+ } while (compl != NULL && !is_first_match(compl));
+
+ if (!shown_match_ok) { // no displayed match at all
+ cur = -1;
+ }
+
+ return cur;
+}
+
+/// Show the popup menu for the list of matches.
+/// Also adjusts "compl_shown_match" to an entry that is actually displayed.
+void ins_compl_show_pum(void)
+{
+ if (!pum_wanted() || !pum_enough_matches()) {
+ return;
+ }
+
+ // Dirty hard-coded hack: remove any matchparen highlighting.
+ do_cmdline_cmd("if exists('g:loaded_matchparen')|3match none|endif");
+
+ // Update the screen before drawing the popup menu over it.
+ update_screen();
+
+ int cur = -1;
+ bool array_changed = false;
+
+ if (compl_match_array == NULL) {
+ array_changed = true;
+ // Need to build the popup menu list.
+ cur = ins_compl_build_pum();
} else {
// popup menu already exists, only need to find the current item.
- for (i = 0; i < compl_match_arraysize; i++) {
+ for (int i = 0; i < compl_match_arraysize; i++) {
if (compl_match_array[i].pum_text == compl_shown_match->cp_str
- || compl_match_array[i].pum_text
- == compl_shown_match->cp_text[CPT_ABBR]) {
+ || compl_match_array[i].pum_text == compl_shown_match->cp_text[CPT_ABBR]) {
cur = i;
break;
}
}
}
+ if (compl_match_array == NULL) {
+ return;
+ }
+
// In Replace mode when a $ is displayed at the end of the line only
// part of the screen would be updated. We do need to redraw here.
dollar_vcol = -1;
// Compute the screen column of the start of the completed text.
// Use the cursor to get all wrapping and other settings right.
- col = curwin->w_cursor.col;
+ const colnr_T col = curwin->w_cursor.col;
curwin->w_cursor.col = compl_col;
pum_selected_item = cur;
pum_display(compl_match_array, compl_match_arraysize, cur, array_changed, 0);
@@ -1129,15 +1289,15 @@ void ins_compl_show_pum(void)
#define DICT_FIRST (1) ///< use just first element in "dict"
#define DICT_EXACT (2) ///< "dict" is the exact name of a file
-/// Add any identifiers that match the given pattern in the list of dictionary
-/// files "dict_start" to the list of completions.
+/// Add any identifiers that match the given pattern "pat" in the list of
+/// dictionary files "dict_start" to the list of completions.
///
/// @param flags DICT_FIRST and/or DICT_EXACT
/// @param thesaurus Thesaurus completion
-static void ins_compl_dictionaries(char_u *dict_start, char_u *pat, int flags, int thesaurus)
+static void ins_compl_dictionaries(char *dict_start, char *pat, int flags, int thesaurus)
{
- char *dict = (char *)dict_start;
- char_u *ptr;
+ char *dict = dict_start;
+ char *ptr;
char *buf;
regmatch_T regmatch;
char **files;
@@ -1168,16 +1328,16 @@ static void ins_compl_dictionaries(char_u *dict_start, char_u *pat, int flags, i
// to only match at the start of a line. Otherwise just match the
// pattern. Also need to double backslashes.
if (ctrl_x_mode_line_or_eval()) {
- char_u *pat_esc = vim_strsave_escaped(pat, (char_u *)"\\");
+ char *pat_esc = vim_strsave_escaped(pat, "\\");
- size_t len = STRLEN(pat_esc) + 10;
+ 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((char *)ptr, RE_MAGIC);
+ vim_snprintf(ptr, len, "^\\s*\\zs\\V%s", pat_esc);
+ regmatch.regprog = vim_regcomp(ptr, RE_MAGIC);
xfree(pat_esc);
xfree(ptr);
} else {
- regmatch.regprog = vim_regcomp((char *)pat, p_magic ? RE_MAGIC : 0);
+ regmatch.regprog = vim_regcomp(pat, magic_isset() ? RE_MAGIC : 0);
if (regmatch.regprog == NULL) {
goto theend;
}
@@ -1195,7 +1355,7 @@ static void ins_compl_dictionaries(char_u *dict_start, char_u *pat, int flags, i
// backticks (for security, the 'dict' option may have been set in
// a modeline).
copy_option_part(&dict, buf, LSIZE, ",");
- if (!thesaurus && STRCMP(buf, "spell") == 0) {
+ if (!thesaurus && strcmp(buf, "spell") == 0) {
count = -1;
} else if (vim_strchr(buf, '`') != NULL
|| expand_wildcards(1, &buf, &count, &files,
@@ -1215,7 +1375,7 @@ static void ins_compl_dictionaries(char_u *dict_start, char_u *pat, int flags, i
spell_dump_compl(ptr, regmatch.rm_ic, &dir, 0);
} else if (count > 0) { // avoid warning for using "files" uninit
ins_compl_files(count, files, thesaurus, flags,
- &regmatch, (char_u *)buf, &dir);
+ &regmatch, buf, &dir);
if (flags != DICT_EXACT) {
FreeWild(count, files);
}
@@ -1231,22 +1391,70 @@ theend:
xfree(buf);
}
+/// Add all the words in the line "*buf_arg" from the thesaurus file "fname"
+/// skipping the word at 'skip_word'.
+///
+/// @return OK on success.
+static int thesaurus_add_words_in_line(char *fname, char **buf_arg, int dir, const char *skip_word)
+{
+ int status = OK;
+
+ // Add the other matches on the line
+ char *ptr = *buf_arg;
+ while (!got_int) {
+ // Find start of the next word. Skip white
+ // space and punctuation.
+ ptr = find_word_start(ptr);
+ if (*ptr == NUL || *ptr == NL) {
+ break;
+ }
+ char *wstart = ptr;
+
+ // Find end of the word.
+ // Japanese words may have characters in
+ // different classes, only separate words
+ // with single-byte non-word characters.
+ while (*ptr != NUL) {
+ const int l = utfc_ptr2len((const char *)ptr);
+
+ if (l < 2 && !vim_iswordc((uint8_t)(*ptr))) {
+ break;
+ }
+ ptr += l;
+ }
+
+ // Add the word. Skip the regexp match.
+ if (wstart != skip_word) {
+ status = ins_compl_add_infercase(wstart, (int)(ptr - wstart), p_ic,
+ fname, dir, false);
+ if (status == FAIL) {
+ break;
+ }
+ }
+ }
+
+ *buf_arg = ptr;
+ return status;
+}
+
+/// Process "count" dictionary/thesaurus "files" and add the text matching
+/// "regmatch".
static void ins_compl_files(int count, char **files, int thesaurus, int flags, regmatch_T *regmatch,
- char_u *buf, Direction *dir)
+ char *buf, Direction *dir)
FUNC_ATTR_NONNULL_ARG(2, 7)
{
- char_u *ptr;
+ char *ptr;
int i;
FILE *fp;
int add_r;
for (i = 0; i < count && !got_int && !compl_interrupted; i++) {
fp = os_fopen(files[i], "r"); // open dictionary file
- if (flags != DICT_EXACT) {
+ if (flags != DICT_EXACT && !shortmess(SHM_COMPLETIONSCAN)) {
msg_hist_off = true; // reset in msg_trunc_attr()
- vim_snprintf((char *)IObuff, IOSIZE,
+ vim_snprintf(IObuff, IOSIZE,
_("Scanning dictionary: %s"), files[i]);
- (void)msg_trunc_attr((char *)IObuff, true, HL_ATTR(HLF_R));
+ (void)msg_trunc_attr(IObuff, true, HL_ATTR(HLF_R));
}
if (fp == NULL) {
@@ -1255,10 +1463,9 @@ static void ins_compl_files(int count, char **files, int thesaurus, int flags, r
// Read dictionary file line by line.
// Check each line for a match.
- while (!got_int && !compl_interrupted
- && !vim_fgets(buf, LSIZE, fp)) {
+ while (!got_int && !compl_interrupted && !vim_fgets(buf, LSIZE, fp)) {
ptr = buf;
- while (vim_regexec(regmatch, (char *)buf, (colnr_T)(ptr - buf))) {
+ while (vim_regexec(regmatch, buf, (colnr_T)(ptr - buf))) {
ptr = regmatch->startp[0];
if (ctrl_x_mode_line_or_eval()) {
ptr = find_line_end(ptr);
@@ -1267,40 +1474,11 @@ static void ins_compl_files(int count, char **files, int thesaurus, int flags, r
}
add_r = ins_compl_add_infercase(regmatch->startp[0],
(int)(ptr - regmatch->startp[0]),
- p_ic, (char_u *)files[i], *dir, false);
+ p_ic, files[i], *dir, false);
if (thesaurus) {
- char_u *wstart;
-
- // Add the other matches on the line
+ // For a thesaurus, add all the words in the line
ptr = buf;
- while (!got_int) {
- // Find start of the next word. Skip white
- // space and punctuation.
- ptr = find_word_start(ptr);
- if (*ptr == NUL || *ptr == NL) {
- break;
- }
- wstart = ptr;
-
- // Find end of the word.
- // Japanese words may have characters in
- // different classes, only separate words
- // with single-byte non-word characters.
- while (*ptr != NUL) {
- const int l = utfc_ptr2len((char *)ptr);
-
- if (l < 2 && !vim_iswordc(*ptr)) {
- break;
- }
- ptr += l;
- }
-
- // Add the word. Skip the regexp match.
- if (wstart != regmatch->startp[0]) {
- add_r = ins_compl_add_infercase(wstart, (int)(ptr - wstart),
- p_ic, (char_u *)files[i], *dir, false);
- }
- }
+ add_r = thesaurus_add_words_in_line(files[i], &ptr, *dir, regmatch->startp[0]);
}
if (add_r == OK) {
// if dir was BACKWARD then honor it just once
@@ -1323,24 +1501,24 @@ static void ins_compl_files(int count, char **files, int thesaurus, int flags, r
/// Find the start of the next word.
/// Returns a pointer to the first char of the word. Also stops at a NUL.
-char_u *find_word_start(char_u *ptr)
+char *find_word_start(char *ptr)
FUNC_ATTR_PURE
{
while (*ptr != NUL && *ptr != '\n' && mb_get_class(ptr) <= 1) {
- ptr += utfc_ptr2len((char *)ptr);
+ ptr += utfc_ptr2len(ptr);
}
return ptr;
}
/// Find the end of the word. Assumes it starts inside a word.
/// Returns a pointer to just after the word.
-char_u *find_word_end(char_u *ptr)
+char *find_word_end(char *ptr)
FUNC_ATTR_PURE
{
const int start_class = mb_get_class(ptr);
if (start_class > 1) {
while (*ptr != NUL) {
- ptr += utfc_ptr2len((char *)ptr);
+ ptr += utfc_ptr2len(ptr);
if (mb_get_class(ptr) != start_class) {
break;
}
@@ -1350,12 +1528,13 @@ char_u *find_word_end(char_u *ptr)
}
/// Find the end of the line, omitting CR and NL at the end.
-/// Returns a pointer to just after the line.
-static char_u *find_line_end(char_u *ptr)
+///
+/// @return a pointer to just after the line.
+static char *find_line_end(char *ptr)
{
- char_u *s;
+ char *s;
- s = ptr + STRLEN(ptr);
+ s = ptr + strlen(ptr);
while (s > ptr && (s[-1] == CAR || s[-1] == NL)) {
s--;
}
@@ -1391,12 +1570,13 @@ static void ins_compl_free(void)
}
tv_clear(&match->cp_user_data);
xfree(match);
- } while (compl_curr_match != NULL && compl_curr_match != compl_first_match);
+ } while (compl_curr_match != NULL && !is_first_match(compl_curr_match));
compl_first_match = compl_curr_match = NULL;
compl_shown_match = NULL;
compl_old_match = NULL;
}
+/// Reset/clear the completion state.
void ins_compl_clear(void)
{
compl_cont_status = 0;
@@ -1450,14 +1630,20 @@ colnr_T ins_compl_col(void)
return compl_col;
}
+/// Return the length in bytes of the text being completed
+int ins_compl_len(void)
+{
+ return compl_length;
+}
+
/// Delete one character before the cursor and show the subset of the matches
/// that match the word that is now before the cursor.
/// Returns the character to be used, NUL if the work is done and another char
/// to be got from the user.
int ins_compl_bs(void)
{
- char_u *line = get_cursor_line_ptr();
- char_u *p = line + curwin->w_cursor.col;
+ char *line = get_cursor_line_ptr();
+ char *p = line + curwin->w_cursor.col;
MB_PTR_BACK(line, p);
ptrdiff_t p_off = p - line;
@@ -1479,18 +1665,18 @@ int ins_compl_bs(void)
ins_compl_restart();
}
- // ins_compl_restart() calls update_screen(0) which may invalidate the pointer
+ // ins_compl_restart() calls update_screen() which may invalidate the pointer
// TODO(bfredl): get rid of random update_screen() calls deep inside completion logic
line = get_cursor_line_ptr();
xfree(compl_leader);
- compl_leader = vim_strnsave(line + compl_col, (size_t)(p_off - (ptrdiff_t)compl_col));
+ compl_leader = xstrnsave(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.
compl_curr_match = compl_shown_match;
}
-
return NUL;
}
@@ -1567,7 +1753,7 @@ void ins_compl_addleader(int c)
utf_char2bytes(c, (char *)buf);
buf[cc] = NUL;
- ins_char_bytes((char_u *)buf, (size_t)cc);
+ ins_char_bytes(buf, (size_t)cc);
} else {
ins_char(c);
}
@@ -1578,8 +1764,8 @@ void ins_compl_addleader(int c)
}
xfree(compl_leader);
- compl_leader = vim_strnsave(get_cursor_line_ptr() + compl_col,
- (size_t)(curwin->w_cursor.col - compl_col));
+ compl_leader = xstrnsave(get_cursor_line_ptr() + compl_col,
+ (size_t)(curwin->w_cursor.col - compl_col));
ins_compl_new_leader();
}
@@ -1590,7 +1776,7 @@ static void ins_compl_restart(void)
// update screen before restart.
// so if complete is blocked,
// will stay to the last popup menu and reduce flicker
- update_screen(0);
+ update_screen(); // TODO(bfredl): no.
ins_compl_free();
compl_started = false;
compl_matches = 0;
@@ -1599,19 +1785,19 @@ static void ins_compl_restart(void)
}
/// Set the first match, the original text.
-static void ins_compl_set_original_text(char_u *str)
+static void ins_compl_set_original_text(char *str)
FUNC_ATTR_NONNULL_ALL
{
// Replace the original text entry.
- // The CP_ORIGINAL_TEXT flag is either at the first item or might possibly be
- // at the last item for backward completion
- if (compl_first_match->cp_flags & CP_ORIGINAL_TEXT) { // safety check
+ // The CP_ORIGINAL_TEXT flag is either at the first item or might possibly
+ // be at the last item for backward completion
+ if (match_at_original_text(compl_first_match)) { // safety check
xfree(compl_first_match->cp_str);
- compl_first_match->cp_str = vim_strsave(str);
+ compl_first_match->cp_str = xstrdup(str);
} else if (compl_first_match->cp_prev != NULL
- && (compl_first_match->cp_prev->cp_flags & CP_ORIGINAL_TEXT)) {
+ && match_at_original_text(compl_first_match->cp_prev)) {
xfree(compl_first_match->cp_prev->cp_str);
- compl_first_match->cp_prev->cp_str = vim_strsave(str);
+ compl_first_match->cp_prev->cp_str = xstrdup(str);
}
}
@@ -1619,37 +1805,280 @@ static void ins_compl_set_original_text(char_u *str)
/// matches.
void ins_compl_addfrommatch(void)
{
- char_u *p;
+ char *p;
int len = (int)curwin->w_cursor.col - (int)compl_col;
int c;
compl_T *cp;
assert(compl_shown_match != NULL);
p = compl_shown_match->cp_str;
- if ((int)STRLEN(p) <= len) { // the match is too short
+ if ((int)strlen(p) <= len) { // the match is too short
// When still at the original match use the first entry that matches
// the leader.
- if (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) {
- p = NULL;
- 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, STRLEN(compl_leader))) {
- p = cp->cp_str;
- break;
- }
- }
- if (p == NULL || (int)STRLEN(p) <= len) {
- return;
+ if (!match_at_original_text(compl_shown_match)) {
+ return;
+ }
+
+ p = NULL;
+ for (cp = compl_shown_match->cp_next; cp != NULL
+ && !is_first_match(cp); cp = cp->cp_next) {
+ if (compl_leader == NULL
+ || ins_compl_equal(cp, compl_leader, strlen(compl_leader))) {
+ p = cp->cp_str;
+ break;
}
- } else {
+ }
+ if (p == NULL || (int)strlen(p) <= len) {
return;
}
}
p += len;
- c = utf_ptr2char((char *)p);
+ c = utf_ptr2char(p);
ins_compl_addleader(c);
}
+/// Set the CTRL-X completion mode based on the key "c" typed after a CTRL-X.
+/// Uses the global variables: ctrl_x_mode, edit_submode, edit_submode_pre,
+/// compl_cont_mode and compl_cont_status.
+///
+/// @return true when the character is not to be inserted.
+static bool set_ctrl_x_mode(const int c)
+{
+ bool retval = false;
+
+ switch (c) {
+ case Ctrl_E:
+ case Ctrl_Y:
+ // scroll the window one line up or down
+ ctrl_x_mode = CTRL_X_SCROLL;
+ if (!(State & REPLACE_FLAG)) {
+ edit_submode = _(" (insert) Scroll (^E/^Y)");
+ } else {
+ edit_submode = _(" (replace) Scroll (^E/^Y)");
+ }
+ edit_submode_pre = NULL;
+ showmode();
+ break;
+ case Ctrl_L:
+ // complete whole line
+ ctrl_x_mode = CTRL_X_WHOLE_LINE;
+ break;
+ case Ctrl_F:
+ // complete filenames
+ ctrl_x_mode = CTRL_X_FILES;
+ break;
+ case Ctrl_K:
+ // complete words from a dictionary
+ ctrl_x_mode = CTRL_X_DICTIONARY;
+ break;
+ case Ctrl_R:
+ // Register insertion without exiting CTRL-X mode
+ // Simply allow ^R to happen without affecting ^X mode
+ break;
+ case Ctrl_T:
+ // complete words from a thesaurus
+ ctrl_x_mode = CTRL_X_THESAURUS;
+ break;
+ case Ctrl_U:
+ // user defined completion
+ ctrl_x_mode = CTRL_X_FUNCTION;
+ break;
+ case Ctrl_O:
+ // omni completion
+ ctrl_x_mode = CTRL_X_OMNI;
+ break;
+ case 's':
+ case Ctrl_S:
+ // complete spelling suggestions
+ ctrl_x_mode = CTRL_X_SPELL;
+ emsg_off++; // Avoid getting the E756 error twice.
+ spell_back_to_badword();
+ emsg_off--;
+ break;
+ case Ctrl_RSB:
+ // complete tag names
+ ctrl_x_mode = CTRL_X_TAGS;
+ break;
+ case Ctrl_I:
+ case K_S_TAB:
+ // complete keywords from included files
+ ctrl_x_mode = CTRL_X_PATH_PATTERNS;
+ break;
+ case Ctrl_D:
+ // complete definitions from included files
+ ctrl_x_mode = CTRL_X_PATH_DEFINES;
+ break;
+ case Ctrl_V:
+ case Ctrl_Q:
+ // complete vim commands
+ ctrl_x_mode = CTRL_X_CMDLINE;
+ break;
+ case Ctrl_Z:
+ // stop completion
+ ctrl_x_mode = CTRL_X_NORMAL;
+ edit_submode = NULL;
+ showmode();
+ retval = true;
+ break;
+ case Ctrl_P:
+ case Ctrl_N:
+ // ^X^P means LOCAL expansion if nothing interrupted (eg we
+ // just started ^X mode, or there were enough ^X's to cancel
+ // the previous mode, say ^X^F^X^X^P or ^P^X^X^X^P, see below)
+ // do normal expansion when interrupting a different mode (say
+ // ^X^F^X^P or ^P^X^X^P, see below)
+ // nothing changes if interrupting mode 0, (eg, the flag
+ // doesn't change when going to ADDING mode -- Acevedo
+ if (!(compl_cont_status & CONT_INTRPT)) {
+ compl_cont_status |= CONT_LOCAL;
+ } else if (compl_cont_mode != 0) {
+ compl_cont_status &= ~CONT_LOCAL;
+ }
+ FALLTHROUGH;
+ default:
+ // If we have typed at least 2 ^X's... for modes != 0, we set
+ // compl_cont_status = 0 (eg, as if we had just started ^X
+ // mode).
+ // For mode 0, we set "compl_cont_mode" to an impossible
+ // value, in both cases ^X^X can be used to restart the same
+ // mode (avoiding ADDING mode).
+ // Undocumented feature: In a mode != 0 ^X^P and ^X^X^P start
+ // 'complete' and local ^P expansions respectively.
+ // In mode 0 an extra ^X is needed since ^X^P goes to ADDING
+ // mode -- Acevedo
+ if (c == Ctrl_X) {
+ if (compl_cont_mode != 0) {
+ compl_cont_status = 0;
+ } else {
+ compl_cont_mode = CTRL_X_NOT_DEFINED_YET;
+ }
+ }
+ ctrl_x_mode = CTRL_X_NORMAL;
+ edit_submode = NULL;
+ showmode();
+ break;
+ }
+
+ return retval;
+}
+
+/// Stop insert completion mode
+static bool ins_compl_stop(const int c, const int prev_mode, bool retval)
+{
+ // Get here when we have finished typing a sequence of ^N and
+ // ^P or other completion characters in CTRL-X mode. Free up
+ // memory that was used, and make sure we can redo the insert.
+ if (compl_curr_match != NULL || compl_leader != NULL || c == Ctrl_E) {
+ // If any of the original typed text has been changed, eg when
+ // ignorecase is set, we must add back-spaces to the redo
+ // buffer. We add as few as necessary to delete just the part
+ // of the original text that has changed.
+ // When using the longest match, edited the match or used
+ // CTRL-E then don't use the current match.
+ char *ptr;
+ if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E) {
+ ptr = compl_curr_match->cp_str;
+ } else {
+ ptr = NULL;
+ }
+ ins_compl_fixRedoBufForLeader(ptr);
+ }
+
+ bool want_cindent = (get_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) {
+ // re-indent the current line
+ if (want_cindent) {
+ do_c_expr_indent();
+ want_cindent = false; // don't do it again
+ }
+ } else {
+ const int prev_col = curwin->w_cursor.col;
+
+ // put the cursor on the last char, for 'tw' formatting
+ if (prev_col > 0) {
+ dec_cursor();
+ }
+
+ // only format when something was inserted
+ if (!arrow_used && !ins_need_undo_get() && c != Ctrl_E) {
+ insertchar(NUL, 0, -1);
+ }
+
+ if (prev_col > 0
+ && get_cursor_line_ptr()[curwin->w_cursor.col] != NUL) {
+ inc_cursor();
+ }
+ }
+
+ // If the popup menu is displayed pressing CTRL-Y means accepting
+ // the selection without inserting anything. When
+ // compl_enter_selects is set the Enter key does the same.
+ if ((c == Ctrl_Y || (compl_enter_selects
+ && (c == CAR || c == K_KENTER || c == NL)))
+ && pum_visible()) {
+ retval = true;
+ }
+
+ // CTRL-E means completion is Ended, go back to the typed text.
+ // but only do this, if the Popup is still visible
+ if (c == Ctrl_E) {
+ ins_compl_delete();
+ char *p = NULL;
+ if (compl_leader != NULL) {
+ p = compl_leader;
+ } else if (compl_first_match != NULL) {
+ p = compl_orig_text;
+ }
+ if (p != NULL) {
+ const int compl_len = get_compl_len();
+ const int len = (int)strlen(p);
+ if (len > compl_len) {
+ ins_bytes_len(p + compl_len, (size_t)(len - compl_len));
+ }
+ }
+ retval = true;
+ }
+
+ auto_format(false, true);
+
+ // Trigger the CompleteDonePre event to give scripts a chance to
+ // act upon the completion before clearing the info, and restore
+ // ctrl_x_mode, so that complete_info() can be used.
+ ctrl_x_mode = prev_mode;
+ ins_apply_autocmds(EVENT_COMPLETEDONEPRE);
+
+ ins_compl_free();
+ compl_started = false;
+ compl_matches = 0;
+ if (!shortmess(SHM_COMPLETIONMENU)) {
+ msg_clr_cmdline(); // necessary for "noshowmode"
+ }
+ ctrl_x_mode = CTRL_X_NORMAL;
+ compl_enter_selects = false;
+ if (edit_submode != NULL) {
+ edit_submode = NULL;
+ showmode();
+ }
+
+ if (c == Ctrl_C && cmdwin_type != 0) {
+ // Avoid the popup menu remains displayed when leaving the
+ // command line window.
+ update_screen();
+ }
+
+ // Indent now if a key was typed that is in 'cinkeys'.
+ if (want_cindent && in_cinkeys(KEY_COMPLETE, ' ', inindent(0))) {
+ do_c_expr_indent();
+ }
+ // Trigger the CompleteDone event to give scripts a chance to act
+ // upon the end of completion.
+ ins_apply_autocmds(EVENT_COMPLETEDONE);
+
+ return retval;
+}
+
/// Prepare for Insert mode completion, or stop it.
/// Called just after typing a character in Insert mode.
///
@@ -1658,7 +2087,6 @@ void ins_compl_addfrommatch(void)
/// @return true when the character is not to be inserted;
bool ins_compl_prep(int c)
{
- char_u *ptr;
bool retval = false;
const int prev_mode = ctrl_x_mode;
@@ -1698,111 +2126,14 @@ bool ins_compl_prep(int c)
// Set "compl_get_longest" when finding the first matches.
if (ctrl_x_mode_not_defined_yet()
|| (ctrl_x_mode_normal() && !compl_started)) {
- compl_get_longest = (strstr((char *)p_cot, "longest") != NULL);
+ compl_get_longest = compl_longest;
compl_used_match = true;
}
if (ctrl_x_mode_not_defined_yet()) {
// We have just typed CTRL-X and aren't quite sure which CTRL-X mode
// it will be yet. Now we decide.
- switch (c) {
- case Ctrl_E:
- case Ctrl_Y:
- ctrl_x_mode = CTRL_X_SCROLL;
- if (!(State & REPLACE_FLAG)) {
- edit_submode = (char_u *)_(" (insert) Scroll (^E/^Y)");
- } else {
- edit_submode = (char_u *)_(" (replace) Scroll (^E/^Y)");
- }
- edit_submode_pre = NULL;
- showmode();
- break;
- case Ctrl_L:
- ctrl_x_mode = CTRL_X_WHOLE_LINE;
- break;
- case Ctrl_F:
- ctrl_x_mode = CTRL_X_FILES;
- break;
- case Ctrl_K:
- ctrl_x_mode = CTRL_X_DICTIONARY;
- break;
- case Ctrl_R:
- // Simply allow ^R to happen without affecting ^X mode
- break;
- case Ctrl_T:
- ctrl_x_mode = CTRL_X_THESAURUS;
- break;
- case Ctrl_U:
- ctrl_x_mode = CTRL_X_FUNCTION;
- break;
- case Ctrl_O:
- ctrl_x_mode = CTRL_X_OMNI;
- break;
- case 's':
- case Ctrl_S:
- ctrl_x_mode = CTRL_X_SPELL;
- emsg_off++; // Avoid getting the E756 error twice.
- spell_back_to_badword();
- emsg_off--;
- break;
- case Ctrl_RSB:
- ctrl_x_mode = CTRL_X_TAGS;
- break;
- case Ctrl_I:
- case K_S_TAB:
- ctrl_x_mode = CTRL_X_PATH_PATTERNS;
- break;
- case Ctrl_D:
- ctrl_x_mode = CTRL_X_PATH_DEFINES;
- break;
- case Ctrl_V:
- case Ctrl_Q:
- ctrl_x_mode = CTRL_X_CMDLINE;
- break;
- case Ctrl_Z:
- ctrl_x_mode = CTRL_X_NORMAL;
- edit_submode = NULL;
- showmode();
- retval = true;
- break;
- case Ctrl_P:
- case Ctrl_N:
- // ^X^P means LOCAL expansion if nothing interrupted (eg we
- // just started ^X mode, or there were enough ^X's to cancel
- // the previous mode, say ^X^F^X^X^P or ^P^X^X^X^P, see below)
- // do normal expansion when interrupting a different mode (say
- // ^X^F^X^P or ^P^X^X^P, see below)
- // nothing changes if interrupting mode 0, (eg, the flag
- // doesn't change when going to ADDING mode -- Acevedo
- if (!(compl_cont_status & CONT_INTRPT)) {
- compl_cont_status |= CONT_LOCAL;
- } else if (compl_cont_mode != 0) {
- compl_cont_status &= ~CONT_LOCAL;
- }
- FALLTHROUGH;
- default:
- // If we have typed at least 2 ^X's... for modes != 0, we set
- // compl_cont_status = 0 (eg, as if we had just started ^X
- // mode).
- // For mode 0, we set "compl_cont_mode" to an impossible
- // value, in both cases ^X^X can be used to restart the same
- // mode (avoiding ADDING mode).
- // Undocumented feature: In a mode != 0 ^X^P and ^X^X^P start
- // 'complete' and local ^P expansions respectively.
- // In mode 0 an extra ^X is needed since ^X^P goes to ADDING
- // mode -- Acevedo
- if (c == Ctrl_X) {
- if (compl_cont_mode != 0) {
- compl_cont_status = 0;
- } else {
- compl_cont_mode = CTRL_X_NOT_DEFINED_YET;
- }
- }
- ctrl_x_mode = CTRL_X_NORMAL;
- edit_submode = NULL;
- showmode();
- break;
- }
+ retval = set_ctrl_x_mode(c);
} else if (ctrl_x_mode_not_default()) {
// We're already in CTRL-X mode, do we stay in it?
if (!vim_is_ctrl_x_key(c)) {
@@ -1827,107 +2158,7 @@ bool ins_compl_prep(int c)
&& c != Ctrl_R
&& !ins_compl_pum_key(c))
|| ctrl_x_mode == CTRL_X_FINISHED) {
- // Get here when we have finished typing a sequence of ^N and
- // ^P or other completion characters in CTRL-X mode. Free up
- // memory that was used, and make sure we can redo the insert.
- if (compl_curr_match != NULL || compl_leader != NULL || c == Ctrl_E) {
- // If any of the original typed text has been changed, eg when
- // ignorecase is set, we must add back-spaces to the redo
- // buffer. We add as few as necessary to delete just the part
- // of the original text that has changed.
- // When using the longest match, edited the match or used
- // CTRL-E then don't use the current match.
- if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E) {
- ptr = compl_curr_match->cp_str;
- } else {
- ptr = NULL;
- }
- ins_compl_fixRedoBufForLeader(ptr);
- }
-
- bool want_cindent = (can_cindent_get() && 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) {
- // re-indent the current line
- if (want_cindent) {
- do_c_expr_indent();
- want_cindent = false; // don't do it again
- }
- } else {
- int prev_col = curwin->w_cursor.col;
-
- // put the cursor on the last char, for 'tw' formatting
- if (prev_col > 0) {
- dec_cursor();
- }
-
- if (!arrow_used && !ins_need_undo_get() && c != Ctrl_E) {
- insertchar(NUL, 0, -1);
- }
-
- if (prev_col > 0
- && get_cursor_line_ptr()[curwin->w_cursor.col] != NUL) {
- inc_cursor();
- }
- }
-
- // If the popup menu is displayed pressing CTRL-Y means accepting
- // the selection without inserting anything. When
- // compl_enter_selects is set the Enter key does the same.
- if ((c == Ctrl_Y || (compl_enter_selects
- && (c == CAR || c == K_KENTER || c == NL)))
- && pum_visible()) {
- retval = true;
- }
-
- // CTRL-E means completion is Ended, go back to the typed text.
- // but only do this, if the Popup is still visible
- if (c == Ctrl_E) {
- ins_compl_delete();
- if (compl_leader != NULL) {
- ins_bytes(compl_leader + get_compl_len());
- } else if (compl_first_match != NULL) {
- ins_bytes(compl_orig_text + get_compl_len());
- }
- retval = true;
- }
-
- auto_format(false, true);
-
- // Trigger the CompleteDonePre event to give scripts a chance to
- // act upon the completion before clearing the info, and restore
- // ctrl_x_mode, so that complete_info() can be used.
- ctrl_x_mode = prev_mode;
- ins_apply_autocmds(EVENT_COMPLETEDONEPRE);
-
- ins_compl_free();
- compl_started = false;
- compl_matches = 0;
- if (!shortmess(SHM_COMPLETIONMENU)) {
- msg_clr_cmdline(); // necessary for "noshowmode"
- }
- ctrl_x_mode = CTRL_X_NORMAL;
- compl_enter_selects = false;
- if (edit_submode != NULL) {
- edit_submode = NULL;
- showmode();
- }
-
- // Avoid the popup menu remains displayed when leaving the
- // command line window.
- if (c == Ctrl_C && cmdwin_type != 0) {
- update_screen(0);
- }
-
- // Indent now if a key was typed that is in 'cinkeys'.
- if (want_cindent && in_cinkeys(KEY_COMPLETE, ' ', inindent(0))) {
- do_c_expr_indent();
- }
- // Trigger the CompleteDone event to give scripts a chance to act
- // upon the end of completion.
- ins_apply_autocmds(EVENT_COMPLETEDONE);
+ retval = ins_compl_stop(c, prev_mode, retval);
}
} else if (ctrl_x_mode == CTRL_X_LOCAL_MSG) {
// Trigger the CompleteDone event to give scripts a chance to act
@@ -1950,11 +2181,11 @@ bool ins_compl_prep(int c)
/// Fix the redo buffer for the completion leader replacing some of the typed
/// text. This inserts backspaces and appends the changed text.
/// "ptr" is the known leader text or NUL.
-static void ins_compl_fixRedoBufForLeader(char_u *ptr_arg)
+static void ins_compl_fixRedoBufForLeader(char *ptr_arg)
{
int len;
- char_u *p;
- char_u *ptr = ptr_arg;
+ char *p;
+ char *ptr = ptr_arg;
if (ptr == NULL) {
if (compl_leader != NULL) {
@@ -1975,7 +2206,7 @@ static void ins_compl_fixRedoBufForLeader(char_u *ptr_arg)
} else {
len = 0;
}
- AppendToRedobuffLit((char *)ptr + len, -1);
+ AppendToRedobuffLit(ptr + len, -1);
}
/// Loops through the list of windows, loaded-buffers or non-loaded-buffers
@@ -2010,8 +2241,94 @@ static buf_T *ins_compl_next_buf(buf_T *buf, int flag)
return buf;
}
-/// Get the user-defined completion function name for completion 'type'
-static char_u *get_complete_funcname(int type)
+static Callback cfu_cb; ///< 'completefunc' callback function
+static Callback ofu_cb; ///< 'omnifunc' callback function
+static Callback tsrfu_cb; ///< 'thesaurusfunc' callback function
+
+/// Copy a global callback function to a buffer local callback.
+static void copy_global_to_buflocal_cb(Callback *globcb, Callback *bufcb)
+{
+ callback_free(bufcb);
+ if (globcb->type != kCallbackNone) {
+ callback_copy(bufcb, globcb);
+ }
+}
+
+/// Parse the 'completefunc' option value and set the callback function.
+/// Invoked when the 'completefunc' option is set. The option value can be a
+/// name of a function (string), or function(<name>) or funcref(<name>) or a
+/// lambda expression.
+void set_completefunc_option(char **errmsg)
+{
+ if (option_set_callback_func(curbuf->b_p_cfu, &cfu_cb) == FAIL) {
+ *errmsg = e_invarg;
+ return;
+ }
+
+ set_buflocal_cfu_callback(curbuf);
+}
+
+/// Copy the global 'completefunc' callback function to the buffer-local
+/// 'completefunc' callback for "buf".
+void set_buflocal_cfu_callback(buf_T *buf)
+{
+ copy_global_to_buflocal_cb(&cfu_cb, &buf->b_cfu_cb);
+}
+
+/// Parse the 'omnifunc' option value and set the callback function.
+/// Invoked when the 'omnifunc' option is set. The option value can be a
+/// name of a function (string), or function(<name>) or funcref(<name>) or a
+/// lambda expression.
+void set_omnifunc_option(buf_T *buf, char **errmsg)
+{
+ if (option_set_callback_func(buf->b_p_ofu, &ofu_cb) == FAIL) {
+ *errmsg = e_invarg;
+ return;
+ }
+ set_buflocal_ofu_callback(buf);
+}
+
+/// Copy the global 'omnifunc' callback function to the buffer-local 'omnifunc'
+/// callback for "buf".
+void set_buflocal_ofu_callback(buf_T *buf)
+{
+ copy_global_to_buflocal_cb(&ofu_cb, &buf->b_ofu_cb);
+}
+
+/// Parse the 'thesaurusfunc' option value and set the callback function.
+/// Invoked when the 'thesaurusfunc' option is set. The option value can be a
+/// name of a function (string), or function(<name>) or funcref(<name>) or a
+/// lambda expression.
+void set_thesaurusfunc_option(char **errmsg)
+{
+ int retval;
+
+ if (*curbuf->b_p_tsrfu != NUL) {
+ // buffer-local option set
+ retval = option_set_callback_func(curbuf->b_p_tsrfu, &curbuf->b_tsrfu_cb);
+ } else {
+ // global option set
+ retval = option_set_callback_func(p_tsrfu, &tsrfu_cb);
+ }
+
+ if (retval == FAIL) {
+ *errmsg = e_invarg;
+ }
+}
+
+/// Mark the global 'completefunc' 'omnifunc' and 'thesaurusfunc' callbacks with
+/// "copyID" so that they are not garbage collected.
+bool set_ref_in_insexpand_funcs(int copyID)
+{
+ bool abort = set_ref_in_callback(&cfu_cb, copyID, NULL, NULL);
+ abort = abort || set_ref_in_callback(&ofu_cb, copyID, NULL, NULL);
+ abort = abort || set_ref_in_callback(&tsrfu_cb, copyID, NULL, NULL);
+
+ return abort;
+}
+
+/// Get the user-defined completion function name for completion "type"
+static char *get_complete_funcname(int type)
{
switch (type) {
case CTRL_X_FUNCTION:
@@ -2021,19 +2338,32 @@ static char_u *get_complete_funcname(int type)
case CTRL_X_THESAURUS:
return *curbuf->b_p_tsrfu == NUL ? p_tsrfu : curbuf->b_p_tsrfu;
default:
- return (char_u *)"";
+ return "";
+ }
+}
+
+/// Get the callback to use for insert mode completion.
+static Callback *get_insert_callback(int type)
+{
+ if (type == CTRL_X_FUNCTION) {
+ return &curbuf->b_cfu_cb;
+ }
+ if (type == CTRL_X_OMNI) {
+ return &curbuf->b_ofu_cb;
}
+ // CTRL_X_THESAURUS
+ return (*curbuf->b_p_tsrfu != NUL) ? &curbuf->b_tsrfu_cb : &tsrfu_cb;
}
-/// Execute user defined complete function 'completefunc' or 'omnifunc', and
-/// get matches in "matches".
+/// Execute user defined complete function 'completefunc', 'omnifunc' or
+/// 'thesaurusfunc', and get matches in "matches".
///
-/// @param type CTRL_X_OMNI or CTRL_X_FUNCTION
-static void expand_by_function(int type, char_u *base)
+/// @param type either CTRL_X_OMNI or CTRL_X_FUNCTION or CTRL_X_THESAURUS
+static void expand_by_function(int type, char *base)
{
list_T *matchlist = NULL;
dict_T *matchdict = NULL;
- char_u *funcname;
+ char *funcname;
pos_T pos;
typval_T rettv;
const int save_State = State;
@@ -2050,7 +2380,7 @@ 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 ? (char *)base : "";
+ args[1].vval.v_string = base != NULL ? base : "";
pos = curwin->w_cursor;
// Lock the text to avoid weird things from happening. Also disallow
@@ -2058,8 +2388,10 @@ static void expand_by_function(int type, char_u *base)
// Insert mode in another buffer.
textlock++;
+ Callback *cb = get_insert_callback(type);
+
// Call a function, which returns a list or dict.
- if (call_vim_function((char *)funcname, 2, args, &rettv) == OK) {
+ if (callback_call(cb, 2, args, &rettv)) {
switch (rettv.v_type) {
case VAR_LIST:
matchlist = rettv.vval.v_list;
@@ -2147,10 +2479,15 @@ static int ins_compl_add_tv(typval_T *const tv, const Direction dir, bool fast)
for (size_t i = 0; i < CPT_COUNT; i++) {
xfree(cptext[i]);
}
+ tv_clear(&user_data);
return FAIL;
}
- return ins_compl_add((char_u *)word, -1, NULL,
- (char_u **)cptext, true, &user_data, dir, flags, dup);
+ int status = ins_compl_add((char *)word, -1, NULL, cptext, true,
+ &user_data, dir, flags, dup);
+ if (status != OK) {
+ tv_clear(&user_data);
+ }
+ return status;
}
/// Add completions from a list.
@@ -2207,6 +2544,7 @@ static void set_completion(colnr_T startcol, list_T *list)
}
ins_compl_clear();
ins_compl_free();
+ compl_get_longest = compl_longest;
compl_direction = FORWARD;
if (startcol > curwin->w_cursor.col) {
@@ -2215,8 +2553,8 @@ static void set_completion(colnr_T startcol, list_T *list)
compl_col = startcol;
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,
- (size_t)compl_length);
+ compl_orig_text = xstrnsave(get_cursor_line_ptr() + compl_col,
+ (size_t)compl_length);
if (p_ic) {
flags |= CP_ICASE;
}
@@ -2236,9 +2574,10 @@ static void set_completion(colnr_T startcol, list_T *list)
int save_w_leftcol = curwin->w_leftcol;
compl_curr_match = compl_first_match;
- if (compl_no_insert || compl_no_select) {
+ bool no_select = compl_no_select || compl_longest;
+ if (compl_no_insert || no_select) {
ins_complete(K_DOWN, false);
- if (compl_no_select) {
+ if (no_select) {
ins_complete(K_UP, false);
}
} else {
@@ -2256,7 +2595,7 @@ static void set_completion(colnr_T startcol, list_T *list)
}
/// "complete()" function
-void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_complete(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if ((State & MODE_INSERT) == 0) {
emsg(_("E785: complete() can only be used in Insert mode"));
@@ -2280,13 +2619,13 @@ void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "complete_add()" function
-void f_complete_add(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_complete_add(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = ins_compl_add_tv(&argvars[0], 0, false);
}
/// "complete_check()" function
-void f_complete_check(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_complete_check(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int saved = RedrawingDisabled;
@@ -2297,26 +2636,27 @@ void f_complete_check(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// Return Insert completion mode name string
-static char_u *ins_compl_mode(void)
+static char *ins_compl_mode(void)
{
if (ctrl_x_mode_not_defined_yet() || ctrl_x_mode_scroll() || compl_started) {
- return (char_u *)ctrl_x_mode_names[ctrl_x_mode & ~CTRL_X_WANT_IDENT];
+ return ctrl_x_mode_names[ctrl_x_mode & ~CTRL_X_WANT_IDENT];
}
- return (char_u *)"";
+ return "";
}
+/// Assign the sequence number to all the completion matches which don't have
+/// one assigned yet.
static void ins_compl_update_sequence_numbers(void)
{
int number = 0;
compl_T *match;
- if (compl_direction == FORWARD) {
+ if (compl_dir_forward()) {
// search backwards for the first valid (!= -1) number.
// This should normally succeed already at the first loop
// cycle, so it's fast!
for (match = compl_curr_match->cp_prev;
- match != NULL && match != compl_first_match;
- match = match->cp_prev) {
+ match != NULL && !is_first_match(match); match = match->cp_prev) {
if (match->cp_number != -1) {
number = match->cp_number;
break;
@@ -2336,8 +2676,7 @@ static void ins_compl_update_sequence_numbers(void)
// number. This should normally succeed already at the
// first loop cycle, so it's fast!
for (match = compl_curr_match->cp_next;
- match != NULL && match != compl_first_match;
- match = match->cp_next) {
+ match != NULL && !is_first_match(match); match = match->cp_next) {
if (match->cp_number != -1) {
number = match->cp_number;
break;
@@ -2375,15 +2714,15 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
; item = TV_LIST_ITEM_NEXT(what_list, item)) {
const char *what = tv_get_string(TV_LIST_ITEM_TV(item));
- if (STRCMP(what, "mode") == 0) {
+ if (strcmp(what, "mode") == 0) {
what_flag |= CI_WHAT_MODE;
- } else if (STRCMP(what, "pum_visible") == 0) {
+ } else if (strcmp(what, "pum_visible") == 0) {
what_flag |= CI_WHAT_PUM_VISIBLE;
- } else if (STRCMP(what, "items") == 0) {
+ } else if (strcmp(what, "items") == 0) {
what_flag |= CI_WHAT_ITEMS;
- } else if (STRCMP(what, "selected") == 0) {
+ } else if (strcmp(what, "selected") == 0) {
what_flag |= CI_WHAT_SELECTED;
- } else if (STRCMP(what, "inserted") == 0) {
+ } else if (strcmp(what, "inserted") == 0) {
what_flag |= CI_WHAT_INSERTED;
}
}
@@ -2391,8 +2730,7 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
int ret = OK;
if (what_flag & CI_WHAT_MODE) {
- ret = tv_dict_add_str(retdict, S_LEN("mode"),
- (char *)ins_compl_mode());
+ ret = tv_dict_add_str(retdict, S_LEN("mode"), ins_compl_mode());
}
if (ret == OK && (what_flag & CI_WHAT_PUM_VISIBLE)) {
@@ -2406,7 +2744,7 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
if (ret == OK && compl_first_match != NULL) {
compl_T *match = compl_first_match;
do {
- if (!(match->cp_flags & CP_ORIGINAL_TEXT)) {
+ if (!match_at_original_text(match)) {
dict_T *di = tv_dict_alloc();
tv_list_append_dict(li, di);
@@ -2416,13 +2754,14 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
tv_dict_add_str(di, S_LEN("kind"), EMPTY_IF_NULL(match->cp_text[CPT_KIND]));
tv_dict_add_str(di, S_LEN("info"), EMPTY_IF_NULL(match->cp_text[CPT_INFO]));
if (match->cp_user_data.v_type == VAR_UNKNOWN) {
+ // Add an empty string for backwards compatibility
tv_dict_add_str(di, S_LEN("user_data"), "");
} else {
tv_dict_add_tv(di, S_LEN("user_data"), &match->cp_user_data);
}
}
match = match->cp_next;
- } while (match != NULL && match != compl_first_match);
+ } while (match != NULL && !is_first_match(match));
}
}
@@ -2441,7 +2780,7 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
}
/// "complete_info()" function
-void f_complete_info(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_complete_info(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_dict_alloc_ret(rettv);
@@ -2464,6 +2803,485 @@ static bool thesaurus_func_complete(int type)
&& (*curbuf->b_p_tsrfu != NUL || *p_tsrfu != NUL);
}
+/// Return value of process_next_cpt_value()
+enum {
+ INS_COMPL_CPT_OK = 1,
+ INS_COMPL_CPT_CONT,
+ INS_COMPL_CPT_END,
+};
+
+/// Process the next 'complete' option value in st->e_cpt.
+///
+/// If successful, the arguments are set as below:
+/// st->cpt - pointer to the next option value in "st->cpt"
+/// compl_type_arg - type of insert mode completion to use
+/// st->found_all - all matches of this type are found
+/// st->ins_buf - search for completions in this buffer
+/// st->first_match_pos - position of the first completion match
+/// st->last_match_pos - position of the last completion match
+/// st->set_match_pos - true if the first match position should be saved to
+/// avoid loops after the search wraps around.
+/// st->dict - name of the dictionary or thesaurus file to search
+/// st->dict_f - flag specifying whether "dict" is an exact file name or not
+///
+/// @return INS_COMPL_CPT_OK if the next value is processed successfully.
+/// INS_COMPL_CPT_CONT to skip the current completion source matching
+/// the "st->e_cpt" option value and process the next matching source.
+/// INS_COMPL_CPT_END if all the values in "st->e_cpt" are processed.
+static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_arg,
+ pos_T *start_match_pos)
+{
+ int compl_type = -1;
+ int status = INS_COMPL_CPT_OK;
+
+ st->found_all = false;
+
+ while (*st->e_cpt == ',' || *st->e_cpt == ' ') {
+ st->e_cpt++;
+ }
+
+ if (*st->e_cpt == '.' && !curbuf->b_scanned) {
+ st->ins_buf = curbuf;
+ st->first_match_pos = *start_match_pos;
+ // Move the cursor back one character so that ^N can match the
+ // word immediately after the cursor.
+ if (ctrl_x_mode_normal() && dec(&st->first_match_pos) < 0) {
+ // Move the cursor to after the last character in the
+ // buffer, so that word at start of buffer is found
+ // correctly.
+ st->first_match_pos.lnum = st->ins_buf->b_ml.ml_line_count;
+ st->first_match_pos.col = (colnr_T)strlen(ml_get(st->first_match_pos.lnum));
+ }
+ st->last_match_pos = st->first_match_pos;
+ compl_type = 0;
+
+ // Remember the first match so that the loop stops when we
+ // wrap and come back there a second time.
+ st->set_match_pos = true;
+ } else if (vim_strchr("buwU", (uint8_t)(*st->e_cpt)) != NULL
+ && (st->ins_buf = ins_compl_next_buf(st->ins_buf, *st->e_cpt)) != curbuf) {
+ // Scan a buffer, but not the current one.
+ if (st->ins_buf->b_ml.ml_mfp != NULL) { // loaded buffer
+ compl_started = true;
+ st->first_match_pos.col = st->last_match_pos.col = 0;
+ st->first_match_pos.lnum = st->ins_buf->b_ml.ml_line_count + 1;
+ st->last_match_pos.lnum = 0;
+ compl_type = 0;
+ } else { // unloaded buffer, scan like dictionary
+ st->found_all = true;
+ if (st->ins_buf->b_fname == NULL) {
+ status = INS_COMPL_CPT_CONT;
+ goto done;
+ }
+ compl_type = CTRL_X_DICTIONARY;
+ st->dict = st->ins_buf->b_fname;
+ st->dict_f = DICT_EXACT;
+ }
+ if (!shortmess(SHM_COMPLETIONSCAN)) {
+ msg_hist_off = true; // reset in msg_trunc_attr()
+ vim_snprintf(IObuff, IOSIZE, _("Scanning: %s"),
+ st->ins_buf->b_fname == NULL
+ ? buf_spname(st->ins_buf)
+ : st->ins_buf->b_sfname == NULL
+ ? st->ins_buf->b_fname
+ : st->ins_buf->b_sfname);
+ (void)msg_trunc_attr(IObuff, true, HL_ATTR(HLF_R));
+ }
+ } else if (*st->e_cpt == NUL) {
+ status = INS_COMPL_CPT_END;
+ } else {
+ if (ctrl_x_mode_line_or_eval()) {
+ compl_type = -1;
+ } else if (*st->e_cpt == 'k' || *st->e_cpt == 's') {
+ if (*st->e_cpt == 'k') {
+ compl_type = CTRL_X_DICTIONARY;
+ } else {
+ compl_type = CTRL_X_THESAURUS;
+ }
+ if (*++st->e_cpt != ',' && *st->e_cpt != NUL) {
+ st->dict = st->e_cpt;
+ st->dict_f = DICT_FIRST;
+ }
+ } else if (*st->e_cpt == 'i') {
+ compl_type = CTRL_X_PATH_PATTERNS;
+ } else if (*st->e_cpt == 'd') {
+ compl_type = CTRL_X_PATH_DEFINES;
+ } else if (*st->e_cpt == ']' || *st->e_cpt == 't') {
+ compl_type = CTRL_X_TAGS;
+ if (!shortmess(SHM_COMPLETIONSCAN)) {
+ msg_hist_off = true; // reset in msg_trunc_attr()
+ vim_snprintf(IObuff, IOSIZE, "%s", _("Scanning tags."));
+ (void)msg_trunc_attr(IObuff, true, HL_ATTR(HLF_R));
+ }
+ } else {
+ compl_type = -1;
+ }
+
+ // in any case e_cpt is advanced to the next entry
+ (void)copy_option_part(&st->e_cpt, IObuff, IOSIZE, ",");
+
+ st->found_all = true;
+ if (compl_type == -1) {
+ status = INS_COMPL_CPT_CONT;
+ }
+ }
+
+done:
+ *compl_type_arg = compl_type;
+ return status;
+}
+
+/// Get the next set of identifiers or defines matching "compl_pattern" in
+/// included files.
+static void get_next_include_file_completion(int compl_type)
+{
+ find_pattern_in_path(compl_pattern, compl_direction,
+ strlen(compl_pattern), false, false,
+ ((compl_type == CTRL_X_PATH_DEFINES
+ && !(compl_cont_status & CONT_SOL))
+ ? FIND_DEFINE : FIND_ANY),
+ 1L, ACTION_EXPAND, 1, MAXLNUM);
+}
+
+/// Get the next set of words matching "compl_pattern" in dictionary or
+/// thesaurus files.
+static void get_next_dict_tsr_completion(int compl_type, char *dict, int dict_f)
+{
+ if (thesaurus_func_complete(compl_type)) {
+ expand_by_function(compl_type, compl_pattern);
+ } else {
+ ins_compl_dictionaries(dict != NULL ? dict
+ : (compl_type == CTRL_X_THESAURUS
+ ? (*curbuf->b_p_tsr == NUL ? p_tsr : curbuf->b_p_tsr)
+ : (*curbuf->b_p_dict ==
+ NUL ? p_dict : curbuf->b_p_dict)),
+ compl_pattern,
+ dict != NULL ? dict_f : 0,
+ compl_type == CTRL_X_THESAURUS);
+ }
+}
+
+/// Get the next set of tag names matching "compl_pattern".
+static void get_next_tag_completion(void)
+{
+ // set p_ic according to p_ic, p_scs and pat for find_tags().
+ const int save_p_ic = p_ic;
+ p_ic = ignorecase(compl_pattern);
+
+ // Find up to TAG_MANY matches. Avoids that an enormous number
+ // of matches is found when compl_pattern is empty
+ g_tag_at_cursor = true;
+ char **matches;
+ int num_matches;
+ if (find_tags(compl_pattern, &num_matches, &matches,
+ TAG_REGEXP | TAG_NAMES | TAG_NOIC | TAG_INS_COMP
+ | (ctrl_x_mode_not_default() ? TAG_VERBOSE : 0),
+ TAG_MANY, curbuf->b_ffname) == OK && num_matches > 0) {
+ ins_compl_add_matches(num_matches, matches, p_ic);
+ }
+ g_tag_at_cursor = false;
+ p_ic = save_p_ic;
+}
+
+/// Get the next set of filename matching "compl_pattern".
+static void get_next_filename_completion(void)
+{
+ char **matches;
+ int num_matches;
+ if (expand_wildcards(1, &compl_pattern, &num_matches, &matches,
+ EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) != OK) {
+ return;
+ }
+
+ // May change home directory back to "~".
+ tilde_replace(compl_pattern, num_matches, matches);
+#ifdef BACKSLASH_IN_FILENAME
+ if (curbuf->b_p_csl[0] != NUL) {
+ for (int i = 0; i < num_matches; i++) {
+ char *ptr = matches[i];
+ while (*ptr != NUL) {
+ if (curbuf->b_p_csl[0] == 's' && *ptr == '\\') {
+ *ptr = '/';
+ } else if (curbuf->b_p_csl[0] == 'b' && *ptr == '/') {
+ *ptr = '\\';
+ }
+ ptr += utfc_ptr2len(ptr);
+ }
+ }
+ }
+#endif
+ ins_compl_add_matches(num_matches, matches, p_fic || p_wic);
+}
+
+/// Get the next set of command-line completions matching "compl_pattern".
+static void get_next_cmdline_completion(void)
+{
+ char **matches;
+ int num_matches;
+ if (expand_cmdline(&compl_xp, compl_pattern,
+ (int)strlen(compl_pattern),
+ &num_matches, &matches) == EXPAND_OK) {
+ ins_compl_add_matches(num_matches, matches, false);
+ }
+}
+
+/// Get the next set of spell suggestions matching "compl_pattern".
+static void get_next_spell_completion(linenr_T lnum)
+{
+ char **matches;
+ int num_matches = expand_spelling(lnum, compl_pattern, &matches);
+ if (num_matches > 0) {
+ ins_compl_add_matches(num_matches, matches, p_ic);
+ } else {
+ xfree(matches);
+ }
+}
+
+/// Return the next word or line from buffer "ins_buf" at position
+/// "cur_match_pos" for completion. The length of the match is set in "len".
+/// @param ins_buf buffer being scanned
+/// @param cur_match_pos current match position
+/// @param match_len
+/// @param cont_s_ipos next ^X<> will set initial_pos
+static char *ins_comp_get_next_word_or_line(buf_T *ins_buf, pos_T *cur_match_pos, int *match_len,
+ bool *cont_s_ipos)
+{
+ *match_len = 0;
+ char *ptr = ml_get_buf(ins_buf, cur_match_pos->lnum, false) + cur_match_pos->col;
+ int len;
+ if (ctrl_x_mode_line_or_eval()) {
+ if (compl_status_adding()) {
+ if (cur_match_pos->lnum >= ins_buf->b_ml.ml_line_count) {
+ return NULL;
+ }
+ ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, false);
+ if (!p_paste) {
+ ptr = skipwhite(ptr);
+ }
+ }
+ len = (int)strlen(ptr);
+ } else {
+ char *tmp_ptr = ptr;
+
+ if (compl_status_adding() && compl_length <= (int)strlen(tmp_ptr)) {
+ tmp_ptr += compl_length;
+ // Skip if already inside a word.
+ if (vim_iswordp(tmp_ptr)) {
+ return NULL;
+ }
+ // Find start of next word.
+ tmp_ptr = find_word_start(tmp_ptr);
+ }
+ // Find end of this word.
+ tmp_ptr = find_word_end(tmp_ptr);
+ len = (int)(tmp_ptr - ptr);
+
+ if (compl_status_adding() && len == compl_length) {
+ if (cur_match_pos->lnum < ins_buf->b_ml.ml_line_count) {
+ // Try next line, if any. the new word will be "join" as if the
+ // normal command "J" was used. IOSIZE is always greater than
+ // compl_length, so the next strncpy always works -- Acevedo
+ strncpy(IObuff, ptr, (size_t)len); // NOLINT(runtime/printf)
+ ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, false);
+ tmp_ptr = ptr = skipwhite(ptr);
+ // Find start of next word.
+ tmp_ptr = find_word_start(tmp_ptr);
+ // Find end of next word.
+ tmp_ptr = find_word_end(tmp_ptr);
+ if (tmp_ptr > ptr) {
+ if (*ptr != ')' && IObuff[len - 1] != TAB) {
+ if (IObuff[len - 1] != ' ') {
+ IObuff[len++] = ' ';
+ }
+ // IObuf =~ "\k.* ", thus len >= 2
+ if (p_js
+ && (IObuff[len - 2] == '.'
+ || IObuff[len - 2] == '?'
+ || IObuff[len - 2] == '!')) {
+ IObuff[len++] = ' ';
+ }
+ }
+ // copy as much as possible of the new word
+ if (tmp_ptr - ptr >= IOSIZE - len) {
+ tmp_ptr = ptr + IOSIZE - len - 1;
+ }
+ xstrlcpy(IObuff + len, ptr, (size_t)(IOSIZE - len));
+ len += (int)(tmp_ptr - ptr);
+ *cont_s_ipos = true;
+ }
+ IObuff[len] = NUL;
+ ptr = IObuff;
+ }
+ if (len == compl_length) {
+ return NULL;
+ }
+ }
+ }
+
+ *match_len = len;
+ return ptr;
+}
+
+/// Get the next set of words matching "compl_pattern" for default completion(s)
+/// (normal ^P/^N and ^X^L).
+/// Search for "compl_pattern" in the buffer "st->ins_buf" starting from the
+/// position "st->start_pos" in the "compl_direction" direction. If
+/// "st->set_match_pos" is true, then set the "st->first_match_pos" and
+/// "st->last_match_pos".
+///
+/// @return OK if a new next match is found, otherwise FAIL.
+static int get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_pos)
+{
+ // If 'infercase' is set, don't use 'smartcase' here
+ const int save_p_scs = p_scs;
+ assert(st->ins_buf);
+ if (st->ins_buf->b_p_inf) {
+ p_scs = false;
+ }
+
+ // Buffers other than curbuf are scanned from the beginning or the
+ // end but never from the middle, thus setting nowrapscan in this
+ // buffers is a good idea, on the other hand, we always set
+ // wrapscan for curbuf to avoid missing matches -- Acevedo,Webb
+ const int save_p_ws = p_ws;
+ if (st->ins_buf != curbuf) {
+ p_ws = false;
+ } else if (*st->e_cpt == '.') {
+ p_ws = true;
+ }
+ bool looped_around = false;
+ int found_new_match = FAIL;
+ for (;;) {
+ bool cont_s_ipos = false;
+
+ msg_silent++; // Don't want messages for wrapscan.
+ // ctrl_x_mode_line_or_eval() || word-wise search that
+ // has added a word that was at the beginning of the line.
+ if (ctrl_x_mode_line_or_eval() || (compl_cont_status & CONT_SOL)) {
+ found_new_match = search_for_exact_line(st->ins_buf, st->cur_match_pos,
+ compl_direction, compl_pattern);
+ } else {
+ found_new_match = searchit(NULL, st->ins_buf, st->cur_match_pos,
+ NULL, compl_direction, compl_pattern, 1L,
+ SEARCH_KEEP + SEARCH_NFMSG, RE_LAST, NULL);
+ }
+ msg_silent--;
+ if (!compl_started || st->set_match_pos) {
+ // set "compl_started" even on fail
+ compl_started = true;
+ st->first_match_pos = *st->cur_match_pos;
+ st->last_match_pos = *st->cur_match_pos;
+ st->set_match_pos = false;
+ } else if (st->first_match_pos.lnum == st->last_match_pos.lnum
+ && st->first_match_pos.col == st->last_match_pos.col) {
+ found_new_match = FAIL;
+ } else if (compl_dir_forward()
+ && (st->prev_match_pos.lnum > st->cur_match_pos->lnum
+ || (st->prev_match_pos.lnum == st->cur_match_pos->lnum
+ && st->prev_match_pos.col >= st->cur_match_pos->col))) {
+ if (looped_around) {
+ found_new_match = FAIL;
+ } else {
+ looped_around = true;
+ }
+ } else if (!compl_dir_forward()
+ && (st->prev_match_pos.lnum < st->cur_match_pos->lnum
+ || (st->prev_match_pos.lnum == st->cur_match_pos->lnum
+ && st->prev_match_pos.col <= st->cur_match_pos->col))) {
+ if (looped_around) {
+ found_new_match = FAIL;
+ } else {
+ looped_around = true;
+ }
+ }
+ st->prev_match_pos = *st->cur_match_pos;
+ if (found_new_match == FAIL) {
+ break;
+ }
+
+ // when ADDING, the text before the cursor matches, skip it
+ if (compl_status_adding() && st->ins_buf == curbuf
+ && start_pos->lnum == st->cur_match_pos->lnum
+ && start_pos->col == st->cur_match_pos->col) {
+ continue;
+ }
+ int len;
+ char *ptr = ins_comp_get_next_word_or_line(st->ins_buf, st->cur_match_pos,
+ &len, &cont_s_ipos);
+ if (ptr == NULL) {
+ continue;
+ }
+ if (ins_compl_add_infercase(ptr, len, p_ic,
+ st->ins_buf == curbuf ? NULL : st->ins_buf->b_sfname,
+ 0, cont_s_ipos) != NOTDONE) {
+ found_new_match = OK;
+ break;
+ }
+ }
+ p_scs = save_p_scs;
+ p_ws = save_p_ws;
+
+ return found_new_match;
+}
+
+/// get the next set of completion matches for "type".
+/// @return true if a new match is found, otherwise false.
+static bool get_next_completion_match(int type, ins_compl_next_state_T *st, pos_T *ini)
+{
+ int found_new_match = FAIL;
+
+ switch (type) {
+ case -1:
+ break;
+ case CTRL_X_PATH_PATTERNS:
+ case CTRL_X_PATH_DEFINES:
+ get_next_include_file_completion(type);
+ break;
+
+ case CTRL_X_DICTIONARY:
+ case CTRL_X_THESAURUS:
+ get_next_dict_tsr_completion(type, st->dict, st->dict_f);
+ st->dict = NULL;
+ break;
+
+ case CTRL_X_TAGS:
+ get_next_tag_completion();
+ break;
+
+ case CTRL_X_FILES:
+ get_next_filename_completion();
+ break;
+
+ case CTRL_X_CMDLINE:
+ case CTRL_X_CMDLINE_CTRL_X:
+ get_next_cmdline_completion();
+ break;
+
+ case CTRL_X_FUNCTION:
+ case CTRL_X_OMNI:
+ expand_by_function(type, compl_pattern);
+ break;
+
+ case CTRL_X_SPELL:
+ get_next_spell_completion(st->first_match_pos.lnum);
+ break;
+
+ default: // normal ^P/^N and ^X^L
+ found_new_match = get_next_default_completion(st, ini);
+ if (found_new_match == FAIL && st->ins_buf == curbuf) {
+ st->found_all = true;
+ }
+ }
+
+ // check if compl_curr_match has changed, (e.g. other type of
+ // expansion added something)
+ if (type != 0 && compl_curr_match != compl_old_match) {
+ found_new_match = OK;
+ }
+
+ return found_new_match;
+}
+
/// Get the next expansion(s), using "compl_pattern".
/// The search starts at position "ini" in curbuf and in the direction
/// compl_direction.
@@ -2473,28 +3291,10 @@ static bool thesaurus_func_complete(int type)
/// Return the total number of matches or -1 if still unknown -- Acevedo
static int ins_compl_get_exp(pos_T *ini)
{
- static pos_T first_match_pos;
- static pos_T last_match_pos;
- static char *e_cpt = ""; // curr. entry in 'complete'
- static bool found_all = false; // Found all matches of a
- // certain type.
- static buf_T *ins_buf = NULL; // buffer being scanned
-
- pos_T *pos;
- char **matches;
- int save_p_scs;
- bool save_p_ws;
- int save_p_ic;
+ static ins_compl_next_state_T st;
int i;
- int num_matches;
- int len;
int found_new_match;
int type = ctrl_x_mode;
- char_u *ptr;
- char_u *dict = NULL;
- int dict_f = 0;
- bool set_match_pos;
- pos_T prev_pos = { 0, 0, 0 };
assert(curbuf != NULL);
@@ -2502,111 +3302,34 @@ static int ins_compl_get_exp(pos_T *ini)
FOR_ALL_BUFFERS(buf) {
buf->b_scanned = false;
}
- found_all = false;
- ins_buf = curbuf;
- e_cpt = (compl_cont_status & CONT_LOCAL) ? "." : (char *)curbuf->b_p_cpt;
- last_match_pos = first_match_pos = *ini;
- } else if (ins_buf != curbuf && !buf_valid(ins_buf)) {
- ins_buf = curbuf; // In case the buffer was wiped out.
+ st.found_all = false;
+ st.ins_buf = curbuf;
+ st.e_cpt = (compl_cont_status & CONT_LOCAL) ? "." : curbuf->b_p_cpt;
+ st.last_match_pos = st.first_match_pos = *ini;
+ } else if (st.ins_buf != curbuf && !buf_valid(st.ins_buf)) {
+ st.ins_buf = curbuf; // In case the buffer was wiped out.
}
- assert(ins_buf != NULL);
+ assert(st.ins_buf != NULL);
compl_old_match = compl_curr_match; // remember the last current match
- pos = (compl_direction == FORWARD) ? &last_match_pos : &first_match_pos;
+ st.cur_match_pos = compl_dir_forward() ? &st.last_match_pos : &st.first_match_pos;
// For ^N/^P loop over all the flags/windows/buffers in 'complete'
for (;;) {
found_new_match = FAIL;
- set_match_pos = false;
+ st.set_match_pos = false;
// For ^N/^P pick a new entry from e_cpt if compl_started is off,
// or if found_all says this entry is done. For ^X^L only use the
// entries from 'complete' that look in loaded buffers.
if ((ctrl_x_mode_normal() || ctrl_x_mode_line_or_eval())
- && (!compl_started || found_all)) {
- found_all = false;
- while (*e_cpt == ',' || *e_cpt == ' ') {
- e_cpt++;
- }
- if (*e_cpt == '.' && !curbuf->b_scanned) {
- ins_buf = curbuf;
- first_match_pos = *ini;
- // Move the cursor back one character so that ^N can match the
- // word immediately after the cursor.
- if (ctrl_x_mode_normal() && dec(&first_match_pos) < 0) {
- // Move the cursor to after the last character in the
- // buffer, so that word at start of buffer is found
- // correctly.
- first_match_pos.lnum = ins_buf->b_ml.ml_line_count;
- first_match_pos.col = (colnr_T)STRLEN(ml_get(first_match_pos.lnum));
- }
- last_match_pos = first_match_pos;
- type = 0;
-
- // 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("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;
- first_match_pos.col = last_match_pos.col = 0;
- first_match_pos.lnum = ins_buf->b_ml.ml_line_count + 1;
- last_match_pos.lnum = 0;
- type = 0;
- } else { // unloaded buffer, scan like dictionary
- found_all = true;
- if (ins_buf->b_fname == NULL) {
- continue;
- }
- type = CTRL_X_DICTIONARY;
- dict = (char_u *)ins_buf->b_fname;
- dict_f = DICT_EXACT;
- }
- msg_hist_off = true; // reset in msg_trunc_attr()
- vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"),
- ins_buf->b_fname == NULL
- ? buf_spname(ins_buf)
- : ins_buf->b_sfname == NULL
- ? ins_buf->b_fname
- : ins_buf->b_sfname);
- (void)msg_trunc_attr((char *)IObuff, true, HL_ATTR(HLF_R));
- } else if (*e_cpt == NUL) {
+ && (!compl_started || st.found_all)) {
+ int status = process_next_cpt_value(&st, &type, ini);
+ if (status == INS_COMPL_CPT_END) {
break;
- } else {
- if (ctrl_x_mode_line_or_eval()) {
- type = -1;
- } else if (*e_cpt == 'k' || *e_cpt == 's') {
- if (*e_cpt == 'k') {
- type = CTRL_X_DICTIONARY;
- } else {
- type = CTRL_X_THESAURUS;
- }
- if (*++e_cpt != ',' && *e_cpt != NUL) {
- dict = (char_u *)e_cpt;
- dict_f = DICT_FIRST;
- }
- } else if (*e_cpt == 'i') {
- type = CTRL_X_PATH_PATTERNS;
- } else if (*e_cpt == 'd') {
- type = CTRL_X_PATH_DEFINES;
- } else if (*e_cpt == ']' || *e_cpt == 't') {
- msg_hist_off = true; // reset in msg_trunc_attr()
- type = CTRL_X_TAGS;
- vim_snprintf((char *)IObuff, IOSIZE, "%s", _("Scanning tags."));
- (void)msg_trunc_attr((char *)IObuff, true, HL_ATTR(HLF_R));
- } else {
- type = -1;
- }
-
- // in any case e_cpt is advanced to the next entry
- (void)copy_option_part(&e_cpt, (char *)IObuff, IOSIZE, ",");
-
- found_all = true;
- if (type == -1) {
- continue;
- }
+ }
+ if (status == INS_COMPL_CPT_CONT) {
+ continue;
}
}
@@ -2616,271 +3339,12 @@ static int ins_compl_get_exp(pos_T *ini)
break;
}
- switch (type) {
- case -1:
- break;
- case CTRL_X_PATH_PATTERNS:
- case CTRL_X_PATH_DEFINES:
- find_pattern_in_path((char_u *)compl_pattern, compl_direction,
- STRLEN(compl_pattern), false, false,
- ((type == CTRL_X_PATH_DEFINES
- && !(compl_cont_status & CONT_SOL))
- ? FIND_DEFINE
- : FIND_ANY),
- 1L, ACTION_EXPAND, 1, MAXLNUM);
- break;
-
- case CTRL_X_DICTIONARY:
- case CTRL_X_THESAURUS:
- if (thesaurus_func_complete(type)) {
- expand_by_function(type, (char_u *)compl_pattern);
- } else {
- ins_compl_dictionaries(dict != NULL ? dict
- : (type == CTRL_X_THESAURUS
- ? (*curbuf->b_p_tsr == NUL ? p_tsr : curbuf->b_p_tsr)
- : (*curbuf->b_p_dict ==
- NUL ? p_dict : curbuf->b_p_dict)),
- (char_u *)compl_pattern,
- dict != NULL ? dict_f : 0, type == CTRL_X_THESAURUS);
- }
- dict = NULL;
- break;
-
- case CTRL_X_TAGS:
- // set p_ic according to p_ic, p_scs and pat for find_tags().
- save_p_ic = p_ic;
- p_ic = ignorecase((char_u *)compl_pattern);
-
- // Find up to TAG_MANY matches. Avoids that an enormous number
- // of matches is found when compl_pattern is empty
- g_tag_at_cursor = true;
- if (find_tags((char_u *)compl_pattern, &num_matches, &matches,
- TAG_REGEXP | TAG_NAMES | TAG_NOIC | TAG_INS_COMP
- | (ctrl_x_mode_not_default() ? TAG_VERBOSE : 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;
- p_ic = save_p_ic;
- break;
-
- case CTRL_X_FILES:
- if (expand_wildcards(1, &compl_pattern, &num_matches, &matches,
- EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) == OK) {
- // May change home directory back to "~".
- tilde_replace((char_u *)compl_pattern, num_matches, matches);
-#ifdef BACKSLASH_IN_FILENAME
- if (curbuf->b_p_csl[0] != NUL) {
- for (int i = 0; i < num_matches; i++) {
- char_u *ptr = matches[i];
- while (*ptr != NUL) {
- if (curbuf->b_p_csl[0] == 's' && *ptr == '\\') {
- *ptr = '/';
- } else if (curbuf->b_p_csl[0] == 'b' && *ptr == '/') {
- *ptr = '\\';
- }
- ptr += utfc_ptr2len(ptr);
- }
- }
- }
-#endif
- ins_compl_add_matches(num_matches, matches, p_fic || p_wic);
- }
- break;
-
- case CTRL_X_CMDLINE:
- case CTRL_X_CMDLINE_CTRL_X:
- if (expand_cmdline(&compl_xp, (char_u *)compl_pattern,
- (int)STRLEN(compl_pattern),
- &num_matches, &matches) == EXPAND_OK) {
- ins_compl_add_matches(num_matches, matches, false);
- }
- break;
-
- case CTRL_X_FUNCTION:
- case CTRL_X_OMNI:
- expand_by_function(type, (char_u *)compl_pattern);
- break;
-
- case CTRL_X_SPELL:
- num_matches = expand_spelling(first_match_pos.lnum,
- (char_u *)compl_pattern, &matches);
- if (num_matches > 0) {
- ins_compl_add_matches(num_matches, matches, p_ic);
- }
- break;
-
- default: // normal ^P/^N and ^X^L
- // If 'infercase' is set, don't use 'smartcase' here
- save_p_scs = p_scs;
- assert(ins_buf);
- if (ins_buf->b_p_inf) {
- p_scs = false;
- }
-
- // Buffers other than curbuf are scanned from the beginning or the
- // end but never from the middle, thus setting nowrapscan in this
- // buffers is a good idea, on the other hand, we always set
- // wrapscan for curbuf to avoid missing matches -- Acevedo,Webb
- save_p_ws = p_ws;
- if (ins_buf != curbuf) {
- p_ws = false;
- } else if (*e_cpt == '.') {
- p_ws = true;
- }
- bool looped_around = false;
- for (;;) {
- bool cont_s_ipos = false;
-
- msg_silent++; // Don't want messages for wrapscan.
- // ctrl_x_mode_line_or_eval() || word-wise search that
- // has added a word that was at the beginning of the line.
- if (ctrl_x_mode_line_or_eval()
- || (compl_cont_status & CONT_SOL)) {
- found_new_match = search_for_exact_line(ins_buf, pos,
- compl_direction,
- (char_u *)compl_pattern);
- } else {
- found_new_match = searchit(NULL, ins_buf, pos, NULL,
- compl_direction,
- (char_u *)compl_pattern, 1L,
- SEARCH_KEEP + SEARCH_NFMSG,
- RE_LAST, NULL);
- }
- msg_silent--;
- if (!compl_started || set_match_pos) {
- // set "compl_started" even on fail
- compl_started = true;
- first_match_pos = *pos;
- last_match_pos = *pos;
- set_match_pos = false;
- } else if (first_match_pos.lnum == last_match_pos.lnum
- && first_match_pos.col == last_match_pos.col) {
- found_new_match = FAIL;
- } else if ((compl_direction == FORWARD)
- && (prev_pos.lnum > pos->lnum
- || (prev_pos.lnum == pos->lnum
- && prev_pos.col >= pos->col))) {
- if (looped_around) {
- found_new_match = FAIL;
- } else {
- looped_around = true;
- }
- } else if ((compl_direction != FORWARD)
- && (prev_pos.lnum < pos->lnum
- || (prev_pos.lnum == pos->lnum
- && prev_pos.col <= pos->col))) {
- if (looped_around) {
- found_new_match = FAIL;
- } else {
- looped_around = true;
- }
- }
- prev_pos = *pos;
- if (found_new_match == FAIL) {
- if (ins_buf == curbuf) {
- found_all = true;
- }
- break;
- }
-
- // when ADDING, the text before the cursor matches, skip it
- if ((compl_cont_status & CONT_ADDING) && ins_buf == curbuf
- && ini->lnum == pos->lnum
- && ini->col == pos->col) {
- continue;
- }
- ptr = ml_get_buf(ins_buf, pos->lnum, false) + pos->col;
- if (ctrl_x_mode_line_or_eval()) {
- if (compl_cont_status & CONT_ADDING) {
- if (pos->lnum >= ins_buf->b_ml.ml_line_count) {
- continue;
- }
- ptr = ml_get_buf(ins_buf, pos->lnum + 1, false);
- if (!p_paste) {
- ptr = (char_u *)skipwhite((char *)ptr);
- }
- }
- len = (int)STRLEN(ptr);
- } else {
- char_u *tmp_ptr = ptr;
-
- if (compl_cont_status & CONT_ADDING) {
- tmp_ptr += compl_length;
- // Skip if already inside a word.
- if (vim_iswordp(tmp_ptr)) {
- continue;
- }
- // Find start of next word.
- tmp_ptr = find_word_start(tmp_ptr);
- }
- // Find end of this word.
- tmp_ptr = find_word_end(tmp_ptr);
- len = (int)(tmp_ptr - ptr);
-
- if ((compl_cont_status & CONT_ADDING)
- && len == compl_length) {
- if (pos->lnum < ins_buf->b_ml.ml_line_count) {
- // Try next line, if any. the new word will be "join" as if the
- // normal command "J" was used. IOSIZE is always greater than
- // compl_length, so the next STRNCPY always works -- Acevedo
- STRNCPY(IObuff, ptr, len); // NOLINT(runtime/printf)
- ptr = ml_get_buf(ins_buf, pos->lnum + 1, false);
- 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.
- tmp_ptr = find_word_end(tmp_ptr);
- if (tmp_ptr > ptr) {
- if (*ptr != ')' && IObuff[len - 1] != TAB) {
- if (IObuff[len - 1] != ' ') {
- IObuff[len++] = ' ';
- }
- // IObuf =~ "\k.* ", thus len >= 2
- if (p_js
- && (IObuff[len - 2] == '.'
- || IObuff[len - 2] == '?'
- || IObuff[len - 2] == '!')) {
- IObuff[len++] = ' ';
- }
- }
- // copy as much as possible of the new word
- if (tmp_ptr - ptr >= IOSIZE - len) {
- tmp_ptr = ptr + IOSIZE - len - 1;
- }
- STRLCPY(IObuff + len, ptr, IOSIZE - len);
- len += (int)(tmp_ptr - ptr);
- cont_s_ipos = true;
- }
- IObuff[len] = NUL;
- ptr = IObuff;
- }
- if (len == compl_length) {
- continue;
- }
- }
- }
- 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;
- }
- }
- p_scs = save_p_scs;
- p_ws = save_p_ws;
- }
-
- // check if compl_curr_match has changed, (e.g. other type of
- // expansion added something)
- if (type != 0 && compl_curr_match != compl_old_match) {
- found_new_match = OK;
- }
+ // get the next set of completion matches
+ found_new_match = get_next_completion_match(type, &st, ini);
// break the loop for specialized modes (use 'complete' just for the
// generic ctrl_x_mode == CTRL_X_NORMAL) or when we've found a new match
- if ((ctrl_x_mode_not_default()
- && !ctrl_x_mode_line_or_eval())
+ if ((ctrl_x_mode_not_default() && !ctrl_x_mode_line_or_eval())
|| found_new_match != FAIL) {
if (got_int) {
break;
@@ -2890,8 +3354,7 @@ static int ins_compl_get_exp(pos_T *ini)
ins_compl_check_keys(0, false);
}
- if ((ctrl_x_mode_not_default()
- && !ctrl_x_mode_line_or_eval())
+ if ((ctrl_x_mode_not_default() && !ctrl_x_mode_line_or_eval())
|| compl_interrupted) {
break;
}
@@ -2899,8 +3362,8 @@ static int ins_compl_get_exp(pos_T *ini)
} else {
// Mark a buffer scanned when it has been scanned completely
if (type == 0 || type == CTRL_X_PATH_PATTERNS) {
- assert(ins_buf);
- ins_buf->b_scanned = true;
+ assert(st.ins_buf);
+ st.ins_buf->b_scanned = true;
}
compl_started = false;
@@ -2908,16 +3371,14 @@ static int ins_compl_get_exp(pos_T *ini)
}
compl_started = true;
- if ((ctrl_x_mode_normal()
- || ctrl_x_mode_line_or_eval())
- && *e_cpt == NUL) { // Got to end of 'complete'
+ if ((ctrl_x_mode_normal() || ctrl_x_mode_line_or_eval())
+ && *st.e_cpt == NUL) { // Got to end of 'complete'
found_new_match = FAIL;
}
i = -1; // total of matches, unknown
if (found_new_match == FAIL
- || (ctrl_x_mode_not_default()
- && !ctrl_x_mode_line_or_eval())) {
+ || (ctrl_x_mode_not_default() && !ctrl_x_mode_line_or_eval())) {
i = ins_compl_make_cyclic();
}
@@ -2925,7 +3386,7 @@ static int ins_compl_get_exp(pos_T *ini)
// If several matches were added (FORWARD) or the search failed and has
// just been made cyclic then we have to move compl_curr_match to the
// next or previous entry (if any) -- Acevedo
- compl_curr_match = compl_direction == FORWARD
+ compl_curr_match = compl_dir_forward()
? compl_old_match->cp_next
: compl_old_match->cp_prev;
if (compl_curr_match == NULL) {
@@ -2937,6 +3398,31 @@ static int ins_compl_get_exp(pos_T *ini)
return i;
}
+/// Update "compl_shown_match" to the actually shown match, it may differ when
+/// "compl_leader" is used to omit some of the matches.
+static void ins_compl_update_shown_match(void)
+{
+ while (!ins_compl_equal(compl_shown_match,
+ compl_leader, strlen(compl_leader))
+ && compl_shown_match->cp_next != NULL
+ && !is_first_match(compl_shown_match->cp_next)) {
+ compl_shown_match = compl_shown_match->cp_next;
+ }
+
+ // 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, strlen(compl_leader))
+ && (compl_shown_match->cp_next == NULL
+ || is_first_match(compl_shown_match->cp_next))) {
+ while (!ins_compl_equal(compl_shown_match, compl_leader, strlen(compl_leader))
+ && compl_shown_match->cp_prev != NULL
+ && !is_first_match(compl_shown_match->cp_prev)) {
+ compl_shown_match = compl_shown_match->cp_prev;
+ }
+ }
+}
+
/// Delete the old text being completed.
void ins_compl_delete(void)
{
@@ -2944,7 +3430,7 @@ void ins_compl_delete(void)
// In insert mode: Delete the typed part.
// In replace mode: Put the old characters back, if any.
- col = compl_col + (compl_cont_status & CONT_ADDING ? compl_length : 0);
+ col = compl_col + (compl_status_adding() ? compl_length : 0);
if ((int)curwin->w_cursor.col > col) {
if (stop_arrow() == FAIL) {
return;
@@ -2964,7 +3450,7 @@ void ins_compl_delete(void)
void ins_compl_insert(bool in_compl_func)
{
ins_bytes(compl_shown_match->cp_str + get_compl_len());
- compl_used_match = !(compl_shown_match->cp_flags & CP_ORIGINAL_TEXT);
+ compl_used_match = !match_at_original_text(compl_shown_match);
dict_T *dict = ins_compl_dict_alloc(compl_shown_match);
set_vim_var_dict(VV_COMPLETED_ITEM, dict);
@@ -2973,97 +3459,69 @@ void ins_compl_insert(bool in_compl_func)
}
}
-/// Fill in the next completion in the current direction.
-/// If "allow_get_expansion" is true, then we may call ins_compl_get_exp() to
-/// get more completions. If it is false, then we just do nothing when there
-/// are no more completions in a given direction. The latter case is used when
-/// we are still in the middle of finding completions, to allow browsing
-/// through the ones found so far.
-/// @return the total number of matches, or -1 if still unknown -- webb.
-///
-/// compl_curr_match is currently being used by ins_compl_get_exp(), so we use
-/// compl_shown_match here.
-///
-/// Note that this function may be called recursively once only. First with
-/// "allow_get_expansion" true, which calls ins_compl_get_exp(), which in turn
-/// calls this function with "allow_get_expansion" false.
-///
-/// @param count Repeat completion this many times; should be at least 1
-/// @param insert_match Insert the newly selected match
-/// @param in_compl_func Called from complete_check()
-static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match,
- bool in_compl_func)
+/// show the file name for the completion match (if any). Truncate the file
+/// name to avoid a wait for return.
+static void ins_compl_show_filename(void)
{
- int num_matches = -1;
- int todo = count;
- compl_T *found_compl = NULL;
- bool found_end = false;
- const bool started = compl_started;
-
- // When user complete function return -1 for findstart which is next
- // time of 'always', compl_shown_match become NULL.
- if (compl_shown_match == NULL) {
- return -1;
+ char *const lead = _("match in file");
+ int space = sc_col - vim_strsize(lead) - 2;
+ if (space <= 0) {
+ return;
}
- if (compl_leader != NULL
- && (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) == 0) {
- // Set "compl_shown_match" to the actually shown match, it may differ
- // when "compl_leader" is used to omit some of the matches.
- while (!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) {
- compl_shown_match = compl_shown_match->cp_next;
- }
-
- // 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, 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, 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;
- }
+ // 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".
+ char *s;
+ char *e;
+ for (s = e = compl_shown_match->cp_fname; *e != NUL; MB_PTR_ADV(e)) {
+ space -= ptr2cells(e);
+ while (space < 0) {
+ space += ptr2cells(s);
+ MB_PTR_ADV(s);
}
}
+ msg_hist_off = true;
+ vim_snprintf(IObuff, IOSIZE, "%s %s%s", lead,
+ s > compl_shown_match->cp_fname ? "<" : "", s);
+ msg(IObuff);
+ msg_hist_off = false;
+ redraw_cmdline = false; // don't overwrite!
+}
- if (allow_get_expansion && insert_match
- && (!(compl_get_longest || compl_restarting) || compl_used_match)) {
- // Delete old text to be replaced
- ins_compl_delete();
- }
-
- // When finding the longest common text we stick at the original text,
- // don't let CTRL-N or CTRL-P move to the first match.
- bool advance = count != 1 || !allow_get_expansion || !compl_get_longest;
-
- // When restarting the search don't insert the first match either.
- if (compl_restarting) {
- advance = false;
- compl_restarting = false;
- }
+/// Find the next set of matches for completion. Repeat the completion "todo"
+/// times. The number of matches found is returned in 'num_matches'.
+///
+/// @param allow_get_expansion If true, then ins_compl_get_exp() may be called to
+/// get more completions.
+/// If false, then do nothing when there are no more
+/// completions in the given direction.
+/// @param todo repeat completion this many times
+/// @param advance If true, then completion will move to the first match.
+/// Otherwise, the original text will be shown.
+///
+/// @return OK on success and -1 if the number of matches are unknown.
+static int find_next_completion_match(bool allow_get_expansion, int todo, bool advance,
+ int *num_matches)
+{
+ bool found_end = false;
+ compl_T *found_compl = NULL;
- // Repeat this for when <PageUp> or <PageDown> is typed. But don't wrap
- // around.
while (--todo >= 0) {
- if (compl_shows_dir == FORWARD && compl_shown_match->cp_next != NULL) {
+ if (compl_shows_dir_forward() && compl_shown_match->cp_next != NULL) {
compl_shown_match = compl_shown_match->cp_next;
found_end = (compl_first_match != NULL
- && (compl_shown_match->cp_next == compl_first_match
- || compl_shown_match == compl_first_match));
- } else if (compl_shows_dir == BACKWARD
+ && (is_first_match(compl_shown_match->cp_next)
+ || is_first_match(compl_shown_match)));
+ } else if (compl_shows_dir_backward()
&& compl_shown_match->cp_prev != NULL) {
- found_end = (compl_shown_match == compl_first_match);
+ found_end = is_first_match(compl_shown_match);
compl_shown_match = compl_shown_match->cp_prev;
- found_end |= (compl_shown_match == compl_first_match);
+ found_end |= is_first_match(compl_shown_match);
} else {
if (!allow_get_expansion) {
if (advance) {
- if (compl_shows_dir == BACKWARD) {
+ if (compl_shows_dir_backward()) {
compl_pending -= todo + 1;
} else {
compl_pending += todo + 1;
@@ -3073,7 +3531,7 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
}
if (!compl_no_select && advance) {
- if (compl_shows_dir == BACKWARD) {
+ if (compl_shows_dir_backward()) {
compl_pending--;
} else {
compl_pending++;
@@ -3081,7 +3539,7 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
}
// Find matches.
- num_matches = ins_compl_get_exp(&compl_startpos);
+ *num_matches = ins_compl_get_exp(&compl_startpos);
// handle any pending completions
while (compl_pending != 0 && compl_direction == compl_shows_dir
@@ -3099,10 +3557,10 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
}
found_end = false;
}
- if ((compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) == 0
+ if (!match_at_original_text(compl_shown_match)
&& compl_leader != NULL
&& !ins_compl_equal(compl_shown_match,
- compl_leader, STRLEN(compl_leader))) {
+ compl_leader, strlen(compl_leader))) {
todo++;
} else {
// Remember a matching item.
@@ -3119,6 +3577,68 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
}
}
+ return OK;
+}
+
+/// Fill in the next completion in the current direction.
+/// If "allow_get_expansion" is true, then we may call ins_compl_get_exp() to
+/// get more completions. If it is false, then we just do nothing when there
+/// are no more completions in a given direction. The latter case is used when
+/// we are still in the middle of finding completions, to allow browsing
+/// through the ones found so far.
+/// @return the total number of matches, or -1 if still unknown -- webb.
+///
+/// compl_curr_match is currently being used by ins_compl_get_exp(), so we use
+/// compl_shown_match here.
+///
+/// Note that this function may be called recursively once only. First with
+/// "allow_get_expansion" true, which calls ins_compl_get_exp(), which in turn
+/// calls this function with "allow_get_expansion" false.
+///
+/// @param count Repeat completion this many times; should be at least 1
+/// @param insert_match Insert the newly selected match
+/// @param in_compl_func Called from complete_check()
+static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match,
+ bool in_compl_func)
+{
+ int num_matches = -1;
+ int todo = count;
+ const bool started = compl_started;
+
+ // When user complete function return -1 for findstart which is next
+ // time of 'always', compl_shown_match become NULL.
+ if (compl_shown_match == NULL) {
+ return -1;
+ }
+
+ if (compl_leader != NULL && !match_at_original_text(compl_shown_match)) {
+ // Update "compl_shown_match" to the actually shown match
+ ins_compl_update_shown_match();
+ }
+
+ if (allow_get_expansion && insert_match
+ && (!(compl_get_longest || compl_restarting) || compl_used_match)) {
+ // Delete old text to be replaced
+ ins_compl_delete();
+ }
+
+ // When finding the longest common text we stick at the original text,
+ // don't let CTRL-N or CTRL-P move to the first match.
+ bool advance = count != 1 || !allow_get_expansion || !compl_get_longest;
+
+ // When restarting the search don't insert the first match either.
+ if (compl_restarting) {
+ advance = false;
+ compl_restarting = false;
+ }
+
+ // Repeat this for when <PageUp> or <PageDown> is typed. But don't wrap
+ // around.
+ if (find_next_completion_match(allow_get_expansion, todo, advance,
+ &num_matches) == -1) {
+ return -1;
+ }
+
// Insert the text of the new completion, or the compl_leader.
if (compl_no_insert && !started) {
ins_bytes(compl_orig_text + get_compl_len());
@@ -3135,7 +3655,7 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
if (!allow_get_expansion) {
// redraw to show the user what was inserted
- update_screen(0);
+ update_screen(); // TODO(bfredl): no!
// display the updated popup menu
ins_compl_show_pum();
@@ -3154,47 +3674,13 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
}
// Show the file name for the match (if any)
- // Truncate the file name to avoid a wait for return.
if (compl_shown_match->cp_fname != NULL) {
- char *lead = _("match in file");
- 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 = (char *)compl_shown_match->cp_fname; *e != NUL; MB_PTR_ADV(e)) {
- space -= ptr2cells(e);
- while (space < 0) {
- space += ptr2cells(s);
- MB_PTR_ADV(s);
- }
- }
- msg_hist_off = true;
- vim_snprintf((char *)IObuff, IOSIZE, "%s %s%s", lead,
- (char_u *)s > compl_shown_match->cp_fname ? "<" : "", s);
- msg((char *)IObuff);
- msg_hist_off = false;
- redraw_cmdline = false; // don't overwrite!
- }
+ ins_compl_show_filename();
}
return num_matches;
}
-void pum_ext_select_item(int item, bool insert, bool finish)
-{
- if (!pum_visible() || item < -1 || item >= compl_match_arraysize) {
- return;
- }
- pum_want.active = true;
- pum_want.item = item;
- pum_want.insert = insert;
- pum_want.finish = finish;
-}
-
/// Call this while finding completions, to check whether the user has hit a key
/// that should change the currently displayed completion, or exit completion
/// mode. Also, when compl_pending is not zero, show a completion as soon as
@@ -3328,36 +3814,36 @@ static bool ins_compl_use_match(int c)
/// completion)
/// Sets the global variables: compl_col, compl_length and compl_pattern.
/// Uses the global variables: compl_cont_status and ctrl_x_mode
-static int get_normal_compl_info(char_u *line, int startcol, colnr_T curs_col)
+static int get_normal_compl_info(char *line, int startcol, colnr_T curs_col)
{
if ((compl_cont_status & CONT_SOL) || ctrl_x_mode_path_defines()) {
- if (!(compl_cont_status & CONT_ADDING)) {
- while (--startcol >= 0 && vim_isIDc(line[startcol])) {}
+ if (!compl_status_adding()) {
+ while (--startcol >= 0 && vim_isIDc((uint8_t)line[startcol])) {}
compl_col += ++startcol;
compl_length = curs_col - startcol;
}
if (p_ic) {
- compl_pattern = (char *)str_foldcase(line + compl_col, compl_length, NULL, 0);
+ compl_pattern = str_foldcase(line + compl_col, compl_length, NULL, 0);
} else {
- compl_pattern = (char *)vim_strnsave(line + compl_col, (size_t)compl_length);
+ compl_pattern = xstrnsave(line + compl_col, (size_t)compl_length);
}
- } else if (compl_cont_status & CONT_ADDING) {
- char_u *prefix = (char_u *)"\\<";
+ } else if (compl_status_adding()) {
+ char *prefix = "\\<";
// we need up to 2 extra chars for the prefix
- compl_pattern = xmalloc(quote_meta(NULL, line + compl_col,
- compl_length) + 2);
+ compl_pattern = xmalloc(quote_meta(NULL, line + compl_col, compl_length) + 2);
if (!vim_iswordp(line + compl_col)
- || (compl_col > 0 && (vim_iswordp(mb_prevptr(line, line + compl_col))))) {
- prefix = (char_u *)"";
+ || (compl_col > 0
+ && (vim_iswordp(mb_prevptr(line, line + compl_col))))) {
+ prefix = "";
}
STRCPY(compl_pattern, prefix);
- (void)quote_meta((char_u *)compl_pattern + STRLEN(prefix),
+ (void)quote_meta(compl_pattern + strlen(prefix),
line + compl_col, compl_length);
} else if (--startcol < 0
|| !vim_iswordp(mb_prevptr(line, line + startcol + 1))) {
// Match any word of at least two chars
- compl_pattern = (char *)vim_strsave((char_u *)"\\<\\k\\k");
+ compl_pattern = xstrdup("\\<\\k\\k");
compl_col += curs_col;
compl_length = 0;
} else {
@@ -3380,13 +3866,12 @@ static int get_normal_compl_info(char_u *line, int startcol, colnr_T curs_col)
// xmalloc(7) is enough -- Acevedo
compl_pattern = xmalloc(7);
STRCPY(compl_pattern, "\\<");
- (void)quote_meta((char_u *)compl_pattern + 2, line + compl_col, 1);
+ (void)quote_meta(compl_pattern + 2, line + compl_col, 1);
STRCAT(compl_pattern, "\\k");
} else {
- compl_pattern = xmalloc(quote_meta(NULL, line + compl_col,
- compl_length) + 2);
+ compl_pattern = xmalloc(quote_meta(NULL, line + compl_col, compl_length) + 2);
STRCPY(compl_pattern, "\\<");
- (void)quote_meta((char_u *)compl_pattern + 2, line + compl_col, compl_length);
+ (void)quote_meta(compl_pattern + 2, line + compl_col, compl_length);
}
}
@@ -3396,7 +3881,7 @@ static int get_normal_compl_info(char_u *line, int startcol, colnr_T curs_col)
/// Get the pattern, column and length for whole line completion or for the
/// complete() function.
/// Sets the global variables: compl_col, compl_length and compl_pattern.
-static int get_wholeline_compl_info(char_u *line, colnr_T curs_col)
+static int get_wholeline_compl_info(char *line, colnr_T curs_col)
{
compl_col = (colnr_T)getwhitecols(line);
compl_length = (int)curs_col - (int)compl_col;
@@ -3404,9 +3889,9 @@ static int get_wholeline_compl_info(char_u *line, colnr_T curs_col)
compl_length = 0;
}
if (p_ic) {
- compl_pattern = (char *)str_foldcase(line + compl_col, compl_length, NULL, 0);
+ compl_pattern = str_foldcase(line + compl_col, compl_length, NULL, 0);
} else {
- compl_pattern = (char *)vim_strnsave(line + compl_col, (size_t)compl_length);
+ compl_pattern = xstrnsave(line + compl_col, (size_t)compl_length);
}
return OK;
@@ -3414,17 +3899,17 @@ static int get_wholeline_compl_info(char_u *line, colnr_T curs_col)
/// Get the pattern, column and length for filename completion.
/// Sets the global variables: compl_col, compl_length and compl_pattern.
-static int get_filename_compl_info(char_u *line, int startcol, colnr_T curs_col)
+static int get_filename_compl_info(char *line, int startcol, colnr_T curs_col)
{
// Go back to just before the first filename character.
if (startcol > 0) {
- char_u *p = line + startcol;
+ char *p = line + startcol;
MB_PTR_BACK(line, p);
- while (p > line && vim_isfilec(utf_ptr2char((char *)p))) {
+ while (p > line && vim_isfilec(utf_ptr2char(p))) {
MB_PTR_BACK(line, p);
}
- if (p == line && vim_isfilec(utf_ptr2char((char *)p))) {
+ if (p == line && vim_isfilec(utf_ptr2char(p))) {
startcol = 0;
} else {
startcol = (int)(p - line) + 1;
@@ -3433,17 +3918,17 @@ static int get_filename_compl_info(char_u *line, int startcol, colnr_T curs_col)
compl_col += startcol;
compl_length = (int)curs_col - startcol;
- compl_pattern = (char *)addstar(line + compl_col, (size_t)compl_length, EXPAND_FILES);
+ compl_pattern = addstar(line + compl_col, (size_t)compl_length, EXPAND_FILES);
return OK;
}
/// Get the pattern, column and length for command-line completion.
/// Sets the global variables: compl_col, compl_length and compl_pattern.
-static int get_cmdline_compl_info(char_u *line, colnr_T curs_col)
+static int get_cmdline_compl_info(char *line, colnr_T curs_col)
{
- compl_pattern = (char *)vim_strnsave(line, (size_t)curs_col);
- set_cmd_context(&compl_xp, (char_u *)compl_pattern, (int)STRLEN(compl_pattern), curs_col, false);
+ compl_pattern = xstrnsave(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
|| compl_xp.xp_context == EXPAND_NOTHING) {
// No completion possible, use an empty pattern to get a
@@ -3468,7 +3953,7 @@ static int get_userdefined_compl_info(colnr_T curs_col)
const int save_State = State;
// Call 'completefunc' or 'omnifunc' and get pattern length as a string
- char_u *funcname = get_complete_funcname(ctrl_x_mode);
+ char *funcname = get_complete_funcname(ctrl_x_mode);
if (*funcname == NUL) {
semsg(_(e_notset), ctrl_x_mode_function() ? "completefunc" : "omnifunc");
return FAIL;
@@ -3483,7 +3968,8 @@ static int get_userdefined_compl_info(colnr_T curs_col)
pos_T pos = curwin->w_cursor;
textlock++;
- colnr_T col = (colnr_T)call_func_retnr((char *)funcname, 2, args);
+ Callback *cb = get_insert_callback(ctrl_x_mode);
+ colnr_T col = (colnr_T)callback_call_retnr(cb, 2, args);
textlock--;
State = save_State;
@@ -3510,7 +3996,7 @@ static int get_userdefined_compl_info(colnr_T curs_col)
return FAIL;
}
- // Reset extended parameters of completion, when start new
+ // Reset extended parameters of completion, when starting new
// completion.
compl_opt_refresh_always = false;
@@ -3524,9 +4010,9 @@ static int get_userdefined_compl_info(colnr_T curs_col)
// Setup variables for completion. Need to obtain "line" again,
// it may have become invalid.
- char_u *line = ml_get(curwin->w_cursor.lnum);
+ char *line = ml_get(curwin->w_cursor.lnum);
compl_length = curs_col - compl_col;
- compl_pattern = (char *)vim_strnsave(line + compl_col, (size_t)compl_length);
+ compl_pattern = xstrnsave(line + compl_col, (size_t)compl_length);
return OK;
}
@@ -3550,14 +4036,22 @@ static int get_spell_compl_info(int startcol, colnr_T curs_col)
compl_length = (int)curs_col - compl_col;
}
// Need to obtain "line" again, it may have become invalid.
- char_u *line = ml_get(curwin->w_cursor.lnum);
- compl_pattern = (char *)vim_strnsave(line + compl_col, (size_t)compl_length);
+ char *line = ml_get(curwin->w_cursor.lnum);
+ compl_pattern = xstrnsave(line + compl_col, (size_t)compl_length);
return OK;
}
/// Get the completion pattern, column and length.
-static int compl_get_info(char_u *line, int startcol, colnr_T curs_col, bool *line_invalid)
+///
+/// @param startcol start column number of the completion pattern/text
+/// @param cur_col current cursor column
+///
+/// On return, "line_invalid" is set to true, if the current line may have
+/// become invalid and needs to be fetched again.
+///
+/// @return OK on success.
+static int compl_get_info(char *line, int startcol, colnr_T curs_col, bool *line_invalid)
{
if (ctrl_x_mode_normal()
|| ((ctrl_x_mode & CTRL_X_WANT_IDENT)
@@ -3574,12 +4068,12 @@ static int compl_get_info(char_u *line, int startcol, colnr_T curs_col, bool *li
if (get_userdefined_compl_info(curs_col) == FAIL) {
return FAIL;
}
- *line_invalid = true; // 'line' may have become invalid
+ *line_invalid = true; // "line" may have become invalid
} else if (ctrl_x_mode_spell()) {
if (get_spell_compl_info(startcol, curs_col) == FAIL) {
return FAIL;
}
- *line_invalid = true; // 'line' may have become invalid
+ *line_invalid = true; // "line" may have become invalid
} else {
internal_error("ins_complete()");
return FAIL;
@@ -3588,232 +4082,189 @@ static int compl_get_info(char_u *line, int startcol, colnr_T curs_col, bool *li
return OK;
}
-/// Do Insert mode completion.
-/// Called when character "c" was typed, which has a meaning for completion.
-/// Returns OK if completion was done, FAIL if something failed.
-int ins_complete(int c, bool enable_pum)
+/// Continue an interrupted completion mode search in "line".
+///
+/// If this same ctrl_x_mode has been interrupted use the text from
+/// "compl_startpos" to the cursor as a pattern to add a new word instead of
+/// expand the one before the cursor, in word-wise if "compl_startpos" is not in
+/// the same line as the cursor then fix it (the line has been split because it
+/// was longer than 'tw'). if SOL is set then skip the previous pattern, a word
+/// at the beginning of the line has been inserted, we'll look for that.
+static void ins_compl_continue_search(char *line)
{
- char_u *line;
- int startcol = 0; // column where searched text starts
- colnr_T curs_col; // cursor column
- int n;
- int save_w_wrow;
- int save_w_leftcol;
- int insert_match;
- const bool save_did_ai = did_ai;
- int flags = CP_ORIGINAL_TEXT;
- bool line_invalid = false;
-
- compl_direction = ins_compl_key2dir(c);
- insert_match = ins_compl_use_match(c);
-
- if (!compl_started) {
- // First time we hit ^N or ^P (in a row, I mean)
-
- did_ai = false;
- did_si = false;
- can_si = false;
- can_si_back = false;
- if (stop_arrow() == FAIL) {
- return FAIL;
- }
-
- line = ml_get(curwin->w_cursor.lnum);
- curs_col = curwin->w_cursor.col;
- compl_pending = 0;
-
- // If this same ctrl_x_mode has been interrupted use the text from
- // "compl_startpos" to the cursor as a pattern to add a new word
- // instead of expand the one before the cursor, in word-wise if
- // "compl_startpos" is not in the same line as the cursor then fix it
- // (the line has been split because it was longer than 'tw'). if SOL
- // is set then skip the previous pattern, a word at the beginning of
- // the line has been inserted, we'll look for that -- Acevedo.
- if ((compl_cont_status & CONT_INTRPT) == CONT_INTRPT
- && compl_cont_mode == ctrl_x_mode) {
- // it is a continued search
- compl_cont_status &= ~CONT_INTRPT; // remove INTRPT
- if (ctrl_x_mode_normal()
- || ctrl_x_mode_path_patterns()
- || ctrl_x_mode_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 below.
- compl_col = (colnr_T)getwhitecols(line);
- compl_startpos.col = compl_col;
- compl_startpos.lnum = curwin->w_cursor.lnum;
- compl_cont_status &= ~CONT_SOL; // clear SOL if present
- } else {
- // S_IPOS was set when we inserted a word that was at the
- // beginning of the line, which means that we'll go to SOL
- // 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)((char_u *)skipwhite((char *)line
- + compl_length
- + compl_startpos.col) - line);
- }
- compl_col = compl_startpos.col;
- }
- compl_length = curwin->w_cursor.col - (int)compl_col;
- // IObuff is used to add a "word from the next line" would we
- // have enough space? just being paranoid
-#define MIN_SPACE 75
- if (compl_length > (IOSIZE - MIN_SPACE)) {
- compl_cont_status &= ~CONT_SOL;
- compl_length = (IOSIZE - MIN_SPACE);
- compl_col = curwin->w_cursor.col - compl_length;
- }
- compl_cont_status |= CONT_ADDING | CONT_N_ADDS;
- if (compl_length < 1) {
- compl_cont_status &= CONT_LOCAL;
- }
- } else if (ctrl_x_mode_line_or_eval()) {
- compl_cont_status = CONT_ADDING | CONT_N_ADDS;
- } else {
- compl_cont_status = 0;
- }
+ // it is a continued search
+ compl_cont_status &= ~CONT_INTRPT; // remove INTRPT
+ if (ctrl_x_mode_normal()
+ || ctrl_x_mode_path_patterns()
+ || ctrl_x_mode_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 below.
+ compl_col = (colnr_T)getwhitecols(line);
+ compl_startpos.col = compl_col;
+ compl_startpos.lnum = curwin->w_cursor.lnum;
+ compl_cont_status &= ~CONT_SOL; // clear SOL if present
} else {
- compl_cont_status &= CONT_LOCAL;
- }
-
- if (!(compl_cont_status & CONT_ADDING)) { // normal expansion
- compl_cont_mode = ctrl_x_mode;
- if (ctrl_x_mode_not_default()) {
- // Remove LOCAL if ctrl_x_mode != CTRL_X_NORMAL
- compl_cont_status = 0;
+ // S_IPOS was set when we inserted a word that was at the
+ // beginning of the line, which means that we'll go to SOL
+ // 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_cont_status |= CONT_N_ADDS;
- compl_startpos = curwin->w_cursor;
- startcol = (int)curs_col;
- compl_col = 0;
+ compl_col = compl_startpos.col;
}
-
- // Work out completion pattern and original text -- webb
- if (compl_get_info(line, startcol, curs_col, &line_invalid) == FAIL) {
- if (ctrl_x_mode_function() || ctrl_x_mode_omni()
- || thesaurus_func_complete(ctrl_x_mode)) {
- // restore did_ai, so that adding comment leader works
- did_ai = save_did_ai;
- }
- return FAIL;
+ compl_length = curwin->w_cursor.col - (int)compl_col;
+ // IObuff is used to add a "word from the next line" would we
+ // have enough space? just being paranoid
+#define MIN_SPACE 75
+ if (compl_length > (IOSIZE - MIN_SPACE)) {
+ compl_cont_status &= ~CONT_SOL;
+ compl_length = (IOSIZE - MIN_SPACE);
+ compl_col = curwin->w_cursor.col - compl_length;
}
- // If "line" was changed while getting completion info get it again.
- if (line_invalid) {
- line = ml_get(curwin->w_cursor.lnum);
+ compl_cont_status |= CONT_ADDING | CONT_N_ADDS;
+ if (compl_length < 1) {
+ compl_cont_status &= CONT_LOCAL;
}
+ } else if (ctrl_x_mode_line_or_eval()) {
+ compl_cont_status = CONT_ADDING | CONT_N_ADDS;
+ } else {
+ compl_cont_status = 0;
+ }
+}
- if (compl_cont_status & CONT_ADDING) {
- edit_submode_pre = (char_u *)_(" Adding");
- if (ctrl_x_mode_line_or_eval()) {
- // Insert a new line, keep indentation but ignore 'comments'.
- char_u *old = curbuf->b_p_com;
+/// start insert mode completion
+static int ins_compl_start(void)
+{
+ const bool save_did_ai = did_ai;
- curbuf->b_p_com = (char_u *)"";
- compl_startpos.lnum = curwin->w_cursor.lnum;
- compl_startpos.col = compl_col;
- ins_eol('\r');
- curbuf->b_p_com = old;
- compl_length = 0;
- compl_col = curwin->w_cursor.col;
- }
- } else {
- edit_submode_pre = NULL;
- compl_startpos.col = compl_col;
- }
+ // First time we hit ^N or ^P (in a row, I mean)
- if (compl_cont_status & CONT_LOCAL) {
- edit_submode = (char_u *)_(ctrl_x_msgs[CTRL_X_LOCAL_MSG]);
- } else {
- edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode));
- }
+ did_ai = false;
+ did_si = false;
+ can_si = false;
+ can_si_back = false;
+ if (stop_arrow() == FAIL) {
+ return FAIL;
+ }
- // If any of the original typed text has been changed we need to fix
- // the redo buffer.
- ins_compl_fixRedoBufForLeader(NULL);
+ char *line = ml_get(curwin->w_cursor.lnum);
+ colnr_T curs_col = curwin->w_cursor.col;
+ compl_pending = 0;
- // Always add completion for the original text.
- xfree(compl_orig_text);
- compl_orig_text = vim_strnsave(line + compl_col, (size_t)compl_length);
- if (p_ic) {
- flags |= CP_ICASE;
- }
- if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, NULL, 0,
- flags, false) != OK) {
- XFREE_CLEAR(compl_pattern);
- XFREE_CLEAR(compl_orig_text);
- return FAIL;
+ if ((compl_cont_status & CONT_INTRPT) == CONT_INTRPT
+ && compl_cont_mode == ctrl_x_mode) {
+ // this same ctrl-x_mode was interrupted previously. Continue the
+ // completion.
+ ins_compl_continue_search(line);
+ } else {
+ compl_cont_status &= CONT_LOCAL;
+ }
+
+ int startcol = 0; // column where searched text starts
+ if (!compl_status_adding()) { // normal expansion
+ compl_cont_mode = ctrl_x_mode;
+ if (ctrl_x_mode_not_default()) {
+ // Remove LOCAL if ctrl_x_mode != CTRL_X_NORMAL
+ compl_cont_status = 0;
}
+ compl_cont_status |= CONT_N_ADDS;
+ compl_startpos = curwin->w_cursor;
+ startcol = (int)curs_col;
+ compl_col = 0;
+ }
- // showmode might reset the internal line pointers, so it must
- // be called before line = ml_get(), or when this address is no
- // longer needed. -- Acevedo.
- edit_submode_extra = (char_u *)_("-- Searching...");
- edit_submode_highl = HLF_COUNT;
- showmode();
- edit_submode_extra = NULL;
- ui_flush();
- } else if (insert_match && stop_arrow() == FAIL) {
+ // Work out completion pattern and original text -- webb
+ bool line_invalid = false;
+ if (compl_get_info(line, startcol, curs_col, &line_invalid) == FAIL) {
+ if (ctrl_x_mode_function() || ctrl_x_mode_omni()
+ || thesaurus_func_complete(ctrl_x_mode)) {
+ // restore did_ai, so that adding comment leader works
+ did_ai = save_did_ai;
+ }
return FAIL;
}
+ // If "line" was changed while getting completion info get it again.
+ if (line_invalid) {
+ line = ml_get(curwin->w_cursor.lnum);
+ }
- compl_shown_match = compl_curr_match;
- compl_shows_dir = compl_direction;
+ if (compl_status_adding()) {
+ edit_submode_pre = _(" Adding");
+ if (ctrl_x_mode_line_or_eval()) {
+ // Insert a new line, keep indentation but ignore 'comments'.
+ char *old = curbuf->b_p_com;
- // Find next match (and following matches).
- save_w_wrow = curwin->w_wrow;
- save_w_leftcol = curwin->w_leftcol;
- n = ins_compl_next(true, ins_compl_key2count(c), insert_match, false);
+ curbuf->b_p_com = "";
+ compl_startpos.lnum = curwin->w_cursor.lnum;
+ compl_startpos.col = compl_col;
+ ins_eol('\r');
+ curbuf->b_p_com = old;
+ compl_length = 0;
+ compl_col = curwin->w_cursor.col;
+ }
+ } else {
+ edit_submode_pre = NULL;
+ compl_startpos.col = compl_col;
+ }
- if (n > 1) { // all matches have been found
- compl_matches = n;
+ if (compl_cont_status & CONT_LOCAL) {
+ edit_submode = _(ctrl_x_msgs[CTRL_X_LOCAL_MSG]);
+ } else {
+ edit_submode = _(CTRL_X_MSG(ctrl_x_mode));
}
- compl_curr_match = compl_shown_match;
- compl_direction = compl_shows_dir;
- // Eat the ESC that vgetc() returns after a CTRL-C to avoid leaving Insert
- // mode.
- if (got_int && !global_busy) {
- (void)vgetc();
- got_int = false;
+ // If any of the original typed text has been changed we need to fix
+ // the redo buffer.
+ ins_compl_fixRedoBufForLeader(NULL);
+
+ // Always add completion for the original text.
+ xfree(compl_orig_text);
+ compl_orig_text = xstrnsave(line + compl_col, (size_t)compl_length);
+ int flags = CP_ORIGINAL_TEXT;
+ if (p_ic) {
+ flags |= CP_ICASE;
+ }
+ if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, NULL, 0,
+ flags, false) != OK) {
+ XFREE_CLEAR(compl_pattern);
+ XFREE_CLEAR(compl_orig_text);
+ return FAIL;
}
+ // showmode might reset the internal line pointers, so it must
+ // be called before line = ml_get(), or when this address is no
+ // longer needed. -- Acevedo.
+ edit_submode_extra = _("-- Searching...");
+ edit_submode_highl = HLF_COUNT;
+ showmode();
+ edit_submode_extra = NULL;
+ ui_flush();
+
+ return OK;
+}
+
+/// display the completion status message
+static void ins_compl_show_statusmsg(void)
+{
// we found no match if the list has only the "compl_orig_text"-entry
- if (compl_first_match == compl_first_match->cp_next) {
- edit_submode_extra = (compl_cont_status & CONT_ADDING)
- && compl_length > 1
- ? (char_u *)_(e_hitend) : (char_u *)_(e_patnotf);
+ if (is_first_match(compl_first_match->cp_next)) {
+ edit_submode_extra = compl_status_adding() && compl_length > 1 ? _(e_hitend) : _(e_patnotf);
edit_submode_highl = HLF_E;
- // remove N_ADDS flag, so next ^X<> won't try to go to ADDING mode,
- // because we couldn't expand anything at first place, but if we used
- // ^P, ^N, ^X^I or ^X^D we might want to add-expand a single-char-word
- // (such as M in M'exico) if not tried already. -- Acevedo
- if (compl_length > 1
- || (compl_cont_status & CONT_ADDING)
- || (ctrl_x_mode_not_default()
- && !ctrl_x_mode_path_patterns()
- && !ctrl_x_mode_path_defines())) {
- compl_cont_status &= ~CONT_N_ADDS;
- }
- }
-
- if (compl_curr_match->cp_flags & CP_CONT_S_IPOS) {
- compl_cont_status |= CONT_S_IPOS;
- } else {
- compl_cont_status &= ~CONT_S_IPOS;
}
if (edit_submode_extra == NULL) {
- if (compl_curr_match->cp_flags & CP_ORIGINAL_TEXT) {
- edit_submode_extra = (char_u *)_("Back at original");
+ if (match_at_original_text(compl_curr_match)) {
+ edit_submode_extra = _("Back at original");
edit_submode_highl = HLF_W;
} else if (compl_cont_status & CONT_S_IPOS) {
- edit_submode_extra = (char_u *)_("Word from other line");
+ edit_submode_extra = _("Word from other line");
edit_submode_highl = HLF_COUNT;
} else if (compl_curr_match->cp_next == compl_curr_match->cp_prev) {
- edit_submode_extra = (char_u *)_("The only match");
+ edit_submode_extra = _("The only match");
edit_submode_highl = HLF_COUNT;
compl_curr_match->cp_number = 1;
} else {
@@ -3827,14 +4278,14 @@ int ins_complete(int c, bool enable_pum)
if (compl_curr_match->cp_number != -1) {
// Space for 10 text chars. + 2x10-digit no.s = 31.
// Translations may need more than twice that.
- static char_u match_ref[81];
+ static char match_ref[81];
if (compl_matches > 0) {
- vim_snprintf((char *)match_ref, sizeof(match_ref),
+ vim_snprintf(match_ref, sizeof(match_ref),
_("match %d of %d"),
compl_curr_match->cp_number, compl_matches);
} else {
- vim_snprintf((char *)match_ref, sizeof(match_ref),
+ vim_snprintf(match_ref, sizeof(match_ref),
_("match %d"),
compl_curr_match->cp_number);
}
@@ -3862,6 +4313,72 @@ int ins_complete(int c, bool enable_pum)
msg_clr_cmdline(); // necessary for "noshowmode"
}
}
+}
+
+/// Do Insert mode completion.
+/// Called when character "c" was typed, which has a meaning for completion.
+/// Returns OK if completion was done, FAIL if something failed.
+int ins_complete(int c, bool enable_pum)
+{
+ int n;
+ int save_w_wrow;
+ int save_w_leftcol;
+ int insert_match;
+
+ compl_direction = ins_compl_key2dir(c);
+ insert_match = ins_compl_use_match(c);
+
+ if (!compl_started) {
+ if (ins_compl_start() == FAIL) {
+ return FAIL;
+ }
+ } else if (insert_match && stop_arrow() == FAIL) {
+ return FAIL;
+ }
+
+ compl_shown_match = compl_curr_match;
+ compl_shows_dir = compl_direction;
+
+ // Find next match (and following matches).
+ save_w_wrow = curwin->w_wrow;
+ 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;
+ }
+ compl_curr_match = compl_shown_match;
+ compl_direction = compl_shows_dir;
+
+ // Eat the ESC that vgetc() returns after a CTRL-C to avoid leaving Insert
+ // mode.
+ if (got_int && !global_busy) {
+ (void)vgetc();
+ got_int = false;
+ }
+
+ // we found no match if the list has only the "compl_orig_text"-entry
+ if (is_first_match(compl_first_match->cp_next)) {
+ // remove N_ADDS flag, so next ^X<> won't try to go to ADDING mode,
+ // because we couldn't expand anything at first place, but if we used
+ // ^P, ^N, ^X^I or ^X^D we might want to add-expand a single-char-word
+ // (such as M in M'exico) if not tried already. -- Acevedo
+ if (compl_length > 1
+ || compl_status_adding()
+ || (ctrl_x_mode_not_default()
+ && !ctrl_x_mode_path_patterns()
+ && !ctrl_x_mode_path_defines())) {
+ compl_cont_status &= ~CONT_N_ADDS;
+ }
+ }
+
+ if (compl_curr_match->cp_flags & CP_CONT_S_IPOS) {
+ compl_cont_status |= CONT_S_IPOS;
+ } else {
+ compl_cont_status &= ~CONT_S_IPOS;
+ }
+
+ ins_compl_show_statusmsg();
// Show the popup menu, unless we got interrupted.
if (enable_pum && !compl_interrupted) {
@@ -3873,6 +4390,7 @@ int ins_complete(int c, bool enable_pum)
return OK;
}
+/// Remove (if needed) and show the popup menu
static void show_pum(int prev_w_wrow, int prev_w_leftcol)
{
// RedrawingDisabled may be set when invoked through complete().
@@ -3895,7 +4413,7 @@ static void show_pum(int prev_w_wrow, int prev_w_leftcol)
// If dest is not NULL the chars. are copied there quoting (with
// a backslash) the metachars, and dest would be NUL terminated.
// Returns the length (needed) of dest
-static unsigned quote_meta(char_u *dest, char_u *src, int len)
+static unsigned quote_meta(char *dest, char *src, int len)
{
unsigned m = (unsigned)len + 1; // one extra for the NUL
@@ -3909,7 +4427,7 @@ static unsigned quote_meta(char_u *dest, char_u *src, int len)
}
FALLTHROUGH;
case '~':
- if (!p_magic) { // quote these only if magic is set
+ if (!magic_isset()) { // quote these only if magic is set
break;
}
FALLTHROUGH;
@@ -3930,7 +4448,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((char *)src) - 1;
+ const int mb_len = utfc_ptr2len(src) - 1;
if (mb_len > 0 && len >= mb_len) {
for (int i = 0; i < mb_len; i++) {
len--;
@@ -3952,6 +4470,9 @@ static unsigned quote_meta(char_u *dest, char_u *src, int len)
void free_insexpand_stuff(void)
{
XFREE_CLEAR(compl_orig_text);
+ callback_free(&cfu_cb);
+ callback_free(&ofu_cb);
+ callback_free(&tsrfu_cb);
}
#endif
diff --git a/src/nvim/insexpand.h b/src/nvim/insexpand.h
index 8e183455ca..83ba14e0d2 100644
--- a/src/nvim/insexpand.h
+++ b/src/nvim/insexpand.h
@@ -1,15 +1,10 @@
#ifndef NVIM_INSEXPAND_H
#define NVIM_INSEXPAND_H
-#include "nvim/vim.h"
+#include <stdbool.h>
-/// state for pum_ext_select_item.
-EXTERN struct {
- bool active;
- int item;
- bool insert;
- bool finish;
-} pum_want;
+#include "nvim/macros.h"
+#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "insexpand.h.generated.h"
diff --git a/src/nvim/keycodes.c b/src/nvim/keycodes.c
index 5a5257efb2..e19806e464 100644
--- a/src/nvim/keycodes.c
+++ b/src/nvim/keycodes.c
@@ -4,16 +4,25 @@
#include <assert.h>
#include <inttypes.h>
#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
#include "nvim/ascii.h"
#include "nvim/charset.h"
-#include "nvim/edit.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/eval/vars.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
#include "nvim/keycodes.h"
+#include "nvim/log.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/mouse.h"
#include "nvim/strings.h"
+#include "nvim/types.h"
#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -46,8 +55,7 @@ static const struct modmasktable {
#define MOD_KEYS_ENTRY_SIZE 5
-static char_u modifier_keys_table[] =
-{
+static uint8_t modifier_keys_table[] = {
// mod mask with modifier without modifier
MOD_MASK_SHIFT, '&', '9', '@', '1', // begin
MOD_MASK_SHIFT, '&', '0', '@', '2', // cancel
@@ -57,15 +65,15 @@ static char_u modifier_keys_table[] =
MOD_MASK_SHIFT, '*', '4', 'k', 'D', // delete char
MOD_MASK_SHIFT, '*', '5', 'k', 'L', // delete line
MOD_MASK_SHIFT, '*', '7', '@', '7', // end
- MOD_MASK_CTRL, KS_EXTRA, (int)KE_C_END, '@', '7', // end
+ MOD_MASK_CTRL, KS_EXTRA, KE_C_END, '@', '7', // end
MOD_MASK_SHIFT, '*', '9', '@', '9', // exit
MOD_MASK_SHIFT, '*', '0', '@', '0', // find
MOD_MASK_SHIFT, '#', '1', '%', '1', // help
MOD_MASK_SHIFT, '#', '2', 'k', 'h', // home
- MOD_MASK_CTRL, KS_EXTRA, (int)KE_C_HOME, 'k', 'h', // home
+ MOD_MASK_CTRL, KS_EXTRA, KE_C_HOME, 'k', 'h', // home
MOD_MASK_SHIFT, '#', '3', 'k', 'I', // insert
MOD_MASK_SHIFT, '#', '4', 'k', 'l', // left arrow
- MOD_MASK_CTRL, KS_EXTRA, (int)KE_C_LEFT, 'k', 'l', // left arrow
+ MOD_MASK_CTRL, KS_EXTRA, KE_C_LEFT, 'k', 'l', // left arrow
MOD_MASK_SHIFT, '%', 'a', '%', '3', // message
MOD_MASK_SHIFT, '%', 'b', '%', '4', // move
MOD_MASK_SHIFT, '%', 'c', '%', '5', // next
@@ -75,63 +83,63 @@ static char_u modifier_keys_table[] =
MOD_MASK_SHIFT, '%', 'g', '%', '0', // redo
MOD_MASK_SHIFT, '%', 'h', '&', '3', // replace
MOD_MASK_SHIFT, '%', 'i', 'k', 'r', // right arr.
- MOD_MASK_CTRL, KS_EXTRA, (int)KE_C_RIGHT, 'k', 'r', // right arr.
+ MOD_MASK_CTRL, KS_EXTRA, KE_C_RIGHT, 'k', 'r', // right arr.
MOD_MASK_SHIFT, '%', 'j', '&', '5', // resume
MOD_MASK_SHIFT, '!', '1', '&', '6', // save
MOD_MASK_SHIFT, '!', '2', '&', '7', // suspend
MOD_MASK_SHIFT, '!', '3', '&', '8', // undo
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_UP, 'k', 'u', // up arrow
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_DOWN, 'k', 'd', // down arrow
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_UP, 'k', 'u', // up arrow
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_DOWN, 'k', 'd', // down arrow
// vt100 F1
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_XF1, KS_EXTRA, (int)KE_XF1,
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_XF2, KS_EXTRA, (int)KE_XF2,
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_XF3, KS_EXTRA, (int)KE_XF3,
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_XF4, KS_EXTRA, (int)KE_XF4,
-
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F1, 'k', '1', // F1
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F2, 'k', '2',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F3, 'k', '3',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F4, 'k', '4',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F5, 'k', '5',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F6, 'k', '6',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F7, 'k', '7',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F8, 'k', '8',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F9, 'k', '9',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F10, 'k', ';', // F10
-
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F11, 'F', '1',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F12, 'F', '2',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F13, 'F', '3',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F14, 'F', '4',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F15, 'F', '5',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F16, 'F', '6',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F17, 'F', '7',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F18, 'F', '8',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F19, 'F', '9',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F20, 'F', 'A',
-
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F21, 'F', 'B',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F22, 'F', 'C',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F23, 'F', 'D',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F24, 'F', 'E',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F25, 'F', 'F',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F26, 'F', 'G',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F27, 'F', 'H',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F28, 'F', 'I',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F29, 'F', 'J',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F30, 'F', 'K',
-
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F31, 'F', 'L',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F32, 'F', 'M',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F33, 'F', 'N',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F34, 'F', 'O',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F35, 'F', 'P',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F36, 'F', 'Q',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F37, 'F', 'R',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_XF1, KS_EXTRA, KE_XF1,
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_XF2, KS_EXTRA, KE_XF2,
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_XF3, KS_EXTRA, KE_XF3,
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_XF4, KS_EXTRA, KE_XF4,
+
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F1, 'k', '1', // F1
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F2, 'k', '2',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F3, 'k', '3',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F4, 'k', '4',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F5, 'k', '5',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F6, 'k', '6',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F7, 'k', '7',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F8, 'k', '8',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F9, 'k', '9',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F10, 'k', ';', // F10
+
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F11, 'F', '1',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F12, 'F', '2',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F13, 'F', '3',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F14, 'F', '4',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F15, 'F', '5',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F16, 'F', '6',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F17, 'F', '7',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F18, 'F', '8',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F19, 'F', '9',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F20, 'F', 'A',
+
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F21, 'F', 'B',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F22, 'F', 'C',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F23, 'F', 'D',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F24, 'F', 'E',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F25, 'F', 'F',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F26, 'F', 'G',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F27, 'F', 'H',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F28, 'F', 'I',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F29, 'F', 'J',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F30, 'F', 'K',
+
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F31, 'F', 'L',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F32, 'F', 'M',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F33, 'F', 'N',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F34, 'F', 'O',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F35, 'F', 'P',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F36, 'F', 'Q',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F37, 'F', 'R',
// TAB pseudo code
- MOD_MASK_SHIFT, 'k', 'B', KS_EXTRA, (int)KE_TAB,
+ MOD_MASK_SHIFT, 'k', 'B', KS_EXTRA, KE_TAB,
NUL
};
@@ -347,28 +355,27 @@ static struct mousetable {
int button; // Which mouse button is it?
bool is_click; // Is it a mouse button click event?
bool is_drag; // Is it a mouse drag event?
-} mouse_table[] =
-{
- { (int)KE_LEFTMOUSE, MOUSE_LEFT, true, false },
- { (int)KE_LEFTDRAG, MOUSE_LEFT, false, true },
- { (int)KE_LEFTRELEASE, MOUSE_LEFT, false, false },
- { (int)KE_MIDDLEMOUSE, MOUSE_MIDDLE, true, false },
- { (int)KE_MIDDLEDRAG, MOUSE_MIDDLE, false, true },
- { (int)KE_MIDDLERELEASE, MOUSE_MIDDLE, false, false },
- { (int)KE_RIGHTMOUSE, MOUSE_RIGHT, true, false },
- { (int)KE_RIGHTDRAG, MOUSE_RIGHT, false, true },
- { (int)KE_RIGHTRELEASE, MOUSE_RIGHT, false, false },
- { (int)KE_X1MOUSE, MOUSE_X1, true, false },
- { (int)KE_X1DRAG, MOUSE_X1, false, true },
- { (int)KE_X1RELEASE, MOUSE_X1, false, false },
- { (int)KE_X2MOUSE, MOUSE_X2, true, false },
- { (int)KE_X2DRAG, MOUSE_X2, false, true },
- { (int)KE_X2RELEASE, MOUSE_X2, false, false },
+} mouse_table[] = {
+ { KE_LEFTMOUSE, MOUSE_LEFT, true, false },
+ { KE_LEFTDRAG, MOUSE_LEFT, false, true },
+ { KE_LEFTRELEASE, MOUSE_LEFT, false, false },
+ { KE_MIDDLEMOUSE, MOUSE_MIDDLE, true, false },
+ { KE_MIDDLEDRAG, MOUSE_MIDDLE, false, true },
+ { KE_MIDDLERELEASE, MOUSE_MIDDLE, false, false },
+ { KE_RIGHTMOUSE, MOUSE_RIGHT, true, false },
+ { KE_RIGHTDRAG, MOUSE_RIGHT, false, true },
+ { KE_RIGHTRELEASE, MOUSE_RIGHT, false, false },
+ { KE_X1MOUSE, MOUSE_X1, true, false },
+ { KE_X1DRAG, MOUSE_X1, false, true },
+ { KE_X1RELEASE, MOUSE_X1, false, false },
+ { KE_X2MOUSE, MOUSE_X2, true, false },
+ { KE_X2DRAG, MOUSE_X2, false, true },
+ { KE_X2RELEASE, MOUSE_X2, false, false },
// DRAG without CLICK
- { (int)KE_MOUSEMOVE, MOUSE_RELEASE, false, true },
+ { KE_MOUSEMOVE, MOUSE_RELEASE, false, true },
// RELEASE without CLICK
- { (int)KE_IGNORE, MOUSE_RELEASE, false, false },
- { 0, 0, 0, 0 },
+ { KE_IGNORE, MOUSE_RELEASE, false, false },
+ { 0, 0, 0, 0 },
};
/// Return the modifier mask bit (#MOD_MASK_*) corresponding to mod name
@@ -396,22 +403,24 @@ int name_to_mod_mask(int c)
int simplify_key(const int key, int *modifiers)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
- if (*modifiers & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT)) {
- // TAB is a special case.
- if (key == TAB && (*modifiers & MOD_MASK_SHIFT)) {
- *modifiers &= ~MOD_MASK_SHIFT;
- return K_S_TAB;
- }
- const int key0 = KEY2TERMCAP0(key);
- const int key1 = KEY2TERMCAP1(key);
- for (int i = 0; modifier_keys_table[i] != NUL; i += MOD_KEYS_ENTRY_SIZE) {
- if (key0 == modifier_keys_table[i + 3]
- && key1 == modifier_keys_table[i + 4]
- && (*modifiers & modifier_keys_table[i])) {
- *modifiers &= ~modifier_keys_table[i];
- return TERMCAP2KEY(modifier_keys_table[i + 1],
- modifier_keys_table[i + 2]);
- }
+ if (!(*modifiers & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT))) {
+ return key;
+ }
+
+ // TAB is a special case.
+ if (key == TAB && (*modifiers & MOD_MASK_SHIFT)) {
+ *modifiers &= ~MOD_MASK_SHIFT;
+ return K_S_TAB;
+ }
+ const int key0 = KEY2TERMCAP0(key);
+ const int key1 = KEY2TERMCAP1(key);
+ for (int i = 0; modifier_keys_table[i] != NUL; i += MOD_KEYS_ENTRY_SIZE) {
+ if (key0 == modifier_keys_table[i + 3]
+ && key1 == modifier_keys_table[i + 4]
+ && (*modifiers & modifier_keys_table[i])) {
+ *modifiers &= ~modifier_keys_table[i];
+ return TERMCAP2KEY(modifier_keys_table[i + 1],
+ modifier_keys_table[i + 2]);
}
}
return key;
@@ -465,7 +474,7 @@ char_u *get_special_key_name(int c, int modifiers)
int i, idx;
int table_idx;
- char_u *s;
+ char *s;
string[0] = '<';
idx = 1;
@@ -534,12 +543,12 @@ char_u *get_special_key_name(int c, int modifiers)
} else {
s = transchar(c);
while (*s) {
- string[idx++] = *s++;
+ string[idx++] = (uint8_t)(*s++);
}
}
}
} else { // use name of special key
- size_t len = STRLEN(key_names_table[table_idx].name);
+ size_t len = strlen(key_names_table[table_idx].name);
if ((int)len + idx + 2 <= MAX_KEY_NAME_LEN) {
STRCPY(string + idx, key_names_table[table_idx].name);
@@ -563,7 +572,7 @@ char_u *get_special_key_name(int c, int modifiers)
/// @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 **const srcp, const size_t src_len, char_u *const dst,
+unsigned int trans_special(const char **const srcp, const size_t src_len, char *const dst,
const int flags, const bool escape_ks, bool *const did_simplify)
FUNC_ATTR_NONNULL_ARG(1, 3) FUNC_ATTR_WARN_UNUSED_RESULT
{
@@ -573,7 +582,7 @@ unsigned int trans_special(const char_u **const srcp, const size_t src_len, char
return 0;
}
- return special_to_buf(key, modifiers, escape_ks, dst);
+ return special_to_buf(key, modifiers, escape_ks, (char_u *)dst);
}
/// Put the character sequence for "key" with "modifiers" into "dst" and return
@@ -616,15 +625,15 @@ unsigned int special_to_buf(int key, int modifiers, bool escape_ks, char_u *dst)
/// @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 **const srcp, const size_t src_len, int *const modp,
+int find_special_key(const char **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 char *last_dash;
+ const char *end_of_name;
+ const char *src;
+ const char *bp;
+ const char *const end = *srcp + src_len - 1;
const bool in_string = flags & FSK_IN_STRING;
int modifiers;
int bit;
@@ -682,7 +691,7 @@ int find_special_key(const char_u **const srcp, const size_t src_len, int *const
modifiers = 0x0;
for (bp = src + 1; bp < last_dash; bp++) {
if (*bp != '-') {
- bit = name_to_mod_mask(*bp);
+ bit = name_to_mod_mask((uint8_t)(*bp));
if (bit == 0x0) {
break; // Illegal modifier name
}
@@ -709,12 +718,12 @@ int find_special_key(const char_u **const srcp, const size_t src_len, int *const
// Special case for a double-quoted string
off = l = 2;
} else {
- l = utfc_ptr2len((char *)last_dash + 1);
+ l = utfc_ptr2len(last_dash + 1);
}
if (modifiers != 0 && last_dash[l + 1] == '>') {
- key = utf_ptr2char((char *)last_dash + off);
+ key = utf_ptr2char(last_dash + off);
} else {
- key = get_special_key_code(last_dash + off);
+ key = get_special_key_code((char_u *)last_dash + off);
if (!(flags & FSK_KEEP_X_KEY)) {
key = handle_x_keys(key);
}
@@ -861,10 +870,9 @@ int get_mouse_button(int code, bool *is_click, bool *is_drag)
/// @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).
-/// 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.
+/// If `*bufp` is non-NULL, it will be used directly,
+/// and is assumed to be 128 bytes long (enough for transcoding LHS of mapping),
+/// and 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
@@ -872,18 +880,18 @@ int get_mouse_button(int code, bool *is_click, bool *is_drag)
/// @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, which is also saved to "bufp".
+/// @return The same as what `*bufp` is set to.
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;
+ char key;
size_t dlen = 0;
- const char_u *src;
- const char_u *const end = (char_u *)from + from_len - 1;
- char_u *result; // buffer for resulting string
+ const char *src;
+ const char *const end = from + from_len - 1;
+ char *result; // buffer for resulting string
const bool do_backslash = !(cpo_flags & FLAG_CPO_BSLASH); // backslash is a special character
const bool do_special = !(flags & REPTERM_NO_SPECIAL);
@@ -895,12 +903,12 @@ char *replace_termcodes(const char *const from, const size_t from_len, char **co
const size_t buf_len = allocated ? from_len * 6 + 1 : 128;
result = allocated ? xmalloc(buf_len) : *bufp;
- src = (char_u *)from;
+ src = from;
// Check for #n at start only: function key n
if ((flags & REPTERM_FROM_PART) && from_len > 1 && src[0] == '#'
&& ascii_isdigit(src[1])) { // function key
- result[dlen++] = K_SPECIAL;
+ result[dlen++] = (char)K_SPECIAL;
result[dlen++] = 'k';
if (src[1] == '0') {
result[dlen++] = ';'; // #0 is F10 is "k;"
@@ -917,7 +925,7 @@ char *replace_termcodes(const char *const from, const size_t from_len, char **co
}
// Check for special <> keycodes, like "<C-S-LeftMouse>"
if (do_special && ((flags & REPTERM_DO_LT) || ((end - src) >= 3
- && STRNCMP(src, "<lt>", 4) != 0))) {
+ && 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) {
@@ -925,12 +933,12 @@ char *replace_termcodes(const char *const from, const size_t from_len, char **co
emsg(_(e_usingsid));
} else {
src += 5;
- result[dlen++] = K_SPECIAL;
- result[dlen++] = (int)KS_EXTRA;
- result[dlen++] = (int)KE_SNR;
- snprintf((char *)result + dlen, buf_len - dlen, "%" PRId64,
+ result[dlen++] = (char)K_SPECIAL;
+ result[dlen++] = (char)KS_EXTRA;
+ result[dlen++] = KE_SNR;
+ snprintf(result + dlen, buf_len - dlen, "%" PRId64,
(int64_t)current_sctx.sc_sid);
- dlen += STRLEN(result + dlen);
+ dlen += strlen(result + dlen);
result[dlen++] = '_';
continue;
}
@@ -946,7 +954,8 @@ char *replace_termcodes(const char *const from, const size_t from_len, char **co
}
if (do_special) {
- char_u *p, *s, len;
+ char *p, *s;
+ int len;
// Replace <Leader> by the value of "mapleader".
// Replace <LocalLeader> by the value of "maplocalleader".
@@ -964,8 +973,8 @@ char *replace_termcodes(const char *const from, const size_t from_len, char **co
if (len != 0) {
// Allow up to 8 * 6 characters for "mapleader".
- if (p == NULL || *p == NUL || STRLEN(p) > 8 * 6) {
- s = (char_u *)"\\";
+ if (p == NULL || *p == NUL || strlen(p) > 8 * 6) {
+ s = "\\";
} else {
s = p;
}
@@ -993,12 +1002,12 @@ char *replace_termcodes(const char *const from, const size_t from_len, char **co
}
// skip multibyte char correctly
- for (i = utfc_ptr2len_len(src, (int)(end - src) + 1); i > 0; i--) {
+ for (i = utfc_ptr2len_len((char *)src, (int)(end - src) + 1); i > 0; i--) {
// If the character is K_SPECIAL, replace it with K_SPECIAL
// KS_SPECIAL KE_FILLER.
- if (*src == K_SPECIAL) {
- result[dlen++] = K_SPECIAL;
- result[dlen++] = KS_SPECIAL;
+ if (*src == (char)K_SPECIAL) {
+ result[dlen++] = (char)K_SPECIAL;
+ result[dlen++] = (char)KS_SPECIAL;
result[dlen++] = KE_FILLER;
} else {
result[dlen++] = *src;
@@ -1050,8 +1059,8 @@ 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;
+ char *res = xmalloc(strlen(p) * 4 + 1);
+ char_u *d = (char_u *)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.
@@ -1067,7 +1076,7 @@ char *vim_strsave_escape_ks(char *p)
}
*d = NUL;
- return (char *)res;
+ return res;
}
/// Remove escaping from K_SPECIAL characters. Reverse of
diff --git a/src/nvim/keycodes.h b/src/nvim/keycodes.h
index c4d984ee17..7842dee92c 100644
--- a/src/nvim/keycodes.h
+++ b/src/nvim/keycodes.h
@@ -1,6 +1,10 @@
#ifndef NVIM_KEYCODES_H
#define NVIM_KEYCODES_H
+#include <stddef.h>
+
+#include "nvim/ascii.h"
+#include "nvim/option_defs.h"
#include "nvim/strings.h"
// Keycode definitions for special keys.
diff --git a/src/nvim/lib/ringbuf.h b/src/nvim/lib/ringbuf.h
index 4fd110a531..907018a06e 100644
--- a/src/nvim/lib/ringbuf.h
+++ b/src/nvim/lib/ringbuf.h
@@ -25,14 +25,9 @@
#define _RINGBUF_LENGTH(rb) \
((rb)->first == NULL ? 0 \
- : ((rb)->next == (rb)->first) ? (size_t)((rb)->buf_end - (rb)->buf) + 1 \
- : ((rb)->next > \
- (rb)->first) ? (size_t)((rb)->next - \
- (rb)->first) \
- : (size_t)((rb)-> \
- next - (rb)->buf + \
- (rb)->buf_end - \
- (rb)->first + 1))
+ : ((rb)->next == (rb)->first) ? (size_t)((rb)->buf_end - (rb)->buf) + 1 \
+ : ((rb)->next > (rb)->first) ? (size_t)((rb)->next - (rb)->first) \
+ : (size_t)((rb)->next - (rb)->buf + (rb)->buf_end - (rb)->first + 1))
#define _RINGBUF_NEXT(rb, var) \
((var) == (rb)->buf_end ? (rb)->buf : (var) + 1)
diff --git a/src/nvim/linematch.c b/src/nvim/linematch.c
new file mode 100644
index 0000000000..a9dac40731
--- /dev/null
+++ b/src/nvim/linematch.c
@@ -0,0 +1,384 @@
+// 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 <stddef.h>
+#include <string.h>
+
+#include "nvim/linematch.h"
+#include "nvim/macros.h"
+#include "nvim/memory.h"
+
+// struct for running the diff linematch algorithm
+typedef struct {
+ int *df_decision; // to keep track of this path traveled
+ int df_lev_score; // to keep track of the total score of this path
+ size_t df_path_idx; // current index of this path
+} diffcmppath_T;
+
+#define LN_MAX_BUFS 8
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "linematch.c.generated.h"
+#endif
+
+static size_t line_len(const char *s)
+{
+ char *end = strchr(s, '\n');
+ if (end) {
+ return (size_t)(end - s);
+ }
+ return strlen(s);
+}
+
+/// Same as matching_chars but ignore whitespace
+///
+/// @param s1
+/// @param s2
+static int matching_chars_iwhite(const char *s1, const char *s2)
+{
+ // the newly processed strings that will be compared
+ // delete the white space characters, and/or replace all upper case with lower
+ char *strsproc[2];
+ const char *strsorig[2] = { s1, s2 };
+ for (int k = 0; k < 2; k++) {
+ size_t d = 0;
+ size_t i = 0;
+ size_t slen = line_len(strsorig[k]);
+ strsproc[k] = xmalloc((slen + 1) * sizeof(char));
+ while (d + i < slen) {
+ char e = strsorig[k][i + d];
+ if (e != ' ' && e != '\t') {
+ strsproc[k][i] = e;
+ i++;
+ } else {
+ d++;
+ }
+ }
+ strsproc[k][i] = '\0';
+ }
+ int matching = matching_chars(strsproc[0], strsproc[1]);
+ xfree(strsproc[0]);
+ xfree(strsproc[1]);
+ return matching;
+}
+
+/// update the path of a point in the diff linematch algorithm
+/// @param diffcmppath
+/// @param score
+/// @param to
+/// @param from
+/// @param choice
+static void update_path_flat(diffcmppath_T *diffcmppath, int score, size_t to, size_t from,
+ const int choice)
+{
+ size_t path_idx = diffcmppath[from].df_path_idx;
+
+ for (size_t k = 0; k < path_idx; k++) {
+ diffcmppath[to].df_decision[k] = diffcmppath[from].df_decision[k];
+ }
+
+ diffcmppath[to].df_decision[path_idx] = choice;
+ diffcmppath[to].df_lev_score = score;
+ diffcmppath[to].df_path_idx = path_idx + 1;
+}
+
+#define MATCH_CHAR_MAX_LEN 800
+
+/// Return matching characters between "s1" and "s2" whilst respecting sequence order.
+/// Consider the case of two strings 'AAACCC' and 'CCCAAA', the
+/// return value from this function will be 3, either to match
+/// the 3 C's, or the 3 A's.
+///
+/// Examples:
+/// matching_chars("aabc", "acba") -> 2 // 'a' and 'b' in common
+/// matching_chars("123hello567", "he123ll567o") -> 8 // '123', 'll' and '567' in common
+/// matching_chars("abcdefg", "gfedcba") -> 1 // all characters in common,
+/// // but only at most 1 in sequence
+///
+/// @param s1
+/// @param s2
+static int matching_chars(const char *s1, const char *s2)
+{
+ size_t s1len = MIN(MATCH_CHAR_MAX_LEN - 1, line_len(s1));
+ size_t s2len = MIN(MATCH_CHAR_MAX_LEN - 1, line_len(s2));
+ int matrix[2][MATCH_CHAR_MAX_LEN] = { 0 };
+ bool icur = 1; // save space by storing only two rows for i axis
+ for (size_t i = 0; i < s1len; i++) {
+ icur = !icur;
+ int *e1 = matrix[icur];
+ int *e2 = matrix[!icur];
+ for (size_t j = 0; j < s2len; j++) {
+ // skip char in s1
+ if (e2[j + 1] > e1[j + 1]) {
+ e1[j + 1] = e2[j + 1];
+ }
+ // skip char in s2
+ if (e1[j] > e1[j + 1]) {
+ e1[j + 1] = e1[j];
+ }
+ // compare char in s1 and s2
+ if ((s1[i] == s2[j]) && (e2[j] + 1) > e1[j + 1]) {
+ e1[j + 1] = e2[j] + 1;
+ }
+ }
+ }
+ return matrix[icur][s2len];
+}
+
+/// count the matching characters between a variable number of strings "sp"
+/// mark the strings that have already been compared to extract them later
+/// without re-running the character match counting.
+/// @param sp
+/// @param fomvals
+/// @param n
+static int count_n_matched_chars(const char **sp, const size_t n, bool iwhite)
+{
+ int matched_chars = 0;
+ int matched = 0;
+ for (size_t i = 0; i < n; i++) {
+ for (size_t j = i + 1; j < n; j++) {
+ if (sp[i] != NULL && sp[j] != NULL) {
+ matched++;
+ // TODO(lewis6991): handle whitespace ignoring higher up in the stack
+ matched_chars += iwhite ? matching_chars_iwhite(sp[i], sp[j])
+ : matching_chars(sp[i], sp[j]);
+ }
+ }
+ }
+
+ // prioritize a match of 3 (or more lines) equally to a match of 2 lines
+ if (matched >= 2) {
+ matched_chars *= 2;
+ matched_chars /= matched;
+ }
+
+ return matched_chars;
+}
+
+void fastforward_buf_to_lnum(const char **s, long lnum)
+{
+ for (long i = 0; i < lnum - 1; i++) {
+ *s = strchr(*s, '\n');
+ (*s)++;
+ }
+}
+
+/// try all the different ways to compare these lines and use the one that
+/// results in the most matching characters
+/// @param df_iters
+/// @param paths
+/// @param npaths
+/// @param path_idx
+/// @param choice
+/// @param diffcmppath
+/// @param diff_len
+/// @param ndiffs
+/// @param diff_blk
+static void try_possible_paths(const int *df_iters, const size_t *paths, const int npaths,
+ const int path_idx, int *choice, diffcmppath_T *diffcmppath,
+ const int *diff_len, const size_t ndiffs, const char **diff_blk,
+ bool iwhite)
+{
+ if (path_idx == npaths) {
+ if ((*choice) > 0) {
+ int from_vals[LN_MAX_BUFS];
+ const int *to_vals = df_iters;
+ const char *current_lines[LN_MAX_BUFS];
+ for (size_t k = 0; k < ndiffs; k++) {
+ from_vals[k] = df_iters[k];
+ // get the index at all of the places
+ if ((*choice) & (1 << k)) {
+ from_vals[k]--;
+ const char *p = diff_blk[k];
+ fastforward_buf_to_lnum(&p, df_iters[k]);
+ current_lines[k] = p;
+ } else {
+ current_lines[k] = NULL;
+ }
+ }
+ size_t unwrapped_idx_from = unwrap_indexes(from_vals, diff_len, ndiffs);
+ size_t unwrapped_idx_to = unwrap_indexes(to_vals, diff_len, ndiffs);
+ int matched_chars = count_n_matched_chars(current_lines, ndiffs, iwhite);
+ int score = diffcmppath[unwrapped_idx_from].df_lev_score + matched_chars;
+ if (score > diffcmppath[unwrapped_idx_to].df_lev_score) {
+ update_path_flat(diffcmppath, score, unwrapped_idx_to, unwrapped_idx_from, *choice);
+ }
+ } else {
+ // initialize the 0, 0, 0 ... choice
+ size_t i = 0;
+ while (i < ndiffs && df_iters[i] == 0) {
+ i++;
+ if (i == ndiffs) {
+ diffcmppath[0].df_lev_score = 0;
+ diffcmppath[0].df_path_idx = 0;
+ }
+ }
+ }
+ return;
+ }
+ size_t bit_place = paths[path_idx];
+ *(choice) |= (1 << bit_place); // set it to 1
+ try_possible_paths(df_iters, paths, npaths, path_idx + 1, choice,
+ diffcmppath, diff_len, ndiffs, diff_blk, iwhite);
+ *(choice) &= ~(1 << bit_place); // set it to 0
+ try_possible_paths(df_iters, paths, npaths, path_idx + 1, choice,
+ diffcmppath, diff_len, ndiffs, diff_blk, iwhite);
+}
+
+/// unwrap indexes to access n dimensional tensor
+/// @param values
+/// @param diff_len
+/// @param ndiffs
+static size_t unwrap_indexes(const int *values, const int *diff_len, const size_t ndiffs)
+{
+ size_t num_unwrap_scalar = 1;
+ for (size_t k = 0; k < ndiffs; k++) {
+ num_unwrap_scalar *= (size_t)diff_len[k] + 1;
+ }
+
+ size_t path_idx = 0;
+ for (size_t k = 0; k < ndiffs; k++) {
+ num_unwrap_scalar /= (size_t)diff_len[k] + 1;
+
+ // (k == 0) space optimization
+ int n = k == 0 ? values[k] % 2 : values[k];
+ path_idx += num_unwrap_scalar * (size_t)n;
+ }
+ return path_idx;
+}
+
+/// populate the values of the linematch algorithm tensor, and find the best
+/// decision for how to compare the relevant lines from each of the buffers at
+/// each point in the tensor
+/// @param df_iters
+/// @param ch_dim
+/// @param diffcmppath
+/// @param diff_len
+/// @param ndiffs
+/// @param diff_blk
+static void populate_tensor(int *df_iters, const size_t ch_dim, diffcmppath_T *diffcmppath,
+ const int *diff_len, const size_t ndiffs, const char **diff_blk,
+ bool iwhite)
+{
+ if (ch_dim == ndiffs) {
+ int npaths = 0;
+ size_t paths[LN_MAX_BUFS];
+
+ for (size_t j = 0; j < ndiffs; j++) {
+ if (df_iters[j] > 0) {
+ paths[npaths] = j;
+ npaths++;
+ }
+ }
+ int choice = 0;
+ size_t unwrapper_idx_to = unwrap_indexes(df_iters, diff_len, ndiffs);
+ diffcmppath[unwrapper_idx_to].df_lev_score = -1;
+ try_possible_paths(df_iters, paths, npaths, 0, &choice, diffcmppath,
+ diff_len, ndiffs, diff_blk, iwhite);
+ return;
+ }
+
+ for (int i = 0; i <= diff_len[ch_dim]; i++) {
+ df_iters[ch_dim] = i;
+ populate_tensor(df_iters, ch_dim + 1, diffcmppath, diff_len,
+ ndiffs, diff_blk, iwhite);
+ }
+}
+
+/// algorithm to find an optimal alignment of lines of a diff block with 2 or
+/// more files. The algorithm is generalized to work for any number of files
+/// which corresponds to another dimension added to the tensor used in the
+/// algorithm
+///
+/// for questions and information about the linematch algorithm please contact
+/// Jonathon White (jonathonwhite@protonmail.com)
+///
+/// for explanation, a summary of the algorithm in 3 dimensions (3 files
+/// compared) follows
+///
+/// The 3d case (for 3 buffers) of the algorithm implemented when diffopt
+/// 'linematch' is enabled. The algorithm constructs a 3d tensor to
+/// compare a diff between 3 buffers. The dimensions of the tensor are
+/// the length of the diff in each buffer plus 1 A path is constructed by
+/// moving from one edge of the cube/3d tensor to the opposite edge.
+/// Motions from one cell of the cube to the next represent decisions. In
+/// a 3d cube, there are a total of 7 decisions that can be made,
+/// represented by the enum df_path3_choice which is defined in
+/// buffer_defs.h a comparison of buffer 0 and 1 represents a motion
+/// toward the opposite edge of the cube with components along the 0 and
+/// 1 axes. a comparison of buffer 0, 1, and 2 represents a motion
+/// toward the opposite edge of the cube with components along the 0, 1,
+/// and 2 axes. A skip of buffer 0 represents a motion along only the 0
+/// axis. For each action, a point value is awarded, and the path is
+/// saved for reference later, if it is found to have been the optimal
+/// path. The optimal path has the highest score. The score is
+/// calculated as the summation of the total characters matching between
+/// all of the lines which were compared. The structure of the algorithm
+/// is that of a dynamic programming problem. We can calculate a point
+/// i,j,k in the cube as a function of i-1, j-1, and k-1. To find the
+/// score and path at point i,j,k, we must determine which path we want
+/// to use, this is done by looking at the possibilities and choosing
+/// the one which results in the local highest score. The total highest
+/// scored path is, then in the end represented by the cell in the
+/// opposite corner from the start location. The entire algorithm
+/// consists of populating the 3d cube with the optimal paths from which
+/// it may have came.
+///
+/// Optimizations:
+/// As the function to calculate the cell of a tensor at point i,j,k is a
+/// function of the cells at i-1, j-1, k-1, the whole tensor doesn't need
+/// to be stored in memory at once. In the case of the 3d cube, only two
+/// slices (along k and j axis) are stored in memory. For the 2d matrix
+/// (for 2 files), only two rows are stored at a time. The next/previous
+/// slice (or row) is always calculated from the other, and they alternate
+/// at each iteration.
+/// In the 3d case, 3 arrays are populated to memorize the score (matched
+/// characters) of the 3 buffers, so a redundant calculation of the
+/// scores does not occur
+/// @param diff_blk
+/// @param diff_len
+/// @param ndiffs
+/// @param [out] [allocated] decisions
+/// @return the length of decisions
+size_t linematch_nbuffers(const char **diff_blk, const int *diff_len, const size_t ndiffs,
+ int **decisions, bool iwhite)
+{
+ assert(ndiffs <= LN_MAX_BUFS);
+
+ size_t memsize = 1;
+ size_t memsize_decisions = 0;
+ for (size_t i = 0; i < ndiffs; i++) {
+ assert(diff_len[i] >= 0);
+ memsize *= i == 0 ? 2 : (size_t)(diff_len[i] + 1);
+ memsize_decisions += (size_t)diff_len[i];
+ }
+
+ // create the flattened path matrix
+ diffcmppath_T *diffcmppath = xmalloc(sizeof(diffcmppath_T) * memsize);
+ // allocate memory here
+ for (size_t i = 0; i < memsize; i++) {
+ diffcmppath[i].df_decision = xmalloc(memsize_decisions * sizeof(int));
+ }
+
+ // memory for avoiding repetitive calculations of score
+ int df_iters[LN_MAX_BUFS];
+ populate_tensor(df_iters, 0, diffcmppath, diff_len, ndiffs, diff_blk, iwhite);
+
+ const size_t u = unwrap_indexes(diff_len, diff_len, ndiffs);
+ const size_t best_path_idx = diffcmppath[u].df_path_idx;
+ const int *best_path_decisions = diffcmppath[u].df_decision;
+
+ *decisions = xmalloc(sizeof(int) * best_path_idx);
+ for (size_t i = 0; i < best_path_idx; i++) {
+ (*decisions)[i] = best_path_decisions[i];
+ }
+
+ for (size_t i = 0; i < memsize; i++) {
+ xfree(diffcmppath[i].df_decision);
+ }
+ xfree(diffcmppath);
+
+ return best_path_idx;
+}
diff --git a/src/nvim/linematch.h b/src/nvim/linematch.h
new file mode 100644
index 0000000000..052d438617
--- /dev/null
+++ b/src/nvim/linematch.h
@@ -0,0 +1,10 @@
+#ifndef NVIM_LINEMATCH_H
+#define NVIM_LINEMATCH_H
+
+#include <stddef.h>
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "linematch.h.generated.h"
+#endif
+
+#endif // NVIM_LINEMATCH_H
diff --git a/src/nvim/locale.c b/src/nvim/locale.c
new file mode 100644
index 0000000000..c3cfd3bedb
--- /dev/null
+++ b/src/nvim/locale.c
@@ -0,0 +1,377 @@
+// 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
+
+// locale.c: functions for language/locale configuration
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#include "auto/config.h"
+#ifdef HAVE_LOCALE_H
+# include <locale.h>
+#endif
+
+#include "nvim/ascii.h"
+#include "nvim/buffer.h"
+#include "nvim/charset.h"
+#include "nvim/eval.h"
+#include "nvim/ex_cmds_defs.h"
+#include "nvim/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/locale.h"
+#include "nvim/macros.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
+#include "nvim/option.h"
+#include "nvim/os/os.h"
+#include "nvim/os/shell.h"
+#include "nvim/path.h"
+#include "nvim/profile.h"
+#include "nvim/types.h"
+#include "nvim/vim.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "locale.c.generated.h"
+#endif
+
+#if defined(HAVE_LOCALE_H)
+# define HAVE_GET_LOCALE_VAL
+
+static char *get_locale_val(int what)
+{
+ // Obtain the locale value from the libraries.
+ char *loc = setlocale(what, NULL);
+
+ return loc;
+}
+#endif
+
+/// @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(const char *lang)
+{
+ return lang != NULL && ASCII_ISALPHA(lang[0]) && ASCII_ISALPHA(lang[1]);
+}
+
+/// Obtain the current messages language. Used to set the default for
+/// 'helplang'. May return NULL or an empty string.
+char *get_mess_lang(void)
+{
+ char *p;
+
+#ifdef HAVE_GET_LOCALE_VAL
+# if defined(LC_MESSAGES)
+ p = get_locale_val(LC_MESSAGES);
+# else
+ // This is necessary for Win32, where LC_MESSAGES is not defined and $LANG
+ // may be set to the LCID number. LC_COLLATE is the best guess, LC_TIME
+ // and LC_MONETARY may be set differently for a Japanese working in the
+ // US.
+ p = get_locale_val(LC_COLLATE);
+# endif
+#else
+ p = os_getenv("LC_ALL");
+ if (!is_valid_mess_lang(p)) {
+ p = os_getenv("LC_MESSAGES");
+ if (!is_valid_mess_lang(p)) {
+ p = os_getenv("LANG");
+ }
+ }
+#endif
+ return is_valid_mess_lang(p) ? p : NULL;
+}
+
+// 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 *get_mess_env(void)
+{
+ char *p;
+
+ p = (char *)os_getenv("LC_ALL");
+ if (p == NULL) {
+ p = (char *)os_getenv("LC_MESSAGES");
+ if (p == NULL) {
+ 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 = get_locale_val(LC_CTYPE);
+ }
+# endif
+ }
+ }
+ return p;
+}
+#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)
+{
+ const char *loc;
+
+#ifdef HAVE_GET_LOCALE_VAL
+ loc = get_locale_val(LC_CTYPE);
+#else
+ // setlocale() not supported: use the default value
+ loc = "C";
+#endif
+ set_vim_var_string(VV_CTYPE, loc, -1);
+
+ // 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 = get_mess_env();
+#elif defined(LC_MESSAGES)
+ loc = get_locale_val(LC_MESSAGES);
+#else
+ // In Windows LC_MESSAGES is not defined fallback to LC_CTYPE
+ loc = get_locale_val(LC_CTYPE);
+#endif
+ set_vim_var_string(VV_LANG, loc, -1);
+
+#ifdef HAVE_GET_LOCALE_VAL
+ loc = get_locale_val(LC_TIME);
+#endif
+ set_vim_var_string(VV_LC_TIME, loc, -1);
+
+#ifdef HAVE_GET_LOCALE_VAL
+ loc = get_locale_val(LC_COLLATE);
+#else
+ // setlocale() not supported: use the default value
+ loc = "C";
+#endif
+ set_vim_var_string(VV_COLLATE, loc, -1);
+}
+
+#if defined(HAVE_LOCALE_H)
+/// Setup to use the current locale (for ctype() and many other things).
+void init_locale(void)
+{
+ setlocale(LC_ALL, "");
+
+# ifdef LC_NUMERIC
+ // Make sure strtod() uses a decimal point, not a comma.
+ setlocale(LC_NUMERIC, "C");
+# endif
+
+ char localepath[MAXPATHL] = { 0 };
+ snprintf(localepath, sizeof(localepath), "%s", get_vim_var_str(VV_PROGPATH));
+ char *tail = path_tail_with_sep(localepath);
+ *tail = NUL;
+ tail = path_tail(localepath);
+ xstrlcpy(tail, "share/locale",
+ sizeof(localepath) - (size_t)(tail - localepath));
+ bindtextdomain(PROJECT_NAME, localepath);
+ textdomain(PROJECT_NAME);
+ TIME_MSG("locale set");
+}
+#endif
+
+#ifdef HAVE_WORKING_LIBINTL
+
+/// ":language": Set the language (locale).
+///
+/// @param eap
+void ex_language(exarg_T *eap)
+{
+ char *loc;
+ char *p;
+ char *name;
+ int what = LC_ALL;
+ char *whatstr = "";
+# ifdef LC_MESSAGES
+# define VIM_LC_MESSAGES LC_MESSAGES
+# else
+# define VIM_LC_MESSAGES 6789
+# endif
+
+ name = eap->arg;
+
+ // 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);
+ if ((*p == NUL || ascii_iswhite(*p)) && p - eap->arg >= 3) {
+ if (STRNICMP(eap->arg, "messages", p - eap->arg) == 0) {
+ what = VIM_LC_MESSAGES;
+ name = skipwhite(p);
+ whatstr = "messages ";
+ } else if (STRNICMP(eap->arg, "ctype", p - eap->arg) == 0) {
+ what = LC_CTYPE;
+ name = skipwhite(p);
+ whatstr = "ctype ";
+ } else if (STRNICMP(eap->arg, "time", p - eap->arg) == 0) {
+ what = LC_TIME;
+ name = skipwhite(p);
+ whatstr = "time ";
+ } else if (STRNICMP(eap->arg, "collate", p - eap->arg) == 0) {
+ what = LC_COLLATE;
+ name = skipwhite(p);
+ whatstr = "collate ";
+ }
+ }
+
+ if (*name == NUL) {
+ if (what == VIM_LC_MESSAGES) {
+ p = get_mess_env();
+ } else {
+ p = setlocale(what, NULL);
+ }
+ if (p == NULL || *p == NUL) {
+ p = "Unknown";
+ }
+ smsg(_("Current %slanguage: \"%s\""), whatstr, p);
+ } else {
+# ifndef LC_MESSAGES
+ if (what == VIM_LC_MESSAGES) {
+ loc = "";
+ } else {
+# endif
+ loc = setlocale(what, name);
+# ifdef LC_NUMERIC
+ // Make sure strtod() uses a decimal point, not a comma.
+ setlocale(LC_NUMERIC, "C");
+# endif
+# ifndef LC_MESSAGES
+ }
+# endif
+ if (loc == NULL) {
+ semsg(_("E197: Cannot set language to \"%s\""), name);
+ } else {
+# ifdef HAVE_NL_MSG_CAT_CNTR
+ // Need to do this for GNU gettext, otherwise cached translations
+ // will be used again.
+ extern int _nl_msg_cat_cntr;
+
+ _nl_msg_cat_cntr++;
+# endif
+ // Reset $LC_ALL, otherwise it would overrule everything.
+ os_setenv("LC_ALL", "", 1);
+
+ if (what != LC_TIME && what != LC_COLLATE) {
+ // Tell gettext() what to translate to. It apparently doesn't
+ // use the currently effective locale.
+ if (what == LC_ALL) {
+ os_setenv("LANG", name, 1);
+
+ // Clear $LANGUAGE because GNU gettext uses it.
+ os_setenv("LANGUAGE", "", 1);
+ }
+ if (what != LC_CTYPE) {
+ os_setenv("LC_MESSAGES", name, 1);
+ set_helplang_default(name);
+ }
+ }
+
+ // Set v:lang, v:lc_time, v:collate and v:ctype to the final result.
+ set_lang_var();
+ maketitle();
+ }
+ }
+}
+
+static char **locales = NULL; // Array of all available locales
+
+# ifndef MSWIN
+static bool did_init_locales = false;
+
+/// @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 *loc;
+ char *saveptr = NULL;
+
+ // Find all available locales by running command "locale -a". If this
+ // doesn't work we won't have completion.
+ char *locale_a = get_cmd_output("locale -a", NULL, kShellOptSilent, NULL);
+ if (locale_a == NULL) {
+ return NULL;
+ }
+ ga_init(&locales_ga, sizeof(char *), 20);
+
+ // Transform locale_a string where each locale is separated by "\n"
+ // into an array of locale strings.
+ loc = os_strtok(locale_a, "\n", &saveptr);
+
+ while (loc != NULL) {
+ 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 **)locales_ga.ga_data)[locales_ga.ga_len] = NULL;
+ return locales_ga.ga_data;
+}
+# endif
+
+/// Lazy initialization of all available locales.
+static void init_locales(void)
+{
+# ifndef MSWIN
+ if (did_init_locales) {
+ return;
+ }
+
+ did_init_locales = true;
+ locales = find_locales();
+# endif
+}
+
+# if defined(EXITFREE)
+void free_locales(void)
+{
+ if (locales == NULL) {
+ return;
+ }
+
+ for (int i = 0; locales[i] != NULL; i++) {
+ xfree(locales[i]);
+ }
+ XFREE_CLEAR(locales);
+}
+# endif
+
+/// Function given to ExpandGeneric() to obtain the possible arguments of the
+/// ":language" command.
+char *get_lang_arg(expand_T *xp, int idx)
+{
+ if (idx == 0) {
+ return "messages";
+ }
+ if (idx == 1) {
+ return "ctype";
+ }
+ if (idx == 2) {
+ return "time";
+ }
+ if (idx == 3) {
+ return "collate";
+ }
+
+ init_locales();
+ if (locales == NULL) {
+ return NULL;
+ }
+ return locales[idx - 4];
+}
+
+/// Function given to ExpandGeneric() to obtain the available locales.
+char *get_locales(expand_T *xp, int idx)
+{
+ init_locales();
+ if (locales == NULL) {
+ return NULL;
+ }
+ return locales[idx];
+}
+
+#endif
diff --git a/src/nvim/locale.h b/src/nvim/locale.h
new file mode 100644
index 0000000000..39735d371f
--- /dev/null
+++ b/src/nvim/locale.h
@@ -0,0 +1,10 @@
+#ifndef NVIM_LOCALE_H
+#define NVIM_LOCALE_H
+
+#include "nvim/ex_cmds_defs.h"
+#include "nvim/types.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "locale.h.generated.h"
+#endif
+#endif // NVIM_LOCALE_H
diff --git a/src/nvim/log.c b/src/nvim/log.c
index 99b17a612b..2c214aa32d 100644
--- a/src/nvim/log.c
+++ b/src/nvim/log.c
@@ -9,21 +9,27 @@
//
#include <assert.h>
+#include <errno.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
#include <uv.h>
#include "auto/config.h"
+#include "nvim/ascii.h"
#include "nvim/eval.h"
+#include "nvim/globals.h"
#include "nvim/log.h"
-#include "nvim/main.h"
+#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/os/os.h"
+#include "nvim/os/stdpaths_defs.h"
#include "nvim/os/time.h"
#include "nvim/path.h"
-#include "nvim/types.h"
/// Cached location of the expanded log file path decided by log_path_init().
static char log_file_path[MAXPATHL + 1] = { 0 };
@@ -60,16 +66,16 @@ static bool log_try_create(char *fname)
static void log_path_init(void)
{
size_t size = sizeof(log_file_path);
- expand_env((char_u *)"$" ENV_LOGFILE, (char_u *)log_file_path, (int)size - 1);
+ expand_env("$" ENV_LOGFILE, 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)
+ || os_isdir(log_file_path)
|| !log_try_create(log_file_path)) {
// 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 *)loghome)) {
+ if (!os_isdir(loghome)) {
log_dir_failure = (os_mkdir_recurse(loghome, 0700, &failed_dir) != 0);
}
XFREE_CLEAR(loghome);
diff --git a/src/nvim/log.h b/src/nvim/log.h
index cbee0e0f81..14d46c2ea7 100644
--- a/src/nvim/log.h
+++ b/src/nvim/log.h
@@ -11,6 +11,7 @@
// NVIM_PROBE(nvim_foo_bar, 1, string.data);
#if defined(HAVE_SYS_SDT_H)
# include <sys/sdt.h> // NOLINT
+
# define NVIM_PROBE(name, n, ...) STAP_PROBE##n(neovim, name, __VA_ARGS__)
#else
# define NVIM_PROBE(name, n, ...)
diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c
index 49d49f76b9..6160b84485 100644
--- a/src/nvim/lua/converter.c
+++ b/src/nvim/lua/converter.c
@@ -4,27 +4,31 @@
#include <assert.h>
#include <lauxlib.h>
#include <lua.h>
-#include <lualib.h>
#include <stdbool.h>
+#include <stddef.h>
#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
-#include "nvim/assert.h"
-#include "nvim/func_attr.h"
#include "nvim/memory.h"
// FIXME: vim.h is not actually needed, but otherwise it states MAXPATHL is
// redefined
+#include "klib/kvec.h"
#include "nvim/ascii.h"
#include "nvim/eval/decode.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/eval/typval_encode.h"
#include "nvim/eval/userfunc.h"
-#include "nvim/globals.h"
-#include "nvim/lib/kvec.h"
+#include "nvim/garray.h"
+#include "nvim/gettext.h"
#include "nvim/lua/converter.h"
#include "nvim/lua/executor.h"
#include "nvim/macros.h"
#include "nvim/message.h"
+#include "nvim/types.h"
#include "nvim/vim.h"
/// Determine, which keys lua table contains
@@ -385,15 +389,12 @@ nlua_pop_typval_table_processing_end:
break;
}
case LUA_TFUNCTION: {
- LuaCFunctionState *state = xmalloc(sizeof(LuaCFunctionState));
- state->lua_callable.func_ref = nlua_ref_global(lstate, -1);
+ LuaRef func = nlua_ref_global(lstate, -1);
- char_u *name = register_cfunc(&nlua_CFunction_func_call,
- &nlua_CFunction_func_free,
- state);
+ char *name = register_luafunc(func);
cur.tv->v_type = VAR_FUNC;
- cur.tv->vval.v_string = (char *)vim_strsave(name);
+ cur.tv->vval.v_string = xstrdup(name);
break;
}
case LUA_TUSERDATA: {
@@ -476,8 +477,8 @@ static bool typval_conv_special = false;
#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
do { \
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); \
+ if (fp != NULL && fp->uf_flags & FC_LUAREF) { \
+ nlua_pushref(lstate, fp->uf_luaref); \
} else { \
TYPVAL_ENCODE_CONV_NIL(tv); \
} \
@@ -568,6 +569,7 @@ static bool typval_conv_special = false;
#define TYPVAL_ENCODE_FIRST_ARG_TYPE lua_State *const
#define TYPVAL_ENCODE_FIRST_ARG_NAME lstate
#include "nvim/eval/typval_encode.c.h"
+
#undef TYPVAL_ENCODE_SCOPE
#undef TYPVAL_ENCODE_NAME
#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
@@ -909,9 +911,8 @@ Float nlua_pop_Float(lua_State *lstate, Error *err)
lua_pop(lstate, 1);
if (table_props.type != kObjectTypeFloat) {
return 0;
- } else {
- return (Float)table_props.val;
}
+ return (Float)table_props.val;
}
/// Convert lua table to array without determining whether it is array
diff --git a/src/nvim/lua/converter.h b/src/nvim/lua/converter.h
index f6a85900ba..ddc0acfbfa 100644
--- a/src/nvim/lua/converter.h
+++ b/src/nvim/lua/converter.h
@@ -9,14 +9,6 @@
#include "nvim/eval/typval.h"
#include "nvim/func_attr.h"
-typedef struct {
- LuaRef func_ref;
-} LuaCallable;
-
-typedef struct {
- LuaCallable lua_callable;
-} LuaCFunctionState;
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "lua/converter.h.generated.h"
#endif
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index d1d1480696..5ffd90fddd 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -1,17 +1,22 @@
// 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 <lauxlib.h>
#include <lua.h>
#include <lualib.h>
+#include <stddef.h>
+#include <string.h>
#include <tree_sitter/api.h>
+#include <uv.h>
+#include "klib/kvec.h"
#include "luv/luv.h"
+#include "nvim/api/extmark.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
-#include "nvim/api/vim.h"
#include "nvim/ascii.h"
-#include "nvim/assert.h"
#include "nvim/buffer_defs.h"
#include "nvim/change.h"
#include "nvim/cursor.h"
@@ -19,27 +24,39 @@
#include "nvim/eval.h"
#include "nvim/eval/funcs.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/eval/userfunc.h"
+#include "nvim/event/defs.h"
#include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
#include "nvim/event/time.h"
#include "nvim/ex_cmds.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_getln.h"
-#include "nvim/extmark.h"
-#include "nvim/func_attr.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/keycodes.h"
#include "nvim/lua/converter.h"
#include "nvim/lua/executor.h"
#include "nvim/lua/stdlib.h"
#include "nvim/lua/treesitter.h"
#include "nvim/macros.h"
-#include "nvim/map.h"
+#include "nvim/main.h"
#include "nvim/memline.h"
+#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/msgpack_rpc/channel.h"
+#include "nvim/option_defs.h"
+#include "nvim/os/fileio.h"
#include "nvim/os/os.h"
+#include "nvim/path.h"
+#include "nvim/pos.h"
#include "nvim/profile.h"
#include "nvim/runtime.h"
+#include "nvim/strings.h"
+#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/usercmd.h"
#include "nvim/version.h"
@@ -188,8 +205,8 @@ static int nlua_luv_cfpcall(lua_State *lstate, int nargs, int nresult, int flags
if (status) {
if (status == LUA_ERRMEM && !(flags & LUVF_CALLBACK_NOEXIT)) {
// consider out of memory errors unrecoverable, just like xmalloc()
- mch_errmsg(e_outofmem);
- mch_errmsg("\n");
+ os_errmsg(e_outofmem);
+ os_errmsg("\n");
preserve_exit();
}
const char *error = lua_tostring(lstate, -1);
@@ -241,10 +258,10 @@ static int nlua_luv_thread_common_cfpcall(lua_State *lstate, int nargs, int nres
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");
+ os_errmsg(e_outofmem);
+ os_errmsg("\n");
lua_close(lstate);
-#ifdef WIN32
+#ifdef MSWIN
ExitThread(0);
#else
pthread_exit(0);
@@ -306,6 +323,36 @@ static int nlua_thr_api_nvim__get_runtime(lua_State *lstate)
return 1;
}
+/// Copies args starting at `lua_arg0` to Lua `_G.arg`, and sets `_G.arg[0]` to the scriptname.
+///
+/// Example (arg[0] => "foo.lua", arg[1] => "--arg1", …):
+/// nvim -l foo.lua --arg1 --arg2
+///
+/// @note Lua CLI sets args before "-e" as _negative_ `_G.arg` indices, but we currently don't.
+///
+/// @see https://www.lua.org/pil/1.4.html
+/// @see https://github.com/premake/premake-core/blob/1c1304637f4f5e50ba8c57aae8d1d80ec3b7aaf2/src/host/premake.c#L563-L594
+///
+/// @returns number of args
+static int nlua_init_argv(lua_State *const L, char **argv, int argc, int lua_arg0)
+{
+ int i = 0;
+ lua_newtable(L); // _G.arg
+
+ if (lua_arg0 > 0) {
+ lua_pushstring(L, argv[lua_arg0 - 1]);
+ lua_rawseti(L, -2, 0); // _G.arg[0] = "foo.lua"
+
+ for (; lua_arg0 >= 0 && i + lua_arg0 < argc; i++) {
+ lua_pushstring(L, argv[i + lua_arg0]);
+ lua_rawseti(L, -2, i + 1); // _G.arg[i+1] = "--foo"
+ }
+ }
+
+ lua_setglobal(L, "arg");
+ return i;
+}
+
static void nlua_schedule_event(void **argv)
{
LuaRef cb = (LuaRef)(ptrdiff_t)argv[0];
@@ -396,7 +443,7 @@ static int nlua_wait(lua_State *lstate)
bool fast_only = false;
if (lua_top >= 4) {
- fast_only = lua_toboolean(lstate, 4);
+ fast_only = lua_toboolean(lstate, 4);
}
MultiQueue *loop_events = fast_only || in_fast_callback > 0
@@ -581,14 +628,79 @@ static bool nlua_init_packages(lua_State *lstate)
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");
+ os_errmsg((char *)lua_tostring(lstate, -1));
+ os_errmsg("\n");
return false;
}
return true;
}
+/// "vim.ui_attach(ns_id, {ext_foo=true}, cb)" function
+static int nlua_ui_attach(lua_State *lstate)
+ FUNC_ATTR_NONNULL_ALL
+{
+ uint32_t ns_id = (uint32_t)luaL_checkinteger(lstate, 1);
+
+ if (!ns_initialized(ns_id)) {
+ return luaL_error(lstate, "invalid ns_id");
+ }
+ if (!lua_istable(lstate, 2)) {
+ return luaL_error(lstate, "ext_widgets must be a table");
+ }
+ if (!lua_isfunction(lstate, 3)) {
+ return luaL_error(lstate, "callback must be a Lua function");
+ }
+
+ bool ext_widgets[kUIGlobalCount] = { false };
+ bool tbl_has_true_val = false;
+
+ lua_pushvalue(lstate, 2);
+ lua_pushnil(lstate);
+ while (lua_next(lstate, -2)) {
+ // [dict, key, val]
+ size_t len;
+ const char *s = lua_tolstring(lstate, -2, &len);
+ bool val = lua_toboolean(lstate, -1);
+
+ for (size_t i = 0; i < kUIGlobalCount; i++) {
+ if (strequal(s, ui_ext_names[i])) {
+ if (val) {
+ tbl_has_true_val = true;
+ }
+ ext_widgets[i] = val;
+ goto ok;
+ }
+ }
+
+ return luaL_error(lstate, "Unexpected key: %s", s);
+ok:
+ lua_pop(lstate, 1);
+ }
+
+ if (!tbl_has_true_val) {
+ return luaL_error(lstate, "ext_widgets table must contain at least one 'true' value");
+ }
+
+ LuaRef ui_event_cb = nlua_ref_global(lstate, 3);
+ ui_add_cb(ns_id, ui_event_cb, ext_widgets);
+ return 0;
+}
+
+/// "vim.ui_detach(ns_id)" function
+static int nlua_ui_detach(lua_State *lstate)
+ FUNC_ATTR_NONNULL_ALL
+{
+ uint32_t ns_id = (uint32_t)luaL_checkinteger(lstate, 1);
+
+ if (!ns_initialized(ns_id)) {
+ return luaL_error(lstate, "invalid ns_id");
+ }
+
+ ui_remove_cb(ns_id);
+ return 0;
+}
+
/// Initialize lua interpreter state
///
/// Called by lua interpreter itself to initialize state.
@@ -604,7 +716,7 @@ static bool nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
lua_setfield(lstate, -2, "debug");
lua_pop(lstate, 1);
-#ifdef WIN32
+#ifdef MSWIN
// os.getenv
lua_getglobal(lstate, "os");
lua_pushcfunction(lstate, &nlua_getenv);
@@ -649,6 +761,14 @@ static bool nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
lua_pushcfunction(lstate, &nlua_wait);
lua_setfield(lstate, -2, "wait");
+ // ui_attach
+ lua_pushcfunction(lstate, &nlua_ui_attach);
+ lua_setfield(lstate, -2, "ui_attach");
+
+ // ui_detach
+ lua_pushcfunction(lstate, &nlua_ui_detach);
+ lua_setfield(lstate, -2, "ui_detach");
+
nlua_common_vim_init(lstate, false);
// patch require() (only for --startuptime)
@@ -675,10 +795,8 @@ static bool nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
return true;
}
-/// Initialize global lua interpreter
-///
-/// Crashes Nvim if initialization fails.
-void nlua_init(void)
+/// Initializes global Lua interpreter, or exits Nvim on failure.
+void nlua_init(char **argv, int argc, int lua_arg0)
{
#ifdef NLUA_TRACK_REFS
const char *env = os_getenv("NVIM_LUA_NOTRACK");
@@ -689,20 +807,19 @@ void nlua_init(void)
lua_State *lstate = luaL_newstate();
if (lstate == NULL) {
- mch_errmsg(_("E970: Failed to initialize lua interpreter\n"));
+ os_errmsg(_("E970: Failed to initialize lua interpreter\n"));
os_exit(1);
}
luaL_openlibs(lstate);
if (!nlua_state_init(lstate)) {
- mch_errmsg(_("E970: Failed to initialize builtin lua modules\n"));
+ os_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();
+ nlua_init_argv(lstate, argv, argc, lua_arg0);
}
static lua_State *nlua_thread_acquire_vm(void)
@@ -935,8 +1052,8 @@ static int nlua_require(lua_State *const lstate)
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);
+ vim_snprintf(IObuff, IOSIZE, "require('%s')", name);
+ time_msg(IObuff, &start_time);
}
time_pop(rel_time);
@@ -967,12 +1084,12 @@ static int nlua_debug(lua_State *lstate)
if (input.v_type != VAR_STRING
|| input.vval.v_string == NULL
|| *input.vval.v_string == NUL
- || STRCMP(input.vval.v_string, "cont") == 0) {
+ || strcmp(input.vval.v_string, "cont") == 0) {
tv_clear(&input);
return 0;
}
if (luaL_loadbuffer(lstate, (const char *)input.vval.v_string,
- STRLEN(input.vval.v_string), "=(debug command)")) {
+ strlen(input.vval.v_string), "=(debug command)")) {
nlua_error(lstate, _("E5115: Error while loading debug string: %.*s"));
} else if (nlua_pcall(lstate, 0, 0)) {
nlua_error(lstate, _("E5116: Error while calling debug string: %.*s"));
@@ -990,7 +1107,7 @@ int nlua_in_fast_event(lua_State *lstate)
static bool viml_func_is_fast(const char *name)
{
- const EvalFuncDef *const fdef = find_internal_func((const char *)name);
+ const EvalFuncDef *const fdef = find_internal_func(name);
if (fdef) {
return fdef->fast;
}
@@ -1027,15 +1144,15 @@ int nlua_call(lua_State *lstate)
// TODO(bfredl): this should be simplified in error handling refactor
force_abort = false;
suppress_errthrow = false;
- current_exception = NULL;
+ did_throw = false;
did_emsg = false;
try_start();
typval_T rettv;
funcexe_T funcexe = FUNCEXE_INIT;
- funcexe.firstline = curwin->w_cursor.lnum;
- funcexe.lastline = curwin->w_cursor.lnum;
- funcexe.evaluate = true;
+ funcexe.fe_firstline = curwin->w_cursor.lnum;
+ funcexe.fe_lastline = curwin->w_cursor.lnum;
+ funcexe.fe_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((char *)name, (int)name_len, &rettv, nargs, vim_args, &funcexe);
@@ -1093,13 +1210,14 @@ static int nlua_rpc(lua_State *lstate, bool request)
Object result = rpc_send_call(chan_id, name, args, &res_mem, &err);
if (!ERROR_SET(&err)) {
nlua_push_Object(lstate, result, false);
- arena_mem_free(res_mem, NULL);
+ arena_mem_free(res_mem);
}
} else {
if (!rpc_send_event(chan_id, name, args)) {
api_set_error(&err, kErrorTypeValidation,
"Invalid channel: %" PRIu64, chan_id);
}
+ api_free_array(args); // TODO(bfredl): no
}
check_err:
@@ -1124,7 +1242,7 @@ static int nlua_empty_dict_tostring(lua_State *lstate)
return 1;
}
-#ifdef WIN32
+#ifdef MSWIN
/// os.getenv: override os.getenv to maintain coherency. #9681
///
/// uv_os_setenv uses SetEnvironmentVariableW which does not update _environ.
@@ -1224,7 +1342,7 @@ void nlua_typval_eval(const String str, typval_T *const arg, typval_T *const ret
const size_t lcmd_len = sizeof(EVALHEADER) - 1 + str.size + 1;
char *lcmd;
if (lcmd_len < IOSIZE) {
- lcmd = (char *)IObuff;
+ lcmd = IObuff;
} else {
lcmd = xmalloc(lcmd_len);
}
@@ -1234,7 +1352,7 @@ void nlua_typval_eval(const String str, typval_T *const arg, typval_T *const ret
#undef EVALHEADER
nlua_typval_exec(lcmd, lcmd_len, "luaeval()", arg, 1, true, ret_tv);
- if (lcmd != (char *)IObuff) {
+ if (lcmd != IObuff) {
xfree(lcmd);
}
}
@@ -1248,7 +1366,7 @@ void nlua_typval_call(const char *str, size_t len, typval_T *const args, int arg
const size_t lcmd_len = sizeof(CALLHEADER) - 1 + len + sizeof(CALLSUFFIX) - 1;
char *lcmd;
if (lcmd_len < IOSIZE) {
- lcmd = (char *)IObuff;
+ lcmd = IObuff;
} else {
lcmd = xmalloc(lcmd_len);
}
@@ -1261,7 +1379,7 @@ void nlua_typval_call(const char *str, size_t len, typval_T *const args, int arg
nlua_typval_exec(lcmd, lcmd_len, "v:lua", args, argcount, false, ret_tv);
- if (lcmd != (char *)IObuff) {
+ if (lcmd != IObuff) {
xfree(lcmd);
}
}
@@ -1322,11 +1440,11 @@ int nlua_source_using_linegetter(LineGetter fgetline, void *cookie, char *name)
estack_push(ETYPE_SCRIPT, name, 0);
garray_T ga;
- char_u *line = NULL;
+ char *line = NULL;
- ga_init(&ga, (int)sizeof(char_u *), 10);
- while ((line = (char_u *)fgetline(0, cookie, 0, false)) != NULL) {
- GA_APPEND(char_u *, &ga, line);
+ ga_init(&ga, (int)sizeof(char *), 10);
+ while ((line = fgetline(0, cookie, 0, false)) != NULL) {
+ GA_APPEND(char *, &ga, line);
}
char *code = ga_concat_strings_sep(&ga, "\n");
size_t len = strlen(code);
@@ -1348,23 +1466,22 @@ int nlua_source_using_linegetter(LineGetter fgetline, void *cookie, char *name)
/// @param[in] argcount Count of typval arguments
/// @param[in] argvars Typval Arguments
/// @param[out] rettv The return value from the called function.
-int typval_exec_lua_callable(lua_State *lstate, LuaCallable lua_cb, int argcount, typval_T *argvars,
- typval_T *rettv)
+int typval_exec_lua_callable(LuaRef lua_cb, int argcount, typval_T *argvars, typval_T *rettv)
{
- LuaRef cb = lua_cb.func_ref;
+ lua_State *lstate = global_lstate;
- nlua_pushref(lstate, cb);
+ nlua_pushref(lstate, lua_cb);
PUSH_ALL_TYPVALS(lstate, argvars, argcount, false);
if (nlua_pcall(lstate, argcount, 1)) {
nlua_print(lstate);
- return ERROR_OTHER;
+ return FCERR_OTHER;
}
nlua_pop_typval(lstate, rettv);
- return ERROR_NONE;
+ return FCERR_NONE;
}
/// Execute Lua string
@@ -1422,9 +1539,10 @@ bool nlua_ref_is_function(LuaRef ref)
/// @param name if non-NULL, sent to callback as first arg
/// if NULL, only args are used
/// @param retval if true, convert return value to Object
-/// if false, discard return value
+/// if false, only check if return value is truthy
/// @param err Error details, if any (if NULL, errors are echoed)
-/// @return Return value of function, if retval was set. Otherwise NIL.
+/// @return Return value of function, if retval was set. Otherwise
+/// BOOLEAN_OBJ(true) or NIL.
Object nlua_call_ref(LuaRef ref, const char *name, Array args, bool retval, Error *err)
{
lua_State *const lstate = global_lstate;
@@ -1438,7 +1556,7 @@ Object nlua_call_ref(LuaRef ref, const char *name, Array args, bool retval, Erro
nlua_push_Object(lstate, args.items[i], false);
}
- if (nlua_pcall(lstate, nargs, retval ? 1 : 0)) {
+ if (nlua_pcall(lstate, nargs, 1)) {
// if err is passed, the caller will deal with the error.
if (err) {
size_t len;
@@ -1458,7 +1576,10 @@ Object nlua_call_ref(LuaRef ref, const char *name, Array args, bool retval, Erro
}
return nlua_pop_Object(lstate, false, err);
} else {
- return NIL;
+ bool value = lua_toboolean(lstate, -1);
+ lua_pop(lstate, 1);
+
+ return value ? BOOLEAN_OBJ(true) : NIL;
}
}
@@ -1524,7 +1645,7 @@ void ex_luado(exarg_T *const eap)
+ (sizeof(DOEND) - 1));
char *lcmd;
if (lcmd_len < IOSIZE) {
- lcmd = (char *)IObuff;
+ lcmd = IObuff;
} else {
lcmd = xmalloc(lcmd_len + 1);
}
@@ -1561,7 +1682,7 @@ void ex_luado(exarg_T *const eap)
break;
}
if (lua_isstring(lstate, -1)) {
- size_t old_line_len = STRLEN(old_line);
+ size_t old_line_len = strlen(old_line);
size_t new_line_len;
const char *const new_line = lua_tolstring(lstate, -1, &new_line_len);
@@ -1578,7 +1699,7 @@ void ex_luado(exarg_T *const eap)
}
lua_pop(lstate, 1);
check_cursor();
- update_screen(NOT_VALID);
+ redraw_curbuf_later(UPD_NOT_VALID);
}
/// Run lua file
@@ -1592,21 +1713,51 @@ void ex_luafile(exarg_T *const eap)
nlua_exec_file((const char *)eap->arg);
}
-/// execute lua code from a file.
+/// Executes Lua code from a file or "-" (stdin).
///
-/// Note: we call the lua global loadfile as opposed to calling luaL_loadfile
-/// in case loadfile has been overridden in the users environment.
+/// Calls the Lua `loadfile` global as opposed to `luaL_loadfile` in case `loadfile` was overridden
+/// in the user environment.
///
-/// @param path path of the file
+/// @param path Path to the file, may be "-" (stdin) during startup.
///
-/// @return true if everything ok, false if there was an error (echoed)
+/// @return true on success, false on error (echoed) or user canceled (CTRL-c) while reading "-"
+/// (stdin).
bool nlua_exec_file(const char *path)
FUNC_ATTR_NONNULL_ALL
{
lua_State *const lstate = global_lstate;
+ if (!strequal(path, "-")) {
+ lua_getglobal(lstate, "loadfile");
+ lua_pushstring(lstate, path);
+ } else {
+ FileDescriptor *stdin_dup = file_open_stdin();
+
+ StringBuilder sb = KV_INITIAL_VALUE;
+ kv_resize(sb, 64);
+ ptrdiff_t read_size = -1;
+ // Read all input from stdin, unless interrupted (ctrl-c).
+ while (true) {
+ if (got_int) { // User canceled.
+ return false;
+ }
+ read_size = file_read(stdin_dup, IObuff, 64);
+ if (read_size < 0) { // Error.
+ return false;
+ }
+ if (read_size > 0) {
+ kv_concat_len(sb, IObuff, (size_t)read_size);
+ }
+ if (read_size < 64) { // EOF.
+ break;
+ }
+ }
+ kv_push(sb, NUL);
+ file_free(stdin_dup, false);
- lua_getglobal(lstate, "loadfile");
- lua_pushstring(lstate, path);
+ lua_getglobal(lstate, "loadstring");
+ lua_pushstring(lstate, sb.items);
+ kv_destroy(sb);
+ }
if (nlua_pcall(lstate, 1, 2)) {
nlua_error(lstate, _("E5111: Error calling lua: %.*s"));
@@ -1662,6 +1813,9 @@ static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
lua_pushcfunction(lstate, tslua_has_language);
lua_setfield(lstate, -2, "_ts_has_language");
+ lua_pushcfunction(lstate, tslua_remove_lang);
+ lua_setfield(lstate, -2, "_ts_remove_language");
+
lua_pushcfunction(lstate, tslua_inspect_lang);
lua_setfield(lstate, -2, "_ts_inspect_language");
@@ -1675,7 +1829,7 @@ static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
lua_setfield(lstate, -2, "_ts_get_minimum_language_version");
}
-int nlua_expand_pat(expand_T *xp, char_u *pat, int *num_results, char ***results)
+int nlua_expand_pat(expand_T *xp, char *pat, int *num_results, char ***results)
{
lua_State *const lstate = global_lstate;
int ret = OK;
@@ -1688,7 +1842,7 @@ int nlua_expand_pat(expand_T *xp, char_u *pat, int *num_results, char ***results
luaL_checktype(lstate, -1, LUA_TFUNCTION);
// [ vim, vim._expand_pat, buf ]
- lua_pushlstring(lstate, (const char *)pat, STRLEN(pat));
+ lua_pushlstring(lstate, (const char *)pat, strlen(pat));
if (nlua_pcall(lstate, 1, 2) != 0) {
nlua_error(lstate,
@@ -1723,7 +1877,7 @@ int nlua_expand_pat(expand_T *xp, char_u *pat, int *num_results, char ***results
goto cleanup_array;
}
- GA_APPEND(char_u *, &result_array, (char_u *)string_to_cstr(v.data.string));
+ GA_APPEND(char *, &result_array, string_to_cstr(v.data.string));
}
xp->xp_pattern += prefix_len;
@@ -1749,27 +1903,7 @@ static int nlua_is_thread(lua_State *lstate)
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)
-{
- lua_State *const lstate = global_lstate;
- LuaCFunctionState *funcstate = (LuaCFunctionState *)state;
-
- return typval_exec_lua_callable(lstate, funcstate->lua_callable,
- argcount, argvars, rettv);
-}
-
-void nlua_CFunction_func_free(void *state)
-{
- lua_State *const lstate = global_lstate;
- LuaCFunctionState *funcstate = (LuaCFunctionState *)state;
-
- nlua_unref_global(lstate, funcstate->lua_callable.func_ref);
- xfree(funcstate);
-}
-
-bool nlua_is_table_from_lua(typval_T *const arg)
+bool nlua_is_table_from_lua(const typval_T *const arg)
{
if (arg->v_type == VAR_DICT) {
return arg->vval.v_dict->lua_table_ref != LUA_NOREF;
@@ -1780,7 +1914,7 @@ bool nlua_is_table_from_lua(typval_T *const arg)
}
}
-char_u *nlua_register_table_as_callable(typval_T *const arg)
+char *nlua_register_table_as_callable(const typval_T *const arg)
{
LuaRef table_ref = LUA_NOREF;
if (arg->v_type == VAR_DICT) {
@@ -1814,11 +1948,9 @@ 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_global(lstate, -1);
+ LuaRef func = nlua_ref_global(lstate, -1);
- char_u *name = register_cfunc(&nlua_CFunction_func_call,
- &nlua_CFunction_func_free, state);
+ char *name = register_luafunc(func);
lua_pop(lstate, 1); // []
assert(top == lua_gettop(lstate));
@@ -1828,8 +1960,8 @@ char_u *nlua_register_table_as_callable(typval_T *const arg)
void nlua_execute_on_key(int c)
{
- char_u buf[NUMBUFLEN];
- size_t buf_len = special_to_buf(c, mod_mask, false, buf);
+ char buf[NUMBUFLEN];
+ size_t buf_len = special_to_buf(c, mod_mask, false, (char_u *)buf);
lua_State *const lstate = global_lstate;
@@ -1845,7 +1977,7 @@ void nlua_execute_on_key(int c)
luaL_checktype(lstate, -1, LUA_TFUNCTION);
// [ vim, vim._on_key, buf ]
- lua_pushlstring(lstate, (const char *)buf, buf_len);
+ lua_pushlstring(lstate, buf, buf_len);
int save_got_int = got_int;
got_int = false; // avoid interrupts when the key typed is Ctrl-C
@@ -1924,6 +2056,9 @@ int nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap, bool preview)
nlua_pushref(lstate, preview ? cmd->uc_preview_luaref : cmd->uc_luaref);
lua_newtable(lstate);
+ lua_pushstring(lstate, cmd->uc_name);
+ lua_setfield(lstate, -2, "name");
+
lua_pushboolean(lstate, eap->forceit == 1);
lua_setfield(lstate, -2, "bang");
@@ -1940,7 +2075,7 @@ int nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap, bool preview)
// Split args by unescaped whitespace |<f-args>| (nargs dependent)
if (cmd->uc_argt & EX_NOSPC) {
- if ((cmd->uc_argt & EX_NEEDARG) || STRLEN(eap->arg)) {
+ if ((cmd->uc_argt & EX_NEEDARG) || strlen(eap->arg)) {
// For commands where nargs is 1 or "?" and argument is passed, fargs = { args }
lua_rawseti(lstate, -2, 1);
} else {
@@ -1950,7 +2085,7 @@ int nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap, bool preview)
} 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 length = strlen(eap->arg);
size_t end = 0;
size_t len = 0;
int i = 1;
@@ -1975,7 +2110,8 @@ int nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap, bool preview)
}
lua_setfield(lstate, -2, "fargs");
- lua_pushstring(lstate, (const char *)&eap->regname);
+ char reg[2] = { (char)eap->regname, NUL };
+ lua_pushstring(lstate, reg);
lua_setfield(lstate, -2, "reg");
lua_pushinteger(lstate, eap->addr_count);
@@ -1998,7 +2134,7 @@ int nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap, bool preview)
lua_newtable(lstate); // smods table
- lua_pushinteger(lstate, cmdmod.cmod_tab);
+ lua_pushinteger(lstate, cmdmod.cmod_tab - 1);
lua_setfield(lstate, -2, "tab");
lua_pushinteger(lstate, cmdmod.cmod_verbose - 1);
@@ -2019,6 +2155,8 @@ int nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap, bool preview)
lua_pushboolean(lstate, cmdmod.cmod_split & WSP_VERT);
lua_setfield(lstate, -2, "vertical");
+ lua_pushboolean(lstate, cmdmod.cmod_split & WSP_HOR);
+ lua_setfield(lstate, -2, "horizontal");
lua_pushboolean(lstate, cmdmod.cmod_flags & CMOD_SILENT);
lua_setfield(lstate, -2, "silent");
lua_pushboolean(lstate, cmdmod.cmod_flags & CMOD_ERRSILENT);
@@ -2113,3 +2251,80 @@ plain:
kv_printf(str, "<Lua %d>", ref);
return str.items;
}
+
+char *nlua_read_secure(const char *path)
+{
+ lua_State *const lstate = global_lstate;
+ const int top = lua_gettop(lstate);
+
+ lua_getglobal(lstate, "vim");
+ lua_getfield(lstate, -1, "secure");
+ lua_getfield(lstate, -1, "read");
+ lua_pushstring(lstate, path);
+ if (nlua_pcall(lstate, 1, 1)) {
+ nlua_error(lstate, _("Error executing vim.secure.read: %.*s"));
+ lua_settop(lstate, top);
+ return NULL;
+ }
+
+ size_t len = 0;
+ const char *contents = lua_tolstring(lstate, -1, &len);
+ char *buf = NULL;
+ if (contents != NULL) {
+ // Add one to include trailing null byte
+ buf = xcalloc(len + 1, sizeof(char));
+ memcpy(buf, contents, len + 1);
+ }
+
+ lua_settop(lstate, top);
+ return buf;
+}
+
+bool nlua_trust(const char *action, const char *path)
+{
+ lua_State *const lstate = global_lstate;
+ const int top = lua_gettop(lstate);
+
+ lua_getglobal(lstate, "vim");
+ lua_getfield(lstate, -1, "secure");
+ lua_getfield(lstate, -1, "trust");
+
+ lua_newtable(lstate);
+ lua_pushstring(lstate, "action");
+ lua_pushstring(lstate, action);
+ lua_settable(lstate, -3);
+ if (path == NULL) {
+ lua_pushstring(lstate, "bufnr");
+ lua_pushnumber(lstate, 0);
+ lua_settable(lstate, -3);
+ } else {
+ lua_pushstring(lstate, "path");
+ lua_pushstring(lstate, path);
+ lua_settable(lstate, -3);
+ }
+
+ if (nlua_pcall(lstate, 1, 2)) {
+ nlua_error(lstate, _("Error executing vim.secure.trust: %.*s"));
+ lua_settop(lstate, top);
+ return false;
+ }
+
+ bool success = lua_toboolean(lstate, -2);
+ const char *msg = lua_tostring(lstate, -1);
+ if (msg != NULL) {
+ if (success) {
+ if (strcmp(action, "allow") == 0) {
+ smsg("Allowed \"%s\" in trust database.", msg);
+ } else if (strcmp(action, "deny") == 0) {
+ smsg("Denied \"%s\" in trust database.", msg);
+ } else if (strcmp(action, "remove") == 0) {
+ smsg("Removed \"%s\" from trust database.", msg);
+ }
+ } else {
+ semsg(e_trustfile, msg);
+ }
+ }
+
+ lua_settop(lstate, top);
+ return success;
+}
diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h
index 78346fd81f..c6747833e5 100644
--- a/src/nvim/lua/executor.h
+++ b/src/nvim/lua/executor.h
@@ -3,13 +3,17 @@
#include <lauxlib.h>
#include <lua.h>
+#include <stdbool.h>
#include "nvim/api/private/defs.h"
+#include "nvim/api/private/helpers.h"
#include "nvim/assert.h"
#include "nvim/eval/typval.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/func_attr.h"
#include "nvim/lua/converter.h"
+#include "nvim/macros.h"
+#include "nvim/types.h"
#include "nvim/usercmd.h"
// Generated by msgpack-gen.lua
diff --git a/src/nvim/lua/spell.c b/src/nvim/lua/spell.c
index 31a2b2d19f..d510d25e90 100644
--- a/src/nvim/lua/spell.c
+++ b/src/nvim/lua/spell.c
@@ -1,15 +1,25 @@
// 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 <lauxlib.h>
+#include <limits.h>
#include <lua.h>
-
+#include <stdbool.h>
+#include <stddef.h>
+
+#include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/highlight_defs.h"
#include "nvim/lua/spell.h"
+#include "nvim/message.h"
#include "nvim/spell.h"
-#include "nvim/vim.h"
+#include "nvim/types.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "lua/spell.c.generated.h"
+# include "lua/spell.c.generated.h" // IWYU pragma: export
#endif
int nlua_spell_check(lua_State *lstate)
@@ -51,7 +61,7 @@ int nlua_spell_check(lua_State *lstate)
while (*str != NUL) {
attr = HLF_COUNT;
- len = spell_check(curwin, (char_u *)str, &attr, &capcol, false);
+ len = spell_check(curwin, (char *)str, &attr, &capcol, false);
assert(len <= INT_MAX);
if (attr != HLF_COUNT) {
diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c
index 5a82ae30b5..6ebca6d97e 100644
--- a/src/nvim/lua/stdlib.c
+++ b/src/nvim/lua/stdlib.c
@@ -1,62 +1,51 @@
// 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 <lauxlib.h>
#include <lua.h>
-#include <lualib.h>
-
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "auto/config.h"
#include "cjson/lua_cjson.h"
-#include "luv/luv.h"
#include "mpack/lmpack.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
-#include "nvim/api/vim.h"
#include "nvim/ascii.h"
-#include "nvim/assert.h"
#include "nvim/buffer_defs.h"
-#include "nvim/change.h"
-#include "nvim/cursor.h"
#include "nvim/eval.h"
-#include "nvim/eval/userfunc.h"
-#include "nvim/event/loop.h"
-#include "nvim/event/time.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/ex_eval.h"
-#include "nvim/ex_getln.h"
-#include "nvim/extmark.h"
-#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/macros.h"
#include "nvim/map.h"
+#include "nvim/mbyte.h"
#include "nvim/memline.h"
-#include "nvim/message.h"
-#include "nvim/msgpack_rpc/channel.h"
-#include "nvim/os/os.h"
+#include "nvim/memory.h"
+#include "nvim/pos.h"
#include "nvim/regexp.h"
-#include "nvim/regexp_defs.h"
-#include "nvim/screen.h"
#include "nvim/types.h"
-#include "nvim/undo.h"
-#include "nvim/version.h"
#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "lua/stdlib.c.generated.h"
#endif
-static int regex_match(lua_State *lstate, regprog_T **prog, char_u *str)
+static int regex_match(lua_State *lstate, regprog_T **prog, char *str)
{
regmatch_T rm;
rm.regprog = *prog;
rm.rm_ic = false;
- bool match = vim_regexec(&rm, (char *)str, 0);
+ bool match = vim_regexec(&rm, str, 0);
*prog = rm.regprog;
if (match) {
@@ -71,7 +60,7 @@ static int regex_match_str(lua_State *lstate)
{
regprog_T **prog = regex_check(lstate);
const char *str = luaL_checkstring(lstate, 2);
- int nret = regex_match(lstate, prog, (char_u *)str);
+ int nret = regex_match(lstate, prog, (char *)str);
if (!*prog) {
return luaL_error(lstate, "regex: internal error");
@@ -111,14 +100,14 @@ static int regex_match_line(lua_State *lstate)
return luaL_error(lstate, "invalid row");
}
- char_u *line = ml_get_buf(buf, rownr + 1, false);
- size_t len = STRLEN(line);
+ char *line = ml_get_buf(buf, rownr + 1, false);
+ size_t len = strlen(line);
if (start < 0 || (size_t)start > len) {
return luaL_error(lstate, "invalid start");
}
- char_u save = NUL;
+ char save = NUL;
if (end >= 0) {
if ((size_t)end > len || end < start) {
return luaL_error(lstate, "invalid end");
@@ -187,7 +176,7 @@ int nlua_str_utfindex(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
}
size_t codepoints = 0, codeunits = 0;
- mb_utflen((const char_u *)s1, (size_t)idx, &codepoints, &codeunits);
+ mb_utflen(s1, (size_t)idx, &codepoints, &codeunits);
lua_pushinteger(lstate, (long)codepoints);
lua_pushinteger(lstate, (long)codeunits);
@@ -209,7 +198,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(s1 + i, (int)(s1_len - i));
lua_pushinteger(lstate, (long)i + 1);
lua_rawseti(lstate, -2, (int)idx);
idx++;
@@ -277,8 +266,7 @@ int nlua_str_byteindex(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
use_utf16 = lua_toboolean(lstate, 3);
}
- ssize_t byteidx = mb_utf_index_to_bytes((const char_u *)s1, s1_len,
- (size_t)idx, use_utf16);
+ ssize_t byteidx = mb_utf_index_to_bytes(s1, s1_len, (size_t)idx, use_utf16);
if (byteidx == -1) {
return luaL_error(lstate, "index out of range");
}
@@ -304,8 +292,10 @@ int nlua_regex(lua_State *lstate)
nlua_push_errstr(lstate, "couldn't parse regex: %s", err.msg);
api_clear_error(&err);
return lua_error(lstate);
+ } else if (prog == NULL) {
+ nlua_push_errstr(lstate, "couldn't parse regex");
+ return lua_error(lstate);
}
- assert(prog);
regprog_T **p = lua_newuserdata(lstate, sizeof(regprog_T *));
*p = prog;
@@ -369,15 +359,21 @@ int nlua_setvar(lua_State *lstate)
return 0;
}
+ bool watched = tv_dict_is_watched(dict);
+
if (del) {
// Delete the key
if (di == NULL) {
// Doesn't exist, nothing to do
return 0;
- } else {
- // Delete the entry
- tv_dict_item_remove(dict, di);
}
+ // Notify watchers
+ if (watched) {
+ tv_dict_watcher_notify(dict, key.data, NULL, &di->di_tv);
+ }
+
+ // Delete the entry
+ tv_dict_item_remove(dict, di);
} else {
// Update the key
typval_T tv;
@@ -388,17 +384,29 @@ int nlua_setvar(lua_State *lstate)
return luaL_error(lstate, "Couldn't convert lua value");
}
+ typval_T oldtv = TV_INITIAL_VALUE;
+
if (di == NULL) {
// Need to create an entry
di = tv_dict_item_alloc_len(key.data, key.size);
tv_dict_add(dict, di);
} else {
+ if (watched) {
+ tv_copy(&di->di_tv, &oldtv);
+ }
// Clear the old value
tv_clear(&di->di_tv);
}
// Update the value
tv_copy(&tv, &di->di_tv);
+
+ // Notify watchers
+ if (watched) {
+ tv_dict_watcher_notify(dict, key.data, &tv, &oldtv);
+ tv_clear(&oldtv);
+ }
+
// Clear the temporary variable
tv_clear(&tv);
}
@@ -474,6 +482,48 @@ static int nlua_stricmp(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
return 1;
}
+/// Convert string from one encoding to another
+static int nlua_iconv(lua_State *lstate)
+{
+ int narg = lua_gettop(lstate);
+
+ if (narg < 3) {
+ return luaL_error(lstate, "Expected at least 3 arguments");
+ }
+
+ for (int i = 1; i <= 3; i++) {
+ if (lua_type(lstate, i) != LUA_TSTRING) {
+ return luaL_argerror(lstate, i, "expected string");
+ }
+ }
+
+ size_t str_len = 0;
+ const char *str = lua_tolstring(lstate, 1, &str_len);
+
+ char *from = enc_canonize(enc_skip((char *)lua_tolstring(lstate, 2, NULL)));
+ char *to = enc_canonize(enc_skip((char *)lua_tolstring(lstate, 3, NULL)));
+
+ vimconv_T vimconv;
+ vimconv.vc_type = CONV_NONE;
+ convert_setup_ext(&vimconv, from, false, to, false);
+
+ char *ret = string_convert(&vimconv, (char *)str, &str_len);
+
+ convert_setup(&vimconv, NULL, NULL);
+
+ xfree(from);
+ xfree(to);
+
+ if (ret == NULL) {
+ lua_pushnil(lstate);
+ } else {
+ lua_pushlstring(lstate, ret, str_len);
+ xfree(ret);
+ }
+
+ return 1;
+}
+
void nlua_state_add_stdlib(lua_State *const lstate, bool is_thread)
{
if (!is_thread) {
@@ -519,6 +569,11 @@ void nlua_state_add_stdlib(lua_State *const lstate, bool is_thread)
// vim.spell
luaopen_spell(lstate);
lua_setfield(lstate, -2, "spell");
+
+ // vim.iconv
+ // depends on p_ambw, p_emoji
+ lua_pushcfunction(lstate, &nlua_iconv);
+ lua_setfield(lstate, -2, "iconv");
}
// vim.mpack
diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c
index f0d847e352..56f4daed1a 100644
--- a/src/nvim/lua/treesitter.c
+++ b/src/nvim/lua/treesitter.c
@@ -6,20 +6,28 @@
// trees and nodes, and could be broken out as a reusable lua package
#include <assert.h>
-#include <inttypes.h>
#include <lauxlib.h>
+#include <limits.h>
#include <lua.h>
-#include <lualib.h>
#include <stdbool.h>
#include <stdint.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <uv.h>
+#include "klib/kvec.h"
#include "nvim/api/private/helpers.h"
-#include "nvim/buffer.h"
-#include "nvim/lib/kvec.h"
+#include "nvim/buffer_defs.h"
+#include "nvim/globals.h"
#include "nvim/lua/treesitter.h"
+#include "nvim/macros.h"
+#include "nvim/map.h"
#include "nvim/memline.h"
+#include "nvim/memory.h"
+#include "nvim/pos.h"
+#include "nvim/strings.h"
+#include "nvim/types.h"
#include "tree_sitter/api.h"
#define TS_META_PARSER "treesitter_parser"
@@ -85,6 +93,10 @@ static struct luaL_Reg node_meta[] = {
{ "prev_sibling", node_prev_sibling },
{ "next_named_sibling", node_next_named_sibling },
{ "prev_named_sibling", node_prev_named_sibling },
+ { "named_children", node_named_children },
+ { "root", node_root },
+ { "byte_length", node_byte_length },
+
{ NULL, NULL }
};
@@ -145,40 +157,50 @@ int tslua_has_language(lua_State *L)
return 1;
}
+// Creates the language into the internal language map.
+//
+// Returns true if the language is correctly loaded in the language map
int tslua_add_language(lua_State *L)
{
const char *path = luaL_checkstring(L, 1);
const char *lang_name = luaL_checkstring(L, 2);
+ const char *symbol_name = lang_name;
+
+ if (lua_gettop(L) >= 3 && !lua_isnil(L, 3)) {
+ symbol_name = luaL_checkstring(L, 3);
+ }
if (pmap_has(cstr_t)(&langs, lang_name)) {
- return 0;
+ lua_pushboolean(L, true);
+ return 1;
}
#define BUFSIZE 128
char symbol_buf[BUFSIZE];
- snprintf(symbol_buf, BUFSIZE, "tree_sitter_%s", lang_name);
+ snprintf(symbol_buf, BUFSIZE, "tree_sitter_%s", symbol_name);
#undef BUFSIZE
uv_lib_t lib;
if (uv_dlopen(path, &lib)) {
- snprintf((char *)IObuff, IOSIZE, "Failed to load parser: uv_dlopen: %s",
- uv_dlerror(&lib));
+ snprintf(IObuff, IOSIZE, "Failed to load parser for language '%s': uv_dlopen: %s",
+ lang_name, uv_dlerror(&lib));
uv_dlclose(&lib);
- lua_pushstring(L, (char *)IObuff);
+ lua_pushstring(L, IObuff);
return lua_error(L);
}
TSLanguage *(*lang_parser)(void);
if (uv_dlsym(&lib, symbol_buf, (void **)&lang_parser)) {
- snprintf((char *)IObuff, IOSIZE, "Failed to load parser: uv_dlsym: %s",
+ snprintf(IObuff, IOSIZE, "Failed to load parser: uv_dlsym: %s",
uv_dlerror(&lib));
uv_dlclose(&lib);
- lua_pushstring(L, (char *)IObuff);
+ lua_pushstring(L, IObuff);
return lua_error(L);
}
TSLanguage *lang = lang_parser();
if (lang == NULL) {
+ uv_dlclose(&lib);
return luaL_error(L, "Failed to load parser %s: internal error", path);
}
@@ -198,6 +220,19 @@ int tslua_add_language(lua_State *L)
return 1;
}
+int tslua_remove_lang(lua_State *L)
+{
+ const char *lang_name = luaL_checkstring(L, 1);
+ bool present = pmap_has(cstr_t)(&langs, lang_name);
+ if (present) {
+ char *key = (char *)pmap_key(cstr_t)(&langs, lang_name);
+ pmap_del(cstr_t)(&langs, lang_name);
+ xfree(key);
+ }
+ lua_pushboolean(L, present);
+ return 1;
+}
+
int tslua_inspect_lang(lua_State *L)
{
const char *lang_name = luaL_checkstring(L, 1);
@@ -302,8 +337,8 @@ static const char *input_cb(void *payload, uint32_t byte_index, TSPoint position
*bytes_read = 0;
return "";
}
- char *line = (char *)ml_get_buf(bp, (linenr_T)position.row + 1, false);
- size_t len = STRLEN(line);
+ char *line = ml_get_buf(bp, (linenr_T)position.row + 1, false);
+ size_t len = strlen(line);
if (position.column > len) {
*bytes_read = 0;
return "";
@@ -593,7 +628,7 @@ void push_tree(lua_State *L, TSTree *tree, bool do_copy)
lua_setfenv(L, -2); // [udata]
}
-static TSTree **tree_check(lua_State *L, uint16_t index)
+static TSTree **tree_check(lua_State *L, int index)
{
TSTree **ud = luaL_checkudata(L, index, TS_META_TREE);
return ud;
@@ -804,7 +839,7 @@ static int node_field(lua_State *L)
do {
const char *current_field = ts_tree_cursor_current_field_name(&cursor);
- if (current_field != NULL && !STRCMP(field_name, current_field)) {
+ if (current_field != NULL && !strcmp(field_name, current_field)) {
push_node(L, ts_tree_cursor_current_node(&cursor), 1); // [table, node]
lua_rawseti(L, -2, (int)++curr_index);
}
@@ -1036,6 +1071,57 @@ static int node_prev_named_sibling(lua_State *L)
return 1;
}
+static int node_named_children(lua_State *L)
+{
+ TSNode source;
+ if (!node_check(L, 1, &source)) {
+ return 0;
+ }
+ TSTreeCursor cursor = ts_tree_cursor_new(source);
+
+ lua_newtable(L);
+ int curr_index = 0;
+
+ if (ts_tree_cursor_goto_first_child(&cursor)) {
+ do {
+ TSNode node = ts_tree_cursor_current_node(&cursor);
+ if (ts_node_is_named(node)) {
+ push_node(L, node, 1);
+ lua_rawseti(L, -2, ++curr_index);
+ }
+ } while (ts_tree_cursor_goto_next_sibling(&cursor));
+ }
+
+ ts_tree_cursor_delete(&cursor);
+ return 1;
+}
+
+static int node_root(lua_State *L)
+{
+ TSNode node;
+ if (!node_check(L, 1, &node)) {
+ return 0;
+ }
+
+ TSNode root = ts_tree_root_node(node.tree);
+ push_node(L, root, 1);
+ return 1;
+}
+
+static int node_byte_length(lua_State *L)
+{
+ TSNode node;
+ if (!node_check(L, 1, &node)) {
+ return 0;
+ }
+
+ uint32_t start_byte = ts_node_start_byte(node);
+ uint32_t end_byte = ts_node_end_byte(node);
+
+ lua_pushnumber(L, end_byte - start_byte);
+ return 1;
+}
+
/// assumes the match table being on top of the stack
static void set_match(lua_State *L, TSQueryMatch *match, int nodeidx)
{
@@ -1195,8 +1281,8 @@ int tslua_parse_query(lua_State *L)
TSQuery *query = ts_query_new(lang, src, (uint32_t)len, &error_offset, &error_type);
if (!query) {
- return luaL_error(L, "query: %s at position %d",
- query_err_string(error_type), (int)error_offset);
+ return luaL_error(L, "query: %s at position %d for language %s",
+ query_err_string(error_type), (int)error_offset, lang_name);
}
TSQuery **ud = lua_newuserdata(L, sizeof(TSQuery *)); // [udata]
@@ -1288,7 +1374,7 @@ static int query_inspect(lua_State *L)
lua_rawseti(L, -2, nextitem++); // [retval, patterns, pat, pred]
}
// last predicate should have ended with TypeDone
- lua_pop(L, 1); // [retval, patters, pat]
+ lua_pop(L, 1); // [retval, patterns, pat]
lua_rawseti(L, -2, (int)i + 1); // [retval, patterns]
}
lua_setfield(L, -2, "patterns"); // [retval]
diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c
index b2b5dfedee..857b159af5 100644
--- a/src/nvim/lua/xdiff.c
+++ b/src/nvim/lua/xdiff.c
@@ -1,21 +1,26 @@
// 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 <errno.h>
#include <lauxlib.h>
#include <lua.h>
-#include <lualib.h>
-#include <stdio.h>
-#include <stdlib.h>
+#include <stdbool.h>
#include <string.h>
+#include "luaconf.h"
+#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
+#include "nvim/linematch.h"
#include "nvim/lua/converter.h"
#include "nvim/lua/executor.h"
#include "nvim/lua/xdiff.h"
+#include "nvim/macros.h"
+#include "nvim/memory.h"
#include "nvim/vim.h"
#include "xdiff/xdiff.h"
+#define COMPARED_BUFFER0 (1 << 0)
+#define COMPARED_BUFFER1 (1 << 1)
+
typedef enum {
kNluaXdiffModeUnified = 0,
kNluaXdiffModeOnHunkCB,
@@ -25,12 +30,81 @@ typedef enum {
typedef struct {
lua_State *lstate;
Error *err;
+ mmfile_t *ma;
+ mmfile_t *mb;
+ bool linematch;
+ bool iwhite;
} hunkpriv_t;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "lua/xdiff.c.generated.h"
#endif
+static void lua_pushhunk(lua_State *lstate, long start_a, long count_a, long start_b, long count_b)
+{
+ // Mimic extra offsets done by xdiff, see:
+ // src/xdiff/xemit.c:284
+ // src/xdiff/xutils.c:(356,368)
+ if (count_a > 0) {
+ start_a += 1;
+ }
+ if (count_b > 0) {
+ start_b += 1;
+ }
+ lua_createtable(lstate, 0, 0);
+ lua_pushinteger(lstate, start_a);
+ lua_rawseti(lstate, -2, 1);
+ lua_pushinteger(lstate, count_a);
+ lua_rawseti(lstate, -2, 2);
+ lua_pushinteger(lstate, start_b);
+ lua_rawseti(lstate, -2, 3);
+ lua_pushinteger(lstate, count_b);
+ lua_rawseti(lstate, -2, 4);
+ lua_rawseti(lstate, -2, (signed)lua_objlen(lstate, -2) + 1);
+}
+
+static void get_linematch_results(lua_State *lstate, mmfile_t *ma, mmfile_t *mb, long start_a,
+ long count_a, long start_b, long count_b, bool iwhite)
+{
+ // get the pointer to char of the start of the diff to pass it to linematch algorithm
+ const char *diff_begin[2] = { ma->ptr, mb->ptr };
+ int diff_length[2] = { (int)count_a, (int)count_b };
+
+ fastforward_buf_to_lnum(&diff_begin[0], start_a + 1);
+ fastforward_buf_to_lnum(&diff_begin[1], start_b + 1);
+
+ int *decisions = NULL;
+ size_t decisions_length = linematch_nbuffers(diff_begin, diff_length, 2, &decisions, iwhite);
+
+ long lnuma = start_a, lnumb = start_b;
+
+ long hunkstarta = lnuma;
+ long hunkstartb = lnumb;
+ long hunkcounta = 0;
+ long hunkcountb = 0;
+ for (size_t i = 0; i < decisions_length; i++) {
+ if (i && (decisions[i - 1] != decisions[i])) {
+ lua_pushhunk(lstate, hunkstarta, hunkcounta, hunkstartb, hunkcountb);
+
+ hunkstarta = lnuma;
+ hunkstartb = lnumb;
+ hunkcounta = 0;
+ hunkcountb = 0;
+ // create a new hunk
+ }
+ if (decisions[i] & COMPARED_BUFFER0) {
+ lnuma++;
+ hunkcounta++;
+ }
+ if (decisions[i] & COMPARED_BUFFER1) {
+ lnumb++;
+ hunkcountb++;
+ }
+ }
+ lua_pushhunk(lstate, hunkstarta, hunkcounta, hunkstartb, hunkcountb);
+ xfree(decisions);
+}
+
static int write_string(void *priv, mmbuffer_t *mb, int nbuf)
{
luaL_Buffer *buf = (luaL_Buffer *)priv;
@@ -52,30 +126,15 @@ static int write_string(void *priv, mmbuffer_t *mb, int nbuf)
// hunk_func callback used when opts.hunk_lines = true
static int hunk_locations_cb(long start_a, long count_a, long start_b, long count_b, void *cb_data)
{
- // Mimic extra offsets done by xdiff, see:
- // src/xdiff/xemit.c:284
- // src/xdiff/xutils.c:(356,368)
- if (count_a > 0) {
- start_a += 1;
- }
- if (count_b > 0) {
- start_b += 1;
+ hunkpriv_t *priv = (hunkpriv_t *)cb_data;
+ lua_State *lstate = priv->lstate;
+ if (priv->linematch) {
+ get_linematch_results(lstate, priv->ma, priv->mb, start_a, count_a, start_b, count_b,
+ priv->iwhite);
+ } else {
+ lua_pushhunk(lstate, start_a, count_a, start_b, count_b);
}
- lua_State *lstate = (lua_State *)cb_data;
- lua_createtable(lstate, 0, 0);
-
- lua_pushinteger(lstate, start_a);
- lua_rawseti(lstate, -2, 1);
- lua_pushinteger(lstate, count_a);
- lua_rawseti(lstate, -2, 2);
- lua_pushinteger(lstate, start_b);
- lua_rawseti(lstate, -2, 3);
- lua_pushinteger(lstate, count_b);
- lua_rawseti(lstate, -2, 4);
-
- lua_rawseti(lstate, -2, (signed)lua_objlen(lstate, -2) + 1);
-
return 0;
}
@@ -149,7 +208,7 @@ static bool check_xdiff_opt(ObjectType actType, ObjectType expType, const char *
}
static NluaXdiffMode process_xdl_diff_opts(lua_State *lstate, xdemitconf_t *cfg, xpparam_t *params,
- Error *err)
+ bool *linematch, Error *err)
{
const DictionaryOf(LuaRef) opts = nlua_pop_Dictionary(lstate, true, err);
@@ -205,6 +264,11 @@ static NluaXdiffMode process_xdl_diff_opts(lua_State *lstate, xdemitconf_t *cfg,
goto exit_1;
}
cfg->interhunkctxlen = v->data.integer;
+ } else if (strequal("linematch", k.data)) {
+ *linematch = api_object_to_bool(*v, "linematch", false, err);
+ if (ERROR_SET(err)) {
+ goto exit_1;
+ }
} else {
struct {
const char *name;
@@ -244,10 +308,8 @@ static NluaXdiffMode process_xdl_diff_opts(lua_State *lstate, xdemitconf_t *cfg,
if (had_on_hunk) {
mode = kNluaXdiffModeOnHunkCB;
- cfg->hunk_func = call_on_hunk_cb;
} else if (had_result_type_indices) {
mode = kNluaXdiffModeLocations;
- cfg->hunk_func = hunk_locations_cb;
}
exit_1:
@@ -268,6 +330,7 @@ int nlua_xdl_diff(lua_State *lstate)
xdemitconf_t cfg;
xpparam_t params;
xdemitcb_t ecb;
+ bool linematch = false;
CLEAR_FIELD(cfg);
CLEAR_FIELD(params);
@@ -280,7 +343,7 @@ int nlua_xdl_diff(lua_State *lstate)
return luaL_argerror(lstate, 3, "expected table");
}
- mode = process_xdl_diff_opts(lstate, &cfg, &params, &err);
+ mode = process_xdl_diff_opts(lstate, &cfg, &params, &linematch, &err);
if (ERROR_SET(&err)) {
goto exit_0;
@@ -288,7 +351,7 @@ int nlua_xdl_diff(lua_State *lstate)
}
luaL_Buffer buf;
- hunkpriv_t *priv = NULL;
+ hunkpriv_t priv;
switch (mode) {
case kNluaXdiffModeUnified:
luaL_buffinit(lstate, &buf);
@@ -296,14 +359,24 @@ int nlua_xdl_diff(lua_State *lstate)
ecb.out_line = write_string;
break;
case kNluaXdiffModeOnHunkCB:
- priv = xmalloc(sizeof(*priv));
- priv->lstate = lstate;
- priv->err = &err;
- ecb.priv = priv;
+ cfg.hunk_func = call_on_hunk_cb;
+ priv = (hunkpriv_t) {
+ .lstate = lstate,
+ .err = &err,
+ };
+ ecb.priv = &priv;
break;
case kNluaXdiffModeLocations:
+ cfg.hunk_func = hunk_locations_cb;
+ priv = (hunkpriv_t) {
+ .lstate = lstate,
+ .ma = &ma,
+ .mb = &mb,
+ .linematch = linematch,
+ .iwhite = (params.flags & XDF_IGNORE_WHITESPACE) > 0
+ };
+ ecb.priv = &priv;
lua_createtable(lstate, 0, 0);
- ecb.priv = lstate;
break;
}
@@ -314,8 +387,6 @@ int nlua_xdl_diff(lua_State *lstate)
}
}
- XFREE_CLEAR(priv);
-
exit_0:
if (ERROR_SET(&err)) {
luaL_where(lstate, 1);
diff --git a/src/nvim/macros.h b/src/nvim/macros.h
index a896a406d1..242e3c381a 100644
--- a/src/nvim/macros.h
+++ b/src/nvim/macros.h
@@ -11,7 +11,6 @@
#else
# ifndef INIT
# define INIT(...) __VA_ARGS__
-# define COMMA ,
# endif
#endif
@@ -31,7 +30,7 @@
/// @return `s, sizeof(s) - 1`
#define S_LEN(s) (s), (sizeof(s) - 1)
-/// LINEEMPTY() - return TRUE if the line is empty
+/// LINEEMPTY() - return true if the line is empty
#define LINEEMPTY(p) (*ml_get(p) == NUL)
// toupper() and tolower() that use the current locale.
@@ -54,7 +53,7 @@
#define ASCII_ISALNUM(c) (ASCII_ISALPHA(c) || ascii_isdigit(c))
// Returns empty string if it is NULL.
-#define EMPTY_IF_NULL(x) (char *)((x) ? (x) : (char_u *)"")
+#define EMPTY_IF_NULL(x) ((x) ? (x) : "")
/// Adjust chars in a language according to 'langmap' option.
/// NOTE that there is no noticeable overhead if 'langmap' is not set.
@@ -86,7 +85,7 @@
// 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)
-#elif defined(WIN32)
+#elif defined(MSWIN)
# 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)
@@ -104,7 +103,7 @@
// 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 *)(s), (char *)(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)))
@@ -178,14 +177,14 @@
// Type of uv_buf_t.len is platform-dependent.
// Related: https://github.com/libuv/libuv/pull/1236
-#if defined(WIN32)
+#if defined(MSWIN)
# define UV_BUF_LEN(x) (ULONG)(x)
#else
# define UV_BUF_LEN(x) (x)
#endif
// Type of read()/write() `count` param is platform-dependent.
-#if defined(WIN32)
+#if defined(MSWIN)
# define IO_COUNT(x) (unsigned)(x)
#else
# define IO_COUNT(x) (x)
diff --git a/src/nvim/main.c b/src/nvim/main.c
index d06b475934..bbe877356d 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -3,47 +3,51 @@
#define EXTERN
#include <assert.h>
-#include <msgpack.h>
+#include <limits.h>
+#include <msgpack/pack.h>
#include <stdbool.h>
#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
+#include "auto/config.h"
#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/autocmd.h"
#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
#include "nvim/channel.h"
-#include "nvim/charset.h"
#include "nvim/decoration.h"
#include "nvim/decoration_provider.h"
#include "nvim/diff.h"
#include "nvim/drawscreen.h"
#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/event/multiqueue.h"
+#include "nvim/event/stream.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/garray.h"
+#include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/grid.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/insexpand.h"
+#include "nvim/keycodes.h"
+#include "nvim/locale.h"
+#include "nvim/log.h"
#include "nvim/lua/executor.h"
+#include "nvim/macros.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"
+#include "nvim/memfile_defs.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
@@ -52,32 +56,37 @@
#include "nvim/normal.h"
#include "nvim/ops.h"
#include "nvim/option.h"
+#include "nvim/option_defs.h"
#include "nvim/optionstr.h"
#include "nvim/os/fileio.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
-#include "nvim/os/os_defs.h"
+#include "nvim/os/stdpaths_defs.h"
#include "nvim/os/time.h"
-#include "nvim/os_unix.h"
#include "nvim/path.h"
#include "nvim/popupmenu.h"
+#include "nvim/pos.h"
#include "nvim/profile.h"
#include "nvim/quickfix.h"
#include "nvim/runtime.h"
#include "nvim/shada.h"
#include "nvim/sign.h"
-#include "nvim/state.h"
+#include "nvim/statusline.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
+#include "nvim/terminal.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
+#include "nvim/ui_client.h"
#include "nvim/ui_compositor.h"
#include "nvim/version.h"
+#include "nvim/vim.h"
#include "nvim/window.h"
-#ifdef WIN32
+#ifdef MSWIN
# include "nvim/os/os_win_console.h"
#endif
+#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/api/ui.h"
#include "nvim/event/loop.h"
@@ -86,22 +95,22 @@
#include "nvim/msgpack_rpc/helpers.h"
#include "nvim/msgpack_rpc/server.h"
#include "nvim/os/signal.h"
-#ifndef WIN32
-# include "nvim/os/pty_process_unix.h"
-#endif
-#include "nvim/api/extmark.h"
// values for "window_layout"
-#define WIN_HOR 1 // "-o" horizontally split windows
-#define WIN_VER 2 // "-O" vertically split windows
-#define WIN_TABS 3 // "-p" windows on tab pages
+enum {
+ WIN_HOR = 1, // "-o" horizontally split windows
+ WIN_VER = 2, // "-O" vertically split windows
+ WIN_TABS = 3, // "-p" windows on tab pages
+};
// Values for edit_type.
-#define EDIT_NONE 0 // no edit type yet
-#define EDIT_FILE 1 // file name argument[s] given, use argument list
-#define EDIT_STDIN 2 // read file from stdin
-#define EDIT_TAG 3 // tag name argument given, use tagname
-#define EDIT_QF 4 // start in quickfix mode
+enum {
+ EDIT_NONE = 0, // no edit type yet
+ EDIT_FILE = 1, // file name argument[s] given, use argument list
+ EDIT_STDIN = 2, // read file from stdin
+ EDIT_TAG = 3, // tag name argument given, use tagname
+ EDIT_QF = 4, // start in quickfix mode
+};
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "main.c.generated.h"
@@ -170,7 +179,7 @@ void early_init(mparm_T *paramp)
runtime_init();
highlight_init();
-#ifdef WIN32
+#ifdef MSWIN
OSVERSIONINFO ovi;
ovi.dwOSVersionInfoSize = sizeof(ovi);
GetVersionEx(&ovi);
@@ -187,11 +196,11 @@ void early_init(mparm_T *paramp)
init_locale();
#endif
- // Allocate the first window and buffer.
- // Can't do anything without it, exit when it fails.
- if (!win_alloc_first()) {
- os_exit(0);
- }
+ // tabpage local options (p_ch) must be set before allocating first tabpage.
+ set_init_tablocal();
+
+ // Allocate the first tabpage, window and buffer.
+ win_alloc_first();
TIME_MSG("init first window");
alist_init(&global_alist); // Init the argument list to empty.
@@ -212,13 +221,13 @@ void early_init(mparm_T *paramp)
#ifdef MAKE_LIB
int nvim_main(int argc, char **argv); // silence -Wmissing-prototypes
int nvim_main(int argc, char **argv)
-#elif defined(WIN32)
+#elif defined(MSWIN)
int wmain(int argc, wchar_t **argv_w) // multibyte args on Windows. #7060
#else
int main(int argc, char **argv)
#endif
{
-#if defined(WIN32) && !defined(MAKE_LIB)
+#if defined(MSWIN) && !defined(MAKE_LIB)
char **argv = xmalloc((size_t)argc * sizeof(char *));
for (int i = 0; i < argc; i++) {
char *buf = NULL;
@@ -230,10 +239,10 @@ int main(int argc, char **argv)
argv0 = argv[0];
- char_u *fname = NULL; // file name from command line
+ char *fname = NULL; // file name from command line
mparm_T params; // various parameters passed between
// main() and other functions.
- char_u *cwd = NULL; // current working dir on startup
+ char *cwd = NULL; // current working dir on startup
time_init();
// Many variables are in `params` so that we can pass them around easily.
@@ -267,8 +276,7 @@ int main(int argc, char **argv)
// argument list "global_alist".
command_line_scan(&params);
- nlua_init();
-
+ nlua_init(argv, argc, params.lua_arg0);
TIME_MSG("init lua interpreter");
if (embedded_mode) {
@@ -278,9 +286,35 @@ int main(int argc, char **argv)
}
}
- server_init(params.listen_addr);
+#ifdef MSWIN
+ // on windows we use CONIN special file, thus we don't know this yet.
+ bool has_term = true;
+#else
+ bool has_term = (stdin_isatty || stdout_isatty || stderr_isatty);
+#endif
+ bool use_builtin_ui = (has_term && !headless_mode && !embedded_mode && !silent_mode);
+
+ // don't bind the server yet, if we are using builtin ui.
+ // This will be done when nvim server has been forked from the ui process
+ if (!use_builtin_ui) {
+ server_init(params.listen_addr);
+ }
+
if (params.remote) {
- remote_request(&params, params.remote, params.server_addr, argc, argv);
+ remote_request(&params, params.remote, params.server_addr, argc, argv,
+ use_builtin_ui);
+ }
+
+ bool remote_ui = (ui_client_channel_id != 0);
+
+ if (use_builtin_ui && !remote_ui) {
+ ui_client_forward_stdin = !stdin_isatty;
+ uint64_t rv = ui_client_start_server(params.argc, params.argv);
+ if (!rv) {
+ os_errmsg("Failed to start Nvim server!\n");
+ getout(1);
+ }
+ ui_client_channel_id = rv;
}
if (GARGCOUNT > 0) {
@@ -316,7 +350,7 @@ int main(int argc, char **argv)
assert(p_ch >= 0 && Rows >= p_ch && Rows - p_ch <= INT_MAX);
cmdline_row = (int)(Rows - p_ch);
msg_row = cmdline_row;
- screenalloc(); // allocate screen buffers
+ default_grid_alloc(); // allocate screen buffers
set_init_2(headless_mode);
TIME_MSG("inits 2");
@@ -331,57 +365,57 @@ int main(int argc, char **argv)
debug_break_level = params.use_debug_break_level;
// Read ex-commands if invoked with "-es".
- if (!params.input_isatty && !params.input_neverscript
- && silent_mode && exmode_active) {
- input_start(STDIN_FILENO);
+ if (!stdin_isatty && !params.input_istext && silent_mode && exmode_active) {
+ input_start();
+ }
+
+ if (ui_client_channel_id) {
+ ui_client_run(remote_ui); // NORETURN
}
// Wait for UIs to set up Nvim or show early messages
// and prompts (--cmd, swapfile dialog, …).
bool use_remote_ui = (embedded_mode && !headless_mode);
- bool use_builtin_ui = (!headless_mode && !embedded_mode && !silent_mode);
- if (use_remote_ui || use_builtin_ui) {
+ if (use_remote_ui) {
TIME_MSG("waiting for UI");
- if (use_remote_ui) {
- remote_ui_wait_for_attach();
- } else {
- ui_builtin_start();
- }
+ remote_ui_wait_for_attach();
TIME_MSG("done waiting for UI");
-
- // prepare screen now, so external UIs can display messages
- starting = NO_BUFFERS;
- screenclear();
- TIME_MSG("init screen for UI");
+ firstwin->w_prev_height = firstwin->w_height; // may have changed
}
- if (ui_client_channel_id) {
- ui_client_init(ui_client_channel_id);
- ui_client_execute(ui_client_channel_id);
- abort(); // unreachable
+ // prepare screen now
+ starting = NO_BUFFERS;
+ screenclear();
+ win_new_screensize();
+ TIME_MSG("clear screen");
+
+ // Handle "foo | nvim". EDIT_FILE may be overwritten now. #6299
+ if (edit_stdin(&params)) {
+ params.edit_type = EDIT_STDIN;
}
+ open_script_files(&params);
+
// 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);
+ Object o = NLUA_EXEC_STATIC("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");
+ bool vimrc_none = strequal(params.use_vimrc, "NONE");
// Reset 'loadplugins' for "-u NONE" before "--cmd" arguments.
// Allows for setting 'loadplugins' there.
- if (vimrc_none) {
- // When using --clean we still want to load plugins
- p_lpl = params.clean;
- }
+ // For --clean we still want to load plugins.
+ p_lpl = vimrc_none ? params.clean : p_lpl;
// Execute --cmd arguments.
exe_pre_commands(&params);
@@ -434,10 +468,8 @@ int main(int argc, char **argv)
p_ut = 1;
}
- //
// Read in registers, history etc, from the ShaDa file.
// This is where v:oldfiles gets filled.
- //
if (*p_shada != NUL) {
shada_read_everything(NULL, false, true);
TIME_MSG("reading ShaDa");
@@ -470,20 +502,13 @@ 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 || stdin_fd >= 0) && !recoverymode) {
+ if (params.edit_type == EDIT_STDIN && !recoverymode) {
read_stdin();
}
setmouse(); // may start using the mouse
- if (exmode_active || use_remote_ui || use_builtin_ui) {
- // Don't clear the screen when starting in Ex mode, or when a UI might have
- // displayed messages.
- redraw_later(curwin, VALID);
- } else {
- screenclear(); // clear screen
- TIME_MSG("clearing screen");
- }
+ redraw_later(curwin, UPD_VALID);
no_wait_return = true;
@@ -508,7 +533,7 @@ int main(int argc, char **argv)
// When started with "-q errorfile" jump to first error now.
if (params.edit_type == EDIT_QF) {
- qf_jump(NULL, 0, 0, FALSE);
+ qf_jump(NULL, 0, 0, false);
TIME_MSG("jump to first error");
}
@@ -520,7 +545,9 @@ int main(int argc, char **argv)
if (params.diff_mode) {
// set options in each window for "nvim -d".
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- diff_win_options(wp, TRUE);
+ if (!wp->w_arg_idx_invalid) {
+ diff_win_options(wp, true);
+ }
}
}
@@ -529,7 +556,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((char_u *)params.tagname);
+ handle_tag(params.tagname);
// Execute any "+", "-c" and "-S" arguments.
if (params.n_commands > 0) {
@@ -539,7 +566,7 @@ int main(int argc, char **argv)
starting = 0;
RedrawingDisabled = 0;
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
no_wait_return = false;
// 'autochdir' has been postponed.
@@ -553,6 +580,13 @@ int main(int argc, char **argv)
TIME_MSG("UIEnter autocommands");
}
+#ifdef MSWIN
+ if (use_builtin_ui) {
+ os_icon_init();
+ }
+ os_title_save();
+#endif
+
// Adjust default register name for "unnamed" in 'clipboard'. Can only be
// done after the clipboard is available and all initial commands that may
// modify the 'clipboard' setting have run; i.e. just before entering the
@@ -578,13 +612,19 @@ int main(int argc, char **argv)
(void)eval_has_provider("clipboard");
}
+ if (params.luaf != NULL) {
+ bool lua_ok = nlua_exec_file(params.luaf);
+ TIME_MSG("executing Lua -l script");
+ getout(lua_ok ? 0 : 1);
+ }
+
TIME_MSG("before starting main loop");
ILOG("starting main loop");
// Main loop: never returns.
normal_enter(false, false);
-#if defined(WIN32) && !defined(MAKE_LIB)
+#if defined(MSWIN) && !defined(MAKE_LIB)
xfree(argv);
#endif
return 0;
@@ -595,15 +635,19 @@ void os_exit(int r)
{
exiting = true;
- ui_flush();
- ui_call_stop();
- ml_close_all(true); // remove all memfiles
+ if (ui_client_channel_id) {
+ ui_client_stop();
+ } else {
+ ui_flush();
+ ui_call_stop();
+ ml_close_all(true); // remove all memfiles
+ }
if (!event_teardown() && r == 0) {
r = 1; // Exit with error if main_loop did not teardown gracefully.
}
- if (input_global_fd() >= 0) {
- stream_set_blocking(input_global_fd(), true); // normalize stream (#2598)
+ if (used_stdin) {
+ stream_set_blocking(STDIN_FILENO, true); // normalize stream (#2598)
}
ILOG("Nvim exit: %d", r);
@@ -621,9 +665,8 @@ void getout(int exitval)
{
exiting = true;
- // When running in Ex mode an error causes us to exit with a non-zero exit
- // code. POSIX requires this, although it's not 100% clear from the
- // standard.
+ // On error during Ex mode, exit with a non-zero code.
+ // POSIX requires this, although it's not 100% clear from the standard.
if (exmode_active) {
exitval += ex_exitval;
}
@@ -713,8 +756,9 @@ void getout(int exitval)
if (did_emsg) {
// give the user a chance to read the (error) message
- no_wait_return = FALSE;
- wait_return(FALSE);
+ no_wait_return = false;
+ // TODO(justinmk): this may call getout(0), clobbering exitval...
+ wait_return(false);
}
// Position the cursor again, the autocommands may have moved it
@@ -722,21 +766,25 @@ void getout(int exitval)
// Apply 'titleold'.
if (p_title && *p_titleold != NUL) {
- ui_call_set_title(cstr_as_string((char *)p_titleold));
+ ui_call_set_title(cstr_as_string(p_titleold));
}
- cs_end();
if (garbage_collect_at_exit) {
garbage_collect(false);
}
+#ifdef MSWIN
+ // Restore Windows console icon before exiting.
+ os_icon_set(NULL, NULL);
+ os_title_reset();
+#endif
+
os_exit(exitval);
}
-/// Preserve files and exit.
-/// @note IObuff must contain a message.
-/// @note This may be called from deadly_signal() in a signal handler, avoid
-/// unsafe functions, such as allocating memory.
+/// Preserve files, print contents of `IObuff`, and exit 1.
+///
+/// May be called from deadly_signal().
void preserve_exit(void)
FUNC_ATTR_NORETURN
{
@@ -745,9 +793,9 @@ void preserve_exit(void)
// Prevent repeated calls into this method.
if (really_exiting) {
- if (input_global_fd() >= 0) {
+ if (used_stdin) {
// normalize stream (#2598)
- stream_set_blocking(input_global_fd(), true);
+ stream_set_blocking(STDIN_FILENO, true);
}
exit(2);
}
@@ -755,15 +803,15 @@ void preserve_exit(void)
really_exiting = true;
// Ignore SIGHUP while we are already exiting. #9274
signal_reject_deadly();
- mch_errmsg(IObuff);
- mch_errmsg("\n");
+ os_errmsg(IObuff);
+ os_errmsg("\n");
ui_flush();
ml_close_notmod(); // close all not-modified buffers
FOR_ALL_BUFFERS(buf) {
if (buf->b_ml.ml_mfp != NULL && buf->b_ml.ml_mfp->mf_fname != NULL) {
- mch_errmsg("Vim: preserving files...\r\n");
+ os_errmsg("Vim: preserving files...\r\n");
ui_flush();
ml_sync_all(false, false, true); // preserve all swap files
break;
@@ -772,7 +820,7 @@ void preserve_exit(void)
ml_close_all(false); // close all memfiles, without deleting
- mch_errmsg("Vim: Finished.\r\n");
+ os_errmsg("Vim: Finished.\r\n");
getout(1);
}
@@ -801,30 +849,6 @@ static int get_number_arg(const char *p, int *idx, int def)
return def;
}
-#if defined(HAVE_LOCALE_H)
-/// Setup to use the current locale (for ctype() and many other things).
-static void init_locale(void)
-{
- setlocale(LC_ALL, "");
-
-# ifdef LC_NUMERIC
- // Make sure strtod() uses a decimal point, not a comma.
- setlocale(LC_NUMERIC, "C");
-# endif
-
- char localepath[MAXPATHL] = { 0 };
- snprintf(localepath, sizeof(localepath), "%s", get_vim_var_str(VV_PROGPATH));
- char *tail = path_tail_with_sep(localepath);
- *tail = NUL;
- tail = path_tail(localepath);
- xstrlcpy(tail, "share/locale",
- sizeof(localepath) - (size_t)(tail - localepath));
- bindtextdomain(PROJECT_NAME, localepath);
- textdomain(PROJECT_NAME);
- TIME_MSG("locale set");
-}
-#endif
-
static uint64_t server_connect(char *server_addr, const char **errmsg)
{
if (server_addr == NULL) {
@@ -845,16 +869,25 @@ static uint64_t server_connect(char *server_addr, const char **errmsg)
/// Handle remote subcommands
static void remote_request(mparm_T *params, int remote_args, char *server_addr, int argc,
- char **argv)
+ char **argv, bool ui_only)
{
+ bool is_ui = strequal(argv[remote_args], "--remote-ui");
+ if (ui_only && !is_ui) {
+ // TODO(bfredl): this implies always starting the TUI.
+ // if we be smart we could delay this past should_exit
+ return;
+ }
+
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 (is_ui) {
if (!chan) {
- emsg(connect_error);
- exit(1);
+ os_errmsg("Remote ui failed to start: ");
+ os_errmsg(connect_error);
+ os_errmsg("\n");
+ os_exit(1);
}
ui_client_channel_id = chan;
@@ -878,15 +911,15 @@ static void remote_request(mparm_T *params, int remote_args, char *server_addr,
Object o = nlua_exec(s, a, &err);
api_free_array(a);
if (ERROR_SET(&err)) {
- mch_errmsg(err.msg);
- mch_errmsg("\n");
+ os_errmsg(err.msg);
+ os_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_errmsg("vim._cs_remote returned unexpected value\n");
os_exit(2);
}
@@ -896,28 +929,28 @@ static void remote_request(mparm_T *params, int remote_args, char *server_addr,
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_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_errmsg(rvobj.data.dictionary.items[i].value.data.string.data);
+ os_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_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_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_errmsg("vim._cs_remote didn't return a value for should_exit or tabbed, bailing\n");
os_exit(2);
}
api_free_object(o);
@@ -933,14 +966,14 @@ static void remote_request(mparm_T *params, int remote_args, char *server_addr,
/// Decides whether text (as opposed to commands) will be read from stdin.
/// @see EDIT_STDIN
-static bool edit_stdin(bool explicit, mparm_T *parmp)
+static bool edit_stdin(mparm_T *parmp)
{
bool implicit = !headless_mode
- && !embedded_mode
- && (!exmode_active || parmp->input_neverscript)
- && !parmp->input_isatty
- && scriptin[0] == NULL; // `-s -` was not given.
- return explicit || implicit;
+ && !(embedded_mode && stdin_fd <= 0)
+ && (!exmode_active || parmp->input_istext)
+ && !stdin_isatty
+ && parmp->scriptin == NULL; // `-s -` was not given.
+ return parmp->had_stdin_file || implicit;
}
/// Scan the command line arguments.
@@ -949,7 +982,6 @@ static void command_line_scan(mparm_T *parmp)
int argc = parmp->argc;
char **argv = parmp->argv;
int argv_idx; // index in argv[n][]
- bool had_stdin_file = false; // found explicit "-" argument
bool had_minmin = false; // found "--" argument
int want_argument; // option argument with argument
long n;
@@ -969,9 +1001,9 @@ static void command_line_scan(mparm_T *parmp)
} else {
parmp->commands[parmp->n_commands++] = &(argv[0][1]);
}
-
- // Optional argument.
} else if (argv[0][0] == '-' && !had_minmin) {
+ // Optional argument.
+
want_argument = false;
char c = argv[0][argv_idx++];
switch (c) {
@@ -981,17 +1013,15 @@ static void command_line_scan(mparm_T *parmp)
silent_mode = true;
parmp->no_swap_file = true;
} else {
- if (parmp->edit_type != EDIT_NONE
- && parmp->edit_type != EDIT_FILE
- && parmp->edit_type != EDIT_STDIN) {
+ if (parmp->edit_type > EDIT_STDIN) {
mainerr(err_too_many_args, argv[0]);
}
- had_stdin_file = true;
+ parmp->had_stdin_file = true;
parmp->edit_type = EDIT_STDIN;
}
argv_idx = -1; // skip to next argument
break;
- case '-': // "--" don't take any more option arguments
+ case '-': // "--" No more option arguments.
// "--help" give help message
// "--version" give version message
// "--noplugin[s]" skip plugins
@@ -1052,7 +1082,7 @@ static void command_line_scan(mparm_T *parmp)
} else if (STRNICMP(argv[0] + argv_idx, "clean", 5) == 0) {
parmp->use_vimrc = "NONE";
parmp->clean = true;
- set_option_value("shadafile", 0L, "NONE", 0);
+ set_option_value_give_err("shadafile", 0L, "NONE", 0);
} else if (STRNICMP(argv[0] + argv_idx, "luamod-dev", 9) == 0) {
nlua_disable_preload = true;
} else {
@@ -1066,7 +1096,7 @@ static void command_line_scan(mparm_T *parmp)
}
break;
case 'A': // "-A" start in Arabic mode.
- set_option_value("arabic", 1L, NULL, 0);
+ set_option_value_give_err("arabic", 1L, NULL, 0);
break;
case 'b': // "-b" binary mode.
// Needs to be effective before expanding file names, because
@@ -1087,7 +1117,7 @@ static void command_line_scan(mparm_T *parmp)
break;
case 'E': // "-E" Ex mode
exmode_active = true;
- parmp->input_neverscript = true;
+ parmp->input_istext = true;
break;
case 'f': // "-f" GUI: run in foreground.
break;
@@ -1097,11 +1127,7 @@ static void command_line_scan(mparm_T *parmp)
os_exit(0);
case 'H': // "-H" start in Hebrew mode: rl + hkmap set.
p_hkmap = true;
- set_option_value("rl", 1L, NULL, 0);
- break;
- case 'l': // "-l" lisp mode, 'lisp' and 'showmatch' on.
- set_option_value("lisp", 1L, NULL, 0);
- p_sm = true;
+ set_option_value_give_err("rl", 1L, NULL, 0);
break;
case 'M': // "-M" no changes or writing of files
reset_modifiable();
@@ -1181,15 +1207,15 @@ static void command_line_scan(mparm_T *parmp)
// default is 10: a little bit verbose
p_verbose = get_number_arg(argv[0], &argv_idx, 10);
if (argv[0][argv_idx] != NUL) {
- set_option_value("verbosefile", 0L, argv[0] + argv_idx, 0);
- argv_idx = (int)STRLEN(argv[0]);
+ set_option_value_give_err("verbosefile", 0L, argv[0] + argv_idx, 0);
+ argv_idx = (int)strlen(argv[0]);
}
break;
case 'w': // "-w{number}" set window height
// "-w {scriptout}" write to script
- if (ascii_isdigit(((char_u *)argv[0])[argv_idx])) {
+ if (ascii_isdigit((argv[0])[argv_idx])) {
n = get_number_arg(argv[0], &argv_idx, 10);
- set_option_value("window", n, NULL, 0);
+ set_option_value_give_err("window", n, NULL, 0);
break;
}
want_argument = true;
@@ -1207,6 +1233,7 @@ static void command_line_scan(mparm_T *parmp)
FALLTHROUGH;
case 'S': // "-S {file}" execute Vim script
case 'i': // "-i {shada}" use for ShaDa file
+ case 'l': // "-l {file}" Lua mode
case 'u': // "-u {vimrc}" vim inits file
case 'U': // "-U {gvimrc}" gvim inits file
case 'W': // "-W {scriptout}" overwrite
@@ -1252,7 +1279,7 @@ static void command_line_scan(mparm_T *parmp)
a = argv[0];
}
- size_t s_size = STRLEN(a) + 9;
+ size_t s_size = strlen(a) + 9;
char *s = xmalloc(s_size);
snprintf(s, s_size, "so %s", a);
parmp->cmds_tofree[parmp->n_commands] = true;
@@ -1284,41 +1311,37 @@ static void command_line_scan(mparm_T *parmp)
break;
case 'i': // "-i {shada}" use for shada
- set_option_value("shadafile", 0L, argv[0], 0);
+ set_option_value_give_err("shadafile", 0L, argv[0], 0);
break;
- case 's': { // "-s {scriptin}" read from script file
- if (scriptin[0] != NULL) {
+ case 'l': // "-l" Lua script: args after "-l".
+ headless_mode = true;
+ silent_mode = true;
+ p_verbose = 1;
+ parmp->no_swap_file = true;
+ parmp->use_vimrc = parmp->use_vimrc ? parmp->use_vimrc : "NONE";
+ if (p_shadafile == NULL || *p_shadafile == NUL) {
+ set_option_value_give_err("shadafile", 0L, "NONE", 0);
+ }
+ parmp->luaf = argv[0];
+ argc--;
+ if (argc > 0) { // Lua args after "-l <file>".
+ parmp->lua_arg0 = parmp->argc - argc;
+ argc = 0;
+ }
+ break;
+
+ case 's': // "-s {scriptin}" read from script file
+ if (parmp->scriptin != NULL) {
scripterror:
- vim_snprintf((char *)IObuff, IOSIZE,
+ vim_snprintf(IObuff, IOSIZE,
_("Attempt to open script file again: \"%s %s\"\n"),
argv[-1], argv[0]);
- mch_errmsg((const char *)IObuff);
- os_exit(2);
- }
- int error;
- if (strequal(argv[0], "-")) {
- const int stdin_dup_fd = os_dup(STDIN_FILENO);
-#ifdef WIN32
- // Replace the original stdin with the console input handle.
- os_replace_stdin_to_conin();
-#endif
- FileDescriptor *const stdin_dup = file_open_fd_new(&error, stdin_dup_fd,
- kFileReadOnly|kFileNonBlocking);
- assert(stdin_dup != NULL);
- scriptin[0] = stdin_dup;
- } else if ((scriptin[0] =
- file_open_new(&error, argv[0], kFileReadOnly|kFileNonBlocking,
- 0)) == NULL) {
- vim_snprintf((char *)IObuff, IOSIZE,
- _("Cannot open for reading: \"%s\": %s\n"),
- argv[0], os_strerror(error));
- mch_errmsg((const char *)IObuff);
+ os_errmsg(IObuff);
os_exit(2);
}
- save_typebuf();
+ parmp->scriptin = argv[0];
break;
- }
case 't': // "-t {tag}"
parmp->tagname = argv[0];
@@ -1331,35 +1354,27 @@ scripterror:
case 'w': // "-w {nr}" 'window' value
// "-w {scriptout}" append to script file
- if (ascii_isdigit(*((char_u *)argv[0]))) {
+ if (ascii_isdigit(*(argv[0]))) {
argv_idx = 0;
n = get_number_arg(argv[0], &argv_idx, 10);
- set_option_value("window", n, NULL, 0);
+ set_option_value_give_err("window", n, NULL, 0);
argv_idx = -1;
break;
}
FALLTHROUGH;
case 'W': // "-W {scriptout}" overwrite script file
- if (scriptout != NULL) {
+ if (parmp->scriptout != NULL) {
goto scripterror;
}
- if ((scriptout = os_fopen(argv[0], c == 'w' ? APPENDBIN : WRITEBIN))
- == NULL) {
- mch_errmsg(_("Cannot open for script output: \""));
- mch_errmsg(argv[0]);
- mch_errmsg("\"\n");
- os_exit(2);
- }
- break;
+ parmp->scriptout = argv[0];
+ parmp->scriptout_append = (c == 'w');
}
}
} else { // File name argument.
argv_idx = -1; // skip to next argument
// Check for only one type of editing.
- if (parmp->edit_type != EDIT_NONE
- && parmp->edit_type != EDIT_FILE
- && parmp->edit_type != EDIT_STDIN) {
+ if (parmp->edit_type > EDIT_STDIN) {
mainerr(err_too_many_args, argv[0]);
}
parmp->edit_type = EDIT_FILE;
@@ -1368,8 +1383,8 @@ scripterror:
ga_grow(&global_alist.al_ga, 1);
char *p = xstrdup(argv[0]);
- if (parmp->diff_mode && os_isdir((char_u *)p) && GARGCOUNT > 0
- && !os_isdir((char_u *)alist_name(&GARGLIST[0]))) {
+ if (parmp->diff_mode && os_isdir(p) && GARGCOUNT > 0
+ && !os_isdir(alist_name(&GARGLIST[0]))) {
char *r = concat_fnames(p, path_tail(alist_name(&GARGLIST[0])), true);
xfree(p);
p = r;
@@ -1380,7 +1395,7 @@ scripterror:
path_fix_case(p);
#endif
- int alist_fnum_flag = edit_stdin(had_stdin_file, parmp)
+ int alist_fnum_flag = edit_stdin(parmp)
? 1 // add buffer nr after exp.
: 2; // add buffer number now and use curbuf
alist_add(&global_alist, p, alist_fnum_flag);
@@ -1395,31 +1410,25 @@ scripterror:
}
}
- if (embedded_mode && silent_mode) {
- mainerr(_("--embed conflicts with -es/-Es"), NULL);
+ if (embedded_mode && (silent_mode || parmp->luaf)) {
+ mainerr(_("--embed conflicts with -es/-Es/-l"), NULL);
}
// If there is a "+123" or "-c" command, set v:swapcommand to the first one.
if (parmp->n_commands > 0) {
- const size_t swcmd_len = STRLEN(parmp->commands[0]) + 3;
+ const size_t swcmd_len = strlen(parmp->commands[0]) + 3;
char *const swcmd = xmalloc(swcmd_len);
snprintf(swcmd, swcmd_len, ":%s\r", parmp->commands[0]);
set_vim_var_string(VV_SWAPCOMMAND, swcmd, -1);
xfree(swcmd);
}
- // Handle "foo | nvim". EDIT_FILE may be overwritten now. #6299
- if (edit_stdin(had_stdin_file, parmp)) {
- parmp->edit_type = EDIT_STDIN;
- }
-
TIME_MSG("parsing arguments");
}
-/*
- * Many variables are in "params" so that we can pass them to invoked
- * functions without a lot of arguments. "argc" and "argv" are also
- * copied, so that they can be changed. */
+// Many variables are in "params" so that we can pass them to invoked
+// functions without a lot of arguments. "argc" and "argv" are also
+// copied, so that they can be changed.
static void init_params(mparm_T *paramp, int argc, char **argv)
{
CLEAR_POINTER(paramp);
@@ -1430,6 +1439,8 @@ static void init_params(mparm_T *paramp, int argc, char **argv)
paramp->listen_addr = NULL;
paramp->server_addr = NULL;
paramp->remote = 0;
+ paramp->luaf = NULL;
+ paramp->lua_arg0 = -1;
}
/// Initialize global startuptime file if "--startuptime" passed as an argument.
@@ -1442,25 +1453,13 @@ static void init_startuptime(mparm_T *paramp)
break;
}
}
-
- starttime = time(NULL);
}
static void check_and_set_isatty(mparm_T *paramp)
{
- stdin_isatty
- = paramp->input_isatty = os_isatty(STDIN_FILENO);
- stdout_isatty
- = paramp->output_isatty = os_isatty(STDOUT_FILENO);
- paramp->err_isatty = os_isatty(STDERR_FILENO);
-#ifndef WIN32
- int tty_fd = paramp->input_isatty
- ? STDIN_FILENO
- : (paramp->output_isatty
- ? STDOUT_FILENO
- : (paramp->err_isatty ? STDERR_FILENO : -1));
- pty_process_save_termios(tty_fd);
-#endif
+ stdin_isatty = os_isatty(STDIN_FILENO);
+ stdout_isatty = os_isatty(STDOUT_FILENO);
+ stderr_isatty = os_isatty(STDERR_FILENO);
TIME_MSG("window checked");
}
@@ -1478,7 +1477,7 @@ static void init_path(const char *exename)
set_vim_var_string(VV_PROGPATH, exepath, -1);
set_vim_var_string(VV_PROGNAME, path_tail(exename), -1);
-#ifdef WIN32
+#ifdef MSWIN
// Append the process start directory to $PATH, so that ":!foo" finds tools
// shipped with Windows package. This also mimics SearchPath().
os_setenv_append_path(exepath);
@@ -1486,14 +1485,12 @@ 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)
+static char *get_fname(mparm_T *parmp, char *cwd)
{
- return (char_u *)alist_name(&GARGLIST[0]);
+ return alist_name(&GARGLIST[0]);
}
-/*
- * Decide about window layout for diff mode after reading vimrc.
- */
+// Decide about window layout for diff mode after reading vimrc.
static void set_window_layout(mparm_T *paramp)
{
if (paramp->diff_mode && paramp->window_layout == 0) {
@@ -1505,18 +1502,16 @@ 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.
- */
+// "-q errorfile": Load the error file now.
+// If the error file can't be read, exit before doing anything else.
static void handle_quickfix(mparm_T *paramp)
{
if (paramp->edit_type == EDIT_QF) {
if (paramp->use_ef != NULL) {
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, (char *)p_ef, p_efm, true, (char *)IObuff, (char *)p_menc) < 0) {
+ vim_snprintf(IObuff, IOSIZE, "cfile %s", p_ef);
+ if (qf_init(NULL, p_ef, p_efm, true, IObuff, p_menc) < 0) {
msg_putchar('\n');
os_exit(3);
}
@@ -1524,17 +1519,15 @@ static void handle_quickfix(mparm_T *paramp)
}
}
-/*
- * Need to jump to the tag before executing the '-c command'.
- * Makes "vim -c '/return' -t main" work.
- */
-static void handle_tag(char_u *tagname)
+// Need to jump to the tag before executing the '-c command'.
+// Makes "vim -c '/return' -t main" work.
+static void handle_tag(char *tagname)
{
if (tagname != NULL) {
swap_exists_did_quit = false;
- vim_snprintf((char *)IObuff, IOSIZE, "ta %s", tagname);
- do_cmdline_cmd((char *)IObuff);
+ vim_snprintf(IObuff, IOSIZE, "ta %s", tagname);
+ do_cmdline_cmd(IObuff);
TIME_MSG("jumping to tag");
// If the user doesn't want to edit the file then we quit here.
@@ -1566,18 +1559,46 @@ static void read_stdin(void)
check_swap_exists_action();
}
-/*
- * Create the requested number of windows and edit buffers in them.
- * Also does recovery if "recoverymode" set.
- */
+static void open_script_files(mparm_T *parmp)
+{
+ if (parmp->scriptin) {
+ int error;
+ if (strequal(parmp->scriptin, "-")) {
+ FileDescriptor *stdin_dup = file_open_stdin();
+ scriptin[0] = stdin_dup;
+ } else {
+ scriptin[0] = file_open_new(&error, parmp->scriptin,
+ kFileReadOnly|kFileNonBlocking, 0);
+ if (scriptin[0] == NULL) {
+ vim_snprintf((char *)IObuff, IOSIZE,
+ _("Cannot open for reading: \"%s\": %s\n"),
+ parmp->scriptin, os_strerror(error));
+ os_errmsg(IObuff);
+ os_exit(2);
+ }
+ }
+ save_typebuf();
+ }
+
+ if (parmp->scriptout) {
+ scriptout = os_fopen(parmp->scriptout, parmp->scriptout_append ? APPENDBIN : WRITEBIN);
+ if (scriptout == NULL) {
+ os_errmsg(_("Cannot open for script output: \""));
+ os_errmsg(parmp->scriptout);
+ os_errmsg("\"\n");
+ os_exit(2);
+ }
+ }
+}
+
+// Create the requested number of windows and edit buffers in them.
+// Also does recovery if "recoverymode" set.
static void create_windows(mparm_T *parmp)
{
int dorewind;
int done = 0;
- /*
- * Create the number of windows that was requested.
- */
+ // Create the number of windows that was requested.
if (parmp->window_count == -1) { // was not set
parmp->window_count = 1;
}
@@ -1638,7 +1659,7 @@ static void create_windows(mparm_T *parmp)
}
curwin = curwin->w_next;
}
- dorewind = FALSE;
+ dorewind = false;
curbuf = curwin->w_buffer;
if (curbuf->b_ml.ml_mfp == NULL) {
// Set 'foldlevel' to 'foldlevelstart' if it's not negative..
@@ -1647,15 +1668,15 @@ static void create_windows(mparm_T *parmp)
}
// When getting the ATTENTION prompt here, use a dialog.
swap_exists_action = SEA_DIALOG;
- set_buflisted(TRUE);
+ set_buflisted(true);
// create memfile, read file
- (void)open_buffer(FALSE, NULL, 0);
+ (void)open_buffer(false, NULL, 0);
if (swap_exists_action == SEA_QUIT) {
if (got_int || only_one_window()) {
// abort selected or quit and only one window
- did_emsg = FALSE; // avoid hit-enter prompt
+ did_emsg = false; // avoid hit-enter prompt
getout(1);
}
// We can't close the window, it would disturb what
@@ -1667,7 +1688,7 @@ static void create_windows(mparm_T *parmp)
} else {
handle_swap_exists(NULL);
}
- dorewind = TRUE; // start again
+ dorewind = true; // start again
}
os_breakcheck();
if (got_int) {
@@ -1688,7 +1709,7 @@ static void create_windows(mparm_T *parmp)
/// If opened more than one window, start editing files in the other
/// windows. make_windows() has already opened the windows.
-static void edit_buffers(mparm_T *parmp, char_u *cwd)
+static void edit_buffers(mparm_T *parmp, char *cwd)
{
int arg_idx; // index in argument list
int i;
@@ -1696,9 +1717,7 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd)
win_T *win;
char *p_shm_save = NULL;
- /*
- * Don't execute Win/Buf Enter/Leave autocommands here
- */
+ // Don't execute Win/Buf Enter/Leave autocommands here
autocmd_no_enter++;
autocmd_no_leave++;
@@ -1709,9 +1728,9 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd)
}
arg_idx = 1;
- for (i = 1; i < parmp->window_count; ++i) {
+ for (i = 1; i < parmp->window_count; i++) {
if (cwd != NULL) {
- os_chdir((char *)cwd);
+ os_chdir(cwd);
}
// When w_arg_idx is -1 remove the window (see create_windows()).
if (curwin->w_arg_idx == -1) {
@@ -1733,9 +1752,9 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd)
if (i == 1) {
char buf[100];
- p_shm_save = xstrdup((char *)p_shm);
+ p_shm_save = xstrdup(p_shm);
snprintf(buf, sizeof(buf), "F%s", p_shm);
- set_option_value("shm", 0L, buf, 0);
+ set_option_value_give_err("shm", 0L, buf, 0);
}
} else {
if (curwin->w_next == NULL) { // just checking
@@ -1760,7 +1779,7 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd)
// abort or quit selected
if (got_int || only_one_window()) {
// abort selected and only one window
- did_emsg = FALSE; // avoid hit-enter prompt
+ did_emsg = false; // avoid hit-enter prompt
getout(1);
}
win_close(curwin, true, false);
@@ -1779,7 +1798,7 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd)
}
if (p_shm_save != NULL) {
- set_option_value("shm", 0L, p_shm_save, 0);
+ set_option_value_give_err("shm", 0L, p_shm_save, 0);
xfree(p_shm_save);
}
@@ -1807,41 +1826,37 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd)
}
}
-/*
- * Execute the commands from --cmd arguments "cmds[cnt]".
- */
+// Execute the commands from --cmd arguments "cmds[cnt]".
static void exe_pre_commands(mparm_T *parmp)
{
char **cmds = parmp->pre_commands;
int cnt = parmp->n_pre_commands;
int i;
- if (cnt > 0) {
- curwin->w_cursor.lnum = 0; // just in case..
- estack_push(ETYPE_ARGS, _("pre-vimrc command line"), 0);
- current_sctx.sc_sid = SID_CMDARG;
- for (i = 0; i < cnt; i++) {
- do_cmdline_cmd(cmds[i]);
- }
- estack_pop();
- current_sctx.sc_sid = 0;
- TIME_MSG("--cmd commands");
+ if (cnt <= 0) {
+ return;
}
+
+ curwin->w_cursor.lnum = 0; // just in case..
+ estack_push(ETYPE_ARGS, _("pre-vimrc command line"), 0);
+ current_sctx.sc_sid = SID_CMDARG;
+ for (i = 0; i < cnt; i++) {
+ do_cmdline_cmd(cmds[i]);
+ }
+ estack_pop();
+ current_sctx.sc_sid = 0;
+ TIME_MSG("--cmd commands");
}
-/*
- * Execute "+", "-c" and "-S" arguments.
- */
+// Execute "+", "-c" and "-S" arguments.
static void exe_commands(mparm_T *parmp)
{
int i;
- /*
- * We start commands on line 0, make "vim +/pat file" match a
- * pattern on line 1. But don't move the cursor when an autocommand
- * with g`" was used.
- */
- msg_scroll = TRUE;
+ // We start commands on line 0, make "vim +/pat file" match a
+ // pattern on line 1. But don't move the cursor when an autocommand
+ // with g`" was used.
+ msg_scroll = true;
if (parmp->tagname == NULL && curwin->w_cursor.lnum <= 1) {
curwin->w_cursor.lnum = 0;
}
@@ -1861,12 +1876,12 @@ static void exe_commands(mparm_T *parmp)
}
if (!exmode_active) {
- msg_scroll = FALSE;
+ msg_scroll = false;
}
// When started with "-q errorfile" jump to first error again.
if (parmp->edit_type == EDIT_QF) {
- qf_jump(NULL, 0, 0, FALSE);
+ qf_jump(NULL, 0, 0, false);
}
TIME_MSG("executing command arguments");
}
@@ -1936,12 +1951,12 @@ static bool do_user_initialization(void)
return do_exrc;
}
- char_u *init_lua_path = (char_u *)stdpaths_user_conf_subpath("init.lua");
- char_u *user_vimrc = (char_u *)stdpaths_user_conf_subpath("init.vim");
+ char *init_lua_path = stdpaths_user_conf_subpath("init.lua");
+ char *user_vimrc = stdpaths_user_conf_subpath("init.vim");
// init.lua
if (os_path_exists(init_lua_path)
- && do_source((char *)init_lua_path, true, DOSO_VIMRC)) {
+ && do_source(init_lua_path, true, DOSO_VIMRC)) {
if (os_path_exists(user_vimrc)) {
semsg(_("E5422: Conflicting configs: \"%s\" \"%s\""), init_lua_path,
user_vimrc);
@@ -1949,15 +1964,16 @@ static bool do_user_initialization(void)
xfree(user_vimrc);
xfree(init_lua_path);
- return false;
+ do_exrc = p_exrc;
+ return do_exrc;
}
xfree(init_lua_path);
// init.vim
- if (do_source((char *)user_vimrc, true, DOSO_VIMRC) != FAIL) {
+ if (do_source(user_vimrc, true, DOSO_VIMRC) != FAIL) {
do_exrc = p_exrc;
if (do_exrc) {
- do_exrc = (path_full_compare(VIMRC_FILE, (char *)user_vimrc, false, true) != kEqualFiles);
+ do_exrc = (path_full_compare(VIMRC_FILE, user_vimrc, false, true) != kEqualFiles);
}
xfree(user_vimrc);
return do_exrc;
@@ -2001,6 +2017,41 @@ static bool do_user_initialization(void)
return do_exrc;
}
+// Read initialization commands from ".nvim.lua", ".nvimrc", or ".exrc" in
+// current directory. This is only done if the 'exrc' option is set.
+// Only do this if VIMRC_FILE is not the same as vimrc file sourced in
+// do_user_initialization.
+static void do_exrc_initialization(void)
+{
+ char *str;
+
+ if (os_path_exists(VIMRC_LUA_FILE)) {
+ str = nlua_read_secure(VIMRC_LUA_FILE);
+ if (str != NULL) {
+ Error err = ERROR_INIT;
+ nlua_exec(cstr_as_string(str), (Array)ARRAY_DICT_INIT, &err);
+ xfree(str);
+ if (ERROR_SET(&err)) {
+ semsg("Error detected while processing %s:", VIMRC_LUA_FILE);
+ semsg_multiline(err.msg);
+ api_clear_error(&err);
+ }
+ }
+ } else if (os_path_exists(VIMRC_FILE)) {
+ str = nlua_read_secure(VIMRC_FILE);
+ if (str != NULL) {
+ do_source_str(str, VIMRC_FILE);
+ xfree(str);
+ }
+ } else if (os_path_exists(EXRC_FILE)) {
+ str = nlua_read_secure(EXRC_FILE);
+ if (str != NULL) {
+ do_source_str(str, EXRC_FILE);
+ xfree(str);
+ }
+ }
+}
+
/// Source startup scripts
static void source_startup_scripts(const mparm_T *const parmp)
FUNC_ATTR_NONNULL_ALL
@@ -2019,35 +2070,8 @@ static void source_startup_scripts(const mparm_T *const parmp)
do_system_initialization();
if (do_user_initialization()) {
- // Read initialization commands from ".vimrc" or ".exrc" in current
- // directory. This is only done if the 'exrc' option is set.
- // Because of security reasons we disallow shell and write commands
- // now, except for unix if the file is owned by the user or 'secure'
- // option has been reset in environment of global "exrc" or "vimrc".
- // Only do this if VIMRC_FILE is not the same as vimrc file sourced in
- // do_user_initialization.
-#if defined(UNIX)
- // If vimrc file is not owned by user, set 'secure' mode.
- 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 (!os_file_owned(EXRC_FILE)) {
- secure = p_secure;
- } else {
- secure = 0;
- }
-#endif
- (void)do_source(EXRC_FILE, false, DOSO_NONE);
- }
+ do_exrc_initialization();
}
- if (secure == 2) {
- need_wait_return = true;
- }
- secure = 0;
}
TIME_MSG("sourcing vimrc file(s)");
}
@@ -2062,19 +2086,20 @@ static int execute_env(char *env)
FUNC_ATTR_NONNULL_ALL
{
const char *initstr = os_getenv(env);
- if (initstr != NULL) {
- estack_push(ETYPE_ENV, env, 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);
-
- estack_pop();
- current_sctx = save_current_sctx;
- return OK;
- }
- return FAIL;
+ if (initstr == NULL) {
+ return FAIL;
+ }
+
+ estack_push(ETYPE_ENV, env, 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);
+
+ estack_pop();
+ current_sctx = save_current_sctx;
+ return OK;
}
/// Prints the following then exits:
@@ -2090,17 +2115,17 @@ static void mainerr(const char *errstr, const char *str)
signal_stop(); // kill us with CTRL-C here, if you like
- mch_errmsg(prgname);
- mch_errmsg(": ");
- mch_errmsg(_(errstr));
+ os_errmsg(prgname);
+ os_errmsg(": ");
+ os_errmsg(_(errstr));
if (str != NULL) {
- mch_errmsg(": \"");
- mch_errmsg(str);
- mch_errmsg("\"");
+ os_errmsg(": \"");
+ os_errmsg((char *)str);
+ os_errmsg("\"");
}
- mch_errmsg(_("\nMore info with \""));
- mch_errmsg(prgname);
- mch_errmsg(" -h\"\n");
+ os_errmsg(_("\nMore info with \""));
+ os_errmsg(prgname);
+ os_errmsg(" -h\"\n");
os_exit(1);
}
@@ -2109,8 +2134,8 @@ static void mainerr(const char *errstr, const char *str)
static void version(void)
{
// TODO(bfred): not like this?
- nlua_init();
- info_message = true; // use mch_msg(), not mch_errmsg()
+ nlua_init(NULL, 0, -1);
+ info_message = true; // use os_msg(), not os_errmsg()
list_version();
msg_putchar('\n');
msg_didout = false;
@@ -2121,53 +2146,53 @@ static void usage(void)
{
signal_stop(); // kill us with CTRL-C here, if you like
- mch_msg(_("Usage:\n"));
- mch_msg(_(" nvim [options] [file ...] Edit file(s)\n"));
- mch_msg(_(" nvim [options] -t <tag> Edit file where tag is defined\n"));
- mch_msg(_(" nvim [options] -q [errorfile] Edit file with first error\n"));
- mch_msg(_("\nOptions:\n"));
- mch_msg(_(" -- Only file names after this\n"));
- mch_msg(_(" + Start at end of file\n"));
- mch_msg(_(" --cmd <cmd> Execute <cmd> before any config\n"));
- mch_msg(_(" +<cmd>, -c <cmd> Execute <cmd> after config and first file\n"));
- mch_msg("\n");
- mch_msg(_(" -b Binary mode\n"));
- mch_msg(_(" -d Diff mode\n"));
- mch_msg(_(" -e, -E Ex mode\n"));
- mch_msg(_(" -es, -Es Silent (batch) mode\n"));
- mch_msg(_(" -h, --help Print this help message\n"));
- mch_msg(_(" -i <shada> Use this shada file\n"));
- mch_msg(_(" -m Modifications (writing files) not allowed\n"));
- mch_msg(_(" -M Modifications in text not allowed\n"));
- mch_msg(_(" -n No swap file, use memory only\n"));
- mch_msg(_(" -o[N] Open N windows (default: one per file)\n"));
- mch_msg(_(" -O[N] Open N vertical windows (default: one per file)\n"));
- mch_msg(_(" -p[N] Open N tab pages (default: one per file)\n"));
- mch_msg(_(" -r, -L List swap files\n"));
- mch_msg(_(" -r <file> Recover edit state for this file\n"));
- mch_msg(_(" -R Read-only mode\n"));
- mch_msg(_(" -S <session> Source <session> after loading the first file\n"));
- mch_msg(_(" -s <scriptin> Read Normal mode commands from <scriptin>\n"));
- mch_msg(_(" -u <config> Use this config file\n"));
- mch_msg(_(" -v, --version Print version information\n"));
- mch_msg(_(" -V[N][file] Verbose [level][file]\n"));
- mch_msg("\n");
- mch_msg(_(" --api-info Write msgpack-encoded API metadata to stdout\n"));
- mch_msg(_(" --embed Use stdin/stdout as a msgpack-rpc channel\n"));
- 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"));
+ os_msg(_("Usage:\n"));
+ os_msg(_(" nvim [options] [file ...] Edit file(s)\n"));
+ os_msg(_(" nvim [options] -t <tag> Edit file where tag is defined\n"));
+ os_msg(_(" nvim [options] -q [errorfile] Edit file with first error\n"));
+ os_msg(_("\nOptions:\n"));
+ os_msg(_(" -- Only file names after this\n"));
+ os_msg(_(" + Start at end of file\n"));
+ os_msg(_(" --cmd <cmd> Execute <cmd> before any config\n"));
+ os_msg(_(" +<cmd>, -c <cmd> Execute <cmd> after config and first file\n"));
+ os_msg(_(" -l <script> [args...] Execute Lua <script> (with optional args)\n"));
+ os_msg("\n");
+ os_msg(_(" -b Binary mode\n"));
+ os_msg(_(" -d Diff mode\n"));
+ os_msg(_(" -e, -E Ex mode\n"));
+ os_msg(_(" -es, -Es Silent (batch) mode\n"));
+ os_msg(_(" -h, --help Print this help message\n"));
+ os_msg(_(" -i <shada> Use this shada file\n"));
+ os_msg(_(" -m Modifications (writing files) not allowed\n"));
+ os_msg(_(" -M Modifications in text not allowed\n"));
+ os_msg(_(" -n No swap file, use memory only\n"));
+ os_msg(_(" -o[N] Open N windows (default: one per file)\n"));
+ os_msg(_(" -O[N] Open N vertical windows (default: one per file)\n"));
+ os_msg(_(" -p[N] Open N tab pages (default: one per file)\n"));
+ os_msg(_(" -r, -L List swap files\n"));
+ os_msg(_(" -r <file> Recover edit state for this file\n"));
+ os_msg(_(" -R Read-only mode\n"));
+ os_msg(_(" -S <session> Source <session> after loading the first file\n"));
+ os_msg(_(" -s <scriptin> Read Normal mode commands from <scriptin>\n"));
+ os_msg(_(" -u <config> Use this config file\n"));
+ os_msg(_(" -v, --version Print version information\n"));
+ os_msg(_(" -V[N][file] Verbose [level][file]\n"));
+ os_msg("\n");
+ os_msg(_(" --api-info Write msgpack-encoded API metadata to stdout\n"));
+ os_msg(_(" --clean \"Factory defaults\" (skip user config and plugins, shada)\n"));
+ os_msg(_(" --embed Use stdin/stdout as a msgpack-rpc channel\n"));
+ os_msg(_(" --headless Don't start a user interface\n"));
+ os_msg(_(" --listen <address> Serve RPC API from this address\n"));
+ os_msg(_(" --noplugin Don't load plugins\n"));
+ os_msg(_(" --remote[-subcommand] Execute commands remotely on a server\n"));
+ os_msg(_(" --server <address> Specify RPC server to send commands to\n"));
+ os_msg(_(" --startuptime <file> Write startup timing messages to <file>\n"));
+ os_msg(_("\nSee \":help startup-options\" for all options.\n"));
}
-/*
- * Check the result of the ATTENTION dialog:
- * When "Quit" selected, exit Vim.
- * When "Recover" selected, recover the file.
- */
+// Check the result of the ATTENTION dialog:
+// When "Quit" selected, exit Vim.
+// When "Recover" selected, recover the file.
static void check_swap_exists_action(void)
{
if (swap_exists_action == SEA_QUIT) {
diff --git a/src/nvim/main.h b/src/nvim/main.h
index 0c497a7c0e..2d54837872 100644
--- a/src/nvim/main.h
+++ b/src/nvim/main.h
@@ -1,6 +1,8 @@
#ifndef NVIM_MAIN_H
#define NVIM_MAIN_H
+#include <stdbool.h>
+
#include "nvim/event/loop.h"
// Maximum number of commands from + or -c arguments.
@@ -21,15 +23,15 @@ typedef struct {
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
+ char *luaf; // Lua script filename from "-l"
+ int lua_arg0; // Lua script args start index.
int edit_type; // type of editing to do
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
- bool err_isatty; // stderr is a terminal
- bool input_neverscript; // never treat stdin as script (-E/-Es)
+ bool input_istext; // stdin is text, not executable (-E/-Es)
+
int no_swap_file; // "-n" argument used
int use_debug_break_level;
int window_count; // number of windows to use
@@ -40,6 +42,10 @@ typedef struct {
char *listen_addr; // --listen {address}
int remote; // --remote-[subcmd] {file1} {file2}
char *server_addr; // --server {address}
+ char *scriptin; // -s {filename}
+ char *scriptout; // -w/-W {filename}
+ bool scriptout_append; // append (-w) instead of overwrite (-W)
+ bool had_stdin_file; // explicit - as a file to edit
} mparm_T;
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/map.c b/src/nvim/map.c
index 907c1d5996..24066659a8 100644
--- a/src/nvim/map.c
+++ b/src/nvim/map.c
@@ -8,17 +8,16 @@
// khash.h does not make its own copy of the key or value.
//
-#include <lauxlib.h>
-#include <lua.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
-#include "nvim/lib/khash.h"
+#include "auto/config.h"
+#include "klib/khash.h"
+#include "nvim/gettext.h"
#include "nvim/map.h"
#include "nvim/map_defs.h"
#include "nvim/memory.h"
-#include "nvim/vim.h"
#define cstr_t_hash kh_str_hash_func
#define cstr_t_eq kh_str_hash_equal
@@ -169,6 +168,7 @@ 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(uint32_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)
diff --git a/src/nvim/map.h b/src/nvim/map.h
index 92c9ebcc34..6a548ad30b 100644
--- a/src/nvim/map.h
+++ b/src/nvim/map.h
@@ -2,12 +2,17 @@
#define NVIM_MAP_H
#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include "klib/khash.h"
#include "nvim/api/private/defs.h"
#include "nvim/extmark_defs.h"
+#include "nvim/gettext.h"
#include "nvim/highlight_defs.h"
#include "nvim/map_defs.h"
#include "nvim/tui/input_defs.h"
+#include "nvim/types.h"
#include "nvim/ui_client.h"
#if defined(__NetBSD__)
@@ -40,6 +45,7 @@ MAP_DECLS(int, cstr_t)
MAP_DECLS(cstr_t, ptr_t)
MAP_DECLS(cstr_t, int)
MAP_DECLS(ptr_t, ptr_t)
+MAP_DECLS(uint32_t, ptr_t)
MAP_DECLS(uint64_t, ptr_t)
MAP_DECLS(uint64_t, ssize_t)
MAP_DECLS(uint64_t, uint64_t)
diff --git a/src/nvim/map_defs.h b/src/nvim/map_defs.h
index 7b4596ce2e..61afedbe50 100644
--- a/src/nvim/map_defs.h
+++ b/src/nvim/map_defs.h
@@ -1,7 +1,7 @@
#ifndef NVIM_MAP_DEFS_H
#define NVIM_MAP_DEFS_H
-#include "nvim/lib/khash.h"
+#include "klib/khash.h"
typedef const char *cstr_t;
typedef void *ptr_t;
diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c
index 64a798a27b..831d1299a8 100644
--- a/src/nvim/mapping.c
+++ b/src/nvim/mapping.c
@@ -5,32 +5,43 @@
#include <assert.h>
#include <inttypes.h>
+#include <lauxlib.h>
+#include <limits.h>
#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include "nvim/api/private/converter.h"
+#include "nvim/api/private/defs.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/cmdexpand.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
-#include "nvim/ex_docmd.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_session.h"
-#include "nvim/func_attr.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/highlight_defs.h"
#include "nvim/keycodes.h"
#include "nvim/lua/executor.h"
+#include "nvim/macros.h"
#include "nvim/mapping.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/option.h"
+#include "nvim/option_defs.h"
+#include "nvim/pos.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
-#include "nvim/ui.h"
+#include "nvim/search.h"
+#include "nvim/strings.h"
#include "nvim/vim.h"
/// List used for abbreviations.
@@ -151,7 +162,7 @@ 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))) {
+ && (mp->m_desc == NULL || message_filtered(mp->m_desc))) {
return;
}
@@ -176,7 +187,7 @@ static void showmap(mapblock_T *mp, bool local)
// 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
+ msg_putchar(' '); // pad with blanks
len++;
} while (len < 12);
@@ -214,7 +225,6 @@ static void showmap(mapblock_T *mp, bool local)
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
@@ -263,52 +273,59 @@ static bool set_maparg_lhs_rhs(const char *const orig_lhs, const size_t orig_lhs
if (replaced == NULL) {
return false;
}
- mapargs->lhs_len = STRLEN(replaced);
- STRLCPY(mapargs->lhs, replaced, sizeof(mapargs->lhs));
+ mapargs->lhs_len = strlen(replaced);
+ xstrlcpy(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));
+ mapargs->alt_lhs_len = strlen(replaced);
+ xstrlcpy(mapargs->alt_lhs, replaced, sizeof(mapargs->alt_lhs));
} else {
mapargs->alt_lhs_len = 0;
}
+ set_maparg_rhs(orig_rhs, orig_rhs_len, rhs_lua, cpo_flags, mapargs);
+
+ return true;
+}
+
+/// @see set_maparg_lhs_rhs
+static void set_maparg_rhs(const char *const orig_rhs, const size_t orig_rhs_len,
+ const LuaRef rhs_lua, const int cpo_flags, MapArguments *const mapargs)
+{
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);
-
+ mapargs->orig_rhs = xcalloc(mapargs->orig_rhs_len + 1, sizeof(char));
+ xstrlcpy(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 = xcalloc(1, sizeof(char)); // single NUL-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
+ char *replaced = replace_termcodes(orig_rhs, orig_rhs_len, &rhs_buf, REPTERM_DO_LT, NULL,
+ cpo_flags);
+ mapargs->rhs_len = strlen(replaced);
+ // NB: 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;
+ mapargs->rhs = 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 = xcalloc(1, sizeof(char));
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);
+ mapargs->rhs = xstrdup(tmp_buf);
}
- return true;
}
/// Parse a string of |:map-arguments| into a @ref MapArguments struct.
@@ -330,53 +347,53 @@ static bool set_maparg_lhs_rhs(const char *const orig_lhs, const size_t orig_lhs
/// @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)
+static int str_to_mapargs(const char *strargs, bool is_unmap, MapArguments *mapargs)
{
- const char_u *to_parse = strargs;
- to_parse = (char_u *)skipwhite((char *)to_parse);
+ const char *to_parse = strargs;
+ to_parse = skipwhite(to_parse);
CLEAR_POINTER(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);
+ if (strncmp(to_parse, "<buffer>", 8) == 0) {
+ to_parse = skipwhite(to_parse + 8);
mapargs->buffer = true;
continue;
}
- if (STRNCMP(to_parse, "<nowait>", 8) == 0) {
- to_parse = (char_u *)skipwhite((char *)to_parse + 8);
+ if (strncmp(to_parse, "<nowait>", 8) == 0) {
+ to_parse = skipwhite(to_parse + 8);
mapargs->nowait = true;
continue;
}
- if (STRNCMP(to_parse, "<silent>", 8) == 0) {
- to_parse = (char_u *)skipwhite((char *)to_parse + 8);
+ if (strncmp(to_parse, "<silent>", 8) == 0) {
+ to_parse = skipwhite(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);
+ if (strncmp(to_parse, "<special>", 9) == 0) {
+ to_parse = skipwhite(to_parse + 9);
continue;
}
- if (STRNCMP(to_parse, "<script>", 8) == 0) {
- to_parse = (char_u *)skipwhite((char *)to_parse + 8);
+ if (strncmp(to_parse, "<script>", 8) == 0) {
+ to_parse = skipwhite(to_parse + 8);
mapargs->script = true;
continue;
}
- if (STRNCMP(to_parse, "<expr>", 6) == 0) {
- to_parse = (char_u *)skipwhite((char *)to_parse + 6);
+ if (strncmp(to_parse, "<expr>", 6) == 0) {
+ to_parse = skipwhite(to_parse + 6);
mapargs->expr = true;
continue;
}
- if (STRNCMP(to_parse, "<unique>", 8) == 0) {
- to_parse = (char_u *)skipwhite((char *)to_parse + 8);
+ if (strncmp(to_parse, "<unique>", 8) == 0) {
+ to_parse = skipwhite(to_parse + 8);
mapargs->unique = true;
continue;
}
@@ -393,7 +410,7 @@ static int str_to_mapargs(const char_u *strargs, bool is_unmap, MapArguments *ma
//
// With :unmap, literal white space is included in the {lhs}; there is no
// separate {rhs}.
- const char *lhs_end = (char *)to_parse;
+ const char *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] == '\\'))
@@ -405,20 +422,20 @@ static int str_to_mapargs(const char_u *strargs, bool is_unmap, MapArguments *ma
// {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);
+ const char *rhs_start = 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);
+ size_t orig_lhs_len = (size_t)(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);
+ char lhs_to_replace[256];
+ xstrlcpy(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,
+ size_t orig_rhs_len = strlen(rhs_start);
+ if (!set_maparg_lhs_rhs(lhs_to_replace, orig_lhs_len,
+ rhs_start, orig_rhs_len, LUA_NOREF,
CPO_TO_CPO_FLAGS, mapargs)) {
return 1;
}
@@ -433,7 +450,7 @@ static int str_to_mapargs(const char_u *strargs, bool is_unmap, MapArguments *ma
/// and "desc" fields are used.
/// "rhs", "rhs_lua", "orig_rhs" fields are cleared if "simplified" is false.
/// @param sid -1 to use current_sctx
-static void map_add(buf_T *buf, mapblock_T **map_table, mapblock_T **abbr_table, const char_u *keys,
+static void map_add(buf_T *buf, mapblock_T **map_table, mapblock_T **abbr_table, const char *keys,
MapArguments *args, int noremap, int mode, bool is_abbr, scid_T sid,
linenr_T lnum, bool simplified)
{
@@ -448,7 +465,7 @@ static void map_add(buf_T *buf, mapblock_T **map_table, mapblock_T **abbr_table,
}
}
- mp->m_keys = vim_strsave(keys);
+ mp->m_keys = xstrdup(keys);
mp->m_str = args->rhs;
mp->m_orig_str = args->orig_rhs;
mp->m_luaref = args->rhs_lua;
@@ -457,7 +474,7 @@ static void map_add(buf_T *buf, mapblock_T **map_table, mapblock_T **abbr_table,
args->orig_rhs = NULL;
args->rhs_lua = LUA_NOREF;
}
- mp->m_keylen = (int)STRLEN(mp->m_keys);
+ mp->m_keylen = (int)strlen(mp->m_keys);
mp->m_noremap = noremap;
mp->m_nowait = args->nowait;
mp->m_silent = args->silent;
@@ -483,7 +500,7 @@ static void map_add(buf_T *buf, mapblock_T **map_table, mapblock_T **abbr_table,
mp->m_next = *abbr_table;
*abbr_table = mp;
} else {
- const int n = MAP_HASH(mp->m_mode, mp->m_keys[0]);
+ const int n = MAP_HASH(mp->m_mode, (uint8_t)mp->m_keys[0]);
mp->m_next = map_table[n];
map_table[n] = mp;
}
@@ -502,7 +519,7 @@ static void map_add(buf_T *buf, mapblock_T **map_table, mapblock_T **abbr_table,
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;
+ const char *p;
int n;
int retval = 0;
mapblock_T **abbr_table;
@@ -539,7 +556,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
goto theend;
}
- const char_u *lhs = (char_u *)&args->lhs;
+ const char *lhs = (char *)&args->lhs;
const bool did_simplify = args->alt_lhs_len != 0;
// The following is done twice if we have two versions of keys
@@ -553,11 +570,11 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
if (!did_simplify) {
break;
}
- lhs = (char_u *)&args->alt_lhs;
+ lhs = (char *)&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;
+ lhs = (char *)&args->alt_lhs;
len = (int)args->alt_lhs_len;
}
@@ -577,9 +594,9 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
const int first = vim_iswordp(lhs);
int last = first;
- p = lhs + utfc_ptr2len((char *)lhs);
+ p = (char *)lhs + utfc_ptr2len((char *)lhs);
n = 1;
- while (p < lhs + len) {
+ while (p < (char *)lhs + len) {
n++; // nr of (multi-byte) chars
last = vim_iswordp(p); // type of last char
if (same == -1 && last != first) {
@@ -626,7 +643,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
// 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) {
+ && strncmp(mp->m_keys, lhs, (size_t)len) == 0) {
if (is_abbrev) {
semsg(_("E224: global abbreviation already exists for %s"),
mp->m_keys);
@@ -660,7 +677,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
did_local = true;
} else {
n = mp->m_keylen;
- if (STRNCMP(mp->m_keys, lhs, (size_t)(n < len ? n : len)) == 0) {
+ if (strncmp(mp->m_keys, lhs, (size_t)(n < len ? n : len)) == 0) {
showmap(mp, true);
did_local = true;
}
@@ -679,9 +696,9 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
for (int round = 0; (round == 0 || maptype == MAPTYPE_UNMAP) && round <= 1
&& !did_it && !got_int; round++) {
int hash_start, hash_end;
- if (has_lhs || is_abbrev) {
+ if ((round == 0 && has_lhs) || is_abbrev) {
// just use one hash
- hash_start = is_abbrev ? 0 : MAP_HASH(mode, lhs[0]);
+ hash_start = is_abbrev ? 0 : MAP_HASH(mode, (uint8_t)lhs[0]);
hash_end = hash_start + 1;
} else {
// need to loop over all hash lists
@@ -703,13 +720,13 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
}
} else { // do we have a match?
if (round) { // second round: Try unmap "rhs" string
- n = (int)STRLEN(mp->m_str);
+ 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 (strncmp(p, lhs, (size_t)(n < len ? n : len)) == 0) {
if (maptype == MAPTYPE_UNMAP) {
// Delete entry.
// Only accept a full match. For abbreviations
@@ -791,7 +808,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
}
// May need to put this entry into another hash list.
- int new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]);
+ int new_hash = MAP_HASH(mp->m_mode, (uint8_t)mp->m_keys[0]);
if (!is_abbrev && new_hash != hash) {
*mpp = mp->m_next;
mp->m_next = map_table[new_hash];
@@ -840,7 +857,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
}
// Get here when adding a new entry to the maphash[] list or abbrlist.
- map_add(buf, map_table, abbr_table, lhs, args, noremap, mode, is_abbrev,
+ map_add(buf, map_table, abbr_table, (char *)lhs, args, noremap, mode, is_abbrev,
-1, // sid
0, // lnum
keyround1_simplified);
@@ -884,7 +901,7 @@ theend:
///
/// @param maptype MAPTYPE_MAP for |:map|
/// MAPTYPE_UNMAP for |:unmap|
-/// MAPTYPE_NOREMAP for |noremap|.
+/// MAPTYPE_NOREMAP 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.
@@ -898,7 +915,7 @@ theend:
/// - 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)
+int do_map(int maptype, char *arg, int mode, bool is_abbrev)
{
MapArguments parsed_args;
int result = str_to_mapargs(arg, maptype == MAPTYPE_UNMAP, &parsed_args);
@@ -965,12 +982,12 @@ static int get_map_mode(char **cmdp, bool forceit)
/// 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 *cmdp, char_u *arg, int forceit, int abbr)
+static void do_mapclear(char *cmdp, char *arg, int forceit, int abbr)
{
int mode;
int local;
- local = (STRCMP(arg, "<buffer>") == 0);
+ local = (strcmp(arg, "<buffer>") == 0);
if (!local && *arg != NUL) {
emsg(_(e_invarg));
return;
@@ -1018,7 +1035,7 @@ void map_clear_mode(buf_T *buf, int mode, bool local, bool abbr)
continue;
}
// May need to put this entry into another hash list.
- new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]);
+ new_hash = MAP_HASH(mp->m_mode, (uint8_t)mp->m_keys[0]);
if (!abbr && new_hash != hash) {
*mpp = mp->m_next;
if (local) {
@@ -1053,9 +1070,9 @@ bool map_to_exists(const char *const str, const char *const modechars, const boo
int retval;
char *buf = NULL;
- const char_u *const rhs = (char_u *)replace_termcodes(str, strlen(str),
- &buf, REPTERM_DO_LT,
- NULL, CPO_TO_CPO_FLAGS);
+ const char *const rhs = replace_termcodes(str, strlen(str),
+ &buf, REPTERM_DO_LT,
+ NULL, CPO_TO_CPO_FLAGS);
#define MAPMODE(mode, modechars, chr, modeflags) \
do { \
@@ -1073,7 +1090,7 @@ bool map_to_exists(const char *const str, const char *const modechars, const boo
MAPMODE(mode, modechars, 'c', MODE_CMDLINE);
#undef MAPMODE
- retval = map_to_exists_mode((char *)rhs, mode, abbr);
+ retval = map_to_exists_mode(rhs, mode, abbr);
xfree(buf);
return retval;
@@ -1113,7 +1130,7 @@ int map_to_exists_mode(const char *const rhs, const int mode, const bool abbr)
mp = maphash[hash];
}
for (; mp; mp = mp->m_next) {
- if ((mp->m_mode & mode) && strstr((char *)mp->m_str, rhs) != NULL) {
+ if ((mp->m_mode & mode) && strstr(mp->m_str, rhs) != NULL) {
return true;
}
}
@@ -1182,7 +1199,7 @@ static char_u *translate_mapping(char_u *str, int cpo_flags)
}
if (c) {
- ga_append(&ga, (char)c);
+ ga_append(&ga, (uint8_t)c);
}
}
ga_append(&ga, NUL);
@@ -1195,7 +1212,7 @@ static char_u *translate_mapping(char_u *str, int cpo_flags)
/// @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 *cmd, char_u *arg, bool forceit, bool isabbrev,
+char_u *set_context_in_map_cmd(expand_T *xp, char *cmd, char *arg, bool forceit, bool isabbrev,
bool isunmap, cmdidx_T cmdidx)
{
if (forceit && cmdidx != CMD_map && cmdidx != CMD_unmap) {
@@ -1213,38 +1230,38 @@ char_u *set_context_in_map_cmd(expand_T *xp, char *cmd, char_u *arg, bool forcei
xp->xp_context = EXPAND_MAPPINGS;
expand_buffer = false;
for (;;) {
- if (STRNCMP(arg, "<buffer>", 8) == 0) {
+ if (strncmp(arg, "<buffer>", 8) == 0) {
expand_buffer = true;
- arg = (char_u *)skipwhite((char *)arg + 8);
+ arg = skipwhite(arg + 8);
continue;
}
- if (STRNCMP(arg, "<unique>", 8) == 0) {
- arg = (char_u *)skipwhite((char *)arg + 8);
+ if (strncmp(arg, "<unique>", 8) == 0) {
+ arg = skipwhite(arg + 8);
continue;
}
- if (STRNCMP(arg, "<nowait>", 8) == 0) {
- arg = (char_u *)skipwhite((char *)arg + 8);
+ if (strncmp(arg, "<nowait>", 8) == 0) {
+ arg = skipwhite(arg + 8);
continue;
}
- if (STRNCMP(arg, "<silent>", 8) == 0) {
- arg = (char_u *)skipwhite((char *)arg + 8);
+ if (strncmp(arg, "<silent>", 8) == 0) {
+ arg = skipwhite(arg + 8);
continue;
}
- if (STRNCMP(arg, "<special>", 9) == 0) {
- arg = (char_u *)skipwhite((char *)arg + 9);
+ if (strncmp(arg, "<special>", 9) == 0) {
+ arg = skipwhite(arg + 9);
continue;
}
- if (STRNCMP(arg, "<script>", 8) == 0) {
- arg = (char_u *)skipwhite((char *)arg + 8);
+ if (strncmp(arg, "<script>", 8) == 0) {
+ arg = skipwhite(arg + 8);
continue;
}
- if (STRNCMP(arg, "<expr>", 6) == 0) {
- arg = (char_u *)skipwhite((char *)arg + 6);
+ if (strncmp(arg, "<expr>", 6) == 0) {
+ arg = skipwhite(arg + 6);
continue;
}
break;
}
- xp->xp_pattern = (char *)arg;
+ xp->xp_pattern = arg;
}
return NULL;
@@ -1253,98 +1270,140 @@ char_u *set_context_in_map_cmd(expand_T *xp, char *cmd, char_u *arg, bool forcei
/// 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 ***file)
+int ExpandMappings(char *pat, regmatch_T *regmatch, int *numMatches, char ***matches)
{
- 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 {
+ const bool fuzzy = cmdline_fuzzy_complete(pat);
+
+ *numMatches = 0; // return values in case of FAIL
+ *matches = NULL;
+
+ garray_T ga;
+ if (!fuzzy) {
+ ga_init(&ga, sizeof(char *), 3);
+ } else {
+ ga_init(&ga, sizeof(fuzmatch_str_T), 3);
+ }
+
+ // First search in map modifier arguments
+ for (int i = 0; i < 7; i++) {
+ char *p;
+ if (i == 0) {
+ p = "<silent>";
+ } else if (i == 1) {
+ p = "<unique>";
+ } else if (i == 2) {
+ p = "<script>";
+ } else if (i == 3) {
+ p = "<expr>";
+ } else if (i == 4 && !expand_buffer) {
+ p = "<buffer>";
+ } else if (i == 5) {
+ p = "<nowait>";
+ } else if (i == 6) {
+ p = "<special>";
+ } else {
+ continue;
+ }
+
+ bool match;
+ int score = 0;
+ if (!fuzzy) {
+ match = vim_regexec(regmatch, p, (colnr_T)0);
+ } else {
+ score = fuzzy_match_str(p, pat);
+ match = (score != 0);
+ }
+
+ if (!match) {
+ continue;
+ }
+
+ if (fuzzy) {
+ GA_APPEND(fuzmatch_str_T, &ga, ((fuzmatch_str_T){
+ .idx = ga.ga_len,
+ .str = xstrdup(p),
+ .score = score,
+ }));
+ } else {
+ GA_APPEND(char *, &ga, xstrdup(p));
+ }
+ }
+
+ for (int hash = 0; hash < 256; hash++) {
+ mapblock_T *mp;
+ 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)) {
continue;
}
- if (vim_regexec(regmatch, (char *)p, (colnr_T)0)) {
- if (round == 1) {
- count++;
- } else {
- (*file)[count++] = (char *)vim_strsave(p);
- }
+ char *p = (char *)translate_mapping((char_u *)mp->m_keys, CPO_TO_CPO_FLAGS);
+ if (p == NULL) {
+ continue;
}
- }
- 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];
+ bool match;
+ int score = 0;
+ if (!fuzzy) {
+ match = vim_regexec(regmatch, p, (colnr_T)0);
} else {
- mp = maphash[hash];
+ score = fuzzy_match_str(p, pat);
+ match = (score != 0);
}
- 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++] = (char *)p;
- p = NULL;
- }
- }
- xfree(p);
- }
- } // for (mp)
- } // for (hash)
- if (count == 0) { // no match found
- break; // for (round)
- }
+ if (!match) {
+ xfree(p);
+ continue;
+ }
- if (round == 1) {
- *file = xmalloc((size_t)count * sizeof(char_u *));
- }
- } // for (round)
+ if (fuzzy) {
+ GA_APPEND(fuzmatch_str_T, &ga, ((fuzmatch_str_T){
+ .idx = ga.ga_len,
+ .str = p,
+ .score = score,
+ }));
+ } else {
+ GA_APPEND(char *, &ga, p);
+ }
+ } // for (mp)
+ } // for (hash)
+ if (ga.ga_len == 0) {
+ return FAIL;
+ }
+
+ if (!fuzzy) {
+ *matches = ga.ga_data;
+ *numMatches = ga.ga_len;
+ } else {
+ fuzzymatches_to_strmatches(ga.ga_data, matches, ga.ga_len, false);
+ *numMatches = ga.ga_len;
+ }
+
+ int count = *numMatches;
if (count > 1) {
// Sort the matches
- sort_strings(*file, count);
+ // Fuzzy matching already sorts the matches
+ if (!fuzzy) {
+ sort_strings(*matches, count);
+ }
// Remove multiple entries
- char **ptr1 = *file;
+ char **ptr1 = *matches;
char **ptr2 = ptr1 + 1;
char **ptr3 = ptr1 + count;
while (ptr2 < ptr3) {
- if (STRCMP(*ptr1, *ptr2)) {
+ if (strcmp(*ptr1, *ptr2) != 0) {
*++ptr1 = *ptr2++;
} else {
xfree(*ptr2++);
@@ -1353,7 +1412,7 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char ***file)
}
}
- *num_file = count;
+ *numMatches = count;
return count == 0 ? FAIL : OK;
}
@@ -1373,12 +1432,12 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char ***file)
// 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)
+bool check_abbr(int c, char *ptr, int col, int mincol)
{
int len;
int scol; // starting column of the abbr.
int j;
- char_u *s;
+ char *s;
char_u tb[MB_MAXBYTES + 4];
mapblock_T *mp;
mapblock_T *mp2;
@@ -1404,7 +1463,7 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol)
{
bool vim_abbr;
- char_u *p = mb_prevptr(ptr, ptr + col);
+ char *p = mb_prevptr(ptr, ptr + col);
if (!vim_iswordp(p)) {
vim_abbr = true; // Vim added abbr.
} else {
@@ -1417,7 +1476,7 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol)
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);
+ p += utfc_ptr2len(p);
break;
}
clen++;
@@ -1441,19 +1500,19 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol)
mp->m_next == NULL ? (mp = mp2, mp2 = NULL) :
(mp = mp->m_next)) {
int qlen = mp->m_keylen;
- char_u *q = mp->m_keys;
+ char *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);
+ q = xstrdup(mp->m_keys);
+ vim_unescape_ks((char_u *)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);
+ && !strncmp(q, ptr, (size_t)len);
if (q != mp->m_keys) {
xfree(q);
}
@@ -1491,9 +1550,9 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol)
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);
+ char *escaped = vim_strsave_escape_ks((char *)tb + j);
if (escaped != NULL) {
- newlen = (int)STRLEN(escaped);
+ newlen = (int)strlen(escaped);
memmove(tb + j, escaped, (size_t)newlen);
j += newlen;
xfree(escaped);
@@ -1503,17 +1562,23 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol)
// insert the last typed char
(void)ins_typebuf((char *)tb, 1, 0, true, mp->m_silent);
}
- if (mp->m_expr) {
+
+ // copy values here, calling eval_map_expr() may make "mp" invalid!
+ const int noremap = mp->m_noremap;
+ const bool silent = mp->m_silent;
+ const bool expr = mp->m_expr;
+
+ if (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);
+ (void)ins_typebuf(s, noremap, 0, true, silent);
// no abbrev. for these chars
- typebuf.tb_no_abbr_cnt += (int)STRLEN(s) + j + 1;
- if (mp->m_expr) {
+ typebuf.tb_no_abbr_cnt += (int)strlen(s) + j + 1;
+ if (expr) {
xfree(s);
}
}
@@ -1522,7 +1587,7 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol)
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);
+ (void)ins_typebuf((char *)tb, 1, 0, true, silent);
}
return true;
}
@@ -1532,20 +1597,23 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol)
/// Evaluate the RHS of a mapping or abbreviations and take care of escaping
/// special characters.
+/// Careful: after this "mp" will be invalid if the mapping was deleted.
///
/// @param c NUL or typed character for abbreviation
-char_u *eval_map_expr(mapblock_T *mp, int c)
+char *eval_map_expr(mapblock_T *mp, int c)
{
- char_u *p = NULL;
- char_u *expr = NULL;
+ char *p = NULL;
+ char *expr = NULL;
// 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);
+ expr = xstrdup(mp->m_str);
+ vim_unescape_ks((char_u *)expr);
}
+ const bool replace_keycodes = mp->m_replace_keycodes;
+
// Forbid changing text or using ":normal" to avoid most of the bad side
// effects. Also restore the cursor position.
textlock++;
@@ -1559,7 +1627,7 @@ char_u *eval_map_expr(mapblock_T *mp, int c)
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);
+ p = xstrndup(ret.data.string.data, ret.data.string.size);
}
api_free_object(ret);
if (err.type != kErrorTypeNone) {
@@ -1567,7 +1635,7 @@ char_u *eval_map_expr(mapblock_T *mp, int c)
api_clear_error(&err);
}
} else {
- p = (char_u *)eval_to_string((char *)expr, NULL, false);
+ p = eval_to_string(expr, NULL, false);
xfree(expr);
}
textlock--;
@@ -1580,13 +1648,13 @@ char_u *eval_map_expr(mapblock_T *mp, int c)
return NULL;
}
- char_u *res = NULL;
+ char *res = NULL;
- if (mp->m_replace_keycodes) {
- replace_termcodes((char *)p, STRLEN(p), (char **)&res, REPTERM_DO_LT, NULL, CPO_TO_CPO_FLAGS);
+ if (replace_keycodes) {
+ replace_termcodes(p, strlen(p), &res, REPTERM_DO_LT, NULL, CPO_TO_CPO_FLAGS);
} else {
// Escape K_SPECIAL in the result to be able to use the string as typeahead.
- res = (char_u *)vim_strsave_escape_ks((char *)p);
+ res = vim_strsave_escape_ks(p);
}
xfree(p);
@@ -1600,8 +1668,8 @@ char_u *eval_map_expr(mapblock_T *mp, int c)
int makemap(FILE *fd, buf_T *buf)
{
mapblock_T *mp;
- char_u c1, c2, c3;
- char_u *p;
+ char c1, c2, c3;
+ char *p;
char *cmd;
int abbr;
int hash;
@@ -1640,7 +1708,7 @@ int makemap(FILE *fd, buf_T *buf)
continue;
}
for (p = mp->m_str; *p != NUL; p++) {
- if (p[0] == K_SPECIAL && p[1] == KS_EXTRA
+ if ((uint8_t)p[0] == K_SPECIAL && (uint8_t)p[1] == KS_EXTRA
&& p[2] == KE_SNR) {
break;
}
@@ -1811,9 +1879,9 @@ int makemap(FILE *fd, buf_T *buf)
// "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)
+int put_escstr(FILE *fd, char *strstart, int what)
{
- char_u *str = strstart;
+ char_u *str = (char_u *)strstart;
int c;
// :map xx <Nop>
@@ -1890,7 +1958,7 @@ int put_escstr(FILE *fd, char_u *strstart, int what)
}
} else if (c < ' ' || c > '~' || c == '|'
|| (what == 0 && c == ' ')
- || (what == 1 && str == strstart && c == ' ')
+ || (what == 1 && str == (char_u *)strstart && c == ' ')
|| (what != 2 && c == '<')) {
if (putc(Ctrl_V, fd) < 0) {
return FAIL;
@@ -1912,14 +1980,14 @@ int put_escstr(FILE *fd, char_u *strstart, int what)
/// @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)
+char *check_map(char *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);
+ len = (int)strlen(keys);
for (int local = 1; local >= 0; local--) {
// loop over all hash lists
for (int hash = 0; hash < 256; hash++) {
@@ -1940,15 +2008,15 @@ char_u *check_map(char_u *keys, int mode, int exact, int ign_mod, int abbr, mapb
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;
+ char *s = mp->m_keys;
int keylen = mp->m_keylen;
if (ign_mod && keylen >= 3
- && s[0] == K_SPECIAL && s[1] == KS_MODIFIER) {
+ && (uint8_t)s[0] == K_SPECIAL && (uint8_t)s[1] == KS_MODIFIER) {
s += 3;
keylen -= 3;
}
minlen = keylen < len ? keylen : len;
- if (STRNCMP(s, keys, minlen) == 0) {
+ if (strncmp(s, keys, (size_t)minlen) == 0) {
if (mp_ptr != NULL) {
*mp_ptr = mp;
}
@@ -1967,7 +2035,7 @@ char_u *check_map(char_u *keys, int mode, int exact, int ign_mod, int abbr, mapb
}
/// "hasmapto()" function
-void f_hasmapto(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_hasmapto(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *mode;
const char *const name = tv_get_string(&argvars[0]);
@@ -1989,18 +2057,19 @@ void f_hasmapto(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/// Fill a dictionary with all applicable maparg() like dictionaries
+/// 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,
- const char *lhsrawalt, long buffer_value, bool compatible)
- FUNC_ATTR_NONNULL_ARG(1, 2)
+///
+/// @return A Dictionary.
+static Dictionary mapblock_fill_dict(const mapblock_T *const mp, const char *lhsrawalt,
+ const long buffer_value, const bool compatible)
+ FUNC_ATTR_NONNULL_ARG(1)
{
- char *const lhs = str2special_save((const char *)mp->m_keys,
- compatible, !compatible);
+ Dictionary dict = ARRAY_DICT_INIT;
+ char *const lhs = str2special_save(mp->m_keys, compatible, !compatible);
char *const mapmode = map_mode_to_chars(mp->m_mode);
varnumber_T noremap_value;
@@ -2015,37 +2084,35 @@ static void mapblock_fill_dict(dict_T *const dict, const mapblock_T *const mp,
}
if (mp->m_luaref != LUA_NOREF) {
- tv_dict_add_nr(dict, S_LEN("callback"), mp->m_luaref);
+ PUT(dict, "callback", LUAREF_OBJ(api_new_luaref(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));
- }
+ PUT(dict, "rhs", STRING_OBJ(compatible
+ ? cstr_to_string(mp->m_orig_str)
+ : cstr_as_string(str2special_save(mp->m_str, false, true))));
}
if (mp->m_desc != NULL) {
- tv_dict_add_allocated_str(dict, S_LEN("desc"), xstrdup(mp->m_desc));
+ PUT(dict, "desc", STRING_OBJ(cstr_to_string(mp->m_desc)));
}
- tv_dict_add_allocated_str(dict, S_LEN("lhs"), lhs);
- tv_dict_add_str(dict, S_LEN("lhsraw"), (const char *)mp->m_keys);
+ PUT(dict, "lhs", STRING_OBJ(cstr_as_string(lhs)));
+ PUT(dict, "lhsraw", STRING_OBJ(cstr_to_string((const char *)mp->m_keys)));
if (lhsrawalt != NULL) {
// Also add the value for the simplified entry.
- tv_dict_add_str(dict, S_LEN("lhsrawalt"), lhsrawalt);
- }
- 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);
+ PUT(dict, "lhsrawalt", STRING_OBJ(cstr_to_string(lhsrawalt)));
+ }
+ PUT(dict, "noremap", INTEGER_OBJ(noremap_value));
+ PUT(dict, "script", INTEGER_OBJ(mp->m_noremap == REMAP_SCRIPT ? 1 : 0));
+ PUT(dict, "expr", INTEGER_OBJ(mp->m_expr ? 1 : 0));
+ PUT(dict, "silent", INTEGER_OBJ(mp->m_silent ? 1 : 0));
+ PUT(dict, "sid", INTEGER_OBJ((varnumber_T)mp->m_script_ctx.sc_sid));
+ PUT(dict, "lnum", INTEGER_OBJ((varnumber_T)mp->m_script_ctx.sc_lnum));
+ PUT(dict, "buffer", INTEGER_OBJ((varnumber_T)buffer_value));
+ PUT(dict, "nowait", INTEGER_OBJ(mp->m_nowait ? 1 : 0));
if (mp->m_replace_keycodes) {
- tv_dict_add_nr(dict, S_LEN("replace_keycodes"), 1);
+ PUT(dict, "replace_keycodes", INTEGER_OBJ(1));
}
- tv_dict_add_allocated_str(dict, S_LEN("mode"), mapmode);
+ PUT(dict, "mode", STRING_OBJ(cstr_as_string(mapmode)));
+
+ return dict;
}
static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
@@ -2085,21 +2152,21 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
const int flags = REPTERM_FROM_PART | REPTERM_DO_LT;
const int 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);
+ char *keys_simplified = replace_termcodes(keys, strlen(keys), &keys_buf, flags, &did_simplify,
+ CPO_TO_CPO_FLAGS);
mapblock_T *mp = NULL;
int buffer_local;
LuaRef rhs_lua;
- char_u *rhs = check_map(keys_simplified, mode, exact, false, abbr, &mp, &buffer_local, &rhs_lua);
+ char *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),
+ strlen(keys),
&alt_keys_buf, flags | REPTERM_NO_SIMPLIFY, NULL,
CPO_TO_CPO_FLAGS);
- rhs = check_map((char_u *)alt_keys_buf, mode, exact, false, abbr, &mp, &buffer_local, &rhs_lua);
+ rhs = check_map(alt_keys_buf, mode, exact, false, abbr, &mp, &buffer_local, &rhs_lua);
}
if (!get_dict) {
@@ -2108,17 +2175,22 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
if (*rhs == NUL) {
rettv->vval.v_string = xstrdup("<Nop>");
} else {
- rettv->vval.v_string = str2special_save((char *)rhs, false, false);
+ rettv->vval.v_string = str2special_save(rhs, false, false);
}
} else if (rhs_lua != LUA_NOREF) {
rettv->vval.v_string = nlua_funcref_str(mp->m_luaref);
}
} else {
- tv_dict_alloc_ret(rettv);
+ // Return a dictionary.
if (mp != NULL && (rhs != NULL || rhs_lua != LUA_NOREF)) {
- // Return a dictionary.
- mapblock_fill_dict(rettv->vval.v_dict, mp, did_simplify ? (char *)keys_simplified : NULL,
- buffer_local, true);
+ Dictionary dict = mapblock_fill_dict(mp,
+ did_simplify ? keys_simplified : NULL,
+ buffer_local, true);
+ (void)object_to_vim(DICTIONARY_OBJ(dict), rettv, NULL);
+ api_free_dictionary(dict);
+ } else {
+ // Return an empty dictionary.
+ tv_dict_alloc_ret(rettv);
}
}
@@ -2127,7 +2199,7 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
}
/// "mapset()" function
-void f_mapset(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_mapset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char buf[NUMBUFLEN];
const char *which = tv_get_string_buf_chk(&argvars[0], buf);
@@ -2147,29 +2219,36 @@ void f_mapset(typval_T *argvars, typval_T *rettv, FunPtr fptr)
char *lhs = tv_dict_get_string(d, "lhs", false);
char *lhsraw = tv_dict_get_string(d, "lhsraw", false);
char *lhsrawalt = tv_dict_get_string(d, "lhsrawalt", false);
- char *rhs = tv_dict_get_string(d, "rhs", false);
- if (lhs == NULL || lhsraw == NULL || rhs == NULL) {
+ char *orig_rhs = tv_dict_get_string(d, "rhs", false);
+ LuaRef rhs_lua = LUA_NOREF;
+ dictitem_T *callback_di = tv_dict_find(d, S_LEN("callback"));
+ if (callback_di != NULL) {
+ Object callback_obj = vim_to_object(&callback_di->di_tv);
+ if (callback_obj.type == kObjectTypeLuaRef && callback_obj.data.luaref != LUA_NOREF) {
+ rhs_lua = callback_obj.data.luaref;
+ orig_rhs = "";
+ callback_obj.data.luaref = LUA_NOREF;
+ }
+ api_free_object(callback_obj);
+ }
+ if (lhs == NULL || lhsraw == NULL || orig_rhs == NULL) {
emsg(_("E460: entries missing in mapset() dict argument"));
+ api_free_luaref(rhs_lua);
return;
}
- char *orig_rhs = rhs;
- char *arg_buf = NULL;
- rhs = replace_termcodes(rhs, STRLEN(rhs), &arg_buf, REPTERM_DO_LT, NULL, CPO_TO_CPO_FLAGS);
- int noremap = tv_dict_get_number(d, "noremap") ? REMAP_NONE : 0;
+ int noremap = tv_dict_get_number(d, "noremap") != 0 ? REMAP_NONE : 0;
if (tv_dict_get_number(d, "script") != 0) {
noremap = REMAP_SCRIPT;
}
- MapArguments args = { // TODO(zeertzjq): support restoring "callback"?
- .rhs = (char_u *)rhs,
- .rhs_lua = LUA_NOREF,
- .orig_rhs = vim_strsave((char_u *)orig_rhs),
+ MapArguments args = {
.expr = tv_dict_get_number(d, "expr") != 0,
.silent = tv_dict_get_number(d, "silent") != 0,
.nowait = tv_dict_get_number(d, "nowait") != 0,
.replace_keycodes = tv_dict_get_number(d, "replace_keycodes") != 0,
.desc = tv_dict_get_string(d, "desc", false),
};
+ set_maparg_rhs(orig_rhs, strlen(orig_rhs), rhs_lua, CPO_TO_CPO_FLAGS, &args);
scid_T sid = (scid_T)tv_dict_get_number(d, "sid");
linenr_T lnum = (linenr_T)tv_dict_get_number(d, "lnum");
bool buffer = tv_dict_get_number(d, "buffer") != 0;
@@ -2180,28 +2259,28 @@ void f_mapset(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// Delete any existing mapping for this lhs and mode.
MapArguments unmap_args = MAP_ARGUMENTS_INIT;
- set_maparg_lhs_rhs(lhs, strlen(lhs), rhs, strlen(rhs), LUA_NOREF, 0, &unmap_args);
+ set_maparg_lhs_rhs(lhs, strlen(lhs), "", 0, LUA_NOREF, 0, &unmap_args);
unmap_args.buffer = buffer;
- buf_do_map(MAPTYPE_UNMAP, &unmap_args, mode, false, curbuf);
+ buf_do_map(MAPTYPE_UNMAP, &unmap_args, mode, is_abbr, curbuf);
xfree(unmap_args.rhs);
xfree(unmap_args.orig_rhs);
if (lhsrawalt != NULL) {
- map_add(curbuf, map_table, abbr_table, (char_u *)lhsrawalt, &args, noremap, mode, is_abbr,
+ map_add(curbuf, map_table, abbr_table, lhsrawalt, &args, noremap, mode, is_abbr,
sid, lnum, true);
}
- map_add(curbuf, map_table, abbr_table, (char_u *)lhsraw, &args, noremap, mode, is_abbr,
+ map_add(curbuf, map_table, abbr_table, lhsraw, &args, noremap, mode, is_abbr,
sid, lnum, false);
}
/// "maparg()" function
-void f_maparg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_maparg(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
get_maparg(argvars, rettv, true);
}
/// "mapcheck()" function
-void f_mapcheck(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_mapcheck(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
get_maparg(argvars, rettv, false);
}
@@ -2315,8 +2394,8 @@ void langmap_init(void)
/// changed at any time!
void langmap_set(void)
{
- char_u *p;
- char_u *p2;
+ char *p;
+ char *p2;
int from, to;
ga_clear(&langmap_mapga); // clear the previous map first
@@ -2342,7 +2421,7 @@ void langmap_set(void)
if (p[0] == '\\' && p[1] != NUL) {
p++;
}
- from = utf_ptr2char((char *)p);
+ from = utf_ptr2char(p);
to = NUL;
if (p2 == NULL) {
MB_PTR_ADV(p);
@@ -2350,14 +2429,14 @@ void langmap_set(void)
if (p[0] == '\\') {
p++;
}
- to = utf_ptr2char((char *)p);
+ to = utf_ptr2char(p);
}
} else {
if (p2[0] != ',') {
if (p2[0] == '\\') {
p2++;
}
- to = utf_ptr2char((char *)p2);
+ to = utf_ptr2char(p2);
}
}
if (to == NUL) {
@@ -2381,8 +2460,7 @@ void langmap_set(void)
p = p2;
if (p[0] != NUL) {
if (p[0] != ',') {
- semsg(_("E358: 'langmap': Extra characters after semicolon: %s"),
- p);
+ semsg(_("E358: 'langmap': Extra characters after semicolon: %s"), p);
return;
}
p++;
@@ -2402,7 +2480,7 @@ static void do_exmap(exarg_T *eap, int isabbrev)
switch (do_map((*cmdp == 'n') ? MAPTYPE_NOREMAP
: (*cmdp == 'u') ? MAPTYPE_UNMAP : MAPTYPE_MAP,
- (char_u *)eap->arg, mode, isabbrev)) {
+ eap->arg, mode, isabbrev)) {
case 1:
emsg(_(e_invarg));
break;
@@ -2421,8 +2499,7 @@ void ex_abbreviate(exarg_T *eap)
/// ":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 we are in a secure mode we print the mappings for security reasons.
if (secure) {
secure = 2;
msg_outtrans(eap->cmd);
@@ -2440,13 +2517,13 @@ void ex_unmap(exarg_T *eap)
/// ":mapclear" and friends.
void ex_mapclear(exarg_T *eap)
{
- do_mapclear(eap->cmd, (char_u *)eap->arg, eap->forceit, false);
+ do_mapclear(eap->cmd, eap->arg, eap->forceit, false);
}
/// ":abclear" and friends.
void ex_abclear(exarg_T *eap)
{
- do_mapclear(eap->cmd, (char_u *)eap->arg, true, true);
+ do_mapclear(eap->cmd, eap->arg, true, true);
}
/// Set, tweak, or remove a mapping in a mode. Acts as the implementation for
@@ -2523,7 +2600,7 @@ void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mod
}
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) {
+ if (strncmp(p, "!", 2) == 0) {
mode_val = get_map_mode(&p, true); // mapmode-ic
} else {
mode_val = get_map_mode(&p, false);
@@ -2599,12 +2676,10 @@ fail_and_free:
///
/// @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)
+ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf)
{
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
@@ -2623,25 +2698,11 @@ ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf, bool from_lua)
}
// Check for correct mode
if (int_mode & current_maphash->m_mode) {
- mapblock_fill_dict(dict, current_maphash, NULL, 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);
+ ADD(mappings,
+ DICTIONARY_OBJ(mapblock_fill_dict(current_maphash, NULL, buffer_value, false)));
}
}
}
- tv_dict_free(dict);
return mappings;
}
diff --git a/src/nvim/mapping.h b/src/nvim/mapping.h
index 7c48c3bce2..58e28810bc 100644
--- a/src/nvim/mapping.h
+++ b/src/nvim/mapping.h
@@ -1,6 +1,10 @@
#ifndef NVIM_MAPPING_H
#define NVIM_MAPPING_H
+#include <stdbool.h>
+#include <stddef.h>
+
+#include "lauxlib.h"
#include "nvim/buffer_defs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/types.h"
@@ -9,7 +13,7 @@
/// 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
+/// struct declaration. |:noremap| is included, since it behaves like a map
/// argument when used in a mapping.
///
/// @see mapblock_T
@@ -28,19 +32,19 @@ struct map_arguments {
/// 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];
+ char 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];
+ char alt_lhs[MAXMAPLEN + 1];
size_t alt_lhs_len;
- char_u *rhs; /// The {rhs} of the mapping.
+ char *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}.
+ char *orig_rhs; /// The original text of the {rhs}.
size_t orig_rhs_len;
char *desc; /// map description
};
diff --git a/src/nvim/mark.c b/src/nvim/mark.c
index 13ffbfbd21..0c7f5efd89 100644
--- a/src/nvim/mark.c
+++ b/src/nvim/mark.c
@@ -1,26 +1,30 @@
// 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
-/*
- * mark.c: functions for setting marks and jumping to them
- */
+// mark.c: functions for setting marks and jumping to them
#include <assert.h>
-#include <inttypes.h>
#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
#include <string.h>
#include "nvim/ascii.h"
#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/diff.h"
#include "nvim/edit.h"
-#include "nvim/eval.h"
#include "nvim/eval/typval.h"
-#include "nvim/ex_cmds.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/extmark.h"
+#include "nvim/extmark_defs.h"
#include "nvim/fold.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/highlight_defs.h"
#include "nvim/mark.h"
#include "nvim/mark_defs.h"
#include "nvim/mbyte.h"
@@ -29,31 +33,26 @@
#include "nvim/message.h"
#include "nvim/move.h"
#include "nvim/normal.h"
-#include "nvim/option.h"
+#include "nvim/option_defs.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
-#include "nvim/os/time.h"
#include "nvim/path.h"
#include "nvim/quickfix.h"
-#include "nvim/search.h"
#include "nvim/sign.h"
#include "nvim/strings.h"
-#include "nvim/ui.h"
+#include "nvim/textobject.h"
+#include "nvim/undo_defs.h"
#include "nvim/vim.h"
#include "nvim/map.h"
#include "nvim/eval/userfunc.h"
-/*
- * This file contains routines to maintain and manipulate marks.
- */
+// This file contains routines to maintain and manipulate marks.
-/*
- * If a named file mark's lnum is non-zero, it is valid.
- * If a named file mark's fnum is non-zero, it is for an existing buffer,
- * otherwise it is from .shada and namedfm[n].fname is the file name.
- * There are marks 'A - 'Z (set by user) and '0 to '9 (set when writing
- * shada).
- */
+// If a named file mark's lnum is non-zero, it is valid.
+// If a named file mark's fnum is non-zero, it is for an existing buffer,
+// otherwise it is from .shada and namedfm[n].fname is the file name.
+// There are marks 'A - 'Z (set by user) and '0 to '9 (set when writing
+// shada).
/// Global marks (marks with file number or name)
static xfmark_T namedfm[NGLOBALMARKS];
@@ -87,10 +86,9 @@ static xfmark_T* lookup_user_mark(int mark)
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "mark.c.generated.h"
#endif
-/*
- * Set named mark "c" at current cursor position.
- * Returns OK on success, FAIL if bad name given.
- */
+
+// Set named mark "c" at current cursor position.
+// Returns OK on success, FAIL if bad name given.
int setmark(int c)
{
fmarkv_T view = mark_view_make(curwin->w_topline, curwin->w_cursor);
@@ -118,11 +116,9 @@ void clear_fmark(fmark_T *fm)
CLEAR_POINTER(fm);
}
-/*
- * Set named mark "c" to position "pos".
- * When "c" is upper case use file "fnum".
- * Returns OK on success, FAIL if bad name given.
- */
+// Set named mark "c" to position "pos".
+// 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, fmarkv_T *view_pt)
{
int i;
@@ -197,10 +193,8 @@ int setmark_pos(int c, pos_T *pos, int fnum, fmarkv_T *view_pt)
return mark_set_user(buf, c);
}
-/*
- * Set the previous context mark to the current position and add it to the
- * jump list.
- */
+// Set the previous context mark to the current position and add it to the
+// jump list.
void setpcmark(void)
{
xfmark_T *fm;
@@ -241,12 +235,10 @@ void setpcmark(void)
SET_XFMARK(fm, curwin->w_pcmark, curbuf->b_fnum, view, NULL);
}
-/*
- * To change context, call setpcmark(), then move the current position to
- * where ever, then call checkpcmark(). This ensures that the previous
- * context will only be changed if the cursor moved to a different line.
- * If pcmark was deleted (with "dG") the previous mark is restored.
- */
+// To change context, call setpcmark(), then move the current position to
+// where ever, then call checkpcmark(). This ensures that the previous
+// context will only be changed if the cursor moved to a different line.
+// If pcmark was deleted (with "dG") the previous mark is restored.
void checkpcmark(void)
{
if (curwin->w_prev_pcmark.lnum != 0
@@ -504,7 +496,7 @@ static int call_umf(
args[1].vval.v_string = markname_str;
funcexe_T funcexe = FUNCEXE_INIT;
- funcexe.evaluate = true;
+ funcexe.fe_evaluate = true;
return call_func(umf, -1, out, 2, args, &funcexe);
}
@@ -715,6 +707,7 @@ MarkMoveRes mark_move_to(fmark_T *fm, MarkMove flags)
// Need to change buffer
fm_copy = *fm; // Copy, autocommand may change it
fm = &fm_copy;
+ // Jump to the file with the mark
res |= switch_to_mark_buf(fm, !(flags & kMarkJumpList));
// Failed switching buffer
if (res & kMarkMoveFailed) {
@@ -732,6 +725,7 @@ MarkMoveRes mark_move_to(fmark_T *fm, MarkMove flags)
// Move the cursor while keeping track of what changed for the caller
pos_T prev_pos = curwin->w_cursor;
pos_T pos = fm->mark;
+ // Set lnum again, autocommands my have changed it
curwin->w_cursor = fm->mark;
if (flags & kMarkBeginLine) {
beginline(BL_WHITE | BL_FIX);
@@ -810,73 +804,66 @@ fmark_T *getnextmark(pos_T *startpos, int dir, int begin_line)
return result;
}
-/*
- * For an xtended filemark: set the fnum from the fname.
- * This is used for marks obtained from the .shada file. It's postponed
- * until the mark is used to avoid a long startup delay.
- */
+// For an xtended filemark: set the fnum from the fname.
+// This is used for marks obtained from the .shada file. It's postponed
+// until the mark is used to avoid a long startup delay.
static void fname2fnum(xfmark_T *fm)
{
- char_u *p;
-
- if (fm->fname != NULL) {
- /*
- * First expand "~/" in the file name to the home directory.
- * Don't expand the whole name, it may contain other '~' chars.
- */
- if (fm->fname[0] == '~' && (fm->fname[1] == '/'
+ if (fm->fname == NULL) {
+ return;
+ }
+
+ // First expand "~/" in the file name to the home directory.
+ // Don't expand the whole name, it may contain other '~' chars.
#ifdef BACKSLASH_IN_FILENAME
- || fm->fname[1] == '\\'
+ if (fm->fname[0] == '~' && (fm->fname[1] == '/' || fm->fname[1] == '\\')) {
+#else
+ if (fm->fname[0] == '~' && (fm->fname[1] == '/')) {
#endif
- )) {
- int len;
- expand_env((char_u *)"~/", NameBuff, MAXPATHL);
- len = (int)STRLEN(NameBuff);
- STRLCPY(NameBuff + len, fm->fname + 2, MAXPATHL - len);
- } else {
- STRLCPY(NameBuff, fm->fname, MAXPATHL);
- }
+ expand_env("~/", NameBuff, MAXPATHL);
+ int len = (int)strlen(NameBuff);
+ xstrlcpy(NameBuff + len, fm->fname + 2, (size_t)(MAXPATHL - len));
+ } else {
+ xstrlcpy(NameBuff, fm->fname, MAXPATHL);
+ }
- // Try to shorten the file name.
- os_dirname(IObuff, IOSIZE);
- p = path_shorten_fname(NameBuff, IObuff);
+ // Try to shorten the file name.
+ os_dirname(IObuff, IOSIZE);
+ char *p = path_shorten_fname(NameBuff, IObuff);
- // buflist_new() will call fmarks_check_names()
- (void)buflist_new((char *)NameBuff, (char *)p, (linenr_T)1, 0);
- }
+ // buflist_new() will call fmarks_check_names()
+ (void)buflist_new(NameBuff, p, (linenr_T)1, 0);
}
-/*
- * Check all file marks for a name that matches the file name in buf.
- * May replace the name with an fnum.
- * Used for marks that come from the .shada file.
- */
+// Check all file marks for a name that matches the file name in buf.
+// May replace the name with an fnum.
+// Used for marks that come from the .shada file.
void fmarks_check_names(buf_T *buf)
{
- char_u *name = (char_u *)buf->b_ffname;
+ char *name = buf->b_ffname;
int i;
if (buf->b_ffname == NULL) {
return;
}
- for (i = 0; i < NGLOBALMARKS; ++i) {
+ for (i = 0; i < NGLOBALMARKS; i++) {
fmarks_check_one(&namedfm[i], name, buf);
}
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- for (i = 0; i < wp->w_jumplistlen; ++i) {
+ for (i = 0; i < wp->w_jumplistlen; i++) {
fmarks_check_one(&wp->w_jumplist[i], name, buf);
}
}
}
-static void fmarks_check_one(xfmark_T *fm, char_u *name, buf_T *buf)
+static void fmarks_check_one(xfmark_T *fm, char *name, buf_T *buf)
{
if (fm->fmark.fnum == 0
&& fm->fname != NULL
- && FNAMECMP(name, fm->fname) == 0) {
+ && path_fnamecmp(name, fm->fname) == 0) {
fm->fmark.fnum = buf->b_fnum;
XFREE_CLEAR(fm->fname);
}
@@ -949,39 +936,35 @@ void clrallmarks(buf_T *const buf)
buf->b_changelistlen = 0;
}
-/*
- * Get name of file from a filemark.
- * When it's in the current buffer, return the text at the mark.
- * Returns an allocated string.
- */
-char_u *fm_getname(fmark_T *fmark, int lead_len)
+// Get name of file from a filemark.
+// When it's in the current buffer, return the text at the mark.
+// Returns an allocated string.
+char *fm_getname(fmark_T *fmark, int lead_len)
{
if (fmark->fnum == curbuf->b_fnum) { // current buffer
return mark_line(&(fmark->mark), lead_len);
}
- return (char_u *)buflist_nr2name(fmark->fnum, false, true);
+ return buflist_nr2name(fmark->fnum, false, true);
}
-/*
- * Return the line at mark "mp". Truncate to fit in window.
- * The returned string has been allocated.
- */
-static char_u *mark_line(pos_T *mp, int lead_len)
+/// Return the line at mark "mp". Truncate to fit in window.
+/// The returned string has been allocated.
+static char *mark_line(pos_T *mp, int lead_len)
{
- char_u *s, *p;
+ char *s, *p;
int len;
if (mp->lnum == 0 || mp->lnum > curbuf->b_ml.ml_line_count) {
- return vim_strsave((char_u *)"-invalid-");
+ return xstrdup("-invalid-");
}
assert(Columns >= 0);
// Allow for up to 5 bytes per character.
- s = vim_strnsave((char_u *)skipwhite((char *)ml_get(mp->lnum)), (size_t)Columns * 5);
+ s = xstrnsave(skipwhite(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((char *)p);
+ len += ptr2cells(p);
if (len >= Columns - lead_len) {
break;
}
@@ -990,14 +973,12 @@ static char_u *mark_line(pos_T *mp, int lead_len)
return s;
}
-/*
- * print the marks
- */
+// print the marks
void ex_marks(exarg_T *eap)
{
- char_u *arg = (char_u *)eap->arg;
+ char *arg = eap->arg;
int i;
- char_u *name;
+ char *name;
pos_T *posp, *startp, *endp;
if (arg != NULL && *arg == NUL) {
@@ -1005,14 +986,14 @@ void ex_marks(exarg_T *eap)
}
show_one_mark('\'', arg, &curwin->w_pcmark, NULL, true);
- for (i = 0; i < NMARKS; ++i) {
+ for (i = 0; i < NMARKS; i++) {
show_one_mark(i + 'a', arg, &curbuf->b_namedm[i].mark, NULL, true);
}
- for (i = 0; i < NGLOBALMARKS; ++i) {
+ for (i = 0; i < NGLOBALMARKS; i++) {
if (namedfm[i].fmark.fnum != 0) {
name = fm_getname(&namedfm[i].fmark, 15);
} else {
- name = (char_u *)namedfm[i].fname;
+ name = namedfm[i].fname;
}
if (name != NULL) {
show_one_mark(i >= NMARKS ? i - NMARKS + '0' : i + 'A',
@@ -1044,11 +1025,11 @@ void ex_marks(exarg_T *eap)
}
/// @param current in current file
-static void show_one_mark(int c, char_u *arg, pos_T *p, char_u *name_arg, int current)
+static void show_one_mark(int c, char *arg, pos_T *p, char *name_arg, int current)
{
static bool did_title = false;
bool mustfree = false;
- char_u *name = name_arg;
+ char *name = name_arg;
if (c == -1) { // finish up
if (did_title) {
@@ -1061,7 +1042,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((char *)arg, c) != NULL)
+ && (arg == NULL || vim_strchr(arg, c) != NULL)
&& p->lnum != 0) {
// don't output anything if 'q' typed at --more-- prompt
if (name == NULL && current) {
@@ -1076,13 +1057,12 @@ 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 %6" PRIdLINENR " %4d ", c, p->lnum, p->col);
- msg_outtrans((char *)IObuff);
+ snprintf(IObuff, IOSIZE, " %c %6" PRIdLINENR " %4d ", c, p->lnum, p->col);
+ msg_outtrans(IObuff);
if (name != NULL) {
msg_outtrans_attr(name, current ? HL_ATTR(HLF_D) : 0);
}
}
- ui_flush(); // show one line at a time
}
if (mustfree) {
xfree(name);
@@ -1090,12 +1070,10 @@ static void show_one_mark(int c, char_u *arg, pos_T *p, char_u *name_arg, int cu
}
}
-/*
- * ":delmarks[!] [marks]"
- */
+// ":delmarks[!] [marks]"
void ex_delmarks(exarg_T *eap)
{
- char_u *p;
+ char *p;
int from, to;
int i;
int lower;
@@ -1111,17 +1089,17 @@ void ex_delmarks(exarg_T *eap)
emsg(_(e_argreq));
} else {
// clear specified marks only
- for (p = (char_u *)eap->arg; *p != NUL; p++) {
+ for (p = eap->arg; *p != NUL; p++) {
lower = ASCII_ISLOWER(*p);
digit = ascii_isdigit(*p);
if (lower || digit || ASCII_ISUPPER(*p)) {
if (p[1] == '-') {
// clear range of marks
- from = *p;
- to = p[2];
+ from = (uint8_t)(*p);
+ to = (uint8_t)p[2];
if (!(lower ? ASCII_ISLOWER(p[2])
- : (digit ? ascii_isdigit(p[2])
- : ASCII_ISUPPER(p[2])))
+ : (digit ? ascii_isdigit(p[2])
+ : ASCII_ISUPPER(p[2])))
|| to < from) {
semsg(_(e_invarg2), p);
return;
@@ -1129,10 +1107,10 @@ void ex_delmarks(exarg_T *eap)
p += 2;
} else {
// clear one lower case mark
- from = to = *p;
+ from = to = (uint8_t)(*p);
}
- for (i = from; i <= to; ++i) {
+ for (i = from; i <= to; i++) {
if (lower) {
curbuf->b_namedm[i - 'a'].mark.lnum = 0;
} else {
@@ -1173,25 +1151,23 @@ void ex_delmarks(exarg_T *eap)
}
}
-/*
- * print the jumplist
- */
+// print the jumplist
void ex_jumps(exarg_T *eap)
{
int i;
- char_u *name;
+ char *name;
cleanup_jumplist(curwin, true);
// Highlight title
msg_puts_title(_("\n jump line col file/text"));
- for (i = 0; i < curwin->w_jumplistlen && !got_int; ++i) {
+ for (i = 0; i < curwin->w_jumplistlen && !got_int; i++) {
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-");
+ name = xstrdup("-invalid-");
}
// apply :filter /pat/ or file name not available
if (name == NULL || message_filtered(name)) {
@@ -1204,18 +1180,17 @@ void ex_jumps(exarg_T *eap)
xfree(name);
break;
}
- snprintf((char *)IObuff, IOSIZE, "%c %2d %5" PRIdLINENR " %4d ",
+ snprintf(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(IObuff);
msg_outtrans_attr(name,
curwin->w_jumplist[i].fmark.fnum == curbuf->b_fnum
? HL_ATTR(HLF_D) : 0);
xfree(name);
os_breakcheck();
}
- ui_flush();
}
if (curwin->w_jumplistidx == curwin->w_jumplistlen) {
msg_puts("\n>");
@@ -1229,36 +1204,32 @@ void ex_clearjumps(exarg_T *eap)
curwin->w_jumplistidx = 0;
}
-/*
- * print the changelist
- */
+// print the changelist
void ex_changes(exarg_T *eap)
{
int i;
- char_u *name;
+ char *name;
// Highlight title
msg_puts_title(_("\nchange line col text"));
- for (i = 0; i < curbuf->b_changelistlen && !got_int; ++i) {
+ for (i = 0; i < curbuf->b_changelistlen && !got_int; i++) {
if (curbuf->b_changelist[i].mark.lnum != 0) {
msg_putchar('\n');
if (got_int) {
break;
}
- sprintf((char *)IObuff, "%c %3d %5ld %4d ",
- i == curwin->w_changelistidx ? '>' : ' ',
- i > curwin->w_changelistidx ? i - curwin->w_changelistidx
- : curwin->w_changelistidx - i,
- (long)curbuf->b_changelist[i].mark.lnum,
- curbuf->b_changelist[i].mark.col);
- msg_outtrans((char *)IObuff);
+ snprintf(IObuff, IOSIZE, "%c %3d %5ld %4d ",
+ i == curwin->w_changelistidx ? '>' : ' ',
+ i > curwin->w_changelistidx ? i - curwin->w_changelistidx : curwin->w_changelistidx - i,
+ (long)curbuf->b_changelist[i].mark.lnum,
+ curbuf->b_changelist[i].mark.col);
+ msg_outtrans(IObuff);
name = mark_line(&curbuf->b_changelist[i].mark, 17);
msg_outtrans_attr(name, HL_ATTR(HLF_D));
xfree(name);
os_breakcheck();
}
- ui_flush();
}
if (curwin->w_changelistidx == curbuf->b_changelistlen) {
msg_puts("\n>");
@@ -1268,43 +1239,41 @@ void ex_changes(exarg_T *eap)
#define ONE_ADJUST(add) \
{ \
lp = add; \
- if (*lp >= line1 && *lp <= line2) \
- { \
- if (amount == MAXLNUM) \
- *lp = 0; \
- else \
- *lp += amount; \
+ if (*lp >= line1 && *lp <= line2) { \
+ if (amount == MAXLNUM) { \
+ *lp = 0; \
+ } else { \
+ *lp += amount; \
+ } \
+ } else if (amount_after && *lp > line2) { \
+ *lp += amount_after; \
} \
- else if (amount_after && *lp > line2) \
- *lp += amount_after; \
}
// don't delete the line, just put at first deleted line
#define ONE_ADJUST_NODEL(add) \
{ \
lp = add; \
- if (*lp >= line1 && *lp <= line2) \
- { \
- if (amount == MAXLNUM) \
- *lp = line1; \
- else \
- *lp += amount; \
+ if (*lp >= line1 && *lp <= line2) { \
+ if (amount == MAXLNUM) { \
+ *lp = line1; \
+ } else { \
+ *lp += amount; \
+ } \
+ } else if (amount_after && *lp > line2) { \
+ *lp += amount_after; \
} \
- else if (amount_after && *lp > line2) \
- *lp += amount_after; \
- }
-
-/*
- * Adjust marks between line1 and line2 (inclusive) to move 'amount' lines.
- * Must be called before changed_*(), appended_lines() or deleted_lines().
- * May be called before or after changing the text.
- * When deleting lines line1 to line2, use an 'amount' of MAXLNUM: The marks
- * within this range are made invalid.
- * If 'amount_after' is non-zero adjust marks after line2.
- * Example: Delete lines 34 and 35: mark_adjust(34, 35, MAXLNUM, -2);
- * Example: Insert two lines below 55: mark_adjust(56, MAXLNUM, 2, 0);
- * or: mark_adjust(56, 55, MAXLNUM, 2);
- */
+ }
+
+// Adjust marks between line1 and line2 (inclusive) to move 'amount' lines.
+// Must be called before changed_*(), appended_lines() or deleted_lines().
+// May be called before or after changing the text.
+// When deleting lines line1 to line2, use an 'amount' of MAXLNUM: The marks
+// within this range are made invalid.
+// If 'amount_after' is non-zero adjust marks after line2.
+// Example: Delete lines 34 and 35: mark_adjust(34, 35, MAXLNUM, -2);
+// 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, linenr_T amount, linenr_T amount_after,
ExtmarkOp op)
{
@@ -1399,9 +1368,7 @@ static void mark_adjust_internal(linenr_T line1, linenr_T line2, linenr_T amount
ONE_ADJUST_NODEL(&(saved_cursor.lnum));
}
- /*
- * Adjust items in all windows related to the current buffer.
- */
+ // Adjust items in all windows related to the current buffer.
FOR_ALL_TAB_WINDOWS(tab, win) {
if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) {
// Marks in the jumplist. When deleting lines, this may create
@@ -1477,8 +1444,7 @@ static void mark_adjust_internal(linenr_T line1, linenr_T line2, linenr_T amount
#define COL_ADJUST(pp) \
{ \
posp = pp; \
- if (posp->lnum == lnum && posp->col >= mincol) \
- { \
+ 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) { \
@@ -1543,12 +1509,10 @@ void mark_col_adjust(linenr_T lnum, colnr_T mincol, linenr_T lnum_amount, long c
// saved cursor for formatting
COL_ADJUST(&saved_cursor);
- /*
- * Adjust items in all windows related to the current buffer.
- */
+ // Adjust items in all windows related to the current buffer.
FOR_ALL_WINDOWS_IN_TAB(win, curtab) {
// marks in the jumplist
- for (i = 0; i < win->w_jumplistlen; ++i) {
+ for (i = 0; i < win->w_jumplistlen; i++) {
if (win->w_jumplist[i].fmark.fnum == fnum) {
COL_ADJUST(&(win->w_jumplist[i].fmark.mark));
}
@@ -1572,18 +1536,21 @@ void mark_col_adjust(linenr_T lnum, colnr_T mincol, linenr_T lnum_amount, long c
// When deleting lines, this may create duplicate marks in the
// jumplist. They will be removed here for the specified window.
-// When "checktail" is true, removes tail jump if it matches current position.
-void cleanup_jumplist(win_T *wp, bool checktail)
+// When "loadfiles" is true first ensure entries have the "fnum" field set
+// (this may be a bit slow).
+void cleanup_jumplist(win_T *wp, bool loadfiles)
{
int i;
- // Load all the files from the jump list. This is
- // needed to properly clean up duplicate entries, but will take some
- // time.
- for (i = 0; i < wp->w_jumplistlen; i++) {
- if ((wp->w_jumplist[i].fmark.fnum == 0)
- && (wp->w_jumplist[i].fmark.mark.lnum != 0)) {
- fname2fnum(&wp->w_jumplist[i]);
+ if (loadfiles) {
+ // If specified, load all the files from the jump list. This is
+ // needed to properly clean up duplicate entries, but will take some
+ // time.
+ for (i = 0; i < wp->w_jumplistlen; i++) {
+ if ((wp->w_jumplist[i].fmark.fnum == 0)
+ && (wp->w_jumplist[i].fmark.mark.lnum != 0)) {
+ fname2fnum(&wp->w_jumplist[i]);
+ }
}
}
@@ -1631,8 +1598,8 @@ void cleanup_jumplist(win_T *wp, bool checktail)
// When pointer is below last jump, remove the jump if it matches the current
// line. This avoids useless/phantom jumps. #9805
- if (checktail && wp->w_jumplistlen
- && wp->w_jumplistidx == wp->w_jumplistlen) {
+ if (loadfiles // otherwise (i.e.: Shada), last entry should be kept
+ && wp->w_jumplistlen && wp->w_jumplistidx == wp->w_jumplistlen) {
const xfmark_T *fm_last = &wp->w_jumplist[wp->w_jumplistlen - 1];
if (fm_last->fmark.fnum == curbuf->b_fnum
&& fm_last->fmark.mark.lnum == wp->w_cursor.lnum) {
@@ -1643,14 +1610,12 @@ void cleanup_jumplist(win_T *wp, bool checktail)
}
}
-/*
- * Copy the jumplist from window "from" to window "to".
- */
+// Copy the jumplist from window "from" to window "to".
void copy_jumplist(win_T *from, win_T *to)
{
int i;
- for (i = 0; i < from->w_jumplistlen; ++i) {
+ 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 = xstrdup(from->w_jumplist[i].fname);
@@ -1678,16 +1643,13 @@ const void *mark_jumplist_iter(const void *const iter, const win_T *const win, x
*fm = (xfmark_T)INIT_XFMARK;
return NULL;
}
- const xfmark_T *const iter_mark =
- (iter == NULL
- ? &(win->w_jumplist[0])
- : (const xfmark_T *const)iter);
+ const xfmark_T *const iter_mark = iter == NULL ? &(win->w_jumplist[0])
+ : (const xfmark_T *const)iter;
*fm = *iter_mark;
if (iter_mark == &(win->w_jumplist[win->w_jumplistlen - 1])) {
return NULL;
- } else {
- return iter_mark + 1;
}
+ return iter_mark + 1;
}
/// Iterate over global marks
@@ -1717,9 +1679,9 @@ const void *mark_global_iter(const void *const iter, char *const name, xfmark_T
return NULL;
}
size_t iter_off = (size_t)(iter_mark - &(namedfm[0]));
- *name = (char)(iter_off < NMARKS
- ? 'A' + (char)iter_off
- : '0' + (char)(iter_off - NMARKS));
+ *name = (char)(iter_off < NMARKS ?
+ 'A' + (char)iter_off :
+ '0' + (char)(iter_off - NMARKS));
*fm = *iter_mark;
while ((size_t)(++iter_mark - &(namedfm[0])) < ARRAY_SIZE(namedfm)) {
if (iter_mark->fmark.mark.lnum) {
@@ -1781,16 +1743,11 @@ const void *mark_buffer_iter(const void *const iter, const buf_T *const buf, cha
FUNC_ATTR_NONNULL_ARG(2, 3, 4) FUNC_ATTR_WARN_UNUSED_RESULT
{
*name = NUL;
- char mark_name = (char)(iter == NULL
- ? NUL
- : (iter == &(buf->b_last_cursor)
- ? '"'
- : (iter == &(buf->b_last_insert)
- ? '^'
- : (iter == &(buf->b_last_change)
- ? '.'
- : 'a' + (char)((const fmark_T *)iter
- - &(buf->b_namedm[0]))))));
+ char mark_name = (char)(iter == NULL ? NUL :
+ iter == &(buf->b_last_cursor) ? '"' :
+ iter == &(buf->b_last_insert) ? '^' :
+ iter == &(buf->b_last_change) ? '.' :
+ 'a' + (char)((const fmark_T *)iter - &(buf->b_namedm[0])));
const fmark_T *iter_mark = next_buffer_mark(buf, &mark_name);
while (iter_mark != NULL && iter_mark->mark.lnum == 0) {
iter_mark = next_buffer_mark(buf, &mark_name);
@@ -1867,14 +1824,12 @@ bool mark_set_local(const char name, buf_T *const buf, const fmark_T fm, const b
return true;
}
-/*
- * Free items in the jumplist of window "wp".
- */
+// Free items in the jumplist of window "wp".
void free_jumplist(win_T *wp)
{
int i;
- for (i = 0; i < wp->w_jumplistlen; ++i) {
+ for (i = 0; i < wp->w_jumplistlen; i++) {
free_xfmark(wp->w_jumplist[i]);
}
wp->w_jumplistlen = 0;
@@ -1911,8 +1866,8 @@ void mark_mb_adjustpos(buf_T *buf, pos_T *lp)
FUNC_ATTR_NONNULL_ALL
{
if (lp->col > 0 || lp->coladd > 1) {
- const char_u *const p = ml_get_buf(buf, lp->lnum, false);
- if (*p == NUL || (int)STRLEN(p) < lp->col) {
+ const char *const p = ml_get_buf(buf, lp->lnum, false);
+ if (*p == NUL || (int)strlen(p) < lp->col) {
lp->col = 0;
} else {
lp->col -= utf_head_off(p, p + lp->col);
@@ -1921,8 +1876,8 @@ 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((char *)p + lp->col))
- && ptr2cells((char *)p + lp->col) > 1) {
+ && vim_isprintc(utf_ptr2char(p + lp->col))
+ && ptr2cells(p + lp->col) > 1) {
lp->coladd = 0;
}
}
diff --git a/src/nvim/mark.h b/src/nvim/mark.h
index 6da976e8d3..af0abba864 100644
--- a/src/nvim/mark.h
+++ b/src/nvim/mark.h
@@ -1,9 +1,12 @@
#ifndef NVIM_MARK_H
#define NVIM_MARK_H
+#include <stdbool.h>
+#include <stddef.h>
+
#include "nvim/ascii.h"
#include "nvim/buffer_defs.h"
-#include "nvim/ex_cmds_defs.h" // for exarg_T
+#include "nvim/ex_cmds_defs.h"
#include "nvim/extmark_defs.h"
#include "nvim/func_attr.h"
#include "nvim/macros.h"
@@ -78,12 +81,13 @@ static inline int mark_local_index(const char name)
: -1))));
}
-static inline bool lt(pos_T, pos_T) REAL_FATTR_CONST REAL_FATTR_ALWAYS_INLINE;
-static inline bool equalpos(pos_T, pos_T)
+static inline bool lt(pos_T a, pos_T b)
+ REAL_FATTR_CONST REAL_FATTR_ALWAYS_INLINE;
+static inline bool equalpos(pos_T a, pos_T b)
REAL_FATTR_CONST REAL_FATTR_ALWAYS_INLINE;
-static inline bool ltoreq(pos_T, pos_T)
+static inline bool ltoreq(pos_T a, pos_T b)
REAL_FATTR_CONST REAL_FATTR_ALWAYS_INLINE;
-static inline void clearpos(pos_T *)
+static inline void clearpos(pos_T *a)
REAL_FATTR_ALWAYS_INLINE;
/// Return true if position a is before (less than) position b.
diff --git a/src/nvim/mark_defs.h b/src/nvim/mark_defs.h
index a78056c5f9..f9df0028db 100644
--- a/src/nvim/mark_defs.h
+++ b/src/nvim/mark_defs.h
@@ -5,10 +5,8 @@
#include "nvim/os/time.h"
#include "nvim/pos.h"
-/*
- * marks: positions in a file
- * (a normal mark is a lnum/col pair, the same as a file position)
- */
+// marks: positions in a file
+// (a normal mark is a lnum/col pair, the same as a file position)
/// Flags for outcomes when moving to a mark.
typedef enum {
@@ -88,4 +86,4 @@ typedef struct xfilemark {
#define INIT_XFMARK { INIT_FMARK, NULL }
-#endif // NVIM_MARK_DEFS_H
+#endif // NVIM_MARK_DEFS_H
diff --git a/src/nvim/marktree.c b/src/nvim/marktree.c
index 03340a99d6..2036ddd21d 100644
--- a/src/nvim/marktree.c
+++ b/src/nvim/marktree.c
@@ -48,10 +48,15 @@
// at the repo root.
#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "klib/kvec.h"
#include "nvim/garray.h"
-#include "nvim/lib/kvec.h"
#include "nvim/marktree.h"
+#include "nvim/memory.h"
+#include "nvim/pos.h"
#define T MT_BRANCH_FACTOR
#define ILEN (sizeof(mtnode_t) + (2 * T) * sizeof(void *))
diff --git a/src/nvim/marktree.h b/src/nvim/marktree.h
index e2e05eebd5..5ce4b2cd24 100644
--- a/src/nvim/marktree.h
+++ b/src/nvim/marktree.h
@@ -2,14 +2,19 @@
#define NVIM_MARKTREE_H
#include <assert.h>
+#include <stdbool.h>
+#include <stddef.h>
#include <stdint.h>
#include "nvim/assert.h"
#include "nvim/garray.h"
#include "nvim/map.h"
+#include "nvim/map_defs.h"
#include "nvim/pos.h"
#include "nvim/types.h"
+struct mtnode_s;
+
#define MT_MAX_DEPTH 20
#define MT_BRANCH_FACTOR 10
diff --git a/src/nvim/match.c b/src/nvim/match.c
index 1c34c9f004..6663dfd7ec 100644
--- a/src/nvim/match.c
+++ b/src/nvim/match.c
@@ -3,22 +3,39 @@
// match.c: functions for highlighting matches
+#include <assert.h>
+#include <inttypes.h>
#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include "nvim/ascii.h"
#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/drawscreen.h"
-#include "nvim/eval.h"
#include "nvim/eval/funcs.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/eval/window.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/fold.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
+#include "nvim/macros.h"
#include "nvim/match.h"
+#include "nvim/mbyte.h"
#include "nvim/memline.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
+#include "nvim/option_defs.h"
+#include "nvim/pos.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
-#include "nvim/runtime.h"
+#include "nvim/strings.h"
+#include "nvim/types.h"
#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -29,13 +46,13 @@ 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).
+/// Add match to the match list of window "wp".
+/// If "pat" is not NULL the pattern will be highlighted with the group "grp"
+/// with priority "prio".
+/// If "pos_list" is not NULL the list of posisions defines the highlights.
+/// Optionally, a desired ID "id" can be specified (greater than or equal to 1).
+/// If no particular ID is desired, -1 must be specified for "id".
///
-/// @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,
@@ -47,7 +64,7 @@ static int match_add(win_T *wp, const char *const grp, const char *const pat, in
matchitem_T *m;
int hlg_id;
regprog_T *regprog = NULL;
- int rtype = SOME_VALID;
+ int rtype = UPD_SOME_VALID;
if (*grp == NUL || (pat != NULL && *pat == NUL)) {
return -1;
@@ -58,16 +75,26 @@ static int match_add(win_T *wp, const char *const grp, const char *const pat, in
(int64_t)id);
return -1;
}
- if (id != -1) {
- cur = wp->w_match_head;
- while (cur != NULL) {
- if (cur->id == id) {
+ if (id == -1) {
+ // use the next available match ID
+ id = wp->w_next_match_id++;
+ } else {
+ // check the given ID is not already in use
+ for (cur = wp->w_match_head; cur != NULL; cur = cur->mit_next) {
+ if (cur->mit_id == id) {
semsg(_("E801: ID already taken: %" PRId64), (int64_t)id);
return -1;
}
- cur = cur->next;
+ }
+
+ // Make sure the next match ID is always higher than the highest
+ // manually selected ID. Add some extra in case a few more IDs are
+ // added soon.
+ if (wp->w_next_match_id < id + 100) {
+ wp->w_next_match_id = id + 100;
}
}
+
if ((hlg_id = syn_check_group(grp, strlen(grp))) == 0) {
return -1;
}
@@ -76,30 +103,22 @@ static int match_add(win_T *wp, const char *const grp, const char *const pat, in
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 (pos_list != NULL) {
+ m->mit_pos_array = xcalloc((size_t)tv_list_len(pos_list), sizeof(llpos_T));
+ m->mit_pos_count = tv_list_len(pos_list);
+ }
+ m->mit_id = id;
+ m->mit_priority = prio;
+ m->mit_pattern = pat == NULL ? NULL: xstrdup(pat);
+ m->mit_hlg_id = hlg_id;
+ m->mit_match.regprog = regprog;
+ m->mit_match.rmm_ic = false;
+ m->mit_match.rmm_maxcol = 0;
+ m->mit_conceal_char = 0;
if (conceal_char != NULL) {
- m->conceal_char = utf_ptr2char(conceal_char);
+ m->mit_conceal_char = utf_ptr2char(conceal_char);
}
// Set up position matches
@@ -129,7 +148,7 @@ static int match_add(win_T *wp, const char *const grp, const char *const pat, in
if (lnum <= 0) {
continue;
}
- m->pos.pos[i].lnum = lnum;
+ m->mit_pos_array[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);
@@ -150,15 +169,15 @@ static int match_add(win_T *wp, const char *const grp, const char *const pat, in
}
}
}
- m->pos.pos[i].col = col;
- m->pos.pos[i].len = len;
+ m->mit_pos_array[i].col = col;
+ m->mit_pos_array[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;
+ m->mit_pos_array[i].lnum = (linenr_T)TV_LIST_ITEM_TV(li)->vval.v_number;
+ m->mit_pos_array[i].col = 0;
+ m->mit_pos_array[i].len = 0;
} else {
semsg(_("E5031: List or number required at position %d"),
(int)tv_list_idx_of_item(pos_list, li));
@@ -171,9 +190,6 @@ static int match_add(win_T *wp, const char *const grp, const char *const pat, in
botlnum = lnum + 1;
}
i++;
- if (i >= MAXPOSMATCH) {
- break;
- }
});
// Calculate top and bottom lines for redrawing area
@@ -191,9 +207,9 @@ static int match_add(win_T *wp, const char *const grp, const char *const pat, in
wp->w_buffer->b_mod_bot = botlnum;
wp->w_buffer->b_mod_xlines = 0;
}
- m->pos.toplnum = toplnum;
- m->pos.botlnum = botlnum;
- rtype = VALID;
+ m->mit_toplnum = toplnum;
+ m->mit_botlnum = botlnum;
+ rtype = UPD_VALID;
}
}
@@ -201,21 +217,23 @@ static int match_add(win_T *wp, const char *const grp, const char *const pat, in
// the match priorities.
cur = wp->w_match_head;
prev = cur;
- while (cur != NULL && prio >= cur->priority) {
+ while (cur != NULL && prio >= cur->mit_priority) {
prev = cur;
- cur = cur->next;
+ cur = cur->mit_next;
}
if (cur == prev) {
wp->w_match_head = m;
} else {
- prev->next = m;
+ prev->mit_next = m;
}
- m->next = cur;
+ m->mit_next = cur;
redraw_later(wp, rtype);
return id;
fail:
+ xfree(m->mit_pattern);
+ xfree(m->mit_pos_array);
xfree(m);
return -1;
}
@@ -227,19 +245,18 @@ 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;
+ int rtype = UPD_SOME_VALID;
if (id < 1) {
if (perr) {
- semsg(_("E802: Invalid ID: %" PRId64
- " (must be greater than or equal to 1)"),
+ semsg(_("E802: Invalid ID: %" PRId64 " (must be greater than or equal to 1)"),
(int64_t)id);
}
return -1;
}
- while (cur != NULL && cur->id != id) {
+ while (cur != NULL && cur->mit_id != id) {
prev = cur;
- cur = cur->next;
+ cur = cur->mit_next;
}
if (cur == NULL) {
if (perr) {
@@ -248,28 +265,29 @@ static int match_delete(win_T *wp, int id, bool perr)
return -1;
}
if (cur == prev) {
- wp->w_match_head = cur->next;
+ wp->w_match_head = cur->mit_next;
} else {
- prev->next = cur->next;
+ prev->mit_next = cur->mit_next;
}
- vim_regfree(cur->match.regprog);
- xfree(cur->pattern);
- if (cur->pos.toplnum != 0) {
+ vim_regfree(cur->mit_match.regprog);
+ xfree(cur->mit_pattern);
+ if (cur->mit_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_top > cur->mit_toplnum) {
+ wp->w_buffer->b_mod_top = cur->mit_toplnum;
}
- if (wp->w_buffer->b_mod_bot < cur->pos.botlnum) {
- wp->w_buffer->b_mod_bot = cur->pos.botlnum;
+ if (wp->w_buffer->b_mod_bot < cur->mit_botlnum) {
+ wp->w_buffer->b_mod_bot = cur->mit_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_top = cur->mit_toplnum;
+ wp->w_buffer->b_mod_bot = cur->mit_botlnum;
wp->w_buffer->b_mod_xlines = 0;
}
- rtype = VALID;
+ rtype = UPD_VALID;
}
+ xfree(cur->mit_pos_array);
xfree(cur);
redraw_later(wp, rtype);
return 0;
@@ -281,23 +299,24 @@ 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);
+ m = wp->w_match_head->mit_next;
+ vim_regfree(wp->w_match_head->mit_match.regprog);
+ xfree(wp->w_match_head->mit_pattern);
+ xfree(wp->w_match_head->mit_pos_array);
xfree(wp->w_match_head);
wp->w_match_head = m;
}
- redraw_later(wp, SOME_VALID);
+ redraw_later(wp, UPD_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)
+static 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;
+ while (cur != NULL && cur->mit_id != id) {
+ cur = cur->mit_next;
}
return cur;
}
@@ -310,18 +329,18 @@ void init_search_hl(win_T *wp, match_T *search_hl)
// 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;
+ cur->mit_hl.rm = cur->mit_match;
+ if (cur->mit_hlg_id == 0) {
+ cur->mit_hl.attr = 0;
} else {
- cur->hl.attr = syn_id2attr(cur->hlg_id);
+ cur->mit_hl.attr = syn_id2attr(cur->mit_hlg_id);
}
- cur->hl.buf = wp->w_buffer;
- cur->hl.lnum = 0;
- cur->hl.first_lnum = 0;
+ cur->mit_hl.buf = wp->w_buffer;
+ cur->mit_hl.lnum = 0;
+ cur->mit_hl.first_lnum = 0;
// Set the time limit to 'redrawtime'.
- cur->hl.tm = profile_setlimit(p_rdt);
- cur = cur->next;
+ cur->mit_hl.tm = profile_setlimit(p_rdt);
+ cur = cur->mit_next;
}
search_hl->buf = wp->w_buffer;
search_hl->lnum = 0;
@@ -332,19 +351,19 @@ void init_search_hl(win_T *wp, match_T *search_hl)
}
/// @param shl points to a match. Fill on match.
-/// @param posmatch match positions
+/// @param posmatch match item with 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)
+static int next_search_hl_pos(match_T *shl, linenr_T lnum, matchitem_T *match, 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];
+ for (i = match->mit_pos_cur; i < match->mit_pos_count; i++) {
+ llpos_T *pos = &match->mit_pos_array[i];
if (pos->lnum == 0) {
break;
@@ -354,25 +373,24 @@ static int next_search_hl_pos(match_T *shl, linenr_T lnum, posmatch_T *posmatch,
}
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) {
+ // if this match comes before the one at "found" then swap them
+ if (pos->col < match->mit_pos_array[found].col) {
llpos_T tmp = *pos;
- *pos = posmatch->pos[found];
- posmatch->pos[found] = tmp;
+ *pos = match->mit_pos_array[found];
+ match->mit_pos_array[found] = tmp;
}
} else {
found = i;
}
}
}
- posmatch->cur = 0;
+ match->mit_pos_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;
+ colnr_T start = match->mit_pos_array[found].col == 0
+ ? 0: match->mit_pos_array[found].col - 1;
+ colnr_T end = match->mit_pos_array[found].col == 0
+ ? MAXCOL : start + match->mit_pos_array[found].len;
shl->lnum = lnum;
shl->rm.startpos[0].lnum = 0;
@@ -381,7 +399,7 @@ static int next_search_hl_pos(match_T *shl, linenr_T lnum, posmatch_T *posmatch,
shl->rm.endpos[0].col = end;
shl->is_addpos = true;
shl->has_cursor = false;
- posmatch->cur = found + 1;
+ match->mit_pos_cur = found + 1;
return 1;
}
return 0;
@@ -407,7 +425,7 @@ static void next_search_hl(win_T *win, match_T *search_hl, match_T *shl, linenr_
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) {
+ if ((lnum < search_first_line || lnum > search_last_line) && cur == NULL) {
shl->lnum = 0;
return;
}
@@ -443,7 +461,7 @@ static void next_search_hl(win_T *win, match_T *search_hl, match_T *shl, linenr_
} 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;
+ char *ml;
matchcol = shl->rm.startpos[0].col;
ml = ml_get_buf(shl->buf, lnum, false) + matchcol;
@@ -452,7 +470,7 @@ static void next_search_hl(win_T *win, match_T *search_hl, match_T *shl, linenr_
shl->lnum = 0;
break;
}
- matchcol += utfc_ptr2len((char *)ml);
+ matchcol += utfc_ptr2len(ml);
} else {
matchcol = shl->rm.endpos[0].col;
}
@@ -460,18 +478,17 @@ static void next_search_hl(win_T *win, match_T *search_hl, match_T *shl, linenr_
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);
+ // cur->mit_match.
+ bool regprog_is_copy = (shl != search_hl && cur != NULL
+ && shl == &cur->mit_hl
+ && cur->mit_match.regprog == cur->mit_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;
+ cur->mit_match.regprog = cur->mit_hl.rm.regprog;
}
if (called_emsg > called_emsg_before || got_int || timed_out) {
// Error while handling regexp: stop using this regexp.
@@ -486,7 +503,7 @@ static void next_search_hl(win_T *win, match_T *search_hl, match_T *shl, linenr_
break;
}
} else if (cur != NULL) {
- nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol);
+ nmatched = next_search_hl_pos(shl, lnum, cur, matchcol);
}
if (nmatched == 0) {
shl->lnum = 0; // no match found
@@ -521,7 +538,7 @@ void prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum)
shl = search_hl;
shl_flag = true;
} else {
- shl = &cur->hl; // -V595
+ shl = &cur->mit_hl; // -V595
}
if (shl->rm.regprog != NULL
&& shl->lnum == 0
@@ -536,7 +553,7 @@ void prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum)
}
}
if (cur != NULL) {
- cur->pos.cur = 0;
+ cur->mit_pos_cur = 0;
}
bool pos_inprogress = true; // mark that a position match search is
// in progress
@@ -545,7 +562,7 @@ void prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum)
|| (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);
+ pos_inprogress = !(cur == NULL || cur->mit_pos_cur == 0);
if (shl->lnum != 0) {
shl->first_lnum = shl->lnum
+ shl->rm.endpos[0].lnum
@@ -558,7 +575,7 @@ void prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum)
}
}
if (shl != search_hl && cur != NULL) {
- cur = cur->next;
+ cur = cur->mit_next;
}
}
}
@@ -580,9 +597,10 @@ static void check_cur_search_hl(win_T *wp, match_T *shl)
}
/// 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,
+///
+/// @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 **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
@@ -598,7 +616,7 @@ bool prepare_search_hl_line(win_T *wp, linenr_T lnum, colnr_T mincol, char_u **l
shl = search_hl;
shl_flag = true;
} else {
- shl = &cur->hl; // -V595
+ shl = &cur->mit_hl; // -V595
}
shl->startcol = MAXCOL;
shl->endcol = MAXCOL;
@@ -606,7 +624,7 @@ bool prepare_search_hl_line(win_T *wp, linenr_T lnum, colnr_T mincol, char_u **l
shl->is_addpos = false;
shl->has_cursor = false;
if (cur != NULL) {
- cur->pos.cur = 0;
+ cur->mit_pos_cur = 0;
}
next_search_hl(wp, search_hl, shl, lnum, mincol,
shl == search_hl ? NULL : cur);
@@ -636,7 +654,7 @@ bool prepare_search_hl_line(win_T *wp, linenr_T lnum, colnr_T mincol, char_u **l
// 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);
+ shl->endcol += utfc_ptr2len(*line + shl->endcol);
} else {
shl->endcol++;
}
@@ -649,7 +667,7 @@ bool prepare_search_hl_line(win_T *wp, linenr_T lnum, colnr_T mincol, char_u **l
area_highlighting = true;
}
if (shl != search_hl && cur != NULL) {
- cur = cur->next;
+ cur = cur->mit_next;
}
}
return area_highlighting;
@@ -660,9 +678,11 @@ bool prepare_search_hl_line(win_T *wp, linenr_T lnum, colnr_T mincol, char_u **l
/// 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!
+/// "on_last_col" is set to true with non-zero search_attr and the next column
+/// is endcol.
/// 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,
+int update_search_hl(win_T *wp, linenr_T lnum, colnr_T col, char **line, match_T *search_hl,
+ int *has_match_conc, int *match_conc, int lcs_eol_one, bool *on_last_col,
bool *search_attr_from_match)
{
matchitem_T *cur = wp->w_match_head; // points to the match list
@@ -674,14 +694,14 @@ int update_search_hl(win_T *wp, linenr_T lnum, colnr_T col, char_u **line, match
// 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)) {
+ && (cur == NULL || cur->mit_priority > SEARCH_HL_PRIORITY)) {
shl = search_hl;
shl_flag = true;
} else {
- shl = &cur->hl;
+ shl = &cur->mit_hl;
}
if (cur != NULL) {
- cur->pos.cur = 0;
+ cur->mit_pos_cur = 0;
}
bool pos_inprogress = true; // mark that a position match search is
// in progress
@@ -690,7 +710,7 @@ int update_search_hl(win_T *wp, linenr_T lnum, colnr_T col, char_u **line, match
if (shl->startcol != MAXCOL
&& col >= shl->startcol
&& col < shl->endcol) {
- int next_col = col + utfc_ptr2len((char *)(*line) + col);
+ int next_col = col + utfc_ptr2len(*line + col);
if (shl->endcol < next_col) {
shl->endcol = next_col;
@@ -706,9 +726,9 @@ int update_search_hl(win_T *wp, linenr_T lnum, colnr_T col, char_u **line, match
// the match.
if (cur != NULL
&& shl != search_hl
- && syn_name2id("Conceal") == cur->hlg_id) {
+ && syn_name2id("Conceal") == cur->mit_hlg_id) {
*has_match_conc = col == shl->startcol ? 2 : 1;
- *match_conc = cur->conceal_char;
+ *match_conc = cur->mit_conceal_char;
} else {
*has_match_conc = 0;
}
@@ -717,7 +737,7 @@ int update_search_hl(win_T *wp, linenr_T lnum, colnr_T col, char_u **line, match
next_search_hl(wp, search_hl, shl, lnum, col,
shl == search_hl ? NULL : cur);
- pos_inprogress = !(cur == NULL || cur->pos.cur == 0);
+ pos_inprogress = !(cur == NULL || cur->mit_pos_cur == 0);
// Need to get the line again, a multi-line regexp
// may have made it invalid.
@@ -738,7 +758,13 @@ int update_search_hl(win_T *wp, linenr_T lnum, colnr_T col, char_u **line, match
if (shl->startcol == shl->endcol) {
// highlight empty match, try again after it
- shl->endcol += utfc_ptr2len((char *)(*line) + shl->endcol);
+ char *p = *line + shl->endcol;
+
+ if (*p == NUL) {
+ shl->endcol++;
+ } else {
+ shl->endcol += utfc_ptr2len(p);
+ }
}
// Loop to check if the match starts at the
@@ -749,30 +775,31 @@ int update_search_hl(win_T *wp, linenr_T lnum, colnr_T col, char_u **line, match
break;
}
if (shl != search_hl && cur != NULL) {
- cur = cur->next;
+ cur = cur->mit_next;
}
}
- // Use attributes from match with highest priority among
- // 'search_hl' and the match list.
+ // 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)) {
+ && (cur == NULL || cur->mit_priority > SEARCH_HL_PRIORITY)) {
shl = search_hl;
shl_flag = true;
} else {
- shl = &cur->hl;
+ shl = &cur->mit_hl;
}
if (shl->attr_cur != 0) {
search_attr = shl->attr_cur;
+ *on_last_col = col + 1 >= shl->endcol;
*search_attr_from_match = shl != search_hl;
}
if (shl != search_hl && cur != NULL) {
- cur = cur->next;
+ cur = cur->mit_next;
}
}
// Only highlight one character after the last column.
@@ -792,17 +819,24 @@ bool get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol)
prevcol++;
}
- if (!search_hl->is_addpos && prevcol == search_hl->startcol) {
+ // Highlight a character after the end of the line if the match started
+ // at the end of the line or when the match continues in the next line
+ // (match includes the line break).
+ if (!search_hl->is_addpos && (prevcol == (long)search_hl->startcol
+ || (prevcol > (long)search_hl->startcol
+ && search_hl->endcol == MAXCOL))) {
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;
+ }
+ cur = wp->w_match_head;
+ while (cur != NULL) {
+ if (!cur->mit_hl.is_addpos && (prevcol == (long)cur->mit_hl.startcol
+ || (prevcol > (long)cur->mit_hl.startcol
+ && cur->mit_hl.endcol == MAXCOL))) {
+ return true;
}
+ cur = cur->mit_next;
}
+
return false;
}
@@ -815,21 +849,20 @@ void get_search_match_hl(win_T *wp, match_T *search_hl, long col, int *char_attr
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)) {
+ && (cur == NULL || cur->mit_priority > SEARCH_HL_PRIORITY)) {
shl = search_hl;
shl_flag = true;
} else {
- shl = &cur->hl;
+ shl = &cur->mit_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;
+ cur = cur->mit_next;
}
}
}
@@ -847,19 +880,21 @@ static int matchadd_dict_arg(typval_T *tv, const char **conceal_char, win_T **wi
*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;
- }
+ if ((di = tv_dict_find(tv->vval.v_dict, S_LEN("window"))) == NULL) {
+ return OK;
+ }
+
+ *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)
+void f_clearmatches(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
win_T *win = get_optional_window(argvars, 0);
@@ -869,7 +904,7 @@ void f_clearmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "getmatches()" function
-void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_getmatches(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
matchitem_T *cur;
int i;
@@ -883,13 +918,13 @@ void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
cur = win->w_match_head;
while (cur != NULL) {
dict_T *dict = tv_dict_alloc();
- if (cur->match.regprog == NULL) {
+ if (cur->mit_match.regprog == NULL) {
// match added with matchaddpos()
- for (i = 0; i < MAXPOSMATCH; i++) {
+ for (i = 0; i < cur->mit_pos_count; i++) {
llpos_T *llpos;
char buf[30]; // use 30 to avoid compiler warning
- llpos = &cur->pos.pos[i];
+ llpos = &cur->mit_pos_array[i];
if (llpos->lnum == 0) {
break;
}
@@ -904,27 +939,27 @@ void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
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("pattern"), (const char *)cur->mit_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);
+ (const char *)syn_id2name(cur->mit_hlg_id));
+ tv_dict_add_nr(dict, S_LEN("priority"), (varnumber_T)cur->mit_priority);
+ tv_dict_add_nr(dict, S_LEN("id"), (varnumber_T)cur->mit_id);
- if (cur->conceal_char) {
+ if (cur->mit_conceal_char) {
char buf[MB_MAXBYTES + 1];
- buf[utf_char2bytes(cur->conceal_char, buf)] = NUL;
+ buf[utf_char2bytes(cur->mit_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;
+ cur = cur->mit_next;
}
}
/// "setmatches()" function
-void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_setmatches(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
dict_T *d;
list_T *s = NULL;
@@ -1027,7 +1062,7 @@ void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "matchadd()" function
-void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_matchadd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char grpbuf[NUMBUFLEN];
char patbuf[NUMBUFLEN];
@@ -1069,7 +1104,7 @@ void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "matchaddpo()" function
-void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_matchaddpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = -1;
@@ -1120,7 +1155,7 @@ void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "matcharg()" function
-void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_matcharg(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const int id = (int)tv_get_number(&argvars[0]);
@@ -1133,8 +1168,8 @@ void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
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);
+ (const char *)syn_id2name(m->mit_hlg_id), -1);
+ tv_list_append_string(rettv->vval.v_list, (const char *)m->mit_pattern, -1);
} else {
tv_list_append_string(rettv->vval.v_list, NULL, 0);
tv_list_append_string(rettv->vval.v_list, NULL, 0);
@@ -1143,7 +1178,7 @@ void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "matchdelete()" function
-void f_matchdelete(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_matchdelete(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
win_T *win = get_optional_window(argvars, 1);
if (win == NULL) {
@@ -1159,9 +1194,9 @@ void f_matchdelete(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// skipping commands to find the next command.
void ex_match(exarg_T *eap)
{
- char_u *p;
- char_u *g = NULL;
- char_u *end;
+ char *p;
+ char *g = NULL;
+ char *end;
int c;
int id;
@@ -1178,25 +1213,25 @@ void ex_match(exarg_T *eap)
}
if (ends_excmd(*eap->arg)) {
- end = (char_u *)eap->arg;
+ end = 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;
+ end = eap->arg + 4;
} else {
- p = skiptowhite((char_u *)eap->arg);
+ p = skiptowhite(eap->arg);
if (!eap->skip) {
- g = vim_strnsave((char_u *)eap->arg, (size_t)(p - (char_u *)eap->arg));
+ g = xstrnsave(eap->arg, (size_t)(p - eap->arg));
}
- p = (char_u *)skipwhite((char *)p);
+ 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);
+ end = skip_regexp(p + 1, *p, true);
if (!eap->skip) {
- if (*end != NUL && !ends_excmd(*skipwhite((char *)end + 1))) {
+ if (*end != NUL && !ends_excmd(*skipwhite(end + 1))) {
xfree(g);
eap->errmsg = ex_errmsg(e_trailing_arg, (const char *)end);
return;
@@ -1207,13 +1242,13 @@ void ex_match(exarg_T *eap)
return;
}
- c = *end;
+ c = (uint8_t)(*end);
*end = NUL;
match_add(curwin, (const char *)g, (const char *)p + 1, 10, id,
NULL, NULL);
xfree(g);
- *end = (char_u)c;
+ *end = (char)c;
}
}
- eap->nextcmd = (char *)find_nextcmd(end);
+ eap->nextcmd = find_nextcmd(end);
}
diff --git a/src/nvim/math.c b/src/nvim/math.c
index b427688083..31c6b5af69 100644
--- a/src/nvim/math.c
+++ b/src/nvim/math.c
@@ -10,7 +10,7 @@
#include "nvim/math.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "math.c.generated.h"
+# include "math.c.generated.h" // IWYU pragma: export
#endif
int xfpclassify(double d)
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index af9e214d92..8b50ba719a 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -25,36 +25,51 @@
/// Vim scripts may contain an ":scriptencoding" command. This has an effect
/// for some commands, like ":menutrans".
-#include <inttypes.h>
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <iconv.h>
#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <wctype.h>
-#include "nvim/ascii.h"
-#include "nvim/vim.h"
-#ifdef HAVE_LOCALE_H
-# include <locale.h>
-#endif
+#include "auto/config.h"
#include "nvim/arabic.h"
+#include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/drawscreen.h"
-#include "nvim/eval.h"
-#include "nvim/fileio.h"
-#include "nvim/func_attr.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/grid_defs.h"
#include "nvim/iconv.h"
+#include "nvim/keycodes.h"
+#include "nvim/macros.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
+#include "nvim/mbyte_defs.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
+#include "nvim/option_defs.h"
#include "nvim/os/os.h"
-#include "nvim/path.h"
+#include "nvim/os/os_defs.h"
+#include "nvim/pos.h"
#include "nvim/screen.h"
-#include "nvim/spell.h"
#include "nvim/strings.h"
+#include "nvim/types.h"
+#include "nvim/vim.h"
+
+#ifdef HAVE_LOCALE_H
+# include <locale.h>
+#endif
typedef struct {
int rangeStart;
@@ -68,11 +83,12 @@ struct interval {
long last;
};
+// uncrustify:off
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "mbyte.c.generated.h"
-
# include "unicode_tables.generated.h"
#endif
+// uncrustify:on
static char e_list_item_nr_is_not_list[]
= N_("E1109: List item %d is not a List");
@@ -84,8 +100,8 @@ static char e_list_item_nr_cell_width_invalid[]
= N_("E1112: List item %d cell width invalid");
static char e_overlapping_ranges_for_nr[]
= N_("E1113: Overlapping ranges for 0x%lx");
-static char e_only_values_of_0x100_and_higher_supported[]
- = N_("E1114: Only values of 0x100 and higher supported");
+static char e_only_values_of_0x80_and_higher_supported[]
+ = N_("E1114: Only values of 0x80 and higher supported");
// To speed up BYTELEN(); keep a lookup table to quickly get the length in
// bytes of a UTF-8 character from the first byte of a UTF-8 string. Bytes
@@ -132,14 +148,11 @@ const uint8_t utf8len_tab_zero[] = {
4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 0, 0, // F?
};
-/*
- * Canonical encoding names and their properties.
- * "iso-8859-n" is handled by enc_canonize() directly.
- */
+// Canonical encoding names and their properties.
+// "iso-8859-n" is handled by enc_canonize() directly.
static struct
{ const char *name; int prop; int codepage; }
-enc_canon_table[] =
-{
+enc_canon_table[] = {
#define IDX_LATIN_1 0
{ "latin1", ENC_8BIT + ENC_LATIN1, 1252 },
#define IDX_ISO_2 1
@@ -269,13 +282,10 @@ enc_canon_table[] =
#define IDX_COUNT 59
};
-/*
- * Aliases for encoding names.
- */
+// Aliases for encoding names.
static struct
{ const char *name; int canon; }
-enc_alias_table[] =
-{
+enc_alias_table[] = {
{ "ansi", IDX_LATIN_1 },
{ "iso-8859-1", IDX_LATIN_1 },
{ "latin2", IDX_ISO_2 },
@@ -342,46 +352,40 @@ enc_alias_table[] =
{ NULL, 0 }
};
-/*
- * Find encoding "name" in the list of canonical encoding names.
- * Returns -1 if not found.
- */
-static int enc_canon_search(const char_u *name)
+/// Find encoding "name" in the list of canonical encoding names.
+/// Returns -1 if not found.
+static int enc_canon_search(const char *name)
FUNC_ATTR_PURE
{
for (int i = 0; i < IDX_COUNT; i++) {
- if (STRCMP(name, enc_canon_table[i].name) == 0) {
+ if (strcmp(name, enc_canon_table[i].name) == 0) {
return i;
}
}
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)
+// Find canonical encoding "name" in the list and return its properties.
+// Returns 0 if not found.
+int enc_canon_props(const char *name)
FUNC_ATTR_PURE
{
- int i = enc_canon_search(name);
+ int i = enc_canon_search((char *)name);
if (i >= 0) {
return enc_canon_table[i].prop;
- } else if (STRNCMP(name, "2byte-", 6) == 0) {
+ } else if (strncmp(name, "2byte-", 6) == 0) {
return ENC_DBCS;
- } else if (STRNCMP(name, "8bit-", 5) == 0 || STRNCMP(name, "iso-8859-", 9) == 0) {
+ } else if (strncmp(name, "8bit-", 5) == 0 || strncmp(name, "iso-8859-", 9) == 0) {
return ENC_8BIT;
}
return 0;
}
-/*
- * Return the size of the BOM for the current buffer:
- * 0 - no BOM
- * 2 - UCS-2 or UTF-16 BOM
- * 4 - UCS-4 BOM
- * 3 - UTF-8 BOM
- */
+// Return the size of the BOM for the current buffer:
+// 0 - no BOM
+// 2 - UCS-2 or UTF-16 BOM
+// 4 - UCS-4 BOM
+// 3 - UTF-8 BOM
int bomb_size(void)
FUNC_ATTR_PURE
{
@@ -389,24 +393,22 @@ int bomb_size(void)
if (curbuf->b_p_bomb && !curbuf->b_p_bin) {
if (*curbuf->b_p_fenc == NUL
- || STRCMP(curbuf->b_p_fenc, "utf-8") == 0) {
+ || strcmp(curbuf->b_p_fenc, "utf-8") == 0) {
n = 3;
- } else if (STRNCMP(curbuf->b_p_fenc, "ucs-2", 5) == 0
- || STRNCMP(curbuf->b_p_fenc, "utf-16", 6) == 0) {
+ } else if (strncmp(curbuf->b_p_fenc, "ucs-2", 5) == 0
+ || strncmp(curbuf->b_p_fenc, "utf-16", 6) == 0) {
n = 2;
- } else if (STRNCMP(curbuf->b_p_fenc, "ucs-4", 5) == 0) {
+ } else if (strncmp(curbuf->b_p_fenc, "ucs-4", 5) == 0) {
n = 4;
}
}
return n;
}
-/*
- * Remove all BOM from "s" by moving remaining text.
- */
-void remove_bom(char_u *s)
+// Remove all BOM from "s" by moving remaining text.
+void remove_bom(char *s)
{
- char *p = (char *)s;
+ char *p = s;
while ((p = strchr(p, 0xef)) != NULL) {
if ((uint8_t)p[1] == 0xbb && (uint8_t)p[2] == 0xbf) {
@@ -417,37 +419,33 @@ void remove_bom(char_u *s)
}
}
-/*
- * Get class of pointer:
- * 0 for blank or NUL
- * 1 for punctuation
- * 2 for an (ASCII) word character
- * >2 for other word characters
- */
-int mb_get_class(const char_u *p)
+// Get class of pointer:
+// 0 for blank or NUL
+// 1 for punctuation
+// 2 for an (ASCII) word character
+// >2 for other word characters
+int mb_get_class(const char *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)
+int mb_get_class_tab(const char *p, const uint64_t *const chartab)
FUNC_ATTR_PURE
{
- if (MB_BYTE2LEN(p[0]) == 1) {
+ if (MB_BYTE2LEN((uint8_t)p[0]) == 1) {
if (p[0] == NUL || ascii_iswhite(p[0])) {
return 0;
}
- if (vim_iswordc_tab(p[0], chartab)) {
+ if (vim_iswordc_tab((uint8_t)p[0], chartab)) {
return 2;
}
return 1;
}
- return utf_class_tab(utf_ptr2char((char *)p), chartab);
+ return utf_class_tab(utf_ptr2char(p), chartab);
}
-/*
- * Return true if "c" is in "table".
- */
+// Return true if "c" is in "table".
static bool intable(const struct interval *table, size_t n_items, int c)
FUNC_ATTR_PURE
{
@@ -484,12 +482,16 @@ static bool intable(const struct interval *table, size_t n_items, int c)
/// gen_unicode_tables.lua, which must be manually invoked as needed.
int utf_char2cells(int c)
{
- if (c >= 0x100) {
+ // Use the value from setcellwidths() at 0x80 and higher, unless the
+ // character is not printable.
+ if (c >= 0x80 && vim_isprintc(c)) {
int n = cw_value(c);
if (n != 0) {
return n;
}
+ }
+ if (c >= 0x100) {
if (!utf_printable(c)) {
return 6; // unprintable, displays <xxxx>
}
@@ -536,13 +538,13 @@ int utf_ptr2cells(const char *p)
/// Like utf_ptr2cells(), but limit string length to "size".
/// For an empty string or truncated character returns 1.
-int utf_ptr2cells_len(const char_u *p, int size)
+int utf_ptr2cells_len(const char *p, int size)
{
int c;
// Need to convert to a wide character.
- if (size > 0 && *p >= 0x80) {
- if (utf_ptr2len_len(p, size) < utf8len_tab[*p]) {
+ if (size > 0 && (uint8_t)(*p) >= 0x80) {
+ if (utf_ptr2len_len(p, size) < utf8len_tab[(uint8_t)(*p)]) {
return 1; // truncated
}
c = utf_ptr2char((char *)p);
@@ -568,8 +570,8 @@ size_t mb_string2cells(const char *str)
{
size_t clen = 0;
- for (const char_u *p = (char_u *)str; *p != NUL; p += utfc_ptr2len((char *)p)) {
- clen += (size_t)utf_ptr2cells((char *)p);
+ for (const char *p = str; *p != NUL; p += utfc_ptr2len(p)) {
+ clen += (size_t)utf_ptr2cells(p);
}
return clen;
@@ -586,9 +588,9 @@ size_t mb_string2cells_len(const char *str, size_t size)
{
size_t clen = 0;
- for (const char_u *p = (char_u *)str; *p != NUL && p < (char_u *)str + size;
- p += utfc_ptr2len_len(p, (int)size + (int)(p - (char_u *)str))) {
- clen += (size_t)utf_ptr2cells((char *)p);
+ for (const char *p = str; *p != NUL && p < str + size;
+ p += utfc_ptr2len_len(p, (int)size + (int)(p - str))) {
+ clen += (size_t)utf_ptr2cells(p);
}
return clen;
@@ -601,7 +603,7 @@ size_t mb_string2cells_len(const char *str, size_t size)
/// For an overlong sequence this may return zero.
/// Does not include composing characters for obvious reasons.
///
-/// @param[in] p String to convert.
+/// @param[in] p_in String to convert.
///
/// @return Unicode codepoint or byte value.
int utf_ptr2char(const char *const p_in)
@@ -646,22 +648,20 @@ int utf_ptr2char(const char *const p_in)
return p[0];
}
-/*
- * Convert a UTF-8 byte sequence to a wide character.
- * String is assumed to be terminated by NUL or after "n" bytes, whichever
- * comes first.
- * The function is safe in the sense that it never accesses memory beyond the
- * first "n" bytes of "s".
- *
- * On success, returns decoded codepoint, advances "s" to the beginning of
- * next character and decreases "n" accordingly.
- *
- * If end of string was reached, returns 0 and, if "n" > 0, advances "s" past
- * NUL byte.
- *
- * If byte sequence is illegal or incomplete, returns -1 and does not advance
- * "s".
- */
+// Convert a UTF-8 byte sequence to a wide character.
+// String is assumed to be terminated by NUL or after "n" bytes, whichever
+// comes first.
+// The function is safe in the sense that it never accesses memory beyond the
+// first "n" bytes of "s".
+//
+// On success, returns decoded codepoint, advances "s" to the beginning of
+// next character and decreases "n" accordingly.
+//
+// If end of string was reached, returns 0 and, if "n" > 0, advances "s" past
+// NUL byte.
+//
+// If byte sequence is illegal or incomplete, returns -1 and does not advance
+// "s".
static int utf_safe_read_char_adv(const char_u **s, size_t *n)
{
int c;
@@ -701,38 +701,32 @@ static int utf_safe_read_char_adv(const char_u **s, size_t *n)
return -1;
}
-/*
- * Get character at **pp and advance *pp to the next character.
- * Note: composing characters are skipped!
- */
-int mb_ptr2char_adv(const char_u **const pp)
+// Get character at **pp and advance *pp to the next character.
+// Note: composing characters are skipped!
+int mb_ptr2char_adv(const char **const pp)
{
int c;
- c = utf_ptr2char((char *)(*pp));
- *pp += utfc_ptr2len((char *)(*pp));
+ c = utf_ptr2char(*pp);
+ *pp += utfc_ptr2len(*pp);
return c;
}
-/*
- * Get character at **pp and advance *pp to the next character.
- * Note: composing characters are returned as separate characters.
- */
-int mb_cptr2char_adv(const char_u **pp)
+// Get character at **pp and advance *pp to the next character.
+// Note: composing characters are returned as separate characters.
+int mb_cptr2char_adv(const char **pp)
{
int c;
- c = utf_ptr2char((char *)(*pp));
- *pp += utf_ptr2len((char *)(*pp));
+ c = utf_ptr2char(*pp);
+ *pp += utf_ptr2len(*pp);
return c;
}
-/*
- * Check if the character pointed to by "p2" is a composing character when it
- * comes after "p1". For Arabic sometimes "ab" is replaced with "c", which
- * behaves like a composing character.
- */
-bool utf_composinglike(const char_u *p1, const char_u *p2)
+/// Check if the character pointed to by "p2" is a composing character when it
+/// comes after "p1". For Arabic sometimes "ab" is replaced with "c", which
+/// behaves like a composing character.
+bool utf_composinglike(const char *p1, const char *p2)
{
int c2;
@@ -754,28 +748,25 @@ bool utf_composinglike(const char_u *p1, const char_u *p2)
/// space at least for #MAX_MCO + 1 elements.
///
/// @return leading character.
-int utfc_ptr2char(const char_u *p, int *pcc)
+int utfc_ptr2char(const char *p, int *pcc)
{
- int len;
- int c;
- int cc;
int i = 0;
- c = utf_ptr2char((char *)p);
- len = utf_ptr2len((char *)p);
+ int c = utf_ptr2char(p);
+ int len = utf_ptr2len(p);
// Only accept a composing char when the first char isn't illegal.
- if ((len > 1 || *p < 0x80)
- && p[len] >= 0x80
+ if ((len > 1 || (uint8_t)(*p) < 0x80)
+ && (uint8_t)p[len] >= 0x80
&& utf_composinglike(p, p + len)) {
- cc = utf_ptr2char((char *)p + len);
+ int cc = utf_ptr2char(p + len);
for (;;) {
pcc[i++] = cc;
if (i == MAX_MCO) {
break;
}
- len += utf_ptr2len((char *)p + len);
- if (p[len] < 0x80 || !utf_iscomposing(cc = utf_ptr2char((char *)p + len))) {
+ len += utf_ptr2len(p + len);
+ if ((uint8_t)p[len] < 0x80 || !utf_iscomposing(cc = utf_ptr2char(p + len))) {
break;
}
}
@@ -788,13 +779,11 @@ int utfc_ptr2char(const char_u *p, int *pcc)
return c;
}
-/*
- * Convert a UTF-8 byte string to a wide character. Also get up to MAX_MCO
- * composing characters. Use no more than p[maxlen].
- *
- * @param [out] pcc: composing chars, last one is 0
- */
-int utfc_ptr2char_len(const char_u *p, int *pcc, int maxlen)
+// Convert a UTF-8 byte string to a wide character. Also get up to MAX_MCO
+// composing characters. Use no more than p[maxlen].
+//
+// @param [out] pcc: composing chars, last one is 0
+int utfc_ptr2char_len(const char *p, int *pcc, int maxlen)
{
assert(maxlen > 0);
@@ -803,14 +792,14 @@ 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((char *)p) : *p;
+ int c = safe ? utf_ptr2char(p) : (uint8_t)(*p);
// Only accept a composing char when the first char isn't illegal.
- if ((safe || c < 0x80) && len < maxlen && p[len] >= 0x80) {
+ if ((safe || c < 0x80) && len < maxlen && (uint8_t)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((char *)p + len)) < 0x80
+ if (!safe || (pcc[i] = utf_ptr2char(p + len)) < 0x80
|| !(i == 0 ? utf_composinglike(p, p + len) : utf_iscomposing(pcc[i]))) {
break;
}
@@ -849,31 +838,27 @@ int utf_ptr2len(const char *const p_in)
return len;
}
-/*
- * Return length of UTF-8 character, obtained from the first byte.
- * "b" must be between 0 and 255!
- * Returns 1 for an invalid first byte value.
- */
+// Return length of UTF-8 character, obtained from the first byte.
+// "b" must be between 0 and 255!
+// Returns 1 for an invalid first byte value.
int utf_byte2len(int b)
{
return utf8len_tab[b];
}
-/*
- * Get the length of UTF-8 byte sequence "p[size]". Does not include any
- * following composing characters.
- * Returns 1 for "".
- * Returns 1 for an illegal byte sequence (also in incomplete byte seq.).
- * Returns number > "size" for an incomplete byte sequence.
- * Never returns zero.
- */
-int utf_ptr2len_len(const char_u *p, int size)
+// Get the length of UTF-8 byte sequence "p[size]". Does not include any
+// following composing characters.
+// Returns 1 for "".
+// Returns 1 for an illegal byte sequence (also in incomplete byte seq.).
+// Returns number > "size" for an incomplete byte sequence.
+// Never returns zero.
+int utf_ptr2len_len(const char *p, int size)
{
int len;
int i;
int m;
- len = utf8len_tab[*p];
+ len = utf8len_tab[(uint8_t)(*p)];
if (len == 1) {
return 1; // NUL, ascii or illegal lead byte
}
@@ -882,7 +867,7 @@ int utf_ptr2len_len(const char_u *p, int size)
} else {
m = len;
}
- for (i = 1; i < m; ++i) {
+ for (i = 1; i < m; i++) {
if ((p[i] & 0xc0) != 0x80) {
return 1;
}
@@ -893,21 +878,20 @@ 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.
/// Returns zero for NUL.
-int utfc_ptr2len(const char *const p_in)
+int utfc_ptr2len(const char *const p)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
- uint8_t *p = (uint8_t *)p_in;
- uint8_t b0 = *p;
+ uint8_t b0 = (uint8_t)(*p);
if (b0 == NUL) {
return 0;
}
- if (b0 < 0x80 && p[1] < 0x80) { // be quick for ASCII
+ if (b0 < 0x80 && (uint8_t)p[1] < 0x80) { // be quick for ASCII
return 1;
}
// Skip over first UTF-8 char, stopping at a NUL byte.
- int len = utf_ptr2len((char *)p);
+ int len = utf_ptr2len(p);
// Check for illegal byte.
if (len == 1 && b0 >= 0x80) {
@@ -918,23 +902,21 @@ int utfc_ptr2len(const char *const p_in)
// skip all of them (otherwise the cursor would get stuck).
int prevlen = 0;
for (;;) {
- if (p[len] < 0x80 || !utf_composinglike(p + prevlen, p + len)) {
+ if ((uint8_t)p[len] < 0x80 || !utf_composinglike(p + prevlen, p + len)) {
return len;
}
// Skip over composing char.
prevlen = len;
- len += utf_ptr2len((char *)p + len);
+ len += utf_ptr2len(p + len);
}
}
-/*
- * Return the number of bytes the UTF-8 encoding of the character at "p[size]"
- * takes. This includes following composing characters.
- * Returns 0 for an empty string.
- * Returns 1 for an illegal char or an incomplete byte sequence.
- */
-int utfc_ptr2len_len(const char_u *p, int size)
+/// Return the number of bytes the UTF-8 encoding of the character at "p[size]"
+/// takes. This includes following composing characters.
+/// Returns 0 for an empty string.
+/// Returns 1 for an illegal char or an incomplete byte sequence.
+int utfc_ptr2len_len(const char *p, int size)
{
int len;
int prevlen;
@@ -942,7 +924,7 @@ int utfc_ptr2len_len(const char_u *p, int size)
if (size < 1 || *p == NUL) {
return 0;
}
- if (p[0] < 0x80 && (size == 1 || p[1] < 0x80)) { // be quick for ASCII
+ if ((uint8_t)p[0] < 0x80 && (size == 1 || (uint8_t)p[1] < 0x80)) { // be quick for ASCII
return 1;
}
@@ -950,26 +932,22 @@ int utfc_ptr2len_len(const char_u *p, int size)
len = utf_ptr2len_len(p, size);
// Check for illegal byte and incomplete byte sequence.
- if ((len == 1 && p[0] >= 0x80) || len > size) {
+ if ((len == 1 && (uint8_t)p[0] >= 0x80) || len > size) {
return 1;
}
- /*
- * Check for composing characters. We can handle only the first six, but
- * skip all of them (otherwise the cursor would get stuck).
- */
+ // Check for composing characters. We can handle only the first six, but
+ // skip all of them (otherwise the cursor would get stuck).
prevlen = 0;
while (len < size) {
int len_next_char;
- if (p[len] < 0x80) {
+ if ((uint8_t)p[len] < 0x80) {
break;
}
- /*
- * Next character length should not go beyond size to ensure that
- * utf_composinglike(...) does not read beyond size.
- */
+ // Next character length should not go beyond size to ensure that
+ // utf_composinglike(...) does not read beyond size.
len_next_char = utf_ptr2len_len(p + len, size - len);
if (len_next_char > size - len) {
break;
@@ -1048,26 +1026,21 @@ int utf_char2bytes(const int c, char *const buf)
}
}
-/*
- * Return true if "c" is a composing UTF-8 character. This means it will be
- * drawn on top of the preceding character.
- * Based on code from Markus Kuhn.
- */
+// Return true if "c" is a composing UTF-8 character. This means it will be
+// drawn on top of the preceding character.
+// Based on code from Markus Kuhn.
bool utf_iscomposing(int c)
{
return intable(combining, ARRAY_SIZE(combining), c);
}
-/*
- * Return true for characters that can be displayed in a normal way.
- * Only for characters of 0x100 and above!
- */
+// Return true for characters that can be displayed in a normal way.
+// Only for characters of 0x100 and above!
bool utf_printable(int c)
{
// Sorted list of non-overlapping intervals.
// 0xd800-0xdfff is reserved for UTF-16, actually illegal.
- static struct interval nonprint[] =
- {
+ static struct interval nonprint[] = {
{ 0x070f, 0x070f }, { 0x180b, 0x180e }, { 0x200b, 0x200f }, { 0x202a, 0x202e },
{ 0x2060, 0x206f }, { 0xd800, 0xdfff }, { 0xfeff, 0xfeff }, { 0xfff9, 0xfffb },
{ 0xfffe, 0xffff }
@@ -1076,12 +1049,10 @@ bool utf_printable(int c)
return !intable(nonprint, ARRAY_SIZE(nonprint), c);
}
-/*
- * Get class of a Unicode character.
- * 0: white space
- * 1: punctuation
- * 2 or bigger: some class of word character.
- */
+// Get class of a Unicode character.
+// 0: white space
+// 1: punctuation
+// 2 or bigger: some class of word character.
int utf_class(const int c)
{
return utf_class_tab(c, curbuf->b_chartab);
@@ -1210,11 +1181,9 @@ bool utf_ambiguous_width(int c)
|| intable(emoji_all, ARRAY_SIZE(emoji_all), c));
}
-/*
- * Generic conversion function for case operations.
- * Return the converted equivalent of "a", which is a UCS-4 character. Use
- * the given conversion "table". Uses binary search on "table".
- */
+// Generic conversion function for case operations.
+// Return the converted equivalent of "a", which is a UCS-4 character. Use
+// the given conversion "table". Uses binary search on "table".
static int utf_convert(int a, const convertStruct *const table, size_t n_items)
{
size_t start, mid, end; // indices into table
@@ -1235,15 +1204,12 @@ static int utf_convert(int a, const convertStruct *const table, size_t n_items)
&& a <= table[start].rangeEnd
&& (a - table[start].rangeStart) % table[start].step == 0) {
return a + table[start].offset;
- } else {
- return a;
}
+ return a;
}
-/*
- * Return the folded-case equivalent of "a", which is a UCS-4 character. Uses
- * simple case folding.
- */
+// Return the folded-case equivalent of "a", which is a UCS-4 character. Uses
+// simple case folding.
int utf_fold(int a)
{
if (a < 0x80) {
@@ -1267,12 +1233,9 @@ int mb_toupper(int a)
return TOUPPER_ASC(a);
}
-#if defined(__STDC_ISO_10646__)
- // If towupper() is available and handles Unicode, use it.
if (!(cmp_flags & CMP_INTERNAL)) {
return (int)towupper((wint_t)a);
}
-#endif
// For characters below 128 use locale sensitive toupper().
if (a < 128) {
@@ -1298,12 +1261,9 @@ int mb_tolower(int a)
return TOLOWER_ASC(a);
}
-#if defined(__STDC_ISO_10646__)
- // If towlower() is available and handles Unicode, use it.
if (!(cmp_flags & CMP_INTERNAL)) {
return (int)towlower((wint_t)a);
}
-#endif
// For characters below 128 use locale sensitive tolower().
if (a < 128) {
@@ -1398,7 +1358,7 @@ static int utf_strnicmp(const char_u *s1, const char_u *s2, size_t n1, size_t n2
return n1 == 0 ? -1 : 1;
}
-#ifdef WIN32
+#ifdef MSWIN
# ifndef CP_UTF8
# define CP_UTF8 65001 // magic number from winnls.h
# endif
@@ -1500,16 +1460,16 @@ int utf16_to_utf8(const wchar_t *utf16, int utf16len, char **utf8)
/// @param len maximum length (an earlier NUL terminates)
/// @param[out] codepoints incremented with UTF-32 code point size
/// @param[out] codeunits incremented with UTF-16 code unit size
-void mb_utflen(const char_u *s, size_t len, size_t *codepoints, size_t *codeunits)
+void mb_utflen(const char *s, size_t len, size_t *codepoints, size_t *codeunits)
FUNC_ATTR_NONNULL_ALL
{
size_t count = 0, extra = 0;
size_t clen;
- for (size_t i = 0; i < len && s[i] != NUL; i += clen) {
+ for (size_t i = 0; i < len; i += clen) {
clen = (size_t)utf_ptr2len_len(s + i, (int)(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((char *)s + i) : s[i];
+ int c = (clen > 1) ? utf_ptr2char(s + i) : (uint8_t)s[i];
count++;
if (c > 0xFFFF) {
extra++;
@@ -1519,7 +1479,7 @@ void mb_utflen(const char_u *s, size_t len, size_t *codepoints, size_t *codeunit
*codeunits += count + extra;
}
-ssize_t mb_utf_index_to_bytes(const char_u *s, size_t len, size_t index, bool use_utf16_units)
+ssize_t mb_utf_index_to_bytes(const char *s, size_t len, size_t index, bool use_utf16_units)
FUNC_ATTR_NONNULL_ALL
{
size_t count = 0;
@@ -1527,11 +1487,11 @@ ssize_t mb_utf_index_to_bytes(const char_u *s, size_t len, size_t index, bool us
if (index == 0) {
return 0;
}
- for (i = 0; i < len && s[i] != NUL; i += clen) {
+ for (i = 0; i < len; i += clen) {
clen = (size_t)utf_ptr2len_len(s + i, (int)(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((char *)s + i) : s[i];
+ int c = (clen > 1) ? utf_ptr2char(s + i) : (uint8_t)s[i];
count++;
if (use_utf16_units && c > 0xFFFF) {
count++;
@@ -1543,17 +1503,16 @@ ssize_t mb_utf_index_to_bytes(const char_u *s, size_t len, size_t index, bool us
return -1;
}
-/*
- * Version of strnicmp() that handles multi-byte characters.
- * Needed for Big5, Shift-JIS and UTF-8 encoding. Other DBCS encodings can
- * probably use strnicmp(), because there are no ASCII characters in the
- * second byte.
- * Returns zero if s1 and s2 are equal (ignoring case), the difference between
- * two characters otherwise.
- */
-int mb_strnicmp(const char_u *s1, const char_u *s2, const size_t nn)
+/// Version of strnicmp() that handles multi-byte characters.
+/// Needed for Big5, Shift-JIS and UTF-8 encoding. Other DBCS encodings can
+/// probably use strnicmp(), because there are no ASCII characters in the
+/// second byte.
+///
+/// @return zero if s1 and s2 are equal (ignoring case), the difference between
+/// two characters otherwise.
+int mb_strnicmp(const char *s1, const char *s2, const size_t nn)
{
- return utf_strnicmp(s1, s2, nn, nn);
+ return utf_strnicmp((char_u *)s1, (char_u *)s2, nn, nn);
}
/// Compare strings case-insensitively
@@ -1570,72 +1529,73 @@ int mb_strnicmp(const char_u *s1, const char_u *s2, const size_t nn)
/// @return 0 if strings are equal, <0 if s1 < s2, >0 if s1 > s2.
int mb_stricmp(const char *s1, const char *s2)
{
- return mb_strnicmp((const char_u *)s1, (const char_u *)s2, MAXCOL);
+ return mb_strnicmp(s1, s2, MAXCOL);
}
-/*
- * "g8": show bytes of the UTF-8 char under the cursor. Doesn't matter what
- * 'encoding' has been set to.
- */
+// "g8": show bytes of the UTF-8 char under the cursor. Doesn't matter what
+// 'encoding' has been set to.
void show_utf8(void)
{
int len;
int rlen = 0;
- char_u *line;
+ char *line;
int clen;
int i;
// Get the byte length of the char under the cursor, including composing
// characters.
line = get_cursor_pos_ptr();
- len = utfc_ptr2len((char *)line);
+ len = utfc_ptr2len(line);
if (len == 0) {
msg("NUL");
return;
}
clen = 0;
- for (i = 0; i < len; ++i) {
+ for (i = 0; i < len; i++) {
if (clen == 0) {
// start of (composing) character, get its length
if (i > 0) {
STRCPY(IObuff + rlen, "+ ");
rlen += 2;
}
- clen = utf_ptr2len((char *)line + i);
+ clen = utf_ptr2len(line + i);
}
- sprintf((char *)IObuff + rlen, "%02x ",
- (line[i] == NL) ? NUL : line[i]); // NUL is stored as NL
+ sprintf(IObuff + rlen, "%02x ", // NOLINT(runtime/printf)
+ (line[i] == NL) ? NUL : (uint8_t)line[i]); // NUL is stored as NL
clen--;
- rlen += (int)STRLEN(IObuff + rlen);
+ rlen += (int)strlen(IObuff + rlen);
if (rlen > IOSIZE - 20) {
break;
}
}
- msg((char *)IObuff);
+ msg(IObuff);
}
/// Return offset from "p" to the start of a character, including composing characters.
/// "base" must be the start of the string, which must be NUL terminated.
/// If "p" points to the NUL at the end of the string return 0.
/// Returns 0 when already at the first byte of a character.
-int utf_head_off(const char_u *base, const char_u *p)
+int utf_head_off(const char *base_in, const char *p_in)
{
int c;
int len;
- if (*p < 0x80) { // be quick for ASCII
+ if ((uint8_t)(*p_in) < 0x80) { // be quick for ASCII
return 0;
}
+ const uint8_t *base = (uint8_t *)base_in;
+ const uint8_t *p = (uint8_t *)p_in;
+
// Skip backwards over trailing bytes: 10xx.xxxx
// Skip backwards again if on a composing char.
- const char_u *q;
- for (q = p;; --q) {
+ const uint8_t *q;
+ for (q = p;; q--) {
// Move s to the last byte of this char.
- const char_u *s;
- for (s = q; (s[1] & 0xc0) == 0x80; ++s) {}
+ const uint8_t *s;
+ for (s = q; (s[1] & 0xc0) == 0x80; s++) {}
// Move q to the first byte of this char.
while (q > base && (*q & 0xc0) == 0x80) {
@@ -1659,7 +1619,7 @@ int utf_head_off(const char_u *base, const char_u *p)
if (arabic_maycombine(c)) {
// Advance to get a sneak-peak at the next char
- const char_u *j = q;
+ const uint8_t *j = q;
j--;
// Move j to the first byte of this char.
while (j > base && (*j & 0xc0) == 0x80) {
@@ -1828,11 +1788,12 @@ void mb_copy_char(const char **const fp, char **const tp)
*fp += l;
}
-/// Return the offset from "p" to the first byte of a character. When "p" is
+/// Return the offset from "p_in" to the first byte of a character. When "p_in" 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 mb_off_next(const char *base, const char *p_in)
{
+ const uint8_t *p = (uint8_t *)p_in;
int i;
int j;
@@ -1844,7 +1805,7 @@ int mb_off_next(const char_u *base, const char_u *p)
for (i = 0; (p[i] & 0xc0) == 0x80; i++) {}
if (i > 0) {
// Check for illegal sequence.
- for (j = 0; p - j > base; j++) {
+ for (j = 0; p - j > (uint8_t *)base; j++) {
if ((p[-j] & 0xc0) != 0x80) {
break;
}
@@ -1920,16 +1881,14 @@ int utf_cp_head_off(const char_u *base, const char_u *p)
return i;
}
-/*
- * Find the next illegal byte sequence.
- */
+// Find the next illegal byte sequence.
void utf_find_illegal(void)
{
pos_T pos = curwin->w_cursor;
- char_u *p;
+ char *p;
int len;
vimconv_T vimconv;
- char_u *tofree = NULL;
+ char *tofree = NULL;
vimconv.vc_type = CONV_NONE;
if (enc_canon_props(curbuf->b_p_fenc) & ENC_8BIT) {
@@ -1954,9 +1913,8 @@ 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((char *)p);
- if (*p >= 0x80 && (len == 1
- || utf_char2len(utf_ptr2char((char *)p)) != len)) {
+ len = utf_ptr2len(p);
+ if ((uint8_t)(*p) >= 0x80 && (len == 1 || utf_char2len(utf_ptr2char(p)) != len)) {
if (vimconv.vc_type == CONV_NONE) {
curwin->w_cursor.col += (colnr_T)(p - get_cursor_pos_ptr());
} else {
@@ -1964,7 +1922,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((char *)p);
+ l = utf_ptr2len(p);
curwin->w_cursor.col += l;
}
}
@@ -1975,7 +1933,7 @@ void utf_find_illegal(void)
if (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count) {
break;
}
- ++curwin->w_cursor.lnum;
+ curwin->w_cursor.lnum++;
curwin->w_cursor.col = 0;
}
@@ -1989,8 +1947,7 @@ theend:
}
/// @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.
+/// When "end" is NULL stop at the first NUL. Otherwise stop at "end".
bool utf_valid_string(const char_u *s, const char_u *end)
{
const char_u *p = s;
@@ -2013,10 +1970,8 @@ bool utf_valid_string(const char_u *s, const char_u *end)
return true;
}
-/*
- * If the cursor moves on an trail byte, set the cursor on the lead byte.
- * Thus it moves left if necessary.
- */
+// If the cursor moves on an trail byte, set the cursor on the lead byte.
+// Thus it moves left if necessary.
void mb_adjust_cursor(void)
{
mark_mb_adjustpos(curbuf, &curwin->w_cursor);
@@ -2033,8 +1988,8 @@ void mb_check_adjust_col(void *win_)
// Column 0 is always valid.
if (oldcol != 0) {
- char *p = (char *)ml_get_buf(win->w_buffer, win->w_cursor.lnum, false);
- colnr_T len = (colnr_T)STRLEN(p);
+ char *p = ml_get_buf(win->w_buffer, win->w_cursor.lnum, false);
+ colnr_T len = (colnr_T)strlen(p);
// Empty line or invalid column?
if (len == 0 || oldcol < 0) {
@@ -2045,7 +2000,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((char_u *)p, (char_u *)p + win->w_cursor.col);
+ win->w_cursor.col -= utf_head_off(p, p + win->w_cursor.col);
}
// Reset `coladd` when the cursor would be on the right half of a
@@ -2061,7 +2016,7 @@ void mb_check_adjust_col(void *win_)
/// @param line start of the string
///
/// @return a pointer to the character before "*p", if there is one.
-char_u *mb_prevptr(char_u *line, char_u *p)
+char *mb_prevptr(char *line, char *p)
{
if (p > line) {
MB_PTR_BACK(line, p);
@@ -2071,9 +2026,9 @@ char_u *mb_prevptr(char_u *line, char_u *p)
/// Return the character length of "str". Each multi-byte character (with
/// following composing characters) counts as one.
-int mb_charlen(const char_u *str)
+int mb_charlen(const char *str)
{
- const char_u *p = str;
+ const char *p = str;
int count;
if (p == NULL) {
@@ -2081,20 +2036,20 @@ int mb_charlen(const char_u *str)
}
for (count = 0; *p != NUL; count++) {
- p += utfc_ptr2len((char *)p);
+ p += utfc_ptr2len(p);
}
return count;
}
/// Like mb_charlen() but for a string with specified length.
-int mb_charlen_len(const char_u *str, int len)
+int mb_charlen_len(const char *str, int len)
{
- const char_u *p = str;
+ const char *p = str;
int count;
for (count = 0; *p != NUL && p < str + len; count++) {
- p += utfc_ptr2len((char *)p);
+ p += utfc_ptr2len(p);
}
return count;
@@ -2147,45 +2102,41 @@ const char *mb_unescape(const char **const pp)
return NULL;
}
-/*
- * Skip the Vim specific head of a 'encoding' name.
- */
-char_u *enc_skip(char_u *p)
+/// Skip the Vim specific head of a 'encoding' name.
+char *enc_skip(char *p)
{
- if (STRNCMP(p, "2byte-", 6) == 0) {
+ if (strncmp(p, "2byte-", 6) == 0) {
return p + 6;
}
- if (STRNCMP(p, "8bit-", 5) == 0) {
+ if (strncmp(p, "8bit-", 5) == 0) {
return p + 5;
}
return p;
}
-/*
- * Find the canonical name for encoding "enc".
- * When the name isn't recognized, returns "enc" itself, but with all lower
- * case characters and '_' replaced with '-'.
- * Returns an allocated string.
- */
-char_u *enc_canonize(char_u *enc) FUNC_ATTR_NONNULL_RET
+/// Find the canonical name for encoding "enc".
+/// When the name isn't recognized, returns "enc" itself, but with all lower
+/// case characters and '_' replaced with '-'.
+///
+/// @return an allocated string.
+char *enc_canonize(char *enc)
+ FUNC_ATTR_NONNULL_RET
{
- char_u *p, *s;
- int i;
-
- if (STRCMP(enc, "default") == 0) {
+ char *p, *s;
+ if (strcmp(enc, "default") == 0) {
// Use the default encoding as found by set_init_1().
- return vim_strsave(fenc_default);
+ return xstrdup(fenc_default);
}
// copy "enc" to allocated memory, with room for two '-'
- char_u *r = xmalloc(STRLEN(enc) + 3);
+ char *r = xmalloc(strlen(enc) + 3);
// Make it all lower case and replace '_' with '-'.
p = r;
- for (s = enc; *s != NUL; ++s) {
+ for (s = enc; *s != NUL; s++) {
if (*s == '_') {
*p++ = '-';
} else {
- *p++ = (char_u)TOLOWER_ASC(*s);
+ *p++ = (char)TOLOWER_ASC(*s);
}
}
*p = NUL;
@@ -2194,27 +2145,28 @@ char_u *enc_canonize(char_u *enc) FUNC_ATTR_NONNULL_RET
p = enc_skip(r);
// Change "microsoft-cp" to "cp". Used in some spell files.
- if (STRNCMP(p, "microsoft-cp", 12) == 0) {
+ if (strncmp(p, "microsoft-cp", 12) == 0) {
STRMOVE(p, p + 10);
}
// "iso8859" -> "iso-8859"
- if (STRNCMP(p, "iso8859", 7) == 0) {
+ if (strncmp(p, "iso8859", 7) == 0) {
STRMOVE(p + 4, p + 3);
p[3] = '-';
}
// "iso-8859n" -> "iso-8859-n"
- if (STRNCMP(p, "iso-8859", 8) == 0 && p[8] != '-') {
+ if (strncmp(p, "iso-8859", 8) == 0 && p[8] != '-') {
STRMOVE(p + 9, p + 8);
p[8] = '-';
}
// "latin-N" -> "latinN"
- if (STRNCMP(p, "latin-", 6) == 0) {
+ if (strncmp(p, "latin-", 6) == 0) {
STRMOVE(p + 5, p + 6);
}
+ int i;
if (enc_canon_search(p) >= 0) {
// canonical name can be used unmodified
if (p != r) {
@@ -2223,19 +2175,19 @@ char_u *enc_canonize(char_u *enc) FUNC_ATTR_NONNULL_RET
} else if ((i = enc_alias_search(p)) >= 0) {
// alias recognized, get canonical name
xfree(r);
- r = vim_strsave((char_u *)enc_canon_table[i].name);
+ r = xstrdup(enc_canon_table[i].name);
}
return r;
}
/// Search for an encoding alias of "name".
/// Returns -1 when not found.
-static int enc_alias_search(const char_u *name)
+static int enc_alias_search(const char *name)
{
int i;
- for (i = 0; enc_alias_table[i].name != NULL; ++i) {
- if (STRCMP(name, enc_alias_table[i].name) == 0) {
+ for (i = 0; enc_alias_table[i].name != NULL; i++) {
+ if (strcmp(name, enc_alias_table[i].name) == 0) {
return enc_alias_table[i].canon;
}
}
@@ -2246,11 +2198,9 @@ static int enc_alias_search(const char_u *name)
# include <langinfo.h>
#endif
-/*
- * Get the canonicalized encoding of the current locale.
- * Returns an allocated string when successful, NULL when not.
- */
-char_u *enc_locale(void)
+// Get the canonicalized encoding of the current locale.
+// Returns an allocated string when successful, NULL when not.
+char *enc_locale(void)
{
int i;
char buf[50];
@@ -2286,7 +2236,7 @@ char_u *enc_locale(void)
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] == '_') {
+ && !isalnum((uint8_t)p[4]) && p[4] != '-' && p[-3] == '_') {
// Copy "XY.EUC" to "euc-XY" to buf[10].
memmove(buf, "euc-", 4);
buf[4] = (char)(ASCII_ISALNUM(p[-2]) ? TOLOWER_ASC(p[-2]) : 0);
@@ -2310,22 +2260,18 @@ enc_locale_copy_enc:
buf[i] = NUL;
}
- return enc_canonize((char_u *)buf);
+ return enc_canonize(buf);
}
-#if defined(HAVE_ICONV)
-
-/*
- * Call iconv_open() with a check if iconv() works properly (there are broken
- * versions).
- * Returns (void *)-1 if failed.
- * (should return iconv_t, but that causes problems with prototypes).
- */
-void *my_iconv_open(char_u *to, char_u *from)
+// Call iconv_open() with a check if iconv() works properly (there are broken
+// versions).
+// Returns (void *)-1 if failed.
+// (should return iconv_t, but that causes problems with prototypes).
+void *my_iconv_open(char *to, char *from)
{
iconv_t fd;
-# define ICONV_TESTLEN 400
- char_u tobuf[ICONV_TESTLEN];
+#define ICONV_TESTLEN 400
+ char tobuf[ICONV_TESTLEN];
char *p;
size_t tolen;
static WorkingStatus iconv_working = kUnknown;
@@ -2333,17 +2279,15 @@ void *my_iconv_open(char_u *to, char_u *from)
if (iconv_working == kBroken) {
return (void *)-1; // detected a broken iconv() previously
}
- fd = iconv_open((char *)enc_skip(to), (char *)enc_skip(from));
+ fd = iconv_open(enc_skip(to), enc_skip(from));
if (fd != (iconv_t)-1 && iconv_working == kUnknown) {
- /*
- * Do a dummy iconv() call to check if it actually works. There is a
- * version of iconv() on Linux that is broken. We can't ignore it,
- * because it's wide-spread. The symptoms are that after outputting
- * the initial shift state the "to" pointer is NULL and conversion
- * stops for no apparent reason after about 8160 characters.
- */
- p = (char *)tobuf;
+ // Do a dummy iconv() call to check if it actually works. There is a
+ // version of iconv() on Linux that is broken. We can't ignore it,
+ // because it's wide-spread. The symptoms are that after outputting
+ // the initial shift state the "to" pointer is NULL and conversion
+ // stops for no apparent reason after about 8160 characters.
+ p = tobuf;
tolen = ICONV_TESTLEN;
(void)iconv(fd, NULL, NULL, &p, &tolen);
if (p == NULL) {
@@ -2358,15 +2302,13 @@ void *my_iconv_open(char_u *to, char_u *from)
return (void *)fd;
}
-/*
- * Convert the string "str[slen]" with iconv().
- * If "unconvlenp" is not NULL handle the string ending in an incomplete
- * sequence and set "*unconvlenp" to the length of it.
- * Returns the converted string in allocated memory. NULL for an error.
- * If resultlenp is not NULL, sets it to the result length in bytes.
- */
-static char_u *iconv_string(const vimconv_T *const vcp, char_u *str, size_t slen,
- size_t *unconvlenp, size_t *resultlenp)
+// Convert the string "str[slen]" with iconv().
+// If "unconvlenp" is not NULL handle the string ending in an incomplete
+// sequence and set "*unconvlenp" to the length of it.
+// Returns the converted string in allocated memory. NULL for an error.
+// If resultlenp is not NULL, sets it to the result length in bytes.
+static char *iconv_string(const vimconv_T *const vcp, const char *str, size_t slen,
+ size_t *unconvlenp, size_t *resultlenp)
{
const char *from;
size_t fromlen;
@@ -2374,11 +2316,11 @@ static char_u *iconv_string(const vimconv_T *const vcp, char_u *str, size_t slen
size_t tolen;
size_t len = 0;
size_t done = 0;
- char_u *result = NULL;
- char_u *p;
+ char *result = NULL;
+ char *p;
int l;
- from = (char *)str;
+ from = str;
fromlen = slen;
for (;;) {
if (len == 0 || ICONV_ERRNO == ICONV_E2BIG) {
@@ -2393,7 +2335,7 @@ static char_u *iconv_string(const vimconv_T *const vcp, char_u *str, size_t slen
result = p;
}
- to = (char *)result + done;
+ to = result + done;
tolen = len - done - 2;
// Avoid a warning for systems with a wrong iconv() prototype by
// casting the second argument to void *.
@@ -2424,7 +2366,7 @@ static char_u *iconv_string(const vimconv_T *const vcp, char_u *str, size_t slen
if (utf_ptr2cells(from) > 1) {
*to++ = '?';
}
- l = utfc_ptr2len_len((const char_u *)from, (int)fromlen);
+ l = utfc_ptr2len_len(from, (int)fromlen);
from += l;
fromlen -= (size_t)l;
} else if (ICONV_ERRNO != ICONV_E2BIG) {
@@ -2433,34 +2375,31 @@ static char_u *iconv_string(const vimconv_T *const vcp, char_u *str, size_t slen
break;
}
// Not enough room or skipping illegal sequence.
- done = (size_t)(to - (char *)result);
+ done = (size_t)(to - result);
}
if (resultlenp != NULL && result != NULL) {
- *resultlenp = (size_t)(to - (char *)result);
+ *resultlenp = (size_t)(to - result);
}
return result;
}
-#endif // HAVE_ICONV
-
-/*
- * Setup "vcp" for conversion from "from" to "to".
- * The names must have been made canonical with enc_canonize().
- * vcp->vc_type must have been initialized to CONV_NONE.
- * Note: cannot be used for conversion from/to ucs-2 and ucs-4 (will use utf-8
- * instead).
- * Afterwards invoke with "from" and "to" equal to NULL to cleanup.
- * Return FAIL when conversion is not supported, OK otherwise.
- */
-int convert_setup(vimconv_T *vcp, char_u *from, char_u *to)
+/// Setup "vcp" for conversion from "from" to "to".
+/// The names must have been made canonical with enc_canonize().
+/// vcp->vc_type must have been initialized to CONV_NONE.
+/// Note: cannot be used for conversion from/to ucs-2 and ucs-4 (will use utf-8
+/// instead).
+/// Afterwards invoke with "from" and "to" equal to NULL to cleanup.
+///
+/// @return FAIL when conversion is not supported, OK otherwise.
+int convert_setup(vimconv_T *vcp, char *from, char *to)
{
return convert_setup_ext(vcp, from, true, to, true);
}
/// As convert_setup(), but only when from_unicode_is_utf8 is true will all
/// "from" unicode charsets be considered utf-8. Same for "to".
-int convert_setup_ext(vimconv_T *vcp, char_u *from, bool from_unicode_is_utf8, char_u *to,
+int convert_setup_ext(vimconv_T *vcp, char *from, bool from_unicode_is_utf8, char *to,
bool to_unicode_is_utf8)
{
int from_prop;
@@ -2469,16 +2408,14 @@ int convert_setup_ext(vimconv_T *vcp, char_u *from, bool from_unicode_is_utf8, c
int to_is_utf8;
// Reset to no conversion.
-#ifdef HAVE_ICONV
if (vcp->vc_type == CONV_ICONV && vcp->vc_fd != (iconv_t)-1) {
iconv_close(vcp->vc_fd);
}
-#endif
*vcp = (vimconv_T)MBYTE_NONE_CONV;
// No conversion when one of the names is empty or they are equal.
if (from == NULL || *from == NUL || to == NULL || *to == NUL
- || STRCMP(from, to) == 0) {
+ || strcmp(from, to) == 0) {
return OK;
}
@@ -2509,18 +2446,15 @@ int convert_setup_ext(vimconv_T *vcp, char_u *from, bool from_unicode_is_utf8, c
} else if (from_is_utf8 && (to_prop & ENC_LATIN9)) {
// Internal utf-8 -> latin9 conversion.
vcp->vc_type = CONV_TO_LATIN9;
- }
-#ifdef HAVE_ICONV
- else { // NOLINT(readability/braces)
+ } else {
// Use iconv() for conversion.
- vcp->vc_fd = (iconv_t)my_iconv_open(to_is_utf8 ? (char_u *)"utf-8" : to,
- from_is_utf8 ? (char_u *)"utf-8" : from);
+ vcp->vc_fd = (iconv_t)my_iconv_open(to_is_utf8 ? "utf-8" : to,
+ from_is_utf8 ? "utf-8" : from);
if (vcp->vc_fd != (iconv_t)-1) {
vcp->vc_type = CONV_ICONV;
vcp->vc_factor = 4; // could be longer too...
}
}
-#endif
if (vcp->vc_type == CONV_NONE) {
return FAIL;
}
@@ -2528,25 +2462,20 @@ int convert_setup_ext(vimconv_T *vcp, char_u *from, bool from_unicode_is_utf8, c
return OK;
}
-/*
- * Convert text "ptr[*lenp]" according to "vcp".
- * Returns the result in allocated memory and sets "*lenp".
- * When "lenp" is NULL, use NUL terminated strings.
- * Illegal chars are often changed to "?", unless vcp->vc_fail is set.
- * When something goes wrong, NULL is returned and "*lenp" is unchanged.
- */
-char_u *string_convert(const vimconv_T *const vcp, char_u *ptr, size_t *lenp)
+/// Convert text "ptr[*lenp]" according to "vcp".
+/// Returns the result in allocated memory and sets "*lenp".
+/// When "lenp" is NULL, use NUL terminated strings.
+/// Illegal chars are often changed to "?", unless vcp->vc_fail is set.
+/// When something goes wrong, NULL is returned and "*lenp" is unchanged.
+char *string_convert(const vimconv_T *const vcp, char *ptr, size_t *lenp)
{
return string_convert_ext(vcp, ptr, lenp, NULL);
}
-/*
- * Like string_convert(), but when "unconvlenp" is not NULL and there are is
- * an incomplete sequence at the end it is not converted and "*unconvlenp" is
- * set to the number of remaining bytes.
- */
-char_u *string_convert_ext(const vimconv_T *const vcp, char_u *ptr, size_t *lenp,
- size_t *unconvlenp)
+// Like string_convert(), but when "unconvlenp" is not NULL and there are is
+// an incomplete sequence at the end it is not converted and "*unconvlenp" is
+// set to the number of remaining bytes.
+char *string_convert_ext(const vimconv_T *const vcp, char *ptr, size_t *lenp, size_t *unconvlenp)
{
char_u *retval = NULL;
char_u *d;
@@ -2555,20 +2484,20 @@ char_u *string_convert_ext(const vimconv_T *const vcp, char_u *ptr, size_t *lenp
size_t len;
if (lenp == NULL) {
- len = STRLEN(ptr);
+ len = strlen(ptr);
} else {
len = *lenp;
}
if (len == 0) {
- return vim_strsave((char_u *)"");
+ return xstrdup("");
}
switch (vcp->vc_type) {
case CONV_TO_UTF8: // latin1 to utf-8 conversion
retval = xmalloc(len * 2 + 1);
d = retval;
- for (size_t i = 0; i < len; ++i) {
- c = ptr[i];
+ for (size_t i = 0; i < len; i++) {
+ c = (uint8_t)ptr[i];
if (c < 0x80) {
*d++ = (char_u)c;
} else {
@@ -2585,8 +2514,8 @@ char_u *string_convert_ext(const vimconv_T *const vcp, char_u *ptr, size_t *lenp
case CONV_9_TO_UTF8: // latin9 to utf-8 conversion
retval = xmalloc(len * 3 + 1);
d = retval;
- for (size_t i = 0; i < len; ++i) {
- c = ptr[i];
+ for (size_t i = 0; i < len; i++) {
+ c = (uint8_t)ptr[i];
switch (c) {
case 0xa4:
c = 0x20ac; break; // euro
@@ -2622,7 +2551,7 @@ char_u *string_convert_ext(const vimconv_T *const vcp, char_u *ptr, size_t *lenp
if (l == 0) {
*d++ = NUL;
} else if (l == 1) {
- uint8_t l_w = utf8len_tab_zero[ptr[i]];
+ uint8_t l_w = utf8len_tab_zero[(uint8_t)ptr[i]];
if (l_w == 0) {
// Illegal utf-8 byte cannot be converted
@@ -2634,9 +2563,9 @@ char_u *string_convert_ext(const vimconv_T *const vcp, char_u *ptr, size_t *lenp
*unconvlenp = len - i;
break;
}
- *d++ = ptr[i];
+ *d++ = (uint8_t)ptr[i];
} else {
- c = utf_ptr2char((char *)ptr + i);
+ c = utf_ptr2char(ptr + i);
if (vcp->vc_type == CONV_TO_LATIN9) {
switch (c) {
case 0x20ac:
@@ -2688,14 +2617,12 @@ char_u *string_convert_ext(const vimconv_T *const vcp, char_u *ptr, size_t *lenp
}
break;
-#ifdef HAVE_ICONV
case CONV_ICONV: // conversion with vcp->vc_fd
- retval = iconv_string(vcp, ptr, len, unconvlenp, lenp);
+ retval = (char_u *)iconv_string(vcp, ptr, len, unconvlenp, lenp);
break;
-#endif
}
- return retval;
+ return (char *)retval;
}
/// Table set by setcellwidths().
@@ -2748,7 +2675,7 @@ static int tv_nr_compare(const void *a1, const void *a2)
}
/// "setcellwidths()" function
-void f_setcellwidths(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_setcellwidths(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[0].v_type != VAR_LIST || argvars[0].vval.v_list == NULL) {
emsg(_(e_listreq));
@@ -2774,7 +2701,7 @@ void f_setcellwidths(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (li_tv->v_type != VAR_LIST || li_tv->vval.v_list == NULL) {
semsg(_(e_list_item_nr_is_not_list), item);
- xfree(ptrs);
+ xfree((void *)ptrs);
return;
}
@@ -2790,25 +2717,25 @@ void f_setcellwidths(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
if (i == 0) {
n1 = lili_tv->vval.v_number;
- if (n1 < 0x100) {
- emsg(_(e_only_values_of_0x100_and_higher_supported));
- xfree(ptrs);
+ if (n1 < 0x80) {
+ emsg(_(e_only_values_of_0x80_and_higher_supported));
+ xfree((void *)ptrs);
return;
}
} else if (i == 1 && lili_tv->vval.v_number < n1) {
semsg(_(e_list_item_nr_range_invalid), item);
- xfree(ptrs);
+ xfree((void *)ptrs);
return;
} else if (i == 2 && (lili_tv->vval.v_number < 1 || lili_tv->vval.v_number > 2)) {
semsg(_(e_list_item_nr_cell_width_invalid), item);
- xfree(ptrs);
+ xfree((void *)ptrs);
return;
}
}
if (i != 3) {
semsg(_(e_list_item_nr_does_not_contain_3_numbers), item);
- xfree(ptrs);
+ xfree((void *)ptrs);
return;
}
@@ -2827,7 +2754,7 @@ void f_setcellwidths(typval_T *argvars, typval_T *rettv, FunPtr fptr)
const varnumber_T n1 = TV_LIST_ITEM_TV(lili)->vval.v_number;
if (item > 0 && n1 <= table[item - 1].last) {
semsg(_(e_overlapping_ranges_for_nr), (long)n1);
- xfree(ptrs);
+ xfree((void *)ptrs);
xfree(table);
return;
}
@@ -2838,7 +2765,7 @@ void f_setcellwidths(typval_T *argvars, typval_T *rettv, FunPtr fptr)
table[item].width = (char)TV_LIST_ITEM_TV(lili)->vval.v_number;
}
- xfree(ptrs);
+ xfree((void *)ptrs);
cw_interval_T *const cw_table_save = cw_table;
const size_t cw_table_size_save = cw_table_size;
@@ -2857,14 +2784,29 @@ void f_setcellwidths(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
xfree(cw_table_save);
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
+}
+
+/// "getcellwidths()" function
+void f_getcellwidths(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ tv_list_alloc_ret(rettv, (ptrdiff_t)cw_table_size);
+
+ for (size_t i = 0; i < cw_table_size; i++) {
+ list_T *entry = tv_list_alloc(3);
+ tv_list_append_number(entry, (varnumber_T)cw_table[i].first);
+ tv_list_append_number(entry, (varnumber_T)cw_table[i].last);
+ tv_list_append_number(entry, (varnumber_T)cw_table[i].width);
+
+ tv_list_append_list(rettv->vval.v_list, entry);
+ }
}
-void f_charclass(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_charclass(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- if (tv_check_for_string(&argvars[0]) == FAIL
+ if (tv_check_for_string_arg(argvars, 0) == FAIL
|| argvars[0].vval.v_string == NULL) {
return;
}
- rettv->vval.v_number = mb_get_class((const char_u *)argvars[0].vval.v_string);
+ rettv->vval.v_number = mb_get_class(argvars[0].vval.v_string);
}
diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h
index 2a9afcbd03..780f33e05b 100644
--- a/src/nvim/mbyte.h
+++ b/src/nvim/mbyte.h
@@ -8,15 +8,13 @@
#include "nvim/eval/typval.h"
#include "nvim/func_attr.h"
#include "nvim/mbyte_defs.h"
-#include "nvim/os/os_defs.h" // For indirect
-#include "nvim/types.h" // for char_u
-
-/*
- * Return byte length of character that starts with byte "b".
- * Returns 1 for a single-byte character.
- * MB_BYTE2LEN_CHECK() can be used to count a special key as one byte.
- * Don't call MB_BYTE2LEN(b) with b < 0 or b > 255!
- */
+#include "nvim/os/os_defs.h"
+#include "nvim/types.h"
+
+// Return byte length of character that starts with byte "b".
+// Returns 1 for a single-byte character.
+// MB_BYTE2LEN_CHECK() can be used to count a special key as one byte.
+// Don't call MB_BYTE2LEN(b) with b < 0 or b > 255!
#define MB_BYTE2LEN(b) utf8len_tab[b]
#define MB_BYTE2LEN_CHECK(b) (((b) < 0 || (b) > 255) ? 1 : utf8len_tab[b])
diff --git a/src/nvim/mbyte_defs.h b/src/nvim/mbyte_defs.h
index 53b01a211f..e913e20f9f 100644
--- a/src/nvim/mbyte_defs.h
+++ b/src/nvim/mbyte_defs.h
@@ -46,9 +46,7 @@ typedef enum {
typedef struct {
int vc_type; ///< Zero or more ConvFlags.
int vc_factor; ///< Maximal expansion factor.
-#ifdef HAVE_ICONV
iconv_t vc_fd; ///< Value for CONV_ICONV.
-#endif
bool vc_fail; ///< What to do with invalid characters: if true, fail,
///< otherwise use '?'.
} vimconv_T;
diff --git a/src/nvim/memfile.c b/src/nvim/memfile.c
index 216ee26620..46be9ccea5 100644
--- a/src/nvim/memfile.c
+++ b/src/nvim/memfile.c
@@ -43,19 +43,25 @@
#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
+#include <stdio.h>
#include <string.h>
-#include "nvim/ascii.h"
#include "nvim/assert.h"
+#include "nvim/buffer_defs.h"
#include "nvim/fileio.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/macros.h"
#include "nvim/memfile.h"
+#include "nvim/memfile_defs.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
+#include "nvim/os/fs_defs.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
-#include "nvim/os_unix.h"
#include "nvim/path.h"
+#include "nvim/pos.h"
#include "nvim/vim.h"
#define MEMFILE_PAGE_SIZE 4096 /// default page size
@@ -77,7 +83,7 @@
///
/// @return - The open memory file, on success.
/// - NULL, on failure (e.g. file does not exist).
-memfile_T *mf_open(char_u *fname, int flags)
+memfile_T *mf_open(char *fname, int flags)
{
memfile_T *mfp = xmalloc(sizeof(memfile_T));
@@ -148,7 +154,7 @@ memfile_T *mf_open(char_u *fname, int flags)
///
/// @return OK On success.
/// FAIL If file could not be opened.
-int mf_open_file(memfile_T *mfp, char_u *fname)
+int mf_open_file(memfile_T *mfp, char *fname)
{
if (mf_do_open(mfp, fname, O_RDWR | O_CREAT | O_EXCL)) {
mfp->mf_dirty = true;
@@ -170,7 +176,7 @@ void mf_close(memfile_T *mfp, bool del_file)
emsg(_(e_swapclose));
}
if (del_file && mfp->mf_fname != NULL) {
- os_remove((char *)mfp->mf_fname);
+ os_remove(mfp->mf_fname);
}
// free entries in used list
@@ -210,7 +216,7 @@ void mf_close_file(buf_T *buf, bool getlines)
mfp->mf_fd = -1;
if (mfp->mf_fname != NULL) {
- os_remove((char *)mfp->mf_fname); // delete the swap file
+ os_remove(mfp->mf_fname); // delete the swap file
mf_free_fnames(mfp);
}
}
@@ -749,10 +755,10 @@ void mf_free_fnames(memfile_T *mfp)
///
/// Only called when creating or renaming the swapfile. Either way it's a new
/// name so we must work out the full path name.
-void mf_set_fnames(memfile_T *mfp, char_u *fname)
+void mf_set_fnames(memfile_T *mfp, char *fname)
{
mfp->mf_fname = fname;
- mfp->mf_ffname = (char_u *)FullName_save((char *)mfp->mf_fname, false);
+ mfp->mf_ffname = FullName_save(mfp->mf_fname, false);
}
/// Make name of memfile's swapfile a full path.
@@ -760,11 +766,13 @@ void mf_set_fnames(memfile_T *mfp, char_u *fname)
/// Used before doing a :cd
void mf_fullname(memfile_T *mfp)
{
- if (mfp != NULL && mfp->mf_fname != NULL && mfp->mf_ffname != NULL) {
- xfree(mfp->mf_fname);
- mfp->mf_fname = mfp->mf_ffname;
- mfp->mf_ffname = NULL;
+ if (mfp == NULL || mfp->mf_fname == NULL || mfp->mf_ffname == NULL) {
+ return;
}
+
+ xfree(mfp->mf_fname);
+ mfp->mf_fname = mfp->mf_ffname;
+ mfp->mf_ffname = NULL;
}
/// Return true if there are any translations pending for memfile.
@@ -779,7 +787,7 @@ bool mf_need_trans(memfile_T *mfp)
///
/// @param flags Flags for open().
/// @return A bool indicating success of the `open` call.
-static bool mf_do_open(memfile_T *mfp, char_u *fname, int flags)
+static bool mf_do_open(memfile_T *mfp, char *fname, int flags)
{
// fname cannot be NameBuff, because it must have been allocated.
mf_set_fnames(mfp, fname);
@@ -789,7 +797,7 @@ static bool mf_do_open(memfile_T *mfp, char_u *fname, int flags)
/// exist yet. If there is a symbolic link, this is most likely an attack.
FileInfo file_info;
if ((flags & O_CREAT)
- && os_fileinfo_link((char *)mfp->mf_fname, &file_info)) {
+ && os_fileinfo_link(mfp->mf_fname, &file_info)) {
mfp->mf_fd = -1;
emsg(_("E300: Swap file already exists (symlink attack?)"));
} else {
@@ -815,8 +823,10 @@ static bool mf_do_open(memfile_T *mfp, char_u *fname, int flags)
/// The number of buckets in the hashtable is increased by a factor of
/// MHT_GROWTH_FACTOR when the average number of items per bucket
/// exceeds 2 ^ MHT_LOG_LOAD_FACTOR.
-#define MHT_LOG_LOAD_FACTOR 6
-#define MHT_GROWTH_FACTOR 2 // must be a power of two
+enum {
+ MHT_LOG_LOAD_FACTOR = 6,
+ MHT_GROWTH_FACTOR = 2, // must be a power of two
+};
/// Initialize an empty hash table.
static void mf_hash_init(mf_hashtab_T *mht)
diff --git a/src/nvim/memfile_defs.h b/src/nvim/memfile_defs.h
index 537d3e5f65..53152c28f8 100644
--- a/src/nvim/memfile_defs.h
+++ b/src/nvim/memfile_defs.h
@@ -88,8 +88,8 @@ typedef struct mf_blocknr_trans_item {
/// A memory file.
typedef struct memfile {
- char_u *mf_fname; /// name of the file
- char_u *mf_ffname; /// idem, full path
+ char *mf_fname; /// name of the file
+ char *mf_ffname; /// idem, full path
int mf_fd; /// file descriptor
bhdr_T *mf_free_first; /// first block header in free list
bhdr_T *mf_used_first; /// mru block header in used list
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 23bc5d59c8..10a8195e7a 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -5,55 +5,65 @@
// #define CHECK(c, s) do { if (c) emsg(s); } while (0)
#define CHECK(c, s) do {} while (0)
-/*
- * memline.c: Contains the functions for appending, deleting and changing the
- * text lines. The memfile functions are used to store the information in
- * blocks of memory, backed up by a file. The structure of the information is
- * a tree. The root of the tree is a pointer block. The leaves of the tree
- * are data blocks. In between may be several layers of pointer blocks,
- * forming branches.
- *
- * Three types of blocks are used:
- * - Block nr 0 contains information for recovery
- * - Pointer blocks contain list of pointers to other blocks.
- * - Data blocks contain the actual text.
- *
- * Block nr 0 contains the block0 structure (see below).
- *
- * Block nr 1 is the first pointer block. It is the root of the tree.
- * Other pointer blocks are branches.
- *
- * If a line is too big to fit in a single page, the block containing that
- * line is made big enough to hold the line. It may span several pages.
- * Otherwise all blocks are one page.
- *
- * A data block that was filled when starting to edit a file and was not
- * changed since then, can have a negative block number. This means that it
- * has not yet been assigned a place in the file. When recovering, the lines
- * in this data block can be read from the original file. When the block is
- * changed (lines appended/deleted/changed) or when it is flushed it gets a
- * positive number. Use mf_trans_del() to get the new number, before calling
- * mf_get().
- */
+// memline.c: Contains the functions for appending, deleting and changing the
+// text lines. The memfile functions are used to store the information in
+// blocks of memory, backed up by a file. The structure of the information is
+// a tree. The root of the tree is a pointer block. The leaves of the tree
+// are data blocks. In between may be several layers of pointer blocks,
+// forming branches.
+//
+// Three types of blocks are used:
+// - Block nr 0 contains information for recovery
+// - Pointer blocks contain list of pointers to other blocks.
+// - Data blocks contain the actual text.
+//
+// Block nr 0 contains the block0 structure (see below).
+//
+// Block nr 1 is the first pointer block. It is the root of the tree.
+// Other pointer blocks are branches.
+//
+// If a line is too big to fit in a single page, the block containing that
+// line is made big enough to hold the line. It may span several pages.
+// Otherwise all blocks are one page.
+//
+// A data block that was filled when starting to edit a file and was not
+// changed since then, can have a negative block number. This means that it
+// has not yet been assigned a place in the file. When recovering, the lines
+// in this data block can be read from the original file. When the block is
+// changed (lines appended/deleted/changed) or when it is flushed it gets a
+// positive number. Use mf_trans_del() to get the new number, before calling
+// mf_get().
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdbool.h>
+#include <stdio.h>
#include <string.h>
+#include <time.h>
+#include <uv.h>
+#include "auto/config.h"
+#include "klib/kvec.h"
#include "nvim/ascii.h"
#include "nvim/autocmd.h"
#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
#include "nvim/change.h"
#include "nvim/cursor.h"
#include "nvim/drawscreen.h"
#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/fileio.h"
-#include "nvim/func_attr.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/highlight_defs.h"
#include "nvim/input.h"
+#include "nvim/macros.h"
#include "nvim/main.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
@@ -62,19 +72,21 @@
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/option.h"
+#include "nvim/os/fs.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/os/process.h"
-#include "nvim/os_unix.h"
+#include "nvim/os/time.h"
#include "nvim/path.h"
-#include "nvim/sha256.h"
+#include "nvim/pos.h"
+#include "nvim/screen.h"
#include "nvim/spell.h"
#include "nvim/strings.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/version.h"
#include "nvim/vim.h"
-#include "nvim/window.h"
#ifndef UNIX // it's in os/unix_defs.h for Unix
# include <time.h>
@@ -85,14 +97,14 @@ typedef struct pointer_block PTR_BL; // contents of a pointer block
typedef struct data_block DATA_BL; // contents of a data block
typedef struct pointer_entry PTR_EN; // block/line-count pair
-#define DATA_ID (('d' << 8) + 'a') // data block id
-#define PTR_ID (('p' << 8) + 't') // pointer block id
-#define BLOCK0_ID0 'b' // block 0 id 0
-#define BLOCK0_ID1 '0' // block 0 id 1
+enum {
+ DATA_ID = (('d' << 8) + 'a'), // data block id
+ PTR_ID = (('p' << 8) + 't'), // pointer block id
+ BLOCK0_ID0 = 'b', // block 0 id 0
+ BLOCK0_ID1 = '0', // block 0 id 1
+};
-/*
- * pointer to a block, used in a pointer block
- */
+// pointer to a block, used in a pointer block
struct pointer_entry {
blocknr_T pe_bnum; // block number
linenr_T pe_line_count; // number of lines in this branch
@@ -100,9 +112,7 @@ struct pointer_entry {
int pe_page_count; // number of pages in block pe_bnum
};
-/*
- * A pointer block contains a list of branches in the tree.
- */
+// A pointer block contains a list of branches in the tree.
struct pointer_block {
uint16_t pb_id; // ID for pointer block: PTR_ID
uint16_t pb_count; // number of pointers in this block
@@ -111,94 +121,87 @@ struct pointer_block {
// followed by empty space until end of page
};
-/*
- * A data block is a leaf in the tree.
- *
- * The text of the lines is at the end of the block. The text of the first line
- * in the block is put at the end, the text of the second line in front of it,
- * etc. Thus the order of the lines is the opposite of the line number.
- */
+// A data block is a leaf in the tree.
+//
+// The text of the lines is at the end of the block. The text of the first line
+// in the block is put at the end, the text of the second line in front of it,
+// etc. Thus the order of the lines is the opposite of the line number.
struct data_block {
uint16_t db_id; // ID for data block: DATA_ID
unsigned db_free; // free space available
unsigned db_txt_start; // byte where text starts
unsigned db_txt_end; // byte just after data block
- linenr_T db_line_count; // number of lines in this block
+ // linenr_T db_line_count;
+ long db_line_count; // number of lines in this block
unsigned db_index[1]; // index for start of line (actually bigger)
// followed by empty space up to db_txt_start
// followed by the text in the lines until
// end of page
};
-/*
- * The low bits of db_index hold the actual index. The topmost bit is
- * used for the global command to be able to mark a line.
- * This method is not clean, but otherwise there would be at least one extra
- * byte used for each line.
- * The mark has to be in this place to keep it with the correct line when other
- * lines are inserted or deleted.
- */
+// The low bits of db_index hold the actual index. The topmost bit is
+// used for the global command to be able to mark a line.
+// This method is not clean, but otherwise there would be at least one extra
+// byte used for each line.
+// The mark has to be in this place to keep it with the correct line when other
+// lines are inserted or deleted.
#define DB_MARKED ((unsigned)1 << ((sizeof(unsigned) * 8) - 1))
#define DB_INDEX_MASK (~DB_MARKED)
#define INDEX_SIZE (sizeof(unsigned)) // size of one db_index entry
#define HEADER_SIZE (sizeof(DATA_BL) - INDEX_SIZE) // size of data block header
-#define B0_FNAME_SIZE_ORG 900 // what it was in older versions
-#define B0_FNAME_SIZE_NOCRYPT 898 // 2 bytes used for other things
-#define B0_FNAME_SIZE_CRYPT 890 // 10 bytes used for other things
-#define B0_UNAME_SIZE 40
-#define B0_HNAME_SIZE 40
-/*
- * Restrict the numbers to 32 bits, otherwise most compilers will complain.
- * This won't detect a 64 bit machine that only swaps a byte in the top 32
- * bits, but that is crazy anyway.
- */
-#define B0_MAGIC_LONG 0x30313233L
-#define B0_MAGIC_INT 0x20212223L
-#define B0_MAGIC_SHORT 0x10111213L
-#define B0_MAGIC_CHAR 0x55
-
-/*
- * Block zero holds all info about the swap file.
- *
- * NOTE: DEFINITION OF BLOCK 0 SHOULD NOT CHANGE! It would make all existing
- * swap files unusable!
- *
- * If size of block0 changes anyway, adjust MIN_SWAP_PAGE_SIZE in vim.h!!
- *
- * This block is built up of single bytes, to make it portable across
- * different machines. b0_magic_* is used to check the byte order and size of
- * variables, because the rest of the swap file is not portable.
- */
+enum {
+ B0_FNAME_SIZE_ORG = 900, // what it was in older versions
+ B0_FNAME_SIZE_NOCRYPT = 898, // 2 bytes used for other things
+ B0_FNAME_SIZE_CRYPT = 890, // 10 bytes used for other things
+ B0_UNAME_SIZE = 40,
+ B0_HNAME_SIZE = 40,
+};
+// Restrict the numbers to 32 bits, otherwise most compilers will complain.
+// This won't detect a 64 bit machine that only swaps a byte in the top 32
+// bits, but that is crazy anyway.
+enum {
+ B0_MAGIC_LONG = 0x30313233L,
+ B0_MAGIC_INT = 0x20212223L,
+ B0_MAGIC_SHORT = 0x10111213L,
+ B0_MAGIC_CHAR = 0x55,
+};
+
+// Block zero holds all info about the swap file.
+//
+// NOTE: DEFINITION OF BLOCK 0 SHOULD NOT CHANGE! It would make all existing
+// swap files unusable!
+//
+// If size of block0 changes anyway, adjust MIN_SWAP_PAGE_SIZE in vim.h!!
+//
+// This block is built up of single bytes, to make it portable across
+// different machines. b0_magic_* is used to check the byte order and size of
+// variables, because the rest of the swap file is not portable.
struct block0 {
- char_u b0_id[2]; ///< ID for block 0: BLOCK0_ID0 and BLOCK0_ID1.
- char_u b0_version[10]; // Vim version string
- char_u b0_page_size[4]; // number of bytes per page
- char_u b0_mtime[4]; // last modification time of file
- char_u b0_ino[4]; // inode of b0_fname
- char_u b0_pid[4]; // process id of creator (or 0)
- char_u b0_uname[B0_UNAME_SIZE]; // name of user (uid if no name)
- char_u b0_hname[B0_HNAME_SIZE]; // host name (if it has a name)
- char_u b0_fname[B0_FNAME_SIZE_ORG]; // name of file being edited
- long b0_magic_long; // check for byte order of long
- int b0_magic_int; // check for byte order of int
- short b0_magic_short; // check for byte order of short
- char_u b0_magic_char; // check for last char
+ char_u b0_id[2]; ///< ID for block 0: BLOCK0_ID0 and BLOCK0_ID1.
+ char b0_version[10]; // Vim version string
+ char_u b0_page_size[4]; // number of bytes per page
+ char_u b0_mtime[4]; // last modification time of file
+ char_u b0_ino[4]; // inode of b0_fname
+ char_u b0_pid[4]; // process id of creator (or 0)
+ char_u b0_uname[B0_UNAME_SIZE]; // name of user (uid if no name)
+ char_u b0_hname[B0_HNAME_SIZE]; // host name (if it has a name)
+ char b0_fname[B0_FNAME_SIZE_ORG]; // name of file being edited
+ long b0_magic_long; // check for byte order of long
+ int b0_magic_int; // check for byte order of int
+ int16_t b0_magic_short; // check for byte order of short
+ char_u b0_magic_char; // check for last char
};
-/*
- * Note: b0_dirty and b0_flags are put at the end of the file name. For very
- * long file names in older versions of Vim they are invalid.
- * The 'fileencoding' comes before b0_flags, with a NUL in front. But only
- * when there is room, for very long file names it's omitted.
- */
+// Note: b0_dirty and b0_flags are put at the end of the file name. For very
+// long file names in older versions of Vim they are invalid.
+// The 'fileencoding' comes before b0_flags, with a NUL in front. But only
+// when there is room, for very long file names it's omitted.
#define B0_DIRTY 0x55
#define b0_dirty b0_fname[B0_FNAME_SIZE_ORG - 1]
-/*
- * The b0_flags field is new in Vim 7.0.
- */
+// The b0_flags field is new in Vim 7.0.
#define b0_flags b0_fname[B0_FNAME_SIZE_ORG - 2]
// The lowest two bits contain the fileformat. Zero means it's not set
@@ -216,21 +219,19 @@ struct block0 {
#define STACK_INCR 5 // nr of entries added to ml_stack at a time
-/*
- * The line number where the first mark may be is remembered.
- * If it is 0 there are no marks at all.
- * (always used for the current buffer only, no buffer change possible while
- * executing a global command).
- */
+// The line number where the first mark may be is remembered.
+// If it is 0 there are no marks at all.
+// (always used for the current buffer only, no buffer change possible while
+// executing a global command).
static linenr_T lowest_marked = 0;
-/*
- * arguments for ml_find_line()
- */
-#define ML_DELETE 0x11 // delete line
-#define ML_INSERT 0x12 // insert line
-#define ML_FIND 0x13 // just find the line
-#define ML_FLUSH 0x02 // flush locked block
+// arguments for ml_find_line()
+enum {
+ ML_DELETE = 0x11, // delete line
+ ML_INSERT = 0x12, // insert line
+ ML_FIND = 0x13, // just find the line
+ ML_FLUSH = 0x02, // flush locked block
+};
#define ML_SIMPLE(x) ((x) & 0x10) // DEL, INS or FIND
// argument for ml_upd_block0()
@@ -249,13 +250,8 @@ typedef enum {
int ml_open(buf_T *buf)
{
bhdr_T *hp = NULL;
- ZERO_BL *b0p;
- PTR_BL *pp;
- DATA_BL *dp;
- /*
- * init fields in memline struct
- */
+ // init fields in memline struct
buf->b_ml.ml_stack_size = 0; // no stack yet
buf->b_ml.ml_stack = NULL; // no stack yet
buf->b_ml.ml_stack_top = 0; // nothing in the stack
@@ -269,9 +265,7 @@ int ml_open(buf_T *buf)
buf->b_p_swf = false;
}
- /*
- * When 'updatecount' is non-zero swap file may be opened later.
- */
+ // When 'updatecount' is non-zero swap file may be opened later.
if (!buf->terminal && p_uc && buf->b_p_swf) {
buf->b_may_swap = true;
} else {
@@ -287,30 +281,27 @@ int ml_open(buf_T *buf)
buf->b_ml.ml_mfp = mfp;
buf->b_ml.ml_flags = ML_EMPTY;
buf->b_ml.ml_line_count = 1;
- curwin->w_nrwidth_line_count = 0;
- /*
- * fill block0 struct and write page 0
- */
+ // fill block0 struct and write page 0
hp = mf_new(mfp, false, 1);
if (hp->bh_bnum != 0) {
iemsg(_("E298: Didn't get block nr 0?"));
goto error;
}
- b0p = hp->bh_data;
+ ZERO_BL *b0p = hp->bh_data;
b0p->b0_id[0] = BLOCK0_ID0;
b0p->b0_id[1] = BLOCK0_ID1;
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_int = B0_MAGIC_INT;
+ b0p->b0_magic_short = (int16_t)B0_MAGIC_SHORT;
b0p->b0_magic_char = B0_MAGIC_CHAR;
- xstrlcpy(xstpcpy((char *)b0p->b0_version, "VIM "), Version, 6);
+ xstrlcpy(xstpcpy(b0p->b0_version, "VIM "), Version, 6);
long_to_char((long)mfp->mf_page_size, b0p->b0_page_size);
if (!buf->b_spell) {
b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0;
- b0p->b0_flags = (uint8_t)(get_fileformat(buf) + 1);
+ b0p->b0_flags = (char)(get_fileformat(buf) + 1);
set_b0_fname(b0p, buf);
(void)os_get_username((char *)b0p->b0_uname, B0_UNAME_SIZE);
b0p->b0_uname[B0_UNAME_SIZE - 1] = NUL;
@@ -319,21 +310,17 @@ int ml_open(buf_T *buf)
long_to_char(os_get_pid(), b0p->b0_pid);
}
- /*
- * Always sync block number 0 to disk, so we can check the file name in
- * the swap file in findswapname(). Don't do this for a help files or
- * a spell buffer though.
- * Only works when there's a swapfile, otherwise it's done when the file
- * is created.
- */
+ // Always sync block number 0 to disk, so we can check the file name in
+ // the swap file in findswapname(). Don't do this for a help files or
+ // a spell buffer though.
+ // Only works when there's a swapfile, otherwise it's done when the file
+ // is created.
mf_put(mfp, hp, true, false);
if (!buf->b_help && !B_SPELL(buf)) {
(void)mf_sync(mfp, 0);
}
- /*
- * Fill in root pointer block and write page 1.
- */
+ // Fill in root pointer block and write page 1.
if ((hp = ml_new_ptr(mfp)) == NULL) {
goto error;
}
@@ -341,7 +328,7 @@ int ml_open(buf_T *buf)
iemsg(_("E298: Didn't get block nr 1?"));
goto error;
}
- pp = hp->bh_data;
+ PTR_BL *pp = hp->bh_data;
pp->pb_count = 1;
pp->pb_pointer[0].pe_bnum = 2;
pp->pb_pointer[0].pe_page_count = 1;
@@ -349,16 +336,14 @@ int ml_open(buf_T *buf)
pp->pb_pointer[0].pe_line_count = 1; // line count after insertion
mf_put(mfp, hp, true, false);
- /*
- * Allocate first data block and create an empty line 1.
- */
+ // Allocate first data block and create an empty line 1.
hp = ml_new_data(mfp, false, 1);
if (hp->bh_bnum != 2) {
iemsg(_("E298: Didn't get block nr 2?"));
goto error;
}
- dp = hp->bh_data;
+ DATA_BL *dp = hp->bh_data;
dp->db_index[0] = --dp->db_txt_start; // at end of block
dp->db_free -= 1 + (unsigned)INDEX_SIZE;
dp->db_line_count = 1;
@@ -382,16 +367,11 @@ error:
void ml_setname(buf_T *buf)
{
bool success = false;
- memfile_T *mfp;
- char_u *fname;
- char *dirp;
- mfp = buf->b_ml.ml_mfp;
+ memfile_T *mfp = buf->b_ml.ml_mfp;
if (mfp->mf_fd < 0) { // there is no swap file yet
- /*
- * When 'updatecount' is 0 and 'noswapfile' there is no swap file.
- * For help files we will make a swap file now.
- */
+ // 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.cmod_flags & CMOD_NOSWAPFILE) == 0) {
ml_open_file(buf); // create a swap file
}
@@ -399,13 +379,13 @@ void ml_setname(buf_T *buf)
}
// Try all directories in the 'directory' option.
- dirp = (char *)p_dir;
+ char *dirp = p_dir;
bool found_existing_dir = false;
for (;;) {
if (*dirp == NUL) { // tried all directories, fail
break;
}
- fname = (char_u *)findswapname(buf, &dirp, (char *)mfp->mf_fname, &found_existing_dir);
+ char *fname = findswapname(buf, &dirp, mfp->mf_fname, &found_existing_dir);
// alloc's fname
if (dirp == NULL) { // out of memory
break;
@@ -415,7 +395,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 (path_fnamecmp(fname, mfp->mf_fname) == 0) {
xfree(fname);
success = true;
break;
@@ -438,7 +418,7 @@ void ml_setname(buf_T *buf)
}
if (mfp->mf_fd == -1) { // need to (re)open the swap file
- mfp->mf_fd = os_open((char *)mfp->mf_fname, O_RDWR, 0);
+ mfp->mf_fd = os_open(mfp->mf_fname, O_RDWR, 0);
if (mfp->mf_fd < 0) {
// could not (re)open the swap file, what can we do????
emsg(_("E301: Oops, lost the swap file!!!"));
@@ -468,11 +448,7 @@ void ml_open_files(void)
/// and the memfile will be in memory only (no recovery possible).
void ml_open_file(buf_T *buf)
{
- memfile_T *mfp;
- char_u *fname;
- char *dirp;
-
- mfp = buf->b_ml.ml_mfp;
+ memfile_T *mfp = buf->b_ml.ml_mfp;
if (mfp == NULL || mfp->mf_fd >= 0 || !buf->b_p_swf
|| (cmdmod.cmod_flags & CMOD_NOSWAPFILE)
|| buf->terminal) {
@@ -481,7 +457,7 @@ void ml_open_file(buf_T *buf)
// For a spell buffer use a temp file name.
if (buf->b_spell) {
- fname = vim_tempname();
+ char *fname = vim_tempname();
if (fname != NULL) {
(void)mf_open_file(mfp, fname); // consumes fname!
}
@@ -490,7 +466,7 @@ void ml_open_file(buf_T *buf)
}
// Try all directories in 'directory' option.
- dirp = (char *)p_dir;
+ char *dirp = p_dir;
bool found_existing_dir = false;
for (;;) {
if (*dirp == NUL) {
@@ -499,7 +475,7 @@ void ml_open_file(buf_T *buf)
// There is a small chance that between choosing the swap file name
// and creating it, another Vim creates the file. In that case the
// creation will fail and we will use another directory.
- fname = (char_u *)findswapname(buf, &dirp, NULL, &found_existing_dir);
+ char *fname = findswapname(buf, &dirp, NULL, &found_existing_dir);
if (dirp == NULL) {
break; // out of memory
}
@@ -523,7 +499,7 @@ void ml_open_file(buf_T *buf)
}
if (*p_dir != NUL && mfp->mf_fname == NULL) {
- need_wait_return = true; // call wait_return later
+ need_wait_return = true; // call wait_return() later
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);
@@ -552,7 +528,7 @@ void check_need_swap(bool newfile)
/// Close memline for buffer 'buf'.
///
-/// @param del_file if TRUE, delete the swap file
+/// @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
@@ -590,7 +566,7 @@ void ml_close_notmod(void)
{
FOR_ALL_BUFFERS(buf) {
if (!bufIsChanged(buf)) {
- ml_close(buf, TRUE); // close all not-modified buffers
+ ml_close(buf, true); // close all not-modified buffers
}
}
}
@@ -616,21 +592,19 @@ static bool ml_check_b0_strings(ZERO_BL *b0p)
return (memchr(b0p->b0_version, NUL, 10)
&& memchr(b0p->b0_uname, NUL, B0_UNAME_SIZE)
&& memchr(b0p->b0_hname, NUL, B0_HNAME_SIZE)
- && memchr(b0p->b0_fname, NUL, B0_FNAME_SIZE_CRYPT)); // -V512
+ && memchr(b0p->b0_fname, NUL, B0_FNAME_SIZE_CRYPT)); // -V1086
}
/// 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;
bhdr_T *hp;
- ZERO_BL *b0p;
- mfp = buf->b_ml.ml_mfp;
+ memfile_T *mfp = buf->b_ml.ml_mfp;
if (mfp == NULL || (hp = mf_get(mfp, 0, 1)) == NULL) {
return;
}
- b0p = hp->bh_data;
+ ZERO_BL *b0p = hp->bh_data;
if (ml_check_b0_id(b0p) == FAIL) {
iemsg(_("E304: ml_upd_block0(): Didn't get block 0??"));
} else {
@@ -653,22 +627,20 @@ static void set_b0_fname(ZERO_BL *b0p, buf_T *buf)
} else {
char uname[B0_UNAME_SIZE];
- /*
- * For a file under the home directory of the current user, we try to
- * replace the home directory path with "~user". This helps when
- * editing the same file on different machines over a network.
- * First replace home dir path with "~/" with home_replace().
- * Then insert the user name to get "~user/".
- */
+ // For a file under the home directory of the current user, we try to
+ // replace the home directory path with "~user". This helps when
+ // editing the same file on different machines over a network.
+ // First replace home dir path with "~/" with home_replace().
+ // Then insert the user name to get "~user/".
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_username(uname, B0_UNAME_SIZE);
- size_t ulen = STRLEN(uname);
- size_t flen = STRLEN(b0p->b0_fname);
+ size_t ulen = strlen(uname);
+ size_t flen = strlen(b0p->b0_fname);
if (retval == FAIL || ulen + flen > B0_FNAME_SIZE_CRYPT - 1) {
- STRLCPY(b0p->b0_fname, buf->b_ffname, B0_FNAME_SIZE_CRYPT);
+ xstrlcpy(b0p->b0_fname, buf->b_ffname, B0_FNAME_SIZE_CRYPT);
} else {
memmove(b0p->b0_fname + ulen + 1, b0p->b0_fname + 1, flen);
memmove(b0p->b0_fname + 1, uname, ulen);
@@ -703,30 +675,46 @@ static void set_b0_fname(ZERO_BL *b0p, buf_T *buf)
/// not set.
static void set_b0_dir_flag(ZERO_BL *b0p, buf_T *buf)
{
- if (same_directory(buf->b_ml.ml_mfp->mf_fname, (char_u *)buf->b_ffname)) {
+ if (same_directory(buf->b_ml.ml_mfp->mf_fname, buf->b_ffname)) {
b0p->b0_flags |= B0_SAME_DIR;
} else {
- b0p->b0_flags &= (uint8_t) ~B0_SAME_DIR;
+ b0p->b0_flags = (char)(b0p->b0_flags & ~B0_SAME_DIR);
}
}
/// When there is room, add the 'fileencoding' to block zero.
static void add_b0_fenc(ZERO_BL *b0p, buf_T *buf)
{
- int n;
- int size = B0_FNAME_SIZE_NOCRYPT;
+ const int size = B0_FNAME_SIZE_NOCRYPT;
- n = (int)STRLEN(buf->b_p_fenc);
- if ((int)STRLEN(b0p->b0_fname) + n + 1 > size) {
- b0p->b0_flags &= (uint8_t) ~B0_HAS_FENC;
+ int n = (int)strlen(buf->b_p_fenc);
+ if ((int)strlen(b0p->b0_fname) + n + 1 > size) {
+ b0p->b0_flags = (char)(b0p->b0_flags & ~B0_HAS_FENC);
} else {
memmove((char *)b0p->b0_fname + size - n,
- (char *)buf->b_p_fenc, (size_t)n);
+ buf->b_p_fenc, (size_t)n);
*(b0p->b0_fname + size - n - 1) = NUL;
b0p->b0_flags |= B0_HAS_FENC;
}
}
+/// Return true if the process with number "b0p->b0_pid" is still running.
+/// "swap_fname" is the name of the swap file, if it's from before a reboot then
+/// the result is false;
+static bool swapfile_process_running(const ZERO_BL *b0p, const char *swap_fname)
+{
+ FileInfo st;
+ double uptime;
+ // If the system rebooted after when the swap file was written then the
+ // process can't be running now.
+ if (os_fileinfo(swap_fname, &st)
+ && uv_uptime(&uptime) == 0
+ && (Timestamp)st.stat.st_mtim.tv_sec < os_time() - (Timestamp)uptime) {
+ return false;
+ }
+ return os_proc_running((int)char_to_long(b0p->b0_pid));
+}
+
/// Try to recover curbuf from the .swp file.
///
/// @param checkext if true, check the extension and detect whether it is a
@@ -735,75 +723,60 @@ void ml_recover(bool checkext)
{
buf_T *buf = NULL;
memfile_T *mfp = NULL;
- char_u *fname;
- char_u *fname_used = NULL;
+ char *fname_used = NULL;
bhdr_T *hp = NULL;
ZERO_BL *b0p;
int b0_ff;
- char_u *b0_fenc = NULL;
+ char *b0_fenc = NULL;
PTR_BL *pp;
DATA_BL *dp;
infoptr_T *ip;
- blocknr_T bnum;
- int len;
bool directly;
- linenr_T lnum;
- char_u *p;
- int i;
- long error;
- bool cannot_open;
- linenr_T line_count;
- bool has_error;
- int idx;
- int top;
- int txt_start;
- off_T size;
- int called_from_main;
+ char *p;
bool serious_error = true;
- long mtime;
- int attr;
int orig_file_status = NOTDONE;
- recoverymode = TRUE;
- called_from_main = (curbuf->b_ml.ml_mfp == NULL);
- attr = HL_ATTR(HLF_E);
+ recoverymode = true;
+ int called_from_main = (curbuf->b_ml.ml_mfp == NULL);
+ int attr = HL_ATTR(HLF_E);
// 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 = (char_u *)curbuf->b_fname;
+ char *fname = curbuf->b_fname;
if (fname == NULL) { // When there is no file name
- fname = (char_u *)"";
+ fname = "";
}
- len = (int)STRLEN(fname);
+ int len = (int)strlen(fname);
if (checkext && len >= 4
&& STRNICMP(fname + len - 4, ".s", 2) == 0
- && vim_strchr("abcdefghijklmnopqrstuvw", TOLOWER_ASC(fname[len - 2])) != NULL
+ && vim_strchr("abcdefghijklmnopqrstuvw", TOLOWER_ASC((uint8_t)fname[len - 2])) != NULL
&& ASCII_ISALPHA(fname[len - 1])) {
directly = true;
- fname_used = vim_strsave(fname); // make a copy for mf_open()
+ fname_used = xstrdup(fname); // make a copy for mf_open()
} else {
directly = false;
// count the number of matching swap files
- len = recover_names(fname, FALSE, 0, NULL);
+ len = recover_names(fname, false, 0, NULL);
if (len == 0) { // no swap files found
semsg(_("E305: No swap file found for %s"), fname);
goto theend;
}
+ int i;
if (len == 1) { // one swap file found, use it
i = 1;
} else { // several swap files found, choose
// list the names of the swap files
- (void)recover_names(fname, TRUE, 0, NULL);
+ (void)recover_names(fname, true, 0, NULL);
msg_putchar('\n');
msg_puts(_("Enter number of swap file to use (0 to quit): "));
- i = get_number(FALSE, NULL);
+ i = get_number(false, NULL);
if (i < 1 || i > len) {
goto theend;
}
}
// get the swap file name that will be used
- (void)recover_names(fname, FALSE, i, &fname_used);
+ (void)recover_names(fname, false, i, &fname_used);
}
if (fname_used == NULL) {
goto theend; // user chose invalid number.
@@ -813,15 +786,11 @@ void ml_recover(bool checkext)
getout(1);
}
- /*
- * Allocate a buffer structure for the swap file that is used for recovery.
- * Only the memline in it is really used.
- */
+ // Allocate a buffer structure for the swap file that is used for recovery.
+ // Only the memline in it is really used.
buf = xmalloc(sizeof(buf_T));
- /*
- * init fields in memline struct
- */
+ // init fields in memline struct
buf->b_ml.ml_stack_size = 0; // no stack yet
buf->b_ml.ml_stack = NULL; // no stack yet
buf->b_ml.ml_stack_top = 0; // nothing in the stack
@@ -830,11 +799,9 @@ void ml_recover(bool checkext)
buf->b_ml.ml_locked = NULL; // no locked block
buf->b_ml.ml_flags = 0;
- /*
- * open the memfile from the old swap file
- */
- p = vim_strsave(fname_used); // save "fname_used" for the message:
- // mf_open() will consume "fname_used"!
+ // open the memfile from the old swap file
+ p = xstrdup(fname_used); // save "fname_used" for the message:
+ // mf_open() will consume "fname_used"!
mfp = mf_open(fname_used, O_RDONLY);
fname_used = p;
if (mfp == NULL || mfp->mf_fd < 0) {
@@ -843,17 +810,13 @@ void ml_recover(bool checkext)
}
buf->b_ml.ml_mfp = mfp;
- /*
- * The page size set in mf_open() might be different from the page size
- * used in the swap file, we must get it from block 0. But to read block
- * 0 we need a page size. Use the minimal size for block 0 here, it will
- * be set to the real value below.
- */
+ // The page size set in mf_open() might be different from the page size
+ // used in the swap file, we must get it from block 0. But to read block
+ // 0 we need a page size. Use the minimal size for block 0 here, it will
+ // be set to the real value below.
mfp->mf_page_size = MIN_SWAP_PAGE_SIZE;
- /*
- * try to read block 0
- */
+ // try to read block 0
if ((hp = mf_get(mfp, 0, 1)) == NULL) {
msg_start();
msg_puts_attr(_("Unable to read block 0 from "), attr | MSG_HIST);
@@ -864,7 +827,7 @@ void ml_recover(bool checkext)
goto theend;
}
b0p = hp->bh_data;
- if (STRNCMP(b0p->b0_version, "VIM 3.0", 7) == 0) {
+ if (strncmp(b0p->b0_version, "VIM 3.0", 7) == 0) {
msg_start();
msg_outtrans_attr(mfp->mf_fname, MSG_HIST);
msg_puts_attr(_(" cannot be used with this version of Vim.\n"),
@@ -891,10 +854,8 @@ void ml_recover(bool checkext)
goto theend;
}
- /*
- * If we guessed the wrong page size, we have to recalculate the
- * highest block number in the file.
- */
+ // If we guessed the wrong page size, we have to recalculate the
+ // highest block number in the file.
if (mfp->mf_page_size != (unsigned)char_to_long(b0p->b0_page_size)) {
unsigned previous_page_size = mfp->mf_page_size;
@@ -907,6 +868,7 @@ void ml_recover(bool checkext)
msg_end();
goto theend;
}
+ off_T size;
if ((size = vim_lseek(mfp->mf_fd, (off_T)0L, SEEK_END)) <= 0) {
mfp->mf_blocknr_max = 0; // no file or empty file
} else {
@@ -922,36 +884,32 @@ void ml_recover(bool checkext)
b0p = hp->bh_data;
}
- /*
- * If .swp file name given directly, use name from swap file for buffer.
- */
+ // If .swp file name given directly, use name from swap file for buffer.
if (directly) {
- expand_env(b0p->b0_fname, NameBuff, MAXPATHL);
- if (setfname(curbuf, (char *)NameBuff, NULL, true) == FAIL) {
+ expand_env((char *)b0p->b0_fname, NameBuff, MAXPATHL);
+ if (setfname(curbuf, NameBuff, NULL, true) == FAIL) {
goto theend;
}
}
- home_replace(NULL, (char *)mfp->mf_fname, (char *)NameBuff, MAXPATHL, true);
+ home_replace(NULL, mfp->mf_fname, NameBuff, MAXPATHL, true);
smsg(_("Using swap file \"%s\""), NameBuff);
if (buf_spname(curbuf) != NULL) {
- STRLCPY(NameBuff, buf_spname(curbuf), MAXPATHL);
+ xstrlcpy(NameBuff, buf_spname(curbuf), MAXPATHL);
} else {
- home_replace(NULL, curbuf->b_ffname, (char *)NameBuff, MAXPATHL, true);
+ home_replace(NULL, curbuf->b_ffname, NameBuff, MAXPATHL, true);
}
smsg(_("Original file \"%s\""), NameBuff);
msg_putchar('\n');
- /*
- * check date of swap file and original file
- */
+ // check date of swap file and original file
FileInfo org_file_info;
FileInfo swp_file_info;
- mtime = char_to_long(b0p->b0_mtime);
+ long mtime = char_to_long(b0p->b0_mtime);
if (curbuf->b_ffname != NULL
&& os_fileinfo(curbuf->b_ffname, &org_file_info)
- && ((os_fileinfo((char *)mfp->mf_fname, &swp_file_info)
+ && ((os_fileinfo(mfp->mf_fname, &swp_file_info)
&& org_file_info.stat.st_mtim.tv_sec
> swp_file_info.stat.st_mtim.tv_sec)
|| org_file_info.stat.st_mtim.tv_sec != mtime)) {
@@ -964,25 +922,21 @@ 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--) {}
- b0_fenc = vim_strnsave(p, (size_t)(b0p->b0_fname + fnsize - p));
+ for (p = (char *)b0p->b0_fname + fnsize; p > b0p->b0_fname && p[-1] != NUL; p--) {}
+ b0_fenc = xstrnsave(p, (size_t)(b0p->b0_fname + fnsize - p));
}
mf_put(mfp, hp, false, false); // release block 0
hp = NULL;
- /*
- * Now that we are sure that the file is going to be recovered, clear the
- * contents of the current buffer.
- */
+ // Now that we are sure that the file is going to be recovered, clear the
+ // contents of the current buffer.
while (!(curbuf->b_ml.ml_flags & ML_EMPTY)) {
ml_delete((linenr_T)1, false);
}
- /*
- * Try reading the original file to obtain the values of 'fileformat',
- * 'fileencoding', etc. Ignore errors. The text itself is not used.
- */
+ // Try reading the original file to obtain the values of 'fileformat',
+ // 'fileencoding', etc. Ignore errors. The text itself is not used.
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, false);
@@ -993,35 +947,29 @@ void ml_recover(bool checkext)
set_fileformat(b0_ff - 1, OPT_LOCAL);
}
if (b0_fenc != NULL) {
- set_option_value("fenc", 0L, (char *)b0_fenc, OPT_LOCAL);
+ set_option_value_give_err("fenc", 0L, b0_fenc, OPT_LOCAL);
xfree(b0_fenc);
}
unchanged(curbuf, true, true);
- bnum = 1; // start with block 1
+ blocknr_T bnum = 1; // start with block 1
unsigned page_count = 1; // which is 1 page
- lnum = 0; // append after line 0 in curbuf
- line_count = 0;
- idx = 0; // start with first index in block 1
- error = 0;
+ linenr_T lnum = 0; // append after line 0 in curbuf
+ linenr_T line_count = 0;
+ int idx = 0; // start with first index in block 1
+ long error = 0;
buf->b_ml.ml_stack_top = 0; // -V1048
buf->b_ml.ml_stack = NULL;
buf->b_ml.ml_stack_size = 0; // -V1048
- if (curbuf->b_ffname == NULL) {
- cannot_open = true;
- } else {
- cannot_open = false;
- }
+ bool cannot_open = (curbuf->b_ffname == NULL);
serious_error = false;
for (; !got_int; line_breakcheck()) {
if (hp != NULL) {
mf_put(mfp, hp, false, false); // release previous block
}
- /*
- * get block
- */
+ // get block
if ((hp = mf_get(mfp, bnum, page_count)) == NULL) {
if (bnum == 1) {
semsg(_("E309: Unable to read block 1 from %s"), mfp->mf_fname);
@@ -1035,7 +983,7 @@ void ml_recover(bool checkext)
if (pp->pb_id == PTR_ID) { // it is a pointer block
// check line count when using pointer block first time
if (idx == 0 && line_count != 0) {
- for (i = 0; i < (int)pp->pb_count; ++i) {
+ for (int i = 0; i < (int)pp->pb_count; i++) {
line_count -= pp->pb_pointer[i].pe_line_count;
}
if (line_count != 0) {
@@ -1051,11 +999,9 @@ void ml_recover(bool checkext)
error++;
} else if (idx < (int)pp->pb_count) { // go a block deeper
if (pp->pb_pointer[idx].pe_bnum < 0) {
- /*
- * Data block with negative block number.
- * Try to read lines from the original file.
- * This is slow, but it works.
- */
+ // Data block with negative block number.
+ // Try to read lines from the original file.
+ // This is slow, but it works.
if (!cannot_open) {
line_count = pp->pb_pointer[idx].pe_line_count;
if (readfile(curbuf->b_ffname, NULL, lnum,
@@ -1071,14 +1017,12 @@ void ml_recover(bool checkext)
ml_append(lnum++, _("???LINES MISSING"),
(colnr_T)0, true);
}
- ++idx; // get same block again for next index
+ idx++; // get same block again for next index
continue;
}
- /*
- * going one block deeper in the tree
- */
- top = ml_add_stack(buf); // new entry in stack
+ // going one block deeper in the tree
+ int top = ml_add_stack(buf); // new entry in stack
ip = &(buf->b_ml.ml_stack[top]);
ip->ip_bnum = bnum;
ip->ip_index = idx;
@@ -1103,7 +1047,7 @@ void ml_recover(bool checkext)
} else {
// it is a data block
// Append all the lines in this block
- has_error = false;
+ bool has_error = false;
// check length of block
// if wrong, use length in pointer block
if (page_count * mfp->mf_page_size != dp->db_txt_end) {
@@ -1118,10 +1062,8 @@ void ml_recover(bool checkext)
// make sure there is a NUL at the end of the block
*((char_u *)dp + dp->db_txt_end - 1) = NUL;
- /*
- * check number of lines in block
- * if wrong, use count in data block
- */
+ // check number of lines in block
+ // if wrong, use count in data block
if (line_count != dp->db_line_count) {
ml_append(lnum++,
_("??? from here until ???END lines"
@@ -1131,16 +1073,16 @@ void ml_recover(bool checkext)
has_error = true;
}
- for (i = 0; i < dp->db_line_count; ++i) {
- txt_start = (dp->db_index[i] & DB_INDEX_MASK);
+ for (int i = 0; i < dp->db_line_count; i++) {
+ int txt_start = (dp->db_index[i] & DB_INDEX_MASK);
if (txt_start <= (int)HEADER_SIZE
|| txt_start >= (int)dp->db_txt_end) {
- p = (char_u *)"???";
+ p = "???";
error++;
} else {
- p = (char_u *)dp + txt_start;
+ p = (char *)dp + txt_start;
}
- ml_append(lnum++, (char *)p, (colnr_T)0, true);
+ ml_append(lnum++, p, (colnr_T)0, true);
}
if (has_error) {
ml_append(lnum++, _("???END"), (colnr_T)0, true);
@@ -1153,22 +1095,18 @@ void ml_recover(bool checkext)
break;
}
- /*
- * go one block up in the tree
- */
+ // go one block up in the tree
ip = &(buf->b_ml.ml_stack[--(buf->b_ml.ml_stack_top)]);
bnum = ip->ip_bnum;
idx = ip->ip_index + 1; // go to next index
page_count = 1;
}
- /*
- * Compare the buffer contents with the original file. When they differ
- * set the 'modified' flag.
- * Lines 1 - lnum are the new contents.
- * Lines lnum + 1 to ml_line_count are the original contents.
- * Line ml_line_count + 1 in the dummy empty line.
- */
+ // Compare the buffer contents with the original file. When they differ
+ // set the 'modified' flag.
+ // Lines 1 - lnum are the new contents.
+ // Lines lnum + 1 to ml_line_count are the original contents.
+ // Line ml_line_count + 1 in the dummy empty line.
if (orig_file_status != OK || curbuf->b_ml.ml_line_count != lnum * 2 + 1) {
// Recovering an empty file results in two lines and the first line is
// empty. Don't set the modified flag then.
@@ -1177,10 +1115,10 @@ void ml_recover(bool checkext)
buf_inc_changedtick(curbuf);
}
} else {
- for (idx = 1; idx <= lnum; ++idx) {
+ for (idx = 1; idx <= lnum; idx++) {
// Need to copy one line, fetching the other one may flush it.
- p = vim_strsave(ml_get(idx));
- i = STRCMP(p, ml_get(idx + lnum));
+ p = xstrdup(ml_get(idx));
+ int i = strcmp(p, ml_get(idx + lnum));
xfree(p);
if (i != 0) {
changed_internal();
@@ -1190,10 +1128,8 @@ void ml_recover(bool checkext)
}
}
- /*
- * Delete the lines from the original file and the dummy line from the
- * empty buffer. These will now be after the last line in the buffer.
- */
+ // Delete the lines from the original file and the dummy line from the
+ // empty buffer. These will now be after the last line in the buffer.
while (curbuf->b_ml.ml_line_count > lnum
&& !(curbuf->b_ml.ml_flags & ML_EMPTY)) {
ml_delete(curbuf->b_ml.ml_line_count, false);
@@ -1201,7 +1137,7 @@ void ml_recover(bool checkext)
curbuf->b_flags |= BF_RECOVERED;
check_cursor();
- recoverymode = FALSE;
+ recoverymode = false;
if (got_int) {
emsg(_("E311: Recovery Interrupted"));
} else if (error) {
@@ -1219,26 +1155,33 @@ void ml_recover(bool checkext)
} else {
msg(_("Recovery completed. Buffer contents equals file contents."));
}
- msg_puts(_("\nYou may want to delete the .swp file now.\n\n"));
+ msg_puts(_("\nYou may want to delete the .swp file now."));
+ if (swapfile_process_running(b0p, fname_used)) {
+ // Warn there could be an active Vim on the same file, the user may
+ // want to kill it.
+ msg_puts(_("\nNote: process STILL RUNNING: "));
+ msg_outnum(char_to_long(b0p->b0_pid));
+ }
+ msg_puts("\n\n");
cmdline_row = msg_row;
}
- redraw_curbuf_later(NOT_VALID);
+ redraw_curbuf_later(UPD_NOT_VALID);
theend:
xfree(fname_used);
- recoverymode = FALSE;
+ recoverymode = false;
if (mfp != NULL) {
if (hp != NULL) {
mf_put(mfp, hp, false, false);
}
mf_close(mfp, false); // will also xfree(mfp->mf_fname)
}
- if (buf != NULL) { //may be NULL if swap file not found.
+ if (buf != NULL) { // may be NULL if swap file not found.
xfree(buf->b_ml.ml_stack);
xfree(buf);
}
if (serious_error && called_from_main) {
- ml_close(curbuf, TRUE);
+ 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);
@@ -1255,23 +1198,20 @@ theend:
/// - find the name of the n'th swap file when recovering
///
/// @param fname base for swap file name
-/// @param list when TRUE, list the swap file names
+/// @param list when true, list the swap file names
/// @param nr when non-zero, return nr'th swap file name
/// @param fname_out result when "nr" > 0
-int recover_names(char_u *fname, int list, int nr, char_u **fname_out)
+int recover_names(char *fname, int list, int nr, char **fname_out)
{
int num_names;
char *(names[6]);
- char_u *tail;
- char_u *p;
- int num_files;
+ char *tail;
+ char *p;
int file_count = 0;
char **files;
- char *dirp;
- char_u *dir_name;
- char_u *fname_res = NULL;
+ char *fname_res = NULL;
#ifdef HAVE_READLINK
- char_u fname_buf[MAXPATHL];
+ char fname_buf[MAXPATHL];
#endif
if (fname != NULL) {
@@ -1293,13 +1233,13 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out)
// Do the loop for every directory in 'directory'.
// First allocate some memory to put the directory name in.
- dir_name = xmalloc(STRLEN(p_dir) + 1);
- dirp = (char *)p_dir;
+ char *dir_name = xmalloc(strlen(p_dir) + 1);
+ char *dirp = p_dir;
while (*dirp) {
// 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, (char *)dir_name, 31000, ",");
+ (void)copy_option_part(&dirp, dir_name, 31000, ",");
if (dir_name[0] == '.' && dir_name[1] == NUL) { // check current dir
if (fname == NULL) {
@@ -1310,34 +1250,34 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out)
names[2] = xstrdup(".sw?");
num_names = 3;
} else {
- num_names = recov_file_names(names, fname_res, TRUE);
+ num_names = recov_file_names(names, fname_res, true);
}
} else { // check directory dir_name
if (fname == NULL) {
- names[0] = concat_fnames((char *)dir_name, "*.sw?", true);
+ names[0] = concat_fnames(dir_name, "*.sw?", true);
// For Unix names starting with a dot are special. MS-Windows
// supports this too, on some file systems.
- names[1] = concat_fnames((char *)dir_name, ".*.sw?", true);
- names[2] = concat_fnames((char *)dir_name, ".sw?", true);
+ names[1] = concat_fnames(dir_name, ".*.sw?", true);
+ names[2] = concat_fnames(dir_name, ".sw?", true);
num_names = 3;
} else {
- int len = (int)STRLEN(dir_name);
+ int len = (int)strlen(dir_name);
p = dir_name + len;
- if (after_pathsep((char *)dir_name, (char *)p)
+ if (after_pathsep(dir_name, p)
&& len > 1
&& p[-1] == p[-2]) {
// Ends with '//', Use Full path for swap name
- tail = (char_u *)make_percent_swname((char *)dir_name,
- (char *)fname_res);
+ tail = make_percent_swname(dir_name, fname_res);
} else {
- tail = (char_u *)path_tail((char *)fname_res);
- tail = (char_u *)concat_fnames((char *)dir_name, (char *)tail, true);
+ tail = path_tail(fname_res);
+ tail = concat_fnames(dir_name, tail, true);
}
- num_names = recov_file_names(names, tail, FALSE);
+ num_names = recov_file_names(names, tail, false);
xfree(tail);
}
}
+ int num_files;
if (num_names == 0) {
num_files = 0;
} else if (expand_wildcards(num_names, names, &num_files, &files,
@@ -1345,17 +1285,15 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out)
num_files = 0;
}
- /*
- * When no swap file found, wildcard expansion might have failed (e.g.
- * not able to execute the shell).
- * Try finding a swap file by simply adding ".swp" to the file name.
- */
+ // When no swap file found, wildcard expansion might have failed (e.g.
+ // not able to execute the shell).
+ // Try finding a swap file by simply adding ".swp" to the file name.
if (*dirp == NUL && file_count + num_files == 0 && fname != NULL) {
- char_u *swapname = (char_u *)modname((char *)fname_res, ".swp", true);
+ char *swapname = modname(fname_res, ".swp", true);
if (swapname != NULL) {
if (os_path_exists(swapname)) {
- files = xmalloc(sizeof(char_u *));
- files[0] = (char *)swapname;
+ files = xmalloc(sizeof(char *));
+ files[0] = swapname;
swapname = NULL;
num_files = 1;
}
@@ -1363,15 +1301,13 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out)
}
}
- /*
- * remove swapfile name of the current buffer, it must be ignored
- */
+ // remove swapfile name of the current buffer, it must be ignored
if (curbuf->b_ml.ml_mfp != NULL
&& (p = curbuf->b_ml.ml_mfp->mf_fname) != NULL) {
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((char *)p, files[i], true, false) & kEqualFiles) {
+ if (path_full_compare(p, 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.
@@ -1379,7 +1315,7 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out)
if (--num_files == 0) {
xfree(files);
} else {
- for (; i < num_files; ++i) {
+ for (; i < num_files; i++) {
files[i] = files[i + 1];
}
}
@@ -1389,7 +1325,7 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out)
if (nr > 0) {
file_count += num_files;
if (nr <= file_count) {
- *fname_out = vim_strsave((char_u *)files[nr - 1 + num_files - file_count]);
+ *fname_out = xstrdup(files[nr - 1 + num_files - file_count]);
dirp = ""; // stop searching
}
} else if (list) {
@@ -1406,13 +1342,13 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out)
}
if (num_files) {
- for (int i = 0; i < num_files; ++i) {
+ for (int i = 0; i < num_files; i++) {
// print the swap file name
msg_outnum((long)++file_count);
msg_puts(". ");
msg_puts((const char *)path_tail(files[i]));
msg_putchar('\n');
- (void)swapfile_info((char_u *)files[i]);
+ (void)swapfile_info(files[i]);
}
} else {
msg_puts(_(" -- none --\n"));
@@ -1422,7 +1358,7 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out)
file_count += num_files;
}
- for (int i = 0; i < num_names; ++i) {
+ for (int i = 0; i < num_names; i++) {
xfree(names[i]);
}
if (num_files > 0) {
@@ -1440,17 +1376,19 @@ char *make_percent_swname(const char *dir, const char *name)
{
char *d = NULL;
char *f = fix_fname(name != NULL ? name : "");
- if (f != NULL) {
- char *s = xstrdup(f);
- for (d = s; *d != NUL; MB_PTR_ADV(d)) {
- if (vim_ispathsep(*d)) {
- *d = '%';
- }
+ if (f == NULL) {
+ return NULL;
+ }
+
+ char *s = xstrdup(f);
+ for (d = s; *d != NUL; MB_PTR_ADV(d)) {
+ if (vim_ispathsep(*d)) {
+ *d = '%';
}
- d = concat_fnames(dir, s, TRUE);
- xfree(s);
- xfree(f);
}
+ d = concat_fnames(dir, s, true);
+ xfree(s);
+ xfree(f);
return d;
}
@@ -1472,7 +1410,7 @@ void get_b0_dict(const char *fname, dict_T *d)
tv_dict_add_str(d, S_LEN("error"), "Magic number mismatch");
} else {
// We have swap information.
- tv_dict_add_str_len(d, S_LEN("version"), (char *)b0.b0_version, 10);
+ tv_dict_add_str_len(d, S_LEN("version"), b0.b0_version, 10);
tv_dict_add_str_len(d, S_LEN("user"), (char *)b0.b0_uname,
B0_UNAME_SIZE);
tv_dict_add_str_len(d, S_LEN("host"), (char *)b0.b0_hname,
@@ -1497,7 +1435,7 @@ void get_b0_dict(const char *fname, dict_T *d)
/// Give information about an existing swap file.
///
/// @return timestamp (0 when unknown).
-static time_t swapfile_info(char_u *fname)
+static time_t swapfile_info(char *fname)
{
assert(fname != NULL);
int fd;
@@ -1509,28 +1447,29 @@ static time_t swapfile_info(char_u *fname)
// print the swap file date
FileInfo file_info;
- if (os_fileinfo((char *)fname, &file_info)) {
+ if (os_fileinfo(fname, &file_info)) {
#ifdef UNIX
// print name of owner of the file
if (os_get_uname((uv_uid_t)file_info.stat.st_uid, uname, B0_UNAME_SIZE) == OK) {
msg_puts(_(" owned by: "));
msg_outtrans(uname);
msg_puts(_(" dated: "));
- } else
-#endif
+ } else {
+ msg_puts(_(" dated: "));
+ }
+#else
msg_puts(_(" dated: "));
+#endif
x = file_info.stat.st_mtim.tv_sec;
- char ctime_buf[50];
- msg_puts(os_ctime_r(&x, ctime_buf, sizeof(ctime_buf)));
+ char ctime_buf[100]; // hopefully enough for every language
+ msg_puts(os_ctime_r(&x, ctime_buf, sizeof(ctime_buf), true));
}
- /*
- * print the original file name
- */
- fd = os_open((char *)fname, O_RDONLY, 0);
+ // print the original file name
+ fd = os_open(fname, O_RDONLY, 0);
if (fd >= 0) {
if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0)) {
- if (STRNCMP(b0.b0_version, "VIM 3.0", 7) == 0) {
+ if (strncmp(b0.b0_version, "VIM 3.0", 7) == 0) {
msg_puts(_(" [from Vim version 3.0]"));
} else if (ml_check_b0_id(&b0) == FAIL) {
msg_puts(_(" [does not look like a Vim swap file]"));
@@ -1564,7 +1503,7 @@ static time_t swapfile_info(char_u *fname)
if (char_to_long(b0.b0_pid) != 0L) {
msg_puts(_("\n process ID: "));
msg_outnum(char_to_long(b0.b0_pid));
- if (os_proc_running((int)char_to_long(b0.b0_pid))) {
+ if (swapfile_process_running(&b0, (const char *)fname)) {
msg_puts(_(" (STILL RUNNING)"));
process_still_running = true;
}
@@ -1591,10 +1530,9 @@ static time_t swapfile_info(char_u *fname)
static bool swapfile_unchanged(char *fname)
{
struct block0 b0;
- int ret = true;
// Swap file must exist.
- if (!os_path_exists((char_u *)fname)) {
+ if (!os_path_exists(fname)) {
return false;
}
@@ -1608,6 +1546,8 @@ static bool swapfile_unchanged(char *fname)
return false;
}
+ bool ret = true;
+
// the ID and magic number must be correct
if (ml_check_b0_id(&b0) == FAIL || b0_magic_wrong(&b0)) {
ret = false;
@@ -1618,20 +1558,33 @@ static bool swapfile_unchanged(char *fname)
ret = false;
}
+ // Host name must be known and must equal the current host name, otherwise
+ // comparing pid is meaningless.
+ if (*(b0.b0_hname) == NUL) {
+ ret = false;
+ } else {
+ char hostname[B0_HNAME_SIZE];
+ os_get_hostname(hostname, B0_HNAME_SIZE);
+ hostname[B0_HNAME_SIZE - 1] = NUL;
+ b0.b0_hname[B0_HNAME_SIZE - 1] = NUL; // in case of corruption
+ if (STRICMP(b0.b0_hname, hostname) != 0) {
+ ret = false;
+ }
+ }
+
// process must be known and not running.
- long pid = char_to_long(b0.b0_pid);
- if (pid == 0L || os_proc_running((int)pid)) {
+ if (char_to_long(b0.b0_pid) == 0L || swapfile_process_running(&b0, fname)) {
ret = false;
}
- // TODO(bram): Should we check if the swap file was created on the current
- // system? And the current user?
+ // We do not check the user, it should be irrelevant for whether the swap
+ // file is still useful.
close(fd);
return ret;
}
-static int recov_file_names(char **names, char_u *path, int prepend_dot)
+static int recov_file_names(char **names, char *path, int prepend_dot)
FUNC_ATTR_NONNULL_ALL
{
int num_names = 0;
@@ -1639,7 +1592,7 @@ static int recov_file_names(char **names, char_u *path, int prepend_dot)
// May also add the file name with a dot prepended, for swap file in same
// dir as original file.
if (prepend_dot) {
- names[num_names] = modname((char *)path, ".sw?", true);
+ names[num_names] = modname(path, ".sw?", true);
if (names[num_names] == NULL) {
return num_names;
}
@@ -1647,14 +1600,14 @@ static int recov_file_names(char **names, char_u *path, int prepend_dot)
}
// Form the normal swap file name pattern by appending ".sw?".
- names[num_names] = concat_fnames((char *)path, ".sw?", false);
+ names[num_names] = concat_fnames(path, ".sw?", false);
if (num_names >= 1) { // check if we have the same name twice
- char_u *p = (char_u *)names[num_names - 1];
- int i = (int)STRLEN(names[num_names - 1]) - (int)STRLEN(names[num_names]);
+ char *p = names[num_names - 1];
+ int i = (int)strlen(names[num_names - 1]) - (int)strlen(names[num_names]);
if (i > 0) {
p += i; // file name has been expanded to full path
}
- if (STRCMP(p, names[num_names]) != 0) {
+ if (strcmp(p, names[num_names]) != 0) {
num_names++;
} else {
xfree(names[num_names]);
@@ -1668,8 +1621,8 @@ static int recov_file_names(char **names, char_u *path, int prepend_dot)
/// 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
+/// @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)
@@ -1683,10 +1636,8 @@ void ml_sync_all(int check_file, int check_char, bool do_fsync)
(void)ml_find_line(buf, (linenr_T)0, ML_FLUSH);
if (bufIsChanged(buf) && check_file && mf_need_trans(buf->b_ml.ml_mfp)
&& buf->b_ffname != NULL) {
- /*
- * If the original file does not exist anymore or has been changed
- * call ml_preserve() to get rid of all negative numbered blocks.
- */
+ // If the original file does not exist anymore or has been changed
+ // call ml_preserve() to get rid of all negative numbered blocks.
FileInfo file_info;
if (!os_fileinfo(buf->b_ffname, &file_info)
|| file_info.stat.st_mtim.tv_sec != buf->b_mtime_read
@@ -1714,13 +1665,10 @@ void ml_sync_all(int check_file, int check_char, bool do_fsync)
/// 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.
+/// @param message if true, the success of preserving is reported.
void ml_preserve(buf_T *buf, int message, bool do_fsync)
{
- bhdr_T *hp;
- linenr_T lnum;
memfile_T *mfp = buf->b_ml.ml_mfp;
- int status;
int got_int_save = got_int;
if (mfp == NULL || mfp->mf_fname == NULL) {
@@ -1736,27 +1684,25 @@ void ml_preserve(buf_T *buf, int message, bool do_fsync)
ml_flush_line(buf); // flush buffered line
(void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush locked block
- status = mf_sync(mfp, MFS_ALL | (do_fsync ? MFS_FLUSH : 0));
+ int status = mf_sync(mfp, MFS_ALL | (do_fsync ? MFS_FLUSH : 0));
// stack is invalid after mf_sync(.., MFS_ALL)
buf->b_ml.ml_stack_top = 0;
- /*
- * Some of the data blocks may have been changed from negative to
- * positive block number. In that case the pointer blocks need to be
- * updated.
- *
- * We don't know in which pointer block the references are, so we visit
- * all data blocks until there are no more translations to be done (or
- * we hit the end of the file, which can only happen in case a write fails,
- * e.g. when file system if full).
- * ml_find_line() does the work by translating the negative block numbers
- * when getting the first line of each data block.
- */
+ // Some of the data blocks may have been changed from negative to
+ // positive block number. In that case the pointer blocks need to be
+ // updated.
+ //
+ // We don't know in which pointer block the references are, so we visit
+ // all data blocks until there are no more translations to be done (or
+ // we hit the end of the file, which can only happen in case a write fails,
+ // e.g. when file system if full).
+ // ml_find_line() does the work by translating the negative block numbers
+ // when getting the first line of each data block.
if (mf_need_trans(mfp) && !got_int) {
- lnum = 1;
+ linenr_T lnum = 1;
while (mf_need_trans(mfp) && lnum <= buf->b_ml.ml_line_count) {
- hp = ml_find_line(buf, lnum, ML_FIND);
+ bhdr_T *hp = ml_find_line(buf, lnum, ML_FIND);
if (hp == NULL) {
status = FAIL;
goto theend;
@@ -1783,25 +1729,23 @@ theend:
}
}
-/*
- * NOTE: The pointer returned by the ml_get_*() functions only remains valid
- * until the next call!
- * line1 = ml_get(1);
- * line2 = ml_get(2); // line1 is now invalid!
- * Make a copy of the line if necessary.
- */
+// NOTE: The pointer returned by the ml_get_*() functions only remains valid
+// until the next call!
+// line1 = ml_get(1);
+// 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).
-char_u *ml_get(linenr_T lnum)
+char *ml_get(linenr_T lnum)
{
return ml_get_buf(curbuf, lnum, false);
}
/// @return pointer to position "pos".
-char_u *ml_get_pos(const pos_T *pos)
+char *ml_get_pos(const pos_T *pos)
FUNC_ATTR_NONNULL_ALL
{
return ml_get_buf(curbuf, pos->lnum, false) + pos->col;
@@ -1815,20 +1759,17 @@ int gchar_pos(pos_T *pos)
if (pos->col == MAXCOL) {
return NUL;
}
- return utf_ptr2char((char *)ml_get_pos(pos));
+ return utf_ptr2char(ml_get_pos(pos));
}
/// @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)
+char *ml_get_buf(buf_T *buf, linenr_T lnum, bool will_change)
FUNC_ATTR_NONNULL_ALL
{
- bhdr_T *hp;
- DATA_BL *dp;
- char_u *ptr;
static int recursive = 0;
- static char_u questions[4];
+ static char questions[4];
if (lnum > buf->b_ml.ml_line_count) { // invalid line number
if (recursive == 0) {
@@ -1850,23 +1791,20 @@ errorret:
}
if (buf->b_ml.ml_mfp == NULL) { // there are no lines
- return (char_u *)"";
+ return "";
}
- /*
- * See if it is the same line as requested last time.
- * Otherwise may need to flush last used line.
- * Don't use the last used line when 'swapfile' is reset, need to load all
- * blocks.
- */
+ // See if it is the same line as requested last time.
+ // Otherwise may need to flush last used line.
+ // Don't use the last used line when 'swapfile' is reset, need to load all
+ // blocks.
if (buf->b_ml.ml_line_lnum != lnum) {
ml_flush_line(buf);
- /*
- * Find the data block containing the line.
- * This also fills the stack with the blocks from the root to the data
- * block and releases any locked block.
- */
+ // Find the data block containing the line.
+ // This also fills the stack with the blocks from the root to the data
+ // block and releases any locked block.
+ bhdr_T *hp;
if ((hp = ml_find_line(buf, lnum, ML_FIND)) == NULL) {
if (recursive == 0) {
// Avoid giving this message for a recursive call, may happen
@@ -1881,10 +1819,9 @@ errorret:
goto errorret;
}
- dp = hp->bh_data;
+ DATA_BL *dp = hp->bh_data;
- ptr = (char_u *)dp +
- ((dp->db_index[lnum - buf->b_ml.ml_locked_low]) & DB_INDEX_MASK);
+ char *ptr = (char *)dp + (dp->db_index[lnum - buf->b_ml.ml_locked_low] & DB_INDEX_MASK);
buf->b_ml.ml_line_ptr = ptr;
buf->b_ml.ml_line_lnum = lnum;
buf->b_ml.ml_flags &= ~ML_LINE_DIRTY;
@@ -1908,7 +1845,7 @@ int ml_line_alloced(void)
/// "line" does not need to be allocated, but can't be another line in a
/// buffer, unlocking may make it invalid.
///
-/// newfile: TRUE when starting to edit a new file, meaning that pe_old_lnum
+/// newfile: true when starting to edit a new file, meaning that pe_old_lnum
/// will be set for recovery
/// Check: The caller of this function should probably also call
/// appended_lines().
@@ -1922,14 +1859,14 @@ int ml_line_alloced(void)
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) {
+ if (curbuf->b_ml.ml_mfp == NULL && open_buffer(false, NULL, 0) == FAIL) {
return FAIL;
}
if (curbuf->b_ml.ml_line_lnum != 0) {
ml_flush_line(curbuf);
}
- return ml_append_int(curbuf, lnum, (char_u *)line, len, newfile, false);
+ return ml_append_int(curbuf, lnum, line, len, newfile, false);
}
/// Like ml_append() but for an arbitrary buffer. The buffer must already have
@@ -1939,7 +1876,7 @@ int ml_append(linenr_T lnum, char *line, colnr_T len, bool newfile)
/// @param line text of the new line
/// @param len length of new line, including NUL, or 0
/// @param newfile flag, see above
-int ml_append_buf(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, bool newfile)
+int ml_append_buf(buf_T *buf, linenr_T lnum, char *line, colnr_T len, bool newfile)
FUNC_ATTR_NONNULL_ARG(1)
{
if (buf->b_ml.ml_mfp == NULL) {
@@ -1949,7 +1886,7 @@ int ml_append_buf(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, bool new
if (buf->b_ml.ml_line_lnum != 0) {
ml_flush_line(buf);
}
- return ml_append_int(buf, lnum, line, len, newfile, FALSE);
+ return ml_append_int(buf, lnum, line, len, newfile, false);
}
/// @param lnum append after this line (can be 0)
@@ -1957,20 +1894,8 @@ int ml_append_buf(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, bool new
/// @param len length of line, including NUL, or 0
/// @param newfile flag, see above
/// @param mark mark the new line
-static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, bool newfile,
- int mark)
+static int ml_append_int(buf_T *buf, linenr_T lnum, char *line, colnr_T len, bool newfile, int mark)
{
- int i;
- int line_count; // number of indexes in current block
- int offset;
- int from, to;
- int page_count;
- int db_idx; // index for lnum in data block
- bhdr_T *hp;
- DATA_BL *dp;
- PTR_BL *pp;
- infoptr_T *ip;
-
// lnum out of range
if (lnum > buf->b_ml.ml_line_count || buf->b_ml.ml_mfp == NULL) {
return FAIL;
@@ -1981,18 +1906,17 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b
}
if (len == 0) {
- len = (colnr_T)STRLEN(line) + 1; // space needed for the text
+ len = (colnr_T)strlen(line) + 1; // space needed for the text
}
int space_needed = len + (int)INDEX_SIZE; // space needed for text + index
memfile_T *mfp = buf->b_ml.ml_mfp;
int page_size = (int)mfp->mf_page_size;
- /*
- * find the data block containing the previous line
- * This also fills the stack with the blocks from the root to the data block
- * This also releases any locked block.
- */
+ // find the data block containing the previous line
+ // This also fills the stack with the blocks from the root to the data block
+ // This also releases any locked block.
+ bhdr_T *hp;
if ((hp = ml_find_line(buf, lnum == 0 ? (linenr_T)1 : lnum,
ML_INSERT)) == NULL) {
return FAIL;
@@ -2000,32 +1924,29 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b
buf->b_ml.ml_flags &= ~ML_EMPTY;
+ int db_idx; // index for lnum in data block
if (lnum == 0) { // got line one instead, correct db_idx
db_idx = -1; // careful, it is negative!
} else {
db_idx = lnum - buf->b_ml.ml_locked_low;
}
- // get line count before the insertion
- line_count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low;
+ // get line count (number of indexes in current block) before the insertion
+ int line_count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low;
- dp = hp->bh_data;
+ DATA_BL *dp = hp->bh_data;
- /*
- * If
- * - there is not enough room in the current block
- * - appending to the last line in the block
- * - not appending to the last line in the file
- * insert in front of the next block.
- */
+ // If
+ // - there is not enough room in the current block
+ // - appending to the last line in the block
+ // - not appending to the last line in the file
+ // insert in front of the next block.
if ((int)dp->db_free < space_needed && db_idx == line_count - 1
&& lnum < buf->b_ml.ml_line_count) {
- /*
- * Now that the line is not going to be inserted in the block that we
- * expected, the line count has to be adjusted in the pointer blocks
- * by using ml_locked_lineadd.
- */
- --(buf->b_ml.ml_locked_lineadd);
- --(buf->b_ml.ml_locked_high);
+ // Now that the line is not going to be inserted in the block that we
+ // expected, the line count has to be adjusted in the pointer blocks
+ // by using ml_locked_lineadd.
+ (buf->b_ml.ml_locked_lineadd)--;
+ (buf->b_ml.ml_locked_high)--;
if ((hp = ml_find_line(buf, lnum + 1, ML_INSERT)) == NULL) {
return FAIL;
}
@@ -2038,25 +1959,20 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b
dp = hp->bh_data;
}
- ++buf->b_ml.ml_line_count;
+ buf->b_ml.ml_line_count++;
if ((int)dp->db_free >= space_needed) { // enough room in data block
- /*
- * Insert new line in existing data block, or in data block allocated above.
- */
+ // Insert new line in existing data block, or in data block allocated above.
dp->db_txt_start -= (unsigned)len;
dp->db_free -= (unsigned)space_needed;
dp->db_line_count++;
- /*
- * move the text of the lines that follow to the front
- * adjust the indexes of the lines that follow
- */
+ // move the text of the lines that follow to the front
+ // adjust the indexes of the lines that follow
if (line_count > db_idx + 1) { // if there are following lines
- /*
- * Offset is the start of the previous line.
- * This will become the character just after the new line.
- */
+ // Offset is the start of the previous line.
+ // This will become the character just after the new line.
+ int offset;
if (db_idx < 0) {
offset = (int)dp->db_txt_end;
} else {
@@ -2065,7 +1981,7 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b
memmove((char *)dp + dp->db_txt_start,
(char *)dp + dp->db_txt_start + len,
(size_t)offset - (dp->db_txt_start + (size_t)len));
- for (i = line_count - 1; i > db_idx; i--) {
+ for (int i = line_count - 1; i > db_idx; i--) {
dp->db_index[i + 1] = dp->db_index[i] - (unsigned)len;
}
dp->db_index[db_idx + 1] = (unsigned)(offset - len);
@@ -2073,31 +1989,25 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b
dp->db_index[db_idx + 1] = dp->db_txt_start;
}
- /*
- * copy the text into the block
- */
+ // copy the text into the block
memmove((char *)dp + dp->db_index[db_idx + 1], line, (size_t)len);
if (mark) {
dp->db_index[db_idx + 1] |= DB_MARKED;
}
- /*
- * Mark the block dirty.
- */
+ // Mark the block dirty.
buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
if (!newfile) {
buf->b_ml.ml_flags |= ML_LOCKED_POS;
}
} else { // not enough space in data block
- /*
- * If there is not enough room we have to create a new data block and copy some
- * lines into it.
- * Then we have to insert an entry in the pointer block.
- * If this pointer block also is full, we go up another block, and so on, up
- * to the root if necessary.
- * The line counts in the pointer blocks have already been adjusted by
- * ml_find_line().
- */
+ // If there is not enough room we have to create a new data block and copy some
+ // lines into it.
+ // Then we have to insert an entry in the pointer block.
+ // If this pointer block also is full, we go up another block, and so on, up
+ // to the root if necessary.
+ // The line counts in the pointer blocks have already been adjusted by
+ // ml_find_line().
int line_count_left, line_count_right;
int page_count_left, page_count_right;
bhdr_T *hp_left;
@@ -2115,14 +2025,12 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b
int pb_idx;
PTR_BL *pp_new;
- /*
- * We are going to allocate a new data block. Depending on the
- * situation it will be put to the left or right of the existing
- * block. If possible we put the new line in the left block and move
- * the lines after it to the right block. Otherwise the new line is
- * also put in the right block. This method is more efficient when
- * inserting a lot of lines at one place.
- */
+ // We are going to allocate a new data block. Depending on the
+ // situation it will be put to the left or right of the existing
+ // block. If possible we put the new line in the left block and move
+ // the lines after it to the right block. Otherwise the new line is
+ // also put in the right block. This method is more efficient when
+ // inserting a lot of lines at one place.
if (db_idx < 0) { // left block is new, right block is existing
lines_moved = 0;
in_left = true;
@@ -2146,7 +2054,7 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b
}
}
- page_count = ((space_needed + (int)HEADER_SIZE) + page_size - 1) / page_size;
+ int page_count = ((space_needed + (int)HEADER_SIZE) + page_size - 1) / page_size;
hp_new = ml_new_data(mfp, newfile, page_count);
if (db_idx < 0) { // left block is new
hp_left = hp_new;
@@ -2166,9 +2074,7 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b
page_count_left = (int)hp_left->bh_page_count;
page_count_right = (int)hp_right->bh_page_count;
- /*
- * May move the new line into the right/new block.
- */
+ // May move the new line into the right/new block.
if (!in_left) {
dp_right->db_txt_start -= (unsigned)len;
dp_right->db_free -= (unsigned)len + (unsigned)INDEX_SIZE;
@@ -2181,25 +2087,19 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b
line, (size_t)len);
line_count_right++;
}
- /*
- * may move lines from the left/old block to the right/new one.
- */
+ // may move lines from the left/old block to the right/new one.
if (lines_moved) {
- /*
- */
dp_right->db_txt_start -= (unsigned)data_moved;
dp_right->db_free -= (unsigned)total_moved;
memmove((char *)dp_right + dp_right->db_txt_start,
(char *)dp_left + dp_left->db_txt_start,
(size_t)data_moved);
- offset = (int)(dp_right->db_txt_start - dp_left->db_txt_start);
+ int offset = (int)(dp_right->db_txt_start - dp_left->db_txt_start);
dp_left->db_txt_start += (unsigned)data_moved;
dp_left->db_free += (unsigned)total_moved;
- /*
- * update indexes in the new block
- */
- for (to = line_count_right, from = db_idx + 1;
+ // update indexes in the new block
+ for (int to = line_count_right, from = db_idx + 1;
from < line_count_left; from++, to++) {
dp_right->db_index[to] = dp->db_index[from] + (unsigned)offset;
}
@@ -2207,9 +2107,7 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b
line_count_left -= lines_moved;
}
- /*
- * May move the new line into the left (old or new) block.
- */
+ // May move the new line into the left (old or new) block.
if (in_left) {
dp_left->db_txt_start -= (unsigned)len;
dp_left->db_free -= (unsigned)len + (unsigned)INDEX_SIZE;
@@ -2236,12 +2134,10 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b
dp_left->db_line_count = line_count_left;
dp_right->db_line_count = line_count_right;
- /*
- * release the two data blocks
- * The new one (hp_new) already has a correct blocknumber.
- * The old one (hp, in ml_locked) gets a positive blocknumber if
- * we changed it and we are not editing a new file.
- */
+ // release the two data blocks
+ // The new one (hp_new) already has a correct blocknumber.
+ // The old one (hp, in ml_locked) gets a positive blocknumber if
+ // we changed it and we are not editing a new file.
if (lines_moved || in_left) {
buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
}
@@ -2250,35 +2146,28 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b
}
mf_put(mfp, hp_new, true, false);
- /*
- * flush the old data block
- * set ml_locked_lineadd to 0, because the updating of the
- * pointer blocks is done below
- */
+ // flush the old data block
+ // set ml_locked_lineadd to 0, because the updating of the
+ // pointer blocks is done below
lineadd = buf->b_ml.ml_locked_lineadd;
buf->b_ml.ml_locked_lineadd = 0;
(void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush data block
- /*
- * update pointer blocks for the new data block
- */
- for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0;
- --stack_idx) {
- ip = &(buf->b_ml.ml_stack[stack_idx]);
+ // update pointer blocks for the new data block
+ for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0; stack_idx--) {
+ infoptr_T *ip = &(buf->b_ml.ml_stack[stack_idx]);
pb_idx = ip->ip_index;
if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL) {
return FAIL;
}
- pp = hp->bh_data; // must be pointer block
+ PTR_BL *pp = hp->bh_data; // must be pointer block
if (pp->pb_id != PTR_ID) {
iemsg(_("E317: pointer block id wrong 3"));
mf_put(mfp, hp, false, false);
return FAIL;
}
- /*
- * TODO: If the pointer block is full and we are adding at the end
- * try to insert in front of the next block
- */
+ // TODO(vim): If the pointer block is full and we are adding at the end
+ // try to insert in front of the next block
// block not full, add one entry
if (pp->pb_count < pp->pb_count_max) {
if (pb_idx + 1 < (int)pp->pb_count) {
@@ -2286,7 +2175,7 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b
&pp->pb_pointer[pb_idx + 1],
(size_t)(pp->pb_count - pb_idx - 1) * sizeof(PTR_EN));
}
- ++pp->pb_count;
+ pp->pb_count++;
pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
@@ -2305,18 +2194,16 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b
buf->b_ml.ml_stack_top = stack_idx + 1; // truncate stack
if (lineadd) {
- --(buf->b_ml.ml_stack_top);
+ (buf->b_ml.ml_stack_top)--;
// fix line count for rest of blocks in the stack
ml_lineadd(buf, lineadd);
// fix stack itself
buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high +=
lineadd;
- ++(buf->b_ml.ml_stack_top);
+ (buf->b_ml.ml_stack_top)++;
}
- /*
- * We are finished, break the loop here.
- */
+ // We are finished, break the loop here.
break;
}
// pointer block full
@@ -2386,11 +2273,11 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b
// recompute line counts
line_count_right = 0;
- for (i = 0; i < (int)pp_new->pb_count; i++) {
+ for (int 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++) {
+ for (int i = 0; i < (int)pp->pb_count; i++) {
line_count_left += pp->pb_pointer[i].pe_line_count;
}
@@ -2402,9 +2289,7 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b
mf_put(mfp, hp_new, true, false);
}
- /*
- * Safety check: fallen out of for loop?
- */
+ // Safety check: fallen out of for loop?
if (stack_idx < 0) {
iemsg(_("E318: Updated too many blocks?"));
buf->b_ml.ml_stack_top = 0; // invalidate stack
@@ -2416,18 +2301,19 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b
return OK;
}
-void ml_add_deleted_len(char_u *ptr, ssize_t len)
+void ml_add_deleted_len(char *ptr, ssize_t len)
{
ml_add_deleted_len_buf(curbuf, ptr, len);
}
-void ml_add_deleted_len_buf(buf_T *buf, char_u *ptr, ssize_t len)
+void ml_add_deleted_len_buf(buf_T *buf, char *ptr, ssize_t len)
{
if (inhibit_delete_count) {
return;
}
- if (len == -1) {
- len = (ssize_t)STRLEN(ptr);
+ ssize_t maxlen = (ssize_t)strlen(ptr);
+ if (len == -1 || len > maxlen) {
+ len = maxlen;
}
curbuf->deleted_bytes += (size_t)len + 1;
curbuf->deleted_bytes2 += (size_t)len + 1;
@@ -2441,7 +2327,7 @@ void ml_add_deleted_len_buf(buf_T *buf, char_u *ptr, ssize_t len)
int ml_replace(linenr_T lnum, char *line, bool copy)
{
- return ml_replace_buf(curbuf, lnum, (char_u *)line, copy);
+ return ml_replace_buf(curbuf, lnum, line, copy);
}
/// Replace line "lnum", with buffering, in current buffer.
@@ -2453,10 +2339,10 @@ int ml_replace(linenr_T lnum, char *line, bool copy)
/// 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.
+/// changed_lines(), unless update_screen(UPD_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)
+int ml_replace_buf(buf_T *buf, linenr_T lnum, char *line, bool copy)
{
if (line == NULL) { // just checking...
return FAIL;
@@ -2470,7 +2356,7 @@ int ml_replace_buf(buf_T *buf, linenr_T lnum, char_u *line, bool copy)
bool readlen = true;
if (copy) {
- line = vim_strsave(line);
+ line = xstrdup(line);
}
if (buf->b_ml.ml_line_lnum != lnum) { // other line buffered
ml_flush_line(buf); // flush it
@@ -2508,18 +2394,6 @@ int ml_delete(linenr_T lnum, bool message)
static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message)
{
- bhdr_T *hp;
- memfile_T *mfp;
- DATA_BL *dp;
- PTR_BL *pp;
- infoptr_T *ip;
- int count; // number of entries in block
- int idx;
- int stack_idx;
- int line_start;
- long line_size;
- int i;
-
if (lnum < 1 || lnum > buf->b_ml.ml_line_count) {
return FAIL;
}
@@ -2528,42 +2402,40 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message)
lowest_marked--;
}
- /*
- * If the file becomes empty the last line is replaced by an empty line.
- */
+ // If the file becomes empty the last line is replaced by an empty line.
if (buf->b_ml.ml_line_count == 1) { // file becomes empty
if (message) {
set_keep_msg(_(no_lines_msg), 0);
}
- i = ml_replace((linenr_T)1, "", true);
+ int i = ml_replace((linenr_T)1, "", true);
buf->b_ml.ml_flags |= ML_EMPTY;
return i;
}
- /*
- * find the data block containing the line
- * This also fills the stack with the blocks from the root to the data block
- * This also releases any locked block.
- */
- mfp = buf->b_ml.ml_mfp;
+ // find the data block containing the line
+ // This also fills the stack with the blocks from the root to the data block
+ // This also releases any locked block.
+ memfile_T *mfp = buf->b_ml.ml_mfp;
if (mfp == NULL) {
return FAIL;
}
+ bhdr_T *hp;
if ((hp = ml_find_line(buf, lnum, ML_DELETE)) == NULL) {
return FAIL;
}
- dp = hp->bh_data;
- // compute line count before the delete
- count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 2;
- idx = lnum - buf->b_ml.ml_locked_low;
+ DATA_BL *dp = hp->bh_data;
+ // compute line count (number of entries in block) before the delete
+ int count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 2;
+ int idx = lnum - buf->b_ml.ml_locked_low;
- --buf->b_ml.ml_line_count;
+ buf->b_ml.ml_line_count--;
- line_start = ((dp->db_index[idx]) & DB_INDEX_MASK);
+ int line_start = ((dp->db_index[idx]) & DB_INDEX_MASK);
+ long line_size;
if (idx == 0) { // first line in block, text at the end
line_size = dp->db_txt_end - (unsigned)line_start;
} else {
@@ -2573,29 +2445,26 @@ 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);
-
- /*
- * special case: If there is only one line in the data block it becomes empty.
- * Then we have to remove the entry, pointing to this data block, from the
- * pointer block. If this pointer block also becomes empty, we go up another
- * block, and so on, up to the root if necessary.
- * The line counts in the pointer blocks have already been adjusted by
- * ml_find_line().
- */
+ ml_add_deleted_len_buf(buf, (char *)dp + line_start, line_size - 1);
+
+ // special case: If there is only one line in the data block it becomes empty.
+ // Then we have to remove the entry, pointing to this data block, from the
+ // pointer block. If this pointer block also becomes empty, we go up another
+ // block, and so on, up to the root if necessary.
+ // The line counts in the pointer blocks have already been adjusted by
+ // ml_find_line().
if (count == 1) {
mf_free(mfp, hp); // free the data block
buf->b_ml.ml_locked = NULL;
- for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0;
- --stack_idx) {
+ for (int stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0; stack_idx--) {
buf->b_ml.ml_stack_top = 0; // stack is invalid when failing
- ip = &(buf->b_ml.ml_stack[stack_idx]);
+ infoptr_T *ip = &(buf->b_ml.ml_stack[stack_idx]);
idx = ip->ip_index;
if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL) {
return FAIL;
}
- pp = hp->bh_data; // must be pointer block
+ PTR_BL *pp = hp->bh_data; // must be pointer block
if (pp->pb_id != PTR_ID) {
iemsg(_("E317: pointer block id wrong 4"));
mf_put(mfp, hp, false, false);
@@ -2618,7 +2487,7 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message)
buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high +=
buf->b_ml.ml_locked_lineadd;
}
- ++(buf->b_ml.ml_stack_top);
+ (buf->b_ml.ml_stack_top)++;
break;
}
@@ -2632,7 +2501,7 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message)
// delete the index by moving the next indexes backwards
// Adjust the indexes for the text movement.
- for (i = idx; i < count - 1; i++) {
+ for (int i = idx; i < count - 1; i++) {
dp->db_index[i] = dp->db_index[i + 1] + (unsigned)line_size;
}
@@ -2640,9 +2509,7 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message)
dp->db_txt_start += (unsigned)line_size;
dp->db_line_count--;
- /*
- * mark the block dirty and make sure it is in the file (for recovery)
- */
+ // mark the block dirty and make sure it is in the file (for recovery)
buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
}
@@ -2653,8 +2520,6 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message)
/// set the B_MARKED flag for line 'lnum'
void ml_setmarked(linenr_T lnum)
{
- bhdr_T *hp;
- DATA_BL *dp;
// invalid line number
if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count
|| curbuf->b_ml.ml_mfp == NULL) {
@@ -2664,15 +2529,14 @@ void ml_setmarked(linenr_T lnum)
lowest_marked = lnum;
}
- /*
- * find the data block containing the line
- * This also fills the stack with the blocks from the root to the data block
- * This also releases any locked block.
- */
+ // find the data block containing the line
+ // This also fills the stack with the blocks from the root to the data block
+ // This also releases any locked block.
+ bhdr_T *hp;
if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL) {
return; // give error message?
}
- dp = hp->bh_data;
+ DATA_BL *dp = hp->bh_data;
dp->db_index[lnum - curbuf->b_ml.ml_locked_low] |= DB_MARKED;
curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
}
@@ -2680,32 +2544,24 @@ void ml_setmarked(linenr_T lnum)
/// find the first line with its B_MARKED flag set
linenr_T ml_firstmarked(void)
{
- bhdr_T *hp;
- DATA_BL *dp;
- linenr_T lnum;
- int i;
-
if (curbuf->b_ml.ml_mfp == NULL) {
return (linenr_T)0;
}
- /*
- * The search starts with lowest_marked line. This is the last line where
- * a mark was found, adjusted by inserting/deleting lines.
- */
- for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count;) {
- /*
- * Find the data block containing the line.
- * This also fills the stack with the blocks from the root to the data
- * block This also releases any locked block.
- */
+ // The search starts with lowest_marked line. This is the last line where
+ // a mark was found, adjusted by inserting/deleting lines.
+ for (linenr_T lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count;) {
+ // Find the data block containing the line.
+ // This also fills the stack with the blocks from the root to the data
+ // block This also releases any locked block.
+ bhdr_T *hp;
if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL) {
return (linenr_T)0; // give error message?
}
- dp = hp->bh_data;
+ DATA_BL *dp = hp->bh_data;
- for (i = lnum - curbuf->b_ml.ml_locked_low;
- lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum) {
+ for (int i = lnum - curbuf->b_ml.ml_locked_low;
+ lnum <= curbuf->b_ml.ml_locked_high; i++, lnum++) {
if ((dp->db_index[i]) & DB_MARKED) {
(dp->db_index[i]) &= DB_INDEX_MASK;
curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
@@ -2721,31 +2577,23 @@ linenr_T ml_firstmarked(void)
/// clear all DB_MARKED flags
void ml_clearmarked(void)
{
- bhdr_T *hp;
- DATA_BL *dp;
- linenr_T lnum;
- int i;
-
if (curbuf->b_ml.ml_mfp == NULL) { // nothing to do
return;
}
- /*
- * The search starts with line lowest_marked.
- */
- for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count;) {
- /*
- * Find the data block containing the line.
- * This also fills the stack with the blocks from the root to the data
- * block and releases any locked block.
- */
+ // The search starts with line lowest_marked.
+ for (linenr_T lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count;) {
+ // Find the data block containing the line.
+ // This also fills the stack with the blocks from the root to the data
+ // block and releases any locked block.
+ bhdr_T *hp;
if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL) {
return; // give error message?
}
- dp = hp->bh_data;
+ DATA_BL *dp = hp->bh_data;
- for (i = lnum - curbuf->b_ml.ml_locked_low;
- lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum) {
+ for (int i = lnum - curbuf->b_ml.ml_locked_low;
+ lnum <= curbuf->b_ml.ml_locked_high; i++, lnum++) {
if ((dp->db_index[i]) & DB_MARKED) {
(dp->db_index[i]) &= DB_INDEX_MASK;
curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
@@ -2770,18 +2618,6 @@ size_t ml_flush_deleted_bytes(buf_T *buf, size_t *codepoints, size_t *codeunits)
/// flush ml_line if necessary
static void ml_flush_line(buf_T *buf)
{
- bhdr_T *hp;
- DATA_BL *dp;
- linenr_T lnum;
- char_u *new_line;
- char_u *old_line;
- colnr_T new_len;
- int old_len;
- int extra;
- int idx;
- int start;
- int count;
- int i;
static bool entered = false;
if (buf->b_ml.ml_line_lnum == 0 || buf->b_ml.ml_mfp == NULL) {
@@ -2796,31 +2632,30 @@ static void ml_flush_line(buf_T *buf)
buf->flush_count++;
- lnum = buf->b_ml.ml_line_lnum;
- new_line = buf->b_ml.ml_line_ptr;
+ linenr_T lnum = buf->b_ml.ml_line_lnum;
+ char *new_line = buf->b_ml.ml_line_ptr;
- hp = ml_find_line(buf, lnum, ML_FIND);
+ bhdr_T *hp = ml_find_line(buf, lnum, ML_FIND);
if (hp == NULL) {
siemsg(_("E320: Cannot find line %" PRId64), (int64_t)lnum);
} else {
- dp = hp->bh_data;
- idx = lnum - buf->b_ml.ml_locked_low;
- start = ((dp->db_index[idx]) & DB_INDEX_MASK);
- old_line = (char_u *)dp + start;
+ DATA_BL *dp = hp->bh_data;
+ int idx = lnum - buf->b_ml.ml_locked_low;
+ int start = ((dp->db_index[idx]) & DB_INDEX_MASK);
+ char *old_line = (char *)dp + start;
+ int old_len;
if (idx == 0) { // line is last in block
old_len = (int)dp->db_txt_end - start;
} else { // text of previous line follows
old_len = (int)(dp->db_index[idx - 1] & DB_INDEX_MASK) - start;
}
- new_len = (colnr_T)STRLEN(new_line) + 1;
- extra = new_len - old_len; // negative if lines gets smaller
+ colnr_T new_len = (colnr_T)strlen(new_line) + 1;
+ int extra = new_len - old_len; // negative if lines gets smaller
- /*
- * if new line fits in data block, replace directly
- */
+ // if new line fits in data block, replace directly
if ((int)dp->db_free >= extra) {
// if the length changes and there are following lines
- count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 1;
+ int count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 1;
if (extra != 0 && idx < count - 1) {
// move text of following lines
memmove((char *)dp + dp->db_txt_start - extra,
@@ -2828,7 +2663,7 @@ static void ml_flush_line(buf_T *buf)
(size_t)(start - (int)dp->db_txt_start));
// adjust pointers of this and following lines
- for (i = idx + 1; i < count; i++) {
+ for (int i = idx + 1; i < count; i++) {
dp->db_index[i] -= (unsigned)extra;
}
}
@@ -2905,39 +2740,28 @@ static bhdr_T *ml_new_ptr(memfile_T *mfp)
/// @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;
PTR_BL *pp;
- infoptr_T *ip;
bhdr_T *hp;
- memfile_T *mfp;
- linenr_T t;
- blocknr_T bnum, bnum2;
- int dirty;
- linenr_T low, high;
int top;
- int page_count;
- int idx;
- mfp = buf->b_ml.ml_mfp;
+ memfile_T *mfp = buf->b_ml.ml_mfp;
- /*
- * If there is a locked block check if the wanted line is in it.
- * If not, flush and release the locked block.
- * Don't do this for ML_INSERT_SAME, because the stack need to be updated.
- * Don't do this for ML_FLUSH, because we want to flush the locked block.
- * Don't do this when 'swapfile' is reset, we want to load all the blocks.
- */
+ // If there is a locked block check if the wanted line is in it.
+ // If not, flush and release the locked block.
+ // Don't do this for ML_INSERT_SAME, because the stack need to be updated.
+ // Don't do this for ML_FLUSH, because we want to flush the locked block.
+ // Don't do this when 'swapfile' is reset, we want to load all the blocks.
if (buf->b_ml.ml_locked) {
if (ML_SIMPLE(action)
&& buf->b_ml.ml_locked_low <= lnum
&& buf->b_ml.ml_locked_high >= lnum) {
// remember to update pointer blocks and stack later
if (action == ML_INSERT) {
- ++(buf->b_ml.ml_locked_lineadd);
- ++(buf->b_ml.ml_locked_high);
+ (buf->b_ml.ml_locked_lineadd)++;
+ (buf->b_ml.ml_locked_high)++;
} else if (action == ML_DELETE) {
- --(buf->b_ml.ml_locked_lineadd);
- --(buf->b_ml.ml_locked_high);
+ (buf->b_ml.ml_locked_lineadd)--;
+ (buf->b_ml.ml_locked_high)--;
}
return buf->b_ml.ml_locked;
}
@@ -2946,10 +2770,8 @@ static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action)
buf->b_ml.ml_flags & ML_LOCKED_POS);
buf->b_ml.ml_locked = NULL;
- /*
- * If lines have been added or deleted in the locked block, need to
- * update the line count in pointer blocks.
- */
+ // If lines have been added or deleted in the locked block, need to
+ // update the line count in pointer blocks.
if (buf->b_ml.ml_locked_lineadd != 0) {
ml_lineadd(buf, buf->b_ml.ml_locked_lineadd);
}
@@ -2959,14 +2781,15 @@ static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action)
return NULL;
}
- bnum = 1; // start at the root of the tree
- page_count = 1;
- low = 1;
- high = buf->b_ml.ml_line_count;
+ blocknr_T bnum = 1; // start at the root of the tree
+ blocknr_T bnum2;
+ int page_count = 1;
+ linenr_T low = 1;
+ linenr_T high = buf->b_ml.ml_line_count;
if (action == ML_FIND) { // first try stack entries
- for (top = buf->b_ml.ml_stack_top - 1; top >= 0; --top) {
- ip = &(buf->b_ml.ml_stack[top]);
+ for (top = buf->b_ml.ml_stack_top - 1; top >= 0; top--) {
+ infoptr_T *ip = &(buf->b_ml.ml_stack[top]);
if (ip->ip_low <= lnum && ip->ip_high >= lnum) {
bnum = ip->ip_bnum;
low = ip->ip_low;
@@ -2981,24 +2804,20 @@ static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action)
} else { // ML_DELETE or ML_INSERT
buf->b_ml.ml_stack_top = 0; // start at the root
}
- /*
- * search downwards in the tree until a data block is found
- */
+ // search downwards in the tree until a data block is found
for (;;) {
if ((hp = mf_get(mfp, bnum, (unsigned)page_count)) == NULL) {
goto error_noblock;
}
- /*
- * update high for insert/delete
- */
+ // update high for insert/delete
if (action == ML_INSERT) {
high++;
} else if (action == ML_DELETE) {
high--;
}
- dp = hp->bh_data;
+ DATA_BL *dp = hp->bh_data;
if (dp->db_id == DATA_ID) { // data block
buf->b_ml.ml_locked = hp;
buf->b_ml.ml_locked_low = low;
@@ -3015,15 +2834,16 @@ static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action)
}
top = ml_add_stack(buf); // add new entry to stack
- ip = &(buf->b_ml.ml_stack[top]);
+ infoptr_T *ip = &(buf->b_ml.ml_stack[top]);
ip->ip_bnum = bnum;
ip->ip_low = low;
ip->ip_high = high;
ip->ip_index = -1; // index not known yet
- dirty = FALSE;
- for (idx = 0; idx < (int)pp->pb_count; ++idx) {
- t = pp->pb_pointer[idx].pe_line_count;
+ bool dirty = false;
+ int idx;
+ for (idx = 0; idx < (int)pp->pb_count; idx++) {
+ linenr_T t = pp->pb_pointer[idx].pe_line_count;
CHECK(t == 0, _("pe_line_count is zero"));
if ((low += t) > lnum) {
ip->ip_index = idx;
@@ -3032,15 +2852,13 @@ static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action)
high = low - 1;
low -= t;
- /*
- * a negative block number may have been changed
- */
+ // a negative block number may have been changed
if (bnum < 0) {
bnum2 = mf_trans_del(mfp, bnum);
if (bnum != bnum2) {
bnum = bnum2;
pp->pb_pointer[idx].pe_bnum = bnum;
- dirty = TRUE;
+ dirty = true;
}
}
@@ -3058,10 +2876,10 @@ static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action)
}
if (action == ML_DELETE) {
pp->pb_pointer[idx].pe_line_count--;
- dirty = TRUE;
+ dirty = true;
} else if (action == ML_INSERT) {
pp->pb_pointer[idx].pe_line_count++;
- dirty = TRUE;
+ dirty = true;
}
mf_put(mfp, hp, dirty, false);
}
@@ -3069,11 +2887,9 @@ static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action)
error_block:
mf_put(mfp, hp, false, false);
error_noblock:
- /*
- * If action is ML_DELETE or ML_INSERT we have to correct the tree for
- * the incremented/decremented line counts, because there won't be a line
- * inserted/deleted after all.
- */
+ // If action is ML_DELETE or ML_INSERT we have to correct the tree for
+ // the incremented/decremented line counts, because there won't be a line
+ // inserted/deleted after all.
if (action == ML_DELETE) {
ml_lineadd(buf, 1);
} else if (action == ML_INSERT) {
@@ -3113,18 +2929,15 @@ static int ml_add_stack(buf_T *buf)
/// Count is the number of lines added, negative if lines have been deleted.
static void ml_lineadd(buf_T *buf, int count)
{
- int idx;
- infoptr_T *ip;
- PTR_BL *pp;
memfile_T *mfp = buf->b_ml.ml_mfp;
- bhdr_T *hp;
- for (idx = buf->b_ml.ml_stack_top - 1; idx >= 0; --idx) {
- ip = &(buf->b_ml.ml_stack[idx]);
+ for (int idx = buf->b_ml.ml_stack_top - 1; idx >= 0; idx--) {
+ infoptr_T *ip = &(buf->b_ml.ml_stack[idx]);
+ bhdr_T *hp;
if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL) {
break;
}
- pp = hp->bh_data; // must be pointer block
+ PTR_BL *pp = hp->bh_data; // must be pointer block
if (pp->pb_id != PTR_ID) {
mf_put(mfp, hp, false, false);
iemsg(_("E317: pointer block id wrong 2"));
@@ -3144,10 +2957,9 @@ static void ml_lineadd(buf_T *buf, int count)
///
/// @return OK if it worked and the resolved link in "buf[MAXPATHL]",
/// FAIL otherwise
-int resolve_symlink(const char_u *fname, char_u *buf)
+int resolve_symlink(const char *fname, char *buf)
{
- char_u tmp[MAXPATHL];
- int ret;
+ char tmp[MAXPATHL];
int depth = 0;
if (fname == NULL) {
@@ -3155,7 +2967,7 @@ int resolve_symlink(const char_u *fname, char_u *buf)
}
// Put the result so far in tmp[], starting with the original name.
- STRLCPY(tmp, fname, MAXPATHL);
+ xstrlcpy(tmp, fname, MAXPATHL);
for (;;) {
// Limit symlink depth to 100, catch recursive loops.
@@ -3164,7 +2976,7 @@ int resolve_symlink(const char_u *fname, char_u *buf)
return FAIL;
}
- ret = (int)readlink((char *)tmp, (char *)buf, MAXPATHL - 1);
+ int ret = (int)readlink(tmp, buf, MAXPATHL - 1);
if (ret <= 0) {
if (errno == EINVAL || errno == ENOENT) {
// Found non-symlink or not existing file, stop here.
@@ -3190,57 +3002,54 @@ int resolve_symlink(const char_u *fname, char_u *buf)
if (path_is_absolute(buf)) {
STRCPY(tmp, buf);
} else {
- char_u *tail = (char_u *)path_tail((char *)tmp);
- if (STRLEN(tail) + STRLEN(buf) >= MAXPATHL) {
+ char *tail = path_tail(tmp);
+ if (strlen(tail) + strlen(buf) >= MAXPATHL) {
return FAIL;
}
STRCPY(tail, buf);
}
}
- /*
- * Try to resolve the full name of the file so that the swapfile name will
- * be consistent even when opening a relative symlink from different
- * working directories.
- */
- return vim_FullName((char *)tmp, (char *)buf, MAXPATHL, TRUE);
+ // Try to resolve the full name of the file so that the swapfile name will
+ // be consistent even when opening a relative symlink from different
+ // working directories.
+ return vim_FullName(tmp, buf, MAXPATHL, true);
}
#endif
/// 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 *makeswapname(char *fname, char *ffname, buf_T *buf, char *dir_name)
{
- char_u *r, *s;
- char_u *fname_res = fname;
+ char *fname_res = fname;
#ifdef HAVE_READLINK
- char_u fname_buf[MAXPATHL];
+ char fname_buf[MAXPATHL];
// Expand symlink in the file name, so that we put the swap file with the
// actual file instead of with the symlink.
- if (resolve_symlink(fname, fname_buf) == OK) {
+ if (resolve_symlink(fname, (char *)fname_buf) == OK) {
fname_res = fname_buf;
}
#endif
- int len = (int)STRLEN(dir_name);
+ int len = (int)strlen(dir_name);
- s = dir_name + len;
- if (after_pathsep((char *)dir_name, (char *)s)
+ char *s = dir_name + len;
+ if (after_pathsep(dir_name, s)
&& len > 1
&& s[-1] == s[-2]) { // Ends with '//', Use Full path
- r = NULL;
- s = (char_u *)make_percent_swname((char *)dir_name, (char *)fname_res);
+ char *r = NULL;
+ s = make_percent_swname(dir_name, fname_res);
if (s != NULL) {
- r = (char_u *)modname((char *)s, ".swp", false);
+ r = modname(s, ".swp", false);
xfree(s);
}
return r;
}
// Prepend a '.' to the swap file name for the current directory.
- r = (char_u *)modname((char *)fname_res, ".swp",
- dir_name[0] == '.' && dir_name[1] == NUL);
+ char *r = modname(fname_res, ".swp",
+ dir_name[0] == '.' && dir_name[1] == NUL);
if (r == NULL) { // out of memory
return NULL;
}
@@ -3262,30 +3071,27 @@ char_u *makeswapname(char_u *fname, char_u *ffname, buf_T *buf, char_u *dir_name
/// The return value is an allocated string and can be NULL.
///
/// @param dname don't use "dirname", it is a global for Alpha
-char_u *get_file_in_dir(char_u *fname, char_u *dname)
+char *get_file_in_dir(char *fname, char *dname)
{
- char_u *t;
- char_u *tail;
- char_u *retval;
- int save_char;
+ char *retval;
- tail = (char_u *)path_tail((char *)fname);
+ char *tail = path_tail(fname);
if (dname[0] == '.' && dname[1] == NUL) {
- retval = vim_strsave(fname);
+ retval = xstrdup(fname);
} else if (dname[0] == '.' && vim_ispathsep(dname[1])) {
if (tail == fname) { // no path before file name
- retval = (char_u *)concat_fnames((char *)dname + 2, (char *)tail, TRUE);
+ retval = concat_fnames(dname + 2, tail, true);
} else {
- save_char = *tail;
+ char save_char = *tail;
*tail = NUL;
- t = (char_u *)concat_fnames((char *)fname, (char *)dname + 2, true);
- *tail = (uint8_t)save_char;
- retval = (char_u *)concat_fnames((char *)t, (char *)tail, true);
+ char *t = concat_fnames(fname, dname + 2, true);
+ *tail = save_char;
+ retval = concat_fnames(t, tail, true);
xfree(t);
}
} else {
- retval = (char_u *)concat_fnames((char *)dname, (char *)tail, TRUE);
+ retval = concat_fnames(dname, tail, true);
}
return retval;
@@ -3295,7 +3101,7 @@ char_u *get_file_in_dir(char_u *fname, char_u *dname)
///
/// @param buf buffer being edited
/// @param fname swap file name
-static void attention_message(buf_T *buf, char_u *fname)
+static void attention_message(buf_T *buf, char *fname)
{
assert(buf->b_fname != NULL);
@@ -3315,7 +3121,7 @@ static void attention_message(buf_T *buf, char_u *fname)
msg_puts(_(" dated: "));
time_t x = file_info.stat.st_mtim.tv_sec;
char ctime_buf[50];
- msg_puts(os_ctime_r(&x, ctime_buf, sizeof(ctime_buf)));
+ msg_puts(os_ctime_r(&x, ctime_buf, sizeof(ctime_buf), true));
if (swap_mtime != 0 && x > swap_mtime) {
msg_puts(_(" NEWER than swap file!\n"));
}
@@ -3331,7 +3137,7 @@ 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((char *)fname);
+ msg_outtrans(fname);
msg_puts(_("\"\n to avoid this message.\n"));
cmdline_row = msg_row;
no_wait_return--;
@@ -3347,9 +3153,9 @@ static void attention_message(buf_T *buf, char_u *fname)
/// 4: delete it
/// 5: quit
/// 6: abort
-static int do_swapexists(buf_T *buf, char_u *fname)
+static int do_swapexists(buf_T *buf, char *fname)
{
- set_vim_var_string(VV_SWAPNAME, (char *)fname, -1);
+ set_vim_var_string(VV_SWAPNAME, fname, -1);
set_vim_var_string(VV_SWAPCHOICE, NULL, -1);
// Trigger SwapExists autocommands with <afile> set to the file being
@@ -3404,24 +3210,17 @@ static int do_swapexists(buf_T *buf, char_u *fname)
static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_existing_dir)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1, 2, 4)
{
- char *fname;
size_t n;
- char *dir_name;
char *buf_fname = buf->b_fname;
- /*
- * Isolate a directory name from *dirp and put it in dir_name.
- * First allocate some memory to put the directory name in.
- */
+ // Isolate a directory name from *dirp and put it in dir_name.
+ // First allocate some memory to put the directory name in.
const size_t dir_len = strlen(*dirp) + 1;
- dir_name = xmalloc(dir_len);
+ char *dir_name = xmalloc(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, (char_u *)buf->b_ffname, buf,
- (char_u *)dir_name);
+ // we try different names until we find one that does not exist yet
+ char *fname = makeswapname(buf_fname, buf->b_ffname, buf, dir_name);
for (;;) {
if (fname == NULL) { // must be out of memory
@@ -3441,7 +3240,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 && path_fnamecmp(fname, old_fname) == 0) {
break;
}
@@ -3455,7 +3254,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
&& !buf->b_help && !(buf->b_flags & BF_DUMMY)) {
int fd;
struct block0 b0;
- int differ = FALSE;
+ int differ = false;
// Try to read block 0 from the swap file to get the original
// file name (and inode number).
@@ -3466,25 +3265,25 @@ 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((char *)buf->b_ffname),
- path_tail((char *)b0.b0_fname)) != 0
- || !same_directory((char_u *)fname, (char_u *)buf->b_ffname)) {
+ if (path_fnamecmp(path_tail(buf->b_ffname),
+ path_tail((char *)b0.b0_fname)) != 0
+ || !same_directory(fname, 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((char_u *)buf->b_ffname, NameBuff,
+ expand_env((char *)b0.b0_fname, NameBuff, MAXPATHL);
+ if (fnamecmp_ino(buf->b_ffname, NameBuff,
char_to_long(b0.b0_ino))) {
- differ = TRUE;
+ differ = true;
}
}
} else {
// The name in the swap file may be
// "~user/path/file". Expand it first.
- expand_env(b0.b0_fname, NameBuff, MAXPATHL);
- if (fnamecmp_ino((char_u *)buf->b_ffname, NameBuff,
+ expand_env((char *)b0.b0_fname, NameBuff, MAXPATHL);
+ if (fnamecmp_ino(buf->b_ffname, NameBuff,
char_to_long(b0.b0_ino))) {
- differ = TRUE;
+ differ = true;
}
}
}
@@ -3494,14 +3293,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((char *)p_shm, SHM_ATTENTION) == NULL) {
+ && vim_strchr(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((char_u *)buf->b_fname) && swapfile_unchanged(fname)) {
+ if (os_path_exists(buf->b_fname) && swapfile_unchanged(fname)) {
choice = 4;
if (p_verbose > 0) {
verb_msg(_("Found a swap file that is not useful, deleting it"));
@@ -3513,12 +3312,12 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
if (choice == 0
&& swap_exists_action != SEA_NONE
&& has_autocmd(EVENT_SWAPEXISTS, buf_fname, buf)) {
- choice = do_swapexists(buf, (char_u *)fname);
+ choice = do_swapexists(buf, fname);
}
if (choice == 0) {
// Show info about the existing swap file.
- attention_message(buf, (char_u *)fname);
+ attention_message(buf, fname);
// We don't want a 'q' typed at the more-prompt
// interrupt loading a file.
@@ -3543,13 +3342,13 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
memcpy(name, sw_msg_1, sw_msg_1_len + 1);
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,
+ choice = do_dialog(VIM_WARNING, _("VIM - ATTENTION"),
+ name,
process_still_running
- ? (char_u *)_("&Open Read-Only\n&Edit anyway\n&Recover"
- "\n&Quit\n&Abort") :
- (char_u *)_("&Open Read-Only\n&Edit anyway\n&Recover"
- "\n&Delete it\n&Quit\n&Abort"),
+ ? _("&Open Read-Only\n&Edit anyway\n&Recover"
+ "\n&Quit\n&Abort") :
+ _("&Open Read-Only\n&Edit anyway\n&Recover"
+ "\n&Delete it\n&Quit\n&Abort"),
1, NULL, false);
if (process_still_running && choice >= 4) {
@@ -3564,7 +3363,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
if (choice > 0) {
switch (choice) {
case 1:
- buf->b_p_ro = TRUE;
+ buf->b_p_ro = true;
break;
case 2:
break;
@@ -3579,12 +3378,12 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
break;
case 6:
swap_exists_action = SEA_QUIT;
- got_int = TRUE;
+ got_int = true;
break;
}
// If the file was deleted this fname can be used.
- if (!os_path_exists((char_u *)fname)) {
+ if (!os_path_exists(fname)) {
break;
}
} else {
@@ -3598,25 +3397,23 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
}
}
- /*
- * Change the ".swp" extension to find another file that can be used.
- * First decrement the last char: ".swo", ".swn", etc.
- * If that still isn't enough decrement the last but one char: ".svz"
- * Can happen when editing many "No Name" buffers.
- */
+ // Change the ".swp" extension to find another file that can be used.
+ // First decrement the last char: ".swo", ".swn", etc.
+ // If that still isn't enough decrement the last but one char: ".svz"
+ // Can happen when editing many "No Name" buffers.
if (fname[n - 1] == 'a') { // ".s?a"
if (fname[n - 2] == 'a') { // ".saa": tried enough, give up
emsg(_("E326: Too many swap files found"));
XFREE_CLEAR(fname);
break;
}
- --fname[n - 2]; // ".svz", ".suz", etc.
+ fname[n - 2]--; // ".svz", ".suz", etc.
fname[n - 1] = 'z' + 1;
}
- --fname[n - 1]; // ".swo", ".swn", etc.
+ fname[n - 1]--; // ".swo", ".swn", etc.
}
- if (os_isdir((char_u *)dir_name)) {
+ if (os_isdir(dir_name)) {
*found_existing_dir = true;
} else if (!*found_existing_dir && **dirp == NUL) {
int ret;
@@ -3636,8 +3433,8 @@ 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 != B0_MAGIC_LONG
- || b0p->b0_magic_int != (int)B0_MAGIC_INT
- || b0p->b0_magic_short != (short)B0_MAGIC_SHORT
+ || b0p->b0_magic_int != B0_MAGIC_INT
+ || b0p->b0_magic_short != (int16_t)B0_MAGIC_SHORT
|| b0p->b0_magic_char != B0_MAGIC_CHAR;
}
@@ -3667,18 +3464,18 @@ static int b0_magic_wrong(ZERO_BL *b0p)
///
/// current file doesn't exist, file for swap file exist, file name(s) not
/// available -> probably different
-/// == 0 != 0 FAIL X TRUE
-/// == 0 != 0 X FAIL TRUE
+/// == 0 != 0 FAIL X true
+/// == 0 != 0 X FAIL true
///
/// current file exists, inode for swap unknown, file name(s) not
/// available -> probably different
-/// != 0 == 0 FAIL X TRUE
-/// != 0 == 0 X FAIL TRUE
+/// != 0 == 0 FAIL X true
+/// != 0 == 0 X FAIL true
///
/// current file doesn't exist, inode for swap unknown, one file name not
/// available -> probably different
-/// == 0 == 0 FAIL OK TRUE
-/// == 0 == 0 OK FAIL TRUE
+/// == 0 == 0 FAIL OK true
+/// == 0 == 0 OK FAIL true
///
/// current file doesn't exist, inode for swap unknown, both file names not
/// available -> compare file names
@@ -3689,26 +3486,24 @@ static int b0_magic_wrong(ZERO_BL *b0p)
///
/// @param fname_c current file name
/// @param fname_s file name from swap file
-static bool fnamecmp_ino(char_u *fname_c, char_u *fname_s, long ino_block0)
+static bool fnamecmp_ino(char *fname_c, char *fname_s, long ino_block0)
{
uint64_t ino_c = 0; // ino of current file
uint64_t ino_s; // ino of file from swap file
- char_u buf_c[MAXPATHL]; // full path of fname_c
- char_u buf_s[MAXPATHL]; // full path of fname_s
+ char buf_c[MAXPATHL]; // full path of fname_c
+ char buf_s[MAXPATHL]; // full path of fname_s
int retval_c; // flag: buf_c valid
int retval_s; // flag: buf_s valid
FileInfo file_info;
- if (os_fileinfo((char *)fname_c, &file_info)) {
+ if (os_fileinfo(fname_c, &file_info)) {
ino_c = os_fileinfo_inode(&file_info);
}
- /*
- * First we try to get the inode from the file name, because the inode in
- * the swap file may be outdated. If that fails (e.g. this path is not
- * valid on this machine), use the inode from block 0.
- */
- if (os_fileinfo((char *)fname_s, &file_info)) {
+ // First we try to get the inode from the file name, because the inode in
+ // the swap file may be outdated. If that fails (e.g. this path is not
+ // valid on this machine), use the inode from block 0.
+ if (os_fileinfo(fname_s, &file_info)) {
ino_s = os_fileinfo_inode(&file_info);
} else {
ino_s = (uint64_t)ino_block0;
@@ -3718,23 +3513,19 @@ static bool fnamecmp_ino(char_u *fname_c, char_u *fname_s, long ino_block0)
return ino_c != ino_s;
}
- /*
- * One of the inode numbers is unknown, try a forced vim_FullName() and
- * compare the file names.
- */
- retval_c = vim_FullName((char *)fname_c, (char *)buf_c, MAXPATHL, TRUE);
- retval_s = vim_FullName((char *)fname_s, (char *)buf_s, MAXPATHL, TRUE);
+ // One of the inode numbers is unknown, try a forced vim_FullName() and
+ // compare the file names.
+ retval_c = vim_FullName(fname_c, (char *)buf_c, MAXPATHL, true);
+ retval_s = vim_FullName(fname_s, (char *)buf_s, MAXPATHL, true);
if (retval_c == OK && retval_s == OK) {
- return STRCMP(buf_c, buf_s) != 0;
+ return strcmp(buf_c, buf_s) != 0;
}
- /*
- * Can't compare inodes or file names, guess that the files are different,
- * unless both appear not to exist at all, then compare with the file name
- * in the swap file.
- */
+ // Can't compare inodes or file names, guess that the files are different,
+ // unless both appear not to exist at all, then compare with the file name
+ // in the swap file.
if (ino_s == 0 && ino_c == 0 && retval_c == FAIL && retval_s == FAIL) {
- return STRCMP(fname_c, fname_s) != 0;
+ return strcmp(fname_c, fname_s) != 0;
}
return true;
}
@@ -3752,7 +3543,7 @@ static void long_to_char(long n, char_u *s)
s[3] = (char_u)(n & 0xff);
}
-static long char_to_long(char_u *s)
+static long char_to_long(const char_u *s)
{
long retval;
@@ -3783,7 +3574,7 @@ void ml_setflags(buf_T *buf)
if (hp->bh_bnum == 0) {
b0p = hp->bh_data;
b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0;
- b0p->b0_flags = (uint8_t)((b0p->b0_flags & ~B0_FF_MASK) | (uint8_t)(get_fileformat(buf) + 1));
+ b0p->b0_flags = (char)((b0p->b0_flags & ~B0_FF_MASK) | (uint8_t)(get_fileformat(buf) + 1));
add_b0_fenc(b0p, buf);
hp->bh_flags |= BH_DIRTY;
mf_sync(buf->b_ml.ml_mfp, MFS_ZERO);
@@ -3792,8 +3583,10 @@ 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
+enum {
+ MLCS_MAXL = 800, // max no of lines in chunk
+ MLCS_MINL = 400, // should be half of MLCS_MAXL
+};
/// Keep information for finding byte offset of a line
///
@@ -3829,20 +3622,16 @@ static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype)
}
if (updtype == ML_CHNK_UPDLINE && buf->b_ml.ml_line_count == 1) {
- /*
- * First line in empty buffer from ml_flush_line() -- reset
- */
+ // First line in empty buffer from ml_flush_line() -- reset
buf->b_ml.ml_usedchunks = 1;
buf->b_ml.ml_chunksize[0].mlcs_numlines = 1;
buf->b_ml.ml_chunksize[0].mlcs_totalsize =
- (long)STRLEN(buf->b_ml.ml_line_ptr) + 1;
+ (long)strlen(buf->b_ml.ml_line_ptr) + 1;
return;
}
- /*
- * Find chunk that our line belongs to, curline will be at start of the
- * chunk.
- */
+ // Find chunk that our line belongs to, curline will be at start of the
+ // chunk.
if (buf != ml_upd_lastbuf || line != ml_upd_lastline + 1
|| updtype != ML_CHNK_ADDLINE) {
for (curline = 1, curix = 0;
@@ -3930,10 +3719,8 @@ static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype)
curchnk->mlcs_numlines = 0;
curchnk->mlcs_totalsize = 0;
} else {
- /*
- * Line is just prior to last, move count for last
- * This is the common case when loading a new file
- */
+ // Line is just prior to last, move count for last
+ // This is the common case when loading a new file
hp = ml_find_line(buf, buf->b_ml.ml_line_count, ML_FIND);
if (hp == NULL) {
buf->b_ml.ml_usedchunks = -1;
@@ -4046,10 +3833,8 @@ long ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp, bool no_ff)
if (lnum == 0 && offset <= 0) {
return 1; // Not a "find offset" and offset 0 _must_ be in line 1
}
- /*
- * Find the last chunk before the one containing our line. Last chunk is
- * special because it will never qualify
- */
+ // Find the last chunk before the one containing our line. Last chunk is
+ // special because it will never qualify
curline = 1;
curix = 0;
size = 0;
@@ -4163,7 +3948,7 @@ void goto_byte(long cnt)
curwin->w_cursor.lnum = lnum;
curwin->w_cursor.col = (colnr_T)boff;
curwin->w_cursor.coladd = 0;
- curwin->w_set_curswant = TRUE;
+ curwin->w_set_curswant = true;
}
check_cursor();
@@ -4181,7 +3966,7 @@ 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);
+ const char *const p = ml_get_pos(lp);
if (*p != NUL) { // still within line, move to next char (may be NUL)
const int l = utfc_ptr2len((char *)p);
@@ -4214,8 +3999,8 @@ int dec(pos_T *lp)
lp->coladd = 0;
if (lp->col == MAXCOL) {
// past end of line
- char_u *p = ml_get(lp->lnum);
- lp->col = (colnr_T)STRLEN(p);
+ char *p = ml_get(lp->lnum);
+ lp->col = (colnr_T)strlen(p);
lp->col -= utf_head_off(p, p + lp->col);
return 0;
}
@@ -4223,15 +4008,15 @@ int dec(pos_T *lp)
if (lp->col > 0) {
// still within line
lp->col--;
- char_u *p = ml_get(lp->lnum);
+ char *p = ml_get(lp->lnum);
lp->col -= utf_head_off(p, p + lp->col);
return 0;
}
if (lp->lnum > 1) {
// there is a prior line
lp->lnum--;
- char_u *p = ml_get(lp->lnum);
- lp->col = (colnr_T)STRLEN(p);
+ char *p = ml_get(lp->lnum);
+ lp->col = (colnr_T)strlen(p);
lp->col -= utf_head_off(p, p + lp->col);
return 1;
}
diff --git a/src/nvim/memline.h b/src/nvim/memline.h
index 441adf3e87..f4190f0210 100644
--- a/src/nvim/memline.h
+++ b/src/nvim/memline.h
@@ -1,8 +1,8 @@
#ifndef NVIM_MEMLINE_H
#define NVIM_MEMLINE_H
-#include "nvim/buffer_defs.h" // for buf_T
-#include "nvim/pos.h" // for pos_T, linenr_T, colnr_T
+#include "nvim/buffer_defs.h"
+#include "nvim/pos.h"
#include "nvim/types.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/memline_defs.h b/src/nvim/memline_defs.h
index 922a2c98d1..6bb9255909 100644
--- a/src/nvim/memline_defs.h
+++ b/src/nvim/memline_defs.h
@@ -56,7 +56,7 @@ typedef struct memline {
int ml_flags;
linenr_T ml_line_lnum; // line number of cached line, 0 if not valid
- char_u *ml_line_ptr; // pointer to cached line
+ char *ml_line_ptr; // pointer to cached line
size_t ml_line_offset; // cached byte offset of ml_line_lnum
int ml_line_offset_ff; // fileformat of cached line
@@ -69,4 +69,4 @@ typedef struct memline {
int ml_usedchunks;
} memline_T;
-#endif // NVIM_MEMLINE_DEFS_H
+#endif // NVIM_MEMLINE_DEFS_H
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index fb36d4ccf4..5356300382 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -6,23 +6,32 @@
#include <assert.h>
#include <inttypes.h>
#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
#include <string.h>
+#include <time.h>
#include "nvim/api/extmark.h"
#include "nvim/arglist.h"
+#include "nvim/ascii.h"
+#include "nvim/buffer_updates.h"
#include "nvim/context.h"
#include "nvim/decoration_provider.h"
#include "nvim/eval.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
#include "nvim/insexpand.h"
#include "nvim/lua/executor.h"
+#include "nvim/main.h"
#include "nvim/mapping.h"
#include "nvim/memfile.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/sign.h"
#include "nvim/ui.h"
+#include "nvim/usercmd.h"
#include "nvim/vim.h"
#ifdef UNIT_TESTING
@@ -60,6 +69,8 @@ void try_to_free_memory(void)
// Try to save all buffers and release as many blocks as possible
mf_release_all();
+ arena_free_reuse_blks();
+
trying_to_free = false;
}
@@ -110,8 +121,8 @@ void *xmalloc(size_t size)
{
void *ret = try_malloc(size);
if (!ret) {
- mch_errmsg(e_outofmem);
- mch_errmsg("\n");
+ os_errmsg(e_outofmem);
+ os_errmsg("\n");
preserve_exit();
}
return ret;
@@ -141,8 +152,8 @@ void *xcalloc(size_t count, size_t size)
try_to_free_memory();
ret = calloc(allocated_count, allocated_size);
if (!ret) {
- mch_errmsg(e_outofmem);
- mch_errmsg("\n");
+ os_errmsg(e_outofmem);
+ os_errmsg("\n");
preserve_exit();
}
}
@@ -163,8 +174,8 @@ void *xrealloc(void *ptr, size_t size)
try_to_free_memory();
ret = realloc(ptr, allocated_size);
if (!ret) {
- mch_errmsg(e_outofmem);
- mch_errmsg("\n");
+ os_errmsg(e_outofmem);
+ os_errmsg("\n");
preserve_exit();
}
}
@@ -183,7 +194,7 @@ void *xmallocz(size_t size)
{
size_t total_size = size + 1;
if (total_size < size) {
- mch_errmsg(_("Vim: Data too large to fit into virtual memory space\n"));
+ os_errmsg(_("Vim: Data too large to fit into virtual memory space\n"));
preserve_exit();
}
@@ -435,9 +446,8 @@ char *xstrdupnul(const char *const str)
{
if (str == NULL) {
return xmallocz(0);
- } else {
- return xstrdup(str);
}
+ return xstrdup(str);
}
/// A version of memchr that starts the search at `src + len`.
@@ -499,22 +509,22 @@ bool striequal(const char *a, const char *b)
return (a == NULL && b == NULL) || (a && b && STRICMP(a, b) == 0);
}
-/*
- * Avoid repeating the error message many times (they take 1 second each).
- * Did_outofmem_msg is reset when a character is read.
- */
+// Avoid repeating the error message many times (they take 1 second each).
+// Did_outofmem_msg is reset when a character is read.
void do_outofmem_msg(size_t size)
{
- if (!did_outofmem_msg) {
- // Don't hide this message
- emsg_silent = 0;
+ if (did_outofmem_msg) {
+ return;
+ }
- /* Must come first to avoid coming back here when printing the error
- * message fails, e.g. when setting v:errmsg. */
- did_outofmem_msg = true;
+ // Don't hide this message
+ emsg_silent = 0;
- semsg(_("E342: Out of memory! (allocating %" PRIu64 " bytes)"), (uint64_t)size);
- }
+ // Must come first to avoid coming back here when printing the error
+ // message fails, e.g. when setting v:errmsg.
+ did_outofmem_msg = true;
+
+ semsg(_("E342: Out of memory! (allocating %" PRIu64 " bytes)"), (uint64_t)size);
}
/// Writes time_t to "buf[8]".
@@ -528,24 +538,22 @@ void time_to_bytes(time_t time_, uint8_t buf[8])
}
#define ARENA_BLOCK_SIZE 4096
+#define REUSE_MAX 4
-void arena_start(Arena *arena, ArenaMem *reuse_blk)
+static struct consumed_blk *arena_reuse_blk;
+static size_t arena_reuse_blk_count = 0;
+
+static void arena_free_reuse_blks(void)
{
- if (reuse_blk && *reuse_blk) {
- arena->cur_blk = (char *)(*reuse_blk);
- *reuse_blk = NULL;
- } else {
- arena->cur_blk = xmalloc(ARENA_BLOCK_SIZE);
+ while (arena_reuse_blk_count > 0) {
+ struct consumed_blk *blk = arena_reuse_blk;
+ arena_reuse_blk = arena_reuse_blk->prev;
+ xfree(blk);
+ arena_reuse_blk_count--;
}
- 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.
+/// Finish the allocations in an arena.
///
/// This does not immediately free the memory, but leaves existing allocated
/// objects valid, and returns an opaque ArenaMem handle, which can be used to
@@ -558,47 +566,79 @@ ArenaMem arena_finish(Arena *arena)
return res;
}
+void alloc_block(Arena *arena)
+{
+ struct consumed_blk *prev_blk = (struct consumed_blk *)arena->cur_blk;
+ if (arena_reuse_blk_count > 0) {
+ arena->cur_blk = (char *)arena_reuse_blk;
+ arena_reuse_blk = arena_reuse_blk->prev;
+ arena_reuse_blk_count--;
+ } else {
+ arena_alloc_count++;
+ 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;
+}
+
+static size_t arena_align_offset(uint64_t off)
+{
+ return ((off + (ARENA_ALIGN - 1)) & ~(ARENA_ALIGN - 1));
+}
+
+/// @param arena if NULL, do a global allocation. caller must then free the value!
+/// @param size if zero, will still return a non-null pointer, but not a usable or unique one
void *arena_alloc(Arena *arena, size_t size, bool align)
{
- if (align) {
- arena->pos = (arena->pos + (ARENA_ALIGN - 1)) & ~(ARENA_ALIGN - 1);
+ if (!arena) {
+ return xmalloc(size);
}
- if (arena->pos + size > arena->size) {
- if (size > (arena->size - sizeof(struct consumed_blk)) >> 1) {
+ if (!arena->cur_blk) {
+ alloc_block(arena);
+ }
+ size_t alloc_pos = align ? arena_align_offset(arena->pos) : arena->pos;
+ if (alloc_pos + size > arena->size) {
+ if (size > (ARENA_BLOCK_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));
+ arena_alloc_count++;
+ size_t hdr_size = sizeof(struct consumed_blk);
+ size_t aligned_hdr_size = (align ? arena_align_offset(hdr_size) : hdr_size);
+ char *alloc = xmalloc(size + aligned_hdr_size);
+
+ // to simplify free-list management, arena->cur_blk must
+ // always be a normal, ARENA_BLOCK_SIZE sized, block
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));
+ return alloc + aligned_hdr_size;
} 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;
+ alloc_block(arena); // resets arena->pos
+ alloc_pos = align ? arena_align_offset(arena->pos) : arena->pos;
}
}
- char *mem = arena->cur_blk + arena->pos;
- arena->pos += size;
+ char *mem = arena->cur_blk + alloc_pos;
+ arena->pos = alloc_pos + size;
return mem;
}
-void arena_mem_free(ArenaMem mem, ArenaMem *reuse_blk)
+void arena_mem_free(ArenaMem mem)
{
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;
+ if (arena_reuse_blk_count < REUSE_MAX && b != NULL) {
+ struct consumed_blk *reuse_blk = b;
b = b->prev;
- (*reuse_blk)->prev = NULL;
+ reuse_blk->prev = arena_reuse_blk;
+ arena_reuse_blk = reuse_blk;
+ arena_reuse_blk_count++;
}
while (b) {
@@ -620,7 +660,6 @@ char *arena_memdupz(Arena *arena, const char *buf, size_t size)
# include "nvim/autocmd.h"
# include "nvim/buffer.h"
-# include "nvim/charset.h"
# include "nvim/cmdhist.h"
# include "nvim/diff.h"
# include "nvim/edit.h"
@@ -629,33 +668,24 @@ char *arena_memdupz(Arena *arena, const char *buf, size_t size)
# include "nvim/ex_docmd.h"
# include "nvim/ex_getln.h"
# include "nvim/file_search.h"
-# include "nvim/fold.h"
# include "nvim/getchar.h"
# include "nvim/grid.h"
# include "nvim/mark.h"
-# include "nvim/mbyte.h"
-# include "nvim/memline.h"
-# include "nvim/move.h"
# include "nvim/ops.h"
# include "nvim/option.h"
# include "nvim/os/os.h"
-# include "nvim/os_unix.h"
-# include "nvim/path.h"
# include "nvim/quickfix.h"
# include "nvim/regexp.h"
# include "nvim/search.h"
# include "nvim/spell.h"
-# include "nvim/syntax.h"
# include "nvim/tag.h"
# include "nvim/window.h"
-/*
- * Free everything that we allocated.
- * Can be used to detect memory leaks, e.g., with ccmalloc.
- * NOTE: This is tricky! Things are freed that functions depend on. Don't be
- * surprised if Vim crashes...
- * Some things can't be freed, esp. things local to a library function.
- */
+// Free everything that we allocated.
+// Can be used to detect memory leaks, e.g., with ccmalloc.
+// NOTE: This is tricky! Things are freed that functions depend on. Don't be
+// surprised if Vim crashes...
+// Some things can't be freed, esp. things local to a library function.
void free_all_mem(void)
{
buf_T *buf, *nextbuf;
@@ -782,6 +812,11 @@ void free_all_mem(void)
bufref_T bufref;
set_bufref(&bufref, buf);
nextbuf = buf->b_next;
+
+ // Since options (in addition to other stuff) have been freed above we need to ensure no
+ // callbacks are called, so free them before closing the buffer.
+ buf_free_callbacks(buf);
+
close_buffer(NULL, buf, DOBUF_WIPE, false, false);
// Didn't work, try next one.
buf = bufref_valid(&bufref) ? nextbuf : firstbuf;
@@ -797,8 +832,11 @@ void free_all_mem(void)
decor_free_all_mem();
- nlua_free_all_mem();
ui_free_all_mem();
+ nlua_free_all_mem();
+
+ // should be last, in case earlier free functions deallocates arenas
+ arena_free_reuse_blks();
}
#endif
diff --git a/src/nvim/memory.h b/src/nvim/memory.h
index 63d607c2ce..5b9798dc0d 100644
--- a/src/nvim/memory.h
+++ b/src/nvim/memory.h
@@ -1,10 +1,12 @@
#ifndef NVIM_MEMORY_H
#define NVIM_MEMORY_H
-#include <stdbool.h> // for bool
-#include <stddef.h> // for size_t
-#include <stdint.h> // for uint8_t
-#include <time.h> // for time_t
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <time.h>
+
+#include "nvim/macros.h"
/// `malloc()` function signature
typedef void *(*MemMalloc)(size_t);
@@ -37,18 +39,20 @@ extern MemRealloc mem_realloc;
extern bool entered_free_all_mem;
#endif
+EXTERN size_t arena_alloc_count INIT(= 0);
+
typedef struct consumed_blk {
struct consumed_blk *prev;
} *ArenaMem;
-#define ARENA_ALIGN sizeof(void *)
+#define ARENA_ALIGN MAX(sizeof(void *), sizeof(double))
typedef struct {
char *cur_blk;
size_t pos, size;
} Arena;
-// inits an empty arena. use arena_start() to actually allocate space!
+// inits an empty arena.
#define ARENA_EMPTY { .cur_blk = NULL, .pos = 0, .size = 0 }
#define kv_fixsize_arena(a, v, s) \
diff --git a/src/nvim/menu.c b/src/nvim/menu.c
index c3cf4457fc..2a18b08d8d 100644
--- a/src/nvim/menu.c
+++ b/src/nvim/menu.c
@@ -1,35 +1,45 @@
// 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
-/*
- * Code for menus. Used for the GUI and 'wildmenu'.
- * GUI/Motif support by Robert Webb
- */
+// Code for menus. Used for the GUI and 'wildmenu'.
+// GUI/Motif support by Robert Webb
#include <assert.h>
-#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
#include <string.h>
#include "nvim/ascii.h"
#include "nvim/autocmd.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/highlight_defs.h"
#include "nvim/keycodes.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/menu.h"
+#include "nvim/menu_defs.h"
#include "nvim/message.h"
+#include "nvim/option_defs.h"
#include "nvim/popupmenu.h"
-#include "nvim/screen.h"
+#include "nvim/pos.h"
#include "nvim/state.h"
#include "nvim/strings.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
+#include "nvim/undo_defs.h"
#include "nvim/vim.h"
-#include "nvim/window.h"
#define MENUDEPTH 10 // maximum depth of menus
@@ -47,7 +57,7 @@ static char e_nomenu[] = N_("E329: No menu \"%s\"");
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);
+ return (strncmp(name, "WinBar", 6) == 0);
}
static vimmenu_T **get_root_menu(const char *const name)
@@ -79,17 +89,17 @@ void ex_menu(exarg_T *eap)
arg = eap->arg;
for (;;) {
- if (STRNCMP(arg, "<script>", 8) == 0) {
+ if (strncmp(arg, "<script>", 8) == 0) {
noremap = REMAP_SCRIPT;
arg = skipwhite(arg + 8);
continue;
}
- if (STRNCMP(arg, "<silent>", 8) == 0) {
+ if (strncmp(arg, "<silent>", 8) == 0) {
silent = true;
arg = skipwhite(arg + 8);
continue;
}
- if (STRNCMP(arg, "<special>", 9) == 0) {
+ if (strncmp(arg, "<special>", 9) == 0) {
// Ignore obsolete "<special>" modifier.
arg = skipwhite(arg + 9);
continue;
@@ -99,7 +109,7 @@ void ex_menu(exarg_T *eap)
// Locate an optional "icon=filename" argument
// TODO(nvim): Currently this is only parsed. Should expose it to UIs.
- if (STRNCMP(arg, "icon=", 5) == 0) {
+ if (strncmp(arg, "icon=", 5) == 0) {
arg += 5;
while (*arg != NUL && *arg != ' ') {
if (*arg == '\\') {
@@ -141,20 +151,16 @@ void ex_menu(exarg_T *eap)
}
pri_tab[MENUDEPTH] = -1; // mark end of the table
- /*
- * Check for "disable" or "enable" argument.
- */
- if (STRNCMP(arg, "enable", 6) == 0 && ascii_iswhite(arg[6])) {
+ // Check for "disable" or "enable" argument.
+ if (strncmp(arg, "enable", 6) == 0 && ascii_iswhite(arg[6])) {
enable = kTrue;
arg = skipwhite(arg + 6);
- } else if (STRNCMP(arg, "disable", 7) == 0 && ascii_iswhite(arg[7])) {
+ } else if (strncmp(arg, "disable", 7) == 0 && ascii_iswhite(arg[7])) {
enable = kFalse;
arg = skipwhite(arg + 7);
}
- /*
- * If there is no argument, display all menus.
- */
+ // If there is no argument, display all menus.
if (*arg == NUL) {
show_menus(arg, modes);
return;
@@ -168,9 +174,7 @@ void ex_menu(exarg_T *eap)
map_to = menu_translate_tab_and_shift(arg);
- /*
- * If there is only a menu name, display menus with that name.
- */
+ // If there is only a menu name, display menus with that name.
if (*map_to == NUL && !unmenu && enable == kNone) {
show_menus(menu_path, modes);
goto theend;
@@ -185,7 +189,7 @@ void ex_menu(exarg_T *eap)
// Change sensitivity of the menu.
// For the PopUp menu, remove a menu for each mode separately.
// Careful: menu_enable_recurse() changes menu_path.
- if (STRCMP(menu_path, "*") == 0) { // meaning: do all menus
+ if (strcmp(menu_path, "*") == 0) { // meaning: do all menus
menu_path = "";
}
@@ -200,16 +204,12 @@ void ex_menu(exarg_T *eap)
}
menu_enable_recurse(*root_menu_ptr, menu_path, modes, enable);
} else if (unmenu) {
- /*
- * Delete menu(s).
- */
- if (STRCMP(menu_path, "*") == 0) { // meaning: remove all menus
+ // Delete menu(s).
+ if (strcmp(menu_path, "*") == 0) { // meaning: remove all menus
menu_path = "";
}
- /*
- * For the PopUp menu, remove a menu for each mode separately.
- */
+ // For the PopUp menu, remove a menu for each mode separately.
if (menu_is_popup(menu_path)) {
for (i = 0; i < MENU_INDEX_TIP; i++) {
if (modes & (1 << i)) {
@@ -223,10 +223,8 @@ void ex_menu(exarg_T *eap)
// Careful: remove_menu() changes menu_path
remove_menu(root_menu_ptr, menu_path, modes, false);
} else {
- /*
- * Add menu(s).
- * Replace special key codes.
- */
+ // Add menu(s).
+ // Replace special key codes.
if (STRICMP(map_to, "<nop>") == 0) { // "<Nop>" means nothing
map_to = "";
map_buf = NULL;
@@ -234,7 +232,7 @@ void ex_menu(exarg_T *eap)
map_buf = NULL; // Menu tips are plain text.
} else {
map_buf = NULL;
- map_to = replace_termcodes(map_to, STRLEN(map_to), &map_buf,
+ map_to = replace_termcodes(map_to, strlen(map_to), &map_buf,
REPTERM_DO_LT, NULL, CPO_TO_CPO_FLAGS);
}
menuarg.modes = modes;
@@ -242,9 +240,7 @@ void ex_menu(exarg_T *eap)
menuarg.silent[0] = silent;
add_menu_path(menu_path, &menuarg, pri_tab, map_to);
- /*
- * For the PopUp menu, add a menu for each mode separately.
- */
+ // For the PopUp menu, add a menu for each mode separately.
if (menu_is_popup(menu_path)) {
for (i = 0; i < MENU_INDEX_TIP; i++) {
if (modes & (1 << i)) {
@@ -302,7 +298,7 @@ static int add_menu_path(const char *const menu_path, vimmenu_T *menuarg, const
// Get name of this element in the menu hierarchy, and the simplified
// name (without mnemonic and accelerator text).
next_name = menu_name_skip(name);
- map_to = menutrans_lookup(name, (int)STRLEN(name));
+ map_to = menutrans_lookup(name, (int)strlen(name));
if (map_to != NULL) {
en_name = name;
name = map_to;
@@ -384,11 +380,9 @@ static int add_menu_path(const char *const menu_path, vimmenu_T *menuarg, const
} else {
old_modes = menu->modes;
- /*
- * If this menu option was previously only available in other
- * modes, then make sure it's available for this one now
- * Also enable a menu when it's created or changed.
- */
+ // If this menu option was previously only available in other
+ // modes, then make sure it's available for this one now
+ // Also enable a menu when it's created or changed.
{
menu->modes |= modes;
menu->enabled |= modes;
@@ -405,10 +399,8 @@ static int add_menu_path(const char *const menu_path, vimmenu_T *menuarg, const
}
xfree(path_name);
- /*
- * Only add system menu items which have not been defined yet.
- * First check if this was an ":amenu".
- */
+ // Only add system menu items which have not been defined yet.
+ // First check if this was an ":amenu".
amenu = ((modes & (MENU_NORMAL_MODE | MENU_INSERT_MODE)) ==
(MENU_NORMAL_MODE | MENU_INSERT_MODE));
if (sys_menu) {
@@ -419,7 +411,7 @@ static int add_menu_path(const char *const menu_path, vimmenu_T *menuarg, const
p = (call_data == NULL) ? NULL : xstrdup(call_data);
// loop over all modes, may add more than one
- for (i = 0; i < MENU_MODES; ++i) {
+ for (i = 0; i < MENU_MODES; i++) {
if (modes & (1 << i)) {
// free any old menu
free_menu_string(menu, i);
@@ -444,7 +436,7 @@ static int add_menu_path(const char *const menu_path, vimmenu_T *menuarg, const
}
if (c != 0) {
- menu->strings[i] = xmalloc(STRLEN(call_data) + 5);
+ menu->strings[i] = xmalloc(strlen(call_data) + 5);
menu->strings[i][0] = c;
if (d == 0) {
STRCPY(menu->strings[i] + 1, call_data);
@@ -453,7 +445,7 @@ static int add_menu_path(const char *const menu_path, vimmenu_T *menuarg, const
STRCPY(menu->strings[i] + 2, call_data);
}
if (c == Ctrl_C) {
- int len = (int)STRLEN(menu->strings[i]);
+ int len = (int)strlen(menu->strings[i]);
menu->strings[i][len] = Ctrl_BSL;
menu->strings[i][len + 1] = Ctrl_G;
@@ -491,10 +483,8 @@ erret:
return FAIL;
}
-/*
- * Set the (sub)menu with the given name to enabled or disabled.
- * Called recursively.
- */
+// Set the (sub)menu with the given name to enabled or disabled.
+// Called recursively.
static int menu_enable_recurse(vimmenu_T *menu, char *name, int modes, int enable)
{
char *p;
@@ -522,11 +512,9 @@ static int menu_enable_recurse(vimmenu_T *menu, char *name, int modes, int enabl
menu->enabled &= ~modes;
}
- /*
- * When name is empty, we are doing all menu items for the given
- * modes, so keep looping, otherwise we are just doing the named
- * menu item (which has been found) so break here.
- */
+ // When name is empty, we are doing all menu items for the given
+ // modes, so keep looping, otherwise we are just doing the named
+ // menu item (which has been found) so break here.
if (*name != NUL && *name != '*') {
break;
}
@@ -577,11 +565,9 @@ static int remove_menu(vimmenu_T **menup, char *name, int modes, bool silent)
return FAIL;
}
- /*
- * When name is empty, we are removing all menu items for the given
- * modes, so keep looping, otherwise we are just removing the named
- * menu item (which has been found) so break here.
- */
+ // When name is empty, we are removing all menu items for the given
+ // modes, so keep looping, otherwise we are just removing the named
+ // menu item (which has been found) so break here.
if (*name != NUL) {
break;
}
@@ -628,9 +614,7 @@ static int remove_menu(vimmenu_T **menup, char *name, int modes, bool silent)
return OK;
}
-/*
- * Free the given menu structure and remove it from the linked list.
- */
+// Free the given menu structure and remove it from the linked list.
static void free_menu(vimmenu_T **menup)
{
int i;
@@ -652,9 +636,7 @@ static void free_menu(vimmenu_T **menup)
xfree(menu);
}
-/*
- * Free the menu->string with the given index.
- */
+// Free the menu->string with the given index.
static void free_menu_string(vimmenu_T *menu, int idx)
{
int count = 0;
@@ -715,8 +697,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(menu->strings[bit],
- false, false));
+ 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"),
(menu->enabled & (1 << bit)) ? 1 : 0);
@@ -853,7 +834,7 @@ static void show_menus_recursive(vimmenu_T *menu, int modes, int depth)
msg_puts(" ");
}
// Same highlighting as for directories!?
- msg_outtrans_attr((char_u *)menu->name, HL_ATTR(HLF_D));
+ msg_outtrans_attr(menu->name, HL_ATTR(HLF_D));
}
if (menu != NULL && menu->children == NULL) {
@@ -888,7 +869,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((char_u *)menu->strings[bit], false, 0);
+ msg_outtrans_special(menu->strings[bit], false, 0);
}
}
}
@@ -909,16 +890,12 @@ static void show_menus_recursive(vimmenu_T *menu, int modes, int depth)
}
}
-/*
- * Used when expanding menu names.
- */
+// Used when expanding menu names.
static vimmenu_T *expand_menu = NULL;
static int expand_modes = 0x0;
-static int expand_emenu; // TRUE for ":emenu" command
+static int expand_emenu; // true for ":emenu" command
-/*
- * Work out what to complete when doing command line completion of menu names.
- */
+// Work out what to complete when doing command line completion of menu names.
char *set_context_in_menu_cmd(expand_T *xp, const char *cmd, char *arg, bool forceit)
FUNC_ATTR_NONNULL_ALL
{
@@ -933,17 +910,17 @@ char *set_context_in_menu_cmd(expand_T *xp, const char *cmd, char *arg, bool for
xp->xp_context = EXPAND_UNSUCCESSFUL;
// Check for priority numbers, enable and disable
- for (p = arg; *p; ++p) {
+ for (p = arg; *p; p++) {
if (!ascii_isdigit(*p) && *p != '.') {
break;
}
}
if (!ascii_iswhite(*p)) {
- if (STRNCMP(arg, "enable", 6) == 0
+ if (strncmp(arg, "enable", 6) == 0
&& (arg[6] == NUL || ascii_iswhite(arg[6]))) {
p = arg + 6;
- } else if (STRNCMP(arg, "disable", 7) == 0
+ } else if (strncmp(arg, "disable", 7) == 0
&& (arg[7] == NUL || ascii_iswhite(arg[7]))) {
p = arg + 7;
} else {
@@ -957,7 +934,7 @@ char *set_context_in_menu_cmd(expand_T *xp, const char *cmd, char *arg, bool for
arg = after_dot = p;
- for (; *p && !ascii_iswhite(*p); ++p) {
+ for (; *p && !ascii_iswhite(*p); p++) {
if ((*p == '\\' || *p == Ctrl_V) && p[1] != NUL) {
p++;
} else if (*p == '.') {
@@ -984,7 +961,7 @@ char *set_context_in_menu_cmd(expand_T *xp, const char *cmd, char *arg, bool for
if (after_dot > arg) {
size_t path_len = (size_t)(after_dot - arg);
path_name = xmalloc(path_len);
- STRLCPY(path_name, arg, path_len);
+ xstrlcpy(path_name, arg, path_len);
}
name = path_name;
while (name != NULL && *name) {
@@ -994,10 +971,8 @@ char *set_context_in_menu_cmd(expand_T *xp, const char *cmd, char *arg, bool for
// Found menu
if ((*p != NUL && menu->children == NULL)
|| ((menu->modes & expand_modes) == 0x0)) {
- /*
- * Menu path continues, but we have reached a leaf.
- * Or menu exists only in another mode.
- */
+ // Menu path continues, but we have reached a leaf.
+ // Or menu exists only in another mode.
xfree(path_name);
return NULL;
}
@@ -1024,10 +999,8 @@ char *set_context_in_menu_cmd(expand_T *xp, const char *cmd, char *arg, bool for
return NULL;
}
-/*
- * Function given to ExpandGeneric() to obtain the list of (sub)menus (not
- * entries).
- */
+// Function given to ExpandGeneric() to obtain the list of (sub)menus (not
+// entries).
char *get_menu_name(expand_T *xp, int idx)
{
static vimmenu_T *menu = NULL;
@@ -1056,7 +1029,7 @@ char *get_menu_name(expand_T *xp, int idx)
} else {
str = menu->dname;
if (menu->en_dname == NULL) {
- should_advance = TRUE;
+ should_advance = true;
}
}
} else {
@@ -1073,10 +1046,8 @@ char *get_menu_name(expand_T *xp, int idx)
return str;
}
-/*
- * Function given to ExpandGeneric() to obtain the list of menus and menu
- * entries.
- */
+// Function given to ExpandGeneric() to obtain the list of menus and menu
+// entries.
char *get_menu_names(expand_T *xp, int idx)
{
static vimmenu_T *menu = NULL;
@@ -1094,7 +1065,7 @@ char *get_menu_names(expand_T *xp, int idx)
while (menu != NULL
&& (menu_is_hidden(menu->dname)
|| (expand_emenu && menu_is_separator(menu->dname))
- || menu->dname[STRLEN(menu->dname) - 1] == '.')) {
+ || menu->dname[strlen(menu->dname) - 1] == '.')) {
menu = menu->next;
}
@@ -1105,9 +1076,9 @@ char *get_menu_names(expand_T *xp, int idx)
if (menu->modes & expand_modes) {
if (menu->children != NULL) {
if (should_advance) {
- STRLCPY(tbuffer, menu->en_dname, TBUFFER_LEN);
+ xstrlcpy(tbuffer, menu->en_dname, TBUFFER_LEN);
} else {
- STRLCPY(tbuffer, menu->dname, TBUFFER_LEN);
+ xstrlcpy(tbuffer, menu->dname, TBUFFER_LEN);
if (menu->en_dname == NULL) {
should_advance = true;
}
@@ -1163,10 +1134,8 @@ char *menu_name_skip(char *const name)
return p;
}
-/*
- * 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.
- */
+/// 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 *const name, const vimmenu_T *const menu)
{
if (menu->en_name != NULL
@@ -1181,7 +1150,7 @@ static bool menu_namecmp(const char *const name, const char *const mname)
{
int i;
- for (i = 0; name[i] != NUL && name[i] != TAB; ++i) {
+ for (i = 0; name[i] != NUL && name[i] != TAB; i++) {
if (name[i] != mname[i]) {
break;
}
@@ -1314,13 +1283,11 @@ static char *get_menu_mode_str(int modes)
return "";
}
-/*
- * Modify a menu name starting with "PopUp" to include the mode character.
- * Returns the name in allocated memory.
- */
+// Modify a menu name starting with "PopUp" to include the mode character.
+// Returns the name in allocated memory.
static char *popup_mode_name(char *name, int idx)
{
- size_t len = STRLEN(name);
+ size_t len = strlen(name);
assert(len >= 4);
char *mode_chars = menu_mode_chars[idx];
@@ -1372,7 +1339,7 @@ static char *menu_text(const char *str, int *mnemonic, char **actext)
break;
}
if (mnemonic != NULL && p[1] != '&') {
- *mnemonic = (char_u)p[1];
+ *mnemonic = (uint8_t)p[1];
}
STRMOVE(p, p + 1);
p = p + 1;
@@ -1395,23 +1362,21 @@ bool menu_is_menubar(const char *const name)
bool menu_is_popup(const char *const name)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
- return STRNCMP(name, "PopUp", 5) == 0;
+ return strncmp(name, "PopUp", 5) == 0;
}
// Return true if "name" is a toolbar menu 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;
+ return strncmp(name, "ToolBar", 7) == 0;
}
-/*
- * Return TRUE if the name is a menu separator identifier: Starts and ends
- * with '-'
- */
+/// Return true if the name is a menu separator identifier: Starts and ends
+/// with '-'
int menu_is_separator(char *name)
{
- return name[0] == '-' && name[STRLEN(name) - 1] == '-';
+ return name[0] == '-' && name[strlen(name) - 1] == '-';
}
/// True if a popup menu or starts with \ref MNU_HIDDEN_CHAR
@@ -1479,15 +1444,17 @@ void show_popupmenu(void)
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) {
+ 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);
+ if (menu == NULL || menu->children == NULL) {
+ return;
}
+
+ pum_show_popupmenu(menu);
}
/// Execute "menu". Use by ":emenu" and the window toolbar.
@@ -1567,7 +1534,7 @@ void execute_menu(const exarg_T *eap, vimmenu_T *menu, int mode_idx)
ex_normal_busy++;
if (save_current_state(&save_state)) {
- exec_normal_cmd((char_u *)menu->strings[idx], menu->noremap[idx],
+ exec_normal_cmd(menu->strings[idx], menu->noremap[idx],
menu->silent[idx]);
}
restore_current_state(&save_state);
@@ -1734,9 +1701,7 @@ theend:
return menu;
}
-/*
- * Translation of menu names. Just a simple lookup table.
- */
+// Translation of menu names. Just a simple lookup table.
typedef struct {
char *from; // English name
@@ -1752,11 +1717,9 @@ static garray_T menutrans_ga = GA_EMPTY_INIT_VALUE;
xfree(_mt->from_noamp); \
xfree(_mt->to)
-/*
- * ":menutrans".
- * This function is also defined without the +multi_lang feature, in which
- * case the commands are ignored.
- */
+// ":menutrans".
+// This function is also defined without the +multi_lang feature, in which
+// case the commands are ignored.
void ex_menutranslate(exarg_T *eap)
{
char *arg = eap->arg;
@@ -1766,10 +1729,8 @@ void ex_menutranslate(exarg_T *eap)
ga_init(&menutrans_ga, (int)sizeof(menutrans_T), 5);
}
- /*
- * ":menutrans clear": clear all translations.
- */
- if (STRNCMP(arg, "clear", 5) == 0 && ends_excmd(*skipwhite(arg + 5))) {
+ // ":menutrans clear": clear all translations.
+ if (strncmp(arg, "clear", 5) == 0 && ends_excmd(*skipwhite(arg + 5))) {
GA_DEEP_CLEAR(&menutrans_ga, menutrans_T, FREE_MENUTRANS);
// Delete all "menutrans_" global variables.
@@ -1800,9 +1761,7 @@ void ex_menutranslate(exarg_T *eap)
}
}
-/*
- * Find the character just after one part of a menu name.
- */
+// Find the character just after one part of a menu name.
static char *menu_skip_part(char *p)
{
while (*p != NUL && *p != '.' && !ascii_iswhite(*p)) {
@@ -1814,10 +1773,8 @@ static char *menu_skip_part(char *p)
return p;
}
-/*
- * Lookup part of a menu name in the translations.
- * Return a pointer to the translation or NULL if not found.
- */
+// Lookup part of a menu name in the translations.
+// Return a pointer to the translation or NULL if not found.
static char *menutrans_lookup(char *name, int len)
{
menutrans_T *tp = (menutrans_T *)menutrans_ga.ga_data;
@@ -1845,9 +1802,7 @@ static char *menutrans_lookup(char *name, int len)
return NULL;
}
-/*
- * Unescape the name in the translate dictionary table.
- */
+// Unescape the name in the translate dictionary table.
static void menu_unescape_name(char *name)
{
char *p;
@@ -1859,10 +1814,8 @@ static void menu_unescape_name(char *name)
}
}
-/*
- * Isolate the menu name.
- * Skip the menu name, and translate <Tab> into a real TAB.
- */
+// Isolate the menu name.
+// Skip the menu name, and translate <Tab> into a real TAB.
static char *menu_translate_tab_and_shift(char *arg_start)
{
char *arg = arg_start;
@@ -1945,7 +1898,7 @@ static void menuitem_getinfo(const char *menu_name, const vimmenu_T *menu, int m
/// "menu_info()" function
/// Return information about a menu (including all the child menus)
-void f_menu_info(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_menu_info(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_dict_alloc_ret(rettv);
dict_T *const retdict = rettv->vval.v_dict;
diff --git a/src/nvim/menu.h b/src/nvim/menu.h
index be294a1831..32959cf35f 100644
--- a/src/nvim/menu.h
+++ b/src/nvim/menu.h
@@ -1,11 +1,11 @@
#ifndef NVIM_MENU_H
#define NVIM_MENU_H
-#include <stdbool.h> // for bool
+#include <stdbool.h>
-#include "nvim/ex_cmds_defs.h" // for exarg_T
+#include "nvim/ex_cmds_defs.h"
#include "nvim/menu_defs.h"
-#include "nvim/types.h" // for char_u and expand_T
+#include "nvim/types.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "menu.h.generated.h"
diff --git a/src/nvim/menu_defs.h b/src/nvim/menu_defs.h
index 5fdb222bde..79b267ae49 100644
--- a/src/nvim/menu_defs.h
+++ b/src/nvim/menu_defs.h
@@ -1,7 +1,7 @@
#ifndef NVIM_MENU_DEFS_H
#define NVIM_MENU_DEFS_H
-#include <stdbool.h> // for bool
+#include <stdbool.h>
/// Indices into vimmenu_T->strings[] and vimmenu_T->noremap[] for each mode
/// \addtogroup MENU_INDEX
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 684cf7207c..7f29b19031 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -1,122 +1,127 @@
// 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
-/*
- * message.c: functions for displaying messages on the command line
- */
+// message.c: functions for displaying messages on the command line
#include <assert.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
-#include "nvim/assert.h"
+#include "nvim/buffer_defs.h"
+#include "nvim/channel.h"
#include "nvim/charset.h"
#include "nvim/drawscreen.h"
#include "nvim/eval.h"
-#include "nvim/ex_docmd.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/event/defs.h"
+#include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
+#include "nvim/ex_cmds_defs.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/gettext.h"
+#include "nvim/globals.h"
#include "nvim/grid.h"
#include "nvim/highlight.h"
#include "nvim/indent.h"
#include "nvim/input.h"
#include "nvim/keycodes.h"
+#include "nvim/log.h"
#include "nvim/main.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/mouse.h"
-#include "nvim/normal.h"
#include "nvim/ops.h"
#include "nvim/option.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
-#include "nvim/os/time.h"
+#include "nvim/pos.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
+#include "nvim/screen.h"
#include "nvim/strings.h"
-#include "nvim/syntax.h"
#include "nvim/ui.h"
#include "nvim/ui_compositor.h"
#include "nvim/vim.h"
-/*
- * To be able to scroll back at the "more" and "hit-enter" prompts we need to
- * store the displayed text and remember where screen lines start.
- */
+// To be able to scroll back at the "more" and "hit-enter" prompts we need to
+// store the displayed text and remember where screen lines start.
typedef struct msgchunk_S msgchunk_T;
struct msgchunk_S {
msgchunk_T *sb_next;
msgchunk_T *sb_prev;
- char sb_eol; // TRUE when line ends after this text
+ char sb_eol; // true when line ends after this text
int sb_msg_col; // column in which text starts
int sb_attr; // text attributes
- char_u sb_text[1]; // text to be displayed, actually longer
+ char sb_text[1]; // text to be displayed, actually longer
};
// Magic chars used in confirm dialog strings
-#define DLG_BUTTON_SEP '\n'
-#define DLG_HOTKEY_CHAR '&'
+enum {
+ DLG_BUTTON_SEP = '\n',
+ DLG_HOTKEY_CHAR = '&',
+};
-static int confirm_msg_used = FALSE; // displaying confirm_msg
+static int confirm_msg_used = false; // displaying confirm_msg
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "message.c.generated.h"
#endif
-static char_u *confirm_msg = NULL; // ":confirm" message
-static char_u *confirm_msg_tail; // tail of confirm_msg
+static char *confirm_msg = NULL; // ":confirm" message
+static char *confirm_msg_tail; // tail of confirm_msg
MessageHistoryEntry *first_msg_hist = NULL;
MessageHistoryEntry *last_msg_hist = NULL;
static int msg_hist_len = 0;
static FILE *verbose_fd = NULL;
-static int verbose_did_open = FALSE;
+static int verbose_did_open = false;
bool keep_msg_more = false; // keep_msg was set by msgmore()
-/*
- * When writing messages to the screen, there are many different situations.
- * A number of variables is used to remember the current state:
- * msg_didany true when messages were written since the last time the
- * user reacted to a prompt.
- * Reset: After hitting a key for the hit-return prompt,
- * hitting <CR> for the command line or input().
- * Set: When any message is written to the screen.
- * msg_didout true when something was written to the current line.
- * Reset: When advancing to the next line, when the current
- * text can be overwritten.
- * Set: When any message is written to the screen.
- * msg_nowait No extra delay for the last drawn message.
- * Used in normal_cmd() before the mode message is drawn.
- * emsg_on_display There was an error message recently. Indicates that there
- * should be a delay before redrawing.
- * msg_scroll The next message should not overwrite the current one.
- * msg_scrolled How many lines the screen has been scrolled (because of
- * messages). Used in update_screen() to scroll the screen
- * back. Incremented each time the screen scrolls a line.
- * msg_scrolled_ign TRUE when msg_scrolled is non-zero and msg_puts_attr()
- * writes something without scrolling should not make
- * need_wait_return to be set. This is a hack to make ":ts"
- * work without an extra prompt.
- * lines_left Number of lines available for messages before the
- * more-prompt is to be given. -1 when not set.
- * need_wait_return true when the hit-return prompt is needed.
- * Reset: After giving the hit-return prompt, when the user
- * has answered some other prompt.
- * Set: When the ruler or typeahead display is overwritten,
- * scrolling the screen for some message.
- * keep_msg Message to be displayed after redrawing the screen, in
- * main_loop().
- * This is an allocated string or NULL when not used.
- */
+// When writing messages to the screen, there are many different situations.
+// A number of variables is used to remember the current state:
+// msg_didany true when messages were written since the last time the
+// user reacted to a prompt.
+// Reset: After hitting a key for the hit-return prompt,
+// hitting <CR> for the command line or input().
+// Set: When any message is written to the screen.
+// msg_didout true when something was written to the current line.
+// Reset: When advancing to the next line, when the current
+// text can be overwritten.
+// Set: When any message is written to the screen.
+// msg_nowait No extra delay for the last drawn message.
+// Used in normal_cmd() before the mode message is drawn.
+// emsg_on_display There was an error message recently. Indicates that there
+// should be a delay before redrawing.
+// msg_scroll The next message should not overwrite the current one.
+// msg_scrolled How many lines the screen has been scrolled (because of
+// messages). Used in update_screen() to scroll the screen
+// back. Incremented each time the screen scrolls a line.
+// msg_scrolled_ign true when msg_scrolled is non-zero and msg_puts_attr()
+// writes something without scrolling should not make
+// need_wait_return to be set. This is a hack to make ":ts"
+// work without an extra prompt.
+// lines_left Number of lines available for messages before the
+// more-prompt is to be given. -1 when not set.
+// need_wait_return true when the hit-return prompt is needed.
+// Reset: After giving the hit-return prompt, when the user
+// has answered some other prompt.
+// Set: When the ruler or typeahead display is overwritten,
+// scrolling the screen for some message.
+// keep_msg Message to be displayed after redrawing the screen, in
+// main_loop().
+// 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;
@@ -134,7 +139,6 @@ static bool msg_ext_history_visible = false;
static bool msg_ext_keep_after_cmdline = false;
static int msg_grid_pos_at_flush = 0;
-static int msg_grid_scroll_discount = 0;
static void ui_ext_msg_set_pos(int row, bool scrolled)
{
@@ -159,8 +163,7 @@ void msg_grid_set_pos(int row, bool scrolled)
bool msg_use_grid(void)
{
- return default_grid.chars && msg_use_msgsep()
- && !ui_has(kUIMessages);
+ return default_grid.chars && !ui_has(kUIMessages);
}
void msg_grid_validate(void)
@@ -178,19 +181,17 @@ void msg_grid_validate(void)
xfree(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 : max_rows;
+ // Tricky: allow resize while pager or ex mode is active
+ int pos = MAX(max_rows - msg_scrolled, 0);
+ msg_grid.throttled = false; // don't throttle in 'cmdheight' area
+ msg_grid_set_pos(pos, msg_scrolled);
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.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(max_rows, false);
- }
} else if (!should_alloc && msg_grid.chars) {
ui_comp_remove_grid(&msg_grid);
grid_free(&msg_grid);
@@ -204,7 +205,7 @@ void msg_grid_validate(void)
msg_grid_set_pos(max_rows, false);
}
- if (msg_grid.chars && cmdline_row < msg_grid_pos) {
+ if (msg_grid.chars && !msg_scrolled && cmdline_row < msg_grid_pos) {
// TODO(bfredl): this should already be the case, but fails in some
// "batched" executions where compute_cmdrow() use stale positions or
// something.
@@ -213,9 +214,9 @@ void msg_grid_validate(void)
}
/// Displays the string 's' on the status line
-/// When terminal not initialized (yet) mch_errmsg(..) is used.
+/// When terminal not initialized (yet) os_errmsg(..) is used.
///
-/// @return TRUE if wait_return not called
+/// @return true if wait_return() not called
int msg(char *s)
{
return msg_attr_keep(s, 0, false, false);
@@ -251,7 +252,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, (int)(next_spec - s), attr);
+ msg_outtrans_len_attr(s, (int)(next_spec - s), attr);
if (*next_spec != TAB && *need_clear) {
msg_clr_eos();
@@ -265,7 +266,7 @@ void msg_multiline_attr(const char *s, int attr, bool check_int, bool *need_clea
// Print the rest of the message. We know there is no special
// character because strpbrk returned NULL
if (*s != NUL) {
- msg_outtrans_attr((char_u *)s, attr);
+ msg_outtrans_attr(s, attr);
}
}
@@ -275,12 +276,12 @@ void msg_multiattr(HlMessage hl_msg, const char *kind, bool history)
msg_start();
msg_clr_eos();
bool need_clear = false;
+ msg_ext_set_kind(kind);
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);
}
@@ -294,7 +295,7 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline)
{
static int entered = 0;
int retval;
- char_u *buf = NULL;
+ char *buf = NULL;
if (keep && multiline) {
// Not implemented. 'multiline' is only used by nvim-added messages,
@@ -305,7 +306,7 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline)
// Skip messages not match ":filter pattern".
// Don't filter when there is an error.
- if (!emsg_on_display && message_filtered((char_u *)s)) {
+ if (!emsg_on_display && message_filtered((char *)s)) {
return true;
}
@@ -313,38 +314,36 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline)
set_vim_var_string(VV_STATUSMSG, s, -1);
}
- /*
- * It is possible that displaying a messages causes a problem (e.g.,
- * when redrawing the window), which causes another message, etc.. To
- * break this loop, limit the recursiveness to 3 levels.
- */
+ // It is possible that displaying a messages causes a problem (e.g.,
+ // when redrawing the window), which causes another message, etc.. To
+ // break this loop, limit the recursiveness to 3 levels.
if (entered >= 3) {
- return TRUE;
+ return true;
}
entered++;
// Add message to history (unless it's a repeated kept message or a
// truncated message)
- if ((const char_u *)s != keep_msg
+ if (s != keep_msg
|| (*s != '<'
&& last_msg_hist != NULL
&& last_msg_hist->msg != NULL
- && STRCMP(s, last_msg_hist->msg))) {
+ && strcmp(s, last_msg_hist->msg) != 0)) {
add_msg_hist(s, -1, attr, multiline);
}
// Truncate the message if needed.
msg_start();
- buf = msg_strtrunc((char_u *)s, FALSE);
+ buf = msg_strtrunc((char *)s, false);
if (buf != NULL) {
- s = (const char *)buf;
+ s = buf;
}
bool need_clear = true;
if (multiline) {
msg_multiline_attr(s, attr, false, &need_clear);
} else {
- msg_outtrans_attr((char_u *)s, attr);
+ msg_outtrans_attr(s, attr);
}
if (need_clear) {
msg_clr_eos();
@@ -367,9 +366,9 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline)
/// @return an allocated string or NULL when no truncating is done.
///
/// @param force always truncate
-char_u *msg_strtrunc(char_u *s, int force)
+char *msg_strtrunc(char *s, int force)
{
- char_u *buf = NULL;
+ char *buf = NULL;
int len;
int room;
@@ -377,7 +376,7 @@ 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((char *)s);
+ len = vim_strsize(s);
if (msg_scrolled != 0) {
// Use all the columns.
room = (Rows - msg_row) * Columns - 1;
@@ -390,7 +389,7 @@ char_u *msg_strtrunc(char_u *s, int force)
// composing chars)
len = (room + 2) * 18;
buf = xmalloc((size_t)len);
- trunc_string((char *)s, (char *)buf, room, len);
+ trunc_string(s, buf, room, len);
}
}
return buf;
@@ -420,7 +419,7 @@ void trunc_string(char *s, char *buf, int room_in, int buflen)
half = room / 2;
// First part: Start of the string.
- for (e = 0; len < half && e < buflen; ++e) {
+ for (e = 0; len < half && e < buflen; e++) {
if (s[e] == NUL) {
// text fits without truncating!
buf[e] = NUL;
@@ -441,10 +440,10 @@ void trunc_string(char *s, char *buf, int room_in, int buflen)
}
// Last part: End of the string.
- half = i = (int)STRLEN(s);
+ half = i = (int)strlen(s);
for (;;) {
do {
- half = half - utf_head_off((char_u *)s, (char_u *)s + half - 1) - 1;
+ half = half - utf_head_off(s, s + half - 1) - 1;
} while (half > 0 && utf_iscomposing(utf_ptr2char(s + half)));
n = ptr2cells(s + half);
if (len + n > room || half == 0) {
@@ -457,7 +456,7 @@ void trunc_string(char *s, char *buf, int room_in, int buflen)
if (i <= e + 3) {
// text fits without truncating
if (s != buf) {
- len = (int)STRLEN(s);
+ len = (int)strlen(s);
if (len >= buflen) {
len = buflen - 1;
}
@@ -471,7 +470,7 @@ void trunc_string(char *s, char *buf, int room_in, int buflen)
} else if (e + 3 < buflen) {
// set the middle and copy the last part
memmove(buf + e, "...", (size_t)3);
- len = (int)STRLEN(s + i) + 1;
+ len = (int)strlen(s + i) + 1;
if (len >= buflen - e - 3) {
len = buflen - e - 3 - 1;
}
@@ -483,10 +482,8 @@ void trunc_string(char *s, char *buf, int room_in, int buflen)
}
}
-/*
- * Note: Caller of smsg() and smsg_attr() must check the resulting string is
- * shorter than IOSIZE!!!
- */
+// Note: Caller of smsg() and smsg_attr() must check the resulting string is
+// shorter than IOSIZE!!!
int smsg(const char *s, ...)
FUNC_ATTR_PRINTF(1, 2)
@@ -494,10 +491,10 @@ int smsg(const char *s, ...)
va_list arglist;
va_start(arglist, s);
- vim_vsnprintf((char *)IObuff, IOSIZE, s, arglist);
+ vim_vsnprintf(IObuff, IOSIZE, s, arglist);
va_end(arglist);
- return msg((char *)IObuff);
+ return msg(IObuff);
}
int smsg_attr(int attr, const char *s, ...)
@@ -506,7 +503,7 @@ int smsg_attr(int attr, const char *s, ...)
va_list arglist;
va_start(arglist, s);
- vim_vsnprintf((char *)IObuff, IOSIZE, s, arglist);
+ vim_vsnprintf(IObuff, IOSIZE, s, arglist);
va_end(arglist);
return msg_attr((const char *)IObuff, attr);
}
@@ -517,15 +514,13 @@ int smsg_attr_keep(int attr, const char *s, ...)
va_list arglist;
va_start(arglist, s);
- vim_vsnprintf((char *)IObuff, IOSIZE, s, arglist);
+ vim_vsnprintf(IObuff, IOSIZE, s, arglist);
va_end(arglist);
return msg_attr_keep((const char *)IObuff, attr, true, false);
}
-/*
- * Remember the last sourcing name/lnum used in an error message, so that it
- * isn't printed each time when it didn't change.
- */
+// Remember the last sourcing name/lnum used in an error message, so that it
+// isn't printed each time when it didn't change.
static int last_sourcing_lnum = 0;
static char *last_sourcing_name = NULL;
@@ -565,7 +560,7 @@ static char *get_emsg_source(void)
}
const char *const p = _("Error detected while processing %s:");
- const size_t buf_len = STRLEN(sname) + strlen(p) + 1;
+ const size_t buf_len = strlen(sname) + strlen(p) + 1;
char *const buf = xmalloc(buf_len);
snprintf(buf, buf_len, p, sname);
xfree(tofree);
@@ -608,10 +603,10 @@ void msg_source(int attr)
}
recursive = true;
- msg_scroll = true; // this will take more than one line
no_wait_return++;
char *p = get_emsg_source();
if (p != NULL) {
+ msg_scroll = true; // this will take more than one line
msg_attr(p, attr);
xfree(p);
}
@@ -634,18 +629,18 @@ void msg_source(int attr)
recursive = false;
}
-/// @return TRUE if not giving error messages right now:
+/// @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((char *)p_debug, 'm') == NULL
- && vim_strchr((char *)p_debug, 't') == NULL)
+ if ((emsg_off > 0 && vim_strchr(p_debug, 'm') == NULL
+ && vim_strchr(p_debug, 't') == NULL)
|| emsg_skip > 0) {
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
static bool emsg_multiline(const char *s, bool multiline)
@@ -665,7 +660,7 @@ static bool emsg_multiline(const char *s, bool multiline)
bool severe = emsg_severe;
emsg_severe = false;
- if (!emsg_off || vim_strchr((char *)p_debug, 't') != NULL) {
+ 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
@@ -673,18 +668,23 @@ static bool emsg_multiline(const char *s, bool multiline)
// interrupt message).
if (cause_errthrow(s, severe, &ignore)) {
if (!ignore) {
- did_emsg = true;
+ did_emsg++;
}
return true;
}
+ if (in_assert_fails && emsg_assert_fails_msg == NULL) {
+ emsg_assert_fails_msg = xstrdup(s);
+ emsg_assert_fails_lnum = SOURCING_LNUM;
+ xfree(emsg_assert_fails_context);
+ emsg_assert_fails_context = xstrdup(SOURCING_NAME == NULL ? "" : SOURCING_NAME);
+ }
+
// set "v:errmsg", also when using ":silent! cmd"
set_vim_var_string(VV_ERRMSG, s, -1);
- /*
- * When using ":silent! cmd" ignore error messages.
- * But do write it to the redirection file.
- */
+ // When using ":silent! cmd" ignore error messages.
+ // But do write it to the redirection file.
if (emsg_silent != 0) {
if (!emsg_noredir) {
msg_start();
@@ -738,14 +738,14 @@ static bool emsg_multiline(const char *s, bool multiline)
} else {
flush_buffers(FLUSH_MINIMAL); // flush internal buffers
}
- did_emsg = true; // flag for DoOneCmd()
+ did_emsg++; // flag for DoOneCmd()
}
emsg_on_display = true; // remember there is an error message
attr = HL_ATTR(HLF_E); // set highlight mode for error messages
if (msg_scrolled != 0) {
need_wait_return = true; // needed in case emsg() is called after
- } // wait_return has reset need_wait_return
+ } // wait_return() has reset need_wait_return
// and a redraw is expected because
// msg_scrolled is non-zero
if (msg_ext_kind == NULL) {
@@ -753,7 +753,7 @@ static bool emsg_multiline(const char *s, bool multiline)
}
// Display name and line number for the source of the error.
- // Sets "msg_scroll".
+ msg_scroll = true;
msg_source(attr);
// Display the error message itself.
@@ -764,9 +764,9 @@ static bool emsg_multiline(const char *s, bool multiline)
/// emsg() - display an error message
///
/// Rings the bell, if appropriate, and calls message() to do the real work
-/// When terminal not initialized (yet) mch_errmsg(..) is used.
+/// When terminal not initialized (yet) os_errmsg(..) is used.
///
-/// @return true if wait_return not called
+/// @return true if wait_return() not called
bool emsg(const char *s)
{
return emsg_multiline(s, false);
@@ -881,10 +881,10 @@ void msg_schedule_semsg(const char *const fmt, ...)
{
va_list ap;
va_start(ap, fmt);
- vim_vsnprintf((char *)IObuff, IOSIZE, fmt, ap);
+ vim_vsnprintf(IObuff, IOSIZE, fmt, ap);
va_end(ap);
- char *s = xstrdup((char *)IObuff);
+ char *s = xstrdup(IObuff);
loop_schedule_deferred(&main_loop, event_create(msg_semsg_event, 1, s));
}
@@ -900,7 +900,7 @@ char *msg_trunc_attr(char *s, bool force, int attr)
// Add message to history before truncating.
add_msg_hist(s, -1, attr, false);
- char *ts = (char *)msg_may_trunc(force, (char_u *)s);
+ char *ts = msg_may_trunc(force, s);
msg_hist_off = true;
n = msg_attr(ts, attr);
@@ -917,14 +917,16 @@ char *msg_trunc_attr(char *s, bool force, int attr)
/// @return a pointer to where the truncated message starts.
///
/// @note: May change the message by replacing a character with '<'.
-char_u *msg_may_trunc(bool force, char_u *s)
+char *msg_may_trunc(bool force, char *s)
{
- int room;
+ if (ui_has(kUIMessages)) {
+ return s;
+ }
- room = (Rows - cmdline_row - 1) * Columns + sc_col - 1;
+ int room = (Rows - cmdline_row - 1) * Columns + sc_col - 1;
if ((force || (shortmess(SHM_TRUNC) && !exmode_active))
- && (int)STRLEN(s) - room > 0 && p_ch > 0) {
- int size = vim_strsize((char *)s);
+ && (int)strlen(s) - room > 0) {
+ int size = vim_strsize(s);
// There may be room anyway when there are multibyte chars.
if (size <= room) {
@@ -932,8 +934,8 @@ char_u *msg_may_trunc(bool force, char_u *s)
}
int n;
for (n = 0; size >= room;) {
- size -= utf_ptr2cells((char *)s + n);
- n += utfc_ptr2len((char *)s + n);
+ size -= utf_ptr2cells(s + n);
+ n += utfc_ptr2len(s + n);
}
n--;
s += n;
@@ -950,15 +952,6 @@ void hl_msg_free(HlMessage hl_msg)
kv_destroy(hl_msg);
}
-#define LINE_BUFFER_SIZE 4096
-
-void add_hl_msg_hist(HlMessage hl_msg)
-{
- 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)
{
@@ -982,7 +975,7 @@ static void add_msg_hist_multiattr(const char *s, int len, int attr, bool multil
struct msg_hist *p = xmalloc(sizeof(struct msg_hist));
if (s) {
if (len < 0) {
- len = (int)STRLEN(s);
+ len = (int)strlen(s);
}
// remove leading and trailing newlines
while (len > 0 && *s == '\n') {
@@ -992,7 +985,7 @@ static void add_msg_hist_multiattr(const char *s, int len, int attr, bool multil
while (len > 0 && s[len - 1] == '\n') {
len--;
}
- p->msg = (char_u *)xmemdupz(s, (size_t)len);
+ p->msg = xmemdupz(s, (size_t)len);
} else {
p->msg = NULL;
}
@@ -1042,7 +1035,7 @@ void ex_messages(void *const eap_p)
struct msg_hist *p;
int c = 0;
- if (STRCMP(eap->arg, "clear") == 0) {
+ if (strcmp(eap->arg, "clear") == 0) {
int keep = eap->addr_count == 0 ? 0 : eap->line2;
while (msg_hist_len > keep) {
@@ -1086,7 +1079,7 @@ void ex_messages(void *const eap_p)
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_entry, STRING_OBJ(copy_string(chunk.text, NULL)));
ADD(content, ARRAY_OBJ(content_entry));
}
} else if (p->msg && p->msg[0]) {
@@ -1109,7 +1102,7 @@ void ex_messages(void *const eap_p)
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);
+ msg_attr_keep(p->msg, p->attr, false, p->multiline);
}
}
msg_hist_off = false;
@@ -1131,7 +1124,7 @@ void msg_end_prompt(void)
/// Wait for the user to hit a key (normally Enter)
///
-/// @param redraw if true, redraw the entire screen NOT_VALID
+/// @param redraw if true, redraw the entire screen UPD_NOT_VALID
/// if false, do a normal redraw
/// if -1, don't redraw at all
void wait_return(int redraw)
@@ -1143,7 +1136,7 @@ void wait_return(int redraw)
FILE *save_scriptout;
if (redraw == true) {
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
}
// If using ":silent cmd", don't wait for a return. Also don't set
@@ -1156,12 +1149,10 @@ void wait_return(int redraw)
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
- * the end. Adjust cmdline_row to avoid the next message overwriting the
- * last one.
- */
+ // 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
+ // the end. Adjust cmdline_row to avoid the next message overwriting the
+ // last one.
if (vgetc_busy > 0) {
return;
}
@@ -1177,17 +1168,13 @@ void wait_return(int redraw)
oldState = State;
if (quit_more) {
c = CAR; // just pretend CR was hit
- quit_more = FALSE;
- got_int = FALSE;
+ quit_more = false;
+ got_int = false;
} else if (exmode_active) {
msg_puts(" "); // make sure the cursor is on the right line
c = CAR; // no need for a return in ex mode
- got_int = FALSE;
+ got_int = false;
} else {
- // Make sure the hit-return prompt is on screen when 'guioptions' was
- // just changed.
- screenalloc();
-
State = MODE_HITRETURN;
setmouse();
cmdline_row = msg_row;
@@ -1226,12 +1213,10 @@ void wait_return(int redraw)
reg_recording = save_reg_recording;
scriptout = save_scriptout;
- /*
- * Allow scrolling back in the messages.
- * Also accept scroll-down commands when messages fill the screen,
- * to avoid that typing one 'j' too many makes the messages
- * disappear.
- */
+ // Allow scrolling back in the messages.
+ // Also accept scroll-down commands when messages fill the screen,
+ // to avoid that typing one 'j' too many makes the messages
+ // disappear.
if (p_more) {
if (c == 'b' || c == 'k' || c == 'u' || c == 'g'
|| c == K_UP || c == K_PAGEUP) {
@@ -1247,8 +1232,8 @@ void wait_return(int redraw)
}
if (quit_more) {
c = CAR; // just pretend CR was hit
- quit_more = FALSE;
- got_int = FALSE;
+ quit_more = false;
+ got_int = false;
} else if (c != K_IGNORE) {
c = K_IGNORE;
hit_return_msg();
@@ -1268,9 +1253,7 @@ void wait_return(int redraw)
|| c == K_MOUSEDOWN || c == K_MOUSEUP
|| c == K_MOUSEMOVE);
os_breakcheck();
- /*
- * Avoid that the mouse-up event causes visual mode to start.
- */
+ // Avoid that the mouse-up event causes visual mode to start.
if (c == K_LEFTMOUSE || c == K_MIDDLEMOUSE || c == K_RIGHTMOUSE
|| c == K_X1MOUSE || c == K_X2MOUSE) {
(void)jump_to_mouse(MOUSE_SETPOS, NULL, 0);
@@ -1307,7 +1290,7 @@ 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((char *)keep_msg) >=
+ if (keep_msg != NULL && vim_strsize(keep_msg) >=
(Rows - cmdline_row - 1) * Columns + sc_col) {
XFREE_CLEAR(keep_msg); // don't redisplay message, it's too long
}
@@ -1316,7 +1299,7 @@ void wait_return(int redraw)
ui_refresh();
} else if (!skip_redraw) {
if (redraw == true || (msg_scrolled != 0 && redraw != -1)) {
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
}
if (ui_has(kUIMessages)) {
msg_ext_clear(true);
@@ -1350,7 +1333,7 @@ void set_keep_msg(char *s, int attr)
{
xfree(keep_msg);
if (s != NULL && msg_silent == 0) {
- keep_msg = vim_strsave((char_u *)s);
+ keep_msg = xstrdup(s);
} else {
keep_msg = NULL;
}
@@ -1421,22 +1404,25 @@ void msg_start(void)
need_fileinfo = false;
}
- const bool no_msg_area = !ui_has_messages();
-
- if (need_clr_eos || (no_msg_area && redrawing_cmdline)) {
+ if (need_clr_eos || (p_ch == 0 && redrawing_cmdline)) {
// Halfway an ":echo" command and getting an (error) message: clear
// any text from the command.
need_clr_eos = false;
msg_clr_eos();
}
+ // if cmdheight=0, we need to scroll in the first line of msg_grid upon the screen
+ if (p_ch == 0 && !ui_has(kUIMessages) && !msg_scrolled) {
+ msg_grid_validate();
+ msg_scroll_up(false, true);
+ msg_scrolled++;
+ cmdline_row = Rows - 1;
+ }
+
if (!msg_scroll && full_screen) { // overwrite last message
msg_row = cmdline_row;
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
+ } else if (msg_didout || (p_ch == 0 && !ui_has(kUIMessages))) { // start message on next line
msg_putchar('\n');
did_return = true;
cmdline_row = msg_row;
@@ -1497,20 +1483,20 @@ void msg_outnum(long n)
msg_puts(buf);
}
-void msg_home_replace(char_u *fname)
+void msg_home_replace(char *fname)
{
msg_home_replace_attr(fname, 0);
}
-void msg_home_replace_hl(char_u *fname)
+void msg_home_replace_hl(char *fname)
{
msg_home_replace_attr(fname, HL_ATTR(HLF_D));
}
-static void msg_home_replace_attr(char_u *fname, int attr)
+static void msg_home_replace_attr(char *fname, int attr)
{
- char *name = home_replace_save(NULL, (char *)fname);
- msg_outtrans_attr((char_u *)name, attr);
+ char *name = home_replace_save(NULL, fname);
+ msg_outtrans_attr(name, attr);
xfree(name);
}
@@ -1521,15 +1507,15 @@ static void msg_home_replace_attr(char_u *fname, int attr)
/// @return the number of characters it takes on the screen.
int msg_outtrans(char *str)
{
- return msg_outtrans_attr((char_u *)str, 0);
+ return msg_outtrans_attr(str, 0);
}
-int msg_outtrans_attr(const char_u *str, int attr)
+int msg_outtrans_attr(const char *str, int attr)
{
- return msg_outtrans_len_attr(str, (int)STRLEN(str), attr);
+ return msg_outtrans_len_attr(str, (int)strlen(str), attr);
}
-int msg_outtrans_len(const char_u *str, int len)
+int msg_outtrans_len(const char *str, int len)
{
return msg_outtrans_len_attr(str, len, 0);
}
@@ -1538,24 +1524,24 @@ int msg_outtrans_len(const char_u *str, int len)
/// Handles multi-byte characters.
///
/// @return pointer to the next character.
-char_u *msg_outtrans_one(char_u *p, int attr)
+char *msg_outtrans_one(char *p, int attr)
{
int l;
- if ((l = utfc_ptr2len((char *)p)) > 1) {
+ if ((l = utfc_ptr2len(p)) > 1) {
msg_outtrans_len_attr(p, l, attr);
return p + l;
}
- msg_puts_attr((const char *)transchar_byte(*p), attr);
+ msg_puts_attr((const char *)transchar_byte((uint8_t)(*p)), attr);
return p + 1;
}
-int msg_outtrans_len_attr(const char_u *msgstr, int len, int attr)
+int msg_outtrans_len_attr(const char *msgstr, int len, int attr)
{
int retval = 0;
- const char *str = (const char *)msgstr;
- const char *plain_start = (const char *)msgstr;
- char_u *s;
+ const char *str = msgstr;
+ const char *plain_start = msgstr;
+ char *s;
int mb_l;
int c;
int save_got_int = got_int;
@@ -1575,13 +1561,11 @@ int msg_outtrans_len_attr(const char_u *msgstr, int len, int attr)
msg_puts_attr(" ", attr);
}
- /*
- * Go over the string. Special characters are translated and printed.
- * Normal characters are printed several at a time.
- */
+ // Go over the string. Special characters are translated and printed.
+ // Normal characters are printed several at a time.
while (--len >= 0 && !got_int) {
// Don't include composing chars after the end.
- mb_l = utfc_ptr2len_len((char_u *)str, len + 1);
+ mb_l = utfc_ptr2len_len(str, len + 1);
if (mb_l > 1) {
c = utf_ptr2char(str);
if (vim_isprintc(c)) {
@@ -1601,7 +1585,7 @@ int msg_outtrans_len_attr(const char_u *msgstr, int len, int attr)
len -= mb_l - 1;
str += mb_l;
} else {
- s = transchar_byte((uint8_t)(*str));
+ s = (char *)transchar_byte((uint8_t)(*str));
if (s[1] != NUL) {
// Unprintable char: print the printable chars so far and the
// translation of the unprintable char.
@@ -1610,7 +1594,7 @@ int msg_outtrans_len_attr(const char_u *msgstr, int len, int attr)
}
plain_start = str + 1;
msg_puts_attr((const char *)s, attr == 0 ? HL_ATTR(HLF_8) : attr);
- retval += (int)STRLEN(s);
+ retval += (int)strlen(s);
} else {
retval++;
}
@@ -1628,12 +1612,13 @@ int msg_outtrans_len_attr(const char_u *msgstr, int len, int attr)
return retval;
}
-void msg_make(char_u *arg)
+void msg_make(char *arg)
{
int i;
- static char_u *str = (char_u *)"eeffoc", *rs = (char_u *)"Plon#dqg#vxjduB";
+ static char *str = "eeffoc";
+ static char *rs = "Plon#dqg#vxjduB";
- arg = (char_u *)skipwhite((char *)arg);
+ arg = skipwhite(arg);
for (i = 5; *arg && i >= 0; i--) {
if (*arg++ != str[i]) {
break;
@@ -1641,7 +1626,7 @@ void msg_make(char_u *arg)
}
if (i < 0) {
msg_putchar('\n');
- for (i = 0; rs[i]; ++i) {
+ for (i = 0; rs[i]; i++) {
msg_putchar(rs[i] - 3);
}
}
@@ -1653,7 +1638,7 @@ void msg_make(char_u *arg)
/// If K_SPECIAL is encountered, then it is taken in conjunction with the
/// following character and shown as <F1>, <S-Up> etc. Any other character
/// which is not printable shown in <> form.
-/// If 'from' is TRUE (lhs of a mapping), a space is shown as <Space>.
+/// If 'from' is true (lhs of a mapping), a space is shown as <Space>.
/// If a character is displayed in one of these special ways, is also
/// highlighted (its highlight name is '8' in the p_hl variable).
/// Otherwise characters are not highlighted.
@@ -1662,12 +1647,12 @@ void msg_make(char_u *arg)
///
/// @param from true for LHS of a mapping
/// @param maxlen screen columns, 0 for unlimited
-int msg_outtrans_special(const char_u *strstart, bool from, int maxlen)
+int msg_outtrans_special(const char *strstart, bool from, int maxlen)
{
if (strstart == NULL) {
return 0; // Do nothing.
}
- const char_u *str = strstart;
+ const char *str = strstart;
int retval = 0;
int attr = HL_ATTR(HLF_8);
@@ -1678,7 +1663,7 @@ int msg_outtrans_special(const char_u *strstart, bool from, int maxlen)
text = "<Space>";
str++;
} else {
- text = str2special((const char **)&str, from, false);
+ text = str2special(&str, from, false);
}
if (text[0] != NUL && text[1] == NUL) {
// single-byte character or illegal byte
@@ -1702,8 +1687,8 @@ int msg_outtrans_special(const char_u *strstart, bool from, int maxlen)
/// Used for lhs or rhs of mappings.
///
/// @param[in] str String to convert.
-/// @param[in] replace_spaces Convert spaces into `<Space>`, normally used fo
-/// lhs, but not rhs.
+/// @param[in] replace_spaces Convert spaces into `<Space>`, normally used for
+/// lhs of mapping and keytrans(), but not rhs.
/// @param[in] replace_lt Convert `<` into `<lt>`.
///
/// @return [allocated] Converted string.
@@ -1725,8 +1710,8 @@ char *str2special_save(const char *const str, const bool replace_spaces, const b
/// Convert character, replacing key with printable representation.
///
/// @param[in,out] sp String to convert. Is advanced to the next key code.
-/// @param[in] replace_spaces Convert spaces into <Space>, normally used for
-/// lhs, but not rhs.
+/// @param[in] replace_spaces Convert spaces into `<Space>`, normally used for
+/// lhs of mapping and keytrans(), but not rhs.
/// @param[in] replace_lt Convert `<` into `<lt>`.
///
/// @return Converted key code, in a static buffer. Buffer is always one and the
@@ -1817,20 +1802,20 @@ void str2specialbuf(const char *sp, char *buf, size_t len)
}
/// print line for :print or :list command
-void msg_prt_line(char_u *s, int list)
+void msg_prt_line(char *s, int list)
{
int c;
int col = 0;
int n_extra = 0;
int c_extra = 0;
int c_final = 0;
- char_u *p_extra = NULL; // init to make SASC shut up
+ char *p_extra = NULL; // init to make SASC shut up
int n;
int attr = 0;
- char_u *lead = NULL;
+ char *lead = NULL;
bool in_multispace = false;
int multispace_pos = 0;
- char_u *trail = NULL;
+ char *trail = NULL;
int l;
if (curwin->w_p_list) {
@@ -1840,7 +1825,7 @@ void msg_prt_line(char_u *s, int list)
if (list) {
// find start of trailing whitespace
if (curwin->w_p_lcs_chars.trail) {
- trail = s + STRLEN(s);
+ trail = s + strlen(s);
while (trail > s && ascii_iswhite(trail[-1])) {
trail--;
}
@@ -1872,16 +1857,16 @@ void msg_prt_line(char_u *s, int list)
c = c_extra;
} else {
assert(p_extra != NULL);
- c = *p_extra++;
+ c = (unsigned char)(*p_extra++);
}
- } else if ((l = utfc_ptr2len((char *)s)) > 1) {
- col += utf_ptr2cells((char *)s);
+ } else if ((l = utfc_ptr2len(s)) > 1) {
+ col += utf_ptr2cells(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((char *)s) == 160
- || utf_ptr2char((char *)s) == 0x202f)) {
+ && (utf_ptr2char(s) == 160
+ || utf_ptr2char(s) == 0x202f)) {
int len = utf_char2bytes(curwin->w_p_lcs_chars.nbsp, buf);
buf[len] = NUL;
} else {
@@ -1893,7 +1878,7 @@ void msg_prt_line(char_u *s, int list)
continue;
} else {
attr = 0;
- c = *s++;
+ c = (unsigned char)(*s++);
in_multispace = c == ' ' && ((col > 0 && s[-2] == ' ') || *s == ' ');
if (!in_multispace) {
multispace_pos = 0;
@@ -1919,7 +1904,7 @@ void msg_prt_line(char_u *s, int list)
c = curwin->w_p_lcs_chars.nbsp;
attr = HL_ATTR(HLF_0);
} else if (c == NUL && list && curwin->w_p_lcs_chars.eol != NUL) {
- p_extra = (char_u *)"";
+ p_extra = "";
c_extra = NUL;
c_final = NUL;
n_extra = 1;
@@ -1928,10 +1913,10 @@ void msg_prt_line(char_u *s, int list)
s--;
} else if (c != NUL && (n = byte2cells(c)) > 1) {
n_extra = n - 1;
- p_extra = transchar_byte(c);
+ p_extra = (char *)transchar_byte(c);
c_extra = NUL;
c_final = NUL;
- c = *p_extra++;
+ c = (unsigned char)(*p_extra++);
// Use special coloring to be able to distinguish <hex> from
// the same in plain text.
attr = HL_ATTR(HLF_0);
@@ -1976,13 +1961,13 @@ void msg_prt_line(char_u *s, int list)
/// 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)
+static char *screen_puts_mbyte(char *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((char *)s);
+ cw = utf_ptr2cells(s);
if (cw > 1
&& (cmdmsg_rl ? msg_col <= 1 : msg_col == Columns - 1)) {
// Doesn't fit, print a highlighted '>' to fill it up.
@@ -2022,12 +2007,12 @@ void msg_puts_title(const char *s)
/// 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)
+void msg_outtrans_long_attr(char *longstr, int attr)
{
- msg_outtrans_long_len_attr(longstr, (int)STRLEN(longstr), attr);
+ msg_outtrans_long_len_attr(longstr, (int)strlen(longstr), attr);
}
-void msg_outtrans_long_len_attr(char_u *longstr, int len, int attr)
+void msg_outtrans_long_len_attr(char *longstr, int len, int attr)
{
int slen = len;
int room;
@@ -2083,7 +2068,7 @@ void msg_puts_attr_len(const char *const str, const ptrdiff_t len, int attr)
overflow = true;
}
} else {
- overflow = msg_scrolled != 0;
+ overflow = msg_scrolled > (p_ch == 0 ? 1 : 0);
}
if (overflow && !msg_scrolled_ign && strcmp(str, "\r") != 0) {
@@ -2104,7 +2089,7 @@ 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, (int)len, attr, false);
+ msg_puts_display(str, (int)len, attr, false);
}
need_fileinfo = false;
@@ -2147,10 +2132,10 @@ static void msg_ext_emit_chunk(void)
/// 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)
+static void msg_puts_display(const char *str, int maxlen, int attr, int recurse)
{
- const char_u *s = str;
- const char_u *t_s = str; // String from "t_s" to "s" is still todo.
+ const char *s = str;
+ const char *t_s = str; // String from "t_s" to "s" is still todo.
int t_col = 0; // Screen cells todo, 0 when "t_s" not used.
int l;
int cw;
@@ -2167,7 +2152,7 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs
msg_ext_last_attr = attr;
}
// Concat pieces with the same highlight
- size_t len = STRNLEN(str, maxlen); // -V781
+ size_t len = strnlen(str, (size_t)maxlen); // -V781
ga_concat_len(&msg_ext_last_chunk, (char *)str, len);
msg_ext_cur_len += len;
return;
@@ -2185,12 +2170,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((char *)s) > 1
+ || (utf_ptr2cells(s) > 1
&& msg_col <= 2))
: ((*s != '\r' && msg_col + t_col >= Columns - 1)
|| (*s == TAB
&& msg_col + t_col >= ((Columns - 1) & ~7))
- || (utf_ptr2cells((char *)s) > 1
+ || (utf_ptr2cells(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
@@ -2206,8 +2191,8 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs
}
// Scroll the screen up one line.
- bool has_last_char = (*s >= ' ' && !cmdmsg_rl);
- msg_scroll_up(!has_last_char);
+ bool has_last_char = ((uint8_t)(*s) >= ' ' && !cmdmsg_rl);
+ msg_scroll_up(!has_last_char, false);
msg_row = Rows - 2;
if (msg_col >= Columns) { // can happen after screen resize
@@ -2220,9 +2205,9 @@ 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((char *)s);
+ l = utfc_ptr2len(s);
}
- s = screen_puts_mbyte((char_u *)s, l, attr);
+ s = screen_puts_mbyte((char *)s, l, attr);
did_last_char = true;
} else {
did_last_char = false;
@@ -2243,16 +2228,14 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs
}
inc_msg_scrolled();
- need_wait_return = true; // may need wait_return in main()
+ need_wait_return = true; // may need wait_return() in main()
redraw_cmdline = true;
if (cmdline_row > 0 && !exmode_active) {
cmdline_row--;
}
- /*
- * If screen is completely filled and 'more' is set then wait
- * for a character.
- */
+ // If screen is completely filled and 'more' is set then wait
+ // for a character.
if (lines_left > 0) {
lines_left--;
}
@@ -2275,7 +2258,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((char *)s) > 1
+ || (utf_ptr2cells(s) > 1
&& msg_col + t_col >= Columns - 1)
;
if (t_col > 0 && (wrap || *s == '\r' || *s == '\b'
@@ -2311,20 +2294,20 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs
} while (msg_col & 7);
} else if (*s == BELL) { // beep (from ":sh")
vim_beep(BO_SH);
- } else if (*s >= 0x20) { // printable char
- cw = utf_ptr2cells((char *)s);
+ } else if ((uint8_t)(*s) >= 0x20) { // printable char
+ cw = utf_ptr2cells(s);
if (maxlen >= 0) {
// avoid including composing chars after the end
l = utfc_ptr2len_len(s, (int)((str + maxlen) - s));
} else {
- l = utfc_ptr2len((char *)s);
+ l = utfc_ptr2len(s);
}
// When drawing from right to left or when a double-wide character
// doesn't fit, draw a single character here. Otherwise collect
// characters and draw them all at once later.
if (cmdmsg_rl || (cw > 1 && msg_col + t_col >= Columns - 1)) {
if (l > 1) {
- s = screen_puts_mbyte((char_u *)s, l, attr) - 1;
+ s = screen_puts_mbyte((char *)s, l, attr) - 1;
} else {
msg_screen_putchar(*s, attr);
}
@@ -2344,7 +2327,7 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs
if (t_col > 0) {
t_puts(&t_col, t_s, s, attr);
}
- if (p_more && !recurse) {
+ if (p_more && !recurse && !(s == sb_str + 1 && *sb_str == '\n')) {
store_sb_text((char **)&sb_str, (char *)s, attr, &sb_col, false);
}
@@ -2353,27 +2336,20 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs
/// @return true when ":filter pattern" was used and "msg" does not match
/// "pattern".
-bool message_filtered(char_u *msg)
+bool message_filtered(char *msg)
{
if (cmdmod.cmod_filter_regmatch.regprog == NULL) {
return false;
}
- bool match = vim_regexec(&cmdmod.cmod_filter_regmatch, (char *)msg, (colnr_T)0);
+ bool match = vim_regexec(&cmdmod.cmod_filter_regmatch, msg, (colnr_T)0);
return cmdmod.cmod_filter_force ? match : !match;
}
/// including horizontal separator
int msg_scrollsize(void)
{
- return msg_scrolled + (int)p_ch + 1;
-}
-
-bool msg_use_msgsep(void)
-{
- // the full-screen scroll behavior doesn't really make sense with
- // 'ext_multigrid'
- return ((dy_flags & DY_MSGSEP) || ui_has(kUIMultigrid));
+ return msg_scrolled + (int)p_ch + ((p_ch > 0 || msg_scrolled > 1) ? 1 : 0);
}
bool msg_do_throttle(void)
@@ -2382,27 +2358,28 @@ bool msg_do_throttle(void)
}
/// Scroll the screen up one line for displaying the next message line.
-void msg_scroll_up(bool may_throttle)
+void msg_scroll_up(bool may_throttle, bool zerocmd)
{
if (may_throttle && msg_do_throttle()) {
msg_grid.throttled = true;
}
msg_did_scroll = true;
- if (msg_use_msgsep()) {
- if (msg_grid_pos > 0) {
- msg_grid_set_pos(msg_grid_pos - 1, true);
- } else {
- 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;
+ if (msg_grid_pos > 0) {
+ msg_grid_set_pos(msg_grid_pos - 1, !zerocmd);
+
+ // When displaying the first line with cmdheight=0, we need to draw over
+ // the existing last line of the screen.
+ if (zerocmd && msg_grid.chars) {
+ grid_clear_line(&msg_grid, msg_grid.line_offset[0], msg_grid.cols, false);
}
} else {
- grid_del_lines(&msg_grid_adj, 0, 1, Rows, 0, Columns);
+ 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;
}
- grid_fill(&msg_grid_adj, Rows - 1, Rows, 0, Columns, ' ', ' ',
- HL_ATTR(HLF_MSG));
+ grid_fill(&msg_grid_adj, Rows - 1, Rows, 0, Columns, ' ', ' ', HL_ATTR(HLF_MSG));
}
/// Send throttled message output to UI clients
@@ -2464,24 +2441,21 @@ void msg_reset_scroll(void)
}
// TODO(bfredl): some duplicate logic with update_screen(). Later on
// we should properly disentangle message clear with full screen redraw.
- if (msg_use_grid()) {
- 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 - (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++) {
- grid_clear_line(&msg_grid, msg_grid.line_offset[i],
- msg_grid.cols, false);
- }
+ 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 - (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++) {
+ grid_clear_line(&msg_grid, msg_grid.line_offset[i],
+ msg_grid.cols, false);
}
- } else {
- redraw_all_later(NOT_VALID);
}
msg_scrolled = 0;
msg_scrolled_at_flush = 0;
+ msg_grid_scroll_discount = 0;
}
/// Increment "msg_scrolled".
@@ -2506,8 +2480,8 @@ static void inc_msg_scrolled(void)
xfree(tofree);
}
msg_scrolled++;
- if (must_redraw < VALID) {
- must_redraw = VALID;
+ if (must_redraw < UPD_VALID) {
+ must_redraw = UPD_VALID;
}
}
@@ -2557,7 +2531,7 @@ static void store_sb_text(char **sb_str, char *s, int attr, int *sb_col, int fin
}
mp->sb_next = NULL;
} else if (finish && last_msgchunk != NULL) {
- last_msgchunk->sb_eol = TRUE;
+ last_msgchunk->sb_eol = true;
}
*sb_str = s;
@@ -2614,7 +2588,7 @@ void sb_text_end_cmdline(void)
}
/// Clear any text remembered for scrolling back.
-/// When "all" is FALSE keep the last line.
+/// When "all" is false keep the last line.
/// Called when redrawing the screen.
void clear_sb_text(int all)
{
@@ -2649,7 +2623,7 @@ void show_sb_text(void)
vim_beep(BO_MESS);
} else {
do_more_prompt('G');
- wait_return(FALSE);
+ wait_return(false);
}
}
@@ -2668,7 +2642,7 @@ static msgchunk_T *msg_sb_start(msgchunk_T *mps)
void msg_sb_eol(void)
{
if (last_msgchunk != NULL) {
- last_msgchunk->sb_eol = TRUE;
+ last_msgchunk->sb_eol = true;
}
}
@@ -2678,7 +2652,7 @@ void msg_sb_eol(void)
static msgchunk_T *disp_sb_line(int row, msgchunk_T *smp)
{
msgchunk_T *mp = smp;
- char_u *p;
+ char *p;
for (;;) {
msg_row = row;
@@ -2687,7 +2661,7 @@ static msgchunk_T *disp_sb_line(int row, msgchunk_T *smp)
if (*p == '\n') { // don't display the line break
p++;
}
- msg_puts_display(p, -1, mp->sb_attr, TRUE);
+ msg_puts_display(p, -1, mp->sb_attr, true);
if (mp->sb_eol || mp->sb_next == NULL) {
break;
}
@@ -2698,18 +2672,17 @@ static msgchunk_T *disp_sb_line(int row, msgchunk_T *smp)
}
/// 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)
+static void t_puts(int *t_col, const char *t_s, const char *s, int attr)
{
attr = hl_combine_attr(HL_ATTR(HLF_MSG), attr);
// Output postponed text.
msg_didout = true; // Remember that line is not empty.
- grid_puts_len(&msg_grid_adj, (char_u *)t_s, (int)(s - t_s), msg_row, msg_col,
- attr);
+ grid_puts_len(&msg_grid_adj, (char *)t_s, (int)(s - t_s), msg_row, msg_col, attr);
msg_col += *t_col;
*t_col = 0;
// If the string starts with a composing character don't increment the
// column position for it.
- if (utf_iscomposing(utf_ptr2char((char *)t_s))) {
+ if (utf_iscomposing(utf_ptr2char(t_s))) {
msg_col--;
}
if (msg_col >= Columns) {
@@ -2718,7 +2691,7 @@ static void t_puts(int *t_col, const char_u *t_s, const char_u *s, int attr)
}
}
-/// @return TRUE when messages should be printed to stdout/stderr:
+/// @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)
@@ -2755,9 +2728,9 @@ static void msg_puts_printf(const char *str, const ptrdiff_t maxlen)
memcpy(p, s, (size_t)len);
*(p + len) = '\0';
if (info_message) {
- mch_msg(buf);
+ os_msg(buf);
} else {
- mch_errmsg(buf);
+ os_errmsg(buf);
}
}
@@ -2786,14 +2759,14 @@ static void msg_puts_printf(const char *str, const ptrdiff_t maxlen)
/// 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".
+/// @return true when jumping ahead to "confirm_msg_tail".
static int do_more_prompt(int typed_char)
{
static bool entered = false;
int used_typed_char = typed_char;
int oldState = State;
int c;
- int retval = FALSE;
+ int retval = false;
int toscroll;
bool to_redraw = false;
msgchunk_T *mp_last = NULL;
@@ -2817,7 +2790,7 @@ static int do_more_prompt(int typed_char)
// "g<": Find first line on the last page.
mp_last = msg_sb_start(last_msgchunk);
for (i = 0; i < Rows - 2 && mp_last != NULL
- && mp_last->sb_prev != NULL; ++i) {
+ && mp_last->sb_prev != NULL; i++) {
mp_last = msg_sb_start(mp_last->sb_prev);
}
}
@@ -2825,12 +2798,10 @@ static int do_more_prompt(int typed_char)
State = MODE_ASKMORE;
setmouse();
if (typed_char == NUL) {
- msg_moremsg(FALSE);
+ msg_moremsg(false);
}
for (;;) {
- /*
- * Get a typed character directly from the user.
- */
+ // Get a typed character directly from the user.
if (used_typed_char != NUL) {
c = used_typed_char; // was typed at hit-enter prompt
used_typed_char = NUL;
@@ -2898,10 +2869,10 @@ static int do_more_prompt(int typed_char)
case ESC:
if (confirm_msg_used) {
// Jump to the choices of the dialog.
- retval = TRUE;
+ retval = true;
} else {
- got_int = TRUE;
- quit_more = TRUE;
+ got_int = true;
+ quit_more = true;
}
// When there is some more output (wrapping line) display that
// without another prompt.
@@ -2917,7 +2888,7 @@ static int do_more_prompt(int typed_char)
break;
default: // no valid response
- msg_moremsg(TRUE);
+ msg_moremsg(true);
continue;
}
@@ -2936,8 +2907,7 @@ static int do_more_prompt(int typed_char)
}
// go to start of line at top of the screen
- for (i = 0; i < Rows - 2 && mp != NULL && mp->sb_prev != NULL;
- ++i) {
+ for (i = 0; i < Rows - 2 && mp != NULL && mp->sb_prev != NULL; i++) {
mp = msg_sb_start(mp->sb_prev);
}
@@ -2977,6 +2947,11 @@ static int do_more_prompt(int typed_char)
}
} else {
// First display any text that we scrolled back.
+ // if p_ch=0 we need to allocate a line for "press enter" messages!
+ if (cmdline_row >= Rows && !ui_has(kUIMessages)) {
+ msg_scroll_up(true, false);
+ msg_scrolled++;
+ }
while (toscroll > 0 && mp_last != NULL) {
if (msg_do_throttle() && !msg_grid.throttled) {
// Tricky: we redraw at one line higher than usual. Therefore
@@ -2985,7 +2960,7 @@ static int do_more_prompt(int typed_char)
msg_grid_scroll_discount++;
}
// scroll up, display line at bottom
- msg_scroll_up(true);
+ msg_scroll_up(true, false);
inc_msg_scrolled();
grid_fill(&msg_grid_adj, Rows - 2, Rows - 1, 0, Columns, ' ', ' ',
HL_ATTR(HLF_MSG));
@@ -3029,34 +3004,39 @@ static int do_more_prompt(int typed_char)
return retval;
}
-#if defined(WIN32)
-void mch_errmsg(char *str)
+#if defined(MSWIN)
+/// Headless (no UI) error message handler.
+static void do_msg(const char *str, bool errmsg)
{
+ static bool did_err = false;
assert(str != NULL);
wchar_t *utf16str;
int r = utf8_to_utf16(str, -1, &utf16str);
- if (r != 0) {
+ if (r != 0 && !did_err) {
+ did_err = true;
fprintf(stderr, "utf8_to_utf16 failed: %d", r);
- } else {
- fwprintf(stderr, L"%ls", utf16str);
+ ELOG("utf8_to_utf16 failed: %d", r);
+ } else if (r == 0) {
+ if (errmsg) {
+ fwprintf(stderr, L"%ls", utf16str);
+ } else {
+ wprintf(L"%ls", utf16str);
+ }
xfree(utf16str);
}
}
-/// Give a message. To be used when the UI is not initialized yet.
-void mch_msg(char *str)
+void os_errmsg(const char *str)
{
- assert(str != NULL);
- wchar_t *utf16str;
- int r = utf8_to_utf16(str, -1, &utf16str);
- if (r != 0) {
- fprintf(stderr, "utf8_to_utf16 failed: %d", r);
- } else {
- wprintf(L"%ls", utf16str);
- xfree(utf16str);
- }
+ do_msg(str, true);
}
-#endif // WIN32
+
+/// Headless (no UI) message handler.
+void os_msg(const char *str)
+{
+ do_msg(str, false);
+}
+#endif // MSWIN
/// Put a character on the screen at the current message position and advance
/// to the next position. Only for printable ASCII!
@@ -3081,14 +3061,13 @@ static void msg_screen_putchar(int c, int attr)
void msg_moremsg(int full)
{
int attr;
- char_u *s = (char_u *)_("-- More --");
+ char *s = _("-- More --");
attr = hl_combine_attr(HL_ATTR(HLF_MSG), HL_ATTR(HLF_M));
grid_puts(&msg_grid_adj, s, Rows - 1, 0, attr);
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((char *)s), attr);
+ grid_puts(&msg_grid_adj, _(" SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "),
+ Rows - 1, vim_strsize(s), attr);
}
}
@@ -3122,7 +3101,7 @@ void repeat_message(void)
/// Skip this when ":silent" was used, no need to clear for redirection.
void msg_clr_eos(void)
{
- if (msg_silent == 0 && p_ch > 0) {
+ if (msg_silent == 0) {
msg_clr_eos_force();
}
}
@@ -3144,12 +3123,10 @@ void msg_clr_eos_force(void)
msg_row = msg_grid_pos;
}
- if (ui_has_messages()) {
- 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));
- }
+ 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)) {
@@ -3167,17 +3144,15 @@ void msg_clr_cmdline(void)
}
/// end putting a message on the screen
-/// call wait_return if the message does not fit in the available space
+/// call wait_return() if the message does not fit in the available space
///
-/// @return TRUE if wait_return not called.
+/// @return true if wait_return() not called.
int msg_end(void)
{
- /*
- * If the string is larger than the window,
- * or the ruler option is set and we run into it,
- * we have to redraw the window.
- * Do not do this if we are abandoning the file or editing the command line.
- */
+ // If the string is larger than the window,
+ // or the ruler option is set and we run into it,
+ // 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 & MODE_CMDLINE)) {
wait_return(false);
return false;
@@ -3194,6 +3169,7 @@ int msg_end(void)
void msg_ext_ui_flush(void)
{
if (!ui_has(kUIMessages)) {
+ msg_ext_kind = NULL;
return;
}
@@ -3245,8 +3221,8 @@ void msg_ext_clear_later(void)
{
if (msg_ext_is_visible()) {
msg_ext_need_clear = true;
- if (must_redraw < VALID) {
- must_redraw = VALID;
+ if (must_redraw < UPD_VALID) {
+ must_redraw = UPD_VALID;
}
}
}
@@ -3283,7 +3259,7 @@ void msg_check(void)
/// @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;
+ const char *s = str;
static int cur_col = 0;
if (maxlen == 0) {
@@ -3308,7 +3284,7 @@ static void redir_write(const char *const str, const ptrdiff_t maxlen)
ga_concat_len(capture_ga, " ", 1);
}
if (redir_reg) {
- write_reg_contents(redir_reg, (char_u *)" ", 1, true);
+ write_reg_contents(redir_reg, " ", 1, true);
} else if (redir_vname) {
var_redir_str(" ", -1);
} else if (redir_fd != NULL) {
@@ -3321,7 +3297,7 @@ static void redir_write(const char *const str, const ptrdiff_t maxlen)
}
}
- size_t len = maxlen == -1 ? STRLEN(s) : (size_t)maxlen;
+ size_t len = maxlen == -1 ? strlen(s) : (size_t)maxlen;
if (capture_ga) {
ga_concat_len(capture_ga, str, len);
}
@@ -3334,7 +3310,7 @@ static void redir_write(const char *const str, const ptrdiff_t maxlen)
// Write and adjust the current column.
while (*s != NUL
- && (maxlen < 0 || (int)(s - (const char_u *)str) < maxlen)) {
+ && (maxlen < 0 || (int)(s - str) < maxlen)) {
if (!redir_reg && !redir_vname && !capture_ga) {
if (redir_fd != NULL) {
putc(*s, redir_fd);
@@ -3392,7 +3368,7 @@ void verbose_enter_scroll(void)
msg_silent++;
} else {
// always scroll up, don't overwrite
- msg_scroll = TRUE;
+ msg_scroll = true;
}
}
@@ -3415,7 +3391,7 @@ void verbose_stop(void)
fclose(verbose_fd);
verbose_fd = NULL;
}
- verbose_did_open = FALSE;
+ verbose_did_open = false;
}
/// Open the file 'verbosefile'.
@@ -3425,9 +3401,9 @@ int verbose_open(void)
{
if (verbose_fd == NULL && !verbose_did_open) {
// Only give the error message once.
- verbose_did_open = TRUE;
+ verbose_did_open = true;
- verbose_fd = os_fopen((char *)p_vfile, "a");
+ verbose_fd = os_fopen(p_vfile, "a");
if (verbose_fd == NULL) {
semsg(_(e_notopen), p_vfile);
return FAIL;
@@ -3471,10 +3447,10 @@ void give_warning(char *message, bool hl)
no_wait_return--;
}
-void give_warning2(char_u *const message, char_u *const a1, bool hl)
+void give_warning2(char *const message, char *const a1, bool hl)
{
- vim_snprintf((char *)IObuff, IOSIZE, (char *)message, a1);
- give_warning((char *)IObuff, hl);
+ vim_snprintf(IObuff, IOSIZE, message, a1);
+ give_warning(IObuff, hl);
}
/// Advance msg cursor to column "col".
@@ -3524,13 +3500,13 @@ void msg_advance(int col)
/// different letter.
///
/// @param textfiel IObuff for inputdialog(), NULL otherwise
-/// @param ex_cmd when TRUE pressing : accepts default and starts Ex command
+/// @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)
+int do_dialog(int type, char *title, char *message, char *buttons, int dfltbutton, char *textfield,
+ int ex_cmd)
{
int retval = 0;
- char_u *hotkeys;
+ char *hotkeys;
int c;
int i;
@@ -3547,10 +3523,8 @@ int do_dialog(int type, char_u *title, char_u *message, char_u *buttons, int dfl
State = MODE_CONFIRM;
setmouse();
- /*
- * Since we wait for a keypress, don't make the
- * user press RETURN as well afterwards.
- */
+ // Since we wait for a keypress, don't make the
+ // user press RETURN as well afterwards.
no_wait_return++;
hotkeys = msg_show_console_dialog(message, buttons, dfltbutton);
@@ -3580,10 +3554,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((char *)hotkeys + i) == c) {
+ if (utf_ptr2char(hotkeys + i) == c) {
break;
}
- i += utfc_ptr2len((char *)hotkeys + i) - 1;
+ i += utfc_ptr2len(hotkeys + i) - 1;
retval++;
}
if (hotkeys[i]) {
@@ -3610,14 +3584,14 @@ int do_dialog(int type, char_u *title, char_u *message, char_u *buttons, int dfl
/// characters. Return the length of the character in bytes.
///
/// @param lowercase make character lower case
-static int copy_char(const char_u *from, char_u *to, bool lowercase)
+static int copy_char(const char *from, char *to, bool lowercase)
FUNC_ATTR_NONNULL_ALL
{
if (lowercase) {
- int c = mb_tolower(utf_ptr2char((char *)from));
- return utf_char2bytes(c, (char *)to);
+ int c = mb_tolower(utf_ptr2char(from));
+ return utf_char2bytes(c, to);
}
- int len = utfc_ptr2len((char *)from);
+ int len = utfc_ptr2len(from);
memmove(to, from, (size_t)len);
return len;
}
@@ -3637,7 +3611,7 @@ static int copy_char(const char_u *from, char_u *to, bool lowercase)
/// corresponding button has a hotkey
///
/// @return Pointer to memory allocated for storing hotkeys
-static char_u *console_dialog_alloc(const char_u *message, char_u *buttons, bool has_hotkey[])
+static char *console_dialog_alloc(const char *message, char *buttons, bool has_hotkey[])
{
int lenhotkey = HOTK_LEN; // count first button
has_hotkey[0] = false;
@@ -3645,7 +3619,7 @@ static char_u *console_dialog_alloc(const char_u *message, char_u *buttons, bool
// Compute the size of memory to allocate.
int len = 0;
int idx = 0;
- char_u *r = buttons;
+ char *r = buttons;
while (*r) {
if (*r == DLG_BUTTON_SEP) {
len += 3; // '\n' -> ', '; 'x' -> '(x)'
@@ -3665,9 +3639,9 @@ static char_u *console_dialog_alloc(const char_u *message, char_u *buttons, bool
MB_PTR_ADV(r);
}
- len += (int)(STRLEN(message)
+ len += (int)(strlen(message)
+ 2 // for the NL's
- + STRLEN(buttons)
+ + strlen(buttons)
+ 3); // for the ": " and NUL
lenhotkey++; // for the NUL
@@ -3691,11 +3665,11 @@ static char_u *console_dialog_alloc(const char_u *message, char_u *buttons, bool
/// 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)
+static char *msg_show_console_dialog(char *message, char *buttons, int dfltbutton)
FUNC_ATTR_NONNULL_RET
{
bool has_hotkey[HAS_HOTKEY_LEN] = { false };
- char_u *hotk = console_dialog_alloc(message, buttons, has_hotkey);
+ char *hotk = console_dialog_alloc(message, buttons, has_hotkey);
copy_hotkeys_and_msg(message, buttons, dfltbutton, has_hotkey, hotk);
@@ -3711,13 +3685,13 @@ static char_u *msg_show_console_dialog(char_u *message, char_u *buttons, int dfl
/// @param has_hotkey An element in this array is true if corresponding button
/// has a hotkey
/// @param[out] hotkeys_ptr Pointer to the memory location where hotkeys will be copied
-static void copy_hotkeys_and_msg(const char_u *message, char_u *buttons, int default_button_idx,
- const bool has_hotkey[], char_u *hotkeys_ptr)
+static void copy_hotkeys_and_msg(const char *message, char *buttons, int default_button_idx,
+ const bool has_hotkey[], char *hotkeys_ptr)
{
*confirm_msg = '\n';
STRCPY(confirm_msg + 1, message);
- char_u *msgp = confirm_msg + 1 + STRLEN(message);
+ char *msgp = confirm_msg + 1 + strlen(message);
// Define first default hotkey. Keep the hotkey string NUL
// terminated to avoid reading past the end.
@@ -3734,14 +3708,14 @@ static void copy_hotkeys_and_msg(const char_u *message, char_u *buttons, int def
}
int idx = 0;
- char_u *r = buttons;
+ char *r = buttons;
while (*r) {
if (*r == DLG_BUTTON_SEP) {
*msgp++ = ',';
*msgp++ = ' '; // '\n' -> ', '
// Advance to next hotkey and set default hotkey
- hotkeys_ptr += STRLEN(hotkeys_ptr);
+ hotkeys_ptr += strlen(hotkeys_ptr);
hotkeys_ptr[copy_char(r + 1, hotkeys_ptr, true)] = NUL;
if (default_button_idx) {
@@ -3790,28 +3764,28 @@ void display_confirm_msg(void)
confirm_msg_used++;
if (confirm_msg != NULL) {
msg_ext_set_kind("confirm");
- msg_puts_attr((const char *)confirm_msg, HL_ATTR(HLF_M));
+ msg_puts_attr(confirm_msg, HL_ATTR(HLF_M));
}
confirm_msg_used--;
}
-int vim_dialog_yesno(int type, char_u *title, char_u *message, int dflt)
+int vim_dialog_yesno(int type, char *title, char *message, int dflt)
{
if (do_dialog(type,
- title == NULL ? (char_u *)_("Question") : title,
+ title == NULL ? _("Question") : title,
message,
- (char_u *)_("&Yes\n&No"), dflt, NULL, FALSE) == 1) {
+ _("&Yes\n&No"), dflt, NULL, false) == 1) {
return VIM_YES;
}
return VIM_NO;
}
-int vim_dialog_yesnocancel(int type, char_u *title, char_u *message, int dflt)
+int vim_dialog_yesnocancel(int type, char *title, char *message, int dflt)
{
switch (do_dialog(type,
- title == NULL ? (char_u *)_("Question") : title,
+ title == NULL ? _("Question") : title,
message,
- (char_u *)_("&Yes\n&No\n&Cancel"), dflt, NULL, FALSE)) {
+ _("&Yes\n&No\n&Cancel"), dflt, NULL, false)) {
case 1:
return VIM_YES;
case 2:
@@ -3820,13 +3794,13 @@ int vim_dialog_yesnocancel(int type, char_u *title, char_u *message, int dflt)
return VIM_CANCEL;
}
-int vim_dialog_yesnoallcancel(int type, char_u *title, char_u *message, int dflt)
+int vim_dialog_yesnoallcancel(int type, char *title, char *message, int dflt)
{
switch (do_dialog(type,
- title == NULL ? (char_u *)"Question" : title,
+ title == NULL ? "Question" : title,
message,
- (char_u *)_("&Yes\n&No\nSave &All\n&Discard All\n&Cancel"),
- dflt, NULL, FALSE)) {
+ _("&Yes\n&No\nSave &All\n&Discard All\n&Cancel"),
+ dflt, NULL, false)) {
case 1:
return VIM_YES;
case 2:
diff --git a/src/nvim/message.h b/src/nvim/message.h
index 2de2890213..191d3b8da7 100644
--- a/src/nvim/message.h
+++ b/src/nvim/message.h
@@ -5,15 +5,13 @@
#include <stdbool.h>
#include <stddef.h>
+#include "klib/kvec.h"
#include "nvim/api/private/defs.h"
#include "nvim/grid_defs.h"
-#include "nvim/lib/kvec.h"
#include "nvim/macros.h"
#include "nvim/types.h"
-/*
- * Types of dialogs passed to do_dialog().
- */
+// Types of dialogs passed to do_dialog().
#define VIM_GENERIC 0
#define VIM_ERROR 1
#define VIM_WARNING 2
@@ -21,9 +19,7 @@
#define VIM_QUESTION 4
#define VIM_LAST_TYPE 4 // sentinel value
-/*
- * Return values for functions like vim_dialogyesno()
- */
+// Return values for functions like vim_dialogyesno()
#define VIM_YES 2
#define VIM_NO 3
#define VIM_CANCEL 4
@@ -40,7 +36,7 @@ typedef kvec_t(HlMessageChunk) HlMessage;
/// Message history for `:messages`
typedef struct msg_hist {
struct msg_hist *next; ///< Next message.
- char_u *msg; ///< Message text.
+ char *msg; ///< Message text.
const char *kind; ///< Message kind (for msg_ext)
int attr; ///< Message highlighting.
bool multiline; ///< Multiline message.
@@ -70,6 +66,8 @@ 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);
+EXTERN int msg_grid_scroll_discount 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 a8d0b3b584..950b025e53 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -1,24 +1,47 @@
// 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 <stdlib.h>
+#include <string.h>
#include "nvim/ascii.h"
+#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
-#include "nvim/diff.h"
#include "nvim/drawscreen.h"
+#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_docmd.h"
#include "nvim/fold.h"
+#include "nvim/getchar.h"
+#include "nvim/globals.h"
#include "nvim/grid.h"
+#include "nvim/keycodes.h"
+#include "nvim/macros.h"
+#include "nvim/mark.h"
+#include "nvim/mbyte.h"
#include "nvim/memline.h"
+#include "nvim/menu.h"
+#include "nvim/message.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
-#include "nvim/os_unix.h"
+#include "nvim/normal.h"
+#include "nvim/ops.h"
+#include "nvim/option.h"
#include "nvim/plines.h"
+#include "nvim/pos.h"
+#include "nvim/screen.h"
+#include "nvim/search.h"
#include "nvim/state.h"
+#include "nvim/statusline.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/ui_compositor.h"
#include "nvim/vim.h"
@@ -31,8 +54,156 @@
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)
+/// 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 *p)
+{
+ if (MB_BYTE2LEN((uint8_t)p[0]) > 1) {
+ return mb_get_class(p);
+ }
+
+ const int c = (uint8_t)(*p);
+ if (c == ' ' || c == '\t') {
+ return 0;
+ }
+ if (vim_iswordc(c)) {
+ 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("-+*/%<>&|^!=", c) != NULL) {
+ return 1;
+ }
+ return c;
+}
+
+/// Move "pos" back to the start of the word it's in.
+static void find_start_of_word(pos_T *pos)
+{
+ char *line;
+ int cclass;
+ int col;
+
+ line = ml_get(pos->lnum);
+ cclass = get_mouse_class(line + pos->col);
+
+ while (pos->col > 0) {
+ col = pos->col - 1;
+ col -= utf_head_off(line, line + col);
+ if (get_mouse_class(line + col) != cclass) {
+ break;
+ }
+ pos->col = col;
+ }
+}
+
+/// 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 *line;
+ int cclass;
+ int col;
+
+ line = ml_get(pos->lnum);
+ if (*p_sel == 'e' && pos->col > 0) {
+ pos->col--;
+ pos->col -= utf_head_off(line, line + pos->col);
+ }
+ cclass = get_mouse_class(line + pos->col);
+ while (line[pos->col] != NUL) {
+ col = pos->col + utfc_ptr2len(line + pos->col);
+ if (get_mouse_class(line + col) != cclass) {
+ if (*p_sel == 'e') {
+ pos->col = col;
+ }
+ break;
+ }
+ pos->col = col;
+ }
+}
+
+/// 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;
+ if (tabnr <= 0) {
+ tabpage_move(9999);
+ } else if (tabnr < tabpage_index(curtab)) {
+ tabpage_move(tabnr - 1);
+ } else {
+ tabpage_move(tabnr);
+ }
+}
+
+/// 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);
+}
+
+/// Translate window coordinates to buffer position without any side effects.
+/// Returns IN_BUFFER and sets "mpos->col" to the column when in buffer text.
+/// The column is one for the first column.
+static int get_fpos_of_mouse(pos_T *mpos)
{
int grid = mouse_grid;
int row = mouse_row;
@@ -67,13 +238,724 @@ int get_fpos_of_mouse(pos_T *mpos)
mpos->col = vcol2col(wp, mpos->lnum, col);
- if (mpos->col > 0) {
- mpos->col--;
- }
mpos->coladd = 0;
return IN_BUFFER;
}
+/// Do the appropriate action for the current mouse click in the current mode.
+/// Not used for Command-line 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 (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
+/// middle press - yes if active no yank and put
+/// right press - yes start or extend yes
+/// right press S yes no change yes "#" (2)
+/// right drag - yes extend no
+/// right relse - yes extend no
+///
+/// Insert or Replace Mode:
+/// event modi- position visual change action
+/// fier cursor window
+/// left press - yes (cannot be active) yes
+/// left press C yes (cannot be active) yes "CTRL-O^]" (2)
+/// left press S yes (cannot be active) yes "CTRL-O*" (2)
+/// left drag - yes start or extend (1) no CTRL-O (1)
+/// left relse - yes start or extend (1) no CTRL-O (1)
+/// middle press - no (cannot be active) no put register
+/// right press - yes start or extend yes CTRL-O
+/// right press S yes (cannot be active) yes "CTRL-O#" (2)
+///
+/// (1) only if mouse pointer moved since press
+/// (2) only if click is in same buffer
+///
+/// @param oap operator argument, can be NULL
+/// @param c K_LEFTMOUSE, etc
+/// @param dir Direction to 'put' if necessary
+/// @param fixindent PUT_FIXINDENT if fixing indent necessary
+///
+/// @return true if start_arrow() should be called for edit mode.
+bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
+{
+ static bool got_click = false; // got a click some time back
+
+ int which_button; // MOUSE_LEFT, _MIDDLE or _RIGHT
+ bool is_click; // If false it's a drag or release event
+ bool is_drag; // If true it's a drag event
+ 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_statuscol; // mouse in status column
+ 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
+ int c1, c2;
+ pos_T save_cursor;
+ win_T *old_curwin = curwin;
+ static pos_T orig_cursor;
+ colnr_T leftcol, rightcol;
+ pos_T end_visual;
+ long diff;
+ int old_active = VIsual_active;
+ int old_mode = VIsual_mode;
+ int regname;
+
+ save_cursor = curwin->w_cursor;
+
+ 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.
+ // 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.
+ nc = safe_vgetc();
+ if (c == nc) {
+ continue;
+ }
+ vungetc(nc);
+ mouse_grid = save_mouse_grid;
+ mouse_row = save_mouse_row;
+ mouse_col = save_mouse_col;
+ }
+ }
+ break;
+ }
+
+ if (c == K_MOUSEMOVE) {
+ // Mouse moved without a button pressed.
+ return false;
+ }
+
+ // Ignore drag and release events if we didn't get a click.
+ if (is_click) {
+ got_click = true;
+ } else {
+ if (!got_click) { // didn't get click, ignore
+ return false;
+ }
+ if (!is_drag) { // release, reset got_click
+ got_click = false;
+ if (in_tab_line) {
+ in_tab_line = false;
+ return false;
+ }
+ }
+ }
+
+ // CTRL right mouse button does CTRL-T
+ if (is_click && (mod_mask & MOD_MASK_CTRL) && which_button == MOUSE_RIGHT) {
+ if (State & MODE_INSERT) {
+ stuffcharReadbuff(Ctrl_O);
+ }
+ if (count > 1) {
+ stuffnumReadbuff(count);
+ }
+ stuffcharReadbuff(Ctrl_T);
+ got_click = false; // ignore drag&release now
+ return false;
+ }
+
+ // 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.*".
+ if ((mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT
+ | MOD_MASK_META))
+ && (!is_click
+ || (mod_mask & MOD_MASK_MULTI_CLICK)
+ || which_button == MOUSE_MIDDLE)
+ && !((mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT))
+ && mouse_model_popup()
+ && which_button == MOUSE_LEFT)
+ && !((mod_mask & MOD_MASK_ALT)
+ && !mouse_model_popup()
+ && which_button == MOUSE_RIGHT)) {
+ 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 (!is_click && which_button == MOUSE_MIDDLE) {
+ return false;
+ }
+
+ if (oap != NULL) {
+ regname = oap->regname;
+ } else {
+ regname = 0;
+ }
+
+ // Middle mouse button does a 'put' of the selected text
+ if (which_button == MOUSE_MIDDLE) {
+ 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_active) {
+ if (VIsual_select) {
+ stuffcharReadbuff(Ctrl_G);
+ stuffReadbuff("\"+p");
+ } else {
+ stuffcharReadbuff('y');
+ stuffcharReadbuff(K_MIDDLEMOUSE);
+ }
+ return false;
+ }
+ // 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 & MODE_INSERT)) {
+ if (regname == '.') {
+ insert_reg(regname, true);
+ } else {
+ if (regname == 0 && eval_has_provider("clipboard")) {
+ regname = '*';
+ }
+ if ((State & REPLACE_FLAG) && !yank_register_mline(regname)) {
+ insert_reg(regname, true);
+ } else {
+ do_put(regname, NULL, BACKWARD, 1L,
+ (fixindent ? PUT_FIXINDENT : 0) | PUT_CURSEND);
+
+ // Repeat it with CTRL-R CTRL-O r or CTRL-R CTRL-P r
+ AppendCharToRedobuff(Ctrl_R);
+ AppendCharToRedobuff(fixindent ? Ctrl_P : Ctrl_O);
+ AppendCharToRedobuff(regname == 0 ? '"' : regname);
+ }
+ }
+ return false;
+ }
+ }
+
+ // When dragging or button-up stay in the same window.
+ if (!is_click) {
+ jump_flags |= MOUSE_FOCUS | MOUSE_DID_MOVE;
+ }
+
+ start_visual.lnum = 0;
+
+ if (tab_page_click_defs != NULL) { // only when initialized
+ // Check for clicking in the tab page line.
+ if (mouse_grid <= 1 && mouse_row == 0 && firstwin->w_winrow > 0) {
+ if (is_drag) {
+ if (in_tab_line) {
+ move_tab_to_mouse();
+ }
+ return false;
+ }
+
+ // click in a tab selects that tab page
+ if (is_click && cmdwin_type == 0 && mouse_col < Columns) {
+ in_tab_line = true;
+ c1 = tab_page_click_defs[mouse_col].tabnr;
+ switch (tab_page_click_defs[mouse_col].type) {
+ case kStlClickDisabled:
+ break;
+ case kStlClickTabClose: {
+ tabpage_T *tp;
+
+ // Close the current or specified tab page.
+ if (c1 == 999) {
+ tp = curtab;
+ } else {
+ tp = find_tabpage(c1);
+ }
+ if (tp == curtab) {
+ if (first_tabpage->tp_next != NULL) {
+ tabpage_close(false);
+ }
+ } else if (tp != NULL) {
+ tabpage_close_other(tp, false);
+ }
+ break;
+ }
+ case kStlClickTabSwitch:
+ if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) {
+ // double click opens new page
+ end_visual_mode();
+ tabpage_new();
+ tabpage_move(c1 == 0 ? 9999 : c1 - 1);
+ } else {
+ // Go to specified tab page, or next one if not clicking
+ // on a label.
+ goto_tabpage(c1);
+
+ // It's like clicking on the status line of a window.
+ if (curwin != old_curwin) {
+ end_visual_mode();
+ }
+ }
+ break;
+ case kStlClickFuncRun:
+ call_click_def_func(tab_page_click_defs, mouse_col, which_button);
+ break;
+ }
+ }
+ return true;
+ } else if (is_drag && in_tab_line) {
+ move_tab_to_mouse();
+ 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
+ if (mouse_model_popup()) {
+ if (which_button == MOUSE_RIGHT
+ && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) {
+ 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 (VIsual_mode == 'V') {
+ if ((curwin->w_cursor.lnum <= VIsual.lnum
+ && (m_pos.lnum < curwin->w_cursor.lnum || VIsual.lnum < m_pos.lnum))
+ || (VIsual.lnum < curwin->w_cursor.lnum
+ && (m_pos.lnum < VIsual.lnum || curwin->w_cursor.lnum < m_pos.lnum))) {
+ jump_flags = MOUSE_MAY_STOP_VIS;
+ }
+ } else if ((ltoreq(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);
+ redraw_curbuf_later(VIsual_active ? UPD_INVERTED : UPD_VALID);
+ update_screen();
+ 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))) {
+ which_button = MOUSE_RIGHT;
+ mod_mask &= ~MOD_MASK_SHIFT;
+ }
+ }
+
+ 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
+ if (VIsual_active) {
+ jump_flags |= MOUSE_MAY_STOP_VIS;
+ }
+ } else {
+ jump_flags |= MOUSE_MAY_VIS;
+ }
+ } else if (which_button == MOUSE_RIGHT) {
+ if (is_click && VIsual_active) {
+ // 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;
+ } else {
+ start_visual = VIsual;
+ end_visual = curwin->w_cursor;
+ }
+ }
+ jump_flags |= MOUSE_FOCUS;
+ jump_flags |= MOUSE_MAY_VIS;
+ }
+ }
+
+ // 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;
+ }
+
+ // When releasing the button let jump_to_mouse() know.
+ if (!is_click && !is_drag) {
+ jump_flags |= MOUSE_RELEASED;
+ }
+
+ // 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_statuscol = (jump_flags & MOUSE_STATUSCOL);
+ in_status_line = (jump_flags & IN_STATUS_LINE);
+ in_sep_line = (jump_flags & IN_SEP_LINE);
+
+ if ((in_winbar || in_status_line || in_statuscol) && 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
+ : in_winbar ? wp->w_winbar_click_defs
+ : wp->w_statuscol_click_defs;
+
+ if (in_status_line && global_stl_height() > 0) {
+ // global statusline is displayed for the current window,
+ // and spans the whole screen.
+ click_defs = curwin->w_status_click_defs;
+ click_col = mouse_col;
+ }
+
+ 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 || in_statuscol) {
+ // A drag or release event in the window bar and status column 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.
+ if (curwin != old_curwin && oap != NULL && oap->op_type != OP_NOP) {
+ clearop(oap);
+ }
+
+ if (mod_mask == 0
+ && !is_drag
+ && (jump_flags & (MOUSE_FOLD_CLOSE | MOUSE_FOLD_OPEN))
+ && which_button == MOUSE_LEFT) {
+ // open or close a fold at this line
+ if (jump_flags & MOUSE_FOLD_OPEN) {
+ openFold(curwin->w_cursor, 1L);
+ } else {
+ closeFold(curwin->w_cursor, 1L);
+ }
+ // don't move the cursor if still in the same window
+ if (curwin == old_curwin) {
+ curwin->w_cursor = save_cursor;
+ }
+ }
+
+ // 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) {
+ mouse_dragging = 2;
+ } else {
+ mouse_dragging = 1;
+ }
+ }
+
+ // When dragging the mouse above the window, scroll down.
+ if (is_drag && mouse_row < 0 && !in_status_line) {
+ scroll_redraw(false, 1L);
+ mouse_row = 0;
+ }
+
+ if (start_visual.lnum) { // right click in visual mode
+ // When ALT is pressed make Visual mode blockwise.
+ if (mod_mask & MOD_MASK_ALT) {
+ 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.
+ if (VIsual_mode == Ctrl_V) {
+ getvcols(curwin, &start_visual, &end_visual, &leftcol, &rightcol);
+ if (curwin->w_curswant > (leftcol + rightcol) / 2) {
+ end_visual.col = leftcol;
+ } else {
+ end_visual.col = rightcol;
+ }
+ if (curwin->w_cursor.lnum >=
+ (start_visual.lnum + end_visual.lnum) / 2) {
+ end_visual.lnum = start_visual.lnum;
+ }
+
+ // move VIsual to the right column
+ start_visual = curwin->w_cursor; // save the cursor pos
+ curwin->w_cursor = end_visual;
+ coladvance(end_visual.col);
+ 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 (lt(curwin->w_cursor, start_visual)) {
+ VIsual = end_visual;
+ } else if (lt(end_visual, curwin->w_cursor)) {
+ VIsual = start_visual;
+ } else {
+ // In the same line, compare column number
+ if (end_visual.lnum == start_visual.lnum) {
+ if (curwin->w_cursor.col - start_visual.col >
+ end_visual.col - curwin->w_cursor.col) {
+ VIsual = start_visual;
+ } else {
+ VIsual = end_visual;
+ }
+ } else {
+ // In different lines, compare line number
+ diff = (curwin->w_cursor.lnum - start_visual.lnum) -
+ (end_visual.lnum - curwin->w_cursor.lnum);
+
+ if (diff > 0) { // closest to end
+ VIsual = start_visual;
+ } else if (diff < 0) { // closest to start
+ VIsual = end_visual;
+ } else { // in the middle line
+ if (curwin->w_cursor.col <
+ (start_visual.col + end_visual.col) / 2) {
+ VIsual = end_visual;
+ } else {
+ VIsual = start_visual;
+ }
+ }
+ }
+ }
+ }
+ } 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.
+ if (which_button == MOUSE_MIDDLE) {
+ if (regname == 0 && eval_has_provider("clipboard")) {
+ regname = '*';
+ }
+ if (yank_register_mline(regname)) {
+ if (mouse_past_bottom) {
+ dir = FORWARD;
+ }
+ } else if (mouse_past_eol) {
+ dir = FORWARD;
+ }
+
+ if (fixindent) {
+ c1 = (dir == BACKWARD) ? '[' : ']';
+ c2 = 'p';
+ } else {
+ c1 = (dir == FORWARD) ? 'p' : 'P';
+ c2 = NUL;
+ }
+ prep_redo(regname, count, NUL, c1, NUL, c2, NUL);
+
+ // 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);
+ } 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
+ } 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
+ } 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) {
+ stuffcharReadbuff('*');
+ } else { // MOUSE_RIGHT
+ stuffcharReadbuff('#');
+ }
+ } 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;
+ } else {
+ VIsual = curwin->w_cursor;
+ orig_cursor = VIsual;
+ VIsual_active = true;
+ VIsual_reselect = true;
+ // start Select mode if 'selectmode' contains "mouse"
+ may_start_select('o');
+ setmouse();
+ }
+ if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) {
+ // Double click with ALT pressed makes it blockwise.
+ if (mod_mask & MOD_MASK_ALT) {
+ VIsual_mode = Ctrl_V;
+ } else {
+ VIsual_mode = 'v';
+ }
+ } else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_3CLICK) {
+ VIsual_mode = 'V';
+ } else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_4CLICK) {
+ VIsual_mode = Ctrl_V;
+ }
+ }
+ // 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.
+ end_visual = curwin->w_cursor;
+ while (gc = gchar_pos(&end_visual), ascii_iswhite(gc)) {
+ inc(&end_visual);
+ }
+ if (oap != NULL) {
+ oap->motion_type = kMTCharWise;
+ }
+ if (oap != NULL
+ && VIsual_mode == 'v'
+ && !vim_iswordc(gchar_pos(&end_visual))
+ && equalpos(curwin->w_cursor, VIsual)
+ && (pos = findmatch(oap, NUL)) != NULL) {
+ curwin->w_cursor = *pos;
+ if (oap->motion_type == kMTLineWise) {
+ VIsual_mode = 'V';
+ } else if (*p_sel == 'e') {
+ if (lt(curwin->w_cursor, VIsual)) {
+ VIsual.col++;
+ } else {
+ curwin->w_cursor.col++;
+ }
+ }
+ }
+ }
+
+ if (pos == NULL && (is_click || is_drag)) {
+ // 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);
+ } else {
+ find_start_of_word(&VIsual);
+ if (*p_sel == 'e' && *get_cursor_pos_ptr() != NUL) {
+ curwin->w_cursor.col +=
+ utfc_ptr2len(get_cursor_pos_ptr());
+ }
+ find_end_of_word(&curwin->w_cursor);
+ }
+ }
+ curwin->w_set_curswant = true;
+ }
+ if (is_click) {
+ redraw_curbuf_later(UPD_INVERTED); // update the inversion
+ }
+ } else if (VIsual_active && !old_active) {
+ if (mod_mask & MOD_MASK_ALT) {
+ VIsual_mode = Ctrl_V;
+ } else {
+ VIsual_mode = 'v';
+ }
+ }
+
+ // If Visual mode changed show it later.
+ if ((!VIsual_active && old_active && mode_displayed)
+ || (VIsual_active && p_smd && msg_silent == 0
+ && (!old_active || VIsual_mode != old_mode))) {
+ redraw_cmdline = true;
+ }
+
+ return moved;
+}
+
/// Return true if "c" is a mouse key.
bool is_mouse_key(int c)
{
@@ -100,6 +982,21 @@ bool is_mouse_key(int c)
|| c == K_X2DRAG
|| c == K_X2RELEASE;
}
+
+/// @return true when 'mousemodel' is set to "popup" or "popup_setpos".
+static bool mouse_model_popup(void)
+{
+ return p_mousem[0] == 'p';
+}
+
+static win_T *dragwin = NULL; ///< window being dragged
+
+/// Reset the window being dragged. To be called when switching tab page.
+void reset_dragwin(void)
+{
+ dragwin = NULL;
+}
+
/// Move the cursor to the specified row and column on the screen.
/// Change current window if necessary. Returns an integer with the
/// CURSOR_MOVED bit set if the cursor has moved or unset otherwise.
@@ -134,9 +1031,9 @@ int jump_to_mouse(int flags, bool *inclusive, int which_button)
static bool on_status_line = false;
static bool on_sep_line = false;
static bool on_winbar = false;
+ static bool on_statuscol = false;
static int prev_row = -1;
static int prev_col = -1;
- static win_T *dragwin = NULL; // window being dragged
static int did_drag = false; // drag was noticed
win_T *wp, *old_curwin;
@@ -177,9 +1074,12 @@ retnomove:
if (on_winbar) {
return IN_OTHER_WIN | MOUSE_WINBAR;
}
+ if (on_statuscol) {
+ return IN_OTHER_WIN | MOUSE_STATUSCOL;
+ }
if (flags & MOUSE_MAY_STOP_VIS) {
end_visual_mode();
- redraw_curbuf_later(INVERTED); // delete the inversion
+ redraw_curbuf_later(UPD_INVERTED); // delete the inversion
}
return IN_BUFFER;
}
@@ -211,6 +1111,10 @@ retnomove:
? wp->w_winbar_height != 0
: false;
+ on_statuscol = !on_status_line && !on_winbar && col < win_col_off(wp)
+ ? *wp->w_p_stc != NUL
+ : false;
+
on_sep_line = grid == DEFAULT_GRID_HANDLE && col >= wp->w_width
? col - wp->w_width + 1 == 1
: false;
@@ -238,6 +1142,10 @@ retnomove:
return IN_OTHER_WIN | MOUSE_WINBAR;
}
+ if (on_statuscol) {
+ return IN_OTHER_WIN | MOUSE_STATUSCOL;
+ }
+
fdc = win_fdccol_count(wp);
dragwin = NULL;
@@ -279,7 +1187,7 @@ retnomove:
: col >= fdc + (cmdwin_type == 0 && wp == curwin ? 0 : 1))
&& (flags & MOUSE_MAY_STOP_VIS)))) {
end_visual_mode();
- redraw_curbuf_later(INVERTED); // delete the inversion
+ redraw_curbuf_later(UPD_INVERTED); // delete the inversion
}
if (cmdwin_type != 0 && wp != curwin) {
// A click outside the command-line window: Use modeless
@@ -303,17 +1211,15 @@ retnomove:
// Don't use start_arrow() if we're in the same window
if (curwin == old_curwin) {
return IN_STATUS_LINE;
- } else {
- return IN_STATUS_LINE | CURSOR_MOVED;
}
+ return IN_STATUS_LINE | CURSOR_MOVED;
}
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;
- } else {
- return IN_SEP_LINE | CURSOR_MOVED;
}
+ return IN_SEP_LINE | CURSOR_MOVED;
}
curwin->w_cursor.lnum = curwin->w_topline;
@@ -340,12 +1246,15 @@ retnomove:
} 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 if (on_statuscol && which_button == MOUSE_RIGHT) {
+ // After a click on the status column don't start Visual mode.
+ return IN_OTHER_WIN | MOUSE_STATUSCOL;
} else {
// keep_window_focus must be true
// before moving the cursor for a left click, stop Visual mode
if (flags & MOUSE_MAY_STOP_VIS) {
end_visual_mode();
- redraw_curbuf_later(INVERTED); // delete the inversion
+ redraw_curbuf_later(UPD_INVERTED); // delete the inversion
}
if (grid == 0) {
@@ -374,14 +1283,14 @@ retnomove:
if (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline)) {
curwin->w_topfill++;
} else {
- --curwin->w_topline;
+ curwin->w_topline--;
curwin->w_topfill = 0;
}
}
check_topfill(curwin, false);
curwin->w_valid &=
~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
row = 0;
} else if (row >= curwin->w_height_inner) {
count = 0;
@@ -410,7 +1319,7 @@ retnomove:
}
}
check_topfill(curwin, false);
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
curwin->w_valid &=
~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
row = curwin->w_height_inner - 1;
@@ -631,14 +1540,16 @@ 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);
+ char *line = ml_get_buf(wp->w_buffer, lnum, false);
+ chartabsize_T cts;
+ init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
+ while (cts.cts_vcol < vcol && *cts.cts_ptr != NUL) {
+ cts.cts_vcol += win_lbr_chartabsize(&cts, NULL);
+ MB_PTR_ADV(cts.cts_ptr);
+ }
+ clear_chartabsize_arg(&cts);
+
+ return (colnr_T)(cts.cts_ptr - line);
}
/// Set UI mouse depending on current mode and 'mouse'.
@@ -652,7 +1563,7 @@ void setmouse(void)
// Set orig_topline. Used when jumping to another window, so that a double
// click still works.
-void set_mouse_topline(win_T *wp)
+static void set_mouse_topline(win_T *wp)
{
orig_topline = wp->w_topline;
orig_topfill = wp->w_topfill;
@@ -664,7 +1575,7 @@ void set_mouse_topline(win_T *wp)
static colnr_T scroll_line_len(linenr_T lnum)
{
colnr_T col = 0;
- char_u *line = ml_get(lnum);
+ char *line = ml_get(lnum);
if (*line != NUL) {
for (;;) {
int numchar = win_chartabsize(curwin, line, col);
@@ -702,8 +1613,8 @@ static linenr_T find_longest_lnum(void)
max = len;
ret = lnum;
} else if (len == (colnr_T)max
- && abs((int)(lnum - curwin->w_cursor.lnum))
- < abs((int)(ret - curwin->w_cursor.lnum))) {
+ && abs(lnum - curwin->w_cursor.lnum)
+ < abs(ret - curwin->w_cursor.lnum)) {
ret = lnum;
}
}
@@ -769,10 +1680,10 @@ static int mouse_adjust_click(win_T *wp, int row, int col)
// highlighting the second byte, not the ninth.
linenr_T lnum = wp->w_cursor.lnum;
- char_u *line = ml_get(lnum);
- char_u *ptr = line;
- char_u *ptr_end;
- char_u *ptr_row_offset = line; // Where we begin adjusting `ptr_end`
+ char *line = ml_get(lnum);
+ char *ptr = line;
+ char *ptr_end;
+ char *ptr_row_offset = line; // Where we begin adjusting `ptr_end`
// Find the offset where scanning should begin.
int offset = wp->w_leftcol;
@@ -791,7 +1702,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((char *)ptr);
+ ptr += utfc_ptr2len(ptr);
}
ptr_row_offset = ptr;
@@ -802,7 +1713,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((char *)ptr_end);
+ ptr_end += utfc_ptr2len(ptr_end);
}
int matchid;
@@ -844,7 +1755,7 @@ static int mouse_adjust_click(win_T *wp, int row, int col)
while (prev_matchid == matchid && *ptr != NUL) {
INCR();
- ptr += utfc_ptr2len((char *)ptr);
+ ptr += utfc_ptr2len(ptr);
matchid = syn_get_concealed_id(wp, lnum, (colnr_T)(ptr - line));
}
@@ -852,7 +1763,7 @@ static int mouse_adjust_click(win_T *wp, int row, int col)
}
}
- ptr += utfc_ptr2len((char *)ptr);
+ ptr += utfc_ptr2len(ptr);
}
return col + nudge;
diff --git a/src/nvim/mouse.h b/src/nvim/mouse.h
index 08261e4a30..8b2d7e0acd 100644
--- a/src/nvim/mouse.h
+++ b/src/nvim/mouse.h
@@ -4,42 +4,54 @@
#include <stdbool.h>
#include "nvim/buffer_defs.h"
+#include "nvim/normal.h"
#include "nvim/vim.h"
-
-// jump_to_mouse() returns one of first four these values, possibly with
-// some of the other three added.
-#define IN_UNKNOWN 0
-#define IN_BUFFER 1
-#define IN_STATUS_LINE 2 // on status or command line
-#define IN_SEP_LINE 4 // on vertical separator line
-#define IN_OTHER_WIN 8 // in other window but can't go there
-#define CURSOR_MOVED 0x100
-#define MOUSE_FOLD_CLOSE 0x200 // clicked on '-' in fold column
-#define MOUSE_FOLD_OPEN 0x400 // clicked on '+' in fold column
-#define MOUSE_WINBAR 0x800 // in window toolbar
-
-// flags for jump_to_mouse()
-#define MOUSE_FOCUS 0x01 // need to stay in this window
-#define MOUSE_MAY_VIS 0x02 // may start Visual mode
-#define MOUSE_DID_MOVE 0x04 // only act when mouse has moved
-#define MOUSE_SETPOS 0x08 // only set current mouse position
-#define MOUSE_MAY_STOP_VIS 0x10 // may stop Visual mode
-#define MOUSE_RELEASED 0x20 // button was released
-
-// Codes for mouse button events in lower three bits:
-#define MOUSE_LEFT 0x00
-#define MOUSE_MIDDLE 0x01
-#define MOUSE_RIGHT 0x02
-#define MOUSE_RELEASE 0x03
-
-#define MOUSE_X1 0x300 // Mouse-button X1 (6th)
-#define MOUSE_X2 0x400 // Mouse-button X2
-
-// 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)
+#include "nvim/window.h"
+
+/// jump_to_mouse() returns one of first five these values, possibly with
+/// some of the other five added.
+enum {
+ IN_UNKNOWN = 0,
+ IN_BUFFER = 1,
+ IN_STATUS_LINE = 2, ///< on status or command line
+ IN_SEP_LINE = 4, ///< on vertical separator line
+ IN_OTHER_WIN = 8, ///< in other window but can't go there
+ CURSOR_MOVED = 0x100,
+ MOUSE_FOLD_CLOSE = 0x200, ///< clicked on '-' in fold column
+ MOUSE_FOLD_OPEN = 0x400, ///< clicked on '+' in fold column
+ MOUSE_WINBAR = 0x800, ///< in window toolbar
+ MOUSE_STATUSCOL = 0x1000, ///< in 'statuscolumn'
+};
+
+/// flags for jump_to_mouse()
+enum {
+ MOUSE_FOCUS = 0x01, ///< need to stay in this window
+ MOUSE_MAY_VIS = 0x02, ///< may start Visual mode
+ MOUSE_DID_MOVE = 0x04, ///< only act when mouse has moved
+ MOUSE_SETPOS = 0x08, ///< only set current mouse position
+ MOUSE_MAY_STOP_VIS = 0x10, ///< may stop Visual mode
+ MOUSE_RELEASED = 0x20, ///< button was released
+};
+
+enum {
+ // Codes for mouse button events in lower three bits:
+ MOUSE_LEFT = 0x00,
+ MOUSE_MIDDLE = 0x01,
+ MOUSE_RIGHT = 0x02,
+ MOUSE_RELEASE = 0x03,
+
+ // mouse buttons that are handled like a key press
+ MOUSE_X1 = 0x300, ///< Mouse-button X1 (6th)
+ MOUSE_X2 = 0x400, ///< Mouse-button X2
+};
+
+/// Direction for nv_mousescroll() and ins_mousescroll()
+enum {
+ MSCR_DOWN = 0, ///< DOWN must be false
+ MSCR_UP = 1,
+ MSCR_LEFT = -1,
+ 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 1ed7acd012..3af26b910e 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -1,20 +1,19 @@
// 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
-/*
- * move.c: Functions for moving the cursor and scrolling text.
- *
- * There are two ways to move the cursor:
- * 1. Move the cursor directly, the text is scrolled to keep the cursor in the
- * window.
- * 2. Scroll the text, the cursor is moved into the text visible in the
- * window.
- * The 'scrolloff' option makes this a bit complicated.
- */
+// move.c: Functions for moving the cursor and scrolling text.
+//
+// There are two ways to move the cursor:
+// 1. Move the cursor directly, the text is scrolled to keep the cursor in the
+// window.
+// 2. Scroll the text, the cursor is moved into the text visible in the
+// window.
+// The 'scrolloff' option makes this a bit complicated.
#include <assert.h>
-#include <inttypes.h>
+#include <limits.h>
#include <stdbool.h>
+#include <stddef.h>
#include "nvim/ascii.h"
#include "nvim/buffer.h"
@@ -23,18 +22,30 @@
#include "nvim/diff.h"
#include "nvim/drawscreen.h"
#include "nvim/edit.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/eval/window.h"
#include "nvim/fold.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
#include "nvim/grid.h"
#include "nvim/highlight.h"
+#include "nvim/macros.h"
#include "nvim/mbyte.h"
-#include "nvim/memline.h"
+#include "nvim/memline_defs.h"
+#include "nvim/message.h"
+#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/plines.h"
#include "nvim/popupmenu.h"
+#include "nvim/pos.h"
+#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/strings.h"
+#include "nvim/types.h"
+#include "nvim/vim.h"
#include "nvim/window.h"
typedef struct {
@@ -47,19 +58,15 @@ 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.
- */
+// Compute wp->w_botline for the current wp->w_topline. Can be called after
+// wp->w_topline changed.
static void comp_botline(win_T *wp)
{
linenr_T lnum;
int done;
- /*
- * If w_cline_row is valid, start there.
- * Otherwise have to start at w_topline.
- */
+ // If w_cline_row is valid, start there.
+ // Otherwise have to start at w_topline.
check_cursor_moved(wp);
if (wp->w_valid & VALID_CROW) {
lnum = wp->w_cursor.lnum;
@@ -105,7 +112,7 @@ void redraw_for_cursorline(win_T *wp)
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_later(wp, UPD_VALID);
}
}
@@ -118,11 +125,11 @@ static void redraw_for_cursorcolumn(win_T *wp)
if ((wp->w_valid & VALID_VIRTCOL) == 0 && !pum_visible()) {
if (wp->w_p_cuc || ((HL_ATTR(HLF_LC) || win_hl_attr(wp, HLF_LC)) && using_hlsearch())) {
// When 'cursorcolumn' is set or "CurSearch" is in use
- // need to redraw with SOME_VALID.
- redraw_later(wp, SOME_VALID);
+ // need to redraw with UPD_SOME_VALID.
+ redraw_later(wp, UPD_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);
+ // When 'cursorlineopt' contains "screenline" need to redraw with UPD_VALID.
+ redraw_later(wp, UPD_VALID);
}
}
// If the cursor moves horizontally when 'concealcursor' is active, then the
@@ -133,21 +140,7 @@ static void redraw_for_cursorcolumn(win_T *wp)
}
}
-/*
- * Update curwin->w_topline and redraw if necessary.
- * Used to update the screen before printing a message.
- */
-void update_topline_redraw(void)
-{
- update_topline(curwin);
- if (must_redraw) {
- update_screen(0);
- }
-}
-
-/*
- * Update curwin->w_topline to move the cursor onto the screen.
- */
+// Update curwin->w_topline to move the cursor onto the screen.
void update_topline(win_T *wp)
{
linenr_T old_topline;
@@ -157,12 +150,16 @@ void update_topline(win_T *wp)
long *so_ptr = wp->w_p_so >= 0 ? &wp->w_p_so : &p_so;
long save_so = *so_ptr;
+ // Cursor is updated instead when this is true for 'splitkeep'.
+ if (skip_update_topline) {
+ return;
+ }
+
// If there is no valid screen and when the window height is zero just use
// the cursor line.
if (!default_grid.chars || wp->w_height_inner == 0) {
wp->w_topline = wp->w_cursor.lnum;
wp->w_botline = wp->w_topline;
- wp->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
wp->w_viewport_invalid = true;
wp->w_scbind_pos = 1;
return;
@@ -184,7 +181,7 @@ void update_topline(win_T *wp)
// If the buffer is empty, always set topline to 1.
if (buf_is_empty(curbuf)) { // special case - file is empty
if (wp->w_topline != 1) {
- redraw_later(wp, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
}
wp->w_topline = 1;
wp->w_botline = 2;
@@ -250,14 +247,12 @@ void update_topline(win_T *wp)
}
}
- /*
- * If the cursor is below the bottom of the window, scroll the window
- * to put the cursor on the window.
- * When w_botline is invalid, recompute it first, to avoid a redraw later.
- * If w_botline was approximated, we might need a redraw later in a few
- * cases, but we don't want to spend (a lot of) time recomputing w_botline
- * for every small change.
- */
+ // If the cursor is below the bottom of the window, scroll the window
+ // to put the cursor on the window.
+ // When w_botline is invalid, recompute it first, to avoid a redraw later.
+ // If w_botline was approximated, we might need a redraw later in a few
+ // cases, but we don't want to spend (a lot of) time recomputing w_botline
+ // for every small change.
if (check_botline) {
if (!(wp->w_valid & VALID_BOTLINE_AP)) {
validate_botline(wp);
@@ -328,17 +323,15 @@ void update_topline(win_T *wp)
wp->w_viewport_invalid = true;
win_check_anchored_floats(wp);
- /*
- * Need to redraw when topline changed.
- */
+ // Need to redraw when topline changed.
if (wp->w_topline != old_topline
|| wp->w_topfill != old_topfill) {
dollar_vcol = -1;
if (wp->w_skipcol != 0) {
wp->w_skipcol = 0;
- redraw_later(wp, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
} else {
- redraw_later(wp, VALID);
+ redraw_later(wp, UPD_VALID);
}
// May need to set w_skipcol when cursor in w_topline.
if (wp->w_cursor.lnum == wp->w_topline) {
@@ -349,22 +342,9 @@ void update_topline(win_T *wp)
*so_ptr = save_so;
}
-/*
- * Update win->w_topline to move the cursor onto the screen.
- */
-void update_topline_win(win_T *win)
-{
- switchwin_T switchwin;
- switch_win(&switchwin, win, NULL, true);
- update_topline(curwin);
- restore_win(&switchwin, true);
-}
-
-/*
- * Return the scrolljump value to use for the current window.
- * When 'scrolljump' is positive use it as-is.
- * When 'scrolljump' is negative use it as a percentage of the window height.
- */
+// Return the scrolljump value to use for the current window.
+// When 'scrolljump' is positive use it as-is.
+// When 'scrolljump' is negative use it as a percentage of the window height.
static int scrolljump_value(void)
{
long result = p_sj >= 0 ? p_sj : (curwin->w_height_inner * -p_sj) / 100;
@@ -372,10 +352,8 @@ static int scrolljump_value(void)
return (int)result;
}
-/*
- * Return true when there are not 'scrolloff' lines above the cursor for the
- * current window.
- */
+// Return true when there are not 'scrolloff' lines above the cursor for the
+// current window.
static bool check_top_offset(void)
{
long so = get_scrolloff_value(curwin);
@@ -403,18 +381,23 @@ static bool check_top_offset(void)
return false;
}
+/// Update w_curswant.
+void update_curswant_force(void)
+{
+ validate_virtcol();
+ curwin->w_curswant = curwin->w_virtcol;
+ curwin->w_set_curswant = false;
+}
+
+/// Update w_curswant if w_set_curswant is set.
void update_curswant(void)
{
if (curwin->w_set_curswant) {
- validate_virtcol();
- curwin->w_curswant = curwin->w_virtcol;
- curwin->w_set_curswant = false;
+ update_curswant_force();
}
}
-/*
- * Check if the cursor has moved. Set the w_valid flag accordingly.
- */
+// Check if the cursor has moved. Set the w_valid flag accordingly.
void check_cursor_moved(win_T *wp)
{
if (wp->w_cursor.lnum != wp->w_valid_cursor.lnum) {
@@ -435,11 +418,9 @@ void check_cursor_moved(win_T *wp)
}
}
-/*
- * Call this function when some window settings have changed, which require
- * the cursor position, botline and topline to be recomputed and the window to
- * be redrawn. E.g, when changing the 'wrap' option or folding.
- */
+// Call this function when some window settings have changed, which require
+// the cursor position, botline and topline to be recomputed and the window to
+// be redrawn. E.g, when changing the 'wrap' option or folding.
void changed_window_setting(void)
{
changed_window_setting_win(curwin);
@@ -450,12 +431,10 @@ void changed_window_setting_win(win_T *wp)
wp->w_lines_valid = 0;
changed_line_abv_curs_win(wp);
wp->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP|VALID_TOPLINE);
- redraw_later(wp, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
}
-/*
- * Set wp->w_topline to a certain number.
- */
+// Set wp->w_topline to a certain number.
void set_topline(win_T *wp, linenr_T lnum)
{
linenr_T prev_topline = wp->w_topline;
@@ -472,14 +451,12 @@ void set_topline(win_T *wp, linenr_T lnum)
}
wp->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_TOPLINE);
// Don't set VALID_TOPLINE here, 'scrolloff' needs to be checked.
- redraw_later(wp, VALID);
+ redraw_later(wp, UPD_VALID);
}
-/*
- * Call this function when the length of the cursor line (in screen
- * characters) has changed, and the change is before the cursor.
- * Need to take care of w_botline separately!
- */
+// Call this function when the length of the cursor line (in screen
+// characters) has changed, and the change is before the cursor.
+// Need to take care of w_botline separately!
void changed_cline_bef_curs(void)
{
curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL
@@ -492,11 +469,9 @@ void changed_cline_bef_curs_win(win_T *wp)
|VALID_CHEIGHT|VALID_TOPLINE);
}
-/*
- * Call this function when the length of a line (in screen characters) above
- * the cursor have changed.
- * Need to take care of w_botline separately!
- */
+// Call this function when the length of a line (in screen characters) above
+// the cursor have changed.
+// Need to take care of w_botline separately!
void changed_line_abv_curs(void)
{
curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL|VALID_CROW
@@ -509,9 +484,7 @@ void changed_line_abv_curs_win(win_T *wp)
|VALID_CHEIGHT|VALID_TOPLINE);
}
-/*
- * Make sure the value of curwin->w_botline is valid.
- */
+// Make sure the value of curwin->w_botline is valid.
void validate_botline(win_T *wp)
{
if (!(wp->w_valid & VALID_BOTLINE)) {
@@ -519,9 +492,7 @@ void validate_botline(win_T *wp)
}
}
-/*
- * Mark curwin->w_botline as invalid (because of some change in the buffer).
- */
+// Mark curwin->w_botline as invalid (because of some change in the buffer).
void invalidate_botline(void)
{
curwin->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP);
@@ -537,9 +508,7 @@ void approximate_botline_win(win_T *wp)
wp->w_valid &= ~VALID_BOTLINE;
}
-/*
- * Return true if curwin->w_wrow and curwin->w_wcol are valid.
- */
+// Return true if curwin->w_wrow and curwin->w_wcol are valid.
int cursor_valid(void)
{
check_cursor_moved(curwin);
@@ -547,10 +516,8 @@ int cursor_valid(void)
(VALID_WROW|VALID_WCOL);
}
-/*
- * Validate cursor position. Makes sure w_wrow and w_wcol are valid.
- * w_topline must be valid, you may need to call update_topline() first!
- */
+// Validate cursor position. Makes sure w_wrow and w_wcol are valid.
+// w_topline must be valid, you may need to call update_topline() first!
void validate_cursor(void)
{
check_cursor_moved(curwin);
@@ -559,10 +526,8 @@ void validate_cursor(void)
}
}
-/*
- * Compute wp->w_cline_row and wp->w_cline_height, based on the current value
- * of wp->w_topline.
- */
+// Compute wp->w_cline_row and wp->w_cline_height, based on the current value
+// of wp->w_topline.
static void curs_rows(win_T *wp)
{
// Check if wp->w_lines[].wl_size is invalid
@@ -571,7 +536,7 @@ static void curs_rows(win_T *wp)
|| wp->w_lines[0].wl_lnum > wp->w_topline);
int i = 0;
wp->w_cline_row = 0;
- for (linenr_T lnum = wp->w_topline; lnum < wp->w_cursor.lnum; ++i) {
+ for (linenr_T lnum = wp->w_topline; lnum < wp->w_cursor.lnum; i++) {
bool valid = false;
if (!all_invalid && i < wp->w_lines_valid) {
if (wp->w_lines[i].wl_lnum < lnum || !wp->w_lines[i].wl_valid) {
@@ -587,7 +552,7 @@ static void curs_rows(win_T *wp)
valid = true;
}
} else if (wp->w_lines[i].wl_lnum > lnum) {
- --i; // hold at inserted lines
+ i--; // hold at inserted lines
}
}
if (valid && (lnum != wp->w_topline || !win_may_fill(wp))) {
@@ -633,77 +598,77 @@ static void curs_rows(win_T *wp)
wp->w_valid |= VALID_CROW|VALID_CHEIGHT;
}
-/*
- * Validate curwin->w_virtcol only.
- */
+// Validate curwin->w_virtcol only.
void validate_virtcol(void)
{
validate_virtcol_win(curwin);
}
-/*
- * Validate wp->w_virtcol only.
- */
+// Validate wp->w_virtcol only.
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_valid & VALID_VIRTCOL) {
+ return;
}
+
+ getvvcol(wp, &wp->w_cursor, NULL, &(wp->w_virtcol), NULL);
+ redraw_for_cursorcolumn(wp);
+ wp->w_valid |= VALID_VIRTCOL;
}
-/*
- * Validate curwin->w_cline_height only.
- */
+// Validate curwin->w_cline_height only.
void validate_cheight(void)
{
check_cursor_moved(curwin);
- if (!(curwin->w_valid & VALID_CHEIGHT)) {
- curwin->w_cline_height = plines_win_full(curwin, curwin->w_cursor.lnum,
- NULL, &curwin->w_cline_folded,
- true);
- curwin->w_valid |= VALID_CHEIGHT;
+
+ if (curwin->w_valid & VALID_CHEIGHT) {
+ return;
}
+
+ curwin->w_cline_height = plines_win_full(curwin, curwin->w_cursor.lnum,
+ NULL, &curwin->w_cline_folded,
+ true);
+ curwin->w_valid |= VALID_CHEIGHT;
}
-/*
- * Validate w_wcol and w_virtcol only.
- */
+// Validate w_wcol and w_virtcol only.
void validate_cursor_col(void)
{
validate_virtcol();
- if (!(curwin->w_valid & VALID_WCOL)) {
- colnr_T col = curwin->w_virtcol;
- colnr_T off = curwin_col_off();
- col += off;
- int width = curwin->w_width_inner - off + curwin_col_off2();
-
- // long line wrapping, adjust curwin->w_wrow
- if (curwin->w_p_wrap && col >= (colnr_T)curwin->w_width_inner
- && width > 0) {
- // use same formula as what is used in curs_columns()
- col -= ((col - curwin->w_width_inner) / width + 1) * width;
- }
- if (col > (int)curwin->w_leftcol) {
- col -= curwin->w_leftcol;
- } else {
- col = 0;
- }
- curwin->w_wcol = col;
- curwin->w_valid |= VALID_WCOL;
+ if (curwin->w_valid & VALID_WCOL) {
+ return;
}
+
+ colnr_T col = curwin->w_virtcol;
+ colnr_T off = curwin_col_off();
+ col += off;
+ int width = curwin->w_width_inner - off + curwin_col_off2();
+
+ // long line wrapping, adjust curwin->w_wrow
+ if (curwin->w_p_wrap && col >= (colnr_T)curwin->w_width_inner
+ && width > 0) {
+ // use same formula as what is used in curs_columns()
+ col -= ((col - curwin->w_width_inner) / width + 1) * width;
+ }
+ if (col > (int)curwin->w_leftcol) {
+ col -= curwin->w_leftcol;
+ } else {
+ col = 0;
+ }
+ curwin->w_wcol = col;
+
+ curwin->w_valid |= VALID_WCOL;
}
-/*
- * Compute offset of a window, occupied by absolute or relative line number,
- * fold column and sign column (these don't move when scrolling horizontally).
- */
+// Compute offset of a window, occupied by absolute or relative line number,
+// fold column and sign column (these don't move when scrolling horizontally).
int win_col_off(win_T *wp)
{
- return ((wp->w_p_nu || wp->w_p_rnu) ? number_width(wp) + 1 : 0)
+ return ((wp->w_p_nu || wp->w_p_rnu || (*wp->w_p_stc != NUL)) ?
+ (number_width(wp) + (*wp->w_p_stc == NUL)) : 0)
+ (cmdwin_type == 0 || wp != curwin ? 0 : 1)
+ win_fdccol_count(wp)
+ (win_signcol_count(wp) * win_signcol_width(wp));
@@ -714,11 +679,9 @@ int curwin_col_off(void)
return win_col_off(curwin);
}
-/*
- * Return the difference in column offset for the second screen line of a
- * wrapped line. It's 8 if 'number' or 'relativenumber' is on and 'n' is in
- * 'cpoptions'.
- */
+// Return the difference in column offset for the second screen line of a
+// wrapped line. It's 8 if 'number' or 'relativenumber' is on and 'n' is in
+// 'cpoptions'.
int win_col_off2(win_T *wp)
{
if ((wp->w_p_nu || wp->w_p_rnu) && vim_strchr(p_cpo, CPO_NUMCOL) != NULL) {
@@ -746,9 +709,7 @@ void curs_columns(win_T *wp, int may_scroll)
long so = get_scrolloff_value(wp);
long siso = get_sidescrolloff_value(wp);
- /*
- * First make sure that w_topline is valid (after moving the cursor).
- */
+ // First make sure that w_topline is valid (after moving the cursor).
update_topline(wp);
// Next make sure that w_cline_row is valid.
@@ -756,9 +717,7 @@ void curs_columns(win_T *wp, int may_scroll)
curs_rows(wp);
}
- /*
- * Compute the number of virtual columns.
- */
+ // Compute the number of virtual columns.
if (wp->w_cline_folded) {
// In a folded line the cursor is always in the first column
startcol = wp->w_virtcol = endcol = wp->w_leftcol;
@@ -801,7 +760,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 *const sbr = (char *)get_showbreak_value(wp);
+ char *const sbr = get_showbreak_value(wp);
if (*sbr && *get_cursor_pos_ptr() == NUL
&& wp->w_wcol == vim_strsize(sbr)) {
wp->w_wcol = 0;
@@ -847,7 +806,7 @@ void curs_columns(win_T *wp, int may_scroll)
wp->w_leftcol = new_leftcol;
win_check_anchored_floats(wp);
// screen has to be redrawn with new wp->w_leftcol
- redraw_later(wp, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
}
}
wp->w_wcol -= wp->w_leftcol;
@@ -871,8 +830,7 @@ void curs_columns(win_T *wp, int may_scroll)
if ((wp->w_wrow >= wp->w_height_inner
|| ((prev_skipcol > 0
|| wp->w_wrow + so >= wp->w_height_inner)
- && (plines =
- plines_win_nofill(wp, wp->w_cursor.lnum, false)) - 1
+ && (plines = plines_win_nofill(wp, wp->w_cursor.lnum, false)) - 1
>= wp->w_height_inner))
&& wp->w_height_inner != 0
&& wp->w_cursor.lnum == wp->w_topline
@@ -954,7 +912,7 @@ void curs_columns(win_T *wp, int may_scroll)
wp->w_skipcol = 0;
}
if (prev_skipcol != wp->w_skipcol) {
- redraw_later(wp, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
}
redraw_for_cursorcolumn(curwin);
@@ -980,11 +938,16 @@ void textpos2screenpos(win_T *wp, pos_T *pos, int *rowp, int *scolp, int *ccolp,
int rowoff = 0;
colnr_T coloff = 0;
bool visible_row = false;
-
- if (pos->lnum >= wp->w_topline && pos->lnum < wp->w_botline) {
- row = plines_m_win(wp, wp->w_topline, pos->lnum - 1) + 1;
+ bool is_folded = false;
+
+ if (pos->lnum >= wp->w_topline && pos->lnum <= wp->w_botline) {
+ linenr_T lnum = pos->lnum;
+ is_folded = hasFoldingWin(wp, lnum, &lnum, NULL, true, NULL);
+ row = plines_m_win(wp, wp->w_topline, lnum - 1) + 1;
+ // Add filler lines above this buffer line.
+ row += win_get_fill(wp, lnum);
visible_row = true;
- } else if (pos->lnum < wp->w_topline) {
+ } else if (!local || pos->lnum < wp->w_topline) {
row = 0;
} else {
row = wp->w_height_inner;
@@ -993,46 +956,108 @@ void textpos2screenpos(win_T *wp, pos_T *pos, int *rowp, int *scolp, int *ccolp,
bool existing_row = (pos->lnum > 0
&& pos->lnum <= wp->w_buffer->b_ml.ml_line_count);
- if ((local && existing_row) || visible_row) {
- colnr_T off;
- colnr_T col;
- int width;
-
- getvcol(wp, pos, &scol, &ccol, &ecol);
-
- // similar to what is done in validate_cursor_col()
- col = scol;
- off = win_col_off(wp);
- col += off;
- width = wp->w_width - off + win_col_off2(wp);
-
- // long line wrapping, adjust row
- if (wp->w_p_wrap && col >= (colnr_T)wp->w_width && width > 0) {
- // use same formula as what is used in curs_columns()
- rowoff = visible_row ? ((col - wp->w_width) / width + 1) : 0;
- col -= rowoff * width;
- }
+ if ((local || visible_row) && existing_row) {
+ const colnr_T off = win_col_off(wp);
+ if (is_folded) {
+ row += local ? 0 : wp->w_winrow + wp->w_winrow_off;
+ coloff = (local ? 0 : wp->w_wincol + wp->w_wincol_off) + 1 + off;
+ } else {
+ getvcol(wp, pos, &scol, &ccol, &ecol);
+
+ // similar to what is done in validate_cursor_col()
+ colnr_T col = scol;
+ col += off;
+ int width = wp->w_width - off + win_col_off2(wp);
+
+ // long line wrapping, adjust row
+ if (wp->w_p_wrap && col >= (colnr_T)wp->w_width && width > 0) {
+ // use same formula as what is used in curs_columns()
+ rowoff = visible_row ? ((col - wp->w_width) / width + 1) : 0;
+ col -= rowoff * width;
+ }
- col -= wp->w_leftcol;
+ col -= wp->w_leftcol;
- if (col >= 0 && col < wp->w_width) {
- 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
- if (local) {
- coloff = col < 0 ? -1 : wp->w_width_inner + 1;
+ if (col >= 0 && col < wp->w_width && row + rowoff <= wp->w_height) {
+ coloff = col - scol + (local ? 0 : wp->w_wincol + wp->w_wincol_off) + 1;
+ row += local ? 0 : wp->w_winrow + wp->w_winrow_off;
} else {
- row = 0;
+ // character is left, right or below of the window
+ scol = ccol = ecol = 0;
+ if (local) {
+ coloff = col < 0 ? -1 : wp->w_width_inner + 1;
+ } else {
+ row = rowoff = 0;
+ }
}
}
}
- *rowp = (local ? 0 : wp->w_winrow + wp->w_winrow_off) + row + rowoff;
+ *rowp = row + rowoff;
*scolp = scol + coloff;
*ccolp = ccol + coloff;
*ecolp = ecol + coloff;
}
+/// "screenpos({winid}, {lnum}, {col})" function
+void f_screenpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ tv_dict_alloc_ret(rettv);
+ dict_T *dict = rettv->vval.v_dict;
+
+ win_T *wp = find_win_by_nr_or_id(&argvars[0]);
+ if (wp == NULL) {
+ return;
+ }
+
+ pos_T pos = {
+ .lnum = (linenr_T)tv_get_number(&argvars[1]),
+ .col = (colnr_T)tv_get_number(&argvars[2]) - 1,
+ .coladd = 0
+ };
+ if (pos.lnum > wp->w_buffer->b_ml.ml_line_count) {
+ semsg(_(e_invalid_line_number_nr), pos.lnum);
+ return;
+ }
+ int row = 0;
+ int scol = 0, ccol = 0, ecol = 0;
+ textpos2screenpos(wp, &pos, &row, &scol, &ccol, &ecol, false);
+
+ tv_dict_add_nr(dict, S_LEN("row"), row);
+ tv_dict_add_nr(dict, S_LEN("col"), scol);
+ tv_dict_add_nr(dict, S_LEN("curscol"), ccol);
+ tv_dict_add_nr(dict, S_LEN("endcol"), ecol);
+}
+
+/// "virtcol2col({winid}, {lnum}, {col})" function
+void f_virtcol2col(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->vval.v_number = -1;
+
+ if (tv_check_for_number_arg(argvars, 0) == FAIL
+ || tv_check_for_number_arg(argvars, 1) == FAIL
+ || tv_check_for_number_arg(argvars, 2) == FAIL) {
+ return;
+ }
+
+ win_T *wp = find_win_by_nr_or_id(&argvars[0]);
+ if (wp == NULL) {
+ return;
+ }
+
+ bool error = false;
+ linenr_T lnum = (linenr_T)tv_get_number_chk(&argvars[1], &error);
+ if (error || lnum < 0 || lnum > wp->w_buffer->b_ml.ml_line_count) {
+ return;
+ }
+
+ int screencol = (int)tv_get_number_chk(&argvars[2], &error);
+ if (error || screencol < 0) {
+ return;
+ }
+
+ rettv->vval.v_number = vcol2col(wp, lnum, screencol);
+}
+
/// Scroll the current window down by "line_count" logical lines. "CTRL-Y"
///
/// @param line_count number of lines to scroll
@@ -1053,7 +1078,7 @@ bool scrolldown(long line_count, int byfold)
if (curwin->w_topline == 1) {
break;
}
- --curwin->w_topline;
+ curwin->w_topline--;
curwin->w_topfill = 0;
// A sequence of folded lines only counts for one logical line
linenr_T first;
@@ -1068,7 +1093,7 @@ bool scrolldown(long line_count, int byfold)
done += plines_win_nofill(curwin, curwin->w_topline, true);
}
}
- --curwin->w_botline; // approximate w_botline
+ curwin->w_botline--; // approximate w_botline
invalidate_botline();
}
curwin->w_wrow += done; // keep w_wrow updated
@@ -1079,10 +1104,8 @@ bool scrolldown(long line_count, int byfold)
}
check_topfill(curwin, true);
- /*
- * Compute the row number of the last row of the cursor line
- * and move the cursor onto the displayed part of the window.
- */
+ // Compute the row number of the last row of the cursor line
+ // 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) {
validate_virtcol();
@@ -1130,7 +1153,7 @@ bool scrollup(long line_count, int byfold)
linenr_T lnum = curwin->w_topline;
while (line_count--) {
if (curwin->w_topfill > 0) {
- --curwin->w_topfill;
+ curwin->w_topfill--;
} else {
if (byfold) {
(void)hasFolding(lnum, NULL, &lnum);
@@ -1187,7 +1210,7 @@ void check_topfill(win_T *wp, bool down)
int n = plines_win_nofill(wp, wp->w_topline, true);
if (wp->w_topfill + n > wp->w_height_inner) {
if (down && wp->w_topline > 1) {
- --wp->w_topline;
+ wp->w_topline--;
wp->w_topfill = 0;
} else {
wp->w_topfill = wp->w_height_inner - n;
@@ -1200,10 +1223,8 @@ void check_topfill(win_T *wp, bool down)
win_check_anchored_floats(curwin);
}
-/*
- * Use as many filler lines as possible for w_topline. Make sure w_topline
- * is still visible.
- */
+// Use as many filler lines as possible for w_topline. Make sure w_topline
+// is still visible.
static void max_topfill(void)
{
int n = plines_win_nofill(curwin, curwin->w_topline, true);
@@ -1217,10 +1238,8 @@ static void max_topfill(void)
}
}
-/*
- * Scroll the screen one line down, but don't do it if it would move the
- * cursor off the screen.
- */
+// Scroll the screen one line down, but don't do it if it would move the
+// cursor off the screen.
void scrolldown_clamp(void)
{
int can_fill = (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline));
@@ -1249,22 +1268,20 @@ void scrolldown_clamp(void)
}
if (end_row < curwin->w_height_inner - get_scrolloff_value(curwin)) {
if (can_fill) {
- ++curwin->w_topfill;
+ curwin->w_topfill++;
check_topfill(curwin, true);
} else {
- --curwin->w_topline;
+ curwin->w_topline--;
curwin->w_topfill = 0;
}
(void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
- --curwin->w_botline; // approximate w_botline
+ curwin->w_botline--; // approximate w_botline
curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
}
}
-/*
- * Scroll the screen one line up, but don't do it if it would move the cursor
- * off the screen.
- */
+// Scroll the screen one line up, but don't do it if it would move the cursor
+// off the screen.
void scrollup_clamp(void)
{
if (curwin->w_topline == curbuf->b_ml.ml_line_count
@@ -1296,12 +1313,10 @@ void scrollup_clamp(void)
}
}
-/*
- * Add one line above "lp->lnum". This can be a filler line, a closed fold or
- * a (wrapped) text line. Uses and sets "lp->fill".
- * Returns the height of the added line in "lp->height".
- * Lines above the first one are incredibly high: MAXCOL.
- */
+// Add one line above "lp->lnum". This can be a filler line, a closed fold or
+// a (wrapped) text line. Uses and sets "lp->fill".
+// Returns the height of the added line in "lp->height".
+// Lines above the first one are incredibly high: MAXCOL.
static void topline_back(win_T *wp, lineoff_T *lp)
{
if (lp->fill < win_get_fill(wp, lp->lnum)) {
@@ -1309,7 +1324,7 @@ static void topline_back(win_T *wp, lineoff_T *lp)
lp->fill++;
lp->height = 1;
} else {
- --lp->lnum;
+ lp->lnum--;
lp->fill = 0;
if (lp->lnum < 1) {
lp->height = MAXCOL;
@@ -1322,12 +1337,10 @@ static void topline_back(win_T *wp, lineoff_T *lp)
}
}
-/*
- * Add one line below "lp->lnum". This can be a filler line, a closed fold or
- * a (wrapped) text line. Uses and sets "lp->fill".
- * Returns the height of the added line in "lp->height".
- * Lines below the last one are incredibly high.
- */
+// Add one line below "lp->lnum". This can be a filler line, a closed fold or
+// a (wrapped) text line. Uses and sets "lp->fill".
+// Returns the height of the added line in "lp->height".
+// Lines below the last one are incredibly high.
static void botline_forw(win_T *wp, lineoff_T *lp)
{
if (lp->fill < win_get_fill(wp, lp->lnum + 1)) {
@@ -1335,7 +1348,7 @@ static void botline_forw(win_T *wp, lineoff_T *lp)
lp->fill++;
lp->height = 1;
} else {
- ++lp->lnum;
+ lp->lnum++;
lp->fill = 0;
assert(wp->w_buffer != 0);
if (lp->lnum > wp->w_buffer->b_ml.ml_line_count) {
@@ -1349,11 +1362,9 @@ static void botline_forw(win_T *wp, lineoff_T *lp)
}
}
-/*
- * Switch from including filler lines below lp->lnum to including filler
- * lines above loff.lnum + 1. This keeps pointing to the same line.
- * When there are no filler lines nothing changes.
- */
+// Switch from including filler lines below lp->lnum to including filler
+// lines above loff.lnum + 1. This keeps pointing to the same line.
+// When there are no filler lines nothing changes.
static void botline_topline(lineoff_T *lp)
{
if (lp->fill > 0) {
@@ -1362,11 +1373,9 @@ static void botline_topline(lineoff_T *lp)
}
}
-/*
- * Switch from including filler lines above lp->lnum to including filler
- * lines below loff.lnum - 1. This keeps pointing to the same line.
- * When there are no filler lines nothing changes.
- */
+// Switch from including filler lines above lp->lnum to including filler
+// lines below loff.lnum - 1. This keeps pointing to the same line.
+// When there are no filler lines nothing changes.
static void topline_botline(lineoff_T *lp)
{
if (lp->fill > 0) {
@@ -1375,11 +1384,9 @@ static void topline_botline(lineoff_T *lp)
}
}
-/*
- * Recompute topline to put the cursor at the top of the window.
- * Scroll at least "min_scroll" lines.
- * If "always" is true, always set topline (for "zt").
- */
+// Recompute topline to put the cursor at the top of the window.
+// Scroll at least "min_scroll" lines.
+// If "always" is true, always set topline (for "zt").
void scroll_cursor_top(int min_scroll, int always)
{
int scrolled = 0;
@@ -1394,13 +1401,11 @@ void scroll_cursor_top(int min_scroll, int always)
off = mouse_dragging - 1;
}
- /*
- * Decrease topline until:
- * - it has become 1
- * - (part of) the cursor line is moved off the screen or
- * - moved at least 'scrolljump' lines and
- * - at least 'scrolloff' lines above and below the cursor
- */
+ // Decrease topline until:
+ // - it has become 1
+ // - (part of) the cursor line is moved off the screen or
+ // - moved at least 'scrolljump' lines and
+ // - at least 'scrolloff' lines above and below the cursor
validate_cheight();
int used = curwin->w_cline_height; // includes filler lines above
if (curwin->w_cursor.lnum < curwin->w_topline) {
@@ -1421,10 +1426,8 @@ void scroll_cursor_top(int min_scroll, int always)
// Hide filler lines above cursor line by adding them to "extra".
int extra = win_get_fill(curwin, curwin->w_cursor.lnum);
- /*
- * Check if the lines from "top" to "bot" fit in the window. If they do,
- * set new_topline and advance "top" and "bot" to include more lines.
- */
+ // Check if the lines from "top" to "bot" fit in the window. If they do,
+ // set new_topline and advance "top" and "bot" to include more lines.
while (top > 0) {
int i = hasFolding(top, &top, NULL)
? 1 // count one logical line for a sequence of folded lines
@@ -1445,9 +1448,7 @@ void scroll_cursor_top(int min_scroll, int always)
scrolled += i;
}
- /*
- * If scrolling is needed, scroll at least 'sj' lines.
- */
+ // If scrolling is needed, scroll at least 'sj' lines.
if ((new_topline >= curwin->w_topline || scrolled > min_scroll)
&& extra >= off) {
break;
@@ -1459,18 +1460,14 @@ void scroll_cursor_top(int min_scroll, int always)
bot++;
}
- /*
- * If we don't have enough space, put cursor in the middle.
- * This makes sure we get the same position when using "k" and "j"
- * in a small window.
- */
+ // If we don't have enough space, put cursor in the middle.
+ // This makes sure we get the same position when using "k" and "j"
+ // in a small window.
if (used > curwin->w_height_inner) {
scroll_cursor_halfway(false);
} else {
- /*
- * If "always" is false, only adjust topline to a lower value, higher
- * value may happen with wrapping lines
- */
+ // If "always" is false, only adjust topline to a lower value, higher
+ // value may happen with wrapping lines
if (new_topline < curwin->w_topline || always) {
curwin->w_topline = new_topline;
}
@@ -1495,10 +1492,8 @@ void scroll_cursor_top(int min_scroll, int always)
}
}
-/*
- * Set w_empty_rows and w_filler_rows for window "wp", having used up "used"
- * screen lines for text lines.
- */
+// Set w_empty_rows and w_filler_rows for window "wp", having used up "used"
+// screen lines for text lines.
void set_empty_rows(win_T *wp, int used)
{
wp->w_filler_rows = 0;
@@ -1577,13 +1572,11 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
}
}
- /*
- * Stop counting lines to scroll when
- * - hitting start of the file
- * - scrolled nothing or at least 'sj' lines
- * - at least 'so' lines below the cursor
- * - lines between botline and cursor have been counted
- */
+ // Stop counting lines to scroll when
+ // - hitting start of the file
+ // - scrolled nothing or at least 'sj' lines
+ // - at least 'so' lines below the cursor
+ // - lines between botline and cursor have been counted
if (!hasFolding(curwin->w_cursor.lnum, &loff.lnum, &boff.lnum)) {
loff.lnum = cln;
boff.lnum = cln;
@@ -1673,21 +1666,17 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
}
}
- /*
- * Scroll up if the cursor is off the bottom of the screen a bit.
- * Otherwise put it at 1/2 of the screen.
- */
+ // Scroll up if the cursor is off the bottom of the screen a bit.
+ // Otherwise put it at 1/2 of the screen.
if (line_count >= curwin->w_height_inner && line_count > min_scroll) {
scroll_cursor_halfway(false);
} else {
scrollup(line_count, true);
}
- /*
- * If topline didn't change we need to restore w_botline and w_empty_rows
- * (we changed them).
- * If topline did change, update_screen() will set botline.
- */
+ // If topline didn't change we need to restore w_botline and w_empty_rows
+ // (we changed them).
+ // If topline did change, update_screen() will set botline.
if (curwin->w_topline == old_topline && set_topbot) {
curwin->w_botline = old_botline;
curwin->w_empty_rows = old_empty_rows;
@@ -1726,7 +1715,7 @@ void scroll_cursor_halfway(int atend)
}
below += boff.height;
} else {
- ++below; // count a "~" line
+ below++; // count a "~" line
if (atend) {
used++;
}
@@ -1760,18 +1749,14 @@ void scroll_cursor_halfway(int atend)
curwin->w_valid |= VALID_TOPLINE;
}
-/*
- * Correct the cursor position so that it is in a part of the screen at least
- * 'so' lines from the top and bottom, if possible.
- * If not possible, put it at the same position as scroll_cursor_halfway().
- * When called topline must be valid!
- */
+// Correct the cursor position so that it is in a part of the screen at least
+// 'so' lines from the top and bottom, if possible.
+// If not possible, put it at the same position as scroll_cursor_halfway().
+// When called topline must be valid!
void cursor_correct(void)
{
- /*
- * How many lines we would like to have above/below the cursor depends on
- * whether the first/last line of the file is on screen.
- */
+ // How many lines we would like to have above/below the cursor depends on
+ // whether the first/last line of the file is on screen.
int above_wanted = (int)get_scrolloff_value(curwin);
int below_wanted = (int)get_scrolloff_value(curwin);
if (mouse_dragging > 0) {
@@ -1795,10 +1780,8 @@ void cursor_correct(void)
}
}
- /*
- * If there are sufficient file-lines above and below the cursor, we can
- * return now.
- */
+ // If there are sufficient file-lines above and below the cursor, we can
+ // return now.
linenr_T cln = curwin->w_cursor.lnum; // Cursor Line Number
if (cln >= curwin->w_topline + above_wanted
&& cln < curwin->w_botline - below_wanted
@@ -1806,12 +1789,10 @@ void cursor_correct(void)
return;
}
- /*
- * Narrow down the area where the cursor can be put by taking lines from
- * the top and the bottom until:
- * - the desired context lines are found
- * - the lines from the top is past the lines from the bottom
- */
+ // Narrow down the area where the cursor can be put by taking lines from
+ // the top and the bottom until:
+ // - the desired context lines are found
+ // - the lines from the top is past the lines from the bottom
linenr_T topline = curwin->w_topline;
linenr_T botline = curwin->w_botline - 1;
// count filler lines as context
@@ -1860,11 +1841,9 @@ void cursor_correct(void)
curwin->w_viewport_invalid = true;
}
-/*
- * move screen 'count' pages up or down and update screen
- *
- * return FAIL for failure, OK otherwise
- */
+// move screen 'count' pages up or down and update screen
+//
+// return FAIL for failure, OK otherwise
int onepage(Direction dir, long count)
{
long n;
@@ -1899,7 +1878,7 @@ int onepage(Direction dir, long count)
if (ONE_WINDOW && p_window > 0 && p_window < Rows - 1) {
// Vi compatible scrolling
if (p_window <= 2) {
- ++curwin->w_topline;
+ curwin->w_topline++;
} else {
curwin->w_topline += (linenr_T)p_window - 2;
}
@@ -1935,7 +1914,7 @@ int onepage(Direction dir, long count)
if (ONE_WINDOW && p_window > 0 && p_window < Rows - 1) {
// Vi compatible scrolling (sort of)
if (p_window <= 2) {
- --curwin->w_topline;
+ curwin->w_topline--;
} else {
curwin->w_topline -= (linenr_T)p_window - 2;
}
@@ -2000,7 +1979,7 @@ int onepage(Direction dir, long count)
max_topfill();
}
if (curwin->w_topfill == loff.fill) {
- --curwin->w_topline;
+ curwin->w_topline--;
curwin->w_topfill = 0;
}
comp_botline(curwin);
@@ -2040,22 +2019,20 @@ int onepage(Direction dir, long count)
}
}
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
return retval;
}
-/*
- * Decide how much overlap to use for page-up or page-down scrolling.
- * This is symmetric, so that doing both keeps the same lines displayed.
- * Three lines are examined:
- *
- * before CTRL-F after CTRL-F / before CTRL-B
- * etc. l1
- * l1 last but one line ------------
- * l2 last text line l2 top text line
- * ------------- l3 second text line
- * l3 etc.
- */
+// Decide how much overlap to use for page-up or page-down scrolling.
+// This is symmetric, so that doing both keeps the same lines displayed.
+// Three lines are examined:
+//
+// before CTRL-F after CTRL-F / before CTRL-B
+// etc. l1
+// l1 last but one line ------------
+// l2 last text line l2 top text line
+// ------------- l3 second text line
+// l3 etc.
static void get_scroll_overlap(lineoff_T *lp, int dir)
{
int min_height = curwin->w_height_inner - 2;
@@ -2125,9 +2102,7 @@ void halfpage(bool flag, linenr_T Prenum)
validate_botline(curwin);
int room = curwin->w_empty_rows + curwin->w_filler_rows;
if (flag) {
- /*
- * scroll the text up
- */
+ // scroll the text up
while (n > 0 && curwin->w_botline <= curbuf->b_ml.ml_line_count) {
if (curwin->w_topfill > 0) {
i = 1;
@@ -2144,7 +2119,7 @@ void halfpage(bool flag, linenr_T Prenum)
curwin->w_topfill = win_get_fill(curwin, curwin->w_topline);
if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
- ++curwin->w_cursor.lnum;
+ curwin->w_cursor.lnum++;
curwin->w_valid &=
~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL);
}
@@ -2177,7 +2152,7 @@ void halfpage(bool flag, linenr_T Prenum)
&& curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
(void)hasFolding(curwin->w_cursor.lnum, NULL,
&curwin->w_cursor.lnum);
- ++curwin->w_cursor.lnum;
+ curwin->w_cursor.lnum++;
}
} else {
curwin->w_cursor.lnum += n;
@@ -2185,9 +2160,7 @@ void halfpage(bool flag, linenr_T Prenum)
check_cursor_lnum();
}
} else {
- /*
- * scroll the text down
- */
+ // scroll the text down
while (n > 0 && curwin->w_topline > 1) {
if (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline)) {
i = 1;
@@ -2199,7 +2172,7 @@ void halfpage(bool flag, linenr_T Prenum)
if (n < 0 && scrolled > 0) {
break;
}
- --curwin->w_topline;
+ curwin->w_topline--;
(void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
curwin->w_topfill = 0;
}
@@ -2207,7 +2180,7 @@ void halfpage(bool flag, linenr_T Prenum)
VALID_BOTLINE|VALID_BOTLINE_AP);
scrolled += i;
if (curwin->w_cursor.lnum > 1) {
- --curwin->w_cursor.lnum;
+ curwin->w_cursor.lnum--;
curwin->w_valid &= ~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL);
}
}
@@ -2218,7 +2191,7 @@ void halfpage(bool flag, linenr_T Prenum)
curwin->w_cursor.lnum = 1;
} else if (hasAnyFolding(curwin)) {
while (--n >= 0 && curwin->w_cursor.lnum > 1) {
- --curwin->w_cursor.lnum;
+ curwin->w_cursor.lnum--;
(void)hasFolding(curwin->w_cursor.lnum,
&curwin->w_cursor.lnum, NULL);
}
@@ -2232,7 +2205,7 @@ void halfpage(bool flag, linenr_T Prenum)
check_topfill(curwin, !flag);
cursor_correct();
beginline(BL_SOL | BL_FIX);
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
}
void do_check_cursorbind(void)
@@ -2247,9 +2220,7 @@ void do_check_cursorbind(void)
int old_VIsual_select = VIsual_select;
int old_VIsual_active = VIsual_active;
- /*
- * loop through the cursorbound windows
- */
+ // loop through the cursorbound windows
VIsual_select = VIsual_active = 0;
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
curwin = wp;
@@ -2284,7 +2255,7 @@ void do_check_cursorbind(void)
}
// Correct cursor for multi-byte character.
mb_adjust_cursor();
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
// Only scroll when 'scrollbind' hasn't done this.
if (!curwin->w_p_scb) {
@@ -2294,9 +2265,7 @@ void do_check_cursorbind(void)
}
}
- /*
- * reset current-window
- */
+ // reset current-window
VIsual_select = old_VIsual_select;
VIsual_active = old_VIsual_active;
curwin = old_curwin;
diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c
index 1ca68979f4..d60e18590f 100644
--- a/src/nvim/msgpack_rpc/channel.c
+++ b/src/nvim/msgpack_rpc/channel.c
@@ -1,36 +1,43 @@
// 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 <msgpack.h>
+#include <msgpack/object.h>
+#include <msgpack/pack.h>
+#include <msgpack/sbuffer.h>
+#include <msgpack/unpack.h>
#include <stdbool.h>
-#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <uv.h>
+#include "klib/kvec.h"
+#include "nvim/api/private/defs.h"
+#include "nvim/api/private/dispatch.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/ui.h"
-#include "nvim/api/vim.h"
-#include "nvim/ascii.h"
#include "nvim/channel.h"
-#include "nvim/eval.h"
-#include "nvim/event/libuv_process.h"
+#include "nvim/event/defs.h"
#include "nvim/event/loop.h"
+#include "nvim/event/process.h"
#include "nvim/event/rstream.h"
-#include "nvim/event/socket.h"
+#include "nvim/event/stream.h"
#include "nvim/event/wstream.h"
-#include "nvim/lib/kvec.h"
#include "nvim/log.h"
#include "nvim/main.h"
#include "nvim/map.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/msgpack_rpc/channel.h"
+#include "nvim/msgpack_rpc/channel_defs.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/rbuffer.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
-#include "nvim/vim.h"
+#include "nvim/ui_client.h"
#if MIN_LOG_LEVEL > LOGLVL_DBG
# define log_client_msg(...)
@@ -94,7 +101,6 @@ bool rpc_send_event(uint64_t id, const char *name, Array args)
Channel *channel = NULL;
if (id && (!(channel = find_rpc_channel(id)))) {
- api_free_array(args);
return false;
}
@@ -130,6 +136,7 @@ Object rpc_send_call(uint64_t id, const char *method_name, Array args, ArenaMem
uint32_t request_id = rpc->next_request_id++;
// Send the msgpack-rpc request
send_request(channel, request_id, method_name, args);
+ api_free_array(args);
// Push the frame
ChannelCallFrame frame = { request_id, false, false, NIL, NULL };
@@ -158,7 +165,7 @@ Object rpc_send_call(uint64_t id, const char *method_name, Array args, ArenaMem
}
// frame.result was allocated in an arena
- arena_mem_free(frame.result_mem, &rpc->unpacker->reuse_blk);
+ arena_mem_free(frame.result_mem);
frame.result_mem = NULL;
}
@@ -244,8 +251,8 @@ static void parse_msgpack(Channel *channel)
ui_client_event_raw_line(p->grid_line_event);
} else if (p->ui_handler.fn != NULL && p->result.type == kObjectTypeArray) {
p->ui_handler.fn(p->result.data.array);
- arena_mem_free(arena_finish(&p->arena), &p->reuse_blk);
}
+ arena_mem_free(arena_finish(&p->arena));
} else if (p->type == kMessageTypeResponse) {
ChannelCallFrame *frame = kv_last(channel->rpc.call_stack);
if (p->request_id != frame->request_id) {
@@ -293,9 +300,9 @@ static void handle_request(Channel *channel, Unpacker *p, Array args)
assert(p->type == kMessageTypeRequest || p->type == kMessageTypeNotification);
if (!p->handler.fn) {
- send_error(channel, p->type, p->request_id, p->unpack_error.msg);
+ send_error(channel, p->handler, 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);
+ arena_mem_free(arena_finish(&p->arena));
return;
}
@@ -304,7 +311,8 @@ static void handle_request(Channel *channel, Unpacker *p, Array args)
evdata->channel = channel;
evdata->handler = p->handler;
evdata->args = args;
- evdata->used_mem = arena_finish(&p->arena);
+ evdata->used_mem = p->arena;
+ p->arena = (Arena)ARENA_EMPTY;
evdata->request_id = p->request_id;
channel_incref(channel);
if (p->handler.fast) {
@@ -344,24 +352,27 @@ static void request_event(void **argv)
// channel was closed, abort any pending requests
goto free_ret;
}
- Object result = handler.fn(channel->id, e->args, &error);
+
+ Object result = handler.fn(channel->id, e->args, &e->used_mem, &error);
if (e->type == kMessageTypeRequest || ERROR_SET(&error)) {
// Send the response.
msgpack_packer response;
msgpack_packer_init(&response, &out_buffer, msgpack_sbuffer_write);
channel_write(channel, serialize_response(channel->id,
+ e->handler,
e->type,
e->request_id,
&error,
result,
&out_buffer));
- } else {
+ }
+ if (!handler.arena_return) {
api_free_object(result);
}
free_ret:
- // e->args is allocated in an arena
- arena_mem_free(e->used_mem, &channel->rpc.unpacker->reuse_blk);
+ // e->args (and possibly result) are allocated in an arena
+ arena_mem_free(arena_finish(&e->used_mem));
channel_decref(channel);
xfree(e);
api_clear_error(&error);
@@ -431,11 +442,13 @@ static void internal_read_event(void **argv)
wstream_release_wbuffer(buffer);
}
-static void send_error(Channel *chan, MessageType type, uint32_t id, char *err)
+static void send_error(Channel *chan, MsgpackRpcRequestHandler handler, MessageType type,
+ uint32_t id, char *err)
{
Error e = ERROR_INIT;
api_set_error(&e, kErrorTypeException, "%s", err);
channel_write(chan, serialize_response(chan->id,
+ handler,
type,
id,
&e,
@@ -479,7 +492,6 @@ static void broadcast_event(const char *name, Array args)
});
if (!kv_size(subscribed)) {
- api_free_array(args);
goto end;
}
@@ -534,26 +546,8 @@ void rpc_close(Channel *channel)
channel_decref(channel);
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);
+ || (channel->id == ui_client_channel_id && channel->streamtype != kChannelStreamProc)) {
+ exit_from_channel(0);
}
}
@@ -599,22 +593,30 @@ static WBuffer *serialize_request(uint64_t channel_id, uint32_t request_id, cons
refcount,
xfree);
msgpack_sbuffer_clear(sbuffer);
- api_free_array(args);
return rv;
}
-static WBuffer *serialize_response(uint64_t channel_id, MessageType type, uint32_t response_id,
- Error *err, Object arg, msgpack_sbuffer *sbuffer)
+static WBuffer *serialize_response(uint64_t channel_id, MsgpackRpcRequestHandler handler,
+ MessageType type, uint32_t response_id, Error *err, Object arg,
+ msgpack_sbuffer *sbuffer)
{
msgpack_packer pac;
msgpack_packer_init(&pac, sbuffer, msgpack_sbuffer_write);
if (ERROR_SET(err) && type == kMessageTypeNotification) {
- Array args = ARRAY_DICT_INIT;
- ADD(args, INTEGER_OBJ(err->type));
- ADD(args, STRING_OBJ(cstr_to_string(err->msg)));
- msgpack_rpc_serialize_request(0, cstr_as_string("nvim_error_event"),
- args, &pac);
- api_free_array(args);
+ if (handler.fn == handle_nvim_paste) {
+ // TODO(bfredl): this is pretty much ad-hoc. maybe TUI and UI:s should be
+ // allowed to ask nvim to just scream directly in the users face
+ // instead of sending nvim_error_event, in general.
+ semsg("paste: %s", err->msg);
+ api_clear_error(err);
+ } else {
+ Array args = ARRAY_DICT_INIT;
+ ADD(args, INTEGER_OBJ(err->type));
+ ADD(args, STRING_OBJ(cstr_to_string(err->msg)));
+ msgpack_rpc_serialize_request(0, cstr_as_string("nvim_error_event"),
+ args, &pac);
+ api_free_array(args);
+ }
} else {
msgpack_rpc_serialize_response(response_id, err, arg, &pac);
}
@@ -624,7 +626,6 @@ static WBuffer *serialize_response(uint64_t channel_id, MessageType type, uint32
1, // responses only go though 1 channel
xfree);
msgpack_sbuffer_clear(sbuffer);
- api_free_object(arg);
return rv;
}
@@ -642,7 +643,7 @@ void rpc_set_client_info(uint64_t id, Dictionary info)
Dictionary rpc_client_info(Channel *chan)
{
- return copy_dictionary(chan->rpc.info);
+ return copy_dictionary(chan->rpc.info, NULL);
}
const char *rpc_client_name(Channel *chan)
diff --git a/src/nvim/msgpack_rpc/channel.h b/src/nvim/msgpack_rpc/channel.h
index ac7911bb2c..ce5806930c 100644
--- a/src/nvim/msgpack_rpc/channel.h
+++ b/src/nvim/msgpack_rpc/channel.h
@@ -6,8 +6,10 @@
#include "nvim/api/private/defs.h"
#include "nvim/channel.h"
+#include "nvim/event/multiqueue.h"
#include "nvim/event/process.h"
#include "nvim/event/socket.h"
+#include "nvim/macros.h"
#include "nvim/vim.h"
#define METHOD_MAXLEN 512
diff --git a/src/nvim/msgpack_rpc/channel_defs.h b/src/nvim/msgpack_rpc/channel_defs.h
index e622ebddf5..404e68329a 100644
--- a/src/nvim/msgpack_rpc/channel_defs.h
+++ b/src/nvim/msgpack_rpc/channel_defs.h
@@ -27,7 +27,7 @@ typedef struct {
MsgpackRpcRequestHandler handler;
Array args;
uint32_t request_id;
- ArenaMem used_mem;
+ Arena used_mem;
} RequestEvent;
typedef struct {
diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c
index 488321be42..5f0f03dd69 100644
--- a/src/nvim/msgpack_rpc/helpers.c
+++ b/src/nvim/msgpack_rpc/helpers.c
@@ -1,21 +1,25 @@
// 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 <inttypes.h>
-#include <msgpack.h>
+#include <msgpack/object.h>
+#include <msgpack/sbuffer.h>
+#include <msgpack/unpack.h>
+#include <msgpack/zone.h>
#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
-#include "nvim/api/private/dispatch.h"
+#include "klib/kvec.h"
+#include "msgpack/pack.h"
#include "nvim/api/private/helpers.h"
#include "nvim/assert.h"
-#include "nvim/lib/kvec.h"
-#include "nvim/log.h"
+#include "nvim/event/wstream.h"
#include "nvim/memory.h"
#include "nvim/msgpack_rpc/helpers.h"
-#include "nvim/vim.h"
+#include "nvim/types.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "keysets.generated.h"
+# include "keysets.generated.h" // IWYU pragma: export
# include "msgpack_rpc/helpers.c.generated.h"
#endif
@@ -35,6 +39,8 @@ typedef struct {
size_t idx;
} MPToAPIObjectStackItem;
+// uncrustify:off
+
/// Convert type used by msgpack parser to Nvim API type.
///
/// @param[in] obj Msgpack value to convert.
@@ -95,9 +101,8 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg)
dest = conv(((String) { \
.size = obj->via.attr.size, \
.data = (obj->via.attr.ptr == NULL || obj->via.attr.size == 0 \
- ? xmemdupz("", 0) \
- : xmemdupz(obj->via.attr.ptr, obj->via.attr.size)), \
- })); \
+ ? xmemdupz("", 0) \
+ : xmemdupz(obj->via.attr.ptr, obj->via.attr.size)), })); \
break; \
}
STR_CASE(MSGPACK_OBJECT_STR, str, cur.mobj, *cur.aobj, STRING_OBJ)
@@ -115,7 +120,7 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg)
.mobj = &cur.mobj->via.array.ptr[idx],
.aobj = &cur.aobj->data.array.items[idx],
.container = false,
- }));
+ }));
}
} else {
*cur.aobj = ARRAY_OBJ(((Array) {
@@ -124,7 +129,7 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg)
.items = (size > 0
? xcalloc(size, sizeof(*cur.aobj->data.array.items))
: NULL),
- }));
+ }));
cur.container = true;
kv_last(stack) = cur;
}
@@ -168,7 +173,7 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg)
.mobj = &cur.mobj->via.map.ptr[idx].val,
.aobj = &cur.aobj->data.dictionary.items[idx].value,
.container = false,
- }));
+ }));
}
}
} else {
@@ -178,7 +183,7 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg)
.items = (size > 0
? xcalloc(size, sizeof(*cur.aobj->data.dictionary.items))
: NULL),
- }));
+ }));
cur.container = true;
kv_last(stack) = cur;
}
@@ -368,7 +373,7 @@ void msgpack_rpc_from_object(const Object result, msgpack_packer *const res)
kvi_push(stack, ((APIToMPObjectStackItem) {
.aobj = &cur.aobj->data.array.items[idx],
.container = false,
- }));
+ }));
}
} else {
msgpack_pack_array(res, size);
@@ -386,12 +391,11 @@ void msgpack_rpc_from_object(const Object result, msgpack_packer *const res)
const size_t idx = cur.idx;
cur.idx++;
kv_last(stack) = cur;
- msgpack_rpc_from_string(cur.aobj->data.dictionary.items[idx].key,
- res);
+ msgpack_rpc_from_string(cur.aobj->data.dictionary.items[idx].key, res);
kvi_push(stack, ((APIToMPObjectStackItem) {
.aobj = &cur.aobj->data.dictionary.items[idx].value,
.container = false,
- }));
+ }));
}
} else {
msgpack_pack_map(res, size);
@@ -408,6 +412,8 @@ void msgpack_rpc_from_object(const Object result, msgpack_packer *const res)
kvi_destroy(stack);
}
+// uncrustify:on
+
void msgpack_rpc_from_array(Array result, msgpack_packer *res)
FUNC_ATTR_NONNULL_ARG(2)
{
diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c
index b252f0998e..1d75c208be 100644
--- a/src/nvim/msgpack_rpc/server.c
+++ b/src/nvim/msgpack_rpc/server.c
@@ -1,25 +1,22 @@
// 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 <stdlib.h>
+#include <stdbool.h>
+#include <stdio.h>
#include <string.h>
+#include <uv.h>
-#include "nvim/ascii.h"
+#include "nvim/channel.h"
#include "nvim/eval.h"
#include "nvim/event/socket.h"
-#include "nvim/fileio.h"
#include "nvim/garray.h"
#include "nvim/log.h"
#include "nvim/main.h"
#include "nvim/memory.h"
-#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/server.h"
#include "nvim/os/os.h"
-#include "nvim/path.h"
-#include "nvim/strings.h"
-#include "nvim/vim.h"
+#include "nvim/os/stdpaths_defs.h"
#define MAX_CONNECTIONS 32
#define ENV_LISTEN "NVIM_LISTEN_ADDRESS" // deprecated
@@ -94,7 +91,7 @@ char *server_address_new(const char *name)
{
static uint32_t count = 0;
char fmt[ADDRESS_MAX_SIZE];
-#ifdef WIN32
+#ifdef MSWIN
int r = snprintf(fmt, sizeof(fmt), "\\\\.\\pipe\\%s.%" PRIu64 ".%" PRIu32,
name ? name : "nvim", os_get_pid(), count++);
#else
@@ -104,7 +101,7 @@ char *server_address_new(const char *name)
xfree(dir);
#endif
if ((size_t)r >= sizeof(fmt)) {
- ELOG("truncated server address");
+ ELOG("truncated server address: %.40s...", fmt);
}
return xstrdup(fmt);
}
@@ -173,7 +170,7 @@ int server_start(const char *addr)
((SocketWatcher **)watchers.ga_data)[watchers.ga_len++] = watcher;
// Update v:servername, if not set.
- if (STRLEN(get_vim_var_str(VV_SEND_SERVER)) == 0) {
+ if (strlen(get_vim_var_str(VV_SEND_SERVER)) == 0) {
set_vservername(&watchers);
}
@@ -216,7 +213,7 @@ bool server_stop(char *endpoint)
watchers.ga_len--;
// Bump v:servername to the next available server, if any.
- if (strequal(addr, (char *)get_vim_var_str(VV_SEND_SERVER))) {
+ if (strequal(addr, 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
index c8e9fdd4c3..44a16beb48 100644
--- a/src/nvim/msgpack_rpc/unpacker.c
+++ b/src/nvim/msgpack_rpc/unpacker.c
@@ -1,9 +1,17 @@
// 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 <stdlib.h>
+
+#include "klib/kvec.h"
+#include "mpack/conv.h"
#include "nvim/api/private/helpers.h"
-#include "nvim/log.h"
+#include "nvim/ascii.h"
+#include "nvim/macros.h"
#include "nvim/memory.h"
+#include "nvim/msgpack_rpc/channel_defs.h"
#include "nvim/msgpack_rpc/helpers.h"
#include "nvim/msgpack_rpc/unpacker.h"
#include "nvim/ui_client.h"
@@ -178,16 +186,14 @@ 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->unpack_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);
+ arena_mem_free(arena_finish(&p->arena));
}
bool unpacker_parse_header(Unpacker *p)
@@ -292,7 +298,7 @@ error:
//
// When method is "grid_line", we furthermore decode a cell at a time like:
//
-// <0>[2, "redraw", <10>[{11}["grid_line", <13>[g, r, c, [<14>[cell], <14>[cell], ...]], ...], <11>[...], ...]]
+// <0>[2, "redraw", <10>[{11}["grid_line", <14>[g, r, c, [<15>[cell], <15>[cell], ...]], ...], <11>[...], ...]]
//
// where [cell] is [char, repeat, attr], where 'repeat' and 'attr' is optional
@@ -308,21 +314,23 @@ bool unpacker_advance(Unpacker *p)
p->state = 10;
} else {
p->state = p->type == kMessageTypeResponse ? 1 : 2;
- arena_start(&p->arena, &p->reuse_blk);
+ p->arena = (Arena)ARENA_EMPTY;
}
}
- if (p->state >= 10 && p->state != 12) {
+ if (p->state >= 10 && p->state != 13) {
if (!unpacker_parse_redraw(p)) {
return false;
}
- if (p->state == 14) {
+ if (p->state == 15) {
// grid_line event already unpacked
goto done;
} else {
+ assert(p->state == 12);
// unpack other ui events using mpack_parse()
- arena_start(&p->arena, &p->reuse_blk);
+ p->arena = (Arena)ARENA_EMPTY;
+ p->state = 13;
}
}
@@ -349,11 +357,11 @@ done:
case 2:
p->state = 0;
return true;
- case 12:
- case 14:
+ case 13:
+ case 15:
p->ncalls--;
if (p->ncalls > 0) {
- p->state = (p->state == 14) ? 13 : 12;
+ p->state = (p->state == 15) ? 14 : 12;
} else if (p->nevents > 0) {
p->state = 11;
} else {
@@ -374,6 +382,7 @@ bool unpacker_parse_redraw(Unpacker *p)
size_t size = p->read_size;
GridLineEvent *g = p->grid_line_event;
+// -V:NEXT_TYPE:501
#define NEXT_TYPE(tok, typ) \
result = mpack_rtoken(&data, &size, &tok); \
if (result == MPACK_EOF) { \
@@ -416,19 +425,19 @@ redo:
if (p->ui_handler.fn != ui_client_event_grid_line) {
p->state = 12;
if (p->grid_line_event) {
- arena_mem_free(arena_finish(&p->arena), &p->reuse_blk);
+ arena_mem_free(arena_finish(&p->arena));
p->grid_line_event = NULL;
}
return true;
} else {
- p->state = 13;
- arena_start(&p->arena, &p->reuse_blk);
+ p->state = 14;
+ p->arena = (Arena)ARENA_EMPTY;
p->grid_line_event = arena_alloc(&p->arena, sizeof *p->grid_line_event, true);
g = p->grid_line_event;
}
FALLTHROUGH;
- case 13:
+ case 14:
NEXT_TYPE(tok, MPACK_TOKEN_ARRAY);
int eventarrsize = (int)tok.length;
if (eventarrsize != 4) {
@@ -449,10 +458,10 @@ redo:
p->read_ptr = data;
p->read_size = size;
- p->state = 14;
+ p->state = 15;
FALLTHROUGH;
- case 14:
+ case 15:
assert(g->icell < g->ncells);
NEXT_TYPE(tok, MPACK_TOKEN_ARRAY);
@@ -506,6 +515,9 @@ redo:
}
goto redo;
+ case 12:
+ return true;
+
default:
abort();
}
diff --git a/src/nvim/msgpack_rpc/unpacker.h b/src/nvim/msgpack_rpc/unpacker.h
index f39439be63..b8b2d38d3b 100644
--- a/src/nvim/msgpack_rpc/unpacker.h
+++ b/src/nvim/msgpack_rpc/unpacker.h
@@ -7,10 +7,14 @@
#include "mpack/mpack_core.h"
#include "mpack/object.h"
+#include "nvim/api/private/defs.h"
#include "nvim/api/private/dispatch.h"
#include "nvim/api/private/helpers.h"
+#include "nvim/grid_defs.h"
#include "nvim/memory.h"
#include "nvim/msgpack_rpc/channel_defs.h"
+#include "nvim/types.h"
+#include "nvim/ui_client.h"
struct Unpacker {
mpack_parser_t parser;
@@ -32,8 +36,6 @@ struct Unpacker {
Error unpack_error;
Arena arena;
- // one length free-list of reusable blocks
- ArenaMem reuse_blk;
int nevents;
int ncalls;
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 2c7dadad0b..58a18ca5a8 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -8,14 +8,21 @@
//
#include <assert.h>
+#include <ctype.h>
#include <inttypes.h>
+#include <limits.h>
#include <stdbool.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
+#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
#include "nvim/change.h"
#include "nvim/charset.h"
#include "nvim/cmdhist.h"
@@ -25,8 +32,6 @@
#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
-#include "nvim/eval/userfunc.h"
-#include "nvim/event/loop.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_cmds2.h"
#include "nvim/ex_docmd.h"
@@ -34,18 +39,18 @@
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
#include "nvim/help.h"
-#include "nvim/indent.h"
+#include "nvim/highlight_defs.h"
#include "nvim/keycodes.h"
-#include "nvim/log.h"
-#include "nvim/main.h"
+#include "nvim/macros.h"
#include "nvim/mapping.h"
#include "nvim/mark.h"
+#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
-#include "nvim/menu.h"
#include "nvim/message.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
@@ -57,14 +62,19 @@
#include "nvim/plines.h"
#include "nvim/profile.h"
#include "nvim/quickfix.h"
+#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/spell.h"
#include "nvim/spellfile.h"
#include "nvim/spellsuggest.h"
#include "nvim/state.h"
+#include "nvim/statusline.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/tag.h"
+#include "nvim/textformat.h"
+#include "nvim/textobject.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/vim.h"
@@ -143,8 +153,7 @@ static const struct nv_cmd {
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[] =
-{
+} nv_cmds[] = {
{ NUL, nv_error, 0, 0 },
{ Ctrl_A, nv_addsub, 0, 0 },
{ Ctrl_B, nv_page, NV_STS, BACKWARD },
@@ -442,12 +451,34 @@ static int find_command(int cmdchar)
/// message, return true.
static bool check_text_locked(oparg_T *oap)
{
- if (text_locked()) {
+ if (!text_locked()) {
+ return false;
+ }
+
+ if (oap != NULL) {
clearopbeep(oap);
- text_locked_msg();
+ }
+ text_locked_msg();
+ return true;
+}
+
+/// If text is locked, "curbuf->b_ro_locked" or "allbuf_lock" is set:
+/// Give an error message, possibly beep and return true.
+/// "oap" may be NULL.
+static bool check_text_or_curbuf_locked(oparg_T *oap)
+{
+ if (check_text_locked(oap)) {
return true;
}
- return false;
+
+ if (!curbuf_locked()) {
+ return false;
+ }
+
+ if (oap != NULL) {
+ clearop(oap);
+ }
+ return true;
}
/// Normal state entry point. This is called on:
@@ -523,7 +554,7 @@ static bool normal_handle_special_visual_command(NormalState *s)
&& (nv_cmds[s->idx].cmd_flags & NV_STS)
&& !(mod_mask & MOD_MASK_SHIFT)) {
end_visual_mode();
- redraw_curbuf_later(INVERTED);
+ redraw_curbuf_later(UPD_INVERTED);
}
// Keys that work different when 'keymodel' contains "startsel"
@@ -611,6 +642,7 @@ static bool normal_need_redraw_mode_message(NormalState *s)
&& stuff_empty()
&& typebuf_typed()
&& emsg_silent == 0
+ && !in_assert_fails
&& !did_wait_return
&& s->oa.op_type == OP_NOP);
}
@@ -627,18 +659,18 @@ static void normal_redraw_mode_message(NormalState *s)
// If need to redraw, and there is a "keep_msg", redraw before the
// delay
if (must_redraw && keep_msg != NULL && !emsg_on_display) {
- char_u *kmsg;
+ char *kmsg;
kmsg = keep_msg;
keep_msg = NULL;
// Showmode() will clear keep_msg, but we want to use it anyway.
// First update w_topline.
setcursor();
- update_screen(0);
+ update_screen();
// now reset it, otherwise it's put in the history again
keep_msg = kmsg;
- kmsg = vim_strsave(keep_msg);
+ kmsg = xstrdup(keep_msg);
msg_attr((const char *)kmsg, keep_msg_attr);
xfree(kmsg);
}
@@ -1097,8 +1129,7 @@ static int normal_execute(VimState *state, int key)
goto finish;
}
- if ((nv_cmds[s->idx].cmd_flags & NV_NCW)
- && (check_text_locked(&s->oa) || curbuf_locked())) {
+ if ((nv_cmds[s->idx].cmd_flags & NV_NCW) && check_text_or_curbuf_locked(&s->oa)) {
// this command is not allowed now
s->command_finished = true;
goto finish;
@@ -1130,6 +1161,7 @@ static int normal_execute(VimState *state, int key)
if (s->need_flushbuf) {
ui_flush();
}
+
if (s->ca.cmdchar != K_IGNORE && s->ca.cmdchar != K_EVENT) {
did_cursorhold = false;
}
@@ -1220,8 +1252,7 @@ static void normal_check_interrupt(NormalState *s)
static void normal_check_window_scrolled(NormalState *s)
{
if (!finish_op) {
- // Trigger Scroll if the viewport changed.
- may_trigger_winscrolled();
+ may_trigger_win_scrolled_resized();
}
}
@@ -1281,10 +1312,10 @@ static void normal_redraw(NormalState *s)
validate_cursor();
if (VIsual_active) {
- redraw_curbuf_later(INVERTED); // update inverted part
- update_screen(0);
+ redraw_curbuf_later(UPD_INVERTED); // update inverted part
+ update_screen();
} else if (must_redraw) {
- update_screen(0);
+ update_screen();
} else if (redraw_cmdline || clear_cmdline || redraw_mode) {
showmode();
}
@@ -1300,7 +1331,7 @@ static void normal_redraw(NormalState *s)
// Display message after redraw. If an external message is still visible,
// it contains the kept message already.
if (keep_msg != NULL && !msg_ext_is_visible()) {
- char_u *const p = vim_strsave(keep_msg);
+ char *const p = xstrdup(keep_msg);
// msg_start() will set keep_msg to NULL, make a copy
// first. Don't reset keep_msg, msg_attr_keep() uses it to
@@ -1387,6 +1418,9 @@ static int normal_check(VimState *state)
fclose(time_fd);
time_fd = NULL;
}
+ // After the first screen update may start triggering WinScrolled
+ // autocmd events. Store all the scroll positions and sizes now.
+ may_make_initial_scroll_size_snapshot();
}
// May perform garbage collection when waiting for a character, but
@@ -1431,856 +1465,6 @@ 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.
-static void move_tab_to_mouse(void)
-{
- int tabnr = tab_page_click_defs[mouse_col].tabnr;
- if (tabnr <= 0) {
- tabpage_move(9999);
- } else if (tabnr < tabpage_index(curtab)) {
- tabpage_move(tabnr - 1);
- } else {
- tabpage_move(tabnr);
- }
-}
-
-/// 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 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 (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
-/// middle press - yes if active no yank and put
-/// right press - yes start or extend yes
-/// right press S yes no change yes "#" (2)
-/// right drag - yes extend no
-/// right relse - yes extend no
-///
-/// Insert or Replace Mode:
-/// event modi- position visual change action
-/// fier cursor window
-/// left press - yes (cannot be active) yes
-/// left press C yes (cannot be active) yes "CTRL-O^]" (2)
-/// left press S yes (cannot be active) yes "CTRL-O*" (2)
-/// left drag - yes start or extend (1) no CTRL-O (1)
-/// left relse - yes start or extend (1) no CTRL-O (1)
-/// middle press - no (cannot be active) no put register
-/// right press - yes start or extend yes CTRL-O
-/// right press S yes (cannot be active) yes "CTRL-O#" (2)
-///
-/// (1) only if mouse pointer moved since press
-/// (2) only if click is in same buffer
-///
-/// @param oap operator argument, can be NULL
-/// @param c K_LEFTMOUSE, etc
-/// @param dir Direction to 'put' if necessary
-/// @param fixindent PUT_FIXINDENT if fixing indent necessary
-///
-/// @return true if start_arrow() should be called for edit mode.
-bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
-{
- static bool got_click = false; // got a click some time back
-
- int which_button; // MOUSE_LEFT, _MIDDLE or _RIGHT
- bool is_click; // If false it's a drag or release event
- bool is_drag; // If true it's a drag event
- 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
- int c1, c2;
- pos_T save_cursor;
- win_T *old_curwin = curwin;
- static pos_T orig_cursor;
- colnr_T leftcol, rightcol;
- pos_T end_visual;
- long diff;
- int old_active = VIsual_active;
- int old_mode = VIsual_mode;
- int regname;
-
- save_cursor = curwin->w_cursor;
-
- 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.
- // 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.
- nc = safe_vgetc();
- if (c == nc) {
- continue;
- }
- vungetc(nc);
- mouse_grid = save_mouse_grid;
- mouse_row = save_mouse_row;
- mouse_col = save_mouse_col;
- }
- }
- break;
- }
-
- if (c == K_MOUSEMOVE) {
- // Mouse moved without a button pressed.
- return false;
- }
-
- // Ignore drag and release events if we didn't get a click.
- if (is_click) {
- got_click = true;
- } else {
- if (!got_click) { // didn't get click, ignore
- return false;
- }
- if (!is_drag) { // release, reset got_click
- got_click = false;
- if (in_tab_line) {
- in_tab_line = false;
- return false;
- }
- }
- }
-
- // CTRL right mouse button does CTRL-T
- if (is_click && (mod_mask & MOD_MASK_CTRL) && which_button == MOUSE_RIGHT) {
- if (State & MODE_INSERT) {
- stuffcharReadbuff(Ctrl_O);
- }
- if (count > 1) {
- stuffnumReadbuff(count);
- }
- stuffcharReadbuff(Ctrl_T);
- got_click = false; // ignore drag&release now
- return false;
- }
-
- // 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.*".
- if ((mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT
- | MOD_MASK_META))
- && (!is_click
- || (mod_mask & MOD_MASK_MULTI_CLICK)
- || which_button == MOUSE_MIDDLE)
- && !((mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT))
- && mouse_model_popup()
- && which_button == MOUSE_LEFT)
- && !((mod_mask & MOD_MASK_ALT)
- && !mouse_model_popup()
- && which_button == MOUSE_RIGHT)) {
- 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 (!is_click && which_button == MOUSE_MIDDLE) {
- return false;
- }
-
- if (oap != NULL) {
- regname = oap->regname;
- } else {
- regname = 0;
- }
-
- // Middle mouse button does a 'put' of the selected text
- if (which_button == MOUSE_MIDDLE) {
- 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_active) {
- if (VIsual_select) {
- stuffcharReadbuff(Ctrl_G);
- stuffReadbuff("\"+p");
- } else {
- stuffcharReadbuff('y');
- stuffcharReadbuff(K_MIDDLEMOUSE);
- }
- return false;
- }
- // 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 & MODE_INSERT)) {
- if (regname == '.') {
- insert_reg(regname, true);
- } else {
- if (regname == 0 && eval_has_provider("clipboard")) {
- regname = '*';
- }
- if ((State & REPLACE_FLAG) && !yank_register_mline(regname)) {
- insert_reg(regname, true);
- } else {
- do_put(regname, NULL, BACKWARD, 1L,
- (fixindent ? PUT_FIXINDENT : 0) | PUT_CURSEND);
-
- // Repeat it with CTRL-R CTRL-O r or CTRL-R CTRL-P r
- AppendCharToRedobuff(Ctrl_R);
- AppendCharToRedobuff(fixindent ? Ctrl_P : Ctrl_O);
- AppendCharToRedobuff(regname == 0 ? '"' : regname);
- }
- }
- return false;
- }
- }
-
- // When dragging or button-up stay in the same window.
- if (!is_click) {
- jump_flags |= MOUSE_FOCUS | MOUSE_DID_MOVE;
- }
-
- start_visual.lnum = 0;
-
- // Check for clicking in the tab page line.
- if (mouse_grid <= 1 && mouse_row == 0 && firstwin->w_winrow > 0) {
- if (is_drag) {
- if (in_tab_line) {
- move_tab_to_mouse();
- }
- return false;
- }
-
- // click in a tab selects that tab page
- if (is_click
- && cmdwin_type == 0
- && mouse_col < Columns) {
- in_tab_line = true;
- c1 = tab_page_click_defs[mouse_col].tabnr;
- switch (tab_page_click_defs[mouse_col].type) {
- case kStlClickDisabled:
- break;
- case kStlClickTabClose: {
- tabpage_T *tp;
-
- // Close the current or specified tab page.
- if (c1 == 999) {
- tp = curtab;
- } else {
- tp = find_tabpage(c1);
- }
- if (tp == curtab) {
- if (first_tabpage->tp_next != NULL) {
- tabpage_close(false);
- }
- } else if (tp != NULL) {
- tabpage_close_other(tp, false);
- }
- break;
- }
- case kStlClickTabSwitch:
- if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) {
- // double click opens new page
- end_visual_mode();
- tabpage_new();
- tabpage_move(c1 == 0 ? 9999 : c1 - 1);
- } else {
- // Go to specified tab page, or next one if not clicking
- // on a label.
- goto_tabpage(c1);
-
- // It's like clicking on the status line of a window.
- if (curwin != old_curwin) {
- end_visual_mode();
- }
- }
- break;
- case kStlClickFuncRun:
- call_click_def_func(tab_page_click_defs, mouse_col, which_button);
- break;
- }
- }
- return true;
- } else if (is_drag && in_tab_line) {
- move_tab_to_mouse();
- 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
- if (mouse_model_popup()) {
- if (which_button == MOUSE_RIGHT
- && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) {
- 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);
- redraw_curbuf_later(VIsual_active ? INVERTED : VALID);
- update_screen(0);
- 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))) {
- which_button = MOUSE_RIGHT;
- mod_mask &= ~MOD_MASK_SHIFT;
- }
- }
-
- 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
- if (VIsual_active) {
- jump_flags |= MOUSE_MAY_STOP_VIS;
- }
- } else {
- jump_flags |= MOUSE_MAY_VIS;
- }
- } else if (which_button == MOUSE_RIGHT) {
- if (is_click && VIsual_active) {
- // 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;
- } else {
- start_visual = VIsual;
- end_visual = curwin->w_cursor;
- }
- }
- jump_flags |= MOUSE_FOCUS;
- jump_flags |= MOUSE_MAY_VIS;
- }
- }
-
- // 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;
- }
-
- // When releasing the button let jump_to_mouse() know.
- if (!is_click && !is_drag) {
- jump_flags |= MOUSE_RELEASED;
- }
-
- // 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 (in_status_line && global_stl_height() > 0) {
- // global statusline is displayed for the current window,
- // and spans the whole screen.
- click_defs = curwin->w_status_click_defs;
- click_col = mouse_col;
- }
-
- 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.
- if (curwin != old_curwin && oap != NULL && oap->op_type != OP_NOP) {
- clearop(oap);
- }
-
- if (mod_mask == 0
- && !is_drag
- && (jump_flags & (MOUSE_FOLD_CLOSE | MOUSE_FOLD_OPEN))
- && which_button == MOUSE_LEFT) {
- // open or close a fold at this line
- if (jump_flags & MOUSE_FOLD_OPEN) {
- openFold(curwin->w_cursor, 1L);
- } else {
- closeFold(curwin->w_cursor, 1L);
- }
- // don't move the cursor if still in the same window
- if (curwin == old_curwin) {
- curwin->w_cursor = save_cursor;
- }
- }
-
- // 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) {
- mouse_dragging = 2;
- } else {
- mouse_dragging = 1;
- }
- }
-
- // When dragging the mouse above the window, scroll down.
- if (is_drag && mouse_row < 0 && !in_status_line) {
- scroll_redraw(false, 1L);
- mouse_row = 0;
- }
-
- if (start_visual.lnum) { // right click in visual mode
- // When ALT is pressed make Visual mode blockwise.
- if (mod_mask & MOD_MASK_ALT) {
- 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.
- if (VIsual_mode == Ctrl_V) {
- getvcols(curwin, &start_visual, &end_visual, &leftcol, &rightcol);
- if (curwin->w_curswant > (leftcol + rightcol) / 2) {
- end_visual.col = leftcol;
- } else {
- end_visual.col = rightcol;
- }
- if (curwin->w_cursor.lnum >=
- (start_visual.lnum + end_visual.lnum) / 2) {
- end_visual.lnum = start_visual.lnum;
- }
-
- // move VIsual to the right column
- start_visual = curwin->w_cursor; // save the cursor pos
- curwin->w_cursor = end_visual;
- coladvance(end_visual.col);
- 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 (lt(curwin->w_cursor, start_visual)) {
- VIsual = end_visual;
- } else if (lt(end_visual, curwin->w_cursor)) {
- VIsual = start_visual;
- } else {
- // In the same line, compare column number
- if (end_visual.lnum == start_visual.lnum) {
- if (curwin->w_cursor.col - start_visual.col >
- end_visual.col - curwin->w_cursor.col) {
- VIsual = start_visual;
- } else {
- VIsual = end_visual;
- }
- } else {
- // In different lines, compare line number
- diff = (curwin->w_cursor.lnum - start_visual.lnum) -
- (end_visual.lnum - curwin->w_cursor.lnum);
-
- if (diff > 0) { // closest to end
- VIsual = start_visual;
- } else if (diff < 0) { // closest to start
- VIsual = end_visual;
- } else { // in the middle line
- if (curwin->w_cursor.col <
- (start_visual.col + end_visual.col) / 2) {
- VIsual = end_visual;
- } else {
- VIsual = start_visual;
- }
- }
- }
- }
- }
- } 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.
- if (which_button == MOUSE_MIDDLE) {
- if (regname == 0 && eval_has_provider("clipboard")) {
- regname = '*';
- }
- if (yank_register_mline(regname)) {
- if (mouse_past_bottom) {
- dir = FORWARD;
- }
- } else if (mouse_past_eol) {
- dir = FORWARD;
- }
-
- if (fixindent) {
- c1 = (dir == BACKWARD) ? '[' : ']';
- c2 = 'p';
- } else {
- c1 = (dir == FORWARD) ? 'p' : 'P';
- c2 = NUL;
- }
- prep_redo(regname, count, NUL, c1, NUL, c2, NUL);
-
- // 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);
- } 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
- } 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
- } 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) {
- stuffcharReadbuff('*');
- } else { // MOUSE_RIGHT
- stuffcharReadbuff('#');
- }
- } 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;
- } else {
- VIsual = curwin->w_cursor;
- orig_cursor = VIsual;
- VIsual_active = true;
- VIsual_reselect = true;
- // start Select mode if 'selectmode' contains "mouse"
- may_start_select('o');
- setmouse();
- }
- if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) {
- // Double click with ALT pressed makes it blockwise.
- if (mod_mask & MOD_MASK_ALT) {
- VIsual_mode = Ctrl_V;
- } else {
- VIsual_mode = 'v';
- }
- } else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_3CLICK) {
- VIsual_mode = 'V';
- } else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_4CLICK) {
- VIsual_mode = Ctrl_V;
- }
- }
- // 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.
- end_visual = curwin->w_cursor;
- while (gc = gchar_pos(&end_visual), ascii_iswhite(gc)) {
- inc(&end_visual);
- }
- if (oap != NULL) {
- oap->motion_type = kMTCharWise;
- }
- if (oap != NULL
- && VIsual_mode == 'v'
- && !vim_iswordc(gchar_pos(&end_visual))
- && equalpos(curwin->w_cursor, VIsual)
- && (pos = findmatch(oap, NUL)) != NULL) {
- curwin->w_cursor = *pos;
- if (oap->motion_type == kMTLineWise) {
- VIsual_mode = 'V';
- } else if (*p_sel == 'e') {
- if (lt(curwin->w_cursor, VIsual)) {
- VIsual.col++;
- } else {
- curwin->w_cursor.col++;
- }
- }
- }
- }
-
- if (pos == NULL && (is_click || is_drag)) {
- // 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);
- } else {
- find_start_of_word(&VIsual);
- if (*p_sel == 'e' && *get_cursor_pos_ptr() != NUL) {
- curwin->w_cursor.col +=
- utfc_ptr2len((char *)get_cursor_pos_ptr());
- }
- find_end_of_word(&curwin->w_cursor);
- }
- }
- curwin->w_set_curswant = true;
- }
- if (is_click) {
- redraw_curbuf_later(INVERTED); // update the inversion
- }
- } else if (VIsual_active && !old_active) {
- if (mod_mask & MOD_MASK_ALT) {
- VIsual_mode = Ctrl_V;
- } else {
- VIsual_mode = 'v';
- }
- }
-
- // If Visual mode changed show it later.
- if ((!VIsual_active && old_active && mode_displayed)
- || (VIsual_active && p_smd && msg_silent == 0
- && (!old_active || VIsual_mode != old_mode))) {
- redraw_cmdline = true;
- }
-
- return moved;
-}
-
-/// Move "pos" back to the start of the word it's in.
-static void find_start_of_word(pos_T *pos)
-{
- char_u *line;
- int cclass;
- int col;
-
- line = ml_get(pos->lnum);
- cclass = get_mouse_class(line + pos->col);
-
- while (pos->col > 0) {
- col = pos->col - 1;
- col -= utf_head_off(line, line + col);
- if (get_mouse_class(line + col) != cclass) {
- break;
- }
- pos->col = col;
- }
-}
-
-/// 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;
- int cclass;
- int col;
-
- line = ml_get(pos->lnum);
- if (*p_sel == 'e' && pos->col > 0) {
- pos->col--;
- pos->col -= utf_head_off(line, line + pos->col);
- }
- cclass = get_mouse_class(line + pos->col);
- while (line[pos->col] != NUL) {
- col = pos->col + utfc_ptr2len((char *)line + pos->col);
- if (get_mouse_class(line + col) != cclass) {
- if (*p_sel == 'e') {
- pos->col = col;
- }
- break;
- }
- pos->col = col;
- }
-}
-
-/// 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) {
- return mb_get_class(p);
- }
-
- const int c = *p;
- if (c == ' ' || c == '\t') {
- return 0;
- }
- if (vim_iswordc(c)) {
- 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("-+*/%<>&|^!=", c) != NULL) {
- return 1;
- }
- return c;
-}
-
/// End Visual mode.
/// This function should ALWAYS be called to end Visual mode, except from
/// do_pending_operator().
@@ -2311,7 +1495,7 @@ void reset_VIsual_and_resel(void)
{
if (VIsual_active) {
end_visual_mode();
- redraw_curbuf_later(INVERTED); // delete the inversion later
+ redraw_curbuf_later(UPD_INVERTED); // delete the inversion later
}
VIsual_reselect = false;
}
@@ -2321,7 +1505,7 @@ void reset_VIsual(void)
{
if (VIsual_active) {
end_visual_mode();
- redraw_curbuf_later(INVERTED); // delete the inversion later
+ redraw_curbuf_later(UPD_INVERTED); // delete the inversion later
VIsual_reselect = false;
}
}
@@ -2342,8 +1526,7 @@ void restore_visual_mode(void)
/// @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)
+static bool find_is_eval_item(const char *const ptr, int *const colp, int *const bnp, const int dir)
{
// Accept everything inside [].
if ((*ptr == ']' && dir == BACKWARD) || (*ptr == '[' && dir == FORWARD)) {
@@ -2414,7 +1597,7 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, char **text
// if i == 0: try to find an identifier
// if i == 1: try to find any non-white text
- char_u *ptr = ml_get_buf(wp->w_buffer, lnum, false);
+ char *ptr = ml_get_buf(wp->w_buffer, lnum, false);
for (i = (find_type & FIND_IDENT) ? 0 : 1; i < 2; i++) {
// 1. skip to start of identifier/text
col = startcol;
@@ -2427,7 +1610,7 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, char **text
if (this_class != 0 && (i == 1 || this_class != 1)) {
break;
}
- col += utfc_ptr2len((char *)ptr + col);
+ col += utfc_ptr2len(ptr + col);
}
// When starting on a ']' count it, so that we include the '['.
@@ -2438,7 +1621,7 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, char **text
//
// Remember class of character under cursor.
if ((find_type & FIND_EVAL) && ptr[col] == ']') {
- this_class = mb_get_class((char_u *)"a");
+ this_class = mb_get_class("a");
} else {
this_class = mb_get_class(ptr + col);
}
@@ -2477,7 +1660,7 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, char **text
return 0;
}
ptr += col;
- *text = (char *)ptr;
+ *text = ptr;
if (textcol != NULL) {
*textcol = col;
}
@@ -2495,7 +1678,7 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, char **text
|| ((find_type & FIND_EVAL)
&& col <= (int)startcol
&& find_is_eval_item(ptr + col, &col, &bn, FORWARD)))) {
- col += utfc_ptr2len((char *)ptr + col);
+ col += utfc_ptr2len(ptr + col);
}
assert(col >= 0);
@@ -2620,9 +1803,7 @@ void may_clear_cmdline(void)
}
// Routines for displaying a partly typed command
-#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 char old_showcmd_buf[SHOWCMD_BUFLEN]; // For push_showcmd()
static bool showcmd_is_clear = true;
static bool showcmd_visual = false;
@@ -2652,8 +1833,8 @@ void clear_showcmd(void)
lines = bot - top + 1;
if (VIsual_mode == Ctrl_V) {
- char_u *const saved_sbr = p_sbr;
- char_u *const saved_w_sbr = curwin->w_p_sbr;
+ char *const saved_sbr = p_sbr;
+ char *const saved_w_sbr = curwin->w_p_sbr;
// Make 'sbr' empty for a moment to get the correct size.
p_sbr = empty_option;
@@ -2661,12 +1842,12 @@ void clear_showcmd(void)
getvcols(curwin, &curwin->w_cursor, &VIsual, &leftcol, &rightcol);
p_sbr = saved_sbr;
curwin->w_p_sbr = saved_w_sbr;
- snprintf((char *)showcmd_buf, SHOWCMD_BUFLEN, "%" PRId64 "x%" PRId64,
+ snprintf(showcmd_buf, SHOWCMD_BUFLEN, "%" PRId64 "x%" PRId64,
(int64_t)lines, (int64_t)rightcol - leftcol + 1);
} else if (VIsual_mode == 'V' || VIsual.lnum != curwin->w_cursor.lnum) {
- snprintf((char *)showcmd_buf, SHOWCMD_BUFLEN, "%" PRId64, (int64_t)lines);
+ snprintf(showcmd_buf, SHOWCMD_BUFLEN, "%" PRId64, (int64_t)lines);
} else {
- char_u *s, *e;
+ char *s, *e;
int l;
int bytes = 0;
int chars = 0;
@@ -2679,7 +1860,7 @@ void clear_showcmd(void)
e = ml_get_pos(&VIsual);
}
while ((*p_sel != 'e') ? s <= e : s < e) {
- l = utfc_ptr2len((char *)s);
+ l = utfc_ptr2len(s);
if (l == 0) {
bytes++;
chars++;
@@ -2690,9 +1871,9 @@ void clear_showcmd(void)
s += l;
}
if (bytes == chars) {
- sprintf((char *)showcmd_buf, "%d", chars);
+ snprintf(showcmd_buf, SHOWCMD_BUFLEN, "%d", chars);
} else {
- sprintf((char *)showcmd_buf, "%d-%d", chars, bytes);
+ snprintf(showcmd_buf, SHOWCMD_BUFLEN, "%d-%d", chars, bytes);
}
}
int limit = ui_has(kUIMessages) ? SHOWCMD_BUFLEN - 1 : SHOWCMD_COLS;
@@ -2716,8 +1897,6 @@ void clear_showcmd(void)
/// @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[] = {
K_IGNORE,
K_LEFTMOUSE, K_LEFTDRAG, K_LEFTRELEASE, K_MOUSEMOVE,
@@ -2740,19 +1919,19 @@ 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 (int i = 0; ignore[i] != 0; i++) {
if (ignore[i] == c) {
return false;
}
}
}
- p = transchar(c);
+ char *p = transchar(c);
if (*p == ' ') {
STRCPY(p, "<20>");
}
- size_t old_len = STRLEN(showcmd_buf);
- size_t extra_len = STRLEN(p);
+ size_t old_len = strlen(showcmd_buf);
+ size_t extra_len = strlen(p);
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;
@@ -2784,7 +1963,7 @@ static void del_from_showcmd(int len)
return;
}
- old_len = (int)STRLEN(showcmd_buf);
+ old_len = (int)strlen(showcmd_buf);
if (len > old_len) {
len = old_len;
}
@@ -2817,13 +1996,23 @@ void pop_showcmd(void)
static void display_showcmd(void)
{
- if (!ui_has_messages()) {
+ int len = (int)strlen(showcmd_buf);
+ showcmd_is_clear = (len == 0);
+
+ if (*p_sloc == 's') {
+ win_redr_status(curwin);
+ setcursor(); // put cursor back where it belongs
+ return;
+ }
+ if (*p_sloc == 't') {
+ draw_tabline();
+ setcursor(); // put cursor back where it belongs
+ return;
+ }
+ // 'showcmdloc' is "last" or empty
+ if (p_ch == 0 && !ui_has(kUIMessages)) {
return;
}
-
- int len;
- len = (int)STRLEN(showcmd_buf);
- showcmd_is_clear = (len == 0);
if (ui_has(kUIMessages)) {
MAXSIZE_TEMP_ARRAY(content, 1);
@@ -2831,7 +2020,7 @@ static void display_showcmd(void)
if (len > 0) {
// placeholder for future highlight support
ADD_C(chunk, INTEGER_OBJ(0));
- ADD_C(chunk, STRING_OBJ(cstr_as_string((char *)showcmd_buf)));
+ ADD_C(chunk, STRING_OBJ(cstr_as_string(showcmd_buf)));
ADD_C(content, ARRAY_OBJ(chunk));
}
ui_call_msg_showcmd(content);
@@ -2848,7 +2037,7 @@ static void display_showcmd(void)
}
// clear the rest of an old message by outputting up to SHOWCMD_COLS spaces
- grid_puts(&msg_grid_adj, (char_u *)" " + len, showcmd_row,
+ grid_puts(&msg_grid_adj, (char *)" " + len, showcmd_row,
sc_col + len, HL_ATTR(HLF_MSG));
grid_puts_line_flush(false);
@@ -2956,7 +2145,7 @@ void check_scrollbind(linenr_T topline_diff, long leftcol_diff)
}
}
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
cursor_correct();
curwin->w_redr_status = true;
}
@@ -3022,17 +2211,19 @@ static void nv_addsub(cmdarg_T *cap)
/// CTRL-F, CTRL-B, etc: Scroll page up or down.
static void nv_page(cmdarg_T *cap)
{
- if (!checkclearop(cap->oap)) {
- if (mod_mask & MOD_MASK_CTRL) {
- // <C-PageUp>: tab page back; <C-PageDown>: tab page forward
- if (cap->arg == BACKWARD) {
- goto_tabpage(-(int)cap->count1);
- } else {
- goto_tabpage((int)cap->count0);
- }
+ if (checkclearop(cap->oap)) {
+ return;
+ }
+
+ if (mod_mask & MOD_MASK_CTRL) {
+ // <C-PageUp>: tab page back; <C-PageDown>: tab page forward
+ if (cap->arg == BACKWARD) {
+ goto_tabpage(-(int)cap->count1);
} else {
- (void)onepage(cap->arg, cap->count1);
+ goto_tabpage((int)cap->count0);
}
+ } else {
+ (void)onepage(cap->arg, cap->count1);
}
}
@@ -3044,22 +2235,23 @@ static void nv_gd(oparg_T *oap, int nchar, int thisblock)
size_t len;
char *ptr;
if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0
- || !find_decl((char_u *)ptr, len, nchar == 'd', thisblock, SEARCH_START)) {
+ || !find_decl(ptr, len, nchar == 'd', thisblock, SEARCH_START)) {
clearopbeep(oap);
- } 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;
+ }
+
+ 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.
-static bool is_ident(char_u *line, int offset)
+static bool is_ident(const char *line, int offset)
{
bool incomment = false;
int instring = 0;
@@ -3067,11 +2259,11 @@ static bool is_ident(char_u *line, int offset)
for (int i = 0; i < offset && line[i] != NUL; i++) {
if (instring != 0) {
- if (prev != '\\' && line[i] == instring) {
+ if (prev != '\\' && (uint8_t)line[i] == instring) {
instring = 0;
}
} else if ((line[i] == '"' || line[i] == '\'') && !incomment) {
- instring = line[i];
+ instring = (uint8_t)line[i];
} else {
if (incomment) {
if (prev == '*' && line[i] == '/') {
@@ -3084,7 +2276,7 @@ static bool is_ident(char_u *line, int offset)
}
}
- prev = line[i];
+ prev = (uint8_t)line[i];
}
return incomment == false && instring == 0;
@@ -3098,9 +2290,9 @@ static bool is_ident(char_u *line, int offset)
/// @param flags_arg flags passed to searchit()
///
/// @return fail when not found.
-bool find_decl(char_u *ptr, size_t len, bool locally, bool thisblock, int flags_arg)
+bool find_decl(char *ptr, size_t len, bool locally, bool thisblock, int flags_arg)
{
- char_u *pat;
+ char *pat;
pos_T old_pos;
pos_T par_pos;
pos_T found_pos;
@@ -3116,7 +2308,7 @@ bool find_decl(char_u *ptr, size_t len, bool locally, bool thisblock, int flags_
// Put "\V" before the pattern to avoid that the special meaning of "."
// and "~" causes trouble.
assert(len <= INT_MAX);
- sprintf((char *)pat, vim_iswordp(ptr) ? "\\V\\<%.*s\\>" : "\\V%.*s",
+ sprintf(pat, vim_iswordp(ptr) ? "\\V\\<%.*s\\>" : "\\V%.*s", // NOLINT(runtime/printf)
(int)len, ptr);
old_pos = curwin->w_cursor;
save_p_ws = p_ws;
@@ -3134,7 +2326,7 @@ 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((char *)get_cursor_line_ptr()) != NUL) {
+ && *skipwhite(get_cursor_line_ptr()) != NUL) {
curwin->w_cursor.lnum--;
}
}
@@ -3171,7 +2363,7 @@ bool find_decl(char_u *ptr, size_t len, bool locally, bool thisblock, int flags_
}
break;
}
- if (get_leader_len((char *)get_cursor_line_ptr(), NULL, false, true) > 0) {
+ if (get_leader_len(get_cursor_line_ptr(), NULL, false, true) > 0) {
// Ignore this line, continue at start of next line.
curwin->w_cursor.lnum++;
curwin->w_cursor.col = 0;
@@ -3358,10 +2550,10 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist)
validate_virtcol();
colnr_T virtcol = curwin->w_virtcol;
if (virtcol > (colnr_T)width1 && *get_showbreak_value(curwin) != NUL) {
- virtcol -= vim_strsize((char *)get_showbreak_value(curwin));
+ virtcol -= vim_strsize(get_showbreak_value(curwin));
}
- int c = utf_ptr2char((char *)get_cursor_pos_ptr());
+ int c = utf_ptr2char(get_cursor_pos_ptr());
if (dir == FORWARD && virtcol < curwin->w_curswant
&& (curwin->w_curswant <= (colnr_T)width1)
&& !vim_isprintc(c) && c > 255) {
@@ -3410,7 +2602,7 @@ static void nv_mousescroll(cmdarg_T *cap)
if (cap->arg == MSCR_UP || cap->arg == MSCR_DOWN) {
if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
(void)onepage(cap->arg ? FORWARD : BACKWARD, 1L);
- } else {
+ } else if (p_mousescroll_vert > 0) {
cap->count1 = p_mousescroll_vert;
cap->count0 = p_mousescroll_vert;
nv_scroll_line(cap);
@@ -3490,7 +2682,7 @@ void scroll_redraw(int up, long count)
if (moved) {
curwin->w_viewport_invalid = true;
}
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
}
/// Get the count specified after a 'z' command. Only the 'z<CR>', 'zl', 'zh',
@@ -3514,6 +2706,7 @@ static bool nv_z_get_count(cmdarg_T *cap, int *nchar_arg)
no_mapping--;
allow_keys--;
(void)add_to_showcmd(nchar);
+
if (nchar == K_DEL || nchar == K_KDEL) {
n /= 10;
} else if (ascii_isdigit(nchar)) {
@@ -3554,6 +2747,7 @@ static int nv_zg_zw(cmdarg_T *cap, int nchar)
no_mapping--;
allow_keys--;
(void)add_to_showcmd(nchar);
+
if (vim_strchr("gGwW", nchar) == NULL) {
clearopbeep(cap->oap);
return OK;
@@ -3579,7 +2773,7 @@ static int nv_zg_zw(cmdarg_T *cap, int nchar)
len = spell_move_to(curwin, FORWARD, true, true, NULL);
emsg_off--;
if (len != 0 && curwin->w_cursor.col <= pos.col) {
- ptr = (char *)ml_get_pos(&curwin->w_cursor);
+ ptr = ml_get_pos(&curwin->w_cursor);
}
curwin->w_cursor = pos;
}
@@ -3588,7 +2782,7 @@ static int nv_zg_zw(cmdarg_T *cap, int nchar)
return FAIL;
}
assert(len <= INT_MAX);
- spell_add_word((char_u *)ptr, (int)len,
+ 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);
@@ -3605,7 +2799,7 @@ static void nv_zet(cmdarg_T *cap)
long old_fdl = curwin->w_p_fdl;
int old_fen = curwin->w_p_fen;
- int l_p_siso = (int)get_sidescrolloff_value(curwin);
+ int siso = (int)get_sidescrolloff_value(curwin);
if (ascii_isdigit(nchar) && !nv_z_get_count(cap, &nchar)) {
return;
@@ -3656,7 +2850,7 @@ static void nv_zet(cmdarg_T *cap)
case 't':
scroll_cursor_top(0, true);
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
set_fraction(curwin);
break;
@@ -3667,7 +2861,7 @@ static void nv_zet(cmdarg_T *cap)
case 'z':
scroll_cursor_halfway(true);
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
set_fraction(curwin);
break;
@@ -3690,7 +2884,7 @@ static void nv_zet(cmdarg_T *cap)
case 'b':
scroll_cursor_bot(0, true);
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
set_fraction(curwin);
break;
@@ -3735,14 +2929,14 @@ static void nv_zet(cmdarg_T *cap)
} else {
getvcol(curwin, &curwin->w_cursor, &col, NULL, NULL);
}
- if (col > l_p_siso) {
- col -= l_p_siso;
+ if (col > siso) {
+ col -= siso;
} else {
col = 0;
}
if (curwin->w_leftcol != col) {
curwin->w_leftcol = col;
- redraw_later(curwin, NOT_VALID);
+ redraw_later(curwin, UPD_NOT_VALID);
}
}
break;
@@ -3756,14 +2950,14 @@ static void nv_zet(cmdarg_T *cap)
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col);
}
n = curwin->w_width_inner - curwin_col_off();
- if (col + l_p_siso < n) {
+ if (col + siso < n) {
col = 0;
} else {
- col = col + l_p_siso - n + 1;
+ col = col + siso - n + 1;
}
if (curwin->w_leftcol != col) {
curwin->w_leftcol = col;
- redraw_later(curwin, NOT_VALID);
+ redraw_later(curwin, UPD_NOT_VALID);
}
}
break;
@@ -4024,44 +3218,45 @@ static void nv_colon(cmdarg_T *cap)
if (VIsual_active && !is_cmdkey && !is_lua) {
nv_operator(cap);
- } else {
- if (cap->oap->op_type != OP_NOP) {
- // Using ":" as a movement is charwise exclusive.
- cap->oap->motion_type = kMTCharWise;
- cap->oap->inclusive = false;
- } else if (cap->count0 && !is_cmdkey && !is_lua) {
- // translate "count:" into ":.,.+(count - 1)"
- stuffcharReadbuff('.');
- if (cap->count0 > 1) {
- stuffReadbuff(",.+");
- stuffnumReadbuff(cap->count0 - 1L);
- }
- }
+ return;
+ }
- // When typing, don't type below an old message
- if (KeyTyped) {
- compute_cmdrow();
+ if (cap->oap->op_type != OP_NOP) {
+ // Using ":" as a movement is charwise exclusive.
+ cap->oap->motion_type = kMTCharWise;
+ cap->oap->inclusive = false;
+ } else if (cap->count0 && !is_cmdkey && !is_lua) {
+ // translate "count:" into ":.,.+(count - 1)"
+ stuffcharReadbuff('.');
+ if (cap->count0 > 1) {
+ stuffReadbuff(",.+");
+ stuffnumReadbuff(cap->count0 - 1L);
}
+ }
- if (is_lua) {
- cmd_result = map_execute_lua();
- } else {
- // 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);
- }
+ // When typing, don't type below an old message
+ if (KeyTyped) {
+ compute_cmdrow();
+ }
- if (cmd_result == false) {
- // The Ex command failed, do not execute the operator.
- clearop(cap->oap);
- } else if (cap->oap->op_type != OP_NOP
- && (cap->oap->start.lnum > curbuf->b_ml.ml_line_count
- || cap->oap->start.col >
- (colnr_T)STRLEN(ml_get(cap->oap->start.lnum))
- || did_emsg)) {
- // The start of the operator has become invalid by the Ex command.
- clearopbeep(cap->oap);
- }
+ if (is_lua) {
+ cmd_result = map_execute_lua();
+ } else {
+ // 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 (cmd_result == false) {
+ // The Ex command failed, do not execute the operator.
+ clearop(cap->oap);
+ } else if (cap->oap->op_type != OP_NOP
+ && (cap->oap->start.lnum > curbuf->b_ml.ml_line_count
+ || cap->oap->start.col >
+ (colnr_T)strlen(ml_get(cap->oap->start.lnum))
+ || did_emsg)) {
+ // The start of the operator has become invalid by the Ex command.
+ clearopbeep(cap->oap);
}
}
@@ -4092,14 +3287,16 @@ static void nv_ctrlh(cmdarg_T *cap)
/// CTRL-L: clear screen and redraw.
static void nv_clear(cmdarg_T *cap)
{
- if (!checkclearop(cap->oap)) {
- // Clear all syntax states to force resyncing.
- syn_stack_free_all(curwin->w_s);
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- wp->w_s->b_syn_slow = false;
- }
- redraw_later(curwin, CLEAR);
+ if (checkclearop(cap->oap)) {
+ return;
}
+
+ // Clear all syntax states to force resyncing.
+ syn_stack_free_all(curwin->w_s);
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ wp->w_s->b_syn_slow = false;
+ }
+ redraw_later(curwin, UPD_CLEAR);
}
/// CTRL-O: In Select mode: switch to Visual mode for one command.
@@ -4130,21 +3327,23 @@ static void nv_hat(cmdarg_T *cap)
/// "Z" commands.
static void nv_Zet(cmdarg_T *cap)
{
- if (!checkclearopq(cap->oap)) {
- switch (cap->nchar) {
- // "ZZ": equivalent to ":x".
- case 'Z':
- do_cmdline_cmd("x");
- break;
+ if (checkclearopq(cap->oap)) {
+ return;
+ }
- // "ZQ": equivalent to ":q!" (Elvis compatible).
- case 'Q':
- do_cmdline_cmd("q!");
- break;
+ switch (cap->nchar) {
+ // "ZZ": equivalent to ":x".
+ case 'Z':
+ do_cmdline_cmd("x");
+ break;
- default:
- clearopbeep(cap->oap);
- }
+ // "ZQ": equivalent to ":q!" (Elvis compatible).
+ case 'Q':
+ do_cmdline_cmd("q!");
+ break;
+
+ default:
+ clearopbeep(cap->oap);
}
}
@@ -4164,7 +3363,7 @@ void do_nv_ident(int c1, int c2)
/// '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 **ptr_arg,
+static size_t nv_K_getcmd(cmdarg_T *cap, char *kp, bool kp_help, bool kp_ex, char **ptr_arg,
size_t n, char *buf, size_t buf_size)
{
if (kp_help) {
@@ -4201,8 +3400,8 @@ static size_t nv_K_getcmd(cmdarg_T *cap, char_u *kp, bool kp_help, bool kp_ex, c
// 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);
+ 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));
}
@@ -4216,7 +3415,7 @@ static size_t nv_K_getcmd(cmdarg_T *cap, char_u *kp, bool kp_help, bool kp_ex, c
}
STRCAT(buf, " ");
if (cap->count0 != 0 && (isman || isman_s)) {
- snprintf(buf + STRLEN(buf), buf_size - STRLEN(buf), "%" PRId64,
+ snprintf(buf + strlen(buf), buf_size - strlen(buf), "%" PRId64,
(int64_t)cap->count0);
STRCAT(buf, " ");
}
@@ -4234,12 +3433,12 @@ static size_t nv_K_getcmd(cmdarg_T *cap, char_u *kp, bool kp_help, bool kp_ex, c
static void nv_ident(cmdarg_T *cap)
{
char *ptr = NULL;
- char_u *p;
+ char *p;
size_t n = 0; // init for GCC
int cmdchar;
bool g_cmd; // "g" command
bool tag_cmd = false;
- char_u *aux_ptr;
+ char *aux_ptr;
if (cap->cmdchar == 'g') { // "g*", "g#", "g]" and "gCTRL-]"
cmdchar = cap->nchar;
@@ -4275,15 +3474,14 @@ static void nv_ident(cmdarg_T *cap)
// Allocate buffer to put the command in. Inserting backslashes can
// double the length of the word. p_kp / curbuf->b_p_kp could be added
// and some numbers.
- char_u *kp = *curbuf->b_p_kp == NUL ? p_kp : curbuf->b_p_kp; // 'keywordprg'
- 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);
+ char *kp = *curbuf->b_p_kp == NUL ? p_kp : curbuf->b_p_kp; // 'keywordprg'
+ bool kp_help = (*kp == NUL || strcmp(kp, ":he") == 0 || strcmp(kp, ":help") == 0);
if (kp_help && *skipwhite(ptr) == NUL) {
emsg(_(e_noident)); // found white space only
return;
}
- size_t buf_size = n * 2 + 30 + STRLEN(kp);
+ bool kp_ex = (*kp == ':'); // 'keywordprg' is an ex command
+ size_t buf_size = n * 2 + 30 + strlen(kp);
char *buf = xmalloc(buf_size);
buf[0] = NUL;
@@ -4295,9 +3493,9 @@ static void nv_ident(cmdarg_T *cap)
// Call setpcmark() first, so "*``" puts the cursor back where
// it was.
setpcmark();
- curwin->w_cursor.col = (colnr_T)(ptr - (char *)get_cursor_line_ptr());
+ curwin->w_cursor.col = (colnr_T)(ptr - get_cursor_line_ptr());
- if (!g_cmd && vim_iswordp((char_u *)ptr)) {
+ if (!g_cmd && vim_iswordp(ptr)) {
STRCPY(buf, "\\<");
}
no_smartcase = true; // don't use 'smartcase' now
@@ -4312,11 +3510,7 @@ static void nv_ident(cmdarg_T *cap)
case ']':
tag_cmd = true;
- if (p_cst) {
- STRCPY(buf, "cstag ");
- } else {
- STRCPY(buf, "ts ");
- }
+ STRCPY(buf, "ts ");
break;
default:
@@ -4337,45 +3531,45 @@ static void nv_ident(cmdarg_T *cap)
ptr = xstrnsave(ptr, n);
if (kp_ex) {
// Escape the argument properly for an Ex command
- p = (char_u *)vim_strsave_fnameescape((const char *)ptr, VSE_NONE);
+ p = vim_strsave_fnameescape((const char *)ptr, VSE_NONE);
} else {
// Escape the argument properly for a shell command
- p = vim_strsave_shellescape((char_u *)ptr, true, true);
+ p = vim_strsave_shellescape(ptr, true, true);
}
xfree(ptr);
- char *newbuf = xrealloc(buf, STRLEN(buf) + STRLEN(p) + 1);
+ char *newbuf = xrealloc(buf, strlen(buf) + strlen(p) + 1);
buf = newbuf;
STRCAT(buf, p);
xfree(p);
} else {
if (cmdchar == '*') {
- aux_ptr = (char_u *)(p_magic ? "/.*~[^$\\" : "/^$\\");
+ aux_ptr = (magic_isset() ? "/.*~[^$\\" : "/^$\\");
} else if (cmdchar == '#') {
- aux_ptr = (char_u *)(p_magic ? "/?.*~[^$\\" : "/?^$\\");
+ aux_ptr = (magic_isset() ? "/?.*~[^$\\" : "/?^$\\");
} else if (tag_cmd) {
if (curbuf->b_help) {
// ":help" handles unescaped argument
- aux_ptr = (char_u *)"";
+ aux_ptr = "";
} else {
- aux_ptr = (char_u *)"\\|\"\n[";
+ aux_ptr = "\\|\"\n[";
}
} else {
- aux_ptr = (char_u *)"\\|\"\n*?[";
+ aux_ptr = "\\|\"\n*?[";
}
- p = (char_u *)buf + STRLEN(buf);
+ p = buf + strlen(buf);
while (n-- > 0) {
// put a backslash before \ and some others
- if (vim_strchr((char *)aux_ptr, *ptr) != NULL) {
+ if (vim_strchr(aux_ptr, (uint8_t)(*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);
for (size_t i = 0; i < len && n > 0; i++, n--) {
- *p++ = (char_u)(*ptr++);
+ *p++ = *ptr++;
}
- *p++ = (char_u)(*ptr++);
+ *p++ = *ptr++;
}
*p = NUL;
}
@@ -4383,16 +3577,15 @@ static void nv_ident(cmdarg_T *cap)
// Execute the command.
if (cmdchar == '*' || cmdchar == '#') {
if (!g_cmd
- && vim_iswordp(mb_prevptr(get_cursor_line_ptr(), (char_u *)ptr))) {
+ && vim_iswordp(mb_prevptr(get_cursor_line_ptr(), ptr))) {
STRCAT(buf, "\\>");
}
// put pattern in search history
init_history();
- add_to_history(HIST_SEARCH, (char_u *)buf, true, NUL);
+ add_to_history(HIST_SEARCH, buf, true, NUL);
- (void)normal_search(cap, cmdchar == '*' ? '/' : '?', (char_u *)buf, 0,
- NULL);
+ (void)normal_search(cap, cmdchar == '*' ? '/' : '?', buf, 0, NULL);
} else {
g_tag_at_cursor = true;
do_cmdline_cmd(buf);
@@ -4427,14 +3620,14 @@ bool get_visual_text(cmdarg_T *cap, char **pp, size_t *lenp)
return false;
}
if (VIsual_mode == 'V') {
- *pp = (char *)get_cursor_line_ptr();
- *lenp = STRLEN(*pp);
+ *pp = get_cursor_line_ptr();
+ *lenp = strlen(*pp);
} else {
if (lt(curwin->w_cursor, VIsual)) {
- *pp = (char *)ml_get_pos(&curwin->w_cursor);
+ *pp = ml_get_pos(&curwin->w_cursor);
*lenp = (size_t)VIsual.col - (size_t)curwin->w_cursor.col + 1;
} else {
- *pp = (char *)ml_get_pos(&VIsual);
+ *pp = ml_get_pos(&VIsual);
*lenp = (size_t)curwin->w_cursor.col - (size_t)VIsual.col + 1;
}
if (**pp == NUL) {
@@ -4453,7 +3646,7 @@ bool get_visual_text(cmdarg_T *cap, char **pp, size_t *lenp)
static void nv_tagpop(cmdarg_T *cap)
{
if (!checkclearopq(cap->oap)) {
- do_tag((char_u *)"", DT_POP, (int)cap->count1, false, true);
+ do_tag("", DT_POP, (int)cap->count1, false, true);
}
}
@@ -4480,7 +3673,9 @@ static void nv_scroll(cmdarg_T *cap)
&& curwin->w_cursor.lnum > curwin->w_topline; n--) {
(void)hasFolding(curwin->w_cursor.lnum,
&curwin->w_cursor.lnum, NULL);
- curwin->w_cursor.lnum--;
+ if (curwin->w_cursor.lnum > curwin->w_topline) {
+ curwin->w_cursor.lnum--;
+ }
}
} else {
curwin->w_cursor.lnum -= (linenr_T)cap->count1 - 1;
@@ -4566,9 +3761,9 @@ static void nv_right(cmdarg_T *cap)
// <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((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))
+ 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))
&& 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
@@ -4602,7 +3797,7 @@ static void nv_right(cmdarg_T *cap)
if (virtual_active()) {
oneright();
} else {
- curwin->w_cursor.col += utfc_ptr2len((char *)get_cursor_pos_ptr());
+ curwin->w_cursor.col += utfc_ptr2len(get_cursor_pos_ptr());
}
}
}
@@ -4636,9 +3831,9 @@ static void nv_left(cmdarg_T *cap)
// '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((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))
+ && vim_strchr(p_ww, 'b') != NULL)
+ || (cap->cmdchar == 'h' && vim_strchr(p_ww, 'h') != NULL)
+ || (cap->cmdchar == K_LEFT && vim_strchr(p_ww, '<') != NULL))
&& curwin->w_cursor.lnum > 1) {
curwin->w_cursor.lnum--;
coladvance(MAXCOL);
@@ -4650,10 +3845,10 @@ static void nv_left(cmdarg_T *cap)
// Don't adjust op_end now, otherwise it won't work.
if ((cap->oap->op_type == OP_DELETE || cap->oap->op_type == OP_CHANGE)
&& !LINEEMPTY(curwin->w_cursor.lnum)) {
- char_u *cp = get_cursor_pos_ptr();
+ char *cp = get_cursor_pos_ptr();
if (*cp != NUL) {
- curwin->w_cursor.col += utfc_ptr2len((char *)cp);
+ curwin->w_cursor.col += utfc_ptr2len(cp);
}
cap->retval |= CA_NO_ADJ_OP_END;
}
@@ -4679,13 +3874,14 @@ static void nv_up(cmdarg_T *cap)
// <S-Up> is page up
cap->arg = BACKWARD;
nv_page(cap);
- } else {
- cap->oap->motion_type = kMTLineWise;
- if (cursor_up(cap->count1, cap->oap->op_type == OP_NOP) == false) {
- clearopbeep(cap->oap);
- } else if (cap->arg) {
- beginline(BL_WHITE | BL_FIX);
- }
+ return;
+ }
+
+ cap->oap->motion_type = kMTLineWise;
+ if (cursor_up(cap->count1, cap->oap->op_type == OP_NOP) == false) {
+ clearopbeep(cap->oap);
+ } else if (cap->arg) {
+ beginline(BL_WHITE | BL_FIX);
}
}
@@ -4725,14 +3921,10 @@ static void nv_down(cmdarg_T *cap)
/// Grab the file name under the cursor and edit it.
static void nv_gotofile(cmdarg_T *cap)
{
- char_u *ptr;
+ char *ptr;
linenr_T lnum = -1;
- if (check_text_locked(cap->oap)) {
- return;
- }
- if (curbuf_locked()) {
- clearop(cap->oap);
+ if (check_text_or_curbuf_locked(cap->oap)) {
return;
}
@@ -4744,7 +3936,7 @@ static void nv_gotofile(cmdarg_T *cap)
(void)autowrite(curbuf, false);
}
setpcmark();
- if (do_ecmd(0, (char *)ptr, NULL, NULL, ECMD_LAST,
+ if (do_ecmd(0, ptr, NULL, NULL, ECMD_LAST,
buf_hide(curbuf) ? ECMD_HIDE : 0, curwin) == OK
&& cap->nchar == 'F' && lnum >= 0) {
curwin->w_cursor.lnum = lnum;
@@ -4841,9 +4033,8 @@ static void nv_next(cmdarg_T *cap)
/// @param opt extra flags for do_search()
///
/// @return 0 for failure, 1 for found, 2 for found and line offset added.
-static int normal_search(cmdarg_T *cap, int dir, char_u *pat, int opt, int *wrapped)
+static int normal_search(cmdarg_T *cap, int dir, char *pat, int opt, int *wrapped)
{
- int i;
searchit_arg_T sia;
cap->oap->motion_type = kMTCharWise;
@@ -4852,8 +4043,8 @@ static int normal_search(cmdarg_T *cap, int dir, char_u *pat, int opt, int *wrap
curwin->w_set_curswant = true;
CLEAR_FIELD(sia);
- i = do_search(cap->oap, dir, dir, pat, cap->count1,
- opt | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG, &sia);
+ int i = do_search(cap->oap, dir, dir, pat, cap->count1,
+ opt | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG, &sia);
if (wrapped != NULL) {
*wrapped = sia.sa_wrapped;
}
@@ -4892,22 +4083,23 @@ static void nv_csearch(cmdarg_T *cap)
cap->oap->motion_type = kMTCharWise;
if (IS_SPECIAL(cap->nchar) || searchc(cap, t_cmd) == false) {
clearopbeep(cap->oap);
- } else {
- curwin->w_set_curswant = true;
- // Include a Tab for "tx" and for "dfx".
- if (gchar_cursor() == TAB && virtual_active() && cap->arg == FORWARD
- && (t_cmd || cap->oap->op_type != OP_NOP)) {
- colnr_T scol, ecol;
+ return;
+ }
- getvcol(curwin, &curwin->w_cursor, &scol, NULL, &ecol);
- curwin->w_cursor.coladd = ecol - scol;
- } else {
- curwin->w_cursor.coladd = 0;
- }
- adjust_for_sel(cap);
- if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP) {
- foldOpenCursor();
- }
+ curwin->w_set_curswant = true;
+ // Include a Tab for "tx" and for "dfx".
+ if (gchar_cursor() == TAB && virtual_active() && cap->arg == FORWARD
+ && (t_cmd || cap->oap->op_type != OP_NOP)) {
+ colnr_T scol, ecol;
+
+ getvcol(curwin, &curwin->w_cursor, &scol, NULL, &ecol);
+ curwin->w_cursor.coladd = ecol - scol;
+ } else {
+ curwin->w_cursor.coladd = 0;
+ }
+ adjust_for_sel(cap);
+ if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP) {
+ foldOpenCursor();
}
}
@@ -5061,7 +4253,7 @@ static void nv_brackets(cmdarg_T *cap)
} else {
// Make a copy, if the line was changed it will be freed.
ptr = xstrnsave(ptr, len);
- find_pattern_in_path((char_u *)ptr, 0, len, true,
+ find_pattern_in_path(ptr, 0, len, true,
cap->count0 == 0 ? !isupper(cap->nchar) : false,
(((cap->nchar & 0xf) == ('d' & 0xf))
? FIND_DEFINE
@@ -5154,9 +4346,8 @@ static void nv_brackets(cmdarg_T *cap)
cap->nchar == 's', false, NULL) == 0) {
clearopbeep(cap->oap);
break;
- } else {
- curwin->w_set_curswant = true;
}
+ curwin->w_set_curswant = true;
}
if (cap->oap->op_type == OP_NOP && (fdo_flags & FDO_SEARCH) && KeyTyped) {
foldOpenCursor();
@@ -5230,25 +4421,28 @@ static void nv_brace(cmdarg_T *cap)
cap->oap->inclusive = false;
curwin->w_set_curswant = true;
- if (findsent(cap->arg, cap->count1) == false) {
+ if (findsent(cap->arg, cap->count1) == FAIL) {
clearopbeep(cap->oap);
- } else {
- // Don't leave the cursor on the NUL past end of line.
- adjust_cursor(cap->oap);
- curwin->w_cursor.coladd = 0;
- if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP) {
- foldOpenCursor();
- }
+ return;
+ }
+
+ // Don't leave the cursor on the NUL past end of line.
+ adjust_cursor(cap->oap);
+ curwin->w_cursor.coladd = 0;
+ if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP) {
+ foldOpenCursor();
}
}
/// "m" command: Mark a position.
static void nv_mark(cmdarg_T *cap)
{
- if (!checkclearop(cap->oap)) {
- if (setmark(cap->nchar) == false) {
- clearopbeep(cap->oap);
- }
+ if (checkclearop(cap->oap)) {
+ return;
+ }
+
+ if (setmark(cap->nchar) == false) {
+ clearopbeep(cap->oap);
}
}
@@ -5262,11 +4456,12 @@ static void nv_findpar(cmdarg_T *cap)
curwin->w_set_curswant = true;
if (!findpar(&cap->oap->inclusive, cap->arg, cap->count1, NUL, false)) {
clearopbeep(cap->oap);
- } else {
- curwin->w_cursor.coladd = 0;
- if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP) {
- foldOpenCursor();
- }
+ return;
+ }
+
+ curwin->w_cursor.coladd = 0;
+ if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP) {
+ foldOpenCursor();
}
}
@@ -5287,20 +4482,22 @@ static void nv_undo(cmdarg_T *cap)
/// <Undo> command.
static void nv_kundo(cmdarg_T *cap)
{
- if (!checkclearopq(cap->oap)) {
- if (bt_prompt(curbuf)) {
- clearopbeep(cap->oap);
- return;
- }
- u_undo((int)cap->count1);
- curwin->w_set_curswant = true;
+ if (checkclearopq(cap->oap)) {
+ return;
}
+
+ if (bt_prompt(curbuf)) {
+ clearopbeep(cap->oap);
+ return;
+ }
+ u_undo((int)cap->count1);
+ curwin->w_set_curswant = true;
}
/// Handle the "r" command.
static void nv_replace(cmdarg_T *cap)
{
- char_u *ptr;
+ char *ptr;
int had_ctrl_v;
if (checkclearop(cap->oap)) {
@@ -5364,7 +4561,7 @@ static void nv_replace(cmdarg_T *cap)
// Abort if not enough characters to replace.
ptr = get_cursor_pos_ptr();
- if (STRLEN(ptr) < (unsigned)cap->count1
+ if (strlen(ptr) < (unsigned)cap->count1
|| (mb_charlen(ptr) < cap->count1)) {
clearopbeep(cap->oap);
return;
@@ -5505,15 +4702,20 @@ static void nv_Replace(cmdarg_T *cap)
VIsual_mode_orig = VIsual_mode; // remember original area for gv
VIsual_mode = 'V';
nv_operator(cap);
- } else if (!checkclearopq(cap->oap)) {
- if (!MODIFIABLE(curbuf)) {
- emsg(_(e_modifiable));
- } else {
- if (virtual_active()) {
- coladvance(getviscol());
- }
- invoke_edit(cap, false, cap->arg ? 'V' : 'R', false);
+ return;
+ }
+
+ if (checkclearopq(cap->oap)) {
+ return;
+ }
+
+ if (!MODIFIABLE(curbuf)) {
+ emsg(_(e_modifiable));
+ } else {
+ if (virtual_active()) {
+ coladvance(getviscol());
}
+ invoke_edit(cap, false, cap->arg ? 'V' : 'R', false);
}
}
@@ -5524,20 +4726,25 @@ static void nv_vreplace(cmdarg_T *cap)
cap->cmdchar = 'r';
cap->nchar = cap->extra_char;
nv_replace(cap); // Do same as "r" in Visual mode for now
- } else if (!checkclearopq(cap->oap)) {
- if (!MODIFIABLE(curbuf)) {
- emsg(_(e_modifiable));
- } else {
- if (cap->extra_char == Ctrl_V) { // get another character
- cap->extra_char = get_literal(false);
- }
- stuffcharReadbuff(cap->extra_char);
- stuffcharReadbuff(ESC);
- if (virtual_active()) {
- coladvance(getviscol());
- }
- invoke_edit(cap, true, 'v', false);
+ return;
+ }
+
+ if (checkclearopq(cap->oap)) {
+ return;
+ }
+
+ if (!MODIFIABLE(curbuf)) {
+ emsg(_(e_modifiable));
+ } else {
+ if (cap->extra_char == Ctrl_V) { // get another character
+ cap->extra_char = get_literal(false);
}
+ stuffcharReadbuff(cap->extra_char);
+ stuffcharReadbuff(ESC);
+ if (virtual_active()) {
+ coladvance(getviscol());
+ }
+ invoke_edit(cap, true, 'v', false);
}
}
@@ -5552,7 +4759,7 @@ static void n_swapchar(cmdarg_T *cap)
return;
}
- if (LINEEMPTY(curwin->w_cursor.lnum) && vim_strchr((char *)p_ww, '~') == NULL) {
+ if (LINEEMPTY(curwin->w_cursor.lnum) && vim_strchr(p_ww, '~') == NULL) {
clearopbeep(cap->oap);
return;
}
@@ -5568,7 +4775,7 @@ static void n_swapchar(cmdarg_T *cap)
did_change |= swapchar(cap->oap->op_type, &curwin->w_cursor);
inc_cursor();
if (gchar_cursor() == NUL) {
- if (vim_strchr((char *)p_ww, '~') != NULL
+ if (vim_strchr(p_ww, '~') != NULL
&& curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
curwin->w_cursor.lnum++;
curwin->w_cursor.col = 0;
@@ -5624,7 +4831,7 @@ static MarkMoveRes nv_mark_move_to(cmdarg_T *cap, MarkMove flags, fmark_T *fm)
/// Handle commands that are operators in Visual mode.
static void v_visop(cmdarg_T *cap)
{
- static char_u trans[] = "YyDdCcxdXdAAIIrr";
+ static char trans[] = "YyDdCcxdXdAAIIrr";
// Uppercase means linewise, except in block mode, then "D" deletes till
// the end of the line, and "C" replaces till EOL
@@ -5636,7 +4843,7 @@ static void v_visop(cmdarg_T *cap)
curwin->w_curswant = MAXCOL;
}
}
- cap->cmdchar = (uint8_t)(*(vim_strchr((char *)trans, cap->cmdchar) + 1));
+ cap->cmdchar = (uint8_t)(*(vim_strchr(trans, cap->cmdchar) + 1));
nv_operator(cap);
}
@@ -5695,6 +4902,10 @@ static void nv_gomark(cmdarg_T *cap)
{
int name;
MarkMove flags = jop_flags & JOP_VIEW ? kMarkSetView : 0; // flags for moving to the mark
+ if (cap->oap->op_type != OP_NOP) {
+ // When there is a pending operator, do not restore the view as this is usually unexpected.
+ flags = 0;
+ }
MarkMoveRes move_res = 0; // Result from moving to the mark
const bool old_KeyTyped = KeyTyped; // getting file may reset it
@@ -5734,42 +4945,44 @@ static void nv_pcmark(cmdarg_T *cap)
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) {
- if (!goto_tabpage_lastused()) {
- clearopbeep(cap->oap);
- }
- return;
- }
+ if (checkclearopq(cap->oap)) {
+ return;
+ }
- if (cap->cmdchar == 'g') {
- fm = get_changelist(curbuf, curwin, (int)cap->count1);
- } else {
- 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"));
- } else if (cap->count1 < 0) {
- emsg(_("E662: At start of changelist"));
- } else {
- emsg(_("E663: At end of changelist"));
- }
- } else {
+ if (cap->cmdchar == TAB && mod_mask == MOD_MASK_CTRL) {
+ if (!goto_tabpage_lastused()) {
clearopbeep(cap->oap);
}
- if (cap->oap->op_type == OP_NOP
- && (move_res & kMarkSwitchedBuf || move_res & kMarkChangedLine)
- && (fdo_flags & FDO_MARK)
- && old_KeyTyped) {
- foldOpenCursor();
+ return;
+ }
+
+ if (cap->cmdchar == 'g') {
+ fm = get_changelist(curbuf, curwin, (int)cap->count1);
+ } else {
+ 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"));
+ } else if (cap->count1 < 0) {
+ emsg(_("E662: At start of changelist"));
+ } else {
+ emsg(_("E663: At end of changelist"));
}
+ } else {
+ clearopbeep(cap->oap);
+ }
+ if (cap->oap->op_type == OP_NOP
+ && (move_res & kMarkSwitchedBuf || move_res & kMarkChangedLine)
+ && (fdo_flags & FDO_MARK)
+ && old_KeyTyped) {
+ foldOpenCursor();
}
}
@@ -5819,7 +5032,7 @@ static void nv_visual(cmdarg_T *cap)
showmode();
may_trigger_modechanged();
}
- redraw_curbuf_later(INVERTED); // update the inversion
+ redraw_curbuf_later(UPD_INVERTED); // update the inversion
} else { // start Visual mode
if (cap->count0 > 0 && resel_VIsual_mode != NUL) {
// use previously selected part
@@ -5844,10 +5057,12 @@ static void nv_visual(cmdarg_T *cap)
VIsual_mode = resel_VIsual_mode;
if (VIsual_mode == 'v') {
if (resel_VIsual_line_count <= 1) {
- validate_virtcol();
+ update_curswant_force();
assert(cap->count0 >= INT_MIN && cap->count0 <= INT_MAX);
- curwin->w_curswant = (curwin->w_virtcol
- + resel_VIsual_vcol * (int)cap->count0 - 1);
+ curwin->w_curswant += resel_VIsual_vcol * (int)cap->count0;
+ if (*p_sel != 'e') {
+ curwin->w_curswant--;
+ }
} else {
curwin->w_curswant = resel_VIsual_vcol;
}
@@ -5857,15 +5072,14 @@ static void nv_visual(cmdarg_T *cap)
curwin->w_curswant = MAXCOL;
coladvance(MAXCOL);
} else if (VIsual_mode == Ctrl_V) {
- validate_virtcol();
+ update_curswant_force();
assert(cap->count0 >= INT_MIN && cap->count0 <= INT_MAX);
- curwin->w_curswant = (curwin->w_virtcol
- + resel_VIsual_vcol * (int)cap->count0 - 1);
+ curwin->w_curswant += resel_VIsual_vcol * (int)cap->count0 - 1;
coladvance(curwin->w_curswant);
} else {
curwin->w_set_curswant = true;
}
- redraw_curbuf_later(INVERTED); // show the inversion
+ redraw_curbuf_later(UPD_INVERTED); // show the inversion
} else {
if (!cap->arg) {
// start Select mode when 'selectmode' contains "cmd"
@@ -5900,7 +5114,7 @@ void start_selection(void)
void may_start_select(int c)
{
VIsual_select = (c == 'o' || (stuff_empty() && typebuf_typed()))
- && vim_strchr((char *)p_slm, c) != NULL;
+ && vim_strchr(p_slm, c) != NULL;
}
/// Start Visual mode "c".
@@ -5931,7 +5145,7 @@ static void n_start_visual_mode(int c)
}
// Only need to redraw this line, unless still need to redraw an old
// Visual area (when 'lazyredraw' is set).
- if (curwin->w_redr_type < INVERTED) {
+ if (curwin->w_redr_type < UPD_INVERTED) {
curwin->w_old_cursor_lnum = curwin->w_cursor.lnum;
curwin->w_old_visual_lnum = curwin->w_cursor.lnum;
}
@@ -6018,7 +5232,7 @@ static void nv_gv_cmd(cmdarg_T *cap)
may_start_select('c');
}
setmouse();
- redraw_curbuf_later(INVERTED);
+ redraw_curbuf_later(UPD_INVERTED);
showmode();
}
@@ -6071,7 +5285,7 @@ static void nv_g_underscore_cmd(cmdarg_T *cap)
return;
}
- char_u *ptr = get_cursor_line_ptr();
+ char *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) {
@@ -6109,9 +5323,7 @@ static void nv_g_dollar_cmd(cmdarg_T *cap)
coladvance((colnr_T)i);
// Make sure we stick in this column.
- validate_virtcol();
- curwin->w_curswant = curwin->w_virtcol;
- curwin->w_set_curswant = false;
+ update_curswant_force();
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
@@ -6142,9 +5354,7 @@ static void nv_g_dollar_cmd(cmdarg_T *cap)
}
// Make sure we stick in this column.
- validate_virtcol();
- curwin->w_curswant = curwin->w_virtcol;
- curwin->w_set_curswant = false;
+ update_curswant_force();
}
}
@@ -6154,7 +5364,7 @@ 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());
+ 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;
@@ -6503,43 +5713,46 @@ static void nv_g_cmd(cmdarg_T *cap)
/// Handle "o" and "O" commands.
static void n_opencmd(cmdarg_T *cap)
{
- if (!checkclearopq(cap->oap)) {
- if (cap->cmdchar == 'O') {
- // Open above the first line of a folded sequence of lines
- (void)hasFolding(curwin->w_cursor.lnum,
- &curwin->w_cursor.lnum, NULL);
- } else {
- // Open below the last line of a folded sequence of lines
- (void)hasFolding(curwin->w_cursor.lnum,
- NULL, &curwin->w_cursor.lnum);
- }
- if (u_save((linenr_T)(curwin->w_cursor.lnum -
- (cap->cmdchar == 'O' ? 1 : 0)),
- (linenr_T)(curwin->w_cursor.lnum +
- (cap->cmdchar == 'o' ? 1 : 0))
- )
- && open_line(cap->cmdchar == 'O' ? BACKWARD : FORWARD,
- 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;
- }
- invoke_edit(cap, false, cap->cmdchar, true);
+ if (checkclearopq(cap->oap)) {
+ return;
+ }
+
+ if (cap->cmdchar == 'O') {
+ // Open above the first line of a folded sequence of lines
+ (void)hasFolding(curwin->w_cursor.lnum,
+ &curwin->w_cursor.lnum, NULL);
+ } else {
+ // Open below the last line of a folded sequence of lines
+ (void)hasFolding(curwin->w_cursor.lnum,
+ NULL, &curwin->w_cursor.lnum);
+ }
+ if (u_save((linenr_T)(curwin->w_cursor.lnum -
+ (cap->cmdchar == 'O' ? 1 : 0)),
+ (linenr_T)(curwin->w_cursor.lnum +
+ (cap->cmdchar == 'o' ? 1 : 0)))
+ && open_line(cap->cmdchar == 'O' ? BACKWARD : FORWARD,
+ 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;
}
+ invoke_edit(cap, false, cap->cmdchar, true);
}
}
/// "." 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 (start_redo(cap->count0, restart_edit != 0 && !arrow_used) == false) {
- clearopbeep(cap->oap);
- }
+ if (checkclearopq(cap->oap)) {
+ return;
+ }
+
+ // 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);
}
}
@@ -6563,10 +5776,12 @@ static void nv_redo_or_register(cmdarg_T *cap)
return;
}
- if (!checkclearopq(cap->oap)) {
- u_redo((int)cap->count1);
- curwin->w_set_curswant = true;
+ if (checkclearopq(cap->oap)) {
+ return;
}
+
+ u_redo((int)cap->count1);
+ curwin->w_set_curswant = true;
}
/// Handle "U" command.
@@ -6578,10 +5793,15 @@ static void nv_Undo(cmdarg_T *cap)
cap->cmdchar = 'g';
cap->nchar = 'U';
nv_operator(cap);
- } else if (!checkclearopq(cap->oap)) {
- u_undoline();
- curwin->w_set_curswant = true;
+ return;
+ }
+
+ if (checkclearopq(cap->oap)) {
+ return;
}
+
+ u_undoline();
+ curwin->w_set_curswant = true;
}
/// '~' command: If tilde is not an operator and Visual is off: swap case of a
@@ -6837,7 +6057,7 @@ bool unadjust_for_sel(void)
mark_mb_adjustpos(curbuf, pp);
} else if (pp->lnum > 1) {
pp->lnum--;
- pp->col = (colnr_T)STRLEN(ml_get(pp->lnum));
+ pp->col = (colnr_T)strlen(ml_get(pp->lnum));
return true;
}
}
@@ -6901,7 +6121,7 @@ static void nv_normal(cmdarg_T *cap)
}
if (VIsual_active) {
end_visual_mode(); // stop Visual
- redraw_curbuf_later(INVERTED);
+ redraw_curbuf_later(UPD_INVERTED);
}
} else {
clearopbeep(cap->oap);
@@ -6920,10 +6140,7 @@ static void nv_esc(cmdarg_T *cap)
&& cap->oap->regname == 0);
if (cap->arg) { // true for CTRL-C
- if (restart_edit == 0
- && cmdwin_type == 0
- && !VIsual_active
- && no_reason) {
+ if (restart_edit == 0 && cmdwin_type == 0 && !VIsual_active && no_reason) {
if (anyBufIsChanged()) {
msg(_("Type :qa! and press <Enter> to abandon all changes"
" and exit Nvim"));
@@ -6955,7 +6172,7 @@ static void nv_esc(cmdarg_T *cap)
end_visual_mode(); // stop Visual
check_cursor_col(); // make sure cursor is not beyond EOL
curwin->w_set_curswant = true;
- redraw_curbuf_later(INVERTED);
+ redraw_curbuf_later(UPD_INVERTED);
} else if (no_reason) {
vim_beep(BO_ESC);
}
@@ -6974,7 +6191,7 @@ void set_cursor_for_append_to_line(void)
coladvance(MAXCOL);
State = save_State;
} else {
- curwin->w_cursor.col += (colnr_T)STRLEN(get_cursor_pos_ptr());
+ curwin->w_cursor.col += (colnr_T)strlen(get_cursor_pos_ptr());
}
}
@@ -7067,7 +6284,7 @@ static void nv_object(cmdarg_T *cap)
{
bool flag;
bool include;
- char_u *mps_save;
+ char *mps_save;
if (cap->cmdchar == 'i') {
include = false; // "ix" = inner object: exclude white space
@@ -7076,7 +6293,7 @@ static void nv_object(cmdarg_T *cap)
}
// Make sure (), [], {} and <> are in 'matchpairs'
mps_save = curbuf->b_p_mps;
- curbuf->b_p_mps = (char_u *)"(:),{:},[:],<:>";
+ curbuf->b_p_mps = "(:),{:},[:],<:>";
switch (cap->nchar) {
case 'w': // "aw" = a word
@@ -7147,16 +6364,21 @@ static void nv_record(cmdarg_T *cap)
cap->cmdchar = 'g';
cap->nchar = 'q';
nv_operator(cap);
- } else if (!checkclearop(cap->oap)) {
- if (cap->nchar == ':' || cap->nchar == '/' || cap->nchar == '?') {
- stuffcharReadbuff(cap->nchar);
- stuffcharReadbuff(K_CMDWIN);
- } else {
- // (stop) recording into a named register, unless executing a
- // register.
- if (reg_executing == 0 && do_record(cap->nchar) == FAIL) {
- clearopbeep(cap->oap);
- }
+ return;
+ }
+
+ if (checkclearop(cap->oap)) {
+ return;
+ }
+
+ if (cap->nchar == ':' || cap->nchar == '/' || cap->nchar == '?') {
+ stuffcharReadbuff(cap->nchar);
+ stuffcharReadbuff(K_CMDWIN);
+ } else {
+ // (stop) recording into a named register, unless executing a
+ // register.
+ if (reg_executing == 0 && do_record(cap->nchar) == FAIL) {
+ clearopbeep(cap->oap);
}
}
}
@@ -7198,24 +6420,29 @@ static void nv_join(cmdarg_T *cap)
{
if (VIsual_active) { // join the visual lines
nv_operator(cap);
- } else if (!checkclearop(cap->oap)) {
- if (cap->count0 <= 1) {
- cap->count0 = 2; // default for join is two lines!
- }
- if (curwin->w_cursor.lnum + cap->count0 - 1 >
- curbuf->b_ml.ml_line_count) {
- // can't join when on the last line
- if (cap->count0 <= 2) {
- clearopbeep(cap->oap);
- return;
- }
- cap->count0 = curbuf->b_ml.ml_line_count - curwin->w_cursor.lnum + 1;
- }
+ return;
+ }
- prep_redo(cap->oap->regname, cap->count0,
- NUL, cap->cmdchar, NUL, NUL, cap->nchar);
- do_join((size_t)cap->count0, cap->nchar == NUL, true, true, true);
+ if (checkclearop(cap->oap)) {
+ return;
+ }
+
+ if (cap->count0 <= 1) {
+ cap->count0 = 2; // default for join is two lines!
}
+ if (curwin->w_cursor.lnum + cap->count0 - 1 >
+ curbuf->b_ml.ml_line_count) {
+ // can't join when on the last line
+ if (cap->count0 <= 2) {
+ clearopbeep(cap->oap);
+ return;
+ }
+ cap->count0 = curbuf->b_ml.ml_line_count - curwin->w_cursor.lnum + 1;
+ }
+
+ prep_redo(cap->oap->regname, cap->count0,
+ NUL, cap->cmdchar, NUL, NUL, cap->nchar);
+ do_join((size_t)cap->count0, cap->nchar == NUL, true, true, true);
}
/// "P", "gP", "p" and "gp" commands.
@@ -7245,117 +6472,121 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent)
} else {
clearopbeep(cap->oap);
}
- } else if (bt_prompt(curbuf) && !prompt_curpos_editable()) {
- clearopbeep(cap->oap);
- } else {
- if (fix_indent) {
- dir = (cap->cmdchar == ']' && cap->nchar == 'p')
- ? FORWARD : BACKWARD;
- flags |= PUT_FIXINDENT;
- } else {
- dir = (cap->cmdchar == 'P'
- || ((cap->cmdchar == 'g' || cap->cmdchar == 'z')
- && cap->nchar == 'P')) ? BACKWARD : FORWARD;
- }
- prep_redo_cmd(cap);
- if (cap->cmdchar == 'g') {
- flags |= PUT_CURSEND;
- } else if (cap->cmdchar == 'z') {
- flags |= PUT_BLOCK_INNER;
- }
-
- if (VIsual_active) {
- // Putting in Visual mode: The put text replaces the selected
- // text. First delete the selected text, then put the new text.
- // Need to save and restore the registers that the delete
- // 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);
- if (regname == 0 || regname == '"' || clipoverwrite
- || ascii_isdigit(regname) || regname == '-') {
- // The delete might overwrite the register we want to put, save it first
- savereg = copy_register(regname);
- }
+ return;
+ }
- // To place the cursor correctly after a blockwise put, and to leave the
- // text in the correct position when putting over a selection with
- // 'virtualedit' and past the end of the line, we use the 'c' operator in
- // do_put(), which requires the visual selection to still be active.
- if (!VIsual_active || VIsual_mode == 'V' || regname != '.') {
- // Now delete the selected text. Avoid messages here.
- cap->cmdchar = 'd';
- cap->nchar = NUL;
- cap->oap->regname = keep_registers ? '_' : NUL;
- msg_silent++;
- nv_operator(cap);
- do_pending_operator(cap, 0, false);
- empty = (curbuf->b_ml.ml_flags & ML_EMPTY);
- msg_silent--;
+ if (bt_prompt(curbuf) && !prompt_curpos_editable()) {
+ clearopbeep(cap->oap);
+ return;
+ }
- // delete PUT_LINE_BACKWARD;
- cap->oap->regname = regname;
- }
+ if (fix_indent) {
+ dir = (cap->cmdchar == ']' && cap->nchar == 'p')
+ ? FORWARD : BACKWARD;
+ flags |= PUT_FIXINDENT;
+ } else {
+ dir = (cap->cmdchar == 'P'
+ || ((cap->cmdchar == 'g' || cap->cmdchar == 'z')
+ && cap->nchar == 'P')) ? BACKWARD : FORWARD;
+ }
+ prep_redo_cmd(cap);
+ if (cap->cmdchar == 'g') {
+ flags |= PUT_CURSEND;
+ } else if (cap->cmdchar == 'z') {
+ flags |= PUT_BLOCK_INNER;
+ }
- // When deleted a linewise Visual area, put the register as
- // lines to avoid it joined with the next line. When deletion was
- // charwise, split a line when putting lines.
- if (VIsual_mode == 'V') {
- flags |= PUT_LINE;
- } else if (VIsual_mode == 'v') {
- flags |= PUT_LINE_SPLIT;
- }
- if (VIsual_mode == Ctrl_V && dir == FORWARD) {
- flags |= PUT_LINE_FORWARD;
- }
- dir = BACKWARD;
- if ((VIsual_mode != 'V'
- && curwin->w_cursor.col < curbuf->b_op_start.col)
- || (VIsual_mode == 'V'
- && curwin->w_cursor.lnum < curbuf->b_op_start.lnum)) {
- // cursor is at the end of the line or end of file, put
- // forward.
- dir = FORWARD;
- }
- // May have been reset in do_put().
- VIsual_active = true;
+ if (VIsual_active) {
+ // Putting in Visual mode: The put text replaces the selected
+ // text. First delete the selected text, then put the new text.
+ // Need to save and restore the registers that the delete
+ // 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);
+ if (regname == 0 || regname == '"' || clipoverwrite
+ || ascii_isdigit(regname) || regname == '-') {
+ // The delete might overwrite the register we want to put, save it first
+ savereg = copy_register(regname);
+ }
+
+ // To place the cursor correctly after a blockwise put, and to leave the
+ // text in the correct position when putting over a selection with
+ // 'virtualedit' and past the end of the line, we use the 'c' operator in
+ // do_put(), which requires the visual selection to still be active.
+ if (!VIsual_active || VIsual_mode == 'V' || regname != '.') {
+ // Now delete the selected text. Avoid messages here.
+ cap->cmdchar = 'd';
+ cap->nchar = NUL;
+ cap->oap->regname = keep_registers ? '_' : NUL;
+ msg_silent++;
+ nv_operator(cap);
+ do_pending_operator(cap, 0, false);
+ empty = (curbuf->b_ml.ml_flags & ML_EMPTY);
+ msg_silent--;
+
+ // delete PUT_LINE_BACKWARD;
+ cap->oap->regname = regname;
+ }
+
+ // When deleted a linewise Visual area, put the register as
+ // lines to avoid it joined with the next line. When deletion was
+ // charwise, split a line when putting lines.
+ if (VIsual_mode == 'V') {
+ flags |= PUT_LINE;
+ } else if (VIsual_mode == 'v') {
+ flags |= PUT_LINE_SPLIT;
+ }
+ if (VIsual_mode == Ctrl_V && dir == FORWARD) {
+ flags |= PUT_LINE_FORWARD;
+ }
+ dir = BACKWARD;
+ if ((VIsual_mode != 'V'
+ && curwin->w_cursor.col < curbuf->b_op_start.col)
+ || (VIsual_mode == 'V'
+ && curwin->w_cursor.lnum < curbuf->b_op_start.lnum)) {
+ // cursor is at the end of the line or end of file, put
+ // forward.
+ dir = FORWARD;
}
- do_put(cap->oap->regname, savereg, dir, cap->count1, flags);
+ // May have been reset in do_put().
+ VIsual_active = true;
+ }
+ do_put(cap->oap->regname, savereg, dir, cap->count1, flags);
- // If a register was saved, free it
- if (savereg != NULL) {
- free_register(savereg);
- xfree(savereg);
- }
+ // If a register was saved, free it
+ if (savereg != NULL) {
+ free_register(savereg);
+ xfree(savereg);
+ }
- // What to reselect with "gv"? Selecting the just put text seems to
- // be the most useful, since the original text was removed.
- if (was_visual) {
- curbuf->b_visual.vi_start = curbuf->b_op_start;
- curbuf->b_visual.vi_end = curbuf->b_op_end;
- // need to adjust cursor position
- if (*p_sel == 'e') {
- inc(&curbuf->b_visual.vi_end);
- }
+ // What to reselect with "gv"? Selecting the just put text seems to
+ // be the most useful, since the original text was removed.
+ if (was_visual) {
+ curbuf->b_visual.vi_start = curbuf->b_op_start;
+ curbuf->b_visual.vi_end = curbuf->b_op_end;
+ // need to adjust cursor position
+ if (*p_sel == 'e') {
+ inc(&curbuf->b_visual.vi_end);
}
+ }
- // When all lines were selected and deleted do_put() leaves an empty
- // line that needs to be deleted now.
- if (empty && *ml_get(curbuf->b_ml.ml_line_count) == NUL) {
- ml_delete(curbuf->b_ml.ml_line_count, true);
- deleted_lines(curbuf->b_ml.ml_line_count + 1, 1);
+ // When all lines were selected and deleted do_put() leaves an empty
+ // line that needs to be deleted now.
+ if (empty && *ml_get(curbuf->b_ml.ml_line_count) == NUL) {
+ ml_delete(curbuf->b_ml.ml_line_count, true);
+ deleted_lines(curbuf->b_ml.ml_line_count + 1, 1);
- // If the cursor was in that line, move it to the end of the last
- // line.
- if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
- curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
- coladvance(MAXCOL);
- }
+ // If the cursor was in that line, move it to the end of the last
+ // line.
+ if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
+ curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
+ coladvance(MAXCOL);
}
- auto_format(false, true);
}
+ auto_format(false, true);
}
/// "o" and "O" commands.
@@ -7399,12 +6630,6 @@ static void nv_event(cmdarg_T *cap)
}
}
-/// @return true when 'mousemodel' is set to "popup" or "popup_setpos".
-static bool mouse_model_popup(void)
-{
- return p_mousem[0] == 'p';
-}
-
void normal_cmd(oparg_T *oap, bool toplevel)
{
NormalState s;
diff --git a/src/nvim/normal.h b/src/nvim/normal.h
index 9bda70eacd..bed1a40b97 100644
--- a/src/nvim/normal.h
+++ b/src/nvim/normal.h
@@ -3,7 +3,8 @@
#include <stdbool.h>
-#include "nvim/buffer_defs.h" // for win_T
+#include "nvim/buffer_defs.h"
+#include "nvim/macros.h"
#include "nvim/pos.h"
// Values for find_ident_under_cursor()
@@ -22,9 +23,7 @@ typedef enum {
kMTUnknown = -1, ///< Unknown or invalid motion type
} MotionType;
-/*
- * Arguments for operators.
- */
+// Arguments for operators.
typedef struct oparg_S {
int op_type; // current pending operator type
int regname; // register to use for the operator
@@ -53,11 +52,9 @@ typedef struct oparg_S {
// block
} oparg_T;
-/*
- * Arguments for Normal mode commands.
- */
+// Arguments for Normal mode commands.
typedef struct cmdarg_S {
- oparg_T *oap; // Operator arguments
+ oparg_T *oap; // Operator arguments
int prechar; // prefix character (optional, always 'g')
int cmdchar; // command character
int nchar; // next command character (optional)
@@ -69,13 +66,19 @@ typedef struct cmdarg_S {
long count1; // count before command, default 1
int arg; // extra argument from nv_cmds[]
int retval; // return: CA_* values
- char_u *searchbuf; // return: pointer to search pattern or NULL
+ char *searchbuf; // return: pointer to search pattern or NULL
} cmdarg_T;
// values for retval:
#define CA_COMMAND_BUSY 1 // skip restarting edit() once
#define CA_NO_ADJ_OP_END 2 // don't adjust operator end
+// columns needed by shown command
+#define SHOWCMD_COLS 10
+// 'showcmd' buffer shared between normal.c and statusline.c
+#define SHOWCMD_BUFLEN (SHOWCMD_COLS + 1 + 30)
+EXTERN char showcmd_buf[SHOWCMD_BUFLEN];
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "normal.h.generated.h"
#endif
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 3be4536f16..435ca106ab 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -1,18 +1,22 @@
// 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
-/*
- * ops.c: implementation of various operators: op_shift, op_delete, op_tilde,
- * op_change, op_yank, do_put, do_join
- */
+// ops.c: implementation of various operators: op_shift, op_delete, op_tilde,
+// op_change, op_yank, do_put, do_join
#include <assert.h>
+#include <ctype.h>
#include <inttypes.h>
+#include <limits.h>
#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
+#include "nvim/api/private/defs.h"
#include "nvim/ascii.h"
#include "nvim/assert.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/change.h"
#include "nvim/charset.h"
@@ -21,17 +25,19 @@
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
-#include "nvim/ex_cmds.h"
#include "nvim/ex_cmds2.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_getln.h"
#include "nvim/extmark.h"
#include "nvim/fold.h"
+#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
#include "nvim/globals.h"
+#include "nvim/highlight_defs.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
-#include "nvim/lib/kvec.h"
-#include "nvim/log.h"
+#include "nvim/keycodes.h"
#include "nvim/macros.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
@@ -45,12 +51,14 @@
#include "nvim/option.h"
#include "nvim/os/input.h"
#include "nvim/os/time.h"
-#include "nvim/path.h"
#include "nvim/plines.h"
+#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/state.h"
#include "nvim/strings.h"
#include "nvim/terminal.h"
+#include "nvim/textformat.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/vim.h"
@@ -66,25 +74,23 @@ static bool clipboard_delay_update = false; // delay clipboard update
static bool clipboard_needs_update = false; // clipboard was updated
static bool clipboard_didwarn = false;
-/*
- * structure used by block_prep, op_delete and op_yank for blockwise operators
- * also op_change, op_shift, op_insert, op_replace - AKelly
- */
+// structure used by block_prep, op_delete and op_yank for blockwise operators
+// also op_change, op_shift, op_insert, op_replace - AKelly
struct block_def {
int startspaces; // 'extra' cols before first char
int endspaces; // 'extra' cols after last char
int textlen; // chars in block
- char_u *textstart; // pointer to 1st char (partially) in block
+ char *textstart; // pointer to 1st char (partially) in block
colnr_T textcol; // index of chars (partially) in block
colnr_T start_vcol; // start col of 1st char wholly inside block
colnr_T end_vcol; // start col of 1st char wholly after block
- int is_short; // TRUE if line is too short to fit in block
- int is_MAX; // TRUE if curswant==MAXCOL when starting
- int is_oneChar; // TRUE if block within one character
+ int is_short; // true if line is too short to fit in block
+ int is_MAX; // true if curswant==MAXCOL when starting
+ int is_oneChar; // true if block within one character
int pre_whitesp; // screen cols of ws before block
int pre_whitesp_c; // chars of ws before block
colnr_T end_char_vcols; // number of vcols of post-block char
- colnr_T start_char_vcols; // number of vcols of pre-block char
+ colnr_T start_char_vcols; // number of vcols of pre-block char
};
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -95,13 +101,10 @@ struct block_def {
#define OPF_LINES 1 // operator always works on lines
#define OPF_CHANGE 2 // operator changes text
-/*
- * The names of operators.
- * IMPORTANT: Index must correspond with defines in vim.h!!!
- * The third field indicates whether the operator always works on lines.
- */
-static char opchars[][3] =
-{
+// The names of operators.
+// IMPORTANT: Index must correspond with defines in vim.h!!!
+// The third field indicates whether the operator always works on lines.
+static char opchars[][3] = {
{ NUL, NUL, 0 }, // OP_NOP
{ 'd', NUL, OPF_CHANGE }, // OP_DELETE
{ 'y', NUL, 0 }, // OP_YANK
@@ -181,13 +184,13 @@ 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;
@@ -224,7 +227,7 @@ void op_shift(oparg_T *oap, int curs_top, int amount)
}
for (i = oap->line_count - 1; i >= 0; i--) {
- first_char = *get_cursor_line_ptr();
+ first_char = (uint8_t)(*get_cursor_line_ptr());
if (first_char == NUL) { // empty line
curwin->w_cursor.col = 0;
} else if (oap->motion_type == kMTBlockWise) {
@@ -234,7 +237,7 @@ void op_shift(oparg_T *oap, int curs_top, int amount)
// isn't set or 'cindent' isn't set or '#' isn't in 'cino'.
shift_line(oap->op_type == OP_LSHIFT, p_sr, amount, false);
}
- ++curwin->w_cursor.lnum;
+ curwin->w_cursor.lnum++;
}
if (oap->motion_type == kMTBlockWise) {
@@ -244,7 +247,7 @@ void op_shift(oparg_T *oap, int curs_top, int amount)
curwin->w_cursor.lnum = oap->start.lnum;
beginline(BL_SOL | BL_FIX); // shift_line() may have set cursor.col
} else {
- --curwin->w_cursor.lnum; // put cursor on last line, for ":>"
+ curwin->w_cursor.lnum--; // put cursor on last line, for ":>"
}
// The cursor line is not in a closed fold
foldOpenCursor();
@@ -261,17 +264,17 @@ void op_shift(oparg_T *oap, int curs_top, int amount)
"%" PRId64 " line %sed %d times", amount);
char *msg_line_plural = NGETTEXT("%" PRId64 " lines %sed %d time",
"%" PRId64 " lines %sed %d times", amount);
- vim_snprintf((char *)IObuff, IOSIZE,
+ vim_snprintf(IObuff, IOSIZE,
NGETTEXT(msg_line_single, msg_line_plural, oap->line_count),
(int64_t)oap->line_count, op, amount);
- msg_attr_keep((char *)IObuff, 0, true, false);
+ msg_attr_keep(IObuff, 0, true, false);
}
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));
+ 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--;
}
@@ -332,7 +335,7 @@ static void shift_block(oparg_T *oap, int amount)
{
const bool left = (oap->op_type == OP_LSHIFT);
const int oldstate = State;
- char_u *newp;
+ char *newp;
const int oldcol = curwin->w_cursor.col;
const int sw_val = (int)get_sw_value_indent(curbuf);
const int ts_val = (int)curbuf->b_p_ts;
@@ -355,34 +358,40 @@ static void shift_block(oparg_T *oap, int amount)
return; // multiplication overflow
}
- char_u *const oldp = get_cursor_line_ptr();
+ char *const oldp = get_cursor_line_ptr();
int startcol, oldlen, newlen;
if (!left) {
- /*
- * 1. Get start vcol
- * 2. Total ws vcols
- * 3. Divvy into TABs & spp
- * 4. Construct new string
- */
+ // 1. Get start vcol
+ // 2. Total ws vcols
+ // 3. Divvy into TABs & spp
+ // 4. Construct new string
total += bd.pre_whitesp; // all virtual WS up to & incl a split TAB
colnr_T ws_vcol = bd.start_vcol - bd.pre_whitesp;
- char_u *old_textstart = bd.textstart;
+ char *old_textstart = bd.textstart;
if (bd.startspaces) {
- if (utfc_ptr2len((char *)bd.textstart) == 1) {
+ if (utfc_ptr2len(bd.textstart) == 1) {
bd.textstart++;
} else {
ws_vcol = 0;
bd.startspaces = 0;
}
}
- for (; ascii_iswhite(*bd.textstart);) {
- // TODO(fmoralesc): is passing bd.textstart for start of the line OK?
- incr = lbr_chartabsize_adv(bd.textstart, &bd.textstart, bd.start_vcol);
+
+ // TODO(vim): is passing bd.textstart for start of the line OK?
+ chartabsize_T cts;
+ init_chartabsize_arg(&cts, curwin, curwin->w_cursor.lnum,
+ bd.start_vcol, bd.textstart, bd.textstart);
+ while (ascii_iswhite(*cts.cts_ptr)) {
+ incr = lbr_chartabsize_adv(&cts);
total += incr;
- bd.start_vcol += incr;
+ cts.cts_vcol += incr;
}
+ bd.textstart = cts.cts_ptr;
+ bd.start_vcol = cts.cts_vcol;
+ clear_chartabsize_arg(&cts);
+
// OK, now total=all the VWS reqd, and textstart points at the 1st
// non-ws char in the block.
if (!curbuf->b_p_et) {
@@ -394,10 +403,10 @@ static void shift_block(oparg_T *oap, int amount)
// if we're splitting a TAB, allow for it
int col_pre = bd.pre_whitesp_c - (bd.startspaces != 0);
bd.textcol -= col_pre;
- const int len = (int)STRLEN(bd.textstart) + 1;
+ const int len = (int)strlen(bd.textstart) + 1;
int col = bd.textcol + i + j + len;
assert(col >= 0);
- newp = (char_u *)xmalloc((size_t)col);
+ newp = xmalloc((size_t)col);
memset(newp, NUL, (size_t)col);
memmove(newp, oldp, (size_t)bd.textcol);
startcol = bd.textcol;
@@ -410,22 +419,20 @@ static void shift_block(oparg_T *oap, int amount)
} else { // left
colnr_T destination_col; // column to which text in block will
// be shifted
- char_u *verbatim_copy_end; // end of the part of the line which is
+ char *verbatim_copy_end; // end of the part of the line which is
// copied verbatim
colnr_T verbatim_copy_width; // the (displayed) width of this part
// of line
size_t fill; // nr of spaces that replace a TAB
size_t new_line_len; // the length of the line after the
// block shift
- char_u *non_white = bd.textstart;
+ char *non_white = bd.textstart;
- /*
- * Firstly, let's find the first non-whitespace character that is
- * displayed after the block's start column and the character's column
- * number. Also, let's calculate the width of all the whitespace
- * characters that are displayed in the block and precede the searched
- * non-whitespace character.
- */
+ // Firstly, let's find the first non-whitespace character that is
+ // displayed after the block's start column and the character's column
+ // number. Also, let's calculate the width of all the whitespace
+ // characters that are displayed in the block and precede the searched
+ // non-whitespace character.
// If "bd.startspaces" is set, "bd.textstart" points to the character,
// the part of which is displayed at the block's beginning. Let's start
@@ -437,10 +444,16 @@ static void shift_block(oparg_T *oap, int amount)
// The character's column is in "bd.start_vcol".
colnr_T non_white_col = bd.start_vcol;
- while (ascii_iswhite(*non_white)) {
- incr = lbr_chartabsize_adv(bd.textstart, &non_white, non_white_col);
- non_white_col += incr;
+ chartabsize_T cts;
+ init_chartabsize_arg(&cts, curwin, curwin->w_cursor.lnum,
+ non_white_col, bd.textstart, non_white);
+ while (ascii_iswhite(*cts.cts_ptr)) {
+ incr = lbr_chartabsize_adv(&cts);
+ cts.cts_vcol += incr;
}
+ non_white_col = cts.cts_vcol;
+ non_white = cts.cts_ptr;
+ clear_chartabsize_arg(&cts);
const colnr_T block_space_width = non_white_col - oap->start_vcol;
// We will shift by "total" or "block_space_width", whichever is less.
@@ -461,17 +474,19 @@ static void shift_block(oparg_T *oap, int amount)
if (bd.startspaces) {
verbatim_copy_width -= bd.start_char_vcols;
}
- while (verbatim_copy_width < destination_col) {
- char_u *line = verbatim_copy_end;
-
- // TODO: is passing verbatim_copy_end for start of the line OK?
- incr = lbr_chartabsize(line, verbatim_copy_end, verbatim_copy_width);
- if (verbatim_copy_width + incr > destination_col) {
+ init_chartabsize_arg(&cts, curwin, 0, verbatim_copy_width,
+ bd.textstart, verbatim_copy_end);
+ while (cts.cts_vcol < destination_col) {
+ incr = lbr_chartabsize(&cts);
+ if (cts.cts_vcol + incr > destination_col) {
break;
}
- verbatim_copy_width += incr;
- MB_PTR_ADV(verbatim_copy_end);
+ cts.cts_vcol += incr;
+ MB_PTR_ADV(cts.cts_ptr);
}
+ verbatim_copy_width = cts.cts_vcol;
+ verbatim_copy_end = cts.cts_ptr;
+ clear_chartabsize_arg(&cts);
// If "destination_col" is different from the width of the initial
// part of the line that will be copied, it means we encountered a tab
@@ -485,9 +500,9 @@ static void shift_block(oparg_T *oap, int amount)
// - the beginning of the original line up to "verbatim_copy_end",
// - "fill" number of spaces,
// - the rest of the line, pointed to by non_white.
- new_line_len = verbatim_diff + fill + STRLEN(non_white) + 1;
+ new_line_len = verbatim_diff + fill + strlen(non_white) + 1;
- newp = (char_u *)xmalloc(new_line_len);
+ newp = xmalloc(new_line_len);
startcol = (int)verbatim_diff;
oldlen = bd.textcol + (int)(non_white - bd.textstart) - (int)verbatim_diff;
newlen = (int)fill;
@@ -496,7 +511,7 @@ 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, (char *)newp, false);
+ ml_replace(curwin->w_cursor.lnum, newp, false);
changed_bytes(curwin->w_cursor.lnum, bd.textcol);
extmark_splice_cols(curbuf, (int)curwin->w_cursor.lnum - 1, startcol,
oldlen, newlen,
@@ -508,14 +523,14 @@ static void shift_block(oparg_T *oap, int amount)
/// 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)
+static void block_insert(oparg_T *oap, char *s, int b_insert, struct block_def *bdp)
{
int ts_val;
int count = 0; // extra spaces to replace a cut TAB
int spaces = 0; // non-zero if cutting a TAB
colnr_T offset; // pointer along new line
- size_t s_len = STRLEN(s);
- char_u *newp, *oldp; // new, old lines
+ size_t s_len = strlen(s);
+ char *newp, *oldp; // new, old lines
linenr_T lnum; // loop var
int oldstate = State;
State = MODE_INSERT; // don't want MODE_REPLACE for State
@@ -563,7 +578,7 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def
assert(count >= 0);
// Make sure the allocated size matches what is actually copied below.
- newp = xmalloc(STRLEN(oldp) + (size_t)spaces + s_len
+ newp = xmalloc(strlen(oldp) + (size_t)spaces + s_len
+ (spaces > 0 && !bdp->is_short ? (size_t)ts_val - (size_t)spaces : 0)
+ (size_t)count + 1);
@@ -600,7 +615,7 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def
}
STRMOVE(newp + offset, oldp);
- ml_replace(lnum, (char *)newp, false);
+ ml_replace(lnum, newp, false);
extmark_splice_cols(curbuf, (int)lnum - 1, startcol,
skipped, offset - startcol, kExtmarkUndo);
@@ -621,7 +636,7 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def
void op_reindent(oparg_T *oap, Indenter how)
{
long i = 0;
- char_u *l;
+ char *l;
int amount;
linenr_T first_changed = 0;
linenr_T last_changed = 0;
@@ -651,7 +666,7 @@ void op_reindent(oparg_T *oap, Indenter how)
// 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());
+ l = skipwhite(get_cursor_line_ptr());
if (*l == NUL) { // empty or blank line
amount = 0;
} else {
@@ -674,15 +689,15 @@ void op_reindent(oparg_T *oap, Indenter how)
curwin->w_cursor.lnum = start_lnum;
beginline(BL_SOL | BL_FIX);
- /* Mark changed lines so that they will be redrawn. When Visual
- * highlighting was present, need to continue until the last line. When
- * there is no change still need to remove the Visual highlighting. */
+ // Mark changed lines so that they will be redrawn. When Visual
+ // highlighting was present, need to continue until the last line. When
+ // 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 + (linenr_T)oap->line_count :
last_changed + 1, 0L, true);
} else if (oap->is_VIsual) {
- redraw_curbuf_later(INVERTED);
+ redraw_curbuf_later(UPD_INVERTED);
}
if (oap->line_count > p_report) {
@@ -698,17 +713,15 @@ void op_reindent(oparg_T *oap, Indenter how)
}
}
-/*
- * Keep the last expression line here, for repeating.
- */
-static char_u *expr_line = NULL;
+// Keep the last expression line here, for repeating.
+static char *expr_line = NULL;
/// Get an expression for the "\"=expr1" or "CTRL-R =expr1"
///
/// @return '=' when OK, NUL otherwise.
int get_expr_register(void)
{
- char_u *new_line;
+ char *new_line;
new_line = getcmdline('=', 0L, 0, true);
if (new_line == NULL) {
@@ -724,7 +737,7 @@ int get_expr_register(void)
/// Set the expression for the '=' register.
/// Argument must be an allocated string.
-void set_expr_line(char_u *new_line)
+void set_expr_line(char *new_line)
{
xfree(expr_line);
expr_line = new_line;
@@ -733,40 +746,40 @@ void set_expr_line(char_u *new_line)
/// Get the result of the '=' register expression.
///
/// @return a pointer to allocated memory, or NULL for failure.
-char_u *get_expr_line(void)
+char *get_expr_line(void)
{
- char_u *expr_copy;
- char_u *rv;
+ char *expr_copy;
+ char *rv;
static int nested = 0;
if (expr_line == NULL) {
return NULL;
}
- /* Make a copy of the expression, because evaluating it may cause it to be
- * changed. */
- expr_copy = vim_strsave(expr_line);
+ // Make a copy of the expression, because evaluating it may cause it to be
+ // changed.
+ expr_copy = xstrdup(expr_line);
- /* When we are invoked recursively limit the evaluation to 10 levels.
- * Then return the string as-is. */
+ // When we are invoked recursively limit the evaluation to 10 levels.
+ // Then return the string as-is.
if (nested >= 10) {
return expr_copy;
}
nested++;
- rv = (char_u *)eval_to_string((char *)expr_copy, NULL, true);
+ rv = eval_to_string(expr_copy, NULL, true);
nested--;
xfree(expr_copy);
return rv;
}
/// Get the '=' register expression itself, without evaluating it.
-char_u *get_expr_line_src(void)
+char *get_expr_line_src(void)
{
if (expr_line == NULL) {
return NULL;
}
- return vim_strsave(expr_line);
+ return xstrdup(expr_line);
}
/// @return whether `regname` is a valid name of a yank register.
@@ -889,9 +902,8 @@ bool yank_register_mline(int regname)
/// @return FAIL for failure, OK otherwise.
int do_record(int c)
{
- char_u *p;
+ char *p;
static int regname;
- static bool changed_cmdheight = false;
yankreg_T *old_y_previous;
int retval;
@@ -902,18 +914,11 @@ int do_record(int c)
retval = FAIL;
} else {
reg_recording = c;
+ // TODO(bfredl): showmode based messaging is currently missing with cmdheight=0
showmode();
regname = c;
retval = OK;
- if (!ui_has_messages()) {
- // Enable macro indicator temporarily
- set_option_value("ch", 1L, NULL, 0);
- update_screen(VALID);
-
- changed_cmdheight = true;
- }
-
apply_autocmds(EVENT_RECORDINGENTER, NULL, NULL, false, curbuf);
}
} else { // stop recording
@@ -922,10 +927,10 @@ int do_record(int c)
dict_T *dict = get_v_event(&save_v_event);
// The recorded text contents.
- p = get_recorded();
+ p = (char *)get_recorded();
if (p != NULL) {
// Remove escaping for K_SPECIAL in multi-byte chars.
- vim_unescape_ks(p);
+ vim_unescape_ks((char_u *)p);
(void)tv_dict_add_str(dict, S_LEN("regcontents"), (const char *)p);
}
@@ -943,7 +948,7 @@ int do_record(int c)
restore_v_event(dict, &save_v_event);
reg_recorded = reg_recording;
reg_recording = 0;
- if (ui_has(kUIMessages)) {
+ if (p_ch == 0 || ui_has(kUIMessages)) {
showmode();
} else {
msg("");
@@ -959,12 +964,6 @@ int do_record(int c)
y_previous = old_y_previous;
}
-
- if (changed_cmdheight) {
- // Restore cmdheight
- set_option_value("ch", 0L, NULL, 0);
- redraw_all_later(CLEAR);
- }
}
return retval;
}
@@ -983,7 +982,7 @@ static void set_yreg_additional_data(yankreg_T *reg, dict_T *additional_data)
/// uppercase). "p" must have been allocated.
///
/// @return FAIL for failure, OK otherwise
-static int stuff_yank(int regname, char_u *p)
+static int stuff_yank(int regname, char *p)
{
// check for read-only register
if (regname != 0 && !valid_yank_reg(regname, true)) {
@@ -997,18 +996,18 @@ 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 **pp = &(reg->y_array[reg->y_size - 1]);
- char_u *lp = xmalloc(STRLEN(*pp) + STRLEN(p) + 1);
+ char *lp = xmalloc(strlen(*pp) + strlen(p) + 1);
STRCPY(lp, *pp);
// TODO(philix): use xstpcpy() in stuff_yank()
STRCAT(lp, p);
xfree(p);
xfree(*pp);
- *pp = (char *)lp;
+ *pp = lp;
} else {
free_register(reg);
set_yreg_additional_data(reg, NULL);
- reg->y_array = xmalloc(sizeof(char_u *));
- reg->y_array[0] = (char *)p;
+ reg->y_array = xmalloc(sizeof(char *));
+ reg->y_array[0] = p;
reg->y_size = 1;
reg->y_type = kMTCharWise;
}
@@ -1029,22 +1028,22 @@ static int execreg_lastc = NUL;
/// 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 **lines, size_t *idx)
+static char *execreg_line_continuation(char **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);
+ ga_init(&ga, (int)sizeof(char), 400);
- char_u *p;
+ char *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(lines[i]);
+ p = skipwhite(lines[i]);
if (*p != '\\' && (p[0] != '"' || p[1] != '\\' || p[2] != ' ')) {
break;
}
@@ -1054,18 +1053,18 @@ static char_u *execreg_line_continuation(char **lines, size_t *idx)
// join all the lines
ga_concat(&ga, lines[cmd_start]);
for (size_t j = cmd_start + 1; j <= cmd_end; j++) {
- p = (char_u *)skipwhite(lines[j]);
+ p = skipwhite(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_concat(&ga, p + 1);
}
}
ga_append(&ga, NUL);
- char_u *str = vim_strsave(ga.ga_data);
+ char *str = xstrdup(ga.ga_data);
ga_clear(&ga);
*idx = i;
@@ -1081,7 +1080,7 @@ static char_u *execreg_line_continuation(char **lines, size_t *idx)
/// @return FAIL for failure, OK otherwise
int do_execreg(int regname, int colon, int addcr, int silent)
{
- char_u *p;
+ char *p;
int retval = OK;
if (regname == '@') { // repeat previous one
@@ -1111,14 +1110,14 @@ int do_execreg(int regname, int colon, int addcr, int silent)
XFREE_CLEAR(new_last_cmdline);
// Escape all control characters with a CTRL-V
p = vim_strsave_escaped_ext(last_cmdline,
- (char_u *)"\001\002\003\004\005\006\007"
+ "\001\002\003\004\005\006\007"
"\010\011\012\013\014\015\016\017"
"\020\021\022\023\024\025\026\027"
"\030\031\032\033\034\035\036\037",
Ctrl_V, false);
// When in Visual mode "'<,'>" will be prepended to the command.
// Remove it when it's already there.
- if (VIsual_active && STRNCMP(p, "'<,'>", 5) == 0) {
+ if (VIsual_active && strncmp(p, "'<,'>", 5) == 0) {
retval = put_in_typebuf(p + 5, true, true, silent);
} else {
retval = put_in_typebuf(p, true, true, silent);
@@ -1148,9 +1147,7 @@ int do_execreg(int regname, int colon, int addcr, int silent)
// Disallow remapping for ":@r".
int remap = colon ? REMAP_NONE : REMAP_YES;
- /*
- * Insert lines into typeahead buffer, from last one to first one.
- */
+ // Insert lines into typeahead buffer, from last one to first one.
put_reedit_in_typebuf(silent);
char *escaped;
for (size_t i = reg->y_size; i-- > 0;) { // from y_size - 1 to 0 included
@@ -1162,16 +1159,16 @@ int do_execreg(int regname, int colon, int addcr, int silent)
}
// Handle line-continuation for :@<register>
- char_u *str = (char_u *)reg->y_array[i];
+ char *str = reg->y_array[i];
bool free_str = false;
if (colon && i > 0) {
- p = (char_u *)skipwhite((char *)str);
+ p = skipwhite(str);
if (*p == '\\' || (p[0] == '"' && p[1] == '\\' && p[2] == ' ')) {
str = execreg_line_continuation(reg->y_array, &i);
free_str = true;
}
}
- escaped = vim_strsave_escape_ks((char *)str);
+ escaped = vim_strsave_escape_ks(str);
if (free_str) {
xfree(str);
}
@@ -1196,18 +1193,20 @@ static void put_reedit_in_typebuf(int silent)
{
char_u buf[3];
- if (restart_edit != NUL) {
- if (restart_edit == 'V') {
- buf[0] = 'g';
- buf[1] = 'R';
- buf[2] = NUL;
- } else {
- buf[0] = (char_u)(restart_edit == 'I' ? 'i' : restart_edit);
- buf[1] = NUL;
- }
- if (ins_typebuf((char *)buf, REMAP_NONE, 0, true, silent) == OK) {
- restart_edit = NUL;
- }
+ if (restart_edit == NUL) {
+ return;
+ }
+
+ if (restart_edit == 'V') {
+ buf[0] = 'g';
+ buf[1] = 'R';
+ buf[2] = NUL;
+ } else {
+ buf[0] = (char_u)(restart_edit == 'I' ? 'i' : restart_edit);
+ buf[1] = NUL;
+ }
+ if (ins_typebuf((char *)buf, REMAP_NONE, 0, true, silent) == OK) {
+ restart_edit = NUL;
}
}
@@ -1217,7 +1216,7 @@ static void put_reedit_in_typebuf(int silent)
/// @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)
+static int put_in_typebuf(char *s, bool esc, bool colon, int silent)
{
int retval = OK;
@@ -1229,9 +1228,9 @@ static int put_in_typebuf(char_u *s, bool esc, bool colon, int silent)
char *p;
if (esc) {
- p = vim_strsave_escape_ks((char *)s);
+ p = vim_strsave_escape_ks(s);
} else {
- p = (char *)s;
+ p = s;
}
if (p == NULL) {
retval = FAIL;
@@ -1260,11 +1259,9 @@ int insert_reg(int regname, bool literally_arg)
bool allocated;
const bool literally = literally_arg || is_literal_register(regname);
- /*
- * It is possible to get into an endless loop by having CTRL-R a in
- * register a and then, in insert mode, doing CTRL-R a.
- * If you hit CTRL-C, the loop will be broken here.
- */
+ // It is possible to get into an endless loop by having CTRL-R a in
+ // register a and then, in insert mode, doing CTRL-R a.
+ // If you hit CTRL-C, the loop will be broken here.
os_breakcheck();
if (got_int) {
return FAIL;
@@ -1311,34 +1308,6 @@ int insert_reg(int regname, bool literally_arg)
return retval;
}
-/// Stuff a string into the typeahead buffer, such that edit() will insert it
-/// literally ("literally" true) or interpret is as typed characters.
-static void stuffescaped(const char *arg, bool literally)
-{
- while (*arg != NUL) {
- // Stuff a sequence of normal ASCII characters, that's fast. Also
- // stuff K_SPECIAL to get the effect of a special key when "literally"
- // is true.
- const char *const start = arg;
- while ((*arg >= ' ' && *arg < DEL) || ((uint8_t)(*arg) == K_SPECIAL
- && !literally)) {
- arg++;
- }
- if (arg > start) {
- stuffReadbuffLen(start, (arg - start));
- }
-
- // stuff a single special character
- if (*arg != NUL) {
- const int c = mb_cptr2char_adv((const char_u **)&arg);
- if (literally && ((c < ' ' && c != TAB) || c == DEL)) {
- stuffcharReadbuff(Ctrl_V);
- }
- stuffcharReadbuff(c);
- }
- }
-}
-
/// If "regname" is a special register, return true and store a pointer to its
/// value in "argp".
///
@@ -1365,7 +1334,7 @@ bool get_spec_reg(int regname, char **argp, bool *allocated, bool errmsg)
return true;
case '=': // result of expression
- *argp = (char *)get_expr_line();
+ *argp = get_expr_line();
*allocated = true;
return true;
@@ -1373,18 +1342,18 @@ bool get_spec_reg(int regname, char **argp, bool *allocated, bool errmsg)
if (last_cmdline == NULL && errmsg) {
emsg(_(e_nolastcmd));
}
- *argp = (char *)last_cmdline;
+ *argp = last_cmdline;
return true;
case '/': // last search-pattern
if (last_search_pat() == NULL && errmsg) {
emsg(_(e_noprevre));
}
- *argp = (char *)last_search_pat();
+ *argp = last_search_pat();
return true;
case '.': // last inserted text
- *argp = (char *)get_last_insert_save();
+ *argp = get_last_insert_save();
*allocated = true;
if (*argp == NULL && errmsg) {
emsg(_(e_noinstext));
@@ -1396,9 +1365,8 @@ bool get_spec_reg(int regname, char **argp, bool *allocated, bool errmsg)
if (!errmsg) {
return false;
}
- *argp
- = (char *)file_name_at_cursor(FNAME_MESS | FNAME_HYP | (regname == Ctrl_P ? FNAME_EXP : 0),
- 1L, NULL);
+ *argp = file_name_at_cursor(FNAME_MESS | FNAME_HYP | (regname == Ctrl_P ? FNAME_EXP : 0),
+ 1L, NULL);
*allocated = true;
return true;
@@ -1419,7 +1387,7 @@ bool get_spec_reg(int regname, char **argp, bool *allocated, bool errmsg)
return false;
}
- *argp = (char *)ml_get_buf(curwin->w_buffer, curwin->w_cursor.lnum, false);
+ *argp = ml_get_buf(curwin->w_buffer, curwin->w_cursor.lnum, false);
return true;
case '_': // black hole: always empty
@@ -1451,15 +1419,15 @@ bool cmdline_paste_reg(int regname, bool literally_arg, bool remcr)
}
for (size_t i = 0; i < reg->y_size; i++) {
- cmdline_paste_str((char_u *)reg->y_array[i], literally);
+ cmdline_paste_str(reg->y_array[i], literally);
// Insert ^M between lines, unless `remcr` is true.
if (i < reg->y_size - 1 && !remcr) {
- cmdline_paste_str((char_u *)"\r", literally);
+ cmdline_paste_str("\r", literally);
}
- /* Check for CTRL-C, in case someone tries to paste a few thousand
- * lines and gets bored. */
+ // Check for CTRL-C, in case someone tries to paste a few thousand
+ // lines and gets bored.
os_breakcheck();
if (got_int) {
return FAIL;
@@ -1488,8 +1456,8 @@ int op_delete(oparg_T *oap)
{
int n;
linenr_T lnum;
- char_u *ptr;
- char_u *newp, *oldp;
+ char *ptr;
+ char *newp, *oldp;
struct block_def bd = { 0 };
linenr_T old_lcount = curbuf->b_ml.ml_line_count;
@@ -1514,11 +1482,9 @@ int op_delete(oparg_T *oap)
mb_adjust_opend(oap);
- /*
- * Imitate the strange Vi behaviour: If the delete spans more than one
- * line and motion_type == kMTCharWise and the result is a blank line, make the
- * delete linewise. Don't do this for the change command or Visual mode.
- */
+ // Imitate the strange Vi behaviour: If the delete spans more than one
+ // line and motion_type == kMTCharWise and the result is a blank line, make the
+ // delete linewise. Don't do this for the change command or Visual mode.
if (oap->motion_type == kMTCharWise
&& !oap->is_VIsual
&& oap->line_count > 1
@@ -1528,16 +1494,14 @@ int op_delete(oparg_T *oap)
if (*ptr != NUL) {
ptr += oap->inclusive;
}
- ptr = (char_u *)skipwhite((char *)ptr);
+ ptr = skipwhite(ptr);
if (*ptr == NUL && inindent(0)) {
oap->motion_type = kMTLineWise;
}
}
- /*
- * Check for trying to delete (e.g. "D") in an empty line.
- * Note: For the change operator it is ok.
- */
+ // Check for trying to delete (e.g. "D") in an empty line.
+ // Note: For the change operator it is ok.
if (oap->motion_type != kMTLineWise
&& oap->line_count == 1
&& oap->op_type == OP_DELETE
@@ -1555,11 +1519,9 @@ int op_delete(oparg_T *oap)
return OK;
}
- /*
- * Do a yank of whatever we're about to delete.
- * If a yank register was specified, put the deleted text into that
- * register. For the black hole register '_' don't yank anything.
- */
+ // Do a yank of whatever we're about to delete.
+ // If a yank register was specified, put the deleted text into that
+ // register. For the black hole register '_' don't yank anything.
if (oap->regname != '_') {
yankreg_T *reg = NULL;
int did_yank = false;
@@ -1577,6 +1539,7 @@ int op_delete(oparg_T *oap)
// 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 = &y_regs[1];
@@ -1602,9 +1565,7 @@ int op_delete(oparg_T *oap)
}
}
- /*
- * block mode delete
- */
+ // block mode delete
if (oap->motion_type == kMTBlockWise) {
if (u_save((linenr_T)(oap->start.lnum - 1),
(linenr_T)(oap->end.lnum + 1)) == FAIL) {
@@ -1628,7 +1589,7 @@ int op_delete(oparg_T *oap)
// Thus the number of characters may increase!
n = bd.textlen - bd.startspaces - bd.endspaces;
oldp = ml_get(lnum);
- newp = (char_u *)xmalloc(STRLEN(oldp) - (size_t)n + 1);
+ newp = xmalloc(strlen(oldp) - (size_t)n + 1);
// copy up to deleted part
memmove(newp, oldp, (size_t)bd.textcol);
// insert spaces
@@ -1638,7 +1599,7 @@ 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, (char *)newp, false);
+ ml_replace(lnum, newp, false);
extmark_splice_cols(curbuf, (int)lnum - 1, bd.textcol,
bd.textlen, bd.startspaces + bd.endspaces,
@@ -1672,7 +1633,7 @@ int op_delete(oparg_T *oap)
beginline(0); // cursor in column 0
}
- int old_len = (int)STRLEN(ml_get(curwin->w_cursor.lnum));
+ int old_len = (int)strlen(ml_get(curwin->w_cursor.lnum));
truncate_line(false); // delete the rest of the line
extmark_splice_cols(curbuf,
@@ -1745,8 +1706,8 @@ int op_delete(oparg_T *oap)
if (virtual_op) {
// fix up things for virtualedit-delete:
// break the tabs which are going to get in our way
- char_u *curline = get_cursor_line_ptr();
- int len = (int)STRLEN(curline);
+ char *curline = get_cursor_line_ptr();
+ int len = (int)strlen(curline);
if (oap->end.coladd != 0
&& (int)oap->end.col >= len - 1
@@ -1825,17 +1786,19 @@ setmarks:
/// Used for deletion.
static void mb_adjust_opend(oparg_T *oap)
{
- if (oap->inclusive) {
- char *p = (char *)ml_get(oap->end.lnum);
- oap->end.col += utf_cp_tail_off(p, p + oap->end.col);
+ if (!oap->inclusive) {
+ return;
}
+
+ char *p = ml_get(oap->end.lnum);
+ oap->end.col += utf_cp_tail_off(p, p + oap->end.col);
}
/// 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;
+ *(ml_get_buf(curbuf, lp.lnum, true) + lp.col) = (char)c;
if (!curbuf_splice_pending) {
extmark_splice_cols(curbuf, (int)lp.lnum - 1, lp.col, 1, 1, kExtmarkUndo);
}
@@ -1859,10 +1822,10 @@ static int op_replace(oparg_T *oap, int c)
{
int n, numc;
int num_chars;
- char_u *newp, *oldp;
+ char *newp, *oldp;
colnr_T oldlen;
struct block_def bd;
- char_u *after_p = NULL;
+ char *after_p = NULL;
int had_ctrl_v_cr = false;
if ((curbuf->b_ml.ml_flags & ML_EMPTY) || oap->empty) {
@@ -1883,9 +1846,7 @@ static int op_replace(oparg_T *oap, int c)
return FAIL;
}
- /*
- * block mode replace
- */
+ // block mode replace
if (oap->motion_type == kMTBlockWise) {
bd.is_MAX = (curwin->w_curswant == MAXCOL);
for (; curwin->w_cursor.lnum <= oap->end.lnum; curwin->w_cursor.lnum++) {
@@ -1937,7 +1898,7 @@ static int op_replace(oparg_T *oap, int c)
numc *= utf_char2len(c);
oldp = get_cursor_line_ptr();
- oldlen = (int)STRLEN(oldp);
+ oldlen = (int)strlen(oldp);
size_t newp_size = (size_t)bd.textcol + (size_t)bd.startspaces;
if (had_ctrl_v_cr || (c != '\r' && c != '\n')) {
@@ -1963,7 +1924,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, (char *)newp + newp_len);
+ newp_len += utf_char2bytes(c, newp + newp_len);
}
if (!bd.is_short) {
// insert post-spaces
@@ -1976,16 +1937,16 @@ static int op_replace(oparg_T *oap, int c)
} else {
// Replacing with \r or \n means splitting the line.
after_p_len = (size_t)col;
- after_p = (char_u *)xmalloc(after_p_len);
+ after_p = xmalloc(after_p_len);
memmove(after_p, oldp, after_p_len);
newrows = 1;
}
// replace the line
- ml_replace(curwin->w_cursor.lnum, (char *)newp, false);
+ ml_replace(curwin->w_cursor.lnum, newp, false);
curbuf_splice_pending++;
linenr_T baselnum = curwin->w_cursor.lnum;
if (after_p != NULL) {
- ml_append(curwin->w_cursor.lnum++, (char *)after_p, (int)after_p_len, false);
+ ml_append(curwin->w_cursor.lnum++, after_p, (int)after_p_len, false);
appended_lines_mark(curwin->w_cursor.lnum, 1L);
oap->end.lnum++;
xfree(after_p);
@@ -2000,9 +1961,9 @@ static int op_replace(oparg_T *oap, int c)
if (oap->motion_type == kMTLineWise) {
oap->start.col = 0;
curwin->w_cursor.col = 0;
- oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum));
+ oap->end.col = (colnr_T)strlen(ml_get(oap->end.lnum));
if (oap->end.col) {
- --oap->end.col;
+ oap->end.col--;
}
} else if (!oap->inclusive) {
dec(&(oap->end));
@@ -2011,10 +1972,12 @@ static int op_replace(oparg_T *oap, int c)
// TODO(bfredl): we could batch all the splicing
// done on the same line, at least
while (ltoreq(curwin->w_cursor, oap->end)) {
+ bool done = false;
+
n = gchar_cursor();
if (n != NUL) {
int new_byte_len = utf_char2len(c);
- int old_byte_len = utfc_ptr2len((char *)get_cursor_pos_ptr());
+ int old_byte_len = utfc_ptr2len(get_cursor_pos_ptr());
if (new_byte_len > 1 || old_byte_len > 1) {
// This is slow, but it handles replacing a single-byte
@@ -2023,6 +1986,7 @@ static int op_replace(oparg_T *oap, int c)
oap->end.col += new_byte_len - old_byte_len;
}
replace_character(c);
+ done = true;
} else {
if (n == TAB) {
int end_vcol = 0;
@@ -2038,9 +2002,14 @@ static int op_replace(oparg_T *oap, int c)
getvpos(&oap->end, end_vcol);
}
}
- pbyte(curwin->w_cursor, c);
+ // with "coladd" set may move to just after a TAB
+ if (gchar_cursor() != NUL) {
+ pbyte(curwin->w_cursor, c);
+ done = true;
+ }
}
- } else if (virtual_op && curwin->w_cursor.lnum == oap->end.lnum) {
+ }
+ if (!done && virtual_op && curwin->w_cursor.lnum == oap->end.lnum) {
int virtcols = oap->end.coladd;
if (curwin->w_cursor.lnum == oap->start.lnum
@@ -2090,7 +2059,7 @@ void op_tilde(oparg_T *oap)
{
pos_T pos;
struct block_def bd;
- int did_change = FALSE;
+ int did_change = false;
if (u_save((linenr_T)(oap->start.lnum - 1),
(linenr_T)(oap->end.lnum + 1)) == FAIL) {
@@ -2114,9 +2083,9 @@ void op_tilde(oparg_T *oap)
if (oap->motion_type == kMTLineWise) {
oap->start.col = 0;
pos.col = 0;
- oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum));
+ oap->end.col = (colnr_T)strlen(ml_get(oap->end.lnum));
if (oap->end.col) {
- --oap->end.col;
+ oap->end.col--;
}
} else if (!oap->inclusive) {
dec(&(oap->end));
@@ -2129,7 +2098,7 @@ void op_tilde(oparg_T *oap)
for (;;) {
did_change |= swapchars(oap->op_type, &pos,
pos.lnum == oap->end.lnum ? oap->end.col + 1 :
- (int)STRLEN(ml_get_pos(&pos)));
+ (int)strlen(ml_get_pos(&pos)));
if (ltoreq(oap->end, pos) || inc(&pos) == -1) {
break;
}
@@ -2143,7 +2112,7 @@ void op_tilde(oparg_T *oap)
if (!did_change && oap->is_VIsual) {
// No change: need to remove the Visual selection
- redraw_curbuf_later(INVERTED);
+ redraw_curbuf_later(UPD_INVERTED);
}
if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) {
@@ -2165,14 +2134,14 @@ void op_tilde(oparg_T *oap)
/// @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.
+/// @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((char *)ml_get_pos(pos));
+ const int len = utfc_ptr2len(ml_get_pos(pos));
// we're counting bytes, not characters
if (len > 0) {
@@ -2235,7 +2204,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((char *)get_cursor_pos_ptr()), false, false);
+ del_bytes(utf_ptr2len(get_cursor_pos_ptr()), false, false);
ins_char(nc);
curwin->w_cursor = sp;
} else {
@@ -2250,7 +2219,7 @@ bool swapchar(int op_type, pos_T *pos)
void op_insert(oparg_T *oap, long count1)
{
long ins_len, pre_textlen = 0;
- char_u *firstline, *ins_text;
+ char *firstline, *ins_text;
colnr_T ind_pre_col = 0, ind_post_col;
int ind_pre_vcol = 0, ind_post_vcol = 0;
struct block_def bd;
@@ -2262,7 +2231,8 @@ void op_insert(oparg_T *oap, long count1)
// vis block is still marked. Get rid of it now.
curwin->w_cursor.lnum = oap->start.lnum;
- update_screen(INVERTED);
+ redraw_curbuf_later(UPD_INVERTED);
+ update_screen();
if (oap->motion_type == kMTBlockWise) {
// When 'virtualedit' is used, need to insert the extra spaces before
@@ -2280,9 +2250,9 @@ void op_insert(oparg_T *oap, long count1)
}
curwin->w_ve_flags = VE_ALL;
coladvance_force(oap->op_type == OP_APPEND
- ? oap->end_vcol + 1 : getviscol());
+ ? oap->end_vcol + 1 : getviscol());
if (oap->op_type == OP_APPEND) {
- --curwin->w_cursor.col;
+ curwin->w_cursor.col--;
}
curwin->w_ve_flags = old_ve_flags;
}
@@ -2296,17 +2266,17 @@ void op_insert(oparg_T *oap, long count1)
if (oap->op_type == OP_APPEND) {
firstline += bd.textlen;
}
- pre_textlen = (long)STRLEN(firstline);
+ pre_textlen = (long)strlen(firstline);
}
if (oap->op_type == OP_APPEND) {
if (oap->motion_type == kMTBlockWise
&& curwin->w_cursor.coladd == 0) {
// Move the cursor to the character right of the block.
- curwin->w_set_curswant = TRUE;
+ curwin->w_set_curswant = true;
while (*get_cursor_pos_ptr() != NUL
&& (curwin->w_cursor.col < bd.textcol + bd.textlen)) {
- ++curwin->w_cursor.col;
+ curwin->w_cursor.col++;
}
if (bd.is_short && !bd.is_MAX) {
// First line was too short, make it longer and adjust the
@@ -2412,19 +2382,17 @@ void op_insert(oparg_T *oap, long count1)
if (oap->op_type == OP_APPEND) {
pre_textlen += bd2.textlen - bd.textlen;
if (bd2.endspaces) {
- --bd2.textlen;
+ bd2.textlen--;
}
}
bd.textcol = bd2.textcol;
bd.textlen = bd2.textlen;
}
- /*
- * Subsequent calls to ml_get() flush the firstline data - take a
- * copy of the required string.
- */
+ // Subsequent calls to ml_get() flush the firstline data - take a
+ // copy of the required string.
firstline = ml_get(oap->start.lnum);
- const size_t len = STRLEN(firstline);
+ 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) {
@@ -2446,9 +2414,9 @@ void op_insert(oparg_T *oap, long count1)
} else {
firstline += add;
}
- ins_len = (long)STRLEN(firstline) - pre_textlen - offset;
+ ins_len = (long)strlen(firstline) - pre_textlen - offset;
if (pre_textlen >= 0 && ins_len > 0) {
- ins_text = vim_strnsave(firstline, (size_t)ins_len);
+ ins_text = xstrnsave(firstline, (size_t)ins_len);
// block handled here
if (u_save(oap->start.lnum, (linenr_T)(oap->end.lnum + 1)) == OK) {
block_insert(oap, ins_text, (oap->op_type == OP_INSERT), &bd);
@@ -2463,7 +2431,7 @@ void op_insert(oparg_T *oap, long count1)
/// handle a change operation
///
-/// @return TRUE if edit() returns because of a CTRL-O command
+/// @return true if edit() returns because of a CTRL-O command
int op_change(oparg_T *oap)
{
colnr_T l;
@@ -2473,10 +2441,10 @@ int op_change(oparg_T *oap)
long ins_len;
long pre_textlen = 0;
long pre_indent = 0;
- char_u *newp;
- char_u *firstline;
- char_u *ins_text;
- char_u *oldp;
+ char *newp;
+ char *firstline;
+ char *ins_text;
+ char *oldp;
struct block_def bd;
l = oap->start.col;
@@ -2489,10 +2457,10 @@ int op_change(oparg_T *oap)
// save for undo
if (curbuf->b_ml.ml_flags & ML_EMPTY) {
if (u_save_cursor() == FAIL) {
- return FALSE;
+ return false;
}
} else if (op_delete(oap) == FAIL) {
- return FALSE;
+ return false;
}
if ((l > curwin->w_cursor.col) && !LINEEMPTY(curwin->w_cursor.lnum)
@@ -2509,7 +2477,7 @@ int op_change(oparg_T *oap)
coladvance_force(getviscol());
}
firstline = ml_get(oap->start.lnum);
- pre_textlen = (long)STRLEN(firstline);
+ pre_textlen = (long)strlen(firstline);
pre_indent = (long)getwhitecols(firstline);
bd.textcol = curwin->w_cursor.col;
}
@@ -2518,13 +2486,17 @@ int op_change(oparg_T *oap)
fix_indent();
}
- retval = edit(NUL, FALSE, (linenr_T)1);
+ // Reset finish_op now, don't want it set inside edit().
+ const bool save_finish_op = finish_op;
+ finish_op = false;
+
+ retval = edit(NUL, false, (linenr_T)1);
- /*
- * In Visual block mode, handle copying the new text to all lines of the
- * block.
- * Don't repeat the insert when Insert mode ended with CTRL-C.
- */
+ finish_op = save_finish_op;
+
+ // In Visual block mode, handle copying the new text to all lines of the
+ // block.
+ // Don't repeat the insert when Insert mode ended with CTRL-C.
if (oap->motion_type == kMTBlockWise
&& oap->start.lnum != oap->end.lnum && !got_int) {
// Auto-indenting may have changed the indent. If the cursor was past
@@ -2537,12 +2509,12 @@ int op_change(oparg_T *oap)
bd.textcol += (colnr_T)(new_indent - pre_indent);
}
- ins_len = (long)STRLEN(firstline) - pre_textlen;
+ ins_len = (long)strlen(firstline) - pre_textlen;
if (ins_len > 0) {
// Subsequent calls to ml_get() flush the firstline data - take a
// copy of the inserted text.
- ins_text = (char_u *)xmalloc((size_t)(ins_len + 1));
- STRLCPY(ins_text, firstline + bd.textcol, ins_len + 1);
+ ins_text = xmalloc((size_t)(ins_len + 1));
+ xstrlcpy(ins_text, firstline + bd.textcol, (size_t)ins_len + 1);
for (linenr = oap->start.lnum + 1; linenr <= oap->end.lnum;
linenr++) {
block_prep(oap, &bd, linenr, true);
@@ -2558,7 +2530,7 @@ int op_change(oparg_T *oap)
vpos.coladd = 0;
}
oldp = ml_get(linenr);
- newp = xmalloc(STRLEN(oldp) + (size_t)vpos.coladd
+ newp = xmalloc(strlen(oldp) + (size_t)vpos.coladd
+ (size_t)ins_len + 1);
// copy up to block start
memmove(newp, oldp, (size_t)bd.textcol);
@@ -2569,7 +2541,7 @@ int op_change(oparg_T *oap)
offset += ins_len;
oldp += bd.textcol;
STRMOVE(newp + offset, oldp);
- ml_replace(linenr, (char *)newp, false);
+ ml_replace(linenr, newp, false);
extmark_splice_cols(curbuf, (int)linenr - 1, bd.textcol,
0, vpos.coladd + (int)ins_len, kExtmarkUndo);
}
@@ -2604,12 +2576,14 @@ void free_register(yankreg_T *reg)
FUNC_ATTR_NONNULL_ALL
{
set_yreg_additional_data(reg, NULL);
- if (reg->y_array != NULL) {
- for (size_t i = reg->y_size; i-- > 0;) { // from y_size - 1 to 0 included
- xfree(reg->y_array[i]);
- }
- XFREE_CLEAR(reg->y_array);
+ if (reg->y_array == NULL) {
+ return;
}
+
+ for (size_t i = reg->y_size; i-- > 0;) { // from y_size - 1 to 0 included
+ xfree(reg->y_array[i]);
+ }
+ XFREE_CLEAR(reg->y_array);
}
/// Yanks the text between "oap->start" and "oap->end" into a yank register.
@@ -2649,8 +2623,8 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
MotionType yank_type = oap->motion_type;
size_t yanklines = (size_t)oap->line_count;
linenr_T yankendlnum = oap->end.lnum;
- char_u *p;
- char_u *pnew;
+ char *p;
+ char *pnew;
struct block_def bd;
yankreg_T *curr = reg; // copy of current register
@@ -2701,7 +2675,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
break;
case kMTLineWise:
- reg->y_array[y_idx] = (char *)vim_strsave(ml_get(lnum));
+ reg->y_array[y_idx] = xstrdup(ml_get(lnum));
break;
case kMTCharWise: {
@@ -2717,8 +2691,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
if (virtual_op) {
getvcol(curwin, &oap->start, &cs, NULL, &ce);
if (ce != cs && oap->start.coladd > 0) {
- /* Part of a tab selected -- but don't
- * double-count it. */
+ // Part of a tab selected -- but don't double-count it.
bd.startspaces = (ce - cs + 1)
- oap->start.coladd;
startcol++;
@@ -2751,7 +2724,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
}
}
if (endcol == MAXCOL) {
- endcol = (colnr_T)STRLEN(p);
+ endcol = (colnr_T)strlen(p);
}
if (startcol > endcol
|| is_oneChar) {
@@ -2770,7 +2743,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));
+ new_ptr = xmalloc(sizeof(char *) * (curr->y_size + reg->y_size));
for (j = 0; j < curr->y_size; j++) {
new_ptr[j] = curr->y_array[j];
}
@@ -2786,13 +2759,13 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
// the new block, unless being Vi compatible.
if (curr->y_type == kMTCharWise
&& vim_strchr(p_cpo, CPO_REGAPPEND) == NULL) {
- pnew = xmalloc(STRLEN(curr->y_array[curr->y_size - 1])
- + STRLEN(reg->y_array[0]) + 1);
+ pnew = xmalloc(strlen(curr->y_array[curr->y_size - 1])
+ + strlen(reg->y_array[0]) + 1);
STRCPY(pnew, curr->y_array[--j]);
STRCAT(pnew, reg->y_array[0]);
xfree(curr->y_array[j]);
xfree(reg->y_array[0]);
- curr->y_array[j++] = (char *)pnew;
+ curr->y_array[j++] = pnew;
y_idx = 1;
} else {
y_idx = 0;
@@ -2819,7 +2792,10 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
}
// redisplay now, so message is not deleted
- update_topline_redraw();
+ update_topline(curwin);
+ if (must_redraw) {
+ update_screen();
+ }
if (yank_type == kMTBlockWise) {
smsg(NGETTEXT("block of %" PRId64 " line yanked%s",
"block of %" PRId64 " lines yanked%s", yanklines),
@@ -2855,8 +2831,8 @@ 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] = (char *)pnew;
+ char *pnew = xmallocz((size_t)size);
+ reg->y_array[y_idx] = pnew;
memset(pnew, ' ', (size_t)bd->startspaces);
pnew += bd->startspaces;
memmove(pnew, bd->textstart, (size_t)bd->textlen);
@@ -2944,9 +2920,9 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
/// @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;
- char_u *newp;
- char_u *oldp;
+ char *ptr;
+ char *newp;
+ char *oldp;
int yanklen;
size_t totlen = 0; // init for gcc
linenr_T lnum = 0;
@@ -2983,10 +2959,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
curbuf->b_op_start = curwin->w_cursor; // default for '[ mark
curbuf->b_op_end = curwin->w_cursor; // default for '] mark
- /*
- * Using inserted text works differently, because the register includes
- * special characters (newlines, etc.).
- */
+ // Using inserted text works differently, because the register includes
+ // special characters (newlines, etc.).
if (regname == '.' && !reg) {
bool non_linewise_vis = (VIsual_active && VIsual_mode != 'V');
@@ -3035,14 +3009,14 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
// curwin->w_cursor.col marks the byte position of the cursor in the
// currunt line. It increases up to a max of
- // STRLEN(ml_get(curwin->w_cursor.lnum)). With 'virtualedit' and the
+ // strlen(ml_get(curwin->w_cursor.lnum)). With 'virtualedit' and the
// cursor past the end of the line, curwin->w_cursor.coladd is
// incremented instead of curwin->w_cursor.col.
- char_u *cursor_pos = get_cursor_pos_ptr();
+ char *cursor_pos = get_cursor_pos_ptr();
bool one_past_line = (*cursor_pos == NUL);
bool eol = false;
if (!one_past_line) {
- eol = (*(cursor_pos + utfc_ptr2len((char *)cursor_pos)) == NUL);
+ eol = (*(cursor_pos + utfc_ptr2len(cursor_pos)) == NUL);
}
bool ve_allows = (cur_ve_flags == VE_ALL || cur_ve_flags == VE_ONEMORE);
@@ -3066,10 +3040,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
return;
}
- /*
- * For special registers '%' (file name), '#' (alternate file name) and
- * ':' (last command line), etc. we have to create a fake yank register.
- */
+ // For special registers '%' (file name), '#' (alternate file name) and
+ // ':' (last command line), etc. we have to create a fake yank register.
if (!reg && get_spec_reg(regname, &insert_string, &allocated, true)) {
if (insert_string == NULL) {
return;
@@ -3087,18 +3059,18 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
if (insert_string != NULL) {
y_type = kMTCharWise;
if (regname == '=') {
- /* For the = register we need to split the string at NL
- * characters.
- * Loop twice: count the number of lines and save them. */
+ // For the = register we need to split the string at NL
+ // characters.
+ // Loop twice: count the number of lines and save them.
for (;;) {
y_size = 0;
- ptr = (char_u *)insert_string;
+ ptr = insert_string;
while (ptr != NULL) {
if (y_array != NULL) {
- y_array[y_size] = (char *)ptr;
+ y_array[y_size] = ptr;
}
y_size++;
- ptr = (char_u *)vim_strchr((char *)ptr, '\n');
+ ptr = vim_strchr(ptr, '\n');
if (ptr != NULL) {
if (y_array != NULL) {
*ptr = NUL;
@@ -3114,7 +3086,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
if (y_array != NULL) {
break;
}
- y_array = xmalloc(y_size * sizeof(char_u *));
+ y_array = xmalloc(y_size * sizeof(char *));
}
} else {
y_size = 1; // use fake one-line yank register
@@ -3146,12 +3118,12 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
if (u_save_cursor() == FAIL) {
goto end;
}
- char_u *p = get_cursor_pos_ptr();
+ char *p = get_cursor_pos_ptr();
if (dir == FORWARD && *p != NUL) {
MB_PTR_ADV(p);
}
- ptr = vim_strsave(p);
- ml_append(curwin->w_cursor.lnum, (char *)ptr, (colnr_T)0, false);
+ ptr = xstrdup(p);
+ ml_append(curwin->w_cursor.lnum, ptr, (colnr_T)0, false);
xfree(ptr);
oldp = get_cursor_line_ptr();
@@ -3159,8 +3131,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
if (dir == FORWARD && *p != NUL) {
MB_PTR_ADV(p);
}
- ptr = vim_strnsave(oldp, (size_t)(p - oldp));
- ml_replace(curwin->w_cursor.lnum, (char *)ptr, false);
+ ptr = xstrnsave(oldp, (size_t)(p - oldp));
+ ml_replace(curwin->w_cursor.lnum, ptr, false);
nr_lines++;
dir = FORWARD;
}
@@ -3179,7 +3151,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
if (y_size == 0 || y_array == NULL) {
semsg(_("E353: Nothing in register %s"),
- regname == 0 ? (char_u *)"\"" : transchar(regname));
+ regname == 0 ? "\"" : transchar(regname));
goto end;
}
@@ -3219,7 +3191,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
goto end;
}
- yanklen = (int)STRLEN(y_array[0]);
+ yanklen = (int)strlen(y_array[0]);
if (cur_ve_flags == VE_ALL && y_type == kMTCharWise) {
if (gchar_cursor() == TAB) {
@@ -3242,9 +3214,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
lnum = curwin->w_cursor.lnum;
col = curwin->w_cursor.col;
- /*
- * Block mode
- */
+ // Block mode
if (y_type == kMTBlockWise) {
int c = gchar_cursor();
colnr_T endcol2 = 0;
@@ -3257,7 +3227,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
}
// move to start of next multi-byte character
- curwin->w_cursor.col += utfc_ptr2len((char *)get_cursor_pos_ptr());
+ curwin->w_cursor.col += utfc_ptr2len(get_cursor_pos_ptr());
col++;
} else {
getvcol(curwin, &curwin->w_cursor, &col, NULL, &endcol2);
@@ -3306,42 +3276,52 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
}
// get the old line and advance to the position to insert at
oldp = get_cursor_line_ptr();
- oldlen = STRLEN(oldp);
- for (ptr = oldp; vcol < col && *ptr;) {
+ oldlen = strlen(oldp);
+ chartabsize_T cts;
+ init_chartabsize_arg(&cts, curwin, curwin->w_cursor.lnum, 0, oldp, oldp);
+
+ while (cts.cts_vcol < col && *cts.cts_ptr != NUL) {
// Count a tab for what it's worth (if list mode not on)
- incr = lbr_chartabsize_adv(oldp, &ptr, vcol);
- vcol += incr;
+ incr = lbr_chartabsize_adv(&cts);
+ cts.cts_vcol += incr;
}
+ vcol = cts.cts_vcol;
+ ptr = cts.cts_ptr;
bd.textcol = (colnr_T)(ptr - oldp);
+ clear_chartabsize_arg(&cts);
shortline = (vcol < col) || (vcol == col && !*ptr);
- if (vcol < col) { // line too short, padd with spaces
+ if (vcol < col) { // line too short, pad with spaces
bd.startspaces = col - vcol;
} else if (vcol > col) {
bd.endspaces = vcol - col;
bd.startspaces = incr - bd.endspaces;
- --bd.textcol;
+ bd.textcol--;
delcount = 1;
bd.textcol -= utf_head_off(oldp, oldp + bd.textcol);
if (oldp[bd.textcol] != TAB) {
- /* Only a Tab can be split into spaces. Other
- * characters will have to be moved to after the
- * block, causing misalignment. */
+ // Only a Tab can be split into spaces. Other
+ // characters will have to be moved to after the
+ // block, causing misalignment.
delcount = 0;
bd.endspaces = 0;
}
}
- yanklen = (int)STRLEN(y_array[i]);
+ yanklen = (int)strlen(y_array[i]);
if ((flags & PUT_BLOCK_INNER) == 0) {
// calculate number of spaces required to fill right side of
// block
spaces = y_width + 1;
+ init_chartabsize_arg(&cts, curwin, 0, 0, y_array[i], y_array[i]);
for (int j = 0; j < yanklen; j++) {
- spaces -= lbr_chartabsize(NULL, (char_u *)(&y_array[i][j]), 0);
+ spaces -= lbr_chartabsize(&cts);
+ cts.cts_ptr++;
+ cts.cts_vcol = 0;
}
+ clear_chartabsize_arg(&cts);
if (spaces < 0) {
spaces = 0;
}
@@ -3355,9 +3335,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
break;
}
- totlen = (size_t)(count * (yanklen + spaces)
- + bd.startspaces + bd.endspaces);
- newp = (char_u *)xmalloc(totlen + oldlen + 1);
+ totlen = (size_t)(count * (yanklen + spaces) + bd.startspaces + bd.endspaces);
+ newp = xmalloc(totlen + oldlen + 1);
// copy part up to cursor to new line
ptr = newp;
@@ -3390,11 +3369,11 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
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, (char *)newp, false);
+ ml_replace(curwin->w_cursor.lnum, newp, false);
extmark_splice_cols(curbuf, (int)curwin->w_cursor.lnum - 1, bd.textcol,
delcount, (int)totlen + lines_appended, kExtmarkUndo);
- ++curwin->w_cursor.lnum;
+ curwin->w_cursor.lnum++;
if (i == 0) {
curwin->w_cursor.col += bd.startspaces;
}
@@ -3410,6 +3389,9 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
// adjust '] mark
curbuf->b_op_end.lnum = curwin->w_cursor.lnum - 1;
curbuf->b_op_end.col = bd.textcol + (colnr_T)totlen - 1;
+ if (curbuf->b_op_end.col < 0) {
+ curbuf->b_op_end.col = 0;
+ }
curbuf->b_op_end.coladd = 0;
if (flags & PUT_CURSEND) {
colnr_T len;
@@ -3418,7 +3400,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
curwin->w_cursor.col++;
// in Insert mode we might be after the NUL, correct for that
- len = (colnr_T)STRLEN(get_cursor_line_ptr());
+ len = (colnr_T)strlen(get_cursor_line_ptr());
if (curwin->w_cursor.col > len) {
curwin->w_cursor.col = len;
}
@@ -3431,7 +3413,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((char *)get_cursor_pos_ptr());
+ int bytelen = utfc_ptr2len(get_cursor_pos_ptr());
// put it on the next of the multi-byte character.
col += bytelen;
@@ -3482,7 +3464,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
totlen = (size_t)(count * yanklen);
do {
oldp = ml_get(lnum);
- oldlen = STRLEN(oldp);
+ oldlen = strlen(oldp);
if (lnum > start_lnum) {
pos_T pos = {
.lnum = lnum,
@@ -3497,7 +3479,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
lnum++;
continue;
}
- newp = (char_u *)xmalloc(totlen + oldlen + 1);
+ newp = xmalloc(totlen + oldlen + 1);
memmove(newp, oldp, (size_t)col);
ptr = newp + col;
for (i = 0; i < (size_t)count; i++) {
@@ -3505,7 +3487,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
ptr += yanklen;
}
STRMOVE(ptr, oldp + col);
- ml_replace(lnum, (char *)newp, false);
+ ml_replace(lnum, newp, false);
// compute the byte offset for the last character
first_byte_off = utf_head_off(newp, ptr - 1);
@@ -3553,22 +3535,22 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
// Then append y_array[0] to first line.
lnum = new_cursor.lnum;
ptr = ml_get(lnum) + col;
- totlen = STRLEN(y_array[y_size - 1]);
- newp = (char_u *)xmalloc((size_t)(STRLEN(ptr) + totlen + 1));
+ totlen = strlen(y_array[y_size - 1]);
+ newp = xmalloc((size_t)(strlen(ptr) + totlen + 1));
STRCPY(newp, y_array[y_size - 1]);
STRCAT(newp, ptr);
// insert second line
- ml_append(lnum, (char *)newp, (colnr_T)0, false);
+ ml_append(lnum, newp, (colnr_T)0, false);
new_lnum++;
xfree(newp);
oldp = ml_get(lnum);
- newp = (char_u *)xmalloc((size_t)col + (size_t)yanklen + 1);
+ newp = xmalloc((size_t)col + (size_t)yanklen + 1);
// copy first part of line
memmove(newp, oldp, (size_t)col);
// append to first line
memmove(newp + col, y_array[0], (size_t)yanklen + 1);
- ml_replace(lnum, (char *)newp, false);
+ ml_replace(lnum, newp, false);
curwin->w_cursor.lnum = lnum;
i = 1;
@@ -3588,7 +3570,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
curwin->w_cursor.lnum = lnum;
ptr = ml_get(lnum);
if (cnt == count && i == y_size - 1) {
- lendiff = (int)STRLEN(ptr);
+ lendiff = (int)strlen(ptr);
}
if (*ptr == '#' && preprocs_left()) {
indent = 0; // Leave # lines at start
@@ -3605,7 +3587,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
curwin->w_cursor = old_pos;
// remember how many chars were removed
if (cnt == count && i == y_size - 1) {
- lendiff -= (int)STRLEN(ml_get(lnum));
+ lendiff -= (int)strlen(ml_get(lnum));
}
}
}
@@ -3615,9 +3597,9 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
if (y_type == kMTCharWise
|| (y_type == kMTLineWise && flags & PUT_LINE_SPLIT)) {
for (i = 0; i < y_size - 1; i++) {
- totsize += (bcount_t)STRLEN(y_array[i]) + 1;
+ 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) {
@@ -3661,13 +3643,13 @@ 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 = new_lnum;
- len = STRLEN(y_array[y_size - 1]);
+ len = strlen(y_array[y_size - 1]);
col = (colnr_T)len - lendiff;
if (col > 1) {
curbuf->b_op_end.col = col - 1;
if (len > 0) {
- curbuf->b_op_end.col -= utf_head_off((char_u *)y_array[y_size - 1],
- (char_u *)y_array[y_size - 1] + len - 1);
+ 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;
@@ -3698,7 +3680,7 @@ error:
// put cursor on first non-blank in first inserted line
curwin->w_cursor.col = 0;
if (dir == FORWARD) {
- ++curwin->w_cursor.lnum;
+ curwin->w_cursor.lnum++;
}
beginline(BL_WHITE | BL_FIX);
} else { // put cursor on first inserted character
@@ -3708,7 +3690,7 @@ error:
}
msgmore(nr_lines);
- curwin->w_set_curswant = TRUE;
+ curwin->w_set_curswant = true;
end:
if (cmdmod.cmod_flags & CMOD_LOCKMARKS) {
@@ -3722,7 +3704,7 @@ end:
xfree(y_array);
}
- VIsual_active = FALSE;
+ VIsual_active = false;
// If the cursor is past the end of the line put it at the end.
adjust_cursor_eol();
@@ -3734,24 +3716,27 @@ void adjust_cursor_eol(void)
{
unsigned int cur_ve_flags = get_ve_flags();
- if (curwin->w_cursor.col > 0
- && gchar_cursor() == NUL
- && (cur_ve_flags & VE_ONEMORE) == 0
- && !(restart_edit || (State & MODE_INSERT))) {
- // Put the cursor on the last character in the line.
- dec_cursor();
+ const bool adj_cursor = (curwin->w_cursor.col > 0
+ && gchar_cursor() == NUL
+ && (cur_ve_flags & VE_ONEMORE) == 0
+ && !(restart_edit || (State & MODE_INSERT)));
+ if (!adj_cursor) {
+ return;
+ }
+
+ // Put the cursor on the last character in the line.
+ dec_cursor();
- if (cur_ve_flags == VE_ALL) {
- colnr_T scol, ecol;
+ if (cur_ve_flags == VE_ALL) {
+ colnr_T scol, ecol;
- // Coladd is set to the width of the last character.
- getvcol(curwin, &curwin->w_cursor, &scol, NULL, &ecol);
- curwin->w_cursor.coladd = ecol - scol + 1;
- }
+ // Coladd is set to the width of the last character.
+ getvcol(curwin, &curwin->w_cursor, &scol, NULL, &ecol);
+ curwin->w_cursor.coladd = ecol - scol + 1;
}
}
-/// @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)
@@ -3786,10 +3771,10 @@ int get_unname_register(void)
/// ":dis" and ":registers": Display the contents of the yank registers.
void ex_display(exarg_T *eap)
{
- char_u *p;
+ char *p;
yankreg_T *yb;
int name;
- char_u *arg = (char_u *)eap->arg;
+ char *arg = eap->arg;
int clen;
int type;
@@ -3811,7 +3796,7 @@ void ex_display(exarg_T *eap)
type = 'b'; break;
}
- if (arg != NULL && vim_strchr((char *)arg, name) == NULL) {
+ if (arg != NULL && vim_strchr(arg, name) == NULL) {
continue; // did not ask for this register
}
@@ -3837,7 +3822,7 @@ void ex_display(exarg_T *eap)
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]);
+ do_show = !message_filtered(yb->y_array[j]);
}
if (do_show || yb->y_size == 0) {
@@ -3855,9 +3840,9 @@ void ex_display(exarg_T *eap)
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);
+ for (p = yb->y_array[j];
+ *p != NUL && (n -= ptr2cells(p)) >= 0; p++) { // -V1019
+ clen = utfc_ptr2len(p);
msg_outtrans_len(p, clen);
p += clen - 1;
}
@@ -3865,22 +3850,21 @@ void ex_display(exarg_T *eap)
if (n > 1 && yb->y_type == kMTLineWise) {
msg_puts_attr("^J", attr);
}
- ui_flush(); // show one line at a time
}
os_breakcheck();
}
}
// display last inserted text
- if ((p = get_last_insert()) != NULL
- && (arg == NULL || vim_strchr((char *)arg, '.') != NULL) && !got_int
+ if ((p = (char *)get_last_insert()) != NULL
+ && (arg == NULL || vim_strchr(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((char *)arg, ':') != NULL)
+ if (last_cmdline != NULL && (arg == NULL || vim_strchr(arg, ':') != NULL)
&& !got_int && !message_filtered(last_cmdline)) {
msg_puts("\n c \": ");
dis_msg(last_cmdline, false);
@@ -3888,33 +3872,33 @@ void ex_display(exarg_T *eap)
// display current file name
if (curbuf->b_fname != NULL
- && (arg == NULL || vim_strchr((char *)arg, '%') != NULL) && !got_int
- && !message_filtered((char_u *)curbuf->b_fname)) {
+ && (arg == NULL || vim_strchr(arg, '%') != NULL) && !got_int
+ && !message_filtered(curbuf->b_fname)) {
msg_puts("\n c \"% ");
- dis_msg((char_u *)curbuf->b_fname, false);
+ dis_msg(curbuf->b_fname, false);
}
// display alternate file name
- if ((arg == NULL || vim_strchr((char *)arg, '%') != NULL) && !got_int) {
+ if ((arg == NULL || vim_strchr(arg, '%') != NULL) && !got_int) {
char *fname;
linenr_T dummy;
- if (buflist_name_nr(0, &fname, &dummy) != FAIL && !message_filtered((char_u *)fname)) {
+ if (buflist_name_nr(0, &fname, &dummy) != FAIL && !message_filtered(fname)) {
msg_puts("\n c \"# ");
- dis_msg((char_u *)fname, false);
+ dis_msg(fname, false);
}
}
// display last search pattern
if (last_search_pat() != NULL
- && (arg == NULL || vim_strchr((char *)arg, '/') != NULL) && !got_int
+ && (arg == NULL || vim_strchr(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((char *)arg, '=') != NULL)
+ if (expr_line != NULL && (arg == NULL || vim_strchr(arg, '=') != NULL)
&& !got_int && !message_filtered(expr_line)) {
msg_puts("\n c \"= ");
dis_msg(expr_line, false);
@@ -3925,7 +3909,7 @@ void ex_display(exarg_T *eap)
/// truncate at end of screen line
///
/// @param skip_esc if true, ignore trailing ESC
-static void dis_msg(const char_u *p, bool skip_esc)
+static void dis_msg(const char *p, bool skip_esc)
FUNC_ATTR_NONNULL_ALL
{
int n;
@@ -3934,8 +3918,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((char *)p)) >= 0) {
- if ((l = utfc_ptr2len((char *)p)) > 1) {
+ && (n -= ptr2cells(p)) >= 0) {
+ if ((l = utfc_ptr2len(p)) > 1) {
msg_outtrans_len(p, l);
p += l;
} else {
@@ -3956,11 +3940,11 @@ static void dis_msg(const char_u *p, bool skip_esc)
/// @param include_space - whether to skip space following the comment leader
/// @param[out] is_comment - whether the current line ends with an unclosed
/// comment.
-char_u *skip_comment(char_u *line, bool process, bool include_space, bool *is_comment)
+char *skip_comment(char *line, bool process, bool include_space, bool *is_comment)
{
char *comment_flags = NULL;
int lead_len;
- int leader_offset = get_last_leader_offset((char *)line, &comment_flags);
+ int leader_offset = get_last_leader_offset(line, &comment_flags);
*is_comment = false;
if (leader_offset != -1) {
@@ -3982,7 +3966,7 @@ char_u *skip_comment(char_u *line, bool process, bool include_space, bool *is_co
return line;
}
- lead_len = get_leader_len((char *)line, &comment_flags, false, include_space);
+ lead_len = get_leader_len(line, &comment_flags, false, include_space);
if (lead_len == 0) {
return line;
@@ -4011,8 +3995,8 @@ char_u *skip_comment(char_u *line, bool process, bool include_space, bool *is_co
}
/// @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
+/// @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.
@@ -4020,11 +4004,11 @@ char_u *skip_comment(char_u *line, bool process, bool include_space, bool *is_co
/// @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;
- char_u *curr_start = NULL;
- char_u *cend;
- char_u *newp;
- char_u *spaces; // number of spaces inserted before a line
+ char *curr = NULL;
+ char *curr_start = NULL;
+ char *cend;
+ char *newp;
+ char *spaces; // number of spaces inserted before a line
int endcurr1 = NUL;
int endcurr2 = NUL;
int currsize = 0; // size of the current line
@@ -4033,7 +4017,7 @@ int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions
colnr_T col = 0;
int ret = OK;
int *comments = NULL;
- int remove_comments = (use_formatoptions == TRUE)
+ int remove_comments = (use_formatoptions == true)
&& has_format_option(FO_REMOVE_COMS);
bool prev_was_comment = false;
assert(count >= 1);
@@ -4053,18 +4037,18 @@ int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions
// Don't move anything, just compute the final line length
// 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));
+ curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t));
+ curr = curr_start;
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);
+ curwin->w_buffer->b_op_start.col = (colnr_T)strlen(curr);
}
if (remove_comments) {
// We don't want to remove the comment leader if the
// previous line is not a comment.
if (t > 0 && prev_was_comment) {
- char_u *new_curr = skip_comment(curr, true, insert_space,
- &prev_was_comment);
+ char *new_curr = skip_comment(curr, true, insert_space, &prev_was_comment);
comments[t] = (int)(new_curr - curr);
curr = new_curr;
} else {
@@ -4073,26 +4057,26 @@ int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions
}
if (insert_space && t > 0) {
- curr = (char_u *)skipwhite((char *)curr);
+ curr = skipwhite(curr);
if (*curr != NUL
&& *curr != ')'
&& sumsize != 0
&& endcurr1 != TAB
&& (!has_format_option(FO_MBYTE_JOIN)
- || (utf_ptr2char((char *)curr) < 0x100 && endcurr1 < 0x100))
+ || (utf_ptr2char(curr) < 0x100 && endcurr1 < 0x100))
&& (!has_format_option(FO_MBYTE_JOIN2)
- || (utf_ptr2char((char *)curr) < 0x100 && !utf_eat_space(endcurr1))
+ || (utf_ptr2char(curr) < 0x100 && !utf_eat_space(endcurr1))
|| (endcurr1 < 0x100
- && !utf_eat_space(utf_ptr2char((char *)curr))))) {
+ && !utf_eat_space(utf_ptr2char(curr))))) {
// don't add a space if the line is ending in a space
if (endcurr1 == ' ') {
endcurr1 = endcurr2;
} else {
- ++spaces[t];
+ spaces[t]++;
}
// Extra space when 'joinspaces' set and line ends in '.', '?', or '!'.
if (p_js && (endcurr1 == '.' || endcurr1 == '?' || endcurr1 == '!')) {
- ++spaces[t];
+ spaces[t]++;
}
}
}
@@ -4104,16 +4088,16 @@ int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions
0, spaces[t], spaces[t],
kExtmarkUndo);
}
- currsize = (int)STRLEN(curr);
+ currsize = (int)strlen(curr);
sumsize += currsize + spaces[t];
endcurr1 = endcurr2 = NUL;
if (insert_space && currsize > 0) {
cend = curr + currsize;
MB_PTR_BACK(curr, cend);
- endcurr1 = utf_ptr2char((char *)cend);
+ endcurr1 = utf_ptr2char(cend);
if (cend > curr) {
MB_PTR_BACK(curr, cend);
- endcurr2 = utf_ptr2char((char *)cend);
+ endcurr2 = utf_ptr2char(cend);
}
}
line_breakcheck();
@@ -4127,17 +4111,15 @@ int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions
col = sumsize - currsize - spaces[count - 1];
// allocate the space for the new line
- newp = (char_u *)xmalloc((size_t)sumsize + 1);
+ newp = xmalloc((size_t)sumsize + 1);
cend = newp + sumsize;
*cend = 0;
- /*
- * Move affected lines to the new long one.
- *
- * Move marks from each deleted line to the joined line, adjusting the
- * column. This is not Vi compatible, but Vi deletes the marks, thus that
- * should not really be a problem.
- */
+ // Move affected lines to the new long one.
+ //
+ // Move marks from each deleted line to the joined line, adjusting the
+ // column. This is not Vi compatible, but Vi deletes the marks, thus that
+ // should not really be a problem.
curbuf_splice_pending++;
@@ -4163,17 +4145,18 @@ int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions
break;
}
- curr = curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t - 1));
+ curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t - 1));
+ curr = curr_start;
if (remove_comments) {
curr += comments[t - 1];
}
if (insert_space && t > 1) {
- curr = (char_u *)skipwhite((char *)curr);
+ curr = skipwhite(curr);
}
- currsize = (int)STRLEN(curr);
+ currsize = (int)strlen(curr);
}
- ml_replace(curwin->w_cursor.lnum, (char *)newp, false);
+ ml_replace(curwin->w_cursor.lnum, newp, false);
if (setmark && (cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) {
// Set the '] mark.
@@ -4186,11 +4169,9 @@ int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions
changed_lines(curwin->w_cursor.lnum, currsize,
curwin->w_cursor.lnum + 1, 0L, true);
- /*
- * Delete following lines. To do this we move the cursor there
- * briefly, and then move it back. After del_lines() the cursor may
- * have moved up (last line deleted), so the current lnum is kept in t.
- */
+ // Delete following lines. To do this we move the cursor there
+ // briefly, and then move it back. After del_lines() the cursor may
+ // have moved up (last line deleted), so the current lnum is kept in t.
t = curwin->w_cursor.lnum;
curwin->w_cursor.lnum++;
del_lines((long)count - 1, false);
@@ -4198,17 +4179,15 @@ int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions
curbuf_splice_pending--;
curbuf->deleted_bytes2 = 0;
- /*
- * Set the cursor column:
- * Vi compatible: use the column of the first join
- * vim: use the column of the last join
- */
+ // Set the cursor column:
+ // Vi compatible: use the column of the first join
+ // vim: use the column of the last join
curwin->w_cursor.col =
(vim_strchr(p_cpo, CPO_JOINCOL) != NULL ? currsize : col);
check_cursor_col();
curwin->w_cursor.coladd = 0;
- curwin->w_set_curswant = TRUE;
+ curwin->w_set_curswant = true;
theend:
xfree(spaces);
@@ -4218,530 +4197,29 @@ theend:
return ret;
}
-/// @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)
+/// Reset 'linebreak' and take care of side effects.
+/// @return the previous value, to be passed to restore_lbr().
+static bool reset_lbr(void)
{
- int idx1 = 0, idx2 = 0;
- char_u *p;
- char_u *line1;
- char_u *line2;
-
- if (leader1_len == 0) {
- return leader2_len == 0;
- }
-
- /*
- * If first leader has 'f' flag, the lines can be joined only if the
- * second line does not have a leader.
- * If first leader has 'e' flag, the lines can never be joined.
- * If first leader has 's' flag, the lines can only be joined if there is
- * some text after it and the second line has the 'm' flag.
- */
- if (leader1_flags != NULL) {
- for (p = leader1_flags; *p && *p != ':'; ++p) {
- if (*p == COM_FIRST) {
- return leader2_len == 0;
- }
- if (*p == COM_END) {
- return FALSE;
- }
- if (*p == COM_START) {
- if (*(ml_get(lnum) + leader1_len) == NUL) {
- return FALSE;
- }
- if (leader2_flags == NULL || leader2_len == 0) {
- return FALSE;
- }
- for (p = leader2_flags; *p && *p != ':'; ++p) {
- if (*p == COM_MIDDLE) {
- return TRUE;
- }
- }
- return FALSE;
- }
- }
- }
-
- /*
- * Get current line and next line, compare the leaders.
- * 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++) {}
- line2 = ml_get(lnum + 1);
- for (idx2 = 0; idx2 < leader2_len; ++idx2) {
- if (!ascii_iswhite(line2[idx2])) {
- if (line1[idx1++] != line2[idx2]) {
- break;
- }
- } else {
- while (ascii_iswhite(line1[idx1])) {
- idx1++;
- }
- }
+ if (!curwin->w_p_lbr) {
+ return false;
}
- xfree(line1);
-
- return idx2 == leader2_len && idx1 == leader1_len;
+ // changing 'linebreak' may require w_virtcol to be updated
+ curwin->w_p_lbr = false;
+ curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL);
+ return true;
}
-/// Implementation of the format operator 'gq'.
-///
-/// @param keep_cursor keep cursor on same text char
-static void op_format(oparg_T *oap, int keep_cursor)
+/// Restore 'linebreak' and take care of side effects.
+static void restore_lbr(bool lbr_saved)
{
- 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.
- curwin->w_cursor = oap->cursor_start;
-
- if (u_save((linenr_T)(oap->start.lnum - 1),
- (linenr_T)(oap->end.lnum + 1)) == FAIL) {
+ if (curwin->w_p_lbr || !lbr_saved) {
return;
}
- curwin->w_cursor = oap->start;
-
- if (oap->is_VIsual) {
- // When there is no change: need to remove the Visual selection
- redraw_curbuf_later(INVERTED);
- }
-
- 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).
- if (keep_cursor) {
- saved_cursor = oap->cursor_start;
- }
-
- format_lines((linenr_T)oap->line_count, keep_cursor);
-
- /*
- * Leave the cursor at the first non-blank of the last formatted line.
- * If the cursor was moved one line back (e.g. with "Q}") go to the next
- * line, so "." will do the next lines.
- */
- if (oap->end_adjusted && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
- ++curwin->w_cursor.lnum;
- }
- beginline(BL_WHITE | BL_FIX);
- old_line_count = curbuf->b_ml.ml_line_count - old_line_count;
- msgmore(old_line_count);
-
- 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;
- saved_cursor.lnum = 0;
-
- // formatting may have made the cursor position invalid
- check_cursor();
- }
-
- if (oap->is_VIsual) {
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_old_cursor_lnum != 0) {
- // When lines have been inserted or deleted, adjust the end of
- // the Visual area to be redrawn.
- if (wp->w_old_cursor_lnum > wp->w_old_visual_lnum) {
- wp->w_old_cursor_lnum += old_line_count;
- } else {
- wp->w_old_visual_lnum += old_line_count;
- }
- }
- }
- }
-}
-
-/// Implementation of the format operator 'gq' for when using 'formatexpr'.
-static void op_formatexpr(oparg_T *oap)
-{
- if (oap->is_VIsual) {
- // When there is no change: need to remove the Visual selection
- redraw_curbuf_later(INVERTED);
- }
-
- if (fex_format(oap->start.lnum, oap->line_count, NUL) != 0) {
- // As documented: when 'formatexpr' returns non-zero fall back to
- // internal formatting.
- op_format(oap, false);
- }
-}
-
-/// @param c character to be inserted
-int fex_format(linenr_T lnum, long count, int c)
-{
- int use_sandbox = was_set_insecurely(curwin, "formatexpr", OPT_LOCAL);
- int r;
- char_u *fex;
-
- /*
- * Set v:lnum to the first line number and v:count to the number of lines.
- * Set v:char to the character to be inserted (can be NUL).
- */
- set_vim_var_nr(VV_LNUM, (varnumber_T)lnum);
- set_vim_var_nr(VV_COUNT, (varnumber_T)count);
- set_vim_var_char(c);
-
- // Make a copy, the option could be changed while calling it.
- fex = vim_strsave(curbuf->b_p_fex);
- // Evaluate the function.
- if (use_sandbox) {
- sandbox++;
- }
- r = (int)eval_to_number((char *)fex);
- if (use_sandbox) {
- sandbox--;
- }
-
- set_vim_var_string(VV_CHAR, NULL, -1);
- xfree(fex);
-
- return r;
-}
-
-/// @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.
-///
-/// @param avoid_fex don't use 'formatexpr'
-void format_lines(linenr_T line_count, int avoid_fex)
-{
- bool is_not_par; // current line not part of parag.
- bool next_is_not_par; // next line not part of paragraph
- bool is_end_par; // at end of paragraph
- bool prev_is_end_par = false; // prev. line not part of parag.
- bool next_is_start_par = false;
- 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 = 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;
-
- // length of a line to force formatting: 3 * 'tw'
- const int max_len = comp_textwidth(true) * 3;
-
- // check for 'q', '2' and '1' in 'formatoptions'
- const bool do_comments = has_format_option(FO_Q_COMS); // format comments
- int do_comments_list = 0; // format comments with 'n' or '2'
- const bool do_second_indent = has_format_option(FO_Q_SECOND);
- const bool do_number_indent = has_format_option(FO_Q_NUMBER);
- const bool do_trail_white = has_format_option(FO_WHITE_PAR);
-
- // Get info about the previous and current line.
- if (curwin->w_cursor.lnum > 1) {
- is_not_par = fmt_check_par(curwin->w_cursor.lnum - 1,
- &leader_len, &leader_flags, do_comments);
- } else {
- is_not_par = true;
- }
- next_is_not_par = fmt_check_par(curwin->w_cursor.lnum,
- &next_leader_len, &next_leader_flags, do_comments
- );
- is_end_par = (is_not_par || next_is_not_par);
- if (!is_end_par && do_trail_white) {
- is_end_par = !ends_in_white(curwin->w_cursor.lnum - 1);
- }
-
- curwin->w_cursor.lnum--;
- for (count = line_count; count != 0 && !got_int; --count) {
- /*
- * Advance to next paragraph.
- */
- if (advance) {
- curwin->w_cursor.lnum++;
- prev_is_end_par = is_end_par;
- is_not_par = next_is_not_par;
- leader_len = next_leader_len;
- leader_flags = next_leader_flags;
- }
-
- /*
- * The last line to be formatted.
- */
- if (count == 1 || curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count) {
- next_is_not_par = true;
- next_leader_len = 0;
- next_leader_flags = NULL;
- } else {
- next_is_not_par = fmt_check_par(curwin->w_cursor.lnum + 1,
- &next_leader_len, &next_leader_flags, do_comments
- );
- if (do_number_indent) {
- next_is_start_par =
- (get_number_indent(curwin->w_cursor.lnum + 1) > 0);
- }
- }
- advance = true;
- is_end_par = (is_not_par || next_is_not_par || next_is_start_par);
- if (!is_end_par && do_trail_white) {
- is_end_par = !ends_in_white(curwin->w_cursor.lnum);
- }
-
- /*
- * Skip lines that are not in a paragraph.
- */
- if (is_not_par) {
- if (line_count < 0) {
- break;
- }
- } else {
- /*
- * For the first line of a paragraph, check indent of second line.
- * Don't do this for comments and empty lines.
- */
- if (first_par_line
- && (do_second_indent || do_number_indent)
- && prev_is_end_par
- && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
- if (do_second_indent && !LINEEMPTY(curwin->w_cursor.lnum + 1)) {
- if (leader_len == 0 && next_leader_len == 0) {
- // no comment found
- second_indent =
- get_indent_lnum(curwin->w_cursor.lnum + 1);
- } else {
- second_indent = next_leader_len;
- do_comments_list = 1;
- }
- } else if (do_number_indent) {
- if (leader_len == 0 && next_leader_len == 0) {
- // no comment found
- second_indent =
- get_number_indent(curwin->w_cursor.lnum);
- } else {
- // get_number_indent() is now "comment aware"...
- second_indent =
- get_number_indent(curwin->w_cursor.lnum);
- do_comments_list = 1;
- }
- }
- }
-
- /*
- * When the comment leader changes, it's the end of the paragraph.
- */
- if (curwin->w_cursor.lnum >= curbuf->b_ml.ml_line_count
- || !same_leader(curwin->w_cursor.lnum,
- leader_len, leader_flags,
- next_leader_len,
- next_leader_flags)) {
- // 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;
- }
- }
-
- /*
- * If we have got to the end of a paragraph, or the line is
- * getting long, format it.
- */
- if (is_end_par || force_format) {
- if (need_set_indent) {
- 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 = 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 = MODE_INSERT; // for open_line()
- smd_save = p_smd;
- p_smd = FALSE;
- insertchar(NUL, INSCHAR_FORMAT
- + (do_comments ? INSCHAR_DO_COM : 0)
- + (do_comments && do_comments_list
- ? INSCHAR_COM_LIST : 0)
- + (avoid_fex ? INSCHAR_NO_FEX : 0), second_indent);
- State = old_State;
- p_smd = smd_save;
- second_indent = -1;
- // at end of par.: need to set indent of next par.
- need_set_indent = is_end_par;
- if (is_end_par) {
- // When called with a negative line count, break at the
- // end of the paragraph.
- if (line_count < 0) {
- break;
- }
- first_par_line = true;
- }
- force_format = false;
- }
-
- /*
- * When still in same paragraph, join the lines together. But
- * first delete the leader from the second line.
- */
- if (!is_end_par) {
- advance = false;
- curwin->w_cursor.lnum++;
- curwin->w_cursor.col = 0;
- if (line_count < 0 && u_save_cursor() == FAIL) {
- break;
- }
- if (next_leader_len > 0) {
- (void)del_bytes(next_leader_len, false, false);
- mark_col_adjust(curwin->w_cursor.lnum, (colnr_T)0, 0L,
- (long)-next_leader_len, 0);
- } else if (second_indent > 0) { // the "leader" for FO_Q_SECOND
- int indent = (int)getwhitecols_curline();
-
- if (indent > 0) {
- (void)del_bytes(indent, false, false);
- mark_col_adjust(curwin->w_cursor.lnum,
- (colnr_T)0, 0L, (long)-indent, 0);
- }
- }
- curwin->w_cursor.lnum--;
- if (do_join(2, TRUE, FALSE, FALSE, false) == FAIL) {
- beep_flush();
- break;
- }
- first_par_line = false;
- // If the line is getting long, format it next time
- if (STRLEN(get_cursor_line_ptr()) > (size_t)max_len) {
- force_format = true;
- } else {
- force_format = false;
- }
- }
- }
- line_breakcheck();
- }
-}
-
-/// @return TRUE if line "lnum" ends in a white character.
-static int ends_in_white(linenr_T lnum)
-{
- char_u *s = ml_get(lnum);
- size_t l;
-
- if (*s == NUL) {
- return FALSE;
- }
- l = STRLEN(s) - 1;
- 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.
-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
- char_u *ptr;
-
- ptr = ml_get(lnum);
- if (do_comments) {
- *leader_len = get_leader_len((char *)ptr, (char **)leader_flags, false, true);
- } else {
- *leader_len = 0;
- }
-
- if (*leader_len > 0) {
- /*
- * Search for 'e' flag in comment leader flags.
- */
- flags = *leader_flags;
- while (*flags && *flags != ':' && *flags != COM_END) {
- flags++;
- }
- }
- return *skipwhite((char *)ptr + *leader_len) == NUL
- || (*leader_len > 0 && *flags == COM_END)
- || startPS(lnum, NUL, FALSE);
-}
-
-/// 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;
- int leader_len = 0; // leader len of current line
- char_u *leader_flags = NULL; // flags for leader of current line
- int next_leader_len = 0; // leader len of next line
- char_u *next_leader_flags = NULL; // flags for leader of next line
-
- if (lnum <= 1) {
- return TRUE; // start of the file
- }
- p = ml_get(lnum - 1);
- if (*p == NUL) {
- return TRUE; // after empty line
- }
- const bool do_comments = has_format_option(FO_Q_COMS); // format comments
- if (fmt_check_par(lnum - 1, &leader_len, &leader_flags, do_comments)) {
- return true; // after non-paragraph line
- }
-
- if (fmt_check_par(lnum, &next_leader_len, &next_leader_flags, do_comments)) {
- return true; // "lnum" is not a paragraph line
- }
-
- if (has_format_option(FO_WHITE_PAR) && !ends_in_white(lnum - 1)) {
- return TRUE; // missing trailing space in previous line.
- }
- if (has_format_option(FO_Q_NUMBER) && (get_number_indent(lnum) > 0)) {
- return TRUE; // numbered item starts in "lnum".
- }
- if (!same_leader(lnum - 1, leader_len, leader_flags,
- next_leader_len, next_leader_flags)) {
- return TRUE; // change of comment leader.
- }
- return FALSE;
+ // changing 'linebreak' may require w_virtcol to be updated
+ curwin->w_p_lbr = true;
+ curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL);
}
/// prepare a few things for block mode yank/delete/tilde
@@ -4758,15 +4236,14 @@ int paragraph_start(linenr_T lnum)
static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, bool is_del)
{
int incr = 0;
- char_u *pend;
- char_u *pstart;
- char_u *line;
- char_u *prev_pstart;
- char_u *prev_pend;
- const int lbr_saved = curwin->w_p_lbr;
-
+ char *pend;
+ char *pstart;
+ char *line;
+ char *prev_pstart;
+ char *prev_pend;
// Avoid a problem with unwanted linebreaks in block mode.
- curwin->w_p_lbr = false;
+ const bool lbr_saved = reset_lbr();
+
bdp->startspaces = 0;
bdp->endspaces = 0;
bdp->textlen = 0;
@@ -4780,22 +4257,28 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, bool
bdp->start_char_vcols = 0;
line = ml_get(lnum);
- pstart = line;
prev_pstart = line;
- while (bdp->start_vcol < oap->start_vcol && *pstart) {
+
+ chartabsize_T cts;
+ init_chartabsize_arg(&cts, curwin, lnum, bdp->start_vcol, line, line);
+ while (cts.cts_vcol < oap->start_vcol && *cts.cts_ptr != NUL) {
// Count a tab for what it's worth (if list mode not on)
- incr = lbr_chartabsize(line, pstart, bdp->start_vcol);
- bdp->start_vcol += incr;
- if (ascii_iswhite(*pstart)) {
+ incr = lbr_chartabsize(&cts);
+ cts.cts_vcol += incr;
+ if (ascii_iswhite(*cts.cts_ptr)) {
bdp->pre_whitesp += incr;
bdp->pre_whitesp_c++;
} else {
bdp->pre_whitesp = 0;
bdp->pre_whitesp_c = 0;
}
- prev_pstart = pstart;
- MB_PTR_ADV(pstart);
+ prev_pstart = cts.cts_ptr;
+ MB_PTR_ADV(cts.cts_ptr);
}
+ bdp->start_vcol = cts.cts_vcol;
+ pstart = cts.cts_ptr;
+ clear_chartabsize_arg(&cts);
+
bdp->start_char_vcols = incr;
if (bdp->start_vcol < oap->start_vcol) { // line too short
bdp->end_vcol = bdp->start_vcol;
@@ -4831,13 +4314,18 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, bool
}
}
} else {
+ init_chartabsize_arg(&cts, curwin, lnum, bdp->end_vcol, line, pend);
prev_pend = pend;
- while (bdp->end_vcol <= oap->end_vcol && *pend != NUL) {
+ while (cts.cts_vcol <= oap->end_vcol && *cts.cts_ptr != NUL) {
// Count a tab for what it's worth (if list mode not on)
- prev_pend = pend;
- incr = lbr_chartabsize_adv(line, &pend, bdp->end_vcol);
- bdp->end_vcol += incr;
+ prev_pend = cts.cts_ptr;
+ incr = lbr_chartabsize_adv(&cts);
+ cts.cts_vcol += incr;
}
+ bdp->end_vcol = cts.cts_vcol;
+ pend = cts.cts_ptr;
+ clear_chartabsize_arg(&cts);
+
if (bdp->end_vcol <= oap->end_vcol
&& (!is_del
|| oap->op_type == OP_APPEND
@@ -4869,7 +4357,7 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, bool
}
bdp->textcol = (colnr_T)(pstart - line);
bdp->textstart = pstart;
- curwin->w_p_lbr = lbr_saved;
+ restore_lbr(lbr_saved);
}
/// Handle the add/subtract operator.
@@ -4921,20 +4409,20 @@ void op_addsub(oparg_T *oap, linenr_T Prenum1, bool g_cmd)
} else if (oap->motion_type == kMTLineWise) {
curwin->w_cursor.col = 0;
pos.col = 0;
- length = (colnr_T)STRLEN(ml_get(pos.lnum));
+ length = (colnr_T)strlen(ml_get(pos.lnum));
} else {
// oap->motion_type == kMTCharWise
if (pos.lnum == oap->start.lnum && !oap->inclusive) {
dec(&(oap->end));
}
- length = (colnr_T)STRLEN(ml_get(pos.lnum));
+ length = (colnr_T)strlen(ml_get(pos.lnum));
pos.col = 0;
if (pos.lnum == oap->start.lnum) {
pos.col += oap->start.col;
length -= oap->start.col;
}
if (pos.lnum == oap->end.lnum) {
- length = (int)STRLEN(ml_get(oap->end.lnum));
+ length = (int)strlen(ml_get(oap->end.lnum));
if (oap->end.col >= length) {
oap->end.col = length - 1;
}
@@ -4962,7 +4450,7 @@ void op_addsub(oparg_T *oap, linenr_T Prenum1, bool g_cmd)
if (!change_cnt && oap->is_VIsual) {
// No change: need to remove the Visual selection
- redraw_curbuf_later(INVERTED);
+ redraw_curbuf_later(UPD_INVERTED);
}
// Set '[ mark if something changed. Keep the last end
@@ -4990,13 +4478,13 @@ void op_addsub(oparg_T *oap, linenr_T Prenum1, bool g_cmd)
int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
{
int col;
- char_u *buf1 = NULL;
- char_u buf2[NUMBUFLEN];
+ char *buf1 = NULL;
+ char buf2[NUMBUFLEN];
int pre; // 'X' or 'x': hex; '0': octal; 'B' or 'b': bin
static bool hexupper = false; // 0xABC
uvarnumber_T n;
uvarnumber_T oldn;
- char_u *ptr;
+ char *ptr;
int c;
int todel;
int firstdigit;
@@ -5010,12 +4498,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((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"
+ 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"
// "Unsigned"
- const bool do_unsigned = vim_strchr((char *)curbuf->b_p_nf, 'u') != NULL;
+ const bool do_unsigned = vim_strchr(curbuf->b_p_nf, 'u') != NULL;
if (virtual_active()) {
save_coladd = pos->coladd;
@@ -5026,7 +4514,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
ptr = ml_get(pos->lnum);
col = pos->col;
- if (*ptr == NUL || col + !!save_coladd >= (int)STRLEN(ptr)) {
+ if (*ptr == NUL || col + !!save_coladd >= (int)strlen(ptr)) {
goto theend;
}
@@ -5098,7 +4586,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((char *)ptr + col);
+ int mb_len = utfc_ptr2len(ptr + col);
col += mb_len;
length -= mb_len;
@@ -5117,7 +4605,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
}
// If a number was found, and saving for undo works, replace the number.
- firstdigit = ptr[col];
+ firstdigit = (uint8_t)ptr[col];
if (!ascii_isdigit(firstdigit) && !(do_alpha && ASCII_ISALPHA(firstdigit))) {
beep_flush();
goto theend;
@@ -5166,7 +4654,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
// get the number value (unsigned)
if (visual && VIsual_mode != 'V') {
maxlen = (curbuf->b_visual.vi_curswant == MAXCOL
- ? (int)STRLEN(ptr) - col
+ ? (int)strlen(ptr) - col
: length);
}
@@ -5271,7 +4759,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
length--;
}
if (pre == 'b' || pre == 'B' || pre == 'x' || pre == 'X') {
- *ptr++ = (char_u)pre;
+ *ptr++ = (char)pre;
length--;
}
@@ -5293,15 +4781,15 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
buf2[i] = '\0';
} else if (pre == 0) {
- vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIu64, (uint64_t)n);
+ vim_snprintf(buf2, ARRAY_SIZE(buf2), "%" PRIu64, (uint64_t)n);
} else if (pre == '0') {
- vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIo64, (uint64_t)n);
+ vim_snprintf(buf2, ARRAY_SIZE(buf2), "%" PRIo64, (uint64_t)n);
} else if (hexupper) {
- vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIX64, (uint64_t)n);
+ vim_snprintf(buf2, ARRAY_SIZE(buf2), "%" PRIX64, (uint64_t)n);
} else {
- vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIx64, (uint64_t)n);
+ vim_snprintf(buf2, ARRAY_SIZE(buf2), "%" PRIx64, (uint64_t)n);
}
- length -= (int)STRLEN(buf2);
+ length -= (int)strlen(buf2);
// Adjust number of zeros to the new number of digits, so the
// total length of the number remains the same.
@@ -5412,13 +4900,13 @@ void format_reg_type(MotionType reg_type, colnr_T reg_width, char *buf, size_t b
/// Otherwise just return `s`.
///
/// @return a void * for use in get_reg_contents().
-static void *get_reg_wrap_one_line(char_u *s, int flags)
+static void *get_reg_wrap_one_line(char *s, int flags)
{
if (!(flags & kGRegList)) {
return s;
}
list_T *const list = tv_list_alloc(1);
- tv_list_append_allocated_string(list, (char *)s);
+ tv_list_append_allocated_string(list, s);
return list;
}
@@ -5460,9 +4948,9 @@ void *get_reg_contents(int regname, int flags)
return NULL;
}
if (allocated) {
- return get_reg_wrap_one_line((char_u *)retval, flags);
+ return get_reg_wrap_one_line(retval, flags);
}
- return get_reg_wrap_one_line(vim_strsave((char_u *)retval), flags);
+ return get_reg_wrap_one_line(xstrdup(retval), flags);
}
yankreg_T *reg = get_yank_register(regname, YREG_PASTE);
@@ -5479,16 +4967,12 @@ void *get_reg_contents(int regname, int flags)
return list;
}
- /*
- * Compute length of resulting string.
- */
+ // Compute length of resulting string.
size_t len = 0;
for (size_t i = 0; i < reg->y_size; i++) {
- len += STRLEN(reg->y_array[i]);
- /*
- * Insert a newline between lines and after last line if
- * y_type is kMTLineWise.
- */
+ len += strlen(reg->y_array[i]);
+ // Insert a newline between lines and after last line if
+ // y_type is kMTLineWise.
if (reg->y_type == kMTLineWise || i < reg->y_size - 1) {
len++;
}
@@ -5496,18 +4980,14 @@ void *get_reg_contents(int regname, int flags)
retval = xmalloc(len + 1);
- /*
- * Copy the lines of the yank register into the string.
- */
+ // Copy the lines of the yank register into the string.
len = 0;
for (size_t i = 0; i < reg->y_size; i++) {
STRCPY(retval + len, reg->y_array[i]);
- len += STRLEN(retval + len);
+ len += strlen(retval + len);
- /*
- * Insert a NL between lines and after the last line if y_type is
- * kMTLineWise.
- */
+ // Insert a NL between lines and after the last line if y_type is
+ // kMTLineWise.
if (reg->y_type == kMTLineWise || i < reg->y_size - 1) {
retval[len++] = '\n';
}
@@ -5548,7 +5028,7 @@ static void finish_write_reg(int name, yankreg_T *reg, yankreg_T *old_y_previous
/// 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)
+void write_reg_contents(int name, const char *str, ssize_t len, int must_append)
{
write_reg_contents_ex(name, str, len, must_append, kMTUnknown, 0L);
}
@@ -5557,9 +5037,9 @@ void write_reg_contents_lst(int name, char **strings, bool must_append, MotionTy
colnr_T block_len)
{
if (name == '/' || name == '=') {
- char_u *s = (char_u *)strings[0];
+ char *s = strings[0];
if (strings[0] == NULL) {
- s = (char_u *)"";
+ s = "";
} else if (strings[1] != NULL) {
emsg(_("E883: search pattern and expression register may not "
"contain two or more lines"));
@@ -5579,7 +5059,7 @@ void write_reg_contents_lst(int name, char **strings, bool must_append, MotionTy
return;
}
- str_to_reg(reg, yank_type, (char *)strings, STRLEN((char_u *)strings),
+ str_to_reg(reg, yank_type, (char *)strings, strlen((char *)strings),
block_len, true);
finish_write_reg(name, reg, old_y_previous);
}
@@ -5602,16 +5082,16 @@ void write_reg_contents_lst(int name, char **strings, bool must_append, MotionTy
/// is an uppercase letter.
/// @param yank_type The motion type (kMTUnknown to auto detect)
/// @param block_len width of visual block
-void write_reg_contents_ex(int name, const char_u *str, ssize_t len, bool must_append,
+void write_reg_contents_ex(int name, const char *str, ssize_t len, bool must_append,
MotionType yank_type, colnr_T block_len)
{
if (len < 0) {
- len = (ssize_t)STRLEN(str);
+ len = (ssize_t)strlen(str);
}
// Special case: '/' search pattern
if (name == '/') {
- set_last_search_pat(str, RE_SEARCH, TRUE, TRUE);
+ set_last_search_pat(str, RE_SEARCH, true, true);
return;
}
@@ -5619,14 +5099,14 @@ void write_reg_contents_ex(int name, const char_u *str, ssize_t len, bool must_a
buf_T *buf;
if (ascii_isdigit(*str)) {
- int num = atoi((char *)str);
+ int num = atoi(str);
buf = buflist_findnr(num);
if (buf == NULL) {
semsg(_(e_nobufnr), (int64_t)num);
}
} else {
- buf = buflist_findnr(buflist_findpat((char *)str, (char *)str + STRLEN(str),
+ buf = buflist_findnr(buflist_findpat(str, str + strlen(str),
true, false, false));
}
if (buf == NULL) {
@@ -5643,7 +5123,7 @@ void write_reg_contents_ex(int name, const char_u *str, ssize_t len, bool must_a
if (must_append && expr_line) {
// append has been specified and expr_line already exists, so we'll
// append the new string to expr_line.
- size_t exprlen = STRLEN(expr_line);
+ size_t exprlen = strlen(expr_line);
totlen += exprlen;
offset = exprlen;
@@ -5667,7 +5147,7 @@ void write_reg_contents_ex(int name, const char_u *str, ssize_t len, bool must_a
if (!(reg = init_write_reg(name, &old_y_previous, must_append))) {
return;
}
- str_to_reg(reg, yank_type, (char *)str, (size_t)len, block_len, false);
+ str_to_reg(reg, yank_type, str, (size_t)len, block_len, false);
finish_write_reg(name, reg, old_y_previous);
}
@@ -5701,18 +5181,18 @@ static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, const char *str,
// Count the number of lines within the string
if (str_list) {
- for (char_u **ss = (char_u **)str; *ss != NULL; ++ss) {
+ for (char **ss = (char **)str; *ss != NULL; ss++) {
newlines++;
}
} else {
newlines = memcnt(str, '\n', len);
if (yank_type == kMTCharWise || len == 0 || str[len - 1] != '\n') {
extraline = 1;
- ++newlines; // count extra newline at the end
+ newlines++; // count extra newline at the end
}
if (y_ptr->y_size > 0 && y_ptr->y_type == kMTCharWise) {
append = true;
- --newlines; // uncount newline when appending first line
+ newlines--; // uncount newline when appending first line
}
}
@@ -5723,7 +5203,7 @@ static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, const char *str,
}
// Grow the register array to hold the pointers to the new lines.
- char **pp = xrealloc(y_ptr->y_array, (y_ptr->y_size + newlines) * sizeof(char_u *));
+ char **pp = xrealloc(y_ptr->y_array, (y_ptr->y_size + newlines) * sizeof(char *));
y_ptr->y_array = pp;
size_t lnum = y_ptr->y_size; // The current line number.
@@ -5733,8 +5213,8 @@ static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, const char *str,
// Find the end of each line and save it into the array.
if (str_list) {
- for (char_u **ss = (char_u **)str; *ss != NULL; ++ss, ++lnum) {
- size_t ss_len = STRLEN(*ss);
+ for (char **ss = (char **)str; *ss != NULL; ss++, lnum++) {
+ size_t ss_len = strlen(*ss);
pp[lnum] = xmemdupz(*ss, ss_len);
if (ss_len > maxlen) {
maxlen = ss_len;
@@ -5742,18 +5222,17 @@ static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, const char *str,
}
} else {
size_t line_len;
- for (const char_u *start = (char_u *)str, *end = (char_u *)str + len;
+ for (const char *start = str, *end = str + len;
start < end + extraline;
start += line_len + 1, lnum++) {
assert(end - start >= 0);
- line_len = (size_t)((char_u *)xmemscan(start, '\n',
- (size_t)(end - start)) - start);
+ line_len = (size_t)((char *)xmemscan(start, '\n', (size_t)(end - start)) - start);
if (line_len > maxlen) {
maxlen = line_len;
}
// When appending, copy the previous line and free it after.
- size_t extra = append ? STRLEN(pp[--lnum]) : 0;
+ size_t extra = append ? strlen(pp[--lnum]) : 0;
char *s = xmallocz(line_len + extra);
if (extra > 0) {
memcpy(s, pp[lnum], extra);
@@ -5799,8 +5278,8 @@ void clear_oparg(oparg_T *oap)
/// 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)
+static varnumber_T line_count_info(char *line, varnumber_T *wc, varnumber_T *cc, varnumber_T limit,
+ int eol_size)
{
varnumber_T i;
varnumber_T words = 0;
@@ -5817,7 +5296,7 @@ static varnumber_T line_count_info(char_u *line, varnumber_T *wc, varnumber_T *c
is_word = 1;
}
chars++;
- i += utfc_ptr2len((char *)line + i);
+ i += utfc_ptr2len(line + i);
}
if (is_word) {
@@ -5841,9 +5320,9 @@ static varnumber_T line_count_info(char_u *line, varnumber_T *wc, varnumber_T *c
/// @param dict when not NULL, store the info there instead of showing it.
void cursor_pos_info(dict_T *dict)
{
- char_u *p;
- char_u buf1[50];
- char_u buf2[40];
+ char *p;
+ char buf1[50];
+ char buf2[40];
linenr_T lnum;
varnumber_T byte_count = 0;
varnumber_T bom_count = 0;
@@ -5883,12 +5362,12 @@ void cursor_pos_info(dict_T *dict)
max_pos = VIsual;
}
if (*p_sel == 'e' && max_pos.col > 0) {
- --max_pos.col;
+ max_pos.col--;
}
if (l_VIsual_mode == Ctrl_V) {
- char_u *const saved_sbr = p_sbr;
- char_u *const saved_w_sbr = curwin->w_p_sbr;
+ char *const saved_sbr = p_sbr;
+ char *const saved_w_sbr = curwin->w_p_sbr;
// Make 'sbr' empty for a moment to get the correct size.
p_sbr = empty_option;
@@ -5896,8 +5375,7 @@ void cursor_pos_info(dict_T *dict)
oparg.is_VIsual = true;
oparg.motion_type = kMTBlockWise;
oparg.op_type = OP_NOP;
- getvcols(curwin, &min_pos, &max_pos,
- &oparg.start_vcol, &oparg.end_vcol);
+ getvcols(curwin, &min_pos, &max_pos, &oparg.start_vcol, &oparg.end_vcol);
p_sbr = saved_sbr;
curwin->w_p_sbr = saved_w_sbr;
if (curwin->w_curswant == MAXCOL) {
@@ -5913,7 +5391,7 @@ void cursor_pos_info(dict_T *dict)
line_count_selected = max_pos.lnum - min_pos.lnum + 1;
}
- for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) {
+ for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; lnum++) {
// Check for a CTRL-C every 100000 characters.
if (byte_count > last_check) {
os_breakcheck();
@@ -5926,7 +5404,7 @@ void cursor_pos_info(dict_T *dict)
// Do extra processing for VIsual mode.
if (l_VIsual_active
&& lnum >= min_pos.lnum && lnum <= max_pos.lnum) {
- char_u *s = NULL;
+ char *s = NULL;
long len = 0L;
switch (l_VIsual_mode) {
@@ -5958,7 +5436,7 @@ void cursor_pos_info(dict_T *dict)
if (lnum == curbuf->b_ml.ml_line_count
&& !curbuf->b_p_eol
&& (curbuf->b_p_bin || !curbuf->b_p_fixeol)
- && (long)STRLEN(s) < len) {
+ && (long)strlen(s) < len) {
byte_count_cursor -= eol_size;
}
}
@@ -5990,7 +5468,7 @@ void cursor_pos_info(dict_T *dict)
getvcols(curwin, &min_pos, &max_pos, &min_pos.col, &max_pos.col);
int64_t cols;
STRICT_SUB(oparg.end_vcol + 1, oparg.start_vcol, &cols, int64_t);
- vim_snprintf((char *)buf1, sizeof(buf1), _("%" PRId64 " Cols; "),
+ vim_snprintf(buf1, sizeof(buf1), _("%" PRId64 " Cols; "),
cols);
} else {
buf1[0] = NUL;
@@ -5998,7 +5476,7 @@ void cursor_pos_info(dict_T *dict)
if (char_count_cursor == byte_count_cursor
&& char_count == byte_count) {
- vim_snprintf((char *)IObuff, IOSIZE,
+ vim_snprintf(IObuff, IOSIZE,
_("Selected %s%" PRId64 " of %" PRId64 " Lines;"
" %" PRId64 " of %" PRId64 " Words;"
" %" PRId64 " of %" PRId64 " Bytes"),
@@ -6007,7 +5485,7 @@ void cursor_pos_info(dict_T *dict)
(int64_t)word_count_cursor, (int64_t)word_count,
(int64_t)byte_count_cursor, (int64_t)byte_count);
} else {
- vim_snprintf((char *)IObuff, IOSIZE,
+ vim_snprintf(IObuff, IOSIZE,
_("Selected %s%" PRId64 " of %" PRId64 " Lines;"
" %" PRId64 " of %" PRId64 " Words;"
" %" PRId64 " of %" PRId64 " Chars;"
@@ -6021,28 +5499,28 @@ void cursor_pos_info(dict_T *dict)
} else {
p = get_cursor_line_ptr();
validate_virtcol();
- col_print((char *)buf1, sizeof(buf1), (int)curwin->w_cursor.col + 1,
+ col_print(buf1, sizeof(buf1), (int)curwin->w_cursor.col + 1,
(int)curwin->w_virtcol + 1);
- col_print((char *)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) {
- vim_snprintf((char *)IObuff, IOSIZE,
+ vim_snprintf(IObuff, IOSIZE,
_("Col %s of %s; Line %" PRId64 " of %" PRId64 ";"
" Word %" PRId64 " of %" PRId64 ";"
" Byte %" PRId64 " of %" PRId64 ""),
- (char *)buf1, (char *)buf2,
+ buf1, buf2,
(int64_t)curwin->w_cursor.lnum,
(int64_t)curbuf->b_ml.ml_line_count,
(int64_t)word_count_cursor, (int64_t)word_count,
(int64_t)byte_count_cursor, (int64_t)byte_count);
} else {
- vim_snprintf((char *)IObuff, IOSIZE,
+ vim_snprintf(IObuff, IOSIZE,
_("Col %s of %s; Line %" PRId64 " of %" PRId64 ";"
" Word %" PRId64 " of %" PRId64 ";"
" Char %" PRId64 " of %" PRId64 ";"
" Byte %" PRId64 " of %" PRId64 ""),
- (char *)buf1, (char *)buf2,
+ buf1, buf2,
(int64_t)curwin->w_cursor.lnum,
(int64_t)curbuf->b_ml.ml_line_count,
(int64_t)word_count_cursor, (int64_t)word_count,
@@ -6054,19 +5532,19 @@ void cursor_pos_info(dict_T *dict)
bom_count = bomb_size();
if (dict == NULL && bom_count > 0) {
- const size_t len = STRLEN(IObuff);
- vim_snprintf((char *)IObuff + len, IOSIZE - len,
+ const size_t len = strlen(IObuff);
+ vim_snprintf(IObuff + len, IOSIZE - len,
_("(+%" PRId64 " for BOM)"), (int64_t)bom_count);
}
if (dict == NULL) {
// Don't shorten this message, the user asked for it.
p = p_shm;
- p_shm = (char_u *)"";
+ p_shm = "";
if (p_ch < 1) {
msg_start();
msg_scroll = true;
}
- msg((char *)IObuff);
+ msg(IObuff);
p_shm = p;
}
}
@@ -6101,13 +5579,22 @@ static void op_colon(oparg_T *oap)
} else {
stuffnumReadbuff((long)oap->start.lnum);
}
- if (oap->end.lnum != oap->start.lnum) {
+
+ // When using !! on a closed fold the range ".!" works best to operate
+ // on, it will be made the whole closed fold later.
+ linenr_T endOfStartFold = oap->start.lnum;
+ (void)hasFolding(oap->start.lnum, NULL, &endOfStartFold);
+ if (oap->end.lnum != oap->start.lnum && oap->end.lnum != endOfStartFold) {
+ // Make it a range with the end line.
stuffcharReadbuff(',');
if (oap->end.lnum == curwin->w_cursor.lnum) {
stuffcharReadbuff('.');
} else if (oap->end.lnum == curbuf->b_ml.ml_line_count) {
stuffcharReadbuff('$');
- } else if (oap->start.lnum == curwin->w_cursor.lnum) {
+ } else if (oap->start.lnum == curwin->w_cursor.lnum
+ // do not use ".+number" for a closed fold, it would count
+ // folded lines twice
+ && !hasFolding(oap->end.lnum, NULL, NULL)) {
stuffReadbuff(".+");
stuffnumReadbuff(oap->line_count - 1);
} else {
@@ -6139,10 +5626,11 @@ static void op_colon(oparg_T *oap)
static Callback opfunc_cb;
/// Process the 'operatorfunc' option value.
-/// @return OK or FAIL
-int set_operatorfunc_option(void)
+void set_operatorfunc_option(char **errmsg)
{
- return option_set_callback_func(p_opfunc, &opfunc_cb);
+ if (option_set_callback_func(p_opfunc, &opfunc_cb) == FAIL) {
+ *errmsg = e_invarg;
+ }
}
#if defined(EXITFREE)
@@ -6152,12 +5640,17 @@ void free_operatorfunc_option(void)
}
#endif
+/// Mark the global 'operatorfunc' callback with "copyID" so that it is not
+/// garbage collected.
+bool set_ref_in_opfunc(int copyID)
+{
+ return set_ref_in_callback(&opfunc_cb, copyID, NULL, NULL);
+}
+
/// 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;
@@ -6184,9 +5677,11 @@ static void op_function(const oparg_T *oap)
// Reset virtual_op so that 'virtualedit' can be changed in the
// function.
+ const TriState save_virtual_op = virtual_op;
virtual_op = kNone;
// Reset finish_op so that mode() returns the right value.
+ const bool save_finish_op = finish_op;
finish_op = false;
typval_T rettv;
@@ -6303,10 +5798,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
const bool redo_yank = vim_strchr(p_cpo, CPO_YANK) != NULL && !gui_yank;
// Avoid a problem with unwanted linebreaks in block mode
- if (curwin->w_p_lbr) {
- curwin->w_valid &= ~VALID_VIRTCOL;
- }
- curwin->w_p_lbr = false;
+ (void)reset_lbr();
oap->is_VIsual = VIsual_active;
if (oap->motion_force == 'V') {
oap->motion_type = kMTLineWise;
@@ -6353,7 +5845,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((char *)cap->searchbuf, -1);
+ AppendToRedobuffLit(cap->searchbuf, -1);
}
AppendToRedobuff(NL_STR);
} else if (cap->cmdchar == ':' || cap->cmdchar == K_COMMAND) {
@@ -6363,7 +5855,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
if (repeat_cmdline == NULL) {
ResetRedobuff();
} else {
- AppendToRedobuffLit((char *)repeat_cmdline, -1);
+ AppendToRedobuffLit(repeat_cmdline, -1);
AppendToRedobuff(NL_STR);
XFREE_CLEAR(repeat_cmdline);
}
@@ -6413,10 +5905,10 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
if (lt(VIsual, curwin->w_cursor)) {
VIsual.col = 0;
curwin->w_cursor.col =
- (colnr_T)STRLEN(ml_get(curwin->w_cursor.lnum));
+ (colnr_T)strlen(ml_get(curwin->w_cursor.lnum));
} else {
curwin->w_cursor.col = 0;
- VIsual.col = (colnr_T)STRLEN(ml_get(VIsual.lnum));
+ VIsual.col = (colnr_T)strlen(ml_get(VIsual.lnum));
}
VIsual_mode = 'v';
} else if (VIsual_mode == 'v') {
@@ -6446,7 +5938,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
|| oap->motion_type == kMTLineWise)
&& hasFolding(curwin->w_cursor.lnum, NULL,
&curwin->w_cursor.lnum)) {
- curwin->w_cursor.col = (colnr_T)STRLEN(get_cursor_line_ptr());
+ curwin->w_cursor.col = (colnr_T)strlen(get_cursor_line_ptr());
}
}
oap->end = curwin->w_cursor;
@@ -6464,7 +5956,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
curwin->w_cursor.col = 0;
}
if (hasFolding(oap->start.lnum, NULL, &oap->start.lnum)) {
- oap->start.col = (colnr_T)STRLEN(ml_get(oap->start.lnum));
+ oap->start.col = (colnr_T)strlen(ml_get(oap->start.lnum));
}
}
oap->end = oap->start;
@@ -6595,15 +6087,15 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
|| oap->op_type == OP_FILTER)
&& oap->motion_force == NUL) {
// Make sure redrawing is correct.
- curwin->w_p_lbr = lbr_saved;
- redraw_curbuf_later(INVERTED);
+ restore_lbr(lbr_saved);
+ redraw_curbuf_later(UPD_INVERTED);
}
}
}
// Include the trailing byte of a multi-byte char.
if (oap->inclusive) {
- const int l = utfc_ptr2len((char *)ml_get_pos(&oap->end));
+ const int l = utfc_ptr2len(ml_get_pos(&oap->end));
if (l > 1) {
oap->end.col += l - 1;
}
@@ -6627,8 +6119,8 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
// 'modifiable is off or creating a fold.
if (oap->is_VIsual && (oap->empty || !MODIFIABLE(curbuf)
|| oap->op_type == OP_FOLD)) {
- curwin->w_p_lbr = lbr_saved;
- redraw_curbuf_later(INVERTED);
+ restore_lbr(lbr_saved);
+ redraw_curbuf_later(UPD_INVERTED);
}
// If the end of an operator is in column one while oap->motion_type
@@ -6648,7 +6140,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
if (inindent(0)) {
oap->motion_type = kMTLineWise;
} else {
- oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum));
+ oap->end.col = (colnr_T)strlen(ml_get(oap->end.lnum));
if (oap->end.col) {
oap->end.col--;
oap->inclusive = true;
@@ -6703,7 +6195,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
CancelRedo();
}
} else {
- curwin->w_p_lbr = lbr_saved;
+ restore_lbr(lbr_saved);
oap->excl_tr_ws = cap->cmdchar == 'z';
(void)op_yank(oap, !gui_yank);
}
@@ -6727,10 +6219,8 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
restart_edit = 0;
// Restore linebreak, so that when the user edits it looks as before.
- curwin->w_p_lbr = lbr_saved;
+ restore_lbr(lbr_saved);
- // Reset finish_op now, don't want it set inside edit().
- finish_op = false;
if (op_change(oap)) { // will call edit()
cap->retval |= CA_COMMAND_BUSY;
}
@@ -6754,7 +6244,11 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
// If 'equalprg' is empty, do the indenting internally.
if (oap->op_type == OP_INDENT && *get_equalprg() == NUL) {
if (curbuf->b_p_lisp) {
- op_reindent(oap, get_lisp_indent);
+ if (use_indentexpr_for_lisp()) {
+ op_reindent(oap, get_expr_indent);
+ } else {
+ op_reindent(oap, get_lisp_indent);
+ }
break;
}
op_reindent(oap,
@@ -6798,9 +6292,8 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
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;
+ // Restore linebreak, so that when the user edits it looks as before.
+ restore_lbr(lbr_saved);
// call 'operatorfunc'
op_function(oap);
@@ -6824,12 +6317,12 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
restart_edit = 0;
// Restore linebreak, so that when the user edits it looks as before.
- curwin->w_p_lbr = lbr_saved;
+ restore_lbr(lbr_saved);
op_insert(oap, cap->count1);
// Reset linebreak, so that formatting works correctly.
- curwin->w_p_lbr = false;
+ (void)reset_lbr();
// TODO(brammool): when inserting in several lines, should format all
// the lines.
@@ -6850,7 +6343,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
CancelRedo();
} else {
// Restore linebreak, so that when the user edits it looks as before.
- curwin->w_p_lbr = lbr_saved;
+ restore_lbr(lbr_saved);
op_replace(oap, cap->nchar);
}
@@ -6888,7 +6381,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
CancelRedo();
} else {
VIsual_active = true;
- curwin->w_p_lbr = lbr_saved;
+ restore_lbr(lbr_saved);
op_addsub(oap, (linenr_T)cap->count1, redo_VIsual.rv_arg);
VIsual_active = false;
}
@@ -6903,7 +6396,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
if (!p_sol && oap->motion_type == kMTLineWise && !oap->end_adjusted
&& (oap->op_type == OP_LSHIFT || oap->op_type == OP_RSHIFT
|| oap->op_type == OP_DELETE)) {
- curwin->w_p_lbr = false;
+ (void)reset_lbr();
coladvance(curwin->w_curswant = old_col);
}
} else {
@@ -6912,7 +6405,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
clearop(oap);
motion_force = NUL;
}
- curwin->w_p_lbr = lbr_saved;
+ restore_lbr(lbr_saved);
}
/// Check if the default register (used in an unnamed paste) should be a
@@ -6950,7 +6443,7 @@ static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing)
}
if (!eval_has_provider("clipboard")) {
- if (batch_change_count == 1 && !quiet
+ if (batch_change_count <= 1 && !quiet
&& (!clipboard_didwarn || (explicit_cb_reg && !redirecting()))) {
clipboard_didwarn = true;
// Do NOT error (emsg()) here--if it interrupts :redir we get into
@@ -7042,7 +6535,7 @@ 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) {
@@ -7063,7 +6556,7 @@ void finish_yankreg_from_object(yankreg_T *reg, bool clipboard_adjust)
if (reg->y_type == kMTBlockWise) {
size_t maxlen = 0;
for (size_t i = 0; i < reg->y_size; i++) {
- size_t rowlen = STRLEN(reg->y_array[i]);
+ size_t rowlen = strlen(reg->y_array[i]);
if (rowlen > maxlen) {
maxlen = rowlen;
}
@@ -7106,8 +6599,8 @@ 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 = (char_u *)TV_LIST_ITEM_TV(tv_list_last(res))->vval.v_string;
- if (regtype == NULL || STRLEN(regtype) > 1) {
+ char *regtype = TV_LIST_ITEM_TV(tv_list_last(res))->vval.v_string;
+ if (regtype == NULL || strlen(regtype) > 1) {
goto err;
}
switch (regtype[0]) {
@@ -7150,7 +6643,7 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet)
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) {
@@ -7169,7 +6662,7 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet)
if (reg->y_type == kMTBlockWise) {
size_t maxlen = 0;
for (size_t i = 0; i < reg->y_size; i++) {
- size_t rowlen = STRLEN(reg->y_array[i]);
+ size_t rowlen = strlen(reg->y_array[i]);
if (rowlen > maxlen) {
maxlen = rowlen;
}
@@ -7419,13 +6912,13 @@ bcount_t get_region_bytecount(buf_T *buf, linenr_T start_lnum, linenr_T end_lnum
return end_col - start_col;
}
const char *first = (const char *)ml_get_buf(buf, start_lnum, false);
- bcount_t deleted_bytes = (bcount_t)STRLEN(first) - start_col + 1;
+ bcount_t deleted_bytes = (bcount_t)strlen(first) - start_col + 1;
for (linenr_T i = 1; i <= end_lnum - start_lnum - 1; i++) {
if (start_lnum + i > max_lnum) {
return deleted_bytes;
}
- deleted_bytes += (bcount_t)STRLEN(ml_get_buf(buf, start_lnum + i, false)) + 1;
+ deleted_bytes += (bcount_t)strlen(ml_get_buf(buf, start_lnum + i, false)) + 1;
}
if (end_lnum > max_lnum) {
return deleted_bytes;
diff --git a/src/nvim/ops.h b/src/nvim/ops.h
index 05893c9940..75ea1853a0 100644
--- a/src/nvim/ops.h
+++ b/src/nvim/ops.h
@@ -2,14 +2,17 @@
#define NVIM_OPS_H
#include <stdbool.h>
+#include <stddef.h>
#include "nvim/ascii.h"
#include "nvim/eval/typval.h"
-#include "nvim/ex_cmds_defs.h" // for exarg_T
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/extmark.h"
#include "nvim/macros.h"
-#include "nvim/normal.h" // for MotionType and oparg_T
+#include "nvim/normal.h"
#include "nvim/os/time.h"
+#include "nvim/pos.h"
#include "nvim/types.h"
typedef int (*Indenter)(void);
@@ -23,15 +26,13 @@ typedef int (*Indenter)(void);
#define PUT_LINE_FORWARD 32 // put linewise register below Visual sel.
#define PUT_BLOCK_INNER 64 // in block mode, do not add trailing spaces
-/*
- * Registers:
- * 0 = register for latest (unnamed) yank
- * 1..9 = registers '1' to '9', for deletes
- * 10..35 = registers 'a' to 'z'
- * 36 = delete register '-'
- * 37 = selection register '*'
- * 38 = clipboard register '+'
- */
+// Registers:
+// 0 = register for latest (unnamed) yank
+// 1..9 = registers '1' to '9', for deletes
+// 10..35 = registers 'a' to 'z'
+// 36 = delete register '-'
+// 37 = selection register '*'
+// 38 = clipboard register '+'
#define DELETION_REGISTER 36
#define NUM_SAVED_REGISTERS 37
// The following registers should not be saved in ShaDa file:
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 0acfc5c261..1e7cd62d22 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -21,37 +21,46 @@
#define IN_OPTION_C
#include <assert.h>
+#include <ctype.h>
#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include "nvim/arglist.h"
+#include "auto/config.h"
+#include "nvim/api/private/defs.h"
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
+#include "nvim/change.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand.h"
#include "nvim/cursor_shape.h"
#include "nvim/decoration_provider.h"
#include "nvim/diff.h"
#include "nvim/drawscreen.h"
-#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
-#include "nvim/ex_cmds2.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
#include "nvim/ex_session.h"
-#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/garray.h"
-#include "nvim/getchar.h"
-#include "nvim/hardcopy.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/grid_defs.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
+#include "nvim/insexpand.h"
#include "nvim/keycodes.h"
+#include "nvim/locale.h"
+#include "nvim/log.h"
#include "nvim/macros.h"
#include "nvim/mapping.h"
#include "nvim/mbyte.h"
@@ -64,60 +73,49 @@
#include "nvim/normal.h"
#include "nvim/ops.h"
#include "nvim/option.h"
+#include "nvim/option_defs.h"
#include "nvim/optionstr.h"
#include "nvim/os/os.h"
-#include "nvim/os_unix.h"
#include "nvim/path.h"
#include "nvim/popupmenu.h"
+#include "nvim/pos.h"
#include "nvim/regexp.h"
+#include "nvim/runtime.h"
#include "nvim/screen.h"
+#include "nvim/search.h"
+#include "nvim/sign_defs.h"
#include "nvim/spell.h"
#include "nvim/spellfile.h"
#include "nvim/spellsuggest.h"
#include "nvim/strings.h"
-#include "nvim/syntax.h"
+#include "nvim/tag.h"
+#include "nvim/terminal.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
-#include "nvim/ui_compositor.h"
#include "nvim/undo.h"
#include "nvim/vim.h"
#include "nvim/window.h"
-#ifdef WIN32
+#ifdef MSWIN
# include "nvim/os/pty_conpty_win.h"
#endif
#include "nvim/api/extmark.h"
#include "nvim/api/private/helpers.h"
-#include "nvim/api/vim.h"
#include "nvim/lua/executor.h"
#include "nvim/os/input.h"
#include "nvim/os/lang.h"
-/*
- * The options that are local to a window or buffer have "indir" set to one of
- * these values. Special values:
- * PV_NONE: global option.
- * PV_WIN is added: window-local option
- * PV_BUF is added: buffer-local option
- * PV_BOTH is added: global option which also has a local value.
- */
-#define PV_BOTH 0x1000
-#define PV_WIN 0x2000
-#define PV_BUF 0x4000
-#define PV_MASK 0x0fff
-#define OPT_WIN(x) (idopt_T)(PV_WIN + (int)(x))
-#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,
- PV_MAXVAL = 0xffff, // to avoid warnings for value out of range
-} idopt_T;
-
-/*
- * Options local to a window have a value local to a buffer and global to all
- * buffers. Indicate this by setting "var" to VAR_WIN.
- */
-#define VAR_WIN ((char_u *)-1)
+static char e_unknown_option[]
+ = N_("E518: Unknown option");
+static char e_not_allowed_in_modeline[]
+ = N_("E520: Not allowed in a modeline");
+static char e_not_allowed_in_modeline_when_modelineexpr_is_off[]
+ = N_("E992: Not allowed in a modeline when 'modelineexpr' is off");
+static char e_key_code_not_set[]
+ = N_("E846: Key code not set");
+static char e_number_required_after_equal[]
+ = N_("E521: Number required after =");
+static char e_preview_window_already_exists[]
+ = N_("E590: A preview window already exists");
static char *p_term = NULL;
static char *p_ttytype = NULL;
@@ -134,29 +132,14 @@ static int p_et_nopaste;
static long p_sts_nopaste;
static long p_tw_nopaste;
static long p_wm_nopaste;
-static char_u *p_vsts_nopaste;
-
-typedef struct vimoption {
- char *fullname; // full option name
- char *shortname; // permissible abbreviation
- uint32_t flags; // see below
- char_u *var; // global option: pointer to variable;
- // window-local option: VAR_WIN;
- // buffer-local option: global value
- idopt_T indir; // global option: PV_NONE;
- // local option: indirect option index
- char_u *def_val; // default values for variable (neovim!!)
- LastSet last_set; // script in which the option was last set
-} vimoption_T;
-
-/*
- * options[] is initialized here.
- * The order of the options MUST be alphabetic for ":set all" and findoption().
- * All option names MUST start with a lowercase letter (for findoption()).
- * Exception: "t_" options are at the end.
- * The options with a NULL variable are 'hidden': a set command for them is
- * ignored and they are not printed.
- */
+static char *p_vsts_nopaste;
+
+// options[] is initialized here.
+// The order of the options MUST be alphabetic for ":set all" and findoption().
+// All option names MUST start with a lowercase letter (for findoption()).
+// Exception: "t_" options are at the end.
+// The options with a NULL variable are 'hidden': a set command for them is
+// ignored and they are not printed.
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "options.generated.h"
@@ -164,10 +147,24 @@ typedef struct vimoption {
#define OPTION_COUNT ARRAY_SIZE(options)
+typedef enum {
+ OP_NONE = 0,
+ OP_ADDING, ///< "opt+=arg"
+ OP_PREPENDING, ///< "opt^=arg"
+ OP_REMOVING, ///< "opt-=arg"
+} set_op_T;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "option.c.generated.h"
#endif
+void set_init_tablocal(void)
+{
+ // susy baka: cmdheight calls itself OPT_GLOBAL but is really tablocal!
+ int ch_idx = findoption("cmdheight");
+ p_ch = (long)options[ch_idx].def_val;
+}
+
/// Initialize the options, first part.
///
/// Called only once from main(), just after creating the first buffer.
@@ -178,14 +175,10 @@ typedef struct vimoption {
/// editor state initialized here. Do logging in set_init_2 or later.
void set_init_1(bool clean_arg)
{
- int opt_idx;
-
langmap_init();
- /*
- * Find default value for 'shell' option.
- * Don't use it if it is empty.
- */
+ // Find default value for 'shell' option.
+ // Don't use it if it is empty.
{
const char *shell = os_getenv("SHELL");
if (shell != NULL) {
@@ -200,10 +193,8 @@ void set_init_1(bool clean_arg)
}
}
- /*
- * Set the default for 'backupskip' to include environment variables for
- * temp files.
- */
+ // Set the default for 'backupskip' to include environment variables for
+ // temp files.
{
#ifdef UNIX
static char *(names[4]) = { "", "TMPDIR", "TEMP", "TMP" };
@@ -211,7 +202,7 @@ void set_init_1(bool clean_arg)
static char *(names[3]) = { "TMPDIR", "TEMP", "TMP" };
#endif
garray_T ga;
- opt_idx = findoption("backupskip");
+ int opt_idx = findoption("backupskip");
ga_init(&ga, 1, 100);
for (size_t n = 0; n < ARRAY_SIZE(names); n++) {
@@ -225,7 +216,7 @@ void set_init_1(bool clean_arg)
p = "/tmp";
# endif
mustfree = false;
- } else
+ } else // NOLINT(readability/braces)
#endif
{
p = vim_getenv(names[n]);
@@ -237,7 +228,7 @@ void set_init_1(bool clean_arg)
xstrlcpy(item, p, len);
add_pathsep(item);
xstrlcat(item, "*", len);
- if (find_dup_item(ga.ga_data, (char_u *)item, options[opt_idx].flags)
+ if (find_dup_item(ga.ga_data, item, options[opt_idx].flags)
== NULL) {
ga_grow(&ga, (int)len);
if (!GA_EMPTY(&ga)) {
@@ -260,19 +251,14 @@ void set_init_1(bool clean_arg)
}
{
- char_u *cdpath;
- char_u *buf;
- int i;
- int j;
-
// Initialize the 'cdpath' option's default value.
- cdpath = (char_u *)vim_getenv("CDPATH");
+ char *cdpath = vim_getenv("CDPATH");
if (cdpath != NULL) {
- buf = xmalloc(2 * STRLEN(cdpath) + 2);
+ char *buf = xmalloc(2 * strlen(cdpath) + 2);
{
buf[0] = ','; // start with ",", current dir first
- j = 1;
- for (i = 0; cdpath[i] != NUL; i++) {
+ int j = 1;
+ for (int i = 0; cdpath[i] != NUL; i++) {
if (vim_ispathlistsep(cdpath[i])) {
buf[j++] = ',';
} else {
@@ -283,7 +269,7 @@ void set_init_1(bool clean_arg)
}
}
buf[j] = NUL;
- opt_idx = findoption("cdpath");
+ int opt_idx = findoption("cdpath");
if (opt_idx >= 0) {
options[opt_idx].def_val = buf;
options[opt_idx].flags |= P_DEF_ALLOCED;
@@ -295,28 +281,6 @@ void set_init_1(bool clean_arg)
}
}
-#if defined(MSWIN) || defined(MAC)
- // Set print encoding on platforms that don't default to latin1
- set_string_default("printencoding", "hp-roman8", false);
-#endif
-
- // 'printexpr' must be allocated to be able to evaluate it.
- set_string_default("printexpr",
-#ifdef UNIX
- "system(['lpr'] "
- "+ (empty(&printdevice)?[]:['-P', &printdevice]) "
- "+ [v:fname_in])"
- ". delete(v:fname_in)"
- "+ v:shell_error",
-#elif defined(MSWIN)
- "system(['copy', v:fname_in, "
- "empty(&printdevice)?'LPT1':&printdevice])"
- ". delete(v:fname_in)",
-#else
- "",
-#endif
- false);
-
char *backupdir = stdpaths_user_state_subpath("backup", 2, true);
const size_t backupdir_len = strlen(backupdir);
backupdir = xrealloc(backupdir, backupdir_len + 3);
@@ -339,10 +303,8 @@ void set_init_1(bool clean_arg)
rtp = NULL; // ownership taken
}
- /*
- * Set all the options (except the terminal options) to their default
- * value. Also set the global value for local options.
- */
+ // Set all the options (except the terminal options) to their default
+ // value. Also set the global value for local options.
set_options_default(0);
curbuf->b_p_initialized = true;
@@ -365,34 +327,32 @@ void set_init_1(bool clean_arg)
// didset_options() because it only depends on 'encoding'.
init_spell_chartab();
- /*
- * Expand environment variables and things like "~" for the defaults.
- * If option_expand() returns non-NULL the variable is expanded. This can
- * only happen for non-indirect options.
- * Also set the default to the expanded value, so ":set" does not list
- * them.
- * Don't set the P_ALLOCED flag, because we don't want to free the
- * default.
- */
- for (opt_idx = 0; options[opt_idx].fullname; opt_idx++) {
- if (options[opt_idx].flags & P_NO_DEF_EXP) {
+ // Expand environment variables and things like "~" for the defaults.
+ // If option_expand() returns non-NULL the variable is expanded. This can
+ // only happen for non-indirect options.
+ // Also set the default to the expanded value, so ":set" does not list
+ // them.
+ // Don't set the P_ALLOCED flag, because we don't want to free the
+ // default.
+ for (int opt_idx = 0; options[opt_idx].fullname; opt_idx++) {
+ vimoption_T *opt = &options[opt_idx];
+ if (opt->flags & P_NO_DEF_EXP) {
continue;
}
char *p;
- if ((options[opt_idx].flags & P_GETTEXT)
- && options[opt_idx].var != NULL) {
- p = _(*(char **)options[opt_idx].var);
+ if ((opt->flags & P_GETTEXT) && opt->var != NULL) {
+ p = _(*(char **)opt->var);
} else {
- p = (char *)option_expand(opt_idx, NULL);
+ p = option_expand(opt_idx, NULL);
}
if (p != NULL) {
p = xstrdup(p);
- *(char **)options[opt_idx].var = p;
- if (options[opt_idx].flags & P_DEF_ALLOCED) {
- xfree(options[opt_idx].def_val);
+ *(char **)opt->var = p;
+ if (opt->flags & P_DEF_ALLOCED) {
+ xfree(opt->def_val);
}
- options[opt_idx].def_val = (char_u *)p;
- options[opt_idx].flags |= P_DEF_ALLOCED;
+ opt->def_val = p;
+ opt->flags |= P_DEF_ALLOCED;
}
}
@@ -404,7 +364,7 @@ void set_init_1(bool clean_arg)
// NOTE: mlterm's author is being asked to 'set' a variable
// instead of an environment variable due to inheritance.
if (os_env_exists("MLTERM")) {
- set_option_value("tbidi", 1L, NULL, 0);
+ set_option_value_give_err("tbidi", 1L, NULL, 0);
}
didset_options2();
@@ -414,17 +374,17 @@ void set_init_1(bool clean_arg)
// enc_locale() will try to find the encoding of the current locale.
// This will be used when 'default' is used as encoding specifier
// in 'fileencodings'
- char_u *p = enc_locale();
+ char *p = enc_locale();
if (p == NULL) {
// use utf-8 as 'default' if locale encoding can't be detected.
- p = (char_u *)xmemdupz(S_LEN("utf-8"));
+ p = xmemdupz(S_LEN("utf-8"));
}
fenc_default = p;
#ifdef HAVE_WORKING_LIBINTL
// GNU gettext 0.10.37 supports this feature: set the codeset used for
// translated messages independently from the current locale.
- (void)bind_textdomain_codeset(PROJECT_NAME, (char *)p_enc);
+ (void)bind_textdomain_codeset(PROJECT_NAME, p_enc);
#endif
// Set the default for 'helplang'.
@@ -435,32 +395,32 @@ void set_init_1(bool clean_arg)
/// This does not take care of side effects!
///
/// @param opt_flags OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL
-static void set_option_default(int opt_idx, int opt_flags)
+static void set_option_default(const int opt_idx, int opt_flags)
{
- char_u *varp; // pointer to variable for current option
int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
- varp = get_varp_scope(&(options[opt_idx]), both ? OPT_LOCAL : opt_flags);
- uint32_t flags = options[opt_idx].flags;
+ // pointer to variable for current option
+ vimoption_T *opt = &options[opt_idx];
+ char_u *varp = (char_u *)get_varp_scope(opt, both ? OPT_LOCAL : opt_flags);
+ uint32_t flags = opt->flags;
if (varp != NULL) { // skip hidden option, nothing to do for it
if (flags & P_STRING) {
// Use set_string_option_direct() for local options to handle
// freeing and allocating the value.
- if (options[opt_idx].indir != PV_NONE) {
- set_string_option_direct(NULL, opt_idx,
- (char *)options[opt_idx].def_val, opt_flags, 0);
+ if (opt->indir != PV_NONE) {
+ set_string_option_direct(NULL, opt_idx, opt->def_val, opt_flags, 0);
} else {
if ((opt_flags & OPT_FREE) && (flags & P_ALLOCED)) {
- free_string_option(*(char_u **)(varp));
+ free_string_option(*(char **)(varp));
}
- *(char_u **)varp = options[opt_idx].def_val;
- options[opt_idx].flags &= ~P_ALLOCED;
+ *(char **)varp = opt->def_val;
+ opt->flags &= ~P_ALLOCED;
}
} else if (flags & P_NUM) {
- if (options[opt_idx].indir == PV_SCROLL) {
+ if (opt->indir == PV_SCROLL) {
win_comp_scroll(curwin);
} else {
- long def_val = (long)options[opt_idx].def_val;
+ long def_val = (long)opt->def_val;
if ((long *)varp == &curwin->w_p_so
|| (long *)varp == &curwin->w_p_siso) {
// 'scrolloff' and 'sidescrolloff' local values have a
@@ -471,21 +431,21 @@ static void set_option_default(int opt_idx, int opt_flags)
}
// May also set global value for local option.
if (both) {
- *(long *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) =
+ *(long *)get_varp_scope(opt, OPT_GLOBAL) =
def_val;
}
}
} else { // P_BOOL
- *(int *)varp = (int)(intptr_t)options[opt_idx].def_val;
+ *(int *)varp = (int)(intptr_t)opt->def_val;
#ifdef UNIX
// 'modeline' defaults to off for root
- if (options[opt_idx].indir == PV_ML && getuid() == ROOT_UID) {
+ if (opt->indir == PV_ML && getuid() == ROOT_UID) {
*(int *)varp = false;
}
#endif
// May also set global value for local option.
if (both) {
- *(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) =
+ *(int *)get_varp_scope(opt, OPT_GLOBAL) =
*(int *)varp;
}
}
@@ -528,32 +488,31 @@ static void set_string_default(const char *name, char *val, bool allocated)
{
int opt_idx = findoption(name);
if (opt_idx >= 0) {
- if (options[opt_idx].flags & P_DEF_ALLOCED) {
- xfree(options[opt_idx].def_val);
+ vimoption_T *opt = &options[opt_idx];
+ if (opt->flags & P_DEF_ALLOCED) {
+ xfree(opt->def_val);
}
- options[opt_idx].def_val = allocated
- ? (char_u *)val
- : (char_u *)xstrdup(val);
- options[opt_idx].flags |= P_DEF_ALLOCED;
+ opt->def_val = allocated ? val : xstrdup(val);
+ opt->flags |= P_DEF_ALLOCED;
}
}
-// For an option value that contains comma separated items, find "newval" in
-// "origval". Return NULL if not found.
-static char_u *find_dup_item(char_u *origval, const char_u *newval, uint32_t flags)
+/// For an option value that contains comma separated items, find "newval" in
+/// "origval". Return NULL if not found.
+static char *find_dup_item(char *origval, const char *newval, uint32_t flags)
FUNC_ATTR_NONNULL_ARG(2)
{
- int bs = 0;
-
if (origval == NULL) {
return NULL;
}
- const size_t newlen = STRLEN(newval);
- for (char_u *s = origval; *s != NUL; s++) {
+ int bs = 0;
+
+ const size_t newlen = strlen(newval);
+ for (char *s = origval; *s != NUL; s++) {
if ((!(flags & P_COMMA) || s == origval || (s[-1] == ',' && !(bs & 1)))
- && STRNCMP(s, newval, newlen) == 0
+ && strncmp(s, newval, newlen) == 0
&& (!(flags & P_COMMA) || s[newlen] == ',' || s[newlen] == NUL)) {
return s;
}
@@ -574,11 +533,9 @@ static char_u *find_dup_item(char_u *origval, const char_u *newval, uint32_t fla
/// Used for 'lines' and 'columns'.
void set_number_default(char *name, long val)
{
- int opt_idx;
-
- opt_idx = findoption(name);
+ int opt_idx = findoption(name);
if (opt_idx >= 0) {
- options[opt_idx].def_val = (char_u *)(intptr_t)val;
+ options[opt_idx].def_val = (char *)(intptr_t)val;
}
}
@@ -590,17 +547,18 @@ void free_all_options(void)
if (options[i].indir == PV_NONE) {
// global option: free value and default value.
if ((options[i].flags & P_ALLOCED) && options[i].var != NULL) {
- free_string_option(*(char_u **)options[i].var);
+ free_string_option(*(char **)options[i].var);
}
if (options[i].flags & P_DEF_ALLOCED) {
free_string_option(options[i].def_val);
}
} else if (options[i].var != VAR_WIN && (options[i].flags & P_STRING)) {
// buffer-local option: free global value
- clear_string_option((char_u **)options[i].var);
+ clear_string_option((char **)options[i].var);
}
}
free_operatorfunc_option();
+ free_tagfunc_option();
}
#endif
@@ -610,25 +568,20 @@ void set_init_2(bool headless)
// set in set_init_1 but logging is not allowed there
ILOG("startup runtimepath/packpath value: %s", p_rtp);
- int idx;
-
// 'scroll' defaults to half the window height. The stored default is zero,
// which results in the actual value computed from the window height.
- idx = findoption("scroll");
+ int idx = findoption("scroll");
if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) {
set_option_default(idx, OPT_LOCAL);
}
comp_col();
- /*
- * 'window' is only for backwards compatibility with Vi.
- * Default is Rows - 1.
- */
+ // 'window' is only for backwards compatibility with Vi.
+ // Default is Rows - 1.
if (!option_was_set("window")) {
p_window = Rows - 1;
}
set_number_default("window", Rows - 1);
- (void)parse_printoptions(); // parse 'printoptions' default value
}
/// Initialize the options, part three: After reading the .vimrc
@@ -649,41 +602,41 @@ void set_init_3(void)
: !(options[idx_sp].flags & P_WAS_SET);
size_t len = 0;
- char_u *p = (char_u *)invocation_path_tail(p_sh, &len);
- p = vim_strnsave(p, len);
+ char *p = (char *)invocation_path_tail(p_sh, &len);
+ p = xstrnsave(p, len);
{
//
// 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 (path_fnamecmp(p, "csh") == 0
+ || path_fnamecmp(p, "tcsh") == 0) {
if (do_sp) {
- p_sp = (char_u *)"|& tee";
+ p_sp = "|& tee";
options[idx_sp].def_val = p_sp;
}
if (do_srr) {
- p_srr = (char_u *)">&";
+ p_srr = ">&";
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 (path_fnamecmp(p, "sh") == 0
+ || path_fnamecmp(p, "ksh") == 0
+ || path_fnamecmp(p, "mksh") == 0
+ || path_fnamecmp(p, "pdksh") == 0
+ || path_fnamecmp(p, "zsh") == 0
+ || path_fnamecmp(p, "zsh-beta") == 0
+ || path_fnamecmp(p, "bash") == 0
+ || path_fnamecmp(p, "fish") == 0
+ || path_fnamecmp(p, "ash") == 0
+ || path_fnamecmp(p, "dash") == 0) {
// Always use POSIX shell style redirection if we reach this
if (do_sp) {
- p_sp = (char_u *)"2>&1| tee";
+ p_sp = "2>&1| tee";
options[idx_sp].def_val = p_sp;
}
if (do_srr) {
- p_srr = (char_u *)">%s 2>&1";
+ p_srr = ">%s 2>&1";
options[idx_srr].def_val = p_srr;
}
}
@@ -715,23 +668,25 @@ void set_helplang_default(const char *lang)
return;
}
int idx = findoption("hlg");
- if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) {
- if (options[idx].flags & P_ALLOCED) {
- free_string_option(p_hlg);
- }
- p_hlg = (char_u *)xmemdupz(lang, lang_len);
- // zh_CN becomes "cn", zh_TW becomes "tw".
- if (STRNICMP(p_hlg, "zh_", 3) == 0 && STRLEN(p_hlg) >= 5) {
- p_hlg[0] = (char_u)TOLOWER_ASC(p_hlg[3]);
- p_hlg[1] = (char_u)TOLOWER_ASC(p_hlg[4]);
- } else if (STRLEN(p_hlg) >= 1 && *p_hlg == 'C') {
- // any C like setting, such as C.UTF-8, becomes "en"
- p_hlg[0] = 'e';
- p_hlg[1] = 'n';
- }
- p_hlg[2] = NUL;
- options[idx].flags |= P_ALLOCED;
+ if (idx < 0 || (options[idx].flags & P_WAS_SET)) {
+ return;
}
+
+ if (options[idx].flags & P_ALLOCED) {
+ free_string_option(p_hlg);
+ }
+ p_hlg = xmemdupz(lang, lang_len);
+ // zh_CN becomes "cn", zh_TW becomes "tw".
+ if (STRNICMP(p_hlg, "zh_", 3) == 0 && strlen(p_hlg) >= 5) {
+ p_hlg[0] = (char)TOLOWER_ASC(p_hlg[3]);
+ p_hlg[1] = (char)TOLOWER_ASC(p_hlg[4]);
+ } else if (strlen(p_hlg) >= 1 && *p_hlg == 'C') {
+ // any C like setting, such as C.UTF-8, becomes "en"
+ p_hlg[0] = 'e';
+ p_hlg[1] = 'n';
+ }
+ p_hlg[2] = NUL;
+ options[idx].flags |= P_ALLOCED;
}
/// 'title' and 'icon' only default to true if they have not been set or reset
@@ -741,21 +696,17 @@ void set_helplang_default(const char *lang)
/// machine.
void set_title_defaults(void)
{
- int idx1;
-
- /*
- * If GUI is (going to be) used, we can always set the window title and
- * icon name. Saves a bit of time, because the X11 display server does
- * not need to be contacted.
- */
- idx1 = findoption("title");
+ // If GUI is (going to be) used, we can always set the window title and
+ // icon name. Saves a bit of time, because the X11 display server does
+ // not need to be contacted.
+ int idx1 = findoption("title");
if (idx1 >= 0 && !(options[idx1].flags & P_WAS_SET)) {
- options[idx1].def_val = (char_u *)(intptr_t)0;
+ options[idx1].def_val = 0;
p_title = 0;
}
idx1 = findoption("icon");
if (idx1 >= 0 && !(options[idx1].flags & P_WAS_SET)) {
- options[idx1].def_val = (char_u *)(intptr_t)0;
+ options[idx1].def_val = 0;
p_icon = 0;
}
}
@@ -775,6 +726,342 @@ void ex_set(exarg_T *eap)
(void)do_set(eap->arg, flags);
}
+/// Part of do_set() for string options.
+/// @return FAIL on failure, do not process further options.
+static int do_set_string(int opt_idx, int opt_flags, char **argp, int nextchar, set_op_T op_arg,
+ uint32_t flags, char *varp_arg, char *errbuf, size_t errbuflen,
+ int *value_checked, char **errmsg)
+{
+ char *arg = *argp;
+ set_op_T op = op_arg;
+ char *varp = varp_arg;
+ char *save_arg = NULL;
+ char *s = NULL;
+ char_u *origval_l = NULL;
+ char_u *origval_g = NULL;
+ char whichwrap[80];
+
+ // When using ":set opt=val" for a global option
+ // with a local value the local value will be
+ // reset, use the global value here.
+ if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0
+ && ((int)options[opt_idx].indir & PV_BOTH)) {
+ varp = (char *)options[opt_idx].var;
+ }
+
+ // The old value is kept until we are sure that the new value is valid.
+ char *oldval = *(char **)varp;
+
+ if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
+ origval_l = *(char_u **)get_varp_scope(&(options[opt_idx]), OPT_LOCAL);
+ origval_g = *(char_u **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL);
+
+ // A global-local string option might have an empty option as value to
+ // indicate that the global value should be used.
+ if (((int)options[opt_idx].indir & PV_BOTH) && origval_l == (char_u *)empty_option) {
+ origval_l = origval_g;
+ }
+ }
+
+ char *origval;
+ // When setting the local value of a global option, the old value may be
+ // the global value.
+ if (((int)options[opt_idx].indir & PV_BOTH) && (opt_flags & OPT_LOCAL)) {
+ origval = *(char **)get_varp(&options[opt_idx]);
+ } else {
+ origval = oldval;
+ }
+
+ char *newval;
+ if (nextchar == '&') { // set to default val
+ newval = options[opt_idx].def_val;
+ // expand environment variables and ~ since the default value was
+ // already expanded, only required when an environment variable was set
+ // later
+ if (newval == NULL) {
+ newval = empty_option;
+ } else if (!(options[opt_idx].flags & P_NO_DEF_EXP)) {
+ s = option_expand(opt_idx, newval);
+ if (s == NULL) {
+ s = newval;
+ }
+ newval = xstrdup(s);
+ } else {
+ newval = xstrdup(newval);
+ }
+ } else if (nextchar == '<') { // set to global val
+ newval = xstrdup(*(char **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL));
+ } else {
+ arg++; // jump to after the '=' or ':'
+
+ // Set 'keywordprg' to ":help" if an empty
+ // value was passed to :set by the user.
+ if (varp == (char *)&p_kp && (*arg == NUL || *arg == ' ')) {
+ save_arg = arg;
+ arg = ":help";
+ } else if (varp == (char *)&p_bs && ascii_isdigit(**(char_u **)varp)) {
+ // Convert 'backspace' number to string, for
+ // adding, prepending and removing string.
+ int i = getdigits_int((char **)varp, true, 0);
+ switch (i) {
+ case 0:
+ *(char **)varp = empty_option;
+ break;
+ case 1:
+ *(char_u **)varp = (char_u *)xstrdup("indent,eol");
+ break;
+ case 2:
+ *(char_u **)varp = (char_u *)xstrdup("indent,eol,start");
+ break;
+ case 3:
+ *(char_u **)varp = (char_u *)xstrdup("indent,eol,nostop");
+ break;
+ }
+ xfree(oldval);
+ if (origval == oldval) {
+ origval = *(char **)varp;
+ }
+ if (origval_l == (char_u *)oldval) {
+ origval_l = *(char_u **)varp;
+ }
+ if (origval_g == (char_u *)oldval) {
+ origval_g = *(char_u **)varp;
+ }
+ oldval = *(char **)varp;
+ } else if (varp == (char *)&p_ww && ascii_isdigit(*arg)) {
+ // Convert 'whichwrap' number to string, for backwards compatibility
+ // with Vim 3.0.
+ *whichwrap = NUL;
+ int i = getdigits_int(&arg, true, 0);
+ if (i & 1) {
+ xstrlcat(whichwrap, "b,", sizeof(whichwrap));
+ }
+ if (i & 2) {
+ xstrlcat(whichwrap, "s,", sizeof(whichwrap));
+ }
+ if (i & 4) {
+ xstrlcat(whichwrap, "h,l,", sizeof(whichwrap));
+ }
+ if (i & 8) {
+ xstrlcat(whichwrap, "<,>,", sizeof(whichwrap));
+ }
+ if (i & 16) {
+ xstrlcat(whichwrap, "[,],", sizeof(whichwrap));
+ }
+ if (*whichwrap != NUL) { // remove trailing ,
+ whichwrap[strlen(whichwrap) - 1] = NUL;
+ }
+ save_arg = arg;
+ arg = whichwrap;
+ } else if (*arg == '>' && (varp == (char *)&p_dir || varp == (char *)&p_bdir)) {
+ // Remove '>' before 'dir' and 'bdir', for backwards compatibility with
+ // version 3.0
+ arg++;
+ }
+
+ // Copy the new string into allocated memory.
+ // Can't use set_string_option_direct(), because we need to remove the
+ // backslashes.
+
+ // get a bit too much
+ size_t newlen = strlen(arg) + 1;
+ if (op != OP_NONE) {
+ newlen += strlen(origval) + 1;
+ }
+ newval = xmalloc(newlen);
+ s = newval;
+
+ // Copy the string, skip over escaped chars.
+ // For MS-Windows backslashes before normal file name characters
+ // are not removed, and keep backslash at start, for "\\machine\path",
+ // but do remove it for "\\\\machine\\path".
+ // The reverse is found in ExpandOldSetting().
+ while (*arg != NUL && !ascii_iswhite(*arg)) {
+ if (*arg == '\\' && arg[1] != NUL
+#ifdef BACKSLASH_IN_FILENAME
+ && !((flags & P_EXPAND)
+ && vim_isfilec((uint8_t)arg[1])
+ && !ascii_iswhite(arg[1])
+ && (arg[1] != '\\'
+ || (s == newval && arg[2] != '\\')))
+#endif
+ ) {
+ arg++; // remove backslash
+ }
+ int i = utfc_ptr2len(arg);
+ if (i > 1) {
+ // copy multibyte char
+ memmove(s, arg, (size_t)i);
+ arg += i;
+ s += i;
+ } else {
+ *s++ = *arg++;
+ }
+ }
+ *s = NUL;
+
+ // Expand environment variables and ~.
+ // Don't do it when adding without inserting a comma.
+ if (op == OP_NONE || (flags & P_COMMA)) {
+ s = option_expand(opt_idx, newval);
+ if (s != NULL) {
+ xfree(newval);
+ newlen = (unsigned)strlen(s) + 1;
+ if (op != OP_NONE) {
+ newlen += (unsigned)strlen(origval) + 1;
+ }
+ newval = xmalloc(newlen);
+ STRCPY(newval, s);
+ }
+ }
+
+ // locate newval[] in origval[] when removing it
+ // and when adding to avoid duplicates
+ int len = 0;
+ if (op == OP_REMOVING || (flags & P_NODUP)) {
+ len = (int)strlen(newval);
+ s = find_dup_item(origval, newval, flags);
+
+ // do not add if already there
+ if ((op == OP_ADDING || op == OP_PREPENDING) && s != NULL) {
+ op = OP_NONE;
+ STRCPY(newval, origval);
+ }
+
+ // if no duplicate, move pointer to end of original value
+ if (s == NULL) {
+ s = origval + (int)strlen(origval);
+ }
+ }
+
+ // concatenate the two strings; add a ',' if needed
+ if (op == OP_ADDING || op == OP_PREPENDING) {
+ int comma = ((flags & P_COMMA) && *origval != NUL && *newval != NUL);
+ if (op == OP_ADDING) {
+ len = (int)strlen(origval);
+ // Strip a trailing comma, would get 2.
+ if (comma && len > 1
+ && (flags & P_ONECOMMA) == P_ONECOMMA
+ && origval[len - 1] == ','
+ && origval[len - 2] != '\\') {
+ len--;
+ }
+ memmove(newval + len + comma, newval, strlen(newval) + 1);
+ memmove(newval, origval, (size_t)len);
+ } else {
+ len = (int)strlen(newval);
+ STRMOVE(newval + len + comma, origval);
+ }
+ if (comma) {
+ newval[len] = ',';
+ }
+ }
+
+ // Remove newval[] from origval[]. (Note: "len" has been set above and
+ // is used here).
+ if (op == OP_REMOVING) {
+ STRCPY(newval, origval);
+ if (*s) {
+ // may need to remove a comma
+ if (flags & P_COMMA) {
+ if (s == origval) {
+ // include comma after string
+ if (s[len] == ',') {
+ len++;
+ }
+ } else {
+ // include comma before string
+ s--;
+ len++;
+ }
+ }
+ STRMOVE(newval + (s - origval), s + len);
+ }
+ }
+
+ if (flags & P_FLAGLIST) {
+ // Remove flags that appear twice.
+ for (s = newval; *s;) {
+ // if options have P_FLAGLIST and P_ONECOMMA such as
+ // 'whichwrap'
+ if (flags & P_ONECOMMA) {
+ if (*s != ',' && *(s + 1) == ','
+ && vim_strchr(s + 2, (uint8_t)(*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, (uint8_t)(*s)) != NULL) {
+ STRMOVE(s, s + 1);
+ continue;
+ }
+ }
+ s++;
+ }
+ }
+
+ if (save_arg != NULL) {
+ arg = save_arg; // arg was temporarily changed, restore it
+ }
+ }
+
+ // Set the new value.
+ *(char_u **)(varp) = (char_u *)newval;
+
+ // origval may be freed by did_set_string_option(), make a copy.
+ char *saved_origval = (origval != NULL) ? xstrdup(origval) : NULL;
+ char *saved_origval_l = (origval_l != NULL) ? xstrdup((char *)origval_l) : NULL;
+ char *saved_origval_g = (origval_g != NULL) ? xstrdup((char *)origval_g) : NULL;
+
+ // newval (and varp) may become invalid if the buffer is closed by
+ // autocommands.
+ char *saved_newval = (newval != NULL) ? xstrdup(newval) : NULL;
+
+ {
+ uint32_t *p = insecure_flag(curwin, opt_idx, opt_flags);
+ const int secure_saved = secure;
+
+ // When an option is set in the sandbox, from a modeline or in secure
+ // mode, then deal with side effects in secure mode. Also when the
+ // value was set with the P_INSECURE flag and is not completely
+ // replaced.
+ if ((opt_flags & OPT_MODELINE)
+ || sandbox != 0
+ || (op != OP_NONE && (*p & P_INSECURE))) {
+ secure = 1;
+ }
+
+ // Handle side effects, and set the global value for ":set" on local
+ // options. Note: when setting 'syntax' or 'filetype' autocommands may
+ // be triggered that can cause havoc.
+ *errmsg = did_set_string_option(opt_idx, (char **)varp, oldval,
+ errbuf, errbuflen,
+ opt_flags, value_checked);
+
+ secure = secure_saved;
+ }
+
+ if (*errmsg == NULL) {
+ if (!starting) {
+ trigger_optionset_string(opt_idx, opt_flags, saved_origval, saved_origval_l,
+ saved_origval_g, saved_newval);
+ }
+ if (options[opt_idx].flags & P_UI_OPTION) {
+ ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
+ STRING_OBJ(cstr_as_string(saved_newval)));
+ }
+ }
+ xfree(saved_origval);
+ xfree(saved_origval_l);
+ xfree(saved_origval_g);
+ xfree(saved_newval);
+
+ *argp = arg;
+ return *errmsg == NULL ? OK : FAIL;
+}
+
/// Parse 'arg' for option settings.
///
/// 'arg' may be IObuff, but only when no errors can be present and option
@@ -792,23 +1079,7 @@ void ex_set(exarg_T *eap)
/// @return FAIL if an error is detected, OK otherwise
int do_set(char *arg, int opt_flags)
{
- int opt_idx;
- char *errmsg;
- char errbuf[80];
- char_u *startarg;
- int prefix; // 1: nothing, 0: "no", 2: "inv" in front of name
- char_u nextchar; // next non-white char after option name
- int afterchar; // character just after option name
- int len;
- int i;
- varnumber_T value;
- int key;
- uint32_t flags; // flags for current option
- char *varp = NULL; // pointer to variable for current option
int did_show = false; // already showed one value
- int adding; // "opt+=arg"
- int prepending; // "opt^=arg"
- int removing; // "opt-=arg"
if (*arg == NUL) {
showoptions(0, opt_flags);
@@ -816,16 +1087,16 @@ int do_set(char *arg, int opt_flags)
goto theend;
}
+ char errbuf[80];
+
while (*arg != NUL) { // loop to process all options
- errmsg = NULL;
- startarg = (char_u *)arg; // remember for error message
+ char *errmsg = NULL;
+ char *startarg = arg; // remember for error message
- if (STRNCMP(arg, "all", 3) == 0 && !isalpha(arg[3])
+ if (strncmp(arg, "all", 3) == 0 && !ASCII_ISALPHA(arg[3])
&& !(opt_flags & OPT_MODELINE)) {
- /*
- * ":set all" show all options.
- * ":set all&" set all options to their default value.
- */
+ // ":set all" show all options.
+ // ":set all&" set all options to their default value.
arg += 3;
if (*arg == '&') {
arg++;
@@ -834,23 +1105,25 @@ int do_set(char *arg, int opt_flags)
didset_options();
didset_options2();
ui_refresh_options();
- redraw_all_later(CLEAR);
+ redraw_all_later(UPD_CLEAR);
} else {
showoptions(1, opt_flags);
did_show = true;
}
} else {
- prefix = 1;
- if (STRNCMP(arg, "no", 2) == 0) {
+ int prefix = 1; // 1: nothing, 0: "no", 2: "inv" in front of name
+ if (strncmp(arg, "no", 2) == 0) {
prefix = 0;
arg += 2;
- } else if (STRNCMP(arg, "inv", 3) == 0) {
+ } else if (strncmp(arg, "inv", 3) == 0) {
prefix = 2;
arg += 3;
}
// find end of name
- key = 0;
+ int key = 0;
+ int len;
+ int opt_idx;
if (*arg == '<') {
opt_idx = -1;
// look out for <t_>;>
@@ -871,7 +1144,7 @@ int do_set(char *arg, int opt_flags)
}
len++;
if (opt_idx == -1) {
- key = find_key_option((char_u *)arg + 1, true);
+ key = find_key_option(arg + 1, true);
}
} else {
len = 0;
@@ -885,54 +1158,55 @@ int do_set(char *arg, int opt_flags)
}
opt_idx = findoption_len((const char *)arg, (size_t)len);
if (opt_idx == -1) {
- key = find_key_option((char_u *)arg, false);
+ key = find_key_option(arg, false);
}
}
// remember character after option name
- afterchar = (uint8_t)arg[len];
+ int afterchar = (uint8_t)arg[len];
// skip white space, allow ":set ai ?"
while (ascii_iswhite(arg[len])) {
len++;
}
- adding = false;
- prepending = false;
- removing = false;
+ set_op_T op = OP_NONE;
if (arg[len] != NUL && arg[len + 1] == '=') {
if (arg[len] == '+') {
- adding = true; // "+="
+ op = OP_ADDING; // "+="
len++;
} else if (arg[len] == '^') {
- prepending = true; // "^="
+ op = OP_PREPENDING; // "^="
len++;
} else if (arg[len] == '-') {
- removing = true; // "-="
+ op = OP_REMOVING; // "-="
len++;
}
}
- nextchar = (uint8_t)arg[len];
+ char_u nextchar = (uint8_t)arg[len]; // next non-white char after option name
if (opt_idx == -1 && key == 0) { // found a mismatch: skip
- errmsg = N_("E518: Unknown option");
+ errmsg = e_unknown_option;
goto skip;
}
+ uint32_t flags; // flags for current option
+ char *varp = NULL; // pointer to variable for current option
+
if (opt_idx >= 0) {
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("=:!&<", nextchar) == NULL
+ if (vim_strchr("=:!&<", (uint8_t)nextchar) == NULL
&& (!(options[opt_idx].flags & P_BOOL)
|| nextchar == '?')) {
- errmsg = _(e_unsupportedoption);
+ errmsg = e_unsupportedoption;
}
goto skip;
}
flags = options[opt_idx].flags;
- varp = (char *)get_varp_scope(&(options[opt_idx]), opt_flags);
+ varp = get_varp_scope(&(options[opt_idx]), opt_flags);
} else {
flags = P_STRING;
}
@@ -953,11 +1227,11 @@ int do_set(char *arg, int opt_flags)
// Disallow changing some options from modelines.
if (opt_flags & OPT_MODELINE) {
if (flags & (P_SECURE | P_NO_ML)) {
- errmsg = N_("E520: Not allowed in a modeline");
+ errmsg = e_not_allowed_in_modeline;
goto skip;
}
if ((flags & P_MLE) && !p_mle) {
- errmsg = N_("E992: Not allowed in a modeline when 'modelineexpr' is off");
+ errmsg = e_not_allowed_in_modeline_when_modelineexpr_is_off;
goto skip;
}
// In diff mode some options are overruled. This avoids that
@@ -977,7 +1251,7 @@ int do_set(char *arg, int opt_flags)
goto skip;
}
- if (vim_strchr("?=:!&<", nextchar) != NULL) {
+ if (vim_strchr("?=:!&<", (uint8_t)nextchar) != NULL) {
arg += len;
if (nextchar == '&' && arg[1] == 'v' && arg[2] == 'i') {
if (arg[3] == 'm') { // "opt&vim": set to Vim default
@@ -986,7 +1260,7 @@ int do_set(char *arg, int opt_flags)
arg += 2;
}
}
- if (vim_strchr("?!&<", nextchar) != NULL
+ if (vim_strchr("?!&<", (uint8_t)nextchar) != NULL
&& arg[1] != NUL && !ascii_iswhite(arg[1])) {
errmsg = e_trailing;
goto skip;
@@ -999,11 +1273,9 @@ int do_set(char *arg, int opt_flags)
//
if (nextchar == '?'
|| (prefix == 1
- && vim_strchr("=:&<", nextchar) == NULL
+ && vim_strchr("=:&<", (uint8_t)nextchar) == NULL
&& !(flags & P_BOOL))) {
- /*
- * print value
- */
+ // print value
if (did_show) {
msg_putchar('\n'); // cursor below last one
} else {
@@ -1025,7 +1297,7 @@ int do_set(char *arg, int opt_flags)
}
}
} else {
- errmsg = N_("E846: Key code not set");
+ errmsg = e_key_code_not_set;
goto skip;
}
if (nextchar != '?'
@@ -1033,8 +1305,8 @@ int do_set(char *arg, int opt_flags)
errmsg = e_trailing;
}
} else {
- int value_is_replaced = !prepending && !adding && !removing;
int value_checked = false;
+ varnumber_T value;
if (flags & P_BOOL) { // boolean
if (nextchar == '=' || nextchar == ':') {
@@ -1042,11 +1314,9 @@ int do_set(char *arg, int opt_flags)
goto skip;
}
- /*
- * ":set opt!": invert
- * ":set opt&": reset to default value
- * ":set opt<": reset to global value
- */
+ // ":set opt!": invert
+ // ":set opt&": reset to default value
+ // ":set opt<": reset to global value
if (nextchar == '!') {
value = *(int *)(varp) ^ 1;
} else if (nextchar == '&') {
@@ -1061,10 +1331,8 @@ int do_set(char *arg, int opt_flags)
OPT_GLOBAL);
}
} else {
- /*
- * ":set invopt": invert
- * ":set opt" or ":set noopt": set or reset
- */
+ // ":set invopt": invert
+ // ":set opt" or ":set noopt": set or reset
if (nextchar != NUL && !ascii_iswhite(afterchar)) {
errmsg = e_trailing;
goto skip;
@@ -1078,7 +1346,7 @@ int do_set(char *arg, int opt_flags)
errmsg = set_bool_option(opt_idx, (char_u *)varp, (int)value, opt_flags);
} else { // Numeric or string.
- if (vim_strchr("=:&<", nextchar) == NULL
+ if (vim_strchr("=:&<", (uint8_t)nextchar) == NULL
|| prefix != 1) {
errmsg = e_invarg;
goto skip;
@@ -1109,390 +1377,44 @@ int do_set(char *arg, int opt_flags)
|| *arg == '^'
|| (*arg != NUL && (!arg[1] || ascii_iswhite(arg[1]))
&& !ascii_isdigit(*arg)))) {
- value = string_to_key((char_u *)arg);
+ value = string_to_key(arg);
if (value == 0 && (long *)varp != &p_wcm) {
errmsg = e_invarg;
goto skip;
}
} else if (*arg == '-' || ascii_isdigit(*arg)) {
+ int i;
// Allow negative, octal and hex numbers.
- vim_str2nr((char_u *)arg, NULL, &i, STR2NR_ALL, &value, NULL, 0, true);
+ vim_str2nr(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 =");
+ errmsg = e_number_required_after_equal;
goto skip;
}
} else {
- errmsg = N_("E521: Number required after =");
+ errmsg = e_number_required_after_equal;
goto skip;
}
- if (adding) {
+ if (op == OP_ADDING) {
value = *(long *)varp + value;
}
- if (prepending) {
+ if (op == OP_PREPENDING) {
value = *(long *)varp * value;
}
- if (removing) {
+ if (op == OP_REMOVING) {
value = *(long *)varp - value;
}
errmsg = set_num_option(opt_idx, (char_u *)varp, (long)value,
errbuf, sizeof(errbuf),
opt_flags);
} else if (opt_idx >= 0) { // String.
- char_u *save_arg = NULL;
- char_u *s = NULL;
- char_u *oldval = NULL; // previous value if *varp
- char_u *newval;
- char_u *origval = NULL;
- char_u *origval_l = NULL;
- char_u *origval_g = NULL;
- char *saved_origval = NULL;
- char *saved_origval_l = NULL;
- char *saved_origval_g = NULL;
- char *saved_newval = NULL;
- unsigned newlen;
- int comma;
-
- // When using ":set opt=val" for a global option
- // with a local value the local value will be
- // reset, use the global value here.
- if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0
- && ((int)options[opt_idx].indir & PV_BOTH)) {
- varp = (char *)options[opt_idx].var;
- }
-
- // The old value is kept until we are sure that the
- // new value is valid.
- oldval = *(char_u **)varp;
-
- if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
- origval_l = *(char_u **)get_varp_scope(&(options[opt_idx]), OPT_LOCAL);
- origval_g = *(char_u **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL);
-
- // A global-local string option might have an empty
- // option as value to indicate that the global
- // value should be used.
- if (((int)options[opt_idx].indir & PV_BOTH) && origval_l == empty_option) {
- origval_l = origval_g;
- }
- }
-
- // When setting the local value of a global
- // option, the old value may be the global value.
- if (((int)options[opt_idx].indir & PV_BOTH) && (opt_flags & OPT_LOCAL)) {
- origval = *(char_u **)get_varp(&options[opt_idx]);
- } else {
- origval = oldval;
- }
-
- if (nextchar == '&') { // set to default val
- newval = options[opt_idx].def_val;
- // expand environment variables and ~ since the
- // default value was already expanded, only
- // required when an environment variable was set
- // later
- if (newval == NULL) {
- newval = empty_option;
- } else if (!(options[opt_idx].flags & P_NO_DEF_EXP)) {
- s = option_expand(opt_idx, newval);
- if (s == NULL) {
- s = newval;
- }
- newval = vim_strsave(s);
- } else {
- newval = (char_u *)xstrdup((char *)newval);
- }
- } else if (nextchar == '<') { // set to global val
- newval = vim_strsave(*(char_u **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL));
- } else {
- arg++; // jump to after the '=' or ':'
-
- // Set 'keywordprg' to ":help" if an empty
- // value was passed to :set by the user.
- // Misuse errbuf[] for the resulting string.
- if (varp == (char *)&p_kp && (*arg == NUL || *arg == ' ')) {
- STRCPY(errbuf, ":help");
- save_arg = (char_u *)arg;
- arg = errbuf;
- } else if (varp == (char *)&p_bs && ascii_isdigit(**(char_u **)varp)) {
- // Convert 'backspace' number to string, for
- // adding, prepending and removing string.
- i = getdigits_int((char **)varp, true, 0);
- switch (i) {
- case 0:
- *(char_u **)varp = empty_option;
- break;
- case 1:
- *(char_u **)varp = vim_strsave((char_u *)"indent,eol");
- break;
- case 2:
- *(char_u **)varp = vim_strsave((char_u *)"indent,eol,start");
- break;
- case 3:
- *(char_u **)varp = vim_strsave((char_u *)"indent,eol,nostop");
- break;
- }
- xfree(oldval);
- if (origval == oldval) {
- origval = *(char_u **)varp;
- }
- if (origval_l == oldval) {
- origval_l = *(char_u **)varp;
- }
- if (origval_g == oldval) {
- origval_g = *(char_u **)varp;
- }
- oldval = *(char_u **)varp;
- } else if (varp == (char *)&p_ww && ascii_isdigit(*arg)) {
- // Convert 'whichwrap' number to string, for
- // backwards compatibility with Vim 3.0.
- // Misuse errbuf[] for the resulting string.
- *errbuf = NUL;
- i = getdigits_int(&arg, true, 0);
- if (i & 1) {
- STRLCAT(errbuf, "b,", sizeof(errbuf));
- }
- if (i & 2) {
- STRLCAT(errbuf, "s,", sizeof(errbuf));
- }
- if (i & 4) {
- STRLCAT(errbuf, "h,l,", sizeof(errbuf));
- }
- if (i & 8) {
- STRLCAT(errbuf, "<,>,", sizeof(errbuf));
- }
- if (i & 16) {
- STRLCAT(errbuf, "[,],", sizeof(errbuf));
- }
- save_arg = (char_u *)arg;
- arg = errbuf;
- } else if (*arg == '>'
- && (varp == (char *)&p_dir
- || varp == (char *)&p_bdir)) {
- // Remove '>' before 'dir' and 'bdir', for
- // backwards compatibility with version 3.0
- arg++;
- }
-
- /*
- * Copy the new string into allocated memory.
- * Can't use set_string_option_direct(), because
- * we need to remove the backslashes.
- */
- // get a bit too much
- newlen = (unsigned)STRLEN(arg) + 1;
- if (adding || prepending || removing) {
- newlen += (unsigned)STRLEN(origval) + 1;
- }
- newval = xmalloc(newlen);
- s = newval;
-
- /*
- * Copy the string, skip over escaped chars.
- * For WIN32 backslashes before normal
- * file name characters are not removed, and keep
- * backslash at start, for "\\machine\path", but
- * do remove it for "\\\\machine\\path".
- * The reverse is found in ExpandOldSetting().
- */
- while (*arg && !ascii_iswhite(*arg)) {
- if (*arg == '\\' && arg[1] != NUL
-#ifdef BACKSLASH_IN_FILENAME
- && !((flags & P_EXPAND)
- && vim_isfilec(arg[1])
- && !ascii_iswhite(arg[1])
- && (arg[1] != '\\'
- || (s == newval
- && arg[2] != '\\')))
-#endif
- ) {
- arg++; // remove backslash
- }
- i = utfc_ptr2len(arg);
- if (i > 1) {
- // copy multibyte char
- memmove(s, arg, (size_t)i);
- arg += i;
- s += i;
- } else {
- *s++ = (uint8_t)(*arg++);
- }
- }
- *s = NUL;
-
- /*
- * Expand environment variables and ~.
- * Don't do it when adding without inserting a
- * comma.
- */
- if (!(adding || prepending || removing)
- || (flags & P_COMMA)) {
- s = option_expand(opt_idx, newval);
- if (s != NULL) {
- xfree(newval);
- newlen = (unsigned)STRLEN(s) + 1;
- if (adding || prepending || removing) {
- newlen += (unsigned)STRLEN(origval) + 1;
- }
- newval = xmalloc(newlen);
- STRCPY(newval, s);
- }
- }
-
- // locate newval[] in origval[] when removing it
- // and when adding to avoid duplicates
- i = 0; // init for GCC
- if (removing || (flags & P_NODUP)) {
- i = (int)STRLEN(newval);
- s = find_dup_item(origval, newval, flags);
-
- // do not add if already there
- if ((adding || prepending) && s != NULL) {
- prepending = false;
- adding = false;
- STRCPY(newval, origval);
- }
-
- // if no duplicate, move pointer to end of
- // original value
- if (s == NULL) {
- s = origval + (int)STRLEN(origval);
- }
- }
-
- // concatenate the two strings; add a ',' if
- // needed
- if (adding || prepending) {
- comma = ((flags & P_COMMA) && *origval != NUL
- && *newval != NUL);
- if (adding) {
- i = (int)STRLEN(origval);
- // Strip a trailing comma, would get 2.
- if (comma && i > 1
- && (flags & P_ONECOMMA) == P_ONECOMMA
- && origval[i - 1] == ','
- && origval[i - 2] != '\\') {
- i--;
- }
- memmove(newval + i + comma, newval,
- STRLEN(newval) + 1);
- memmove(newval, origval, (size_t)i);
- } else {
- i = (int)STRLEN(newval);
- STRMOVE(newval + i + comma, origval);
- }
- if (comma) {
- newval[i] = ',';
- }
- }
-
- // Remove newval[] from origval[]. (Note: "i" has
- // been set above and is used here).
- if (removing) {
- STRCPY(newval, origval);
- if (*s) {
- // may need to remove a comma
- if (flags & P_COMMA) {
- if (s == origval) {
- // include comma after string
- if (s[i] == ',') {
- i++;
- }
- } else {
- // include comma before string
- s--;
- i++;
- }
- }
- STRMOVE(newval + (s - origval), s + i);
- }
- }
-
- if (flags & P_FLAGLIST) {
- // Remove flags that appear twice.
- for (s = newval; *s;) {
- // if options have P_FLAGLIST and P_ONECOMMA such as
- // 'whichwrap'
- if (flags & P_ONECOMMA) {
- if (*s != ',' && *(s + 1) == ','
- && 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((char *)s + 1, *s) != NULL) {
- STRMOVE(s, s + 1);
- continue;
- }
- }
- s++;
- }
- }
-
- if (save_arg != NULL) { // number for 'whichwrap'
- arg = (char *)save_arg;
- }
- }
-
- // Set the new value.
- *(char_u **)(varp) = newval;
-
- // origval may be freed by
- // did_set_string_option(), make a copy.
- saved_origval = (origval != NULL) ? xstrdup((char *)origval) : 0;
- saved_origval_l = (origval_l != NULL) ? xstrdup((char *)origval_l) : 0;
- saved_origval_g = (origval_g != NULL) ? xstrdup((char *)origval_g) : 0;
-
- // newval (and varp) may become invalid if the
- // buffer is closed by autocommands.
- saved_newval = (newval != NULL) ? xstrdup((char *)newval) : 0;
-
- {
- uint32_t *p = insecure_flag(curwin, opt_idx, opt_flags);
- const int secure_saved = secure;
-
- // When an option is set in the sandbox, from a
- // modeline or in secure mode, then deal with side
- // effects in secure mode. Also when the value was
- // set with the P_INSECURE flag and is not
- // completely replaced.
- if ((opt_flags & OPT_MODELINE)
- || sandbox != 0
- || (!value_is_replaced && (*p & P_INSECURE))) {
- secure = 1;
- }
-
- // Handle side effects, and set the global value
- // for ":set" on local options. Note: when setting
- // 'syntax' or 'filetype' autocommands may be
- // triggered that can cause havoc.
- errmsg = did_set_string_option(opt_idx, (char_u **)varp, oldval,
- errbuf, sizeof(errbuf),
- opt_flags, &value_checked);
-
- secure = secure_saved;
- }
-
- if (errmsg == NULL) {
- if (!starting) {
- trigger_optionsset_string(opt_idx, opt_flags, saved_origval, saved_origval_l,
- saved_origval_g, saved_newval);
- }
- if (options[opt_idx].flags & P_UI_OPTION) {
- ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
- STRING_OBJ(cstr_as_string(saved_newval)));
+ if (do_set_string(opt_idx, opt_flags, &arg, nextchar,
+ op, flags, varp, errbuf, sizeof(errbuf),
+ &value_checked, &errmsg) == FAIL) {
+ if (errmsg != NULL) {
+ goto skip;
}
- }
- xfree(saved_origval);
- xfree(saved_origval_l);
- xfree(saved_origval_g);
- xfree(saved_newval);
-
- // If error detected, print the error message.
- if (errmsg != NULL) {
- goto skip;
+ break;
}
} else {
// key code option(FIXME(tarruda): Show a warning or something
@@ -1501,18 +1423,16 @@ int do_set(char *arg, int opt_flags)
}
if (opt_idx >= 0) {
- did_set_option(opt_idx, opt_flags, value_is_replaced, value_checked);
+ did_set_option(opt_idx, opt_flags, op == OP_NONE, value_checked);
}
}
skip:
- /*
- * Advance to next argument.
- * - skip until a blank found, taking care of backslashes
- * - skip blanks
- * - skip one "=val" argument (for hidden options ":set gfn =xx")
- */
- for (i = 0; i < 2; i++) {
+ // Advance to next argument.
+ // - skip until a blank found, taking care of backslashes
+ // - skip blanks
+ // - skip one "=val" argument (for hidden options ":set gfn =xx")
+ for (int i = 0; i < 2; i++) {
while (*arg != NUL && !ascii_iswhite(*arg)) {
if (*arg++ == '\\' && *arg != NUL) {
arg++;
@@ -1526,20 +1446,20 @@ skip:
}
if (errmsg != NULL) {
- STRLCPY(IObuff, _(errmsg), IOSIZE);
- i = (int)STRLEN(IObuff) + 2;
- if (i + ((char_u *)arg - startarg) < IOSIZE) {
+ xstrlcpy(IObuff, _(errmsg), IOSIZE);
+ int i = (int)strlen(IObuff) + 2;
+ if (i + (arg - startarg) < IOSIZE) {
// append the argument with the error
STRCAT(IObuff, ": ");
- assert((char_u *)arg >= startarg);
- memmove(IObuff + i, startarg, (size_t)((char_u *)arg - startarg));
- IObuff[i + ((char_u *)arg - startarg)] = NUL;
+ assert(arg >= startarg);
+ memmove(IObuff + i, startarg, (size_t)(arg - startarg));
+ IObuff[i + (arg - startarg)] = NUL;
}
// make sure all characters are printable
- trans_characters((char *)IObuff, IOSIZE);
+ trans_characters(IObuff, IOSIZE);
- no_wait_return++; // wait_return done later
- emsg((char *)IObuff); // show error highlighted
+ no_wait_return++; // wait_return() done later
+ emsg(IObuff); // show error highlighted
no_wait_return--;
return FAIL;
@@ -1552,11 +1472,11 @@ theend:
if (silent_mode && did_show) {
// After displaying option values in silent mode.
silent_mode = false;
- info_message = true; // use mch_msg(), not mch_errmsg()
+ info_message = true; // use os_msg(), not os_errmsg()
msg_putchar('\n');
ui_flush();
silent_mode = true;
- info_message = false; // use mch_msg(), not mch_errmsg()
+ info_message = false; // use os_msg(), not os_errmsg()
}
return OK;
@@ -1587,33 +1507,15 @@ void did_set_option(int opt_idx, int opt_flags, int new_value, int value_checked
/// Convert a key name or string into a key value.
/// Used for 'wildchar' and 'cedit' options.
-static int string_to_key(char_u *arg)
+int string_to_key(char *arg)
{
if (*arg == '<') {
return find_key_option(arg + 1, true);
}
if (*arg == '^') {
- return CTRL_CHR(arg[1]);
- }
- return *arg;
-}
-
-/// Check value of 'cedit' and set cedit_key.
-/// Returns NULL if value is OK, error message otherwise.
-char *check_cedit(void)
-{
- int n;
-
- if (*p_cedit == NUL) {
- cedit_key = -1;
- } else {
- n = string_to_key(p_cedit);
- if (vim_isprintc(n)) {
- return e_invarg;
- }
- cedit_key = n;
+ return CTRL_CHR((uint8_t)arg[1]);
}
- return NULL;
+ return (uint8_t)(*arg);
}
// When changing 'title', 'titlestring', 'icon' or 'iconstring', call
@@ -1632,10 +1534,8 @@ void did_set_title(void)
/// @param opt_flags OPT_LOCAL and/or OPT_GLOBAL
void set_options_bin(int oldval, int newval, int opt_flags)
{
- /*
- * The option values that are changed when 'bin' changes are
- * copied when 'bin is set and restored when 'bin' is reset.
- */
+ // The option values that are changed when 'bin' changes are
+ // copied when 'bin is set and restored when 'bin' is reset.
if (newval) {
if (!oldval) { // switched on
if (!(opt_flags & OPT_GLOBAL)) {
@@ -1688,11 +1588,9 @@ void set_options_bin(int oldval, int newval, int opt_flags)
/// number, return -1.
int get_shada_parameter(int type)
{
- char_u *p;
-
- p = find_shada_parameter(type);
+ char *p = find_shada_parameter(type);
if (p != NULL && ascii_isdigit(*p)) {
- return atoi((char *)p);
+ return atoi(p);
}
return -1;
}
@@ -1700,18 +1598,16 @@ int get_shada_parameter(int type)
/// Find the parameter represented by the given character (eg ''', ':', '"', or
/// '/') in the 'shada' option and return a pointer to the string after it.
/// Return NULL if the parameter is not specified in the string.
-char_u *find_shada_parameter(int type)
+char *find_shada_parameter(int type)
{
- char_u *p;
-
- for (p = p_shada; *p; p++) {
+ for (char *p = p_shada; *p; p++) {
if (*p == type) {
return p + 1;
}
if (*p == 'n') { // 'n' is always the last one
break;
}
- p = (char_u *)vim_strchr((char *)p, ','); // skip until next ','
+ p = vim_strchr(p, ','); // skip until next ','
if (p == NULL) { // hit the end without finding parameter
break;
}
@@ -1723,7 +1619,7 @@ char_u *find_shada_parameter(int type)
/// These string options cannot be indirect!
/// If "val" is NULL expand the current value of the option.
/// Return pointer to NameBuff, or NULL when not expanded.
-static char_u *option_expand(int opt_idx, char_u *val)
+static char *option_expand(int opt_idx, char *val)
{
// if option doesn't need expansion nothing to do
if (!(options[opt_idx].flags & P_EXPAND) || options[opt_idx].var == NULL) {
@@ -1731,26 +1627,24 @@ static char_u *option_expand(int opt_idx, char_u *val)
}
if (val == NULL) {
- val = *(char_u **)options[opt_idx].var;
+ val = *(char **)options[opt_idx].var;
}
// If val is longer than MAXPATHL no meaningful expansion can be done,
// expand_env() would truncate the string.
- if (val == NULL || STRLEN(val) > MAXPATHL) {
+ if (val == NULL || strlen(val) > MAXPATHL) {
return NULL;
}
- /*
- * Expanding this with NameBuff, expand_env() must not be passed IObuff.
- * Escape spaces when expanding 'tags', they are used to separate file
- * names.
- * For 'spellsuggest' expand after "file:".
- */
+ // Expanding this with NameBuff, expand_env() must not be passed IObuff.
+ // Escape spaces when expanding 'tags', they are used to separate file
+ // names.
+ // For 'spellsuggest' expand after "file:".
expand_env_esc(val, NameBuff, MAXPATHL,
- (char_u **)options[opt_idx].var == &p_tags, false,
- (char_u **)options[opt_idx].var == &p_sps ? (char_u *)"file:" :
+ (char **)options[opt_idx].var == &p_tags, false,
+ (char_u **)options[opt_idx].var == (char_u **)&p_sps ? "file:" :
NULL);
- if (STRCMP(NameBuff, val) == 0) { // they are the same
+ if (strcmp(NameBuff, val) == 0) { // they are the same
return NULL;
}
@@ -1800,11 +1694,9 @@ static void didset_options2(void)
/// Check for string options that are NULL (normally only termcap options).
void check_options(void)
{
- int opt_idx;
-
- for (opt_idx = 0; options[opt_idx].fullname != NULL; opt_idx++) {
+ for (int opt_idx = 0; options[opt_idx].fullname != NULL; opt_idx++) {
if ((options[opt_idx].flags & P_STRING) && options[opt_idx].var != NULL) {
- check_string_option((char_u **)get_varp(&(options[opt_idx])));
+ check_string_option((char **)get_varp(&(options[opt_idx])));
}
}
}
@@ -1863,12 +1755,12 @@ void redraw_titles(void)
/// Return true if "val" is a valid name: only consists of alphanumeric ASCII
/// characters or characters in "allowed".
-bool valid_name(const char_u *val, const char *allowed)
+bool valid_name(const char *val, const char *allowed)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- for (const char_u *s = val; *s != NUL; s++) {
+ for (const char *s = val; *s != NUL; s++) {
if (!ASCII_ISALNUM(*s)
- && vim_strchr(allowed, *s) == NULL) {
+ && vim_strchr(allowed, (uint8_t)(*s)) == NULL) {
return false;
}
}
@@ -1935,15 +1827,16 @@ 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,
- script_ctx.sc_seq,
- script_ctx.sc_lnum + SOURCING_LNUM
- },
- current_channel_id
+ LastSet last_set = {
+ .script_ctx = script_ctx,
+ .channel_id = current_channel_id,
};
+ // Modeline already has the line number set.
+ if (!(opt_flags & OPT_MODELINE)) {
+ last_set.script_ctx.sc_lnum += SOURCING_LNUM;
+ }
+
// Remember where the option was set. For local options need to do that
// in the buffer or window structure.
if (both || (opt_flags & OPT_GLOBAL) || (indir & (PV_BUF|PV_WIN)) == 0) {
@@ -1958,6 +1851,46 @@ void set_option_sctx_idx(int opt_idx, int opt_flags, sctx_T script_ctx)
}
}
+/// Apply the OptionSet autocommand.
+static void apply_optionset_autocmd(int opt_idx, long opt_flags, long oldval, long oldval_g,
+ long newval, const char *errmsg)
+{
+ // Don't do this while starting up, failure or recursively.
+ if (starting || errmsg != NULL || *get_vim_var_str(VV_OPTION_TYPE) != NUL) {
+ return;
+ }
+
+ char buf_old[12], buf_old_global[12], buf_new[12], buf_type[12];
+
+ vim_snprintf(buf_old, sizeof(buf_old), "%ld", oldval);
+ vim_snprintf(buf_old_global, sizeof(buf_old_global), "%ld", oldval_g);
+ vim_snprintf(buf_new, sizeof(buf_new), "%ld", newval);
+ vim_snprintf(buf_type, sizeof(buf_type), "%s",
+ (opt_flags & OPT_LOCAL) ? "local" : "global");
+ set_vim_var_string(VV_OPTION_NEW, buf_new, -1);
+ set_vim_var_string(VV_OPTION_OLD, buf_old, -1);
+ set_vim_var_string(VV_OPTION_TYPE, buf_type, -1);
+ if (opt_flags & OPT_LOCAL) {
+ set_vim_var_string(VV_OPTION_COMMAND, "setlocal", -1);
+ set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1);
+ }
+ if (opt_flags & OPT_GLOBAL) {
+ set_vim_var_string(VV_OPTION_COMMAND, "setglobal", -1);
+ set_vim_var_string(VV_OPTION_OLDGLOBAL, buf_old, -1);
+ }
+ if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
+ set_vim_var_string(VV_OPTION_COMMAND, "set", -1);
+ set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1);
+ set_vim_var_string(VV_OPTION_OLDGLOBAL, buf_old_global, -1);
+ }
+ if (opt_flags & OPT_MODELINE) {
+ set_vim_var_string(VV_OPTION_COMMAND, "modeline", -1);
+ set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1);
+ }
+ apply_autocmds(EVENT_OPTIONSET, options[opt_idx].fullname, NULL, false, NULL);
+ reset_v_option_vars();
+}
+
/// Set the value of a boolean option, taking care of side effects
///
/// @param[in] opt_idx Option index in options[] table.
@@ -1971,6 +1904,7 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
{
int old_value = *(int *)varp;
int old_global_value = 0;
+ char *errmsg = NULL;
// Disallow changing some options from secure mode
if ((secure || sandbox != 0)
@@ -2025,7 +1959,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, (char_u *)bp->b_fname);
+ u_read_undo(NULL, hash, bp->b_fname);
}
}
}
@@ -2044,14 +1978,12 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
} else if ((int *)varp == &curbuf->b_p_ma) {
// when 'modifiable' is changed, redraw the window title
redraw_titles();
- } else if ((int *)varp == &curbuf->b_p_eol) {
- // when 'endofline' is changed, redraw the window title
- redraw_titles();
- } else if ((int *)varp == &curbuf->b_p_fixeol) {
- // when 'fixeol' is changed, redraw the window title
- redraw_titles();
- } else if ((int *)varp == &curbuf->b_p_bomb) {
- // when 'bomb' is changed, redraw the window title and tab page text
+ } else if ((int *)varp == &curbuf->b_p_eof
+ || (int *)varp == &curbuf->b_p_eol
+ || (int *)varp == &curbuf->b_p_fixeol
+ || (int *)varp == &curbuf->b_p_bomb) {
+ // redraw the window title and tab page text when 'endoffile', 'endofline',
+ // 'fixeol' or 'bomb' is changed
redraw_titles();
} else if ((int *)varp == &curbuf->b_p_bin) {
// when 'bin' is set also set some other options
@@ -2075,7 +2007,7 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
paste_option_changed();
} else if ((int *)varp == &p_ic && p_hls) {
// when 'ignorecase' is set or reset and 'hlsearch' is set, redraw
- redraw_all_later(SOME_VALID);
+ redraw_all_later(UPD_SOME_VALID);
} else if ((int *)varp == &p_hls) {
// when 'hlsearch' is set or reset: reset no_hlsearch
set_no_hlsearch(false);
@@ -2092,7 +2024,7 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
FOR_ALL_WINDOWS_IN_TAB(win, curtab) {
if (win->w_p_pvw && win != curwin) {
curwin->w_p_pvw = false;
- return N_("E590: A preview window already exists");
+ return e_preview_window_already_exists;
}
}
}
@@ -2111,10 +2043,9 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
}
redraw_titles();
modified_was_set = value;
- }
#ifdef BACKSLASH_IN_FILENAME
- else if ((int *)varp == &p_ssl) {
+ } else if ((int *)varp == &p_ssl) {
if (p_ssl) {
psepc = '/';
psepcN = '\\';
@@ -2129,9 +2060,8 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
buflist_slash_adjust();
alist_slash_adjust();
scriptnames_slash_adjust();
- }
#endif
- else if ((int *)varp == &curwin->w_p_wrap) {
+ } else if ((int *)varp == &curwin->w_p_wrap) {
// If 'wrap' is set, set w_leftcol to zero.
if (curwin->w_p_wrap) {
curwin->w_leftcol = 0;
@@ -2151,18 +2081,16 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
}
} else if ((int *)varp == &curwin->w_p_spell) { // 'spell'
if (curwin->w_p_spell) {
- char *errmsg = did_set_spelllang(curwin);
- if (errmsg != NULL) {
- emsg(_(errmsg));
- }
+ errmsg = did_set_spelllang(curwin);
}
+ } else if (((int *)varp == &curwin->w_p_nu || (int *)varp == &curwin->w_p_rnu)
+ && *curwin->w_p_stc != NUL) { // '(relative)number' + 'statuscolumn'
+ curwin->w_nrwidth_line_count = 0;
}
if ((int *)varp == &curwin->w_p_arab) {
if (curwin->w_p_arab) {
- /*
- * 'arabic' is set, handle various sub-settings.
- */
+ // 'arabic' is set, handle various sub-settings.
if (!p_tbidi) {
// set rightleft mode
if (!curwin->w_p_rl) {
@@ -2173,13 +2101,13 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
// Enable Arabic shaping (major part of what Arabic requires)
if (!p_arshape) {
p_arshape = true;
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
}
}
// Arabic requires a utf-8 encoding, inform the user if it's not
// set.
- if (STRCMP(p_enc, "utf-8") != 0) {
+ if (strcmp(p_enc, "utf-8") != 0) {
static char *w_arabic = N_("W17: Arabic requires UTF-8, do ':set encoding=utf-8'");
msg_source(HL_ATTR(HLF_W));
@@ -2191,11 +2119,9 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
p_deco = true;
// Force-set the necessary keymap for arabic.
- set_option_value("keymap", 0L, "arabic", OPT_LOCAL);
+ errmsg = set_option_value("keymap", 0L, "arabic", OPT_LOCAL);
} else {
- /*
- * 'arabic' is reset, handle various sub-settings.
- */
+ // 'arabic' is reset, handle various sub-settings.
if (!p_tbidi) {
// reset rightleft mode
if (curwin->w_p_rl) {
@@ -2216,48 +2142,16 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
}
}
- /*
- * End of handling side effects for bool options.
- */
+ // End of handling side effects for bool options.
// after handling side effects, call autocommand
options[opt_idx].flags |= P_WAS_SET;
- // Don't do this while starting up or recursively.
- if (!starting && *get_vim_var_str(VV_OPTION_TYPE) == NUL) {
- char buf_old[2];
- char buf_old_global[2];
- char buf_new[2];
- char buf_type[7];
- vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%d", old_value ? true : false);
- vim_snprintf(buf_old_global, ARRAY_SIZE(buf_old_global), "%d", old_global_value ? true : false);
- vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%d", value ? true : false);
- vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s",
- (opt_flags & OPT_LOCAL) ? "local" : "global");
- set_vim_var_string(VV_OPTION_NEW, buf_new, -1);
- set_vim_var_string(VV_OPTION_OLD, buf_old, -1);
- set_vim_var_string(VV_OPTION_TYPE, buf_type, -1);
- if (opt_flags & OPT_LOCAL) {
- set_vim_var_string(VV_OPTION_COMMAND, "setlocal", -1);
- set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1);
- }
- if (opt_flags & OPT_GLOBAL) {
- set_vim_var_string(VV_OPTION_COMMAND, "setglobal", -1);
- set_vim_var_string(VV_OPTION_OLDGLOBAL, buf_old, -1);
- }
- if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
- set_vim_var_string(VV_OPTION_COMMAND, "set", -1);
- set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1);
- set_vim_var_string(VV_OPTION_OLDGLOBAL, buf_old_global, -1);
- }
- if (opt_flags & OPT_MODELINE) {
- set_vim_var_string(VV_OPTION_COMMAND, "modeline", -1);
- set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1);
- }
- apply_autocmds(EVENT_OPTIONSET, options[opt_idx].fullname, NULL, false, NULL);
- reset_v_option_vars();
- }
+ apply_optionset_autocmd(opt_idx, opt_flags,
+ (long)(old_value ? true : false),
+ (long)(old_global_value ? true : false),
+ (long)(value ? true : false), NULL);
if (options[opt_idx].flags & P_UI_OPTION) {
ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
@@ -2271,7 +2165,7 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
}
check_redraw(options[opt_idx].flags);
- return NULL;
+ return errmsg;
}
/// Set the value of a number option, taking care of side effects
@@ -2354,6 +2248,8 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
int minval = 0;
if (value < minval) {
errmsg = e_positive;
+ } else {
+ p_ch_was_zero = value == 0;
}
} else if (pp == &p_tm) {
if (value < 0) {
@@ -2412,7 +2308,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
} else if (pp == &curwin->w_p_nuw || pp == &curwin->w_allbuf_opt.wo_nuw) {
if (value < 1) {
errmsg = e_positive;
- } else if (value > 20) {
+ } else if (value > MAX_NUMBERWIDTH) {
errmsg = e_invarg;
}
} else if (pp == &curbuf->b_p_iminsert || pp == &p_iminsert) {
@@ -2546,7 +2442,9 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
// if p_ch changed value, change the command line height
// Only compute the new window layout when startup has been
// completed. Otherwise the frame sizes may be wrong.
- if (p_ch != old_value && full_screen) {
+ if ((p_ch != old_value
+ || tabline_height() + global_stl_height() + topframe->fr_height != Rows - p_ch)
+ && full_screen) {
command_height();
}
} else if (pp == &p_uc) {
@@ -2622,9 +2520,10 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
Rows = (int)p_lines;
Columns = (int)p_columns;
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);
+ int new_row = (int)(Rows - MAX(p_ch, 1));
+ if (cmdline_row > new_row && Rows > p_ch) {
+ assert(p_ch >= 0 && new_row <= INT_MAX);
+ cmdline_row = new_row;
}
}
if (p_window >= Rows || !option_was_set("window")) {
@@ -2664,41 +2563,8 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
options[opt_idx].flags |= P_WAS_SET;
- // Don't do this while starting up, failure or recursively.
- if (!starting && errmsg == NULL && *get_vim_var_str(VV_OPTION_TYPE) == NUL) {
- char buf_old[NUMBUFLEN];
- char buf_old_global[NUMBUFLEN];
- char buf_new[NUMBUFLEN];
- char buf_type[7];
-
- vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%ld", old_value);
- vim_snprintf(buf_old_global, ARRAY_SIZE(buf_old_global), "%ld", old_global_value);
- vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%ld", value);
- vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s",
- (opt_flags & OPT_LOCAL) ? "local" : "global");
- set_vim_var_string(VV_OPTION_NEW, buf_new, -1);
- set_vim_var_string(VV_OPTION_OLD, buf_old, -1);
- set_vim_var_string(VV_OPTION_TYPE, buf_type, -1);
- if (opt_flags & OPT_LOCAL) {
- set_vim_var_string(VV_OPTION_COMMAND, "setlocal", -1);
- set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1);
- }
- if (opt_flags & OPT_GLOBAL) {
- set_vim_var_string(VV_OPTION_COMMAND, "setglobal", -1);
- set_vim_var_string(VV_OPTION_OLDGLOBAL, buf_old, -1);
- }
- if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
- set_vim_var_string(VV_OPTION_COMMAND, "set", -1);
- set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1);
- set_vim_var_string(VV_OPTION_OLDGLOBAL, buf_old_global, -1);
- }
- if (opt_flags & OPT_MODELINE) {
- set_vim_var_string(VV_OPTION_COMMAND, "modeline", -1);
- set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1);
- }
- apply_autocmds(EVENT_OPTIONSET, options[opt_idx].fullname, NULL, false, NULL);
- reset_v_option_vars();
- }
+ apply_optionset_autocmd(opt_idx, opt_flags, old_value, old_global_value,
+ value, errmsg);
if (errmsg == NULL && options[opt_idx].flags & P_UI_OPTION) {
ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
@@ -2716,32 +2582,38 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
}
/// Called after an option changed: check if something needs to be redrawn.
-void check_redraw(uint32_t flags)
+void check_redraw_for(buf_T *buf, win_T *win, uint32_t flags)
{
- // Careful: P_RCLR and P_RALL are a combination of other P_ flags
- bool doclear = (flags & P_RCLR) == P_RCLR;
- bool all = ((flags & P_RALL) == P_RALL || doclear);
+ // Careful: P_RALL is a combination of other P_ flags
+ bool all = (flags & P_RALL) == P_RALL;
if ((flags & P_RSTAT) || all) { // mark all status lines and window bars dirty
status_redraw_all();
}
+ if ((flags & P_RTABL) || all) { // mark tablines dirty
+ redraw_tabline = true;
+ }
+
if ((flags & P_RBUF) || (flags & P_RWIN) || all) {
- changed_window_setting();
+ changed_window_setting_win(win);
}
if (flags & P_RBUF) {
- redraw_curbuf_later(NOT_VALID);
+ redraw_buf_later(buf, UPD_NOT_VALID);
}
if (flags & P_RWINONLY) {
- redraw_later(curwin, NOT_VALID);
+ redraw_later(win, UPD_NOT_VALID);
}
- if (doclear) {
- redraw_all_later(CLEAR);
- } else if (all) {
- redraw_all_later(NOT_VALID);
+ if (all) {
+ redraw_all_later(UPD_NOT_VALID);
}
}
+void check_redraw(uint32_t flags)
+{
+ check_redraw_for(curbuf, curwin, flags);
+}
+
/// Find index for named option
///
/// @param[in] arg Option to find index for.
@@ -2751,15 +2623,14 @@ void check_redraw(uint32_t flags)
int findoption_len(const char *const arg, const size_t len)
{
const char *s;
- const char *p;
static int quick_tab[27] = { 0, 0 }; // quick access table
// For first call: Initialize the quick-access table.
// It contains the index for the first option that starts with a certain
// letter. There are 26 letters, plus the first "t_" option.
if (quick_tab[1] == 0) {
- p = options[0].fullname;
- for (short int i = 1; (s = options[i].fullname) != NULL; i++) {
+ const char *p = options[0].fullname;
+ for (uint16_t i = 1; (s = options[i].fullname) != NULL; i++) {
if (s[0] != p[0]) {
if (s[0] == 't' && s[1] == '_') {
quick_tab[26] = i;
@@ -2804,11 +2675,11 @@ int findoption_len(const char *const arg, const size_t len)
opt_idx = -1;
} else {
// Nvim: handle option aliases.
- if (STRNCMP(options[opt_idx].fullname, "viminfo", 7) == 0) {
- if (STRLEN(options[opt_idx].fullname) == 7) {
+ if (strncmp(options[opt_idx].fullname, "viminfo", 7) == 0) {
+ if (strlen(options[opt_idx].fullname) == 7) {
return findoption_len("shada", 5);
}
- assert(STRCMP(options[opt_idx].fullname, "viminfofile") == 0);
+ assert(strcmp(options[opt_idx].fullname, "viminfofile") == 0);
return findoption_len("shadafile", 9);
}
}
@@ -2888,7 +2759,7 @@ bool set_tty_option(const char *name, char *value)
void set_tty_background(const char *value)
{
- if (option_was_set("bg") || strequal((char *)p_bg, value)) {
+ if (option_was_set("bg") || strequal(p_bg, value)) {
// background is already set... ignore
return;
}
@@ -2898,7 +2769,7 @@ void set_tty_background(const char *value)
? "autocmd VimEnter * ++once ++nested set bg=light"
: "autocmd VimEnter * ++once ++nested set bg=dark");
} else {
- set_option_value("bg", 0L, value, 0);
+ set_option_value_give_err("bg", 0L, value, 0);
reset_option_was_set("bg");
}
}
@@ -2917,6 +2788,7 @@ int findoption(const char *const arg)
/// Gets the value for an option.
///
/// @param stringval NULL when only checking existence
+/// @param flagsp set to the option flags (P_xxxx) (if not NULL)
///
/// @returns:
/// Number option: gov_number, *numval gets value.
@@ -2926,7 +2798,8 @@ int findoption(const char *const arg)
/// Hidden Toggle option: gov_hidden_bool.
/// Hidden String option: gov_hidden_string.
/// Unknown option: gov_unknown.
-getoption_T get_option_value(const char *name, long *numval, char **stringval, int opt_flags)
+getoption_T get_option_value(const char *name, long *numval, char **stringval, uint32_t *flagsp,
+ int scope)
{
if (get_tty_option(name, stringval)) {
return gov_string;
@@ -2937,14 +2810,19 @@ getoption_T get_option_value(const char *name, long *numval, char **stringval, i
return gov_unknown;
}
- char_u *varp = get_varp_scope(&(options[opt_idx]), opt_flags);
+ char_u *varp = (char_u *)get_varp_scope(&(options[opt_idx]), scope);
+
+ if (flagsp != NULL) {
+ // Return the P_xxxx option flags.
+ *flagsp = options[opt_idx].flags;
+ }
if (options[opt_idx].flags & P_STRING) {
if (varp == NULL) { // hidden option
return gov_hidden_string;
}
if (stringval != NULL) {
- if ((char_u **)varp == &p_pt) { // 'pastetoggle'
+ if ((char **)varp == &p_pt) { // 'pastetoggle'
*stringval = str2special_save(*(char **)(varp), false, false);
} else {
*stringval = xstrdup(*(char **)(varp));
@@ -2992,7 +2870,6 @@ int get_option_value_strict(char *name, int64_t *numval, char **stringval, int o
return SOPT_STRING | SOPT_GLOBAL;
}
- char_u *varp = NULL;
int rv = 0;
int opt_idx = findoption(name);
if (opt_idx < 0) {
@@ -3028,15 +2905,13 @@ int get_option_value_strict(char *name, int64_t *numval, char **stringval, int o
if (p->indir & PV_WIN) {
if (opt_type == SREQ_BUF) {
return 0; // Requested buffer-local, not window-local option
- } else {
- rv |= SOPT_WIN;
}
+ rv |= SOPT_WIN;
} else if (p->indir & PV_BUF) {
if (opt_type == SREQ_WIN) {
return 0; // Requested window-local, not buffer-local option
- } else {
- rv |= SOPT_BUF;
}
+ rv |= SOPT_BUF;
}
}
@@ -3044,12 +2919,13 @@ int get_option_value_strict(char *name, int64_t *numval, char **stringval, int o
return rv;
}
+ char_u *varp = NULL;
+
if (opt_type == SREQ_GLOBAL) {
if (p->var == VAR_WIN) {
return 0;
- } else {
- varp = p->var;
}
+ varp = p->var;
} else {
if (opt_type == SREQ_BUF) {
// Special case: 'modified' is b_changed, but we also want to
@@ -3063,7 +2939,7 @@ int get_option_value_strict(char *name, int64_t *numval, char **stringval, int o
// only getting a pointer, no need to use aucmd_prepbuf()
curbuf = (buf_T *)from;
curwin->w_buffer = curbuf;
- varp = get_varp_scope(p, OPT_LOCAL);
+ varp = (char_u *)get_varp_scope(p, OPT_LOCAL);
curbuf = save_curbuf;
curwin->w_buffer = curbuf;
}
@@ -3071,7 +2947,7 @@ int get_option_value_strict(char *name, int64_t *numval, char **stringval, int o
win_T *save_curwin = curwin;
curwin = (win_T *)from;
curbuf = curwin->w_buffer;
- varp = get_varp_scope(p, OPT_LOCAL);
+ varp = (char_u *)get_varp_scope(p, OPT_LOCAL);
curwin = save_curwin;
curbuf = curwin->w_buffer;
}
@@ -3083,7 +2959,7 @@ int get_option_value_strict(char *name, int64_t *numval, char **stringval, int o
if (varp != NULL) {
if (p->flags & P_STRING) {
- *stringval = xstrdup(*(char **)(varp));
+ *stringval = *(char **)(varp);
} else if (p->flags & P_NUM) {
*numval = *(long *)varp;
} else {
@@ -3094,47 +2970,10 @@ int get_option_value_strict(char *name, int64_t *numval, char **stringval, int o
return rv;
}
-/// Return the flags for the option at 'opt_idx'.
-uint32_t get_option_flags(int opt_idx)
-{
- return options[opt_idx].flags;
-}
-
-/// Set a flag for the option at 'opt_idx'.
-void set_option_flag(int opt_idx, uint32_t flag)
+// Return information for option at 'opt_idx'
+vimoption_T *get_option(int opt_idx)
{
- options[opt_idx].flags |= flag;
-}
-
-/// Clear a flag for the option at 'opt_idx'.
-void clear_option_flag(int opt_idx, uint32_t flag)
-{
- options[opt_idx].flags &= ~flag;
-}
-
-/// Returns true if the option at 'opt_idx' is a global option
-bool is_global_option(int opt_idx)
-{
- return options[opt_idx].indir == PV_NONE;
-}
-
-/// Returns true if the option at 'opt_idx' is a global option which also has a
-/// local value.
-int is_global_local_option(int opt_idx)
-{
- return options[opt_idx].indir & PV_BOTH;
-}
-
-/// Returns true if the option at 'opt_idx' is a window-local option
-bool is_window_local_option(int opt_idx)
-{
- return options[opt_idx].var == VAR_WIN;
-}
-
-/// Returns true if the option at 'opt_idx' is a hidden option
-bool is_hidden_option(int opt_idx)
-{
- return options[opt_idx].var == NULL;
+ return &options[opt_idx];
}
/// Set the value of an option
@@ -3147,7 +2986,7 @@ bool is_hidden_option(int opt_idx)
/// is cleared (the exact semantics of this depend
/// on the option).
///
-/// @return NULL on success, error message on error.
+/// @return NULL on success, an untranslated error message on error.
char *set_option_value(const char *const name, const long number, const char *const string,
const int opt_flags)
FUNC_ATTR_NONNULL_ARG(1)
@@ -3156,65 +2995,83 @@ char *set_option_value(const char *const name, const long number, const char *co
return NULL; // Fail silently; many old vimrcs set t_xx options.
}
- int opt_idx;
- char_u *varp;
-
- opt_idx = findoption(name);
+ int opt_idx = findoption(name);
if (opt_idx < 0) {
semsg(_("E355: Unknown option: %s"), name);
- } else {
- uint32_t flags = options[opt_idx].flags;
- // Disallow changing some options in the sandbox
- if (sandbox > 0 && (flags & P_SECURE)) {
- emsg(_(e_sandbox));
- return NULL;
+ return NULL;
+ }
+
+ uint32_t flags = options[opt_idx].flags;
+ // Disallow changing some options in the sandbox
+ if (sandbox > 0 && (flags & P_SECURE)) {
+ emsg(_(e_sandbox));
+ return NULL;
+ }
+
+ if (flags & P_STRING) {
+ const char *s = string;
+ if (s == NULL || opt_flags & OPT_CLEAR) {
+ s = "";
}
- if (flags & P_STRING) {
- const char *s = string;
- if (s == NULL || opt_flags & OPT_CLEAR) {
- s = "";
- }
- return set_string_option(opt_idx, s, opt_flags);
- }
-
- 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, &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 set_string_option(opt_idx, s, opt_flags);
+ }
+
+ char_u *varp = (char_u *)get_varp_scope(&(options[opt_idx]), opt_flags);
+ if (varp == NULL) {
+ // hidden option is not changed
+ return NULL;
+ }
+
+ 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, &s, NULL, OPT_GLOBAL);
}
}
- return NULL;
+ if (flags & P_NUM) {
+ return set_num_option(opt_idx, varp, numval, NULL, 0, opt_flags);
+ }
+ return set_bool_option(opt_idx, varp, (int)numval, opt_flags);
+}
+
+/// Call set_option_value() and when an error is returned report it.
+///
+/// @param opt_flags OPT_LOCAL or 0 (both)
+void set_option_value_give_err(const char *name, long number, const char *string, int opt_flags)
+{
+ char *errmsg = set_option_value(name, number, string, opt_flags);
+
+ if (errmsg != NULL) {
+ emsg(_(errmsg));
+ }
+}
+
+bool is_option_allocated(const char *name)
+{
+ int idx = findoption(name);
+ return idx >= 0 && (options[idx].flags & P_ALLOCED);
}
/// Return true if "name" is a string option.
@@ -3228,19 +3085,18 @@ bool is_string_option(const char *name)
// Translate a string like "t_xx", "<t_xx>" or "<S-Tab>" to a key number.
// When "has_lt" is true there is a '<' before "*arg_arg".
// Returns 0 when the key is not recognized.
-int find_key_option_len(const char_u *arg_arg, size_t len, bool has_lt)
+int find_key_option_len(const char *arg_arg, size_t len, bool has_lt)
{
int key = 0;
- int modifiers;
- const char_u *arg = arg_arg;
+ const char *arg = arg_arg;
// Don't use get_special_key_code() for t_xx, we don't want it to call
// add_termcap_entry().
if (len >= 4 && arg[0] == 't' && arg[1] == '_') {
- key = TERMCAP2KEY(arg[2], arg[3]);
+ key = TERMCAP2KEY((uint8_t)arg[2], (uint8_t)arg[3]);
} else if (has_lt) {
arg--; // put arg at the '<'
- modifiers = 0;
+ int modifiers = 0;
key = find_special_key(&arg, len + 1, &modifiers,
FSK_KEYCODE | FSK_KEEP_X_KEY | FSK_SIMPLIFY, NULL);
if (modifiers) { // can't handle modifiers here
@@ -3250,9 +3106,9 @@ int find_key_option_len(const char_u *arg_arg, size_t len, bool has_lt)
return key;
}
-static int find_key_option(const char_u *arg, bool has_lt)
+static int find_key_option(const char *arg, bool has_lt)
{
- return find_key_option_len(arg, STRLEN(arg), has_lt);
+ return find_key_option_len(arg, strlen(arg), has_lt);
}
/// if 'all' == 0: show changed options
@@ -3261,16 +3117,6 @@ static int find_key_option(const char_u *arg, bool has_lt)
/// @param opt_flags OPT_LOCAL and/or OPT_GLOBAL
static void showoptions(int all, int opt_flags)
{
- vimoption_T *p;
- int col;
- char_u *varp;
- int item_count;
- int run;
- int row, rows;
- int cols;
- int i;
- int len;
-
#define INC 20
#define GAP 3
@@ -3289,32 +3135,33 @@ static void showoptions(int all, int opt_flags)
// 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++) {
+ for (int run = 1; run <= 2 && !got_int; run++) {
// collect the items in items[]
- item_count = 0;
- for (p = &options[0]; p->fullname != NULL; p++) {
+ int item_count = 0;
+ for (vimoption_T *p = &options[0]; p->fullname != NULL; p++) {
// apply :filter /pat/
- if (message_filtered((char_u *)p->fullname)) {
+ if (message_filtered(p->fullname)) {
continue;
}
- varp = NULL;
+ char_u *varp = NULL;
if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) != 0) {
if (p->indir != PV_NONE) {
- varp = get_varp_scope(p, opt_flags);
+ varp = (char_u *)get_varp_scope(p, opt_flags);
}
} else {
varp = get_varp(p);
}
if (varp != NULL
&& (all == 1 || (all == 0 && !optval_default(p, varp)))) {
+ int len;
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((char *)NameBuff) + 1;
+ len = (int)strlen(p->fullname) + vim_strsize(NameBuff) + 1;
}
if ((len <= INC - GAP && run == 1)
|| (len > INC - GAP && run == 2)) {
@@ -3323,15 +3170,15 @@ static void showoptions(int all, int opt_flags)
}
}
- /*
- * display the items
- */
+ int rows;
+
+ // display the items
if (run == 1) {
assert(Columns <= INT_MAX - GAP
&& Columns + GAP >= INT_MIN + 3
&& (Columns + GAP - 3) / INC >= INT_MIN
&& (Columns + GAP - 3) / INC <= INT_MAX);
- cols = (Columns + GAP - 3) / INC;
+ int cols = (Columns + GAP - 3) / INC;
if (cols == 0) {
cols = 1;
}
@@ -3339,18 +3186,17 @@ static void showoptions(int all, int opt_flags)
} else { // run == 2
rows = item_count;
}
- for (row = 0; row < rows && !got_int; row++) {
+ for (int row = 0; row < rows && !got_int; row++) {
msg_putchar('\n'); // go to next line
if (got_int) { // 'q' typed in more
break;
}
- col = 0;
- for (i = row; i < item_count; i += rows) {
+ int col = 0;
+ for (int i = row; i < item_count; i += rows) {
msg_col = col; // make columns
showoneopt(items[i], opt_flags);
col += INC;
}
- ui_flush();
os_breakcheck();
}
}
@@ -3358,7 +3204,7 @@ static void showoptions(int all, int opt_flags)
}
/// Return true if option "p" has its default value.
-static int optval_default(vimoption_T *p, char_u *varp)
+static int optval_default(vimoption_T *p, const char_u *varp)
{
if (varp == NULL) {
return true; // hidden option is always at default
@@ -3370,7 +3216,7 @@ static int optval_default(vimoption_T *p, char_u *varp)
return *(int *)varp == (int)(intptr_t)p->def_val;
}
// P_STRING
- return STRCMP(*(char_u **)varp, p->def_val) == 0;
+ return strcmp(*(char **)varp, p->def_val) == 0;
}
/// Send update to UIs with values of UI relevant options
@@ -3405,13 +3251,12 @@ void ui_refresh_options(void)
/// @param opt_flags OPT_LOCAL or OPT_GLOBAL
static void showoneopt(vimoption_T *p, int opt_flags)
{
- char_u *varp;
int save_silent = silent_mode;
silent_mode = false;
- info_message = true; // use mch_msg(), not mch_errmsg()
+ info_message = true; // use os_msg(), not os_errmsg()
- varp = get_varp_scope(p, opt_flags);
+ char_u *varp = (char_u *)get_varp_scope(p, opt_flags);
// for 'modified' we also need to check if 'ff' or 'fenc' changed.
if ((p->flags & P_BOOL) && ((int *)varp == &curbuf->b_changed
@@ -3427,7 +3272,7 @@ static void showoneopt(vimoption_T *p, int opt_flags)
msg_putchar('=');
// put value string in NameBuff
option_value2string(p, opt_flags);
- msg_outtrans((char *)NameBuff);
+ msg_outtrans(NameBuff);
}
silent_mode = save_silent;
@@ -3456,25 +3301,15 @@ static void showoneopt(vimoption_T *p, int opt_flags)
/// Return FAIL on error, OK otherwise.
int makeset(FILE *fd, int opt_flags, int local_only)
{
- vimoption_T *p;
- char_u *varp; // currently used value
- char_u *varp_fresh; // local value
- char_u *varp_local = NULL; // fresh value
- char *cmd;
- int round;
- int pri;
-
- /*
- * Some options are never written:
- * - Options that don't have a default (terminal name, columns, lines).
- * - Terminal options.
- * - Hidden options.
- *
- * Do the loop over "options[]" twice: once for options with the
- * P_PRI_MKRC flag and once without.
- */
- for (pri = 1; pri >= 0; pri--) {
- for (p = &options[0]; p->fullname; p++) {
+ // Some options are never written:
+ // - Options that don't have a default (terminal name, columns, lines).
+ // - Terminal options.
+ // - Hidden options.
+ //
+ // Do the loop over "options[]" twice: once for options with the
+ // P_PRI_MKRC flag and once without.
+ for (int pri = 1; pri >= 0; pri--) {
+ for (vimoption_T *p = &options[0]; p->fullname; p++) {
if (!(p->flags & P_NO_MKRC)
&& ((pri == 1) == ((p->flags & P_PRI_MKRC) != 0))) {
// skip global option when only doing locals
@@ -3488,13 +3323,13 @@ int makeset(FILE *fd, int opt_flags, int local_only)
continue;
}
- varp = get_varp_scope(p, opt_flags);
+ char *varp = get_varp_scope(p, opt_flags); // currently used value
// Hidden options are never written.
if (!varp) {
continue;
}
// Global values are only written when not at the default value.
- if ((opt_flags & OPT_GLOBAL) && optval_default(p, varp)) {
+ if ((opt_flags & OPT_GLOBAL) && optval_default(p, (char_u *)varp)) {
continue;
}
@@ -3503,7 +3338,8 @@ int makeset(FILE *fd, int opt_flags, int local_only)
continue;
}
- round = 2;
+ int round = 2;
+ char_u *varp_local = NULL; // fresh value
if (p->indir != PV_NONE) {
if (p->var == VAR_WIN) {
// skip window-local option when only doing globals
@@ -3513,11 +3349,11 @@ int makeset(FILE *fd, int opt_flags, int local_only)
// When fresh value of window-local option is not at the
// default, need to write it too.
if (!(opt_flags & OPT_GLOBAL) && !local_only) {
- varp_fresh = get_varp_scope(p, OPT_GLOBAL);
+ char_u *varp_fresh = (char_u *)get_varp_scope(p, OPT_GLOBAL); // local value
if (!optval_default(p, varp_fresh)) {
round = 1;
- varp_local = varp;
- varp = varp_fresh;
+ varp_local = (char_u *)varp;
+ varp = (char *)varp_fresh;
}
}
}
@@ -3525,7 +3361,8 @@ int makeset(FILE *fd, int opt_flags, int local_only)
// Round 1: fresh value for window-local options.
// Round 2: other values
- for (; round <= 2; varp = varp_local, round++) {
+ for (; round <= 2; varp = (char *)varp_local, round++) {
+ char *cmd;
if (round == 1 || (opt_flags & OPT_GLOBAL)) {
cmd = "set";
} else {
@@ -3553,8 +3390,7 @@ int makeset(FILE *fd, int opt_flags, int local_only)
}
do_endif = true;
}
- if (put_setstring(fd, cmd, p->fullname, (char_u **)varp,
- p->flags) == FAIL) {
+ if (put_setstring(fd, cmd, p->fullname, (char **)varp, p->flags) == FAIL) {
return FAIL;
}
if (do_endif) {
@@ -3575,65 +3411,58 @@ int makeset(FILE *fd, int opt_flags, int local_only)
int makefoldset(FILE *fd)
{
if (put_setstring(fd, "setlocal", "fdm", &curwin->w_p_fdm, 0) == FAIL
- || put_setstring(fd, "setlocal", "fde", &curwin->w_p_fde, 0)
- == FAIL
- || put_setstring(fd, "setlocal", "fmr", &curwin->w_p_fmr, 0)
- == FAIL
- || put_setstring(fd, "setlocal", "fdi", &curwin->w_p_fdi, 0)
- == FAIL
+ || put_setstring(fd, "setlocal", "fde", &curwin->w_p_fde, 0) == FAIL
+ || put_setstring(fd, "setlocal", "fmr", &curwin->w_p_fmr, 0) == FAIL
+ || put_setstring(fd, "setlocal", "fdi", &curwin->w_p_fdi, 0) == FAIL
|| put_setnum(fd, "setlocal", "fdl", &curwin->w_p_fdl) == FAIL
|| put_setnum(fd, "setlocal", "fml", &curwin->w_p_fml) == FAIL
|| put_setnum(fd, "setlocal", "fdn", &curwin->w_p_fdn) == FAIL
- || put_setbool(fd, "setlocal", "fen",
- curwin->w_p_fen) == FAIL) {
+ || put_setbool(fd, "setlocal", "fen", curwin->w_p_fen) == FAIL) {
return FAIL;
}
return OK;
}
-static int put_setstring(FILE *fd, char *cmd, char *name, char_u **valuep, uint64_t flags)
+static int put_setstring(FILE *fd, char *cmd, char *name, char **valuep, uint64_t flags)
{
- char_u *s;
- char_u *buf = NULL;
- char_u *part = NULL;
- char *p;
-
if (fprintf(fd, "%s %s=", cmd, name) < 0) {
return FAIL;
}
+
+ char *buf = NULL;
+ char_u *part = NULL;
+
if (*valuep != NULL) {
// Output 'pastetoggle' as key names. For other
// options some characters have to be escaped with
// CTRL-V or backslash
if (valuep == &p_pt) {
- s = *valuep;
+ char_u *s = (char_u *)(*valuep);
while (*s != NUL) {
- if (put_escstr(fd, (char_u *)str2special((const char **)&s, false,
- false), 2)
- == FAIL) {
+ if (put_escstr(fd, (char *)str2special((const char **)&s, false, false), 2) == FAIL) {
return FAIL;
}
}
} else if ((flags & P_EXPAND) != 0) {
- size_t size = (size_t)STRLEN(*valuep) + 1;
+ size_t size = (size_t)strlen(*valuep) + 1;
// replace home directory in the whole option value into "buf"
buf = xmalloc(size);
- home_replace(NULL, (char *)(*valuep), (char *)buf, size, false);
+ home_replace(NULL, *valuep, 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((char *)(*valuep), ',') != NULL) {
+ && vim_strchr(*valuep, ',') != NULL) {
part = xmalloc(size);
// write line break to clear the option, e.g. ':set rtp='
if (put_eol(fd) == FAIL) {
goto fail;
}
- p = (char *)buf;
+ char *p = buf;
while (*p != NUL) {
// for each comma separated option part, append value to
// the option, :set rtp+=value
@@ -3641,7 +3470,7 @@ static int put_setstring(FILE *fd, char *cmd, char *name, char_u **valuep, uint6
goto fail;
}
(void)copy_option_part(&p, (char *)part, size, ",");
- if (put_escstr(fd, part, 2) == FAIL || put_eol(fd) == FAIL) {
+ if (put_escstr(fd, (char *)part, 2) == FAIL || put_eol(fd) == FAIL) {
goto fail;
}
}
@@ -3670,11 +3499,10 @@ fail:
static int put_setnum(FILE *fd, char *cmd, char *name, long *valuep)
{
- long wc;
-
if (fprintf(fd, "%s %s=", cmd, name) < 0) {
return FAIL;
}
+ long wc;
if (wc_use_keyname((char_u *)valuep, &wc)) {
// print 'wildchar' and 'wildcharm' as a key name
if (fputs((char *)get_special_key_name((int)wc, 0), fd) < 0) {
@@ -3714,8 +3542,7 @@ void unset_global_local_option(char *name, void *from)
}
p = &(options[opt_idx]);
- switch ((int)p->indir)
- {
+ switch ((int)p->indir) {
// global option with local value: use local value if it's been set
case PV_EP:
clear_string_option(&buf->b_p_ep);
@@ -3780,7 +3607,7 @@ void unset_global_local_option(char *name, void *from)
clear_string_option(&((win_T *)from)->w_p_stl);
break;
case PV_WBR:
- clear_string_option((char_u **)&((win_T *)from)->w_p_wbr);
+ clear_string_option(&((win_T *)from)->w_p_wbr);
break;
case PV_UL:
buf->b_p_ul = NO_LOCAL_UNDOLEVEL;
@@ -3794,102 +3621,104 @@ void unset_global_local_option(char *name, void *from)
case PV_LCS:
clear_string_option(&((win_T *)from)->w_p_lcs);
set_chars_option((win_T *)from, &((win_T *)from)->w_p_lcs, true);
- redraw_later((win_T *)from, NOT_VALID);
+ redraw_later((win_T *)from, UPD_NOT_VALID);
break;
case PV_FCS:
clear_string_option(&((win_T *)from)->w_p_fcs);
set_chars_option((win_T *)from, &((win_T *)from)->w_p_fcs, true);
- redraw_later((win_T *)from, NOT_VALID);
+ redraw_later((win_T *)from, UPD_NOT_VALID);
break;
case PV_VE:
clear_string_option(&((win_T *)from)->w_p_ve);
((win_T *)from)->w_ve_flags = 0;
break;
+ case PV_STC:
+ clear_string_option(&((win_T *)from)->w_p_stc);
+ break;
}
}
-/// Get pointer to option variable, depending on local or global scope.
-static char_u *get_varp_scope(vimoption_T *p, int opt_flags)
+char *get_varp_scope_from(vimoption_T *p, int scope, buf_T *buf, win_T *win)
{
- if ((opt_flags & OPT_GLOBAL) && p->indir != PV_NONE) {
+ if ((scope & OPT_GLOBAL) && p->indir != PV_NONE) {
if (p->var == VAR_WIN) {
- return (char_u *)GLOBAL_WO(get_varp(p));
+ return GLOBAL_WO(get_varp_from(p, buf, win));
}
- return p->var;
+ return (char *)p->var;
}
- if ((opt_flags & OPT_LOCAL) && ((int)p->indir & PV_BOTH)) {
+ if ((scope & OPT_LOCAL) && ((int)p->indir & PV_BOTH)) {
switch ((int)p->indir) {
case PV_FP:
- return (char_u *)&(curbuf->b_p_fp);
+ return (char *)&(buf->b_p_fp);
case PV_EFM:
- return (char_u *)&(curbuf->b_p_efm);
+ return (char *)&(buf->b_p_efm);
case PV_GP:
- return (char_u *)&(curbuf->b_p_gp);
+ return (char *)&(buf->b_p_gp);
case PV_MP:
- return (char_u *)&(curbuf->b_p_mp);
+ return (char *)&(buf->b_p_mp);
case PV_EP:
- return (char_u *)&(curbuf->b_p_ep);
+ return (char *)&(buf->b_p_ep);
case PV_KP:
- return (char_u *)&(curbuf->b_p_kp);
+ return (char *)&(buf->b_p_kp);
case PV_PATH:
- return (char_u *)&(curbuf->b_p_path);
+ return (char *)&(buf->b_p_path);
case PV_AR:
- return (char_u *)&(curbuf->b_p_ar);
+ return (char *)&(buf->b_p_ar);
case PV_TAGS:
- return (char_u *)&(curbuf->b_p_tags);
+ return (char *)&(buf->b_p_tags);
case PV_TC:
- return (char_u *)&(curbuf->b_p_tc);
+ return (char *)&(buf->b_p_tc);
case PV_SISO:
- return (char_u *)&(curwin->w_p_siso);
+ return (char *)&(win->w_p_siso);
case PV_SO:
- return (char_u *)&(curwin->w_p_so);
+ return (char *)&(win->w_p_so);
case PV_DEF:
- return (char_u *)&(curbuf->b_p_def);
+ return (char *)&(buf->b_p_def);
case PV_INC:
- return (char_u *)&(curbuf->b_p_inc);
+ return (char *)&(buf->b_p_inc);
case PV_DICT:
- return (char_u *)&(curbuf->b_p_dict);
+ return (char *)&(buf->b_p_dict);
case PV_TSR:
- return (char_u *)&(curbuf->b_p_tsr);
+ return (char *)&(buf->b_p_tsr);
case PV_TSRFU:
- return (char_u *)&(curbuf->b_p_tsrfu);
+ return (char *)&(buf->b_p_tsrfu);
case PV_TFU:
- return (char_u *)&(curbuf->b_p_tfu);
+ return (char *)&(buf->b_p_tfu);
case PV_SBR:
- return (char_u *)&(curwin->w_p_sbr);
+ return (char *)&(win->w_p_sbr);
case PV_STL:
- return (char_u *)&(curwin->w_p_stl);
+ return (char *)&(win->w_p_stl);
case PV_WBR:
- return (char_u *)&(curwin->w_p_wbr);
+ return (char *)&(win->w_p_wbr);
case PV_UL:
- return (char_u *)&(curbuf->b_p_ul);
+ return (char *)&(buf->b_p_ul);
case PV_LW:
- return (char_u *)&(curbuf->b_p_lw);
+ return (char *)&(buf->b_p_lw);
case PV_BKC:
- return (char_u *)&(curbuf->b_p_bkc);
+ return (char *)&(buf->b_p_bkc);
case PV_MENC:
- return (char_u *)&(curbuf->b_p_menc);
+ return (char *)&(buf->b_p_menc);
case PV_FCS:
- return (char_u *)&(curwin->w_p_fcs);
+ return (char *)&(win->w_p_fcs);
case PV_LCS:
- return (char_u *)&(curwin->w_p_lcs);
+ return (char *)&(win->w_p_lcs);
case PV_VE:
- return (char_u *)&(curwin->w_p_ve);
+ return (char *)&(win->w_p_ve);
}
return NULL; // "cannot happen"
}
- return get_varp(p);
+ return (char *)get_varp_from(p, buf, win);
}
-/// Get pointer to option variable at 'opt_idx', depending on local or global
-/// scope.
-char_u *get_option_varp_scope(int opt_idx, int opt_flags)
+/// Get pointer to option variable, depending on local or global scope.
+///
+/// @param scope can be OPT_LOCAL, OPT_GLOBAL or a combination.
+char *get_varp_scope(vimoption_T *p, int scope)
{
- return get_varp_scope(&(options[opt_idx]), opt_flags);
+ return get_varp_scope_from(p, scope, curbuf, curwin);
}
-/// Get pointer to option variable.
-static char_u *get_varp(vimoption_T *p)
+static char_u *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win)
{
// hidden option, always return NULL
if (p->var == NULL) {
@@ -3902,317 +3731,317 @@ static char_u *get_varp(vimoption_T *p)
// global option with local value: use local value if it's been set
case PV_EP:
- return *curbuf->b_p_ep != NUL
- ? (char_u *)&curbuf->b_p_ep : p->var;
+ return *buf->b_p_ep != NUL
+ ? (char_u *)&buf->b_p_ep : p->var;
case PV_KP:
- return *curbuf->b_p_kp != NUL
- ? (char_u *)&curbuf->b_p_kp : p->var;
+ return *buf->b_p_kp != NUL
+ ? (char_u *)&buf->b_p_kp : p->var;
case PV_PATH:
- return *curbuf->b_p_path != NUL
- ? (char_u *)&(curbuf->b_p_path) : p->var;
+ return *buf->b_p_path != NUL
+ ? (char_u *)&(buf->b_p_path) : p->var;
case PV_AR:
- return curbuf->b_p_ar >= 0
- ? (char_u *)&(curbuf->b_p_ar) : p->var;
+ return buf->b_p_ar >= 0
+ ? (char_u *)&(buf->b_p_ar) : p->var;
case PV_TAGS:
- return *curbuf->b_p_tags != NUL
- ? (char_u *)&(curbuf->b_p_tags) : p->var;
+ return *buf->b_p_tags != NUL
+ ? (char_u *)&(buf->b_p_tags) : p->var;
case PV_TC:
- return *curbuf->b_p_tc != NUL
- ? (char_u *)&(curbuf->b_p_tc) : p->var;
+ return *buf->b_p_tc != NUL
+ ? (char_u *)&(buf->b_p_tc) : p->var;
case PV_SISO:
- return curwin->w_p_siso >= 0
- ? (char_u *)&(curwin->w_p_siso) : p->var;
+ return win->w_p_siso >= 0
+ ? (char_u *)&(win->w_p_siso) : p->var;
case PV_SO:
- return curwin->w_p_so >= 0
- ? (char_u *)&(curwin->w_p_so) : p->var;
+ return win->w_p_so >= 0
+ ? (char_u *)&(win->w_p_so) : p->var;
case PV_BKC:
- return *curbuf->b_p_bkc != NUL
- ? (char_u *)&(curbuf->b_p_bkc) : p->var;
+ return *buf->b_p_bkc != NUL
+ ? (char_u *)&(buf->b_p_bkc) : p->var;
case PV_DEF:
- return *curbuf->b_p_def != NUL
- ? (char_u *)&(curbuf->b_p_def) : p->var;
+ return *buf->b_p_def != NUL
+ ? (char_u *)&(buf->b_p_def) : p->var;
case PV_INC:
- return *curbuf->b_p_inc != NUL
- ? (char_u *)&(curbuf->b_p_inc) : p->var;
+ return *buf->b_p_inc != NUL
+ ? (char_u *)&(buf->b_p_inc) : p->var;
case PV_DICT:
- return *curbuf->b_p_dict != NUL
- ? (char_u *)&(curbuf->b_p_dict) : p->var;
+ return *buf->b_p_dict != NUL
+ ? (char_u *)&(buf->b_p_dict) : p->var;
case PV_TSR:
- return *curbuf->b_p_tsr != NUL
- ? (char_u *)&(curbuf->b_p_tsr) : p->var;
+ return *buf->b_p_tsr != NUL
+ ? (char_u *)&(buf->b_p_tsr) : p->var;
case PV_TSRFU:
- return *curbuf->b_p_tsrfu != NUL
- ? (char_u *)&(curbuf->b_p_tsrfu) : p->var;
+ return *buf->b_p_tsrfu != NUL
+ ? (char_u *)&(buf->b_p_tsrfu) : p->var;
case PV_FP:
- return *curbuf->b_p_fp != NUL
- ? (char_u *)&(curbuf->b_p_fp) : p->var;
+ return *buf->b_p_fp != NUL
+ ? (char_u *)&(buf->b_p_fp) : p->var;
case PV_EFM:
- return *curbuf->b_p_efm != NUL
- ? (char_u *)&(curbuf->b_p_efm) : p->var;
+ return *buf->b_p_efm != NUL
+ ? (char_u *)&(buf->b_p_efm) : p->var;
case PV_GP:
- return *curbuf->b_p_gp != NUL
- ? (char_u *)&(curbuf->b_p_gp) : p->var;
+ return *buf->b_p_gp != NUL
+ ? (char_u *)&(buf->b_p_gp) : p->var;
case PV_MP:
- return *curbuf->b_p_mp != NUL
- ? (char_u *)&(curbuf->b_p_mp) : p->var;
+ return *buf->b_p_mp != NUL
+ ? (char_u *)&(buf->b_p_mp) : p->var;
case PV_SBR:
- return *curwin->w_p_sbr != NUL
- ? (char_u *)&(curwin->w_p_sbr) : p->var;
+ return *win->w_p_sbr != NUL
+ ? (char_u *)&(win->w_p_sbr) : p->var;
case PV_STL:
- return *curwin->w_p_stl != NUL
- ? (char_u *)&(curwin->w_p_stl) : p->var;
+ return *win->w_p_stl != NUL
+ ? (char_u *)&(win->w_p_stl) : p->var;
case PV_WBR:
- return *curwin->w_p_wbr != NUL
- ? (char_u *)&(curwin->w_p_wbr) : p->var;
+ return *win->w_p_wbr != NUL
+ ? (char_u *)&(win->w_p_wbr) : p->var;
case PV_UL:
- return curbuf->b_p_ul != NO_LOCAL_UNDOLEVEL
- ? (char_u *)&(curbuf->b_p_ul) : p->var;
+ return buf->b_p_ul != NO_LOCAL_UNDOLEVEL
+ ? (char_u *)&(buf->b_p_ul) : p->var;
case PV_LW:
- return *curbuf->b_p_lw != NUL
- ? (char_u *)&(curbuf->b_p_lw) : p->var;
+ return *buf->b_p_lw != NUL
+ ? (char_u *)&(buf->b_p_lw) : p->var;
case PV_MENC:
- return *curbuf->b_p_menc != NUL
- ? (char_u *)&(curbuf->b_p_menc) : p->var;
+ return *buf->b_p_menc != NUL
+ ? (char_u *)&(buf->b_p_menc) : p->var;
case PV_FCS:
- return *curwin->w_p_fcs != NUL
- ? (char_u *)&(curwin->w_p_fcs) : p->var;
+ return *win->w_p_fcs != NUL
+ ? (char_u *)&(win->w_p_fcs) : p->var;
case PV_LCS:
- return *curwin->w_p_lcs != NUL
- ? (char_u *)&(curwin->w_p_lcs) : p->var;
+ return *win->w_p_lcs != NUL
+ ? (char_u *)&(win->w_p_lcs) : p->var;
case PV_VE:
- return *curwin->w_p_ve != NUL
- ? (char_u *)&curwin->w_p_ve : p->var;
+ return *win->w_p_ve != NUL
+ ? (char_u *)&win->w_p_ve : p->var;
case PV_ARAB:
- return (char_u *)&(curwin->w_p_arab);
+ return (char_u *)&(win->w_p_arab);
case PV_LIST:
- return (char_u *)&(curwin->w_p_list);
+ return (char_u *)&(win->w_p_list);
case PV_SPELL:
- return (char_u *)&(curwin->w_p_spell);
+ return (char_u *)&(win->w_p_spell);
case PV_CUC:
- return (char_u *)&(curwin->w_p_cuc);
+ return (char_u *)&(win->w_p_cuc);
case PV_CUL:
- return (char_u *)&(curwin->w_p_cul);
+ return (char_u *)&(win->w_p_cul);
case PV_CULOPT:
- return (char_u *)&(curwin->w_p_culopt);
+ return (char_u *)&(win->w_p_culopt);
case PV_CC:
- return (char_u *)&(curwin->w_p_cc);
+ return (char_u *)&(win->w_p_cc);
case PV_DIFF:
- return (char_u *)&(curwin->w_p_diff);
+ return (char_u *)&(win->w_p_diff);
case PV_FDC:
- return (char_u *)&(curwin->w_p_fdc);
+ return (char_u *)&(win->w_p_fdc);
case PV_FEN:
- return (char_u *)&(curwin->w_p_fen);
+ return (char_u *)&(win->w_p_fen);
case PV_FDI:
- return (char_u *)&(curwin->w_p_fdi);
+ return (char_u *)&(win->w_p_fdi);
case PV_FDL:
- return (char_u *)&(curwin->w_p_fdl);
+ return (char_u *)&(win->w_p_fdl);
case PV_FDM:
- return (char_u *)&(curwin->w_p_fdm);
+ return (char_u *)&(win->w_p_fdm);
case PV_FML:
- return (char_u *)&(curwin->w_p_fml);
+ return (char_u *)&(win->w_p_fml);
case PV_FDN:
- return (char_u *)&(curwin->w_p_fdn);
+ return (char_u *)&(win->w_p_fdn);
case PV_FDE:
- return (char_u *)&(curwin->w_p_fde);
+ return (char_u *)&(win->w_p_fde);
case PV_FDT:
- return (char_u *)&(curwin->w_p_fdt);
+ return (char_u *)&(win->w_p_fdt);
case PV_FMR:
- return (char_u *)&(curwin->w_p_fmr);
+ return (char_u *)&(win->w_p_fmr);
case PV_NU:
- return (char_u *)&(curwin->w_p_nu);
+ return (char_u *)&(win->w_p_nu);
case PV_RNU:
- return (char_u *)&(curwin->w_p_rnu);
+ return (char_u *)&(win->w_p_rnu);
case PV_NUW:
- return (char_u *)&(curwin->w_p_nuw);
+ return (char_u *)&(win->w_p_nuw);
case PV_WFH:
- return (char_u *)&(curwin->w_p_wfh);
+ return (char_u *)&(win->w_p_wfh);
case PV_WFW:
- return (char_u *)&(curwin->w_p_wfw);
+ return (char_u *)&(win->w_p_wfw);
case PV_PVW:
- return (char_u *)&(curwin->w_p_pvw);
+ return (char_u *)&(win->w_p_pvw);
case PV_RL:
- return (char_u *)&(curwin->w_p_rl);
+ return (char_u *)&(win->w_p_rl);
case PV_RLC:
- return (char_u *)&(curwin->w_p_rlc);
+ return (char_u *)&(win->w_p_rlc);
case PV_SCROLL:
- return (char_u *)&(curwin->w_p_scr);
+ return (char_u *)&(win->w_p_scr);
case PV_WRAP:
- return (char_u *)&(curwin->w_p_wrap);
+ return (char_u *)&(win->w_p_wrap);
case PV_LBR:
- return (char_u *)&(curwin->w_p_lbr);
+ return (char_u *)&(win->w_p_lbr);
case PV_BRI:
- return (char_u *)&(curwin->w_p_bri);
+ return (char_u *)&(win->w_p_bri);
case PV_BRIOPT:
- return (char_u *)&(curwin->w_p_briopt);
+ return (char_u *)&(win->w_p_briopt);
case PV_SCBIND:
- return (char_u *)&(curwin->w_p_scb);
+ return (char_u *)&(win->w_p_scb);
case PV_CRBIND:
- return (char_u *)&(curwin->w_p_crb);
+ return (char_u *)&(win->w_p_crb);
case PV_COCU:
- return (char_u *)&(curwin->w_p_cocu);
+ return (char_u *)&(win->w_p_cocu);
case PV_COLE:
- return (char_u *)&(curwin->w_p_cole);
+ return (char_u *)&(win->w_p_cole);
case PV_AI:
- return (char_u *)&(curbuf->b_p_ai);
+ return (char_u *)&(buf->b_p_ai);
case PV_BIN:
- return (char_u *)&(curbuf->b_p_bin);
+ return (char_u *)&(buf->b_p_bin);
case PV_BOMB:
- return (char_u *)&(curbuf->b_p_bomb);
+ return (char_u *)&(buf->b_p_bomb);
case PV_BH:
- return (char_u *)&(curbuf->b_p_bh);
+ return (char_u *)&(buf->b_p_bh);
case PV_BT:
- return (char_u *)&(curbuf->b_p_bt);
+ return (char_u *)&(buf->b_p_bt);
case PV_BL:
- return (char_u *)&(curbuf->b_p_bl);
+ return (char_u *)&(buf->b_p_bl);
case PV_CHANNEL:
- return (char_u *)&(curbuf->b_p_channel);
+ return (char_u *)&(buf->b_p_channel);
case PV_CI:
- return (char_u *)&(curbuf->b_p_ci);
+ return (char_u *)&(buf->b_p_ci);
case PV_CIN:
- return (char_u *)&(curbuf->b_p_cin);
+ return (char_u *)&(buf->b_p_cin);
case PV_CINK:
- return (char_u *)&(curbuf->b_p_cink);
+ return (char_u *)&(buf->b_p_cink);
case PV_CINO:
- return (char_u *)&(curbuf->b_p_cino);
+ return (char_u *)&(buf->b_p_cino);
case PV_CINSD:
- return (char_u *)&(curbuf->b_p_cinsd);
+ return (char_u *)&(buf->b_p_cinsd);
case PV_CINW:
- return (char_u *)&(curbuf->b_p_cinw);
+ return (char_u *)&(buf->b_p_cinw);
case PV_COM:
- return (char_u *)&(curbuf->b_p_com);
+ return (char_u *)&(buf->b_p_com);
case PV_CMS:
- return (char_u *)&(curbuf->b_p_cms);
+ return (char_u *)&(buf->b_p_cms);
case PV_CPT:
- return (char_u *)&(curbuf->b_p_cpt);
+ return (char_u *)&(buf->b_p_cpt);
#ifdef BACKSLASH_IN_FILENAME
case PV_CSL:
- return (char_u *)&(curbuf->b_p_csl);
+ return (char_u *)&(buf->b_p_csl);
#endif
case PV_CFU:
- return (char_u *)&(curbuf->b_p_cfu);
+ return (char_u *)&(buf->b_p_cfu);
case PV_OFU:
- return (char_u *)&(curbuf->b_p_ofu);
+ return (char_u *)&(buf->b_p_ofu);
+ case PV_EOF:
+ return (char_u *)&(buf->b_p_eof);
case PV_EOL:
- return (char_u *)&(curbuf->b_p_eol);
+ return (char_u *)&(buf->b_p_eol);
case PV_FIXEOL:
- return (char_u *)&(curbuf->b_p_fixeol);
+ return (char_u *)&(buf->b_p_fixeol);
case PV_ET:
- return (char_u *)&(curbuf->b_p_et);
+ return (char_u *)&(buf->b_p_et);
case PV_FENC:
- return (char_u *)&(curbuf->b_p_fenc);
+ return (char_u *)&(buf->b_p_fenc);
case PV_FF:
- return (char_u *)&(curbuf->b_p_ff);
+ return (char_u *)&(buf->b_p_ff);
case PV_FT:
- return (char_u *)&(curbuf->b_p_ft);
+ return (char_u *)&(buf->b_p_ft);
case PV_FO:
- return (char_u *)&(curbuf->b_p_fo);
+ return (char_u *)&(buf->b_p_fo);
case PV_FLP:
- return (char_u *)&(curbuf->b_p_flp);
+ return (char_u *)&(buf->b_p_flp);
case PV_IMI:
- return (char_u *)&(curbuf->b_p_iminsert);
+ return (char_u *)&(buf->b_p_iminsert);
case PV_IMS:
- return (char_u *)&(curbuf->b_p_imsearch);
+ return (char_u *)&(buf->b_p_imsearch);
case PV_INF:
- return (char_u *)&(curbuf->b_p_inf);
+ return (char_u *)&(buf->b_p_inf);
case PV_ISK:
- return (char_u *)&(curbuf->b_p_isk);
+ return (char_u *)&(buf->b_p_isk);
case PV_INEX:
- return (char_u *)&(curbuf->b_p_inex);
+ return (char_u *)&(buf->b_p_inex);
case PV_INDE:
- return (char_u *)&(curbuf->b_p_inde);
+ return (char_u *)&(buf->b_p_inde);
case PV_INDK:
- return (char_u *)&(curbuf->b_p_indk);
+ return (char_u *)&(buf->b_p_indk);
case PV_FEX:
- return (char_u *)&(curbuf->b_p_fex);
+ return (char_u *)&(buf->b_p_fex);
case PV_LISP:
- return (char_u *)&(curbuf->b_p_lisp);
+ return (char_u *)&(buf->b_p_lisp);
+ case PV_LOP:
+ return (char_u *)&(buf->b_p_lop);
case PV_ML:
- return (char_u *)&(curbuf->b_p_ml);
+ return (char_u *)&(buf->b_p_ml);
case PV_MPS:
- return (char_u *)&(curbuf->b_p_mps);
+ return (char_u *)&(buf->b_p_mps);
case PV_MA:
- return (char_u *)&(curbuf->b_p_ma);
+ return (char_u *)&(buf->b_p_ma);
case PV_MOD:
- return (char_u *)&(curbuf->b_changed);
+ return (char_u *)&(buf->b_changed);
case PV_NF:
- return (char_u *)&(curbuf->b_p_nf);
+ return (char_u *)&(buf->b_p_nf);
case PV_PI:
- return (char_u *)&(curbuf->b_p_pi);
+ return (char_u *)&(buf->b_p_pi);
case PV_QE:
- return (char_u *)&(curbuf->b_p_qe);
+ return (char_u *)&(buf->b_p_qe);
case PV_RO:
- return (char_u *)&(curbuf->b_p_ro);
+ return (char_u *)&(buf->b_p_ro);
case PV_SCBK:
- return (char_u *)&(curbuf->b_p_scbk);
+ return (char_u *)&(buf->b_p_scbk);
case PV_SI:
- return (char_u *)&(curbuf->b_p_si);
+ return (char_u *)&(buf->b_p_si);
case PV_STS:
- return (char_u *)&(curbuf->b_p_sts);
+ return (char_u *)&(buf->b_p_sts);
case PV_SUA:
- return (char_u *)&(curbuf->b_p_sua);
+ return (char_u *)&(buf->b_p_sua);
case PV_SWF:
- return (char_u *)&(curbuf->b_p_swf);
+ return (char_u *)&(buf->b_p_swf);
case PV_SMC:
- return (char_u *)&(curbuf->b_p_smc);
+ return (char_u *)&(buf->b_p_smc);
case PV_SYN:
- return (char_u *)&(curbuf->b_p_syn);
+ return (char_u *)&(buf->b_p_syn);
case PV_SPC:
- return (char_u *)&(curwin->w_s->b_p_spc);
+ return (char_u *)&(win->w_s->b_p_spc);
case PV_SPF:
- return (char_u *)&(curwin->w_s->b_p_spf);
+ return (char_u *)&(win->w_s->b_p_spf);
case PV_SPL:
- return (char_u *)&(curwin->w_s->b_p_spl);
+ return (char_u *)&(win->w_s->b_p_spl);
case PV_SPO:
- return (char_u *)&(curwin->w_s->b_p_spo);
+ return (char_u *)&(win->w_s->b_p_spo);
case PV_SW:
- return (char_u *)&(curbuf->b_p_sw);
+ return (char_u *)&(buf->b_p_sw);
case PV_TFU:
- return (char_u *)&(curbuf->b_p_tfu);
+ return (char_u *)&(buf->b_p_tfu);
case PV_UMF:
- return (char_u *)&(curbuf->b_p_umf);
+ return (char_u *)&(buf->b_p_umf);
case PV_TS:
- return (char_u *)&(curbuf->b_p_ts);
+ return (char_u *)&(buf->b_p_ts);
case PV_TW:
- return (char_u *)&(curbuf->b_p_tw);
+ return (char_u *)&(buf->b_p_tw);
case PV_UDF:
- return (char_u *)&(curbuf->b_p_udf);
+ return (char_u *)&(buf->b_p_udf);
case PV_WM:
- return (char_u *)&(curbuf->b_p_wm);
+ return (char_u *)&(buf->b_p_wm);
case PV_VSTS:
- return (char_u *)&(curbuf->b_p_vsts);
+ return (char_u *)&(buf->b_p_vsts);
case PV_VTS:
- return (char_u *)&(curbuf->b_p_vts);
+ return (char_u *)&(buf->b_p_vts);
case PV_KMAP:
- return (char_u *)&(curbuf->b_p_keymap);
+ return (char_u *)&(buf->b_p_keymap);
case PV_SCL:
- return (char_u *)&(curwin->w_p_scl);
+ return (char_u *)&(win->w_p_scl);
case PV_WINHL:
- return (char_u *)&(curwin->w_p_winhl);
+ return (char_u *)&(win->w_p_winhl);
case PV_WINBL:
- return (char_u *)&(curwin->w_p_winbl);
+ return (char_u *)&(win->w_p_winbl);
+ case PV_STC:
+ return (char_u *)&(win->w_p_stc);
default:
iemsg(_("E356: get_varp ERROR"));
}
// always return a valid pointer to avoid a crash!
- return (char_u *)&(curbuf->b_p_wm);
-}
-
-/// Return a pointer to the variable for option at 'opt_idx'
-char_u *get_option_var(int opt_idx)
-{
- return options[opt_idx].var;
+ return (char_u *)&(buf->b_p_wm);
}
-/// Return the full name of the option at 'opt_idx'
-char *get_option_fullname(int opt_idx)
+/// Get pointer to option variable.
+static inline char_u *get_varp(vimoption_T *p)
{
- return options[opt_idx].fullname;
+ return get_varp_from(p, curbuf, curwin);
}
/// Get the value of 'equalprg', either the buffer-local one or the global one.
-char_u *get_equalprg(void)
+char *get_equalprg(void)
{
if (*curbuf->b_p_ep == NUL) {
return p_ep;
@@ -4229,6 +4058,14 @@ void win_copy_options(win_T *wp_from, win_T *wp_to)
didset_window_options(wp_to, true);
}
+static char *copy_option_val(const char *val)
+{
+ if (val == empty_option) {
+ return empty_option; // no need to allocate memory
+ }
+ return xstrdup(val);
+}
+
/// Copy the options from one winopt_T to another.
/// Doesn't free the old option values in "to", use clear_winopt() for that.
/// The 'scroll' option is not copied, because it depends on the window height.
@@ -4237,21 +4074,23 @@ void copy_winopt(winopt_T *from, winopt_T *to)
{
to->wo_arab = from->wo_arab;
to->wo_list = from->wo_list;
+ to->wo_lcs = copy_option_val(from->wo_lcs);
+ to->wo_fcs = copy_option_val(from->wo_fcs);
to->wo_nu = from->wo_nu;
to->wo_rnu = from->wo_rnu;
- to->wo_ve = vim_strsave(from->wo_ve);
+ to->wo_ve = copy_option_val(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_rlc = copy_option_val(from->wo_rlc);
+ to->wo_sbr = copy_option_val(from->wo_sbr);
+ to->wo_stl = copy_option_val(from->wo_stl);
+ to->wo_wbr = copy_option_val(from->wo_wbr);
to->wo_wrap = from->wo_wrap;
to->wo_wrap_save = from->wo_wrap_save;
to->wo_lbr = from->wo_lbr;
to->wo_bri = from->wo_bri;
- to->wo_briopt = vim_strsave(from->wo_briopt);
+ to->wo_briopt = copy_option_val(from->wo_briopt);
to->wo_scb = from->wo_scb;
to->wo_scb_save = from->wo_scb_save;
to->wo_crb = from->wo_crb;
@@ -4259,33 +4098,30 @@ void copy_winopt(winopt_T *from, winopt_T *to)
to->wo_spell = from->wo_spell;
to->wo_cuc = from->wo_cuc;
to->wo_cul = from->wo_cul;
- to->wo_culopt = vim_strsave(from->wo_culopt);
- to->wo_cc = vim_strsave(from->wo_cc);
+ to->wo_culopt = copy_option_val(from->wo_culopt);
+ to->wo_cc = copy_option_val(from->wo_cc);
to->wo_diff = from->wo_diff;
to->wo_diff_saved = from->wo_diff_saved;
- to->wo_cocu = vim_strsave(from->wo_cocu);
+ to->wo_cocu = copy_option_val(from->wo_cocu);
to->wo_cole = from->wo_cole;
- to->wo_fdc = vim_strsave(from->wo_fdc);
- to->wo_fdc_save = from->wo_diff_saved
- ? vim_strsave(from->wo_fdc_save) : empty_option;
+ to->wo_fdc = copy_option_val(from->wo_fdc);
+ to->wo_fdc_save = from->wo_diff_saved ? xstrdup(from->wo_fdc_save) : empty_option;
to->wo_fen = from->wo_fen;
to->wo_fen_save = from->wo_fen_save;
- to->wo_fdi = vim_strsave(from->wo_fdi);
+ to->wo_fdi = copy_option_val(from->wo_fdi);
to->wo_fml = from->wo_fml;
to->wo_fdl = from->wo_fdl;
to->wo_fdl_save = from->wo_fdl_save;
- to->wo_fdm = vim_strsave(from->wo_fdm);
- to->wo_fdm_save = from->wo_diff_saved
- ? vim_strsave(from->wo_fdm_save) : empty_option;
+ to->wo_fdm = copy_option_val(from->wo_fdm);
+ to->wo_fdm_save = from->wo_diff_saved ? xstrdup(from->wo_fdm_save) : empty_option;
to->wo_fdn = from->wo_fdn;
- to->wo_fde = vim_strsave(from->wo_fde);
- to->wo_fdt = vim_strsave(from->wo_fdt);
- to->wo_fmr = vim_strsave(from->wo_fmr);
- to->wo_scl = vim_strsave(from->wo_scl);
- to->wo_winhl = vim_strsave(from->wo_winhl);
- to->wo_fcs = vim_strsave(from->wo_fcs);
- to->wo_lcs = vim_strsave(from->wo_lcs);
+ to->wo_fde = copy_option_val(from->wo_fde);
+ to->wo_fdt = copy_option_val(from->wo_fdt);
+ to->wo_fmr = copy_option_val(from->wo_fmr);
+ to->wo_scl = copy_option_val(from->wo_scl);
+ to->wo_winhl = copy_option_val(from->wo_winhl);
to->wo_winbl = from->wo_winbl;
+ to->wo_stc = copy_option_val(from->wo_stc);
// 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));
@@ -4319,10 +4155,11 @@ static void check_winopt(winopt_T *wop)
check_string_option(&wop->wo_cocu);
check_string_option(&wop->wo_briopt);
check_string_option(&wop->wo_winhl);
- check_string_option(&wop->wo_fcs);
check_string_option(&wop->wo_lcs);
+ check_string_option(&wop->wo_fcs);
check_string_option(&wop->wo_ve);
- check_string_option((char_u **)&wop->wo_wbr);
+ check_string_option(&wop->wo_wbr);
+ check_string_option(&wop->wo_stc);
}
/// Free the allocated memory inside a winopt_T.
@@ -4345,10 +4182,11 @@ void clear_winopt(winopt_T *wop)
clear_string_option(&wop->wo_cocu);
clear_string_option(&wop->wo_briopt);
clear_string_option(&wop->wo_winhl);
- clear_string_option(&wop->wo_fcs);
clear_string_option(&wop->wo_lcs);
+ clear_string_option(&wop->wo_fcs);
clear_string_option(&wop->wo_ve);
- clear_string_option((char_u **)&wop->wo_wbr);
+ clear_string_option(&wop->wo_wbr);
+ clear_string_option(&wop->wo_stc);
}
void didset_window_options(win_T *wp, bool valid_cursor)
@@ -4394,14 +4232,11 @@ static void init_buf_opt_idx(void)
void buf_copy_options(buf_T *buf, int flags)
{
int should_copy = true;
- char_u *save_p_isk = NULL; // init for GCC
- int dont_do_help;
+ char *save_p_isk = NULL; // init for GCC
int did_isk = false;
- /*
- * Skip this when the option defaults have not been set yet. Happens when
- * main() allocates the first buffer.
- */
+ // Skip this when the option defaults have not been set yet. Happens when
+ // main() allocates the first buffer.
if (p_cpo != NULL) {
//
// Always copy when entering and 'cpo' contains 'S'.
@@ -4428,7 +4263,7 @@ void buf_copy_options(buf_T *buf, int flags)
// 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;
+ bool 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;
@@ -4438,19 +4273,19 @@ void buf_copy_options(buf_T *buf, int flags)
if (!buf->b_p_initialized) {
free_buf_options(buf, true);
buf->b_p_ro = false; // don't copy readonly
- buf->b_p_fenc = vim_strsave(p_fenc);
+ buf->b_p_fenc = xstrdup(p_fenc);
switch (*p_ffs) {
case 'm':
- buf->b_p_ff = vim_strsave((char_u *)FF_MAC);
+ buf->b_p_ff = xstrdup(FF_MAC);
break;
case 'd':
- buf->b_p_ff = vim_strsave((char_u *)FF_DOS);
+ buf->b_p_ff = xstrdup(FF_DOS);
break;
case 'u':
- buf->b_p_ff = vim_strsave((char_u *)FF_UNIX);
+ buf->b_p_ff = xstrdup(FF_UNIX);
break;
default:
- buf->b_p_ff = vim_strsave(p_ff);
+ buf->b_p_ff = xstrdup(p_ff);
break;
}
buf->b_p_bh = empty_option;
@@ -4495,44 +4330,45 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_p_swf = p_swf;
COPY_OPT_SCTX(buf, BV_SWF);
}
- buf->b_p_cpt = vim_strsave(p_cpt);
+ buf->b_p_cpt = xstrdup(p_cpt);
COPY_OPT_SCTX(buf, BV_CPT);
#ifdef BACKSLASH_IN_FILENAME
- buf->b_p_csl = vim_strsave(p_csl);
+ buf->b_p_csl = xstrdup(p_csl);
COPY_OPT_SCTX(buf, BV_CSL);
#endif
- buf->b_p_cfu = vim_strsave(p_cfu);
+ buf->b_p_cfu = xstrdup(p_cfu);
COPY_OPT_SCTX(buf, BV_CFU);
- buf->b_p_ofu = vim_strsave(p_ofu);
+ set_buflocal_cfu_callback(buf);
+ buf->b_p_ofu = xstrdup(p_ofu);
COPY_OPT_SCTX(buf, BV_OFU);
- buf->b_p_tfu = vim_strsave(p_tfu);
+ set_buflocal_ofu_callback(buf);
+ buf->b_p_tfu = xstrdup(p_tfu);
COPY_OPT_SCTX(buf, BV_TFU);
- buf->b_p_umf = vim_strsave(p_umf);
+ buf->b_p_umf = xstrdup(p_umf);
COPY_OPT_SCTX(buf, BV_UMF);
+ set_buflocal_tfu_callback(buf);
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);
+ buf->b_p_vsts = xstrdup(p_vsts);
COPY_OPT_SCTX(buf, BV_VSTS);
if (p_vsts && p_vsts != empty_option) {
(void)tabstop_set(p_vsts, &buf->b_p_vsts_array);
} else {
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);
+ buf->b_p_vsts_nopaste = p_vsts_nopaste ? xstrdup(p_vsts_nopaste) : NULL;
+ buf->b_p_com = xstrdup(p_com);
COPY_OPT_SCTX(buf, BV_COM);
- buf->b_p_cms = vim_strsave(p_cms);
+ buf->b_p_cms = xstrdup(p_cms);
COPY_OPT_SCTX(buf, BV_CMS);
- buf->b_p_fo = vim_strsave(p_fo);
+ buf->b_p_fo = xstrdup(p_fo);
COPY_OPT_SCTX(buf, BV_FO);
- buf->b_p_flp = vim_strsave(p_flp);
+ buf->b_p_flp = xstrdup(p_flp);
COPY_OPT_SCTX(buf, BV_FLP);
- buf->b_p_nf = vim_strsave(p_nf);
+ buf->b_p_nf = xstrdup(p_nf);
COPY_OPT_SCTX(buf, BV_NF);
- buf->b_p_mps = vim_strsave(p_mps);
+ buf->b_p_mps = xstrdup(p_mps);
COPY_OPT_SCTX(buf, BV_MPS);
buf->b_p_si = p_si;
COPY_OPT_SCTX(buf, BV_SI);
@@ -4542,18 +4378,20 @@ void buf_copy_options(buf_T *buf, int flags)
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);
+ buf->b_p_cink = xstrdup(p_cink);
COPY_OPT_SCTX(buf, BV_CINK);
- buf->b_p_cino = vim_strsave(p_cino);
+ buf->b_p_cino = xstrdup(p_cino);
COPY_OPT_SCTX(buf, BV_CINO);
- buf->b_p_cinsd = vim_strsave(p_cinsd);
+ buf->b_p_cinsd = xstrdup(p_cinsd);
COPY_OPT_SCTX(buf, BV_CINSD);
+ buf->b_p_lop = xstrdup(p_lop);
+ COPY_OPT_SCTX(buf, BV_LOP);
// 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);
+ buf->b_p_cinw = xstrdup(p_cinw);
COPY_OPT_SCTX(buf, BV_CINW);
buf->b_p_lisp = p_lisp;
COPY_OPT_SCTX(buf, BV_LISP);
@@ -4562,25 +4400,25 @@ void buf_copy_options(buf_T *buf, int flags)
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);
+ buf->b_s.b_p_spc = xstrdup(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);
+ buf->b_s.b_p_spf = xstrdup(p_spf);
COPY_OPT_SCTX(buf, BV_SPF);
- buf->b_s.b_p_spl = vim_strsave(p_spl);
+ buf->b_s.b_p_spl = xstrdup(p_spl);
COPY_OPT_SCTX(buf, BV_SPL);
- buf->b_s.b_p_spo = vim_strsave(p_spo);
+ buf->b_s.b_p_spo = xstrdup(p_spo);
COPY_OPT_SCTX(buf, BV_SPO);
- buf->b_p_inde = vim_strsave(p_inde);
+ buf->b_p_inde = xstrdup(p_inde);
COPY_OPT_SCTX(buf, BV_INDE);
- buf->b_p_indk = vim_strsave(p_indk);
+ buf->b_p_indk = xstrdup(p_indk);
COPY_OPT_SCTX(buf, BV_INDK);
buf->b_p_fp = empty_option;
- buf->b_p_fex = vim_strsave(p_fex);
+ buf->b_p_fex = xstrdup(p_fex);
COPY_OPT_SCTX(buf, BV_FEX);
- buf->b_p_sua = vim_strsave(p_sua);
+ buf->b_p_sua = xstrdup(p_sua);
COPY_OPT_SCTX(buf, BV_SUA);
- buf->b_p_keymap = vim_strsave(p_keymap);
+ buf->b_p_keymap = xstrdup(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
@@ -4607,24 +4445,22 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_tc_flags = 0;
buf->b_p_def = empty_option;
buf->b_p_inc = empty_option;
- buf->b_p_inex = vim_strsave(p_inex);
+ buf->b_p_inex = xstrdup(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);
+ buf->b_p_qe = xstrdup(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;
- /*
- * Don't copy the options set by ex_help(), use the saved values,
- * when going from a help buffer to a non-help buffer.
- * Don't touch these at all when BCO_NOHELP is used and going from
- * or to a help buffer.
- */
+ // Don't copy the options set by ex_help(), use the saved values,
+ // when going from a help buffer to a non-help buffer.
+ // Don't touch these at all when BCO_NOHELP is used and going from
+ // or to a help buffer.
if (dont_do_help) {
buf->b_p_isk = save_p_isk;
if (p_vts && p_vts != empty_option && !buf->b_p_vts_array) {
@@ -4633,12 +4469,12 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_p_vts_array = NULL;
}
} else {
- buf->b_p_isk = vim_strsave(p_isk);
+ buf->b_p_isk = xstrdup(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);
+ buf->b_p_vts = xstrdup(p_vts);
COPY_OPT_SCTX(buf, BV_VTS);
if (p_vts && p_vts != empty_option && !buf->b_p_vts_array) {
(void)tabstop_set(p_vts, &buf->b_p_vts_array);
@@ -4654,10 +4490,8 @@ void buf_copy_options(buf_T *buf, int flags)
}
}
- /*
- * When the options should be copied (ignoring BCO_ALWAYS), set the
- * flag that indicates that the options have been initialized.
- */
+ // When the options should be copied (ignoring BCO_ALWAYS), set the
+ // flag that indicates that the options have been initialized.
if (should_copy) {
buf->b_p_initialized = true;
}
@@ -4683,15 +4517,15 @@ void reset_modifiable(void)
}
/// Set the global value for 'iminsert' to the local value.
-void set_iminsert_global(void)
+void set_iminsert_global(buf_T *buf)
{
- p_iminsert = curbuf->b_p_iminsert;
+ p_iminsert = buf->b_p_iminsert;
}
/// Set the global value for 'imsearch' to the local value.
-void set_imsearch_global(void)
+void set_imsearch_global(buf_T *buf)
{
- p_imsearch = curbuf->b_p_imsearch;
+ p_imsearch = buf->b_p_imsearch;
}
static int expand_option_idx = -1;
@@ -4699,30 +4533,22 @@ static char_u expand_option_name[5] = { 't', '_', NUL, NUL, NUL };
static int expand_option_flags = 0;
/// @param opt_flags OPT_GLOBAL and/or OPT_LOCAL
-void set_context_in_set_cmd(expand_T *xp, char_u *arg, int opt_flags)
+void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags)
{
- char_u nextchar;
- uint32_t flags = 0; // init for GCC
- int opt_idx = 0; // init for GCC
- char_u *p;
- char_u *s;
- int is_term_option = false;
- int key;
-
expand_option_flags = opt_flags;
xp->xp_context = EXPAND_SETTINGS;
if (*arg == NUL) {
- xp->xp_pattern = (char *)arg;
+ xp->xp_pattern = arg;
return;
}
- p = arg + STRLEN(arg) - 1;
+ char *p = arg + strlen(arg) - 1;
if (*p == ' ' && *(p - 1) != '\\') {
- xp->xp_pattern = (char *)p + 1;
+ xp->xp_pattern = p + 1;
return;
}
while (p > arg) {
- s = p;
+ char *s = p;
// count number of backslashes before ' ' or ','
if (*p == ' ' || *p == ',') {
while (s > arg && *(s - 1) == '\\') {
@@ -4736,23 +4562,29 @@ void set_context_in_set_cmd(expand_T *xp, char_u *arg, int opt_flags)
}
p--;
}
- if (STRNCMP(p, "no", 2) == 0) {
+ if (strncmp(p, "no", 2) == 0) {
xp->xp_context = EXPAND_BOOL_SETTINGS;
p += 2;
}
- if (STRNCMP(p, "inv", 3) == 0) {
+ if (strncmp(p, "inv", 3) == 0) {
xp->xp_context = EXPAND_BOOL_SETTINGS;
p += 3;
}
- xp->xp_pattern = (char *)p;
+ xp->xp_pattern = p;
arg = p;
+
+ char nextchar;
+ uint32_t flags = 0;
+ int opt_idx = 0;
+ int is_term_option = false;
+
if (*arg == '<') {
while (*p != '>') {
if (*p++ == NUL) { // expand terminal option name
return;
}
}
- key = get_special_key_code(arg + 1);
+ int key = get_special_key_code((char_u *)arg + 1);
if (key == 0) { // unknown name
xp->xp_context = EXPAND_NOTHING;
return;
@@ -4772,8 +4604,8 @@ void set_context_in_set_cmd(expand_T *xp, char_u *arg, int opt_flags)
}
nextchar = *++p;
is_term_option = true;
- expand_option_name[2] = p[-2];
- expand_option_name[3] = p[-1];
+ expand_option_name[2] = (char_u)p[-2];
+ expand_option_name[3] = (char_u)p[-1];
} else {
// Allow * wildcard.
while (ASCII_ISALNUM(*p) || *p == '_' || *p == '*') {
@@ -4812,7 +4644,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 = (char *)p + 1;
+ xp->xp_pattern = p + 1;
return;
}
xp->xp_context = EXPAND_NOTHING;
@@ -4820,30 +4652,29 @@ void set_context_in_set_cmd(expand_T *xp, char_u *arg, int opt_flags)
return;
}
- xp->xp_pattern = (char *)p + 1;
+ xp->xp_pattern = p + 1;
if (flags & P_EXPAND) {
- p = options[opt_idx].var;
- if (p == (char_u *)&p_bdir
- || p == (char_u *)&p_dir
- || p == (char_u *)&p_path
- || p == (char_u *)&p_pp
- || p == (char_u *)&p_rtp
- || p == (char_u *)&p_cdpath
- || p == (char_u *)&p_vdir) {
+ p = (char *)options[opt_idx].var;
+ if (p == (char *)&p_bdir
+ || p == (char *)&p_dir
+ || p == (char *)&p_path
+ || p == (char *)&p_pp
+ || p == (char *)&p_rtp
+ || p == (char *)&p_cdpath
+ || p == (char *)&p_vdir) {
xp->xp_context = EXPAND_DIRECTORIES;
- if (p == (char_u *)&p_path
- || p == (char_u *)&p_cdpath) {
+ if (p == (char *)&p_path || p == (char *)&p_cdpath) {
xp->xp_backslash = XP_BS_THREE;
} else {
xp->xp_backslash = XP_BS_ONE;
}
- } else if (p == (char_u *)&p_ft) {
+ } else if (p == (char *)&p_ft) {
xp->xp_context = EXPAND_FILETYPE;
} else {
xp->xp_context = EXPAND_FILES;
// for 'tags' need three backslashes for a space
- if (p == (char_u *)&p_tags) {
+ if (p == (char *)&p_tags) {
xp->xp_backslash = XP_BS_THREE;
} else {
xp->xp_backslash = XP_BS_ONE;
@@ -4853,57 +4684,100 @@ 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 > (char_u *)xp->xp_pattern; p--) {
+ for (p = arg + strlen(arg) - 1; p > xp->xp_pattern; p--) {
// count number of backslashes before ' ' or ','
if (*p == ' ' || *p == ',') {
- s = p;
- while (s > (char_u *)xp->xp_pattern && *(s - 1) == '\\') {
+ char *s = p;
+ while (s > 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 = (char *)p + 1;
+ xp->xp_pattern = p + 1;
break;
}
}
// for 'spellsuggest' start at "file:"
if (options[opt_idx].var == (char_u *)&p_sps
- && STRNCMP(p, "file:", 5) == 0) {
- xp->xp_pattern = (char *)p + 5;
+ && strncmp(p, "file:", 5) == 0) {
+ xp->xp_pattern = p + 5;
break;
}
}
}
-int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char ***file)
+/// Returns true if "str" either matches "regmatch" or fuzzy matches "pat".
+///
+/// If "test_only" is true and "fuzzy" is false and if "str" matches the regular
+/// expression "regmatch", then returns true. Otherwise returns false.
+///
+/// If "test_only" is false and "fuzzy" is false and if "str" matches the
+/// regular expression "regmatch", then stores the match in matches[idx] and
+/// returns true.
+///
+/// If "test_only" is true and "fuzzy" is true and if "str" fuzzy matches
+/// "fuzzystr", then returns true. Otherwise returns false.
+///
+/// If "test_only" is false and "fuzzy" is true and if "str" fuzzy matches
+/// "fuzzystr", then stores the match details in fuzmatch[idx] and returns true.
+static bool match_str(char *const str, regmatch_T *const regmatch, char **const matches,
+ const int idx, const bool test_only, const bool fuzzy,
+ const char *const fuzzystr, fuzmatch_str_T *const fuzmatch)
+{
+ if (!fuzzy) {
+ if (vim_regexec(regmatch, str, (colnr_T)0)) {
+ if (!test_only) {
+ matches[idx] = xstrdup(str);
+ }
+ return true;
+ }
+ } else {
+ const int score = fuzzy_match_str(str, fuzzystr);
+ if (score != 0) {
+ if (!test_only) {
+ fuzmatch[idx].idx = idx;
+ fuzmatch[idx].str = xstrdup(str);
+ fuzmatch[idx].score = score;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+int ExpandSettings(expand_T *xp, regmatch_T *regmatch, char *fuzzystr, int *numMatches,
+ char ***matches, const bool can_fuzzy)
{
int num_normal = 0; // Nr of matching non-term-code settings
- int match;
int count = 0;
- char_u *str;
- int loop;
static char *(names[]) = { "all" };
int ic = regmatch->rm_ic; // remember the ignore-case flag
+ fuzmatch_str_T *fuzmatch = NULL;
+ const bool fuzzy = can_fuzzy && cmdline_fuzzy_complete(fuzzystr);
+
// do this loop twice:
// loop == 0: count the number of matching options
// loop == 1: copy the matching options into allocated memory
- for (loop = 0; loop <= 1; loop++) {
+ for (int loop = 0; loop <= 1; loop++) {
+ int match;
regmatch->rm_ic = ic;
if (xp->xp_context != EXPAND_BOOL_SETTINGS) {
for (match = 0; match < (int)ARRAY_SIZE(names);
match++) {
- if (vim_regexec(regmatch, names[match], (colnr_T)0)) {
+ if (match_str(names[match], regmatch, *matches,
+ count, (loop == 0), fuzzy, fuzzystr, fuzmatch)) {
if (loop == 0) {
num_normal++;
} else {
- (*file)[count++] = xstrdup(names[match]);
+ count++;
}
}
}
}
- for (size_t opt_idx = 0; (str = (char_u *)options[opt_idx].fullname) != NULL;
+ char *str;
+ for (size_t opt_idx = 0; (str = options[opt_idx].fullname) != NULL;
opt_idx++) {
if (options[opt_idx].var == NULL) {
continue;
@@ -4912,46 +4786,56 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char ***fi
&& !(options[opt_idx].flags & P_BOOL)) {
continue;
}
- match = false;
- if (vim_regexec(regmatch, (char *)str, (colnr_T)0)
- || (options[opt_idx].shortname != NULL
- && vim_regexec(regmatch,
- options[opt_idx].shortname,
- (colnr_T)0))) {
- match = true;
- }
- if (match) {
+ if (match_str(str, regmatch, *matches, count, (loop == 0),
+ fuzzy, fuzzystr, fuzmatch)) {
if (loop == 0) {
num_normal++;
} else {
- (*file)[count++] = (char *)vim_strsave(str);
+ count++;
+ }
+ } else if (!fuzzy && options[opt_idx].shortname != NULL
+ && vim_regexec(regmatch, options[opt_idx].shortname, (colnr_T)0)) {
+ // Compare against the abbreviated option name (for regular
+ // expression match). Fuzzy matching (previous if) already
+ // matches against both the expanded and abbreviated names.
+ if (loop == 0) {
+ num_normal++;
+ } else {
+ (*matches)[count++] = xstrdup(str);
}
}
}
if (loop == 0) {
if (num_normal > 0) {
- *num_file = num_normal;
+ *numMatches = num_normal;
} else {
return OK;
}
- *file = xmalloc((size_t)(*num_file) * sizeof(char_u *));
+ if (!fuzzy) {
+ *matches = xmalloc((size_t)(*numMatches) * sizeof(char *));
+ } else {
+ fuzmatch = xmalloc((size_t)(*numMatches) * sizeof(fuzmatch_str_T));
+ }
}
}
+
+ if (fuzzy) {
+ fuzzymatches_to_strmatches(fuzmatch, matches, count, false);
+ }
+
return OK;
}
void ExpandOldSetting(int *num_file, char ***file)
{
- char_u *var = NULL;
+ char *var = NULL;
*num_file = 0;
*file = xmalloc(sizeof(char_u *));
- /*
- * For a terminal key code expand_option_idx is < 0.
- */
+ // For a terminal key code expand_option_idx is < 0.
if (expand_option_idx < 0) {
expand_option_idx = findoption((const char *)expand_option_name);
}
@@ -4961,12 +4845,12 @@ void ExpandOldSetting(int *num_file, char ***file)
option_value2string(&options[expand_option_idx], expand_option_flags);
var = NameBuff;
} else {
- var = (char_u *)"";
+ var = "";
}
// A backslash is required before some characters. This is the reverse of
// what happens in do_set().
- char_u *buf = vim_strsave_escaped(var, escape_chars);
+ char *buf = vim_strsave_escaped(var, escape_chars);
#ifdef BACKSLASH_IN_FILENAME
// For MS-Windows et al. we don't double backslashes at the start and
@@ -4975,14 +4859,14 @@ void ExpandOldSetting(int *num_file, char ***file)
if (var[0] == '\\' && var[1] == '\\'
&& expand_option_idx >= 0
&& (options[expand_option_idx].flags & P_EXPAND)
- && vim_isfilec(var[2])
+ && vim_isfilec((uint8_t)var[2])
&& (var[2] != '\\' || (var == buf && var[4] != '\\'))) {
STRMOVE(var, var + 1);
}
}
#endif
- *file[0] = (char *)buf;
+ *file[0] = buf;
*num_file = 1;
}
@@ -4990,36 +4874,34 @@ void ExpandOldSetting(int *num_file, char ***file)
/// NameBuff[]. Must not be called with a hidden option!
///
/// @param opt_flags OPT_GLOBAL and/or OPT_LOCAL
-static void option_value2string(vimoption_T *opp, int opt_flags)
+static void option_value2string(vimoption_T *opp, int scope)
{
- char_u *varp;
-
- varp = get_varp_scope(opp, opt_flags);
+ char *varp = get_varp_scope(opp, scope);
if (opp->flags & P_NUM) {
long wc = 0;
- if (wc_use_keyname(varp, &wc)) {
- STRLCPY(NameBuff, get_special_key_name((int)wc, 0), sizeof(NameBuff));
+ if (wc_use_keyname((char_u *)varp, &wc)) {
+ xstrlcpy(NameBuff, (char *)get_special_key_name((int)wc, 0), sizeof(NameBuff));
} else if (wc != 0) {
- STRLCPY(NameBuff, transchar((int)wc), sizeof(NameBuff));
+ xstrlcpy(NameBuff, transchar((int)wc), sizeof(NameBuff));
} else {
- snprintf((char *)NameBuff,
+ snprintf(NameBuff,
sizeof(NameBuff),
"%" PRId64,
- (int64_t)*(long *)varp);
+ (int64_t)(*(long *)varp));
}
} else { // P_STRING
- varp = *(char_u **)(varp);
+ varp = *(char **)(varp);
if (varp == NULL) { // Just in case.
NameBuff[0] = NUL;
} else if (opp->flags & P_EXPAND) {
- home_replace(NULL, (char *)varp, (char *)NameBuff, MAXPATHL, false);
+ home_replace(NULL, 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);
+ } else if ((char **)opp->var == &p_pt) {
+ str2specialbuf((const char *)p_pt, NameBuff, MAXPATHL);
} else {
- STRLCPY(NameBuff, varp, MAXPATHL);
+ xstrlcpy(NameBuff, varp, MAXPATHL);
}
}
}
@@ -5027,7 +4909,7 @@ static void option_value2string(vimoption_T *opp, int opt_flags)
/// Return true if "varp" points to 'wildchar' or 'wildcharm' and it can be
/// printed as a keyname.
/// "*wcp" is set to the value of the option if it's 'wildchar' or 'wildcharm'.
-static int wc_use_keyname(char_u *varp, long *wcp)
+static int wc_use_keyname(const char_u *varp, long *wcp)
{
if (((long *)varp == &p_wc) || ((long *)varp == &p_wcm)) {
*wcp = *(long *)varp;
@@ -5038,25 +4920,14 @@ static int wc_use_keyname(char_u *varp, long *wcp)
return false;
}
-/// Return true if format option 'x' is in effect.
-/// Take care of no formatting when 'paste' is set.
-bool has_format_option(int x)
- FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
-{
- if (p_paste) {
- return false;
- }
- return vim_strchr((char *)curbuf->b_p_fo, x) != NULL;
-}
-
/// @returns true if "x" is present in 'shortmess' option, or
/// 'shortmess' contains 'a' and "x" is present in SHM_ALL_ABBREVIATIONS.
bool shortmess(int x)
{
return (p_shm != NULL
- && (vim_strchr((char *)p_shm, x) != NULL
- || (vim_strchr((char *)p_shm, 'a') != NULL
- && vim_strchr((char *)SHM_ALL_ABBREVIATIONS, x) != NULL)));
+ && (vim_strchr(p_shm, x) != NULL
+ || (vim_strchr(p_shm, 'a') != NULL
+ && vim_strchr(SHM_ALL_ABBREVIATIONS, x) != NULL)));
}
/// paste_option_changed() - Called after p_paste was set or reset.
@@ -5070,10 +4941,8 @@ static void paste_option_changed(void)
static int save_hkmap = 0;
if (p_paste) {
- /*
- * Paste switched from off to on.
- * Save the current values, so they can be restored later.
- */
+ // Paste switched from off to on.
+ // Save the current values, so they can be restored later.
if (!old_p_paste) {
// save options for each buffer
FOR_ALL_BUFFERS(buf) {
@@ -5086,7 +4955,7 @@ static void paste_option_changed(void)
xfree(buf->b_p_vsts_nopaste);
}
buf->b_p_vsts_nopaste = buf->b_p_vsts && buf->b_p_vsts != empty_option
- ? vim_strsave(buf->b_p_vsts)
+ ? xstrdup(buf->b_p_vsts)
: NULL;
}
@@ -5105,9 +4974,7 @@ static void paste_option_changed(void)
if (p_vsts_nopaste) {
xfree(p_vsts_nopaste);
}
- p_vsts_nopaste = p_vsts && p_vsts != empty_option
- ? vim_strsave(p_vsts)
- : NULL;
+ p_vsts_nopaste = p_vsts && p_vsts != empty_option ? xstrdup(p_vsts) : NULL;
}
// Always set the option values, also when 'paste' is set when it is
@@ -5157,9 +5024,7 @@ static void paste_option_changed(void)
if (buf->b_p_vsts) {
free_string_option(buf->b_p_vsts);
}
- buf->b_p_vsts = buf->b_p_vsts_nopaste
- ? vim_strsave(buf->b_p_vsts_nopaste)
- : empty_option;
+ buf->b_p_vsts = buf->b_p_vsts_nopaste ? xstrdup(buf->b_p_vsts_nopaste) : empty_option;
xfree(buf->b_p_vsts_array);
if (buf->b_p_vsts && buf->b_p_vsts != empty_option) {
(void)tabstop_set(buf->b_p_vsts, &buf->b_p_vsts_array);
@@ -5186,7 +5051,7 @@ static void paste_option_changed(void)
if (p_vsts) {
free_string_option(p_vsts);
}
- p_vsts = p_vsts_nopaste ? vim_strsave(p_vsts_nopaste) : empty_option;
+ p_vsts = p_vsts_nopaste ? xstrdup(p_vsts_nopaste) : empty_option;
}
old_p_paste = p_paste;
@@ -5237,33 +5102,31 @@ bool option_was_set(const char *name)
void reset_option_was_set(const char *name)
{
const int idx = findoption(name);
-
- if (idx >= 0) {
- options[idx].flags &= ~P_WAS_SET;
+ if (idx < 0) {
+ return;
}
+
+ options[idx].flags &= ~P_WAS_SET;
}
/// fill_breakat_flags() -- called when 'breakat' changes value.
void fill_breakat_flags(void)
{
- char_u *p;
- int i;
-
- for (i = 0; i < 256; i++) {
+ for (int i = 0; i < 256; i++) {
breakat_flags[i] = false;
}
if (p_breakat != NULL) {
- for (p = p_breakat; *p; p++) {
+ for (char_u *p = (char_u *)p_breakat; *p; p++) {
breakat_flags[*p] = true;
}
}
}
/// fill_culopt_flags() -- called when 'culopt' changes value
-int fill_culopt_flags(char_u *val, win_T *wp)
+int fill_culopt_flags(char *val, win_T *wp)
{
- char_u *p;
+ char *p;
char_u culopt_flags_new = 0;
if (val == NULL) {
@@ -5272,16 +5135,16 @@ int fill_culopt_flags(char_u *val, win_T *wp)
p = val;
}
while (*p != NUL) {
- if (STRNCMP(p, "line", 4) == 0) {
+ if (strncmp(p, "line", 4) == 0) {
p += 4;
culopt_flags_new |= CULOPT_LINE;
- } else if (STRNCMP(p, "both", 4) == 0) {
+ } else if (strncmp(p, "both", 4) == 0) {
p += 4;
culopt_flags_new |= CULOPT_LINE | CULOPT_NBR;
- } else if (STRNCMP(p, "number", 6) == 0) {
+ } else if (strncmp(p, "number", 6) == 0) {
p += 6;
culopt_flags_new |= CULOPT_NBR;
- } else if (STRNCMP(p, "screenline", 10) == 0) {
+ } else if (strncmp(p, "screenline", 10) == 0) {
p += 10;
culopt_flags_new |= CULOPT_SCRLINE;
}
@@ -5303,10 +5166,24 @@ int fill_culopt_flags(char_u *val, win_T *wp)
return OK;
}
+/// Get the value of 'magic' taking "magic_overruled" into account.
+bool magic_isset(void)
+{
+ switch (magic_overruled) {
+ case OPTION_MAGIC_ON:
+ return true;
+ case OPTION_MAGIC_OFF:
+ return false;
+ case OPTION_MAGIC_NOT_SET:
+ break;
+ }
+ return p_magic;
+}
+
/// Set the callback function value for an option that accepts a function name,
/// lambda, et al. (e.g. 'operatorfunc', 'tagfunc', etc.)
/// @return OK if the option is successfully set to a function, otherwise FAIL
-int option_set_callback_func(char_u *optval, Callback *optcb)
+int option_set_callback_func(char *optval, Callback *optcb)
{
if (optval == NULL || *optval == NUL) {
callback_free(optcb);
@@ -5315,10 +5192,10 @@ int option_set_callback_func(char_u *optval, Callback *optcb)
typval_T *tv;
if (*optval == '{'
- || (STRNCMP(optval, "function(", 9) == 0)
- || (STRNCMP(optval, "funcref(", 8) == 0)) {
+ || (strncmp(optval, "function(", 9) == 0)
+ || (strncmp(optval, "funcref(", 8) == 0)) {
// Lambda expression or a funcref
- tv = eval_expr((char *)optval);
+ tv = eval_expr(optval);
if (tv == NULL) {
return FAIL;
}
@@ -5326,11 +5203,11 @@ int option_set_callback_func(char_u *optval, Callback *optcb)
// treat everything else as a function name string
tv = xcalloc(1, sizeof(*tv));
tv->v_type = VAR_STRING;
- tv->vval.v_string = (char *)vim_strsave(optval);
+ tv->vval.v_string = xstrdup(optval);
}
Callback cb;
- if (!callback_from_typval(&cb, tv)) {
+ if (!callback_from_typval(&cb, tv) || cb.type == kCallbackNone) {
tv_free(tv);
return FAIL;
}
@@ -5341,59 +5218,6 @@ int option_set_callback_func(char_u *optval, Callback *optcb)
return OK;
}
-/// Read the 'wildmode' option, fill wim_flags[].
-int check_opt_wim(void)
-{
- char_u new_wim_flags[4];
- char_u *p;
- int i;
- int idx = 0;
-
- for (i = 0; i < 4; i++) {
- new_wim_flags[i] = 0;
- }
-
- for (p = p_wim; *p; p++) {
- for (i = 0; ASCII_ISALPHA(p[i]); i++) {}
- if (p[i] != NUL && p[i] != ',' && p[i] != ':') {
- return FAIL;
- }
- if (i == 7 && STRNCMP(p, "longest", 7) == 0) {
- new_wim_flags[idx] |= WIM_LONGEST;
- } else if (i == 4 && STRNCMP(p, "full", 4) == 0) {
- new_wim_flags[idx] |= WIM_FULL;
- } else if (i == 4 && STRNCMP(p, "list", 4) == 0) {
- new_wim_flags[idx] |= WIM_LIST;
- } else if (i == 8 && STRNCMP(p, "lastused", 8) == 0) {
- new_wim_flags[idx] |= WIM_BUFLASTUSED;
- } else {
- return FAIL;
- }
- p += i;
- if (*p == NUL) {
- break;
- }
- if (*p == ',') {
- if (idx == 3) {
- return FAIL;
- }
- idx++;
- }
- }
-
- // fill remaining entries with last flag
- while (idx < 3) {
- new_wim_flags[idx + 1] = new_wim_flags[idx];
- idx++;
- }
-
- // only when there are no errors, wim_flags[] is changed
- for (i = 0; i < 4; i++) {
- wim_flags[i] = new_wim_flags[i];
- }
- return OK;
-}
-
/// Check if backspacing over something is allowed.
/// @param what BS_INDENT, BS_EOL, BS_START, or BS_NOSTOP
bool can_bs(int what)
@@ -5411,99 +5235,7 @@ bool can_bs(int what)
case '0':
return false;
}
- return vim_strchr((char *)p_bs, what) != NULL;
-}
-
-/// Save the current values of 'fileformat' and 'fileencoding', so that we know
-/// the file must be considered changed when the value is different.
-void save_file_ff(buf_T *buf)
-{
- buf->b_start_ffc = *buf->b_p_ff;
- buf->b_start_eol = buf->b_p_eol;
- buf->b_start_bomb = buf->b_p_bomb;
-
- // Only use free/alloc when necessary, they take time.
- 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 = (char *)vim_strsave(buf->b_p_fenc);
- }
-}
-
-/// Return true if 'fileformat' and/or 'fileencoding' has a different value
-/// from when editing started (save_file_ff() called).
-/// Also when 'endofline' was changed and 'binary' is set, or when 'bomb' was
-/// changed and 'binary' is not set.
-/// Also when 'endofline' was changed and 'fixeol' is not set.
-/// When "ignore_empty" is true don't consider a new, empty buffer to be
-/// changed.
-bool file_ff_differs(buf_T *buf, bool ignore_empty)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
-{
- // In a buffer that was never loaded the options are not valid.
- if (buf->b_flags & BF_NEVERLOADED) {
- return false;
- }
- if (ignore_empty
- && (buf->b_flags & BF_NEW)
- && buf->b_ml.ml_line_count == 1
- && *ml_get_buf(buf, (linenr_T)1, false) == NUL) {
- return false;
- }
- if (buf->b_start_ffc != *buf->b_p_ff) {
- return true;
- }
- if ((buf->b_p_bin || !buf->b_p_fixeol) && buf->b_start_eol != buf->b_p_eol) {
- return true;
- }
- if (!buf->b_p_bin && buf->b_start_bomb != buf->b_p_bomb) {
- return true;
- }
- if (buf->b_start_fenc == NULL) {
- return *buf->b_p_fenc != NUL;
- }
- return STRCMP(buf->b_start_fenc, buf->b_p_fenc) != 0;
-}
-
-/// This is called when 'breakindentopt' is changed and when a window is
-/// initialized
-bool briopt_check(win_T *wp)
-{
- int bri_shift = 0;
- int bri_min = 20;
- bool bri_sbr = false;
- int bri_list = 0;
-
- char *p = (char *)wp->w_p_briopt;
- while (*p != NUL) {
- 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);
- } else if (STRNCMP(p, "min:", 4) == 0 && ascii_isdigit(p[4])) {
- p += 4;
- bri_min = getdigits_int(&p, true, 0);
- } else if (STRNCMP(p, "sbr", 3) == 0) {
- p += 3;
- bri_sbr = true;
- } else if (STRNCMP(p, "list:", 5) == 0) {
- p += 5;
- bri_list = (int)getdigits(&p, false, 0);
- }
- if (*p != ',' && *p != NUL) {
- return false;
- }
- if (*p == ',') {
- p++;
- }
- }
-
- wp->w_briopt_shift = bri_shift;
- wp->w_briopt_min = bri_min;
- wp->w_briopt_sbr = bri_sbr;
- wp->w_briopt_list = bri_list;
-
- return true;
+ return vim_strchr(p_bs, what) != NULL;
}
/// Get the local or global value of 'backupcopy'.
@@ -5514,6 +5246,17 @@ 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 'formatlistpat'.
+///
+/// @param buf The buffer.
+char *get_flp_value(buf_T *buf)
+{
+ if (buf->b_p_flp == NULL || *buf->b_p_flp == NUL) {
+ return p_flp;
+ }
+ return buf->b_p_flp;
+}
+
/// Get the local or global value of the 'virtualedit' flags.
unsigned int get_ve_flags(void)
{
@@ -5524,13 +5267,13 @@ unsigned int get_ve_flags(void)
///
/// @param win If not NULL, the window to get the local option from; global
/// otherwise.
-char_u *get_showbreak_value(win_T *const win)
+char *get_showbreak_value(win_T *const win)
FUNC_ATTR_WARN_UNUSED_RESULT
{
if (win->w_p_sbr == NULL || *win->w_p_sbr == NUL) {
return p_sbr;
}
- if (STRCMP(win->w_p_sbr, "NONE") == 0) {
+ if (strcmp(win->w_p_sbr, "NONE") == 0) {
return empty_option;
}
return win->w_p_sbr;
@@ -5540,7 +5283,7 @@ char_u *get_showbreak_value(win_T *const win)
int get_fileformat(const buf_T *buf)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
- int c = *buf->b_p_ff;
+ int c = (unsigned char)(*buf->b_p_ff);
if (buf->b_p_bin || c == 'u') {
return EOL_UNIX;
@@ -5567,7 +5310,7 @@ int get_fileformat_force(const buf_T *buf, const exarg_T *eap)
? (eap->force_bin == FORCE_BIN) : buf->b_p_bin) {
return EOL_UNIX;
}
- c = *buf->b_p_ff;
+ c = (unsigned char)(*buf->b_p_ff);
}
if (c == 'u') {
return EOL_UNIX;
@@ -5624,7 +5367,7 @@ void set_fileformat(int eol_style, int opt_flags)
}
/// Skip to next part of an option argument: skip space and comma
-char_u *skip_to_option_part(const char_u *p)
+char *skip_to_option_part(const char *p)
{
if (*p == ',') {
p++;
@@ -5632,7 +5375,7 @@ char_u *skip_to_option_part(const char_u *p)
while (*p == ' ') {
p++;
}
- return (char_u *)p;
+ return (char *)p;
}
/// Isolate one part of a string option separated by `sep_chars`.
@@ -5652,9 +5395,9 @@ size_t copy_option_part(char **option, char *buf, size_t maxlen, char *sep_chars
if (*p == '.') {
buf[len++] = *p++;
}
- while (*p != NUL && vim_strchr(sep_chars, *p) == NULL) {
+ while (*p != NUL && vim_strchr(sep_chars, (uint8_t)(*p)) == NULL) {
// Skip backslash before a separator character and space.
- if (p[0] == '\\' && vim_strchr(sep_chars, p[1]) != NULL) {
+ if (p[0] == '\\' && vim_strchr(sep_chars, (uint8_t)p[1]) != NULL) {
p++;
}
if (len < maxlen - 1) {
@@ -5667,7 +5410,7 @@ size_t copy_option_part(char **option, char *buf, size_t maxlen, char *sep_chars
if (*p != NUL && *p != ',') { // skip non-standard separator
p++;
}
- p = (char *)skip_to_option_part((char_u *)p); // p points to next file name
+ p = skip_to_option_part(p); // p points to next file name
*option = p;
return len;
@@ -5676,13 +5419,13 @@ size_t copy_option_part(char **option, char *buf, size_t maxlen, char *sep_chars
/// Return true when 'shell' has "csh" in the tail.
int csh_like_shell(void)
{
- return strstr(path_tail((char *)p_sh), "csh") != NULL;
+ return strstr(path_tail(p_sh), "csh") != NULL;
}
/// Return true when 'shell' has "fish" in the tail.
bool fish_like_shell(void)
{
- return strstr(path_tail((char *)p_sh), "fish") != NULL;
+ return strstr(path_tail(p_sh), "fish") != NULL;
}
/// Return the number of requested sign columns, based on current
@@ -5840,7 +5583,7 @@ static Dictionary vimoption2dict(vimoption_T *opt)
const char *type;
Object def;
// TODO(bfredl): do you even nocp?
- char_u *def_val = opt->def_val;
+ char_u *def_val = (char_u *)opt->def_val;
if (opt->flags & P_STRING) {
type = "string";
def = CSTR_TO_OBJ(def_val ? (char *)def_val : "");
diff --git a/src/nvim/option.h b/src/nvim/option.h
index c65d2ee182..636257bfe8 100644
--- a/src/nvim/option.h
+++ b/src/nvim/option.h
@@ -1,7 +1,7 @@
#ifndef NVIM_OPTION_H
#define NVIM_OPTION_H
-#include "nvim/ex_cmds_defs.h" // for exarg_T
+#include "nvim/ex_cmds_defs.h"
/// Returned by get_option_value().
typedef enum {
@@ -19,6 +19,8 @@ typedef enum {
#define BCO_ALWAYS 2 // always copy the options
#define BCO_NOHELP 4 // don't touch the help related options
+#define MAX_NUMBERWIDTH 20 // used for 'numberwidth' and 'statuscolumn'
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "option.h.generated.h"
#endif
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 7211d466a8..16b46dca7a 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -1,8 +1,8 @@
#ifndef NVIM_OPTION_DEFS_H
#define NVIM_OPTION_DEFS_H
-#include "eval/typval.h" // For scid_T
-#include "nvim/macros.h" // For EXTERN
+#include "nvim/eval/typval.h"
+#include "nvim/macros.h"
#include "nvim/types.h"
// option_defs.h: definition of global variables for settable options
@@ -24,6 +24,8 @@
#define P_NO_MKRC 0x200U ///< don't include in :mkvimrc output
// when option changed, what to display:
+#define P_UI_OPTION 0x400U ///< send option to remote UI
+#define P_RTABL 0x800U ///< redraw tabline
#define P_RSTAT 0x1000U ///< redraw status lines
#define P_RWIN 0x2000U ///< redraw current window and recompute text
#define P_RBUF 0x4000U ///< redraw current buffer and recompute text
@@ -49,9 +51,9 @@
#define P_NDNAME 0x8000000U ///< only normal dir name chars allowed
#define P_RWINONLY 0x10000000U ///< only redraw current window
#define P_MLE 0x20000000U ///< under control of 'modelineexpr'
+#define P_FUNC 0x40000000U ///< accept a function reference or a lambda
-#define P_NO_DEF_EXP 0x40000000U ///< Do not expand default value.
-#define P_UI_OPTION 0x80000000U ///< send option to remote ui
+#define P_NO_DEF_EXP 0x80000000U ///< Do not expand default value.
/// Flags for option-setting functions
///
@@ -97,7 +99,7 @@ typedef enum {
// Default values for 'errorformat'.
// The "%f|%l| %m" one is used for when the contents of the quickfix window is
// written to a file.
-#ifdef WIN32
+#ifdef MSWIN
# define DFLT_EFM \
"%f(%l) \\=: %t%*\\D%n: %m,%*[^\"]\"%f\"%*\\D%l: %m,%f(%l) \\=: %m,%*[^ ] %f %l: %m,%f:%l:%c:%m,%f(%l):%m,%f:%l:%m,%f|%l| %m"
#else
@@ -239,7 +241,7 @@ enum {
SHM_MOD = 'm', ///< Modified.
SHM_FILE = 'f', ///< (file 1 of 2)
SHM_LAST = 'i', ///< Last line incomplete.
- SHM_TEXT = 'x', ///< Tx instead of textmode.
+ SHM_TEXT = 'x', ///< tx instead of textmode.
SHM_LINES = 'l', ///< "L" instead of "lines".
SHM_NEW = 'n', ///< "[New]" instead of "[New file]".
SHM_WRI = 'w', ///< "[w]" instead of "written".
@@ -253,15 +255,15 @@ enum {
SHM_ATTENTION = 'A', ///< No ATTENTION messages.
SHM_INTRO = 'I', ///< Intro messages.
SHM_COMPLETIONMENU = 'c', ///< Completion menu messages.
+ SHM_COMPLETIONSCAN = 'C', ///< Completion scanning messages.
SHM_RECORDING = 'q', ///< Short recording message.
SHM_FILEINFO = 'F', ///< No file info messages.
- SHM_SEARCHCOUNT = 'S', ///< Search sats: '[1/10]'
+ SHM_SEARCHCOUNT = 'S', ///< Search stats: '[1/10]'
};
/// Represented by 'a' flag.
-#define SHM_ALL_ABBREVIATIONS ((char_u[]) { \
+#define SHM_ALL_ABBREVIATIONS ((char[]) { \
SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW, SHM_WRI, \
- 0, \
- })
+ 0 })
// characters for p_go:
#define GO_ASEL 'a' // autoselect
@@ -333,6 +335,9 @@ enum {
STL_ALTPERCENT = 'P', ///< Percentage as TOP BOT ALL or NN%.
STL_ARGLISTSTAT = 'a', ///< Argument list status as (x of y).
STL_PAGENUM = 'N', ///< Page number (when printing).
+ STL_SHOWCMD = 'S', ///< 'showcmd' buffer
+ STL_FOLDCOL = 'C', ///< Fold column for 'statuscolumn'
+ STL_SIGNCOL = 's', ///< Sign column for 'statuscolumn'
STL_VIM_EXPR = '{', ///< Start of expression to substitute.
STL_SEPARATE = '=', ///< Separation between alignment sections.
STL_TRUNCMARK = '<', ///< Truncation mark if line is too long.
@@ -350,10 +355,10 @@ enum {
STL_HELPFLAG, STL_HELPFLAG_ALT, STL_FILETYPE, STL_FILETYPE_ALT, \
STL_PREVIEWFLAG, STL_PREVIEWFLAG_ALT, STL_MODIFIED, STL_MODIFIED_ALT, \
STL_QUICKFIX, STL_PERCENTAGE, STL_ALTPERCENT, STL_ARGLISTSTAT, STL_PAGENUM, \
- STL_VIM_EXPR, STL_SEPARATE, STL_TRUNCMARK, STL_USER_HL, STL_HIGHLIGHT, \
- STL_TABPAGENR, STL_TABCLOSENR, STL_CLICK_FUNC, \
- 0, \
- })
+ STL_SHOWCMD, STL_FOLDCOL, STL_SIGNCOL, STL_VIM_EXPR, STL_SEPARATE, \
+ STL_TRUNCMARK, STL_USER_HL, STL_HIGHLIGHT, STL_TABPAGENR, STL_TABCLOSENR, \
+ STL_CLICK_FUNC, STL_TABPAGENR, STL_TABCLOSENR, STL_CLICK_FUNC, \
+ 0, })
// flags used for parsed 'wildmode'
#define WIM_FULL 0x01
@@ -379,12 +384,10 @@ enum {
#define LISPWORD_VALUE \
"defun,define,defmacro,set!,lambda,if,case,let,flet,let*,letrec,do,do*,define-syntax,let-syntax,letrec-syntax,destructuring-bind,defpackage,defparameter,defstruct,deftype,defvar,do-all-symbols,do-external-symbols,do-symbols,dolist,dotimes,ecase,etypecase,eval-when,labels,macrolet,multiple-value-bind,multiple-value-call,multiple-value-prog1,multiple-value-setq,prog1,progv,typecase,unless,unwind-protect,when,with-input-from-string,with-open-file,with-open-stream,with-output-to-string,with-package-iterator,define-condition,handler-bind,handler-case,restart-bind,restart-case,with-simple-restart,store-value,use-value,muffle-warning,abort,continue,with-slots,with-slots*,with-accessors,with-accessors*,defclass,defmethod,print-unreadable-object"
-/*
- * The following are actual variables for the options
- */
+// The following are actual variables for the options
EXTERN long p_aleph; // 'aleph'
-EXTERN char_u *p_ambw; ///< 'ambiwidth'
+EXTERN char *p_ambw; ///< 'ambiwidth'
EXTERN int p_acd; ///< 'autochdir'
EXTERN int p_ai; ///< 'autoindent'
EXTERN int p_bin; ///< 'binary'
@@ -392,29 +395,30 @@ EXTERN int p_bomb; ///< 'bomb'
EXTERN int p_bl; ///< 'buflisted'
EXTERN int p_cin; ///< 'cindent'
EXTERN long p_channel; ///< 'channel'
-EXTERN char_u *p_cink; ///< 'cinkeys'
-EXTERN char_u *p_cinsd; ///< 'cinscopedecls'
-EXTERN char_u *p_cinw; ///< 'cinwords'
-EXTERN char_u *p_cfu; ///< 'completefunc'
-EXTERN char_u *p_ofu; ///< 'omnifunc'
-EXTERN char_u *p_tsrfu; ///< 'thesaurusfunc'
+EXTERN char *p_cink; ///< 'cinkeys'
+EXTERN char *p_cinsd; ///< 'cinscopedecls'
+EXTERN char *p_cinw; ///< 'cinwords'
+EXTERN char *p_umf; ///< 'usermarkfunc'
+EXTERN char *p_cfu; ///< 'completefunc'
+EXTERN char *p_ofu; ///< 'omnifunc'
+EXTERN char *p_tsrfu; ///< 'thesaurusfunc'
EXTERN int p_ci; ///< 'copyindent'
EXTERN int p_ar; // 'autoread'
EXTERN int p_aw; // 'autowrite'
EXTERN int p_awa; // 'autowriteall'
-EXTERN char_u *p_bs; // 'backspace'
-EXTERN char_u *p_bg; // 'background'
+EXTERN char *p_bs; // 'backspace'
+EXTERN char *p_bg; // 'background'
EXTERN int p_bk; // 'backup'
-EXTERN char_u *p_bkc; // 'backupcopy'
+EXTERN char *p_bkc; // 'backupcopy'
EXTERN unsigned int bkc_flags; ///< flags from 'backupcopy'
#define BKC_YES 0x001
#define BKC_AUTO 0x002
#define BKC_NO 0x004
#define BKC_BREAKSYMLINK 0x008
#define BKC_BREAKHARDLINK 0x010
-EXTERN char_u *p_bdir; // 'backupdir'
-EXTERN char_u *p_bex; // 'backupext'
-EXTERN char_u *p_bo; // 'belloff'
+EXTERN char *p_bdir; // 'backupdir'
+EXTERN char *p_bex; // 'backupext'
+EXTERN char *p_bo; // 'belloff'
EXTERN char breakat_flags[256]; // which characters are in 'breakat'
EXTERN unsigned bo_flags;
@@ -439,88 +443,80 @@ EXTERN unsigned bo_flags;
#define BO_SPELL 0x20000
#define BO_WILD 0x40000
-EXTERN char_u *p_bsk; // 'backupskip'
-EXTERN char_u *p_breakat; // 'breakat'
-EXTERN char_u *p_bh; ///< 'bufhidden'
-EXTERN char_u *p_bt; ///< 'buftype'
-EXTERN char_u *p_cmp; // 'casemap'
+EXTERN char *p_bsk; // 'backupskip'
+EXTERN char *p_breakat; // 'breakat'
+EXTERN char *p_bh; ///< 'bufhidden'
+EXTERN char *p_bt; ///< 'buftype'
+EXTERN char *p_cmp; // 'casemap'
EXTERN unsigned cmp_flags;
#define CMP_INTERNAL 0x001
#define CMP_KEEPASCII 0x002
-EXTERN char_u *p_enc; // 'encoding'
-EXTERN int p_deco; // 'delcombine'
-EXTERN char_u *p_ccv; // 'charconvert'
-EXTERN char_u *p_cino; ///< 'cinoptions'
-EXTERN char_u *p_cedit; // 'cedit'
-EXTERN char_u *p_cb; // 'clipboard'
+EXTERN char *p_enc; // 'encoding'
+EXTERN int p_deco; // 'delcombine'
+EXTERN char *p_ccv; // 'charconvert'
+EXTERN char *p_cino; ///< 'cinoptions'
+EXTERN char *p_cedit; // 'cedit'
+EXTERN char *p_cb; // 'clipboard'
EXTERN unsigned cb_flags;
#define CB_UNNAMED 0x001
#define CB_UNNAMEDPLUS 0x002
#define CB_UNNAMEDMASK (CB_UNNAMED | CB_UNNAMEDPLUS)
EXTERN long p_cwh; // 'cmdwinheight'
EXTERN long p_ch; // 'cmdheight'
-EXTERN char_u *p_cms; ///< 'commentstring'
-EXTERN char_u *p_cpt; ///< 'complete'
+EXTERN char *p_cms; ///< 'commentstring'
+EXTERN char *p_cpt; ///< 'complete'
EXTERN long p_columns; // 'columns'
EXTERN int p_confirm; // 'confirm'
-EXTERN char_u *p_cot; // 'completeopt'
+EXTERN char *p_cot; // 'completeopt'
#ifdef BACKSLASH_IN_FILENAME
-EXTERN char_u *p_csl; // 'completeslash'
+EXTERN char *p_csl; // 'completeslash'
#endif
EXTERN long p_pb; // 'pumblend'
EXTERN long p_ph; // 'pumheight'
EXTERN long p_pw; // 'pumwidth'
-EXTERN char_u *p_com; ///< 'comments'
+EXTERN char *p_com; ///< 'comments'
EXTERN char *p_cpo; // 'cpoptions'
-EXTERN char_u *p_csprg; // 'cscopeprg'
-EXTERN int p_csre; // 'cscoperelative'
-EXTERN char_u *p_csqf; // 'cscopequickfix'
-#define CSQF_CMDS "sgdctefia"
-#define CSQF_FLAGS "+-0"
-EXTERN int p_cst; // 'cscopetag'
-EXTERN long p_csto; // 'cscopetagorder'
-EXTERN long p_cspc; // 'cscopepathcomp'
-EXTERN int p_csverbose; // 'cscopeverbose'
-EXTERN char_u *p_debug; // 'debug'
-EXTERN char_u *p_def; // 'define'
-EXTERN char_u *p_inc;
-EXTERN char_u *p_dip; // 'diffopt'
-EXTERN char_u *p_dex; // 'diffexpr'
-EXTERN char_u *p_dict; // 'dictionary'
+EXTERN char *p_debug; // 'debug'
+EXTERN char *p_def; // 'define'
+EXTERN char *p_inc;
+EXTERN char *p_dip; // 'diffopt'
+EXTERN char *p_dex; // 'diffexpr'
+EXTERN char *p_dict; // 'dictionary'
EXTERN int p_dg; // 'digraph'
-EXTERN char_u *p_dir; // 'directory'
-EXTERN char_u *p_dy; // 'display'
+EXTERN char *p_dir; // 'directory'
+EXTERN char *p_dy; // 'display'
EXTERN unsigned dy_flags;
#define DY_LASTLINE 0x001
#define DY_TRUNCATE 0x002
#define DY_UHEX 0x004
-// code should use msg_use_msgsep() to check if msgsep is active
+// legacy flag, not used
#define DY_MSGSEP 0x008
EXTERN int p_ed; // 'edcompatible'
-EXTERN char_u *p_ead; // 'eadirection'
+EXTERN char *p_ead; // 'eadirection'
EXTERN int p_emoji; // 'emoji'
EXTERN int p_ea; // 'equalalways'
-EXTERN char_u *p_ep; // 'equalprg'
+EXTERN char *p_ep; // 'equalprg'
EXTERN int p_eb; // 'errorbells'
-EXTERN char_u *p_ef; // 'errorfile'
-EXTERN char *p_efm; // 'errorformat'
-EXTERN char *p_gefm; // 'grepformat'
-EXTERN char_u *p_gp; // 'grepprg'
+EXTERN char *p_ef; // 'errorfile'
+EXTERN char *p_efm; // 'errorformat'
+EXTERN char *p_gefm; // 'grepformat'
+EXTERN char *p_gp; // 'grepprg'
+EXTERN int p_eof; ///< 'endoffile'
EXTERN int p_eol; ///< 'endofline'
-EXTERN char_u *p_ei; // 'eventignore'
+EXTERN char *p_ei; // 'eventignore'
EXTERN int p_et; ///< 'expandtab'
EXTERN int p_exrc; // 'exrc'
-EXTERN char_u *p_fenc; ///< 'fileencoding'
-EXTERN char_u *p_fencs; // 'fileencodings'
-EXTERN char_u *p_ff; ///< 'fileformat'
-EXTERN char *p_ffs; // 'fileformats'
+EXTERN char *p_fenc; ///< 'fileencoding'
+EXTERN char *p_fencs; // 'fileencodings'
+EXTERN char *p_ff; ///< 'fileformat'
+EXTERN char *p_ffs; // 'fileformats'
EXTERN int p_fic; // 'fileignorecase'
-EXTERN char_u *p_ft; ///< 'filetype'
-EXTERN char_u *p_fcs; ///< 'fillchar'
+EXTERN char *p_ft; ///< 'filetype'
+EXTERN char *p_fcs; ///< 'fillchar'
EXTERN int p_fixeol; ///< 'fixendofline'
-EXTERN char_u *p_fcl; // 'foldclose'
+EXTERN char *p_fcl; // 'foldclose'
EXTERN long p_fdls; // 'foldlevelstart'
-EXTERN char_u *p_fdo; // 'foldopen'
+EXTERN char *p_fdo; // 'foldopen'
EXTERN unsigned fdo_flags;
#define FDO_ALL 0x001
#define FDO_BLOCK 0x002
@@ -533,137 +529,129 @@ EXTERN unsigned fdo_flags;
#define FDO_INSERT 0x100
#define FDO_UNDO 0x200
#define FDO_JUMP 0x400
-EXTERN char_u *p_fex; ///< 'formatexpr'
-EXTERN char_u *p_flp; ///< 'formatlistpat'
-EXTERN char_u *p_fo; ///< 'formatoptions'
-EXTERN char_u *p_fp; // 'formatprg'
+EXTERN char *p_fex; ///< 'formatexpr'
+EXTERN char *p_flp; ///< 'formatlistpat'
+EXTERN char *p_fo; ///< 'formatoptions'
+EXTERN char *p_fp; // 'formatprg'
EXTERN int p_fs; // 'fsync'
EXTERN int p_gd; // 'gdefault'
-EXTERN char_u *p_pdev; // 'printdevice'
-EXTERN char_u *p_penc; // 'printencoding'
-EXTERN char_u *p_pexpr; // 'printexpr'
-EXTERN char_u *p_pmfn; // 'printmbfont'
-EXTERN char_u *p_pmcs; // 'printmbcharset'
-EXTERN char_u *p_pfn; // 'printfont'
-EXTERN char_u *p_popt; // 'printoptions'
-EXTERN char_u *p_header; // 'printheader'
-EXTERN char_u *p_guicursor; // 'guicursor'
-EXTERN char_u *p_guifont; // 'guifont'
-EXTERN char_u *p_guifontwide; // 'guifontwide'
-EXTERN char_u *p_hf; // 'helpfile'
+EXTERN char *p_guicursor; // 'guicursor'
+EXTERN char *p_guifont; // 'guifont'
+EXTERN char *p_guifontwide; // 'guifontwide'
+EXTERN char *p_hf; // 'helpfile'
EXTERN long p_hh; // 'helpheight'
-EXTERN char_u *p_hlg; // 'helplang'
+EXTERN char *p_hlg; // 'helplang'
EXTERN int p_hid; // 'hidden'
-EXTERN char_u *p_hl; // 'highlight'
+EXTERN char *p_hl; // 'highlight'
EXTERN int p_hls; // 'hlsearch'
EXTERN long p_hi; // 'history'
EXTERN int p_hkmap; // 'hkmap'
EXTERN int p_hkmapp; // 'hkmapp'
EXTERN int p_arshape; // 'arabicshape'
EXTERN int p_icon; // 'icon'
-EXTERN char_u *p_iconstring; // 'iconstring'
+EXTERN char *p_iconstring; // 'iconstring'
EXTERN int p_ic; // 'ignorecase'
EXTERN long p_iminsert; ///< 'iminsert'
EXTERN long p_imsearch; ///< 'imsearch'
EXTERN int p_inf; ///< 'infercase'
-EXTERN char_u *p_inex; ///< 'includeexpr'
+EXTERN char *p_inex; ///< 'includeexpr'
EXTERN int p_is; // 'incsearch'
-EXTERN char_u *p_inde; ///< 'indentexpr'
-EXTERN char_u *p_indk; ///< 'indentkeys'
-EXTERN char_u *p_icm; // 'inccommand'
-EXTERN char_u *p_isf; // 'isfname'
-EXTERN char_u *p_isi; // 'isident'
-EXTERN char_u *p_isk; ///< 'iskeyword'
-EXTERN char_u *p_isp; // 'isprint'
+EXTERN char *p_inde; ///< 'indentexpr'
+EXTERN char *p_indk; ///< 'indentkeys'
+EXTERN char *p_icm; // 'inccommand'
+EXTERN char *p_isf; // 'isfname'
+EXTERN char *p_isi; // 'isident'
+EXTERN char *p_isk; ///< 'iskeyword'
+EXTERN char *p_isp; // 'isprint'
EXTERN int p_js; // 'joinspaces'
-EXTERN char_u *p_jop; // 'jumpooptions'
+EXTERN char *p_jop; // 'jumpooptions'
EXTERN unsigned jop_flags;
#define JOP_STACK 0x01
#define JOP_VIEW 0x02
-EXTERN char_u *p_keymap; ///< 'keymap'
-EXTERN char_u *p_kp; // 'keywordprg'
-EXTERN char_u *p_km; // 'keymodel'
-EXTERN char_u *p_langmap; // 'langmap'
+EXTERN char *p_keymap; ///< 'keymap'
+EXTERN char *p_kp; // 'keywordprg'
+EXTERN char *p_km; // 'keymodel'
+EXTERN char *p_langmap; // 'langmap'
EXTERN int p_lnr; // 'langnoremap'
EXTERN int p_lrm; // 'langremap'
-EXTERN char_u *p_lm; // 'langmenu'
-EXTERN long p_lines; // 'lines'
-EXTERN long p_linespace; // 'linespace'
+EXTERN char *p_lm; // 'langmenu'
+EXTERN long p_lines; // 'lines'
+EXTERN long p_linespace; // 'linespace'
EXTERN int p_lisp; ///< 'lisp'
-EXTERN char_u *p_lispwords; // 'lispwords'
+EXTERN char *p_lop; ///< 'lispoptions'
+EXTERN char *p_lispwords; // 'lispwords'
EXTERN long p_ls; // 'laststatus'
EXTERN long p_stal; // 'showtabline'
-EXTERN char_u *p_lcs; // 'listchars'
+EXTERN char *p_lcs; // 'listchars'
EXTERN int p_lz; // 'lazyredraw'
EXTERN int p_lpl; // 'loadplugins'
EXTERN int p_magic; // 'magic'
-EXTERN char_u *p_menc; // 'makeencoding'
-EXTERN char *p_mef; // 'makeef'
-EXTERN char_u *p_mp; // 'makeprg'
-EXTERN char_u *p_mps; ///< 'matchpairs'
-EXTERN char_u *p_cc; // 'colorcolumn'
-EXTERN int p_cc_cols[256]; // array for 'colorcolumn' columns
+EXTERN char *p_menc; // 'makeencoding'
+EXTERN char *p_mef; // 'makeef'
+EXTERN char *p_mp; // 'makeprg'
+EXTERN char *p_mps; ///< 'matchpairs'
EXTERN long p_mat; // 'matchtime'
EXTERN long p_mco; // 'maxcombine'
EXTERN long p_mfd; // 'maxfuncdepth'
EXTERN long p_mmd; // 'maxmapdepth'
EXTERN long p_mmp; // 'maxmempattern'
EXTERN long p_mis; // 'menuitems'
-EXTERN char_u *p_msm; // 'mkspellmem'
+EXTERN char *p_msm; // 'mkspellmem'
EXTERN int p_ml; ///< 'modeline'
-EXTERN int p_mle; // 'modelineexpr'
+EXTERN int p_mle; // 'modelineexpr'
EXTERN long p_mls; // 'modelines'
EXTERN int p_ma; ///< 'modifiable'
EXTERN int p_mod; ///< 'modified'
-EXTERN char_u *p_mouse; // 'mouse'
-EXTERN char_u *p_mousem; // 'mousemodel'
-EXTERN int p_mousef; // 'mousefocus'
-EXTERN char_u *p_mousescroll; // 'mousescroll'
+EXTERN char *p_mouse; // 'mouse'
+EXTERN char *p_mousem; // 'mousemodel'
+EXTERN int p_mousemev; ///< 'mousemoveevent'
+EXTERN int p_mousef; // 'mousefocus'
+EXTERN char *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_nf; ///< 'nrformats'
-EXTERN char_u *p_opfunc; // 'operatorfunc'
-EXTERN char_u *p_para; // 'paragraphs'
+EXTERN char *p_nf; ///< 'nrformats'
+EXTERN char *p_opfunc; // 'operatorfunc'
+EXTERN char *p_para; // 'paragraphs'
EXTERN int p_paste; // 'paste'
-EXTERN char_u *p_pt; // 'pastetoggle'
-EXTERN char_u *p_pex; // 'patchexpr'
-EXTERN char_u *p_pm; // 'patchmode'
-EXTERN char_u *p_path; // 'path'
-EXTERN char_u *p_cdpath; // 'cdpath'
+EXTERN char *p_pt; // 'pastetoggle'
+EXTERN char *p_pex; // 'patchexpr'
+EXTERN char *p_pm; // 'patchmode'
+EXTERN char *p_path; // 'path'
+EXTERN char *p_cdpath; // 'cdpath'
EXTERN int p_pi; ///< 'preserveindent'
EXTERN long p_pyx; // 'pyxversion'
-EXTERN char_u *p_qe; ///< 'quoteescape'
+EXTERN char *p_qe; ///< 'quoteescape'
EXTERN int p_ro; ///< 'readonly'
-EXTERN char_u *p_rdb; // 'redrawdebug'
+EXTERN char *p_rdb; // 'redrawdebug'
EXTERN unsigned rdb_flags;
#define RDB_COMPOSITOR 0x001
#define RDB_NOTHROTTLE 0x002
#define RDB_INVALID 0x004
#define RDB_NODELTA 0x008
-EXTERN long p_rdt; // 'redrawtime'
-EXTERN long p_re; // 'regexpengine'
-EXTERN long p_report; // 'report'
-EXTERN long p_pvh; // 'previewheight'
-EXTERN int p_ari; // 'allowrevins'
-EXTERN int p_ri; // 'revins'
-EXTERN int p_ru; // 'ruler'
-EXTERN char_u *p_ruf; // 'rulerformat'
-EXTERN char_u *p_pp; // 'packpath'
-EXTERN char_u *p_qftf; // 'quickfixtextfunc'
-EXTERN char_u *p_rtp; // 'runtimepath'
-EXTERN long p_scbk; // 'scrollback'
-EXTERN long p_sj; // 'scrolljump'
-EXTERN long p_so; // 'scrolloff'
-EXTERN char *p_sbo; // 'scrollopt'
-EXTERN char_u *p_sections; // 'sections'
-EXTERN int p_secure; // 'secure'
-EXTERN char_u *p_sel; // 'selection'
-EXTERN char_u *p_slm; // 'selectmode'
-EXTERN char_u *p_ssop; // 'sessionoptions'
+EXTERN long p_rdt; // 'redrawtime'
+EXTERN long p_re; // 'regexpengine'
+EXTERN long p_report; // 'report'
+EXTERN long p_pvh; // 'previewheight'
+EXTERN int p_ari; // 'allowrevins'
+EXTERN int p_ri; // 'revins'
+EXTERN int p_ru; // 'ruler'
+EXTERN char *p_ruf; // 'rulerformat'
+EXTERN char *p_pp; // 'packpath'
+EXTERN char *p_qftf; // 'quickfixtextfunc'
+EXTERN char *p_rtp; // 'runtimepath'
+EXTERN long p_scbk; // 'scrollback'
+EXTERN long p_sj; // 'scrolljump'
+EXTERN long p_so; // 'scrolloff'
+EXTERN char *p_sbo; // 'scrollopt'
+EXTERN char *p_sections; // 'sections'
+EXTERN int p_secure; // 'secure'
+EXTERN char *p_sel; // 'selection'
+EXTERN char *p_slm; // 'selectmode'
+EXTERN char *p_ssop; // 'sessionoptions'
EXTERN unsigned ssop_flags;
#define SSOP_BUFFERS 0x001
@@ -685,24 +673,25 @@ EXTERN unsigned ssop_flags;
#define SSOP_TERMINAL 0x10000
#define SSOP_SKIP_RTP 0x20000
-EXTERN char_u *p_sh; // 'shell'
-EXTERN char_u *p_shcf; // 'shellcmdflag'
-EXTERN char_u *p_sp; // 'shellpipe'
-EXTERN char_u *p_shq; // 'shellquote'
-EXTERN char_u *p_sxq; // 'shellxquote'
-EXTERN char_u *p_sxe; // 'shellxescape'
-EXTERN char_u *p_srr; // 'shellredir'
+EXTERN char *p_sh; // 'shell'
+EXTERN char *p_shcf; // 'shellcmdflag'
+EXTERN char *p_sp; // 'shellpipe'
+EXTERN char *p_shq; // 'shellquote'
+EXTERN char *p_sxq; // 'shellxquote'
+EXTERN char *p_sxe; // 'shellxescape'
+EXTERN char *p_srr; // 'shellredir'
EXTERN int p_stmp; // 'shelltemp'
#ifdef BACKSLASH_IN_FILENAME
EXTERN int p_ssl; // 'shellslash'
#endif
-EXTERN char_u *p_stl; // 'statusline'
-EXTERN char *p_wbr; // 'winbar'
+EXTERN char *p_stl; // 'statusline'
+EXTERN char *p_wbr; // 'winbar'
EXTERN int p_sr; // 'shiftround'
EXTERN long p_sw; ///< 'shiftwidth'
-EXTERN char_u *p_shm; // 'shortmess'
-EXTERN char_u *p_sbr; // 'showbreak'
+EXTERN char *p_shm; // 'shortmess'
+EXTERN char *p_sbr; // 'showbreak'
EXTERN int p_sc; // 'showcmd'
+EXTERN char *p_sloc; // 'showcmdloc'
EXTERN int p_sft; // 'showfulltag'
EXTERN int p_sm; // 'showmatch'
EXTERN int p_smd; // 'showmode'
@@ -713,12 +702,12 @@ EXTERN int p_si; ///< 'smartindent'
EXTERN int p_sta; // 'smarttab'
EXTERN long p_sts; ///< 'softtabstop'
EXTERN int p_sb; // 'splitbelow'
-EXTERN char_u *p_sua; ///< 'suffixesadd'
+EXTERN char *p_sua; ///< 'suffixesadd'
EXTERN int p_swf; ///< 'swapfile'
EXTERN long p_smc; ///< 'synmaxcol'
EXTERN long p_tpm; // 'tabpagemax'
-EXTERN char_u *p_tal; // 'tabline'
-EXTERN char_u *p_tpf; // 'termpastefilter'
+EXTERN char *p_tal; // 'tabline'
+EXTERN char *p_tpf; // 'termpastefilter'
EXTERN unsigned int tpf_flags; ///< flags from 'termpastefilter'
#define TPF_BS 0x001
#define TPF_HT 0x002
@@ -727,16 +716,18 @@ EXTERN unsigned int tpf_flags; ///< flags from 'termpastefilter'
#define TPF_DEL 0x010
#define TPF_C0 0x020
#define TPF_C1 0x040
-EXTERN char_u *p_tfu; ///< 'tagfunc'
-EXTERN char_u *p_spc; ///< 'spellcapcheck'
-EXTERN char_u *p_spf; ///< 'spellfile'
-EXTERN char_u *p_spl; ///< 'spelllang'
-EXTERN char_u *p_spo; // 'spelloptions'
-EXTERN char_u *p_sps; // 'spellsuggest'
+EXTERN char *p_tfu; ///< 'tagfunc'
+EXTERN char *p_spc; ///< 'spellcapcheck'
+EXTERN char *p_spf; ///< 'spellfile'
+EXTERN char *p_spk; ///< 'splitkeep'
+EXTERN char *p_spl; ///< 'spelllang'
+EXTERN char *p_spo; // 'spelloptions'
+EXTERN unsigned int spo_flags;
+EXTERN char *p_sps; // 'spellsuggest'
EXTERN int p_spr; // 'splitright'
EXTERN int p_sol; // 'startofline'
-EXTERN char_u *p_su; // 'suffixes'
-EXTERN char_u *p_swb; // 'switchbuf'
+EXTERN char *p_su; // 'suffixes'
+EXTERN char *p_swb; // 'switchbuf'
EXTERN unsigned swb_flags;
// Keep in sync with p_swb_values in optionstr.c
#define SWB_USEOPEN 0x001
@@ -745,10 +736,10 @@ EXTERN unsigned swb_flags;
#define SWB_NEWTAB 0x008
#define SWB_VSPLIT 0x010
#define SWB_USELAST 0x020
-EXTERN char_u *p_syn; ///< 'syntax'
+EXTERN char *p_syn; ///< 'syntax'
EXTERN long p_ts; ///< 'tabstop'
EXTERN int p_tbs; ///< 'tagbsearch'
-EXTERN char_u *p_tc; ///< 'tagcase'
+EXTERN char *p_tc; ///< 'tagcase'
EXTERN unsigned tc_flags; ///< flags from 'tagcase'
#define TC_FOLLOWIC 0x01
#define TC_IGNORE 0x02
@@ -757,7 +748,7 @@ EXTERN unsigned tc_flags; ///< flags from 'tagcase'
#define TC_SMART 0x10
EXTERN long p_tl; ///< 'taglength'
EXTERN int p_tr; ///< 'tagrelative'
-EXTERN char_u *p_tags; ///< 'tags'
+EXTERN char *p_tags; ///< 'tags'
EXTERN int p_tgst; ///< 'tagstack'
EXTERN int p_tbidi; ///< 'termbidi'
EXTERN long p_tw; ///< 'textwidth'
@@ -766,53 +757,54 @@ EXTERN int p_timeout; ///< 'timeout'
EXTERN long p_tm; ///< 'timeoutlen'
EXTERN int p_title; ///< 'title'
EXTERN long p_titlelen; ///< 'titlelen'
-EXTERN char_u *p_titleold; ///< 'titleold'
-EXTERN char_u *p_titlestring; ///< 'titlestring'
-EXTERN char_u *p_tsr; ///< 'thesaurus'
+EXTERN char *p_titleold; ///< 'titleold'
+EXTERN char *p_titlestring; ///< 'titlestring'
+EXTERN char *p_tsr; ///< 'thesaurus'
EXTERN int p_tgc; ///< 'termguicolors'
EXTERN int p_ttimeout; ///< 'ttimeout'
EXTERN long p_ttm; ///< 'ttimeoutlen'
-EXTERN char_u *p_udir; ///< 'undodir'
+EXTERN char *p_udir; ///< 'undodir'
EXTERN int p_udf; ///< 'undofile'
EXTERN long p_ul; ///< 'undolevels'
EXTERN long p_ur; ///< 'undoreload'
EXTERN long p_uc; ///< 'updatecount'
EXTERN long p_ut; ///< 'updatetime'
-EXTERN char_u *p_shada; ///< 'shada'
+EXTERN char *p_shada; ///< 'shada'
EXTERN char *p_shadafile; ///< 'shadafile'
-EXTERN char_u *p_vsts; ///< 'varsofttabstop'
-EXTERN char_u *p_vts; ///< 'vartabstop'
-EXTERN char_u *p_vdir; ///< 'viewdir'
-EXTERN char_u *p_vop; ///< 'viewoptions'
+EXTERN char *p_vsts; ///< 'varsofttabstop'
+EXTERN char *p_vts; ///< 'vartabstop'
+EXTERN char *p_vdir; ///< 'viewdir'
+EXTERN char *p_vop; ///< 'viewoptions'
EXTERN unsigned vop_flags; ///< uses SSOP_ flags
EXTERN int p_vb; ///< 'visualbell'
-EXTERN char_u *p_ve; ///< 'virtualedit'
+EXTERN char *p_ve; ///< 'virtualedit'
EXTERN unsigned ve_flags;
-#define VE_BLOCK 5U // includes "all"
-#define VE_INSERT 6U // includes "all"
+#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"
+#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
+char *p_vfile = ""; // used before options are initialized
#else
-extern char_u *p_vfile; // 'verbosefile'
+extern char *p_vfile; // 'verbosefile'
#endif
EXTERN int p_warn; // 'warn'
-EXTERN char_u *p_wop; // 'wildoptions'
+EXTERN char *p_wop; // 'wildoptions'
EXTERN unsigned wop_flags;
#define WOP_TAGFILE 0x01
#define WOP_PUM 0x02
+#define WOP_FUZZY 0x04
EXTERN long p_window; // 'window'
-EXTERN char_u *p_wak; // 'winaltkeys'
-EXTERN char_u *p_wig; // 'wildignore'
-EXTERN char_u *p_ww; // 'whichwrap'
+EXTERN char *p_wak; // 'winaltkeys'
+EXTERN char *p_wig; // 'wildignore'
+EXTERN char *p_ww; // 'whichwrap'
EXTERN long p_wc; // 'wildchar'
EXTERN long p_wcm; // 'wildcharm'
EXTERN int p_wic; // 'wildignorecase'
-EXTERN char_u *p_wim; // 'wildmode'
+EXTERN char *p_wim; // 'wildmode'
EXTERN int p_wmnu; // 'wildmenu'
EXTERN long p_wh; // 'winheight'
EXTERN long p_wmh; // 'winminheight'
@@ -863,6 +855,7 @@ enum {
BV_CFU,
BV_DEF,
BV_INC,
+ BV_EOF,
BV_EOL,
BV_FIXEOL,
BV_EP,
@@ -885,6 +878,7 @@ enum {
BV_KMAP,
BV_KP,
BV_LISP,
+ BV_LOP,
BV_LW,
BV_MENC,
BV_MA,
@@ -925,11 +919,9 @@ enum {
BV_COUNT, // must be the last one
};
-/*
- * "indir" values for window-local options.
- * These need to be defined globally, so that the WV_COUNT can be used in the
- * window structure.
- */
+// "indir" values for window-local options.
+// These need to be defined globally, so that the WV_COUNT can be used in the
+// window structure.
enum {
WV_LIST = 0,
WV_ARAB,
@@ -967,14 +959,15 @@ enum {
WV_CULOPT,
WV_CC,
WV_SBR,
+ WV_STC,
WV_STL,
WV_WFH,
WV_WFW,
WV_WRAP,
WV_SCL,
WV_WINHL,
- WV_FCS,
WV_LCS,
+ WV_FCS,
WV_WINBL,
WV_WBR,
WV_COUNT, // must be the last one
@@ -993,4 +986,41 @@ typedef struct {
uint64_t channel_id; /// Only used when script_id is SID_API_CLIENT.
} LastSet;
-#endif // NVIM_OPTION_DEFS_H
+// WV_ and BV_ values get typecasted to this for the "indir" field
+typedef enum {
+ PV_NONE = 0,
+ PV_MAXVAL = 0xffff, // to avoid warnings for value out of range
+} idopt_T;
+
+typedef struct vimoption {
+ char *fullname; // full option name
+ char *shortname; // permissible abbreviation
+ uint32_t flags; // see below
+ char_u *var; // global option: pointer to variable;
+ // window-local option: VAR_WIN;
+ // buffer-local option: global value
+ idopt_T indir; // global option: PV_NONE;
+ // local option: indirect option index
+ char *def_val; // default values for variable (neovim!!)
+ LastSet last_set; // script in which the option was last set
+} vimoption_T;
+
+// The options that are local to a window or buffer have "indir" set to one of
+// these values. Special values:
+// PV_NONE: global option.
+// PV_WIN is added: window-local option
+// PV_BUF is added: buffer-local option
+// PV_BOTH is added: global option which also has a local value.
+#define PV_BOTH 0x1000
+#define PV_WIN 0x2000
+#define PV_BUF 0x4000
+#define PV_MASK 0x0fff
+#define OPT_WIN(x) (idopt_T)(PV_WIN + (int)(x))
+#define OPT_BUF(x) (idopt_T)(PV_BUF + (int)(x))
+#define OPT_BOTH(x) (idopt_T)(PV_BOTH + (int)(x))
+
+// Options local to a window have a value local to a buffer and global to all
+// buffers. Indicate this by setting "var" to VAR_WIN.
+#define VAR_WIN ((char_u *)-1)
+
+#endif // NVIM_OPTION_DEFS_H
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 5e4c73a5db..bfbfdd938e 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -10,6 +10,7 @@
-- secure=nil, gettext=nil, noglob=nil, normal_fname_chars=nil,
-- pri_mkrc=nil, deny_in_modelines=nil, normal_dname_chars=nil,
-- modelineexpr=nil,
+-- func=nil,
-- expand=nil, nodefault=nil, no_mkrc=nil,
-- alloced=nil,
-- save_pv_indir=nil,
@@ -19,8 +20,8 @@
-- types: bool, number, string
-- lists: (nil), comma, onecomma, flags, flagscomma
-- scopes: global, buffer, window
--- redraw options: statuslines, current_window, curent_window_only,
--- current_buffer, all_windows, everything, curswant
+-- redraw options: statuslines, tabline, current_window, current_window_only,
+-- current_buffer, all_windows, curswant
-- defaults: {condition=#if condition, if_true=default, if_false=default}
-- #if condition:
-- string: #ifdef string
@@ -128,7 +129,6 @@ return {
full_name='background', abbreviation='bg',
short_desc=N_("\"dark\" or \"light\", used for highlight colors"),
type='string', scope={'global'},
- redraw={'all_windows'},
varname='p_bg',
defaults={if_true="dark"}
},
@@ -395,7 +395,6 @@ return {
short_desc=N_("number of columns in the display"),
type='number', scope={'global'},
no_mkrc=true,
- redraw={'everything'},
varname='p_columns',
defaults={if_true=macros('DFLT_COLS')}
},
@@ -422,7 +421,6 @@ return {
full_name='compatible', abbreviation='cp',
short_desc=N_("No description"),
type='bool', scope={'global'},
- redraw={'all_windows'},
varname='p_force_off',
-- pri_mkrc isn't needed here, optval_default()
-- always returns TRUE for 'compatible'
@@ -458,6 +456,7 @@ return {
type='string', scope={'buffer'},
secure=true,
alloced=true,
+ func=true,
varname='p_cfu',
defaults={if_true=""}
},
@@ -499,58 +498,6 @@ return {
defaults={if_true=macros('CPO_VIM')}
},
{
- full_name='cscopepathcomp', abbreviation='cspc',
- short_desc=N_("how many components of the path to show"),
- type='number', scope={'global'},
- varname='p_cspc',
- defaults={if_true=0}
- },
- {
- full_name='cscopeprg', abbreviation='csprg',
- short_desc=N_("command to execute cscope"),
- type='string', scope={'global'},
- secure=true,
- expand=true,
- varname='p_csprg',
- defaults={if_true="cscope"}
- },
- {
- full_name='cscopequickfix', abbreviation='csqf',
- short_desc=N_("use quickfix window for cscope results"),
- type='string', list='onecomma', scope={'global'},
- deny_duplicates=true,
- varname='p_csqf',
- defaults={if_true=""}
- },
- {
- full_name='cscoperelative', abbreviation='csre',
- short_desc=N_("Use cscope.out path basename as prefix"),
- type='bool', scope={'global'},
- varname='p_csre',
- defaults={if_true=0}
- },
- {
- full_name='cscopetag', abbreviation='cst',
- short_desc=N_("use cscope for tag commands"),
- type='bool', scope={'global'},
- varname='p_cst',
- defaults={if_true=0}
- },
- {
- full_name='cscopetagorder', abbreviation='csto',
- short_desc=N_("determines \":cstag\" search order"),
- type='number', scope={'global'},
- varname='p_csto',
- defaults={if_true=0}
- },
- {
- full_name='cscopeverbose', abbreviation='csverb',
- short_desc=N_("give messages when adding a cscope database"),
- type='bool', scope={'global'},
- varname='p_csverbose',
- defaults={if_true=1}
- },
- {
full_name='cursorbind', abbreviation='crb',
short_desc=N_("move cursor in window as it moves in other windows"),
type='bool', scope={'window'},
@@ -663,7 +610,7 @@ return {
deny_duplicates=true,
redraw={'all_windows'},
varname='p_dy',
- defaults={if_true="lastline,msgsep"}
+ defaults={if_true="lastline"}
},
{
full_name='eadirection', abbreviation='ead',
@@ -696,6 +643,15 @@ return {
defaults={if_true=macros('ENC_DFLT')}
},
{
+ full_name='endoffile', abbreviation='eof',
+ short_desc=N_("write CTRL-Z for last line in file"),
+ type='bool', scope={'buffer'},
+ no_mkrc=true,
+ redraw={'statuslines'},
+ varname='p_eof',
+ defaults={if_true=false}
+ },
+ {
full_name='endofline', abbreviation='eol',
short_desc=N_("write <EOL> for last line in file"),
type='bool', scope={'buffer'},
@@ -708,7 +664,6 @@ return {
full_name='equalalways', abbreviation='ea',
short_desc=N_("windows are automatically made the same size"),
type='bool', scope={'global'},
- redraw={'all_windows'},
varname='p_ea',
defaults={if_true=true}
},
@@ -1014,7 +969,7 @@ return {
expand=true,
varname='p_gp',
defaults={
- condition='WIN32',
+ condition='MSWIN',
-- Add an extra file name so that grep will always
-- insert a file name in the match line. */
if_true="findstr /n $* nul",
@@ -1051,7 +1006,6 @@ return {
full_name='guioptions', abbreviation='go',
short_desc=N_("GUI: Which components and options are used"),
type='string', list='flags', scope={'global'},
- redraw={'all_windows'},
enable_if=false,
},
{
@@ -1195,7 +1149,6 @@ return {
full_name='inccommand', abbreviation='icm',
short_desc=N_("Live preview of substitution"),
type='string', scope={'global'},
- redraw={'all_windows'},
varname='p_icm',
defaults={if_true="nosplit"}
},
@@ -1276,7 +1229,7 @@ return {
deny_duplicates=true,
varname='p_isi',
defaults={
- condition='WIN32',
+ condition='MSWIN',
if_true="@,48-57,_,128-167,224-235",
if_false="@,48-57,_,192-255"
}
@@ -1403,7 +1356,6 @@ return {
short_desc=N_("of lines in the display"),
type='number', scope={'global'},
no_mkrc=true,
- redraw={'everything'},
varname='p_lines',
defaults={if_true=macros('DFLT_ROWS')}
},
@@ -1423,6 +1375,14 @@ return {
defaults={if_true=false}
},
{
+ full_name='lispoptions', abbreviation='lop',
+ short_desc=N_("options for lisp indenting"),
+ type='string', list='onecomma', scope={'buffer'},
+ deny_duplicates=true,
+ varname='p_lop', pv_name='p_lop',
+ defaults={if_true=''}
+ },
+ {
full_name='lispwords', abbreviation='lw',
short_desc=N_("words that change how lisp indenting works"),
type='string', list='onecomma', scope={'global', 'buffer'},
@@ -1622,6 +1582,14 @@ return {
defaults={if_true="popup_setpos"}
},
{
+ full_name='mousemoveevent', abbreviation='mousemev',
+ short_desc=N_("deliver mouse move events to input queue"),
+ type='bool', scope={'global'},
+ redraw={'ui_option'},
+ varname='p_mousemev',
+ defaults={if_true=false}
+ },
+ {
full_name='mousescroll',
short_desc=N_("amount to scroll by when scrolling with a mouse"),
type='string', list='comma', scope={'global'},
@@ -1672,6 +1640,7 @@ return {
type='string', scope={'buffer'},
secure=true,
alloced=true,
+ func=true,
varname='p_ofu',
defaults={if_true=""}
},
@@ -1687,6 +1656,7 @@ return {
short_desc=N_("function to be called for |g@| operator"),
type='string', scope={'global'},
secure=true,
+ func=true,
varname='p_opfunc',
defaults={if_true=""}
},
@@ -1770,65 +1740,6 @@ return {
defaults={if_true=false}
},
{
- full_name='printdevice', abbreviation='pdev',
- short_desc=N_("name of the printer to be used for :hardcopy"),
- type='string', scope={'global'},
- secure=true,
- varname='p_pdev',
- defaults={if_true=""}
- },
- {
- full_name='printencoding', abbreviation='penc',
- short_desc=N_("encoding to be used for printing"),
- type='string', scope={'global'},
- varname='p_penc',
- defaults={if_true=""}
- },
- {
- full_name='printexpr', abbreviation='pexpr',
- short_desc=N_("expression used to print PostScript for :hardcopy"),
- type='string', scope={'global'},
- secure=true,
- varname='p_pexpr',
- defaults={if_true=""}
- },
- {
- full_name='printfont', abbreviation='pfn',
- short_desc=N_("name of the font to be used for :hardcopy"),
- type='string', scope={'global'},
- varname='p_pfn',
- defaults={if_true="courier"}
- },
- {
- full_name='printheader', abbreviation='pheader',
- short_desc=N_("format of the header used for :hardcopy"),
- type='string', scope={'global'},
- varname='p_header',
- defaults={if_true="%<%f%h%m%=Page %N"}
- },
- {
- full_name='printmbcharset', abbreviation='pmbcs',
- short_desc=N_("CJK character set to be used for :hardcopy"),
- type='string', scope={'global'},
- varname='p_pmcs',
- defaults={if_true=""}
- },
- {
- full_name='printmbfont', abbreviation='pmbfn',
- short_desc=N_("font names to be used for CJK output of :hardcopy"),
- type='string', scope={'global'},
- varname='p_pmfn',
- defaults={if_true=""}
- },
- {
- full_name='printoptions', abbreviation='popt',
- short_desc=N_("controls the format of :hardcopy output"),
- type='string', list='onecomma', scope={'global'},
- deny_duplicates=true,
- varname='p_popt',
- defaults={if_true=""}
- },
- {
full_name='prompt',
short_desc=N_("enable prompt in Ex mode"),
type='bool', scope={'global'},
@@ -1869,6 +1780,8 @@ return {
full_name='quickfixtextfunc', abbreviation='qftf',
short_desc=N_("customize the quickfix window"),
type='string', scope={'global'},
+ secure=true,
+ func=true,
varname='p_qftf',
defaults={if_true=""}
},
@@ -2015,7 +1928,6 @@ return {
full_name='scrolloff', abbreviation='so',
short_desc=N_("minimum nr. of lines above and below cursor"),
type='number', scope={'global', 'window'},
- redraw={'all_windows'},
varname='p_so',
defaults={if_true=0}
},
@@ -2036,7 +1948,7 @@ return {
},
{
full_name='secure',
- short_desc=N_("mode for reading .vimrc in current dir"),
+ short_desc=N_("No description"),
type='bool', scope={'global'},
secure=true,
varname='p_secure',
@@ -2092,7 +2004,7 @@ return {
expand=true,
varname='p_sh',
defaults={
- condition='WIN32',
+ condition='MSWIN',
if_true="cmd.exe",
if_false="sh"
}
@@ -2104,7 +2016,7 @@ return {
secure=true,
varname='p_shcf',
defaults={
- condition='WIN32',
+ condition='MSWIN',
if_true="/s /c",
if_false="-c"
}
@@ -2116,7 +2028,7 @@ return {
secure=true,
varname='p_sp',
defaults={
- condition='WIN32',
+ condition='MSWIN',
if_true=">%s 2>&1",
if_false="| tee",
}
@@ -2136,7 +2048,7 @@ return {
secure=true,
varname='p_srr',
defaults={
- condition='WIN32',
+ condition='MSWIN',
if_true=">%s 2>&1",
if_false=">"
}
@@ -2163,7 +2075,7 @@ return {
secure=true,
varname='p_sxq',
defaults={
- condition='WIN32',
+ condition='MSWIN',
if_true="\"",
if_false="",
}
@@ -2213,6 +2125,13 @@ return {
defaults={if_true=true}
},
{
+ full_name='showcmdloc', abbreviation='sloc',
+ short_desc=N_("change location of partial command"),
+ type='string', scope={'global'},
+ varname='p_sloc',
+ defaults={if_true="last"}
+ },
+ {
full_name='showfulltag', abbreviation='sft',
short_desc=N_("show full tag pattern when completing tag"),
type='bool', scope={'global'},
@@ -2252,7 +2171,6 @@ return {
full_name='sidescrolloff', abbreviation='siso',
short_desc=N_("min. nr. of columns to left and right of cursor"),
type='number', scope={'global', 'window'},
- redraw={'all_windows'},
varname='p_siso',
defaults={if_true=0}
},
@@ -2347,6 +2265,7 @@ return {
secure=true,
expand=true,
varname='p_spo',
+ redraw={'current_buffer'},
defaults={if_true=""}
},
{
@@ -2357,6 +2276,13 @@ return {
defaults={if_true=false}
},
{
+ full_name='splitkeep', abbreviation='spk',
+ short_desc=N_("determines scroll behavior for split windows"),
+ type='string', scope={'global'},
+ varname='p_spk',
+ defaults={if_true='cursor'}
+ },
+ {
full_name='splitright', abbreviation='spr',
short_desc=N_("new window is put right of the current one"),
type='bool', scope={'global'},
@@ -2372,6 +2298,15 @@ return {
defaults={if_true=false}
},
{
+ full_name='statuscolumn', abbreviation='stc',
+ short_desc=N_("custom format for the status column"),
+ type='string', scope={'window'},
+ redraw={'current_window'},
+ secure=true,
+ alloced=true,
+ defaults={if_true=""}
+ },
+ {
full_name='statusline', abbreviation='stl',
short_desc=N_("custom format for the status line"),
type='string', scope={'global', 'window'},
@@ -2436,6 +2371,8 @@ return {
full_name='tagfunc', abbreviation='tfu',
short_desc=N_("function used to perform tag searches"),
type='string', scope={'buffer'},
+ secure=true,
+ func=true,
varname='p_tfu',
defaults={if_true=""}
},
@@ -2444,7 +2381,7 @@ return {
short_desc=N_("custom format for the console tab pages line"),
type='string', scope={'global'},
modelineexpr=true,
- redraw={'all_windows'},
+ redraw={'tabline'},
varname='p_tal',
defaults={if_true=""}
},
@@ -2566,6 +2503,7 @@ return {
type='string', scope={'global', 'buffer'},
secure=true,
alloced=true,
+ func=true,
varname='p_tsrfu',
defaults={if_true=""}
},
@@ -2716,7 +2654,7 @@ return {
full_name='verbose', abbreviation='vbs',
short_desc=N_("give informative messages"),
type='number', scope={'global'},
- varname='p_verbose',
+ varname='p_verbose', redraw={'ui_option'},
defaults={if_true=0}
},
{
@@ -2866,7 +2804,8 @@ return {
{
full_name='winhighlight', abbreviation='winhl',
short_desc=N_("Setup window-local highlights");
- type='string', scope={'window'},
+ type='string', list='onecomma', scope={'window'},
+ deny_duplicates=true,
alloced=true,
redraw={'current_window'},
defaults={if_true=""}
diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c
index c50225dfdf..a5a708600f 100644
--- a/src/nvim/optionstr.c
+++ b/src/nvim/optionstr.c
@@ -2,14 +2,14 @@
// 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 <stdlib.h>
+#include <stdint.h>
#include <string.h>
#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
#include "nvim/autocmd.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/cursor_shape.h"
@@ -17,26 +17,40 @@
#include "nvim/digraph.h"
#include "nvim/drawscreen.h"
#include "nvim/eval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/eval/userfunc.h"
#include "nvim/eval/vars.h"
-#include "nvim/hardcopy.h"
+#include "nvim/ex_getln.h"
+#include "nvim/fold.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
#include "nvim/highlight_group.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
#include "nvim/insexpand.h"
#include "nvim/keycodes.h"
+#include "nvim/macros.h"
#include "nvim/mapping.h"
+#include "nvim/mbyte.h"
#include "nvim/memline.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/ops.h"
#include "nvim/option.h"
+#include "nvim/option_defs.h"
#include "nvim/optionstr.h"
+#include "nvim/os/os.h"
+#include "nvim/pos.h"
#include "nvim/quickfix.h"
#include "nvim/runtime.h"
+#include "nvim/screen.h"
#include "nvim/spell.h"
#include "nvim/spellfile.h"
#include "nvim/spellsuggest.h"
#include "nvim/strings.h"
+#include "nvim/tag.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
#include "nvim/window.h"
@@ -45,8 +59,14 @@
# include "optionstr.c.generated.h"
#endif
-static char e_unclosed_expression_sequence[] = N_("E540: Unclosed expression sequence");
-static char e_unbalanced_groups[] = N_("E542: unbalanced groups");
+static char e_unclosed_expression_sequence[]
+ = N_("E540: Unclosed expression sequence");
+static char e_unbalanced_groups[]
+ = N_("E542: unbalanced groups");
+static char e_backupext_and_patchmode_are_equal[]
+ = N_("E589: 'backupext' and 'patchmode' are equal");
+static char e_showbreak_contains_unprintable_or_wide_character[]
+ = N_("E595: 'showbreak' contains unprintable or wide character");
static char *(p_ambw_values[]) = { "single", "double", NULL };
static char *(p_bg_values[]) = { "light", "dark", NULL };
@@ -68,9 +88,10 @@ static char *(p_ssop_values[]) = { "buffers", "winpos", "resize", "winsize", "lo
// Keep in sync with SWB_ flags in option_defs.h
static char *(p_swb_values[]) = { "useopen", "usetab", "split", "newtab", "vsplit", "uselast",
NULL };
+static char *(p_spk_values[]) = { "cursor", "screen", "topline", NULL };
static char *(p_tc_values[]) = { "followic", "ignore", "match", "followscs", "smart", NULL };
static char *(p_ve_values[]) = { "block", "insert", "all", "onemore", "none", "NONE", NULL };
-static char *(p_wop_values[]) = { "tagfile", "pum", NULL };
+static char *(p_wop_values[]) = { "tagfile", "pum", "fuzzy", NULL };
static char *(p_wak_values[]) = { "yes", "menu", "no", NULL };
static char *(p_mousem_values[]) = { "extend", "popup", "popup_setpos", "mac", NULL };
static char *(p_sel_values[]) = { "inclusive", "exclusive", "old", NULL };
@@ -100,16 +121,19 @@ static char *(p_fdc_values[]) = { "auto", "auto:1", "auto:2", "auto:3", "auto:4"
"auto:6", "auto:7", "auto:8", "auto:9", "0", "1", "2", "3", "4",
"5", "6", "7", "8", "9", NULL };
static char *(p_cb_values[]) = { "unnamed", "unnamedplus", NULL };
+static char *(p_spo_values[]) = { "camel", "noplainbuffer", NULL };
static char *(p_icm_values[]) = { "nosplit", "split", NULL };
static char *(p_jop_values[]) = { "stack", "view", NULL };
static char *(p_tpf_values[]) = { "BS", "HT", "FF", "ESC", "DEL", "C0", "C1", NULL };
static char *(p_rdb_values[]) = { "compositor", "nothrottle", "invalid", "nodelta", NULL };
+static char *(p_sloc_values[]) = { "last", "statusline", "tabline", NULL };
/// All possible flags for 'shm'.
-static char_u SHM_ALL[] = { SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW,
- SHM_WRI, SHM_ABBREVIATIONS, SHM_WRITE, SHM_TRUNC, SHM_TRUNCALL,
- SHM_OVER, SHM_OVERALL, SHM_SEARCH, SHM_ATTENTION, SHM_INTRO,
- SHM_COMPLETIONMENU, SHM_RECORDING, SHM_FILEINFO, SHM_SEARCHCOUNT, 0, };
+static char SHM_ALL[] = { SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW,
+ SHM_WRI, SHM_ABBREVIATIONS, SHM_WRITE, SHM_TRUNC, SHM_TRUNCALL,
+ SHM_OVER, SHM_OVERALL, SHM_SEARCH, SHM_ATTENTION, SHM_INTRO,
+ SHM_COMPLETIONMENU, SHM_COMPLETIONSCAN, SHM_RECORDING, SHM_FILEINFO,
+ SHM_SEARCHCOUNT, 0, };
/// After setting various option values: recompute variables that depend on
/// option values.
@@ -139,40 +163,41 @@ void didset_string_options(void)
/// "oldval_l" the old local value (only non-NULL if global and local value are set)
/// "oldval_g" the old global value (only non-NULL if global and local value are set)
/// "newval" the new value
-void trigger_optionsset_string(int opt_idx, int opt_flags, char *oldval, char *oldval_l,
- char *oldval_g, char *newval)
+void trigger_optionset_string(int opt_idx, int opt_flags, char *oldval, char *oldval_l,
+ char *oldval_g, char *newval)
{
// Don't do this recursively.
- if (oldval != NULL
- && newval != NULL
- && *get_vim_var_str(VV_OPTION_TYPE) == NUL) {
- char buf_type[7];
+ if (oldval == NULL || newval == NULL
+ || *get_vim_var_str(VV_OPTION_TYPE) != NUL) {
+ return;
+ }
- vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s",
- (opt_flags & OPT_LOCAL) ? "local" : "global");
- set_vim_var_string(VV_OPTION_OLD, oldval, -1);
- set_vim_var_string(VV_OPTION_NEW, newval, -1);
- set_vim_var_string(VV_OPTION_TYPE, buf_type, -1);
- if (opt_flags & OPT_LOCAL) {
- set_vim_var_string(VV_OPTION_COMMAND, "setlocal", -1);
- set_vim_var_string(VV_OPTION_OLDLOCAL, oldval, -1);
- }
- if (opt_flags & OPT_GLOBAL) {
- set_vim_var_string(VV_OPTION_COMMAND, "setglobal", -1);
- set_vim_var_string(VV_OPTION_OLDGLOBAL, oldval, -1);
- }
- if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
- set_vim_var_string(VV_OPTION_COMMAND, "set", -1);
- set_vim_var_string(VV_OPTION_OLDLOCAL, oldval_l, -1);
- set_vim_var_string(VV_OPTION_OLDGLOBAL, oldval_g, -1);
- }
- if (opt_flags & OPT_MODELINE) {
- set_vim_var_string(VV_OPTION_COMMAND, "modeline", -1);
- set_vim_var_string(VV_OPTION_OLDLOCAL, oldval, -1);
- }
- apply_autocmds(EVENT_OPTIONSET, get_option_fullname(opt_idx), NULL, false, NULL);
- reset_v_option_vars();
+ char buf_type[7];
+
+ vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s",
+ (opt_flags & OPT_LOCAL) ? "local" : "global");
+ set_vim_var_string(VV_OPTION_OLD, oldval, -1);
+ set_vim_var_string(VV_OPTION_NEW, newval, -1);
+ set_vim_var_string(VV_OPTION_TYPE, buf_type, -1);
+ if (opt_flags & OPT_LOCAL) {
+ set_vim_var_string(VV_OPTION_COMMAND, "setlocal", -1);
+ set_vim_var_string(VV_OPTION_OLDLOCAL, oldval, -1);
+ }
+ if (opt_flags & OPT_GLOBAL) {
+ set_vim_var_string(VV_OPTION_COMMAND, "setglobal", -1);
+ set_vim_var_string(VV_OPTION_OLDGLOBAL, oldval, -1);
+ }
+ if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
+ set_vim_var_string(VV_OPTION_COMMAND, "set", -1);
+ set_vim_var_string(VV_OPTION_OLDLOCAL, oldval_l, -1);
+ set_vim_var_string(VV_OPTION_OLDGLOBAL, oldval_g, -1);
+ }
+ if (opt_flags & OPT_MODELINE) {
+ set_vim_var_string(VV_OPTION_COMMAND, "modeline", -1);
+ set_vim_var_string(VV_OPTION_OLDLOCAL, oldval, -1);
}
+ apply_autocmds(EVENT_OPTIONSET, get_option(opt_idx)->fullname, NULL, false, NULL);
+ reset_v_option_vars();
}
static char *illegal_char(char *errbuf, size_t errbuflen, int c)
@@ -181,7 +206,7 @@ static char *illegal_char(char *errbuf, size_t errbuflen, int c)
return "";
}
vim_snprintf(errbuf, errbuflen, _("E539: Illegal character <%s>"),
- (char *)transchar(c));
+ transchar(c));
return errbuf;
}
@@ -218,6 +243,7 @@ void check_buf_options(buf_T *buf)
check_string_option(&buf->b_p_cink);
check_string_option(&buf->b_p_cino);
parse_cino(buf);
+ check_string_option(&buf->b_p_lop);
check_string_option(&buf->b_p_ft);
check_string_option(&buf->b_p_cinw);
check_string_option(&buf->b_p_cinsd);
@@ -245,17 +271,17 @@ void check_buf_options(buf_T *buf)
/// Free the string allocated for an option.
/// Checks for the string being empty_option. This may happen if we're out of
-/// memory, vim_strsave() returned NULL, which was replaced by empty_option by
+/// memory, xstrdup() returned NULL, which was replaced by empty_option by
/// check_options().
/// Does NOT check for P_ALLOCED flag!
-void free_string_option(char_u *p)
+void free_string_option(char *p)
{
if (p != empty_option) {
xfree(p);
}
}
-void clear_string_option(char_u **pp)
+void clear_string_option(char **pp)
{
if (*pp != empty_option) {
xfree(*pp);
@@ -263,7 +289,7 @@ void clear_string_option(char_u **pp)
*pp = empty_option;
}
-void check_string_option(char_u **pp)
+void check_string_option(char **pp)
{
if (*pp == NULL) {
*pp = empty_option;
@@ -272,20 +298,20 @@ void check_string_option(char_u **pp)
/// Set global value for string option when it's a local option.
///
-/// @param opt_idx option index
+/// @param opt option
/// @param varp pointer to option variable
-static void set_string_option_global(int opt_idx, char_u **varp)
+static void set_string_option_global(vimoption_T *opt, char **varp)
{
- char_u **p, *s;
+ char **p;
// the global value is always allocated
- if (is_window_local_option(opt_idx)) {
- p = (char_u **)GLOBAL_WO(varp);
+ if (opt->var == VAR_WIN) {
+ p = (char **)GLOBAL_WO(varp);
} else {
- p = (char_u **)get_option_var(opt_idx);
+ p = (char **)opt->var;
}
- if (!is_global_option(opt_idx) && p != varp) {
- s = vim_strsave(*varp);
+ if (opt->indir != PV_NONE && p != varp) {
+ char *s = xstrdup(*varp);
free_string_option(*p);
*p = s;
}
@@ -316,32 +342,34 @@ void set_string_option_direct(const char *name, int opt_idx, const char *val, in
}
}
- if (is_hidden_option(idx)) { // can't set hidden option
+ vimoption_T *opt = get_option(idx);
+
+ if (opt->var == NULL) { // can't set hidden option
return;
}
- assert((void *)get_option_var(idx) != (void *)&p_shada);
+ assert((void *)opt->var != (void *)&p_shada);
s = xstrdup(val);
{
- varp = (char **)get_option_varp_scope(idx, both ? OPT_LOCAL : opt_flags);
- if ((opt_flags & OPT_FREE) && (get_option_flags(idx) & P_ALLOCED)) {
- free_string_option((char_u *)(*varp));
+ varp = (char **)get_varp_scope(opt, both ? OPT_LOCAL : opt_flags);
+ if ((opt_flags & OPT_FREE) && (opt->flags & P_ALLOCED)) {
+ free_string_option(*varp);
}
*varp = s;
// For buffer/window local option may also set the global value.
if (both) {
- set_string_option_global(idx, (char_u **)varp);
+ set_string_option_global(opt, varp);
}
- set_option_flag(idx, P_ALLOCED);
+ opt->flags |= P_ALLOCED;
// 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 (is_global_local_option(idx) && both) {
- free_string_option((char_u *)(*varp));
- *varp = (char *)empty_option;
+ if ((opt->indir & PV_BOTH) && both) {
+ free_string_option(*varp);
+ *varp = empty_option;
}
if (set_sid != SID_NONE) {
sctx_T script_ctx;
@@ -381,28 +409,28 @@ void set_string_option_direct_in_win(win_T *wp, const char *name, int opt_idx, c
/// @param[in] opt_flags Option flags: expected to contain #OPT_LOCAL and/or
/// #OPT_GLOBAL.
///
-/// @return NULL on success, error message on error.
+/// @return NULL on success, an untranslated error message on error.
char *set_string_option(const int opt_idx, const char *const value, const int opt_flags)
FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_WARN_UNUSED_RESULT
{
- if (is_hidden_option(opt_idx)) { // don't set hidden option
+ vimoption_T *opt = get_option(opt_idx);
+
+ if (opt->var == NULL) { // don't set hidden option
return NULL;
}
char *const s = xstrdup(value);
char **const varp
- = (char **)get_option_varp_scope(opt_idx,
- (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0
- ? (is_global_local_option(opt_idx)
- ? OPT_GLOBAL : OPT_LOCAL)
- : opt_flags);
+ = (char **)get_varp_scope(opt, ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0
+ ? ((opt->indir & PV_BOTH) ? OPT_GLOBAL : OPT_LOCAL)
+ : opt_flags));
char *const oldval = *varp;
char *oldval_l = NULL;
char *oldval_g = NULL;
if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
- oldval_l = *(char **)get_option_varp_scope(opt_idx, OPT_LOCAL);
- oldval_g = *(char **)get_option_varp_scope(opt_idx, OPT_GLOBAL);
+ oldval_l = *(char **)get_varp_scope(opt, OPT_LOCAL);
+ oldval_g = *(char **)get_varp_scope(opt, OPT_GLOBAL);
}
*varp = s;
@@ -413,21 +441,21 @@ char *set_string_option(const int opt_idx, const char *const value, const int op
char *const saved_newval = xstrdup(s);
int value_checked = false;
- char *const r = did_set_string_option(opt_idx, (char_u **)varp, (char_u *)oldval,
- NULL, 0,
- opt_flags, &value_checked);
- if (r == NULL) {
+ char *const errmsg = did_set_string_option(opt_idx, varp, oldval,
+ NULL, 0,
+ opt_flags, &value_checked);
+ if (errmsg == NULL) {
did_set_option(opt_idx, opt_flags, true, value_checked);
}
// call autocommand after handling side effects
- if (r == NULL) {
+ if (errmsg == NULL) {
if (!starting) {
- trigger_optionsset_string(opt_idx, opt_flags, saved_oldval, saved_oldval_l, saved_oldval_g,
- saved_newval);
+ trigger_optionset_string(opt_idx, opt_flags, saved_oldval, saved_oldval_l, saved_oldval_g,
+ saved_newval);
}
- if (get_option_flags(opt_idx) & P_UI_OPTION) {
- ui_call_option_set(cstr_as_string(get_option_fullname(opt_idx)),
+ if (opt->flags & P_UI_OPTION) {
+ ui_call_option_set(cstr_as_string(opt->fullname),
STRING_OBJ(cstr_as_string(saved_newval)));
}
}
@@ -436,12 +464,12 @@ char *set_string_option(const int opt_idx, const char *const value, const int op
xfree(saved_oldval_g);
xfree(saved_newval);
- return r;
+ return errmsg;
}
/// Return true if "val" is a valid 'filetype' name.
/// Also used for 'syntax' and 'keymap'.
-static bool valid_filetype(const char_u *val)
+static bool valid_filetype(const char *val)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
return valid_name(val, ".-_");
@@ -456,7 +484,7 @@ static char *check_mousescroll(char *string)
for (;;) {
char *end = vim_strchr(string, ',');
- size_t length = end ? (size_t)(end - string) : STRLEN(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.
@@ -513,7 +541,7 @@ static char *check_mousescroll(char *string)
/// Handle setting 'signcolumn' for value 'val'
///
/// @return OK when the value is valid, FAIL otherwise
-static int check_signcolumn(char_u *val)
+static int check_signcolumn(char *val)
{
if (*val == NUL) {
return FAIL;
@@ -524,8 +552,8 @@ static int check_signcolumn(char_u *val)
}
// check for 'auto:<NUMBER>-<NUMBER>'
- if (STRLEN(val) == 8
- && !STRNCMP(val, "auto:", 5)
+ if (strlen(val) == 8
+ && !strncmp(val, "auto:", 5)
&& ascii_isdigit(val[5])
&& val[6] == '-'
&& ascii_isdigit(val[7])) {
@@ -586,7 +614,7 @@ char *check_stl_option(char *s)
groupdepth++;
continue;
}
- if (vim_strchr(STL_ALL, *s) == NULL) {
+ if (vim_strchr(STL_ALL, (uint8_t)(*s)) == NULL) {
return illegal_char(errbuf, sizeof(errbuf), *s);
}
if (*s == '{') {
@@ -612,894 +640,1220 @@ char *check_stl_option(char *s)
static int shada_idx = -1;
-/// Handle string options that need some action to perform when changed.
-/// The new value must be allocated.
-///
-/// @param opt_idx index in options[] table
-/// @param varp pointer to the option variable
-/// @param oldval previous value of the option
-/// @param errbuf buffer for errors, or NULL
-/// @param errbuflen length of errors buffer
-/// @param opt_flags OPT_LOCAL and/or OPT_GLOBAL
-/// @param value_checked value was checked to be safe, no need to set P_INSECURE
-///
-/// @return NULL for success, or an untranslated error message for an error
-char *did_set_string_option(int opt_idx, char_u **varp, char_u *oldval, char *errbuf,
- size_t errbuflen, int opt_flags, int *value_checked)
+static bool check_illegal_path_names(char *val, uint32_t flags)
{
- char *errmsg = NULL;
- char *s, *p;
- int did_chartab = false;
- char_u **gvarp;
- bool free_oldval = (get_option_flags(opt_idx) & P_ALLOCED);
- bool value_changed = false;
+ // 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.
+ return (((flags & P_NFNAME)
+ && strpbrk(val, (secure ? "/\\*?[|;&<>\r\n" : "/\\*?[<>\r\n")) != NULL)
+ || ((flags & P_NDNAME)
+ && strpbrk(val, "*?[|;&<>\r\n") != NULL));
+}
- // Get the global option to compare with, otherwise we would have to check
- // two values for all local options.
- gvarp = (char_u **)get_option_varp_scope(opt_idx, OPT_GLOBAL);
+static void did_set_backupcopy(buf_T *buf, char *oldval, int opt_flags, char **errmsg)
+{
+ char *bkc = p_bkc;
+ unsigned int *flags = &bkc_flags;
- // Disallow changing some options from secure mode
- if ((secure || sandbox != 0)
- && (get_option_flags(opt_idx) & P_SECURE)) {
- errmsg = e_secure;
- } else if (((get_option_flags(opt_idx) & P_NFNAME)
- && strpbrk((char *)(*varp),
- (secure ? "/\\*?[|;&<>\r\n" : "/\\*?[<>\r\n")) != NULL)
- || ((get_option_flags(opt_idx) & P_NDNAME)
- && 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.
- errmsg = e_invarg;
- } else if (gvarp == &p_bkc) { // 'backupcopy'
- char_u *bkc = p_bkc;
- unsigned int *flags = &bkc_flags;
+ if (opt_flags & OPT_LOCAL) {
+ bkc = buf->b_p_bkc;
+ flags = &buf->b_bkc_flags;
+ }
- if (opt_flags & OPT_LOCAL) {
- bkc = curbuf->b_p_bkc;
- flags = &curbuf->b_bkc_flags;
+ if ((opt_flags & OPT_LOCAL) && *bkc == NUL) {
+ // make the local value empty: use the global value
+ *flags = 0;
+ } else {
+ if (opt_strings_flags(bkc, p_bkc_values, flags, true) != OK) {
+ *errmsg = e_invarg;
}
- if ((opt_flags & OPT_LOCAL) && *bkc == NUL) {
- // make the local value empty: use the global value
- *flags = 0;
- } else {
- if (opt_strings_flags(bkc, p_bkc_values, flags, true) != OK) {
- errmsg = e_invarg;
- }
-
- if (((*flags & BKC_AUTO) != 0)
- + ((*flags & BKC_YES) != 0)
- + ((*flags & BKC_NO) != 0) != 1) {
- // Must have exactly one of "auto", "yes" and "no".
- (void)opt_strings_flags(oldval, p_bkc_values, flags, true);
- errmsg = e_invarg;
- }
- }
- } else if (varp == &p_bex || varp == &p_pm) { // 'backupext' and 'patchmode'
- if (STRCMP(*p_bex == '.' ? p_bex + 1 : p_bex,
- *p_pm == '.' ? p_pm + 1 : p_pm) == 0) {
- errmsg = N_("E589: 'backupext' and 'patchmode' are equal");
+ if (((*flags & BKC_AUTO) != 0)
+ + ((*flags & BKC_YES) != 0)
+ + ((*flags & BKC_NO) != 0) != 1) {
+ // Must have exactly one of "auto", "yes" and "no".
+ (void)opt_strings_flags(oldval, p_bkc_values, flags, true);
+ *errmsg = e_invarg;
}
- } else if (varp == &curwin->w_p_briopt) { // 'breakindentopt'
- if (briopt_check(curwin) == FAIL) {
- errmsg = e_invarg;
+ }
+}
+
+static void did_set_backupext_or_patchmode(char **errmsg)
+{
+ if (strcmp(*p_bex == '.' ? p_bex + 1 : p_bex,
+ *p_pm == '.' ? p_pm + 1 : p_pm) == 0) {
+ *errmsg = e_backupext_and_patchmode_are_equal;
+ }
+}
+
+static void did_set_breakindentopt(win_T *win, char **errmsg)
+{
+ if (briopt_check(win) == FAIL) {
+ *errmsg = e_invarg;
+ }
+ // list setting requires a redraw
+ if (win == curwin && win->w_briopt_list) {
+ redraw_all_later(UPD_NOT_VALID);
+ }
+}
+
+static void did_set_isopt(buf_T *buf, bool *did_chartab, char **errmsg)
+{
+ // 'isident', 'iskeyword', 'isprint or 'isfname' option: refill g_chartab[]
+ // If the new option is invalid, use old value. 'lisp' option: refill
+ // g_chartab[] for '-' char
+ if (buf_init_chartab(buf, true) == FAIL) {
+ *did_chartab = true; // need to restore it below
+ *errmsg = e_invarg; // error in value
+ }
+}
+
+static void did_set_helpfile(void)
+{
+ // May compute new values for $VIM and $VIMRUNTIME
+ if (didset_vim) {
+ vim_unsetenv_ext("VIM");
+ }
+ if (didset_vimruntime) {
+ vim_unsetenv_ext("VIMRUNTIME");
+ }
+}
+
+static void did_set_cursorlineopt(win_T *win, char **varp, char **errmsg)
+{
+ if (**varp == NUL || fill_culopt_flags(*varp, win) != OK) {
+ *errmsg = e_invarg;
+ }
+}
+
+static void did_set_helplang(char **errmsg)
+{
+ // Check for "", "ab", "ab,cd", etc.
+ for (char *s = p_hlg; *s != NUL; s += 3) {
+ if (s[1] == NUL || ((s[2] != ',' || s[3] == NUL) && s[2] != NUL)) {
+ *errmsg = e_invarg;
+ break;
}
- } else if (varp == &p_isi
- || varp == &(curbuf->b_p_isk)
- || varp == &p_isp
- || varp == &p_isf) {
- // 'isident', 'iskeyword', 'isprint or 'isfname' option: refill g_chartab[]
- // If the new option is invalid, use old value. 'lisp' option: refill
- // g_chartab[] for '-' char
- if (init_chartab() == FAIL) {
- did_chartab = true; // need to restore it below
- errmsg = e_invarg; // error in value
+ if (s[2] == NUL) {
+ break;
}
- } else if (varp == &p_hf) { // 'helpfile'
- // May compute new values for $VIM and $VIMRUNTIME
- if (didset_vim) {
- vim_unsetenv_ext("VIM");
+ }
+}
+
+static void did_set_highlight(char **varp, char **errmsg)
+{
+ if (strcmp(*varp, HIGHLIGHT_INIT) != 0) {
+ *errmsg = e_unsupportedoption;
+ }
+}
+
+static void did_set_opt_flags(char *val, char **values, unsigned *flagp, bool list, char **errmsg)
+{
+ if (opt_strings_flags(val, values, flagp, list) != OK) {
+ *errmsg = e_invarg;
+ }
+}
+
+static void did_set_opt_strings(char *val, char **values, bool list, char **errmsg)
+{
+ did_set_opt_flags(val, values, NULL, list, errmsg);
+}
+
+static void did_set_sessionoptions(char *oldval, char **errmsg)
+{
+ if (opt_strings_flags(p_ssop, p_ssop_values, &ssop_flags, true) != OK) {
+ *errmsg = e_invarg;
+ }
+ if ((ssop_flags & SSOP_CURDIR) && (ssop_flags & SSOP_SESDIR)) {
+ // Don't allow both "sesdir" and "curdir".
+ (void)opt_strings_flags(oldval, p_ssop_values, &ssop_flags, true);
+ *errmsg = e_invarg;
+ }
+}
+
+static void did_set_ambiwidth(char **errmsg)
+{
+ if (check_opt_strings(p_ambw, p_ambw_values, false) != OK) {
+ *errmsg = e_invarg;
+ } else {
+ *errmsg = check_chars_options();
+ }
+}
+
+static void did_set_background(char **errmsg)
+{
+ if (check_opt_strings(p_bg, p_bg_values, false) != OK) {
+ *errmsg = e_invarg;
+ return;
+ }
+
+ int dark = (*p_bg == 'd');
+
+ init_highlight(false, false);
+
+ if (dark != (*p_bg == 'd') && get_var_value("g:colors_name") != NULL) {
+ // The color scheme must have set 'background' back to another
+ // value, that's not what we want here. Disable the color
+ // scheme and set the colors again.
+ do_unlet(S_LEN("g:colors_name"), true);
+ free_string_option(p_bg);
+ p_bg = xstrdup((dark ? "dark" : "light"));
+ check_string_option(&p_bg);
+ init_highlight(false, false);
+ }
+}
+
+static void did_set_wildmode(char **errmsg)
+{
+ if (check_opt_wim() == FAIL) {
+ *errmsg = e_invarg;
+ }
+}
+
+static void did_set_winaltkeys(char **errmsg)
+{
+ if (*p_wak == NUL || check_opt_strings(p_wak, p_wak_values, false) != OK) {
+ *errmsg = e_invarg;
+ }
+}
+
+static void did_set_eventignore(char **errmsg)
+{
+ if (check_ei() == FAIL) {
+ *errmsg = e_invarg;
+ }
+}
+
+// 'encoding', 'fileencoding' and 'makeencoding'
+static void did_set_encoding(buf_T *buf, char **varp, char **gvarp, int opt_flags, char **errmsg)
+{
+ if (gvarp == &p_fenc) {
+ if (!MODIFIABLE(buf) && opt_flags != OPT_GLOBAL) {
+ *errmsg = e_modifiable;
+ return;
}
- if (didset_vimruntime) {
- vim_unsetenv_ext("VIMRUNTIME");
+
+ if (vim_strchr(*varp, ',') != NULL) {
+ // No comma allowed in 'fileencoding'; catches confusing it
+ // with 'fileencodings'.
+ *errmsg = e_invarg;
+ return;
}
- } else if (varp == &p_rtp || varp == &p_pp) { // 'runtimepath' 'packpath'
- runtime_search_path_invalidate();
- } else if (varp == &curwin->w_p_culopt
- || gvarp == &curwin->w_allbuf_opt.wo_culopt) { // 'cursorlineopt'
- if (**varp == NUL || fill_culopt_flags(*varp, curwin) != OK) {
- errmsg = e_invarg;
+
+ // May show a "+" in the title now.
+ redraw_titles();
+ // Add 'fileencoding' to the swap file.
+ ml_setflags(buf);
+ }
+
+ // canonize the value, so that strcmp() can be used on it
+ char *p = enc_canonize(*varp);
+ xfree(*varp);
+ *varp = p;
+ if (varp == &p_enc) {
+ // only encoding=utf-8 allowed
+ if (strcmp(p_enc, "utf-8") != 0) {
+ *errmsg = e_unsupportedoption;
+ return;
}
- } else if (varp == &curwin->w_p_cc) { // 'colorcolumn'
- errmsg = check_colorcolumn(curwin);
- } else if (varp == &p_hlg) { // 'helplang'
- // Check for "", "ab", "ab,cd", etc.
- for (s = (char *)p_hlg; *s != NUL; s += 3) {
- if (s[1] == NUL || ((s[2] != ',' || s[3] == NUL) && s[2] != NUL)) {
- errmsg = e_invarg;
- break;
+ spell_reload();
+ }
+}
+
+static void did_set_keymap(buf_T *buf, char **varp, int opt_flags, int *value_checked,
+ char **errmsg)
+{
+ if (!valid_filetype(*varp)) {
+ *errmsg = e_invarg;
+ return;
+ }
+
+ int secure_save = secure;
+
+ // Reset the secure flag, since the value of 'keymap' has
+ // been checked to be safe.
+ secure = 0;
+
+ // load or unload key mapping tables
+ *errmsg = keymap_init();
+
+ secure = secure_save;
+
+ // Since we check the value, there is no need to set P_INSECURE,
+ // even when the value comes from a modeline.
+ *value_checked = true;
+
+ if (*errmsg == NULL) {
+ if (*buf->b_p_keymap != NUL) {
+ // Installed a new keymap, switch on using it.
+ buf->b_p_iminsert = B_IMODE_LMAP;
+ if (buf->b_p_imsearch != B_IMODE_USE_INSERT) {
+ buf->b_p_imsearch = B_IMODE_LMAP;
}
- if (s[2] == NUL) {
- break;
+ } else {
+ // Cleared the keymap, may reset 'iminsert' and 'imsearch'.
+ if (buf->b_p_iminsert == B_IMODE_LMAP) {
+ buf->b_p_iminsert = B_IMODE_NONE;
+ }
+ if (buf->b_p_imsearch == B_IMODE_LMAP) {
+ buf->b_p_imsearch = B_IMODE_USE_INSERT;
}
}
- } else if (varp == &p_hl) { // 'highlight'
- if (STRCMP(*varp, HIGHLIGHT_INIT) != 0) {
- errmsg = e_unsupportedoption;
+ if ((opt_flags & OPT_LOCAL) == 0) {
+ set_iminsert_global(buf);
+ set_imsearch_global(buf);
}
- } else if (varp == &p_jop) { // 'jumpoptions'
- if (opt_strings_flags(p_jop, p_jop_values, &jop_flags, true) != OK) {
- errmsg = e_invarg;
+ status_redraw_buf(buf);
+ }
+}
+
+static void did_set_fileformat(buf_T *buf, char **varp, const char *oldval, int opt_flags,
+ char **errmsg)
+{
+ if (!MODIFIABLE(buf) && !(opt_flags & OPT_GLOBAL)) {
+ *errmsg = e_modifiable;
+ } else if (check_opt_strings(*varp, p_ff_values, false) != OK) {
+ *errmsg = e_invarg;
+ } else {
+ redraw_titles();
+ // update flag in swap file
+ ml_setflags(buf);
+ // Redraw needed when switching to/from "mac": a CR in the text
+ // will be displayed differently.
+ if (get_fileformat(buf) == EOL_MAC || *oldval == 'm') {
+ redraw_buf_later(buf, UPD_NOT_VALID);
}
- } else if (gvarp == &p_nf) { // 'nrformats'
- if (check_opt_strings(*varp, p_nf_values, true) != OK) {
- errmsg = e_invarg;
+ }
+}
+
+static void did_set_matchpairs(char **varp, char **errmsg)
+{
+ for (char *p = *varp; *p != NUL; p++) {
+ int x2 = -1;
+ int x3 = -1;
+
+ p += utfc_ptr2len(p);
+ if (*p != NUL) {
+ x2 = (unsigned char)(*p++);
}
- } else if (varp == &p_ssop) { // 'sessionoptions'
- if (opt_strings_flags(p_ssop, p_ssop_values, &ssop_flags, true) != OK) {
- errmsg = e_invarg;
+ if (*p != NUL) {
+ x3 = utf_ptr2char(p);
+ p += utfc_ptr2len(p);
}
- if ((ssop_flags & SSOP_CURDIR) && (ssop_flags & SSOP_SESDIR)) {
- // Don't allow both "sesdir" and "curdir".
- (void)opt_strings_flags(oldval, p_ssop_values, &ssop_flags, true);
- errmsg = e_invarg;
+ if (x2 != ':' || x3 == -1 || (*p != NUL && *p != ',')) {
+ *errmsg = e_invarg;
+ break;
}
- } else if (varp == &p_vop) { // 'viewoptions'
- if (opt_strings_flags(p_vop, p_ssop_values, &vop_flags, true) != OK) {
- errmsg = e_invarg;
+ if (*p == NUL) {
+ break;
}
- } else if (varp == &p_rdb) { // 'redrawdebug'
- if (opt_strings_flags(p_rdb, p_rdb_values, &rdb_flags, true) != OK) {
- errmsg = e_invarg;
+ }
+}
+
+static void did_set_comments(char **varp, char *errbuf, size_t errbuflen, char **errmsg)
+{
+ for (char *s = *varp; *s;) {
+ while (*s && *s != ':') {
+ if (vim_strchr(COM_ALL, (uint8_t)(*s)) == NULL
+ && !ascii_isdigit(*s) && *s != '-') {
+ *errmsg = illegal_char(errbuf, errbuflen, *s);
+ break;
+ }
+ s++;
}
- } else if (varp == (char_u **)&p_sbo) { // 'scrollopt'
- if (check_opt_strings((char_u *)p_sbo, p_scbopt_values, true) != OK) {
- errmsg = e_invarg;
+ if (*s++ == NUL) {
+ *errmsg = N_("E524: Missing colon");
+ } else if (*s == ',' || *s == NUL) {
+ *errmsg = N_("E525: Zero length string");
}
- } else if (varp == &p_ambw || (int *)varp == &p_emoji) { // 'ambiwidth'
- if (check_opt_strings(p_ambw, p_ambw_values, false) != OK) {
- errmsg = e_invarg;
- } else {
- errmsg = check_chars_options();
+ if (*errmsg != NULL) {
+ break;
}
- } else if (varp == &p_bg) { // 'background'
- if (check_opt_strings(p_bg, p_bg_values, false) == OK) {
- int dark = (*p_bg == 'd');
-
- init_highlight(false, false);
-
- if (dark != (*p_bg == 'd') && get_var_value("g:colors_name") != NULL) {
- // The color scheme must have set 'background' back to another
- // value, that's not what we want here. Disable the color
- // scheme and set the colors again.
- do_unlet(S_LEN("g:colors_name"), true);
- free_string_option(p_bg);
- p_bg = vim_strsave((char_u *)(dark ? "dark" : "light"));
- check_string_option(&p_bg);
- init_highlight(false, false);
+ while (*s && *s != ',') {
+ if (*s == '\\' && s[1] != NUL) {
+ s++;
}
- } else {
- errmsg = e_invarg;
- }
- } else if (varp == &p_wim) { // 'wildmode'
- if (check_opt_wim() == FAIL) {
- errmsg = e_invarg;
+ s++;
}
- } else if (varp == &p_wop) { // 'wildoptions'
- if (opt_strings_flags(p_wop, p_wop_values, &wop_flags, true) != OK) {
- errmsg = e_invarg;
+ s = skip_to_option_part(s);
+ }
+}
+
+static void did_set_global_listfillchars(win_T *win, char **varp, int opt_flags, char **errmsg)
+{
+ char **local_ptr = varp == &p_lcs ? &win->w_p_lcs : &win->w_p_fcs;
+ // only apply the global value to "win" when it does not have a local value
+ *errmsg = set_chars_option(win, varp, **local_ptr == NUL || !(opt_flags & OPT_GLOBAL));
+ if (*errmsg == NULL) {
+ // If the current window is set to use the global
+ // 'listchars'/'fillchars' value, clear the window-local value.
+ if (!(opt_flags & OPT_GLOBAL)) {
+ clear_string_option(local_ptr);
+ }
+ FOR_ALL_TAB_WINDOWS(tp, wp) {
+ // If the current window has a local value need to apply it
+ // again, it was changed when setting the global value.
+ // If no error was returned above, we don't expect an error
+ // here, so ignore the return value.
+ local_ptr = varp == &p_lcs ? &wp->w_p_lcs : &wp->w_p_fcs;
+ if (**local_ptr == NUL) {
+ (void)set_chars_option(wp, local_ptr, true);
+ }
}
- } else if (varp == &p_wak) { // 'winaltkeys'
- if (*p_wak == NUL
- || check_opt_strings(p_wak, p_wak_values, false) != OK) {
- errmsg = e_invarg;
+ redraw_all_later(UPD_NOT_VALID);
+ }
+}
+
+static void did_set_verbosefile(char **errmsg)
+{
+ verbose_stop();
+ if (*p_vfile != NUL && verbose_open() == FAIL) {
+ *errmsg = e_invarg;
+ }
+}
+
+static void did_set_shada(vimoption_T **opt, int *opt_idx, bool *free_oldval, char *errbuf,
+ size_t errbuflen, char **errmsg)
+{
+ // TODO(ZyX-I): Remove this code in the future, alongside with &viminfo
+ // option.
+ *opt_idx = (((*opt)->fullname[0] == 'v')
+ ? (shada_idx == -1 ? ((shada_idx = findoption("shada"))) : shada_idx)
+ : *opt_idx);
+ *opt = get_option(*opt_idx);
+ // Update free_oldval now that we have the opt_idx for 'shada', otherwise
+ // there would be a disconnect between the check for P_ALLOCED at the start
+ // of the function and the set of P_ALLOCED at the end of the function.
+ *free_oldval = ((*opt)->flags & P_ALLOCED);
+ for (char *s = p_shada; *s;) {
+ // Check it's a valid character
+ if (vim_strchr("!\"%'/:<@cfhnrs", (uint8_t)(*s)) == NULL) {
+ *errmsg = illegal_char(errbuf, errbuflen, *s);
+ break;
}
- } else if (varp == &p_ei) { // 'eventignore'
- if (check_ei() == FAIL) {
- errmsg = e_invarg;
+ if (*s == 'n') { // name is always last one
+ break;
+ } else if (*s == 'r') { // skip until next ','
+ while (*++s && *s != ',') {}
+ } else if (*s == '%') {
+ // optional number
+ while (ascii_isdigit(*++s)) {}
+ } else if (*s == '!' || *s == 'h' || *s == 'c') {
+ s++; // no extra chars
+ } else { // must have a number
+ while (ascii_isdigit(*++s)) {}
+
+ if (!ascii_isdigit(*(s - 1))) {
+ if (errbuf != NULL) {
+ vim_snprintf(errbuf, errbuflen,
+ _("E526: Missing number after <%s>"),
+ transchar_byte((uint8_t)(*(s - 1))));
+ *errmsg = errbuf;
+ } else {
+ *errmsg = "";
+ }
+ break;
+ }
}
- } else if (varp == &p_enc || gvarp == &p_fenc || gvarp == &p_menc) {
- // 'encoding', 'fileencoding' and 'makeencoding'
- if (gvarp == &p_fenc) {
- if (!MODIFIABLE(curbuf) && opt_flags != OPT_GLOBAL) {
- errmsg = e_modifiable;
- } else if (vim_strchr((char *)(*varp), ',') != NULL) {
- // No comma allowed in 'fileencoding'; catches confusing it
- // with 'fileencodings'.
- errmsg = e_invarg;
+ if (*s == ',') {
+ s++;
+ } else if (*s) {
+ if (errbuf != NULL) {
+ *errmsg = N_("E527: Missing comma");
} else {
- // May show a "+" in the title now.
- redraw_titles();
- // Add 'fileencoding' to the swap file.
- ml_setflags(curbuf);
+ *errmsg = "";
}
+ break;
}
+ }
+ if (*p_shada && *errmsg == NULL && get_shada_parameter('\'') < 0) {
+ *errmsg = N_("E528: Must specify a ' value");
+ }
+}
- if (errmsg == NULL) {
- // canonize the value, so that STRCMP() can be used on it
- p = (char *)enc_canonize(*varp);
- xfree(*varp);
- *varp = (char_u *)p;
- if (varp == &p_enc) {
- // only encoding=utf-8 allowed
- if (STRCMP(p_enc, "utf-8") != 0) {
- errmsg = e_unsupportedoption;
- } else {
- spell_reload();
- }
- }
+static void did_set_showbreak(char **varp, char **errmsg)
+{
+ for (char *s = *varp; *s;) {
+ if (ptr2cells(s) != 1) {
+ *errmsg = e_showbreak_contains_unprintable_or_wide_character;
}
- } else if (varp == &p_penc) {
- // Canonize printencoding if VIM standard one
- p = (char *)enc_canonize(p_penc);
- xfree(p_penc);
- p_penc = (char_u *)p;
- } else if (varp == &curbuf->b_p_keymap) {
- if (!valid_filetype(*varp)) {
- errmsg = e_invarg;
- } else {
- int secure_save = secure;
+ MB_PTR_ADV(s);
+ }
+}
+
+static void did_set_titleiconstring(char **varp)
+{
+ // 'titlestring' and 'iconstring'
+ int flagval = (varp == &p_titlestring) ? STL_IN_TITLE : STL_IN_ICON;
+
+ // NULL => statusline syntax
+ if (vim_strchr(*varp, '%') && check_stl_option(*varp) == NULL) {
+ stl_syntax |= flagval;
+ } else {
+ stl_syntax &= ~flagval;
+ }
+ did_set_title();
+}
+
+static void did_set_selection(char **errmsg)
+{
+ if (*p_sel == NUL || check_opt_strings(p_sel, p_sel_values, false) != OK) {
+ *errmsg = e_invarg;
+ }
+}
- // Reset the secure flag, since the value of 'keymap' has
- // been checked to be safe.
- secure = 0;
+static void did_set_keymodel(char **errmsg)
+{
+ if (check_opt_strings(p_km, p_km_values, true) != OK) {
+ *errmsg = e_invarg;
+ return;
+ }
+ km_stopsel = (vim_strchr(p_km, 'o') != NULL);
+ km_startsel = (vim_strchr(p_km, 'a') != NULL);
+}
+
+static void did_set_display(char **errmsg)
+{
+ if (opt_strings_flags(p_dy, p_dy_values, &dy_flags, true) != OK) {
+ *errmsg = e_invarg;
+ return;
+ }
+ (void)init_chartab();
+ msg_grid_validate();
+}
- // load or unload key mapping tables
- errmsg = keymap_init();
+static void did_set_spellfile(char **varp, char **errmsg)
+{
+ // When there is a window for this buffer in which 'spell'
+ // is set load the wordlists.
- secure = secure_save;
+ if ((!valid_spellfile(*varp))) {
+ *errmsg = e_invarg;
+ } else {
+ *errmsg = did_set_spell_option(true);
+ }
+}
- // Since we check the value, there is no need to set P_INSECURE,
- // even when the value comes from a modeline.
- *value_checked = true;
+static void did_set_spell(char **varp, char **errmsg)
+{
+ // When there is a window for this buffer in which 'spell'
+ // is set load the wordlists.
+ if (!valid_spelllang(*varp)) {
+ *errmsg = e_invarg;
+ } else {
+ *errmsg = did_set_spell_option(false);
+ }
+}
+
+static void did_set_spellcapcheck(win_T *win, char **errmsg)
+{
+ // When 'spellcapcheck' is set compile the regexp program.
+ *errmsg = compile_cap_prog(win->w_s);
+}
+
+static void did_set_spelloptions(win_T *win, char **errmsg)
+{
+ if (opt_strings_flags(win->w_s->b_p_spo, p_spo_values, &(win->w_s->b_p_spo_flags),
+ true) != OK) {
+ *errmsg = e_invarg;
+ }
+}
+
+static void did_set_spellsuggest(char **errmsg)
+{
+ if (spell_check_sps() != OK) {
+ *errmsg = e_invarg;
+ }
+}
+
+static void did_set_mkspellmem(char **errmsg)
+{
+ if (spell_check_msm() != OK) {
+ *errmsg = e_invarg;
+ }
+}
+
+static void did_set_buftype(buf_T *buf, win_T *win, char **errmsg)
+{
+ // When 'buftype' is set, check for valid value.
+ if ((buf->terminal && buf->b_p_bt[0] != 't')
+ || (!buf->terminal && buf->b_p_bt[0] == 't')
+ || check_opt_strings(buf->b_p_bt, p_buftype_values, false) != OK) {
+ *errmsg = e_invarg;
+ } else {
+ if (win->w_status_height || global_stl_height()) {
+ win->w_redr_status = true;
+ redraw_later(win, UPD_VALID);
}
+ buf->b_help = (buf->b_p_bt[0] == 'h');
+ redraw_titles();
+ }
+}
- if (errmsg == NULL) {
- if (*curbuf->b_p_keymap != NUL) {
- // Installed a new keymap, switch on using it.
- curbuf->b_p_iminsert = B_IMODE_LMAP;
- if (curbuf->b_p_imsearch != B_IMODE_USE_INSERT) {
- curbuf->b_p_imsearch = B_IMODE_LMAP;
- }
- } else {
- // Cleared the keymap, may reset 'iminsert' and 'imsearch'.
- if (curbuf->b_p_iminsert == B_IMODE_LMAP) {
- curbuf->b_p_iminsert = B_IMODE_NONE;
- }
- if (curbuf->b_p_imsearch == B_IMODE_LMAP) {
- curbuf->b_p_imsearch = B_IMODE_USE_INSERT;
- }
- }
- if ((opt_flags & OPT_LOCAL) == 0) {
- set_iminsert_global();
- set_imsearch_global();
- }
- status_redraw_curbuf();
+// 'statusline', 'winbar', 'tabline', 'rulerformat' or 'statuscolumn'
+static void did_set_statusline(win_T *win, char **varp, char **gvarp, char **errmsg)
+{
+ if (varp == &p_ruf) { // reset ru_wid first
+ ru_wid = 0;
+ } else if (varp == &win->w_p_stc) {
+ win->w_nrwidth_line_count = 0;
+ }
+ char *s = *varp;
+ if (varp == &p_ruf && *s == '%') {
+ // set ru_wid if 'ruf' starts with "%99("
+ if (*++s == '-') { // ignore a '-'
+ s++;
}
- } else if (gvarp == &p_ff) { // 'fileformat'
- if (!MODIFIABLE(curbuf) && !(opt_flags & OPT_GLOBAL)) {
- errmsg = e_modifiable;
- } else if (check_opt_strings(*varp, p_ff_values, false) != OK) {
- errmsg = e_invarg;
+ int wid = getdigits_int(&s, true, 0);
+ if (wid && *s == '(' && (*errmsg = check_stl_option(p_ruf)) == NULL) {
+ ru_wid = wid;
} else {
- redraw_titles();
- // update flag in swap file
- ml_setflags(curbuf);
- // Redraw needed when switching to/from "mac": a CR in the text
- // will be displayed differently.
- if (get_fileformat(curbuf) == EOL_MAC || *oldval == 'm') {
- redraw_curbuf_later(NOT_VALID);
- }
- }
- } else if (varp == (char_u **)&p_ffs) { // 'fileformats'
- if (check_opt_strings((char_u *)p_ffs, p_ff_values, true) != OK) {
- errmsg = e_invarg;
+ *errmsg = check_stl_option(p_ruf);
}
- } else if (gvarp == &p_mps) { // 'matchpairs'
- for (p = (char *)(*varp); *p != NUL; p++) {
- int x2 = -1;
- int x3 = -1;
+ } else if (varp == &p_ruf || s[0] != '%' || s[1] != '!') {
+ // check 'statusline', 'winbar', 'tabline' or 'statuscolumn'
+ // only if it doesn't start with "%!"
+ *errmsg = check_stl_option(s);
+ }
+ if (varp == &p_ruf && *errmsg == NULL) {
+ comp_col();
+ }
+ // add / remove window bars for 'winbar'
+ if (gvarp == &p_wbr) {
+ set_winbar(true);
+ }
+}
- p += utfc_ptr2len(p);
- if (*p != NUL) {
- x2 = (unsigned char)(*p++);
- }
- if (*p != NUL) {
- x3 = utf_ptr2char(p);
- p += utfc_ptr2len(p);
- }
- if (x2 != ':' || x3 == -1 || (*p != NUL && *p != ',')) {
- errmsg = e_invarg;
- break;
- }
- if (*p == NUL) {
- break;
- }
+static void did_set_complete(char **varp, char *errbuf, size_t errbuflen, char **errmsg)
+{
+ // check if it is a valid value for 'complete' -- Acevedo
+ for (char *s = *varp; *s;) {
+ while (*s == ',' || *s == ' ') {
+ s++;
}
- } else if (gvarp == &p_com) { // 'comments'
- for (s = (char *)(*varp); *s;) {
- while (*s && *s != ':') {
- if (vim_strchr(COM_ALL, *s) == NULL
- && !ascii_isdigit(*s) && *s != '-') {
- errmsg = illegal_char(errbuf, errbuflen, *s);
- break;
- }
- s++;
- }
- if (*s++ == NUL) {
- errmsg = N_("E524: Missing colon");
- } else if (*s == ',' || *s == NUL) {
- errmsg = N_("E525: Zero length string");
- }
- if (errmsg != NULL) {
- break;
- }
- while (*s && *s != ',') {
- if (*s == '\\' && s[1] != NUL) {
- s++;
- }
- s++;
- }
- s = (char *)skip_to_option_part((char_u *)s);
- }
- } else if (varp == &p_lcs) { // global 'listchars'
- errmsg = set_chars_option(curwin, varp, false);
- if (errmsg == NULL) {
- // The current window is set to use the global 'listchars' value.
- // So clear the window-local value.
- if (!(opt_flags & OPT_GLOBAL)) {
- clear_string_option(&curwin->w_p_lcs);
- }
- FOR_ALL_TAB_WINDOWS(tp, wp) {
- // If no error was returned above, we don't expect an error
- // here, so ignore the return value.
- (void)set_chars_option(wp, &wp->w_p_lcs, true);
- }
- redraw_all_later(NOT_VALID);
- }
- } else if (varp == &curwin->w_p_lcs) { // local 'listchars'
- errmsg = set_chars_option(curwin, varp, true);
- } else if (varp == &p_fcs) { // global 'fillchars'
- errmsg = set_chars_option(curwin, varp, false);
- if (errmsg == NULL) {
- // The current window is set to use the global 'fillchars' value.
- // So clear the window-local value.
- if (!(opt_flags & OPT_GLOBAL)) {
- clear_string_option(&curwin->w_p_fcs);
- }
- FOR_ALL_TAB_WINDOWS(tp, wp) {
- // If no error was returned above, we don't expect an error
- // here, so ignore the return value.
- (void)set_chars_option(wp, &wp->w_p_fcs, true);
- }
- redraw_all_later(NOT_VALID);
+ if (!*s) {
+ break;
}
- } else if (varp == &curwin->w_p_fcs) { // local 'fillchars'
- errmsg = set_chars_option(curwin, varp, true);
- } else if (varp == &p_cedit) { // 'cedit'
- errmsg = check_cedit();
- } else if (varp == &p_vfile) { // 'verbosefile'
- verbose_stop();
- if (*p_vfile != NUL && verbose_open() == FAIL) {
- errmsg = e_invarg;
+ if (vim_strchr(".wbuksid]tU", (uint8_t)(*s)) == NULL) {
+ *errmsg = illegal_char(errbuf, errbuflen, *s);
+ break;
}
- } else if (varp == &p_shada) { // 'shada'
- // TODO(ZyX-I): Remove this code in the future, alongside with &viminfo
- // option.
- opt_idx = ((get_option_fullname(opt_idx)[0] == 'v')
- ? (shada_idx == -1 ? ((shada_idx = findoption("shada"))) : shada_idx)
- : opt_idx);
- // Update free_oldval now that we have the opt_idx for 'shada', otherwise
- // there would be a disconnect between the check for P_ALLOCED at the start
- // of the function and the set of P_ALLOCED at the end of the function.
- free_oldval = (get_option_flags(opt_idx) & P_ALLOCED);
- for (s = (char *)p_shada; *s;) {
- // Check it's a valid character
- if (vim_strchr("!\"%'/:<@cfhnrs", *s) == NULL) {
- errmsg = illegal_char(errbuf, errbuflen, *s);
- break;
- }
- if (*s == 'n') { // name is always last one
- break;
- } else if (*s == 'r') { // skip until next ','
- while (*++s && *s != ',') {}
- } else if (*s == '%') {
- // optional number
- while (ascii_isdigit(*++s)) {}
- } else if (*s == '!' || *s == 'h' || *s == 'c') {
- s++; // no extra chars
- } else { // must have a number
- while (ascii_isdigit(*++s)) {}
-
- if (!ascii_isdigit(*(s - 1))) {
- if (errbuf != NULL) {
- vim_snprintf(errbuf, errbuflen,
- _("E526: Missing number after <%s>"),
- transchar_byte(*(s - 1)));
- errmsg = errbuf;
- } else {
- errmsg = "";
+ if (*++s != NUL && *s != ',' && *s != ' ') {
+ if (s[-1] == 'k' || s[-1] == 's') {
+ // skip optional filename after 'k' and 's'
+ while (*s && *s != ',' && *s != ' ') {
+ if (*s == '\\' && s[1] != NUL) {
+ s++;
}
- break;
+ s++;
}
- }
- if (*s == ',') {
- s++;
- } else if (*s) {
+ } else {
if (errbuf != NULL) {
- errmsg = N_("E527: Missing comma");
+ vim_snprintf(errbuf, errbuflen,
+ _("E535: Illegal character after <%c>"),
+ *--s);
+ *errmsg = errbuf;
} else {
- errmsg = "";
+ *errmsg = "";
}
break;
}
}
- if (*p_shada && errmsg == NULL && get_shada_parameter('\'') < 0) {
- errmsg = N_("E528: Must specify a ' value");
+ }
+}
+
+static void did_set_completeopt(char **errmsg)
+{
+ if (check_opt_strings(p_cot, p_cot_values, true) != OK) {
+ *errmsg = e_invarg;
+ } else {
+ completeopt_was_set();
+ }
+}
+
+static void did_set_signcolumn(win_T *win, char **varp, const char *oldval, char **errmsg)
+{
+ if (check_signcolumn(*varp) != OK) {
+ *errmsg = e_invarg;
+ }
+ // When changing the 'signcolumn' to or from 'number', recompute the
+ // width of the number column if 'number' or 'relativenumber' is set.
+ if (((*oldval == 'n' && *(oldval + 1) == 'u')
+ || (*win->w_p_scl == 'n' && *(win->w_p_scl + 1) == 'u'))
+ && (win->w_p_nu || win->w_p_rnu)) {
+ win->w_nrwidth_line_count = 0;
+ }
+}
+
+static void did_set_foldcolumn(char **varp, char **errmsg)
+{
+ if (**varp == NUL || check_opt_strings(*varp, p_fdc_values, false) != OK) {
+ *errmsg = e_invarg;
+ }
+}
+
+static void did_set_pastetoggle(void)
+{
+ // 'pastetoggle': translate key codes like in a mapping
+ if (*p_pt) {
+ char *p = NULL;
+ (void)replace_termcodes(p_pt,
+ strlen(p_pt),
+ &p, REPTERM_FROM_PART | REPTERM_DO_LT, NULL,
+ CPO_TO_CPO_FLAGS);
+ if (p != NULL) {
+ free_string_option(p_pt);
+ p_pt = p;
+ }
+ }
+}
+
+static void did_set_backspace(char **errmsg)
+{
+ if (ascii_isdigit(*p_bs)) {
+ if (*p_bs > '3' || p_bs[1] != NUL) {
+ *errmsg = e_invarg;
}
- } else if (gvarp == &p_sbr) { // 'showbreak'
- for (s = (char *)(*varp); *s;) {
- if (ptr2cells(s) != 1) {
- errmsg = N_("E595: 'showbreak' contains unprintable or wide character");
- }
- MB_PTR_ADV(s);
+ } else if (check_opt_strings(p_bs, p_bs_values, true) != OK) {
+ *errmsg = e_invarg;
+ }
+}
+
+static void did_set_tagcase(buf_T *buf, int opt_flags, char **errmsg)
+{
+ unsigned int *flags;
+ char *p;
+
+ if (opt_flags & OPT_LOCAL) {
+ p = buf->b_p_tc;
+ flags = &buf->b_tc_flags;
+ } else {
+ p = p_tc;
+ flags = &tc_flags;
+ }
+
+ if ((opt_flags & OPT_LOCAL) && *p == NUL) {
+ // make the local value empty: use the global value
+ *flags = 0;
+ } else if (*p == NUL
+ || opt_strings_flags(p, p_tc_values, flags, false) != OK) {
+ *errmsg = e_invarg;
+ }
+}
+
+static void did_set_diffopt(char **errmsg)
+{
+ if (diffopt_changed() == FAIL) {
+ *errmsg = e_invarg;
+ }
+}
+
+static void did_set_foldmethod(win_T *win, char **varp, char **errmsg)
+{
+ if (check_opt_strings(*varp, p_fdm_values, false) != OK
+ || *win->w_p_fdm == NUL) {
+ *errmsg = e_invarg;
+ } else {
+ foldUpdateAll(win);
+ if (foldmethodIsDiff(win)) {
+ newFoldLevel();
+ }
+ }
+}
+
+static void did_set_foldmarker(win_T *win, char **varp, char **errmsg)
+{
+ char *p = vim_strchr(*varp, ',');
+ if (p == NULL) {
+ *errmsg = N_("E536: comma required");
+ } else if (p == *varp || p[1] == NUL) {
+ *errmsg = e_invarg;
+ } else if (foldmethodIsMarker(win)) {
+ foldUpdateAll(win);
+ }
+}
+
+static void did_set_commentstring(char **varp, char **errmsg)
+{
+ if (**varp != NUL && strstr(*varp, "%s") == NULL) {
+ *errmsg = N_("E537: 'commentstring' must be empty or contain %s");
+ }
+}
+
+static void did_set_foldignore(win_T *win)
+{
+ if (foldmethodIsIndent(win)) {
+ foldUpdateAll(win);
+ }
+}
+
+static void did_set_virtualedit(win_T *win, int opt_flags, char *oldval, char **errmsg)
+{
+ char *ve = p_ve;
+ unsigned int *flags = &ve_flags;
+
+ if (opt_flags & OPT_LOCAL) {
+ ve = win->w_p_ve;
+ flags = &win->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_win(win);
+ coladvance(win->w_virtcol);
+ }
+ }
+}
+
+static void did_set_lispoptions(char **varp, char **errmsg)
+{
+ if (**varp != NUL && strcmp(*varp, "expr:0") != 0 && strcmp(*varp, "expr:1") != 0) {
+ *errmsg = e_invarg;
+ }
+}
+
+static void did_set_filetype_or_syntax(char **varp, char *oldval, int *value_checked,
+ bool *value_changed, char **errmsg)
+{
+ if (!valid_filetype(*varp)) {
+ *errmsg = e_invarg;
+ return;
+ }
+
+ *value_changed = strcmp(oldval, *varp) != 0;
+
+ // Since we check the value, there is no need to set P_INSECURE,
+ // even when the value comes from a modeline.
+ *value_checked = true;
+}
+
+static void did_set_winhl(win_T *win, char **errmsg)
+{
+ if (!parse_winhl_opt(win)) {
+ *errmsg = e_invarg;
+ }
+}
+
+static void did_set_varsoftabstop(buf_T *buf, char **varp, char **errmsg)
+{
+ if (!(*varp)[0] || ((*varp)[0] == '0' && !(*varp)[1])) {
+ XFREE_CLEAR(buf->b_p_vsts_array);
+ return;
+ }
+
+ for (char *cp = *varp; *cp; cp++) {
+ if (ascii_isdigit(*cp)) {
+ continue;
+ }
+ if (*cp == ',' && cp > *varp && *(cp - 1) != ',') {
+ continue;
+ }
+ *errmsg = e_invarg;
+ return;
+ }
+
+ long *oldarray = buf->b_p_vsts_array;
+ if (tabstop_set(*varp, &(buf->b_p_vsts_array))) {
+ xfree(oldarray);
+ } else {
+ *errmsg = e_invarg;
+ }
+}
+
+static void did_set_vartabstop(buf_T *buf, win_T *win, char **varp, char **errmsg)
+{
+ if (!(*varp)[0] || ((*varp)[0] == '0' && !(*varp)[1])) {
+ XFREE_CLEAR(buf->b_p_vts_array);
+ return;
+ }
+
+ for (char *cp = *varp; *cp; cp++) {
+ if (ascii_isdigit(*cp)) {
+ continue;
+ }
+ if (*cp == ',' && cp > *varp && *(cp - 1) != ',') {
+ continue;
+ }
+ *errmsg = e_invarg;
+ return;
+ }
+
+ long *oldarray = buf->b_p_vts_array;
+ if (tabstop_set(*varp, &(buf->b_p_vts_array))) {
+ xfree(oldarray);
+ if (foldmethodIsIndent(win)) {
+ foldUpdateAll(win);
+ }
+ } else {
+ *errmsg = e_invarg;
+ }
+}
+
+static void did_set_optexpr(win_T *win, char **p_opt, char **varp, char **gvarp)
+{
+ char *name = get_scriptlocal_funcname(*p_opt);
+ if (name != NULL) {
+ free_string_option(*p_opt);
+ *p_opt = name;
+ }
+}
+
+// handle option that is a list of flags.
+static void did_set_option_listflag(char **varp, char *flags, char *errbuf, size_t errbuflen,
+ char **errmsg)
+{
+ for (char *s = *varp; *s; s++) {
+ if (vim_strchr(flags, *s) == NULL) {
+ *errmsg = illegal_char(errbuf, errbuflen, *s);
+ break;
+ }
+ }
+}
+
+// When 'syntax' is set, load the syntax of that name
+static void do_syntax_autocmd(buf_T *buf, bool value_changed)
+{
+ static int syn_recursive = 0;
+
+ syn_recursive++;
+ // Only pass true for "force" when the value changed or not used
+ // recursively, to avoid endless recurrence.
+ apply_autocmds(EVENT_SYNTAX, buf->b_p_syn, buf->b_fname,
+ value_changed || syn_recursive == 1, buf);
+ buf->b_flags |= BF_SYN_SET;
+ syn_recursive--;
+}
+
+static void do_filetype_autocmd(buf_T *buf, char **varp, int opt_flags, bool value_changed)
+{
+ // 'filetype' is set, trigger the FileType autocommand
+ // Skip this when called from a modeline and the filetype was
+ // already set to this value.
+ if (!(opt_flags & OPT_MODELINE) || value_changed) {
+ static int ft_recursive = 0;
+ int secure_save = secure;
+
+ // Reset the secure flag, since the value of 'filetype' has
+ // been checked to be safe.
+ secure = 0;
+
+ ft_recursive++;
+ did_filetype = true;
+ // Only pass true for "force" when the value changed or not
+ // used recursively, to avoid endless recurrence.
+ apply_autocmds(EVENT_FILETYPE, buf->b_p_ft, buf->b_fname,
+ value_changed || ft_recursive == 1, buf);
+ ft_recursive--;
+ // Just in case the old "buf" is now invalid
+ if (varp != &(buf->b_p_ft)) {
+ varp = NULL;
+ }
+ secure = secure_save;
+ }
+}
+
+static void did_set_spelllang_source(win_T *win)
+{
+ char fname[200];
+ char *q = win->w_s->b_p_spl;
+
+ // Skip the first name if it is "cjk".
+ if (strncmp(q, "cjk,", 4) == 0) {
+ q += 4;
+ }
+
+ // Source the spell/LANG.vim in 'runtimepath'.
+ // They could set 'spellcapcheck' depending on the language.
+ // Use the first name in 'spelllang' up to '_region' or
+ // '.encoding'.
+ char *p;
+ for (p = q; *p != NUL; p++) {
+ if (!ASCII_ISALNUM(*p) && *p != '-') {
+ break;
}
+ }
+ if (p > q) {
+ vim_snprintf(fname, sizeof(fname), "spell/%.*s.vim", (int)(p - q), q);
+ source_runtime(fname, DIP_ALL);
+ }
+}
+
+/// Handle string options that need some action to perform when changed.
+/// The new value must be allocated.
+///
+/// @param opt_idx index in options[] table
+/// @param varp pointer to the option variable
+/// @param oldval previous value of the option
+/// @param errbuf buffer for errors, or NULL
+/// @param errbuflen length of errors buffer
+/// @param opt_flags OPT_LOCAL and/or OPT_GLOBAL
+/// @param value_checked value was checked to be safe, no need to set P_INSECURE
+///
+/// @return NULL for success, or an untranslated error message for an error
+static char *did_set_string_option_for(buf_T *buf, win_T *win, int opt_idx, char **varp,
+ char *oldval, char *errbuf, size_t errbuflen, int opt_flags,
+ int *value_checked)
+{
+ char *errmsg = NULL;
+ bool did_chartab = false;
+ vimoption_T *opt = get_option(opt_idx);
+ bool free_oldval = (opt->flags & P_ALLOCED);
+ bool value_changed = false;
+
+ // Get the global option to compare with, otherwise we would have to check
+ // two values for all local options.
+ char **gvarp = (char **)get_varp_scope(opt, OPT_GLOBAL);
+
+ // Disallow changing some options from secure mode
+ if ((secure || sandbox != 0)
+ && (opt->flags & P_SECURE)) {
+ errmsg = e_secure;
+ } else if (check_illegal_path_names(*varp, opt->flags)) {
+ // Check for a "normal" directory or file name in some options.
+ errmsg = e_invarg;
+ } else if (gvarp == &p_bkc) { // 'backupcopy'
+ did_set_backupcopy(buf, oldval, opt_flags, &errmsg);
+ } else if (varp == &p_bex || varp == &p_pm) { // 'backupext' and 'patchmode'
+ did_set_backupext_or_patchmode(&errmsg);
+ } else if (varp == &win->w_p_briopt) { // 'breakindentopt'
+ did_set_breakindentopt(win, &errmsg);
+ } else if (varp == &p_isi
+ || varp == &buf->b_p_isk
+ || varp == &p_isp
+ || varp == &p_isf) {
+ // 'isident', 'iskeyword', 'isprint or 'isfname' option
+ did_set_isopt(buf, &did_chartab, &errmsg);
+ } else if (varp == &p_hf) { // 'helpfile'
+ did_set_helpfile();
+ } else if (varp == &p_rtp || varp == &p_pp) { // 'runtimepath' 'packpath'
+ runtime_search_path_invalidate();
+ } else if (varp == &win->w_p_culopt
+ || gvarp == &win->w_allbuf_opt.wo_culopt) { // 'cursorlineopt'
+ did_set_cursorlineopt(win, varp, &errmsg);
+ } else if (varp == &win->w_p_cc) { // 'colorcolumn'
+ errmsg = check_colorcolumn(win);
+ } else if (varp == &p_hlg) { // 'helplang'
+ did_set_helplang(&errmsg);
+ } else if (varp == &p_hl) { // 'highlight'
+ did_set_highlight(varp, &errmsg);
+ } else if (varp == &p_jop) { // 'jumpoptions'
+ did_set_opt_flags(p_jop, p_jop_values, &jop_flags, true, &errmsg);
+ } else if (gvarp == &p_nf) { // 'nrformats'
+ did_set_opt_strings(*varp, p_nf_values, true, &errmsg);
+ } else if (varp == &p_ssop) { // 'sessionoptions'
+ did_set_sessionoptions(oldval, &errmsg);
+ } else if (varp == &p_vop) { // 'viewoptions'
+ did_set_opt_flags(p_vop, p_ssop_values, &vop_flags, true, &errmsg);
+ } else if (varp == &p_rdb) { // 'redrawdebug'
+ did_set_opt_flags(p_rdb, p_rdb_values, &rdb_flags, true, &errmsg);
+ } else if (varp == &p_sbo) { // 'scrollopt'
+ did_set_opt_strings(p_sbo, p_scbopt_values, true, &errmsg);
+ } else if (varp == &p_ambw || (int *)varp == &p_emoji) { // 'ambiwidth'
+ did_set_ambiwidth(&errmsg);
+ } else if (varp == &p_bg) { // 'background'
+ did_set_background(&errmsg);
+ } else if (varp == &p_wim) { // 'wildmode'
+ did_set_wildmode(&errmsg);
+ } else if (varp == &p_wop) { // 'wildoptions'
+ did_set_opt_flags(p_wop, p_wop_values, &wop_flags, true, &errmsg);
+ } else if (varp == &p_wak) { // 'winaltkeys'
+ did_set_winaltkeys(&errmsg);
+ } else if (varp == &p_ei) { // 'eventignore'
+ did_set_eventignore(&errmsg);
+ } else if (varp == &p_enc || gvarp == &p_fenc || gvarp == &p_menc) {
+ // 'encoding', 'fileencoding' and 'makeencoding'
+ did_set_encoding(buf, varp, gvarp, opt_flags, &errmsg);
+ } else if (varp == &buf->b_p_keymap) {
+ did_set_keymap(buf, varp, opt_flags, value_checked, &errmsg);
+ } else if (gvarp == &p_ff) { // 'fileformat'
+ did_set_fileformat(buf, varp, oldval, opt_flags, &errmsg);
+ } else if (varp == &p_ffs) { // 'fileformats'
+ did_set_opt_strings(p_ffs, p_ff_values, true, &errmsg);
+ } else if (gvarp == &p_mps) { // 'matchpairs'
+ did_set_matchpairs(varp, &errmsg);
+ } else if (gvarp == &p_com) { // 'comments'
+ did_set_comments(varp, errbuf, errbuflen, &errmsg);
+ } else if (varp == &p_lcs || varp == &p_fcs) { // global 'listchars' or 'fillchars'
+ did_set_global_listfillchars(win, varp, opt_flags, &errmsg);
+ } else if (varp == &win->w_p_lcs) { // local 'listchars'
+ errmsg = set_chars_option(win, varp, true);
+ } else if (varp == &win->w_p_fcs) { // local 'fillchars'
+ errmsg = set_chars_option(win, varp, true);
+ } else if (varp == &p_cedit) { // 'cedit'
+ errmsg = check_cedit();
+ } else if (varp == &p_vfile) { // 'verbosefile'
+ did_set_verbosefile(&errmsg);
+ } else if (varp == &p_shada) { // 'shada'
+ did_set_shada(&opt, &opt_idx, &free_oldval, errbuf, errbuflen, &errmsg);
+ } else if (gvarp == &p_sbr) { // 'showbreak'
+ did_set_showbreak(varp, &errmsg);
} else if (varp == &p_guicursor) { // 'guicursor'
errmsg = parse_shape_opt(SHAPE_CURSOR);
- } else if (varp == &p_popt) {
- errmsg = parse_printoptions();
- } else if (varp == &p_pmfn) {
- errmsg = parse_printmbfont();
} else if (varp == &p_langmap) { // 'langmap'
langmap_set();
} else if (varp == &p_breakat) { // 'breakat'
fill_breakat_flags();
} else if (varp == &p_titlestring || varp == &p_iconstring) {
// 'titlestring' and 'iconstring'
- int flagval = (varp == &p_titlestring) ? STL_IN_TITLE : STL_IN_ICON;
-
- // NULL => statusline syntax
- if (vim_strchr((char *)(*varp), '%') && check_stl_option((char *)(*varp)) == NULL) {
- stl_syntax |= flagval;
- } else {
- stl_syntax &= ~flagval;
- }
- did_set_title();
+ did_set_titleiconstring(varp);
} else if (varp == &p_sel) { // 'selection'
- if (*p_sel == NUL
- || check_opt_strings(p_sel, p_sel_values, false) != OK) {
- errmsg = e_invarg;
- }
+ did_set_selection(&errmsg);
} else if (varp == &p_slm) { // 'selectmode'
- if (check_opt_strings(p_slm, p_slm_values, true) != OK) {
- errmsg = e_invarg;
- }
+ did_set_opt_strings(p_slm, p_slm_values, true, &errmsg);
} else if (varp == &p_km) { // 'keymodel'
- if (check_opt_strings(p_km, p_km_values, true) != OK) {
- errmsg = e_invarg;
- } else {
- km_stopsel = (vim_strchr((char *)p_km, 'o') != NULL);
- km_startsel = (vim_strchr((char *)p_km, 'a') != NULL);
- }
+ did_set_keymodel(&errmsg);
} else if (varp == &p_mousem) { // 'mousemodel'
- if (check_opt_strings(p_mousem, p_mousem_values, false) != OK) {
- errmsg = e_invarg;
- }
+ did_set_opt_strings(p_mousem, p_mousem_values, false, &errmsg);
} else if (varp == &p_mousescroll) { // 'mousescroll'
- errmsg = check_mousescroll((char *)p_mousescroll);
+ errmsg = check_mousescroll(p_mousescroll);
} else if (varp == &p_swb) { // 'switchbuf'
- if (opt_strings_flags(p_swb, p_swb_values, &swb_flags, true) != OK) {
- errmsg = e_invarg;
- }
+ did_set_opt_flags(p_swb, p_swb_values, &swb_flags, true, &errmsg);
+ } else if (varp == &p_spk) { // 'splitkeep'
+ did_set_opt_strings(p_spk, p_spk_values, false, &errmsg);
} else if (varp == &p_debug) { // 'debug'
- if (check_opt_strings(p_debug, p_debug_values, true) != OK) {
- errmsg = e_invarg;
- }
+ did_set_opt_strings(p_debug, p_debug_values, true, &errmsg);
} else if (varp == &p_dy) { // 'display'
- if (opt_strings_flags(p_dy, p_dy_values, &dy_flags, true) != OK) {
- errmsg = e_invarg;
- } else {
- (void)init_chartab();
- msg_grid_validate();
- }
+ did_set_display(&errmsg);
} else if (varp == &p_ead) { // 'eadirection'
- if (check_opt_strings(p_ead, p_ead_values, false) != OK) {
- errmsg = e_invarg;
- }
+ did_set_opt_strings(p_ead, p_ead_values, false, &errmsg);
} else if (varp == &p_cb) { // 'clipboard'
- if (opt_strings_flags(p_cb, p_cb_values, &cb_flags, true) != OK) {
- errmsg = e_invarg;
- }
- } else if (varp == &(curwin->w_s->b_p_spl) // 'spell'
- || varp == &(curwin->w_s->b_p_spf)) {
- // When 'spelllang' or 'spellfile' is set and there is a window for this
- // buffer in which 'spell' is set load the wordlists.
- const bool is_spellfile = varp == &(curwin->w_s->b_p_spf);
-
- if ((is_spellfile && !valid_spellfile(*varp))
- || (!is_spellfile && !valid_spelllang(*varp))) {
- errmsg = e_invarg;
- } else {
- errmsg = did_set_spell_option(is_spellfile);
- }
- } else if (varp == &(curwin->w_s->b_p_spc)) {
- // When 'spellcapcheck' is set compile the regexp program.
- errmsg = compile_cap_prog(curwin->w_s);
- } else if (varp == &(curwin->w_s->b_p_spo)) { // 'spelloptions'
- if (**varp != NUL && STRCMP("camel", *varp) != 0) {
- errmsg = e_invarg;
- }
+ did_set_opt_flags(p_cb, p_cb_values, &cb_flags, true, &errmsg);
+ } else if (varp == &win->w_s->b_p_spf) {
+ did_set_spellfile(varp, &errmsg);
+ } else if (varp == &win->w_s->b_p_spl) { // 'spell'
+ did_set_spell(varp, &errmsg);
+ } else if (varp == &win->w_s->b_p_spc) {
+ did_set_spellcapcheck(win, &errmsg);
+ } else if (varp == &win->w_s->b_p_spo) { // 'spelloptions'
+ did_set_spelloptions(win, &errmsg);
} else if (varp == &p_sps) { // 'spellsuggest'
- if (spell_check_sps() != OK) {
- errmsg = e_invarg;
- }
+ did_set_spellsuggest(&errmsg);
} else if (varp == &p_msm) { // 'mkspellmem'
- if (spell_check_msm() != OK) {
- errmsg = e_invarg;
- }
+ did_set_mkspellmem(&errmsg);
} else if (gvarp == &p_bh) {
- // When 'bufhidden' is set, check for valid value.
- if (check_opt_strings(curbuf->b_p_bh, p_bufhidden_values, false) != OK) {
- errmsg = e_invarg;
- }
- } else if (gvarp == &p_bt) {
- // When 'buftype' is set, check for valid value.
- if ((curbuf->terminal && curbuf->b_p_bt[0] != 't')
- || (!curbuf->terminal && curbuf->b_p_bt[0] == 't')
- || check_opt_strings(curbuf->b_p_bt, p_buftype_values, false) != OK) {
- errmsg = e_invarg;
- } else {
- 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 || 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
- ru_wid = 0;
- }
- s = (char *)(*varp);
- if (varp == &p_ruf && *s == '%') {
- // set ru_wid if 'ruf' starts with "%99("
- if (*++s == '-') { // ignore a '-'
- s++;
- }
- wid = getdigits_int(&s, true, 0);
- if (wid && *s == '(' && (errmsg = check_stl_option((char *)p_ruf)) == NULL) {
- ru_wid = wid;
- } else {
- errmsg = check_stl_option((char *)p_ruf);
- }
- } else if (varp == &p_ruf || s[0] != '%' || s[1] != '!') {
- // check 'statusline', 'winbar' or 'tabline' only if it doesn't start with "%!"
- errmsg = check_stl_option(s);
- }
- if (varp == &p_ruf && errmsg == NULL) {
- comp_col();
- }
- // add / remove window bars for 'winbar'
- if (gvarp == (char_u **)&p_wbr) {
- set_winbar(true);
- }
- } else if (gvarp == &p_cpt) {
- // check if it is a valid value for 'complete' -- Acevedo
- for (s = (char *)(*varp); *s;) {
- while (*s == ',' || *s == ' ') {
- s++;
- }
- if (!*s) {
- break;
- }
- if (vim_strchr(".wbuksid]tU", *s) == NULL) {
- errmsg = illegal_char(errbuf, errbuflen, *s);
- break;
- }
- if (*++s != NUL && *s != ',' && *s != ' ') {
- if (s[-1] == 'k' || s[-1] == 's') {
- // skip optional filename after 'k' and 's'
- while (*s && *s != ',' && *s != ' ') {
- if (*s == '\\' && s[1] != NUL) {
- s++;
- }
- s++;
- }
- } else {
- if (errbuf != NULL) {
- vim_snprintf(errbuf, errbuflen,
- _("E535: Illegal character after <%c>"),
- *--s);
- errmsg = errbuf;
- } else {
- errmsg = "";
- }
- break;
- }
- }
- }
+ did_set_opt_strings(buf->b_p_bh, p_bufhidden_values, false, &errmsg);
+ } else if (gvarp == &p_bt) { // 'buftype'
+ did_set_buftype(buf, win, &errmsg);
+ } else if (gvarp == &p_stl || gvarp == &p_wbr || varp == &p_tal
+ || varp == &p_ruf || varp == &win->w_p_stc) {
+ // 'statusline', 'winbar', 'tabline', 'rulerformat' or 'statuscolumn'
+ did_set_statusline(win, varp, gvarp, &errmsg);
+ } else if (gvarp == &p_cpt) { // 'complete'
+ did_set_complete(varp, errbuf, errbuflen, &errmsg);
} else if (varp == &p_cot) { // 'completeopt'
- if (check_opt_strings(p_cot, p_cot_values, true) != OK) {
- errmsg = e_invarg;
- } else {
- completeopt_was_set();
- }
+ did_set_completeopt(&errmsg);
#ifdef BACKSLASH_IN_FILENAME
} else if (gvarp == &p_csl) { // 'completeslash'
if (check_opt_strings(p_csl, p_csl_values, false) != OK
- || check_opt_strings(curbuf->b_p_csl, p_csl_values, false) != OK) {
+ || check_opt_strings(buf->b_p_csl, p_csl_values, false) != OK) {
errmsg = e_invarg;
}
#endif
- } else if (varp == &curwin->w_p_scl) { // 'signcolumn'
- if (check_signcolumn(*varp) != OK) {
- errmsg = e_invarg;
- }
- // When changing the 'signcolumn' to or from 'number', recompute the
- // width of the number column if 'number' or 'relativenumber' is set.
- if (((*oldval == 'n' && *(oldval + 1) == 'u')
- || (*curwin->w_p_scl == 'n' && *(curwin->w_p_scl + 1) == 'u'))
- && (curwin->w_p_nu || curwin->w_p_rnu)) {
- curwin->w_nrwidth_line_count = 0;
- }
- } else if (varp == &curwin->w_p_fdc || varp == &curwin->w_allbuf_opt.wo_fdc) {
+ } else if (varp == &win->w_p_scl) { // 'signcolumn'
+ did_set_signcolumn(win, varp, oldval, &errmsg);
+ } else if (varp == &p_sloc) { // 'showcmdloc'
+ did_set_opt_strings(*varp, p_sloc_values, false, &errmsg);
+ } else if (varp == &win->w_p_fdc
+ || varp == &win->w_allbuf_opt.wo_fdc) {
// 'foldcolumn'
- if (**varp == NUL || check_opt_strings(*varp, p_fdc_values, false) != OK) {
- errmsg = e_invarg;
- }
- } else if (varp == &p_pt) {
- // 'pastetoggle': translate key codes like in a mapping
- if (*p_pt) {
- p = NULL;
- (void)replace_termcodes((char *)p_pt,
- STRLEN(p_pt),
- &p, REPTERM_FROM_PART | REPTERM_DO_LT, NULL,
- CPO_TO_CPO_FLAGS);
- if (p != NULL) {
- free_string_option(p_pt);
- p_pt = (char_u *)p;
- }
- }
+ did_set_foldcolumn(varp, &errmsg);
+ } else if (varp == &p_pt) { // 'pastetoggle'
+ did_set_pastetoggle();
} else if (varp == &p_bs) { // 'backspace'
- if (ascii_isdigit(*p_bs)) {
- if (*p_bs > '3' || p_bs[1] != NUL) {
- errmsg = e_invarg;
- }
- } else if (check_opt_strings(p_bs, p_bs_values, true) != OK) {
- errmsg = e_invarg;
- }
+ did_set_backspace(&errmsg);
} else if (varp == &p_bo) {
- if (opt_strings_flags(p_bo, p_bo_values, &bo_flags, true) != OK) {
- errmsg = e_invarg;
- }
+ did_set_opt_flags(p_bo, p_bo_values, &bo_flags, true, &errmsg);
} else if (gvarp == &p_tc) { // 'tagcase'
- unsigned int *flags;
-
- if (opt_flags & OPT_LOCAL) {
- p = (char *)curbuf->b_p_tc;
- flags = &curbuf->b_tc_flags;
- } else {
- p = (char *)p_tc;
- flags = &tc_flags;
- }
-
- if ((opt_flags & OPT_LOCAL) && *p == NUL) {
- // make the local value empty: use the global value
- *flags = 0;
- } else if (*p == NUL
- || opt_strings_flags((char_u *)p, p_tc_values, flags, false) != OK) {
- errmsg = e_invarg;
- }
+ did_set_tagcase(buf, opt_flags, &errmsg);
} else if (varp == &p_cmp) { // 'casemap'
- if (opt_strings_flags(p_cmp, p_cmp_values, &cmp_flags, true) != OK) {
- errmsg = e_invarg;
- }
+ did_set_opt_flags(p_cmp, p_cmp_values, &cmp_flags, true, &errmsg);
} else if (varp == &p_dip) { // 'diffopt'
- if (diffopt_changed() == FAIL) {
- errmsg = e_invarg;
- }
- } else if (gvarp == &curwin->w_allbuf_opt.wo_fdm) { // 'foldmethod'
- if (check_opt_strings(*varp, p_fdm_values, false) != OK
- || *curwin->w_p_fdm == NUL) {
- errmsg = e_invarg;
- } else {
- foldUpdateAll(curwin);
- if (foldmethodIsDiff(curwin)) {
- newFoldLevel();
- }
- }
- } else if (varp == &curwin->w_p_fde) { // 'foldexpr'
- if (foldmethodIsExpr(curwin)) {
- foldUpdateAll(curwin);
- }
- } else if (gvarp == &curwin->w_allbuf_opt.wo_fmr) { // 'foldmarker'
- p = vim_strchr((char *)(*varp), ',');
- if (p == NULL) {
- errmsg = N_("E536: comma required");
- } else if ((char_u *)p == *varp || p[1] == NUL) {
- errmsg = e_invarg;
- } else if (foldmethodIsMarker(curwin)) {
- foldUpdateAll(curwin);
- }
+ did_set_diffopt(&errmsg);
+ } else if (gvarp == &win->w_allbuf_opt.wo_fdm) { // 'foldmethod'
+ did_set_foldmethod(win, varp, &errmsg);
+ } else if (gvarp == &win->w_allbuf_opt.wo_fmr) { // 'foldmarker'
+ did_set_foldmarker(win, varp, &errmsg);
} else if (gvarp == &p_cms) { // 'commentstring'
- if (**varp != NUL && strstr((char *)(*varp), "%s") == NULL) {
- errmsg = N_("E537: 'commentstring' must be empty or contain %s");
- }
+ did_set_commentstring(varp, &errmsg);
} else if (varp == &p_fdo) { // 'foldopen'
- if (opt_strings_flags(p_fdo, p_fdo_values, &fdo_flags, true) != OK) {
- errmsg = e_invarg;
- }
+ did_set_opt_flags(p_fdo, p_fdo_values, &fdo_flags, true, &errmsg);
} else if (varp == &p_fcl) { // 'foldclose'
- if (check_opt_strings(p_fcl, p_fcl_values, true) != OK) {
- errmsg = e_invarg;
- }
- } else if (gvarp == &curwin->w_allbuf_opt.wo_fdi) { // 'foldignore'
- if (foldmethodIsIndent(curwin)) {
- foldUpdateAll(curwin);
- }
+ did_set_opt_strings(*varp, p_fcl_values, true, &errmsg);
+ } else if (gvarp == &win->w_allbuf_opt.wo_fdi) { // 'foldignore'
+ did_set_foldignore(win);
} 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 = (char *)p_csqf;
- while (*p != NUL) {
- if (vim_strchr(CSQF_CMDS, *p) == NULL
- || p[1] == NUL
- || vim_strchr(CSQF_FLAGS, p[1]) == NULL
- || (p[2] != NUL && p[2] != ',')) {
- errmsg = e_invarg;
- break;
- } else if (p[2] == NUL) {
- break;
- } else {
- p += 3;
- }
- }
- }
+ did_set_virtualedit(win, opt_flags, oldval, &errmsg);
} else if (gvarp == &p_cino) { // 'cinoptions'
// TODO(vim): recognize errors
- parse_cino(curbuf);
+ parse_cino(buf);
+ } else if (gvarp == &p_lop) { // 'lispoptions'
+ did_set_lispoptions(varp, &errmsg);
} else if (varp == &p_icm) { // 'inccommand'
- if (check_opt_strings(p_icm, p_icm_values, false) != OK) {
- errmsg = e_invarg;
- }
- } else if (gvarp == &p_ft) {
- if (!valid_filetype(*varp)) {
- errmsg = e_invarg;
- } else {
- value_changed = STRCMP(oldval, *varp) != 0;
-
- // Since we check the value, there is no need to set P_INSECURE,
- // even when the value comes from a modeline.
- *value_checked = true;
- }
- } else if (gvarp == &p_syn) {
- if (!valid_filetype(*varp)) {
- errmsg = e_invarg;
- } else {
- value_changed = STRCMP(oldval, *varp) != 0;
-
- // Since we check the value, there is no need to set P_INSECURE,
- // even when the value comes from a modeline.
- *value_checked = true;
- }
- } else if (varp == &curwin->w_p_winhl) {
- if (!parse_winhl_opt(curwin)) {
- errmsg = e_invarg;
- }
+ did_set_opt_strings(*varp, p_icm_values, false, &errmsg);
+ } else if (gvarp == &p_ft || gvarp == &p_syn) {
+ did_set_filetype_or_syntax(varp, oldval, value_checked, &value_changed, &errmsg);
+ } else if (varp == &win->w_p_winhl) {
+ did_set_winhl(win, &errmsg);
} else if (varp == &p_tpf) {
- if (opt_strings_flags(p_tpf, p_tpf_values, &tpf_flags, true) != OK) {
- errmsg = e_invarg;
- }
- } else if (varp == &(curbuf->b_p_vsts)) { // 'varsofttabstop'
- char_u *cp;
-
- if (!(*varp)[0] || ((*varp)[0] == '0' && !(*varp)[1])) {
- XFREE_CLEAR(curbuf->b_p_vsts_array);
- } else {
- for (cp = *varp; *cp; cp++) {
- if (ascii_isdigit(*cp)) {
- continue;
- }
- if (*cp == ',' && cp > *varp && *(cp - 1) != ',') {
- continue;
- }
- errmsg = e_invarg;
- break;
- }
- if (errmsg == NULL) {
- long *oldarray = curbuf->b_p_vsts_array;
- if (tabstop_set(*varp, &(curbuf->b_p_vsts_array))) {
- xfree(oldarray);
- } else {
- errmsg = e_invarg;
- }
- }
- }
- } else if (varp == &(curbuf->b_p_vts)) { // 'vartabstop'
- char_u *cp;
-
- if (!(*varp)[0] || ((*varp)[0] == '0' && !(*varp)[1])) {
- XFREE_CLEAR(curbuf->b_p_vts_array);
- } else {
- for (cp = *varp; *cp; cp++) {
- if (ascii_isdigit(*cp)) {
- continue;
- }
- if (*cp == ',' && cp > *varp && *(cp - 1) != ',') {
- continue;
- }
- errmsg = e_invarg;
- break;
- }
- if (errmsg == NULL) {
- long *oldarray = curbuf->b_p_vts_array;
- if (tabstop_set(*varp, &(curbuf->b_p_vts_array))) {
- xfree(oldarray);
- if (foldmethodIsIndent(curwin)) {
- foldUpdateAll(curwin);
- }
- } else {
- errmsg = e_invarg;
- }
- }
- }
+ did_set_opt_flags(p_tpf, p_tpf_values, &tpf_flags, true, &errmsg);
+ } else if (varp == &buf->b_p_vsts) { // 'varsofttabstop'
+ did_set_varsoftabstop(buf, varp, &errmsg);
+ } else if (varp == &buf->b_p_vts) { // 'vartabstop'
+ did_set_vartabstop(buf, win, varp, &errmsg);
+ } else if (varp == &p_dex) { // 'diffexpr'
+ did_set_optexpr(win, &p_dex, varp, gvarp);
+ } else if (varp == &win->w_p_fde) { // 'foldexpr'
+ did_set_optexpr(win, &win->w_p_fde, varp, gvarp);
+ if (foldmethodIsExpr(win)) {
+ foldUpdateAll(win);
+ }
+ } else if (varp == &win->w_p_fdt) { // 'foldtext'
+ did_set_optexpr(win, &win->w_p_fdt, varp, gvarp);
+ } else if (varp == &p_pex) { // 'patchexpr'
+ did_set_optexpr(win, &p_pex, varp, gvarp);
+ } else if (gvarp == &p_fex) { // 'formatexpr'
+ did_set_optexpr(win, &buf->b_p_fex, varp, gvarp);
+ } else if (gvarp == &p_inex) { // 'includeexpr'
+ did_set_optexpr(win, &buf->b_p_inex, varp, gvarp);
+ } else if (gvarp == &p_inde) { // 'indentexpr'
+ did_set_optexpr(win, &buf->b_p_inde, varp, gvarp);
+ } else if (gvarp == &p_cfu) { // 'completefunc'
+ set_completefunc_option(&errmsg);
+ } else if (gvarp == &p_ofu) { // 'omnifunc'
+ set_omnifunc_option(buf, &errmsg);
+ } else if (gvarp == &p_tsrfu) { // 'thesaurusfunc'
+ set_thesaurusfunc_option(&errmsg);
} else if (varp == &p_opfunc) { // 'operatorfunc'
- if (set_operatorfunc_option() == FAIL) {
- errmsg = e_invarg;
- }
+ set_operatorfunc_option(&errmsg);
} else if (varp == &p_qftf) { // 'quickfixtextfunc'
- if (qf_process_qftf_option() == FAIL) {
- errmsg = e_invarg;
- }
- } else {
- // Options that are a list of flags.
- p = NULL;
- if (varp == &p_ww) { // 'whichwrap'
- p = WW_ALL;
- }
- if (varp == &p_shm) { // 'shortmess'
- p = (char *)SHM_ALL;
- } else if (varp == (char_u **)&(p_cpo)) { // 'cpoptions'
- p = CPO_VI;
- } else if (varp == &(curbuf->b_p_fo)) { // 'formatoptions'
- p = FO_ALL;
- } else if (varp == &curwin->w_p_cocu) { // 'concealcursor'
- p = COCU_ALL;
- } else if (varp == &p_mouse) { // 'mouse'
- p = MOUSE_ALL;
- }
- if (p != NULL) {
- for (s = (char *)(*varp); *s; s++) {
- if (vim_strchr(p, *s) == NULL) {
- errmsg = illegal_char(errbuf, errbuflen, *s);
- break;
- }
- }
+ qf_process_qftf_option(&errmsg);
+ } else if (gvarp == &p_tfu) { // 'tagfunc'
+ set_tagfunc_option(&errmsg);
+ } else if (varp == &p_ww) { // 'whichwrap'
+ did_set_option_listflag(varp, WW_ALL, errbuf, errbuflen, &errmsg);
+ } else if (varp == &p_shm) { // 'shortmess'
+ did_set_option_listflag(varp, SHM_ALL, errbuf, errbuflen, &errmsg);
+ } else if (varp == &p_cpo) { // 'cpoptions'
+ did_set_option_listflag(varp, CPO_VI, errbuf, errbuflen, &errmsg);
+ } else if (varp == &buf->b_p_fo) { // 'formatoptions'
+ did_set_option_listflag(varp, FO_ALL, errbuf, errbuflen, &errmsg);
+ } else if (varp == &win->w_p_cocu) { // 'concealcursor'
+ did_set_option_listflag(varp, COCU_ALL, errbuf, errbuflen, &errmsg);
+ } else if (varp == &p_mouse) { // 'mouse'
+ did_set_option_listflag(varp, MOUSE_ALL, errbuf, errbuflen, &errmsg);
+ } else if (gvarp == &p_flp) {
+ if (win->w_briopt_list) {
+ // Changing Formatlistpattern when briopt includes the list setting:
+ // redraw
+ redraw_all_later(UPD_NOT_VALID);
}
}
@@ -1509,7 +1863,7 @@ char *did_set_string_option(int opt_idx, char_u **varp, char_u *oldval, char *er
*varp = oldval;
// When resetting some values, need to act on it.
if (did_chartab) {
- (void)init_chartab();
+ (void)buf_init_chartab(buf, true);
}
} else {
// Remember where the option was set.
@@ -1520,81 +1874,27 @@ char *did_set_string_option(int opt_idx, char_u **varp, char_u *oldval, char *er
if (free_oldval) {
free_string_option(oldval);
}
- set_option_flag(opt_idx, P_ALLOCED);
+ opt->flags |= P_ALLOCED;
if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0
- && is_global_local_option(opt_idx)) {
+ && (opt->indir & PV_BOTH)) {
// global option with local value set to use global value; free
// the local value and make it empty
- p = (char *)get_option_varp_scope(opt_idx, OPT_LOCAL);
- free_string_option(*(char_u **)p);
- *(char_u **)p = empty_option;
+ char *p = get_varp_scope(opt, OPT_LOCAL);
+ free_string_option(*(char **)p);
+ *(char **)p = empty_option;
} else if (!(opt_flags & OPT_LOCAL) && opt_flags != OPT_GLOBAL) {
// May set global value for local option.
- set_string_option_global(opt_idx, varp);
+ set_string_option_global(opt, varp);
}
// Trigger the autocommand only after setting the flags.
- // When 'syntax' is set, load the syntax of that name
- if (varp == &(curbuf->b_p_syn)) {
- static int syn_recursive = 0;
-
- syn_recursive++;
- // Only pass true for "force" when the value changed or not used
- // recursively, to avoid endless recurrence.
- 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--;
- } else if (varp == &(curbuf->b_p_ft)) {
- // 'filetype' is set, trigger the FileType autocommand
- // Skip this when called from a modeline and the filetype was
- // already set to this value.
- if (!(opt_flags & OPT_MODELINE) || value_changed) {
- static int ft_recursive = 0;
- int secure_save = secure;
-
- // Reset the secure flag, since the value of 'filetype' has
- // been checked to be safe.
- secure = 0;
-
- ft_recursive++;
- did_filetype = true;
- // Only pass true for "force" when the value changed or not
- // used recursively, to avoid endless recurrence.
- 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
- if (varp != &(curbuf->b_p_ft)) {
- varp = NULL;
- }
- secure = secure_save;
- }
- }
- if (varp == &(curwin->w_s->b_p_spl)) {
- char_u fname[200];
- char_u *q = curwin->w_s->b_p_spl;
-
- // Skip the first name if it is "cjk".
- if (STRNCMP(q, "cjk,", 4) == 0) {
- q += 4;
- }
-
- // Source the spell/LANG.vim in 'runtimepath'.
- // They could set 'spellcapcheck' depending on the language.
- // Use the first name in 'spelllang' up to '_region' or
- // '.encoding'.
- for (p = (char *)q; *p != NUL; p++) {
- if (!ASCII_ISALNUM(*p) && *p != '-') {
- break;
- }
- }
- if (p > (char *)q) {
- vim_snprintf((char *)fname, sizeof(fname), "spell/%.*s.vim",
- (int)(p - (char *)q), q);
- source_runtime((char *)fname, DIP_ALL);
- }
+ if (varp == &buf->b_p_syn) {
+ do_syntax_autocmd(buf, value_changed);
+ } else if (varp == &buf->b_p_ft) {
+ do_filetype_autocmd(buf, varp, opt_flags, value_changed);
+ } else if (varp == &win->w_s->b_p_spl) {
+ did_set_spelllang_source(win);
}
}
@@ -1602,22 +1902,29 @@ char *did_set_string_option(int opt_idx, char_u **varp, char_u *oldval, char *er
setmouse(); // in case 'mouse' changed
}
- if (curwin->w_curswant != MAXCOL
- && (get_option_flags(opt_idx) & (P_CURSWANT | P_RALL)) != 0) {
- curwin->w_set_curswant = true;
+ if (win->w_curswant != MAXCOL
+ && (opt->flags & (P_CURSWANT | P_RALL)) != 0) {
+ win->w_set_curswant = true;
}
- check_redraw(get_option_flags(opt_idx));
+ check_redraw_for(buf, win, opt->flags);
return errmsg;
}
+char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf, size_t errbuflen,
+ int opt_flags, int *value_checked)
+{
+ return did_set_string_option_for(curbuf, curwin, opt_idx, varp, oldval, errbuf, errbuflen,
+ opt_flags, value_checked);
+}
+
/// Check an option that can be a range of string values.
///
/// @param list when true: accept a list of values
///
/// @return OK for correct value, FAIL otherwise. Empty is always OK.
-static int check_opt_strings(char_u *val, char **values, int list)
+static int check_opt_strings(char *val, char **values, int list)
{
return opt_strings_flags(val, values, NULL, list);
}
@@ -1630,7 +1937,7 @@ static int check_opt_strings(char_u *val, char **values, int list)
/// @param list when true: accept a list of values
///
/// @return OK for correct value, FAIL otherwise. Empty is always OK.
-static int opt_strings_flags(char_u *val, char **values, unsigned *flagp, bool list)
+static int opt_strings_flags(char *val, char **values, unsigned *flagp, bool list)
{
unsigned int new_flags = 0;
@@ -1640,8 +1947,8 @@ static int opt_strings_flags(char_u *val, char **values, unsigned *flagp, bool l
return FAIL;
}
- size_t len = STRLEN(values[i]);
- if (STRNCMP(values[i], val, len) == 0
+ size_t len = strlen(values[i]);
+ if (strncmp(values[i], val, len) == 0
&& ((list && val[len] == ',') || val[len] == NUL)) {
val += len + (val[len] == ',');
assert(i < sizeof(1U) * 8);
@@ -1658,7 +1965,7 @@ static int opt_strings_flags(char_u *val, char **values, unsigned *flagp, bool l
}
/// @return OK if "p" is a valid fileformat name, FAIL otherwise.
-int check_ff_value(char_u *p)
+int check_ff_value(char *p)
{
return check_opt_strings(p, p_ff_values, false);
}
diff --git a/src/nvim/optionstr.h b/src/nvim/optionstr.h
index ac8d90e10e..3520cc2061 100644
--- a/src/nvim/optionstr.h
+++ b/src/nvim/optionstr.h
@@ -1,7 +1,7 @@
#ifndef NVIM_OPTIONSTR_H
#define NVIM_OPTIONSTR_H
-#include "nvim/buffer_defs.h" // for buf_T, win_T
+#include "nvim/buffer_defs.h"
#include "nvim/option_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/os/dl.c b/src/nvim/os/dl.c
index 7d095d31e3..519cef7876 100644
--- a/src/nvim/os/dl.c
+++ b/src/nvim/os/dl.c
@@ -4,13 +4,14 @@
/// Functions for using external native libraries
#include <stdbool.h>
+#include <stddef.h>
#include <stdint.h>
#include <uv.h>
+#include "nvim/gettext.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/os/dl.h"
-#include "nvim/os/os.h"
/// possible function prototypes that can be called by os_libcall()
/// int -> int
diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index 795bff66cb..0611de14aa 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -4,23 +4,36 @@
// Environment inspection
#include <assert.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
#include <uv.h>
+#include "auto/config.h"
#include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
#include "nvim/eval.h"
+#include "nvim/ex_cmds_defs.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/log.h"
#include "nvim/macros.h"
#include "nvim/map.h"
#include "nvim/memory.h"
#include "nvim/message.h"
+#include "nvim/option_defs.h"
#include "nvim/os/os.h"
#include "nvim/path.h"
#include "nvim/strings.h"
+#include "nvim/types.h"
#include "nvim/version.h"
#include "nvim/vim.h"
-#ifdef WIN32
+#ifdef MSWIN
# include "nvim/mbyte.h" // for utf8_to_utf16, utf16_to_utf8
#endif
@@ -128,7 +141,7 @@ int os_setenv(const char *name, const char *value, int overwrite)
if (name[0] == '\0') {
return -1;
}
-#ifdef WIN32
+#ifdef MSWIN
if (!overwrite && os_getenv(name) != NULL) {
return 0;
}
@@ -143,7 +156,7 @@ int os_setenv(const char *name, const char *value, int overwrite)
#endif
uv_mutex_lock(&mutex);
int r;
-#ifdef WIN32
+#ifdef MSWIN
// libintl uses getenv() for LC_ALL/LANG/etc., so we must use _putenv_s().
if (striequal(name, "LC_ALL") || striequal(name, "LANGUAGE")
|| striequal(name, "LANG") || striequal(name, "LC_MESSAGES")) {
@@ -186,7 +199,7 @@ int os_unsetenv(const char *name)
size_t os_get_fullenv_size(void)
{
size_t len = 0;
-#ifdef _WIN32
+#ifdef MSWIN
wchar_t *envstrings = GetEnvironmentStringsW();
wchar_t *p = envstrings;
size_t l;
@@ -235,7 +248,7 @@ void os_free_fullenv(char **env)
/// @param env_size size of `env`, @see os_fullenv_size
void os_copy_fullenv(char **env, size_t env_size)
{
-#ifdef _WIN32
+#ifdef MSWIN
wchar_t *envstrings = GetEnvironmentStringsW();
if (!envstrings) {
return;
@@ -280,7 +293,7 @@ void os_copy_fullenv(char **env, size_t env_size)
/// @return [allocated] environment variable's value, or NULL
char *os_getenvname_at_index(size_t index)
{
-#ifdef _WIN32
+#ifdef MSWIN
wchar_t *envstrings = GetEnvironmentStringsW();
if (!envstrings) {
return NULL;
@@ -347,7 +360,7 @@ char *os_getenvname_at_index(size_t index)
/// @return the process ID.
int64_t os_get_pid(void)
{
-#ifdef _WIN32
+#ifdef MSWIN
return (int64_t)GetCurrentProcessId();
#else
return (int64_t)getpid();
@@ -368,7 +381,7 @@ void os_get_hostname(char *hostname, size_t size)
} else {
xstrlcpy(hostname, vutsname.nodename, size);
}
-#elif defined(WIN32)
+#elif defined(MSWIN)
wchar_t host_utf16[MAX_COMPUTERNAME_LENGTH + 1];
DWORD host_wsize = sizeof(host_utf16) / sizeof(host_utf16[0]);
if (GetComputerNameW(host_utf16, &host_wsize) == 0) {
@@ -418,7 +431,7 @@ void init_homedir(void)
const char *var = os_getenv("HOME");
-#ifdef WIN32
+#ifdef MSWIN
// Typically, $HOME is not defined on Windows, unless the user has
// specifically defined it for Vim's sake. However, on Windows NT
// platforms, $HOMEDRIVE and $HOMEPATH are automatically defined for
@@ -451,7 +464,7 @@ void init_homedir(void)
var = NULL;
const char *exp = os_getenv(os_buf);
if (exp != NULL && *exp != NUL
- && STRLEN(exp) + STRLEN(p) < MAXPATHL) {
+ && strlen(exp) + strlen(p) < MAXPATHL) {
vim_snprintf(os_buf, MAXPATHL, "%s%s", exp, p + 1);
var = os_buf;
}
@@ -475,7 +488,7 @@ void init_homedir(void)
if (var != NULL) {
// Change to the directory and get the actual path. This resolves
// links. Don't do it when we can't return.
- if (os_dirname((char_u *)os_buf, MAXPATHL) == OK && os_chdir(os_buf) == 0) {
+ if (os_dirname(os_buf, MAXPATHL) == OK && os_chdir(os_buf) == 0) {
if (!os_chdir(var) && os_dirname(IObuff, IOSIZE) == OK) {
var = (char *)IObuff;
}
@@ -487,7 +500,7 @@ void init_homedir(void)
// Fall back to current working directory if home is not found
if ((var == NULL || *var == NUL)
- && os_dirname((char_u *)os_buf, sizeof(os_buf)) == OK) {
+ && os_dirname(os_buf, sizeof(os_buf)) == OK) {
var = os_buf;
}
#endif
@@ -530,7 +543,7 @@ void free_homedir(void)
/// @see {expand_env}
char *expand_env_save(char *src)
{
- return (char *)expand_env_save_opt((char_u *)src, false);
+ return expand_env_save_opt(src, false);
}
/// Similar to expand_env_save() but when "one" is `true` handle the string as
@@ -538,9 +551,9 @@ char *expand_env_save(char *src)
/// @param src String containing environment variables to expand
/// @param one Should treat as only one file name
/// @see {expand_env}
-char_u *expand_env_save_opt(char_u *src, bool one)
+char *expand_env_save_opt(char *src, bool one)
{
- char_u *p = xmalloc(MAXPATHL);
+ char *p = xmalloc(MAXPATHL);
expand_env_esc(src, p, MAXPATHL, false, one, NULL);
return p;
}
@@ -553,7 +566,7 @@ char_u *expand_env_save_opt(char_u *src, bool one)
/// @param src Input string e.g. "$HOME/vim.hlp"
/// @param dst[out] Where to put the result
/// @param dstlen Maximum length of the result
-void expand_env(char_u *src, char_u *dst, int dstlen)
+void expand_env(char *src, char *dst, int dstlen)
{
expand_env_esc(src, dst, dstlen, false, false, NULL);
}
@@ -567,34 +580,34 @@ void expand_env(char_u *src, char_u *dst, int dstlen)
/// @param esc Escape spaces in expanded variables
/// @param one `srcp` is a single filename
/// @param prefix Start again after this (can be NULL)
-void expand_env_esc(char_u *restrict srcp, char_u *restrict dst, int dstlen, bool esc, bool one,
- char_u *prefix)
+void expand_env_esc(char *restrict srcp, char *restrict dst, int dstlen, bool esc, bool one,
+ char *prefix)
FUNC_ATTR_NONNULL_ARG(1, 2)
{
- char_u *tail;
- char_u *var;
+ char *tail;
+ char *var;
bool copy_char;
bool mustfree; // var was allocated, need to free it later
bool at_start = true; // at start of a name
- int prefix_len = (prefix == NULL) ? 0 : (int)STRLEN(prefix);
+ int prefix_len = (prefix == NULL) ? 0 : (int)strlen(prefix);
- char *src = skipwhite((char *)srcp);
+ char *src = skipwhite(srcp);
dstlen--; // leave one char space for "\,"
while (*src && dstlen > 0) {
// Skip over `=expr`.
if (src[0] == '`' && src[1] == '=') {
- var = (char_u *)src;
+ var = src;
src += 2;
(void)skip_expr(&src);
if (*src == '`') {
src++;
}
- size_t len = (size_t)(src - (char *)var);
+ size_t len = (size_t)(src - var);
if (len > (size_t)dstlen) {
len = (size_t)dstlen;
}
- memcpy((char *)dst, (char *)var, len);
+ memcpy(dst, var, len);
dst += len;
dstlen -= (int)len;
continue;
@@ -607,7 +620,7 @@ void expand_env_esc(char_u *restrict srcp, char_u *restrict dst, int dstlen, boo
// The variable name is copied into dst temporarily, because it may
// be a string in read-only memory and a NUL needs to be appended.
if (*src != '~') { // environment var
- tail = (char_u *)src + 1;
+ tail = src + 1;
var = dst;
int c = dstlen - 1;
@@ -621,7 +634,7 @@ void expand_env_esc(char_u *restrict srcp, char_u *restrict dst, int dstlen, boo
} else // NOLINT
#endif
{
- while (c-- > 0 && *tail != NUL && vim_isIDc(*tail)) {
+ while (c-- > 0 && *tail != NUL && vim_isIDc((uint8_t)(*tail))) {
*var++ = *tail++;
}
}
@@ -636,25 +649,25 @@ void expand_env_esc(char_u *restrict srcp, char_u *restrict dst, int dstlen, boo
}
#endif
*var = NUL;
- var = (char_u *)vim_getenv((char *)dst);
+ var = vim_getenv(dst);
mustfree = true;
#if defined(UNIX)
}
#endif
} else if (src[1] == NUL // home directory
|| vim_ispathsep(src[1])
- || vim_strchr(" ,\t\n", src[1]) != NULL) {
- var = (char_u *)homedir;
- tail = (char_u *)src + 1;
+ || vim_strchr(" ,\t\n", (uint8_t)src[1]) != NULL) {
+ var = homedir;
+ tail = src + 1;
} else { // user directory
#if defined(UNIX)
// Copy ~user to dst[], so we can put a NUL after it.
- tail = (char_u *)src;
+ tail = src;
var = dst;
int c = dstlen - 1;
while (c-- > 0
&& *tail
- && vim_isfilec(*tail)
+ && vim_isfilec((uint8_t)(*tail))
&& !vim_ispathsep(*tail)) {
*var++ = *tail++;
}
@@ -662,7 +675,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_userdir((char *)dst + 1);
+ : os_get_userdir(dst + 1);
mustfree = true;
if (var == NULL) {
expand_T xpc;
@@ -676,7 +689,7 @@ void expand_env_esc(char_u *restrict srcp, char_u *restrict dst, int dstlen, boo
#else
// cannot expand user's home directory, so don't try
var = NULL;
- tail = (char_u *)""; // for gcc
+ tail = ""; // for gcc
#endif // UNIX
}
@@ -684,7 +697,7 @@ void expand_env_esc(char_u *restrict srcp, char_u *restrict dst, int dstlen, boo
// If 'shellslash' is set change backslashes to forward slashes.
// Can't use slash_adjust(), p_ssl may be set temporarily.
if (p_ssl && var != NULL && vim_strchr(var, '\\') != NULL) {
- char_u *p = vim_strsave(var);
+ char *p = xstrdup(var);
if (mustfree) {
xfree(var);
@@ -697,8 +710,8 @@ 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 && strpbrk((char *)var, " \t") != NULL) {
- char_u *p = vim_strsave_escaped(var, (char_u *)" \t");
+ if (esc && var != NULL && strpbrk(var, " \t") != NULL) {
+ char *p = vim_strsave_escaped(var, " \t");
if (mustfree) {
xfree(var);
@@ -708,13 +721,13 @@ void expand_env_esc(char_u *restrict srcp, char_u *restrict dst, int dstlen, boo
}
if (var != NULL && *var != NUL
- && (STRLEN(var) + STRLEN(tail) + 1 < (unsigned)dstlen)) {
+ && (strlen(var) + strlen(tail) + 1 < (unsigned)dstlen)) {
STRCPY(dst, var);
- dstlen -= (int)STRLEN(var);
- int c = (int)STRLEN(var);
+ dstlen -= (int)strlen(var);
+ int c = (int)strlen(var);
// if var[] ends in a path separator and tail[] starts
// with it, skip a character
- if (after_pathsep((char *)dst, (char *)dst + c)
+ if (after_pathsep(dst, dst + c)
#if defined(BACKSLASH_IN_FILENAME)
&& dst[-1] != ':'
#endif
@@ -722,7 +735,7 @@ void expand_env_esc(char_u *restrict srcp, char_u *restrict dst, int dstlen, boo
tail++;
}
dst += c;
- src = (char *)tail;
+ src = tail;
copy_char = false;
}
if (mustfree) {
@@ -736,18 +749,18 @@ void expand_env_esc(char_u *restrict srcp, char_u *restrict dst, int dstlen, boo
// ":edit foo ~ foo".
at_start = false;
if (src[0] == '\\' && src[1] != NUL) {
- *dst++ = (char_u)(*src++);
+ *dst++ = *src++;
dstlen--;
} else if ((src[0] == ' ' || src[0] == ',') && !one) {
at_start = true;
}
if (dstlen > 0) {
- *dst++ = (char_u)(*src++);
+ *dst++ = *src++;
dstlen--;
if (prefix != NULL
- && src - prefix_len >= (char *)srcp
- && STRNCMP(src - prefix_len, prefix, prefix_len) == 0) {
+ && src - prefix_len >= srcp
+ && strncmp(src - prefix_len, prefix, (size_t)prefix_len) == 0) {
at_start = true;
}
}
@@ -765,12 +778,12 @@ static char *vim_version_dir(const char *vimdir)
return NULL;
}
char *p = concat_fnames(vimdir, VIM_VERSION_NODOT, true);
- if (os_isdir((char_u *)p)) {
+ if (os_isdir(p)) {
return p;
}
xfree(p);
p = concat_fnames(vimdir, RUNTIME_DIRNAME, true);
- if (os_isdir((char_u *)p)) {
+ if (os_isdir(p)) {
return p;
}
xfree(p);
@@ -800,11 +813,11 @@ static char *vim_version_dir(const char *vimdir)
/// @return The new pend including dirname or just pend
static char *remove_tail(char *path, char *pend, char *dirname)
{
- size_t len = STRLEN(dirname);
+ size_t len = strlen(dirname);
char *new_tail = pend - len - 1;
if (new_tail >= path
- && FNAMENCMP((char_u *)new_tail, (char_u *)dirname, len) == 0
+ && path_fnamencmp(new_tail, dirname, len) == 0
&& (new_tail == path || after_pathsep(path, new_tail))) {
return new_tail;
}
@@ -837,10 +850,9 @@ const void *vim_env_iter(const char delim, const char *const val, const void *co
if (dirend == NULL) {
*len = strlen(varval);
return NULL;
- } else {
- *len = (size_t)(dirend - varval);
- return dirend + 1;
}
+ *len = (size_t)(dirend - varval);
+ return dirend + 1;
}
/// Iterates $PATH-like delimited list `val` in reverse order.
@@ -870,11 +882,10 @@ const void *vim_env_iter_rev(const char delim, const char *const val, const void
*len = varlen;
*dir = val;
return NULL;
- } else {
- *dir = colon + 1;
- *len = (size_t)(varend - colon);
- return colon - 1;
}
+ *dir = colon + 1;
+ *len = (size_t)(varend - colon);
+ return colon - 1;
}
/// @param[out] exe_name should be at least MAXPATHL in size
@@ -900,7 +911,7 @@ char *vim_getenv(const char *name)
// init_path() should have been called before now.
assert(get_vim_var_str(VV_PROGPATH)[0] != NUL);
-#ifdef WIN32
+#ifdef MSWIN
if (strcmp(name, "HOME") == 0) {
return xstrdup(homedir);
}
@@ -937,8 +948,8 @@ 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((char *)p_hf, '$') == NULL) {
- vim_path = (char *)p_hf;
+ if (p_hf != NULL && vim_strchr(p_hf, '$') == NULL) {
+ vim_path = p_hf;
}
char exe_name[MAXPATHL];
@@ -957,7 +968,7 @@ char *vim_getenv(const char *name)
char *vim_path_end = path_tail(vim_path);
// remove "doc/" from 'helpfile', if present
- if (vim_path == (char *)p_hf) {
+ if (vim_path == p_hf) {
vim_path_end = remove_tail(vim_path, vim_path_end, "doc");
}
@@ -976,7 +987,7 @@ char *vim_getenv(const char *name)
assert(vim_path_end >= vim_path);
vim_path = xstrndup(vim_path, (size_t)(vim_path_end - vim_path));
- if (!os_isdir((char_u *)vim_path)) {
+ if (!os_isdir(vim_path)) {
xfree(vim_path);
vim_path = NULL;
}
@@ -1045,7 +1056,7 @@ size_t home_replace(const buf_T *const buf, const char *src, char *const dst, si
}
if (buf != NULL && buf->b_help) {
- const size_t dlen = STRLCPY(dst, path_tail((char *)src), dstlen);
+ const size_t dlen = xstrlcpy(dst, path_tail((char *)src), dstlen);
return MIN(dlen, dstlen - 1);
}
@@ -1056,7 +1067,7 @@ size_t home_replace(const buf_T *const buf, const char *src, char *const dst, si
}
const char *homedir_env = os_getenv("HOME");
-#ifdef WIN32
+#ifdef MSWIN
if (homedir_env == NULL) {
homedir_env = os_getenv("USERPROFILE");
}
@@ -1098,7 +1109,7 @@ size_t home_replace(const buf_T *const buf, const char *src, char *const dst, si
size_t len = dirlen;
for (;;) {
if (len
- && FNAMENCMP(src, (char_u *)p, len) == 0
+ && path_fnamencmp(src, p, len) == 0
&& (vim_ispathsep(src[len])
|| (!one && (src[len] == ',' || src[len] == ' '))
|| src[len] == NUL)) {
@@ -1119,10 +1130,16 @@ size_t home_replace(const buf_T *const buf, const char *src, char *const dst, si
len = envlen;
}
+ if (dstlen == 0) {
+ break; // Avoid overflowing below.
+ }
// if (!one) skip to separator: space or comma.
while (*src && (one || (*src != ',' && *src != ' ')) && --dstlen > 0) {
*dst_p++ = *src++;
}
+ if (dstlen == 0) {
+ break; // Avoid overflowing below.
+ }
// Skip separator.
while ((*src == ' ' || *src == ',') && --dstlen > 0) {
*dst_p++ = *src++;
@@ -1146,7 +1163,7 @@ char *home_replace_save(buf_T *buf, const char *src)
{
size_t len = 3; // space for "~/" and trailing NUL
if (src != NULL) { // just in case
- len += STRLEN(src);
+ len += strlen(src);
}
char *dst = xmalloc(len);
home_replace(buf, src, dst, len, true);
@@ -1156,15 +1173,12 @@ char *home_replace_save(buf_T *buf, const char *src)
/// Function given to ExpandGeneric() to obtain an environment variable name.
char *get_env_name(expand_T *xp, int idx)
{
-#define ENVNAMELEN 100
- // this static buffer is needed to avoid a memory leak in ExpandGeneric
- static char_u name[ENVNAMELEN];
assert(idx >= 0);
char *envname = os_getenvname_at_index((size_t)idx);
if (envname) {
- STRLCPY(name, envname, ENVNAMELEN);
+ xstrlcpy(xp->xp_buf, envname, EXPAND_BUF_LEN);
xfree(envname);
- return (char *)name;
+ return xp->xp_buf;
}
return NULL;
}
@@ -1177,14 +1191,14 @@ char *get_env_name(expand_T *xp, int idx)
bool os_setenv_append_path(const char *fname)
FUNC_ATTR_NONNULL_ALL
{
-#ifdef WIN32
+#ifdef MSWIN
// 8191 (plus NUL) is considered the practical maximum.
# define MAX_ENVPATHLEN 8192
#else
// No prescribed maximum on unix.
# define MAX_ENVPATHLEN INT_MAX
#endif
- if (!path_is_absolute((char_u *)fname)) {
+ if (!path_is_absolute(fname)) {
internal_error("os_setenv_append_path()");
return false;
}
diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c
index b1710737d0..5af39555c9 100644
--- a/src/nvim/os/fileio.c
+++ b/src/nvim/os/fileio.c
@@ -11,22 +11,21 @@
#include <fcntl.h>
#include <stdbool.h>
#include <stddef.h>
-
-#include "auto/config.h"
-
-#ifdef HAVE_SYS_UIO_H
-# include <sys/uio.h>
-#endif
-
+#include <stdint.h>
#include <uv.h>
+#include "auto/config.h"
+#include "nvim/gettext.h"
#include "nvim/globals.h"
+#include "nvim/log.h"
#include "nvim/macros.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/os/fileio.h"
#include "nvim/os/os.h"
+#include "nvim/os/os_defs.h"
#include "nvim/rbuffer.h"
+#include "nvim/types.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os/fileio.c.generated.h"
@@ -71,6 +70,7 @@ int file_open(FileDescriptor *const ret_fp, const char *const fname, const int f
FLAG(flags, kFileReadOnly, O_RDONLY, kFalse, wr != kTrue);
#ifdef O_NOFOLLOW
FLAG(flags, kFileNoSymlink, O_NOFOLLOW, kNone, true);
+ FLAG(flags, kFileMkDir, O_CREAT|O_WRONLY, kTrue, !(flags & kFileCreateOnly));
#endif
#undef FLAG
// wr is used for kFileReadOnly flag, but on
@@ -78,6 +78,13 @@ int file_open(FileDescriptor *const ret_fp, const char *const fname, const int f
// `error: variable ‘wr’ set but not used [-Werror=unused-but-set-variable]`
(void)wr;
+ if (flags & kFileMkDir) {
+ int mkdir_ret = os_file_mkdir((char *)fname, 0755);
+ if (mkdir_ret < 0) {
+ return mkdir_ret;
+ }
+ }
+
const int fd = os_open(fname, os_open_flags, mode);
if (fd < 0) {
@@ -162,6 +169,30 @@ FileDescriptor *file_open_fd_new(int *const error, const int fd, const int flags
return fp;
}
+/// Opens standard input as a FileDescriptor.
+FileDescriptor *file_open_stdin(void)
+ FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ int error;
+ int stdin_dup_fd;
+ if (stdin_fd > 0) {
+ stdin_dup_fd = stdin_fd;
+ } else {
+ stdin_dup_fd = os_dup(STDIN_FILENO);
+#ifdef MSWIN
+ // Replace the original stdin with the console input handle.
+ os_replace_stdin_to_conin();
+#endif
+ }
+ FileDescriptor *const stdin_dup = file_open_fd_new(&error, stdin_dup_fd,
+ kFileReadOnly|kFileNonBlocking);
+ assert(stdin_dup != NULL);
+ if (error != 0) {
+ ELOG("failed to open stdin: %s", os_strerror(error));
+ }
+ return stdin_dup;
+}
+
/// Close file and free its buffer
///
/// @param[in,out] fp File to close.
diff --git a/src/nvim/os/fileio.h b/src/nvim/os/fileio.h
index 426dc422c2..5e47bbf921 100644
--- a/src/nvim/os/fileio.h
+++ b/src/nvim/os/fileio.h
@@ -35,9 +35,10 @@ typedef enum {
///< be used with kFileCreateOnly.
kFileNonBlocking = 128, ///< Do not restart read() or write() syscall if
///< EAGAIN was encountered.
+ kFileMkDir = 256,
} FileOpenFlags;
-static inline bool file_eof(const FileDescriptor *const fp)
+static inline bool file_eof(const FileDescriptor *fp)
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL;
/// Check whether end of file was encountered
@@ -51,7 +52,7 @@ static inline bool file_eof(const FileDescriptor *const fp)
return fp->eof && rbuffer_size(fp->rv) == 0;
}
-static inline int file_fd(const FileDescriptor *const fp)
+static inline int file_fd(const FileDescriptor *fp)
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL;
/// Return the file descriptor associated with the FileDescriptor structure
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 0d62a5f5f9..302faa8140 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -5,11 +5,23 @@
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
-#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
#include "auto/config.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/log.h"
+#include "nvim/macros.h"
+#include "nvim/option_defs.h"
+#include "nvim/os/fs_defs.h"
+#include "nvim/types.h"
+#include "nvim/vim.h"
#ifdef HAVE_SYS_UIO_H
# include <sys/uio.h>
@@ -18,16 +30,14 @@
#include <uv.h>
#include "nvim/ascii.h"
-#include "nvim/assert.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/option.h"
#include "nvim/os/os.h"
-#include "nvim/os/os_defs.h"
#include "nvim/path.h"
-#include "nvim/strings.h"
-#ifdef WIN32
+struct iovec;
+
+#ifdef MSWIN
# include "nvim/mbyte.h" // for utf8_to_utf16, utf16_to_utf8
#endif
@@ -95,12 +105,12 @@ int os_chdir(const char *path)
/// @param buf Buffer to store the directory name.
/// @param len Length of `buf`.
/// @return `OK` for success, `FAIL` for failure.
-int os_dirname(char_u *buf, size_t len)
+int os_dirname(char *buf, size_t len)
FUNC_ATTR_NONNULL_ALL
{
int error_number;
- if ((error_number = uv_cwd((char *)buf, &len)) != kLibuvSuccess) {
- STRLCPY(buf, uv_strerror(error_number), len);
+ if ((error_number = uv_cwd(buf, &len)) != kLibuvSuccess) {
+ xstrlcpy(buf, uv_strerror(error_number), len);
return FAIL;
}
return OK;
@@ -121,18 +131,17 @@ bool os_isrealdir(const char *name)
fs_loop_unlock();
if (S_ISLNK(request.statbuf.st_mode)) {
return false;
- } else {
- return S_ISDIR(request.statbuf.st_mode);
}
+ return S_ISDIR(request.statbuf.st_mode);
}
/// Check if the given path exists and is a directory.
///
/// @return `true` if `name` is a directory.
-bool os_isdir(const char_u *name)
+bool os_isdir(const char *name)
FUNC_ATTR_NONNULL_ALL
{
- int32_t mode = os_getperm((const char *)name);
+ int32_t mode = os_getperm(name);
if (mode < 0) {
return false;
}
@@ -151,7 +160,7 @@ bool os_isdir(const char_u *name)
int os_nodetype(const char *name)
FUNC_ATTR_NONNULL_ALL
{
-#ifndef WIN32 // Unix
+#ifndef MSWIN // Unix
uv_stat_t statbuf;
if (0 != os_stat(name, &statbuf)) {
return NODE_NORMAL; // File doesn't exist.
@@ -171,7 +180,7 @@ int os_nodetype(const char *name)
// Edge case from Vim os_win32.c:
// We can't open a file with a name "\\.\con" or "\\.\prn", trying to read
// from it later will cause Vim to hang. Thus return NODE_WRITABLE here.
- if (STRNCMP(name, "\\\\.\\", 4) == 0) {
+ if (strncmp(name, "\\\\.\\", 4) == 0) {
return NODE_WRITABLE;
}
@@ -241,7 +250,7 @@ bool os_can_exe(const char *name, char **abspath, bool use_path)
FUNC_ATTR_NONNULL_ARG(1)
{
if (!use_path || gettail_dir(name) != name) {
-#ifdef WIN32
+#ifdef MSWIN
if (is_executable_ext(name, abspath)) {
#else
// Must have path separator, cannot execute files in the current directory.
@@ -249,9 +258,8 @@ bool os_can_exe(const char *name, char **abspath, bool use_path)
&& is_executable(name, abspath)) {
#endif
return true;
- } else {
- return false;
}
+ return false;
}
return is_executable_in_path(name, abspath);
@@ -270,7 +278,7 @@ static bool is_executable(const char *name, char **abspath)
return false;
}
-#ifdef WIN32
+#ifdef MSWIN
// Windows does not have exec bit; just check if the file exists and is not
// a directory.
const bool ok = S_ISREG(mode);
@@ -287,14 +295,16 @@ static bool is_executable(const char *name, char **abspath)
return ok;
}
-#ifdef WIN32
+#ifdef MSWIN
/// Checks if file `name` is executable under any of these conditions:
/// - extension is in $PATHEXT and `name` is executable
/// - result of any $PATHEXT extension appended to `name` is executable
static bool is_executable_ext(const char *name, char **abspath)
FUNC_ATTR_NONNULL_ARG(1)
{
- const bool is_unix_shell = strstr((char *)path_tail(p_sh), "sh") != NULL;
+ const bool is_unix_shell = strstr(path_tail(p_sh), "powershell") == NULL
+ && strstr(path_tail(p_sh), "pwsh") == NULL
+ && strstr(path_tail(p_sh), "sh") != NULL;
char *nameext = strrchr(name, '.');
size_t nameext_len = nameext ? strlen(nameext) : 0;
xstrlcpy(os_buf, name, sizeof(os_buf));
@@ -320,11 +330,11 @@ static bool is_executable_ext(const char *name, char **abspath)
const char *ext_end = ext;
size_t ext_len =
- copy_option_part(&ext_end, (char_u *)buf_end,
+ copy_option_part((char **)&ext_end, buf_end,
sizeof(os_buf) - (size_t)(buf_end - os_buf), ENV_SEPSTR);
if (ext_len != 0) {
bool in_pathext = nameext_len == ext_len
- && 0 == mb_strnicmp((char_u *)nameext, (char_u *)ext, ext_len);
+ && 0 == mb_strnicmp(nameext, ext, ext_len);
if (((in_pathext || is_unix_shell) && is_executable(name, abspath))
|| is_executable(os_buf, abspath)) {
@@ -351,7 +361,7 @@ static bool is_executable_in_path(const char *name, char **abspath)
return false;
}
-#ifdef WIN32
+#ifdef MSWIN
// Prepend ".;" to $PATH.
size_t pathlen = strlen(path_env);
char *path = memcpy(xmallocz(pathlen + 2), "." ENV_SEPSTR, 2);
@@ -371,10 +381,10 @@ static bool is_executable_in_path(const char *name, char **abspath)
char *e = xstrchrnul(p, ENV_SEPCHAR);
// Combine the $PATH segment with `name`.
- STRLCPY(buf, p, e - p + 1);
+ xstrlcpy(buf, p, (size_t)(e - p) + 1);
append_path(buf, name, buf_len);
-#ifdef WIN32
+#ifdef MSWIN
if (is_executable_ext(buf, abspath)) {
#else
if (is_executable(buf, abspath)) {
@@ -446,7 +456,7 @@ FILE *os_fopen(const char *path, const char *flags)
default:
abort();
}
-#ifdef WIN32
+#ifdef MSWIN
if (flags[1] == 'b') {
iflags |= O_BINARY;
}
@@ -756,9 +766,8 @@ int32_t os_getperm(const char *name)
int stat_result = os_stat(name, &statbuf);
if (stat_result == kLibuvSuccess) {
return (int32_t)statbuf.st_mode;
- } else {
- return stat_result;
}
+ return stat_result;
}
/// Set the permission of a file.
@@ -772,6 +781,38 @@ int os_setperm(const char *const name, int perm)
return (r == kLibuvSuccess ? OK : FAIL);
}
+#if defined(HAVE_ACL)
+# ifdef HAVE_SYS_ACL_H
+# include <sys/acl.h>
+# endif
+# ifdef HAVE_SYS_ACCESS_H
+# 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 os_get_acl(const char *fname)
+{
+ vim_acl_T ret = NULL;
+ return ret;
+}
+
+// Set the ACL of file "fname" to "acl" (unless it's NULL).
+void os_set_acl(const char *fname, vim_acl_T aclent)
+{
+ if (aclent == NULL) {
+ return;
+ }
+}
+
+void os_free_acl(vim_acl_T aclent)
+{
+ if (aclent == NULL) {
+ return;
+ }
+}
+#endif
+
#ifdef UNIX
/// Checks if the current user owns a file.
///
@@ -821,10 +862,10 @@ int os_fchown(int fd, uv_uid_t owner, uv_gid_t group)
/// Check if a path exists.
///
/// @return `true` if `path` exists
-bool os_path_exists(const char_u *path)
+bool os_path_exists(const char *path)
{
uv_stat_t statbuf;
- return os_stat((char *)path, &statbuf) == kLibuvSuccess;
+ return os_stat(path, &statbuf) == kLibuvSuccess;
}
/// Sets file access and modification times.
@@ -865,7 +906,7 @@ int os_file_is_writable(const char *name)
int r;
RUN_UV_FS_FUNC(r, uv_fs_access, name, W_OK, NULL);
if (r == 0) {
- return os_isdir((char_u *)name) ? 2 : 1;
+ return os_isdir(name) ? 2 : 1;
}
return 0;
}
@@ -873,12 +914,11 @@ int os_file_is_writable(const char *name)
/// Rename a file or directory.
///
/// @return `OK` for success, `FAIL` for failure.
-int os_rename(const char_u *path, const char_u *new_path)
+int os_rename(const char *path, const char *new_path)
FUNC_ATTR_NONNULL_ALL
{
int r;
- RUN_UV_FS_FUNC(r, uv_fs_rename, (const char *)path, (const char *)new_path,
- NULL);
+ RUN_UV_FS_FUNC(r, uv_fs_rename, path, new_path, NULL);
return (r == kLibuvSuccess ? OK : FAIL);
}
@@ -911,11 +951,11 @@ int os_mkdir_recurse(const char *const dir, int32_t mode, char **const failed_di
// We're done when it's "/" or "c:/".
const size_t dirlen = strlen(dir);
char *const curdir = xmemdupz(dir, dirlen);
- char *const past_head = (char *)get_past_head((char_u *)curdir);
+ char *const past_head = get_past_head(curdir);
char *e = curdir + dirlen;
const char *const real_end = e;
const char past_head_save = *past_head;
- while (!os_isdir((char_u *)curdir)) {
+ while (!os_isdir(curdir)) {
e = path_tail_with_sep(curdir);
if (e <= past_head) {
*past_head = NUL;
@@ -946,6 +986,37 @@ int os_mkdir_recurse(const char *const dir, int32_t mode, char **const failed_di
return 0;
}
+/// Create the parent directory of a file if it does not exist
+///
+/// @param[in] fname Full path of the file name whose parent directories
+/// we want to create
+/// @param[in] mode Permissions for the newly-created directory.
+///
+/// @return `0` for success, libuv error code for failure.
+int os_file_mkdir(char *fname, int32_t mode)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ if (!dir_of_file_exists(fname)) {
+ char *tail = path_tail_with_sep(fname);
+ char *last_char = tail + strlen(tail) - 1;
+ if (vim_ispathsep(*last_char)) {
+ emsg(_(e_noname));
+ return -1;
+ }
+ char c = *tail;
+ *tail = NUL;
+ int r;
+ char *failed_dir;
+ if (((r = os_mkdir_recurse(fname, mode, &failed_dir)) < 0)) {
+ semsg(_(e_mkdir), failed_dir, os_strerror(r));
+ xfree(failed_dir);
+ }
+ *tail = c;
+ return r;
+ }
+ return 0;
+}
+
/// Create a unique temporary directory.
///
/// @param[in] template Template of the path to the directory with XXXXXX
@@ -1208,7 +1279,7 @@ char *os_realpath(const char *name, char *buf)
return result == kLibuvSuccess ? buf : NULL;
}
-#ifdef WIN32
+#ifdef MSWIN
# include <shlobj.h>
/// When "fname" is the name of a shortcut (*.lnk) resolve the file it points
@@ -1310,7 +1381,7 @@ 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((uint8_t)p[0]) && p[1] == L':' && IS_PATH_SEP(p[2])) {
p += 3;
} 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
index c68081da02..75c24b8db2 100644
--- a/src/nvim/os/fs.h
+++ b/src/nvim/os/fs.h
@@ -1,8 +1,8 @@
#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
+#include "nvim/os/fs_defs.h"
+#include "nvim/types.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os/fs.h.generated.h"
diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c
index bfe6d59dc6..759b3cf83c 100644
--- a/src/nvim/os/input.c
+++ b/src/nvim/os/input.c
@@ -3,25 +3,33 @@
#include <assert.h>
#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
#include <string.h>
#include <uv.h>
#include "nvim/api/private/defs.h"
#include "nvim/ascii.h"
#include "nvim/autocmd.h"
+#include "nvim/buffer_defs.h"
#include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
#include "nvim/event/rstream.h"
+#include "nvim/event/stream.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
#include "nvim/keycodes.h"
+#include "nvim/log.h"
+#include "nvim/macros.h"
#include "nvim/main.h"
-#include "nvim/mbyte.h"
-#include "nvim/memory.h"
#include "nvim/msgpack_rpc/channel.h"
+#include "nvim/option_defs.h"
#include "nvim/os/input.h"
+#include "nvim/os/time.h"
#include "nvim/profile.h"
-#include "nvim/screen.h"
+#include "nvim/rbuffer.h"
#include "nvim/state.h"
-#include "nvim/ui.h"
#include "nvim/vim.h"
#define READ_BUFFER_SIZE 0xfff
@@ -36,8 +44,9 @@ typedef enum {
static Stream read_stream = { .closed = true }; // Input before UI starts.
static RBuffer *input_buffer = NULL;
static bool input_eof = false;
-static int global_fd = -1;
static bool blocking = false;
+static int cursorhold_time = 0; ///< time waiting for CursorHold event
+static int cursorhold_tb_change_cnt = 0; ///< tb_change_cnt when waiting started
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os/input.c.generated.h"
@@ -48,25 +57,14 @@ void input_init(void)
input_buffer = rbuffer_new(INPUT_BUFFER_SIZE + MAX_KEY_CODE_LEN);
}
-void input_global_fd_init(int fd)
-{
- global_fd = fd;
-}
-
-/// Global TTY (or pipe for "-es") input stream, before UI starts.
-int input_global_fd(void)
-{
- return global_fd;
-}
-
-void input_start(int fd)
+void input_start(void)
{
if (!read_stream.closed) {
return;
}
- input_global_fd_init(fd);
- rstream_init_fd(&main_loop, &read_stream, fd, READ_BUFFER_SIZE);
+ used_stdin = true;
+ rstream_init_fd(&main_loop, &read_stream, STDIN_FILENO, READ_BUFFER_SIZE);
rstream_start(&read_stream, input_read_cb, NULL);
}
@@ -97,13 +95,25 @@ static void create_cursorhold_event(bool events_enabled)
multiqueue_put(main_loop.events, cursorhold_event, 0);
}
+static void restart_cursorhold_wait(int tb_change_cnt)
+{
+ cursorhold_time = 0;
+ cursorhold_tb_change_cnt = tb_change_cnt;
+}
+
/// Low level input function
///
/// 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)
{
+ // This check is needed so that feeding typeahead from RPC can prevent CursorHold.
+ if (tb_change_cnt != cursorhold_tb_change_cnt) {
+ restart_cursorhold_wait(tb_change_cnt);
+ }
+
if (maxlen && rbuffer_size(input_buffer)) {
+ restart_cursorhold_wait(tb_change_cnt);
return (int)rbuffer_read(input_buffer, (char *)buf, (size_t)maxlen);
}
@@ -118,18 +128,22 @@ int os_inchar(uint8_t *buf, int maxlen, int ms, int tb_change_cnt, MultiQueue *e
return 0;
}
} else {
- if ((result = inbuf_poll((int)p_ut, events)) == kInputNone) {
+ uint64_t wait_start = os_hrtime();
+ cursorhold_time = MIN(cursorhold_time, (int)p_ut);
+ if ((result = inbuf_poll((int)p_ut - cursorhold_time, events)) == kInputNone) {
if (read_stream.closed && silent_mode) {
// Drained eventloop & initial input; exit silent/batch-mode (-es/-Es).
read_error_exit();
}
-
+ restart_cursorhold_wait(tb_change_cnt);
if (trigger_cursorhold() && !typebuf_changed(tb_change_cnt)) {
create_cursorhold_event(events == main_loop.events);
} else {
before_blocking();
result = inbuf_poll(-1, events);
}
+ } else {
+ cursorhold_time += (int)((os_hrtime() - wait_start) / 1000000);
}
}
@@ -141,6 +155,7 @@ int os_inchar(uint8_t *buf, int maxlen, int ms, int tb_change_cnt, MultiQueue *e
}
if (maxlen && rbuffer_size(input_buffer)) {
+ restart_cursorhold_wait(tb_change_cnt);
// Safe to convert rbuffer_read to int, it will never overflow since we use
// relatively small buffers.
return (int)rbuffer_read(input_buffer, (char *)buf, (size_t)maxlen);
@@ -239,7 +254,8 @@ size_t input_enqueue(String keys)
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, FSK_KEYCODE, true, NULL);
+ = trans_special((const char **)&ptr, (size_t)(end - ptr), (char *)buf, FSK_KEYCODE, true,
+ NULL);
if (new_size) {
new_size = handle_mouse_event(&ptr, buf, new_size);
@@ -287,36 +303,35 @@ 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
- || code == KE_MOUSEDOWN
- || code == KE_MOUSEUP
- || code == KE_MOUSELEFT
- || code == KE_MOUSERIGHT) {
+ if ((code >= KE_MOUSEDOWN && code <= KE_MOUSERIGHT) || code == KE_MOUSEMOVE) {
return 0;
}
- uint64_t mouse_time = os_hrtime(); // time of current mouse click (ns)
-
- // compute the time elapsed since the previous mouse click and
- // convert p_mouse from ms to ns
- uint64_t timediff = mouse_time - orig_mouse_time;
- uint64_t mouset = (uint64_t)p_mouset * 1000000;
- if (code == orig_mouse_code
- && timediff < mouset
- && orig_num_clicks != 4
- && orig_mouse_grid == grid
- && orig_mouse_col == col
- && orig_mouse_row == row) {
- orig_num_clicks++;
- } else {
- orig_num_clicks = 1;
+
+ // For click events the number of clicks is updated.
+ if (code == KE_LEFTMOUSE || code == KE_RIGHTMOUSE || code == KE_MIDDLEMOUSE) {
+ uint64_t mouse_time = os_hrtime(); // time of current mouse click (ns)
+ // compute the time elapsed since the previous mouse click and
+ // convert p_mouse from ms to ns
+ uint64_t timediff = mouse_time - orig_mouse_time;
+ uint64_t mouset = (uint64_t)p_mouset * 1000000;
+ if (code == orig_mouse_code
+ && timediff < mouset
+ && orig_num_clicks != 4
+ && orig_mouse_grid == grid
+ && orig_mouse_col == col
+ && orig_mouse_row == row) {
+ orig_num_clicks++;
+ } else {
+ orig_num_clicks = 1;
+ }
+ orig_mouse_code = code;
+ orig_mouse_time = mouse_time;
}
- orig_mouse_code = code;
+ // For drag and release events the number of clicks is kept.
+
orig_mouse_grid = grid;
orig_mouse_col = col;
orig_mouse_row = row;
- orig_mouse_time = mouse_time;
uint8_t modifiers = 0;
if (orig_num_clicks == 2) {
@@ -347,7 +362,8 @@ static unsigned int handle_mouse_event(char **ptr, uint8_t *buf, unsigned int bu
if (type != KS_EXTRA
|| !((mouse_code >= KE_LEFTMOUSE && mouse_code <= KE_RIGHTRELEASE)
- || (mouse_code >= KE_MOUSEDOWN && mouse_code <= KE_MOUSERIGHT))) {
+ || (mouse_code >= KE_MOUSEDOWN && mouse_code <= KE_MOUSERIGHT)
+ || mouse_code == KE_MOUSEMOVE)) {
return bufsize;
}
@@ -450,9 +466,8 @@ static InbufPollResult inbuf_poll(int ms, MultiQueue *events)
if (input_ready(events)) {
return kInputAvail;
- } else {
- return input_eof ? kInputEof : kInputNone;
}
+ return input_eof ? kInputEof : kInputNone;
}
void input_done(void)
diff --git a/src/nvim/os/input.h b/src/nvim/os/input.h
index 7026781407..6f25efdc7b 100644
--- a/src/nvim/os/input.h
+++ b/src/nvim/os/input.h
@@ -7,6 +7,8 @@
#include "nvim/api/private/defs.h"
#include "nvim/event/multiqueue.h"
+EXTERN bool used_stdin INIT(= false);
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os/input.h.generated.h"
#endif
diff --git a/src/nvim/os/lang.c b/src/nvim/os/lang.c
index 28f43ff3af..57c82bba86 100644
--- a/src/nvim/os/lang.c
+++ b/src/nvim/os/lang.c
@@ -7,16 +7,16 @@
# include <CoreServices/CoreServices.h>
# undef Boolean
# undef FileInfo
-#endif
-#include "auto/config.h"
+# include "auto/config.h"
+# ifdef HAVE_LOCALE_H
+# include <locale.h>
+# endif
+# include "nvim/os/os.h"
-#ifdef HAVE_LOCALE_H
-# include <locale.h>
#endif
#include "nvim/os/lang.h"
-#include "nvim/os/os.h"
void lang_init(void)
{
diff --git a/src/nvim/os/mem.c b/src/nvim/os/mem.c
index eccb3c97e5..0b7e8065ef 100644
--- a/src/nvim/os/mem.c
+++ b/src/nvim/os/mem.c
@@ -3,6 +3,7 @@
/// Functions for accessing system memory information.
+#include <stdint.h>
#include <uv.h>
#include "nvim/os/os.h"
diff --git a/src/nvim/os/nvim.manifest b/src/nvim/os/nvim.manifest
new file mode 100644
index 0000000000..8878822a5d
--- /dev/null
+++ b/src/nvim/os/nvim.manifest
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
+ <assemblyIdentity
+ processorArchitecture="*"
+ version="0.9.0.0"
+ type="win32"
+ name="Neovim"
+ />
+ <description>Neovim</description>
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!-- Windows 10 and Windows 11 -->
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
+ <!-- Windows 8.1 -->
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
+ <!-- Windows 8 -->
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+ </application>
+ </compatibility>
+</assembly>
diff --git a/src/nvim/os/os_defs.h b/src/nvim/os/os_defs.h
index a4361859ec..a30e16eeba 100644
--- a/src/nvim/os/os_defs.h
+++ b/src/nvim/os/os_defs.h
@@ -7,7 +7,7 @@
#include <sys/stat.h>
#include <sys/types.h>
-#ifdef WIN32
+#ifdef MSWIN
# include "nvim/os/win_defs.h"
#else
# include "nvim/os/unix_defs.h"
@@ -43,7 +43,7 @@
/// Converts system error code to libuv error code.
#define os_translate_sys_error uv_translate_sys_error
-#ifdef WIN32
+#ifdef MSWIN
# define os_strtok strtok_s
#else
# define os_strtok strtok_r
diff --git a/src/nvim/os/os_win_console.c b/src/nvim/os/os_win_console.c
index 20b7f869f1..006e27d28f 100644
--- a/src/nvim/os/os_win_console.c
+++ b/src/nvim/os/os_win_console.c
@@ -2,6 +2,7 @@
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include "nvim/os/input.h"
+#include "nvim/os/os.h"
#include "nvim/os/os_win_console.h"
#include "nvim/vim.h"
@@ -9,7 +10,12 @@
# include "os/os_win_console.c.generated.h"
#endif
-int os_get_conin_fd(void)
+static char origTitle[256] = { 0 };
+static HWND hWnd = NULL;
+static HICON hOrigIconSmall = NULL;
+static HICON hOrigIcon = NULL;
+
+int os_open_conin_fd(void)
{
const HANDLE conin_handle = CreateFile("CONIN$",
GENERIC_READ | GENERIC_WRITE,
@@ -25,7 +31,7 @@ int os_get_conin_fd(void)
void os_replace_stdin_to_conin(void)
{
close(STDIN_FILENO);
- const int conin_fd = os_get_conin_fd();
+ const int conin_fd = os_open_conin_fd();
assert(conin_fd == STDIN_FILENO);
}
@@ -45,3 +51,57 @@ void os_replace_stdout_and_stderr_to_conout(void)
const int conerr_fd = _open_osfhandle((intptr_t)conout_handle, 0);
assert(conerr_fd == STDERR_FILENO);
}
+
+/// Sets Windows console icon, or pass NULL to restore original icon.
+void os_icon_set(HICON hIconSmall, HICON hIcon)
+{
+ if (hWnd == NULL) {
+ return;
+ }
+ hIconSmall = hIconSmall ? hIconSmall : hOrigIconSmall;
+ hIcon = hIcon ? hIcon : hOrigIcon;
+
+ if (hIconSmall != NULL) {
+ SendMessage(hWnd, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)hIconSmall);
+ }
+ if (hIcon != NULL) {
+ SendMessage(hWnd, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)hIcon);
+ }
+}
+
+/// Sets Nvim logo as Windows console icon.
+///
+/// Saves the original icon so it can be restored at exit.
+void os_icon_init(void)
+{
+ if ((hWnd = GetConsoleWindow()) == NULL) {
+ return;
+ }
+ // Save Windows console icon to be restored later.
+ hOrigIconSmall = (HICON)SendMessage(hWnd, WM_GETICON, (WPARAM)ICON_SMALL, (LPARAM)0);
+ hOrigIcon = (HICON)SendMessage(hWnd, WM_GETICON, (WPARAM)ICON_BIG, (LPARAM)0);
+
+ const char *vimruntime = os_getenv("VIMRUNTIME");
+ if (vimruntime != NULL) {
+ snprintf(NameBuff, MAXPATHL, "%s" _PATHSEPSTR "neovim.ico", vimruntime);
+ if (!os_path_exists(NameBuff)) {
+ WLOG("neovim.ico not found: %s", NameBuff);
+ } else {
+ HICON hVimIcon = LoadImage(NULL, NameBuff, IMAGE_ICON, 64, 64,
+ LR_LOADFROMFILE | LR_LOADMAP3DCOLORS);
+ os_icon_set(hVimIcon, hVimIcon);
+ }
+ }
+}
+
+/// Saves the original Windows console title.
+void os_title_save(void)
+{
+ GetConsoleTitle(origTitle, sizeof(origTitle));
+}
+
+/// Resets the original Windows console title.
+void os_title_reset(void)
+{
+ SetConsoleTitle(origTitle);
+}
diff --git a/src/nvim/os/process.c b/src/nvim/os/process.c
index e70bc71961..f4d95e141b 100644
--- a/src/nvim/os/process.c
+++ b/src/nvim/os/process.c
@@ -6,10 +6,21 @@
/// psutil is a good reference for cross-platform syscall voodoo:
/// https://github.com/giampaolo/psutil/tree/master/psutil/arch
-#include <uv.h> // for HANDLE (win32)
+#include <assert.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <uv.h>
-#ifdef WIN32
-# include <tlhelp32.h> // for CreateToolhelp32Snapshot
+#include "nvim/log.h"
+#include "nvim/memory.h"
+#include "nvim/os/process.h"
+
+#ifdef MSWIN
+# include <tlhelp32.h>
+
+# include "nvim/api/private/helpers.h"
#endif
#if defined(__FreeBSD__) // XXX: OpenBSD ?
@@ -27,18 +38,11 @@
# include <sys/sysctl.h>
#endif
-#include "nvim/api/private/helpers.h"
-#include "nvim/globals.h"
-#include "nvim/log.h"
-#include "nvim/os/os.h"
-#include "nvim/os/os_defs.h"
-#include "nvim/os/process.h"
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "os/process.c.generated.h"
+# include "os/process.c.generated.h" // IWYU pragma: export
#endif
-#ifdef WIN32
+#ifdef MSWIN
static bool os_proc_tree_kill_rec(HANDLE process, int sig)
{
if (process == NULL) {
@@ -114,7 +118,7 @@ int os_proc_children(int ppid, int **proc_list, size_t *proc_count)
*proc_list = NULL;
*proc_count = 0;
-#ifdef WIN32
+#ifdef MSWIN
PROCESSENTRY32 pe;
// Snapshot of all processes.
@@ -215,7 +219,7 @@ int os_proc_children(int ppid, int **proc_list, size_t *proc_count)
return 0;
}
-#ifdef WIN32
+#ifdef MSWIN
/// Gets various properties of the process identified by `pid`.
///
/// @param pid Process to inspect.
@@ -260,5 +264,16 @@ Dictionary os_proc_info(int pid)
/// Return true if process `pid` is running.
bool os_proc_running(int pid)
{
- return uv_kill(pid, 0) == 0;
+ int err = uv_kill(pid, 0);
+ // If there is no error the process must be running.
+ if (err == 0) {
+ return true;
+ }
+ // If the error is ESRCH then the process is not running.
+ if (err == UV_ESRCH) {
+ return false;
+ }
+ // If the process is running and owned by another user we get EPERM. With
+ // other errors the process might be running, assuming it is then.
+ return true;
}
diff --git a/src/nvim/os/pty_conpty_win.c b/src/nvim/os/pty_conpty_win.c
index f9478d951f..43c89f8865 100644
--- a/src/nvim/os/pty_conpty_win.c
+++ b/src/nvim/os/pty_conpty_win.c
@@ -67,7 +67,7 @@ conpty_t *os_conpty_init(char **in_name, char **out_name, uint16_t width, uint16
| PIPE_ACCESS_OUTBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE;
sa.nLength = sizeof(sa);
- snprintf(buf, sizeof(buf), "\\\\.\\pipe\\nvim-term-in-%d-%d",
+ snprintf(buf, sizeof(buf), "\\\\.\\pipe\\nvim-term-in-%lld-%d",
os_get_pid(), count);
*in_name = xstrdup(buf);
if ((in_read = CreateNamedPipeA(*in_name,
@@ -81,7 +81,7 @@ conpty_t *os_conpty_init(char **in_name, char **out_name, uint16_t width, uint16
emsg = "create input pipe failed";
goto failed;
}
- snprintf(buf, sizeof(buf), "\\\\.\\pipe\\nvim-term-out-%d-%d",
+ snprintf(buf, sizeof(buf), "\\\\.\\pipe\\nvim-term-out-%lld-%d",
os_get_pid(), count);
*out_name = xstrdup(buf);
if ((out_write = CreateNamedPipeA(*out_name,
diff --git a/src/nvim/os/pty_conpty_win.h b/src/nvim/os/pty_conpty_win.h
index 15e7c3da0c..0c25a5970e 100644
--- a/src/nvim/os/pty_conpty_win.h
+++ b/src/nvim/os/pty_conpty_win.h
@@ -1,7 +1,7 @@
#ifndef NVIM_OS_PTY_CONPTY_WIN_H
#define NVIM_OS_PTY_CONPTY_WIN_H
-#include "nvim/lib/kvec.h"
+#include "klib/kvec.h"
#include "nvim/os/input.h"
#ifndef HPCON
diff --git a/src/nvim/os/pty_process.h b/src/nvim/os/pty_process.h
index 94923499ca..07d346be22 100644
--- a/src/nvim/os/pty_process.h
+++ b/src/nvim/os/pty_process.h
@@ -1,7 +1,7 @@
#ifndef NVIM_OS_PTY_PROCESS_H
#define NVIM_OS_PTY_PROCESS_H
-#ifdef WIN32
+#ifdef MSWIN
# include "nvim/os/pty_process_win.h"
#else
# include "nvim/os/pty_process_unix.h"
diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c
index c5d6af0ff6..2413f0339b 100644
--- a/src/nvim/os/pty_process_unix.c
+++ b/src/nvim/os/pty_process_unix.c
@@ -2,13 +2,15 @@
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// Some of the code came from pangoterm and libuv
-#include <stdbool.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
-#include <sys/types.h>
#include <sys/wait.h>
-#include <termios.h>
// forkpty is not in POSIX, so headers are platform-specific
#if defined(__FreeBSD__) || defined(__DragonFly__)
@@ -31,13 +33,16 @@
#include <uv.h>
+#include "auto/config.h"
+#include "klib/klist.h"
+#include "nvim/eval/typval.h"
#include "nvim/event/loop.h"
#include "nvim/event/process.h"
-#include "nvim/event/rstream.h"
-#include "nvim/event/wstream.h"
-#include "nvim/lib/klist.h"
+#include "nvim/event/stream.h"
#include "nvim/log.h"
#include "nvim/os/os.h"
+#include "nvim/os/os_defs.h"
+#include "nvim/os/pty_process.h"
#include "nvim/os/pty_process_unix.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -155,31 +160,13 @@ static pid_t forkpty(int *amaster, char *name, struct termios *termp, struct win
#endif
-/// termios saved at startup (for TUI) or initialized by pty_process_spawn().
-static struct termios termios_default;
-
-/// Saves the termios properties associated with `tty_fd`.
-///
-/// @param tty_fd TTY file descriptor, or -1 if not in a terminal.
-void pty_process_save_termios(int tty_fd)
-{
- 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
int pty_process_spawn(PtyProcess *ptyproc)
FUNC_ATTR_NONNULL_ALL
{
+ // termios initialized at first use
+ static struct termios termios_default;
if (!termios_default.c_cflag) {
- // TODO(jkeyes): We could pass NULL to forkpty() instead ...
init_termios(&termios_default);
}
diff --git a/src/nvim/os/pty_process_unix.h b/src/nvim/os/pty_process_unix.h
index 765490b92b..0cc68cf3e9 100644
--- a/src/nvim/os/pty_process_unix.h
+++ b/src/nvim/os/pty_process_unix.h
@@ -1,8 +1,10 @@
#ifndef NVIM_OS_PTY_PROCESS_UNIX_H
#define NVIM_OS_PTY_PROCESS_UNIX_H
+#include <stdint.h>
#include <sys/ioctl.h>
+#include "nvim/event/loop.h"
#include "nvim/event/process.h"
typedef struct pty_process {
diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c
index b793b8f9c6..f1e2c5440f 100644
--- a/src/nvim/os/shell.c
+++ b/src/nvim/os/shell.c
@@ -3,30 +3,45 @@
#include <assert.h>
#include <stdbool.h>
-#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
#include <string.h>
#include <uv.h>
+#include "auto/config.h"
+#include "klib/kvec.h"
#include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/eval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/event/libuv_process.h"
#include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
+#include "nvim/event/process.h"
#include "nvim/event/rstream.h"
+#include "nvim/event/stream.h"
+#include "nvim/event/wstream.h"
#include "nvim/ex_cmds.h"
#include "nvim/fileio.h"
-#include "nvim/lib/kvec.h"
-#include "nvim/log.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/macros.h"
#include "nvim/main.h"
+#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/option_defs.h"
+#include "nvim/os/fs.h"
+#include "nvim/os/os_defs.h"
#include "nvim/os/shell.h"
#include "nvim/os/signal.h"
+#include "nvim/os/time.h"
#include "nvim/path.h"
+#include "nvim/pos.h"
#include "nvim/profile.h"
-#include "nvim/screen.h"
+#include "nvim/rbuffer.h"
#include "nvim/strings.h"
#include "nvim/tag.h"
#include "nvim/types.h"
@@ -50,13 +65,13 @@ typedef struct {
static void save_patterns(int num_pat, char **pat, int *num_file, char ***file)
{
- *file = xmalloc((size_t)num_pat * sizeof(char_u *));
+ *file = xmalloc((size_t)num_pat * sizeof(char *));
for (int i = 0; i < num_pat; i++) {
- char_u *s = vim_strsave((char_u *)pat[i]);
+ char *s = xstrdup(pat[i]);
// Be compatible with expand_filename(): halve the number of
// backslashes.
backslash_halve(s);
- (*file)[i] = (char *)s;
+ (*file)[i] = s;
}
*num_file = num_pat;
}
@@ -64,7 +79,7 @@ static void save_patterns(int num_pat, char **pat, int *num_file, char ***file)
static bool have_wildcard(int num, char **file)
{
for (int i = 0; i < num; i++) {
- if (path_has_wildcard((char_u *)file[i])) {
+ if (path_has_wildcard(file[i])) {
return true;
}
}
@@ -74,7 +89,7 @@ static bool have_wildcard(int num, char **file)
static bool have_dollars(int num, char **file)
{
for (int i = 0; i < num; i++) {
- if (vim_strchr((char *)file[i], '$') != NULL) {
+ if (vim_strchr(file[i], '$') != NULL) {
return true;
}
}
@@ -105,15 +120,15 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
{
int i;
size_t len;
- char_u *p;
+ char *p;
bool dir;
- char_u *extra_shell_arg = NULL;
+ char *extra_shell_arg = NULL;
ShellOpts shellopts = kShellOptExpand | kShellOptSilent;
int j;
- char_u *tempname;
- char_u *command;
+ char *tempname;
+ char *command;
FILE *fd;
- char_u *buffer;
+ char *buffer;
#define STYLE_ECHO 0 // use "echo", the default
#define STYLE_GLOB 1 // use "glob", for csh
#define STYLE_VIMGLOB 2 // use "vimglob", for Posix sh
@@ -129,7 +144,7 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
bool is_fish_shell =
#if defined(UNIX)
- STRNCMP(invocation_path_tail(p_sh, NULL), "fish", 4) == 0;
+ strncmp((char *)invocation_path_tail(p_sh, NULL), "fish", 4) == 0;
#else
false;
#endif
@@ -178,27 +193,27 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
// STYLE_ECHO: space separated.
// A shell we don't know, stay safe and use "echo".
if (num_pat == 1 && *pat[0] == '`'
- && (len = STRLEN(pat[0])) > 2
+ && (len = strlen(pat[0])) > 2
&& *(pat[0] + len - 1) == '`') {
shell_style = STYLE_BT;
- } else if ((len = STRLEN(p_sh)) >= 3) {
- if (STRCMP(p_sh + len - 3, "csh") == 0) {
+ } else if ((len = strlen(p_sh)) >= 3) {
+ if (strcmp(p_sh + len - 3, "csh") == 0) {
shell_style = STYLE_GLOB;
- } else if (STRCMP(p_sh + len - 3, "zsh") == 0) {
+ } else if (strcmp(p_sh + len - 3, "zsh") == 0) {
shell_style = STYLE_PRINT;
}
}
if (shell_style == STYLE_ECHO
- && strstr(path_tail((char *)p_sh), "sh") != NULL) {
+ && strstr(path_tail(p_sh), "sh") != NULL) {
shell_style = STYLE_VIMGLOB;
}
// Compute the length of the command. We need 2 extra bytes: for the
// optional '&' and for the NUL.
// Worst case: "unset nonomatch; print -N >" plus two is 29
- len = STRLEN(tempname) + 29;
+ len = strlen(tempname) + 29;
if (shell_style == STYLE_VIMGLOB) {
- len += STRLEN(sh_vimglob_func);
+ len += strlen(sh_vimglob_func);
}
for (i = 0; i < num_pat; i++) {
@@ -206,7 +221,7 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
// "command" below.
len++; // add space
for (j = 0; pat[i][j] != NUL; j++) {
- if (vim_strchr(SHELL_SPECIAL, pat[i][j]) != NULL) {
+ if (vim_strchr(SHELL_SPECIAL, (uint8_t)pat[i][j]) != NULL) {
len++; // may add a backslash
}
len++;
@@ -233,7 +248,7 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
STRCPY(command, "(");
}
STRCAT(command, pat[0] + 1); // exclude first backtick
- p = command + STRLEN(command) - 1;
+ p = command + strlen(command) - 1;
if (is_fish_shell) {
*p-- = ';';
STRCAT(command, " end");
@@ -279,7 +294,7 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
// characters, except inside ``.
bool intick = false;
- p = command + STRLEN(command);
+ p = command + strlen(command);
*p++ = ' ';
for (j = 0; pat[i][j] != NUL; j++) {
if (pat[i][j] == '`') {
@@ -289,14 +304,14 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
// backslash inside backticks, before a special character
// and before a backtick.
if (intick
- || vim_strchr(SHELL_SPECIAL, pat[i][j + 1]) != NULL
+ || vim_strchr(SHELL_SPECIAL, (uint8_t)pat[i][j + 1]) != NULL
|| pat[i][j + 1] == '`') {
*p++ = '\\';
}
j++;
} else if (!intick
&& ((flags & EW_KEEPDOLLAR) == 0 || pat[i][j] != '$')
- && vim_strchr(SHELL_SPECIAL, pat[i][j]) != NULL) {
+ && vim_strchr(SHELL_SPECIAL, (uint8_t)pat[i][j]) != NULL) {
// Put a backslash before a special character, but not
// when inside ``. And not for $var when EW_KEEPDOLLAR is
// set.
@@ -304,7 +319,7 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
}
// Copy one character.
- *p++ = (char_u)pat[i][j];
+ *p++ = pat[i][j];
}
*p = NUL;
}
@@ -322,13 +337,13 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
// the argument list, otherwise zsh gives an error message and doesn't
// expand any other pattern.
if (shell_style == STYLE_PRINT) {
- extra_shell_arg = (char_u *)"-G"; // Use zsh NULL_GLOB option
+ extra_shell_arg = "-G"; // Use zsh NULL_GLOB option
// If we use -f then shell variables set in .cshrc won't get expanded.
// vi can do it, so we will too, but it is only necessary if there is a "$"
// in one of the patterns, otherwise we can still use the fast option.
} else if (shell_style == STYLE_GLOB && !have_dollars(num_pat, pat)) {
- extra_shell_arg = (char_u *)"-f"; // Use csh fast option
+ extra_shell_arg = "-f"; // Use csh fast option
}
// execute the shell command
@@ -343,7 +358,7 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
xfree(command);
if (i) { // os_call_shell() failed
- os_remove((char *)tempname);
+ os_remove(tempname);
xfree(tempname);
// With interactive completion, the error message is not printed.
if (!(flags & EW_SILENT)) {
@@ -362,7 +377,7 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
}
// read the names from the file into memory
- fd = fopen((char *)tempname, READBIN);
+ fd = fopen(tempname, READBIN);
if (fd == NULL) {
// Something went wrong, perhaps a file name with a special char.
if (!(flags & EW_SILENT)) {
@@ -392,9 +407,9 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
buffer = xmalloc(len + 1);
// fread() doesn't terminate buffer with NUL;
// appropriate termination (not always NUL) is done below.
- size_t readlen = fread((char *)buffer, 1, len, fd);
+ size_t readlen = fread(buffer, 1, len, fd);
fclose(fd);
- os_remove((char *)tempname);
+ os_remove(tempname);
if (readlen != len) {
// unexpected read error
semsg(_(e_notread), tempname);
@@ -412,7 +427,7 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
while (*p != ' ' && *p != '\n') {
p++;
}
- p = (char_u *)skipwhite((char *)p); // skip to next entry
+ p = skipwhite(p); // skip to next entry
}
// file names are separated with NL
} else if (shell_style == STYLE_BT || shell_style == STYLE_VIMGLOB) {
@@ -425,7 +440,7 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
if (*p != NUL) {
p++;
}
- p = (char_u *)skipwhite((char *)p); // skip leading white space
+ p = skipwhite(p); // skip leading white space
}
// file names are separated with NUL
} else {
@@ -439,7 +454,7 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
if (shell_style == STYLE_PRINT && !did_find_nul) {
// If there is a NUL, set did_find_nul, else set check_spaces
buffer[len] = NUL;
- if (len && (int)STRLEN(buffer) < (int)len) {
+ if (len && (int)strlen(buffer) < (int)len) {
did_find_nul = true;
} else {
check_spaces = true;
@@ -473,12 +488,12 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
goto notfound;
}
*num_file = i;
- *file = xmalloc(sizeof(char_u *) * (size_t)i);
+ *file = xmalloc(sizeof(char *) * (size_t)i);
// Isolate the individual file names.
p = buffer;
for (i = 0; i < *num_file; i++) {
- (*file)[i] = (char *)p;
+ (*file)[i] = p;
// Space or NL separates
if (shell_style == STYLE_ECHO || shell_style == STYLE_BT
|| shell_style == STYLE_VIMGLOB) {
@@ -490,7 +505,7 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
*p = NUL;
} else {
*p++ = NUL;
- p = (char_u *)skipwhite((char *)p); // skip to next entry
+ p = skipwhite(p); // skip to next entry
}
} else { // NUL separates
while (*p && p < buffer + len) { // skip entry
@@ -503,12 +518,12 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
// Move the file names to allocated memory.
for (j = 0, i = 0; i < *num_file; i++) {
// Require the files to exist. Helps when using /bin/sh
- if (!(flags & EW_NOTFOUND) && !os_path_exists((char_u *)(*file)[i])) {
+ if (!(flags & EW_NOTFOUND) && !os_path_exists((*file)[i])) {
continue;
}
// check if this entry should be included
- dir = (os_isdir((char_u *)(*file)[i]));
+ dir = (os_isdir((*file)[i]));
if ((dir && !(flags & EW_DIR)) || (!dir && !(flags & EW_FILE))) {
continue;
}
@@ -519,12 +534,12 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
continue;
}
- p = xmalloc(STRLEN((*file)[i]) + 1 + dir);
+ p = xmalloc(strlen((*file)[i]) + 1 + dir);
STRCPY(p, (*file)[i]);
if (dir) {
- add_pathsep((char *)p); // add '/' to a directory name
+ add_pathsep(p); // add '/' to a directory name
}
- (*file)[j++] = (char *)p;
+ (*file)[j++] = p;
}
xfree(buffer);
*num_file = j;
@@ -638,7 +653,7 @@ char *shell_argv_to_str(char **const argv)
/// @param extra_args Extra arguments to the shell, or NULL.
///
/// @return shell command exit code
-int os_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_args)
+int os_call_shell(char *cmd, ShellOpts opts, char *extra_args)
{
DynamicBuffer input = DYNAMIC_BUFFER_INIT;
char *output = NULL, **output_ptr = NULL;
@@ -667,7 +682,7 @@ int os_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_args)
}
size_t nread;
- int exitcode = do_os_system(shell_build_argv((char *)cmd, (char *)extra_args),
+ int exitcode = do_os_system(shell_build_argv(cmd, extra_args),
input.data, input.len, output_ptr, &nread,
emsg_silent, forward_output);
xfree(input.data);
@@ -693,7 +708,7 @@ int os_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_args)
/// Invalidates cached tags.
///
/// @return shell command exit code
-int call_shell(char_u *cmd, ShellOpts opts, char_u *extra_shell_arg)
+int call_shell(char *cmd, ShellOpts opts, char *extra_shell_arg)
{
int retval;
proftime_T wait_time;
@@ -737,23 +752,23 @@ int call_shell(char_u *cmd, ShellOpts opts, char_u *extra_shell_arg)
/// @param ret_len length of the stdout
///
/// @return an allocated string, or NULL for error.
-char_u *get_cmd_output(char_u *cmd, char_u *infile, ShellOpts flags, size_t *ret_len)
+char *get_cmd_output(char *cmd, char *infile, ShellOpts flags, size_t *ret_len)
{
- char_u *buffer = NULL;
+ char *buffer = NULL;
if (check_secure()) {
return NULL;
}
// get a name for the temp file
- char_u *tempname = vim_tempname();
+ char *tempname = vim_tempname();
if (tempname == NULL) {
emsg(_(e_notmp));
return NULL;
}
// Add the redirection stuff
- char_u *command = (char_u *)make_filter_cmd((char *)cmd, (char *)infile, (char *)tempname);
+ char *command = make_filter_cmd(cmd, infile, tempname);
// Call the shell to execute the command (errors are ignored).
// Don't check timestamps here.
@@ -764,7 +779,7 @@ char_u *get_cmd_output(char_u *cmd, char_u *infile, ShellOpts flags, size_t *ret
xfree(command);
// read the names from the file into memory
- FILE *fd = os_fopen((char *)tempname, READBIN);
+ FILE *fd = os_fopen(tempname, READBIN);
if (fd == NULL) {
semsg(_(e_notopen), tempname);
@@ -776,9 +791,9 @@ char_u *get_cmd_output(char_u *cmd, char_u *infile, ShellOpts flags, size_t *ret
fseek(fd, 0L, SEEK_SET);
buffer = xmalloc(len + 1);
- size_t i = fread((char *)buffer, 1, len, fd);
+ size_t i = fread(buffer, 1, len, fd);
fclose(fd);
- os_remove((char *)tempname);
+ os_remove(tempname);
if (i != len) {
semsg(_(e_notread), tempname);
XFREE_CLEAR(buffer);
@@ -805,7 +820,7 @@ done:
/// char *output = NULL;
/// size_t nread = 0;
/// char *argv[] = {"ls", "-la", NULL};
-/// int exitcode = os_sytem(argv, NULL, 0, &output, &nread);
+/// int exitcode = os_system(argv, NULL, 0, &output, &nread);
///
/// @param argv The commandline arguments to be passed to the shell. `argv`
/// will be consumed.
@@ -911,7 +926,7 @@ static int do_os_system(char **argv, const char *input, size_t len, char **outpu
out_data_ring(NULL, SIZE_MAX);
}
if (forward_output) {
- // caller should decide if wait_return is invoked
+ // caller should decide if wait_return() is invoked
no_wait_return++;
msg_end();
no_wait_return--;
@@ -1106,13 +1121,13 @@ 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;
+ int i = *p ? utfc_ptr2len_len(p, (int)(end - p)) : 1;
if (!eof && i == 1 && utf8len_tab_zero[*(uint8_t *)p] > (end - p)) {
*count = (size_t)(p - output);
goto end;
}
- (void)msg_outtrans_len_attr((char_u *)p, i, 0);
+ (void)msg_outtrans_len_attr(p, i, 0);
p += i;
}
}
@@ -1150,14 +1165,14 @@ static void out_data_cb(Stream *stream, RBuffer *buf, size_t count, void *data,
/// @param argv The vector that will be filled with copies of the parsed
/// words. It can be NULL if the caller only needs to count words.
/// @return The number of words parsed.
-static size_t tokenize(const char_u *const str, char **const argv)
+static size_t tokenize(const char *const str, char **const argv)
FUNC_ATTR_NONNULL_ARG(1)
{
size_t argc = 0;
- const char *p = (const char *)str;
+ const char *p = str;
while (*p != NUL) {
- const size_t len = word_length((const char_u *)p);
+ const size_t len = word_length(p);
if (argv != NULL) {
// Fill the slot
@@ -1175,9 +1190,9 @@ static size_t tokenize(const char_u *const str, char **const argv)
///
/// @param str A pointer to the first character of the word
/// @return The offset from `str` at which the word ends.
-static size_t word_length(const char_u *str)
+static size_t word_length(const char *str)
{
- const char_u *p = str;
+ const char *p = str;
bool inquote = false;
size_t length = 0;
@@ -1208,10 +1223,10 @@ static void read_input(DynamicBuffer *buf)
{
size_t written = 0, l = 0, len = 0;
linenr_T lnum = curbuf->b_op_start.lnum;
- char_u *lp = ml_get(lnum);
+ char *lp = ml_get(lnum);
for (;;) {
- l = strlen((char *)lp + written);
+ l = strlen(lp + written);
if (l == 0) {
len = 0;
} else if (lp[written] == NL) {
@@ -1220,7 +1235,7 @@ static void read_input(DynamicBuffer *buf)
dynamic_buffer_ensure(buf, buf->len + len);
buf->data[buf->len++] = NUL;
} else {
- char_u *s = (char_u *)vim_strchr((char *)lp + written, NL);
+ char *s = vim_strchr(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);
@@ -1316,17 +1331,17 @@ static char *shell_xescape_xquote(const char *cmd)
}
const char *ecmd = cmd;
- if (*p_sxe != NUL && STRCMP(p_sxq, "(") == 0) {
- ecmd = (char *)vim_strsave_escaped_ext((char_u *)cmd, p_sxe, '^', false);
+ if (*p_sxe != NUL && strcmp(p_sxq, "(") == 0) {
+ ecmd = vim_strsave_escaped_ext(cmd, p_sxe, '^', false);
}
- size_t ncmd_size = strlen(ecmd) + STRLEN(p_sxq) * 2 + 1;
+ size_t ncmd_size = strlen(ecmd) + strlen(p_sxq) * 2 + 1;
char *ncmd = xmalloc(ncmd_size);
// When 'shellxquote' is ( append ).
// When 'shellxquote' is "( append )".
- if (STRCMP(p_sxq, "(") == 0) {
+ if (strcmp(p_sxq, "(") == 0) {
vim_snprintf(ncmd, ncmd_size, "(%s)", ecmd);
- } else if (STRCMP(p_sxq, "\"(") == 0) {
+ } else if (strcmp(p_sxq, "\"(") == 0) {
vim_snprintf(ncmd, ncmd_size, "\"(%s)\"", ecmd);
} else {
vim_snprintf(ncmd, ncmd_size, "%s%s%s", p_sxq, ecmd, p_sxq);
diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c
index e592570966..b8daaabba2 100644
--- a/src/nvim/os/signal.c
+++ b/src/nvim/os/signal.c
@@ -3,23 +3,20 @@
#include <assert.h>
#include <stdbool.h>
-#include <uv.h>
-#ifndef WIN32
+#include <stdio.h>
+#ifndef MSWIN
# include <signal.h> // for sigset_t
#endif
-#include "nvim/ascii.h"
#include "nvim/autocmd.h"
+#include "nvim/buffer_defs.h"
#include "nvim/eval.h"
-#include "nvim/event/loop.h"
#include "nvim/event/signal.h"
#include "nvim/globals.h"
#include "nvim/log.h"
#include "nvim/main.h"
#include "nvim/memline.h"
-#include "nvim/memory.h"
#include "nvim/os/signal.h"
-#include "nvim/vim.h"
static SignalWatcher spipe, shup, squit, sterm, susr1, swinch;
#ifdef SIGPWR
@@ -34,7 +31,7 @@ static bool rejecting_deadly;
void signal_init(void)
{
-#ifndef WIN32
+#ifndef MSWIN
// Ensure a clean slate by unblocking all signals. For example, if SIGCHLD is
// blocked, libuv may hang after spawning a subprocess on Linux. #5230
sigset_t mask;
@@ -175,7 +172,7 @@ static void deadly_signal(int signum)
ILOG("got signal %d (%s)", signum, signal_name(signum));
- snprintf((char *)IObuff, sizeof(IObuff), "Vim: Caught deadly signal '%s'\r\n",
+ snprintf(IObuff, sizeof(IObuff), "Vim: Caught deadly signal '%s'\r\n",
signal_name(signum));
// Preserve files and exit.
diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c
index 59d315d44c..6b07b6ef70 100644
--- a/src/nvim/os/stdpaths.c
+++ b/src/nvim/os/stdpaths.c
@@ -2,6 +2,7 @@
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include <stdbool.h>
+#include <string.h>
#include "nvim/ascii.h"
#include "nvim/fileio.h"
@@ -21,7 +22,7 @@ static const char *xdg_env_vars[] = {
[kXDGDataDirs] = "XDG_DATA_DIRS",
};
-#ifdef WIN32
+#ifdef MSWIN
static const char *const xdg_defaults_env_vars[] = {
[kXDGConfigHome] = "LOCALAPPDATA",
[kXDGDataHome] = "LOCALAPPDATA",
@@ -37,7 +38,7 @@ static const char *const xdg_defaults_env_vars[] = {
///
/// Used in case environment variables contain nothing. Need to be expanded.
static const char *const xdg_defaults[] = {
-#ifdef WIN32
+#ifdef MSWIN
[kXDGConfigHome] = "~\\AppData\\Local",
[kXDGDataHome] = "~\\AppData\\Local",
[kXDGCacheHome] = "~\\AppData\\Local\\Temp",
@@ -69,7 +70,7 @@ char *stdpaths_get_xdg_var(const XDGVarType idx)
const char *env_val = os_getenv(env);
-#ifdef WIN32
+#ifdef MSWIN
if (env_val == NULL && xdg_defaults_env_vars[idx] != NULL) {
env_val = os_getenv(xdg_defaults_env_vars[idx]);
}
@@ -87,6 +88,9 @@ char *stdpaths_get_xdg_var(const XDGVarType idx)
} else if (idx == kXDGRuntimeDir) {
// Special-case: stdpath('run') is defined at startup.
ret = vim_gettempdir();
+ if (ret == NULL) {
+ ret = "/tmp/";
+ }
size_t len = strlen(ret);
ret = xstrndup(ret, len >= 2 ? len - 1 : 0); // Trim trailing slash.
}
@@ -107,7 +111,7 @@ char *get_xdg_home(const XDGVarType idx)
{
char *dir = stdpaths_get_xdg_var(idx);
if (dir) {
-#if defined(WIN32)
+#if defined(MSWIN)
dir = concat_fnames_realloc(dir,
((idx == kXDGDataHome
|| idx == kXDGStateHome) ? "nvim-data" : "nvim"),
@@ -117,7 +121,7 @@ char *get_xdg_home(const XDGVarType idx)
#endif
#ifdef BACKSLASH_IN_FILENAME
- slash_adjust((char_u *)dir);
+ slash_adjust(dir);
#endif
}
return dir;
diff --git a/src/nvim/os/time.c b/src/nvim/os/time.c
index 396bf6986a..873302a27d 100644
--- a/src/nvim/os/time.c
+++ b/src/nvim/os/time.c
@@ -1,22 +1,34 @@
// 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 <limits.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
#include <uv.h>
-#include "nvim/assert.h"
+#include "auto/config.h"
#include "nvim/event/loop.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/log.h"
+#include "nvim/macros.h"
#include "nvim/main.h"
+#include "nvim/memory.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/os/time.h"
+struct tm;
+
static uv_mutex_t delay_mutex;
static uv_cond_t delay_cond;
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "os/time.c.generated.h"
+# include "os/time.c.generated.h" // IWYU pragma: export
#endif
/// Initializes the time module
@@ -67,7 +79,7 @@ void os_delay(uint64_t ms, bool ignoreinput)
}
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, (int)ms, got_int);
} else {
- os_microdelay(ms * 1000u, ignoreinput);
+ os_microdelay(ms * 1000U, ignoreinput);
}
}
@@ -80,10 +92,10 @@ void os_delay(uint64_t ms, bool ignoreinput)
/// If false, waiting is aborted on any input.
void os_microdelay(uint64_t us, bool ignoreinput)
{
- uint64_t elapsed = 0u;
+ uint64_t elapsed = 0U;
uint64_t base = uv_hrtime();
// Convert microseconds to nanoseconds, or UINT64_MAX on overflow.
- const uint64_t ns = (us < UINT64_MAX / 1000u) ? us * 1000u : UINT64_MAX;
+ const uint64_t ns = (us < UINT64_MAX / 1000U) ? us * 1000U : UINT64_MAX;
uv_mutex_lock(&delay_mutex);
@@ -92,7 +104,7 @@ void os_microdelay(uint64_t us, bool ignoreinput)
// Else we check for input in ~100ms intervals.
const uint64_t ns_delta = ignoreinput
? ns - elapsed
- : MIN(ns - elapsed, 100000000u); // 100ms
+ : MIN(ns - elapsed, 100000000U); // 100ms
const int rv = uv_cond_timedwait(&delay_cond, &delay_mutex, ns_delta);
if (0 != rv && UV_ETIMEDOUT != rv) {
@@ -167,18 +179,28 @@ struct tm *os_localtime(struct tm *result) FUNC_ATTR_NONNULL_ALL
/// @param result[out] Pointer to a 'char' where the result should be placed
/// @param result_len length of result buffer
/// @return human-readable string of current local time
-char *os_ctime_r(const time_t *restrict clock, char *restrict result, size_t result_len)
+char *os_ctime_r(const time_t *restrict clock, char *restrict result, size_t result_len,
+ bool add_newline)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
{
struct tm clock_local;
struct tm *clock_local_ptr = os_localtime_r(clock, &clock_local);
// MSVC returns NULL for an invalid value of seconds.
if (clock_local_ptr == NULL) {
- xstrlcpy(result, _("(Invalid)"), result_len);
+ xstrlcpy(result, _("(Invalid)"), result_len - 1);
} else {
- strftime(result, result_len, _("%a %b %d %H:%M:%S %Y"), clock_local_ptr);
+ // xgettext:no-c-format
+ if (strftime(result, result_len - 1, _("%a %b %d %H:%M:%S %Y"), clock_local_ptr) == 0) {
+ // Quoting "man strftime":
+ // > If the length of the result string (including the terminating
+ // > null byte) would exceed max bytes, then strftime() returns 0,
+ // > and the contents of the array are undefined.
+ xstrlcpy(result, _("(Invalid)"), result_len - 1);
+ }
+ }
+ if (add_newline) {
+ xstrlcat(result, "\n", result_len);
}
- xstrlcat(result, "\n", result_len);
return result;
}
@@ -187,11 +209,11 @@ char *os_ctime_r(const time_t *restrict clock, char *restrict result, size_t res
/// @param result[out] Pointer to a 'char' where the result should be placed
/// @param result_len length of result buffer
/// @return human-readable string of current local time
-char *os_ctime(char *result, size_t result_len)
+char *os_ctime(char *result, size_t result_len, bool add_newline)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
{
time_t rawtime = time(NULL);
- return os_ctime_r(&rawtime, result, result_len);
+ return os_ctime_r(&rawtime, result, result_len, add_newline);
}
/// Portable version of POSIX strptime()
diff --git a/src/nvim/os/tty.c b/src/nvim/os/tty.c
index 126b1b0044..b5124bd83a 100644
--- a/src/nvim/os/tty.c
+++ b/src/nvim/os/tty.c
@@ -5,14 +5,14 @@
// Terminal/console utils
//
-#include "nvim/os/os.h"
+#include "nvim/os/os.h" // IWYU pragma: keep (Windows)
#include "nvim/os/tty.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "os/tty.c.generated.h"
+# include "os/tty.c.generated.h" // IWYU pragma: export
#endif
-#ifdef WIN32
+#ifdef MSWIN
# if !defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING)
# define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
# endif
diff --git a/src/nvim/os/unix_defs.h b/src/nvim/os/unix_defs.h
index 4ed3b51694..8d002fc5e9 100644
--- a/src/nvim/os/unix_defs.h
+++ b/src/nvim/os/unix_defs.h
@@ -3,6 +3,9 @@
#include <sys/param.h>
#include <unistd.h>
+#if defined(HAVE_TERMIOS_H)
+# include <termios.h>
+#endif
// POSIX.1-2008 says that NAME_MAX should be in here
#include <limits.h>
diff --git a/src/nvim/os/users.c b/src/nvim/os/users.c
index bd34e917b2..ef2986246b 100644
--- a/src/nvim/os/users.c
+++ b/src/nvim/os/users.c
@@ -3,6 +3,9 @@
// users.c -- operating system user information
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
#include <uv.h>
#include "auto/config.h"
@@ -10,11 +13,12 @@
#include "nvim/garray.h"
#include "nvim/memory.h"
#include "nvim/os/os.h"
-#include "nvim/strings.h"
+#include "nvim/types.h"
+#include "nvim/vim.h"
#ifdef HAVE_PWD_H
# include <pwd.h>
#endif
-#ifdef WIN32
+#ifdef MSWIN
# include <lm.h>
#endif
@@ -56,7 +60,7 @@ int os_get_usernames(garray_T *users)
}
endpwent();
}
-#elif defined(WIN32)
+#elif defined(MSWIN)
{
DWORD nusers = 0, ntotal = 0, i;
PUSER_INFO_0 uinfo;
@@ -93,7 +97,7 @@ int os_get_usernames(garray_T *users)
for (i = 0; i < users->ga_len; i++) {
char *local_user = ((char **)users->ga_data)[i];
- if (STRCMP(local_user, user_env) == 0) {
+ if (strcmp(local_user, user_env) == 0) {
break;
}
}
@@ -142,7 +146,7 @@ int os_get_uname(uv_uid_t uid, char *s, size_t len)
if ((pw = getpwuid(uid)) != NULL // NOLINT(runtime/threadsafe_fn)
&& pw->pw_name != NULL && *(pw->pw_name) != NUL) {
- STRLCPY(s, pw->pw_name, len);
+ xstrlcpy(s, pw->pw_name, len);
return OK;
}
#endif
@@ -208,17 +212,17 @@ char *get_users(expand_T *xp, int idx)
/// @return 0 if name does not match any user name.
/// 1 if name partially matches the beginning of a user name.
/// 2 is name fully matches a user name.
-int match_user(char_u *name)
+int match_user(char *name)
{
- int n = (int)STRLEN(name);
+ int n = (int)strlen(name);
int result = 0;
init_users();
for (int i = 0; i < ga_users.ga_len; i++) {
- if (STRCMP(((char_u **)ga_users.ga_data)[i], name) == 0) {
+ if (strcmp(((char **)ga_users.ga_data)[i], name) == 0) {
return 2; // full match
}
- if (STRNCMP(((char_u **)ga_users.ga_data)[i], name, n) == 0) {
+ if (strncmp(((char **)ga_users.ga_data)[i], name, (size_t)n) == 0) {
result = 1; // partial match
}
}
diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h
index 1ae86d6bbe..4f8a242a51 100644
--- a/src/nvim/os/win_defs.h
+++ b/src/nvim/os/win_defs.h
@@ -1,7 +1,7 @@
#ifndef NVIM_OS_WIN_DEFS_H
#define NVIM_OS_WIN_DEFS_H
-#ifndef WIN32
+#ifndef MSWIN
# error Header must be included only when compiling for Windows.
#endif
diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c
deleted file mode 100644
index 473bf5072c..0000000000
--- a/src/nvim/os_unix.c
+++ /dev/null
@@ -1,73 +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 <assert.h>
-#include <errno.h>
-#include <inttypes.h>
-#include <stdbool.h>
-#include <string.h>
-
-#include "nvim/ascii.h"
-#include "nvim/buffer.h"
-#include "nvim/charset.h"
-#include "nvim/eval.h"
-#include "nvim/ex_cmds.h"
-#include "nvim/fileio.h"
-#include "nvim/garray.h"
-#include "nvim/getchar.h"
-#include "nvim/main.h"
-#include "nvim/mbyte.h"
-#include "nvim/memline.h"
-#include "nvim/memory.h"
-#include "nvim/message.h"
-#include "nvim/mouse.h"
-#include "nvim/msgpack_rpc/helpers.h"
-#include "nvim/os/input.h"
-#include "nvim/os/os.h"
-#include "nvim/os/shell.h"
-#include "nvim/os/signal.h"
-#include "nvim/os/time.h"
-#include "nvim/os_unix.h"
-#include "nvim/path.h"
-#include "nvim/screen.h"
-#include "nvim/strings.h"
-#include "nvim/syntax.h"
-#include "nvim/types.h"
-#include "nvim/ui.h"
-#include "nvim/vim.h"
-
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "os_unix.c.generated.h"
-#endif
-
-#if defined(HAVE_ACL)
-# ifdef HAVE_SYS_ACL_H
-# include <sys/acl.h>
-# endif
-# ifdef HAVE_SYS_ACCESS_H
-# 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)
-{
- vim_acl_T ret = NULL;
- return ret;
-}
-
-// Set the ACL of file "fname" to "acl" (unless it's NULL).
-void mch_set_acl(const char_u *fname, vim_acl_T aclent)
-{
- if (aclent == NULL) {
- return;
- }
-}
-
-void mch_free_acl(vim_acl_T aclent)
-{
- if (aclent == NULL) {
- return;
- }
-}
-#endif
diff --git a/src/nvim/os_unix.h b/src/nvim/os_unix.h
deleted file mode 100644
index aae05f7fcc..0000000000
--- a/src/nvim/os_unix.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef NVIM_OS_UNIX_H
-#define NVIM_OS_UNIX_H
-
-#include "nvim/os/shell.h"
-#include "nvim/types.h" // for vim_acl_T
-
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "os_unix.h.generated.h"
-#endif
-#endif // NVIM_OS_UNIX_H
diff --git a/src/nvim/path.c b/src/nvim/path.c
index 1500254de5..513f366a27 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -2,11 +2,17 @@
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include <assert.h>
-#include <inttypes.h>
+#include <ctype.h>
+#include <limits.h>
#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
+#include "auto/config.h"
#include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
#include "nvim/eval.h"
@@ -14,27 +20,29 @@
#include "nvim/file_search.h"
#include "nvim/fileio.h"
#include "nvim/garray.h"
-#include "nvim/memfile.h"
-#include "nvim/memline.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/option.h"
+#include "nvim/os/fs_defs.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/os/shell.h"
-#include "nvim/os_unix.h"
#include "nvim/path.h"
-#include "nvim/quickfix.h"
+#include "nvim/pos.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
#include "nvim/strings.h"
-#include "nvim/tag.h"
#include "nvim/types.h"
#include "nvim/vim.h"
#include "nvim/window.h"
-#define URL_SLASH 1 // path_is_url() has found "://"
-#define URL_BACKSLASH 2 // path_is_url() has found ":\\"
+enum {
+ URL_SLASH = 1, // path_is_url() has found ":/"
+ URL_BACKSLASH = 2, // path_is_url() has found ":\\"
+};
#ifdef gen_expand_wildcards
# undef gen_expand_wildcards
@@ -62,9 +70,9 @@ FileComparison path_full_compare(char *const s1, char *const s2, const bool chec
FileID file_id_1, file_id_2;
if (expandenv) {
- expand_env((char_u *)s1, (char_u *)exp1, MAXPATHL);
+ expand_env(s1, exp1, MAXPATHL);
} else {
- STRLCPY(exp1, s1, MAXPATHL);
+ xstrlcpy(exp1, s1, MAXPATHL);
}
bool id_ok_1 = os_fileid(exp1, &file_id_1);
bool id_ok_2 = os_fileid(s2, &file_id_2);
@@ -73,7 +81,7 @@ FileComparison path_full_compare(char *const s1, char *const s2, const bool chec
if (checkname) {
vim_FullName(exp1, full1, MAXPATHL, false);
vim_FullName(s2, full2, MAXPATHL, false);
- if (FNAMECMP(full1, full2) == 0) {
+ if (path_fnamecmp(full1, full2) == 0) {
return kEqualFileNames;
}
}
@@ -104,7 +112,7 @@ char *path_tail(const char *fname)
return "";
}
- const char *tail = (char *)get_past_head((char_u *)fname);
+ const char *tail = get_past_head(fname);
const char *p = tail;
// Find last part of path.
while (*p != NUL) {
@@ -130,7 +138,7 @@ char *path_tail_with_sep(char *fname)
assert(fname != NULL);
// Don't remove the '/' from "c:/file".
- char *past_head = (char *)get_past_head((char_u *)fname);
+ char *past_head = get_past_head(fname);
char *tail = path_tail(fname);
while (tail > past_head && after_pathsep(fname, tail)) {
tail--;
@@ -147,11 +155,11 @@ char *path_tail_with_sep(char *fname)
/// @post if `len` is not null, stores the length of the executable name.
///
/// @return The position of the last path separator + 1.
-const char_u *invocation_path_tail(const char_u *invocation, size_t *len)
+const char *invocation_path_tail(const char *invocation, size_t *len)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ARG(1)
{
- const char_u *tail = get_past_head((char_u *)invocation);
- const char_u *p = tail;
+ const char *tail = get_past_head(invocation);
+ const char *p = tail;
while (*p != NUL && *p != ' ') {
bool was_sep = vim_ispathsep_nocolon(*p);
MB_PTR_ADV(p);
@@ -190,7 +198,7 @@ const char *path_next_component(const char *fname)
/// - 1 otherwise
int path_head_length(void)
{
-#ifdef WIN32
+#ifdef MSWIN
return 3;
#else
return 1;
@@ -203,10 +211,10 @@ int path_head_length(void)
/// @return
/// - True if path begins with a path head
/// - False otherwise
-bool is_path_head(const char_u *path)
+bool is_path_head(const char *path)
{
-#ifdef WIN32
- return isalpha(path[0]) && path[1] == ':';
+#ifdef MSWIN
+ return isalpha((uint8_t)path[0]) && path[1] == ':';
#else
return vim_ispathsep(*path);
#endif
@@ -215,11 +223,11 @@ bool is_path_head(const char_u *path)
/// Get a pointer to one character past the head of a path name.
/// Unix: after "/"; Win: after "c:\"
/// If there is no head, path is returned.
-char_u *get_past_head(const char_u *path)
+char *get_past_head(const char *path)
{
- const char_u *retval = path;
+ const char *retval = path;
-#ifdef WIN32
+#ifdef MSWIN
// May skip "c:"
if (is_path_head(path)) {
retval = path + 2;
@@ -230,13 +238,11 @@ char_u *get_past_head(const char_u *path)
retval++;
}
- return (char_u *)retval;
+ return (char *)retval;
}
-/*
- * Return TRUE if 'c' is a path separator.
- * Note that for MS-Windows this includes the colon.
- */
+/// Return true if 'c' is a path separator.
+/// Note that for MS-Windows this includes the colon.
int vim_ispathsep(int c)
{
#ifdef UNIX
@@ -250,9 +256,7 @@ int vim_ispathsep(int c)
#endif
}
-/*
- * Like vim_ispathsep(c), but exclude the colon for MS-Windows.
- */
+// Like vim_ispathsep(c), but exclude the colon for MS-Windows.
int vim_ispathsep_nocolon(int c)
{
return vim_ispathsep(c)
@@ -262,9 +266,7 @@ int vim_ispathsep_nocolon(int c)
;
}
-/*
- * return TRUE if 'c' is a path list separator.
- */
+/// return true if 'c' is a path list separator.
int vim_ispathlistsep(int c)
{
#ifdef UNIX
@@ -278,13 +280,13 @@ int vim_ispathlistsep(int c)
/// "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)
+void shorten_dir_len(char *str, int trim_len)
{
- char_u *tail = (char_u *)path_tail((char *)str);
- char_u *d = str;
+ char *tail = path_tail(str);
+ char *d = str;
bool skip = false;
int dirchunk_len = 0;
- for (char_u *s = str;; s++) {
+ for (char *s = str;; s++) {
if (s >= tail) { // copy the whole tail
*d++ = *s;
if (*s == NUL) {
@@ -304,7 +306,7 @@ void shorten_dir_len(char_u *str, int trim_len)
skip = true;
}
}
- int l = utfc_ptr2len((char *)s);
+ int l = utfc_ptr2len(s);
while (--l > 0) {
*d++ = *++s;
}
@@ -314,20 +316,18 @@ void shorten_dir_len(char_u *str, int trim_len)
/// 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)
+void shorten_dir(char *str)
{
shorten_dir_len(str, 1);
}
-/*
- * Return TRUE if the directory of "fname" exists, FALSE otherwise.
- * Also returns TRUE if there is no directory name.
- * "fname" must be writable!.
- */
-bool dir_of_file_exists(char_u *fname)
+/// Return true if the directory of "fname" exists, false otherwise.
+/// Also returns true if there is no directory name.
+/// "fname" must be writable!.
+bool dir_of_file_exists(char *fname)
{
- char *p = path_tail_with_sep((char *)fname);
- if ((char_u *)p == fname) {
+ char *p = path_tail_with_sep(fname);
+ if (p == fname) {
return true;
}
char c = *p;
@@ -339,6 +339,12 @@ bool dir_of_file_exists(char_u *fname)
/// Compare two file names
///
+/// On some systems case in a file name does not matter, on others it does.
+///
+/// @note Does not account for maximum name lengths and things like "../dir",
+/// thus it is not 100% accurate. OS may also use different algorithm for
+/// case-insensitive comparison.
+///
/// Handles '/' and '\\' correctly and deals with &fileignorecase option.
///
/// @param[in] fname1 First file name.
@@ -376,21 +382,21 @@ int path_fnamencmp(const char *const fname1, const char *const fname2, size_t le
const char *p1 = fname1;
const char *p2 = fname2;
while (len > 0) {
- c1 = utf_ptr2char((const char_u *)p1);
- c2 = utf_ptr2char((const char_u *)p2);
+ c1 = utf_ptr2char(p1);
+ c2 = utf_ptr2char(p2);
if ((c1 == NUL || c2 == NUL
|| (!((c1 == '/' || c1 == '\\') && (c2 == '\\' || c2 == '/'))))
&& (p_fic ? (c1 != c2 && CH_FOLD(c1) != CH_FOLD(c2)) : c1 != c2)) {
break;
}
- len -= (size_t)utfc_ptr2len((const char_u *)p1);
- p1 += utfc_ptr2len((const char_u *)p1);
- p2 += utfc_ptr2len((const char_u *)p2);
+ len -= (size_t)utfc_ptr2len(p1);
+ p1 += utfc_ptr2len(p1);
+ p2 += utfc_ptr2len(p2);
}
return p_fic ? CH_FOLD(c1) - CH_FOLD(c2) : c1 - c2;
#else
if (p_fic) {
- return mb_strnicmp((const char_u *)fname1, (const char_u *)fname2, len);
+ return mb_strnicmp(fname1, fname2, len);
}
return strncmp(fname1, fname2, len);
#endif
@@ -505,7 +511,7 @@ char *FullName_save(const char *fname, bool force)
char *save_abs_path(const char *name)
FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
{
- if (!path_is_absolute((char_u *)name)) {
+ if (!path_is_absolute(name)) {
return FullName_save(name, true);
}
return xstrdup(name);
@@ -515,7 +521,7 @@ char *save_abs_path(const char *name)
/// @param p The path to expand.
/// @returns Unix: True if it contains one of "?[{`'$".
/// @returns Windows: True if it contains one of "*?$[".
-bool path_has_wildcard(const char_u *p)
+bool path_has_wildcard(const char *p)
FUNC_ATTR_NONNULL_ALL
{
for (; *p; MB_PTR_ADV(p)) {
@@ -530,7 +536,7 @@ bool path_has_wildcard(const char_u *p)
// Windows:
const char *wildcards = "?*$[`";
#endif
- if (vim_strchr(wildcards, *p) != NULL
+ if (vim_strchr(wildcards, (uint8_t)(*p)) != NULL
|| (p[0] == '~' && p[1] != NUL)) {
return true;
}
@@ -538,9 +544,7 @@ bool path_has_wildcard(const char_u *p)
return false;
}
-/*
- * Unix style wildcard expansion code.
- */
+// Unix style wildcard expansion code.
static int pstrcmp(const void *a, const void *b)
{
return pathcmp(*(char **)a, *(char **)b, -1);
@@ -550,7 +554,7 @@ static int pstrcmp(const void *a, const void *b)
/// @param p The path to expand.
/// @returns Unix: True if it contains one of *?[{.
/// @returns Windows: True if it contains one of *?[.
-bool path_has_exp_wildcard(const char_u *p)
+bool path_has_exp_wildcard(const char *p)
FUNC_ATTR_NONNULL_ALL
{
for (; *p != NUL; MB_PTR_ADV(p)) {
@@ -564,7 +568,7 @@ bool path_has_exp_wildcard(const char_u *p)
#else
const char *wildcards = "*?["; // Windows.
#endif
- if (vim_strchr(wildcards, *p) != NULL) {
+ if (vim_strchr(wildcards, (uint8_t)(*p)) != NULL) {
return true;
}
}
@@ -579,10 +583,10 @@ bool path_has_exp_wildcard(const char_u *p)
/// @param path The path to search.
/// @param flags Flags for regexp expansion.
/// - EW_ICASE: Ignore case.
-/// - EW_NOERROR: Silence error messeges.
+/// - EW_NOERROR: Silence error messages.
/// - EW_NOTWILD: Add matches literally.
/// @returns the number of matches found.
-static size_t path_expand(garray_T *gap, const char_u *path, int flags)
+static size_t path_expand(garray_T *gap, const char *path, int flags)
FUNC_ATTR_NONNULL_ALL
{
return do_path_expand(gap, path, 0, flags, false);
@@ -606,7 +610,7 @@ static const char *scandir_next_with_dots(Directory *dir)
/// Implementation of path_expand().
///
/// Chars before `path + wildoff` do not get expanded.
-static size_t do_path_expand(garray_T *gap, const char_u *path, size_t wildoff, int flags,
+static size_t do_path_expand(garray_T *gap, const char *path, size_t wildoff, int flags,
bool didstar)
FUNC_ATTR_NONNULL_ALL
{
@@ -625,19 +629,19 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, size_t wildoff,
// Make room for file name. When doing encoding conversion the actual
// length may be quite a bit longer, thus use the maximum possible length.
- char_u *buf = xmalloc(MAXPATHL);
+ char *buf = xmalloc(MAXPATHL);
// Find the first part in the path name that contains a wildcard.
// When EW_ICASE is set every letter is considered to be a wildcard.
// Copy it into "buf", including the preceding characters.
- char_u *p = buf;
- char_u *s = buf;
- char_u *e = NULL;
- const char_u *path_end = path;
+ char *p = buf;
+ char *s = buf;
+ char *e = NULL;
+ const char *path_end = path;
while (*path_end != NUL) {
- /* May ignore a wildcard that has a backslash before it; it will
- * be removed by rem_backslash() or file_pat_to_reg_pat() below. */
- if (path_end >= path + wildoff && rem_backslash(path_end)) {
+ // May ignore a wildcard that has a backslash before it; it will
+ // be removed by rem_backslash() or file_pat_to_reg_pat() below.
+ if (path_end >= path + wildoff && rem_backslash((char *)path_end)) {
*p++ = *path_end++;
} else if (vim_ispathsep_nocolon(*path_end)) {
if (e != NULL) {
@@ -645,11 +649,11 @@ 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("*?[{~$", *path_end) != NULL
-#ifndef WIN32
+ && (vim_strchr("*?[{~$", (uint8_t)(*path_end)) != NULL
+#ifndef MSWIN
|| (!p_fic && (flags & EW_ICASE) && mb_isalpha(utf_ptr2char((char *)path_end)))
#endif
- )) {
+ )) { // NOLINT(whitespace/parens)
e = p;
}
len = (size_t)(utfc_ptr2len((char *)path_end));
@@ -661,9 +665,9 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, size_t wildoff,
*e = NUL;
// Now we have one wildcard component between "s" and "e".
- /* Remove backslashes between "wildoff" and the start of the wildcard
- * component. */
- for (p = buf + wildoff; p < s; ++p) {
+ // Remove backslashes between "wildoff" and the start of the wildcard
+ // component.
+ for (p = buf + wildoff; p < s; p++) {
if (rem_backslash(p)) {
STRMOVE(p, p + 1);
e--;
@@ -672,7 +676,7 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, size_t wildoff,
}
// Check for "**" between "s" and "e".
- for (p = s; p < e; ++p) {
+ for (p = s; p < e; p++) {
if (p[0] == '*' && p[1] == '*') {
starstar = true;
}
@@ -680,7 +684,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 *pat = file_pat_to_reg_pat((char *)s, (char *)e, NULL, false);
+ char *pat = file_pat_to_reg_pat(s, e, NULL, false);
if (pat == NULL) {
xfree(buf);
return 0;
@@ -708,8 +712,8 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, size_t wildoff,
return 0;
}
- /* If "**" is by itself, this is the first time we encounter it and more
- * is following then find matches without any directory. */
+ // If "**" is by itself, this is the first time we encounter it and more
+ // is following then find matches without any directory.
if (!didstar && stardepth < 100 && starstar && e - s == 2
&& *path_end == '/') {
STRCPY(s, path_end + 1);
@@ -720,27 +724,27 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, size_t wildoff,
*s = NUL;
Directory dir;
- char *dirpath = (*buf == NUL ? "." : (char *)buf);
+ char *dirpath = (*buf == NUL ? "." : buf);
if (os_file_is_readable(dirpath) && os_scandir(&dir, dirpath)) {
// Find all matching entries.
- char_u *name;
+ char *name;
scandir_next_with_dots(NULL); // initialize
- while (!got_int && (name = (char_u *)scandir_next_with_dots(&dir)) != NULL) {
+ while (!got_int && (name = (char *)scandir_next_with_dots(&dir)) != NULL) {
if ((name[0] != '.'
|| starts_with_dot
|| ((flags & EW_DODOT)
&& name[1] != NUL
&& (name[1] != '.' || name[2] != NUL))) // -V557
- && ((regmatch.regprog != NULL && vim_regexec(&regmatch, (char *)name, 0))
+ && ((regmatch.regprog != NULL && vim_regexec(&regmatch, name, 0))
|| ((flags & EW_NOTWILD)
- && FNAMENCMP(path + (s - buf), name, e - s) == 0))) {
+ && path_fnamencmp(path + (s - buf), name, (size_t)(e - s)) == 0))) {
STRCPY(s, name);
- len = STRLEN(buf);
+ len = strlen(buf);
if (starstar && stardepth < 100) {
- /* For "**" in the pattern first go deeper in the tree to
- * find matches. */
- STRCPY(buf + len, "/**");
+ // For "**" in the pattern first go deeper in the tree to
+ // find matches.
+ STRCPY(buf + len, "/**"); // NOLINT
STRCPY(buf + len + 3, path_end);
stardepth++;
(void)do_path_expand(gap, buf, len + 1, flags, true);
@@ -761,8 +765,9 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, size_t wildoff,
backslash_halve(buf + len + 1);
}
// add existing file or symbolic link
- if ((flags & EW_ALLLINKS) ? os_fileinfo_link((char *)buf, &file_info)
- : os_path_exists(buf)) {
+ if ((flags & EW_ALLLINKS)
+ ? os_fileinfo_link(buf, &file_info)
+ : os_path_exists(buf)) {
addfile(gap, buf, flags);
}
}
@@ -778,21 +783,19 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, size_t wildoff,
// slow, thus skip it.
size_t matches = (size_t)(gap->ga_len - start_len);
if (matches > 0 && !got_int) {
- qsort(((char_u **)gap->ga_data) + start_len, matches,
- sizeof(char_u *), pstrcmp);
+ qsort(((char **)gap->ga_data) + start_len, matches,
+ sizeof(char *), pstrcmp);
}
return matches;
}
-/*
- * Moves "*psep" back to the previous path separator in "path".
- * Returns FAIL is "*psep" ends up at the beginning of "path".
- */
-static int find_previous_pathsep(char_u *path, char_u **psep)
+// Moves "*psep" back to the previous path separator in "path".
+// Returns FAIL is "*psep" ends up at the beginning of "path".
+static int find_previous_pathsep(char *path, char **psep)
{
// skip the current separator
if (*psep > path && vim_ispathsep(**psep)) {
- --*psep;
+ (*psep)--;
}
// find the previous separator
@@ -806,25 +809,23 @@ static int find_previous_pathsep(char_u *path, char_u **psep)
return FAIL;
}
-/*
- * Returns TRUE if "maybe_unique" is unique wrt other_paths in "gap".
- * "maybe_unique" is the end portion of "((char_u **)gap->ga_data)[i]".
- */
-static bool is_unique(char_u *maybe_unique, garray_T *gap, int i)
+/// Returns true if "maybe_unique" is unique wrt other_paths in "gap".
+/// "maybe_unique" is the end portion of "((char_u **)gap->ga_data)[i]".
+static bool is_unique(char *maybe_unique, garray_T *gap, int i)
{
- char_u **other_paths = (char_u **)gap->ga_data;
+ char **other_paths = gap->ga_data;
for (int j = 0; j < gap->ga_len; j++) {
if (j == i) {
continue; // don't compare it with itself
}
- size_t candidate_len = STRLEN(maybe_unique);
- size_t other_path_len = STRLEN(other_paths[j]);
+ size_t candidate_len = strlen(maybe_unique);
+ size_t other_path_len = strlen(other_paths[j]);
if (other_path_len < candidate_len) {
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
+ char *rival = other_paths[j] + other_path_len - candidate_len;
+ if (path_fnamecmp(maybe_unique, rival) == 0
&& (rival == other_paths[j] || vim_ispathsep(*(rival - 1)))) {
return false; // match
}
@@ -832,33 +833,31 @@ static bool is_unique(char_u *maybe_unique, garray_T *gap, int i)
return true; // no match found
}
-/*
- * Split the 'path' option into an array of strings in garray_T. Relative
- * paths are expanded to their equivalent fullpath. This includes the "."
- * (relative to current buffer directory) and empty path (relative to current
- * directory) notations.
- *
- * TODO: handle upward search (;) and path limiter (**N) notations by
- * expanding each into their equivalent path(s).
- */
-static void expand_path_option(char_u *curdir, garray_T *gap)
+// Split the 'path' option into an array of strings in garray_T. Relative
+// paths are expanded to their equivalent fullpath. This includes the "."
+// (relative to current buffer directory) and empty path (relative to current
+// directory) notations.
+//
+// TODO(vim): handle upward search (;) and path limiter (**N) notations by
+// expanding each into their equivalent path(s).
+static void expand_path_option(char *curdir, garray_T *gap)
{
- char_u *path_option = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path;
- char_u *buf = xmalloc(MAXPATHL);
+ char *path_option = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path;
+ char *buf = xmalloc(MAXPATHL);
while (*path_option != NUL) {
- copy_option_part((char **)&path_option, (char *)buf, MAXPATHL, " ,");
+ copy_option_part(&path_option, buf, MAXPATHL, " ,");
if (buf[0] == '.' && (buf[1] == NUL || vim_ispathsep(buf[1]))) {
- /* Relative to current buffer:
- * "/path/file" + "." -> "/path/"
- * "/path/file" + "./subdir" -> "/path/subdir" */
+ // Relative to current buffer:
+ // "/path/file" + "." -> "/path/"
+ // "/path/file" + "./subdir" -> "/path/subdir"
if (curbuf->b_ffname == NULL) {
continue;
}
- 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) {
+ char *p = path_tail(curbuf->b_ffname);
+ size_t len = (size_t)(p - curbuf->b_ffname);
+ if (len + strlen(buf) >= MAXPATHL) {
continue;
}
if (buf[1] == NUL) {
@@ -870,12 +869,12 @@ static void expand_path_option(char_u *curdir, garray_T *gap)
simplify_filename(buf);
} else if (buf[0] == NUL) {
STRCPY(buf, curdir); // relative to current directory
- } else if (path_with_url((char *)buf)) {
+ } else if (path_with_url(buf)) {
continue; // URL can't be used here
} else if (!path_is_absolute(buf)) {
// Expand relative path to their full path equivalent
- size_t len = STRLEN(curdir);
- if (len + STRLEN(buf) + 3 > MAXPATHL) {
+ size_t len = strlen(curdir);
+ if (len + strlen(buf) + 3 > MAXPATHL) {
continue;
}
STRMOVE(buf + len + 1, buf);
@@ -884,31 +883,29 @@ static void expand_path_option(char_u *curdir, garray_T *gap)
simplify_filename(buf);
}
- GA_APPEND(char_u *, gap, vim_strsave(buf));
+ GA_APPEND(char *, gap, xstrdup(buf));
}
xfree(buf);
}
-/*
- * Returns a pointer to the file or directory name in "fname" that matches the
- * longest path in "ga"p, or NULL if there is no match. For example:
- *
- * path: /foo/bar/baz
- * fname: /foo/bar/baz/quux.txt
- * returns: ^this
- */
-static char_u *get_path_cutoff(char_u *fname, garray_T *gap)
+// Returns a pointer to the file or directory name in "fname" that matches the
+// longest path in "ga"p, or NULL if there is no match. For example:
+//
+// path: /foo/bar/baz
+// fname: /foo/bar/baz/quux.txt
+// returns: ^this
+static char *get_path_cutoff(char *fname, garray_T *gap)
{
int maxlen = 0;
- char_u **path_part = (char_u **)gap->ga_data;
- char_u *cutoff = NULL;
+ char **path_part = gap->ga_data;
+ char *cutoff = NULL;
for (int i = 0; i < gap->ga_len; i++) {
int j = 0;
while ((fname[j] == path_part[i][j]
-#ifdef WIN32
+#ifdef MSWIN
|| (vim_ispathsep(fname[j]) && vim_ispathsep(path_part[i][j]))
#endif
) // NOLINT(whitespace/parens)
@@ -931,32 +928,30 @@ static char_u *get_path_cutoff(char_u *fname, garray_T *gap)
return cutoff;
}
-/*
- * Sorts, removes duplicates and modifies all the fullpath names in "gap" so
- * that they are unique with respect to each other while conserving the part
- * that matches the pattern. Beware, this is at least O(n^2) wrt "gap->ga_len".
- */
-static void uniquefy_paths(garray_T *gap, char_u *pattern)
+/// Sorts, removes duplicates and modifies all the fullpath names in "gap" so
+/// that they are unique with respect to each other while conserving the part
+/// that matches the pattern. Beware, this is at least O(n^2) wrt "gap->ga_len".
+static void uniquefy_paths(garray_T *gap, char *pattern)
{
- char_u **fnames = (char_u **)gap->ga_data;
+ char **fnames = gap->ga_data;
bool sort_again = false;
regmatch_T regmatch;
garray_T path_ga;
- char_u **in_curdir = NULL;
- char_u *short_name;
+ char **in_curdir = NULL;
+ char *short_name;
ga_remove_duplicate_strings(gap);
- ga_init(&path_ga, (int)sizeof(char_u *), 1);
+ ga_init(&path_ga, (int)sizeof(char *), 1);
// We need to prepend a '*' at the beginning of file_pattern so that the
// regex matches anywhere in the path. FIXME: is this valid for all
// possible patterns?
- size_t len = STRLEN(pattern);
- char_u *file_pattern = xmalloc(len + 2);
+ size_t len = strlen(pattern);
+ char *file_pattern = xmalloc(len + 2);
file_pattern[0] = '*';
file_pattern[1] = NUL;
STRCAT(file_pattern, pattern);
- char *pat = file_pat_to_reg_pat((char *)file_pattern, NULL, NULL, true);
+ char *pat = file_pat_to_reg_pat(file_pattern, NULL, NULL, true);
xfree(file_pattern);
if (pat == NULL) {
return;
@@ -969,24 +964,24 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern)
return;
}
- char_u *curdir = xmalloc(MAXPATHL);
+ char *curdir = xmalloc(MAXPATHL);
os_dirname(curdir, MAXPATHL);
expand_path_option(curdir, &path_ga);
- in_curdir = xcalloc((size_t)gap->ga_len, sizeof(char_u *));
+ in_curdir = xcalloc((size_t)gap->ga_len, sizeof(char *));
for (int i = 0; i < gap->ga_len && !got_int; i++) {
- char_u *path = fnames[i];
+ char *path = fnames[i];
int is_in_curdir;
- char_u *dir_end = (char_u *)gettail_dir((const char *)path);
- char_u *pathsep_p;
- char_u *path_cutoff;
+ char *dir_end = (char *)gettail_dir((const char *)path);
+ char *pathsep_p;
+ char *path_cutoff;
- len = STRLEN(path);
- is_in_curdir = FNAMENCMP(curdir, path, dir_end - path) == 0
+ len = strlen(path);
+ is_in_curdir = path_fnamencmp(curdir, path, (size_t)(dir_end - path)) == 0
&& curdir[dir_end - path] == NUL;
if (is_in_curdir) {
- in_curdir[i] = vim_strsave(path);
+ in_curdir[i] = xstrdup(path);
}
// Shorten the filename while maintaining its uniqueness
@@ -998,20 +993,20 @@ 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, (char *)path_cutoff, (colnr_T)0)
+ && vim_regexec(&regmatch, path_cutoff, (colnr_T)0)
&& is_unique(path_cutoff, gap, i)) {
sort_again = true;
- memmove(path, path_cutoff, STRLEN(path_cutoff) + 1);
+ memmove(path, path_cutoff, strlen(path_cutoff) + 1);
} else {
// Here all files can be reached without path, so get shortest
// 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, (char *)pathsep_p + 1, (colnr_T)0)
+ if (vim_regexec(&regmatch, pathsep_p + 1, (colnr_T)0)
&& is_unique(pathsep_p + 1, gap, i)
&& path_cutoff != NULL && pathsep_p + 1 >= path_cutoff) {
sort_again = true;
- memmove(path, pathsep_p + 1, STRLEN(pathsep_p));
+ memmove(path, pathsep_p + 1, strlen(pathsep_p));
break;
}
}
@@ -1031,8 +1026,8 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern)
short_name = path_shorten_fname(path, curdir);
if (short_name != NULL && short_name > path + 1) {
STRCPY(path, ".");
- add_pathsep((char *)path);
- STRMOVE(path + STRLEN(path), short_name);
+ add_pathsep(path);
+ STRMOVE(path + strlen(path), short_name);
}
}
os_breakcheck();
@@ -1040,15 +1035,15 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern)
// Shorten filenames in /in/current/directory/{filename}
for (int i = 0; i < gap->ga_len && !got_int; i++) {
- char_u *rel_path;
- char_u *path = in_curdir[i];
+ char *rel_path;
+ char *path = in_curdir[i];
if (path == NULL) {
continue;
}
- /* If the {filename} is not unique, change it to ./{filename}.
- * Else reduce it to {filename} */
+ // If the {filename} is not unique, change it to ./{filename}.
+ // Else reduce it to {filename}
short_name = path_shorten_fname(path, curdir);
if (short_name == NULL) {
short_name = path;
@@ -1058,9 +1053,9 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern)
continue;
}
- rel_path = xmalloc(STRLEN(short_name) + STRLEN(PATHSEPSTR) + 2);
+ rel_path = xmalloc(strlen(short_name) + strlen(PATHSEPSTR) + 2);
STRCPY(rel_path, ".");
- add_pathsep((char *)rel_path);
+ add_pathsep(rel_path);
STRCAT(rel_path, short_name);
xfree(fnames[i]);
@@ -1119,21 +1114,21 @@ const char *gettail_dir(const char *const fname)
/// Returns the total number of matches.
///
/// @param flags EW_* flags
-static int expand_in_path(garray_T *const gap, char_u *const pattern, const int flags)
+static int expand_in_path(garray_T *const gap, char *const pattern, const int flags)
{
garray_T path_ga;
- char_u *const curdir = xmalloc(MAXPATHL);
+ char *const curdir = xmalloc(MAXPATHL);
os_dirname(curdir, MAXPATHL);
- ga_init(&path_ga, (int)sizeof(char_u *), 1);
+ ga_init(&path_ga, (int)sizeof(char *), 1);
expand_path_option(curdir, &path_ga);
xfree(curdir);
if (GA_EMPTY(&path_ga)) {
return 0;
}
- char_u *const paths = ga_concat_strings(&path_ga);
+ char *const paths = ga_concat_strings(&path_ga);
ga_clear_strings(&path_ga);
int glob_flags = 0;
@@ -1143,22 +1138,20 @@ static int expand_in_path(garray_T *const gap, char_u *const pattern, const int
if (flags & EW_ADDSLASH) {
glob_flags |= WILD_ADD_SLASH;
}
- globpath((char *)paths, pattern, gap, glob_flags);
+ globpath(paths, pattern, gap, glob_flags);
xfree(paths);
return gap->ga_len;
}
-/*
- * Return TRUE if "p" contains what looks like an environment variable.
- * Allowing for escaping.
- */
-static bool has_env_var(char_u *p)
+/// Return true if "p" contains what looks like an environment variable.
+/// Allowing for escaping.
+static bool has_env_var(char *p)
{
for (; *p; MB_PTR_ADV(p)) {
if (*p == '\\' && p[1] != NUL) {
p++;
- } else if (vim_strchr("$", *p) != NULL) {
+ } else if (vim_strchr("$", (uint8_t)(*p)) != NULL) {
return true;
}
}
@@ -1167,9 +1160,9 @@ static bool has_env_var(char_u *p)
#ifdef SPECIAL_WILDCHAR
-// Return TRUE if "p" contains a special wildcard character, one that Vim
+// Return true if "p" contains a special wildcard character, one that Vim
// cannot expand, requires using a shell.
-static bool has_special_wildchar(char_u *p)
+static bool has_special_wildchar(char *p)
{
for (; *p; MB_PTR_ADV(p)) {
// Disallow line break characters.
@@ -1179,13 +1172,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(SPECIAL_WILDCHAR, *p) != NULL) {
+ } else if (vim_strchr(SPECIAL_WILDCHAR, (uint8_t)(*p)) != NULL) {
// A { must be followed by a matching }.
- if (*p == '{' && vim_strchr((char *)p, '}') == NULL) {
+ if (*p == '{' && vim_strchr(p, '}') == NULL) {
continue;
}
// A quote and backtick must be followed by another one.
- if ((*p == '`' || *p == '\'') && vim_strchr((char *)p, *p) == NULL) {
+ if ((*p == '`' || *p == '\'') && vim_strchr(p, (uint8_t)(*p)) == NULL) {
continue;
}
return true;
@@ -1216,34 +1209,31 @@ static bool has_special_wildchar(char_u *p)
int gen_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, int flags)
{
garray_T ga;
- char_u *p;
+ char *p;
static bool recursive = false;
int add_pat;
bool did_expand_in_path = false;
- /*
- * expand_env() is called to expand things like "~user". If this fails,
- * it calls ExpandOne(), which brings us back here. In this case, always
- * call the machine specific expansion function, if possible. Otherwise,
- * return FAIL.
- */
- if (recursive)
+ // expand_env() is called to expand things like "~user". If this fails,
+ // it calls ExpandOne(), which brings us back here. In this case, always
+ // call the machine specific expansion function, if possible. Otherwise,
+ // return FAIL.
+ if (recursive) {
#ifdef SPECIAL_WILDCHAR
- { return os_expand_wildcards(num_pat, pat, num_file, file, flags); }
+ return os_expand_wildcards(num_pat, pat, num_file, file, flags);
#else
- { return FAIL; }
+ return FAIL;
#endif
+ }
#ifdef SPECIAL_WILDCHAR
- /*
- * If there are any special wildcard characters which we cannot handle
- * here, call machine specific function for all the expansion. This
- * avoids starting the shell for each argument separately.
- * For `=expr` do use the internal function.
- */
+ // If there are any special wildcard characters which we cannot handle
+ // here, call machine specific function for all the expansion. This
+ // avoids starting the shell for each argument separately.
+ // For `=expr` do use the internal function.
for (int i = 0; i < num_pat; i++) {
- if (has_special_wildchar((char_u *)pat[i])
- && !(vim_backtick((char_u *)pat[i]) && pat[i][1] == '=')) {
+ if (has_special_wildchar(pat[i])
+ && !(vim_backtick(pat[i]) && pat[i][1] == '=')) {
return os_expand_wildcards(num_pat, pat, num_file, file, flags);
}
}
@@ -1251,20 +1241,18 @@ int gen_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, i
recursive = true;
- /*
- * The matching file names are stored in a growarray. Init it empty.
- */
- ga_init(&ga, (int)sizeof(char_u *), 30);
+ // The matching file names are stored in a growarray. Init it empty.
+ ga_init(&ga, (int)sizeof(char *), 30);
for (int i = 0; i < num_pat && !got_int; i++) {
add_pat = -1;
- p = (char_u *)pat[i];
+ p = pat[i];
if (vim_backtick(p)) {
add_pat = expand_backtick(&ga, p, flags);
if (add_pat == -1) {
recursive = false;
- FreeWild(ga.ga_len, ga.ga_data);
+ ga_clear_strings(&ga);
*num_file = 0;
*file = NULL;
return FAIL;
@@ -1274,41 +1262,38 @@ int gen_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, i
if ((has_env_var(p) && !(flags & EW_NOTENV)) || *p == '~') {
p = expand_env_save_opt(p, true);
if (p == NULL) {
- p = (char_u *)pat[i];
- }
+ p = pat[i];
+ } else {
#ifdef UNIX
- /*
- * On Unix, if expand_env() can't expand an environment
- * variable, use the shell to do that. Discard previously
- * found file names and start all over again.
- */
- else if (has_env_var(p) || *p == '~') {
- xfree(p);
- ga_clear_strings(&ga);
- i = os_expand_wildcards(num_pat, pat, num_file, file,
- flags | EW_KEEPDOLLAR);
- recursive = false;
- return i;
- }
+ // On Unix, if expand_env() can't expand an environment
+ // variable, use the shell to do that. Discard previously
+ // found file names and start all over again.
+ if (has_env_var(p) || *p == '~') {
+ xfree(p);
+ ga_clear_strings(&ga);
+ i = os_expand_wildcards(num_pat, pat, num_file, file,
+ flags | EW_KEEPDOLLAR);
+ recursive = false;
+ return i;
+ }
#endif
+ }
}
- /*
- * If there are wildcards: Expand file names and add each match to
- * the list. If there is no match, and EW_NOTFOUND is given, add
- * the pattern.
- * If there are no wildcards: Add the file name if it exists or
- * when EW_NOTFOUND is given.
- */
- if (path_has_exp_wildcard(p)) {
+ // If there are wildcards or case-insensitive expansion is
+ // required: Expand file names and add each match to the list. If
+ // there is no match, and EW_NOTFOUND is given, add the pattern.
+ // Otherwise: Add the file name if it exists or when EW_NOTFOUND is
+ // given.
+ if (path_has_exp_wildcard(p) || (flags & EW_ICASE)) {
if ((flags & EW_PATH)
&& !path_is_absolute(p)
&& !(p[0] == '.'
&& (vim_ispathsep(p[1])
|| (p[1] == '.'
&& vim_ispathsep(p[2]))))) {
- /* :find completion where 'path' is used.
- * Recursiveness is OK here. */
+ // :find completion where 'path' is used.
+ // Recursiveness is OK here.
recursive = false;
add_pat = expand_in_path(&ga, p, flags);
recursive = true;
@@ -1322,10 +1307,10 @@ int gen_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, i
}
if (add_pat == -1 || (add_pat == 0 && (flags & EW_NOTFOUND))) {
- char_u *t = backslash_halve_save(p);
+ char *t = backslash_halve_save(p);
- /* When EW_NOTFOUND is used, always add files and dirs. Makes
- * "vim c:/" work. */
+ // When EW_NOTFOUND is used, always add files and dirs. Makes
+ // "vim c:/" work.
if (flags & EW_NOTFOUND) {
addfile(&ga, t, flags | EW_DIR | EW_FILE);
} else {
@@ -1340,7 +1325,7 @@ int gen_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, i
if (did_expand_in_path && !GA_EMPTY(&ga) && (flags & EW_PATH)) {
uniquefy_paths(&ga, p);
}
- if (p != (char_u *)pat[i]) {
+ if (p != pat[i]) {
xfree(p);
}
}
@@ -1365,12 +1350,10 @@ void FreeWild(int count, char **files)
xfree(files);
}
-/*
- * Return TRUE if we can expand this backtick thing here.
- */
-static int vim_backtick(char_u *p)
+/// @return true if we can expand this backtick thing here.
+static int vim_backtick(char *p)
{
- return *p == '`' && *(p + 1) != NUL && *(p + STRLEN(p) - 1) == '`';
+ return *p == '`' && *(p + 1) != NUL && *(p + strlen(p) - 1) == '`';
}
/// Expand an item in `backticks` by executing it as a command.
@@ -1378,20 +1361,19 @@ static int vim_backtick(char_u *p)
/// Returns number of file names found, -1 if an error is encountered.
///
/// @param flags EW_* flags
-static int expand_backtick(garray_T *gap, char_u *pat, int flags)
+static int expand_backtick(garray_T *gap, char *pat, int flags)
{
char *p;
char *buffer;
int cnt = 0;
// Create the command: lop off the backticks.
- char *cmd = (char *)vim_strnsave(pat + 1, STRLEN(pat) - 2);
+ char *cmd = xstrnsave(pat + 1, strlen(pat) - 2);
if (*cmd == '=') { // `={expr}`: Expand expression
buffer = eval_to_string(cmd + 1, &p, true);
} else {
- buffer = (char *)get_cmd_output((char_u *)cmd, NULL, (flags & EW_SILENT) ? kShellOptSilent : 0,
- NULL);
+ buffer = get_cmd_output(cmd, NULL, (flags & EW_SILENT) ? kShellOptSilent : 0, NULL);
}
xfree(cmd);
if (buffer == NULL) {
@@ -1409,7 +1391,7 @@ static int expand_backtick(garray_T *gap, char_u *pat, int flags)
if (p > cmd) {
char i = *p;
*p = NUL;
- addfile(gap, (char_u *)cmd, flags);
+ addfile(gap, cmd, flags);
*p = i;
cnt++;
}
@@ -1432,23 +1414,23 @@ static int expand_backtick(garray_T *gap, char_u *pat, int flags)
/// backslash twice.
/// When 'shellslash' set do it the other way around.
/// When the path looks like a URL leave it unmodified.
-void slash_adjust(char_u *p)
+void slash_adjust(char *p)
{
- if (path_with_url((const char *)p)) {
+ if (path_with_url(p)) {
return;
}
if (*p == '`') {
// don't replace backslash in backtick quoted strings
- const size_t len = STRLEN(p);
+ const size_t len = strlen(p);
if (len > 2 && *(p + len - 1) == '`') {
return;
}
}
while (*p) {
- if (*p == (char_u)psepcN) {
- *p = (char_u)psepc;
+ if (*p == psepcN) {
+ *p = psepc;
}
MB_PTR_ADV(p);
}
@@ -1464,7 +1446,7 @@ void slash_adjust(char_u *p)
/// EW_ALLLINKS add symlink also when the referred file does not exist
///
/// @param f filename
-void addfile(garray_T *gap, char_u *f, int flags)
+void addfile(garray_T *gap, char *f, int flags)
{
bool isdir;
FileInfo file_info;
@@ -1472,14 +1454,14 @@ void addfile(garray_T *gap, char_u *f, int flags)
// if the file/dir/link doesn't exist, may not add it
if (!(flags & EW_NOTFOUND)
&& ((flags & EW_ALLLINKS)
- ? !os_fileinfo_link((char *)f, &file_info)
+ ? !os_fileinfo_link(f, &file_info)
: !os_path_exists(f))) {
return;
}
#ifdef FNAME_ILLEGAL
// if the file/dir contains illegal characters, don't add it
- if (strpbrk((char *)f, FNAME_ILLEGAL) != NULL) {
+ if (strpbrk(f, FNAME_ILLEGAL) != NULL) {
return;
}
#endif
@@ -1492,35 +1474,31 @@ void addfile(garray_T *gap, char_u *f, int flags)
// If the file isn't executable, may not add it. Do accept directories.
// When invoked from expand_shellcmd() do not use $PATH.
if (!isdir && (flags & EW_EXEC)
- && !os_can_exe((char *)f, NULL, !(flags & EW_SHELLCMD))) {
+ && !os_can_exe(f, NULL, !(flags & EW_SHELLCMD))) {
return;
}
- char_u *p = xmalloc(STRLEN(f) + 1 + isdir);
+ char *p = xmalloc(strlen(f) + 1 + isdir);
STRCPY(p, f);
#ifdef BACKSLASH_IN_FILENAME
slash_adjust(p);
#endif
- /*
- * Append a slash or backslash after directory names if none is present.
- */
+ // Append a slash or backslash after directory names if none is present.
if (isdir && (flags & EW_ADDSLASH)) {
- add_pathsep((char *)p);
+ add_pathsep(p);
}
- GA_APPEND(char_u *, gap, p);
+ GA_APPEND(char *, gap, p);
}
-/*
- * Converts a file name into a canonical form. It simplifies a file name into
- * its simplest form by stripping out unneeded components, if any. The
- * resulting file name is simplified in place and will either be the same
- * length as that supplied, or shorter.
- */
-void simplify_filename(char_u *filename)
+// Converts a file name into a canonical form. It simplifies a file name into
+// its simplest form by stripping out unneeded components, if any. The
+// resulting file name is simplified in place and will either be the same
+// length as that supplied, or shorter.
+void simplify_filename(char *filename)
{
int components = 0;
- char_u *p, *tail, *start;
+ char *p, *tail, *start;
bool stripping_disabled = false;
bool relative = true;
@@ -1540,8 +1518,8 @@ void simplify_filename(char_u *filename)
start = p; // remember start after "c:/" or "/" or "///"
do {
- /* At this point "p" is pointing to the char following a single "/"
- * or "p" is at the "start" of the (absolute or relative) path name. */
+ // At this point "p" is pointing to the char following a single "/"
+ // or "p" is at the "start" of the (absolute or relative) path name.
if (vim_ispathsep(*p)) {
STRMOVE(p, p + 1); // remove duplicate "/"
} else if (p[0] == '.'
@@ -1549,10 +1527,10 @@ void simplify_filename(char_u *filename)
if (p == start && relative) {
p += 1 + (p[1] != NUL); // keep single "." or leading "./"
} else {
- /* Strip "./" or ".///". If we are at the end of the file name
- * and there is no trailing path separator, either strip "/." if
- * we are after "start", or strip "." if we are at the beginning
- * of an absolute path name . */
+ // Strip "./" or ".///". If we are at the end of the file name
+ // and there is no trailing path separator, either strip "/." if
+ // we are after "start", or strip "." if we are at the beginning
+ // of an absolute path name.
tail = p + 1;
if (p[1] != NUL) {
while (vim_ispathsep(*tail)) {
@@ -1573,86 +1551,86 @@ void simplify_filename(char_u *filename)
if (components > 0) { // strip one preceding component
bool do_strip = false;
- char_u saved_char;
+ char saved_char;
// Don't strip for an erroneous file name.
if (!stripping_disabled) {
- /* If the preceding component does not exist in the file
- * system, we strip it. On Unix, we don't accept a symbolic
- * link that refers to a non-existent file. */
+ // If the preceding component does not exist in the file
+ // system, we strip it. On Unix, we don't accept a symbolic
+ // link that refers to a non-existent file.
saved_char = p[-1];
p[-1] = NUL;
FileInfo file_info;
- if (!os_fileinfo_link((char *)filename, &file_info)) {
+ if (!os_fileinfo_link(filename, &file_info)) {
do_strip = true;
}
p[-1] = saved_char;
p--;
// Skip back to after previous '/'.
- while (p > start && !after_pathsep((char *)start, (char *)p)) {
+ while (p > start && !after_pathsep(start, p)) {
MB_PTR_BACK(start, p);
}
if (!do_strip) {
- /* If the component exists in the file system, check
- * that stripping it won't change the meaning of the
- * file name. First get information about the
- * unstripped file name. This may fail if the component
- * to strip is not a searchable directory (but a regular
- * file, for instance), since the trailing "/.." cannot
- * be applied then. We don't strip it then since we
- * don't want to replace an erroneous file name by
- * a valid one, and we disable stripping of later
- * components. */
+ // If the component exists in the file system, check
+ // that stripping it won't change the meaning of the
+ // file name. First get information about the
+ // unstripped file name. This may fail if the component
+ // to strip is not a searchable directory (but a regular
+ // file, for instance), since the trailing "/.." cannot
+ // be applied then. We don't strip it then since we
+ // don't want to replace an erroneous file name by
+ // a valid one, and we disable stripping of later
+ // components.
saved_char = *tail;
*tail = NUL;
- if (os_fileinfo((char *)filename, &file_info)) {
+ if (os_fileinfo(filename, &file_info)) {
do_strip = true;
} else {
stripping_disabled = true;
}
*tail = saved_char;
if (do_strip) {
- /* The check for the unstripped file name
- * above works also for a symbolic link pointing to
- * a searchable directory. But then the parent of
- * the directory pointed to by the link must be the
- * same as the stripped file name. (The latter
- * exists in the file system since it is the
- * component's parent directory.) */
+ // The check for the unstripped file name
+ // above works also for a symbolic link pointing to
+ // a searchable directory. But then the parent of
+ // the directory pointed to by the link must be the
+ // same as the stripped file name. (The latter
+ // exists in the file system since it is the
+ // component's parent directory.)
FileInfo new_file_info;
if (p == start && relative) {
os_fileinfo(".", &new_file_info);
} else {
saved_char = *p;
*p = NUL;
- os_fileinfo((char *)filename, &new_file_info);
+ os_fileinfo(filename, &new_file_info);
*p = saved_char;
}
if (!os_fileinfo_id_equal(&file_info, &new_file_info)) {
do_strip = false;
- /* We don't disable stripping of later
- * components since the unstripped path name is
- * still valid. */
+ // We don't disable stripping of later
+ // components since the unstripped path name is
+ // still valid.
}
}
}
}
if (!do_strip) {
- /* Skip the ".." or "../" and reset the counter for the
- * components that might be stripped later on. */
+ // Skip the ".." or "../" and reset the counter for the
+ // components that might be stripped later on.
p = tail;
components = 0;
} else {
- /* Strip previous component. If the result would get empty
- * and there is no trailing path separator, leave a single
- * "." instead. If we are at the end of the file name and
- * there is no trailing path separator and a preceding
- * component is left after stripping, strip its trailing
- * path separator as well. */
+ // Strip previous component. If the result would get empty
+ // and there is no trailing path separator, leave a single
+ // "." instead. If we are at the end of the file name and
+ // there is no trailing path separator and a preceding
+ // component is left after stripping, strip its trailing
+ // path separator as well.
if (p == start && relative && tail[-1] == '.') {
*p++ = '.';
*p = NUL;
@@ -1676,7 +1654,7 @@ void simplify_filename(char_u *filename)
}
} else {
components++; // Simple path component.
- p = (char_u *)path_next_component((const char *)p);
+ p = (char *)path_next_component(p);
}
} while (*p != NUL);
}
@@ -1684,7 +1662,7 @@ 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 = eval_to_string_safe((char *)curbuf->b_p_inex, NULL,
+ char *res = eval_to_string_safe(curbuf->b_p_inex, NULL,
was_set_insecurely(curwin, "includeexpr", OPT_LOCAL));
set_vim_var_string(VV_FNAME, NULL, 0);
return res;
@@ -1694,20 +1672,20 @@ static char *eval_includeexpr(const char *const ptr, const size_t len)
/// Otherwise like file_name_at_cursor().
///
/// @param rel_fname file we are searching relative to
-char_u *find_file_name_in_path(char_u *ptr, size_t len, int options, long count, char_u *rel_fname)
+char *find_file_name_in_path(char *ptr, size_t len, int options, long count, char *rel_fname)
{
- char_u *file_name;
- char_u *tofree = NULL;
+ char *file_name;
+ char *tofree = NULL;
if (len == 0) {
return NULL;
}
if ((options & FNAME_INCL) && *curbuf->b_p_inex != NUL) {
- tofree = (char_u *)eval_includeexpr((char *)ptr, len);
+ tofree = eval_includeexpr(ptr, len);
if (tofree != NULL) {
ptr = tofree;
- len = STRLEN(ptr);
+ len = strlen(ptr);
}
}
@@ -1715,35 +1693,34 @@ char_u *find_file_name_in_path(char_u *ptr, size_t len, int options, long count,
file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS, true,
rel_fname);
- /*
- * If the file could not be found in a normal way, try applying
- * 'includeexpr' (unless done already).
- */
+ // If the file could not be found in a normal way, try applying
+ // 'includeexpr' (unless done already).
if (file_name == NULL
&& !(options & FNAME_INCL) && *curbuf->b_p_inex != NUL) {
- tofree = (char_u *)eval_includeexpr((char *)ptr, len);
+ tofree = eval_includeexpr(ptr, len);
if (tofree != NULL) {
ptr = tofree;
- len = STRLEN(ptr);
+ len = strlen(ptr);
file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS,
- TRUE, rel_fname);
+ true, rel_fname);
}
}
if (file_name == NULL && (options & FNAME_MESS)) {
- char_u c = ptr[len];
+ char c = ptr[len];
ptr[len] = NUL;
semsg(_("E447: Can't find file \"%s\" in path"), ptr);
ptr[len] = c;
}
- /* Repeat finding the file "count" times. This matters when it
- * appears several times in the path. */
+ // Repeat finding the file "count" times. This matters when it
+ // appears several times in the path.
while (file_name != NULL && --count > 0) {
xfree(file_name);
- file_name = find_file_in_path(ptr, len, options, FALSE, rel_fname);
+ file_name =
+ find_file_in_path(ptr, len, options, false, rel_fname);
}
} else {
- file_name = vim_strnsave(ptr, len);
+ file_name = xstrnsave(ptr, len);
}
xfree(tofree);
@@ -1751,12 +1728,26 @@ char_u *find_file_name_in_path(char_u *ptr, size_t len, int options, long count,
return file_name;
}
-// Check if the "://" of a URL is at the pointer, return URL_SLASH.
+/// Checks for a Windows drive letter ("C:/") at the start of the path.
+///
+/// @see https://url.spec.whatwg.org/#start-with-a-windows-drive-letter
+bool path_has_drive_letter(const char *p)
+ FUNC_ATTR_NONNULL_ALL
+{
+ return strlen(p) >= 2
+ && ASCII_ISALPHA(p[0])
+ && (p[1] == ':' || p[1] == '|')
+ && (strlen(p) == 2 || ((p[2] == '/') | (p[2] == '\\') | (p[2] == '?') | (p[2] == '#')));
+}
+
+// Check if the ":/" of a URL is at the pointer, return URL_SLASH.
// Also check for ":\\", which MS Internet Explorer accepts, return
// URL_BACKSLASH.
int path_is_url(const char *p)
{
- if (strncmp(p, "://", 3) == 0) {
+ // In the spec ':' is enough to recognize a scheme
+ // https://url.spec.whatwg.org/#scheme-state
+ if (strncmp(p, ":/", 2) == 0) {
return URL_SLASH;
} else if (strncmp(p, ":\\\\", 3) == 0) {
return URL_BACKSLASH;
@@ -1777,19 +1768,23 @@ int path_with_url(const char *fname)
// non-URL text.
// first character must be alpha
- if (!isalpha(*fname)) {
+ if (!ASCII_ISALPHA(*fname)) {
+ return 0;
+ }
+
+ if (path_has_drive_letter(fname)) {
return 0;
}
// check body: alpha or dash
- for (p = fname + 1; (isalpha(*p) || (*p == '-')); p++) {}
+ for (p = fname + 1; (ASCII_ISALPHA(*p) || (*p == '-')); p++) {}
// check last char is not a dash
if (p[-1] == '-') {
return 0;
}
- // "://" or ":\\" must follow
+ // ":/" or ":\\" must follow
return path_is_url(p);
}
@@ -1802,12 +1797,10 @@ bool path_with_extension(const char *path, const char *extension)
return strcmp(last_dot + 1, extension) == 0;
}
-/*
- * Return TRUE if "name" is a full (absolute) path name or URL.
- */
-bool vim_isAbsName(char_u *name)
+/// Return true if "name" is a full (absolute) path name or URL.
+bool vim_isAbsName(char *name)
{
- return path_with_url((char *)name) != 0 || path_is_absolute(name);
+ return path_with_url(name) != 0 || path_is_absolute(name);
}
/// Save absolute file name to "buf[len]".
@@ -1830,8 +1823,8 @@ int vim_FullName(const char *fname, char *buf, size_t len, bool force)
if (strlen(fname) > (len - 1)) {
xstrlcpy(buf, fname, len); // truncate
-#ifdef WIN32
- slash_adjust((char_u *)buf);
+#ifdef MSWIN
+ slash_adjust(buf);
#endif
return FAIL;
}
@@ -1841,12 +1834,12 @@ int vim_FullName(const char *fname, char *buf, size_t len, bool force)
return OK;
}
- int rv = path_to_absolute((char_u *)fname, (char_u *)buf, len, force);
+ int rv = path_to_absolute(fname, buf, len, force);
if (rv == FAIL) {
xstrlcpy(buf, fname, len); // something failed; use the filename
}
-#ifdef WIN32
- slash_adjust((char_u *)buf);
+#ifdef MSWIN
+ slash_adjust(buf);
#endif
return rv;
}
@@ -1866,7 +1859,7 @@ char *fix_fname(const char *fname)
#ifdef UNIX
return FullName_save(fname, true);
#else
- if (!vim_isAbsName((char_u *)fname)
+ if (!vim_isAbsName((char *)fname)
|| strstr(fname, "..") != NULL
|| strstr(fname, "//") != NULL
# ifdef BACKSLASH_IN_FILENAME
@@ -1879,7 +1872,7 @@ char *fix_fname(const char *fname)
fname = xstrdup(fname);
# ifdef USE_FNAME_CASE
- path_fix_case(fname); // set correct case for file name
+ path_fix_case((char *)fname); // set correct case for file name
# endif
return (char *)fname;
@@ -1900,7 +1893,7 @@ void path_fix_case(char *name)
}
// Open the directory where the file is located.
- char *slash = (char *)STRRCHR(name, '/');
+ char *slash = strrchr(name, '/');
char *tail;
Directory dir;
bool ok;
@@ -1922,13 +1915,13 @@ void path_fix_case(char *name)
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)) {
- char_u newname[MAXPATHL + 1];
+ if (STRICMP(tail, entry) == 0 && strlen(tail) == strlen(entry)) {
+ char newname[MAXPATHL + 1];
// Verify the inode is equal.
- STRLCPY(newname, name, MAXPATHL + 1);
- STRLCPY(newname + (tail - name), entry,
- MAXPATHL - (tail - name) + 1);
+ xstrlcpy(newname, name, MAXPATHL + 1);
+ xstrlcpy(newname + (tail - name), entry,
+ (size_t)(MAXPATHL - (tail - name) + 1));
FileInfo file_info_new;
if (os_fileinfo_link((char *)newname, &file_info_new)
&& os_fileinfo_id_equal(&file_info, &file_info_new)) {
@@ -1941,22 +1934,18 @@ void path_fix_case(char *name)
os_closedir(&dir);
}
-/*
- * Return TRUE if "p" points to just after a path separator.
- * Takes care of multi-byte characters.
- * "b" must point to the start of the file name
- */
+/// Return true if "p" points to just after a path separator.
+/// Takes care of multi-byte characters.
+/// "b" must point to the start of the file name
int after_pathsep(const char *b, const char *p)
{
return p > b && vim_ispathsep(p[-1])
- && utf_head_off((char_u *)b, (char_u *)p - 1) == 0;
+ && utf_head_off(b, p - 1) == 0;
}
-/*
- * Return TRUE if file names "f1" and "f2" are in the same directory.
- * "f1" may be a short name, "f2" must be a full path.
- */
-bool same_directory(char_u *f1, char_u *f2)
+/// Return true if file names "f1" and "f2" are in the same directory.
+/// "f1" may be a short name, "f2" must be a full path.
+bool same_directory(char *f1, char *f2)
{
char ffname[MAXPATHL];
char *t1;
@@ -1967,18 +1956,16 @@ bool same_directory(char_u *f1, char_u *f2)
return false;
}
- (void)vim_FullName((char *)f1, (char *)ffname, MAXPATHL, FALSE);
+ (void)vim_FullName(f1, (char *)ffname, MAXPATHL, false);
t1 = path_tail_with_sep(ffname);
- t2 = path_tail_with_sep((char *)f2);
- return t1 - ffname == (char_u *)t2 - f2
- && pathcmp((char *)ffname, (char *)f2, (int)(t1 - ffname)) == 0;
+ t2 = path_tail_with_sep(f2);
+ return t1 - ffname == t2 - f2
+ && pathcmp((char *)ffname, f2, (int)(t1 - ffname)) == 0;
}
-/*
- * Compare path "p[]" to "q[]".
- * If "maxlen" >= 0 compare "p[maxlen]" to "q[maxlen]"
- * Return value like strcmp(p, q), but consider path separators.
- */
+// Compare path "p[]" to "q[]".
+// If "maxlen" >= 0 compare "p[maxlen]" to "q[maxlen]"
+// Return value like strcmp(p, q), but consider path separators.
int pathcmp(const char *p, const char *q, int maxlen)
{
int i, j;
@@ -2057,10 +2044,10 @@ int pathcmp(const char *p, const char *q, int maxlen)
/// - Pointer into `full_path` if shortened.
/// - `full_path` unchanged if no shorter name is possible.
/// - NULL if `full_path` is NULL.
-char_u *path_try_shorten_fname(char_u *full_path)
+char *path_try_shorten_fname(char *full_path)
{
- char_u *dirname = xmalloc(MAXPATHL);
- char_u *p = full_path;
+ char *dirname = xmalloc(MAXPATHL);
+ char *p = full_path;
if (os_dirname(dirname, MAXPATHL) == OK) {
p = path_shorten_fname(full_path, dirname);
@@ -2079,14 +2066,14 @@ char_u *path_try_shorten_fname(char_u *full_path)
/// @return
/// - Pointer into `full_path` if shortened.
/// - NULL if no shorter name is possible.
-char_u *path_shorten_fname(char_u *full_path, char_u *dir_name)
+char *path_shorten_fname(char *full_path, char *dir_name)
{
if (full_path == NULL) {
return NULL;
}
assert(dir_name != NULL);
- size_t len = STRLEN(dir_name);
+ size_t len = strlen(dir_name);
// If dir_name is a path head, full_path can always be made relative.
if (len == (size_t)path_head_length() && is_path_head(dir_name)) {
@@ -2095,11 +2082,11 @@ 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 (path_fnamencmp(dir_name, full_path, len) != 0) {
return NULL;
}
- char_u *p = full_path + len;
+ char *p = full_path + len;
// If *p is not pointing to a path separator, this means that full_path's
// last directory name is longer than *dir_name's last directory, so they
@@ -2125,20 +2112,25 @@ char_u *path_shorten_fname(char_u *full_path, char_u *dir_name)
/// If FAIL is returned, *num_file and *file are either
/// unchanged or *num_file is set to 0 and *file is set
/// to NULL or points to "".
-int expand_wildcards_eval(char_u **pat, int *num_file, char ***file, int flags)
+int expand_wildcards_eval(char **pat, int *num_file, char ***file, int flags)
{
int ret = FAIL;
- char_u *eval_pat = NULL;
- char *exp_pat = (char *)(*pat);
+ char *eval_pat = NULL;
+ char *exp_pat = *pat;
char *ignored_msg;
size_t usedlen;
+ const bool is_cur_alt_file = *exp_pat == '%' || *exp_pat == '#';
+ bool star_follows = false;
- if (*exp_pat == '%' || *exp_pat == '#' || *exp_pat == '<') {
+ if (is_cur_alt_file || *exp_pat == '<') {
emsg_off++;
- eval_pat = eval_vars((char_u *)exp_pat, (char_u *)exp_pat, &usedlen, NULL, &ignored_msg, NULL);
+ eval_pat = eval_vars(exp_pat, exp_pat, &usedlen, NULL, &ignored_msg,
+ NULL,
+ true);
emsg_off--;
if (eval_pat != NULL) {
- exp_pat = (char *)concat_str(eval_pat, (char_u *)exp_pat + usedlen);
+ star_follows = strcmp(exp_pat + usedlen, "*") == 0;
+ exp_pat = concat_str(eval_pat, exp_pat + usedlen);
}
}
@@ -2147,6 +2139,16 @@ int expand_wildcards_eval(char_u **pat, int *num_file, char ***file, int flags)
}
if (eval_pat != NULL) {
+ if (*num_file == 0 && is_cur_alt_file && star_follows) {
+ // Expanding "%" or "#" and the file does not exist: Add the
+ // pattern anyway (without the star) so that this works for remote
+ // files and non-file buffer names.
+ *file = xmalloc(sizeof(char *));
+ **file = eval_pat;
+ eval_pat = NULL;
+ *num_file = 1;
+ ret = OK;
+ }
xfree(exp_pat);
xfree(eval_pat);
}
@@ -2172,7 +2174,7 @@ int expand_wildcards(int num_pat, char **pat, int *num_files, char ***files, int
{
int retval;
int i, j;
- char_u *p;
+ char *p;
int non_suf_match; // number without matching suffix
retval = gen_expand_wildcards(num_pat, pat, num_files, files, flags);
@@ -2182,19 +2184,17 @@ int expand_wildcards(int num_pat, char **pat, int *num_files, char ***files, int
return retval;
}
- /*
- * Remove names that match 'wildignore'.
- */
+ // Remove names that match 'wildignore'.
if (*p_wig) {
- char_u *ffname;
+ char *ffname;
// check all files in (*files)[]
assert(*num_files == 0 || *files != NULL);
for (i = 0; i < *num_files; i++) {
- ffname = (char_u *)FullName_save((*files)[i], false);
+ ffname = FullName_save((*files)[i], false);
assert((*files)[i] != NULL);
assert(ffname != NULL);
- if (match_file_list(p_wig, (char_u *)(*files)[i], ffname)) {
+ if (match_file_list(p_wig, (*files)[i], ffname)) {
// remove this matching file from the list
xfree((*files)[i]);
for (j = i; j + 1 < *num_files; j++) {
@@ -2213,13 +2213,13 @@ int expand_wildcards(int num_pat, char **pat, int *num_files, char ***files, int
if (*num_files > 1 && !got_int) {
non_suf_match = 0;
for (i = 0; i < *num_files; i++) {
- if (!match_suffix((char_u *)(*files)[i])) {
+ if (!match_suffix((*files)[i])) {
// Move the name without matching suffix to the front of the list.
- p = (char_u *)(*files)[i];
+ p = (*files)[i];
for (j = i; j > non_suf_match; j--) {
(*files)[j] = (*files)[j - 1];
}
- (*files)[non_suf_match++] = (char *)p;
+ (*files)[non_suf_match++] = p;
}
}
}
@@ -2233,29 +2233,27 @@ int expand_wildcards(int num_pat, char **pat, int *num_files, char ***files, int
return retval;
}
-/*
- * Return TRUE if "fname" matches with an entry in 'suffixes'.
- */
-int match_suffix(char_u *fname)
+/// @return true if "fname" matches with an entry in 'suffixes'.
+int match_suffix(char *fname)
{
#define MAXSUFLEN 30 // maximum length of a file suffix
- char_u suf_buf[MAXSUFLEN];
+ char suf_buf[MAXSUFLEN];
- size_t fnamelen = STRLEN(fname);
+ size_t fnamelen = strlen(fname);
size_t setsuflen = 0;
- for (char_u *setsuf = p_su; *setsuf;) {
- setsuflen = copy_option_part((char **)&setsuf, (char *)suf_buf, MAXSUFLEN, ".,");
+ for (char *setsuf = p_su; *setsuf;) {
+ setsuflen = copy_option_part(&setsuf, suf_buf, MAXSUFLEN, ".,");
if (setsuflen == 0) {
- char_u *tail = (char_u *)path_tail((char *)fname);
+ char *tail = path_tail(fname);
// empty entry: match name without a '.'
- if (vim_strchr((char *)tail, '.') == NULL) {
+ if (vim_strchr(tail, '.') == NULL) {
setsuflen = 1;
break;
}
} else {
if (fnamelen >= setsuflen
- && FNAMENCMP(suf_buf, fname + fnamelen - setsuflen, setsuflen) == 0) {
+ && path_fnamencmp(suf_buf, fname + fnamelen - setsuflen, setsuflen) == 0) {
break;
}
setsuflen = 0;
@@ -2273,14 +2271,14 @@ int path_full_dir_name(char *directory, char *buffer, size_t len)
int SUCCESS = 0;
int retval = OK;
- if (STRLEN(directory) == 0) {
- return os_dirname((char_u *)buffer, len);
+ if (strlen(directory) == 0) {
+ return os_dirname(buffer, len);
}
char old_dir[MAXPATHL];
// Get current directory name.
- if (os_dirname((char_u *)old_dir, MAXPATHL) == FAIL) {
+ if (os_dirname(old_dir, MAXPATHL) == FAIL) {
return FAIL;
}
@@ -2293,14 +2291,14 @@ int path_full_dir_name(char *directory, char *buffer, size_t len)
// Path does not exist (yet). For a full path fail,
// will use the path as-is. For a relative path use
// the current directory and append the file name.
- if (path_is_absolute((const char_u *)directory)) {
+ if (path_is_absolute(directory)) {
// Do not return immediately since we may be in the wrong directory.
retval = FAIL;
} else {
xstrlcpy(buffer, old_dir, len);
append_path(buffer, directory, len);
}
- } else if (os_dirname((char_u *)buffer, len) == FAIL) {
+ } else if (os_dirname(buffer, len) == FAIL) {
// Do not return immediately since we are in the wrong directory.
retval = FAIL;
}
@@ -2354,9 +2352,9 @@ int append_path(char *path, const char *to_append, size_t max_len)
/// @param force also expand when "fname" is already absolute.
///
/// @return FAIL for failure, OK for success.
-static int path_to_absolute(const char_u *fname, char_u *buf, size_t len, int force)
+static int path_to_absolute(const char *fname, char *buf, size_t len, int force)
{
- char_u *p;
+ char *p;
*buf = NUL;
char *relative_directory = xmalloc(len);
@@ -2364,49 +2362,42 @@ static int path_to_absolute(const char_u *fname, char_u *buf, size_t len, int fo
// expand it if forced or not an absolute path
if (force || !path_is_absolute(fname)) {
- p = STRRCHR(fname, '/');
-#ifdef WIN32
+ p = strrchr(fname, '/');
+#ifdef MSWIN
if (p == NULL) {
- p = STRRCHR(fname, '\\');
+ p = strrchr(fname, '\\');
}
#endif
if (p != NULL) {
- // relative to root
- if (p == fname) {
- // only one path component
- relative_directory[0] = PATHSEP;
- relative_directory[1] = NUL;
- } else {
- assert(p >= fname);
- memcpy(relative_directory, fname, (size_t)(p - fname));
- relative_directory[p - fname] = NUL;
- }
- end_of_path = (char *)(p + 1);
+ assert(p >= fname);
+ memcpy(relative_directory, fname, (size_t)(p - fname + 1));
+ relative_directory[p - fname + 1] = NUL;
+ end_of_path = p + 1;
} else {
relative_directory[0] = NUL;
end_of_path = (char *)fname;
}
- if (FAIL == path_full_dir_name(relative_directory, (char *)buf, len)) {
+ if (FAIL == path_full_dir_name(relative_directory, buf, len)) {
xfree(relative_directory);
return FAIL;
}
}
xfree(relative_directory);
- return append_path((char *)buf, end_of_path, len);
+ return append_path(buf, end_of_path, len);
}
/// Check if file `fname` is a full (absolute) path.
///
-/// @return `TRUE` if "fname" is absolute.
-int path_is_absolute(const char_u *fname)
+/// @return `true` if "fname" is absolute.
+int path_is_absolute(const char *fname)
{
-#ifdef WIN32
+#ifdef MSWIN
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((uint8_t)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 '~'.
@@ -2426,11 +2417,11 @@ void path_guess_exepath(const char *argv0, char *buf, size_t bufsize)
{
const char *path = os_getenv("PATH");
- if (path == NULL || path_is_absolute((char_u *)argv0)) {
+ if (path == NULL || path_is_absolute(argv0)) {
xstrlcpy(buf, argv0, bufsize);
} else if (argv0[0] == '.' || strchr(argv0, PATHSEP)) {
// Relative to CWD.
- if (os_dirname((char_u *)buf, MAXPATHL) != OK) {
+ if (os_dirname(buf, MAXPATHL) != OK) {
buf[0] = NUL;
}
xstrlcat(buf, PATHSEPSTR, bufsize);
@@ -2448,11 +2439,11 @@ void path_guess_exepath(const char *argv0, char *buf, size_t bufsize)
if (dir_len + 1 > sizeof(NameBuff)) {
continue;
}
- STRLCPY(NameBuff, dir, dir_len + 1);
- STRLCAT(NameBuff, PATHSEPSTR, sizeof(NameBuff));
- STRLCAT(NameBuff, argv0, sizeof(NameBuff));
- if (os_can_exe((char *)NameBuff, NULL, false)) {
- xstrlcpy(buf, (char *)NameBuff, bufsize);
+ xstrlcpy(NameBuff, dir, dir_len + 1);
+ xstrlcat(NameBuff, PATHSEPSTR, sizeof(NameBuff));
+ xstrlcat(NameBuff, argv0, sizeof(NameBuff));
+ if (os_can_exe(NameBuff, NULL, false)) {
+ xstrlcpy(buf, NameBuff, bufsize);
return;
}
} while (iter != NULL);
diff --git a/src/nvim/path.h b/src/nvim/path.h
index 1006ef9ffb..c8d192dffe 100644
--- a/src/nvim/path.h
+++ b/src/nvim/path.h
@@ -18,8 +18,8 @@
#define EW_NOERROR 0x200 // no error for bad regexp
#define EW_NOTWILD 0x400 // add match with literal name if exists
#define EW_KEEPDOLLAR 0x800 // do not escape $, $var is expanded
-/* Note: mostly EW_NOTFOUND and EW_SILENT are mutually exclusive: EW_NOTFOUND
-* is used when executing commands and EW_SILENT for interactive expanding. */
+// Note: mostly EW_NOTFOUND and EW_SILENT are mutually exclusive: EW_NOTFOUND
+// is used when executing commands and EW_SILENT for interactive expanding.
#define EW_ALLLINKS 0x1000 // also links not pointing to existing file
#define EW_SHELLCMD 0x2000 // called from expand_shellcmd(), don't check
// if executable is in $PATH
@@ -39,4 +39,4 @@ typedef enum file_comparison {
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "path.h.generated.h"
#endif
-#endif
+#endif // NVIM_PATH_H
diff --git a/src/nvim/plines.c b/src/nvim/plines.c
index 70bdbd8b1d..5469d94800 100644
--- a/src/nvim/plines.c
+++ b/src/nvim/plines.c
@@ -10,25 +10,20 @@
#include <string.h>
#include "nvim/ascii.h"
-#include "nvim/buffer.h"
#include "nvim/charset.h"
-#include "nvim/cursor.h"
#include "nvim/decoration.h"
#include "nvim/diff.h"
#include "nvim/fold.h"
-#include "nvim/func_attr.h"
+#include "nvim/globals.h"
#include "nvim/indent.h"
-#include "nvim/main.h"
+#include "nvim/macros.h"
#include "nvim/mbyte.h"
#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/strings.h"
+#include "nvim/pos.h"
#include "nvim/vim.h"
-#include "nvim/window.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "plines.c.generated.h"
@@ -37,6 +32,9 @@
/// Functions calculating vertical size of text when displayed inside a window.
/// Calls horizontal size functions defined below.
+/// Return the number of window lines occupied by buffer line "lnum".
+/// Includes any filler lines.
+///
/// @param winheight when true limit to window height
int plines_win(win_T *wp, linenr_T lnum, bool winheight)
{
@@ -53,7 +51,7 @@ int plines_win(win_T *wp, linenr_T lnum, bool winheight)
/// @return Number of filler lines above lnum
int win_get_fill(win_T *wp, linenr_T lnum)
{
- int virt_lines = decor_virt_lines(wp, lnum, NULL);
+ int virt_lines = decor_virt_lines(wp, lnum, NULL, kNone);
// be quick when there are no filler lines
if (diffopt_filler()) {
@@ -71,6 +69,9 @@ bool win_may_fill(win_T *wp)
return (wp->w_p_diff && diffopt_filler()) || wp->w_buffer->b_virt_line_blocks;
}
+/// Return the number of window lines occupied by buffer line "lnum".
+/// Does not include filler lines.
+///
/// @param winheight when true limit to window height
int plines_win_nofill(win_T *wp, linenr_T lnum, bool winheight)
{
@@ -98,7 +99,7 @@ int plines_win_nofill(win_T *wp, linenr_T lnum, bool winheight)
/// "wp". Does not care about folding, 'wrap' or 'diff'.
int plines_win_nofold(win_T *wp, linenr_T lnum)
{
- char_u *s;
+ char *s;
unsigned int col;
int width;
@@ -106,7 +107,7 @@ int plines_win_nofold(win_T *wp, linenr_T lnum)
if (*s == NUL) { // empty line
return 1;
}
- col = win_linetabsize(wp, s, MAXCOL);
+ col = win_linetabsize(wp, lnum, s, MAXCOL);
// If list mode is on, then the '$' at the end of the line may take up one
// extra column.
@@ -144,24 +145,28 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column)
return lines + 1;
}
- char_u *line = ml_get_buf(wp->w_buffer, lnum, false);
- char_u *s = line;
+ char *line = ml_get_buf(wp->w_buffer, lnum, false);
colnr_T col = 0;
- while (*s != NUL && --column >= 0) {
- col += win_lbr_chartabsize(wp, line, s, col, NULL);
- MB_PTR_ADV(s);
+ chartabsize_T cts;
+
+ init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
+ while (*cts.cts_ptr != NUL && --column >= 0) {
+ cts.cts_vcol += win_lbr_chartabsize(&cts, NULL);
+ MB_PTR_ADV(cts.cts_ptr);
}
- // If *s is a TAB, and the TAB is not displayed as ^I, and we're not in
- // MODE_INSERT state, then col must be adjusted so that it represents the
+ // If *cts.cts_ptr is a TAB, and the TAB is not displayed as ^I, and we're not
+ // in 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)
+ col = cts.cts_vcol;
+ if (*cts.cts_ptr == TAB && (State & MODE_NORMAL)
&& (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
- col += win_lbr_chartabsize(wp, line, s, col, NULL) - 1;
+ col += win_lbr_chartabsize(&cts, NULL) - 1;
}
+ clear_chartabsize_arg(&cts);
// Add column offset for 'number', 'relativenumber', 'foldcolumn', etc.
int width = wp->w_width_inner - win_col_off(wp);
@@ -223,14 +228,13 @@ int plines_m_win(win_T *wp, linenr_T first, linenr_T last)
/// @param col
///
/// @return Number of characters.
-int win_chartabsize(win_T *wp, char_u *p, colnr_T col)
+int win_chartabsize(win_T *wp, char *p, colnr_T col)
{
buf_T *buf = wp->w_buffer;
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((char *)p);
}
+ return ptr2cells(p);
}
/// Return the number of characters the string 's' will take on the screen,
@@ -239,26 +243,26 @@ int win_chartabsize(win_T *wp, char_u *p, colnr_T col)
/// @param s
///
/// @return Number of characters the string will take on the screen.
-int linetabsize(char_u *s)
+int linetabsize(char *s)
{
return linetabsize_col(0, s);
}
-/// Like linetabsize(), but starting at column "startcol".
+/// Like linetabsize(), but "s" starts at column "startcol".
///
/// @param startcol
/// @param s
///
/// @return Number of characters the string will take on the screen.
-int linetabsize_col(int startcol, char_u *s)
+int linetabsize_col(int startcol, char *s)
{
- colnr_T col = startcol;
- char_u *line = s; // pointer to start of line, for breakindent
-
- while (*s != NUL) {
- col += lbr_chartabsize_adv(line, &s, col);
+ chartabsize_T cts;
+ init_chartabsize_arg(&cts, curwin, 0, startcol, s, s);
+ while (*cts.cts_ptr != NUL) {
+ cts.cts_vcol += lbr_chartabsize_adv(&cts);
}
- return (int)col;
+ clear_chartabsize_arg(&cts);
+ return cts.cts_vcol;
}
/// Like linetabsize(), but for a given window instead of the current one.
@@ -268,19 +272,38 @@ int linetabsize_col(int startcol, char_u *s)
/// @param len
///
/// @return Number of characters the string will take on the screen.
-unsigned int win_linetabsize(win_T *wp, char_u *line, colnr_T len)
+unsigned int win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len)
{
- colnr_T col = 0;
-
- for (char_u *s = line;
- *s != NUL && (len == MAXCOL || s < line + len);
- MB_PTR_ADV(s)) {
- col += win_lbr_chartabsize(wp, line, s, col, NULL);
+ chartabsize_T cts;
+ init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
+ for (; *cts.cts_ptr != NUL && (len == MAXCOL || cts.cts_ptr < line + len);
+ MB_PTR_ADV(cts.cts_ptr)) {
+ cts.cts_vcol += win_lbr_chartabsize(&cts, NULL);
}
+ clear_chartabsize_arg(&cts);
+ return (unsigned int)cts.cts_vcol;
+}
- return (unsigned int)col;
+/// Prepare the structure passed to chartabsize functions.
+///
+/// "line" is the start of the line, "ptr" is the first relevant character.
+/// When "lnum" is zero do not use text properties that insert text.
+void init_chartabsize_arg(chartabsize_T *cts, win_T *wp, linenr_T lnum FUNC_ATTR_UNUSED,
+ colnr_T col, char *line, char *ptr)
+{
+ cts->cts_win = wp;
+ cts->cts_vcol = col;
+ cts->cts_line = line;
+ cts->cts_ptr = ptr;
+ cts->cts_cur_text_width = 0;
+ // TODO(bfredl): actually lookup inline virtual text here
+ cts->cts_has_virt_text = false;
}
+/// Free any allocated item in "cts".
+void clear_chartabsize_arg(chartabsize_T *cts)
+{}
+
/// like win_chartabsize(), but also check for line breaks on the screen
///
/// @param line
@@ -288,16 +311,16 @@ unsigned int win_linetabsize(win_T *wp, char_u *line, colnr_T len)
/// @param col
///
/// @return The number of characters taken up on the screen.
-int lbr_chartabsize(char_u *line, unsigned char *s, colnr_T col)
+int lbr_chartabsize(chartabsize_T *cts)
{
if (!curwin->w_p_lbr && *get_showbreak_value(curwin) == NUL
- && !curwin->w_p_bri) {
+ && !curwin->w_p_bri && !cts->cts_has_virt_text) {
if (curwin->w_p_wrap) {
- return win_nolbr_chartabsize(curwin, s, col, NULL);
+ return win_nolbr_chartabsize(cts, NULL);
}
- return win_chartabsize(curwin, s, col);
+ return win_chartabsize(curwin, cts->cts_ptr, cts->cts_vcol);
}
- return win_lbr_chartabsize(curwin, line == NULL ? s: line, s, col, NULL);
+ return win_lbr_chartabsize(cts, NULL);
}
/// Call lbr_chartabsize() and advance the pointer.
@@ -307,12 +330,12 @@ int lbr_chartabsize(char_u *line, unsigned char *s, colnr_T col)
/// @param col
///
/// @return The number of characters take up on the screen.
-int lbr_chartabsize_adv(char_u *line, char_u **s, colnr_T col)
+int lbr_chartabsize_adv(chartabsize_T *cts)
{
int retval;
- retval = lbr_chartabsize(line, *s, col);
- MB_PTR_ADV(*s);
+ retval = lbr_chartabsize(cts);
+ MB_PTR_ADV(cts->cts_ptr);
return retval;
}
@@ -322,35 +345,44 @@ int lbr_chartabsize_adv(char_u *line, char_u **s, colnr_T col)
/// string at start of line. Warning: *headp is only set if it's a non-zero
/// value, init to 0 before calling.
///
-/// @param wp
-/// @param line
-/// @param s
-/// @param col
+/// @param cts
/// @param headp
///
/// @return The number of characters taken up on the screen.
-int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *headp)
+int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
{
+ win_T *wp = cts->cts_win;
+ char *line = cts->cts_line; // start of the line
+ char *s = cts->cts_ptr;
+ colnr_T vcol = cts->cts_vcol;
+
colnr_T col2;
- colnr_T col_adj = 0; // col + screen size of tab
+ colnr_T col_adj = 0; // vcol + screen size of tab
colnr_T colmax;
int added;
int mb_added = 0;
int numberextra;
- char_u *ps;
+ char *ps;
int n;
+ cts->cts_cur_text_width = 0;
+
// No 'linebreak', 'showbreak' and 'breakindent': return quickly.
- if (!wp->w_p_lbr && !wp->w_p_bri && *get_showbreak_value(wp) == NUL) {
+ if (!wp->w_p_lbr && !wp->w_p_bri && *get_showbreak_value(wp) == NUL
+ && !cts->cts_has_virt_text) {
if (wp->w_p_wrap) {
- return win_nolbr_chartabsize(wp, s, col, headp);
+ return win_nolbr_chartabsize(cts, headp);
}
- return win_chartabsize(wp, s, col);
+ return win_chartabsize(wp, s, vcol);
+ }
+
+ // First get normal size, without 'linebreak' or virtual text
+ int size = win_chartabsize(wp, s, vcol);
+ if (cts->cts_has_virt_text) {
+ // TODO(bfredl): inline virtual text
}
- // First get normal size, without 'linebreak'
- int size = win_chartabsize(wp, s, col);
- int c = *s;
+ int c = (uint8_t)(*s);
if (*s == TAB) {
col_adj = size - 1;
}
@@ -359,45 +391,45 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he
// needs a break here
if (wp->w_p_lbr
&& vim_isbreak(c)
- && !vim_isbreak((int)s[1])
+ && !vim_isbreak((uint8_t)s[1])
&& wp->w_p_wrap
&& (wp->w_width_inner != 0)) {
// Count all characters from first non-blank after a blank up to next
// non-blank after a blank.
numberextra = win_col_off(wp);
- col2 = col;
+ col2 = vcol;
colmax = (colnr_T)(wp->w_width_inner - numberextra - col_adj);
- if (col >= colmax) {
+ if (vcol >= colmax) {
colmax += col_adj;
n = colmax + win_col_off2(wp);
if (n > 0) {
- colmax += (((col - colmax) / n) + 1) * n - col_adj;
+ colmax += (((vcol - colmax) / n) + 1) * n - col_adj;
}
}
for (;;) {
ps = s;
MB_PTR_ADV(s);
- c = *s;
+ c = (uint8_t)(*s);
if (!(c != NUL
- && (vim_isbreak(c) || col2 == col || !vim_isbreak((int)(*ps))))) {
+ && (vim_isbreak(c) || col2 == vcol || !vim_isbreak((uint8_t)(*ps))))) {
break;
}
col2 += win_chartabsize(wp, s, col2);
if (col2 >= colmax) { // doesn't fit
- size = colmax - col + col_adj;
+ size = colmax - vcol + col_adj;
break;
}
}
} else if ((size == 2)
- && (MB_BYTE2LEN(*s) > 1)
+ && (MB_BYTE2LEN((uint8_t)(*s)) > 1)
&& wp->w_p_wrap
- && in_win_border(wp, col)) {
+ && in_win_border(wp, vcol)) {
// Count the ">" in the last column.
size++;
mb_added = 1;
@@ -406,43 +438,43 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he
// May have to add something for 'breakindent' and/or 'showbreak'
// string at start of line.
// Set *headp to the size of what we add.
+ // Do not use 'showbreak' at the NUL after the text.
added = 0;
-
- char *const sbr = (char *)get_showbreak_value(wp);
- if ((*sbr != NUL || wp->w_p_bri) && wp->w_p_wrap && col != 0) {
+ char *const sbr = c == NUL ? empty_option : get_showbreak_value(wp);
+ if ((*sbr != NUL || wp->w_p_bri) && wp->w_p_wrap && vcol != 0) {
colnr_T sbrlen = 0;
int numberwidth = win_col_off(wp);
numberextra = numberwidth;
- col += numberextra + mb_added;
+ vcol += numberextra + mb_added;
- if (col >= (colnr_T)wp->w_width_inner) {
- col -= wp->w_width_inner;
+ if (vcol >= (colnr_T)wp->w_width_inner) {
+ vcol -= wp->w_width_inner;
numberextra = wp->w_width_inner - (numberextra - win_col_off2(wp));
- if (col >= numberextra && numberextra > 0) {
- col %= numberextra;
+ if (vcol >= numberextra && numberextra > 0) {
+ vcol %= numberextra;
}
if (*sbr != NUL) {
- sbrlen = (colnr_T)mb_charlen((char_u *)sbr);
- if (col >= sbrlen) {
- col -= sbrlen;
+ sbrlen = (colnr_T)mb_charlen(sbr);
+ if (vcol >= sbrlen) {
+ vcol -= sbrlen;
}
}
- if (col >= numberextra && numberextra > 0) {
- col %= numberextra;
- } else if (col > 0 && numberextra > 0) {
- col += numberwidth - win_col_off2(wp);
+ if (vcol >= numberextra && numberextra > 0) {
+ vcol %= numberextra;
+ } else if (vcol > 0 && numberextra > 0) {
+ vcol += numberwidth - win_col_off2(wp);
}
numberwidth -= win_col_off2(wp);
}
- if (col == 0 || (col + size + sbrlen > (colnr_T)wp->w_width_inner)) {
+ if (vcol == 0 || (vcol + size + sbrlen > (colnr_T)wp->w_width_inner)) {
if (*sbr != NUL) {
if (size + sbrlen + numberwidth > (colnr_T)wp->w_width_inner) {
// Calculate effective window width.
int width = (colnr_T)wp->w_width_inner - sbrlen - numberwidth;
- int prev_width = col ? ((colnr_T)wp->w_width_inner - (sbrlen + col))
+ int prev_width = vcol ? ((colnr_T)wp->w_width_inner - (sbrlen + vcol))
: 0;
if (width <= 0) {
@@ -463,7 +495,7 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he
}
size += added;
- if (col != 0) {
+ if (vcol != 0) {
added = 0;
}
}
@@ -485,8 +517,11 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he
/// @param headp
///
/// @return The number of characters take up on the screen.
-static int win_nolbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp)
+static int win_nolbr_chartabsize(chartabsize_T *cts, int *headp)
{
+ win_T *wp = cts->cts_win;
+ char *s = cts->cts_ptr;
+ colnr_T col = cts->cts_vcol;
int n;
if ((*s == TAB) && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
@@ -494,11 +529,11 @@ 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((char *)s);
+ n = ptr2cells(s);
// Add one cell for a double-width character in the last column of the
// window, displayed with a ">".
- if ((n == 2) && (MB_BYTE2LEN(*s) > 1) && in_win_border(wp, col)) {
+ if ((n == 2) && (MB_BYTE2LEN((uint8_t)(*s)) > 1) && in_win_border(wp, col)) {
if (headp != NULL) {
*headp = 1;
}
diff --git a/src/nvim/plines.h b/src/nvim/plines.h
index 32778b69f1..808f6d284e 100644
--- a/src/nvim/plines.h
+++ b/src/nvim/plines.h
@@ -1,8 +1,24 @@
#ifndef NVIM_PLINES_H
#define NVIM_PLINES_H
+#include <stdbool.h>
+
+#include "nvim/buffer_defs.h"
#include "nvim/vim.h"
+// Argument for lbr_chartabsize().
+typedef struct {
+ win_T *cts_win;
+ char *cts_line; // start of the line
+ char *cts_ptr; // current position in line
+
+ bool cts_has_virt_text; // true if if a property inserts text
+ int cts_cur_text_width; // width of current inserted text
+ // TODO(bfredl): iterator in to the marktree for scanning virt text
+
+ int cts_vcol; // virtual column at current position
+} chartabsize_T;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "plines.h.generated.h"
#endif
diff --git a/src/nvim/po/CMakeLists.txt b/src/nvim/po/CMakeLists.txt
index 28f5723046..1db21880bb 100644
--- a/src/nvim/po/CMakeLists.txt
+++ b/src/nvim/po/CMakeLists.txt
@@ -37,7 +37,6 @@ if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG)
set(ENV{OLD_PO_FILE_INPUT} yes)
set(ENV{OLD_PO_FILE_OUTPUT} yes)
-
set(NVIM_RELATIVE_SOURCES)
foreach(SRC ${NVIM_SOURCES} ${NVIM_HEADERS})
file(RELATIVE_PATH RELATIVE_SRC ${CMAKE_CURRENT_SOURCE_DIR} ${SRC})
@@ -46,18 +45,19 @@ if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG)
set(NVIM_POT ${CMAKE_CURRENT_BINARY_DIR}/nvim.pot)
+ list(SORT NVIM_RELATIVE_SOURCES)
add_custom_command(
- OUTPUT nvim.pot
- COMMAND ${CMAKE_COMMAND}
- -DXGETTEXT_PRG=${XGETTEXT_PRG}
- -DPOT_FILE=${NVIM_POT}
- -DSEARCH_DIR=${CMAKE_CURRENT_SOURCE_DIR}
- "\"-DSOURCES=${NVIM_RELATIVE_SOURCES}\""
- -P ${PROJECT_SOURCE_DIR}/cmake/RunXgettext.cmake
- DEPENDS ${NVIM_SOURCES})
-
- add_custom_target(potfile DEPENDS ${NVIM_POT})
- set_target_properties(potfile PROPERTIES FOLDER po)
+ OUTPUT ${NVIM_POT}
+ COMMAND $<TARGET_FILE:nvim> -u NONE -i NONE -n --headless --cmd "set cpo+=+"
+ -S ${CMAKE_CURRENT_SOURCE_DIR}/tojavascript.vim ${NVIM_POT} ${PROJECT_SOURCE_DIR}/runtime/optwin.vim
+ COMMAND ${XGETTEXT_PRG} -o ${NVIM_POT} --default-domain=nvim
+ --add-comments --keyword=_ --keyword=N_ --keyword=NGETTEXT:1,2
+ -D ${CMAKE_CURRENT_SOURCE_DIR} -D ${CMAKE_CURRENT_BINARY_DIR}
+ ${NVIM_RELATIVE_SOURCES} optwin.js
+ COMMAND $<TARGET_FILE:nvim> -u NONE -i NONE -n --headless --cmd "set cpo+=+"
+ -S ${CMAKE_CURRENT_SOURCE_DIR}/fixfilenames.vim ${NVIM_POT} ../../../runtime/optwin.vim
+ VERBATIM
+ DEPENDS ${NVIM_SOURCES} nvim nvim_runtime_deps)
set(LANGUAGE_MO_FILES)
set(UPDATE_PO_TARGETS)
@@ -68,11 +68,7 @@ if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG)
add_custom_command(
OUTPUT ${moFile}
- COMMAND ${CMAKE_COMMAND}
- -DMSGFMT_PRG=${GETTEXT_MSGFMT_EXECUTABLE}
- -DMO_FILE=${moFile}
- -DPO_FILE=${poFile}
- -P ${PROJECT_SOURCE_DIR}/cmake/RunMsgfmt.cmake
+ COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} -o ${moFile} ${poFile}
DEPENDS ${poFile} ${NVIM_POT})
install_helper(
@@ -101,8 +97,6 @@ if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG)
lang inputName outputName inputEnc outputEnc outputCharSet)
set(inputFile ${CMAKE_CURRENT_SOURCE_DIR}/${inputName}.po)
set(outputFile ${CMAKE_CURRENT_SOURCE_DIR}/${outputName}.po)
- string(TOUPPER ${inputEnc} upperInputEnc)
- string(TOLOWER ${inputEnc} lowerInputEnc)
add_custom_target(update-po-${lang}
COMMAND ${CMAKE_COMMAND}
@@ -172,11 +166,8 @@ if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG)
set(poFile "${CMAKE_CURRENT_SOURCE_DIR}/${LANGUAGE}.po")
add_custom_target(update-po-${LANGUAGE}
- COMMAND ${CMAKE_COMMAND}
- -DMSGMERGE_PRG=${GETTEXT_MSGMERGE_EXECUTABLE}
- -DPO_FILE=${poFile}
- -DPOT_FILE=${NVIM_POT}
- -P ${PROJECT_SOURCE_DIR}/cmake/RunMsgmerge.cmake
+ COMMAND ${GETTEXT_MSGMERGE_EXECUTABLE} -q --update --backup=none --sort-by-file
+ ${poFile} ${NVIM_POT}
COMMENT "Updating ${LANGUAGE}.po"
DEPENDS ${NVIM_POT})
diff --git a/src/nvim/po/af.po b/src/nvim/po/af.po
index 82345f8a46..92a1a6ca3c 100644
--- a/src/nvim/po/af.po
+++ b/src/nvim/po/af.po
@@ -2794,101 +2794,6 @@ msgstr "soektog het BO getref, gaan voort van ONDER af"
msgid "search hit BOTTOM, continuing at TOP"
msgstr "soektog het ONDER getref, gaan voort van BO af"
-msgid "E550: Missing colon"
-msgstr "E550: Ontbrekende dubbelpunt"
-
-msgid "E551: Illegal component"
-msgstr "E551: Ongeldige komponent"
-
-msgid "E552: digit expected"
-msgstr "E552: syfer verwag"
-
-#, c-format
-msgid "Page %d"
-msgstr "Bladsy %d"
-
-msgid "No text to be printed"
-msgstr "Geen teks om te druk nie"
-
-#, fuzzy, c-format
-#~ msgid "Printing page %d (%zu%%)"
-#~ msgstr "Druk nou bladsy %d (%d%%)"
-
-#, c-format
-msgid " Copy %d of %d"
-msgstr " Kopie %d van %d"
-
-#, c-format
-msgid "Printed: %s"
-msgstr "Gedruk: %s"
-
-msgid "Printing aborted"
-msgstr "Drukkery gestaak"
-
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: Kan nie na 'PostScript' afvoerler skryf nie"
-
-#, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E624: Kan nie ler \"%s\" oopmaak nie"
-
-#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: Kan nie 'PostScript' hulpbron-ler \"%s\" lees nie"
-
-#, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E618: Ler \"%s\" is nie 'n 'PostScript' hulpbron-ler nie"
-
-#, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr ""
-"E619: Ler \"%s\" is nie 'n ondersteunde 'PostScript' hulpbron-ler nie"
-
-#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr "E621: \"%s\" die hulpbron ler het die verkeerde weergawe"
-
-#~ msgid "E673: Incompatible multi-byte encoding and character set."
-#~ msgstr ""
-
-#~ msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-#~ msgstr ""
-
-#~ msgid "E675: No default font specified for multi-byte printing."
-#~ msgstr ""
-
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: Kan nie 'PostScript' afvoerler oopmaak nie"
-
-#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: Kan nie ler %s oopmaak nie"
-
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E456: Kan nie 'PostScript' hulpbron-ler \"prolog.ps\" lees nie"
-
-#, fuzzy
-#~ msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-#~ msgstr "E456: Kan nie 'PostScript' hulpbron-ler \"%s\" vind nie"
-
-#, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E456: Kan nie 'PostScript' hulpbron-ler \"%s\" vind nie"
-
-#, fuzzy, c-format
-#~ msgid "E620: Unable to convert to print encoding \"%s\""
-#~ msgstr "E620: Kon nie van wye-greep na \"%s\" enkodering verander nie"
-
-msgid "Sending to printer..."
-msgstr "Besig om te stuur na drukker..."
-
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: Kon nie 'PostScript' ler druk nie"
-
-msgid "Print job sent."
-msgstr "Druktaak gestuur."
-
msgid "Add a new database"
msgstr "Voeg 'n nuwe databasis by"
@@ -5320,8 +5225,8 @@ msgstr "E424: Te veel verskillende uitlig-eienskappe in gebruik"
msgid "E669: Unprintable character in group name"
msgstr "E669: Onvertoonbare karakter in groepnaam"
-msgid "W18: Invalid character in group name"
-msgstr "W18: Ongeldige karakter groepnaam"
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Ongeldige karakter groepnaam"
#~ msgid "E849: Too many highlight and syntax groups"
#~ msgstr ""
diff --git a/src/nvim/po/ca.po b/src/nvim/po/ca.po
index 6c4d6ddd22..964fcc9325 100644
--- a/src/nvim/po/ca.po
+++ b/src/nvim/po/ca.po
@@ -3013,128 +3013,6 @@ msgstr "la cerca ha arribat a DALT, es continua a BAIX"
msgid "search hit BOTTOM, continuing at TOP"
msgstr "la cerca ha arribat a BAIX, es continua a DALT"
-#: ../hardcopy.c:240
-msgid "E550: Missing colon"
-msgstr "E550: Falta un carcter \":\""
-
-#: ../hardcopy.c:252
-msgid "E551: Illegal component"
-msgstr "E551: Component illegal"
-
-#: ../hardcopy.c:259
-msgid "E552: digit expected"
-msgstr "E552: S'esperava un dgit"
-
-#: ../hardcopy.c:473
-#, c-format
-msgid "Page %d"
-msgstr "Pgina %d"
-
-#: ../hardcopy.c:597
-msgid "No text to be printed"
-msgstr "No hi ha text per imprimir"
-
-#: ../hardcopy.c:668
-#, c-format
-msgid "Printing page %d (%d%%)"
-msgstr "Imprimint la pgina %d (%d%%)"
-
-#: ../hardcopy.c:680
-#, c-format
-msgid " Copy %d of %d"
-msgstr " Cpia %d de %d"
-
-#: ../hardcopy.c:733
-#, c-format
-msgid "Printed: %s"
-msgstr "S'ha imprs: %s"
-
-#: ../hardcopy.c:740
-msgid "Printing aborted"
-msgstr "S'ha avortat la impressi"
-
-#: ../hardcopy.c:1365
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: Error en escriure el fitxer PostScript"
-
-#: ../hardcopy.c:1747
-#, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E624: No s'ha pogut obrir el fitxer \"%s\""
-
-#: ../hardcopy.c:1756 ../hardcopy.c:2470
-#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: No s'ha pogut llegir el fitxer de recursos PostScript \"%s\""
-
-#: ../hardcopy.c:1772
-#, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E618: El fitxer \"%s\" no s un fitxer de recursos PostScript"
-
-#: ../hardcopy.c:1788 ../hardcopy.c:1805 ../hardcopy.c:1844
-#, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr "E619: El fitxer de recursos PostScript \"%s\" no est suportat"
-
-#: ../hardcopy.c:1856
-#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr "E621: La versi del fitxer de recursos \"%s\" no s vlida"
-
-#: ../hardcopy.c:2225
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr "E673: Joc de carcters i codificaci multi-octet no compatibles."
-
-#: ../hardcopy.c:2238
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr ""
-"E674: printmbcharset no pot estar buit si la codificaci s multi-octet"
-
-#: ../hardcopy.c:2254
-msgid "E675: No default font specified for multi-byte printing."
-msgstr ""
-"E675: No heu especificat cap fosa per defecte per a la impressi multi-octet."
-
-#: ../hardcopy.c:2426
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: No s'ha pogut obrir el fitxer PostScript generat"
-
-#: ../hardcopy.c:2458
-#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: No s'ha pogut obrir el fitxer \"%s\""
-
-#: ../hardcopy.c:2583
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E456: No s'ha trobat el fitxer de recursos PostScript \"prolog.ps\""
-
-#: ../hardcopy.c:2593
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr "E456: No s'ha trobat el fitxer de recursos PostScript \"cidfont.ps\""
-
-#: ../hardcopy.c:2622 ../hardcopy.c:2639 ../hardcopy.c:2665
-#, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E456: No s'ha trobat el fitxer de recursos PostScript \"%s.ps\""
-
-#: ../hardcopy.c:2654
-#, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr "E620: No s'ha pogut convertir a la codificaci d'impressi \"%s\""
-
-#: ../hardcopy.c:2877
-msgid "Sending to printer..."
-msgstr "S'est enviant a la impressora..."
-
-#: ../hardcopy.c:2881
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: Error en imprimir el fitxer PostScript"
-
-#: ../hardcopy.c:2883
-msgid "Print job sent."
-msgstr "S'ha enviat la tasca d'impressi."
-
#: ../if_cscope.c:85
msgid "Add a new database"
msgstr "Afegeix una base de dades nova"
@@ -5983,9 +5861,9 @@ msgstr "E424: Hi ha massa atributs de ressalt diferents en s"
msgid "E669: Unprintable character in group name"
msgstr "E669: Carcter no imprimible en el nom del grup"
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
-msgstr "W18: Hi ha un carcter no vlid en el nom del grup"
+#: ../highlight_group.c:1756
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Hi ha un carcter no vlid en el nom del grup"
#: ../syntax.c:7448
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/cleanup.vim b/src/nvim/po/cleanup.vim
index 8384286b0d..6df5edd498 100644
--- a/src/nvim/po/cleanup.vim
+++ b/src/nvim/po/cleanup.vim
@@ -22,7 +22,9 @@ silent g/^msgstr"/s//msgstr "/
silent g/^msgid"/s//msgid "/
silent g/^msgstr ""\(\n"\)\@!/?^msgid?,.s/^/#\~ /
+" clean up empty lines
silent g/^\n\n\n/.d
+silent! %s/\n\+\%$//
if s:was_diff
setl diff
diff --git a/src/nvim/po/cs.cp1250.po b/src/nvim/po/cs.cp1250.po
index 859039eb87..b939139fb5 100644
--- a/src/nvim/po/cs.cp1250.po
+++ b/src/nvim/po/cs.cp1250.po
@@ -3077,131 +3077,6 @@ msgstr "hledn doshlo zatku, pokraovn od konce"
msgid "search hit BOTTOM, continuing at TOP"
msgstr "hledn doshlo konce, pokraovn od zatku"
-#: ../hardcopy.c:240
-#, fuzzy
-msgid "E550: Missing colon"
-msgstr "Chyb dvojteka"
-
-#: ../hardcopy.c:252
-#, fuzzy
-msgid "E551: Illegal component"
-msgstr "neppustn soust"
-
-#: ../hardcopy.c:259
-#, fuzzy
-msgid "E552: digit expected"
-msgstr "oekvna slice"
-
-#: ../hardcopy.c:473
-#, c-format
-msgid "Page %d"
-msgstr ""
-
-#: ../hardcopy.c:597
-msgid "No text to be printed"
-msgstr "dn text k vytitn"
-
-#: ../hardcopy.c:668
-#, c-format
-msgid "Printing page %d (%d%%)"
-msgstr "Tisknu stranu %d (%d%%)"
-
-#: ../hardcopy.c:680
-#, c-format
-msgid " Copy %d of %d"
-msgstr " Kopie %d z %d"
-
-#: ../hardcopy.c:733
-#, c-format
-msgid "Printed: %s"
-msgstr "Vytitno: %s"
-
-#: ../hardcopy.c:740
-msgid "Printing aborted"
-msgstr "Tisk zruen"
-
-#: ../hardcopy.c:1365
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: Nelze zapisovat do vstupnho PostScriptovho souboru"
-
-#: ../hardcopy.c:1747
-#, fuzzy, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E456: Nelze otevt soubor \"%s\""
-
-#: ../hardcopy.c:1756 ../hardcopy.c:2470
-#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: Nelze st zdrojov PostScriptov soubor \"%s\""
-
-#: ../hardcopy.c:1772
-#, fuzzy, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E457: Nelze st zdrojov PostScriptov soubor \"%s\""
-
-#: ../hardcopy.c:1788 ../hardcopy.c:1805 ../hardcopy.c:1844
-#, fuzzy, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr "E457: Nelze st zdrojov PostScriptov soubor \"%s\""
-
-#: ../hardcopy.c:1856
-#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr ""
-
-#: ../hardcopy.c:2225
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr ""
-
-#: ../hardcopy.c:2238
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr ""
-
-#: ../hardcopy.c:2254
-msgid "E675: No default font specified for multi-byte printing."
-msgstr ""
-
-#: ../hardcopy.c:2426
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: Nelze otevt vstupn PostScriptov soubor"
-
-#: ../hardcopy.c:2458
-#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: Nelze otevt soubor \"%s\""
-
-#: ../hardcopy.c:2583
-#, fuzzy
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E457: Nelze st zdrojov PostScriptov soubor \"%s\""
-
-#: ../hardcopy.c:2593
-#, fuzzy
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr "E457: Nelze st zdrojov PostScriptov soubor \"%s\""
-
-#: ../hardcopy.c:2622 ../hardcopy.c:2639 ../hardcopy.c:2665
-#, fuzzy, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E457: Nelze st zdrojov PostScriptov soubor \"%s\""
-
-#: ../hardcopy.c:2654
-#, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr ""
-
-#: ../hardcopy.c:2877
-msgid "Sending to printer..."
-msgstr "Odeslm na tiskrnu..."
-
-#: ../hardcopy.c:2881
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: Selhal tisk PostScriptovho souboru"
-
-#: ../hardcopy.c:2883
-msgid "Print job sent."
-msgstr "Tiskov loha odeslna."
-
#: ../if_cscope.c:85
msgid "Add a new database"
msgstr "Pidat novou databzi"
@@ -6072,10 +5947,10 @@ msgstr ""
msgid "E669: Unprintable character in group name"
msgstr ""
-#: ../syntax.c:7434
+#: ../highlight_group.c:1756
#, fuzzy
-msgid "W18: Invalid character in group name"
-msgstr "E182: Chybn jmno pkazu"
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Chybn jmno pkazu"
#: ../syntax.c:7448
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/cs.po b/src/nvim/po/cs.po
index 4d9ad58836..f98dc2828e 100644
--- a/src/nvim/po/cs.po
+++ b/src/nvim/po/cs.po
@@ -3077,131 +3077,6 @@ msgstr "hledn doshlo zatku, pokraovn od konce"
msgid "search hit BOTTOM, continuing at TOP"
msgstr "hledn doshlo konce, pokraovn od zatku"
-#: ../hardcopy.c:240
-#, fuzzy
-msgid "E550: Missing colon"
-msgstr "Chyb dvojteka"
-
-#: ../hardcopy.c:252
-#, fuzzy
-msgid "E551: Illegal component"
-msgstr "neppustn soust"
-
-#: ../hardcopy.c:259
-#, fuzzy
-msgid "E552: digit expected"
-msgstr "oekvna slice"
-
-#: ../hardcopy.c:473
-#, c-format
-msgid "Page %d"
-msgstr ""
-
-#: ../hardcopy.c:597
-msgid "No text to be printed"
-msgstr "dn text k vytitn"
-
-#: ../hardcopy.c:668
-#, c-format
-msgid "Printing page %d (%d%%)"
-msgstr "Tisknu stranu %d (%d%%)"
-
-#: ../hardcopy.c:680
-#, c-format
-msgid " Copy %d of %d"
-msgstr " Kopie %d z %d"
-
-#: ../hardcopy.c:733
-#, c-format
-msgid "Printed: %s"
-msgstr "Vytitno: %s"
-
-#: ../hardcopy.c:740
-msgid "Printing aborted"
-msgstr "Tisk zruen"
-
-#: ../hardcopy.c:1365
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: Nelze zapisovat do vstupnho PostScriptovho souboru"
-
-#: ../hardcopy.c:1747
-#, fuzzy, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E456: Nelze otevt soubor \"%s\""
-
-#: ../hardcopy.c:1756 ../hardcopy.c:2470
-#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: Nelze st zdrojov PostScriptov soubor \"%s\""
-
-#: ../hardcopy.c:1772
-#, fuzzy, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E457: Nelze st zdrojov PostScriptov soubor \"%s\""
-
-#: ../hardcopy.c:1788 ../hardcopy.c:1805 ../hardcopy.c:1844
-#, fuzzy, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr "E457: Nelze st zdrojov PostScriptov soubor \"%s\""
-
-#: ../hardcopy.c:1856
-#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr ""
-
-#: ../hardcopy.c:2225
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr ""
-
-#: ../hardcopy.c:2238
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr ""
-
-#: ../hardcopy.c:2254
-msgid "E675: No default font specified for multi-byte printing."
-msgstr ""
-
-#: ../hardcopy.c:2426
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: Nelze otevt vstupn PostScriptov soubor"
-
-#: ../hardcopy.c:2458
-#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: Nelze otevt soubor \"%s\""
-
-#: ../hardcopy.c:2583
-#, fuzzy
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E457: Nelze st zdrojov PostScriptov soubor \"%s\""
-
-#: ../hardcopy.c:2593
-#, fuzzy
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr "E457: Nelze st zdrojov PostScriptov soubor \"%s\""
-
-#: ../hardcopy.c:2622 ../hardcopy.c:2639 ../hardcopy.c:2665
-#, fuzzy, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E457: Nelze st zdrojov PostScriptov soubor \"%s\""
-
-#: ../hardcopy.c:2654
-#, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr ""
-
-#: ../hardcopy.c:2877
-msgid "Sending to printer..."
-msgstr "Odeslm na tiskrnu..."
-
-#: ../hardcopy.c:2881
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: Selhal tisk PostScriptovho souboru"
-
-#: ../hardcopy.c:2883
-msgid "Print job sent."
-msgstr "Tiskov loha odeslna."
-
#: ../if_cscope.c:85
msgid "Add a new database"
msgstr "Pidat novou databzi"
@@ -6072,10 +5947,10 @@ msgstr ""
msgid "E669: Unprintable character in group name"
msgstr ""
-#: ../syntax.c:7434
+#: ../highlight_group.c:1756
#, fuzzy
-msgid "W18: Invalid character in group name"
-msgstr "E182: Chybn jmno pkazu"
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Chybn jmno pkazu"
#: ../syntax.c:7448
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/da.po b/src/nvim/po/da.po
index dfcc052288..c55918abc9 100644
--- a/src/nvim/po/da.po
+++ b/src/nvim/po/da.po
@@ -2384,99 +2384,6 @@ msgstr "Størrelse:"
msgid "E256: Hangul automata ERROR"
msgstr "E256: FEJL ved Hangul automata"
-msgid "E550: Missing colon"
-msgstr "E550: Manglende kolon"
-
-msgid "E551: Illegal component"
-msgstr "E551: Ulovlig komponent"
-
-msgid "E552: digit expected"
-msgstr "E552: ciffer ventet"
-
-#, c-format
-msgid "Page %d"
-msgstr "Side %d"
-
-msgid "No text to be printed"
-msgstr "Ingen tekst at udskrive"
-
-#, c-format
-msgid "Printing page %d (%d%%)"
-msgstr "Udskriver side %d (%d%%)"
-
-#, c-format
-msgid " Copy %d of %d"
-msgstr " Kopi %d af %d"
-
-#, c-format
-msgid "Printed: %s"
-msgstr "Udskrev: %s"
-
-msgid "Printing aborted"
-msgstr "Udskrivning afbrudt"
-
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: Fejl ved skrivning til PostScript-output-fil"
-
-#, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E624: Kan ikke åbne filen \"%s\""
-
-#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: Kan ikke læse PostScript-ressourcefilen \"%s\""
-
-#, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E618: filen \"%s\" er ikke en PostScript-ressourcefil"
-
-#, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr "E619: filen \"%s\" er ikke en understøttet PostScript-ressourcefil"
-
-#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr "E621: \"%s\"-ressourcefilen har forkert version"
-
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr "E673: Inkompatibel multibyte-kodning og -tegnsæt."
-
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr "E674: printmbcharset må ikke være tom med multibyte-kodning."
-
-msgid "E675: No default font specified for multi-byte printing."
-msgstr "E675: Ingen standardskrifttype angivet for multibyte-udskrivning."
-
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: Kan ikke åbne PostScript-output-fil"
-
-#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: Kan ikke åbne filen \"%s\""
-
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E456: Kan ikke finde PostScript-ressourcefilen \"prolog.ps\""
-
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr "E456: Kan ikke finde PostScript-ressourcefilen \"cidfont.ps\""
-
-#, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E456: Kan ikke finde PostScript-ressourcefilen \"%s.ps\""
-
-#, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr "E620: Kan ikke konvertere til udskrivningskodningen \"%s\""
-
-msgid "Sending to printer..."
-msgstr "Sender til printer..."
-
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: Kunne ikke udskrive PostScript-fil"
-
-msgid "Print job sent."
-msgstr "Udskrivningsjob sendt."
-
msgid "Add a new database"
msgstr "Tilføj en ny database"
@@ -5599,8 +5506,8 @@ msgstr "E424: For mange forskellige fremhævningsattributter i brug"
msgid "E669: Unprintable character in group name"
msgstr "E669: Tegn som ikke kan udskrives i gruppenavn"
-msgid "W18: Invalid character in group name"
-msgstr "W18: Ugyldige tegn i gruppenavn"
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Ugyldige tegn i gruppenavn"
msgid "E849: Too many highlight and syntax groups"
msgstr "E849: For mange fremhævnings- og syntaksgrupper"
diff --git a/src/nvim/po/de.po b/src/nvim/po/de.po
index 2dde77e9f7..efd52d3efe 100644
--- a/src/nvim/po/de.po
+++ b/src/nvim/po/de.po
@@ -2434,126 +2434,6 @@ msgstr "Suche erreichte den ANFANG und wurde am ENDE fortgesetzt"
msgid "search hit BOTTOM, continuing at TOP"
msgstr "Suche erreichte das ENDE und wurde am ANFANG fortgesetzt"
-#: ../hardcopy.c:296
-msgid "E550: Missing colon"
-msgstr "E550: Fehlender Doppelpunkt"
-
-#: ../hardcopy.c:308
-msgid "E551: Illegal component"
-msgstr "E551: Unzulssige Komponente"
-
-#: ../hardcopy.c:315
-msgid "E552: digit expected"
-msgstr "E552: Ziffer erwartet"
-
-#: ../hardcopy.c:529
-#, c-format
-msgid "Page %d"
-msgstr "Seite %d"
-
-#: ../hardcopy.c:653
-msgid "No text to be printed"
-msgstr "Kein Text zum Drucken"
-
-#: ../hardcopy.c:724
-#, c-format
-msgid "Printing page %d (%d%%)"
-msgstr "Drucke Seite %d (%d%%)"
-
-#: ../hardcopy.c:736
-#, c-format
-msgid " Copy %d of %d"
-msgstr " Kopiere %d von %d"
-
-#: ../hardcopy.c:789
-#, c-format
-msgid "Printed: %s"
-msgstr "Gedruckt: %s"
-
-#: ../hardcopy.c:796
-msgid "Printing aborted"
-msgstr "Druck abgebrochen"
-
-#: ../hardcopy.c:1307
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: Fehler beim Schreiben der PostScript-Ausgabedatei"
-
-#: ../hardcopy.c:1679
-#, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E624: Datei \"%s\" kann nicht geffnet werden"
-
-#: ../hardcopy.c:1688 ../hardcopy.c:2401
-#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: PostScript-Ressourcendatei \"%s\" kann nicht gelesen werden"
-
-#: ../hardcopy.c:1704
-#, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E618: Datei \"%s\" ist keine PostScript-Ressourcendatei"
-
-#: ../hardcopy.c:1720 ../hardcopy.c:1737 ../hardcopy.c:1776
-#, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr "E619: Datei \"%s\" ist keine untersttzte PostScript-Ressourcendatei"
-
-#: ../hardcopy.c:1788
-#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr "E621: \"%s\" Ressource-Datei hat die falsche Version"
-
-#: ../hardcopy.c:2157
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr "E673: Unzulssiger Multibyte-Zeichensatz"
-
-#: ../hardcopy.c:2169
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr "E674: Bei Multibyte-Zeichensatz darf 'printmbcharset' nicht leer sein."
-
-#: ../hardcopy.c:2185
-msgid "E675: No default font specified for multi-byte printing."
-msgstr "E675: Keine Standardschriftart fr Multibyte-Druck angegeben."
-
-#: ../hardcopy.c:2357
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: PostScript-Ausgabedatei kann nicht geffnet werden"
-
-#: ../hardcopy.c:2389
-#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: Datei \"%s\" kann nicht geffnet werden"
-
-#: ../hardcopy.c:2514
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E456: PostScript-Ressourcendatei \"prolog.ps\" nicht gefunden"
-
-#: ../hardcopy.c:2524
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr "E456: PostScript-Ressourcendatei \"cidfont.ps\" nicht gefunden"
-
-#: ../hardcopy.c:2553 ../hardcopy.c:2570 ../hardcopy.c:2596
-#, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E456: PostScript-Ressourcendatei \"%s\" nicht gefunden"
-
-#: ../hardcopy.c:2585
-#, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr "E620: Umwandlung zu Drucker-Zeichensatz \"%s\" fehlgeschlagen"
-
-#: ../hardcopy.c:2808
-msgid "Sending to printer..."
-msgstr "Schicke zum Drucker..."
-
-#: ../hardcopy.c:2812
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: Druck der PostScript-Datei fehlgeschlagen"
-
-#: ../hardcopy.c:2814
-msgid "Print job sent."
-msgstr "Druckauftrag abgeschickt"
-
#: ../if_cscope.c:49
msgid "Add a new database"
msgstr "Eine neue Datenbank hinzufgen"
@@ -5408,8 +5288,8 @@ msgid "E669: Unprintable character in group name"
msgstr "E669: Nicht druckbare Zeichen im Namen der Gruppe"
#: ../syntax.c:7304
-msgid "W18: Invalid character in group name"
-msgstr "W18: Ungltiges Zeichen im Namen der Gruppe"
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Ungltiges Zeichen im Namen der Gruppe"
#: ../syntax.c:7318
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/en_GB.po b/src/nvim/po/en_GB.po
index 66cdba6f92..1c03b305f9 100644
--- a/src/nvim/po/en_GB.po
+++ b/src/nvim/po/en_GB.po
@@ -2923,128 +2923,6 @@ msgstr ""
msgid "search hit BOTTOM, continuing at TOP"
msgstr ""
-#: ../hardcopy.c:240
-msgid "E550: Missing colon"
-msgstr ""
-
-#: ../hardcopy.c:252
-msgid "E551: Illegal component"
-msgstr ""
-
-#: ../hardcopy.c:259
-msgid "E552: digit expected"
-msgstr ""
-
-#: ../hardcopy.c:473
-#, c-format
-msgid "Page %d"
-msgstr ""
-
-#: ../hardcopy.c:597
-msgid "No text to be printed"
-msgstr ""
-
-#: ../hardcopy.c:668
-#, c-format
-msgid "Printing page %d (%d%%)"
-msgstr ""
-
-#: ../hardcopy.c:680
-#, c-format
-msgid " Copy %d of %d"
-msgstr ""
-
-#: ../hardcopy.c:733
-#, c-format
-msgid "Printed: %s"
-msgstr ""
-
-#: ../hardcopy.c:740
-msgid "Printing aborted"
-msgstr ""
-
-#: ../hardcopy.c:1365
-#, fuzzy
-msgid "E455: Error writing to PostScript output file"
-msgstr "E324: Cannot open PostScript output file"
-
-#: ../hardcopy.c:1747
-#, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E624: Cannot open file \"%s\""
-
-#: ../hardcopy.c:1756 ../hardcopy.c:2470
-#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: Cannot read PostScript resource file \"%s\""
-
-#: ../hardcopy.c:1772
-#, fuzzy, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E457: Cannot read PostScript resource file \"%s\""
-
-#: ../hardcopy.c:1788 ../hardcopy.c:1805 ../hardcopy.c:1844
-#, fuzzy, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr "E457: Cannot read PostScript resource file \"%s\""
-
-#: ../hardcopy.c:1856
-#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr ""
-
-#: ../hardcopy.c:2225
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr ""
-
-#: ../hardcopy.c:2238
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr ""
-
-#: ../hardcopy.c:2254
-msgid "E675: No default font specified for multi-byte printing."
-msgstr ""
-
-#: ../hardcopy.c:2426
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: Cannot open PostScript output file"
-
-#: ../hardcopy.c:2458
-#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: Cannot open file \"%s\""
-
-#: ../hardcopy.c:2583
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E456: Cannot find PostScript resource file \"prolog.ps\""
-
-#: ../hardcopy.c:2593
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr "E456: Cannot find PostScript resource file \"cidfont.ps\""
-
-#: ../hardcopy.c:2622 ../hardcopy.c:2639 ../hardcopy.c:2665
-#, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E456: Cannot find PostScript resource file \"%s.ps\""
-
-#: ../hardcopy.c:2654
-#, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr ""
-
-#: ../hardcopy.c:2877
-msgid "Sending to printer..."
-msgstr ""
-
-#: ../hardcopy.c:2881
-#, fuzzy
-msgid "E365: Failed to print PostScript file"
-msgstr "E324: Cannot open PostScript output file"
-
-#: ../hardcopy.c:2883
-msgid "Print job sent."
-msgstr ""
-
#: ../if_cscope.c:85
msgid "Add a new database"
msgstr ""
@@ -5730,8 +5608,8 @@ msgstr ""
msgid "E669: Unprintable character in group name"
msgstr ""
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
+#: ../highlight_group.c:1756
+msgid "E5248: Invalid character in group name"
msgstr ""
#: ../syntax.c:7448
diff --git a/src/nvim/po/eo.po b/src/nvim/po/eo.po
index 1c503d0a84..aaa39d6041 100644
--- a/src/nvim/po/eo.po
+++ b/src/nvim/po/eo.po
@@ -2246,100 +2246,6 @@ msgstr "Stilo:"
msgid "Size:"
msgstr "Grando:"
-msgid "E550: Missing colon"
-msgstr "E550: Mankas dupunkto"
-
-msgid "E551: Illegal component"
-msgstr "E551: Nevalida komponento"
-
-msgid "E552: digit expected"
-msgstr "E552: cifero atendita"
-
-#, c-format
-msgid "Page %d"
-msgstr "Paĝo %d"
-
-msgid "No text to be printed"
-msgstr "Neniu presenda teksto"
-
-#, c-format
-msgid "Printing page %d (%d%%)"
-msgstr "Presas paĝon %d (%d%%)"
-
-#, c-format
-msgid " Copy %d of %d"
-msgstr " Kopio %d de %d"
-
-#, c-format
-msgid "Printed: %s"
-msgstr "Presis: %s"
-
-msgid "Printing aborted"
-msgstr "Presado ĉesigita"
-
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: Eraro dum skribo de PostSkripta eliga dosiero"
-
-#, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E624: Ne eblas malfermi dosieron \"%s\""
-
-#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: Ne eblas legi dosieron de PostSkripta rimedo \"%s\""
-
-#, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E618: \"%s\" ne estas dosiero de PostSkripta rimedo"
-
-#, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr "E619: \"%s\" ne estas subtenata dosiero de PostSkripta rimedo"
-
-#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr "E621: \"%s\" dosiero de rimedo havas neĝustan version"
-
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr "E673: Nekongrua plurbajta kodoprezento kaj signaro."
-
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr ""
-"E674: printmbcharset ne rajtas esti malplena kun plurbajta kodoprezento."
-
-msgid "E675: No default font specified for multi-byte printing."
-msgstr "E675: Neniu defaŭlta tiparo specifita por plurbajta presado."
-
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: Ne eblas malfermi eligan PostSkriptan dosieron"
-
-#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: Ne eblas malfermi dosieron \"%s\""
-
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E456: Dosiero de PostSkripta rimedo \"prolog.ps\" ne troveblas"
-
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr "E456: Dosiero de PostSkripta rimedo \"cidfont.ps\" ne troveblas"
-
-#, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E456: Dosiero de PostSkripta rimedo \"%s.ps\" ne troveblas"
-
-#, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr "E620: Ne eblas konverti al la presa kodoprezento \"%s\""
-
-msgid "Sending to printer..."
-msgstr "Sendas al presilo..."
-
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: Presado de PostSkripta dosiero malsukcesis"
-
-msgid "Print job sent."
-msgstr "Laboro de presado sendita."
-
msgid "Add a new database"
msgstr "Aldoni novan datumbazon"
@@ -5378,8 +5284,8 @@ msgstr "E424: Tro da malsamaj atributoj de emfazo uzataj"
msgid "E669: Unprintable character in group name"
msgstr "E669: Nepresebla signo en nomo de grupo"
-msgid "W18: Invalid character in group name"
-msgstr "W18: Nevalida signo en nomo de grupo"
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Nevalida signo en nomo de grupo"
msgid "E849: Too many highlight and syntax groups"
msgstr "E849: Tro da emfazaj kaj sintaksaj grupoj"
diff --git a/src/nvim/po/es.po b/src/nvim/po/es.po
index adea651b7c..120a29af15 100644
--- a/src/nvim/po/es.po
+++ b/src/nvim/po/es.po
@@ -3054,129 +3054,6 @@ msgstr "La búsqueda ha llegado al PRINCIPIO, continuando desde el FINAL"
msgid "search hit BOTTOM, continuing at TOP"
msgstr "La búsqueda ha llegado al FINAL, continuando desde el PRINCIPIO"
-#: ../hardcopy.c:240
-msgid "E550: Missing colon"
-msgstr "E550: Falta un símbolo de dos puntos"
-
-#: ../hardcopy.c:252
-msgid "E551: Illegal component"
-msgstr "E551: Componente ilegal"
-
-#: ../hardcopy.c:259
-msgid "E552: digit expected"
-msgstr "E552: Se esperaba un dígito"
-
-#: ../hardcopy.c:473
-#, c-format
-msgid "Page %d"
-msgstr "Página %d"
-
-#: ../hardcopy.c:597
-msgid "No text to be printed"
-msgstr "No hay texto que imprimir"
-
-#: ../hardcopy.c:668
-#, c-format
-msgid "Printing page %d (%d%%)"
-msgstr "Imprimiendo la página %d (%d%%)"
-
-#: ../hardcopy.c:680
-#, c-format
-msgid " Copy %d of %d"
-msgstr "Copia %d de %d"
-
-#: ../hardcopy.c:733
-#, c-format
-msgid "Printed: %s"
-msgstr "Impreso: %s"
-
-#: ../hardcopy.c:740
-msgid "Printing aborted"
-msgstr "Impresión interrumpida"
-
-#: ../hardcopy.c:1365
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: Error escribiendo al archivo PostScript de salida"
-
-#: ../hardcopy.c:1747
-#, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E624: No se pudo abrir el archivo \"%s\""
-
-#: ../hardcopy.c:1756 ../hardcopy.c:2470
-#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: No se pudo leer el archivo de recursos de PostScript \"%s\""
-
-#: ../hardcopy.c:1772
-#, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E618: El archivo \"%s\" no es un archivo de recursos PostScript"
-
-#: ../hardcopy.c:1788 ../hardcopy.c:1805 ../hardcopy.c:1844
-#, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr "E619: El archivo \"%s\" no es un recurso PostScript que pueda usar"
-
-#: ../hardcopy.c:1856
-#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr "E621: La versión del archivo de recursos \"%s\" es incorrecta"
-
-#: ../hardcopy.c:2225
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr "E673: Codificación y set de caracteres multi-byte incompatibles"
-
-#: ../hardcopy.c:2238
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr ""
-"E674: \"printmbcharset\" no puede estar vacío en una codificación multi-byte"
-
-#: ../hardcopy.c:2254
-msgid "E675: No default font specified for multi-byte printing."
-msgstr ""
-"E675: No se ha definido un tipo de letra predeterminado para impresión "
-"multi-byte"
-
-#: ../hardcopy.c:2426
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: No se pudo abrir el archivo PostScript de salida"
-
-#: ../hardcopy.c:2458
-#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: No se pudo abrir el archivo %s"
-
-#: ../hardcopy.c:2583
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E456: No se encontró el archivo de recursos PostScript \"prolog.ps\""
-
-#: ../hardcopy.c:2593
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr "E456: No se encontró el archivo de recursos PostScript \"cidfont.ps\""
-
-#: ../hardcopy.c:2622 ../hardcopy.c:2639 ../hardcopy.c:2665
-#, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E456: No se encontró el archivo de recursos PostScript \"%s.ps\""
-
-#: ../hardcopy.c:2654
-#, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr "E620: No se pudo convertir a la codificación de impresión \"%s\""
-
-#: ../hardcopy.c:2877
-msgid "Sending to printer..."
-msgstr "Enviando a la impresora..."
-
-#: ../hardcopy.c:2881
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: Falló la impresión del archivo PostScript"
-
-#: ../hardcopy.c:2883
-msgid "Print job sent."
-msgstr "Se ha enviado la tarea de impresión."
-
#: ../if_cscope.c:85
msgid "Add a new database"
msgstr "Añadir una nueva base de datos"
@@ -6053,9 +5930,9 @@ msgstr "E669: Carácter no imprimible en el nombre del grupo"
# This is an error, but since there previously was no check only
# * give a warning.
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
-msgstr "W18: Hay un carácter no válido en el nombre del grupo"
+#: ../highlight_group.c:1756
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Hay un carácter no válido en el nombre del grupo"
#: ../syntax.c:7448
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/fi.po b/src/nvim/po/fi.po
index f10d4ce977..c8db9a64b5 100644
--- a/src/nvim/po/fi.po
+++ b/src/nvim/po/fi.po
@@ -2896,99 +2896,6 @@ msgstr "haku pääsi ALKUUN, jatketaan LOPUSTA"
msgid "search hit BOTTOM, continuing at TOP"
msgstr "haku pääsi LOPPUUN, jatketaan ALUSTA"
-msgid "E550: Missing colon"
-msgstr "E550: kaksoispiste puuttuu"
-
-msgid "E551: Illegal component"
-msgstr "E551: Virheellinen komponentti"
-
-msgid "E552: digit expected"
-msgstr "E552: pitäisi olla numero"
-
-#, c-format
-msgid "Page %d"
-msgstr "Sivu %d"
-
-msgid "No text to be printed"
-msgstr "Ei tekstiä tulostettavaksi"
-
-#, fuzzy, c-format
-#~ msgid "Printing page %d (%zu%%)"
-#~ msgstr "Tulostetaan sivua %d (%d %%)"
-
-#, c-format
-msgid " Copy %d of %d"
-msgstr " Kopio %d/%d"
-
-#, c-format
-msgid "Printed: %s"
-msgstr "Tulostettu: %s"
-
-msgid "Printing aborted"
-msgstr "Tulostus peruttu"
-
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: Virhe kirjoitettaessa PostScriptiä tiedostoon"
-
-#, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E624: Ei voi avata tiedostoa %s"
-
-#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: Ei voi lukea PostScript-resurssitiedostoa %s"
-
-#, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E618: tiedosto %s ei ole PostScript-resurssitiedosto"
-
-#, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr "E619: tiedosto %s ei ole tuettu PostScript-resurssitiedosto"
-
-#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr "E621: resurssitiedoston %s versio on väärä"
-
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr "E673: Tukematon monitvauinen merkistökoodaus ja merkistö."
-
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr "E674: printmbcharset ei voi olla tyhjä monitavuiselle koodaukselle."
-
-msgid "E675: No default font specified for multi-byte printing."
-msgstr "E675: Ei oletusfonttia monitavuiseen tulostukseen"
-
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: PostScript-tulostetiedoston avaus ei onnistu"
-
-#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: Tiedoston %s avaus ei onnistu"
-
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E456: PostScript-resurssitiedostoa prolog.ps ei löydy"
-
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr "E456: PostScript-resurssitiedostoa cidfont.ps ei löydy"
-
-#, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E456: Postscript-resurssitiedosta %s.ps ei löydy"
-
-#, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr "E620: Tulostuskoodaukseen %s muunto ei onnistu"
-
-msgid "Sending to printer..."
-msgstr "Lähetetään tulostimelle..."
-
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: PostScript-tiedoston tulostus epäonnistui"
-
-msgid "Print job sent."
-msgstr "Tulostustyö lähetetty."
-
msgid "Add a new database"
msgstr "Lisää uusi tietokanta"
@@ -5369,8 +5276,8 @@ msgstr "E424: Liikaa eri korostusattribuutteja"
msgid "E669: Unprintable character in group name"
msgstr "E669: Tulostuskelvoton merkki ryhmän nimessä"
-msgid "W18: Invalid character in group name"
-msgstr "W18: Virheellinen merkki ryhmän nimessä"
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Virheellinen merkki ryhmän nimessä"
msgid "E849: Too many highlight and syntax groups"
msgstr "E849: Liikaa korostuksia ja syntaksiryhmiä"
diff --git a/src/nvim/po/fixfilenames.vim b/src/nvim/po/fixfilenames.vim
new file mode 100644
index 0000000000..04bc0791c0
--- /dev/null
+++ b/src/nvim/po/fixfilenames.vim
@@ -0,0 +1,13 @@
+" Invoked with the name "vim.pot" and a list of Vim script names.
+" Converts them to a .js file, stripping comments, so that xgettext works.
+
+set shortmess+=A
+
+for name in argv()[1:]
+ let jsname = fnamemodify(name, ":t:r") .. ".js"
+ exe "%s+" .. jsname .. "+" .. substitute(name, '\\', '/', 'g') .. "+"
+endfor
+
+write
+last
+quit
diff --git a/src/nvim/po/fr.po b/src/nvim/po/fr.po
index 614ba013e6..f4fce68ac5 100644
--- a/src/nvim/po/fr.po
+++ b/src/nvim/po/fr.po
@@ -1881,103 +1881,6 @@ msgstr "Style :"
msgid "Size:"
msgstr "Taille :"
-msgid "E550: Missing colon"
-msgstr "E550: ':' manquant"
-
-# DB - Il s'agit ici d'un problme lors du parsing d'une option dont le contenu
-# est une liste d'lments spars par des virgules.
-msgid "E551: Illegal component"
-msgstr "E551: lment invalide"
-
-msgid "E552: digit expected"
-msgstr "E552: chiffre attendu"
-
-#, c-format
-msgid "Page %d"
-msgstr "Page %d"
-
-msgid "No text to be printed"
-msgstr "Aucun texte imprimer"
-
-#, c-format
-msgid "Printing page %d (%d%%)"
-msgstr "Impression de la page %d (%d%%)"
-
-#, c-format
-msgid " Copy %d of %d"
-msgstr " Copie %d sur %d"
-
-#, c-format
-msgid "Printed: %s"
-msgstr "Imprim : %s"
-
-msgid "Printing aborted"
-msgstr "Impression interrompue"
-
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: Erreur lors de l'criture du fichier PostScript"
-
-#, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E624: Impossible d'ouvrir le fichier \"%s\""
-
-#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: Impossible de lire le fichier de ressource PostScript \"%s\""
-
-#, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E618: \"%s\" n'est pas un fichier de ressource PostScript"
-
-#, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr "E619: \"%s\" n'est pas un fichier de ressource PostScript support"
-
-#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr "E621: La version du fichier de ressource \"%s\" est errone"
-
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr "E673: Jeu de caractres et encodage multi-octets incompatibles"
-
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr ""
-"E674: 'printmbcharset' ne peut pas tre vide avec un encodage multi-octets"
-
-msgid "E675: No default font specified for multi-byte printing."
-msgstr "E675: Aucune police par dfaut pour l'impression multi-octets"
-
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: Impossible d'ouvrir le fichier PostScript de sortie"
-
-#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: Impossible d'ouvrir le fichier \"%s\""
-
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E456: Le fichier de ressource PostScript \"prolog.ps\" est introuvable"
-
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr ""
-"E456: Le fichier de ressource PostScript \"cidfont.ps\" est introuvable"
-
-#, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E456: Le fichier de ressource PostScript \"%s.ps\" est introuvable"
-
-#, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr "E620: La conversion pour imprimer dans l'encodage \"%s\" a chou"
-
-msgid "Sending to printer..."
-msgstr "Envoi l'imprimante..."
-
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: L'impression du fichier PostScript a chou"
-
-msgid "Print job sent."
-msgstr "Tche d'impression envoye."
-
msgid "E679: recursive loop loading syncolor.vim"
msgstr "E679: boucle rcursive lors du chargement de syncolor.vim"
@@ -2040,8 +1943,8 @@ msgstr ""
msgid "E669: Unprintable character in group name"
msgstr "E669: Caractre non imprimable dans un nom de groupe"
-msgid "W18: Invalid character in group name"
-msgstr "W18: Caractre invalide dans un nom de groupe"
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Caractre invalide dans un nom de groupe"
msgid "E849: Too many highlight and syntax groups"
msgstr "E849: Trop de groupes de surbrillance et de syntaxe"
diff --git a/src/nvim/po/ga.po b/src/nvim/po/ga.po
index 1c25ee481c..5107aed75f 100644
--- a/src/nvim/po/ga.po
+++ b/src/nvim/po/ga.po
@@ -2385,99 +2385,6 @@ msgstr "Mid:"
msgid "E256: Hangul automata ERROR"
msgstr "E256: EARRID leis na huathoibrein Hangul"
-msgid "E550: Missing colon"
-msgstr "E550: Idirstad ar iarraidh"
-
-msgid "E551: Illegal component"
-msgstr "E551: Comhphirt neamhcheadaithe"
-
-msgid "E552: digit expected"
-msgstr "E552: ag sil le digit"
-
-#, c-format
-msgid "Page %d"
-msgstr "Leathanach %d"
-
-msgid "No text to be printed"
-msgstr "Nl aon tacs le priontil"
-
-#, c-format
-msgid "Printing page %d (%d%%)"
-msgstr "Leathanach %d (%d%%) phriontil"
-
-#, c-format
-msgid " Copy %d of %d"
-msgstr " Cip %d de %d"
-
-#, c-format
-msgid "Printed: %s"
-msgstr "Priontilte: %s"
-
-msgid "Printing aborted"
-msgstr "Priontil tobscortha"
-
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: Earrid le linn scrobh chuig aschomhad PostScript"
-
-#, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E624: N fidir an comhad \"%s\" a oscailt"
-
-#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: N fidir comhad acmhainne PostScript \"%s\" a lamh"
-
-#, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E618: Nl comhad \"%s\" ina chomhad acmhainne PostScript"
-
-#, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr "E619: T \"%s\" ina chomhad acmhainne PostScript gan tac"
-
-#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr "E621: T an leagan mcheart ar an gcomhad acmhainne \"%s\""
-
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr "E673: Ionchd agus tacar carachtar ilbhirt neamh-chomhoirinach."
-
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr ""
-"E674: n cheadatear printmbcharset a bheith folamh le hionchd ilbhirt."
-
-msgid "E675: No default font specified for multi-byte printing."
-msgstr "E675: Nor ramhshocraodh cl le haghaidh priontla ilbhirt."
-
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: N fidir aschomhad PostScript a oscailt"
-
-#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: N fidir an comhad \"%s\" a oscailt"
-
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E456: Comhad acmhainne PostScript \"prolog.ps\" gan aimsi"
-
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr "E456: Comhad acmhainne PostScript \"cidfont.ps\" gan aimsi"
-
-#, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E456: Comhad acmhainne PostScript \"%s.ps\" gan aimsi"
-
-#, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr "E620: N fidir an t-ionchd priontla \"%s\" a thiont"
-
-msgid "Sending to printer..."
-msgstr " sheoladh chuig an phrintir..."
-
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: Theip ar phriontil comhaid PostScript"
-
-msgid "Print job sent."
-msgstr "Seoladh jab priontla."
msgid "Add a new database"
msgstr "Bunachar sonra nua"
@@ -5668,8 +5575,8 @@ msgstr "E424: An iomarca trithe aibhsithe in sid"
msgid "E669: Unprintable character in group name"
msgstr "E669: Carachtar neamhghrafach in ainm grpa"
-msgid "W18: Invalid character in group name"
-msgstr "W18: Carachtar neamhbhail in ainm grpa"
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Carachtar neamhbhail in ainm grpa"
msgid "E849: Too many highlight and syntax groups"
msgstr "E849: An iomarca grpa aibhsithe agus comhrire"
diff --git a/src/nvim/po/it.po b/src/nvim/po/it.po
index 313280c807..2191876724 100644
--- a/src/nvim/po/it.po
+++ b/src/nvim/po/it.po
@@ -3049,126 +3049,6 @@ msgstr "raggiunta la CIMA nella ricerca, continuo dal FONDO"
msgid "search hit BOTTOM, continuing at TOP"
msgstr "raggiunto il FONDO nella ricerca, continuo dalla CIMA"
-#: ../hardcopy.c:240
-msgid "E550: Missing colon"
-msgstr "E550: Manca ':'"
-
-#: ../hardcopy.c:252
-msgid "E551: Illegal component"
-msgstr "E551: Componente non valido"
-
-#: ../hardcopy.c:259
-msgid "E552: digit expected"
-msgstr "E552: aspettavo un numero"
-
-#: ../hardcopy.c:473
-#, c-format
-msgid "Page %d"
-msgstr "Pagina %d"
-
-#: ../hardcopy.c:597
-msgid "No text to be printed"
-msgstr "Manca testo da stampare"
-
-#: ../hardcopy.c:668
-#, c-format
-msgid "Printing page %d (%d%%)"
-msgstr "Sto stampando pagina %d (%d%%)"
-
-#: ../hardcopy.c:680
-#, c-format
-msgid " Copy %d of %d"
-msgstr " Copia %d di %d"
-
-#: ../hardcopy.c:733
-#, c-format
-msgid "Printed: %s"
-msgstr "Stampato: %s"
-
-#: ../hardcopy.c:740
-msgid "Printing aborted"
-msgstr "Stampa non completata"
-
-#: ../hardcopy.c:1365
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: Errore in scrittura a file PostScript di output"
-
-#: ../hardcopy.c:1747
-#, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E624: Non riesco ad aprire il file \"%s\""
-
-#: ../hardcopy.c:1756 ../hardcopy.c:2470
-#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: Non riesco a leggere file risorse PostScript \"%s\""
-
-#: ../hardcopy.c:1772
-#, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E618: file \"%s\" non un file di risorse PostScript"
-
-#: ../hardcopy.c:1788 ../hardcopy.c:1805 ../hardcopy.c:1844
-#, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr "E619: file \"%s\" non un file di risorse PostScript supportato"
-
-#: ../hardcopy.c:1856
-#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr "E621: il file di risorse \"%s\" ha una versione sbagliata"
-
-#: ../hardcopy.c:2225
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr "E673: Codifica e set di caratteri multi-byte non compatibili."
-
-#: ../hardcopy.c:2238
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr "E674: printmbcharset non pu essere nullo con codifica multi-byte."
-
-#: ../hardcopy.c:2254
-msgid "E675: No default font specified for multi-byte printing."
-msgstr "E675: Font predefinito non specificato per stampa multi-byte."
-
-#: ../hardcopy.c:2426
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: Non riesco ad aprire file PostScript di output"
-
-#: ../hardcopy.c:2458
-#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: Non riesco ad aprire il file \"%s\""
-
-#: ../hardcopy.c:2583
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E456: Non trovo file risorse PostScript \"prolog.ps\""
-
-#: ../hardcopy.c:2593
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr "E456: Non trovo file risorse PostScript \"cidfont.ps\""
-
-#: ../hardcopy.c:2622 ../hardcopy.c:2639 ../hardcopy.c:2665
-#, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E456: Non trovo file risorse PostScript \"%s.ps\""
-
-#: ../hardcopy.c:2654
-#, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr "E620: Impossibile convertire a codifica di stampa \"%s\""
-
-#: ../hardcopy.c:2877
-msgid "Sending to printer..."
-msgstr "Invio a stampante..."
-
-#: ../hardcopy.c:2881
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: Non riesco ad aprire file PostScript"
-
-#: ../hardcopy.c:2883
-msgid "Print job sent."
-msgstr "Richiesta di stampa inviata."
-
#: ../if_cscope.c:85
msgid "Add a new database"
msgstr "Aggiungi un nuovo database"
@@ -6046,9 +5926,9 @@ msgstr "E424: Troppi gruppi evidenziazione differenti in uso"
msgid "E669: Unprintable character in group name"
msgstr "E669: Carattere non stampabile in un nome di gruppo"
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
-msgstr "W18: Carattere non ammesso in un nome di gruppo"
+#: ../highlight_group.c:1756
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Carattere non ammesso in un nome di gruppo"
#: ../syntax.c:7448
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/ja.euc-jp.po b/src/nvim/po/ja.euc-jp.po
index d7d0faca80..87b4fc5ccd 100644
--- a/src/nvim/po/ja.euc-jp.po
+++ b/src/nvim/po/ja.euc-jp.po
@@ -2126,101 +2126,6 @@ msgstr ":"
msgid "E256: Hangul automata ERROR"
msgstr "E256: ϥ󥰥륪ȥޥȥ󥨥顼"
-msgid "E550: Missing colon"
-msgstr "E550: 󤬤ޤ"
-
-msgid "E551: Illegal component"
-msgstr "E551: ʹʸǤǤ"
-
-msgid "E552: digit expected"
-msgstr "E552: ͤɬפǤ"
-
-#, c-format
-msgid "Page %d"
-msgstr "%d ڡ"
-
-msgid "No text to be printed"
-msgstr "ƥȤޤ"
-
-#, c-format
-msgid "Printing page %d (%d%%)"
-msgstr ": ڡ %d (%d%%)"
-
-#, c-format
-msgid " Copy %d of %d"
-msgstr " ԡ %d ( %d )"
-
-#, c-format
-msgid "Printed: %s"
-msgstr "ޤ: %s"
-
-msgid "Printing aborted"
-msgstr "ߤޤ"
-
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: PostScriptϥեνߥ顼Ǥ"
-
-#, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E624: ե \"%s\" 򳫤ޤ"
-
-#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: PostScriptΥ꥽ե \"%s\" ɹޤ"
-
-#, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E618: ե \"%s\" PostScript ꥽եǤϤޤ"
-
-#, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr "E619: ե \"%s\" бƤʤ PostScript ꥽եǤ"
-
-#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr "E621: ꥽ե \"%s\" ϥС󤬰ۤʤޤ"
-
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr "E673: ߴ̵ޥХȥ󥳡ǥ󥰤ʸåȤǤ"
-
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr ""
-"E674: ޥХȥ󥳡ǥ󥰤Ǥ printmbcharset ˤǤޤ"
-
-msgid "E675: No default font specified for multi-byte printing."
-msgstr ""
-"E675: ޥХʸ뤿ΥǥեȥեȤꤵƤޤ"
-""
-
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: PostScriptѤΥե򳫤ޤ"
-
-#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: ե \"%s\" 򳫤ޤ"
-
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E456: PostScriptΥ꥽ե \"prolog.ps\" Ĥޤ"
-
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr "E456: PostScriptΥ꥽ե \"cidfont.ps\" Ĥޤ"
-
-#, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E456: PostScriptΥ꥽ե \"%s.ps\" Ĥޤ"
-
-#, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr "E620: 󥳡 \"%s\" ѴǤޤ"
-
-msgid "Sending to printer..."
-msgstr "ץ󥿤..."
-
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: PostScriptեΰ˼Ԥޤ"
-
-msgid "Print job sent."
-msgstr "֤ޤ"
#, c-format
msgid "E799: Invalid ID: %d (must be greater than or equal to 1)"
diff --git a/src/nvim/po/ja.po b/src/nvim/po/ja.po
index b56345e066..bdf67933f3 100644
--- a/src/nvim/po/ja.po
+++ b/src/nvim/po/ja.po
@@ -2126,102 +2126,6 @@ msgstr "サイズ:"
msgid "E256: Hangul automata ERROR"
msgstr "E256: ハングルオートマトンエラー"
-msgid "E550: Missing colon"
-msgstr "E550: コロンがありません"
-
-msgid "E551: Illegal component"
-msgstr "E551: 不正な構文要素です"
-
-msgid "E552: digit expected"
-msgstr "E552: 数値が必要です"
-
-#, c-format
-msgid "Page %d"
-msgstr "%d ページ"
-
-msgid "No text to be printed"
-msgstr "印刷するテキストがありません"
-
-#, c-format
-msgid "Printing page %d (%d%%)"
-msgstr "印刷中: ページ %d (%d%%)"
-
-#, c-format
-msgid " Copy %d of %d"
-msgstr " コピー %d (全 %d 中)"
-
-#, c-format
-msgid "Printed: %s"
-msgstr "印刷しました: %s"
-
-msgid "Printing aborted"
-msgstr "印刷が中止されました"
-
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: PostScript出力ファイルの書込みエラーです"
-
-#, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E624: ファイル \"%s\" を開けません"
-
-#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: PostScriptのリソースファイル \"%s\" を読込めません"
-
-#, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E618: ファイル \"%s\" は PostScript リソースファイルではありません"
-
-#, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr "E619: ファイル \"%s\" は対応していない PostScript リソースファイルです"
-
-#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr "E621: リソースファイル \"%s\" はバージョンが異なります"
-
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr "E673: 互換性の無いマルチバイトエンコーディングと文字セットです。"
-
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr ""
-"E674: マルチバイトエンコーディングでは printmbcharset を空にできません。"
-
-msgid "E675: No default font specified for multi-byte printing."
-msgstr ""
-"E675: マルチバイト文字を印刷するためのデフォルトフォントが指定されていませ"
-"ん。"
-
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: PostScript出力用のファイルを開けません"
-
-#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: ファイル \"%s\" を開けません"
-
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E456: PostScriptのリソースファイル \"prolog.ps\" が見つかりません"
-
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr "E456: PostScriptのリソースファイル \"cidfont.ps\" が見つかりません"
-
-#, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E456: PostScriptのリソースファイル \"%s.ps\" が見つかりません"
-
-#, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr "E620: 印刷エンコード \"%s\" へ変換できません"
-
-msgid "Sending to printer..."
-msgstr "プリンタに送信中..."
-
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: PostScriptファイルの印刷に失敗しました"
-
-msgid "Print job sent."
-msgstr "印刷ジョブを送信しました。"
-
#, c-format
msgid "E799: Invalid ID: %d (must be greater than or equal to 1)"
msgstr "E799: 無効な ID: %d (1 以上でなければなりません)"
diff --git a/src/nvim/po/ko.UTF-8.po b/src/nvim/po/ko.UTF-8.po
index b99c22caeb..8a6b228b18 100644
--- a/src/nvim/po/ko.UTF-8.po
+++ b/src/nvim/po/ko.UTF-8.po
@@ -2973,126 +2973,6 @@ msgstr "처음까지 찾았음, 끝에서 계속"
msgid "search hit BOTTOM, continuing at TOP"
msgstr "끝까지 찾았음, 처음부터 계속"
-#: ../hardcopy.c:240
-msgid "E550: Missing colon"
-msgstr "E550: 콜론이 없습니다"
-
-#: ../hardcopy.c:252
-msgid "E551: Illegal component"
-msgstr "E551: 이상한 컴포넌트"
-
-#: ../hardcopy.c:259
-msgid "E552: digit expected"
-msgstr "E552: 숫자가 필요합니다"
-
-#: ../hardcopy.c:473
-#, c-format
-msgid "Page %d"
-msgstr "페이지 %d"
-
-#: ../hardcopy.c:597
-msgid "No text to be printed"
-msgstr "인쇄될 텍스트가 없습니다"
-
-#: ../hardcopy.c:668
-#, c-format
-msgid "Printing page %d (%d%%)"
-msgstr "페이지 %d 인쇄중 (%d%%)"
-
-#: ../hardcopy.c:680
-#, c-format
-msgid " Copy %d of %d"
-msgstr " 복사 %d / %d"
-
-#: ../hardcopy.c:733
-#, c-format
-msgid "Printed: %s"
-msgstr "인쇄됨: %s"
-
-#: ../hardcopy.c:740
-msgid "Printing aborted"
-msgstr "인쇄가 취소되었습니다."
-
-#: ../hardcopy.c:1365
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: 포스트스크립트 출력파일에 쓸 수 없습니다."
-
-#: ../hardcopy.c:1747
-#, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E624: \"%s\" 파일을 열 수 없습니다"
-
-#: ../hardcopy.c:1756 ../hardcopy.c:2470
-#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: 포스트스크립트 리소스 파일 \"%s\"을(를) 읽을 수 없습니다"
-
-#: ../hardcopy.c:1772
-#, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E618: 파일 \"%s\"은(는) 포스트스크립트 리소스 파일이 아닙니다"
-
-#: ../hardcopy.c:1788 ../hardcopy.c:1805 ../hardcopy.c:1844
-#, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr "E619: 파일 \"%s\"은(는) 지원되는 포스트스크립트 리소스 파일이 아닙니다"
-
-#: ../hardcopy.c:1856
-#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr "E621: \"%s\" 리소스 파일은 버전이 잘못되었습니다"
-
-#: ../hardcopy.c:2225
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr "E673: 호환되지 않는 다중문자 인코딩과 문자셋."
-
-#: ../hardcopy.c:2238
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr "E674: printmbcharset는 다중문자 인코딩에서 반드시 설정되어야 합니다."
-
-#: ../hardcopy.c:2254
-msgid "E675: No default font specified for multi-byte printing."
-msgstr "E675: 다중문자 인쇄를 위한 글꼴이 설정되어 있지 않습니다"
-
-#: ../hardcopy.c:2426
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: 포스트스크립트 출력파일을 열 수 없습니다"
-
-#: ../hardcopy.c:2458
-#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: \"%s\" 파일을 열 수 없습니다"
-
-#: ../hardcopy.c:2583
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E456: 포스트스크립트 리소스 파일 \"prolog.ps\"를 찾을 수 없습니다"
-
-#: ../hardcopy.c:2593
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr "E456: 포스트스크립트 리소스 파일 \"cidfont.ps\"를 찾을 수 없습니다"
-
-#: ../hardcopy.c:2622 ../hardcopy.c:2639 ../hardcopy.c:2665
-#, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E456: 포스트스크립트 리소스 파일 \"%s.ps\"를 찾을 수 없습니다"
-
-#: ../hardcopy.c:2654
-#, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr "E620: \"%s\" 인쇄 인코딩으로 변환할 수 없습니다"
-
-#: ../hardcopy.c:2877
-msgid "Sending to printer..."
-msgstr "프린터로 보내는 중..."
-
-#: ../hardcopy.c:2881
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: 포스트스크립트 파일을 인쇄할 수 없습니다"
-
-#: ../hardcopy.c:2883
-msgid "Print job sent."
-msgstr "인쇄작업이 끝났습니다."
-
#: ../if_cscope.c:85
msgid "Add a new database"
msgstr "새 데이터베이스 더하기"
@@ -5913,9 +5793,9 @@ msgstr "E424: 너무 많은 다른 하이라이트 속성이 사용되고 있습
msgid "E669: Unprintable character in group name"
msgstr "E669: 그룹 이름에 출력할 수 없는 문자가 있습니다"
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
-msgstr "W18: 그룹 이름에 이상한 문자"
+#: ../highlight_group.c:1756
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: 그룹 이름에 이상한 문자"
#: ../syntax.c:7448
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/nb.po b/src/nvim/po/nb.po
index 2285d755cf..75f4eb28a3 100644
--- a/src/nvim/po/nb.po
+++ b/src/nvim/po/nb.po
@@ -2997,126 +2997,6 @@ msgstr "Sket traff TOPPEN, fortsetter fra BUNNEN"
msgid "search hit BOTTOM, continuing at TOP"
msgstr "Sket traff BUNNEN, fortsetter fra TOPPEN"
-#: ../hardcopy.c:240
-msgid "E550: Missing colon"
-msgstr "E550: Mangler kolon"
-
-#: ../hardcopy.c:252
-msgid "E551: Illegal component"
-msgstr "E551: Ulovlig komponent"
-
-#: ../hardcopy.c:259
-msgid "E552: digit expected"
-msgstr "E552: Siffer forventet"
-
-#: ../hardcopy.c:473
-#, c-format
-msgid "Page %d"
-msgstr "Side %d"
-
-#: ../hardcopy.c:597
-msgid "No text to be printed"
-msgstr "Ingen tekst for utskrift"
-
-#: ../hardcopy.c:668
-#, c-format
-msgid "Printing page %d (%d%%)"
-msgstr "Skriver ut side %d (%d%%)"
-
-#: ../hardcopy.c:680
-#, c-format
-msgid " Copy %d of %d"
-msgstr " Kopi %d av %d"
-
-#: ../hardcopy.c:733
-#, c-format
-msgid "Printed: %s"
-msgstr "Skrevet ut: %s"
-
-#: ../hardcopy.c:740
-msgid "Printing aborted"
-msgstr "Utskrift avbrutt"
-
-#: ../hardcopy.c:1365
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: Feil under skriving til Postscript-fil"
-
-#: ../hardcopy.c:1747
-#, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E624: Kan ikke pne filen \"%s\""
-
-#: ../hardcopy.c:1756 ../hardcopy.c:2470
-#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: Kan ikke lese Postscript-ressursfil \"%s\""
-
-#: ../hardcopy.c:1772
-#, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E618: Filen \"%s\" er ikke en Postscript-ressursfil"
-
-#: ../hardcopy.c:1788 ../hardcopy.c:1805 ../hardcopy.c:1844
-#, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr "E619: Det er ikke sttte for Postscript-ressursfilen \"%s\""
-
-#: ../hardcopy.c:1856
-#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr "E621: Ressursfilen \"%s\" er feil versjon"
-
-#: ../hardcopy.c:2225
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr "E673: Inkompatibel multibytekoding og tegnsett"
-
-#: ../hardcopy.c:2238
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr "E674: printmbcharset kan ikke vre tom med multibytekoding"
-
-#: ../hardcopy.c:2254
-msgid "E675: No default font specified for multi-byte printing."
-msgstr "E675: Ingen standardfont spesifisert for multibyteutskrift"
-
-#: ../hardcopy.c:2426
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: Kan ikke pne Postscript-fil for skriving"
-
-#: ../hardcopy.c:2458
-#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: Kan ikke pne filen \"%s\""
-
-#: ../hardcopy.c:2583
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E456: Fant ikke Postscript-ressursfilen \"prolog.ps\""
-
-#: ../hardcopy.c:2593
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr "E456: Fant ikke Postscript-ressursfilen \"cidfont.ps\""
-
-#: ../hardcopy.c:2622 ../hardcopy.c:2639 ../hardcopy.c:2665
-#, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E456: Fant ikke Postscript-ressursfilen \"%s.ps\""
-
-#: ../hardcopy.c:2654
-#, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr "E620: Klarte ikke konvertere til utskriftskoding \"%s\""
-
-#: ../hardcopy.c:2877
-msgid "Sending to printer..."
-msgstr "Sender til skriver..."
-
-#: ../hardcopy.c:2881
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: Feil under utskrift av Postscript-fil"
-
-#: ../hardcopy.c:2883
-msgid "Print job sent."
-msgstr "Skriverjobb sendt."
-
#: ../if_cscope.c:85
msgid "Add a new database"
msgstr "Legg til en ny database"
@@ -5930,9 +5810,9 @@ msgstr "E424: For mange forskjellige uthevingsattributter i bruk"
msgid "E669: Unprintable character in group name"
msgstr "E669: Ikke-skrivbart tegn i gruppenavn"
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
-msgstr "W18: Ugyldig tegn i gruppenavn"
+#: ../highlight_group.c:1756
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Ugyldig tegn i gruppenavn"
#: ../syntax.c:7448
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/nl.po b/src/nvim/po/nl.po
index 00d113c83c..d07c566e28 100644
--- a/src/nvim/po/nl.po
+++ b/src/nvim/po/nl.po
@@ -2985,126 +2985,6 @@ msgstr "zoeken bereikte TOP, verder vanaf BODEM"
msgid "search hit BOTTOM, continuing at TOP"
msgstr "zoeken bereikte BODEM, verder vanaf TOP"
-#: ../hardcopy.c:240
-msgid "E550: Missing colon"
-msgstr "E550: dubbelepunt ontbreekt"
-
-#: ../hardcopy.c:252
-msgid "E551: Illegal component"
-msgstr "E551: ongeldige component"
-
-#: ../hardcopy.c:259
-msgid "E552: digit expected"
-msgstr "E552: cijfer verwacht"
-
-#: ../hardcopy.c:473
-#, c-format
-msgid "Page %d"
-msgstr "Pagina %d"
-
-#: ../hardcopy.c:597
-msgid "No text to be printed"
-msgstr "Geen tekst om af te drukken"
-
-#: ../hardcopy.c:668
-#, c-format
-msgid "Printing page %d (%d%%)"
-msgstr "Afdrukken van pagina %d (%d%%)"
-
-#: ../hardcopy.c:680
-#, c-format
-msgid " Copy %d of %d"
-msgstr "Kopie %d van %d"
-
-#: ../hardcopy.c:733
-#, c-format
-msgid "Printed: %s"
-msgstr "Afgedrukt: %s"
-
-#: ../hardcopy.c:740
-msgid "Printing aborted"
-msgstr "Afdrukken afgebroken"
-
-#: ../hardcopy.c:1365
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: wegschrijven Postscript-uitvoerbestand is mislukt"
-
-#: ../hardcopy.c:1747
-#, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E624: openen bestand \"%s\" is mislukt"
-
-#: ../hardcopy.c:1756 ../hardcopy.c:2470
-#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: kan 'Postscript resource'-bestand \"%s\" niet lezen"
-
-#: ../hardcopy.c:1772
-#, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E618: bestand \"%s\" is geen 'Postscript resource'-bestand"
-
-#: ../hardcopy.c:1788 ../hardcopy.c:1805 ../hardcopy.c:1844
-#, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr "E619: bestand \"%s\" is geen ondersteund 'Postscript resource'-bestand"
-
-#: ../hardcopy.c:1856
-#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr "E621: 'resource'-bestand \"%s\" heeft verkeerde versie"
-
-#: ../hardcopy.c:2225
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr "E673: Multi-byte-codering en de tekenverzameling zijn onverenigbaar."
-
-#: ../hardcopy.c:2238
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr "E674: printmbcharset mag bij multi-byte-codering niet leeg zijn."
-
-#: ../hardcopy.c:2254
-msgid "E675: No default font specified for multi-byte printing."
-msgstr "E675: geen standaard lettertype opgegeven voor multi-byte-afdrukken."
-
-#: ../hardcopy.c:2426
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: openen van PostScript-uitoverbestand is mislukt"
-
-#: ../hardcopy.c:2458
-#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: Bestand \"%s\" kan niet worden geopend"
-
-#: ../hardcopy.c:2583
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E456: 'PostScript resource'-bestand \"prolog.ps\" is niet gevonden"
-
-#: ../hardcopy.c:2593
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr "E456: 'PostScript resource'-bestand \"cidfont.ps\" is niet gevonden"
-
-#: ../hardcopy.c:2622 ../hardcopy.c:2639 ../hardcopy.c:2665
-#, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E456: 'PostScript resource'-bestand \"%s.ps\" is niet gevonden"
-
-#: ../hardcopy.c:2654
-#, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr "E620: omzetten naar afdrukcodering \"%s\" is mislukt"
-
-#: ../hardcopy.c:2877
-msgid "Sending to printer..."
-msgstr "Naar printer versturen..."
-
-#: ../hardcopy.c:2881
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: Afdrukken van PostScript-bestand is mislukt"
-
-#: ../hardcopy.c:2883
-msgid "Print job sent."
-msgstr "Afdrukopdracht verzonden"
-
#: ../if_cscope.c:85
msgid "Add a new database"
msgstr "Nieuwe databank toevoegen"
@@ -5913,8 +5793,8 @@ msgstr ""
msgid "E669: Unprintable character in group name"
msgstr ""
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
+#: ../highlight_group.c:1756
+msgid "E5248: Invalid character in group name"
msgstr ""
#: ../syntax.c:7448
diff --git a/src/nvim/po/no.po b/src/nvim/po/no.po
index 2285d755cf..75f4eb28a3 100644
--- a/src/nvim/po/no.po
+++ b/src/nvim/po/no.po
@@ -2997,126 +2997,6 @@ msgstr "Sket traff TOPPEN, fortsetter fra BUNNEN"
msgid "search hit BOTTOM, continuing at TOP"
msgstr "Sket traff BUNNEN, fortsetter fra TOPPEN"
-#: ../hardcopy.c:240
-msgid "E550: Missing colon"
-msgstr "E550: Mangler kolon"
-
-#: ../hardcopy.c:252
-msgid "E551: Illegal component"
-msgstr "E551: Ulovlig komponent"
-
-#: ../hardcopy.c:259
-msgid "E552: digit expected"
-msgstr "E552: Siffer forventet"
-
-#: ../hardcopy.c:473
-#, c-format
-msgid "Page %d"
-msgstr "Side %d"
-
-#: ../hardcopy.c:597
-msgid "No text to be printed"
-msgstr "Ingen tekst for utskrift"
-
-#: ../hardcopy.c:668
-#, c-format
-msgid "Printing page %d (%d%%)"
-msgstr "Skriver ut side %d (%d%%)"
-
-#: ../hardcopy.c:680
-#, c-format
-msgid " Copy %d of %d"
-msgstr " Kopi %d av %d"
-
-#: ../hardcopy.c:733
-#, c-format
-msgid "Printed: %s"
-msgstr "Skrevet ut: %s"
-
-#: ../hardcopy.c:740
-msgid "Printing aborted"
-msgstr "Utskrift avbrutt"
-
-#: ../hardcopy.c:1365
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: Feil under skriving til Postscript-fil"
-
-#: ../hardcopy.c:1747
-#, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E624: Kan ikke pne filen \"%s\""
-
-#: ../hardcopy.c:1756 ../hardcopy.c:2470
-#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: Kan ikke lese Postscript-ressursfil \"%s\""
-
-#: ../hardcopy.c:1772
-#, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E618: Filen \"%s\" er ikke en Postscript-ressursfil"
-
-#: ../hardcopy.c:1788 ../hardcopy.c:1805 ../hardcopy.c:1844
-#, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr "E619: Det er ikke sttte for Postscript-ressursfilen \"%s\""
-
-#: ../hardcopy.c:1856
-#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr "E621: Ressursfilen \"%s\" er feil versjon"
-
-#: ../hardcopy.c:2225
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr "E673: Inkompatibel multibytekoding og tegnsett"
-
-#: ../hardcopy.c:2238
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr "E674: printmbcharset kan ikke vre tom med multibytekoding"
-
-#: ../hardcopy.c:2254
-msgid "E675: No default font specified for multi-byte printing."
-msgstr "E675: Ingen standardfont spesifisert for multibyteutskrift"
-
-#: ../hardcopy.c:2426
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: Kan ikke pne Postscript-fil for skriving"
-
-#: ../hardcopy.c:2458
-#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: Kan ikke pne filen \"%s\""
-
-#: ../hardcopy.c:2583
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E456: Fant ikke Postscript-ressursfilen \"prolog.ps\""
-
-#: ../hardcopy.c:2593
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr "E456: Fant ikke Postscript-ressursfilen \"cidfont.ps\""
-
-#: ../hardcopy.c:2622 ../hardcopy.c:2639 ../hardcopy.c:2665
-#, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E456: Fant ikke Postscript-ressursfilen \"%s.ps\""
-
-#: ../hardcopy.c:2654
-#, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr "E620: Klarte ikke konvertere til utskriftskoding \"%s\""
-
-#: ../hardcopy.c:2877
-msgid "Sending to printer..."
-msgstr "Sender til skriver..."
-
-#: ../hardcopy.c:2881
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: Feil under utskrift av Postscript-fil"
-
-#: ../hardcopy.c:2883
-msgid "Print job sent."
-msgstr "Skriverjobb sendt."
-
#: ../if_cscope.c:85
msgid "Add a new database"
msgstr "Legg til en ny database"
@@ -5930,9 +5810,9 @@ msgstr "E424: For mange forskjellige uthevingsattributter i bruk"
msgid "E669: Unprintable character in group name"
msgstr "E669: Ikke-skrivbart tegn i gruppenavn"
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
-msgstr "W18: Ugyldig tegn i gruppenavn"
+#: ../highlight_group.c:1756
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Ugyldig tegn i gruppenavn"
#: ../syntax.c:7448
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/pl.UTF-8.po b/src/nvim/po/pl.UTF-8.po
index 5f1779d1bd..c00130ac8c 100644
--- a/src/nvim/po/pl.UTF-8.po
+++ b/src/nvim/po/pl.UTF-8.po
@@ -2954,126 +2954,6 @@ msgstr "szukanie dobiło GÓRY; kontynuacja od KOŃCA"
msgid "search hit BOTTOM, continuing at TOP"
msgstr "szukanie dobiło KOŃCA; kontynuacja od GÓRY"
-#: ../hardcopy.c:240
-msgid "E550: Missing colon"
-msgstr "E550: Brak dwukropka"
-
-#: ../hardcopy.c:252
-msgid "E551: Illegal component"
-msgstr "E551: Niedozwolona część"
-
-#: ../hardcopy.c:259
-msgid "E552: digit expected"
-msgstr "E552: oczekiwałem na cyfrę"
-
-#: ../hardcopy.c:473
-#, c-format
-msgid "Page %d"
-msgstr "Strona %d"
-
-#: ../hardcopy.c:597
-msgid "No text to be printed"
-msgstr "Brak tekstu do drukowania"
-
-#: ../hardcopy.c:668
-#, c-format
-msgid "Printing page %d (%d%%)"
-msgstr "Drukuję stronę %d (%d%%)"
-
-#: ../hardcopy.c:680
-#, c-format
-msgid " Copy %d of %d"
-msgstr " Kopia %d z %d"
-
-#: ../hardcopy.c:733
-#, c-format
-msgid "Printed: %s"
-msgstr "Wydrukowano: %s"
-
-#: ../hardcopy.c:740
-msgid "Printing aborted"
-msgstr "Drukowanie odwołane"
-
-#: ../hardcopy.c:1365
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: Nie można zapisać do wyjściowego pliku PostScriptu"
-
-#: ../hardcopy.c:1747
-#, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E624: Nie mogę otworzyć pliku \"%s\""
-
-#: ../hardcopy.c:1756 ../hardcopy.c:2470
-#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: Nie można odczytać pliku zasobów PostScriptu \"%s\""
-
-#: ../hardcopy.c:1772
-#, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E618: plik \"%s\" nie jest plikiem zasobów PostScriptu"
-
-#: ../hardcopy.c:1788 ../hardcopy.c:1805 ../hardcopy.c:1844
-#, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr "E619: plik \"%s\" nie jest wspieranym plikiem zasobów PostScriptu"
-
-#: ../hardcopy.c:1856
-#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr "E621: \"%s\" nieprawidłowa wersja pliku zasobów"
-
-#: ../hardcopy.c:2225
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr "E673: Niekompatybilne kodowanie wielobajtowe i zestaw znaków."
-
-#: ../hardcopy.c:2238
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr "E674: printmbcharset nie może być pusty przy kodowaniu wielobajtowym."
-
-#: ../hardcopy.c:2254
-msgid "E675: No default font specified for multi-byte printing."
-msgstr "E675: Nie określono domyślnej czcionki dla drukowania wielobajtowego."
-
-#: ../hardcopy.c:2426
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: Nie można otworzyć pliku PostScript do wyjścia"
-
-#: ../hardcopy.c:2458
-#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: Nie mogę otworzyć pliku \"%s\""
-
-#: ../hardcopy.c:2583
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E456: Nie można znaleźć pliku zasobów PostScriptu \"prolog.ps\""
-
-#: ../hardcopy.c:2593
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr "E456: Nie można znaleźć pliku zasobów PostScriptu \"cidfont.ps\""
-
-#: ../hardcopy.c:2622 ../hardcopy.c:2639 ../hardcopy.c:2665
-#, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E456: Nie można znaleźć pliku zasobów PostScriptu \"%s.ps\""
-
-#: ../hardcopy.c:2654
-#, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr "E620: Nie można przekonwertować by drukować kodowanie \"%s\""
-
-#: ../hardcopy.c:2877
-msgid "Sending to printer..."
-msgstr "Przesyłam do drukarki..."
-
-#: ../hardcopy.c:2881
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: Drukowanie pliku PostScript nie powiodło się"
-
-#: ../hardcopy.c:2883
-msgid "Print job sent."
-msgstr "Zadanie drukowanie przesłane."
-
#: ../if_cscope.c:85
msgid "Add a new database"
msgstr "Dodaj nową bazę danych"
@@ -5908,9 +5788,9 @@ msgstr "E424: Zbyt wiele różnych atrybutów podkreślania w użyciu"
msgid "E669: Unprintable character in group name"
msgstr "E669: Niedrukowalny znak w nazwie grupy"
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
-msgstr "W18: nieprawidłowy znak w nazwie grupy"
+#: ../highlight_group.c:1756
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: nieprawidłowy znak w nazwie grupy"
#: ../syntax.c:7448
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/pt_BR.po b/src/nvim/po/pt_BR.po
index 533d916de1..ca5a4508c7 100644
--- a/src/nvim/po/pt_BR.po
+++ b/src/nvim/po/pt_BR.po
@@ -4417,129 +4417,6 @@ msgstr "E663: No final da lista de modificaes"
msgid "Type :quit<Enter> to exit Nvim"
msgstr "Digite :quit<Enter> para sair do Vim"
-#: ../hardcopy.c:296
-msgid "E550: Missing colon"
-msgstr "E550: Dois-pontos faltando"
-
-#: ../hardcopy.c:308
-msgid "E551: Illegal component"
-msgstr "E551: Elemento invlido"
-
-#: ../hardcopy.c:315
-msgid "E552: digit expected"
-msgstr "E552: era esperado um algarismo"
-
-#: ../hardcopy.c:529
-#, c-format
-msgid "Page %d"
-msgstr "Pgina %d"
-
-#: ../hardcopy.c:653
-msgid "No text to be printed"
-msgstr "Sem texto para imprimir"
-
-#: ../hardcopy.c:724
-#, c-format
-msgid "Printing page %d (%d%%)"
-msgstr "Imprimindo pgina %d (%d%%)"
-
-#: ../hardcopy.c:736
-#, c-format
-msgid " Copy %d of %d"
-msgstr " Cpia %d de %d"
-
-#: ../hardcopy.c:789
-#, c-format
-msgid "Printed: %s"
-msgstr "Impresso: %s"
-
-#: ../hardcopy.c:796
-msgid "Printing aborted"
-msgstr "Impresso cancelada"
-
-#: ../hardcopy.c:1307
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: Erro ao escrever no arquivo PostScript"
-
-#: ../hardcopy.c:1679
-#, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E624: Impossvel abrir arquivo \"%s\""
-
-#: ../hardcopy.c:1688 ../hardcopy.c:2402
-#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: Impossvel ler o arquivo de recursos de PostScript \"%s\""
-
-#: ../hardcopy.c:1704
-#, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E618: arquivo \"%s\" no um arquivo de recursos de PostScript"
-
-#: ../hardcopy.c:1720 ../hardcopy.c:1737 ../hardcopy.c:1776
-#, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr ""
-"E619: arquivo \"%s\" no um arquivo de recursos de PostScript suportado"
-
-#: ../hardcopy.c:1788
-#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr "E621: verso errada do arquivo de recursos \"%s\""
-
-#: ../hardcopy.c:2157
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr ""
-"E673: Codificao multi-byte incompatvel com o conjunto de caracteres."
-
-#: ../hardcopy.c:2170
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr ""
-"E674: 'printmbcharset' no pode estar vazio com codificaes multi-byte."
-
-#: ../hardcopy.c:2186
-msgid "E675: No default font specified for multi-byte printing."
-msgstr "E675: Nenhuma fonte padro especificada para impresso em multi-byte."
-
-#: ../hardcopy.c:2358
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: Impossvel abrir arquivo PostScript para sada"
-
-#: ../hardcopy.c:2390
-#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: Impossvel abrir arquivo \"%s\""
-
-#: ../hardcopy.c:2515
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E456: Arquivo de recursos de PostScript \"prolog.ps\" no encontrado"
-
-#: ../hardcopy.c:2525
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr "E456: Arquivo de recursos de PostScript \"cidfont.ps\" no encontrado"
-
-#: ../hardcopy.c:2554 ../hardcopy.c:2571 ../hardcopy.c:2597
-#, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E456: Arquivo de recursos de PostScript \"%s.ps\" no encontrado"
-
-#: ../hardcopy.c:2586
-#, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr "E620: Impossvel converter para a codificao \"%s\" para impresso"
-
-#: ../hardcopy.c:2809
-msgid "Sending to printer..."
-msgstr "Enviando impressora..."
-
-#: ../hardcopy.c:2813
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: No foi possvel imprimir o arquivo PostScript"
-
-#: ../hardcopy.c:2815
-msgid "Print job sent."
-msgstr "Trabalho de impresso enviado."
-
#. This happens when the FileChangedRO autocommand changes the
#. * file in a way it becomes shorter.
#: ../undo.c:355
@@ -6070,8 +5947,8 @@ msgid "E669: Unprintable character in group name"
msgstr "E669: Caractere no-imprimvel no nome do grupo"
#: ../syntax.c:7320
-msgid "W18: Invalid character in group name"
-msgstr "W18: Caractere invlido no nome do grupo"
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Caractere invlido no nome do grupo"
#: ../syntax.c:7334
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/ru.po b/src/nvim/po/ru.po
index 7566036d3e..1235111c58 100644
--- a/src/nvim/po/ru.po
+++ b/src/nvim/po/ru.po
@@ -2979,126 +2979,6 @@ msgstr "Поиск будет продолжен с КОНЦА документ
msgid "search hit BOTTOM, continuing at TOP"
msgstr "Поиск будет продолжен с НАЧАЛА документа"
-#: ../hardcopy.c:240
-msgid "E550: Missing colon"
-msgstr "E550: Пропущено двоеточие"
-
-#: ../hardcopy.c:252
-msgid "E551: Illegal component"
-msgstr "E551: Недопустимый компонент"
-
-#: ../hardcopy.c:259
-msgid "E552: digit expected"
-msgstr "E552: Требуется указать цифру"
-
-#: ../hardcopy.c:473
-#, c-format
-msgid "Page %d"
-msgstr "Страница %d"
-
-#: ../hardcopy.c:597
-msgid "No text to be printed"
-msgstr "Печатать нечего"
-
-#: ../hardcopy.c:668
-#, c-format
-msgid "Printing page %d (%d%%)"
-msgstr "Печать стр. %d (%d%%)"
-
-#: ../hardcopy.c:680
-#, c-format
-msgid " Copy %d of %d"
-msgstr " Копия %d из %d"
-
-#: ../hardcopy.c:733
-#, c-format
-msgid "Printed: %s"
-msgstr "Напечатано: %s"
-
-#: ../hardcopy.c:740
-msgid "Printing aborted"
-msgstr "Печать прекращена"
-
-#: ../hardcopy.c:1365
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: Ошибка записи в файл PostScript"
-
-#: ../hardcopy.c:1747
-#, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E624: Невозможно открыть файл \"%s\""
-
-#: ../hardcopy.c:1756 ../hardcopy.c:2470
-#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: Невозможно прочитать файл ресурсов PostScript \"%s\""
-
-#: ../hardcopy.c:1772
-#, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E618: Файл \"%s\" не является файлом ресурсов PostScript"
-
-#: ../hardcopy.c:1788 ../hardcopy.c:1805 ../hardcopy.c:1844
-#, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr "E619: Файл \"%s\" не является допустимым файлом ресурсов PostScript"
-
-#: ../hardcopy.c:1856
-#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr "E621: Файл ресурсов \"%s\" неизвестной версии"
-
-#: ../hardcopy.c:2225
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr "E673: Несовместимые многобайтовая кодировка и набор символов."
-
-#: ../hardcopy.c:2238
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr "E674: printmbcharset не может быть пустым при многобайтовой кодировке."
-
-#: ../hardcopy.c:2254
-msgid "E675: No default font specified for multi-byte printing."
-msgstr "E675: Нет определения шрифта по умолчанию для многобайтовой печати."
-
-#: ../hardcopy.c:2426
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: Невозможно открыть файл PostScript"
-
-#: ../hardcopy.c:2458
-#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: Невозможно открыть файл \"%s\""
-
-#: ../hardcopy.c:2583
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E456: Файл ресурсов PostScript \"prolog.ps\" не найден"
-
-#: ../hardcopy.c:2593
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr "E456: Файл ресурсов PostScript \"cidfont.ps\" не найден"
-
-#: ../hardcopy.c:2622 ../hardcopy.c:2639 ../hardcopy.c:2665
-#, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E456: Файл ресурсов PostScript \"%s.ps\" не найден"
-
-#: ../hardcopy.c:2654
-#, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr "E620: Невозможно преобразовать в кодировку печать \"%s\""
-
-#: ../hardcopy.c:2877
-msgid "Sending to printer..."
-msgstr "Отправка на печать..."
-
-#: ../hardcopy.c:2881
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: Не удалось выполнить печать файла PostScript"
-
-#: ../hardcopy.c:2883
-msgid "Print job sent."
-msgstr "Задание на печать отправлено."
-
#: ../if_cscope.c:85
msgid "Add a new database"
msgstr "Добавить новую базу данных"
@@ -5977,9 +5857,9 @@ msgstr "E424: Используется слишком много разных а
msgid "E669: Unprintable character in group name"
msgstr "E669: Непечатный символ в имени группы"
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
-msgstr "W18: Недопустимый символ в имени группы"
+#: ../highlight_group.c:1756
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Недопустимый символ в имени группы"
#: ../syntax.c:7448
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/sk.cp1250.po b/src/nvim/po/sk.cp1250.po
index ff95c68a12..f0fdc47a1e 100644
--- a/src/nvim/po/sk.cp1250.po
+++ b/src/nvim/po/sk.cp1250.po
@@ -2994,127 +2994,6 @@ msgstr "hadanie dosiahlo zaiatok, pokraovanie od konca"
msgid "search hit BOTTOM, continuing at TOP"
msgstr "hadanie dosiahlo koniec, pokraovanie od zaiatku"
-#: ../hardcopy.c:240
-msgid "E550: Missing colon"
-msgstr "E550: Chba dvojbodka"
-
-#: ../hardcopy.c:252
-msgid "E551: Illegal component"
-msgstr "E551: Neprpustn komponent"
-
-#: ../hardcopy.c:259
-msgid "E552: digit expected"
-msgstr "E552: oakvan slica"
-
-#: ../hardcopy.c:473
-#, c-format
-msgid "Page %d"
-msgstr "Strana %d"
-
-#: ../hardcopy.c:597
-msgid "No text to be printed"
-msgstr "iadny text na tla"
-
-#: ../hardcopy.c:668
-#, c-format
-msgid "Printing page %d (%d%%)"
-msgstr "Tlam stranu %d (%d%%)"
-
-#: ../hardcopy.c:680
-#, c-format
-msgid " Copy %d of %d"
-msgstr " Kpia %d z %d"
-
-#: ../hardcopy.c:733
-#, c-format
-msgid "Printed: %s"
-msgstr "Vytlaen: %s"
-
-#: ../hardcopy.c:740
-msgid "Printing aborted"
-msgstr "Tla bola zruen"
-
-#: ../hardcopy.c:1365
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: Ned sa zapisova do vstupnho PostScriptovho sboru"
-
-#: ../hardcopy.c:1747
-#, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E624: Ned sa otvori sbor \"%s\""
-
-#: ../hardcopy.c:1756 ../hardcopy.c:2470
-#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: Ned sa ta PostScriptov sbor \"%s\""
-
-#: ../hardcopy.c:1772
-#, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E618: sbor \"%s\" nie je vo formte PostScript"
-
-#: ../hardcopy.c:1788 ../hardcopy.c:1805 ../hardcopy.c:1844
-#, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr "E619: sbor \"%s\" nie je podporvan PostScriptov sbor"
-
-#: ../hardcopy.c:1856
-#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr "E621: \"%s\" zdrojov sbor m zl slo verzie"
-
-#: ../hardcopy.c:2225
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr "E673: nekompatibiln viacbajtov kdovanie a znakov sada."
-
-#: ../hardcopy.c:2238
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr ""
-"E674: voba printmbcharset neme by przdna pri viacbajtovom kdovan."
-
-#: ../hardcopy.c:2254
-msgid "E675: No default font specified for multi-byte printing."
-msgstr "E675: Nie je pecifikovan iadne psmo pre viacbajtov tlaenie."
-
-#: ../hardcopy.c:2426
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: Ned sa otvori vstupn PostScriptov sbor"
-
-#: ../hardcopy.c:2458
-#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: Ned sa otvori sbor \"%s\""
-
-#: ../hardcopy.c:2583
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E456: Nemono njs PostScriptov zdrojov sbor \"prolog.ps\""
-
-#: ../hardcopy.c:2593
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr "E456: Nemono njs PostScriptov zdrojov sbor \"cidfont.ps\""
-
-#: ../hardcopy.c:2622 ../hardcopy.c:2639 ../hardcopy.c:2665
-#, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E456: Nemono njs PostScriptov zdrojov sbor \"%s.ps\""
-
-#: ../hardcopy.c:2654
-#, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr "E620: Nemono skonvertova do kdovania na tlaenie \"%s\""
-
-#: ../hardcopy.c:2877
-msgid "Sending to printer..."
-msgstr "Posielam na tlaiare..."
-
-#: ../hardcopy.c:2881
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: PostScriptov sbor sa nepodarilo vytlai"
-
-#: ../hardcopy.c:2883
-msgid "Print job sent."
-msgstr "Tlaov loha bola odoslan."
-
#: ../if_cscope.c:85
msgid "Add a new database"
msgstr "Prida nov databzu"
@@ -5940,9 +5819,9 @@ msgstr "E424: Pouvanch prli vea odlinch zvrazovacch vlastnost"
msgid "E669: Unprintable character in group name"
msgstr "E669: Netlaiteln znak v mene skupiny"
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
-msgstr "W18: Chybn znak v mene skupiny"
+#: ../highlight_group.c:1756
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Chybn znak v mene skupiny"
#: ../syntax.c:7448
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/sk.po b/src/nvim/po/sk.po
index d35622726f..2d6b4ed901 100644
--- a/src/nvim/po/sk.po
+++ b/src/nvim/po/sk.po
@@ -2994,127 +2994,6 @@ msgstr "hadanie dosiahlo zaiatok, pokraovanie od konca"
msgid "search hit BOTTOM, continuing at TOP"
msgstr "hadanie dosiahlo koniec, pokraovanie od zaiatku"
-#: ../hardcopy.c:240
-msgid "E550: Missing colon"
-msgstr "E550: Chba dvojbodka"
-
-#: ../hardcopy.c:252
-msgid "E551: Illegal component"
-msgstr "E551: Neprpustn komponent"
-
-#: ../hardcopy.c:259
-msgid "E552: digit expected"
-msgstr "E552: oakvan slica"
-
-#: ../hardcopy.c:473
-#, c-format
-msgid "Page %d"
-msgstr "Strana %d"
-
-#: ../hardcopy.c:597
-msgid "No text to be printed"
-msgstr "iadny text na tla"
-
-#: ../hardcopy.c:668
-#, c-format
-msgid "Printing page %d (%d%%)"
-msgstr "Tlam stranu %d (%d%%)"
-
-#: ../hardcopy.c:680
-#, c-format
-msgid " Copy %d of %d"
-msgstr " Kpia %d z %d"
-
-#: ../hardcopy.c:733
-#, c-format
-msgid "Printed: %s"
-msgstr "Vytlaen: %s"
-
-#: ../hardcopy.c:740
-msgid "Printing aborted"
-msgstr "Tla bola zruen"
-
-#: ../hardcopy.c:1365
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: Ned sa zapisova do vstupnho PostScriptovho sboru"
-
-#: ../hardcopy.c:1747
-#, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E624: Ned sa otvori sbor \"%s\""
-
-#: ../hardcopy.c:1756 ../hardcopy.c:2470
-#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: Ned sa ta PostScriptov sbor \"%s\""
-
-#: ../hardcopy.c:1772
-#, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E618: sbor \"%s\" nie je vo formte PostScript"
-
-#: ../hardcopy.c:1788 ../hardcopy.c:1805 ../hardcopy.c:1844
-#, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr "E619: sbor \"%s\" nie je podporvan PostScriptov sbor"
-
-#: ../hardcopy.c:1856
-#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr "E621: \"%s\" zdrojov sbor m zl slo verzie"
-
-#: ../hardcopy.c:2225
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr "E673: nekompatibiln viacbajtov kdovanie a znakov sada."
-
-#: ../hardcopy.c:2238
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr ""
-"E674: voba printmbcharset neme by przdna pri viacbajtovom kdovan."
-
-#: ../hardcopy.c:2254
-msgid "E675: No default font specified for multi-byte printing."
-msgstr "E675: Nie je pecifikovan iadne psmo pre viacbajtov tlaenie."
-
-#: ../hardcopy.c:2426
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: Ned sa otvori vstupn PostScriptov sbor"
-
-#: ../hardcopy.c:2458
-#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: Ned sa otvori sbor \"%s\""
-
-#: ../hardcopy.c:2583
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E456: Nemono njs PostScriptov zdrojov sbor \"prolog.ps\""
-
-#: ../hardcopy.c:2593
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr "E456: Nemono njs PostScriptov zdrojov sbor \"cidfont.ps\""
-
-#: ../hardcopy.c:2622 ../hardcopy.c:2639 ../hardcopy.c:2665
-#, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E456: Nemono njs PostScriptov zdrojov sbor \"%s.ps\""
-
-#: ../hardcopy.c:2654
-#, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr "E620: Nemono skonvertova do kdovania na tlaenie \"%s\""
-
-#: ../hardcopy.c:2877
-msgid "Sending to printer..."
-msgstr "Posielam na tlaiare..."
-
-#: ../hardcopy.c:2881
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: PostScriptov sbor sa nepodarilo vytlai"
-
-#: ../hardcopy.c:2883
-msgid "Print job sent."
-msgstr "Tlaov loha bola odoslan."
-
#: ../if_cscope.c:85
msgid "Add a new database"
msgstr "Prida nov databzu"
@@ -5940,9 +5819,9 @@ msgstr "E424: Pouvanch prli vea odlinch zvrazovacch vlastnost"
msgid "E669: Unprintable character in group name"
msgstr "E669: Netlaiteln znak v mene skupiny"
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
-msgstr "W18: Chybn znak v mene skupiny"
+#: ../highlight_group.c:1756
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Chybn znak v mene skupiny"
#: ../syntax.c:7448
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/sr.po b/src/nvim/po/sr.po
index 3c45e1bf80..4a9dcf4a5d 100644
--- a/src/nvim/po/sr.po
+++ b/src/nvim/po/sr.po
@@ -2573,100 +2573,6 @@ msgstr "Стил:"
msgid "Size:"
msgstr "Величина:"
-msgid "E550: Missing colon"
-msgstr "E550: Недостаје двотачка"
-
-msgid "E551: Illegal component"
-msgstr "E551: Неисправна компонента"
-
-msgid "E552: digit expected"
-msgstr "E552: очекује се цифра"
-
-#, c-format
-msgid "Page %d"
-msgstr "Страна %d"
-
-msgid "No text to be printed"
-msgstr "Нема текста за штампу"
-
-#, c-format
-msgid "Printing page %d (%d%%)"
-msgstr "Штампање стране %d (%d%%)"
-
-#, c-format
-msgid " Copy %d of %d"
-msgstr " Копија %d од %d"
-
-#, c-format
-msgid "Printed: %s"
-msgstr "Одштампано: %s"
-
-msgid "Printing aborted"
-msgstr "Штампање прекинуто"
-
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: Грешка приликом уписа у PostScript излазну датотеку"
-
-#, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E624: Датотека \"%s\" не може да се отвори"
-
-#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: PostScript resource датотека \"%s\" не може да се чита"
-
-#, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E618: датотека \"%s\" није PostScript resource датотека"
-
-#, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr "E619: датотека \"%s\" није подржана PostScript resource датотека"
-
-#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr "E621: \"%s\" resource датотека је погрешне верзије"
-
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr "E673: Вишебајтно кодирање и скуп карактера нису компатибилни."
-
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr "E674: printmbcharset не може бити празно са вишебајтним кодирањем."
-
-msgid "E675: No default font specified for multi-byte printing."
-msgstr "E675: Није наведен подразумевани фонт за вишебајтно штампање."
-
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: PostScript излазна датотека не може да се отвори"
-
-#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: Датотека \"%s\" не може да се отвори"
-
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E456: PostScript resource датотека \"prolog.ps\" не може да се пронађе"
-
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr ""
-"E456: PostScript resource датотека \"cidfont.ps\" не може да се пронађе"
-
-#, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E456: PostScript resource датотека \"%s.ps\" не може да се пронађе"
-
-#, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr "E620: Није могућа конверзија у кодирање за штампу \"%s\""
-
-msgid "Sending to printer..."
-msgstr "Слање штампачу..."
-
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: PostScript датотека није успела да се одштампа"
-
-msgid "Print job sent."
-msgstr "Задатак штампе је послат"
-
msgid "Add a new database"
msgstr "Додај нову базу"
@@ -6050,8 +5956,8 @@ msgstr "E424: У употреби је превише различитих ат
msgid "E669: Unprintable character in group name"
msgstr "E669: У имену групе је карактер који не може да се штампа"
-msgid "W18: Invalid character in group name"
-msgstr "W18: Неважећи карактер у имену групе"
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Неважећи карактер у имену групе"
msgid "E849: Too many highlight and syntax groups"
msgstr "E849: Превише синтаксних и група истицања"
diff --git a/src/nvim/po/sv.po b/src/nvim/po/sv.po
index d50c9d695d..e46579c067 100644
--- a/src/nvim/po/sv.po
+++ b/src/nvim/po/sv.po
@@ -2056,8 +2056,8 @@ msgid "E669: Unprintable character in group name"
msgstr "E669: Outskrivbart tecken i gruppnamn"
#: ../syntax.c:7292
-msgid "W18: Invalid character in group name"
-msgstr "W18: Ogiltigt tecken i gruppnamn"
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Ogiltigt tecken i gruppnamn"
#: ../syntax.c:7306
msgid "E849: Too many highlight and syntax groups"
@@ -4168,126 +4168,6 @@ msgstr "E782: fel vid lsning av .sug-fil: %s"
msgid "E783: duplicate char in MAP entry"
msgstr "E783: dubblerat tecken i MAP-post"
-#: ../hardcopy.c:296
-msgid "E550: Missing colon"
-msgstr "E550: Kolon saknas"
-
-#: ../hardcopy.c:308
-msgid "E551: Illegal component"
-msgstr "E551: Otillten komponent"
-
-#: ../hardcopy.c:315
-msgid "E552: digit expected"
-msgstr "E552: siffra frvntades"
-
-#: ../hardcopy.c:529
-#, c-format
-msgid "Page %d"
-msgstr "Sida %d"
-
-#: ../hardcopy.c:653
-msgid "No text to be printed"
-msgstr "Ingen text att skriva ut"
-
-#: ../hardcopy.c:724
-#, c-format
-msgid "Printing page %d (%d%%)"
-msgstr "Skriver ut sida %d (%d%%)"
-
-#: ../hardcopy.c:736
-#, c-format
-msgid " Copy %d of %d"
-msgstr " Kopia %d av %d"
-
-#: ../hardcopy.c:789
-#, c-format
-msgid "Printed: %s"
-msgstr "Skrev ut: %s"
-
-#: ../hardcopy.c:796
-msgid "Printing aborted"
-msgstr "Utskrift avbruten"
-
-#: ../hardcopy.c:1307
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: Fel vid skrivning av utdata till PostScript-fil"
-
-#: ../hardcopy.c:1679
-#, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E624: Kan inte ppna fil \"%s\""
-
-#: ../hardcopy.c:1688 ../hardcopy.c:2401
-#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: Kan inte lsa PostScript-resursfil \"%s\""
-
-#: ../hardcopy.c:1704
-#, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E618: fil \"%s\" r inte en PostScript-resursfil"
-
-#: ../hardcopy.c:1720 ../hardcopy.c:1737 ../hardcopy.c:1776
-#, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr "E619: fil \"%s\" r inte en PostScript-resursfil som stds"
-
-#: ../hardcopy.c:1788
-#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr "E621: \"%s\"-kllfilen har fel version"
-
-#: ../hardcopy.c:2157
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr "E673: Inkompatibel flerbitskodning och teckenuppsttning."
-
-#: ../hardcopy.c:2169
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr "E674: printmbcharset kan inte vara tom med flerbitskodning."
-
-#: ../hardcopy.c:2185
-msgid "E675: No default font specified for multi-byte printing."
-msgstr "E675: Ingen standardfont angiven fr flerbitsutskrifter."
-
-#: ../hardcopy.c:2357
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: Kan inte ppna PostScript-utfil"
-
-#: ../hardcopy.c:2389
-#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: Kan inte ppna fil \"%s\""
-
-#: ../hardcopy.c:2514
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E456: Kan inte hitta PostScript-kllfilen \"prolog.ps\""
-
-#: ../hardcopy.c:2524
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr "E456: Kan inte hitta PostScript-kllfilen \"cidfont.ps\""
-
-#: ../hardcopy.c:2553 ../hardcopy.c:2570 ../hardcopy.c:2596
-#, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E456: Kan inte hitta PostScript-kllfilen \"%s.ps\""
-
-#: ../hardcopy.c:2585
-#, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr "E620: Kunde inte konvertera frn utskriftkodning \"%s\""
-
-#: ../hardcopy.c:2808
-msgid "Sending to printer..."
-msgstr "Skickar till skrivare..."
-
-#: ../hardcopy.c:2812
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: Misslyckades med att skriva ut PostScript-fil"
-
-#: ../hardcopy.c:2814
-msgid "Print job sent."
-msgstr "Skrivarjobb skickat."
-
#: ../mark.c:673
msgid "No marks set"
msgstr "Inga mrken satta"
diff --git a/src/nvim/po/tojavascript.vim b/src/nvim/po/tojavascript.vim
new file mode 100644
index 0000000000..7868570be7
--- /dev/null
+++ b/src/nvim/po/tojavascript.vim
@@ -0,0 +1,18 @@
+" Invoked with the name "vim.pot" and a list of Vim script names.
+" Converts them to a .js file, stripping comments, so that xgettext works.
+" Javascript is used because, like Vim, it accepts both single and double
+" quoted strings.
+
+set shortmess+=A
+
+for name in argv()[1:]
+ exe 'edit ' .. fnameescape(name)
+
+ " Strip comments
+ g/^\s*"/s/.*//
+
+ " Write as .js file, xgettext recognizes them
+ exe 'w! ' .. fnamemodify(name, ":t:r") .. ".js"
+endfor
+
+quit
diff --git a/src/nvim/po/tr.po b/src/nvim/po/tr.po
index fae2fd4967..eb7879efc4 100644
--- a/src/nvim/po/tr.po
+++ b/src/nvim/po/tr.po
@@ -1,15 +1,15 @@
# Turkish translations for Neovim
# Neovim Türkçe çevirileri
# Copyright (C) 2019-2022 Emir SARI <emir_sari@icloud.com>
-# This file is distributed under the same license as the Vim package.
+# This file is distributed under the same license as the Nvim package.
# Emir SARI <emir_sari@icloud.com>, 2019-2022
#
msgid ""
msgstr ""
"Project-Id-Version: Neovim Turkish Localization Project\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2022-04-13 22:59+0300\n"
-"PO-Revision-Date: 2022-07-20 05:00+0300\n"
+"POT-Creation-Date: 2022-09-30 20:46+0300\n"
+"PO-Revision-Date: 2022-07-22 23:00+0300\n"
"Last-Translator: Emir SARI <emir_sari@icloud.com>\n"
"Language-Team: Turkish <https://github.com/bitigchi/neovim>\n"
"Language: tr\n"
@@ -18,6 +18,21 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+msgid "E163: There is only one file to edit"
+msgstr "E163: Düzenlenecek yalnızca bir dosya var"
+
+msgid "E164: Cannot go before first file"
+msgstr "E164: İlk dosyadan öncesine gidilemez"
+
+msgid "E165: Cannot go beyond last file"
+msgstr "E165: Son dosyadan öteye gidilemez"
+
+msgid "E610: No argument to delete"
+msgstr "E610: Silinecek bir argüman yok"
+
+msgid "E249: window layout changed unexpectedly"
+msgstr "E249: Pencere yerleşimi beklenmedik bir biçimde değişti"
+
msgid "--Deleted--"
msgstr "--Silindi--"
@@ -49,8 +64,9 @@ msgstr "E680: <arabellek=%d>: Geçersiz arabellek numarası "
msgid "E217: Can't execute autocommands for ALL events"
msgstr "E217: Otokomutlar TÜM olaylar için çalıştırılamıyor"
-msgid "No matching autocommands"
-msgstr "Eşleşen otokomut yok"
+#, c-format
+msgid "No matching autocommands: %s"
+msgstr "Eşleşen otokomut yok: %s"
msgid "E218: autocommand nesting too deep"
msgstr "E218: Çok fazla iç içe geçmiş otokomut"
@@ -79,24 +95,18 @@ msgstr "E216: Böyle bir olay yok: %s"
msgid "E216: No such group or event: %s"
msgstr "E216: Böyle bir grup veya olay yok: %s"
-msgid "[Location List]"
-msgstr "[Konum Listesi]"
-
-msgid "[Quickfix List]"
-msgstr "[Hızlı Düzelt Listesi]"
-
msgid "E855: Autocommands caused command to abort"
msgstr "E855: Otokomutlar komutun durmasına neden oldu"
+msgid "E937: Attempt to delete a buffer that is in use"
+msgstr "E937: Kullanımda olan bir arabellek silinmeye çalışılıyor"
+
msgid "E82: Cannot allocate any buffer, exiting..."
msgstr "E82: Arabellek ayrılamadı, çıkılıyor..."
msgid "E83: Cannot allocate buffer, using other one..."
msgstr "E83: Arabellek ayrılamadı, başka bir tane kullanılıyor..."
-msgid "E937: Attempt to delete a buffer that is in use"
-msgstr "E937: Kullanımda olan bir arabellek silinmeye çalışılıyor"
-
msgid "E515: No buffers were unloaded"
msgstr "E515: Hiçbir arabellek bellekten kaldırılmadı"
@@ -125,8 +135,8 @@ msgstr "E88: İlk arabellekten öncesine gidilemez"
msgid ""
"E89: No write since last change for buffer %<PRId64> (add ! to override)"
msgstr ""
-"E89: %<PRId64> numaralı arabellek son değişiklikten sonra yazılmadı (geçersiz "
-"kılmak için ! ekleyin)"
+"E89: %<PRId64> numaralı arabellek son değişiklikten sonra yazılmadı "
+"(geçersiz kılmak için ! ekleyin)"
#, c-format
msgid "E89: %s will be killed (add ! to override)"
@@ -192,12 +202,6 @@ msgstr "[Adsız]"
msgid "help"
msgstr "yardım"
-msgid "[Help]"
-msgstr "[Yardım]"
-
-msgid "[Preview]"
-msgstr "[Önizleme]"
-
msgid "All"
msgstr "Tüm Belge"
@@ -216,6 +220,12 @@ msgstr "[İstem]"
msgid "[Scratch]"
msgstr "[Geçici alan]"
+msgid "[Location List]"
+msgstr "[Konum Listesi]"
+
+msgid "[Quickfix List]"
+msgstr "[Hızlı Düzelt Listesi]"
+
msgid "W10: Warning: Changing a readonly file"
msgstr "W10: Uyarı: Saltokunur bir dosya değiştiriliyor"
@@ -231,6 +241,15 @@ msgstr "Kapalı akışa veri gönderilemez"
msgid "Can't send raw data to rpc channel"
msgstr "rpc kanalına ham veri gönderilemez"
+msgid "tagname"
+msgstr "etiket adı"
+
+msgid " kind file\n"
+msgstr " dosya türü\n"
+
+msgid "'history' option is zero"
+msgstr "'history' seçeneği sıfır"
+
msgid "E474: Failed to convert list to msgpack string buffer"
msgstr "E474: Liste, msgpack dizi arabelleğine dönüştürülemedi"
@@ -339,7 +358,7 @@ msgid "E103: Buffer \"%s\" is not in diff mode"
msgstr "E103: Arabellek \"%s\" karşılaştırma kipinde değil"
msgid "E787: Buffer changed unexpectedly"
-msgstr "E787: Arabellek beklenmeyen bir biçimde değiştirildi"
+msgstr "E787: Arabellek beklenmedik bir biçimde değiştirildi"
#, c-format
msgid "E1214: Digraph must be just two characters: %s"
@@ -441,140 +460,22 @@ msgstr "E105: :loadkeymap kaynak alınmayan bir dosyada kullanılıyor"
msgid "E791: Empty keymap entry"
msgstr "E791: Boş düğme eşlem girdisi"
-msgid " Keyword completion (^N^P)"
-msgstr " Anahtar sözcük tamamlaması (^N^P)"
-
-msgid " ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"
-msgstr " ^X kipi (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"
-
-msgid " Whole line completion (^L^N^P)"
-msgstr " Tam satır tamamlaması (^L^N^P)"
-
-msgid " File name completion (^F^N^P)"
-msgstr " Dosya adı tamamlaması (^F^N^P)"
-
-msgid " Tag completion (^]^N^P)"
-msgstr " Etiket tamamlaması (^]^N^P)"
-
-msgid " Path pattern completion (^N^P)"
-msgstr " Yol dizgisi tamamlaması (^N^P)"
-
-msgid " Definition completion (^D^N^P)"
-msgstr " Tanım tamamlaması (^D^N^P)"
-
-msgid " Dictionary completion (^K^N^P)"
-msgstr " Sözlük tamamlaması (^K^N^P)"
-
-msgid " Thesaurus completion (^T^N^P)"
-msgstr " Eşanlamlılar sözlüğü tamamlaması (^T^N^P)"
-
-msgid " Command-line completion (^V^N^P)"
-msgstr " Komut satırı tamamlaması (^V^N^P)"
-
-msgid " User defined completion (^U^N^P)"
-msgstr " Kullanıcı tanımlı tamamlamalar (^U^N^P)"
-
-msgid " Omni completion (^O^N^P)"
-msgstr " Omni tamamlaması (^O^N^P)"
-
-msgid " Spelling suggestion (s^N^P)"
-msgstr " Yazım önerisi (s^N^P)"
-
-msgid " Keyword Local completion (^N^P)"
-msgstr " Dahili anahtar sözcük tamamlaması (^N^P)"
-
-msgid "Hit end of paragraph"
-msgstr "Paragrafın sonuna varıldı"
-
-msgid "E839: Completion function changed window"
-msgstr "E839: Tamamlama işlevi pencereyi değiştirdi"
-
-msgid "E840: Completion function deleted text"
-msgstr "E840: Tamamlama işlevi metni sildi"
-
-msgid "'dictionary' option is empty"
-msgstr "'dictionary' seçeneği boş"
-
-msgid "'thesaurus' option is empty"
-msgstr "'thesaurus' seçeneği boş"
-
-#, c-format
-msgid "Scanning dictionary: %s"
-msgstr "Sözlük taranıyor: %s"
-
-msgid " (insert) Scroll (^E/^Y)"
-msgstr " (ekle) Kaydır (^E/^Y)"
-
-msgid " (replace) Scroll (^E/^Y)"
-msgstr " (değiştir) Kaydır (^E/^Y)"
-
-#, c-format
-msgid "Scanning: %s"
-msgstr "Taranıyor: %s"
-
-msgid "Scanning tags."
-msgstr "Etiketler taranıyor..."
-
-msgid "match in file"
-msgstr "dosya içinde eşleşme"
-
-msgid " Adding"
-msgstr " Ekleniyor"
-
-msgid "-- Searching..."
-msgstr "-- Aranıyor..."
-
-msgid "Back at original"
-msgstr "Başlangıca geri dönüldü"
-
-msgid "Word from other line"
-msgstr "Sözcük diğer satırdan"
-
-msgid "The only match"
-msgstr "Tek eşleşen"
-
-#, c-format
-msgid "match %d of %d"
-msgstr "eşleşme %d/%d"
-
-#, c-format
-msgid "match %d"
-msgstr "eşleşme %d"
-
-msgid "E18: Unexpected characters in :let"
-msgstr "E18: :let içinde beklenmeyen karakter"
-
msgid "E111: Missing ']'"
msgstr "E111: ']' eksik"
msgid "E719: Cannot use [:] with a Dictionary"
msgstr "E719: [:], bir Sözlük ile kullanılamaz"
-#, c-format
-msgid "E461: Illegal variable name: %s"
-msgstr "E461: İzin verilmeyen değişken adı: %s"
-
-msgid "E995: Cannot modify existing variable"
-msgstr "E995: Mevcut değişken değiştirilemiyor"
-
msgid "E274: No white space allowed before parenthesis"
msgstr "E274: Ayraçtan önce boşluğa izin verilmiyor"
#, c-format
-msgid "E940: Cannot lock or unlock variable %s"
-msgstr "E940: Değişken %s kilitlenemiyor veya açılamıyor"
-
-#, c-format
msgid "E80: Error while writing: %s"
msgstr "E80: Yazma sırasında hata: %s"
msgid "E1098: String, List or Blob required"
msgstr "E1098: Dizi, Liste veya İkili Nesne gerekiyor"
-#, c-format
-msgid "E734: Wrong variable type for %s="
-msgstr "E734: %s= için yanlış değişken türü"
-
msgid ""
"E5700: Expression from 'spellsuggest' must yield lists with exactly two "
"values"
@@ -582,41 +483,6 @@ msgstr ""
"E5700: 'spellsuggest'ten olan ifade, yalnızca iki değerli listelere izin "
"vermelidir"
-msgid "E991: cannot use =<< here"
-msgstr "E991: Burada =<< kullanılamaz"
-
-msgid "E221: Marker cannot start with lower case letter"
-msgstr "E221: İmleyici küçük harfle başlayamaz"
-
-msgid "E172: Missing marker"
-msgstr "E172: İmleyici eksik"
-
-#, c-format
-msgid "E990: Missing end marker '%s'"
-msgstr "E990: Son imleyici '%s' eksik"
-
-msgid "E687: Less targets than List items"
-msgstr "E687: Liste ögelerinden daha az hedef var"
-
-msgid "E688: More targets than List items"
-msgstr "E688: Liste ögelerinden daha fazla hedef var"
-
-msgid "E452: Double ; in list of variables"
-msgstr "E452: Değişkenler listesinde çifte ;"
-
-#, c-format
-msgid "E738: Can't list variables for %s"
-msgstr "E738: %s için değişkenler listelenemiyor"
-
-msgid "E996: Cannot lock an environment variable"
-msgstr "E996: Ortam değişkeni kilitlenemiyor"
-
-msgid "E996: Cannot lock an option"
-msgstr "E996: Seçenek kilitlenemiyor"
-
-msgid "E996: Cannot lock a register"
-msgstr "E996: Yazmaç kilitlenemiyor"
-
#, c-format
msgid "E121: Undefined variable: %.*s"
msgstr "E121: Tanımlanmamış değişken: %.*s"
@@ -652,10 +518,6 @@ msgstr "E996: Bir liste veya sözlük kilitlenemiyor"
msgid "E690: Missing \"in\" after :for"
msgstr "E690: :for sonrası \"in\" eksik"
-#, c-format
-msgid "E108: No such variable: \"%s\""
-msgstr "E108: Böyle bir değişken yok: \"%s\""
-
msgid "E109: Missing ':' after '?'"
msgstr "E109: '?' sonrası ':' eksik"
@@ -747,26 +609,6 @@ msgstr "Komut çalıştırılıyor: \"%s\""
msgid "E921: Invalid callback argument"
msgstr "E921: Geçersiz geri çağırma argümanı"
-#, c-format
-msgid "E963: setting %s to value with wrong type"
-msgstr "E963: %s yanlış türe sahip değere ayarlanıyor"
-
-#, c-format
-msgid "E794: Cannot set variable in the sandbox: \"%.*s\""
-msgstr "E794: Kum havuzunda değişken ayarlanamaz: \"%.*s\""
-
-#, c-format
-msgid "E795: Cannot delete variable %.*s"
-msgstr "E795: %.*s değişkeni silinemiyor"
-
-#, c-format
-msgid "E704: Funcref variable name must start with a capital: %s"
-msgstr "E704: Funcref değişkeni BÜYÜK harf ile başlamalıdır: %s"
-
-#, c-format
-msgid "E705: Variable name conflicts with existing function: %s"
-msgstr "E705: Değişken adı mevcut işlevle çakışıyor: %s"
-
msgid "E698: variable nested too deep for making a copy"
msgstr "E698: Değişken kopyalama için çok iç içe geçmiş"
@@ -852,8 +694,8 @@ msgid ""
"E474: Only UTF-8 code points up to U+10FFFF are allowed to appear unescaped: "
"%.*s"
msgstr ""
-"E474: Yalnızca U+10FFFF'e kadar olan UTF-8 kod noktalarına kaçışsız "
-"var olma izni verilir: %.*s"
+"E474: Yalnızca U+10FFFF'e kadar olan UTF-8 kod noktalarına kaçışsız var olma "
+"izni verilir: %.*s"
#, c-format
msgid "E474: Expected string end: %.*s"
@@ -888,8 +730,8 @@ msgid ""
"E685: internal error: while converting number \"%.*s\" to integer vim_str2nr "
"consumed %i bytes in place of %zu"
msgstr ""
-"E685: İçsel hata: \"%.*s\" sayısı tamsayıya dönüştürülürken "
-"vim_str2nr %i bayt harcadı, %zu bayt harcaması gerekiyordu"
+"E685: İçsel hata: \"%.*s\" sayısı tamsayıya dönüştürülürken vim_str2nr %i "
+"bayt harcadı, %zu bayt harcaması gerekiyordu"
msgid "E474: Attempt to decode a blank string"
msgstr "E474: Boş bir dizinin kodu çözülmeye çalışılıyor"
@@ -1019,15 +861,15 @@ msgstr "E474: JSON içinde sonsuzluk temsil edilemiyor"
#, c-format
msgid ""
"E474: String \"%.*s\" contains byte that does not start any UTF-8 character"
-msgstr "E474: \"%.*s\" dizisi, herhangi bir UTF-8 karakter başlatmayan bayt "
-"içeriyor"
+msgstr ""
+"E474: \"%.*s\" dizisi, herhangi bir UTF-8 karakter başlatmayan bayt içeriyor"
#, c-format
msgid ""
"E474: UTF-8 string contains code point which belongs to a surrogate pair: "
"%.*s"
-msgstr "E474: UTF-8 dizisi, bir vekil çifte iye olan kod noktası içeriyor: "
-"%.*s"
+msgstr ""
+"E474: UTF-8 dizisi, bir vekil çifte iye olan kod noktası içeriyor: %.*s"
msgid "E474: Unable to convert EXT string to JSON"
msgstr "E474: EXT dizisi, JSON'a dönüştürülemiyor"
@@ -1086,9 +928,6 @@ msgstr "E158: Geçersiz arabellek adı: %s"
msgid "Invalid channel stream \"%s\""
msgstr "Geçersiz kanal akışı \"%s\""
-msgid "E785: complete() can only be used in Insert mode"
-msgstr "E785: complete() yalnızca Ekleme kipinde kullanılabilir"
-
msgid "&Ok"
msgstr "&Tamam"
@@ -1169,10 +1008,6 @@ msgid "E5010: List item %d of the second argument is not a string"
msgstr "E5010: İkinci argümanın %d liste ögesi bir dizi değil"
#, c-format
-msgid "E927: Invalid action: '%s'"
-msgstr "E927: Geçersiz eylem: '%s'"
-
-#, c-format
msgid "E962: Invalid action: '%s'"
msgstr "E962: Geçersiz eylem: '%s'"
@@ -1180,18 +1015,6 @@ msgstr "E962: Geçersiz eylem: '%s'"
msgid "connection failed: %s"
msgstr "bağlantı başarısız: %s"
-msgid "sort() argument"
-msgstr "sort() argümanı"
-
-msgid "uniq() argument"
-msgstr "uniq() argümanı"
-
-msgid "E702: Sort compare function failed"
-msgstr "E702: Sıralayıp karşılaştırma işlevi başarısız oldu"
-
-msgid "E882: Uniq compare function failed"
-msgstr "E882: Benzersizlik karşılaştırma işlevi başarısız oldu"
-
#, c-format
msgid "E6100: \"%s\" is not a valid stdpath"
msgstr "E6100: \"%s\", geçerli bir stdpath değil"
@@ -1225,6 +1048,18 @@ msgid "E80: Error when closing file %s: %s"
msgstr "E80: %s dosyası kapatılırken hata: %s"
#, c-format
+msgid "E1174: String required for argument %d"
+msgstr "E1174: %d argümanı için dizi gerekiyor"
+
+#, c-format
+msgid "E1142: Non-empty string required for argument %d"
+msgstr "E1142: %d argümanı için boş olmayan dizi gerekiyor"
+
+#, c-format
+msgid "E1210: Number required for argument %d"
+msgstr "E1210: %d argümanı için sayı gerekiyor"
+
+#, c-format
msgid "E5142: Failed to open file %s: %s"
msgstr "E5142: %s dosyası açılamadı: %s"
@@ -1236,6 +1071,18 @@ msgstr "E5143: %s dosyası yazılamadı: %s"
msgid "E5144: Failed to close file %s: %s"
msgstr "E5144: %s dosyası kapatılamadı: %s"
+msgid "sort() argument"
+msgstr "sort() argümanı"
+
+msgid "uniq() argument"
+msgstr "uniq() argümanı"
+
+msgid "E702: Sort compare function failed"
+msgstr "E702: Sıralayıp karşılaştırma işlevi başarısız oldu"
+
+msgid "E882: Uniq compare function failed"
+msgstr "E882: Benzersizlik karşılaştırma işlevi başarısız oldu"
+
msgid "E6000: Argument is not a function or function name"
msgstr "E6000: Argüman bir işlev veya işlev adı değil"
@@ -1324,7 +1171,8 @@ msgid "E907: Using a special value as a Float"
msgstr "E907: Bir Kayan Noktalı Değer olarak bir özel değer kullanılıyor"
msgid "E975: Using a Blob as a Float"
-msgstr "E975: Bir kayan noktalı değer olarak bir ikili geniş nesne kullanılıyor"
+msgstr ""
+"E975: Bir kayan noktalı değer olarak bir ikili geniş nesne kullanılıyor"
msgid "E808: Number or Float required"
msgstr "E808: Sayı veya kayan noktalı değer gerekiyor"
@@ -1445,6 +1293,9 @@ msgstr "E126: :endfunction eksik"
msgid "W22: Text found after :endfunction: %s"
msgstr "W22: :endfunction sonrası metin bulundu: %s"
+msgid "E1058: function nesting too deep"
+msgstr "E1058: Pek çok iç içe geçmiş işlev"
+
#, c-format
msgid "E707: Function name conflicts with variable: %s"
msgstr "E707: İşlev adı şu değişken ile çakışıyor: %s"
@@ -1468,6 +1319,72 @@ msgstr "%s işlevi silinemiyor: Şu an program içinde kullanımda"
msgid "E133: :return not inside a function"
msgstr "E133: :return bir işlev içinde değil"
+msgid "E18: Unexpected characters in :let"
+msgstr "E18: :let içinde beklenmedik karakter"
+
+#, c-format
+msgid "E940: Cannot lock or unlock variable %s"
+msgstr "E940: Değişken %s kilitlenemiyor veya açılamıyor"
+
+msgid "E991: cannot use =<< here"
+msgstr "E991: Burada =<< kullanılamaz"
+
+msgid "E221: Marker cannot start with lower case letter"
+msgstr "E221: İmleyici küçük harfle başlayamaz"
+
+msgid "E172: Missing marker"
+msgstr "E172: İmleyici eksik"
+
+#, c-format
+msgid "E990: Missing end marker '%s'"
+msgstr "E990: Son imleyici '%s' eksik"
+
+msgid "E687: Less targets than List items"
+msgstr "E687: Liste ögelerinden daha az hedef var"
+
+msgid "E688: More targets than List items"
+msgstr "E688: Liste ögelerinden daha fazla hedef var"
+
+msgid "E452: Double ; in list of variables"
+msgstr "E452: Değişkenler listesinde çifte ;"
+
+#, c-format
+msgid "E738: Can't list variables for %s"
+msgstr "E738: %s için değişkenler listelenemiyor"
+
+msgid "E996: Cannot lock an environment variable"
+msgstr "E996: Ortam değişkeni kilitlenemiyor"
+
+msgid "E996: Cannot lock an option"
+msgstr "E996: Seçenek kilitlenemiyor"
+
+msgid "E996: Cannot lock a register"
+msgstr "E996: Yazmaç kilitlenemiyor"
+
+#, c-format
+msgid "E108: No such variable: \"%s\""
+msgstr "E108: Böyle bir değişken yok: \"%s\""
+
+#, c-format
+msgid "E963: setting %s to value with wrong type"
+msgstr "E963: %s yanlış türe sahip değere ayarlanıyor"
+
+#, c-format
+msgid "E794: Cannot set variable in the sandbox: \"%.*s\""
+msgstr "E794: Kum havuzunda değişken ayarlanamaz: \"%.*s\""
+
+#, c-format
+msgid "E795: Cannot delete variable %.*s"
+msgstr "E795: %.*s değişkeni silinemiyor"
+
+#, c-format
+msgid "E704: Funcref variable name must start with a capital: %s"
+msgstr "E704: Funcref değişkeni BÜYÜK harf ile başlamalıdır: %s"
+
+#, c-format
+msgid "E705: Variable name conflicts with existing function: %s"
+msgstr "E705: Değişken adı mevcut işlevle çakışıyor: %s"
+
msgid "tcp address must be host:port"
msgstr "tcp adresi makine:kapı olmalı"
@@ -1596,51 +1513,9 @@ msgstr "Dizginin bulunduğu her satır: %s"
msgid "Pattern not found: %s"
msgstr "Dizgi bulunamadı: %s"
-msgid "E478: Don't panic!"
-msgstr "E478: Panik yok!"
-
-#, c-format
-msgid "E661: Sorry, no '%s' help for %s"
-msgstr "E661: Üzgünüm, '%s' yardımı %s için mevcut değil"
-
-#, c-format
-msgid "E149: Sorry, no help for %s"
-msgstr "E149: Üzgünüm, %s için yardım mevcut değil"
-
-#, c-format
-msgid "Sorry, help file \"%s\" not found"
-msgstr "Üzgünüm, \"%s\" yardım dosyası bulunamadı"
-
-#, c-format
-msgid "E151: No match: %s"
-msgstr "E151: Eşleşme bulunamadı: %s"
-
-#, c-format
-msgid "E152: Cannot open %s for writing"
-msgstr "E152: %s yazma için açılamıyor"
-
-#, c-format
-msgid "E153: Unable to open %s for reading"
-msgstr "E153: %s okuma için açılamıyor"
-
-#, c-format
-msgid "E670: Mix of help file encodings within a language: %s"
-msgstr "E670: Bir dilde yardım dosyası kodlamaları karıştı: %s"
-
-#, c-format
-msgid "E154: Duplicate tag \"%s\" in file %s/%s"
-msgstr "E154: Şu dosyada yinelenen \"%s\" etiketi: %s/%s"
-
-#, c-format
-msgid "E150: Not a directory: %s"
-msgstr "E150: %s, bir dizin değil"
-
msgid "No old files"
msgstr "Eski dosya yok"
-msgid "E750: First use \":profile start {fname}\""
-msgstr "E750: İlk kullanım \":profile start {dosyaadı}\""
-
#, c-format
msgid "Save changes to \"%s\"?"
msgstr "Değişiklikler şuraya kaydedilsin mi: \"%s\"?"
@@ -1660,102 +1535,24 @@ msgstr "E162: \"%s\" arabelleği son değişiklikten sonra yazılmadı"
msgid "Warning: Entered other buffer unexpectedly (check autocommands)"
msgstr "Uyarı: Diğer arabelleğe aniden girildi (otokomutları denetleyin)"
-msgid "E163: There is only one file to edit"
-msgstr "E163: Düzenlenecek yalnızca bir dosya var"
-
-msgid "E164: Cannot go before first file"
-msgstr "E164: İlk dosyadan öncesine gidilemez"
-
-msgid "E165: Cannot go beyond last file"
-msgstr "E165: Son dosyadan öteye gidilemez"
-
-msgid "E610: No argument to delete"
-msgstr "E610: Silinecek bir argüman yok"
-
#, c-format
msgid "E666: compiler not supported: %s"
msgstr "E666: Derleyici desteklenmiyor: %s"
-#, c-format
-msgid "Cannot source a directory: \"%s\""
-msgstr "Dizin kaynak alınamıyor: \"%s\""
-
-#, c-format
-msgid "could not source \"%s\""
-msgstr "\"%s\" kaynak alınamadı"
-
-#, c-format
-msgid "line %<PRId64>: could not source \"%s\""
-msgstr "%<PRId64>. satır: \"%s\" kaynak alınamadı"
-
-#, c-format
-msgid "sourcing \"%s\""
-msgstr "\"%s\" kaynak alınıyor"
-
-#, c-format
-msgid "line %<PRId64>: sourcing \"%s\""
-msgstr "%<PRId64>. satır: \"%s\" kaynak alınıyor"
-
-#, c-format
-msgid "finished sourcing %s"
-msgstr "%s kaynak alınması bitti"
-
-msgid "modeline"
-msgstr "kip satırı"
-
-msgid "--cmd argument"
-msgstr "--cmd argümanı"
-
-msgid "-c argument"
-msgstr "-c argümanı"
-
-msgid "environment variable"
-msgstr "ortam değişkeni"
-
-msgid "error handler"
-msgstr "hata işleyicisi"
-
-msgid "changed window size"
-msgstr "değiştirilen pencere boyutu"
-
-msgid "Lua"
-msgstr "Lua"
-
-#, c-format
-msgid "API client (channel id %<PRIu64>)"
-msgstr "API istemcisi (kanal kimliği %<PRIu64>"
-
-msgid "anonymous :source"
-msgstr "anonim :source"
-
-#, c-format
-msgid "anonymous :source (script id %d)"
-msgstr "anonim :source (betik kimliği %d)"
-
-msgid "W15: Warning: Wrong line separator, ^M may be missing"
-msgstr "W15: Uyarı: Yanlış satır ayırıcısı, ^M eksik olabilir"
-
-msgid "E167: :scriptencoding used outside of a sourced file"
-msgstr "E167: :scriptencoding kaynak alınmış bir dosyanın dışında kullanıldı"
+msgid "E464: Ambiguous use of user-defined command"
+msgstr "E464: Kullanıcı tanımlı komutun belirsiz kullanımı"
-msgid "E168: :finish used outside of a sourced file"
-msgstr "E168: :finish kaynak alınmış bir dosyanın dışında kullanıldı"
+msgid "E492: Not an editor command"
+msgstr "E492: Bir düzenleyici komutu değil"
-#, c-format
-msgid "Current %slanguage: \"%s\""
-msgstr "Şu anki %sdil: \"%s\""
+msgid "E498: no :source file name to substitute for \"<sfile>\""
+msgstr "E498: \"<kdosyası>\" yerine koymak için :source dosya adı yok"
-#, c-format
-msgid "E197: Cannot set language to \"%s\""
-msgstr "E197: \"%s\" diline ayarlanamıyor"
+msgid "E489: no call stack to substitute for \"<stack>\""
+msgstr "E489: \"<yığın>\" yerine koymak için çağrı yığını yok"
-#, c-format
-msgid "E184: No such user-defined command: %s"
-msgstr "E184: Böyle bir kullanıcı tanımlı komut yok: %s"
-
-#, c-format
-msgid "E1237: No such user-defined command in current buffer: %s"
-msgstr "E1237: Geçerli arabellekte böyle bir kullanıcı tanımlı komut yok: %s"
+msgid "E1274: No script file name to substitute for \"<script>\""
+msgstr "E1274: \"<betik>\" yerine koymak için betik dosyası adı yok"
msgid "Entering Ex mode. Type \"visual\" to go to Normal mode."
msgstr "Ex kipine giriliyor. Normal kipe geri dönmek için \"visual\" yazın."
@@ -1770,9 +1567,6 @@ msgstr "Çalıştırılıyor: %s"
msgid "line %"
msgstr "satır %"
-msgid "E169: Command too recursive"
-msgstr "E169: Komut çok özyineli"
-
#, c-format
msgid "E605: Exception not caught: %s"
msgstr "E605: Kural dışı durum yakalanmadı: %s"
@@ -1783,11 +1577,11 @@ msgstr "Kaynak alınan dosyanın sonu"
msgid "End of function"
msgstr "İşlevin sonu"
-msgid "E464: Ambiguous use of user-defined command"
-msgstr "E464: Kullanıcı tanımlı komutun belirsiz kullanımı"
-
-msgid "E492: Not an editor command"
-msgstr "E492: Bir düzenleyici komutu değil"
+msgid ""
+"INTERNAL: Cannot use EX_DFLALL with ADDR_NONE, ADDR_UNSIGNED or ADDR_QUICKFIX"
+msgstr ""
+"DAHİLİ: EX_DFLALL; ADDR_NONE, ADDR_UNSIGNED veya ADDR_QUICKFIX ile birlikte "
+"kullanılamaz"
msgid "E493: Backwards range given"
msgstr "E493: Geriye dönük erim verildi"
@@ -1798,12 +1592,6 @@ msgstr "Geriye dönük erim verildi, takas edilebilir"
msgid "E494: Use w or w>>"
msgstr "E494: w veya w>> kullanın"
-msgid ""
-"INTERNAL: Cannot use EX_DFLALL with ADDR_NONE, ADDR_UNSIGNED or ADDR_QUICKFIX"
-msgstr ""
-"DAHİLİ: EX_DFLALL; ADDR_NONE, ADDR_UNSIGNED veya ADDR_QUICKFIX ile birlikte "
-"kullanılamaz"
-
msgid "E943: Command table needs to be updated, run 'make'"
msgstr "E943: Komut tablosunun güncellenmesi gerekiyor, 'make' çalıştırın"
@@ -1811,68 +1599,6 @@ msgid "E319: The command is not available in this version"
msgstr "E319: Üzgünüm, komut bu sürümde mevcut değil"
#, c-format
-msgid "E174: Command already exists: add ! to replace it: %s"
-msgstr "E174: Komut zaten mevcut: Değiştirmek için ! ekleyin: %s"
-
-msgid ""
-"\n"
-" Name Args Address Complete Definition"
-msgstr ""
-"\n"
-" Ad Dğkl Adres Tam Tanım"
-
-msgid "No user-defined commands found"
-msgstr "Kullanıcı tanımlı bir komut bulunamadı"
-
-msgid "E175: No attribute specified"
-msgstr "E175: Bir öznitelik belirtilmemiş"
-
-msgid "E176: Invalid number of arguments"
-msgstr "E176: Geçersiz argüman sayısı"
-
-msgid "E177: Count cannot be specified twice"
-msgstr "E177: Sayım iki defa belirtilemez"
-
-msgid "E178: Invalid default value for count"
-msgstr "E178: Sayım için geçersiz öntanımlı değer"
-
-msgid "E179: argument required for -complete"
-msgstr "E179: -complete için argüman gerekiyor"
-
-msgid "E179: argument required for -addr"
-msgstr "E179: -addr için argüman gerekiyor"
-
-#, c-format
-msgid "E181: Invalid attribute: %s"
-msgstr "E181: Geçersiz öznitelik: %s"
-
-msgid "E1208: -complete used without -nargs"
-msgstr "E1208: -complete, -nargs olmadan kullanıldı"
-
-msgid "E182: Invalid command name"
-msgstr "E182: Geçersiz komut adı"
-
-msgid "E183: User defined commands must start with an uppercase letter"
-msgstr "E183: Kullanıcı tanımlı komutlar BÜYÜK harfle başlamalıdır"
-
-msgid "E841: Reserved name, cannot be used for user defined command"
-msgstr "E841: Ayrılmış ad, kullanıcı tanımlı komut için kullanılamaz"
-
-#, c-format
-msgid "E180: Invalid address type value: %s"
-msgstr "E180: Geçersiz adres türü değeri: %s"
-
-#, c-format
-msgid "E180: Invalid complete value: %s"
-msgstr "E180: Geçersiz tam değer: %s"
-
-msgid "E468: Completion argument only allowed for custom completion"
-msgstr "E468: Tamamlama argümanına yalnızca özel tamamlamalarda izin verilir"
-
-msgid "E467: Custom completion requires a function argument"
-msgstr "E467: Özel tamamlama bir işlev argümanı gerektirir"
-
-#, c-format
msgid "E185: Cannot find color scheme '%s'"
msgstr "E185: '%s' renk düzeni bulunamadı"
@@ -1931,9 +1657,6 @@ msgstr ""
msgid "E497: no autocommand match name to substitute for \"<amatch>\""
msgstr "E497: \"<oeşi>\" yerine koymak için otokomut eşleşme adı yok"
-msgid "E498: no :source file name to substitute for \"<sfile>\""
-msgstr "E498: \"<kdosyası>\" yerine koymak için :source dosya adı yok"
-
msgid "E842: no line number to use for \"<slnum>\""
msgstr "E842: \"<slnum>\" kullanımı için satır numarası yok"
@@ -2051,9 +1774,6 @@ msgstr "E602: :try olmadan :endtry"
msgid "E193: :endfunction not inside a function"
msgstr "E193: :endfunction, bir işlev içinde değil"
-msgid "E788: Not allowed to edit another buffer now"
-msgstr "E788: Şu anda başka bir arabellek düzenlenemez"
-
msgid "E811: Not allowed to change buffer information now"
msgstr "E811: Şu anda arabellek bilgisi değiştirilemez"
@@ -2088,15 +1808,6 @@ msgstr "E5404: %i parçası bitiş %"
msgid "E5406: Chunk %i end %"
msgstr "E5406: %i parçası bitiş %"
-msgid "tagname"
-msgstr "etiket adı"
-
-msgid " kind file\n"
-msgstr " dosya türü\n"
-
-msgid "'history' option is zero"
-msgstr "'history' seçeneği sıfır"
-
msgid "[Command Line]"
msgstr "[Komut Satırı]"
@@ -2133,6 +1844,10 @@ msgstr "E347: Başka bir \"%s\" dosyası yol içinde bulunamadı"
msgid "E812: Autocommands changed buffer or buffer name"
msgstr "E812: Otokomutlar arabelleği veya arabellek adını değiştirdi"
+#, c-format
+msgid "E676: No matching autocommands for buftype=%s buffer"
+msgstr "E676: buftype=%s arabelleği için eşleşen otokomut yok"
+
msgid "is a directory"
msgstr "bir dizin"
@@ -2207,14 +1922,11 @@ msgstr "[Yeni Dosya]"
msgid "[New]"
msgstr "[Yeni]"
-msgid "E676: No matching autocommands for acwrite buffer"
-msgstr "E676: acwrite arabelleği için eşleşen bir otokomut yok"
-
msgid "E203: Autocommands deleted or unloaded buffer to be written"
msgstr "E203: Otokomutlar arabelleği silmiş veya yazılması için kaldırmışlar"
msgid "E204: Autocommand changed number of lines in unexpected way"
-msgstr "E204: Otokomut satır sayısını beklenmeyen biçimde değiştirdi"
+msgstr "E204: Otokomut satır sayısını beklenmedik biçimde değiştirdi"
msgid "is not a file or writable device"
msgstr "bir dosya veya yazılabilir aygıt değil"
@@ -2422,31 +2134,6 @@ msgstr "E222: Okunan arabelleğe ekle"
msgid "E223: recursive mapping"
msgstr "E223: Özyineli eşlemleme"
-#, c-format
-msgid "E224: global abbreviation already exists for %s"
-msgstr "E224: %s için global kısaltma hâlihazırda var"
-
-#, c-format
-msgid "E225: global mapping already exists for %s"
-msgstr "E225: %s için global eşlemleme hâlihazırda var"
-
-#, c-format
-msgid "E226: abbreviation already exists for %s"
-msgstr "E226: %s için kısaltma hâlihazırda var"
-
-#, c-format
-msgid "E227: mapping already exists for %s"
-msgstr "E227: %s için eşlemleme hâlihazırda var"
-
-msgid "No abbreviation found"
-msgstr "Kısaltma bulunamadı"
-
-msgid "No mapping found"
-msgstr "Eşlemleme bulunamadı"
-
-msgid "E228: makemap: Illegal mode"
-msgstr "E228: makemap: İzin verilmeyen kip"
-
msgid "--No lines in buffer--"
msgstr "--Arabellek içinde satır yok--"
@@ -2473,6 +2160,9 @@ msgstr ""
"E12: Geçerli dizin veya etiket aramasında exrc veya vimrc'den komutlara izin "
"verilmiyor"
+msgid "E169: Command too recursive"
+msgstr "E169: Komut çok özyineli"
+
msgid "E171: Missing :endif"
msgstr "E171: :endif eksik"
@@ -2572,8 +2262,9 @@ msgstr "E906: rpc kanalı için geçersiz akış, 'rpc' kullanın"
#, c-format
msgid ""
"E5210: dict key '%s' already set for buffered stream in channel %<PRIu64>"
-msgstr "E5210: '%s' sözlük anahtarı, %<PRIu64> kanalında halihazırda "
-"arabelleklenmiş akış için ayarlandı"
+msgstr ""
+"E5210: '%s' sözlük anahtarı, %<PRIu64> kanalında halihazırda arabelleklenmiş "
+"akış için ayarlandı"
#, c-format
msgid "E364: Library call failed for \"%s()\""
@@ -2703,6 +2394,17 @@ msgid "E45: 'readonly' option is set (add ! to override)"
msgstr "E45: 'readonly' seçeneği ayarlanmış (geçersiz kılmak için ! ekleyin)"
#, c-format
+msgid "E734: Wrong variable type for %s="
+msgstr "E734: %s= için yanlış değişken türü"
+
+#, c-format
+msgid "E461: Illegal variable name: %s"
+msgstr "E461: İzin verilmeyen değişken adı: %s"
+
+msgid "E995: Cannot modify existing variable"
+msgstr "E995: Mevcut değişken değiştirilemiyor"
+
+#, c-format
msgid "E46: Cannot change read-only variable \"%.*s\""
msgstr "E46: Saltokunur değişken \"%.*s\" değiştirilemiyor"
@@ -2750,6 +2452,9 @@ msgstr "E48: Kum havuzunda izin verilmiyor"
msgid "E523: Not allowed here"
msgstr "E523: Burada izin verilmiyor"
+msgid "E565: Not allowed to change text or change window"
+msgstr "E565: Metin veya pencere değiştirmeye izin verilmiyor"
+
msgid "E359: Screen mode setting not supported"
msgstr "E359: Ekran kipi ayarı desteklenmiyor"
@@ -2842,6 +2547,9 @@ msgstr "E919: '%s' içinde dizin bulunamadı: \"%s\""
msgid "E952: Autocommand caused recursive behavior"
msgstr "E952: Otokomut özyineli davranışa neden oldu"
+msgid "E328: Menu only exists in another mode"
+msgstr "E328: Menü yalnızca başka bir kipte mevcut"
+
msgid "E813: Cannot close autocmd window"
msgstr "E813: Otokomut penceresi kapatılamıyor"
@@ -2858,6 +2566,9 @@ msgstr "E856: Dosya adı pek uzun"
msgid "E806: using Float as a String"
msgstr "E806: Kayan Noktalı Değer, bir Dizi yerine kullanılıyor"
+msgid "E788: Not allowed to edit another buffer now"
+msgstr "E788: Şu anda başka bir arabellek düzenlenemez"
+
#, c-format
msgid "E5500: autocmd has thrown an exception: %s"
msgstr "E5500: Otokomut, bir istisna attı: %s"
@@ -2869,10 +2580,6 @@ msgid "E5521: <Cmd> mapping must end with <CR> before second <Cmd>"
msgstr "E5521: <Cmd> eşlemlemesi ikinci <Cmd>'den önce <CR> ile bitmelidir"
#, c-format
-msgid "E5522: <Cmd> mapping must not include %s key"
-msgstr "E5522: <Cmd> eşlemlemesi %s anahtarını içermemelidir"
-
-#, c-format
msgid "E5555: API call: %s"
msgstr "E5555: API çağrısı: %s"
@@ -2898,9 +2605,17 @@ msgstr "E1240: Ortaya çıkan metin pek uzun"
msgid "E1247: Line number out of range"
msgstr "E1247: Satır numarası erim dışında"
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Grup adında geçersiz karakter"
+
msgid "E1249: Highlight group name too long"
msgstr "E1249: Vurgulama grubu adı pek uzun"
+msgid "E5767: Cannot use :undo! to redo or move to a different undo branch"
+msgstr ""
+"E5767: Yinelemek veya başka bir geri al dalına taşımak için :undo! "
+"kullanılamaz"
+
msgid "search hit TOP, continuing at BOTTOM"
msgstr "Arama dosyanın BAŞINI geçti, dosyanın SONUNDAN sürüyor"
@@ -2910,98 +2625,44 @@ msgstr "Arama dosyanın SONUNU geçti, dosyanın BAŞINDAN sürüyor"
msgid " line "
msgstr " satır "
-msgid "E550: Missing colon"
-msgstr "E550: İki nokta eksik"
-
-msgid "E551: Illegal component"
-msgstr "E551: Geçersiz bileşen"
-
-msgid "E552: digit expected"
-msgstr "E552: Basamak bekleniyordu"
-
-#, c-format
-msgid "Page %d"
-msgstr "Sayfa %d"
-
-msgid "No text to be printed"
-msgstr "Yazdırılacak metin yok"
-
-#, c-format
-msgid "Printing page %d (%zu%%)"
-msgstr "Sayfa %d yazdırılıyor (%%%zu)"
-
-#, c-format
-msgid " Copy %d of %d"
-msgstr " Kopya %d/%d"
+msgid "E478: Don't panic!"
+msgstr "E478: Panik yok!"
#, c-format
-msgid "Printed: %s"
-msgstr "Yazdırıldı: %s"
-
-msgid "Printing aborted"
-msgstr "Yazdırma durduruldu"
-
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: PostScript çıktı dosyasına yazarken hata"
+msgid "E661: Sorry, no '%s' help for %s"
+msgstr "E661: Üzgünüm, '%s' yardımı %s için mevcut değil"
#, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E624: \"%s\" dosyası açılamıyor"
+msgid "E149: Sorry, no help for %s"
+msgstr "E149: Üzgünüm, %s için yardım mevcut değil"
#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: PostScript özkaynak dosyası \"%s\" okunamıyor"
+msgid "Sorry, help file \"%s\" not found"
+msgstr "Üzgünüm, \"%s\" yardım dosyası bulunamadı"
#, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E618: \"%s\" dosyası bir PostScript özkaynak dosyası değil"
+msgid "E151: No match: %s"
+msgstr "E151: Eşleşme bulunamadı: %s"
#, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr "E619: \"%s\" dosyası desteklenen bir PostScript özkaynak dosyası değil"
+msgid "E152: Cannot open %s for writing"
+msgstr "E152: %s yazma için açılamıyor"
#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr "E621: \"%s\" özkaynak dosyası sürümü hatalı"
-
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr "E673: Uyumsuz çoklu bayt kodlaması ve karakter kümesi."
-
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr "E674: printmbcharset çoklu bayt kodlamada boş olamaz."
-
-msgid "E675: No default font specified for multi-byte printing."
-msgstr "E675: Çoklu bayt yazdırma için öntanımlı yazıtipi ayarlanmamış."
-
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: PostScript çıktı dosyası açılamıyor"
+msgid "E153: Unable to open %s for reading"
+msgstr "E153: %s okuma için açılamıyor"
#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: \"%s\" dosyası açılamıyor"
-
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E456: PostScript özkaynak dosyası \"prolog.ps\" bulunamıyor"
-
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr "E456: PostScript özkaynak dosyası \"cidfont.ps\" bulunamıyor"
+msgid "E670: Mix of help file encodings within a language: %s"
+msgstr "E670: Bir dilde yardım dosyası kodlamaları karıştı: %s"
#, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E456: PostScript özkaynak dosyası \"%s.ps\" bulunamıyor"
+msgid "E154: Duplicate tag \"%s\" in file %s/%s"
+msgstr "E154: Şu dosyada yinelenen \"%s\" etiketi: %s/%s"
#, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr "E620: \"%s\" yazdırma kodlamasına dönüştürülemiyor"
-
-msgid "Sending to printer..."
-msgstr "Yazıcıya gönderiliyor..."
-
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: PostScript dosyası yazdırılamadı"
-
-msgid "Print job sent."
-msgstr "Yazdırma işi gönderildi."
+msgid "E150: Not a directory: %s"
+msgstr "E150: %s, bir dizin değil"
msgid "E424: Too many different highlighting attributes in use"
msgstr "E424: Çok fazla değişik vurgulama kuralları kullanılıyor"
@@ -3025,6 +2686,9 @@ msgstr "E414: Grup ayarları mevcut, vurgulama bağlantısı yok sayıldı"
msgid "E415: unexpected equal sign: %s"
msgstr "E415: Beklenmedik eşittir imi: %s"
+msgid "E423: Illegal argument"
+msgstr "E423: İzin verilmeyen argüman"
+
#, c-format
msgid "E416: missing equal sign: %s"
msgstr "E416: Eksik eşittir imi: %s"
@@ -3054,9 +2718,6 @@ msgstr "E423: İzin verilmeyen argüman: %s"
msgid "E669: Unprintable character in group name"
msgstr "E669: Grup adında yazdırılamayan karakter"
-msgid "W18: Invalid character in group name"
-msgstr "W18: Grup adında geçersiz karakter"
-
msgid "E849: Too many highlight and syntax groups"
msgstr "E849: Çok fazla vurgulama ve sözdizim grupları"
@@ -3220,6 +2881,114 @@ msgstr ""
msgid "Type number and <Enter> (q or empty cancels): "
msgstr "Sayı girip <Enter>'a basın (q veya boş iptal eder): "
+msgid " Keyword completion (^N^P)"
+msgstr " Anahtar sözcük tamamlaması (^N^P)"
+
+msgid " ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"
+msgstr " ^X kipi (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"
+
+msgid " Whole line completion (^L^N^P)"
+msgstr " Tam satır tamamlaması (^L^N^P)"
+
+msgid " File name completion (^F^N^P)"
+msgstr " Dosya adı tamamlaması (^F^N^P)"
+
+msgid " Tag completion (^]^N^P)"
+msgstr " Etiket tamamlaması (^]^N^P)"
+
+msgid " Path pattern completion (^N^P)"
+msgstr " Yol dizgisi tamamlaması (^N^P)"
+
+msgid " Definition completion (^D^N^P)"
+msgstr " Tanım tamamlaması (^D^N^P)"
+
+msgid " Dictionary completion (^K^N^P)"
+msgstr " Sözlük tamamlaması (^K^N^P)"
+
+msgid " Thesaurus completion (^T^N^P)"
+msgstr " Eşanlamlılar sözlüğü tamamlaması (^T^N^P)"
+
+msgid " Command-line completion (^V^N^P)"
+msgstr " Komut satırı tamamlaması (^V^N^P)"
+
+msgid " User defined completion (^U^N^P)"
+msgstr " Kullanıcı tanımlı tamamlamalar (^U^N^P)"
+
+msgid " Omni completion (^O^N^P)"
+msgstr " Omni tamamlaması (^O^N^P)"
+
+msgid " Spelling suggestion (s^N^P)"
+msgstr " Yazım önerisi (s^N^P)"
+
+msgid " Keyword Local completion (^N^P)"
+msgstr " Dahili anahtar sözcük tamamlaması (^N^P)"
+
+msgid "Hit end of paragraph"
+msgstr "Paragrafın sonuna varıldı"
+
+msgid "E840: Completion function deleted text"
+msgstr "E840: Tamamlama işlevi metni sildi"
+
+msgid "'dictionary' option is empty"
+msgstr "'dictionary' seçeneği boş"
+
+msgid "'thesaurus' option is empty"
+msgstr "'thesaurus' seçeneği boş"
+
+#, c-format
+msgid "Scanning dictionary: %s"
+msgstr "Sözlük taranıyor: %s"
+
+msgid " (insert) Scroll (^E/^Y)"
+msgstr " (ekle) Kaydır (^E/^Y)"
+
+msgid " (replace) Scroll (^E/^Y)"
+msgstr " (değiştir) Kaydır (^E/^Y)"
+
+msgid "E785: complete() can only be used in Insert mode"
+msgstr "E785: complete() yalnızca Ekleme kipinde kullanılabilir"
+
+#, c-format
+msgid "Scanning: %s"
+msgstr "Taranıyor: %s"
+
+msgid "Scanning tags."
+msgstr "Etiketler taranıyor..."
+
+msgid "match in file"
+msgstr "dosya içinde eşleşme"
+
+msgid " Adding"
+msgstr " Ekleniyor"
+
+msgid "-- Searching..."
+msgstr "-- Aranıyor..."
+
+msgid "Back at original"
+msgstr "Başlangıca geri dönüldü"
+
+msgid "Word from other line"
+msgstr "Sözcük diğer satırdan"
+
+msgid "The only match"
+msgstr "Tek eşleşen"
+
+#, c-format
+msgid "match %d of %d"
+msgstr "eşleşme %d/%d"
+
+#, c-format
+msgid "match %d"
+msgstr "eşleşme %d"
+
+#, c-format
+msgid "Current %slanguage: \"%s\""
+msgstr "Şu anki %sdil: \"%s\""
+
+#, c-format
+msgid "E197: Cannot set language to \"%s\""
+msgstr "E197: \"%s\" diline ayarlanamıyor"
+
#, c-format
msgid "E1502: Lua failed to grow stack to %i"
msgstr "E1502: Lua, yığını %i olarak büyütemedi"
@@ -3366,16 +3135,14 @@ msgid "Usage:\n"
msgstr "Kullanım:\n"
msgid " nvim [options] [file ...] Edit file(s)\n"
-msgstr ""
-"nvim [seçenekler] [dosya ...] Dosyaları düzenle\n"
+msgstr "nvim [seçenekler] [dosya ...] Dosyaları düzenle\n"
msgid " nvim [options] -t <tag> Edit file where tag is defined\n"
msgstr ""
"nvim [seçenekler] -t <etiket> Etiketin tanımlandığı dosyayı düzenle\n"
msgid " nvim [options] -q [errorfile] Edit file with first error\n"
-msgstr ""
-"nvim [seçenekler] -q [hatadosyası] İlk hatalı dosyayı düzenle\n"
+msgstr "nvim [seçenekler] -q [hatadosyası] İlk hatalı dosyayı düzenle\n"
msgid ""
"\n"
@@ -3385,12 +3152,10 @@ msgstr ""
"Seçenekler:\n"
msgid " -- Only file names after this\n"
-msgstr ""
-"-- Yalnızca bundan sonraki dosya adları\n"
+msgstr "-- Yalnızca bundan sonraki dosya adları\n"
msgid " + Start at end of file\n"
-msgstr ""
-"+ Dosyanın sonunda başlat\n"
+msgstr "+ Dosyanın sonunda başlat\n"
msgid " --cmd <cmd> Execute <cmd> before any config\n"
msgstr ""
@@ -3401,40 +3166,31 @@ msgstr ""
"+<komut>, -c <komut> Yapılandırma ve ilk dosya sonrası <komut> çalıştır\n"
msgid " -b Binary mode\n"
-msgstr ""
-"-b İkili kip\n"
+msgstr "-b İkili kip\n"
msgid " -d Diff mode\n"
-msgstr ""
-"-d Diff kipi\n"
+msgstr "-d Diff kipi\n"
msgid " -e, -E Ex mode\n"
-msgstr ""
-"-e, -E Ex kipi\n"
+msgstr "-e, -E Ex kipi\n"
msgid " -es, -Es Silent (batch) mode\n"
-msgstr ""
-"-es, -Es Sessiz (toplu iş) kipi\n"
+msgstr "-es, -Es Sessiz (toplu iş) kipi\n"
msgid " -h, --help Print this help message\n"
-msgstr ""
-"-h, --help Bu yardım iletisini yazdır\n"
+msgstr "-h, --help Bu yardım iletisini yazdır\n"
msgid " -i <shada> Use this shada file\n"
-msgstr ""
-"-i <shada> Bu shada (paylaşılan veri) dosyasını kullan\n"
+msgstr "-i <shada> Bu shada (paylaşılan veri) dosyasını kullan\n"
msgid " -m Modifications (writing files) not allowed\n"
-msgstr ""
-"-m Değişikliklere (dosya yazmaya) izin verme\n"
+msgstr "-m Değişikliklere (dosya yazmaya) izin verme\n"
msgid " -M Modifications in text not allowed\n"
-msgstr ""
-"-M Metinde değişikliklere izin verme\n"
+msgstr "-M Metinde değişikliklere izin verme\n"
msgid " -n No swap file, use memory only\n"
-msgstr ""
-"-n Takas dosyası yok, yalnızca belleği kullan\n"
+msgstr "-n Takas dosyası yok, yalnızca belleği kullan\n"
msgid " -o[N] Open N windows (default: one per file)\n"
msgstr ""
@@ -3450,68 +3206,61 @@ msgstr ""
"-p[N] N sekme sayfası aç (öntanımlı: dosya başına bir tane)\n"
msgid " -r, -L List swap files\n"
-msgstr ""
-"-r, -L Takas dosyalarını listele\n"
+msgstr "-r, -L Takas dosyalarını listele\n"
msgid " -r <file> Recover edit state for this file\n"
-msgstr ""
-"-r <dosya> Bu dosyanın düzenleme durumunu kurtar\n"
+msgstr "-r <dosya> Bu dosyanın düzenleme durumunu kurtar\n"
msgid " -R Read-only mode\n"
-msgstr ""
-"-R Saltokunur kip\n"
+msgstr "-R Saltokunur kip\n"
msgid " -S <session> Source <session> after loading the first file\n"
msgstr ""
"-S <oturum> İlk dosyayı yükledikten sonra <oturum>'u kaynak al\n"
msgid " -s <scriptin> Read Normal mode commands from <scriptin>\n"
-msgstr ""
-"-s <betikgir> Normal kip komutlarını <betikgir>'den oku\n"
+msgstr "-s <betikgir> Normal kip komutlarını <betikgir>'den oku\n"
msgid " -u <config> Use this config file\n"
-msgstr ""
-"-u <yapılandırma> Bu yapılandırma dosyasını kullan\n"
+msgstr "-u <yapılandırma> Bu yapılandırma dosyasını kullan\n"
msgid " -v, --version Print version information\n"
-msgstr ""
-"-v, --version Sürüm bilgisini yazdır\n"
+msgstr "-v, --version Sürüm bilgisini yazdır\n"
msgid " -V[N][file] Verbose [level][file]\n"
-msgstr ""
-"-V[N][dosya] Ayrıntılı bilgi ver [düzey][dosya]\n"
+msgstr "-V[N][dosya] Ayrıntılı bilgi ver [düzey][dosya]\n"
msgid " --api-info Write msgpack-encoded API metadata to stdout\n"
+msgstr "--api-info stdout'a msgpack kodlu API üstverisi yaz\n"
+
+msgid ""
+" --clean \"Factory defaults\" (skip user config and plugins, "
+"shada)\n"
msgstr ""
-"--api-info stdout'a msgpack kodlu API üstverisi yaz\n"
+" --clean \"Fabrika ayarları\" (yapılandırma/eklentileri atla, "
+"shada)\n"
msgid " --embed Use stdin/stdout as a msgpack-rpc channel\n"
msgstr ""
"--embed stdin/stdout'u msgpack-rpc kanalı olarak kullan\n"
msgid " --headless Don't start a user interface\n"
-msgstr ""
-"--headless Bir kullanıcı arayüzü başlatma\n"
+msgstr "--headless Bir kullanıcı arayüzü başlatma\n"
msgid " --listen <address> Serve RPC API from this address\n"
-msgstr ""
-"--listen <adres> Bu adresten RPC API'si sun\n"
+msgstr "--listen <adres> Bu adresten RPC API'si sun\n"
msgid " --noplugin Don't load plugins\n"
-msgstr ""
-"--noplugin Eklentileri yükleme\n"
+msgstr "--noplugin Eklentileri yükleme\n"
msgid " --remote[-subcommand] Execute commands remotely on a server\n"
-msgstr ""
-"--remote[-subcommand] Bir sunucuda komutları uzaktan çalıştır\n"
+msgstr "--remote[-subcommand] Bir sunucuda komutları uzaktan çalıştır\n"
msgid " --server <address> Specify RPC server to send commands to\n"
-msgstr ""
-"--server <adres> Komut gönderilecek RPC sunucusunu belirt\n"
+msgstr "--server <adres> Komut gönderilecek RPC sunucusunu belirt\n"
msgid " --startuptime <file> Write startup timing messages to <file>\n"
-msgstr ""
-"--startuptime <dosya> Başlangıç zamanlama iletilerini <dosya>'ya yaz\n"
+msgstr "--startuptime <dosya> Başlangıç zamanlama iletilerini <dosya>'ya yaz\n"
msgid ""
"\n"
@@ -3520,6 +3269,42 @@ msgstr ""
"\n"
"Tüm seçenekler için \":help startup-options\" yazın.\n"
+#, c-format
+msgid "E224: global abbreviation already exists for %s"
+msgstr "E224: %s için global kısaltma hâlihazırda var"
+
+#, c-format
+msgid "E225: global mapping already exists for %s"
+msgstr "E225: %s için global eşlemleme hâlihazırda var"
+
+#, c-format
+msgid "E226: abbreviation already exists for %s"
+msgstr "E226: %s için kısaltma hâlihazırda var"
+
+#, c-format
+msgid "E227: mapping already exists for %s"
+msgstr "E227: %s için eşlemleme hâlihazırda var"
+
+msgid "No abbreviation found"
+msgstr "Kısaltma bulunamadı"
+
+msgid "No mapping found"
+msgstr "Eşlemleme bulunamadı"
+
+msgid "E228: makemap: Illegal mode"
+msgstr "E228: makemap: İzin verilmeyen kip"
+
+msgid "E460: entries missing in mapset() dict argument"
+msgstr "E460: mapset() sözlük argümanında girdiler eksik"
+
+#, c-format
+msgid "E357: 'langmap': Matching character missing for %s"
+msgstr "E357: 'langmap': %s için eşleşen karakter eksik"
+
+#, c-format
+msgid "E358: 'langmap': Extra characters after semicolon: %s"
+msgstr "E358: 'langmap': Noktalı virgülden sonra ek karakterler: %s"
+
msgid "No marks set"
msgstr "İm ayarlanmamış"
@@ -3588,6 +3373,29 @@ msgstr "E798: Kimlik, \":match\" için ayrılmış: %<PRId64>"
msgid "E798: ID is reserved for \"match\": %<PRId64>"
msgstr "E798: Kimlik, \":match\" için ayrılmış: %<PRId64>"
+#, c-format
+msgid "E1109: List item %d is not a List"
+msgstr "E1109: Liste ögesi %d, bir Liste değil"
+
+#, c-format
+msgid "E1110: List item %d does not contain 3 numbers"
+msgstr "E1110: Liste ögesi %d, 3 adet sayı içermiyor"
+
+#, c-format
+msgid "E1111: List item %d range invalid"
+msgstr "E1111: Liste ögesi %d erimi geçersiz"
+
+#, c-format
+msgid "E1112: List item %d cell width invalid"
+msgstr "E1112: Liste ögesi %d hücre genişliği geçersiz"
+
+#, c-format
+msgid "E1113: Overlapping ranges for 0x%lx"
+msgstr "E1113: 0x%lx için üst üste binen erimler"
+
+msgid "E1114: Only values of 0x100 and higher supported"
+msgstr "E1114: Yalnızca 0x100 ve üstü değerler desteklenir"
+
msgid "E293: block was not locked"
msgstr "E293: Blok kilitlenmemişti"
@@ -4003,8 +3811,8 @@ msgstr "E326: Çok fazla takas dosyası bulundu"
msgid ""
"E303: Unable to create directory \"%s\" for swap file, recovery impossible: "
"%s"
-msgstr "E303: Takas dosyası için \"%s\" dizini oluşturulamadı, kurtarma "
-"olanaksız: %s"
+msgstr ""
+"E303: Takas dosyası için \"%s\" dizini oluşturulamadı, kurtarma olanaksız: %s"
msgid "Vim: Data too large to fit into virtual memory space\n"
msgstr "Vim: Veri, sanal bellek alanına sığmak için çok büyük\n"
@@ -4016,9 +3824,6 @@ msgstr "E342: Bellek tükendi!! (%<PRIu64> bayt ayrılıyor)"
msgid "E327: Part of menu-item path is not sub-menu"
msgstr "E327: Menü öge yolunun bir kısmı alt menü değil"
-msgid "E328: Menu only exists in another mode"
-msgstr "E328: Menü yalnızca başka bir kipte mevcut"
-
#, c-format
msgid "E329: No menu \"%s\""
msgstr "E329: Menü \"%s\" yok"
@@ -4053,9 +3858,15 @@ msgstr "E333: Menü yolu bir menü ögesine çıkmalı"
msgid "E334: Menu not found: %s"
msgstr "E334: Menü bulunamadı: %s"
+msgid "E336: Menu path must lead to a sub-menu"
+msgstr "E336: Menü yolu bir alt menüye çıkmalı"
+
+msgid "E337: Menu not found - check menu names"
+msgstr "E337: Menü bulunamadı - menü adlarını denetleyin"
+
#, c-format
msgid "Error detected while processing %s:"
-msgstr "%s işlenirken hata tespit edildi:"
+msgstr "%s işlenirken hata algılandı:"
#, c-format
msgid "line %4ld:"
@@ -4117,10 +3928,10 @@ msgstr ""
"İ&ptal"
msgid "E349: No identifier under cursor"
-msgstr "E349: İmleç altında bir tanımlayıcı yok"
+msgstr "E349: İmleç altında tanımlayıcı yok"
msgid "E348: No string under cursor"
-msgstr "E348: İmleç altında bir dizi yok"
+msgstr "E348: İmleç altında dizi yok"
msgid "E352: Cannot erase folds with current 'foldmethod'"
msgstr "E352: Şu anki 'foldmethod' ile kıvırmalar silinemiyor"
@@ -4177,22 +3988,24 @@ msgstr "%<PRId64> Sütun; "
msgid ""
"Selected %s%<PRId64> of %<PRId64> Lines; %<PRId64> of %<PRId64> Words; "
"%<PRId64> of %<PRId64> Bytes"
-msgstr "%s%<PRId64>/%<PRId64> satır; %<PRId64>/%<PRId64> sözcük; "
-"%<PRId64>/%<PRId64> bayt seçildi"
+msgstr ""
+"%s%<PRId64>/%<PRId64> satır; %<PRId64>/%<PRId64> sözcük; %<PRId64>/%<PRId64> "
+"bayt seçildi"
#, c-format
msgid ""
"Selected %s%<PRId64> of %<PRId64> Lines; %<PRId64> of %<PRId64> Words; "
"%<PRId64> of %<PRId64> Chars; %<PRId64> of %<PRId64> Bytes"
msgstr ""
-"%s%<PRId64>/%<PRId64> satır; %<PRId64>/%<PRId64> sözcük; "
-"%<PRId64>/%<PRId64> karakter; %<PRId64>/%<PRId64> bayt seçildi"
+"%s%<PRId64>/%<PRId64> satır; %<PRId64>/%<PRId64> sözcük; %<PRId64>/%<PRId64> "
+"karakter; %<PRId64>/%<PRId64> bayt seçildi"
#, c-format
msgid ""
"Col %s of %s; Line %<PRId64> of %<PRId64>; Word %<PRId64> of %<PRId64>; Byte "
"%<PRId64> of %<PRId64>"
-msgstr "%s/%s sütun; %<PRId64>/%<PRId64> satır; %<PRId64>/%<PRId64> sözcük; "
+msgstr ""
+"%s/%s sütun; %<PRId64>/%<PRId64> satır; %<PRId64>/%<PRId64> sözcük; "
"%<PRId64>/%<PRId64> bayt"
#, c-format
@@ -4225,59 +4038,6 @@ msgstr "E846: Anahtar kodu ayarlanmamış"
msgid "E521: Number required after ="
msgstr "E521: = sonrası sayı gerekiyor"
-#, c-format
-msgid "E539: Illegal character <%s>"
-msgstr "E539: İzin verilmeyen karakter <%s>"
-
-#, c-format
-msgid "For option %s"
-msgstr "%s seçeneği için"
-
-msgid "E589: 'backupext' and 'patchmode' are equal"
-msgstr "E589: 'backupext' ve 'patchmode' birbirine eşit"
-
-msgid "E834: Conflicts with value of 'listchars'"
-msgstr "E834: 'listchars' değeriyle çakışmalar var"
-
-msgid "E835: Conflicts with value of 'fillchars'"
-msgstr "E835: 'fillchars' değeriyle çakışmalar var"
-
-msgid "E524: Missing colon"
-msgstr "E524: İki nokta eksik"
-
-msgid "E525: Zero length string"
-msgstr "E525: Sıfır uzunlukta dizi"
-
-#, c-format
-msgid "E526: Missing number after <%s>"
-msgstr "E526: <%s> sonrası sayı eksik"
-
-msgid "E527: Missing comma"
-msgstr "E527: Virgül eksik"
-
-msgid "E528: Must specify a ' value"
-msgstr "E528: Bir ' değeri belirtmeli"
-
-msgid "E595: 'showbreak' contains unprintable or wide character"
-msgstr "E595: 'showbreak' yazdırılamaz veya geniş karakter içeriyor"
-
-#, c-format
-msgid "E535: Illegal character after <%c>"
-msgstr "E535: <%c> sonrası izin verilmeyen karakter"
-
-msgid "E536: comma required"
-msgstr "E536: Virgül gerekiyor"
-
-#, c-format
-msgid "E537: 'commentstring' must be empty or contain %s"
-msgstr "E537: 'commentstring' boş olmalı veya %s içermeli"
-
-msgid "E540: Unclosed expression sequence"
-msgstr "E540: Kapatılmamış ifade sıralaması"
-
-msgid "E542: unbalanced groups"
-msgstr "E542: Dengelenmemiş gruplar"
-
msgid "E590: A preview window already exists"
msgstr "E590: Bir önizleme penceresi hâlihazırda mevcut"
@@ -4324,13 +4084,52 @@ msgstr ""
msgid "E356: get_varp ERROR"
msgstr "E356: get_varp HATASI"
+msgid "E540: Unclosed expression sequence"
+msgstr "E540: Kapatılmamış ifade sıralaması"
+
+msgid "E542: unbalanced groups"
+msgstr "E542: Dengelenmemiş gruplar"
+
+msgid "E589: 'backupext' and 'patchmode' are equal"
+msgstr "E589: 'backupext' ve 'patchmode' birbirine eşit"
+
+msgid "E595: 'showbreak' contains unprintable or wide character"
+msgstr "E595: 'showbreak' yazdırılamaz veya geniş karakter içeriyor"
+
#, c-format
-msgid "E357: 'langmap': Matching character missing for %s"
-msgstr "E357: 'langmap': %s için eşleşen karakter eksik"
+msgid "E539: Illegal character <%s>"
+msgstr "E539: İzin verilmeyen karakter <%s>"
#, c-format
-msgid "E358: 'langmap': Extra characters after semicolon: %s"
-msgstr "E358: 'langmap': Noktalı virgülden sonra ek karakterler: %s"
+msgid "For option %s"
+msgstr "%s seçeneği için"
+
+msgid "E524: Missing colon"
+msgstr "E524: İki nokta eksik"
+
+msgid "E525: Zero length string"
+msgstr "E525: Sıfır uzunlukta dizi"
+
+#, c-format
+msgid "E526: Missing number after <%s>"
+msgstr "E526: <%s> sonrası sayı eksik"
+
+msgid "E527: Missing comma"
+msgstr "E527: Virgül eksik"
+
+msgid "E528: Must specify a ' value"
+msgstr "E528: Bir ' değeri belirtmeli"
+
+#, c-format
+msgid "E535: Illegal character after <%c>"
+msgstr "E535: <%c> sonrası izin verilmeyen karakter"
+
+msgid "E536: comma required"
+msgstr "E536: Virgül gerekiyor"
+
+#, c-format
+msgid "E537: 'commentstring' must be empty or contain %s"
+msgstr "E537: 'commentstring' boş olmalı veya %s içermeli"
#, c-format
msgid "dlerror = \"%s\""
@@ -4368,6 +4167,9 @@ msgstr "%a %b %d %H:%M:%S %Y"
msgid "E447: Can't find file \"%s\" in path"
msgstr "E447: \"%s\" dosyası yol içinde bulunamadı"
+msgid "E750: First use \":profile start {fname}\""
+msgstr "E750: İlk kullanım \":profile start {dosyaadı}\""
+
msgid "E553: No more items"
msgstr "E553: Öge yok"
@@ -4383,7 +4185,7 @@ msgstr "E372: Biçim dizisinde pek fazla %%%c"
#, c-format
msgid "E373: Unexpected %%%c in format string"
-msgstr "E373: Biçimlendirme dizisinde beklenmeyen %%%c"
+msgstr "E373: Biçimlendirme dizisinde beklenmedik %%%c"
msgid "E374: Missing ] in format string"
msgstr "E374: Biçimlendirme dizisinde ] eksik"
@@ -4446,6 +4248,10 @@ msgid "E777: String or List expected"
msgstr "E777: Dizi veya liste bekleniyordu"
#, c-format
+msgid "E927: Invalid action: '%s'"
+msgstr "E927: Geçersiz eylem: '%s'"
+
+#, c-format
msgid "E369: invalid item in %s%%[]"
msgstr "E369: %s%%[] içinde geçersiz öge"
@@ -4492,13 +4298,16 @@ msgstr "E956: Dizgi özyineli olarak kullanılamaz"
msgid "E1204: No Number allowed after .: '\\%%%c'"
msgstr "E1204: . sonrası Sayıya izin verilmiyor: '\\%%%c'"
+msgid "E1290: substitute nesting too deep"
+msgstr "E1290: Değiştirme iç içe geçmesi pek derin"
+
#, c-format
msgid "E554: Syntax error in %s{...}"
-msgstr "E554: %s{...} içinde sözdizimi hatası"
+msgstr "E554: %s{...} içinde sözdizim hatası"
#, c-format
msgid "E888: (NFA regexp) cannot repeat %s"
-msgstr "E888: (BSO düzenli ifadesi) %s tekrar edemiyor"
+msgstr "E888: (BSO düzenli ifadesi) %s yinelenemiyor"
msgid ""
"E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be "
@@ -4530,6 +4339,77 @@ msgstr "\"%s\", çalışma zamanı yolu içinde aranıyor"
msgid "not found in runtime path: \"%s\""
msgstr "çalışma zamanı yolu içinde bulunamadı: \"%s\""
+#, c-format
+msgid "Cannot source a directory: \"%s\""
+msgstr "Dizin kaynak alınamıyor: \"%s\""
+
+#, c-format
+msgid "could not source \"%s\""
+msgstr "\"%s\" kaynak alınamadı"
+
+#, c-format
+msgid "line %<PRId64>: could not source \"%s\""
+msgstr "%<PRId64>. satır: \"%s\" kaynak alınamadı"
+
+#, c-format
+msgid "sourcing \"%s\""
+msgstr "\"%s\" kaynak alınıyor"
+
+#, c-format
+msgid "line %<PRId64>: sourcing \"%s\""
+msgstr "%<PRId64>. satır: \"%s\" kaynak alınıyor"
+
+#, c-format
+msgid "finished sourcing %s"
+msgstr "%s kaynak alınması bitti"
+
+msgid "modeline"
+msgstr "kip satırı"
+
+msgid "--cmd argument"
+msgstr "--cmd argümanı"
+
+msgid "-c argument"
+msgstr "-c argümanı"
+
+msgid "environment variable"
+msgstr "ortam değişkeni"
+
+msgid "error handler"
+msgstr "hata işleyicisi"
+
+msgid "changed window size"
+msgstr "değiştirilen pencere boyutu"
+
+msgid "Lua"
+msgstr "Lua"
+
+#, c-format
+msgid "API client (channel id %<PRIu64>)"
+msgstr "API istemcisi (kanal kimliği %<PRIu64>"
+
+msgid "anonymous :source"
+msgstr "anonim :source"
+
+#, c-format
+msgid "anonymous :source (script id %d)"
+msgstr "anonim :source (betik kimliği %d)"
+
+msgid "W15: Warning: Wrong line separator, ^M may be missing"
+msgstr "W15: Uyarı: Yanlış satır ayırıcısı, ^M eksik olabilir"
+
+msgid "E167: :scriptencoding used outside of a sourced file"
+msgstr "E167: :scriptencoding kaynak alınmış bir dosyanın dışında kullanıldı"
+
+msgid "E168: :finish used outside of a sourced file"
+msgstr "E168: :finish kaynak alınmış bir dosyanın dışında kullanıldı"
+
+msgid "E834: Conflicts with value of 'listchars'"
+msgstr "E834: 'listchars' değeriyle çakışmalar var"
+
+msgid "E835: Conflicts with value of 'fillchars'"
+msgstr "E835: 'fillchars' değeriyle çakışmalar var"
+
msgid " TERMINAL"
msgstr " UÇBİRİM"
@@ -4545,6 +4425,9 @@ msgstr " GERİ AL"
msgid " INSERT"
msgstr " EKLE"
+msgid " (terminal)"
+msgstr " (uçbirim)"
+
msgid " (insert)"
msgstr " (ekle)"
@@ -4764,13 +4647,14 @@ msgstr ""
#, c-format
msgid "Did not rename %s to %s because there were errors during writing it"
-msgstr "%s dosyasını %s olarak yeniden adlandırmanızın nedeni yazma sırasında "
+msgstr ""
+"%s dosyasını %s olarak yeniden adlandırmanızın nedeni yazma sırasında "
"hatalar olması mı?"
#, c-format
msgid "Do not forget to remove %s or rename it manually to %s."
-msgstr "%s dosyasını kaldırmayı veya el ile %s olarak yeniden adlandırmayı "
-"unutmayın."
+msgstr ""
+"%s dosyasını kaldırmayı veya el ile %s olarak yeniden adlandırmayı unutmayın."
#, c-format
msgid "System error while reading ShaDa file: %s"
@@ -4801,8 +4685,8 @@ msgid ""
"Error while reading ShaDa file: there is an item at position %<PRIu64> that "
"is stated to be too long"
msgstr ""
-"Paylaşılan veri dosyası okunurken hata: %<PRIu64> konumunda bir öge var; ancak"
-" pek uzun olduğu belirtildi"
+"Paylaşılan veri dosyası okunurken hata: %<PRIu64> konumunda bir öge var; "
+"ancak pek uzun olduğu belirtildi"
#, c-format
msgid ""
@@ -4915,21 +4799,6 @@ msgstr "E797: SpellFileMissing otokomutu arabelleği sildi"
msgid "Warning: region %s not supported"
msgstr "Uyarı: %s bölgesi desteklenmiyor"
-msgid "Sorry, no suggestions"
-msgstr "Üzgünüm, şu an için bir önerim yok"
-
-#, c-format
-msgid "Sorry, only %<PRId64> suggestions"
-msgstr "Üzgünüm, yalnızca %<PRId64> öneri"
-
-#, c-format
-msgid "Change \"%.*s\" to:"
-msgstr "\"%.*s\" şuna değiştirilecek:"
-
-#, c-format
-msgid " < \"%.*s\""
-msgstr " < \"%.*s\""
-
msgid "E752: No previous spell replacement"
msgstr "E752: Öncesinde düzeltilmiş bir yazım yok"
@@ -4940,6 +4809,9 @@ msgstr "E753: Bulunamadı: %s"
msgid "E758: Truncated spell file"
msgstr "E758: Kırpılmış yazım dosyası"
+msgid "E1280: Illegal character in word"
+msgstr "E1280: Sözcükte izin verilmeyen karakter"
+
#, c-format
msgid "Trailing text in %s line %d: %s"
msgstr "%s içinde %d. satır ucunda fazladan metin: %s"
@@ -5058,10 +4930,10 @@ msgstr "%s içinde %d. satırda yinelenen ek: %s"
#, c-format
msgid ""
-"Affix also used for BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGESTin %s "
+"Affix also used for BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST in %s "
"line %d: %s"
msgstr ""
-"Ek aynı zamanda %s içinde %d. satırda BAD/RARE/KEEPCASE/NEEDAFFIX/"
+"Sonek aynı zamanda %s içinde %d. satırda BAD/RARE/KEEPCASE/NEEDAFFIX/"
"NEEDCOMPOUND/NOSUGGEST için de kullanılmış: %s"
#, c-format
@@ -5257,6 +5129,27 @@ msgstr "E763: Sözcük karakterleri yazım dosyaları arasında ayrım gösteriy
msgid "E783: duplicate char in MAP entry"
msgstr "E783: MAP girdisinde yinelenen karakter"
+msgid "Sorry, no suggestions"
+msgstr "Üzgünüm, şu an için bir önerim yok"
+
+#, c-format
+msgid "Sorry, only %<PRId64> suggestions"
+msgstr "Üzgünüm, yalnızca %<PRId64> öneri"
+
+#, c-format
+msgid "Change \"%.*s\" to:"
+msgstr "\"%.*s\" şuna değiştirilecek:"
+
+#, c-format
+msgid " < \"%.*s\""
+msgstr " < \"%.*s\""
+
+msgid "[Help]"
+msgstr "[Yardım]"
+
+msgid "[Preview]"
+msgstr "[Önizleme]"
+
msgid "E766: Insufficient arguments for printf()"
msgstr "E766: printf() için yetersiz argüman"
@@ -5512,7 +5405,7 @@ msgid "Beep!"
msgstr "Bip!"
msgid "E881: Line count changed unexpectedly"
-msgstr "E881: Satır sayısı beklenmeyen bir biçimde değişti"
+msgstr "E881: Satır sayısı beklenmedik bir biçimde değişti"
#, c-format
msgid "E828: Cannot open undo file for writing: %s"
@@ -5631,6 +5524,76 @@ msgstr "E439: Geri al listesi hasarlı"
msgid "E440: undo line missing"
msgstr "E440: Geri al satırı eksik"
+msgid "E1208: -complete used without allowing arguments"
+msgstr "E1208: -complete, argümanlara izin vermeden kullanıldı"
+
+#, c-format
+msgid "E184: No such user-defined command: %s"
+msgstr "E184: Böyle bir kullanıcı tanımlı komut yok: %s"
+
+#, c-format
+msgid "E1237: No such user-defined command in current buffer: %s"
+msgstr "E1237: Geçerli arabellekte böyle bir kullanıcı tanımlı komut yok: %s"
+
+msgid ""
+"\n"
+" Name Args Address Complete Definition"
+msgstr ""
+"\n"
+" Ad Dğkl Adres Tam Tanım"
+
+msgid "No user-defined commands found"
+msgstr "Kullanıcı tanımlı bir komut bulunamadı"
+
+#, c-format
+msgid "E180: Invalid address type value: %s"
+msgstr "E180: Geçersiz adres türü değeri: %s"
+
+#, c-format
+msgid "E180: Invalid complete value: %s"
+msgstr "E180: Geçersiz tam değer: %s"
+
+msgid "E468: Completion argument only allowed for custom completion"
+msgstr "E468: Tamamlama argümanına yalnızca özel tamamlamalarda izin verilir"
+
+msgid "E467: Custom completion requires a function argument"
+msgstr "E467: Özel tamamlama bir işlev argümanı gerektirir"
+
+msgid "E175: No attribute specified"
+msgstr "E175: Bir öznitelik belirtilmemiş"
+
+msgid "E176: Invalid number of arguments"
+msgstr "E176: Geçersiz argüman sayısı"
+
+msgid "E177: Count cannot be specified twice"
+msgstr "E177: Sayım iki defa belirtilemez"
+
+msgid "E178: Invalid default value for count"
+msgstr "E178: Sayım için geçersiz öntanımlı değer"
+
+msgid "E179: argument required for -complete"
+msgstr "E179: -complete için argüman gerekiyor"
+
+msgid "E179: argument required for -addr"
+msgstr "E179: -addr için argüman gerekiyor"
+
+#, c-format
+msgid "E181: Invalid attribute: %s"
+msgstr "E181: Geçersiz öznitelik: %s"
+
+#, c-format
+msgid "E174: Command already exists: add ! to replace it: %s"
+msgstr "E174: Komut zaten mevcut: Değiştirmek için ! ekleyin: %s"
+
+msgid "E182: Invalid command name"
+msgstr "E182: Geçersiz komut adı"
+
+msgid "E183: User defined commands must start with an uppercase letter"
+msgstr "E183: Kullanıcı tanımlı komutlar BÜYÜK harfle başlamalıdır"
+
+msgid "E841: Reserved name, cannot be used for user defined command"
+msgstr "E841: Ayrılmış ad, kullanıcı tanımlı komut için kullanılamaz"
+
msgid ""
"\n"
"\n"
@@ -5662,9 +5625,6 @@ msgstr "$VIMRUNTIME öntanımlı konumu: \""
msgid "Nvim is open source and freely distributable"
msgstr "Nvim açık kaynaklıdır ve özgürce dağıtılabilir"
-msgid "https://neovim.io/#chat"
-msgstr "https://neovim.io/#chat"
-
msgid "type :help nvim<Enter> if you are new! "
msgstr "eğer yeniyseniz :help nvim<Enter> "
@@ -5864,12 +5824,18 @@ msgstr "E15: Lambda için kapatma kıvrımlı ayracı eksik: %.*s"
msgid "E109: Missing ':' after '?': %.*s"
msgstr "E109: '?' sonrası ':' eksik: %.*s"
+msgid "E1159: Cannot split a window when closing the buffer"
+msgstr "E1159: Arabellek kapatılırken bir pencere bölünemez"
+
msgid "Already only one window"
msgstr "Zaten tek pencere"
msgid "E441: There is no preview window"
msgstr "E441: Önizleme penceresi yok"
+msgid "E242: Can't split a window while closing another"
+msgstr "E242: Bir pencere kapatılırken başka bir pencere bölünemez"
+
msgid "E442: Can't split topleft and botright at the same time"
msgstr "E442: Üst sol ve alt sağ pencereler aynı anda bölünemez"
@@ -5886,4 +5852,4 @@ msgid "E445: Other window contains changes"
msgstr "E445: Diğer pencerede değişiklikler var"
msgid "E446: No file name under cursor"
-msgstr "E446: İmleç altında bir dosya adı yok"
+msgstr "E446: İmleç altında dosya adı yok"
diff --git a/src/nvim/po/uk.po b/src/nvim/po/uk.po
index da87d50683..06f845f113 100644
--- a/src/nvim/po/uk.po
+++ b/src/nvim/po/uk.po
@@ -2913,99 +2913,6 @@ msgstr "Пошук дійшов до КІНЦЯ, продовжується з
msgid " line "
msgstr " рядок "
-msgid "E550: Missing colon"
-msgstr "E550: Пропущено двокрапку"
-
-msgid "E551: Illegal component"
-msgstr "E551: Некоректний компонент"
-
-msgid "E552: digit expected"
-msgstr "E552: очікується цифра"
-
-#, c-format
-msgid "Page %d"
-msgstr "Сторінка %d"
-
-msgid "No text to be printed"
-msgstr "Нічого друкувати"
-
-#, c-format
-msgid "Printing page %d (%zu%%)"
-msgstr "Друкується сторінка %d (%zu%%)"
-
-#, c-format
-msgid " Copy %d of %d"
-msgstr " Копія %d з %d"
-
-#, c-format
-msgid "Printed: %s"
-msgstr "Надруковано: %s"
-
-msgid "Printing aborted"
-msgstr "Друк перервано"
-
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: Не вдалося записати вихідний файл PostScript"
-
-#, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E624: Не вдалося відкрити файл «%s»"
-
-#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: Не вдалося прочитати файл ресурсів PostScript «%s»"
-
-#, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E618: «%s» не є файлом ресурсів PostScript"
-
-#, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr "E619: «%s» не є підтримуваним файлом ресурсів PostScript"
-
-#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr "E621: Неправильна версія файлу ресурсів «%s»"
-
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr "E673: Несумісні багатобайтове кодування й набір символів."
-
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr ""
-"E674: printmbcharset не може бути порожнім з багатобайтовим кодуванням."
-
-msgid "E675: No default font specified for multi-byte printing."
-msgstr "E675: Не зазначено шрифт для багатобайтового друку."
-
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: Не вдалося відкрити файл PostScript для виводу"
-
-#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: Не вдалося відкрити файл «%s»"
-
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E456: Не вдалося знайти файл ресурсів PostScript «prolog.ps»"
-
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr "E456: Не вдалося знайти файл ресурсів PostScript «cidfont.ps»"
-
-#, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E456: Не вдалося знайти файл ресурсів PostScript «%s.ps»"
-
-#, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr "E620: Не вдалося перетворити до кодування друку «%s»"
-
-msgid "Sending to printer..."
-msgstr "Відсилається на принтер..."
-
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: Не вдалося надрукувати файл PostScript"
-
-msgid "Print job sent."
-msgstr "Завдання друку відіслано."
msgid "E424: Too many different highlighting attributes in use"
msgstr "E424: Використано забагато різних атрибутів кольору"
@@ -3058,8 +2965,8 @@ msgstr "E423: Неправильний аргумент: %s"
msgid "E669: Unprintable character in group name"
msgstr "E669: Недруковний символ у назві групи"
-msgid "W18: Invalid character in group name"
-msgstr "W18: Некоректний символ у назві групи"
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Некоректний символ у назві групи"
msgid "E849: Too many highlight and syntax groups"
msgstr "E849: Забагато груп підсвічування і синтаксису"
@@ -5643,9 +5550,6 @@ msgstr " заміна для $VIMRUNTIME: \""
msgid "Nvim is open source and freely distributable"
msgstr "Nvim — це відкрита й вільно розповсюджувана програма"
-msgid "https://neovim.io/#chat"
-msgstr "https://neovim.io/#chat"
-
msgid "type :help nvim<Enter> if you are new! "
msgstr ":help nvim<Enter> якщо ви вперше! "
diff --git a/src/nvim/po/vi.po b/src/nvim/po/vi.po
index c693f910d8..44772c99ad 100644
--- a/src/nvim/po/vi.po
+++ b/src/nvim/po/vi.po
@@ -3020,127 +3020,6 @@ msgstr "tìm kiếm sẽ được tiếp tục từ CUỐI tài liệu"
msgid "search hit BOTTOM, continuing at TOP"
msgstr "tìm kiếm sẽ được tiếp tục từ ĐẦU tài liệu"
-#: ../hardcopy.c:240
-msgid "E550: Missing colon"
-msgstr "E550: Thiếu dấu hai chấm"
-
-#: ../hardcopy.c:252
-msgid "E551: Illegal component"
-msgstr "E551: Thành phần không cho phép"
-
-#: ../hardcopy.c:259
-msgid "E552: digit expected"
-msgstr "E552: Cần chỉ ra một số"
-
-#: ../hardcopy.c:473
-#, c-format
-msgid "Page %d"
-msgstr "Trang %d"
-
-#: ../hardcopy.c:597
-msgid "No text to be printed"
-msgstr "Không có gì để in"
-
-#: ../hardcopy.c:668
-#, c-format
-msgid "Printing page %d (%d%%)"
-msgstr "In trang %d (%d%%)"
-
-#: ../hardcopy.c:680
-#, c-format
-msgid " Copy %d of %d"
-msgstr " Sao chép %d của %d"
-
-#: ../hardcopy.c:733
-#, c-format
-msgid "Printed: %s"
-msgstr "Đã in: %s"
-
-#: ../hardcopy.c:740
-msgid "Printing aborted"
-msgstr "In bị dừng"
-
-#: ../hardcopy.c:1365
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: Lỗi ghi nhớ vào tập tin PostScript"
-
-#: ../hardcopy.c:1747
-#, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E624: Không thể mở tập tin \"%s\""
-
-#: ../hardcopy.c:1756 ../hardcopy.c:2470
-#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: Không thể đọc tập tin tài nguyên PostScript \"%s\""
-
-#: ../hardcopy.c:1772
-#, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E618: \"%s\" không phải là tập tin tài nguyên PostScript"
-
-#: ../hardcopy.c:1788 ../hardcopy.c:1805 ../hardcopy.c:1844
-#, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr "E619: \"%s\" không phải là tập tin tài nguyên PostScript được hỗ trợ"
-
-#: ../hardcopy.c:1856
-#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr "E621: tập tin tài nguyên \"%s\" có phiên bản không đúng"
-
-#: ../hardcopy.c:2225
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr ""
-
-#: ../hardcopy.c:2238
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr ""
-
-#: ../hardcopy.c:2254
-msgid "E675: No default font specified for multi-byte printing."
-msgstr ""
-
-#: ../hardcopy.c:2426
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: Không thể mở tập tin PostScript"
-
-#: ../hardcopy.c:2458
-#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: Không thể mở tập tin \"%s\""
-
-#: ../hardcopy.c:2583
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E456: Không tìm thấy tập tin tài nguyên PostScript \"prolog.ps\""
-
-#: ../hardcopy.c:2593
-#, fuzzy
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr "E456: Không tìm thấy tập tin tài nguyên PostScript \"%s.ps\""
-
-#: ../hardcopy.c:2622 ../hardcopy.c:2639 ../hardcopy.c:2665
-#, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E456: Không tìm thấy tập tin tài nguyên PostScript \"%s.ps\""
-
-#: ../hardcopy.c:2654
-#, fuzzy, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr "E620: Không thể chuyển từ các ký tự nhiều byte thành bảng mã \"%s\""
-
-#: ../hardcopy.c:2877
-msgid "Sending to printer..."
-msgstr "Gửi tới máy in..."
-
-#: ../hardcopy.c:2881
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: In tập tin PostScript không thành công"
-
-#: ../hardcopy.c:2883
-msgid "Print job sent."
-msgstr "Đã gửi công việc in."
-
#: ../if_cscope.c:85
msgid "Add a new database"
msgstr "Thêm một cơ sở dữ liệu mới"
@@ -5975,9 +5854,9 @@ msgstr "E424: Sử dụng quá nhiều thuộc tính chiếu sáng cú pháp"
msgid "E669: Unprintable character in group name"
msgstr "E669: Ký tự không thể tin ra trong tên nhóm"
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
-msgstr "W18: Ký tự không cho phép trong tên nhóm"
+#: ../highlight_group.c:1756
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Ký tự không cho phép trong tên nhóm"
#: ../syntax.c:7448
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/zh_CN.UTF-8.po b/src/nvim/po/zh_CN.UTF-8.po
index 70c1389d7f..af050823d3 100644
--- a/src/nvim/po/zh_CN.UTF-8.po
+++ b/src/nvim/po/zh_CN.UTF-8.po
@@ -8,6 +8,9 @@
# TRANSLATORS
# Edyfox <edyfox@gmail.com>
# Yuheng Xie <elephant@linux.net.cn>
+# lilydjwg <lilydjwg@gmail.com>
+# Ada (Haowen) Yu <me@yuhaowen.com>
+# Sizhe Zhao <prc.zhao@outlook.com>
#
# Original translations.
#
@@ -15,1185 +18,1939 @@ msgid ""
msgstr ""
"Project-Id-Version: Vim(Simplified Chinese)\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-05-26 14:21+0200\n"
-"PO-Revision-Date: 2006-04-21 14:00+0800\n"
-"Last-Translator: Yuheng Xie <elephant@linux.net.cn>\n"
+"POT-Creation-Date: 2022-11-17 08:00+0800\n"
+"PO-Revision-Date: 2023-01-24 13:00+0800\n"
+"Last-Translator: Sizhe Zhao <prc.zhao@outlook.com>\n"
"Language-Team: Simplified Chinese <i18n-translation@lists.linux.net.cn>\n"
-"Language: \n"
+"Language: zh_CN\n"
"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=utf-8\n"
-"Content-Transfer-Encoding: 8-bit\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-#: ../api/private/helpers.c:201
+#: ../arglist.c:67
+msgid "E1156: Cannot change the argument list recursively"
+msgstr "E1156: 不能递归修改参数列表"
+
+#: ../arglist.c:628
+msgid "E163: There is only one file to edit"
+msgstr "E163: 只有一个文件可编辑"
+
+#: ../arglist.c:630
+msgid "E164: Cannot go before first file"
+msgstr "E164: 无法切换,已是第一个文件"
+
+#: ../arglist.c:632
+msgid "E165: Cannot go beyond last file"
+msgstr "E165: 无法切换,已是最后一个文件"
+
+#: ../arglist.c:782
#, fuzzy
-msgid "Unable to get option value"
-msgstr "选项参数后的内容无效"
+msgid "E610: No argument to delete"
+msgstr "E144: :z 不接受非数字的参数"
-#: ../api/private/helpers.c:204
-msgid "internal error: unknown option type"
-msgstr "内部错误:未知的选项类型"
+#: ../arglist.c:980
+msgid "E249: window layout changed unexpectedly"
+msgstr "E249: 窗口布局意外地改变了"
-#: ../buffer.c:92
-msgid "[Location List]"
-msgstr "[Location 列表]"
+#: ../autocmd.c:160
+msgid "--Deleted--"
+msgstr "--已删除--"
-#: ../buffer.c:93
-msgid "[Quickfix List]"
-msgstr "[Quickfix 列表]"
+#: ../autocmd.c:451
+#, c-format
+msgid "auto-removing autocommand: %s <buffer=%d>"
+msgstr "自动删除自动命令: %s <buffer=%d>"
+
+#. the group doesn't exist
+#: ../autocmd.c:501
+#, c-format
+msgid "E367: No such group: \"%s\""
+msgstr "E367: 无此组: \"%s\""
+
+#: ../autocmd.c:505
+#, fuzzy
+msgid "E936: Cannot delete the current group"
+msgstr "E351: 不能在当前的 'foldmethod' 下删除折叠"
+
+#: ../autocmd.c:513
+msgid "W19: Deleting augroup that is still in use"
+msgstr "W19: 删除依旧在使用中的自动组"
+
+#. Highlight title
+#: ../autocmd.c:896
+msgid ""
+"\n"
+"--- Autocommands ---"
+msgstr ""
+"\n"
+"--- 自动命令 ---"
+
+#: ../autocmd.c:1106
+#, c-format
+msgid "E680: <buffer=%d>: invalid buffer number "
+msgstr "E680: <buffer=%d>: 无效的缓冲区号 "
+
+#: ../autocmd.c:1254
+msgid "E217: Can't execute autocommands for ALL events"
+msgstr "E217: 不能对所有事件执行自动命令"
+
+#: ../autocmd.c:1276
+#, c-format
+msgid "No matching autocommands: %s"
+msgstr "没有匹配的自动命令:%s"
+
+#: ../autocmd.c:1693
+msgid "E218: autocommand nesting too deep"
+msgstr "E218: 自动命令嵌套层数过深"
+
+#: ../autocmd.c:2040
+#, c-format
+msgid "%s Autocommands for \"%s\""
+msgstr "%s 自动命令 \"%s\""
+
+#: ../autocmd.c:2049
+#, c-format
+msgid "Executing %s"
+msgstr "执行 %s"
+
+#: ../autocmd.c:2178
+#, c-format
+msgid "autocommand %s"
+msgstr "自动命令 %s"
-#: ../buffer.c:94
+#: ../autocmd.c:2617
+#, c-format
+msgid "E215: Illegal character after *: %s"
+msgstr "E215: * 后面有无效字符: %s"
+
+#: ../autocmd.c:2625
+#, c-format
+msgid "E216: No such event: %s"
+msgstr "E216: 无此事件: %s"
+
+#: ../autocmd.c:2627
+#, c-format
+msgid "E216: No such group or event: %s"
+msgstr "E216: 无此组或事件: %s"
+
+#: ../buffer.c:109
msgid "E855: Autocommands caused command to abort"
msgstr "E855: 自动命令导致命令被停止"
-#: ../buffer.c:135
+#: ../buffer.c:111
+#, c-format
+msgid "E937: Attempt to delete a buffer that is in use: %s"
+msgstr "E937: 试图删除使用中的缓冲区:%s"
+
+#: ../buffer.c:219
msgid "E82: Cannot allocate any buffer, exiting..."
msgstr "E82: 无法分配任何缓冲区,退出程序..."
-#: ../buffer.c:138
+#: ../buffer.c:227
msgid "E83: Cannot allocate buffer, using other one..."
msgstr "E83: 无法分配缓冲区,使用另一个缓冲区..."
-#: ../buffer.c:763
+#: ../buffer.c:1070
msgid "E515: No buffers were unloaded"
msgstr "E515: 没有释放任何缓冲区"
-#: ../buffer.c:765
+#: ../buffer.c:1072
msgid "E516: No buffers were deleted"
msgstr "E516: 没有删除任何缓冲区"
-#: ../buffer.c:767
+#: ../buffer.c:1074
msgid "E517: No buffers were wiped out"
msgstr "E517: 没有清除任何缓冲区"
-#: ../buffer.c:772
-msgid "1 buffer unloaded"
-msgstr "释放了 1 个缓冲区"
-
-#: ../buffer.c:774
-#, c-format
-msgid "%d buffers unloaded"
-msgstr "释放了 %d 个缓冲区"
-
-#: ../buffer.c:777
-msgid "1 buffer deleted"
-msgstr "删除了 1 个缓冲区"
-
-#: ../buffer.c:779
-#, c-format
-msgid "%d buffers deleted"
-msgstr "删除了 %d 个缓冲区"
+#: ../buffer.c:1079
+#, fuzzy, c-format
+msgid "%d buffer unloaded"
+msgid_plural "%d buffers unloaded"
+msgstr[0] "释放了 %d 个缓冲区"
-#: ../buffer.c:782
-msgid "1 buffer wiped out"
-msgstr "清除了 1 个缓冲区"
+#: ../buffer.c:1082
+#, fuzzy, c-format
+msgid "%d buffer deleted"
+msgid_plural "%d buffers deleted"
+msgstr[0] "删除了 %d 个缓冲区"
-#: ../buffer.c:784
-#, c-format
-msgid "%d buffers wiped out"
-msgstr "清除了 %d 个缓冲区"
+#: ../buffer.c:1085
+#, fuzzy, c-format
+msgid "%d buffer wiped out"
+msgid_plural "%d buffers wiped out"
+msgstr[0] "清除了 %d 个缓冲区"
-#: ../buffer.c:806
+#: ../buffer.c:1102
msgid "E90: Cannot unload last buffer"
msgstr "E90: 无法释放最后一个缓冲区"
-#: ../buffer.c:874
+#: ../buffer.c:1191
msgid "E84: No modified buffer found"
msgstr "E84: 没有修改过的缓冲区"
#. back where we started, didn't find anything.
-#: ../buffer.c:903
+#: ../buffer.c:1224
msgid "E85: There is no listed buffer"
msgstr "E85: 没有可列出的缓冲区"
-#: ../buffer.c:913
-#, c-format
-msgid "E86: Buffer %<PRId64> does not exist"
-msgstr "E86: 缓冲区 %<PRId64> 不存在"
-
-#: ../buffer.c:915
+#: ../buffer.c:1237
msgid "E87: Cannot go beyond last buffer"
msgstr "E87: 无法切换,已是最后一个缓冲区"
-#: ../buffer.c:917
+#: ../buffer.c:1239
msgid "E88: Cannot go before first buffer"
msgstr "E88: 无法切换,已是第一个缓冲区"
-#: ../buffer.c:945
+#: ../buffer.c:1277
#, c-format
msgid ""
"E89: No write since last change for buffer %<PRId64> (add ! to override)"
msgstr "E89: 缓冲区 %<PRId64> 已修改但尚未保存 (请加 ! 强制执行)"
+#: ../buffer.c:1290
+#, c-format
+msgid "E89: %s will be killed (add ! to override)"
+msgstr "E89: \"%s\" 会被结束 (请加 ! 强制执行)"
+
+#: ../buffer.c:1698
+msgid "E948: Job still running (add ! to end the job)"
+msgstr "E948: 任务仍在运行(添加 ! 来结束此任务)"
+
+#: ../buffer.c:1700
+msgid "E37: No write since last change (add ! to override)"
+msgstr "E37: 已修改但尚未保存 (可用 ! 强制执行)"
+
+#: ../buffer.c:1709
+msgid "E948: Job still running"
+msgstr "E948: 任务仍在运行"
+
+#: ../buffer.c:1711
+msgid "E37: No write since last change"
+msgstr "E37: 已修改但尚未保存"
+
#. wrap around (may cause duplicates)
-#: ../buffer.c:1423
+#: ../buffer.c:1858
msgid "W14: Warning: List of file names overflow"
msgstr "W14: 警告: 文件名过多"
-#: ../buffer.c:1555 ../quickfix.c:3361
+#: ../buffer.c:2032 ../quickfix.c:6290 ../window.c:195
#, c-format
msgid "E92: Buffer %<PRId64> not found"
msgstr "E92: 找不到缓冲区 %<PRId64>"
-#: ../buffer.c:1798
+#: ../buffer.c:2289
#, c-format
msgid "E93: More than one match for %s"
msgstr "E93: 找到不止一个 %s"
-#: ../buffer.c:1800
+#: ../buffer.c:2291
#, c-format
msgid "E94: No matching buffer for %s"
msgstr "E94: 没有匹配的缓冲区 %s"
-#: ../buffer.c:2161
+#: ../buffer.c:2786
#, c-format
msgid "line %<PRId64>"
msgstr "第 %<PRId64> 行"
-#: ../buffer.c:2233
+#: ../buffer.c:2856
msgid "E95: Buffer with this name already exists"
msgstr "E95: 已有缓冲区使用该名称"
-#: ../buffer.c:2498
+#: ../buffer.c:3109
msgid " [Modified]"
msgstr " [已修改]"
-#: ../buffer.c:2501
+#: ../buffer.c:3111
msgid "[Not edited]"
msgstr "[未编辑]"
-#: ../buffer.c:2504
-msgid "[New file]"
-msgstr "[新文件]"
-
-#: ../buffer.c:2505
+#: ../buffer.c:3115
msgid "[Read errors]"
msgstr "[读错误]"
-#: ../buffer.c:2506 ../buffer.c:3217 ../fileio.c:1807 ../screen.c:4895
+#: ../buffer.c:3117 ../fileio.c:1777 ../statusline.c:122 ../statusline.c:1545
msgid "[RO]"
msgstr "[只读]"
-#: ../buffer.c:2507 ../fileio.c:1807
+#: ../buffer.c:3117 ../fileio.c:1777
msgid "[readonly]"
msgstr "[只读]"
-#: ../buffer.c:2524
-#, c-format
-msgid "1 line --%d%%--"
-msgstr "1 行 --%d%%--"
-
-#: ../buffer.c:2526
-#, c-format
-msgid "%<PRId64> lines --%d%%--"
-msgstr "%<PRId64> 行 --%d%%--"
+#: ../buffer.c:3136
+#, fuzzy, c-format
+msgid "%<PRId64> line --%d%%--"
+msgid_plural "%<PRId64> lines --%d%%--"
+msgstr[0] "%<PRId64> 行 --%d%%--"
-#: ../buffer.c:2530
+#: ../buffer.c:3142
#, c-format
msgid "line %<PRId64> of %<PRId64> --%d%%-- col "
msgstr "行 %<PRId64> / %<PRId64> --%d%%-- 列 "
-#: ../buffer.c:2632 ../buffer.c:4292 ../memline.c:1554
+#: ../buffer.c:3234 ../buffer.c:4143 ../memline.c:1456
msgid "[No Name]"
msgstr "[未命名]"
-#. must be a help buffer
-#: ../buffer.c:2667
+#. Must be a help buffer.
+#: ../buffer.c:3279
msgid "help"
msgstr "帮助"
-#: ../buffer.c:3225 ../screen.c:4883
-msgid "[Help]"
-msgstr "[帮助]"
-
-#: ../buffer.c:3254 ../screen.c:4887
-msgid "[Preview]"
-msgstr "[预览]"
-
-#: ../buffer.c:3528
+#: ../buffer.c:3421
msgid "All"
msgstr "全部"
-#: ../buffer.c:3528
+#: ../buffer.c:3421
msgid "Bot"
msgstr "底端"
-#: ../buffer.c:3531
+#: ../buffer.c:3423
msgid "Top"
msgstr "顶端"
-#: ../buffer.c:4244
-msgid ""
-"\n"
-"# Buffer list:\n"
-msgstr ""
-"\n"
-"# 缓冲区列表:\n"
+#: ../buffer.c:3912
+msgid "E382: Cannot write, 'buftype' option is set"
+msgstr "E382: 无法写入,已设定选项 'buftype'"
-#: ../buffer.c:4289
+#: ../buffer.c:3954
+msgid "[Prompt]"
+msgstr "[提示符]"
+
+#: ../buffer.c:3956
msgid "[Scratch]"
-msgstr ""
+msgstr "[涂鸦]"
-#: ../buffer.c:4529
-msgid ""
-"\n"
-"--- Signs ---"
+#: ../buffer.h:72
+msgid "[Location List]"
+msgstr "[Location 列表]"
+
+#: ../buffer.h:73
+msgid "[Quickfix List]"
+msgstr "[Quickfix 列表]"
+
+#: ../change.c:68
+msgid "W10: Warning: Changing a readonly file"
+msgstr "W10: 警告: 正在修改只读文件"
+
+#: ../channel.c:499
+msgid "can only be opened in headless mode"
msgstr ""
-"\n"
-"--- Signs ---"
-#: ../buffer.c:4538
-#, c-format
-msgid "Signs for %s:"
-msgstr "%s 的 Signs:"
+#: ../channel.c:504
+msgid "channel was already open"
+msgstr "channel 已打开"
-#: ../buffer.c:4543
-#, c-format
-msgid " line=%<PRId64> id=%d name=%s"
-msgstr " 行=%<PRId64> id=%d 名称=%s"
+#: ../channel.c:550 ../channel.c:564 ../channel.c:574
+msgid "Can't send data to closed stream"
+msgstr "无法将数据发送到关闭的 stream"
+
+#: ../channel.c:560 ../channel.c:579
+msgid "Can't send raw data to rpc channel"
+msgstr "无法将 raw data 发送到 rpc channel"
+
+#: ../cmdexpand.c:925
+msgid "tagname"
+msgstr "tag 名"
+
+#: ../cmdexpand.c:928
+msgid " kind file\n"
+msgstr " 类型 文件\n"
+
+#: ../cmdhist.c:616
+msgid "'history' option is zero"
+msgstr "选项 'history' 为零"
-#: ../cursor_shape.c:68
+#: ../context.c:335
+msgid "E474: Failed to convert list to msgpack string buffer"
+msgstr ""
+
+#: ../cursor_shape.c:136
msgid "E545: Missing colon"
msgstr "E545: 缺少冒号"
-#: ../cursor_shape.c:70 ../cursor_shape.c:94
+#: ../cursor_shape.c:139 ../cursor_shape.c:164
msgid "E546: Illegal mode"
msgstr "E546: 无效的模式"
-#: ../cursor_shape.c:134
+#: ../cursor_shape.c:197 ../optionstr.c:510
msgid "E548: digit expected"
msgstr "E548: 此处需要数字"
-#: ../cursor_shape.c:138
+#: ../cursor_shape.c:202
msgid "E549: Illegal percentage"
msgstr "E549: 无效的百分比"
-#: ../diff.c:146
+#: ../debugger.c:106
+msgid "Entering Debug mode. Type \"cont\" to continue."
+msgstr "进入调试模式。输入 \"cont\" 继续运行。"
+
+# do not translate
+#: ../debugger.c:109
+#, c-format
+msgid "Oldval = \"%s\""
+msgstr ""
+
+#: ../debugger.c:114
+#, c-format
+msgid "Newval = \"%s\""
+msgstr "新值 = \"%s\""
+
+#: ../debugger.c:124 ../debugger.c:388
+#, c-format
+msgid "line %<PRId64>: %s"
+msgstr "第 %<PRId64> 行: %s"
+
+#: ../debugger.c:126 ../debugger.c:390
#, c-format
-msgid "E96: Can not diff more than %<PRId64> buffers"
-msgstr "E96: 不能比较(diff) %<PRId64> 个以上的缓冲区"
+msgid "cmd: %s"
+msgstr "命令: %s"
-#: ../diff.c:753
+#: ../debugger.c:347
#, fuzzy
+msgid "frame is zero"
+msgstr "E726: 步长为零"
+
+#: ../debugger.c:354
+#, c-format
+msgid "frame at highest level: %d"
+msgstr "帧级别最高:%d"
+
+#: ../debugger.c:434
+#, c-format
+msgid "Breakpoint in \"%s%s\" line %<PRId64>"
+msgstr "断点 \"%s%s\" 第 %<PRId64> 行"
+
+#: ../debugger.c:684
+#, c-format
+msgid "E161: Breakpoint not found: %s"
+msgstr "E161: 找不到断点: %s"
+
+#: ../debugger.c:717
+msgid "No breakpoints defined"
+msgstr "没有定义断点"
+
+#: ../debugger.c:725
+#, c-format
+msgid "%3d %s %s line %<PRId64>"
+msgstr "%3d %s %s 第 %<PRId64> 行"
+
+#: ../debugger.c:731
+#, c-format
+msgid "%3d expr %s"
+msgstr "%3d 表达式 %s"
+
+#: ../diff.c:206
+#, c-format
+msgid "E96: Cannot diff more than %<PRId64> buffers"
+msgstr "E96: 不能比较 %<PRId64> 个以上的缓冲区"
+
+#: ../diff.c:767
+#, c-format
+msgid "Not enough memory to use internal diff for buffer \"%s\""
+msgstr "为缓冲区 \"%s\" 使用内部比对(diff)时无足够的内存"
+
+#: ../diff.c:1082
msgid "E810: Cannot read or write temp files"
-msgstr "E557: 无法打开 termcap 文件"
+msgstr "E810: 无法读写临时文件"
-#: ../diff.c:755
+#: ../diff.c:1084
msgid "E97: Cannot create diffs"
msgstr "E97: 无法创建 diff"
-#: ../diff.c:966
-#, fuzzy
+#: ../diff.c:1125
+msgid "E960: Problem creating the internal diff"
+msgstr "E960: 创建内部 diff 时遇到问题"
+
+#: ../diff.c:1284
msgid "E816: Cannot read patch output"
-msgstr "E98: 无法读取 diff 的输出"
+msgstr "E816: 无法读取 patch 的输出"
-#: ../diff.c:1220
+#: ../diff.c:1740
msgid "E98: Cannot read diff output"
msgstr "E98: 无法读取 diff 的输出"
-#: ../diff.c:2081
+#: ../diff.c:2847
msgid "E99: Current buffer is not in diff mode"
-msgstr "E99: 当前缓冲区不在 diff 模式"
+msgstr "E99: 当前缓冲区不在差异模式"
-#: ../diff.c:2100
-#, fuzzy
+#: ../diff.c:2867
msgid "E793: No other buffer in diff mode is modifiable"
-msgstr "E100: 没有其它处于 diff 模式的缓冲区"
+msgstr "E793: 没有其它处于差异模式的缓冲区可修改"
-#: ../diff.c:2102
+#: ../diff.c:2869
msgid "E100: No other buffer in diff mode"
-msgstr "E100: 没有其它处于 diff 模式的缓冲区"
+msgstr "E100: 没有其它处于差异模式的缓冲区"
-#: ../diff.c:2112
+#: ../diff.c:2880
msgid "E101: More than two buffers in diff mode, don't know which one to use"
-msgstr "E101: 有两个以上的缓冲区处于 diff 模式,不能决定用哪一个"
+msgstr "E101: 有两个以上的缓冲区处于差异模式,不能决定用哪一个"
-#: ../diff.c:2141
+#: ../diff.c:2909
#, c-format
msgid "E102: Can't find buffer \"%s\""
msgstr "E102: 找不到缓冲区 \"%s\""
-#: ../diff.c:2152
+#: ../diff.c:2920
#, c-format
msgid "E103: Buffer \"%s\" is not in diff mode"
-msgstr "E103: 缓冲区 \"%s\" 不在 diff 模式"
+msgstr "E103: 缓冲区 \"%s\" 不处于差异模式"
-#: ../diff.c:2193
+#: ../diff.c:2960
msgid "E787: Buffer changed unexpectedly"
msgstr "E787: 意外地改变了缓冲区"
-#: ../digraph.c:1598
+#: ../digraph.c:50
+#, c-format
+msgid "E1214: Digraph must be just two characters: %s"
+msgstr "E1214: 二合字符必须只有两个字符:%s"
+
+#: ../digraph.c:52
+#, c-format
+msgid "E1215: Digraph must be one character: %s"
+msgstr "E1215: 二合字符必须是一个字符:%s"
+
+#: ../digraph.c:54
+msgid ""
+"E1216: digraph_setlist() argument must be a list of lists with two items"
+msgstr "E1216: digraph_setlist() 参数必须是包含两项的列表的列表"
+
+#: ../digraph.c:1664
msgid "E104: Escape not allowed in digraph"
-msgstr "E104: 复合字符(digraph)中不能使用 Escape"
+msgstr "E104: 二合字符中不能使用 Escape"
+
+#: ../digraph.c:1737
+msgid "Custom"
+msgstr "自定义"
+
+#: ../digraph.c:1794
+msgid "Latin supplement"
+msgstr "拉丁文补充"
+
+#: ../digraph.c:1795
+msgid "Greek and Coptic"
+msgstr "希腊和科普特文"
+
+#: ../digraph.c:1796
+msgid "Cyrillic"
+msgstr "西里尔文"
+
+#: ../digraph.c:1797
+msgid "Hebrew"
+msgstr "希伯来文"
+
+#: ../digraph.c:1798
+msgid "Arabic"
+msgstr "阿拉伯文"
+
+#: ../digraph.c:1799
+msgid "Latin extended"
+msgstr "拉丁文扩展"
-#: ../digraph.c:1760
+#: ../digraph.c:1800
+msgid "Greek extended"
+msgstr "希腊文扩展"
+
+#: ../digraph.c:1801
+msgid "Punctuation"
+msgstr "标点符号"
+
+#: ../digraph.c:1802
+msgid "Super- and subscripts"
+msgstr "上下标"
+
+#: ../digraph.c:1803
+msgid "Currency"
+msgstr "货币符号"
+
+#: ../digraph.c:1804 ../digraph.c:1809 ../digraph.c:1819
+msgid "Other"
+msgstr "其他"
+
+#: ../digraph.c:1805
+msgid "Roman numbers"
+msgstr "罗马数字"
+
+#: ../digraph.c:1806
+msgid "Arrows"
+msgstr "箭头"
+
+#: ../digraph.c:1807
+msgid "Mathematical operators"
+msgstr "数学运算符"
+
+#: ../digraph.c:1808
+msgid "Technical"
+msgstr "技术符号"
+
+#: ../digraph.c:1810
+msgid "Box drawing"
+msgstr "方框绘图"
+
+#: ../digraph.c:1811
+msgid "Block elements"
+msgstr "块状元素"
+
+#: ../digraph.c:1812
+msgid "Geometric shapes"
+msgstr "几何形状"
+
+#: ../digraph.c:1813
+msgid "Symbols"
+msgstr "符号"
+
+#: ../digraph.c:1814
+msgid "Dingbats"
+msgstr "杂锦字符"
+
+#: ../digraph.c:1815
+msgid "CJK symbols and punctuation"
+msgstr "中日韩标点符号"
+
+#: ../digraph.c:1816
+msgid "Hiragana"
+msgstr "日文平假名"
+
+#: ../digraph.c:1817
+msgid "Katakana"
+msgstr "日文片假名"
+
+#: ../digraph.c:1818
+msgid "Bopomofo"
+msgstr "注音符号"
+
+#: ../digraph.c:2062
msgid "E544: Keymap file not found"
msgstr "E544: 找不到 Keymap 文件"
-#: ../digraph.c:1785
+#: ../digraph.c:2083
msgid "E105: Using :loadkeymap not in a sourced file"
-msgstr "E105: 不是在脚本文件中使用 :loadkeymap "
+msgstr "E105: 不是在脚本文件中使用 :loadkeymap"
-#: ../digraph.c:1821
+#: ../digraph.c:2118
msgid "E791: Empty keymap entry"
msgstr "E791: 空的键位映射项"
-#: ../edit.c:82
-msgid " Keyword completion (^N^P)"
-msgstr " 关键字补全 (^N^P)"
+#. TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead
+#. maximum nesting of lists and dicts
+#: ../eval.c:92
+msgid "E111: Missing ']'"
+msgstr "E111: 缺少 ']'"
-#. ctrl_x_mode == 0, ^P/^N compl.
-#: ../edit.c:83
-msgid " ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"
-msgstr " ^X 模式 (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"
+#: ../eval.c:93
+msgid "E719: Cannot use [:] with a Dictionary"
+msgstr "E719: 不能对 Dictionary 使用 [:]"
-#: ../edit.c:85
-msgid " Whole line completion (^L^N^P)"
-msgstr " 整行补全 (^L^N^P)"
+#: ../eval.c:95
+msgid "E274: No white space allowed before parenthesis"
+msgstr "E274: 小括号前不允许空白字符"
-#: ../edit.c:86
-msgid " File name completion (^F^N^P)"
-msgstr " 文件名补全 (^F^N^P)"
+#: ../eval.c:96
+#, c-format
+msgid "E80: Error while writing: %s"
+msgstr "E80: 写入时出错:%s"
-#: ../edit.c:87
-msgid " Tag completion (^]^N^P)"
-msgstr " Tag 补全 (^]^N^P)"
+#: ../eval.c:97
+msgid "E1098: String, List or Blob required"
+msgstr "E1098: 需要字符串、列表或 Blob"
-#: ../edit.c:88
-msgid " Path pattern completion (^N^P)"
-msgstr " 头文件模式补全 (^N^P)"
+#: ../eval.c:98
+#, c-format
+msgid "E1169: Expression too recursive: %s"
+msgstr "E1169: 表达式递归层数过多:%s"
-#: ../edit.c:89
-msgid " Definition completion (^D^N^P)"
-msgstr " 定义补全 (^D^N^P)"
+#: ../eval.c:100
+#, c-format
+msgid "E1203: Dot can only be used on a dictionary: %s"
+msgstr "E1203: 点只能用于字典:%s"
-#: ../edit.c:91
-msgid " Dictionary completion (^K^N^P)"
-msgstr " Dictionary 补全 (^K^N^P)"
+#: ../eval.c:1095
+msgid ""
+"E5700: Expression from 'spellsuggest' must yield lists with exactly two "
+"values"
+msgstr ""
-#: ../edit.c:92
-msgid " Thesaurus completion (^T^N^P)"
-msgstr " Thesaurus 补全 (^T^N^P)"
+#: ../eval.c:1327 ../eval/vars.c:1112
+#, c-format
+msgid "E121: Undefined variable: %.*s"
+msgstr "E121: 变量未定义: %.*s"
-#: ../edit.c:93
-msgid " Command-line completion (^V^N^P)"
-msgstr " 命令行补全 (^V^N^P)"
+#: ../eval.c:1358
+msgid "E689: Can only index a List, Dictionary or Blob"
+msgstr "E689: 只能索引列表、字典或 Blob"
-#: ../edit.c:94
-msgid " User defined completion (^U^N^P)"
-msgstr " 用户自定义补全 (^U^N^P)"
+#: ../eval.c:1364
+msgid "E708: [:] must come last"
+msgstr "E708: [:] 必须在最后"
-#: ../edit.c:95
-msgid " Omni completion (^O^N^P)"
-msgstr " 全能补全 (^O^N^P)"
+#: ../eval.c:1376
+#, fuzzy
+msgid "E713: Cannot use empty key after ."
+msgstr "E713: Dictionary 的键不能为空"
-#: ../edit.c:96
-msgid " Spelling suggestion (s^N^P)"
-msgstr " 拼写建议 (s^N^P)"
+#: ../eval.c:1412
+#, fuzzy
+msgid "E709: [:] requires a List or Blob value"
+msgstr "E709: [:] 需要一个 List 值"
-#: ../edit.c:97
-msgid " Keyword Local completion (^N^P)"
-msgstr " 关键字局部补全 (^N^P)"
+#: ../eval.c:1665
+msgid "E972: Blob value does not have the right number of bytes"
+msgstr "E972: Blob 值的字节数不对"
-#: ../edit.c:100
-msgid "Hit end of paragraph"
-msgstr "已到段落结尾"
+#: ../eval.c:1728
+msgid "E996: Cannot lock a range"
+msgstr "E996: 不能锁定范围"
-#: ../edit.c:101
-msgid "E839: Completion function changed window"
-msgstr "E839: 补全函数更改了窗口"
+#: ../eval.c:1771
+msgid "E710: List value has more items than target"
+msgstr "E710: List 值的项比目标多"
-#: ../edit.c:102
-msgid "E840: Completion function deleted text"
-msgstr "E840: 补全函数删除了文本"
+#: ../eval.c:1776
+msgid "E711: List value has not enough items"
+msgstr "E711: List 值没有足够多的项"
-#: ../edit.c:1847
-msgid "'dictionary' option is empty"
-msgstr "选项 'dictionary' 为空"
+#: ../eval.c:1784
+msgid "E996: Cannot lock a list or dict"
+msgstr "E996: 不能锁定列表或字典"
-#: ../edit.c:1848
-msgid "'thesaurus' option is empty"
-msgstr "选项 'thesaurus' 为空"
+#: ../eval.c:1866
+msgid "E690: Missing \"in\" after :for"
+msgstr "E690: :for 后缺少 \"in\""
-#: ../edit.c:2655
-#, c-format
-msgid "Scanning dictionary: %s"
-msgstr "正在扫描 dictionary: %s"
+#: ../eval.c:2389
+msgid "E109: Missing ':' after '?'"
+msgstr "E109: '?' 后缺少 ':'"
-#: ../edit.c:3079
-msgid " (insert) Scroll (^E/^Y)"
-msgstr " (插入) Scroll (^E/^Y)"
+#: ../eval.c:2893
+msgid "E804: Cannot use '%' with Float"
+msgstr "E804: 不能对浮点数使用 '%'"
-#: ../edit.c:3081
-msgid " (replace) Scroll (^E/^Y)"
-msgstr " (替换) Scroll (^E/^Y)"
+#: ../eval.c:3037
+msgid "E973: Blob literal should have an even number of hex characters"
+msgstr "E973: Blob 字面量应该有偶数个十六进制字符"
+
+#: ../eval.c:3137
+msgid "E110: Missing ')'"
+msgstr "E110: 缺少 ')'"
+
+#: ../eval.c:3372
+#, fuzzy
+msgid "E260: Missing name after ->"
+msgstr "E526: <%s> 后面缺少数字"
-#: ../edit.c:3587
+#: ../eval.c:3431
+msgid "E695: Cannot index a Funcref"
+msgstr "E695: 不能索引 Funcref"
+
+#: ../eval.c:3442
+msgid "E909: Cannot index a special variable"
+msgstr "E909: 不能索引特殊变量"
+
+#: ../eval.c:3730
#, c-format
-msgid "Scanning: %s"
-msgstr "正在扫描: %s"
+msgid "E112: Option name missing: %s"
+msgstr "E112: 缺少选项名称:%s"
-#: ../edit.c:3614
-msgid "Scanning tags."
-msgstr "扫描标签."
+#: ../eval.c:3751
+#, c-format
+msgid "E113: Unknown option: %s"
+msgstr "E113: 未知的选项:%s"
-#: ../edit.c:4519
-msgid " Adding"
-msgstr " 增加"
+#: ../eval.c:3798
+#, c-format
+msgid "E114: Missing quote: %s"
+msgstr "E114: 缺少引号:%s"
-#. showmode might reset the internal line pointers, so it must
-#. * be called before line = ml_get(), or when this address is no
-#. * longer needed. -- Acevedo.
-#.
-#: ../edit.c:4562
-msgid "-- Searching..."
-msgstr "-- 查找中..."
+#: ../eval.c:3936
+#, c-format
+msgid "E115: Missing quote: %s"
+msgstr "E115: 缺少引号:%s"
-#: ../edit.c:4618
-msgid "Back at original"
-msgstr "回到起点"
+#: ../eval.c:4031
+#, c-format
+msgid "E696: Missing comma in List: %s"
+msgstr "E696: 列表中缺少逗号:%s"
-#: ../edit.c:4621
-msgid "Word from other line"
-msgstr "另一行的词"
+#: ../eval.c:4038
+#, c-format
+msgid "E697: Missing end of List ']': %s"
+msgstr "E697: 列表缺少结束符 ']':%s"
-#: ../edit.c:4624
-msgid "The only match"
-msgstr "唯一匹配"
+#: ../eval.c:4338
+msgid "Not enough memory to set references, garbage collection aborted!"
+msgstr "没有足够的内存来设置引用,垃圾回收已中止!"
-#: ../edit.c:4680
+#: ../eval.c:4687
#, c-format
-msgid "match %d of %d"
-msgstr "匹配 %d / %d"
+msgid "E720: Missing colon in Dictionary: %s"
+msgstr "E720: 字典中缺少冒号:%s"
-#: ../edit.c:4684
+#: ../eval.c:4710
#, c-format
-msgid "match %d"
-msgstr "匹配 %d"
+msgid "E721: Duplicate key in Dictionary: \"%s\""
+msgstr "E721: Dictionary 中出现重复的键: \"%s\""
-#: ../eval.c:137
-msgid "E18: Unexpected characters in :let"
-msgstr "E18: :let 中出现异常字符"
+#: ../eval.c:4728
+#, c-format
+msgid "E722: Missing comma in Dictionary: %s"
+msgstr "E722: 字典中缺少逗号:%s"
-#: ../eval.c:138
+#: ../eval.c:4735
#, c-format
-msgid "E684: list index out of range: %<PRId64>"
-msgstr "E684: List 索引超出范围: %<PRId64>"
+msgid "E723: Missing end of Dictionary '}': %s"
+msgstr "E723: 字典缺少结束符 '}':%s"
+
+#: ../eval.c:4854
+msgid "map() argument"
+msgstr "map() 参数"
+
+#: ../eval.c:4855
+msgid "filter() argument"
+msgstr "filter() 参数"
-#: ../eval.c:139
+#: ../eval.c:5076
#, c-format
-msgid "E121: Undefined variable: %s"
-msgstr "E121: 未定义的变量: %s"
+msgid "E700: Unknown function: %s"
+msgstr "E700: 未知的函数:%s"
-#: ../eval.c:140
-msgid "E111: Missing ']'"
-msgstr "E111: 缺少 ']'"
+#: ../eval.c:5112
+msgid "E922: expected a dict"
+msgstr "E922: 需要字典"
+
+#: ../eval.c:5122
+msgid "E923: Second argument of function() must be a list or a dict"
+msgstr "E923: function() 的第二个参数必须是列表或字典"
+
+#: ../eval.c:5397
+msgid "E5050: {opts} must be the only argument"
+msgstr "E5050: {opts} 必须是唯一的参数"
-#: ../eval.c:141
+#: ../eval.c:5794 ../os/shell.c:718
#, c-format
-msgid "E686: Argument of %s must be a List"
-msgstr "E686: %s 的参数必须是 List"
+msgid "Executing command: \"%s\""
+msgstr "执行命令:\"%s\""
+
+#: ../eval.c:5902
+msgid "E921: Invalid callback argument"
+msgstr "E921: 回调参数无效"
+
+#: ../eval.c:7603
+msgid "E698: variable nested too deep for making a copy"
+msgstr "E698: 变量嵌套过深,无法复制"
+
+#: ../eval.c:8078
+msgid ""
+"\n"
+"\tLast set from "
+msgstr ""
+"\n"
+"\t最近修改于 "
+
+#: ../eval.c:8690
+msgid "E5009: $VIMRUNTIME is empty or unset"
+msgstr "E5009: $VIMRUNTIME 为空或未设置"
-#: ../eval.c:143
+#: ../eval.c:8694
#, c-format
-msgid "E712: Argument of %s must be a List or Dictionary"
-msgstr "E712: %s 的参数必须是 List 或者 Dictionary"
+msgid "E5009: Invalid $VIMRUNTIME: %s"
+msgstr "E5009: $VIMRUNTIME 无效:%s"
-#: ../eval.c:144
-msgid "E713: Cannot use empty key for Dictionary"
-msgstr "E713: Dictionary 的键不能为空"
+#: ../eval.c:8696
+msgid "E5009: Invalid 'runtimepath'"
+msgstr "E5009: 'runtimepath' 无效"
-#: ../eval.c:145
-msgid "E714: List required"
-msgstr "E714: 需要 List"
+#: ../eval.c:8783
+msgid "E977: Can only compare Blob with Blob"
+msgstr "E977: Blob 只能与 Blob 比较"
-#: ../eval.c:146
-msgid "E715: Dictionary required"
-msgstr "E715: 需要 Dictionary"
+#: ../eval.c:8806
+msgid "E691: Can only compare List with List"
+msgstr "E691: 列表只能与列表比较"
+
+#: ../eval.c:8808
+msgid "E692: Invalid operation for List"
+msgstr "E692: 操作对列表无效"
+
+#: ../eval.c:8829
+msgid "E735: Can only compare Dictionary with Dictionary"
+msgstr "E735: 字典只能与字典比较"
+
+#: ../eval.c:8831
+msgid "E736: Invalid operation for Dictionary"
+msgstr "E736: 操作对字典无效"
-#: ../eval.c:147
+#: ../eval.c:8845
+msgid "E694: Invalid operation for Funcrefs"
+msgstr "E694: 操作对 Funcrefs 无效"
+
+#: ../eval/decode.c:132
#, c-format
-msgid "E118: Too many arguments for function: %s"
-msgstr "E118: 函数的参数过多: %s"
+msgid "E474: Expected comma before list item: %s"
+msgstr "E474: 列表项前应有逗号:%s"
-#: ../eval.c:148
+#: ../eval/decode.c:140
#, c-format
-msgid "E716: Key not present in Dictionary: %s"
-msgstr "E716: Dictionary 中不存在键: %s"
+msgid "E474: Expected colon before dictionary value: %s"
+msgstr "E474: 字典值前应有冒号:%s"
-#: ../eval.c:150
+#: ../eval/decode.c:167
#, c-format
-msgid "E122: Function %s already exists, add ! to replace it"
-msgstr "E122: 函数 %s 已存在,请加 ! 强制替换"
+msgid "E474: Expected string key: %s"
+msgstr "E474: 需要字符串键:%s"
-#: ../eval.c:151
-msgid "E717: Dictionary entry already exists"
-msgstr "E717: Dictionary 项已存在"
+#: ../eval/decode.c:173
+#, c-format
+msgid "E474: Expected comma before dictionary key: %s"
+msgstr "E474: 字典键前应有逗号:%s"
-#: ../eval.c:152
-msgid "E718: Funcref required"
-msgstr "E718: 需要 Funcref"
+#: ../eval/decode.c:340
+#, c-format
+msgid "E474: Unfinished escape sequence: %.*s"
+msgstr "E474: 转义序列不完整:%.*s"
-#: ../eval.c:153
-msgid "E719: Cannot use [:] with a Dictionary"
-msgstr "E719: 不能对 Dictionary 使用 [:]"
+#: ../eval/decode.c:347
+#, c-format
+msgid "E474: Unfinished unicode escape sequence: %.*s"
+msgstr "E474: Unicode 转义序列不完整:%.*s"
-#: ../eval.c:154
+#: ../eval/decode.c:354
#, c-format
-msgid "E734: Wrong variable type for %s="
-msgstr "E734: %s= 的变量类型不正确"
+msgid "E474: Expected four hex digits after \\u: %.*s"
+msgstr "E474: \\u 之后应有四位十六进制数字:%.*s"
-#: ../eval.c:155
+#: ../eval/decode.c:375
#, c-format
-msgid "E130: Unknown function: %s"
-msgstr "E130: 未知的函数: %s"
+msgid "E474: Unknown escape sequence: %.*s"
+msgstr "E474: 未知的转义序列:%.*s"
-#: ../eval.c:156
+#: ../eval/decode.c:382
#, c-format
-msgid "E461: Illegal variable name: %s"
-msgstr "E461: 无效的变量名: %s"
+msgid "E474: ASCII control characters cannot be present inside string: %.*s"
+msgstr "E474: 字符串中不能出现 ASCII 控制字符:%.*s"
-#: ../eval.c:157
-#, fuzzy
-msgid "E806: using Float as a String"
-msgstr "E730: 将 List 作 String 使用"
+#: ../eval/decode.c:395
+#, c-format
+msgid "E474: Only UTF-8 strings allowed: %.*s"
+msgstr "E474: 只允许 UTF-8 字符串:%.*s"
-#: ../eval.c:1830
-msgid "E687: Less targets than List items"
-msgstr "E687: 目标比 List 项数少"
+#: ../eval/decode.c:398
+#, c-format
+msgid ""
+"E474: Only UTF-8 code points up to U+10FFFF are allowed to appear unescaped: "
+"%.*s"
+msgstr ""
-#: ../eval.c:1834
-msgid "E688: More targets than List items"
-msgstr "E688: 目标比 List 项数多"
+#: ../eval/decode.c:409
+#, c-format
+msgid "E474: Expected string end: %.*s"
+msgstr ""
-#: ../eval.c:1906
-msgid "Double ; in list of variables"
-msgstr "变量列表中出现两个 ;"
+#: ../eval/decode.c:555
+#, c-format
+msgid "E474: Leading zeroes are not allowed: %.*s"
+msgstr "E474: 不允许前导零:%.*s"
-#: ../eval.c:2078
+#: ../eval/decode.c:584
#, c-format
-msgid "E738: Can't list variables for %s"
-msgstr "E738: 无法列出 %s 的变量"
+msgid "E474: Missing number after minus sign: %.*s"
+msgstr "E474: 负号后缺少数字:%.*s"
-#: ../eval.c:2391
-msgid "E689: Can only index a List or Dictionary"
-msgstr "E689: 只能索引 List 或 Dictionary"
+#: ../eval/decode.c:587
+#, c-format
+msgid "E474: Missing number after decimal dot: %.*s"
+msgstr "E474: 小数点后缺少数字:%.*s"
-#: ../eval.c:2396
-msgid "E708: [:] must come last"
-msgstr "E708: [:] 必须在最后"
+#: ../eval/decode.c:590
+#, c-format
+msgid "E474: Missing exponent: %.*s"
+msgstr "E474: 缺少指数:%.*s"
-#: ../eval.c:2439
-msgid "E709: [:] requires a List value"
-msgstr "E709: [:] 需要一个 List 值"
+#: ../eval/decode.c:602
+#, c-format
+msgid ""
+"E685: internal error: while converting number \"%.*s\" to float string2float "
+"consumed %zu bytes in place of %zu"
+msgstr ""
-#: ../eval.c:2674
-msgid "E710: List value has more items than target"
-msgstr "E710: List 值的项比目标多"
+#: ../eval/decode.c:613
+#, c-format
+msgid ""
+"E685: internal error: while converting number \"%.*s\" to integer vim_str2nr "
+"consumed %i bytes in place of %zu"
+msgstr ""
-#: ../eval.c:2678
-msgid "E711: List value has not enough items"
-msgstr "E711: List 值没有足够多的项"
+#: ../eval/decode.c:665
+msgid "E474: Attempt to decode a blank string"
+msgstr "E474: 试图解码空字符串"
-#: ../eval.c:2867
-msgid "E690: Missing \"in\" after :for"
-msgstr "E690: :for 后缺少 \"in\""
+#: ../eval/decode.c:682
+#, c-format
+msgid "E474: No container to close: %.*s"
+msgstr "E474: 没有容器可以关闭:%.*s"
-#: ../eval.c:3063
+#: ../eval/decode.c:687
#, c-format
-msgid "E107: Missing parentheses: %s"
-msgstr "E107: 缺少括号: %s"
+msgid "E474: Closing list with curly bracket: %.*s"
+msgstr "E474: 用花括号结束列表:%.*s"
-#: ../eval.c:3263
+#: ../eval/decode.c:690
#, c-format
-msgid "E108: No such variable: \"%s\""
-msgstr "E108: 无此变量: \"%s\""
+msgid "E474: Closing dictionary with square bracket: %.*s"
+msgstr "E474: 用方括号结束字典:%.*s"
-#: ../eval.c:3333
-msgid "E743: variable nested too deep for (un)lock"
-msgstr "E743: (un)lock 的变量嵌套过深"
+#: ../eval/decode.c:694
+#, c-format
+msgid "E474: Trailing comma: %.*s"
+msgstr "E474: 尾随逗号:%.*s"
-#: ../eval.c:3630
-msgid "E109: Missing ':' after '?'"
-msgstr "E109: '?' 后缺少 ':'"
+#: ../eval/decode.c:697
+#, c-format
+msgid "E474: Expected value after colon: %.*s"
+msgstr "E474: 冒号后应有值:%.*s"
-#: ../eval.c:3893
-msgid "E691: Can only compare List with List"
-msgstr "E691: 只能比较 List 和 List"
+#: ../eval/decode.c:701
+#, fuzzy, c-format
+msgid "E474: Expected value: %.*s"
+msgstr "E415: 不该有的等号: %s"
-#: ../eval.c:3895
-msgid "E692: Invalid operation for Lists"
-msgstr "E692: 对 List 无效的操作"
+#: ../eval/decode.c:720
+#, fuzzy, c-format
+msgid "E474: Comma not inside container: %.*s"
+msgstr "E242: %s 为不能识别的颜色名称"
-#: ../eval.c:3915
-msgid "E735: Can only compare Dictionary with Dictionary"
-msgstr "E735: 只能比较 Dictionary 和 Dictionary"
+#: ../eval/decode.c:725
+#, fuzzy, c-format
+msgid "E474: Duplicate comma: %.*s"
+msgstr "E721: Dictionary 中出现重复的键: \"%s\""
-#: ../eval.c:3917
-msgid "E736: Invalid operation for Dictionary"
-msgstr "E736: 对 Dictionary 无效的操作"
+#: ../eval/decode.c:728
+#, c-format
+msgid "E474: Comma after colon: %.*s"
+msgstr "E474: 冒号后有逗号:%.*s"
-#: ../eval.c:3932
-msgid "E693: Can only compare Funcref with Funcref"
-msgstr "E693: 只能比较 Funcref 和 Funcref"
+#: ../eval/decode.c:732
+#, c-format
+msgid "E474: Using comma in place of colon: %.*s"
+msgstr "E474: 应使用冒号,但用了逗号:%.*s"
-#: ../eval.c:3934
-msgid "E694: Invalid operation for Funcrefs"
-msgstr "E694: 对 Funcrefs 无效的操作"
+#: ../eval/decode.c:740
+#, c-format
+msgid "E474: Leading comma: %.*s"
+msgstr "E474: 前导逗号:%.*s"
-#: ../eval.c:4277
-#, fuzzy
-msgid "E804: Cannot use '%' with Float"
-msgstr "E719: 不能对 Dictionary 使用 [:]"
+#: ../eval/decode.c:748
+#, fuzzy, c-format
+msgid "E474: Colon not inside container: %.*s"
+msgstr "E242: %s 为不能识别的颜色名称"
-#: ../eval.c:4478
-msgid "E110: Missing ')'"
-msgstr "E110: 缺少 ')'"
+#: ../eval/decode.c:753
+#, c-format
+msgid "E474: Using colon not in dictionary: %.*s"
+msgstr "E474: 在字典之外使用冒号:%.*s"
-#: ../eval.c:4609
-msgid "E695: Cannot index a Funcref"
-msgstr "E695: 不能索引一个 Funcref"
+#: ../eval/decode.c:756
+#, fuzzy, c-format
+msgid "E474: Unexpected colon: %.*s"
+msgstr "E415: 不该有的等号: %s"
-#: ../eval.c:4839
+#: ../eval/decode.c:759
#, c-format
-msgid "E112: Option name missing: %s"
-msgstr "E112: 缺少选项名称: %s"
+msgid "E474: Colon after comma: %.*s"
+msgstr "E474: 逗号后有冒号:%.*s"
-#: ../eval.c:4855
+#: ../eval/decode.c:762
#, c-format
-msgid "E113: Unknown option: %s"
-msgstr "E113: 未知的选项: %s"
+msgid "E474: Duplicate colon: %.*s"
+msgstr "E474: 重复的冒号:%.*s"
+
+#: ../eval/decode.c:775
+#, fuzzy, c-format
+msgid "E474: Expected null: %.*s"
+msgstr "E415: 不该有的等号: %s"
+
+#: ../eval/decode.c:787
+#, fuzzy, c-format
+msgid "E474: Expected true: %.*s"
+msgstr "E415: 不该有的等号: %s"
+
+#: ../eval/decode.c:799
+#, fuzzy, c-format
+msgid "E474: Expected false: %.*s"
+msgstr "E415: 不该有的等号: %s"
+
+#: ../eval/decode.c:879
+#, fuzzy, c-format
+msgid "E474: Unidentified byte: %.*s"
+msgstr "E121: 未定义的变量: %s"
+
+#: ../eval/decode.c:898
+#, fuzzy, c-format
+msgid "E474: Trailing characters: %.*s"
+msgstr "E488: 多余的尾部字符"
-#: ../eval.c:4904
+#: ../eval/decode.c:906
#, c-format
-msgid "E114: Missing quote: %s"
-msgstr "E114: 缺少引号: %s"
+msgid "E474: Unexpected end of input: %.*s"
+msgstr "E474: 输入意外结束:%.*s"
-#: ../eval.c:5020
+#: ../eval/encode.c:123
#, c-format
-msgid "E115: Missing quote: %s"
-msgstr "E115: 缺少引号: %s"
+msgid "key %s"
+msgstr ""
-#: ../eval.c:5084
+#: ../eval/encode.c:124
#, c-format
-msgid "E696: Missing comma in List: %s"
-msgstr "E696: List 中缺少逗号: %s"
+msgid "key %s at index %i from special map"
+msgstr ""
-#: ../eval.c:5091
+#: ../eval/encode.c:125
#, c-format
-msgid "E697: Missing end of List ']': %s"
-msgstr "E697: List 缺少结束符 ']': %s"
+msgid "index %i"
+msgstr "索引 %i"
-#: ../eval.c:6475
+#: ../eval/encode.c:126
+msgid "partial"
+msgstr "部分"
+
+#: ../eval/encode.c:127
#, c-format
-msgid "E720: Missing colon in Dictionary: %s"
-msgstr "E720: Dictionary 中缺少冒号: %s"
+msgid "argument %i"
+msgstr "参数 %i"
-#: ../eval.c:6499
+#: ../eval/encode.c:128
+msgid "partial self dictionary"
+msgstr ""
+
+#: ../eval/encode.c:203
+msgid "itself"
+msgstr ""
+
+#. Only give this message once for a recursive call to avoid
+#. flooding the user with errors.
+#: ../eval/encode.c:453 ../eval/encode.c:533
+msgid "E724: unable to correctly dump variable with self-referencing container"
+msgstr ""
+
+#: ../eval/encode.c:563
+msgid "E474: Unable to represent NaN value in JSON"
+msgstr "E474: 在 JSON 中无法表示 NaN 值"
+
+#: ../eval/encode.c:567
+msgid "E474: Unable to represent infinity in JSON"
+msgstr "E474: 在 JSON 中无法表示无穷大"
+
+#: ../eval/encode.c:635
#, c-format
-msgid "E721: Duplicate key in Dictionary: \"%s\""
-msgstr "E721: Dictionary 中出现重复的键: \"%s\""
+msgid ""
+"E474: String \"%.*s\" contains byte that does not start any UTF-8 character"
+msgstr "E474: 字符串 \"%.*s\" 包含不起始任何 UTF-8 字符的字节"
-#: ../eval.c:6517
+#: ../eval/encode.c:642
#, c-format
-msgid "E722: Missing comma in Dictionary: %s"
-msgstr "E722: Dictionary 中缺少逗号: %s"
+msgid ""
+"E474: UTF-8 string contains code point which belongs to a surrogate pair: "
+"%.*s"
+msgstr "E474: UTF-8 字符串包含属于代理对的码点:%.*s"
-#: ../eval.c:6524
+#: ../eval/encode.c:724
+msgid "E474: Unable to convert EXT string to JSON"
+msgstr "E474: 无法将 EXT 字符串转换为 JSON"
+
+#: ../eval/encode.c:752
#, c-format
-msgid "E723: Missing end of Dictionary '}': %s"
-msgstr "E723: Dictionary 缺少结束符 '}': %s"
+msgid "E474: Error while dumping %s, %s: attempt to dump function reference"
+msgstr ""
-#: ../eval.c:6555
-msgid "E724: variable nested too deep for displaying"
-msgstr "E724: 变量嵌套过深无法显示"
+#: ../eval/encode.c:797
+#, fuzzy
+msgid "E474: Invalid key in special dictionary"
+msgstr "E736: 对 Dictionary 无效的操作"
-#: ../eval.c:7188
-#, fuzzy, c-format
-msgid "E740: Too many arguments for function %s"
-msgstr "E118: 函数的参数过多: %s"
+#: ../eval/encode.c:853
+msgid "encode_tv2string() argument"
+msgstr "encode_tv2string() 参数"
-#: ../eval.c:7190
-#, fuzzy, c-format
-msgid "E116: Invalid arguments for function %s"
-msgstr "E118: 函数的参数过多: %s"
+#: ../eval/encode.c:881
+msgid ":echo argument"
+msgstr ":echo 参数"
-#: ../eval.c:7377
-#, fuzzy, c-format
-msgid "E117: Unknown function: %s"
-msgstr "E130: 未知的函数: %s"
+#: ../eval/encode.c:905
+msgid "encode_tv2json() argument"
+msgstr "encode_tv2json() 参数"
-#: ../eval.c:7383
+#: ../eval/encode.c:966
#, c-format
-msgid "E119: Not enough arguments for function: %s"
-msgstr "E119: 函数 %s 的参数太少"
+msgid "E5004: Error while dumping %s, %s: attempt to dump function reference"
+msgstr "E5004: dump %s, %s 时出错:试图 dump 函数引用"
-#: ../eval.c:7387
+#: ../eval/encode.c:1018
#, c-format
-msgid "E120: Using <SID> not in a script context: %s"
-msgstr "E120: <SID> 不能在 script 上下文外使用: %s"
+msgid "E5005: Unable to dump %s: container references itself in %s"
+msgstr "E5005: 无法 dump %s:容器在 %s 中引用了自己"
-#: ../eval.c:7391
-#, fuzzy, c-format
-msgid "E725: Calling dict function without Dictionary: %s"
-msgstr "E720: Dictionary 中缺少冒号: %s"
+#: ../eval/executor.c:23
+#, c-format
+msgid "E684: list index out of range: %<PRId64>"
+msgstr "E684: List 索引超出范围: %<PRId64>"
-#: ../eval.c:7453
-#, fuzzy
-msgid "E808: Number or Float required"
-msgstr "E521: = 后面需要数字"
+#: ../eval/funcs.c:147
+#, c-format
+msgid "E899: Argument of %s must be a List or Blob"
+msgstr "E899: %s 的参数必须是列表或 Blob"
-#: ../eval.c:7503
-#, fuzzy
+#: ../eval/funcs.c:148 ../match.c:45
+msgid "E957: Invalid window number"
+msgstr "E957: 无效的窗口号"
+
+#: ../eval/funcs.c:149
+#, c-format
+msgid "E998: Reduce of an empty %s with no initial value"
+msgstr "E998: 在没有初始值的情况下 reduce 空的 %s"
+
+#: ../eval/funcs.c:151
+#, c-format
+msgid "E1023: Using a Number as a Bool: %d"
+msgstr "E1023: 将整数作布尔值使用:%d"
+
+#: ../eval/funcs.c:153
+msgid "E1308: Cannot resize a window in another tab page"
+msgstr "E1308: 不能修改其他标签页中窗口的大小"
+
+#: ../eval/funcs.c:328 ../eval/funcs.c:7006
+#, c-format
+msgid "Error converting the call result: %s"
+msgstr "转换调用结果时出错:%s"
+
+#: ../eval/funcs.c:366 ../eval/funcs.c:374
msgid "add() argument"
-msgstr "-c 参数"
+msgstr "add() 参数"
-#: ../eval.c:7907
-msgid "E699: Too many arguments"
-msgstr "E699: 参数过多"
+#: ../eval/funcs.c:679 ../sign.c:1451
+#, c-format
+msgid "E158: Invalid buffer name: %s"
+msgstr "E158: 缓冲区名无效:%s"
-#: ../eval.c:8073
-msgid "E785: complete() can only be used in Insert mode"
-msgstr "E785: complete() 只能在插入模式中使用"
+#: ../eval/funcs.c:814
+#, c-format
+msgid "Invalid channel stream \"%s\""
+msgstr "channel stream \"%s\" 无效"
-#: ../eval.c:8156
+#: ../eval/funcs.c:1109
msgid "&Ok"
msgstr "确定(&O)"
-#: ../eval.c:8676
-#, c-format
-msgid "E737: Key already exists: %s"
-msgstr "E737: 键已存在: %s"
+#: ../eval/funcs.c:1239
+msgid "Context stack is empty"
+msgstr "上下文堆栈为空"
-#: ../eval.c:8692
-#, fuzzy
-msgid "extend() argument"
-msgstr "--cmd 参数"
+#: ../eval/funcs.c:1483
+msgid "dictwatcheradd() argument"
+msgstr "dictwatcheradd() 参数"
-#: ../eval.c:8915
-#, fuzzy
-msgid "map() argument"
-msgstr "-c 参数"
+#: ../eval/funcs.c:2178
+msgid "E900: maxdepth must be non-negative number"
+msgstr "E900: maxdepth 必须是非负整数"
-#: ../eval.c:8916
-#, fuzzy
-msgid "filter() argument"
-msgstr "-c 参数"
+#: ../eval/funcs.c:2186
+msgid "flatten() argument"
+msgstr "flatten() 参数"
-#: ../eval.c:9229
-#, c-format
-msgid "+-%s%3ld line: "
-msgid_plural "+-%s%3ld lines: "
-msgstr[0] "+-%s%3ld 行: "
+#: ../eval/funcs.c:2197 ../eval/typval.c:2458
+msgid "extend() argument"
+msgstr "extend() 参数"
-#: ../eval.c:9291
-#, c-format
-msgid "E700: Unknown function: %s"
-msgstr "E700: 未知的函数: %s"
+#: ../eval/funcs.c:2877 ../eval/funcs.c:3891
+msgid "E5000: Cannot find tab number."
+msgstr "E5000: 找不到标签页号。"
+
+#: ../eval/funcs.c:2885 ../eval/funcs.c:3899
+msgid "E5001: Higher scope cannot be -1 if lower scope is >= 0."
+msgstr "E5001: 低边界 >= 0 时高边界不能为 -1。"
-#: ../eval.c:10729
+#: ../eval/funcs.c:2892 ../eval/funcs.c:3906
+msgid "E5002: Cannot find window number."
+msgstr "E5002: 找不到窗口号。"
+
+#: ../eval/funcs.c:4120
msgid "called inputrestore() more often than inputsave()"
msgstr "inputrestore() 的调用次数多于 inputsave()"
-#: ../eval.c:10771
-#, fuzzy
+#: ../eval/funcs.c:4153 ../eval/funcs.c:4190
msgid "insert() argument"
-msgstr "-c 参数"
+msgstr "insert() 参数"
-#: ../eval.c:10841
+#: ../eval/funcs.c:4260
msgid "E786: Range not allowed"
msgstr "E786: 不允许的范围"
-#: ../eval.c:11140
+#: ../eval/funcs.c:4743
+msgid "E474: Failed to convert list to string"
+msgstr "E474: 无法将列表转换为字符串"
+
+#: ../eval/funcs.c:4760
+#, c-format
+msgid "E474: Failed to parse %.*s"
+msgstr "E474: 无法解析 %.*s"
+
+#: ../eval/funcs.c:4826
msgid "E701: Invalid type for len()"
msgstr "E701: len() 的类型无效"
-#: ../eval.c:11980
+#: ../eval/funcs.c:5348
+#, c-format
+msgid "msgpackdump() argument, index %i"
+msgstr "msgpackdump() 参数,索引 %i"
+
+#: ../eval/funcs.c:5517
+msgid "E5070: Character number must not be less than zero"
+msgstr "E5070: 字符数不能小于零"
+
+#: ../eval/funcs.c:5521
+#, c-format
+msgid "E5071: Character number must not be greater than INT_MAX (%i)"
+msgstr "E5071: 字符数不能大于 INT_MAX (%i)"
+
+#: ../eval/funcs.c:5907
msgid "E726: Stride is zero"
msgstr "E726: 步长为零"
-#: ../eval.c:11982
+#: ../eval/funcs.c:5909
msgid "E727: Start past end"
msgstr "E727: 起始值在终止值后"
-#: ../eval.c:12024 ../eval.c:15297
+#: ../eval/funcs.c:6006
msgid "<empty>"
msgstr "<空>"
-#: ../eval.c:12282
-#, fuzzy
+#: ../eval/funcs.c:6329
msgid "remove() argument"
-msgstr "--cmd 参数"
+msgstr "remove() 参数"
-#: ../eval.c:12466
+#: ../eval/funcs.c:6447
msgid "E655: Too many symbolic links (cycle?)"
msgstr "E655: 符号连接过多(循环?)"
-#: ../eval.c:12593
-#, fuzzy
+#: ../eval/funcs.c:6577
msgid "reverse() argument"
-msgstr "-c 参数"
+msgstr "reverse() 参数"
-#: ../eval.c:13721
-#, fuzzy
-msgid "sort() argument"
-msgstr "-c 参数"
+#: ../eval/funcs.c:7040
+#, c-format
+msgid "E5010: List item %d of the second argument is not a string"
+msgstr "E5010: 第二个参数的列表项 %d 不是字符串"
-#: ../eval.c:13721
-#, fuzzy
-msgid "uniq() argument"
-msgstr "-c 参数"
+#: ../eval/funcs.c:7916
+#, c-format
+msgid "E962: Invalid action: '%s'"
+msgstr "E962: 动作无效:'%s'"
-#: ../eval.c:13776
-msgid "E702: Sort compare function failed"
-msgstr "E702: Sort 比较函数失败"
+#: ../eval/funcs.c:8056
+#, c-format
+msgid "connection failed: %s"
+msgstr "连接失败:%s"
-#: ../eval.c:13806
-#, fuzzy
-msgid "E882: Uniq compare function failed"
-msgstr "E702: Sort 比较函数失败"
+#: ../eval/funcs.c:8328
+#, fuzzy, c-format
+msgid "E6100: \"%s\" is not a valid stdpath"
+msgstr "E236: 字体 \"%s\" 不是等宽字体"
-#: ../eval.c:14085
+#: ../eval/funcs.c:8420 ../os/time.c:189
msgid "(Invalid)"
msgstr "(无效)"
-#: ../eval.c:14590
-msgid "E677: Error writing temp file"
-msgstr "E677: 写临时文件出错"
+#: ../eval/funcs.c:8774
+#, c-format
+msgid "E935: invalid submatch number: %d"
+msgstr "E935: 子匹配号无效:%d"
-#: ../eval.c:16159
-#, fuzzy
-msgid "E805: Using a Float as a Number"
-msgstr "E745: 将 List 作数字使用"
+#: ../eval/funcs.c:9213
+msgid "Can only call this function in an unmodified buffer"
+msgstr "只能在未修改的缓冲区中调用这个函数"
-#: ../eval.c:16162
-msgid "E703: Using a Funcref as a Number"
-msgstr "E703: 将 Funcref 作数字使用"
+#: ../eval/funcs.c:10026
+msgid "writefile() first argument must be a List or a Blob"
+msgstr "writefile() 的第一个参数必须是列表或者 blob"
-#: ../eval.c:16170
-msgid "E745: Using a List as a Number"
-msgstr "E745: 将 List 作数字使用"
+#. Using %s, p and not %c, *p to preserve multibyte characters
+#: ../eval/funcs.c:10053
+#, c-format
+msgid "E5060: Unknown flag: %s"
+msgstr "E5060: 未知的标志:%s"
-#: ../eval.c:16173
-msgid "E728: Using a Dictionary as a Number"
-msgstr "E728: 将 Dictionary 作数字使用"
+#: ../eval/funcs.c:10067
+msgid "E482: Can't open file with an empty name"
+msgstr "E482: 无法打开名称为空的文件"
-#: ../eval.c:16259
-msgid "E729: using Funcref as a String"
-msgstr "E729: 将 Funcref 作 String 使用"
+#: ../eval/funcs.c:10072
+#, c-format
+msgid "E482: Can't open file %s for writing: %s"
+msgstr ""
-#: ../eval.c:16262
-msgid "E730: using List as a String"
-msgstr "E730: 将 List 作 String 使用"
+#: ../eval/funcs.c:10085
+#, c-format
+msgid "E80: Error when closing file %s: %s"
+msgstr "E80: 关闭文件 %s 时出错: %s"
-#: ../eval.c:16265
-msgid "E731: using Dictionary as a String"
-msgstr "E731: 将 Dictionary 作 String 使用"
+#: ../eval/typval.c:44
+#, c-format
+msgid "E1174: String required for argument %d"
+msgstr "E1174: 参数 %d 需要字符串"
-#: ../eval.c:16619
+#: ../eval/typval.c:46
#, c-format
-msgid "E706: Variable type mismatch for: %s"
-msgstr "E706: 变量类型不匹配: %s"
+msgid "E1175: Non-empty string required for argument %d"
+msgstr "E1175: 参数 %d 需要非空字符串"
-#: ../eval.c:16705
-#, fuzzy, c-format
-msgid "E795: Cannot delete variable %s"
-msgstr "E738: 无法列出 %s 的变量"
+#: ../eval/typval.c:48
+#, c-format
+msgid "E1210: Number required for argument %d"
+msgstr "E1210: 参数 %d 需要整数"
-#: ../eval.c:16724
+#: ../eval/typval.c:50
#, c-format
-msgid "E704: Funcref variable name must start with a capital: %s"
-msgstr "E704: Funcref 变量名必须以大写字母开头: %s"
+msgid "E1222: String or List required for argument %d"
+msgstr "E1222: 参数 %d 需要字符串或列表"
-#: ../eval.c:16732
+#: ../eval/typval.c:76
#, c-format
-msgid "E705: Variable name conflicts with existing function: %s"
-msgstr "E705: 变量名与已有函数名冲突: %s"
+msgid "E5142: Failed to open file %s: %s"
+msgstr "E5142: 无法打开文件 %s:%s"
-#: ../eval.c:16763
+#: ../eval/typval.c:98
#, c-format
-msgid "E741: Value is locked: %s"
-msgstr "E741: 值已锁定: %s"
+msgid "E5143: Failed to write to file %s: %s"
+msgstr "E5143: 无法写入文件 %s:%s"
-#: ../eval.c:16764 ../eval.c:16769 ../message.c:1839
-msgid "Unknown"
-msgstr "未知"
+#: ../eval/typval.c:109
+#, c-format
+msgid "E5144: Failed to close file %s: %s"
+msgstr "E5144: 无法关闭文件 %s:%s"
+
+#: ../eval/typval.c:1150
+msgid "sort() argument"
+msgstr "sort() 参数"
+
+#: ../eval/typval.c:1151
+msgid "uniq() argument"
+msgstr "uniq() 参数"
+
+#: ../eval/typval.c:1242
+msgid "E702: Sort compare function failed"
+msgstr "E702: Sort 比较函数失败"
+
+#: ../eval/typval.c:1265
+msgid "E882: Uniq compare function failed"
+msgstr "E882: Uniq 比较函数失败"
-#: ../eval.c:16768
+#: ../eval/typval.c:2198
+msgid "E6000: Argument is not a function or function name"
+msgstr ""
+
+#: ../eval/typval.c:2476
#, c-format
-msgid "E742: Cannot change value of %s"
-msgstr "E742: 无法改变 %s 的值"
+msgid "E737: Key already exists: %s"
+msgstr "E737: 键已存在:%s"
-#: ../eval.c:16838
-msgid "E698: variable nested too deep for making a copy"
-msgstr "E698: 变量嵌套过深无法复制"
+#: ../eval/typval.c:3315
+msgid "E743: variable nested too deep for (un)lock"
+msgstr "E743: 变量嵌套过深,无法锁定/解锁"
-#: ../eval.c:17249
+#: ../eval/typval.c:3455
#, c-format
-msgid "E123: Undefined function: %s"
-msgstr "E123: 函数 %s 尚未定义"
+msgid "E741: Value is locked: %.*s"
+msgstr "E741: 值已锁定:%.*s"
-#: ../eval.c:17260
+#: ../eval/typval.c:3458
#, c-format
-msgid "E124: Missing '(': %s"
-msgstr "E124: 缺少 '(': %s"
+msgid "E742: Cannot change value of %.*s"
+msgstr "E742: 无法改变 %.*s 的值"
-#: ../eval.c:17293
-#, fuzzy
-msgid "E862: Cannot use g: here"
-msgstr "E284: 不能设定 IC 值"
+#: ../eval/typval.c:3464 ../message.c:2469
+msgid "Unknown"
+msgstr "未知"
+
+#: ../eval/typval.c:3591
+msgid "E805: Expected a Number or a String, Float found"
+msgstr "E805: 需要整数或字符串,但是提供了浮点数"
+
+#: ../eval/typval.c:3595
+msgid "E703: Expected a Number or a String, Funcref found"
+msgstr "E703: 需要整数或字符串,但是提供了 Funcref"
+
+#: ../eval/typval.c:3598
+msgid "E745: Expected a Number or a String, List found"
+msgstr "E745: 需要整数或字符串,但是提供了列表"
+
+#: ../eval/typval.c:3601
+msgid "E728: Expected a Number or a String, Dictionary found"
+msgstr "E728: 需要整数或字符串,但是提供了字典"
+
+#: ../eval/typval.c:3604
+msgid "E974: Expected a Number or a String, Blob found"
+msgstr "E974: 需要整数或字符串,但是提供了 Blob"
+
+#: ../eval/typval.c:3607
+msgid "E5299: Expected a Number or a String, Boolean found"
+msgstr "E5299: 需要整数或字符串,但是提供了布尔值"
+
+#: ../eval/typval.c:3610
+msgid "E5300: Expected a Number or a String"
+msgstr "E5300: 需要整数或字符串"
+
+#: ../eval/typval.c:3625
+msgid "E745: Using a List as a Number"
+msgstr "E745: 将列表当作整数使用"
+
+#: ../eval/typval.c:3626
+msgid "E728: Using a Dictionary as a Number"
+msgstr "E728: 将字典当作整数使用"
+
+#: ../eval/typval.c:3627
+msgid "E805: Using a Float as a Number"
+msgstr "E805: 将浮点数当作整数使用"
+
+#: ../eval/typval.c:3628
+msgid "E974: Using a Blob as a Number"
+msgstr "E974: 将 Blob 当作整数使用"
+
+#: ../eval/typval.c:3629
+msgid "E685: using an invalid value as a Number"
+msgstr "E685: 将无效值当作整数使用"
+
+#: ../eval/typval.c:3670
+msgid "E730: using List as a String"
+msgstr "E730: 将列表当作字符串使用"
+
+#: ../eval/typval.c:3671
+msgid "E731: using Dictionary as a String"
+msgstr "E731: 将字典当作字符串使用"
+
+#: ../eval/typval.c:3673
+msgid "E976: using Blob as a String"
+msgstr "E976: 将 Blob 当作字符串使用"
+
+#: ../eval/typval.c:3674
+msgid "E908: using an invalid value as a String"
+msgstr "E908: 将无效值当作字符串使用"
+
+#: ../eval/typval.c:3815
+msgid "E891: Using a Funcref as a Float"
+msgstr "E891: 将 Funcref 当作浮点数使用"
+
+#: ../eval/typval.c:3818
+msgid "E892: Using a String as a Float"
+msgstr "E892: 将字符串当作浮点数使用"
+
+#: ../eval/typval.c:3821
+msgid "E893: Using a List as a Float"
+msgstr "E893: 将列表当作浮点数使用"
+
+#: ../eval/typval.c:3824
+msgid "E894: Using a Dictionary as a Float"
+msgstr "E894: 将字典当作浮点数使用"
+
+#: ../eval/typval.c:3827
+msgid "E362: Using a boolean value as a Float"
+msgstr "E362: 将布尔值当作浮点数使用"
+
+#: ../eval/typval.c:3830
+msgid "E907: Using a special value as a Float"
+msgstr "E907: 将特殊值当作浮点数使用"
+
+#: ../eval/typval.c:3833
+msgid "E975: Using a Blob as a Float"
+msgstr "E975: 将 Blob 当作浮点数使用"
+
+#: ../eval/typval.h:515
+msgid "E808: Number or Float required"
+msgstr "E808: 需要整数或浮点数"
-#: ../eval.c:17312
+#: ../eval/userfunc.c:67
#, c-format
-msgid "E125: Illegal argument: %s"
-msgstr "E125: 无效的参数: %s"
+msgid "E122: Function %s already exists, add ! to replace it"
+msgstr "E122: 函数 %s 已存在,请加 ! 强制替换"
-#: ../eval.c:17323
-#, fuzzy, c-format
-msgid "E853: Duplicate argument name: %s"
-msgstr "E125: 无效的参数: %s"
+#: ../eval/userfunc.c:68
+msgid "E717: Dictionary entry already exists"
+msgstr "E717: 字典项已存在"
-#: ../eval.c:17416
-msgid "E126: Missing :endfunction"
-msgstr "E126: 缺少 :endfunction"
+#: ../eval/userfunc.c:69
+msgid "E718: Funcref required"
+msgstr "E718: 需要 Funcref"
-#: ../eval.c:17537
-#, fuzzy, c-format
-msgid "E707: Function name conflicts with variable: %s"
-msgstr "E746: 函数名与脚本文件名不匹配: %s"
+#: ../eval/userfunc.c:70
+#, c-format
+msgid "E130: Unknown function: %s"
+msgstr "E130: 函数未知: %s"
-#: ../eval.c:17549
+#: ../eval/userfunc.c:72
#, c-format
-msgid "E127: Cannot redefine function %s: It is in use"
-msgstr "E127: 函数 %s 正在使用中,不能重新定义"
+msgid "E1068: No white space allowed before '%s': %s"
+msgstr "E1068: '%s' 前不允许有空白:%s"
-#: ../eval.c:17604
+#: ../eval/userfunc.c:124
#, c-format
-msgid "E746: Function name does not match script file name: %s"
-msgstr "E746: 函数名与脚本文件名不匹配: %s"
+msgid "E125: Illegal argument: %s"
+msgstr "E125: 参数无效: %s"
-#: ../eval.c:17716
-msgid "E129: Function name required"
-msgstr "E129: 需要函数名"
+#: ../eval/userfunc.c:137
+#, c-format
+msgid "E853: Duplicate argument name: %s"
+msgstr "E853: 参数名重复:%s"
-#: ../eval.c:17824
-#, fuzzy, c-format
-msgid "E128: Function name must start with a capital or \"s:\": %s"
-msgstr "E128: 函数名必须以大写字母开头或者包含冒号: %s"
+#: ../eval/userfunc.c:171
+msgid "E989: Non-default argument follows default argument"
+msgstr "E989: 默认参数后有非默认参数"
-#: ../eval.c:17833
-#, fuzzy, c-format
-msgid "E884: Function name cannot contain a colon: %s"
-msgstr "E128: 函数名必须以大写字母开头或者包含冒号: %s"
+#: ../eval/userfunc.c:498
+#, c-format
+msgid "E740: Too many arguments for function %s"
+msgstr "E740: 函数 %s 的参数过多"
-#: ../eval.c:18336
+#: ../eval/userfunc.c:500
#, c-format
-msgid "E131: Cannot delete function %s: It is in use"
-msgstr "E131: 无法删除函数 %s: 正在使用中"
+msgid "E116: Invalid arguments for function %s"
+msgstr "E116: 函数 %s 的参数无效"
-#: ../eval.c:18441
+#: ../eval/userfunc.c:859
msgid "E132: Function call depth is higher than 'maxfuncdepth'"
msgstr "E132: 函数调用深度超出 'maxfuncdepth'"
-#: ../eval.c:18568
+#: ../eval/userfunc.c:1039
#, c-format
msgid "calling %s"
msgstr "调用 %s"
-#: ../eval.c:18651
+#: ../eval/userfunc.c:1154
#, c-format
msgid "%s aborted"
msgstr "%s 已中止"
-#: ../eval.c:18653
+#: ../eval/userfunc.c:1156
#, c-format
msgid "%s returning #%<PRId64>"
msgstr "%s 返回 #%<PRId64> "
-#: ../eval.c:18670
+#: ../eval/userfunc.c:1173
#, c-format
msgid "%s returning %s"
msgstr "%s 返回 %s"
-#: ../eval.c:18691 ../ex_cmds2.c:2695
+#: ../eval/userfunc.c:1196 ../runtime.c:2083
#, c-format
msgid "continuing in %s"
msgstr "在 %s 中继续"
-#: ../eval.c:18795
-msgid "E133: :return not inside a function"
-msgstr "E133: :return 不在函数中"
+#: ../eval/userfunc.c:1391
+msgid "E699: Too many arguments"
+msgstr "E699: 参数过多"
-#: ../eval.c:19159
-msgid ""
-"\n"
-"# global variables:\n"
-msgstr ""
-"\n"
-"# 全局变量:\n"
+#: ../eval/userfunc.c:1441
+#, c-format
+msgid "E117: Unknown function: %s"
+msgstr "E117: 未知的函数:%s"
-#: ../eval.c:19254
-msgid ""
-"\n"
-"\tLast set from "
-msgstr ""
-"\n"
-"\t最近修改于 "
+#: ../eval/userfunc.c:1444
+#, c-format
+msgid "E276: Cannot use function as a method: %s"
+msgstr "E276: 不能将函数用作方法:%s"
-#: ../eval.c:19272
-#, fuzzy
-msgid "No old files"
-msgstr "没有包含文件"
+#: ../eval/userfunc.c:1447
+#, c-format
+msgid "E933: Function was deleted: %s"
+msgstr "E933: 函数已被删除:%s"
-#: ../ex_cmds.c:122
+#: ../eval/userfunc.c:1453
#, c-format
-msgid "<%s>%s%s %d, Hex %02x, Octal %03o"
-msgstr "<%s>%s%s %d, 十六进制 %02x, 八进制 %03o"
+msgid "E119: Not enough arguments for function: %s"
+msgstr "E119: 函数 %s 的参数不足"
-#: ../ex_cmds.c:145
+#: ../eval/userfunc.c:1457
#, c-format
-msgid "> %d, Hex %04x, Octal %o"
-msgstr "> %d, 十六进制 %04x, 八进制 %o"
+msgid "E120: Using <SID> not in a script context: %s"
+msgstr "E120: <SID> 不能在 script 上下文外使用:%s"
-#: ../ex_cmds.c:146
+#: ../eval/userfunc.c:1461
#, c-format
-msgid "> %d, Hex %08x, Octal %o"
-msgstr "> %d, 十六进制 %08x, 八进制 %o"
+msgid "E725: Calling dict function without Dictionary: %s"
+msgstr "E725: 调用字典函数但是没有字典:%s"
-#: ../ex_cmds.c:684
-msgid "E134: Move lines into themselves"
-msgstr "E134: 把行移动到自已中"
+#: ../eval/userfunc.c:1759
+msgid "E129: Function name required"
+msgstr "E129: 需要函数名"
-#: ../ex_cmds.c:747
-msgid "1 line moved"
-msgstr "移动了 1 行"
+#: ../eval/userfunc.c:1895
+#, c-format
+msgid "E128: Function name must start with a capital or \"s:\": %s"
+msgstr "E128: 函数名必须以大写字母或 \"s:\" 开头:%s"
-#: ../ex_cmds.c:749
+#: ../eval/userfunc.c:1904
#, c-format
-msgid "%<PRId64> lines moved"
-msgstr "移动了 %<PRId64> 行"
+msgid "E884: Function name cannot contain a colon: %s"
+msgstr "E884: 函数名不能包含冒号:%s"
+
+#: ../eval/userfunc.c:1972
+msgid "E454: function list was modified"
+msgstr "E454: 函数列表已被修改"
-#: ../ex_cmds.c:1175
+#: ../eval/userfunc.c:2125
#, c-format
-msgid "%<PRId64> lines filtered"
-msgstr "过滤了 %<PRId64> 行"
+msgid "E123: Undefined function: %s"
+msgstr "E123: 函数未定义:%s"
-#: ../ex_cmds.c:1194
-msgid "E135: *Filter* Autocommands must not change current buffer"
-msgstr "E135: *Filter* 自动命令不可以改变当前缓冲区"
+#: ../eval/userfunc.c:2135
+#, c-format
+msgid "E124: Missing '(': %s"
+msgstr "E124: 缺少 '(':%s"
-#: ../ex_cmds.c:1244
-msgid "[No write since last change]\n"
-msgstr "[已修改但尚未保存]\n"
+#: ../eval/userfunc.c:2167
+msgid "E862: Cannot use g: here"
+msgstr "E862: 此处不能使用 g:"
-# bad to translate
-#: ../ex_cmds.c:1424
+#: ../eval/userfunc.c:2197
#, c-format
-msgid "%sviminfo: %s in line: "
-msgstr "%sviminfo: %s 位于行: "
+msgid "E932: Closure function should not be at top level: %s"
+msgstr "E932: 闭包函数不能位于顶层:%s"
-#: ../ex_cmds.c:1431
-msgid "E136: viminfo: Too many errors, skipping rest of file"
-msgstr "E136: viminfo: 错误过多,忽略文件的剩余部分"
+#: ../eval/userfunc.c:2272
+msgid "E126: Missing :endfunction"
+msgstr "E126: 缺少 :endfunction"
-#: ../ex_cmds.c:1458
+#: ../eval/userfunc.c:2327
#, c-format
-msgid "Reading viminfo file \"%s\"%s%s%s"
-msgstr "读取 viminfo 文件 \"%s\"%s%s%s"
+msgid "W22: Text found after :endfunction: %s"
+msgstr "W22: :endfunction 后有文本:%s"
-#: ../ex_cmds.c:1460
-msgid " info"
-msgstr " 信息"
+#: ../eval/userfunc.c:2363
+msgid "E1058: function nesting too deep"
+msgstr "E1058: 函数嵌套层数过深"
-#: ../ex_cmds.c:1461
-msgid " marks"
-msgstr " 标记"
+#: ../eval/userfunc.c:2472
+#, c-format
+msgid "E707: Function name conflicts with variable: %s"
+msgstr "E707: 函数名与变量名冲突:%s"
-#: ../ex_cmds.c:1462
-#, fuzzy
-msgid " oldfiles"
-msgstr "没有包含文件"
+#: ../eval/userfunc.c:2488
+#, c-format
+msgid "E127: Cannot redefine function %s: It is in use"
+msgstr "E127: 函数 %s 正在使用中,不能重新定义"
-#: ../ex_cmds.c:1463
-msgid " FAILED"
-msgstr " 失败"
+#: ../eval/userfunc.c:2555
+#, c-format
+msgid "E746: Function name does not match script file name: %s"
+msgstr "E746: 函数名与脚本文件名不匹配: %s"
-#. avoid a wait_return for this message, it's annoying
-#: ../ex_cmds.c:1541
+#: ../eval/userfunc.c:2778
#, c-format
-msgid "E137: Viminfo file is not writable: %s"
-msgstr "E137: Viminfo 文件不可写入: %s"
+msgid "E131: Cannot delete function %s: It is in use"
+msgstr "E131: 函数 %s 正在使用中,不能删除"
-#: ../ex_cmds.c:1626
+#: ../eval/userfunc.c:2784
#, c-format
-msgid "E138: Can't write viminfo file %s!"
-msgstr "E138: 无法写入 viminfo 文件 %s!"
+msgid "Cannot delete function %s: It is being used internally"
+msgstr "无法删除函数 %s,内部使用这个函数"
+
+#: ../eval/userfunc.c:2916
+msgid "E133: :return not inside a function"
+msgstr "E133: :return 在函数外"
-#: ../ex_cmds.c:1635
+#. TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead
+#. maximum nesting of lists and dicts
+#: ../eval/vars.c:52
+msgid "E18: Unexpected characters in :let"
+msgstr "E18: :let 中出现异常字符"
+
+#: ../eval/vars.c:53
#, c-format
-msgid "Writing viminfo file \"%s\""
-msgstr "写入 viminfo 文件 \"%s\""
+msgid "E940: Cannot lock or unlock variable %s"
+msgstr "E940: 不能锁定或解锁变量 %s"
-# do not translate to avoid writing Chinese in files
-#. Write the info:
-#: ../ex_cmds.c:1720
-#, fuzzy, c-format
-msgid "# This viminfo file was generated by Vim %s.\n"
-msgstr "# 这个 viminfo 文件是由 Vim %s 生成的。\n"
+#: ../eval/vars.c:77
+msgid "E991: cannot use =<< here"
+msgstr "E991: 此处不能使用 =<<"
-# do not translate to avoid writing Chinese in files
-#: ../ex_cmds.c:1722
-#, fuzzy
-msgid ""
-"# You may edit it if you're careful!\n"
-"\n"
-msgstr ""
-"# 如果要自行修改请特别小心!\n"
-"\n"
+#: ../eval/vars.c:109
+msgid "E221: Marker cannot start with lower case letter"
+msgstr "E221: 标记不能以小写字母开头"
-# do not translate to avoid writing Chinese in files
-#: ../ex_cmds.c:1723
-#, fuzzy
-msgid "# Value of 'encoding' when this file was written\n"
-msgstr "# 'encoding' 在此文件建立时的值\n"
+#: ../eval/vars.c:113
+msgid "E172: Missing marker"
+msgstr "E172: 缺少标记"
+
+#: ../eval/vars.c:124
+#, c-format
+msgid "E990: Missing end marker '%s'"
+msgstr "E990: 缺少结束标记 '%s'"
+
+#: ../eval/vars.c:311
+msgid "E687: Less targets than List items"
+msgstr "E687: 目标比 List 项数少"
+
+#: ../eval/vars.c:315
+msgid "E688: More targets than List items"
+msgstr "E688: 目标比 List 项数多"
+
+#: ../eval/vars.c:391
+msgid "E452: Double ; in list of variables"
+msgstr "E452: 变量列表中有两个 ;"
+
+#: ../eval/vars.c:532
+#, c-format
+msgid "E738: Can't list variables for %s"
+msgstr "E738: 无法列出 %s 的变量"
+
+#: ../eval/vars.c:584
+msgid "E996: Cannot lock an environment variable"
+msgstr "E996: 不能锁定环境变量"
+
+#: ../eval/vars.c:625
+msgid "E996: Cannot lock an option"
+msgstr "E996: 不能锁定选项"
+
+#: ../eval/vars.c:716
+msgid "E996: Cannot lock a register"
+msgstr "E996: 不能锁定寄存器"
+
+#: ../eval/vars.c:1015
+#, c-format
+msgid "E108: No such variable: \"%s\""
+msgstr "E108: 无此变量:\"%s\""
+
+#: ../eval/vars.c:1331
+#, c-format
+msgid "E963: setting %s to value with wrong type"
+msgstr "E963: 将 %s 设置为类型错误的值"
+
+#: ../eval/vars.c:1414
+#, c-format
+msgid "E794: Cannot set variable in the sandbox: \"%.*s\""
+msgstr "E794: 不能在沙盒中设置变量:\"%.*s\""
+
+#: ../eval/vars.c:1460
+#, c-format
+msgid "E795: Cannot delete variable %.*s"
+msgstr "E795: 无法删除变量 %.*s"
+
+#: ../eval/vars.c:1483
+#, c-format
+msgid "E704: Funcref variable name must start with a capital: %s"
+msgstr "E704: Funcref 变量名必须以大写字母开头: %s"
+
+#: ../eval/vars.c:1490
+#, c-format
+msgid "E705: Variable name conflicts with existing function: %s"
+msgstr "E705: 变量名与已有函数名冲突: %s"
+
+#: ../event/socket.c:220
+msgid "tcp address must be host:port"
+msgstr "TCP 地址必须为 host:port"
+
+#: ../event/socket.c:231
+msgid "failed to lookup host or port"
+msgstr "无法查找主机或端口"
+
+#: ../event/socket.c:256
+msgid "connection refused"
+msgstr "连接被拒绝"
+
+#: ../ex_cmds.c:164
+#, c-format
+msgid "<%s>%s%s %d, Hex %02x, Oct %03o, Digr %s"
+msgstr "<%s>%s%s %d, 十六进制 %02x, 八进制 %03o, 二合字符 %s"
+
+#: ../ex_cmds.c:169
+#, c-format
+msgid "<%s>%s%s %d, Hex %02x, Octal %03o"
+msgstr "<%s>%s%s %d, 十六进制 %02x, 八进制 %03o"
+
+#: ../ex_cmds.c:213
+#, c-format
+msgid "> %d, Hex %04x, Oct %o, Digr %s"
+msgstr "> %d, 十六进制 %04x, 八进制 %o, 二合字符 %s"
+
+#: ../ex_cmds.c:214
+#, c-format
+msgid "> %d, Hex %08x, Oct %o, Digr %s"
+msgstr "> %d, 十六进制 %08x, 八进制 %o, 二合字符 %s"
+
+#: ../ex_cmds.c:220
+#, c-format
+msgid "> %d, Hex %04x, Octal %o"
+msgstr "> %d, 十六进制 %04x, 八进制 %o"
-#: ../ex_cmds.c:1800
-msgid "Illegal starting char"
-msgstr "无效的启动字符"
+#: ../ex_cmds.c:221
+#, c-format
+msgid "> %d, Hex %08x, Octal %o"
+msgstr "> %d, 十六进制 %08x, 八进制 %o"
-#: ../ex_cmds.c:2162
+#: ../ex_cmds.c:914
+msgid "E134: Cannot move a range of lines into itself"
+msgstr "E134: 不能把一系列行移动到自身当中"
+
+#: ../ex_cmds.c:1021
+#, c-format
+msgid "1 line moved"
+msgid_plural "%<PRId64> lines moved"
+msgstr[0] "移动了 %<PRId64> 行"
+
+#. will call wait_return()
+#: ../ex_cmds.c:1335
+#, c-format
+msgid "E482: Can't create file %s"
+msgstr "E482: 无法创建文件 %s"
+
+#: ../ex_cmds.c:1436
+#, c-format
+msgid "%<PRId64> lines filtered"
+msgstr "过滤了 %<PRId64> 行"
+
+#: ../ex_cmds.c:1458
+msgid "E135: *Filter* Autocommands must not change current buffer"
+msgstr "E135: *Filter* 自动命令不可以改变当前缓冲区"
+
+#: ../ex_cmds.c:1498
+msgid "[No write since last change]\n"
+msgstr "[已修改但尚未保存]\n"
+
+#: ../ex_cmds.c:1793
+#, c-format
+msgid "E503: \"%s\" is not a file or writable device"
+msgstr "E503: \"%s\" 不是文件或可写的设备"
+
+#: ../ex_cmds.c:1877
msgid "Write partial file?"
msgstr "要写入部分文件吗?"
-#: ../ex_cmds.c:2166
+#: ../ex_cmds.c:1882
msgid "E140: Use ! to write partial buffer"
msgstr "E140: 请使用 ! 来写入部分缓冲区"
-#: ../ex_cmds.c:2281
+#: ../ex_cmds.c:2006
#, c-format
msgid "Overwrite existing file \"%s\"?"
msgstr "覆盖已存在的文件 \"%s\" 吗?"
-#: ../ex_cmds.c:2317
+#: ../ex_cmds.c:2043
#, c-format
msgid "Swap file \"%s\" exists, overwrite anyway?"
msgstr "交换文件 \"%s\" 已存在,确实要覆盖吗?"
-#: ../ex_cmds.c:2326
+#: ../ex_cmds.c:2052
#, c-format
msgid "E768: Swap file exists: %s (:silent! overrides)"
msgstr "E768: 交换文件已存在: %s (:silent! 强制执行)"
-#: ../ex_cmds.c:2381
+#: ../ex_cmds.c:2110
#, c-format
msgid "E141: No file name for buffer %<PRId64>"
msgstr "E141: 缓冲区 %<PRId64> 没有文件名"
-#: ../ex_cmds.c:2412
+#: ../ex_cmds.c:2144
msgid "E142: File not written: Writing is disabled by 'write' option"
msgstr "E142: 文件未写入: 写入被 'write' 选项禁用"
-#: ../ex_cmds.c:2434
+#: ../ex_cmds.c:2163
#, c-format
msgid ""
"'readonly' option is set for \"%s\".\n"
@@ -1202,7 +1959,7 @@ msgstr ""
"\"%s\" 已设定 'readonly' 选项。\n"
"确实要覆盖吗?"
-#: ../ex_cmds.c:2439
+#: ../ex_cmds.c:2167
#, c-format
msgid ""
"File permissions of \"%s\" are read-only.\n"
@@ -1213,1069 +1970,744 @@ msgstr ""
"它仍然有可能被写入。\n"
"你想继续尝试吗?"
-#: ../ex_cmds.c:2451
-#, fuzzy, c-format
+#: ../ex_cmds.c:2181
+#, c-format
msgid "E505: \"%s\" is read-only (add ! to override)"
-msgstr "只读 (请加 ! 强制执行)"
+msgstr "E505: \"%s\" 只读 (请加 ! 强制执行)"
-#: ../ex_cmds.c:3120
+#: ../ex_cmds.c:2902
#, c-format
msgid "E143: Autocommands unexpectedly deleted new buffer %s"
msgstr "E143: 自动命令意外地删除了新缓冲区 %s"
-#: ../ex_cmds.c:3313
+#: ../ex_cmds.c:3118
msgid "E144: non-numeric argument to :z"
msgstr "E144: :z 不接受非数字的参数"
-#: ../ex_cmds.c:3498
+#: ../ex_cmds.c:3429
msgid "E146: Regular expressions can't be delimited by letters"
msgstr "E146: 正则表达式不能用字母作分界"
-#: ../ex_cmds.c:3964
+#. Same highlight as wait_return().
+#: ../ex_cmds.c:3952
#, c-format
msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
msgstr "替换为 %s (y/n/a/q/l/^E/^Y)?"
-#: ../ex_cmds.c:4379
+#: ../ex_cmds.c:4462
msgid "(Interrupted) "
msgstr "(已中断) "
-#: ../ex_cmds.c:4384
-msgid "1 match"
-msgstr "1 个匹配,"
-
-#: ../ex_cmds.c:4384
-msgid "1 substitution"
-msgstr "1 次替换,"
-
-#: ../ex_cmds.c:4387
-#, c-format
-msgid "%<PRId64> matches"
-msgstr "%<PRId64> 个匹配,"
+#: ../ex_cmds.c:4468
+#, fuzzy, c-format
+msgid "%<PRId64> match on %<PRId64> line"
+msgid_plural "%<PRId64> matches on %<PRId64> line"
+msgstr[0] "共 %<PRId64> 行"
-#: ../ex_cmds.c:4388
-#, c-format
-msgid "%<PRId64> substitutions"
-msgstr "%<PRId64> 次替换,"
+#: ../ex_cmds.c:4470
+#, fuzzy, c-format
+msgid "%<PRId64> substitution on %<PRId64> line"
+msgid_plural "%<PRId64> substitutions on %<PRId64> line"
+msgstr[0] "%<PRId64> 次替换,"
-#: ../ex_cmds.c:4392
-msgid " on 1 line"
-msgstr "共 1 行"
+#: ../ex_cmds.c:4473
+#, fuzzy, c-format
+msgid "%<PRId64> match on %<PRId64> lines"
+msgid_plural "%<PRId64> matches on %<PRId64> lines"
+msgstr[0] "共 %<PRId64> 行"
-#: ../ex_cmds.c:4395
-#, c-format
-msgid " on %<PRId64> lines"
-msgstr "共 %<PRId64> 行"
+#: ../ex_cmds.c:4475
+#, fuzzy, c-format
+msgid "%<PRId64> substitution on %<PRId64> lines"
+msgid_plural "%<PRId64> substitutions on %<PRId64> lines"
+msgstr[0] "%<PRId64> 次替换,"
-#: ../ex_cmds.c:4438
-msgid "E147: Cannot do :global recursive"
-msgstr "E147: :global 不能递归执行"
+#. will increment global_busy to break out of the loop
+#: ../ex_cmds.c:4536
+msgid "E147: Cannot do :global recursive with a range"
+msgstr "E147: 不能带范围递归执行 :global"
-#: ../ex_cmds.c:4467
+#: ../ex_cmds.c:4565
msgid "E148: Regular expression missing from global"
msgstr "E148: global 缺少正则表达式"
-#: ../ex_cmds.c:4508
+#: ../ex_cmds.c:4610
#, c-format
msgid "Pattern found in every line: %s"
msgstr "每行都匹配表达式: %s"
-#: ../ex_cmds.c:4510
-#, fuzzy, c-format
-msgid "Pattern not found: %s"
-msgstr "找不到模式"
-
-# do not translate to avoid writing Chinese in files
-#: ../ex_cmds.c:4587
-#, fuzzy
-msgid ""
-"\n"
-"# Last Substitute String:\n"
-"$"
-msgstr ""
-"\n"
-"# 最近的替换字符串:\n"
-"$"
-
-#: ../ex_cmds.c:4679
-msgid "E478: Don't panic!"
-msgstr "E478: 不要慌!"
-
-#: ../ex_cmds.c:4717
-#, c-format
-msgid "E661: Sorry, no '%s' help for %s"
-msgstr "E661: 抱歉,没有 '%s' 的 %s 的说明"
-
-#: ../ex_cmds.c:4719
+#: ../ex_cmds.c:4612
#, c-format
-msgid "E149: Sorry, no help for %s"
-msgstr "E149: 抱歉,没有 %s 的说明"
-
-#: ../ex_cmds.c:4751
-#, c-format
-msgid "Sorry, help file \"%s\" not found"
-msgstr "抱歉,找不到帮助文件 \"%s\""
-
-#: ../ex_cmds.c:5323
-#, c-format
-msgid "E150: Not a directory: %s"
-msgstr "E150: 不是目录: %s"
-
-#: ../ex_cmds.c:5446
-#, c-format
-msgid "E152: Cannot open %s for writing"
-msgstr "E152: 无法打开并写入 %s"
-
-#: ../ex_cmds.c:5471
-#, c-format
-msgid "E153: Unable to open %s for reading"
-msgstr "E153: 无法打开并读取 %s"
-
-#: ../ex_cmds.c:5500
-#, c-format
-msgid "E670: Mix of help file encodings within a language: %s"
-msgstr "E670: 在一种语言中混合了多种帮助文件编码: %s"
-
-#: ../ex_cmds.c:5565
-#, c-format
-msgid "E154: Duplicate tag \"%s\" in file %s/%s"
-msgstr "E154: Tag \"%s\" 在文件 %s/%s 中重复出现"
-
-#: ../ex_cmds.c:5687
-#, c-format
-msgid "E160: Unknown sign command: %s"
-msgstr "E160: 未知的 sign 命令: %s"
-
-#: ../ex_cmds.c:5704
-msgid "E156: Missing sign name"
-msgstr "E156: 缺少 sign 名称"
-
-#: ../ex_cmds.c:5746
-msgid "E612: Too many signs defined"
-msgstr "E612: Signs 定义过多"
-
-#: ../ex_cmds.c:5813
-#, c-format
-msgid "E239: Invalid sign text: %s"
-msgstr "E239: 无效的 sign 文字: %s"
-
-#: ../ex_cmds.c:5844 ../ex_cmds.c:6035
-#, c-format
-msgid "E155: Unknown sign: %s"
-msgstr "E155: 未知的 sign: %s"
-
-#: ../ex_cmds.c:5877
-msgid "E159: Missing sign number"
-msgstr "E159: 缺少 sign 号"
-
-#: ../ex_cmds.c:5971
-#, c-format
-msgid "E158: Invalid buffer name: %s"
-msgstr "E158: 无效的缓冲区名: %s"
-
-#: ../ex_cmds.c:6008
-#, c-format
-msgid "E157: Invalid sign ID: %<PRId64>"
-msgstr "E157: 无效的 sign ID: %<PRId64>"
-
-#: ../ex_cmds.c:6066
-msgid " (not supported)"
-msgstr " (不支持)"
-
-#: ../ex_cmds.c:6169
-msgid "[Deleted]"
-msgstr "[已删除]"
-
-#: ../ex_cmds2.c:139
-msgid "Entering Debug mode. Type \"cont\" to continue."
-msgstr "进入调试模式。输入 \"cont\" 继续运行。"
-
-#: ../ex_cmds2.c:143 ../ex_docmd.c:759
-#, c-format
-msgid "line %<PRId64>: %s"
-msgstr "第 %<PRId64> 行: %s"
-
-#: ../ex_cmds2.c:145
-#, c-format
-msgid "cmd: %s"
-msgstr "命令: %s"
+msgid "Pattern not found: %s"
+msgstr "找不到模式:%s"
-#: ../ex_cmds2.c:322
-#, c-format
-msgid "Breakpoint in \"%s%s\" line %<PRId64>"
-msgstr "断点 \"%s%s\" 第 %<PRId64> 行"
+#: ../ex_cmds.c:4921
+msgid "No old files"
+msgstr "没有旧文件"
-#: ../ex_cmds2.c:581
+#: ../ex_cmds2.c:204
#, c-format
-msgid "E161: Breakpoint not found: %s"
-msgstr "E161: 找不到断点: %s"
-
-#: ../ex_cmds2.c:611
-msgid "No breakpoints defined"
-msgstr "没有定义断点"
+msgid "Save changes to \"%s\"?"
+msgstr "将改变保存到 \"%s\" 吗?"
-#: ../ex_cmds2.c:617
+#: ../ex_cmds2.c:255
#, c-format
-msgid "%3d %s %s line %<PRId64>"
-msgstr "%3d %s %s 第 %<PRId64> 行"
-
-#: ../ex_cmds2.c:942
-#, fuzzy
-msgid "E750: First use \":profile start {fname}\""
-msgstr "E750: 请先使用 :profile start <fname>"
+msgid "Close \"%s\"?"
+msgstr "关闭 \"%s\"?"
-#: ../ex_cmds2.c:1269
+#: ../ex_cmds2.c:380
#, c-format
-msgid "Save changes to \"%s\"?"
-msgstr "将改变保存到 \"%s\" 吗?"
-
-#: ../ex_cmds2.c:1271 ../ex_docmd.c:8851
-msgid "Untitled"
-msgstr "未命名"
+msgid "E947: Job still running in buffer \"%s\""
+msgstr "E947: 任务仍在缓冲区 \"%s\" 中运行"
-#: ../ex_cmds2.c:1421
+#: ../ex_cmds2.c:381
#, c-format
msgid "E162: No write since last change for buffer \"%s\""
msgstr "E162: 缓冲区 \"%s\" 已修改但尚未保存"
-#: ../ex_cmds2.c:1480
+#: ../ex_cmds2.c:441
msgid "Warning: Entered other buffer unexpectedly (check autocommands)"
msgstr "警告: 意外地进入了其它缓冲区 (请检查自动命令)"
-#: ../ex_cmds2.c:1826
-msgid "E163: There is only one file to edit"
-msgstr "E163: 只有一个文件可编辑"
-
-#: ../ex_cmds2.c:1828
-msgid "E164: Cannot go before first file"
-msgstr "E164: 无法切换,已是第一个文件"
-
-#: ../ex_cmds2.c:1830
-msgid "E165: Cannot go beyond last file"
-msgstr "E165: 无法切换,已是最后一个文件"
-
-#: ../ex_cmds2.c:2175
+#: ../ex_cmds2.c:735
#, c-format
msgid "E666: compiler not supported: %s"
msgstr "E666: 不支持编译器: %s"
-#: ../ex_cmds2.c:2257
-#, c-format
-msgid "Searching for \"%s\" in \"%s\""
-msgstr "正在查找 \"%s\",在 \"%s\" 中"
-
-#: ../ex_cmds2.c:2284
-#, c-format
-msgid "Searching for \"%s\""
-msgstr "正在查找 \"%s\""
-
-#: ../ex_cmds2.c:2307
-#, c-format
-msgid "not found in 'runtimepath': \"%s\""
-msgstr "在 'runtimepath' 中找不到 \"%s\""
-
-#: ../ex_cmds2.c:2472
-#, c-format
-msgid "Cannot source a directory: \"%s\""
-msgstr "不能执行目录: \"%s\""
-
-#: ../ex_cmds2.c:2518
-#, c-format
-msgid "could not source \"%s\""
-msgstr "不能执行 \"%s\""
-
-#: ../ex_cmds2.c:2520
-#, c-format
-msgid "line %<PRId64>: could not source \"%s\""
-msgstr "第 %<PRId64> 行: 不能执行 \"%s\""
-
-#: ../ex_cmds2.c:2535
-#, c-format
-msgid "sourcing \"%s\""
-msgstr "执行 \"%s\""
-
-#: ../ex_cmds2.c:2537
-#, c-format
-msgid "line %<PRId64>: sourcing \"%s\""
-msgstr "第 %<PRId64> 行: 执行 \"%s\""
-
-#: ../ex_cmds2.c:2693
-#, c-format
-msgid "finished sourcing %s"
-msgstr "结束执行 %s"
-
-#: ../ex_cmds2.c:2765
-msgid "modeline"
-msgstr "modeline"
-
-#: ../ex_cmds2.c:2767
-msgid "--cmd argument"
-msgstr "--cmd 参数"
-
-#: ../ex_cmds2.c:2769
-msgid "-c argument"
-msgstr "-c 参数"
-
-#: ../ex_cmds2.c:2771
-msgid "environment variable"
-msgstr "环境变量"
-
-#: ../ex_cmds2.c:2773
-msgid "error handler"
-msgstr "错误的处理程序"
+#: ../ex_docmd.c:90
+msgid "E464: Ambiguous use of user-defined command"
+msgstr "E464: 不确定的用户自定义命令"
-#: ../ex_cmds2.c:3020
-msgid "W15: Warning: Wrong line separator, ^M may be missing"
-msgstr "W15: 警告: 错误的行分隔符,可能是少了 ^M"
+#: ../ex_docmd.c:92
+msgid "E492: Not an editor command"
+msgstr "E492: 不是编辑器的命令"
-#: ../ex_cmds2.c:3139
-msgid "E167: :scriptencoding used outside of a sourced file"
-msgstr "E167: 在脚本文件外使用了 :scriptencoding"
+#: ../ex_docmd.c:94
+msgid "E498: no :source file name to substitute for \"<sfile>\""
+msgstr "E498: 没有用于替换 \"<sfile>\" 的 :source 文件名"
-#: ../ex_cmds2.c:3166
-msgid "E168: :finish used outside of a sourced file"
-msgstr "E168: 在脚本文件外使用了 :finish"
+#: ../ex_docmd.c:96
+msgid "E489: no call stack to substitute for \"<stack>\""
+msgstr "E489: 没有用于替换 \"<stack>\" 的调用栈"
-#: ../ex_cmds2.c:3389
-#, c-format
-msgid "Current %slanguage: \"%s\""
-msgstr "当前的 %s语言: \"%s\""
-
-#: ../ex_cmds2.c:3404
-#, c-format
-msgid "E197: Cannot set language to \"%s\""
-msgstr "E197: 不能设定语言为 \"%s\""
+#: ../ex_docmd.c:98
+msgid "E1274: No script file name to substitute for \"<script>\""
+msgstr "E1274: 没有脚本文件名可用于替换 \"<script>\""
#. don't redisplay the window
#. don't wait for return
-#: ../ex_docmd.c:387
+#: ../ex_docmd.c:203
msgid "Entering Ex mode. Type \"visual\" to go to Normal mode."
msgstr "进入 Ex 模式。输入 \"visual\" 回到正常模式。"
-#: ../ex_docmd.c:428
+#: ../ex_docmd.c:244
msgid "E501: At end-of-file"
msgstr "E501: 已到文件末尾"
-#: ../ex_docmd.c:513
-msgid "E169: Command too recursive"
-msgstr "E169: 命令递归层数过多"
+#: ../ex_docmd.c:267
+#, c-format
+msgid "Executing: %s"
+msgstr "执行:%s"
+
+#: ../ex_docmd.c:269
+msgid "line %"
+msgstr "在行号 %"
-#: ../ex_docmd.c:1006
+#: ../ex_docmd.c:781
#, c-format
msgid "E605: Exception not caught: %s"
msgstr "E605: 异常没有被捕获: %s"
-#: ../ex_docmd.c:1085
+#: ../ex_docmd.c:853
msgid "End of sourced file"
msgstr "脚本文件结束"
-#: ../ex_docmd.c:1086
+#: ../ex_docmd.c:854
msgid "End of function"
msgstr "函数结束"
-#: ../ex_docmd.c:1628
-msgid "E464: Ambiguous use of user-defined command"
-msgstr "E464: 不确定的用户自定义命令"
-
-#: ../ex_docmd.c:1638
-msgid "E492: Not an editor command"
-msgstr "E492: 不是编辑器的命令"
+#: ../ex_docmd.c:1279
+msgid ""
+"INTERNAL: Cannot use EX_DFLALL with ADDR_NONE, ADDR_UNSIGNED or ADDR_QUICKFIX"
+msgstr ""
+"内部信息:不能使用 EX_DFLALL 与 ADDR_NONE, ADDR_UNSIGNED 或 ADDR_QUICKFIX"
-#: ../ex_docmd.c:1729
+#: ../ex_docmd.c:2118
msgid "E493: Backwards range given"
msgstr "E493: 使用了逆向的范围"
-#: ../ex_docmd.c:1733
+#: ../ex_docmd.c:2121
msgid "Backwards range given, OK to swap"
msgstr "使用了逆向的范围,确定交换吗"
#. append
#. typed wrong
-#: ../ex_docmd.c:1787
+#: ../ex_docmd.c:2179
msgid "E494: Use w or w>>"
msgstr "E494: 请使用 w 或 w>>"
-#: ../ex_docmd.c:3454
+#: ../ex_docmd.c:2957
+msgid "E943: Command table needs to be updated, run 'make'"
+msgstr "E943: 命令表需要更新,请执行 'make'"
+
+#: ../ex_docmd.c:3542
msgid "E319: The command is not available in this version"
msgstr "E319: 抱歉,命令在此版本中不可用"
-#: ../ex_docmd.c:3752
-msgid "E172: Only one file name allowed"
-msgstr "E172: 只允许一个文件名"
-
-#: ../ex_docmd.c:4238
-msgid "1 more file to edit. Quit anyway?"
-msgstr "还有 1 个文件未编辑。确实要退出吗?"
-
-#: ../ex_docmd.c:4242
-#, c-format
-msgid "%d more files to edit. Quit anyway?"
-msgstr "还有 %d 个文件未编辑。确实要退出吗?"
-
-#: ../ex_docmd.c:4248
-msgid "E173: 1 more file to edit"
-msgstr "E173: 还有 1 个文件未编辑"
-
-#: ../ex_docmd.c:4250
-#, c-format
-msgid "E173: %<PRId64> more files to edit"
-msgstr "E173: 还有 %<PRId64> 个文件未编辑"
-
-#: ../ex_docmd.c:4320
-msgid "E174: Command already exists: add ! to replace it"
-msgstr "E174: 命令已存在: 请加 ! 强制替换"
-
-#: ../ex_docmd.c:4432
-msgid ""
-"\n"
-" Name Args Range Complete Definition"
-msgstr ""
-"\n"
-" 名称 参数 范围 补全 定义 "
-
-#: ../ex_docmd.c:4516
-msgid "No user-defined commands found"
-msgstr "找不到用户自定义命令"
-
-#: ../ex_docmd.c:4538
-msgid "E175: No attribute specified"
-msgstr "E175: 没有指定属性"
-
-#: ../ex_docmd.c:4583
-msgid "E176: Invalid number of arguments"
-msgstr "E176: 无效的参数个数"
-
-#: ../ex_docmd.c:4594
-msgid "E177: Count cannot be specified twice"
-msgstr "E177: 不能指定两次计数"
-
-#: ../ex_docmd.c:4603
-msgid "E178: Invalid default value for count"
-msgstr "E178: 无效的计数默认值"
-
-#: ../ex_docmd.c:4625
-msgid "E179: argument required for -complete"
-msgstr "E179: -complete 需要参数"
-
-#: ../ex_docmd.c:4635
-#, c-format
-msgid "E181: Invalid attribute: %s"
-msgstr "E181: 无效的属性: %s"
-
-#: ../ex_docmd.c:4678
-msgid "E182: Invalid command name"
-msgstr "E182: 无效的命令名"
-
-#: ../ex_docmd.c:4691
-msgid "E183: User defined commands must start with an uppercase letter"
-msgstr "E183: 用户自定义命令必须以大写字母开头"
-
-#: ../ex_docmd.c:4696
-#, fuzzy
-msgid "E841: Reserved name, cannot be used for user defined command"
-msgstr "E464: 不确定的用户自定义命令"
+#: ../ex_docmd.c:4388
+#, fuzzy, c-format
+msgid "%d more file to edit. Quit anyway?"
+msgid_plural "%d more files to edit. Quit anyway?"
+msgstr[0] "还有 %d 个文件未编辑。确实要退出吗?"
-#: ../ex_docmd.c:4751
+#: ../ex_docmd.c:4395
#, c-format
-msgid "E184: No such user-defined command: %s"
-msgstr "E184: 没有这个用户自定义命令: %s"
+msgid "E173: %<PRId64> more file to edit"
+msgid_plural "E173: %<PRId64> more files to edit"
+msgstr[0] "E173: 还有 %<PRId64> 个文件未编辑"
-#: ../ex_docmd.c:5219
+#: ../ex_docmd.c:4430
#, c-format
-msgid "E180: Invalid complete value: %s"
-msgstr "E180: 无效的补全类型: %s"
-
-#: ../ex_docmd.c:5225
-msgid "E468: Completion argument only allowed for custom completion"
-msgstr "E468: 只有 custom 补全才允许参数"
-
-#: ../ex_docmd.c:5231
-msgid "E467: Custom completion requires a function argument"
-msgstr "E467: Custom 补全需要一个函数参数"
-
-#: ../ex_docmd.c:5257
-#, fuzzy, c-format
msgid "E185: Cannot find color scheme '%s'"
-msgstr "E185: 找不到配色方案 %s"
+msgstr "E185: 找不到配色方案 '%s'"
-#: ../ex_docmd.c:5263
+#: ../ex_docmd.c:4437
msgid "Greetings, Vim user!"
msgstr "您好,Vim 用户!"
-#: ../ex_docmd.c:5431
+#: ../ex_docmd.c:4662
msgid "E784: Cannot close last tab page"
msgstr "E784: 不能关闭最后一个 tab 页"
-#: ../ex_docmd.c:5462
+#: ../ex_docmd.c:4687
msgid "Already only one tab page"
msgstr "已经只剩一个 tab 页了"
-#: ../ex_docmd.c:6004
+#: ../ex_docmd.c:5072
#, c-format
msgid "Tab page %d"
msgstr "Tab 页 %d"
-#: ../ex_docmd.c:6295
+#: ../ex_docmd.c:5296
+msgid "E25: Nvim does not have a built-in GUI"
+msgstr "E25: 无法使用图形界面: 编译时没有启用"
+
+#: ../ex_docmd.c:5307
msgid "No swap file"
msgstr "无交换文件"
-#: ../ex_docmd.c:6478
-msgid "E747: Cannot change directory, buffer is modified (add ! to override)"
-msgstr "E747: 不能改变目录,缓冲区已修改 (请加 ! 强制执行)"
-
-#: ../ex_docmd.c:6485
+#: ../ex_docmd.c:5518
msgid "E186: No previous directory"
msgstr "E186: 前一个目录不存在"
-#: ../ex_docmd.c:6530
+#: ../ex_docmd.c:5624
msgid "E187: Unknown"
msgstr "E187: 未知"
-#: ../ex_docmd.c:6610
+#: ../ex_docmd.c:5689
msgid "E465: :winsize requires two number arguments"
msgstr "E465: :winsize 需要两个数字参数"
-#: ../ex_docmd.c:6655
-msgid "E188: Obtaining window position not implemented for this platform"
-msgstr "E188: 在此平台上不能获得窗口位置"
-
-#: ../ex_docmd.c:6662
-msgid "E466: :winpos requires two number arguments"
-msgstr "E466: :winpos 需要两个数字参数"
-
-#: ../ex_docmd.c:7241
-#, c-format
-msgid "E739: Cannot create directory: %s"
-msgstr "E739: 无法创建目录: %s"
-
-#: ../ex_docmd.c:7268
+#: ../ex_docmd.c:6207
#, c-format
msgid "E189: \"%s\" exists (add ! to override)"
msgstr "E189: \"%s\" 已存在 (请加 ! 强制执行)"
-#: ../ex_docmd.c:7273
+#: ../ex_docmd.c:6213
#, c-format
msgid "E190: Cannot open \"%s\" for writing"
msgstr "E190: 无法打开并写入 \"%s\""
#. set mark
-#: ../ex_docmd.c:7294
+#: ../ex_docmd.c:6231
msgid "E191: Argument must be a letter or forward/backward quote"
msgstr "E191: 参数必须是一个字母或者正/反引号"
-#: ../ex_docmd.c:7333
+#: ../ex_docmd.c:6315
msgid "E192: Recursive use of :normal too deep"
msgstr "E192: :normal 递归层数过深"
-#: ../ex_docmd.c:7807
+#: ../ex_docmd.c:6795
msgid "E194: No alternate file name to substitute for '#'"
msgstr "E194: 没有用于替换 '#' 的交替文件名"
-#: ../ex_docmd.c:7841
+#: ../ex_docmd.c:6834
msgid "E495: no autocommand file name to substitute for \"<afile>\""
msgstr "E495: 没有用于替换 \"<afile>\" 的自动命令文件名"
-#: ../ex_docmd.c:7850
+#: ../ex_docmd.c:6842
msgid "E496: no autocommand buffer number to substitute for \"<abuf>\""
msgstr "E496: 没有用于替换 \"<abuf>\" 的自动命令缓冲区号"
-#: ../ex_docmd.c:7861
+#: ../ex_docmd.c:6852
msgid "E497: no autocommand match name to substitute for \"<amatch>\""
msgstr "E497: 没有用于替换 \"<amatch>\" 的自动命令匹配名"
-#: ../ex_docmd.c:7870
-msgid "E498: no :source file name to substitute for \"<sfile>\""
-msgstr "E498: 没有用于替换 \"<sfile>\" 的 :source 文件名"
-
-#: ../ex_docmd.c:7876
-#, fuzzy
+#: ../ex_docmd.c:6884
msgid "E842: no line number to use for \"<slnum>\""
-msgstr "E498: 没有用于替换 \"<sfile>\" 的 :source 文件名"
+msgstr "E842: 没有行号可用于替换 \"<slnum>\""
-#: ../ex_docmd.c:7903
-#, fuzzy, c-format
+#: ../ex_docmd.c:6893
+msgid "E961: no line number to use for \"<sflnum>\""
+msgstr "E961: 没有用于替换 \"<sflnum>\" 的行号"
+
+#: ../ex_docmd.c:6942
+#, fuzzy, no-c-format
msgid "E499: Empty file name for '%' or '#', only works with \":p:h\""
msgstr "E499: '%' 或 '#' 为空文件名,只能用于 \":p:h\""
-#: ../ex_docmd.c:7905
+#: ../ex_docmd.c:6944
msgid "E500: Evaluates to an empty string"
msgstr "E500: 结果为空字符串"
-#: ../ex_docmd.c:8838
-msgid "E195: Cannot open viminfo file for reading"
-msgstr "E195: 无法打开并读取 viminfo 文件"
+#: ../ex_docmd.c:7018
+msgid "Untitled"
+msgstr "未命名"
-#: ../ex_eval.c:464
+#: ../ex_eval.c:456
msgid "E608: Cannot :throw exceptions with 'Vim' prefix"
msgstr "E608: 不能 :throw 前缀为 'Vim' 的异常"
-#. always scroll up, don't overwrite
-#: ../ex_eval.c:496
+#: ../ex_eval.c:500
#, c-format
msgid "Exception thrown: %s"
msgstr "抛出异常: %s"
-#: ../ex_eval.c:545
+#: ../ex_eval.c:553
#, c-format
msgid "Exception finished: %s"
msgstr "完成异常: %s"
-#: ../ex_eval.c:546
+#: ../ex_eval.c:553
#, c-format
msgid "Exception discarded: %s"
msgstr "丢弃异常: %s"
-#: ../ex_eval.c:588 ../ex_eval.c:634
+#: ../ex_eval.c:597 ../ex_eval.c:647
#, c-format
msgid "%s, line %<PRId64>"
msgstr "%s,第 %<PRId64> 行"
-#. always scroll up, don't overwrite
-#: ../ex_eval.c:608
+#: ../ex_eval.c:620
#, c-format
msgid "Exception caught: %s"
msgstr "捕获异常: %s"
-#: ../ex_eval.c:676
+#: ../ex_eval.c:688
#, c-format
msgid "%s made pending"
msgstr "%s 待定"
-#: ../ex_eval.c:679
-#, fuzzy, c-format
+#: ../ex_eval.c:691
+#, c-format
msgid "%s resumed"
-msgstr " 已返回\n"
+msgstr "%s 已恢复"
-#: ../ex_eval.c:683
+#: ../ex_eval.c:695
#, c-format
msgid "%s discarded"
msgstr "%s 舍弃"
-#: ../ex_eval.c:708
+#: ../ex_eval.c:720
msgid "Exception"
msgstr "异常"
-#: ../ex_eval.c:713
+#: ../ex_eval.c:724
msgid "Error and interrupt"
msgstr "错误和中断"
-#: ../ex_eval.c:715
+#: ../ex_eval.c:726
msgid "Error"
msgstr "错误"
#. if (pending & CSTP_INTERRUPT)
-#: ../ex_eval.c:717
+#: ../ex_eval.c:728
msgid "Interrupt"
msgstr "中断"
-#: ../ex_eval.c:795
+#: ../ex_eval.c:816
msgid "E579: :if nesting too deep"
msgstr "E579: :if 嵌套层数过深"
-#: ../ex_eval.c:830
+#: ../ex_eval.c:844
msgid "E580: :endif without :if"
msgstr "E580: :endif 缺少对应的 :if"
-#: ../ex_eval.c:873
+#: ../ex_eval.c:874
msgid "E581: :else without :if"
msgstr "E581: :else 缺少对应的 :if"
-#: ../ex_eval.c:876
+#: ../ex_eval.c:877
msgid "E582: :elseif without :if"
msgstr "E582: :elseif 缺少对应的 :if"
-#: ../ex_eval.c:880
+#: ../ex_eval.c:881
msgid "E583: multiple :else"
msgstr "E583: 多个 :else"
-#: ../ex_eval.c:883
+#: ../ex_eval.c:884
msgid "E584: :elseif after :else"
msgstr "E584: :elseif 在 :else 后面"
-#: ../ex_eval.c:941
+#: ../ex_eval.c:951
msgid "E585: :while/:for nesting too deep"
msgstr "E585: :while/:for 嵌套层数过深"
-#: ../ex_eval.c:1028
+#: ../ex_eval.c:1022
msgid "E586: :continue without :while or :for"
msgstr "E586: :continue 缺少对应的 :while 或 :for"
-#: ../ex_eval.c:1061
+#: ../ex_eval.c:1052
msgid "E587: :break without :while or :for"
msgstr "E587: :break 缺少对应的 :while 或 :for"
-#: ../ex_eval.c:1102
+#: ../ex_eval.c:1091
msgid "E732: Using :endfor with :while"
msgstr "E732: :while 以 :endfor 结尾"
-#: ../ex_eval.c:1104
+#: ../ex_eval.c:1093
msgid "E733: Using :endwhile with :for"
msgstr "E733: :for 以 :endwhile 结尾"
-#: ../ex_eval.c:1247
+#: ../ex_eval.c:1229
msgid "E601: :try nesting too deep"
msgstr "E601: :try 嵌套层数过深"
-#: ../ex_eval.c:1317
+#: ../ex_eval.c:1286
msgid "E603: :catch without :try"
msgstr "E603: :catch 缺少对应的 :try"
#. Give up for a ":catch" after ":finally" and ignore it.
-#. * Just parse.
-#: ../ex_eval.c:1332
+#. Just parse.
+#: ../ex_eval.c:1303
msgid "E604: :catch after :finally"
msgstr "E604: :catch 在 :finally 后面"
-#: ../ex_eval.c:1451
+#: ../ex_eval.c:1426
msgid "E606: :finally without :try"
msgstr "E606: :finally 缺少对应的 :try"
#. Give up for a multiple ":finally" and ignore it.
-#: ../ex_eval.c:1467
+#: ../ex_eval.c:1445
msgid "E607: multiple :finally"
msgstr "E607: 多个 :finally"
-#: ../ex_eval.c:1571
+#: ../ex_eval.c:1540
msgid "E602: :endtry without :try"
msgstr "E602: :endtry 缺少对应的 :try"
-#: ../ex_eval.c:2026
+#: ../ex_eval.c:1984
msgid "E193: :endfunction not inside a function"
msgstr "E193: :endfunction 不在函数内"
-#: ../ex_getln.c:1643
-msgid "E788: Not allowed to edit another buffer now"
-msgstr "E788: 目前不允许编辑别的缓冲区"
-
-#: ../ex_getln.c:1656
-#, fuzzy
+#: ../ex_getln.c:2661
msgid "E811: Not allowed to change buffer information now"
-msgstr "E788: 目前不允许编辑别的缓冲区"
+msgstr "E811: 目前不允许更改缓冲区的信息"
-#: ../ex_getln.c:3178
-msgid "tagname"
-msgstr "tag 名"
+#: ../ex_getln.c:2939
+#, c-format
+msgid "E5408: Unable to get g:Nvim_color_cmdline callback: %s"
+msgstr ""
-#: ../ex_getln.c:3181
-msgid " kind file\n"
-msgstr " 类型 文件\n"
+#: ../ex_getln.c:2973
+#, c-format
+msgid "E5407: Callback has thrown an exception: %s"
+msgstr ""
-#: ../ex_getln.c:4799
-msgid "'history' option is zero"
-msgstr "选项 'history' 为零"
+#: ../ex_getln.c:2986
+msgid "E5400: Callback should return list"
+msgstr "E5400: 回调应返回列表"
-# do not translate to avoid writing Chinese in files
-#: ../ex_getln.c:5046
-#, fuzzy, c-format
-msgid ""
-"\n"
-"# %s History (newest to oldest):\n"
+#: ../ex_getln.c:2996
+#, c-format
+msgid "E5401: List item %i is not a List"
+msgstr "E5401: 列表项 %i 不是列表"
+
+#: ../ex_getln.c:3001
+#, c-format
+msgid "E5402: List item %i has incorrect length: %d /= 3"
msgstr ""
-"\n"
-"# %s 历史记录 (从新到旧):\n"
-# do not translate to avoid writing Chinese in files
-#: ../ex_getln.c:5047
-#, fuzzy
-msgid "Command Line"
-msgstr "命令行"
+#: ../ex_getln.c:3011
+msgid "E5403: Chunk %i start %"
+msgstr ""
-# do not translate to avoid writing Chinese in files
-#: ../ex_getln.c:5048
-#, fuzzy
-msgid "Search String"
-msgstr "查找字符串"
+#: ../ex_getln.c:3016
+msgid "E5405: Chunk %i start %"
+msgstr ""
-# do not translate to avoid writing Chinese in files
-#: ../ex_getln.c:5049
-#, fuzzy
-msgid "Expression"
-msgstr "表达式"
+#: ../ex_getln.c:3032
+msgid "E5404: Chunk %i end %"
+msgstr ""
-# do not translate to avoid writing Chinese in files
-#: ../ex_getln.c:5050
-#, fuzzy
-msgid "Input Line"
-msgstr "输入行"
+#: ../ex_getln.c:3039
+msgid "E5406: Chunk %i end %"
+msgstr ""
-#: ../ex_getln.c:5117
-msgid "E198: cmd_pchar beyond the command length"
-msgstr "E198: cmd_pchar 超过命令长度"
+# do not translate to avoid writing Chinese in files
+#. Create empty command-line buffer.
+#: ../ex_getln.c:4223
+msgid "[Command Line]"
+msgstr ""
-#: ../ex_getln.c:5279
+#: ../ex_getln.c:4331
msgid "E199: Active window or buffer deleted"
msgstr "E199: 活动窗口或缓冲区已被删除"
-#: ../file_search.c:203
+#: ../file_search.c:185
msgid "E854: path too long for completion"
msgstr "E854: 补全用的路径太长"
-#: ../file_search.c:446
+#: ../file_search.c:419
#, c-format
msgid ""
"E343: Invalid path: '**[number]' must be at the end of the path or be "
"followed by '%s'."
msgstr "E343: 无效的路径: '**[number]' 必须在路径末尾或者后面接 '%s'。"
-#: ../file_search.c:1505
+#: ../file_search.c:1486
#, c-format
msgid "E344: Can't find directory \"%s\" in cdpath"
msgstr "E344: cdpath 中找不到目录 \"%s\""
-#: ../file_search.c:1508
+#: ../file_search.c:1489
#, c-format
msgid "E345: Can't find file \"%s\" in path"
msgstr "E345: 在路径中找不到文件 \"%s\""
-#: ../file_search.c:1512
+#: ../file_search.c:1494
#, c-format
msgid "E346: No more directory \"%s\" found in cdpath"
msgstr "E346: 在路径中找不到更多的文件 \"%s\""
-#: ../file_search.c:1515
+#: ../file_search.c:1497
#, c-format
msgid "E347: No more file \"%s\" found in path"
msgstr "E347: 在路径中找不到更多的文件 \"%s\""
-#: ../fileio.c:137
-#, fuzzy
+#: ../fileio.c:126
msgid "E812: Autocommands changed buffer or buffer name"
-msgstr "E135: *Filter* 自动命令不可以改变当前缓冲区"
+msgstr "E812: 自动命令改变了缓冲区或者缓冲区名称"
-#: ../fileio.c:368
-msgid "Illegal file name"
-msgstr "无效的文件名"
+#: ../fileio.c:128
+#, c-format
+msgid "E676: No matching autocommands for buftype=%s buffer"
+msgstr ""
-#: ../fileio.c:395 ../fileio.c:476 ../fileio.c:2543 ../fileio.c:2578
+#: ../fileio.c:262 ../fileio.c:2499 ../fileio.c:2529
msgid "is a directory"
msgstr "是目录"
-#: ../fileio.c:397
+#: ../fileio.c:363
+msgid "Illegal file name"
+msgstr "无效的文件名"
+
+#: ../fileio.c:399
msgid "is not a file"
msgstr "不是文件"
-#: ../fileio.c:508 ../fileio.c:3522
-msgid "[New File]"
-msgstr "[新文件]"
-
-#: ../fileio.c:511
+#: ../fileio.c:490
msgid "[New DIRECTORY]"
msgstr "[新目录]"
-#: ../fileio.c:529 ../fileio.c:532
+#. libuv only returns -errno
+#. in Unix and in Windows
+#. open() does not set
+#. EOVERFLOW
+#: ../fileio.c:510 ../fileio.c:516
msgid "[File too big]"
msgstr "[文件过大]"
-#: ../fileio.c:534
+#: ../fileio.c:518
msgid "[Permission Denied]"
msgstr "[权限不足]"
-#: ../fileio.c:653
+#: ../fileio.c:661
msgid "E200: *ReadPre autocommands made the file unreadable"
msgstr "E200: *ReadPre 自动命令导致文件不可读"
-#: ../fileio.c:655
+#: ../fileio.c:663
msgid "E201: *ReadPre autocommands must not change current buffer"
msgstr "E201: *ReadPre 自动命令不允许改变当前缓冲区"
-#: ../fileio.c:672
-msgid "Nvim: Reading from stdin...\n"
-msgstr "Vim: 从标准输入读取...\n"
-
#. Re-opening the original file failed!
-#: ../fileio.c:909
+#: ../fileio.c:865
msgid "E202: Conversion made file unreadable!"
-msgstr "E202: 转换导致文件不可读"
-
-#. fifo or socket
-#: ../fileio.c:1782
-msgid "[fifo/socket]"
-msgstr "[fifo/socket]"
+msgstr "E202: 转换导致文件不可读!"
#. fifo
-#: ../fileio.c:1788
+#: ../fileio.c:1762
msgid "[fifo]"
msgstr "[fifo]"
#. or socket
-#: ../fileio.c:1794
+#: ../fileio.c:1766
msgid "[socket]"
msgstr "[socket]"
#. or character special
-#: ../fileio.c:1801
-#, fuzzy
+#: ../fileio.c:1771
msgid "[character special]"
-msgstr "1 个字符"
+msgstr "[字符设备]"
-#: ../fileio.c:1815
+#: ../fileio.c:1785
msgid "[CR missing]"
msgstr "[缺少 CR]'"
-#: ../fileio.c:1819
+#: ../fileio.c:1789
msgid "[long lines split]"
msgstr "[长行分割]"
-#: ../fileio.c:1823 ../fileio.c:3512
+#: ../fileio.c:1793 ../fileio.c:3427
msgid "[NOT converted]"
msgstr "[未转换]"
-#: ../fileio.c:1826 ../fileio.c:3515
+#: ../fileio.c:1796 ../fileio.c:3430
msgid "[converted]"
msgstr "[已转换]"
-#: ../fileio.c:1831
+#: ../fileio.c:1801
#, c-format
msgid "[CONVERSION ERROR in line %<PRId64>]"
msgstr "[第 %<PRId64> 行转换错误]"
-#: ../fileio.c:1835
+#: ../fileio.c:1805
#, c-format
msgid "[ILLEGAL BYTE in line %<PRId64>]"
msgstr "[第 %<PRId64> 行无效字符]"
-#: ../fileio.c:1838
+#: ../fileio.c:1808
msgid "[READ ERRORS]"
msgstr "[读错误]"
-#: ../fileio.c:2104
+#: ../fileio.c:2069
msgid "Can't find temp file for conversion"
msgstr "找不到用于转换的临时文件"
-#: ../fileio.c:2110
+#: ../fileio.c:2075
msgid "Conversion with 'charconvert' failed"
msgstr "'charconvert' 转换失败"
-#: ../fileio.c:2113
+#: ../fileio.c:2078
msgid "can't read output of 'charconvert'"
msgstr "无法读取 'charconvert' 的输出"
-#: ../fileio.c:2437
-msgid "E676: No matching autocommands for acwrite buffer"
-msgstr "E676: 找不到 acwrite 缓冲区对应的自动命令"
+#: ../fileio.c:2116
+msgid "[New]"
+msgstr "[新]"
+
+#: ../fileio.c:2116
+msgid "[New File]"
+msgstr "[新文件]"
-#: ../fileio.c:2466
+#: ../fileio.c:2416
msgid "E203: Autocommands deleted or unloaded buffer to be written"
msgstr "E203: 自动命令删除或释放了要写入的缓冲区"
-#: ../fileio.c:2486
+#: ../fileio.c:2435
msgid "E204: Autocommand changed number of lines in unexpected way"
msgstr "E204: 自动命令意外地改变了行数"
-#: ../fileio.c:2548 ../fileio.c:2565
+#: ../fileio.c:2503 ../fileio.c:2517
msgid "is not a file or writable device"
msgstr "不是文件或可写的设备"
-#: ../fileio.c:2601
+#: ../fileio.c:2547
msgid "is read-only (add ! to override)"
msgstr "只读 (请加 ! 强制执行)"
-#: ../fileio.c:2886
-msgid "E506: Can't write to backup file (add ! to override)"
-msgstr "E506: 无法写入备份文件 (请加 ! 强制执行)"
-
-#: ../fileio.c:2898
-msgid "E507: Close error for backup file (add ! to override)"
-msgstr "E507: 关闭备份文件出错 (请加 ! 强制执行)"
-
-#: ../fileio.c:2901
-msgid "E508: Can't read file for backup (add ! to override)"
-msgstr "E508: 无法读取文件以供备份 (请加 ! 强制执行)"
+#: ../fileio.c:2701 ../fileio.c:2852
+#, c-format
+msgid "E303: Unable to create directory \"%s\" for backup file: %s"
+msgstr "E303: 无法为备份文件创建目录 \"%s\":%s"
-#: ../fileio.c:2923
+#: ../fileio.c:2792 ../fileio.c:2813
msgid "E509: Cannot create backup file (add ! to override)"
msgstr "E509: 无法创建备份文件 (请加 ! 强制执行)"
-#: ../fileio.c:3008
+#: ../fileio.c:2912
msgid "E510: Can't make backup file (add ! to override)"
msgstr "E510: 无法生成备份文件 (请加 ! 强制执行)"
#. Can't write without a tempfile!
-#: ../fileio.c:3121
+#: ../fileio.c:3015
msgid "E214: Can't find temp file for writing"
msgstr "E214: 找不到用于写入的临时文件"
-#: ../fileio.c:3134
+#: ../fileio.c:3031
msgid "E213: Cannot convert (add ! to write without conversion)"
msgstr "E213: 无法转换 (请加 ! 强制不转换写入)"
-#: ../fileio.c:3169
+#: ../fileio.c:3081
msgid "E166: Can't open linked file for writing"
msgstr "E166: 无法打开并写入链接文件"
-#: ../fileio.c:3173
-msgid "E212: Can't open file for writing"
-msgstr "E212: 无法打开并写入文件"
-
-#: ../fileio.c:3363
-msgid "E667: Fsync failed"
-msgstr "E667: 同步失败"
+#: ../fileio.c:3084
+#, c-format
+msgid "E212: Can't open file for writing: %s"
+msgstr "E212: 无法打开并写入文件:%s"
-#: ../fileio.c:3398
-msgid "E512: Close failed"
-msgstr "E512: 关闭失败"
+#: ../fileio.c:3324
+#, c-format
+msgid "E512: Close failed: %s"
+msgstr "E512: 关闭失败:%s"
-#: ../fileio.c:3436
+#: ../fileio.c:3363
msgid "E513: write error, conversion failed (make 'fenc' empty to override)"
msgstr "E513: 写入错误,转换失败 (请将 'fenc' 置空以强制执行)"
-#: ../fileio.c:3441
-#, fuzzy, c-format
-msgid ""
-"E513: write error, conversion failed in line %<PRId64> (make 'fenc' empty to "
-"override)"
-msgstr "E513: 写入错误,转换失败 (请将 'fenc' 置空以强制执行)"
+#. NOLINT(runtime/printf)
+#: ../fileio.c:3369
+msgid "E513: write error, conversion failed in line %"
+msgstr ""
-#: ../fileio.c:3448
+#: ../fileio.c:3376
msgid "E514: write error (file system full?)"
msgstr "E514: 写入错误 (文件系统已满?)"
-#: ../fileio.c:3506
+#: ../fileio.c:3420
msgid " CONVERSION ERROR"
msgstr " 转换错误"
-#: ../fileio.c:3509
+#: ../fileio.c:3423
#, fuzzy, c-format
msgid " in line %<PRId64>;"
msgstr "第 %<PRId64> 行"
-#: ../fileio.c:3519
+#: ../fileio.c:3434
msgid "[Device]"
msgstr "[设备]"
-#: ../fileio.c:3522
-msgid "[New]"
-msgstr "[新]"
-
-#: ../fileio.c:3535
+#: ../fileio.c:3451
msgid " [a]"
msgstr " [a]"
-#: ../fileio.c:3535
+#: ../fileio.c:3451
msgid " appended"
msgstr " 已追加"
-#: ../fileio.c:3537
+#: ../fileio.c:3453
msgid " [w]"
msgstr " [w]"
-#: ../fileio.c:3537
+#: ../fileio.c:3453
msgid " written"
msgstr " 已写入"
-#: ../fileio.c:3579
+#: ../fileio.c:3496
msgid "E205: Patchmode: can't save original file"
msgstr "E205: Patchmode: 无法保存原始文件"
-#: ../fileio.c:3602
+#: ../fileio.c:3515
msgid "E206: patchmode: can't touch empty original file"
msgstr "E206: Patchmode: 无法生成空的原始文件"
-#: ../fileio.c:3616
+#: ../fileio.c:3530
msgid "E207: Can't delete backup file"
msgstr "E207: 无法删除备份文件"
-#: ../fileio.c:3672
+#. Set highlight for error messages.
+#: ../fileio.c:3584
msgid ""
"\n"
"WARNING: Original file may be lost or damaged\n"
@@ -2283,1309 +2715,1511 @@ msgstr ""
"\n"
"警告: 原始文件可能已丢失或损坏\n"
-#: ../fileio.c:3675
+#: ../fileio.c:3586
msgid "don't quit the editor until the file is successfully written!"
msgstr "在文件正确写入前请勿退出编辑器!"
-#: ../fileio.c:3795
+#: ../fileio.c:3721
msgid "[dos]"
msgstr "[dos]"
-#: ../fileio.c:3795
+#: ../fileio.c:3721
msgid "[dos format]"
msgstr "[dos 格式]"
-#: ../fileio.c:3801
+#: ../fileio.c:3726
msgid "[mac]"
msgstr "[mac]"
-#: ../fileio.c:3801
+#: ../fileio.c:3726
msgid "[mac format]"
msgstr "[mac 格式]"
-#: ../fileio.c:3807
+#: ../fileio.c:3731
msgid "[unix]"
msgstr "[unix]"
-#: ../fileio.c:3807
+#: ../fileio.c:3731
msgid "[unix format]"
msgstr "[unix 格式]"
-#: ../fileio.c:3831
-msgid "1 line, "
-msgstr "1 行,"
-
-#: ../fileio.c:3833
+#: ../fileio.c:3753
#, c-format
-msgid "%<PRId64> lines, "
-msgstr "%<PRId64> 行,"
-
-#: ../fileio.c:3836
-msgid "1 character"
-msgstr "1 个字符"
+msgid "%<PRId64> line, "
+msgid_plural "%<PRId64> lines, "
+msgstr[0] "%<PRId64> 行,"
-#: ../fileio.c:3838
+#: ../fileio.c:3757
#, c-format
-msgid "%<PRId64> characters"
-msgstr "%<PRId64> 个字符"
+msgid "%<PRId64> byte"
+msgid_plural "%<PRId64> bytes"
+msgstr[0] "%<PRId64> 字节"
-#: ../fileio.c:3849
+#: ../fileio.c:3766
msgid "[noeol]"
msgstr "[noeol]"
-#: ../fileio.c:3849
+#: ../fileio.c:3766
msgid "[Incomplete last line]"
msgstr "[最后一行不完整]"
-#. don't overwrite messages here
-#. must give this prompt
-#. don't use emsg() here, don't want to flush the buffers
-#: ../fileio.c:3865
+#. Don't overwrite messages here.
+#. Must give this prompt.
+#. Don't use emsg() here, don't want to flush the buffers.
+#: ../fileio.c:3779
msgid "WARNING: The file has been changed since reading it!!!"
msgstr "警告: 此文件自读入后已发生变动!!!"
-#: ../fileio.c:3867
+#: ../fileio.c:3781
msgid "Do you really want to write to it"
msgstr "确实要写入吗"
-#: ../fileio.c:4648
+#: ../fileio.c:4629
#, c-format
msgid "E208: Error writing to \"%s\""
msgstr "E208: 写入文件 \"%s\" 出错"
-#: ../fileio.c:4655
+#: ../fileio.c:4637
#, c-format
msgid "E209: Error closing \"%s\""
msgstr "E209: 关闭文件 \"%s\" 出错"
-#: ../fileio.c:4657
+#: ../fileio.c:4640
#, c-format
msgid "E210: Error reading \"%s\""
msgstr "E210: 读取文件 \"%s\" 出错"
-#: ../fileio.c:4883
+#: ../fileio.c:4859
msgid "E246: FileChangedShell autocommand deleted buffer"
msgstr "E246: FileChangedShell 自动命令删除了缓冲区"
-#: ../fileio.c:4894
+#: ../fileio.c:4876
#, c-format
msgid "E211: File \"%s\" no longer available"
msgstr "E211: 文件 \"%s\" 已经不存在"
-#: ../fileio.c:4906
+#: ../fileio.c:4888
#, c-format
msgid ""
"W12: Warning: File \"%s\" has changed and the buffer was changed in Vim as "
"well"
msgstr "W12: 警告: 文件 \"%s\" 已变动,并且在 Vim 中的缓冲区也已变动"
-#: ../fileio.c:4907
+#: ../fileio.c:4889
msgid "See \":help W12\" for more info."
msgstr "进一步说明请见 \":help W12\""
-#: ../fileio.c:4910
+#: ../fileio.c:4891
#, c-format
msgid "W11: Warning: File \"%s\" has changed since editing started"
msgstr "W11: 警告: 编辑开始后,文件 \"%s\" 已变动"
-#: ../fileio.c:4911
+#: ../fileio.c:4892
msgid "See \":help W11\" for more info."
msgstr "进一步说明请见 \":help W11\""
-#: ../fileio.c:4914
+#: ../fileio.c:4894
#, c-format
msgid "W16: Warning: Mode of file \"%s\" has changed since editing started"
msgstr "W16: 警告: 编辑开始后,文件 \"%s\" 的模式已变动"
-#: ../fileio.c:4915
+#: ../fileio.c:4895
msgid "See \":help W16\" for more info."
msgstr "进一步说明请见 \":help W16\""
-#: ../fileio.c:4927
+#: ../fileio.c:4908
#, c-format
msgid "W13: Warning: File \"%s\" has been created after editing started"
msgstr "W13: 警告: 编辑开始后,文件 \"%s\" 已被创建"
-#: ../fileio.c:4947
+#: ../fileio.c:4929
msgid "Warning"
msgstr "警告"
-#: ../fileio.c:4948
+#: ../fileio.c:4930
msgid ""
"&OK\n"
-"&Load File"
+"&Load File\n"
+"Load File &and Options"
msgstr ""
"确定(&O)\n"
-"加载文件(&L)"
+"加载文件(&L)\n"
+"加载文件和选项(&A)"
-#: ../fileio.c:5065
+#: ../fileio.c:5050
#, c-format
msgid "E462: Could not prepare for reloading \"%s\""
msgstr "E462: 无法为重新加载 \"%s\" 做准备"
-#: ../fileio.c:5078
+#: ../fileio.c:5062
#, c-format
msgid "E321: Could not reload \"%s\""
msgstr "E321: 无法重新加载 \"%s\""
-#: ../fileio.c:5601
-msgid "--Deleted--"
-msgstr "--已删除--"
-
-#: ../fileio.c:5732
-#, c-format
-msgid "auto-removing autocommand: %s <buffer=%d>"
-msgstr "自动删除自动命令: %s <buffer=%d>"
-
-#. the group doesn't exist
-#: ../fileio.c:5772
-#, c-format
-msgid "E367: No such group: \"%s\""
-msgstr "E367: 无此组: \"%s\""
-
-#: ../fileio.c:5897
-#, c-format
-msgid "E215: Illegal character after *: %s"
-msgstr "E215: * 后面有无效字符: %s"
-
-#: ../fileio.c:5905
-#, c-format
-msgid "E216: No such event: %s"
-msgstr "E216: 无此事件: %s"
-
-#: ../fileio.c:5907
-#, c-format
-msgid "E216: No such group or event: %s"
-msgstr "E216: 无此组或事件: %s"
-
-#. Highlight title
-#: ../fileio.c:6090
-msgid ""
-"\n"
-"--- Autocommands ---"
-msgstr ""
-"\n"
-"--- 自动命令 ---"
-
-#: ../fileio.c:6293
-#, c-format
-msgid "E680: <buffer=%d>: invalid buffer number "
-msgstr "E680: <buffer=%d>: 无效的缓冲区号 "
-
-#: ../fileio.c:6370
-msgid "E217: Can't execute autocommands for ALL events"
-msgstr "E217: 不能对所有事件执行自动命令"
-
-#: ../fileio.c:6393
-msgid "No matching autocommands"
-msgstr "没有匹配的自动命令"
-
-#: ../fileio.c:6831
-msgid "E218: autocommand nesting too deep"
-msgstr "E218: 自动命令嵌套层数过深"
-
-#: ../fileio.c:7143
-#, c-format
-msgid "%s Autocommands for \"%s\""
-msgstr "%s 自动命令 \"%s\""
-
-#: ../fileio.c:7149
-#, c-format
-msgid "Executing %s"
-msgstr "执行 %s"
-
-#: ../fileio.c:7211
-#, c-format
-msgid "autocommand %s"
-msgstr "自动命令 %s"
-
-#: ../fileio.c:7795
+#: ../fileio.c:5680
msgid "E219: Missing {."
msgstr "E219: 缺少 {。"
-#: ../fileio.c:7797
+#: ../fileio.c:5682
msgid "E220: Missing }."
msgstr "E220: 缺少 }。"
-#: ../fold.c:93
+#: ../fold.c:104
msgid "E490: No fold found"
msgstr "E490: 找不到折叠"
-#: ../fold.c:544
+#: ../fold.c:520
msgid "E350: Cannot create fold with current 'foldmethod'"
msgstr "E350: 不能在当前的 'foldmethod' 下创建折叠"
-#: ../fold.c:546
+#: ../fold.c:522
msgid "E351: Cannot delete fold with current 'foldmethod'"
msgstr "E351: 不能在当前的 'foldmethod' 下删除折叠"
-#: ../fold.c:1784
+#: ../fold.c:1789
#, c-format
-msgid "+--%3ld lines folded "
-msgstr "+--已折叠 %3ld 行"
+msgid "+--%3ld line folded"
+msgid_plural "+--%3ld lines folded "
+msgstr[0] "+--已折叠 %3ld 行"
+
+#: ../fold.c:3275
+#, c-format
+msgid "+-%s%3ld line: "
+msgid_plural "+-%s%3ld lines: "
+msgstr[0] "+-%s%3ld 行: "
#. buffer has already been read
-#: ../getchar.c:273
+#: ../getchar.c:235
msgid "E222: Add to read buffer"
msgstr "E222: 添加到已读缓冲区中"
-#: ../getchar.c:2040
+#: ../getchar.c:2172
msgid "E223: recursive mapping"
msgstr "E223: 递归映射"
-#: ../getchar.c:2849
-#, c-format
-msgid "E224: global abbreviation already exists for %s"
-msgstr "E224: 全局缩写 %s 已存在"
-
-#: ../getchar.c:2852
-#, c-format
-msgid "E225: global mapping already exists for %s"
-msgstr "E225: 全局映射 %s 已存在"
-
-#: ../getchar.c:2952
-#, c-format
-msgid "E226: abbreviation already exists for %s"
-msgstr "E226: 缩写 %s 已存在"
-
-#: ../getchar.c:2955
-#, c-format
-msgid "E227: mapping already exists for %s"
-msgstr "E227: 映射 %s 已存在"
-
-#: ../getchar.c:3008
-msgid "No abbreviation found"
-msgstr "找不到缩写"
-
-#: ../getchar.c:3010
-msgid "No mapping found"
-msgstr "找不到映射"
-
-#: ../getchar.c:3974
-msgid "E228: makemap: Illegal mode"
-msgstr "E228: makemap: 无效的模式"
-
-#. key value of 'cedit' option
-#. type of cmdline window or 0
-#. result of cmdline window or 0
-#: ../globals.h:924
+#. /< type of cmdline window or 0
+#. /< result of cmdline window or 0
+#. /< cmdline recursion level
+#: ../globals.h:815
msgid "--No lines in buffer--"
msgstr "--缓冲区无内容--"
-#.
-#. * The error messages that can be shared are included here.
-#. * Excluded are errors that are only used once and debugging messages.
-#.
-#: ../globals.h:996
+#. uncrustify:off
+#. The error messages that can be shared are included here.
+#. Excluded are errors that are only used once and debugging messages.
+#: ../globals.h:861
msgid "E470: Command aborted"
msgstr "E470: 命令被中止"
-#: ../globals.h:997
+#: ../globals.h:862
+msgid "E905: Cannot set this option after startup"
+msgstr "E905: 启动后不能设置这个选项"
+
+#: ../globals.h:863
+msgid "E903: Could not spawn API job"
+msgstr "E903: 无法生成 API 任务"
+
+#: ../globals.h:864
msgid "E471: Argument required"
msgstr "E471: 需要参数"
-#: ../globals.h:998
+#: ../globals.h:865
msgid "E10: \\ should be followed by /, ? or &"
msgstr "E10: \\ 后面应该跟有 /、? 或 &"
-#: ../globals.h:1000
+#: ../globals.h:866
msgid "E11: Invalid in command-line window; <CR> executes, CTRL-C quits"
msgstr "E11: 在命令行窗口中无效;<CR> 执行,CTRL-C 退出"
-#: ../globals.h:1002
+#: ../globals.h:867
msgid "E12: Command not allowed from exrc/vimrc in current dir or tag search"
msgstr "E12: 当前目录中的 exrc/vimrc 或 tag 查找中不允许此命令"
-#: ../globals.h:1003
+#: ../globals.h:868
+msgid "E169: Command too recursive"
+msgstr "E169: 命令递归层数过多"
+
+#: ../globals.h:869
msgid "E171: Missing :endif"
msgstr "E171: 缺少 :endif"
-#: ../globals.h:1004
+#: ../globals.h:870
msgid "E600: Missing :endtry"
msgstr "E600: 缺少 :endtry"
-#: ../globals.h:1005
+#: ../globals.h:871
msgid "E170: Missing :endwhile"
msgstr "E170: 缺少 :endwhile"
-#: ../globals.h:1006
+#: ../globals.h:872
msgid "E170: Missing :endfor"
msgstr "E170: 缺少 :endfor"
-#: ../globals.h:1007
+#: ../globals.h:873
msgid "E588: :endwhile without :while"
msgstr "E588: :endwhile 缺少对应的 :while"
-#: ../globals.h:1008
+#: ../globals.h:874
msgid "E588: :endfor without :for"
msgstr "E588: :endfor 缺少对应的 :for"
-#: ../globals.h:1009
+#: ../globals.h:875
msgid "E13: File exists (add ! to override)"
msgstr "E13: 文件已存在 (请加 ! 强制执行)"
-#: ../globals.h:1010
+#: ../globals.h:876
msgid "E472: Command failed"
msgstr "E472: 命令执行失败"
-#: ../globals.h:1011
+#: ../globals.h:877
msgid "E473: Internal error"
msgstr "E473: 内部错误"
-#: ../globals.h:1012
+#: ../globals.h:878
+#, c-format
+msgid "E685: Internal error: %s"
+msgstr "E685: 内部错误: %s"
+
+#: ../globals.h:879
msgid "Interrupted"
msgstr "已中断"
-#: ../globals.h:1013
-msgid "E14: Invalid address"
-msgstr "E14: 无效的地址"
-
-#: ../globals.h:1014
+#: ../globals.h:880
msgid "E474: Invalid argument"
msgstr "E474: 无效的参数"
-#: ../globals.h:1015
+#: ../globals.h:881
#, c-format
msgid "E475: Invalid argument: %s"
msgstr "E475: 无效的参数: %s"
-#: ../globals.h:1016
+#: ../globals.h:882
+#, c-format
+msgid "E475: Invalid value for argument %s"
+msgstr "E475: 参数 %s 的值无效"
+
+#: ../globals.h:883
+#, c-format
+msgid "E475: Invalid value for argument %s: %s"
+msgstr "E475: 参数 %s 的值无效:%s"
+
+#: ../globals.h:884
+#, c-format
+msgid "E983: Duplicate argument: %s"
+msgstr "E983: 重复的参数:%s"
+
+#: ../globals.h:885
#, c-format
msgid "E15: Invalid expression: %s"
msgstr "E15: 无效的表达式: %s"
-#: ../globals.h:1017
+#: ../globals.h:886
msgid "E16: Invalid range"
msgstr "E16: 无效的范围"
-#: ../globals.h:1018
+#: ../globals.h:887
msgid "E476: Invalid command"
msgstr "E476: 无效的命令"
-#: ../globals.h:1019
+#: ../globals.h:888
#, c-format
msgid "E17: \"%s\" is a directory"
msgstr "E17: \"%s\" 是目录"
-#: ../globals.h:1020
-#, fuzzy
-msgid "E900: Invalid job id"
-msgstr "E49: 无效的滚动大小"
+#: ../globals.h:889
+msgid "E756: Spell checking is not possible"
+msgstr "E756: 拼写检查不可能"
-#: ../globals.h:1021
+#: ../globals.h:890
+msgid "E900: Invalid channel id"
+msgstr "E900: channel id 无效"
+
+#: ../globals.h:891
+msgid "E900: Invalid channel id: not a job"
+msgstr "E900: channel id 无效:不是任务"
+
+#: ../globals.h:892
msgid "E901: Job table is full"
-msgstr "E901: 任务表已经满"
+msgstr "E901: 任务表已满"
+
+#: ../globals.h:893
+#, c-format
+msgid "E903: Process failed to start: %s: \"%s\""
+msgstr "E903: 进程启动失败:%s:\"%s\""
+
+#: ../globals.h:894
+msgid "E904: channel is not a pty"
+msgstr "E664: channel 不是 pty"
+
+#: ../globals.h:895
+#, c-format
+msgid "E905: Couldn't open stdio channel: %s"
+msgstr "E905: 无法打开 stdio channel:%s"
+
+#: ../globals.h:896
+msgid "E906: invalid stream for channel"
+msgstr "E906: stream 对 channel 无效"
+
+#: ../globals.h:897
+msgid "E906: invalid stream for rpc channel, use 'rpc'"
+msgstr "E906: stream 对 rpc channel 无效,请使用 'rpc'"
+
+#: ../globals.h:898
+#, c-format
+msgid ""
+"E5210: dict key '%s' already set for buffered stream in channel %<PRIu64>"
+msgstr ""
-#: ../globals.h:1024
+#: ../globals.h:899
#, c-format
msgid "E364: Library call failed for \"%s()\""
msgstr "E364: 调用函数库 \"%s()\" 失败"
-#: ../globals.h:1026
+#: ../globals.h:900
+#, c-format
+msgid "E667: Fsync failed: %s"
+msgstr "E667: Fsync 失败:%s"
+
+#: ../globals.h:901
+#, c-format
+msgid "E739: Cannot create directory %s: %s"
+msgstr "E739: 无法创建目录 %s:%s"
+
+#: ../globals.h:902
msgid "E19: Mark has invalid line number"
msgstr "E19: 标记的行号无效"
-#: ../globals.h:1027
+#: ../globals.h:903
msgid "E20: Mark not set"
msgstr "E20: 没有设定标记"
-#: ../globals.h:1029
+#: ../globals.h:904
msgid "E21: Cannot make changes, 'modifiable' is off"
msgstr "E21: 不能修改,因为选项 'modifiable' 是关的"
-#: ../globals.h:1030
+#: ../globals.h:905
msgid "E22: Scripts nested too deep"
msgstr "E22: 脚本嵌套过深"
-#: ../globals.h:1031
+#: ../globals.h:906
msgid "E23: No alternate file"
msgstr "E23: 没有交替文件"
-#: ../globals.h:1032
+#: ../globals.h:907
msgid "E24: No such abbreviation"
msgstr "E24: 没有这个缩写"
-#: ../globals.h:1033
+#: ../globals.h:908
msgid "E477: No ! allowed"
msgstr "E477: 不能使用 \"!\""
-#: ../globals.h:1035
-msgid "E25: Nvim does not have a built-in GUI"
-msgstr "E25: 无法使用图形界面: 编译时没有启用"
-
-#: ../globals.h:1036
+#: ../globals.h:909
#, c-format
msgid "E28: No such highlight group name: %s"
msgstr "E28: 没有这个高亮群组名: %s"
-#: ../globals.h:1037
+#: ../globals.h:910
msgid "E29: No inserted text yet"
msgstr "E29: 没有插入过文字"
-#: ../globals.h:1038
+#: ../globals.h:911
msgid "E30: No previous command line"
msgstr "E30: 没有前一个命令行"
-#: ../globals.h:1039
+#: ../globals.h:912
msgid "E31: No such mapping"
msgstr "E31: 没有这个映射"
-#: ../globals.h:1040
+#: ../globals.h:913
msgid "E479: No match"
msgstr "E479: 没有匹配"
-#: ../globals.h:1041
+#: ../globals.h:914
#, c-format
msgid "E480: No match: %s"
msgstr "E480: 没有匹配: %s"
-#: ../globals.h:1042
+#: ../globals.h:915
msgid "E32: No file name"
msgstr "E32: 没有文件名"
-#: ../globals.h:1044
+#: ../globals.h:916
msgid "E33: No previous substitute regular expression"
msgstr "E33: 没有前一个替换正则表达式"
-#: ../globals.h:1045
+#: ../globals.h:917
msgid "E34: No previous command"
msgstr "E34: 没有前一个命令"
-#: ../globals.h:1046
+#: ../globals.h:918
msgid "E35: No previous regular expression"
msgstr "E35: 没有前一个正则表达式"
-#: ../globals.h:1047
+#: ../globals.h:919
msgid "E481: No range allowed"
msgstr "E481: 不能使用范围"
-#: ../globals.h:1048
+#: ../globals.h:920
msgid "E36: Not enough room"
msgstr "E36: 没有足够的空间"
-#: ../globals.h:1049
-#, c-format
-msgid "E482: Can't create file %s"
-msgstr "E482: 无法创建文件 %s"
-
-#: ../globals.h:1050
+#: ../globals.h:921
msgid "E483: Can't get temp file name"
msgstr "E483: 无法获取临时文件名"
-#: ../globals.h:1051
+#: ../globals.h:922
#, c-format
msgid "E484: Can't open file %s"
msgstr "E484: 无法打开文件 %s"
-#: ../globals.h:1052
+#: ../globals.h:923
+#, c-format
+msgid "E484: Can't open file %s: %s"
+msgstr "E484: 无法打开文件 %s:%s"
+
+#: ../globals.h:924
#, c-format
msgid "E485: Can't read file %s"
msgstr "E485: 无法读取文件 %s"
-#: ../globals.h:1054
-msgid "E37: No write since last change (add ! to override)"
-msgstr "E37: 已修改但尚未保存 (可用 ! 强制执行)"
-
-#: ../globals.h:1055
-#, fuzzy
-msgid "E37: No write since last change"
-msgstr "[已修改但尚未保存]\n"
-
-#: ../globals.h:1056
+#: ../globals.h:925
msgid "E38: Null argument"
msgstr "E38: 空的参数"
-#: ../globals.h:1057
+#: ../globals.h:926
msgid "E39: Number expected"
msgstr "E39: 此处需要数字"
-#: ../globals.h:1058
+#: ../globals.h:927
#, c-format
msgid "E40: Can't open errorfile %s"
msgstr "E40: 无法打开错误文件 %s"
-#: ../globals.h:1059
+#: ../globals.h:928
msgid "E41: Out of memory!"
msgstr "E41: 内存不足!"
-#: ../globals.h:1060
+#: ../globals.h:929
msgid "Pattern not found"
msgstr "找不到模式"
-#: ../globals.h:1061
+#: ../globals.h:930
#, c-format
msgid "E486: Pattern not found: %s"
msgstr "E486: 找不到模式: %s"
-#: ../globals.h:1062
+#: ../globals.h:931
msgid "E487: Argument must be positive"
msgstr "E487: 参数必须是正数"
-#: ../globals.h:1064
+#: ../globals.h:932
msgid "E459: Cannot go back to previous directory"
msgstr "E459: 无法回到前一个目录"
-#: ../globals.h:1066
+#: ../globals.h:934
msgid "E42: No Errors"
msgstr "E42: 没有错误"
-#: ../globals.h:1067
+#: ../globals.h:935
msgid "E776: No location list"
-msgstr "E776: 没有 location 列表"
+msgstr "E776: 没有位置列表"
-#: ../globals.h:1068
+#: ../globals.h:936
msgid "E43: Damaged match string"
msgstr "E43: 已损坏的匹配字符串"
-#: ../globals.h:1069
+#: ../globals.h:937
msgid "E44: Corrupted regexp program"
msgstr "E44: 已损坏的正则表达式程序"
-#: ../globals.h:1071
+#: ../globals.h:938
msgid "E45: 'readonly' option is set (add ! to override)"
msgstr "E45: 已设定选项 'readonly' (请加 ! 强制执行)"
-#: ../globals.h:1073
+#: ../globals.h:939
+#, c-format
+msgid "E734: Wrong variable type for %s="
+msgstr "E734: %s= 的变量类型不正确"
+
+#: ../globals.h:940
#, c-format
-msgid "E46: Cannot change read-only variable \"%s\""
-msgstr "E46: 不能改变只读变量 \"%s\""
+msgid "E461: Illegal variable name: %s"
+msgstr "E461: 变量名无效: %s"
-#: ../globals.h:1075
-#, fuzzy, c-format
-msgid "E794: Cannot set variable in the sandbox: \"%s\""
-msgstr "E46: 不能在 sandbox 中设定变量: \"%s\""
+#: ../globals.h:941
+msgid "E995: Cannot modify existing variable"
+msgstr "E995: 不能修改已有变量"
-#: ../globals.h:1076
+#: ../globals.h:942
+#, c-format
+msgid "E46: Cannot change read-only variable \"%.*s\""
+msgstr "E46: 不能改变只读变量 \"%.*s\""
+
+#: ../globals.h:943
+msgid "E928: String required"
+msgstr "E928: 需要字符串"
+
+#: ../globals.h:944
+msgid "E715: Dictionary required"
+msgstr "E715: 需要字典"
+
+#: ../globals.h:945
+#, c-format
+msgid "E979: Blob index out of range: %<PRId64>"
+msgstr "E979: Blob 索引超出范围:%<PRId64>"
+
+#: ../globals.h:946
+msgid "E978: Invalid operation for Blob"
+msgstr "E978: 操作对 Blob 无效"
+
+#: ../globals.h:947
+#, c-format
+msgid "E118: Too many arguments for function: %s"
+msgstr "E118: 函数 %s 的参数过多"
+
+#: ../globals.h:948
+#, c-format
+msgid "E716: Key not present in Dictionary: \"%s\""
+msgstr "E716: 字典中不存在键:\"%s\""
+
+#: ../globals.h:949
+msgid "E714: List required"
+msgstr "E714: 需要列表"
+
+#: ../globals.h:950
+msgid "E897: List or Blob required"
+msgstr "E897: 需要列表或 Blob"
+
+#: ../globals.h:951
+#, c-format
+msgid "E712: Argument of %s must be a List or Dictionary"
+msgstr "E712: %s 的参数必须是 List 或者 Dictionary"
+
+#: ../globals.h:952
+#, c-format
+msgid "E896: Argument of %s must be a List, Dictionary or Blob"
+msgstr "E896: %s 的参数必须是列表、字典或 Blob"
+
+#: ../globals.h:953
msgid "E47: Error while reading errorfile"
msgstr "E47: 读取错误文件失败"
-#: ../globals.h:1078
+#: ../globals.h:954
msgid "E48: Not allowed in sandbox"
msgstr "E48: 不允许在 sandbox 中使用"
-#: ../globals.h:1080
+#: ../globals.h:955
msgid "E523: Not allowed here"
msgstr "E523: 不允许在此使用"
-#: ../globals.h:1082
+#: ../globals.h:956
+msgid "E565: Not allowed to change text or change window"
+msgstr "E565: 不允许改变文本或窗口"
+
+#: ../globals.h:957
msgid "E359: Screen mode setting not supported"
msgstr "E359: 不支持设定屏幕模式"
-#: ../globals.h:1083
+#: ../globals.h:958
msgid "E49: Invalid scroll size"
-msgstr "E49: 无效的滚动大小"
+msgstr "E49: 滚动大小无效"
-#: ../globals.h:1084
+#: ../globals.h:959
msgid "E91: 'shell' option is empty"
-msgstr "E91: 选项 'shell' 为空"
+msgstr "E91: 'shell' 选项为空"
-#: ../globals.h:1085
+#: ../globals.h:960
msgid "E255: Couldn't read in sign data!"
msgstr "E255: 无法读取 sign 数据!"
-#: ../globals.h:1086
+#: ../globals.h:961
msgid "E72: Close error on swap file"
msgstr "E72: 交换文件关闭错误"
-#: ../globals.h:1087
+#: ../globals.h:962
msgid "E73: tag stack empty"
msgstr "E73: tag 堆栈为空"
-#: ../globals.h:1088
+#: ../globals.h:963
msgid "E74: Command too complex"
-msgstr "E74: 命令过复杂"
+msgstr "E74: 命令过于复杂"
-#: ../globals.h:1089
+#: ../globals.h:964
msgid "E75: Name too long"
msgstr "E75: 名字过长"
-#: ../globals.h:1090
+#: ../globals.h:965
msgid "E76: Too many ["
msgstr "E76: [ 过多"
-#: ../globals.h:1091
+#: ../globals.h:966
msgid "E77: Too many file names"
msgstr "E77: 文件名过多"
-#: ../globals.h:1092
+#: ../globals.h:967
msgid "E488: Trailing characters"
msgstr "E488: 多余的尾部字符"
-#: ../globals.h:1093
+#: ../globals.h:968
+#, c-format
+msgid "E488: Trailing characters: %s"
+msgstr "E488: 多余的尾部字符:%s"
+
+#: ../globals.h:969
msgid "E78: Unknown mark"
msgstr "E78: 未知的标记"
-#: ../globals.h:1094
+#: ../globals.h:970
msgid "E79: Cannot expand wildcards"
msgstr "E79: 无法扩展通配符"
-#: ../globals.h:1096
+#: ../globals.h:971
msgid "E591: 'winheight' cannot be smaller than 'winminheight'"
msgstr "E591: 'winheight' 不能小于 'winminheight'"
-#: ../globals.h:1098
+#: ../globals.h:972
msgid "E592: 'winwidth' cannot be smaller than 'winminwidth'"
msgstr "E592: 'winwidth' 不能小于 'winminwidth'"
-#: ../globals.h:1099
+#: ../globals.h:973
msgid "E80: Error while writing"
msgstr "E80: 写入出错"
-#: ../globals.h:1100
-msgid "Zero count"
-msgstr "计数为零"
+#: ../globals.h:974
+msgid "E939: Positive count required"
+msgstr "E939: 需要正的计数"
-#: ../globals.h:1101
+#: ../globals.h:975
msgid "E81: Using <SID> not in a script context"
msgstr "E81: 在脚本环境外使用了 <SID>"
-#: ../globals.h:1102
+#: ../globals.h:976
#, c-format
-msgid "E685: Internal error: %s"
-msgstr "E685: 内部错误: %s"
+msgid "E107: Missing parentheses: %s"
+msgstr "E107: 缺少括号:%s"
-#: ../globals.h:1104
+#: ../globals.h:977
msgid "E363: pattern uses more memory than 'maxmempattern'"
msgstr "E363: 表达式的内存使用超出 'maxmempattern'"
-#: ../globals.h:1105
+#: ../globals.h:978
msgid "E749: empty buffer"
msgstr "E749: 空的缓冲区"
-#: ../globals.h:1108
+#: ../globals.h:979
+#, c-format
+msgid "E86: Buffer %<PRId64> does not exist"
+msgstr "E86: 缓冲区 %<PRId64> 不存在"
+
+#: ../globals.h:981
msgid "E682: Invalid search pattern or delimiter"
msgstr "E682: 无效的搜索表达式或分隔符"
-#: ../globals.h:1109
+#: ../globals.h:982
msgid "E139: File is loaded in another buffer"
msgstr "E139: 文件已在另一个缓冲区中被加载"
-#: ../globals.h:1110
+#: ../globals.h:983
#, c-format
msgid "E764: Option '%s' is not set"
msgstr "E764: 没有设定选项 '%s'"
-#: ../globals.h:1111
-#, fuzzy
+#: ../globals.h:984
msgid "E850: Invalid register name"
-msgstr "E354: 无效的寄存器名: '%s'"
+msgstr "E850: 寄存器名无效"
+
+#: ../globals.h:985
+#, c-format
+msgid "E919: Directory not found in '%s': \"%s\""
+msgstr "E919: '%s' 中未找到目录:\"%s\""
+
+#: ../globals.h:986
+msgid "E952: Autocommand caused recursive behavior"
+msgstr "E952: 自动命令导致了递归行为"
+
+#: ../globals.h:987
+msgid "E328: Menu only exists in another mode"
+msgstr "E328: 菜单只在其它模式中存在"
+
+#: ../globals.h:988
+msgid "E813: Cannot close autocmd window"
+msgstr "E813: 不能关闭自动命令窗口"
-#: ../globals.h:1114
+#: ../globals.h:989
+#, c-format
+msgid "E686: Argument of %s must be a List"
+msgstr "E686: %s 的参数必须是列表"
+
+#: ../globals.h:990
+msgid "E519: Option not supported"
+msgstr "E519: 不支持该选项"
+
+#: ../globals.h:991
+msgid "E856: Filename too long"
+msgstr "E856: 文件名过长"
+
+#: ../globals.h:992
+msgid "E806: using Float as a String"
+msgstr "E806: 将浮点数当作字符串使用"
+
+#: ../globals.h:993
+msgid "E788: Not allowed to edit another buffer now"
+msgstr "E788: 目前不允许编辑别的缓冲区"
+
+#: ../globals.h:995
+#, c-format
+msgid "E5500: autocmd has thrown an exception: %s"
+msgstr "E5500: 自动命令抛出了异常:%s"
+
+#: ../globals.h:996
+msgid "E5520: <Cmd> mapping must end with <CR>"
+msgstr "E5520: <Cmd> 映射必须以 <CR> 结束"
+
+#: ../globals.h:998
+msgid "E5521: <Cmd> mapping must end with <CR> before second <Cmd>"
+msgstr ""
+
+#: ../globals.h:1000
+#, c-format
+msgid "E5555: API call: %s"
+msgstr ""
+
+#: ../globals.h:1002
+#, c-format
+msgid "E5560: %s must not be called in a lua loop callback"
+msgstr "E5560: Lua 循环回调中不能调用 %s"
+
+#: ../globals.h:1004
+msgid "E5601: Cannot close window, only floating window would remain"
+msgstr "E5601: 无法关闭窗口,不然就只剩下浮动窗口了"
+
+#: ../globals.h:1005
+msgid "E5602: Cannot exchange or rotate float"
+msgstr ""
+
+#: ../globals.h:1007
+msgid "E1155: Cannot define autocommands for ALL events"
+msgstr "E1155: 不能对所有事件定义自动命令"
+
+#: ../globals.h:1009
+msgid "E1240: Resulting text too long"
+msgstr "E1240: 得到的文本过长"
+
+#: ../globals.h:1011
+msgid "E1247: Line number out of range"
+msgstr "E1247: 行号超出范围"
+
+#: ../globals.h:1013
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: 组名中含有无效字符"
+
+#: ../globals.h:1015
+msgid "E1249: Highlight group name too long"
+msgstr "E1249: 高亮组名称过长"
+
+#: ../globals.h:1018
+msgid "E5767: Cannot use :undo! to redo or move to a different undo branch"
+msgstr ""
+
+#: ../globals.h:1020
msgid "search hit TOP, continuing at BOTTOM"
msgstr "已查找到文件开头,再从结尾继续查找"
-#: ../globals.h:1115
+#: ../globals.h:1021
msgid "search hit BOTTOM, continuing at TOP"
msgstr "已查找到文件结尾,再从开头继续查找"
-#: ../hardcopy.c:240
-msgid "E550: Missing colon"
-msgstr "E550: 缺少冒号"
-
-#: ../hardcopy.c:252
-msgid "E551: Illegal component"
-msgstr "E551: 无效的部分"
+#: ../globals.h:1023
+msgid " line "
+msgstr " 行 "
-#: ../hardcopy.c:259
-msgid "E552: digit expected"
-msgstr "E552: 应该要有数字"
+#: ../help.c:79
+msgid "E478: Don't panic!"
+msgstr "E478: 不要慌!"
-#: ../hardcopy.c:473
+#: ../help.c:120
#, c-format
-msgid "Page %d"
-msgstr "第 %d 页"
+msgid "E661: Sorry, no '%s' help for %s"
+msgstr "E661: 抱歉,没有 '%s' 的 %s 的说明"
-#: ../hardcopy.c:597
-msgid "No text to be printed"
-msgstr "没有要打印的文字"
+#: ../help.c:122
+#, c-format
+msgid "E149: Sorry, no help for %s"
+msgstr "E149: 抱歉,没有 %s 的说明"
-#: ../hardcopy.c:668
+#: ../help.c:154
#, c-format
-msgid "Printing page %d (%d%%)"
-msgstr "正在打印第 %d 页 (%d%%)"
+msgid "Sorry, help file \"%s\" not found"
+msgstr "抱歉,找不到帮助文件 \"%s\""
-#: ../hardcopy.c:680
+#: ../help.c:900 ../help.c:1094
#, c-format
-msgid " Copy %d of %d"
-msgstr "复制 %d / %d"
+msgid "E151: No match: %s"
+msgstr "E151: 没有匹配:%s"
-#: ../hardcopy.c:733
+#: ../help.c:920
#, c-format
-msgid "Printed: %s"
-msgstr "已打印: %s"
+msgid "E152: Cannot open %s for writing"
+msgstr "E152: 无法打开并写入 %s"
-#: ../hardcopy.c:740
-msgid "Printing aborted"
-msgstr "打印中止"
+#: ../help.c:941
+#, c-format
+msgid "E153: Unable to open %s for reading"
+msgstr "E153: 无法打开并读取 %s"
-#: ../hardcopy.c:1365
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: 写入 PostScript 输出文件出错"
+#: ../help.c:969
+#, c-format
+msgid "E670: Mix of help file encodings within a language: %s"
+msgstr "E670: 在一种语言中混合了多种帮助文件编码: %s"
-#: ../hardcopy.c:1747
+#: ../help.c:1026
#, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E624: 无法打开文件 \"%s\""
+msgid "E154: Duplicate tag \"%s\" in file %s/%s"
+msgstr "E154: Tag \"%s\" 在文件 %s/%s 中重复出现"
-#: ../hardcopy.c:1756 ../hardcopy.c:2470
+#: ../help.c:1185
#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: 无法读取 PostScript 资源文件 \"%s\""
+msgid "E150: Not a directory: %s"
+msgstr "E150: 不是目录: %s"
+
+#: ../highlight.c:95
+msgid "E424: Too many different highlighting attributes in use"
+msgstr "E424: 使用了太多不同的高亮度属性"
-#: ../hardcopy.c:1772
+#: ../highlight_group.c:910
#, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E618: 文件 \"%s\" 不是 PostScript 资源文件"
+msgid "E411: highlight group not found: %s"
+msgstr "E411: 找不到 highlight group: %s"
-#: ../hardcopy.c:1788 ../hardcopy.c:1805 ../hardcopy.c:1844
+#: ../highlight_group.c:933
#, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr "E619: 文件 \"%s\" 不是已支持的 PostScript 资源文件"
+msgid "E412: Not enough arguments: \":highlight link %s\""
+msgstr "E412: 参数太少: \":highlight link %s\""
-#: ../hardcopy.c:1856
+#: ../highlight_group.c:939
#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr "E621: \"%s\" 资源文件版本不正确"
+msgid "E413: Too many arguments: \":highlight link %s\""
+msgstr "E413: 参数过多: \":highlight link %s\""
-#: ../hardcopy.c:2225
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr "E673: 不兼容的多字节编码和字符集。"
+#: ../highlight_group.c:966
+msgid "E414: group has settings, highlight link ignored"
+msgstr "E414: 已设定组, 忽略 highlight link"
-#: ../hardcopy.c:2238
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr "E674: printmbcharset 在多字节编码下不能为空。"
+#: ../highlight_group.c:1039
+#, c-format
+msgid "E415: unexpected equal sign: %s"
+msgstr "E415: 不该有的等号: %s"
+
+#: ../highlight_group.c:1051 ../highlight_group.c:1099
+msgid "E423: Illegal argument"
+msgstr "E423: 无效的参数"
-#: ../hardcopy.c:2254
-msgid "E675: No default font specified for multi-byte printing."
-msgstr "E675: 没有指定多字节打印的默认字体。"
+#: ../highlight_group.c:1072
+#, c-format
+msgid "E416: missing equal sign: %s"
+msgstr "E416: 缺少等号: %s"
-#: ../hardcopy.c:2426
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: 无法打开 PostScript 输出文件"
+#: ../highlight_group.c:1093
+#, c-format
+msgid "E417: missing argument: %s"
+msgstr "E417: 缺少参数: %s"
-#: ../hardcopy.c:2458
+#: ../highlight_group.c:1127
#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: 无法打开文件 \"%s\""
+msgid "E418: Illegal value: %s"
+msgstr "E418: 不合法的值: %s"
-#: ../hardcopy.c:2583
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E456: 找不到 PostScript 资源文件 \"prolog.ps\""
+#: ../highlight_group.c:1175
+msgid "E419: FG color unknown"
+msgstr "E419: 错误的前景颜色"
-#: ../hardcopy.c:2593
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr "E456: 找不到 PostScript 资源文件 \"cidfont.ps\""
+#: ../highlight_group.c:1183
+msgid "E420: BG color unknown"
+msgstr "E420: 错误的背景颜色"
-#: ../hardcopy.c:2622 ../hardcopy.c:2639 ../hardcopy.c:2665
+#: ../highlight_group.c:1198
#, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E456: 找不到 PostScript 资源文件 \"%s.ps\""
+msgid "E421: Color name or number not recognized: %s"
+msgstr "E421: 错误的颜色名称或数值: %s"
-#: ../hardcopy.c:2654
+#: ../highlight_group.c:1330
#, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr "E620: 无法转换至打印编码 \"%s\""
+msgid "E423: Illegal argument: %s"
+msgstr "E423: 无效的参数: %s"
+
+#: ../highlight_group.c:1845
+msgid "E669: Unprintable character in group name"
+msgstr "E669: 组名中存在不可显示字符"
-#: ../hardcopy.c:2877
-msgid "Sending to printer..."
-msgstr "发送到打印机……"
+#: ../highlight_group.c:1872
+msgid "E849: Too many highlight and syntax groups"
+msgstr "E849: 高亮和语法组过多"
-#: ../hardcopy.c:2881
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: 无法打印 PostScript 文件"
+#: ../input.c:232
+msgid "Type number and <Enter> or click with the mouse (q or empty cancels): "
+msgstr "输入数字并按 <Enter> 或点击鼠标(q 或空取消):"
-#: ../hardcopy.c:2883
-msgid "Print job sent."
-msgstr "打印任务已被发送。"
+#: ../input.c:235
+msgid "Type number and <Enter> (q or empty cancels): "
+msgstr "输入数字并按 <Enter>(q 或空取消):"
-#: ../if_cscope.c:85
-msgid "Add a new database"
-msgstr "添加一个新的数据库"
+#: ../insexpand.c:99
+msgid " Keyword completion (^N^P)"
+msgstr " 关键字补全 (^N^P)"
-#: ../if_cscope.c:87
-msgid "Query for a pattern"
-msgstr "查询一个模式"
+#. CTRL_X_NORMAL, ^P/^N compl.
+#: ../insexpand.c:100
+msgid " ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"
+msgstr " ^X 模式 (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"
-#: ../if_cscope.c:89
-msgid "Show this message"
-msgstr "显示此信息"
+#. CTRL_X_SCROLL: depends on state
+#: ../insexpand.c:102
+msgid " Whole line completion (^L^N^P)"
+msgstr " 整行补全 (^L^N^P)"
-#: ../if_cscope.c:91
-msgid "Kill a connection"
-msgstr "结束一个连接"
+#: ../insexpand.c:103
+msgid " File name completion (^F^N^P)"
+msgstr " 文件名补全 (^F^N^P)"
-#: ../if_cscope.c:93
-msgid "Reinit all connections"
-msgstr "重置所有连接"
+#: ../insexpand.c:104
+msgid " Tag completion (^]^N^P)"
+msgstr " Tag 补全 (^]^N^P)"
-#: ../if_cscope.c:95
-msgid "Show connections"
-msgstr "显示连接"
+#: ../insexpand.c:105
+msgid " Path pattern completion (^N^P)"
+msgstr " 头文件模式补全 (^N^P)"
-#: ../if_cscope.c:101
-#, c-format
-msgid "E560: Usage: cs[cope] %s"
-msgstr "E560: 用法: cs[cope] %s"
+#: ../insexpand.c:106
+msgid " Definition completion (^D^N^P)"
+msgstr " 定义补全 (^D^N^P)"
-#: ../if_cscope.c:225
-msgid "This cscope command does not support splitting the window.\n"
-msgstr "这个 cscope 命令不支持分割窗口。\n"
+#. CTRL_X_FINISHED
+#: ../insexpand.c:108
+msgid " Dictionary completion (^K^N^P)"
+msgstr " Dictionary 补全 (^K^N^P)"
-#: ../if_cscope.c:266
-msgid "E562: Usage: cstag <ident>"
-msgstr "E562: 用法: cstag <ident>"
+#: ../insexpand.c:109
+msgid " Thesaurus completion (^T^N^P)"
+msgstr " Thesaurus 补全 (^T^N^P)"
-#: ../if_cscope.c:313
-msgid "E257: cstag: tag not found"
-msgstr "E257: cstag: 找不到 tag"
+#. CTRL_X_EVAL doesn't use msg.
+#: ../insexpand.c:110 ../insexpand.c:116
+msgid " Command-line completion (^V^N^P)"
+msgstr " 命令行补全 (^V^N^P)"
-#: ../if_cscope.c:461
-#, c-format
-msgid "E563: stat(%s) error: %d"
-msgstr "E563: stat(%s) 错误: %d"
+#: ../insexpand.c:111
+msgid " User defined completion (^U^N^P)"
+msgstr " 用户自定义补全 (^U^N^P)"
-#: ../if_cscope.c:551
-#, c-format
-msgid "E564: %s is not a directory or a valid cscope database"
-msgstr "E564: %s 不是目录或有效的 cscope 数据库"
+#: ../insexpand.c:112
+msgid " Omni completion (^O^N^P)"
+msgstr " 全能补全 (^O^N^P)"
-#: ../if_cscope.c:566
-#, c-format
-msgid "Added cscope database %s"
-msgstr "添加了 cscope 数据库 %s"
+#: ../insexpand.c:113
+msgid " Spelling suggestion (s^N^P)"
+msgstr " 拼写建议 (s^N^P)"
+
+#: ../insexpand.c:114
+msgid " Keyword Local completion (^N^P)"
+msgstr " 关键字局部补全 (^N^P)"
-#: ../if_cscope.c:616
+#: ../insexpand.c:190
+msgid "Hit end of paragraph"
+msgstr "已到段落结尾"
+
+#: ../insexpand.c:191
+msgid "E840: Completion function deleted text"
+msgstr "E840: 补全函数删除了文本"
+
+#: ../insexpand.c:469
+msgid "'dictionary' option is empty"
+msgstr "选项 'dictionary' 为空"
+
+#: ../insexpand.c:470
+msgid "'thesaurus' option is empty"
+msgstr "选项 'thesaurus' 为空"
+
+#: ../insexpand.c:1456
#, c-format
-msgid "E262: error reading cscope connection %<PRId64>"
-msgstr "E262: 读取 cscope 连接 %<PRId64> 出错"
+msgid "Scanning dictionary: %s"
+msgstr "正在扫描 dictionary: %s"
-#: ../if_cscope.c:711
-msgid "E561: unknown cscope search type"
-msgstr "E561: 未知的 cscope 查找类型"
+#: ../insexpand.c:1854
+msgid " (insert) Scroll (^E/^Y)"
+msgstr " (插入) Scroll (^E/^Y)"
-#: ../if_cscope.c:752 ../if_cscope.c:789
-msgid "E566: Could not create cscope pipes"
-msgstr "E566: 无法创建 cscope 管道"
+#: ../insexpand.c:1856
+msgid " (replace) Scroll (^E/^Y)"
+msgstr " (替换) Scroll (^E/^Y)"
-#: ../if_cscope.c:767
-msgid "E622: Could not fork for cscope"
-msgstr "E622: 无法对 cscope 进行 fork"
+#: ../insexpand.c:2600
+msgid "E785: complete() can only be used in Insert mode"
+msgstr "E785: complete() 只能在插入模式中使用"
-#: ../if_cscope.c:849
-#, fuzzy
-msgid "cs_create_connection setpgid failed"
-msgstr "cs_create_connection 执行失败"
+#. reset in msg_trunc_attr()
+#: ../insexpand.c:2881
+#, c-format
+msgid "Scanning: %s"
+msgstr "正在扫描: %s"
-#: ../if_cscope.c:853 ../if_cscope.c:889
-msgid "cs_create_connection exec failed"
-msgstr "cs_create_connection 执行失败"
+#. reset in msg_trunc_attr()
+#: ../insexpand.c:2912
+msgid "Scanning tags."
+msgstr "扫描标签."
-#: ../if_cscope.c:863 ../if_cscope.c:902
-msgid "cs_create_connection: fdopen for to_fp failed"
-msgstr "cs_create_connection: fdopen to_fp 失败"
+#: ../insexpand.c:3465
+msgid "match in file"
+msgstr "在文件中匹配"
-#: ../if_cscope.c:865 ../if_cscope.c:906
-msgid "cs_create_connection: fdopen for fr_fp failed"
-msgstr "cs_create_connection: fdopen fr_fp 失败"
+#: ../insexpand.c:4198
+msgid " Adding"
+msgstr " 增加"
+
+#. showmode might reset the internal line pointers, so it must
+#. be called before line = ml_get(), or when this address is no
+#. longer needed. -- Acevedo.
+#: ../insexpand.c:4243
+msgid "-- Searching..."
+msgstr "-- 查找中..."
-#: ../if_cscope.c:890
-msgid "E623: Could not spawn cscope process"
-msgstr "E623: 无法生成 cscope 进程"
+#: ../insexpand.c:4263
+msgid "Back at original"
+msgstr "回到起点"
+
+#: ../insexpand.c:4266
+msgid "Word from other line"
+msgstr "另一行的词"
-#: ../if_cscope.c:932
-msgid "E567: no cscope connections"
-msgstr "E567: 没有 cscope 连接"
+#: ../insexpand.c:4269
+msgid "The only match"
+msgstr "唯一匹配"
-#: ../if_cscope.c:1009
+#: ../insexpand.c:4287
#, c-format
-msgid "E469: invalid cscopequickfix flag %c for %c"
-msgstr "E469: cscopequickfix 标志 %c 对 %c 无效"
+msgid "match %d of %d"
+msgstr "匹配 %d / %d"
-#: ../if_cscope.c:1058
+#: ../insexpand.c:4291
#, c-format
-msgid "E259: no matches found for cscope query %s of %s"
-msgstr "E259: cscope 查询 %s %s 没有找到匹配的结果"
+msgid "match %d"
+msgstr "匹配 %d"
-#: ../if_cscope.c:1142
-msgid "cscope commands:\n"
-msgstr "cscope 命令:\n"
+#: ../locale.c:227
+#, c-format
+msgid "Current %slanguage: \"%s\""
+msgstr "当前的 %s语言: \"%s\""
-#: ../if_cscope.c:1150
-#, fuzzy, c-format
-msgid "%-5s: %s%*s (Usage: %s)"
-msgstr "%-5s: %-30s (用法: %s)"
+#: ../locale.c:243
+#, c-format
+msgid "E197: Cannot set language to \"%s\""
+msgstr "E197: 不能设定语言为 \"%s\""
+
+#: ../lua/converter.c:68 ../lua/converter.c:204 ../lua/converter.c:621
+#, c-format
+msgid "E1502: Lua failed to grow stack to %i"
+msgstr ""
-#: ../if_cscope.c:1155
+#: ../lua/converter.c:380
msgid ""
-"\n"
-" a: Find assignments to this symbol\n"
-" c: Find functions calling this function\n"
-" d: Find functions called by this function\n"
-" e: Find this egrep pattern\n"
-" f: Find this file\n"
-" g: Find this definition\n"
-" i: Find files #including this file\n"
-" s: Find this C symbol\n"
-" t: Find this text string\n"
+"E5100: Cannot convert given lua table: table should either have a sequence "
+"of positive integer keys or contain only string keys"
msgstr ""
-"\n"
-" a: 搜索对此符号的赋值\n"
-" c: 搜索调用此函数的函数\n"
-" d: 搜索此函数调用的函数\n"
-" e: 搜索此 egrep 模式\n"
-" f: 搜索此文件\n"
-" g: 搜索此定义\n"
-" i: 搜索包含此文件的文件\n"
-" s: 搜索此 C 符号\n"
-" t: 搜索此文本字符串\n"
-#: ../if_cscope.c:1226
-msgid "E568: duplicate cscope database not added"
-msgstr "E568: 重复的 cscope 数据库未被加入"
+#: ../lua/converter.c:409 ../lua/converter.c:415
+msgid "E5101: Cannot convert given lua type"
+msgstr ""
-#: ../if_cscope.c:1335
+#: ../lua/converter.c:509 ../lua/converter.c:532
#, c-format
-msgid "E261: cscope connection %s not found"
-msgstr "E261: 找不到 cscope 连接 %s"
+msgid "E5102: Lua failed to grow stack to %i"
+msgstr ""
-#: ../if_cscope.c:1364
+#: ../lua/executor.c:333
#, c-format
-msgid "cscope connection %s closed"
-msgstr "cscope 连接 %s 已关闭"
+msgid "Error executing vim.schedule lua callback: %.*s"
+msgstr ""
-#. should not reach here
-#: ../if_cscope.c:1486
-msgid "E570: fatal error in cs_manage_matches"
-msgstr "E570: cs_manage_matches 严重错误"
+#: ../lua/executor.c:782
+msgid "E970: Failed to initialize lua interpreter\n"
+msgstr "E970: 无法初始化 Lua 解释器\n"
-#: ../if_cscope.c:1693
-#, c-format
-msgid "Cscope tag: %s"
-msgstr "Cscope tag: %s"
+#: ../lua/executor.c:787
+msgid "E970: Failed to initialize builtin lua modules\n"
+msgstr "E970: 无法初始化内置 Lua 模块\n"
-#: ../if_cscope.c:1711
-msgid ""
-"\n"
-" # line"
+#: ../lua/executor.c:972
+#, c-format
+msgid "E5114: Error while converting print argument #%i: %.*s"
msgstr ""
-"\n"
-" # 行 "
-#: ../if_cscope.c:1713
-msgid "filename / context / line\n"
-msgstr "文件名 / 上下文 / 行\n"
+#: ../lua/executor.c:1066
+#, fuzzy, c-format
+msgid "E5115: Error while loading debug string: %.*s"
+msgstr "E782: 当读取.sug 文件时错误"
+
+#: ../lua/executor.c:1068
+#, fuzzy, c-format
+msgid "E5116: Error while calling debug string: %.*s"
+msgstr "E782: 当读取.sug 文件时错误"
+
+#: ../lua/executor.c:1370
+#, fuzzy, c-format
+msgid "E5108: Error executing Lua function: %.*s"
+msgstr "E208: 写入文件 \"%s\" 出错"
+
+#: ../lua/executor.c:1390
+#, fuzzy, c-format
+msgid "E5107: Error loading lua %.*s"
+msgstr "E210: 读取文件 \"%s\" 出错"
+
+#: ../lua/executor.c:1397
+#, fuzzy, c-format
+msgid "E5108: Error executing lua %.*s"
+msgstr "E208: 写入文件 \"%s\" 出错"
-#: ../if_cscope.c:1809
+#: ../lua/executor.c:1539
#, c-format
-msgid "E609: Cscope error: %s"
-msgstr "E609: Cscope 错误: %s"
+msgid "Error executing lua callback: %.*s"
+msgstr ""
-#: ../if_cscope.c:2053
-msgid "All cscope databases reset"
-msgstr "所有 cscope 数据库已被重置"
+#: ../lua/executor.c:1605
+msgid "cannot save undo information"
+msgstr "无法保存撤销信息"
-#: ../if_cscope.c:2123
-msgid "no cscope connections\n"
-msgstr "没有 cscope 连接\n"
+#: ../lua/executor.c:1631
+#, fuzzy, c-format
+msgid "E5109: Error loading lua: %.*s"
+msgstr "E209: 关闭文件 \"%s\" 出错"
-#: ../if_cscope.c:2126
-msgid " # pid database name prepend path\n"
-msgstr " # pid 数据库名 prepend path\n"
+#: ../lua/executor.c:1641
+#, fuzzy, c-format
+msgid "E5110: Error executing lua: %.*s"
+msgstr "E210: 读取文件 \"%s\" 出错"
-#: ../main.c:144
-msgid "Unknown option argument"
-msgstr "未知的选项参数"
+#: ../lua/executor.c:1653 ../lua/executor.c:1705
+#, fuzzy, c-format
+msgid "E5111: Error calling lua: %.*s"
+msgstr "E209: 关闭文件 \"%s\" 出错"
-#: ../main.c:146
-msgid "Too many edit arguments"
-msgstr "编辑参数过多"
+#. 1
+#: ../lua/executor.c:1715
+#, fuzzy, c-format
+msgid "E5112: Error while creating lua chunk: %.*s"
+msgstr "E782: 当读取.sug 文件时错误"
+
+#: ../lua/executor.c:1726
+#, fuzzy, c-format
+msgid "E5113: Error while calling lua chunk: %.*s"
+msgstr "E782: 当读取.sug 文件时错误"
+
+#: ../lua/executor.c:1791
+#, c-format
+msgid "Error executing vim._expand_pat: %.*s"
+msgstr ""
-#: ../main.c:148
+#: ../lua/executor.c:1928
+#, c-format
+msgid "Error executing vim.on_key Lua callback: %.*s"
+msgstr ""
+
+#: ../lua/executor.c:2149
+#, c-format
+msgid "Error executing Lua callback: %.*s"
+msgstr ""
+
+#. Error messages
+#: ../main.c:126
msgid "Argument missing after"
msgstr "缺少必要的参数"
-#: ../main.c:150
+#: ../main.c:127
msgid "Garbage after option argument"
msgstr "选项参数后的内容无效"
-#: ../main.c:152
+#: ../main.c:128
+msgid "Unknown option argument"
+msgstr "未知的选项参数"
+
+#: ../main.c:129
+msgid "Too many edit arguments"
+msgstr "编辑参数过多"
+
+#: ../main.c:131
msgid "Too many \"+command\", \"-c command\" or \"--cmd command\" arguments"
msgstr "\"+command\"、\"-c command\" 或 \"--cmd command\" 参数过多"
-#: ../main.c:154
-msgid "Invalid argument for"
-msgstr "无效的参数"
-
-#: ../main.c:294
+#: ../main.c:1004
#, c-format
-msgid "%d files to edit\n"
-msgstr "还有 %d 个文件等待编辑\n"
+msgid "E5421: Failed to open stdin: %s"
+msgstr "E5421: 无法打开标准输入:%s"
-#: ../main.c:1342
-msgid "Attempt to open script file again: \""
-msgstr "试图再次打开脚本文件: \""
+#: ../main.c:1284
+#, c-format
+msgid "Attempt to open script file again: \"%s %s\"\n"
+msgstr "试图再次打开脚本文件:\"%s %s\"\n"
-#: ../main.c:1350
-msgid "Cannot open for reading: \""
-msgstr "无法打开并读取: \""
+#: ../main.c:1303
+#, c-format
+msgid "Cannot open for reading: \"%s\": %s\n"
+msgstr "无法打开并读取:\"%s\":%s\n"
-#: ../main.c:1393
+#: ../main.c:1337
msgid "Cannot open for script output: \""
msgstr "无法打开并输出脚本: \""
-#: ../main.c:1622
-msgid "Vim: Warning: Output is not to a terminal\n"
-msgstr "Vim: 警告: 输出不是到终端(屏幕)\n"
-
-#: ../main.c:1624
-msgid "Vim: Warning: Input is not from a terminal\n"
-msgstr "Vim: 警告: 输入不是来自终端(键盘)\n"
+#: ../main.c:1388
+msgid "--embed conflicts with -es/-Es"
+msgstr ""
#. just in case..
-#: ../main.c:1891
+#: ../main.c:1795
msgid "pre-vimrc command line"
msgstr "pre-vimrc 命令行"
-#: ../main.c:1964
+#: ../main.c:1916
+#, c-format
+msgid "E5422: Conflicting configs: \"%s\" \"%s\""
+msgstr ""
+
+#: ../main.c:1985
#, c-format
msgid "E282: Cannot read from \"%s\""
msgstr "E282: 无法读取 \"%s\""
-#: ../main.c:2149
+#: ../main.c:2071
+#, fuzzy
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)"
-msgstr "[文件 ..] 编辑指定的文件"
+#. kill us with CTRL-C here, if you like
+#: ../main.c:2094
+msgid "Usage:\n"
+msgstr "用法:\n"
-#: ../main.c:2179
-msgid "- read text from stdin"
-msgstr "- 从标准输入(stdin)读取文本"
+#: ../main.c:2095
+msgid " nvim [options] [file ...] Edit file(s)\n"
+msgstr " nvim [options] [文件 ..] 编辑指定的文件\n"
-#: ../main.c:2180
-msgid "-t tag edit file where tag is defined"
-msgstr "-t tag 编辑 tag 定义处的文件"
+#: ../main.c:2096
+msgid " nvim [options] -t <tag> Edit file where tag is defined\n"
+msgstr " nvim [options] -t tag 编辑 tag 定义处的文件\n"
-#: ../main.c:2181
-msgid "-q [errorfile] edit file with first error"
-msgstr "-q [errorfile] 编辑第一个出错处的文件"
+#: ../main.c:2097
+msgid " nvim [options] -q [errorfile] Edit file with first error\n"
+msgstr " nvim [options] -q [errorfile] 编辑第一个出错处的文件\n"
-#: ../main.c:2187
+#: ../main.c:2098
msgid ""
"\n"
-"\n"
-"Usage:"
+"Options:\n"
msgstr ""
"\n"
-"\n"
-"用法:"
+"选项:\n"
-#: ../main.c:2189
-msgid " vim [arguments] "
-msgstr " vim [参数] "
-
-#: ../main.c:2193
-msgid ""
-"\n"
-" or:"
-msgstr ""
-"\n"
-" 或:"
+#: ../main.c:2099
+msgid " -- Only file names after this\n"
+msgstr " -- 在这以后只有文件名\n"
-#: ../main.c:2196
-msgid ""
-"\n"
-"\n"
-"Arguments:\n"
-msgstr ""
-"\n"
-"\n"
-"参数:\n"
+#: ../main.c:2100
+msgid " + Start at end of file\n"
+msgstr " + 从文件末尾开始\n"
-#: ../main.c:2197
-msgid "--\t\t\tOnly file names after this"
-msgstr "--\t\t\t在这以后只有文件名"
+#: ../main.c:2101
+msgid " --cmd <cmd> Execute <cmd> before any config\n"
+msgstr " --cmd <cmd> 加载任何配置前执行 <cmd>\n"
-#: ../main.c:2199
-msgid "--literal\t\tDon't expand wildcards"
-msgstr "--literal\t\t不扩展通配符"
+#: ../main.c:2102
+msgid " +<cmd>, -c <cmd> Execute <cmd> after config and first file\n"
+msgstr " +<cmd>, -c <cmd> 加载第一个配置和第一个文件后执行 <cmd>\n"
-#: ../main.c:2201
-msgid "-v\t\t\tVi mode (like \"vi\")"
-msgstr "-v\t\t\tVi 模式 (同 \"vi\")"
+#: ../main.c:2104
+msgid " -b Binary mode\n"
+msgstr " -b 二进制模式\n"
-#: ../main.c:2202
-msgid "-e\t\t\tEx mode (like \"ex\")"
-msgstr "-e\t\t\tEx 模式 (同 \"ex\")"
+#: ../main.c:2105
+msgid " -d Diff mode\n"
+msgstr " -d 差异模式\n"
-#: ../main.c:2203
-msgid "-E\t\t\tImproved Ex mode"
-msgstr ""
+#: ../main.c:2106
+msgid " -e, -E Ex mode\n"
+msgstr " -e, -E Ex 模式\n"
-#: ../main.c:2204
-msgid "-s\t\t\tSilent (batch) mode (only for \"ex\")"
-msgstr "-s\t\t\t安静(批处理)模式 (只能与 \"ex\" 一起使用)"
+#: ../main.c:2107
+msgid " -es, -Es Silent (batch) mode\n"
+msgstr " -es, -Es 安静(批处理)模式\n"
-#: ../main.c:2205
-msgid "-d\t\t\tDiff mode (like \"vimdiff\")"
-msgstr "-d\t\t\tDiff 模式 (同 \"vimdiff\")"
+#: ../main.c:2108
+msgid " -h, --help Print this help message\n"
+msgstr " -h, --help 打印此帮助信息\n"
-#: ../main.c:2206
-msgid "-y\t\t\tEasy mode (like \"evim\", modeless)"
-msgstr "-y\t\t\t容易模式 (同 \"evim\",无模式)"
+#: ../main.c:2109
+msgid " -i <shada> Use this shada file\n"
+msgstr " -i <shada> 使用这个 shada 文件\n"
-#: ../main.c:2207
-msgid "-R\t\t\tReadonly mode (like \"view\")"
-msgstr "-R\t\t\t只读模式 (同 \"view\")"
+#: ../main.c:2110
+msgid " -m Modifications (writing files) not allowed\n"
+msgstr " -m 不可修改(写入文件)\n"
-#: ../main.c:2209
-msgid "-m\t\t\tModifications (writing files) not allowed"
-msgstr "-m\t\t\t不可修改(写入文件)"
+#: ../main.c:2111
+msgid " -M Modifications in text not allowed\n"
+msgstr " -M 文本不可修改\n"
-#: ../main.c:2210
-msgid "-M\t\t\tModifications in text not allowed"
-msgstr "-M\t\t\t文本不可修改"
+#: ../main.c:2112
+msgid " -n No swap file, use memory only\n"
+msgstr " -n 不使用交换文件,只使用内存\n"
-#: ../main.c:2211
-msgid "-b\t\t\tBinary mode"
-msgstr "-b\t\t\t二进制模式"
+#: ../main.c:2113
+msgid " -o[N] Open N windows (default: one per file)\n"
+msgstr " -o[N] 打开 N 个窗口(默认:每个文件一个)\n"
-#: ../main.c:2212
-msgid "-l\t\t\tLisp mode"
-msgstr "-l\t\t\tLisp 模式"
+#: ../main.c:2114
+#, fuzzy
+msgid ""
+" -O[N] Open N vertical windows (default: one per file)\n"
+msgstr "-o[N]\t\t打开 N 个窗口 (默认值: 每个文件一个)"
-#: ../main.c:2213
-msgid "-C\t\t\tCompatible with Vi: 'compatible'"
-msgstr "-C\t\t\t兼容传统的 Vi: 'compatible'"
+#: ../main.c:2115
+msgid " -p[N] Open N tab pages (default: one per file)\n"
+msgstr " -p[N] 打开 N 个标签页(默认:每个文件一个)\n"
-#: ../main.c:2214
-msgid "-N\t\t\tNot fully Vi compatible: 'nocompatible'"
-msgstr "-N\t\t\t不完全兼容传统的 Vi: 'nocompatible'"
+#: ../main.c:2116
+msgid " -r, -L List swap files\n"
+msgstr " -r, -L 列出交换文件\n"
-#: ../main.c:2215
-msgid "-V[N][fname]\t\tBe verbose [level N] [log messages to fname]"
-msgstr "-V[N][fname]\t\t详细 [level N] [log messages to fname]"
+#: ../main.c:2117
+msgid " -r <file> Recover edit state for this file\n"
+msgstr " -r <file> 恢复这个文件的编辑状态\n"
-#: ../main.c:2216
-msgid "-D\t\t\tDebugging mode"
-msgstr "-D\t\t\t调试模式"
+#: ../main.c:2118
+msgid " -R Read-only mode\n"
+msgstr " -R 只读模式\n"
-#: ../main.c:2217
-msgid "-n\t\t\tNo swap file, use memory only"
-msgstr "-n\t\t\t不使用交换文件,只使用内存"
+#: ../main.c:2119
+msgid " -S <session> Source <session> after loading the first file\n"
+msgstr " -S <session> 加载第一个文件后执行文件 <session>\n"
-#: ../main.c:2218
-msgid "-r\t\t\tList swap files and exit"
-msgstr "-r\t\t\t列出交换文件并退出"
+#: ../main.c:2120
+msgid " -s <scriptin> Read Normal mode commands from <scriptin>\n"
+msgstr " -s <scriptin> 从文件 <scriptin> 读入正常模式的命令\n"
-#: ../main.c:2219
-msgid "-r (with file name)\tRecover crashed session"
-msgstr "-r (跟文件名)\t\t恢复崩溃的会话"
+#: ../main.c:2121
+msgid " -u <config> Use this config file\n"
+msgstr " -u <config> 使用此配置文件\n"
-#: ../main.c:2220
-msgid "-L\t\t\tSame as -r"
-msgstr "-L\t\t\t同 -r"
+#: ../main.c:2122
+msgid " -v, --version Print version information\n"
+msgstr " -v, --version 打印版本信息\n"
-#: ../main.c:2221
-msgid "-A\t\t\tstart in Arabic mode"
-msgstr "-A\t\t\t以 Arabic 模式启动"
+#: ../main.c:2123
+msgid " -V[N][file] Verbose [level][file]\n"
+msgstr " -V[N][file] 啰嗦 [等级][文件]\n"
-#: ../main.c:2222
-msgid "-H\t\t\tStart in Hebrew mode"
-msgstr "-H\t\t\t以 Hebrew 模式启动"
+#: ../main.c:2125
+msgid " --api-info Write msgpack-encoded API metadata to stdout\n"
+msgstr " --api-info 将 msgpack 编码的 API 元数据写入标准输出\n"
-#: ../main.c:2223
-msgid "-F\t\t\tStart in Farsi mode"
-msgstr "-F\t\t\t以 Farsi 模式启动"
+#: ../main.c:2126
+msgid ""
+" --clean \"Factory defaults\" (skip user config and plugins, "
+"shada)\n"
+msgstr ""
+" --clean \"出厂设置\"(跳过用户配置、plugin 和 shada)\n"
-#: ../main.c:2224
-msgid "-T <terminal>\tSet terminal type to <terminal>"
-msgstr "-T <terminal>\t设定终端类型为 <terminal>"
+#: ../main.c:2127
+msgid " --embed Use stdin/stdout as a msgpack-rpc channel\n"
+msgstr ""
-#: ../main.c:2225
-msgid "-u <vimrc>\t\tUse <vimrc> instead of any .vimrc"
-msgstr "-u <vimrc>\t\t使用 <vimrc> 替代任何 .vimrc"
+#: ../main.c:2128
+msgid " --headless Don't start a user interface\n"
+msgstr " --headless 不要启动用户界面\n"
-#: ../main.c:2226
-msgid "--noplugin\t\tDon't load plugin scripts"
-msgstr "--noplugin\t\t不加载 plugin 脚本"
+#: ../main.c:2129
+msgid " --listen <address> Serve RPC API from this address\n"
+msgstr " --listen <address> 在这个地址提供 RPC API\n"
-#: ../main.c:2227
-msgid "-p[N]\t\tOpen N tab pages (default: one for each file)"
-msgstr "-P[N]\t\t打开 N 个标签页 (默认值: 每个文件一个)"
+#: ../main.c:2130
+msgid " --noplugin Don't load plugins\n"
+msgstr " --noplugin 不加载 plugin 脚本\n"
-#: ../main.c:2228
-msgid "-o[N]\t\tOpen N windows (default: one for each file)"
-msgstr "-o[N]\t\t打开 N 个窗口 (默认值: 每个文件一个)"
+#: ../main.c:2131
+msgid " --remote[-subcommand] Execute commands remotely on a server\n"
+msgstr ""
-#: ../main.c:2229
-msgid "-O[N]\t\tLike -o but split vertically"
-msgstr "-O[N]\t\t同 -o 但垂直分割"
+#: ../main.c:2132
+msgid " --server <address> Specify RPC server to send commands to\n"
+msgstr ""
-#: ../main.c:2230
-msgid "+\t\t\tStart at end of file"
-msgstr "+\t\t\t启动后跳到文件末尾"
+#: ../main.c:2133
+msgid " --startuptime <file> Write startup timing messages to <file>\n"
+msgstr " --startuptime <file> 将启动时间信息写入到文件 <file>\n"
-#: ../main.c:2231
-msgid "+<lnum>\t\tStart at line <lnum>"
-msgstr "+<lnum>\t\t启动后跳到第 <lnum> 行"
+#: ../main.c:2134
+msgid ""
+"\n"
+"See \":help startup-options\" for all options.\n"
+msgstr ""
-#: ../main.c:2232
-msgid "--cmd <command>\tExecute <command> before loading any vimrc file"
-msgstr "--cmd <command>\t加载任何 vimrc 文件前执行 <command>"
+#: ../mapping.c:645
+#, c-format
+msgid "E224: global abbreviation already exists for %s"
+msgstr "E224: 全局缩写 %s 已存在"
-#: ../main.c:2233
-msgid "-c <command>\t\tExecute <command> after loading the first file"
-msgstr "-c <command>\t\t加载第一个文件后执行 <command>"
+#: ../mapping.c:648
+#, c-format
+msgid "E225: global mapping already exists for %s"
+msgstr "E225: 全局映射 %s 已存在"
-#: ../main.c:2235
-msgid "-S <session>\t\tSource file <session> after loading the first file"
-msgstr "-S <session>\t\t加载第一个文件后执行文件 <session>"
+#: ../mapping.c:762
+#, c-format
+msgid "E226: abbreviation already exists for %s"
+msgstr "E226: 缩写 %s 已存在"
-#: ../main.c:2236
-msgid "-s <scriptin>\tRead Normal mode commands from file <scriptin>"
-msgstr "-s <scriptin>\t从文件 <scriptin> 读入正常模式的命令"
+#: ../mapping.c:764
+#, c-format
+msgid "E227: mapping already exists for %s"
+msgstr "E227: 映射 %s 已存在"
-#: ../main.c:2237
-msgid "-w <scriptout>\tAppend all typed commands to file <scriptout>"
-msgstr "-w <scriptout>\t将所有输入的命令追加到文件 <scriptout>"
+#: ../mapping.c:844
+msgid "No abbreviation found"
+msgstr "找不到缩写"
-#: ../main.c:2238
-msgid "-W <scriptout>\tWrite all typed commands to file <scriptout>"
-msgstr "-W <scriptout>\t将所有输入的命令写入到文件 <scriptout>"
+#: ../mapping.c:846
+msgid "No mapping found"
+msgstr "找不到映射"
-#: ../main.c:2240
-msgid "--startuptime <file>\tWrite startup timing messages to <file>"
-msgstr "--startuptime <file>\t将启动时间信息写入到文件 <file>"
+#: ../mapping.c:1751
+msgid "E228: makemap: Illegal mode"
+msgstr "E228: makemap: 无效的模式"
-#: ../main.c:2242
-msgid "-i <viminfo>\t\tUse <viminfo> instead of .viminfo"
-msgstr "-i <viminfo>\t\t使用 <viminfo> 取代 .viminfo"
+#: ../mapping.c:2181
+msgid "E460: entries missing in mapset() dict argument"
+msgstr "E460: mapset() dict 参数缺少项"
-#: ../main.c:2243
-msgid "-h or --help\tPrint Help (this message) and exit"
-msgstr "-h 或 --help\t打印帮助(本信息)并退出"
+#: ../mapping.c:2389
+#, c-format
+msgid "E357: 'langmap': Matching character missing for %s"
+msgstr "E357: 'langmap': 找不到 %s 对应的字符"
-#: ../main.c:2244
-msgid "--version\t\tPrint version information and exit"
-msgstr "--version\t\t打印版本信息并退出"
+#: ../mapping.c:2409
+#, c-format
+msgid "E358: 'langmap': Extra characters after semicolon: %s"
+msgstr "E358: 'langmap': 分号后有多余的字符: %s"
-#: ../mark.c:676
+#: ../mark.c:879
msgid "No marks set"
msgstr "没有设定标记"
-#: ../mark.c:678
+#: ../mark.c:881
#, c-format
msgid "E283: No marks matching \"%s\""
msgstr "E283: 没有匹配 \"%s\" 的标记"
#. Highlight title
-#: ../mark.c:687
+#: ../mark.c:895
msgid ""
"\n"
"mark line col file/text"
@@ -3594,7 +4228,7 @@ msgstr ""
"标记 行 列 文件/文本"
#. Highlight title
-#: ../mark.c:789
+#: ../mark.c:1002
msgid ""
"\n"
" jump line col file/text"
@@ -3603,7 +4237,7 @@ msgstr ""
" 跳转 行 列 文件/文本"
#. Highlight title
-#: ../mark.c:831
+#: ../mark.c:1054
msgid ""
"\n"
"change line col text"
@@ -3611,109 +4245,159 @@ msgstr ""
"\n"
" 改变 行 列 文本"
-#: ../mark.c:1238
-msgid ""
-"\n"
-"# File marks:\n"
+#: ../match.c:73
+#, c-format
+msgid "E799: Invalid ID: %<PRId64> (must be greater than or equal to 1)"
+msgstr "E799: ID 无效:%<PRId64>(必须大于等于 1)"
+
+#: ../match.c:85
+#, c-format
+msgid "E801: ID already taken: %<PRId64>"
+msgstr "E801: ID 已被使用:%<PRId64>"
+
+#: ../match.c:140
+#, c-format
+msgid "E5030: Empty list at position %d"
msgstr ""
-"\n"
-"# 文件标记:\n"
-#. Write the jumplist with -'
-#: ../mark.c:1271
-msgid ""
-"\n"
-"# Jumplist (newest first):\n"
+#: ../match.c:182
+#, c-format
+msgid "E5031: List or number required at position %d"
+msgstr "E5031: 位置 %d 需要列表或整数"
+
+#: ../match.c:252
+#, c-format
+msgid "E802: Invalid ID: %<PRId64> (must be greater than or equal to 1)"
+msgstr "E802: 无效的 ID:%<PRId64>(必须大于等于 1)"
+
+#: ../match.c:263
+#, c-format
+msgid "E803: ID not found: %<PRId64>"
+msgstr "E803: 找不到 ID:%<PRId64>"
+
+#: ../match.c:978
+#, c-format
+msgid "E474: List item %d is either not a dictionary or an empty one"
msgstr ""
-"\n"
-"# 跳转列表 (从新到旧):\n"
-#: ../mark.c:1352
-msgid ""
-"\n"
-"# History of marks within files (newest to oldest):\n"
+#: ../match.c:987
+#, c-format
+msgid "E474: List item %d is missing one of the required keys"
msgstr ""
-"\n"
-"# 文件内的标记历史记录 (从新到旧):\n"
-#: ../mark.c:1431
-msgid "Missing '>'"
-msgstr "缺少 '>'"
+#: ../match.c:1093
+#, c-format
+msgid "E798: ID is reserved for \":match\": %<PRId64>"
+msgstr "E798: ID 为 \":match\" 保留:%<PRId64>"
-#: ../memfile.c:426
+#: ../match.c:1144
+#, c-format
+msgid "E798: ID is reserved for \"match\": %<PRId64>"
+msgstr "E798: ID 为 \"match\" 保留:%<PRId64>"
+
+#: ../mbyte.c:98
+#, c-format
+msgid "E1109: List item %d is not a List"
+msgstr "E1109: 列表项 %d 不是列表"
+
+#: ../mbyte.c:100
+#, c-format
+msgid "E1110: List item %d does not contain 3 numbers"
+msgstr "E1110: 列表项 %d 不包含三个整数"
+
+#: ../mbyte.c:102
+#, c-format
+msgid "E1111: List item %d range invalid"
+msgstr "E1111: 列表项 %d 范围无效"
+
+#: ../mbyte.c:104
+#, c-format
+msgid "E1112: List item %d cell width invalid"
+msgstr "E1112: 列表项 %d 单元格宽度无效"
+
+#: ../mbyte.c:106
+#, c-format
+msgid "E1113: Overlapping ranges for 0x%lx"
+msgstr "E1113: 重叠范围为 0x%lx"
+
+#: ../mbyte.c:108
+msgid "E1114: Only values of 0x100 and higher supported"
+msgstr "E1114: 只支持 0x100 或更高的值"
+
+#: ../memfile.c:334
msgid "E293: block was not locked"
msgstr "E293: 块未被锁定"
-#: ../memfile.c:799
+#: ../memfile.c:582
msgid "E294: Seek error in swap file read"
msgstr "E294: 交换文件读取定位错误"
-#: ../memfile.c:803
+#: ../memfile.c:589
msgid "E295: Read error in swap file"
msgstr "E295: 交换文件读取错误"
-#: ../memfile.c:849
+#: ../memfile.c:641
msgid "E296: Seek error in swap file write"
msgstr "E296: 交换文件写入定位错误"
-#: ../memfile.c:865
+#: ../memfile.c:657
msgid "E297: Write error in swap file"
msgstr "E297: 交换文件写入错误"
-#: ../memfile.c:1036
+#: ../memfile.c:801
msgid "E300: Swap file already exists (symlink attack?)"
msgstr "E300: 交换文件已存在 (符号连接攻击?)"
-#: ../memline.c:318
+#: ../memline.c:288
msgid "E298: Didn't get block nr 0?"
msgstr "E298: 找不到块 0?"
-#: ../memline.c:361
+#: ../memline.c:328
msgid "E298: Didn't get block nr 1?"
msgstr "E298: 找不到块 1?"
-#: ../memline.c:377
+#: ../memline.c:342
msgid "E298: Didn't get block nr 2?"
msgstr "E298: 找不到块 2?"
#. could not (re)open the swap file, what can we do????
-#: ../memline.c:465
+#: ../memline.c:424
msgid "E301: Oops, lost the swap file!!!"
msgstr "E301: 噢,交换文件不见了!!!"
-#: ../memline.c:477
+#: ../memline.c:430
msgid "E302: Could not rename swap file"
msgstr "E302: 无法重命名交换文件"
-#: ../memline.c:554
+#: ../memline.c:504
#, c-format
msgid "E303: Unable to open swap file for \"%s\", recovery impossible"
msgstr "E303: 无法打开 \"%s\" 的交换文件,恢复将不可能"
-#: ../memline.c:666
+#: ../memline.c:609
msgid "E304: ml_upd_block0(): Didn't get block 0??"
msgstr "E304: ml_upd_block0(): 找不到块 0?"
#. no swap files found
-#: ../memline.c:830
+#: ../memline.c:745
#, c-format
msgid "E305: No swap file found for %s"
msgstr "E305: 找不到 %s 的交换文件"
-#: ../memline.c:839
+#: ../memline.c:755
msgid "Enter number of swap file to use (0 to quit): "
msgstr "请输入要使用的交换文件编号 (0 退出): "
-#: ../memline.c:879
+#: ../memline.c:791
#, c-format
msgid "E306: Cannot open %s"
msgstr "E306: 无法打开 %s"
-#: ../memline.c:897
+#: ../memline.c:805
msgid "Unable to read block 0 from "
msgstr "无法读取块 0: "
-#: ../memline.c:900
+#: ../memline.c:807
msgid ""
"\n"
"Maybe no changes were made or Vim did not update the swap file."
@@ -3721,28 +4405,28 @@ msgstr ""
"\n"
"可能你没做过任何修改或是 Vim 还来不及更新交换文件。"
-#: ../memline.c:909
+#: ../memline.c:816
msgid " cannot be used with this version of Vim.\n"
msgstr " 不能在该版本的 Vim 中使用。\n"
-#: ../memline.c:911
+#: ../memline.c:818
msgid "Use Vim version 3.0.\n"
msgstr "使用 Vim 3.0。\n"
-#: ../memline.c:916
+#: ../memline.c:823
#, c-format
msgid "E307: %s does not look like a Vim swap file"
msgstr "E307: %s 看起来不像是 Vim 交换文件"
-#: ../memline.c:922
+#: ../memline.c:829
msgid " cannot be used on this computer.\n"
msgstr " 不能在这台电脑上使用。\n"
-#: ../memline.c:924
+#: ../memline.c:831
msgid "The file was created on "
msgstr "此文件创建于 "
-#: ../memline.c:928
+#: ../memline.c:835
msgid ""
",\n"
"or the file has been damaged."
@@ -3750,100 +4434,92 @@ msgstr ""
",\n"
"或是此文件已损坏。"
-#: ../memline.c:945
+#: ../memline.c:849
msgid " has been damaged (page size is smaller than minimum value).\n"
msgstr "已损坏(页面大小小于最小值)。\n"
-#: ../memline.c:974
+#: ../memline.c:879
#, c-format
msgid "Using swap file \"%s\""
msgstr "使用交换文件 \"%s\""
-#: ../memline.c:980
+#: ../memline.c:886
#, c-format
msgid "Original file \"%s\""
msgstr "原始文件 \"%s\""
-#: ../memline.c:995
+#: ../memline.c:899
msgid "E308: Warning: Original file may have been changed"
msgstr "E308: 警告: 原始文件可能已被修改"
-#: ../memline.c:1061
+#: ../memline.c:958
#, c-format
msgid "E309: Unable to read block 1 from %s"
msgstr "E309: 无法从 %s 读取块 1"
# do not translate to avoid writing Chinese in files
-#: ../memline.c:1065
-#, fuzzy
+#: ../memline.c:962
msgid "???MANY LINES MISSING"
-msgstr "???缺少了太多行"
+msgstr ""
# do not translate to avoid writing Chinese in files
-#: ../memline.c:1076
-#, fuzzy
+#: ../memline.c:974
msgid "???LINE COUNT WRONG"
-msgstr "???行数错误"
+msgstr ""
# do not translate to avoid writing Chinese in files
-#: ../memline.c:1082
-#, fuzzy
+#: ../memline.c:980
msgid "???EMPTY BLOCK"
-msgstr "???空的块"
+msgstr ""
# do not translate to avoid writing Chinese in files
-#: ../memline.c:1103
-#, fuzzy
+#: ../memline.c:1000
msgid "???LINES MISSING"
-msgstr "???缺少了一些行"
+msgstr ""
-#: ../memline.c:1128
+#: ../memline.c:1023
#, c-format
msgid "E310: Block 1 ID wrong (%s not a .swp file?)"
msgstr "E310: 块 1 ID 错误 (%s 不是交换文件?)"
# do not translate to avoid writing Chinese in files
-#: ../memline.c:1133
-#, fuzzy
+#: ../memline.c:1028
msgid "???BLOCK MISSING"
-msgstr "???缺少块"
+msgstr ""
# do not translate to avoid writing Chinese in files
-#: ../memline.c:1147
-#, fuzzy
+#: ../memline.c:1038
msgid "??? from here until ???END lines may be messed up"
-msgstr "??? 从这里到 ???END 的行可能已混乱"
+msgstr ""
# do not translate to avoid writing Chinese in files
-#: ../memline.c:1164
-#, fuzzy
+#: ../memline.c:1052
msgid "??? from here until ???END lines may have been inserted/deleted"
-msgstr "??? 从这里到 ???END 的行可能已被插入/删除过"
+msgstr ""
# do not translate to avoid writing Chinese in files
-#: ../memline.c:1181
-#, fuzzy
+#: ../memline.c:1071
msgid "???END"
-msgstr "???END"
+msgstr ""
-#: ../memline.c:1238
+#: ../memline.c:1125
msgid "E311: Recovery Interrupted"
msgstr "E311: 恢复已被中断"
-#: ../memline.c:1243
+#: ../memline.c:1129
msgid ""
"E312: Errors detected while recovering; look for lines starting with ???"
msgstr "E312: 恢复时发生错误;请注意开头为 ??? 的行"
-#: ../memline.c:1245
+#: ../memline.c:1131
msgid "See \":help E312\" for more information."
msgstr "更多信息请见 \":help E312\""
-#: ../memline.c:1249
+#: ../memline.c:1135
msgid "Recovery completed. You should check if everything is OK."
msgstr "恢复完毕。请确定一切正常。"
-#: ../memline.c:1251
+#: ../memline.c:1136
msgid ""
"\n"
"(You might want to write out this file under another name\n"
@@ -3851,71 +4527,74 @@ msgstr ""
"\n"
"(你可能想要将这个文件另存为别的文件名\n"
-#: ../memline.c:1252
-#, fuzzy
+#: ../memline.c:1137
msgid "and run diff with the original file to check for changes)"
-msgstr "再运行 diff 与原文件比较以检查是否有改变)\n"
+msgstr "再运行 diff 与原文件比较以检查是否有改变)"
-#: ../memline.c:1254
+#: ../memline.c:1139
msgid "Recovery completed. Buffer contents equals file contents."
msgstr "恢复完成。缓冲区内容与文件内容相同。"
-#: ../memline.c:1255
-#, fuzzy
+#: ../memline.c:1141
msgid ""
"\n"
"You may want to delete the .swp file now.\n"
"\n"
msgstr ""
-"然后把 .swp 文件删掉。\n"
+"\n"
+"你现在可以删除 .swp 文件了。\n"
"\n"
#. use msg() to start the scrolling properly
-#: ../memline.c:1327
+#: ../memline.c:1206
msgid "Swap files found:"
msgstr "找到交换文件:"
-#: ../memline.c:1446
+#: ../memline.c:1311
msgid " In current directory:\n"
msgstr " 位于当前目录:\n"
-#: ../memline.c:1448
+#: ../memline.c:1313
msgid " Using specified name:\n"
msgstr " 使用指定的名字:\n"
-#: ../memline.c:1450
+#: ../memline.c:1316
msgid " In directory "
msgstr " 位于目录 "
-#: ../memline.c:1465
+#: ../memline.c:1331
msgid " -- none --\n"
msgstr " -- 无 --\n"
-#: ../memline.c:1527
+#: ../memline.c:1429
msgid " owned by: "
msgstr " 所有者: "
-#: ../memline.c:1529
+#: ../memline.c:1431
msgid " dated: "
msgstr " 日期: "
-#: ../memline.c:1532 ../memline.c:3231
+#: ../memline.c:1433 ../memline.c:1436 ../memline.c:3084
msgid " dated: "
msgstr " 日期: "
-#: ../memline.c:1548
+#: ../memline.c:1448
msgid " [from Vim version 3.0]"
msgstr " [来自 Vim 版本 3.0]"
-#: ../memline.c:1550
+#: ../memline.c:1450
msgid " [does not look like a Vim swap file]"
msgstr " [不像是 Vim 交换文件]"
-#: ../memline.c:1552
+#: ../memline.c:1452
+msgid " [garbled strings (not nul terminated)]"
+msgstr ""
+
+#: ../memline.c:1454
msgid " file name: "
msgstr " 文件名: "
-#: ../memline.c:1558
+#: ../memline.c:1461
msgid ""
"\n"
" modified: "
@@ -3923,15 +4602,15 @@ msgstr ""
"\n"
" 修改过: "
-#: ../memline.c:1559
+#: ../memline.c:1462
msgid "YES"
msgstr "是"
-#: ../memline.c:1559
+#: ../memline.c:1462
msgid "no"
msgstr "否"
-#: ../memline.c:1562
+#: ../memline.c:1465
msgid ""
"\n"
" user name: "
@@ -3939,11 +4618,11 @@ msgstr ""
"\n"
" 用户名: "
-#: ../memline.c:1568
+#: ../memline.c:1471
msgid " host name: "
msgstr " 主机名: "
-#: ../memline.c:1570
+#: ../memline.c:1473
msgid ""
"\n"
" host name: "
@@ -3951,7 +4630,7 @@ msgstr ""
"\n"
" 主机名: "
-#: ../memline.c:1575
+#: ../memline.c:1479
msgid ""
"\n"
" process ID: "
@@ -3959,11 +4638,11 @@ msgstr ""
"\n"
" 进程 ID: "
-#: ../memline.c:1579
-msgid " (still running)"
-msgstr " (仍在运行)"
+#: ../memline.c:1482
+msgid " (STILL RUNNING)"
+msgstr " (还!在!运!行!)"
-#: ../memline.c:1586
+#: ../memline.c:1488
msgid ""
"\n"
" [not usable on this computer]"
@@ -3971,97 +4650,97 @@ msgstr ""
"\n"
" [不能在本机上使用]"
-#: ../memline.c:1590
+#: ../memline.c:1492
msgid " [cannot be read]"
msgstr " [无法读取]"
-#: ../memline.c:1593
+#: ../memline.c:1496
msgid " [cannot be opened]"
msgstr " [无法打开]"
-#: ../memline.c:1698
+#: ../memline.c:1638
msgid "E313: Cannot preserve, there is no swap file"
msgstr "E313: 无法保留,没有交换文件"
-#: ../memline.c:1747
+#: ../memline.c:1687
msgid "File preserved"
msgstr "文件已保留"
-#: ../memline.c:1749
+#: ../memline.c:1689
msgid "E314: Preserve failed"
msgstr "E314: 保留失败"
-#: ../memline.c:1819
+#: ../memline.c:1741
#, c-format
msgid "E315: ml_get: invalid lnum: %<PRId64>"
msgstr "E315: ml_get: 无效的 lnum: %<PRId64>"
-#: ../memline.c:1851
-#, c-format
-msgid "E316: ml_get: cannot find line %<PRId64>"
+#: ../memline.c:1777
+#, fuzzy, c-format
+msgid "E316: ml_get: cannot find line %<PRId64> in buffer %d %s"
msgstr "E316: ml_get: 找不到第 %<PRId64> 行"
-#: ../memline.c:2236
+#: ../memline.c:2128
msgid "E317: pointer block id wrong 3"
msgstr "E317: 指针块 id 错误 3"
-#: ../memline.c:2311
+#: ../memline.c:2202
msgid "stack_idx should be 0"
msgstr "stack_idx 应该是 0"
-#: ../memline.c:2369
+#: ../memline.c:2257
msgid "E318: Updated too many blocks?"
msgstr "E318: 更新了太多的块?"
-#: ../memline.c:2511
+#: ../memline.c:2432
msgid "E317: pointer block id wrong 4"
msgstr "E317: 指针块 id 错误 4"
-#: ../memline.c:2536
+#: ../memline.c:2458
msgid "deleted block 1?"
msgstr "删除了块 1?"
-#: ../memline.c:2707
+#: ../memline.c:2603
#, c-format
msgid "E320: Cannot find line %<PRId64>"
msgstr "E320: 找不到第 %<PRId64> 行"
-#: ../memline.c:2916
+#: ../memline.c:2795
msgid "E317: pointer block id wrong"
msgstr "E317: 指针块 id 错误"
-#: ../memline.c:2930
+#: ../memline.c:2810
msgid "pe_line_count is zero"
msgstr "pe_line_count 为零"
-#: ../memline.c:2955
+#: ../memline.c:2833
#, c-format
msgid "E322: line number out of range: %<PRId64> past the end"
msgstr "E322: 行号超出范围: %<PRId64> 超出结尾"
-#: ../memline.c:2959
+#: ../memline.c:2836
#, c-format
msgid "E323: line count wrong in block %<PRId64>"
msgstr "E323: 块 %<PRId64> 行数错误"
-#: ../memline.c:2999
+#: ../memline.c:2874
msgid "Stack size increases"
msgstr "堆栈大小增加"
-#: ../memline.c:3038
+#: ../memline.c:2906
msgid "E317: pointer block id wrong 2"
msgstr "E317: 指针块 id 错误 2"
-#: ../memline.c:3070
+#: ../memline.c:2938
#, c-format
msgid "E773: Symlink loop for \"%s\""
msgstr "E773: \"%s\" 符号连接出现循环"
-#: ../memline.c:3221
+#: ../memline.c:3072
msgid "E325: ATTENTION"
msgstr "E325: 注意"
-#: ../memline.c:3222
+#: ../memline.c:3073
msgid ""
"\n"
"Found a swap file by the name \""
@@ -4069,44 +4748,42 @@ msgstr ""
"\n"
"发现交换文件 \""
-#: ../memline.c:3226
+#: ../memline.c:3077
msgid "While opening file \""
msgstr "正在打开文件 \""
-#: ../memline.c:3239
+#: ../memline.c:3082
+#, fuzzy
+msgid " CANNOT BE FOUND"
+msgstr " 找不到"
+
+#: ../memline.c:3089
msgid " NEWER than swap file!\n"
msgstr " 比交换文件新!\n"
-#: ../memline.c:3244
-#, fuzzy
+#. Some of these messages are long to allow translation to
+#. other languages.
+#: ../memline.c:3094
msgid ""
"\n"
"(1) Another program may be editing the same file. If this is the case,\n"
" be careful not to end up with two different instances of the same\n"
-" file when making changes."
+" file when making changes. Quit, or continue with caution.\n"
msgstr ""
"\n"
"(1) 另一个程序可能也在编辑同一个文件。\n"
" 如果是这样,修改时请注意避免同一个文件产生两个不同的版本。\n"
-"\n"
+" 退出,或者小心地继续。\n"
-#: ../memline.c:3245
-#, fuzzy
-msgid " Quit, or continue with caution.\n"
-msgstr " 退出,或小心地继续。\n"
-
-#: ../memline.c:3246
-#, fuzzy
+#: ../memline.c:3098
msgid "(2) An edit session for this file crashed.\n"
-msgstr ""
-"\n"
-"(2) 上次编辑此文件时崩溃。\n"
+msgstr "(2) 上次编辑此文件时崩溃。\n"
-#: ../memline.c:3247
+#: ../memline.c:3099
msgid " If this is the case, use \":recover\" or \"vim -r "
msgstr " 如果是这样,请用 \":recover\" 或 \"vim -r "
-#: ../memline.c:3249
+#: ../memline.c:3101
msgid ""
"\"\n"
" to recover the changes (see \":help recovery\").\n"
@@ -4114,11 +4791,11 @@ msgstr ""
"\"\n"
" 恢复修改的内容 (请见 \":help recovery\")。\n"
-#: ../memline.c:3250
+#: ../memline.c:3102
msgid " If you did this already, delete the swap file \""
msgstr " 如果你已经进行了恢复,请删除交换文件 \""
-#: ../memline.c:3252
+#: ../memline.c:3104
msgid ""
"\"\n"
" to avoid this message.\n"
@@ -4126,23 +4803,23 @@ msgstr ""
"\"\n"
" 以避免再看到此消息。\n"
-#: ../memline.c:3450 ../memline.c:3452
+#: ../memline.c:3270
+msgid "Found a swap file that is not useful, deleting it"
+msgstr "找到一个没用的交换文件,删了"
+
+#: ../memline.c:3296
msgid "Swap file \""
msgstr "交换文件 \""
-#: ../memline.c:3451 ../memline.c:3455
+#: ../memline.c:3297
msgid "\" already exists!"
msgstr "\" 已存在!"
-#: ../memline.c:3457
+#: ../memline.c:3309
msgid "VIM - ATTENTION"
msgstr "VIM - 注意"
-#: ../memline.c:3459
-msgid "Swap file already exists!"
-msgstr "交换文件已存在!"
-
-#: ../memline.c:3464
+#: ../memline.c:3312
msgid ""
"&Open Read-Only\n"
"&Edit anyway\n"
@@ -4156,7 +4833,7 @@ msgstr ""
"退出(&Q)\n"
"中止(&A)"
-#: ../memline.c:3467
+#: ../memline.c:3314
msgid ""
"&Open Read-Only\n"
"&Edit anyway\n"
@@ -4172,57 +4849,62 @@ msgstr ""
"退出(&Q)\n"
"中止(&A)"
-#.
-#. * Change the ".swp" extension to find another file that can be used.
-#. * First decrement the last char: ".swo", ".swn", etc.
-#. * If that still isn't enough decrement the last but one char: ".svz"
-#. * Can happen when editing many "No Name" buffers.
-#.
+#. Change the ".swp" extension to find another file that can be used.
+#. First decrement the last char: ".swo", ".swn", etc.
+#. If that still isn't enough decrement the last but one char: ".svz"
+#. Can happen when editing many "No Name" buffers.
#. ".s?a"
#. ".saa": tried enough, give up
-#: ../memline.c:3528
+#: ../memline.c:3370
msgid "E326: Too many swap files found"
msgstr "E326: 找到太多交换文件"
-#: ../memory.c:227
+#: ../memline.c:3386
+#, c-format
+msgid ""
+"E303: Unable to create directory \"%s\" for swap file, recovery impossible: "
+"%s"
+msgstr "E303: 无法为交换文件创建目录 \"%s\",恢复将不可能:%s"
+
+#: ../memory.c:197
+msgid "Vim: Data too large to fit into virtual memory space\n"
+msgstr "Vim:数据太大无法放入虚拟内存空间\n"
+
+#: ../memory.c:527
#, c-format
msgid "E342: Out of memory! (allocating %<PRIu64> bytes)"
msgstr "E342: 内存不足!(分配 %<PRIu64> 字节)"
-#: ../menu.c:62
+#: ../menu.c:52
msgid "E327: Part of menu-item path is not sub-menu"
msgstr "E327: 菜单项的某部分路径不是子菜单"
-#: ../menu.c:63
-msgid "E328: Menu only exists in another mode"
-msgstr "E328: 菜单只在其它模式中存在"
-
-#: ../menu.c:64
+#: ../menu.c:53
#, c-format
msgid "E329: No menu \"%s\""
msgstr "E329: 没有菜单 \"%s\""
#. Only a mnemonic or accelerator is not valid.
-#: ../menu.c:329
-#, fuzzy
+#: ../menu.c:310
msgid "E792: Empty menu name"
-msgstr "E749: 空的缓冲区"
+msgstr "E792: 空的菜单名称"
-#: ../menu.c:340
+#: ../menu.c:321
msgid "E330: Menu path must not lead to a sub-menu"
msgstr "E330: 菜单路径不能指向子菜单"
-#: ../menu.c:365
+#: ../menu.c:347
msgid "E331: Must not add menu items directly to menu bar"
msgstr "E331: 不能把菜单项直接加到菜单栏中"
-#: ../menu.c:370
+#: ../menu.c:352
msgid "E332: Separator cannot be part of a menu path"
msgstr "E332: 分隔线不能是菜单路径的一部分"
+#. When there are no menus at all, the title still needs to be shown.
#. Now we have found the matching menu, and we list the mappings
#. Highlight title
-#: ../menu.c:762
+#: ../menu.c:805
msgid ""
"\n"
"--- Menus ---"
@@ -4230,69 +4912,85 @@ msgstr ""
"\n"
"--- 菜单 ---"
-#: ../menu.c:1313
+#: ../menu.c:1568
+#, c-format
+msgid "E335: Menu not defined for %s mode"
+msgstr "E335: %s 模式中菜单未定义"
+
+#: ../menu.c:1588
msgid "E333: Menu path must lead to a menu item"
msgstr "E333: 菜单路径必须指向菜单项"
-#: ../menu.c:1330
+#: ../menu.c:1608
#, c-format
msgid "E334: Menu not found: %s"
msgstr "E334: 找不到菜单: %s"
-#: ../menu.c:1396
-#, c-format
-msgid "E335: Menu not defined for %s mode"
-msgstr "E335: %s 模式中菜单未定义"
-
-#: ../menu.c:1426
+#: ../menu.c:1670
msgid "E336: Menu path must lead to a sub-menu"
msgstr "E336: 菜单路径必须指向子菜单"
-#: ../menu.c:1447
+#: ../menu.c:1694
msgid "E337: Menu not found - check menu names"
msgstr "E337: 找不到菜单 - 请检查菜单名称"
-#: ../message.c:423
+#: ../message.c:562
#, c-format
msgid "Error detected while processing %s:"
msgstr "处理 %s 时发生错误:"
-#: ../message.c:445
+#: ../message.c:584
#, c-format
msgid "line %4ld:"
msgstr "第 %4ld 行:"
-#: ../message.c:617
+#: ../message.c:777
#, c-format
msgid "E354: Invalid register name: '%s'"
msgstr "E354: 无效的寄存器名: '%s'"
-#: ../message.c:986
+#: ../message.c:1319
msgid "Interrupt: "
msgstr "已中断: "
-#: ../message.c:988
+#: ../message.c:1322
msgid "Press ENTER or type command to continue"
msgstr "请按 ENTER 或其它命令继续"
-#: ../message.c:1843
+#: ../message.c:1367
+#, c-format
+msgid "%ld more line"
+msgid_plural "%ld more lines"
+msgstr[0] "多了 %ld 行"
+
+#: ../message.c:1371
+#, c-format
+msgid "%ld line less"
+msgid_plural "%ld fewer lines"
+msgstr[0] "少了 %ld 行"
+
+#: ../message.c:1375
+msgid " (Interrupted)"
+msgstr " (已中断)"
+
+#: ../message.c:2473
#, c-format
msgid "%s line %<PRId64>"
msgstr "%s 第 %<PRId64> 行"
-#: ../message.c:2392
+#: ../message.c:3057
msgid "-- More --"
msgstr "-- 更多 --"
-#: ../message.c:2398
+#: ../message.c:3062
msgid " SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "
msgstr " 空格/d/j: 屏幕/页/行 下翻,b/u/k: 上翻,q: 退出 "
-#: ../message.c:3021 ../message.c:3031
+#: ../message.c:3768 ../message.c:3779
msgid "Question"
msgstr "问题"
-#: ../message.c:3023
+#: ../message.c:3770
msgid ""
"&Yes\n"
"&No"
@@ -4300,7 +4998,7 @@ msgstr ""
"是(&Y)\n"
"否(&N)"
-#: ../message.c:3033
+#: ../message.c:3781
msgid ""
"&Yes\n"
"&No\n"
@@ -4310,7 +5008,7 @@ msgstr ""
"否(&N)\n"
"取消(&C)"
-#: ../message.c:3045
+#: ../message.c:3795
msgid ""
"&Yes\n"
"&No\n"
@@ -4324,207 +5022,123 @@ msgstr ""
"全部丢弃(&D)\n"
"取消(&C)"
-#: ../message.c:3058
-msgid "E766: Insufficient arguments for printf()"
-msgstr "E766: printf() 的参数不足"
-
-#: ../message.c:3119
-#, fuzzy
-msgid "E807: Expected Float argument for printf()"
-msgstr "E766: printf() 的参数不足"
-
-#: ../message.c:3873
-msgid "E767: Too many arguments to printf()"
-msgstr "E767: printf() 的参数过多"
-
-#: ../misc1.c:2256
-msgid "W10: Warning: Changing a readonly file"
-msgstr "W10: 警告: 正在修改一个只读文件"
-
-#: ../misc1.c:2537
-#, fuzzy
-msgid "Type number and <Enter> or click with mouse (empty cancels): "
-msgstr "请输入数字或点击鼠标 (<Enter> 取消): "
-
-#: ../misc1.c:2539
-#, fuzzy
-msgid "Type number and <Enter> (empty cancels): "
-msgstr "请选择数字 (<Enter> 取消): "
-
-#: ../misc1.c:2585
-msgid "1 more line"
-msgstr "多了 1 行"
-
-#: ../misc1.c:2588
-msgid "1 line less"
-msgstr "少了 1 行"
-
-#: ../misc1.c:2593
-#, c-format
-msgid "%<PRId64> more lines"
-msgstr "多了 %<PRId64> 行"
-
-#: ../misc1.c:2596
-#, c-format
-msgid "%<PRId64> fewer lines"
-msgstr "少了 %<PRId64> 行"
-
-#: ../misc1.c:2599
-msgid " (Interrupted)"
-msgstr " (已中断)"
-
-#: ../misc1.c:2635
-msgid "Beep!"
-msgstr "Beep!"
-
-#: ../misc2.c:738
-#, c-format
-msgid "Calling shell to execute: \"%s\""
-msgstr "调用 shell 执行: \"%s\""
-
-#: ../normal.c:183
+#. 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.
+#: ../normal.c:120
msgid "E349: No identifier under cursor"
msgstr "E349: 光标处没有识别字"
-#: ../normal.c:1866
-msgid "E774: 'operatorfunc' is empty"
-msgstr "E774: 'operatorfunc' 为空"
-
-#: ../normal.c:2637
-msgid "Warning: terminal cannot highlight"
-msgstr "警告: 你的终端不能显示高亮"
-
-#: ../normal.c:2807
+#: ../normal.c:1633
msgid "E348: No string under cursor"
msgstr "E348: 光标处没有字符串"
-#: ../normal.c:3937
+#: ../normal.c:2983
msgid "E352: Cannot erase folds with current 'foldmethod'"
msgstr "E352: 不能在当前的 'foldmethod' 下删除 fold"
-#: ../normal.c:5897
+#: ../normal.c:4915
msgid "E664: changelist is empty"
msgstr "E664: 改变列表为空"
-#: ../normal.c:5899
+#: ../normal.c:4917
msgid "E662: At start of changelist"
msgstr "E662: 已在改变列表的开始处"
-#: ../normal.c:5901
+#: ../normal.c:4919
msgid "E663: At end of changelist"
msgstr "E663: 已在改变列表的末尾处"
-#: ../normal.c:7053
-msgid "Type :quit<Enter> to exit Nvim"
-msgstr "输入 :quit<Enter> 退出 Vim"
-
-#: ../ops.c:248
-#, c-format
-msgid "1 line %sed 1 time"
-msgstr "1 行 %s 了 1 次"
+#: ../normal.c:6081
+msgid "Type :qa! and press <Enter> to abandon all changes and exit Nvim"
+msgstr "输入 :qa! 并按 <Enter> 来放弃所有更改并退出 Nvim"
-#: ../ops.c:250
-#, c-format
-msgid "1 line %sed %d times"
-msgstr "1 行 %s 了 %d 次"
+#: ../normal.c:6084
+msgid "Type :qa and press <Enter> to exit Nvim"
+msgstr "输入 :qa 并按 <Enter> 退出 Nvim"
-#: ../ops.c:253
-#, c-format
-msgid "%<PRId64> lines %sed 1 time"
-msgstr "%<PRId64> 行 %s 了 1 次"
+#: ../ops.c:263
+#, fuzzy, c-format
+msgid "%<PRId64> line %sed %d time"
+msgid_plural "%<PRId64> line %sed %d times"
+msgstr[0] "%<PRId64> 行 %s 了 %d 次"
-#: ../ops.c:256
-#, c-format
-msgid "%<PRId64> lines %sed %d times"
-msgstr "%<PRId64> 行 %s 了 %d 次"
+#: ../ops.c:265
+#, fuzzy, c-format
+msgid "%<PRId64> lines %sed %d time"
+msgid_plural "%<PRId64> lines %sed %d times"
+msgstr[0] "%<PRId64> 行 %s 了 %d 次"
-#: ../ops.c:592
+#: ../ops.c:662
#, c-format
msgid "%<PRId64> lines to indent... "
msgstr "缩进 %<PRId64> 行…… "
-#: ../ops.c:634
-msgid "1 line indented "
-msgstr "缩进了 1 行 "
-
-#: ../ops.c:636
+#: ../ops.c:705
#, c-format
-msgid "%<PRId64> lines indented "
-msgstr "缩进了 %<PRId64> 行 "
+msgid "%<PRId64> line indented "
+msgid_plural "%<PRId64> lines indented "
+msgstr[0] "缩进了 %<PRId64> 行 "
-#: ../ops.c:938
+#: ../ops.c:1088
msgid "E748: No previously used register"
msgstr "E748: 没有前一个使用的寄存器"
-#. must display the prompt
-#: ../ops.c:1433
-msgid "cannot yank; delete anyway"
-msgstr "无法复制;改为删除"
-
-#: ../ops.c:1929
-msgid "1 line changed"
-msgstr "改变了 1 行"
-
-#: ../ops.c:1931
+#: ../ops.c:2122
#, c-format
-msgid "%<PRId64> lines changed"
-msgstr "改变了 %<PRId64> 行"
+msgid "%<PRId64> line changed"
+msgid_plural "%<PRId64> lines changed"
+msgstr[0] "改变了 %<PRId64> 行"
-#: ../ops.c:2521
-msgid "block of 1 line yanked"
-msgstr "复制了 1 行的块"
-
-#: ../ops.c:2523
-msgid "1 line yanked"
-msgstr "复制了 1 行"
-
-#: ../ops.c:2525
+#: ../ops.c:2786
#, c-format
-msgid "block of %<PRId64> lines yanked"
-msgstr "复制了 %<PRId64> 行的块"
+msgid " into \"%c"
+msgstr "进 \"%c "
-#: ../ops.c:2528
-#, c-format
-msgid "%<PRId64> lines yanked"
-msgstr "复制了 %<PRId64> 行"
+#: ../ops.c:2795
+#, fuzzy, c-format
+msgid "block of %<PRId64> line yanked%s"
+msgid_plural "block of %<PRId64> lines yanked%s"
+msgstr[0] "复制了 %<PRId64> 行的块"
+
+#: ../ops.c:2799
+#, fuzzy, c-format
+msgid "%<PRId64> line yanked%s"
+msgid_plural "%<PRId64> lines yanked%s"
+msgstr[0] "复制了 %<PRId64> 行"
-#: ../ops.c:2710
+#: ../ops.c:3148
#, c-format
msgid "E353: Nothing in register %s"
msgstr "E353: 寄存器 %s 里没有东西"
#. Highlight title
-#: ../ops.c:3185
+#: ../ops.c:3779
msgid ""
"\n"
-"--- Registers ---"
+"Type Name Content"
msgstr ""
"\n"
-"--- 寄存器 ---"
+"类型 名称 内容"
-#: ../ops.c:4455
-msgid "Illegal register name"
-msgstr "无效的寄存器名"
+#: ../ops.c:4453
+#, c-format
+msgid "%<PRId64> lines changed"
+msgid_plural "%<PRId64> lines changed"
+msgstr[0] "改变了 %<PRId64> 行"
-#: ../ops.c:4533
+#: ../ops.c:5034
msgid ""
-"\n"
-"# Registers:\n"
-msgstr ""
-"\n"
-"# 寄存器:\n"
-
-#: ../ops.c:4575
-#, c-format
-msgid "E574: Unknown register type %d"
-msgstr "E574: 未知的寄存器类型 %d"
+"E883: search pattern and expression register may not contain two or more "
+"lines"
+msgstr "E883: 搜索模式和表达式寄存器只能包含一行文本"
-#: ../ops.c:5089
+#: ../ops.c:5462
#, c-format
msgid "%<PRId64> Cols; "
msgstr "%<PRId64> 列; "
-#: ../ops.c:5097
+#: ../ops.c:5471
#, c-format
msgid ""
"Selected %s%<PRId64> of %<PRId64> Lines; %<PRId64> of %<PRId64> Words; "
@@ -4533,7 +5147,7 @@ msgstr ""
"选择了 %s%<PRId64>/%<PRId64> 行; %<PRId64>/%<PRId64> 个词; %<PRId64>/"
"%<PRId64> 个字节"
-#: ../ops.c:5105
+#: ../ops.c:5480
#, c-format
msgid ""
"Selected %s%<PRId64> of %<PRId64> Lines; %<PRId64> of %<PRId64> Words; "
@@ -4542,7 +5156,7 @@ msgstr ""
"选择了 %s%<PRId64>/%<PRId64> 行; %<PRId64>/%<PRId64> 个词; %<PRId64>/"
"%<PRId64> 个字符; %<PRId64>/%<PRId64> 个字节"
-#: ../ops.c:5123
+#: ../ops.c:5500
#, c-format
msgid ""
"Col %s of %s; Line %<PRId64> of %<PRId64>; Word %<PRId64> of %<PRId64>; Byte "
@@ -4551,7 +5165,7 @@ msgstr ""
"第 %s/%s 列; 第 %<PRId64>/%<PRId64> 行; 第 %<PRId64>/%<PRId64> 个词; 第 "
"%<PRId64>/%<PRId64> 个字节"
-#: ../ops.c:5133
+#: ../ops.c:5510
#, c-format
msgid ""
"Col %s of %s; Line %<PRId64> of %<PRId64>; Word %<PRId64> of %<PRId64>; Char "
@@ -4560,156 +5174,67 @@ msgstr ""
"第 %s/%s 列; 第 %<PRId64>/%<PRId64> 行; 第 %<PRId64>/%<PRId64> 个词; 第 "
"%<PRId64>/%<PRId64> 个字符; 第 %<PRId64>/%<PRId64> 个字节"
-#: ../ops.c:5146
+#: ../ops.c:5528
#, c-format
msgid "(+%<PRId64> for BOM)"
msgstr ""
-#: ../option.c:1238
-msgid "%<%f%h%m%=Page %N"
-msgstr "%<%f%h%m%=页 %N"
-
-#: ../option.c:1574
-msgid "Thanks for flying Vim"
-msgstr "感谢您选择 Vim"
+#: ../ops.c:5648
+msgid "E774: 'operatorfunc' is empty"
+msgstr "E774: 'operatorfunc' 为空"
-#. found a mismatch: skip
-#: ../option.c:2698
+#: ../option.c:107
msgid "E518: Unknown option"
msgstr "E518: 未知的选项"
-#: ../option.c:2709
-msgid "E519: Option not supported"
-msgstr "E519: 不支持该选项"
-
-#: ../option.c:2740
+#: ../option.c:109
msgid "E520: Not allowed in a modeline"
msgstr "E520: 不允许在 modeline 中使用"
-#: ../option.c:2815
+#: ../option.c:111
+msgid "E992: Not allowed in a modeline when 'modelineexpr' is off"
+msgstr "E992: 当 'modelineexpr' 关闭时不允许在 modeline 中使用"
+
+#: ../option.c:113
msgid "E846: Key code not set"
msgstr "E846: 未设置键位代码"
-#: ../option.c:2924
+#: ../option.c:115
msgid "E521: Number required after ="
msgstr "E521: = 后面需要数字"
-#: ../option.c:3226 ../option.c:3864
-msgid "E522: Not found in termcap"
-msgstr "E522: Termcap 里面找不到"
-
-#: ../option.c:3335
-#, c-format
-msgid "E539: Illegal character <%s>"
-msgstr "E539: 无效的字符 <%s>"
-
-#: ../option.c:3862
-msgid "E529: Cannot set 'term' to empty string"
-msgstr "E529: 不能设定 'term' 为空字符串"
-
-#: ../option.c:3885
-msgid "E589: 'backupext' and 'patchmode' are equal"
-msgstr "E589: 'backupext' 和 'patchmode' 相等"
-
-#: ../option.c:3964
-msgid "E834: Conflicts with value of 'listchars'"
-msgstr "E834: 与'listchars'中的值发生冲突"
-
-#: ../option.c:3966
-msgid "E835: Conflicts with value of 'fillchars'"
-msgstr "E835: 与'fillchars'中的值冲突"
-
-#: ../option.c:4163
-msgid "E524: Missing colon"
-msgstr "E524: 缺少冒号"
-
-#: ../option.c:4165
-msgid "E525: Zero length string"
-msgstr "E525: 字符串长度为零"
-
-#: ../option.c:4220
-#, c-format
-msgid "E526: Missing number after <%s>"
-msgstr "E526: <%s> 后面缺少数字"
-
-#: ../option.c:4232
-msgid "E527: Missing comma"
-msgstr "E527: 缺少逗号"
-
-#: ../option.c:4239
-msgid "E528: Must specify a ' value"
-msgstr "E528: 必须指定一个 ' 值"
-
-#: ../option.c:4271
-msgid "E595: contains unprintable or wide character"
-msgstr "E595: 包含不可显示字符或宽字符"
-
-#: ../option.c:4469
-#, c-format
-msgid "E535: Illegal character after <%c>"
-msgstr "E535: <%c> 后面有无效的字符"
-
-#: ../option.c:4534
-msgid "E536: comma required"
-msgstr "E536: 需要逗号"
-
-#: ../option.c:4543
-#, c-format
-msgid "E537: 'commentstring' must be empty or contain %s"
-msgstr "E537: 'commentstring' 必须为空或包含 %s"
-
-#: ../option.c:4928
-msgid "E540: Unclosed expression sequence"
-msgstr "E540: 没有结束的表达式序列"
-
-#: ../option.c:4932
-msgid "E541: too many items"
-msgstr "E541: 项目过多"
-
-#: ../option.c:4934
-msgid "E542: unbalanced groups"
-msgstr "E542: 错乱的组"
-
-#: ../option.c:5148
+#: ../option.c:117
msgid "E590: A preview window already exists"
msgstr "E590: 预览窗口已存在"
-#: ../option.c:5311
+#: ../option.c:2116
msgid "W17: Arabic requires UTF-8, do ':set encoding=utf-8'"
msgstr "W17: Arabic 需要 UTF-8,请执行 ':set encoding=utf-8'"
-#: ../option.c:5623
+#: ../option.c:2523
#, c-format
msgid "E593: Need at least %d lines"
msgstr "E593: 至少需要 %d 行"
-#: ../option.c:5631
+#: ../option.c:2531
#, c-format
msgid "E594: Need at least %d columns"
msgstr "E594: 至少需要 %d 列"
-#: ../option.c:6011
+#: ../option.c:3064 ../option.c:3616
#, c-format
msgid "E355: Unknown option: %s"
msgstr "E355: 未知的选项: %s"
#. 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.
-#: ../option.c:6037
-#, fuzzy, c-format
+#. is empty. In both cases, we are trying to set a
+#. num option using a string.
+#: ../option.c:3092
+#, c-format
msgid "E521: Number required: &%s = '%s'"
-msgstr "E521: = 后面需要数字"
-
-#: ../option.c:6149
-msgid ""
-"\n"
-"--- Terminal codes ---"
-msgstr ""
-"\n"
-"--- 终端编码 ---"
+msgstr "E521: 需要整数:&%s = '%s'"
-#: ../option.c:6151
+#: ../option.c:3198
msgid ""
"\n"
"--- Global option values ---"
@@ -4717,7 +5242,7 @@ msgstr ""
"\n"
"--- 全局选项值 ---"
-#: ../option.c:6153
+#: ../option.c:3200
msgid ""
"\n"
"--- Local option values ---"
@@ -4725,7 +5250,7 @@ msgstr ""
"\n"
"--- 局部选项值 ---"
-#: ../option.c:6155
+#: ../option.c:3202
msgid ""
"\n"
"--- Options ---"
@@ -4733,653 +5258,1070 @@ msgstr ""
"\n"
"--- 选项 ---"
-#: ../option.c:6816
+#: ../option.c:4096
msgid "E356: get_varp ERROR"
msgstr "E356: get_varp 错误"
-#: ../option.c:7696
+#: ../optionstr.c:62
+msgid "E540: Unclosed expression sequence"
+msgstr "E540: 没有结束的表达式序列"
+
+#: ../optionstr.c:64
+msgid "E542: unbalanced groups"
+msgstr "E542: 错乱的组"
+
+#: ../optionstr.c:66
+msgid "E589: 'backupext' and 'patchmode' are equal"
+msgstr "E589: 'backupext' 和 'patchmode' 相等"
+
+#: ../optionstr.c:68
+msgid "E595: 'showbreak' contains unprintable or wide character"
+msgstr "E595: 'showbreak' 包含不可显示字符或宽字符"
+
+#: ../optionstr.c:205
#, c-format
-msgid "E357: 'langmap': Matching character missing for %s"
-msgstr "E357: 'langmap': 找不到 %s 对应的字符"
+msgid "E539: Illegal character <%s>"
+msgstr "E539: 无效的字符 <%s>"
-#: ../option.c:7715
+#: ../optionstr.c:337
#, c-format
-msgid "E358: 'langmap': Extra characters after semicolon: %s"
-msgstr "E358: 'langmap': 分号后有多余的字符: %s"
+msgid "For option %s"
+msgstr "对选项 %s"
-#: ../os/shell.c:194
-msgid ""
-"\n"
-"Cannot execute shell "
+#: ../optionstr.c:959
+msgid "E524: Missing colon"
+msgstr "E524: 缺少冒号"
+
+#: ../optionstr.c:961
+msgid "E525: Zero length string"
+msgstr "E525: 字符串长度为零"
+
+#: ../optionstr.c:1040
+#, c-format
+msgid "E526: Missing number after <%s>"
+msgstr "E526: <%s> 后面缺少数字"
+
+#: ../optionstr.c:1053
+msgid "E527: Missing comma"
+msgstr "E527: 缺少逗号"
+
+#: ../optionstr.c:1061
+msgid "E528: Must specify a ' value"
+msgstr "E528: 必须指定一个 ' 值"
+
+#: ../optionstr.c:1243
+#, c-format
+msgid "E535: Illegal character after <%c>"
+msgstr "E535: <%c> 后面有无效的字符"
+
+#: ../optionstr.c:1351
+msgid "E536: comma required"
+msgstr "E536: 需要逗号"
+
+#: ../optionstr.c:1359
+#, c-format
+msgid "E537: 'commentstring' must be empty or contain %s"
+msgstr "E537: 'commentstring' 必须为空或包含 %s"
+
+# do not translate
+#: ../os/dl.c:53 ../os/dl.c:61
+#, c-format
+msgid "dlerror = \"%s\""
msgstr ""
-"\n"
-"无法执行 shell"
-#: ../os/shell.c:439
+#: ../os/fileio.c:434
+#, c-format
+msgid "E5420: Failed to write to file: %s"
+msgstr "E5420: 无法写入文件:%s"
+
+#: ../os/input.c:564
+msgid "Vim: Error reading input, exiting...\n"
+msgstr "Vim:读取输入时出错,正在退出……\n"
+
+#: ../os/shell.c:696
msgid ""
"\n"
"shell returned "
msgstr ""
"\n"
-"Shell 已返回"
+"Shell 返回了 "
-#: ../os_unix.c:465 ../os_unix.c:471
+#: ../os/shell.c:878
msgid ""
"\n"
-"Could not get security context for "
+"shell failed to start: "
msgstr ""
-
-#: ../os_unix.c:479
-msgid ""
"\n"
-"Could not set security context for "
-msgstr ""
+"Shell 无法启动:"
-# do not translate
-#: ../os_unix.c:1558 ../os_unix.c:1647
+#. Can happen if system() tries to send input to a shell command that was
+#. backgrounded (:call system("cat - &", "foo")). #3529 #5241
+#: ../os/shell.c:1316
#, c-format
-msgid "dlerror = \"%s\""
-msgstr "dlerror = \"%s\""
+msgid "E5677: Error writing input to shell-command: %s"
+msgstr "E5677: 向 Shell 命令输入时出错:%s"
+
+#: ../os/time.c:191
+msgid "%a %b %d %H:%M:%S %Y"
+msgstr "%Y-%m-%d %H:%M:%S"
-#: ../path.c:1449
+#: ../path.c:1711
#, c-format
msgid "E447: Can't find file \"%s\" in path"
msgstr "E447: 在路径中找不到文件 \"%s\""
-#: ../quickfix.c:359
+#: ../profile.c:307
+msgid "E750: First use \":profile start {fname}\""
+msgstr "E750: 请先使用 \":profile start {fname}\""
+
+#: ../quickfix.c:246
+msgid "E553: No more items"
+msgstr "E553: 没有更多的项"
+
+#: ../quickfix.c:278
+msgid "E925: Current quickfix list was changed"
+msgstr "E925: 当前 quickfix 列表已改变"
+
+#: ../quickfix.c:280
+msgid "E926: Current location list was changed"
+msgstr "E926: 当前位置列表已改变"
+
+#. Each errorformat pattern can occur only once
+#: ../quickfix.c:382
#, c-format
msgid "E372: Too many %%%c in format string"
msgstr "E372: 格式化字符串里有太多 %%%c "
-#: ../quickfix.c:371
+#: ../quickfix.c:389
#, c-format
msgid "E373: Unexpected %%%c in format string"
msgstr "E373: 格式化字符串不应该出现 %%%c "
-#: ../quickfix.c:420
+#: ../quickfix.c:447
msgid "E374: Missing ] in format string"
-msgstr "E374: 格式化字符串里少了 ]"
+msgstr "E374: 格式化字符串中缺少 ]"
-#: ../quickfix.c:431
+#: ../quickfix.c:457
#, c-format
msgid "E375: Unsupported %%%c in format string"
msgstr "E375: 格式化字符串里有不支持的 %%%c "
-#: ../quickfix.c:448
+#: ../quickfix.c:476
#, c-format
msgid "E376: Invalid %%%c in format string prefix"
msgstr "E376: 格式化字符串开头里有不正确的 %%%c "
-#: ../quickfix.c:454
+#: ../quickfix.c:526
#, c-format
msgid "E377: Invalid %%%c in format string"
msgstr "E377: 格式化字符串里有不正确的 %%%c "
#. nothing found
-#: ../quickfix.c:477
+#: ../quickfix.c:629
msgid "E378: 'errorformat' contains no pattern"
msgstr "E378: 'errorformat' 未设定"
-#: ../quickfix.c:695
+#: ../quickfix.c:1564
msgid "E379: Missing or empty directory name"
msgstr "E379: 找不到目录名称或是空的目录名称"
-#: ../quickfix.c:1305
-msgid "E553: No more items"
-msgstr "E553: 没有更多的项"
+#: ../quickfix.c:2737
+msgid "E924: Current window was closed"
+msgstr "E924: 当前窗口已关闭"
-#: ../quickfix.c:1674
+#: ../quickfix.c:2809
#, c-format
msgid "(%d of %d)%s%s: "
msgstr "(%d / %d)%s%s: "
-#: ../quickfix.c:1676
+#: ../quickfix.c:2811
msgid " (line deleted)"
msgstr " (行已删除)"
-#: ../quickfix.c:1863
+#: ../quickfix.c:3252
+#, c-format
+msgid "%serror list %d of %d; %d errors "
+msgstr "%s 错误列表 %d / %d;共 %d 个错误"
+
+#: ../quickfix.c:3287
msgid "E380: At bottom of quickfix stack"
msgstr "E380: Quickfix 堆栈底端"
-#: ../quickfix.c:1869
+#: ../quickfix.c:3293
msgid "E381: At top of quickfix stack"
msgstr "E381: Quickfix 堆栈顶端"
-#: ../quickfix.c:1880
-#, c-format
-msgid "error list %d of %d; %d errors"
-msgstr "错误列表 %d / %d;共 %d 个错误"
+#: ../quickfix.c:3327
+msgid "No entries"
+msgstr "没有项目"
-#: ../quickfix.c:2427
-msgid "E382: Cannot write, 'buftype' option is set"
-msgstr "E382: 无法写入,已设定选项 'buftype'"
-
-#: ../quickfix.c:2812
+#: ../quickfix.c:5335
msgid "E683: File name missing or invalid pattern"
msgstr "E683: 缺少文件名或模式无效"
-#: ../quickfix.c:2911
+#: ../quickfix.c:5398
#, c-format
msgid "Cannot open file \"%s\""
msgstr "无法打开文件 \"%s\""
-#: ../quickfix.c:3429
+#: ../quickfix.c:6680
+msgid "cannot have both a list and a \"what\" argument"
+msgstr "不能同时有列表和 \"what\" 参数"
+
+#: ../quickfix.c:6796
msgid "E681: Buffer is not loaded"
msgstr "E681: 缓冲区未加载"
-#: ../quickfix.c:3487
+#: ../quickfix.c:6959
msgid "E777: String or List expected"
-msgstr "E777: 此处需要 String 或者 List"
+msgstr "E777: 此处需要字符串或列表"
-#: ../regexp.c:359
+#: ../quickfix.c:7246
+#, c-format
+msgid "E927: Invalid action: '%s'"
+msgstr "E927: 动作无效:'%s'"
+
+#: ../regexp.c:103
#, c-format
msgid "E369: invalid item in %s%%[]"
msgstr "E369: %s%%[] 中有无效的项"
-#: ../regexp.c:374
+#: ../regexp.c:107
#, c-format
msgid "E769: Missing ] after %s["
msgstr "E769: %s[ 后缺少 ]"
-#: ../regexp.c:375
+#: ../regexp.c:108
+msgid "E944: Reverse range in character class"
+msgstr "E944: 字符类中有逆向范围"
+
+#: ../regexp.c:109
+msgid "E945: Range too large in character class"
+msgstr "E945: 字符类中的范围太大了"
+
+#: ../regexp.c:110
#, c-format
msgid "E53: Unmatched %s%%("
msgstr "E53: 不匹配的 %s%%("
-#: ../regexp.c:376
+#: ../regexp.c:111
#, c-format
msgid "E54: Unmatched %s("
msgstr "E54: 不匹配的 %s("
-#: ../regexp.c:377
+#: ../regexp.c:112
#, c-format
msgid "E55: Unmatched %s)"
msgstr "E55: 不匹配的 %s)"
-#: ../regexp.c:378
+#: ../regexp.c:113
msgid "E66: \\z( not allowed here"
msgstr "E66: 此处不允许 \\z("
-#: ../regexp.c:379
-msgid "E67: \\z1 et al. not allowed here"
-msgstr "E67: 此处不允许 \\z1 等"
+#: ../regexp.c:114
+msgid "E67: \\z1 - \\z9 not allowed here"
+msgstr "E67: 此处不允许 \\z1 - \\z9"
-#: ../regexp.c:380
+#: ../regexp.c:115
#, c-format
msgid "E69: Missing ] after %s%%["
msgstr "E69: %s%%[ 后缺少 ]"
-#: ../regexp.c:381
+#: ../regexp.c:116
#, c-format
msgid "E70: Empty %s%%[]"
msgstr "E70: 空的 %s%%[]"
-#: ../regexp.c:1209 ../regexp.c:1224
-msgid "E339: Pattern too long"
-msgstr "E339: 模式太长"
-
-#: ../regexp.c:1371
-msgid "E50: Too many \\z("
-msgstr "E50: 太多 \\z("
+#: ../regexp.c:117
+msgid "E956: Cannot use pattern recursively"
+msgstr "E956: 不能递归地使用模式"
-#: ../regexp.c:1378
+#: ../regexp.c:119
#, c-format
-msgid "E51: Too many %s("
-msgstr "E51: 太多 %s("
+msgid "E1204: No Number allowed after .: '\\%%%c'"
+msgstr "E1204: . 后不能加整数: '\\%%%c'"
-#: ../regexp.c:1427
-msgid "E52: Unmatched \\z("
-msgstr "E52: 不匹配的 \\z("
-
-#: ../regexp.c:1637
+#: ../regexp.c:121
#, c-format
-msgid "E59: invalid character after %s@"
-msgstr "E59: %s@ 后面有无效的字符"
+msgid "E1273: (NFA regexp) missing value in '\\%%%c'"
+msgstr "E1273: (NFA 正则) '\\%%%c' 中缺少值"
-#: ../regexp.c:1672
+#: ../regexp.c:123
#, c-format
-msgid "E60: Too many complex %s{...}s"
-msgstr "E60: 太多复杂的 %s{...}s"
+msgid "E1281: Atom '\\%%#=%c' must be at the start of the pattern"
+msgstr "E1281: 原子 '\\%%#=%c' 必须位于模式的开头"
-#: ../regexp.c:1687
-#, c-format
-msgid "E61: Nested %s*"
-msgstr "E61: 嵌套的 %s*"
+#: ../regexp.c:124
+msgid "E1290: substitute nesting too deep"
+msgstr "E1290: 替换嵌套层数过深"
-#: ../regexp.c:1690
+#: ../regexp.c:498
#, c-format
-msgid "E62: Nested %s%c"
-msgstr "E62: 嵌套的 %s%c"
+msgid "E654: missing delimiter after search pattern: %s"
+msgstr "E654: 搜索模式后缺少分割符:%s"
-#: ../regexp.c:1800
-msgid "E63: invalid use of \\_"
-msgstr "E63: 不正确地使用 \\_"
+#: ../regexp.c:919
+#, c-format
+msgid "E554: Syntax error in %s{...}"
+msgstr "E554: %s{...} 中语法错误"
-#: ../regexp.c:1850
+#: ../regexp.c:1294
#, c-format
-msgid "E64: %s%c follows nothing"
-msgstr "E64: %s%c 前面无内容"
+msgid "E888: (NFA regexp) cannot repeat %s"
+msgstr "E888: (NFA regexp) 不能重复 %s"
-#: ../regexp.c:1902
-msgid "E65: Illegal back reference"
-msgstr "E65: 无效的回引"
+#: ../regexp.c:2302
+msgid ""
+"E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be "
+"used "
+msgstr "E864: \\%#= 后面只能是0,1,或者2。自动引擎将会被使用"
-#: ../regexp.c:1943
-msgid "E68: Invalid character after \\z"
-msgstr "E68: \\z 后面有无效的字符"
+#: ../regexp.c:2383
+msgid "Switching to backtracking RE engine for pattern: "
+msgstr "为此模式切换到回溯正则引擎:"
-#: ../regexp.c:2049 ../regexp_nfa.c:1296
+#: ../runtime.c:266
#, c-format
-msgid "E678: Invalid character after %s%%[dxouU]"
-msgstr "E678: %s%%[dxouU] 后面有无效的字符"
+msgid "Searching for \"%s\" in \"%s\""
+msgstr "正在查找 \"%s\",在 \"%s\" 中"
-#: ../regexp.c:2107
+#: ../runtime.c:303 ../runtime.c:436
#, c-format
-msgid "E71: Invalid character after %s%%"
-msgstr "E71: %s%% 后面有无效的字符"
+msgid "Searching for \"%s\""
+msgstr "正在查找 \"%s\""
-#: ../regexp.c:3017
+#: ../runtime.c:334
#, c-format
-msgid "E554: Syntax error in %s{...}"
-msgstr "E554: %s{...} 中语法错误"
+msgid "not found in '%s': \"%s\""
+msgstr "在 '%s' 中找不到:\"%s\""
-#: ../regexp.c:3805
-msgid "External submatches:\n"
-msgstr "外部符合:\n"
+#: ../runtime.c:400
+#, c-format
+msgid "Searching for \"%s\" in runtime path"
+msgstr "正在查找 \"%s\",在 runtime path 中"
-#: ../regexp.c:7022
-msgid ""
-"E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be "
-"used "
-msgstr ""
-"E864: \\%#= 后面只能是0,1,或者2。自动引擎将会被使用"
+#: ../runtime.c:464
+#, c-format
+msgid "not found in runtime path: \"%s\""
+msgstr "在 runtime path 中找不到:\"%s\""
-#: ../regexp_nfa.c:239
-msgid "E865: (NFA) Regexp end encountered prematurely"
-msgstr "E865: (NFA) 过早地遇到了正则表达式的结尾"
+#: ../runtime.c:1912
+#, c-format
+msgid "Cannot source a directory: \"%s\""
+msgstr "不能执行目录: \"%s\""
-#: ../regexp_nfa.c:240
+#: ../runtime.c:1947
#, c-format
-msgid "E866: (NFA regexp) Misplaced %c"
-msgstr "E866: (NFA regexp) %c 放错了位置"
+msgid "could not source \"%s\""
+msgstr "不能执行 \"%s\""
-#: ../regexp_nfa.c:242
+#: ../runtime.c:1949
#, c-format
-msgid "E877: (NFA regexp) Invalid character class: %<PRId64>"
-msgstr "E877: (NFA regexp) 不可用的字符类: %<PRId64>"
+msgid "line %<PRId64>: could not source \"%s\""
+msgstr "第 %<PRId64> 行: 不能执行 \"%s\""
-#: ../regexp_nfa.c:1261
+#: ../runtime.c:1963
#, c-format
-msgid "E867: (NFA) Unknown operator '\\z%c'"
-msgstr "E867: (NFA) 未知的操作符 '\\z%c'"
+msgid "sourcing \"%s\""
+msgstr "执行 \"%s\""
-#: ../regexp_nfa.c:1387
+#: ../runtime.c:1965
#, c-format
-msgid "E867: (NFA) Unknown operator '\\%%%c'"
-msgstr "E867: (NFA) 未知的操作符 '\\%%%c'"
+msgid "line %<PRId64>: sourcing \"%s\""
+msgstr "第 %<PRId64> 行: 执行 \"%s\""
-#: ../regexp_nfa.c:1802
+#: ../runtime.c:2081
#, c-format
-msgid "E869: (NFA) Unknown operator '\\@%c'"
-msgstr "E869: (NFA) 未知的操作符 '\\@%c'"
+msgid "finished sourcing %s"
+msgstr "结束执行 %s"
-#: ../regexp_nfa.c:1831
-msgid "E870: (NFA regexp) Error reading repetition limits"
-msgstr "E870: (NFA regexp) 读取重复限制时出错"
+#: ../runtime.c:2210
+msgid "modeline"
+msgstr "modeline"
-#. Can't have a multi follow a multi.
-#: ../regexp_nfa.c:1895
-msgid "E871: (NFA regexp) Can't have a multi follow a multi !"
-msgstr "E871: (NFA regexp) 不能多个跟多个!"
+#: ../runtime.c:2212
+msgid "--cmd argument"
+msgstr "--cmd 参数"
-#. Too many `('
-#: ../regexp_nfa.c:2037
-msgid "E872: (NFA regexp) Too many '('"
-msgstr "E872: (NFA regexp) 太多 '('"
+#: ../runtime.c:2214
+msgid "-c argument"
+msgstr "-c 参数"
-#: ../regexp_nfa.c:2042
-#, fuzzy
-msgid "E879: (NFA regexp) Too many \\z("
-msgstr "E50: 太多 \\z("
+#: ../runtime.c:2216
+msgid "environment variable"
+msgstr "环境变量"
-#: ../regexp_nfa.c:2066
-msgid "E873: (NFA regexp) proper termination error"
-msgstr "E873: (NFA regexp) 未适当终止"
+#: ../runtime.c:2218
+msgid "error handler"
+msgstr "错误的处理程序"
-#: ../regexp_nfa.c:2599
-msgid "E874: (NFA) Could not pop the stack !"
-msgstr "E874: (NFA) 无法出栈!"
+#: ../runtime.c:2220
+msgid "changed window size"
+msgstr "改变了窗口大小"
-#: ../regexp_nfa.c:3298
-msgid ""
-"E875: (NFA regexp) (While converting from postfix to NFA), too many states "
-"left on stack"
-msgstr "E875: (NFA regexp) (从后缀转换到 NFA 时),栈上遗留了太多状态"
+#: ../runtime.c:2222
+msgid "Lua"
+msgstr "Lua"
-#: ../regexp_nfa.c:3302
-msgid "E876: (NFA regexp) Not enough space to store the whole NFA "
-msgstr "E876: (NFA regexp) 没有足够的空间存储整个NFA "
+#: ../runtime.c:2224
+#, c-format
+msgid "API client (channel id %<PRIu64>)"
+msgstr ""
-#: ../regexp_nfa.c:4571 ../regexp_nfa.c:4869
-msgid ""
-"Could not open temporary log file for writing, displaying on stderr ... "
+#: ../runtime.c:2227
+msgid "anonymous :source"
msgstr ""
-"无法打开临时日志文件进行写入,显示在 stderr 中..."
-#: ../regexp_nfa.c:4840
+#: ../runtime.c:2231
#, c-format
-msgid "(NFA) COULD NOT OPEN %s !"
-msgstr "(NFA) 不能打开 %s !"
+msgid "anonymous :source (script id %d)"
+msgstr ""
-#: ../regexp_nfa.c:6049
-#, fuzzy
-msgid "Could not open temporary log file for writing "
-msgstr "E214: 找不到用于写入的临时文件"
+#: ../runtime.c:2417
+msgid "W15: Warning: Wrong line separator, ^M may be missing"
+msgstr "W15: 警告: 错误的行分隔符,可能是少了 ^M"
+
+#: ../runtime.c:2457
+msgid "E167: :scriptencoding used outside of a sourced file"
+msgstr "E167: 在脚本文件外使用了 :scriptencoding"
-#: ../screen.c:7435
+#: ../runtime.c:2482
+msgid "E168: :finish used outside of a sourced file"
+msgstr "E168: 在脚本文件外使用了 :finish"
+
+#: ../screen.c:57
+msgid "E834: Conflicts with value of 'listchars'"
+msgstr "E834: 与'listchars'中的值发生冲突"
+
+#: ../screen.c:58
+msgid "E835: Conflicts with value of 'fillchars'"
+msgstr "E835: 与'fillchars'中的值冲突"
+
+#: ../screen.c:521
+msgid " TERMINAL"
+msgstr " 终端"
+
+#: ../screen.c:523
msgid " VREPLACE"
msgstr " V-替换"
-#: ../screen.c:7437
+#: ../screen.c:525
msgid " REPLACE"
msgstr " 替换"
-#: ../screen.c:7440
+#: ../screen.c:528
msgid " REVERSE"
msgstr " 反向"
-#: ../screen.c:7441
+#: ../screen.c:530
msgid " INSERT"
msgstr " 插入"
-#: ../screen.c:7443
+#: ../screen.c:534
+msgid " (terminal)"
+msgstr " (终端)"
+
+#: ../screen.c:536
msgid " (insert)"
msgstr " (插入)"
-#: ../screen.c:7445
+#: ../screen.c:539
msgid " (replace)"
msgstr " (替换)"
-#: ../screen.c:7447
+#: ../screen.c:541
msgid " (vreplace)"
msgstr " (V-替换)"
-#: ../screen.c:7449
+#: ../screen.c:544
msgid " Hebrew"
-msgstr " Hebrew"
+msgstr " 希伯来文"
-#: ../screen.c:7454
+#: ../screen.c:548
msgid " Arabic"
-msgstr " Arabic"
-
-#: ../screen.c:7456
-msgid " (lang)"
-msgstr " (语言)"
+msgstr " 阿拉伯文"
-#: ../screen.c:7459
+#: ../screen.c:555
msgid " (paste)"
msgstr " (粘帖)"
-#: ../screen.c:7469
+#: ../screen.c:567
msgid " VISUAL"
msgstr " 可视"
-#: ../screen.c:7470
+#: ../screen.c:569
msgid " VISUAL LINE"
msgstr " 可视 行"
-#: ../screen.c:7471
+#: ../screen.c:571
msgid " VISUAL BLOCK"
msgstr " 可视 块"
-#: ../screen.c:7472
+#: ../screen.c:573
msgid " SELECT"
msgstr " 选择"
-#: ../screen.c:7473
+#: ../screen.c:575
msgid " SELECT LINE"
msgstr " 选择 行"
-#: ../screen.c:7474
+#: ../screen.c:577
msgid " SELECT BLOCK"
msgstr " 选择 块"
-#: ../screen.c:7486 ../screen.c:7541
+#: ../screen.c:673
msgid "recording"
msgstr "记录中"
-#: ../search.c:487
+#: ../search.c:563
#, c-format
msgid "E383: Invalid search string: %s"
msgstr "E383: 无效的查找字符串: %s"
-#: ../search.c:832
+#: ../search.c:928
#, c-format
msgid "E384: search hit TOP without match for: %s"
msgstr "E384: 已查找到文件开头仍找不到 %s"
-#: ../search.c:835
+#: ../search.c:931
#, c-format
msgid "E385: search hit BOTTOM without match for: %s"
msgstr "E385: 已查找到文件结尾仍找不到 %s"
-#: ../search.c:1200
+#: ../search.c:1382
msgid "E386: Expected '?' or '/' after ';'"
msgstr "E386: 在 ';' 后面应该有 '?' 或 '/'"
-#: ../search.c:4085
+#: ../search.c:3540
msgid " (includes previously listed match)"
msgstr " (包括上次列出符合项)"
#. cursor at status line
-#: ../search.c:4104
+#: ../search.c:3557
msgid "--- Included files "
msgstr "--- 包含文件 "
-#: ../search.c:4106
+#: ../search.c:3559
msgid "not found "
msgstr "找不到 "
-#: ../search.c:4107
+#: ../search.c:3561
msgid "in path ---\n"
msgstr "在路径 ---\n"
-#: ../search.c:4168
+#: ../search.c:3620
msgid " (Already listed)"
msgstr " (已列出)"
-#: ../search.c:4170
+#: ../search.c:3622
msgid " NOT FOUND"
msgstr " 找不到"
-#: ../search.c:4211
+#: ../search.c:3664
#, c-format
msgid "Scanning included file: %s"
msgstr "查找包含文件: %s"
-#: ../search.c:4216
+#: ../search.c:3669
#, c-format
msgid "Searching included file %s"
msgstr "查找包含的文件 %s"
-#: ../search.c:4405
+#: ../search.c:3862
msgid "E387: Match is on current line"
msgstr "E387: 当前行匹配"
-#: ../search.c:4517
+#: ../search.c:3988
msgid "All included files were found"
msgstr "所有包含文件都已找到"
-#: ../search.c:4519
+#: ../search.c:3990
msgid "No included files"
msgstr "没有包含文件"
-#: ../search.c:4527
+#: ../search.c:3998
msgid "E388: Couldn't find definition"
msgstr "E388: 找不到定义"
-#: ../search.c:4529
+#: ../search.c:4000
msgid "E389: Couldn't find pattern"
msgstr "E389: 找不到 pattern"
-#: ../search.c:4668
-#, fuzzy
-msgid "Substitute "
-msgstr "1 次替换,"
+#: ../shada.c:699
+msgid "too few bytes read"
+msgstr ""
+
+#: ../shada.c:721
+#, c-format
+msgid "System error while skipping in ShaDa file: %s"
+msgstr ""
+
+#: ../shada.c:725 ../shada.c:3271
+#, c-format
+msgid ""
+"Error while reading ShaDa file: last entry specified that it occupies "
+"%<PRIu64> bytes, but file ended earlier"
+msgstr ""
+
+#: ../shada.c:770
+#, c-format
+msgid "System error while closing ShaDa file: %s"
+msgstr ""
+
+#: ../shada.c:805
+#, c-format
+msgid "System error while writing ShaDa file: %s"
+msgstr ""
+
+#: ../shada.c:841
+#, c-format
+msgid "Reading ShaDa file \"%s\"%s%s%s%s"
+msgstr "读取 ShaDa 文件 \"%s\"%s%s%s%s"
+
+#: ../shada.c:843
+msgid " info"
+msgstr " 信息"
+
+#: ../shada.c:844
+msgid " marks"
+msgstr " 标记"
+
+#: ../shada.c:845
+msgid " oldfiles"
+msgstr " 旧文件"
+
+#: ../shada.c:846
+msgid " FAILED"
+msgstr " 失败"
+
+#: ../shada.c:852
+#, c-format
+msgid "System error while opening ShaDa file %s for reading: %s"
+msgstr ""
+
+#: ../shada.c:1536
+msgid "additional elements of ShaDa "
+msgstr ""
-#: ../search.c:4681
+#: ../shada.c:1556
+msgid "additional data of ShaDa "
+msgstr ""
+
+#: ../shada.c:1625
+#, c-format
+msgid "Failed to write variable %s"
+msgstr ""
+
+#: ../shada.c:1914
+#, c-format
+msgid ""
+"Failed to parse ShaDa file due to a msgpack parser error at position "
+"%<PRIu64>"
+msgstr ""
+
+#: ../shada.c:1929
+#, c-format
+msgid ""
+"Failed to parse ShaDa file: incomplete msgpack string at position %<PRIu64>"
+msgstr ""
+
+#: ../shada.c:1936
+#, c-format
+msgid ""
+"Failed to parse ShaDa file: extra bytes in msgpack string at position "
+"%<PRIu64>"
+msgstr ""
+
+#: ../shada.c:2989
+#, c-format
+msgid ""
+"System error while opening ShaDa file %s for reading to merge before writing "
+"it: %s"
+msgstr ""
+
+#. Tried names from .tmp.a to .tmp.z, all failed. Something must be
+#. wrong then.
+#: ../shada.c:3021
+#, c-format
+msgid "E138: All %s.tmp.X files exist, cannot write ShaDa file!"
+msgstr ""
+
+#: ../shada.c:3032
+#, c-format
+msgid "System error while opening temporary ShaDa file %s for writing: %s"
+msgstr ""
+
+#: ../shada.c:3047
+#, c-format
+msgid "Failed to create directory %s for writing ShaDa file: %s"
+msgstr ""
+
+#: ../shada.c:3061
+#, c-format
+msgid "System error while opening ShaDa file %s for writing: %s"
+msgstr ""
+
+#: ../shada.c:3077
+#, c-format
+msgid "Writing ShaDa file \"%s\""
+msgstr "写入 ShaDa 文件 \"%s\""
+
+#: ../shada.c:3104
+#, c-format
+msgid "E137: ShaDa file is not writable: %s"
+msgstr "E137: ShaDa 文件不可写入:%s"
+
+#: ../shada.c:3116
+#, c-format
+msgid "Failed setting uid and gid for file %s: %s"
+msgstr "无法为文件 %s 设置 uid 和 gid:%s"
+
+#: ../shada.c:3124
+#, c-format
+msgid "Can't rename ShaDa file from %s to %s!"
+msgstr ""
+
+#: ../shada.c:3132
+#, c-format
+msgid "Did not rename %s because %s does not look like a ShaDa file"
+msgstr ""
+
+#: ../shada.c:3135
+#, c-format
+msgid "Did not rename %s to %s because there were errors during writing it"
+msgstr ""
+
+#: ../shada.c:3141
+#, c-format
+msgid "Do not forget to remove %s or rename it manually to %s."
+msgstr ""
+
+#: ../shada.c:3267
+#, c-format
+msgid "System error while reading ShaDa file: %s"
+msgstr ""
+
+#: ../shada.c:3304
+#, c-format
+msgid "System error while reading integer from ShaDa file: %s"
+msgstr ""
+
+#: ../shada.c:3308
#, c-format
msgid ""
+"Error while reading ShaDa file: expected positive integer at position "
+"%<PRIu64>, but got nothing"
+msgstr ""
+
+#: ../shada.c:3335
+#, c-format
+msgid ""
+"Error while reading ShaDa file: expected positive integer at position "
+"%<PRIu64>"
+msgstr ""
+
+#: ../shada.c:3530
+#, c-format
+msgid ""
+"Error while reading ShaDa file: there is an item at position %<PRIu64> that "
+"is stated to be too long"
+msgstr ""
+
+#. kSDItemUnknown cannot possibly pass that far because it is -1 and that
+#. will fail in msgpack_read_uint64. But kSDItemMissing may and it will
+#. otherwise be skipped because (1 << 0) will never appear in flags.
+#: ../shada.c:3544
+#, c-format
+msgid ""
+"Error while reading ShaDa file: there is an item at position %<PRIu64> that "
+"must not be there: Missing items are for internal uses only"
+msgstr ""
+
+#: ../shada.c:3914
+#, c-format
+msgid ""
+"Error while reading ShaDa file: buffer list at position %<PRIu64> contains "
+"entry that is not a dictionary"
+msgstr ""
+
+#: ../shada.c:3941
+#, c-format
+msgid ""
+"Error while reading ShaDa file: buffer list at position %<PRIu64> contains "
+"entry with invalid line number"
+msgstr ""
+
+#: ../shada.c:3948
+#, c-format
+msgid ""
+"Error while reading ShaDa file: buffer list at position %<PRIu64> contains "
+"entry with invalid column number"
+msgstr ""
+
+#: ../shada.c:3955
+#, c-format
+msgid ""
+"Error while reading ShaDa file: buffer list at position %<PRIu64> contains "
+"entry that does not have a file name"
+msgstr ""
+
+#: ../sign.c:292
+msgid "[Deleted]"
+msgstr "[已删除]"
+
+#: ../sign.c:709
+msgid ""
"\n"
-"# Last %sSearch Pattern:\n"
-"~"
+"--- Signs ---"
msgstr ""
"\n"
-"# 最后 %s搜索模式:\n"
-"~"
+"--- Signs ---"
-#: ../spell.c:951
+#: ../sign.c:718
+#, c-format
+msgid "Signs for %s:"
+msgstr "%s 的 Signs:"
+
+#: ../sign.c:730
+#, c-format
+msgid " group=%s"
+msgstr " 组=%s"
+
+#: ../sign.c:736
+#, c-format
+msgid " line=%ld id=%d%s name=%s priority=%d"
+msgstr " 行=%ld id=%d%s 名称=%s 优先级=%d"
+
+#: ../sign.c:879
+msgid "E612: Too many signs defined"
+msgstr "E612: Signs 定义过多"
+
+#: ../sign.c:933
+#, c-format
+msgid "E239: Invalid sign text: %s"
+msgstr "E239: 无效的 sign 文字: %s"
+
+#: ../sign.c:1036 ../sign.c:1053 ../sign.c:1085
+#, c-format
+msgid "E155: Unknown sign: %s"
+msgstr "E155: 未知的 sign: %s"
+
+#: ../sign.c:1114
+#, c-format
+msgid "E885: Not possible to change sign %s"
+msgstr "E885: 不可能改变标号 %s"
+
+#: ../sign.c:1161
+msgid "E159: Missing sign number"
+msgstr "E159: 缺少 sign 号"
+
+#: ../sign.c:1171
+#, c-format
+msgid "E157: Invalid sign ID: %<PRId64>"
+msgstr "E157: 无效的 sign ID: %<PRId64>"
+
+#: ../sign.c:1182
+msgid "E934: Cannot jump to a buffer that does not have a name"
+msgstr "E934: 无法跳转到没有名字的缓冲区"
+
+#: ../sign.c:1476
+#, c-format
+msgid "E160: Unknown sign command: %s"
+msgstr "E160: 未知的 sign 命令: %s"
+
+#: ../sign.c:1489
+msgid "E156: Missing sign name"
+msgstr "E156: 缺少 sign 名称"
+
+#: ../sign.c:1676
+msgid " (not supported)"
+msgstr " (不支持)"
+
+#. mode values for find_word
+#. find word case-folded
+#. find keep-case word
+#. find word after prefix
+#. find case-folded compound word
+#. find keep-case compound word
+#: ../spell.c:194
msgid "E759: Format error in spell file"
msgstr "E759: 拼写文件格式错误"
-#: ../spell.c:952
+#: ../spell.c:1553
+#, c-format
+msgid "Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\""
+msgstr "警告: 找不到单词列表 \"%s.%s.spl\" or \"%s.ascii.spl\""
+
+#: ../spell.c:1968
+msgid "E797: SpellFileMissing autocommand deleted buffer"
+msgstr "E797: SpellFileMissing 自动命令删除了缓冲区"
+
+#. This is probably an error. Give a warning and
+#. accept the words anyway.
+#: ../spell.c:1992
+#, c-format
+msgid "Warning: region %s not supported"
+msgstr "警告: 区域 %s 不支持"
+
+#: ../spell.c:2579
+msgid "E752: No previous spell replacement"
+msgstr "E752: 之前没有拼写替换"
+
+#: ../spell.c:2624
+#, c-format
+msgid "E753: Not found: %s"
+msgstr "E753: 找不到: %s"
+
+#: ../spellfile.c:329
msgid "E758: Truncated spell file"
msgstr "E758: 已截断的拼写文件"
-#: ../spell.c:953
+#: ../spellfile.c:330
+msgid "E1280: Illegal character in word"
+msgstr "E1280: 词中有非法字符"
+
+#: ../spellfile.c:331
#, c-format
msgid "Trailing text in %s line %d: %s"
msgstr "%s 第 %d 行,多余的后续字符: %s"
-#: ../spell.c:954
+#: ../spellfile.c:332
#, c-format
msgid "Affix name too long in %s line %d: %s"
msgstr "%s 第 %d 行,附加项名字太长: %s"
-#: ../spell.c:955
-msgid "E761: Format error in affix file FOL, LOW or UPP"
-msgstr "E761: 附加文件 FOL、LOW 或 UPP 中格式错误"
-
-#: ../spell.c:957
-msgid "E762: Character in FOL, LOW or UPP is out of range"
-msgstr "E762: FOL、LOW 或 UPP 中字符超出范围"
-
-#: ../spell.c:958
+#: ../spellfile.c:333
msgid "Compressing word tree..."
msgstr "压缩单词树……"
-#: ../spell.c:1951
-msgid "E756: Spell checking is not enabled"
-msgstr "E756: 拼写检查未启用"
-
-#: ../spell.c:2249
-#, c-format
-msgid "Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\""
-msgstr "警告: 找不到单词列表 \"%s.%s.spl\" or \"%s.ascii.spl\""
-
-#: ../spell.c:2473
+#: ../spellfile.c:622
#, c-format
msgid "Reading spell file \"%s\""
msgstr "读取拼写文件 \"%s\""
-#: ../spell.c:2496
+#: ../spellfile.c:647
msgid "E757: This does not look like a spell file"
msgstr "E757: 这看起来不像是拼写文件"
-#: ../spell.c:2501
+#: ../spellfile.c:650
+#, c-format
+msgid "E5042: Failed to read spell file %s: %s"
+msgstr "E5042: 无法读取拼写文件 %s:%s"
+
+#: ../spellfile.c:658
msgid "E771: Old spell file, needs to be updated"
msgstr "E771: 旧版本的拼写文件,需要更新"
-#: ../spell.c:2504
+#: ../spellfile.c:661
msgid "E772: Spell file is for newer version of Vim"
msgstr "E772: 为更高版本的 Vim 所用的拼写文件"
-#: ../spell.c:2602
+#: ../spellfile.c:769
msgid "E770: Unsupported section in spell file"
msgstr "E770: 拼写文件中存在不支持的节"
-#: ../spell.c:3762
+#: ../spellfile.c:935
#, c-format
-msgid "Warning: region %s not supported"
-msgstr "警告: 区域 %s 不支持"
+msgid "E778: This does not look like a .sug file: %s"
+msgstr "E778: 看起来不像是 .sug 文件: %s"
-#: ../spell.c:4550
+#: ../spellfile.c:941
+#, c-format
+msgid "E779: Old .sug file, needs to be updated: %s"
+msgstr "E779: 旧的.sug 文件,需要更新: %s"
+
+#: ../spellfile.c:945
+#, c-format
+msgid "E780: .sug file is for newer version of Vim: %s"
+msgstr "E780: .sug 文件适用于较新的vim 版本: %s"
+
+#: ../spellfile.c:954
+#, c-format
+msgid "E781: .sug file doesn't match .spl file: %s"
+msgstr "E781: .sug 文件不能匹配 .spl 文件: %s"
+
+#: ../spellfile.c:964
+#, c-format
+msgid "E782: error while reading .sug file: %s"
+msgstr "E782: 读取 .sug 文件时出错:%s"
+
+#: ../spellfile.c:2067
#, c-format
msgid "Reading affix file %s..."
msgstr "读取附加文件 %s ……"
-#: ../spell.c:4589 ../spell.c:5635 ../spell.c:6140
+#: ../spellfile.c:2103 ../spellfile.c:3177
#, c-format
msgid "Conversion failure for word in %s line %d: %s"
msgstr "单词 %s 转换失败,第 %d 行: %s"
-#: ../spell.c:4630 ../spell.c:6170
+#: ../spellfile.c:2150 ../spellfile.c:3750
#, c-format
msgid "Conversion in %s not supported: from %s to %s"
msgstr "不支持 %s 中的转换: 从 %s 到 %s"
-#: ../spell.c:4642
+#: ../spellfile.c:2163
#, c-format
msgid "Invalid value for FLAG in %s line %d: %s"
msgstr "%s 第 %d 行,FLAG 的值无效: %s"
-#: ../spell.c:4655
+#: ../spellfile.c:2177
#, c-format
msgid "FLAG after using flags in %s line %d: %s"
msgstr "%s 第 %d 行,在使用标志后出现 FLAG: %s"
-#: ../spell.c:4723
+#: ../spellfile.c:2238
#, c-format
msgid ""
"Defining COMPOUNDFORBIDFLAG after PFX item may give wrong results in %s line "
"%d"
-msgstr "在 PFX 项之后定义 COMPOUNDFORBIDFLAG (%s 第%d行)可能会给出错误的结果"
-"%d"
+msgstr ""
+"在 PFX 项之后定义 COMPOUNDFORBIDFLAG (%s 第 %d 行)可能会给出错误的结果"
-#: ../spell.c:4731
+#: ../spellfile.c:2246
#, c-format
msgid ""
"Defining COMPOUNDPERMITFLAG after PFX item may give wrong results in %s line "
"%d"
-msgstr "在 PFX 项之后定义 COMPOUNDPERMITFLAG (%s 第%d行)可能会给出错误的结果"
-"%d"
+msgstr ""
+"在 PFX 项之后定义 COMPOUNDPERMITFLAG (%s 第 %d 行)可能会给出错误的结果"
-#: ../spell.c:4747
-#, fuzzy, c-format
+#: ../spellfile.c:2261
+#, c-format
msgid "Wrong COMPOUNDRULES value in %s line %d: %s"
-msgstr "%s 第 %d 行,错误的 COMPOUNDMIN 值: %s"
+msgstr "%s 第 %d 行,错误的 COMPOUNDRULES 值: %s"
-#: ../spell.c:4771
+#: ../spellfile.c:2285
#, c-format
msgid "Wrong COMPOUNDWORDMAX value in %s line %d: %s"
msgstr "%s 第 %d 行,错误的 COMPOUNDWORDMAX 值: %s"
-#: ../spell.c:4777
+#: ../spellfile.c:2292
#, c-format
msgid "Wrong COMPOUNDMIN value in %s line %d: %s"
msgstr "%s 第 %d 行,错误的 COMPOUNDMIN 值: %s"
-#: ../spell.c:4783
+#: ../spellfile.c:2299
#, c-format
msgid "Wrong COMPOUNDSYLMAX value in %s line %d: %s"
msgstr "%s 第 %d 行,错误的 COMPOUNDSYLMAX 值: %s"
-#: ../spell.c:4795
+#: ../spellfile.c:2312
#, c-format
msgid "Wrong CHECKCOMPOUNDPATTERN value in %s line %d: %s"
msgstr "%s 第 %d 行,错误的 CHECKCOMPOUNDPATTERN 值: %s"
-#: ../spell.c:4847
+#: ../spellfile.c:2368
#, c-format
msgid "Different combining flag in continued affix block in %s line %d: %s"
msgstr "%s 第 %d 行,在连续的附加块中出现不同的组合标志: %s"
-#: ../spell.c:4850
+#: ../spellfile.c:2372
#, c-format
msgid "Duplicate affix in %s line %d: %s"
msgstr "%s 第 %d 行,重复的附加项: %s"
-#: ../spell.c:4871
+#: ../spellfile.c:2391
#, c-format
msgid ""
"Affix also used for BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST in %s "
@@ -5388,334 +6330,337 @@ msgstr ""
"%s 第 %d 行,附加项被 BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST 使"
"用: %s"
-#: ../spell.c:4893
+#: ../spellfile.c:2421
#, c-format
msgid "Expected Y or N in %s line %d: %s"
msgstr "%s 第 %d 行,此处需要 Y 或 N: %s"
-#: ../spell.c:4968
+#: ../spellfile.c:2497
#, c-format
msgid "Broken condition in %s line %d: %s"
msgstr "%s 第 %d 行,错误的条件: %s"
-#: ../spell.c:5091
+#: ../spellfile.c:2613
#, c-format
msgid "Expected REP(SAL) count in %s line %d"
msgstr "%s 第 %d 行,此处需要 REP(SAL) 计数"
-#: ../spell.c:5120
+#: ../spellfile.c:2648
#, c-format
msgid "Expected MAP count in %s line %d"
msgstr "%s 第 %d 行,此处需要 MAP 计数"
-#: ../spell.c:5132
+#: ../spellfile.c:2661
#, c-format
msgid "Duplicate character in MAP in %s line %d"
msgstr "%s 第 %d 行,MAP 中存在重复的字符"
-#: ../spell.c:5176
+#: ../spellfile.c:2706
#, c-format
msgid "Unrecognized or duplicate item in %s line %d: %s"
msgstr "%s 第 %d 行,无法识别或重复的项: %s"
-#: ../spell.c:5197
-#, c-format
-msgid "Missing FOL/LOW/UPP line in %s"
-msgstr "%s 中缺少 FOL/LOW/UPP 行"
-
-#: ../spell.c:5220
+#: ../spellfile.c:2738
msgid "COMPOUNDSYLMAX used without SYLLABLE"
msgstr "在没有 SYLLABLE 的情况下使用了 COMPOUNDSYLMAX"
-#: ../spell.c:5236
+#: ../spellfile.c:2756
msgid "Too many postponed prefixes"
msgstr "太多延迟前缀"
-#: ../spell.c:5238
+#: ../spellfile.c:2758
msgid "Too many compound flags"
msgstr "太多组合标志"
-#: ../spell.c:5240
+#: ../spellfile.c:2760
msgid "Too many postponed prefixes and/or compound flags"
msgstr "太多延迟前缀和/或组合标志"
-#: ../spell.c:5250
+#: ../spellfile.c:2771
#, c-format
msgid "Missing SOFO%s line in %s"
msgstr "%s 中缺少 SOFO%s 行"
-#: ../spell.c:5253
+#: ../spellfile.c:2774
#, c-format
msgid "Both SAL and SOFO lines in %s"
msgstr "%s 同时出现 SQL 和 SOFO 行"
-#: ../spell.c:5331
+#: ../spellfile.c:2855
#, c-format
msgid "Flag is not a number in %s line %d: %s"
msgstr "%s 第 %d 行,标志不是数字: %s"
-#: ../spell.c:5334
+#: ../spellfile.c:2858
#, c-format
msgid "Illegal flag in %s line %d: %s"
msgstr "%s 第 %d 行,无效的标志: %s"
-#: ../spell.c:5493 ../spell.c:5501
+#: ../spellfile.c:3029 ../spellfile.c:3038
#, c-format
msgid "%s value differs from what is used in another .aff file"
msgstr "%s 的值与另一个 .aff 文件中使用的值不相同"
-#: ../spell.c:5602
+#: ../spellfile.c:3142
#, c-format
msgid "Reading dictionary file %s..."
msgstr "读取字典文件 %s ……"
-#: ../spell.c:5611
+#: ../spellfile.c:3150
#, c-format
msgid "E760: No word count in %s"
msgstr "E760: %s 中没有单词计数"
-#: ../spell.c:5669
+#: ../spellfile.c:3214
#, c-format
-msgid "line %6d, word %6d - %s"
-msgstr "第 %6d 行,第 %6d 个单词 - %s"
+msgid "line %6d, word %6ld - %s"
+msgstr "第 %6d 行,第 %6ld 个单词 - %s"
-#: ../spell.c:5691
+#: ../spellfile.c:3237
#, c-format
msgid "Duplicate word in %s line %d: %s"
msgstr "%s 第 %d 行,重复的单词: %s"
-#: ../spell.c:5694
+#: ../spellfile.c:3240
#, c-format
msgid "First duplicate word in %s line %d: %s"
msgstr "%s 第 %d 行,首次重复的单词: %s"
-#: ../spell.c:5746
+#: ../spellfile.c:3301
#, c-format
msgid "%d duplicate word(s) in %s"
msgstr "存在 %d 个重复的单词,在 %s 中"
-#: ../spell.c:5748
+#: ../spellfile.c:3304
#, c-format
msgid "Ignored %d word(s) with non-ASCII characters in %s"
msgstr "忽略了含有非 ASCII 字符的 %d 个单词,在 %s 中"
-#: ../spell.c:6115
+#: ../spellfile.c:3695
#, c-format
msgid "Reading word file %s..."
msgstr "读取单词文件 %s ……"
-#: ../spell.c:6155
+#: ../spellfile.c:3723
#, c-format
-msgid "Duplicate /encoding= line ignored in %s line %d: %s"
+msgid "Conversion failure for word in %s line %ld: %s"
+msgstr "单词 %s 转换失败,第 %ld 行: %s"
+
+#: ../spellfile.c:3737
+#, c-format
+msgid "Duplicate /encoding= line ignored in %s line %ld: %s"
msgstr "%s 第 %ld 行,重复的 /encoding= 行已被忽略: %s"
-#: ../spell.c:6159
+#: ../spellfile.c:3740
#, c-format
-msgid "/encoding= line after word ignored in %s line %d: %s"
-msgstr "%s 第 %d 行,单词后的 /encoding= 行已被忽略: %s"
+msgid "/encoding= line after word ignored in %s line %ld: %s"
+msgstr "%s 第 %ld 行,单词后的 /encoding= 行已被忽略: %s"
-#: ../spell.c:6180
+#: ../spellfile.c:3761
#, c-format
-msgid "Duplicate /regions= line ignored in %s line %d: %s"
-msgstr "%s 第 %d 行,重复的 /regions= 行已被忽略: %s"
+msgid "Duplicate /regions= line ignored in %s line %ld: %s"
+msgstr "%s 第 %ld 行,重复的 /regions= 行已被忽略: %s"
-#: ../spell.c:6185
+#: ../spellfile.c:3766
#, c-format
-msgid "Too many regions in %s line %d: %s"
-msgstr "%s 第 %d 行,太多区域: %s"
+msgid "Too many regions in %s line %ld: %s"
+msgstr "%s 第 %ld 行,太多区域: %s"
-#: ../spell.c:6198
+#: ../spellfile.c:3779
#, c-format
-msgid "/ line ignored in %s line %d: %s"
-msgstr "%s 第 %d 行,/ 行已被忽略: %s"
+msgid "/ line ignored in %s line %ld: %s"
+msgstr "%s 第 %ld 行,/ 行已被忽略: %s"
-#: ../spell.c:6224
+#: ../spellfile.c:3806
#, c-format
-msgid "Invalid region nr in %s line %d: %s"
-msgstr "%s 第 %d 行,无效的区域号: %s"
+msgid "Invalid region nr in %s line %ld: %s"
+msgstr "%s 第 %ld 行,区域号无效:%s"
-#: ../spell.c:6230
+#: ../spellfile.c:3812
#, c-format
-msgid "Unrecognized flags in %s line %d: %s"
-msgstr "%s 第 %d 行,不可识别的标志: %s"
+msgid "Unrecognized flags in %s line %ld: %s"
+msgstr "%s 第 %ld 行,不可识别的标志: %s"
-#: ../spell.c:6257
+#: ../spellfile.c:3839
#, c-format
msgid "Ignored %d words with non-ASCII characters"
msgstr "忽略了含有非 ASCII 字符的 %d 个单词"
-#: ../spell.c:6656
+#: ../spellfile.c:4231
#, c-format
-msgid "Compressed %d of %d nodes; %d (%d%%) remaining"
-msgstr "压缩了 %d/%d 个节点;剩余 %d (%d%%)"
+msgid "Compressed %s of %ld nodes; %ld (%ld%%) remaining"
+msgstr "压缩了 %s / %ld 个节点;剩余 %ld (%ld%%)"
-#: ../spell.c:7340
+#: ../spellfile.c:4933
msgid "Reading back spell file..."
msgstr "读取拼写文件……"
#. Go through the trie of good words, soundfold each word and add it to
#. the soundfold trie.
-#: ../spell.c:7357
+#: ../spellfile.c:4951
msgid "Performing soundfolding..."
msgstr "正在 soundfolding……"
-#: ../spell.c:7368
+#: ../spellfile.c:4964
#, c-format
msgid "Number of words after soundfolding: %<PRId64>"
msgstr "soundfolding 后的单词数: %<PRId64>"
-#: ../spell.c:7476
+#: ../spellfile.c:5072
#, c-format
msgid "Total number of words: %d"
msgstr "单词总数: %d"
-#: ../spell.c:7655
+#: ../spellfile.c:5213
#, c-format
msgid "Writing suggestion file %s..."
msgstr "写入建议文件 %s ……"
-#: ../spell.c:7707 ../spell.c:7927
+#: ../spellfile.c:5269 ../spellfile.c:5457
#, c-format
msgid "Estimated runtime memory use: %d bytes"
msgstr "估计运行时内存用量: %d 字节"
-#: ../spell.c:7820
+#: ../spellfile.c:5353
msgid "E751: Output file name must not have region name"
msgstr "E751: 输出文件名不能含有区域名"
-#: ../spell.c:7822
-msgid "E754: Only up to 8 regions supported"
-msgstr "E754: 最多只支持 8 个区域"
+#: ../spellfile.c:5355
+#, c-format
+msgid "E754: Only up to %d regions supported"
+msgstr "E754: 最多只支持 %d 个区域"
-#: ../spell.c:7846
+#: ../spellfile.c:5379
#, c-format
msgid "E755: Invalid region in %s"
msgstr "E755: %s 出现无效的范围"
-#: ../spell.c:7907
+#: ../spellfile.c:5436
msgid "Warning: both compounding and NOBREAK specified"
msgstr "警告: 同时指定了 compounding 和 NOBREAK"
-#: ../spell.c:7920
+#: ../spellfile.c:5450
#, c-format
msgid "Writing spell file %s..."
msgstr "写入拼写文件 %s ……"
-#: ../spell.c:7925
+#: ../spellfile.c:5455
msgid "Done!"
msgstr "完成!"
-#: ../spell.c:8034
+#: ../spellfile.c:5576
#, c-format
msgid "E765: 'spellfile' does not have %<PRId64> entries"
msgstr "E765: 'spellfile' 没有 %<PRId64> 项"
-#: ../spell.c:8074
-#, fuzzy, c-format
+#: ../spellfile.c:5621
+#, c-format
msgid "Word '%.*s' removed from %s"
-msgstr "从 %s 中删除了单词"
+msgstr "单词 '%.*s' 从 %s 中删除了"
-#: ../spell.c:8117
-#, fuzzy, c-format
+#: ../spellfile.c:5625
+msgid "Seek error in spellfile"
+msgstr "拼写文件中定位错误"
+
+#: ../spellfile.c:5671
+#, c-format
msgid "Word '%.*s' added to %s"
-msgstr "向 %s 中添加了单词"
+msgstr "单词 '%.*s' 添加到了 %s"
-#: ../spell.c:8381
+#: ../spellfile.c:5803
msgid "E763: Word characters differ between spell files"
msgstr "E763: 拼写文件之间的字符不相同"
-#: ../spell.c:8684
+#. This should have been checked when generating the .spl
+#. file.
+#: ../spellfile.c:5899
+msgid "E783: duplicate char in MAP entry"
+msgstr "E783: MAP 条目中有重复的字符"
+
+#: ../spellsuggest.c:530
msgid "Sorry, no suggestions"
msgstr "抱歉,没有建议"
-#: ../spell.c:8687
+#: ../spellsuggest.c:533
#, c-format
msgid "Sorry, only %<PRId64> suggestions"
msgstr "抱歉,只有 %<PRId64> 条建议"
#. for when 'cmdheight' > 1
#. avoid more prompt
-#: ../spell.c:8704
+#: ../spellsuggest.c:547
#, c-format
msgid "Change \"%.*s\" to:"
msgstr "将 \"%.*s\" 改为:"
-#: ../spell.c:8737
+#: ../spellsuggest.c:581
#, c-format
msgid " < \"%.*s\""
msgstr " < \"%.*s\""
-#: ../spell.c:8882
-msgid "E752: No previous spell replacement"
-msgstr "E752: 之前没有拼写替换"
+#: ../statusline.c:110 ../statusline.c:1553
+msgid "[Help]"
+msgstr "[帮助]"
-#: ../spell.c:8925
-#, c-format
-msgid "E753: Not found: %s"
-msgstr "E753: 找不到: %s"
+#: ../statusline.c:114 ../statusline.c:1588
+msgid "[Preview]"
+msgstr "[预览]"
-#: ../spell.c:9276
-#, c-format
-msgid "E778: This does not look like a .sug file: %s"
-msgstr "E778: 看起来不像是 .sug 文件: %s"
+#: ../strings.c:496
+msgid "E766: Insufficient arguments for printf()"
+msgstr "E766: printf() 的参数不足"
-#: ../spell.c:9282
-#, c-format
-msgid "E779: Old .sug file, needs to be updated: %s"
-msgstr "E779: 旧的.sug 文件,需要更新: %s"
+#: ../strings.c:618
+msgid "E807: Expected Float argument for printf()"
+msgstr "E807: 期盼浮点数作为printf()参数"
-#: ../spell.c:9286
-#, c-format
-msgid "E780: .sug file is for newer version of Vim: %s"
-msgstr "E780: .sug 文件适用于较新的vim 版本: %s"
+#: ../strings.c:1400
+msgid "E767: Too many arguments to printf()"
+msgstr "E767: printf() 的参数过多"
-#: ../spell.c:9295
+#: ../syntax.c:61
#, c-format
-msgid "E781: .sug file doesn't match .spl file: %s"
-msgstr "E781: .sug 文件不能匹配 .spl 文件: %s"
-
-#: ../spell.c:9305
-#, fuzzy, c-format
-msgid "E782: error while reading .sug file: %s"
-msgstr "E47: 读取错误文件失败"
-
-#. This should have been checked when generating the .spl
-#. file.
-#: ../spell.c:11575
-msgid "E783: duplicate char in MAP entry"
-msgstr "E783: MAP 条目中有重复的字符"
+msgid "E390: Illegal argument: %s"
+msgstr "E390: 无效的参数: %s"
-#: ../syntax.c:266
+#: ../syntax.c:241
msgid "No Syntax items defined for this buffer"
msgstr "这个缓冲区没有定义任何语法项"
-#: ../syntax.c:3083 ../syntax.c:3104 ../syntax.c:3127
-#, c-format
-msgid "E390: Illegal argument: %s"
-msgstr "E390: 无效的参数: %s"
+#: ../syntax.c:2706
+msgid "'redrawtime' exceeded, syntax highlighting disabled"
+msgstr "'redrawtime' 已过,语法高亮被禁用"
-#: ../syntax.c:3299
+#: ../syntax.c:2941
+msgid "syntax iskeyword not set"
+msgstr "syntax iskeyword 未设置"
+
+#: ../syntax.c:3118
#, c-format
msgid "E391: No such syntax cluster: %s"
msgstr "E391: 无此语法 cluster: \"%s\""
-#: ../syntax.c:3433
+#: ../syntax.c:3234
msgid "syncing on C-style comments"
msgstr "C风格注释同步中"
-#: ../syntax.c:3439
+#: ../syntax.c:3240
msgid "no syncing"
msgstr "没有同步"
-#: ../syntax.c:3441
+#: ../syntax.c:3243
+msgid "syncing starts at the first line"
+msgstr "同步开始于第一行"
+
+#: ../syntax.c:3245
msgid "syncing starts "
msgstr "同步开始"
-#: ../syntax.c:3443 ../syntax.c:3506
+#: ../syntax.c:3247 ../syntax.c:3316
msgid " lines before top line"
msgstr "行号超出范围"
-#: ../syntax.c:3448
+#: ../syntax.c:3253
msgid ""
"\n"
"--- Syntax sync items ---"
@@ -5723,7 +6668,7 @@ msgstr ""
"\n"
"--- 语法同步项目 (Syntax sync items) ---"
-#: ../syntax.c:3452
+#: ../syntax.c:3257
msgid ""
"\n"
"syncing on items"
@@ -5731,7 +6676,7 @@ msgstr ""
"\n"
"同步中:"
-#: ../syntax.c:3457
+#: ../syntax.c:3262
msgid ""
"\n"
"--- Syntax items ---"
@@ -5739,276 +6684,211 @@ msgstr ""
"\n"
"--- 语法项目 ---"
-#: ../syntax.c:3475
+#: ../syntax.c:3279
#, c-format
msgid "E392: No such syntax cluster: %s"
msgstr "E392: 无此语法 cluster: \"%s\""
-#: ../syntax.c:3497
+#: ../syntax.c:3303
+msgid "from the first line"
+msgstr "从第一行"
+
+#: ../syntax.c:3306
msgid "minimal "
msgstr "最小"
-#: ../syntax.c:3503
+#: ../syntax.c:3313
msgid "maximal "
msgstr "最大"
-#: ../syntax.c:3513
-#, fuzzy
+#: ../syntax.c:3324
msgid "; match "
-msgstr "匹配 %d"
+msgstr ";匹配 "
-#: ../syntax.c:3515
-#, fuzzy
+#: ../syntax.c:3326
msgid " line breaks"
-msgstr "少于一行"
+msgstr " 个换行"
-#: ../syntax.c:4076
+#: ../syntax.c:3875
msgid "E395: contains argument not accepted here"
msgstr "E395: 使用了不正确的参数"
-#: ../syntax.c:4096
-#, fuzzy
+#: ../syntax.c:3894
msgid "E844: invalid cchar value"
-msgstr "E474: 无效的参数"
+msgstr "E844: cchar 值无效"
-#: ../syntax.c:4107
+#: ../syntax.c:3905
msgid "E393: group[t]here not accepted here"
msgstr "E393: 使用了不正确的参数"
-#: ../syntax.c:4126
+#: ../syntax.c:3927
#, c-format
msgid "E394: Didn't find region item for %s"
msgstr "E394: 找不到 %s 的 region item"
-#: ../syntax.c:4188
+#: ../syntax.c:3988
msgid "E397: Filename required"
msgstr "E397: 需要文件名称"
-#: ../syntax.c:4221
-#, fuzzy
+#: ../syntax.c:4019
msgid "E847: Too many syntax includes"
-msgstr "E77: 文件名过多"
+msgstr "E847: syntax include 过多"
-#: ../syntax.c:4303
-#, fuzzy, c-format
+#: ../syntax.c:4107
+#, c-format
msgid "E789: Missing ']': %s"
-msgstr "E747: 缺少 ']': %s"
+msgstr "E789: 缺少 ']':%s"
+
+#: ../syntax.c:4112
+#, c-format
+msgid "E890: trailing char after ']': %s]%s"
+msgstr "E890: ']' 后有尾随的字符:%s]%s"
-#: ../syntax.c:4531
+#: ../syntax.c:4320
#, c-format
msgid "E398: Missing '=': %s"
msgstr "E398: 缺少 '=': %s"
-#: ../syntax.c:4666
+#: ../syntax.c:4450
#, c-format
msgid "E399: Not enough arguments: syntax region %s"
msgstr "E399: syntax region %s 的参数太少"
-#: ../syntax.c:4870
-#, fuzzy
+#: ../syntax.c:4629
msgid "E848: Too many syntax clusters"
-msgstr "E391: 无此语法 cluster: \"%s\""
+msgstr "E848: syntax cluster 过多"
-#: ../syntax.c:4954
+#: ../syntax.c:4714
msgid "E400: No cluster specified"
msgstr "E400: 没有指定的属性"
#. end delimiter not found
-#: ../syntax.c:4986
+#: ../syntax.c:4746
#, c-format
msgid "E401: Pattern delimiter not found: %s"
msgstr "E401: 找不到分隔符号: %s"
-#: ../syntax.c:5049
+#: ../syntax.c:4816
#, c-format
msgid "E402: Garbage after pattern: %s"
msgstr "E402: '%s' 后面的东西不能识别"
-#: ../syntax.c:5120
+#: ../syntax.c:4893
msgid "E403: syntax sync: line continuations pattern specified twice"
msgstr "E403: 语法同步: 连接行符号指定了两次"
-#: ../syntax.c:5169
+#: ../syntax.c:4942
#, c-format
msgid "E404: Illegal arguments: %s"
msgstr "E404: 无效的参数: %s"
-#: ../syntax.c:5217
+#: ../syntax.c:4978
#, c-format
msgid "E405: Missing equal sign: %s"
msgstr "E405: 缺少等号: %s"
-#: ../syntax.c:5222
+#: ../syntax.c:4983
#, c-format
msgid "E406: Empty argument: %s"
msgstr "E406: 空的参数: %s"
-#: ../syntax.c:5240
+#: ../syntax.c:4998
#, c-format
msgid "E407: %s not allowed here"
msgstr "E407: %s 不能在此出现"
-#: ../syntax.c:5246
+#: ../syntax.c:5004
#, c-format
msgid "E408: %s must be first in contains list"
msgstr "E408: %s 必须是列表里的第一个"
-#: ../syntax.c:5304
+#: ../syntax.c:5067
#, c-format
msgid "E409: Unknown group name: %s"
msgstr "E409: 不正确的组名: %s"
-#: ../syntax.c:5512
+#: ../syntax.c:5272
#, c-format
msgid "E410: Invalid :syntax subcommand: %s"
msgstr "E410: 不正确的 :syntax 子命令: %s"
-#: ../syntax.c:5854
+#: ../syntax.c:5660
msgid ""
" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"
msgstr ""
+" 总 计 计 数 匹 配 最 慢 的 平 均 名 字 模 式"
-#: ../syntax.c:6146
-msgid "E679: recursive loop loading syncolor.vim"
-msgstr "E679: 加载 syncolor.vim 时出现嵌套循环"
-
-#: ../syntax.c:6256
-#, c-format
-msgid "E411: highlight group not found: %s"
-msgstr "E411: 找不到 highlight group: %s"
-
-#: ../syntax.c:6278
-#, c-format
-msgid "E412: Not enough arguments: \":highlight link %s\""
-msgstr "E412: 参数太少: \":highlight link %s\""
-
-#: ../syntax.c:6284
-#, c-format
-msgid "E413: Too many arguments: \":highlight link %s\""
-msgstr "E413: 参数过多: \":highlight link %s\""
-
-#: ../syntax.c:6302
-msgid "E414: group has settings, highlight link ignored"
-msgstr "E414: 已设定组, 忽略 highlight link"
-
-#: ../syntax.c:6367
-#, c-format
-msgid "E415: unexpected equal sign: %s"
-msgstr "E415: 不该有的等号: %s"
-
-#: ../syntax.c:6395
-#, c-format
-msgid "E416: missing equal sign: %s"
-msgstr "E416: 缺少等号: %s"
-
-#: ../syntax.c:6418
-#, c-format
-msgid "E417: missing argument: %s"
-msgstr "E417: 缺少参数: %s"
-
-#: ../syntax.c:6446
-#, c-format
-msgid "E418: Illegal value: %s"
-msgstr "E418: 不合法的值: %s"
-
-#: ../syntax.c:6496
-msgid "E419: FG color unknown"
-msgstr "E419: 错误的前景颜色"
-
-#: ../syntax.c:6504
-msgid "E420: BG color unknown"
-msgstr "E420: 错误的背景颜色"
-
-#: ../syntax.c:6564
-#, c-format
-msgid "E421: Color name or number not recognized: %s"
-msgstr "E421: 错误的颜色名称或数值: %s"
-
-#: ../syntax.c:6714
-#, c-format
-msgid "E422: terminal code too long: %s"
-msgstr "E422: 终端编码太长: %s"
-
-#: ../syntax.c:6753
-#, c-format
-msgid "E423: Illegal argument: %s"
-msgstr "E423: 无效的参数: %s"
-
-#: ../syntax.c:6925
-msgid "E424: Too many different highlighting attributes in use"
-msgstr "E424: 使用了太多不同的高亮度属性"
-
-#: ../syntax.c:7427
-msgid "E669: Unprintable character in group name"
-msgstr "E669: 组名中存在不可显示字符"
-
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
-msgstr "W18: 组名中含有无效字符"
-
-#: ../syntax.c:7448
-msgid "E849: Too many highlight and syntax groups"
-msgstr "E849: 高亮和语法组过多"
-
-#: ../tag.c:104
+#: ../tag.c:117
msgid "E555: at bottom of tag stack"
msgstr "E555: 已在 tag 堆栈底部"
-#: ../tag.c:105
+#: ../tag.c:118
msgid "E556: at top of tag stack"
msgstr "E556: 已在 tag 堆栈顶部"
-#: ../tag.c:380
+#: ../tag.c:120
+msgid "E986: cannot modify the tag stack within tagfunc"
+msgstr "E986: 在 tagfunc 中不能修改 tag 堆栈"
+
+#: ../tag.c:122
+msgid "E987: invalid return value from tagfunc"
+msgstr "E987: tagfunc 返回了无效的值"
+
+#: ../tag.c:124
+msgid "E1299: Window unexpectedly closed while searching for tags"
+msgstr "E1299: 搜索 tag 时窗口意外关闭"
+
+#: ../tag.c:427
msgid "E425: Cannot go before first matching tag"
msgstr "E425: 已到第一个匹配的 tag"
-#: ../tag.c:504
+#: ../tag.c:568
#, c-format
msgid "E426: tag not found: %s"
msgstr "E426: 找不到 tag: %s"
-#: ../tag.c:528
-msgid " # pri kind tag"
-msgstr " # pri kind tag"
-
-#: ../tag.c:531
-msgid "file\n"
-msgstr "文件\n"
-
-#: ../tag.c:829
+#: ../tag.c:610
msgid "E427: There is only one matching tag"
msgstr "E427: 只有一个匹配的 tag"
-#: ../tag.c:831
+#: ../tag.c:612
msgid "E428: Cannot go beyond last matching tag"
msgstr "E428: 己到最后一个匹配的 tag"
-#: ../tag.c:850
+#: ../tag.c:641
#, c-format
msgid "File \"%s\" does not exist"
msgstr "文件 \"%s\" 不存在"
#. Give an indication of the number of matching tags
-#: ../tag.c:859
+#: ../tag.c:649
#, c-format
msgid "tag %d of %d%s"
msgstr "找到 tag: %d / %d%s"
-#: ../tag.c:862
+#: ../tag.c:652
msgid " or more"
msgstr " 或更多"
-#: ../tag.c:864
+#: ../tag.c:654
msgid " Using tag with different case!"
msgstr " 以不同大小写来使用 tag!"
-#: ../tag.c:909
+#: ../tag.c:701
#, c-format
msgid "E429: File \"%s\" does not exist"
msgstr "E429: 文件 \"%s\" 不存在"
+#: ../tag.c:749
+msgid " # pri kind tag"
+msgstr " # pri kind tag"
+
+#: ../tag.c:752
+msgid "file\n"
+msgstr "文件\n"
+
#. Highlight title
-#: ../tag.c:960
+#: ../tag.c:1064
msgid ""
"\n"
" # TO tag FROM line in file/text"
@@ -6016,1620 +6896,2495 @@ msgstr ""
"\n"
" # 到 tag 从 行 在 文件/文本"
-#: ../tag.c:1303
+#: ../tag.c:1635
#, c-format
msgid "Searching tags file %s"
msgstr "查找 tag 文件 %s"
-#: ../tag.c:1545
-msgid "Ignoring long line in tags file"
-msgstr "忽略较长的行在 tags 文件中"
-
-#: ../tag.c:1915
+#: ../tag.c:2155
#, c-format
msgid "E431: Format error in tags file \"%s\""
msgstr "E431: Tag 文件 \"%s\" 格式错误"
-#: ../tag.c:1917
+#: ../tag.c:2156
#, c-format
msgid "Before byte %<PRId64>"
msgstr "在第 %<PRId64> 字节之前"
-#: ../tag.c:1929
+#: ../tag.c:2168
#, c-format
msgid "E432: Tags file not sorted: %s"
msgstr "E432: Tag 文件未排序: %s"
#. never opened any tags file
-#: ../tag.c:1960
+#: ../tag.c:2195
msgid "E433: No tags file"
msgstr "E433: 没有 tag 文件"
-#: ../tag.c:2536
+#: ../tag.c:2794
msgid "E434: Can't find tag pattern"
msgstr "E434: 找不到 tag 模式"
-#: ../tag.c:2544
+#: ../tag.c:2800
msgid "E435: Couldn't find tag, just guessing!"
msgstr "E435: 找不到 tag,试着猜!"
-#: ../tag.c:2797
-#, fuzzy, c-format
+#: ../tag.c:3070
+#, c-format
msgid "Duplicate field name: %s"
-msgstr "%s 第 %d 行,重复的附加项: %s"
-
-#: ../term.c:1442
-msgid "' not known. Available builtin terminals are:"
-msgstr "' 未知。可用的内建终端有:"
-
-#: ../term.c:1463
-msgid "defaulting to '"
-msgstr "默认值为: '"
-
-#: ../term.c:1731
-msgid "E557: Cannot open termcap file"
-msgstr "E557: 无法打开 termcap 文件"
-
-#: ../term.c:1735
-msgid "E558: Terminal entry not found in terminfo"
-msgstr "E558: 在 terminfo 中找不到终端项"
+msgstr "字段名重复:%s"
-#: ../term.c:1737
-msgid "E559: Terminal entry not found in termcap"
-msgstr "E559: 在 termcap 中找不到终端项"
+#: ../testing.c:38
+msgid ""
+"E856: assert_fails() second argument must be a string or a list with one or "
+"two strings"
+msgstr ""
+"E856: assert_fails() 的第二个参数必须是字符串或包含一或两个字符串的列表"
-#: ../term.c:1878
-#, c-format
-msgid "E436: No \"%s\" entry in termcap"
-msgstr "E436: termcap 中没有 \"%s\" 项"
+#: ../testing.c:40
+msgid "E1115: assert_fails() fourth argument must be a number"
+msgstr "E1115: assert_fails() 的第四个参数必须是整数"
-#: ../term.c:2249
-msgid "E437: terminal capability \"cm\" required"
-msgstr "E437: 终端需要能力 \"cm\""
+#: ../testing.c:42
+msgid "E1116: assert_fails() fifth argument must be a string"
+msgstr "E1116: assert_fails() 的第五个参数必须是字符串"
-#. Highlight title
-#: ../term.c:4376
-msgid ""
-"\n"
-"--- Terminal keys ---"
-msgstr ""
-"\n"
-"--- 终端按键 ---"
+#: ../testing.c:44
+msgid "E1142: Calling test_garbagecollect_now() while v:testing is not set"
+msgstr "E1142: 调用 test_garbagecollect_now(),但 v:testing 未设置"
-#: ../ui.c:481
-msgid "Vim: Error reading input, exiting...\n"
-msgstr "Vim: 读错误,退出中...\n"
+#: ../ui.c:342
+msgid "Beep!"
+msgstr "Beep!"
#. This happens when the FileChangedRO autocommand changes the
-#. * file in a way it becomes shorter.
-#: ../undo.c:379
-#, fuzzy
+#. file in a way it becomes shorter.
+#: ../undo.c:357
msgid "E881: Line count changed unexpectedly"
-msgstr "E787: 意外地改变了缓冲区"
+msgstr "E881: 行数意外地改变了"
-#: ../undo.c:627
-#, fuzzy, c-format
+#: ../undo.c:628
+#, c-format
msgid "E828: Cannot open undo file for writing: %s"
-msgstr "E212: 无法打开并写入文件"
+msgstr "E828: 无法打开并写入撤销文件:%s"
-#: ../undo.c:717
+#: ../undo.c:710
+#, c-format
+msgid "E5003: Unable to create directory \"%s\" for undo file: %s"
+msgstr "E303: 无法为交换文件创建目录 \"%s\":%s"
+
+#: ../undo.c:749
#, c-format
msgid "E825: Corrupted undo file (%s): %s"
msgstr "E825: 已损坏的撤销文件 (%s): %s"
-#: ../undo.c:1039
+#: ../undo.c:1169
msgid "Cannot write undo file in any directory in 'undodir'"
msgstr "不能写入撤销文件到 'undodir' 中的任何文件夹"
-#: ../undo.c:1074
+#: ../undo.c:1205
#, c-format
msgid "Will not overwrite with undo file, cannot read: %s"
msgstr "不能重写撤销文件, 不可读取: %s"
-#: ../undo.c:1092
+#: ../undo.c:1222
#, c-format
msgid "Will not overwrite, this is not an undo file: %s"
msgstr "这个文件: %s 不是撤销文件,不可以重写"
-#: ../undo.c:1108
+#: ../undo.c:1239
msgid "Skipping undo file write, nothing to undo"
msgstr "跳过写入撤销文件,没有任何撤销"
-#: ../undo.c:1121
-#, fuzzy, c-format
+#: ../undo.c:1252
+#, c-format
msgid "Writing undo file: %s"
-msgstr "写入 viminfo 文件 \"%s\""
+msgstr "写入撤销文件:%s"
-#: ../undo.c:1213
-#, fuzzy, c-format
+#: ../undo.c:1340
+#, c-format
msgid "E829: write error in undo file: %s"
-msgstr "E297: 交换文件写入错误"
+msgstr "E829: 撤销文件写入错误:%s"
-#: ../undo.c:1280
+#: ../undo.c:1387
#, c-format
msgid "Not reading undo file, owner differs: %s"
-msgstr "不能读取撤销文件, 所有者不同: %s"
+msgstr "不能读取撤销文件,所有者不同:%s"
-#: ../undo.c:1292
-#, fuzzy, c-format
+#: ../undo.c:1400
+#, c-format
msgid "Reading undo file: %s"
-msgstr "读取单词文件 %s ……"
+msgstr "读取撤销文件:%s"
-#: ../undo.c:1299
-#, fuzzy, c-format
+#: ../undo.c:1407
+#, c-format
msgid "E822: Cannot open undo file for reading: %s"
-msgstr "E195: 无法打开并读取 viminfo 文件"
+msgstr "E822: 无法打开并读取撤销文件:%s"
-#: ../undo.c:1308
-#, fuzzy, c-format
+#: ../undo.c:1421
+#, c-format
msgid "E823: Not an undo file: %s"
-msgstr "E753: 找不到: %s"
+msgstr "E823: 不是撤销文件:%s"
-#: ../undo.c:1313
-#, fuzzy, c-format
+#: ../undo.c:1426
+#, c-format
msgid "E824: Incompatible undo file: %s"
-msgstr "E484: 无法打开文件 %s"
+msgstr "E824: 不兼容的撤销文件:%s"
-#: ../undo.c:1328
+#: ../undo.c:1442
msgid "File contents changed, cannot use undo info"
msgstr "文件内容已改变,不能使用撤销信息"
-#: ../undo.c:1497
-#, fuzzy, c-format
+#: ../undo.c:1636
+#, c-format
msgid "Finished reading undo file %s"
-msgstr "结束执行 %s"
+msgstr "读取撤销文件 %s 结束"
-#: ../undo.c:1586 ../undo.c:1812
+#: ../undo.c:1871 ../undo.c:2106
msgid "Already at oldest change"
msgstr "已位于最旧的改变"
-#: ../undo.c:1597 ../undo.c:1814
+#: ../undo.c:1882 ../undo.c:2108
msgid "Already at newest change"
msgstr "已位于最新的改变"
-#: ../undo.c:1806
-#, fuzzy, c-format
+#: ../undo.c:2100
+#, c-format
msgid "E830: Undo number %<PRId64> not found"
-msgstr "找不到撤销号 %<PRId64>"
+msgstr "E830: 找不到撤销号 %<PRId64>"
-#: ../undo.c:1979
+#: ../undo.c:2280
msgid "E438: u_undo: line numbers wrong"
-msgstr "E438: u_undo: 行号错误"
+msgstr "E438: u_undo:行号错误"
-#: ../undo.c:2183
+#: ../undo.c:2538
msgid "more line"
msgstr "行被加入"
-#: ../undo.c:2185
+#: ../undo.c:2540
msgid "more lines"
msgstr "行被加入"
-#: ../undo.c:2187
+#: ../undo.c:2542
msgid "line less"
msgstr "行被去掉"
-#: ../undo.c:2189
+#: ../undo.c:2544
msgid "fewer lines"
msgstr "行被去掉"
-#: ../undo.c:2193
+#: ../undo.c:2548
msgid "change"
msgstr "行发生改变"
-#: ../undo.c:2195
+#: ../undo.c:2550
msgid "changes"
msgstr "行发生改变"
-#: ../undo.c:2225
+#: ../undo.c:2589
#, c-format
msgid "%<PRId64> %s; %s #%<PRId64> %s"
msgstr "%<PRId64> %s;%s #%<PRId64> %s"
-#: ../undo.c:2228
+#: ../undo.c:2592
msgid "before"
msgstr "before"
-#: ../undo.c:2228
+#: ../undo.c:2592
msgid "after"
msgstr "after"
-#: ../undo.c:2325
+#: ../undo.c:2613
+#, c-format
+msgid "%<PRId64> second ago"
+msgid_plural "%<PRId64> seconds ago"
+msgstr[0] "%<PRId64> 秒前"
+
+#: ../undo.c:2697
msgid "Nothing to undo"
msgstr "无可撤销"
-#: ../undo.c:2330
+#: ../undo.c:2702
msgid "number changes when saved"
msgstr " 编号 变更 时间 保存"
-#: ../undo.c:2360
-#, fuzzy, c-format
-msgid "%<PRId64> seconds ago"
-msgstr "%<PRId64> 列; "
-
-#: ../undo.c:2372
-#, fuzzy
+#: ../undo.c:2724
msgid "E790: undojoin is not allowed after undo"
-msgstr "E407: %s 不能在此出现"
+msgstr "E790: undo 后不允许 undojoin"
-#: ../undo.c:2466
+#: ../undo.c:2807
msgid "E439: undo list corrupt"
msgstr "E439: 撤销列表损坏"
-#: ../undo.c:2495
+#: ../undo.c:2830
msgid "E440: undo line missing"
msgstr "E440: 找不到要撤销的行"
-#: ../version.c:600
+#: ../usercmd.c:46
+msgid "E1208: -complete used without allowing arguments"
+msgstr "E1208: 用了 -complete 却不允许使用参数"
+
+#: ../usercmd.c:48
+#, c-format
+msgid "E184: No such user-defined command: %s"
+msgstr "E184: 没有这个用户自定义命令: %s"
+
+#: ../usercmd.c:50
+#, c-format
+msgid "E1237: No such user-defined command in current buffer: %s"
+msgstr "E1237: 当前缓冲区中无此用户定义命令:%s"
+
+#: ../usercmd.c:416
msgid ""
"\n"
-"Included patches: "
+" Name Args Address Complete Definition"
msgstr ""
"\n"
-"包含补丁: "
+" 名称 参数 范围 补全 定义 "
-#: ../version.c:627
-#, fuzzy
-msgid ""
-"\n"
-"Extra patches: "
-msgstr "外部符合:\n"
+#: ../usercmd.c:561
+msgid "No user-defined commands found"
+msgstr "找不到用户自定义命令"
+
+#: ../usercmd.c:585
+#, c-format
+msgid "E180: Invalid address type value: %s"
+msgstr "E180: 无效的地址类型:%s"
+
+#: ../usercmd.c:633
+#, c-format
+msgid "E180: Invalid complete value: %s"
+msgstr "E180: 无效的补全类型:%s"
+
+#: ../usercmd.c:639
+msgid "E468: Completion argument only allowed for custom completion"
+msgstr "E468: 只有 custom 补全才允许参数"
+
+#: ../usercmd.c:645
+msgid "E467: Custom completion requires a function argument"
+msgstr "E467: Custom 补全需要一个函数参数"
+
+#: ../usercmd.c:662
+msgid "E175: No attribute specified"
+msgstr "E175: 没有指定属性"
-#: ../version.c:639 ../version.c:864
-msgid "Modified by "
-msgstr "修改者 "
+#: ../usercmd.c:710
+msgid "E176: Invalid number of arguments"
+msgstr "E176: 无效的参数个数"
-#: ../version.c:646
+#: ../usercmd.c:721
+msgid "E177: Count cannot be specified twice"
+msgstr "E177: 不能指定两次计数"
+
+#: ../usercmd.c:730
+msgid "E178: Invalid default value for count"
+msgstr "E178: 无效的计数默认值"
+
+#: ../usercmd.c:763
+msgid "E179: argument required for -complete"
+msgstr "E179: -complete 需要参数"
+
+#: ../usercmd.c:774
+msgid "E179: argument required for -addr"
+msgstr "E179: -addr 需要参数"
+
+#: ../usercmd.c:786
+#, c-format
+msgid "E181: Invalid attribute: %s"
+msgstr "E181: 无效的属性: %s"
+
+#: ../usercmd.c:866
+#, c-format
+msgid "E174: Command already exists: add ! to replace it: %s"
+msgstr "E174: 命令已存在:请加 ! 强制替换:%s"
+
+#: ../usercmd.c:955
+msgid "E182: Invalid command name"
+msgstr "E182: 无效的命令名"
+
+#: ../usercmd.c:966
+msgid "E183: User defined commands must start with an uppercase letter"
+msgstr "E183: 用户自定义命令必须以大写字母开头"
+
+#: ../usercmd.c:968
+msgid "E841: Reserved name, cannot be used for user defined command"
+msgstr "E841: 名称已被保留,不能用于用户自定义命令"
+
+#: ../version.c:2636
msgid ""
"\n"
-"Compiled "
+"\n"
+"Features: "
msgstr ""
"\n"
-"编译"
-
-#: ../version.c:649
-msgid "by "
-msgstr "者 "
+"\n"
+"功能: "
-#: ../version.c:660
+#: ../version.c:2740
msgid ""
"\n"
-"Huge version "
+"Compiled "
msgstr ""
"\n"
-"巨型版本 "
-
-#: ../version.c:661
-msgid "without GUI."
-msgstr "无图形界面。"
+"编译"
-#: ../version.c:662
-msgid " Features included (+) or not (-):\n"
-msgstr " 可使用(+)与不可使用(-)的功能:\n"
+#: ../version.c:2743
+msgid "by "
+msgstr "者 "
-#: ../version.c:667
+#: ../version.c:2757
msgid " system vimrc file: \""
msgstr " 系统 vimrc 文件: \""
-#: ../version.c:672
-msgid " user vimrc file: \""
-msgstr " 用户 vimrc 文件: \""
-
-#: ../version.c:677
-msgid " 2nd user vimrc file: \""
-msgstr " 第二用户 vimrc 文件: \""
-
-#: ../version.c:682
-msgid " 3rd user vimrc file: \""
-msgstr " 第三用户 vimrc 文件: \""
-
-#: ../version.c:687
-msgid " user exrc file: \""
-msgstr " 用户 exrc 文件: \""
-
-#: ../version.c:692
-msgid " 2nd user exrc file: \""
-msgstr " 第二用户 exrc 文件: \""
-
-#: ../version.c:699
+#: ../version.c:2764
msgid " fall-back for $VIM: \""
msgstr " $VIM 预设值: \""
-#: ../version.c:705
+#: ../version.c:2770
msgid " f-b for $VIMRUNTIME: \""
msgstr " $VIMRUNTIME 预设值: \""
-#: ../version.c:709
-msgid "Compilation: "
-msgstr "编译方式: "
-
-#: ../version.c:712
-msgid "Linking: "
-msgstr "链接方式: "
+#: ../version.c:2805
+msgid "Nvim is open source and freely distributable"
+msgstr "Nvim 是可自由分发的开放源代码软件"
-#: ../version.c:717
-msgid " DEBUG BUILD"
-msgstr " 调试版本"
+#: ../version.c:2808
+msgid "type :help nvim<Enter> if you are new! "
+msgstr "输入 :help nvim<Enter> 了解 Neovim! "
-#: ../version.c:767
-msgid "VIM - Vi IMproved"
-msgstr "VIM - Vi IMproved"
+#: ../version.c:2809
+msgid "type :checkhealth<Enter> to optimize Nvim"
+msgstr "输入 :checkhealth<Enter> 优化 Neovim "
-#: ../version.c:769
-msgid "version "
-msgstr "版本 "
+#: ../version.c:2810
+msgid "type :q<Enter> to exit "
+msgstr "输入 :q<Enter> 退出 "
-#: ../version.c:770
-msgid "by Bram Moolenaar et al."
-msgstr "维护人 Bram Moolenaar 等"
+#: ../version.c:2811
+msgid "type :help<Enter> for help "
+msgstr "输入 :help<Enter> 查看帮助 "
-#: ../version.c:774
-msgid "Vim is open source and freely distributable"
-msgstr "Vim 是可自由分发的开放源代码软件"
+#: ../version.c:2813
+#, c-format
+msgid "type :help news<Enter> to see changes in v%s.%s"
+msgstr "输入 :help news<Enter> 查看 v%s.%s 的变化"
-#: ../version.c:776
+#: ../version.c:2816
msgid "Help poor children in Uganda!"
msgstr "帮助乌干达的可怜儿童!"
-#: ../version.c:777
+#: ../version.c:2817
msgid "type :help iccf<Enter> for information "
msgstr "输入 :help iccf<Enter> 查看说明 "
-#: ../version.c:779
-msgid "type :q<Enter> to exit "
-msgstr "输入 :q<Enter> 退出 "
-
-#: ../version.c:780
-msgid "type :help<Enter> or <F1> for on-line help"
-msgstr "输入 :help<Enter> 或 <F1> 查看在线帮助 "
-
-#: ../version.c:781
-msgid "type :help version7<Enter> for version info"
-msgstr "输入 :help version7<Enter> 查看版本信息 "
-
-#: ../version.c:784
-msgid "Running in Vi compatible mode"
-msgstr "运行于 Vi 兼容模式"
-
-#: ../version.c:785
-msgid "type :set nocp<Enter> for Vim defaults"
-msgstr "输入 :set nocp<Enter> 恢复默认的 Vim "
-
-#: ../version.c:786
-msgid "type :help cp-default<Enter> for info on this"
-msgstr "输入 :help cp-default<Enter> 查看相关说明 "
-
-#: ../version.c:827
+#: ../version.c:2850
msgid "Sponsor Vim development!"
msgstr "赞助 Vim 的开发!"
-#: ../version.c:828
+#: ../version.c:2851
msgid "Become a registered Vim user!"
msgstr "成为 Vim 的注册用户!"
-#: ../version.c:831
+#: ../version.c:2854
msgid "type :help sponsor<Enter> for information "
msgstr "输入 :help sponsor<Enter> 查看说明 "
-#: ../version.c:832
+#: ../version.c:2855
msgid "type :help register<Enter> for information "
msgstr "输入 :help register<Enter> 查看说明 "
-#: ../version.c:834
+#: ../version.c:2857
msgid "menu Help->Sponsor/Register for information "
msgstr "菜单 Help->Sponsor/Register 查看说明 "
-#: ../window.c:119
+#: ../viml/parser/expressions.c:292
+#, c-format
+msgid "E15: Invalid control character present in input: %.*s"
+msgstr ""
+
+#: ../viml/parser/expressions.c:519
+#, c-format
+msgid "E112: Option name missing: %.*s"
+msgstr "E112: 缺少选项名称:%.*s"
+
+#: ../viml/parser/expressions.c:678 ../viml/parser/expressions.c:694
+#, c-format
+msgid "E15: Unexpected EOC character: %.*s"
+msgstr ""
+
+#: ../viml/parser/expressions.c:706
+#, c-format
+msgid "E15: Unidentified character: %.*s"
+msgstr ""
+
+#: ../viml/parser/expressions.c:1335
+#, c-format
+msgid "E15: Operator is not associative: %.*s"
+msgstr ""
+
+#. / Record missing operator: for things like
+#. /
+#. / :echo @a @a
+#. /
+#. / (allowed) or
+#. /
+#. / :echo (@a @a)
+#. /
+#. / (parsed as OpMissing(@a, @a)).
+#. Multiple expressions allowed, return without calling
+#. viml_parser_advance().
+#: ../viml/parser/expressions.c:1440
+#, c-format
+msgid "E15: Missing operator: %.*s"
+msgstr ""
+
+#: ../viml/parser/expressions.c:2081
+#, c-format
+msgid "E15: Expected lambda arguments list or arrow: %.*s"
+msgstr ""
+
+#: ../viml/parser/expressions.c:2101
+#, c-format
+msgid "E15: Expected value part of assignment lvalue: %.*s"
+msgstr ""
+
+#: ../viml/parser/expressions.c:2120
+#, c-format
+msgid "E15: Expected assignment operator or subscript: %.*s"
+msgstr ""
+
+#: ../viml/parser/expressions.c:2171
+msgid "E15: Unexpected "
+msgstr ""
+
+#: ../viml/parser/expressions.c:2181
+#, c-format
+msgid "E15: Unexpected multiplication-like operator: %.*s"
+msgstr ""
+
+#: ../viml/parser/expressions.c:2241
+msgid "E15: Environment variable name missing"
+msgstr "E15: 缺少环境变量名称"
+
+#: ../viml/parser/expressions.c:2259
+#, c-format
+msgid "E15: Expected value, got comparison operator: %.*s"
+msgstr ""
+
+#. Value level: comma appearing here is not valid.
+#. Note: in Vim string(,x) will give E116, this is not the case here.
+#: ../viml/parser/expressions.c:2286
+#, c-format
+msgid "E15: Expected value, got comma: %.*s"
+msgstr ""
+
+#: ../viml/parser/expressions.c:2320
+#, c-format
+msgid "E15: Comma outside of call, lambda or literal: %.*s"
+msgstr ""
+
+#: ../viml/parser/expressions.c:2404
+#, c-format
+msgid "E15: Colon outside of dictionary or ternary operator: %.*s"
+msgstr ""
+
+#: ../viml/parser/expressions.c:2451
+#, c-format
+msgid "E15: Expected value, got closing bracket: %.*s"
+msgstr ""
+
+#: ../viml/parser/expressions.c:2464
+#, c-format
+msgid "E475: Unable to assign to empty list: %.*s"
+msgstr "E475: 无法赋值给空列表:%.*s"
+
+#: ../viml/parser/expressions.c:2474 ../viml/parser/expressions.c:2609
+#, c-format
+msgid "E15: Unexpected closing figure brace: %.*s"
+msgstr ""
+
+#: ../viml/parser/expressions.c:2507
+#, c-format
+msgid "E475: Nested lists not allowed when assigning: %.*s"
+msgstr ""
+
+#: ../viml/parser/expressions.c:2557
+#, c-format
+msgid "E15: Expected value, got closing figure brace: %.*s"
+msgstr ""
+
+#: ../viml/parser/expressions.c:2587
+#, c-format
+msgid "E15: Don't know what figure brace means: %.*s"
+msgstr ""
+
+#. Only first branch is valid.
+#: ../viml/parser/expressions.c:2706
+#, c-format
+msgid "E15: Unexpected arrow: %.*s"
+msgstr ""
+
+#: ../viml/parser/expressions.c:2707
+#, c-format
+msgid "E15: Arrow outside of lambda: %.*s"
+msgstr ""
+
+#: ../viml/parser/expressions.c:2788
+#, c-format
+msgid "E15: Unexpected dot: %.*s"
+msgstr ""
+
+#: ../viml/parser/expressions.c:2791
+#, c-format
+msgid "E15: Cannot concatenate in assignments: %.*s"
+msgstr ""
+
+#: ../viml/parser/expressions.c:2813
+#, c-format
+msgid "E15: Expected value, got parenthesis: %.*s"
+msgstr ""
+
+#: ../viml/parser/expressions.c:2846
+#, c-format
+msgid "E15: Unexpected closing parenthesis: %.*s"
+msgstr ""
+
+#: ../viml/parser/expressions.c:2889
+#, c-format
+msgid "E15: Expected value, got question mark: %.*s"
+msgstr ""
+
+#: ../viml/parser/expressions.c:2911
+#, c-format
+msgid "E114: Missing double quote: %.*s"
+msgstr "E114: 缺少双引号:%.*s"
+
+#: ../viml/parser/expressions.c:2912
+#, c-format
+msgid "E115: Missing single quote: %.*s"
+msgstr "E115: 缺少单引号:%.*s"
+
+#: ../viml/parser/expressions.c:2931
+#, c-format
+msgid "E475: Expected closing bracket to end list assignment lvalue: %.*s"
+msgstr ""
+
+#: ../viml/parser/expressions.c:2934
+#, c-format
+msgid "E15: Misplaced assignment: %.*s"
+msgstr ""
+
+#: ../viml/parser/expressions.c:2938
+#, c-format
+msgid "E15: Unexpected assignment: %.*s"
+msgstr ""
+
+#: ../viml/parser/expressions.c:2965
+#, c-format
+msgid "E15: Expected value, got EOC: %.*s"
+msgstr ""
+
+#: ../viml/parser/expressions.c:2988
+#, c-format
+msgid "E116: Missing closing parenthesis for function call: %.*s"
+msgstr "E116: 函数调用缺少结束括号:%.*s"
+
+#: ../viml/parser/expressions.c:2993
+#, c-format
+msgid "E110: Missing closing parenthesis for nested expression: %.*s"
+msgstr "E110: 嵌套表达式缺少结束括号:%.*s"
+
+#: ../viml/parser/expressions.c:3001
+#, c-format
+msgid "E697: Missing end of List ']': %.*s"
+msgstr "E697: List 缺少结束符 ']': %.*s"
+
+#: ../viml/parser/expressions.c:3008
+#, c-format
+msgid "E723: Missing end of Dictionary '}': %.*s"
+msgstr "E723: 字典缺少结束符 '}':%.*s"
+
+#: ../viml/parser/expressions.c:3013
+#, c-format
+msgid "E15: Missing closing figure brace: %.*s"
+msgstr ""
+
+#: ../viml/parser/expressions.c:3018
+#, c-format
+msgid "E15: Missing closing figure brace for lambda: %.*s"
+msgstr ""
+
+#. Actually Vim throws E109 in more cases.
+#: ../viml/parser/expressions.c:3068
+#, c-format
+msgid "E109: Missing ':' after '?': %.*s"
+msgstr "E109: '?' 后缺少 ':':%.*s"
+
+#: ../window.c:97
+msgid "E1159: Cannot split a window when closing the buffer"
+msgstr "E1159: 关闭缓冲区时不能分割窗口"
+
+#: ../window.c:99
msgid "Already only one window"
msgstr "已经只剩一个窗口了"
-#: ../window.c:224
+#: ../window.c:259
msgid "E441: There is no preview window"
msgstr "E441: 没有预览窗口"
-#: ../window.c:559
+#: ../window.c:988
+msgid "E242: Can't split a window while closing another"
+msgstr "E242: 在关闭窗口时不能分割另一个窗口"
+
+#: ../window.c:1025
msgid "E442: Can't split topleft and botright at the same time"
msgstr "E442: 不能同时进行 topleft 和 botright 分割"
-#: ../window.c:1228
+#: ../window.c:1868
msgid "E443: Cannot rotate when another window is split"
msgstr "E443: 有其它分割窗口时不能旋转"
-#: ../window.c:1803
+#: ../window.c:2657
msgid "E444: Cannot close last window"
msgstr "E444: 不能关闭最后一个窗口"
-#: ../window.c:1810
-#, fuzzy
-msgid "E813: Cannot close autocmd window"
-msgstr "E444: 不能关闭最后一个窗口"
-
-#: ../window.c:1814
-#, fuzzy
+#: ../window.c:2671
msgid "E814: Cannot close window, only autocmd window would remain"
-msgstr "E444: 不能关闭最后一个窗口"
+msgstr "E814: 无法关闭窗口,不然就只剩下自动命令窗口了"
-#: ../window.c:2717
+#: ../window.c:3867
msgid "E445: Other window contains changes"
msgstr "E445: 其它窗口有改变的内容"
-#: ../window.c:4805
+#: ../window.c:6498
msgid "E446: No file name under cursor"
msgstr "E446: 光标处没有文件名"
-#~ msgid "Patch file"
-#~ msgstr "Patch 文件"
+#: ../../../runtime/optwin.vim:72
+msgid "(local to window)"
+msgstr "(局部于窗口)"
-#~ msgid ""
-#~ "&OK\n"
-#~ "&Cancel"
-#~ msgstr ""
-#~ "确定(&O)\n"
-#~ "取消(&C)"
+#: ../../../runtime/optwin.vim:73
+msgid "(local to buffer)"
+msgstr "(局部于缓冲区)"
-#~ msgid "E240: No connection to Vim server"
-#~ msgstr "E240: 没有到 Vim 服务器的连接"
+#: ../../../runtime/optwin.vim:74
+msgid "(global or local to buffer)"
+msgstr "(全局或局部于缓冲区)"
-#~ msgid "E241: Unable to send to %s"
-#~ msgstr "E241: 无法发送到 %s"
+#: ../../../runtime/optwin.vim:153
+msgid ""
+"\" Each \"set\" line shows the current value of an option (on the left)."
+msgstr "\" 每个 \"set\" 行显示左侧选项的当前值"
-#~ msgid "E277: Unable to read a server reply"
-#~ msgstr "E277: 无法读取服务器响应"
+#: ../../../runtime/optwin.vim:154
+msgid "\" Hit <Enter> on a \"set\" line to execute it."
+msgstr "\" 在 \"set\" 行上按 <回车> 来执行。"
-#~ msgid "E258: Unable to send to client"
-#~ msgstr "E258: 无法发送到客户端"
+#: ../../../runtime/optwin.vim:155
+msgid "\" A boolean option will be toggled."
+msgstr "\" 布尔选项将被切换。"
-#~ msgid "Save As"
-#~ msgstr "另存为"
+#: ../../../runtime/optwin.vim:156
+msgid ""
+"\" For other options you can edit the value before hitting "
+"<Enter>."
+msgstr "\" 对于其他选项,您可以在按 <回车> 之前编辑该值。"
-#~ msgid "Edit File"
-#~ msgstr "编辑文件"
+#: ../../../runtime/optwin.vim:157
+msgid "\" Hit <Enter> on a help line to open a help window on this option."
+msgstr "\" 在帮助行上按 <回车> 来打开关于此选项的帮助窗口。"
-#~ msgid " (NOT FOUND)"
-#~ msgstr " (找不到)"
+#: ../../../runtime/optwin.vim:158
+msgid "\" Hit <Enter> on an index line to jump there."
+msgstr "\" 在索引行上按 <回车> 来跳转到那里。"
-#~ msgid "Source Vim script"
-#~ msgstr "执行 Vim 脚本"
+#: ../../../runtime/optwin.vim:159
+msgid "\" Hit <Space> on a \"set\" line to refresh it."
+msgstr "\" 在 \"set\" 行上按 <空格> 来刷新。 "
-#~ msgid "Edit File in new window"
-#~ msgstr "在新窗口编辑文件"
+#: ../../../runtime/optwin.vim:228
+msgid "important"
+msgstr "重要选项"
-#~ msgid "Append File"
-#~ msgstr "追加文件"
+#: ../../../runtime/optwin.vim:229
+msgid "behave very Vi compatible (not advisable)"
+msgstr "非常兼容 Vi(不建议)"
-#~ msgid "Window position: X %d, Y %d"
-#~ msgstr "窗口位置: X %d, Y %d"
+#: ../../../runtime/optwin.vim:231
+msgid "list of flags to specify Vi compatibility"
+msgstr "指定 Vi 兼容性的标志列表"
-#~ msgid "Save Redirection"
-#~ msgstr "保存重定向"
+#: ../../../runtime/optwin.vim:233
+msgid "paste mode, insert typed text literally"
+msgstr "粘贴模式,按本义插入输入的文本"
-#~ msgid "Save View"
-#~ msgstr "保存视图"
+#: ../../../runtime/optwin.vim:235
+msgid "key sequence to toggle paste mode"
+msgstr "切换粘贴模式的按键序列"
-#~ msgid "Save Session"
-#~ msgstr "保存会话"
+#: ../../../runtime/optwin.vim:241
+msgid "list of directories used for runtime files and plugins"
+msgstr "运行时文件和插件使用的目录列表"
-#~ msgid "Save Setup"
-#~ msgstr "保存设定"
+#: ../../../runtime/optwin.vim:243
+msgid "list of directories used for plugin packages"
+msgstr "插件包使用的目录列表"
-#~ msgid "E196: No digraphs in this version"
-#~ msgstr "E196: 此版本无复合字符(digraph)"
+#: ../../../runtime/optwin.vim:245
+msgid "name of the main help file"
+msgstr "主帮助文件的名称"
-#~ msgid "Reading from stdin..."
-#~ msgstr "从标准输入读取..."
+#: ../../../runtime/optwin.vim:249
+msgid "moving around, searching and patterns"
+msgstr "移动、搜索以及正则表达式"
-#~ msgid "[NL found]"
-#~ msgstr "[找到 NL]"
+#: ../../../runtime/optwin.vim:250
+msgid "list of flags specifying which commands wrap to another line"
+msgstr "指定哪些命令折行的标志列表"
-#~ msgid "[crypted]"
-#~ msgstr "[已加密]"
+#: ../../../runtime/optwin.vim:252
+msgid ""
+"many jump commands move the cursor to the first non-blank\n"
+"character of a line"
+msgstr "许多跳转命令将光标移动到第一个非空的位置行中的字符"
-#~ msgid "NetBeans disallows writes of unmodified buffers"
-#~ msgstr "NetBeans 不允许未修改的缓冲区写入"
+#: ../../../runtime/optwin.vim:254
+msgid "nroff macro names that separate paragraphs"
+msgstr "用于分隔段落的 nroff 宏名"
-#~ msgid "Partial writes disallowed for NetBeans buffers"
-#~ msgstr "NetBeans 不允许缓冲区部分写入"
+#: ../../../runtime/optwin.vim:256
+msgid "nroff macro names that separate sections"
+msgstr "用于分隔小节的 nroff 宏名"
-#~ msgid "E460: The resource fork would be lost (add ! to override)"
-#~ msgstr "E460: Resource fork 会丢失 (请加 ! 强制执行)"
+#: ../../../runtime/optwin.vim:258
+msgid "list of directory names used for file searching"
+msgstr "用于文件搜索的目录名称列表"
-#~ msgid "E229: Cannot start the GUI"
-#~ msgstr "E229: 无法启动图形界面"
+#: ../../../runtime/optwin.vim:261
+msgid ":cd without argument goes to the home directory"
+msgstr "不带参数的 :cd 进入主目录"
-#~ msgid "E230: Cannot read from \"%s\""
-#~ msgstr "E230: 无法读取文件 \"%s\""
+#: ../../../runtime/optwin.vim:263
+msgid "list of directory names used for :cd"
+msgstr "目录名称列表用于 :cd"
-#~ msgid "E665: Cannot start GUI, no valid font found"
-#~ msgstr "E665: 无法启动图形界面,找不到有效的字体"
+#: ../../../runtime/optwin.vim:266
+msgid "change to directory of file in buffer"
+msgstr "切换到缓冲区的文件所在的目录"
-#~ msgid "E231: 'guifontwide' invalid"
-#~ msgstr "E231: 无效的 'guifontwide'"
+#: ../../../runtime/optwin.vim:269
+msgid "search commands wrap around the end of the buffer"
+msgstr "搜索在缓冲区折行的命令"
-#~ msgid "E599: Value of 'imactivatekey' is invalid"
-#~ msgstr "E599: 'imactivatekey' 的值无效"
+#: ../../../runtime/optwin.vim:271
+msgid "show match for partly typed search command"
+msgstr "显示匹配部分键入的搜索命令"
-#~ msgid "E254: Cannot allocate color %s"
-#~ msgstr "E254: 无法分配颜色 %s"
+#: ../../../runtime/optwin.vim:273
+msgid "change the way backslashes are used in search patterns"
+msgstr "改变反斜杠在搜索模式中的使用方式"
-#~ msgid "No match at cursor, finding next"
-#~ msgstr "在光标处没有匹配,查找下一个"
+#: ../../../runtime/optwin.vim:275
+msgid "select the default regexp engine used"
+msgstr "选择默认的正则表达式引擎"
-#~ msgid "<cannot open> "
-#~ msgstr "<无法打开>"
+#: ../../../runtime/optwin.vim:277
+msgid "ignore case when using a search pattern"
+msgstr "使用搜索模式时忽略大小写"
-#~ msgid "E616: vim_SelFile: can't get font %s"
-#~ msgstr "E616: vim_SelFile: 无法获取字体 %s"
+#: ../../../runtime/optwin.vim:279
+msgid "override 'ignorecase' when pattern has upper case characters"
+msgstr "当模式包含大写字符时,覆盖 'ignorecase'"
-#~ msgid "E614: vim_SelFile: can't return to current directory"
-#~ msgstr "E614: vim_SelFile: 无法返回当前目录"
+#: ../../../runtime/optwin.vim:281
+msgid "what method to use for changing case of letters"
+msgstr "用什么方法来改变字母的大小写"
-#~ msgid "Pathname:"
-#~ msgstr "路径:"
+#: ../../../runtime/optwin.vim:283
+msgid "maximum amount of memory in Kbyte used for pattern matching"
+msgstr "模式匹配使用的最大内存(以千字节为单位)"
-#~ msgid "E615: vim_SelFile: can't get current directory"
-#~ msgstr "E615: vim_SelFile: 无法获取当前目录"
+#: ../../../runtime/optwin.vim:285
+msgid "pattern for a macro definition line"
+msgstr "宏定义行的模式"
-#~ msgid "OK"
-#~ msgstr "确定"
+#: ../../../runtime/optwin.vim:289
+msgid "pattern for an include-file line"
+msgstr "包含文件行的模式"
-#~ msgid "Cancel"
-#~ msgstr "取消"
+#: ../../../runtime/optwin.vim:292
+msgid "expression used to transform an include line to a file name"
+msgstr "用于将包含行转换为文件名的表达式"
-#~ msgid "Scrollbar Widget: Could not get geometry of thumb pixmap."
-#~ msgstr "滚动条部件: 无法获取滑块图像的几何大小"
+#: ../../../runtime/optwin.vim:298
+msgid "tags"
+msgstr "标签"
-#~ msgid "Vim dialog"
-#~ msgstr "Vim 对话框"
+#: ../../../runtime/optwin.vim:299
+msgid "use binary searching in tags files"
+msgstr "在标签文件中使用二分法查找"
-#~ msgid "E232: Cannot create BalloonEval with both message and callback"
-#~ msgstr "E232: 不能同时使用消息和回调函数来创建 BalloonEval"
+#: ../../../runtime/optwin.vim:301
+msgid "number of significant characters in a tag name or zero"
+msgstr "标签名称中的有效字符数,默认为零"
-#~ msgid "Vim dialog..."
-#~ msgstr "Vim 对话框..."
+#: ../../../runtime/optwin.vim:303
+msgid "list of file names to search for tags"
+msgstr "用于搜索标签的文件名列表"
-#~ msgid "Input _Methods"
-#~ msgstr "输入法(_M)"
+#: ../../../runtime/optwin.vim:306
+msgid ""
+"how to handle case when searching in tags files:\n"
+"\"followic\" to follow 'ignorecase', \"ignore\" or \"match\""
+msgstr ""
+"在标签文件中搜索如何处理大小写: \"followic\" 跟随 'ignorecase', \"ignore\" 或"
+"者 \"match\""
-#~ msgid "VIM - Search and Replace..."
-#~ msgstr "VIM - 查找和替换..."
+#: ../../../runtime/optwin.vim:309
+msgid "file names in a tags file are relative to the tags file"
+msgstr "标签文件里的文件名是相对于标签文件的路径"
-#~ msgid "VIM - Search..."
-#~ msgstr "VIM - 查找..."
+#: ../../../runtime/optwin.vim:311
+msgid "a :tag command will use the tagstack"
+msgstr ":tag 命令将使用 tagstack"
-#~ msgid "Find what:"
-#~ msgstr "查找内容:"
+#: ../../../runtime/optwin.vim:313
+msgid "when completing tags in Insert mode show more info"
+msgstr "在插入模式补全标签时显示更多信息"
-#~ msgid "Replace with:"
-#~ msgstr "替换为:"
+#: ../../../runtime/optwin.vim:316
+msgid "a function to be used to perform tag searches"
+msgstr "用于执行标签搜索的函数"
-#~ msgid "Match whole word only"
-#~ msgstr "匹配完整的词"
+#: ../../../runtime/optwin.vim:322
+msgid "displaying text"
+msgstr "显示文本"
-#~ msgid "Match case"
-#~ msgstr "匹配大小写"
+#: ../../../runtime/optwin.vim:323
+msgid "number of lines to scroll for CTRL-U and CTRL-D"
+msgstr "按 CTRL-U 和 CTRL-D 滚动的行数"
-#~ msgid "Direction"
-#~ msgstr "方向"
+#: ../../../runtime/optwin.vim:326
+msgid "number of screen lines to show around the cursor"
+msgstr "在光标周围显示的屏幕行数"
-#~ msgid "Up"
-#~ msgstr "向上"
+#: ../../../runtime/optwin.vim:328
+msgid "long lines wrap"
+msgstr "长行自动换行"
-#~ msgid "Down"
-#~ msgstr "向下"
+#: ../../../runtime/optwin.vim:331
+msgid "wrap long lines at a character in 'breakat'"
+msgstr "在 'breakat' 中的字符处对长行折行"
-#~ msgid "Find Next"
-#~ msgstr "查找下一个"
+#: ../../../runtime/optwin.vim:334
+msgid "preserve indentation in wrapped text"
+msgstr "在折行文本中保持缩进"
-#~ msgid "Replace"
-#~ msgstr "替换"
+#: ../../../runtime/optwin.vim:337
+msgid "adjust breakindent behaviour"
+msgstr "调整 breakindent 的行为"
-#~ msgid "Replace All"
-#~ msgstr "全部替换"
+#: ../../../runtime/optwin.vim:340
+msgid "which characters might cause a line break"
+msgstr "哪些字符可能导致换行"
-#~ msgid "Vim: Received \"die\" request from session manager\n"
-#~ msgstr "Vim: 从会话管理器收到 \"die\" 请求\n"
+#: ../../../runtime/optwin.vim:342
+msgid "string to put before wrapped screen lines"
+msgstr "放在折回的屏幕行之前的字符串"
-#~ msgid "Close"
-#~ msgstr "关闭"
+#: ../../../runtime/optwin.vim:344
+msgid "minimal number of columns to scroll horizontally"
+msgstr "水平滚动的最小列数"
-#~ msgid "New tab"
-#~ msgstr "新建标签"
+#: ../../../runtime/optwin.vim:346
+msgid "minimal number of columns to keep left and right of the cursor"
+msgstr "保留在光标左右的最小列数"
-#~ msgid "Open Tab..."
-#~ msgstr "打开标签..."
+#: ../../../runtime/optwin.vim:348
+msgid ""
+"include \"lastline\" to show the last line even if it doesn't fit\n"
+"include \"uhex\" to show unprintable characters as a hex number"
+msgstr ""
+"包含 \"lastline\" 来显示最后一行,即使它显示不下\n"
+"包含 \"uhex\" 以十六进制显示不可打印字符"
-#~ msgid "Vim: Main window unexpectedly destroyed\n"
-#~ msgstr "Vim: 主窗口被意外地摧毁\n"
+#: ../../../runtime/optwin.vim:350
+msgid "characters to use for the status line, folds and filler lines"
+msgstr "用于状态行、折叠和填充行的字符"
-#~ msgid "Font Selection"
-#~ msgstr "选择字体"
+#: ../../../runtime/optwin.vim:352
+msgid "number of lines used for the command-line"
+msgstr "用于命令行的行数"
-#~ msgid "Used CUT_BUFFER0 instead of empty selection"
-#~ msgstr "使用 CUT_BUFFER0 来取代空选择"
+#: ../../../runtime/optwin.vim:354
+msgid "width of the display"
+msgstr "显示的宽度"
-#~ msgid "&Filter"
-#~ msgstr "过滤(&F)"
+#: ../../../runtime/optwin.vim:356
+msgid "number of lines in the display"
+msgstr "显示的行数"
-#~ msgid "&Cancel"
-#~ msgstr "取消(&C)"
+#: ../../../runtime/optwin.vim:358
+msgid "number of lines to scroll for CTRL-F and CTRL-B"
+msgstr "按 CTRL-F 和 CTRL-B 滚动的行数"
-#~ msgid "Directories"
-#~ msgstr "目录"
+#: ../../../runtime/optwin.vim:360
+msgid "don't redraw while executing macros"
+msgstr "在执行宏时不要重新绘制"
-#~ msgid "Filter"
-#~ msgstr "过滤器"
+#: ../../../runtime/optwin.vim:363
+msgid "timeout for 'hlsearch' and :match highlighting in msec"
+msgstr "'hlsearch' 和 :match 高亮的超时时间(以毫秒计)"
-#~ msgid "&Help"
-#~ msgstr "帮助(&H)"
+#: ../../../runtime/optwin.vim:366
+msgid ""
+"delay in msec for each char written to the display\n"
+"(for debugging)"
+msgstr "每个字符写到显示的延时(以毫秒计;用于调试)"
-#~ msgid "Files"
-#~ msgstr "文件"
+#: ../../../runtime/optwin.vim:368
+msgid "show <Tab> as ^I and end-of-line as $"
+msgstr "以 ^I 显示 <Tab>, 以 $ 显示行尾"
-#~ msgid "&OK"
-#~ msgstr "确定(&O)"
+#: ../../../runtime/optwin.vim:371
+msgid "list of strings used for list mode"
+msgstr "用于 list 模式的字符串列表"
-#~ msgid "Selection"
-#~ msgstr "选择"
+#: ../../../runtime/optwin.vim:373
+msgid "show the line number for each line"
+msgstr "对每一行显示行号"
-#~ msgid "Find &Next"
-#~ msgstr "查找下一个(&N)"
+#: ../../../runtime/optwin.vim:376
+msgid "show the relative line number for each line"
+msgstr "显示每行的相对行号"
-#~ msgid "&Replace"
-#~ msgstr "替换(&R)"
+#: ../../../runtime/optwin.vim:380
+msgid "number of columns to use for the line number"
+msgstr "用于行号的列数"
-#~ msgid "Replace &All"
-#~ msgstr "全部替换(&A)"
+#: ../../../runtime/optwin.vim:385
+msgid "controls whether concealable text is hidden"
+msgstr "控制是否隐藏可隐藏文本"
-#~ msgid "&Undo"
-#~ msgstr "撤销(&U)"
+#: ../../../runtime/optwin.vim:388
+msgid "modes in which text in the cursor line can be concealed"
+msgstr "可隐藏光标行的文本的模式"
-#~ msgid "E671: Cannot find window title \"%s\""
-#~ msgstr "E671: 找不到窗口标题 \"%s\""
+#: ../../../runtime/optwin.vim:394
+msgid "syntax, highlighting and spelling"
+msgstr "语法、高亮和拼写"
-#~ msgid "E243: Argument not supported: \"-%s\"; Use the OLE version."
-#~ msgstr "E243: 不支持的参数: \"-%s\";请使用 OLE 版本。"
+#: ../../../runtime/optwin.vim:395
+msgid "\"dark\" or \"light\"; the background color brightness"
+msgstr "\"dark\" 或者 \"light\";背景色亮度"
-#~ msgid "E672: Unable to open window inside MDI application"
-#~ msgstr "E672: 无法在 MDI 应用程序中打开窗口"
+#: ../../../runtime/optwin.vim:397
+msgid "type of file; triggers the FileType event when set"
+msgstr "文件类型; 在设置时触发 FileType 事件"
-#~ msgid "Close tab"
-#~ msgstr "关闭标签"
+#: ../../../runtime/optwin.vim:401
+msgid "name of syntax highlighting used"
+msgstr "使用中的语法高亮显示的名称"
-#~ msgid "Open tab..."
-#~ msgstr "打开标签..."
+#: ../../../runtime/optwin.vim:404
+msgid "maximum column to look for syntax items"
+msgstr "寻找语法项的最大列"
-#~ msgid "Find string (use '\\\\' to find a '\\')"
-#~ msgstr "查找字符串 (使用 '\\\\' 来查找 '\\')"
+#: ../../../runtime/optwin.vim:408
+msgid "which highlighting to use for various occasions"
+msgstr "在不同场合使用哪些高亮提示"
-#~ msgid "Find & Replace (use '\\\\' to find a '\\')"
-#~ msgstr "查找和替换字符串 (使用 '\\\\' 来查找 '\\')"
+#: ../../../runtime/optwin.vim:410
+msgid "highlight all matches for the last used search pattern"
+msgstr "高亮显示最后使用的搜索模式的所有匹配项"
-#~ msgid "Not Used"
-#~ msgstr "未使用"
+#: ../../../runtime/optwin.vim:413
+msgid "use GUI colors for the terminal"
+msgstr "为终端使用 GUI 颜色"
-#~ msgid "Directory\t*.nothing\n"
-#~ msgstr "目录\t*.nothing\n"
+#: ../../../runtime/optwin.vim:417
+msgid "highlight the screen column of the cursor"
+msgstr "突出显示光标的屏幕列"
-#~ msgid ""
-#~ "Vim E458: Cannot allocate colormap entry, some colors may be incorrect"
-#~ msgstr "Vim E458: 无法分配颜色表项,某些颜色可能不正确"
+#: ../../../runtime/optwin.vim:420
+msgid "highlight the screen line of the cursor"
+msgstr "突出显示光标的屏幕行"
-#~ msgid "E250: Fonts for the following charsets are missing in fontset %s:"
-#~ msgstr "E250: Fontset %s 缺少下列字符集的字体:"
+#: ../../../runtime/optwin.vim:423
+msgid "specifies which area 'cursorline' highlights"
+msgstr "指定 'cursorline' 高亮显示的区域"
-#~ msgid "E252: Fontset name: %s"
-#~ msgstr "E252: Fontset 名称: %s"
+#: ../../../runtime/optwin.vim:426
+msgid "columns to highlight"
+msgstr "要高亮的列"
-#~ msgid "Font '%s' is not fixed-width"
-#~ msgstr "'%s' 不是固定宽度的字体"
+#: ../../../runtime/optwin.vim:429
+msgid "highlight spelling mistakes"
+msgstr "高亮拼写错误"
-#~ msgid "E253: Fontset name: %s\n"
-#~ msgstr "E253: Fontset 名称: %s\n"
+#: ../../../runtime/optwin.vim:432
+msgid "list of accepted languages"
+msgstr "接受的语言列表"
-#~ msgid "Font0: %s\n"
-#~ msgstr "字体0: %s\n"
+#: ../../../runtime/optwin.vim:435
+msgid "file that \"zg\" adds good words to"
+msgstr "\"zg\" 添加正确单词的文件"
-#~ msgid "Font1: %s\n"
-#~ msgstr "字体1: %s\n"
+#: ../../../runtime/optwin.vim:438
+msgid "pattern to locate the end of a sentence"
+msgstr "定位句子尾部的模式"
-#~ msgid "Font%<PRId64> width is not twice that of font0\n"
-#~ msgstr "字体%<PRId64>的宽度不是字体0的两倍\n"
+#: ../../../runtime/optwin.vim:441
+msgid "flags to change how spell checking works"
+msgstr "更改拼写检查工作方式的标志"
-#~ msgid "Font0 width: %<PRId64>\n"
-#~ msgstr "字体0的宽度:%<PRId64>\n"
+#: ../../../runtime/optwin.vim:444
+msgid "methods used to suggest corrections"
+msgstr "用于建议修正的方法"
-#~ msgid ""
-#~ "Font1 width: %<PRId64>\n"
-#~ "\n"
-#~ msgstr ""
-#~ "字体1的宽度: %<PRId64>\n"
-#~ "\n"
+#: ../../../runtime/optwin.vim:446
+msgid "amount of memory used by :mkspell before compressing"
+msgstr ":mkspell 在压缩前使用的内存量"
-#~ msgid "Invalid font specification"
-#~ msgstr "指定了无效的字体"
+#: ../../../runtime/optwin.vim:451
+msgid "multiple windows"
+msgstr "多个窗口"
-#~ msgid "&Dismiss"
-#~ msgstr "取消(&D)"
+#: ../../../runtime/optwin.vim:452
+msgid "0, 1, 2 or 3; when to use a status line for the last window"
+msgstr "0、1、2 或 3;何时为最后一个窗口使用状态行"
-#~ msgid "no specific match"
-#~ msgstr "找不到匹配的项"
+#: ../../../runtime/optwin.vim:455
+msgid "alternate format to be used for a status line"
+msgstr "用于状态行的替代格式"
-#~ msgid "Vim - Font Selector"
-#~ msgstr "Vim - 字体选择器"
+#: ../../../runtime/optwin.vim:458
+msgid "make all windows the same size when adding/removing windows"
+msgstr "当添加/删除窗口时,使所有窗口的大小相同"
-#~ msgid "Name:"
-#~ msgstr "名称:"
+#: ../../../runtime/optwin.vim:460
+msgid "in which direction 'equalalways' works: \"ver\", \"hor\" or \"both\""
+msgstr "'equalalways' 的工作方向:\"ver\", \"hor\" 或者 \"both\""
-#~ msgid "Encoding:"
-#~ msgstr "编码:"
+#: ../../../runtime/optwin.vim:462
+msgid "minimal number of lines used for the current window"
+msgstr "当前窗口的最小行数"
-#~ msgid "Font:"
-#~ msgstr "字体:"
+#: ../../../runtime/optwin.vim:464
+msgid "minimal number of lines used for any window"
+msgstr "任何窗口的最小行数"
-#~ msgid "Style:"
-#~ msgstr "风格:"
+#: ../../../runtime/optwin.vim:466
+msgid "keep the height of the window"
+msgstr "保持窗口的高度"
-#~ msgid "Size:"
-#~ msgstr "尺寸:"
+#: ../../../runtime/optwin.vim:469
+msgid "keep the width of the window"
+msgstr "保持窗口的宽度"
-#~ msgid "E256: Hangul automata ERROR"
-#~ msgstr "E256: Hangul automata 错误"
+#: ../../../runtime/optwin.vim:472
+msgid "minimal number of columns used for the current window"
+msgstr "当前窗口的最小列数"
-#~ msgid "E563: stat error"
-#~ msgstr "E563: stat 错误"
+#: ../../../runtime/optwin.vim:474
+msgid "minimal number of columns used for any window"
+msgstr "任何窗口的最小列数"
-#~ msgid "E625: cannot open cscope database: %s"
-#~ msgstr "E625: 无法打开 cscope 数据库: %s"
+#: ../../../runtime/optwin.vim:476
+msgid "initial height of the help window"
+msgstr "帮助窗口的初始高度"
-#~ msgid "E626: cannot get cscope database information"
-#~ msgstr "E626: 无法获取 cscope 数据库信息"
+#: ../../../runtime/optwin.vim:481
+msgid "default height for the preview window"
+msgstr "预览窗口的默认高度"
-#~ msgid "E569: maximum number of cscope connections reached"
-#~ msgstr "E569: 已达到 cscope 的最大连接数"
+#: ../../../runtime/optwin.vim:483
+msgid "identifies the preview window"
+msgstr "标识预览窗口"
-#~ msgid ""
-#~ "???: Sorry, this command is disabled, the MzScheme library could not be "
-#~ "loaded."
-#~ msgstr "???: 抱歉,此命令不可用,无法加载 MzScheme 库"
+#: ../../../runtime/optwin.vim:487
+msgid "don't unload a buffer when no longer shown in a window"
+msgstr "当缓冲区不再显示在窗口中时,不要卸载它"
-#~ msgid "invalid expression"
-#~ msgstr "无效的表达式"
+#: ../../../runtime/optwin.vim:489
+msgid ""
+"\"useopen\" and/or \"split\"; which window to use when jumping\n"
+"to a buffer"
+msgstr "当向缓冲区跳转时可使用窗口: \"useopen\" 和/或 \"split\""
-#~ msgid "expressions disabled at compile time"
-#~ msgstr "编译时没有启用表达式"
+#: ../../../runtime/optwin.vim:491
+msgid "a new window is put below the current one"
+msgstr "新窗口放在当前窗口的下面"
-#~ msgid "hidden option"
-#~ msgstr "隐藏的选项"
+#: ../../../runtime/optwin.vim:493
+msgid "determines scroll behavior for split windows"
+msgstr ""
-#~ msgid "unknown option"
-#~ msgstr "未知的选项"
+#: ../../../runtime/optwin.vim:495
+msgid "a new window is put right of the current one"
+msgstr "新窗口放在当前窗口的右边"
-#~ msgid "window index is out of range"
-#~ msgstr "窗口索引超出范围"
+#: ../../../runtime/optwin.vim:497
+msgid "this window scrolls together with other bound windows"
+msgstr "此窗口与其他已绑定的窗口一起滚动"
-#~ msgid "couldn't open buffer"
-#~ msgstr "无法打开缓冲区"
+#: ../../../runtime/optwin.vim:500
+msgid "\"ver\", \"hor\" and/or \"jump\"; list of options for 'scrollbind'"
+msgstr "'scrollbind' 的选项列表: \"ver\", \"hor\" 和/或 \"jump\""
-#~ msgid "cannot save undo information"
-#~ msgstr "无法保存撤销信息"
+#: ../../../runtime/optwin.vim:502
+msgid "this window's cursor moves together with other bound windows"
+msgstr "此窗口的光标与其他已绑定的窗口一起移动"
-#~ msgid "cannot delete line"
-#~ msgstr "无法删除行"
+#: ../../../runtime/optwin.vim:506
+msgid "size of a terminal window"
+msgstr "终端窗口的大小"
-#~ msgid "cannot replace line"
-#~ msgstr "无法替换行"
+#: ../../../runtime/optwin.vim:509
+msgid "key that precedes Vim commands in a terminal window"
+msgstr "终端窗口中 Vim 命令的前导键"
-#~ msgid "cannot insert line"
-#~ msgstr "无法插入行"
+#: ../../../runtime/optwin.vim:515
+msgid "multiple tab pages"
+msgstr "多个标签页"
-#~ msgid "string cannot contain newlines"
-#~ msgstr "字符串不能包含换行(NL)"
+#: ../../../runtime/optwin.vim:516
+msgid "0, 1 or 2; when to use a tab pages line"
+msgstr "0, 1 或 2; 何时使用标签页行"
-#~ msgid "Vim error: ~a"
-#~ msgstr "Vim 错误: ~a"
+#: ../../../runtime/optwin.vim:518
+msgid "maximum number of tab pages to open for -p and \"tab all\""
+msgstr "-p 和 \"tab all\" 打开的最大标签页数"
-#~ msgid "Vim error"
-#~ msgstr "Vim 错误"
+#: ../../../runtime/optwin.vim:520
+msgid "custom tab pages line"
+msgstr "自定义标签页行"
-#~ msgid "buffer is invalid"
-#~ msgstr "缓冲区无效"
+#: ../../../runtime/optwin.vim:523
+msgid "custom tab page label for the GUI"
+msgstr "为 GUI 自定义标签页的标签"
-#~ msgid "window is invalid"
-#~ msgstr "窗口无效"
+#: ../../../runtime/optwin.vim:525
+msgid "custom tab page tooltip for the GUI"
+msgstr "为 GUI 自定义标签页的工具提示"
-#~ msgid "linenr out of range"
-#~ msgstr "行号超出范围"
+#: ../../../runtime/optwin.vim:530
+msgid "terminal"
+msgstr "终端"
-#~ msgid "not allowed in the Vim sandbox"
-#~ msgstr "不允许在 sandbox 中使用"
+#: ../../../runtime/optwin.vim:531
+msgid "minimal number of lines to scroll at a time"
+msgstr "一次滚动的最少行数"
-#~ msgid ""
-#~ "E263: Sorry, this command is disabled, the Python library could not be "
-#~ "loaded."
-#~ msgstr "E263: 抱歉,此命令不可用,无法加载 Python 库。"
+#: ../../../runtime/optwin.vim:534
+msgid "specifies what the cursor looks like in different modes"
+msgstr "指定光标在不同模式下的样子"
-#~ msgid "E659: Cannot invoke Python recursively"
-#~ msgstr "E659: 不能递归调用 Python"
+#: ../../../runtime/optwin.vim:539
+msgid "show info in the window title"
+msgstr "在窗口标题中显示信息"
-#~ msgid "can't delete OutputObject attributes"
-#~ msgstr "不能删除 OutputObject 属性"
+#: ../../../runtime/optwin.vim:542
+msgid "percentage of 'columns' used for the window title"
+msgstr "窗口标题的 'columns' 的百分比"
-#~ msgid "softspace must be an integer"
-#~ msgstr "softspace 必须是整数"
+#: ../../../runtime/optwin.vim:544
+msgid "when not empty, string to be used for the window title"
+msgstr "非空时,用于窗口标题的字符串"
-#~ msgid "invalid attribute"
-#~ msgstr "无效的属性"
+#: ../../../runtime/optwin.vim:546
+msgid "string to restore the title to when exiting Vim"
+msgstr "退出 Vim 时用于恢复标题的字符串"
-#~ msgid "writelines() requires list of strings"
-#~ msgstr "writelines() 需要字符串列表作参数"
+#: ../../../runtime/optwin.vim:549
+msgid "set the text of the icon for this window"
+msgstr "设置此窗口图标的文本"
-#~ msgid "E264: Python: Error initialising I/O objects"
-#~ msgstr "E264: Python: 初始化 I/O 对象出错"
+#: ../../../runtime/optwin.vim:552
+msgid "when not empty, text for the icon of this window"
+msgstr "非空时,设置此窗口图标的文本"
-#~ msgid "attempt to refer to deleted buffer"
-#~ msgstr "试图引用已被删除的缓冲区"
+#: ../../../runtime/optwin.vim:557
+msgid "using the mouse"
+msgstr "使用鼠标"
-#~ msgid "line number out of range"
-#~ msgstr "行号超出范围"
+#: ../../../runtime/optwin.vim:558
+msgid "list of flags for using the mouse"
+msgstr "使用鼠标时的标志列表"
-#~ msgid "<buffer object (deleted) at %8lX>"
-#~ msgstr "<缓冲区对象(已删除): %8lX>"
+#: ../../../runtime/optwin.vim:561
+msgid "the window with the mouse pointer becomes the current one"
+msgstr "带有鼠标指针的窗口成为当前窗口"
-#~ msgid "invalid mark name"
-#~ msgstr "无效的标记名称"
+#: ../../../runtime/optwin.vim:563
+msgid "hide the mouse pointer while typing"
+msgstr "在输入时隐藏鼠标指针"
-#~ msgid "no such buffer"
-#~ msgstr "无此缓冲区"
+#: ../../../runtime/optwin.vim:566
+msgid ""
+"\"extend\", \"popup\" or \"popup_setpos\"; what the right\n"
+"mouse button is used for"
+msgstr "\"extend\", \"popup\" 或 \"popup_setpos\"; 鼠标右键用于什么"
-#~ msgid "attempt to refer to deleted window"
-#~ msgstr "试图引用已被删除的窗口"
+#: ../../../runtime/optwin.vim:568
+msgid "maximum time in msec to recognize a double-click"
+msgstr "识别双击的最大时间(以毫秒计)"
-#~ msgid "readonly attribute"
-#~ msgstr "只读属性"
+#: ../../../runtime/optwin.vim:571
+msgid "what the mouse pointer looks like in different modes"
+msgstr "鼠标指针在不同模式下的样子"
-#~ msgid "cursor position outside buffer"
-#~ msgstr "光标位置在缓冲区外"
+#: ../../../runtime/optwin.vim:577
+msgid "GUI"
+msgstr "图形用户界面"
-#~ msgid "<window object (deleted) at %.8lX>"
-#~ msgstr "<窗口对象(已删除): %.8lX>"
+#: ../../../runtime/optwin.vim:578
+msgid "list of font names to be used in the GUI"
+msgstr "在 GUI 中使用的字体名称列表"
-#~ msgid "<window object (unknown) at %.8lX>"
-#~ msgstr "<窗口对象(未知): %.8lX>"
+#: ../../../runtime/optwin.vim:581
+msgid "pair of fonts to be used, for multibyte editing"
+msgstr "用于多字节编辑的字体对"
-#~ msgid "<window %d>"
-#~ msgstr "<窗口 %d>"
+#: ../../../runtime/optwin.vim:584
+msgid "list of font names to be used for double-wide characters"
+msgstr "用于双宽度字符的字体名称列表"
-#~ msgid "no such window"
-#~ msgstr "无此窗口"
+#: ../../../runtime/optwin.vim:586
+msgid "list of flags that specify how the GUI works"
+msgstr "指定 GUI 工作方式的标志列表"
-#~ msgid ""
-#~ "E266: Sorry, this command is disabled, the Ruby library could not be "
-#~ "loaded."
-#~ msgstr "E266: 抱歉,此命令不可用,无法加载 Ruby 库"
+#: ../../../runtime/optwin.vim:589
+msgid "\"icons\", \"text\" and/or \"tooltips\"; how to show the toolbar"
+msgstr "\"icons\", \"text\" 和/或 \"tooltips\"; 如何显示工具栏"
-#~ msgid "E273: unknown longjmp status %d"
-#~ msgstr "E273: 未知的 longjmp 状态 %d"
+#: ../../../runtime/optwin.vim:592
+msgid "size of toolbar icons"
+msgstr "工具栏图标大小"
-#~ msgid "Toggle implementation/definition"
-#~ msgstr "切换实现/定义"
+#: ../../../runtime/optwin.vim:597
+msgid ""
+"\"last\", \"buffer\" or \"current\": which directory used for the file "
+"browser"
+msgstr "\"last\", \"buffer\" 或 \"current\"; 文件浏览器使用哪个目录"
-#~ msgid "Show base class of"
-#~ msgstr "显示 base class of:"
+#: ../../../runtime/optwin.vim:601
+msgid "language to be used for the menus"
+msgstr "菜单使用的语言"
-#~ msgid "Show overridden member function"
-#~ msgstr "显示被覆盖的成员函数"
+#: ../../../runtime/optwin.vim:604
+msgid "maximum number of items in one menu"
+msgstr "单个菜单中的最大项目数量"
-#~ msgid "Retrieve from file"
-#~ msgstr "恢复: 从文件"
+#: ../../../runtime/optwin.vim:607
+msgid "\"no\", \"yes\" or \"menu\"; how to use the ALT key"
+msgstr "\"no\", \"yes\" 或 \"menu\"; 如何使用 ALT 键"
-#~ msgid "Retrieve from project"
-#~ msgstr "恢复: 从对象"
+#: ../../../runtime/optwin.vim:610
+msgid "number of pixel lines to use between characters"
+msgstr "字符之间使用的像素行数"
-#~ msgid "Retrieve from all projects"
-#~ msgstr "恢复: 从所有项目"
+#: ../../../runtime/optwin.vim:613
+msgid "delay in milliseconds before a balloon may pop up"
+msgstr "延迟指定毫秒后,气泡可能会弹出"
-#~ msgid "Retrieve"
-#~ msgstr "恢复"
+#: ../../../runtime/optwin.vim:616
+msgid "use balloon evaluation in the GUI"
+msgstr "在 GUI 中使用气泡求值"
-#~ msgid "Show source of"
-#~ msgstr "显示源代码: "
+#: ../../../runtime/optwin.vim:620
+msgid "use balloon evaluation in the terminal"
+msgstr "在终端中使用气泡求值"
-#~ msgid "Find symbol"
-#~ msgstr "查找 symbol"
+#: ../../../runtime/optwin.vim:624
+msgid "expression to show in balloon eval"
+msgstr "在气泡求值中显示的表达式"
-#~ msgid "Browse class"
-#~ msgstr "浏览 class"
+#: ../../../runtime/optwin.vim:631
+msgid "printing"
+msgstr "打印"
-#~ msgid "Show class in hierarchy"
-#~ msgstr "显示层次关系的类"
+#: ../../../runtime/optwin.vim:654
+msgid "messages and info"
+msgstr "消息和信息"
-#~ msgid "Show class in restricted hierarchy"
-#~ msgstr "显示 restricted 层次关系的 class"
+#: ../../../runtime/optwin.vim:655
+msgid "add 's' flag in 'shortmess' (don't show search message)"
+msgstr "在 'shortmess' 中添加 's' 标志(不显示搜索消息)"
-#~ msgid "Xref refers to"
-#~ msgstr "Xref 参考到"
+#: ../../../runtime/optwin.vim:657
+msgid "list of flags to make messages shorter"
+msgstr "使消息更短的标志列表"
-#~ msgid "Xref referred by"
-#~ msgstr "Xref 被谁参考:"
+#: ../../../runtime/optwin.vim:659
+msgid "show (partial) command keys in the status line"
+msgstr "在状态行中显示(不完整的)命令键"
-#~ msgid "Xref has a"
-#~ msgstr "Xref 有"
+#: ../../../runtime/optwin.vim:663
+msgid "display the current mode in the status line"
+msgstr "在状态行中显示当前模式"
-#~ msgid "Xref used by"
-#~ msgstr "Xref 被谁使用:"
+#: ../../../runtime/optwin.vim:665
+msgid "show cursor position below each window"
+msgstr "在每个窗口下方显示光标的位置"
-#~ msgid "Show docu of"
-#~ msgstr "显示文件: "
+#: ../../../runtime/optwin.vim:670
+msgid "alternate format to be used for the ruler"
+msgstr "ruler 的替代格式"
-#~ msgid "Generate docu for"
-#~ msgstr "产生文件: "
+#: ../../../runtime/optwin.vim:673
+msgid "threshold for reporting number of changed lines"
+msgstr "报告已更改行数的阈值"
-#~ msgid ""
-#~ "Cannot connect to SNiFF+. Check environment (sniffemacs must be found in "
-#~ "$PATH).\n"
-#~ msgstr ""
-#~ "不能连接到 SNiFF+。请检查环境变量 ($PATH 里必需可以找到 sniffemacs)\n"
+#: ../../../runtime/optwin.vim:675
+msgid "the higher the more messages are given"
+msgstr "等级越高,给出的信息越多"
-#~ msgid "E274: Sniff: Error during read. Disconnected"
-#~ msgstr "E274: Sniff: 读取错误. 取消连接"
+#: ../../../runtime/optwin.vim:677
+msgid "file to write messages in"
+msgstr "用于写入消息的文件"
-#~ msgid "SNiFF+ is currently "
-#~ msgstr "SNiFF+ 目前"
+#: ../../../runtime/optwin.vim:679
+msgid "pause listings when the screen is full"
+msgstr "当屏幕满时暂停显示清单"
-#~ msgid "not "
-#~ msgstr "未"
+#: ../../../runtime/optwin.vim:682
+msgid "start a dialog when a command fails"
+msgstr "当命令失败时开启对话框"
-#~ msgid "connected"
-#~ msgstr "连接中"
+#: ../../../runtime/optwin.vim:685
+msgid "ring the bell for error messages"
+msgstr "错误信息响铃"
-#~ msgid "E275: Unknown SNiFF+ request: %s"
-#~ msgstr "E275: 不正确的 SNiff+ 调用: %s"
+#: ../../../runtime/optwin.vim:687
+msgid "use a visual bell instead of beeping"
+msgstr "使用视觉铃声代替响铃"
-#~ msgid "E276: Error connecting to SNiFF+"
-#~ msgstr "E276: 连接到 SNiFF+ 失败"
+#: ../../../runtime/optwin.vim:689
+msgid "do not ring the bell for these reasons"
+msgstr "不要为这些原因响铃"
-#~ msgid "E278: SNiFF+ not connected"
-#~ msgstr "E278: 未连接到 SNiFF+"
+#: ../../../runtime/optwin.vim:692
+msgid "list of preferred languages for finding help"
+msgstr "查找帮助的首选语言列表"
-#~ msgid "E279: Not a SNiFF+ buffer"
-#~ msgstr "E279: 不是 SNiFF+ 的缓冲区"
+#: ../../../runtime/optwin.vim:697
+msgid "selecting text"
+msgstr "选择文本"
-#~ msgid "Sniff: Error during write. Disconnected"
-#~ msgstr "Sniff: 写入错误。结束连接"
+#: ../../../runtime/optwin.vim:698
+msgid "\"old\", \"inclusive\" or \"exclusive\"; how selecting text behaves"
+msgstr "\"old\", \"inclusive\" 或 \"exclusive\"; 如何选择文本"
-#~ msgid "invalid buffer number"
-#~ msgstr "无效的缓冲区号"
+#: ../../../runtime/optwin.vim:700
+msgid ""
+"\"mouse\", \"key\" and/or \"cmd\"; when to start Select mode\n"
+"instead of Visual mode"
+msgstr "\"mouse\", \"key\" 和/或 \"cmd\"; 何时启动选择模式而不是可视模式"
-#~ msgid "not implemented yet"
-#~ msgstr "尚未实现"
+#: ../../../runtime/optwin.vim:703
+msgid ""
+"\"unnamed\" to use the * register like unnamed register\n"
+"\"autoselect\" to always put selected text on the clipboard"
+msgstr ""
+"\"unnamed\": 像无名寄存器一样使用 * 寄存器\n"
+"\"autoselect\": 将选择的文本始终放在剪贴板上"
-#~ msgid "cannot set line(s)"
-#~ msgstr "无法设定行"
+#: ../../../runtime/optwin.vim:706
+msgid "\"startsel\" and/or \"stopsel\"; what special keys can do"
+msgstr "\"startsel\" 和/或 \"stopsel\"; 特殊键可以做"
-#~ msgid "mark not set"
-#~ msgstr "没有设定标记"
+#: ../../../runtime/optwin.vim:710
+msgid "editing text"
+msgstr "编辑文本"
-#~ msgid "row %d column %d"
-#~ msgstr "第 %d 行 第 %d 列"
+#: ../../../runtime/optwin.vim:711
+msgid "maximum number of changes that can be undone"
+msgstr "可以撤销的最大更改数"
-#~ msgid "cannot insert/append line"
-#~ msgstr "无法插入/追加行"
+#: ../../../runtime/optwin.vim:714
+msgid "automatically save and restore undo history"
+msgstr "自动保存和恢复撤销历史"
-#~ msgid "unknown flag: "
-#~ msgstr "未知的标志: "
+#: ../../../runtime/optwin.vim:716
+msgid "list of directories for undo files"
+msgstr "撤销文件的目录列表"
-#~ msgid "unknown vimOption"
-#~ msgstr "未知的 vim 选项"
+#: ../../../runtime/optwin.vim:718
+msgid "maximum number lines to save for undo on a buffer reload"
+msgstr "缓冲区重新加载时为撤销保存的最大行数"
-#~ msgid "keyboard interrupt"
-#~ msgstr "键盘中断"
+#: ../../../runtime/optwin.vim:720
+msgid "changes have been made and not written to a file"
+msgstr "已经做出了修改,但没有被写入文件"
-#~ msgid "vim error"
-#~ msgstr "vim 错误"
+#: ../../../runtime/optwin.vim:723
+msgid "buffer is not to be written"
+msgstr "缓冲区不会被写入"
-#~ msgid "cannot create buffer/window command: object is being deleted"
-#~ msgstr "无法创建缓冲区/窗口命令: 对象将被删除"
+#: ../../../runtime/optwin.vim:726
+msgid "changes to the text are possible"
+msgstr "可以对文本进行更改"
-#~ msgid ""
-#~ "cannot register callback command: buffer/window is already being deleted"
-#~ msgstr "无法注册回调命令: 缓冲区/窗口已被删除"
+#: ../../../runtime/optwin.vim:729
+msgid "line length above which to break a line"
+msgstr "超过行长度就断行"
-#~ msgid ""
-#~ "E280: TCL FATAL ERROR: reflist corrupt!? Please report this to vim-"
-#~ "dev@vim.org"
-#~ msgstr "E280: TCL 严重错误: reflist 损坏!?请报告给 vim-dev@vim.org"
+#: ../../../runtime/optwin.vim:732
+msgid "margin from the right in which to break a line"
+msgstr "断行开始的右边距"
-#~ msgid "cannot register callback command: buffer/window reference not found"
-#~ msgstr "无法注册回调命令: 找不到缓冲区/窗口引用"
+#: ../../../runtime/optwin.vim:735
+msgid "specifies what <BS>, CTRL-W, etc. can do in Insert mode"
+msgstr "指定 <BS>, CTRL-W 等在插入模式下可以做什么"
-#~ msgid ""
-#~ "E571: Sorry, this command is disabled: the Tcl library could not be "
-#~ "loaded."
-#~ msgstr "E571: 抱歉,此命令不可用,无法加载 Tcl 库"
+#: ../../../runtime/optwin.vim:737
+msgid "definition of what comment lines look like"
+msgstr "定义注释行长什么样"
-#~ msgid ""
-#~ "E281: TCL ERROR: exit code is not int!? Please report this to vim-dev@vim."
-#~ "org"
-#~ msgstr "E281: TCL 错误: 退出返回值不是整数!?请报告给 vim-dev@vim.org"
+#: ../../../runtime/optwin.vim:740
+msgid "list of flags that tell how automatic formatting works"
+msgstr "控制自动格式化如何工作的标志列表"
-#~ msgid "E572: exit code %d"
-#~ msgstr "E572: 退出返回值 %d"
+#: ../../../runtime/optwin.vim:743
+msgid "pattern to recognize a numbered list"
+msgstr "识别编号列表的模式"
-#~ msgid "cannot get line"
-#~ msgstr "无法获取行"
+#: ../../../runtime/optwin.vim:747
+msgid "expression used for \"gq\" to format lines"
+msgstr "用 \"gq\" 格式化行时使用的表达式"
-#~ msgid "Unable to register a command server name"
-#~ msgstr "无法注册命令服务器名"
+#: ../../../runtime/optwin.vim:752
+msgid "specifies how Insert mode completion works for CTRL-N and CTRL-P"
+msgstr "指定使用 CTRL-N 和 CTRL-P 进行插入模式补全的工作方式"
-#~ msgid "E248: Failed to send command to the destination program"
-#~ msgstr "E248: 无法发送命令到目的程序"
+#: ../../../runtime/optwin.vim:755
+msgid "whether to use a popup menu for Insert mode completion"
+msgstr "是否在插入模式补全时使用弹出菜单"
-#~ msgid "E573: Invalid server id used: %s"
-#~ msgstr "E573: 使用了无效的服务器 id: %s"
+#: ../../../runtime/optwin.vim:757
+msgid "maximum height of the popup menu"
+msgstr "弹出菜单的最大高度"
-#~ msgid "E251: VIM instance registry property is badly formed. Deleted!"
-#~ msgstr "E251: VIM 实例注册属性有误。已删除!"
+#: ../../../runtime/optwin.vim:759
+msgid "minimum width of the popup menu"
+msgstr "弹出菜单的最小宽度"
-#~ msgid "This Vim was not compiled with the diff feature."
-#~ msgstr "此 Vim 编译时没有加入 diff 功能"
+#: ../../../runtime/optwin.vim:761
+msgid "user defined function for Insert mode completion"
+msgstr "用于插入模式补全的用户定义函数"
-#~ msgid "Vim: Error: Failure to start gvim from NetBeans\n"
-#~ msgstr "Vim: 错误: 无法从 NetBeans 中启动 gvim\n"
+#: ../../../runtime/optwin.vim:764
+msgid "function for filetype-specific Insert mode completion"
+msgstr "用于特定文件类型的插入模式补全的函数"
-#~ msgid "-register\t\tRegister this gvim for OLE"
-#~ msgstr "-register\t\t注册此 gvim 到 OLE"
+#: ../../../runtime/optwin.vim:767
+msgid "list of dictionary files for keyword completion"
+msgstr "用于关键字补全的字典文件列表"
-#~ msgid "-unregister\t\tUnregister gvim for OLE"
-#~ msgstr "-unregister\t\t取消 OLE 中的 gvim 注册"
+#: ../../../runtime/optwin.vim:770
+msgid "list of thesaurus files for keyword completion"
+msgstr "用于关键字补全的同义词字典文件列表"
-#~ msgid "-g\t\t\tRun using GUI (like \"gvim\")"
-#~ msgstr "-g\t\t\t使用图形界面 (同 \"gvim\")"
+#: ../../../runtime/optwin.vim:773
+msgid "function used for thesaurus completion"
+msgstr "用于同义词字典补全的函数"
-#~ msgid "-f or --nofork\tForeground: Don't fork when starting GUI"
-#~ msgstr "-f 或 --nofork\t前台: 启动图形界面时不 fork"
+#: ../../../runtime/optwin.vim:777
+msgid "adjust case of a keyword completion match"
+msgstr "调整关键字补全匹配的大小写"
-#~ msgid "-V[N]\t\tVerbose level"
-#~ msgstr "-V[N]\t\tVerbose 等级"
+#: ../../../runtime/optwin.vim:781
+msgid "enable entering digraphs with c1 <BS> c2"
+msgstr "支持使用 c1 <BS> c2 输入二合字符"
-#~ msgid "-f\t\t\tDon't use newcli to open window"
-#~ msgstr "-f\t\t\t不使用 newcli 来打开窗口"
+#: ../../../runtime/optwin.vim:784
+msgid "the \"~\" command behaves like an operator"
+msgstr "\"~\"命令表现得像操作符"
-#~ msgid "-dev <device>\t\tUse <device> for I/O"
-#~ msgstr "-dev <device>\t\t使用 <device> 进行输入输出"
+#: ../../../runtime/optwin.vim:786
+msgid "function called for the \"g@\" operator"
+msgstr "\"g@\" 操作符调用的函数"
-#~ msgid "-U <gvimrc>\t\tUse <gvimrc> instead of any .gvimrc"
-#~ msgstr "-U <gvimrc>\t\t使用 <gvimrc> 替代任何 .gvimrc"
+#: ../../../runtime/optwin.vim:788
+msgid "when inserting a bracket, briefly jump to its match"
+msgstr "当插入括号时,短暂地跳转到匹配它的括号"
-#~ msgid "-x\t\t\tEdit encrypted files"
-#~ msgstr "-x\t\t\t编辑加密的文件"
+#: ../../../runtime/optwin.vim:790
+msgid "tenth of a second to show a match for 'showmatch'"
+msgstr "显示 'showmatch' 的匹配的时长(以十分之一秒计)"
-#~ msgid "-display <display>\tConnect vim to this particular X-server"
-#~ msgstr "-display <display>\t将 vim 与指定的 X-server 连接"
+#: ../../../runtime/optwin.vim:792
+msgid "list of pairs that match for the \"%\" command"
+msgstr "\"%\" 命令匹配的对列表"
-#~ msgid "-X\t\t\tDo not connect to X server"
-#~ msgstr "-X\t\t\t不连接到 X Server"
+#: ../../../runtime/optwin.vim:795
+msgid "use two spaces after '.' when joining a line"
+msgstr "连接行时,在 '.' 后面添加两个空格"
-#~ msgid "--remote <files>\tEdit <files> in a Vim server if possible"
-#~ msgstr "--remote <files>\t如有可能,在 Vim 服务器上编辑文件 <files>"
+#: ../../../runtime/optwin.vim:797
+msgid ""
+"\"alpha\", \"octal\", \"hex\", \"bin\" and/or \"unsigned\"; number formats\n"
+"recognized for CTRL-A and CTRL-X commands"
+msgstr ""
+"\"alpha\", \"octal\", \"hex\", \"bin\" 和/或 \"unsigned\"\n"
+"CTRL-A 和 CTRL-X 命令识别的数字的格式"
-#~ msgid "--remote-silent <files> Same, don't complain if there is no server"
-#~ msgstr "--remote-silent <files> 同上,找不到服务器时不抱怨"
+#: ../../../runtime/optwin.vim:802
+msgid "tabs and indenting"
+msgstr "Tab 和缩进"
-#~ msgid ""
-#~ "--remote-wait <files> As --remote but wait for files to have been edited"
-#~ msgstr "--remote-wait <files> 同 --remote 但会等待文件完成编辑"
+#: ../../../runtime/optwin.vim:803
+msgid "number of spaces a <Tab> in the text stands for"
+msgstr "<Tab> 在文本中代表的空格数"
-#~ msgid ""
-#~ "--remote-wait-silent <files> Same, don't complain if there is no server"
-#~ msgstr "--remote-wait-silent <files> 同上,找不到服务器时不抱怨"
+#: ../../../runtime/optwin.vim:806
+msgid "number of spaces used for each step of (auto)indent"
+msgstr "每步(自动)缩进所使用的空格数"
-#~ msgid "--remote-tab <files> As --remote but open tab page for each file"
-#~ msgstr "--remote-tab <files> 同 --remote 但对每个文件打开一个标签页"
+#: ../../../runtime/optwin.vim:810
+msgid "list of number of spaces a tab counts for"
+msgstr "tab 代表的空格数的列表"
-#~ msgid "--remote-send <keys>\tSend <keys> to a Vim server and exit"
-#~ msgstr "--remote-send <keys>\t送出 <keys> 到 Vim 服务器并退出"
+#: ../../../runtime/optwin.vim:813
+msgid "list of number of spaces a soft tabsstop counts for"
+msgstr "软制表符代表的空格数的列表"
-#~ msgid ""
-#~ "--remote-expr <expr>\tEvaluate <expr> in a Vim server and print result"
-#~ msgstr "--remote-expr <expr>\t在 Vim 服务器上求 <expr> 的值并打印结果"
+#: ../../../runtime/optwin.vim:817
+msgid "a <Tab> in an indent inserts 'shiftwidth' spaces"
+msgstr "用 <Tab> 键缩进时插入 'shiftwidth' 个空格"
-#~ msgid "--serverlist\t\tList available Vim server names and exit"
-#~ msgstr "--serverlist\t\t列出可用的 Vim 服务器名称并退出"
+#: ../../../runtime/optwin.vim:819
+msgid "if non-zero, number of spaces to insert for a <Tab>"
+msgstr "如果非零,为 <Tab> 插入的空格数"
-#~ msgid "--servername <name>\tSend to/become the Vim server <name>"
-#~ msgstr "--servername <name>\t发送到或成为 Vim 服务器 <name>"
+#: ../../../runtime/optwin.vim:822
+msgid "round to 'shiftwidth' for \"<<\" and \">>\""
+msgstr "用 \"<<\" 和 \">>\" 缩进时,插入 'shiftwidth' 整数倍个空格"
-#~ msgid ""
-#~ "\n"
-#~ "Arguments recognised by gvim (Motif version):\n"
-#~ msgstr ""
-#~ "\n"
-#~ "gvim (Motif 版本) 可识别的参数:\n"
+#: ../../../runtime/optwin.vim:824
+msgid "expand <Tab> to spaces in Insert mode"
+msgstr "在插入模式下将 <Tab> 展开为空格"
-#~ msgid ""
-#~ "\n"
-#~ "Arguments recognised by gvim (neXtaw version):\n"
-#~ msgstr ""
-#~ "\n"
-#~ "gvim (neXtaw 版本) 可识别的参数:\n"
+#: ../../../runtime/optwin.vim:827
+msgid "automatically set the indent of a new line"
+msgstr "自动设置新行缩进"
-#~ msgid ""
-#~ "\n"
-#~ "Arguments recognised by gvim (Athena version):\n"
-#~ msgstr ""
-#~ "\n"
-#~ "gvim (Athena 版本) 可识别的参数:\n"
+#: ../../../runtime/optwin.vim:831
+msgid "do clever autoindenting"
+msgstr "智能自动缩进"
-#~ msgid "-display <display>\tRun vim on <display>"
-#~ msgstr "-display <display>\t在 <display> 上运行 vim"
+#: ../../../runtime/optwin.vim:836
+msgid "enable specific indenting for C code"
+msgstr "为 C 代码启用特定的缩进"
-#~ msgid "-iconic\t\tStart vim iconified"
-#~ msgstr "-iconic\t\t启动后最小化"
+#: ../../../runtime/optwin.vim:839
+msgid "options for C-indenting"
+msgstr "C 风格缩进的选项"
-#~ msgid "-name <name>\t\tUse resource as if vim was <name>"
-#~ msgstr "-name <name>\t\t读取 Resource 时把 vim 视为 <name>"
+#: ../../../runtime/optwin.vim:842
+msgid "keys that trigger C-indenting in Insert mode"
+msgstr "在插入模式下触发 C 风格缩进的键"
-#~ msgid "\t\t\t (Unimplemented)\n"
-#~ msgstr "\t\t\t (尚未实现)\n"
+#: ../../../runtime/optwin.vim:845
+msgid "list of words that cause more C-indent"
+msgstr "导致更多 C 风格缩进的单词列表"
-#~ msgid "-background <color>\tUse <color> for the background (also: -bg)"
-#~ msgstr "-background <color>\t使用 <color> 作为背景色 (也可用 -bg)"
+#: ../../../runtime/optwin.vim:848
+msgid "list of scope declaration names used by cino-g"
+msgstr "cino-g 使用的作用域声明名称列表"
-#~ msgid "-foreground <color>\tUse <color> for normal text (also: -fg)"
-#~ msgstr "-foreground <color>\t使用 <color> 作为一般文字颜色 (也可用 -fg)"
+#: ../../../runtime/optwin.vim:851
+msgid "expression used to obtain the indent of a line"
+msgstr "用于获取一行缩进的表达式"
-#~ msgid "-font <font>\t\tUse <font> for normal text (also: -fn)"
-#~ msgstr "-font <font>\t使用 <font> 作为一般字体 (也可用 -fn)"
+#: ../../../runtime/optwin.vim:854
+msgid "keys that trigger indenting with 'indentexpr' in Insert mode"
+msgstr "在插入模式下使用 'indentexpr' 触发缩进的键"
-#~ msgid "-boldfont <font>\tUse <font> for bold text"
-#~ msgstr "-boldfont <font>\t使用 <font> 作为粗体字体"
+#: ../../../runtime/optwin.vim:858
+msgid "copy whitespace for indenting from previous line"
+msgstr "为缩进从上一行复制空白字符"
-#~ msgid "-italicfont <font>\tUse <font> for italic text"
-#~ msgstr "-italicfont <font>\t使用 <font> 作为斜体字体"
+#: ../../../runtime/optwin.vim:861
+msgid "preserve kind of whitespace when changing indent"
+msgstr "在更改缩进时保留空白类型"
-#~ msgid "-geometry <geom>\tUse <geom> for initial geometry (also: -geom)"
-#~ msgstr "-geometry <geom>\t使用 <geom> 作为初始位置 (也可用 -geom)"
+#: ../../../runtime/optwin.vim:865
+msgid "enable lisp mode"
+msgstr "启用 lisp 模式"
-#~ msgid "-borderwidth <width>\tUse a border width of <width> (also: -bw)"
-#~ msgstr "-borderwidth <width>\t设定边框宽度为 <width> (也可用 -bw)"
+#: ../../../runtime/optwin.vim:868
+msgid "words that change how lisp indenting works"
+msgstr "改变 lisp 如何缩进的单词"
-#~ msgid ""
-#~ "-scrollbarwidth <width> Use a scrollbar width of <width> (also: -sw)"
-#~ msgstr "-scrollbarwidth <width> 设定滚动条宽度为 <width> (也可用 -sw)"
+#: ../../../runtime/optwin.vim:870
+msgid "options for Lisp indenting"
+msgstr "Lisp 缩进选项"
-#~ msgid "-menuheight <height>\tUse a menu bar height of <height> (also: -mh)"
-#~ msgstr "-menuheight <height>\t设定菜单栏高度为 <height> (也可用 -mh)"
+#: ../../../runtime/optwin.vim:876
+msgid "folding"
+msgstr "折叠"
-#~ msgid "-reverse\t\tUse reverse video (also: -rv)"
-#~ msgstr "-reverse\t\t使用反显 (也可用 -rv)"
+#: ../../../runtime/optwin.vim:877
+msgid "unset to display all folds open"
+msgstr "取消设置以打开所有折叠"
-#~ msgid "+reverse\t\tDon't use reverse video (also: +rv)"
-#~ msgstr "+reverse\t\t不使用反显 (也可用 +rv)"
+#: ../../../runtime/optwin.vim:880
+msgid "folds with a level higher than this number will be closed"
+msgstr "等级比这个数字高的折叠将被关闭"
-#~ msgid "-xrm <resource>\tSet the specified resource"
-#~ msgstr "-xrm <resource>\t设定指定的资源"
+#: ../../../runtime/optwin.vim:883
+msgid "value for 'foldlevel' when starting to edit a file"
+msgstr "开始编辑文件时,'foldlevel' 的值"
-#~ msgid ""
-#~ "\n"
-#~ "Arguments recognised by gvim (RISC OS version):\n"
-#~ msgstr ""
-#~ "\n"
-#~ "gvim (RISC OS 版本) 可识别的参数:\n"
+#: ../../../runtime/optwin.vim:885
+msgid "width of the column used to indicate folds"
+msgstr "用来指示折叠的列的宽度"
-#~ msgid "--columns <number>\tInitial width of window in columns"
-#~ msgstr "--columns <number>\t窗口初始宽度"
+#: ../../../runtime/optwin.vim:888
+msgid "expression used to display the text of a closed fold"
+msgstr "用于显示已关闭折叠的表达式"
-#~ msgid "--rows <number>\tInitial height of window in rows"
-#~ msgstr "--rows <number>\t窗口初始高度"
+#: ../../../runtime/optwin.vim:891
+msgid "set to \"all\" to close a fold when the cursor leaves it"
+msgstr "设置为 \"all\" 来在光标离开时关闭折叠"
-#~ msgid ""
-#~ "\n"
-#~ "Arguments recognised by gvim (GTK+ version):\n"
-#~ msgstr ""
-#~ "\n"
-#~ "gvim (GTK+ 版本) 可识别的参数:\n"
+#: ../../../runtime/optwin.vim:893
+msgid "specifies for which commands a fold will be opened"
+msgstr "指定会打开折叠的命令"
-#~ msgid "-display <display>\tRun vim on <display> (also: --display)"
-#~ msgstr "-display <display>\t在 <display> 上运行 vim (也可用 --display)"
+#: ../../../runtime/optwin.vim:895
+msgid "minimum number of screen lines for a fold to be closed"
+msgstr "可关闭折叠所需的最小屏幕行数"
-#~ msgid "--role <role>\tSet a unique role to identify the main window"
-#~ msgstr "--role <role>\t设置用于区分主窗口的窗口角色名"
+#: ../../../runtime/optwin.vim:898
+msgid "template for comments; used to put the marker in"
+msgstr "注释的模板,用于放置折叠标记"
-#~ msgid "--socketid <xid>\tOpen Vim inside another GTK widget"
-#~ msgstr "--socketid <xid>\t在另一个 GTK 部件中打开 Vim"
+#: ../../../runtime/optwin.vim:900
+msgid ""
+"folding type: \"manual\", \"indent\", \"expr\", \"marker\",\n"
+"\"syntax\" or \"diff\""
+msgstr ""
+"折叠类型:\"manual\", \"indent\", \"expr\", \"marker\", \n"
+"\"syntax\" 或者 \"diff\""
-#~ msgid "-P <parent title>\tOpen Vim inside parent application"
-#~ msgstr "-P <parent title>\t在父应用程序中打开 Vim"
+#: ../../../runtime/optwin.vim:903
+msgid "expression used when 'foldmethod' is \"expr\""
+msgstr "当 'foldmethod' 为 \"expr\" 时使用的表达式"
-#~ msgid "No display"
-#~ msgstr "没有 display"
+#: ../../../runtime/optwin.vim:906
+msgid "used to ignore lines when 'foldmethod' is \"indent\""
+msgstr "当 'foldmethod' 为 \"indent\" 时用于忽略行"
-#~ msgid ": Send failed.\n"
-#~ msgstr ": 发送失败。\n"
+#: ../../../runtime/optwin.vim:909
+msgid "markers used when 'foldmethod' is \"marker\""
+msgstr "当 'foldmethod' 为 \"marker\" 时所使用的标记"
-#~ msgid ": Send failed. Trying to execute locally\n"
-#~ msgstr ": 发送失败。尝试本地执行\n"
+#: ../../../runtime/optwin.vim:912
+msgid "maximum fold depth for when 'foldmethod' is \"indent\" or \"syntax\""
+msgstr "当 'foldmethod' 为 \"indent\" 或 \"syntax\" 时的最大折叠深度"
-#~ msgid "%d of %d edited"
-#~ msgstr "%d 中 %d 已编辑"
+#: ../../../runtime/optwin.vim:919
+msgid "diff mode"
+msgstr "差异模式"
-#~ msgid "No display: Send expression failed.\n"
-#~ msgstr "没有 display: 发送表达式失败。\n"
+#: ../../../runtime/optwin.vim:920
+msgid "use diff mode for the current window"
+msgstr "对当前窗口使用差异模式"
-#~ msgid ": Send expression failed.\n"
-#~ msgstr ": 发送表达式失败。\n"
+#: ../../../runtime/optwin.vim:923
+msgid "options for using diff mode"
+msgstr "使用差异模式的选项"
-#~ msgid "E543: Not a valid codepage"
-#~ msgstr "E543: 无效的代码页"
+#: ../../../runtime/optwin.vim:925
+msgid "expression used to obtain a diff file"
+msgstr "用于获取差异文件的表达式"
-#~ msgid "E285: Failed to create input context"
-#~ msgstr "E285: 无法创建输入上下文"
+#: ../../../runtime/optwin.vim:927
+msgid "expression used to patch a file"
+msgstr "用于给文件打补丁的表达式"
-#~ msgid "E286: Failed to open input method"
-#~ msgstr "E286: 无法打开输入法"
+#: ../../../runtime/optwin.vim:932
+msgid "mapping"
+msgstr "映射"
-#~ msgid "E287: Warning: Could not set destroy callback to IM"
-#~ msgstr "E287: 警告: 无法设定输入法的释放回调函数"
+#: ../../../runtime/optwin.vim:933
+msgid "maximum depth of mapping"
+msgstr "最大映射深度"
-#~ msgid "E288: input method doesn't support any style"
-#~ msgstr "E288: 输入法不支持任何风格"
+#: ../../../runtime/optwin.vim:935
+msgid "allow timing out halfway into a mapping"
+msgstr "允许在映射中途超时"
-#~ msgid "E289: input method doesn't support my preedit type"
-#~ msgstr "E289: 输入法不支持我的预编辑类型"
+#: ../../../runtime/optwin.vim:937
+msgid "allow timing out halfway into a key code"
+msgstr "允许在键码中途超时"
-#~ msgid "E290: over-the-spot style requires fontset"
-#~ msgstr "E290: over-the-spot 风格需要 Fontset"
+#: ../../../runtime/optwin.vim:939
+msgid "time in msec for 'timeout'"
+msgstr "'timeout' 的时间(以毫秒计)"
-#~ msgid "E291: Your GTK+ is older than 1.2.3. Status area disabled"
-#~ msgstr "E291: 你的 GTK+ 比 1.2.3 旧。状态区不可用。"
+#: ../../../runtime/optwin.vim:941
+msgid "time in msec for 'ttimeout'"
+msgstr "'ttimeout' 的时间(以毫秒计)"
-#~ msgid "E292: Input Method Server is not running"
-#~ msgstr "E292: 输入法服务器未运行"
+#: ../../../runtime/optwin.vim:945
+msgid "reading and writing files"
+msgstr "读写文件"
-#~ msgid ""
-#~ "\n"
-#~ " [not usable with this version of Vim]"
-#~ msgstr ""
-#~ "\n"
-#~ " [不能在该版本的 Vim 上使用]"
+#: ../../../runtime/optwin.vim:946
+msgid "enable using settings from modelines when reading a file"
+msgstr "读取文件时是否使用 modeline 里的设置"
-#~ msgid "Tear off this menu"
-#~ msgstr "撕下此菜单"
+#: ../../../runtime/optwin.vim:949
+msgid "allow setting expression options from a modeline"
+msgstr "允许从 modeline 中设置表达式选项"
-#~ msgid "Select Directory dialog"
-#~ msgstr "选择目录对话框"
+#: ../../../runtime/optwin.vim:951
+msgid "number of lines to check for modelines"
+msgstr "为 modeline 而检查的行数"
-#~ msgid "Save File dialog"
-#~ msgstr "保存文件对话框"
+#: ../../../runtime/optwin.vim:953
+msgid "binary file editing"
+msgstr "二进制文件编辑"
-#~ msgid "Open File dialog"
-#~ msgstr "打开文件对话框"
+#: ../../../runtime/optwin.vim:956
+msgid "last line in the file has an end-of-line"
+msgstr "文件的最后一行有换行符"
-#~ msgid "E338: Sorry, no file browser in console mode"
-#~ msgstr "E338: 抱歉,控制台模式下没有文件浏览器"
+#: ../../../runtime/optwin.vim:959
+msgid "last line in the file followed by CTRL-Z"
+msgstr ""
-#~ msgid "Vim: preserving files...\n"
-#~ msgstr "Vim: 正在保留文件……\n"
+#: ../../../runtime/optwin.vim:962
+msgid "fixes missing end-of-line at end of text file"
+msgstr "修复文本文件末尾缺少换行符的问题"
-#~ msgid "Vim: Finished.\n"
-#~ msgstr "Vim: 结束。\n"
+#: ../../../runtime/optwin.vim:965
+msgid "prepend a Byte Order Mark to the file"
+msgstr "在文件前加上字节顺序标记"
-#~ msgid "ERROR: "
-#~ msgstr "错误: "
+#: ../../../runtime/optwin.vim:968
+msgid "end-of-line format: \"dos\", \"unix\" or \"mac\""
+msgstr "换行符格式: \"dos\", \"unix\" 或 \"mac\""
-#~ msgid ""
-#~ "\n"
-#~ "[bytes] total alloc-freed %<PRIu64>-%<PRIu64>, in use %<PRIu64>, peak use "
-#~ "%<PRIu64>\n"
-#~ msgstr ""
-#~ "\n"
-#~ "[字节] 总共 alloc-free %<PRIu64>-%<PRIu64>,使用中 %<PRIu64>,高峰使用 "
-#~ "%<PRIu64>\n"
+#: ../../../runtime/optwin.vim:971
+msgid "list of file formats to look for when editing a file"
+msgstr "编辑文件时要检查的文件格式列表"
-#~ msgid ""
-#~ "[calls] total re/malloc()'s %<PRIu64>, total free()'s %<PRIu64>\n"
-#~ "\n"
-#~ msgstr ""
-#~ "[调用] 总共 re/malloc(): %<PRIu64>,总共 free()': %<PRIu64>\n"
-#~ "\n"
+#: ../../../runtime/optwin.vim:973
+msgid "writing files is allowed"
+msgstr "允许写入文件"
-#~ msgid "E340: Line is becoming too long"
-#~ msgstr "E340: 此行过长"
+#: ../../../runtime/optwin.vim:975
+msgid "write a backup file before overwriting a file"
+msgstr "覆盖文件前先写入备份文件"
-#~ msgid "E341: Internal error: lalloc(%<PRId64>, )"
-#~ msgstr "E341: 内部错误: lalloc(%<PRId64>, )"
+#: ../../../runtime/optwin.vim:977
+msgid "keep a backup after overwriting a file"
+msgstr "覆盖文件后保留备份"
-#~ msgid "E547: Illegal mouseshape"
-#~ msgstr "E547: 无效的鼠标形状"
+#: ../../../runtime/optwin.vim:979
+msgid "patterns that specify for which files a backup is not made"
+msgstr "不备份的文件的模式"
-#~ msgid "Enter encryption key: "
-#~ msgstr "输入密码: "
+#: ../../../runtime/optwin.vim:981
+msgid "whether to make the backup as a copy or rename the existing file"
+msgstr "通过复制还是重命名已有文件进行备份"
-#~ msgid "Enter same key again: "
-#~ msgstr "请再输入一次: "
+#: ../../../runtime/optwin.vim:984
+msgid "list of directories to put backup files in"
+msgstr "存放备份文件的目录列表"
-#~ msgid "Keys don't match!"
-#~ msgstr "两次密码不匹配!"
+#: ../../../runtime/optwin.vim:986
+msgid "file name extension for the backup file"
+msgstr "备份文件使用的扩展名"
-#~ msgid "Cannot connect to Netbeans #2"
-#~ msgstr "无法连接到 Netbeans #2"
+#: ../../../runtime/optwin.vim:988
+msgid "automatically write a file when leaving a modified buffer"
+msgstr "在离开修改过的缓冲区时自动写入文件"
-#~ msgid "Cannot connect to Netbeans"
-#~ msgstr "无法连接到 Netbeans"
+#: ../../../runtime/optwin.vim:990
+msgid "as 'autowrite', but works with more commands"
+msgstr "类似于 'autowrite',但适用于更多命令"
-#~ msgid "E668: Wrong access mode for NetBeans connection info file: \"%s\""
-#~ msgstr "E668: NetBeans 连接信息文件中错误的访问模式: \"%s\""
+#: ../../../runtime/optwin.vim:992
+msgid "always write without asking for confirmation"
+msgstr "写文件时总是不需要确认"
-#~ msgid "read from Netbeans socket"
-#~ msgstr "从 Netbeans 套接字读取"
+#: ../../../runtime/optwin.vim:994
+msgid "automatically read a file when it was modified outside of Vim"
+msgstr "在 Vim 之外修改了文件时,自动读取文件"
-#~ msgid "E658: NetBeans connection lost for buffer %<PRId64>"
-#~ msgstr "E658: 缓冲区 %<PRId64> 丢失 NetBeans 连接"
+#: ../../../runtime/optwin.vim:997
+msgid "keep oldest version of a file; specifies file name extension"
+msgstr "保存最旧版本的文件;指定文件扩展名"
-#~ msgid "E505: "
-#~ msgstr "E505: "
+#: ../../../runtime/optwin.vim:999
+msgid "forcibly sync the file to disk after writing it"
+msgstr "文件写入后强制同步到磁盘"
-#~ msgid "E775: Eval feature not available"
-#~ msgstr "E775: 求值功能不可用"
+#: ../../../runtime/optwin.vim:1003
+msgid "the swap file"
+msgstr "交换文件"
-#~ msgid "freeing %<PRId64> lines"
-#~ msgstr "释放了 %<PRId64> 行"
+#: ../../../runtime/optwin.vim:1004
+msgid "list of directories for the swap file"
+msgstr "交换文件的目录列表"
-#~ msgid "E530: Cannot change term in GUI"
-#~ msgstr "E530: 在图形界面中不能改变终端"
+#: ../../../runtime/optwin.vim:1006
+msgid "use a swap file for this buffer"
+msgstr "对这个缓冲区使用交换文件"
-#~ msgid "E531: Use \":gui\" to start the GUI"
-#~ msgstr "E531: 请用 \":gui\" 启动图形界面"
+#: ../../../runtime/optwin.vim:1009
+msgid "number of characters typed to cause a swap file update"
+msgstr "导致交换文件更新的字符数"
-#~ msgid "E617: Cannot be changed in the GTK+ 2 GUI"
-#~ msgstr "E617: 在 GTK+ 2 图形界面中不能更改"
+#: ../../../runtime/optwin.vim:1011
+msgid "time in msec after which the swap file will be updated"
+msgstr "更新交换文件前所需的毫秒数"
-#~ msgid "E596: Invalid font(s)"
-#~ msgstr "E596: 无效的字体"
+# do not translate to avoid writing Chinese in files
+#: ../../../runtime/optwin.vim:1015
+msgid "command line editing"
+msgstr ""
-#~ msgid "E597: can't select fontset"
-#~ msgstr "E597: 无法选择 Fontset"
+#: ../../../runtime/optwin.vim:1016
+msgid "how many command lines are remembered"
+msgstr "记住的命令行数"
-#~ msgid "E598: Invalid fontset"
-#~ msgstr "E598: 无效的 Fontset"
+#: ../../../runtime/optwin.vim:1018
+msgid "key that triggers command-line expansion"
+msgstr "触发命令行扩展的键"
-#~ msgid "E533: can't select wide font"
-#~ msgstr "E533: 无法选择宽字体"
+#: ../../../runtime/optwin.vim:1020
+msgid "like 'wildchar' but can also be used in a mapping"
+msgstr "类似 'wildchar',但也可以在映射中使用"
-#~ msgid "E534: Invalid wide font"
-#~ msgstr "E534: 无效的宽字体"
+#: ../../../runtime/optwin.vim:1022
+msgid "specifies how command line completion works"
+msgstr "指定命令行如何补全"
-#~ msgid "E538: No mouse support"
-#~ msgstr "E538: 不支持鼠标"
+#: ../../../runtime/optwin.vim:1025
+msgid "empty or \"tagfile\" to list file name of matching tags"
+msgstr "空或 \"tagfile\" 来列出匹配标签的文件名"
-#~ msgid "cannot open "
-#~ msgstr "不能打开"
+#: ../../../runtime/optwin.vim:1028
+msgid "list of file name extensions that have a lower priority"
+msgstr "优先级低的文件扩展名列表"
-#~ msgid "VIM: Can't open window!\n"
-#~ msgstr "VIM: 不能打开窗口!\n"
+#: ../../../runtime/optwin.vim:1031
+msgid "list of file name extensions added when searching for a file"
+msgstr "搜索文件时添加的文件扩展名列表"
-#~ msgid "Need Amigados version 2.04 or later\n"
-#~ msgstr "需要 Amigados 版本 2.04 以上\n"
+#: ../../../runtime/optwin.vim:1036
+msgid "list of patterns to ignore files for file name completion"
+msgstr "文件名补全所忽略文件的模式列表"
-#~ msgid "Need %s version %<PRId64>\n"
-#~ msgstr "需要 %s 版本 %<PRId64>\n"
+#: ../../../runtime/optwin.vim:1039
+msgid "ignore case when using file names"
+msgstr "使用文件名时忽略大小写"
-#~ msgid "Cannot open NIL:\n"
-#~ msgstr "不能打开 NIL:\n"
+#: ../../../runtime/optwin.vim:1041
+msgid "ignore case when completing file names"
+msgstr "补全文件名时忽略大小写"
-#~ msgid "Cannot create "
-#~ msgstr "不能创建 "
+#: ../../../runtime/optwin.vim:1044
+msgid "command-line completion shows a list of matches"
+msgstr "命令行补全时显示匹配列表"
-#~ msgid "Vim exiting with %d\n"
-#~ msgstr "Vim 返回值: %d\n"
+#: ../../../runtime/optwin.vim:1047
+msgid "key used to open the command-line window"
+msgstr "用于打开命令行窗口的键"
-#~ msgid "cannot change console mode ?!\n"
-#~ msgstr "不能切换主控台(console)模式 !?\n"
+#: ../../../runtime/optwin.vim:1049
+msgid "height of the command-line window"
+msgstr "命令行窗口的高度"
-#~ msgid "mch_get_shellsize: not a console??\n"
-#~ msgstr "mch_get_shellsize: 不是主控台(console)??\n"
+#: ../../../runtime/optwin.vim:1053
+msgid "executing external commands"
+msgstr "执行外部命令"
-#~ msgid "E360: Cannot execute shell with -f option"
-#~ msgstr "E360: 不能用 -f 选项执行 shell"
+#: ../../../runtime/optwin.vim:1054
+msgid "name of the shell program used for external commands"
+msgstr "用于外部命令的 shell 程序的名称"
-#~ msgid "Cannot execute "
-#~ msgstr "不能执行 "
+#: ../../../runtime/optwin.vim:1056
+msgid "character(s) to enclose a shell command in"
+msgstr "用于包围 shell 命令的字符"
-#~ msgid "shell "
-#~ msgstr "shell "
+#: ../../../runtime/optwin.vim:1058
+msgid "like 'shellquote' but include the redirection"
+msgstr "类似 'shellquote',但包含重定向"
-#~ msgid " returned\n"
-#~ msgstr " 已返回\n"
+#: ../../../runtime/optwin.vim:1060
+msgid "characters to escape when 'shellxquote' is ("
+msgstr "'shellxquote' 为 ( 时需要转义的字符"
-#~ msgid "ANCHOR_BUF_SIZE too small."
-#~ msgstr "ANCHOR_BUF_SIZE 太小"
+#: ../../../runtime/optwin.vim:1062
+msgid "argument for 'shell' to execute a command"
+msgstr "'shell' 执行命令的参数"
-#~ msgid "I/O ERROR"
-#~ msgstr "I/O 错误"
+#: ../../../runtime/optwin.vim:1064
+msgid "used to redirect command output to a file"
+msgstr "用于将命令输出重定向到文件"
-#~ msgid "Message"
-#~ msgstr "消息"
+#: ../../../runtime/optwin.vim:1066
+msgid "use a temp file for shell commands instead of using a pipe"
+msgstr "对 shell 命令使用临时文件而不是管道"
-#~ msgid "'columns' is not 80, cannot execute external commands"
-#~ msgstr "'columns' 不是 80, 不能执行外部命令"
+#: ../../../runtime/optwin.vim:1068
+msgid "program used for \"=\" command"
+msgstr "用于 \"=\" 命令的程序"
-#~ msgid "E237: Printer selection failed"
-#~ msgstr "E237: 选择打印机失败"
+#: ../../../runtime/optwin.vim:1071
+msgid "program used to format lines with \"gq\" command"
+msgstr "用 \"gq\" 命令格式化代码时使用的程序"
-#~ msgid "to %s on %s"
-#~ msgstr "从 %s 到 %s"
+#: ../../../runtime/optwin.vim:1073
+msgid "program used for the \"K\" command"
+msgstr "用于 \"K\" 命令的程序"
-#~ msgid "E613: Unknown printer font: %s"
-#~ msgstr "E613: 未知的打印机字体: %s"
+#: ../../../runtime/optwin.vim:1075
+msgid "warn when using a shell command and a buffer has changes"
+msgstr "当使用 shell 命令并且缓冲区有修改时发出警告"
-#~ msgid "E238: Print error: %s"
-#~ msgstr "E238: 打印错误: %s"
+#: ../../../runtime/optwin.vim:1080
+msgid "running make and jumping to errors (quickfix)"
+msgstr "运行 make 并跳到错误(快速修复)"
-#~ msgid "Printing '%s'"
-#~ msgstr "打印 '%s'"
+#: ../../../runtime/optwin.vim:1081
+msgid "name of the file that contains error messages"
+msgstr "包含错误消息的文件的名称"
-#~ msgid "E244: Illegal charset name \"%s\" in font name \"%s\""
-#~ msgstr "E244: 字符集 \"%s\" 不能对应字体\"%s\""
+#: ../../../runtime/optwin.vim:1083
+msgid "list of formats for error messages"
+msgstr "错误消息的格式列表"
-#~ msgid "E245: Illegal char '%c' in font name \"%s\""
-#~ msgstr "E245: 不正确的字符 '%c' 出现在字体名称 \"%s\" 内"
+#: ../../../runtime/optwin.vim:1086
+msgid "program used for the \":make\" command"
+msgstr "用于 \":make\" 命令的程序"
-#~ msgid "Vim: Double signal, exiting\n"
-#~ msgstr "Vim: 双重信号,退出中\n"
+#: ../../../runtime/optwin.vim:1089
+msgid "string used to put the output of \":make\" in the error file"
+msgstr "用于将 \":make\" 的输出放在错误文件中的字符串"
-#~ msgid "Vim: Caught deadly signal %s\n"
-#~ msgstr "Vim: 拦截到致命信号(deadly signal) %s\n"
+#: ../../../runtime/optwin.vim:1091
+msgid "name of the errorfile for the 'makeprg' command"
+msgstr "'makeprg' 命令的错误文件的名称"
-#~ msgid "Vim: Caught deadly signal\n"
-#~ msgstr "Vim: 拦截到致命信号(deadly signal)\n"
+#: ../../../runtime/optwin.vim:1093
+msgid "program used for the \":grep\" command"
+msgstr "用于 \":grep\" 命令的程序"
-#~ msgid "Opening the X display took %<PRId64> msec"
-#~ msgstr "打开 X display 用时 %<PRId64> 秒"
+#: ../../../runtime/optwin.vim:1096
+msgid "list of formats for output of 'grepprg'"
+msgstr "'grepprg' 的输出格式列表"
+
+#: ../../../runtime/optwin.vim:1098
+msgid "encoding of the \":make\" and \":grep\" output"
+msgstr "\":make\" 和 \":grep\" 输出的编码"
+
+#: ../../../runtime/optwin.vim:1105
+msgid "system specific"
+msgstr "系统特定"
+
+#: ../../../runtime/optwin.vim:1106
+msgid "use forward slashes in file names; for Unix-like shells"
+msgstr "在文件名中使用正斜杠;用于类 Unix shell"
+
+#: ../../../runtime/optwin.vim:1108
+msgid "specifies slash/backslash used for completion"
+msgstr "指定补全时使用的斜杠/反斜杠"
+
+#: ../../../runtime/optwin.vim:1113
+msgid "language specific"
+msgstr "语言特定"
+
+#: ../../../runtime/optwin.vim:1114
+msgid "specifies the characters in a file name"
+msgstr "指定文件名中的字符"
+
+#: ../../../runtime/optwin.vim:1116
+msgid "specifies the characters in an identifier"
+msgstr "指定标识符中的字符"
+
+#: ../../../runtime/optwin.vim:1118
+msgid "specifies the characters in a keyword"
+msgstr "指定关键字中的字符"
+
+#: ../../../runtime/optwin.vim:1121
+msgid "specifies printable characters"
+msgstr "指定可打印字符"
+
+#: ../../../runtime/optwin.vim:1124
+msgid "specifies escape characters in a string"
+msgstr "指定字符串中的转义字符"
+
+#: ../../../runtime/optwin.vim:1129
+msgid "display the buffer right-to-left"
+msgstr "从右到左显示缓冲区"
+
+#: ../../../runtime/optwin.vim:1132
+msgid "when to edit the command-line right-to-left"
+msgstr "何时从右到左编辑命令行"
+
+#: ../../../runtime/optwin.vim:1135
+msgid "insert characters backwards"
+msgstr "倒序插入字符"
+
+#: ../../../runtime/optwin.vim:1137
+msgid "allow CTRL-_ in Insert and Command-line mode to toggle 'revins'"
+msgstr "在插入和命令行模式下允许 CTRL-_ 切换 'revins'"
+
+#: ../../../runtime/optwin.vim:1139
+msgid "the ASCII code for the first letter of the Hebrew alphabet"
+msgstr "希伯来字母表第一个字母的 ASCII 码"
+
+#: ../../../runtime/optwin.vim:1141
+msgid "use Hebrew keyboard mapping"
+msgstr "使用希伯来语键盘映射"
+
+#: ../../../runtime/optwin.vim:1143
+msgid "use phonetic Hebrew keyboard mapping"
+msgstr "使用希伯来语的语音键盘映射"
+
+#: ../../../runtime/optwin.vim:1147
+msgid "prepare for editing Arabic text"
+msgstr "准备编辑阿拉伯语文本"
+
+#: ../../../runtime/optwin.vim:1150
+msgid "perform shaping of Arabic characters"
+msgstr "阿拉伯语的字形重整"
+
+#: ../../../runtime/optwin.vim:1152
+msgid "terminal will perform bidi handling"
+msgstr "终端支持双向文本"
+
+#: ../../../runtime/optwin.vim:1156
+msgid "name of a keyboard mapping"
+msgstr "键盘映射名称"
+
+#: ../../../runtime/optwin.vim:1160
+msgid "list of characters that are translated in Normal mode"
+msgstr "在普通模式下转换的字符列表"
+
+#: ../../../runtime/optwin.vim:1162
+msgid "apply 'langmap' to mapped characters"
+msgstr "对映射的字符应用 'langmap'"
+
+#: ../../../runtime/optwin.vim:1166
+msgid "when set never use IM; overrules following IM options"
+msgstr "设置时总不使用输入法;覆盖以下输入法选项"
+
+#: ../../../runtime/optwin.vim:1169
+msgid "in Insert mode: 1: use :lmap; 2: use IM; 0: neither"
+msgstr "插入模式:1:使用 :lamp;2:使用输入法;0:都不用"
+
+#: ../../../runtime/optwin.vim:1172
+msgid "entering a search pattern: 1: use :lmap; 2: use IM; 0: neither"
+msgstr "输入搜索模式时:1:使用 :lamp;2:使用输入法;0:都不用"
+
+#: ../../../runtime/optwin.vim:1176
+msgid "when set always use IM when starting to edit a command line"
+msgstr "如果设置,开始编辑命令行时总是使用输入法"
+
+#: ../../../runtime/optwin.vim:1178
+msgid "function to obtain IME status"
+msgstr "获取输入法状态的函数"
+
+#: ../../../runtime/optwin.vim:1180
+msgid "function to enable/disable IME"
+msgstr "启用/禁用输入法的函数"
+
+#: ../../../runtime/optwin.vim:1185
+msgid "multi-byte characters"
+msgstr "多字节字符"
+
+#: ../../../runtime/optwin.vim:1186
+msgid "character encoding used in Nvim: \"utf-8\""
+msgstr "Nvim 中使用的字符编码:\"utf-8\""
+
+#: ../../../runtime/optwin.vim:1188
+msgid "character encoding for the current file"
+msgstr "当前文件的字符编码"
+
+#: ../../../runtime/optwin.vim:1191
+msgid "automatically detected character encodings"
+msgstr "自动检测字符编码"
+
+#: ../../../runtime/optwin.vim:1193
+msgid "expression used for character encoding conversion"
+msgstr "用于字符编码转换的表达式"
+
+#: ../../../runtime/optwin.vim:1195
+msgid "delete combining (composing) characters on their own"
+msgstr "删除组合字符本身"
+
+#: ../../../runtime/optwin.vim:1197
+msgid "maximum number of combining (composing) characters displayed"
+msgstr "显示的最大字符组合数"
+
+#: ../../../runtime/optwin.vim:1200
+msgid "key that activates the X input method"
+msgstr "激活 X 输入方法的键"
+
+#: ../../../runtime/optwin.vim:1203
+msgid "width of ambiguous width characters"
+msgstr "宽度有歧义字符的宽度"
+
+#: ../../../runtime/optwin.vim:1205
+msgid "emoji characters are full width"
+msgstr "表情字符视作全宽"
+
+#: ../../../runtime/optwin.vim:1209
+msgid "various"
+msgstr "杂项"
+
+#: ../../../runtime/optwin.vim:1210
+msgid ""
+"when to use virtual editing: \"block\", \"insert\", \"all\"\n"
+"and/or \"onemore\""
+msgstr "何时使用虚拟编辑:\"block\", \"insert\", \"all\" 和/或 \"onemore\""
+
+#: ../../../runtime/optwin.vim:1212
+msgid "list of autocommand events which are to be ignored"
+msgstr "要忽略的自动命令事件列表"
+
+#: ../../../runtime/optwin.vim:1214
+msgid "load plugin scripts when starting up"
+msgstr "启动时加载插件脚本"
+
+#: ../../../runtime/optwin.vim:1216
+msgid "enable reading .vimrc/.exrc/.gvimrc in the current directory"
+msgstr "启用读取在当前目录下的 .vimrc/.exrc/.gvimrc"
+
+#: ../../../runtime/optwin.vim:1218
+msgid "safer working with script files in the current directory"
+msgstr "在当前目录下使用脚本文件时更安全"
+
+#: ../../../runtime/optwin.vim:1220
+msgid "use the 'g' flag for \":substitute\""
+msgstr "打开 \":substitute\" 的 'g' 标志"
+
+#: ../../../runtime/optwin.vim:1223
+msgid "allow reading/writing devices"
+msgstr "允许读写设备"
+
+#: ../../../runtime/optwin.vim:1227
+msgid "maximum depth of function calls"
+msgstr "函数调用的最大深度"
+
+#: ../../../runtime/optwin.vim:1231
+msgid "list of words that specifies what to put in a session file"
+msgstr "指定放入会话文件的内容的单词列表"
+
+#: ../../../runtime/optwin.vim:1233
+msgid "list of words that specifies what to save for :mkview"
+msgstr "指定 :mkview 保存的内容的单词列表"
+
+#: ../../../runtime/optwin.vim:1235
+msgid "directory where to store files with :mkview"
+msgstr ":mkview 存放文件的目录"
+
+#: ../../../runtime/optwin.vim:1239
+msgid "list that specifies what to write in the ShaDa file"
+msgstr "指定在 ShaDa 文件中写入的内容的列表"
+
+#: ../../../runtime/optwin.vim:1243
+msgid "what happens with a buffer when it's no longer in a window"
+msgstr "当缓冲区不再位于窗口中时,会发生什么"
+
+#: ../../../runtime/optwin.vim:1246
+msgid "empty, \"nofile\", \"nowrite\", \"quickfix\", etc.: type of buffer"
+msgstr "空, \"nofile\", \"nowrite\", \"quickfix\" 等; 缓冲区类型"
+
+#: ../../../runtime/optwin.vim:1250
+msgid "whether the buffer shows up in the buffer list"
+msgstr "缓冲区是否显示在缓冲区列表中"
+
+#: ../../../runtime/optwin.vim:1253
+msgid "set to \"msg\" to see all error messages"
+msgstr "设置为 \"msg\" 以查看所有错误消息"
+
+#: ../../../runtime/optwin.vim:1255
+msgid "whether to show the signcolumn"
+msgstr "是否显示标号列"
+
+#: ../../../runtime/optwin.vim:1282
+msgid "name of the MzScheme dynamic library"
+msgstr "MzScheme 动态库的名字"
+
+#: ../../../runtime/optwin.vim:1284
+msgid "name of the MzScheme GC dynamic library"
+msgstr "MzScheme GC 动态库的名字"
+
+#: ../../../runtime/optwin.vim:1288
+msgid "whether to use Python 2 or 3"
+msgstr "是否使用 Python 2 和 3"
+
+#~ msgid "\t\t\t (Unimplemented)\n"
+#~ msgstr "\t\t\t (尚未实现)\n"
#~ msgid ""
#~ "\n"
-#~ "Vim: Got X error\n"
+#~ "\n"
+#~ "Arguments:\n"
#~ msgstr ""
#~ "\n"
-#~ "Vim: X 错误\n"
-
-#~ msgid "Testing the X display failed"
-#~ msgstr "测试 X display 失败"
-
-#~ msgid "Opening the X display timed out"
-#~ msgstr "打开 X display 超时"
+#~ "\n"
+#~ "参数:\n"
#~ msgid ""
#~ "\n"
-#~ "Cannot execute shell sh\n"
+#~ " [not usable with this version of Vim]"
#~ msgstr ""
#~ "\n"
-#~ "无法执行 shell sh\n"
+#~ " [不能在该版本的 Vim 上使用]"
#~ msgid ""
#~ "\n"
-#~ "Cannot create pipes\n"
+#~ " a: Find assignments to this symbol\n"
+#~ " c: Find functions calling this function\n"
+#~ " d: Find functions called by this function\n"
+#~ " e: Find this egrep pattern\n"
+#~ " f: Find this file\n"
+#~ " g: Find this definition\n"
+#~ " i: Find files #including this file\n"
+#~ " s: Find this C symbol\n"
+#~ " t: Find this text string\n"
#~ msgstr ""
#~ "\n"
-#~ "无法建立管道\n"
+#~ " a: 搜索对此符号的赋值\n"
+#~ " c: 搜索调用此函数的函数\n"
+#~ " d: 搜索此函数调用的函数\n"
+#~ " e: 搜索此 egrep 模式\n"
+#~ " f: 搜索此文件\n"
+#~ " g: 搜索此定义\n"
+#~ " i: 搜索包含此文件的文件\n"
+#~ " s: 搜索此 C 符号\n"
+#~ " t: 搜索此文本字符串\n"
#~ msgid ""
#~ "\n"
-#~ "Cannot fork\n"
+#~ " # line"
#~ msgstr ""
#~ "\n"
-#~ "无法 fork\n"
+#~ " # 行 "
#~ msgid ""
#~ "\n"
-#~ "Command terminated\n"
+#~ " or:"
#~ msgstr ""
#~ "\n"
-#~ "命令已结束\n"
+#~ " 或:"
-#~ msgid "XSMP lost ICE connection"
-#~ msgstr "XSMP 丢失了到 ICE 的连接"
-
-#~ msgid "Opening the X display failed"
-#~ msgstr "打开 X display 失败"
+# do not translate to avoid writing Chinese in files
+#, fuzzy, c-format
+#~ msgid ""
+#~ "\n"
+#~ "# %s History (newest to oldest):\n"
+#~ msgstr ""
+#~ "\n"
+#~ "# %s 历史记录 (从新到旧):\n"
-#~ msgid "XSMP handling save-yourself request"
-#~ msgstr "XSMP 处理 save-yourself 请求"
+#~ msgid ""
+#~ "\n"
+#~ "# Buffer list:\n"
+#~ msgstr ""
+#~ "\n"
+#~ "# 缓冲区列表:\n"
-#~ msgid "XSMP opening connection"
-#~ msgstr "XSMP 打开连接"
+#~ msgid ""
+#~ "\n"
+#~ "# File marks:\n"
+#~ msgstr ""
+#~ "\n"
+#~ "# 文件标记:\n"
-#~ msgid "XSMP ICE connection watch failed"
-#~ msgstr "XSMP ICE 连接监视失败"
+#~ msgid ""
+#~ "\n"
+#~ "# History of marks within files (newest to oldest):\n"
+#~ msgstr ""
+#~ "\n"
+#~ "# 文件内的标记历史记录 (从新到旧):\n"
-#~ msgid "XSMP SmcOpenConnection failed: %s"
-#~ msgstr "XSMP SmcOpenConnection 调用失败: %s"
+#~ msgid ""
+#~ "\n"
+#~ "# Jumplist (newest first):\n"
+#~ msgstr ""
+#~ "\n"
+#~ "# 跳转列表 (从新到旧):\n"
-#~ msgid "At line"
-#~ msgstr "在行号 "
+#, c-format
+#~ msgid ""
+#~ "\n"
+#~ "# Last %sSearch Pattern:\n"
+#~ "~"
+#~ msgstr ""
+#~ "\n"
+#~ "# 最后 %s搜索模式:\n"
+#~ "~"
-#~ msgid "Could not load vim32.dll!"
-#~ msgstr "无法加载 vim32.dll!"
+# do not translate to avoid writing Chinese in files
+#, fuzzy
+#~ msgid ""
+#~ "\n"
+#~ "# Last Substitute String:\n"
+#~ "$"
+#~ msgstr ""
+#~ "\n"
+#~ "# 最近的替换字符串:\n"
+#~ "$"
-#~ msgid "VIM Error"
-#~ msgstr "VIM 错误"
+#~ msgid ""
+#~ "\n"
+#~ "# Registers:\n"
+#~ msgstr ""
+#~ "\n"
+#~ "# 寄存器:\n"
-#~ msgid "Could not fix up function pointers to the DLL!"
-#~ msgstr "无法修正到 DLL 的函数指针!"
+#~ msgid ""
+#~ "\n"
+#~ "# global variables:\n"
+#~ msgstr ""
+#~ "\n"
+#~ "# 全局变量:\n"
-#~ msgid "shell returned %d"
-#~ msgstr "Shell 返回 %d"
+#~ msgid ""
+#~ "\n"
+#~ "--- Registers ---"
+#~ msgstr ""
+#~ "\n"
+#~ "--- 寄存器 ---"
-#~ msgid "Vim: Caught %s event\n"
-#~ msgstr "Vim: 拦截到 %s 事件\n"
+#~ msgid ""
+#~ "\n"
+#~ "--- Terminal codes ---"
+#~ msgstr ""
+#~ "\n"
+#~ "--- 终端编码 ---"
-#~ msgid "close"
-#~ msgstr "关闭"
+#~ msgid ""
+#~ "\n"
+#~ "--- Terminal keys ---"
+#~ msgstr ""
+#~ "\n"
+#~ "--- 终端按键 ---"
-#~ msgid "logoff"
-#~ msgstr "注消"
+#~ msgid ""
+#~ "\n"
+#~ "16-bit MS-DOS version"
+#~ msgstr ""
+#~ "\n"
+#~ "16 位 MS-DOS 版本"
-#~ msgid "shutdown"
-#~ msgstr "关机"
+#~ msgid ""
+#~ "\n"
+#~ "32-bit MS-DOS version"
+#~ msgstr ""
+#~ "\n"
+#~ "32 位 MS-DOS 版本"
-#~ msgid "E371: Command not found"
-#~ msgstr "E371: 找不到命令"
+#~ msgid ""
+#~ "\n"
+#~ "Arguments recognised by gvim (Athena version):\n"
+#~ msgstr ""
+#~ "\n"
+#~ "gvim (Athena 版本) 可识别的参数:\n"
#~ msgid ""
-#~ "VIMRUN.EXE not found in your $PATH.\n"
-#~ "External commands will not pause after completion.\n"
-#~ "See :help win32-vimrun for more information."
+#~ "\n"
+#~ "Arguments recognised by gvim (GTK+ version):\n"
#~ msgstr ""
-#~ "在你的 $PATH 中找不到 VIMRUN.EXE。\n"
-#~ "外部命令执行完毕后将不会暂停。\n"
-#~ "进一步说明请见 :help win32-vimrun"
+#~ "\n"
+#~ "gvim (GTK+ 版本) 可识别的参数:\n"
-#~ msgid "Vim Warning"
-#~ msgstr "Vim 警告"
+#~ msgid ""
+#~ "\n"
+#~ "Arguments recognised by gvim (Motif version):\n"
+#~ msgstr ""
+#~ "\n"
+#~ "gvim (Motif 版本) 可识别的参数:\n"
-#~ msgid "Conversion in %s not supported"
-#~ msgstr "不支持 %s 中的转换"
+#~ msgid ""
+#~ "\n"
+#~ "Arguments recognised by gvim (RISC OS version):\n"
+#~ msgstr ""
+#~ "\n"
+#~ "gvim (RISC OS 版本) 可识别的参数:\n"
-#~ msgid "E396: containedin argument not accepted here"
-#~ msgstr "E396: 使用了不正确的参数"
+#~ msgid ""
+#~ "\n"
+#~ "Arguments recognised by gvim (neXtaw version):\n"
+#~ msgstr ""
+#~ "\n"
+#~ "gvim (neXtaw 版本) 可识别的参数:\n"
-#~ msgid "E430: Tag file path truncated for %s\n"
-#~ msgstr "E430: Tag 文件路径被截断为 %s\n"
+#~ msgid ""
+#~ "\n"
+#~ "Big version "
+#~ msgstr ""
+#~ "\n"
+#~ "大型版本 "
-#~ msgid "new shell started\n"
-#~ msgstr "启动新 shell\n"
+#~ msgid ""
+#~ "\n"
+#~ "Cannot create pipes\n"
+#~ msgstr ""
+#~ "\n"
+#~ "无法建立管道\n"
-#~ msgid "No undo possible; continue anyway"
-#~ msgstr "无法撤销;仍然继续"
+#~ msgid ""
+#~ "\n"
+#~ "Cannot execute shell "
+#~ msgstr ""
+#~ "\n"
+#~ "无法执行 shell"
-#~ msgid "number changes time"
-#~ msgstr " 编号 改变 时间"
+#~ msgid ""
+#~ "\n"
+#~ "Cannot execute shell sh\n"
+#~ msgstr ""
+#~ "\n"
+#~ "无法执行 shell sh\n"
#~ msgid ""
#~ "\n"
-#~ "MS-Windows 16/32-bit GUI version"
+#~ "Cannot fork\n"
#~ msgstr ""
#~ "\n"
-#~ "MS-Windows 16/32 位图形界面版本"
+#~ "无法 fork\n"
#~ msgid ""
#~ "\n"
-#~ "MS-Windows 32-bit GUI version"
+#~ "Command terminated\n"
#~ msgstr ""
#~ "\n"
-#~ "MS-Windows 32 位图形界面版本"
+#~ "命令已结束\n"
-#~ msgid " in Win32s mode"
-#~ msgstr " Win32s 模式"
+#, fuzzy
+#~ msgid ""
+#~ "\n"
+#~ "Extra patches: "
+#~ msgstr "外部符合:\n"
-#~ msgid " with OLE support"
-#~ msgstr " 带 OLE 支持"
+#~ msgid ""
+#~ "\n"
+#~ "Huge version "
+#~ msgstr ""
+#~ "\n"
+#~ "巨型版本 "
#~ msgid ""
#~ "\n"
-#~ "MS-Windows 32-bit console version"
+#~ "Included patches: "
#~ msgstr ""
#~ "\n"
-#~ "MS-Windows 32 位控制台版本"
+#~ "包含补丁: "
#~ msgid ""
#~ "\n"
@@ -7640,17 +9395,24 @@ msgstr "E446: 光标处没有文件名"
#~ msgid ""
#~ "\n"
-#~ "32-bit MS-DOS version"
+#~ "MS-Windows 16/32-bit GUI version"
#~ msgstr ""
#~ "\n"
-#~ "32 位 MS-DOS 版本"
+#~ "MS-Windows 16/32 位图形界面版本"
#~ msgid ""
#~ "\n"
-#~ "16-bit MS-DOS version"
+#~ "MS-Windows 32-bit GUI version"
#~ msgstr ""
#~ "\n"
-#~ "16 位 MS-DOS 版本"
+#~ "MS-Windows 32 位图形界面版本"
+
+#~ msgid ""
+#~ "\n"
+#~ "MS-Windows 32-bit console version"
+#~ msgstr ""
+#~ "\n"
+#~ "MS-Windows 32 位控制台版本"
#~ msgid ""
#~ "\n"
@@ -7675,6 +9437,13 @@ msgstr "E446: 光标处没有文件名"
#~ msgid ""
#~ "\n"
+#~ "Normal version "
+#~ msgstr ""
+#~ "\n"
+#~ "正常版本 "
+
+#~ msgid ""
+#~ "\n"
#~ "RISC OS version"
#~ msgstr ""
#~ "\n"
@@ -7682,73 +9451,463 @@ msgstr "E446: 光标处没有文件名"
#~ msgid ""
#~ "\n"
-#~ "Big version "
+#~ "Small version "
#~ msgstr ""
#~ "\n"
-#~ "大型版本 "
+#~ "小型版本 "
#~ msgid ""
#~ "\n"
-#~ "Normal version "
+#~ "Tiny version "
#~ msgstr ""
#~ "\n"
-#~ "正常版本 "
+#~ "微型版本 "
#~ msgid ""
#~ "\n"
-#~ "Small version "
+#~ "Vim: Got X error\n"
#~ msgstr ""
#~ "\n"
-#~ "小型版本 "
+#~ "Vim: X 错误\n"
#~ msgid ""
#~ "\n"
-#~ "Tiny version "
+#~ "[bytes] total alloc-freed %<PRIu64>-%<PRIu64>, in use %<PRIu64>, peak use "
+#~ "%<PRIu64>\n"
#~ msgstr ""
#~ "\n"
-#~ "微型版本 "
+#~ "[字节] 总共 alloc-free %<PRIu64>-%<PRIu64>,使用中 %<PRIu64>,高峰使用 "
+#~ "%<PRIu64>\n"
-#~ msgid "with GTK2-GNOME GUI."
-#~ msgstr "带 GTK2-GNOME 图形界面。"
+#, fuzzy
+#~ msgid " for Vim defaults "
+#~ msgstr " # pid 数据库名称 prepend path\n"
-#~ msgid "with GTK-GNOME GUI."
-#~ msgstr "带 GTK-GNOME 图形界面。"
+#~ msgid " user exrc file: \""
+#~ msgstr " 用户 exrc 文件: \""
-#~ msgid "with GTK2 GUI."
-#~ msgstr "带 GTK2 图形界面。"
+#~ msgid " user vimrc file: \""
+#~ msgstr " 用户 vimrc 文件: \""
-#~ msgid "with GTK GUI."
-#~ msgstr "带 GTK 图形界面。"
+#~ msgid " system menu file: \""
+#~ msgstr " 系统菜单文件: \""
-#~ msgid "with X11-Motif GUI."
-#~ msgstr "带 X11-Motif 图形界面。"
+#~ msgid " user gvimrc file: \""
+#~ msgstr " 用户 gvimrc 文件: \""
-#~ msgid "with X11-neXtaw GUI."
-#~ msgstr "带 X11-neXtaw 图形界面。"
+#~ msgid " 2nd user exrc file: \""
+#~ msgstr " 第二用户 exrc 文件: \""
-#~ msgid "with X11-Athena GUI."
-#~ msgstr "带 X11-Athena 图形界面。"
+#~ msgid " DEBUG BUILD"
+#~ msgstr " 调试版本"
-#~ msgid "with Photon GUI."
-#~ msgstr "带 Photon 图形界面。"
+#~ msgid " Features included (+) or not (-):\n"
+#~ msgstr " 可使用(+)与不可使用(-)的功能:\n"
-#~ msgid "with GUI."
-#~ msgstr "带图形界面。"
+#, fuzzy
+#~ msgid " Quit, or continue with caution.\n"
+#~ msgstr " 退出,或小心地继续。\n"
-#~ msgid "with Carbon GUI."
-#~ msgstr "带 Carbon 图形界面。"
+#~ msgid " system gvimrc file: \""
+#~ msgstr " 系统 gvimrc 文件: \""
-#~ msgid "with Cocoa GUI."
-#~ msgstr "带 Cocoa 图形界面。"
+#~ msgid " # pid database name prepend path\n"
+#~ msgstr " # pid 数据库名 prepend path\n"
-#~ msgid "with (classic) GUI."
-#~ msgstr "带(传统)图形界面。"
+#~ msgid " (NOT FOUND)"
+#~ msgstr " (找不到)"
-#~ msgid " system gvimrc file: \""
-#~ msgstr " 系统 gvimrc 文件: \""
+#~ msgid " (RET/BS: line, SPACE/b: page, d/u: half page, q: quit)"
+#~ msgstr " (RET/BS: 向下/向上一行, 空格/b: 一页, d/u: 半页, q: 退出)"
-#~ msgid " user gvimrc file: \""
-#~ msgstr " 用户 gvimrc 文件: \""
+#~ msgid " (RET: line, SPACE: page, d: half page, q: quit)"
+#~ msgstr " (RET: 向下一行, 空白键: 一页, d: 半页, q: 退出)"
+
+#~ msgid " (lang)"
+#~ msgstr " (语言)"
+
+#~ msgid " 2nd user vimrc file: \""
+#~ msgstr " 第二用户 vimrc 文件: \""
+
+#~ msgid " 3rd user vimrc file: \""
+#~ msgstr " 第三用户 vimrc 文件: \""
+
+#~ msgid " BLOCK"
+#~ msgstr " 块"
+
+#~ msgid " LINE"
+#~ msgstr " 行"
+
+#~ msgid " in Win32s mode"
+#~ msgstr " Win32s 模式"
+
+#~ msgid " on 1 line"
+#~ msgstr "共 1 行"
+
+#~ msgid " returned\n"
+#~ msgstr " 已返回\n"
+
+#~ msgid " vim [arguments] "
+#~ msgstr " vim [参数] "
+
+#~ msgid " with OLE support"
+#~ msgstr " 带 OLE 支持"
+
+#~ msgid "\"\n"
+#~ msgstr "\"\n"
+
+# do not translate to avoid writing Chinese in files
+#, fuzzy, c-format
+#~ msgid "# This viminfo file was generated by Vim %s.\n"
+#~ msgstr "# 这个 viminfo 文件是由 Vim %s 生成的。\n"
+
+# do not translate to avoid writing Chinese in files
+#, fuzzy
+#~ msgid "# Value of 'encoding' when this file was written\n"
+#~ msgstr "# 'encoding' 在此文件建立时的值\n"
+
+# do not translate to avoid writing Chinese in files
+#, fuzzy
+#~ msgid ""
+#~ "# You may edit it if you're careful!\n"
+#~ "\n"
+#~ msgstr ""
+#~ "# 如果要自行修改请特别小心!\n"
+#~ "\n"
+
+#, fuzzy, c-format
+#~ msgid "%-5s: %s%*s (Usage: %s)"
+#~ msgstr "%-5s: %-30s (用法: %s)"
+
+#~ msgid "%2d %-5ld %-34s <none>\n"
+#~ msgstr "%2d %-5ld %-34s <无>\n"
+
+#~ msgid "%<%f%h%m%=Page %N"
+#~ msgstr "%<%f%h%m%=页 %N"
+
+#, c-format
+#~ msgid "%<PRId64> characters"
+#~ msgstr "%<PRId64> 个字符"
+
+#, c-format
+#~ msgid "%<PRId64> fewer lines"
+#~ msgstr "少了 %<PRId64> 行"
+
+#, c-format
+#~ msgid "%<PRId64> lines %sed 1 time"
+#~ msgstr "%<PRId64> 行 %s 了 1 次"
+
+#, c-format
+#~ msgid "%<PRId64> lines moved"
+#~ msgstr "移动了 %<PRId64> 行"
+
+#, c-format
+#~ msgid "%<PRId64> more lines"
+#~ msgstr "多了 %<PRId64> 行"
+
+#, c-format
+#~ msgid "%d files to edit\n"
+#~ msgstr "还有 %d 个文件等待编辑\n"
+
+#~ msgid "%d of %d edited"
+#~ msgstr "%d 中 %d 已编辑"
+
+# bad to translate
+#, c-format
+#~ msgid "%sviminfo: %s in line: "
+#~ msgstr "%sviminfo: %s 位于行: "
+
+#~ msgid "&Cancel"
+#~ msgstr "取消(&C)"
+
+#~ msgid "&Dismiss"
+#~ msgstr "取消(&D)"
+
+#~ msgid "&Filter"
+#~ msgstr "过滤(&F)"
+
+#~ msgid "&Help"
+#~ msgstr "帮助(&H)"
+
+#~ msgid "&OK"
+#~ msgstr "确定(&O)"
+
+#~ msgid ""
+#~ "&OK\n"
+#~ "&Cancel"
+#~ msgstr ""
+#~ "确定(&O)\n"
+#~ "取消(&C)"
+
+#~ msgid ""
+#~ "&OK\n"
+#~ "&Load File"
+#~ msgstr ""
+#~ "确定(&O)\n"
+#~ "加载文件(&L)"
+
+#~ msgid "&Replace"
+#~ msgstr "替换(&R)"
+
+#~ msgid "&Undo"
+#~ msgstr "撤销(&U)"
+
+#~ msgid "' not known. Available builtin terminals are:"
+#~ msgstr "' 未知。可用的内建终端有:"
+
+#, c-format
+#~ msgid "(NFA) COULD NOT OPEN %s !"
+#~ msgstr "(NFA) 不能打开 %s !"
+
+#~ msgid "+\t\t\tStart at end of file"
+#~ msgstr "+\t\t\t启动后跳到文件末尾"
+
+#~ msgid "+<lnum>\t\tStart at line <lnum>"
+#~ msgstr "+<lnum>\t\t启动后跳到第 <lnum> 行"
+
+#~ msgid "+reverse\t\tDon't use reverse video (also: +rv)"
+#~ msgstr "+reverse\t\t不使用反显 (也可用 +rv)"
+
+#~ msgid "--columns <number>\tInitial width of window in columns"
+#~ msgstr "--columns <number>\t窗口初始宽度"
+
+#~ msgid "--help\t\tShow Gnome arguments"
+#~ msgstr "--help\t\t显示 Gnome 相关参数"
+
+#~ msgid "--literal\t\tDon't expand wildcards"
+#~ msgstr "--literal\t\t不扩展通配符"
+
+#~ msgid "--remote <files>\tEdit <files> in a Vim server if possible"
+#~ msgstr "--remote <files>\t如有可能,在 Vim 服务器上编辑文件 <files>"
+
+#~ msgid ""
+#~ "--remote-expr <expr>\tEvaluate <expr> in a Vim server and print result"
+#~ msgstr "--remote-expr <expr>\t在 Vim 服务器上求 <expr> 的值并打印结果"
+
+#~ msgid "--remote-send <keys>\tSend <keys> to a Vim server and exit"
+#~ msgstr "--remote-send <keys>\t送出 <keys> 到 Vim 服务器并退出"
+
+#~ msgid "--remote-silent <files> Same, don't complain if there is no server"
+#~ msgstr "--remote-silent <files> 同上,找不到服务器时不抱怨"
+
+#~ msgid "--remote-tab <files> As --remote but open tab page for each file"
+#~ msgstr "--remote-tab <files> 同 --remote 但对每个文件打开一个标签页"
+
+#~ msgid ""
+#~ "--remote-wait <files> As --remote but wait for files to have been edited"
+#~ msgstr "--remote-wait <files> 同 --remote 但会等待文件完成编辑"
+
+#~ msgid ""
+#~ "--remote-wait-silent <files> Same, don't complain if there is no server"
+#~ msgstr "--remote-wait-silent <files> 同上,找不到服务器时不抱怨"
+
+#~ msgid "--role <role>\tSet a unique role to identify the main window"
+#~ msgstr "--role <role>\t设置用于区分主窗口的窗口角色名"
+
+#~ msgid "--rows <number>\tInitial height of window in rows"
+#~ msgstr "--rows <number>\t窗口初始高度"
+
+#~ msgid "--serverlist\t\tList available Vim server names and exit"
+#~ msgstr "--serverlist\t\t列出可用的 Vim 服务器名称并退出"
+
+#~ msgid "--servername <name>\tSend to/become the Vim server <name>"
+#~ msgstr "--servername <name>\t发送到或成为 Vim 服务器 <name>"
+
+#~ msgid "--socketid <xid>\tOpen Vim inside another GTK widget"
+#~ msgstr "--socketid <xid>\t在另一个 GTK 部件中打开 Vim"
+
+#~ msgid "-A\t\t\tstart in Arabic mode"
+#~ msgstr "-A\t\t\t以 Arabic 模式启动"
+
+#~ msgid "-C\t\t\tCompatible with Vi: 'compatible'"
+#~ msgstr "-C\t\t\t兼容传统的 Vi: 'compatible'"
+
+#~ msgid "-D\t\t\tDebugging mode"
+#~ msgstr "-D\t\t\t调试模式"
+
+#~ msgid "-F\t\t\tStart in Farsi mode"
+#~ msgstr "-F\t\t\t以 Farsi 模式启动"
+
+#~ msgid "-H\t\t\tStart in Hebrew mode"
+#~ msgstr "-H\t\t\t以 Hebrew 模式启动"
+
+#~ msgid "-L\t\t\tSame as -r"
+#~ msgstr "-L\t\t\t同 -r"
+
+#~ msgid "-N\t\t\tNot fully Vi compatible: 'nocompatible'"
+#~ msgstr "-N\t\t\t不完全兼容传统的 Vi: 'nocompatible'"
+
+#~ msgid "-O[N]\t\tLike -o but split vertically"
+#~ msgstr "-O[N]\t\t同 -o 但垂直分割"
+
+#~ msgid "-P <parent title>\tOpen Vim inside parent application"
+#~ msgstr "-P <parent title>\t在父应用程序中打开 Vim"
+
+#~ msgid "-R\t\t\tReadonly mode (like \"view\")"
+#~ msgstr "-R\t\t\t只读模式 (同 \"view\")"
+
+#~ msgid "-T <terminal>\tSet terminal type to <terminal>"
+#~ msgstr "-T <terminal>\t设定终端类型为 <terminal>"
+
+#~ msgid "-U <gvimrc>\t\tUse <gvimrc> instead of any .gvimrc"
+#~ msgstr "-U <gvimrc>\t\t使用 <gvimrc> 替代任何 .gvimrc"
+
+#~ msgid "-V[N]\t\tVerbose level"
+#~ msgstr "-V[N]\t\tVerbose 等级"
+
+#~ msgid "-V[N][fname]\t\tBe verbose [level N] [log messages to fname]"
+#~ msgstr "-V[N][fname]\t\t详细 [level N] [log messages to fname]"
+
+#~ msgid "-W <scriptout>\tWrite all typed commands to file <scriptout>"
+#~ msgstr "-W <scriptout>\t将所有输入的命令写入到文件 <scriptout>"
+
+#~ msgid "-X\t\t\tDo not connect to X server"
+#~ msgstr "-X\t\t\t不连接到 X Server"
+
+#~ msgid "-b\t\t\tBinary mode"
+#~ msgstr "-b\t\t\t二进制模式"
+
+#~ msgid "-background <color>\tUse <color> for the background (also: -bg)"
+#~ msgstr "-background <color>\t使用 <color> 作为背景色 (也可用 -bg)"
+
+#~ msgid "-boldfont <font>\tUse <font> for bold text"
+#~ msgstr "-boldfont <font>\t使用 <font> 作为粗体字体"
+
+#~ msgid "-borderwidth <width>\tUse a border width of <width> (also: -bw)"
+#~ msgstr "-borderwidth <width>\t设定边框宽度为 <width> (也可用 -bw)"
+
+#~ msgid "-d\t\t\tDiff mode (like \"vimdiff\")"
+#~ msgstr "-d\t\t\tDiff 模式 (同 \"vimdiff\")"
+
+#~ msgid "-dev <device>\t\tUse <device> for I/O"
+#~ msgstr "-dev <device>\t\t使用 <device> 进行输入输出"
+
+#~ msgid "-display <display>\tConnect vim to this particular X-server"
+#~ msgstr "-display <display>\t将 vim 与指定的 X-server 连接"
+
+#~ msgid "-display <display>\tRun vim on <display>"
+#~ msgstr "-display <display>\t在 <display> 上运行 vim"
+
+#~ msgid "-display <display>\tRun vim on <display> (also: --display)"
+#~ msgstr "-display <display>\t在 <display> 上运行 vim (也可用 --display)"
+
+#~ msgid "-e\t\t\tEx mode (like \"ex\")"
+#~ msgstr "-e\t\t\tEx 模式 (同 \"ex\")"
+
+#~ msgid "-f\t\t\tDon't use newcli to open window"
+#~ msgstr "-f\t\t\t不使用 newcli 来打开窗口"
+
+#~ msgid "-f or --nofork\tForeground: Don't fork when starting GUI"
+#~ msgstr "-f 或 --nofork\t前台: 启动图形界面时不 fork"
+
+#~ msgid "-font <font>\t\tUse <font> for normal text (also: -fn)"
+#~ msgstr "-font <font>\t使用 <font> 作为一般字体 (也可用 -fn)"
+
+#~ msgid "-foreground <color>\tUse <color> for normal text (also: -fg)"
+#~ msgstr "-foreground <color>\t使用 <color> 作为一般文字颜色 (也可用 -fg)"
+
+#~ msgid "-g\t\t\tRun using GUI (like \"gvim\")"
+#~ msgstr "-g\t\t\t使用图形界面 (同 \"gvim\")"
+
+#~ msgid "-geometry <geom>\tUse <geom> for initial geometry (also: -geom)"
+#~ msgstr "-geometry <geom>\t使用 <geom> 作为初始位置 (也可用 -geom)"
+
+#~ msgid "-h or --help\tPrint Help (this message) and exit"
+#~ msgstr "-h 或 --help\t打印帮助(本信息)并退出"
+
+#~ msgid "-i <viminfo>\t\tUse <viminfo> instead of .viminfo"
+#~ msgstr "-i <viminfo>\t\t使用 <viminfo> 取代 .viminfo"
+
+#~ msgid "-iconic\t\tStart vim iconified"
+#~ msgstr "-iconic\t\t启动后最小化"
+
+#~ msgid "-italicfont <font>\tUse <font> for italic text"
+#~ msgstr "-italicfont <font>\t使用 <font> 作为斜体字体"
+
+#~ msgid "-menuheight <height>\tUse a menu bar height of <height> (also: -mh)"
+#~ msgstr "-menuheight <height>\t设定菜单栏高度为 <height> (也可用 -mh)"
+
+#~ msgid "-name <name>\t\tUse resource as if vim was <name>"
+#~ msgstr "-name <name>\t\t读取 Resource 时把 vim 视为 <name>"
+
+#~ msgid "-r\t\t\tList swap files and exit"
+#~ msgstr "-r\t\t\t列出交换文件并退出"
+
+#~ msgid "-r (with file name)\tRecover crashed session"
+#~ msgstr "-r (跟文件名)\t\t恢复崩溃的会话"
+
+#~ msgid "-register\t\tRegister this gvim for OLE"
+#~ msgstr "-register\t\t注册此 gvim 到 OLE"
+
+#~ msgid "-reverse\t\tUse reverse video (also: -rv)"
+#~ msgstr "-reverse\t\t使用反显 (也可用 -rv)"
+
+#~ msgid "-s\t\t\tSilent (batch) mode (only for \"ex\")"
+#~ msgstr "-s\t\t\t安静(批处理)模式 (只能与 \"ex\" 一起使用)"
+
+#~ msgid ""
+#~ "-scrollbarwidth <width> Use a scrollbar width of <width> (also: -sw)"
+#~ msgstr "-scrollbarwidth <width> 设定滚动条宽度为 <width> (也可用 -sw)"
+
+#~ msgid "-u <vimrc>\t\tUse <vimrc> instead of any .vimrc"
+#~ msgstr "-u <vimrc>\t\t使用 <vimrc> 替代任何 .vimrc"
+
+#~ msgid "-unregister\t\tUnregister gvim for OLE"
+#~ msgstr "-unregister\t\t取消 OLE 中的 gvim 注册"
+
+#~ msgid "-v\t\t\tVi mode (like \"vi\")"
+#~ msgstr "-v\t\t\tVi 模式 (同 \"vi\")"
+
+#~ msgid "-w <scriptout>\tAppend all typed commands to file <scriptout>"
+#~ msgstr "-w <scriptout>\t将所有输入的命令追加到文件 <scriptout>"
+
+#~ msgid "-x\t\t\tEdit encrypted files"
+#~ msgstr "-x\t\t\t编辑加密的文件"
+
+#~ msgid "-xrm <resource>\tSet the specified resource"
+#~ msgstr "-xrm <resource>\t设定指定的资源"
+
+#~ msgid "-y\t\t\tEasy mode (like \"evim\", modeless)"
+#~ msgstr "-y\t\t\t容易模式 (同 \"evim\",无模式)"
+
+#~ msgid "1 buffer deleted"
+#~ msgstr "删除了 1 个缓冲区"
+
+#~ msgid "1 buffer unloaded"
+#~ msgstr "释放了 1 个缓冲区"
+
+#~ msgid "1 buffer wiped out"
+#~ msgstr "清除了 1 个缓冲区"
+
+#, c-format
+#~ msgid "1 line %sed %d times"
+#~ msgstr "1 行 %s 了 %d 次"
+
+#, c-format
+#~ msgid "1 line %sed 1 time"
+#~ msgstr "1 行 %s 了 1 次"
+
+#, c-format
+#~ msgid "1 line --%d%%--"
+#~ msgstr "1 行 --%d%%--"
+
+#~ msgid "1 line changed"
+#~ msgstr "改变了 1 行"
+
+#~ msgid "1 line indented "
+#~ msgstr "缩进了 1 行 "
+
+#~ msgid "1 line yanked"
+#~ msgstr "复制了 1 行"
+
+#~ msgid "1 match"
+#~ msgstr "1 个匹配,"
+
+#~ msgid "1 more file to edit. Quit anyway?"
+#~ msgstr "还有 1 个文件未编辑。确实要退出吗?"
+
+#~ msgid "1 substitution"
+#~ msgstr "1 次替换,"
#~ msgid "2nd user gvimrc file: \""
#~ msgstr "第二用户 gvimrc 文件: \""
@@ -7756,182 +9915,1439 @@ msgstr "E446: 光标处没有文件名"
#~ msgid "3rd user gvimrc file: \""
#~ msgstr "第三用户 gvimrc 文件: \""
-#~ msgid " system menu file: \""
-#~ msgstr " 系统菜单文件: \""
+#~ msgid ": Send expression failed.\n"
+#~ msgstr ": 发送表达式失败。\n"
+
+#~ msgid ": Send failed.\n"
+#~ msgstr ": 发送失败。\n"
+
+#~ msgid ": Send failed. Trying to execute locally\n"
+#~ msgstr ": 发送失败。尝试本地执行\n"
+
+#~ msgid "<buffer object (deleted) at %8lX>"
+#~ msgstr "<缓冲区对象(已删除): %8lX>"
+
+#~ msgid "<cannot open> "
+#~ msgstr "<无法打开>"
+
+#~ msgid "<window %d>"
+#~ msgstr "<窗口 %d>"
+
+#~ msgid "<window object (deleted) at %.8lX>"
+#~ msgstr "<窗口对象(已删除): %.8lX>"
+
+#~ msgid "<window object (unknown) at %.8lX>"
+#~ msgstr "<窗口对象(未知): %.8lX>"
+
+#~ msgid ""
+#~ "???: Sorry, this command is disabled, the MzScheme library could not be "
+#~ "loaded."
+#~ msgstr "???: 抱歉,此命令不可用,无法加载 MzScheme 库"
+
+#~ msgid "ANCHOR_BUF_SIZE too small."
+#~ msgstr "ANCHOR_BUF_SIZE 太小"
+
+#~ msgid "Add a new database"
+#~ msgstr "添加一个新的数据库"
+
+#, c-format
+#~ msgid "Added cscope database %s"
+#~ msgstr "添加了 cscope 数据库 %s"
+
+#~ msgid "Affix flags ignored when PFXPOSTPONE used in %s line %d: %s"
+#~ msgstr "%s 第 %d 行,使用 PFXPOSTPONE 时附加标志被忽略: %s"
+
+#~ msgid "All cscope databases reset"
+#~ msgstr "所有 cscope 数据库已被重置"
+
+#~ msgid "Append File"
+#~ msgstr "追加文件"
+
+#~ msgid "At line"
+#~ msgstr "在行号 "
+
+#~ msgid "Binary tag search"
+#~ msgstr "二进制查找(Binary search) 标签(Tags)"
+
+#~ msgid "Browse class"
+#~ msgstr "浏览 class"
+
+#, c-format
+#~ msgid "Calling shell to execute: \"%s\""
+#~ msgstr "调用 shell 执行: \"%s\""
+
+#~ msgid "Cancel"
+#~ msgstr "取消"
+
+#~ msgid "Cannot connect to Netbeans"
+#~ msgstr "无法连接到 Netbeans"
+
+#~ msgid "Cannot connect to Netbeans #2"
+#~ msgstr "无法连接到 Netbeans #2"
+
+#~ msgid ""
+#~ "Cannot connect to SNiFF+. Check environment (sniffemacs must be found in "
+#~ "$PATH).\n"
+#~ msgstr ""
+#~ "不能连接到 SNiFF+。请检查环境变量 ($PATH 里必需可以找到 sniffemacs)\n"
+
+#~ msgid "Cannot create "
+#~ msgstr "不能创建 "
+
+#~ msgid "Cannot execute "
+#~ msgstr "不能执行 "
+
+#~ msgid "Cannot open NIL:\n"
+#~ msgstr "不能打开 NIL:\n"
+
+#~ msgid "Close tab"
+#~ msgstr "关闭标签"
+
+#~ msgid "Compilation: "
+#~ msgstr "编译方式: "
#~ msgid "Compiler: "
#~ msgstr "编译器: "
-#~ msgid "menu Help->Orphans for information "
-#~ msgstr "菜单 帮助->孤儿 查看说明 "
+#~ msgid "Conversion in %s not supported"
+#~ msgstr "不支持 %s 中的转换"
-#~ msgid "Running modeless, typed text is inserted"
-#~ msgstr "无模式运行,输入文字即插入"
+#~ msgid "Could not fix up function pointers to the DLL!"
+#~ msgstr "无法修正到 DLL 的函数指针!"
-#~ msgid "menu Edit->Global Settings->Toggle Insert Mode "
-#~ msgstr "菜单 编辑->全局设定->开/关插入模式 "
+#~ msgid "Could not load vim32.dll!"
+#~ msgstr "无法加载 vim32.dll!"
-#, fuzzy
-#~ msgid " for two modes "
-#~ msgstr " # pid 数据库名称 prepend path\n"
+#~ msgid ""
+#~ "Could not open temporary log file for writing, displaying on stderr ... "
+#~ msgstr "无法打开临时日志文件进行写入,显示在 stderr 中..."
-#, fuzzy
-#~ msgid " for Vim defaults "
-#~ msgstr " # pid 数据库名称 prepend path\n"
+#, c-format
+#~ msgid "Cscope tag: %s"
+#~ msgstr "Cscope tag: %s"
-#~ msgid "WARNING: Windows 95/98/ME detected"
-#~ msgstr "警告: 检测到 Windows 95/98/ME"
+#~ msgid "Diff with Vim"
+#~ msgstr "用 Vim 比较(diff)"
-#~ msgid "type :help windows95<Enter> for info on this"
-#~ msgstr "输入 :help windows95<Enter> 查看相关说明 "
+#~ msgid "Direction"
+#~ msgstr "方向"
+
+#~ msgid "Directories"
+#~ msgstr "目录"
+
+#~ msgid "Directory\t*.nothing\n"
+#~ msgstr "目录\t*.nothing\n"
+
+#~ msgid "Down"
+#~ msgstr "向下"
+
+#~ msgid "E106: Unknown variable: \"%s\""
+#~ msgstr "E106: 未定义的变量: \"%s\""
+
+#~ msgid "E130: Undefined function: %s"
+#~ msgstr "E130: 函数 %s 尚未定义"
+
+#~ msgid "E136: viminfo: Too many errors, skipping rest of file"
+#~ msgstr "E136: viminfo: 错误过多,忽略文件的剩余部分"
+
+#, c-format
+#~ msgid "E138: Can't write viminfo file %s!"
+#~ msgstr "E138: 无法写入 viminfo 文件 %s!"
+
+#~ msgid "E14: Invalid address"
+#~ msgstr "E14: 无效的地址"
+
+#~ msgid "E172: Only one file name allowed"
+#~ msgstr "E172: 只允许一个文件名"
+
+#~ msgid "E173: 1 more file to edit"
+#~ msgstr "E173: 还有 1 个文件未编辑"
+
+#~ msgid "E188: Obtaining window position not implemented for this platform"
+#~ msgstr "E188: 在此平台上不能获得窗口位置"
+
+#~ msgid "E195: Cannot open viminfo file for reading"
+#~ msgstr "E195: 无法打开并读取 viminfo 文件"
+
+#~ msgid "E196: No digraphs in this version"
+#~ msgstr "E196: 此版本无复合字符(digraph)"
+
+#~ msgid "E198: cmd_pchar beyond the command length"
+#~ msgstr "E198: cmd_pchar 超过命令长度"
+
+#~ msgid "E229: Cannot start the GUI"
+#~ msgstr "E229: 无法启动图形界面"
+
+#~ msgid "E230: Cannot read from \"%s\""
+#~ msgstr "E230: 无法读取文件 \"%s\""
+
+#~ msgid "E231: 'guifontwide' invalid"
+#~ msgstr "E231: 无效的 'guifontwide'"
+
+#~ msgid "E232: Cannot create BalloonEval with both message and callback"
+#~ msgstr "E232: 不能同时使用消息和回调函数来创建 BalloonEval"
+
+#~ msgid "E233: cannot open display"
+#~ msgstr "E233: 无法打开 display"
+
+#~ msgid "E234: Unknown fontset: %s"
+#~ msgstr "E234: 未知的 Fontset: %s"
+
+#~ msgid "E237: Printer selection failed"
+#~ msgstr "E237: 选择打印机失败"
+
+#~ msgid "E238: Print error: %s"
+#~ msgstr "E238: 打印错误: %s"
+
+#~ msgid "E240: No connection to Vim server"
+#~ msgstr "E240: 没有到 Vim 服务器的连接"
+
+#~ msgid "E243: Argument not supported: \"-%s\"; Use the OLE version."
+#~ msgstr "E243: 不支持的参数: \"-%s\";请使用 OLE 版本。"
+
+#~ msgid "E244: Illegal charset name \"%s\" in font name \"%s\""
+#~ msgstr "E244: 字符集 \"%s\" 不能对应字体\"%s\""
+
+#~ msgid "E245: Illegal char '%c' in font name \"%s\""
+#~ msgstr "E245: 不正确的字符 '%c' 出现在字体名称 \"%s\" 内"
+
+#~ msgid "E247: no registered server named \"%s\""
+#~ msgstr "E247: 没有名叫 \"%s\" 的已注册的服务器"
+
+#~ msgid "E248: Failed to send command to the destination program"
+#~ msgstr "E248: 无法发送命令到目的程序"
+
+#~ msgid "E249: couldn't read VIM instance registry property"
+#~ msgstr "E249: 不能读取 VIM 的 注册表属性"
+
+#~ msgid "E250: Fonts for the following charsets are missing in fontset %s:"
+#~ msgstr "E250: Fontset %s 缺少下列字符集的字体:"
+
+#~ msgid "E251: VIM instance registry property is badly formed. Deleted!"
+#~ msgstr "E251: VIM 实例注册属性有误。已删除!"
+
+#~ msgid "E252: Fontset name: %s"
+#~ msgstr "E252: Fontset 名称: %s"
+
+#~ msgid "E253: Fontset name: %s\n"
+#~ msgstr "E253: Fontset 名称: %s\n"
+
+#~ msgid "E256: Hangul automata ERROR"
+#~ msgstr "E256: Hangul automata 错误"
+
+#~ msgid "E257: cstag: tag not found"
+#~ msgstr "E257: cstag: 找不到 tag"
+
+#~ msgid "E258: Unable to send to client"
+#~ msgstr "E258: 无法发送到客户端"
+
+#, c-format
+#~ msgid "E259: no matches found for cscope query %s of %s"
+#~ msgstr "E259: cscope 查询 %s %s 没有找到匹配的结果"
+
+#~ msgid "E260: cscope connection not found"
+#~ msgstr "E260: 找不到 cscope 连接"
+
+#, c-format
+#~ msgid "E261: cscope connection %s not found"
+#~ msgstr "E261: 找不到 cscope 连接 %s"
+
+#, c-format
+#~ msgid "E262: error reading cscope connection %<PRId64>"
+#~ msgstr "E262: 读取 cscope 连接 %<PRId64> 出错"
+
+#~ msgid ""
+#~ "E263: Sorry, this command is disabled, the Python library could not be "
+#~ "loaded."
+#~ msgstr "E263: 抱歉,此命令不可用,无法加载 Python 库。"
+
+#~ msgid "E264: Python: Error initialising I/O objects"
+#~ msgstr "E264: Python: 初始化 I/O 对象出错"
+
+#~ msgid ""
+#~ "E266: Sorry, this command is disabled, the Ruby library could not be "
+#~ "loaded."
+#~ msgstr "E266: 抱歉,此命令不可用,无法加载 Ruby 库"
+
+#~ msgid "E26: Hebrew cannot be used: Not enabled at compile time\n"
+#~ msgstr "E26: 无法使用 Hebrew: 编译时没有启用\n"
+
+#~ msgid "E273: unknown longjmp status %d"
+#~ msgstr "E273: 未知的 longjmp 状态 %d"
+
+#~ msgid "E274: Sniff: Error during read. Disconnected"
+#~ msgstr "E274: Sniff: 读取错误. 取消连接"
+
+#~ msgid "E275: Unknown SNiFF+ request: %s"
+#~ msgstr "E275: 不正确的 SNiff+ 调用: %s"
+
+#~ msgid "E276: Error connecting to SNiFF+"
+#~ msgstr "E276: 连接到 SNiFF+ 失败"
+
+#~ msgid "E277: Unable to read a server reply"
+#~ msgstr "E277: 无法读取服务器响应"
+
+#~ msgid "E278: SNiFF+ not connected"
+#~ msgstr "E278: 未连接到 SNiFF+"
+
+#~ msgid "E279: Not a SNiFF+ buffer"
+#~ msgstr "E279: 不是 SNiFF+ 的缓冲区"
+
+#~ msgid "E27: Farsi cannot be used: Not enabled at compile time\n"
+#~ msgstr "E27: 无法使用 Farsi: 编译时没有启用\n"
+
+#~ msgid ""
+#~ "E280: TCL FATAL ERROR: reflist corrupt!? Please report this to vim-"
+#~ "dev@vim.org"
+#~ msgstr "E280: TCL 严重错误: reflist 损坏!?请报告给 vim-dev@vim.org"
+
+#~ msgid ""
+#~ "E281: TCL ERROR: exit code is not int!? Please report this to vim-dev@vim."
+#~ "org"
+#~ msgstr "E281: TCL 错误: 退出返回值不是整数!?请报告给 vim-dev@vim.org"
+
+#~ msgid "E285: Failed to create input context"
+#~ msgstr "E285: 无法创建输入上下文"
+
+#~ msgid "E287: Warning: Could not set destroy callback to IM"
+#~ msgstr "E287: 警告: 无法设定输入法的释放回调函数"
+
+#~ msgid "E288: input method doesn't support any style"
+#~ msgstr "E288: 输入法不支持任何风格"
+
+#~ msgid "E289: input method doesn't support my preedit type"
+#~ msgstr "E289: 输入法不支持我的预编辑类型"
+
+#~ msgid "E290: over-the-spot style requires fontset"
+#~ msgstr "E290: over-the-spot 风格需要 Fontset"
+
+#~ msgid "E291: Your GTK+ is older than 1.2.3. Status area disabled"
+#~ msgstr "E291: 你的 GTK+ 比 1.2.3 旧。状态区不可用。"
+
+#~ msgid "E292: Input Method Server is not running"
+#~ msgstr "E292: 输入法服务器未运行"
+
+#~ msgid "E338: Sorry, no file browser in console mode"
+#~ msgstr "E338: 抱歉,控制台模式下没有文件浏览器"
+
+#~ msgid "E339: Pattern too long"
+#~ msgstr "E339: 模式太长"
+
+#~ msgid "E341: Internal error: lalloc(%<PRId64>, )"
+#~ msgstr "E341: 内部错误: lalloc(%<PRId64>, )"
+
+#~ msgid "E360: Cannot execute shell with -f option"
+#~ msgstr "E360: 不能用 -f 选项执行 shell"
+
+#~ msgid "E361: Crash intercepted; regexp too complex?"
+#~ msgstr "E361: 不能执行; regular expression 太复杂?"
+
+#~ msgid "E363: pattern caused out-of-stack error"
+#~ msgstr "E363: regular expression 造成堆栈用光的错误"
#~ msgid "E370: Could not load library %s"
#~ msgstr "E370: 无法加载库 %s"
+#~ msgid "E371: Command not found"
+#~ msgstr "E371: 找不到命令"
+
+#~ msgid "E396: containedin argument not accepted here"
+#~ msgstr "E396: 使用了不正确的参数"
+
+#, c-format
+#~ msgid "E422: terminal code too long: %s"
+#~ msgstr "E422: 终端编码太长: %s"
+
+#~ msgid "E430: Tag file path truncated for %s\n"
+#~ msgstr "E430: Tag 文件路径被截断为 %s\n"
+
+#, c-format
+#~ msgid "E436: No \"%s\" entry in termcap"
+#~ msgstr "E436: termcap 中没有 \"%s\" 项"
+
+#~ msgid "E437: terminal capability \"cm\" required"
+#~ msgstr "E437: 终端需要能力 \"cm\""
+
+#~ msgid "E448: Could not load library function %s"
+#~ msgstr "E448: 无法加载库函数 %s"
+
+#~ msgid "E449: Invalid expression received"
+#~ msgstr "E449: 收到无效的表达式"
+
+#~ msgid "E460: The resource fork would be lost (add ! to override)"
+#~ msgstr "E460: Resource fork 会丢失 (请加 ! 强制执行)"
+
+#, c-format
+#~ msgid "E469: invalid cscopequickfix flag %c for %c"
+#~ msgstr "E469: cscopequickfix 标志 %c 对 %c 无效"
+
+#~ msgid "E505: "
+#~ msgstr "E505: "
+
+#~ msgid "E506: Can't write to backup file (add ! to override)"
+#~ msgstr "E506: 无法写入备份文件 (请加 ! 强制执行)"
+
+#~ msgid "E507: Close error for backup file (add ! to override)"
+#~ msgstr "E507: 关闭备份文件出错 (请加 ! 强制执行)"
+
+#~ msgid "E508: Can't read file for backup (add ! to override)"
+#~ msgstr "E508: 无法读取文件以供备份 (请加 ! 强制执行)"
+
+#~ msgid "E50: Too many \\z("
+#~ msgstr "E50: 太多 \\z("
+
+#, fuzzy, c-format
#~ msgid ""
-#~ "Sorry, this command is disabled: the Perl library could not be loaded."
-#~ msgstr "抱歉,此命令不可用: 无法加载 Perl 库。"
+#~ "E513: write error, conversion failed in line %<PRId64> (make 'fenc' empty "
+#~ "to override)"
+#~ msgstr "E513: 写入错误,转换失败 (请将 'fenc' 置空以强制执行)"
-#~ msgid "Edit with &multiple Vims"
-#~ msgstr "用多个 Vim 编辑(&M)"
+#, c-format
+#~ msgid "E51: Too many %s("
+#~ msgstr "E51: 太多 %s("
-#~ msgid "Edit with single &Vim"
-#~ msgstr "用单个 Vim 编辑(&V)"
+#~ msgid "E522: Not found in termcap"
+#~ msgstr "E522: Termcap 里面找不到"
-#~ msgid "Diff with Vim"
-#~ msgstr "用 Vim 比较(diff)"
+#~ msgid "E52: Unmatched \\z("
+#~ msgstr "E52: 不匹配的 \\z("
+
+#~ msgid "E530: Cannot change term in GUI"
+#~ msgstr "E530: 在图形界面中不能改变终端"
+
+#~ msgid "E531: Use \":gui\" to start the GUI"
+#~ msgstr "E531: 请用 \":gui\" 启动图形界面"
+
+#~ msgid "E533: can't select wide font"
+#~ msgstr "E533: 无法选择宽字体"
+
+#~ msgid "E538: No mouse support"
+#~ msgstr "E538: 不支持鼠标"
+
+#~ msgid "E541: too many items"
+#~ msgstr "E541: 项目过多"
+
+#~ msgid "E543: Not a valid codepage"
+#~ msgstr "E543: 无效的代码页"
+
+#~ msgid "E547: Illegal mouseshape"
+#~ msgstr "E547: 无效的鼠标形状"
+
+#~ msgid "E557: Cannot open termcap file"
+#~ msgstr "E557: 无法打开 termcap 文件"
+
+#~ msgid "E558: Terminal entry not found in terminfo"
+#~ msgstr "E558: 在 terminfo 中找不到终端项"
+
+#~ msgid "E559: Terminal entry not found in termcap"
+#~ msgstr "E559: 在 termcap 中找不到终端项"
+
+#, c-format
+#~ msgid "E560: Usage: cs[cope] %s"
+#~ msgstr "E560: 用法: cs[cope] %s"
+
+#~ msgid "E561: unknown cscope search type"
+#~ msgstr "E561: 未知的 cscope 查找类型"
+
+#~ msgid "E562: Usage: cstag <ident>"
+#~ msgstr "E562: 用法: cstag <ident>"
+
+#~ msgid "E563: stat error"
+#~ msgstr "E563: stat 错误"
+
+#, c-format
+#~ msgid "E563: stat(%s) error: %d"
+#~ msgstr "E563: stat(%s) 错误: %d"
+
+#, c-format
+#~ msgid "E564: %s is not a directory or a valid cscope database"
+#~ msgstr "E564: %s 不是目录或有效的 cscope 数据库"
+
+#~ msgid "E566: Could not create cscope pipes"
+#~ msgstr "E566: 无法创建 cscope 管道"
+
+#~ msgid "E567: no cscope connections"
+#~ msgstr "E567: 没有 cscope 连接"
+
+#~ msgid "E568: duplicate cscope database not added"
+#~ msgstr "E568: 重复的 cscope 数据库未被加入"
+
+#~ msgid "E569: maximum number of cscope connections reached"
+#~ msgstr "E569: 已达到 cscope 的最大连接数"
+
+#~ msgid "E570: fatal error in cs_manage_matches"
+#~ msgstr "E570: cs_manage_matches 严重错误"
+
+#~ msgid ""
+#~ "E571: Sorry, this command is disabled: the Tcl library could not be "
+#~ "loaded."
+#~ msgstr "E571: 抱歉,此命令不可用,无法加载 Tcl 库"
+
+#~ msgid "E572: exit code %d"
+#~ msgstr "E572: 退出返回值 %d"
+
+#~ msgid "E573: Invalid server id used: %s"
+#~ msgstr "E573: 使用了无效的服务器 id: %s"
+
+#, c-format
+#~ msgid "E574: Unknown register type %d"
+#~ msgstr "E574: 未知的寄存器类型 %d"
+
+#~ msgid "E596: Invalid font(s)"
+#~ msgstr "E596: 无效的字体"
+
+#~ msgid "E597: can't select fontset"
+#~ msgstr "E597: 无法选择 Fontset"
+
+#~ msgid "E599: Value of 'imactivatekey' is invalid"
+#~ msgstr "E599: 'imactivatekey' 的值无效"
+
+#, c-format
+#~ msgid "E59: invalid character after %s@"
+#~ msgstr "E59: %s@ 后面有无效的字符"
+
+#, c-format
+#~ msgid "E609: Cscope error: %s"
+#~ msgstr "E609: Cscope 错误: %s"
+
+#, c-format
+#~ msgid "E60: Too many complex %s{...}s"
+#~ msgstr "E60: 太多复杂的 %s{...}s"
+
+#~ msgid "E613: Unknown printer font: %s"
+#~ msgstr "E613: 未知的打印机字体: %s"
+
+#~ msgid "E614: vim_SelFile: can't return to current directory"
+#~ msgstr "E614: vim_SelFile: 无法返回当前目录"
+
+#~ msgid "E615: vim_SelFile: can't get current directory"
+#~ msgstr "E615: vim_SelFile: 无法获取当前目录"
+
+#~ msgid "E616: vim_SelFile: can't get font %s"
+#~ msgstr "E616: vim_SelFile: 无法获取字体 %s"
+
+#~ msgid "E617: Cannot be changed in the GTK+ 2 GUI"
+#~ msgstr "E617: 在 GTK+ 2 图形界面中不能更改"
+
+#, c-format
+#~ msgid "E61: Nested %s*"
+#~ msgstr "E61: 嵌套的 %s*"
+
+#~ msgid "E622: Could not fork for cscope"
+#~ msgstr "E622: 无法对 cscope 进行 fork"
+
+#~ msgid "E625: cannot open cscope database: %s"
+#~ msgstr "E625: 无法打开 cscope 数据库: %s"
+
+#~ msgid "E626: cannot get cscope database information"
+#~ msgstr "E626: 无法获取 cscope 数据库信息"
+
+#, 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 "E658: NetBeans connection lost for buffer %<PRId64>"
+#~ msgstr "E658: 缓冲区 %<PRId64> 丢失 NetBeans 连接"
+
+#~ msgid "E65: Illegal back reference"
+#~ msgstr "E65: 无效的回引"
+
+#~ msgid "E665: Cannot start GUI, no valid font found"
+#~ msgstr "E665: 无法启动图形界面,找不到有效的字体"
+
+#~ msgid "E668: Wrong access mode for NetBeans connection info file: \"%s\""
+#~ msgstr "E668: NetBeans 连接信息文件中错误的访问模式: \"%s\""
+
+#~ msgid "E672: Unable to open window inside MDI application"
+#~ msgstr "E672: 无法在 MDI 应用程序中打开窗口"
+
+#, c-format
+#~ msgid "E678: Invalid character after %s%%[dxouU]"
+#~ msgstr "E678: %s%%[dxouU] 后面有无效的字符"
+
+#~ msgid "E679: recursive loop loading syncolor.vim"
+#~ msgstr "E679: 加载 syncolor.vim 时出现嵌套循环"
+
+#~ msgid "E68: Invalid character after \\z"
+#~ msgstr "E68: \\z 后面有无效的字符"
+
+#~ msgid "E693: Can only compare Funcref with Funcref"
+#~ msgstr "E693: 只能比较 Funcref 和 Funcref"
+
+#, c-format
+#~ msgid "E706: Variable type mismatch for: %s"
+#~ msgstr "E706: 变量类型不匹配: %s"
+
+#~ msgid "E724: variable nested too deep for displaying"
+#~ msgstr "E724: 变量嵌套过深无法显示"
+
+#~ msgid "E729: using Funcref as a String"
+#~ msgstr "E729: 将函数当做字符串使用"
+
+#~ msgid "E744: NetBeans does not allow changes in read-only files"
+#~ msgstr "E744: NetBeans 不允许改变只读文件"
+
+#~ msgid ""
+#~ "E747: Cannot change directory, buffer is modified (add ! to override)"
+#~ msgstr "E747: 不能改变目录,缓冲区已修改 (请加 ! 强制执行)"
+
+#~ msgid "E761: Format error in affix file FOL, LOW or UPP"
+#~ msgstr "E761: 附加文件 FOL、LOW 或 UPP 中格式错误"
+
+#~ msgid "E762: Character in FOL, LOW or UPP is out of range"
+#~ msgstr "E762: FOL、LOW 或 UPP 中字符超出范围"
+
+#~ msgid "E775: Eval feature not available"
+#~ msgstr "E775: 求值功能不可用"
+
+#~ msgid "E800: Arabic cannot be used: Not enabled at compile time\n"
+#~ msgstr "E800: 无法使用 Arabic: 编译时没有启用\n"
+
+#~ msgid "E839: Completion function changed window"
+#~ msgstr "E839: 补全函数更改了窗口"
+
+#~ msgid "E865: (NFA) Regexp end encountered prematurely"
+#~ msgstr "E865: (NFA) 过早地遇到了正则表达式的结尾"
+
+#, c-format
+#~ msgid "E867: (NFA) Unknown operator '\\%%%c'"
+#~ msgstr "E867: (NFA) 未知的操作符 '\\%%%c'"
+
+#, c-format
+#~ msgid "E867: (NFA) Unknown operator '\\z%c'"
+#~ msgstr "E867: (NFA) 未知的操作符 '\\z%c'"
+
+#, c-format
+#~ msgid "E869: (NFA) Unknown operator '\\@%c'"
+#~ msgstr "E869: (NFA) 未知的操作符 '\\@%c'"
+
+#~ msgid "E870: (NFA regexp) Error reading repetition limits"
+#~ msgstr "E870: (NFA regexp) 读取重复限制时出错"
+
+#~ msgid "E871: (NFA regexp) Can't have a multi follow a multi !"
+#~ msgstr "E871: (NFA regexp) 不能多个跟多个!"
+
+#~ msgid "E872: (NFA regexp) Too many '('"
+#~ msgstr "E872: (NFA regexp) 太多 '('"
+
+#~ msgid "E873: (NFA regexp) proper termination error"
+#~ msgstr "E873: (NFA regexp) 未适当终止"
+
+#~ msgid "E874: (NFA) Could not pop the stack !"
+#~ msgstr "E874: (NFA) 无法出栈!"
+
+#~ msgid ""
+#~ "E875: (NFA regexp) (While converting from postfix to NFA), too many "
+#~ "states left on stack"
+#~ msgstr "E875: (NFA regexp) (从后缀转换到 NFA 时),栈上遗留了太多状态"
+
+#~ msgid "E876: (NFA regexp) Not enough space to store the whole NFA "
+#~ msgstr "E876: (NFA regexp) 没有足够的空间存储整个NFA "
+
+#, c-format
+#~ msgid "E877: (NFA regexp) Invalid character class: %<PRId64>"
+#~ msgstr "E877: (NFA regexp) 不可用的字符类: %<PRId64>"
+
+#, fuzzy
+#~ msgid "E879: (NFA regexp) Too many \\z("
+#~ msgstr "E50: 太多 \\z("
+
+#~ msgid "ERROR: "
+#~ msgstr "错误: "
+
+#~ msgid "Edit File"
+#~ msgstr "编辑文件"
+
+#~ msgid "Edit File in new window"
+#~ msgstr "在新窗口编辑文件"
#~ msgid "Edit with &Vim"
#~ msgstr "用 Vim 编辑(&V)"
+#~ msgid "Edit with &multiple Vims"
+#~ msgstr "用多个 Vim 编辑(&M)"
+
#~ msgid "Edit with existing Vim - "
#~ msgstr "用当前的 Vim 编辑 - "
+#~ msgid "Edit with single &Vim"
+#~ msgstr "用单个 Vim 编辑(&V)"
+
#~ msgid "Edits the selected file(s) with Vim"
#~ msgstr "用 Vim 编辑选中的文件"
+#~ msgid "Encoding:"
+#~ msgstr "编码:"
+
+#~ msgid "Enter encryption key: "
+#~ msgstr "输入密码: "
+
+#~ msgid "Enter nr of choice (<CR> to abort): "
+#~ msgstr "输入 nr 或选择 (<CR> 退出): "
+
+#~ msgid "Enter same key again: "
+#~ msgstr "请再输入一次: "
+
#~ msgid "Error creating process: Check if gvim is in your path!"
#~ msgstr "创建进程失败: 请检查 gvim 是否在路径中!"
-#~ msgid "gvimext.dll error"
-#~ msgstr "gvimext.dll 错误"
+# do not translate to avoid writing Chinese in files
+#, fuzzy
+#~ msgid "Expression"
+#~ msgstr "表达式"
+
+#~ msgid "External submatches:\n"
+#~ msgstr "外部符合:\n"
+
+#~ msgid "Files"
+#~ msgstr "文件"
+
+#~ msgid "Filter"
+#~ msgstr "过滤器"
+
+#~ msgid "Find & Replace (use '\\\\' to find a '\\')"
+#~ msgstr "查找和替换字符串 (使用 '\\\\' 来查找 '\\')"
+
+#~ msgid "Find &Next"
+#~ msgstr "查找下一个(&N)"
+
+#~ msgid "Find Next"
+#~ msgstr "查找下一个"
+
+#~ msgid "Find string (use '\\\\' to find a '\\')"
+#~ msgstr "查找字符串 (使用 '\\\\' 来查找 '\\')"
+
+#~ msgid "Find symbol"
+#~ msgstr "查找 symbol"
+
+#~ msgid "Find what:"
+#~ msgstr "查找内容:"
+
+#~ msgid "Font '%s' is not fixed-width"
+#~ msgstr "'%s' 不是固定宽度的字体"
+
+#~ msgid "Font Selection"
+#~ msgstr "选择字体"
+
+#~ msgid "Font%<PRId64> width is not twice that of font0\n"
+#~ msgstr "字体%<PRId64>的宽度不是字体0的两倍\n"
+
+#~ msgid "Font0 width: %<PRId64>\n"
+#~ msgstr "字体0的宽度:%<PRId64>\n"
+
+#~ msgid "Font0: %s\n"
+#~ msgstr "字体0: %s\n"
+
+#~ msgid ""
+#~ "Font1 width: %<PRId64>\n"
+#~ "\n"
+#~ msgstr ""
+#~ "字体1的宽度: %<PRId64>\n"
+#~ "\n"
+
+#~ msgid "Font1: %s\n"
+#~ msgstr "字体1: %s\n"
+
+#~ msgid "Font:"
+#~ msgstr "字体:"
+
+#~ msgid "Generate docu for"
+#~ msgstr "产生文件: "
+
+#~ msgid "Hit ENTER to continue"
+#~ msgstr "请按 ENTER 继续"
+
+#~ msgid "I/O ERROR"
+#~ msgstr "I/O 错误"
+
+#~ msgid "Ignoring long line in tags file"
+#~ msgstr "忽略较长的行在 tags 文件中"
+
+#~ msgid "Illegal register name"
+#~ msgstr "无效的寄存器名"
+
+#~ msgid "Illegal starting char"
+#~ msgstr "无效的启动字符"
+
+# do not translate to avoid writing Chinese in files
+#, fuzzy
+#~ msgid "Input Line"
+#~ msgstr "输入行"
+
+#~ msgid "Input _Methods"
+#~ msgstr "输入法(_M)"
+
+#~ msgid "Invalid argument for"
+#~ msgstr "无效的参数"
+
+#~ msgid "Invalid font specification"
+#~ msgstr "指定了无效的字体"
+
+#~ msgid "Keys don't match!"
+#~ msgstr "两次密码不匹配!"
+
+#~ msgid "Kill a connection"
+#~ msgstr "结束一个连接"
+
+#~ msgid "Linear tag search"
+#~ msgstr "线性查找标签 (Tags)"
+
+#~ msgid "Linking: "
+#~ msgstr "链接方式: "
+
+#~ msgid "Match case"
+#~ msgstr "匹配大小写"
+
+#~ msgid "Match whole word only"
+#~ msgstr "匹配完整的词"
+
+#~ msgid "Message"
+#~ msgstr "消息"
+
+#~ msgid "Missing '>'"
+#~ msgstr "缺少 '>'"
+
+#, c-format
+#~ msgid "Missing FOL/LOW/UPP line in %s"
+#~ msgstr "%s 中缺少 FOL/LOW/UPP 行"
+
+#~ msgid "Modified by "
+#~ msgstr "修改者 "
+
+#~ msgid "Name:"
+#~ msgstr "名称:"
+
+#~ msgid "Need %s version %<PRId64>\n"
+#~ msgstr "需要 %s 版本 %<PRId64>\n"
+
+#~ msgid "Need Amigados version 2.04 or later\n"
+#~ msgstr "需要 Amigados 版本 2.04 以上\n"
+
+#~ msgid "NetBeans disallows writes of unmodified buffers"
+#~ msgstr "NetBeans 不允许未修改的缓冲区写入"
+
+#~ msgid "New tab"
+#~ msgstr "新建标签"
+
+#~ msgid "No display: Send expression failed.\n"
+#~ msgstr "没有 display: 发送表达式失败。\n"
+
+#~ msgid "No match at cursor, finding next"
+#~ msgstr "在光标处没有匹配,查找下一个"
+
+#~ msgid "No undo possible; continue anyway"
+#~ msgstr "无法撤销;仍然继续"
+
+#~ msgid "Not Used"
+#~ msgstr "未使用"
+
+#~ msgid "Nvim: Reading from stdin...\n"
+#~ msgstr "Vim: 从标准输入读取...\n"
+
+#~ msgid "OK"
+#~ msgstr "确定"
+
+#~ msgid "Open File dialog"
+#~ msgstr "打开文件对话框"
+
+#~ msgid "Open Tab..."
+#~ msgstr "打开标签..."
+
+#~ msgid "Open tab..."
+#~ msgstr "打开标签..."
+
+#~ msgid "Opening the X display failed"
+#~ msgstr "打开 X display 失败"
+
+#~ msgid "Opening the X display timed out"
+#~ msgstr "打开 X display 超时"
+
+#~ msgid "Opening the X display took %<PRId64> msec"
+#~ msgstr "打开 X display 用时 %<PRId64> 秒"
+
+#~ msgid "Partial writes disallowed for NetBeans buffers"
+#~ msgstr "NetBeans 不允许缓冲区部分写入"
#~ msgid "Path length too long!"
#~ msgstr "路径太长!"
-#~ msgid "E234: Unknown fontset: %s"
-#~ msgstr "E234: 未知的 Fontset: %s"
+#~ msgid "Pathname:"
+#~ msgstr "路径:"
-#~ msgid "E235: Unknown font: %s"
-#~ msgstr "E235: 未知的字体: %s"
+#~ msgid "Query for a pattern"
+#~ msgstr "查询一个模式"
-#~ msgid "E236: Font \"%s\" is not fixed-width"
-#~ msgstr "E236: 字体 \"%s\" 不是等宽字体"
+#~ msgid "Reading from stdin..."
+#~ msgstr "从标准输入读取..."
-#~ msgid "E448: Could not load library function %s"
-#~ msgstr "E448: 无法加载库函数 %s"
+#~ msgid "Reinit all connections"
+#~ msgstr "重置所有连接"
-#~ msgid "E26: Hebrew cannot be used: Not enabled at compile time\n"
-#~ msgstr "E26: 无法使用 Hebrew: 编译时没有启用\n"
+#~ msgid "Replace"
+#~ msgstr "替换"
-#~ msgid "E27: Farsi cannot be used: Not enabled at compile time\n"
-#~ msgstr "E27: 无法使用 Farsi: 编译时没有启用\n"
+#~ msgid "Replace &All"
+#~ msgstr "全部替换(&A)"
-#~ msgid "E800: Arabic cannot be used: Not enabled at compile time\n"
-#~ msgstr "E800: 无法使用 Arabic: 编译时没有启用\n"
+#~ msgid "Replace All"
+#~ msgstr "全部替换"
-#~ msgid "E247: no registered server named \"%s\""
-#~ msgstr "E247: 没有名叫 \"%s\" 的已注册的服务器"
+#~ msgid "Replace with:"
+#~ msgstr "替换为:"
-#~ msgid "E233: cannot open display"
-#~ msgstr "E233: 无法打开 display"
+#~ msgid "Retrieve"
+#~ msgstr "恢复"
-#~ msgid "E449: Invalid expression received"
-#~ msgstr "E449: 收到无效的表达式"
+#~ msgid "Retrieve from all projects"
+#~ msgstr "恢复: 从所有项目"
-#~ msgid "E744: NetBeans does not allow changes in read-only files"
-#~ msgstr "E744: NetBeans 不允许改变只读文件"
+#~ msgid "Retrieve from file"
+#~ msgstr "恢复: 从文件"
-#~ msgid "Affix flags ignored when PFXPOSTPONE used in %s line %d: %s"
-#~ msgstr "%s 第 %d 行,使用 PFXPOSTPONE 时附加标志被忽略: %s"
+#~ msgid "Retrieve from project"
+#~ msgstr "恢复: 从对象"
-#~ msgid "[No file]"
-#~ msgstr "[未命名]"
+#~ msgid "Run Macro"
+#~ msgstr "执行宏"
+
+#~ msgid "Running in Vi compatible mode"
+#~ msgstr "运行于 Vi 兼容模式"
+
+#~ msgid "Running modeless, typed text is inserted"
+#~ msgstr "无模式运行,输入文字即插入"
+
+#~ msgid "SNiFF+ is currently "
+#~ msgstr "SNiFF+ 目前"
+
+#~ msgid "Save As"
+#~ msgstr "另存为"
+
+#~ msgid "Save File dialog"
+#~ msgstr "保存文件对话框"
+
+#~ msgid "Save Redirection"
+#~ msgstr "保存重定向"
+
+#~ msgid "Save Session"
+#~ msgstr "保存会话"
+
+#~ msgid "Save Setup"
+#~ msgstr "保存设定"
+
+#~ msgid "Save View"
+#~ msgstr "保存视图"
+
+#~ msgid "Scrollbar Widget: Could not get geometry of thumb pixmap."
+#~ msgstr "滚动条部件: 无法获取滑块图像的几何大小"
+
+# do not translate to avoid writing Chinese in files
+#, fuzzy
+#~ msgid "Search String"
+#~ msgstr "查找字符串"
+
+#~ msgid "Select Directory dialog"
+#~ msgstr "选择目录对话框"
+
+#~ msgid "Show base class of"
+#~ msgstr "显示 base class of:"
+
+#~ msgid "Show class in hierarchy"
+#~ msgstr "显示层次关系的类"
+
+#~ msgid "Show class in restricted hierarchy"
+#~ msgstr "显示 restricted 层次关系的 class"
+
+#~ msgid "Show connections"
+#~ msgstr "显示连接"
+
+#~ msgid "Show docu of"
+#~ msgstr "显示文件: "
+
+#~ msgid "Show overridden member function"
+#~ msgstr "显示被覆盖的成员函数"
+
+#~ msgid "Show source of"
+#~ msgstr "显示源代码: "
+
+#~ msgid "Show this message"
+#~ msgstr "显示此信息"
+
+#~ msgid "Size:"
+#~ msgstr "尺寸:"
+
+#~ msgid "Sniff: Error during write. Disconnected"
+#~ msgstr "Sniff: 写入错误。结束连接"
+
+#~ msgid ""
+#~ "Sorry, this command is disabled: the Perl library could not be loaded."
+#~ msgstr "抱歉,此命令不可用: 无法加载 Perl 库。"
+
+#~ msgid "Source Vim script"
+#~ msgstr "执行 Vim 脚本"
+
+#~ msgid "Style:"
+#~ msgstr "风格:"
+
+#, fuzzy
+#~ msgid "Substitute "
+#~ msgstr "1 次替换,"
+
+#~ msgid "Swap file already exists!"
+#~ msgstr "交换文件已存在!"
+
+#~ msgid "Tear off this menu"
+#~ msgstr "撕下此菜单"
+
+#~ msgid "Testing the X display failed"
+#~ msgstr "测试 X display 失败"
+
+#~ msgid "Thanks for flying Vim"
+#~ msgstr "感谢您选择 Vim"
+
+#~ msgid "This Vim was not compiled with the diff feature."
+#~ msgstr "此 Vim 编译时没有加入 diff 功能"
+
+#~ msgid "This cscope command does not support splitting the window.\n"
+#~ msgstr "这个 cscope 命令不支持分割窗口。\n"
+
+#~ msgid "Toggle implementation/definition"
+#~ msgstr "切换实现/定义"
+
+#, fuzzy
+#~ msgid "Unable to get option value"
+#~ msgstr "选项参数后的内容无效"
+
+#~ msgid "Unable to register a command server name"
+#~ msgstr "无法注册命令服务器名"
+
+#~ msgid "Up"
+#~ msgstr "向上"
+
+#~ msgid "Used CUT_BUFFER0 instead of empty selection"
+#~ msgstr "使用 CUT_BUFFER0 来取代空选择"
+
+#~ msgid "VIM - Search and Replace..."
+#~ msgstr "VIM - 查找和替换..."
+
+#~ msgid "VIM - Search..."
+#~ msgstr "VIM - 查找..."
+
+#~ msgid "VIM - Vi IMproved"
+#~ msgstr "VIM - Vi IMproved"
+
+#~ msgid "VIM Error"
+#~ msgstr "VIM 错误"
+
+#~ msgid "VIM: Can't open window!\n"
+#~ msgstr "VIM: 不能打开窗口!\n"
+
+#~ msgid ""
+#~ "VIMRUN.EXE not found in your $PATH.\n"
+#~ "External commands will not pause after completion.\n"
+#~ "See :help win32-vimrun for more information."
+#~ msgstr ""
+#~ "在你的 $PATH 中找不到 VIMRUN.EXE。\n"
+#~ "外部命令执行完毕后将不会暂停。\n"
+#~ "进一步说明请见 :help win32-vimrun"
+
+#~ msgid "Vim - Font Selector"
+#~ msgstr "Vim - 字体选择器"
+
+#~ msgid ""
+#~ "Vim E458: Cannot allocate colormap entry, some colors may be incorrect"
+#~ msgstr "Vim E458: 无法分配颜色表项,某些颜色可能不正确"
+
+#~ msgid "Vim Warning"
+#~ msgstr "Vim 警告"
+
+#~ msgid "Vim dialog"
+#~ msgstr "Vim 对话框"
+
+#~ msgid "Vim dialog..."
+#~ msgstr "Vim 对话框..."
+
+#~ msgid "Vim error"
+#~ msgstr "Vim 错误"
+
+#~ msgid "Vim error: ~a"
+#~ msgstr "Vim 错误: ~a"
+
+#~ msgid "Vim exiting with %d\n"
+#~ msgstr "Vim 返回值: %d\n"
+
+#~ msgid "Vim: Caught %s event\n"
+#~ msgstr "Vim: 拦截到 %s 事件\n"
+
+#~ msgid "Vim: Caught deadly signal\n"
+#~ msgstr "Vim: 拦截到致命信号(deadly signal)\n"
+
+#~ msgid "Vim: Caught deadly signal %s\n"
+#~ msgstr "Vim: 拦截到致命信号(deadly signal) %s\n"
+
+#~ msgid "Vim: Double signal, exiting\n"
+#~ msgstr "Vim: 双重信号,退出中\n"
+
+#~ msgid "Vim: Error: Failure to start gvim from NetBeans\n"
+#~ msgstr "Vim: 错误: 无法从 NetBeans 中启动 gvim\n"
+
+#~ msgid "Vim: Finished.\n"
+#~ msgstr "Vim: 结束。\n"
+
+#~ msgid "Vim: Main window unexpectedly destroyed\n"
+#~ msgstr "Vim: 主窗口被意外地摧毁\n"
+
+#~ msgid "Vim: Received \"die\" request from session manager\n"
+#~ msgstr "Vim: 从会话管理器收到 \"die\" 请求\n"
+
+#~ msgid "Vim: Warning: Input is not from a terminal\n"
+#~ msgstr "Vim: 警告: 输入不是来自终端(键盘)\n"
+
+#~ msgid "Vim: Warning: Output is not to a terminal\n"
+#~ msgstr "Vim: 警告: 输出不是到终端(屏幕)\n"
+
+#~ msgid "Vim: preserving files...\n"
+#~ msgstr "Vim: 正在保留文件……\n"
+
+#~ msgid "WARNING: Windows 95/98/ME detected"
+#~ msgstr "警告: 检测到 Windows 95/98/ME"
+
+#~ msgid "Warning: terminal cannot highlight"
+#~ msgstr "警告: 你的终端不能显示高亮"
+
+#~ msgid "Window position: X %d, Y %d"
+#~ msgstr "窗口位置: X %d, Y %d"
+
+#~ msgid "XSMP ICE connection watch failed"
+#~ msgstr "XSMP ICE 连接监视失败"
+
+#~ msgid "XSMP handling save-yourself request"
+#~ msgstr "XSMP 处理 save-yourself 请求"
+
+#~ msgid "XSMP lost ICE connection"
+#~ msgstr "XSMP 丢失了到 ICE 的连接"
+
+#~ msgid "XSMP opening connection"
+#~ msgstr "XSMP 打开连接"
+
+#~ msgid "Xref has a"
+#~ msgstr "Xref 有"
+
+#~ msgid "Xref referred by"
+#~ msgstr "Xref 被谁参考:"
+
+#~ msgid "Xref refers to"
+#~ msgstr "Xref 参考到"
+
+#~ msgid "Xref used by"
+#~ msgstr "Xref 被谁使用:"
+
+#~ msgid "Zero count"
+#~ msgstr "计数为零"
#~ msgid "[Error List]"
#~ msgstr "[错误列表]"
-#~ msgid "E106: Unknown variable: \"%s\""
-#~ msgstr "E106: 未定义的变量: \"%s\""
+#~ msgid "[NL found]"
+#~ msgstr "[找到 NL]"
-#~ msgid "function "
-#~ msgstr "函数 "
+#~ msgid "[New file]"
+#~ msgstr "[新文件]"
-#~ msgid "E130: Undefined function: %s"
-#~ msgstr "E130: 函数 %s 尚未定义"
+#~ msgid "[No file]"
+#~ msgstr "[未命名]"
-#~ msgid "Run Macro"
-#~ msgstr "执行宏"
+#~ msgid ""
+#~ "[calls] total re/malloc()'s %<PRIu64>, total free()'s %<PRIu64>\n"
+#~ "\n"
+#~ msgstr ""
+#~ "[调用] 总共 re/malloc(): %<PRIu64>,总共 free()': %<PRIu64>\n"
+#~ "\n"
-#~ msgid "E242: Color name not recognized: %s"
-#~ msgstr "E242: %s 为不能识别的颜色名称"
+#~ msgid "[crypted]"
+#~ msgstr "[已加密]"
-#~ msgid "error reading cscope connection %d"
-#~ msgstr "读取 cscope 连接 %d 时错误"
+#~ msgid "[fifo/socket]"
+#~ msgstr "[fifo/socket]"
-#~ msgid "E260: cscope connection not found"
-#~ msgstr "E260: 找不到 cscope 连接"
+#~ msgid "[string too long]"
+#~ msgstr "[字符串太长]"
-#~ msgid "cscope connection closed"
-#~ msgstr "cscope 连接已关闭"
+#~ msgid "attempt to refer to deleted buffer"
+#~ msgstr "试图引用已被删除的缓冲区"
+
+#~ msgid "attempt to refer to deleted window"
+#~ msgstr "试图引用已被删除的窗口"
+
+#~ msgid "block of 1 line yanked"
+#~ msgstr "复制了 1 行的块"
+
+#~ msgid "buffer is invalid"
+#~ msgstr "缓冲区无效"
+
+#~ msgid "by Bram Moolenaar et al."
+#~ msgstr "维护人 Bram Moolenaar 等"
+
+#~ msgid "can't delete OutputObject attributes"
+#~ msgstr "不能删除 OutputObject 属性"
+
+#~ msgid "cannot change console mode ?!\n"
+#~ msgstr "不能切换主控台(console)模式 !?\n"
+
+#~ msgid "cannot create buffer/window command: object is being deleted"
+#~ msgstr "无法创建缓冲区/窗口命令: 对象将被删除"
+
+#~ msgid "cannot delete line"
+#~ msgstr "无法删除行"
+
+#~ msgid "cannot insert line"
+#~ msgstr "无法插入行"
+
+#~ msgid "cannot insert/append line"
+#~ msgstr "无法插入/追加行"
+
+#~ msgid "cannot open "
+#~ msgstr "不能打开"
+
+#~ msgid ""
+#~ "cannot register callback command: buffer/window is already being deleted"
+#~ msgstr "无法注册回调命令: 缓冲区/窗口已被删除"
+
+#~ msgid "cannot register callback command: buffer/window reference not found"
+#~ msgstr "无法注册回调命令: 找不到缓冲区/窗口引用"
+
+#~ msgid "cannot replace line"
+#~ msgstr "无法替换行"
+
+#~ msgid "cannot set line(s)"
+#~ msgstr "无法设定行"
+
+#~ msgid "cannot yank; delete anyway"
+#~ msgstr "无法复制;改为删除"
+
+#~ msgid "close"
+#~ msgstr "关闭"
+
+#~ msgid "connected"
+#~ msgstr "连接中"
#~ msgid "couldn't malloc\n"
#~ msgstr "不能使用 malloc\n"
-#~ msgid "%2d %-5ld %-34s <none>\n"
-#~ msgstr "%2d %-5ld %-34s <无>\n"
+#~ msgid "couldn't open buffer"
+#~ msgstr "无法打开缓冲区"
-#~ msgid "E249: couldn't read VIM instance registry property"
-#~ msgstr "E249: 不能读取 VIM 的 注册表属性"
+#~ msgid "cs_create_connection exec failed"
+#~ msgstr "cs_create_connection 执行失败"
-#~ msgid "\"\n"
-#~ msgstr "\"\n"
+#, fuzzy
+#~ msgid "cs_create_connection setpgid failed"
+#~ msgstr "cs_create_connection 执行失败"
-#~ msgid "--help\t\tShow Gnome arguments"
-#~ msgstr "--help\t\t显示 Gnome 相关参数"
+#~ msgid "cs_create_connection: fdopen for fr_fp failed"
+#~ msgstr "cs_create_connection: fdopen fr_fp 失败"
-#~ msgid "[string too long]"
-#~ msgstr "[字符串太长]"
+#~ msgid "cs_create_connection: fdopen for to_fp failed"
+#~ msgstr "cs_create_connection: fdopen to_fp 失败"
-#~ msgid "Hit ENTER to continue"
-#~ msgstr "请按 ENTER 继续"
+#~ msgid "cscope commands:\n"
+#~ msgstr "cscope 命令:\n"
-#~ msgid " (RET/BS: line, SPACE/b: page, d/u: half page, q: quit)"
-#~ msgstr " (RET/BS: 向下/向上一行, 空格/b: 一页, d/u: 半页, q: 退出)"
+#, c-format
+#~ msgid "cscope connection %s closed"
+#~ msgstr "cscope 连接 %s 已关闭"
-#~ msgid " (RET: line, SPACE: page, d: half page, q: quit)"
-#~ msgstr " (RET: 向下一行, 空白键: 一页, d: 半页, q: 退出)"
+#~ msgid "cursor position outside buffer"
+#~ msgstr "光标位置在缓冲区外"
-#~ msgid "E361: Crash intercepted; regexp too complex?"
-#~ msgstr "E361: 不能执行; regular expression 太复杂?"
+#~ msgid "defaulting to '"
+#~ msgstr "默认值为: '"
-#~ msgid "E363: pattern caused out-of-stack error"
-#~ msgstr "E363: regular expression 造成堆栈用光的错误"
+#~ msgid "error reading cscope connection %d"
+#~ msgstr "读取 cscope 连接 %d 时错误"
-#~ msgid " BLOCK"
-#~ msgstr " 块"
+#~ msgid "filename / context / line\n"
+#~ msgstr "文件名 / 上下文 / 行\n"
-#~ msgid " LINE"
-#~ msgstr " 行"
+#~ msgid "freeing %<PRId64> lines"
+#~ msgstr "释放了 %<PRId64> 行"
-#~ msgid "Enter nr of choice (<CR> to abort): "
-#~ msgstr "输入 nr 或选择 (<CR> 退出): "
+#~ msgid "gvimext.dll error"
+#~ msgstr "gvimext.dll 错误"
-#~ msgid "Linear tag search"
-#~ msgstr "线性查找标签 (Tags)"
+#~ msgid "hidden option"
+#~ msgstr "隐藏的选项"
-#~ msgid "Binary tag search"
-#~ msgstr "二进制查找(Binary search) 标签(Tags)"
+#~ msgid "internal error: unknown option type"
+#~ msgstr "内部错误:未知的选项类型"
+
+#~ msgid "invalid attribute"
+#~ msgstr "无效的属性"
+
+#~ msgid "invalid buffer number"
+#~ msgstr "无效的缓冲区号"
+
+#~ msgid "invalid expression"
+#~ msgstr "无效的表达式"
+
+#~ msgid "invalid mark name"
+#~ msgstr "无效的标记名称"
+
+#~ msgid "keyboard interrupt"
+#~ msgstr "键盘中断"
+
+#~ msgid "linenr out of range"
+#~ msgstr "行号超出范围"
+
+#~ msgid "logoff"
+#~ msgstr "注消"
+
+#~ msgid "mark not set"
+#~ msgstr "没有设定标记"
+
+#~ msgid "mch_get_shellsize: not a console??\n"
+#~ msgstr "mch_get_shellsize: 不是主控台(console)??\n"
+
+#~ msgid "menu Edit->Global Settings->Toggle Insert Mode "
+#~ msgstr "菜单 编辑->全局设定->开/关插入模式 "
+
+#~ msgid "menu Help->Orphans for information "
+#~ msgstr "菜单 帮助->孤儿 查看说明 "
+
+#~ msgid "new shell started\n"
+#~ msgstr "启动新 shell\n"
+
+#~ msgid "no cscope connections\n"
+#~ msgstr "没有 cscope 连接\n"
+
+#~ msgid "no specific match"
+#~ msgstr "找不到匹配的项"
+
+#~ msgid "no such buffer"
+#~ msgstr "无此缓冲区"
+
+#~ msgid "no such window"
+#~ msgstr "无此窗口"
+
+#~ msgid "not "
+#~ msgstr "未"
+
+#~ msgid "not allowed in the Vim sandbox"
+#~ msgstr "不允许在 sandbox 中使用"
+
+#~ msgid "not implemented yet"
+#~ msgstr "尚未实现"
+
+#~ msgid "number changes time"
+#~ msgstr " 编号 改变 时间"
+
+#~ msgid "read from Netbeans socket"
+#~ msgstr "从 Netbeans 套接字读取"
+
+#~ msgid "readonly attribute"
+#~ msgstr "只读属性"
+
+#~ msgid "row %d column %d"
+#~ msgstr "第 %d 行 第 %d 列"
+
+#~ msgid "shell "
+#~ msgstr "shell "
+
+#~ msgid "shell returned %d"
+#~ msgstr "Shell 返回 %d"
+
+#~ msgid "shutdown"
+#~ msgstr "关机"
+
+#~ msgid "softspace must be an integer"
+#~ msgstr "softspace 必须是整数"
+
+#~ msgid "string cannot contain newlines"
+#~ msgstr "字符串不能包含换行(NL)"
+
+#~ msgid "to %s on %s"
+#~ msgstr "从 %s 到 %s"
+
+#~ msgid "type :help cp-default<Enter> for info on this"
+#~ msgstr "输入 :help cp-default<Enter> 查看相关说明 "
+
+#~ msgid "type :help windows95<Enter> for info on this"
+#~ msgstr "输入 :help windows95<Enter> 查看相关说明 "
+
+#~ msgid "type :help<Enter> or <F1> for on-line help"
+#~ msgstr "输入 :help<Enter> 或 <F1> 查看在线帮助 "
+
+#~ msgid "type :set nocp<Enter> for Vim defaults"
+#~ msgstr "输入 :set nocp<Enter> 恢复默认的 Vim "
+
+#~ msgid "unknown flag: "
+#~ msgstr "未知的标志: "
+
+#~ msgid "unknown option"
+#~ msgstr "未知的选项"
+
+#~ msgid "unknown vimOption"
+#~ msgstr "未知的 vim 选项"
+
+#~ msgid "version "
+#~ msgstr "版本 "
+
+#~ msgid "vim error"
+#~ msgstr "vim 错误"
+
+#~ msgid "window index is out of range"
+#~ msgstr "窗口索引超出范围"
+
+#~ msgid "window is invalid"
+#~ msgstr "窗口无效"
+
+#~ msgid "with (classic) GUI."
+#~ msgstr "带(传统)图形界面。"
#~ msgid "with BeOS GUI."
#~ msgstr "使用 BeOS 图形界面。"
+
+#~ msgid "with Carbon GUI."
+#~ msgstr "带 Carbon 图形界面。"
+
+#~ msgid "with Cocoa GUI."
+#~ msgstr "带 Cocoa 图形界面。"
+
+#~ msgid "with GTK GUI."
+#~ msgstr "带 GTK 图形界面。"
+
+#~ msgid "with GTK-GNOME GUI."
+#~ msgstr "带 GTK-GNOME 图形界面。"
+
+#~ msgid "with GTK2 GUI."
+#~ msgstr "带 GTK2 图形界面。"
+
+#~ msgid "with GTK2-GNOME GUI."
+#~ msgstr "带 GTK2-GNOME 图形界面。"
+
+#~ msgid "with GUI."
+#~ msgstr "带图形界面。"
+
+#~ msgid "with Photon GUI."
+#~ msgstr "带 Photon 图形界面。"
+
+#~ msgid "with X11-Athena GUI."
+#~ msgstr "带 X11-Athena 图形界面。"
+
+#~ msgid "with X11-Motif GUI."
+#~ msgstr "带 X11-Motif 图形界面。"
+
+#~ msgid "with X11-neXtaw GUI."
+#~ msgstr "带 X11-neXtaw 图形界面。"
+
+#~ msgid "without GUI."
+#~ msgstr "无图形界面。"
+
+#~ msgid "writelines() requires list of strings"
+#~ msgstr "writelines() 需要字符串列表作参数"
diff --git a/src/nvim/po/zh_TW.UTF-8.po b/src/nvim/po/zh_TW.UTF-8.po
index e2fb2d39d4..cba95e2af2 100644
--- a/src/nvim/po/zh_TW.UTF-8.po
+++ b/src/nvim/po/zh_TW.UTF-8.po
@@ -59,19 +59,19 @@ msgstr "無法傳送回應訊息"
#: ../api/private/helpers.c:204
msgid "internal error: unknown option type"
-msgstr ""
+msgstr "內部錯誤: 未知的選項類型"
#: ../buffer.c:92
msgid "[Location List]"
-msgstr ""
+msgstr "[Location 列表]"
#: ../buffer.c:93
msgid "[Quickfix List]"
-msgstr ""
+msgstr "[Quickfix 列表]"
#: ../buffer.c:94
msgid "E855: Autocommands caused command to abort"
-msgstr ""
+msgstr "E855: 自動命令導致命令被停止"
#: ../buffer.c:135
msgid "E82: Cannot allocate any buffer, exiting..."
@@ -349,7 +349,7 @@ msgstr "E103: 緩衝區 \"%s\" 不是在 diff 模式"
#: ../diff.c:2193
msgid "E787: Buffer changed unexpectedly"
-msgstr ""
+msgstr "E787: 意外地改變了緩衝區"
#: ../digraph.c:1598
msgid "E104: Escape not allowed in digraph"
@@ -365,7 +365,7 @@ msgstr "E105: 使用 :loadkeymap "
#: ../digraph.c:1821
msgid "E791: Empty keymap entry"
-msgstr ""
+msgstr "E791: 空的鍵位映射項"
#: ../edit.c:82
msgid " Keyword completion (^N^P)"
@@ -434,11 +434,11 @@ msgstr "已到段落結尾"
#: ../edit.c:101
msgid "E839: Completion function changed window"
-msgstr ""
+msgstr "E839: 補全函式更改了窗口"
#: ../edit.c:102
msgid "E840: Completion function deleted text"
-msgstr ""
+msgstr "E840: 補全函式刪除了文本"
#: ../edit.c:1847
msgid "'dictionary' option is empty"
@@ -556,7 +556,7 @@ msgstr "E118: 函式 %s 的引數過多"
#: ../eval.c:148
#, c-format
msgid "E716: Key not present in Dictionary: %s"
-msgstr ""
+msgstr "E716: 鍵在字典中不存在: %s"
#: ../eval.c:150
#, c-format
@@ -581,7 +581,7 @@ msgstr "E360: 不能用 -f 選項執行 shell"
#: ../eval.c:154
#, c-format
msgid "E734: Wrong variable type for %s="
-msgstr ""
+msgstr "E734: 錯誤的變數類型: %s="
#: ../eval.c:155
#, fuzzy, c-format
@@ -595,19 +595,19 @@ msgstr "E461: 不合法的變數名稱: %s"
#: ../eval.c:157
msgid "E806: using Float as a String"
-msgstr ""
+msgstr "E806: 使用浮點數作為字串"
#: ../eval.c:1830
msgid "E687: Less targets than List items"
-msgstr ""
+msgstr "E687: 目標比列表項數少"
#: ../eval.c:1834
msgid "E688: More targets than List items"
-msgstr ""
+msgstr "E688: 目標比列表項數多"
#: ../eval.c:1906
msgid "Double ; in list of variables"
-msgstr ""
+msgstr "變數列表出現兩個 ;"
#: ../eval.c:2078
#, fuzzy, c-format
@@ -616,23 +616,23 @@ msgstr "E138: 無法寫入 viminfo 檔案 %s !"
#: ../eval.c:2391
msgid "E689: Can only index a List or Dictionary"
-msgstr ""
+msgstr "E689: 只能索引一個列表或者字典"
#: ../eval.c:2396
msgid "E708: [:] must come last"
-msgstr ""
+msgstr "E708: [:] 必須在最後"
#: ../eval.c:2439
msgid "E709: [:] requires a List value"
-msgstr ""
+msgstr "E709: [:] 需要一個列表值"
#: ../eval.c:2674
msgid "E710: List value has more items than target"
-msgstr ""
+msgstr "E710: 列表值的項比目標多"
#: ../eval.c:2678
msgid "E711: List value has not enough items"
-msgstr ""
+msgstr "E711: 列表值沒有足夠多的項"
#: ../eval.c:2867
#, fuzzy
@@ -651,7 +651,7 @@ msgstr "E108: 無此變數: \"%s\""
#: ../eval.c:3333
msgid "E743: variable nested too deep for (un)lock"
-msgstr ""
+msgstr "E743: (un)lock 的變數嵌套過深"
#: ../eval.c:3630
msgid "E109: Missing ':' after '?'"
@@ -659,7 +659,7 @@ msgstr "E109: '?' 後缺少 ':'"
#: ../eval.c:3893
msgid "E691: Can only compare List with List"
-msgstr ""
+msgstr "E691: 只能比較列表和列表"
#: ../eval.c:3895
#, fuzzy
@@ -668,7 +668,7 @@ msgstr "E449: 收到不正確的運算式"
#: ../eval.c:3915
msgid "E735: Can only compare Dictionary with Dictionary"
-msgstr ""
+msgstr "E735: 只能比較字典和字典"
#: ../eval.c:3917
#, fuzzy
@@ -677,7 +677,7 @@ msgstr "E116: 函式 %s 的引數不正確"
#: ../eval.c:3932
msgid "E693: Can only compare Funcref with Funcref"
-msgstr ""
+msgstr "E693: 只能比較Funcref 和 Funcref"
#: ../eval.c:3934
#, fuzzy
@@ -736,7 +736,7 @@ msgstr "E242: 找不到顏色: %s"
#: ../eval.c:6499
#, c-format
msgid "E721: Duplicate key in Dictionary: \"%s\""
-msgstr ""
+msgstr "E721: Dictionary 中出現重複的鍵: \"%s\""
#: ../eval.c:6517
#, fuzzy, c-format
@@ -781,7 +781,7 @@ msgstr "E120: <SID> 不能在 script 本文外使用: %s"
#: ../eval.c:7391
#, c-format
msgid "E725: Calling dict function without Dictionary: %s"
-msgstr ""
+msgstr "E725: 調用字典函式但是沒有字典: %s"
#: ../eval.c:7453
#, fuzzy
@@ -814,16 +814,15 @@ msgstr "E227: %s 的 mapping 已經存在"
#: ../eval.c:8692
msgid "extend() argument"
-msgstr ""
+msgstr "extend() 參數"
#: ../eval.c:8915
-#, fuzzy
msgid "map() argument"
-msgstr "vim [參數] "
+msgstr "map() 參數"
#: ../eval.c:8916
msgid "filter() argument"
-msgstr ""
+msgstr "filter() 參數"
#: ../eval.c:9229
#, c-format
@@ -857,19 +856,19 @@ msgstr "E596: 不正確的字型"
#: ../eval.c:11980
msgid "E726: Stride is zero"
-msgstr ""
+msgstr "E726: 步長為零"
#: ../eval.c:11982
msgid "E727: Start past end"
-msgstr ""
+msgstr "E727: 起始值在終止值後"
#: ../eval.c:12024 ../eval.c:15297
msgid "<empty>"
-msgstr ""
+msgstr "<空>"
#: ../eval.c:12282
msgid "remove() argument"
-msgstr ""
+msgstr "remove() 參數"
#: ../eval.c:12466
msgid "E655: Too many symbolic links (cycle?)"
@@ -877,11 +876,11 @@ msgstr "E655: 太多層的符號鏈結(symlink) (循環?)"
#: ../eval.c:12593
msgid "reverse() argument"
-msgstr ""
+msgstr "reverse() 參數"
#: ../eval.c:13721
msgid "sort() argument"
-msgstr ""
+msgstr "sort() 參數"
#: ../eval.c:13721
#, fuzzy
@@ -895,7 +894,7 @@ msgstr "E237: 無法選擇此印表機"
#: ../eval.c:13806
msgid "E882: Uniq compare function failed"
-msgstr ""
+msgstr "E882: Uniq 比較函式失敗"
#: ../eval.c:14085
msgid "(Invalid)"
@@ -908,32 +907,31 @@ msgstr "E208: 寫入檔案 \"%s\" 錯誤"
#: ../eval.c:16159
msgid "E805: Using a Float as a Number"
-msgstr ""
+msgstr "E805: 將浮點數當做數字使用"
#: ../eval.c:16162
msgid "E703: Using a Funcref as a Number"
-msgstr ""
+msgstr "E703: 將函式當做數字使用"
#: ../eval.c:16170
msgid "E745: Using a List as a Number"
-msgstr ""
+msgstr "E745: 將列表當做數字使用"
#: ../eval.c:16173
msgid "E728: Using a Dictionary as a Number"
-msgstr ""
+msgstr "E728: 將字典當做數字使用"
#: ../eval.c:16259
msgid "E729: using Funcref as a String"
-msgstr ""
+msgstr "E729: 將函式當做字串使用"
#: ../eval.c:16262
-#, fuzzy
msgid "E730: using List as a String"
-msgstr "E374: 格式化字串裡少了 ]"
+msgstr "E730: 將列表當做字串使用"
#: ../eval.c:16265
msgid "E731: using Dictionary as a String"
-msgstr ""
+msgstr "E731: 將字典當做字串使用"
#: ../eval.c:16619
#, fuzzy, c-format
@@ -953,12 +951,12 @@ msgstr "E128: 函式名稱第一個字母必須大寫: %s"
#: ../eval.c:16732
#, c-format
msgid "E705: Variable name conflicts with existing function: %s"
-msgstr ""
+msgstr "E705: 變數名與已有函式名衝突: %s"
#: ../eval.c:16763
#, c-format
msgid "E741: Value is locked: %s"
-msgstr ""
+msgstr "E741: 值已鎖定: %s"
#: ../eval.c:16764 ../eval.c:16769 ../message.c:1839
msgid "Unknown"
@@ -971,7 +969,7 @@ msgstr "E284: 不能設定 IC 數值"
#: ../eval.c:16838
msgid "E698: variable nested too deep for making a copy"
-msgstr ""
+msgstr "E698: 變數嵌套過深無法複製"
#: ../eval.c:17249
#, c-format
@@ -1216,7 +1214,7 @@ msgstr "要覆寫已存在的檔案 \"%.*s\"?"
#: ../ex_cmds.c:2317
#, c-format
msgid "Swap file \"%s\" exists, overwrite anyway?"
-msgstr ""
+msgstr "交換文件 \"%s\" 已存在,確實需要覆蓋嗎?"
#: ../ex_cmds.c:2326
#, fuzzy, c-format
@@ -1456,7 +1454,7 @@ msgstr "%3d %s %s 第 %<PRId64> 行 "
#: ../ex_cmds2.c:942
msgid "E750: First use \":profile start {fname}\""
-msgstr ""
+msgstr "E750: 請先使用 :profile start <fname>"
#: ../ex_cmds2.c:1269
#, fuzzy, c-format
@@ -1555,7 +1553,7 @@ msgstr "vim [參數] "
#: ../ex_cmds2.c:2771
msgid "environment variable"
-msgstr ""
+msgstr "環境變數"
#: ../ex_cmds2.c:2773
#, fuzzy
@@ -2047,7 +2045,7 @@ msgstr "E199: 已刪除掉作用中的視窗或暫存區"
#: ../file_search.c:203
msgid "E854: path too long for completion"
-msgstr ""
+msgstr "E854: 補全用的路徑太長了"
#: ../file_search.c:446
#, c-format
@@ -2099,11 +2097,11 @@ msgstr "[未命名]"
#: ../fileio.c:511
msgid "[New DIRECTORY]"
-msgstr ""
+msgstr "[新目錄]"
#: ../fileio.c:529 ../fileio.c:532
msgid "[File too big]"
-msgstr ""
+msgstr "[文件太大]"
#: ../fileio.c:534
msgid "[Permission Denied]"
@@ -2265,7 +2263,7 @@ msgstr "E513: 無法寫入 -- 轉換失敗"
msgid ""
"E513: write error, conversion failed in line %<PRId64> (make 'fenc' empty to "
"override)"
-msgstr ""
+msgstr "E513: 寫入錯誤,轉換失敗 (請將 'fenc' 置空以強制執行)"
#: ../fileio.c:3448
msgid "E514: write error (file system full?)"
@@ -2720,7 +2718,7 @@ msgstr "E49: 錯誤的捲動大小"
#: ../globals.h:1021
msgid "E901: Job table is full"
-msgstr ""
+msgstr "E901: 任務表已經滿"
#: ../globals.h:1024
#, c-format
@@ -2877,7 +2875,7 @@ msgstr "E42: 沒有錯誤"
#: ../globals.h:1067
msgid "E776: No location list"
-msgstr ""
+msgstr "E776: 沒有位置列表"
#: ../globals.h:1068
msgid "E43: Damaged match string"
@@ -2992,7 +2990,7 @@ msgstr "E473: 內部錯誤"
#: ../globals.h:1104
msgid "E363: pattern uses more memory than 'maxmempattern'"
-msgstr ""
+msgstr "E363: 表達式的內存超出 'maxmempattern'"
#: ../globals.h:1105
#, fuzzy
@@ -3026,127 +3024,6 @@ msgstr "已搜尋到檔案開頭;再從結尾繼續搜尋"
msgid "search hit BOTTOM, continuing at TOP"
msgstr "已搜尋到檔案結尾;再從開頭繼續搜尋"
-#: ../hardcopy.c:240
-msgid "E550: Missing colon"
-msgstr "E550: 缺少 colon"
-
-#: ../hardcopy.c:252
-msgid "E551: Illegal component"
-msgstr "E551: 不正確的模式"
-
-#: ../hardcopy.c:259
-msgid "E552: digit expected"
-msgstr "E552: 應該要有數字"
-
-#: ../hardcopy.c:473
-#, c-format
-msgid "Page %d"
-msgstr "第 %d 頁"
-
-#: ../hardcopy.c:597
-msgid "No text to be printed"
-msgstr "沒有要列印的文字"
-
-#: ../hardcopy.c:668
-#, c-format
-msgid "Printing page %d (%d%%)"
-msgstr "列印中: 第 %d 頁 (%d%%)"
-
-#: ../hardcopy.c:680
-#, c-format
-msgid " Copy %d of %d"
-msgstr "複製 %d / %d"
-
-#: ../hardcopy.c:733
-#, c-format
-msgid "Printed: %s"
-msgstr "已列印: %s"
-
-#: ../hardcopy.c:740
-msgid "Printing aborted"
-msgstr "已取消列印"
-
-#: ../hardcopy.c:1365
-msgid "E455: Error writing to PostScript output file"
-msgstr "E455: 無法寫入 PostScript 輸出檔"
-
-#: ../hardcopy.c:1747
-#, c-format
-msgid "E624: Can't open file \"%s\""
-msgstr "E624: 無法開啟檔案 \"%s\""
-
-#: ../hardcopy.c:1756 ../hardcopy.c:2470
-#, c-format
-msgid "E457: Can't read PostScript resource file \"%s\""
-msgstr "E457: 無法讀取 PostScript 資源檔 \"%s\""
-
-#: ../hardcopy.c:1772
-#, c-format
-msgid "E618: file \"%s\" is not a PostScript resource file"
-msgstr "E618: 檔案 \"%s\" 不是 PostScript 資源檔 "
-
-#: ../hardcopy.c:1788 ../hardcopy.c:1805 ../hardcopy.c:1844
-#, c-format
-msgid "E619: file \"%s\" is not a supported PostScript resource file"
-msgstr "E619: 不支援 PostScript 資源檔 \"%s\""
-
-#: ../hardcopy.c:1856
-#, c-format
-msgid "E621: \"%s\" resource file has wrong version"
-msgstr "E621: \"%s\" 資源檔版本錯誤"
-
-#: ../hardcopy.c:2225
-msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr ""
-
-#: ../hardcopy.c:2238
-msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr ""
-
-#: ../hardcopy.c:2254
-msgid "E675: No default font specified for multi-byte printing."
-msgstr ""
-
-#: ../hardcopy.c:2426
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: 無法開啟 PostScript 輸出檔"
-
-#: ../hardcopy.c:2458
-#, c-format
-msgid "E456: Can't open file \"%s\""
-msgstr "E456: 無法開啟檔案 \"%s\""
-
-#: ../hardcopy.c:2583
-msgid "E456: Can't find PostScript resource file \"prolog.ps\""
-msgstr "E456: 無法讀取 PostScript 資源檔 \"prolog.ps\""
-
-#: ../hardcopy.c:2593
-#, fuzzy
-msgid "E456: Can't find PostScript resource file \"cidfont.ps\""
-msgstr "E456: 無法讀取 PostScript 資源檔 \"%s.ps\""
-
-#: ../hardcopy.c:2622 ../hardcopy.c:2639 ../hardcopy.c:2665
-#, c-format
-msgid "E456: Can't find PostScript resource file \"%s.ps\""
-msgstr "E456: 無法讀取 PostScript 資源檔 \"%s.ps\""
-
-#: ../hardcopy.c:2654
-#, fuzzy, c-format
-msgid "E620: Unable to convert to print encoding \"%s\""
-msgstr "E620:無法轉換至 \"%s\" 字元編碼"
-
-#: ../hardcopy.c:2877
-msgid "Sending to printer..."
-msgstr "傳送資料到印表機..."
-
-#: ../hardcopy.c:2881
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: 無法列印 PostScript 檔案"
-
-#: ../hardcopy.c:2883
-msgid "Print job sent."
-msgstr "已送出列印工作。"
-
#: ../if_cscope.c:85
msgid "Add a new database"
msgstr "新增資料庫"
@@ -3267,6 +3144,7 @@ msgstr "%-5s: %-30s (用法: %s)"
#: ../if_cscope.c:1155
msgid ""
"\n"
+" a: Find assignments to this symbol\n"
" c: Find functions calling this function\n"
" d: Find functions called by this function\n"
" e: Find this egrep pattern\n"
@@ -3276,6 +3154,16 @@ msgid ""
" s: Find this C symbol\n"
" t: Find this text string\n"
msgstr ""
+"\n"
+" a: 搜索對此符號的賦值\n"
+" c: 搜索調用此函式的函式\n"
+" d: 搜索此函式調用的函式\n"
+" e: 搜索此 egrep 模式\n"
+" f: 搜索此文件\n"
+" g: 搜索此定義\n"
+" i: 搜索包含此文件的文件\n"
+" s: 搜索此 C 符号\n"
+" t: 搜索此文本字串\n"
#: ../if_cscope.c:1226
msgid "E568: duplicate cscope database not added"
@@ -3509,7 +3397,7 @@ msgstr "-N\t\t\t'nocompatible' 不完全與傳統 Vi 相容,可使用 Vim 加
#: ../main.c:2215
msgid "-V[N][fname]\t\tBe verbose [level N] [log messages to fname]"
-msgstr ""
+msgstr "-V[N][fname]\t\t詳細 [level N] [log messages to fname]"
#: ../main.c:2216
msgid "-D\t\t\tDebugging mode"
@@ -3602,7 +3490,7 @@ msgstr "-W <scriptout>\t對檔案 <scriptout> 寫入所有輸入的命令"
#: ../main.c:2240
msgid "--startuptime <file>\tWrite startup timing messages to <file>"
-msgstr ""
+msgstr "--startuptime <file>\t將啟動時間寫入到文件 <file>"
#: ../main.c:2242
msgid "-i <viminfo>\t\tUse <viminfo> instead of .viminfo"
@@ -3794,7 +3682,7 @@ msgstr ""
#: ../memline.c:945
msgid " has been damaged (page size is smaller than minimum value).\n"
-msgstr ""
+msgstr "已损坏(頁面大小小於最小值)。\n"
#: ../memline.c:974
#, c-format
@@ -4082,7 +3970,7 @@ msgstr "E317: 指標區塊 id 錯 2"
#: ../memline.c:3070
#, c-format
msgid "E773: Symlink loop for \"%s\""
-msgstr ""
+msgstr "E773: \"%s\" 符號鏈接出現循環"
#: ../memline.c:3221
msgid "E325: ATTENTION"
@@ -4231,7 +4119,7 @@ msgstr "E329: 沒有那樣的選單"
#. Only a mnemonic or accelerator is not valid.
#: ../menu.c:329
msgid "E792: Empty menu name"
-msgstr ""
+msgstr "E792: 空的菜單名稱"
#: ../menu.c:340
msgid "E330: Menu path must not lead to a sub-menu"
@@ -4312,7 +4200,7 @@ msgstr "-- 尚有 --"
#: ../message.c:2398
msgid " SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "
-msgstr ""
+msgstr " 空格/d/j: 屏幕/頁/行 下翻,b/u/k: 上翻,q: 退出 "
#: ../message.c:3021 ../message.c:3031
msgid "Question"
@@ -4357,7 +4245,7 @@ msgstr "E116: 函式 %s 的引數不正確"
#: ../message.c:3119
msgid "E807: Expected Float argument for printf()"
-msgstr ""
+msgstr "E807: 期盼浮點數作為printf()參數"
#: ../message.c:3873
#, fuzzy
@@ -4370,11 +4258,11 @@ msgstr "W10: 注意: 你正在修改一個唯讀檔"
#: ../misc1.c:2537
msgid "Type number and <Enter> or click with mouse (empty cancels): "
-msgstr ""
+msgstr "請輸入數字並<Enter>或點擊鼠標(空白取消): "
#: ../misc1.c:2539
msgid "Type number and <Enter> (empty cancels): "
-msgstr ""
+msgstr "請選擇數字並(<Enter> 取消): "
#: ../misc1.c:2585
msgid "1 more line"
@@ -4400,7 +4288,7 @@ msgstr " (已中斷)"
#: ../misc1.c:2635
msgid "Beep!"
-msgstr ""
+msgstr "Beep!"
#: ../misc2.c:738
#, c-format
@@ -4617,7 +4505,7 @@ msgstr "E520: 不能在 Modeline 裡出現"
#: ../option.c:2815
msgid "E846: Key code not set"
-msgstr ""
+msgstr "E846: 未設置鍵位代碼"
#: ../option.c:2924
msgid "E521: Number required after ="
@@ -4642,11 +4530,11 @@ msgstr "E589: 'backupext' 跟 'patchmode' 是一樣的"
#: ../option.c:3964
msgid "E834: Conflicts with value of 'listchars'"
-msgstr ""
+msgstr "E834: 與'listchars'中的值發生衝突"
#: ../option.c:3966
msgid "E835: Conflicts with value of 'fillchars'"
-msgstr ""
+msgstr "E835: 與'fillchars'中的值發生衝突"
#: ../option.c:4163
msgid "E524: Missing colon"
@@ -4884,7 +4772,7 @@ msgstr "E382: 無法寫入,'buftype' 選項已設定"
#: ../quickfix.c:2812
msgid "E683: File name missing or invalid pattern"
-msgstr ""
+msgstr "E683: 缺少文件名或模式無效"
#: ../quickfix.c:2911
#, fuzzy, c-format
@@ -5022,25 +4910,26 @@ msgid ""
"E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be "
"used "
msgstr ""
+"E864: \\%#= 後面只能是0,1,或者2。自動引擎將會被使用"
#: ../regexp_nfa.c:239
msgid "E865: (NFA) Regexp end encountered prematurely"
-msgstr ""
+msgstr "E865: (NFA) 過早的遇到了正則表達式的結尾"
#: ../regexp_nfa.c:240
#, c-format
msgid "E866: (NFA regexp) Misplaced %c"
-msgstr ""
+msgstr "E866: (NFA regexp) %c 放錯了位置"
#: ../regexp_nfa.c:242
#, c-format
msgid "E877: (NFA regexp) Invalid character class: %<PRId64>"
-msgstr ""
+msgstr "E877: (NFA regexp) 不可用的字元類: %<PRId64>"
#: ../regexp_nfa.c:1261
#, c-format
msgid "E867: (NFA) Unknown operator '\\z%c'"
-msgstr ""
+msgstr "E867: (NFA) 未知的操作符 '\\z%c'"
#: ../regexp_nfa.c:1387
#, c-format
@@ -5050,21 +4939,21 @@ msgstr ""
#: ../regexp_nfa.c:1802
#, c-format
msgid "E869: (NFA) Unknown operator '\\@%c'"
-msgstr ""
+msgstr "E869: (NFA) 未知的操作符 '\\%%%c'"
#: ../regexp_nfa.c:1831
msgid "E870: (NFA regexp) Error reading repetition limits"
-msgstr ""
+msgstr "E870: (NFA regexp) 读取重复限制时出错"
#. Can't have a multi follow a multi.
#: ../regexp_nfa.c:1895
msgid "E871: (NFA regexp) Can't have a multi follow a multi !"
-msgstr ""
+msgstr "E871: (NFA regexp) 不能多个跟多个!"
#. Too many `('
#: ../regexp_nfa.c:2037
msgid "E872: (NFA regexp) Too many '('"
-msgstr ""
+msgstr "E872: (NFA regexp) 太多 '('"
#: ../regexp_nfa.c:2042
#, fuzzy
@@ -5073,31 +4962,32 @@ msgstr "E50: 太多 \\z("
#: ../regexp_nfa.c:2066
msgid "E873: (NFA regexp) proper termination error"
-msgstr ""
+msgstr "E873: (NFA regexp) 未適當終止"
#: ../regexp_nfa.c:2599
msgid "E874: (NFA) Could not pop the stack !"
-msgstr ""
+msgstr "E874: (NFA) 無法出棧!"
#: ../regexp_nfa.c:3298
msgid ""
"E875: (NFA regexp) (While converting from postfix to NFA), too many states "
"left on stack"
-msgstr ""
+msgstr "E875: (NFA regexp) (從後綴轉到 NFA 时),棧上遺留了太多狀態"
#: ../regexp_nfa.c:3302
msgid "E876: (NFA regexp) Not enough space to store the whole NFA "
-msgstr ""
+msgstr "E876: (NFA regexp) 沒有足夠的空間存儲NFA "
#: ../regexp_nfa.c:4571 ../regexp_nfa.c:4869
msgid ""
"Could not open temporary log file for writing, displaying on stderr ... "
msgstr ""
+"無法打開臨時日志文件進行寫入,顯示在stderr中..."
#: ../regexp_nfa.c:4840
#, c-format
msgid "(NFA) COULD NOT OPEN %s !"
-msgstr ""
+msgstr "(NFA) 不能打开 %s !"
#: ../regexp_nfa.c:6049
#, fuzzy
@@ -5270,17 +5160,17 @@ msgstr "E297: 暫存檔寫入錯誤"
#: ../spell.c:952
msgid "E758: Truncated spell file"
-msgstr ""
+msgstr "E758: 已截斷的拼寫文件"
#: ../spell.c:953
#, c-format
msgid "Trailing text in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,多餘的後續文本: %s"
#: ../spell.c:954
#, c-format
msgid "Affix name too long in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,附加項名字太長: %s"
#: ../spell.c:955
#, fuzzy
@@ -5289,20 +5179,20 @@ msgstr "E431: Tag 檔 \"%s\" 格式錯誤"
#: ../spell.c:957
msgid "E762: Character in FOL, LOW or UPP is out of range"
-msgstr ""
+msgstr "E762: FOL、LOW 或 UPP 中字元超出範圍"
#: ../spell.c:958
msgid "Compressing word tree..."
-msgstr ""
+msgstr "壓縮單詞樹……"
#: ../spell.c:1951
msgid "E756: Spell checking is not enabled"
-msgstr ""
+msgstr "E756: 拼寫檢查未啟用"
#: ../spell.c:2249
#, c-format
msgid "Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\""
-msgstr ""
+msgstr "警告: 找不到單詞列表 \"%s.%s.spl\" or \"%s.ascii.spl\""
#: ../spell.c:2473
#, fuzzy, c-format
@@ -5316,11 +5206,11 @@ msgstr "E307: %s 看起來不像是 Vim 暫存檔"
#: ../spell.c:2501
msgid "E771: Old spell file, needs to be updated"
-msgstr ""
+msgstr "E771: 舊的拼寫文件,需要更新"
#: ../spell.c:2504
msgid "E772: Spell file is for newer version of Vim"
-msgstr ""
+msgstr "E772: 為更高版本的 Vim 所使用的拼寫文件"
#: ../spell.c:2602
#, fuzzy
@@ -5340,66 +5230,68 @@ msgstr "搜尋 tag 檔案 \"%s\""
#: ../spell.c:4589 ../spell.c:5635 ../spell.c:6140
#, c-format
msgid "Conversion failure for word in %s line %d: %s"
-msgstr ""
+msgstr "單詞 %s 轉換失敗,第 %d 行: %s"
#: ../spell.c:4630 ../spell.c:6170
#, c-format
msgid "Conversion in %s not supported: from %s to %s"
-msgstr ""
+msgstr "不支持 %s 中的轉換: 从 %s 到 %s"
#: ../spell.c:4642
#, c-format
msgid "Invalid value for FLAG in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,FLAG 的值无效: %s"
#: ../spell.c:4655
#, c-format
msgid "FLAG after using flags in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,在使用標志後出現 FLAG: %s"
#: ../spell.c:4723
#, c-format
msgid ""
"Defining COMPOUNDFORBIDFLAG after PFX item may give wrong results in %s line "
"%d"
-msgstr ""
+msgstr "在 PFX 項之後定義 COMPOUNDFORBIDFLAG (%s 第%d行)可能會給出的錯誤結果"
+"%d"
#: ../spell.c:4731
#, c-format
msgid ""
"Defining COMPOUNDPERMITFLAG after PFX item may give wrong results in %s line "
"%d"
-msgstr ""
+msgstr "在 PFX 項之後定義 COMPOUNDFORBIDFLAG (%s 第%d行)可能會給出的錯誤結果"
+"%d"
#: ../spell.c:4747
#, c-format
msgid "Wrong COMPOUNDRULES value in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,错误的 COMPOUNDMIN 值: %s"
#: ../spell.c:4771
#, c-format
msgid "Wrong COMPOUNDWORDMAX value in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,错误的 COMPOUNDWORDMAX 值: %s"
#: ../spell.c:4777
#, c-format
msgid "Wrong COMPOUNDMIN value in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,错误的 COMPOUNDMIN 值: %s"
#: ../spell.c:4783
#, c-format
msgid "Wrong COMPOUNDSYLMAX value in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,错误的 COMPOUNDSYLMAX 值: %s"
#: ../spell.c:4795
#, c-format
msgid "Wrong CHECKCOMPOUNDPATTERN value in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,错误的 CHECKCOMPOUNDPATTERN 值: %s"
#: ../spell.c:4847
#, c-format
msgid "Different combining flag in continued affix block in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,在連續的附加塊種出現不同的組合標誌: %s"
#: ../spell.c:4850
#, fuzzy, c-format
@@ -5412,45 +5304,47 @@ msgid ""
"Affix also used for BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST in %s "
"line %d: %s"
msgstr ""
+"%s 第 %d 行,附加項被 BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST 使"
+"用: %s"
#: ../spell.c:4893
#, c-format
msgid "Expected Y or N in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,此處需要 Y 或 N: %s"
#: ../spell.c:4968
#, c-format
msgid "Broken condition in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,錯誤的條件: %s"
#: ../spell.c:5091
#, c-format
msgid "Expected REP(SAL) count in %s line %d"
-msgstr ""
+msgstr "%s 第 %d 行,此處需要 REP(SAL) 計數"
#: ../spell.c:5120
#, c-format
msgid "Expected MAP count in %s line %d"
-msgstr ""
+msgstr "%s 第 %d 行,此處需要 MAP 計數"
#: ../spell.c:5132
#, c-format
msgid "Duplicate character in MAP in %s line %d"
-msgstr ""
+msgstr "%s 第 %d 行,MAP 中存在重複的字元"
#: ../spell.c:5176
#, c-format
msgid "Unrecognized or duplicate item in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,無法識別或重複的項: %s"
#: ../spell.c:5197
#, c-format
msgid "Missing FOL/LOW/UPP line in %s"
-msgstr ""
+msgstr "%s 中缺少 FOL/LOW/UPP 行"
#: ../spell.c:5220
msgid "COMPOUNDSYLMAX used without SYLLABLE"
-msgstr ""
+msgstr "在没有 SYLLABLE 的情況下使用了 COMPOUNDSYLMAX"
#: ../spell.c:5236
#, fuzzy
@@ -5464,32 +5358,32 @@ msgstr "太多編輯參數"
#: ../spell.c:5240
msgid "Too many postponed prefixes and/or compound flags"
-msgstr ""
+msgstr "太多延遲前綴和/或組合標誌"
#: ../spell.c:5250
#, c-format
msgid "Missing SOFO%s line in %s"
-msgstr ""
+msgstr "%s 中缺少 SOFO%s 行"
#: ../spell.c:5253
#, c-format
msgid "Both SAL and SOFO lines in %s"
-msgstr ""
+msgstr "%s 同時出現 SAL 和 SOFO 行"
#: ../spell.c:5331
#, c-format
msgid "Flag is not a number in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,標誌不是數字: %s"
#: ../spell.c:5334
#, c-format
msgid "Illegal flag in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,無效的標誌: %s"
#: ../spell.c:5493 ../spell.c:5501
#, c-format
msgid "%s value differs from what is used in another .aff file"
-msgstr ""
+msgstr "%s 的值與另一個 .aff 文件中使用的值不相同"
#: ../spell.c:5602
#, fuzzy, c-format
@@ -5499,12 +5393,12 @@ msgstr "掃瞄字典: %s"
#: ../spell.c:5611
#, c-format
msgid "E760: No word count in %s"
-msgstr ""
+msgstr "E760: %s 中没有單詞計數"
#: ../spell.c:5669
#, c-format
msgid "line %6d, word %6d - %s"
-msgstr ""
+msgstr "第 %6d 行,第 %6d 个單詞 - %s"
#: ../spell.c:5691
#, fuzzy, c-format
@@ -5514,17 +5408,17 @@ msgstr "每一行都找不到: %s"
#: ../spell.c:5694
#, c-format
msgid "First duplicate word in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,首次出現重複的單詞: %s"
#: ../spell.c:5746
#, c-format
msgid "%d duplicate word(s) in %s"
-msgstr ""
+msgstr "存在 %d 个重複的單詞,在 %s 中"
#: ../spell.c:5748
#, c-format
msgid "Ignored %d word(s) with non-ASCII characters in %s"
-msgstr ""
+msgstr "忽略了含有非 ASCII 字元的 %d 个單詞,在 %s 中"
#: ../spell.c:6115
#, fuzzy, c-format
@@ -5534,42 +5428,42 @@ msgstr "從標準輸入讀取..."
#: ../spell.c:6155
#, c-format
msgid "Duplicate /encoding= line ignored in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %ld 行,重复的 /encoding= 行已被忽略: %s"
#: ../spell.c:6159
#, c-format
msgid "/encoding= line after word ignored in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,单词后的 /encoding= 行已被忽略: %s"
#: ../spell.c:6180
#, c-format
msgid "Duplicate /regions= line ignored in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,重复的 /regions= 行已被忽略: %s"
#: ../spell.c:6185
#, c-format
msgid "Too many regions in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,太多區域: %s"
#: ../spell.c:6198
#, c-format
msgid "/ line ignored in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,/ 行已被忽略: %s"
#: ../spell.c:6224
#, fuzzy, c-format
msgid "Invalid region nr in %s line %d: %s"
-msgstr "E573: 不正確的伺服器 id : %s"
+msgstr "%s 第 %d 行,無效的區域號: %s"
#: ../spell.c:6230
#, c-format
msgid "Unrecognized flags in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,不可識別的標誌: %s"
#: ../spell.c:6257
#, c-format
msgid "Ignored %d words with non-ASCII characters"
-msgstr ""
+msgstr "忽略了含有非 ASCII 字元的 %d 个單詞"
#: ../spell.c:6656
#, c-format
@@ -5578,23 +5472,23 @@ msgstr ""
#: ../spell.c:7340
msgid "Reading back spell file..."
-msgstr ""
+msgstr "读取拼寫文件……"
#. Go through the trie of good words, soundfold each word and add it to
#. the soundfold trie.
#: ../spell.c:7357
msgid "Performing soundfolding..."
-msgstr ""
+msgstr "正在 soundfolding……"
#: ../spell.c:7368
#, c-format
msgid "Number of words after soundfolding: %<PRId64>"
-msgstr ""
+msgstr "soundfolding 后的單詞数: %<PRId64>"
#: ../spell.c:7476
#, c-format
msgid "Total number of words: %d"
-msgstr ""
+msgstr "單詞总数: %d"
#: ../spell.c:7655
#, fuzzy, c-format
@@ -5604,11 +5498,11 @@ msgstr "寫入 viminfo 檔案 \"%s\" 中"
#: ../spell.c:7707 ../spell.c:7927
#, c-format
msgid "Estimated runtime memory use: %d bytes"
-msgstr ""
+msgstr "估計運行時的內存用量: %d 位元"
#: ../spell.c:7820
msgid "E751: Output file name must not have region name"
-msgstr ""
+msgstr "E751: 輸出文件不能含有區域名"
#: ../spell.c:7822
#, fuzzy
@@ -5622,7 +5516,7 @@ msgstr "E15: 不正確的運算式: %s"
#: ../spell.c:7907
msgid "Warning: both compounding and NOBREAK specified"
-msgstr ""
+msgstr "警告: 同時指定了 compounding 和 NOBREAK"
#: ../spell.c:7920
#, fuzzy, c-format
@@ -5631,30 +5525,30 @@ msgstr "寫入 viminfo 檔案 \"%s\" 中"
#: ../spell.c:7925
msgid "Done!"
-msgstr ""
+msgstr "完成!"
#: ../spell.c:8034
#, c-format
msgid "E765: 'spellfile' does not have %<PRId64> entries"
-msgstr ""
+msgstr "E765: 'spellfile' 没有 %<PRId64> 項"
#: ../spell.c:8074
#, c-format
msgid "Word '%.*s' removed from %s"
-msgstr ""
+msgstr "从 %s 中删除了單詞"
#: ../spell.c:8117
#, c-format
msgid "Word '%.*s' added to %s"
-msgstr ""
+msgstr "向 %s 中添加了單詞"
#: ../spell.c:8381
msgid "E763: Word characters differ between spell files"
-msgstr ""
+msgstr "E763: 拼寫文件之間的字元不相同"
#: ../spell.c:8684
msgid "Sorry, no suggestions"
-msgstr ""
+msgstr "抱歉,没有建议"
#: ../spell.c:8687
#, fuzzy, c-format
@@ -5671,7 +5565,7 @@ msgstr "將變動存儲至 \"%.*s\"?"
#: ../spell.c:8737
#, c-format
msgid " < \"%.*s\""
-msgstr ""
+msgstr " < \"%.*s\""
#: ../spell.c:8882
#, fuzzy
@@ -5691,28 +5585,28 @@ msgstr "E307: %s 看起來不像是 Vim 暫存檔"
#: ../spell.c:9282
#, c-format
msgid "E779: Old .sug file, needs to be updated: %s"
-msgstr ""
+msgstr "E779: 舊的.sug 文件,需要更新: %s"
#: ../spell.c:9286
#, c-format
msgid "E780: .sug file is for newer version of Vim: %s"
-msgstr ""
+msgstr "E780: .sug 文件適用於較新的 Vim 版本: %s"
#: ../spell.c:9295
#, c-format
msgid "E781: .sug file doesn't match .spl file: %s"
-msgstr ""
+msgstr "E781: .sug 文件不能匹配 .spl 文件: %s"
#: ../spell.c:9305
#, fuzzy, c-format
msgid "E782: error while reading .sug file: %s"
-msgstr "E47: 讀取錯誤檔案失敗"
+msgstr "E782: 當讀取.sug 文件時錯誤"
#. This should have been checked when generating the .spl
#. file.
#: ../spell.c:11575
msgid "E783: duplicate char in MAP entry"
-msgstr ""
+msgstr "E783: MAP 條目中有重複的字元"
#: ../syntax.c:266
msgid "No Syntax items defined for this buffer"
@@ -5894,10 +5788,11 @@ msgstr "E410: 不正確的 :syntax 子命令: %s"
msgid ""
" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"
msgstr ""
+" 總 計 計 數 匹 配 最 慢 的 平 均 名 字 模 式"
#: ../syntax.c:6146
msgid "E679: recursive loop loading syncolor.vim"
-msgstr ""
+msgstr "E679: 加載 syncolor.vim 时出現嵌套循環"
#: ../syntax.c:6256
#, c-format
@@ -5969,13 +5864,13 @@ msgstr "E424: 使用了過多相異的高亮度屬性"
msgid "E669: Unprintable character in group name"
msgstr "E669: 群組名稱中有無法列印的字元"
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
-msgstr "W18: 群組名稱中有不正確的字元"
+#: ../highlight_group.c:1756
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: 群組名稱中有不正確的字元"
#: ../syntax.c:7448
msgid "E849: Too many highlight and syntax groups"
-msgstr ""
+msgstr "E849: 高亮和語法組過多"
#: ../tag.c:104
msgid "E555: at bottom of tag stack"
@@ -6083,7 +5978,7 @@ msgstr "E435: 找不到 tag, 用猜的!"
#: ../tag.c:2797
#, c-format
msgid "Duplicate field name: %s"
-msgstr ""
+msgstr "重複的字段名: %s"
#: ../term.c:1442
msgid "' not known. Available builtin terminals are:"
@@ -6131,35 +6026,35 @@ msgstr "Vim: 讀取輸入錯誤,離開中...\n"
#. * file in a way it becomes shorter.
#: ../undo.c:379
msgid "E881: Line count changed unexpectedly"
-msgstr ""
+msgstr "E881: 行數意外地改變了"
#: ../undo.c:627
-#, fuzzy, c-format
+#, c-format
msgid "E828: Cannot open undo file for writing: %s"
-msgstr "E212: 無法以寫入模式開啟"
+msgstr "E828: 無法打開撤銷文件去寫入"
#: ../undo.c:717
#, c-format
msgid "E825: Corrupted undo file (%s): %s"
-msgstr ""
+msgstr "E825: 已損壞的撤銷文件 (%s): %s"
#: ../undo.c:1039
msgid "Cannot write undo file in any directory in 'undodir'"
-msgstr ""
+msgstr "不能寫入撤銷文件到 'undodir' 中的任何文件夾"
#: ../undo.c:1074
#, c-format
msgid "Will not overwrite with undo file, cannot read: %s"
-msgstr ""
+msgstr "不能寫入撤銷文件,不可讀取: %s"
#: ../undo.c:1092
#, c-format
msgid "Will not overwrite, this is not an undo file: %s"
-msgstr ""
+msgstr "不會覆蓋,這不是撤銷文件: %s"
#: ../undo.c:1108
msgid "Skipping undo file write, nothing to undo"
-msgstr ""
+msgstr "跳過撤消文件寫入,沒有可撤消的內容"
#: ../undo.c:1121
#, fuzzy, c-format
@@ -6174,7 +6069,7 @@ msgstr "E297: 暫存檔寫入錯誤"
#: ../undo.c:1280
#, c-format
msgid "Not reading undo file, owner differs: %s"
-msgstr ""
+msgstr "不能讀取撤銷文件,擁有者不同: %s"
#: ../undo.c:1292
#, fuzzy, c-format
@@ -6198,7 +6093,7 @@ msgstr "E484: 無法開啟檔案 %s"
#: ../undo.c:1328
msgid "File contents changed, cannot use undo info"
-msgstr ""
+msgstr "文件內容已經改變,不能使用撤銷信息"
#: ../undo.c:1497
#, fuzzy, c-format
@@ -6207,11 +6102,11 @@ msgstr "結束執行 %s"
#: ../undo.c:1586 ../undo.c:1812
msgid "Already at oldest change"
-msgstr ""
+msgstr "已經在最早的改變"
#: ../undo.c:1597 ../undo.c:1814
msgid "Already at newest change"
-msgstr ""
+msgstr "已經在最新的改變"
#: ../undo.c:1806
#, fuzzy, c-format
@@ -6259,11 +6154,11 @@ msgstr "%<PRId64> 行 %s 過 %d 次"
#: ../undo.c:2228
msgid "before"
-msgstr ""
+msgstr "之前"
#: ../undo.c:2228
msgid "after"
-msgstr ""
+msgstr "之後"
#: ../undo.c:2325
#, fuzzy
@@ -6272,7 +6167,7 @@ msgstr "沒有這個 mapping 對應"
#: ../undo.c:2330
msgid "number changes when saved"
-msgstr ""
+msgstr " 編號 改變 時間 保存"
#: ../undo.c:2360
#, fuzzy, c-format
diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c
index a4afe97ac8..245ce87865 100644
--- a/src/nvim/popupmenu.c
+++ b/src/nvim/popupmenu.c
@@ -6,27 +6,35 @@
/// Popup menu (PUM)
#include <assert.h>
-#include <inttypes.h>
+#include <limits.h>
#include <stdbool.h>
+#include <string.h>
+#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/drawscreen.h"
-#include "nvim/edit.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds.h"
+#include "nvim/getchar.h"
+#include "nvim/globals.h"
#include "nvim/grid.h"
#include "nvim/highlight.h"
#include "nvim/insexpand.h"
+#include "nvim/keycodes.h"
+#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/menu.h"
+#include "nvim/message.h"
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/popupmenu.h"
-#include "nvim/search.h"
+#include "nvim/pos.h"
+#include "nvim/screen.h"
#include "nvim/strings.h"
#include "nvim/ui.h"
#include "nvim/ui_compositor.h"
@@ -70,19 +78,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 = vim_strsize((char *)pum_array[i].pum_text);
+ w = vim_strsize(pum_array[i].pum_text);
if (pum_base_width < w) {
pum_base_width = w;
}
}
if (pum_array[i].pum_kind != NULL) {
- w = vim_strsize((char *)pum_array[i].pum_kind) + 1;
+ w = vim_strsize(pum_array[i].pum_kind) + 1;
if (pum_kind_width < w) {
pum_kind_width = w;
}
}
if (pum_array[i].pum_extra != NULL) {
- w = vim_strsize((char *)pum_array[i].pum_extra) + 1;
+ w = vim_strsize(pum_array[i].pum_extra) + 1;
if (pum_extra_width < w) {
pum_extra_width = w;
}
@@ -156,19 +164,18 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
if (pum_external) {
if (array_changed) {
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 = 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(item, STRING_OBJ(cstr_as_string(array[i].pum_text)));
+ ADD_C(item, STRING_OBJ(cstr_as_string(array[i].pum_kind)));
+ ADD_C(item, STRING_OBJ(cstr_as_string(array[i].pum_extra)));
+ ADD_C(item, STRING_OBJ(cstr_as_string(array[i].pum_info)));
ADD_C(arr, ARRAY_OBJ(item));
}
ui_call_popupmenu_show(arr, selected, pum_win_row, cursor_col,
pum_anchor_grid);
- arena_mem_free(arena_finish(&arena), &ui_ext_fixblk);
+ arena_mem_free(arena_finish(&arena));
} else {
ui_call_popupmenu_select(selected);
return;
@@ -211,11 +218,16 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
// pum above "pum_win_row"
pum_above = true;
- // Leave two lines of context if possible
- if (curwin->w_wrow - curwin->w_cline_row >= 2) {
- context_lines = 2;
+ if (State == MODE_CMDLINE) {
+ // for cmdline pum, no need for context lines
+ context_lines = 0;
} else {
- context_lines = curwin->w_wrow - curwin->w_cline_row;
+ // Leave two lines of context if possible
+ if (curwin->w_wrow - curwin->w_cline_row >= 2) {
+ context_lines = 2;
+ } else {
+ context_lines = curwin->w_wrow - curwin->w_cline_row;
+ }
}
if (pum_win_row >= size + context_lines) {
@@ -234,13 +246,17 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
// pum below "pum_win_row"
pum_above = false;
- // Leave two lines of context if possible
- validate_cheight();
- if (curwin->w_cline_row + curwin->w_cline_height - curwin->w_wrow >= 3) {
- context_lines = 3;
+ if (State == MODE_CMDLINE) {
+ // for cmdline pum, no need for context lines
+ context_lines = 0;
} else {
- context_lines = curwin->w_cline_row
- + curwin->w_cline_height - curwin->w_wrow;
+ // Leave two lines of context if possible
+ validate_cheight();
+ if (curwin->w_cline_row + curwin->w_cline_height - curwin->w_wrow >= 3) {
+ context_lines = 3;
+ } else {
+ context_lines = curwin->w_cline_row + curwin->w_cline_height - curwin->w_wrow;
+ }
}
pum_row = pum_win_row + context_lines;
@@ -265,12 +281,15 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
pum_row = above_row;
pum_height = pum_win_row - above_row;
}
+
+ pum_array = array;
+ // Set "pum_size" before returning so that pum_set_event_info() gets the correct size.
+ pum_size = size;
+
if (pum_external) {
return;
}
- pum_array = array;
- pum_size = size;
pum_compute_size();
int max_width = pum_base_width;
@@ -398,8 +417,8 @@ void pum_redraw(void)
int attr;
int i;
int idx;
- char_u *s;
- char_u *p = NULL;
+ char *s;
+ char *p = NULL;
int totwidth, width, w;
int thumb_pos = 0;
int thumb_height = 1;
@@ -508,24 +527,24 @@ void pum_redraw(void)
if (s == NULL) {
s = p;
}
- w = ptr2cells((char *)p);
+ w = ptr2cells(p);
if ((*p == NUL) || (*p == TAB) || (totwidth + w > pum_width)) {
// Display the text that fits or comes before a Tab.
// First convert it to printable characters.
- char_u *st;
- char_u saved = *p;
+ char *st;
+ char saved = *p;
if (saved != NUL) {
*p = NUL;
}
- st = (char_u *)transstr((const char *)s, true);
+ st = transstr(s, true);
if (saved != NUL) {
*p = saved;
}
if (pum_rl) {
- char *rt = (char *)reverse_text(st);
+ char *rt = reverse_text(st);
char *rt_start = rt;
int size = vim_strsize(rt);
@@ -543,8 +562,7 @@ void pum_redraw(void)
size++;
}
}
- grid_puts_len(&pum_grid, (char_u *)rt, (int)STRLEN(rt), row,
- grid_col - size + 1, attr);
+ grid_puts_len(&pum_grid, rt, (int)strlen(rt), row, grid_col - size + 1, attr);
xfree(rt_start);
xfree(st);
grid_col -= width;
@@ -561,11 +579,11 @@ void pum_redraw(void)
// Display two spaces for a Tab.
if (pum_rl) {
- grid_puts_len(&pum_grid, (char_u *)" ", 2, row, grid_col - 1,
+ grid_puts_len(&pum_grid, " ", 2, row, grid_col - 1,
attr);
grid_col -= 2;
} else {
- grid_puts_len(&pum_grid, (char_u *)" ", 2, row, grid_col, attr);
+ grid_puts_len(&pum_grid, " ", 2, row, grid_col, attr);
grid_col += 2;
}
totwidth += 2;
@@ -704,7 +722,7 @@ static bool pum_set_selected(int n, int repeat)
if ((pum_array[pum_selected].pum_info != NULL)
&& (Rows > 10)
&& (repeat <= 1)
- && (vim_strchr((char *)p_cot, 'p') != NULL)) {
+ && (vim_strchr(p_cot, 'p') != NULL)) {
win_T *curwin_save = curwin;
tabpage_T *curtab_save = curtab;
int res = OK;
@@ -744,29 +762,28 @@ static bool pum_set_selected(int n, int repeat)
if (res == OK) {
// Edit a new, empty buffer. Set options for a "wipeout"
// buffer.
- set_option_value("swf", 0L, NULL, OPT_LOCAL);
- set_option_value("bl", 0L, NULL, OPT_LOCAL);
- set_option_value("bt", 0L, "nofile", OPT_LOCAL);
- set_option_value("bh", 0L, "wipe", OPT_LOCAL);
- set_option_value("diff", 0L, NULL, OPT_LOCAL);
+ set_option_value_give_err("swf", 0L, NULL, OPT_LOCAL);
+ set_option_value_give_err("bl", 0L, NULL, OPT_LOCAL);
+ set_option_value_give_err("bt", 0L, "nofile", OPT_LOCAL);
+ set_option_value_give_err("bh", 0L, "wipe", OPT_LOCAL);
+ set_option_value_give_err("diff", 0L, NULL, OPT_LOCAL);
}
}
if (res == OK) {
- char_u *p, *e;
+ char *p, *e;
linenr_T lnum = 0;
for (p = pum_array[pum_selected].pum_info; *p != NUL;) {
- e = (char_u *)vim_strchr((char *)p, '\n');
+ e = vim_strchr(p, '\n');
if (e == NULL) {
- ml_append(lnum++, (char *)p, 0, false);
+ ml_append(lnum++, p, 0, false);
break;
- } else {
- *e = NUL;
- ml_append(lnum++, (char *)p, (int)(e - p + 1), false);
- *e = '\n';
- p = e + 1;
}
+ *e = NUL;
+ ml_append(lnum++, p, (int)(e - p + 1), false);
+ *e = '\n';
+ p = e + 1;
}
// Increase the height of the preview window to show the
@@ -802,12 +819,12 @@ static bool pum_set_selected(int n, int repeat)
// Return cursor to where we were
validate_cursor();
- redraw_later(curwin, SOME_VALID);
+ redraw_later(curwin, UPD_SOME_VALID);
// When the preview window was resized we need to
// update the view on the buffer. Only go back to
// the window when needed, otherwise it will always be
- // redraw.
+ // redrawn.
if (resized) {
no_u_sync++;
win_enter(curwin_save, true);
@@ -820,7 +837,7 @@ static bool pum_set_selected(int n, int repeat)
// TODO(bfredl): can simplify, get rid of the flag munging?
// or at least eliminate extra redraw before win_enter()?
pum_is_visible = false;
- update_screen(0);
+ update_screen();
pum_is_visible = true;
if (!resized && win_valid(curwin_save)) {
@@ -832,7 +849,7 @@ static bool pum_set_selected(int n, int repeat)
// May need to update the screen again when there are
// autocommands involved.
pum_is_visible = false;
- update_screen(0);
+ update_screen();
pum_is_visible = true;
}
}
@@ -870,6 +887,7 @@ void pum_check_clear(void)
grid_free(&pum_grid);
}
pum_is_drawn = false;
+ pum_external = false;
}
}
@@ -903,6 +921,17 @@ void pum_recompose(void)
ui_comp_compose_grid(&pum_grid);
}
+void pum_ext_select_item(int item, bool insert, bool finish)
+{
+ if (!pum_visible() || item < -1 || item >= pum_size) {
+ return;
+ }
+ pum_want.active = true;
+ pum_want.item = item;
+ pum_want.insert = insert;
+ pum_want.finish = finish;
+}
+
/// Gets the height of the menu.
///
/// @return the height of the popup menu, the number of entries visible.
@@ -1032,10 +1061,16 @@ void pum_show_popupmenu(vimmenu_T *menu)
pumitem_T *array = (pumitem_T *)xcalloc((size_t)pum_size, sizeof(pumitem_T));
for (vimmenu_T *mp = menu->children; mp != NULL; mp = mp->next) {
+ char *s = NULL;
+ // Make a copy of the text, the menu may be redefined in a callback.
if (menu_is_separator(mp->dname)) {
- array[idx++].pum_text = (char_u *)"";
+ s = "";
} else if (mp->modes & mp->enabled & mode) {
- array[idx++].pum_text = (char_u *)mp->dname;
+ s = mp->dname;
+ }
+ if (s != NULL) {
+ s = xstrdup(s);
+ array[idx++].pum_text = s;
}
}
@@ -1047,6 +1082,10 @@ void pum_show_popupmenu(vimmenu_T *menu)
pum_selected = -1;
pum_first = 0;
+ if (!p_mousemev) {
+ // Pretend 'mousemoveevent' is set.
+ ui_call_option_set(STATIC_CSTR_AS_STRING("mousemoveevent"), BOOLEAN_OBJ(true));
+ }
for (;;) {
pum_is_visible = true;
@@ -1102,8 +1141,14 @@ void pum_show_popupmenu(vimmenu_T *menu)
}
}
+ for (idx = 0; idx < pum_size; idx++) {
+ xfree(array[idx].pum_text);
+ }
xfree(array);
pum_undisplay(true);
+ if (!p_mousemev) {
+ ui_call_option_set(STATIC_CSTR_AS_STRING("mousemoveevent"), BOOLEAN_OBJ(false));
+ }
}
void pum_make_popup(const char *path_name, int use_mouse_pos)
diff --git a/src/nvim/popupmenu.h b/src/nvim/popupmenu.h
index 851ad31486..08b791c509 100644
--- a/src/nvim/popupmenu.h
+++ b/src/nvim/popupmenu.h
@@ -1,6 +1,8 @@
#ifndef NVIM_POPUPMENU_H
#define NVIM_POPUPMENU_H
+#include <stdbool.h>
+
#include "nvim/grid_defs.h"
#include "nvim/macros.h"
#include "nvim/types.h"
@@ -8,14 +10,22 @@
/// Used for popup menu items.
typedef struct {
- char_u *pum_text; // main menu text
- char_u *pum_kind; // extra kind text (may be truncated)
- char_u *pum_extra; // extra menu text (may be truncated)
- char_u *pum_info; // extra info
+ char *pum_text; // main menu text
+ char *pum_kind; // extra kind text (may be truncated)
+ char *pum_extra; // extra menu text (may be truncated)
+ char *pum_info; // extra info
} pumitem_T;
EXTERN ScreenGrid pum_grid INIT(= SCREEN_GRID_INIT);
+/// state for pum_ext_select_item.
+EXTERN struct {
+ bool active;
+ int item;
+ bool insert;
+ bool finish;
+} pum_want;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "popupmenu.h.generated.h"
#endif
diff --git a/src/nvim/profile.c b/src/nvim/profile.c
index d4f3756f4d..fd024f2d38 100644
--- a/src/nvim/profile.c
+++ b/src/nvim/profile.c
@@ -3,20 +3,34 @@
#include <assert.h>
#include <math.h>
+#include <stdbool.h>
+#include <stdint.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
-#include "nvim/assert.h"
+#include "nvim/ascii.h"
#include "nvim/charset.h"
#include "nvim/debugger.h"
#include "nvim/eval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/eval/userfunc.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/fileio.h"
-#include "nvim/func_attr.h"
-#include "nvim/globals.h" // for the global `time_fd` (startuptime)
+#include "nvim/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/hashtab.h"
+#include "nvim/keycodes.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
+#include "nvim/option_defs.h"
#include "nvim/os/os.h"
#include "nvim/os/time.h"
+#include "nvim/pos.h"
#include "nvim/profile.h"
#include "nvim/runtime.h"
+#include "nvim/types.h"
#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -279,35 +293,35 @@ void ex_profile(exarg_T *eap)
char *e;
int len;
- e = (char *)skiptowhite((char_u *)eap->arg);
+ e = skiptowhite(eap->arg);
len = (int)(e - eap->arg);
e = skipwhite(e);
- if (len == 5 && STRNCMP(eap->arg, "start", 5) == 0 && *e != NUL) {
+ if (len == 5 && strncmp(eap->arg, "start", 5) == 0 && *e != NUL) {
xfree(profile_fname);
- profile_fname = (char *)expand_env_save_opt((char_u *)e, true);
+ profile_fname = expand_env_save_opt(e, true);
do_profiling = PROF_YES;
profile_set_wait(profile_zero());
set_vim_var_nr(VV_PROFILING, 1L);
} else if (do_profiling == PROF_NONE) {
emsg(_("E750: First use \":profile start {fname}\""));
- } else if (STRCMP(eap->arg, "stop") == 0) {
+ } else if (strcmp(eap->arg, "stop") == 0) {
profile_dump();
do_profiling = PROF_NONE;
set_vim_var_nr(VV_PROFILING, 0L);
profile_reset();
- } else if (STRCMP(eap->arg, "pause") == 0) {
+ } else if (strcmp(eap->arg, "pause") == 0) {
if (do_profiling == PROF_YES) {
pause_time = profile_start();
}
do_profiling = PROF_PAUSED;
- } else if (STRCMP(eap->arg, "continue") == 0) {
+ } else if (strcmp(eap->arg, "continue") == 0) {
if (do_profiling == PROF_PAUSED) {
pause_time = profile_end(pause_time);
profile_set_wait(profile_add(profile_get_wait(), pause_time));
}
do_profiling = PROF_YES;
- } else if (STRCMP(eap->arg, "dump") == 0) {
+ } else if (strcmp(eap->arg, "dump") == 0) {
profile_dump();
} else {
// The rest is similar to ":breakadd".
@@ -340,7 +354,6 @@ char *get_profile_name(expand_T *xp, int idx)
switch (pexpand_what) {
case PEXP_SUBCMD:
return pexpand_cmds[idx];
- // case PEXP_FUNC: TODO
default:
return NULL;
}
@@ -354,18 +367,22 @@ void set_context_in_profile_cmd(expand_T *xp, const char *arg)
pexpand_what = PEXP_SUBCMD;
xp->xp_pattern = (char *)arg;
- char_u *const end_subcmd = skiptowhite((const char_u *)arg);
+ char *const end_subcmd = skiptowhite(arg);
if (*end_subcmd == NUL) {
return;
}
- if ((const char *)end_subcmd - arg == 5 && strncmp(arg, "start", 5) == 0) {
+ if ((end_subcmd - arg == 5 && strncmp(arg, "start", 5) == 0)
+ || (end_subcmd - arg == 4 && strncmp(arg, "file", 4) == 0)) {
xp->xp_context = EXPAND_FILES;
- xp->xp_pattern = skipwhite((char *)end_subcmd);
+ xp->xp_pattern = skipwhite(end_subcmd);
+ return;
+ } else if (end_subcmd - arg == 4 && strncmp(arg, "func", 4) == 0) {
+ xp->xp_context = EXPAND_USER_FUNC;
+ xp->xp_pattern = skipwhite(end_subcmd);
return;
}
- // TODO(tarruda): expand function names after "func"
xp->xp_context = EXPAND_NOTHING;
}
@@ -398,7 +415,7 @@ bool prof_def_func(void)
/// Print the count and times for one function or function line.
///
/// @param prefer_self when equal print only self time
-static void prof_func_line(FILE *fd, int count, proftime_T *total, proftime_T *self,
+static void prof_func_line(FILE *fd, int count, const proftime_T *total, const proftime_T *self,
bool prefer_self)
{
if (count > 0) {
@@ -430,7 +447,7 @@ static void prof_sort_list(FILE *fd, ufunc_T **sorttab, int st_len, char *title,
fp = sorttab[i];
prof_func_line(fd, fp->uf_tm_count, &fp->uf_tm_total, &fp->uf_tm_self,
prefer_self);
- if (fp->uf_name[0] == K_SPECIAL) {
+ if ((uint8_t)fp->uf_name[0] == K_SPECIAL) {
fprintf(fd, " <SNR>%s()\n", fp->uf_name + 3);
} else {
fprintf(fd, " %s()\n", fp->uf_name);
@@ -601,7 +618,7 @@ static void func_dump_profile(FILE *fd)
if (fp->uf_prof_initialized) {
sorttab[st_len++] = fp;
- if (fp->uf_name[0] == K_SPECIAL) {
+ if ((uint8_t)fp->uf_name[0] == K_SPECIAL) {
fprintf(fd, "FUNCTION <SNR>%s()\n", fp->uf_name + 3);
} else {
fprintf(fd, "FUNCTION %s()\n", fp->uf_name);
@@ -612,7 +629,7 @@ static void func_dump_profile(FILE *fd)
.script_ctx = fp->uf_script_ctx,
.channel_id = 0,
};
- char *p = (char *)get_scriptname(last_set, &should_free);
+ char *p = get_scriptname(last_set, &should_free);
fprintf(fd, " Defined: %s:%" PRIdLINENR "\n",
p, fp->uf_script_ctx.sc_lnum);
if (should_free) {
@@ -684,19 +701,19 @@ void script_prof_save(proftime_T *tm)
}
/// Count time spent in children after invoking another script or function.
-void script_prof_restore(proftime_T *tm)
+void script_prof_restore(const proftime_T *tm)
{
- scriptitem_T *si;
+ if (!SCRIPT_ID_VALID(current_sctx.sc_sid)) {
+ return;
+ }
- if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= script_items.ga_len) {
- si = &SCRIPT_ITEM(current_sctx.sc_sid);
- if (si->sn_prof_on && --si->sn_pr_nest == 0) {
- si->sn_pr_child = profile_end(si->sn_pr_child);
- // don't count wait time
- si->sn_pr_child = profile_sub_wait(*tm, si->sn_pr_child);
- si->sn_pr_children = profile_add(si->sn_pr_children, si->sn_pr_child);
- si->sn_prl_children = profile_add(si->sn_prl_children, si->sn_pr_child);
- }
+ scriptitem_T *si = &SCRIPT_ITEM(current_sctx.sc_sid);
+ if (si->sn_prof_on && --si->sn_pr_nest == 0) {
+ si->sn_pr_child = profile_end(si->sn_pr_child);
+ // don't count wait time
+ si->sn_pr_child = profile_sub_wait(*tm, si->sn_pr_child);
+ si->sn_pr_children = profile_add(si->sn_pr_children, si->sn_pr_child);
+ si->sn_prl_children = profile_add(si->sn_prl_children, si->sn_pr_child);
}
}
@@ -721,7 +738,7 @@ static void script_dump_profile(FILE *fd)
fprintf(fd, "\n");
fprintf(fd, "count total (s) self (s)\n");
- sfd = os_fopen((char *)si->sn_name, "r");
+ sfd = os_fopen(si->sn_name, "r");
if (sfd == NULL) {
fprintf(fd, "Cannot open file!\n");
} else {
@@ -770,17 +787,17 @@ static void script_dump_profile(FILE *fd)
/// Dump the profiling info.
void profile_dump(void)
{
- FILE *fd;
+ if (profile_fname == NULL) {
+ return;
+ }
- if (profile_fname != NULL) {
- fd = os_fopen(profile_fname, "w");
- if (fd == NULL) {
- semsg(_(e_notopen), profile_fname);
- } else {
- script_dump_profile(fd);
- func_dump_profile(fd);
- fclose(fd);
- }
+ FILE *fd = os_fopen(profile_fname, "w");
+ if (fd == NULL) {
+ semsg(_(e_notopen), profile_fname);
+ } else {
+ script_dump_profile(fd);
+ func_dump_profile(fd);
+ fclose(fd);
}
}
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 17fbbe17b8..5518fdfa51 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -4,30 +4,44 @@
// quickfix.c: functions for quickfix mode, using a file with error messages
#include <assert.h>
+#include <errno.h>
#include <inttypes.h>
+#include <limits.h>
#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
+#include <time.h>
-#include "nvim/api/private/helpers.h"
#include "nvim/arglist.h"
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/eval/window.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_cmds2.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
#include "nvim/help.h"
+#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
+#include "nvim/macros.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
+#include "nvim/memfile_defs.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
@@ -35,14 +49,16 @@
#include "nvim/normal.h"
#include "nvim/option.h"
#include "nvim/optionstr.h"
+#include "nvim/os/fs_defs.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
-#include "nvim/os_unix.h"
#include "nvim/path.h"
+#include "nvim/pos.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
#include "nvim/search.h"
#include "nvim/strings.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
#include "nvim/window.h"
@@ -67,9 +83,9 @@ struct qfline_S {
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
+ char qf_viscol; ///< set to true if qf_col and qf_end_col is
// screen column
- char qf_cleared; ///< set to TRUE if line has been deleted
+ 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
};
@@ -100,7 +116,7 @@ typedef struct qf_list_S {
qfline_T *qf_ptr; ///< pointer to the current error
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
+ int qf_nonevalid; ///< true if not a single valid entry found
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
@@ -176,6 +192,7 @@ enum {
QF_NOMEM = 3,
QF_IGNORE_LINE = 4,
QF_MULTISCAN = 5,
+ QF_ABORT = 6,
};
/// State information used to parse lines and add entries to a quickfix/location
@@ -336,8 +353,7 @@ static const size_t LINE_MAXLEN = 4096;
static struct fmtpattern {
char convchar;
char *pattern;
-} fmt_pat[FMT_PATTERNS] =
-{
+} fmt_pat[FMT_PATTERNS] = {
{ 'f', ".\\+" }, // only used when at end
{ 'n', "\\d\\+" }, // 1
{ 'l', "\\d\\+" }, // 2
@@ -368,9 +384,9 @@ static char *efmpat_to_regpat(const char *efmpat, char *regpat, efm_T *efminfo,
return NULL;
}
if ((idx && idx < FMT_PATTERN_R
- && vim_strchr("DXOPQ", efminfo->prefix) != NULL)
+ && vim_strchr("DXOPQ", (uint8_t)efminfo->prefix) != NULL)
|| (idx == FMT_PATTERN_R
- && vim_strchr("OPQ", efminfo->prefix) == NULL)) {
+ && vim_strchr("OPQ", (uint8_t)efminfo->prefix) == NULL)) {
semsg(_("E373: Unexpected %%%c in format string"), *efmpat);
return NULL;
}
@@ -452,10 +468,10 @@ static char *scanf_fmt_to_regpat(const char **pefmp, const char *efm, int len, c
static const char *efm_analyze_prefix(const char *efmp, efm_T *efminfo)
FUNC_ATTR_NONNULL_ALL
{
- if (vim_strchr("+-", *efmp) != NULL) {
+ if (vim_strchr("+-", (uint8_t)(*efmp)) != NULL) {
efminfo->flags = *efmp++;
}
- if (vim_strchr("DXAEWINCZGOPQ", *efmp) != NULL) {
+ if (vim_strchr("DXAEWINCZGOPQ", (uint8_t)(*efmp)) != NULL) {
efminfo->prefix = *efmp;
} else {
semsg(_("E376: Invalid %%%c in format string prefix"), *efmp);
@@ -494,7 +510,7 @@ static int efm_to_regpat(const char *efm, int len, efm_T *fmt_ptr, char *regpat)
if (ptr == NULL) {
return FAIL;
}
- } else if (vim_strchr("%\\.^$~[", *efmp) != NULL) {
+ } else if (vim_strchr("%\\.^$~[", (uint8_t)(*efmp)) != NULL) {
*ptr++ = *efmp; // regexp magic characters
} else if (*efmp == '#') {
*ptr++ = '*';
@@ -514,7 +530,7 @@ static int efm_to_regpat(const char *efm, int len, efm_T *fmt_ptr, char *regpat)
} else { // copy normal character
if (*efmp == '\\' && efmp + 1 < efm + len) {
efmp++;
- } else if (vim_strchr(".*^$~[", *efmp) != NULL) {
+ } else if (vim_strchr(".*^$~[", (uint8_t)(*efmp)) != NULL) {
*ptr++ = '\\'; // escape regexp atoms
}
if (*efmp) {
@@ -548,9 +564,9 @@ static void free_efm_list(efm_T **efm_first)
/// a regular expression pattern.
static size_t efm_regpat_bufsz(char *efm)
{
- size_t sz = (FMT_PATTERNS * 3) + (STRLEN(efm) << 2);
+ size_t sz = (FMT_PATTERNS * 3) + (strlen(efm) << 2);
for (int i = FMT_PATTERNS - 1; i >= 0;) {
- sz += STRLEN(fmt_pat[i--].pattern);
+ sz += strlen(fmt_pat[i--].pattern);
}
#ifdef BACKSLASH_IN_FILENAME
sz += 12; // "%f" can become twelve chars longer (see efm_to_regpat)
@@ -562,7 +578,7 @@ static size_t efm_regpat_bufsz(char *efm)
}
/// Return the length of a 'errorformat' option part (separated by ",").
-static int efm_option_part_len(char *efm)
+static int efm_option_part_len(const char *efm)
{
int len;
@@ -607,7 +623,7 @@ static efm_T *parse_efm_option(char *efm)
goto parse_efm_error;
}
// Advance to next part
- efm = (char *)skip_to_option_part((char_u *)efm + len); // skip comma and spaces
+ efm = skip_to_option_part(efm + len); // skip comma and spaces
}
if (fmt_first == NULL) { // nothing found
@@ -652,12 +668,12 @@ static int qf_get_next_str_line(qfstate_T *state)
}
char *p = vim_strchr(p_str, '\n');
- size_t len = (p != NULL) ? (size_t)(p - p_str) + 1 : STRLEN(p_str);
+ size_t len = (p != NULL) ? (size_t)(p - p_str) + 1 : strlen(p_str);
if (len > IOSIZE - 2) {
state->linebuf = qf_grow_linebuf(state, len);
} else {
- state->linebuf = (char *)IObuff;
+ state->linebuf = IObuff;
state->linelen = len;
}
memcpy(state->linebuf, p_str, state->linelen);
@@ -688,16 +704,16 @@ static int qf_get_next_list_line(qfstate_T *state)
return QF_END_OF_INPUT;
}
- size_t len = STRLEN(TV_LIST_ITEM_TV(p_li)->vval.v_string);
+ size_t len = strlen(TV_LIST_ITEM_TV(p_li)->vval.v_string);
if (len > IOSIZE - 2) {
state->linebuf = qf_grow_linebuf(state, len);
} else {
- state->linebuf = (char *)IObuff;
+ state->linebuf = IObuff;
state->linelen = len;
}
- STRLCPY(state->linebuf, TV_LIST_ITEM_TV(p_li)->vval.v_string,
- state->linelen + 1);
+ xstrlcpy(state->linebuf, TV_LIST_ITEM_TV(p_li)->vval.v_string,
+ state->linelen + 1);
state->p_li = TV_LIST_ITEM_NEXT(state->p_list, p_li);
return QF_OK;
@@ -710,17 +726,17 @@ static int qf_get_next_buf_line(qfstate_T *state)
if (state->buflnum > state->lnumlast) {
return QF_END_OF_INPUT;
}
- char *p_buf = (char *)ml_get_buf(state->buf, state->buflnum, false);
+ char *p_buf = ml_get_buf(state->buf, state->buflnum, false);
state->buflnum += 1;
- size_t len = STRLEN(p_buf);
+ size_t len = strlen(p_buf);
if (len > IOSIZE - 2) {
state->linebuf = qf_grow_linebuf(state, len);
} else {
- state->linebuf = (char *)IObuff;
+ state->linebuf = IObuff;
state->linelen = len;
}
- STRLCPY(state->linebuf, p_buf, state->linelen + 1);
+ xstrlcpy(state->linebuf, p_buf, state->linelen + 1);
return QF_OK;
}
@@ -730,7 +746,7 @@ static int qf_get_next_file_line(qfstate_T *state)
{
retry:
errno = 0;
- if (fgets((char *)IObuff, IOSIZE, state->fd) == NULL) {
+ if (fgets(IObuff, IOSIZE, state->fd) == NULL) {
if (errno == EINTR) {
goto retry;
}
@@ -738,7 +754,7 @@ retry:
}
bool discard = false;
- state->linelen = STRLEN(IObuff);
+ state->linelen = strlen(IObuff);
if (state->linelen == IOSIZE - 1
&& !(IObuff[state->linelen - 1] == '\n')) {
// The current line exceeds IObuff, continue reading using growbuf
@@ -761,7 +777,7 @@ retry:
}
break;
}
- state->linelen = STRLEN(state->growbuf + growbuflen);
+ state->linelen = strlen(state->growbuf + growbuflen);
growbuflen += state->linelen;
if (state->growbuf[growbuflen - 1] == '\n') {
break;
@@ -780,13 +796,13 @@ retry:
// The current line is longer than LINE_MAXLEN, continue reading but
// discard everything until EOL or EOF is reached.
errno = 0;
- if (fgets((char *)IObuff, IOSIZE, state->fd) == NULL) {
+ if (fgets(IObuff, IOSIZE, state->fd) == NULL) {
if (errno == EINTR) {
continue;
}
break;
}
- if (STRLEN(IObuff) < IOSIZE - 1 || IObuff[IOSIZE - 2] == '\n') {
+ if (strlen(IObuff) < IOSIZE - 1 || IObuff[IOSIZE - 2] == '\n') {
break;
}
}
@@ -794,15 +810,15 @@ retry:
state->linebuf = state->growbuf;
state->linelen = growbuflen;
} else {
- state->linebuf = (char *)IObuff;
+ state->linebuf = IObuff;
}
// Convert a line if it contains a non-ASCII character
- 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 (state->vc.vc_type != CONV_NONE && has_non_ascii(state->linebuf)) {
+ char *line = string_convert(&state->vc, state->linebuf, &state->linelen);
if (line != NULL) {
if (state->linelen < IOSIZE) {
- STRLCPY(state->linebuf, line, state->linelen + 1);
+ xstrlcpy(state->linebuf, line, state->linelen + 1);
xfree(line);
} else {
xfree(state->growbuf);
@@ -852,7 +868,7 @@ static int qf_get_nextline(qfstate_T *state)
#endif
}
- remove_bom((char_u *)state->linebuf);
+ remove_bom(state->linebuf);
return QF_OK;
}
@@ -909,7 +925,7 @@ restofline:
// match or no match.
fields->valid = true;
for (; fmt_ptr != NULL; fmt_ptr = fmt_ptr->next) {
- idx = (char_u)fmt_ptr->prefix;
+ idx = (uint8_t)fmt_ptr->prefix;
status = qf_parse_get_fields(linebuf, linelen, fmt_ptr, fields,
qfl->qf_multiline, qfl->qf_multiscan,
&tail);
@@ -1000,7 +1016,7 @@ static int qf_setup_state(qfstate_T *pstate, char *restrict enc, const char *res
{
pstate->vc.vc_type = CONV_NONE;
if (enc != NULL && *enc != NUL) {
- convert_setup(&pstate->vc, (char_u *)enc, p_enc);
+ convert_setup(&pstate->vc, enc, p_enc);
}
if (efile != NULL
@@ -1058,6 +1074,7 @@ static int qf_init_ext(qf_info_T *qi, int qf_idx, const char *restrict efile, bu
{
qfstate_T state = { 0 };
qffields_T fields = { 0 };
+ qfline_T *old_last = NULL;
static efm_T *fmt_first = NULL;
static char *last_efm = NULL;
int retval = -1; // default: return error flag
@@ -1071,7 +1088,6 @@ static int qf_init_ext(qf_info_T *qi, int qf_idx, const char *restrict efile, bu
}
qf_list_T *qfl;
- qfline_T *old_last = NULL;
bool adding = false;
if (newlist || qf_idx == qi->qf_listcount) {
// make place for a new list
@@ -1091,14 +1107,14 @@ static int qf_init_ext(qf_info_T *qi, int qf_idx, const char *restrict efile, bu
// Use the local value of 'errorformat' if it's set.
if (errorformat == p_efm && tv == NULL && buf && *buf->b_p_efm != NUL) {
- efm = (char *)buf->b_p_efm;
+ efm = buf->b_p_efm;
} else {
efm = errorformat;
}
// If the errorformat didn't change between calls, then reuse the previously
// parsed values.
- if (last_efm == NULL || (STRCMP(last_efm, efm) != 0)) {
+ if (last_efm == NULL || (strcmp(last_efm, efm) != 0)) {
// free the previously parsed data
XFREE_CLEAR(last_efm);
free_efm_list(&fmt_first);
@@ -1174,13 +1190,15 @@ static void qf_store_title(qf_list_T *qfl, const char *title)
{
XFREE_CLEAR(qfl->qf_title);
- if (title != NULL) {
- size_t len = STRLEN(title) + 1;
- char *p = xmallocz(len);
-
- qfl->qf_title = p;
- STRLCPY(p, title, len + 1);
+ if (title == NULL) {
+ return;
}
+
+ size_t len = strlen(title) + 1;
+ char *p = xmallocz(len);
+
+ qfl->qf_title = p;
+ xstrlcpy(p, title, len + 1);
}
/// The title of a quickfix/location list is set, by default, to the command
@@ -1242,15 +1260,15 @@ static int qf_parse_fmt_f(regmatch_T *rmp, int midx, qffields_T *fields, int pre
}
// Expand ~/file and $HOME/file to full path.
- char c = (char)(*rmp->endp[midx]);
+ char c = *rmp->endp[midx];
*rmp->endp[midx] = NUL;
- expand_env(rmp->startp[midx], (char_u *)fields->namebuf, CMDBUFFSIZE);
- *rmp->endp[midx] = (char_u)c;
+ expand_env(rmp->startp[midx], fields->namebuf, CMDBUFFSIZE);
+ *rmp->endp[midx] = c;
// For separate filename patterns (%O, %P and %Q), the specified file
// should exist.
if (vim_strchr("OPQ", prefix) != NULL
- && !os_path_exists((char_u *)fields->namebuf)) {
+ && !os_path_exists(fields->namebuf)) {
return QF_FAIL;
}
@@ -1264,7 +1282,7 @@ static int qf_parse_fmt_n(regmatch_T *rmp, int midx, qffields_T *fields)
if (rmp->startp[midx] == NULL) {
return QF_FAIL;
}
- fields->enr = (int)atol((char *)rmp->startp[midx]);
+ fields->enr = (int)atol(rmp->startp[midx]);
return QF_OK;
}
@@ -1275,7 +1293,7 @@ static int qf_parse_fmt_l(regmatch_T *rmp, int midx, qffields_T *fields)
if (rmp->startp[midx] == NULL) {
return QF_FAIL;
}
- fields->lnum = (linenr_T)atol((char *)rmp->startp[midx]);
+ fields->lnum = (linenr_T)atol(rmp->startp[midx]);
return QF_OK;
}
@@ -1286,7 +1304,7 @@ 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]);
+ fields->end_lnum = (linenr_T)atol(rmp->startp[midx]);
return QF_OK;
}
@@ -1297,7 +1315,7 @@ static int qf_parse_fmt_c(regmatch_T *rmp, int midx, qffields_T *fields)
if (rmp->startp[midx] == NULL) {
return QF_FAIL;
}
- fields->col = (int)atol((char *)rmp->startp[midx]);
+ fields->col = (int)atol(rmp->startp[midx]);
return QF_OK;
}
@@ -1308,7 +1326,7 @@ 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]);
+ fields->end_col = (int)atol(rmp->startp[midx]);
return QF_OK;
}
@@ -1319,13 +1337,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 = (char)(*rmp->startp[midx]);
+ fields->type = *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 *linebuf, size_t linelen, qffields_T *fields)
+/// Copy a non-error line into the error string. Return the matched line in
+/// "fields->errmsg".
+static int copy_nonerror_line(const char *linebuf, size_t linelen, qffields_T *fields)
FUNC_ATTR_NONNULL_ALL
{
if (linelen >= fields->errmsglen) {
@@ -1333,7 +1351,10 @@ static void qf_parse_fmt_plus(const char *linebuf, size_t linelen, qffields_T *f
fields->errmsg = xrealloc(fields->errmsg, linelen + 1);
fields->errmsglen = linelen + 1;
}
- STRLCPY(fields->errmsg, linebuf, linelen + 1);
+ // copy whole line to error message
+ xstrlcpy(fields->errmsg, linebuf, linelen + 1);
+
+ return QF_OK;
}
/// Parse the match for error message ('%m') pattern in regmatch.
@@ -1349,7 +1370,7 @@ static int qf_parse_fmt_m(regmatch_T *rmp, int midx, qffields_T *fields)
fields->errmsg = xrealloc(fields->errmsg, len + 1);
fields->errmsglen = len + 1;
}
- STRLCPY(fields->errmsg, rmp->startp[midx], len + 1);
+ xstrlcpy(fields->errmsg, rmp->startp[midx], len + 1);
return QF_OK;
}
@@ -1360,7 +1381,7 @@ static int qf_parse_fmt_r(regmatch_T *rmp, int midx, char **tail)
if (rmp->startp[midx] == NULL) {
return QF_FAIL;
}
- *tail = (char *)rmp->startp[midx];
+ *tail = rmp->startp[midx];
return QF_OK;
}
@@ -1372,7 +1393,7 @@ static int qf_parse_fmt_p(regmatch_T *rmp, int midx, qffields_T *fields)
return QF_FAIL;
}
fields->col = 0;
- for (char *match_ptr = (char *)rmp->startp[midx]; (char_u *)match_ptr != rmp->endp[midx];
+ for (char *match_ptr = rmp->startp[midx]; match_ptr != rmp->endp[midx];
match_ptr++) {
fields->col++;
if (*match_ptr == TAB) {
@@ -1392,7 +1413,7 @@ static int qf_parse_fmt_v(regmatch_T *rmp, int midx, qffields_T *fields)
if (rmp->startp[midx] == NULL) {
return QF_FAIL;
}
- fields->col = (int)atol((char *)rmp->startp[midx]);
+ fields->col = (int)atol(rmp->startp[midx]);
fields->use_viscol = true;
return QF_OK;
}
@@ -1409,7 +1430,7 @@ static int qf_parse_fmt_s(regmatch_T *rmp, int midx, qffields_T *fields)
len = CMDBUFFSIZE - 5;
}
STRCPY(fields->pattern, "^\\V");
- STRLCAT(fields->pattern, rmp->startp[midx], len + 4);
+ xstrlcat(fields->pattern, rmp->startp[midx], len + 4);
fields->pattern[len + 3] = '\\';
fields->pattern[len + 4] = '$';
fields->pattern[len + 5] = NUL;
@@ -1424,11 +1445,11 @@ static int qf_parse_fmt_o(regmatch_T *rmp, int midx, qffields_T *fields)
return QF_FAIL;
}
size_t len = (size_t)(rmp->endp[midx] - rmp->startp[midx]);
- size_t dsize = STRLEN(fields->module) + len + 1;
+ size_t dsize = strlen(fields->module) + len + 1;
if (dsize > CMDBUFFSIZE) {
dsize = CMDBUFFSIZE;
}
- STRLCAT(fields->module, rmp->startp[midx], dsize);
+ xstrlcat(fields->module, rmp->startp[midx], dsize);
return QF_OK;
}
@@ -1464,7 +1485,7 @@ static int qf_parse_match(char *linebuf, size_t linelen, efm_T *fmt_ptr, regmatc
if ((idx == 'C' || idx == 'Z') && !qf_multiline) {
return QF_FAIL;
}
- if (vim_strchr("EWIN", idx) != NULL) {
+ if (vim_strchr("EWIN", (uint8_t)idx) != NULL) {
fields->type = idx;
} else {
fields->type = 0;
@@ -1480,7 +1501,7 @@ static int qf_parse_match(char *linebuf, size_t linelen, efm_T *fmt_ptr, regmatc
status = qf_parse_fmt_f(regmatch, midx, fields, idx);
} else if (i == FMT_PATTERN_M) {
if (fmt_ptr->flags == '+' && !qf_multiscan) { // %+
- qf_parse_fmt_plus(linebuf, linelen, fields);
+ status = copy_nonerror_line(linebuf, linelen, fields);
} else if (midx > 0) { // %m
status = qf_parse_fmt_m(regmatch, midx, fields);
}
@@ -1505,7 +1526,7 @@ static int qf_parse_match(char *linebuf, size_t linelen, efm_T *fmt_ptr, regmatc
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)
{
- if (qf_multiscan && vim_strchr("OPQ", fmt_ptr->prefix) == NULL) {
+ if (qf_multiscan && vim_strchr("OPQ", (uint8_t)fmt_ptr->prefix) == NULL) {
return QF_FAIL;
}
@@ -1564,7 +1585,7 @@ static int qf_parse_dir_pfx(int idx, qffields_T *fields, qf_list_T *qfl)
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((char_u *)fields->namebuf)) {
+ if (*fields->namebuf == NUL || os_path_exists(fields->namebuf)) {
if (*fields->namebuf && idx == 'P') {
qfl->qf_currfile = qf_push_dir(fields->namebuf, &qfl->qf_file_stack, true);
} else if (idx == 'Q') {
@@ -1588,15 +1609,8 @@ static int qf_parse_line_nomatch(char *linebuf, size_t linelen, qffields_T *fiel
fields->namebuf[0] = NUL; // no match found, remove file name
fields->lnum = 0; // don't jump to this line
fields->valid = false;
- if (linelen >= fields->errmsglen) {
- // linelen + null terminator
- fields->errmsg = xrealloc(fields->errmsg, linelen + 1);
- fields->errmsglen = linelen + 1;
- }
- // copy whole line to error message
- STRLCPY(fields->errmsg, linebuf, linelen + 1);
- return QF_OK;
+ return copy_nonerror_line(linebuf, linelen, fields);
}
/// Parse multi-line error format prefixes (%C and %Z)
@@ -1609,8 +1623,8 @@ static int qf_parse_multiline_pfx(int idx, qf_list_T *qfl, qffields_T *fields)
return QF_FAIL;
}
if (*fields->errmsg) {
- size_t textlen = STRLEN(qfprev->qf_text);
- size_t errlen = STRLEN(fields->errmsg);
+ size_t textlen = strlen(qfprev->qf_text);
+ size_t errlen = strlen(fields->errmsg);
qfprev->qf_text = xrealloc(qfprev->qf_text, textlen + errlen + 2);
qfprev->qf_text[textlen] = '\n';
STRCPY(qfprev->qf_text + textlen + 1, fields->errmsg);
@@ -1672,14 +1686,16 @@ int qf_stack_get_bufnr(void)
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;
- }
+ if (qi->qf_bufnr == INVALID_QFBUFNR) {
+ return;
+ }
+
+ 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;
}
}
@@ -1790,7 +1806,7 @@ void check_quickfix_busy(void)
/// @param type type character
/// @param valid valid entry
///
-/// @returns QF_OK or QF_FAIL.
+/// @return QF_OK on success or QF_FAIL on failure.
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)
@@ -1891,7 +1907,7 @@ static qf_info_T *ll_get_or_alloc_list(win_T *wp)
/// Get the quickfix/location list stack to use for the specified Ex command.
/// For a location list command, returns the stack for the current window. If
/// the location list is not found, then returns NULL and prints an error
-/// message if 'print_emsg' is TRUE.
+/// message if 'print_emsg' is true.
static qf_info_T *qf_cmd_get_stack(exarg_T *eap, int print_emsg)
{
qf_info_T *qi = &ql_info;
@@ -2063,13 +2079,13 @@ static int qf_get_fnum(qf_list_T *qfl, char *directory, char *fname)
}
slash_adjust(fname);
#endif
- if (directory != NULL && !vim_isAbsName((char_u *)fname)) {
+ if (directory != NULL && !vim_isAbsName(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((char_u *)ptr)) {
+ if (!os_path_exists(ptr)) {
xfree(ptr);
directory = qf_guess_filepath(qfl, fname);
if (directory) {
@@ -2085,7 +2101,7 @@ static int qf_get_fnum(qf_list_T *qfl, char *directory, char *fname)
}
if (qf_last_bufname != NULL
- && STRCMP(bufname, qf_last_bufname) == 0
+ && strcmp(bufname, qf_last_bufname) == 0
&& bufref_valid(&qf_last_bufref)) {
buf = qf_last_bufref.br_buf;
xfree(ptr);
@@ -2116,7 +2132,7 @@ static char *qf_push_dir(char *dirbuf, struct dir_stack_T **stackptr, bool is_fi
*stackptr = ds_new;
// store directory on the stack
- if (vim_isAbsName((char_u *)dirbuf)
+ if (vim_isAbsName(dirbuf)
|| (*stackptr)->next == NULL
|| is_file_stack) {
(*stackptr)->dirname = xstrdup(dirbuf);
@@ -2129,7 +2145,7 @@ static char *qf_push_dir(char *dirbuf, struct dir_stack_T **stackptr, bool is_fi
while (ds_new) {
xfree((*stackptr)->dirname);
(*stackptr)->dirname = concat_fnames(ds_new->dirname, dirbuf, true);
- if (os_isdir((char_u *)(*stackptr)->dirname)) {
+ if (os_isdir((*stackptr)->dirname)) {
break;
}
@@ -2153,12 +2169,11 @@ static char *qf_push_dir(char *dirbuf, struct dir_stack_T **stackptr, bool is_fi
if ((*stackptr)->dirname != NULL) {
return (*stackptr)->dirname;
- } else {
- ds_ptr = *stackptr;
- *stackptr = (*stackptr)->next;
- xfree(ds_ptr);
- return NULL;
}
+ ds_ptr = *stackptr;
+ *stackptr = (*stackptr)->next;
+ xfree(ds_ptr);
+ return NULL;
}
// pop dirbuf from the directory stack and return previous directory or NULL if
@@ -2223,7 +2238,7 @@ static char *qf_guess_filepath(qf_list_T *qfl, char *filename)
xfree(fullname);
fullname = concat_fnames(ds_ptr->dirname, filename, true);
- if (os_path_exists((char_u *)fullname)) {
+ if (os_path_exists(fullname)) {
break;
}
@@ -2511,7 +2526,7 @@ static win_T *qf_find_win_with_normal_buf(void)
}
// Go to a window in any tabpage containing the specified file. Returns true
-// if successfully jumped to the window. Otherwise returns FALSE.
+// if successfully jumped to the window. Otherwise returns false.
static bool qf_goto_tabwin_with_file(int fnum)
{
FOR_ALL_TAB_WINDOWS(tp, wp) {
@@ -2686,6 +2701,10 @@ static int qf_jump_to_usable_window(int qf_fnum, bool newwin, int *opened_window
}
/// Edit the selected file or help file.
+/// @return OK if successfully edited the file.
+/// FAIL on failing to open the buffer.
+/// QF_ABORT if the quickfix/location list was freed by an autocmd
+/// when opening the buffer.
static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, int prev_winid,
int *opened_window)
{
@@ -2702,11 +2721,10 @@ static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, int
if (!can_abandon(curbuf, forceit)) {
no_write_message();
return FAIL;
- } else {
- retval = do_ecmd(qf_ptr->qf_fnum, NULL, NULL, NULL, (linenr_T)1,
- ECMD_HIDE + ECMD_SET_HELP,
- prev_winid == curwin->handle ? curwin : NULL);
}
+ retval = do_ecmd(qf_ptr->qf_fnum, NULL, NULL, NULL, (linenr_T)1,
+ ECMD_HIDE + ECMD_SET_HELP,
+ prev_winid == curwin->handle ? curwin : NULL);
} else {
retval = buflist_getfile(qf_ptr->qf_fnum, (linenr_T)1,
GETF_SETMARK | GETF_SWITCH, forceit);
@@ -2719,13 +2737,13 @@ static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, int
if (wp == NULL && curwin->w_llist != qi) {
emsg(_("E924: Current window was closed"));
*opened_window = false;
- return NOTDONE;
+ return QF_ABORT;
}
}
if (qfl_type == QFLT_QUICKFIX && !qflist_valid(NULL, save_qfid)) {
emsg(_(e_current_quickfix_list_was_changed));
- return NOTDONE;
+ return QF_ABORT;
}
if (old_qf_curlist != qi->qf_curlist // -V560
@@ -2736,7 +2754,7 @@ static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, int
} else {
emsg(_(e_current_location_list_was_changed));
}
- return NOTDONE;
+ return QF_ABORT;
}
return retval;
@@ -2771,7 +2789,7 @@ static void qf_jump_goto_line(linenr_T qf_lnum, int qf_col, char qf_viscol, char
// 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, '/', '/', (char_u *)qf_pattern, (long)1, SEARCH_KEEP, NULL)) {
+ if (!do_search(NULL, '/', '/', qf_pattern, (long)1, SEARCH_KEEP, NULL)) {
curwin->w_cursor = save_cursor;
}
}
@@ -2784,15 +2802,18 @@ static void qf_jump_print_msg(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, buf
// Update the screen before showing the message, unless the screen
// scrolled up.
if (!msg_scrolled) {
- update_topline_redraw();
+ update_topline(curwin);
+ if (must_redraw) {
+ update_screen();
+ }
}
- snprintf((char *)IObuff, IOSIZE, _("(%d of %d)%s%s: "), qf_index,
+ snprintf(IObuff, IOSIZE, _("(%d of %d)%s%s: "), qf_index,
qf_get_curlist(qi)->qf_count,
qf_ptr->qf_cleared ? _(" (line deleted)") : "",
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), (char *)IObuff + len, IOSIZE - len);
+ int len = (int)strlen(IObuff);
+ qf_fmt_text(skipwhite(qf_ptr->qf_text), 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
@@ -2804,16 +2825,17 @@ static void qf_jump_print_msg(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, buf
msg_scroll = false;
}
msg_ext_set_kind("quickfix");
- msg_attr_keep((char *)IObuff, 0, true, false);
+ msg_attr_keep(IObuff, 0, true, false);
msg_scroll = (int)i;
}
/// Find a usable window for opening a file from the quickfix/location list. If
/// a window is not found then open a new window. If 'newwin' is true, then open
/// a new window.
-/// Returns OK if successfully jumped or opened a window. Returns FAIL if not
-/// able to jump/open a window. Returns NOTDONE if a file is not associated
-/// with the entry.
+/// @return OK if successfully jumped or opened a window.
+/// FAIL if not able to jump/open a window.
+/// NOTDONE if a file is not associated with the entry.
+/// QF_ABORT if the quickfix/location list was modified by an autocmd.
static int qf_jump_open_window(qf_info_T *qi, qfline_T *qf_ptr, bool newwin, int *opened_window)
{
qf_list_T *qfl = qf_get_curlist(qi);
@@ -2835,7 +2857,7 @@ static int qf_jump_open_window(qf_info_T *qi, qfline_T *qf_ptr, bool newwin, int
} else {
emsg(_(e_current_location_list_was_changed));
}
- return FAIL;
+ return QF_ABORT;
}
// If currently in the quickfix window, find another window to show the
@@ -2860,7 +2882,7 @@ static int qf_jump_open_window(qf_info_T *qi, qfline_T *qf_ptr, bool newwin, int
} else {
emsg(_(e_current_location_list_was_changed));
}
- return FAIL;
+ return QF_ABORT;
}
return OK;
@@ -2869,9 +2891,9 @@ static int qf_jump_open_window(qf_info_T *qi, qfline_T *qf_ptr, bool newwin, int
/// Edit a selected file from the quickfix/location list and jump to a
/// particular line/column, adjust the folds and display a message about the
/// jump.
-/// Returns OK on success and FAIL on failing to open the file/buffer. Returns
-/// NOTDONE if the quickfix/location list is freed by an autocmd when opening
-/// the file.
+/// @return OK on success and FAIL on failing to open the file/buffer.
+/// QF_ABORT 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,
int prev_winid, int *opened_window, int openfold, int print_message)
{
@@ -2923,7 +2945,7 @@ void qf_jump(qf_info_T *qi, int dir, int errornr, int forceit)
// If 'newwin' is true, then open the file in a new window.
static void qf_jump_newwin(qf_info_T *qi, int dir, int errornr, int forceit, bool newwin)
{
- char *old_swb = (char *)p_swb;
+ char *old_swb = p_swb;
unsigned old_swb_flags = swb_flags;
const bool old_KeyTyped = KeyTyped; // getting file may reset it
@@ -2932,7 +2954,7 @@ static void qf_jump_newwin(qf_info_T *qi, int dir, int errornr, int forceit, boo
}
if (qf_stack_empty(qi) || qf_list_empty(qf_get_curlist(qi))) {
- emsg(_(e_quickfix));
+ emsg(_(e_no_errors));
return;
}
@@ -2965,14 +2987,19 @@ static void qf_jump_newwin(qf_info_T *qi, int dir, int errornr, int forceit, boo
if (retval == FAIL) {
goto failed;
}
+ if (retval == QF_ABORT) {
+ qi = NULL;
+ qf_ptr = NULL;
+ goto theend;
+ }
if (retval == NOTDONE) {
goto theend;
}
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
+ if (retval == QF_ABORT) {
+ // Quickfix/location list was modified by an autocmd
qi = NULL;
qf_ptr = NULL;
}
@@ -2994,10 +3021,10 @@ theend:
qfl->qf_ptr = qf_ptr;
qfl->qf_index = qf_index;
}
- if (p_swb != (char_u *)old_swb && p_swb == empty_option) {
+ if (p_swb != old_swb && p_swb == empty_option) {
// Restore old 'switchbuf' value, but not when an autocommand or
// modeline has changed the value.
- p_swb = (char_u *)old_swb;
+ p_swb = old_swb;
swb_flags = old_swb_flags;
}
decr_quickfix_busy();
@@ -3016,7 +3043,7 @@ static void qf_list_entry(qfline_T *qfp, int qf_idx, bool cursel)
{
char *fname = NULL;
if (qfp->qf_module != NULL && *qfp->qf_module != NUL) {
- vim_snprintf((char *)IObuff, IOSIZE, "%2d %s", qf_idx, qfp->qf_module);
+ vim_snprintf(IObuff, IOSIZE, "%2d %s", qf_idx, qfp->qf_module);
} else {
buf_T *buf;
if (qfp->qf_fnum != 0
@@ -3027,9 +3054,9 @@ static void qf_list_entry(qfline_T *qfp, int qf_idx, bool cursel)
}
}
if (fname == NULL) {
- snprintf((char *)IObuff, IOSIZE, "%2d", qf_idx);
+ snprintf(IObuff, IOSIZE, "%2d", qf_idx);
} else {
- vim_snprintf((char *)IObuff, IOSIZE, "%2d %s", qf_idx, fname);
+ vim_snprintf(IObuff, IOSIZE, "%2d %s", qf_idx, fname);
}
}
@@ -3038,16 +3065,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((char_u *)qfp->qf_module);
+ filter_entry &= message_filtered(qfp->qf_module);
}
if (filter_entry && fname != NULL) {
- filter_entry &= message_filtered((char_u *)fname);
+ filter_entry &= message_filtered(fname);
}
if (filter_entry && qfp->qf_pattern != NULL) {
- filter_entry &= message_filtered((char_u *)qfp->qf_pattern);
+ filter_entry &= message_filtered(qfp->qf_pattern);
}
if (filter_entry) {
- filter_entry &= message_filtered((char_u *)qfp->qf_text);
+ filter_entry &= message_filtered(qfp->qf_text);
}
if (filter_entry) {
return;
@@ -3062,21 +3089,21 @@ 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, (char *)IObuff, IOSIZE);
+ qf_range_text(qfp, IObuff, IOSIZE);
}
- vim_snprintf((char *)IObuff + STRLEN(IObuff), IOSIZE, "%s", qf_types(qfp->qf_type, qfp->qf_nr));
+ vim_snprintf(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, (char *)IObuff, IOSIZE);
+ qf_fmt_text(qfp->qf_pattern, IObuff, IOSIZE);
msg_puts((const char *)IObuff);
msg_puts_attr(":", qfSepAttr);
}
msg_puts(" ");
- char_u *tbuf = IObuff;
+ char *tbuf = IObuff;
size_t tbuflen = IOSIZE;
- size_t len = STRLEN(qfp->qf_text) + 3;
+ size_t len = strlen(qfp->qf_text) + 3;
if (len > IOSIZE) {
tbuf = xmalloc(len);
@@ -3088,14 +3115,12 @@ 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,
- (char *)tbuf, (int)tbuflen);
+ tbuf, (int)tbuflen);
msg_prt_line(tbuf, false);
if (tbuf != IObuff) {
xfree(tbuf);
}
-
- ui_flush(); // show one line at a time
}
// ":clist": list all errors
@@ -3111,7 +3136,7 @@ void qf_list(exarg_T *eap)
}
if (qf_stack_empty(qi) || qf_list_empty(qf_get_curlist(qi))) {
- emsg(_(e_quickfix));
+ emsg(_(e_no_errors));
return;
}
@@ -3147,15 +3172,15 @@ void qf_list(exarg_T *eap)
// Get the attributes for the different quickfix highlight items. Note
// that this depends on syntax items defined in the qf.vim syntax file
- qfFileAttr = syn_name2attr((char_u *)"qfFileName");
+ qfFileAttr = syn_name2attr("qfFileName");
if (qfFileAttr == 0) {
qfFileAttr = HL_ATTR(HLF_D);
}
- qfSepAttr = syn_name2attr((char_u *)"qfSeparator");
+ qfSepAttr = syn_name2attr("qfSeparator");
if (qfSepAttr == 0) {
qfSepAttr = HL_ATTR(HLF_D);
}
- qfLineAttr = syn_name2attr((char_u *)"qfLineNr");
+ qfLineAttr = syn_name2attr("qfLineNr");
if (qfLineAttr == 0) {
qfLineAttr = HL_ATTR(HLF_N);
}
@@ -3180,7 +3205,7 @@ static void qf_fmt_text(const char *restrict text, char *restrict buf, int bufsi
int i;
const char *p = (char *)text;
- for (i = 0; *p != NUL && i < bufsize - 1; ++i) {
+ for (i = 0; *p != NUL && i < bufsize - 1; i++) {
if (*p == '\n') {
buf[i] = ' ';
while (*++p != NUL) {
@@ -3200,18 +3225,18 @@ static void qf_fmt_text(const char *restrict text, char *restrict buf, int bufsi
static void qf_range_text(const qfline_T *qfp, char *buf, int bufsize)
{
vim_snprintf(buf, (size_t)bufsize, "%" PRIdLINENR, qfp->qf_lnum);
- int len = (int)STRLEN(buf);
+ int len = (int)strlen(buf);
if (qfp->qf_end_lnum > 0 && qfp->qf_lnum != qfp->qf_end_lnum) {
vim_snprintf(buf + len, (size_t)(bufsize - len), "-%" PRIdLINENR, qfp->qf_end_lnum);
- len += (int)STRLEN(buf + len);
+ len += (int)strlen(buf + len);
}
if (qfp->qf_col > 0) {
vim_snprintf(buf + len, (size_t)(bufsize - len), " col %d", qfp->qf_col);
- len += (int)STRLEN(buf + len);
+ len += (int)strlen(buf + len);
if (qfp->qf_end_col > 0 && qfp->qf_col != qfp->qf_end_col) {
vim_snprintf(buf + len, (size_t)(bufsize - len), "-%d", qfp->qf_end_col);
- len += (int)STRLEN(buf + len);
+ len += (int)strlen(buf + len);
}
}
buf[len] = NUL;
@@ -3232,13 +3257,13 @@ static void qf_msg(qf_info_T *qi, int which, char *lead)
count);
if (title != NULL) {
- size_t len = STRLEN(buf);
+ size_t len = strlen(buf);
if (len < 34) {
memset(buf + len, ' ', 34 - len);
buf[34] = NUL;
}
- STRLCAT(buf, title, IOSIZE);
+ xstrlcat(buf, title, IOSIZE);
}
trunc_string(buf, buf, Columns - 1, IOSIZE);
msg(buf);
@@ -3263,13 +3288,13 @@ void qf_age(exarg_T *eap)
emsg(_("E380: At bottom of quickfix stack"));
break;
}
- --qi->qf_curlist;
+ qi->qf_curlist--;
} else {
if (qi->qf_curlist >= qi->qf_listcount - 1) {
emsg(_("E381: At top of quickfix stack"));
break;
}
- ++qi->qf_curlist;
+ qi->qf_curlist++;
}
}
qf_msg(qi, qi->qf_curlist, "");
@@ -3393,7 +3418,7 @@ bool qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, linenr_T amount,
found_one = true;
if (qfp->qf_lnum >= line1 && qfp->qf_lnum <= line2) {
if (amount == MAXLNUM) {
- qfp->qf_cleared = TRUE;
+ qfp->qf_cleared = true;
} else {
qfp->qf_lnum += amount;
}
@@ -3460,14 +3485,12 @@ void qf_view_result(bool split)
{
qf_info_T *qi = &ql_info;
- if (!bt_quickfix(curbuf)) {
- return;
- }
if (IS_LL_WINDOW(curwin)) {
qi = GET_LOC_LIST(curwin);
}
+
if (qf_list_empty(qf_get_curlist(qi))) {
- emsg(_(e_quickfix));
+ emsg(_(e_no_errors));
return;
}
@@ -3558,12 +3581,12 @@ static int qf_goto_cwindow(const qf_info_T *qi, bool resize, int sz, bool vertsp
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, "hide", OPT_LOCAL);
+ set_option_value_give_err("swf", 0L, NULL, OPT_LOCAL);
+ set_option_value_give_err("bt", 0L, "quickfix", OPT_LOCAL);
+ set_option_value_give_err("bh", 0L, "hide", OPT_LOCAL);
RESET_BINDING(curwin);
curwin->w_p_diff = false;
- set_option_value("fdm", 0L, "manual", OPT_LOCAL);
+ set_option_value_give_err("fdm", 0L, "manual", OPT_LOCAL);
}
// Open a new quickfix or location list window, load the quickfix buffer and
@@ -3710,7 +3733,7 @@ static void qf_win_goto(win_T *win, linenr_T lnum)
curwin->w_cursor.coladd = 0;
curwin->w_curswant = 0;
update_topline(curwin); // scroll to show the line
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
curwin->w_redr_status = true; // update ruler
curwin = old_curwin;
curbuf = curwin->w_buffer;
@@ -3747,7 +3770,7 @@ linenr_T qf_current_entry(win_T *wp)
}
/// Update the cursor position in the quickfix window to the current error.
-/// Return TRUE if there is a quickfix window.
+/// Return true if there is a quickfix window.
///
/// @param old_qf_index previous qf_index or zero
static bool qf_win_pos_update(qf_info_T *qi, int old_qf_index)
@@ -3831,10 +3854,11 @@ static buf_T *qf_find_buf(qf_info_T *qi)
}
/// Process the 'quickfixtextfunc' option value.
-/// @return OK or FAIL
-int qf_process_qftf_option(void)
+void qf_process_qftf_option(char **errmsg)
{
- return option_set_callback_func(p_qftf, &qftf_cb);
+ if (option_set_callback_func(p_qftf, &qftf_cb) == FAIL) {
+ *errmsg = e_invarg;
+ }
}
/// Update the w:quickfix_title variable in the quickfix/location list window in
@@ -3859,48 +3883,61 @@ static void qf_update_buffer(qf_info_T *qi, qfline_T *old_last)
{
// Check if a buffer for the quickfix list exists. Update it.
buf_T *buf = qf_find_buf(qi);
- if (buf != NULL) {
- linenr_T old_line_count = buf->b_ml.ml_line_count;
- int qf_winid = 0;
-
- win_T *win;
- if (IS_LL_STACK(qi)) {
- if (curwin->w_llist == qi) {
- win = curwin;
- } else {
- win = qf_find_win_with_loclist(qi);
- if (win == NULL) {
- return;
- }
+ if (buf == NULL) {
+ return;
+ }
+
+ linenr_T old_line_count = buf->b_ml.ml_line_count;
+ int qf_winid = 0;
+
+ win_T *win;
+ if (IS_LL_STACK(qi)) {
+ if (curwin->w_llist == qi) {
+ win = curwin;
+ } else {
+ // Find the file window (non-quickfix) with this location list
+ win = qf_find_win_with_loclist(qi);
+ if (win == NULL) {
+ // File window is not found. Find the location list window.
+ win = qf_find_win(qi);
+ }
+ if (win == NULL) {
+ return;
}
- qf_winid = (int)win->handle;
}
+ qf_winid = (int)win->handle;
+ }
- aco_save_T aco;
+ // autocommands may cause trouble
+ incr_quickfix_busy();
- if (old_last == NULL) {
- // set curwin/curbuf to buf and save a few things
- aucmd_prepbuf(&aco, buf);
- }
+ aco_save_T aco;
- qf_update_win_titlevar(qi);
+ if (old_last == NULL) {
+ // set curwin/curbuf to buf and save a few things
+ aucmd_prepbuf(&aco, buf);
+ }
- qf_fill_buffer(qf_get_curlist(qi), buf, old_last, qf_winid);
- buf_inc_changedtick(buf);
+ qf_update_win_titlevar(qi);
- if (old_last == NULL) {
- (void)qf_win_pos_update(qi, 0);
+ qf_fill_buffer(qf_get_curlist(qi), buf, old_last, qf_winid);
+ buf_inc_changedtick(buf);
- // restore curwin/curbuf and a few other things
- aucmd_restbuf(&aco);
- }
+ if (old_last == NULL) {
+ (void)qf_win_pos_update(qi, 0);
- // Only redraw when added lines are visible. This avoids flickering when
- // the added lines are not visible.
- if ((win = qf_find_win(qi)) != NULL && old_line_count < win->w_botline) {
- redraw_buf_later(buf, NOT_VALID);
- }
+ // restore curwin/curbuf and a few other things
+ aucmd_restbuf(&aco);
}
+
+ // Only redraw when added lines are visible. This avoids flickering when
+ // the added lines are not visible.
+ if ((win = qf_find_win(qi)) != NULL && old_line_count < win->w_botline) {
+ redraw_buf_later(buf, UPD_NOT_VALID);
+ }
+
+ // always called after incr_quickfix_busy()
+ decr_quickfix_busy();
}
// Add an error line to the quickfix buffer.
@@ -3911,33 +3948,33 @@ static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum, const qfli
// If the 'quickfixtextfunc' function returned a non-empty custom string
// for this entry, then use it.
if (qftf_str != NULL && *qftf_str != NUL) {
- STRLCPY(IObuff, qftf_str, IOSIZE);
+ xstrlcpy(IObuff, qftf_str, IOSIZE);
} else {
buf_T *errbuf;
int len;
if (qfp->qf_module != NULL) {
- STRLCPY(IObuff, qfp->qf_module, IOSIZE);
- len = (int)STRLEN(IObuff);
+ xstrlcpy(IObuff, qfp->qf_module, IOSIZE);
+ len = (int)strlen(IObuff);
} else if (qfp->qf_fnum != 0
&& (errbuf = buflist_findnr(qfp->qf_fnum)) != NULL
&& errbuf->b_fname != NULL) {
if (qfp->qf_type == 1) { // :helpgrep
- STRLCPY(IObuff, path_tail(errbuf->b_fname), IOSIZE);
+ xstrlcpy(IObuff, path_tail(errbuf->b_fname), IOSIZE);
} else {
// Shorten the file name if not done already.
// For optimization, do this only for the first entry in a
// buffer.
if (first_bufline
&& (errbuf->b_sfname == NULL
- || path_is_absolute((char_u *)errbuf->b_sfname))) {
+ || path_is_absolute(errbuf->b_sfname))) {
if (*dirname == NUL) {
- os_dirname((char_u *)dirname, MAXPATHL);
+ os_dirname(dirname, MAXPATHL);
}
- shorten_buf_fname(errbuf, (char_u *)dirname, false);
+ shorten_buf_fname(errbuf, dirname, false);
}
- STRLCPY(IObuff, errbuf->b_fname, IOSIZE);
+ xstrlcpy(IObuff, errbuf->b_fname, IOSIZE);
}
- len = (int)STRLEN(IObuff);
+ len = (int)strlen(IObuff);
} else {
len = 0;
}
@@ -3945,15 +3982,15 @@ 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, (char *)IObuff + len, IOSIZE - len);
- len += (int)STRLEN(IObuff + len);
+ qf_range_text(qfp, IObuff + len, IOSIZE - len);
+ len += (int)strlen(IObuff + len);
- snprintf((char *)IObuff + len, (size_t)(IOSIZE - len), "%s", qf_types(qfp->qf_type,
- qfp->qf_nr));
- len += (int)STRLEN(IObuff + len);
+ snprintf(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, (char *)IObuff + len, IOSIZE - len);
- len += (int)STRLEN(IObuff + len);
+ qf_fmt_text(qfp->qf_pattern, IObuff + len, IOSIZE - len);
+ len += (int)strlen(IObuff + len);
}
if (len < IOSIZE - 2) {
IObuff[len++] = '|';
@@ -3964,11 +4001,11 @@ 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,
- (char *)IObuff + len, IOSIZE - len);
+ IObuff + len, IOSIZE - len);
}
if (ml_append_buf(buf, lnum, IObuff,
- (colnr_T)STRLEN(IObuff) + 1, false) == FAIL) {
+ (colnr_T)strlen(IObuff) + 1, false) == FAIL) {
return FAIL;
}
return OK;
@@ -3980,6 +4017,12 @@ static list_T *call_qftf_func(qf_list_T *qfl, int qf_winid, long start_idx, long
{
Callback *cb = &qftf_cb;
list_T *qftf_list = NULL;
+ static bool recursive = false;
+
+ if (recursive) {
+ return NULL; // this doesn't work properly recursively
+ }
+ recursive = true;
// 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
@@ -4013,6 +4056,7 @@ static list_T *call_qftf_func(qf_list_T *qfl, int qf_winid, long start_idx, long
tv_dict_unref(dict);
}
+ recursive = false;
return qftf_list;
}
@@ -4039,7 +4083,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) {
+ if (qfl != NULL && qfl->qf_start != NULL) {
char dirname[MAXPATHL];
*dirname = NUL;
@@ -4108,7 +4152,7 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int q
// resembles reading a file into a buffer, it's more logical when using
// autocommands.
curbuf->b_ro_locked++;
- set_option_value("ft", 0L, "qf", OPT_LOCAL);
+ set_option_value_give_err("ft", 0L, "qf", OPT_LOCAL);
curbuf->b_p_ma = false;
keep_filetype = true; // don't detect 'filetype'
@@ -4118,7 +4162,7 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int q
curbuf->b_ro_locked--;
// make sure it will be redrawn
- redraw_curbuf_later(NOT_VALID);
+ redraw_curbuf_later(UPD_NOT_VALID);
}
// Restore KeyTyped, setting 'filetype' may reset it.
@@ -4151,14 +4195,16 @@ static int qf_id2nr(const qf_info_T *const qi, const unsigned qfid)
static int qf_restore_list(qf_info_T *qi, unsigned save_qfid)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
- if (qf_get_curlist(qi)->qf_id != save_qfid) {
- const int curlist = qf_id2nr(qi, save_qfid);
- if (curlist < 0) {
- // list is not present
- return FAIL;
- }
- qi->qf_curlist = curlist;
+ if (qf_get_curlist(qi)->qf_id == save_qfid) {
+ return OK;
+ }
+
+ const int curlist = qf_id2nr(qi, save_qfid);
+ if (curlist < 0) {
+ // list is not present
+ return FAIL;
}
+ qi->qf_curlist = curlist;
return OK;
}
@@ -4175,15 +4221,14 @@ static void qf_jump_first(qf_info_T *qi, unsigned save_qfid, int forceit)
}
}
-// Return TRUE when using ":vimgrep" for ":grep".
+// Return true when using ":vimgrep" for ":grep".
int grep_internal(cmdidx_T cmdidx)
{
return (cmdidx == CMD_grep
|| cmdidx == CMD_lgrep
|| cmdidx == CMD_grepadd
|| cmdidx == CMD_lgrepadd)
- && STRCMP("internal",
- *curbuf->b_p_gp == NUL ? p_gp : curbuf->b_p_gp) == 0;
+ && strcmp("internal", *curbuf->b_p_gp == NUL ? p_gp : curbuf->b_p_gp) == 0;
}
// Return the make/grep autocmd name.
@@ -4213,16 +4258,16 @@ static char *make_get_auname(cmdidx_T cmdidx)
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;
+ size_t len = strlen(p_shq) * 2 + strlen(makecmd) + 1;
if (*p_sp != NUL) {
- len += STRLEN(p_sp) + STRLEN(fname) + 3;
+ len += strlen(p_sp) + strlen(fname) + 3;
}
char *const cmd = xmalloc(len);
- snprintf(cmd, len, "%s%s%s", (char *)p_shq, (char *)makecmd, (char *)p_shq);
+ snprintf(cmd, len, "%s%s%s", p_shq, (char *)makecmd, p_shq);
// If 'shellpipe' empty: don't redirect to 'errorfile'.
if (*p_sp != NUL) {
- append_redir(cmd, len, (char *)p_sp, (char *)fname);
+ append_redir(cmd, len, p_sp, (char *)fname);
}
// Display the fully formed command. Output a newline if there's something
@@ -4241,7 +4286,7 @@ static char *make_get_fullcmd(const char *makecmd, const char *fname)
// Used for ":make", ":lmake", ":grep", ":lgrep", ":grepadd", and ":lgrepadd"
void ex_make(exarg_T *eap)
{
- char *enc = (*curbuf->b_p_menc != NUL) ? (char *)curbuf->b_p_menc : (char *)p_menc;
+ char *enc = (*curbuf->b_p_menc != NUL) ? curbuf->b_p_menc : p_menc;
// Redirect ":grep" to ":vimgrep" if 'grepprg' is "internal".
if (grep_internal(eap->cmdidx)) {
@@ -4275,10 +4320,17 @@ void ex_make(exarg_T *eap)
incr_quickfix_busy();
- int res = qf_init(wp, fname, (eap->cmdidx != CMD_make
- && eap->cmdidx != CMD_lmake) ? p_gefm : p_efm,
- (eap->cmdidx != CMD_grepadd && eap->cmdidx != CMD_lgrepadd),
- qf_cmdtitle(*eap->cmdlinep), enc);
+ char *errorformat = p_efm;
+ bool newlist = true;
+
+ if (eap->cmdidx != CMD_make && eap->cmdidx != CMD_lmake) {
+ errorformat = p_gefm;
+ }
+ if (eap->cmdidx == CMD_grepadd || eap->cmdidx == CMD_lgrepadd) {
+ newlist = false;
+ }
+
+ int res = qf_init(wp, fname, errorformat, newlist, qf_cmdtitle(*eap->cmdlinep), enc);
qf_info_T *qi = &ql_info;
if (wp != NULL) {
@@ -4319,7 +4371,7 @@ static char *get_mef_name(void)
static int off = 0;
if (*p_mef == NUL) {
- name = (char *)vim_tempname();
+ name = vim_tempname();
if (name == NULL) {
emsg(_(e_notmp));
}
@@ -4328,7 +4380,7 @@ static char *get_mef_name(void)
char *p;
- for (p = p_mef; *p; ++p) {
+ for (p = p_mef; *p; p++) {
if (p[0] == '#' && p[1] == '#') {
break;
}
@@ -4345,9 +4397,9 @@ static char *get_mef_name(void)
} else {
off += 19;
}
- name = xmalloc(STRLEN(p_mef) + 30);
+ name = xmalloc(strlen(p_mef) + 30);
STRCPY(name, p_mef);
- snprintf(name + (p - p_mef), STRLEN(name), "%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;
@@ -4832,7 +4884,7 @@ static void qf_get_nth_below_entry(qfline_T *entry_arg, linenr_T n, bool linewis
}
/// Get the nth quickfix entry above the specified entry. Searches backwards in
-/// the list. If linewise is TRUE, then treat multiple entries on a single line
+/// the list. If linewise is true, then treat multiple entries on a single line
/// as one.
static void qf_get_nth_above_entry(qfline_T *entry, linenr_T n, bool linewise, int *errornr)
FUNC_ATTR_NONNULL_ALL
@@ -4898,7 +4950,7 @@ void ex_cbelow(exarg_T *eap)
|| eap->cmdidx == CMD_cafter) ? BUF_HAS_QF_ENTRY : BUF_HAS_LL_ENTRY;
if (!(curbuf->b_has_qf_entry & buf_has_flag)) {
- emsg(_(e_quickfix));
+ emsg(_(e_no_errors));
return;
}
@@ -4910,7 +4962,7 @@ void ex_cbelow(exarg_T *eap)
qf_list_T *qfl = qf_get_curlist(qi);
// check if the list has valid errors
if (!qf_list_has_valid_entries(qfl)) {
- emsg(_(e_quickfix));
+ emsg(_(e_no_errors));
return;
}
@@ -4981,7 +5033,7 @@ void ex_cfile(exarg_T *eap)
set_string_option_direct("ef", -1, eap->arg, OPT_FREE, 0);
}
- char *enc = (*curbuf->b_p_menc != NUL) ? (char *)curbuf->b_p_menc : (char *)p_menc;
+ char *enc = (*curbuf->b_p_menc != NUL) ? curbuf->b_p_menc : p_menc;
if (is_loclist_cmd(eap->cmdidx)) {
wp = curwin;
@@ -4997,8 +5049,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, (char *)p_ef, p_efm, (eap->cmdidx != CMD_caddfile
- && eap->cmdidx != CMD_laddfile),
+ int res = qf_init(wp, 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);
@@ -5062,7 +5114,7 @@ static void vgr_init_regmatch(regmmatch_T *regmatch, char *s)
emsg(_(e_noprevre));
return;
}
- regmatch->regprog = vim_regcomp((char *)last_search_pat(), RE_MAGIC);
+ regmatch->regprog = vim_regcomp(last_search_pat(), RE_MAGIC);
} else {
regmatch->regprog = vim_regcomp(s, RE_MAGIC);
}
@@ -5075,7 +5127,7 @@ static void vgr_init_regmatch(regmmatch_T *regmatch, char *s)
static void vgr_display_fname(char *fname)
{
msg_start();
- char *p = (char *)msg_strtrunc((char_u *)fname, true);
+ char *p = msg_strtrunc(fname, true);
if (p == NULL) {
msg_outtrans(fname);
} else {
@@ -5120,11 +5172,10 @@ static bool vgr_qflist_valid(win_T *wp, qf_info_T *qi, unsigned qfid, char *titl
// An autocmd has freed the location list
emsg(_(e_current_location_list_was_changed));
return false;
- } else {
- // Quickfix list is not found, create a new one.
- qf_new_list(qi, title);
- return true;
}
+ // Quickfix list is not found, create a new one.
+ qf_new_list(qi, title);
+ return true;
}
if (qf_restore_list(qi, qfid) == FAIL) {
return false;
@@ -5140,6 +5191,7 @@ static bool vgr_match_buflines(qf_list_T *qfl, char *fname, buf_T *buf, char *sp
FUNC_ATTR_NONNULL_ARG(1, 3, 4, 5, 6)
{
bool found_match = false;
+ const size_t pat_len = strlen(spat);
for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count && *tomatch > 0; lnum++) {
colnr_T col = 0;
@@ -5154,7 +5206,7 @@ static bool vgr_match_buflines(qf_list_T *qfl, char *fname, buf_T *buf, char *sp
fname,
NULL,
duplicate_name ? 0 : buf->b_fnum,
- (char *)ml_get_buf(buf, regmatch->startpos[0].lnum + lnum, false),
+ 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,
@@ -5176,20 +5228,18 @@ static bool vgr_match_buflines(qf_list_T *qfl, char *fname, buf_T *buf, char *sp
break;
}
col = regmatch->endpos[0].col + (col == regmatch->endpos[0].col);
- if (col > (colnr_T)STRLEN(ml_get_buf(buf, lnum, false))) {
+ 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);
+ char *const str = 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) {
+ while (fuzzy_match(str + col, 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.
@@ -5220,7 +5270,7 @@ static bool vgr_match_buflines(qf_list_T *qfl, char *fname, buf_T *buf, char *sp
break;
}
col = (colnr_T)matches[pat_len - 1] + col + 1;
- if (col > (colnr_T)STRLEN(str)) {
+ if (col > (colnr_T)strlen(str)) {
break;
}
}
@@ -5236,7 +5286,7 @@ static bool vgr_match_buflines(qf_list_T *qfl, char *fname, buf_T *buf, char *sp
/// Jump to the first match and update the directory.
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 *first_match_buf, char *target_dir) // NOLINT(readability-non-const-parameter)
{
buf_T *buf = curbuf;
qf_jump(qi, 0, 0, forceit);
@@ -5262,8 +5312,8 @@ 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 *const fname = (char *)buf->b_ml.ml_mfp->mf_fname;
- const size_t len = STRLEN(fname);
+ const char *const fname = buf->b_ml.ml_mfp->mf_fname;
+ const size_t len = strlen(fname);
return fname[len - 1] != 'p' || fname[len - 2] != 'w';
}
@@ -5305,10 +5355,8 @@ static int vgr_process_args(exarg_T *eap, vgr_args_T *args)
}
// Parse the list of arguments, wildcards have already been expanded.
- if (get_arglist_exp((char_u *)p, &args->fcount, &args->fnames, true) == FAIL) {
- return FAIL;
- }
- if (args->fcount == 0) {
+ if (get_arglist_exp(p, &args->fcount, &args->fnames, true) == FAIL
+ || args->fcount == 0) {
emsg(_(e_nomatch));
return FAIL;
}
@@ -5330,11 +5378,11 @@ static int vgr_process_files(win_T *wp, qf_info_T *qi, vgr_args_T *cmd_args, boo
// 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);
+ os_dirname(dirname_start, MAXPATHL);
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]);
+ char *fname = path_try_shorten_fname(cmd_args->fnames[fi]);
if (time(NULL) > seconds) {
// Display the file name every second or so, show the user we are
// working on it.
@@ -5419,7 +5467,7 @@ static int vgr_process_files(win_T *wp, qf_info_T *qi, vgr_args_T *cmd_args, boo
// directory we jumped to below.
if (buf == *first_match_buf
&& *target_dir == NULL
- && STRCMP(dirname_start, dirname_now) != 0) {
+ && strcmp(dirname_start, dirname_now) != 0) {
*target_dir = xstrdup(dirname_now);
}
@@ -5429,7 +5477,7 @@ static int vgr_process_files(win_T *wp, qf_info_T *qi, vgr_args_T *cmd_args, boo
// options!
aco_save_T aco;
aucmd_prepbuf(&aco, buf);
- apply_autocmds(EVENT_FILETYPE, (char *)buf->b_p_ft, buf->b_fname, true, buf);
+ apply_autocmds(EVENT_FILETYPE, buf->b_p_ft, buf->b_fname, true, buf);
do_modelines(OPT_NOWIN);
aucmd_restbuf(&aco);
}
@@ -5541,8 +5589,8 @@ static void restore_start_dir(char *dirname_start)
{
char *dirname_now = xmalloc(MAXPATHL);
- os_dirname((char_u *)dirname_now, MAXPATHL);
- if (STRCMP(dirname_start, dirname_now) != 0) {
+ os_dirname(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.
exarg_T ea = {
@@ -5609,7 +5657,7 @@ static buf_T *load_dummy_buffer(char *fname, char *dirname_start, char *resultin
if (readfile_result == OK
&& !got_int
&& !(curbuf->b_flags & BF_NEW)) {
- failed = FALSE;
+ failed = false;
if (curbuf != newbuf) {
// Bloody autocommands changed the buffer! Can happen when
// using netrw and editing a remote file. Use the current
@@ -5622,6 +5670,7 @@ static buf_T *load_dummy_buffer(char *fname, char *dirname_start, char *resultin
// Restore curwin/curbuf and a few other things.
aucmd_restbuf(&aco);
+
if (newbuf_to_wipe.br_buf != NULL && bufref_valid(&newbuf_to_wipe)) {
wipe_buffer(newbuf_to_wipe.br_buf, false);
}
@@ -5634,7 +5683,7 @@ static buf_T *load_dummy_buffer(char *fname, char *dirname_start, char *resultin
// 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((char_u *)resulting_dir, MAXPATHL);
+ os_dirname(resulting_dir, MAXPATHL);
restore_start_dir(dirname_start);
if (!bufref_valid(&newbufref)) {
@@ -5677,7 +5726,7 @@ static void wipe_dummy_buffer(buf_T *buf, char *dirname_start)
cleanup_T cs;
// Reset the error/interrupt/exception state here so that aborting()
- // returns FALSE when wiping out the buffer. Otherwise it doesn't
+ // returns false when wiping out the buffer. Otherwise it doesn't
// work when got_int is set.
enter_cleanup(&cs);
@@ -5696,12 +5745,14 @@ static void wipe_dummy_buffer(buf_T *buf, char *dirname_start)
// 'autochdir' option have changed it.
static void unload_dummy_buffer(buf_T *buf, char *dirname_start)
{
- if (curbuf != buf) { // safety check
- close_buffer(NULL, buf, DOBUF_UNLOAD, false, true);
-
- // When autocommands/'autochdir' option changed directory: go back.
- restore_start_dir(dirname_start);
+ if (curbuf == buf) { // safety check
+ return;
}
+
+ close_buffer(NULL, buf, DOBUF_UNLOAD, false, true);
+
+ // When autocommands/'autochdir' option changed directory: go back.
+ restore_start_dir(dirname_start);
}
/// Copy the specified quickfix entry items into a new dict and append the dict
@@ -5826,32 +5877,34 @@ static int qf_get_list_from_lines(dict_T *what, dictitem_T *di, dict_T *retdict)
int status = FAIL;
// Only a List value is supported
- if (di->di_tv.v_type == VAR_LIST && di->di_tv.vval.v_list != NULL) {
- char *errorformat = p_efm;
- dictitem_T *efm_di;
- // If errorformat is supplied then use it, otherwise use the 'efm'
- // option setting
- if ((efm_di = tv_dict_find(what, S_LEN("efm"))) != NULL) {
- if (efm_di->di_tv.v_type != VAR_STRING
- || efm_di->di_tv.vval.v_string == NULL) {
- return FAIL;
- }
- errorformat = efm_di->di_tv.vval.v_string;
- }
-
- list_T *l = tv_list_alloc(kListLenMayKnow);
- qf_info_T *const qi = qf_alloc_stack(QFLT_INTERNAL);
+ if (di->di_tv.v_type != VAR_LIST || di->di_tv.vval.v_list == NULL) {
+ return FAIL;
+ }
- if (qf_init_ext(qi, 0, NULL, NULL, &di->di_tv, errorformat,
- true, (linenr_T)0, (linenr_T)0, NULL, NULL) > 0) {
- (void)get_errorlist(qi, NULL, 0, 0, l);
- qf_free(&qi->qf_lists[0]);
+ char *errorformat = p_efm;
+ dictitem_T *efm_di;
+ // If errorformat is supplied then use it, otherwise use the 'efm'
+ // option setting
+ if ((efm_di = tv_dict_find(what, S_LEN("efm"))) != NULL) {
+ if (efm_di->di_tv.v_type != VAR_STRING
+ || efm_di->di_tv.vval.v_string == NULL) {
+ return FAIL;
}
- xfree(qi);
+ errorformat = efm_di->di_tv.vval.v_string;
+ }
+
+ list_T *l = tv_list_alloc(kListLenMayKnow);
+ qf_info_T *const qi = qf_alloc_stack(QFLT_INTERNAL);
- tv_dict_add_list(retdict, S_LEN("items"), l);
- status = OK;
+ if (qf_init_ext(qi, 0, NULL, NULL, &di->di_tv, errorformat,
+ true, (linenr_T)0, (linenr_T)0, NULL, NULL) > 0) {
+ (void)get_errorlist(qi, NULL, 0, 0, l);
+ qf_free(&qi->qf_lists[0]);
}
+ xfree(qi);
+
+ tv_dict_add_list(retdict, S_LEN("items"), l);
+ status = OK;
return status;
}
@@ -6500,7 +6553,7 @@ static int qf_setprop_curidx(qf_info_T *qi, qf_list_T *qfl, const dictitem_T *di
// If the specified index is '$', then use the last entry
if (di->di_tv.v_type == VAR_STRING
&& di->di_tv.vval.v_string != NULL
- && STRCMP(di->di_tv.vval.v_string, "$") == 0) {
+ && strcmp(di->di_tv.vval.v_string, "$") == 0) {
newidx = qfl->qf_count;
} else {
// Otherwise use the specified index
@@ -6668,7 +6721,8 @@ int set_errorlist(win_T *wp, list_T *list, int action, char *title, dict_T *what
return retval;
}
-/// Mark the context as in use for all the lists in a quickfix stack.
+/// Mark the quickfix context and callback function as in use for all the lists
+/// in a quickfix stack.
static bool mark_quickfix_ctx(qf_info_T *qi, int copyID)
{
bool abort = false;
@@ -6679,6 +6733,9 @@ static bool mark_quickfix_ctx(qf_info_T *qi, int copyID)
&& ctx->v_type != VAR_STRING && ctx->v_type != VAR_FLOAT) {
abort = set_ref_in_item(ctx, copyID, NULL, NULL);
}
+
+ Callback *cb = &qi->qf_lists[i].qf_qftf_cb;
+ abort = abort || set_ref_in_callback(cb, copyID, NULL, NULL);
}
return abort;
@@ -6693,6 +6750,11 @@ bool set_ref_in_quickfix(int copyID)
return abort;
}
+ abort = set_ref_in_callback(&qftf_cb, copyID, NULL, NULL);
+ if (abort) {
+ return abort;
+ }
+
FOR_ALL_TAB_WINDOWS(tp, win) {
if (win->w_llist != NULL) {
abort = mark_quickfix_ctx(win->w_llist, copyID);
@@ -6806,8 +6868,8 @@ void ex_cbuffer(exarg_T *eap)
char *qf_title = qf_cmdtitle(*eap->cmdlinep);
if (buf->b_sfname) {
- vim_snprintf((char *)IObuff, IOSIZE, "%s (%s)", qf_title, buf->b_sfname);
- qf_title = (char *)IObuff;
+ vim_snprintf(IObuff, IOSIZE, "%s (%s)", qf_title, buf->b_sfname);
+ qf_title = IObuff;
}
incr_quickfix_busy();
@@ -6885,43 +6947,45 @@ void ex_cexpr(exarg_T *eap)
// Evaluate the expression. When the result is a string or a list we can
// use it to fill the errorlist.
typval_T tv;
- if (eval0(eap->arg, &tv, &eap->nextcmd, true) != FAIL) {
- if ((tv.v_type == VAR_STRING && tv.vval.v_string != NULL)
- || tv.v_type == VAR_LIST) {
- incr_quickfix_busy();
- int res = qf_init_ext(qi, qi->qf_curlist, NULL, NULL, &tv, p_efm,
- (eap->cmdidx != CMD_caddexpr
- && eap->cmdidx != CMD_laddexpr),
- (linenr_T)0, (linenr_T)0,
- qf_cmdtitle(*eap->cmdlinep), NULL);
- if (qf_stack_empty(qi)) {
- decr_quickfix_busy();
- goto cleanup;
- }
- if (res >= 0) {
- qf_list_changed(qf_get_curlist(qi));
- }
- // 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);
- }
- // Jump to the first error for a new list and if autocmds didn't
- // free the list.
- if (res > 0
- && (eap->cmdidx == CMD_cexpr || eap->cmdidx == CMD_lexpr)
- && qflist_valid(wp, save_qfid)) {
- // display the first error
- qf_jump_first(qi, save_qfid, eap->forceit);
- }
+ if (eval0(eap->arg, &tv, &eap->nextcmd, true) == FAIL) {
+ return;
+ }
+
+ if ((tv.v_type == VAR_STRING && tv.vval.v_string != NULL)
+ || tv.v_type == VAR_LIST) {
+ incr_quickfix_busy();
+ int res = qf_init_ext(qi, qi->qf_curlist, NULL, NULL, &tv, p_efm,
+ (eap->cmdidx != CMD_caddexpr
+ && eap->cmdidx != CMD_laddexpr),
+ (linenr_T)0, (linenr_T)0,
+ qf_cmdtitle(*eap->cmdlinep), NULL);
+ if (qf_stack_empty(qi)) {
decr_quickfix_busy();
- } else {
- emsg(_("E777: String or List expected"));
+ goto cleanup;
}
-cleanup:
- tv_clear(&tv);
+ if (res >= 0) {
+ qf_list_changed(qf_get_curlist(qi));
+ }
+ // 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);
+ }
+ // Jump to the first error for a new list and if autocmds didn't
+ // free the list.
+ if (res > 0
+ && (eap->cmdidx == CMD_cexpr || eap->cmdidx == CMD_lexpr)
+ && qflist_valid(wp, save_qfid)) {
+ // display the first error
+ qf_jump_first(qi, save_qfid, eap->forceit);
+ }
+ decr_quickfix_busy();
+ } else {
+ emsg(_("E777: String or List expected"));
}
+cleanup:
+ tv_clear(&tv);
}
// Get the location list for ":lhelpgrep"
@@ -6952,10 +7016,10 @@ static void hgr_search_file(qf_list_T *qfl, char *fname, regmatch_T *p_regmatch)
linenr_T lnum = 1;
while (!vim_fgets(IObuff, IOSIZE, fd) && !got_int) {
- char *line = (char *)IObuff;
+ char *line = IObuff;
if (vim_regexec(p_regmatch, line, (colnr_T)0)) {
- int l = (int)STRLEN(line);
+ int l = (int)strlen(line);
// remove trailing CR, LF, spaces, etc.
while (l > 0 && line[l - 1] <= ' ') {
@@ -6970,8 +7034,8 @@ static void hgr_search_file(qf_list_T *qfl, char *fname, regmatch_T *p_regmatch)
line,
lnum,
0,
- (int)(p_regmatch->startp[0] - (char_u *)line) + 1, // col
- (int)(p_regmatch->endp[0] - (char_u *)line)
+ (int)(p_regmatch->startp[0] - line) + 1, // col
+ (int)(p_regmatch->endp[0] - line)
+ 1, // end_col
false, // vis_col
NULL, // search pattern
@@ -6980,13 +7044,13 @@ static void hgr_search_file(qf_list_T *qfl, char *fname, regmatch_T *p_regmatch)
true) // valid
== QF_FAIL) {
got_int = true;
- if ((char_u *)line != IObuff) {
+ if (line != IObuff) {
xfree(line);
}
break;
}
}
- if ((char_u *)line != IObuff) {
+ if (line != IObuff) {
xfree(line);
}
lnum++;
@@ -7012,9 +7076,9 @@ static void hgr_search_files_in_dir(qf_list_T *qfl, char *dirname, regmatch_T *p
for (int fi = 0; fi < fcount && !got_int; fi++) {
// Skip files for a different language.
if (lang != NULL
- && STRNICMP(lang, fnames[fi] + STRLEN(fnames[fi]) - 3, 2) != 0
+ && STRNICMP(lang, fnames[fi] + strlen(fnames[fi]) - 3, 2) != 0
&& !(STRNICMP(lang, "en", 2) == 0
- && STRNICMP("txt", fnames[fi] + STRLEN(fnames[fi]) - 3, 3)
+ && STRNICMP("txt", fnames[fi] + strlen(fnames[fi]) - 3, 3)
== 0)) {
continue;
}
@@ -7033,11 +7097,11 @@ static void hgr_search_in_rtp(qf_list_T *qfl, regmatch_T *p_regmatch, const char
FUNC_ATTR_NONNULL_ARG(1, 2)
{
// Go through all directories in 'runtimepath'
- char *p = (char *)p_rtp;
+ char *p = p_rtp;
while (*p != NUL && !got_int) {
- copy_option_part(&p, (char *)NameBuff, MAXPATHL, ",");
+ copy_option_part(&p, NameBuff, MAXPATHL, ",");
- hgr_search_files_in_dir(qfl, (char *)NameBuff, p_regmatch, (char *)lang);
+ hgr_search_files_in_dir(qfl, NameBuff, p_regmatch, (char *)lang);
}
}
@@ -7045,7 +7109,7 @@ static void hgr_search_in_rtp(qf_list_T *qfl, regmatch_T *p_regmatch, const char
void ex_helpgrep(exarg_T *eap)
{
qf_info_T *qi = &ql_info;
- char *au_name = NULL;
+ char *au_name = NULL;
switch (eap->cmdidx) {
case CMD_helpgrep:
@@ -7062,9 +7126,11 @@ void ex_helpgrep(exarg_T *eap)
}
}
+ bool updated = false;
// Make 'cpoptions' empty, the 'l' flag should not be used here.
char *const save_cpo = p_cpo;
- p_cpo = (char *)empty_option;
+ const bool save_cpo_allocated = is_option_allocated("cpo");
+ p_cpo = empty_option;
bool new_qi = false;
if (is_loclist_cmd(eap->cmdidx)) {
@@ -7092,14 +7158,26 @@ void ex_helpgrep(exarg_T *eap)
qfl->qf_ptr = qfl->qf_start;
qfl->qf_index = 1;
qf_list_changed(qfl);
- qf_update_buffer(qi, NULL);
+ updated = true;
}
- if ((char_u *)p_cpo == empty_option) {
+ if (p_cpo == empty_option) {
p_cpo = save_cpo;
} else {
- // Darn, some plugin changed the value.
- free_string_option((char_u *)save_cpo);
+ // Darn, some plugin changed the value. If it's still empty it was
+ // changed and restored, need to restore in the complicated way.
+ if (*p_cpo == NUL) {
+ set_option_value_give_err("cpo", 0L, save_cpo, 0);
+ }
+ if (save_cpo_allocated) {
+ free_string_option(save_cpo);
+ }
+ }
+
+ if (updated) {
+ // This may open a window and source scripts, do this after 'cpo' was
+ // restored.
+ qf_update_buffer(qi, NULL);
}
if (au_name != NULL) {
@@ -7128,7 +7206,9 @@ void ex_helpgrep(exarg_T *eap)
if (new_qi) {
ll_free_all(&qi);
}
- } else if (curwin->w_llist == NULL) {
+ } else if (curwin->w_llist == NULL && new_qi) {
+ // current window didn't have a location list associated with it
+ // before. Associate the new location list now.
curwin->w_llist = qi;
}
}
@@ -7158,14 +7238,14 @@ static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, typval_T *
}
/// "getloclist()" function
-void f_getloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_getloclist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
win_T *wp = find_win_by_nr_or_id(&argvars[0]);
get_qf_loc_list(false, wp, &argvars[1], rettv);
}
/// "getqflist()" functions
-void f_getqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_getqflist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
get_qf_loc_list(true, NULL, &argvars[0], rettv);
}
@@ -7252,7 +7332,7 @@ skip_args:
}
/// "setloclist()" function
-void f_setloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_setloclist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = -1;
@@ -7263,7 +7343,7 @@ void f_setloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "setqflist()" function
-void f_setqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_setqflist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
set_qf_ll_list(NULL, argvars, rettv);
}
diff --git a/src/nvim/rbuffer.c b/src/nvim/rbuffer.c
index 2f718e9c2e..1088dd3778 100644
--- a/src/nvim/rbuffer.c
+++ b/src/nvim/rbuffer.c
@@ -2,15 +2,16 @@
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include <assert.h>
+#include <stdbool.h>
#include <stddef.h>
#include <string.h>
+#include "nvim/macros.h"
#include "nvim/memory.h"
#include "nvim/rbuffer.h"
-#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "rbuffer.c.generated.h"
+# include "rbuffer.c.generated.h" // IWYU pragma: export
#endif
/// Creates a new `RBuffer` instance.
@@ -164,7 +165,8 @@ void rbuffer_consumed_compact(RBuffer *buf, size_t count)
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);
+ assert((size_t)(buf->write_ptr - buf->read_ptr) == buf->size
+ || buf->write_ptr == buf->start_ptr);
memmove(buf->start_ptr, buf->read_ptr, buf->size);
buf->read_ptr = buf->start_ptr;
buf->write_ptr = buf->read_ptr + buf->size;
diff --git a/src/nvim/rbuffer.h b/src/nvim/rbuffer.h
index 3ebbc9d82c..63d5119004 100644
--- a/src/nvim/rbuffer.h
+++ b/src/nvim/rbuffer.h
@@ -17,6 +17,8 @@
#include <stddef.h>
#include <stdint.h>
+struct rbuffer;
+
// Macros that simplify working with the read/write pointers directly by hiding
// ring buffer wrap logic. Some examples:
//
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index b7ec4bf94e..122f3e2020 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -1,9 +1,7 @@
// 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
-/*
- * Handling of regular expressions: vim_regcomp(), vim_regexec(), vim_regsub()
- */
+// Handling of regular expressions: vim_regcomp(), vim_regexec(), vim_regsub()
// By default: do not create debugging logs or files related to regular
// expressions, even when compiling with -DDEBUG.
@@ -15,21 +13,34 @@
#include <inttypes.h>
#include <stdbool.h>
#include <string.h>
+#include <sys/types.h>
#include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/eval/userfunc.h"
#include "nvim/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/keycodes.h"
+#include "nvim/macros.h"
#include "nvim/mark.h"
+#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
+#include "nvim/option_defs.h"
#include "nvim/os/input.h"
#include "nvim/plines.h"
-#include "nvim/profile.h"
+#include "nvim/pos.h"
#include "nvim/regexp.h"
+#include "nvim/regexp_defs.h"
#include "nvim/strings.h"
+#include "nvim/types.h"
+#include "nvim/undo_defs.h"
#include "nvim/vim.h"
#ifdef REGEXP_DEBUG
@@ -41,21 +52,17 @@
# define BT_REGEXP_DEBUG_LOG_NAME "bt_regexp_debug.log"
#endif
-/*
- * 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.
- */
+// 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.
#define Magic(x) ((int)(x) - 256)
#define un_Magic(x) ((x) + 256)
#define is_Magic(x) ((x) < 0)
-/*
- * We should define ftpr as a pointer to a function returning a pointer to
- * a function returning a pointer to a function ...
- * This is impossible, so we declare a pointer to a function returning a
- * pointer to a function returning void. This should work for all compilers.
- */
+// We should define ftpr as a pointer to a function returning a pointer to
+// a function returning a pointer to a function ...
+// This is impossible, so we declare a pointer to a function returning a
+// pointer to a function returning void. This should work for all compilers.
typedef void (*(*fptr_T)(int *, int))(void);
static int no_Magic(int x)
@@ -80,7 +87,7 @@ static int toggle_Magic(int x)
#define REGMAGIC 0234
// Utility definitions.
-#define UCHARAT(p) ((int)(*(char_u *)(p)))
+#define UCHARAT(p) ((int)(*(uint8_t *)(p)))
// Used for an error (down from) vim_regcomp(): give the error message, set
// rc_did_emsg and return NULL
@@ -97,20 +104,24 @@ static int toggle_Magic(int x)
#define MAX_LIMIT (32767L << 16L)
-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");
-static char_u e_unmatchedpp[] = N_("E53: Unmatched %s%%(");
-static char_u e_unmatchedp[] = N_("E54: Unmatched %s(");
-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_regexp_number_after_dot_pos_search[]
+static char e_missingbracket[] = N_("E769: Missing ] after %s[");
+static char e_reverse_range[] = N_("E944: Reverse range in character class");
+static char e_large_class[] = N_("E945: Range too large in character class");
+static char e_unmatchedpp[] = N_("E53: Unmatched %s%%(");
+static char e_unmatchedp[] = N_("E54: Unmatched %s(");
+static char e_unmatchedpar[] = N_("E55: Unmatched %s)");
+static char e_z_not_allowed[] = N_("E66: \\z( not allowed here");
+static char e_z1_not_allowed[] = N_("E67: \\z1 - \\z9 not allowed here");
+static char e_missing_sb[] = N_("E69: Missing ] after %s%%[");
+static char e_empty_sb[] = N_("E70: Empty %s%%[]");
+static char e_recursive[] = N_("E956: Cannot use pattern recursively");
+static char e_regexp_number_after_dot_pos_search_chr[]
= N_("E1204: No Number allowed after .: '\\%%%c'");
-static char_u e_substitute_nesting_too_deep[] = N_("E1290: substitute nesting too deep");
+static char e_nfa_regexp_missing_value_in_chr[]
+ = N_("E1273: (NFA regexp) missing value in '\\%%%c'");
+static char e_atom_engine_must_be_at_start_of_pattern[]
+ = N_("E1281: Atom '\\%%#=%c' must be at the start of the pattern");
+static char e_substitute_nesting_too_deep[] = N_("E1290: substitute nesting too deep");
#define NOT_MULTI 0
#define MULTI_ONE 1
@@ -137,30 +148,26 @@ static int re_multi_type(int c)
return NOT_MULTI;
}
-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).
- * \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
- */
+static char *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).
+// \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
static char REGEXP_INRANGE[] = "]^-n\\";
static char REGEXP_ABBR[] = "nrtebdoxuU";
-/*
- * Translate '\x' to its control character, except "\n", which is Magic.
- */
+// Translate '\x' to its control character, except "\n", which is Magic.
static int backslash_trans(int c)
{
switch (c) {
@@ -181,8 +188,7 @@ static int backslash_trans(int c)
/// recognized. Otherwise "pp" is advanced to after the item.
static int get_char_class(char **pp)
{
- static const char *(class_names[]) =
- {
+ static const char *(class_names[]) = {
"alnum:]",
#define CLASS_ALNUM 0
"alpha:]",
@@ -227,8 +233,8 @@ static int get_char_class(char **pp)
if ((*pp)[1] == ':') {
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;
+ if (strncmp(*pp + 2, class_names[i], strlen(class_names[i])) == 0) {
+ *pp += strlen(class_names[i]) + 2;
return i;
}
}
@@ -236,11 +242,9 @@ static int get_char_class(char **pp)
return CLASS_NONE;
}
-/*
- * Specific version of character class functions.
- * Using a table to keep this fast.
- */
-static short class_tab[256];
+// Specific version of character class functions.
+// Using a table to keep this fast.
+static int16_t class_tab[256];
#define RI_DIGIT 0x01
#define RI_HEX 0x02
@@ -312,24 +316,18 @@ static int re_has_z; ///< \z item detected
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 magic_T reg_magic; ///< magicness of the pattern
static int reg_string; // matching with a string instead of a buffer
// line
static int reg_strict; // "[abc" is illegal
-/*
- * META contains all characters that may be magic, except '^' and '$'.
- */
+// META contains all characters that may be magic, except '^' and '$'.
// uncrustify:off
// META[] is used often enough to justify turning it into a table.
-static char_u META_flags[] = {
+static uint8_t 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,
// % & ( ) * + .
@@ -363,7 +361,7 @@ static int nextchr; // used for ungetchr()
#define REG_NPAREN 3 // \%(\)
typedef struct {
- char_u *regparse;
+ char *regparse;
int prevchr_len;
int curchr;
int prevchr;
@@ -388,21 +386,19 @@ int re_multiline(const regprog_T *prog)
return prog->regflags & RF_HASNL;
}
-/*
- * Check for an equivalence class name "[=a=]". "pp" points to the '['.
- * Returns a character representing the class. Zero means that no item was
- * recognized. Otherwise "pp" is advanced to after the item.
- */
+// Check for an equivalence class name "[=a=]". "pp" points to the '['.
+// Returns a character representing the class. Zero means that no item was
+// recognized. Otherwise "pp" is advanced to after the item.
static int get_equi_class(char **pp)
{
int c;
int l = 1;
- char_u *p = (char_u *)(*pp);
+ char *p = *pp;
if (p[1] == '=' && p[2] != NUL) {
- l = utfc_ptr2len((char *)p + 2);
+ l = utfc_ptr2len(p + 2);
if (p[l + 2] == '=' && p[l + 3] == ']') {
- c = utf_ptr2char((char *)p + 2);
+ c = utf_ptr2char(p + 2);
*pp += l + 4;
return c;
}
@@ -410,22 +406,20 @@ static int get_equi_class(char **pp)
return 0;
}
-/*
- * Check for a collating element "[.a.]". "pp" points to the '['.
- * Returns a character. Zero means that no item was recognized. Otherwise
- * "pp" is advanced to after the item.
- * Currently only single characters are recognized!
- */
+// Check for a collating element "[.a.]". "pp" points to the '['.
+// Returns a character. Zero means that no item was recognized. Otherwise
+// "pp" is advanced to after the item.
+// Currently only single characters are recognized!
static int get_coll_element(char **pp)
{
int c;
int l = 1;
- char_u *p = (char_u *)(*pp);
+ char *p = *pp;
if (p[0] != NUL && p[1] == '.' && p[2] != NUL) {
- l = utfc_ptr2len((char *)p + 2);
+ l = utfc_ptr2len(p + 2);
if (p[l + 2] == '.' && p[l + 3] == ']') {
- c = utf_ptr2char((char *)p + 2);
+ c = utf_ptr2char(p + 2);
*pp += l + 4;
return c;
}
@@ -443,7 +437,7 @@ static void get_cpo_flags(void)
/// Skip over a "[]" range.
/// "p" must point to the character after the '['.
/// The returned pointer is on the matching ']', or the terminating NUL.
-static char_u *skip_anyof(char *p)
+static char *skip_anyof(char *p)
{
int l;
@@ -462,9 +456,9 @@ static char_u *skip_anyof(char *p)
MB_PTR_ADV(p);
}
} else if (*p == '\\'
- && (vim_strchr(REGEXP_INRANGE, p[1]) != NULL
+ && (vim_strchr(REGEXP_INRANGE, (uint8_t)p[1]) != NULL
|| (!reg_cpo_lit
- && vim_strchr(REGEXP_ABBR, p[1]) != NULL))) {
+ && vim_strchr(REGEXP_ABBR, (uint8_t)p[1]) != NULL))) {
p += 2;
} else if (*p == '[') {
if (get_char_class(&p) == CLASS_NONE
@@ -478,20 +472,42 @@ static char_u *skip_anyof(char *p)
}
}
- return (char_u *)p;
+ return p;
}
/// Skip past regular expression.
-/// Stop at end of "startp" or where "dirc" is found ('/', '?', etc).
+/// Stop at end of "startp" or where "delim" is found ('/', '?', etc).
/// Take care of characters with a backslash in front of it.
/// Skip strings inside [ and ].
+char *skip_regexp(char *startp, int delim, int magic)
+{
+ return skip_regexp_ex(startp, delim, magic, NULL, NULL, NULL);
+}
+
+/// Call skip_regexp() and when the delimiter does not match give an error and
+/// return NULL.
+char *skip_regexp_err(char *startp, int delim, int magic)
+{
+ char *p = skip_regexp(startp, delim, magic);
+
+ if (*p != delim) {
+ semsg(_("E654: missing delimiter after search pattern: %s"), startp);
+ return NULL;
+ }
+ return p;
+}
+
+/// skip_regexp() with extra arguments:
/// When "newp" is not NULL and "dirc" is '?', make an allocated copy of the
/// expression and change "\?" to "?". If "*newp" is not NULL the expression
/// is changed in-place.
-char_u *skip_regexp(char_u *startp, int dirc, int magic, char **newp)
+/// If a "\?" is changed to "?" then "dropped" is incremented, unless NULL.
+/// If "magic_val" is not NULL, returns the effective magicness of the pattern
+char *skip_regexp_ex(char *startp, int dirc, int magic, char **newp, int *dropped,
+ magic_T *magic_val)
{
- int mymagic;
- char_u *p = startp;
+ magic_T mymagic;
+ char *p = startp;
if (magic) {
mymagic = MAGIC_ON;
@@ -506,7 +522,7 @@ char_u *skip_regexp(char_u *startp, int dirc, int magic, char **newp)
}
if ((p[0] == '[' && mymagic >= MAGIC_ON)
|| (p[0] == '\\' && p[1] == '[' && mymagic <= MAGIC_OFF)) {
- p = skip_anyof((char *)p + 1);
+ p = skip_anyof(p + 1);
if (p[0] == NUL) {
break;
}
@@ -514,8 +530,11 @@ char_u *skip_regexp(char_u *startp, int dirc, int magic, char **newp)
if (dirc == '?' && newp != NULL && p[1] == '?') {
// change "\?" to "?", make a copy first.
if (*newp == NULL) {
- *newp = (char *)vim_strsave(startp);
- p = (char_u *)(*newp) + (p - startp);
+ *newp = xstrdup(startp);
+ p = *newp + (p - startp);
+ }
+ if (dropped != NULL) {
+ (*dropped)++;
}
STRMOVE(p, p + 1);
} else {
@@ -528,6 +547,9 @@ char_u *skip_regexp(char_u *startp, int dirc, int magic, char **newp)
}
}
}
+ if (magic_val != NULL) {
+ *magic_val = mymagic;
+ }
return p;
}
@@ -536,25 +558,21 @@ 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
-/*
- * Start parsing at "str".
- */
-static void initchr(char_u *str)
+// Start parsing at "str".
+static void initchr(char *str)
{
- regparse = (char *)str;
+ regparse = str;
prevchr_len = 0;
curchr = prevprevchr = prevchr = nextchr = -1;
at_start = true;
prev_at_start = false;
}
-/*
- * Save the current parse state, so that it can be restored and parsing
- * starts in the same state again.
- */
+// Save the current parse state, so that it can be restored and parsing
+// starts in the same state again.
static void save_parse_state(parse_state_T *ps)
{
- ps->regparse = (char_u *)regparse;
+ ps->regparse = regparse;
ps->prevchr_len = prevchr_len;
ps->curchr = curchr;
ps->prevchr = prevchr;
@@ -565,12 +583,10 @@ static void save_parse_state(parse_state_T *ps)
ps->regnpar = regnpar;
}
-/*
- * Restore a previously saved parse state.
- */
+// Restore a previously saved parse state.
static void restore_parse_state(parse_state_T *ps)
{
- regparse = (char *)ps->regparse;
+ regparse = ps->regparse;
prevchr_len = ps->prevchr_len;
curchr = ps->curchr;
prevchr = ps->prevchr;
@@ -581,9 +597,7 @@ static void restore_parse_state(parse_state_T *ps)
regnpar = ps->regnpar;
}
-/*
- * Get the next character without advancing.
- */
+// Get the next character without advancing.
static int peekchr(void)
{
static int after_slash = false;
@@ -663,7 +677,7 @@ static int peekchr(void)
// '$' is only magic as the very last char and if it's in front of
// either "\|", "\)", "\&", or "\n"
if (reg_magic >= MAGIC_OFF) {
- char_u *p = (char_u *)regparse + 1;
+ uint8_t *p = (uint8_t *)regparse + 1;
bool is_magic_all = (reg_magic == MAGIC_ALL);
// ignore \c \C \m \M \v \V and \Z after '$'
@@ -710,9 +724,7 @@ static int peekchr(void)
after_slash--;
curchr = toggle_Magic(curchr);
} else if (vim_strchr(REGEXP_ABBR, c)) {
- /*
- * Handle abbreviations, like "\t" for TAB -- webb
- */
+ // Handle abbreviations, like "\t" for TAB -- webb
curchr = backslash_trans(c);
} else if (reg_magic == MAGIC_NONE && (c == '$' || c == '^')) {
curchr = toggle_Magic(c);
@@ -731,9 +743,7 @@ static int peekchr(void)
return curchr;
}
-/*
- * Eat one lexed character. Do this in a way that we can undo it.
- */
+// Eat one lexed character. Do this in a way that we can undo it.
static void skipchr(void)
{
// peekchr() eats a backslash, do the same here
@@ -755,10 +765,8 @@ static void skipchr(void)
nextchr = -1;
}
-/*
- * Skip a character while keeping the value of prev_at_start for at_start.
- * prevchr and prevprevchr are also kept.
- */
+// Skip a character while keeping the value of prev_at_start for at_start.
+// prevchr and prevprevchr are also kept.
static void skipchr_keepstart(void)
{
int as = prev_at_start;
@@ -771,10 +779,8 @@ static void skipchr_keepstart(void)
prevprevchr = prpr;
}
-/*
- * Get the next character from the pattern. We know about magic and such, so
- * therefore we need a lexical analyzer.
- */
+// Get the next character from the pattern. We know about magic and such, so
+// therefore we need a lexical analyzer.
static int getchr(void)
{
int chr = peekchr();
@@ -783,9 +789,7 @@ static int getchr(void)
return chr;
}
-/*
- * put character back. Works only once!
- */
+// put character back. Works only once!
static void ungetchr(void)
{
nextchr = curchr;
@@ -799,15 +803,13 @@ static void ungetchr(void)
regparse -= prevchr_len;
}
-/*
- * Get and return the value of the hex string at the current position.
- * Return -1 if there is no valid hex number.
- * The position is updated:
- * blahblah\%x20asdf
- * 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.
- */
+// Get and return the value of the hex string at the current position.
+// Return -1 if there is no valid hex number.
+// The position is updated:
+// blahblah\%x20asdf
+// 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.
static int64_t gethexchrs(int maxinputlen)
{
int64_t nr = 0;
@@ -830,10 +832,8 @@ static int64_t gethexchrs(int maxinputlen)
return nr;
}
-/*
- * Get and return the value of the decimal string immediately after the
- * current position. Return -1 for invalid. Consumes all digits.
- */
+// Get and return the value of the decimal string immediately after the
+// current position. Return -1 for invalid. Consumes all digits.
static int64_t getdecchrs(void)
{
int64_t nr = 0;
@@ -857,14 +857,12 @@ static int64_t getdecchrs(void)
return nr;
}
-/*
- * get and return the value of the octal string immediately after the current
- * position. Return -1 for invalid, or 0-255 for valid. Smart enough to handle
- * 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
- */
+// get and return the value of the octal string immediately after the current
+// position. Return -1 for invalid, or 0-255 for valid. Smart enough to handle
+// 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
static int64_t getoctchrs(void)
{
int64_t nr = 0;
@@ -887,16 +885,14 @@ static int64_t getoctchrs(void)
return nr;
}
-/*
- * read_limits - Read two integers to be taken as a minimum and maximum.
- * If the first character is '-', then the range is reversed.
- * Should end with 'end'. If minval is missing, zero is default, if maxval is
- * missing, a very big number is the default.
- */
+// read_limits - Read two integers to be taken as a minimum and maximum.
+// If the first character is '-', then the range is reversed.
+// Should end with 'end'. If minval is missing, zero is default, if maxval is
+// missing, a very big number is the default.
static int read_limits(long *minval, long *maxval)
{
int reverse = false;
- char_u *first_char;
+ char *first_char;
long tmp;
if (*regparse == '-') {
@@ -904,7 +900,7 @@ static int read_limits(long *minval, long *maxval)
regparse++;
reverse = true;
}
- first_char = (char_u *)regparse;
+ first_char = regparse;
*minval = getdigits_long(&regparse, false, 0);
if (*regparse == ',') { // There is a comma.
if (ascii_isdigit(*++regparse)) {
@@ -924,10 +920,8 @@ static int read_limits(long *minval, long *maxval)
EMSG2_RET_FAIL(_("E554: Syntax error in %s{...}"), reg_magic == MAGIC_ALL);
}
- /*
- * Reverse the range if there was a '-', or make sure it is in the right
- * order otherwise.
- */
+ // Reverse the range if there was a '-', or make sure it is in the right
+ // order otherwise.
if ((!reverse && *minval > *maxval) || (reverse && *minval < *maxval)) {
tmp = *minval;
*minval = *maxval;
@@ -937,18 +931,14 @@ static int read_limits(long *minval, long *maxval)
return OK;
}
-/*
- * vim_regexec and friends
- */
+// vim_regexec and friends
-/*
- * Global work variables for vim_regexec().
- */
+// Global work variables for vim_regexec().
// 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 uint8_t *reg_tofree = NULL;
static unsigned reg_tofreelen;
// Structure used to store the execution state of the regex engine.
@@ -969,10 +959,12 @@ static unsigned reg_tofreelen;
typedef struct {
regmatch_T *reg_match;
regmmatch_T *reg_mmatch;
- char_u **reg_startp;
- char_u **reg_endp;
+
+ uint8_t **reg_startp;
+ uint8_t **reg_endp;
lpos_T *reg_startpos;
lpos_T *reg_endpos;
+
win_T *reg_win;
buf_T *reg_buf;
linenr_T reg_firstlnum;
@@ -981,8 +973,8 @@ 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 "line"
+ uint8_t *line; ///< start of current line
+ uint8_t *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
@@ -1026,10 +1018,8 @@ static bool reg_iswordc(int c)
return vim_iswordc_buf(c, rex.reg_buf);
}
-/*
- * Get pointer to the line "lnum", which is relative to "reg_firstlnum".
- */
-static char_u *reg_getline(linenr_T lnum)
+// Get pointer to the line "lnum", which is relative to "reg_firstlnum".
+static char *reg_getline(linenr_T lnum)
{
// when looking behind for a match/no-match lnum is negative. But we
// can't go before line 1
@@ -1038,22 +1028,20 @@ static char_u *reg_getline(linenr_T lnum)
}
if (lnum > rex.reg_maxline) {
// Must have matched the "\n" in the last line.
- return (char_u *)"";
+ return "";
}
return ml_get_buf(rex.reg_buf, rex.reg_firstlnum + lnum, false);
}
-static char_u *reg_startzp[NSUBEXP]; // Workspace to mark beginning
-static char_u *reg_endzp[NSUBEXP]; // and end of \z(...\) matches
+static uint8_t *reg_startzp[NSUBEXP]; // Workspace to mark beginning
+static uint8_t *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)
-/*
- * Create a new extmatch and mark it as referenced once.
- */
+// Create a new extmatch and mark it as referenced once.
static reg_extmatch_T *make_extmatch(void)
FUNC_ATTR_NONNULL_RET
{
@@ -1062,9 +1050,7 @@ static reg_extmatch_T *make_extmatch(void)
return em;
}
-/*
- * Add a reference to an extmatch.
- */
+// Add a reference to an extmatch.
reg_extmatch_T *ref_extmatch(reg_extmatch_T *em)
{
if (em != NULL) {
@@ -1073,10 +1059,8 @@ reg_extmatch_T *ref_extmatch(reg_extmatch_T *em)
return em;
}
-/*
- * Remove a reference to an extmatch. If there are no references left, free
- * the info.
- */
+// Remove a reference to an extmatch. If there are no references left, free
+// the info.
void unref_extmatch(reg_extmatch_T *em)
{
int i;
@@ -1093,7 +1077,8 @@ void unref_extmatch(reg_extmatch_T *em)
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),
+ return mb_get_class_tab((char *)rex.input - 1 -
+ utf_head_off((char *)rex.line, (char *)rex.input - 1),
rex.reg_buf->b_chartab);
}
return -1;
@@ -1111,8 +1096,8 @@ static bool reg_match_visual(void)
colnr_T start2, end2;
colnr_T curswant;
- // Check if the buffer is the current buffer.
- if (rex.reg_buf != curbuf || VIsual.lnum == 0) {
+ // Check if the buffer is the current buffer and not using a string.
+ if (rex.reg_buf != curbuf || VIsual.lnum == 0 || !REG_MULTI) {
return false;
}
@@ -1162,10 +1147,10 @@ static bool reg_match_visual(void)
}
// getvvcol() flushes rex.line, need to get it again
- rex.line = reg_getline(rex.lnum);
+ rex.line = (uint8_t *)reg_getline(rex.lnum);
rex.input = rex.line + col;
- unsigned int cols_u = win_linetabsize(wp, rex.line, col);
+ unsigned int cols_u = win_linetabsize(wp, rex.reg_firstlnum + rex.lnum, (char *)rex.line, col);
assert(cols_u <= MAXCOL);
colnr_T cols = (colnr_T)cols_u;
if (cols < start || cols > end - (*p_sel == 'e')) {
@@ -1175,10 +1160,8 @@ static bool reg_match_visual(void)
return true;
}
-/*
- * Check the regexp program for its magic number.
- * Return true if it's wrong.
- */
+// Check the regexp program for its magic number.
+// Return true if it's wrong.
static int prog_magic_wrong(void)
{
regprog_T *prog;
@@ -1196,62 +1179,62 @@ static int prog_magic_wrong(void)
return false;
}
-/*
- * Cleanup the subexpressions, if this wasn't done yet.
- * This construction is used to clear the subexpressions only when they are
- * used (to increase speed).
- */
+// Cleanup the subexpressions, if this wasn't done yet.
+// This construction is used to clear the subexpressions only when they are
+// used (to increase speed).
static void cleanup_subexpr(void)
{
- if (rex.need_clear_subexpr) {
- if (REG_MULTI) {
- // Use 0xff to set lnum to -1
- memset(rex.reg_startpos, 0xff, sizeof(lpos_T) * NSUBEXP);
- memset(rex.reg_endpos, 0xff, sizeof(lpos_T) * NSUBEXP);
- } else {
- memset(rex.reg_startp, 0, sizeof(char_u *) * NSUBEXP);
- memset(rex.reg_endp, 0, sizeof(char_u *) * NSUBEXP);
- }
- rex.need_clear_subexpr = false;
+ if (!rex.need_clear_subexpr) {
+ return;
+ }
+
+ if (REG_MULTI) {
+ // Use 0xff to set lnum to -1
+ memset(rex.reg_startpos, 0xff, sizeof(lpos_T) * NSUBEXP);
+ memset(rex.reg_endpos, 0xff, sizeof(lpos_T) * NSUBEXP);
+ } else {
+ memset(rex.reg_startp, 0, sizeof(char *) * NSUBEXP);
+ memset(rex.reg_endp, 0, sizeof(char *) * NSUBEXP);
}
+ rex.need_clear_subexpr = false;
}
static void cleanup_zsubexpr(void)
{
- if (rex.need_clear_zsubexpr) {
- if (REG_MULTI) {
- // Use 0xff to set lnum to -1
- memset(reg_startzpos, 0xff, sizeof(lpos_T) * NSUBEXP);
- memset(reg_endzpos, 0xff, sizeof(lpos_T) * NSUBEXP);
- } else {
- memset(reg_startzp, 0, sizeof(char_u *) * NSUBEXP);
- memset(reg_endzp, 0, sizeof(char_u *) * NSUBEXP);
- }
- rex.need_clear_zsubexpr = false;
+ if (!rex.need_clear_zsubexpr) {
+ return;
+ }
+
+ if (REG_MULTI) {
+ // Use 0xff to set lnum to -1
+ memset(reg_startzpos, 0xff, sizeof(lpos_T) * NSUBEXP);
+ memset(reg_endzpos, 0xff, sizeof(lpos_T) * NSUBEXP);
+ } else {
+ memset(reg_startzp, 0, sizeof(char *) * NSUBEXP);
+ memset(reg_endzp, 0, sizeof(char *) * NSUBEXP);
}
+ rex.need_clear_zsubexpr = false;
}
// Advance rex.lnum, rex.line and rex.input to the next line.
static void reg_nextline(void)
{
- rex.line = reg_getline(++rex.lnum);
+ rex.line = (uint8_t *)reg_getline(++rex.lnum);
rex.input = rex.line;
fast_breakcheck();
}
-/*
- * 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.
- */
+// 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)
{
linenr_T clnum = start_lnum;
colnr_T ccol = start_col;
int len;
- char_u *p;
+ char *p;
if (bytelen != NULL) {
*bytelen = 0;
@@ -1260,7 +1243,7 @@ static int match_with_backref(linenr_T start_lnum, colnr_T start_col, linenr_T e
// Since getting one line may invalidate the other, need to make copy.
// Slow!
if (rex.line != reg_tofree) {
- len = (int)STRLEN(rex.line);
+ len = (int)strlen((char *)rex.line);
if (reg_tofree == NULL || len >= (int)reg_tofreelen) {
len += 50; // get some extra
xfree(reg_tofree);
@@ -1279,10 +1262,10 @@ static int match_with_backref(linenr_T start_lnum, colnr_T start_col, linenr_T e
if (clnum == end_lnum) {
len = end_col - ccol;
} else {
- len = (int)STRLEN(p + ccol);
+ len = (int)strlen(p + ccol);
}
- if (cstrncmp(p + ccol, rex.input, &len) != 0) {
+ if (cstrncmp(p + ccol, (char *)rex.input, &len) != 0) {
return RA_NOMATCH; // doesn't match
}
if (bytelen != NULL) {
@@ -1328,8 +1311,7 @@ typedef struct {
} decomp_T;
// 0xfb20 - 0xfb4f
-static decomp_T decomp_table[0xfb4f - 0xfb20 + 1] =
-{
+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
@@ -1395,23 +1377,23 @@ static void mb_decompose(int c, int *c1, int *c2, int *c3)
}
}
-// Compare two strings, ignore case if rex.reg_ic set.
-// Return 0 if strings match, non-zero otherwise.
-// Correct the length "*n" when composing characters are ignored.
-static int cstrncmp(char_u *s1, char_u *s2, int *n)
+/// Compare two strings, ignore case if rex.reg_ic set.
+/// Return 0 if strings match, non-zero otherwise.
+/// Correct the length "*n" when composing characters are ignored.
+static int cstrncmp(char *s1, char *s2, int *n)
{
int result;
if (!rex.reg_ic) {
- result = STRNCMP(s1, s2, *n);
+ result = strncmp(s1, s2, (size_t)(*n));
} else {
assert(*n >= 0);
- result = mb_strnicmp(s1, s2, (size_t)*n);
+ result = mb_strnicmp(s1, s2, (size_t)(*n));
}
// if it failed and it's utf8 and we want to combineignore:
if (result != 0 && rex.reg_icombine) {
- char_u *str1, *str2;
+ char *str1, *str2;
int c1, c2, c11, c12;
int junk;
@@ -1421,12 +1403,12 @@ static int cstrncmp(char_u *s1, char_u *s2, int *n)
str2 = s2;
c1 = c2 = 0;
while ((int)(str1 - s1) < *n) {
- c1 = mb_ptr2char_adv((const char_u **)&str1);
- c2 = mb_ptr2char_adv((const char_u **)&str2);
+ c1 = mb_ptr2char_adv((const char **)&str1);
+ c2 = mb_ptr2char_adv((const char **)&str2);
- /* decompose the character if necessary, into 'base' characters
- * because I don't care about Arabic, I will hard-code the Hebrew
- * which I *do* care about! So sue me... */
+ // decompose the character if necessary, into 'base' characters
+ // because I don't care about Arabic, I will hard-code the Hebrew
+ // which I *do* care about! So sue me...
if (c1 != c2 && (!rex.reg_ic || utf_fold(c1) != utf_fold(c2))) {
// decomposition necessary?
mb_decompose(c1, &c11, &junk, &junk);
@@ -1456,21 +1438,21 @@ static int cstrncmp(char_u *s1, char_u *s2, int *n)
/// @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)
+static inline char *cstrchr(const char *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);
+ 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((char *)p)) {
- if (utf_fold(utf_ptr2char((char *)p)) == folded_c) {
- return (char_u *)p;
+ for (const char *p = s; *p != NUL; p += utfc_ptr2len(p)) {
+ if (utf_fold(utf_ptr2char(p)) == folded_c) {
+ return (char *)p;
}
}
return NULL;
@@ -1482,11 +1464,11 @@ static inline char_u *cstrchr(const char_u *const s, const int c)
} else if (ASCII_ISLOWER(c)) {
cc = TOUPPER_ASC(c);
} else {
- return (char_u *)vim_strchr((char *)s, c);
+ return vim_strchr(s, c);
}
char tofind[] = { (char)c, (char)cc, NUL };
- return (char_u *)strpbrk((const char *)s, tofind);
+ return strpbrk(s, tofind);
}
////////////////////////////////////////////////////////////////
@@ -1523,32 +1505,30 @@ static fptr_T do_Lower(int *d, int c)
return (fptr_T)do_Lower;
}
-/*
- * regtilde(): Replace tildes in the pattern by the old pattern.
- *
- * Short explanation of the tilde: It stands for the previous replacement
- * pattern. If that previous pattern also contains a ~ we should go back a
- * step further... But we insert the previous pattern into the current one
- * and remember that.
- * This still does not handle the case where "magic" changes. So require the
- * user to keep his hands off of "magic".
- *
- * The tildes are parsed once before the first call to vim_regsub().
- */
-char_u *regtilde(char_u *source, int magic, bool preview)
-{
- char_u *newsub = source;
- char_u *tmpsub;
- char_u *p;
+/// regtilde(): Replace tildes in the pattern by the old pattern.
+///
+/// Short explanation of the tilde: It stands for the previous replacement
+/// pattern. If that previous pattern also contains a ~ we should go back a
+/// step further... But we insert the previous pattern into the current one
+/// and remember that.
+/// This still does not handle the case where "magic" changes. So require the
+/// user to keep his hands off of "magic".
+///
+/// The tildes are parsed once before the first call to vim_regsub().
+char *regtilde(char *source, int magic, bool preview)
+{
+ char *newsub = source;
+ char *tmpsub;
+ char *p;
int len;
int prevlen;
- for (p = newsub; *p; ++p) {
+ 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
- prevlen = (int)STRLEN(reg_prev_sub);
- tmpsub = xmalloc(STRLEN(newsub) + (size_t)prevlen);
+ prevlen = (int)strlen(reg_prev_sub);
+ tmpsub = xmalloc(strlen(newsub) + (size_t)prevlen);
// copy prefix
len = (int)(p - newsub); // not including ~
memmove(tmpsub, newsub, (size_t)len);
@@ -1575,7 +1555,7 @@ char_u *regtilde(char_u *source, int magic, bool preview)
if (*p == '\\' && p[1]) { // skip escaped characters
p++;
}
- p += utfc_ptr2len((char *)p) - 1;
+ p += utfc_ptr2len(p) - 1;
}
}
@@ -1584,7 +1564,7 @@ char_u *regtilde(char_u *source, int magic, bool preview)
// Store a copy of newsub in reg_prev_sub. It is always allocated,
// because recursive calls may make the returned string invalid.
xfree(reg_prev_sub);
- reg_prev_sub = vim_strsave(newsub);
+ reg_prev_sub = xstrdup(newsub);
}
return newsub;
@@ -1607,12 +1587,12 @@ 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, ufunc_T *fp)
FUNC_ATTR_NONNULL_ALL
{
typval_T *listarg = argv + argskip;
- if (argcount == argskip) {
+ if (!fp->uf_varargs && fp->uf_args.ga_len <= argskip) {
// called function doesn't take a submatches argument
return argskip;
}
@@ -1623,14 +1603,14 @@ static int fill_submatch_list(int argc FUNC_ATTR_UNUSED, typval_T *argv, int arg
// There are always 10 list items in staticList10_T.
listitem_T *li = tv_list_first(listarg->vval.v_list);
for (int i = 0; i < 10; i++) {
- char_u *s = rsm.sm_match->startp[i];
+ char *s = rsm.sm_match->startp[i];
if (s == NULL || rsm.sm_match->endp[i] == NULL) {
s = NULL;
} else {
- s = vim_strnsave(s, (size_t)(rsm.sm_match->endp[i] - s));
+ s = xstrnsave(s, (size_t)(rsm.sm_match->endp[i] - s));
}
TV_LIST_ITEM_TV(li)->v_type = VAR_STRING;
- TV_LIST_ITEM_TV(li)->vval.v_string = (char *)s;
+ TV_LIST_ITEM_TV(li)->vval.v_string = s;
li = TV_LIST_ITEM_NEXT(argv->vval.v_list, li);
}
return argskip + 1;
@@ -1660,8 +1640,7 @@ static void clear_submatch_list(staticList10_T *sl)
/// 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 destlen,
- int flags)
+int vim_regsub(regmatch_T *rmp, char *source, typval_T *expr, char *dest, int destlen, int flags)
{
regexec_T rex_save;
bool rex_in_use_save = rex_in_use;
@@ -1687,7 +1666,7 @@ int vim_regsub(regmatch_T *rmp, char_u *source, typval_T *expr, char_u *dest, in
return result;
}
-int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *dest, int destlen,
+int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char *source, char *dest, int destlen,
int flags)
{
regexec_T rex_save;
@@ -1717,7 +1696,7 @@ int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *de
// 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 };
+static char *eval_result[MAX_REGSUB_NESTING] = { NULL, NULL, NULL, NULL };
#if defined(EXITFREE)
void free_resub_eval_result(void)
@@ -1728,11 +1707,11 @@ void free_resub_eval_result(void)
}
#endif
-static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int destlen, int flags)
+static int vim_regsub_both(char *source, typval_T *expr, char *dest, int destlen, int flags)
{
- char_u *src;
- char_u *dst;
- char_u *s;
+ char *src;
+ char *dst;
+ char *s;
int c;
int cc;
int no = -1;
@@ -1769,7 +1748,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int des
if (copy) {
if (eval_result[nested] != NULL) {
STRCPY(dest, eval_result[nested]);
- dst += STRLEN(eval_result[nested]);
+ dst += strlen(eval_result[nested]);
XFREE_CLEAR(eval_result[nested]);
}
} else {
@@ -1805,17 +1784,17 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int des
argv[0].v_type = VAR_LIST;
argv[0].vval.v_list = &matchList.sl_list;
funcexe_T funcexe = FUNCEXE_INIT;
- funcexe.argv_func = fill_submatch_list;
- funcexe.evaluate = true;
+ funcexe.fe_argv_func = fill_submatch_list;
+ funcexe.fe_evaluate = true;
if (expr->v_type == VAR_FUNC) {
- s = (char_u *)expr->vval.v_string;
- call_func((char *)s, -1, &rettv, 1, argv, &funcexe);
+ s = expr->vval.v_string;
+ call_func(s, -1, &rettv, 1, argv, &funcexe);
} else if (expr->v_type == VAR_PARTIAL) {
partial_T *partial = expr->vval.v_partial;
- s = (char_u *)partial_name(partial);
- funcexe.partial = partial;
- call_func((char *)s, -1, &rettv, 1, argv, &funcexe);
+ s = partial_name(partial);
+ funcexe.fe_partial = partial;
+ call_func(s, -1, &rettv, 1, argv, &funcexe);
}
if (tv_list_len(&matchList.sl_list) > 0) {
// fill_submatch_list() was called.
@@ -1826,14 +1805,14 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int des
eval_result[nested] = NULL;
} else {
char buf[NUMBUFLEN];
- eval_result[nested] = (char_u *)tv_get_string_buf_chk(&rettv, buf);
+ eval_result[nested] = (char *)tv_get_string_buf_chk(&rettv, buf);
if (eval_result[nested] != NULL) {
- eval_result[nested] = vim_strsave(eval_result[nested]);
+ eval_result[nested] = xstrdup(eval_result[nested]);
}
}
tv_clear(&rettv);
} else {
- eval_result[nested] = (char_u *)eval_to_string((char *)source + 2, NULL, true);
+ eval_result[nested] = eval_to_string(source + 2, NULL, true);
}
nesting--;
@@ -1848,12 +1827,11 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int des
*s = CAR;
} else if (*s == '\\' && s[1] != NUL) {
s++;
- /* Change NL to CR here too, so that this works:
- * :s/abc\\\ndef/\="aaa\\\nbbb"/ on text:
- * abc\
- * def
- * Not when called from vim_regexec_nl().
- */
+ // Change NL to CR here too, so that this works:
+ // :s/abc\\\ndef/\="aaa\\\nbbb"/ on text:
+ // abc{backslash}
+ // def
+ // Not when called from vim_regexec_nl().
if (*s == NL && !rsm.sm_line_lbr) {
*s = CAR;
}
@@ -1862,12 +1840,12 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int des
}
if (had_backslash && (flags & REGSUB_BACKSLASH)) {
// Backslashes will be consumed, need to double them.
- s = vim_strsave_escaped(eval_result[nested], (char_u *)"\\");
+ s = vim_strsave_escaped(eval_result[nested], "\\");
xfree(eval_result[nested]);
eval_result[nested] = s;
}
- dst += STRLEN(eval_result[nested]);
+ dst += strlen(eval_result[nested]);
}
can_f_submatch = prev_can_f_submatch;
@@ -1876,7 +1854,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int des
}
}
} else {
- while ((c = *src++) != NUL) {
+ while ((c = (uint8_t)(*src++)) != NUL) {
if (c == '&' && (flags & REGSUB_MAGIC)) {
no = 0;
} else if (c == '\\' && *src != NUL) {
@@ -1885,7 +1863,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int des
no = 0;
} else if ('0' <= *src && *src <= '9') {
no = *src++ - '0';
- } else if (vim_strchr("uUlLeE", *src)) {
+ } else if (vim_strchr("uUlLeE", (uint8_t)(*src))) {
switch (*src++) {
case 'u':
func_one = (fptr_T)do_upper;
@@ -1914,7 +1892,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int des
iemsg("vim_regsub_both(): not enough space");
return 0;
}
- *dst++ = (char_u)c;
+ *dst++ = (char)c;
*dst++ = *src++;
*dst++ = *src++;
} else {
@@ -1951,10 +1929,10 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int des
}
dst++;
}
- c = *src++;
+ c = (uint8_t)(*src++);
}
} else {
- c = utf_ptr2char((char *)src - 1);
+ c = utf_ptr2char(src - 1);
}
// Write to buffer, if copy is set.
if (func_one != NULL) {
@@ -1966,7 +1944,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int des
cc = c;
}
- int totlen = utfc_ptr2len((char *)src - 1);
+ int totlen = utfc_ptr2len(src - 1);
int charlen = utf_char2len(cc);
if (copy) {
@@ -1974,10 +1952,10 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int des
iemsg("vim_regsub_both(): not enough space");
return 0;
}
- utf_char2bytes(cc, (char *)dst);
+ utf_char2bytes(cc, dst);
}
dst += charlen - 1;
- int clen = utf_ptr2len((char *)src - 1);
+ int clen = utf_ptr2len(src - 1);
// If the character length is shorter than "totlen", there
// are composing characters; copy them as-is.
@@ -2004,7 +1982,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int des
len = rex.reg_mmatch->endpos[no].col
- rex.reg_mmatch->startpos[no].col;
} else {
- len = (int)STRLEN(s);
+ len = (int)strlen(s);
}
}
} else {
@@ -2034,7 +2012,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int des
if (rex.reg_mmatch->endpos[no].lnum == clnum) {
len = rex.reg_mmatch->endpos[no].col;
} else {
- len = (int)STRLEN(s);
+ len = (int)strlen(s);
}
} else {
break;
@@ -2060,7 +2038,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int des
}
dst += 2;
} else {
- c = utf_ptr2char((char *)s);
+ c = utf_ptr2char(s);
if (func_one != (fptr_T)NULL) {
// Turbo C complains without the typecast
@@ -2078,7 +2056,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int des
// Copy composing characters separately, one
// at a time.
- l = utf_ptr2len((char *)s) - 1;
+ l = utf_ptr2len(s) - 1;
s += l;
len -= l;
@@ -2088,7 +2066,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int des
iemsg("vim_regsub_both(): not enough space");
return 0;
}
- utf_char2bytes(cc, (char *)dst);
+ utf_char2bytes(cc, dst);
}
dst += charlen - 1;
}
@@ -2112,14 +2090,12 @@ exit:
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
- * overwritten.
- */
-static char_u *reg_getline_submatch(linenr_T lnum)
+/// Call reg_getline() with the line numbers from the submatch. If a
+/// substitute() was used the reg_maxline and other values have been
+/// overwritten.
+static char *reg_getline_submatch(linenr_T lnum)
{
- char_u *s;
+ char *s;
linenr_T save_first = rex.reg_firstlnum;
linenr_T save_max = rex.reg_maxline;
@@ -2133,15 +2109,14 @@ static char_u *reg_getline_submatch(linenr_T lnum)
return s;
}
-/*
- * Used for the submatch() function: get the string from the n'th submatch in
- * allocated memory.
- * Returns NULL when not in a ":s" command and for a non-existing submatch.
- */
-char_u *reg_submatch(int no)
+/// Used for the submatch() function: get the string from the n'th submatch in
+/// allocated memory.
+///
+/// @return NULL when not in a ":s" command and for a non-existing submatch.
+char *reg_submatch(int no)
{
- char_u *retval = NULL;
- char_u *s;
+ char *retval = NULL;
+ char *s;
int round;
linenr_T lnum;
@@ -2152,10 +2127,8 @@ char_u *reg_submatch(int no)
if (rsm.sm_match == NULL) {
ssize_t len;
- /*
- * First round: compute the length and allocate memory.
- * Second round: copy the text.
- */
+ // First round: compute the length and allocate memory.
+ // Second round: copy the text.
for (round = 1; round <= 2; round++) {
lnum = rsm.sm_mmatch->startpos[no].lnum;
if (lnum < 0 || rsm.sm_mmatch->endpos[no].lnum < 0) {
@@ -2171,13 +2144,13 @@ char_u *reg_submatch(int no)
// Within one line: take form start to end col.
len = rsm.sm_mmatch->endpos[no].col - rsm.sm_mmatch->startpos[no].col;
if (round == 2) {
- STRLCPY(retval, s, len + 1);
+ xstrlcpy(retval, s, (size_t)len + 1);
}
len++;
} else {
// Multiple lines: take start line from start col, middle
// lines completely and end line up to end col.
- len = (ssize_t)STRLEN(s);
+ len = (ssize_t)strlen(s);
if (round == 2) {
STRCPY(retval, s);
retval[len] = '\n';
@@ -2189,15 +2162,16 @@ char_u *reg_submatch(int no)
if (round == 2) {
STRCPY(retval + len, s);
}
- len += (ssize_t)STRLEN(s);
+ len += (ssize_t)strlen(s);
if (round == 2) {
retval[len] = '\n';
}
len++;
}
if (round == 2) {
- STRNCPY(retval + len, reg_getline_submatch(lnum),
- rsm.sm_mmatch->endpos[no].col);
+ strncpy(retval + len, // NOLINT(runtime/printf)
+ reg_getline_submatch(lnum),
+ (size_t)rsm.sm_mmatch->endpos[no].col);
}
len += rsm.sm_mmatch->endpos[no].col;
if (round == 2) {
@@ -2215,7 +2189,7 @@ char_u *reg_submatch(int no)
if (s == NULL || rsm.sm_match->endp[no] == NULL) {
retval = NULL;
} else {
- retval = vim_strnsave(s, (size_t)(rsm.sm_match->endp[no] - s));
+ retval = xstrnsave(s, (size_t)(rsm.sm_match->endp[no] - s));
}
}
@@ -2250,16 +2224,16 @@ list_T *reg_submatch_list(int no)
list = tv_list_alloc(elnum - slnum + 1);
- s = (const char *)reg_getline_submatch(slnum) + scol;
+ s = reg_getline_submatch(slnum) + scol;
if (slnum == elnum) {
tv_list_append_string(list, s, ecol - scol);
} else {
tv_list_append_string(list, s, -1);
for (int i = 1; i < elnum - slnum; i++) {
- s = (const char *)reg_getline_submatch(slnum + i);
+ s = reg_getline_submatch(slnum + i);
tv_list_append_string(list, s, -1);
}
- s = (const char *)reg_getline_submatch(elnum);
+ s = reg_getline_submatch(elnum);
tv_list_append_string(list, s, ecol);
}
} else {
@@ -2275,22 +2249,39 @@ list_T *reg_submatch_list(int no)
return list;
}
+/// Initialize the values used for matching against multiple lines
+///
+/// @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
+static void init_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf, linenr_T lnum)
+{
+ 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;
+}
+
// 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 =
-{
+static regengine_T bt_regengine = {
bt_regcomp,
bt_regfree,
bt_regexec_nl,
bt_regexec_multi,
};
-static regengine_T nfa_regengine =
-{
+static regengine_T nfa_regengine = {
nfa_regcomp,
nfa_regfree,
nfa_regexec_nl,
@@ -2302,28 +2293,26 @@ static regengine_T nfa_regengine =
static int regexp_engine = 0;
#ifdef REGEXP_DEBUG
-static char_u regname[][30] = {
+static uint8_t regname[][30] = {
"AUTOMATIC Regexp Engine",
"BACKTRACKING Regexp Engine",
"NFA Regexp Engine"
};
#endif
-/*
- * Compile a regular expression into internal code.
- * Returns the program in allocated memory.
- * Use vim_regfree() to free the memory.
- * Returns NULL for an error.
- */
+// Compile a regular expression into internal code.
+// Returns the program in allocated memory.
+// Use vim_regfree() to free the memory.
+// Returns NULL for an error.
regprog_T *vim_regcomp(char *expr_arg, int re_flags)
{
regprog_T *prog = NULL;
- char_u *expr = (char_u *)expr_arg;
+ char *expr = expr_arg;
regexp_engine = (int)p_re;
// Check for prefix "\%#=", that sets the regexp engine
- if (STRNCMP(expr, "\\%#=", 4) == 0) {
+ if (strncmp(expr, "\\%#=", 4) == 0) {
int newengine = expr[4] - '0';
if (newengine == AUTOMATIC_ENGINE
@@ -2353,10 +2342,10 @@ regprog_T *vim_regcomp(char *expr_arg, int re_flags)
//
const int called_emsg_before = called_emsg;
if (regexp_engine != BACKTRACKING_ENGINE) {
- prog = nfa_regengine.regcomp(expr,
+ prog = nfa_regengine.regcomp((uint8_t *)expr,
re_flags + (regexp_engine == AUTOMATIC_ENGINE ? RE_AUTO : 0));
} else {
- prog = bt_regengine.regcomp(expr, re_flags);
+ prog = bt_regengine.regcomp((uint8_t *)expr, re_flags);
}
// Check for error compiling regexp with initial engine.
@@ -2381,7 +2370,7 @@ regprog_T *vim_regcomp(char *expr_arg, int re_flags)
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);
+ prog = bt_regengine.regcomp((uint8_t *)expr, re_flags);
}
}
@@ -2395,9 +2384,7 @@ regprog_T *vim_regcomp(char *expr_arg, int re_flags)
return prog;
}
-/*
- * Free a compiled regexp program, returned by vim_regcomp().
- */
+// Free a compiled regexp program, returned by vim_regcomp().
void vim_regfree(regprog_T *prog)
{
if (prog != NULL) {
@@ -2416,12 +2403,12 @@ void free_regexp_stuff(void)
#endif
-static void report_re_switch(char_u *pat)
+static void report_re_switch(char *pat)
{
if (p_verbose > 0) {
verbose_enter();
msg_puts(_("Switching to backtracking RE engine for pattern: "));
- msg_puts((char *)pat);
+ msg_puts(pat);
verbose_leave();
}
}
@@ -2438,7 +2425,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 *line, colnr_T col, bool nl)
{
regexec_T rex_save;
bool rex_in_use_save = rex_in_use;
@@ -2461,7 +2448,7 @@ static bool vim_regexec_string(regmatch_T *rmp, char_u *line, colnr_T col, bool
rex.reg_startpos = NULL;
rex.reg_endpos = NULL;
- int result = rmp->regprog->engine->regexec_nl(rmp, line, col, nl);
+ int result = rmp->regprog->engine->regexec_nl(rmp, (uint8_t *)line, col, nl);
rmp->regprog->re_in_use = false;
// NFA engine aborted because it's very slow, use backtracking engine instead.
@@ -2469,15 +2456,15 @@ static bool vim_regexec_string(regmatch_T *rmp, char_u *line, colnr_T col, bool
&& result == NFA_TOO_EXPENSIVE) {
int save_p_re = (int)p_re;
int re_flags = (int)rmp->regprog->re_flags;
- char_u *pat = vim_strsave(((nfa_regprog_T *)rmp->regprog)->pattern);
+ char *pat = xstrdup(((nfa_regprog_T *)rmp->regprog)->pattern);
p_re = BACKTRACKING_ENGINE;
vim_regfree(rmp->regprog);
report_re_switch(pat);
- rmp->regprog = vim_regcomp((char *)pat, re_flags);
+ rmp->regprog = vim_regcomp(pat, re_flags);
if (rmp->regprog != NULL) {
rmp->regprog->re_in_use = true;
- result = rmp->regprog->engine->regexec_nl(rmp, line, col, nl);
+ result = rmp->regprog->engine->regexec_nl(rmp, (uint8_t *)line, col, nl);
rmp->regprog->re_in_use = false;
}
@@ -2495,7 +2482,7 @@ static bool vim_regexec_string(regmatch_T *rmp, char_u *line, colnr_T col, bool
// 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 *line, colnr_T col)
{
regmatch_T regmatch = { .regprog = *prog, .rm_ic = ignore_case };
bool r = vim_regexec_string(&regmatch, line, col, false);
@@ -2507,13 +2494,13 @@ bool vim_regexec_prog(regprog_T **prog, bool ignore_case, char_u *line, colnr_T
// Return true if there is a match, false if not.
bool vim_regexec(regmatch_T *rmp, char *line, colnr_T col)
{
- return vim_regexec_string(rmp, (char_u *)line, col, false);
+ return vim_regexec_string(rmp, line, col, false);
}
// Like vim_regexec(), but consider a "\n" in "line" to be a line break.
// Note: "rmp->regprog" may be freed and changed.
// Return true if there is a match, false if not.
-bool vim_regexec_nl(regmatch_T *rmp, char_u *line, colnr_T col)
+bool vim_regexec_nl(regmatch_T *rmp, char *line, colnr_T col)
{
return vim_regexec_string(rmp, line, col, true);
}
@@ -2560,7 +2547,7 @@ long vim_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf, linenr_T lnum,
&& result == NFA_TOO_EXPENSIVE) {
int save_p_re = (int)p_re;
int re_flags = (int)rmp->regprog->re_flags;
- char_u *pat = vim_strsave(((nfa_regprog_T *)rmp->regprog)->pattern);
+ char *pat = xstrdup(((nfa_regprog_T *)rmp->regprog)->pattern);
p_re = BACKTRACKING_ENGINE;
regprog_T *prev_prog = rmp->regprog;
@@ -2569,7 +2556,7 @@ long vim_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf, linenr_T lnum,
// checking for \z misuse was already done when compiling for NFA,
// allow all here
reg_do_extmatch = REX_ALL;
- rmp->regprog = vim_regcomp((char *)pat, re_flags);
+ rmp->regprog = vim_regcomp(pat, re_flags);
reg_do_extmatch = 0;
if (rmp->regprog == NULL) {
diff --git a/src/nvim/regexp.h b/src/nvim/regexp.h
index 085f78af54..dcc58fa34c 100644
--- a/src/nvim/regexp.h
+++ b/src/nvim/regexp.h
@@ -17,10 +17,11 @@
#define REX_ALL (REX_SET | REX_USE)
// regexp.c
+// uncrustify:off
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "regexp.h.generated.h"
-
# include "regexp_bt.h.generated.h"
#endif
+// uncrustify:on
#endif // NVIM_REGEXP_H
diff --git a/src/nvim/regexp_bt.c b/src/nvim/regexp_bt.c
index 769d2ceeef..1b32447d77 100644
--- a/src/nvim/regexp_bt.c
+++ b/src/nvim/regexp_bt.c
@@ -1,137 +1,130 @@
// 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.
- */
+// 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>
@@ -139,11 +132,10 @@
#include <string.h>
#include "nvim/garray.h"
+#include "nvim/profile.h"
#include "nvim/regexp.h"
-/*
- * The opcodes are:
- */
+// The opcodes are:
// definition number opnd? meaning
#define END 0 // End of program or NOMATCH operand.
@@ -240,9 +232,7 @@
#define RE_VISUAL 208 // Match Visual area
#define RE_COMPOSING 209 // any composing characters
-/*
- * Flags to be passed up and down.
- */
+// 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 +.
@@ -252,17 +242,17 @@
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 uint8_t *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 uint8_t 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 uint8_t *classchars = (uint8_t *)".iIkKfFpPsSdDxXoOwWhHaAlLuU";
static int classcodes[] = {
ANY, IDENT, SIDENT, KWORD, SKWORD,
FNAME, SFNAME, PRINT, SPRINT,
@@ -273,11 +263,9 @@ static int classcodes[] = {
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)
+// When regcode is set to this value, code is not emitted and size is computed
+// instead.
+#define JUST_CALC_SIZE ((uint8_t *)-1)
// Values for rs_state in regitem_T.
typedef enum regstate_E {
@@ -297,14 +285,12 @@ typedef enum regstate_E {
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".
- */
+// 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
+ uint8_t *ptr; // rex.input pointer, for single-line regexp
lpos_T pos; // rex.input pos, for multi-line regexp
} rs_u;
int rs_len;
@@ -313,7 +299,7 @@ typedef struct {
// struct to save start/end pointer/position in for \(\)
typedef struct {
union {
- char_u *ptr;
+ uint8_t *ptr;
lpos_T pos;
} se_u;
} save_se_T;
@@ -327,16 +313,14 @@ typedef struct regbehind_S {
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.
- */
+// 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
+ uint8_t *rs_scan; // current node in program
union {
save_se_T sesave;
regsave_T regsave;
@@ -355,73 +339,67 @@ typedef struct regstar_S {
// 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
+ uint8_t *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
- */
+// "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.
- */
+// 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.
- */
+// 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)
@@ -433,14 +411,14 @@ static regsave_T behind_pos;
// 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);
+static uint8_t *reg(int paren, int *flagp);
#ifdef BT_REGEXP_DUMP
-static void regdump(char_u *, bt_regprog_T *);
+static void regdump(uint8_t *, bt_regprog_T *);
#endif
#ifdef REGEXP_DEBUG
-static char_u *regprop(char_u *);
+static uint8_t *regprop(uint8_t *);
static int regnarrate = 0;
#endif
@@ -449,12 +427,10 @@ static int regnarrate = 0;
# 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()
+// Setup to parse the regexp. Used once to get the length and once to do it.
+static void regcomp_start(uint8_t *expr, int re_flags) // see vim_regcomp()
{
- initchr(expr);
+ initchr((char *)expr);
if (re_flags & RE_MAGIC) {
reg_magic = MAGIC_ON;
} else {
@@ -484,21 +460,17 @@ static bool use_multibytecode(int c)
|| utf_iscomposing(c));
}
-/*
- * Emit (if appropriate) a byte of code
- */
+// Emit (if appropriate) a byte of code
static void regc(int b)
{
if (regcode == JUST_CALC_SIZE) {
regsize++;
} else {
- *regcode++ = (char_u)b;
+ *regcode++ = (uint8_t)b;
}
}
-/*
- * Emit (if appropriate) a multi-byte character of code
- */
+// Emit (if appropriate) a multi-byte character of code
static void regmbc(int c)
{
if (regcode == JUST_CALC_SIZE) {
@@ -508,11 +480,9 @@ static void regmbc(int c)
}
}
-/*
- * 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()
- */
+// 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)
{
{
@@ -1481,43 +1451,37 @@ static void reg_equi_class(int c)
regmbc(c);
}
-/*
- * Emit a node.
- * Return pointer to generated code.
- */
-static char_u *regnode(int op)
+// Emit a node.
+// Return pointer to generated code.
+static uint8_t *regnode(int op)
{
- char_u *ret;
+ uint8_t *ret;
ret = regcode;
if (ret == JUST_CALC_SIZE) {
regsize += 3;
} else {
- *regcode++ = (char_u)op;
+ *regcode++ = (uint8_t)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)
+// Write a four bytes number at "p" and return pointer to the next char.
+static uint8_t *re_put_uint32(uint8_t *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);
+ *p++ = (uint8_t)((val >> 24) & 0377);
+ *p++ = (uint8_t)((val >> 16) & 0377);
+ *p++ = (uint8_t)((val >> 8) & 0377);
+ *p++ = (uint8_t)(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)
+// 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 uint8_t *regnext(uint8_t *p)
FUNC_ATTR_NONNULL_ALL
{
int offset;
@@ -1539,7 +1503,7 @@ static char_u *regnext(char_u *p)
}
// Set the next-pointer at the end of a node chain.
-static void regtail(char_u *p, char_u *val)
+static void regtail(uint8_t *p, uint8_t *val)
{
int offset;
@@ -1548,9 +1512,9 @@ static void regtail(char_u *p, char_u *val)
}
// Find last node.
- char_u *scan = p;
+ uint8_t *scan = p;
for (;;) {
- char_u *temp = regnext(scan);
+ uint8_t *temp = regnext(scan);
if (temp == NULL) {
break;
}
@@ -1568,15 +1532,13 @@ static void regtail(char_u *p, char_u *val)
if (offset > 0xffff) {
reg_toolong = true;
} else {
- *(scan + 1) = (char_u)(((unsigned)offset >> 8) & 0377);
- *(scan + 2) = (char_u)(offset & 0377);
+ *(scan + 1) = (uint8_t)(((unsigned)offset >> 8) & 0377);
+ *(scan + 2) = (uint8_t)(offset & 0377);
}
}
-/*
- * Like regtail, on item after a BRANCH; nop if none.
- */
-static void regoptail(char_u *p, char_u *val)
+// Like regtail, on item after a BRANCH; nop if none.
+static void regoptail(uint8_t *p, uint8_t *val)
{
// When op is neither BRANCH nor BRACE_COMPLEX0-9, it is "operandless"
if (p == NULL || p == JUST_CALC_SIZE
@@ -1587,16 +1549,14 @@ static void regoptail(char_u *p, char_u *val)
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)
+// Insert an operator in front of already-emitted operand
+//
+// Means relocating the operand.
+static void reginsert(int op, uint8_t *opnd)
{
- char_u *src;
- char_u *dst;
- char_u *place;
+ uint8_t *src;
+ uint8_t *dst;
+ uint8_t *place;
if (regcode == JUST_CALC_SIZE) {
regsize += 3;
@@ -1610,20 +1570,18 @@ static void reginsert(int op, char_u *opnd)
}
place = opnd; // Op node, where operand used to be.
- *place++ = (char_u)op;
+ *place++ = (uint8_t)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)
+// Insert an operator in front of already-emitted operand.
+// Add a number to the operator.
+static void reginsert_nr(int op, long val, uint8_t *opnd)
{
- char_u *src;
- char_u *dst;
- char_u *place;
+ uint8_t *src;
+ uint8_t *dst;
+ uint8_t *place;
if (regcode == JUST_CALC_SIZE) {
regsize += 7;
@@ -1637,24 +1595,22 @@ static void reginsert_nr(int op, long val, char_u *opnd)
}
place = opnd; // Op node, where operand used to be.
- *place++ = (char_u)op;
+ *place++ = (uint8_t)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)
+// 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, uint8_t *opnd)
{
- char_u *src;
- char_u *dst;
- char_u *place;
+ uint8_t *src;
+ uint8_t *dst;
+ uint8_t *place;
if (regcode == JUST_CALC_SIZE) {
regsize += 11;
@@ -1668,7 +1624,7 @@ static void reginsert_limits(int op, long minval, long maxval, char_u *opnd)
}
place = opnd; // Op node, where operand used to be.
- *place++ = (char_u)op;
+ *place++ = (uint8_t)op;
*place++ = NUL;
*place++ = NUL;
assert(minval >= 0 && (uintmax_t)minval <= UINT32_MAX);
@@ -1685,11 +1641,11 @@ static void reginsert_limits(int op, long minval, long maxval, char_u *opnd)
static int seen_endbrace(int refnum)
{
if (!had_endbrace[refnum]) {
- char_u *p;
+ uint8_t *p;
// Trick: check if "@<=" or "@<!" follows, in which case
// the \1 can appear before the referenced match.
- for (p = (char_u *)regparse; *p != NUL; p++) {
+ for (p = (uint8_t *)regparse; *p != NUL; p++) {
if (p[0] == '@' && p[1] == '<' && (p[2] == '!' || p[2] == '=')) {
break;
}
@@ -1704,19 +1660,17 @@ static int seen_endbrace(int refnum)
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)
+// 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 uint8_t *regatom(int *flagp)
{
- char_u *ret;
+ uint8_t *ret;
int flags;
int c;
- char_u *p;
+ uint8_t *p;
int extra = 0;
int save_prev_at_start = prev_at_start;
@@ -1792,7 +1746,7 @@ static char_u *regatom(int *flagp)
case Magic('L'):
case Magic('u'):
case Magic('U'):
- p = (char_u *)vim_strchr((char *)classchars, no_Magic(c));
+ p = (uint8_t *)vim_strchr((char *)classchars, no_Magic(c));
if (p == NULL) {
EMSG_RET_NULL(_("E63: invalid use of \\_"));
}
@@ -1854,17 +1808,17 @@ static char_u *regatom(int *flagp)
case Magic('~'): // previous substitute pattern
if (reg_prev_sub != NULL) {
- char_u *lp;
+ uint8_t *lp;
ret = regnode(EXACTLY);
- lp = reg_prev_sub;
+ lp = (uint8_t *)reg_prev_sub;
while (*lp != NUL) {
regc(*lp++);
}
regc(NUL);
if (*reg_prev_sub != NUL) {
*flagp |= HASWIDTH;
- if ((lp - reg_prev_sub) == 1) {
+ if ((lp - (uint8_t *)reg_prev_sub) == 1) {
*flagp |= SIMPLE;
}
}
@@ -1971,6 +1925,11 @@ static char_u *regatom(int *flagp)
break;
case '#':
+ if (regparse[0] == '=' && regparse[1] >= 48 && regparse[1] <= 50) {
+ // misplaced \%#=1
+ semsg(_(e_atom_engine_must_be_at_start_of_pattern), regparse[1]);
+ return FAIL;
+ }
ret = regnode(CURSOR);
break;
@@ -1989,9 +1948,9 @@ static char_u *regatom(int *flagp)
EMSG_ONE_RET_NULL;
}
{
- char_u *lastbranch;
- char_u *lastnode = NULL;
- char_u *br;
+ uint8_t *lastbranch;
+ uint8_t *lastnode = NULL;
+ uint8_t *br;
ret = NULL;
while ((c = getchr()) != ']') {
@@ -2091,6 +2050,7 @@ static char_u *regatom(int *flagp)
uint32_t n = 0;
int cmp;
bool cur = false;
+ bool got_digit = false;
cmp = c;
if (cmp == '<' || cmp == '>') {
@@ -2101,6 +2061,7 @@ static char_u *regatom(int *flagp)
c = getchr();
}
while (ascii_isdigit(c)) {
+ got_digit = true;
n = n * 10 + (uint32_t)(c - '0');
c = getchr();
}
@@ -2111,13 +2072,13 @@ static char_u *regatom(int *flagp)
if (ret == JUST_CALC_SIZE) {
regsize += 2;
} else {
- *regcode++ = (char_u)c;
- *regcode++ = (char_u)cmp;
+ *regcode++ = (uint8_t)c;
+ *regcode++ = (uint8_t)cmp;
}
break;
- } else if (c == 'l' || c == 'c' || c == 'v') {
+ } else if ((c == 'l' || c == 'c' || c == 'v') && (cur || got_digit)) {
if (cur && n) {
- semsg(_(e_regexp_number_after_dot_pos_search), no_Magic(c));
+ semsg(_(e_regexp_number_after_dot_pos_search_chr), no_Magic(c));
rc_did_emsg = true;
return NULL;
}
@@ -2149,7 +2110,7 @@ static char_u *regatom(int *flagp)
// put the number and the optional
// comparator after the opcode
regcode = re_put_uint32(regcode, n);
- *regcode++ = (char_u)cmp;
+ *regcode++ = (uint8_t)cmp;
}
break;
}
@@ -2163,11 +2124,11 @@ static char_u *regatom(int *flagp)
case Magic('['):
collection:
{
- char_u *lp;
+ uint8_t *lp;
// If there is no matching ']', we assume the '[' is a normal
// character. This makes 'incsearch' and ":help [" work.
- lp = skip_anyof(regparse);
+ lp = (uint8_t *)skip_anyof(regparse);
if (*lp == ']') { // there is a matching ']'
int startc = -1; // > 0 when next '-' is a range
int endc;
@@ -2204,7 +2165,7 @@ collection:
endc = get_coll_element(&regparse);
}
if (endc == 0) {
- endc = mb_ptr2char_adv((const char_u **)&regparse);
+ endc = mb_ptr2char_adv((const char **)&regparse);
}
// Handle \o40, \x20 and \u20AC style sequences
@@ -2236,10 +2197,10 @@ collection:
// 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
+ && (vim_strchr(REGEXP_INRANGE, (uint8_t)regparse[1]) != NULL
|| (!reg_cpo_lit
&& vim_strchr(REGEXP_ABBR,
- regparse[1]) != NULL))) {
+ (uint8_t)regparse[1]) != NULL))) {
regparse++;
if (*regparse == 'n') {
// '\n' in range: also match NL
@@ -2282,8 +2243,7 @@ collection:
if (c_class != 0) {
// produce equivalence class
reg_equi_class(c_class);
- } else if ((c_class =
- get_coll_element(&regparse)) != 0) {
+ } else if ((c_class = get_coll_element(&regparse)) != 0) {
// produce a collating element
regmbc(c_class);
} else {
@@ -2459,7 +2419,7 @@ do_multibyte:
for (len = 0; c != NUL && (len == 0
|| (re_multi_type(peekchr()) == NOT_MULTI
&& !one_exactly
- && !is_Magic(c))); ++len) {
+ && !is_Magic(c))); len++) {
c = no_Magic(c);
{
regmbc(c);
@@ -2469,7 +2429,7 @@ do_multibyte:
// Need to get composing character too.
for (;;) {
l = utf_ptr2len((char *)regparse);
- if (!utf_composinglike((char_u *)regparse, (char_u *)regparse + l)) {
+ if (!utf_composinglike(regparse, regparse + l)) {
break;
}
regmbc(utf_ptr2char((char *)regparse));
@@ -2493,20 +2453,18 @@ do_multibyte:
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)
+// 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 uint8_t *regpiece(int *flagp)
{
- char_u *ret;
+ uint8_t *ret;
int op;
- char_u *next;
+ uint8_t *next;
int flags;
long minval;
long maxval;
@@ -2637,15 +2595,13 @@ static char_u *regpiece(int *flagp)
return ret;
}
-/*
- * Parse one alternative of an | or & operator.
- * Implements the concatenation operator.
- */
-static char_u *regconcat(int *flagp)
+// Parse one alternative of an | or & operator.
+// Implements the concatenation operator.
+static uint8_t *regconcat(int *flagp)
{
- char_u *first = NULL;
- char_u *chain = NULL;
- char_u *latest;
+ uint8_t *first = NULL;
+ uint8_t *chain = NULL;
+ uint8_t *latest;
int flags;
int cont = true;
@@ -2715,15 +2671,13 @@ static char_u *regconcat(int *flagp)
return first;
}
-/*
- * Parse one alternative of an | operator.
- * Implements the & operator.
- */
-static char_u *regbranch(int *flagp)
+// Parse one alternative of an | operator.
+// Implements the & operator.
+static uint8_t *regbranch(int *flagp)
{
- char_u *ret;
- char_u *chain = NULL;
- char_u *latest;
+ uint8_t *ret;
+ uint8_t *chain = NULL;
+ uint8_t *latest;
int flags;
*flagp = WORST | HASNL; // Tentatively.
@@ -2768,11 +2722,11 @@ static char_u *regbranch(int *flagp)
/// 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)
+static uint8_t *reg(int paren, int *flagp)
{
- char_u *ret;
- char_u *br;
- char_u *ender;
+ uint8_t *ret;
+ uint8_t *br;
+ uint8_t *ender;
int parno = 0;
int flags;
@@ -2867,31 +2821,29 @@ static char_u *reg(int paren, int *flagp)
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)
+// 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(uint8_t *expr, int re_flags)
{
- char_u *scan;
- char_u *longest;
+ uint8_t *scan;
+ uint8_t *longest;
int len;
int flags;
@@ -2938,7 +2890,7 @@ static regprog_T *bt_regcomp(char_u *expr, int re_flags)
r->regflags |= RF_LOOKBH;
}
// Remember whether this pattern has any \z specials in it.
- r->reghasz = (char_u)re_has_z;
+ r->reghasz = (uint8_t)re_has_z;
scan = r->program + 1; // First BRANCH.
if (OP(regnext(scan)) == END) { // Only one top-level choice.
scan = OPERAND(scan);
@@ -2956,7 +2908,7 @@ static regprog_T *bt_regcomp(char_u *expr, int re_flags)
|| OP(scan) == NOTHING
|| OP(scan) == MOPEN + 0 || OP(scan) == NOPEN
|| OP(scan) == MCLOSE + 0 || OP(scan) == NCLOSE) {
- char_u *regnext_scan = regnext(scan);
+ uint8_t *regnext_scan = regnext(scan);
if (OP(regnext_scan) == EXACTLY) {
r->regstart = utf_ptr2char((char *)OPERAND(regnext_scan));
}
@@ -2976,9 +2928,9 @@ static regprog_T *bt_regcomp(char_u *expr, int re_flags)
longest = NULL;
len = 0;
for (; scan != NULL; scan = regnext(scan)) {
- if (OP(scan) == EXACTLY && STRLEN(OPERAND(scan)) >= (size_t)len) {
+ if (OP(scan) == EXACTLY && strlen((char *)OPERAND(scan)) >= (size_t)len) {
longest = OPERAND(scan);
- len = (int)STRLEN(OPERAND(scan));
+ len = (int)strlen((char *)OPERAND(scan));
}
}
r->regmust = longest;
@@ -2992,19 +2944,15 @@ static regprog_T *bt_regcomp(char_u *expr, int re_flags)
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.
- */
+// 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.
- */
+// 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;
@@ -3030,9 +2978,7 @@ static int coll_get_char(void)
return (int)nr;
}
-/*
- * Free a compiled regexp program, returned by bt_regcomp().
- */
+// Free a compiled regexp program, returned by bt_regcomp().
static void bt_regfree(regprog_T *prog)
{
xfree(prog);
@@ -3040,11 +2986,9 @@ static void bt_regfree(regprog_T *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).
- */
+// 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;
@@ -3070,7 +3014,7 @@ static void reg_restore(regsave_T *save, garray_T *gap)
// 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.line = (uint8_t *)reg_getline(rex.lnum);
}
rex.input = rex.line + save->rs_u.pos.col;
} else {
@@ -3101,13 +3045,11 @@ static bool reg_save_equal(const regsave_T *save)
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.
- */
+// 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;
@@ -3115,7 +3057,7 @@ static void save_se_multi(save_se_T *savep, lpos_T *posp)
posp->col = (colnr_T)(rex.input - rex.line);
}
-static void save_se_one(save_se_T *savep, char_u **pp)
+static void save_se_one(save_se_T *savep, uint8_t **pp)
{
savep->se_u.ptr = *pp;
*pp = rex.input;
@@ -3125,14 +3067,14 @@ static void save_se_one(save_se_T *savep, char_u **pp)
/// 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)
+static int regrepeat(uint8_t *p, long maxcount)
{
long count = 0;
- char_u *opnd;
+ uint8_t *opnd;
int mask;
int testval = 0;
- char_u *scan = rex.input; // Make local copy of rex.input for speed.
+ uint8_t *scan = rex.input; // Make local copy of rex.input for speed.
opnd = OPERAND(p);
switch (OP(p)) {
case ANY:
@@ -3192,7 +3134,7 @@ static int regrepeat(char_u *p, long maxcount)
case SKWORD:
case SKWORD + ADD_NL:
while (count < maxcount) {
- if (vim_iswordp_buf(scan, rex.reg_buf)
+ if (vim_iswordp_buf((char *)scan, rex.reg_buf)
&& (testval || !ascii_isdigit(*scan))) {
MB_PTR_ADV(scan);
} else if (*scan == NUL) {
@@ -3443,12 +3385,12 @@ do_class:
} 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) {
+ if ((cstrchr((char *)opnd, utf_ptr2char((char *)scan)) == NULL) == testval) {
break;
}
scan += len;
} else {
- if ((cstrchr(opnd, *scan) == NULL) == testval) {
+ if ((cstrchr((char *)opnd, *scan) == NULL) == testval) {
break;
}
scan++;
@@ -3487,11 +3429,9 @@ do_class:
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)
+// 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, uint8_t *scan)
{
regitem_T *rp;
@@ -3509,10 +3449,8 @@ static regitem_T *regstack_push(regstate_T state, char_u *scan)
return rp;
}
-/*
- * Pop an item from the regstack.
- */
-static void regstack_pop(char_u **scan)
+// Pop an item from the regstack.
+static void regstack_pop(uint8_t **scan)
{
regitem_T *rp;
@@ -3530,15 +3468,17 @@ static void save_subexpr(regbehind_T *bp)
// 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];
- }
+ if (rex.need_clear_subexpr) {
+ return;
+ }
+
+ 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];
}
}
}
@@ -3549,15 +3489,17 @@ static void restore_subexpr(regbehind_T *bp)
{
// 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;
- }
+ if (rex.need_clear_subexpr) {
+ return;
+ }
+
+ 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;
}
}
}
@@ -3578,9 +3520,9 @@ static void restore_subexpr(regbehind_T *bp)
/// 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)
+static bool regmatch(uint8_t *scan, proftime_T *tm, int *timed_out)
{
- char_u *next; // Next node.
+ uint8_t *next; // Next node.
int op;
int c;
regitem_T *rp;
@@ -3601,8 +3543,8 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
#ifdef REGEXP_DEBUG
if (scan != NULL && regnarrate) {
- mch_errmsg((char *)regprop(scan));
- mch_errmsg("(\n");
+ os_errmsg((char *)regprop(scan));
+ os_errmsg("(\n");
}
#endif
@@ -3628,18 +3570,18 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
#ifdef REGEXP_DEBUG
if (regnarrate) {
- mch_errmsg((char *)regprop(scan));
- mch_errmsg("...\n");
+ os_errmsg((char *)regprop(scan));
+ os_errmsg("...\n");
if (re_extmatch_in != NULL) {
int i;
- mch_errmsg(_("External submatches:\n"));
+ os_errmsg(_("External submatches:\n"));
for (i = 0; i < NSUBEXP; i++) {
- mch_errmsg(" \"");
+ os_errmsg(" \"");
if (re_extmatch_in->matches[i] != NULL) {
- mch_errmsg((char *)re_extmatch_in->matches[i]);
+ os_errmsg((char *)re_extmatch_in->matches[i]);
}
- mch_errmsg("\"\n");
+ os_errmsg("\"\n");
}
}
}
@@ -3709,7 +3651,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
// Line may have been freed, get it again.
if (REG_MULTI) {
- rex.line = reg_getline(rex.lnum);
+ rex.line = (uint8_t *)reg_getline(rex.lnum);
rex.input = rex.line + col;
}
@@ -3720,7 +3662,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
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))
+ ? (colnr_T)strlen((char *)reg_getline(pos->lnum - rex.reg_firstlnum))
: pos->col;
if (pos->lnum == rex.lnum + rex.reg_firstlnum
@@ -3764,7 +3706,8 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
case RE_VCOL:
if (!re_num_cmp(win_linetabsize(rex.reg_win == NULL
? curwin : rex.reg_win,
- rex.line,
+ rex.reg_firstlnum + rex.lnum,
+ (char *)rex.line,
(colnr_T)(rex.input - rex.line)) + 1,
scan)) {
status = RA_NOMATCH;
@@ -3777,7 +3720,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
} 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);
+ mb_get_class_tab((char *)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) {
@@ -3793,7 +3736,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
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);
+ this_class = mb_get_class_tab((char *)rex.input, rex.reg_buf->b_chartab);
prev_class = reg_prev_class();
if (this_class == prev_class
|| prev_class == 0 || prev_class == 1) {
@@ -3828,7 +3771,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
break;
case KWORD:
- if (!vim_iswordp_buf(rex.input, rex.reg_buf)) {
+ if (!vim_iswordp_buf((char *)rex.input, rex.reg_buf)) {
status = RA_NOMATCH;
} else {
ADVANCE_REGINPUT();
@@ -3837,7 +3780,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
case SKWORD:
if (ascii_isdigit(*rex.input)
- || !vim_iswordp_buf(rex.input, rex.reg_buf)) {
+ || !vim_iswordp_buf((char *)rex.input, rex.reg_buf)) {
status = RA_NOMATCH;
} else {
ADVANCE_REGINPUT();
@@ -4022,7 +3965,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
case EXACTLY: {
int len;
- char_u *opnd;
+ uint8_t *opnd;
opnd = OPERAND(scan);
// Inline the first byte, for speed.
@@ -4037,15 +3980,15 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
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) {
+ len = (int)strlen((char *)opnd);
+ if (cstrncmp((char *)opnd, (char *)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)
+ && utf_composinglike((char *)rex.input, (char *)rex.input + len)
&& !rex.reg_icombine
&& OP(next) != RE_COMPOSING) {
// raaron: This code makes a composing character get
@@ -4064,7 +4007,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
case ANYBUT:
if (c == NUL) {
status = RA_NOMATCH;
- } else if ((cstrchr(OPERAND(scan), c) == NULL) == (op == ANYOF)) {
+ } else if ((cstrchr((char *)OPERAND(scan), c) == NULL) == (op == ANYOF)) {
status = RA_NOMATCH;
} else {
ADVANCE_REGINPUT();
@@ -4074,7 +4017,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
case MULTIBYTECODE: {
int i, len;
- const char_u *opnd = OPERAND(scan);
+ const uint8_t *opnd = OPERAND(scan);
// Safety check (just in case 'encoding' was changed since
// compiling the program).
if ((len = utfc_ptr2len((char *)opnd)) < 2) {
@@ -4269,7 +4212,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
} 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) {
+ if (cstrncmp((char *)rex.reg_startp[no], (char *)rex.input, &len) != 0) {
status = RA_NOMATCH;
}
}
@@ -4282,8 +4225,8 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
&& 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) {
+ if (cstrncmp((char *)rex.line + rex.reg_startpos[no].col,
+ (char *)rex.input, &len) != 0) {
status = RA_NOMATCH;
}
} else {
@@ -4318,8 +4261,8 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
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) {
+ int len = (int)strlen((char *)re_extmatch_in->matches[no]);
+ if (cstrncmp((char *)re_extmatch_in->matches[no], (char *)rex.input, &len) != 0) {
status = RA_NOMATCH;
} else {
rex.input += len;
@@ -4369,7 +4312,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
case BRACE_COMPLEX + 8:
case BRACE_COMPLEX + 9:
no = op - BRACE_COMPLEX;
- ++brace_count[no];
+ brace_count[no]++;
// If not matched enough times yet, try one more
if (brace_count[no] <= (brace_min[no] <= brace_max[no]
@@ -4635,7 +4578,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
// 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
+ brace_count[rp->rs_no]--; // decrement match count
}
regstack_pop(&scan);
break;
@@ -4645,7 +4588,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
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];
+ brace_count[rp->rs_no]--;
// continue with the items after "\{}"
status = RA_CONT;
}
@@ -4744,7 +4687,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
if (limit > 0
&& ((rp->rs_un.regsave.rs_u.pos.lnum
< behind_pos.rs_u.pos.lnum
- ? (colnr_T)STRLEN(rex.line)
+ ? (colnr_T)strlen((char *)rex.line)
: behind_pos.rs_u.pos.col)
- rp->rs_un.regsave.rs_u.pos.col >= limit)) {
no = FAIL;
@@ -4757,15 +4700,15 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
} else {
reg_restore(&rp->rs_un.regsave, &backpos);
rp->rs_un.regsave.rs_u.pos.col =
- (colnr_T)STRLEN(rex.line);
+ (colnr_T)strlen((char *)rex.line);
}
} else {
- const char_u *const line =
- reg_getline(rp->rs_un.regsave.rs_u.pos.lnum);
+ const uint8_t *const line =
+ (uint8_t *)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)
+ utf_head_off((char *)line,
+ (char *)line + rp->rs_un.regsave.rs_u.pos.col - 1)
+ 1;
}
} else {
@@ -4843,12 +4786,12 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
break;
}
rex.lnum--;
- rex.line = reg_getline(rex.lnum);
+ rex.line = (uint8_t *)reg_getline(rex.lnum);
// Just in case regrepeat() didn't count right.
if (rex.line == NULL) {
break;
}
- rex.input = rex.line + STRLEN(rex.line);
+ rex.input = rex.line + strlen((char *)rex.line);
fast_breakcheck();
} else {
MB_PTR_BACK(rex.line, rex.input);
@@ -4974,13 +4917,13 @@ static long regtry(bt_regprog_T *prog, colnr_T col, proftime_T *tm, int *timed_o
&& 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,
- (size_t)(reg_endzpos[i].col - reg_startzpos[i].col));
+ (uint8_t *)xstrnsave((char *)reg_getline(reg_startzpos[i].lnum) + reg_startzpos[i].col,
+ (size_t)(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], (size_t)(reg_endzp[i] - reg_startzp[i]));
+ (uint8_t *)xstrnsave((char *)reg_startzp[i], (size_t)(reg_endzp[i] - reg_startzp[i]));
}
}
}
@@ -4991,15 +4934,16 @@ static long regtry(bt_regprog_T *prog, colnr_T col, proftime_T *tm, int *timed_o
/// 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 startcol column to start looking for match
/// @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)
+static long bt_regexec_both(uint8_t *line, colnr_T startcol, proftime_T *tm, int *timed_out)
{
bt_regprog_T *prog;
- char_u *s;
+ uint8_t *s;
+ colnr_T col = startcol;
long retval = 0L;
// Create "regstack" and "backpos" if they are not allocated yet.
@@ -5022,13 +4966,13 @@ static long bt_regexec_both(char_u *line, colnr_T col, proftime_T *tm, int *time
if (REG_MULTI) {
prog = (bt_regprog_T *)rex.reg_mmatch->regprog;
- line = reg_getline((linenr_T)0);
+ line = (uint8_t *)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;
+ rex.reg_startp = (uint8_t **)rex.reg_match->startp;
+ rex.reg_endp = (uint8_t **)rex.reg_match->endp;
}
// Be paranoid...
@@ -5067,15 +5011,15 @@ static long bt_regexec_both(char_u *line, colnr_T col, proftime_T *tm, int *time
// 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) {
+ while ((s = (uint8_t *)vim_strchr((char *)s, c)) != NULL) {
+ if (cstrncmp((char *)s, (char *)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) {
+ while ((s = (uint8_t *)cstrchr((char *)s, c)) != NULL) {
+ if (cstrncmp((char *)s, (char *)prog->regmust, &prog->regmlen) == 0) {
break; // Found it.
}
MB_PTR_ADV(s);
@@ -5109,7 +5053,7 @@ static long bt_regexec_both(char_u *line, colnr_T col, proftime_T *tm, int *time
while (!got_int) {
if (prog->regstart != NUL) {
// Skip until the char we know it must start with.
- s = cstrchr(rex.line + col, prog->regstart);
+ s = (uint8_t *)cstrchr((char *)rex.line + col, prog->regstart);
if (s == NULL) {
retval = 0;
break;
@@ -5131,7 +5075,7 @@ static long bt_regexec_both(char_u *line, colnr_T col, proftime_T *tm, int *time
// if not currently on the first line, get it again
if (rex.lnum != 0) {
rex.lnum = 0;
- rex.line = reg_getline((linenr_T)0);
+ rex.line = (uint8_t *)reg_getline((linenr_T)0);
}
if (rex.line[col] == NUL) {
break;
@@ -5174,10 +5118,18 @@ theend:
|| (end->lnum == start->lnum && end->col < start->col)) {
rex.reg_mmatch->endpos[0] = rex.reg_mmatch->startpos[0];
}
+
+ // startpos[0] may be set by "\zs", also return the column where
+ // the whole pattern matched.
+ rex.reg_mmatch->rmm_matchcol = col;
} else {
if (rex.reg_match->endp[0] < rex.reg_match->startp[0]) {
rex.reg_match->endp[0] = rex.reg_match->startp[0];
}
+
+ // startpos[0] may be set by "\zs", also return the column where
+ // the whole pattern matched.
+ rex.reg_match->rm_matchcol = col;
}
}
@@ -5193,7 +5145,7 @@ theend:
/// @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)
+static int bt_regexec_nl(regmatch_T *rmp, uint8_t *line, colnr_T col, bool line_lbr)
{
rex.reg_match = rmp;
rex.reg_mmatch = NULL;
@@ -5225,24 +5177,12 @@ static int bt_regexec_nl(regmatch_T *rmp, char_u *line, colnr_T col, bool line_l
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;
-
+ init_regexec_multi(rmp, win, buf, lnum);
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)
+// Compare a number with the operand of RE_LNUM, RE_COL or RE_VCOL.
+static int re_num_cmp(uint32_t val, uint8_t *scan)
{
uint32_t n = (uint32_t)OPERAND_MIN(scan);
@@ -5257,15 +5197,13 @@ static int re_num_cmp(uint32_t val, char_u *scan)
#ifdef BT_REGEXP_DUMP
-/*
- * regdump - dump a regexp onto stdout in vaguely comprehensible form
- */
-static void regdump(char_u *pattern, bt_regprog_T *r)
+// regdump - dump a regexp onto stdout in vaguely comprehensible form
+static void regdump(uint8_t *pattern, bt_regprog_T *r)
{
- char_u *s;
+ uint8_t *s;
int op = EXACTLY; // Arbitrary non-END op.
- char_u *next;
- char_u *end = NULL;
+ uint8_t *next;
+ uint8_t *end = NULL;
FILE *f;
# ifdef BT_REGEXP_LOG
@@ -5345,10 +5283,8 @@ static void regdump(char_u *pattern, bt_regprog_T *r)
#ifdef REGEXP_DEBUG
-/*
- * regprop - printable representation of opcode
- */
-static char_u *regprop(char_u *op)
+// regprop - printable representation of opcode
+static uint8_t *regprop(uint8_t *op)
{
char *p;
static char buf[50];
@@ -5593,7 +5529,7 @@ static char_u *regprop(char_u *op)
case MOPEN + 7:
case MOPEN + 8:
case MOPEN + 9:
- sprintf(buf + STRLEN(buf), "MOPEN%d", OP(op) - MOPEN);
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "MOPEN%d", OP(op) - MOPEN);
p = NULL;
break;
case MCLOSE + 0:
@@ -5608,7 +5544,7 @@ static char_u *regprop(char_u *op)
case MCLOSE + 7:
case MCLOSE + 8:
case MCLOSE + 9:
- sprintf(buf + STRLEN(buf), "MCLOSE%d", OP(op) - MCLOSE);
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "MCLOSE%d", OP(op) - MCLOSE);
p = NULL;
break;
case BACKREF + 1:
@@ -5620,7 +5556,7 @@ static char_u *regprop(char_u *op)
case BACKREF + 7:
case BACKREF + 8:
case BACKREF + 9:
- sprintf(buf + STRLEN(buf), "BACKREF%d", OP(op) - BACKREF);
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "BACKREF%d", OP(op) - BACKREF);
p = NULL;
break;
case NOPEN:
@@ -5638,7 +5574,7 @@ static char_u *regprop(char_u *op)
case ZOPEN + 7:
case ZOPEN + 8:
case ZOPEN + 9:
- sprintf(buf + STRLEN(buf), "ZOPEN%d", OP(op) - ZOPEN);
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "ZOPEN%d", OP(op) - ZOPEN);
p = NULL;
break;
case ZCLOSE + 1:
@@ -5650,7 +5586,7 @@ static char_u *regprop(char_u *op)
case ZCLOSE + 7:
case ZCLOSE + 8:
case ZCLOSE + 9:
- sprintf(buf + STRLEN(buf), "ZCLOSE%d", OP(op) - ZCLOSE);
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "ZCLOSE%d", OP(op) - ZCLOSE);
p = NULL;
break;
case ZREF + 1:
@@ -5662,7 +5598,7 @@ static char_u *regprop(char_u *op)
case ZREF + 7:
case ZREF + 8:
case ZREF + 9:
- sprintf(buf + STRLEN(buf), "ZREF%d", OP(op) - ZREF);
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "ZREF%d", OP(op) - ZREF);
p = NULL;
break;
case STAR:
@@ -5702,7 +5638,8 @@ static char_u *regprop(char_u *op)
case BRACE_COMPLEX + 7:
case BRACE_COMPLEX + 8:
case BRACE_COMPLEX + 9:
- sprintf(buf + STRLEN(buf), "BRACE_COMPLEX%d", OP(op) - BRACE_COMPLEX);
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "BRACE_COMPLEX%d",
+ OP(op) - BRACE_COMPLEX);
p = NULL;
break;
case MULTIBYTECODE:
@@ -5712,13 +5649,13 @@ static char_u *regprop(char_u *op)
p = "NEWL";
break;
default:
- sprintf(buf + STRLEN(buf), "corrupt %d", OP(op));
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "corrupt %d", OP(op));
p = NULL;
break;
}
if (p != NULL) {
STRCAT(buf, p);
}
- return (char_u *)buf;
+ return (uint8_t *)buf;
}
#endif // REGEXP_DEBUG
diff --git a/src/nvim/regexp_defs.h b/src/nvim/regexp_defs.h
index b313dfe877..16bb2db464 100644
--- a/src/nvim/regexp_defs.h
+++ b/src/nvim/regexp_defs.h
@@ -1,13 +1,11 @@
-/*
- * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
- *
- * This is NOT the original regular expression code as written by Henry
- * Spencer. This code has been modified specifically for use with Vim, and
- * should not be used apart from compiling Vim. If you want a good regular
- * expression library, get the original code.
- *
- * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
- */
+// NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
+//
+// This is NOT the original regular expression code as written by Henry
+// Spencer. This code has been modified specifically for use with Vim, and
+// should not be used apart from compiling Vim. If you want a good regular
+// expression library, get the original code.
+//
+// NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
#ifndef NVIM_REGEXP_DEFS_H
#define NVIM_REGEXP_DEFS_H
@@ -17,18 +15,32 @@
#include "nvim/pos.h"
#include "nvim/types.h"
-/*
- * The number of sub-matches is limited to 10.
- * The first one (index 0) is the whole match, referenced with "\0".
- * The second one (index 1) is the first sub-match, referenced with "\1".
- * This goes up to the tenth (index 9), referenced with "\9".
- */
+/// Used for "magic_overruled".
+typedef enum {
+ OPTION_MAGIC_NOT_SET, ///< p_magic not overruled
+ OPTION_MAGIC_ON, ///< magic on inside regexp
+ OPTION_MAGIC_OFF, ///< magic off inside regexp
+} optmagic_T;
+
+/// Magicness of a pattern, used by regexp code.
+/// The order and values matter:
+/// magic <= MAGIC_OFF includes MAGIC_NONE
+/// magic >= MAGIC_ON includes MAGIC_ALL
+typedef enum {
+ MAGIC_NONE = 1, ///< "\V" very unmagic
+ MAGIC_OFF = 2, ///< "\M" or 'magic' off
+ MAGIC_ON = 3, ///< "\m" or 'magic'
+ MAGIC_ALL = 4, ///< "\v" very magic
+} magic_T;
+
+// The number of sub-matches is limited to 10.
+// The first one (index 0) is the whole match, referenced with "\0".
+// The second one (index 1) is the first sub-match, referenced with "\1".
+// This goes up to the tenth (index 9), referenced with "\9".
#define NSUBEXP 10
-/*
- * In the NFA engine: how many braces are allowed.
- * TODO(RE): Use dynamic memory allocation instead of static, like here
- */
+// In the NFA engine: how many braces are allowed.
+// TODO(RE): Use dynamic memory allocation instead of static, like here
#define NFA_MAX_BRACES 20
// In the NFA engine: how many states are allowed.
@@ -55,17 +67,17 @@ typedef struct {
regprog_T *regprog;
lpos_T startpos[NSUBEXP];
lpos_T endpos[NSUBEXP];
+
+ colnr_T rmm_matchcol; ///< match start without "\zs"
int rmm_ic;
colnr_T rmm_maxcol; /// when not zero: maximum column
} regmmatch_T;
#include "nvim/buffer_defs.h"
-/*
- * Structure returned by vim_regcomp() to pass on to vim_regexec().
- * This is the general structure. For the actual matcher, two specific
- * structures are used. See code below.
- */
+// Structure returned by vim_regcomp() to pass on to vim_regexec().
+// This is the general structure. For the actual matcher, two specific
+// structures are used. See code below.
struct regprog {
regengine_T *engine;
unsigned regflags;
@@ -74,11 +86,9 @@ struct regprog {
bool re_in_use; ///< prog is being executed
};
-/*
- * Structure used by the back track matcher.
- * These fields are only to be used in regexp.c!
- * See regexp.c for an explanation.
- */
+// Structure used by the back track matcher.
+// These fields are only to be used in regexp.c!
+// See regexp.c for an explanation.
typedef struct {
// These four members implement regprog_T.
regengine_T *engine;
@@ -107,9 +117,7 @@ struct nfa_state {
int val;
};
-/*
- * Structure used by the NFA matcher.
- */
+// Structure used by the NFA matcher.
typedef struct {
// These four members implement regprog_T.
regengine_T *engine;
@@ -127,29 +135,27 @@ typedef struct {
int has_zend; // pattern contains \ze
int has_backref; // pattern contains \1 .. \9
int reghasz;
- char_u *pattern;
+ char *pattern;
int nsubexp; // number of ()
int nstate;
nfa_state_T state[1]; // actually longer..
} nfa_regprog_T;
-/*
- * Structure to be used for single-line matching.
- * Sub-match "no" starts at "startp[no]" and ends just before "endp[no]".
- * When there is no match, the pointer is NULL.
- */
+// Structure to be used for single-line matching.
+// Sub-match "no" starts at "startp[no]" and ends just before "endp[no]".
+// When there is no match, the pointer is NULL.
typedef struct {
regprog_T *regprog;
- char_u *startp[NSUBEXP];
- char_u *endp[NSUBEXP];
+ char *startp[NSUBEXP];
+ char *endp[NSUBEXP];
+
+ colnr_T rm_matchcol; ///< match start without "\zs"
bool rm_ic;
} regmatch_T;
-/*
- * Structure used to store external references: "\z\(\)" to "\z\1".
- * Use a reference count to avoid the need to copy this around. When it goes
- * from 1 to zero the matches need to be freed.
- */
+// Structure used to store external references: "\z\(\)" to "\z\1".
+// Use a reference count to avoid the need to copy this around. When it goes
+// from 1 to zero the matches need to be freed.
struct reg_extmatch {
int16_t refcnt;
char_u *matches[NSUBEXP];
diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c
index 554def5b8a..93b03f0632 100644
--- a/src/nvim/regexp_nfa.c
+++ b/src/nvim/regexp_nfa.c
@@ -1,11 +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
-/*
- * NFA regular expression implementation.
- *
- * This file is included in "regexp.c".
- */
+// NFA regular expression implementation.
+//
+// This file is included in "regexp.c".
#include <assert.h>
#include <inttypes.h>
@@ -246,10 +244,10 @@ 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_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_value_too_large[] = N_("E951: \\% value too large");
+static char e_nul_found[] = N_("E865: (NFA) Regexp end encountered prematurely");
+static char e_misplaced[] = N_("E866: (NFA regexp) Misplaced %c");
+static char e_ill_char_class[] = N_("E877: (NFA regexp) Invalid character class: %" PRId64);
+static char e_value_too_large[] = N_("E951: \\% value too large");
// Since the out pointers in the list are always
// uninitialized, we use the pointers themselves
@@ -278,10 +276,11 @@ typedef struct {
colnr_T end_col;
} multi[NSUBEXP];
struct linepos {
- char_u *start;
- char_u *end;
+ uint8_t *start;
+ uint8_t *end;
} line[NSUBEXP];
} list;
+ colnr_T orig_start_col; // list.multi[0].start_col without \zs
} regsub_T;
typedef struct {
@@ -297,7 +296,7 @@ struct nfa_pim_S {
regsubs_T subs; // submatch info, only party used
union {
lpos_T pos;
- char_u *ptr;
+ uint8_t *ptr;
} end; // where the match must end
};
@@ -355,7 +354,7 @@ static int nfa_ll_index = 0;
/// Initialize internal variables before NFA compilation.
///
/// @param re_flags @see vim_regcomp()
-static void nfa_regcomp_start(char_u *expr, int re_flags)
+static void nfa_regcomp_start(uint8_t *expr, int re_flags)
{
size_t postfix_size;
size_t nstate_max;
@@ -363,7 +362,7 @@ static void nfa_regcomp_start(char_u *expr, int re_flags)
nstate = 0;
istate = 0;
// A reasonable estimation for maximum size
- nstate_max = (STRLEN(expr) + 1) * 25;
+ nstate_max = (strlen((char *)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.
@@ -383,10 +382,8 @@ static void nfa_regcomp_start(char_u *expr, int re_flags)
regcomp_start(expr, re_flags);
}
-/*
- * Figure out if the NFA state list starts with an anchor, must match at start
- * of the line.
- */
+// Figure out if the NFA state list starts with an anchor, must match at start
+// of the line.
static int nfa_get_reganch(nfa_state_T *start, int depth)
{
nfa_state_T *p = start;
@@ -441,10 +438,8 @@ static int nfa_get_reganch(nfa_state_T *start, int depth)
return 0;
}
-/*
- * Figure out if the NFA state list starts with a character which must match
- * at start of the match.
- */
+// Figure out if the NFA state list starts with a character which must match
+// at start of the match.
static int nfa_get_regstart(nfa_state_T *start, int depth)
{
nfa_state_T *p = start;
@@ -521,17 +516,15 @@ static int nfa_get_regstart(nfa_state_T *start, int depth)
return 0;
}
-/*
- * Figure out if the NFA state list contains just literal text and nothing
- * else. If so return a string in allocated memory with what must match after
- * regstart. Otherwise return NULL.
- */
-static char_u *nfa_get_match_text(nfa_state_T *start)
+// Figure out if the NFA state list contains just literal text and nothing
+// else. If so return a string in allocated memory with what must match after
+// regstart. Otherwise return NULL.
+static uint8_t *nfa_get_match_text(nfa_state_T *start)
{
nfa_state_T *p = start;
int len = 0;
- char_u *ret;
- char_u *s;
+ uint8_t *ret;
+ uint8_t *s;
if (p->c != NFA_MOPEN) {
return NULL; // just in case
@@ -557,10 +550,8 @@ static char_u *nfa_get_match_text(nfa_state_T *start)
return ret;
}
-/*
- * Allocate more space for post_start. Called when
- * running above the estimated number of states.
- */
+// Allocate more space for post_start. Called when
+// running above the estimated number of states.
static void realloc_post_list(void)
{
// For weird patterns the number of states can be very high. Increasing by
@@ -572,17 +563,15 @@ static void realloc_post_list(void)
post_start = new_start;
}
-/*
- * Search between "start" and "end" and try to recognize a
- * character class in expanded form. For example [0-9].
- * On success, return the id the character class to be emitted.
- * On failure, return 0 (=FAIL)
- * Start points to the first char of the range, while end should point
- * to the closing brace.
- * Keep in mind that 'ignorecase' applies at execution time, thus [a-z] may
- * need to be interpreted as [a-zA-Z].
- */
-static int nfa_recognize_char_class(char_u *start, char_u *end, int extra_newl)
+// Search between "start" and "end" and try to recognize a
+// character class in expanded form. For example [0-9].
+// On success, return the id the character class to be emitted.
+// On failure, return 0 (=FAIL)
+// Start points to the first char of the range, while end should point
+// to the closing brace.
+// Keep in mind that 'ignorecase' applies at execution time, thus [a-z] may
+// need to be interpreted as [a-zA-Z].
+static int nfa_recognize_char_class(uint8_t *start, uint8_t *end, int extra_newl)
{
#define CLASS_not 0x80
#define CLASS_af 0x40
@@ -593,7 +582,7 @@ static int nfa_recognize_char_class(char_u *start, char_u *end, int extra_newl)
#define CLASS_o9 0x02
#define CLASS_underscore 0x01
- char_u *p;
+ uint8_t *p;
int config = 0;
bool newl = extra_newl == true;
@@ -700,14 +689,12 @@ static int nfa_recognize_char_class(char_u *start, char_u *end, int extra_newl)
return FAIL;
}
-/*
- * Produce the bytes for equivalence class "c".
- * Currently only handles latin1, latin9 and utf-8.
- * Emits bytes in postfix notation: 'a,b,NFA_OR,c,NFA_OR' is
- * equivalent to 'a OR b OR c'
- *
- * NOTE! When changing this function, also update reg_equi_class()
- */
+// Produce the bytes for equivalence class "c".
+// Currently only handles latin1, latin9 and utf-8.
+// Emits bytes in postfix notation: 'a,b,NFA_OR,c,NFA_OR' is
+// equivalent to 'a OR b OR c'
+//
+// NOTE! When changing this function, also update reg_equi_class()
static void nfa_emit_equi_class(int c)
{
#define EMIT2(c) EMIT(c); EMIT(NFA_CONCAT);
@@ -1748,7 +1735,7 @@ static void nfa_emit_equi_class(int c)
case 0x1ef5:
case 0x1ef7:
case 0x1ef9:
- EMIT2('y') EMIT2(y_acute) EMIT2(y_diaeresis)
+ EMIT2('y') EMIT2(y_acute) EMIT2(y_diaeresis) // NOLINT(whitespace/cast)
EMIT2(0x177) EMIT2(0x1b4) EMIT2(0x233) EMIT2(0x24f)
EMIT2(0x1e8f) EMIT2(0x1e99) EMIT2(0x1ef3)
EMIT2(0x1ef5) EMIT2(0x1ef7) EMIT2(0x1ef9)
@@ -1778,26 +1765,22 @@ static void nfa_emit_equi_class(int c)
#undef EMIT2
}
-/*
- * Code to parse regular expression.
- *
- * We try to reuse parsing functions in regexp.c to
- * minimize surprise and keep the syntax consistent.
- */
-
-/*
- * Parse the lowest level.
- *
- * An atom can be one of a long list of items. Many atoms match one character
- * in the text. It is often an ordinary character or a character class.
- * Braces can be used to make a pattern into an atom. The "\z(\)" construct
- * is only for syntax highlighting.
- *
- * atom ::= ordinary-atom
- * or \( pattern \)
- * or \%( pattern \)
- * or \z( pattern \)
- */
+// Code to parse regular expression.
+//
+// We try to reuse parsing functions in regexp.c to
+// minimize surprise and keep the syntax consistent.
+
+// Parse the lowest level.
+//
+// An atom can be one of a long list of items. Many atoms match one character
+// in the text. It is often an ordinary character or a character class.
+// Braces can be used to make a pattern into an atom. The "\z(\)" construct
+// is only for syntax highlighting.
+//
+// atom ::= ordinary-atom
+// or \( pattern \)
+// or \%( pattern \)
+// or \z( pattern \)
static int nfa_regatom(void)
{
int c;
@@ -1805,9 +1788,9 @@ static int nfa_regatom(void)
int equiclass;
int collclass;
int got_coll_char;
- char_u *p;
- char_u *endp;
- char_u *old_regparse = (char_u *)regparse;
+ uint8_t *p;
+ uint8_t *endp;
+ uint8_t *old_regparse = (uint8_t *)regparse;
int extra = 0;
int emit_range;
int negated;
@@ -1862,9 +1845,7 @@ static int nfa_regatom(void)
// "\_x" is character class plus newline
FALLTHROUGH;
- /*
- * Character classes.
- */
+ // Character classes.
case Magic('.'):
case Magic('i'):
case Magic('I'):
@@ -1892,7 +1873,7 @@ static int nfa_regatom(void)
case Magic('L'):
case Magic('u'):
case Magic('U'):
- p = (char_u *)vim_strchr((char *)classchars, no_Magic(c));
+ p = (uint8_t *)vim_strchr((char *)classchars, no_Magic(c));
if (p == NULL) {
if (extra == NFA_ADD_NL) {
semsg(_(e_ill_char_class), (int64_t)c);
@@ -1905,7 +1886,7 @@ static int nfa_regatom(void)
// When '.' is followed by a composing char ignore the dot, so that
// the composing char is matched here.
if (c == Magic('.') && utf_iscomposing(peekchr())) {
- old_regparse = (char_u *)regparse;
+ old_regparse = (uint8_t *)regparse;
c = getchr();
goto nfa_do_multibyte;
}
@@ -1951,7 +1932,7 @@ static int nfa_regatom(void)
return FAIL;
case Magic('~'): {
- char_u *lp;
+ uint8_t *lp;
// Previous substitute pattern.
// Generated as "\%(pattern\)".
@@ -1959,9 +1940,9 @@ static int nfa_regatom(void)
emsg(_(e_nopresub));
return FAIL;
}
- for (lp = reg_prev_sub; *lp != NUL; MB_CPTR_ADV(lp)) {
+ for (lp = (uint8_t *)reg_prev_sub; *lp != NUL; MB_CPTR_ADV(lp)) {
EMIT(utf_ptr2char((char *)lp));
- if (lp != reg_prev_sub) {
+ if (lp != (uint8_t *)reg_prev_sub) {
EMIT(NFA_CONCAT);
}
}
@@ -2094,6 +2075,12 @@ static int nfa_regatom(void)
break;
case '#':
+ if (regparse[0] == '=' && regparse[1] >= 48
+ && regparse[1] <= 50) {
+ // misplaced \%#=1
+ semsg(_(e_atom_engine_must_be_at_start_of_pattern), regparse[1]);
+ return FAIL;
+ }
EMIT(NFA_CURSOR);
break;
@@ -2141,6 +2128,7 @@ static int nfa_regatom(void)
int64_t n = 0;
const int cmp = c;
bool cur = false;
+ bool got_digit = false;
if (c == '<' || c == '>') {
c = getchr();
@@ -2151,7 +2139,7 @@ static int nfa_regatom(void)
}
while (ascii_isdigit(c)) {
if (cur) {
- semsg(_(e_regexp_number_after_dot_pos_search), no_Magic(c));
+ semsg(_(e_regexp_number_after_dot_pos_search_chr), no_Magic(c));
return FAIL;
}
if (n > (INT32_MAX - (c - '0')) / 10) {
@@ -2161,10 +2149,15 @@ static int nfa_regatom(void)
}
n = n * 10 + (c - '0');
c = getchr();
+ got_digit = true;
}
if (c == 'l' || c == 'c' || c == 'v') {
int32_t limit = INT32_MAX;
+ if (!cur && !got_digit) {
+ semsg(_(e_nfa_regexp_missing_value_in_chr), no_Magic(c));
+ return FAIL;
+ }
if (c == 'l') {
if (cur) {
n = curwin->w_cursor.lnum;
@@ -2216,25 +2209,21 @@ static int nfa_regatom(void)
case Magic('['):
collection:
- /*
- * [abc] uses NFA_START_COLL - NFA_END_COLL
- * [^abc] uses NFA_START_NEG_COLL - NFA_END_NEG_COLL
- * Each character is produced as a regular state, using
- * NFA_CONCAT to bind them together.
- * Besides normal characters there can be:
- * - character classes NFA_CLASS_*
- * - ranges, two characters followed by NFA_RANGE.
- */
-
- p = (char_u *)regparse;
- endp = skip_anyof((char *)p);
+ // [abc] uses NFA_START_COLL - NFA_END_COLL
+ // [^abc] uses NFA_START_NEG_COLL - NFA_END_NEG_COLL
+ // Each character is produced as a regular state, using
+ // NFA_CONCAT to bind them together.
+ // Besides normal characters there can be:
+ // - character classes NFA_CLASS_*
+ // - ranges, two characters followed by NFA_RANGE.
+
+ p = (uint8_t *)regparse;
+ endp = (uint8_t *)skip_anyof((char *)p);
if (*endp == ']') {
- /*
- * Try to reverse engineer character classes. For example,
- * recognize that [0-9] stands for \d and [A-Za-z_] for \h,
- * and perform the necessary substitutions in the NFA.
- */
- int result = nfa_recognize_char_class((char_u *)regparse, endp, extra == NFA_ADD_NL);
+ // Try to reverse engineer character classes. For example,
+ // recognize that [0-9] stands for \d and [A-Za-z_] for \h,
+ // and perform the necessary substitutions in the NFA.
+ int result = nfa_recognize_char_class((uint8_t *)regparse, endp, extra == NFA_ADD_NL);
if (result != FAIL) {
if (result >= NFA_FIRST_NL && result <= NFA_LAST_NL) {
EMIT(result - NFA_ADD_NL);
@@ -2247,10 +2236,8 @@ collection:
MB_PTR_ADV(regparse);
return OK;
}
- /*
- * Failed to recognize a character class. Use the simple
- * version that turns [abc] into 'a' OR 'b' OR 'c'
- */
+ // Failed to recognize a character class. Use the simple
+ // version that turns [abc] into 'a' OR 'b' OR 'c'
startc = -1;
negated = false;
if (*regparse == '^') { // negated range
@@ -2268,7 +2255,7 @@ collection:
}
// Emit the OR branches for each character in the []
emit_range = false;
- while ((char_u *)regparse < endp) {
+ while ((uint8_t *)regparse < endp) {
int oldstartc = startc;
startc = -1;
got_coll_char = false;
@@ -2375,10 +2362,10 @@ collection:
// accepts "\t", "\e", etc., but only when the 'l' flag in
// 'cpoptions' is not included.
if (*regparse == '\\'
- && (char_u *)regparse + 1 <= endp
- && (vim_strchr(REGEXP_INRANGE, regparse[1]) != NULL
+ && (uint8_t *)regparse + 1 <= endp
+ && (vim_strchr(REGEXP_INRANGE, (uint8_t)regparse[1]) != NULL
|| (!reg_cpo_lit
- && vim_strchr(REGEXP_ABBR, regparse[1])
+ && vim_strchr(REGEXP_ABBR, (uint8_t)regparse[1])
!= NULL))) {
MB_PTR_ADV(regparse);
@@ -2542,16 +2529,14 @@ nfa_do_multibyte:
return OK;
}
-/*
- * Parse something followed by possible [*+=].
- *
- * A piece is an atom, possibly followed by a multi, an indication of how many
- * times the atom can be matched. Example: "a*" matches any sequence of "a"
- * characters: "", "a", "aa", etc.
- *
- * piece ::= atom
- * or atom multi
- */
+// Parse something followed by possible [*+=].
+//
+// A piece is an atom, possibly followed by a multi, an indication of how many
+// times the atom can be matched. Example: "a*" matches any sequence of "a"
+// characters: "", "a", "aa", etc.
+//
+// piece ::= atom
+// or atom multi
static int nfa_regpiece(void)
{
int i;
@@ -2589,17 +2574,15 @@ static int nfa_regpiece(void)
break;
case Magic('+'):
- /*
- * Trick: Normally, (a*)\+ would match the whole input "aaa". The
- * first and only submatch would be "aaa". But the backtracking
- * engine interprets the plus as "try matching one more time", and
- * a* matches a second time at the end of the input, the empty
- * string.
- * The submatch will be the empty string.
- *
- * In order to be consistent with the old engine, we replace
- * <atom>+ with <atom><atom>*
- */
+ // Trick: Normally, (a*)\+ would match the whole input "aaa". The
+ // first and only submatch would be "aaa". But the backtracking
+ // engine interprets the plus as "try matching one more time", and
+ // a* matches a second time at the end of the input, the empty
+ // string.
+ // The submatch will be the empty string.
+ //
+ // In order to be consistent with the old engine, we replace
+ // <atom>+ with <atom><atom>*
restore_parse_state(&old_state);
curchr = -1;
if (nfa_regatom() == FAIL) {
@@ -2758,16 +2741,14 @@ static int nfa_regpiece(void)
return OK;
}
-/*
- * Parse one or more pieces, concatenated. It matches a match for the
- * 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.
- */
+// Parse one or more pieces, concatenated. It matches a match for the
+// 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.
static int nfa_regconcat(void)
{
bool cont = true;
@@ -2831,18 +2812,16 @@ static int nfa_regconcat(void)
return OK;
}
-/*
- * Parse a branch, one or more concats, separated by "\&". It matches the
- * last concat, but only if all the preceding concats also match at the same
- * position. Examples:
- * "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.
- */
+// Parse a branch, one or more concats, separated by "\&". It matches the
+// last concat, but only if all the preceding concats also match at the same
+// position. Examples:
+// "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.
static int nfa_regbranch(void)
{
int old_post_pos;
@@ -2948,7 +2927,7 @@ static int nfa_reg(int paren)
}
#ifdef REGEXP_DEBUG
-static char_u code[50];
+static uint8_t code[50];
static void nfa_set_code(int c)
{
@@ -3296,42 +3275,40 @@ static void nfa_set_code(int c)
}
static FILE *log_fd;
-static char_u e_log_open_failed[] =
+static uint8_t e_log_open_failed[] =
N_("Could not open temporary log file for writing, displaying on stderr... ");
-/*
- * Print the postfix notation of the current regexp.
- */
-static void nfa_postfix_dump(char_u *expr, int retval)
+// Print the postfix notation of the current regexp.
+static void nfa_postfix_dump(uint8_t *expr, int retval)
{
int *p;
FILE *f;
f = fopen(NFA_REGEXP_DUMP_LOG, "a");
- if (f != NULL) {
- fprintf(f, "\n-------------------------\n");
- if (retval == FAIL) {
- fprintf(f, ">>> NFA engine failed... \n");
- } else if (retval == OK) {
- fprintf(f, ">>> NFA engine succeeded !\n");
- }
- fprintf(f, "Regexp: \"%s\"\nPostfix notation (char): \"", expr);
- for (p = post_start; *p && p < post_ptr; p++) {
- nfa_set_code(*p);
- fprintf(f, "%s, ", code);
- }
- fprintf(f, "\"\nPostfix notation (int): ");
- for (p = post_start; *p && p < post_ptr; p++) {
- fprintf(f, "%d ", *p);
- }
- fprintf(f, "\n\n");
- fclose(f);
+ if (f == NULL) {
+ return;
+ }
+
+ fprintf(f, "\n-------------------------\n");
+ if (retval == FAIL) {
+ fprintf(f, ">>> NFA engine failed... \n");
+ } else if (retval == OK) {
+ fprintf(f, ">>> NFA engine succeeded !\n");
+ }
+ fprintf(f, "Regexp: \"%s\"\nPostfix notation (char): \"", expr);
+ for (p = post_start; *p && p < post_ptr; p++) {
+ nfa_set_code(*p);
+ fprintf(f, "%s, ", code);
}
+ fprintf(f, "\"\nPostfix notation (int): ");
+ for (p = post_start; *p && p < post_ptr; p++) {
+ fprintf(f, "%d ", *p);
+ }
+ fprintf(f, "\n\n");
+ fclose(f);
}
-/*
- * Print the NFA starting with a root node "state".
- */
+// Print the NFA starting with a root node "state".
static void nfa_print_state(FILE *debugf, nfa_state_T *state)
{
garray_T indent;
@@ -3344,7 +3321,7 @@ 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;
+ uint8_t *p;
if (state == NULL) {
return;
@@ -3353,15 +3330,15 @@ static void nfa_print_state2(FILE *debugf, nfa_state_T *state, garray_T *indent)
fprintf(debugf, "(%2d)", abs(state->id));
// Output indent
- p = (char_u *)indent->ga_data;
+ p = (uint8_t *)indent->ga_data;
if (indent->ga_len >= 3) {
int last = indent->ga_len - 3;
- char_u save[2];
+ uint8_t save[2];
- STRNCPY(save, &p[last], 2);
- STRNCPY(&p[last], "+-", 2);
+ strncpy(save, &p[last], 2); // NOLINT(runtime/printf)
+ memcpy(&p[last], "+-", 2);
fprintf(debugf, " %s", p);
- STRNCPY(&p[last], save, 2); // NOLINT(runtime/printf)
+ strncpy(&p[last], save, 2); // NOLINT(runtime/printf)
} else {
fprintf(debugf, " %s", p);
}
@@ -3381,9 +3358,9 @@ static void nfa_print_state2(FILE *debugf, nfa_state_T *state, garray_T *indent)
// grow indent for state->out
indent->ga_len -= 1;
if (state->out1) {
- ga_concat(indent, (char_u *)"| ");
+ ga_concat(indent, (uint8_t *)"| ");
} else {
- ga_concat(indent, (char_u *)" ");
+ ga_concat(indent, (uint8_t *)" ");
}
ga_append(indent, NUL);
@@ -3391,7 +3368,7 @@ static void nfa_print_state2(FILE *debugf, nfa_state_T *state, garray_T *indent)
// replace last part of indent for state->out1
indent->ga_len -= 3;
- ga_concat(indent, (char_u *)" ");
+ ga_concat(indent, (uint8_t *)" ");
ga_append(indent, NUL);
nfa_print_state2(debugf, state->out1, indent);
@@ -3401,36 +3378,34 @@ static void nfa_print_state2(FILE *debugf, nfa_state_T *state, garray_T *indent)
ga_append(indent, NUL);
}
-/*
- * Print the NFA state machine.
- */
+// Print the NFA state machine.
static void nfa_dump(nfa_regprog_T *prog)
{
FILE *debugf = fopen(NFA_REGEXP_DUMP_LOG, "a");
- if (debugf != NULL) {
- nfa_print_state(debugf, prog->start);
+ if (debugf == NULL) {
+ return;
+ }
- if (prog->reganch) {
- fprintf(debugf, "reganch: %d\n", prog->reganch);
- }
- if (prog->regstart != NUL) {
- fprintf(debugf, "regstart: %c (decimal: %d)\n",
- prog->regstart, prog->regstart);
- }
- if (prog->match_text != NULL) {
- fprintf(debugf, "match_text: \"%s\"\n", prog->match_text);
- }
+ nfa_print_state(debugf, prog->start);
- fclose(debugf);
+ if (prog->reganch) {
+ fprintf(debugf, "reganch: %d\n", prog->reganch);
+ }
+ if (prog->regstart != NUL) {
+ fprintf(debugf, "regstart: %c (decimal: %d)\n",
+ prog->regstart, prog->regstart);
}
+ if (prog->match_text != NULL) {
+ fprintf(debugf, "match_text: \"%s\"\n", prog->match_text);
+ }
+
+ fclose(debugf);
}
-#endif /* REGEXP_DEBUG */
+#endif // REGEXP_DEBUG
-/*
- * Parse r.e. @expr and convert it into postfix form.
- * Return the postfix string on success, NULL otherwise.
- */
+// Parse r.e. @expr and convert it into postfix form.
+// Return the postfix string on success, NULL otherwise.
static int *re2post(void)
{
if (nfa_reg(REG_NOPAREN) == FAIL) {
@@ -3442,18 +3417,14 @@ static int *re2post(void)
// NB. Some of the code below is inspired by Russ's.
-/*
- * Represents an NFA state plus zero or one or two arrows exiting.
- * if c == MATCH, no arrows out; matching state.
- * If c == SPLIT, unlabeled arrows to out and out1 (if != NULL).
- * If c < 256, labeled arrow with character c to out.
- */
+// Represents an NFA state plus zero or one or two arrows exiting.
+// if c == MATCH, no arrows out; matching state.
+// If c == SPLIT, unlabeled arrows to out and out1 (if != NULL).
+// If c < 256, labeled arrow with character c to out.
static nfa_state_T *state_ptr; // points to nfa_prog->state
-/*
- * Allocate and initialize nfa_state_T.
- */
+// Allocate and initialize nfa_state_T.
static nfa_state_T *alloc_state(int c, nfa_state_T *out, nfa_state_T *out1)
{
nfa_state_T *s;
@@ -3476,16 +3447,12 @@ static nfa_state_T *alloc_state(int c, nfa_state_T *out, nfa_state_T *out1)
return s;
}
-/*
- * A partially built NFA without the matching state filled in.
- * Frag_T.start points at the start state.
- * Frag_T.out is a list of places that need to be set to the
- * next state for this fragment.
- */
+// A partially built NFA without the matching state filled in.
+// Frag_T.start points at the start state.
+// Frag_T.out is a list of places that need to be set to the
+// next state for this fragment.
-/*
- * Initialize a Frag_T struct and return it.
- */
+// Initialize a Frag_T struct and return it.
static Frag_T frag(nfa_state_T *start, Ptrlist *out)
{
Frag_T n;
@@ -3495,9 +3462,7 @@ static Frag_T frag(nfa_state_T *start, Ptrlist *out)
return n;
}
-/*
- * Create singleton list containing just outp.
- */
+// Create singleton list containing just outp.
static Ptrlist *list1(nfa_state_T **outp)
{
Ptrlist *l;
@@ -3507,9 +3472,7 @@ static Ptrlist *list1(nfa_state_T **outp)
return l;
}
-/*
- * Patch the list of states at out to point to start.
- */
+// Patch the list of states at out to point to start.
static void patch(Ptrlist *l, nfa_state_T *s)
{
Ptrlist *next;
@@ -3520,9 +3483,7 @@ static void patch(Ptrlist *l, nfa_state_T *s)
}
}
-/*
- * Join the two lists l1 and l2, returning the combination.
- */
+// Join the two lists l1 and l2, returning the combination.
static Ptrlist *append(Ptrlist *l1, Ptrlist *l2)
{
Ptrlist *oldl1;
@@ -3535,9 +3496,7 @@ static Ptrlist *append(Ptrlist *l1, Ptrlist *l2)
return oldl1;
}
-/*
- * Stack used for transforming postfix form into NFA.
- */
+// Stack used for transforming postfix form into NFA.
static Frag_T empty;
static void st_error(int *postfix, int *end, int *p)
@@ -3580,9 +3539,7 @@ static void st_error(int *postfix, int *end, int *p)
emsg(_("E874: (NFA) Could not pop the stack!"));
}
-/*
- * Push an item onto the stack.
- */
+// Push an item onto the stack.
static void st_push(Frag_T s, Frag_T **p, Frag_T *stack_end)
{
Frag_T *stackp = *p;
@@ -3594,9 +3551,7 @@ static void st_push(Frag_T s, Frag_T **p, Frag_T *stack_end)
*p = *p + 1;
}
-/*
- * Pop an item from the stack.
- */
+// Pop an item from the stack.
static Frag_T st_pop(Frag_T **p, Frag_T *stack)
{
Frag_T *stackp;
@@ -3609,10 +3564,8 @@ static Frag_T st_pop(Frag_T **p, Frag_T *stack)
return **p;
}
-/*
- * Estimate the maximum byte length of anything matching "state".
- * When unknown or unlimited return -1.
- */
+// Estimate the maximum byte length of anything matching "state".
+// When unknown or unlimited return -1.
static int nfa_max_width(nfa_state_T *startstate, int depth)
{
int l, r;
@@ -3815,10 +3768,8 @@ static int nfa_max_width(nfa_state_T *startstate, int depth)
return -1;
}
-/*
- * Convert a postfix form into its equivalent NFA.
- * Return the NFA start state on success, NULL otherwise.
- */
+// Convert a postfix form into its equivalent NFA.
+// Return the NFA start state on success, NULL otherwise.
static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size)
{
int *p;
@@ -3854,7 +3805,7 @@ static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size)
stack_end = stack + (nstate + 1);
}
- for (p = postfix; p < end; ++p) {
+ for (p = postfix; p < end; p++) {
switch (*p) {
case NFA_CONCAT:
// Concatenation.
@@ -4338,15 +4289,13 @@ theend:
#undef PUSH
}
-/*
- * After building the NFA program, inspect it to add optimization hints.
- */
+// After building the NFA program, inspect it to add optimization hints.
static void nfa_postprocess(nfa_regprog_T *prog)
{
int i;
int c;
- for (i = 0; i < prog->nstate; ++i) {
+ for (i = 0; i < prog->nstate; i++) {
c = prog->state[i].c;
if (c == NFA_START_INVISIBLE
|| c == NFA_START_INVISIBLE_NEG
@@ -4478,59 +4427,60 @@ static void clear_sub(regsub_T *sub)
sub->in_use = 0;
}
-/*
- * Copy the submatches from "from" to "to".
- */
+// Copy the submatches from "from" to "to".
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) {
- memmove(&to->list.multi[0], &from->list.multi[0],
- sizeof(struct multipos) * (size_t)from->in_use);
- } else {
- memmove(&to->list.line[0], &from->list.line[0],
- sizeof(struct linepos) * (size_t)from->in_use);
- }
+ if (from->in_use <= 0) {
+ return;
+ }
+
+ // Copy the match start and end positions.
+ if (REG_MULTI) {
+ memmove(&to->list.multi[0], &from->list.multi[0],
+ sizeof(struct multipos) * (size_t)from->in_use);
+ to->orig_start_col = from->orig_start_col;
+ } else {
+ memmove(&to->list.line[0], &from->list.line[0],
+ sizeof(struct linepos) * (size_t)from->in_use);
}
}
-/*
- * Like copy_sub() but exclude the main match.
- */
+// Like copy_sub() but exclude the main match.
static void copy_sub_off(regsub_T *to, regsub_T *from)
{
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) {
- memmove(&to->list.multi[1], &from->list.multi[1],
- sizeof(struct multipos) * (size_t)(from->in_use - 1));
- } else {
- memmove(&to->list.line[1], &from->list.line[1],
- sizeof(struct linepos) * (size_t)(from->in_use - 1));
- }
+ if (from->in_use <= 1) {
+ return;
+ }
+
+ // Copy the match start and end positions.
+ if (REG_MULTI) {
+ memmove(&to->list.multi[1], &from->list.multi[1],
+ sizeof(struct multipos) * (size_t)(from->in_use - 1));
+ } else {
+ memmove(&to->list.line[1], &from->list.line[1],
+ sizeof(struct linepos) * (size_t)(from->in_use - 1));
}
}
-/*
- * Like copy_sub() but only do the end of the main match if \ze is present.
- */
+// Like copy_sub() but only do the end of the main match if \ze is present.
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) {
- 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) {
- to->list.line[0].end = from->list.line[0].end;
- }
+ if (!rex.nfa_has_zend) {
+ return;
+ }
+
+ if (REG_MULTI) {
+ 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) {
+ to->list.line[0].end = from->list.line[0].end;
}
}
}
@@ -4543,8 +4493,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;
+ uint8_t *sp1;
+ uint8_t *sp2;
todo = sub1->in_use > sub2->in_use ? sub1->in_use : sub2->in_use;
if (REG_MULTI) {
@@ -4623,6 +4573,20 @@ static bool sub_equal(regsub_T *sub1, regsub_T *sub2)
}
#ifdef REGEXP_DEBUG
+static void open_debug_log(TriState result)
+{
+ log_fd = fopen(NFA_REGEXP_RUN_LOG, "a");
+ if (log_fd == NULL) {
+ emsg(_(e_log_open_failed));
+ log_fd = stderr;
+ }
+
+ fprintf(log_fd, "****************************\n");
+ fprintf(log_fd, "FINISHED RUNNING nfa_regmatch() recursively\n");
+ fprintf(log_fd, "MATCH = %s\n", result == kTrue ? "OK" : result == kNone ? "MAYBE" : "FALSE");
+ fprintf(log_fd, "****************************\n");
+}
+
static void report_state(char *action, regsub_T *sub, nfa_state_T *state, int lid, nfa_pim_T *pim)
{
int col;
@@ -4635,6 +4599,9 @@ static void report_state(char *action, regsub_T *sub, nfa_state_T *state, int li
col = (int)(sub->list.line[0].start - rex.line);
}
nfa_set_code(state->c);
+ if (log_fd == NULL) {
+ open_debug_log(kNone);
+ }
fprintf(log_fd, "> %s state %d to list %d. char %d: %s (start col %d)%s\n",
action, abs(state->id), lid, state->c, code, col,
pim_info(pim));
@@ -4822,7 +4789,7 @@ static regsubs_T *addstate(nfa_list_T *l, nfa_state_T *state, regsubs_T *subs_ar
nfa_thread_T *thread;
struct multipos save_multipos;
int save_in_use;
- char_u *save_ptr;
+ uint8_t *save_ptr;
int i;
regsub_T *sub;
regsubs_T *subs = subs_arg;
@@ -4925,7 +4892,7 @@ static regsubs_T *addstate(nfa_list_T *l, nfa_state_T *state, regsubs_T *subs_ar
// When called from addstate_here() do insert before
// existing states.
if (add_here) {
- for (k = 0; k < l->n && k < listindex; ++k) {
+ for (k = 0; k < l->n && k < listindex; k++) {
if (l->t[k].state->id == state->id) {
found = true;
break;
@@ -5065,7 +5032,7 @@ skip_add:
save_in_use = -1;
} else {
save_in_use = sub->in_use;
- for (i = sub->in_use; i < subidx; ++i) {
+ for (i = sub->in_use; i < subidx; i++) {
sub->list.multi[i].start_lnum = -1;
sub->list.multi[i].end_lnum = -1;
}
@@ -5086,7 +5053,7 @@ skip_add:
save_in_use = -1;
} else {
save_in_use = sub->in_use;
- for (i = sub->in_use; i < subidx; ++i) {
+ for (i = sub->in_use; i < subidx; i++) {
sub->list.line[i].start = NULL;
sub->list.line[i].end = NULL;
}
@@ -5279,15 +5246,13 @@ static regsubs_T *addstate_here(nfa_list_T *l, nfa_state_T *state, regsubs_T *su
sizeof(nfa_thread_T) * (size_t)count);
}
}
- --l->n;
+ l->n--;
*ip = listidx - 1;
return r;
}
-/*
- * Check character class "class" against current character c.
- */
+// Check character class "class" against current character c.
static int check_char_class(int class, int c)
{
switch (class) {
@@ -5421,8 +5386,8 @@ retempty:
&& sub->list.multi[subidx].end_lnum == rex.lnum) {
len = sub->list.multi[subidx].end_col
- sub->list.multi[subidx].start_col;
- if (cstrncmp(rex.line + sub->list.multi[subidx].start_col,
- rex.input, &len) == 0) {
+ if (cstrncmp((char *)rex.line + sub->list.multi[subidx].start_col,
+ (char *)rex.input, &len) == 0) {
*bytelen = len;
return true;
}
@@ -5441,7 +5406,7 @@ retempty:
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) {
+ if (cstrncmp((char *)sub->list.line[subidx].start, (char *)rex.input, &len) == 0) {
*bytelen = len;
return true;
}
@@ -5465,19 +5430,17 @@ static int match_zref(int subidx, int *bytelen)
return true;
}
- len = (int)STRLEN(re_extmatch_in->matches[subidx]);
- if (cstrncmp(re_extmatch_in->matches[subidx], rex.input, &len) == 0) {
+ len = (int)strlen((char *)re_extmatch_in->matches[subidx]);
+ if (cstrncmp((char *)re_extmatch_in->matches[subidx], (char *)rex.input, &len) == 0) {
*bytelen = len;
return true;
}
return false;
}
-/*
- * Save list IDs for all NFA states of "prog" into "list".
- * Also reset the IDs to zero.
- * Only used for the recursive value lastlist[1].
- */
+// Save list IDs for all NFA states of "prog" into "list".
+// Also reset the IDs to zero.
+// Only used for the recursive value lastlist[1].
static void nfa_save_listids(nfa_regprog_T *prog, int *list)
{
int i;
@@ -5492,9 +5455,7 @@ static void nfa_save_listids(nfa_regprog_T *prog, int *list)
}
}
-/*
- * Restore list IDs from "list" to all NFA states.
- */
+// Restore list IDs from "list" to all NFA states.
static void nfa_restore_listids(nfa_regprog_T *prog, int *list)
{
int i;
@@ -5518,11 +5479,9 @@ static bool nfa_re_num_cmp(uintmax_t val, int op, uintmax_t pos)
return val == pos;
}
-/*
- * Recursively call nfa_regmatch()
- * "pim" is NULL or contains info about a Postponed Invisible Match (start
- * position).
- */
+// 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)
FUNC_ATTR_NONNULL_ARG(1, 3, 5, 6, 7)
@@ -5573,10 +5532,10 @@ static int recursive_regmatch(nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T
// bytes if possible.
if (state->val <= 0) {
if (REG_MULTI) {
- rex.line = reg_getline(--rex.lnum);
+ rex.line = (uint8_t *)reg_getline(--rex.lnum);
if (rex.line == NULL) {
// can't go before the first line
- rex.line = reg_getline(++rex.lnum);
+ rex.line = (uint8_t *)reg_getline(++rex.lnum);
}
}
rex.input = rex.line;
@@ -5584,18 +5543,18 @@ static int recursive_regmatch(nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T
if (REG_MULTI && (int)(rex.input - rex.line) < state->val) {
// Not enough bytes in this line, go to end of
// previous line.
- rex.line = reg_getline(--rex.lnum);
+ rex.line = (uint8_t *)reg_getline(--rex.lnum);
if (rex.line == NULL) {
// can't go before the first line
- rex.line = reg_getline(++rex.lnum);
+ rex.line = (uint8_t *)reg_getline(++rex.lnum);
rex.input = rex.line;
} else {
- rex.input = rex.line + STRLEN(rex.line);
+ rex.input = rex.line + strlen((char *)rex.line);
}
}
if ((int)(rex.input - rex.line) >= state->val) {
rex.input -= state->val;
- rex.input -= utf_head_off(rex.line, rex.input);
+ rex.input -= utf_head_off((char *)rex.line, (char *)rex.input);
} else {
rex.input = rex.line;
}
@@ -5646,7 +5605,7 @@ static int recursive_regmatch(nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T
// restore position in input text
rex.lnum = save_reglnum;
if (REG_MULTI) {
- rex.line = reg_getline(rex.lnum);
+ rex.line = (uint8_t *)reg_getline(rex.lnum);
}
rex.input = rex.line + save_reginput_col;
if (result != NFA_TOO_EXPENSIVE) {
@@ -5656,27 +5615,16 @@ static int recursive_regmatch(nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T
nfa_endp = save_nfa_endp;
#ifdef REGEXP_DEBUG
- log_fd = fopen(NFA_REGEXP_RUN_LOG, "a");
- if (log_fd != NULL) {
- fprintf(log_fd, "****************************\n");
- fprintf(log_fd, "FINISHED RUNNING nfa_regmatch() recursively\n");
- fprintf(log_fd, "MATCH = %s\n", !result ? "false" : "OK");
- fprintf(log_fd, "****************************\n");
- } else {
- emsg(_(e_log_open_failed));
- log_fd = stderr;
- }
+ open_debug_log(result);
#endif
return result;
}
-/*
- * Estimate the chance of a match with "state" failing.
- * empty match: 0
- * NFA_ANY: 1
- * specific character: 99
- */
+// Estimate the chance of a match with "state" failing.
+// empty match: 0
+// NFA_ANY: 1
+// specific character: 99
static int failure_chance(nfa_state_T *state, int depth)
{
int c = state->c;
@@ -5831,12 +5779,10 @@ static int failure_chance(nfa_state_T *state, int depth)
return 50;
}
-/*
- * Skip until the char "c" we know a match must start with.
- */
+// Skip until the char "c" we know a match must start with.
static int skip_to_start(int c, colnr_T *colp)
{
- const char_u *const s = cstrchr(rex.line + *colp, c);
+ const uint8_t *const s = (uint8_t *)cstrchr((char *)rex.line + *colp, c);
if (s == NULL) {
return FAIL;
}
@@ -5844,22 +5790,20 @@ static int skip_to_start(int c, colnr_T *colp)
return OK;
}
-/*
- * Check for a match with match_text.
- * Called after skip_to_start() has found regstart.
- * Returns zero for no match, 1 for a match.
- */
-static long find_match_text(colnr_T startcol, int regstart, char_u *match_text)
+// Check for a match with match_text.
+// Called after skip_to_start() has found regstart.
+// Returns zero for no match, 1 for a match.
+static long find_match_text(colnr_T *startcol, int regstart, uint8_t *match_text)
{
#define PTR2LEN(x) utf_ptr2len(x)
- colnr_T col = startcol;
- int regstart_len = PTR2LEN((char *)rex.line + startcol);
+ colnr_T col = *startcol;
+ int regstart_len = PTR2LEN((char *)rex.line + col);
for (;;) {
bool match = true;
- char_u *s1 = match_text;
- char_u *s2 = rex.line + col + regstart_len; // skip regstart
+ uint8_t *s1 = match_text;
+ uint8_t *s2 = rex.line + col + regstart_len; // skip regstart
while (*s1) {
int c1_len = PTR2LEN((char *)s1);
int c1 = utf_ptr2char((char *)s1);
@@ -5887,6 +5831,7 @@ static long find_match_text(colnr_T startcol, int regstart, char_u *match_text)
rex.reg_startp[0] = rex.line + col;
rex.reg_endp[0] = s2;
}
+ *startcol = col;
return 1L;
}
@@ -5896,6 +5841,8 @@ static long find_match_text(colnr_T startcol, int regstart, char_u *match_text)
break;
}
}
+
+ *startcol = col;
return 0L;
#undef PTR2LEN
@@ -5971,16 +5918,15 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
#ifdef REGEXP_DEBUG
log_fd = fopen(NFA_REGEXP_RUN_LOG, "a");
- if (log_fd != NULL) {
- 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);
- fprintf(log_fd, "**********************************\n");
- } else {
+ if (log_fd == NULL) {
emsg(_(e_log_open_failed));
log_fd = stderr;
}
+ 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);
+ fprintf(log_fd, "**********************************\n");
#endif
thislist = &list[0];
@@ -6000,6 +5946,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
if (REG_MULTI) {
m->norm.list.multi[0].start_lnum = rex.lnum;
m->norm.list.multi[0].start_col = (colnr_T)(rex.input - rex.line);
+ m->norm.orig_start_col = m->norm.list.multi[0].start_col;
} else {
m->norm.list.line[0].start = rex.input;
}
@@ -6019,9 +5966,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
add_off = clen; \
}
- /*
- * Run for each character.
- */
+ // Run for each character.
for (;;) {
int curc = utf_ptr2char((char *)rex.input);
int clen = utfc_ptr2len((char *)rex.input);
@@ -6067,9 +6012,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
#ifdef NFA_REGEXP_DEBUG_LOG
fprintf(debug, "\n-------------------\n");
#endif
- /*
- * If the state lists are empty we can stop.
- */
+ // If the state lists are empty we can stop.
if (thislist->n == 0) {
break;
}
@@ -6112,10 +6055,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
}
#endif
- /*
- * Handle the possible codes of the current state.
- * The most important is NFA_MATCH.
- */
+ // Handle the possible codes of the current state.
+ // The most important is NFA_MATCH.
add_state = NULL;
add_here = false;
add_count = 0;
@@ -6410,7 +6351,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
int this_class;
// Get class of current and previous char (if it exists).
- this_class = mb_get_class_tab(rex.input, rex.reg_buf->b_chartab);
+ this_class = mb_get_class_tab((char *)rex.input, rex.reg_buf->b_chartab);
if (this_class <= 1) {
result = false;
} else if (reg_prev_class() == this_class) {
@@ -6431,7 +6372,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
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);
+ this_class = mb_get_class_tab((char *)rex.input, rex.reg_buf->b_chartab);
prev_class = reg_prev_class();
if (this_class == prev_class
|| prev_class == 0 || prev_class == 1) {
@@ -6643,13 +6584,13 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
break;
case NFA_KWORD: // \k
- result = vim_iswordp_buf(rex.input, rex.reg_buf);
+ result = vim_iswordp_buf((char *)rex.input, rex.reg_buf);
ADD_STATE_IF_MATCH(t->state);
break;
case NFA_SKWORD: // \K
result = !ascii_isdigit(curc)
- && vim_iswordp_buf(rex.input, rex.reg_buf);
+ && vim_iswordp_buf((char *)rex.input, rex.reg_buf);
ADD_STATE_IF_MATCH(t->state);
break;
@@ -6910,7 +6851,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
result = col > t->state->val * ts;
}
if (!result) {
- uintmax_t lts = win_linetabsize(wp, rex.line, col);
+ uintmax_t lts = win_linetabsize(wp, rex.reg_firstlnum + rex.lnum, (char *)rex.line, col);
assert(t->state->val >= 0);
result = nfa_re_num_cmp((uintmax_t)t->state->val, op, lts + 1);
}
@@ -6929,7 +6870,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
// Line may have been freed, get it again.
if (REG_MULTI) {
- rex.line = reg_getline(rex.lnum);
+ rex.line = (uint8_t *)reg_getline(rex.lnum);
rex.input = rex.line + col;
}
@@ -6939,7 +6880,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
pos_T *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))
+ ? (colnr_T)strlen((char *)reg_getline(pos->lnum - rex.reg_firstlnum))
: pos->col;
result = pos->lnum == rex.lnum + rex.reg_firstlnum
@@ -7185,6 +7126,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
if (REG_MULTI) {
m->norm.list.multi[0].start_col =
(colnr_T)(rex.input - rex.line) + clen;
+ m->norm.orig_start_col =
+ m->norm.list.multi[0].start_col;
} else {
m->norm.list.line[0].start = rex.input + clen;
}
@@ -7318,6 +7261,9 @@ static long nfa_regtry(nfa_regprog_T *prog, colnr_T col, proftime_T *tm, int *ti
rex.reg_endpos[i].lnum = subs.norm.list.multi[i].end_lnum;
rex.reg_endpos[i].col = subs.norm.list.multi[i].end_col;
}
+ if (rex.reg_mmatch != NULL) {
+ rex.reg_mmatch->rmm_matchcol = subs.norm.orig_start_col;
+ }
if (rex.reg_startpos[0].lnum < 0) {
rex.reg_startpos[0].lnum = 0;
@@ -7362,15 +7308,15 @@ static long nfa_regtry(nfa_regprog_T *prog, colnr_T col, proftime_T *tm, int *ti
&& mpos->start_lnum == mpos->end_lnum
&& mpos->end_col >= mpos->start_col) {
re_extmatch_out->matches[i] =
- vim_strnsave(reg_getline(mpos->start_lnum) + mpos->start_col,
- (size_t)(mpos->end_col - mpos->start_col));
+ (uint8_t *)xstrnsave((char *)reg_getline(mpos->start_lnum) + mpos->start_col,
+ (size_t)(mpos->end_col - mpos->start_col));
}
} else {
struct linepos *lpos = &subs.synt.list.line[i];
if (lpos->start != NULL && lpos->end != NULL) {
re_extmatch_out->matches[i] =
- vim_strnsave(lpos->start, (size_t)(lpos->end - lpos->start));
+ (uint8_t *)xstrnsave((char *)lpos->start, (size_t)(lpos->end - lpos->start));
}
}
}
@@ -7389,7 +7335,7 @@ static long nfa_regtry(nfa_regprog_T *prog, colnr_T col, proftime_T *tm, int *ti
///
/// @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(uint8_t *line, colnr_T startcol, proftime_T *tm, int *timed_out)
{
nfa_regprog_T *prog;
long retval = 0L;
@@ -7397,13 +7343,13 @@ static long nfa_regexec_both(char_u *line, colnr_T startcol, proftime_T *tm, int
if (REG_MULTI) {
prog = (nfa_regprog_T *)rex.reg_mmatch->regprog;
- line = reg_getline((linenr_T)0); // relative to the cursor
+ line = (uint8_t *)reg_getline((linenr_T)0); // relative to the cursor
rex.reg_startpos = rex.reg_mmatch->startpos;
rex.reg_endpos = rex.reg_mmatch->endpos;
} else {
prog = (nfa_regprog_T *)rex.reg_match->regprog;
- rex.reg_startp = rex.reg_match->startp;
- rex.reg_endp = rex.reg_match->endp;
+ rex.reg_startp = (uint8_t **)rex.reg_match->startp;
+ rex.reg_endp = (uint8_t **)rex.reg_match->endp;
}
// Be paranoid...
@@ -7460,7 +7406,13 @@ static long nfa_regexec_both(char_u *line, colnr_T startcol, proftime_T *tm, int
// If match_text is set it contains the full text that must match.
// Nothing else to try. Doesn't handle combining chars well.
if (prog->match_text != NULL && !rex.reg_icombine) {
- return find_match_text(col, prog->regstart, prog->match_text);
+ retval = find_match_text(&col, prog->regstart, prog->match_text);
+ if (REG_MULTI) {
+ rex.reg_mmatch->rmm_matchcol = col;
+ } else {
+ rex.reg_match->rm_matchcol = col;
+ }
+ return retval;
}
}
@@ -7500,17 +7452,19 @@ theend:
if (rex.reg_match->endp[0] < rex.reg_match->startp[0]) {
rex.reg_match->endp[0] = rex.reg_match->startp[0];
}
+
+ // startpos[0] may be set by "\zs", also return the column where
+ // the whole pattern matched.
+ rex.reg_match->rm_matchcol = col;
}
}
return retval;
}
-/*
- * Compile a regular expression into internal code for the NFA matcher.
- * Returns the program in allocated space. Returns NULL for an error.
- */
-static regprog_T *nfa_regcomp(char_u *expr, int re_flags)
+// Compile a regular expression into internal code for the NFA matcher.
+// Returns the program in allocated space. Returns NULL for an error.
+static regprog_T *nfa_regcomp(uint8_t *expr, int re_flags)
{
nfa_regprog_T *prog = NULL;
int *postfix;
@@ -7535,11 +7489,9 @@ static regprog_T *nfa_regcomp(char_u *expr, int re_flags)
goto fail; // Cascaded (syntax?) error
}
- /*
- * In order to build the NFA, we parse the input regexp twice:
- * 1. first pass to count size (so we can allocate space)
- * 2. second to emit code
- */
+ // In order to build the NFA, we parse the input regexp twice:
+ // 1. first pass to count size (so we can allocate space)
+ // 2. second to emit code
#ifdef REGEXP_DEBUG
{
FILE *f = fopen(NFA_REGEXP_RUN_LOG, "a");
@@ -7554,10 +7506,8 @@ static regprog_T *nfa_regcomp(char_u *expr, int re_flags)
}
#endif
- /*
- * PASS 1
- * Count number of NFA states in "nstate". Do not build the NFA.
- */
+ // PASS 1
+ // Count number of NFA states in "nstate". Do not build the NFA.
post2nfa(postfix, post_ptr, true);
// allocate the regprog with space for the compiled regexp
@@ -7566,10 +7516,8 @@ static regprog_T *nfa_regcomp(char_u *expr, int re_flags)
state_ptr = prog->state;
prog->re_in_use = false;
- /*
- * PASS 2
- * Build the NFA
- */
+ // PASS 2
+ // Build the NFA
prog->start = post2nfa(postfix, post_ptr, false);
if (prog->start == NULL) {
goto fail;
@@ -7593,7 +7541,7 @@ static regprog_T *nfa_regcomp(char_u *expr, int re_flags)
#endif
// Remember whether this pattern has any \z specials in it.
prog->reghasz = re_has_z;
- prog->pattern = vim_strsave(expr);
+ prog->pattern = xstrdup((char *)expr);
#ifdef REGEXP_DEBUG
nfa_regengine.expr = NULL;
#endif
@@ -7613,16 +7561,16 @@ fail:
goto out;
}
-/*
- * Free a compiled regexp program, returned by nfa_regcomp().
- */
+// Free a compiled regexp program, returned by nfa_regcomp().
static void nfa_regfree(regprog_T *prog)
{
- if (prog != NULL) {
- xfree(((nfa_regprog_T *)prog)->match_text);
- xfree(((nfa_regprog_T *)prog)->pattern);
- xfree(prog);
+ if (prog == NULL) {
+ return;
}
+
+ xfree(((nfa_regprog_T *)prog)->match_text);
+ xfree(((nfa_regprog_T *)prog)->pattern);
+ xfree(prog);
}
/// Match a regexp against a string.
@@ -7634,7 +7582,7 @@ static void nfa_regfree(regprog_T *prog)
/// @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)
+static int nfa_regexec_nl(regmatch_T *rmp, uint8_t *line, colnr_T col, bool line_lbr)
{
rex.reg_match = rmp;
rex.reg_mmatch = NULL;
@@ -7686,16 +7634,6 @@ static int nfa_regexec_nl(regmatch_T *rmp, char_u *line, colnr_T col, bool line_
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;
- 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;
-
+ init_regexec_multi(rmp, win, buf, lnum);
return nfa_regexec_both(NULL, col, tm, timed_out);
}
diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c
index 914b21bb02..24500b80b9 100644
--- a/src/nvim/runtime.c
+++ b/src/nvim/runtime.c
@@ -5,25 +5,46 @@
///
/// Management of runtime files (including packages)
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <uv.h>
+
+#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
#include "nvim/autocmd.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
#include "nvim/debugger.h"
#include "nvim/eval.h"
#include "nvim/eval/userfunc.h"
-#include "nvim/ex_cmds.h"
-#include "nvim/ex_cmds2.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
+#include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
#include "nvim/lua/executor.h"
+#include "nvim/macros.h"
+#include "nvim/map.h"
+#include "nvim/mbyte.h"
#include "nvim/memline.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
#include "nvim/option.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
+#include "nvim/os/stdpaths_defs.h"
+#include "nvim/path.h"
#include "nvim/profile.h"
#include "nvim/runtime.h"
+#include "nvim/strings.h"
+#include "nvim/usercmd.h"
#include "nvim/vim.h"
/// Structure used to store info for each sourced file.
@@ -83,8 +104,7 @@ estack_T *estack_push(etype_T type, char *name, linenr_T lnum)
void estack_push_ufunc(ufunc_T *ufunc, linenr_T lnum)
{
estack_T *entry = estack_push(ETYPE_UFUNC,
- (char *)(ufunc->uf_name_exp != NULL
- ? ufunc->uf_name_exp : ufunc->uf_name),
+ ufunc->uf_name_exp != NULL ? ufunc->uf_name_exp : ufunc->uf_name,
lnum);
if (entry != NULL) {
entry->es_info.ufunc = ufunc;
@@ -100,10 +120,11 @@ void estack_pop(void)
}
/// Get the current value for <sfile> in allocated memory.
-/// @param which ESTACK_SFILE for <sfile> and ESTACK_STACK for <stack>.
+/// @param which ESTACK_SFILE for <sfile>, ESTACK_STACK for <stack> or
+/// ESTACK_SCRIPT for <script>.
char *estack_sfile(estack_arg_T which)
{
- estack_T *entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
+ const estack_T *entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
if (which == ESTACK_SFILE && entry->es_type != ETYPE_UFUNC) {
if (entry->es_name == NULL) {
return NULL;
@@ -111,6 +132,26 @@ char *estack_sfile(estack_arg_T which)
return xstrdup(entry->es_name);
}
+ // If evaluated in a function or autocommand, return the path of the script
+ // where it is defined, at script level the current script path is returned
+ // instead.
+ if (which == ESTACK_SCRIPT) {
+ // Walk the stack backwards, starting from the current frame.
+ for (int idx = exestack.ga_len - 1; idx >= 0; idx--, entry--) {
+ if (entry->es_type == ETYPE_UFUNC || entry->es_type == ETYPE_AUCMD) {
+ const sctx_T *const def_ctx = (entry->es_type == ETYPE_UFUNC
+ ? &entry->es_info.ufunc->uf_script_ctx
+ : &entry->es_info.aucmd->script_ctx);
+ return def_ctx->sc_sid > 0
+ ? xstrdup((SCRIPT_ITEM(def_ctx->sc_sid).sn_name))
+ : NULL;
+ } else if (entry->es_type == ETYPE_SCRIPT) {
+ return xstrdup(entry->es_name);
+ }
+ }
+ return NULL;
+ }
+
// Give information about each stack entry up to the root.
// For a function we compose the call stack, as it was done in the past:
// "function One[123]..Two[456]..Three"
@@ -171,20 +212,20 @@ void runtime_init(void)
void ex_runtime(exarg_T *eap)
{
char *arg = eap->arg;
- char *p = (char *)skiptowhite((char_u *)arg);
- ptrdiff_t len = p - arg;
+ char *p = skiptowhite(arg);
+ size_t len = (size_t)(p - arg);
int flags = eap->forceit ? DIP_ALL : 0;
- if (STRNCMP(arg, "START", len) == 0) {
+ if (strncmp(arg, "START", len) == 0) {
flags += DIP_START + DIP_NORTP;
arg = skipwhite(arg + len);
- } else if (STRNCMP(arg, "OPT", len) == 0) {
+ } else if (strncmp(arg, "OPT", len) == 0) {
flags += DIP_OPT + DIP_NORTP;
arg = skipwhite(arg + len);
- } else if (STRNCMP(arg, "PACK", len) == 0) {
+ } else if (strncmp(arg, "PACK", len) == 0) {
flags += DIP_START + DIP_OPT + DIP_NORTP;
arg = skipwhite(arg + len);
- } else if (STRNCMP(arg, "ALL", len) == 0) {
+ } else if (strncmp(arg, "ALL", len) == 0) {
flags += DIP_START + DIP_OPT;
arg = skipwhite(arg + len);
}
@@ -205,9 +246,9 @@ static void source_callback(char *fname, void *cookie)
/// When "flags" has DIP_ERR: give an error message if there is no match.
///
/// return FAIL when no file could be sourced, OK otherwise.
-int do_in_path(char_u *path, char *name, int flags, DoInRuntimepathCB callback, void *cookie)
+int do_in_path(char *path, char *name, int flags, DoInRuntimepathCB callback, void *cookie)
{
- char_u *tail;
+ char *tail;
int num_files;
char **files;
int i;
@@ -215,21 +256,21 @@ int do_in_path(char_u *path, char *name, int flags, DoInRuntimepathCB callback,
// Make a copy of 'runtimepath'. Invoking the callback may change the
// value.
- char_u *rtp_copy = vim_strsave(path);
+ char *rtp_copy = xstrdup(path);
char *buf = xmallocz(MAXPATHL);
{
if (p_verbose > 10 && name != NULL) {
verbose_enter();
- smsg(_("Searching for \"%s\" in \"%s\""), name, (char *)path);
+ smsg(_("Searching for \"%s\" in \"%s\""), name, path);
verbose_leave();
}
// Loop over all entries in 'runtimepath'.
- char *rtp = (char *)rtp_copy;
+ char *rtp = rtp_copy;
while (*rtp != NUL && ((flags & DIP_ALL) || !did_one)) {
// Copy the path from 'runtimepath' to buf[].
copy_option_part(&rtp, buf, MAXPATHL, ",");
- size_t buflen = STRLEN(buf);
+ size_t buflen = strlen(buf);
// Skip after or non-after directories.
if (flags & (DIP_NOAFTER | DIP_AFTER)) {
@@ -244,16 +285,16 @@ int do_in_path(char_u *path, char *name, int flags, DoInRuntimepathCB callback,
if (name == NULL) {
(*callback)(buf, cookie);
did_one = true;
- } else if (buflen + STRLEN(name) + 2 < MAXPATHL) {
+ } else if (buflen + strlen(name) + 2 < MAXPATHL) {
add_pathsep(buf);
- tail = (char_u *)buf + STRLEN(buf);
+ tail = buf + strlen(buf);
// Loop over all patterns in "name"
char *np = name;
while (*np != NUL && ((flags & DIP_ALL) || !did_one)) {
// Append the pattern from "name" to buf[].
- assert(MAXPATHL >= (tail - (char_u *)buf));
- copy_option_part(&np, (char *)tail, (size_t)(MAXPATHL - (tail - (char_u *)buf)), "\t ");
+ assert(MAXPATHL >= (tail - buf));
+ copy_option_part(&np, tail, (size_t)(MAXPATHL - (tail - buf)), "\t ");
if (p_verbose > 10) {
verbose_enter();
@@ -322,7 +363,7 @@ RuntimeSearchPath copy_runtime_search_path(const RuntimeSearchPath src)
return dst;
}
-void runtime_search_path_unref(RuntimeSearchPath path, int *ref)
+void runtime_search_path_unref(RuntimeSearchPath path, const int *ref)
FUNC_ATTR_NONNULL_ALL
{
if (*ref) {
@@ -342,19 +383,19 @@ void runtime_search_path_unref(RuntimeSearchPath path, int *ref)
/// When "flags" has DIP_ERR: give an error message if there is no match.
///
/// return FAIL when no file could be sourced, OK otherwise.
-int do_in_cached_path(char_u *name, int flags, DoInRuntimepathCB callback, void *cookie)
+int do_in_cached_path(char *name, int flags, DoInRuntimepathCB callback, void *cookie)
{
- char_u *tail;
+ char *tail;
int num_files;
char **files;
int i;
bool did_one = false;
- char_u buf[MAXPATHL];
+ char buf[MAXPATHL];
if (p_verbose > 10 && name != NULL) {
verbose_enter();
- smsg(_("Searching for \"%s\" in runtime path"), (char *)name);
+ smsg(_("Searching for \"%s\" in runtime path"), name);
verbose_leave();
}
@@ -376,17 +417,17 @@ int do_in_cached_path(char_u *name, int flags, DoInRuntimepathCB callback, void
if (name == NULL) {
(*callback)(item.path, cookie);
- } else if (buflen + STRLEN(name) + 2 < MAXPATHL) {
+ } else if (buflen + strlen(name) + 2 < MAXPATHL) {
STRCPY(buf, item.path);
- add_pathsep((char *)buf);
- tail = buf + STRLEN(buf);
+ add_pathsep(buf);
+ tail = buf + strlen(buf);
// Loop over all patterns in "name"
- char *np = (char *)name;
+ char *np = name;
while (*np != NUL && ((flags & DIP_ALL) || !did_one)) {
// Append the pattern from "name" to buf[].
assert(MAXPATHL >= (tail - buf));
- copy_option_part(&np, (char *)tail, (size_t)(MAXPATHL - (tail - buf)), "\t ");
+ copy_option_part(&np, tail, (size_t)(MAXPATHL - (tail - buf)), "\t ");
if (p_verbose > 10) {
verbose_enter();
@@ -398,7 +439,7 @@ int do_in_cached_path(char_u *name, int flags, DoInRuntimepathCB callback, void
| (flags & DIP_DIRFILE) ? (EW_DIR|EW_FILE) : 0;
// Expand wildcards, invoke the callback for each match.
- char *(pat[]) = { (char *)buf };
+ char *(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);
@@ -478,7 +519,7 @@ ArrayOf(String) runtime_get_named_common(bool lua, Array pat, bool all,
if (lua) {
if (item->has_lua == kNone) {
size_t size = (size_t)snprintf(buf, buf_len, "%s/lua/", item->path);
- item->has_lua = (size < buf_len && os_isdir((char_u *)buf));
+ item->has_lua = (size < buf_len && os_isdir(buf));
}
if (item->has_lua == kFalse) {
continue;
@@ -513,18 +554,18 @@ done:
/// If "name" is NULL calls callback for each entry in "path". Cookie is
/// passed by reference in this case, setting it to NULL indicates that callback
/// has done its job.
-int do_in_path_and_pp(char_u *path, char_u *name, int flags, DoInRuntimepathCB callback,
- void *cookie)
+int do_in_path_and_pp(char *path, char *name, int flags, DoInRuntimepathCB callback, void *cookie)
{
int done = FAIL;
if ((flags & DIP_NORTP) == 0) {
- done |= do_in_path(path, (char *)((name && !*name) ? NULL : name), flags, callback, cookie);
+ done |= do_in_path(path, (name && !*name) ? NULL : name, flags, callback,
+ cookie);
}
if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_START)) {
char *start_dir = "pack/*/start/*/%s%s"; // NOLINT
- size_t len = STRLEN(start_dir) + STRLEN(name) + 6;
+ size_t len = strlen(start_dir) + strlen(name) + 6;
char *s = xmallocz(len); // TODO(bfredl): get rid of random allocations
char *suffix = (flags & DIP_AFTER) ? "after/" : "";
@@ -535,7 +576,7 @@ int do_in_path_and_pp(char_u *path, char_u *name, int flags, DoInRuntimepathCB c
if (done == FAIL || (flags & DIP_ALL)) {
start_dir = "start/*/%s%s"; // NOLINT
- len = STRLEN(start_dir) + STRLEN(name) + 6;
+ len = strlen(start_dir) + strlen(name) + 6;
s = xmallocz(len);
vim_snprintf(s, len, start_dir, suffix, name);
@@ -547,7 +588,7 @@ int do_in_path_and_pp(char_u *path, char_u *name, int flags, DoInRuntimepathCB c
if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_OPT)) {
char *opt_dir = "pack/*/opt/*/%s"; // NOLINT
- size_t len = STRLEN(opt_dir) + STRLEN(name);
+ size_t len = strlen(opt_dir) + strlen(name);
char *s = xmallocz(len);
vim_snprintf(s, len, opt_dir, name);
@@ -557,7 +598,7 @@ int do_in_path_and_pp(char_u *path, char_u *name, int flags, DoInRuntimepathCB c
if (done == FAIL || (flags & DIP_ALL)) {
opt_dir = "opt/*/%s"; // NOLINT
- len = STRLEN(opt_dir) + STRLEN(name);
+ len = strlen(opt_dir) + strlen(name);
s = xmallocz(len);
vim_snprintf(s, len, opt_dir, name);
@@ -604,18 +645,18 @@ static void expand_rtp_entry(RuntimeSearchPath *search_path, Map(String, handle_
}
static void expand_pack_entry(RuntimeSearchPath *search_path, Map(String, handle_T) *rtp_used,
- CharVec *after_path, char_u *pack_entry, size_t pack_entry_len)
+ CharVec *after_path, char *pack_entry, size_t pack_entry_len)
{
static char buf[MAXPATHL];
char *(start_pat[]) = { "/pack/*/start/*", "/start/*" }; // NOLINT
for (int i = 0; i < 2; i++) {
- if (pack_entry_len + STRLEN(start_pat[i]) + 1 > sizeof buf) {
+ if (pack_entry_len + strlen(start_pat[i]) + 1 > sizeof buf) {
continue;
}
- STRLCPY(buf, pack_entry, sizeof buf);
- STRLCPY(buf + pack_entry_len, start_pat[i], sizeof buf - pack_entry_len);
+ xstrlcpy(buf, pack_entry, sizeof buf);
+ xstrlcpy(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);
@@ -630,7 +671,7 @@ static bool path_is_after(char *buf, size_t buflen)
// "after" dir in SOME codepaths not not in ALL codepaths.
return buflen >= 5
&& (!(buflen >= 6) || vim_ispathsep(buf[buflen - 6]))
- && STRCMP(buf + buflen - 5, "after") == 0;
+ && strcmp(buf + buflen - 5, "after") == 0;
}
RuntimeSearchPath runtime_search_path_build(void)
@@ -643,32 +684,32 @@ RuntimeSearchPath runtime_search_path_build(void)
RuntimeSearchPath search_path = KV_INITIAL_VALUE;
CharVec after_path = KV_INITIAL_VALUE;
- static char_u buf[MAXPATHL];
- for (char *entry = (char *)p_pp; *entry != NUL;) {
+ static char buf[MAXPATHL];
+ for (char *entry = p_pp; *entry != NUL;) {
char *cur_entry = entry;
- copy_option_part(&entry, (char *)buf, MAXPATHL, ",");
+ copy_option_part(&entry, buf, MAXPATHL, ",");
- String the_entry = { .data = cur_entry, .size = STRLEN(buf) };
+ String the_entry = { .data = cur_entry, .size = strlen(buf) };
kv_push(pack_entries, the_entry);
map_put(String, handle_T)(&pack_used, the_entry, 0);
}
char *rtp_entry;
- for (rtp_entry = (char *)p_rtp; *rtp_entry != NUL;) {
+ for (rtp_entry = p_rtp; *rtp_entry != NUL;) {
char *cur_entry = rtp_entry;
- copy_option_part(&rtp_entry, (char *)buf, MAXPATHL, ",");
- size_t buflen = STRLEN(buf);
+ copy_option_part(&rtp_entry, buf, MAXPATHL, ",");
+ size_t buflen = strlen(buf);
- if (path_is_after((char *)buf, buflen)) {
+ if (path_is_after(buf, buflen)) {
rtp_entry = cur_entry;
break;
}
// fact: &rtp entries can contain wild chars
- expand_rtp_entry(&search_path, &rtp_used, (char *)buf, false);
+ expand_rtp_entry(&search_path, &rtp_used, buf, false);
- handle_T *h = map_ref(String, handle_T)(&pack_used, cstr_as_string((char *)buf), false);
+ handle_T *h = map_ref(String, handle_T)(&pack_used, cstr_as_string(buf), false);
if (h) {
(*h)++;
expand_pack_entry(&search_path, &rtp_used, &after_path, buf, buflen);
@@ -679,7 +720,7 @@ RuntimeSearchPath runtime_search_path_build(void)
String item = kv_A(pack_entries, i);
handle_T h = map_get(String, handle_T)(&pack_used, item);
if (h == 0) {
- expand_pack_entry(&search_path, &rtp_used, &after_path, (char_u *)item.data, item.size);
+ expand_pack_entry(&search_path, &rtp_used, &after_path, item.data, item.size);
}
}
@@ -691,8 +732,8 @@ RuntimeSearchPath runtime_search_path_build(void)
// "after" dirs in rtp
for (; *rtp_entry != NUL;) {
- copy_option_part(&rtp_entry, (char *)buf, MAXPATHL, ",");
- expand_rtp_entry(&search_path, &rtp_used, (char *)buf, path_is_after((char *)buf, STRLEN(buf)));
+ copy_option_part(&rtp_entry, buf, MAXPATHL, ",");
+ expand_rtp_entry(&search_path, &rtp_used, buf, path_is_after(buf, strlen(buf)));
}
// strings are not owned
@@ -746,13 +787,13 @@ int do_in_runtimepath(char *name, int flags, DoInRuntimepathCB callback, void *c
{
int success = FAIL;
if (!(flags & DIP_NORTP)) {
- success |= do_in_cached_path((name && !*name) ? NULL : (char_u *)name, flags, callback, cookie);
+ success |= do_in_cached_path((name && !*name) ? NULL : 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, (char_u *)name, flags, callback, cookie);
+ success |= do_in_path_and_pp(p_rtp, name, flags, callback, cookie);
}
return success;
}
@@ -768,7 +809,7 @@ int source_runtime(char *name, int flags)
}
/// Just like source_runtime(), but use "path" instead of 'runtimepath'.
-int source_in_path(char_u *path, char_u *name, int flags)
+int source_in_path(char *path, char *name, int flags)
{
return do_in_path_and_pp(path, name, flags, source_callback, NULL);
}
@@ -780,29 +821,37 @@ static void source_all_matches(char *pat)
int num_files;
char **files;
- if (gen_expand_wildcards(1, &pat, &num_files, &files, EW_FILE) == OK) {
- for (int i = 0; i < num_files; i++) {
- (void)do_source(files[i], false, DOSO_NONE);
- }
- FreeWild(num_files, files);
+ if (gen_expand_wildcards(1, &pat, &num_files, &files, EW_FILE) != OK) {
+ return;
}
+
+ for (int i = 0; i < num_files; i++) {
+ (void)do_source(files[i], false, DOSO_NONE);
+ }
+ FreeWild(num_files, files);
}
/// Add the package directory to 'runtimepath'
///
/// @param fname the package path
/// @param is_pack whether the added dir is a "pack/*/start/*/" style package
-static int add_pack_dir_to_rtp(char_u *fname, bool is_pack)
+static int add_pack_dir_to_rtp(char *fname, bool is_pack)
{
- char_u *p4, *p3, *p2, *p1, *p;
- char_u *buf = NULL;
+ char *p;
+ char *buf = NULL;
char *afterdir = NULL;
int retval = FAIL;
- p4 = p3 = p2 = p1 = get_past_head(fname);
+ char *p1 = get_past_head(fname);
+ char *p2 = p1;
+ char *p3 = p1;
+ char *p4 = p1;
for (p = p1; *p; MB_PTR_ADV(p)) {
if (vim_ispathsep_nocolon(*p)) {
- p4 = p3; p3 = p2; p2 = p1; p1 = p;
+ p4 = p3;
+ p3 = p2;
+ p2 = p1;
+ p1 = p;
}
}
@@ -812,9 +861,9 @@ static int add_pack_dir_to_rtp(char_u *fname, bool is_pack)
//
// find the part up to "pack" in 'runtimepath'
p4++; // append pathsep in order to expand symlink
- char_u c = *p4;
+ char c = *p4;
*p4 = NUL;
- char *const ffname = fix_fname((char *)fname);
+ char *const ffname = fix_fname(fname);
*p4 = c;
if (ffname == NULL) {
@@ -833,10 +882,10 @@ 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 **)&entry, (char *)buf, MAXPATHL, ",");
+ copy_option_part((char **)&entry, buf, MAXPATHL, ",");
if (insp == NULL) {
- add_pathsep((char *)buf);
- char *const rtp_ffname = fix_fname((char *)buf);
+ add_pathsep(buf);
+ char *const rtp_ffname = fix_fname(buf);
if (rtp_ffname == NULL) {
goto theend;
}
@@ -848,7 +897,7 @@ static int add_pack_dir_to_rtp(char_u *fname, bool is_pack)
}
}
- if ((p = (char_u *)strstr((char *)buf, "after")) != NULL
+ if ((p = strstr(buf, "after")) != NULL
&& p > buf
&& vim_ispathsep(p[-1])
&& (vim_ispathsep(p[5]) || p[5] == NUL || p[5] == ',')) {
@@ -864,18 +913,18 @@ static int add_pack_dir_to_rtp(char_u *fname, bool is_pack)
if (insp == NULL) {
// Both "fname" and "after" not found, append at the end.
- insp = (const char *)p_rtp + STRLEN(p_rtp);
+ insp = (const char *)p_rtp + strlen(p_rtp);
}
// check if rtp/pack/name/start/name/after exists
- afterdir = concat_fnames((char *)fname, "after", true);
+ afterdir = concat_fnames(fname, "after", true);
size_t afterlen = 0;
- if (is_pack ? pack_has_entries((char_u *)afterdir) : os_isdir((char_u *)afterdir)) {
+ if (is_pack ? pack_has_entries(afterdir) : os_isdir(afterdir)) {
afterlen = strlen(afterdir) + 1; // add one for comma
}
- const size_t oldlen = STRLEN(p_rtp);
- const size_t addlen = STRLEN(fname) + 1; // add one for comma
+ const size_t oldlen = strlen(p_rtp);
+ const size_t addlen = strlen(fname) + 1; // add one for comma
const size_t new_rtp_capacity = oldlen + addlen + afterlen + 1;
// add one for NUL ------------------------------------------^
char *const new_rtp = try_malloc(new_rtp_capacity);
@@ -923,7 +972,7 @@ static int add_pack_dir_to_rtp(char_u *fname, bool is_pack)
xstrlcat(new_rtp, afterdir, new_rtp_capacity);
}
- set_option_value("rtp", 0L, new_rtp, 0);
+ set_option_value_give_err("rtp", 0L, new_rtp, 0);
xfree(new_rtp);
retval = OK;
@@ -937,29 +986,29 @@ theend:
/// Load scripts in "plugin" directory of the package.
/// For opt packages, also load scripts in "ftdetect" (start packages already
/// load these from filetype.vim)
-static int load_pack_plugin(bool opt, char_u *fname)
+static int load_pack_plugin(bool opt, char *fname)
{
static const char *ftpat = "%s/ftdetect/*.vim"; // NOLINT
- char *const ffname = fix_fname((char *)fname);
- size_t len = strlen(ffname) + STRLEN(ftpat);
- char_u *pat = xmallocz(len);
+ char *const ffname = fix_fname(fname);
+ size_t len = strlen(ffname) + strlen(ftpat);
+ char *pat = xmallocz(len);
- vim_snprintf((char *)pat, len, "%s/plugin/**/*.vim", ffname); // NOLINT
- source_all_matches((char *)pat);
- vim_snprintf((char *)pat, len, "%s/plugin/**/*.lua", ffname); // NOLINT
- source_all_matches((char *)pat);
+ vim_snprintf(pat, len, "%s/plugin/**/*.vim", ffname); // NOLINT
+ source_all_matches(pat);
+ vim_snprintf(pat, len, "%s/plugin/**/*.lua", ffname); // NOLINT
+ source_all_matches(pat);
- char_u *cmd = vim_strsave((char_u *)"g:did_load_filetypes");
+ char *cmd = xstrdup("g:did_load_filetypes");
// If runtime/filetype.vim wasn't loaded yet, the scripts will be
// found when it loads.
- if (opt && eval_to_number((char *)cmd) > 0) {
+ if (opt && eval_to_number(cmd) > 0) {
do_cmdline_cmd("augroup filetypedetect");
- vim_snprintf((char *)pat, len, ftpat, ffname);
- source_all_matches((char *)pat);
+ vim_snprintf(pat, len, ftpat, ffname);
+ source_all_matches(pat);
vim_snprintf((char *)pat, len, "%s/ftdetect/*.lua", ffname); // NOLINT
- source_all_matches((char *)pat);
+ source_all_matches(pat);
do_cmdline_cmd("augroup END");
}
xfree(cmd);
@@ -974,7 +1023,7 @@ static int APP_ADD_DIR;
static int APP_LOAD;
static int APP_BOTH;
-static void add_pack_plugin(bool opt, char_u *fname, void *cookie)
+static void add_pack_plugin(bool opt, char *fname, void *cookie)
{
if (cookie != &APP_LOAD) {
char *buf = xmalloc(MAXPATHL);
@@ -983,7 +1032,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 **)&p, buf, MAXPATHL, ",");
- if (path_fnamecmp(buf, (char *)fname) == 0) {
+ if (path_fnamecmp(buf, fname) == 0) {
found = true;
break;
}
@@ -1004,12 +1053,12 @@ static void add_pack_plugin(bool opt, char_u *fname, void *cookie)
static void add_start_pack_plugin(char *fname, void *cookie)
{
- add_pack_plugin(false, (char_u *)fname, cookie);
+ add_pack_plugin(false, fname, cookie);
}
static void add_opt_pack_plugin(char *fname, void *cookie)
{
- add_pack_plugin(true, (char_u *)fname, cookie);
+ add_pack_plugin(true, fname, cookie);
}
/// Add all packages in the "start" directory to 'runtimepath'.
@@ -1018,11 +1067,11 @@ void add_pack_start_dirs(void)
do_in_path(p_pp, NULL, DIP_ALL + DIP_DIR, add_pack_start_dir, NULL);
}
-static bool pack_has_entries(char_u *buf)
+static bool pack_has_entries(char *buf)
{
int num_files;
char **files;
- char *(pat[]) = { (char *)buf };
+ char *(pat[]) = { buf };
if (gen_expand_wildcards(1, pat, &num_files, &files, EW_DIR) == OK) {
FreeWild(num_files, files);
}
@@ -1031,14 +1080,14 @@ static bool pack_has_entries(char_u *buf)
static void add_pack_start_dir(char *fname, void *cookie)
{
- static char_u buf[MAXPATHL];
+ static char buf[MAXPATHL];
char *(start_pat[]) = { "/start/*", "/pack/*/start/*" }; // NOLINT
for (int i = 0; i < 2; i++) {
- if (STRLEN(fname) + STRLEN(start_pat[i]) + 1 > MAXPATHL) {
+ if (strlen(fname) + strlen(start_pat[i]) + 1 > MAXPATHL) {
continue;
}
- STRLCPY(buf, fname, MAXPATHL);
- STRLCAT(buf, start_pat[i], sizeof buf);
+ xstrlcpy(buf, fname, MAXPATHL);
+ xstrlcat(buf, start_pat[i], sizeof buf);
if (pack_has_entries(buf)) {
add_pack_dir_to_rtp(buf, true);
}
@@ -1072,12 +1121,12 @@ void ex_packloadall(exarg_T *eap)
void load_plugins(void)
{
if (p_lpl) {
- char_u *rtp_copy = p_rtp;
- char_u *const plugin_pattern_vim = (char_u *)"plugin/**/*.vim"; // NOLINT
- char_u *const plugin_pattern_lua = (char_u *)"plugin/**/*.lua"; // NOLINT
+ char *rtp_copy = p_rtp;
+ char *const plugin_pattern_vim = "plugin/**/*.vim"; // NOLINT
+ char *const plugin_pattern_lua = "plugin/**/*.lua"; // NOLINT
if (!did_source_packages) {
- rtp_copy = vim_strsave(p_rtp);
+ rtp_copy = xstrdup(p_rtp);
add_pack_start_dirs();
}
@@ -1094,8 +1143,8 @@ void load_plugins(void)
}
TIME_MSG("loading packages");
- source_runtime((char *)plugin_pattern_vim, DIP_ALL | DIP_AFTER);
- source_runtime((char *)plugin_pattern_lua, DIP_ALL | DIP_AFTER);
+ source_runtime(plugin_pattern_vim, DIP_ALL | DIP_AFTER);
+ source_runtime(plugin_pattern_lua, DIP_ALL | DIP_AFTER);
TIME_MSG("loading after plugins");
}
}
@@ -1113,108 +1162,87 @@ void ex_packadd(exarg_T *eap)
continue;
}
- const size_t len = STRLEN(plugpat) + STRLEN(eap->arg) + 5;
+ const size_t len = strlen(plugpat) + strlen(eap->arg) + 5;
char *pat = xmallocz(len);
vim_snprintf(pat, len, plugpat, round == 1 ? "start" : "opt", eap->arg);
// The first round don't give a "not found" error, in the second round
// only when nothing was found in the first round.
- res = do_in_path(p_pp, pat, DIP_ALL + DIP_DIR + (round == 2 && res == FAIL ? DIP_ERR : 0),
- round == 1 ? add_start_pack_plugin : add_opt_pack_plugin,
- eap->forceit ? &APP_ADD_DIR : &APP_BOTH);
+ res =
+ do_in_path(p_pp, pat, DIP_ALL + DIP_DIR + (round == 2 && res == FAIL ? DIP_ERR : 0),
+ round == 1 ? add_start_pack_plugin : add_opt_pack_plugin,
+ eap->forceit ? &APP_ADD_DIR : &APP_BOTH);
xfree(pat);
}
}
/// Expand color scheme, compiler or filetype names.
/// Search from 'runtimepath':
-/// 'runtimepath'/{dirnames}/{pat}.vim
-/// When "flags" has DIP_START: search also from 'start' of 'packpath':
-/// 'packpath'/pack/ * /start/ * /{dirnames}/{pat}.vim
-/// When "flags" has DIP_OPT: search also from 'opt' of 'packpath':
-/// 'packpath'/pack/ * /opt/ * /{dirnames}/{pat}.vim
-/// When "flags" has DIP_LUA: search also performed for .lua files
+/// 'runtimepath'/{dirnames}/{pat}.(vim|lua)
+/// When "flags" has DIP_START: search also from "start" of 'packpath':
+/// 'packpath'/pack/*/start/*/{dirnames}/{pat}.(vim|lua)
+/// When "flags" has DIP_OPT: search also from "opt" of 'packpath':
+/// 'packpath'/pack/*/opt/*/{dirnames}/{pat}.(vim|lua)
/// "dirnames" is an array with one or more directory names.
-int ExpandRTDir(char_u *pat, int flags, int *num_file, char ***file, char *dirnames[])
+int ExpandRTDir(char *pat, int flags, int *num_file, char ***file, char *dirnames[])
{
*num_file = 0;
*file = NULL;
- size_t pat_len = STRLEN(pat);
+ size_t pat_len = strlen(pat);
garray_T ga;
ga_init(&ga, (int)sizeof(char *), 10);
- // TODO(bfredl): this is bullshit, exandpath should not reinvent path logic.
+ // TODO(bfredl): this is bullshit, expandpath should not reinvent path logic.
for (int i = 0; dirnames[i] != NULL; i++) {
- size_t size = STRLEN(dirnames[i]) + pat_len + 7;
- char_u *s = xmalloc(size);
- snprintf((char *)s, size, "%s/%s*.vim", dirnames[i], pat);
- globpath((char *)p_rtp, s, &ga, 0);
- if (flags & DIP_LUA) {
- snprintf((char *)s, size, "%s/%s*.lua", dirnames[i], pat);
- globpath((char *)p_rtp, s, &ga, 0);
- }
+ size_t size = strlen(dirnames[i]) + pat_len + 16;
+ char *s = xmalloc(size);
+ snprintf(s, size, "%s/%s*.\\(vim\\|lua\\)", dirnames[i], pat);
+ globpath(p_rtp, s, &ga, 0);
xfree(s);
}
if (flags & DIP_START) {
for (int i = 0; dirnames[i] != NULL; i++) {
- size_t size = STRLEN(dirnames[i]) + pat_len + 22;
- char_u *s = xmalloc(size);
- snprintf((char *)s, size, "pack/*/start/*/%s/%s*.vim", dirnames[i], pat); // NOLINT
- globpath((char *)p_pp, s, &ga, 0);
- if (flags & DIP_LUA) {
- snprintf((char *)s, size, "pack/*/start/*/%s/%s*.lua", dirnames[i], pat); // NOLINT
- globpath((char *)p_pp, s, &ga, 0);
- }
+ size_t size = strlen(dirnames[i]) + pat_len + 31;
+ char *s = xmalloc(size);
+ snprintf(s, size, "pack/*/start/*/%s/%s*.\\(vim\\|lua\\)", dirnames[i], pat); // NOLINT
+ globpath(p_pp, s, &ga, 0);
xfree(s);
}
for (int i = 0; dirnames[i] != NULL; i++) {
- size_t size = STRLEN(dirnames[i]) + pat_len + 22;
- char_u *s = xmalloc(size);
- snprintf((char *)s, size, "start/*/%s/%s*.vim", dirnames[i], pat); // NOLINT
- globpath((char *)p_pp, s, &ga, 0);
- if (flags & DIP_LUA) {
- snprintf((char *)s, size, "start/*/%s/%s*.lua", dirnames[i], pat); // NOLINT
- globpath((char *)p_pp, s, &ga, 0);
- }
+ size_t size = strlen(dirnames[i]) + pat_len + 31;
+ char *s = xmalloc(size);
+ snprintf(s, size, "start/*/%s/%s*.\\(vim\\|lua\\)", dirnames[i], pat); // NOLINT
+ globpath(p_pp, s, &ga, 0);
xfree(s);
}
}
if (flags & DIP_OPT) {
for (int i = 0; dirnames[i] != NULL; i++) {
- size_t size = STRLEN(dirnames[i]) + pat_len + 20;
- char_u *s = xmalloc(size);
- snprintf((char *)s, size, "pack/*/opt/*/%s/%s*.vim", dirnames[i], pat); // NOLINT
- globpath((char *)p_pp, s, &ga, 0);
- if (flags & DIP_LUA) {
- snprintf((char *)s, size, "pack/*/opt/*/%s/%s*.lua", dirnames[i], pat); // NOLINT
- globpath((char *)p_pp, s, &ga, 0);
- }
+ size_t size = strlen(dirnames[i]) + pat_len + 29;
+ char *s = xmalloc(size);
+ snprintf(s, size, "pack/*/opt/*/%s/%s*.\\(vim\\|lua\\)", dirnames[i], pat); // NOLINT
+ globpath(p_pp, s, &ga, 0);
xfree(s);
}
for (int i = 0; dirnames[i] != NULL; i++) {
- size_t size = STRLEN(dirnames[i]) + pat_len + 20;
- char_u *s = xmalloc(size);
- snprintf((char *)s, size, "opt/*/%s/%s*.vim", dirnames[i], pat); // NOLINT
- globpath((char *)p_pp, s, &ga, 0);
- if (flags & DIP_LUA) {
- snprintf((char *)s, size, "opt/*/%s/%s*.lua", dirnames[i], pat); // NOLINT
- globpath((char *)p_pp, s, &ga, 0);
- }
+ size_t size = strlen(dirnames[i]) + pat_len + 29;
+ char *s = xmalloc(size);
+ snprintf(s, size, "opt/*/%s/%s*.\\(vim\\|lua\\)", dirnames[i], pat); // NOLINT
+ globpath(p_pp, s, &ga, 0);
xfree(s);
}
}
for (int i = 0; i < ga.ga_len; i++) {
- char_u *match = ((char_u **)ga.ga_data)[i];
- char_u *s = match;
- char_u *e = s + STRLEN(s);
+ char *match = ((char **)ga.ga_data)[i];
+ char *s = match;
+ char *e = s + strlen(s);
if (e - s > 4 && (STRNICMP(e - 4, ".vim", 4) == 0
- || ((flags & DIP_LUA)
- && STRNICMP(e - 4, ".lua", 4) == 0))) {
+ || STRNICMP(e - 4, ".lua", 4) == 0)) {
e -= 4;
for (s = e; s > match; MB_PTR_BACK(match, s)) {
if (vim_ispathsep(*s)) {
@@ -1243,27 +1271,27 @@ int ExpandRTDir(char_u *pat, int flags, int *num_file, char ***file, char *dirna
/// Expand loadplugin names:
/// 'packpath'/pack/ * /opt/{pat}
-int ExpandPackAddDir(char_u *pat, int *num_file, char ***file)
+int ExpandPackAddDir(char *pat, int *num_file, char ***file)
{
garray_T ga;
*num_file = 0;
*file = NULL;
- size_t pat_len = STRLEN(pat);
+ size_t pat_len = strlen(pat);
ga_init(&ga, (int)sizeof(char *), 10);
size_t buflen = pat_len + 26;
- char_u *s = xmalloc(buflen);
- snprintf((char *)s, buflen, "pack/*/opt/%s*", pat); // NOLINT
- globpath((char *)p_pp, s, &ga, 0);
- snprintf((char *)s, buflen, "opt/%s*", pat); // NOLINT
- globpath((char *)p_pp, s, &ga, 0);
+ char *s = xmalloc(buflen);
+ snprintf(s, buflen, "pack/*/opt/%s*", pat); // NOLINT
+ globpath(p_pp, s, &ga, 0);
+ snprintf(s, buflen, "opt/%s*", pat); // NOLINT
+ globpath(p_pp, s, &ga, 0);
xfree(s);
for (int i = 0; i < ga.ga_len; i++) {
- char_u *match = ((char_u **)ga.ga_data)[i];
- s = (char_u *)path_tail((char *)match);
- memmove(match, s, STRLEN(s) + 1);
+ char *match = ((char **)ga.ga_data)[i];
+ s = path_tail(match);
+ memmove(match, s, strlen(s) + 1);
}
if (GA_EMPTY(&ga)) {
@@ -1423,7 +1451,7 @@ static inline char *add_dir(char *dest, const char *const dir, const size_t dir_
if (!after_pathsep(dest - 1, dest)) {
*dest++ = PATHSEP;
}
-#if defined(WIN32)
+#if defined(MSWIN)
size_t size = (type == kXDGDataHome ? sizeof("nvim-data") - 1 : NVIM_SIZE);
memmove(dest, (type == kXDGDataHome ? "nvim-data" : "nvim"), size);
dest += size;
@@ -1451,7 +1479,7 @@ char *get_lib_dir(void)
// TODO(bfredl): too fragile? Ideally default_lib_dir would be made empty
// in an appimage build
if (strlen(default_lib_dir) != 0
- && os_isdir((const char_u *)default_lib_dir)) {
+ && os_isdir(default_lib_dir)) {
return xstrdup(default_lib_dir);
}
@@ -1493,7 +1521,7 @@ char *runtimepath_default(bool clean_arg)
if (data_home != NULL) {
data_len = strlen(data_home);
if (data_len != 0) {
-#if defined(WIN32)
+#if defined(MSWIN)
size_t nvim_size = (sizeof("nvim-data") - 1);
#else
size_t nvim_size = NVIM_SIZE;
@@ -1583,7 +1611,7 @@ static void cmd_source(char *fname, exarg_T *eap)
// - after ":argdo", ":windo" or ":bufdo"
// - another command follows
// - inside a loop
- openscript((char_u *)fname, global_busy || listcmd_busy || eap->nextcmd != NULL
+ openscript(fname, global_busy || listcmd_busy || eap->nextcmd != NULL
|| eap->cstack->cs_idx >= 0);
// ":source" read ex commands
@@ -1605,7 +1633,7 @@ void ex_options(exarg_T *eap)
bool multi_mods = 0;
buf[0] = NUL;
- (void)add_win_cmd_modifers(buf, &cmdmod, &multi_mods);
+ (void)add_win_cmd_modifiers(buf, &cmdmod, &multi_mods);
os_setenv("OPTWIN_CMD", buf, 1);
cmd_source(SYS_OPTWIN_FILE, NULL);
@@ -1636,7 +1664,7 @@ int source_level(void *cookie)
/// If possible the handle is closed on exec().
static FILE *fopen_noinh_readbin(char *filename)
{
-#ifdef WIN32
+#ifdef MSWIN
int fd_tmp = os_open(filename, O_RDONLY | O_BINARY | O_NOINHERIT, 0);
#else
int fd_tmp = os_open(filename, O_RDONLY, 0);
@@ -1663,14 +1691,14 @@ static FILE *fopen_noinh_readbin(char *filename)
///
/// @return true if this line did begin with a continuation (the next line
/// should also be considered, if it exists); false otherwise
-static bool concat_continued_line(garray_T *const ga, const int init_growsize,
- const char_u *const p, size_t len)
+static bool concat_continued_line(garray_T *const ga, const int init_growsize, const char *const p,
+ size_t len)
FUNC_ATTR_NONNULL_ALL
{
- const char *const line = (char *)skipwhite_len(p, len);
- len -= (size_t)((char_u *)line - p);
+ const char *const line = skipwhite_len((char *)p, len);
+ len -= (size_t)(line - p);
// Skip lines starting with '\" ', concat lines starting with '\'
- if (len >= 3 && STRNCMP(line, "\"\\ ", 3) == 0) {
+ if (len >= 3 && strncmp(line, "\"\\ ", 3) == 0) {
return true;
} else if (len == 0 || line[0] != '\\') {
return false;
@@ -1700,19 +1728,19 @@ typedef struct {
static char *get_str_line(int c, void *cookie, int indent, bool do_concat)
{
GetStrLineCookie *p = cookie;
- if (STRLEN(p->buf) <= p->offset) {
+ if (strlen(p->buf) <= p->offset) {
return NULL;
}
const char *line = p->buf + p->offset;
- const char *eol = (char *)skip_to_newline((char_u *)line);
+ const char *eol = skip_to_newline(line);
garray_T ga;
- ga_init(&ga, sizeof(char_u), 400);
+ ga_init(&ga, sizeof(char), 400);
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((char_u *)line);
- if (!concat_continued_line(&ga, 400, (char_u *)line, (size_t)(next_eol - (char_u *)line))) {
+ const char *const next_eol = skip_to_newline(line);
+ if (!concat_continued_line(&ga, 400, line, (size_t)(next_eol - line))) {
break;
}
eol = (char *)next_eol;
@@ -1742,7 +1770,7 @@ scriptitem_T *new_script_item(char *const name, scid_T *const sid_out)
SCRIPT_ITEM(script_items.ga_len).sn_name = NULL;
SCRIPT_ITEM(script_items.ga_len).sn_prof_on = false;
}
- SCRIPT_ITEM(sid).sn_name = (char_u *)name;
+ SCRIPT_ITEM(sid).sn_name = name;
new_script_vars(sid); // Allocate the local script variables to use for this script.
return &SCRIPT_ITEM(sid);
}
@@ -1786,7 +1814,7 @@ static void cmd_source_buffer(const exarg_T *const eap)
return;
}
garray_T ga;
- ga_init(&ga, sizeof(char_u), 400);
+ ga_init(&ga, sizeof(char), 400);
const linenr_T final_lnum = eap->line2;
// Copy the contents to be executed.
for (linenr_T curr_lnum = eap->line1; curr_lnum <= final_lnum; curr_lnum++) {
@@ -1794,10 +1822,10 @@ static void cmd_source_buffer(const exarg_T *const eap)
if (ga.ga_len > 400) {
ga_set_growsize(&ga, MIN(ga.ga_len, 8000));
}
- ga_concat(&ga, (char *)ml_get(curr_lnum));
+ ga_concat(&ga, ml_get(curr_lnum));
ga_append(&ga, NL);
}
- ((char_u *)ga.ga_data)[ga.ga_len - 1] = NUL;
+ ((char *)ga.ga_data)[ga.ga_len - 1] = NUL;
const GetStrLineCookie cookie = {
.buf = ga.ga_data,
.offset = 0,
@@ -1858,7 +1886,7 @@ int do_source(char *fname, int check_other, int is_vimrc)
if (fname_exp == NULL) {
return retval;
}
- if (os_isdir((char_u *)fname_exp)) {
+ if (os_isdir(fname_exp)) {
smsg(_("Cannot source a directory: \"%s\""), fname);
goto theend;
}
@@ -1880,7 +1908,7 @@ int do_source(char *fname, int check_other, int is_vimrc)
cookie.fp = fopen_noinh_readbin(fname_exp);
if (cookie.fp == NULL && check_other) {
- // Try again, replacing file name ".vimrc" by "_vimrc" or vice versa,
+ // Try again, replacing file name ".nvimrc" by "_nvimrc" or vice versa,
// and ".exrc" by "_exrc" or vice versa.
p = path_tail(fname_exp);
if ((*p == '.' || *p == '_')
@@ -1935,7 +1963,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, (char_u *)fname_exp, (linenr_T)0);
+ cookie.breakpoint = dbg_find_breakpoint(true, fname_exp, (linenr_T)0);
cookie.fname = fname_exp;
cookie.dbg_tick = debug_tick;
@@ -1964,7 +1992,7 @@ int do_source(char *fname, int check_other, int is_vimrc)
si = get_current_script_id(&fname_exp, &current_sctx);
// Keep the sourcing name/lnum, for recursive calls.
- estack_push(ETYPE_SCRIPT, (char *)si->sn_name, 0);
+ estack_push(ETYPE_SCRIPT, si->sn_name, 0);
if (l_do_profiling == PROF_YES) {
bool forceit = false;
@@ -1993,11 +2021,11 @@ int do_source(char *fname, int check_other, int is_vimrc)
} else {
// Read the first line so we can check for a UTF-8 BOM.
firstline = (uint8_t *)getsourceline(0, (void *)&cookie, 0, true);
- if (firstline != NULL && STRLEN(firstline) >= 3 && firstline[0] == 0xef
+ if (firstline != NULL && strlen((char *)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 = (char *)string_convert(&cookie.conv, (char_u *)firstline + 3, NULL);
+ convert_setup(&cookie.conv, "utf-8", p_enc);
+ p = string_convert(&cookie.conv, (char *)firstline + 3, NULL);
if (p == NULL) {
p = xstrdup((char *)firstline + 3);
}
@@ -2036,8 +2064,8 @@ int do_source(char *fname, int check_other, int is_vimrc)
}
if (l_time_fd != NULL) {
- vim_snprintf((char *)IObuff, IOSIZE, "sourcing %s", fname);
- time_msg((char *)IObuff, &start_time);
+ vim_snprintf(IObuff, IOSIZE, "sourcing %s", fname);
+ time_msg(IObuff, &start_time);
time_pop(rel_time);
}
@@ -2093,14 +2121,14 @@ scriptitem_T *get_current_script_id(char **fnamep, sctx_T *ret_sctx)
// - 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, *fnamep) == 0) {
+ if (si->sn_name != NULL && path_fnamecmp(si->sn_name, *fnamep) == 0) {
// Found it!
break;
}
}
if (script_sctx.sc_sid == 0) {
si = new_script_item(*fnamep, &script_sctx.sc_sid);
- *fnamep = xstrdup((char *)si->sn_name);
+ *fnamep = xstrdup(si->sn_name);
}
if (ret_sctx != NULL) {
*ret_sctx = script_sctx;
@@ -2112,12 +2140,17 @@ scriptitem_T *get_current_script_id(char **fnamep, sctx_T *ret_sctx)
/// ":scriptnames"
void ex_scriptnames(exarg_T *eap)
{
- if (eap->addr_count > 0) {
+ if (eap->addr_count > 0 || *eap->arg != NUL) {
// :script {scriptId}: edit the script
- if (eap->line2 < 1 || eap->line2 > script_items.ga_len) {
+ if (eap->addr_count > 0 && !SCRIPT_ID_VALID(eap->line2)) {
emsg(_(e_invarg));
} else {
- eap->arg = (char *)SCRIPT_ITEM(eap->line2).sn_name;
+ if (eap->addr_count > 0) {
+ eap->arg = SCRIPT_ITEM(eap->line2).sn_name;
+ } else {
+ expand_env(eap->arg, NameBuff, MAXPATHL);
+ eap->arg = NameBuff;
+ }
do_exedit(eap, NULL);
}
return;
@@ -2125,11 +2158,11 @@ void ex_scriptnames(exarg_T *eap)
for (int i = 1; i <= script_items.ga_len && !got_int; i++) {
if (SCRIPT_ITEM(i).sn_name != NULL) {
- home_replace(NULL, (char *)SCRIPT_ITEM(i).sn_name, (char *)NameBuff, MAXPATHL, true);
- vim_snprintf((char *)IObuff, IOSIZE, "%3d: %s", i, NameBuff);
+ home_replace(NULL, SCRIPT_ITEM(i).sn_name, NameBuff, MAXPATHL, true);
+ vim_snprintf(IObuff, IOSIZE, "%3d: %s", i, NameBuff);
if (!message_filtered(IObuff)) {
msg_putchar('\n');
- msg_outtrans((char *)IObuff);
+ msg_outtrans(IObuff);
line_breakcheck();
}
}
@@ -2151,40 +2184,40 @@ void scriptnames_slash_adjust(void)
/// Get a pointer to a script name. Used for ":verbose set".
/// Message appended to "Last set from "
-char_u *get_scriptname(LastSet last_set, bool *should_free)
+char *get_scriptname(LastSet last_set, bool *should_free)
{
*should_free = false;
switch (last_set.script_ctx.sc_sid) {
case SID_MODELINE:
- return (char_u *)_("modeline");
+ return _("modeline");
case SID_CMDARG:
- return (char_u *)_("--cmd argument");
+ return _("--cmd argument");
case SID_CARG:
- return (char_u *)_("-c argument");
+ return _("-c argument");
case SID_ENV:
- return (char_u *)_("environment variable");
+ return _("environment variable");
case SID_ERROR:
- return (char_u *)_("error handler");
+ return _("error handler");
case SID_WINLAYOUT:
- return (char_u *)_("changed window size");
+ return _("changed window size");
case SID_LUA:
- return (char_u *)_("Lua");
+ return _("Lua");
case SID_API_CLIENT:
- snprintf((char *)IObuff, IOSIZE, _("API client (channel id %" PRIu64 ")"), last_set.channel_id);
+ snprintf(IObuff, IOSIZE, _("API client (channel id %" PRIu64 ")"), last_set.channel_id);
return IObuff;
case SID_STR:
- return (char_u *)_("anonymous :source");
+ return _("anonymous :source");
default: {
- char *const sname = (char *)SCRIPT_ITEM(last_set.script_ctx.sc_sid).sn_name;
+ char *const sname = SCRIPT_ITEM(last_set.script_ctx.sc_sid).sn_name;
if (sname == NULL) {
- snprintf((char *)IObuff, IOSIZE, _("anonymous :source (script id %d)"),
+ snprintf(IObuff, IOSIZE, _("anonymous :source (script id %d)"),
last_set.script_ctx.sc_sid);
return IObuff;
}
*should_free = true;
- return (char_u *)home_replace_save(NULL, sname);
+ return home_replace_save(NULL, sname);
}
}
}
@@ -2220,7 +2253,7 @@ char *getsourceline(int c, void *cookie, int indent, bool do_concat)
// If breakpoints have been added/deleted need to check for it.
if (sp->dbg_tick < debug_tick) {
- sp->breakpoint = dbg_find_breakpoint(true, (char_u *)sp->fname, SOURCING_LNUM);
+ sp->breakpoint = dbg_find_breakpoint(true, sp->fname, SOURCING_LNUM);
sp->dbg_tick = debug_tick;
}
if (do_profiling == PROF_YES) {
@@ -2259,11 +2292,10 @@ char *getsourceline(int c, void *cookie, int indent, bool do_concat)
|| (p[0] == '"' && p[1] == '\\' && p[2] == ' '))) {
garray_T ga;
- ga_init(&ga, (int)sizeof(char_u), 400);
+ ga_init(&ga, (int)sizeof(char), 400);
ga_concat(&ga, line);
while (sp->nextline != NULL
- && concat_continued_line(&ga, 400, (char_u *)sp->nextline,
- STRLEN(sp->nextline))) {
+ && concat_continued_line(&ga, 400, sp->nextline, strlen(sp->nextline))) {
xfree(sp->nextline);
sp->nextline = get_one_sourceline(sp);
}
@@ -2277,7 +2309,7 @@ char *getsourceline(int c, void *cookie, int indent, bool do_concat)
char *s;
// Convert the encoding of the script line.
- s = (char *)string_convert(&sp->conv, (char_u *)line, NULL);
+ s = string_convert(&sp->conv, line, NULL);
if (s != NULL) {
xfree(line);
line = s;
@@ -2286,9 +2318,9 @@ char *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((char_u *)sp->fname, SOURCING_LNUM);
+ dbg_breakpoint(sp->fname, SOURCING_LNUM);
// Find next breakpoint.
- sp->breakpoint = dbg_find_breakpoint(true, (char_u *)sp->fname, SOURCING_LNUM);
+ sp->breakpoint = dbg_find_breakpoint(true, sp->fname, SOURCING_LNUM);
sp->dbg_tick = debug_tick;
}
@@ -2326,7 +2358,7 @@ retry:
break;
}
- len = ga.ga_len + (int)STRLEN(buf + ga.ga_len);
+ len = ga.ga_len + (int)strlen(buf + ga.ga_len);
#ifdef USE_CRNL
// Ignore a trailing CTRL-Z, when in Dos mode. Only recognize the
// CTRL-Z by its own, or after a NL.
@@ -2410,14 +2442,14 @@ void ex_scriptencoding(exarg_T *eap)
}
if (*eap->arg != NUL) {
- name = (char *)enc_canonize((char_u *)eap->arg);
+ name = enc_canonize(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, (char_u *)name, p_enc);
+ convert_setup(&sp->conv, name, p_enc);
if (name != eap->arg) {
xfree(name);
diff --git a/src/nvim/runtime.h b/src/nvim/runtime.h
index a255c6c096..97063b900c 100644
--- a/src/nvim/runtime.h
+++ b/src/nvim/runtime.h
@@ -3,10 +3,15 @@
#include <stdbool.h>
+#include "klib/kvec.h"
#include "nvim/autocmd.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_eval_defs.h"
+#include "nvim/garray.h"
+#include "nvim/pos.h"
+#include "nvim/types.h"
typedef enum {
ETYPE_TOP, ///< toplevel
@@ -47,10 +52,11 @@ typedef enum {
ESTACK_NONE,
ESTACK_SFILE,
ESTACK_STACK,
+ ESTACK_SCRIPT,
} estack_arg_T;
typedef struct scriptitem_S {
- char_u *sn_name;
+ char *sn_name;
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
@@ -73,6 +79,7 @@ typedef struct scriptitem_S {
/// Growarray to store info about already sourced scripts.
extern garray_T script_items;
#define SCRIPT_ITEM(id) (((scriptitem_T *)script_items.ga_data)[(id) - 1])
+#define SCRIPT_ID_VALID(id) ((id) > 0 && (id) <= script_items.ga_len)
typedef void (*DoInRuntimepathCB)(char *, void *);
@@ -98,7 +105,6 @@ typedef kvec_t(char *) CharVec;
#define DIP_NORTP 0x20 // do not use 'runtimepath'
#define DIP_NOAFTER 0x40 // skip "after" directories
#define DIP_AFTER 0x80 // only use "after" directories
-#define DIP_LUA 0x100 // also use ".lua" files
#define DIP_DIRFILE 0x200 // find both files and directories
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index b471b93192..ebff52cd69 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -10,35 +10,44 @@
#include <assert.h>
#include <inttypes.h>
+#include <limits.h>
#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
+#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
-#include "nvim/cmdexpand.h"
#include "nvim/cursor.h"
-#include "nvim/drawscreen.h"
#include "nvim/eval.h"
-#include "nvim/extmark.h"
-#include "nvim/fileio.h"
#include "nvim/fold.h"
-#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
#include "nvim/grid.h"
+#include "nvim/grid_defs.h"
#include "nvim/highlight.h"
-#include "nvim/highlight_group.h"
-#include "nvim/menu.h"
+#include "nvim/mbyte.h"
+#include "nvim/memline_defs.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
#include "nvim/move.h"
+#include "nvim/normal.h"
#include "nvim/option.h"
-#include "nvim/optionstr.h"
+#include "nvim/os/os.h"
+#include "nvim/os/time.h"
+#include "nvim/pos.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/state.h"
#include "nvim/statusline.h"
-#include "nvim/ui_compositor.h"
-#include "nvim/undo.h"
+#include "nvim/strings.h"
+#include "nvim/types.h"
+#include "nvim/ui.h"
+#include "nvim/vim.h"
#include "nvim/window.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -69,7 +78,7 @@ bool conceal_cursor_line(const win_T *wp)
} else {
return false;
}
- return vim_strchr((char *)wp->w_p_cocu, c) != NULL;
+ return vim_strchr(wp->w_p_cocu, c) != NULL;
}
/// Whether cursorline is drawn in a special way
@@ -155,8 +164,6 @@ void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row, int endr
} else {
grid_fill(&wp->w_grid, row, endrow, n, wp->w_grid.cols, c1, c2, attr);
}
-
- set_empty_rows(wp, row);
}
/// Compute the width of the foldcolumn. Based on 'foldcolumn' and how much
@@ -182,7 +189,7 @@ int compute_foldcolumn(win_T *wp, int col)
///
/// Assume monocell characters
/// @return number of chars added to \param p
-size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_T lnum)
+size_t fill_foldcolumn(char *p, win_T *wp, foldinfo_T foldinfo, linenr_T lnum)
{
int i = 0;
int level;
@@ -216,7 +223,7 @@ size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_T lnum)
symbol = '>';
}
- len = utf_char2bytes(symbol, (char *)&p[char_counter]);
+ len = utf_char2bytes(symbol, &p[char_counter]);
char_counter += (size_t)len;
if (first_level + i >= level) {
i++;
@@ -230,7 +237,7 @@ size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_T lnum)
char_counter -= (size_t)len;
memset(&p[char_counter], ' ', (size_t)len);
}
- len = utf_char2bytes(wp->w_p_fcs_chars.foldclosed, (char *)&p[char_counter]);
+ len = utf_char2bytes(wp->w_p_fcs_chars.foldclosed, &p[char_counter]);
char_counter += (size_t)len;
}
@@ -239,250 +246,18 @@ size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_T lnum)
/// Mirror text "str" for right-left displaying.
/// Only works for single-byte characters (e.g., numbers).
-void rl_mirror(char_u *str)
+void rl_mirror(char *str)
{
- char_u *p1, *p2;
- char_u t;
+ char *p1, *p2;
+ char t;
- for (p1 = str, p2 = str + STRLEN(str) - 1; p1 < p2; p1++, p2--) {
+ for (p1 = str, p2 = str + strlen(str) - 1; p1 < p2; p1++, p2--) {
t = *p1;
*p1 = *p2;
*p2 = t;
}
}
-/// Get the length of an item as it will be shown in the status line.
-static int wildmenu_match_len(expand_T *xp, char_u *s)
-{
- int len = 0;
-
- int emenu = (xp->xp_context == EXPAND_MENUS
- || xp->xp_context == EXPAND_MENUNAMES);
-
- // Check for menu separators - replace with '|'.
- if (emenu && menu_is_separator((char *)s)) {
- return 1;
- }
-
- while (*s != NUL) {
- s += skip_wildmenu_char(xp, s);
- len += ptr2cells((char *)s);
- MB_PTR_ADV(s);
- }
-
- return len;
-}
-
-/// Return the number of characters that should be skipped in the wildmenu
-/// These are backslashes used for escaping. Do show backslashes in help tags.
-static int skip_wildmenu_char(expand_T *xp, char_u *s)
-{
- if ((rem_backslash(s) && xp->xp_context != EXPAND_HELP)
- || ((xp->xp_context == EXPAND_MENUS
- || xp->xp_context == EXPAND_MENUNAMES)
- && (s[0] == '\t'
- || (s[0] == '\\' && s[1] != NUL)))) {
-#ifndef BACKSLASH_IN_FILENAME
- // TODO(bfredl): Why in the actual fuck are we special casing the
- // shell variety deep in the redraw logic? Shell special snowflakiness
- // should already be eliminated multiple layers before reaching the
- // screen infracstructure.
- if (xp->xp_shell && csh_like_shell() && s[1] == '\\' && s[2] == '!') {
- return 2;
- }
-#endif
- return 1;
- }
- return 0;
-}
-
-/// Show wildchar matches in the status line.
-/// Show at least the "match" item.
-/// We start at item 'first_match' in the list and show all matches that fit.
-///
-/// If inversion is possible we use it. Else '=' characters are used.
-///
-/// @param matches list of matches
-void redraw_wildmenu(expand_T *xp, int num_matches, char **matches, int match, int showtail)
-{
-#define L_MATCH(m) (showtail ? sm_gettail(matches[m], false) : matches[m])
- int row;
- char_u *buf;
- int len;
- int clen; // length in screen cells
- int fillchar;
- int attr;
- int i;
- bool highlight = true;
- char_u *selstart = NULL;
- int selstart_col = 0;
- char_u *selend = NULL;
- static int first_match = 0;
- bool add_left = false;
- char_u *s;
- int emenu;
- int l;
-
- if (matches == NULL) { // interrupted completion?
- return;
- }
-
- buf = xmalloc((size_t)Columns * MB_MAXBYTES + 1);
-
- if (match == -1) { // don't show match but original text
- match = 0;
- highlight = false;
- }
- // count 1 for the ending ">"
- clen = wildmenu_match_len(xp, (char_u *)L_MATCH(match)) + 3;
- if (match == 0) {
- first_match = 0;
- } else if (match < first_match) {
- // jumping left, as far as we can go
- first_match = match;
- add_left = true;
- } else {
- // check if match fits on the screen
- for (i = first_match; i < match; i++) {
- clen += wildmenu_match_len(xp, (char_u *)L_MATCH(i)) + 2;
- }
- if (first_match > 0) {
- clen += 2;
- }
- // jumping right, put match at the left
- if ((long)clen > Columns) {
- first_match = match;
- // if showing the last match, we can add some on the left
- clen = 2;
- for (i = match; i < num_matches; i++) {
- clen += wildmenu_match_len(xp, (char_u *)L_MATCH(i)) + 2;
- if ((long)clen >= Columns) {
- break;
- }
- }
- if (i == num_matches) {
- add_left = true;
- }
- }
- }
- if (add_left) {
- while (first_match > 0) {
- clen += wildmenu_match_len(xp, (char_u *)L_MATCH(first_match - 1)) + 2;
- if ((long)clen >= Columns) {
- break;
- }
- first_match--;
- }
- }
-
- fillchar = fillchar_status(&attr, curwin);
-
- if (first_match == 0) {
- *buf = NUL;
- len = 0;
- } else {
- STRCPY(buf, "< ");
- len = 2;
- }
- clen = len;
-
- i = first_match;
- while (clen + wildmenu_match_len(xp, (char_u *)L_MATCH(i)) + 2 < Columns) {
- if (i == match) {
- selstart = buf + len;
- selstart_col = clen;
- }
-
- s = (char_u *)L_MATCH(i);
- // Check for menu separators - replace with '|'
- emenu = (xp->xp_context == EXPAND_MENUS
- || xp->xp_context == EXPAND_MENUNAMES);
- if (emenu && menu_is_separator((char *)s)) {
- STRCPY(buf + len, transchar('|'));
- l = (int)STRLEN(buf + len);
- len += l;
- clen += l;
- } else {
- for (; *s != NUL; s++) {
- s += skip_wildmenu_char(xp, s);
- clen += ptr2cells((char *)s);
- if ((l = utfc_ptr2len((char *)s)) > 1) {
- STRNCPY(buf + len, s, l); // NOLINT(runtime/printf)
- s += l - 1;
- len += l;
- } else {
- STRCPY(buf + len, transchar_byte(*s));
- len += (int)STRLEN(buf + len);
- }
- }
- }
- if (i == match) {
- selend = buf + len;
- }
-
- *(buf + len++) = ' ';
- *(buf + len++) = ' ';
- clen += 2;
- if (++i == num_matches) {
- break;
- }
- }
-
- if (i != num_matches) {
- *(buf + len++) = '>';
- clen++;
- }
-
- buf[len] = NUL;
-
- row = cmdline_row - 1;
- if (row >= 0) {
- if (wild_menu_showing == 0 || wild_menu_showing == WM_LIST) {
- if (msg_scrolled > 0) {
- // Put the wildmenu just above the command line. If there is
- // no room, scroll the screen one line up.
- if (cmdline_row == Rows - 1) {
- msg_scroll_up(false);
- msg_scrolled++;
- } else {
- cmdline_row++;
- row++;
- }
- wild_menu_showing = WM_SCROLLED;
- } else {
- // 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 && global_stl_height() == 0) {
- save_p_ls = (int)p_ls;
- save_p_wmh = (int)p_wmh;
- p_ls = 2;
- p_wmh = 0;
- last_status(false);
- }
- wild_menu_showing = WM_SHOWN;
- }
- }
-
- // Tricky: wildmenu can be drawn either over a status line, or at empty
- // scrolled space in the message output
- ScreenGrid *grid = (wild_menu_showing == WM_SCROLLED)
- ? &msg_grid_adj : &default_grid;
-
- grid_puts(grid, buf, row, 0, attr);
- if (selstart != NULL && highlight) {
- *selend = NUL;
- grid_puts(grid, selstart, row, selstart_col, HL_ATTR(HLF_WM));
- }
-
- grid_fill(grid, row, row + 1, clen, Columns,
- fillchar, fillchar, attr);
- }
-
- win_redraw_last_status(topframe);
- xfree(buf);
-}
-
/// Only call if (wp->w_vsep_width != 0).
///
/// @return true if the status line of window "wp" is connected to the status
@@ -520,52 +295,54 @@ bool get_keymap_str(win_T *wp, char *fmt, char *buf, int len)
return false;
}
- {
- buf_T *old_curbuf = curbuf;
- win_T *old_curwin = curwin;
- char *s;
-
- curbuf = wp->w_buffer;
- curwin = wp;
- STRCPY(buf, "b:keymap_name"); // must be writable
- emsg_skip++;
- s = p = eval_to_string(buf, NULL, false);
- emsg_skip--;
- curbuf = old_curbuf;
- curwin = old_curwin;
- if (p == NULL || *p == NUL) {
- if (wp->w_buffer->b_kmap_state & KEYMAP_LOADED) {
- p = (char *)wp->w_buffer->b_p_keymap;
- } else {
- p = "lang";
- }
- }
- if (vim_snprintf(buf, (size_t)len, fmt, p) > len - 1) {
- buf[0] = NUL;
+ buf_T *old_curbuf = curbuf;
+ win_T *old_curwin = curwin;
+ char *s;
+
+ curbuf = wp->w_buffer;
+ curwin = wp;
+ STRCPY(buf, "b:keymap_name"); // must be writable
+ emsg_skip++;
+ s = p = eval_to_string(buf, NULL, false);
+ emsg_skip--;
+ curbuf = old_curbuf;
+ curwin = old_curwin;
+ if (p == NULL || *p == NUL) {
+ if (wp->w_buffer->b_kmap_state & KEYMAP_LOADED) {
+ p = wp->w_buffer->b_p_keymap;
+ } else {
+ p = "lang";
}
- xfree(s);
}
+ if (vim_snprintf(buf, (size_t)len, fmt, p) > len - 1) {
+ buf[0] = NUL;
+ }
+ xfree(s);
return buf[0] != NUL;
}
/// Prepare for 'hlsearch' highlighting.
void start_search_hl(void)
{
- if (p_hls && !no_hlsearch) {
- end_search_hl(); // just in case it wasn't called before
- last_pat_prog(&screen_search_hl.rm);
- // Set the time limit to 'redrawtime'.
- screen_search_hl.tm = profile_setlimit(p_rdt);
+ if (!p_hls || no_hlsearch) {
+ return;
}
+
+ end_search_hl(); // just in case it wasn't called before
+ last_pat_prog(&screen_search_hl.rm);
+ // Set the time limit to 'redrawtime'.
+ screen_search_hl.tm = profile_setlimit(p_rdt);
}
/// Clean up for 'hlsearch' highlighting.
void end_search_hl(void)
{
- if (screen_search_hl.rm.regprog != NULL) {
- vim_regfree(screen_search_hl.rm.regprog);
- screen_search_hl.rm.regprog = NULL;
+ if (screen_search_hl.rm.regprog == NULL) {
+ return;
}
+
+ vim_regfree(screen_search_hl.rm.regprog);
+ screen_search_hl.rm.regprog = NULL;
}
/// Check if there should be a delay. Used before clearing or redrawing the
@@ -574,7 +351,8 @@ void check_for_delay(bool check_msg_scroll)
{
if ((emsg_on_display || (check_msg_scroll && msg_scroll))
&& !did_wait_return
- && emsg_silent == 0) {
+ && emsg_silent == 0
+ && !in_assert_fails) {
ui_flush();
os_delay(1006L, true);
emsg_on_display = false;
@@ -584,22 +362,6 @@ void check_for_delay(bool check_msg_scroll)
}
}
-/// 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 stl_clear_click_defs(StlClickDefinition *const click_defs, const long click_defs_size)
-{
- 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(click_defs, 0, (size_t)click_defs_size * sizeof(click_defs[0]));
- }
-}
-
/// Set cursor to its position in the current window.
void setcursor(void)
{
@@ -620,7 +382,7 @@ void setcursor_mayforce(bool force)
// 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((char *)get_cursor_pos_ptr()) == 2
+ - ((utf_ptr2cells(get_cursor_pos_ptr()) == 2
&& vim_isprintc(gchar_cursor())) ? 2 : 1);
}
@@ -734,13 +496,13 @@ int showmode(void)
length = (Rows - msg_row) * Columns - 3;
}
if (edit_submode_extra != NULL) {
- length -= vim_strsize((char *)edit_submode_extra);
+ length -= vim_strsize(edit_submode_extra);
}
if (length > 0) {
if (edit_submode_pre != NULL) {
- length -= vim_strsize((char *)edit_submode_pre);
+ length -= vim_strsize(edit_submode_pre);
}
- if (length - vim_strsize((char *)edit_submode) > 0) {
+ if (length - vim_strsize(edit_submode) > 0) {
if (edit_submode_pre != NULL) {
msg_puts_attr((const char *)edit_submode_pre, attr);
}
@@ -787,8 +549,8 @@ int showmode(void)
if (curwin->w_p_arab) {
msg_puts_attr(_(" Arabic"), attr);
} else if (get_keymap_str(curwin, " (%s)",
- (char *)NameBuff, MAXPATHL)) {
- msg_puts_attr((char *)NameBuff, attr);
+ NameBuff, MAXPATHL)) {
+ msg_puts_attr(NameBuff, attr);
}
}
if ((State & MODE_INSERT) && p_paste) {
@@ -862,6 +624,7 @@ int showmode(void)
if (redrawing() && last->w_status_height == 0 && global_stl_height() == 0) {
win_redr_ruler(last, true);
}
+
redraw_cmdline = false;
redraw_mode = false;
clear_cmdline = false;
@@ -910,242 +673,23 @@ void clearmode(void)
static void recording_mode(int attr)
{
msg_puts_attr(_("recording"), attr);
- if (!shortmess(SHM_RECORDING)) {
- char s[4];
- snprintf(s, ARRAY_SIZE(s), " @%c", reg_recording);
- msg_puts_attr(s, attr);
- }
-}
-
-/// Draw the tab pages line at the top of the Vim window.
-void draw_tabline(void)
-{
- int tabcount = 0;
- int tabwidth = 0;
- int col = 0;
- int scol = 0;
- int attr;
- win_T *wp;
- win_T *cwp;
- int wincount;
- int modified;
- int c;
- int len;
- int attr_nosel = HL_ATTR(HLF_TP);
- int attr_fill = HL_ATTR(HLF_TPF);
- char_u *p;
- int room;
- int use_sep_chars = (t_colors < 8);
-
- if (default_grid.chars == NULL) {
- return;
- }
- redraw_tabline = false;
-
- if (ui_has(kUITabline)) {
- ui_ext_tabline_update();
+ if (shortmess(SHM_RECORDING)) {
return;
}
- if (tabline_height() < 1) {
- return;
- }
-
- // Init TabPageIdxs[] to zero: Clicking outside of tabs has no effect.
- assert(Columns == 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) {
- int saved_did_emsg = did_emsg;
-
- // 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, false);
- if (did_emsg) {
- set_string_option_direct("tabline", -1, "", OPT_FREE, SID_ERROR);
- }
- did_emsg |= saved_did_emsg;
- } else {
- FOR_ALL_TABS(tp) {
- tabcount++;
- }
-
- if (tabcount > 0) {
- tabwidth = (Columns - 1 + tabcount / 2) / tabcount;
- }
-
- if (tabwidth < 6) {
- tabwidth = 6;
- }
-
- attr = attr_nosel;
- tabcount = 0;
-
- FOR_ALL_TABS(tp) {
- if (col >= Columns - 4) {
- break;
- }
-
- scol = col;
-
- if (tp == curtab) {
- cwp = curwin;
- wp = firstwin;
- } else {
- cwp = tp->tp_curwin;
- wp = tp->tp_firstwin;
- }
-
- if (tp->tp_topframe == topframe) {
- attr = win_hl_attr(cwp, HLF_TPS);
- }
- if (use_sep_chars && col > 0) {
- grid_putchar(&default_grid, '|', 0, col++, attr);
- }
-
- if (tp->tp_topframe != topframe) {
- attr = win_hl_attr(cwp, HLF_TP);
- }
-
- grid_putchar(&default_grid, ' ', 0, col++, attr);
-
- modified = false;
-
- for (wincount = 0; wp != NULL; wp = wp->w_next, ++wincount) {
- if (bufIsChanged(wp->w_buffer)) {
- modified = true;
- }
- }
-
- if (modified || wincount > 1) {
- if (wincount > 1) {
- vim_snprintf((char *)NameBuff, MAXPATHL, "%d", wincount);
- len = (int)STRLEN(NameBuff);
- if (col + len >= Columns - 3) {
- break;
- }
- grid_puts_len(&default_grid, NameBuff, len, 0, col,
- hl_combine_attr(attr, win_hl_attr(cwp, HLF_T)));
- col += len;
- }
- if (modified) {
- grid_puts_len(&default_grid, (char_u *)"+", 1, 0, col++, attr);
- }
- grid_putchar(&default_grid, ' ', 0, col++, attr);
- }
-
- room = scol - col + tabwidth - 1;
- if (room > 0) {
- // Get buffer name in NameBuff[]
- get_trans_bufname(cwp->w_buffer);
- shorten_dir(NameBuff);
- len = vim_strsize((char *)NameBuff);
- p = NameBuff;
- while (len > room) {
- len -= ptr2cells((char *)p);
- MB_PTR_ADV(p);
- }
- if (len > Columns - col - 1) {
- len = Columns - col - 1;
- }
-
- grid_puts_len(&default_grid, p, (int)STRLEN(p), 0, col, attr);
- col += len;
- }
- grid_putchar(&default_grid, ' ', 0, col++, attr);
-
- // Store the tab page number in tab_page_click_defs[], so that
- // jump_to_mouse() knows where each one is.
- tabcount++;
- while (scol < col) {
- tab_page_click_defs[scol++] = (StlClickDefinition) {
- .type = kStlClickTabSwitch,
- .tabnr = tabcount,
- .func = NULL,
- };
- }
- }
-
- if (use_sep_chars) {
- c = '_';
- } else {
- c = ' ';
- }
- grid_fill(&default_grid, 0, 1, col, Columns, c, c, attr_fill);
-
- // Put an "X" for closing the current tab if there are several.
- if (first_tabpage->tp_next != NULL) {
- grid_putchar(&default_grid, 'X', 0, Columns - 1, attr_nosel);
- tab_page_click_defs[Columns - 1] = (StlClickDefinition) {
- .type = kStlClickTabClose,
- .tabnr = 999,
- .func = NULL,
- };
- }
- }
-
- // Reset the flag here again, in case evaluating 'tabline' causes it to be
- // set.
- redraw_tabline = false;
-}
-
-static void ui_ext_tabline_update(void)
-{
- Arena arena = ARENA_EMPTY;
- arena_start(&arena, &ui_ext_fixblk);
-
- size_t n_tabs = 0;
- FOR_ALL_TABS(tp) {
- 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_C(tab_info, "name", STRING_OBJ(arena_string(&arena, cstr_as_string((char *)NameBuff))));
-
- 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 = arena_array(&arena, n_buffers);
- FOR_ALL_BUFFERS(buf) {
- // Do not include unlisted buffers
- if (!buf->b_p_bl) {
- continue;
- }
-
- Dictionary buffer_info = arena_dict(&arena, 2);
- PUT_C(buffer_info, "buffer", BUFFER_OBJ(buf->handle));
-
- get_trans_bufname(buf);
- PUT_C(buffer_info, "name", STRING_OBJ(arena_string(&arena, cstr_as_string((char *)NameBuff))));
-
- 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);
+ char s[4];
+ snprintf(s, ARRAY_SIZE(s), " @%c", reg_recording);
+ msg_puts_attr(s, attr);
}
void get_trans_bufname(buf_T *buf)
{
if (buf_spname(buf) != NULL) {
- STRLCPY(NameBuff, buf_spname(buf), MAXPATHL);
+ xstrlcpy(NameBuff, buf_spname(buf), MAXPATHL);
} else {
- home_replace(buf, buf->b_fname, (char *)NameBuff, MAXPATHL, true);
+ home_replace(buf, buf->b_fname, NameBuff, MAXPATHL, true);
}
- trans_characters((char *)NameBuff, MAXPATHL);
+ trans_characters(NameBuff, MAXPATHL);
}
/// Get the character to use in a separator between vertically split windows.
@@ -1174,7 +718,9 @@ bool redrawing(void)
/// Return true if printing messages should currently be done.
bool messaging(void)
{
- return !(p_lz && char_avail() && !KeyTyped) && ui_has_messages();
+ // TODO(bfredl): with general support for "async" messages with p_ch,
+ // this should be re-enabled.
+ return !(p_lz && char_avail() && !KeyTyped) && (p_ch > 0 || ui_has(kUIMessages));
}
#define COL_RULER 17 // columns needed by standard ruler
@@ -1238,6 +784,16 @@ int number_width(win_T *wp)
}
wp->w_nrwidth_line_count = lnum;
+ // make best estimate for 'statuscolumn'
+ if (*wp->w_p_stc != NUL) {
+ char buf[MAXPATHL];
+ wp->w_nrwidth_width = 0;
+ n = build_statuscol_str(wp, lnum, 0, 0, NUL, buf, NULL, NULL);
+ n = MAX(n, (wp->w_p_nu || wp->w_p_rnu) * (int)wp->w_p_nuw);
+ wp->w_nrwidth_width = MIN(n, MAX_NUMBERWIDTH);
+ return wp->w_nrwidth_width;
+ }
+
n = 0;
do {
lnum /= 10;
@@ -1263,17 +819,15 @@ int number_width(win_T *wp)
/// Calls mb_cptr2char_adv(p) and returns the character.
/// If "p" starts with "\x", "\u" or "\U" the hex or unicode value is used.
/// Returns 0 for invalid hex or invalid UTF-8 byte.
-static int get_encoded_char_adv(char_u **p)
+static int get_encoded_char_adv(const char **p)
{
- char_u *s = *p;
+ const char *s = *p;
if (s[0] == '\\' && (s[1] == 'x' || s[1] == 'u' || s[1] == 'U')) {
int64_t num = 0;
- int bytes;
- int n;
- for (bytes = s[1] == 'x' ? 1 : s[1] == 'u' ? 2 : 4; bytes > 0; bytes--) {
+ for (int bytes = s[1] == 'x' ? 1 : s[1] == 'u' ? 2 : 4; bytes > 0; bytes--) {
*p += 2;
- n = hexhex2nr(*p);
+ int n = hexhex2nr(*p);
if (n < 0) {
return 0;
}
@@ -1284,8 +838,8 @@ static int get_encoded_char_adv(char_u **p)
}
// TODO(bfredl): use schar_T representation and utfc_ptr2len
- int clen = utf_ptr2len((char *)s);
- int c = mb_cptr2char_adv((const char_u **)p);
+ int clen = utf_ptr2len(s);
+ int c = mb_cptr2char_adv(p);
if (clen == 1 && c > 127) { // Invalid UTF-8 byte
return 0;
}
@@ -1295,26 +849,22 @@ static int get_encoded_char_adv(char_u **p)
/// Handle setting 'listchars' or 'fillchars'.
/// Assume monocell characters
///
-/// @param varp either &curwin->w_p_lcs or &curwin->w_p_fcs
+/// @param varp either the global or the window-local value.
+/// @param apply if false, do not store the flags, only check for errors.
/// @return error message, NULL if it's OK.
-char *set_chars_option(win_T *wp, char_u **varp, bool set)
+char *set_chars_option(win_T *wp, char **varp, bool apply)
{
- 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:"
- char_u *last_lmultispace = NULL; // Last occurrence of "leadmultispace:"
+ const char *last_multispace = NULL; // Last occurrence of "multispace:"
+ const char *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
+ const bool is_listchars = (varp == &p_lcs || varp == &wp->w_p_lcs);
struct chars_tab {
- int *cp; ///< char value
+ int *cp; ///< char value
char *name; ///< char id
- int def; ///< default value
+ int def; ///< default value
};
- struct chars_tab *tab;
// XXX: Characters taking 2 columns is forbidden (TUI limitation?). Set old defaults in this case.
struct chars_tab fcs_tab[] = {
@@ -1335,7 +885,9 @@ char *set_chars_option(win_T *wp, char_u **varp, bool set)
{ &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.lastline, "lastline", '@' },
};
+
struct chars_tab lcs_tab[] = {
{ &wp->w_p_lcs_chars.eol, "eol", NUL },
{ &wp->w_p_lcs_chars.ext, "extends", NUL },
@@ -1348,30 +900,33 @@ char *set_chars_option(win_T *wp, char_u **varp, bool set)
{ &wp->w_p_lcs_chars.conceal, "conceal", NUL },
};
- if (varp == &p_lcs || varp == &wp->w_p_lcs) {
+ struct chars_tab *tab;
+ int entries;
+ const char *value = *varp;
+ if (is_listchars) {
tab = lcs_tab;
entries = ARRAY_SIZE(lcs_tab);
if (varp == &wp->w_p_lcs && wp->w_p_lcs[0] == NUL) {
- varp = &p_lcs;
+ value = p_lcs; // local value is empty, use the global value
}
} else {
tab = fcs_tab;
entries = ARRAY_SIZE(fcs_tab);
if (varp == &wp->w_p_fcs && wp->w_p_fcs[0] == NUL) {
- varp = &p_fcs;
+ value = p_fcs; // local value is empty, use the global value
}
}
// first round: check for valid value, second round: assign values
- for (round = 0; round <= (set ? 1 : 0); round++) {
+ for (int round = 0; round <= (apply ? 1 : 0); round++) {
if (round > 0) {
// After checking that the value is valid: set defaults
- for (i = 0; i < entries; i++) {
+ for (int i = 0; i < entries; i++) {
if (tab[i].cp != NULL) {
*(tab[i].cp) = tab[i].def;
}
}
- if (varp == &p_lcs || varp == &wp->w_p_lcs) {
+ if (is_listchars) {
wp->w_p_lcs_chars.tab1 = NUL;
wp->w_p_lcs_chars.tab3 = NUL;
@@ -1393,19 +948,20 @@ char *set_chars_option(win_T *wp, char_u **varp, bool set)
}
}
}
- p = *varp;
+ const char *p = value;
while (*p) {
+ int i;
for (i = 0; i < entries; i++) {
- len = (int)STRLEN(tab[i].name);
- if (STRNCMP(p, tab[i].name, len) == 0
+ const size_t len = strlen(tab[i].name);
+ if (strncmp(p, tab[i].name, len) == 0
&& p[len] == ':'
&& p[len + 1] != NUL) {
- c2 = c3 = 0;
- s = p + len + 1;
- c1 = get_encoded_char_adv(&s);
+ const char *s = p + len + 1;
+ int c1 = get_encoded_char_adv(&s);
if (c1 == 0 || char2cells(c1) > 1) {
return e_invarg;
}
+ int c2 = 0, c3 = 0;
if (tab[i].cp == &wp->w_p_lcs_chars.tab2) {
if (*s == NUL) {
return e_invarg;
@@ -1438,19 +994,19 @@ 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
+ const size_t len = strlen("multispace");
+ const size_t len2 = strlen("leadmultispace");
+ if (is_listchars
+ && strncmp(p, "multispace", len) == 0
&& p[len] == ':'
&& p[len + 1] != NUL) {
- s = p + len + 1;
+ const char *s = p + len + 1;
if (round == 0) {
// Get length of lcs-multispace string in the first round
last_multispace = p;
multispace_len = 0;
while (*s != NUL && *s != ',') {
- c1 = get_encoded_char_adv(&s);
+ int c1 = get_encoded_char_adv(&s);
if (c1 == 0 || char2cells(c1) > 1) {
return e_invarg;
}
@@ -1464,24 +1020,24 @@ char *set_chars_option(win_T *wp, char_u **varp, bool set)
} else {
int multispace_pos = 0;
while (*s != NUL && *s != ',') {
- c1 = get_encoded_char_adv(&s);
+ int c1 = get_encoded_char_adv(&s);
if (p == last_multispace) {
wp->w_p_lcs_chars.multispace[multispace_pos++] = c1;
}
}
p = s;
}
- } else if ((varp == &p_lcs || varp == &wp->w_p_lcs)
- && STRNCMP(p, "leadmultispace", len2) == 0
+ } else if (is_listchars
+ && strncmp(p, "leadmultispace", len2) == 0
&& p[len2] == ':'
&& p[len2 + 1] != NUL) {
- s = p + len2 + 1;
+ const char *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);
+ int c1 = get_encoded_char_adv(&s);
if (c1 == 0 || char2cells(c1) > 1) {
return e_invarg;
}
@@ -1495,7 +1051,7 @@ char *set_chars_option(win_T *wp, char_u **varp, bool set)
} else {
int multispace_pos = 0;
while (*s != NUL && *s != ',') {
- c1 = get_encoded_char_adv(&s);
+ int c1 = get_encoded_char_adv(&s);
if (p == last_lmultispace) {
wp->w_p_lcs_chars.leadmultispace[multispace_pos++] = c1;
}
@@ -1506,6 +1062,7 @@ char *set_chars_option(win_T *wp, char_u **varp, bool set)
return e_invarg;
}
}
+
if (*p == ',') {
p++;
}
diff --git a/src/nvim/screen.h b/src/nvim/screen.h
index ea1c58cd80..1d8de8ca21 100644
--- a/src/nvim/screen.h
+++ b/src/nvim/screen.h
@@ -6,15 +6,10 @@
#include "nvim/buffer_defs.h"
#include "nvim/fold.h"
#include "nvim/grid_defs.h"
+#include "nvim/macros.h"
EXTERN match_T screen_search_hl; // used for 'hlsearch' highlight matching
-/// Array defining what should be done when tabline is clicked
-EXTERN StlClickDefinition *tab_page_click_defs INIT(= NULL);
-
-/// Size of the tab_page_click_defs array
-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)
diff --git a/src/nvim/search.c b/src/nvim/search.c
index c53d955974..b24b6ad27c 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -1,37 +1,39 @@
// 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
-/*
- * search.c: code for normal mode searching commands
- */
+// search.c: code for normal mode searching commands
#include <assert.h>
#include <inttypes.h>
-#include <limits.h> // for INT_MAX on MSVC
+#include <limits.h>
#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
#include "nvim/change.h"
#include "nvim/charset.h"
#include "nvim/cmdhist.h"
#include "nvim/cursor.h"
#include "nvim/drawscreen.h"
-#include "nvim/edit.h"
#include "nvim/eval.h"
-#include "nvim/eval/funcs.h"
+#include "nvim/eval/typval.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
-#include "nvim/func_attr.h"
#include "nvim/getchar.h"
-#include "nvim/indent.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/highlight_defs.h"
#include "nvim/indent_c.h"
#include "nvim/insexpand.h"
-#include "nvim/main.h"
+#include "nvim/macros.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
@@ -41,11 +43,13 @@
#include "nvim/move.h"
#include "nvim/normal.h"
#include "nvim/option.h"
+#include "nvim/os/fs.h"
#include "nvim/os/input.h"
#include "nvim/os/time.h"
#include "nvim/path.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
+#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/ui.h"
@@ -82,8 +86,7 @@
// one for other searches. last_idx points to the one that was used the last
// time.
-static struct spat spats[2] =
-{
+static struct spat spats[2] = {
// Last used search pattern
[0] = { NULL, true, false, 0, { '/', false, false, 0L }, NULL },
// Last used substitute pattern
@@ -95,24 +98,23 @@ static int last_idx = 0; // index in spats[] for RE_LAST
static char_u lastc[2] = { NUL, NUL }; // last character searched for
static Direction lastcdir = FORWARD; // last direction of character search
static int last_t_cmd = true; // last search t_cmd
-static char_u lastc_bytes[MB_MAXBYTES + 1];
+static char lastc_bytes[MB_MAXBYTES + 1];
static int lastc_bytelen = 1; // >1 for multi-byte char
// copy of spats[], for keeping the search patterns while executing autocmds
static struct spat saved_spats[2];
+static char *saved_mr_pattern = NULL;
static int saved_spats_last_idx = 0;
static bool saved_spats_no_hlsearch = false;
-static char_u *mr_pattern = NULL; // pattern used by search_regcomp()
-static bool mr_pattern_alloced = false; // mr_pattern was allocated
+// allocated copy of pattern used by search_regcomp()
+static char *mr_pattern = NULL;
-/*
- * Type used by find_pattern_in_path() to remember which included files have
- * been searched already.
- */
+// Type used by find_pattern_in_path() to remember which included files have
+// been searched already.
typedef struct SearchedFile {
FILE *fp; // File pointer
- char_u *name; // Full name of file
+ char *name; // Full name of file
linenr_T lnum; // Line we were up to in file
int matched; // Found a match in this file
} SearchedFile;
@@ -131,17 +133,16 @@ typedef struct SearchedFile {
/// @param regmatch return: pattern and ignore-case flag
///
/// @return FAIL if failed, OK otherwise.
-int search_regcomp(char_u *pat, int pat_save, int pat_use, int options, regmmatch_T *regmatch)
+int search_regcomp(char *pat, char **used_pat, int pat_save, int pat_use, int options,
+ regmmatch_T *regmatch)
{
int magic;
int i;
rc_did_emsg = false;
- magic = p_magic;
+ magic = magic_isset();
- /*
- * If no pattern given, use a previously defined pattern.
- */
+ // If no pattern given, use a previously defined pattern.
if (pat == NULL || *pat == NUL) {
if (pat_use == RE_LAST) {
i = last_idx;
@@ -164,22 +165,19 @@ int search_regcomp(char_u *pat, int pat_save, int pat_use, int options, regmmatc
add_to_history(HIST_SEARCH, pat, true, NUL);
}
- if (mr_pattern_alloced) {
- xfree(mr_pattern);
- mr_pattern_alloced = false;
+ if (used_pat) {
+ *used_pat = pat;
}
+ xfree(mr_pattern);
if (curwin->w_p_rl && *curwin->w_p_rlc == 's') {
mr_pattern = reverse_text(pat);
- mr_pattern_alloced = true;
} else {
- mr_pattern = pat;
+ mr_pattern = xstrdup(pat);
}
- /*
- * Save the currently used pattern in the appropriate place,
- * unless the pattern should not be remembered.
- */
+ // Save the currently used pattern in the appropriate place,
+ // unless the pattern should not be remembered.
if (!(options & SEARCH_KEEP) && (cmdmod.cmod_flags & CMOD_KEEPPATTERNS) == 0) {
// search or global command
if (pat_save == RE_SEARCH || pat_save == RE_BOTH) {
@@ -193,72 +191,81 @@ 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((char *)pat, magic ? RE_MAGIC : 0);
+ regmatch->regprog = vim_regcomp(pat, magic ? RE_MAGIC : 0);
if (regmatch->regprog == NULL) {
return FAIL;
}
return OK;
}
-/*
- * Get search pattern used by search_regcomp().
- */
-char_u *get_search_pat(void)
+/// Get search pattern used by search_regcomp().
+char *get_search_pat(void)
{
return mr_pattern;
}
-void save_re_pat(int idx, char_u *pat, int magic)
+void save_re_pat(int idx, char *pat, int magic)
{
- if (spats[idx].pat != pat) {
- free_spat(&spats[idx]);
- spats[idx].pat = vim_strsave(pat);
- spats[idx].magic = magic;
- spats[idx].no_scs = no_smartcase;
- spats[idx].timestamp = os_time();
- spats[idx].additional_data = NULL;
- last_idx = idx;
- // If 'hlsearch' set and search pat changed: need redraw.
- if (p_hls) {
- redraw_all_later(SOME_VALID);
- }
- set_no_hlsearch(false);
+ if (spats[idx].pat == pat) {
+ return;
+ }
+
+ free_spat(&spats[idx]);
+ spats[idx].pat = xstrdup(pat);
+ spats[idx].magic = magic;
+ spats[idx].no_scs = no_smartcase;
+ spats[idx].timestamp = os_time();
+ spats[idx].additional_data = NULL;
+ last_idx = idx;
+ // If 'hlsearch' set and search pat changed: need redraw.
+ if (p_hls) {
+ redraw_all_later(UPD_SOME_VALID);
}
+ set_no_hlsearch(false);
}
-/*
- * Save the search patterns, so they can be restored later.
- * Used before/after executing autocommands and user functions.
- */
+// Save the search patterns, so they can be restored later.
+// Used before/after executing autocommands and user functions.
static int save_level = 0;
void save_search_patterns(void)
{
- if (save_level++ == 0) {
- saved_spats[0] = spats[0];
- if (spats[0].pat != NULL) {
- saved_spats[0].pat = vim_strsave(spats[0].pat);
- }
- saved_spats[1] = spats[1];
- if (spats[1].pat != NULL) {
- saved_spats[1].pat = vim_strsave(spats[1].pat);
- }
- saved_spats_last_idx = last_idx;
- saved_spats_no_hlsearch = no_hlsearch;
+ if (save_level++ != 0) {
+ return;
+ }
+
+ saved_spats[0] = spats[0];
+ if (spats[0].pat != NULL) {
+ saved_spats[0].pat = xstrdup(spats[0].pat);
}
+ saved_spats[1] = spats[1];
+ if (spats[1].pat != NULL) {
+ saved_spats[1].pat = xstrdup(spats[1].pat);
+ }
+ if (mr_pattern == NULL) {
+ saved_mr_pattern = NULL;
+ } else {
+ saved_mr_pattern = xstrdup(mr_pattern);
+ }
+ saved_spats_last_idx = last_idx;
+ saved_spats_no_hlsearch = no_hlsearch;
}
void restore_search_patterns(void)
{
- if (--save_level == 0) {
- free_spat(&spats[0]);
- spats[0] = saved_spats[0];
- set_vv_searchforward();
- free_spat(&spats[1]);
- spats[1] = saved_spats[1];
- last_idx = saved_spats_last_idx;
- set_no_hlsearch(saved_spats_no_hlsearch);
+ if (--save_level != 0) {
+ return;
}
+
+ free_spat(&spats[0]);
+ spats[0] = saved_spats[0];
+ set_vv_searchforward();
+ free_spat(&spats[1]);
+ spats[1] = saved_spats[1];
+ xfree(mr_pattern);
+ mr_pattern = saved_mr_pattern;
+ last_idx = saved_spats_last_idx;
+ set_no_hlsearch(saved_spats_no_hlsearch);
}
static inline void free_spat(struct spat *const spat)
@@ -275,11 +282,7 @@ void free_search_patterns(void)
CLEAR_FIELD(spats);
- if (mr_pattern_alloced) {
- xfree(mr_pattern);
- mr_pattern_alloced = false;
- mr_pattern = NULL;
- }
+ XFREE_CLEAR(mr_pattern);
}
#endif
@@ -308,7 +311,7 @@ void save_last_search_pattern(void)
saved_last_search_spat = spats[RE_SEARCH];
if (spats[RE_SEARCH].pat != NULL) {
- saved_last_search_spat.pat = vim_strsave(spats[RE_SEARCH].pat);
+ saved_last_search_spat.pat = xstrdup(spats[RE_SEARCH].pat);
}
saved_last_idx = last_idx;
saved_no_hlsearch = no_hlsearch;
@@ -349,22 +352,20 @@ static void restore_incsearch_state(void)
search_match_lines = saved_search_match_lines;
}
-char_u *last_search_pattern(void)
+char *last_search_pattern(void)
{
return spats[RE_SEARCH].pat;
}
-/*
- * Return TRUE when case should be ignored for search pattern "pat".
- * Uses the 'ignorecase' and 'smartcase' options.
- */
-int ignorecase(char_u *pat)
+/// Return true when case should be ignored for search pattern "pat".
+/// Uses the 'ignorecase' and 'smartcase' options.
+int ignorecase(char *pat)
{
return ignorecase_opt(pat, p_ic, p_scs);
}
/// As ignorecase() put pass the "ic" and "scs" flags.
-int ignorecase_opt(char_u *pat, int ic_in, int scs)
+int ignorecase_opt(char *pat, int ic_in, int scs)
{
int ic = ic_in;
if (ic && !no_smartcase && scs
@@ -378,20 +379,24 @@ int ignorecase_opt(char_u *pat, int ic_in, int scs)
}
/// Returns true if pattern `pat` has an uppercase character.
-bool pat_has_uppercase(char_u *pat)
+bool pat_has_uppercase(char *pat)
FUNC_ATTR_NONNULL_ALL
{
- char_u *p = pat;
+ char *p = pat;
+ magic_T magic_val = MAGIC_ON;
+
+ // get the magicness of the pattern
+ (void)skip_regexp_ex(pat, NUL, magic_isset(), NULL, NULL, &magic_val);
while (*p != NUL) {
- const int l = utfc_ptr2len((char *)p);
+ const int l = utfc_ptr2len(p);
if (l > 1) {
- if (mb_isupper(utf_ptr2char((char *)p))) {
+ if (mb_isupper(utf_ptr2char(p))) {
return true;
}
p += l;
- } else if (*p == '\\') {
+ } else if (*p == '\\' && magic_val <= MAGIC_ON) {
if (p[1] == '_' && p[2] != NUL) { // skip "\_X"
p += 3;
} else if (p[1] == '%' && p[2] != NUL) { // skip "\%X"
@@ -401,7 +406,13 @@ bool pat_has_uppercase(char_u *pat)
} else {
p += 1;
}
- } else if (mb_isupper(*p)) {
+ } else if ((*p == '%' || *p == '_') && magic_val == MAGIC_ALL) {
+ if (p[1] != NUL) { // skip "_X" and %X
+ p += 2;
+ } else {
+ p++;
+ }
+ } else if (mb_isupper((uint8_t)(*p))) {
return true;
} else {
p++;
@@ -413,7 +424,7 @@ bool pat_has_uppercase(char_u *pat)
const char *last_csearch(void)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- return (const char *)lastc_bytes;
+ return lastc_bytes;
}
int last_csearch_forward(void)
@@ -423,10 +434,10 @@ int last_csearch_forward(void)
int last_csearch_until(void)
{
- return last_t_cmd == TRUE;
+ return last_t_cmd == true;
}
-void set_last_csearch(int c, char_u *s, int len)
+void set_last_csearch(int c, char *s, int len)
{
*lastc = (char_u)c;
lastc_bytelen = len;
@@ -447,32 +458,28 @@ void set_csearch_until(int t_cmd)
last_t_cmd = t_cmd;
}
-char_u *last_search_pat(void)
+char *last_search_pat(void)
{
return spats[last_idx].pat;
}
-/*
- * Reset search direction to forward. For "gd" and "gD" commands.
- */
+// Reset search direction to forward. For "gd" and "gD" commands.
void reset_search_dir(void)
{
spats[0].off.dir = '/';
set_vv_searchforward();
}
-/*
- * Set the last search pattern. For ":let @/ =" and ShaDa file.
- * Also set the saved search pattern, so that this works in an autocommand.
- */
-void set_last_search_pat(const char_u *s, int idx, int magic, int setlast)
+// Set the last search pattern. For ":let @/ =" and ShaDa file.
+// Also set the saved search pattern, so that this works in an autocommand.
+void set_last_search_pat(const char *s, int idx, int magic, int setlast)
{
free_spat(&spats[idx]);
// An empty string means that nothing should be matched.
if (*s == NUL) {
spats[idx].pat = NULL;
} else {
- spats[idx].pat = (char_u *)xstrdup((char *)s);
+ spats[idx].pat = xstrdup(s);
}
spats[idx].timestamp = os_time();
spats[idx].additional_data = NULL;
@@ -480,8 +487,8 @@ void set_last_search_pat(const char_u *s, int idx, int magic, int setlast)
spats[idx].no_scs = false;
spats[idx].off.dir = '/';
set_vv_searchforward();
- spats[idx].off.line = FALSE;
- spats[idx].off.end = FALSE;
+ spats[idx].off.line = false;
+ spats[idx].off.end = false;
spats[idx].off.off = 0;
if (setlast) {
last_idx = idx;
@@ -492,29 +499,27 @@ void set_last_search_pat(const char_u *s, int idx, int magic, int setlast)
if (spats[idx].pat == NULL) {
saved_spats[idx].pat = NULL;
} else {
- saved_spats[idx].pat = vim_strsave(spats[idx].pat);
+ saved_spats[idx].pat = xstrdup(spats[idx].pat);
}
saved_spats_last_idx = last_idx;
}
// If 'hlsearch' set and search pat changed: need redraw.
if (p_hls && idx == last_idx && !no_hlsearch) {
- redraw_all_later(SOME_VALID);
+ redraw_all_later(UPD_SOME_VALID);
}
}
-/*
- * Get a regexp program for the last used search pattern.
- * This is used for highlighting all matches in a window.
- * Values returned in regmatch->regprog and regmatch->rmm_ic.
- */
+// Get a regexp program for the last used search pattern.
+// This is used for highlighting all matches in a window.
+// Values returned in regmatch->regprog and regmatch->rmm_ic.
void last_pat_prog(regmmatch_T *regmatch)
{
if (spats[last_idx].pat == NULL) {
regmatch->regprog = NULL;
return;
}
- ++emsg_off; // So it doesn't beep if bad expr
- (void)search_regcomp((char_u *)"", 0, last_idx, SEARCH_KEEP, regmatch);
+ emsg_off++; // So it doesn't beep if bad expr
+ (void)search_regcomp("", NULL, 0, last_idx, SEARCH_KEEP, regmatch);
emsg_off--;
}
@@ -541,13 +546,13 @@ void last_pat_prog(regmmatch_T *regmatch)
/// @returns FAIL (zero) for failure, non-zero for success.
/// the index of the first matching
/// subpattern plus one; one if there was none.
-int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir, char_u *pat,
+int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir, char *pat,
long count, int options, int pat_use, searchit_arg_T *extra_arg)
{
int found;
linenr_T lnum; // no init to shut up Apollo cc
regmmatch_T regmatch;
- char_u *ptr;
+ char *ptr;
colnr_T matchcol;
lpos_T endpos;
lpos_T matchpos;
@@ -572,7 +577,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
timed_out = &extra_arg->sa_timed_out;
}
- if (search_regcomp(pat, RE_SEARCH, pat_use,
+ if (search_regcomp(pat, NULL, RE_SEARCH, pat_use,
(options & (SEARCH_HIS + SEARCH_KEEP)), &regmatch) == FAIL) {
if ((options & SEARCH_MSG) && !rc_did_emsg) {
semsg(_("E383: Invalid search string: %s"), mr_pattern);
@@ -580,9 +585,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
return FAIL;
}
- /*
- * find the string
- */
+ // find the string
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
@@ -594,10 +597,10 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
&& pos->col < MAXCOL - 2) {
// Watch out for the "col" being MAXCOL - 2, used in a closed fold.
ptr = ml_get_buf(buf, pos->lnum, false);
- if ((int)STRLEN(ptr) <= pos->col) {
+ if ((int)strlen(ptr) <= pos->col) {
start_char_len = 1;
} else {
- start_char_len = utfc_ptr2len((char *)ptr + pos->col);
+ start_char_len = utfc_ptr2len(ptr + pos->col);
}
} else {
start_char_len = 1;
@@ -610,31 +613,29 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
start_pos = *pos; // remember start pos for detecting no match
found = 0; // default: not found
- at_first_line = TRUE; // default: start in first line
+ at_first_line = true; // default: start in first line
if (pos->lnum == 0) { // correct lnum for when starting in line 0
pos->lnum = 1;
pos->col = 0;
- at_first_line = FALSE; // not in first line now
+ at_first_line = false; // not in first line now
}
- /*
- * Start searching in current line, unless searching backwards and
- * we're in column 0.
- * If we are searching backwards, in column 0, and not including the
- * current position, gain some efficiency by skipping back a line.
- * Otherwise begin the search in the current line.
- */
+ // Start searching in current line, unless searching backwards and
+ // we're in column 0.
+ // If we are searching backwards, in column 0, and not including the
+ // current position, gain some efficiency by skipping back a line.
+ // Otherwise begin the search in the current line.
if (dir == BACKWARD && start_pos.col == 0
&& (options & SEARCH_START) == 0) {
lnum = pos->lnum - 1;
- at_first_line = FALSE;
+ at_first_line = false;
} else {
lnum = pos->lnum;
}
- for (loop = 0; loop <= 1; ++loop) { // loop twice if 'wrapscan' set
+ for (loop = 0; loop <= 1; loop++) { // loop twice if 'wrapscan' set
for (; lnum > 0 && lnum <= buf->b_ml.ml_line_count;
- lnum += dir, at_first_line = FALSE) {
+ lnum += dir, at_first_line = false) {
// Stop after checking "stop_lnum", if it's set.
if (stop_lnum != 0 && (dir == FORWARD
? lnum > stop_lnum : lnum < stop_lnum)) {
@@ -664,18 +665,17 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
submatch = first_submatch(&regmatch);
// "lnum" may be past end of buffer for "\n\zs".
if (lnum + matchpos.lnum > buf->b_ml.ml_line_count) {
- ptr = (char_u *)"";
+ ptr = "";
} else {
ptr = ml_get_buf(buf, lnum + matchpos.lnum, false);
}
- /*
- * Forward search in the first line: match should be after
- * the start position. If not, continue at the end of the
- * match (this is vi compatible) or on the next char.
- */
+ // Forward search in the first line: match should be after
+ // the start position. If not, continue at the end of the
+ // match (this is vi compatible) or on the next char.
if (dir == FORWARD && at_first_line) {
match_ok = true;
+
// When the match starts in a next line it's certainly
// past the start position.
// When match lands on a NUL the cursor will be put
@@ -689,11 +689,9 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
: ((int)matchpos.col
- (ptr[matchpos.col] == NUL)
< (int)start_pos.col + extra_col))) {
- /*
- * If vi-compatible searching, continue at the end
- * of the match, otherwise continue one position
- * forward.
- */
+ // If vi-compatible searching, continue at the end
+ // of the match, otherwise continue one position
+ // forward.
if (vim_strchr(p_cpo, CPO_SEARCH) != NULL) {
if (nmatched > 1) {
// end is in next line, thus no match in
@@ -702,20 +700,22 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
break;
}
matchcol = endpos.col;
- // for empty match (matchcol == matchpos.col): advance one char
+ // for empty match: advance one char
+ if (matchcol == matchpos.col && ptr[matchcol] != NUL) {
+ matchcol += utfc_ptr2len(ptr + matchcol);
+ }
} else {
- // Prepare to start after first matched character.
- matchcol = matchpos.col;
- }
-
- if (matchcol == matchpos.col && ptr[matchcol] != NUL) {
- matchcol += utfc_ptr2len((char *)ptr + matchcol);
+ // Advance "matchcol" to the next character.
+ // This uses rmm_matchcol, the actual start of
+ // the match, ignoring "\zs".
+ matchcol = regmatch.rmm_matchcol;
+ if (ptr[matchcol] != NUL) {
+ matchcol += utfc_ptr2len(ptr + matchcol);
+ }
}
-
if (matchcol == 0 && (options & SEARCH_START)) {
break;
}
-
if (ptr[matchcol] == NUL
|| (nmatched = vim_regexec_multi(&regmatch, win, buf,
lnum, matchcol, tm,
@@ -746,13 +746,11 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
}
}
if (dir == BACKWARD) {
- /*
- * Now, if there are multiple matches on this line,
- * we have to get the last one. Or the last one before
- * the cursor, if we're on that line.
- * When putting the new cursor at the end, compare
- * relative to the end of the match.
- */
+ // Now, if there are multiple matches on this line,
+ // we have to get the last one. Or the last one before
+ // the cursor, if we're on that line.
+ // When putting the new cursor at the end, compare
+ // relative to the end of the match.
match_ok = false;
for (;;) {
// Remember a position that is before the start
@@ -794,7 +792,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((char *)ptr + matchcol);
+ matchcol += utfc_ptr2len(ptr + matchcol);
}
} else {
// Stop when the match is in a next line.
@@ -803,13 +801,12 @@ 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((char *)ptr + matchcol);
+ matchcol += utfc_ptr2len(ptr + matchcol);
}
}
if (ptr[matchcol] == NUL
- || (nmatched =
- vim_regexec_multi(&regmatch, win, buf, lnum + matchpos.lnum, matchcol,
- tm, timed_out)) == 0) {
+ || (nmatched = vim_regexec_multi(&regmatch, win, buf, lnum + matchpos.lnum,
+ matchcol, tm, timed_out)) == 0) {
// If the search timed out, we did find a match
// but it might be the wrong one, so that's not
// OK.
@@ -827,10 +824,8 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
ptr = ml_get_buf(buf, lnum + matchpos.lnum, false);
}
- /*
- * If there is only a match after the cursor, skip
- * this match.
- */
+ // If there is only a match after the cursor, skip
+ // this match.
if (!match_ok) {
continue;
}
@@ -849,7 +844,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
if (endpos.col == 0) {
if (pos->lnum > 1) { // just in case
pos->lnum--;
- pos->col = (colnr_T)STRLEN(ml_get_buf(buf, pos->lnum, false));
+ pos->col = (colnr_T)strlen(ml_get_buf(buf, pos->lnum, false));
}
} else {
pos->col--;
@@ -901,7 +896,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
break; // if second loop, stop where started
}
}
- at_first_line = FALSE;
+ at_first_line = false;
// vim_regexec_multi() may clear "regprog"
if (regmatch.regprog == NULL) {
@@ -966,7 +961,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
// A pattern like "\n\zs" may go past the last line.
if (pos->lnum > buf->b_ml.ml_line_count) {
pos->lnum = buf->b_ml.ml_line_count;
- pos->col = (int)STRLEN(ml_get_buf(buf, pos->lnum, false));
+ pos->col = (int)strlen(ml_get_buf(buf, pos->lnum, false));
if (pos->col > 0) {
pos->col--;
}
@@ -991,7 +986,7 @@ static int first_submatch(regmmatch_T *rp)
{
int submatch;
- for (submatch = 1;; ++submatch) {
+ for (submatch = 1;; submatch++) {
if (rp->startpos[submatch].lnum >= 0) {
break;
}
@@ -1006,63 +1001,57 @@ static int first_submatch(regmmatch_T *rp)
/// Highest level string search function.
/// Search for the 'count'th occurrence of pattern 'pat' in direction 'dirc'
///
-/// Careful: If spats[0].off.line == TRUE and spats[0].off.off == 0 this
+/// Careful: If spats[0].off.line == true and spats[0].off.off == 0 this
/// makes the movement linewise without moving the match position.
///
/// @param dirc if 0: use previous dir.
/// @param pat NULL or empty : use previous string.
-/// @param options if TRUE and
-/// SEARCH_REV == TRUE : go in reverse of previous dir.
-/// SEARCH_ECHO == TRUE : echo the search command and handle options
-/// SEARCH_MSG == TRUE : may give error message
-/// SEARCH_OPT == TRUE : interpret optional flags
-/// SEARCH_HIS == TRUE : put search pattern in history
-/// SEARCH_NOOF == TRUE : don't add offset to position
-/// SEARCH_MARK == TRUE : set previous context mark
-/// SEARCH_KEEP == TRUE : keep previous search pattern
-/// SEARCH_START == TRUE : accept match at curpos itself
-/// SEARCH_PEEK == TRUE : check for typed char, cancel search
+/// @param options if true and
+/// SEARCH_REV == true : go in reverse of previous dir.
+/// SEARCH_ECHO == true : echo the search command and handle options
+/// SEARCH_MSG == true : may give error message
+/// SEARCH_OPT == true : interpret optional flags
+/// SEARCH_HIS == true : put search pattern in history
+/// SEARCH_NOOF == true : don't add offset to position
+/// SEARCH_MARK == true : set previous context mark
+/// SEARCH_KEEP == true : keep previous search pattern
+/// SEARCH_START == true : accept match at curpos itself
+/// SEARCH_PEEK == true : check for typed char, cancel search
/// @param oap can be NULL
/// @param dirc '/' or '?'
/// @param search_delim delimiter for search, e.g. '%' in s%regex%replacement
/// @param sia optional arguments or NULL
///
/// @return 0 for failure, 1 for found, 2 for found and line offset added.
-int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count, int options,
+int do_search(oparg_T *oap, int dirc, int search_delim, char *pat, long count, int options,
searchit_arg_T *sia)
{
pos_T pos; // position of the last match
- char_u *searchstr;
+ char *searchstr;
struct soffset old_off;
int retval; // Return value
- char_u *p;
+ char *p;
long c;
- char_u *dircp;
+ char *dircp;
char *strcopy = NULL;
- char_u *ps;
- char_u *msgbuf = NULL;
+ char *ps;
+ char *msgbuf = NULL;
size_t len;
bool has_offset = false;
- /*
- * A line offset is not remembered, this is vi compatible.
- */
+ // 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.off = 0;
}
- /*
- * Save the values for when (options & SEARCH_KEEP) is used.
- * (there is no "if ()" around this because gcc wants them initialized)
- */
+ // Save the values for when (options & SEARCH_KEEP) is used.
+ // (there is no "if ()" around this because gcc wants them initialized)
old_off = spats[0].off;
pos = curwin->w_cursor; // start searching at the cursor position
- /*
- * Find out the direction of the search.
- */
+ // Find out the direction of the search.
if (dirc == 0) {
dirc = (char_u)spats[0].off.dir;
} else {
@@ -1089,17 +1078,13 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
}
}
- /*
- * Turn 'hlsearch' highlighting back on.
- */
+ // Turn 'hlsearch' highlighting back on.
if (no_hlsearch && !(options & SEARCH_KEEP)) {
- redraw_all_later(SOME_VALID);
+ redraw_all_later(UPD_SOME_VALID);
set_no_hlsearch(false);
}
- /*
- * Repeat the search when pattern followed by ';', e.g. "/foo/;?bar".
- */
+ // Repeat the search when pattern followed by ';', e.g. "/foo/;?bar".
for (;;) {
bool show_top_bot_msg = false;
@@ -1116,29 +1101,27 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
}
} else {
// make search_regcomp() use spats[RE_SEARCH].pat
- searchstr = (char_u *)"";
+ searchstr = "";
}
}
if (pat != NULL && *pat != NUL) { // look for (new) offset
- /*
- * Find end of regular expression.
- * If there is a matching '/' or '?', toss it.
- */
- ps = (char_u *)strcopy;
- p = skip_regexp(pat, search_delim, p_magic, &strcopy);
- if (strcopy != (char *)ps) {
+ // Find end of regular expression.
+ // If there is a matching '/' or '?', toss it.
+ ps = strcopy;
+ p = skip_regexp_ex(pat, search_delim, magic_isset(), &strcopy, NULL, NULL);
+ if (strcopy != ps) {
// made a copy of "pat" to change "\?" to "?"
- searchcmdlen += (int)(STRLEN(pat) - STRLEN(strcopy));
- pat = (char_u *)strcopy;
- searchstr = (char_u *)strcopy;
+ searchcmdlen += (int)(strlen(pat) - strlen(strcopy));
+ pat = strcopy;
+ searchstr = strcopy;
}
if (*p == search_delim) {
dircp = p; // remember where we put the NUL
*p++ = NUL;
}
- spats[0].off.line = FALSE;
- spats[0].off.end = FALSE;
+ spats[0].off.line = false;
+ spats[0].off.end = false;
spats[0].off.off = 0;
// Check for a line offset or a character offset.
// For get_address (echo off) we don't check for a character
@@ -1156,7 +1139,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
if (ascii_isdigit(*p) || *p == '+' || *p == '-') { // got an offset
// 'nr' or '+nr' or '-nr'
if (ascii_isdigit(*p) || ascii_isdigit(*(p + 1))) {
- spats[0].off.off = atol((char *)p);
+ spats[0].off.off = atol(p);
} else if (*p == '-') { // single '-'
spats[0].off.off = -1;
} else { // single '+'
@@ -1176,8 +1159,8 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
if ((options & SEARCH_ECHO) && messaging() && !msg_silent
&& (!cmd_silent || !shortmess(SHM_SEARCHCOUNT))) {
- char_u *trunc;
- char_u off_buf[40];
+ char *trunc;
+ char off_buf[40];
size_t off_len = 0;
// Compute msg_row early.
@@ -1187,7 +1170,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
if (!cmd_silent
&& (spats[0].off.line || spats[0].off.end || spats[0].off.off)) {
p = off_buf; // -V507
- *p++ = (char_u)dirc;
+ *p++ = (char)dirc;
if (spats[0].off.end) {
*p++ = 'e';
} else if (!spats[0].off.line) {
@@ -1198,10 +1181,10 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
}
*p = NUL;
if (spats[0].off.off != 0 || spats[0].off.line) {
- snprintf((char *)p, sizeof(off_buf) - 1 - (size_t)(p - off_buf),
+ snprintf(p, sizeof(off_buf) - 1 - (size_t)(p - off_buf),
"%" PRId64, spats[0].off.off);
}
- off_len = STRLEN(off_buf);
+ off_len = strlen(off_buf);
}
if (*searchstr == NUL) {
@@ -1224,12 +1207,12 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
// Use up to 'showcmd' column.
len = (size_t)((Rows - msg_row - 1) * Columns + sc_col - 1);
}
- if (len < STRLEN(p) + off_len + SEARCH_STAT_BUF_LEN + 3) {
- len = STRLEN(p) + off_len + SEARCH_STAT_BUF_LEN + 3;
+ if (len < strlen(p) + off_len + SEARCH_STAT_BUF_LEN + 3) {
+ len = strlen(p) + off_len + SEARCH_STAT_BUF_LEN + 3;
}
} else {
// Reserve enough space for the search pattern + offset.
- len = STRLEN(p) + off_len + 3;
+ len = strlen(p) + off_len + 3;
}
xfree(msgbuf);
@@ -1240,16 +1223,16 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
// do not fill the msgbuf buffer, if cmd_silent is set, leave it
// empty for the search_stat feature.
if (!cmd_silent) {
- msgbuf[0] = (char_u)dirc;
- if (utf_iscomposing(utf_ptr2char((char *)p))) {
+ msgbuf[0] = (char)dirc;
+ if (utf_iscomposing(utf_ptr2char(p))) {
// Use a space to draw the composing char on.
msgbuf[1] = ' ';
- memmove(msgbuf + 2, p, STRLEN(p));
+ memmove(msgbuf + 2, p, strlen(p));
} else {
- memmove(msgbuf + 1, p, STRLEN(p));
+ memmove(msgbuf + 1, p, strlen(p));
}
if (off_len > 0) {
- memmove(msgbuf + STRLEN(p) + 1, off_buf, off_len);
+ memmove(msgbuf + strlen(p) + 1, off_buf, off_len);
}
trunc = msg_strtrunc(msgbuf, true);
@@ -1263,14 +1246,14 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
// it would be blanked out again very soon. Show it on the
// left, but do reverse the text.
if (curwin->w_p_rl && *curwin->w_p_rlc == 's') {
- char_u *r = reverse_text(trunc != NULL ? trunc : msgbuf);
+ char *r = reverse_text(trunc != NULL ? trunc : msgbuf);
xfree(msgbuf);
msgbuf = r;
// move reversed text to beginning of buffer
while (*r == ' ') {
r++;
}
- size_t pat_len = (size_t)(msgbuf + STRLEN(msgbuf) - r);
+ size_t pat_len = (size_t)(msgbuf + strlen(msgbuf) - r);
memmove(msgbuf, r, pat_len);
// overwrite old text
if ((size_t)(r - msgbuf) >= pat_len) {
@@ -1279,7 +1262,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
memset(msgbuf + pat_len, ' ', (size_t)(r - msgbuf));
}
}
- msg_outtrans((char *)msgbuf);
+ msg_outtrans(msgbuf);
msg_clr_eos();
msg_check();
@@ -1289,16 +1272,14 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
}
}
- /*
- * If there is a character offset, subtract it from the current
- * position, so we don't get stuck at "?pat?e+2" or "/pat/s-2".
- * Skip this if pos.col is near MAXCOL (closed fold).
- * This is not done for a line offset, because then we would not be vi
- * compatible.
- */
+ // If there is a character offset, subtract it from the current
+ // position, so we don't get stuck at "?pat?e+2" or "/pat/s-2".
+ // Skip this if pos.col is near MAXCOL (closed fold).
+ // This is not done for a line offset, because then we would not be vi
+ // compatible.
if (!spats[0].off.line && spats[0].off.off && pos.col < MAXCOL - 2) {
if (spats[0].off.off > 0) {
- for (c = spats[0].off.off; c; --c) {
+ for (c = spats[0].off.off; c; c--) {
if (decl(&pos) == -1) {
break;
}
@@ -1308,7 +1289,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
pos.col = MAXCOL;
}
} else {
- for (c = spats[0].off.off; c; ++c) {
+ for (c = spats[0].off.off; c; c++) {
if (incl(&pos) == -1) {
break;
}
@@ -1330,7 +1311,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
RE_LAST, sia);
if (dircp != NULL) {
- *dircp = (char_u)search_delim; // restore second '/' or '?' for normal_cmd()
+ *dircp = (char)search_delim; // restore second '/' or '?' for normal_cmd()
}
if (!shortmess(SHM_SEARCH)
@@ -1352,9 +1333,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
apply_autocmds(EVENT_SEARCHWRAPPED, NULL, NULL, false, NULL);
}
- /*
- * Add character and/or line offset
- */
+ // Add character and/or line offset
if (!(options & SEARCH_NOOF) || (pat != NULL && *pat == ';')) {
pos_T org_pos = pos;
@@ -1379,9 +1358,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
break;
}
}
- }
- // to the left, check for start of file
- else {
+ } else { // to the left, check for start of file
while (c++ < 0) {
if (decl(&pos) == -1) {
break;
@@ -1421,7 +1398,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
break;
}
- dirc = *++pat;
+ dirc = (uint8_t)(*++pat);
search_delim = dirc;
if (dirc != '?' && dirc != '/') {
retval = 0;
@@ -1435,31 +1412,30 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
setpcmark();
}
curwin->w_cursor = pos;
- curwin->w_set_curswant = TRUE;
+ curwin->w_set_curswant = true;
end_do_search:
if ((options & SEARCH_KEEP) || (cmdmod.cmod_flags & CMOD_KEEPPATTERNS)) {
spats[0].off = old_off;
}
+ xfree(strcopy);
xfree(msgbuf);
return retval;
}
-/*
- * search_for_exact_line(buf, pos, dir, pat)
- *
- * Search for a line starting with the given pattern (ignoring leading
- * white-space), starting from pos and going in direction "dir". "pos" will
- * contain the position of the match found. Blank lines match only if
- * ADDING is set. If p_ic is set then the pattern must be in lowercase.
- * Return OK for success, or FAIL if no line found.
- */
-int search_for_exact_line(buf_T *buf, pos_T *pos, Direction dir, char_u *pat)
+// search_for_exact_line(buf, pos, dir, pat)
+//
+// Search for a line starting with the given pattern (ignoring leading
+// white-space), starting from pos and going in direction "dir". "pos" will
+// contain the position of the match found. Blank lines match only if
+// ADDING is set. If p_ic is set then the pattern must be in lowercase.
+// Return OK for success, or FAIL if no line found.
+int search_for_exact_line(buf_T *buf, pos_T *pos, Direction dir, char *pat)
{
linenr_T start = 0;
- char_u *ptr;
- char_u *p;
+ char *ptr;
+ char *p;
if (buf->b_ml.ml_line_count == 0) {
return FAIL;
@@ -1494,21 +1470,20 @@ 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 = (char_u *)skipwhite((char *)ptr);
+ p = skipwhite(ptr);
pos->col = (colnr_T)(p - ptr);
// when adding lines the matching line may be empty but it is not
// ignored because we are interested in the next line -- Acevedo
- if ((compl_cont_status & CONT_ADDING)
- && !(compl_cont_status & CONT_SOL)) {
+ if (compl_status_adding() && !compl_status_sol()) {
if (mb_strcmp_ic((bool)p_ic, (const char *)p, (const char *)pat) == 0) {
return OK;
}
} else if (*p != NUL) { // Ignore empty lines.
// Expanding lines or words.
- assert(compl_length >= 0);
- if ((p_ic ? mb_strnicmp(p, pat, (size_t)compl_length)
- : STRNCMP(p, pat, compl_length)) == 0) {
+ assert(ins_compl_len() >= 0);
+ if ((p_ic ? mb_strnicmp(p, pat, (size_t)ins_compl_len())
+ : strncmp(p, pat, (size_t)ins_compl_len())) == 0) {
return OK;
}
}
@@ -1516,16 +1491,12 @@ int search_for_exact_line(buf_T *buf, pos_T *pos, Direction dir, char_u *pat)
return FAIL;
}
-/*
- * Character Searches
- */
+// Character Searches
-/*
- * Search for a character in a line. If "t_cmd" is FALSE, move to the
- * position of the character, otherwise move to just before the char.
- * Do this "cap->count1" times.
- * Return FAIL or OK.
- */
+/// Search for a character in a line. If "t_cmd" is false, move to the
+/// position of the character, otherwise move to just before the char.
+/// Do this "cap->count1" times.
+/// Return FAIL or OK.
int searchc(cmdarg_T *cap, int t_cmd)
FUNC_ATTR_NONNULL_ALL
{
@@ -1533,7 +1504,7 @@ int searchc(cmdarg_T *cap, int t_cmd)
int dir = cap->arg; // true for searching forward
long count = cap->count1; // repeat count
int col;
- char_u *p;
+ char *p;
int len;
bool stop = true;
@@ -1542,13 +1513,13 @@ int searchc(cmdarg_T *cap, int t_cmd)
*lastc = (char_u)c;
set_csearch_direction(dir);
set_csearch_until(t_cmd);
- lastc_bytelen = utf_char2bytes(c, (char *)lastc_bytes);
+ lastc_bytelen = utf_char2bytes(c, lastc_bytes);
if (cap->ncharC1 != 0) {
lastc_bytelen += utf_char2bytes(cap->ncharC1,
- (char *)lastc_bytes + lastc_bytelen);
+ lastc_bytes + lastc_bytelen);
if (cap->ncharC2 != 0) {
lastc_bytelen += utf_char2bytes(cap->ncharC2,
- (char *)lastc_bytes + lastc_bytelen);
+ lastc_bytes + lastc_bytelen);
}
}
}
@@ -1581,12 +1552,12 @@ int searchc(cmdarg_T *cap, int t_cmd)
p = get_cursor_line_ptr();
col = curwin->w_cursor.col;
- len = (int)STRLEN(p);
+ len = (int)strlen(p);
while (count--) {
for (;;) {
if (dir > 0) {
- col += utfc_ptr2len((char *)p + col);
+ col += utfc_ptr2len(p + col);
if (col >= len) {
return FAIL;
}
@@ -1600,7 +1571,7 @@ int searchc(cmdarg_T *cap, int t_cmd)
if (p[col] == c && stop) {
break;
}
- } else if (STRNCMP(p + col, lastc_bytes, lastc_bytelen) == 0 && stop) {
+ } else if (strncmp(p + col, lastc_bytes, (size_t)lastc_bytelen) == 0 && stop) {
break;
}
stop = true;
@@ -1623,15 +1594,11 @@ int searchc(cmdarg_T *cap, int t_cmd)
return OK;
}
-/*
- * "Other" Searches
- */
+// "Other" Searches
-/*
- * findmatch - find the matching paren or brace
- *
- * Improvement over vi: Braces inside quotes are ignored.
- */
+// findmatch - find the matching paren or brace
+//
+// Improvement over vi: Braces inside quotes are ignored.
pos_T *findmatch(oparg_T *oap, int initc)
{
return findmatchlimit(oap, initc, 0, 0);
@@ -1642,7 +1609,7 @@ pos_T *findmatch(oparg_T *oap, int initc)
// Update "*prevcol" to the column of the previous character, unless "prevcol"
// is NULL.
// Handles multibyte string correctly.
-static bool check_prevcol(char_u *linep, int col, int ch, int *prevcol)
+static bool check_prevcol(char *linep, int col, int ch, int *prevcol)
{
col--;
if (col > 0) {
@@ -1651,32 +1618,31 @@ static bool check_prevcol(char_u *linep, int col, int ch, int *prevcol)
if (prevcol) {
*prevcol = col;
}
- return col >= 0 && linep[col] == ch;
+ return col >= 0 && (uint8_t)linep[col] == ch;
}
-/*
- * Raw string start is found at linep[startpos.col - 1].
- * Return true if the matching end can be found between startpos and endpos.
- */
-static bool find_rawstring_end(char_u *linep, pos_T *startpos, pos_T *endpos)
+/// Raw string start is found at linep[startpos.col - 1].
+///
+/// @return true if the matching end can be found between startpos and endpos.
+static bool find_rawstring_end(char *linep, pos_T *startpos, pos_T *endpos)
{
- char_u *p;
+ char *p;
linenr_T lnum;
for (p = linep + startpos->col + 1; *p && *p != '('; p++) {}
size_t delim_len = (size_t)((p - linep) - startpos->col - 1);
- char_u *delim_copy = vim_strnsave(linep + startpos->col + 1, delim_len);
+ char *delim_copy = xstrnsave(linep + startpos->col + 1, delim_len);
bool found = false;
for (lnum = startpos->lnum; lnum <= endpos->lnum; lnum++) {
- char_u *line = ml_get(lnum);
+ char *line = ml_get(lnum);
for (p = line + (lnum == startpos->lnum ? startpos->col + 1 : 0); *p; p++) {
if (lnum == endpos->lnum && (colnr_T)(p - line) >= endpos->col) {
break;
}
if (*p == ')'
- && STRNCMP(delim_copy, p + 1, delim_len) == 0
+ && strncmp(delim_copy, p + 1, delim_len) == 0
&& p[delim_len + 1] == '"') {
found = true;
break;
@@ -1697,61 +1663,58 @@ static bool find_rawstring_end(char_u *linep, pos_T *startpos, pos_T *endpos)
static void find_mps_values(int *initc, int *findc, bool *backwards, bool switchit)
FUNC_ATTR_NONNULL_ALL
{
- char_u *ptr = curbuf->b_p_mps;
+ char *ptr = curbuf->b_p_mps;
while (*ptr != NUL) {
- if (utf_ptr2char((char *)ptr) == *initc) {
+ if (utf_ptr2char(ptr) == *initc) {
if (switchit) {
*findc = *initc;
- *initc = utf_ptr2char((char *)ptr + utfc_ptr2len((char *)ptr) + 1);
+ *initc = utf_ptr2char(ptr + utfc_ptr2len(ptr) + 1);
*backwards = true;
} else {
- *findc = utf_ptr2char((char *)ptr + utfc_ptr2len((char *)ptr) + 1);
+ *findc = utf_ptr2char(ptr + utfc_ptr2len(ptr) + 1);
*backwards = false;
}
return;
}
- char_u *prev = ptr;
- ptr += utfc_ptr2len((char *)ptr) + 1;
- if (utf_ptr2char((char *)ptr) == *initc) {
+ char *prev = ptr;
+ ptr += utfc_ptr2len(ptr) + 1;
+ if (utf_ptr2char(ptr) == *initc) {
if (switchit) {
*findc = *initc;
- *initc = utf_ptr2char((char *)prev);
+ *initc = utf_ptr2char(prev);
*backwards = false;
} else {
- *findc = utf_ptr2char((char *)prev);
+ *findc = utf_ptr2char(prev);
*backwards = true;
}
return;
}
- ptr += utfc_ptr2len((char *)ptr);
+ ptr += utfc_ptr2len(ptr);
if (*ptr == ',') {
ptr++;
}
}
}
-/*
- * findmatchlimit -- find the matching paren or brace, if it exists within
- * maxtravel lines of the cursor. A maxtravel of 0 means search until falling
- * off the edge of the file.
- *
- * "initc" is the character to find a match for. NUL means to find the
- * character at or after the cursor. Special values:
- * '*' look for C-style comment / *
- * '/' look for C-style comment / *, ignoring comment-end
- * '#' look for preprocessor directives
- * 'R' look for raw string start: R"delim(text)delim" (only backwards)
- *
- * flags: FM_BACKWARD search backwards (when initc is '/', '*' or '#')
- * FM_FORWARD search forwards (when initc is '/', '*' or '#')
- * FM_BLOCKSTOP stop at start/end of block ({ or } in column 0)
- * FM_SKIPCOMM skip comments (not implemented yet!)
- *
- * "oap" is only used to set oap->motion_type for a linewise motion, it can be
- * NULL
- */
-
+// findmatchlimit -- find the matching paren or brace, if it exists within
+// maxtravel lines of the cursor. A maxtravel of 0 means search until falling
+// off the edge of the file.
+//
+// "initc" is the character to find a match for. NUL means to find the
+// character at or after the cursor. Special values:
+// '*' look for C-style comment / *
+// '/' look for C-style comment / *, ignoring comment-end
+// '#' look for preprocessor directives
+// 'R' look for raw string start: R"delim(text)delim" (only backwards)
+//
+// flags: FM_BACKWARD search backwards (when initc is '/', '*' or '#')
+// FM_FORWARD search forwards (when initc is '/', '*' or '#')
+// FM_BLOCKSTOP stop at start/end of block ({ or } in column 0)
+// FM_SKIPCOMM skip comments (not implemented yet!)
+//
+// "oap" is only used to set oap->motion_type for a linewise motion, it can be
+// NULL
pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
{
static pos_T pos; // current search position
@@ -1760,7 +1723,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
bool backwards = false; // init for gcc
bool raw_string = false; // search for raw string
bool inquote = false; // true when inside quotes
- char_u *ptr;
+ char *ptr;
int hash_dir = 0; // Direction searched for # things
int comment_dir = 0; // Direction searched for comments
int traveled = 0; // how far we've searched so far
@@ -1773,7 +1736,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
pos = curwin->w_cursor;
pos.coladd = 0;
- char_u *linep = ml_get(pos.lnum); // pointer to current line
+ char *linep = ml_get(pos.lnum); // pointer to current line
// vi compatible matching
bool cpo_match = (vim_strchr(p_cpo, CPO_MATCH) != NULL);
@@ -1789,12 +1752,10 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
dir = 0;
}
- /*
- * if initc given, look in the table for the matching character
- * '/' and '*' are special cases: look for start or end of comment.
- * When '/' is used, we ignore running backwards into a star-slash, for
- * "[*" command, we just want to find any comment.
- */
+ // if initc given, look in the table for the matching character
+ // '/' and '*' are special cases: look for start or end of comment.
+ // When '/' is used, we ignore running backwards into a star-slash, for
+ // "[*" command, we just want to find any comment.
if (initc == '/' || initc == '*' || initc == 'R') {
comment_dir = dir;
if (initc == '/') {
@@ -1812,31 +1773,25 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
return NULL;
}
} else {
- /*
- * Either initc is '#', or no initc was given and we need to look
- * under the cursor.
- */
+ // Either initc is '#', or no initc was given and we need to look
+ // under the cursor.
if (initc == '#') {
hash_dir = dir;
} else {
- /*
- * initc was not given, must look for something to match under
- * or near the cursor.
- * Only check for special things when 'cpo' doesn't have '%'.
- */
+ // initc was not given, must look for something to match under
+ // or near the cursor.
+ // Only check for special things when 'cpo' doesn't have '%'.
if (!cpo_match) {
// Are we before or at #if, #else etc.?
- ptr = (char_u *)skipwhite((char *)linep);
+ ptr = skipwhite(linep);
if (*ptr == '#' && pos.col <= (colnr_T)(ptr - linep)) {
- ptr = (char_u *)skipwhite((char *)ptr + 1);
- if (STRNCMP(ptr, "if", 2) == 0
- || STRNCMP(ptr, "endif", 5) == 0
- || STRNCMP(ptr, "el", 2) == 0) {
+ ptr = skipwhite(ptr + 1);
+ if (strncmp(ptr, "if", 2) == 0
+ || strncmp(ptr, "endif", 5) == 0
+ || strncmp(ptr, "el", 2) == 0) {
hash_dir = 1;
}
- }
- // Are we on a comment?
- else if (linep[pos.col] == '/') {
+ } else if (linep[pos.col] == '/') { // Are we on a comment?
if (linep[pos.col + 1] == '*') {
comment_dir = FORWARD;
backwards = false;
@@ -1857,21 +1812,17 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
}
}
- /*
- * If we are not on a comment or the # at the start of a line, then
- * look for brace anywhere on this line after the cursor.
- */
+ // If we are not on a comment or the # at the start of a line, then
+ // look for brace anywhere on this line after the cursor.
if (!hash_dir && !comment_dir) {
- /*
- * Find the brace under or after the cursor.
- * If beyond the end of the line, use the last character in
- * the line.
- */
+ // Find the brace under or after the cursor.
+ // If beyond the end of the line, use the last character in
+ // the line.
if (linep[pos.col] == NUL && pos.col) {
- --pos.col;
+ pos.col--;
}
for (;;) {
- initc = utf_ptr2char((char *)linep + pos.col);
+ initc = utf_ptr2char(linep + pos.col);
if (initc == NUL) {
break;
}
@@ -1880,11 +1831,11 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
if (findc) {
break;
}
- pos.col += utfc_ptr2len((char *)linep + pos.col);
+ pos.col += utfc_ptr2len(linep + pos.col);
}
if (!findc) {
// no brace in the line, maybe use " #if" then
- if (!cpo_match && *skipwhite((char *)linep) == '#') {
+ if (!cpo_match && *skipwhite(linep) == '#') {
hash_dir = 1;
} else {
return NULL;
@@ -1902,17 +1853,15 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
}
}
if (hash_dir) {
- /*
- * Look for matching #if, #else, #elif, or #endif
- */
+ // Look for matching #if, #else, #elif, or #endif
if (oap != NULL) {
oap->motion_type = kMTLineWise; // Linewise for this case only
}
if (initc != '#') {
- ptr = (char_u *)skipwhite(skipwhite((char *)linep) + 1);
- if (STRNCMP(ptr, "if", 2) == 0 || STRNCMP(ptr, "el", 2) == 0) {
+ ptr = skipwhite(skipwhite(linep) + 1);
+ if (strncmp(ptr, "if", 2) == 0 || strncmp(ptr, "el", 2) == 0) {
hash_dir = 1;
- } else if (STRNCMP(ptr, "endif", 5) == 0) {
+ } else if (strncmp(ptr, "endif", 5) == 0) {
hash_dir = -1;
} else {
return NULL;
@@ -1930,36 +1879,36 @@ 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 = (char_u *)skipwhite((char *)linep);
+ ptr = skipwhite(linep);
if (*ptr != '#') {
continue;
}
pos.col = (colnr_T)(ptr - linep);
- ptr = (char_u *)skipwhite((char *)ptr + 1);
+ ptr = skipwhite(ptr + 1);
if (hash_dir > 0) {
- if (STRNCMP(ptr, "if", 2) == 0) {
+ if (strncmp(ptr, "if", 2) == 0) {
count++;
- } else if (STRNCMP(ptr, "el", 2) == 0) {
+ } else if (strncmp(ptr, "el", 2) == 0) {
if (count == 0) {
return &pos;
}
- } else if (STRNCMP(ptr, "endif", 5) == 0) {
+ } else if (strncmp(ptr, "endif", 5) == 0) {
if (count == 0) {
return &pos;
}
count--;
}
} else {
- if (STRNCMP(ptr, "if", 2) == 0) {
+ if (strncmp(ptr, "if", 2) == 0) {
if (count == 0) {
return &pos;
}
count--;
- } else if (initc == '#' && STRNCMP(ptr, "el", 2) == 0) {
+ } else if (initc == '#' && strncmp(ptr, "el", 2) == 0) {
if (count == 0) {
return &pos;
}
- } else if (STRNCMP(ptr, "endif", 5) == 0) {
+ } else if (strncmp(ptr, "endif", 5) == 0) {
count++;
}
}
@@ -1989,10 +1938,8 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
}
while (!got_int) {
- /*
- * Go to the next position, forward or backward. We could use
- * inc() and dec() here, but that is much slower
- */
+ // Go to the next position, forward or backward. We could use
+ // inc() and dec() here, but that is much slower
if (backwards) {
// char to match is inside of comment, don't search outside
if (lispcomm && pos.col < (colnr_T)comment_col) {
@@ -2002,14 +1949,14 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
if (pos.lnum == 1) { // start of file
break;
}
- --pos.lnum;
+ pos.lnum--;
if (maxtravel > 0 && ++traveled > maxtravel) {
break;
}
linep = ml_get(pos.lnum);
- pos.col = (colnr_T)STRLEN(linep); // pos.col on trailing NUL
+ pos.col = (colnr_T)strlen(linep); // pos.col on trailing NUL
do_quotes = -1;
line_breakcheck();
@@ -2037,7 +1984,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
|| lispcomm) {
break;
}
- ++pos.lnum;
+ pos.lnum++;
if (maxtravel && traveled++ > maxtravel) {
break;
@@ -2051,7 +1998,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
comment_col = check_linecomment(linep);
}
} else {
- pos.col += utfc_ptr2len((char *)linep + pos.col);
+ pos.col += utfc_ptr2len(linep + pos.col);
}
}
@@ -2066,23 +2013,21 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
if (comment_dir) {
// Note: comments do not nest, and we ignore quotes in them
- // TODO: ignore comment brackets inside strings
+ // TODO(vim): ignore comment brackets inside strings
if (comment_dir == FORWARD) {
if (linep[pos.col] == '*' && linep[pos.col + 1] == '/') {
pos.col++;
return &pos;
}
} else { // Searching backwards
- /*
- * A comment may contain / * or / /, it may also start or end
- * with / * /. Ignore a / * after / / and after *.
- */
+ // A comment may contain / * or / /, it may also start or end
+ // with / * /. Ignore a / * after / / and after *.
if (pos.col == 0) {
continue;
} else if (raw_string) {
if (linep[pos.col - 1] == 'R'
&& linep[pos.col] == '"'
- && vim_strchr((char *)linep + pos.col + 1, '(') != NULL) {
+ && vim_strchr(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
@@ -2119,20 +2064,16 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
continue;
}
- /*
- * If smart matching ('cpoptions' does not contain '%'), braces inside
- * of quotes are ignored, but only if there is an even number of
- * quotes in the line.
- */
+ // If smart matching ('cpoptions' does not contain '%'), braces inside
+ // of quotes are ignored, but only if there is an even number of
+ // quotes in the line.
if (cpo_match) {
do_quotes = 0;
} else if (do_quotes == -1) {
- /*
- * Count the number of quotes in the line, skipping \" and '"'.
- * Watch out for "\\".
- */
+ // Count the number of quotes in the line, skipping \" and '"'.
+ // Watch out for "\\".
at_start = do_quotes;
- for (ptr = linep; *ptr; ++ptr) {
+ for (ptr = linep; *ptr; ptr++) {
if (ptr == linep + pos.col + backwards) {
at_start = (do_quotes & 1);
}
@@ -2146,10 +2087,8 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
}
do_quotes &= 1; // result is 1 with even number of quotes
- /*
- * If we find an uneven count, check current line and previous
- * one for a '\' at the end.
- */
+ // If we find an uneven count, check current line and previous
+ // one for a '\' at the end.
if (!do_quotes) {
inquote = false;
if (ptr[-1] == '\\') {
@@ -2164,7 +2103,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
}
if (pos.lnum > 1) {
ptr = ml_get(pos.lnum - 1);
- if (*ptr && *(ptr + STRLEN(ptr) - 1) == '\\') {
+ if (*ptr && *(ptr + strlen(ptr) - 1) == '\\') {
do_quotes = 1;
if (start_in_quotes == kNone) {
inquote = at_start;
@@ -2185,18 +2124,16 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
start_in_quotes = kFalse;
}
- /*
- * If 'smartmatch' is set:
- * Things inside quotes are ignored by setting 'inquote'. If we
- * find a quote without a preceding '\' invert 'inquote'. At the
- * end of a line not ending in '\' we reset 'inquote'.
- *
- * In lines with an uneven number of quotes (without preceding '\')
- * we do not know which part to ignore. Therefore we only set
- * 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((char *)linep + pos.col);
+ // If 'smartmatch' is set:
+ // Things inside quotes are ignored by setting 'inquote'. If we
+ // find a quote without a preceding '\' invert 'inquote'. At the
+ // end of a line not ending in '\' we reset 'inquote'.
+ //
+ // In lines with an uneven number of quotes (without preceding '\')
+ // we do not know which part to ignore. Therefore we only set
+ // 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);
switch (c) {
case NUL:
// at end of line without trailing backslash, reset inquote
@@ -2212,7 +2149,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
if (do_quotes) {
int col;
- for (col = pos.col - 1; col >= 0; --col) {
+ for (col = pos.col - 1; col >= 0; col--) {
if (linep[col] != '\\') {
break;
}
@@ -2224,13 +2161,11 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
}
break;
- /*
- * If smart matching ('cpoptions' does not contain '%'):
- * Skip things in single quotes: 'x' or '\x'. Be careful for single
- * single quotes, eg jon's. Things like '\233' or '\x3f' are not
- * skipped, there is never a brace in them.
- * Ignore this when finding matches for `'.
- */
+ // If smart matching ('cpoptions' does not contain '%'):
+ // Skip things in single quotes: 'x' or '\x'. Be careful for single
+ // single quotes, eg jon's. Things like '\233' or '\x3f' are not
+ // skipped, there is never a brace in them.
+ // Ignore this when finding matches for `'.
case '\'':
if (!cpo_match && initc != '\'' && findc != '\'') {
if (backwards) {
@@ -2258,10 +2193,8 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
FALLTHROUGH;
default:
- /*
- * For Lisp skip over backslashed (), {} and [].
- * (actually, we skip #\( et al)
- */
+ // For Lisp skip over backslashed (), {} and [].
+ // (actually, we skip #\( et al)
if (curbuf->b_p_lisp
&& vim_strchr("(){}[]", c) != NULL
&& pos.col > 1
@@ -2306,15 +2239,15 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
/// Check if line[] contains a / / comment.
/// @returns MAXCOL if not, otherwise return the column.
-int check_linecomment(const char_u *line)
+int check_linecomment(const char *line)
{
- const char_u *p = line; // scan from start
+ const char *p = line; // scan from start
// skip Lispish one-line comments
if (curbuf->b_p_lisp) {
if (vim_strchr((char *)p, ';') != NULL) { // there may be comments
bool in_str = false; // inside of string
- while ((p = (char_u *)strpbrk((char *)p, "\";")) != NULL) {
+ while ((p = strpbrk((char *)p, "\";")) != NULL) {
if (*p == '"') {
if (in_str) {
if (*(p - 1) != '\\') { // skip escaped quote
@@ -2336,7 +2269,7 @@ int check_linecomment(const char_u *line)
p = NULL;
}
} else {
- while ((p = (char_u *)vim_strchr((char *)p, '/')) != NULL) {
+ while ((p = 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.
@@ -2371,21 +2304,19 @@ void showmatch(int c)
long save_siso;
int save_state;
colnr_T save_dollar_vcol;
- char_u *p;
+ char *p;
- /*
- * Only show match for chars in the 'matchpairs' option.
- */
+ // Only show match for chars in the 'matchpairs' option.
// 'matchpairs' is "x:y,x:y"
for (p = curbuf->b_p_mps; *p != NUL; p++) {
- if (utf_ptr2char((char *)p) == c && (curwin->w_p_rl ^ p_ri)) {
+ if (utf_ptr2char(p) == c && (curwin->w_p_rl ^ p_ri)) {
break;
}
- p += utfc_ptr2len((char *)p) + 1;
- if (utf_ptr2char((char *)p) == c && !(curwin->w_p_rl ^ p_ri)) {
+ p += utfc_ptr2len(p) + 1;
+ if (utf_ptr2char(p) == c && !(curwin->w_p_rl ^ p_ri)) {
break;
}
- p += utfc_ptr2len((char *)p);
+ p += utfc_ptr2len(p);
if (*p == NUL) {
return;
}
@@ -2396,1865 +2327,63 @@ void showmatch(int c)
if ((lpos = findmatch(NULL, NUL)) == NULL) { // no match, so beep
vim_beep(BO_MATCH);
- } else if (lpos->lnum >= curwin->w_topline
- && lpos->lnum < curwin->w_botline) {
- if (!curwin->w_p_wrap) {
- getvcol(curwin, lpos, NULL, &vcol, NULL);
- }
- if (curwin->w_p_wrap
- || (vcol >= curwin->w_leftcol
- && vcol < curwin->w_leftcol + curwin->w_width_inner)) {
- mpos = *lpos; // save the pos, update_screen() may change it
- save_cursor = curwin->w_cursor;
- save_so = *so;
- save_siso = *siso;
- // Handle "$" in 'cpo': If the ')' is typed on top of the "$",
- // stop displaying the "$".
- if (dollar_vcol >= 0 && dollar_vcol == curwin->w_virtcol) {
- dollar_vcol = -1;
- }
- curwin->w_virtcol++; // do display ')' just before "$"
- update_screen(VALID); // show the new char first
-
- save_dollar_vcol = dollar_vcol;
- save_state = State;
- 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
- *siso = 0; // don't use 'sidescrolloff' here
- show_cursor_info(false);
- setcursor();
- ui_flush();
- // Restore dollar_vcol(), because setcursor() may call curs_rows()
- // which resets it if the matching position is in a previous line
- // and has a higher column number.
- dollar_vcol = save_dollar_vcol;
-
- /*
- * brief pause, unless 'm' is present in 'cpo' and a character is
- * available.
- */
- if (vim_strchr(p_cpo, CPO_SHOWMATCH) != NULL) {
- os_delay((uint64_t)p_mat * 100L + 8, true);
- } else if (!char_avail()) {
- os_delay((uint64_t)p_mat * 100L + 9, false);
- }
- curwin->w_cursor = save_cursor; // restore cursor position
- *so = save_so;
- *siso = save_siso;
- State = save_state;
- ui_cursor_shape(); // may show different cursor shape
- }
- }
-}
-
-// Find the start of the next sentence, searching in the direction specified
-// by the "dir" argument. The cursor is positioned on the start of the next
-// sentence when found. If the next sentence is found, return OK. Return FAIL
-// otherwise. See ":h sentence" for the precise definition of a "sentence"
-// text object.
-int findsent(Direction dir, long count)
-{
- pos_T pos, tpos;
- int c;
- int (*func)(pos_T *);
- bool noskip = false; // do not skip blanks
-
- pos = curwin->w_cursor;
- if (dir == FORWARD) {
- func = incl;
- } else {
- func = decl;
- }
-
- while (count--) {
- const pos_T prev_pos = pos;
-
- // if on an empty line, skip up to a non-empty line
- if (gchar_pos(&pos) == NUL) {
- do {
- if ((*func)(&pos) == -1) {
- break;
- }
- } while (gchar_pos(&pos) == NUL);
- if (dir == FORWARD) {
- goto found;
- }
- // if on the start of a paragraph or a section and searching forward,
- // go to the next line
- } else if (dir == FORWARD && pos.col == 0
- && startPS(pos.lnum, NUL, false)) {
- if (pos.lnum == curbuf->b_ml.ml_line_count) {
- return FAIL;
- }
- pos.lnum++;
- goto found;
- } else if (dir == BACKWARD) {
- decl(&pos);
- }
-
- // go back to the previous non-white non-punctuation character
- bool found_dot = false;
- while (c = gchar_pos(&pos), ascii_iswhite(c)
- || vim_strchr(".!?)]\"'", c) != NULL) {
- tpos = pos;
- if (decl(&tpos) == -1 || (LINEEMPTY(tpos.lnum) && dir == FORWARD)) {
- break;
- }
- if (found_dot) {
- break;
- }
- if (vim_strchr(".!?", c) != NULL) {
- found_dot = true;
- }
- if (vim_strchr(")]\"'", c) != NULL
- && vim_strchr(".!?)]\"'", gchar_pos(&tpos)) == NULL) {
- break;
- }
- decl(&pos);
- }
-
- // remember the line where the search started
- const int startlnum = pos.lnum;
- const bool cpo_J = vim_strchr(p_cpo, CPO_ENDOFSENT) != NULL;
-
- for (;;) { // find end of sentence
- c = gchar_pos(&pos);
- if (c == NUL || (pos.col == 0 && startPS(pos.lnum, NUL, FALSE))) {
- if (dir == BACKWARD && pos.lnum != startlnum) {
- ++pos.lnum;
- }
- break;
- }
- if (c == '.' || c == '!' || c == '?') {
- tpos = pos;
- do {
- if ((c = inc(&tpos)) == -1) {
- break;
- }
- } 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) == ' '))) {
- pos = tpos;
- if (gchar_pos(&pos) == NUL) { // skip NUL at EOL
- inc(&pos);
- }
- break;
- }
- }
- if ((*func)(&pos) == -1) {
- if (count) {
- return FAIL;
- }
- noskip = true;
- break;
- }
- }
-found:
- // skip white space
- while (!noskip && ((c = gchar_pos(&pos)) == ' ' || c == '\t')) {
- if (incl(&pos) == -1) {
- break;
- }
- }
-
- if (equalpos(prev_pos, pos)) {
- // didn't actually move, advance one character and try again
- if ((*func)(&pos) == -1) {
- if (count) {
- return FAIL;
- }
- break;
- }
- count++;
- }
- }
-
- setpcmark();
- curwin->w_cursor = pos;
- return OK;
-}
-
-/// Find the next paragraph or section in direction 'dir'.
-/// Paragraphs are currently supposed to be separated by empty lines.
-/// If 'what' is NUL we go to the next paragraph.
-/// If 'what' is '{' or '}' we go to the next section.
-/// If 'both' is TRUE also stop at '}'.
-///
-/// @param pincl Return: true if last char is to be included
-///
-/// @return TRUE if the next paragraph or section was found.
-bool findpar(bool *pincl, int dir, long count, int what, int both)
-{
- linenr_T curr;
- bool did_skip; // true after separating lines have been skipped
- bool first; // true on first line
- linenr_T fold_first; // first line of a closed fold
- linenr_T fold_last; // last line of a closed fold
- bool fold_skipped; // true if a closed fold was skipped this
- // iteration
-
- curr = curwin->w_cursor.lnum;
-
- while (count--) {
- did_skip = false;
- for (first = true;; first = false) {
- if (*ml_get(curr) != NUL) {
- did_skip = true;
- }
-
- // skip folded lines
- fold_skipped = false;
- if (first && hasFolding(curr, &fold_first, &fold_last)) {
- curr = ((dir > 0) ? fold_last : fold_first) + dir;
- fold_skipped = true;
- }
-
- if (!first && did_skip && startPS(curr, what, both)) {
- break;
- }
-
- if (fold_skipped) {
- curr -= dir;
- }
- if ((curr += dir) < 1 || curr > curbuf->b_ml.ml_line_count) {
- if (count) {
- return false;
- }
- curr -= dir;
- break;
- }
- }
- }
- setpcmark();
- if (both && *ml_get(curr) == '}') { // include line with '}'
- curr++;
- }
- curwin->w_cursor.lnum = curr;
- if (curr == curbuf->b_ml.ml_line_count && what != '}') {
- char_u *line = ml_get(curr);
-
- // Put the cursor on the last character in the last line and make the
- // motion inclusive.
- if ((curwin->w_cursor.col = (colnr_T)STRLEN(line)) != 0) {
- curwin->w_cursor.col--;
- curwin->w_cursor.col -= utf_head_off(line, line + curwin->w_cursor.col);
- *pincl = true;
- }
- } else {
- curwin->w_cursor.col = 0;
- }
- return true;
-}
-
-/*
- * check if the string 's' is a nroff macro that is in option 'opt'
- */
-static int inmacro(char_u *opt, char_u *s)
-{
- char_u *macro;
-
- for (macro = opt; macro[0]; macro++) {
- // Accept two characters in the option being equal to two characters
- // in the line. A space in the option matches with a space in the
- // line or the line having ended.
- if ((macro[0] == s[0]
- || (macro[0] == ' '
- && (s[0] == NUL || s[0] == ' ')))
- && (macro[1] == s[1]
- || ((macro[1] == NUL || macro[1] == ' ')
- && (s[0] == NUL || s[1] == NUL || s[1] == ' ')))) {
- break;
- }
- macro++;
- if (macro[0] == NUL) {
- break;
- }
- }
- return macro[0] != NUL;
-}
-
-/*
- * startPS: return TRUE if line 'lnum' is the start of a section or paragraph.
- * If 'para' is '{' or '}' only check for sections.
- * If 'both' is TRUE also stop at '}'
- */
-int startPS(linenr_T lnum, int para, int both)
-{
- char_u *s;
-
- s = ml_get(lnum);
- if (*s == para || *s == '\f' || (both && *s == '}')) {
- return true;
- }
- if (*s == '.' && (inmacro(p_sections, s + 1)
- || (!para && inmacro(p_para, s + 1)))) {
- return true;
- }
- return false;
-}
-
-/*
- * The following routines do the word searches performed by the 'w', 'W',
- * 'b', 'B', 'e', and 'E' commands.
- */
-
-/*
- * To perform these searches, characters are placed into one of three
- * classes, and transitions between classes determine word boundaries.
- *
- * The classes are:
- *
- * 0 - white space
- * 1 - punctuation
- * 2 or higher - keyword characters (letters, digits and underscore)
- */
-
-static int cls_bigword; // TRUE for "W", "B" or "E"
-
-/*
- * cls() - returns the class of character at curwin->w_cursor
- *
- * If a 'W', 'B', or 'E' motion is being done (cls_bigword == TRUE), chars
- * from class 2 and higher are reported as class 1 since only white space
- * boundaries are of interest.
- */
-static int cls(void)
-{
- int c;
-
- c = gchar_cursor();
- if (c == ' ' || c == '\t' || c == NUL) {
- return 0;
- }
-
- c = utf_class(c);
-
- // If cls_bigword is TRUE, report all non-blanks as class 1.
- if (c != 0 && cls_bigword) {
- return 1;
- }
- return c;
-}
-
-/// fwd_word(count, type, eol) - move forward one word
-///
-/// @return FAIL if the cursor was already at the end of the file.
-/// If eol is TRUE, last word stops at end of line (for operators).
-///
-/// @param bigword "W", "E" or "B"
-int fwd_word(long count, int bigword, int eol)
-{
- int sclass; // starting class
- int i;
- int last_line;
-
- curwin->w_cursor.coladd = 0;
- cls_bigword = bigword;
- while (--count >= 0) {
- // When inside a range of folded lines, move to the last char of the
- // last line.
- if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum)) {
- coladvance(MAXCOL);
- }
- sclass = cls();
-
- /*
- * We always move at least one character, unless on the last
- * character in the buffer.
- */
- last_line = (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count);
- i = inc_cursor();
- if (i == -1 || (i >= 1 && last_line)) { // started at last char in file
- return FAIL;
- }
- if (i >= 1 && eol && count == 0) { // started at last char in line
- return OK;
- }
-
- /*
- * Go one char past end of current word (if any)
- */
- if (sclass != 0) {
- while (cls() == sclass) {
- i = inc_cursor();
- if (i == -1 || (i >= 1 && eol && count == 0)) {
- return OK;
- }
- }
- }
-
- /*
- * go to next non-white
- */
- while (cls() == 0) {
- /*
- * We'll stop if we land on a blank line
- */
- if (curwin->w_cursor.col == 0 && *get_cursor_line_ptr() == NUL) {
- break;
- }
-
- i = inc_cursor();
- if (i == -1 || (i >= 1 && eol && count == 0)) {
- return OK;
- }
- }
- }
- return OK;
-}
-
-/*
- * bck_word() - move backward 'count' words
- *
- * If stop is TRUE and we are already on the start of a word, move one less.
- *
- * Returns FAIL if top of the file was reached.
- */
-int bck_word(long count, int bigword, int stop)
-{
- int sclass; // starting class
-
- curwin->w_cursor.coladd = 0;
- cls_bigword = bigword;
- while (--count >= 0) {
- /* When inside a range of folded lines, move to the first char of the
- * first line. */
- if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL)) {
- curwin->w_cursor.col = 0;
- }
- sclass = cls();
- if (dec_cursor() == -1) { // started at start of file
- return FAIL;
- }
-
- if (!stop || sclass == cls() || sclass == 0) {
- /*
- * Skip white space before the word.
- * Stop on an empty line.
- */
- while (cls() == 0) {
- if (curwin->w_cursor.col == 0
- && LINEEMPTY(curwin->w_cursor.lnum)) {
- goto finished;
- }
- if (dec_cursor() == -1) { // hit start of file, stop here
- return OK;
- }
- }
-
- /*
- * Move backward to start of this word.
- */
- if (skip_chars(cls(), BACKWARD)) {
- return OK;
- }
- }
-
- inc_cursor(); // overshot - forward one
-finished:
- stop = FALSE;
- }
- return OK;
-}
-
-/*
- * end_word() - move to the end of the word
- *
- * There is an apparent bug in the 'e' motion of the real vi. At least on the
- * System V Release 3 version for the 80386. Unlike 'b' and 'w', the 'e'
- * motion crosses blank lines. When the real vi crosses a blank line in an
- * 'e' motion, the cursor is placed on the FIRST character of the next
- * non-blank line. The 'E' command, however, works correctly. Since this
- * appears to be a bug, I have not duplicated it here.
- *
- * Returns FAIL if end of the file was reached.
- *
- * If stop is TRUE and we are already on the end of a word, move one less.
- * If empty is TRUE stop on an empty line.
- */
-int end_word(long count, int bigword, int stop, int empty)
-{
- int sclass; // starting class
-
- curwin->w_cursor.coladd = 0;
- cls_bigword = bigword;
- while (--count >= 0) {
- /* When inside a range of folded lines, move to the last char of the
- * last line. */
- if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum)) {
- coladvance(MAXCOL);
- }
- sclass = cls();
- if (inc_cursor() == -1) {
- return FAIL;
- }
-
- /*
- * If we're in the middle of a word, we just have to move to the end
- * of it.
- */
- if (cls() == sclass && sclass != 0) {
- /*
- * Move forward to end of the current word
- */
- if (skip_chars(sclass, FORWARD)) {
- return FAIL;
- }
- } else if (!stop || sclass == 0) {
- /*
- * We were at the end of a word. Go to the end of the next word.
- * First skip white space, if 'empty' is TRUE, stop at empty line.
- */
- while (cls() == 0) {
- if (empty && curwin->w_cursor.col == 0
- && LINEEMPTY(curwin->w_cursor.lnum)) {
- goto finished;
- }
- if (inc_cursor() == -1) { // hit end of file, stop here
- return FAIL;
- }
- }
-
- /*
- * Move forward to the end of this word.
- */
- if (skip_chars(cls(), FORWARD)) {
- return FAIL;
- }
- }
- dec_cursor(); // overshot - one char backward
-finished:
- stop = FALSE; // we move only one word less
- }
- return OK;
-}
-
-/// Move back to the end of the word.
-///
-/// @param bigword TRUE for "B"
-/// @param eol if true, then stop at end of line.
-///
-/// @return FAIL if start of the file was reached.
-int bckend_word(long count, int bigword, bool eol)
-{
- int sclass; // starting class
- int i;
-
- curwin->w_cursor.coladd = 0;
- cls_bigword = bigword;
- while (--count >= 0) {
- sclass = cls();
- if ((i = dec_cursor()) == -1) {
- return FAIL;
- }
- if (eol && i == 1) {
- return OK;
- }
-
- /*
- * Move backward to before the start of this word.
- */
- if (sclass != 0) {
- while (cls() == sclass) {
- if ((i = dec_cursor()) == -1 || (eol && i == 1)) {
- return OK;
- }
- }
- }
-
- /*
- * Move backward to end of the previous word
- */
- while (cls() == 0) {
- if (curwin->w_cursor.col == 0 && LINEEMPTY(curwin->w_cursor.lnum)) {
- break;
- }
- if ((i = dec_cursor()) == -1 || (eol && i == 1)) {
- return OK;
- }
- }
- }
- return OK;
-}
-
-/// Skip a row of characters of the same class.
-///
-/// @return true when end-of-file reached, false otherwise.
-static bool skip_chars(int cclass, int dir)
-{
- while (cls() == cclass) {
- if ((dir == FORWARD ? inc_cursor() : dec_cursor()) == -1) {
- return true;
- }
- }
- return false;
-}
-
-/*
- * Go back to the start of the word or the start of white space
- */
-static void back_in_line(void)
-{
- int sclass; // starting class
-
- sclass = cls();
- for (;;) {
- if (curwin->w_cursor.col == 0) { // stop at start of line
- break;
- }
- dec_cursor();
- if (cls() != sclass) { // stop at start of word
- inc_cursor();
- break;
- }
- }
-}
-
-static void find_first_blank(pos_T *posp)
-{
- int c;
-
- while (decl(posp) != -1) {
- c = gchar_pos(posp);
- if (!ascii_iswhite(c)) {
- incl(posp);
- break;
- }
- }
-}
-
-/// Skip count/2 sentences and count/2 separating white spaces.
-///
-/// @param at_start_sent cursor is at start of sentence
-static void findsent_forward(long count, bool at_start_sent)
-{
- while (count--) {
- findsent(FORWARD, 1L);
- if (at_start_sent) {
- find_first_blank(&curwin->w_cursor);
- }
- if (count == 0 || at_start_sent) {
- decl(&curwin->w_cursor);
- }
- at_start_sent = !at_start_sent;
- }
-}
-
-/// Find word under cursor, cursor at end.
-/// Used while an operator is pending, and in Visual mode.
-///
-/// @param include TRUE: include word and white space
-/// @param bigword FALSE == word, TRUE == WORD
-int current_word(oparg_T *oap, long count, int include, int bigword)
-{
- pos_T start_pos;
- pos_T pos;
- bool inclusive = true;
- int include_white = FALSE;
-
- cls_bigword = bigword;
- clearpos(&start_pos);
-
- // Correct cursor when 'selection' is exclusive
- if (VIsual_active && *p_sel == 'e' && lt(VIsual, curwin->w_cursor)) {
- dec_cursor();
- }
-
- /*
- * When Visual mode is not active, or when the VIsual area is only one
- * character, select the word and/or white space under the cursor.
- */
- if (!VIsual_active || equalpos(curwin->w_cursor, VIsual)) {
- /*
- * Go to start of current word or white space.
- */
- back_in_line();
- start_pos = curwin->w_cursor;
-
- /*
- * If the start is on white space, and white space should be included
- * (" word"), or start is not on white space, and white space should
- * not be included ("word"), find end of word.
- */
- if ((cls() == 0) == include) {
- if (end_word(1L, bigword, TRUE, TRUE) == FAIL) {
- return FAIL;
- }
- } else {
- /*
- * If the start is not on white space, and white space should be
- * included ("word "), or start is on white space and white
- * space should not be included (" "), find start of word.
- * If we end up in the first column of the next line (single char
- * word) back up to end of the line.
- */
- fwd_word(1L, bigword, TRUE);
- if (curwin->w_cursor.col == 0) {
- decl(&curwin->w_cursor);
- } else {
- oneleft();
- }
-
- if (include) {
- include_white = TRUE;
- }
- }
-
- if (VIsual_active) {
- // should do something when inclusive == false !
- VIsual = start_pos;
- redraw_curbuf_later(INVERTED); // update the inversion
- } else {
- oap->start = start_pos;
- oap->motion_type = kMTCharWise;
- }
- count--;
- }
-
- /*
- * When count is still > 0, extend with more objects.
- */
- while (count > 0) {
- inclusive = true;
- if (VIsual_active && lt(curwin->w_cursor, VIsual)) {
- /*
- * In Visual mode, with cursor at start: move cursor back.
- */
- if (decl(&curwin->w_cursor) == -1) {
- return FAIL;
- }
- if (include != (cls() != 0)) {
- if (bck_word(1L, bigword, TRUE) == FAIL) {
- return FAIL;
- }
- } else {
- if (bckend_word(1L, bigword, true) == FAIL) {
- return FAIL;
- }
- (void)incl(&curwin->w_cursor);
- }
- } else {
- /*
- * Move cursor forward one word and/or white area.
- */
- if (incl(&curwin->w_cursor) == -1) {
- return FAIL;
- }
- if (include != (cls() == 0)) {
- if (fwd_word(1L, bigword, TRUE) == FAIL && count > 1) {
- return FAIL;
- }
- /*
- * If end is just past a new-line, we don't want to include
- * the first character on the line.
- * Put cursor on last char of white.
- */
- if (oneleft() == FAIL) {
- inclusive = false;
- }
- } else {
- if (end_word(1L, bigword, TRUE, TRUE) == FAIL) {
- return FAIL;
- }
- }
- }
- count--;
- }
-
- if (include_white && (cls() != 0
- || (curwin->w_cursor.col == 0 && !inclusive))) {
- /*
- * If we don't include white space at the end, move the start
- * to include some white space there. This makes "daw" work
- * better on the last word in a sentence (and "2daw" on last-but-one
- * word). Also when "2daw" deletes "word." at the end of the line
- * (cursor is at start of next line).
- * But don't delete white space at start of line (indent).
- */
- pos = curwin->w_cursor; // save cursor position
- curwin->w_cursor = start_pos;
- if (oneleft() == OK) {
- back_in_line();
- if (cls() == 0 && curwin->w_cursor.col > 0) {
- if (VIsual_active) {
- VIsual = curwin->w_cursor;
- } else {
- oap->start = curwin->w_cursor;
- }
- }
- }
- curwin->w_cursor = pos; // put cursor back at end
- }
-
- if (VIsual_active) {
- if (*p_sel == 'e' && inclusive && ltoreq(VIsual, curwin->w_cursor)) {
- inc_cursor();
- }
- if (VIsual_mode == 'V') {
- VIsual_mode = 'v';
- redraw_cmdline = true; // show mode later
- }
- } else {
- oap->inclusive = inclusive;
- }
-
- return OK;
-}
-
-/*
- * Find sentence(s) under the cursor, cursor at end.
- * When Visual active, extend it by one or more sentences.
- */
-int current_sent(oparg_T *oap, long count, int include)
-{
- pos_T start_pos;
- pos_T pos;
- bool start_blank;
- int c;
- bool at_start_sent;
- long ncount;
-
- start_pos = curwin->w_cursor;
- pos = start_pos;
- findsent(FORWARD, 1L); // Find start of next sentence.
-
- /*
- * When the Visual area is bigger than one character: Extend it.
- */
- if (VIsual_active && !equalpos(start_pos, VIsual)) {
-extend:
- if (lt(start_pos, VIsual)) {
- /*
- * Cursor at start of Visual area.
- * Find out where we are:
- * - in the white space before a sentence
- * - in a sentence or just after it
- * - at the start of a sentence
- */
- at_start_sent = true;
- decl(&pos);
- while (lt(pos, curwin->w_cursor)) {
- c = gchar_pos(&pos);
- if (!ascii_iswhite(c)) {
- at_start_sent = false;
- break;
- }
- incl(&pos);
- }
- if (!at_start_sent) {
- findsent(BACKWARD, 1L);
- if (equalpos(curwin->w_cursor, start_pos)) {
- at_start_sent = true; // exactly at start of sentence
- } else {
- // inside a sentence, go to its end (start of next)
- findsent(FORWARD, 1L);
- }
- }
- if (include) { // "as" gets twice as much as "is"
- count *= 2;
- }
- while (count--) {
- if (at_start_sent) {
- find_first_blank(&curwin->w_cursor);
- }
- c = gchar_cursor();
- if (!at_start_sent || (!include && !ascii_iswhite(c))) {
- findsent(BACKWARD, 1L);
- }
- at_start_sent = !at_start_sent;
- }
- } else {
- /*
- * Cursor at end of Visual area.
- * Find out where we are:
- * - just before a sentence
- * - just before or in the white space before a sentence
- * - in a sentence
- */
- incl(&pos);
- at_start_sent = true;
- if (!equalpos(pos, curwin->w_cursor)) { // not just before a sentence
- at_start_sent = false;
- while (lt(pos, curwin->w_cursor)) {
- c = gchar_pos(&pos);
- if (!ascii_iswhite(c)) {
- at_start_sent = true;
- break;
- }
- incl(&pos);
- }
- if (at_start_sent) { // in the sentence
- findsent(BACKWARD, 1L);
- } else { // in/before white before a sentence
- curwin->w_cursor = start_pos;
- }
- }
-
- if (include) { // "as" gets twice as much as "is"
- count *= 2;
- }
- findsent_forward(count, at_start_sent);
- if (*p_sel == 'e') {
- ++curwin->w_cursor.col;
- }
- }
- return OK;
- }
-
- /*
- * If the cursor started on a blank, check if it is just before the start
- * of the next sentence.
- */
- while (c = gchar_pos(&pos), ascii_iswhite(c)) {
- incl(&pos);
- }
- if (equalpos(pos, curwin->w_cursor)) {
- start_blank = true;
- find_first_blank(&start_pos); // go back to first blank
- } else {
- start_blank = false;
- findsent(BACKWARD, 1L);
- start_pos = curwin->w_cursor;
- }
- if (include) {
- ncount = count * 2;
- } else {
- ncount = count;
- if (start_blank) {
- ncount--;
- }
- }
- if (ncount > 0) {
- findsent_forward(ncount, true);
- } else {
- decl(&curwin->w_cursor);
- }
-
- if (include) {
- /*
- * If the blank in front of the sentence is included, exclude the
- * blanks at the end of the sentence, go back to the first blank.
- * If there are no trailing blanks, try to include leading blanks.
- */
- if (start_blank) {
- find_first_blank(&curwin->w_cursor);
- c = gchar_pos(&curwin->w_cursor);
- if (ascii_iswhite(c)) {
- decl(&curwin->w_cursor);
- }
- } else if (c = gchar_cursor(), !ascii_iswhite(c)) {
- find_first_blank(&start_pos);
- }
- }
-
- if (VIsual_active) {
- // Avoid getting stuck with "is" on a single space before a sentence.
- if (equalpos(start_pos, curwin->w_cursor)) {
- goto extend;
- }
- if (*p_sel == 'e') {
- ++curwin->w_cursor.col;
- }
- VIsual = start_pos;
- VIsual_mode = 'v';
- redraw_cmdline = true; // show mode later
- redraw_curbuf_later(INVERTED); // update the inversion
- } else {
- // include a newline after the sentence, if there is one
- if (incl(&curwin->w_cursor) == -1) {
- oap->inclusive = true;
- } else {
- oap->inclusive = false;
- }
- oap->start = start_pos;
- oap->motion_type = kMTCharWise;
- }
- return OK;
-}
-
-/// Find block under the cursor, cursor at end.
-/// "what" and "other" are two matching parenthesis/brace/etc.
-///
-/// @param include TRUE == include white space
-/// @param what '(', '{', etc.
-/// @param other ')', '}', etc.
-int current_block(oparg_T *oap, long count, int include, int what, int other)
-{
- pos_T old_pos;
- pos_T *pos = NULL;
- pos_T start_pos;
- pos_T *end_pos;
- pos_T old_start, old_end;
- char *save_cpo;
- bool sol = false; // '{' at start of line
-
- old_pos = curwin->w_cursor;
- old_end = curwin->w_cursor; // remember where we started
- old_start = old_end;
-
- /*
- * If we start on '(', '{', ')', '}', etc., use the whole block inclusive.
- */
- if (!VIsual_active || equalpos(VIsual, curwin->w_cursor)) {
- setpcmark();
- if (what == '{') { // ignore indent
- while (inindent(1)) {
- if (inc_cursor() != 0) {
- break;
- }
- }
- }
- if (gchar_cursor() == what) {
- // cursor on '(' or '{', move cursor just after it
- ++curwin->w_cursor.col;
- }
- } else if (lt(VIsual, curwin->w_cursor)) {
- old_start = VIsual;
- curwin->w_cursor = VIsual; // cursor at low end of Visual
- } else {
- old_end = VIsual;
- }
-
- // Search backwards for unclosed '(', '{', etc..
- // Put this position in start_pos.
- // Ignore quotes here. Keep the "M" flag in 'cpo', as that is what the
- // user wants.
- save_cpo = p_cpo;
- 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) {
- break;
- }
- curwin->w_cursor = *pos;
- start_pos = *pos; // the findmatch for end_pos will overwrite *pos
- }
- } else {
- while (count-- > 0) {
- if ((pos = findmatchlimit(NULL, what, FM_FORWARD, 0)) == NULL) {
- break;
- }
- curwin->w_cursor = *pos;
- start_pos = *pos; // the findmatch for end_pos will overwrite *pos
- }
- }
- p_cpo = save_cpo;
-
- /*
- * Search for matching ')', '}', etc.
- * Put this position in curwin->w_cursor.
- */
- if (pos == NULL || (end_pos = findmatch(NULL, other)) == NULL) {
- curwin->w_cursor = old_pos;
- return FAIL;
- }
- curwin->w_cursor = *end_pos;
-
- // Try to exclude the '(', '{', ')', '}', etc. when "include" is FALSE.
- // If the ending '}', ')' or ']' is only preceded by indent, skip that
- // indent. But only if the resulting area is not smaller than what we
- // started with.
- while (!include) {
- incl(&start_pos);
- sol = (curwin->w_cursor.col == 0);
- decl(&curwin->w_cursor);
- while (inindent(1)) {
- sol = true;
- if (decl(&curwin->w_cursor) != 0) {
- break;
- }
- }
-
- // 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);
- if ((pos = findmatch(NULL, what)) == NULL) {
- curwin->w_cursor = old_pos;
- return FAIL;
- }
- start_pos = *pos;
- curwin->w_cursor = *pos;
- if ((end_pos = findmatch(NULL, other)) == NULL) {
- curwin->w_cursor = old_pos;
- return FAIL;
- }
- curwin->w_cursor = *end_pos;
- } else {
- break;
- }
- }
-
- if (VIsual_active) {
- if (*p_sel == 'e') {
- inc(&curwin->w_cursor);
- }
- if (sol && gchar_cursor() != NUL) {
- inc(&curwin->w_cursor); // include the line break
- }
- VIsual = start_pos;
- VIsual_mode = 'v';
- redraw_curbuf_later(INVERTED); // update the inversion
- showmode();
- } else {
- oap->start = start_pos;
- oap->motion_type = kMTCharWise;
- oap->inclusive = false;
- if (sol) {
- incl(&curwin->w_cursor);
- } else if (ltoreq(start_pos, curwin->w_cursor)) {
- // Include the character under the cursor.
- oap->inclusive = true;
- } else {
- // End is before the start (no text in between <>, [], etc.): don't
- // operate on any text.
- curwin->w_cursor = start_pos;
- }
- }
-
- 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/>".
-static bool in_html_tag(bool end_tag)
-{
- char_u *line = get_cursor_line_ptr();
- char_u *p;
- int c;
- int lc = NUL;
- pos_T pos;
-
- for (p = line + curwin->w_cursor.col; p > line;) {
- if (*p == '<') { // find '<' under/before cursor
- break;
- }
- MB_PTR_BACK(line, p);
- if (*p == '>') { // find '>' before cursor
- break;
- }
- }
- if (*p != '<') {
- return false;
- }
-
- pos.lnum = curwin->w_cursor.lnum;
- pos.col = (colnr_T)(p - line);
-
- MB_PTR_ADV(p);
- if (end_tag) {
- // check that there is a '/' after the '<'
- return *p == '/';
- }
-
- // check that there is no '/' after the '<'
- if (*p == '/') {
- return false;
- }
-
- // check that the matching '>' is not preceded by '/'
- for (;;) {
- if (inc(&pos) < 0) {
- return false;
- }
- c = *ml_get_pos(&pos);
- if (c == '>') {
- break;
- }
- lc = c;
- }
- return lc != '/';
-}
-
-/// Find tag block under the cursor, cursor at end.
-///
-/// @param include true == include white space
-int current_tagblock(oparg_T *oap, long count_arg, bool include)
-{
- long count = count_arg;
- pos_T old_pos;
- pos_T start_pos;
- pos_T end_pos;
- pos_T old_start, old_end;
- char_u *p;
- char_u *cp;
- int len;
- bool do_include = include;
- bool save_p_ws = p_ws;
- int retval = FAIL;
- int is_inclusive = true;
-
- p_ws = false;
-
- old_pos = curwin->w_cursor;
- old_end = curwin->w_cursor; // remember where we started
- old_start = old_end;
- if (!VIsual_active || *p_sel == 'e') {
- decl(&old_end); // old_end is inclusive
- }
- /*
- * If we start on "<aaa>" select that block.
- */
- if (!VIsual_active || equalpos(VIsual, curwin->w_cursor)) {
- setpcmark();
-
- // ignore indent
- while (inindent(1)) {
- if (inc_cursor() != 0) {
- break;
- }
- }
-
- if (in_html_tag(false)) {
- // cursor on start tag, move to its '>'
- while (*get_cursor_pos_ptr() != '>') {
- if (inc_cursor() < 0) {
- break;
- }
- }
- } else if (in_html_tag(true)) {
- // cursor on end tag, move to just before it
- while (*get_cursor_pos_ptr() != '<') {
- if (dec_cursor() < 0) {
- break;
- }
- }
- dec_cursor();
- old_end = curwin->w_cursor;
- }
- } else if (lt(VIsual, curwin->w_cursor)) {
- old_start = VIsual;
- curwin->w_cursor = VIsual; // cursor at low end of Visual
- } else {
- old_end = VIsual;
- }
-
-again:
- /*
- * Search backwards for unclosed "<aaa>".
- * Put this position in start_pos.
- */
- for (long n = 0; n < count; n++) {
- if (do_searchpair("<[^ \t>/!]\\+\\%(\\_s\\_[^>]\\{-}[^/]>\\|$\\|\\_s\\=>\\)",
- "",
- "</[^>]*>", BACKWARD, NULL, 0,
- NULL, (linenr_T)0, 0L) <= 0) {
- curwin->w_cursor = old_pos;
- goto theend;
- }
- }
- start_pos = curwin->w_cursor;
-
- /*
- * Search for matching "</aaa>". First isolate the "aaa".
- */
- inc_cursor();
- p = get_cursor_pos_ptr();
- for (cp = p;
- *cp != NUL && *cp != '>' && !ascii_iswhite(*cp);
- MB_PTR_ADV(cp)) {}
- len = (int)(cp - p);
- if (len == 0) {
- curwin->w_cursor = old_pos;
- goto theend;
- }
- const size_t spat_len = (size_t)len + 39;
- char *const spat = xmalloc(spat_len);
- const size_t epat_len = (size_t)len + 9;
- char *const epat = xmalloc(epat_len);
- snprintf(spat, spat_len,
- "<%.*s\\>\\%%(\\_s\\_[^>]\\{-}\\_[^/]>\\|\\_s\\?>\\)\\c", len, p);
- snprintf(epat, epat_len, "</%.*s>\\c", len, p);
-
- const int r = (int)do_searchpair(spat, "", epat, FORWARD, NULL, 0, NULL, (linenr_T)0, 0L);
-
- xfree(spat);
- xfree(epat);
-
- if (r < 1 || lt(curwin->w_cursor, old_end)) {
- // Can't find other end or it's before the previous end. Could be a
- // HTML tag that doesn't have a matching end. Search backwards for
- // another starting tag.
- count = 1;
- curwin->w_cursor = start_pos;
- goto again;
- }
-
- if (do_include) {
- // Include up to the '>'.
- while (*get_cursor_pos_ptr() != '>') {
- if (inc_cursor() < 0) {
- break;
- }
- }
- } else {
- char_u *c = get_cursor_pos_ptr();
- // Exclude the '<' of the end tag.
- // If the closing tag is on new line, do not decrement cursor, but make
- // operation exclusive, so that the linefeed will be selected
- if (*c == '<' && !VIsual_active && curwin->w_cursor.col == 0) {
- // do not decrement cursor
- is_inclusive = false;
- } else if (*c == '<') {
- dec_cursor();
- }
- }
- end_pos = curwin->w_cursor;
-
- if (!do_include) {
- // Exclude the start tag.
- curwin->w_cursor = start_pos;
- while (inc_cursor() >= 0) {
- if (*get_cursor_pos_ptr() == '>') {
- inc_cursor();
- start_pos = curwin->w_cursor;
- break;
- }
- }
- curwin->w_cursor = end_pos;
-
- // If we are in Visual mode and now have the same text as before set
- // "do_include" and try again.
- if (VIsual_active
- && equalpos(start_pos, old_start)
- && equalpos(end_pos, old_end)) {
- do_include = true;
- curwin->w_cursor = old_start;
- count = count_arg;
- goto again;
- }
- }
-
- if (VIsual_active) {
- // If the end is before the start there is no text between tags, select
- // the char under the cursor.
- if (lt(end_pos, start_pos)) {
- curwin->w_cursor = start_pos;
- } else if (*p_sel == 'e') {
- inc_cursor();
- }
- VIsual = start_pos;
- VIsual_mode = 'v';
- redraw_curbuf_later(INVERTED); // update the inversion
- showmode();
- } else {
- oap->start = start_pos;
- oap->motion_type = kMTCharWise;
- if (lt(end_pos, start_pos)) {
- // End is before the start: there is no text between tags; operate
- // on an empty area.
- curwin->w_cursor = start_pos;
- oap->inclusive = false;
- } else {
- oap->inclusive = is_inclusive;
- }
- }
- retval = OK;
-
-theend:
- p_ws = save_p_ws;
- return retval;
-}
-
-/// @param include TRUE == include white space
-/// @param type 'p' for paragraph, 'S' for section
-int current_par(oparg_T *oap, long count, int include, int type)
-{
- linenr_T start_lnum;
- linenr_T end_lnum;
- int white_in_front;
- int dir;
- int start_is_white;
- int prev_start_is_white;
- int retval = OK;
- int do_white = FALSE;
- int t;
- int i;
-
- if (type == 'S') { // not implemented yet
- return FAIL;
- }
-
- start_lnum = curwin->w_cursor.lnum;
-
- /*
- * When visual area is more than one line: extend it.
- */
- if (VIsual_active && start_lnum != VIsual.lnum) {
-extend:
- if (start_lnum < VIsual.lnum) {
- dir = BACKWARD;
- } else {
- dir = FORWARD;
- }
- for (i = (int)count; --i >= 0;) {
- if (start_lnum ==
- (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count)) {
- retval = FAIL;
- break;
- }
-
- prev_start_is_white = -1;
- for (t = 0; t < 2; ++t) {
- start_lnum += dir;
- start_is_white = linewhite(start_lnum);
- if (prev_start_is_white == start_is_white) {
- start_lnum -= dir;
- break;
- }
- for (;;) {
- if (start_lnum == (dir == BACKWARD
- ? 1 : curbuf->b_ml.ml_line_count)) {
- break;
- }
- if (start_is_white != linewhite(start_lnum + dir)
- || (!start_is_white
- && startPS(start_lnum + (dir > 0
- ? 1 : 0), 0, 0))) {
- break;
- }
- start_lnum += dir;
- }
- if (!include) {
- break;
- }
- if (start_lnum == (dir == BACKWARD
- ? 1 : curbuf->b_ml.ml_line_count)) {
- break;
- }
- prev_start_is_white = start_is_white;
- }
- }
- curwin->w_cursor.lnum = start_lnum;
- curwin->w_cursor.col = 0;
- return retval;
- }
-
- /*
- * First move back to the start_lnum of the paragraph or white lines
- */
- white_in_front = linewhite(start_lnum);
- while (start_lnum > 1) {
- if (white_in_front) { // stop at first white line
- if (!linewhite(start_lnum - 1)) {
- break;
- }
- } else { // stop at first non-white line of start of paragraph
- if (linewhite(start_lnum - 1) || startPS(start_lnum, 0, 0)) {
- break;
- }
- }
- start_lnum--;
- }
-
- /*
- * Move past the end of any white lines.
- */
- end_lnum = start_lnum;
- while (end_lnum <= curbuf->b_ml.ml_line_count && linewhite(end_lnum)) {
- end_lnum++;
- }
-
- end_lnum--;
- i = (int)count;
- if (!include && white_in_front) {
- i--;
- }
- while (i--) {
- if (end_lnum == curbuf->b_ml.ml_line_count) {
- return FAIL;
- }
-
- if (!include) {
- do_white = linewhite(end_lnum + 1);
- }
-
- if (include || !do_white) {
- end_lnum++;
- // skip to end of paragraph
- while (end_lnum < curbuf->b_ml.ml_line_count
- && !linewhite(end_lnum + 1)
- && !startPS(end_lnum + 1, 0, 0)) {
- end_lnum++;
- }
- }
-
- if (i == 0 && white_in_front && include) {
- break;
- }
-
- /*
- * skip to end of white lines after paragraph
- */
- if (include || do_white) {
- while (end_lnum < curbuf->b_ml.ml_line_count
- && linewhite(end_lnum + 1)) {
- end_lnum++;
- }
- }
- }
-
- /*
- * If there are no empty lines at the end, try to find some empty lines at
- * the start (unless that has been done already).
- */
- if (!white_in_front && !linewhite(end_lnum) && include) {
- while (start_lnum > 1 && linewhite(start_lnum - 1)) {
- start_lnum--;
- }
- }
-
- if (VIsual_active) {
- // Problem: when doing "Vipipip" nothing happens in a single white
- // line, we get stuck there. Trap this here.
- if (VIsual_mode == 'V' && start_lnum == curwin->w_cursor.lnum) {
- goto extend;
- }
- if (VIsual.lnum != start_lnum) {
- VIsual.lnum = start_lnum;
- VIsual.col = 0;
- }
- VIsual_mode = 'V';
- redraw_curbuf_later(INVERTED); // update the inversion
- showmode();
- } else {
- oap->start.lnum = start_lnum;
- oap->start.col = 0;
- oap->motion_type = kMTLineWise;
- }
- curwin->w_cursor.lnum = end_lnum;
- curwin->w_cursor.col = 0;
-
- 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.
-///
-/// @param escape escape characters, can be NULL
-///
-/// @return column number of "quotechar" or -1 when not found.
-static int find_next_quote(char_u *line, int col, int quotechar, char_u *escape)
-{
- int c;
-
- for (;;) {
- c = line[col];
- if (c == NUL) {
- return -1;
- } else if (escape != NULL && vim_strchr((char *)escape, c)) {
- col++;
- if (line[col] == NUL) {
- return -1;
- }
- } else if (c == quotechar) {
- break;
- }
- col += utfc_ptr2len((char *)line + col);
- }
- return col;
-}
-
-/// Search backwards in "line" from column "col_start" to find "quotechar".
-/// Quote character escaped by one of the characters in "escape" is not counted
-/// as a quote.
-///
-/// @param escape escape characters, can be NULL
-///
-/// @return the found column or zero.
-static int find_prev_quote(char_u *line, int col_start, int quotechar, char_u *escape)
-{
- int n;
-
- while (col_start > 0) {
- col_start--;
- col_start -= utf_head_off(line, line + col_start);
- n = 0;
- if (escape != NULL) {
- 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) {
- break;
- }
- }
- return col_start;
-}
-
-/// Find quote under the cursor, cursor at end.
-///
-/// @param include true == include quote char
-/// @param quotechar Quote character
-///
-/// @return true if found, else false.
-bool current_quote(oparg_T *oap, long count, bool include, int quotechar)
- FUNC_ATTR_NONNULL_ALL
-{
- char_u *line = get_cursor_line_ptr();
- int col_end;
- int col_start = curwin->w_cursor.col;
- bool inclusive = false;
- bool vis_empty = true; // Visual selection <= 1 char
- bool vis_bef_curs = false; // Visual starts before cursor
- bool did_exclusive_adj = false; // adjusted pos for 'selection'
- bool inside_quotes = false; // Looks like "i'" done before
- bool selected_quote = false; // Has quote inside selection
- int i;
- bool restore_vis_bef = false; // resotre VIsual on abort
-
- // When 'selection' is "exclusive" move the cursor to where it would be
- // with 'selection' "inclusive", so that the logic is the same for both.
- // The cursor then is moved forward after adjusting the area.
- if (VIsual_active) {
- // this only works within one line
- if (VIsual.lnum != curwin->w_cursor.lnum) {
- return false;
- }
-
- vis_bef_curs = lt(VIsual, curwin->w_cursor);
- vis_empty = equalpos(VIsual, curwin->w_cursor);
- if (*p_sel == 'e') {
- if (vis_bef_curs) {
- dec_cursor();
- did_exclusive_adj = true;
- } else if (!vis_empty) {
- dec(&VIsual);
- did_exclusive_adj = true;
- }
- vis_empty = equalpos(VIsual, curwin->w_cursor);
- if (!vis_bef_curs && !vis_empty) {
- // VIsual needs to be start of Visual selection.
- pos_T t = curwin->w_cursor;
-
- curwin->w_cursor = VIsual;
- VIsual = t;
- vis_bef_curs = true;
- restore_vis_bef = true;
- }
- }
- }
-
- if (!vis_empty) {
- // Check if the existing selection exactly spans the text inside
- // quotes.
- if (vis_bef_curs) {
- inside_quotes = VIsual.col > 0
- && line[VIsual.col - 1] == quotechar
- && line[curwin->w_cursor.col] != NUL
- && line[curwin->w_cursor.col + 1] == quotechar;
- i = VIsual.col;
- col_end = curwin->w_cursor.col;
- } else {
- inside_quotes = curwin->w_cursor.col > 0
- && line[curwin->w_cursor.col - 1] == quotechar
- && line[VIsual.col] != NUL
- && line[VIsual.col + 1] == quotechar;
- i = curwin->w_cursor.col;
- col_end = VIsual.col;
- }
-
- // 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;
- }
- }
- }
-
- if (!vis_empty && line[col_start] == quotechar) {
- // Already selecting something and on a quote character. Find the
- // next quoted string.
- if (vis_bef_curs) {
- // Assume we are on a closing quote: move to after the next
- // opening quote.
- col_start = find_next_quote(line, col_start + 1, quotechar, NULL);
- if (col_start < 0) {
- goto abort_search;
- }
- col_end = find_next_quote(line, col_start + 1, quotechar,
- curbuf->b_p_qe);
- if (col_end < 0) {
- // We were on a starting quote perhaps?
- col_end = col_start;
- col_start = curwin->w_cursor.col;
- }
- } else {
- col_end = find_prev_quote(line, col_start, quotechar, NULL);
- if (line[col_end] != quotechar) {
- goto abort_search;
- }
- col_start = find_prev_quote(line, col_end, quotechar,
- curbuf->b_p_qe);
- if (line[col_start] != quotechar) {
- // We were on an ending quote perhaps?
- col_start = col_end;
- col_end = curwin->w_cursor.col;
- }
- }
- } else if (line[col_start] == quotechar || !vis_empty) {
- int first_col = col_start;
-
- if (!vis_empty) {
- if (vis_bef_curs) {
- first_col = find_next_quote(line, col_start, quotechar, NULL);
- } else {
- first_col = find_prev_quote(line, col_start, quotechar, NULL);
- }
- }
- // The cursor is on a quote, we don't know if it's the opening or
- // closing quote. Search from the start of the line to find out.
- // Also do this when there is a Visual area, a' may leave the cursor
- // in between two strings.
- col_start = 0;
- for (;;) {
- // Find open quote character.
- col_start = find_next_quote(line, col_start, quotechar, NULL);
- if (col_start < 0 || col_start > first_col) {
- goto abort_search;
- }
- // Find close quote character.
- col_end = find_next_quote(line, col_start + 1, quotechar,
- curbuf->b_p_qe);
- if (col_end < 0) {
- goto abort_search;
- }
- // If is cursor between start and end quote character, it is
- // target text object.
- if (col_start <= first_col && first_col <= col_end) {
- break;
- }
- col_start = col_end + 1;
- }
- } else {
- // Search backward for a starting quote.
- col_start = find_prev_quote(line, col_start, quotechar, curbuf->b_p_qe);
- if (line[col_start] != quotechar) {
- // No quote before the cursor, look after the cursor.
- col_start = find_next_quote(line, col_start, quotechar, NULL);
- if (col_start < 0) {
- goto abort_search;
- }
- }
-
- // Find close quote character.
- col_end = find_next_quote(line, col_start + 1, quotechar,
- curbuf->b_p_qe);
- if (col_end < 0) {
- goto abort_search;
- }
+ return;
}
- // When "include" is true, include spaces after closing quote or before
- // the starting quote.
- if (include) {
- if (ascii_iswhite(line[col_end + 1])) {
- while (ascii_iswhite(line[col_end + 1])) {
- col_end++;
- }
- } else {
- while (col_start > 0 && ascii_iswhite(line[col_start - 1])) {
- col_start--;
- }
- }
+ if (lpos->lnum < curwin->w_topline || lpos->lnum >= curwin->w_botline) {
+ return;
}
- // 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++;
- }
- curwin->w_cursor.col = col_start;
- if (VIsual_active) {
- // Set the start of the Visual area when the Visual area was empty, we
- // were just inside quotes or the Visual area didn't start at a quote
- // and didn't include a quote.
- if (vis_empty
- || (vis_bef_curs
- && !selected_quote
- && (inside_quotes
- || (line[VIsual.col] != quotechar
- && (VIsual.col == 0
- || line[VIsual.col - 1] != quotechar))))) {
- VIsual = curwin->w_cursor;
- redraw_curbuf_later(INVERTED);
- }
- } else {
- oap->start = curwin->w_cursor;
- oap->motion_type = kMTCharWise;
+ if (!curwin->w_p_wrap) {
+ getvcol(curwin, lpos, NULL, &vcol, NULL);
}
- // Set end position.
- curwin->w_cursor.col = col_end;
- if ((include || count > 1
- // After vi" another i" must include the ".
- || (!vis_empty && inside_quotes)
- ) && inc_cursor() == 2) {
- inclusive = true;
- }
- if (VIsual_active) {
- if (vis_empty || vis_bef_curs) {
- // decrement cursor when 'selection' is not exclusive
- if (*p_sel != 'e') {
- dec_cursor();
- }
- } else {
- // Cursor is at start of Visual area. Set the end of the Visual
- // area when it was just inside quotes or it didn't end at a
- // quote.
- if (inside_quotes
- || (!selected_quote
- && line[VIsual.col] != quotechar
- && (line[VIsual.col] == NUL
- || line[VIsual.col + 1] != quotechar))) {
- dec_cursor();
- VIsual = curwin->w_cursor;
- }
- curwin->w_cursor.col = col_start;
- }
- if (VIsual_mode == 'V') {
- VIsual_mode = 'v';
- redraw_cmdline = true; // show mode later
- }
- } else {
- // Set inclusive and other oap's flags.
- oap->inclusive = inclusive;
+ bool col_visible = curwin->w_p_wrap
+ || (vcol >= curwin->w_leftcol
+ && vcol < curwin->w_leftcol + curwin->w_width_inner);
+ if (!col_visible) {
+ return;
}
- return true;
-
-abort_search:
- if (VIsual_active && *p_sel == 'e') {
- if (did_exclusive_adj) {
- inc_cursor();
- }
- if (restore_vis_bef) {
- pos_T t = curwin->w_cursor;
-
- curwin->w_cursor = VIsual;
- VIsual = t;
- }
- }
- return false;
+ mpos = *lpos; // save the pos, update_screen() may change it
+ save_cursor = curwin->w_cursor;
+ save_so = *so;
+ save_siso = *siso;
+ // Handle "$" in 'cpo': If the ')' is typed on top of the "$",
+ // stop displaying the "$".
+ if (dollar_vcol >= 0 && dollar_vcol == curwin->w_virtcol) {
+ dollar_vcol = -1;
+ }
+ curwin->w_virtcol++; // do display ')' just before "$"
+ update_screen(); // show the new char first
+
+ save_dollar_vcol = dollar_vcol;
+ save_state = State;
+ 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
+ *siso = 0; // don't use 'sidescrolloff' here
+ show_cursor_info(false);
+ setcursor();
+ ui_flush();
+ // Restore dollar_vcol(), because setcursor() may call curs_rows()
+ // which resets it if the matching position is in a previous line
+ // and has a higher column number.
+ dollar_vcol = save_dollar_vcol;
+
+ // brief pause, unless 'm' is present in 'cpo' and a character is
+ // available.
+ if (vim_strchr(p_cpo, CPO_SHOWMATCH) != NULL) {
+ os_delay((uint64_t)p_mat * 100L + 8, true);
+ } else if (!char_avail()) {
+ os_delay((uint64_t)p_mat * 100L + 9, false);
+ }
+ curwin->w_cursor = save_cursor; // restore cursor position
+ *so = save_so;
+ *siso = save_siso;
+ State = save_state;
+ ui_cursor_shape(); // may show different cursor shape
}
/// Find next search match under cursor, cursor at end.
@@ -4292,8 +2421,7 @@ int current_search(long count, bool forward)
}
// Is the pattern is zero-width?, this time, don't care about the direction
- int zero_width = is_zero_width(spats[last_idx].pat, true, &curwin->w_cursor,
- FORWARD);
+ int zero_width = is_zero_width(spats[last_idx].pat, true, &curwin->w_cursor, FORWARD);
if (zero_width == -1) {
return FAIL; // pattern not found
}
@@ -4347,7 +2475,7 @@ int current_search(long count, bool forward)
} else { // try again from end of buffer
// searching backwards, so set pos to last line and col
pos.lnum = curwin->w_buffer->b_ml.ml_line_count;
- pos.col = (colnr_T)STRLEN(ml_get(curwin->w_buffer->b_ml.ml_line_count));
+ pos.col = (colnr_T)strlen(ml_get(curwin->w_buffer->b_ml.ml_line_count));
}
}
}
@@ -4389,7 +2517,7 @@ int current_search(long count, bool forward)
may_start_select('c');
setmouse();
- redraw_curbuf_later(INVERTED);
+ redraw_curbuf_later(UPD_INVERTED);
showmode();
return OK;
@@ -4399,8 +2527,8 @@ int current_search(long count, bool forward)
/// If move is true, check from the beginning of the buffer,
/// else from position "cur".
/// "direction" is FORWARD or BACKWARD.
-/// Returns TRUE, FALSE or -1 for failure.
-static int is_zero_width(char_u *pattern, int move, pos_T *cur, Direction direction)
+/// Returns true, false or -1 for failure.
+static int is_zero_width(char *pattern, int move, pos_T *cur, Direction direction)
{
regmmatch_T regmatch;
int nmatched = 0;
@@ -4413,7 +2541,7 @@ static int is_zero_width(char_u *pattern, int move, pos_T *cur, Direction direct
pattern = spats[last_idx].pat;
}
- if (search_regcomp(pattern, RE_SEARCH, RE_SEARCH,
+ if (search_regcomp(pattern, NULL, RE_SEARCH, RE_SEARCH,
SEARCH_KEEP, &regmatch) == FAIL) {
return -1;
}
@@ -4456,76 +2584,76 @@ static int is_zero_width(char_u *pattern, int move, pos_T *cur, Direction direct
return result;
}
-/*
- * return TRUE if line 'lnum' is empty or has white chars only.
- */
+/// return true if line 'lnum' is empty or has white chars only.
int linewhite(linenr_T lnum)
{
- char_u *p;
+ char *p;
- p = (char_u *)skipwhite((char *)ml_get(lnum));
+ p = skipwhite(ml_get(lnum));
return *p == NUL;
}
-// Add the search count "[3/19]" to "msgbuf".
-// See update_search_stat() for other arguments.
+/// Add the search count "[3/19]" to "msgbuf".
+/// See update_search_stat() for other arguments.
static void cmdline_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, bool show_top_bot_msg,
- char_u *msgbuf, bool recompute, int maxcount, long timeout)
+ char *msgbuf, bool recompute, int maxcount, long timeout)
{
searchstat_T stat;
update_search_stat(dirc, pos, cursor_pos, &stat, recompute, maxcount,
timeout);
- if (stat.cur > 0) {
- char t[SEARCH_STAT_BUF_LEN];
-
- if (curwin->w_p_rl && *curwin->w_p_rlc == 's') {
- if (stat.incomplete == 1) {
- vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[?/??]");
- } else if (stat.cnt > maxcount && stat.cur > maxcount) {
- vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/>%d]",
- maxcount, maxcount);
- } else if (stat.cnt > maxcount) {
- vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/%d]",
- maxcount, stat.cur);
- } else {
- vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/%d]",
- stat.cnt, stat.cur);
- }
+ if (stat.cur <= 0) {
+ return;
+ }
+
+ char t[SEARCH_STAT_BUF_LEN];
+
+ if (curwin->w_p_rl && *curwin->w_p_rlc == 's') {
+ if (stat.incomplete == 1) {
+ vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[?/??]");
+ } else if (stat.cnt > maxcount && stat.cur > maxcount) {
+ vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/>%d]",
+ maxcount, maxcount);
+ } else if (stat.cnt > maxcount) {
+ vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/%d]",
+ maxcount, stat.cur);
} else {
- if (stat.incomplete == 1) {
- vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[?/??]");
- } else if (stat.cnt > maxcount && stat.cur > maxcount) {
- vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/>%d]",
- maxcount, maxcount);
- } else if (stat.cnt > maxcount) {
- vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/>%d]",
- stat.cur, maxcount);
- } else {
- vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/%d]",
- stat.cur, stat.cnt);
- }
+ vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/%d]",
+ stat.cnt, stat.cur);
}
-
- size_t len = strlen(t);
- if (show_top_bot_msg && len + 2 < SEARCH_STAT_BUF_LEN) {
- memmove(t + 2, t, len);
- t[0] = 'W';
- t[1] = ' ';
- len += 2;
+ } else {
+ if (stat.incomplete == 1) {
+ vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[?/??]");
+ } else if (stat.cnt > maxcount && stat.cur > maxcount) {
+ vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/>%d]",
+ maxcount, maxcount);
+ } else if (stat.cnt > maxcount) {
+ vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/>%d]",
+ stat.cur, maxcount);
+ } else {
+ vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/%d]",
+ stat.cur, stat.cnt);
}
+ }
- memmove(msgbuf + STRLEN(msgbuf) - len, t, len);
- if (dirc == '?' && stat.cur == maxcount + 1) {
- stat.cur = -1;
- }
+ size_t len = strlen(t);
+ if (show_top_bot_msg && len + 2 < SEARCH_STAT_BUF_LEN) {
+ memmove(t + 2, t, len);
+ t[0] = 'W';
+ t[1] = ' ';
+ len += 2;
+ }
- // keep the message even after redraw, but don't put in history
- msg_hist_off = true;
- msg_ext_set_kind("search_count");
- give_warning((char *)msgbuf, false);
- msg_hist_off = false;
+ memmove(msgbuf + strlen(msgbuf) - len, t, len);
+ if (dirc == '?' && stat.cur == maxcount + 1) {
+ stat.cur = -1;
}
+
+ // 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);
+ msg_hist_off = false;
}
// Add the search count information to "stat".
@@ -4547,7 +2675,7 @@ static void update_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, searchst
static int incomplete = 0;
static int last_maxcount = SEARCH_STAT_DEF_MAX_COUNT;
static int chgtick = 0;
- static char_u *lastpat = NULL;
+ static char *lastpat = NULL;
static buf_T *lbuf = NULL;
proftime_T start;
@@ -4571,8 +2699,8 @@ static void update_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, searchst
// XXX: above comment should be "no MB_STRCMP function" ?
if (!(chgtick == buf_get_changedtick(curbuf)
&& lastpat != NULL // suppress clang/NULL passed as nonnull parameter
- && STRNICMP(lastpat, spats[last_idx].pat, STRLEN(lastpat)) == 0
- && STRLEN(lastpat) == STRLEN(spats[last_idx].pat)
+ && STRNICMP(lastpat, spats[last_idx].pat, strlen(lastpat)) == 0
+ && strlen(lastpat) == strlen(spats[last_idx].pat)
&& equalpos(lastpos, *cursor_pos)
&& lbuf == curbuf)
|| wraparound || cur < 0 || (maxcount > 0 && cur > maxcount)
@@ -4622,7 +2750,7 @@ static void update_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, searchst
}
if (done_search) {
xfree(lastpat);
- lastpat = vim_strsave(spats[last_idx].pat);
+ lastpat = xstrdup(spats[last_idx].pat);
chgtick = (int)buf_get_changedtick(curbuf);
lbuf = curbuf;
lastpos = p;
@@ -4637,10 +2765,10 @@ static void update_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, searchst
}
// "searchcount()" function
-void f_searchcount(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_searchcount(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
pos_T pos = curwin->w_cursor;
- char_u *pattern = NULL;
+ char *pattern = NULL;
int maxcount = SEARCH_STAT_DEF_MAX_COUNT;
long timeout = SEARCH_STAT_DEF_TIMEOUT;
bool recompute = true;
@@ -4684,9 +2812,9 @@ void f_searchcount(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
}
- di = tv_dict_find(dict, (const char *)"pattern", -1);
+ di = tv_dict_find(dict, "pattern", -1);
if (di != NULL) {
- pattern = (char_u *)tv_get_string_chk(&di->di_tv);
+ pattern = (char *)tv_get_string_chk(&di->di_tv);
if (pattern == NULL) {
return;
}
@@ -4732,7 +2860,7 @@ void f_searchcount(typval_T *argvars, typval_T *rettv, FunPtr fptr)
goto the_end;
}
xfree(spats[last_idx].pat);
- spats[last_idx].pat = vim_strsave(pattern);
+ spats[last_idx].pat = xstrdup(pattern);
}
if (spats[last_idx].pat == NULL || *spats[last_idx].pat == NUL) {
goto the_end; // the previous pattern was never defined
@@ -4830,8 +2958,8 @@ typedef struct {
#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,
+/// are in "matches".
+static int fuzzy_match_compute_score(const char *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
{
@@ -4868,14 +2996,14 @@ static int fuzzy_match_compute_score(const char_u *const str, const int strSz,
// Check for bonuses based on neighbor character value
if (currIdx > 0) {
// Camel case
- const char_u *p = str;
- int neighbor;
+ const char *p = str;
+ int neighbor = ' ';
for (uint32_t sidx = 0; sidx < currIdx; sidx++) {
- neighbor = utf_ptr2char((char *)p);
+ neighbor = utf_ptr2char(p);
MB_PTR_ADV(p);
}
- const int curr = utf_ptr2char((char *)p);
+ const int curr = utf_ptr2char(p);
if (mb_islower(neighbor) && mb_isupper(curr)) {
score += CAMEL_BONUS;
@@ -4895,13 +3023,12 @@ static int fuzzy_match_compute_score(const char_u *const str, const int strSz,
return score;
}
-/// Perform a recursive search for fuzzy matching 'fuzpat' in 'str'.
+/// 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)
+static int fuzzy_match_recursive(const char *fuzpat, const char *str, uint32_t strIdx,
+ int *const outScore, const char *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
@@ -4942,7 +3069,7 @@ static int fuzzy_match_recursive(const char_u *fuzpat, const char_u *str, uint32
// 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);
+ const char *const next_char = (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,
@@ -4996,11 +3123,11 @@ static int fuzzy_match_recursive(const char_u *fuzpat, const char_u *str, uint32
/// 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,
+/// @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 *const str, const char *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
+ FUNC_ATTR_NONNULL_ALL
{
const int len = mb_charlen(str);
bool complete = false;
@@ -5008,22 +3135,22 @@ bool fuzzy_match(char_u *const str, const char_u *const pat_arg, const bool matc
*outScore = 0;
- char_u *const save_pat = vim_strsave(pat_arg);
- char_u *pat = save_pat;
- char_u *p = pat;
+ char *const save_pat = xstrdup(pat_arg);
+ char *pat = save_pat;
+ char *p = pat;
- // Try matching each word in 'pat_arg' in 'str'
+ // 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);
+ p = skipwhite(p);
if (*p == NUL) {
break;
}
pat = p;
- while (*p != NUL && !ascii_iswhite(utf_ptr2char((char *)p))) {
+ while (*p != NUL && !ascii_iswhite(utf_ptr2char(p))) {
MB_PTR_ADV(p);
}
if (*p == NUL) { // processed all the words
@@ -5035,7 +3162,8 @@ bool fuzzy_match(char_u *const str, const char_u *const pat_arg, const bool matc
int score = 0;
int recursionCount = 0;
const int matchCount
- = fuzzy_match_recursive(pat, str, 0, &score, str, len, NULL, matches + numMatches,
+ = fuzzy_match_recursive(pat, str, 0, &score, str, len, NULL,
+ matches + numMatches,
maxMatches - numMatches, 0, &recursionCount);
if (matchCount == 0) {
numMatches = 0;
@@ -5071,17 +3199,17 @@ static int fuzzy_match_item_compare(const void *const s1, const void *const s2)
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
+/// 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'
+/// 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,
+static void fuzzy_match_in_list(list_T *const l, char *const str, const bool matchseq,
+ const char *const key, Callback *const item_cb,
const bool retmatchpos, list_T *const fmatchlist,
const long max_matches)
FUNC_ATTR_NONNULL_ARG(2, 5, 7)
@@ -5104,17 +3232,17 @@ static void fuzzy_match_in_list(list_T *const l, char_u *const str, const bool m
break;
}
- char_u *itemstr = NULL;
+ char *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;
+ itemstr = 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);
+ itemstr = tv_dict_get_string(tv->vval.v_dict, (const char *)key, false);
} else {
typval_T argv[2];
@@ -5125,7 +3253,7 @@ static void fuzzy_match_in_list(list_T *const l, char_u *const str, const bool m
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;
+ itemstr = rettv.vval.v_string;
}
}
tv_dict_unref(tv->vval.v_dict);
@@ -5140,13 +3268,13 @@ static void fuzzy_match_in_list(list_T *const l, char_u *const str, const bool m
items[match_count].score = score;
// Copy the list of matching positions in itemstr to a list, if
- // 'retmatchpos' is set.
+ // "retmatchpos" is set.
if (retmatchpos) {
items[match_count].lmatchpos = tv_list_alloc(kListLenMayKnow);
int j = 0;
- const char_u *p = str;
+ const char *p = (char *)str;
while (*p != NUL) {
- if (!ascii_iswhite(utf_ptr2char((char *)p)) || matchseq) {
+ if (!ascii_iswhite(utf_ptr2char(p)) || matchseq) {
tv_list_append_number(items[match_count].lmatchpos, matches[j]);
j++;
}
@@ -5214,8 +3342,8 @@ static void fuzzy_match_in_list(list_T *const l, char_u *const str, const bool m
xfree(items);
}
-/// Do fuzzy matching. Returns the list of matched strings in 'rettv'.
-/// If 'retmatchpos' is true, also returns the matching character positions.
+/// 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
@@ -5231,7 +3359,7 @@ static void do_fuzzymatch(const typval_T *const argvars, typval_T *const rettv,
}
Callback cb = CALLBACK_NONE;
- const char_u *key = NULL;
+ const char *key = NULL;
bool matchseq = false;
long max_matches = 0;
if (argvars[2].v_type != VAR_UNKNOWN) {
@@ -5250,7 +3378,7 @@ static void do_fuzzymatch(const typval_T *const argvars, typval_T *const rettv,
semsg(_(e_invarg2), tv_get_string(&di->di_tv));
return;
}
- key = (const char_u *)tv_get_string(&di->di_tv);
+ key = tv_get_string(&di->di_tv);
} else if (!tv_dict_get_callback(d, "text_cb", -1, &cb)) {
semsg(_(e_invargval), "text_cb");
return;
@@ -5281,35 +3409,139 @@ static void do_fuzzymatch(const typval_T *const argvars, typval_T *const rettv,
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,
+ fuzzy_match_in_list(argvars[0].vval.v_list,
+ (char *)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)
+void f_matchfuzzy(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
do_fuzzymatch(argvars, rettv, false);
}
/// "matchfuzzypos()" function
-void f_matchfuzzypos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_matchfuzzypos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
do_fuzzymatch(argvars, rettv, true);
}
+/// Same as fuzzy_match_item_compare() except for use with a string match
+static int fuzzy_match_str_compare(const void *const s1, const void *const s2)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE
+{
+ const int v1 = ((fuzmatch_str_T *)s1)->score;
+ const int v2 = ((fuzmatch_str_T *)s2)->score;
+ const int idx1 = ((fuzmatch_str_T *)s1)->idx;
+ const int idx2 = ((fuzmatch_str_T *)s2)->idx;
+
+ return v1 == v2 ? (idx1 - idx2) : v1 > v2 ? -1 : 1;
+}
+
+/// Sort fuzzy matches by score
+static void fuzzy_match_str_sort(fuzmatch_str_T *const fm, const int sz)
+ FUNC_ATTR_NONNULL_ALL
+{
+ // Sort the list by the descending order of the match score
+ qsort(fm, (size_t)sz, sizeof(fuzmatch_str_T), fuzzy_match_str_compare);
+}
+
+/// Same as fuzzy_match_item_compare() except for use with a function name
+/// string match. <SNR> functions should be sorted to the end.
+static int fuzzy_match_func_compare(const void *const s1, const void *const s2)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE
+{
+ const int v1 = ((fuzmatch_str_T *)s1)->score;
+ const int v2 = ((fuzmatch_str_T *)s2)->score;
+ const int idx1 = ((fuzmatch_str_T *)s1)->idx;
+ const int idx2 = ((fuzmatch_str_T *)s2)->idx;
+ const char *const str1 = ((fuzmatch_str_T *)s1)->str;
+ const char *const str2 = ((fuzmatch_str_T *)s2)->str;
+
+ if (*str1 != '<' && *str2 == '<') {
+ return -1;
+ }
+ if (*str1 == '<' && *str2 != '<') {
+ return 1;
+ }
+ return v1 == v2 ? (idx1 - idx2) : v1 > v2 ? -1 : 1;
+}
+
+/// Sort fuzzy matches of function names by score.
+/// <SNR> functions should be sorted to the end.
+static void fuzzy_match_func_sort(fuzmatch_str_T *const fm, const int sz)
+ FUNC_ATTR_NONNULL_ALL
+{
+ // Sort the list by the descending order of the match score
+ qsort(fm, (size_t)sz, sizeof(fuzmatch_str_T), fuzzy_match_func_compare);
+}
+
+/// Fuzzy match "pat" in "str".
+/// @returns 0 if there is no match. Otherwise, returns the match score.
+int fuzzy_match_str(char *const str, const char *const pat)
+ FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ if (str == NULL || pat == NULL) {
+ return 0;
+ }
+
+ int score = 0;
+ uint32_t matchpos[MAX_FUZZY_MATCHES];
+ fuzzy_match(str, pat, true, &score, matchpos, sizeof(matchpos) / sizeof(matchpos[0]));
+
+ return score;
+}
+
+/// Copy a list of fuzzy matches into a string list after sorting the matches by
+/// the fuzzy score. Frees the memory allocated for "fuzmatch".
+void fuzzymatches_to_strmatches(fuzmatch_str_T *const fuzmatch, char ***const matches,
+ const int count, const bool funcsort)
+ FUNC_ATTR_NONNULL_ARG(2)
+{
+ if (count <= 0) {
+ return;
+ }
+
+ *matches = xmalloc((size_t)count * sizeof(char *));
+
+ // Sort the list by the descending order of the match score
+ if (funcsort) {
+ fuzzy_match_func_sort(fuzmatch, count);
+ } else {
+ fuzzy_match_str_sort(fuzmatch, count);
+ }
+
+ for (int i = 0; i < count; i++) {
+ (*matches)[i] = fuzmatch[i].str;
+ }
+ xfree(fuzmatch);
+}
+
+/// Free a list of fuzzy string matches.
+void fuzmatch_str_free(fuzmatch_str_T *const fuzmatch, int count)
+{
+ if (count <= 0 || fuzmatch == NULL) {
+ return;
+ }
+ while (count--) {
+ xfree(fuzmatch[count].str);
+ }
+ xfree(fuzmatch);
+}
+
/// 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)
+static char *get_line_and_copy(linenr_T lnum, char *buf)
{
- char_u *line = ml_get(lnum);
- STRLCPY(buf, line, LSIZE);
+ char *line = ml_get(lnum);
+ xstrlcpy(buf, line, LSIZE);
return buf;
}
/// Find identifiers or defines in included files.
-/// If p_ic && (compl_cont_status & CONT_SOL) then ptr must be in lowercase.
+/// If p_ic && compl_status_sol() then ptr must be in lowercase.
///
/// @param ptr pointer to search pattern
/// @param dir direction of expansion
@@ -5320,7 +3552,7 @@ static char_u *get_line_and_copy(linenr_T lnum, char_u *buf)
/// @param action What to do when we find it
/// @param start_lnum first line to start searching
/// @param end_lnum last line for searching
-void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bool skip_comments,
+void find_pattern_in_path(char *ptr, Direction dir, size_t len, bool whole, bool skip_comments,
int type, long count, int action, linenr_T start_lnum, linenr_T end_lnum)
{
SearchedFile *files; // Stack of included files
@@ -5328,19 +3560,19 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
int max_path_depth = 50;
long match_count = 1;
- char_u *pat;
- char_u *new_fname;
- char_u *curr_fname = (char_u *)curbuf->b_fname;
- char_u *prev_fname = NULL;
+ char *pat;
+ char *new_fname;
+ char *curr_fname = curbuf->b_fname;
+ char *prev_fname = NULL;
linenr_T lnum;
int depth;
int depth_displayed; // For type==CHECK_PATH
int old_files;
int already_searched;
- char_u *file_line;
- char_u *line;
- char_u *p;
- char_u save_char;
+ char *file_line;
+ char *line;
+ char *p;
+ char save_char;
bool define_matched;
regmatch_T regmatch;
regmatch_T incl_regmatch;
@@ -5349,9 +3581,8 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
bool did_show = false;
bool found = false;
int i;
- char_u *already = NULL;
- char_u *startp = NULL;
- char_u *inc_opt = NULL;
+ char *already = NULL;
+ char *startp = NULL;
win_T *curwin_save = NULL;
const int l_g_do_tagpreview = g_do_tagpreview;
@@ -5362,36 +3593,37 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
file_line = xmalloc(LSIZE);
if (type != CHECK_PATH && type != FIND_DEFINE
- // when CONT_SOL is set compare "ptr" with the beginning of the line
- // is faster than quote_meta/regcomp/regexec "ptr" -- Acevedo
- && !(compl_cont_status & CONT_SOL)) {
- pat = xmalloc(len + 5);
+ // when CONT_SOL is set compare "ptr" with the beginning of the
+ // line is faster than quote_meta/regcomp/regexec "ptr" -- Acevedo
+ && !compl_status_sol()) {
+ size_t patlen = len + 5;
+ pat = xmalloc(patlen);
assert(len <= INT_MAX);
- sprintf((char *)pat, whole ? "\\<%.*s\\>" : "%.*s", (int)len, ptr);
+ snprintf(pat, patlen, 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((char *)pat, p_magic ? RE_MAGIC : 0);
+ regmatch.regprog = vim_regcomp(pat, magic_isset() ? RE_MAGIC : 0);
xfree(pat);
if (regmatch.regprog == NULL) {
goto fpip_end;
}
}
- inc_opt = (*curbuf->b_p_inc == NUL) ? p_inc : curbuf->b_p_inc;
+ char *inc_opt = (*curbuf->b_p_inc == NUL) ? p_inc : curbuf->b_p_inc;
if (*inc_opt != NUL) {
- incl_regmatch.regprog = vim_regcomp((char *)inc_opt, p_magic ? RE_MAGIC : 0);
+ incl_regmatch.regprog = vim_regcomp(inc_opt, magic_isset() ? RE_MAGIC : 0);
if (incl_regmatch.regprog == NULL) {
goto fpip_end;
}
- incl_regmatch.rm_ic = FALSE; // don't ignore case in incl. pat.
+ incl_regmatch.rm_ic = false; // don't ignore case in incl. pat.
}
if (type == FIND_DEFINE && (*curbuf->b_p_def != NUL || *p_def != NUL)) {
def_regmatch.regprog = vim_regcomp(*curbuf->b_p_def == NUL
- ? (char *)p_def : (char *)curbuf->b_p_def,
- p_magic ? RE_MAGIC : 0);
+ ? p_def : curbuf->b_p_def,
+ magic_isset() ? RE_MAGIC : 0);
if (def_regmatch.regprog == NULL) {
goto fpip_end;
}
- def_regmatch.rm_ic = FALSE; // don't ignore case in define pat.
+ def_regmatch.rm_ic = false; // don't ignore case in define pat.
}
files = xcalloc((size_t)max_path_depth, sizeof(SearchedFile));
old_files = max_path_depth;
@@ -5408,11 +3640,11 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
for (;;) {
if (incl_regmatch.regprog != NULL
- && 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;
+ && vim_regexec(&incl_regmatch, line, (colnr_T)0)) {
+ char *p_fname = (curr_fname == curbuf->b_fname)
+ ? curbuf->b_ffname : curr_fname;
- if (inc_opt != NULL && strstr((char *)inc_opt, "\\zs") != NULL) {
+ if (inc_opt != NULL && strstr(inc_opt, "\\zs") != NULL) {
// Use text from '\zs' to '\ze' (or end) of 'include'.
new_fname = find_file_name_in_path(incl_regmatch.startp[0],
(size_t)(incl_regmatch.endp[0]
@@ -5422,9 +3654,10 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
} else {
// Use text after match with 'include'.
new_fname = file_name_in_line(incl_regmatch.endp[0], 0,
- FNAME_EXP|FNAME_INCL|FNAME_REL, 1L, p_fname, NULL);
+ FNAME_EXP|FNAME_INCL|FNAME_REL, 1L, p_fname,
+ NULL);
}
- already_searched = FALSE;
+ already_searched = false;
if (new_fname != NULL) {
// Check whether we have already searched in this file
for (i = 0;; i++) {
@@ -5434,7 +3667,7 @@ 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((char *)new_fname, (char *)files[i].name, true,
+ if (path_full_compare(new_fname, files[i].name, true,
true) & kEqualFiles) {
if (type != CHECK_PATH
&& action == ACTION_SHOW_ALL && files[i].matched) {
@@ -5484,12 +3717,10 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
// 'includeexpr' is set.
msg_outtrans_attr(new_fname, HL_ATTR(HLF_D));
} else {
- /*
- * Isolate the file name.
- * Include the surrounding "" or <> if present.
- */
+ // Isolate the file name.
+ // Include the surrounding "" or <> if present.
if (inc_opt != NULL
- && strstr((char *)inc_opt, "\\zs") != NULL) {
+ && strstr(inc_opt, "\\zs") != NULL) {
// pattern contains \zs, use the match
p = incl_regmatch.startp[0];
i = (int)(incl_regmatch.endp[0]
@@ -5497,14 +3728,14 @@ 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((uint8_t)(*p)); p++) {}
+ for (i = 0; vim_isfilec((uint8_t)p[i]); i++) {}
}
if (i == 0) {
// Nothing found, use the rest of the line.
p = incl_regmatch.endp[0];
- i = (int)STRLEN(p);
+ i = (int)strlen(p);
} else if (p > line) {
// Avoid checking before the start of the line, can
// happen if \zs appears in the regexp.
@@ -5530,7 +3761,6 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
}
}
}
- ui_flush(); // output each line directly
}
if (new_fname != NULL) {
@@ -5544,7 +3774,7 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
bigger[i].fp = NULL;
bigger[i].name = NULL;
bigger[i].lnum = 0;
- bigger[i].matched = FALSE;
+ bigger[i].matched = false;
}
for (i = old_files; i < max_path_depth; i++) {
bigger[i + max_path_depth] = files[i];
@@ -5554,7 +3784,7 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
xfree(files);
files = bigger;
}
- if ((files[depth + 1].fp = os_fopen((char *)new_fname, "r")) == NULL) {
+ if ((files[depth + 1].fp = os_fopen(new_fname, "r")) == NULL) {
xfree(new_fname);
} else {
if (++depth == old_files) {
@@ -5565,60 +3795,54 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
}
files[depth].name = curr_fname = new_fname;
files[depth].lnum = 0;
- files[depth].matched = FALSE;
+ files[depth].matched = false;
if (action == ACTION_EXPAND) {
msg_hist_off = true; // reset in msg_trunc_attr()
- vim_snprintf((char *)IObuff, IOSIZE,
+ vim_snprintf(IObuff, IOSIZE,
_("Scanning included file: %s"),
- (char *)new_fname);
- msg_trunc_attr((char *)IObuff, true, HL_ATTR(HLF_R));
+ new_fname);
+ msg_trunc_attr(IObuff, true, HL_ATTR(HLF_R));
} else if (p_verbose >= 5) {
verbose_enter();
- smsg(_("Searching included file %s"),
- (char *)new_fname);
+ smsg(_("Searching included file %s"), new_fname);
verbose_leave();
}
}
}
} else {
- /*
- * Check if the line is a define (type == FIND_DEFINE)
- */
+ // Check if the line is a define (type == FIND_DEFINE)
p = line;
search_line:
define_matched = false;
if (def_regmatch.regprog != NULL
- && vim_regexec(&def_regmatch, (char *)line, (colnr_T)0)) {
+ && 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.
p = def_regmatch.endp[0];
- while (*p && !vim_iswordc(*p)) {
+ while (*p && !vim_iswordc((uint8_t)(*p))) {
p++;
}
define_matched = true;
}
- /*
- * Look for a match. Don't do this if we are looking for a
- * define and this line didn't match define_prog above.
- */
+ // Look for a match. Don't do this if we are looking for a
+ // define and this line didn't match define_prog above.
if (def_regmatch.regprog == NULL || define_matched) {
- if (define_matched
- || (compl_cont_status & CONT_SOL)) {
+ if (define_matched || compl_status_sol()) {
// compare the first "len" chars from "ptr"
- startp = (char_u *)skipwhite((char *)p);
+ startp = skipwhite(p);
if (p_ic) {
matched = !mb_strnicmp(startp, ptr, len);
} else {
- matched = !STRNCMP(startp, ptr, len);
+ matched = !strncmp(startp, ptr, len);
}
if (matched && define_matched && whole
- && vim_iswordc(startp[len])) {
+ && vim_iswordc((uint8_t)startp[len])) {
matched = false;
}
} else if (regmatch.regprog != NULL
- && vim_regexec(&regmatch, (char *)line, (colnr_T)(p - line))) {
+ && vim_regexec(&regmatch, line, (colnr_T)(p - line))) {
matched = true;
startp = regmatch.startp[0];
// Check if the line is not a comment line (unless we are
@@ -5626,21 +3850,19 @@ search_line:
// is not considered to be a comment line.
if (skip_comments) {
if ((*line != '#'
- || STRNCMP(skipwhite((char *)line + 1), "define", 6) != 0)
- && get_leader_len((char *)line, NULL, false, true)) {
+ || strncmp(skipwhite(line + 1), "define", 6) != 0)
+ && get_leader_len(line, NULL, false, true)) {
matched = false;
}
- /*
- * Also check for a "/ *" or "/ /" before the match.
- * Skips lines like "int backwards; / * normal index
- * * /" when looking for "normal".
- * Note: Doesn't skip "/ *" in comments.
- */
- p = (char_u *)skipwhite((char *)line);
+ // Also check for a "/ *" or "/ /" before the match.
+ // Skips lines like "int backwards; / * normal index
+ // * /" when looking for "normal".
+ // Note: Doesn't skip "/ *" in comments.
+ p = skipwhite(line);
if (matched
|| (p[0] == '/' && p[1] == '*') || p[0] == '*') {
- for (p = line; *p && p < startp; ++p) {
+ for (p = line; *p && p < startp; p++) {
if (matched
&& p[0] == '/'
&& (p[1] == '*' || p[1] == '/')) {
@@ -5664,15 +3886,14 @@ search_line:
if (matched) {
if (action == ACTION_EXPAND) {
bool cont_s_ipos = false;
- char_u *aux;
if (depth == -1 && lnum == curwin->w_cursor.lnum) {
break;
}
found = true;
- aux = p = startp;
- if (compl_cont_status & CONT_ADDING) {
- p += compl_length;
+ char *aux = p = startp;
+ if (compl_status_adding()) {
+ p += ins_compl_len();
if (vim_iswordp(p)) {
goto exit_matched;
}
@@ -5681,9 +3902,9 @@ search_line:
p = find_word_end(p);
i = (int)(p - aux);
- if ((compl_cont_status & CONT_ADDING) && i == compl_length) {
- // IOSIZE > compl_length, so the STRNCPY works
- STRNCPY(IObuff, aux, i);
+ if (compl_status_adding() && i == ins_compl_len()) {
+ // IOSIZE > compl_length, so the strncpy works
+ strncpy(IObuff, aux, (size_t)i); // NOLINT(runtime/printf)
// Get the next line: when "depth" < 0 from the current
// buffer, otherwise from the included file. Jump to
@@ -5701,7 +3922,7 @@ search_line:
// we read a line, set "already" to check this "line" later
// if depth >= 0 we'll increase files[depth].lnum far
// below -- Acevedo
- already = aux = p = (char_u *)skipwhite((char *)line);
+ already = aux = p = skipwhite(line);
p = find_word_start(p);
p = find_word_end(p);
if (p > aux) {
@@ -5721,20 +3942,20 @@ search_line:
if (p - aux >= IOSIZE - i) {
p = aux + IOSIZE - i - 1;
}
- STRNCPY(IObuff + i, aux, p - aux);
+ strncpy(IObuff + i, aux, (size_t)(p - aux)); // NOLINT(runtime/printf)
i += (int)(p - aux);
cont_s_ipos = true;
}
IObuff[i] = NUL;
aux = IObuff;
- if (i == compl_length) {
+ if (i == ins_compl_len()) {
goto exit_matched;
}
}
const int add_r = ins_compl_add_infercase(aux, i, p_ic,
- curr_fname == (char_u *)curbuf->b_fname
+ curr_fname == curbuf->b_fname
? NULL : curr_fname,
dir, cont_s_ipos);
if (add_r == OK) {
@@ -5820,14 +4041,14 @@ search_line:
}
if (action != ACTION_SHOW) {
curwin->w_cursor.col = (colnr_T)(startp - line);
- curwin->w_set_curswant = TRUE;
+ curwin->w_set_curswant = true;
}
if (l_g_do_tagpreview != 0
&& curwin != curwin_save && win_valid(curwin_save)) {
// Return cursor to where we were
validate_cursor();
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
win_enter(curwin_save, true);
}
break;
@@ -5838,9 +4059,9 @@ exit_matched:
// are not at the end of it already
if (def_regmatch.regprog == NULL
&& action == ACTION_EXPAND
- && !(compl_cont_status & CONT_SOL)
+ && !compl_status_sol()
&& *startp != NUL
- && *(p = startp + utfc_ptr2len((char *)startp)) != NUL) {
+ && *(p = startp + utfc_ptr2len(startp)) != NUL) {
goto search_line;
}
}
@@ -5852,11 +4073,9 @@ exit_matched:
break;
}
- /*
- * Read the next line. When reading an included file and encountering
- * end-of-file, close the file and continue in the file that included
- * it.
- */
+ // Read the next line. When reading an included file and encountering
+ // end-of-file, close the file and continue in the file that included
+ // it.
while (depth >= 0 && !already
&& vim_fgets(line = file_line, LSIZE, files[depth].fp)) {
fclose(files[depth].fp);
@@ -5864,7 +4083,7 @@ exit_matched:
files[old_files].name = files[depth].name;
files[old_files].matched = files[depth].matched;
depth--;
- curr_fname = (depth == -1) ? (char_u *)curbuf->b_fname
+ curr_fname = (depth == -1) ? curbuf->b_fname
: files[depth].name;
if (depth < depth_displayed) {
depth_displayed = depth;
@@ -5873,7 +4092,7 @@ exit_matched:
if (depth >= 0) { // we could read the line
files[depth].lnum++;
// Remove any CR and LF from the line.
- i = (int)STRLEN(line);
+ i = (int)strlen(line);
if (i > 0 && line[i - 1] == '\n') {
line[--i] = NUL;
}
@@ -5929,11 +4148,11 @@ fpip_end:
vim_regfree(def_regmatch.regprog);
}
-static void show_pat_in_path(char_u *line, int type, bool did_show, int action, FILE *fp,
+static void show_pat_in_path(char *line, int type, bool did_show, int action, FILE *fp,
linenr_T *lnum, long count)
FUNC_ATTR_NONNULL_ARG(1, 6)
{
- char_u *p;
+ char *p;
if (did_show) {
msg_putchar('\n'); // cursor below last one
@@ -5944,7 +4163,7 @@ static void show_pat_in_path(char_u *line, int type, bool did_show, int action,
return;
}
for (;;) {
- p = line + STRLEN(line) - 1;
+ p = line + strlen(line) - 1;
if (fp != NULL) {
// We used fgets(), so get rid of newline at end
if (p >= line && *p == '\n') {
@@ -5956,15 +4175,14 @@ static void show_pat_in_path(char_u *line, int type, bool did_show, int action,
*(p + 1) = NUL;
}
if (action == ACTION_SHOW_ALL) {
- snprintf((char *)IObuff, IOSIZE, "%3ld: ", count); // Show match nr.
+ snprintf(IObuff, IOSIZE, "%3ld: ", count); // Show match nr.
msg_puts((const char *)IObuff);
- snprintf((char *)IObuff, IOSIZE, "%4" PRIdLINENR, *lnum); // Show line nr.
+ snprintf(IObuff, IOSIZE, "%4" PRIdLINENR, *lnum); // Show line nr.
// Highlight line numbers.
msg_puts_attr((const char *)IObuff, HL_ATTR(HLF_N));
msg_puts(" ");
}
- msg_prt_line(line, FALSE);
- ui_flush(); // show one line at a time
+ msg_prt_line(line, false);
// Definition continues until line that doesn't end with '\'
if (got_int || type != FIND_DEFINE || p < line || *p != '\\') {
@@ -5975,7 +4193,7 @@ static void show_pat_in_path(char_u *line, int type, bool did_show, int action,
if (vim_fgets(line, LSIZE, fp)) { // end of file
break;
}
- ++*lnum;
+ (*lnum)++;
} else {
if (++*lnum > curbuf->b_ml.ml_line_count) {
break;
diff --git a/src/nvim/search.h b/src/nvim/search.h
index ff843bb59e..2f140ba840 100644
--- a/src/nvim/search.h
+++ b/src/nvim/search.h
@@ -6,8 +6,11 @@
#include "nvim/buffer_defs.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/normal.h"
#include "nvim/os/time.h"
+#include "nvim/pos.h"
+#include "nvim/types.h"
#include "nvim/vim.h"
// Values for the find_pattern_in_path() function args 'type' and 'action':
@@ -70,7 +73,7 @@ typedef struct soffset {
/// Structure containing last search pattern and its attributes.
typedef struct spat {
- char_u *pat; ///< The pattern (in allocated memory) or NULL.
+ char *pat; ///< The pattern (in allocated memory) or NULL.
bool magic; ///< Magicness of the pattern.
bool no_scs; ///< No smartcase for this pattern.
Timestamp timestamp; ///< Time of the last change.
@@ -96,6 +99,14 @@ typedef struct searchstat {
int last_maxcount; // the max count of the last search
} searchstat_T;
+/// Fuzzy matched string list item. Used for fuzzy match completion. Items are
+/// usually sorted by "score". The "idx" member is used for stable-sort.
+typedef struct {
+ int idx;
+ char *str;
+ int score;
+} fuzmatch_str_T;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "search.h.generated.h"
#endif
diff --git a/src/nvim/sha256.c b/src/nvim/sha256.c
index 53c9cb7c81..db647f3ecb 100644
--- a/src/nvim/sha256.c
+++ b/src/nvim/sha256.c
@@ -13,11 +13,13 @@
/// Vim specific notes:
/// sha256_self_test() is implicitly called once.
-#include <stddef.h> // for size_t
-#include <stdio.h> // for snprintf().
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
-#include "nvim/sha256.h" // for context_sha256_T
-#include "nvim/vim.h" // for STRCPY()/STRLEN().
+#include "nvim/sha256.h"
+#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "sha256.c.generated.h"
@@ -30,10 +32,10 @@
}
#define PUT_UINT32(n, b, i) { \
- (b)[(i)] = (char_u)((n) >> 24); \
- (b)[(i) + 1] = (char_u)((n) >> 16); \
- (b)[(i) + 2] = (char_u)((n) >> 8); \
- (b)[(i) + 3] = (char_u)((n)); \
+ (b)[(i)] = (uint8_t)((n) >> 24); \
+ (b)[(i) + 1] = (uint8_t)((n) >> 16); \
+ (b)[(i) + 2] = (uint8_t)((n) >> 8); \
+ (b)[(i) + 3] = (uint8_t)((n)); \
}
void sha256_start(context_sha256_T *ctx)
@@ -51,7 +53,7 @@ void sha256_start(context_sha256_T *ctx)
ctx->state[7] = 0x5BE0CD19;
}
-static void sha256_process(context_sha256_T *ctx, const char_u data[SHA256_BUFFER_SIZE])
+static void sha256_process(context_sha256_T *ctx, const uint8_t data[SHA256_BUFFER_SIZE])
{
uint32_t temp1, temp2, W[SHA256_BUFFER_SIZE];
uint32_t A, B, C, D, E, F, G, H;
@@ -178,7 +180,7 @@ static void sha256_process(context_sha256_T *ctx, const char_u data[SHA256_BUFFE
ctx->state[7] += H;
}
-void sha256_update(context_sha256_T *ctx, const char_u *input, size_t length)
+void sha256_update(context_sha256_T *ctx, const uint8_t *input, size_t length)
{
if (length == 0) {
return;
@@ -196,7 +198,7 @@ void sha256_update(context_sha256_T *ctx, const char_u *input, size_t length)
size_t fill = SHA256_BUFFER_SIZE - left;
if (left && (length >= fill)) {
- memcpy((void *)(ctx->buffer + left), (void *)input, fill);
+ memcpy(ctx->buffer + left, input, fill);
sha256_process(ctx, ctx->buffer);
length -= fill;
input += fill;
@@ -210,22 +212,22 @@ void sha256_update(context_sha256_T *ctx, const char_u *input, size_t length)
}
if (length) {
- memcpy((void *)(ctx->buffer + left), (void *)input, length);
+ memcpy(ctx->buffer + left, input, length);
}
}
-static char_u sha256_padding[SHA256_BUFFER_SIZE] = {
+static uint8_t sha256_padding[SHA256_BUFFER_SIZE] = {
0x80, 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, 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
};
-void sha256_finish(context_sha256_T *ctx, char_u digest[SHA256_SUM_SIZE])
+void sha256_finish(context_sha256_T *ctx, uint8_t digest[SHA256_SUM_SIZE])
{
uint32_t last, padn;
uint32_t high, low;
- char_u msglen[8];
+ uint8_t msglen[8];
high = (ctx->total[0] >> 29) | (ctx->total[1] << 3);
low = (ctx->total[0] << 3);
@@ -263,7 +265,7 @@ void sha256_finish(context_sha256_T *ctx, char_u digest[SHA256_SUM_SIZE])
const char *sha256_bytes(const uint8_t *restrict buf, size_t buf_len, const uint8_t *restrict salt,
size_t salt_len)
{
- char_u sha256sum[SHA256_SUM_SIZE];
+ uint8_t sha256sum[SHA256_SUM_SIZE];
static char hexit[SHA256_BUFFER_SIZE + 1]; // buf size + NULL
context_sha256_T ctx;
@@ -307,8 +309,8 @@ bool sha256_self_test(void)
{
char output[SHA256_BUFFER_SIZE + 1]; // buf size + NULL
context_sha256_T ctx;
- char_u buf[1000];
- char_u sha256sum[SHA256_SUM_SIZE];
+ uint8_t buf[1000];
+ uint8_t sha256sum[SHA256_SUM_SIZE];
const char *hexit;
static bool sha256_self_tested = false;
@@ -339,7 +341,7 @@ bool sha256_self_test(void)
}
}
- if (memcmp(output, sha_self_test_vector[i], SHA256_BUFFER_SIZE)) {
+ if (memcmp(output, sha_self_test_vector[i], SHA256_BUFFER_SIZE) != 0) {
failures = true;
output[sizeof(output) - 1] = '\0';
diff --git a/src/nvim/sha256.h b/src/nvim/sha256.h
index b52d300de6..eeb4031509 100644
--- a/src/nvim/sha256.h
+++ b/src/nvim/sha256.h
@@ -2,9 +2,9 @@
#define NVIM_SHA256_H
#include <stddef.h>
-#include <stdint.h> // for uint32_t
+#include <stdint.h>
-#include "nvim/types.h" // for char_u
+#include "nvim/types.h"
#define SHA256_BUFFER_SIZE 64
#define SHA256_SUM_SIZE 32
@@ -12,7 +12,7 @@
typedef struct {
uint32_t total[2];
uint32_t state[8];
- char_u buffer[SHA256_BUFFER_SIZE];
+ uint8_t buffer[SHA256_BUFFER_SIZE];
} context_sha256_T;
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index daa8e99d31..90a01aaf97 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -2,44 +2,53 @@
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include <assert.h>
-#include <errno.h>
#include <inttypes.h>
-#include <msgpack.h>
+#include <msgpack/object.h>
+#include <msgpack/pack.h>
+#include <msgpack/sbuffer.h>
+#include <msgpack/unpack.h>
#include <stdbool.h>
#include <stddef.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/stat.h>
#include <uv.h>
+#include "auto/config.h"
+#include "klib/khash.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
#include "nvim/buffer.h"
-#include "nvim/buffer_defs.h"
#include "nvim/cmdhist.h"
+#include "nvim/eval.h"
#include "nvim/eval/decode.h"
#include "nvim/eval/encode.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_docmd.h"
#include "nvim/fileio.h"
#include "nvim/garray.h"
+#include "nvim/gettext.h"
#include "nvim/globals.h"
-#include "nvim/lib/khash.h"
-#include "nvim/lib/kvec.h"
+#include "nvim/hashtab.h"
#include "nvim/macros.h"
#include "nvim/mark.h"
+#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/msgpack_rpc/helpers.h"
+#include "nvim/normal.h"
#include "nvim/ops.h"
#include "nvim/option.h"
#include "nvim/os/fileio.h"
+#include "nvim/os/fs_defs.h"
#include "nvim/os/os.h"
#include "nvim/os/time.h"
#include "nvim/path.h"
#include "nvim/pos.h"
-#include "nvim/quickfix.h"
#include "nvim/regexp.h"
#include "nvim/search.h"
#include "nvim/shada.h"
@@ -166,7 +175,7 @@ typedef enum {
/// Possible results of shada_write function.
typedef enum {
- kSDWriteSuccessfull, ///< Writing was successful.
+ kSDWriteSuccessful, ///< Writing was successful.
kSDWriteReadNotShada, ///< Writing was successful, but when reading it
///< attempted to read file that did not look like
///< a ShaDa file.
@@ -288,8 +297,6 @@ typedef struct {
} data;
} ShadaEntry;
-struct hm_llist_entry;
-
/// One entry in sized linked list
typedef struct hm_llist_entry {
ShadaEntry data; ///< Entry data.
@@ -454,10 +461,7 @@ static const ShadaEntry sd_default_values[] = {
.additional_data = NULL),
DEF_SDE(Variable, global_var,
.name = NULL,
- .value = {
- .v_type = VAR_UNKNOWN,
- .vval = { .v_string = NULL }
- },
+ .value = { .v_type = VAR_UNKNOWN, .vval = { .v_string = NULL } },
.additional_elements = NULL),
DEF_SDE(GlobalMark, filemark,
.name = '"',
@@ -752,7 +756,7 @@ static int open_shada_file_for_reading(const char *const fname, ShaDaReadDef *sd
return error;
}
- assert(STRCMP(p_enc, "utf-8") == 0);
+ assert(strcmp(p_enc, "utf-8") == 0);
return 0;
}
@@ -882,10 +886,10 @@ static const void *shada_hist_iter(const void *const iter, const uint8_t history
.data = {
.history_item = {
.histtype = history_type,
- .string = (char *)hist_he.hisstr,
+ .string = hist_he.hisstr,
.sep = (char)(history_type == HIST_SEARCH
- ? (char)hist_he.hisstr[STRLEN(hist_he.hisstr) + 1]
- : 0),
+ ? hist_he.hisstr[strlen(hist_he.hisstr) + 1]
+ : 0),
.additional_elements = hist_he.additional_elements,
}
}
@@ -1008,7 +1012,7 @@ static inline void hms_to_he_array(const HistoryMergerState *const hms_p,
HMLL_FORALL(&hms_p->hmll, cur_entry, {
hist->timestamp = cur_entry->data.timestamp;
hist->hisnum = (int)(hist - hist_array) + 1;
- hist->hisstr = (char_u *)cur_entry->data.data.history_item.string;
+ hist->hisstr = cur_entry->data.data.history_item.string;
hist->additional_elements =
cur_entry->data.data.history_item.additional_elements;
hist++;
@@ -1053,7 +1057,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 (path_fnamecmp(fname, buf->b_ffname) == 0) {
kh_val(fname_bufs, k) = buf;
return buf;
}
@@ -1127,34 +1131,35 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags)
const bool get_old_files = (flags & (kShaDaGetOldfiles | kShaDaForceit)
&& (force || tv_list_len(oldfiles_list) == 0));
const bool want_marks = flags & kShaDaWantMarks;
- const unsigned srni_flags = (unsigned)(
- (flags & kShaDaWantInfo
- ? (kSDReadUndisableableData
- | kSDReadRegisters
- | kSDReadGlobalMarks
- | (p_hi ? kSDReadHistory : 0)
- | (find_shada_parameter('!') != NULL
- ? kSDReadVariables
- : 0)
- | (find_shada_parameter('%') != NULL
- && ARGCOUNT == 0
- ? kSDReadBufferList
- : 0))
- : 0)
- | (want_marks && get_shada_parameter('\'') > 0
- ? kSDReadLocalMarks | kSDReadChanges
- : 0)
- | (get_old_files
- ? kSDReadLocalMarks
- : 0));
+ const unsigned srni_flags =
+ (unsigned)(
+ (flags & kShaDaWantInfo
+ ? (kSDReadUndisableableData
+ | kSDReadRegisters
+ | kSDReadGlobalMarks
+ | (p_hi ? kSDReadHistory : 0)
+ | (find_shada_parameter('!') != NULL
+ ? kSDReadVariables
+ : 0)
+ | (find_shada_parameter('%') != NULL
+ && ARGCOUNT == 0
+ ? kSDReadBufferList
+ : 0))
+ : 0)
+ | (want_marks && get_shada_parameter('\'') > 0
+ ? kSDReadLocalMarks | kSDReadChanges
+ : 0)
+ | (get_old_files
+ ? kSDReadLocalMarks
+ : 0));
if (srni_flags == 0) {
// Nothing to do.
return;
}
HistoryMergerState hms[HIST_COUNT];
if (srni_flags & kSDReadHistory) {
- for (uint8_t i = 0; i < HIST_COUNT; i++) {
- hms_init(&hms[i], i, (size_t)p_hi, true, true);
+ for (HistoryType i = 0; i < HIST_COUNT; i++) {
+ hms_init(&hms[i], (uint8_t)i, (size_t)p_hi, true, true);
}
}
ShadaEntry cur_entry;
@@ -1191,17 +1196,18 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags)
case kSDItemSearchPattern:
if (!force) {
SearchPattern pat;
- (cur_entry.data.search_pattern.is_substitute_pattern
- ? &get_substitute_pattern
- : &get_search_pattern)(&pat);
+ if (cur_entry.data.search_pattern.is_substitute_pattern) {
+ get_substitute_pattern(&pat);
+ } else {
+ get_search_pattern(&pat);
+ }
if (pat.pat != NULL && pat.timestamp >= cur_entry.timestamp) {
shada_free_shada_entry(&cur_entry);
break;
}
}
- (cur_entry.data.search_pattern.is_substitute_pattern
- ? &set_substitute_pattern
- : &set_search_pattern)((SearchPattern) {
+
+ SearchPattern spat = (SearchPattern) {
.magic = cur_entry.data.search_pattern.magic,
.no_scs = !cur_entry.data.search_pattern.smartcase,
.off = {
@@ -1210,10 +1216,17 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags)
.end = cur_entry.data.search_pattern.place_cursor_at_end,
.off = cur_entry.data.search_pattern.offset,
},
- .pat = (char_u *)cur_entry.data.search_pattern.pat,
+ .pat = cur_entry.data.search_pattern.pat,
.additional_data = cur_entry.data.search_pattern.additional_data,
.timestamp = cur_entry.timestamp,
- });
+ };
+
+ if (cur_entry.data.search_pattern.is_substitute_pattern) {
+ set_substitute_pattern(spat);
+ } else {
+ set_search_pattern(spat);
+ }
+
if (cur_entry.data.search_pattern.is_last_used) {
set_last_used_pattern(cur_entry.data.search_pattern.is_substitute_pattern);
set_no_hlsearch(!cur_entry.data.search_pattern.highlighted);
@@ -1238,7 +1251,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, false);
+ (void)regtilde(cur_entry.data.sub_string.sub, magic_isset(), false);
// Do not free shada entry: its allocated memory was saved above.
break;
case kSDItemHistoryEntry:
@@ -1313,9 +1326,9 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags)
MERGE_JUMPS(curwin->w_jumplistlen, curwin->w_jumplist, xfmark_T,
fmark.timestamp, fmark.mark, cur_entry,
(buf == NULL
- ? (jl_entry.fname != NULL
- && STRCMP(fm.fname, jl_entry.fname) == 0)
- : fm.fmark.fnum == jl_entry.fmark.fnum),
+ ? (jl_entry.fname != NULL
+ && strcmp(fm.fname, jl_entry.fname) == 0)
+ : fm.fmark.fnum == jl_entry.fmark.fnum),
free_xfmark, SDE_TO_XFMARK, ADJUST_IDX, DUMMY_AFTERFREE);
#undef SDE_TO_XFMARK
#undef ADJUST_IDX
@@ -1327,7 +1340,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags)
case kSDItemBufferList:
for (size_t i = 0; i < cur_entry.data.buffer_list.size; i++) {
char *const sfname =
- (char *)path_try_shorten_fname((char_u *)cur_entry.data.buffer_list.buffers[i].fname);
+ path_try_shorten_fname(cur_entry.data.buffer_list.buffers[i].fname);
buf_T *const buf =
buflist_new(cur_entry.data.buffer_list.buffers[i].fname, sfname, 0, BLN_LISTED);
if (buf != NULL) {
@@ -1410,12 +1423,12 @@ shada_read_main_cycle_end:
// memory for the history string itself and separator character which
// may be assigned right away.
if (srni_flags & kSDReadHistory) {
- for (uint8_t i = 0; i < HIST_COUNT; i++) {
+ for (HistoryType i = 0; i < HIST_COUNT; i++) {
hms_insert_whole_neovim_history(&hms[i]);
clr_history(i);
int *new_hisidx;
int *new_hisnum;
- histentry_T *hist = hist_get_array(i, &new_hisidx, &new_hisnum);
+ histentry_T *hist = hist_get_array((uint8_t)i, &new_hisidx, &new_hisnum);
if (hist != NULL) {
hms_to_he_array(&hms[i], hist, new_hisidx, new_hisnum);
}
@@ -1469,15 +1482,15 @@ static char *shada_filename(const char *file)
if (p_shadafile != NULL && *p_shadafile != NUL) {
file = p_shadafile;
} else {
- if ((file = (char *)find_shada_parameter('n')) == NULL || *file == NUL) {
- file = shada_get_default_file();
+ if ((file = find_shada_parameter('n')) == NULL || *file == NUL) {
+ file = shada_get_default_file();
}
// XXX It used to be one level lower, so that whatever is in
// `p_shadafile` was expanded. I intentionally moved it here
// because various expansions must have already be done by the shell.
// If shell is not performing them then they should be done in main.c
// where arguments are parsed, *not here*.
- expand_env((char_u *)file, &(NameBuff[0]), MAXPATHL);
+ expand_env((char *)file, &(NameBuff[0]), MAXPATHL);
file = (const char *)&(NameBuff[0]);
}
}
@@ -1505,7 +1518,7 @@ static char *shada_filename(const char *file)
/// @param[in] max_kbyte Maximum size of an item in KiB. Zero means no
/// restrictions.
///
-/// @return kSDWriteSuccessfull, kSDWriteFailed or kSDWriteIgnError.
+/// @return kSDWriteSuccessful, kSDWriteFailed or kSDWriteIgnError.
static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, ShadaEntry entry,
const size_t max_kbyte)
FUNC_ATTR_NONNULL_ALL
@@ -1551,6 +1564,18 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, ShadaEntr
(sd_default_values[(entry).type].data.attr == (entry).data.attr)
#define ONE_IF_NOT_DEFAULT(entry, attr) \
((size_t)(!CHECK_DEFAULT(entry, attr)))
+
+#define PACK_BOOL(entry, name, attr) \
+ do { \
+ if (!CHECK_DEFAULT(entry, search_pattern.attr)) { \
+ PACK_STATIC_STR(name); \
+ if (sd_default_values[(entry).type].data.search_pattern.attr) { \
+ msgpack_pack_false(spacker); \
+ } else { \
+ msgpack_pack_true(spacker); \
+ } \
+ } \
+ } while (0)
switch (entry.type) {
case kSDItemMissing:
abort();
@@ -1634,17 +1659,6 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, ShadaEntr
msgpack_pack_map(spacker, map_size);
PACK_STATIC_STR(SEARCH_KEY_PAT);
PACK_BIN(cstr_as_string(entry.data.search_pattern.pat));
-#define PACK_BOOL(entry, name, attr) \
- do { \
- if (!CHECK_DEFAULT(entry, search_pattern.attr)) { \
- PACK_STATIC_STR(name); \
- if (sd_default_values[(entry).type].data.search_pattern.attr) { \
- msgpack_pack_false(spacker); \
- } else { \
- msgpack_pack_true(spacker); \
- } \
- } \
- } while (0)
PACK_BOOL(entry, SEARCH_KEY_MAGIC, magic);
PACK_BOOL(entry, SEARCH_KEY_IS_LAST_USED, is_last_used);
PACK_BOOL(entry, SEARCH_KEY_SMARTCASE, smartcase);
@@ -1688,8 +1702,8 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, ShadaEntr
msgpack_pack_long(spacker, entry.data.filemark.mark.col);
}
assert(entry.type == kSDItemJump || entry.type == kSDItemChange
- ? CHECK_DEFAULT(entry, filemark.name)
- : true);
+ ? CHECK_DEFAULT(entry, filemark.name)
+ : true);
if (!CHECK_DEFAULT(entry, filemark.name)) {
PACK_STATIC_STR(KEY_NAME_CHAR);
msgpack_pack_uint8(spacker, (uint8_t)entry.data.filemark.name);
@@ -1699,15 +1713,14 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, ShadaEntr
break;
}
case kSDItemRegister: {
- const size_t map_size = (size_t)(
- 2 // Register contents and name
+ const size_t map_size = (size_t)(2 // Register contents and name
+ ONE_IF_NOT_DEFAULT(entry, reg.type)
+ ONE_IF_NOT_DEFAULT(entry, reg.width)
+ ONE_IF_NOT_DEFAULT(entry, reg.is_unnamed)
// Additional entries, if any:
+ (size_t)(entry.data.reg.additional_data == NULL
- ? 0
- : entry.data.reg.additional_data->dv_hashtab.ht_used));
+ ? 0
+ : entry.data.reg.additional_data->dv_hashtab.ht_used));
msgpack_pack_map(spacker, map_size);
PACK_STATIC_STR(REG_KEY_CONTENTS);
msgpack_pack_array(spacker, entry.data.reg.contents_size);
@@ -1813,7 +1826,7 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, ShadaEntr
}
msgpack_packer_free(spacker);
msgpack_sbuffer_destroy(&sbuf);
- return kSDWriteSuccessfull;
+ return kSDWriteSuccessful;
shada_pack_entry_error:
msgpack_packer_free(spacker);
msgpack_sbuffer_destroy(&sbuf);
@@ -1833,7 +1846,7 @@ static inline ShaDaWriteResult shada_pack_pfreed_entry(msgpack_packer *const pac
const size_t max_kbyte)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE
{
- ShaDaWriteResult ret = kSDWriteSuccessfull;
+ ShaDaWriteResult ret = kSDWriteSuccessful;
ret = shada_pack_entry(packer, entry.data, max_kbyte);
if (entry.can_free_entry) {
shada_free_shada_entry(&entry.data);
@@ -1952,6 +1965,28 @@ static const char *shada_format_entry(const ShadaEntry entry)
ret[0] = 0;
vim_snprintf(S_LEN(ret), "%s", "[ ] ts=%" PRIu64 " ");
// ^ Space for `can_free_entry`
+#define FORMAT_MARK_ENTRY(entry_name, name_fmt, name_fmt_arg) \
+ do { \
+ typval_T ad_tv = { \
+ .v_type = VAR_DICT, \
+ .vval.v_dict = entry.data.filemark.additional_data \
+ }; \
+ size_t ad_len; \
+ char *const ad = encode_tv2string(&ad_tv, &ad_len); \
+ vim_snprintf_add(S_LEN(ret), \
+ entry_name " {" name_fmt " file=[%zu]\"%.512s\", " \
+ "pos={l=%" PRIdLINENR ",c=%" PRIdCOLNR ",a=%" PRIdCOLNR "}, " \
+ "ad={%p:[%zu]%.64s} }", \
+ name_fmt_arg, \
+ strlen(entry.data.filemark.fname), \
+ entry.data.filemark.fname, \
+ entry.data.filemark.mark.lnum, \
+ entry.data.filemark.mark.col, \
+ entry.data.filemark.mark.coladd, \
+ (void *)entry.data.filemark.additional_data, \
+ ad_len, \
+ ad); \
+ } while (0)
switch (entry.type) {
case kSDItemMissing:
vim_snprintf_add(S_LEN(ret), "Missing");
@@ -1980,28 +2015,6 @@ static const char *shada_format_entry(const ShadaEntry entry)
case kSDItemVariable:
vim_snprintf_add(S_LEN(ret), "Variable { TODO }");
break;
-#define FORMAT_MARK_ENTRY(entry_name, name_fmt, name_fmt_arg) \
- do { \
- typval_T ad_tv = { \
- .v_type = VAR_DICT, \
- .vval.v_dict = entry.data.filemark.additional_data \
- }; \
- size_t ad_len; \
- char *const ad = encode_tv2string(&ad_tv, &ad_len); \
- vim_snprintf_add(S_LEN(ret), \
- entry_name " {" name_fmt " file=[%zu]\"%.512s\", " \
- "pos={l=%" PRIdLINENR ",c=%" PRIdCOLNR ",a=%" PRIdCOLNR "}, " \
- "ad={%p:[%zu]%.64s} }", \
- name_fmt_arg, \
- strlen(entry.data.filemark.fname), \
- entry.data.filemark.fname, \
- entry.data.filemark.mark.lnum, \
- entry.data.filemark.mark.col, \
- entry.data.filemark.mark.coladd, \
- (void *)entry.data.filemark.additional_data, \
- ad_len, \
- ad); \
- } while (0)
case kSDItemGlobalMark:
FORMAT_MARK_ENTRY("GlobalMark", " name='%c',", entry.data.filemark.name);
break;
@@ -2047,9 +2060,35 @@ static inline ShaDaWriteResult shada_read_when_writing(ShaDaReadDef *const sd_re
msgpack_packer *const packer)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
- ShaDaWriteResult ret = kSDWriteSuccessfull;
+ ShaDaWriteResult ret = kSDWriteSuccessful;
ShadaEntry entry;
ShaDaReadResult srni_ret;
+
+#define COMPARE_WITH_ENTRY(wms_entry_, entry) \
+ do { \
+ PossiblyFreedShadaEntry *const wms_entry = (wms_entry_); \
+ if (wms_entry->data.type != kSDItemMissing) { \
+ if (wms_entry->data.timestamp >= (entry).timestamp) { \
+ shada_free_shada_entry(&(entry)); \
+ break; \
+ } \
+ if (wms_entry->can_free_entry) { \
+ shada_free_shada_entry(&wms_entry->data); \
+ } \
+ } \
+ *wms_entry = pfs_entry; \
+ } while (0)
+
+#define FREE_POSSIBLY_FREED_SHADA_ENTRY(entry) \
+ do { \
+ 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) })
+
while ((srni_ret = shada_read_next_item(sd_reader, &entry, srni_flags,
max_kbyte))
!= kSDReadStatusFinished) {
@@ -2067,20 +2106,6 @@ static inline ShaDaWriteResult shada_read_when_writing(ShaDaReadDef *const sd_re
case kSDReadStatusMalformed:
continue;
}
-#define COMPARE_WITH_ENTRY(wms_entry_, entry) \
- do { \
- PossiblyFreedShadaEntry *const wms_entry = (wms_entry_); \
- if (wms_entry->data.type != kSDItemMissing) { \
- if (wms_entry->data.timestamp >= (entry).timestamp) { \
- shada_free_shada_entry(&(entry)); \
- break; \
- } \
- if (wms_entry->can_free_entry) { \
- shada_free_shada_entry(&wms_entry->data); \
- } \
- } \
- *wms_entry = pfs_entry; \
- } while (0)
const PossiblyFreedShadaEntry pfs_entry = {
.can_free_entry = true,
.data = entry,
@@ -2097,8 +2122,8 @@ static inline ShaDaWriteResult shada_read_when_writing(ShaDaReadDef *const sd_re
break;
case kSDItemSearchPattern:
COMPARE_WITH_ENTRY((entry.data.search_pattern.is_substitute_pattern
- ? &wms->sub_search_pattern
- : &wms->search_pattern), entry);
+ ? &wms->sub_search_pattern
+ : &wms->search_pattern), entry);
break;
case kSDItemSubString:
COMPARE_WITH_ENTRY(&wms->replacement, entry);
@@ -2220,14 +2245,6 @@ static inline ShaDaWriteResult shada_read_when_writing(ShaDaReadDef *const sd_re
*wms_entry = pfs_entry;
}
} else {
-#define FREE_POSSIBLY_FREED_SHADA_ENTRY(entry) \
- do { \
- 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) })
#define AFTERFREE_DUMMY(entry)
#define DUMMY_IDX_ADJ(i)
MERGE_JUMPS(filemarks->changes_size, filemarks->changes,
@@ -2361,7 +2378,7 @@ static inline void add_search_pattern(PossiblyFreedShadaEntry *const ret_pse,
.is_substitute_pattern = is_substitute_pattern,
.highlighted = ((is_substitute_pattern ^ search_last_used)
&& search_highlighted),
- .pat = (char *)pat.pat,
+ .pat = pat.pat,
.additional_data = pat.additional_data,
.search_backward = (!is_substitute_pattern && pat.off.dir == '?'),
}
@@ -2482,7 +2499,7 @@ static int hist_type2char(const int type)
static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef *const sd_reader)
FUNC_ATTR_NONNULL_ARG(1)
{
- ShaDaWriteResult ret = kSDWriteSuccessfull;
+ ShaDaWriteResult ret = kSDWriteSuccessful;
int max_kbyte_i = get_shada_parameter('s');
if (max_kbyte_i < 0) {
max_kbyte_i = 10;
@@ -2506,7 +2523,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef
bool dump_history = false;
// Initialize history merger
- for (uint8_t i = 0; i < HIST_COUNT; i++) {
+ for (HistoryType i = 0; i < HIST_COUNT; i++) {
long num_saved = get_shada_parameter(hist_type2char(i));
if (num_saved == -1) {
num_saved = p_hi;
@@ -2514,7 +2531,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef
if (num_saved > 0) {
dump_history = true;
dump_one_history[i] = true;
- hms_init(&wms->hms[i], i, (size_t)num_saved, sd_reader != NULL, false);
+ hms_init(&wms->hms[i], (uint8_t)i, (size_t)num_saved, sd_reader != NULL, false);
} else {
dump_one_history[i] = false;
}
@@ -2642,7 +2659,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef
}
tv_clear(&vartv);
tv_clear(&tgttv);
- if (spe_ret == kSDWriteSuccessfull) {
+ if (spe_ret == kSDWriteSuccessful) {
int kh_ret;
(void)kh_put(strset, &wms->dumped_variables, name, &kh_ret);
}
@@ -2650,8 +2667,6 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef
}
// Initialize jump list
- setpcmark();
- cleanup_jumplist(curwin, false);
wms->jumps_size = shada_init_jumps(wms->jumps, &removable_bufs);
const bool search_highlighted = !(no_hlsearch
@@ -2807,7 +2822,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef
if (sd_reader != NULL) {
const ShaDaWriteResult srww_ret = shada_read_when_writing(sd_reader, srni_flags, max_kbyte, wms,
packer);
- if (srww_ret != kSDWriteSuccessfull) {
+ if (srww_ret != kSDWriteSuccessful) {
ret = srww_ret;
}
}
@@ -3007,10 +3022,9 @@ shada_write_file_open: {}
assert(sd_reader.close != NULL);
sd_reader.close(&sd_reader);
return FAIL;
- } else {
- (*wp)++;
- goto shada_write_file_open;
}
+ (*wp)++;
+ goto shada_write_file_open;
} else {
semsg(_(SERR "System error while opening temporary ShaDa file %s "
"for writing: %s"), tempname, os_strerror(error));
@@ -3023,7 +3037,7 @@ shada_write_file_nomerge: {}
if (tail != fname) {
const char tail_save = *tail;
*tail = NUL;
- if (!os_isdir((char_u *)fname)) {
+ if (!os_isdir(fname)) {
int ret;
char *failed_dir;
if ((ret = os_mkdir_recurse(fname, 0700, &failed_dir)) != 0) {
@@ -3068,37 +3082,42 @@ shada_write_file_nomerge: {}
if (!nomerge) {
sd_reader.close(&sd_reader);
bool did_remove = false;
- if (sw_ret == kSDWriteSuccessfull) {
-#ifdef UNIX
- // For Unix we check the owner of the file. It's not very nice to
- // overwrite a user’s viminfo file after a "su root", with a
- // viminfo file that the user can't read.
+ if (sw_ret == kSDWriteSuccessful) {
FileInfo old_info;
- if (os_fileinfo(fname, &old_info)) {
- if (getuid() == ROOT_UID) {
- if (old_info.stat.st_uid != ROOT_UID
- || old_info.stat.st_gid != getgid()) {
- const uv_uid_t old_uid = (uv_uid_t)old_info.stat.st_uid;
- const uv_gid_t old_gid = (uv_gid_t)old_info.stat.st_gid;
- const int fchown_ret = os_fchown(file_fd(sd_writer.cookie),
- old_uid, old_gid);
- if (fchown_ret != 0) {
- semsg(_(RNERR "Failed setting uid and gid for file %s: %s"),
- tempname, os_strerror(fchown_ret));
- goto shada_write_file_did_not_remove;
- }
+ if (!os_fileinfo(fname, &old_info)
+ || S_ISDIR(old_info.stat.st_mode)
+#ifdef UNIX
+ // For Unix we check the owner of the file. It's not very nice
+ // to overwrite a user's viminfo file after a "su root", with a
+ // viminfo file that the user can't read.
+ || (getuid() != ROOT_UID
+ && !(old_info.stat.st_uid == getuid()
+ ? (old_info.stat.st_mode & 0200)
+ : (old_info.stat.st_gid == getgid()
+ ? (old_info.stat.st_mode & 0020)
+ : (old_info.stat.st_mode & 0002))))
+#endif
+ ) {
+ semsg(_("E137: ShaDa file is not writable: %s"), fname);
+ goto shada_write_file_did_not_remove;
+ }
+#ifdef UNIX
+ if (getuid() == ROOT_UID) {
+ if (old_info.stat.st_uid != ROOT_UID
+ || old_info.stat.st_gid != getgid()) {
+ const uv_uid_t old_uid = (uv_uid_t)old_info.stat.st_uid;
+ const uv_gid_t old_gid = (uv_gid_t)old_info.stat.st_gid;
+ const int fchown_ret = os_fchown(file_fd(sd_writer.cookie),
+ old_uid, old_gid);
+ if (fchown_ret != 0) {
+ semsg(_(RNERR "Failed setting uid and gid for file %s: %s"),
+ tempname, os_strerror(fchown_ret));
+ goto shada_write_file_did_not_remove;
}
- } else if (!(old_info.stat.st_uid == getuid()
- ? (old_info.stat.st_mode & 0200)
- : (old_info.stat.st_gid == getgid()
- ? (old_info.stat.st_mode & 0020)
- : (old_info.stat.st_mode & 0002)))) {
- semsg(_("E137: ShaDa file is not writable: %s"), fname);
- goto shada_write_file_did_not_remove;
}
}
#endif
- if (vim_rename((char_u *)tempname, (char_u *)fname) == -1) {
+ if (vim_rename(tempname, fname) == -1) {
semsg(_(RNERR "Can't rename ShaDa file from %s to %s!"),
tempname, fname);
} else {
@@ -3115,9 +3134,7 @@ shada_write_file_nomerge: {}
}
}
if (!did_remove) {
-#ifdef UNIX
shada_write_file_did_not_remove:
-#endif
semsg(_(RNERR "Do not forget to remove %s or rename it manually to %s."),
tempname, fname);
}
@@ -3247,13 +3264,12 @@ static ShaDaReadResult fread_len(ShaDaReadDef *const sd_reader, char *const buff
semsg(_(SERR "System error while reading ShaDa file: %s"),
sd_reader->error);
return kSDReadStatusReadError;
- } else {
- semsg(_(RCERR "Error while reading ShaDa file: "
- "last entry specified that it occupies %" PRIu64 " bytes, "
- "but file ended earlier"),
- (uint64_t)length);
- return kSDReadStatusNotShaDa;
}
+ semsg(_(RCERR "Error while reading ShaDa file: "
+ "last entry specified that it occupies %" PRIu64 " bytes, "
+ "but file ended earlier"),
+ (uint64_t)length);
+ return kSDReadStatusNotShaDa;
}
return kSDReadStatusSuccess;
}
@@ -3336,7 +3352,7 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader, const
error_desc
#define CHECK_KEY(key, \
expected) ((key).via.str.size == (sizeof(expected) - 1) \
- && STRNCMP((key).via.str.ptr, expected, (sizeof(expected) - 1)) == 0)
+ && strncmp((key).via.str.ptr, expected, (sizeof(expected) - 1)) == 0)
#define CLEAR_GA_AND_ERROR_OUT(ga) \
do { \
ga_clear(&(ga)); \
@@ -3566,16 +3582,15 @@ shada_read_next_item_start:
entry->type = kSDItemMissing;
}
return spm_ret;
- } else {
- entry->data.unknown_item.contents = xmalloc(length);
- const ShaDaReadResult fl_ret =
- fread_len(sd_reader, entry->data.unknown_item.contents, length);
- if (fl_ret != kSDReadStatusSuccess) {
- shada_free_shada_entry(entry);
- entry->type = kSDItemMissing;
- }
- return fl_ret;
}
+ entry->data.unknown_item.contents = xmalloc(length);
+ const ShaDaReadResult fl_ret =
+ fread_len(sd_reader, entry->data.unknown_item.contents, length);
+ if (fl_ret != kSDReadStatusSuccess) {
+ shada_free_shada_entry(entry);
+ entry->type = kSDItemMissing;
+ }
+ return fl_ret;
}
msgpack_unpacked unpacked;
@@ -3996,12 +4011,12 @@ static bool shada_removable(const char *name)
bool retval = false;
char *new_name = home_replace_save(NULL, (char *)name);
- for (p = (char *)p_shada; *p;) {
+ for (p = p_shada; *p;) {
(void)copy_option_part(&p, part, ARRAY_SIZE(part), ", ");
if (part[0] == 'r') {
- 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) {
+ home_replace(NULL, part + 1, NameBuff, MAXPATHL, true);
+ size_t n = strlen(NameBuff);
+ if (mb_strnicmp(NameBuff, new_name, n) == 0) {
retval = true;
break;
}
@@ -4021,13 +4036,11 @@ static bool shada_removable(const char *name)
static inline size_t shada_init_jumps(PossiblyFreedShadaEntry *jumps,
khash_t(bufset) *const removable_bufs)
{
- if (!curwin->w_jumplistlen) {
- return 0;
- }
-
+ // Initialize jump list
size_t jumps_size = 0;
const void *jump_iter = NULL;
-
+ setpcmark();
+ cleanup_jumplist(curwin, false);
do {
xfmark_T fm;
jump_iter = mark_jumplist_iter(jump_iter, curwin, &fm);
@@ -4100,7 +4113,6 @@ void shada_encode_jumps(msgpack_sbuffer *const sbuf)
khash_t(bufset) removable_bufs = KHASH_EMPTY_TABLE(bufset);
find_removable_bufs(&removable_bufs);
PossiblyFreedShadaEntry jumps[JUMPLISTSIZE];
- cleanup_jumplist(curwin, true);
size_t jumps_size = shada_init_jumps(jumps, &removable_bufs);
msgpack_packer packer;
msgpack_packer_init(&packer, sbuf, msgpack_sbuffer_write);
diff --git a/src/nvim/sign.c b/src/nvim/sign.c
index f1ddbfd147..d0c093d93a 100644
--- a/src/nvim/sign.c
+++ b/src/nvim/sign.c
@@ -5,20 +5,42 @@
// sign.c: functions for managing with signs
//
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
#include "nvim/ascii.h"
#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval/funcs.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/fold.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/hashtab.h"
+#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
+#include "nvim/memline_defs.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
#include "nvim/move.h"
#include "nvim/option.h"
+#include "nvim/pos.h"
#include "nvim/sign.h"
-#include "nvim/syntax.h"
+#include "nvim/sign_defs.h"
+#include "nvim/strings.h"
+#include "nvim/types.h"
#include "nvim/vim.h"
#include "nvim/window.h"
@@ -26,11 +48,11 @@
typedef struct sign sign_T;
struct sign {
- sign_T *sn_next; // next sign in list
+ sign_T *sn_next; // next sign in list
int sn_typenr; // type number of sign
- char_u *sn_name; // name of sign
- char_u *sn_icon; // name of pixmap
- char_u *sn_text; // text used instead of pixmap
+ char *sn_name; // name of sign
+ char *sn_icon; // name of pixmap
+ char *sn_text; // text used instead of pixmap
int sn_line_hl; // highlight ID for line
int sn_text_hl; // highlight ID for text
int sn_cul_hl; // highlight ID for text on current line when 'cursorline' is set
@@ -71,18 +93,17 @@ void init_signs(void)
/// A new sign in group 'groupname' is added. If the group is not present,
/// create it. Otherwise reference the group.
-///
-static signgroup_T *sign_group_ref(const char_u *groupname)
+static signgroup_T *sign_group_ref(const char *groupname)
{
hash_T hash;
hashitem_T *hi;
signgroup_T *group;
hash = hash_hash(groupname);
- hi = hash_lookup(&sg_table, (char *)groupname, STRLEN(groupname), hash);
+ hi = hash_lookup(&sg_table, (char *)groupname, strlen(groupname), hash);
if (HASHITEM_EMPTY(hi)) {
// new group
- group = xmalloc(sizeof(signgroup_T) + STRLEN(groupname));
+ group = xmalloc(sizeof(signgroup_T) + strlen(groupname));
STRCPY(group->sg_name, groupname);
group->sg_refcount = 1;
@@ -99,35 +120,35 @@ static signgroup_T *sign_group_ref(const char_u *groupname)
/// A sign in group 'groupname' is removed. If all the signs in this group are
/// removed, then remove the group.
-static void sign_group_unref(char_u *groupname)
+static void sign_group_unref(char *groupname)
{
- signgroup_T *group;
+ hashitem_T *hi = hash_find(&sg_table, groupname);
+ if (HASHITEM_EMPTY(hi)) {
+ return;
+ }
- hashitem_T *hi = hash_find(&sg_table, (char *)groupname);
- if (!HASHITEM_EMPTY(hi)) {
- group = HI2SG(hi);
- group->sg_refcount--;
- if (group->sg_refcount == 0) {
- // All the signs in this group are removed
- hash_remove(&sg_table, hi);
- xfree(group);
- }
+ signgroup_T *group = HI2SG(hi);
+ group->sg_refcount--;
+ if (group->sg_refcount == 0) {
+ // All the signs in this group are removed
+ hash_remove(&sg_table, hi);
+ xfree(group);
}
}
/// @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.
-static bool sign_in_group(sign_entry_T *sign, const char_u *group)
+static bool sign_in_group(sign_entry_T *sign, const char *group)
{
- return ((group != NULL && STRCMP(group, "*") == 0)
+ return ((group != NULL && strcmp(group, "*") == 0)
|| (group == NULL && sign->se_group == NULL)
|| (group != NULL && sign->se_group != NULL
- && STRCMP(group, sign->se_group->sg_name) == 0));
+ && strcmp(group, sign->se_group->sg_name) == 0));
}
/// Get the next free sign identifier in the specified group
-static 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 *groupname)
{
int id = 1;
signgroup_T *group = NULL;
@@ -154,7 +175,7 @@ static int sign_group_get_next_signid(buf_T *buf, const char_u *groupname)
// Check whether this sign is already placed in the buffer
found = true;
FOR_ALL_SIGNS_IN_BUF(buf, sign) {
- if (id == sign->se_id && sign_in_group(sign, groupname)) {
+ if (id == sign->se_id && sign_in_group(sign, (char *)groupname)) {
found = false; // sign identifier is in use
break;
}
@@ -177,7 +198,7 @@ static int sign_group_get_next_signid(buf_T *buf, const char_u *groupname)
/// @param typenr typenr of sign we are adding
/// @param has_text_or_icon sign has text or icon
static void insert_sign(buf_T *buf, sign_entry_T *prev, sign_entry_T *next, int id,
- const char_u *group, int prio, linenr_T lnum, int typenr,
+ const char *group, int prio, linenr_T lnum, int typenr,
bool has_text_or_icon)
{
sign_entry_T *newsign = xmalloc(sizeof(sign_entry_T));
@@ -203,7 +224,7 @@ static void insert_sign(buf_T *buf, sign_entry_T *prev, sign_entry_T *next, int
// When adding first sign need to redraw the windows to create the
// column for signs.
if (buf->b_signlist == NULL) {
- redraw_buf_later(buf, NOT_VALID);
+ redraw_buf_later(buf, UPD_NOT_VALID);
changed_line_abv_curs();
}
@@ -224,7 +245,7 @@ static void insert_sign(buf_T *buf, sign_entry_T *prev, sign_entry_T *next, int
/// @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
-static void insert_sign_by_lnum_prio(buf_T *buf, sign_entry_T *prev, int id, const char_u *group,
+static void insert_sign_by_lnum_prio(buf_T *buf, sign_entry_T *prev, int id, const char *group,
int prio, linenr_T lnum, int typenr, bool has_text_or_icon)
{
sign_entry_T *sign;
@@ -259,7 +280,7 @@ static sign_T *find_sign_by_typenr(int typenr)
}
/// Get the name of a sign by its typenr.
-static char_u *sign_typenr2name(int typenr)
+static char *sign_typenr2name(int typenr)
{
sign_T *sp;
@@ -268,7 +289,7 @@ static char_u *sign_typenr2name(int typenr)
return sp->sn_name;
}
}
- return (char_u *)_("[Deleted]");
+ return _("[Deleted]");
}
/// Return information about a sign in a Dict
@@ -280,7 +301,7 @@ static dict_T *sign_get_info(sign_entry_T *sign)
? (char *)""
: (char *)sign->se_group->sg_name));
tv_dict_add_nr(d, S_LEN("lnum"), sign->se_lnum);
- tv_dict_add_str(d, S_LEN("name"), (char *)sign_typenr2name(sign->se_typenr));
+ tv_dict_add_str(d, S_LEN("name"), sign_typenr2name(sign->se_typenr));
tv_dict_add_nr(d, S_LEN("priority"), sign->se_priority);
return d;
@@ -369,7 +390,7 @@ 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
-static void buf_addsign(buf_T *buf, int id, const char_u *groupname, int prio, linenr_T lnum,
+static void buf_addsign(buf_T *buf, int id, const char *groupname, int prio, linenr_T lnum,
int typenr, bool has_text_or_icon)
{
sign_entry_T *sign; // a sign in the signlist
@@ -416,13 +437,13 @@ static void buf_addsign(buf_T *buf, int id, const char_u *groupname, int prio, l
/// @param group sign group
/// @param typenr typenr of sign we are adding
/// @param prio sign priority
-static linenr_T buf_change_sign_type(buf_T *buf, int markId, const char_u *group, int typenr,
+static linenr_T buf_change_sign_type(buf_T *buf, int markId, const char *group, int typenr,
int prio)
{
sign_entry_T *sign; // a sign in the signlist
FOR_ALL_SIGNS_IN_BUF(buf, sign) {
- if (sign->se_id == markId && sign_in_group(sign, group)) {
+ if (sign->se_id == markId && sign_in_group(sign, (char *)group)) {
sign->se_typenr = typenr;
sign->se_priority = prio;
sign_sort_by_prio_on_line(buf, sign);
@@ -534,7 +555,7 @@ int buf_get_signattrs(buf_T *buf, linenr_T lnum, SignTextAttrs sattrs[], HlPriAt
///
/// @return the line number of the deleted sign. If multiple signs are deleted,
/// then returns the line number of the last sign deleted.
-static 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 *group)
{
sign_entry_T **lastp; // pointer to pointer to current sign
sign_entry_T *sign; // a sign in a b_signlist
@@ -555,10 +576,10 @@ static linenr_T buf_delsign(buf_T *buf, linenr_T atlnum, int id, char_u *group)
lnum = sign->se_lnum;
buf_signcols_del_check(buf, lnum, lnum);
if (sign->se_group != NULL) {
- sign_group_unref(sign->se_group->sg_name);
+ sign_group_unref((char *)sign->se_group->sg_name);
}
xfree(sign);
- redraw_buf_line_later(buf, lnum);
+ redraw_buf_line_later(buf, lnum, false);
// Check whether only one sign needs to be deleted
// If deleting a sign with a specific identifier in a particular
// group or deleting any sign at a particular line number, delete
@@ -576,7 +597,7 @@ static linenr_T buf_delsign(buf_T *buf, linenr_T atlnum, int id, char_u *group)
// When deleting the last sign the cursor position may change, because the
// sign columns no longer shows. And the 'signcolumn' may be hidden.
if (buf->b_signlist == NULL) {
- redraw_buf_later(buf, NOT_VALID);
+ redraw_buf_later(buf, UPD_NOT_VALID);
changed_line_abv_curs();
}
@@ -590,7 +611,7 @@ static 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
-static int buf_findsign(buf_T *buf, int id, char_u *group)
+static int buf_findsign(buf_T *buf, int id, char *group)
{
sign_entry_T *sign; // a sign in the signlist
@@ -609,7 +630,7 @@ static int buf_findsign(buf_T *buf, int id, char_u *group)
/// @param buf buffer whose sign we are searching for
/// @param lnum line number of sign
/// @param groupname sign group name
-static sign_entry_T *buf_getsign_at_line(buf_T *buf, linenr_T lnum, char_u *groupname)
+static sign_entry_T *buf_getsign_at_line(buf_T *buf, linenr_T lnum, char *groupname)
{
sign_entry_T *sign; // a sign in the signlist
@@ -633,7 +654,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
-static 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 *groupname)
{
sign_entry_T *sign; // a sign in the signlist
@@ -661,13 +682,13 @@ void buf_delete_signs(buf_T *buf, char *group)
lastp = &buf->b_signlist;
for (sign = buf->b_signlist; sign != NULL; sign = next) {
next = sign->se_next;
- if (sign_in_group(sign, (char_u *)group)) {
+ if (sign_in_group(sign, group)) {
*lastp = next;
if (next != NULL) {
next->se_prev = sign->se_prev;
}
if (sign->se_group != NULL) {
- sign_group_unref(sign->se_group->sg_name);
+ sign_group_unref((char *)sign->se_group->sg_name);
}
xfree(sign);
} else {
@@ -678,7 +699,7 @@ void buf_delete_signs(buf_T *buf, char *group)
}
/// List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers.
-static void sign_list_placed(buf_T *rbuf, char_u *sign_group)
+static void sign_list_placed(buf_T *rbuf, char *sign_group)
{
buf_T *buf;
sign_entry_T *sign;
@@ -725,7 +746,27 @@ static void sign_list_placed(buf_T *rbuf, char_u *sign_group)
}
}
-/// Adjust a placed sign for inserted/deleted lines.
+/// Adjust or delete a placed sign for inserted/deleted lines.
+///
+/// @return the new line number of the sign, or 0 if the sign is in deleted lines.
+static linenr_T sign_adjust_one(const linenr_T se_lnum, linenr_T line1, linenr_T line2,
+ linenr_T amount, linenr_T amount_after)
+{
+ if (se_lnum < line1) {
+ // Ignore changes to lines after the sign
+ return se_lnum;
+ }
+ if (se_lnum > line2) {
+ // Lines inserted or deleted before the sign
+ return se_lnum + amount_after;
+ }
+ if (amount == MAXLNUM) { // sign in deleted lines
+ return 0;
+ }
+ return se_lnum + amount;
+}
+
+/// Adjust placed signs for inserted/deleted lines.
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
@@ -736,9 +777,7 @@ void sign_mark_adjust(linenr_T line1, linenr_T line2, linenr_T amount, linenr_T
int is_fixed = 0;
int signcol = win_signcol_configured(curwin, &is_fixed);
- bool delete = amount == MAXLNUM;
-
- if (delete) {
+ if (amount == MAXLNUM) { // deleting
buf_signcols_del_check(curbuf, line1, line2);
}
@@ -746,11 +785,10 @@ void sign_mark_adjust(linenr_T line1, linenr_T line2, linenr_T amount, linenr_T
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 (!delete) {
- new_lnum += amount;
- } else if (!is_fixed || signcol >= 2) {
+
+ new_lnum = sign_adjust_one(sign->se_lnum, line1, line2, amount, amount_after);
+ if (new_lnum == 0) { // sign in deleted lines
+ if (!is_fixed || signcol >= 2) {
*lastp = next;
if (next) {
next->se_prev = last;
@@ -758,19 +796,23 @@ void sign_mark_adjust(linenr_T line1, linenr_T line2, linenr_T amount, linenr_T
xfree(sign);
continue;
}
- } else if (sign->se_lnum > line2) {
- new_lnum += amount_after;
- }
- // If the new sign line number is past the last line in the buffer,
- // then don't adjust the line number. Otherwise, it will always be past
- // the last line and will not be visible.
- if (sign->se_lnum >= line1 && new_lnum <= curbuf->b_ml.ml_line_count) {
- sign->se_lnum = new_lnum;
+ } else {
+ // If the new sign line number is past the last line in the buffer,
+ // then don't adjust the line number. Otherwise, it will always be past
+ // the last line and will not be visible.
+ if (new_lnum <= curbuf->b_ml.ml_line_count) {
+ sign->se_lnum = new_lnum;
+ }
}
last = sign;
lastp = &sign->se_next;
}
+
+ new_lnum = sign_adjust_one(curbuf->b_signcols.sentinel, line1, line2, amount, amount_after);
+ if (new_lnum != 0) {
+ curbuf->b_signcols.sentinel = new_lnum;
+ }
}
/// Find index of a ":sign" subcmd from its name.
@@ -778,14 +820,14 @@ void sign_mark_adjust(linenr_T line1, linenr_T line2, linenr_T amount, linenr_T
///
/// @param begin_cmd begin of sign subcmd
/// @param end_cmd just after sign subcmd
-static int sign_cmd_idx(char_u *begin_cmd, char_u *end_cmd)
+static int sign_cmd_idx(char *begin_cmd, char *end_cmd)
{
int idx;
- char_u save = *end_cmd;
+ char save = *end_cmd;
- *end_cmd = (char_u)NUL;
+ *end_cmd = NUL;
for (idx = 0;; idx++) {
- if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0) {
+ if (cmds[idx] == NULL || strcmp(begin_cmd, cmds[idx]) == 0) {
break;
}
}
@@ -794,7 +836,7 @@ static int sign_cmd_idx(char_u *begin_cmd, char_u *end_cmd)
}
/// Find a sign by name. Also returns pointer to the previous sign.
-static sign_T *sign_find(const char_u *name, sign_T **sp_prev)
+static sign_T *sign_find(const char *name, sign_T **sp_prev)
{
sign_T *sp;
@@ -802,7 +844,7 @@ static sign_T *sign_find(const char_u *name, sign_T **sp_prev)
*sp_prev = NULL;
}
for (sp = first_sign; sp != NULL; sp = sp->sn_next) {
- if (STRCMP(sp->sn_name, name) == 0) {
+ if (strcmp(sp->sn_name, name) == 0) {
break;
}
if (sp_prev != NULL) {
@@ -814,7 +856,7 @@ static sign_T *sign_find(const char_u *name, sign_T **sp_prev)
}
/// Allocate a new sign
-static sign_T *alloc_new_sign(char_u *name)
+static sign_T *alloc_new_sign(char *name)
{
sign_T *sp;
sign_T *lp;
@@ -848,28 +890,28 @@ static sign_T *alloc_new_sign(char_u *name)
next_sign_typenr = 1; // wrap around
}
- sp->sn_name = vim_strsave(name);
+ sp->sn_name = xstrdup(name);
return sp;
}
/// Initialize the icon information for a new sign
-static void sign_define_init_icon(sign_T *sp, char_u *icon)
+static void sign_define_init_icon(sign_T *sp, char *icon)
{
xfree(sp->sn_icon);
- sp->sn_icon = vim_strsave(icon);
+ sp->sn_icon = xstrdup(icon);
backslash_halve(sp->sn_icon);
}
/// Initialize the text for a new sign
-static int sign_define_init_text(sign_T *sp, char_u *text)
+static int sign_define_init_text(sign_T *sp, char *text)
{
- char_u *s;
- char_u *endp;
+ char *s;
+ char *endp;
int cells;
size_t len;
- endp = text + (int)STRLEN(text);
+ endp = text + (int)strlen(text);
for (s = text; s + 1 < endp; s++) {
if (*s == '\\') {
// Remove a backslash, so that it is possible
@@ -880,11 +922,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((char *)s)) {
- if (!vim_isprintc(utf_ptr2char((char *)s))) {
+ for (s = text; s < endp; s += utfc_ptr2len(s)) {
+ if (!vim_isprintc(utf_ptr2char(s))) {
break;
}
- cells += utf_ptr2cells((char *)s);
+ cells += utf_ptr2cells(s);
}
// Currently must be empty, one or two display cells
if (s != endp || cells > 2) {
@@ -900,7 +942,7 @@ static int sign_define_init_text(sign_T *sp, char_u *text)
// Allocate one byte more if we need to pad up
// with a space.
len = (size_t)(endp - text + ((cells == 1) ? 1 : 0));
- sp->sn_text = vim_strnsave(text, len);
+ sp->sn_text = xstrnsave(text, len);
if (cells == 1) {
STRCPY(sp->sn_text + len - 1, " ");
@@ -910,8 +952,8 @@ static int sign_define_init_text(sign_T *sp, char_u *text)
}
/// Define a new sign or update an existing sign
-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)
+static int sign_define_by_name(char *name, char *icon, char *linehl, char *text, char *texthl,
+ char *culhl, char *numhl)
{
sign_T *sp_prev;
sign_T *sp;
@@ -934,7 +976,7 @@ static int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_
// non-empty sign list.
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_buffer->b_signlist != NULL) {
- redraw_buf_later(wp->w_buffer, NOT_VALID);
+ redraw_buf_later(wp->w_buffer, UPD_NOT_VALID);
}
}
}
@@ -952,7 +994,7 @@ static int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_
if (*linehl == NUL) {
sp->sn_line_hl = 0;
} else {
- sp->sn_line_hl = syn_check_group((char *)linehl, STRLEN(linehl));
+ sp->sn_line_hl = syn_check_group(linehl, strlen(linehl));
}
}
@@ -960,7 +1002,7 @@ static int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_
if (*texthl == NUL) {
sp->sn_text_hl = 0;
} else {
- sp->sn_text_hl = syn_check_group((char *)texthl, STRLEN(texthl));
+ sp->sn_text_hl = syn_check_group(texthl, strlen(texthl));
}
}
@@ -968,7 +1010,7 @@ static int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_
if (*culhl == NUL) {
sp->sn_cul_hl = 0;
} else {
- sp->sn_cul_hl = syn_check_group((char *)culhl, STRLEN(culhl));
+ sp->sn_cul_hl = syn_check_group(culhl, strlen(culhl));
}
}
@@ -976,7 +1018,7 @@ static int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_
if (*numhl == NUL) {
sp->sn_num_hl = 0;
} else {
- sp->sn_num_hl = syn_check_group(numhl, STRLEN(numhl));
+ sp->sn_num_hl = syn_check_group(numhl, strlen(numhl));
}
}
@@ -984,7 +1026,7 @@ static int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_
}
/// Free the sign specified by 'name'.
-static int sign_undefine_by_name(const char_u *name)
+static int sign_undefine_by_name(const char *name)
{
sign_T *sp_prev;
sign_T *sp;
@@ -1000,7 +1042,7 @@ static int sign_undefine_by_name(const char_u *name)
}
/// List the signs matching 'name'
-static void sign_list_by_name(char_u *name)
+static void sign_list_by_name(char *name)
{
sign_T *sp;
@@ -1024,7 +1066,7 @@ static void may_force_numberwidth_recompute(buf_T *buf, int unplace)
}
/// Place a sign at the specified file location or update a sign.
-static int sign_place(int *sign_id, const char_u *sign_group, const char_u *sign_name, buf_T *buf,
+static int sign_place(int *sign_id, const char *sign_group, const char *sign_name, buf_T *buf,
linenr_T lnum, int prio)
{
sign_T *sp;
@@ -1035,7 +1077,7 @@ static int sign_place(int *sign_id, const char_u *sign_group, const char_u *sign
}
for (sp = first_sign; sp != NULL; sp = sp->sn_next) {
- if (STRCMP(sp->sn_name, sign_name) == 0) {
+ if (strcmp(sp->sn_name, sign_name) == 0) {
break;
}
}
@@ -1044,7 +1086,7 @@ static int sign_place(int *sign_id, const char_u *sign_group, const char_u *sign
return FAIL;
}
if (*sign_id == 0) {
- *sign_id = sign_group_get_next_signid(buf, sign_group);
+ *sign_id = sign_group_get_next_signid(buf, (char *)sign_group);
}
if (lnum > 0) {
@@ -1053,17 +1095,17 @@ static int sign_place(int *sign_id, const char_u *sign_group, const char_u *sign
bool has_text_or_icon = sp->sn_text != NULL || sp->sn_icon != NULL;
buf_addsign(buf,
*sign_id,
- sign_group,
+ (char *)sign_group,
prio,
lnum,
sp->sn_typenr,
has_text_or_icon);
} else {
// ":sign place {id} file={fname}": change sign type and/or priority
- lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr, prio);
+ lnum = buf_change_sign_type(buf, *sign_id, (char *)sign_group, sp->sn_typenr, prio);
}
if (lnum > 0) {
- redraw_buf_line_later(buf, lnum);
+ redraw_buf_line_later(buf, lnum, false);
// When displaying signs in the 'number' column, if the width of the
// number column is less than 2, then force recomputing the width.
@@ -1077,15 +1119,15 @@ static int sign_place(int *sign_id, const char_u *sign_group, const char_u *sign
}
/// Unplace the specified sign
-static int sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum)
+static int sign_unplace(int sign_id, char *sign_group, buf_T *buf, linenr_T atlnum)
{
if (buf->b_signlist == NULL) { // No signs in the buffer
return OK;
}
if (sign_id == 0) {
// Delete all the signs in the specified buffer
- redraw_buf_later(buf, NOT_VALID);
- buf_delete_signs(buf, (char *)sign_group);
+ redraw_buf_later(buf, UPD_NOT_VALID);
+ buf_delete_signs(buf, sign_group);
} else {
linenr_T lnum;
@@ -1094,7 +1136,7 @@ static int sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T at
if (lnum == 0) {
return FAIL;
}
- redraw_buf_line_later(buf, lnum);
+ redraw_buf_line_later(buf, lnum, false);
}
// When all the signs in a buffer are removed, force recomputing the
@@ -1108,7 +1150,7 @@ static int sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T at
}
/// Unplace the sign at the current cursor line.
-static void sign_unplace_at_cursor(char_u *groupname)
+static void sign_unplace_at_cursor(char *groupname)
{
int id = -1;
@@ -1121,7 +1163,7 @@ static void sign_unplace_at_cursor(char_u *groupname)
}
/// Jump to a sign.
-static linenr_T sign_jump(int sign_id, char_u *sign_group, buf_T *buf)
+static linenr_T sign_jump(int sign_id, char *sign_group, buf_T *buf)
{
linenr_T lnum;
@@ -1140,7 +1182,7 @@ static linenr_T sign_jump(int sign_id, char_u *sign_group, buf_T *buf)
emsg(_("E934: Cannot jump to a buffer that does not have a name"));
return -1;
}
- size_t cmdlen = STRLEN(buf->b_fname) + 24;
+ size_t cmdlen = strlen(buf->b_fname) + 24;
char *cmd = xmallocz(cmdlen);
snprintf(cmd, cmdlen, "e +%" PRId64 " %s",
(int64_t)lnum, buf->b_fname);
@@ -1154,49 +1196,49 @@ static linenr_T sign_jump(int sign_id, char_u *sign_group, buf_T *buf)
}
/// ":sign define {name} ..." command
-static void sign_define_cmd(char_u *sign_name, char_u *cmdline)
-{
- char_u *arg;
- char_u *p = cmdline;
- char_u *icon = NULL;
- char_u *text = NULL;
- char_u *linehl = NULL;
- char_u *texthl = NULL;
- char_u *culhl = NULL;
- char_u *numhl = NULL;
+static void sign_define_cmd(char *sign_name, char *cmdline)
+{
+ char *arg;
+ char *p = cmdline;
+ char *icon = NULL;
+ char *text = NULL;
+ char *linehl = NULL;
+ char *texthl = NULL;
+ char *culhl = NULL;
+ char *numhl = NULL;
int failed = false;
// set values for a defined sign.
for (;;) {
- arg = (char_u *)skipwhite((char *)p);
+ arg = skipwhite(p);
if (*arg == NUL) {
break;
}
- p = (char_u *)skiptowhite_esc((char *)arg);
- if (STRNCMP(arg, "icon=", 5) == 0) {
+ p = skiptowhite_esc(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) {
+ icon = xstrnsave(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) {
+ text = xstrnsave(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) {
+ linehl = xstrnsave(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) {
+ texthl = xstrnsave(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) {
+ culhl = xstrnsave(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));
+ numhl = xstrnsave(arg, (size_t)(p - arg));
} else {
semsg(_(e_invarg2), arg);
failed = true;
@@ -1205,7 +1247,8 @@ static void sign_define_cmd(char_u *sign_name, char_u *cmdline)
}
if (!failed) {
- sign_define_by_name(sign_name, icon, linehl, text, texthl, culhl, (char *)numhl);
+ sign_define_by_name(sign_name, icon, linehl, text,
+ texthl, culhl, numhl);
}
xfree(icon);
@@ -1217,7 +1260,7 @@ static void sign_define_cmd(char_u *sign_name, char_u *cmdline)
}
/// ":sign place" command
-static void sign_place_cmd(buf_T *buf, linenr_T lnum, char_u *sign_name, int id, char_u *group,
+static void sign_place_cmd(buf_T *buf, linenr_T lnum, char *sign_name, int id, char *group,
int prio)
{
if (id <= 0) {
@@ -1250,7 +1293,7 @@ static void sign_place_cmd(buf_T *buf, linenr_T lnum, char_u *sign_name, int id,
}
/// ":sign unplace" command
-static void sign_unplace_cmd(buf_T *buf, linenr_T lnum, char_u *sign_name, int id, char_u *group)
+static void sign_unplace_cmd(buf_T *buf, linenr_T lnum, const char *sign_name, int id, char *group)
{
if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0')) {
emsg(_(e_invarg));
@@ -1272,7 +1315,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, (char *)group);
+ buf_delete_signs(cbuf, group);
}
}
}
@@ -1307,7 +1350,7 @@ static void sign_unplace_cmd(buf_T *buf, linenr_T lnum, char_u *sign_name, int i
/// :sign jump {id} buffer={nr}
/// :sign jump {id} group={group} file={fname}
/// :sign jump {id} group={group} buffer={nr}
-static void sign_jump_cmd(buf_T *buf, linenr_T lnum, char_u *sign_name, int id, char_u *group)
+static void sign_jump_cmd(buf_T *buf, linenr_T lnum, const char *sign_name, int id, char *group)
{
if (sign_name == NULL && group == NULL && id == -1) {
emsg(_(e_argreq));
@@ -1329,40 +1372,40 @@ static void sign_jump_cmd(buf_T *buf, linenr_T lnum, char_u *sign_name, int id,
/// ":sign jump" commands.
/// The supported arguments are: line={lnum} name={name} group={group}
/// priority={prio} and file={fname} or buffer={nr}.
-static int parse_sign_cmd_args(int cmd, char_u *arg, char_u **sign_name, int *signid,
- char_u **group, int *prio, buf_T **buf, linenr_T *lnum)
+static int parse_sign_cmd_args(int cmd, char *arg, char **sign_name, int *signid, char **group,
+ int *prio, buf_T **buf, linenr_T *lnum)
{
- char_u *arg1;
- char_u *name;
- char_u *filename = NULL;
+ char *arg1;
+ char *name;
+ char *filename = NULL;
int lnum_arg = false;
// first arg could be placed sign id
arg1 = arg;
if (ascii_isdigit(*arg)) {
- *signid = getdigits_int((char **)&arg, true, 0);
+ *signid = getdigits_int(&arg, true, 0);
if (!ascii_iswhite(*arg) && *arg != NUL) {
*signid = -1;
arg = arg1;
} else {
- arg = (char_u *)skipwhite((char *)arg);
+ arg = skipwhite(arg);
}
}
while (*arg != NUL) {
- if (STRNCMP(arg, "line=", 5) == 0) {
+ if (strncmp(arg, "line=", 5) == 0) {
arg += 5;
- *lnum = atoi((char *)arg);
+ *lnum = atoi(arg);
arg = skiptowhite(arg);
lnum_arg = true;
- } else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE) {
+ } else if (strncmp(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE) {
if (*signid != -1) {
emsg(_(e_invarg));
return FAIL;
}
*signid = -2;
arg = skiptowhite(arg + 1);
- } else if (STRNCMP(arg, "name=", 5) == 0) {
+ } else if (strncmp(arg, "name=", 5) == 0) {
arg += 5;
name = arg;
arg = skiptowhite(arg);
@@ -1373,27 +1416,27 @@ static int parse_sign_cmd_args(int cmd, char_u *arg, char_u **sign_name, int *si
name++;
}
*sign_name = name;
- } else if (STRNCMP(arg, "group=", 6) == 0) {
+ } else if (strncmp(arg, "group=", 6) == 0) {
arg += 6;
*group = arg;
arg = skiptowhite(arg);
if (*arg != NUL) {
*arg++ = NUL;
}
- } else if (STRNCMP(arg, "priority=", 9) == 0) {
+ } else if (strncmp(arg, "priority=", 9) == 0) {
arg += 9;
- *prio = atoi((char *)arg);
+ *prio = atoi(arg);
arg = skiptowhite(arg);
- } else if (STRNCMP(arg, "file=", 5) == 0) {
+ } else if (strncmp(arg, "file=", 5) == 0) {
arg += 5;
filename = arg;
- *buf = buflist_findname_exp((char *)arg);
+ *buf = buflist_findname_exp(arg);
break;
- } else if (STRNCMP(arg, "buffer=", 7) == 0) {
+ } else if (strncmp(arg, "buffer=", 7) == 0) {
arg += 7;
filename = arg;
- *buf = buflist_findnr(getdigits_int((char **)&arg, true, 0));
- if (*skipwhite((char *)arg) != NUL) {
+ *buf = buflist_findnr(getdigits_int(&arg, true, 0));
+ if (*skipwhite(arg) != NUL) {
semsg(_(e_trailing_arg), arg);
}
break;
@@ -1401,7 +1444,7 @@ static int parse_sign_cmd_args(int cmd, char_u *arg, char_u **sign_name, int *si
emsg(_(e_invarg));
return FAIL;
}
- arg = (char_u *)skipwhite((char *)arg);
+ arg = skipwhite(arg);
}
if (filename != NULL && *buf == NULL) {
@@ -1421,8 +1464,8 @@ 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 = (char_u *)eap->arg;
- char_u *p;
+ char *arg = eap->arg;
+ char *p;
int idx;
sign_T *sp;
@@ -1433,7 +1476,7 @@ void ex_sign(exarg_T *eap)
semsg(_("E160: Unknown sign command: %s"), arg);
return;
}
- arg = (char_u *)skipwhite((char *)p);
+ arg = skipwhite(p);
if (idx <= SIGNCMD_LIST) {
// Define, undefine or list signs.
@@ -1445,7 +1488,7 @@ void ex_sign(exarg_T *eap)
} else if (*arg == NUL) {
emsg(_("E156: Missing sign name"));
} else {
- char_u *name;
+ char *name;
// Isolate the sign name. If it's a number skip leading zeroes,
// so that "099" and "99" are the same sign. But keep "0".
@@ -1456,7 +1499,7 @@ void ex_sign(exarg_T *eap)
while (arg[0] == '0' && arg[1] != NUL) {
arg++;
}
- name = vim_strsave(arg);
+ name = xstrdup(arg);
if (idx == SIGNCMD_DEFINE) {
sign_define_cmd(name, p);
@@ -1474,8 +1517,8 @@ void ex_sign(exarg_T *eap)
} else {
int id = -1;
linenr_T lnum = -1;
- char_u *sign_name = NULL;
- char_u *group = NULL;
+ char *sign_name = NULL;
+ char *group = NULL;
int prio = SIGN_DEF_PRIO;
buf_T *buf = NULL;
@@ -1500,12 +1543,12 @@ static void sign_getinfo(sign_T *sp, dict_T *retdict)
{
const char *p;
- tv_dict_add_str(retdict, S_LEN("name"), (char *)sp->sn_name);
+ tv_dict_add_str(retdict, S_LEN("name"), sp->sn_name);
if (sp->sn_icon != NULL) {
- tv_dict_add_str(retdict, S_LEN("icon"), (char *)sp->sn_icon);
+ tv_dict_add_str(retdict, S_LEN("icon"), sp->sn_icon);
}
if (sp->sn_text != NULL) {
- tv_dict_add_str(retdict, S_LEN("text"), (char *)sp->sn_text);
+ tv_dict_add_str(retdict, S_LEN("text"), sp->sn_text);
}
if (sp->sn_line_hl > 0) {
p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, false);
@@ -1539,13 +1582,13 @@ static void sign_getinfo(sign_T *sp, dict_T *retdict)
/// If 'name' is NULL, return a list of all the defined signs.
/// Otherwise, return information about the specified sign.
-static void sign_getlist(const char_u *name, list_T *retlist)
+static void sign_getlist(const char *name, list_T *retlist)
{
sign_T *sp = first_sign;
dict_T *dict;
if (name != NULL) {
- sp = sign_find(name, NULL);
+ sp = sign_find((char *)name, NULL);
if (sp == NULL) {
return;
}
@@ -1577,8 +1620,8 @@ list_T *get_buffer_signs(buf_T *buf)
return l;
}
-/// Return information about all the signs placed in a buffer
-static void sign_get_placed_in_buf(buf_T *buf, linenr_T lnum, int sign_id, const char_u *sign_group,
+/// @return information about all the signs placed in a buffer
+static void sign_get_placed_in_buf(buf_T *buf, linenr_T lnum, int sign_id, const char *sign_group,
list_T *retlist)
{
dict_T *d;
@@ -1594,7 +1637,7 @@ static void sign_get_placed_in_buf(buf_T *buf, linenr_T lnum, int sign_id, const
tv_dict_add_list(d, S_LEN("signs"), l);
FOR_ALL_SIGNS_IN_BUF(buf, sign) {
- if (!sign_in_group(sign, sign_group)) {
+ if (!sign_in_group(sign, (char *)sign_group)) {
continue;
}
if ((lnum == 0 && sign_id == 0)
@@ -1609,7 +1652,7 @@ 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.
-static void sign_get_placed(buf_T *buf, linenr_T lnum, int sign_id, const char_u *sign_group,
+static void sign_get_placed(buf_T *buf, linenr_T lnum, int sign_id, const char *sign_group,
list_T *retlist)
{
if (buf != NULL) {
@@ -1629,12 +1672,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((char *)sp->sn_icon);
+ msg_outtrans(sp->sn_icon);
msg_puts(_(" (not supported)"));
}
if (sp->sn_text != NULL) {
msg_puts(" text=");
- msg_outtrans((char *)sp->sn_text);
+ msg_outtrans(sp->sn_text);
}
if (sp->sn_line_hl > 0) {
msg_puts(" linehl=");
@@ -1710,8 +1753,8 @@ static enum {
EXP_SIGN_GROUPS, // expand with name of placed sign groups
} expand_what;
-// Return the n'th sign name (used for command line completion)
-static char_u *get_nth_sign_name(int idx)
+/// @return the n'th sign name (used for command line completion)
+static char *get_nth_sign_name(int idx)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
// Complete with name of signs already defined
@@ -1724,8 +1767,8 @@ static char_u *get_nth_sign_name(int idx)
return NULL;
}
-// Return the n'th sign group name (used for command line completion)
-static char_u *get_nth_sign_group_name(int idx)
+/// @return the n'th sign group name (used for command line completion)
+static char *get_nth_sign_group_name(int idx)
{
// Complete with name of sign groups already defined
int current_idx = 0;
@@ -1735,7 +1778,7 @@ static char_u *get_nth_sign_group_name(int idx)
todo--;
if (current_idx++ == idx) {
signgroup_T *const group = HI2SG(hi);
- return group->sg_name;
+ return (char *)group->sg_name;
}
}
}
@@ -1768,26 +1811,26 @@ char *get_sign_name(expand_T *xp, int idx)
return unplace_arg[idx];
}
case EXP_SIGN_NAMES:
- return (char *)get_nth_sign_name(idx);
+ return get_nth_sign_name(idx);
case EXP_SIGN_GROUPS:
- return (char *)get_nth_sign_group_name(idx);
+ return get_nth_sign_group_name(idx);
default:
return NULL;
}
}
/// Handle command line completion for :sign command.
-void set_context_in_sign_cmd(expand_T *xp, char_u *arg)
+void set_context_in_sign_cmd(expand_T *xp, char *arg)
{
- char_u *end_subcmd;
- char_u *last;
+ char *end_subcmd;
+ char *last;
int cmd_idx;
- char_u *begin_subcmd_args;
+ char *begin_subcmd_args;
// Default: expand subcommands.
xp->xp_context = EXPAND_SIGN;
expand_what = EXP_SUBCMD;
- xp->xp_pattern = (char *)arg;
+ xp->xp_pattern = arg;
end_subcmd = skiptowhite(arg);
if (*end_subcmd == NUL) {
@@ -1801,7 +1844,7 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg)
// :sign {subcmd} {subcmd_args}
// |
// begin_subcmd_args
- begin_subcmd_args = (char_u *)skipwhite((char *)end_subcmd);
+ begin_subcmd_args = skipwhite(end_subcmd);
// Expand last argument of subcmd.
//
@@ -1810,21 +1853,21 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg)
// p
// Loop until reaching last argument.
- char_u *p = begin_subcmd_args;
+ char *p = begin_subcmd_args;
do {
- p = (char_u *)skipwhite((char *)p);
+ p = skipwhite(p);
last = p;
p = skiptowhite(p);
} while (*p != NUL);
- p = (char_u *)vim_strchr((char *)last, '=');
+ p = vim_strchr(last, '=');
// :sign define {name} {args}... {last}=
// | |
// last p
if (p == NULL) {
// Expand last argument name (before equal sign).
- xp->xp_pattern = (char *)last;
+ xp->xp_pattern = last;
switch (cmd_idx) {
case SIGNCMD_DEFINE:
expand_what = EXP_DEFINE;
@@ -1854,26 +1897,26 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg)
}
} else {
// Expand last argument value (after equal sign).
- xp->xp_pattern = (char *)p + 1;
+ xp->xp_pattern = p + 1;
switch (cmd_idx) {
case SIGNCMD_DEFINE:
- if (STRNCMP(last, "texthl", 6) == 0
- || STRNCMP(last, "linehl", 6) == 0
- || STRNCMP(last, "culhl", 5) == 0
- || STRNCMP(last, "numhl", 5) == 0) {
+ if (strncmp(last, "texthl", 6) == 0
+ || strncmp(last, "linehl", 6) == 0
+ || strncmp(last, "culhl", 5) == 0
+ || strncmp(last, "numhl", 5) == 0) {
xp->xp_context = EXPAND_HIGHLIGHT;
- } else if (STRNCMP(last, "icon", 4) == 0) {
+ } else if (strncmp(last, "icon", 4) == 0) {
xp->xp_context = EXPAND_FILES;
} else {
xp->xp_context = EXPAND_NOTHING;
}
break;
case SIGNCMD_PLACE:
- if (STRNCMP(last, "name", 4) == 0) {
+ if (strncmp(last, "name", 4) == 0) {
expand_what = EXP_SIGN_NAMES;
- } else if (STRNCMP(last, "group", 5) == 0) {
+ } else if (strncmp(last, "group", 5) == 0) {
expand_what = EXP_SIGN_GROUPS;
- } else if (STRNCMP(last, "file", 4) == 0) {
+ } else if (strncmp(last, "file", 4) == 0) {
xp->xp_context = EXPAND_BUFFERS;
} else {
xp->xp_context = EXPAND_NOTHING;
@@ -1881,9 +1924,9 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg)
break;
case SIGNCMD_UNPLACE:
case SIGNCMD_JUMP:
- if (STRNCMP(last, "group", 5) == 0) {
+ if (strncmp(last, "group", 5) == 0) {
expand_what = EXP_SIGN_GROUPS;
- } else if (STRNCMP(last, "file", 4) == 0) {
+ } else if (strncmp(last, "file", 4) == 0) {
xp->xp_context = EXPAND_BUFFERS;
} else {
xp->xp_context = EXPAND_NOTHING;
@@ -1928,8 +1971,8 @@ static int sign_define_from_dict(const char *name_arg, dict_T *dict)
numhl = tv_dict_get_string(dict, "numhl", true);
}
- if (sign_define_by_name((char_u *)name, (char_u *)icon, (char_u *)linehl,
- (char_u *)text, (char_u *)texthl, (char_u *)culhl, numhl)
+ if (sign_define_by_name(name, icon, linehl,
+ text, texthl, culhl, numhl)
== OK) {
retval = 0;
}
@@ -1964,7 +2007,7 @@ static 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)
+void f_sign_define(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *name;
@@ -1995,7 +2038,7 @@ void f_sign_define(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "sign_getdefined()" function
-void f_sign_getdefined(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_sign_getdefined(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *name = NULL;
@@ -2005,11 +2048,11 @@ void f_sign_getdefined(typval_T *argvars, typval_T *rettv, FunPtr fptr)
name = tv_get_string(&argvars[0]);
}
- sign_getlist((const char_u *)name, rettv->vval.v_list);
+ sign_getlist(name, rettv->vval.v_list);
}
/// "sign_getplaced()" function
-void f_sign_getplaced(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_sign_getplaced(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
buf_T *buf = NULL;
dict_T *dict;
@@ -2062,12 +2105,11 @@ void f_sign_getplaced(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
- sign_get_placed(buf, lnum, sign_id, (const char_u *)group,
- rettv->vval.v_list);
+ sign_get_placed(buf, lnum, sign_id, group, rettv->vval.v_list);
}
/// "sign_jump()" function
-void f_sign_jump(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_sign_jump(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int sign_id;
char *sign_group = NULL;
@@ -2103,7 +2145,7 @@ void f_sign_jump(typval_T *argvars, typval_T *rettv, FunPtr fptr)
goto cleanup;
}
- rettv->vval.v_number = sign_jump(sign_id, (char_u *)sign_group, buf);
+ rettv->vval.v_number = sign_jump(sign_id, sign_group, buf);
cleanup:
xfree(sign_group);
@@ -2115,8 +2157,8 @@ static int sign_place_from_dict(typval_T *id_tv, typval_T *group_tv, typval_T *n
typval_T *buf_tv, dict_T *dict)
{
int sign_id = 0;
- char_u *group = NULL;
- char_u *sign_name = NULL;
+ char *group = NULL;
+ char *sign_name = NULL;
buf_T *buf = NULL;
dictitem_T *di;
linenr_T lnum = 0;
@@ -2154,14 +2196,14 @@ static int sign_place_from_dict(typval_T *id_tv, typval_T *group_tv, typval_T *n
if (group_tv == NULL) {
group = NULL; // global group
} else {
- group = (char_u *)tv_get_string_chk(group_tv);
+ group = (char *)tv_get_string_chk(group_tv);
if (group == NULL) {
goto cleanup;
}
if (group[0] == '\0') { // global sign group
group = NULL;
} else {
- group = vim_strsave(group);
+ group = xstrdup(group);
}
}
@@ -2175,7 +2217,7 @@ static int sign_place_from_dict(typval_T *id_tv, typval_T *group_tv, typval_T *n
if (name_tv == NULL) {
goto cleanup;
}
- sign_name = (char_u *)tv_get_string_chk(name_tv);
+ sign_name = (char *)tv_get_string_chk(name_tv);
if (sign_name == NULL) {
goto cleanup;
}
@@ -2225,7 +2267,7 @@ cleanup:
}
/// "sign_place()" function
-void f_sign_place(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_sign_place(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
dict_T *dict = NULL;
@@ -2243,7 +2285,7 @@ void f_sign_place(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "sign_placelist()" function. Place multiple signs.
-void f_sign_placelist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_sign_placelist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int sign_id;
@@ -2269,13 +2311,13 @@ void f_sign_placelist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// Undefine multiple signs
static void sign_undefine_multiple(list_T *l, list_T *retlist)
{
- char_u *name;
+ char *name;
int retval;
TV_LIST_ITER_CONST(l, li, {
retval = -1;
- name = (char_u *)tv_get_string_chk(TV_LIST_ITEM_TV(li));
- if (name != NULL && (sign_undefine_by_name(name) == OK)) {
+ name = (char *)tv_get_string_chk(TV_LIST_ITEM_TV(li));
+ if (name != NULL && (sign_undefine_by_name((char *)name) == OK)) {
retval = 0;
}
tv_list_append_number(retlist, retval);
@@ -2283,7 +2325,7 @@ static 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)
+void f_sign_undefine(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *name;
@@ -2308,7 +2350,7 @@ void f_sign_undefine(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
- if (sign_undefine_by_name((const char_u *)name) == OK) {
+ if (sign_undefine_by_name(name) == OK) {
rettv->vval.v_number = 0;
}
}
@@ -2321,20 +2363,20 @@ static int sign_unplace_from_dict(typval_T *group_tv, dict_T *dict)
dictitem_T *di;
int sign_id = 0;
buf_T *buf = NULL;
- char_u *group = NULL;
+ char *group = NULL;
int retval = -1;
// sign group
if (group_tv != NULL) {
- group = (char_u *)tv_get_string(group_tv);
+ group = (char *)tv_get_string(group_tv);
} else {
- group = (char_u *)tv_dict_get_string(dict, "group", false);
+ group = tv_dict_get_string(dict, "group", false);
}
if (group != NULL) {
if (group[0] == '\0') { // global sign group
group = NULL;
} else {
- group = vim_strsave(group);
+ group = xstrdup(group);
}
}
@@ -2373,7 +2415,7 @@ cleanup:
}
/// "sign_unplace()" function
-void f_sign_unplace(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_sign_unplace(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
dict_T *dict = NULL;
@@ -2396,7 +2438,7 @@ void f_sign_unplace(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "sign_unplacelist()" function
-void f_sign_unplacelist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_sign_unplacelist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int retval;
diff --git a/src/nvim/sign_defs.h b/src/nvim/sign_defs.h
index a4fb325ec8..16e783aab7 100644
--- a/src/nvim/sign_defs.h
+++ b/src/nvim/sign_defs.h
@@ -10,9 +10,9 @@
// Sign group
typedef struct signgroup_S {
- uint16_t sg_refcount; // number of signs in this group
- int sg_next_sign_id; // next sign id for this group
- char_u sg_name[1]; // sign group name
+ uint16_t sg_refcount; // number of signs in this group
+ int sg_next_sign_id; // next sign id for this group
+ char sg_name[1]; // sign group name
} signgroup_T;
// Macros to get the sign group structure from the group name
@@ -34,7 +34,7 @@ struct sign_entry {
/// Sign attributes. Used by the screen refresh routines.
typedef struct {
- char_u *text;
+ char *text;
int hl_attr_id;
int priority;
} SignTextAttrs;
@@ -51,4 +51,4 @@ typedef enum {
SIGN_TEXT,
} SignType;
-#endif // NVIM_SIGN_DEFS_H
+#endif // NVIM_SIGN_DEFS_H
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index 1e44d328b3..8e18be5bd1 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -50,7 +50,7 @@
// Use SPELL_PRINTTREE for debugging: dump the word tree after adding a word.
// Only use it for small word lists!
-// Use SPELL_COMPRESS_ALLWAYS for debugging: compress the word tree after
+// Use SPELL_COMPRESS_ALWAYS for debugging: compress the word tree after
// adding a word. Only use it for small word lists!
// Use DEBUG_TRIEWALK to print the changes made in suggest_trie_walk() for a
@@ -60,74 +60,84 @@
#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include <wctype.h>
-
-// for offsetof()
#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/change.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/decoration.h"
+#include "nvim/decoration_provider.h"
#include "nvim/drawscreen.h"
-#include "nvim/edit.h"
#include "nvim/ex_cmds.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
-#include "nvim/func_attr.h"
#include "nvim/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
#include "nvim/hashtab.h"
-#include "nvim/input.h"
+#include "nvim/highlight_defs.h"
#include "nvim/insexpand.h"
+#include "nvim/log.h"
+#include "nvim/macros.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/normal.h"
#include "nvim/option.h"
+#include "nvim/os/fs.h"
#include "nvim/os/input.h"
-#include "nvim/os/os.h"
-#include "nvim/os_unix.h"
+#include "nvim/os/os_defs.h"
#include "nvim/path.h"
+#include "nvim/pos.h"
#include "nvim/regexp.h"
+#include "nvim/runtime.h"
#include "nvim/search.h"
#include "nvim/spell.h"
+#include "nvim/spell_defs.h"
#include "nvim/spellfile.h"
#include "nvim/spellsuggest.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
+#include "nvim/types.h"
#include "nvim/undo.h"
+#include "nvim/vim.h"
+#include "nvim/window.h"
// Result values. Lower number is accepted over higher one.
-#define SP_BANNED (-1)
-#define SP_RARE 0
-#define SP_OK 1
-#define SP_LOCAL 2
-#define SP_BAD 3
+enum {
+ SP_BANNED = -1,
+ SP_RARE = 0,
+ SP_OK = 1,
+ SP_LOCAL = 2,
+ SP_BAD = 3,
+};
// First language that is loaded, start of the linked list of loaded
// languages.
slang_T *first_lang = NULL;
// file used for "zG" and "zW"
-char_u *int_wordlist = NULL;
+char *int_wordlist = NULL;
// Structure to store info for word matching.
typedef struct matchinf_S {
langp_T *mi_lp; // info for language and region
// pointers to original text to be checked
- char_u *mi_word; // start of word being checked
- char_u *mi_end; // end of matching word so far
- char_u *mi_fend; // next char to be added to mi_fword
- char_u *mi_cend; // char after what was used for
+ char *mi_word; // start of word being checked
+ char *mi_end; // end of matching word so far
+ char *mi_fend; // next char to be added to mi_fword
+ char *mi_cend; // char after what was used for
// mi_capflags
// case-folded text
- char_u mi_fword[MAXWLEN + 1]; // mi_word case-folded
+ char mi_fword[MAXWLEN + 1]; // mi_word case-folded
int mi_fwordlen; // nr of valid bytes in mi_fword
// for when checking word after a prefix
@@ -150,20 +160,20 @@ typedef struct matchinf_S {
win_T *mi_win; // buffer being checked
// for NOBREAK
- int mi_result2; // "mi_resul" without following word
- char_u *mi_end2; // "mi_end" without following word
+ int mi_result2; // "mi_result" without following word
+ char *mi_end2; // "mi_end" without following word
} matchinf_T;
// Structure used for the cookie argument of do_in_runtimepath().
typedef struct spelload_S {
- char_u sl_lang[MAXWLEN + 1]; // language name
+ char sl_lang[MAXWLEN + 1]; // language name
slang_T *sl_slang; // resulting slang_T struct
int sl_nobreak; // NOBREAK language found
} spelload_T;
#define SY_MAXLEN 30
typedef struct syl_item_S {
- char_u sy_chars[SY_MAXLEN]; // the sequence of chars
+ char sy_chars[SY_MAXLEN]; // the sequence of chars
int sy_len;
} syl_item_T;
@@ -184,8 +194,8 @@ int did_set_spelltab;
char *e_format = N_("E759: Format error in spell file");
// Remember what "z?" replaced.
-char_u *repl_from = NULL;
-char_u *repl_to = NULL;
+char *repl_from = NULL;
+char *repl_to = NULL;
/// Main spell-checking function.
/// "ptr" points to a character that could be the start of a word.
@@ -204,21 +214,19 @@ char_u *repl_to = NULL;
///
/// @return the length of the word in bytes, also when it's OK, so that the
/// caller can skip over the word.
-size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docount)
+size_t spell_check(win_T *wp, char *ptr, hlf_T *attrp, int *capcol, bool docount)
{
matchinf_T mi; // Most things are put in "mi" so that it can
// be passed to functions quickly.
size_t nrlen = 0; // found a number first
- int c;
size_t wrongcaplen = 0;
- int lpi;
bool count_word = docount;
- bool use_camel_case = *wp->w_s->b_p_spo != NUL;
+ bool use_camel_case = (wp->w_s->b_p_spo_flags & SPO_CAMEL) != 0;
bool camel_case = false;
// A word never starts at a space or a control character. Return quickly
// then, skipping over the character.
- if (*ptr <= ' ') {
+ if ((uint8_t)(*ptr) <= ' ') {
return 1;
}
@@ -234,11 +242,11 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou
// julifeest".
if (*ptr >= '0' && *ptr <= '9') {
if (*ptr == '0' && (ptr[1] == 'b' || ptr[1] == 'B')) {
- mi.mi_end = (char_u *)skipbin((char *)ptr + 2);
+ mi.mi_end = (char *)skipbin(ptr + 2);
} else if (*ptr == '0' && (ptr[1] == 'x' || ptr[1] == 'X')) {
mi.mi_end = skiphex(ptr + 2);
} else {
- mi.mi_end = (char_u *)skipdigits((char *)ptr);
+ mi.mi_end = skipdigits(ptr);
}
nrlen = (size_t)(mi.mi_end - ptr);
}
@@ -250,7 +258,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((char *)mi.mi_fend);
+ int c = utf_ptr2char(mi.mi_fend);
this_upper = SPELL_ISUPPER(c);
}
@@ -258,7 +266,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((char *)mi.mi_fend);
+ int c = utf_ptr2char(mi.mi_fend);
this_upper = SPELL_ISUPPER(c);
camel_case = !prev_upper && this_upper;
}
@@ -267,7 +275,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((char *)ptr);
+ int c = utf_ptr2char(ptr);
if (!SPELL_ISUPPER(c)) {
wrongcaplen = (size_t)(mi.mi_fend - ptr);
}
@@ -294,7 +302,7 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou
(void)spell_casefold(wp, ptr, (int)(mi.mi_fend - ptr), mi.mi_fword,
MAXWLEN + 1);
- mi.mi_fwordlen = (int)STRLEN(mi.mi_fword);
+ mi.mi_fwordlen = (int)strlen(mi.mi_fword);
if (camel_case && mi.mi_fwordlen > 0) {
// introduce a fake word end space into the folded word.
@@ -308,7 +316,7 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou
// Loop over the languages specified in 'spelllang'.
// We check them all, because a word may be matched longer in another
// language.
- for (lpi = 0; lpi < wp->w_s->b_langp.ga_len; ++lpi) {
+ for (int lpi = 0; lpi < wp->w_s->b_langp.ga_len; lpi++) {
mi.mi_lp = LANGP_ENTRY(wp->w_s->b_langp, lpi);
// If reloading fails the language is still in the list but everything
@@ -358,21 +366,21 @@ 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, (char *)ptr, 0);
+ int r = vim_regexec(&regmatch, ptr, 0);
wp->w_s->b_cap_prog = regmatch.regprog;
if (r) {
*capcol = (int)(regmatch.endp[0] - ptr);
}
}
- return (size_t)(utfc_ptr2len((char *)ptr));
+ return (size_t)(utfc_ptr2len(ptr));
} else if (mi.mi_end == ptr) {
// Always include at least one character. Required for when there
// is a mixup in "midword".
MB_PTR_ADV(mi.mi_end);
} else if (mi.mi_result == SP_BAD
&& LANGP_ENTRY(wp->w_s->b_langp, 0)->lp_slang->sl_nobreak) {
- char_u *p, *fp;
+ char *p, *fp;
int save_result = mi.mi_result;
// First language in 'spelllang' is NOBREAK. Find first position
@@ -427,7 +435,7 @@ static void find_word(matchinf_T *mip, int mode)
{
int wlen = 0;
int flen;
- char_u *ptr;
+ char *ptr;
slang_T *slang = mip->mi_lp->lp_slang;
char_u *byts;
idx_T *idxs;
@@ -436,7 +444,7 @@ static void find_word(matchinf_T *mip, int mode)
// Check for word with matching case in keep-case tree.
ptr = mip->mi_word;
flen = 9999; // no case folding, always enough bytes
- byts = slang->sl_kbyts;
+ byts = (char_u *)slang->sl_kbyts;
idxs = slang->sl_kidxs;
if (mode == FIND_KEEPCOMPOUND) {
@@ -447,7 +455,7 @@ static void find_word(matchinf_T *mip, int mode)
// Check for case-folded in case-folded tree.
ptr = mip->mi_fword;
flen = mip->mi_fwordlen; // available case-folded bytes
- byts = slang->sl_fbyts;
+ byts = (char_u *)slang->sl_fbyts;
idxs = slang->sl_fidxs;
if (mode == FIND_PREFIX) {
@@ -468,7 +476,6 @@ static void find_word(matchinf_T *mip, int mode)
int endlen[MAXWLEN]; // length at possible word endings
idx_T endidx[MAXWLEN]; // possible word endings
int endidxcnt = 0;
- int len;
int c;
// Repeat advancing in the tree until:
@@ -480,7 +487,7 @@ static void find_word(matchinf_T *mip, int mode)
flen = fold_more(mip);
}
- len = byts[arridx++];
+ int len = byts[arridx++];
// If the first possible byte is a zero the word could end here.
// Remember this index, we first check for the longest word.
@@ -511,7 +518,7 @@ static void find_word(matchinf_T *mip, int mode)
}
// Perform a binary search in the list of accepted bytes.
- c = ptr[wlen];
+ c = (uint8_t)ptr[wlen];
if (c == TAB) { // <Tab> is handled like <Space>
c = ' ';
}
@@ -555,7 +562,7 @@ static void find_word(matchinf_T *mip, int mode)
}
}
- char_u *p;
+ char *p;
bool word_ends;
// Verify that one of the possible endings is valid. Try the longest
@@ -585,8 +592,8 @@ static void find_word(matchinf_T *mip, int mode)
// when folding case. This can be slow, take a shortcut when the
// case-folded word is equal to the keep-case word.
p = mip->mi_word;
- if (STRNCMP(ptr, p, wlen) != 0) {
- for (char_u *s = ptr; s < ptr + wlen; MB_PTR_ADV(s)) {
+ if (strncmp(ptr, p, (size_t)wlen) != 0) {
+ for (char *s = ptr; s < ptr + wlen; MB_PTR_ADV(s)) {
MB_PTR_ADV(p);
}
wlen = (int)(p - mip->mi_word);
@@ -597,7 +604,7 @@ static void find_word(matchinf_T *mip, int mode)
// prefix ID.
// Repeat this if there are more flags/region alternatives until there
// is a match.
- for (len = byts[arridx - 1]; len > 0 && byts[arridx] == 0; len--, arridx++) {
+ for (int len = byts[arridx - 1]; len > 0 && byts[arridx] == 0; len--, arridx++) {
uint32_t flags = (uint32_t)idxs[arridx];
// For the fold-case tree check that the case of the checked word
@@ -616,11 +623,10 @@ static void find_word(matchinf_T *mip, int mode)
|| !spell_valid_case(mip->mi_capflags, (int)flags)) {
continue;
}
- }
- // When mode is FIND_PREFIX the word must support the prefix:
- // check the prefix ID and the condition. Do that for the list at
- // mip->mi_prefarridx that find_prefix() filled.
- else if (mode == FIND_PREFIX && !prefix_found) {
+ } else if (mode == FIND_PREFIX && !prefix_found) {
+ // When mode is FIND_PREFIX the word must support the prefix:
+ // check the prefix ID and the condition. Do that for the list at
+ // mip->mi_prefarridx that find_prefix() filled.
c = valid_word_prefix(mip->mi_prefcnt, mip->mi_prefarridx,
(int)flags,
mip->mi_word + mip->mi_cprefixlen, slang,
@@ -681,7 +687,8 @@ static void find_word(matchinf_T *mip, int mode)
}
// Quickly check if compounding is possible with this flag.
- if (!byte_in_str(mip->mi_complen == 0 ? slang->sl_compstartflags : slang->sl_compallflags,
+ if (!byte_in_str(mip->mi_complen ==
+ 0 ? slang->sl_compstartflags : slang->sl_compallflags,
(int)((unsigned)flags >> 24))) {
continue;
}
@@ -697,10 +704,10 @@ static void find_word(matchinf_T *mip, int mode)
// Need to check the caps type of the appended compound
// word.
- if (STRNCMP(ptr, mip->mi_word, mip->mi_compoff) != 0) {
+ if (strncmp(ptr, mip->mi_word, (size_t)mip->mi_compoff) != 0) {
// case folding may have changed the length
p = mip->mi_word;
- for (char_u *s = ptr; s < ptr + mip->mi_compoff; MB_PTR_ADV(s)) {
+ for (char *s = ptr; s < ptr + mip->mi_compoff; MB_PTR_ADV(s)) {
MB_PTR_ADV(p);
}
} else {
@@ -733,14 +740,14 @@ static void find_word(matchinf_T *mip, int mode)
mip->mi_compflags[mip->mi_complen] = (char_u)((unsigned)flags >> 24);
mip->mi_compflags[mip->mi_complen + 1] = NUL;
if (word_ends) {
- char_u fword[MAXWLEN] = { 0 };
+ char fword[MAXWLEN] = { 0 };
if (slang->sl_compsylmax < MAXWLEN) {
// "fword" is only needed for checking syllables.
if (ptr == mip->mi_word) {
(void)spell_casefold(mip->mi_win, ptr, wlen, fword, MAXWLEN);
} else {
- STRLCPY(fword, ptr, endlen[endidxcnt] + 1);
+ xstrlcpy(fword, ptr, (size_t)endlen[endidxcnt] + 1);
}
}
if (!can_compound(slang, fword, mip->mi_compflags)) {
@@ -752,9 +759,8 @@ static void find_word(matchinf_T *mip, int mode)
// COMPOUNDRULE, discard the compounded word.
continue;
}
- }
- // Check NEEDCOMPOUND: can't use word without compounding.
- else if (flags & WF_NEEDCOMP) {
+ } else if (flags & WF_NEEDCOMP) {
+ // skip if word is only valid in a compound
continue;
}
@@ -762,7 +768,7 @@ static void find_word(matchinf_T *mip, int mode)
if (!word_ends) {
int save_result = mip->mi_result;
- char_u *save_end = mip->mi_end;
+ char *save_end = mip->mi_end;
langp_T *save_lp = mip->mi_lp;
// Check that a valid word follows. If there is one and we
@@ -782,8 +788,8 @@ static void find_word(matchinf_T *mip, int mode)
// folding case. This can be slow, take a shortcut when
// the case-folded word is equal to the keep-case word.
p = mip->mi_fword;
- if (STRNCMP(ptr, p, wlen) != 0) {
- for (char_u *s = ptr; s < ptr + wlen; MB_PTR_ADV(s)) {
+ if (strncmp(ptr, p, (size_t)wlen) != 0) {
+ for (char *s = ptr; s < ptr + wlen; MB_PTR_ADV(s)) {
MB_PTR_ADV(p);
}
mip->mi_compoff = (int)(p - mip->mi_fword);
@@ -792,14 +798,14 @@ static void find_word(matchinf_T *mip, int mode)
#if 0
c = mip->mi_compoff;
#endif
- ++mip->mi_complen;
+ mip->mi_complen++;
if (flags & WF_COMPROOT) {
- ++mip->mi_compextra;
+ mip->mi_compextra++;
}
// For NOBREAK we need to try all NOBREAK languages, at least
// to find the ".add" file(s).
- for (int lpi = 0; lpi < mip->mi_win->w_s->b_langp.ga_len; ++lpi) {
+ for (int lpi = 0; lpi < mip->mi_win->w_s->b_langp.ga_len; lpi++) {
if (slang->sl_nobreak) {
mip->mi_lp = LANGP_ENTRY(mip->mi_win->w_s->b_langp, lpi);
if (mip->mi_lp->lp_slang->sl_fidxs == NULL
@@ -833,9 +839,9 @@ static void find_word(matchinf_T *mip, int mode)
break;
}
}
- --mip->mi_complen;
+ mip->mi_complen--;
if (flags & WF_COMPROOT) {
- --mip->mi_compextra;
+ mip->mi_compextra--;
}
mip->mi_lp = save_lp;
@@ -903,19 +909,16 @@ static void find_word(matchinf_T *mip, int mode)
/// end of ptr[wlen] and the second part matches after it.
///
/// @param gap &sl_comppat
-bool match_checkcompoundpattern(char_u *ptr, int wlen, garray_T *gap)
+bool match_checkcompoundpattern(char *ptr, int wlen, garray_T *gap)
{
- char_u *p;
- int len;
-
for (int i = 0; i + 1 < gap->ga_len; i += 2) {
- p = ((char_u **)gap->ga_data)[i + 1];
- if (STRNCMP(ptr + wlen, p, STRLEN(p)) == 0) {
+ char *p = ((char **)gap->ga_data)[i + 1];
+ if (strncmp(ptr + wlen, p, strlen(p)) == 0) {
// Second part matches at start of following compound word, now
// check if first part matches at end of previous word.
- p = ((char_u **)gap->ga_data)[i];
- len = (int)STRLEN(p);
- if (len <= wlen && STRNCMP(ptr + wlen - len, p, len) == 0) {
+ p = ((char **)gap->ga_data)[i];
+ int len = (int)strlen(p);
+ if (len <= wlen && strncmp(ptr + wlen - len, p, (size_t)len) == 0) {
return true;
}
}
@@ -923,20 +926,20 @@ bool match_checkcompoundpattern(char_u *ptr, int wlen, garray_T *gap)
return false;
}
-// Returns true if "flags" is a valid sequence of compound flags and "word"
-// does not have too many syllables.
-bool can_compound(slang_T *slang, const char_u *word, const char_u *flags)
+/// @return true if "flags" is a valid sequence of compound flags and "word"
+/// does not have too many syllables.
+bool can_compound(slang_T *slang, const char *word, const uint8_t *flags)
FUNC_ATTR_NONNULL_ALL
{
- char_u uflags[MAXWLEN * 2] = { 0 };
+ char uflags[MAXWLEN * 2] = { 0 };
if (slang->sl_compprog == NULL) {
return false;
}
// Need to convert the single byte flags to utf8 characters.
- char_u *p = uflags;
+ char *p = uflags;
for (int i = 0; flags[i] != NUL; i++) {
- p += utf_char2bytes(flags[i], (char *)p);
+ p += utf_char2bytes(flags[i], p);
}
*p = NUL;
p = uflags;
@@ -949,7 +952,7 @@ bool can_compound(slang_T *slang, const char_u *word, const char_u *flags)
// COMPOUNDWORDMAX then compounding is not allowed.
if (slang->sl_compsylmax < MAXWLEN
&& count_syllables(slang, word) > slang->sl_compsylmax) {
- return (int)STRLEN(flags) < slang->sl_compmax;
+ return (int)strlen((char *)flags) < slang->sl_compmax;
}
return true;
}
@@ -958,18 +961,14 @@ bool can_compound(slang_T *slang, const char_u *word, const char_u *flags)
// compound rule. This is used to stop trying a compound if the flags
// collected so far can't possibly match any compound rule.
// Caller must check that slang->sl_comprules is not NULL.
-bool match_compoundrule(slang_T *slang, char_u *compflags)
+bool match_compoundrule(slang_T *slang, const char_u *compflags)
{
- char_u *p;
- int i;
- int c;
-
// loop over all the COMPOUNDRULE entries
- for (p = slang->sl_comprules; *p != NUL; ++p) {
+ for (char_u *p = (char_u *)slang->sl_comprules; *p != NUL; p++) {
// loop over the flags in the compound word we have made, match
// them against the current rule entry
- for (i = 0;; ++i) {
- c = compflags[i];
+ for (int i = 0;; i++) {
+ int c = compflags[i];
if (c == NUL) {
// found a rule that matches for the flags we have so far
return true;
@@ -1015,15 +1014,12 @@ bool match_compoundrule(slang_T *slang, char_u *compflags)
/// @param totprefcnt nr of prefix IDs
/// @param arridx idx in sl_pidxs[]
/// @param cond_req only use prefixes with a condition
-int valid_word_prefix(int totprefcnt, int arridx, int flags, char_u *word, slang_T *slang,
+int valid_word_prefix(int totprefcnt, int arridx, int flags, char *word, slang_T *slang,
bool cond_req)
{
- int prefcnt;
- int pidx;
-
int prefid = (int)((unsigned)flags >> 24);
- for (prefcnt = totprefcnt - 1; prefcnt >= 0; prefcnt--) {
- pidx = slang->sl_pidxs[arridx + prefcnt];
+ for (int prefcnt = totprefcnt - 1; prefcnt >= 0; prefcnt--) {
+ int pidx = slang->sl_pidxs[arridx + prefcnt];
// Check the prefix ID.
if (prefid != (pidx & 0xff)) {
@@ -1063,30 +1059,23 @@ int valid_word_prefix(int totprefcnt, int arridx, int flags, char_u *word, slang
static void find_prefix(matchinf_T *mip, int mode)
{
idx_T arridx = 0;
- int len;
int wlen = 0;
- int flen;
- int c;
- char_u *ptr;
- idx_T lo, hi, m;
slang_T *slang = mip->mi_lp->lp_slang;
- char_u *byts;
- idx_T *idxs;
- byts = slang->sl_pbyts;
+ char_u *byts = (char_u *)slang->sl_pbyts;
if (byts == NULL) {
return; // array is empty
}
// We use the case-folded word here, since prefixes are always
// case-folded.
- ptr = mip->mi_fword;
- flen = mip->mi_fwordlen; // available case-folded bytes
+ char_u *ptr = (char_u *)mip->mi_fword;
+ int flen = mip->mi_fwordlen; // available case-folded bytes
if (mode == FIND_COMPOUND) {
// Skip over the previously found word(s).
ptr += mip->mi_compoff;
flen -= mip->mi_compoff;
}
- idxs = slang->sl_pidxs;
+ idx_T *idxs = slang->sl_pidxs;
// Repeat advancing in the tree until:
// - there is a byte that doesn't match,
@@ -1097,7 +1086,7 @@ static void find_prefix(matchinf_T *mip, int mode)
flen = fold_more(mip);
}
- len = byts[arridx++];
+ int len = byts[arridx++];
// If the first possible byte is a zero the prefix could end here.
// Check if the following word matches and supports the prefix.
@@ -1137,11 +1126,11 @@ static void find_prefix(matchinf_T *mip, int mode)
}
// Perform a binary search in the list of accepted bytes.
- c = ptr[wlen];
- lo = arridx;
- hi = arridx + len - 1;
+ int c = ptr[wlen];
+ idx_T lo = arridx;
+ idx_T hi = arridx + len - 1;
while (lo < hi) {
- m = (lo + hi) / 2;
+ idx_T m = (lo + hi) / 2;
if (byts[m] > c) {
hi = m - 1;
} else if (byts[m] < c) {
@@ -1169,10 +1158,7 @@ static void find_prefix(matchinf_T *mip, int mode)
// Return the length of the folded chars in bytes.
static int fold_more(matchinf_T *mip)
{
- int flen;
- char_u *p;
-
- p = mip->mi_fend;
+ char *p = mip->mi_fend;
do {
MB_PTR_ADV(mip->mi_fend);
} while (*mip->mi_fend != NUL && spell_iswordp(mip->mi_fend, mip->mi_win));
@@ -1185,7 +1171,7 @@ static int fold_more(matchinf_T *mip)
(void)spell_casefold(mip->mi_win, p, (int)(mip->mi_fend - p),
mip->mi_fword + mip->mi_fwordlen,
MAXWLEN - mip->mi_fwordlen);
- flen = (int)STRLEN(mip->mi_fword + mip->mi_fwordlen);
+ int flen = (int)strlen(mip->mi_fword + mip->mi_fwordlen);
mip->mi_fwordlen += flen;
return flen;
}
@@ -1214,6 +1200,31 @@ bool no_spell_checking(win_T *wp)
return false;
}
+static void decor_spell_nav_start(win_T *wp)
+{
+ decor_state = (DecorState){ 0 };
+ decor_redraw_reset(wp->w_buffer, &decor_state);
+}
+
+static bool decor_spell_nav_col(win_T *wp, linenr_T lnum, linenr_T *decor_lnum, int col,
+ char **decor_error)
+{
+ if (*decor_lnum != lnum) {
+ decor_providers_invoke_spell(wp, lnum - 1, col, lnum - 1, -1, decor_error);
+ decor_redraw_line(wp->w_buffer, lnum - 1, &decor_state);
+ *decor_lnum = lnum;
+ }
+ decor_redraw_col(wp->w_buffer, col, col, false, &decor_state);
+ return decor_state.spell == kTrue;
+}
+
+static inline bool can_syn_spell(win_T *wp, linenr_T lnum, int col)
+{
+ bool can_spell;
+ (void)syn_get_id(wp, lnum, col, false, &can_spell, false);
+ return can_spell;
+}
+
/// Moves to the next spell error.
/// "curline" is false for "[s", "]s", "[S" and "]S".
/// "curline" is true to find word under/after cursor in the same line.
@@ -1227,21 +1238,16 @@ bool no_spell_checking(win_T *wp)
/// @return 0 if not found, length of the badly spelled word otherwise.
size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *attrp)
{
- linenr_T lnum;
pos_T found_pos;
size_t found_len = 0;
- char_u *line;
- char_u *p;
- char_u *endp;
hlf_T attr = HLF_COUNT;
size_t len;
int has_syntax = syntax_present(wp);
- int col;
- bool can_spell;
- char_u *buf = NULL;
+ colnr_T col;
+ char *buf = NULL;
size_t buflen = 0;
int skip = 0;
- int capcol = -1;
+ colnr_T capcol = -1;
bool found_one = false;
bool wrapped = false;
@@ -1249,6 +1255,8 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
return 0;
}
+ size_t ret = 0;
+
// Start looking for bad word at the start of the line, because we can't
// start halfway through a word, we don't know where it starts or ends.
//
@@ -1258,13 +1266,26 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
// We concatenate the start of the next line, so that wrapped words work
// (e.g. "et<line-break>cetera"). Doesn't work when searching backwards
// though...
- lnum = wp->w_cursor.lnum;
+ linenr_T lnum = wp->w_cursor.lnum;
clearpos(&found_pos);
+ char *decor_error = NULL;
+ // Ephemeral extmarks are currently stored in the global decor_state.
+ // When looking for spell errors, we need to:
+ // - temporarily reset decor_state
+ // - run the _on_spell_nav decor callback for each line we look at
+ // - detect if any spell marks are present
+ // - restore decor_state to the value saved here.
+ // TODO(lewis6991): un-globalize decor_state and allow ephemeral marks to be stored into a
+ // temporary DecorState.
+ DecorState saved_decor_start = decor_state;
+ linenr_T decor_lnum = -1;
+ decor_spell_nav_start(wp);
+
while (!got_int) {
- line = ml_get_buf(wp->w_buffer, lnum, false);
+ char *line = ml_get_buf(wp->w_buffer, lnum, false);
- len = STRLEN(line);
+ len = strlen(line);
if (buflen < len + MAXWLEN + 2) {
xfree(buf);
buflen = len + MAXWLEN + 2;
@@ -1279,10 +1300,10 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
// For checking first word with a capital skip white space.
if (capcol == 0) {
- capcol = (int)getwhitecols(line);
+ capcol = (colnr_T)getwhitecols(line);
} else if (curline && wp == curwin) {
// For spellbadword(): check if first word needs a capital.
- col = (int)getwhitecols(line);
+ col = (colnr_T)getwhitecols(line);
if (check_need_cap(lnum, col)) {
capcol = col;
}
@@ -1298,12 +1319,12 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
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),
+ spell_cat_line(buf + strlen(buf),
ml_get_buf(wp->w_buffer, lnum + 1, false),
MAXWLEN);
}
- p = buf + skip;
- endp = buf + len;
+ char *p = buf + skip;
+ char *endp = buf + len;
while (p < endp) {
// When searching backward don't search after the cursor. Unless
// we wrapped around the end of the buffer.
@@ -1329,30 +1350,31 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
|| ((colnr_T)(curline
? p - buf + (ptrdiff_t)len
: p - buf) > wp->w_cursor.col)) {
- if (has_syntax) {
- col = (int)(p - buf);
- (void)syn_get_id(wp, lnum, (colnr_T)col,
- FALSE, &can_spell, FALSE);
- if (!can_spell) {
- attr = HLF_COUNT;
- }
- } else {
- can_spell = true;
+ col = (colnr_T)(p - buf);
+
+ bool can_spell = (!has_syntax && (wp->w_s->b_p_spo_flags & SPO_NPBUFFER) == 0)
+ || decor_spell_nav_col(wp, lnum, &decor_lnum, col, &decor_error)
+ || (has_syntax && can_syn_spell(wp, lnum, col));
+
+ if (!can_spell) {
+ attr = HLF_COUNT;
}
if (can_spell) {
found_one = true;
- found_pos.lnum = lnum;
- found_pos.col = (int)(p - buf);
- found_pos.coladd = 0;
+ found_pos = (pos_T) {
+ .lnum = lnum,
+ .col = col,
+ .coladd = 0
+ };
if (dir == FORWARD) {
// No need to search further.
wp->w_cursor = found_pos;
- xfree(buf);
if (attrp != NULL) {
*attrp = attr;
}
- return len;
+ ret = len;
+ goto theend;
} else if (curline) {
// Insert mode completion: put cursor after
// the bad word.
@@ -1376,8 +1398,8 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
if (dir == BACKWARD && found_pos.lnum != 0) {
// Use the last match in the line (before the cursor).
wp->w_cursor = found_pos;
- xfree(buf);
- return found_len;
+ ret = found_len;
+ goto theend;
}
if (curline) {
@@ -1447,43 +1469,45 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
line_breakcheck();
}
+theend:
+ decor_state_free(&decor_state);
+ xfree(decor_error);
+ decor_state = saved_decor_start;
xfree(buf);
- return 0;
+ return ret;
}
// For spell checking: concatenate the start of the following line "line" into
// "buf", blanking-out special characters. Copy less than "maxlen" bytes.
// Keep the blanks at the start of the next line, this is used in win_line()
// to skip those bytes if the word was OK.
-void spell_cat_line(char_u *buf, char_u *line, int maxlen)
+void spell_cat_line(char *buf, char *line, int maxlen)
{
- char_u *p;
- int n;
-
- p = (char_u *)skipwhite((char *)line);
- while (vim_strchr("*#/\"\t", *p) != NULL) {
+ char_u *p = (char_u *)skipwhite(line);
+ while (vim_strchr("*#/\"\t", (uint8_t)(*p)) != NULL) {
p = (char_u *)skipwhite((char *)p + 1);
}
- if (*p != NUL) {
- // Only worth concatenating if there is something else than spaces to
- // concatenate.
- n = (int)(p - line) + 1;
- if (n < maxlen - 1) {
- memset(buf, ' ', (size_t)n);
- STRLCPY(buf + n, p, maxlen - n);
- }
+ if (*p == NUL) {
+ return;
+ }
+
+ // Only worth concatenating if there is something else than spaces to
+ // concatenate.
+ int n = (int)(p - (char_u *)line) + 1;
+ if (n < maxlen - 1) {
+ memset(buf, ' ', (size_t)n);
+ xstrlcpy(buf + n, (char *)p, (size_t)(maxlen - n));
}
}
// Load word list(s) for "lang" from Vim spell file(s).
// "lang" must be the language without the region: e.g., "en".
-static void spell_load_lang(char_u *lang)
+static void spell_load_lang(char *lang)
{
char fname_enc[85];
int r;
spelload_T sl;
- int round;
// Copy the language name to pass it to spell_load_cb() as a cookie.
// It's truncated when an error is detected.
@@ -1491,22 +1515,26 @@ static void spell_load_lang(char_u *lang)
sl.sl_slang = NULL;
sl.sl_nobreak = false;
+ // Disallow deleting the current buffer. Autocommands can do weird things
+ // and cause "lang" to be freed.
+ curbuf->b_locked++;
+
// We may retry when no spell file is found for the language, an
// autocommand may load it then.
- for (round = 1; round <= 2; ++round) {
+ for (int round = 1; round <= 2; round++) {
// Find the first spell file for "lang" in 'runtimepath' and load it.
- vim_snprintf((char *)fname_enc, sizeof(fname_enc) - 5,
+ vim_snprintf(fname_enc, sizeof(fname_enc) - 5,
"spell/%s.%s.spl", lang, spell_enc());
- r = do_in_runtimepath((char *)fname_enc, 0, spell_load_cb, &sl);
+ r = do_in_runtimepath(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,
+ vim_snprintf(fname_enc, sizeof(fname_enc) - 5,
"spell/%s.ascii.spl", lang);
- r = do_in_runtimepath((char *)fname_enc, 0, spell_load_cb, &sl);
+ r = do_in_runtimepath(fname_enc, 0, spell_load_cb, &sl);
if (r == FAIL && *sl.sl_lang != NUL && round == 1
- && apply_autocmds(EVENT_SPELLFILEMISSING, (char *)lang,
+ && apply_autocmds(EVENT_SPELLFILEMISSING, lang,
curbuf->b_fname, false, curbuf)) {
continue;
}
@@ -1530,38 +1558,40 @@ 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((char *)fname_enc, DIP_ALL, spell_load_cb, &sl);
+ STRCPY(fname_enc + strlen(fname_enc) - 3, "add.spl");
+ do_in_runtimepath(fname_enc, DIP_ALL, spell_load_cb, &sl);
}
+
+ curbuf->b_locked--;
}
// Return the encoding used for spell checking: Use 'encoding', except that we
// use "latin1" for "latin9". And limit to 60 characters (just in case).
-char_u *spell_enc(void)
+char *spell_enc(void)
{
- if (STRLEN(p_enc) < 60 && STRCMP(p_enc, "iso-8859-15") != 0) {
+ if (strlen(p_enc) < 60 && strcmp(p_enc, "iso-8859-15") != 0) {
return p_enc;
}
- return (char_u *)"latin1";
+ return "latin1";
}
// Get the name of the .spl file for the internal wordlist into
// "fname[MAXPATHL]".
-static void int_wordlist_spl(char_u *fname)
+static void int_wordlist_spl(char *fname)
{
- vim_snprintf((char *)fname, MAXPATHL, SPL_FNAME_TMPL,
+ vim_snprintf(fname, MAXPATHL, SPL_FNAME_TMPL,
int_wordlist, spell_enc());
}
-// Allocate a new slang_T for language "lang". "lang" can be NULL.
-// Caller must fill "sl_next".
-slang_T *slang_alloc(char_u *lang)
+/// Allocate a new slang_T for language "lang". "lang" can be NULL.
+/// Caller must fill "sl_next".
+slang_T *slang_alloc(char *lang)
FUNC_ATTR_NONNULL_RET
{
slang_T *lp = xcalloc(1, sizeof(slang_T));
if (lang != NULL) {
- lp->sl_name = vim_strsave(lang);
+ lp->sl_name = xstrdup(lang);
}
ga_init(&lp->sl_rep, sizeof(fromto_T), 10);
ga_init(&lp->sl_repsal, sizeof(fromto_T), 10);
@@ -1624,7 +1654,7 @@ void slang_clear(slang_T *lp)
GA_DEEP_CLEAR(gap, salitem_T, free_salitem);
}
- for (int i = 0; i < lp->sl_prefixcnt; ++i) {
+ for (int i = 0; i < lp->sl_prefixcnt; i++) {
vim_regfree(lp->sl_prefprog[i]);
}
lp->sl_prefixcnt = 0;
@@ -1673,20 +1703,20 @@ void slang_clear_sug(slang_T *lp)
static void spell_load_cb(char *fname, void *cookie)
{
spelload_T *slp = (spelload_T *)cookie;
- slang_T *slang;
-
- 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.
- if (slp->sl_nobreak && slang->sl_add) {
- slang->sl_nobreak = true;
- } else if (slang->sl_nobreak) {
- slp->sl_nobreak = true;
- }
+ slang_T *slang = spell_load_file(fname, slp->sl_lang, NULL, false);
+ if (slang == NULL) {
+ return;
+ }
- slp->sl_slang = slang;
+ // When a previously loaded file has NOBREAK also use it for the
+ // ".add" files.
+ if (slp->sl_nobreak && slang->sl_add) {
+ slang->sl_nobreak = true;
+ } else if (slang->sl_nobreak) {
+ slp->sl_nobreak = true;
}
+
+ slp->sl_slang = slang;
}
/// Add a word to the hashtable of common words.
@@ -1696,31 +1726,29 @@ static void spell_load_cb(char *fname, void *cookie)
/// @param[in] word added to common words hashtable
/// @param[in] len length of word or -1 for NUL terminated
/// @param[in] count 1 to count once, 10 to init
-void count_common_word(slang_T *lp, char_u *word, int len, uint8_t count)
+void count_common_word(slang_T *lp, char *word, int len, uint8_t count)
{
- hash_T hash;
- hashitem_T *hi;
- wordcount_T *wc;
- char_u buf[MAXWLEN];
- char_u *p;
+ char buf[MAXWLEN];
+ char *p;
if (len == -1) {
p = word;
} else if (len >= MAXWLEN) {
return;
} else {
- STRLCPY(buf, word, len + 1);
+ xstrlcpy(buf, word, (size_t)len + 1);
p = buf;
}
- hash = hash_hash(p);
- const size_t p_len = STRLEN(p);
- hi = hash_lookup(&lp->sl_wordcount, (const char *)p, p_len, hash);
+ wordcount_T *wc;
+ hash_T hash = hash_hash(p);
+ const size_t p_len = strlen(p);
+ hashitem_T *hi = hash_lookup(&lp->sl_wordcount, (const char *)p, p_len, hash);
if (HASHITEM_EMPTY(hi)) {
wc = xmalloc(sizeof(wordcount_T) + p_len);
memcpy(wc->wc_word, p, p_len + 1);
wc->wc_count = count;
- hash_add_item(&lp->sl_wordcount, hi, wc->wc_word, hash);
+ hash_add_item(&lp->sl_wordcount, hi, (char *)wc->wc_word, hash);
} else {
wc = HI2WC(hi);
wc->wc_count = (uint16_t)(wc->wc_count + count);
@@ -1732,11 +1760,9 @@ void count_common_word(slang_T *lp, char_u *word, int len, uint8_t count)
// Returns true if byte "n" appears in "str".
// Like strchr() but independent of locale.
-bool byte_in_str(char_u *str, int n)
+bool byte_in_str(uint8_t *str, int n)
{
- char_u *p;
-
- for (p = str; *p != NUL; ++p) {
+ for (uint8_t *p = str; *p != NUL; p++) {
if (*p == n) {
return true;
}
@@ -1748,21 +1774,18 @@ bool byte_in_str(char_u *str, int n)
// in "slang->sl_syl_items".
int init_syl_tab(slang_T *slang)
{
- char_u *p;
- char_u *s;
- int l;
-
ga_init(&slang->sl_syl_items, sizeof(syl_item_T), 4);
- p = (char_u *)vim_strchr((char *)slang->sl_syllable, '/');
+ char *p = vim_strchr((char *)slang->sl_syllable, '/');
while (p != NULL) {
*p++ = NUL;
if (*p == NUL) { // trailing slash
break;
}
- s = p;
- p = (char_u *)vim_strchr((char *)p, '/');
+ char *s = p;
+ p = vim_strchr(p, '/');
+ int l;
if (p == NULL) {
- l = (int)STRLEN(s);
+ l = (int)strlen(s);
} else {
l = (int)(p - s);
}
@@ -1771,7 +1794,7 @@ int init_syl_tab(slang_T *slang)
}
syl_item_T *syl = GA_APPEND_VIA_PTR(syl_item_T, &slang->sl_syl_items);
- STRLCPY(syl->sy_chars, s, l + 1);
+ xstrlcpy(syl->sy_chars, s, (size_t)l + 1);
syl->sy_len = l;
}
return OK;
@@ -1780,20 +1803,18 @@ int init_syl_tab(slang_T *slang)
// Count the number of syllables in "word".
// When "word" contains spaces the syllables after the last space are counted.
// Returns zero if syllables are not defines.
-static int count_syllables(slang_T *slang, const char_u *word)
+static int count_syllables(slang_T *slang, const char *word)
FUNC_ATTR_NONNULL_ALL
{
int cnt = 0;
bool skip = false;
int len;
- syl_item_T *syl;
- int c;
if (slang->sl_syllable == NULL) {
return 0;
}
- for (const char_u *p = word; *p != NUL; p += len) {
+ for (const char *p = word; *p != NUL; p += len) {
// When running into a space reset counter.
if (*p == ' ') {
len = 1;
@@ -1803,10 +1824,10 @@ static int count_syllables(slang_T *slang, const char_u *word)
// Find longest match of syllable items.
len = 0;
- for (int i = 0; i < slang->sl_syl_items.ga_len; ++i) {
- syl = ((syl_item_T *)slang->sl_syl_items.ga_data) + i;
+ for (int i = 0; i < slang->sl_syl_items.ga_len; i++) {
+ syl_item_T *syl = ((syl_item_T *)slang->sl_syl_items.ga_data) + i;
if (syl->sy_len > len
- && STRNCMP(p, syl->sy_chars, syl->sy_len) == 0) {
+ && strncmp(p, syl->sy_chars, (size_t)syl->sy_len) == 0) {
len = syl->sy_len;
}
}
@@ -1815,12 +1836,12 @@ 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((char *)p);
- len = utfc_ptr2len((char *)p);
+ int c = utf_ptr2char(p);
+ len = utfc_ptr2len(p);
if (vim_strchr((char *)slang->sl_syllable, c) == NULL) {
skip = false; // No, search for next syllable
} else if (!skip) {
- ++cnt; // Yes, count it
+ cnt++; // Yes, count it
skip = true; // don't count following syllable chars
}
}
@@ -1828,31 +1849,31 @@ static int count_syllables(slang_T *slang, const char_u *word)
return cnt;
}
-// Parse 'spelllang' and set w_s->b_langp accordingly.
-// Returns NULL if it's OK, an error message otherwise.
+/// Parse 'spelllang' and set w_s->b_langp accordingly.
+/// @return NULL if it's OK, an untranslated error message otherwise.
char *did_set_spelllang(win_T *wp)
{
garray_T ga;
char *splp;
- char_u *region;
- char_u region_cp[3];
+ char *region;
+ char region_cp[3];
bool filename;
int region_mask;
slang_T *slang;
int c;
- char_u lang[MAXWLEN + 1];
- char_u spf_name[MAXPATHL];
+ char lang[MAXWLEN + 1];
+ char spf_name[MAXPATHL];
int len;
- char_u *p;
+ char *p;
int round;
char *spf;
- char_u *use_region = NULL;
+ char *use_region = NULL;
bool dont_use_region = false;
bool nobreak = false;
langp_T *lp, *lp2;
static bool recursive = false;
char *ret_msg = NULL;
- char_u *spl_copy;
+ char *spl_copy;
bufref_T bufref;
set_bufref(&bufref, wp->w_buffer);
@@ -1870,22 +1891,22 @@ char *did_set_spelllang(win_T *wp)
// Make a copy of 'spelllang', the SpellFileMissing autocommands may change
// it under our fingers.
- spl_copy = vim_strsave(wp->w_s->b_p_spl);
+ spl_copy = xstrdup(wp->w_s->b_p_spl);
wp->w_s->b_cjk = 0;
// Loop over comma separated language names.
- for (splp = (char *)spl_copy; *splp != NUL;) {
+ for (splp = spl_copy; *splp != NUL;) {
// Get one language name.
- copy_option_part(&splp, (char *)lang, MAXWLEN, ",");
+ copy_option_part(&splp, lang, MAXWLEN, ",");
region = NULL;
- len = (int)STRLEN(lang);
+ len = (int)strlen(lang);
if (!valid_spelllang(lang)) {
continue;
}
- if (STRCMP(lang, "cjk") == 0) {
+ if (strcmp(lang, "cjk") == 0) {
wp->w_s->b_cjk = 1;
continue;
}
@@ -1893,14 +1914,14 @@ 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 && path_fnamecmp(lang + len - 4, ".spl") == 0) {
filename = true;
// Locate a region and remove it from the file name.
- p = (char_u *)vim_strchr(path_tail((char *)lang), '_');
+ p = vim_strchr(path_tail(lang), '_');
if (p != NULL && ASCII_ISALPHA(p[1]) && ASCII_ISALPHA(p[2])
&& !ASCII_ISALPHA(p[3])) {
- STRLCPY(region_cp, p + 1, 3);
+ xstrlcpy(region_cp, p + 1, 3);
memmove(p, p + 3, (size_t)(len - (p - lang) - 2));
region = region_cp;
} else {
@@ -1909,7 +1930,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((char *)lang, (char *)slang->sl_fname, false, true)
+ if (path_full_compare(lang, slang->sl_fname, false, true)
== kEqualFiles) {
break;
}
@@ -1934,7 +1955,7 @@ char *did_set_spelllang(win_T *wp)
if (region != NULL) {
// If the region differs from what was used before then don't
// use it for 'spellfile'.
- if (use_region != NULL && STRCMP(region, use_region) != 0) {
+ if (use_region != NULL && strcmp(region, use_region) != 0) {
dont_use_region = true;
}
use_region = region;
@@ -1947,8 +1968,8 @@ char *did_set_spelllang(win_T *wp)
} else {
spell_load_lang(lang);
// SpellFileMissing autocommands may do anything, including
- // destroying the buffer we are using...
- if (!bufref_valid(&bufref)) {
+ // destroying the buffer we are using or closing the window.
+ if (!bufref_valid(&bufref) || !win_valid_any_tab(wp)) {
ret_msg = N_("E797: SpellFileMissing autocommand deleted buffer");
goto theend;
}
@@ -1958,7 +1979,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((char *)lang, (char *)slang->sl_fname, false, true) == kEqualFiles
+ ? path_full_compare(lang, slang->sl_fname, false, true) == kEqualFiles
: STRICMP(lang, slang->sl_name) == 0) {
region_mask = REGION_ALL;
if (!filename && region != NULL) {
@@ -1999,7 +2020,7 @@ char *did_set_spelllang(win_T *wp)
// round 1: load first name in 'spellfile'.
// round 2: load second name in 'spellfile.
// etc.
- spf = (char *)curwin->w_s->b_p_spf;
+ spf = curwin->w_s->b_p_spf;
for (round = 0; round == 0 || *spf != NUL; round++) {
if (round == 0) {
// Internal wordlist, if there is one.
@@ -2009,14 +2030,14 @@ char *did_set_spelllang(win_T *wp)
int_wordlist_spl(spf_name);
} else {
// One entry in 'spellfile'.
- copy_option_part(&spf, (char *)spf_name, MAXPATHL - 5, ",");
+ copy_option_part(&spf, 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) {
+ for (c = 0; c < ga.ga_len; c++) {
p = LANGP_ENTRY(ga, c)->lp_slang->sl_fname;
if (p != NULL
- && path_full_compare((char *)spf_name, (char *)p, false, true) == kEqualFiles) {
+ && path_full_compare(spf_name, p, false, true) == kEqualFiles) {
break;
}
}
@@ -2027,7 +2048,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((char *)spf_name, (char *)slang->sl_fname, false, true)
+ if (path_full_compare(spf_name, slang->sl_fname, false, true)
== kEqualFiles) {
break;
}
@@ -2039,8 +2060,8 @@ char *did_set_spelllang(win_T *wp)
if (round == 0) {
STRCPY(lang, "internal wordlist");
} else {
- STRLCPY(lang, path_tail((char *)spf_name), MAXWLEN + 1);
- p = (char_u *)vim_strchr((char *)lang, '.');
+ xstrlcpy(lang, path_tail(spf_name), MAXWLEN + 1);
+ p = vim_strchr(lang, '.');
if (p != NULL) {
*p = NUL; // truncate at ".encoding.add"
}
@@ -2085,7 +2106,7 @@ char *did_set_spelllang(win_T *wp)
// For each language figure out what language to use for sound folding and
// REP items. If the language doesn't support it itself use another one
// with the same name. E.g. for "en-math" use "en".
- for (int i = 0; i < ga.ga_len; ++i) {
+ for (int i = 0; i < ga.ga_len; i++) {
lp = LANGP_ENTRY(ga, i);
// sound folding
@@ -2094,10 +2115,10 @@ char *did_set_spelllang(win_T *wp)
lp->lp_sallang = lp->lp_slang;
} else {
// find first similar language that does sound folding
- for (int j = 0; j < ga.ga_len; ++j) {
+ for (int j = 0; j < ga.ga_len; j++) {
lp2 = LANGP_ENTRY(ga, j);
if (!GA_EMPTY(&lp2->lp_slang->sl_sal)
- && STRNCMP(lp->lp_slang->sl_name,
+ && strncmp(lp->lp_slang->sl_name,
lp2->lp_slang->sl_name, 2) == 0) {
lp->lp_sallang = lp2->lp_slang;
break;
@@ -2111,10 +2132,10 @@ char *did_set_spelllang(win_T *wp)
lp->lp_replang = lp->lp_slang;
} else {
// find first similar language that has REP items
- for (int j = 0; j < ga.ga_len; ++j) {
+ for (int j = 0; j < ga.ga_len; j++) {
lp2 = LANGP_ENTRY(ga, j);
if (!GA_EMPTY(&lp2->lp_slang->sl_rep)
- && STRNCMP(lp->lp_slang->sl_name,
+ && strncmp(lp->lp_slang->sl_name,
lp2->lp_slang->sl_name, 2) == 0) {
lp->lp_replang = lp2->lp_slang;
break;
@@ -2122,7 +2143,7 @@ char *did_set_spelllang(win_T *wp)
}
}
}
- redraw_later(wp, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
theend:
xfree(spl_copy);
@@ -2137,8 +2158,8 @@ static void clear_midword(win_T *wp)
XFREE_CLEAR(wp->w_s->b_spell_ismw_mb);
}
-// Use the "sl_midword" field of language "lp" for buffer "buf".
-// They add up to any currently used midword characters.
+/// Use the "sl_midword" field of language "lp" for buffer "buf".
+/// They add up to any currently used midword characters.
static void use_midword(slang_T *lp, win_T *wp)
FUNC_ATTR_NONNULL_ALL
{
@@ -2146,21 +2167,21 @@ static void use_midword(slang_T *lp, win_T *wp)
return;
}
- for (char_u *p = lp->sl_midword; *p != NUL;) {
- const int c = utf_ptr2char((char *)p);
- const int l = utfc_ptr2len((char *)p);
+ for (char *p = (char *)lp->sl_midword; *p != NUL;) {
+ const int c = utf_ptr2char(p);
+ const int l = utfc_ptr2len(p);
if (c < 256 && l <= 2) {
wp->w_s->b_spell_ismw[c] = true;
} else if (wp->w_s->b_spell_ismw_mb == NULL) {
// First multi-byte char in "b_spell_ismw_mb".
- wp->w_s->b_spell_ismw_mb = vim_strnsave(p, (size_t)l);
+ wp->w_s->b_spell_ismw_mb = xstrnsave(p, (size_t)l);
} else {
// Append multi-byte chars to "b_spell_ismw_mb".
- const int n = (int)STRLEN(wp->w_s->b_spell_ismw_mb);
- char_u *bp = vim_strnsave(wp->w_s->b_spell_ismw_mb, (size_t)n + (size_t)l);
+ const int n = (int)strlen(wp->w_s->b_spell_ismw_mb);
+ char *bp = xstrnsave(wp->w_s->b_spell_ismw_mb, (size_t)n + (size_t)l);
xfree(wp->w_s->b_spell_ismw_mb);
wp->w_s->b_spell_ismw_mb = bp;
- STRLCPY(bp + n, p, l + 1);
+ xstrlcpy(bp + n, p, (size_t)l + 1);
}
p += l;
}
@@ -2169,7 +2190,7 @@ static void use_midword(slang_T *lp, win_T *wp)
// Find the region "region[2]" in "rp" (points to "sl_regions").
// Each region is simply stored as the two characters of its name.
// Returns the index if found (first is 0), REGION_ALL if not found.
-static int find_region(char_u *rp, char_u *region)
+static int find_region(const char *rp, const char *region)
{
int i;
@@ -2194,13 +2215,10 @@ static int find_region(char_u *rp, char_u *region)
/// @param[in] end End of word or NULL for NUL delimited string
///
/// @returns Case type of word
-int captype(char_u *word, char_u *end)
+int captype(char *word, const char *end)
FUNC_ATTR_NONNULL_ARG(1)
{
- char_u *p;
- int firstcap;
- bool allcap;
- bool past_second = false; // past second word char
+ char *p;
// find first letter
for (p = word; !spell_iswordp_nmw(p, curwin); MB_PTR_ADV(p)) {
@@ -2208,14 +2226,16 @@ int captype(char_u *word, char_u *end)
return 0; // only non-word characters, illegal word
}
}
- int c = mb_ptr2char_adv((const char_u **)&p);
- firstcap = allcap = SPELL_ISUPPER(c);
+ int c = mb_ptr2char_adv((const char **)&p);
+ bool allcap;
+ bool firstcap = allcap = SPELL_ISUPPER(c);
+ bool past_second = false; // past second word char
// Need to check all letters to find a word with mixed upper/lower.
// 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((char *)p);
+ c = utf_ptr2char(p);
if (!SPELL_ISUPPER(c)) {
// UUl -> KEEPCAP
if (past_second && allcap) {
@@ -2242,28 +2262,27 @@ int captype(char_u *word, char_u *end)
// Delete the internal wordlist and its .spl file.
void spell_delete_wordlist(void)
{
- char_u fname[MAXPATHL] = { 0 };
-
- if (int_wordlist != NULL) {
- os_remove((char *)int_wordlist);
- int_wordlist_spl(fname);
- os_remove((char *)fname);
- XFREE_CLEAR(int_wordlist);
+ if (int_wordlist == NULL) {
+ return;
}
+
+ char fname[MAXPATHL] = { 0 };
+ os_remove(int_wordlist);
+ int_wordlist_spl(fname);
+ os_remove(fname);
+ XFREE_CLEAR(int_wordlist);
}
// Free all languages.
void spell_free_all(void)
{
- slang_T *slang;
-
// Go through all buffers and handle 'spelllang'. <VN>
FOR_ALL_BUFFERS(buf) {
ga_clear(&buf->b_s.b_langp);
}
while (first_lang != NULL) {
- slang = first_lang;
+ slang_T *slang = first_lang;
first_lang = slang->sl_next;
slang_free(slang);
}
@@ -2319,37 +2338,37 @@ buf_T *open_spellbuf(void)
// Close the buffer used for spell info.
void close_spellbuf(buf_T *buf)
{
- if (buf != NULL) {
- ml_close(buf, TRUE);
- xfree(buf);
+ if (buf == NULL) {
+ return;
}
+
+ ml_close(buf, true);
+ xfree(buf);
}
// Init the chartab used for spelling for ASCII.
void clear_spell_chartab(spelltab_T *sp)
{
- int i;
-
// Init everything to false (zero).
CLEAR_FIELD(sp->st_isw);
CLEAR_FIELD(sp->st_isu);
- for (i = 0; i < 256; i++) {
+ for (int i = 0; i < 256; i++) {
sp->st_fold[i] = (char_u)i;
sp->st_upper[i] = (char_u)i;
}
// We include digits. A word shouldn't start with a digit, but handling
// that is done separately.
- for (i = '0'; i <= '9'; ++i) {
+ for (int i = '0'; i <= '9'; i++) {
sp->st_isw[i] = true;
}
- for (i = 'A'; i <= 'Z'; ++i) {
+ for (int i = 'A'; i <= 'Z'; i++) {
sp->st_isw[i] = true;
sp->st_isu[i] = true;
sp->st_fold[i] = (char_u)(i + 0x20);
}
- for (i = 'a'; i <= 'z'; ++i) {
+ for (int i = 'a'; i <= 'z'; i++) {
sp->st_isw[i] = true;
sp->st_upper[i] = (char_u)(i - 0x20);
}
@@ -2361,11 +2380,9 @@ void clear_spell_chartab(spelltab_T *sp)
// locale. For utf-8 we don't use isalpha() but our own functions.
void init_spell_chartab(void)
{
- int i;
-
did_set_spelltab = false;
clear_spell_chartab(&spelltab);
- for (i = 128; i < 256; i++) {
+ for (int i = 128; i < 256; i++) {
int f = utf_fold(i);
int u = mb_toupper(i);
@@ -2385,29 +2402,27 @@ void init_spell_chartab(void)
/// Thus this only works properly when past the first character of the word.
///
/// @param wp Buffer used.
-bool spell_iswordp(const char_u *p, const win_T *wp)
+bool spell_iswordp(const char *p, const win_T *wp)
FUNC_ATTR_NONNULL_ALL
{
- int c;
-
- const int l = utfc_ptr2len((char *)p);
- const char_u *s = p;
+ const int l = utfc_ptr2len(p);
+ const char *s = p;
if (l == 1) {
// be quick for ASCII
- if (wp->w_s->b_spell_ismw[*p]) {
+ if (wp->w_s->b_spell_ismw[(uint8_t)(*p)]) {
s = p + 1; // skip a mid-word character
}
} else {
- c = utf_ptr2char((char *)p);
+ int c = utf_ptr2char(p);
if (c < 256
? wp->w_s->b_spell_ismw[c]
: (wp->w_s->b_spell_ismw_mb != NULL
- && vim_strchr((char *)wp->w_s->b_spell_ismw_mb, c) != NULL)) {
+ && vim_strchr(wp->w_s->b_spell_ismw_mb, c) != NULL)) {
s = p + l;
}
}
- c = utf_ptr2char((char *)s);
+ int c = utf_ptr2char(s);
if (c > 255) {
return spell_mb_isword_class(mb_get_class(s), wp);
}
@@ -2416,9 +2431,9 @@ bool spell_iswordp(const char_u *p, const win_T *wp)
// Returns true if "p" points to a word character.
// Unlike spell_iswordp() this doesn't check for "midword" characters.
-bool spell_iswordp_nmw(const char_u *p, win_T *wp)
+bool spell_iswordp_nmw(const char *p, win_T *wp)
{
- int c = utf_ptr2char((char *)p);
+ int c = utf_ptr2char(p);
if (c > 255) {
return spell_mb_isword_class(mb_get_class(p), wp);
}
@@ -2448,7 +2463,7 @@ static bool spell_iswordp_w(const int *p, const win_T *wp)
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,
+ && vim_strchr(wp->w_s->b_spell_ismw_mb,
*p) != NULL)) {
s = p + 1;
} else {
@@ -2465,7 +2480,7 @@ static bool spell_iswordp_w(const int *p, const win_T *wp)
// Uses the character definitions from the .spl file.
// When using a multi-byte 'encoding' the length may change!
// Returns FAIL when something wrong.
-int spell_casefold(const win_T *wp, char_u *str, int len, char_u *buf, int buflen)
+int spell_casefold(const win_T *wp, char *str, int len, char *buf, int buflen)
FUNC_ATTR_NONNULL_ALL
{
if (len >= buflen) {
@@ -2476,12 +2491,12 @@ int spell_casefold(const win_T *wp, char_u *str, int len, char_u *buf, int bufle
int outi = 0;
// Fold one character at a time.
- for (char_u *p = str; p < str + len;) {
+ for (char *p = str; p < str + len;) {
if (outi + MB_MAXBYTES > buflen) {
buf[outi] = NUL;
return FAIL;
}
- int c = mb_cptr2char_adv((const char_u **)&p);
+ int c = mb_cptr2char_adv((const char **)&p);
// Exception: greek capital sigma 0x03A3 folds to 0x03C3, except
// when it is the last character in a word, then it folds to
@@ -2496,7 +2511,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, (char *)buf + outi);
+ outi += utf_char2bytes(c, buf + outi);
}
buf[outi] = NUL;
@@ -2508,18 +2523,14 @@ int spell_casefold(const win_T *wp, char_u *str, int len, char_u *buf, int bufle
bool check_need_cap(linenr_T lnum, colnr_T col)
{
bool need_cap = false;
- char_u *line;
- char_u *line_copy = NULL;
- char_u *p;
- colnr_T endcol;
- regmatch_T regmatch;
if (curwin->w_s->b_cap_prog == NULL) {
return false;
}
- line = get_cursor_line_ptr();
- endcol = 0;
+ char *line = get_cursor_line_ptr();
+ char *line_copy = NULL;
+ colnr_T endcol = 0;
if (getwhitecols(line) >= (int)col) {
// At start of line, check if previous line is empty or sentence
// ends there.
@@ -2527,13 +2538,13 @@ bool check_need_cap(linenr_T lnum, colnr_T col)
need_cap = true;
} else {
line = ml_get(lnum - 1);
- if (*skipwhite((char *)line) == NUL) {
+ if (*skipwhite(line) == NUL) {
need_cap = true;
} else {
// Append a space in place of the line break.
- line_copy = concat_str(line, (char_u *)" ");
+ line_copy = concat_str(line, " ");
line = line_copy;
- endcol = (colnr_T)STRLEN(line);
+ endcol = (colnr_T)strlen(line);
}
}
} else {
@@ -2542,15 +2553,17 @@ bool check_need_cap(linenr_T lnum, colnr_T col)
if (endcol > 0) {
// Check if sentence ends before the bad word.
- regmatch.regprog = curwin->w_s->b_cap_prog;
- regmatch.rm_ic = FALSE;
- p = line + endcol;
+ regmatch_T regmatch = {
+ .regprog = curwin->w_s->b_cap_prog,
+ .rm_ic = false
+ };
+ char *p = line + endcol;
for (;;) {
MB_PTR_BACK(line, p);
if (p == line || spell_iswordp_nmw(p, curwin)) {
break;
}
- if (vim_regexec(&regmatch, (char *)p, 0)
+ if (vim_regexec(&regmatch, p, 0)
&& regmatch.endp[0] == line + endcol) {
need_cap = true;
break;
@@ -2568,10 +2581,6 @@ bool check_need_cap(linenr_T lnum, colnr_T col)
void ex_spellrepall(exarg_T *eap)
{
pos_T pos = curwin->w_cursor;
- char_u *frompat;
- int addlen;
- char_u *line;
- char_u *p;
bool save_ws = p_ws;
linenr_T prev_lnum = 0;
@@ -2579,10 +2588,11 @@ void ex_spellrepall(exarg_T *eap)
emsg(_("E752: No previous spell replacement"));
return;
}
- addlen = (int)(STRLEN(repl_to) - STRLEN(repl_from));
+ int addlen = (int)(strlen(repl_to) - strlen(repl_from));
- frompat = xmalloc(STRLEN(repl_from) + 7);
- sprintf((char *)frompat, "\\V\\<%s\\>", repl_from);
+ size_t frompatlen = strlen(repl_from) + 7;
+ char *frompat = xmalloc(frompatlen);
+ snprintf(frompat, frompatlen, "\\V\\<%s\\>", repl_from);
p_ws = false;
sub_nsubs = 0;
@@ -2596,14 +2606,14 @@ void ex_spellrepall(exarg_T *eap)
// Only replace when the right word isn't there yet. This happens
// when changing "etc" to "etc.".
- line = get_cursor_line_ptr();
- if (addlen <= 0 || STRNCMP(line + curwin->w_cursor.col,
- repl_to, STRLEN(repl_to)) != 0) {
- p = xmalloc(STRLEN(line) + (size_t)addlen + 1);
+ char *line = get_cursor_line_ptr();
+ if (addlen <= 0 || strncmp(line + curwin->w_cursor.col,
+ repl_to, strlen(repl_to)) != 0) {
+ char *p = xmalloc(strlen(line) + (size_t)addlen + 1);
memmove(p, line, (size_t)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, (char *)p, false);
+ STRCAT(p, line + curwin->w_cursor.col + strlen(repl_from));
+ ml_replace(curwin->w_cursor.lnum, p, false);
changed_bytes(curwin->w_cursor.lnum, curwin->w_cursor.col);
if (curwin->w_cursor.lnum != prev_lnum) {
@@ -2612,7 +2622,7 @@ void ex_spellrepall(exarg_T *eap)
}
sub_nsubs++;
}
- curwin->w_cursor.col += (colnr_T)STRLEN(repl_to);
+ curwin->w_cursor.col += (colnr_T)strlen(repl_to);
}
p_ws = save_ws;
@@ -2633,30 +2643,30 @@ void ex_spellrepall(exarg_T *eap)
/// @param[in] word source string to copy
/// @param[in,out] wcopy copied string, with case of first letter changed
/// @param[in] upper True to upper case, otherwise lower case
-void onecap_copy(char_u *word, char_u *wcopy, bool upper)
+void onecap_copy(char *word, char *wcopy, bool upper)
{
- char_u *p = word;
- int c = mb_cptr2char_adv((const char_u **)&p);
+ char *p = word;
+ int c = mb_cptr2char_adv((const char **)&p);
if (upper) {
c = SPELL_TOUPPER(c);
} else {
c = SPELL_TOFOLD(c);
}
- int l = utf_char2bytes(c, (char *)wcopy);
- STRLCPY(wcopy + l, p, MAXWLEN - l);
+ int l = utf_char2bytes(c, wcopy);
+ xstrlcpy(wcopy + l, p, (size_t)(MAXWLEN - l));
}
// Make a copy of "word" with all the letters upper cased into
// "wcopy[MAXWLEN]". The result is NUL terminated.
-void allcap_copy(char_u *word, char_u *wcopy)
+void allcap_copy(char *word, char *wcopy)
{
- char_u *d = wcopy;
- for (char_u *s = word; *s != NUL;) {
- int c = mb_cptr2char_adv((const char_u **)&s);
+ char_u *d = (char_u *)wcopy;
+ for (char *s = word; *s != NUL;) {
+ int c = mb_cptr2char_adv((const char **)&s);
if (c == 0xdf) {
c = 'S';
- if (d - wcopy >= MAXWLEN - 1) {
+ if (d - (char_u *)wcopy >= MAXWLEN - 1) {
break;
}
*d++ = (char_u)c;
@@ -2664,7 +2674,7 @@ void allcap_copy(char_u *word, char_u *wcopy)
c = SPELL_TOUPPER(c);
}
- if (d - wcopy >= MAXWLEN - MB_MAXBYTES) {
+ if (d - (char_u *)wcopy >= MAXWLEN - MB_MAXBYTES) {
break;
}
d += utf_char2bytes(c, (char *)d);
@@ -2674,9 +2684,9 @@ void allcap_copy(char_u *word, char_u *wcopy)
// Case-folding may change the number of bytes: Count nr of chars in
// fword[flen] and return the byte length of that many chars in "word".
-int nofold_len(char_u *fword, int flen, char_u *word)
+int nofold_len(char *fword, int flen, char *word)
{
- char_u *p;
+ char *p;
int i = 0;
for (p = fword; p < fword + flen; MB_PTR_ADV(p)) {
@@ -2689,7 +2699,7 @@ int nofold_len(char_u *fword, int flen, char_u *word)
}
// Copy "fword" to "cword", fixing case according to "flags".
-void make_case_word(char_u *fword, char_u *cword, int flags)
+void make_case_word(char *fword, char *cword, int flags)
{
if (flags & WF_ALLCAP) {
// Make it all upper-case
@@ -2719,9 +2729,9 @@ char *eval_soundfold(const char *const word)
langp_T *const lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
if (!GA_EMPTY(&lp->lp_slang->sl_sal)) {
// soundfold the word
- char_u sound[MAXWLEN];
- spell_soundfold(lp->lp_slang, (char_u *)word, false, sound);
- return xstrdup((const char *)sound);
+ char sound[MAXWLEN];
+ spell_soundfold(lp->lp_slang, (char *)word, false, sound);
+ return xstrdup(sound);
}
}
}
@@ -2745,20 +2755,19 @@ char *eval_soundfold(const char *const word)
/// @param[in] inword word to soundfold
/// @param[in] folded whether inword is already case-folded
/// @param[in,out] res destination for soundfolded word
-void spell_soundfold(slang_T *slang, char_u *inword, bool folded, char_u *res)
+void spell_soundfold(slang_T *slang, char *inword, bool folded, char *res)
{
- char_u fword[MAXWLEN];
- char_u *word;
-
if (slang->sl_sofo) {
// SOFOFROM and SOFOTO used
spell_soundfold_sofo(slang, inword, res);
} else {
+ char fword[MAXWLEN];
+ char *word;
// SAL items used. Requires the word to be case-folded.
if (folded) {
word = inword;
} else {
- (void)spell_casefold(curwin, inword, (int)STRLEN(inword), fword, MAXWLEN);
+ (void)spell_casefold(curwin, inword, (int)strlen(inword), fword, MAXWLEN);
word = fword;
}
@@ -2768,7 +2777,7 @@ void spell_soundfold(slang_T *slang, char_u *inword, bool folded, char_u *res)
// Perform sound folding of "inword" into "res" according to SOFOFROM and
// SOFOTO lines.
-static void spell_soundfold_sofo(slang_T *slang, char_u *inword, char_u *res)
+static void spell_soundfold_sofo(slang_T *slang, char *inword, char *res)
{
int ri = 0;
@@ -2776,8 +2785,8 @@ static void spell_soundfold_sofo(slang_T *slang, char_u *inword, char_u *res)
// The sl_sal_first[] table contains the translation for chars up to
// 255, sl_sal the rest.
- for (char_u *s = inword; *s != NUL;) {
- int c = mb_cptr2char_adv((const char_u **)&s);
+ for (char *s = inword; *s != NUL;) {
+ int c = mb_cptr2char_adv((const char **)&s);
if (utf_class(c) == 0) {
c = ' ';
} else if (c < 256) {
@@ -2802,7 +2811,7 @@ static void spell_soundfold_sofo(slang_T *slang, char_u *inword, char_u *res)
}
if (c != NUL && c != prevc) {
- ri += utf_char2bytes(c, (char *)res + ri);
+ ri += utf_char2bytes(c, res + ri);
if (ri + MB_MAXBYTES > MAXWLEN) {
break;
}
@@ -2815,34 +2824,31 @@ static void spell_soundfold_sofo(slang_T *slang, char_u *inword, char_u *res)
// Turn "inword" into its sound-a-like equivalent in "res[MAXWLEN]".
// Multi-byte version of spell_soundfold().
-static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res)
+static void spell_soundfold_wsal(slang_T *slang, const char *inword, char *res)
{
salitem_T *smp = (salitem_T *)slang->sl_sal.ga_data;
int word[MAXWLEN] = { 0 };
int wres[MAXWLEN] = { 0 };
- int l;
int *ws;
int *pf;
- int i, j, z;
+ int j, z;
int reslen;
- int n, k = 0;
+ int k = 0;
int z0;
int k0;
int n0;
- int c;
int pri;
int p0 = -333;
int c0;
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.
- wordlen = 0;
- for (const char_u *s = inword; *s != NUL;) {
- const char_u *t = s;
- c = mb_cptr2char_adv(&s);
+ int wordlen = 0;
+ for (const char *s = (char *)inword; *s != NUL;) {
+ const char_u *t = (char_u *)s;
+ int c = mb_cptr2char_adv(&s);
if (slang->sl_rem_accents) {
if (utf_class(c) == 0) {
if (did_white) {
@@ -2852,7 +2858,7 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res)
did_white = true;
} else {
did_white = false;
- if (!spell_iswordp_nmw(t, curwin)) {
+ if (!spell_iswordp_nmw((char *)t, curwin)) {
continue;
}
}
@@ -2861,13 +2867,14 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res)
}
word[wordlen] = NUL;
+ int c;
// This algorithm comes from Aspell phonet.cpp.
// Converted from C++ to C. Added support for multi-byte chars.
// Changed to keep spaces.
- i = reslen = z = 0;
+ int i = reslen = z = 0;
while ((c = word[i]) != NUL) {
// Start with the first rule that has the character in the word.
- n = slang->sl_sal_first[c & 0xff];
+ int n = slang->sl_sal_first[c & 0xff];
z0 = 0;
if (n >= 0) {
@@ -2875,7 +2882,7 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res)
// If c is 0x300 need extra check for the end of the array, as
// (c & 0xff) is NUL.
for (; ((ws = smp[n].sm_lead_w)[0] & 0xff) == (c & 0xff)
- && ws[0] != NUL; ++n) {
+ && ws[0] != NUL; n++) {
// Quickly skip entries that don't match the word. Most
// entries are less than three chars, optimize for that.
if (c != ws[0]) {
@@ -2887,7 +2894,7 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res)
continue;
}
if (k > 2) {
- for (j = 2; j < k; ++j) {
+ for (j = 2; j < k; j++) {
if (word[i + j] != ws[j]) {
break;
}
@@ -2908,7 +2915,7 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res)
}
k++;
}
- char_u *s = smp[n].sm_rules;
+ char_u *s = (char_u *)smp[n].sm_rules;
pri = 5; // default priority
p0 = *s;
@@ -2948,7 +2955,7 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res)
// Test follow-up rule for "word[i + k]"; loop over
// all entries with the same index byte.
for (; ((ws = smp[n0].sm_lead_w)[0] & 0xff)
- == (c0 & 0xff); ++n0) {
+ == (c0 & 0xff); n0++) {
// Quickly skip entries that don't match the word.
if (c0 != ws[0]) {
continue;
@@ -2960,7 +2967,7 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res)
}
if (k0 > 2) {
pf = word + i + k + 1;
- for (j = 2; j < k0; ++j) {
+ for (j = 2; j < k0; j++) {
if (*pf++ != ws[j]) {
break;
}
@@ -2985,7 +2992,7 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res)
}
p0 = 5;
- s = smp[n0].sm_rules;
+ s = (char_u *)smp[n0].sm_rules;
while (*s == '-') {
// "k0" gets NOT reduced because
// "if (k0 == k)"
@@ -3026,7 +3033,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;
+ s = (char_u *)smp[n].sm_rules;
p0 = (vim_strchr((char *)s, '<') != NULL) ? 1 : 0;
if (p0 == 1 && z == 0) {
// rule with '<' is used
@@ -3102,9 +3109,9 @@ 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], (char *)res + l);
+ int l = 0;
+ for (int n = 0; n < reslen; n++) {
+ l += utf_char2bytes(wres[n], res + l);
if (l + MB_MAXBYTES > MAXWLEN) {
break;
}
@@ -3143,20 +3150,19 @@ void ex_spellinfo(exarg_T *eap)
// ":spelldump"
void ex_spelldump(exarg_T *eap)
{
- char *spl;
- long dummy;
-
if (no_spell_checking(curwin)) {
return;
}
- (void)get_option_value("spl", &dummy, &spl, OPT_LOCAL);
+ char *spl;
+ long dummy;
+ (void)get_option_value("spl", &dummy, &spl, NULL, OPT_LOCAL);
// Create a new empty buffer in a new window.
do_cmdline_cmd("new");
// enable spelling locally in the new window
- set_option_value("spell", true, "", OPT_LOCAL);
- set_option_value("spl", dummy, spl, OPT_LOCAL);
+ set_option_value_give_err("spell", true, "", OPT_LOCAL);
+ set_option_value_give_err("spl", dummy, spl, OPT_LOCAL);
xfree(spl);
if (!buf_is_empty(curbuf)) {
@@ -3169,7 +3175,7 @@ void ex_spelldump(exarg_T *eap)
if (curbuf->b_ml.ml_line_count > 1) {
ml_delete(curbuf->b_ml.ml_line_count, false);
}
- redraw_later(curwin, NOT_VALID);
+ redraw_later(curwin, UPD_NOT_VALID);
}
/// Go through all possible words and:
@@ -3181,24 +3187,23 @@ void ex_spelldump(exarg_T *eap)
/// @param ic ignore case
/// @param dir direction for adding matches
/// @param dumpflags_arg DUMPFLAG_*
-void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg)
+void spell_dump_compl(char *pat, int ic, Direction *dir, int dumpflags_arg)
{
langp_T *lp;
slang_T *slang;
idx_T arridx[MAXWLEN];
int curi[MAXWLEN];
- char_u word[MAXWLEN];
+ char word[MAXWLEN];
int c;
- char_u *byts;
+ char *byts;
idx_T *idxs;
linenr_T lnum = 0;
- int round;
int depth;
int n;
int flags;
- char_u *region_names = NULL; // region names being used
+ char *region_names = NULL; // region names being used
bool do_region = true; // dump region names and numbers
- char_u *p;
+ char *p;
int dumpflags = dumpflags_arg;
int patlen;
@@ -3212,7 +3217,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((char *)pat)) {
+ && (int)strlen(pat) > utfc_ptr2len(pat)) {
dumpflags |= DUMPFLAG_ALLCAP;
}
}
@@ -3220,13 +3225,13 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg)
// Find out if we can support regions: All languages must support the same
// regions or none at all.
- for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi) {
+ for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; lpi++) {
lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
p = lp->lp_slang->sl_regions;
if (p[0] != 0) {
if (region_names == NULL) { // first language with regions
region_names = p;
- } else if (STRCMP(region_names, p) != 0) {
+ } else if (strcmp(region_names, p) != 0) {
do_region = false; // region names are different
break;
}
@@ -3235,15 +3240,15 @@ 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++, (char *)IObuff, (colnr_T)0, false);
+ vim_snprintf(IObuff, IOSIZE, "/regions=%s", region_names);
+ ml_append(lnum++, IObuff, (colnr_T)0, false);
}
} else {
do_region = false;
}
// Loop over all files loaded for the entries in 'spelllang'.
- for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi) {
+ for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; lpi++) {
lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
slang = lp->lp_slang;
if (slang->sl_fbyts == NULL) { // reloading failed
@@ -3251,21 +3256,21 @@ 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++, (char *)IObuff, (colnr_T)0, false);
+ vim_snprintf(IObuff, IOSIZE, "# file: %s", slang->sl_fname);
+ ml_append(lnum++, IObuff, (colnr_T)0, false);
}
// When matching with a pattern and there are no prefixes only use
// parts of the tree that match "pat".
if (pat != NULL && slang->sl_pbyts == NULL) {
- patlen = (int)STRLEN(pat);
+ patlen = (int)strlen(pat);
} else {
patlen = -1;
}
// round 1: case-folded tree
// round 2: keep-case tree
- for (round = 1; round <= 2; ++round) {
+ for (int round = 1; round <= 2; round++) {
if (round == 1) {
dumpflags &= ~DUMPFLAG_KEEPCASE;
byts = slang->sl_fbyts;
@@ -3291,8 +3296,8 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg)
} else {
// Do one more byte at this node.
n = arridx[depth] + curi[depth];
- ++curi[depth];
- c = byts[n];
+ curi[depth]++;
+ c = (uint8_t)byts[n];
if (c == 0 || depth >= MAXWLEN - 1) {
// End of word or reached maximum length, deal with the
// word.
@@ -3315,8 +3320,7 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg)
// when it's the first one.
c = (int)((unsigned)flags >> 24);
if (c == 0 || curi[depth] == 2) {
- dump_word(slang, word, pat, dir,
- dumpflags, flags, lnum);
+ dump_word(slang, word, pat, dir, dumpflags, flags, lnum);
if (pat == NULL) {
lnum++;
}
@@ -3330,7 +3334,7 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg)
}
} else {
// Normal char, go one level deeper.
- word[depth++] = (char_u)c;
+ word[depth++] = (char)c;
arridx[depth] = idxs[n];
curi[depth] = 1;
@@ -3352,17 +3356,15 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg)
}
}
-// Dumps one word: apply case modifications and append a line to the buffer.
-// When "lnum" is zero add insert mode completion.
-static void dump_word(slang_T *slang, char_u *word, char_u *pat, Direction *dir, int dumpflags,
+/// Dumps one word: apply case modifications and append a line to the buffer.
+/// When "lnum" is zero add insert mode completion.
+static void dump_word(slang_T *slang, char *word, char *pat, Direction *dir, int dumpflags,
int wordflags, linenr_T lnum)
{
bool keepcap = false;
- char_u *p;
- char_u *tw;
- char_u cword[MAXWLEN];
- char_u badword[MAXWLEN + 10];
- int i;
+ char *p;
+ char cword[MAXWLEN];
+ char badword[MAXWLEN + 10];
int flags = wordflags;
if (dumpflags & DUMPFLAG_ONECAP) {
@@ -3384,7 +3386,7 @@ static void dump_word(slang_T *slang, char_u *word, char_u *pat, Direction *dir,
keepcap = true;
}
}
- tw = p;
+ char *tw = p;
if (pat == NULL) {
// Add flags and regions after a slash.
@@ -3400,10 +3402,10 @@ static void dump_word(slang_T *slang, char_u *word, char_u *pat, Direction *dir,
STRCAT(badword, "?");
}
if (flags & WF_REGION) {
- for (i = 0; i < 7; i++) {
+ for (int i = 0; i < 7; i++) {
if (flags & (0x10000 << i)) {
- const size_t badword_len = STRLEN(badword);
- snprintf((char *)badword + badword_len,
+ const size_t badword_len = strlen(badword);
+ snprintf(badword + badword_len,
sizeof(badword) - badword_len,
"%d", i + 1);
}
@@ -3416,19 +3418,19 @@ 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, (char *)tw);
+ hi = hash_find(&slang->sl_wordcount, tw);
if (!HASHITEM_EMPTY(hi)) {
- vim_snprintf((char *)IObuff, IOSIZE, "%s\t%d",
+ vim_snprintf(IObuff, IOSIZE, "%s\t%d",
tw, HI2WC(hi)->wc_count);
p = IObuff;
}
}
- ml_append(lnum, (char *)p, (colnr_T)0, false);
+ ml_append(lnum, p, (colnr_T)0, false);
} else if (((dumpflags & DUMPFLAG_ICASE)
- ? mb_strnicmp(p, pat, STRLEN(pat)) == 0
- : STRNCMP(p, pat, STRLEN(pat)) == 0)
- && ins_compl_add_infercase(p, (int)STRLEN(p),
+ ? mb_strnicmp(p, pat, strlen(pat)) == 0
+ : strncmp(p, pat, strlen(pat)) == 0)
+ && ins_compl_add_infercase(p, (int)strlen(p),
p_ic, NULL, *dir, false) == OK) {
// if dir was BACKWARD then honor it just once
*dir = FORWARD;
@@ -3443,42 +3445,35 @@ static void dump_word(slang_T *slang, char_u *word, char_u *pat, Direction *dir,
/// @param flags flags with prefix ID
///
/// @return the updated line number.
-static linenr_T dump_prefixes(slang_T *slang, char_u *word, char_u *pat, Direction *dir,
- int dumpflags, int flags, linenr_T startlnum)
+static linenr_T dump_prefixes(slang_T *slang, char *word, char *pat, Direction *dir, int dumpflags,
+ int flags, linenr_T startlnum)
{
idx_T arridx[MAXWLEN];
int curi[MAXWLEN];
- char_u prefix[MAXWLEN];
- char_u word_up[MAXWLEN];
+ char prefix[MAXWLEN];
+ char word_up[MAXWLEN];
bool has_word_up = false;
- int c;
- char_u *byts;
- idx_T *idxs;
linenr_T lnum = startlnum;
- int depth;
- int n;
- int len;
- int i;
// If the word starts with a lower-case letter make the word with an
// upper-case letter in word_up[].
- c = utf_ptr2char((char *)word);
+ int c = utf_ptr2char(word);
if (SPELL_TOUPPER(c) != c) {
onecap_copy(word, word_up, true);
has_word_up = true;
}
- byts = slang->sl_pbyts;
- idxs = slang->sl_pidxs;
+ char_u *byts = (char_u *)slang->sl_pbyts;
+ idx_T *idxs = slang->sl_pidxs;
if (byts != NULL) { // array not is empty
// Loop over all prefixes, building them byte-by-byte in prefix[].
// When at the end of a prefix check that it supports "flags".
- depth = 0;
+ int depth = 0;
arridx[0] = 0;
curi[0] = 1;
while (depth >= 0 && !got_int) {
- n = arridx[depth];
- len = byts[n];
+ int n = arridx[depth];
+ int len = byts[n];
if (curi[depth] > len) {
// Done all bytes at this node, go up one level.
depth--;
@@ -3486,11 +3481,12 @@ static linenr_T dump_prefixes(slang_T *slang, char_u *word, char_u *pat, Directi
} else {
// Do one more byte at this node.
n += curi[depth];
- ++curi[depth];
+ curi[depth]++;
c = byts[n];
if (c == 0) {
// End of prefix, find out how many IDs there are.
- for (i = 1; i < len; ++i) {
+ int i;
+ for (i = 1; i < len; i++) {
if (byts[n + i] != 0) {
break;
}
@@ -3499,10 +3495,9 @@ static linenr_T dump_prefixes(slang_T *slang, char_u *word, char_u *pat, Directi
c = valid_word_prefix(i, n, flags, word, slang, false);
if (c != 0) {
- STRLCPY(prefix + depth, word, MAXWLEN - depth);
+ xstrlcpy(prefix + depth, word, (size_t)(MAXWLEN - depth));
dump_word(slang, prefix, pat, dir, dumpflags,
- (c & WF_RAREPFX) ? (flags | WF_RARE)
- : flags, lnum);
+ (c & WF_RAREPFX) ? (flags | WF_RARE) : flags, lnum);
if (lnum != 0) {
lnum++;
}
@@ -3512,13 +3507,11 @@ static linenr_T dump_prefixes(slang_T *slang, char_u *word, char_u *pat, Directi
// first letter is upper-case, but only if the prefix has
// a condition.
if (has_word_up) {
- c = valid_word_prefix(i, n, flags, word_up, slang,
- true);
+ c = valid_word_prefix(i, n, flags, word_up, slang, true);
if (c != 0) {
- STRLCPY(prefix + depth, word_up, MAXWLEN - depth);
+ xstrlcpy(prefix + depth, word_up, (size_t)(MAXWLEN - depth));
dump_word(slang, prefix, pat, dir, dumpflags,
- (c & WF_RAREPFX) ? (flags | WF_RARE)
- : flags, lnum);
+ (c & WF_RAREPFX) ? (flags | WF_RARE) : flags, lnum);
if (lnum != 0) {
lnum++;
}
@@ -3526,7 +3519,7 @@ static linenr_T dump_prefixes(slang_T *slang, char_u *word, char_u *pat, Directi
}
} else {
// Normal char, go one level deeper.
- prefix[depth++] = (char_u)c;
+ prefix[depth++] = (char)c;
arridx[depth] = idxs[n];
curi[depth] = 1;
}
@@ -3539,9 +3532,9 @@ static linenr_T dump_prefixes(slang_T *slang, char_u *word, char_u *pat, Directi
// Move "p" to the end of word "start".
// Uses the spell-checking word characters.
-char_u *spell_to_word_end(char_u *start, win_T *win)
+char *spell_to_word_end(char *start, win_T *win)
{
- char_u *p = start;
+ char *p = start;
while (*p != NUL && spell_iswordp(p, win)) {
MB_PTR_ADV(p);
@@ -3556,16 +3549,14 @@ char_u *spell_to_word_end(char_u *start, win_T *win)
// Returns the column number of the word.
int spell_word_start(int startcol)
{
- char_u *line;
- char_u *p;
- int col = 0;
-
if (no_spell_checking(curwin)) {
return startcol;
}
+ char *line = get_cursor_line_ptr();
+ char *p;
+
// Find a word character before "startcol".
- line = get_cursor_line_ptr();
for (p = line + startcol; p > line;) {
MB_PTR_BACK(line, p);
if (spell_iswordp_nmw(p, curwin)) {
@@ -3573,6 +3564,8 @@ int spell_word_start(int startcol)
}
}
+ int col = 0;
+
// Go back to start of the word.
while (p > line) {
col = (int)(p - line);
@@ -3599,7 +3592,7 @@ void spell_expand_check_cap(colnr_T col)
// Used for Insert mode completion CTRL-X ?.
// Returns the number of matches. The matches are in "matchp[]", array of
// allocated strings.
-int expand_spelling(linenr_T lnum, char_u *pat, char ***matchp)
+int expand_spelling(linenr_T lnum, char *pat, char ***matchp)
{
garray_T ga;
@@ -3608,19 +3601,19 @@ int expand_spelling(linenr_T lnum, char_u *pat, char ***matchp)
return ga.ga_len;
}
-/// Return true if "val" is a valid 'spelllang' value.
-bool valid_spelllang(const char_u *val)
+/// @return true if "val" is a valid 'spelllang' value.
+bool valid_spelllang(const char *val)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
return valid_name(val, ".-_,@");
}
-/// Return true if "val" is a valid 'spellfile' value.
-bool valid_spellfile(const char_u *val)
+/// @return true if "val" is a valid 'spellfile' value.
+bool valid_spellfile(const char *val)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- for (const char_u *s = val; *s != NUL; s++) {
- if (!vim_isfilec(*s) && *s != ',' && *s != ' ') {
+ for (const char *s = val; *s != NUL; s++) {
+ if (!vim_isfilec((uint8_t)(*s)) && *s != ',' && *s != ' ') {
return false;
}
}
@@ -3632,22 +3625,23 @@ char *did_set_spell_option(bool is_spellfile)
char *errmsg = NULL;
if (is_spellfile) {
- int l = (int)STRLEN(curwin->w_s->b_p_spf);
+ int l = (int)strlen(curwin->w_s->b_p_spf);
if (l > 0
- && (l < 4 || STRCMP(curwin->w_s->b_p_spf + l - 4, ".add") != 0)) {
+ && (l < 4 || strcmp(curwin->w_s->b_p_spf + l - 4, ".add") != 0)) {
errmsg = e_invarg;
}
}
- if (errmsg == NULL) {
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer == curbuf && wp->w_p_spell) {
- errmsg = did_set_spelllang(wp);
- break;
- }
- }
+ if (errmsg != NULL) {
+ return errmsg;
}
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_buffer == curbuf && wp->w_p_spell) {
+ errmsg = did_set_spelllang(wp);
+ break;
+ }
+ }
return errmsg;
}
@@ -3657,14 +3651,13 @@ char *compile_cap_prog(synblock_T *synblock)
FUNC_ATTR_NONNULL_ALL
{
regprog_T *rp = synblock->b_cap_prog;
- char_u *re;
if (synblock->b_p_spc == NULL || *synblock->b_p_spc == NUL) {
synblock->b_cap_prog = NULL;
} 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((char *)re, RE_MAGIC);
+ char *re = concat_str("^", synblock->b_p_spc);
+ synblock->b_cap_prog = vim_regcomp(re, RE_MAGIC);
xfree(re);
if (synblock->b_cap_prog == NULL) {
synblock->b_cap_prog = rp; // restore the previous program
diff --git a/src/nvim/spell_defs.h b/src/nvim/spell_defs.h
index 61f722b9ee..726af7d698 100644
--- a/src/nvim/spell_defs.h
+++ b/src/nvim/spell_defs.h
@@ -72,19 +72,19 @@ typedef int idx_T;
// si_repsal, sl_rep, and si_sal. Not for sl_sal!
// One replacement: from "ft_from" to "ft_to".
typedef struct fromto_S {
- char_u *ft_from;
- char_u *ft_to;
+ char *ft_from;
+ char *ft_to;
} fromto_T;
// Info from "SAL" entries in ".aff" file used in sl_sal.
// The info is split for quick processing by spell_soundfold().
// Note that "sm_oneof" and "sm_rules" point into sm_lead.
typedef struct salitem_S {
- char_u *sm_lead; // leading letters
- int sm_leadlen; // length of "sm_lead"
- char_u *sm_oneof; // letters from () or NULL
- char_u *sm_rules; // rules like ^, $, priority
- char_u *sm_to; // replacement.
+ char *sm_lead; // leading letters
+ int sm_leadlen; // length of "sm_lead"
+ char_u *sm_oneof; // letters from () or NULL
+ char *sm_rules; // rules like ^, $, priority
+ char *sm_to; // replacement.
int *sm_lead_w; // wide character copy of "sm_lead"
int *sm_oneof_w; // wide character copy of "sm_oneof"
int *sm_to_w; // wide character copy of "sm_to"
@@ -115,21 +115,21 @@ typedef struct slang_S slang_T;
struct slang_S {
slang_T *sl_next; // next language
- char_u *sl_name; // language name "en", "en.rare", "nl", etc.
- char_u *sl_fname; // name of .spl file
- bool sl_add; // true if it's a .add file.
+ char *sl_name; // language name "en", "en.rare", "nl", etc.
+ char *sl_fname; // name of .spl file
+ bool sl_add; // true if it's a .add file.
- char_u *sl_fbyts; // case-folded word bytes
+ char *sl_fbyts; // case-folded word bytes
long sl_fbyts_len; // length of sl_fbyts
idx_T *sl_fidxs; // case-folded word indexes
- char_u *sl_kbyts; // keep-case word bytes
+ char *sl_kbyts; // keep-case word bytes
idx_T *sl_kidxs; // keep-case word indexes
- char_u *sl_pbyts; // prefix tree word bytes
+ char *sl_pbyts; // prefix tree word bytes
idx_T *sl_pidxs; // prefix tree word indexes
char_u *sl_info; // infotext string or NULL
- char_u sl_regions[MAXREGIONS * 2 + 1];
+ char sl_regions[MAXREGIONS * 2 + 1];
// table with up to 8 region names plus NUL
char_u *sl_midword; // MIDWORD string or NULL
@@ -143,9 +143,9 @@ struct slang_S {
garray_T sl_comppat; // CHECKCOMPOUNDPATTERN items
regprog_T *sl_compprog; // COMPOUNDRULE turned into a regexp progrm
// (NULL when no compounding)
- char_u *sl_comprules; // all COMPOUNDRULE concatenated (or NULL)
- char_u *sl_compstartflags; // flags for first compound word
- char_u *sl_compallflags; // all flags for compound words
+ uint8_t *sl_comprules; // all COMPOUNDRULE concatenated (or NULL)
+ uint8_t *sl_compstartflags; // flags for first compound word
+ uint8_t *sl_compallflags; // all flags for compound words
bool sl_nobreak; // When true: no spaces between words
char_u *sl_syllable; // SYLLABLE repeatable chars or NULL
garray_T sl_syl_items; // syllable items
@@ -172,7 +172,7 @@ struct slang_S {
// Info from the .sug file. Loaded on demand.
time_t sl_sugtime; // timestamp for .sug file
- char_u *sl_sbyts; // soundfolded word bytes
+ char *sl_sbyts; // soundfolded word bytes
idx_T *sl_sidxs; // soundfolded word indexes
buf_T *sl_sugbuf; // buffer with word number table
bool sl_sugloaded; // true when .sug file was loaded or failed to
@@ -218,8 +218,7 @@ typedef struct {
// the "w" library function for characters above 255.
#define SPELL_TOFOLD(c) ((c) >= 128 ? utf_fold(c) : (int)spelltab.st_fold[c])
-#define SPELL_TOUPPER(c) ((c) >= 128 ? mb_toupper(c) \
- : (int)spelltab.st_upper[c])
+#define SPELL_TOUPPER(c) ((c) >= 128 ? mb_toupper(c) : (int)spelltab.st_upper[c])
#define SPELL_ISUPPER(c) ((c) >= 128 ? mb_isupper(c) : spelltab.st_isu[c])
@@ -228,7 +227,7 @@ typedef struct {
extern slang_T *first_lang;
// file used for "zG" and "zW"
-extern char_u *int_wordlist;
+extern char *int_wordlist;
extern spelltab_T spelltab;
extern int did_set_spelltab;
@@ -252,7 +251,7 @@ typedef struct wordcount_S {
#define MAXWORDCOUNT 0xffff
// Remember what "z?" replaced.
-extern char_u *repl_from;
-extern char_u *repl_to;
+extern char *repl_from;
+extern char *repl_to;
#endif // NVIM_SPELL_DEFS_H
diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c
index be1373f617..44414ca1a5 100644
--- a/src/nvim/spellfile.c
+++ b/src/nvim/spellfile.c
@@ -226,82 +226,101 @@
// stored as an offset to the previous number in as
// few bytes as possible, see offset2bytes())
-#include <stdint.h>
+#include <assert.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
#include <stdio.h>
-#include <wctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "auto/config.h"
#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/drawscreen.h"
-#include "nvim/ex_cmds2.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/fileio.h"
+#include "nvim/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/hashtab.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
+#include "nvim/message.h"
#include "nvim/option.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
+#include "nvim/os/time.h"
#include "nvim/path.h"
+#include "nvim/pos.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
#include "nvim/spell.h"
#include "nvim/spell_defs.h"
#include "nvim/spellfile.h"
+#include "nvim/strings.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/vim.h"
-#ifndef UNIX // it's in os/unix_defs.h for Unix
-# include <time.h> // for time_t
-#endif
-
// Special byte values for <byte>. Some are only used in the tree for
// postponed prefixes, some only in the other trees. This is a bit messy...
-#define BY_NOFLAGS 0 // end of word without flags or region; for
- // postponed prefix: no <pflags>
-#define BY_INDEX 1 // child is shared, index follows
-#define BY_FLAGS 2 // end of word, <flags> byte follows; for
- // postponed prefix: <pflags> follows
-#define BY_FLAGS2 3 // end of word, <flags> and <flags2> bytes
- // follow; never used in prefix tree
-#define BY_SPECIAL BY_FLAGS2 // highest special byte value
+enum {
+ BY_NOFLAGS = 0, // end of word without flags or region; for postponed prefix: no <pflags>
+ BY_INDEX = 1, // child is shared, index follows
+ BY_FLAGS = 2, // end of word, <flags> byte follows; for postponed prefix: <pflags> follows
+ BY_FLAGS2 = 3, // end of word, <flags> and <flags2> bytes follow; never used in prefix tree
+ BY_SPECIAL = BY_FLAGS2, // highest special byte value
+};
#define ZERO_FLAG 65009 // used when flag is zero: "0"
// Flags used in .spl file for soundsalike flags.
-#define SAL_F0LLOWUP 1
-#define SAL_COLLAPSE 2
-#define SAL_REM_ACCENTS 4
+enum {
+ SAL_F0LLOWUP = 1,
+ SAL_COLLAPSE = 2,
+ SAL_REM_ACCENTS = 4,
+};
#define VIMSPELLMAGIC "VIMspell" // string at start of Vim spell file
#define VIMSPELLMAGICL (sizeof(VIMSPELLMAGIC) - 1)
#define VIMSPELLVERSION 50
// Section IDs. Only renumber them when VIMSPELLVERSION changes!
-#define SN_REGION 0 // <regionname> section
-#define SN_CHARFLAGS 1 // charflags section
-#define SN_MIDWORD 2 // <midword> section
-#define SN_PREFCOND 3 // <prefcond> section
-#define SN_REP 4 // REP items section
-#define SN_SAL 5 // SAL items section
-#define SN_SOFO 6 // soundfolding section
-#define SN_MAP 7 // MAP items section
-#define SN_COMPOUND 8 // compound words section
-#define SN_SYLLABLE 9 // syllable section
-#define SN_NOBREAK 10 // NOBREAK section
-#define SN_SUGFILE 11 // timestamp for .sug file
-#define SN_REPSAL 12 // REPSAL items section
-#define SN_WORDS 13 // common words
-#define SN_NOSPLITSUGS 14 // don't split word for suggestions
-#define SN_INFO 15 // info section
-#define SN_NOCOMPOUNDSUGS 16 // don't compound for suggestions
-#define SN_END 255 // end of sections
+enum {
+ SN_REGION = 0, // <regionname> section
+ SN_CHARFLAGS = 1, // charflags section
+ SN_MIDWORD = 2, // <midword> section
+ SN_PREFCOND = 3, // <prefcond> section
+ SN_REP = 4, // REP items section
+ SN_SAL = 5, // SAL items section
+ SN_SOFO = 6, // soundfolding section
+ SN_MAP = 7, // MAP items section
+ SN_COMPOUND = 8, // compound words section
+ SN_SYLLABLE = 9, // syllable section
+ SN_NOBREAK = 10, // NOBREAK section
+ SN_SUGFILE = 11, // timestamp for .sug file
+ SN_REPSAL = 12, // REPSAL items section
+ SN_WORDS = 13, // common words
+ SN_NOSPLITSUGS = 14, // don't split word for suggestions
+ SN_INFO = 15, // info section
+ SN_NOCOMPOUNDSUGS = 16, // don't compound for suggestions
+ SN_END = 255, // end of sections
+};
#define SNF_REQUIRED 1 // <sectionflags>: required section
-#define CF_WORD 0x01
-#define CF_UPPER 0x02
+enum {
+ CF_WORD = 0x01,
+ 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");
@@ -313,7 +332,7 @@ static char *msg_compressing = N_("Compressing word tree...");
// and .dic file.
// Main structure to store the contents of a ".aff" file.
typedef struct afffile_S {
- char_u *af_enc; // "SET", normalized, alloc'ed string or NULL
+ char *af_enc; // "SET", normalized, alloc'ed string or NULL
int af_flagtype; // AFT_CHAR, AFT_LONG, AFT_NUM or AFT_CAPLONG
unsigned af_rare; // RARE ID for rare word
unsigned af_keepcase; // KEEPCASE ID for keep-case word
@@ -341,12 +360,12 @@ typedef struct afffile_S {
typedef struct affentry_S affentry_T;
// Affix entry from ".aff" file. Used for prefixes and suffixes.
struct affentry_S {
- affentry_T *ae_next; // next affix with same name/number
- char_u *ae_chop; // text to chop off basic word (can be NULL)
- char_u *ae_add; // text to add to basic word (can be NULL)
- char_u *ae_flags; // flags on the affix (can be NULL)
- char_u *ae_cond; // condition (NULL for ".")
- regprog_T *ae_prog; // regexp program for ae_cond or NULL
+ affentry_T *ae_next; // next affix with same name/number
+ char *ae_chop; // text to chop off basic word (can be NULL)
+ char *ae_add; // text to add to basic word (can be NULL)
+ char *ae_flags; // flags on the affix (can be NULL)
+ char *ae_cond; // condition (NULL for ".")
+ regprog_T *ae_prog; // regexp program for ae_cond or NULL
char ae_compforbid; // COMPOUNDFORBIDFLAG found
char ae_comppermit; // COMPOUNDPERMITFLAG found
};
@@ -355,12 +374,12 @@ struct affentry_S {
// Affix header from ".aff" file. Used for af_pref and af_suff.
typedef struct affheader_S {
- char_u ah_key[AH_KEY_LEN]; // key for hashtab == name of affix
+ char ah_key[AH_KEY_LEN]; // key for hashtab == name of affix
unsigned ah_flag; // affix name as number, uses "af_flagtype"
int ah_newID; // prefix ID after renumbering; 0 if not used
int ah_combine; // suffix may combine with prefix
int ah_follows; // another affix block should be following
- affentry_T *ah_first; // first affix entry
+ affentry_T *ah_first; // first affix entry
} affheader_T;
#define HI2AH(hi) ((affheader_T *)(hi)->hi_key)
@@ -415,7 +434,7 @@ struct wordnode_S {
// "wn_region" the LSW of the wordnr.
char_u wn_affixID; // supported/required prefix ID or 0
uint16_t wn_flags; // WF_ flags
- short wn_region; // region mask
+ int16_t wn_region; // region mask
#ifdef SPELL_PRINTTREE
int wn_nr; // sequence nr for printing
@@ -440,7 +459,7 @@ typedef struct spellinfo_S {
sblock_T *si_blocks; // memory blocks used
long si_blocks_cnt; // memory blocks allocated
- int si_did_emsg; // TRUE when ran out of memory
+ int si_did_emsg; // true when ran out of memory
long si_compress_cnt; // words to add before lowering
// compression limit
@@ -454,13 +473,13 @@ typedef struct spellinfo_S {
int si_ascii; // handling only ASCII words
int si_add; // addition file
- int si_clear_chartab; // when TRUE clear char tables
+ int si_clear_chartab; // when true clear char tables
int si_region; // region mask
vimconv_T si_conv; // for conversion to 'encoding'
int si_memtot; // runtime memory used
int si_verbose; // verbose messages
int si_msg_count; // number of words added since last message
- char_u *si_info; // info text chars or NULL
+ char *si_info; // info text chars or NULL
int si_region_count; // number of regions supported (1 when there
// are no regions)
char_u si_region_name[MAXREGIONS * 2 + 1];
@@ -470,8 +489,8 @@ typedef struct spellinfo_S {
garray_T si_rep; // list of fromto_T entries from REP lines
garray_T si_repsal; // list of fromto_T entries from REPSAL lines
garray_T si_sal; // list of fromto_T entries from SAL lines
- char_u *si_sofofr; // SOFOFROM text
- char_u *si_sofoto; // SOFOTO text
+ char *si_sofofr; // SOFOFROM text
+ char *si_sofoto; // SOFOTO text
int si_nosugfile; // NOSUGFILE item found
int si_nosplitsugs; // NOSPLITSUGS item found
int si_nocompoundsugs; // NOCOMPOUNDSUGS item found
@@ -481,16 +500,16 @@ typedef struct spellinfo_S {
time_t si_sugtime; // timestamp for .sug file
int si_rem_accents; // soundsalike: remove accents
garray_T si_map; // MAP info concatenated
- char_u *si_midword; // MIDWORD chars or NULL
+ char *si_midword; // MIDWORD chars or NULL
int si_compmax; // max nr of words for compounding
int si_compminlen; // minimal length for compounding
int si_compsylmax; // max nr of syllables for compounding
int si_compoptions; // COMP_ flags
garray_T si_comppat; // CHECKCOMPOUNDPATTERN items, each stored as
// a string
- char_u *si_compflags; // flags used for compounding
+ char *si_compflags; // flags used for compounding
char_u si_nobreak; // NOBREAK
- char_u *si_syllable; // syllable string
+ char *si_syllable; // syllable string
garray_T si_prefcond; // table with conditions for postponed
// prefixes, each stored as a string
int si_newprefID; // current value for ah_newID
@@ -552,7 +571,7 @@ static inline int spell_check_magic_string(FILE *const fd)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE
{
char buf[VIMSPELLMAGICL];
- SPELL_READ_BYTES(buf, VIMSPELLMAGICL, fd,; );
+ SPELL_READ_BYTES(buf, VIMSPELLMAGICL, fd,; ); // NOLINT(whitespace/parens)
if (memcmp(buf, VIMSPELLMAGIC, VIMSPELLMAGICL) != 0) {
return SP_FORMERROR;
}
@@ -572,10 +591,10 @@ static inline int spell_check_magic_string(FILE *const fd)
/// @param silent no error if file doesn't exist
///
/// @return the slang_T the spell file was loaded into. NULL for error.
-slang_T *spell_load_file(char_u *fname, char_u *lang, slang_T *old_lp, bool silent)
+slang_T *spell_load_file(char *fname, char *lang, slang_T *old_lp, bool silent)
{
FILE *fd;
- char_u *p;
+ char *p;
int n;
int len;
slang_T *lp = NULL;
@@ -583,7 +602,7 @@ slang_T *spell_load_file(char_u *fname, char_u *lang, slang_T *old_lp, bool sile
int res;
bool did_estack_push = false;
- fd = os_fopen((char *)fname, "r");
+ fd = os_fopen(fname, "r");
if (fd == NULL) {
if (!silent) {
semsg(_(e_notopen), fname);
@@ -604,16 +623,16 @@ slang_T *spell_load_file(char_u *fname, char_u *lang, slang_T *old_lp, bool sile
lp = slang_alloc(lang);
// Remember the file name, used to reload the file when it's updated.
- lp->sl_fname = vim_strsave(fname);
+ lp->sl_fname = xstrdup(fname);
// Check for .add.spl.
- lp->sl_add = strstr(path_tail((char *)fname), SPL_FNAME_ADD) != NULL;
+ lp->sl_add = strstr(path_tail(fname), SPL_FNAME_ADD) != NULL;
} else {
lp = old_lp;
}
// Set sourcing_name, so that error messages mention the file name.
- estack_push(ETYPE_SPELL, (char *)fname, 0);
+ estack_push(ETYPE_SPELL, fname, 0);
did_estack_push = true;
// <HEADER>: <fileID>
@@ -697,7 +716,7 @@ slang_T *spell_load_file(char_u *fname, char_u *lang, slang_T *old_lp, bool sile
break;
case SN_MAP:
- p = READ_STRING(fd, len); // <mapstr>
+ p = (char *)READ_STRING(fd, len); // <mapstr>
if (p == NULL) {
goto endFAIL;
}
@@ -819,7 +838,7 @@ endOK:
// Fill in the wordcount fields for a trie.
// Returns the total number of words.
-static void tree_count_words(char_u *byts, idx_T *idxs)
+static void tree_count_words(const char_u *byts, idx_T *idxs)
{
int depth;
idx_T arridx[MAXWLEN];
@@ -845,12 +864,12 @@ static void tree_count_words(char_u *byts, idx_T *idxs)
} else {
// Do one more byte at this node.
n = arridx[depth] + curi[depth];
- ++curi[depth];
+ curi[depth]++;
c = byts[n];
if (c == 0) {
// End of word, count it.
- ++wordcount[depth];
+ wordcount[depth]++;
// Skip over any other NUL bytes (same word with different
// flags).
@@ -869,14 +888,14 @@ static void tree_count_words(char_u *byts, idx_T *idxs)
}
}
-// Load the .sug files for languages that have one and weren't loaded yet.
+/// Load the .sug files for languages that have one and weren't loaded yet.
void suggest_load_files(void)
{
langp_T *lp;
slang_T *slang;
- char_u *dotp;
+ char *dotp;
FILE *fd;
- char_u buf[MAXWLEN];
+ char buf[MAXWLEN];
int i;
time_t timestamp;
int wcount;
@@ -885,7 +904,7 @@ void suggest_load_files(void)
int c;
// Do this for all languages that support sound folding.
- for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi) {
+ for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; lpi++) {
lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
slang = lp->lp_slang;
if (slang->sl_sugtime != 0 && !slang->sl_sugloaded) {
@@ -894,21 +913,21 @@ void suggest_load_files(void)
// don't try again and again.
slang->sl_sugloaded = true;
- dotp = STRRCHR(slang->sl_fname, '.');
- if (dotp == NULL || FNAMECMP(dotp, ".spl") != 0) {
+ dotp = strrchr(slang->sl_fname, '.');
+ if (dotp == NULL || path_fnamecmp(dotp, ".spl") != 0) {
continue;
}
STRCPY(dotp, ".sug");
- fd = os_fopen((char *)slang->sl_fname, "r");
+ fd = os_fopen(slang->sl_fname, "r");
if (fd == NULL) {
goto nextone;
}
// <SUGHEADER>: <fileID> <versionnr> <timestamp>
for (i = 0; i < VIMSUGMAGICL; i++) {
- buf[i] = (char_u)getc(fd); // <fileID>
+ buf[i] = (char)getc(fd); // <fileID>
}
- if (STRNCMP(buf, VIMSUGMAGIC, VIMSUGMAGICL) != 0) {
+ if (strncmp(buf, VIMSUGMAGIC, VIMSUGMAGICL) != 0) {
semsg(_("E778: This does not look like a .sug file: %s"),
slang->sl_fname);
goto nextone;
@@ -960,7 +979,7 @@ someerror:
// Read all the wordnr lists into the buffer, one NUL terminated
// list per line.
ga_init(&ga, 1, 100);
- for (wordnr = 0; wordnr < wcount; ++wordnr) {
+ for (wordnr = 0; wordnr < wcount; wordnr++) {
ga.ga_len = 0;
for (;;) {
c = getc(fd); // <sugline>
@@ -981,8 +1000,8 @@ someerror:
// Need to put word counts in the word tries, so that we can find
// a word by its number.
- tree_count_words(slang->sl_fbyts, slang->sl_fidxs);
- tree_count_words(slang->sl_sbyts, slang->sl_sidxs);
+ tree_count_words((char_u *)slang->sl_fbyts, slang->sl_fidxs);
+ tree_count_words((char_u *)slang->sl_sbyts, slang->sl_sidxs);
nextone:
if (fd != NULL) {
@@ -1031,7 +1050,7 @@ static int read_region_section(FILE *fd, slang_T *lp, int len)
if (len > MAXREGIONS * 2) {
return SP_FORMERROR;
}
- SPELL_READ_NONNUL_BYTES((char *)lp->sl_regions, (size_t)len, fd,; );
+ SPELL_READ_NONNUL_BYTES(lp->sl_regions, (size_t)len, fd,; ); // NOLINT(whitespace/parens)
lp->sl_regions[len] = NUL;
return 0;
}
@@ -1041,12 +1060,12 @@ static int read_region_section(FILE *fd, slang_T *lp, int len)
// Return SP_*ERROR flags.
static int read_charflags_section(FILE *fd)
{
- char_u *flags;
+ char *flags;
char_u *fol;
int flagslen, follen;
// <charflagslen> <charflags>
- flags = read_cnt_string(fd, 1, &flagslen);
+ flags = (char *)read_cnt_string(fd, 1, &flagslen);
if (flagslen < 0) {
return flagslen;
}
@@ -1060,7 +1079,7 @@ static int read_charflags_section(FILE *fd)
// Set the word-char flags and fill SPELL_ISUPPER() table.
if (flags != NULL && fol != NULL) {
- set_spell_charflags(flags, flagslen, fol);
+ set_spell_charflags((char_u *)flags, flagslen, (char *)fol);
}
xfree(flags);
@@ -1098,7 +1117,7 @@ static int read_prefcond_section(FILE *fd, slang_T *lp)
if (n > 0) {
char buf[MAXWLEN + 1];
buf[0] = '^'; // always match at one position only
- SPELL_READ_NONNUL_BYTES(buf + 1, (size_t)n, fd,; );
+ SPELL_READ_NONNUL_BYTES(buf + 1, (size_t)n, fd,; ); // NOLINT(whitespace/parens)
buf[n + 1] = NUL;
lp->sl_prefprog[i] = vim_regcomp(buf, RE_MAGIC | RE_STRING);
}
@@ -1121,17 +1140,17 @@ static int read_rep_section(FILE *fd, garray_T *gap, int16_t *first)
ga_grow(gap, cnt);
// <rep> : <repfromlen> <repfrom> <reptolen> <repto>
- for (; gap->ga_len < cnt; ++gap->ga_len) {
+ for (; gap->ga_len < cnt; gap->ga_len++) {
int c;
ftp = &((fromto_T *)gap->ga_data)[gap->ga_len];
- ftp->ft_from = read_cnt_string(fd, 1, &c);
+ ftp->ft_from = (char *)read_cnt_string(fd, 1, &c);
if (c < 0) {
return c;
}
if (c == 0) {
return SP_FORMERROR;
}
- ftp->ft_to = read_cnt_string(fd, 1, &c);
+ ftp->ft_to = (char *)read_cnt_string(fd, 1, &c);
if (c <= 0) {
xfree(ftp->ft_from);
if (c < 0) {
@@ -1142,13 +1161,13 @@ static int read_rep_section(FILE *fd, garray_T *gap, int16_t *first)
}
// Fill the first-index table.
- for (int i = 0; i < 256; ++i) {
+ for (int i = 0; i < 256; i++) {
first[i] = -1;
}
- for (int i = 0; i < gap->ga_len; ++i) {
+ for (int i = 0; i < gap->ga_len; i++) {
ftp = &((fromto_T *)gap->ga_data)[i];
- if (first[*ftp->ft_from] == -1) {
- first[*ftp->ft_from] = (int16_t)i;
+ if (first[(uint8_t)(*ftp->ft_from)] == -1) {
+ first[(uint8_t)(*ftp->ft_from)] = (int16_t)i;
}
}
return 0;
@@ -1196,24 +1215,24 @@ static int read_sal_section(FILE *fd, slang_T *slang)
return SP_TRUNCERROR;
}
p = xmalloc((size_t)ccnt + 2);
- smp->sm_lead = p;
+ smp->sm_lead = (char *)p;
// Read up to the first special char into sm_lead.
int i = 0;
- for (; i < ccnt; ++i) {
+ for (; i < ccnt; i++) {
c = getc(fd); // <salfrom>
if (vim_strchr("0123456789(-<^$", c) != NULL) {
break;
}
*p++ = (char_u)c;
}
- smp->sm_leadlen = (int)(p - smp->sm_lead);
+ smp->sm_leadlen = (int)(p - (char_u *)smp->sm_lead);
*p++ = NUL;
// Put (abc) chars in sm_oneof, if any.
if (c == '(') {
smp->sm_oneof = p;
- for (++i; i < ccnt; ++i) {
+ for (++i; i < ccnt; i++) {
c = getc(fd); // <salfrom>
if (c == ')') {
break;
@@ -1229,7 +1248,7 @@ static int read_sal_section(FILE *fd, slang_T *slang)
}
// Any following chars go in sm_rules.
- smp->sm_rules = p;
+ smp->sm_rules = (char *)p;
if (i < ccnt) {
// store the char we got while checking for end of sm_lead
*p++ = (char_u)c;
@@ -1244,7 +1263,7 @@ static int read_sal_section(FILE *fd, slang_T *slang)
*p++ = NUL;
// <saltolen> <salto>
- smp->sm_to = read_cnt_string(fd, 1, &ccnt);
+ smp->sm_to = (char *)read_cnt_string(fd, 1, &ccnt);
if (ccnt < 0) {
xfree(smp->sm_lead);
return ccnt;
@@ -1256,7 +1275,7 @@ static int read_sal_section(FILE *fd, slang_T *slang)
if (smp->sm_oneof == NULL) {
smp->sm_oneof_w = NULL;
} else {
- smp->sm_oneof_w = mb_str2wide(smp->sm_oneof);
+ smp->sm_oneof_w = mb_str2wide((char *)smp->sm_oneof);
}
if (smp->sm_to == NULL) {
smp->sm_to_w = NULL;
@@ -1271,12 +1290,12 @@ static int read_sal_section(FILE *fd, slang_T *slang)
smp = &((salitem_T *)gap->ga_data)[gap->ga_len];
p = xmalloc(1);
p[0] = NUL;
- smp->sm_lead = p;
+ smp->sm_lead = (char *)p;
smp->sm_lead_w = mb_str2wide(smp->sm_lead);
smp->sm_leadlen = 0;
smp->sm_oneof = NULL;
smp->sm_oneof_w = NULL;
- smp->sm_rules = p;
+ smp->sm_rules = (char *)p;
smp->sm_to = NULL;
smp->sm_to_w = NULL;
gap->ga_len++;
@@ -1299,7 +1318,7 @@ static int read_words_section(FILE *fd, slang_T *lp, int len)
while (done < len) {
// Read one word at a time.
- for (i = 0;; ++i) {
+ for (i = 0;; i++) {
c = getc(fd);
if (c == EOF) {
return SP_TRUNCERROR;
@@ -1314,7 +1333,7 @@ static int read_words_section(FILE *fd, slang_T *lp, int len)
}
// Init the count to 10.
- count_common_word(lp, word, -1, 10);
+ count_common_word(lp, (char *)word, -1, 10);
done += i + 1;
}
return 0;
@@ -1325,19 +1344,19 @@ static int read_words_section(FILE *fd, slang_T *lp, int len)
static int read_sofo_section(FILE *fd, slang_T *slang)
{
int cnt;
- char_u *from, *to;
+ char *from, *to;
int res;
slang->sl_sofo = true;
// <sofofromlen> <sofofrom>
- from = read_cnt_string(fd, 2, &cnt);
+ from = (char *)read_cnt_string(fd, 2, &cnt);
if (cnt < 0) {
return cnt;
}
// <sofotolen> <sofoto>
- to = read_cnt_string(fd, 2, &cnt);
+ to = (char *)read_cnt_string(fd, 2, &cnt);
if (cnt < 0) {
xfree(from);
return cnt;
@@ -1407,7 +1426,7 @@ static int read_compound(FILE *fd, slang_T *slang, int len)
return SP_TRUNCERROR;
}
todo -= 2;
- ga_init(gap, sizeof(char_u *), c);
+ ga_init(gap, sizeof(char *), c);
ga_grow(gap, c);
while (--c >= 0) {
((char **)(gap->ga_data))[gap->ga_len++] = (char *)read_cnt_string(fd, 1, &cnt);
@@ -1428,25 +1447,25 @@ static int read_compound(FILE *fd, slang_T *slang, int len)
// Conversion to utf-8 may double the size.
c = todo * 2 + 7;
c += todo * 2;
- char_u *pat = xmalloc((size_t)c);
+ char *pat = xmalloc((size_t)c);
// We also need a list of all flags that can appear at the start and one
// for all flags.
- char_u *cp = xmalloc((size_t)todo + 1);
+ uint8_t *cp = xmalloc((size_t)todo + 1);
slang->sl_compstartflags = cp;
*cp = NUL;
- char_u *ap = xmalloc((size_t)todo + 1);
+ uint8_t *ap = xmalloc((size_t)todo + 1);
slang->sl_compallflags = ap;
*ap = NUL;
// And a list of all patterns in their original form, for checking whether
// compounding may work in match_compoundrule(). This is freed when we
// encounter a wildcard, the check doesn't work then.
- char_u *crp = xmalloc((size_t)todo + 1);
+ uint8_t *crp = xmalloc((size_t)todo + 1);
slang->sl_comprules = crp;
- char_u *pp = pat;
+ char_u *pp = (char_u *)pat;
*pp++ = '^';
*pp++ = '\\';
*pp++ = '(';
@@ -1515,7 +1534,7 @@ static int read_compound(FILE *fd, slang_T *slang, int len)
*crp = NUL;
}
- slang->sl_compprog = vim_regcomp((char *)pat, RE_MAGIC + RE_STRING + RE_STRICT);
+ slang->sl_compprog = vim_regcomp(pat, RE_MAGIC + RE_STRING + RE_STRICT);
xfree(pat);
if (slang->sl_compprog == NULL) {
return SP_FORMERROR;
@@ -1526,10 +1545,10 @@ static int read_compound(FILE *fd, slang_T *slang, int len)
// Set the SOFOFROM and SOFOTO items in language "lp".
// Returns SP_*ERROR flags when there is something wrong.
-static int set_sofo(slang_T *lp, char_u *from, char_u *to)
+static int set_sofo(slang_T *lp, char *from, char *to)
{
- char_u *s;
- char_u *p;
+ char *s;
+ char *p;
// Use "sl_sal" as an array with 256 pointers to a list of wide
// characters. The index is the low byte of the character.
@@ -1544,7 +1563,7 @@ static int set_sofo(slang_T *lp, char_u *from, char_u *to)
// First count the number of items for each list. Temporarily use
// sl_sal_first[] for this.
for (p = from, s = to; *p != NUL && *s != NUL;) {
- const int c = mb_cptr2char_adv((const char_u **)&p);
+ const int c = mb_cptr2char_adv((const char **)&p);
MB_CPTR_ADV(s);
if (c >= 256) {
lp->sl_sal_first[c & 0xff]++;
@@ -1567,8 +1586,8 @@ static int set_sofo(slang_T *lp, char_u *from, char_u *to)
// list.
memset(lp->sl_sal_first, 0, sizeof(salfirst_T) * 256);
for (p = from, s = to; *p != NUL && *s != NUL;) {
- const int c = mb_cptr2char_adv((const char_u **)&p);
- const int i = mb_cptr2char_adv((const char_u **)&s);
+ const int c = mb_cptr2char_adv((const char **)&p);
+ const int i = mb_cptr2char_adv((const char **)&s);
if (c >= 256) {
// Append the from-to chars at the end of the list with
// the low byte.
@@ -1597,7 +1616,7 @@ static void set_sal_first(slang_T *lp)
garray_T *gap = &lp->sl_sal;
sfirst = lp->sl_sal_first;
- for (int i = 0; i < 256; ++i) {
+ for (int i = 0; i < 256; i++) {
sfirst[i] = -1;
}
smp = (salitem_T *)gap->ga_data;
@@ -1637,13 +1656,13 @@ static void set_sal_first(slang_T *lp)
// Turn a multi-byte string into a wide character string.
// Return it in allocated memory.
-static int *mb_str2wide(char_u *s)
+static int *mb_str2wide(char *s)
{
int i = 0;
int *res = xmalloc(((size_t)mb_charlen(s) + 1) * sizeof(int));
- for (char_u *p = s; *p != NUL;) {
- res[i++] = mb_ptr2char_adv((const char_u **)&p);
+ for (char *p = s; *p != NUL;) {
+ res[i++] = mb_ptr2char_adv((const char **)&p);
}
res[i] = NUL;
@@ -1658,12 +1677,12 @@ static int *mb_str2wide(char_u *s)
/// @param prefixcnt when "prefixtree" is true: prefix count
///
/// @return zero when OK, SP_ value for an error.
-static int spell_read_tree(FILE *fd, char_u **bytsp, long *bytsp_len, idx_T **idxsp,
- bool prefixtree, int prefixcnt)
+static int spell_read_tree(FILE *fd, char **bytsp, long *bytsp_len, idx_T **idxsp, bool prefixtree,
+ int prefixcnt)
FUNC_ATTR_NONNULL_ARG(1, 2, 4)
{
int idx;
- char_u *bp;
+ char *bp;
idx_T *ip;
// The tree size was computed when writing the file, so that we can
@@ -1676,23 +1695,25 @@ static int spell_read_tree(FILE *fd, char_u **bytsp, long *bytsp_len, idx_T **id
// Invalid length, multiply with sizeof(int) would overflow.
return SP_FORMERROR;
}
- if (len > 0) {
- // Allocate the byte array.
- bp = xmalloc((size_t)len);
- *bytsp = bp;
- if (bytsp_len != NULL) {
- *bytsp_len = len;
- }
+ if (len <= 0) {
+ return 0;
+ }
- // Allocate the index array.
- ip = xcalloc((size_t)len, sizeof(*ip));
- *idxsp = ip;
+ // Allocate the byte array.
+ bp = xmalloc((size_t)len);
+ *bytsp = bp;
+ if (bytsp_len != NULL) {
+ *bytsp_len = len;
+ }
- // Recursively read the tree and store it in the array.
- idx = read_tree_node(fd, bp, ip, (int)len, 0, prefixtree, prefixcnt);
- if (idx < 0) {
- return idx;
- }
+ // Allocate the index array.
+ ip = xcalloc((size_t)len, sizeof(*ip));
+ *idxsp = ip;
+
+ // Recursively read the tree and store it in the array.
+ idx = read_tree_node(fd, (char_u *)bp, ip, (int)len, 0, prefixtree, prefixcnt);
+ if (idx < 0) {
+ return idx;
}
return 0;
}
@@ -1732,7 +1753,7 @@ static idx_T read_tree_node(FILE *fd, char_u *byts, idx_T *idxs, int maxidx, idx
byts[idx++] = (char_u)len;
// Read the byte values, flag/region bytes and shared indexes.
- for (i = 1; i <= len; ++i) {
+ for (i = 1; i <= len; i++) {
c = getc(fd); // <byte>
if (c < 0) {
return SP_TRUNCERROR;
@@ -1795,7 +1816,7 @@ static idx_T read_tree_node(FILE *fd, char_u *byts, idx_T *idxs, int maxidx, idx
// Recursively read the children for non-shared siblings.
// Skip the end-of-word ones (zero byte value) and the shared ones (and
// remove SHARED_MASK)
- for (i = 1; i <= len; ++i) {
+ for (i = 1; i <= len; i++) {
if (byts[startidx + i] != 0) {
if (idxs[startidx + i] & SHARED_MASK) {
idxs[startidx + i] &= ~SHARED_MASK;
@@ -1816,19 +1837,19 @@ static idx_T read_tree_node(FILE *fd, char_u *byts, idx_T *idxs, int maxidx, idx
/// Reload the spell file "fname" if it's loaded.
///
/// @param added_word invoked through "zg"
-static void spell_reload_one(char_u *fname, bool added_word)
+static void spell_reload_one(char *fname, bool added_word)
{
slang_T *slang;
bool didit = false;
for (slang = first_lang; slang != NULL; slang = slang->sl_next) {
- if (path_full_compare((char *)fname, (char *)slang->sl_fname, false, true) == kEqualFiles) {
+ if (path_full_compare(fname, slang->sl_fname, false, true) == kEqualFiles) {
slang_clear(slang);
if (spell_load_file(fname, NULL, slang, false) == NULL) {
// reloading failed, clear the language
slang_clear(slang);
}
- redraw_all_later(SOME_VALID);
+ redraw_all_later(UPD_SOME_VALID);
didit = true;
}
}
@@ -1863,7 +1884,7 @@ static long compress_added = 500000; // word count
// Sets "sps_flags".
int spell_check_msm(void)
{
- char *p = (char *)p_msm;
+ char *p = p_msm;
long start = 0;
long incr = 0;
long added = 0;
@@ -1924,7 +1945,7 @@ static void spell_clear_flags(wordnode_T *node)
wordnode_T *np;
for (np = node; np != NULL; np = np->wn_sibling) {
- np->wn_u1.index = FALSE;
+ np->wn_u1.index = false;
spell_clear_flags(np->wn_child);
}
}
@@ -1940,7 +1961,7 @@ static void spell_print_node(wordnode_T *node, int depth)
msg((char_u *)line2);
msg((char_u *)line3);
} else {
- node->wn_u1.index = TRUE;
+ node->wn_u1.index = true;
if (node->wn_byte != NUL) {
if (node->wn_child != NULL) {
@@ -1984,38 +2005,39 @@ static void spell_print_node(wordnode_T *node, int depth)
static void spell_print_tree(wordnode_T *root)
{
- if (root != NULL) {
- // Clear the "wn_u1.index" fields, used to remember what has been
- // done.
- spell_clear_flags(root);
-
- // Recursively print the tree.
- spell_print_node(root, 0);
+ if (root == NULL) {
+ return;
}
+
+ // Clear the "wn_u1.index" fields, used to remember what has been done.
+ spell_clear_flags(root);
+
+ // Recursively print the tree.
+ spell_print_node(root, 0);
}
-#endif // SPELL_PRINTTREE
+#endif // SPELL_PRINTTREE
// Reads the affix file "fname".
// Returns an afffile_T, NULL for complete failure.
-static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
+static afffile_T *spell_read_aff(spellinfo_T *spin, char *fname)
{
FILE *fd;
- char_u rline[MAXLINELEN];
- char_u *line;
- char_u *pc = NULL;
+ char rline[MAXLINELEN];
+ char *line;
+ char *pc = NULL;
#define MAXITEMCNT 30
- char_u *(items[MAXITEMCNT]);
+ char *(items[MAXITEMCNT]);
int itemcnt;
- char_u *p;
+ char *p;
int lnum = 0;
affheader_T *cur_aff = NULL;
bool did_postpone_prefix = false;
int aff_todo = 0;
hashtab_T *tp;
- char_u *low = NULL;
- char_u *fol = NULL;
- char_u *upp = NULL;
+ char *low = NULL;
+ char *fol = NULL;
+ char *upp = NULL;
int do_rep;
int do_repsal;
int do_sal;
@@ -2023,25 +2045,25 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
bool found_map = false;
hashitem_T *hi;
int l;
- int compminlen = 0; // COMPOUNDMIN value
- int compsylmax = 0; // COMPOUNDSYLMAX value
- int compoptions = 0; // COMP_ flags
- int compmax = 0; // COMPOUNDWORDMAX value
- char_u *compflags = NULL; // COMPOUNDFLAG and COMPOUNDRULE
+ int compminlen = 0; // COMPOUNDMIN value
+ int compsylmax = 0; // COMPOUNDSYLMAX value
+ int compoptions = 0; // COMP_ flags
+ int compmax = 0; // COMPOUNDWORDMAX value
+ char *compflags = NULL; // COMPOUNDFLAG and COMPOUNDRULE
// concatenated
- char_u *midword = NULL; // MIDWORD value
- char_u *syllable = NULL; // SYLLABLE value
- char_u *sofofrom = NULL; // SOFOFROM value
- char_u *sofoto = NULL; // SOFOTO value
+ char *midword = NULL; // MIDWORD value
+ char *syllable = NULL; // SYLLABLE value
+ char *sofofrom = NULL; // SOFOFROM value
+ char *sofoto = NULL; // SOFOTO value
// Open the file.
- fd = os_fopen((char *)fname, "r");
+ fd = os_fopen(fname, "r");
if (fd == NULL) {
semsg(_(e_notopen), fname);
return NULL;
}
- vim_snprintf((char *)IObuff, IOSIZE, _("Reading affix file %s..."), fname);
+ vim_snprintf(IObuff, IOSIZE, _("Reading affix file %s..."), fname);
spell_message(spin, IObuff);
// Only do REP lines when not done in another .aff file already.
@@ -2091,7 +2113,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
// item.
itemcnt = 0;
for (p = line;;) {
- while (*p != NUL && *p <= ' ') { // skip white space and CR/NL
+ while (*p != NUL && (uint8_t)(*p) <= ' ') { // skip white space and CR/NL
p++;
}
if (*p == NUL) {
@@ -2103,11 +2125,11 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
items[itemcnt++] = p;
// A few items have arbitrary text argument, don't split them.
if (itemcnt == 2 && spell_info_item(items[0])) {
- while (*p >= ' ' || *p == TAB) { // skip until CR/NL
+ while ((uint8_t)(*p) >= ' ' || *p == TAB) { // skip until CR/NL
p++;
}
} else {
- while (*p > ' ') { // skip until white space or CR/NL
+ while ((uint8_t)(*p) > ' ') { // skip until white space or CR/NL
p++;
}
}
@@ -2121,21 +2143,20 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
if (itemcnt > 0) {
if (is_aff_rule(items, itemcnt, "SET", 2) && aff->af_enc == NULL) {
// Setup for conversion from "ENC" to 'encoding'.
- aff->af_enc = enc_canonize(items[1]);
+ aff->af_enc = enc_canonize((char *)items[1]);
if (!spin->si_ascii
- && convert_setup(&spin->si_conv, aff->af_enc,
- p_enc) == FAIL) {
+ && convert_setup(&spin->si_conv, aff->af_enc, p_enc) == FAIL) {
smsg(_("Conversion in %s not supported: from %s to %s"),
fname, aff->af_enc, p_enc);
}
spin->si_conv.vc_fail = true;
} else if (is_aff_rule(items, itemcnt, "FLAG", 2)
&& aff->af_flagtype == AFT_CHAR) {
- if (STRCMP(items[1], "long") == 0) {
+ if (strcmp(items[1], "long") == 0) {
aff->af_flagtype = AFT_LONG;
- } else if (STRCMP(items[1], "num") == 0) {
+ } else if (strcmp(items[1], "num") == 0) {
aff->af_flagtype = AFT_NUM;
- } else if (STRCMP(items[1], "caplong") == 0) {
+ } else if (strcmp(items[1], "caplong") == 0) {
aff->af_flagtype = AFT_CAPLONG;
} else {
smsg(_("Invalid value for FLAG in %s line %d: %s"),
@@ -2157,9 +2178,9 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
}
} else if (spell_info_item(items[0]) && itemcnt > 1) {
p = getroom(spin,
- (spin->si_info == NULL ? 0 : STRLEN(spin->si_info))
- + STRLEN(items[0])
- + STRLEN(items[1]) + 3, false);
+ (spin->si_info == NULL ? 0 : strlen(spin->si_info))
+ + strlen(items[0])
+ + strlen(items[1]) + 3, false);
if (spin->si_info != NULL) {
STRCPY(p, spin->si_info);
STRCAT(p, "\n");
@@ -2168,63 +2189,48 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
STRCAT(p, " ");
STRCAT(p, items[1]);
spin->si_info = p;
- } else if (is_aff_rule(items, itemcnt, "MIDWORD", 2)
- && midword == NULL) {
+ } else if (is_aff_rule(items, itemcnt, "MIDWORD", 2) && midword == NULL) {
midword = getroom_save(spin, items[1]);
} else if (is_aff_rule(items, itemcnt, "TRY", 2)) {
// ignored, we look in the tree for what chars may appear
- }
- // TODO: remove "RAR" later
- else if ((is_aff_rule(items, itemcnt, "RAR", 2)
- || is_aff_rule(items, itemcnt, "RARE", 2))
- && aff->af_rare == 0) {
- aff->af_rare = affitem2flag(aff->af_flagtype, items[1],
- fname, lnum);
- }
- // TODO: remove "KEP" later
- else if ((is_aff_rule(items, itemcnt, "KEP", 2)
- || is_aff_rule(items, itemcnt, "KEEPCASE", 2))
- && aff->af_keepcase == 0) {
- aff->af_keepcase = affitem2flag(aff->af_flagtype, items[1],
- fname, lnum);
+ } else if ((is_aff_rule(items, itemcnt, "RAR", 2) // TODO(vim): remove "RAR" later
+ || is_aff_rule(items, itemcnt, "RARE", 2))
+ && aff->af_rare == 0) {
+ aff->af_rare = affitem2flag(aff->af_flagtype, items[1], fname, lnum);
+ } else if ((is_aff_rule(items, itemcnt, "KEP", 2) // TODO(vim): remove "KEP" later
+ || is_aff_rule(items, itemcnt, "KEEPCASE", 2))
+ && aff->af_keepcase == 0) {
+ aff->af_keepcase = affitem2flag(aff->af_flagtype, items[1], fname, lnum);
} else if ((is_aff_rule(items, itemcnt, "BAD", 2)
|| is_aff_rule(items, itemcnt, "FORBIDDENWORD", 2))
&& aff->af_bad == 0) {
- aff->af_bad = affitem2flag(aff->af_flagtype, items[1],
- fname, lnum);
+ aff->af_bad = affitem2flag(aff->af_flagtype, items[1], fname, lnum);
} else if (is_aff_rule(items, itemcnt, "NEEDAFFIX", 2)
&& aff->af_needaffix == 0) {
- aff->af_needaffix = affitem2flag(aff->af_flagtype, items[1],
- fname, lnum);
+ aff->af_needaffix = affitem2flag(aff->af_flagtype, items[1], fname, lnum);
} else if (is_aff_rule(items, itemcnt, "CIRCUMFIX", 2)
&& aff->af_circumfix == 0) {
- aff->af_circumfix = affitem2flag(aff->af_flagtype, items[1],
- fname, lnum);
+ aff->af_circumfix = affitem2flag(aff->af_flagtype, items[1], fname, lnum);
} else if (is_aff_rule(items, itemcnt, "NOSUGGEST", 2)
&& aff->af_nosuggest == 0) {
- aff->af_nosuggest = affitem2flag(aff->af_flagtype, items[1],
- fname, lnum);
+ aff->af_nosuggest = affitem2flag(aff->af_flagtype, items[1], fname, lnum);
} else if ((is_aff_rule(items, itemcnt, "NEEDCOMPOUND", 2)
|| is_aff_rule(items, itemcnt, "ONLYINCOMPOUND", 2))
&& aff->af_needcomp == 0) {
- aff->af_needcomp = affitem2flag(aff->af_flagtype, items[1],
- fname, lnum);
+ aff->af_needcomp = affitem2flag(aff->af_flagtype, items[1], fname, lnum);
} else if (is_aff_rule(items, itemcnt, "COMPOUNDROOT", 2)
&& aff->af_comproot == 0) {
- aff->af_comproot = affitem2flag(aff->af_flagtype, items[1],
- fname, lnum);
+ aff->af_comproot = affitem2flag(aff->af_flagtype, items[1], fname, lnum);
} else if (is_aff_rule(items, itemcnt, "COMPOUNDFORBIDFLAG", 2)
&& aff->af_compforbid == 0) {
- aff->af_compforbid = affitem2flag(aff->af_flagtype, items[1],
- fname, lnum);
+ aff->af_compforbid = affitem2flag(aff->af_flagtype, items[1], fname, lnum);
if (aff->af_pref.ht_used > 0) {
smsg(_("Defining COMPOUNDFORBIDFLAG after PFX item may give wrong results in %s line %d"),
fname, lnum);
}
} else if (is_aff_rule(items, itemcnt, "COMPOUNDPERMITFLAG", 2)
&& aff->af_comppermit == 0) {
- aff->af_comppermit = affitem2flag(aff->af_flagtype, items[1],
- fname, lnum);
+ aff->af_comppermit = affitem2flag(aff->af_flagtype, items[1], fname, lnum);
if (aff->af_pref.ht_used > 0) {
smsg(_("Defining COMPOUNDPERMITFLAG after PFX item may give wrong results in %s line %d"),
fname, lnum);
@@ -2233,7 +2239,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
&& compflags == NULL) {
// Turn flag "c" into COMPOUNDRULE compatible string "c+",
// "Na" into "Na+", "1234" into "1234+".
- p = getroom(spin, STRLEN(items[1]) + 2, false);
+ p = getroom(spin, strlen(items[1]) + 2, false);
STRCPY(p, items[1]);
STRCAT(p, "+");
compflags = p;
@@ -2249,9 +2255,9 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
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;
+ l = (int)strlen(items[1]) + 1;
if (compflags != NULL) {
- l += (int)STRLEN(compflags) + 1;
+ l += (int)strlen(compflags) + 1;
}
p = getroom(spin, (size_t)l, false);
if (compflags != NULL) {
@@ -2301,15 +2307,15 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
// Only add the couple if it isn't already there.
for (i = 0; i < gap->ga_len - 1; i += 2) {
- if (STRCMP(((char **)(gap->ga_data))[i], items[1]) == 0
- && STRCMP(((char **)(gap->ga_data))[i + 1], items[2]) == 0) {
+ if (strcmp(((char **)(gap->ga_data))[i], items[1]) == 0
+ && strcmp(((char **)(gap->ga_data))[i + 1], items[2]) == 0) {
break;
}
}
if (i >= gap->ga_len) {
ga_grow(gap, 2);
- ((char **)(gap->ga_data))[gap->ga_len++] = (char *)getroom_save(spin, items[1]);
- ((char **)(gap->ga_data))[gap->ga_len++] = (char *)getroom_save(spin, items[2]);
+ ((char **)(gap->ga_data))[gap->ga_len++] = getroom_save(spin, items[1]);
+ ((char **)(gap->ga_data))[gap->ga_len++] = getroom_save(spin, items[2]);
}
} else if (is_aff_rule(items, itemcnt, "SYLLABLE", 2)
&& syllable == NULL) {
@@ -2326,12 +2332,12 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
aff->af_pfxpostpone = true;
} else if (is_aff_rule(items, itemcnt, "IGNOREEXTRA", 1)) {
aff->af_ignoreextra = true;
- } else if ((STRCMP(items[0], "PFX") == 0
- || STRCMP(items[0], "SFX") == 0)
+ } else if ((strcmp(items[0], "PFX") == 0
+ || strcmp(items[0], "SFX") == 0)
&& aff_todo == 0
&& itemcnt >= 4) {
int lasti = 4;
- char_u key[AH_KEY_LEN];
+ char key[AH_KEY_LEN];
if (*items[0] == 'P') {
tp = &aff->af_pref;
@@ -2343,7 +2349,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
// times. The affix files that do this have an undocumented
// "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);
+ xstrlcpy(key, items[1], AH_KEY_LEN);
hi = hash_find(tp, (char *)key);
if (!HASHITEM_EMPTY(hi)) {
cur_aff = HI2AH(hi);
@@ -2358,9 +2364,8 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
} else {
// New affix letter.
cur_aff = getroom(spin, sizeof(*cur_aff), true);
- cur_aff->ah_flag = affitem2flag(aff->af_flagtype, items[1],
- fname, lnum);
- if (cur_aff->ah_flag == 0 || STRLEN(items[1]) >= AH_KEY_LEN) {
+ cur_aff->ah_flag = affitem2flag(aff->af_flagtype, items[1], fname, lnum);
+ if (cur_aff->ah_flag == 0 || strlen(items[1]) >= AH_KEY_LEN) {
break;
}
if (cur_aff->ah_flag == aff->af_bad
@@ -2384,7 +2389,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
// Check for the "S" flag, which apparently means that another
// block with the same affix name is following.
- if (itemcnt > lasti && STRCMP(items[lasti], "S") == 0) {
+ if (itemcnt > lasti && strcmp(items[lasti], "S") == 0) {
lasti++;
cur_aff->ah_follows = true;
} else {
@@ -2400,7 +2405,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
smsg(_(e_afftrailing), fname, lnum, items[lasti]);
}
- if (STRCMP(items[2], "Y") != 0 && STRCMP(items[2], "N") != 0) {
+ if (strcmp(items[2], "Y") != 0 && strcmp(items[2], "N") != 0) {
smsg(_("Expected Y or N in %s line %d: %s"),
fname, lnum, items[2]);
}
@@ -2423,10 +2428,10 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
}
aff_todo = atoi((char *)items[3]);
- } else if ((STRCMP(items[0], "PFX") == 0
- || STRCMP(items[0], "SFX") == 0)
+ } else if ((strcmp(items[0], "PFX") == 0
+ || strcmp(items[0], "SFX") == 0)
&& aff_todo > 0
- && STRCMP(cur_aff->ah_key, items[1]) == 0
+ && strcmp(cur_aff->ah_key, items[1]) == 0
&& itemcnt >= 5) {
affentry_T *aff_entry;
bool upper = false;
@@ -2436,7 +2441,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
// mean mistakes go unnoticed. Require a comment-starter.
// Hunspell uses a "-" item.
if (itemcnt > lasti && *items[lasti] != '#'
- && (STRCMP(items[lasti], "-") != 0
+ && (strcmp(items[lasti], "-") != 0
|| itemcnt != lasti + 1)) {
smsg(_(e_afftrailing), fname, lnum, items[lasti]);
}
@@ -2445,14 +2450,14 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
aff_todo--;
aff_entry = getroom(spin, sizeof(*aff_entry), true);
- if (STRCMP(items[2], "0") != 0) {
+ if (strcmp(items[2], "0") != 0) {
aff_entry->ae_chop = getroom_save(spin, items[2]);
}
- if (STRCMP(items[3], "0") != 0) {
+ if (strcmp(items[3], "0") != 0) {
aff_entry->ae_add = getroom_save(spin, items[3]);
// Recognize flags on the affix: abcd/XYZ
- aff_entry->ae_flags = (char_u *)vim_strchr((char *)aff_entry->ae_add, '/');
+ aff_entry->ae_flags = vim_strchr(aff_entry->ae_add, '/');
if (aff_entry->ae_flags != NULL) {
*aff_entry->ae_flags++ = NUL;
aff_process_flags(aff, aff_entry);
@@ -2466,16 +2471,16 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
aff_entry->ae_next = cur_aff->ah_first;
cur_aff->ah_first = aff_entry;
- if (STRCMP(items[4], ".") != 0) {
- char_u buf[MAXLINELEN];
+ if (strcmp(items[4], ".") != 0) {
+ char buf[MAXLINELEN];
aff_entry->ae_cond = getroom_save(spin, items[4]);
if (*items[0] == 'P') {
- sprintf((char *)buf, "^%s", items[4]);
+ sprintf(buf, "^%s", items[4]); // NOLINT(runtime/printf)
} else {
- sprintf((char *)buf, "%s$", items[4]);
+ sprintf(buf, "%s$", items[4]); // NOLINT(runtime/printf)
}
- aff_entry->ae_prog = vim_regcomp((char *)buf, RE_MAGIC + RE_STRING + RE_STRICT);
+ aff_entry->ae_prog = vim_regcomp(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]);
@@ -2495,19 +2500,18 @@ 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((char *)aff_entry->ae_chop)] ==
+ && aff_entry->ae_chop[utfc_ptr2len(aff_entry->ae_chop)] ==
NUL) {
int c, c_up;
- c = utf_ptr2char((char *)aff_entry->ae_chop);
+ c = utf_ptr2char(aff_entry->ae_chop);
c_up = SPELL_TOUPPER(c);
if (c_up != c
&& (aff_entry->ae_cond == NULL
- || utf_ptr2char((char *)aff_entry->ae_cond) == c)) {
- p = aff_entry->ae_add
- + STRLEN(aff_entry->ae_add);
+ || utf_ptr2char(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((char *)p) == c_up) {
+ if (utf_ptr2char(p) == c_up) {
upper = true;
aff_entry->ae_chop = NULL;
*p = NUL;
@@ -2516,14 +2520,13 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
// actual word, thus must check for the
// upper-case letter.
if (aff_entry->ae_cond != NULL) {
- char_u buf[MAXLINELEN];
+ char buf[MAXLINELEN];
onecap_copy(items[4], buf, true);
aff_entry->ae_cond = getroom_save(spin, buf);
if (aff_entry->ae_cond != NULL) {
- sprintf((char *)buf, "^%s",
- aff_entry->ae_cond);
+ sprintf(buf, "^%s", aff_entry->ae_cond); // NOLINT(runtime/printf)
vim_regfree(aff_entry->ae_prog);
- aff_entry->ae_prog = vim_regcomp((char *)buf, RE_MAGIC + RE_STRING);
+ aff_entry->ae_prog = vim_regcomp(buf, RE_MAGIC + RE_STRING);
}
}
}
@@ -2536,9 +2539,8 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
int n;
// Find a previously used condition.
- for (idx = spin->si_prefcond.ga_len - 1; idx >= 0;
- --idx) {
- p = ((char_u **)spin->si_prefcond.ga_data)[idx];
+ for (idx = spin->si_prefcond.ga_len - 1; idx >= 0; idx--) {
+ p = ((char **)spin->si_prefcond.ga_data)[idx];
if (str_equal(p, aff_entry->ae_cond)) {
break;
}
@@ -2548,12 +2550,12 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
idx = spin->si_prefcond.ga_len;
pp = GA_APPEND_VIA_PTR(char_u *, &spin->si_prefcond);
*pp = (aff_entry->ae_cond == NULL) ?
- NULL : getroom_save(spin, aff_entry->ae_cond);
+ NULL : (char_u *)getroom_save(spin, aff_entry->ae_cond);
}
// Add the prefix to the prefix tree.
if (aff_entry->ae_add == NULL) {
- p = (char_u *)"";
+ p = "";
} else {
p = aff_entry->ae_add;
}
@@ -2573,33 +2575,33 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
if (aff_entry->ae_compforbid) {
n |= WFP_COMPFORBID;
}
- tree_add_word(spin, p, spin->si_prefroot, n,
+ tree_add_word(spin, (char_u *)p, spin->si_prefroot, n,
idx, cur_aff->ah_newID);
did_postpone_prefix = true;
}
// Didn't actually use ah_newID, backup si_newprefID.
if (aff_todo == 0 && !did_postpone_prefix) {
- --spin->si_newprefID;
+ spin->si_newprefID--;
cur_aff->ah_newID = 0;
}
}
}
} else if (is_aff_rule(items, itemcnt, "FOL", 2) && fol == NULL) {
- fol = vim_strsave(items[1]);
+ fol = xstrdup(items[1]);
} else if (is_aff_rule(items, itemcnt, "LOW", 2) && low == NULL) {
- low = vim_strsave(items[1]);
+ low = xstrdup(items[1]);
} else if (is_aff_rule(items, itemcnt, "UPP", 2) && upp == NULL) {
- upp = vim_strsave(items[1]);
+ upp = xstrdup(items[1]);
} else if (is_aff_rule(items, itemcnt, "REP", 2)
|| is_aff_rule(items, itemcnt, "REPSAL", 2)) {
// Ignore REP/REPSAL count
- if (!isdigit(*items[1])) {
+ if (!isdigit((uint8_t)(*items[1]))) {
smsg(_("Expected REP(SAL) count in %s line %d"),
fname, lnum);
}
- } else if ((STRCMP(items[0], "REP") == 0
- || STRCMP(items[0], "REPSAL") == 0)
+ } else if ((strcmp(items[0], "REP") == 0
+ || strcmp(items[0], "REPSAL") == 0)
&& itemcnt >= 3) {
// REP/REPSAL item
// Myspell ignores extra arguments, we require it starts with
@@ -2621,15 +2623,15 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
}
}
add_fromto(spin, items[0][3] == 'S'
- ? &spin->si_repsal
- : &spin->si_rep, items[1], items[2]);
+ ? &spin->si_repsal
+ : &spin->si_rep, items[1], items[2]);
}
} else if (is_aff_rule(items, itemcnt, "MAP", 2)) {
// MAP item or count
if (!found_map) {
// First line contains the count.
found_map = true;
- if (!isdigit(*items[1])) {
+ if (!isdigit((uint8_t)(*items[1]))) {
smsg(_("Expected MAP count in %s line %d"),
fname, lnum);
}
@@ -2638,11 +2640,11 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
// Check that every character appears only once.
for (p = items[1]; *p != NUL;) {
- c = mb_ptr2char_adv((const char_u **)&p);
+ c = mb_ptr2char_adv((const char **)&p);
if ((!GA_EMPTY(&spin->si_map)
&& vim_strchr(spin->si_map.ga_data, c)
!= NULL)
- || vim_strchr((char *)p, c) != NULL) {
+ || vim_strchr(p, c) != NULL) {
smsg(_("Duplicate character in MAP in %s line %d"),
fname, lnum);
}
@@ -2659,17 +2661,17 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
if (do_sal) {
// SAL item (sounds-a-like)
// Either one of the known keys or a from-to pair.
- if (STRCMP(items[1], "followup") == 0) {
+ if (strcmp(items[1], "followup") == 0) {
spin->si_followup = sal_to_bool(items[2]);
- } else if (STRCMP(items[1], "collapse_result") == 0) {
+ } else if (strcmp(items[1], "collapse_result") == 0) {
spin->si_collapse = sal_to_bool(items[2]);
- } else if (STRCMP(items[1], "remove_accents") == 0) {
+ } else if (strcmp(items[1], "remove_accents") == 0) {
spin->si_rem_accents = sal_to_bool(items[2]);
} else {
// when "to" is "_" it means empty
add_fromto(spin, &spin->si_sal, items[1],
- STRCMP(items[2], "_") == 0 ? (char_u *)""
- : items[2]);
+ strcmp(items[2], "_") == 0 ? ""
+ : items[2]);
}
}
} else if (is_aff_rule(items, itemcnt, "SOFOFROM", 2)
@@ -2678,12 +2680,12 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
} else if (is_aff_rule(items, itemcnt, "SOFOTO", 2)
&& sofoto == NULL) {
sofoto = getroom_save(spin, items[1]);
- } else if (STRCMP(items[0], "COMMON") == 0) {
+ } else if (strcmp(items[0], "COMMON") == 0) {
int i;
for (i = 1; i < itemcnt; i++) {
if (HASHITEM_EMPTY(hash_find(&spin->si_commonwords, (char *)items[i]))) {
- p = vim_strsave(items[i]);
+ p = xstrdup(items[i]);
hash_add(&spin->si_commonwords, p);
}
}
@@ -2775,11 +2777,11 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
return aff;
}
-// Returns true when items[0] equals "rulename", there are "mincount" items or
-// a comment is following after item "mincount".
-static bool is_aff_rule(char_u **items, int itemcnt, char *rulename, int mincount)
+/// @return true when items[0] equals "rulename", there are "mincount" items or
+/// a comment is following after item "mincount".
+static bool is_aff_rule(char **items, int itemcnt, char *rulename, int mincount)
{
- return STRCMP(items[0], rulename) == 0
+ return strcmp(items[0], rulename) == 0
&& (itemcnt == mincount
|| (itemcnt > mincount && items[mincount][0] == '#'));
}
@@ -2788,18 +2790,18 @@ static bool is_aff_rule(char_u **items, int itemcnt, char *rulename, int mincoun
// ae_flags to ae_comppermit and ae_compforbid.
static void aff_process_flags(afffile_T *affile, affentry_T *entry)
{
- char_u *p;
+ char *p;
char_u *prevp;
unsigned flag;
if (entry->ae_flags != NULL
&& (affile->af_compforbid != 0 || affile->af_comppermit != 0)) {
for (p = entry->ae_flags; *p != NUL;) {
- prevp = p;
+ prevp = (char_u *)p;
flag = get_affitem(affile->af_flagtype, &p);
if (flag == affile->af_comppermit || flag == affile->af_compforbid) {
- STRMOVE(prevp, p);
- p = prevp;
+ STRMOVE(prevp, (char *)p);
+ p = (char *)prevp;
if (flag == affile->af_comppermit) {
entry->ae_comppermit = true;
} else {
@@ -2816,23 +2818,23 @@ static void aff_process_flags(afffile_T *affile, affentry_T *entry)
}
}
-// Returns true if "s" is the name of an info item in the affix file.
-static bool spell_info_item(char_u *s)
+/// @return true if "s" is the name of an info item in the affix file.
+static bool spell_info_item(char *s)
{
- return STRCMP(s, "NAME") == 0
- || STRCMP(s, "HOME") == 0
- || STRCMP(s, "VERSION") == 0
- || STRCMP(s, "AUTHOR") == 0
- || STRCMP(s, "EMAIL") == 0
- || STRCMP(s, "COPYRIGHT") == 0;
+ return strcmp(s, "NAME") == 0
+ || strcmp(s, "HOME") == 0
+ || strcmp(s, "VERSION") == 0
+ || strcmp(s, "AUTHOR") == 0
+ || strcmp(s, "EMAIL") == 0
+ || strcmp(s, "COPYRIGHT") == 0;
}
// Turn an affix flag name into a number, according to the FLAG type.
// returns zero for failure.
-static unsigned affitem2flag(int flagtype, char_u *item, char_u *fname, int lnum)
+static unsigned affitem2flag(int flagtype, char *item, char *fname, int lnum)
{
unsigned res;
- char_u *p = item;
+ char *p = item;
res = get_affitem(flagtype, &p);
if (res == 0) {
@@ -2855,54 +2857,54 @@ static unsigned affitem2flag(int flagtype, char_u *item, char_u *fname, int lnum
// Get one affix name from "*pp" and advance the pointer.
// Returns ZERO_FLAG for "0".
// Returns zero for an error, still advances the pointer then.
-static unsigned get_affitem(int flagtype, char_u **pp)
+static unsigned get_affitem(int flagtype, char **pp)
{
int res;
if (flagtype == AFT_NUM) {
if (!ascii_isdigit(**pp)) {
- ++*pp; // always advance, avoid getting stuck
+ (*pp)++; // always advance, avoid getting stuck
return 0;
}
- res = getdigits_int((char **)pp, true, 0);
+ res = getdigits_int(pp, true, 0);
if (res == 0) {
res = ZERO_FLAG;
}
} else {
- res = mb_ptr2char_adv((const char_u **)pp);
+ res = mb_ptr2char_adv((const char **)pp);
if (flagtype == AFT_LONG || (flagtype == AFT_CAPLONG
&& res >= 'A' && res <= 'Z')) {
if (**pp == NUL) {
return 0;
}
- res = mb_ptr2char_adv((const char_u **)pp) + (res << 16);
+ res = mb_ptr2char_adv((const char **)pp) + (res << 16);
}
}
return (unsigned)res;
}
-// Process the "compflags" string used in an affix file and append it to
-// spin->si_compflags.
-// The processing involves changing the affix names to ID numbers, so that
-// they fit in one byte.
-static void process_compflags(spellinfo_T *spin, afffile_T *aff, char_u *compflags)
+/// Process the "compflags" string used in an affix file and append it to
+/// spin->si_compflags.
+/// The processing involves changing the affix names to ID numbers, so that
+/// they fit in one byte.
+static void process_compflags(spellinfo_T *spin, afffile_T *aff, char *compflags)
{
- char_u *p;
- char_u *prevp;
+ char *p;
+ char *prevp;
unsigned flag;
compitem_T *ci;
int id;
int len;
char_u *tp;
- char_u key[AH_KEY_LEN];
+ char key[AH_KEY_LEN];
hashitem_T *hi;
// Make room for the old and the new compflags, concatenated with a / in
// between. Processing it makes it shorter, but we don't know by how
// much, thus allocate the maximum.
- len = (int)STRLEN(compflags) + 1;
+ len = (int)strlen(compflags) + 1;
if (spin->si_compflags != NULL) {
- len += (int)STRLEN(spin->si_compflags) + 1;
+ len += (int)strlen(spin->si_compflags) + 1;
}
p = getroom(spin, (size_t)len, false);
if (spin->si_compflags != NULL) {
@@ -2910,12 +2912,12 @@ static void process_compflags(spellinfo_T *spin, afffile_T *aff, char_u *compfla
STRCAT(p, "/");
}
spin->si_compflags = p;
- tp = p + STRLEN(p);
+ tp = (char_u *)p + strlen(p);
for (p = compflags; *p != NUL;) {
- if (vim_strchr("/?*+[]", *p) != NULL) {
+ if (vim_strchr("/?*+[]", (uint8_t)(*p)) != NULL) {
// Copy non-flag characters directly.
- *tp++ = *p++;
+ *tp++ = (char_u)(*p++);
} else {
// First get the flag number, also checks validity.
prevp = p;
@@ -2923,7 +2925,7 @@ static void process_compflags(spellinfo_T *spin, afffile_T *aff, char_u *compfla
if (flag != 0) {
// 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);
+ xstrlcpy(key, prevp, (size_t)(p - prevp) + 1);
hi = hash_find(&aff->af_comp, (char *)key);
if (!HASHITEM_EMPTY(hi)) {
id = HI2CI(hi)->ci_newID;
@@ -2938,7 +2940,7 @@ static void process_compflags(spellinfo_T *spin, afffile_T *aff, char_u *compfla
id = spin->si_newcompID--;
} while (vim_strchr("/?*+[]\\-^", id) != NULL);
ci->ci_newID = id;
- hash_add(&aff->af_comp, ci->ci_key);
+ hash_add(&aff->af_comp, (char *)ci->ci_key);
}
*tp++ = (char_u)id;
}
@@ -2964,22 +2966,22 @@ static void check_renumber(spellinfo_T *spin)
}
// Returns true if flag "flag" appears in affix list "afflist".
-static bool flag_in_afflist(int flagtype, char_u *afflist, unsigned flag)
+static bool flag_in_afflist(int flagtype, char *afflist, unsigned flag)
{
char *p;
unsigned n;
switch (flagtype) {
case AFT_CHAR:
- return vim_strchr((char *)afflist, (int)flag) != NULL;
+ return vim_strchr(afflist, (int)flag) != NULL;
case AFT_CAPLONG:
case AFT_LONG:
- for (p = (char *)afflist; *p != NUL;) {
- n = (unsigned)mb_ptr2char_adv((const char_u **)&p);
+ for (p = afflist; *p != NUL;) {
+ n = (unsigned)mb_ptr2char_adv((const char **)&p);
if ((flagtype == AFT_LONG || (n >= 'A' && n <= 'Z'))
&& *p != NUL) {
- n = (unsigned)mb_ptr2char_adv((const char_u **)&p) + (n << 16);
+ n = (unsigned)mb_ptr2char_adv((const char **)&p) + (n << 16);
}
if (n == flag) {
return true;
@@ -2988,7 +2990,7 @@ static bool flag_in_afflist(int flagtype, char_u *afflist, unsigned flag)
break;
case AFT_NUM:
- for (p = (char *)afflist; *p != NUL;) {
+ for (p = afflist; *p != NUL;) {
int digits = getdigits_int(&p, true, 0);
assert(digits >= 0);
n = (unsigned int)digits;
@@ -3016,42 +3018,42 @@ static void aff_check_number(int spinval, int affval, char *name)
}
}
-// Give a warning when "spinval" and "affval" strings are set and not the same.
-static void aff_check_string(char_u *spinval, char_u *affval, char *name)
+/// Give a warning when "spinval" and "affval" strings are set and not the same.
+static void aff_check_string(char *spinval, char *affval, char *name)
{
- if (spinval != NULL && STRCMP(spinval, affval) != 0) {
+ if (spinval != NULL && strcmp(spinval, affval) != 0) {
smsg(_("%s value differs from what is used in another .aff file"),
name);
}
}
-// Returns true if strings "s1" and "s2" are equal. Also consider both being
-// NULL as equal.
-static bool str_equal(char_u *s1, char_u *s2)
+/// @return true if strings "s1" and "s2" are equal. Also consider both being
+/// NULL as equal.
+static bool str_equal(char *s1, char *s2)
{
if (s1 == NULL || s2 == NULL) {
return s1 == s2;
}
- return STRCMP(s1, s2) == 0;
+ return strcmp(s1, s2) == 0;
}
-// Add a from-to item to "gap". Used for REP and SAL items.
-// They are stored case-folded.
-static void add_fromto(spellinfo_T *spin, garray_T *gap, char_u *from, char_u *to)
+/// Add a from-to item to "gap". Used for REP and SAL items.
+/// They are stored case-folded.
+static void add_fromto(spellinfo_T *spin, garray_T *gap, char *from, char *to)
{
- char_u word[MAXWLEN];
+ char word[MAXWLEN];
fromto_T *ftp = GA_APPEND_VIA_PTR(fromto_T, gap);
- (void)spell_casefold(curwin, from, (int)STRLEN(from), word, MAXWLEN);
+ (void)spell_casefold(curwin, from, (int)strlen(from), word, MAXWLEN);
ftp->ft_from = getroom_save(spin, word);
- (void)spell_casefold(curwin, to, (int)STRLEN(to), word, MAXWLEN);
+ (void)spell_casefold(curwin, to, (int)strlen(to), word, MAXWLEN);
ftp->ft_to = getroom_save(spin, word);
}
-// Converts a boolean argument in a SAL line to true or false;
-static bool sal_to_bool(char_u *s)
+/// Converts a boolean argument in a SAL line to true or false;
+static bool sal_to_bool(char *s)
{
- return STRCMP(s, "1") == 0 || STRCMP(s, "true") == 0;
+ return strcmp(s, "1") == 0 || strcmp(s, "true") == 0;
}
// Free the structure filled by spell_read_aff().
@@ -3068,7 +3070,7 @@ static void spell_free_aff(afffile_T *aff)
// All this trouble to free the "ae_prog" items...
for (ht = &aff->af_pref;; ht = &aff->af_suff) {
todo = (int)ht->ht_used;
- for (hi = ht->ht_array; todo > 0; ++hi) {
+ for (hi = ht->ht_array; todo > 0; hi++) {
if (!HASHITEM_EMPTY(hi)) {
todo--;
ah = HI2AH(hi);
@@ -3089,16 +3091,16 @@ static void spell_free_aff(afffile_T *aff)
// Read dictionary file "fname".
// Returns OK or FAIL;
-static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
+static int spell_read_dic(spellinfo_T *spin, char *fname, afffile_T *affile)
{
hashtab_T ht;
- char_u line[MAXLINELEN];
+ char line[MAXLINELEN];
char_u *p;
char_u *afflist;
char_u store_afflist[MAXWLEN];
int pfxlen;
bool need_affix;
- char_u *dw;
+ char *dw;
char_u *pc;
char_u *w;
int l;
@@ -3114,7 +3116,7 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
Timestamp last_msg_time = 0;
// Open the file.
- fd = os_fopen((char *)fname, "r");
+ fd = os_fopen(fname, "r");
if (fd == NULL) {
semsg(_(e_notopen), fname);
return FAIL;
@@ -3123,7 +3125,7 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
// The hashtable is only used to detect duplicated words.
hash_init(&ht);
- vim_snprintf((char *)IObuff, IOSIZE,
+ vim_snprintf(IObuff, IOSIZE,
_("Reading dictionary file %s..."), fname);
spell_message(spin, IObuff);
@@ -3131,14 +3133,14 @@ 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((char *)line))) {
+ if (vim_fgets((char *)line, MAXLINELEN, fd) || !ascii_isdigit(*skipwhite((char *)line))) {
semsg(_("E760: No word count in %s"), fname);
}
// Read all the lines in the file one by one.
// The words are converted to 'encoding' here, before being added to
// the hashtable.
- while (!vim_fgets(line, MAXLINELEN, fd) && !got_int) {
+ while (!vim_fgets((char *)line, MAXLINELEN, fd) && !got_int) {
line_breakcheck();
lnum++;
if (line[0] == '#' || line[0] == '/') {
@@ -3146,7 +3148,7 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
}
// Remove CR, LF and white space from the end. White space halfway through
// the word is kept to allow multi-word terms like "et al.".
- l = (int)STRLEN(line);
+ l = (int)strlen(line);
while (l > 0 && line[l - 1] <= ' ') {
l--;
}
@@ -3157,7 +3159,7 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
// Convert from "SET" to 'encoding' when needed.
if (spin->si_conv.vc_type != CONV_NONE) {
- pc = string_convert(&spin->si_conv, line, NULL);
+ pc = (char_u *)string_convert(&spin->si_conv, (char *)line, NULL);
if (pc == NULL) {
smsg(_("Conversion failure for word in %s line %d: %s"),
fname, lnum, line);
@@ -3166,7 +3168,7 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
w = pc;
} else {
pc = NULL;
- w = line;
+ w = (char_u *)line;
}
// Truncate the word at the "/", set "afflist" to what follows.
@@ -3174,7 +3176,7 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
afflist = NULL;
for (p = w; *p != NUL; MB_PTR_ADV(p)) {
if (*p == '\\' && (p[1] == '\\' || p[1] == '/')) {
- STRMOVE(p, p + 1);
+ STRMOVE(p, (char *)p + 1);
} else if (*p == '/') {
*p = NUL;
afflist = p + 1;
@@ -3183,7 +3185,7 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
}
// Skip non-ASCII words when "spin->si_ascii" is true.
- if (spin->si_ascii && has_non_ascii(w)) {
+ if (spin->si_ascii && has_non_ascii((char *)w)) {
non_ascii++;
xfree(pc);
continue;
@@ -3199,7 +3201,7 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
_("line %6d, word %6ld - %s"),
lnum, spin->si_foldwcount + spin->si_keepwcount, w);
msg_start();
- msg_outtrans_long_attr(message, 0);
+ msg_outtrans_long_attr((char *)message, 0);
msg_clr_eos();
msg_didout = false;
msg_col = 0;
@@ -3208,7 +3210,7 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
}
// Store the word in the hashtable to be able to find duplicates.
- dw = getroom_save(spin, w);
+ dw = getroom_save(spin, (char *)w);
if (dw == NULL) {
retval = FAIL;
xfree(pc);
@@ -3216,7 +3218,7 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
}
hash = hash_hash(dw);
- hi = hash_lookup(&ht, (const char *)dw, STRLEN(dw), hash);
+ hi = hash_lookup(&ht, (const char *)dw, strlen(dw), hash);
if (!HASHITEM_EMPTY(hi)) {
if (p_verbose > 0) {
smsg(_("Duplicate word in %s line %d: %s"),
@@ -3236,45 +3238,45 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
need_affix = false;
if (afflist != NULL) {
// Extract flags from the affix list.
- flags |= get_affix_flags(affile, afflist);
+ flags |= get_affix_flags(affile, (char *)afflist);
if (affile->af_needaffix != 0
- && flag_in_afflist(affile->af_flagtype, afflist,
+ && flag_in_afflist(affile->af_flagtype, (char *)afflist,
affile->af_needaffix)) {
need_affix = true;
}
if (affile->af_pfxpostpone) {
// Need to store the list of prefix IDs with the word.
- pfxlen = get_pfxlist(affile, afflist, store_afflist);
+ pfxlen = get_pfxlist(affile, (char *)afflist, store_afflist);
}
if (spin->si_compflags != NULL) {
// Need to store the list of compound flags with the word.
// Concatenate them to the list of prefix IDs.
- get_compflags(affile, afflist, store_afflist + pfxlen);
+ get_compflags(affile, (char *)afflist, store_afflist + pfxlen);
}
}
// Add the word to the word tree(s).
if (store_word(spin, dw, flags, spin->si_region,
- store_afflist, need_affix) == FAIL) {
+ (char *)store_afflist, need_affix) == FAIL) {
retval = FAIL;
}
if (afflist != NULL) {
// Find all matching suffixes and add the resulting words.
// Additionally do matching prefixes that combine.
- if (store_aff_word(spin, dw, afflist, affile,
+ if (store_aff_word(spin, dw, (char *)afflist, affile,
&affile->af_suff, &affile->af_pref,
- CONDIT_SUF, flags, store_afflist, pfxlen) == FAIL) {
+ CONDIT_SUF, flags, (char *)store_afflist, pfxlen) == FAIL) {
retval = FAIL;
}
// Find all matching prefixes and add the resulting words.
- if (store_aff_word(spin, dw, afflist, affile,
+ if (store_aff_word(spin, dw, (char *)afflist, affile,
&affile->af_pref, NULL,
- CONDIT_SUF, flags, store_afflist, pfxlen) == FAIL) {
+ CONDIT_SUF, flags, (char *)store_afflist, pfxlen) == FAIL) {
retval = FAIL;
}
}
@@ -3297,7 +3299,7 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
// Check for affix flags in "afflist" that are turned into word flags.
// Return WF_ flags.
-static int get_affix_flags(afffile_T *affile, char_u *afflist)
+static int get_affix_flags(afffile_T *affile, char *afflist)
{
int flags = 0;
@@ -3336,13 +3338,13 @@ static int get_affix_flags(afffile_T *affile, char_u *afflist)
// Used for PFXPOSTPONE.
// Put the resulting flags in "store_afflist[MAXWLEN]" with a terminating NUL
// and return the number of affixes.
-static int get_pfxlist(afffile_T *affile, char_u *afflist, char_u *store_afflist)
+static int get_pfxlist(afffile_T *affile, char *afflist, char_u *store_afflist)
{
- char_u *p;
- char_u *prevp;
+ char *p;
+ char *prevp;
int cnt = 0;
int id;
- char_u key[AH_KEY_LEN];
+ char key[AH_KEY_LEN];
hashitem_T *hi;
for (p = afflist; *p != NUL;) {
@@ -3350,7 +3352,7 @@ static int get_pfxlist(afffile_T *affile, char_u *afflist, char_u *store_afflist
if (get_affitem(affile->af_flagtype, &p) != 0) {
// 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);
+ xstrlcpy(key, prevp, (size_t)(p - prevp) + 1);
hi = hash_find(&affile->af_pref, (char *)key);
if (!HASHITEM_EMPTY(hi)) {
id = HI2AH(hi)->ah_newID;
@@ -3371,19 +3373,19 @@ static int get_pfxlist(afffile_T *affile, char_u *afflist, char_u *store_afflist
// Get the list of compound IDs from the affix list "afflist" that are used
// for compound words.
// Puts the flags in "store_afflist[]".
-static void get_compflags(afffile_T *affile, char_u *afflist, char_u *store_afflist)
+static void get_compflags(afffile_T *affile, char *afflist, char_u *store_afflist)
{
- char_u *p;
- char_u *prevp;
+ char *p;
+ char *prevp;
int cnt = 0;
- char_u key[AH_KEY_LEN];
+ char key[AH_KEY_LEN];
hashitem_T *hi;
for (p = afflist; *p != NUL;) {
prevp = p;
if (get_affitem(affile->af_flagtype, &p) != 0) {
// A flag is a compound flag if it appears in "af_comp".
- STRLCPY(key, prevp, p - prevp + 1);
+ xstrlcpy(key, prevp, (size_t)(p - prevp) + 1);
hi = hash_find(&affile->af_comp, (char *)key);
if (!HASHITEM_EMPTY(hi)) {
store_afflist[cnt++] = (char_u)HI2CI(hi)->ci_newID;
@@ -3412,29 +3414,29 @@ static void get_compflags(afffile_T *affile, char_u *afflist, char_u *store_affl
/// @param pfxlen nr of flags in "pfxlist" for prefixes, rest is compound flags
///
/// @return FAIL when out of memory.
-static int store_aff_word(spellinfo_T *spin, char_u *word, char_u *afflist, afffile_T *affile,
- hashtab_T *ht, hashtab_T *xht, int condit, int flags, char_u *pfxlist,
+static int store_aff_word(spellinfo_T *spin, char *word, char *afflist, afffile_T *affile,
+ hashtab_T *ht, hashtab_T *xht, int condit, int flags, char *pfxlist,
int pfxlen)
{
int todo;
hashitem_T *hi;
affheader_T *ah;
affentry_T *ae;
- char_u newword[MAXWLEN];
+ char newword[MAXWLEN];
int retval = OK;
int i, j;
- char_u *p;
+ char *p;
int use_flags;
- char_u *use_pfxlist;
+ char *use_pfxlist;
int use_pfxlen;
bool need_affix;
char_u store_afflist[MAXWLEN];
- char_u pfx_pfxlist[MAXWLEN];
- size_t wordlen = STRLEN(word);
+ char pfx_pfxlist[MAXWLEN];
+ size_t wordlen = strlen(word);
int use_condit;
todo = (int)ht->ht_used;
- for (hi = ht->ht_array; todo > 0 && retval == OK; ++hi) {
+ for (hi = ht->ht_array; todo > 0 && retval == OK; hi++) {
if (!HASHITEM_EMPTY(hi)) {
todo--;
ah = HI2AH(hi);
@@ -3460,7 +3462,7 @@ static int store_aff_word(spellinfo_T *spin, char_u *word, char_u *afflist, afff
|| ae->ae_chop != NULL
|| ae->ae_flags != NULL)
&& (ae->ae_chop == NULL
- || STRLEN(ae->ae_chop) < wordlen)
+ || strlen(ae->ae_chop) < wordlen)
&& (ae->ae_prog == NULL
|| vim_regexec_prog(&ae->ae_prog, false, word, (colnr_T)0))
&& (((condit & CONDIT_CFIX) == 0)
@@ -3474,7 +3476,7 @@ static int store_aff_word(spellinfo_T *spin, char_u *word, char_u *afflist, afff
if (ae->ae_add == NULL) {
*newword = NUL;
} else {
- STRLCPY(newword, ae->ae_add, MAXWLEN);
+ xstrlcpy(newword, ae->ae_add, MAXWLEN);
}
p = word;
if (ae->ae_chop != NULL) {
@@ -3487,10 +3489,10 @@ static int store_aff_word(spellinfo_T *spin, char_u *word, char_u *afflist, afff
STRCAT(newword, p);
} else {
// suffix: chop/add at the end of the word
- STRLCPY(newword, word, MAXWLEN);
+ xstrlcpy(newword, word, MAXWLEN);
if (ae->ae_chop != NULL) {
// Remove chop string.
- p = newword + STRLEN(newword);
+ p = newword + strlen(newword);
i = mb_charlen(ae->ae_chop);
for (; i > 0; i--) {
MB_PTR_BACK(newword, p);
@@ -3511,16 +3513,18 @@ static int store_aff_word(spellinfo_T *spin, char_u *word, char_u *afflist, afff
// Extract flags from the affix list.
use_flags |= get_affix_flags(affile, ae->ae_flags);
- if (affile->af_needaffix != 0 && flag_in_afflist(affile->af_flagtype, ae->ae_flags,
- affile->af_needaffix)) {
+ if (affile->af_needaffix != 0
+ && flag_in_afflist(affile->af_flagtype, ae->ae_flags,
+ affile->af_needaffix)) {
need_affix = true;
}
// When there is a CIRCUMFIX flag the other affix
// must also have it and we don't add the word
// with one affix.
- if (affile->af_circumfix != 0 && flag_in_afflist(affile->af_flagtype, ae->ae_flags,
- affile->af_circumfix)) {
+ if (affile->af_circumfix != 0
+ && flag_in_afflist(affile->af_flagtype, ae->ae_flags,
+ affile->af_circumfix)) {
use_condit |= CONDIT_CFIX;
if ((condit & CONDIT_CFIX) == 0) {
need_affix = true;
@@ -3531,17 +3535,16 @@ static int store_aff_word(spellinfo_T *spin, char_u *word, char_u *afflist, afff
|| spin->si_compflags != NULL) {
if (affile->af_pfxpostpone) {
// Get prefix IDS from the affix list.
- use_pfxlen = get_pfxlist(affile,
- ae->ae_flags, store_afflist);
+ use_pfxlen = get_pfxlist(affile, ae->ae_flags, store_afflist);
} else {
use_pfxlen = 0;
}
- use_pfxlist = store_afflist;
+ use_pfxlist = (char *)store_afflist;
// Combine the prefix IDs. Avoid adding the
// same ID twice.
- for (i = 0; i < pfxlen; ++i) {
- for (j = 0; j < use_pfxlen; ++j) {
+ for (i = 0; i < pfxlen; i++) {
+ for (j = 0; j < use_pfxlen; j++) {
if (pfxlist[i] == use_pfxlist[j]) {
break;
}
@@ -3554,7 +3557,7 @@ static int store_aff_word(spellinfo_T *spin, char_u *word, char_u *afflist, afff
if (spin->si_compflags != NULL) {
// Get compound IDS from the affix list.
get_compflags(affile, ae->ae_flags,
- use_pfxlist + use_pfxlen);
+ (char_u *)use_pfxlist + use_pfxlen);
} else {
use_pfxlist[use_pfxlen] = NUL;
}
@@ -3562,9 +3565,8 @@ static int store_aff_word(spellinfo_T *spin, char_u *word, char_u *afflist, afff
// Combine the list of compound flags.
// Concatenate them to the prefix IDs list.
// Avoid adding the same ID twice.
- for (i = pfxlen; pfxlist[i] != NUL; ++i) {
- for (j = use_pfxlen;
- use_pfxlist[j] != NUL; ++j) {
+ for (i = pfxlen; pfxlist[i] != NUL; i++) {
+ for (j = use_pfxlen; use_pfxlist[j] != NUL; j++) {
if (pfxlist[i] == use_pfxlist[j]) {
break;
}
@@ -3580,7 +3582,7 @@ static int store_aff_word(spellinfo_T *spin, char_u *word, char_u *afflist, afff
// Obey a "COMPOUNDFORBIDFLAG" of the affix: don't
// use the compound flags.
if (use_pfxlist != NULL && ae->ae_compforbid) {
- STRLCPY(pfx_pfxlist, use_pfxlist, use_pfxlen + 1);
+ xstrlcpy(pfx_pfxlist, use_pfxlist, (size_t)use_pfxlen + 1);
use_pfxlist = pfx_pfxlist;
}
@@ -3622,7 +3624,7 @@ static int store_aff_word(spellinfo_T *spin, char_u *word, char_u *afflist, afff
if (store_aff_word(spin, newword, ae->ae_flags,
affile, &affile->af_suff, xht,
use_condit & (xht == NULL
- ? ~0 : ~CONDIT_SUF),
+ ? ~0 : ~CONDIT_SUF),
use_flags, use_pfxlist, pfxlen) == FAIL) {
retval = FAIL;
}
@@ -3656,12 +3658,12 @@ static int store_aff_word(spellinfo_T *spin, char_u *word, char_u *afflist, afff
}
// Read a file with a list of words.
-static int spell_read_wordfile(spellinfo_T *spin, char_u *fname)
+static int spell_read_wordfile(spellinfo_T *spin, char *fname)
{
FILE *fd;
long lnum = 0;
- char_u rline[MAXLINELEN];
- char_u *line;
+ char rline[MAXLINELEN];
+ char *line;
char_u *pc = NULL;
char_u *p;
int l;
@@ -3672,13 +3674,13 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *fname)
int regionmask;
// Open the file.
- fd = os_fopen((char *)fname, "r");
+ fd = os_fopen(fname, "r");
if (fd == NULL) {
semsg(_(e_notopen), fname);
return FAIL;
}
- vim_snprintf((char *)IObuff, IOSIZE, _("Reading word file %s..."), fname);
+ vim_snprintf(IObuff, IOSIZE, _("Reading word file %s..."), fname);
spell_message(spin, IObuff);
// Read all the lines in the file one by one.
@@ -3692,8 +3694,8 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *fname)
}
// Remove CR, LF and white space from the end.
- l = (int)STRLEN(rline);
- while (l > 0 && rline[l - 1] <= ' ') {
+ l = (int)strlen(rline);
+ while (l > 0 && (uint8_t)rline[l - 1] <= ' ') {
l--;
}
if (l == 0) {
@@ -3704,13 +3706,13 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *fname)
// Convert from "/encoding={encoding}" to 'encoding' when needed.
xfree(pc);
if (spin->si_conv.vc_type != CONV_NONE) {
- pc = string_convert(&spin->si_conv, rline, NULL);
+ pc = (char_u *)string_convert(&spin->si_conv, rline, NULL);
if (pc == NULL) {
smsg(_("Conversion failure for word in %s line %ld: %s"),
fname, lnum, rline);
continue;
}
- line = pc;
+ line = (char *)pc;
} else {
pc = NULL;
line = rline;
@@ -3718,7 +3720,7 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *fname)
if (*line == '/') {
line++;
- if (STRNCMP(line, "encoding=", 9) == 0) {
+ if (strncmp(line, "encoding=", 9) == 0) {
if (spin->si_conv.vc_type != CONV_NONE) {
smsg(_("Duplicate /encoding= line ignored in %s line %ld: %s"),
fname, lnum, line - 1);
@@ -3726,14 +3728,13 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *fname)
smsg(_("/encoding= line after word ignored in %s line %ld: %s"),
fname, lnum, line - 1);
} else {
- char_u *enc;
+ char *enc;
// Setup for conversion to 'encoding'.
line += 9;
enc = enc_canonize(line);
if (!spin->si_ascii
- && convert_setup(&spin->si_conv, enc,
- p_enc) == FAIL) {
+ && convert_setup(&spin->si_conv, enc, p_enc) == FAIL) {
smsg(_("Conversion in %s not supported: from %s to %s"),
fname, line, p_enc);
}
@@ -3743,17 +3744,17 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *fname)
continue;
}
- if (STRNCMP(line, "regions=", 8) == 0) {
+ if (strncmp(line, "regions=", 8) == 0) {
if (spin->si_region_count > 1) {
smsg(_("Duplicate /regions= line ignored in %s line %ld: %s"),
fname, lnum, line);
} else {
line += 8;
- if (STRLEN(line) > MAXREGIONS * 2) {
+ if (strlen(line) > MAXREGIONS * 2) {
smsg(_("Too many regions in %s line %ld: %s"),
fname, lnum, line);
} else {
- spin->si_region_count = (int)STRLEN(line) / 2;
+ spin->si_region_count = (int)strlen(line) / 2;
STRCPY(spin->si_region_name, line);
// Adjust the mask for a word valid in all regions.
@@ -3772,7 +3773,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 = (char_u *)vim_strchr((char *)line, '/');
+ p = (char_u *)vim_strchr(line, '/');
if (p != NULL) {
*p++ = NUL;
while (*p != NUL) {
@@ -3822,7 +3823,7 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *fname)
fclose(fd);
if (spin->si_ascii && non_ascii > 0) {
- vim_snprintf((char *)IObuff, IOSIZE,
+ vim_snprintf(IObuff, IOSIZE,
_("Ignored %d words with non-ASCII characters"), non_ascii);
spell_message(spin, IObuff);
}
@@ -3858,7 +3859,7 @@ static void *getroom(spellinfo_T *spin, size_t len, bool align)
bl->sb_next = spin->si_blocks;
spin->si_blocks = bl;
bl->sb_used = 0;
- ++spin->si_blocks_cnt;
+ spin->si_blocks_cnt++;
}
p = bl->sb_data + bl->sb_used;
@@ -3867,11 +3868,12 @@ static void *getroom(spellinfo_T *spin, size_t len, bool align)
return p;
}
-// Make a copy of a string into memory allocated with getroom().
-// Returns NULL when out of memory.
-static char_u *getroom_save(spellinfo_T *spin, char_u *s)
+/// Make a copy of a string into memory allocated with getroom().
+///
+/// @return NULL when out of memory.
+static char *getroom_save(spellinfo_T *spin, char *s)
{
- const size_t s_size = STRLEN(s) + 1;
+ const size_t s_size = strlen(s) + 1;
return memcpy(getroom(spin, s_size, false), s, s_size);
}
@@ -3897,13 +3899,13 @@ static wordnode_T *wordtree_alloc(spellinfo_T *spin)
/// 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)
+static bool valid_spell_word(const char *word, const char *end)
{
- if (!utf_valid_string(word, end)) {
+ if (!utf_valid_string((char_u *)word, (char_u *)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)) {
+ for (const char *p = word; *p != NUL && p < end; p += utfc_ptr2len(p)) {
+ if ((uint8_t)(*p) < ' ' || (p[0] == '/' && p[1] == NUL)) {
return false;
}
}
@@ -3922,10 +3924,10 @@ static bool valid_spell_word(const char_u *word, const char_u *end)
/// @param region supported region(s)
/// @param pfxlist list of prefix ids or null
/// @param need_affix only store word with affix id
-static int store_word(spellinfo_T *spin, char_u *word, int flags, int region, const char_u *pfxlist,
+static int store_word(spellinfo_T *spin, char *word, int flags, int region, const char *pfxlist,
bool need_affix)
{
- int len = (int)STRLEN(word);
+ int len = (int)strlen(word);
int ct = captype(word, word + len);
char_u foldword[MAXWLEN];
int res = OK;
@@ -3935,8 +3937,8 @@ static int store_word(spellinfo_T *spin, char_u *word, int flags, int region, co
return FAIL;
}
- (void)spell_casefold(curwin, word, len, foldword, MAXWLEN);
- for (const char_u *p = pfxlist; res == OK; p++) {
+ (void)spell_casefold(curwin, word, len, (char *)foldword, MAXWLEN);
+ for (const char_u *p = (char_u *)pfxlist; res == OK; p++) {
if (!need_affix || (p != NULL && *p != NUL)) {
res = tree_add_word(spin, foldword, spin->si_foldroot, ct | flags,
region, p == NULL ? 0 : *p);
@@ -3945,19 +3947,19 @@ static int store_word(spellinfo_T *spin, char_u *word, int flags, int region, co
break;
}
}
- ++spin->si_foldwcount;
+ spin->si_foldwcount++;
if (res == OK && (ct == WF_KEEPCAP || (flags & WF_KEEPCAP))) {
- for (const char_u *p = pfxlist; res == OK; p++) {
+ for (const char_u *p = (char_u *)pfxlist; res == OK; p++) {
if (!need_affix || (p != NULL && *p != NUL)) {
- res = tree_add_word(spin, word, spin->si_keeproot, flags,
+ res = tree_add_word(spin, (char_u *)word, spin->si_keeproot, flags,
region, p == NULL ? 0 : *p);
}
if (p == NULL || *p == NUL) {
break;
}
}
- ++spin->si_keepwcount;
+ spin->si_keepwcount++;
}
return res;
}
@@ -3966,8 +3968,8 @@ static int store_word(spellinfo_T *spin, char_u *word, int flags, int region, co
// When "flags" < 0 we are adding to the prefix tree where "flags" is used for
// "rare" and "region" is the condition nr.
// Returns FAIL when out of memory.
-static int tree_add_word(spellinfo_T *spin, char_u *word, wordnode_T *root, int flags, int region,
- int affixID)
+static int tree_add_word(spellinfo_T *spin, const char_u *word, wordnode_T *root, int flags,
+ int region, int affixID)
{
wordnode_T *node = root;
wordnode_T *np;
@@ -3976,12 +3978,12 @@ static int tree_add_word(spellinfo_T *spin, char_u *word, wordnode_T *root, int
int i;
// Add each byte of the word to the tree, including the NUL at the end.
- for (i = 0;; ++i) {
+ for (i = 0;; i++) {
// When there is more than one reference to this node we need to make
// a copy, so that we can modify it. Copy the whole list of siblings
// (we don't optimize for a partly shared list of siblings).
if (node != NULL && node->wn_refs > 1) {
- --node->wn_refs;
+ node->wn_refs--;
copyprev = prev;
for (copyp = node; copyp != NULL; copyp = copyp->wn_sibling) {
// Allocate a new node and copy the info.
@@ -3991,7 +3993,7 @@ static int tree_add_word(spellinfo_T *spin, char_u *word, wordnode_T *root, int
}
np->wn_child = copyp->wn_child;
if (np->wn_child != NULL) {
- ++np->wn_child->wn_refs; // child gets extra ref
+ np->wn_child->wn_refs++; // child gets extra ref
}
np->wn_byte = copyp->wn_byte;
if (np->wn_byte == NUL) {
@@ -4078,7 +4080,7 @@ static int tree_add_word(spellinfo_T *spin, char_u *word, wordnode_T *root, int
#endif
// count nr of words added since last message
- ++spin->si_msg_count;
+ spin->si_msg_count++;
if (spin->si_compress_cnt > 1) {
if (--spin->si_compress_cnt == 1) {
@@ -4097,7 +4099,7 @@ static int tree_add_word(spellinfo_T *spin, char_u *word, wordnode_T *root, int
// 3. When compressed before, added "compress_added" words
// (si_compress_cnt == 1) and the number of free nodes drops below the
// maximum word length.
-#ifndef SPELL_COMPRESS_ALLWAYS
+#ifndef SPELL_COMPRESS_ALWAYS
if (spin->si_compress_cnt == 1 // NOLINT(readability/braces)
? spin->si_free_count < MAXWLEN
: spin->si_blocks_cnt >= compress_start)
@@ -4173,7 +4175,7 @@ static int deref_wordnode(spellinfo_T *spin, wordnode_T *node)
free_wordnode(spin, np);
cnt++;
}
- ++cnt; // length field
+ cnt++; // length field
}
return cnt;
}
@@ -4185,7 +4187,7 @@ static void free_wordnode(spellinfo_T *spin, wordnode_T *n)
{
n->wn_child = spin->si_first_free;
spin->si_first_free = n;
- ++spin->si_free_count;
+ spin->si_free_count++;
}
// Compress a tree: find tails that are identical and can be shared.
@@ -4198,31 +4200,33 @@ static void wordtree_compress(spellinfo_T *spin, wordnode_T *root, const char *n
// Skip the root itself, it's not actually used. The first sibling is the
// start of the tree.
- if (root->wn_sibling != NULL) {
- hash_init(&ht);
- const long n = node_compress(spin, root->wn_sibling, &ht, &tot);
+ if (root->wn_sibling == NULL) {
+ return;
+ }
+
+ hash_init(&ht);
+ const long n = node_compress(spin, root->wn_sibling, &ht, &tot);
#ifndef SPELL_PRINTTREE
- if (spin->si_verbose || p_verbose > 2)
+ if (spin->si_verbose || p_verbose > 2)
#endif
- {
- if (tot > 1000000) {
- perc = (tot - n) / (tot / 100);
- } else if (tot == 0) {
- perc = 0;
- } else {
- perc = (tot - n) * 100 / tot;
- }
- vim_snprintf((char *)IObuff, IOSIZE,
- _("Compressed %s of %ld nodes; %ld (%ld%%) remaining"),
- name, tot, tot - n, perc);
- spell_message(spin, IObuff);
+ {
+ if (tot > 1000000) {
+ perc = (tot - n) / (tot / 100);
+ } else if (tot == 0) {
+ perc = 0;
+ } else {
+ perc = (tot - n) * 100 / tot;
}
+ vim_snprintf(IObuff, IOSIZE,
+ _("Compressed %s of %ld nodes; %ld (%ld%%) remaining"),
+ name, tot, tot - n, perc);
+ spell_message(spin, IObuff);
+ }
#ifdef SPELL_PRINTTREE
- spell_print_tree(root->wn_sibling);
+ spell_print_tree(root->wn_sibling);
#endif
- hash_clear(&ht);
- }
+ hash_clear(&ht);
}
/// Compress a node, its siblings and its children, depth first.
@@ -4252,9 +4256,9 @@ static long node_compress(spellinfo_T *spin, wordnode_T *node, hashtab_T *ht, lo
compressed += node_compress(spin, child, ht, tot);
// Try to find an identical child.
- hash = hash_hash(child->wn_u1.hashkey);
+ hash = hash_hash((char *)child->wn_u1.hashkey);
hi = hash_lookup(ht, (const char *)child->wn_u1.hashkey,
- STRLEN(child->wn_u1.hashkey), hash);
+ strlen((char *)child->wn_u1.hashkey), hash);
if (!HASHITEM_EMPTY(hi)) {
// There are children we encountered before with a hash value
// identical to the current child. Now check if there is one
@@ -4264,7 +4268,7 @@ static long node_compress(spellinfo_T *spin, wordnode_T *node, hashtab_T *ht, lo
// Found one! Now use that child in place of the
// current one. This means the current child and all
// its siblings is unlinked from the tree.
- ++tp->wn_refs;
+ tp->wn_refs++;
compressed += deref_wordnode(spin, child);
np->wn_child = tp;
break;
@@ -4281,7 +4285,7 @@ static long node_compress(spellinfo_T *spin, wordnode_T *node, hashtab_T *ht, lo
} else {
// No other child has this hash value, add it to the
// hashtable.
- hash_add_item(ht, hi, child->wn_u1.hashkey, hash);
+ hash_add_item(ht, hi, (char *)child->wn_u1.hashkey, hash);
}
}
}
@@ -4341,23 +4345,24 @@ 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.
+/// Function given to qsort() to sort the REP items on "from" string.
static int rep_compare(const void *s1, const void *s2)
{
fromto_T *p1 = (fromto_T *)s1;
fromto_T *p2 = (fromto_T *)s2;
- return STRCMP(p1->ft_from, p2->ft_from);
+ return strcmp(p1->ft_from, p2->ft_from);
}
-// Write the Vim .spl file "fname".
-// Return OK/FAIL.
-static int write_vim_spell(spellinfo_T *spin, char_u *fname)
+/// Write the Vim .spl file "fname".
+///
+/// @return OK/FAIL.
+static int write_vim_spell(spellinfo_T *spin, char *fname)
{
int retval = OK;
int regionmask;
- FILE *fd = os_fopen((char *)fname, "w");
+ FILE *fd = os_fopen(fname, "w");
if (fd == NULL) {
semsg(_(e_notopen), fname);
return FAIL;
@@ -4379,7 +4384,7 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname)
if (spin->si_info != NULL) {
putc(SN_INFO, fd); // <sectionID>
putc(0, fd); // <sectionflags>
- size_t i = STRLEN(spin->si_info);
+ size_t i = strlen(spin->si_info);
put_bytes(fd, i, 4); // <sectionlen>
fwv &= fwrite(spin->si_info, i, 1, fd); // <infotext>
}
@@ -4422,7 +4427,7 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname)
put_bytes(fd, 1 + 128 + 2 + l, 4); // <sectionlen>
fputc(128, fd); // <charflagslen>
- for (size_t i = 128; i < 256; ++i) {
+ for (size_t i = 128; i < 256; i++) {
flags = 0;
if (spelltab.st_isw[i]) {
flags |= CF_WORD;
@@ -4442,7 +4447,7 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname)
putc(SN_MIDWORD, fd); // <sectionID>
putc(SNF_REQUIRED, fd); // <sectionflags>
- size_t i = STRLEN(spin->si_midword);
+ size_t i = strlen(spin->si_midword);
put_bytes(fd, i, 4); // <sectionlen>
fwv &= fwrite(spin->si_midword, i, 1, fd);
// <midword>
@@ -4466,7 +4471,7 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname)
// round 1: SN_REP section
// round 2: SN_SAL section (unless SN_SOFO is used)
// round 3: SN_REPSAL section
- for (unsigned int round = 1; round <= 3; ++round) {
+ for (unsigned int round = 1; round <= 3; round++) {
garray_T *gap;
if (round == 1) {
gap = &spin->si_rep;
@@ -4500,13 +4505,13 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname)
// Compute the length of what follows.
size_t l = 2; // count <repcount> or <salcount>
assert(gap->ga_len >= 0);
- for (size_t i = 0; i < (size_t)gap->ga_len; ++i) {
+ for (size_t i = 0; i < (size_t)gap->ga_len; i++) {
fromto_T *ftp = &((fromto_T *)gap->ga_data)[i];
- l += 1 + STRLEN(ftp->ft_from); // count <*fromlen> and <*from>
- l += 1 + STRLEN(ftp->ft_to); // count <*tolen> and <*to>
+ l += 1 + strlen(ftp->ft_from); // count <*fromlen> and <*from>
+ l += 1 + strlen(ftp->ft_to); // count <*tolen> and <*to>
}
if (round == 2) {
- ++l; // count <salflags>
+ l++; // count <salflags>
}
put_bytes(fd, l, 4); // <sectionlen>
@@ -4525,13 +4530,13 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname)
}
put_bytes(fd, (uintmax_t)gap->ga_len, 2); // <repcount> or <salcount>
- for (size_t i = 0; i < (size_t)gap->ga_len; ++i) {
+ for (size_t i = 0; i < (size_t)gap->ga_len; i++) {
// <rep> : <repfromlen> <repfrom> <reptolen> <repto>
// <sal> : <salfromlen> <salfrom> <saltolen> <salto>
fromto_T *ftp = &((fromto_T *)gap->ga_data)[i];
- for (unsigned int rr = 1; rr <= 2; ++rr) {
- char_u *p = rr == 1 ? ftp->ft_from : ftp->ft_to;
- l = STRLEN(p);
+ for (unsigned int rr = 1; rr <= 2; rr++) {
+ char *p = rr == 1 ? ftp->ft_from : ftp->ft_to;
+ l = strlen(p);
assert(l < INT_MAX);
putc((int)l, fd);
if (l > 0) {
@@ -4547,13 +4552,13 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname)
putc(SN_SOFO, fd); // <sectionID>
putc(0, fd); // <sectionflags>
- size_t l = STRLEN(spin->si_sofofr);
- put_bytes(fd, l + STRLEN(spin->si_sofoto) + 4, 4); // <sectionlen>
+ size_t l = strlen(spin->si_sofofr);
+ put_bytes(fd, l + strlen(spin->si_sofoto) + 4, 4); // <sectionlen>
put_bytes(fd, l, 2); // <sofofromlen>
fwv &= fwrite(spin->si_sofofr, l, 1, fd); // <sofofrom>
- l = STRLEN(spin->si_sofoto);
+ l = strlen(spin->si_sofoto);
put_bytes(fd, l, 2); // <sofotolen>
fwv &= fwrite(spin->si_sofoto, l, 1, fd); // <sofoto>
}
@@ -4566,15 +4571,15 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname)
// round 1: count the bytes
// round 2: write the bytes
- for (unsigned int round = 1; round <= 2; ++round) {
+ for (unsigned int round = 1; round <= 2; round++) {
size_t todo;
size_t len = 0;
hashitem_T *hi;
todo = spin->si_commonwords.ht_used;
- for (hi = spin->si_commonwords.ht_array; todo > 0; ++hi) {
+ for (hi = spin->si_commonwords.ht_array; todo > 0; hi++) {
if (!HASHITEM_EMPTY(hi)) {
- size_t l = STRLEN(hi->hi_key) + 1;
+ size_t l = strlen(hi->hi_key) + 1;
len += l;
if (round == 2) { // <word>
fwv &= fwrite(hi->hi_key, l, 1, fd);
@@ -4640,10 +4645,10 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname)
putc(SN_COMPOUND, fd); // <sectionID>
putc(0, fd); // <sectionflags>
- size_t l = STRLEN(spin->si_compflags);
+ size_t l = strlen(spin->si_compflags);
assert(spin->si_comppat.ga_len >= 0);
for (size_t i = 0; i < (size_t)spin->si_comppat.ga_len; i++) {
- l += STRLEN(((char **)(spin->si_comppat.ga_data))[i]) + 1;
+ l += strlen(((char **)(spin->si_comppat.ga_data))[i]) + 1;
}
put_bytes(fd, l + 7, 4); // <sectionlen>
@@ -4655,12 +4660,12 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname)
put_bytes(fd, (uintmax_t)spin->si_comppat.ga_len, 2); // <comppatcount>
for (size_t i = 0; i < (size_t)spin->si_comppat.ga_len; i++) {
char *p = ((char **)(spin->si_comppat.ga_data))[i];
- assert(STRLEN(p) < INT_MAX);
- putc((int)STRLEN(p), fd); // <comppatlen>
- fwv &= fwrite(p, STRLEN(p), 1, fd); // <comppattext>
+ assert(strlen(p) < INT_MAX);
+ putc((int)strlen(p), fd); // <comppatlen>
+ fwv &= fwrite(p, strlen(p), 1, fd); // <comppattext>
}
// <compflags>
- fwv &= fwrite(spin->si_compflags, STRLEN(spin->si_compflags), 1, fd);
+ fwv &= fwrite(spin->si_compflags, strlen(spin->si_compflags), 1, fd);
}
// SN_NOBREAK: NOBREAK flag
@@ -4679,7 +4684,7 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname)
putc(SN_SYLLABLE, fd); // <sectionID>
putc(0, fd); // <sectionflags>
- size_t l = STRLEN(spin->si_syllable);
+ size_t l = strlen(spin->si_syllable);
put_bytes(fd, l, 4); // <sectionlen>
fwv &= fwrite(spin->si_syllable, l, 1, fd); // <syllable>
}
@@ -4689,7 +4694,7 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname)
// <LWORDTREE> <KWORDTREE> <PREFIXTREE>
spin->si_memtot = 0;
- for (unsigned int round = 1; round <= 3; ++round) {
+ for (unsigned int round = 1; round <= 3; round++) {
wordnode_T *tree;
if (round == 1) {
tree = spin->si_foldroot->wn_sibling;
@@ -4878,27 +4883,29 @@ void ex_mkspell(exarg_T *eap)
{
int fcount;
char **fnames;
- char_u *arg = (char_u *)eap->arg;
+ char *arg = eap->arg;
bool ascii = false;
- if (STRNCMP(arg, "-ascii", 6) == 0) {
+ if (strncmp(arg, "-ascii", 6) == 0) {
ascii = true;
- arg = (char_u *)skipwhite((char *)arg + 6);
+ arg = skipwhite(arg + 6);
}
// Expand all the remaining arguments (e.g., $VIMRUNTIME).
- if (get_arglist_exp(arg, &fcount, &fnames, false) == OK) {
- mkspell(fcount, fnames, ascii, eap->forceit, false);
- FreeWild(fcount, fnames);
+ if (get_arglist_exp(arg, &fcount, &fnames, false) != OK) {
+ return;
}
+
+ mkspell(fcount, fnames, ascii, eap->forceit, false);
+ FreeWild(fcount, fnames);
}
// Create the .sug file.
// Uses the soundfold info in "spin".
// Writes the file with the name "wfname", with ".spl" changed to ".sug".
-static void spell_make_sugfile(spellinfo_T *spin, char_u *wfname)
+static void spell_make_sugfile(spellinfo_T *spin, char *wfname)
{
- char_u *fname = NULL;
+ char *fname = NULL;
int len;
slang_T *slang;
bool free_slang = false;
@@ -4909,13 +4916,13 @@ 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((char *)wfname, (char *)slang->sl_fname, false, true)
+ if (path_full_compare(wfname, slang->sl_fname, false, true)
== kEqualFiles) {
break;
}
}
if (slang == NULL) {
- spell_message(spin, (char_u *)_("Reading back spell file..."));
+ spell_message(spin, _("Reading back spell file..."));
slang = spell_load_file(wfname, NULL, NULL, false);
if (slang == NULL) {
return;
@@ -4933,7 +4940,7 @@ static void spell_make_sugfile(spellinfo_T *spin, char_u *wfname)
// Go through the trie of good words, soundfold each word and add it to
// the soundfold trie.
- spell_message(spin, (char_u *)_("Performing soundfolding..."));
+ spell_message(spin, _("Performing soundfolding..."));
if (sug_filltree(spin, slang) == FAIL) {
goto theend;
}
@@ -4950,14 +4957,14 @@ static void spell_make_sugfile(spellinfo_T *spin, char_u *wfname)
(int64_t)spin->si_spellbuf->b_ml.ml_line_count);
// Compress the soundfold trie.
- spell_message(spin, (char_u *)_(msg_compressing));
+ spell_message(spin, _(msg_compressing));
wordtree_compress(spin, spin->si_foldroot, "case-folded");
// Write the .sug file.
// Make the file name by changing ".spl" to ".sug".
fname = xmalloc(MAXPATHL);
- STRLCPY(fname, wfname, MAXPATHL);
- len = (int)STRLEN(fname);
+ xstrlcpy(fname, wfname, MAXPATHL);
+ len = (int)strlen(fname);
fname[len - 2] = 'u';
fname[len - 1] = 'g';
sug_write(spin, fname);
@@ -4994,7 +5001,7 @@ static int sug_filltree(spellinfo_T *spin, slang_T *slang)
// Go through the whole case-folded tree, soundfold each word and put it
// in the trie.
- byts = slang->sl_fbyts;
+ byts = (char_u *)slang->sl_fbyts;
idxs = slang->sl_fidxs;
arridx[0] = 0;
@@ -5015,13 +5022,13 @@ static int sug_filltree(spellinfo_T *spin, slang_T *slang)
} else {
// Do one more byte at this node.
n = arridx[depth] + curi[depth];
- ++curi[depth];
+ curi[depth]++;
c = byts[n];
if (c == 0) {
// Sound-fold the word.
tword[depth] = NUL;
- spell_soundfold(slang, tword, true, tsalword);
+ spell_soundfold(slang, (char *)tword, true, (char *)tsalword);
// We use the "flags" field for the MSB of the wordnr,
// "region" for the LSB of the wordnr.
@@ -5185,16 +5192,16 @@ static int offset2bytes(int nr, char_u *buf)
}
// Write the .sug file in "fname".
-static void sug_write(spellinfo_T *spin, char_u *fname)
+static void sug_write(spellinfo_T *spin, char *fname)
{
// Create the file. Note that an existing file is silently overwritten!
- FILE *fd = os_fopen((char *)fname, "w");
+ FILE *fd = os_fopen(fname, "w");
if (fd == NULL) {
semsg(_(e_notopen), fname);
return;
}
- vim_snprintf((char *)IObuff, IOSIZE,
+ vim_snprintf(IObuff, IOSIZE,
_("Writing suggestion file %s..."), fname);
spell_message(spin, IObuff);
@@ -5233,10 +5240,10 @@ static void sug_write(spellinfo_T *spin, char_u *fname)
assert(wcount >= 0);
put_bytes(fd, (uintmax_t)wcount, 4); // <sugwcount>
- for (linenr_T lnum = 1; lnum <= wcount; ++lnum) {
+ for (linenr_T lnum = 1; lnum <= wcount; lnum++) {
// <sugline>: <sugnr> ... NUL
- char_u *line = ml_get_buf(spin->si_spellbuf, lnum, false);
- size_t len = STRLEN(line) + 1;
+ char *line = ml_get_buf(spin->si_spellbuf, lnum, false);
+ size_t len = strlen(line) + 1;
if (fwrite(line, len, 1, fd) == 0) {
emsg(_(e_write));
goto theend;
@@ -5250,7 +5257,7 @@ static void sug_write(spellinfo_T *spin, char_u *fname)
emsg(_(e_write));
}
- vim_snprintf((char *)IObuff, IOSIZE,
+ vim_snprintf(IObuff, IOSIZE,
_("Estimated runtime memory use: %d bytes"), spin->si_memtot);
spell_message(spin, IObuff);
@@ -5270,8 +5277,7 @@ theend:
/// @param added_word invoked through "zg"
static void mkspell(int fcount, char **fnames, bool ascii, bool over_write, bool added_word)
{
- char_u *fname = NULL;
- char_u *wfname;
+ char *fname = NULL;
char **innames;
int incount;
afffile_T *(afile[MAXREGIONS]);
@@ -5288,9 +5294,9 @@ static void mkspell(int fcount, char **fnames, bool ascii, bool over_write, bool
ga_init(&spin.si_rep, (int)sizeof(fromto_T), 20);
ga_init(&spin.si_repsal, (int)sizeof(fromto_T), 20);
ga_init(&spin.si_sal, (int)sizeof(fromto_T), 20);
- ga_init(&spin.si_map, (int)sizeof(char_u), 100);
- ga_init(&spin.si_comppat, (int)sizeof(char_u *), 20);
- ga_init(&spin.si_prefcond, (int)sizeof(char_u *), 50);
+ ga_init(&spin.si_map, (int)sizeof(char), 100);
+ ga_init(&spin.si_comppat, (int)sizeof(char *), 20);
+ ga_init(&spin.si_prefcond, (int)sizeof(char *), 50);
hash_init(&spin.si_commonwords);
spin.si_newcompID = 127; // start compound ID at first maximum
@@ -5299,43 +5305,43 @@ static void mkspell(int fcount, char **fnames, bool ascii, bool over_write, bool
innames = &fnames[fcount == 1 ? 0 : 1];
incount = fcount - 1;
- wfname = xmalloc(MAXPATHL);
+ char *wfname = xmalloc(MAXPATHL);
if (fcount >= 1) {
- len = (int)STRLEN(fnames[0]);
- if (fcount == 1 && len > 4 && STRCMP(fnames[0] + len - 4, ".add") == 0) {
+ len = (int)strlen(fnames[0]);
+ if (fcount == 1 && len > 4 && strcmp(fnames[0] + len - 4, ".add") == 0) {
// For ":mkspell path/en.latin1.add" output file is
// "path/en.latin1.add.spl".
incount = 1;
- vim_snprintf((char *)wfname, MAXPATHL, "%s.spl", fnames[0]);
+ vim_snprintf(wfname, MAXPATHL, "%s.spl", fnames[0]);
} else if (fcount == 1) {
// For ":mkspell path/vim" output file is "path/vim.latin1.spl".
incount = 1;
- vim_snprintf((char *)wfname, MAXPATHL, SPL_FNAME_TMPL,
- fnames[0], spin.si_ascii ? (char_u *)"ascii" : spell_enc());
- } else if (len > 4 && STRCMP(fnames[0] + len - 4, ".spl") == 0) {
+ vim_snprintf(wfname, MAXPATHL, SPL_FNAME_TMPL,
+ fnames[0], spin.si_ascii ? "ascii" : spell_enc());
+ } else if (len > 4 && strcmp(fnames[0] + len - 4, ".spl") == 0) {
// Name ends in ".spl", use as the file name.
- STRLCPY(wfname, fnames[0], MAXPATHL);
+ xstrlcpy(wfname, fnames[0], MAXPATHL);
} else {
// Name should be language, make the file name from it.
- vim_snprintf((char *)wfname, MAXPATHL, SPL_FNAME_TMPL,
- fnames[0], spin.si_ascii ? (char_u *)"ascii" : spell_enc());
+ vim_snprintf(wfname, MAXPATHL, SPL_FNAME_TMPL,
+ fnames[0], spin.si_ascii ? "ascii" : spell_enc());
}
// Check for .ascii.spl.
- if (strstr(path_tail((char *)wfname), SPL_FNAME_ASCII) != NULL) {
+ if (strstr(path_tail(wfname), SPL_FNAME_ASCII) != NULL) {
spin.si_ascii = true;
}
// Check for .add.spl.
- if (strstr(path_tail((char *)wfname), SPL_FNAME_ADD) != NULL) {
+ if (strstr(path_tail(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((char *)wfname), '_') != NULL) {
+ } else if (vim_strchr(path_tail(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);
@@ -5355,12 +5361,12 @@ static void mkspell(int fcount, char **fnames, bool ascii, bool over_write, bool
// Init the aff and dic pointers.
// Get the region names if there are more than 2 arguments.
- for (i = 0; i < incount; ++i) {
+ for (i = 0; i < incount; i++) {
afile[i] = NULL;
if (incount > 1) {
- len = (int)STRLEN(innames[i]);
- if (STRLEN(path_tail((char *)innames[i])) < 5
+ len = (int)strlen(innames[i]);
+ if (strlen(path_tail(innames[i])) < 5
|| innames[i][len - 3] != '_') {
semsg(_("E755: Invalid region in %s"), innames[i]);
goto theend;
@@ -5387,11 +5393,11 @@ static void mkspell(int fcount, char **fnames, bool ascii, bool over_write, bool
// Read all the .aff and .dic files.
// Text is converted to 'encoding'.
// Words are stored in the case-folded and keep-case trees.
- for (i = 0; i < incount && !error; ++i) {
+ for (i = 0; i < incount && !error; i++) {
spin.si_conv.vc_type = CONV_NONE;
spin.si_region = 1 << i;
- vim_snprintf((char *)fname, MAXPATHL, "%s.aff", innames[i]);
+ vim_snprintf(fname, MAXPATHL, "%s.aff", innames[i]);
if (os_path_exists(fname)) {
// Read the .aff file. Will init "spin->si_conv" based on the
// "SET" line.
@@ -5400,8 +5406,7 @@ static void mkspell(int fcount, char **fnames, bool ascii, bool over_write, bool
error = true;
} else {
// Read the .dic file and store the words in the trees.
- vim_snprintf((char *)fname, MAXPATHL, "%s.dic",
- innames[i]);
+ vim_snprintf(fname, MAXPATHL, "%s.dic", innames[i]);
if (spell_read_dic(&spin, fname, afile[i]) == FAIL) {
error = true;
}
@@ -5409,7 +5414,7 @@ static void mkspell(int fcount, char **fnames, bool ascii, bool over_write, bool
} else {
// No .aff file, try reading the file as a word list. Store
// the words in the trees.
- if (spell_read_wordfile(&spin, (char_u *)innames[i]) == FAIL) {
+ if (spell_read_wordfile(&spin, innames[i]) == FAIL) {
error = true;
}
}
@@ -5424,7 +5429,7 @@ static void mkspell(int fcount, char **fnames, bool ascii, bool over_write, bool
if (!error && !got_int) {
// Combine tails in the tree.
- spell_message(&spin, (char_u *)_(msg_compressing));
+ spell_message(&spin, _(msg_compressing));
wordtree_compress(&spin, spin.si_foldroot, "case-folded");
wordtree_compress(&spin, spin.si_keeproot, "keep-case");
wordtree_compress(&spin, spin.si_prefroot, "prefixes");
@@ -5432,14 +5437,14 @@ static void mkspell(int fcount, char **fnames, bool ascii, bool over_write, bool
if (!error && !got_int) {
// Write the info in the spell file.
- vim_snprintf((char *)IObuff, IOSIZE,
+ vim_snprintf(IObuff, IOSIZE,
_("Writing spell file %s..."), wfname);
spell_message(&spin, IObuff);
error = write_vim_spell(&spin, wfname) == FAIL;
- spell_message(&spin, (char_u *)_("Done!"));
- vim_snprintf((char *)IObuff, IOSIZE,
+ spell_message(&spin, _("Done!"));
+ vim_snprintf(IObuff, IOSIZE,
_("Estimated runtime memory use: %d bytes"), spin.si_memtot);
spell_message(&spin, IObuff);
@@ -5459,7 +5464,7 @@ static void mkspell(int fcount, char **fnames, bool ascii, bool over_write, bool
hash_clear_all(&spin.si_commonwords, 0);
// Free the .aff file structures.
- for (i = 0; i < incount; ++i) {
+ for (i = 0; i < incount; i++) {
if (afile[i] != NULL) {
spell_free_aff(afile[i]);
}
@@ -5482,14 +5487,14 @@ theend:
// Display a message for spell file processing when 'verbose' is set or using
// ":mkspell". "str" can be IObuff.
-static void spell_message(const spellinfo_T *spin, char_u *str)
+static void spell_message(const spellinfo_T *spin, char *str)
FUNC_ATTR_NONNULL_ALL
{
if (spin->si_verbose || p_verbose > 2) {
if (!spin->si_verbose) {
verbose_enter();
}
- msg((char *)str);
+ msg(str);
ui_flush();
if (!spin->si_verbose) {
verbose_leave();
@@ -5503,7 +5508,7 @@ static void spell_message(const spellinfo_T *spin, char_u *str)
// ":[count]spellrare {word}"
void ex_spell(exarg_T *eap)
{
- spell_add_word((char_u *)eap->arg, (int)STRLEN(eap->arg),
+ spell_add_word(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,
@@ -5515,17 +5520,17 @@ void ex_spell(exarg_T *eap)
/// @param what SPELL_ADD_ values
/// @param idx "zG" and "zW": zero, otherwise index in 'spellfile'
/// @param bool // true for "zug", "zuG", "zuw" and "zuW"
-void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo)
+void spell_add_word(char *word, int len, SpellAddType what, int idx, bool undo)
{
FILE *fd = NULL;
buf_T *buf = NULL;
bool new_spf = false;
char *fname;
- char_u *fnamebuf = NULL;
- char_u line[MAXWLEN * 2];
+ char *fnamebuf = NULL;
+ char line[MAXWLEN * 2];
long fpos, fpos_next = 0;
int i;
- char_u *spf;
+ char *spf;
if (!valid_spell_word(word, word + len)) {
emsg(_(e_illegal_character_in_word));
@@ -5539,7 +5544,7 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo
return;
}
}
- fname = (char *)int_wordlist;
+ fname = int_wordlist;
} else {
// If 'spellfile' isn't set figure out a good default value.
if (*curwin->w_s->b_p_spf == NUL) {
@@ -5554,7 +5559,7 @@ 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((char **)&spf, (char *)fnamebuf, MAXPATHL, ",");
+ copy_option_part(&spf, fnamebuf, MAXPATHL, ",");
if (i == idx) {
break;
}
@@ -5566,7 +5571,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((char *)fnamebuf);
+ buf = buflist_findname_exp(fnamebuf);
if (buf != NULL && buf->b_ml.ml_mfp == NULL) {
buf = NULL;
}
@@ -5576,7 +5581,7 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo
return;
}
- fname = (char *)fnamebuf;
+ fname = fnamebuf;
}
if (what == SPELL_ADD_BAD || undo) {
@@ -5584,14 +5589,14 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo
// since its flags sort before the one with WF_BANNED.
fd = os_fopen(fname, "r");
if (fd != NULL) {
- while (!vim_fgets(line, MAXWLEN * 2, fd)) {
+ while (!vim_fgets((char *)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] < ' ')) {
+ if (strncmp(word, line, (size_t)len) == 0
+ && (line[len] == '/' || (uint8_t)line[len] < ' ')) {
// Found duplicate word. Remove it by writing a '#' at
// the start of the line. Mixing reading and writing
// doesn't work for all systems, close the file first.
@@ -5603,7 +5608,7 @@ 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, (char *)NameBuff, MAXPATHL, true);
+ home_replace(NULL, fname, NameBuff, MAXPATHL, true);
smsg(_("Word '%.*s' removed from %s"), len, word, NameBuff);
}
}
@@ -5628,7 +5633,7 @@ 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((char_u *)fname)
+ if (!dir_of_file_exists(fname)
&& (p = (char_u *)path_tail_with_sep(fname)) != (char_u *)fname) {
int c = *p;
@@ -5653,7 +5658,7 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo
}
fclose(fd);
- home_replace(NULL, fname, (char *)NameBuff, MAXPATHL, true);
+ home_replace(NULL, fname, NameBuff, MAXPATHL, true);
smsg(_("Word '%.*s' added to %s"), len, word, NameBuff);
}
}
@@ -5667,7 +5672,7 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo
buf_reload(buf, buf->b_orig_mode, false);
}
- redraw_all_later(SOME_VALID);
+ redraw_all_later(UPD_SOME_VALID);
}
xfree(fnamebuf);
}
@@ -5675,98 +5680,98 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo
// Initialize 'spellfile' for the current buffer.
static void init_spellfile(void)
{
- char_u *buf;
+ char *buf;
int l;
- char_u *fname;
- char_u *rtp;
- char_u *lend;
+ char *fname;
+ char *rtp;
+ char *lend;
bool aspath = false;
- char_u *lstart = curbuf->b_s.b_p_spl;
-
- if (*curwin->w_s->b_p_spl != NUL && !GA_EMPTY(&curwin->w_s->b_langp)) {
- buf = xmalloc(MAXPATHL);
-
- // 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(",._", *lend) == NULL; lend++) {
- if (vim_ispathsep(*lend)) {
- aspath = true;
- lstart = lend + 1;
- }
+ char *lstart = curbuf->b_s.b_p_spl;
+
+ if (*curwin->w_s->b_p_spl == NUL || GA_EMPTY(&curwin->w_s->b_langp)) {
+ return;
+ }
+
+ buf = xmalloc(MAXPATHL);
+
+ // 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(",._", (uint8_t)(*lend)) == NULL; lend++) {
+ if (vim_ispathsep(*lend)) {
+ aspath = true;
+ lstart = lend + 1;
}
+ }
- // Loop over all entries in 'runtimepath'. Use the first one where we
- // are allowed to write.
- rtp = p_rtp;
- while (*rtp != NUL) {
+ // Loop over all entries in 'runtimepath'. Use the first one where we
+ // are allowed to write.
+ rtp = p_rtp;
+ while (*rtp != NUL) {
+ if (aspath) {
+ // Use directory of an entry with path, e.g., for
+ // "/dir/lg.utf-8.spl" use "/dir".
+ xstrlcpy(buf, curbuf->b_s.b_p_spl, (size_t)(lstart - curbuf->b_s.b_p_spl));
+ } else {
+ // Copy the path from 'runtimepath' to buf[].
+ copy_option_part(&rtp, buf, MAXPATHL, ",");
+ }
+ if (os_file_is_writable(buf) == 2) {
+ // Use the first language name from 'spelllang' and the
+ // encoding used in the first loaded .spl file.
if (aspath) {
- // Use directory of an entry with path, e.g., for
- // "/dir/lg.utf-8.spl" use "/dir".
- STRLCPY(buf, curbuf->b_s.b_p_spl,
- lstart - curbuf->b_s.b_p_spl);
+ xstrlcpy(buf, curbuf->b_s.b_p_spl, (size_t)(lend - curbuf->b_s.b_p_spl + 1));
} else {
- // Copy the path from 'runtimepath' to buf[].
- 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
- // encoding used in the first loaded .spl file.
- if (aspath) {
- STRLCPY(buf, curbuf->b_s.b_p_spl,
- lend - curbuf->b_s.b_p_spl + 1);
- } else {
- // Create the "spell" directory if it doesn't exist yet.
- l = (int)STRLEN(buf);
- vim_snprintf((char *)buf + l, MAXPATHL - (size_t)l, "/spell");
- if (os_file_is_writable((char *)buf) != 2) {
- os_mkdir((char *)buf, 0755);
- }
-
- l = (int)STRLEN(buf);
- vim_snprintf((char *)buf + l, MAXPATHL - (size_t)l,
- "/%.*s", (int)(lend - lstart), lstart);
+ // Create the "spell" directory if it doesn't exist yet.
+ l = (int)strlen(buf);
+ vim_snprintf(buf + l, MAXPATHL - (size_t)l, "/spell");
+ if (os_file_is_writable(buf) != 2) {
+ os_mkdir(buf, 0755);
}
- l = (int)STRLEN(buf);
- fname = LANGP_ENTRY(curwin->w_s->b_langp, 0)
- ->lp_slang->sl_fname;
- vim_snprintf((char *)buf + l, MAXPATHL - (size_t)l, ".%s.add",
- ((fname != NULL
- && strstr(path_tail((char *)fname), ".ascii.") != NULL)
- ? "ascii"
- : (const char *)spell_enc()));
- set_option_value("spellfile", 0L, (const char *)buf, OPT_LOCAL);
- break;
+
+ l = (int)strlen(buf);
+ vim_snprintf(buf + l, MAXPATHL - (size_t)l,
+ "/%.*s", (int)(lend - lstart), lstart);
}
- aspath = false;
+ l = (int)strlen(buf);
+ fname = LANGP_ENTRY(curwin->w_s->b_langp, 0)
+ ->lp_slang->sl_fname;
+ vim_snprintf(buf + l, MAXPATHL - (size_t)l, ".%s.add",
+ ((fname != NULL
+ && strstr(path_tail(fname), ".ascii.") != NULL)
+ ? "ascii"
+ : (const char *)spell_enc()));
+ set_option_value_give_err("spellfile", 0L, buf, OPT_LOCAL);
+ break;
}
-
- xfree(buf);
+ aspath = false;
}
+
+ xfree(buf);
}
/// Set the spell character tables from strings in the .spl file.
///
/// @param cnt length of "flags"
-static void set_spell_charflags(char_u *flags, int cnt, char_u *fol)
+static void set_spell_charflags(const char_u *flags, int cnt, char *fol)
{
// We build the new tables here first, so that we can compare with the
// previous one.
spelltab_T new_st;
int i;
- char_u *p = fol;
+ char *p = fol;
int c;
clear_spell_chartab(&new_st);
- for (i = 0; i < 128; ++i) {
+ for (i = 0; i < 128; i++) {
if (i < cnt) {
new_st.st_isw[i + 128] = (flags[i] & CF_WORD) != 0;
new_st.st_isu[i + 128] = (flags[i] & CF_UPPER) != 0;
}
if (*p != NUL) {
- c = mb_ptr2char_adv((const char_u **)&p);
+ c = mb_ptr2char_adv((const char **)&p);
new_st.st_fold[i + 128] = (char_u)c;
if (i + 128 != c && new_st.st_isu[i + 128] && c < 256) {
new_st.st_upper[c] = (char_u)(i + 128);
@@ -5783,7 +5788,7 @@ static int set_spell_finish(spelltab_T *new_st)
if (did_set_spelltab) {
// check that it's the same table
- for (i = 0; i < 256; ++i) {
+ for (i = 0; i < 256; i++) {
if (spelltab.st_isw[i] != new_st->st_isw[i]
|| spelltab.st_isu[i] != new_st->st_isu[i]
|| spelltab.st_fold[i] != new_st->st_fold[i]
@@ -5815,7 +5820,7 @@ static int write_spell_prefcond(FILE *fd, garray_T *gap, size_t *fwv)
// <prefcond> : <condlen> <condstr>
char *p = ((char **)gap->ga_data)[i];
if (p != NULL) {
- size_t len = STRLEN(p);
+ size_t len = strlen(p);
if (fd != NULL) {
assert(len <= INT_MAX);
fputc((int)len, fd);
@@ -5832,9 +5837,9 @@ static int write_spell_prefcond(FILE *fd, garray_T *gap, size_t *fwv)
}
// Use map string "map" for languages "lp".
-static void set_map_str(slang_T *lp, char_u *map)
+static void set_map_str(slang_T *lp, char *map)
{
- char_u *p;
+ char *p;
int headc = 0;
int c;
int i;
@@ -5846,7 +5851,7 @@ static void set_map_str(slang_T *lp, char_u *map)
lp->sl_has_map = true;
// Init the array and hash tables empty.
- for (i = 0; i < 256; ++i) {
+ for (i = 0; i < 256; i++) {
lp->sl_map_array[i] = 0;
}
hash_init(&lp->sl_map_hash);
@@ -5855,7 +5860,7 @@ static void set_map_str(slang_T *lp, char_u *map)
// "aaa/bbb/ccc/". Fill sl_map_array[c] with the character before c and
// before the same slash. For characters above 255 sl_map_hash is used.
for (p = map; *p != NUL;) {
- c = mb_cptr2char_adv((const char_u **)&p);
+ c = mb_cptr2char_adv((const char **)&p);
if (c == '/') {
headc = 0;
} else {
@@ -5877,10 +5882,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((char_u *)b);
- hi = hash_lookup(&lp->sl_map_hash, (const char *)b, STRLEN(b), hash);
+ hash = hash_hash(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, (char_u *)b, hash);
+ hash_add_item(&lp->sl_map_hash, hi, b, hash);
} else {
// This should have been checked when generating the .spl
// file.
diff --git a/src/nvim/spellsuggest.c b/src/nvim/spellsuggest.c
index b4a9bed437..22add418a0 100644
--- a/src/nvim/spellsuggest.c
+++ b/src/nvim/spellsuggest.c
@@ -3,30 +3,48 @@
// spellsuggest.c: functions for spelling suggestions
+#include <assert.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
#include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
#include "nvim/change.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/fileio.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
#include "nvim/hashtab.h"
+#include "nvim/highlight_defs.h"
#include "nvim/input.h"
+#include "nvim/macros.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
+#include "nvim/normal.h"
#include "nvim/option.h"
#include "nvim/os/fs.h"
#include "nvim/os/input.h"
+#include "nvim/os/os_defs.h"
+#include "nvim/pos.h"
#include "nvim/profile.h"
#include "nvim/screen.h"
#include "nvim/spell.h"
-#include "nvim/spell_defs.h"
#include "nvim/spellfile.h"
#include "nvim/spellsuggest.h"
#include "nvim/strings.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/vim.h"
@@ -53,11 +71,11 @@ typedef struct suginfo_S {
int su_maxscore; ///< maximum score for adding to su_ga
int su_sfmaxscore; ///< idem, for when doing soundfold words
garray_T su_sga; ///< like su_ga, sound-folded scoring
- char_u *su_badptr; ///< start of bad word in line
+ char *su_badptr; ///< start of bad word in line
int su_badlen; ///< length of detected bad word in line
int su_badflags; ///< caps flags for bad word
char_u su_badword[MAXWLEN]; ///< bad word truncated at su_badlen
- char_u su_fbadword[MAXWLEN]; ///< su_badword case-folded
+ char su_fbadword[MAXWLEN]; ///< su_badword case-folded
char_u su_sal_badword[MAXWLEN]; ///< su_badword soundfolded
hashtab_T su_banned; ///< table with banned words
slang_T *su_sallang; ///< default language for sound folding
@@ -65,8 +83,8 @@ typedef struct suginfo_S {
/// One word suggestion. Used in "si_ga".
typedef struct {
- char_u *st_word; ///< suggested word, allocated string
- int st_wordlen; ///< STRLEN(st_word)
+ char *st_word; ///< suggested word, allocated string
+ int st_wordlen; ///< strlen(st_word)
int st_orglen; ///< length of replaced text
int st_score; ///< lower is better
int st_altscore; ///< used when st_score compares equal
@@ -91,45 +109,55 @@ typedef struct {
#define SUG_MAX_COUNT(su) (SUG_CLEAN_COUNT(su) + 50)
// score for various changes
-#define SCORE_SPLIT 149 // split bad word
-#define SCORE_SPLIT_NO 249 // split bad word with NOSPLITSUGS
-#define SCORE_ICASE 52 // slightly different case
-#define SCORE_REGION 200 // word is for different region
-#define SCORE_RARE 180 // rare word
-#define SCORE_SWAP 75 // swap two characters
-#define SCORE_SWAP3 110 // swap two characters in three
-#define SCORE_REP 65 // REP replacement
-#define SCORE_SUBST 93 // substitute a character
-#define SCORE_SIMILAR 33 // substitute a similar character
-#define SCORE_SUBCOMP 33 // substitute a composing character
-#define SCORE_DEL 94 // delete a character
-#define SCORE_DELDUP 66 // delete a duplicated character
-#define SCORE_DELCOMP 28 // delete a composing character
-#define SCORE_INS 96 // insert a character
-#define SCORE_INSDUP 67 // insert a duplicate character
-#define SCORE_INSCOMP 30 // insert a composing character
-#define SCORE_NONWORD 103 // change non-word to word char
-
-#define SCORE_FILE 30 // suggestion from a file
-#define SCORE_MAXINIT 350 // Initial maximum score: higher == slower.
- // 350 allows for about three changes.
-
-#define SCORE_COMMON1 30 // subtracted for words seen before
-#define SCORE_COMMON2 40 // subtracted for words often seen
-#define SCORE_COMMON3 50 // subtracted for words very often seen
-#define SCORE_THRES2 10 // word count threshold for COMMON2
-#define SCORE_THRES3 100 // word count threshold for COMMON3
+enum {
+ SCORE_SPLIT = 149, // split bad word
+ SCORE_SPLIT_NO = 249, // split bad word with NOSPLITSUGS
+ SCORE_ICASE = 52, // slightly different case
+ SCORE_REGION = 200, // word is for different region
+ SCORE_RARE = 180, // rare word
+ SCORE_SWAP = 75, // swap two characters
+ SCORE_SWAP3 = 110, // swap two characters in three
+ SCORE_REP = 65, // REP replacement
+ SCORE_SUBST = 93, // substitute a character
+ SCORE_SIMILAR = 33, // substitute a similar character
+ SCORE_SUBCOMP = 33, // substitute a composing character
+ SCORE_DEL = 94, // delete a character
+ SCORE_DELDUP = 66, // delete a duplicated character
+ SCORE_DELCOMP = 28, // delete a composing character
+ SCORE_INS = 96, // insert a character
+ SCORE_INSDUP = 67, // insert a duplicate character
+ SCORE_INSCOMP = 30, // insert a composing character
+ SCORE_NONWORD = 103, // change non-word to word char
+};
+
+enum {
+ SCORE_FILE = 30, // suggestion from a file
+ SCORE_MAXINIT = 350, // Initial maximum score: higher == slower.
+ // 350 allows for about three changes.
+};
+
+enum {
+ SCORE_COMMON1 = 30, // subtracted for words seen before
+ SCORE_COMMON2 = 40, // subtracted for words often seen
+ SCORE_COMMON3 = 50, // subtracted for words very often seen
+ SCORE_THRES2 = 10, // word count threshold for COMMON2
+ SCORE_THRES3 = 100, // word count threshold for COMMON3
+};
// When trying changed soundfold words it becomes slow when trying more than
// two changes. With less than two changes it's slightly faster but we miss a
// few good suggestions. In rare cases we need to try three of four changes.
-#define SCORE_SFMAX1 200 // maximum score for first try
-#define SCORE_SFMAX2 300 // maximum score for second try
-#define SCORE_SFMAX3 400 // maximum score for third try
+enum {
+ SCORE_SFMAX1 = 200, // maximum score for first try
+ SCORE_SFMAX2 = 300, // maximum score for second try
+ SCORE_SFMAX3 = 400, // maximum score for third try
+};
#define SCORE_BIG (SCORE_INS * 3) // big difference
-#define SCORE_MAXMAX 999999 // accept any score
-#define SCORE_LIMITMAX 350 // for spell_edit_score_limit()
+enum {
+ SCORE_MAXMAX = 999999, // accept any score
+ SCORE_LIMITMAX = 350, // for spell_edit_score_limit()
+};
// for spell_edit_score_limit() we need to know the minimum value of
// SCORE_ICASE, SCORE_SWAP, SCORE_DEL, SCORE_SIMILAR and SCORE_INS
@@ -186,19 +214,25 @@ typedef struct trystate_S {
} trystate_T;
// values for ts_isdiff
-#define DIFF_NONE 0 // no different byte (yet)
-#define DIFF_YES 1 // different byte found
-#define DIFF_INSERT 2 // inserting character
+enum {
+ DIFF_NONE = 0, // no different byte (yet)
+ DIFF_YES = 1, // different byte found
+ DIFF_INSERT = 2, // inserting character
+};
// values for ts_flags
-#define TSF_PREFIXOK 1 // already checked that prefix is OK
-#define TSF_DIDSPLIT 2 // tried split at this point
-#define TSF_DIDDEL 4 // did a delete, "ts_delidx" has index
+enum {
+ TSF_PREFIXOK = 1, // already checked that prefix is OK
+ TSF_DIDSPLIT = 2, // tried split at this point
+ TSF_DIDDEL = 4, // did a delete, "ts_delidx" has index
+};
// special values ts_prefixdepth
-#define PFD_NOPREFIX 0xff // not using prefixes
-#define PFD_PREFIXTREE 0xfe // walking through the prefix tree
-#define PFD_NOTSPECIAL 0xfd // highest value that's not special
+enum {
+ PFD_NOPREFIX = 0xff, // not using prefixes
+ PFD_PREFIXTREE = 0xfe, // walking through the prefix tree
+ PFD_NOTSPECIAL = 0xfd, // highest value that's not special
+};
static long spell_suggest_timeout = 5000;
@@ -242,26 +276,27 @@ static int score_wordcount_adj(slang_T *slang, int score, char_u *word, bool spl
int newscore;
hashitem_T *hi = hash_find(&slang->sl_wordcount, (char *)word);
- if (!HASHITEM_EMPTY(hi)) {
- wc = HI2WC(hi);
- if (wc->wc_count < SCORE_THRES2) {
- bonus = SCORE_COMMON1;
- } else if (wc->wc_count < SCORE_THRES3) {
- bonus = SCORE_COMMON2;
- } else {
- bonus = SCORE_COMMON3;
- }
- if (split) {
- newscore = score - bonus / 2;
- } else {
- newscore = score - bonus;
- }
- if (newscore < 0) {
- return 0;
- }
- return newscore;
+ if (HASHITEM_EMPTY(hi)) {
+ return score;
}
- return score;
+
+ wc = HI2WC(hi);
+ if (wc->wc_count < SCORE_THRES2) {
+ bonus = SCORE_COMMON1;
+ } else if (wc->wc_count < SCORE_THRES3) {
+ bonus = SCORE_COMMON2;
+ } else {
+ bonus = SCORE_COMMON3;
+ }
+ if (split) {
+ newscore = score - bonus / 2;
+ } else {
+ newscore = score - bonus;
+ }
+ if (newscore < 0) {
+ return 0;
+ }
+ return newscore;
}
/// Like captype() but for a KEEPCAP word add ONECAP if the word starts with a
@@ -270,42 +305,45 @@ static int score_wordcount_adj(slang_T *slang, int score, char_u *word, bool spl
static int badword_captype(char_u *word, char_u *end)
FUNC_ATTR_NONNULL_ALL
{
- int flags = captype(word, end);
+ int flags = captype((char *)word, (char *)end);
int c;
int l, u;
bool first;
char_u *p;
- if (flags & WF_KEEPCAP) {
- // Count the number of UPPER and lower case letters.
- l = u = 0;
- first = false;
- for (p = word; p < end; MB_PTR_ADV(p)) {
- c = utf_ptr2char((char *)p);
- if (SPELL_ISUPPER(c)) {
- u++;
- if (p == word) {
- first = true;
- }
- } else {
- l++;
+ if (!(flags & WF_KEEPCAP)) {
+ return flags;
+ }
+
+ // Count the number of UPPER and lower case letters.
+ l = u = 0;
+ first = false;
+ for (p = word; p < end; MB_PTR_ADV(p)) {
+ c = utf_ptr2char((char *)p);
+ if (SPELL_ISUPPER(c)) {
+ u++;
+ if (p == word) {
+ first = true;
}
+ } else {
+ l++;
}
+ }
- // If there are more UPPER than lower case letters suggest an
- // ALLCAP word. Otherwise, if the first letter is UPPER then
- // suggest ONECAP. Exception: "ALl" most likely should be "All",
- // require three upper case letters.
- if (u > l && u > 2) {
- flags |= WF_ALLCAP;
- } else if (first) {
- flags |= WF_ONECAP;
- }
+ // If there are more UPPER than lower case letters suggest an
+ // ALLCAP word. Otherwise, if the first letter is UPPER then
+ // suggest ONECAP. Exception: "ALl" most likely should be "All",
+ // require three upper case letters.
+ if (u > l && u > 2) {
+ flags |= WF_ALLCAP;
+ } else if (first) {
+ flags |= WF_ONECAP;
+ }
- if (u >= 2 && l >= 2) { // maCARONI maCAroni
- flags |= WF_MIXCAP;
- }
+ if (u >= 2 && l >= 2) { // maCARONI maCAroni
+ flags |= WF_MIXCAP;
}
+
return flags;
}
@@ -341,9 +379,11 @@ static int bytes2offset(char_u **pp)
}
// values for sps_flags
-#define SPS_BEST 1
-#define SPS_FAST 2
-#define SPS_DOUBLE 4
+enum {
+ SPS_BEST = 1,
+ SPS_FAST = 2,
+ SPS_DOUBLE = 4,
+};
static int sps_flags = SPS_BEST; ///< flags from 'spellsuggest'
static int sps_limit = 9999; ///< max nr of suggestions given
@@ -354,13 +394,13 @@ int spell_check_sps(void)
{
char *p;
char *s;
- char_u buf[MAXPATHL];
+ char buf[MAXPATHL];
int f;
sps_flags = 0;
sps_limit = 9999;
- for (p = (char *)p_sps; *p != NUL;) {
+ for (p = p_sps; *p != NUL;) {
copy_option_part(&p, (char *)buf, MAXPATHL, ",");
f = 0;
@@ -370,15 +410,15 @@ int spell_check_sps(void)
if (*s != NUL && !ascii_isdigit(*s)) {
f = -1;
}
- } else if (STRCMP(buf, "best") == 0) {
+ } else if (strcmp(buf, "best") == 0) {
f = SPS_BEST;
- } else if (STRCMP(buf, "fast") == 0) {
+ } else if (strcmp(buf, "fast") == 0) {
f = SPS_FAST;
- } else if (STRCMP(buf, "double") == 0) {
+ } else if (strcmp(buf, "double") == 0) {
f = SPS_DOUBLE;
- } else if (STRNCMP(buf, "expr:", 5) != 0
- && STRNCMP(buf, "file:", 5) != 0
- && (STRNCMP(buf, "timeout:", 8) != 0
+ } else if (strncmp(buf, "expr:", 5) != 0
+ && strncmp(buf, "file:", 5) != 0
+ && (strncmp(buf, "timeout:", 8) != 0
|| (!ascii_isdigit(buf[8])
&& !(buf[8] == '-' && ascii_isdigit(buf[9]))))) {
f = -1;
@@ -407,9 +447,9 @@ int spell_check_sps(void)
/// When "count" is non-zero use that suggestion.
void spell_suggest(int count)
{
- char_u *line;
+ char *line;
pos_T prev_cursor = curwin->w_cursor;
- char_u wcopy[MAXWLEN + 2];
+ char wcopy[MAXWLEN + 2];
char_u *p;
int c;
suginfo_T sug;
@@ -447,6 +487,11 @@ void spell_suggest(int count)
}
badlen++;
end_visual_mode();
+ // make sure we don't include the NUL at the end of the line
+ line = get_cursor_line_ptr();
+ if (badlen > (int)strlen(line) - (int)curwin->w_cursor.col) {
+ badlen = (int)strlen(line) - (int)curwin->w_cursor.col;
+ }
// Find the start of the badly spelled word.
} else if (spell_move_to(curwin, FORWARD, true, true, NULL) == 0
|| curwin->w_cursor.col > prev_cursor.col) {
@@ -454,21 +499,21 @@ void spell_suggest(int count)
// cursor.
curwin->w_cursor = prev_cursor;
line = get_cursor_line_ptr();
- p = line + curwin->w_cursor.col;
+ p = (char_u *)line + curwin->w_cursor.col;
// Backup to before start of word.
- while (p > line && spell_iswordp_nmw(p, curwin)) {
+ while (p > (char_u *)line && spell_iswordp_nmw((char *)p, curwin)) {
MB_PTR_BACK(line, p);
}
// Forward to start of word.
- while (*p != NUL && !spell_iswordp_nmw(p, curwin)) {
+ while (*p != NUL && !spell_iswordp_nmw((char *)p, curwin)) {
MB_PTR_ADV(p);
}
- if (!spell_iswordp_nmw(p, curwin)) { // No word found.
+ if (!spell_iswordp_nmw((char *)p, curwin)) { // No word found.
beep_flush();
return;
}
- curwin->w_cursor.col = (colnr_T)(p - line);
+ curwin->w_cursor.col = (colnr_T)(p - (char_u *)line);
}
// Get the word and its length.
@@ -477,7 +522,7 @@ void spell_suggest(int count)
need_cap = check_need_cap(curwin->w_cursor.lnum, curwin->w_cursor.col);
// Make a copy of current line since autocommands may free the line.
- line = vim_strsave(get_cursor_line_ptr());
+ line = xstrdup(get_cursor_line_ptr());
spell_suggest_timeout = 5000;
// Get the list of suggestions. Limit to 'lines' - 2 or the number in
@@ -487,7 +532,7 @@ void spell_suggest(int count)
} else {
limit = sps_limit;
}
- spell_find_suggest(line + curwin->w_cursor.col, badlen, &sug, limit,
+ spell_find_suggest((char_u *)line + curwin->w_cursor.col, badlen, &sug, limit,
true, need_cap, true);
if (GA_EMPTY(&sug.su_ga)) {
@@ -508,12 +553,12 @@ void spell_suggest(int count)
msg_start();
msg_row = Rows - 1; // for when 'cmdheight' > 1
lines_left = Rows; // avoid more prompt
- vim_snprintf((char *)IObuff, IOSIZE, _("Change \"%.*s\" to:"),
+ vim_snprintf(IObuff, IOSIZE, _("Change \"%.*s\" to:"),
sug.su_badlen, sug.su_badptr);
- if (cmdmsg_rl && STRNCMP(IObuff, "Change", 6) == 0) {
+ if (cmdmsg_rl && strncmp(IObuff, "Change", 6) == 0) {
// And now the rabbit from the high hat: Avoid showing the
// untranslated message rightleft.
- vim_snprintf((char *)IObuff, IOSIZE, ":ot \"%.*s\" egnahC",
+ vim_snprintf(IObuff, IOSIZE, ":ot \"%.*s\" egnahC",
sug.su_badlen, sug.su_badptr);
}
msg_puts((const char *)IObuff);
@@ -526,23 +571,23 @@ void spell_suggest(int count)
// The suggested word may replace only part of the bad word, add
// the not replaced part. But only when it's not getting too long.
- STRLCPY(wcopy, stp->st_word, MAXWLEN + 1);
+ xstrlcpy(wcopy, stp->st_word, MAXWLEN + 1);
int el = sug.su_badlen - stp->st_orglen;
if (el > 0 && stp->st_wordlen + el <= MAXWLEN) {
- STRLCPY(wcopy + stp->st_wordlen, sug.su_badptr + stp->st_orglen, el + 1);
+ xstrlcpy(wcopy + stp->st_wordlen, sug.su_badptr + stp->st_orglen, (size_t)el + 1);
}
- vim_snprintf((char *)IObuff, IOSIZE, "%2d", i + 1);
+ vim_snprintf(IObuff, IOSIZE, "%2d", i + 1);
if (cmdmsg_rl) {
rl_mirror(IObuff);
}
msg_puts((const char *)IObuff);
- vim_snprintf((char *)IObuff, IOSIZE, " \"%s\"", wcopy);
+ vim_snprintf(IObuff, IOSIZE, " \"%s\"", wcopy);
msg_puts((const char *)IObuff);
// The word may replace more than "su_badlen".
if (sug.su_badlen < stp->st_orglen) {
- vim_snprintf((char *)IObuff, IOSIZE, _(" < \"%.*s\""),
+ vim_snprintf(IObuff, IOSIZE, _(" < \"%.*s\""),
stp->st_orglen, sug.su_badptr);
msg_puts((const char *)IObuff);
}
@@ -550,11 +595,11 @@ void spell_suggest(int count)
if (p_verbose > 0) {
// Add the score.
if (sps_flags & (SPS_DOUBLE | SPS_BEST)) {
- vim_snprintf((char *)IObuff, IOSIZE, " (%s%d - %d)",
+ vim_snprintf(IObuff, IOSIZE, " (%s%d - %d)",
stp->st_salscore ? "s " : "",
stp->st_score, stp->st_altscore);
} else {
- vim_snprintf((char *)IObuff, IOSIZE, " (%d)",
+ vim_snprintf(IObuff, IOSIZE, " (%d)",
stp->st_score);
}
if (cmdmsg_rl) {
@@ -593,19 +638,19 @@ void spell_suggest(int count)
if (sug.su_badlen > stp->st_orglen) {
// Replacing less than "su_badlen", append the remainder to
// repl_to.
- repl_from = vim_strnsave(sug.su_badptr, (size_t)sug.su_badlen);
- vim_snprintf((char *)IObuff, IOSIZE, "%s%.*s", stp->st_word,
+ repl_from = xstrnsave(sug.su_badptr, (size_t)sug.su_badlen);
+ vim_snprintf(IObuff, IOSIZE, "%s%.*s", stp->st_word,
sug.su_badlen - stp->st_orglen,
sug.su_badptr + stp->st_orglen);
- repl_to = vim_strsave(IObuff);
+ repl_to = xstrdup(IObuff);
} else {
// Replacing su_badlen or more, use the whole word.
- repl_from = vim_strnsave(sug.su_badptr, (size_t)stp->st_orglen);
- repl_to = vim_strsave(stp->st_word);
+ repl_from = xstrnsave(sug.su_badptr, (size_t)stp->st_orglen);
+ repl_to = xstrdup(stp->st_word);
}
// Replace the word.
- p = xmalloc(STRLEN(line) - (size_t)stp->st_orglen + (size_t)stp->st_wordlen + 1);
+ p = xmalloc(strlen(line) - (size_t)stp->st_orglen + (size_t)stp->st_wordlen + 1);
c = (int)(sug.su_badptr - line);
memmove(p, line, (size_t)c);
STRCPY(p + c, stp->st_word);
@@ -637,13 +682,13 @@ void spell_suggest(int count)
///
/// @param maxcount maximum nr of suggestions
/// @param need_cap 'spellcapcheck' matched
-void spell_suggest_list(garray_T *gap, char_u *word, int maxcount, bool need_cap, bool interactive)
+void spell_suggest_list(garray_T *gap, char *word, int maxcount, bool need_cap, bool interactive)
{
suginfo_T sug;
suggest_T *stp;
char_u *wcopy;
- spell_find_suggest(word, 0, &sug, maxcount, false, need_cap, interactive);
+ spell_find_suggest((char_u *)word, 0, &sug, maxcount, false, need_cap, interactive);
// Make room in "gap".
ga_init(gap, sizeof(char_u *), sug.su_ga.ga_len + 1);
@@ -653,7 +698,7 @@ void spell_suggest_list(garray_T *gap, char_u *word, int maxcount, bool need_cap
// The suggested word may replace only part of "word", add the not
// replaced part.
- wcopy = xmalloc((size_t)stp->st_wordlen + STRLEN(sug.su_badptr + stp->st_orglen) + 1);
+ wcopy = xmalloc((size_t)stp->st_wordlen + strlen(sug.su_badptr + stp->st_orglen) + 1);
STRCPY(wcopy, stp->st_word);
STRCPY(wcopy + stp->st_wordlen, sug.su_badptr + stp->st_orglen);
((char_u **)gap->ga_data)[gap->ga_len++] = wcopy;
@@ -675,10 +720,10 @@ static void spell_find_suggest(char_u *badptr, int badlen, suginfo_T *su, int ma
bool banbadword, bool need_cap, bool interactive)
{
hlf_T attr = HLF_COUNT;
- char_u buf[MAXPATHL];
+ char buf[MAXPATHL];
char *p;
bool do_combine = false;
- char_u *sps_copy;
+ char *sps_copy;
static bool expr_busy = false;
int c;
langp_T *lp;
@@ -693,7 +738,7 @@ static void spell_find_suggest(char_u *badptr, int badlen, suginfo_T *su, int ma
}
hash_init(&su->su_banned);
- su->su_badptr = badptr;
+ su->su_badptr = (char *)badptr;
if (badlen != 0) {
su->su_badlen = badlen;
} else {
@@ -707,7 +752,7 @@ static void spell_find_suggest(char_u *badptr, int badlen, suginfo_T *su, int ma
if (su->su_badlen >= MAXWLEN) {
su->su_badlen = MAXWLEN - 1; // just in case
}
- STRLCPY(su->su_badword, su->su_badptr, su->su_badlen + 1);
+ xstrlcpy((char *)su->su_badword, su->su_badptr, (size_t)su->su_badlen + 1);
(void)spell_casefold(curwin, su->su_badptr, su->su_badlen, su->su_fbadword,
MAXWLEN);
@@ -717,8 +762,8 @@ static void spell_find_suggest(char_u *badptr, int badlen, suginfo_T *su, int ma
su->su_fbadword[su->su_badlen] = NUL;
// get caps flags for bad word
- su->su_badflags = badword_captype(su->su_badptr,
- su->su_badptr + su->su_badlen);
+ su->su_badflags = badword_captype((char_u *)su->su_badptr,
+ (char_u *)su->su_badptr + su->su_badlen);
if (need_cap) {
su->su_badflags |= WF_ONECAP;
}
@@ -738,44 +783,44 @@ static void spell_find_suggest(char_u *badptr, int badlen, suginfo_T *su, int ma
// Soundfold the bad word with the default sound folding, so that we don't
// have to do this many times.
if (su->su_sallang != NULL) {
- spell_soundfold(su->su_sallang, su->su_fbadword, true,
- su->su_sal_badword);
+ spell_soundfold(su->su_sallang, (char *)su->su_fbadword, true,
+ (char *)su->su_sal_badword);
}
// 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((char *)su->su_badptr);
+ c = utf_ptr2char(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,
+ make_case_word((char *)su->su_badword, buf, WF_ONECAP);
+ add_suggestion(su, &su->su_ga, (char *)buf, su->su_badlen, SCORE_ICASE,
0, true, su->su_sallang, false);
}
// Ban the bad word itself. It may appear in another region.
if (banbadword) {
- add_banned(su, su->su_badword);
+ add_banned(su, (char *)su->su_badword);
}
// Make a copy of 'spellsuggest', because the expression may change it.
- sps_copy = vim_strsave(p_sps);
+ sps_copy = xstrdup(p_sps);
// Loop over the items in 'spellsuggest'.
- for (p = (char *)sps_copy; *p != NUL;) {
+ for (p = sps_copy; *p != NUL;) {
copy_option_part(&p, (char *)buf, MAXPATHL, ",");
- if (STRNCMP(buf, "expr:", 5) == 0) {
+ if (strncmp(buf, "expr:", 5) == 0) {
// Evaluate an expression. Skip this when called recursively,
// when using spellsuggest() in the expression.
if (!expr_busy) {
expr_busy = true;
- spell_suggest_expr(su, buf + 5);
+ spell_suggest_expr(su, (char_u *)buf + 5);
expr_busy = false;
}
- } else if (STRNCMP(buf, "file:", 5) == 0) {
+ } else if (strncmp(buf, "file:", 5) == 0) {
// Use list of suggestions in a file.
- spell_suggest_file(su, buf + 5);
- } else if (STRNCMP(buf, "timeout:", 8) == 0) {
+ spell_suggest_file(su, (char_u *)buf + 5);
+ } else if (strncmp(buf, "timeout:", 8) == 0) {
// Limit the time searching for suggestions.
spell_suggest_timeout = atol((char *)buf + 8);
} else if (!did_intern) {
@@ -814,7 +859,7 @@ static void spell_suggest_expr(suginfo_T *su, char_u *expr)
// Get the word and the score from the items.
score = get_spellword(TV_LIST_ITEM_TV(li)->vval.v_list, &p);
if (score >= 0 && score <= su->su_maxscore) {
- add_suggestion(su, &su->su_ga, (const char_u *)p, su->su_badlen,
+ add_suggestion(su, &su->su_ga, p, su->su_badlen,
score, 0, true, su->su_sallang, false);
}
}
@@ -844,7 +889,7 @@ static void spell_suggest_file(suginfo_T *su, char_u *fname)
}
// Read it line by line.
- while (!vim_fgets(line, MAXWLEN * 2, fd) && !got_int) {
+ while (!vim_fgets((char *)line, MAXWLEN * 2, fd) && !got_int) {
line_breakcheck();
p = (char_u *)vim_strchr((char *)line, '/');
@@ -859,12 +904,12 @@ static void spell_suggest_file(suginfo_T *su, char_u *fname)
// If the suggestion doesn't have specific case duplicate the case
// of the bad word.
- if (captype(p, NULL) == 0) {
- make_case_word(p, cword, su->su_badflags);
+ if (captype((char *)p, NULL) == 0) {
+ make_case_word((char *)p, (char *)cword, su->su_badflags);
p = cword;
}
- add_suggestion(su, &su->su_ga, p, su->su_badlen,
+ add_suggestion(su, &su->su_ga, (char *)p, su->su_badlen,
SCORE_FILE, 0, true, su->su_sallang, false);
}
}
@@ -968,24 +1013,24 @@ static void spell_find_cleanup(suginfo_T *su)
/// Try finding suggestions by recognizing specific situations.
static void suggest_try_special(suginfo_T *su)
{
- int c;
+ char c;
char_u word[MAXWLEN];
// Recognize a word that is repeated: "the the".
- char_u *p = skiptowhite(su->su_fbadword);
- size_t len = (size_t)(p - su->su_fbadword);
- p = (char_u *)skipwhite((char *)p);
- if (STRLEN(p) == len && STRNCMP(su->su_fbadword, p, len) == 0) {
+ char *p = skiptowhite((char *)su->su_fbadword);
+ size_t len = (size_t)(p - (char *)su->su_fbadword);
+ p = skipwhite(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".
c = su->su_fbadword[len];
su->su_fbadword[len] = NUL;
- make_case_word(su->su_fbadword, word, su->su_badflags);
- su->su_fbadword[len] = (char_u)c;
+ make_case_word(su->su_fbadword, (char *)word, su->su_badflags);
+ su->su_fbadword[len] = c;
// Give a soundalike score of 0, compute the score as if deleting one
// character.
- add_suggestion(su, &su->su_ga, word, su->su_badlen,
+ add_suggestion(su, &su->su_ga, (char *)word, su->su_badlen,
RESCORE(SCORE_REP, 0), 0, true, su->su_sallang, false);
}
}
@@ -1038,21 +1083,21 @@ static void prof_report(char *name)
/// Try finding suggestions by adding/removing/swapping letters.
static void suggest_try_change(suginfo_T *su)
{
- char_u fword[MAXWLEN]; // copy of the bad word, case-folded
+ char fword[MAXWLEN]; // copy of the bad word, case-folded
int n;
- char_u *p;
+ char *p;
langp_T *lp;
// We make a copy of the case-folded bad word, so that we can modify it
// to find matches (esp. REP items). Append some more text, changing
// chars after the bad word may help.
STRCPY(fword, su->su_fbadword);
- n = (int)STRLEN(fword);
+ n = (int)strlen(fword);
p = su->su_badptr + su->su_badlen;
- (void)spell_casefold(curwin, p, (int)STRLEN(p), fword + n, MAXWLEN - n);
+ (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);
+ n = (int)strlen(su->su_badptr);
if (n < MAXWLEN) {
fword[n] = NUL;
}
@@ -1110,11 +1155,11 @@ static void suggest_try_change(suginfo_T *su)
/// word splitting for now
/// "similar_chars()"
/// use "slang->sl_repsal" instead of "lp->lp_replang->sl_rep"
-static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool soundfold)
+static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char *fword, bool soundfold)
{
- char_u tword[MAXWLEN]; // good word collected so far
+ char tword[MAXWLEN]; // good word collected so far
trystate_T stack[MAXWLEN];
- char_u preword[MAXWLEN * 3] = { 0 }; // word found with proper case;
+ char preword[MAXWLEN * 3] = { 0 }; // word found with proper case;
// concatenation of prefix compound
// words and split word. NUL terminated
// when going deeper but not when coming
@@ -1132,7 +1177,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
garray_T *gap;
idx_T arridx;
int len;
- char_u *p;
+ char *p;
fromto_T *ftp;
int fl = 0, tl;
int repextra = 0; // extra bytes in fword[] from REP item
@@ -1157,7 +1202,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
if (soundfold) {
// Going through the soundfold tree.
- byts = fbyts = slang->sl_sbyts;
+ byts = fbyts = (char_u *)slang->sl_sbyts;
idxs = fidxs = slang->sl_sidxs;
pbyts = NULL;
pidxs = NULL;
@@ -1166,9 +1211,9 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
} else {
// When there are postponed prefixes we need to use these first. At
// the end of the prefix we continue in the case-fold tree.
- fbyts = slang->sl_fbyts;
+ fbyts = (char_u *)slang->sl_fbyts;
fidxs = slang->sl_fidxs;
- pbyts = slang->sl_pbyts;
+ pbyts = (char_u *)slang->sl_pbyts;
pidxs = slang->sl_pidxs;
if (pbyts != NULL) {
byts = pbyts;
@@ -1223,9 +1268,9 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// 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);
- flags = badword_captype(su->su_badptr, su->su_badptr + n);
- su->su_badflags = badword_captype(su->su_badptr + n,
- su->su_badptr + su->su_badlen);
+ flags = badword_captype((char_u *)su->su_badptr, (char_u *)su->su_badptr + n);
+ su->su_badflags = badword_captype((char_u *)su->su_badptr + n,
+ (char_u *)su->su_badptr + su->su_badlen);
#ifdef DEBUG_TRIEWALK
sprintf(changename[depth], "prefix"); // NOLINT(runtime/printf)
#endif
@@ -1242,7 +1287,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
tword[sp->ts_twordlen] = NUL;
make_case_word(tword + sp->ts_splitoff,
preword + sp->ts_prewordlen, flags);
- sp->ts_prewordlen = (char_u)STRLEN(preword);
+ sp->ts_prewordlen = (char_u)strlen(preword);
sp->ts_splitoff = sp->ts_twordlen;
}
break;
@@ -1321,16 +1366,16 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// need to check if a correct word follows.
if (sp->ts_fidx - sp->ts_splitfidx
== sp->ts_twordlen - sp->ts_splitoff
- && STRNCMP(fword + sp->ts_splitfidx,
+ && strncmp(fword + sp->ts_splitfidx,
tword + sp->ts_splitoff,
- sp->ts_fidx - sp->ts_splitfidx) == 0) {
+ (size_t)(sp->ts_fidx - sp->ts_splitfidx)) == 0) {
preword[sp->ts_prewordlen] = NUL;
newscore = score_wordcount_adj(slang, sp->ts_score,
- preword + sp->ts_prewordlen,
+ (char_u *)preword + sp->ts_prewordlen,
sp->ts_prewordlen > 0);
// Add the suggestion if the score isn't too bad.
if (newscore <= su->su_maxscore) {
- add_suggestion(su, &su->su_ga, preword,
+ add_suggestion(su, &su->su_ga, (char *)preword,
sp->ts_splitfidx - repextra,
newscore, 0, false,
lp->lp_sallang, false);
@@ -1357,9 +1402,9 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
compflags[sp->ts_complen] = (char_u)((unsigned)flags >> 24);
compflags[sp->ts_complen + 1] = NUL;
- STRLCPY(preword + sp->ts_prewordlen,
- tword + sp->ts_splitoff,
- sp->ts_twordlen - sp->ts_splitoff + 1);
+ xstrlcpy(preword + sp->ts_prewordlen,
+ tword + sp->ts_splitoff,
+ (size_t)(sp->ts_twordlen - sp->ts_splitoff) + 1);
// Verify CHECKCOMPOUNDPATTERN rules.
if (match_checkcompoundpattern(preword, sp->ts_prewordlen,
@@ -1370,10 +1415,9 @@ 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 = (char_u *)skipwhite((char *)skiptowhite(p));
+ p = skipwhite(skiptowhite(p));
}
- if (fword_ends && !can_compound(slang, p,
- compflags + sp->ts_compsplit)) {
+ if (fword_ends && !can_compound(slang, p, compflags + sp->ts_compsplit)) {
// Compound is not allowed. But it may still be
// possible if we add another (short) word.
compound_ok = false;
@@ -1393,16 +1437,13 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
STRCPY(preword + sp->ts_prewordlen, tword + sp->ts_splitoff);
} else if (flags & WF_KEEPCAP) {
// Must find the word in the keep-case tree.
- find_keepcap_word(slang, tword + sp->ts_splitoff,
- preword + sp->ts_prewordlen);
+ find_keepcap_word(slang, (char *)tword + sp->ts_splitoff, preword + sp->ts_prewordlen);
} else {
// Include badflags: If the badword is onecap or allcap
// use that for the goodword too. But if the badword is
// allcap and it's only one char long use onecap.
c = su->su_badflags;
- if ((c & WF_ALLCAP)
- && su->su_badlen ==
- utfc_ptr2len((char *)su->su_badptr)) {
+ if ((c & WF_ALLCAP) && su->su_badlen == utfc_ptr2len(su->su_badptr)) {
c = WF_ONECAP;
}
c |= flags;
@@ -1457,7 +1498,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
&& compound_ok) {
// The badword also ends: add suggestions.
#ifdef DEBUG_TRIEWALK
- if (soundfold && STRCMP(preword, "smwrd") == 0) {
+ if (soundfold && strcmp(preword, "smwrd") == 0) {
int j;
// print the stack of changes that brought us here
@@ -1477,7 +1518,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
p = fword + sp->ts_fidx;
MB_PTR_BACK(fword, p);
if (!spell_iswordp(p, curwin) && *preword != NUL) {
- p = preword + STRLEN(preword);
+ p = preword + strlen(preword);
MB_PTR_BACK(preword, p);
if (spell_iswordp(p, curwin)) {
newscore += SCORE_NONWORD;
@@ -1487,12 +1528,12 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// Give a bonus to words seen before.
score = score_wordcount_adj(slang,
sp->ts_score + newscore,
- preword + sp->ts_prewordlen,
+ (char_u *)preword + sp->ts_prewordlen,
sp->ts_prewordlen > 0);
// Add the suggestion if the score isn't too bad.
if (score <= su->su_maxscore) {
- add_suggestion(su, &su->su_ga, preword,
+ add_suggestion(su, &su->su_ga, (char *)preword,
sp->ts_fidx - repextra,
score, 0, false, lp->lp_sallang, false);
@@ -1505,7 +1546,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
preword + sp->ts_prewordlen,
c == 0 ? WF_ALLCAP : 0);
- add_suggestion(su, &su->su_ga, preword,
+ add_suggestion(su, &su->su_ga, (char *)preword,
sp->ts_fidx - repextra,
score + SCORE_ICASE, 0, false,
lp->lp_sallang, false);
@@ -1591,11 +1632,10 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
}
p = preword;
while (*skiptowhite(p) != NUL) {
- p = (char_u *)skipwhite((char *)skiptowhite(p));
+ p = skipwhite(skiptowhite(p));
}
if (sp->ts_complen > sp->ts_compsplit
- && !can_compound(slang, p,
- compflags + sp->ts_compsplit)) {
+ && !can_compound(slang, p, compflags + sp->ts_compsplit)) {
break;
}
@@ -1607,7 +1647,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// Give a bonus to words seen before.
newscore = score_wordcount_adj(slang, newscore,
- preword + sp->ts_prewordlen, true);
+ (char_u *)preword + sp->ts_prewordlen, true);
}
if (TRY_DEEPER(su, stack, depth, newscore)) {
@@ -1633,7 +1673,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
if (!try_compound && !fword_ends) {
STRCAT(preword, " ");
}
- sp->ts_prewordlen = (char_u)STRLEN(preword);
+ sp->ts_prewordlen = (char_u)strlen(preword);
sp->ts_splitoff = sp->ts_twordlen;
sp->ts_splitfidx = sp->ts_fidx;
@@ -1650,7 +1690,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
&& goodword_ends) {
int l;
- l = utfc_ptr2len((char *)fword + sp->ts_fidx);
+ l = utfc_ptr2len(fword + sp->ts_fidx);
if (fword_ends) {
// Copy the skipped character to preword.
memmove(preword + sp->ts_prewordlen, fword + sp->ts_fidx, (size_t)l);
@@ -1675,8 +1715,8 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// set su->su_badflags to the caps type at this
// position
n = nofold_len(fword, sp->ts_fidx, su->su_badptr);
- su->su_badflags = badword_captype(su->su_badptr + n,
- su->su_badptr + su->su_badlen);
+ su->su_badflags = badword_captype((char_u *)su->su_badptr + n,
+ (char_u *)su->su_badptr + su->su_badlen);
// Restart at top of the tree.
sp->ts_arridx = 0;
@@ -1743,7 +1783,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// when the byte was already changed. And don't try when we
// just deleted this byte, accepting it is always cheaper than
// delete + substitute.
- if (c == fword[sp->ts_fidx]
+ if (c == (uint8_t)fword[sp->ts_fidx]
|| (sp->ts_tcharlen > 0
&& sp->ts_isdiff != DIFF_NONE)) {
newscore = 0;
@@ -1753,7 +1793,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
if ((newscore == 0
|| (sp->ts_fidx >= sp->ts_fidxtry
&& ((sp->ts_flags & TSF_DIDDEL) == 0
- || c != fword[sp->ts_delidx])))
+ || c != (uint8_t)fword[sp->ts_delidx])))
&& TRY_DEEPER(su, stack, depth, newscore)) {
go_deeper(stack, depth, newscore);
#ifdef DEBUG_TRIEWALK
@@ -1772,7 +1812,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
if (fword[sp->ts_fidx] != NUL) {
sp->ts_fidx++;
}
- tword[sp->ts_twordlen++] = (char_u)c;
+ tword[sp->ts_twordlen++] = (char)c;
sp->ts_arridx = idxs[arridx];
if (newscore == SCORE_SUBST) {
sp->ts_isdiff = DIFF_YES;
@@ -1798,14 +1838,14 @@ 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 = (char_u)(sp->ts_fcharstart
- + utfc_ptr2len((char *)fword + sp->ts_fcharstart));
+ + utfc_ptr2len(fword + sp->ts_fcharstart));
// For changing a composing character adjust
// the score from SCORE_SUBST to
// SCORE_SUBCOMP.
if (utf_iscomposing(utf_ptr2char((char *)tword + sp->ts_twordlen
- sp->ts_tcharlen))
- && utf_iscomposing(utf_ptr2char((char *)fword
+ && utf_iscomposing(utf_ptr2char(fword
+ sp->ts_fcharstart))) {
sp->ts_score -= SCORE_SUBST - SCORE_SUBCOMP;
} else if (!soundfold
@@ -1813,15 +1853,15 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
&& similar_chars(slang,
utf_ptr2char((char *)tword + sp->ts_twordlen -
sp->ts_tcharlen),
- utf_ptr2char((char *)fword + sp->ts_fcharstart))) {
+ utf_ptr2char(fword + sp->ts_fcharstart))) {
// For a similar character adjust score from
// SCORE_SUBST to SCORE_SIMILAR.
sp->ts_score -= SCORE_SUBST - SCORE_SIMILAR;
}
} else if (sp->ts_isdiff == DIFF_INSERT
&& sp->ts_twordlen > sp->ts_tcharlen) {
- p = tword + sp->ts_twordlen - sp->ts_tcharlen;
- c = utf_ptr2char((char *)p);
+ p = (char *)tword + sp->ts_twordlen - sp->ts_tcharlen;
+ c = utf_ptr2char(p);
if (utf_iscomposing(c)) {
// Inserting a composing char doesn't
// count that much.
@@ -1833,7 +1873,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((char *)p)) {
+ if (c == utf_ptr2char(p)) {
sp->ts_score -= SCORE_INS - SCORE_INSDUP;
}
}
@@ -1884,12 +1924,12 @@ 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((char *)fword + sp->ts_fidx);
+ c = utf_ptr2char(fword + sp->ts_fidx);
stack[depth].ts_fidx =
- (char_u)(stack[depth].ts_fidx + utfc_ptr2len((char *)fword + sp->ts_fidx));
+ (char_u)(stack[depth].ts_fidx + utfc_ptr2len(fword + sp->ts_fidx));
if (utf_iscomposing(c)) {
stack[depth].ts_score -= SCORE_DEL - SCORE_DELCOMP;
- } else if (c == utf_ptr2char((char *)fword + stack[depth].ts_fidx)) {
+ } else if (c == utf_ptr2char(fword + stack[depth].ts_fidx)) {
stack[depth].ts_score -= SCORE_DEL - SCORE_DELDUP;
}
@@ -1949,7 +1989,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
} else {
newscore = SCORE_INS;
}
- if (c != fword[sp->ts_fidx]
+ if (c != (uint8_t)fword[sp->ts_fidx]
&& TRY_DEEPER(su, stack, depth, newscore)) {
go_deeper(stack, depth, newscore);
#ifdef DEBUG_TRIEWALK
@@ -1959,7 +1999,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
#endif
depth++;
sp = &stack[depth];
- tword[sp->ts_twordlen++] = (char_u)c;
+ tword[sp->ts_twordlen++] = (char)c;
sp->ts_arridx = idxs[n];
fl = MB_BYTE2LEN(c);
if (fl > 1) {
@@ -1976,7 +2016,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// soundfold words (illogical but does give a better
// score).
if (sp->ts_twordlen >= 2
- && tword[sp->ts_twordlen - 2] == c) {
+ && (uint8_t)tword[sp->ts_twordlen - 2] == c) {
sp->ts_score -= SCORE_INS - SCORE_INSDUP;
}
}
@@ -1988,7 +2028,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// We change "fword" here, it's changed back afterwards at
// STATE_UNSWAP.
p = fword + sp->ts_fidx;
- c = *p;
+ c = (uint8_t)(*p);
if (c == NUL) {
// End of word, can't swap or replace.
PROF_STORE(sp->ts_state)
@@ -2004,14 +2044,14 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
break;
}
- n = utf_ptr2len((char *)p);
- c = utf_ptr2char((char *)p);
+ n = utf_ptr2len(p);
+ c = utf_ptr2char(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((char *)p + n);
+ c2 = utf_ptr2char(p + n);
}
// When the second character is NUL we can't swap.
@@ -2041,7 +2081,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, (size_t)fl);
- utf_char2bytes(c, (char *)p + fl);
+ utf_char2bytes(c, p + fl);
stack[depth].ts_fidxtry = (char_u)(sp->ts_fidx + n + fl);
} else {
// If this swap doesn't work then SWAP3 won't either.
@@ -2053,10 +2093,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((char *)p);
- c = utf_ptr2char((char *)p + n);
- memmove(p + utfc_ptr2len((char *)p + n), p, (size_t)n);
- utf_char2bytes(c, (char *)p);
+ n = utfc_ptr2len(p);
+ c = utf_ptr2char(p + n);
+ memmove(p + utfc_ptr2len(p + n), p, (size_t)n);
+ utf_char2bytes(c, p);
FALLTHROUGH;
@@ -2064,14 +2104,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((char *)p);
- c = utf_ptr2char((char *)p);
- fl = utf_ptr2len((char *)p + n);
- c2 = utf_ptr2char((char *)p + n);
+ n = utf_ptr2len(p);
+ c = utf_ptr2char(p);
+ fl = utf_ptr2len(p + n);
+ c2 = utf_ptr2char(p + n);
if (!soundfold && !spell_iswordp(p + n + fl, curwin)) {
c3 = c; // don't swap non-word char
} else {
- c3 = utf_ptr2char((char *)p + n + fl);
+ c3 = utf_ptr2char(p + n + fl);
}
// When characters are identical: "121" then SWAP3 result is
@@ -2097,8 +2137,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, (size_t)tl);
- utf_char2bytes(c2, (char *)p + tl);
- utf_char2bytes(c, (char *)p + fl + tl);
+ utf_char2bytes(c2, p + tl);
+ utf_char2bytes(c, p + fl + tl);
stack[depth].ts_fidxtry = (char_u)(sp->ts_fidx + n + fl + tl);
} else {
PROF_STORE(sp->ts_state)
@@ -2109,14 +2149,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((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);
+ 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);
memmove(p + fl + tl, p, (size_t)n);
- utf_char2bytes(c, (char *)p);
- utf_char2bytes(c2, (char *)p + tl);
+ utf_char2bytes(c, p);
+ utf_char2bytes(c2, p + tl);
p = p + tl;
if (!soundfold && !spell_iswordp(p, curwin)) {
@@ -2141,12 +2181,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((char *)p);
- c = utf_ptr2char((char *)p);
- fl = utf_ptr2len((char *)p + n);
- fl += utf_ptr2len((char *)p + n + fl);
+ n = utf_ptr2len(p);
+ c = utf_ptr2char(p);
+ fl = utf_ptr2len(p + n);
+ fl += utf_ptr2len(p + n + fl);
memmove(p, p + n, (size_t)fl);
- utf_char2bytes(c, (char *)p + fl);
+ utf_char2bytes(c, p + fl);
stack[depth].ts_fidxtry = (char_u)(sp->ts_fidx + n + fl);
} else {
PROF_STORE(sp->ts_state)
@@ -2157,12 +2197,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((char *)p);
- n += utfc_ptr2len((char *)p + n);
- c = utf_ptr2char((char *)p + n);
- tl = utfc_ptr2len((char *)p + n);
+ n = utfc_ptr2len(p);
+ n += utfc_ptr2len(p + n);
+ c = utf_ptr2char(p + n);
+ tl = utfc_ptr2len(p + n);
memmove(p + tl, p, (size_t)n);
- utf_char2bytes(c, (char *)p);
+ utf_char2bytes(c, p);
// Rotate three bytes right: "123" -> "312". We change "fword"
// here, it's changed back afterwards at STATE_UNROT3R.
@@ -2178,12 +2218,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((char *)p);
- n += utf_ptr2len((char *)p + n);
- c = utf_ptr2char((char *)p + n);
- tl = utf_ptr2len((char *)p + n);
+ n = utf_ptr2len(p);
+ n += utf_ptr2len(p + n);
+ c = utf_ptr2char(p + n);
+ tl = utf_ptr2len(p + n);
memmove(p + tl, p, (size_t)n);
- utf_char2bytes(c, (char *)p);
+ utf_char2bytes(c, p);
stack[depth].ts_fidxtry = (char_u)(sp->ts_fidx + n + tl);
} else {
PROF_STORE(sp->ts_state)
@@ -2194,12 +2234,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((char *)p);
- tl = utfc_ptr2len((char *)p);
- n = utfc_ptr2len((char *)p + tl);
- n += utfc_ptr2len((char *)p + tl + n);
+ c = utf_ptr2char(p);
+ tl = utfc_ptr2len(p);
+ n = utfc_ptr2len(p + tl);
+ n += utfc_ptr2len(p + tl + n);
memmove(p, p + tl, (size_t)n);
- utf_char2bytes(c, (char *)p + n);
+ utf_char2bytes(c, p + n);
FALLTHROUGH;
@@ -2220,9 +2260,9 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// Use the first byte to quickly find the first entry that may
// match. If the index is -1 there is none.
if (soundfold) {
- sp->ts_curi = slang->sl_repsal_first[fword[sp->ts_fidx]];
+ sp->ts_curi = slang->sl_repsal_first[(uint8_t)fword[sp->ts_fidx]];
} else {
- sp->ts_curi = lp->lp_replang->sl_rep_first[fword[sp->ts_fidx]];
+ sp->ts_curi = lp->lp_replang->sl_rep_first[(uint8_t)fword[sp->ts_fidx]];
}
if (sp->ts_curi < 0) {
@@ -2250,10 +2290,10 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
ftp = (fromto_T *)gap->ga_data + sp->ts_curi++;
if (*ftp->ft_from != *p) {
// past possible matching entries
- sp->ts_curi = (char_u)gap->ga_len;
+ sp->ts_curi = (int16_t)gap->ga_len;
break;
}
- if (STRNCMP(ftp->ft_from, p, STRLEN(ftp->ft_from)) == 0
+ if (strncmp(ftp->ft_from, p, strlen(ftp->ft_from)) == 0
&& TRY_DEEPER(su, stack, depth, SCORE_REP)) {
go_deeper(stack, depth, SCORE_REP);
#ifdef DEBUG_TRIEWALK
@@ -2267,10 +2307,10 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// Change the "from" to the "to" string.
depth++;
- fl = (int)STRLEN(ftp->ft_from);
- tl = (int)STRLEN(ftp->ft_to);
+ fl = (int)strlen(ftp->ft_from);
+ tl = (int)strlen(ftp->ft_to);
if (fl != tl) {
- STRMOVE(p + tl, p + fl);
+ STRMOVE(p + tl, (char *)p + fl);
repextra += tl - fl;
}
memmove(p, ftp->ft_to, (size_t)tl);
@@ -2296,11 +2336,11 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
gap = &lp->lp_replang->sl_rep;
}
ftp = (fromto_T *)gap->ga_data + sp->ts_curi - 1;
- fl = (int)STRLEN(ftp->ft_from);
- tl = (int)STRLEN(ftp->ft_to);
+ fl = (int)strlen(ftp->ft_from);
+ tl = (int)strlen(ftp->ft_to);
p = fword + sp->ts_fidx;
if (fl != tl) {
- STRMOVE(p + fl, p + tl);
+ STRMOVE(p + fl, (char *)p + tl);
repextra -= tl - fl;
}
memmove(p, ftp->ft_from, (size_t)fl);
@@ -2344,9 +2384,9 @@ static void go_deeper(trystate_T *stack, int depth, int score_add)
/// words and put it in "kword".
/// Theoretically there could be several keep-case words that result in the
/// same case-folded word, but we only find one...
-static void find_keepcap_word(slang_T *slang, char_u *fword, char_u *kword)
+static void find_keepcap_word(slang_T *slang, char *fword, char *kword)
{
- char_u uword[MAXWLEN]; // "fword" in upper-case
+ char uword[MAXWLEN]; // "fword" in upper-case
int depth;
idx_T tryidx;
@@ -2363,7 +2403,7 @@ static void find_keepcap_word(slang_T *slang, char_u *fword, char_u *kword)
int c;
idx_T lo, hi, m;
char_u *p;
- char_u *byts = slang->sl_kbyts; // array with bytes of the words
+ char_u *byts = (char_u *)slang->sl_kbyts; // array with bytes of the words
idx_T *idxs = slang->sl_kidxs; // array with indexes
if (byts == NULL) {
@@ -2402,13 +2442,13 @@ 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((char *)fword + fwordidx[depth]);
+ flen = utf_ptr2len(fword + fwordidx[depth]);
ulen = utf_ptr2len((char *)uword + uwordidx[depth]);
if (round[depth] == 1) {
- p = fword + fwordidx[depth];
+ p = (char_u *)fword + fwordidx[depth];
l = flen;
} else {
- p = uword + uwordidx[depth];
+ p = (char_u *)uword + uwordidx[depth];
l = ulen;
}
@@ -2443,12 +2483,14 @@ static void find_keepcap_word(slang_T *slang, char_u *fword, char_u *kword)
// Found the matching char. Copy it to "kword" and go a
// level deeper.
if (round[depth] == 1) {
- STRNCPY(kword + kwordlen[depth], fword + fwordidx[depth], // NOLINT(runtime/printf)
- flen);
+ strncpy(kword + kwordlen[depth], // NOLINT(runtime/printf)
+ fword + fwordidx[depth],
+ (size_t)flen);
kwordlen[depth + 1] = kwordlen[depth] + flen;
} else {
- STRNCPY(kword + kwordlen[depth], uword + uwordidx[depth], // NOLINT(runtime/printf)
- ulen);
+ strncpy(kword + kwordlen[depth], // NOLINT(runtime/printf)
+ uword + uwordidx[depth],
+ (size_t)ulen);
kwordlen[depth + 1] = kwordlen[depth] + ulen;
}
fwordidx[depth + 1] = fwordidx[depth] + flen;
@@ -2483,7 +2525,7 @@ static void score_comp_sal(suginfo_T *su)
lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
if (!GA_EMPTY(&lp->lp_slang->sl_sal)) {
// soundfold the bad word
- spell_soundfold(lp->lp_slang, su->su_fbadword, true, badsound);
+ spell_soundfold(lp->lp_slang, (char *)su->su_fbadword, true, (char *)badsound);
for (i = 0; i < su->su_ga.ga_len; i++) {
stp = &SUG(su->su_ga, i);
@@ -2494,7 +2536,7 @@ static void score_comp_sal(suginfo_T *su)
if (score < SCORE_MAXMAX) {
// Add the suggestion.
sstp = &SUG(su->su_sga, su->su_sga.ga_len);
- sstp->st_word = vim_strsave(stp->st_word);
+ sstp->st_word = xstrdup(stp->st_word);
sstp->st_wordlen = stp->st_wordlen;
sstp->st_score = score;
sstp->st_altscore = 0;
@@ -2515,8 +2557,8 @@ static void score_combine(suginfo_T *su)
garray_T *gap;
langp_T *lp;
suggest_T *stp;
- char_u *p;
- char_u badsound[MAXWLEN];
+ char *p;
+ char badsound[MAXWLEN];
int round;
slang_T *slang = NULL;
@@ -2526,11 +2568,11 @@ static void score_combine(suginfo_T *su)
if (!GA_EMPTY(&lp->lp_slang->sl_sal)) {
// soundfold the bad word
slang = lp->lp_slang;
- spell_soundfold(slang, su->su_fbadword, true, badsound);
+ spell_soundfold(slang, (char *)su->su_fbadword, true, badsound);
for (int i = 0; i < su->su_ga.ga_len; i++) {
stp = &SUG(su->su_ga, i);
- stp->st_altscore = stp_sal_score(stp, su, slang, badsound);
+ stp->st_altscore = stp_sal_score(stp, su, slang, (char_u *)badsound);
if (stp->st_altscore == SCORE_MAXMAX) {
stp->st_score = (stp->st_score * 3 + SCORE_BIG) / 4;
} else {
@@ -2551,8 +2593,7 @@ static void score_combine(suginfo_T *su)
// Add the alternate score to su_sga.
for (int i = 0; i < su->su_sga.ga_len; i++) {
stp = &SUG(su->su_sga, i);
- stp->st_altscore = spell_edit_score(slang,
- su->su_badword, stp->st_word);
+ stp->st_altscore = spell_edit_score(slang, (char_u *)su->su_badword, (char_u *)stp->st_word);
if (stp->st_score == SCORE_MAXMAX) {
stp->st_score = (SCORE_BIG * 7 + stp->st_altscore) / 8;
} else {
@@ -2582,7 +2623,7 @@ static void score_combine(suginfo_T *su)
p = SUG(*gap, i).st_word;
int j;
for (j = 0; j < ga.ga_len; j++) {
- if (STRCMP(stp[j].st_word, p) == 0) {
+ if (strcmp(stp[j].st_word, p) == 0) {
break;
}
}
@@ -2621,7 +2662,7 @@ static int stp_sal_score(suggest_T *stp, suginfo_T *su, slang_T *slang, char_u *
char_u badsound2[MAXWLEN];
char_u fword[MAXWLEN];
char_u goodsound[MAXWLEN];
- char_u goodword[MAXWLEN];
+ char goodword[MAXWLEN];
int lendiff;
lendiff = su->su_badlen - stp->st_orglen;
@@ -2629,7 +2670,7 @@ static int stp_sal_score(suggest_T *stp, suginfo_T *su, slang_T *slang, char_u *
pbad = badsound;
} else {
// soundfold the bad word with more characters following
- (void)spell_casefold(curwin, su->su_badptr, stp->st_orglen, fword, MAXWLEN);
+ (void)spell_casefold(curwin, su->su_badptr, stp->st_orglen, (char *)fword, MAXWLEN);
// When joining two words the sound often changes a lot. E.g., "t he"
// sounds like "t h" while "the" sounds like "@". Avoid that by
@@ -2637,12 +2678,12 @@ static int stp_sal_score(suggest_T *stp, suginfo_T *su, slang_T *slang, char_u *
// space.
if (ascii_iswhite(su->su_badptr[su->su_badlen])
&& *skiptowhite(stp->st_word) == NUL) {
- for (p = fword; *(p = skiptowhite(p)) != NUL;) {
- STRMOVE(p, p + 1);
+ for (p = fword; *(p = (char_u *)skiptowhite((char *)p)) != NUL;) {
+ STRMOVE(p, (char *)p + 1);
}
}
- spell_soundfold(slang, fword, true, badsound2);
+ spell_soundfold(slang, (char *)fword, true, (char *)badsound2);
pbad = badsound2;
}
@@ -2650,17 +2691,17 @@ static int stp_sal_score(suggest_T *stp, suginfo_T *su, slang_T *slang, char_u *
// Add part of the bad word to the good word, so that we soundfold
// what replaces the bad word.
STRCPY(goodword, stp->st_word);
- STRLCPY(goodword + stp->st_wordlen,
- su->su_badptr + su->su_badlen - lendiff, lendiff + 1);
- pgood = goodword;
+ xstrlcpy(goodword + stp->st_wordlen,
+ su->su_badptr + su->su_badlen - lendiff, (size_t)lendiff + 1);
+ pgood = (char_u *)goodword;
} else {
- pgood = stp->st_word;
+ pgood = (char_u *)stp->st_word;
}
// Sound-fold the word and compute the score for the difference.
- spell_soundfold(slang, pgood, false, goodsound);
+ spell_soundfold(slang, (char *)pgood, false, (char *)goodsound);
- return soundalike_score(goodsound, pbad);
+ return soundalike_score((char *)goodsound, (char *)pbad);
}
/// structure used to store soundfolded words that add_sound_suggest() has
@@ -2707,7 +2748,7 @@ static void suggest_try_soundalike(suginfo_T *su)
slang = lp->lp_slang;
if (!GA_EMPTY(&slang->sl_sal) && slang->sl_sbyts != NULL) {
// soundfold the bad word
- spell_soundfold(slang, su->su_fbadword, true, salword);
+ spell_soundfold(slang, (char *)su->su_fbadword, true, (char *)salword);
// try all kinds of inserts/deletes/swaps/etc.
// TODO(vim): also soundfold the next words, so that we can try joining
@@ -2715,7 +2756,7 @@ static void suggest_try_soundalike(suginfo_T *su)
#ifdef SUGGEST_PROFILE
prof_init();
#endif
- suggest_trie_walk(su, lp, salword, true);
+ suggest_trie_walk(su, lp, (char *)salword, true);
#ifdef SUGGEST_PROFILE
prof_report("soundalike");
#endif
@@ -2757,7 +2798,7 @@ static void suggest_try_soundalike_finish(void)
/// produce this soundfolded word.
///
/// @param score soundfold score
-static void add_sound_suggest(suginfo_T *su, char_u *goodword, int score, langp_T *lp)
+static void add_sound_suggest(suginfo_T *su, char *goodword, int score, langp_T *lp)
{
slang_T *slang = lp->lp_slang; // language for sound folding
int sfwordnr;
@@ -2783,14 +2824,14 @@ static void add_sound_suggest(suginfo_T *su, char_u *goodword, int score, langp_
// the words that have a better score than before. Use a hashtable to
// remember the words that have been done.
hash = hash_hash(goodword);
- const size_t goodword_len = STRLEN(goodword);
+ const size_t goodword_len = strlen(goodword);
hi = hash_lookup(&slang->sl_sounddone, (const char *)goodword, goodword_len,
hash);
if (HASHITEM_EMPTY(hi)) {
sft = xmalloc(sizeof(sftword_T) + goodword_len);
sft->sft_score = (int16_t)score;
memcpy(sft->sft_word, goodword, goodword_len + 1);
- hash_add_item(&slang->sl_sounddone, hi, sft->sft_word, hash);
+ hash_add_item(&slang->sl_sounddone, hi, (char *)sft->sft_word, hash);
} else {
sft = HI2SFT(hi);
if (score >= sft->sft_score) {
@@ -2800,21 +2841,21 @@ static void add_sound_suggest(suginfo_T *su, char_u *goodword, int score, langp_
}
// Find the word nr in the soundfold tree.
- sfwordnr = soundfold_find(slang, goodword);
+ sfwordnr = soundfold_find(slang, (char_u *)goodword);
if (sfwordnr < 0) {
internal_error("add_sound_suggest()");
return;
}
// Go over the list of good words that produce this soundfold word
- nrline = ml_get_buf(slang->sl_sugbuf, (linenr_T)sfwordnr + 1, false);
+ nrline = (char_u *)ml_get_buf(slang->sl_sugbuf, (linenr_T)sfwordnr + 1, false);
orgnr = 0;
while (*nrline != NUL) {
// The wordnr was stored in a minimal nr of bytes as an offset to the
// previous wordnr.
orgnr += bytes2offset(&nrline);
- byts = slang->sl_fbyts;
+ byts = (char_u *)slang->sl_fbyts;
idxs = slang->sl_fidxs;
// Lookup the word "orgnr" one of the two tries.
@@ -2866,13 +2907,13 @@ badword:
if (flags & WF_KEEPCAP) {
// Must find the word in the keep-case tree.
- find_keepcap_word(slang, theword, cword);
+ find_keepcap_word(slang, (char *)theword, (char *)cword);
p = cword;
} else {
flags |= su->su_badflags;
if ((flags & WF_CAPMASK) != 0) {
// Need to fix case according to "flags".
- make_case_word(theword, cword, flags);
+ make_case_word((char *)theword, (char *)cword, flags);
p = cword;
} else {
p = theword;
@@ -2883,7 +2924,7 @@ badword:
if (sps_flags & SPS_DOUBLE) {
// Add the suggestion if the score isn't too bad.
if (score <= su->su_maxscore) {
- add_suggestion(su, &su->su_sga, p, su->su_badlen,
+ add_suggestion(su, &su->su_sga, (char *)p, su->su_badlen,
score, 0, false, slang, false);
}
} else {
@@ -2917,9 +2958,9 @@ badword:
// inefficient, using an array is quicker.
limit = MAXSCORE(su->su_sfmaxscore - goodscore, score);
if (limit > SCORE_LIMITMAX) {
- goodscore += spell_edit_score(slang, su->su_badword, p);
+ goodscore += spell_edit_score(slang, (char_u *)su->su_badword, p);
} else {
- goodscore += spell_edit_score_limit(slang, su->su_badword,
+ goodscore += spell_edit_score_limit(slang, (char_u *)su->su_badword,
p, limit);
}
@@ -2931,7 +2972,7 @@ badword:
// Add the suggestion if the score isn't too bad.
goodscore = RESCORE(goodscore, score);
if (goodscore <= su->su_sfmaxscore) {
- add_suggestion(su, &su->su_ga, p, su->su_badlen,
+ add_suggestion(su, &su->su_ga, (char *)p, su->su_badlen,
goodscore, score, true, slang, true);
}
}
@@ -2952,7 +2993,7 @@ static int soundfold_find(slang_T *slang, char_u *word)
idx_T *idxs;
int wordnr = 0;
- byts = slang->sl_sbyts;
+ byts = (char_u *)slang->sl_sbyts;
idxs = slang->sl_sidxs;
for (;;) {
@@ -3029,7 +3070,7 @@ static bool similar_chars(slang_T *slang, int c1, int c2)
if (HASHITEM_EMPTY(hi)) {
m1 = 0;
} else {
- m1 = utf_ptr2char((char *)hi->hi_key + STRLEN(hi->hi_key) + 1);
+ m1 = utf_ptr2char(hi->hi_key + strlen(hi->hi_key) + 1);
}
} else {
m1 = slang->sl_map_array[c1];
@@ -3044,7 +3085,7 @@ static bool similar_chars(slang_T *slang, int c1, int c2)
if (HASHITEM_EMPTY(hi)) {
m2 = 0;
} else {
- m2 = utf_ptr2char((char *)hi->hi_key + STRLEN(hi->hi_key) + 1);
+ m2 = utf_ptr2char(hi->hi_key + strlen(hi->hi_key) + 1);
}
} else {
m2 = slang->sl_map_array[c2];
@@ -3061,7 +3102,7 @@ static bool similar_chars(slang_T *slang, int c1, int c2)
/// @param had_bonus value for st_had_bonus
/// @param slang language for sound folding
/// @param maxsf su_maxscore applies to soundfold score, su_sfmaxscore to the total score.
-static void add_suggestion(suginfo_T *su, garray_T *gap, const char_u *goodword, int badlenarg,
+static void add_suggestion(suginfo_T *su, garray_T *gap, const char *goodword, int badlenarg,
int score, int altscore, bool had_bonus, slang_T *slang, bool maxsf)
{
int goodlen; // len of goodword changed
@@ -3071,8 +3112,8 @@ static void add_suggestion(suginfo_T *su, garray_T *gap, const char_u *goodword,
// Minimize "badlen" for consistency. Avoids that changing "the the" to
// "thee the" is added next to changing the first "the" the "thee".
- const char_u *pgood = goodword + STRLEN(goodword);
- char_u *pbad = su->su_badptr + badlenarg;
+ const char *pgood = goodword + strlen(goodword);
+ char *pbad = su->su_badptr + badlenarg;
for (;;) {
goodlen = (int)(pgood - goodword);
badlen = (int)(pbad - su->su_badptr);
@@ -3081,7 +3122,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((char *)pgood) != utf_ptr2char((char *)pbad)) {
+ if (utf_ptr2char(pgood) != utf_ptr2char(pbad)) {
break;
}
}
@@ -3100,10 +3141,10 @@ static void add_suggestion(suginfo_T *su, garray_T *gap, const char_u *goodword,
// being replaced "thes," -> "these" is a different suggestion from
// "thes" -> "these".
stp = &SUG(*gap, 0);
- for (i = gap->ga_len; --i >= 0; ++stp) {
+ for (i = gap->ga_len; --i >= 0; stp++) {
if (stp->st_wordlen == goodlen
&& stp->st_orglen == badlen
- && STRNCMP(stp->st_word, goodword, goodlen) == 0) {
+ && strncmp(stp->st_word, goodword, (size_t)goodlen) == 0) {
// Found it. Remember the word with the lowest score.
if (stp->st_slang == NULL) {
stp->st_slang = slang;
@@ -3144,7 +3185,7 @@ static void add_suggestion(suginfo_T *su, garray_T *gap, const char_u *goodword,
if (i < 0) {
// Add a suggestion.
stp = GA_APPEND_VIA_PTR(suggest_T, gap);
- stp->st_word = vim_strnsave(goodword, (size_t)goodlen);
+ stp->st_word = xstrnsave(goodword, (size_t)goodlen);
stp->st_wordlen = goodlen;
stp->st_score = score;
stp->st_altscore = altscore;
@@ -3173,7 +3214,7 @@ static void add_suggestion(suginfo_T *su, garray_T *gap, const char_u *goodword,
static void check_suggestions(suginfo_T *su, garray_T *gap)
{
suggest_T *stp;
- char_u longword[MAXWLEN + 1];
+ char longword[MAXWLEN + 1];
int len;
hlf_T attr;
@@ -3183,10 +3224,10 @@ static void check_suggestions(suginfo_T *su, garray_T *gap)
stp = &SUG(*gap, 0);
for (int i = gap->ga_len - 1; i >= 0; i--) {
// Need to append what follows to check for "the the".
- STRLCPY(longword, stp[i].st_word, MAXWLEN + 1);
+ xstrlcpy(longword, stp[i].st_word, MAXWLEN + 1);
len = stp[i].st_wordlen;
- STRLCPY(longword + len, su->su_badptr + stp[i].st_orglen,
- MAXWLEN - len + 1);
+ xstrlcpy(longword + len, su->su_badptr + stp[i].st_orglen,
+ (size_t)(MAXWLEN - len + 1));
attr = HLF_COUNT;
(void)spell_check(curwin, longword, &attr, NULL, false);
if (attr != HLF_COUNT) {
@@ -3201,19 +3242,20 @@ 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)
+static void add_banned(suginfo_T *su, char *word)
{
char_u *s;
hash_T hash;
hashitem_T *hi;
hash = hash_hash(word);
- const size_t word_len = STRLEN(word);
- hi = hash_lookup(&su->su_banned, (const char *)word, word_len, hash);
- if (HASHITEM_EMPTY(hi)) {
- s = xmemdupz(word, word_len);
- hash_add_item(&su->su_banned, hi, s, hash);
+ const size_t word_len = strlen(word);
+ hi = hash_lookup(&su->su_banned, word, word_len, hash);
+ if (!HASHITEM_EMPTY(hi)) { // already present
+ return;
}
+ s = xmemdupz(word, word_len);
+ hash_add_item(&su->su_banned, hi, (char *)s, hash);
}
/// Recompute the score for all suggestions if sound-folding is possible. This
@@ -3240,7 +3282,7 @@ static void rescore_one(suginfo_T *su, suggest_T *stp)
if (slang == su->su_sallang) {
p = su->su_sal_badword;
} else {
- spell_soundfold(slang, su->su_fbadword, true, sal_badword);
+ spell_soundfold(slang, (char *)su->su_fbadword, true, (char *)sal_badword);
p = sal_badword;
}
@@ -3280,21 +3322,23 @@ static int sug_compare(const void *s1, const void *s2)
static int cleanup_suggestions(garray_T *gap, int maxscore, int keep)
FUNC_ATTR_NONNULL_ALL
{
- if (gap->ga_len > 0) {
- // Sort the list.
- qsort(gap->ga_data, (size_t)gap->ga_len, sizeof(suggest_T), sug_compare);
+ if (gap->ga_len <= 0) {
+ return maxscore;
+ }
- // Truncate the list to the number of suggestions that will be displayed.
- if (gap->ga_len > keep) {
- suggest_T *const stp = &SUG(*gap, 0);
+ // Sort the list.
+ qsort(gap->ga_data, (size_t)gap->ga_len, sizeof(suggest_T), sug_compare);
- for (int i = keep; i < gap->ga_len; i++) {
- xfree(stp[i].st_word);
- }
- gap->ga_len = keep;
- if (keep >= 1) {
- return stp[keep - 1].st_score;
- }
+ // Truncate the list to the number of suggestions that will be displayed.
+ if (gap->ga_len > keep) {
+ suggest_T *const stp = &SUG(*gap, 0);
+
+ for (int i = keep; i < gap->ga_len; i++) {
+ xfree(stp[i].st_word);
+ }
+ gap->ga_len = keep;
+ if (keep >= 1) {
+ return stp[keep - 1].st_score;
}
}
return maxscore;
@@ -3307,15 +3351,15 @@ static int cleanup_suggestions(garray_T *gap, int maxscore, int keep)
///
/// @param goodstart sound-folded good word
/// @param badstart sound-folded bad word
-static int soundalike_score(char_u *goodstart, char_u *badstart)
+static int soundalike_score(char *goodstart, char *badstart)
{
- char_u *goodsound = goodstart;
- char_u *badsound = badstart;
+ char *goodsound = goodstart;
+ char *badsound = badstart;
int goodlen;
int badlen;
int n;
- char_u *pl, *ps;
- char_u *pl2, *ps2;
+ char *pl, *ps;
+ char *pl2, *ps2;
int score = 0;
// Adding/inserting "*" at the start (word starts with vowel) shouldn't be
@@ -3346,8 +3390,8 @@ static int soundalike_score(char_u *goodstart, char_u *badstart)
}
}
- goodlen = (int)STRLEN(goodsound);
- badlen = (int)STRLEN(badsound);
+ goodlen = (int)strlen(goodsound);
+ badlen = (int)strlen(badsound);
// Return quickly if the lengths are too different to be fixed by two
// changes.
@@ -3380,7 +3424,7 @@ static int soundalike_score(char_u *goodstart, char_u *badstart)
ps++;
}
// strings must be equal after second delete
- if (STRCMP(pl + 1, ps) == 0) {
+ if (strcmp(pl + 1, ps) == 0) {
return score + SCORE_DEL * 2;
}
@@ -3404,12 +3448,12 @@ static int soundalike_score(char_u *goodstart, char_u *badstart)
// 2: delete then swap, then rest must be equal
if (pl2[0] == ps2[1] && pl2[1] == ps2[0]
- && STRCMP(pl2 + 2, ps2 + 2) == 0) {
+ && strcmp(pl2 + 2, ps2 + 2) == 0) {
return score + SCORE_DEL + SCORE_SWAP;
}
// 3: delete then substitute, then the rest must be equal
- if (STRCMP(pl2 + 1, ps2 + 1) == 0) {
+ if (strcmp(pl2 + 1, ps2 + 1) == 0) {
return score + SCORE_DEL + SCORE_SUBST;
}
@@ -3422,7 +3466,7 @@ static int soundalike_score(char_u *goodstart, char_u *badstart)
ps2++;
}
// delete a char and then strings must be equal
- if (STRCMP(pl2 + 1, ps2) == 0) {
+ if (strcmp(pl2 + 1, ps2) == 0) {
return score + SCORE_SWAP + SCORE_DEL;
}
}
@@ -3435,7 +3479,7 @@ static int soundalike_score(char_u *goodstart, char_u *badstart)
ps2++;
}
// delete a char and then strings must be equal
- if (STRCMP(pl2 + 1, ps2) == 0) {
+ if (strcmp(pl2 + 1, ps2) == 0) {
return score + SCORE_SUBST + SCORE_DEL;
}
@@ -3463,12 +3507,12 @@ static int soundalike_score(char_u *goodstart, char_u *badstart)
}
// 3: swap and swap again
if (pl2[0] == ps2[1] && pl2[1] == ps2[0]
- && STRCMP(pl2 + 2, ps2 + 2) == 0) {
+ && strcmp(pl2 + 2, ps2 + 2) == 0) {
return score + SCORE_SWAP + SCORE_SWAP;
}
// 4: swap and substitute
- if (STRCMP(pl2 + 1, ps2 + 1) == 0) {
+ if (strcmp(pl2 + 1, ps2 + 1) == 0) {
return score + SCORE_SWAP + SCORE_SUBST;
}
}
@@ -3486,12 +3530,12 @@ static int soundalike_score(char_u *goodstart, char_u *badstart)
// 6: substitute and swap
if (pl2[0] == ps2[1] && pl2[1] == ps2[0]
- && STRCMP(pl2 + 2, ps2 + 2) == 0) {
+ && strcmp(pl2 + 2, ps2 + 2) == 0) {
return score + SCORE_SUBST + SCORE_SWAP;
}
// 7: substitute and substitute
- if (STRCMP(pl2 + 1, ps2 + 1) == 0) {
+ if (strcmp(pl2 + 1, ps2 + 1) == 0) {
return score + SCORE_SUBST + SCORE_SUBST;
}
@@ -3502,7 +3546,7 @@ static int soundalike_score(char_u *goodstart, char_u *badstart)
pl2++;
ps2++;
}
- if (STRCMP(pl2 + 1, ps2) == 0) {
+ if (strcmp(pl2 + 1, ps2) == 0) {
return score + SCORE_INS + SCORE_DEL;
}
@@ -3513,7 +3557,7 @@ static int soundalike_score(char_u *goodstart, char_u *badstart)
pl2++;
ps2++;
}
- if (STRCMP(pl2, ps2 + 1) == 0) {
+ if (strcmp(pl2, ps2 + 1) == 0) {
return score + SCORE_INS + SCORE_DEL;
}
@@ -3531,7 +3575,7 @@ static int soundalike_score(char_u *goodstart, char_u *badstart)
/// The implementation of the algorithm comes from Aspell editdist.cpp,
/// edit_distance(). It has been converted from C++ to C and modified to
/// support multi-byte characters.
-static int spell_edit_score(slang_T *slang, char_u *badword, char_u *goodword)
+static int spell_edit_score(slang_T *slang, const char_u *badword, const char_u *goodword)
{
int *cnt;
int j, i;
@@ -3548,12 +3592,12 @@ static int spell_edit_score(slang_T *slang, char_u *badword, char_u *goodword)
// Get the characters from the multi-byte strings and put them in an
// int array for easy access.
badlen = 0;
- for (const char_u *p = badword; *p != NUL;) {
+ for (const char *p = (char *)badword; *p != NUL;) {
wbadword[badlen++] = mb_cptr2char_adv(&p);
}
wbadword[badlen++] = 0;
goodlen = 0;
- for (const char_u *p = goodword; *p != NUL;) {
+ for (const char *p = (char *)goodword; *p != NUL;) {
wgoodword[goodlen++] = mb_cptr2char_adv(&p);
}
wgoodword[goodlen++] = 0;
@@ -3636,7 +3680,8 @@ static int spell_edit_score_limit(slang_T *slang, char_u *badword, char_u *goodw
/// Multi-byte version of spell_edit_score_limit().
/// Keep it in sync with the above!
-static int spell_edit_score_limit_w(slang_T *slang, char_u *badword, char_u *goodword, int limit)
+static int spell_edit_score_limit_w(slang_T *slang, const char_u *badword, const char_u *goodword,
+ int limit)
{
limitscore_T stack[10]; // allow for over 3 * 2 edits
int stackidx;
@@ -3653,12 +3698,12 @@ static int spell_edit_score_limit_w(slang_T *slang, char_u *badword, char_u *goo
// Get the characters from the multi-byte strings and put them in an
// int array for easy access.
bi = 0;
- for (const char_u *p = badword; *p != NUL;) {
+ for (const char *p = (char *)badword; *p != NUL;) {
wbadword[bi++] = mb_cptr2char_adv(&p);
}
wbadword[bi++] = 0;
gi = 0;
- for (const char_u *p = goodword; *p != NUL;) {
+ for (const char *p = (char *)goodword; *p != NUL;) {
wgoodword[gi++] = mb_cptr2char_adv(&p);
}
wgoodword[gi++] = 0;
diff --git a/src/nvim/state.c b/src/nvim/state.c
index 61740800a1..9ba5f81776 100644
--- a/src/nvim/state.c
+++ b/src/nvim/state.c
@@ -1,27 +1,38 @@
// 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 <stddef.h>
+#include <string.h>
#include "nvim/ascii.h"
#include "nvim/autocmd.h"
+#include "nvim/buffer_defs.h"
#include "nvim/drawscreen.h"
#include "nvim/eval.h"
-#include "nvim/ex_docmd.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/event/defs.h"
+#include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
#include "nvim/getchar.h"
+#include "nvim/globals.h"
#include "nvim/insexpand.h"
-#include "nvim/lib/kvec.h"
+#include "nvim/keycodes.h"
#include "nvim/log.h"
+#include "nvim/macros.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/strings.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "state.c.generated.h"
+# include "state.c.generated.h" // IWYU pragma: export
#endif
void state_enter(VimState *s)
@@ -57,7 +68,7 @@ getkey:
// 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);
+ update_screen();
setcursor(); // put cursor back where it belongs
}
// Flush screen updates before blocking
@@ -65,7 +76,7 @@ getkey:
// Call `os_inchar` directly to block for events or user input without
// consuming anything from `input_buffer`(os/input.c) or calling the
// mapping engine.
- (void)os_inchar(NULL, 0, -1, 0, main_loop.events);
+ (void)os_inchar(NULL, 0, -1, typebuf.tb_change_cnt, main_loop.events);
// If an event was put into the queue, we send K_EVENT directly.
if (!multiqueue_empty(main_loop.events)) {
key = K_EVENT;
@@ -229,7 +240,9 @@ void get_mode(char *buf)
/// Fires a ModeChanged autocmd if appropriate.
void may_trigger_modechanged(void)
{
- if (!has_event(EVENT_MODECHANGED)) {
+ // Skip this when got_int is set, the autocommand will not be executed.
+ // Better trigger it next time.
+ if (!has_event(EVENT_MODECHANGED) || got_int) {
return;
}
@@ -237,7 +250,7 @@ void may_trigger_modechanged(void)
char pattern_buf[2 * MODE_MAX_LENGTH];
get_mode(curr_mode);
- if (STRCMP(curr_mode, last_mode) == 0) {
+ if (strcmp(curr_mode, last_mode) == 0) {
return;
}
diff --git a/src/nvim/state.h b/src/nvim/state.h
index b93d57d035..76a38b0dab 100644
--- a/src/nvim/state.h
+++ b/src/nvim/state.h
@@ -3,6 +3,8 @@
#include <stddef.h>
+struct vim_state;
+
typedef struct vim_state VimState;
typedef int (*state_check_callback)(VimState *state);
diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c
index e868a79b9a..6ad1f31143 100644
--- a/src/nvim/statusline.c
+++ b/src/nvim/statusline.c
@@ -5,22 +5,43 @@
#include <assert.h>
#include <inttypes.h>
#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
-#include "nvim/assert.h"
-#include "nvim/autocmd.h"
+#include "nvim/api/private/defs.h"
+#include "nvim/api/private/helpers.h"
+#include "nvim/ascii.h"
#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/drawscreen.h"
#include "nvim/eval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/eval/vars.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
#include "nvim/grid.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
+#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/optionstr.h"
+#include "nvim/os/os.h"
+#include "nvim/path.h"
+#include "nvim/pos.h"
+#include "nvim/screen.h"
+#include "nvim/sign_defs.h"
#include "nvim/statusline.h"
+#include "nvim/strings.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/vim.h"
@@ -43,11 +64,10 @@ void win_redr_status(win_T *wp)
{
int row;
int col;
- char_u *p;
+ char *p;
int len;
int fillchar;
int attr;
- int width;
int this_ru_col;
bool is_stl_global = global_stl_height() > 0;
static bool busy = false;
@@ -74,53 +94,54 @@ 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;
+ const int stl_width = is_stl_global ? Columns : wp->w_width;
get_trans_bufname(wp->w_buffer);
p = NameBuff;
- len = (int)STRLEN(p);
+ len = (int)strlen(p);
- if (bt_help(wp->w_buffer)
- || wp->w_p_pvw
- || bufIsChanged(wp->w_buffer)
- || wp->w_buffer->b_p_ro) {
+ if ((bt_help(wp->w_buffer)
+ || wp->w_p_pvw
+ || bufIsChanged(wp->w_buffer)
+ || wp->w_buffer->b_p_ro)
+ && len < MAXPATHL - 1) {
*(p + len++) = ' ';
}
if (bt_help(wp->w_buffer)) {
- snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", _("[Help]"));
- len += (int)STRLEN(p + len);
+ snprintf(p + len, MAXPATHL - (size_t)len, "%s", _("[Help]"));
+ len += (int)strlen(p + len);
}
if (wp->w_p_pvw) {
- snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", _("[Preview]"));
- len += (int)STRLEN(p + len);
+ snprintf(p + len, MAXPATHL - (size_t)len, "%s", _("[Preview]"));
+ len += (int)strlen(p + len);
}
if (bufIsChanged(wp->w_buffer)) {
- snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", "[+]");
- len += (int)STRLEN(p + len);
+ snprintf(p + len, MAXPATHL - (size_t)len, "%s", "[+]");
+ len += (int)strlen(p + len);
}
if (wp->w_buffer->b_p_ro) {
- snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", _("[RO]"));
- // len += (int)STRLEN(p + len); // dead assignment
+ snprintf(p + len, MAXPATHL - (size_t)len, "%s", _("[RO]"));
+ // len += (int)strlen(p + len); // dead assignment
}
- this_ru_col = ru_col - (Columns - width);
- if (this_ru_col < (width + 1) / 2) {
- this_ru_col = (width + 1) / 2;
+ this_ru_col = ru_col - (Columns - stl_width);
+ if (this_ru_col < (stl_width + 1) / 2) {
+ this_ru_col = (stl_width + 1) / 2;
}
if (this_ru_col <= 1) {
- p = (char_u *)"<"; // No room for file name!
+ p = "<"; // No room for file name!
len = 1;
} else {
int clen = 0, i;
// Count total number of display cells.
- clen = (int)mb_string2cells((char *)p);
+ clen = (int)mb_string2cells(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((char *)p + i)) {
- clen -= utf_ptr2cells((char *)p + i);
+ i += utfc_ptr2len(p + i)) {
+ clen -= utf_ptr2cells(p + i);
}
len = clen;
if (i > 0) {
@@ -136,13 +157,23 @@ void win_redr_status(win_T *wp)
grid_fill(&default_grid, row, row + 1, len + col,
this_ru_col + col, fillchar, fillchar, attr);
- if (get_keymap_str(wp, "<%s>", (char *)NameBuff, MAXPATHL)
- && this_ru_col - len > (int)(STRLEN(NameBuff) + 1)) {
+ if (get_keymap_str(wp, "<%s>", NameBuff, MAXPATHL)
+ && this_ru_col - len > (int)(strlen(NameBuff) + 1)) {
grid_puts(&default_grid, NameBuff, row,
- (int)((size_t)this_ru_col - STRLEN(NameBuff) - 1), attr);
+ (int)((size_t)this_ru_col - strlen(NameBuff) - 1), attr);
}
win_redr_ruler(wp, true);
+
+ // Draw the 'showcmd' information if 'showcmdloc' == "statusline".
+ if (p_sc && *p_sloc == 's') {
+ const int sc_width = MIN(10, this_ru_col - len - 2);
+
+ if (sc_width > 0) {
+ grid_puts_len(&default_grid, showcmd_buf, sc_width, row,
+ wp->w_wincol + this_ru_col - sc_width - 1, attr);
+ }
+ }
}
// May need to draw the character below the vertical separator.
@@ -157,6 +188,252 @@ void win_redr_status(win_T *wp)
busy = false;
}
+/// 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 stl_clear_click_defs(StlClickDefinition *const click_defs, const size_t click_defs_size)
+{
+ if (click_defs != NULL) {
+ for (size_t 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(click_defs, 0, click_defs_size * sizeof(click_defs[0]));
+ }
+}
+
+/// Allocate or resize the click definitions array if needed.
+StlClickDefinition *stl_alloc_click_defs(StlClickDefinition *cdp, long width, size_t *size)
+{
+ if (*size < (size_t)width) {
+ xfree(cdp);
+ *size = (size_t)width;
+ cdp = xcalloc(*size, sizeof(StlClickDefinition));
+ }
+ return cdp;
+}
+
+/// Fill the click definitions array if needed.
+void stl_fill_click_defs(StlClickDefinition *click_defs, StlClickRecord *click_recs, char *buf,
+ int width, bool tabline)
+{
+ if (click_defs == NULL) {
+ return;
+ }
+
+ int col = 0;
+ int len = 0;
+
+ StlClickDefinition cur_click_def = {
+ .type = kStlClickDisabled,
+ };
+ for (int i = 0; click_recs[i].start != NULL; i++) {
+ len += vim_strnsize(buf, (int)(click_recs[i].start - buf));
+ if (col < len) {
+ while (col < len) {
+ click_defs[col++] = cur_click_def;
+ }
+ } else {
+ xfree(cur_click_def.func);
+ }
+ buf = (char *)click_recs[i].start;
+ cur_click_def = click_recs[i].def;
+ if (!tabline && !(cur_click_def.type == kStlClickDisabled
+ || cur_click_def.type == kStlClickFuncRun)) {
+ // window bar and status line only support click functions
+ cur_click_def.type = kStlClickDisabled;
+ }
+ }
+ if (col < width) {
+ while (col < width) {
+ click_defs[col++] = cur_click_def;
+ }
+ } else {
+ xfree(cur_click_def.func);
+ }
+}
+
+/// 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;
+ int curattr;
+ int row;
+ int col = 0;
+ int maxwidth;
+ int width;
+ int n;
+ int len;
+ int fillchar;
+ char buf[MAXPATHL];
+ char *stl;
+ char *p;
+ char *opt_name;
+ int opt_scope = 0;
+ stl_hlrec_t *hltab;
+ StlClickRecord *tabtab;
+ win_T *ewp;
+ int p_crb_save;
+ bool is_stl_global = global_stl_height() > 0;
+
+ ScreenGrid *grid = &default_grid;
+
+ // There is a tiny chance that this gets called recursively: When
+ // redrawing a status line triggers redrawing the ruler or tabline.
+ // Avoid trouble by not allowing recursion.
+ if (entered) {
+ return;
+ }
+ entered = true;
+
+ // setup environment for the task at hand
+ if (wp == NULL) {
+ // Use 'tabline'. Always at the first line of the screen.
+ stl = p_tal;
+ row = 0;
+ fillchar = ' ';
+ attr = HL_ATTR(HLF_TPF);
+ maxwidth = Columns;
+ opt_name = "tabline";
+ } else if (draw_winbar) {
+ opt_name = "winbar";
+ stl = ((*wp->w_p_wbr != NUL) ? wp->w_p_wbr : p_wbr);
+ opt_scope = ((*wp->w_p_wbr != NUL) ? OPT_LOCAL : 0);
+ 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;
+ stl_clear_click_defs(wp->w_winbar_click_defs, wp->w_winbar_click_defs_size);
+ wp->w_winbar_click_defs = stl_alloc_click_defs(wp->w_winbar_click_defs, maxwidth,
+ &wp->w_winbar_click_defs_size);
+ } else {
+ row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp);
+ fillchar = fillchar_status(&attr, wp);
+ maxwidth = is_stl_global ? Columns : wp->w_width;
+ stl_clear_click_defs(wp->w_status_click_defs, wp->w_status_click_defs_size);
+ wp->w_status_click_defs = stl_alloc_click_defs(wp->w_status_click_defs, maxwidth,
+ &wp->w_status_click_defs_size);
+
+ if (draw_ruler) {
+ stl = p_ruf;
+ opt_name = "rulerformat";
+ // advance past any leading group spec - implicit in ru_col
+ if (*stl == '%') {
+ if (*++stl == '-') {
+ stl++;
+ }
+ if (atoi(stl)) {
+ while (ascii_isdigit(*stl)) {
+ stl++;
+ }
+ }
+ if (*stl++ != '(') {
+ stl = p_ruf;
+ }
+ }
+ col = ru_col - (Columns - maxwidth);
+ if (col < (maxwidth + 1) / 2) {
+ col = (maxwidth + 1) / 2;
+ }
+ 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
+ fillchar = ' ';
+ attr = HL_ATTR(HLF_MSG);
+ }
+ } else {
+ opt_name = "statusline";
+ stl = ((*wp->w_p_stl != NUL) ? wp->w_p_stl : p_stl);
+ opt_scope = ((*wp->w_p_stl != NUL) ? OPT_LOCAL : 0);
+ }
+
+ col += is_stl_global ? 0 : wp->w_wincol;
+ }
+
+ if (maxwidth <= 0) {
+ goto theend;
+ }
+
+ // Temporarily reset 'cursorbind', we don't want a side effect from moving
+ // the cursor away and back.
+ ewp = wp == NULL ? curwin : wp;
+ p_crb_save = ewp->w_p_crb;
+ ewp->w_p_crb = false;
+
+ // Make a copy, because the statusline may include a function call that
+ // might change the option value and free the memory.
+ stl = xstrdup(stl);
+ width = build_stl_str_hl(ewp, buf, sizeof(buf), stl, opt_name, opt_scope,
+ fillchar, maxwidth, &hltab, &tabtab, NULL);
+
+ xfree(stl);
+ ewp->w_p_crb = p_crb_save;
+
+ // Make all characters printable.
+ p = transstr(buf, true);
+ len = (int)xstrlcpy(buf, p, sizeof(buf));
+ len = (size_t)len < sizeof(buf) ? len : (int)sizeof(buf) - 1;
+ xfree(p);
+
+ // fill up with "fillchar"
+ while (width < maxwidth && len < (int)sizeof(buf) - 1) {
+ len += utf_char2bytes(fillchar, buf + len);
+ width++;
+ }
+ buf[len] = NUL;
+
+ // Draw each snippet with the specified highlighting.
+ grid_puts_line_start(grid, row);
+
+ curattr = attr;
+ 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);
+ p = hltab[n].start;
+
+ if (hltab[n].userhl == 0) {
+ curattr = attr;
+ } else if (hltab[n].userhl < 0) {
+ curattr = syn_id2attr(-hltab[n].userhl);
+ } else if (wp != NULL && wp != curwin && wp->w_status_height != 0) {
+ curattr = highlight_stlnc[hltab[n].userhl - 1];
+ } else {
+ curattr = highlight_user[hltab[n].userhl - 1];
+ }
+ }
+ // Make sure to use an empty string instead of p, if p is beyond buf + len.
+ grid_puts(grid, p >= buf + len ? "" : p, row, col, curattr);
+
+ grid_puts_line_flush(false);
+
+ // 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;
+
+ stl_fill_click_defs(click_defs, tabtab, buf, maxwidth, wp == NULL);
+
+theend:
+ entered = false;
+}
+
void win_redr_winbar(win_T *wp)
{
static bool entered = false;
@@ -171,19 +448,7 @@ void win_redr_winbar(win_T *wp)
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;
}
@@ -213,11 +478,7 @@ void win_redr_ruler(win_T *wp, bool always)
}
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);
- }
return;
}
@@ -266,7 +527,7 @@ void win_redr_ruler(win_T *wp, bool always)
off = 0;
}
- if (!part_of_status && !ui_has_messages()) {
+ if (!part_of_status && p_ch == 0 && !ui_has(kUIMessages)) {
return;
}
@@ -286,7 +547,7 @@ void win_redr_ruler(win_T *wp, bool always)
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);
+ size_t len = strlen(buffer);
col_print(buffer + len, RULER_BUF_LEN - len,
empty_line ? 0 : (int)wp->w_cursor.col + 1,
(int)virtcol + 1);
@@ -294,7 +555,7 @@ void win_redr_ruler(win_T *wp, bool always)
// Add a "50%" if there is room for it.
// On the last line, don't print in the last column (scrolls the
// screen up on some terminals).
- int i = (int)STRLEN(buffer);
+ 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 && !is_stl_global) { // can't use last char of screen
@@ -342,9 +603,9 @@ void win_redr_ruler(win_T *wp, bool always)
}
ScreenGrid *grid = part_of_status ? &default_grid : &msg_grid_adj;
- grid_puts(grid, (char_u *)buffer, row, this_ru_col + off, attr);
+ grid_puts(grid, buffer, row, this_ru_col + off, attr);
grid_fill(grid, row, row + 1,
- this_ru_col + off + (int)STRLEN(buffer), off + width, fillchar,
+ this_ru_col + off + (int)strlen(buffer), off + width, fillchar,
fillchar, attr);
}
@@ -388,7 +649,6 @@ int fillchar_status(int *attr, win_T *wp)
void redraw_custom_statusline(win_T *wp)
{
static bool entered = false;
- int saved_did_emsg = did_emsg;
// When called recursively return. This can happen when the statusline
// contains an expression that triggers a redraw.
@@ -397,235 +657,262 @@ void redraw_custom_statusline(win_T *wp)
}
entered = true;
- did_emsg = 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, "",
- OPT_FREE | (*wp->w_p_stl != NUL
- ? OPT_LOCAL : OPT_GLOBAL), SID_ERROR);
- }
- did_emsg |= saved_did_emsg;
entered = false;
}
-/// Redraw the status line, window bar or ruler of window "wp".
-/// When "wp" is NULL redraw the tab pages line from 'tabline'.
-void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
+static void ui_ext_tabline_update(void)
{
- static bool entered = false;
- int attr;
- int curattr;
- int row;
+ Arena arena = ARENA_EMPTY;
+
+ size_t n_tabs = 0;
+ FOR_ALL_TABS(tp) {
+ 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_C(tab_info, "name", STRING_OBJ(arena_string(&arena, cstr_as_string(NameBuff))));
+
+ 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 = arena_array(&arena, n_buffers);
+ FOR_ALL_BUFFERS(buf) {
+ // Do not include unlisted buffers
+ if (!buf->b_p_bl) {
+ continue;
+ }
+
+ Dictionary buffer_info = arena_dict(&arena, 2);
+ PUT_C(buffer_info, "buffer", BUFFER_OBJ(buf->handle));
+
+ get_trans_bufname(buf);
+ PUT_C(buffer_info, "name", STRING_OBJ(arena_string(&arena, cstr_as_string(NameBuff))));
+
+ ADD_C(buffers, DICTIONARY_OBJ(buffer_info));
+ }
+
+ ui_call_tabline_update(curtab->handle, tabs, curbuf->handle, buffers);
+ arena_mem_free(arena_finish(&arena));
+}
+
+/// Draw the tab pages line at the top of the Vim window.
+void draw_tabline(void)
+{
+ int tabcount = 0;
+ int tabwidth = 0;
int col = 0;
- int maxwidth;
- int width;
- int n;
+ int scol = 0;
+ int attr;
+ win_T *wp;
+ win_T *cwp;
+ int wincount;
+ int modified;
+ int c;
int len;
- int fillchar;
- char buf[MAXPATHL];
- char_u *stl;
+ int attr_nosel = HL_ATTR(HLF_TP);
+ int attr_fill = HL_ATTR(HLF_TPF);
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;
+ int room;
+ int use_sep_chars = (t_colors < 8);
- ScreenGrid *grid = &default_grid;
+ if (default_grid.chars == NULL) {
+ return;
+ }
+ redraw_tabline = false;
- // There is a tiny chance that this gets called recursively: When
- // redrawing a status line triggers redrawing the ruler or tabline.
- // Avoid trouble by not allowing recursion.
- if (entered) {
+ if (ui_has(kUITabline)) {
+ ui_ext_tabline_update();
return;
}
- entered = true;
- // setup environment for the task at hand
- if (wp == NULL) {
- // Use 'tabline'. Always at the first line of the screen.
- stl = p_tal;
- row = 0;
- fillchar = ' ';
- 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 (tabline_height() < 1) {
+ return;
+ }
- if (row < 0) {
- return;
- }
+ // Clear tab_page_click_defs: Clicking outside of tabs has no effect.
+ assert(tab_page_click_defs_size >= (size_t)Columns);
+ stl_clear_click_defs(tab_page_click_defs, tab_page_click_defs_size);
- 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);
+ // Use the 'tabline' option if it's set.
+ if (*p_tal != NUL) {
+ win_redr_custom(NULL, false, false);
+ } else {
+ FOR_ALL_TABS(tp) {
+ tabcount++;
+ }
- stl_clear_click_defs(wp->w_winbar_click_defs, (long)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));
+ if (tabcount > 0) {
+ tabwidth = (Columns - 1 + tabcount / 2) / tabcount;
}
- } else {
- row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp);
- fillchar = fillchar_status(&attr, wp);
- maxwidth = is_stl_global ? Columns : wp->w_width;
- stl_clear_click_defs(wp->w_status_click_defs, (long)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 = (size_t)maxwidth;
- wp->w_status_click_defs = xcalloc(wp->w_status_click_defs_size, sizeof(StlClickRecord));
+ if (tabwidth < 6) {
+ tabwidth = 6;
}
- if (draw_ruler) {
- stl = p_ruf;
- // advance past any leading group spec - implicit in ru_col
- if (*stl == '%') {
- if (*++stl == '-') {
- stl++;
- }
- if (atoi((char *)stl)) {
- while (ascii_isdigit(*stl)) {
- stl++;
- }
- }
- if (*stl++ != '(') {
- stl = p_ruf;
- }
- }
- col = ru_col - (Columns - maxwidth);
- if (col < (maxwidth + 1) / 2) {
- col = (maxwidth + 1) / 2;
- }
- 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
- fillchar = ' ';
- attr = HL_ATTR(HLF_MSG);
+ attr = attr_nosel;
+ tabcount = 0;
+
+ FOR_ALL_TABS(tp) {
+ if (col >= Columns - 4) {
+ break;
}
- use_sandbox = was_set_insecurely(wp, "rulerformat", 0);
- } else {
- if (*wp->w_p_stl != NUL) {
- stl = wp->w_p_stl;
+ scol = col;
+
+ if (tp == curtab) {
+ cwp = curwin;
+ wp = firstwin;
} else {
- stl = p_stl;
+ cwp = tp->tp_curwin;
+ wp = tp->tp_firstwin;
}
- use_sandbox = was_set_insecurely(wp, "statusline", *wp->w_p_stl == NUL ? 0 : OPT_LOCAL);
- }
- col += is_stl_global ? 0 : wp->w_wincol;
- }
+ if (tp->tp_topframe == topframe) {
+ attr = win_hl_attr(cwp, HLF_TPS);
+ }
+ if (use_sep_chars && col > 0) {
+ grid_putchar(&default_grid, '|', 0, col++, attr);
+ }
- if (maxwidth <= 0) {
- goto theend;
- }
+ if (tp->tp_topframe != topframe) {
+ attr = win_hl_attr(cwp, HLF_TP);
+ }
- // Temporarily reset 'cursorbind', we don't want a side effect from moving
- // the cursor away and back.
- ewp = wp == NULL ? curwin : wp;
- p_crb_save = ewp->w_p_crb;
- ewp->w_p_crb = false;
+ grid_putchar(&default_grid, ' ', 0, col++, attr);
- // Make a copy, because the statusline may include a function call that
- // might change the option value and free the memory.
- stl = vim_strsave(stl);
- width =
- 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;
+ modified = false;
- // Make all characters printable.
- p = transstr(buf, true);
- len = (int)STRLCPY(buf, p, sizeof(buf));
- len = (size_t)len < sizeof(buf) ? len : (int)sizeof(buf) - 1;
- xfree(p);
+ for (wincount = 0; wp != NULL; wp = wp->w_next, wincount++) {
+ if (bufIsChanged(wp->w_buffer)) {
+ modified = true;
+ }
+ }
- // fill up with "fillchar"
- while (width < maxwidth && len < (int)sizeof(buf) - 1) {
- len += utf_char2bytes(fillchar, buf + len);
- width++;
- }
- buf[len] = NUL;
+ if (modified || wincount > 1) {
+ if (wincount > 1) {
+ vim_snprintf(NameBuff, MAXPATHL, "%d", wincount);
+ len = (int)strlen(NameBuff);
+ if (col + len >= Columns - 3) {
+ break;
+ }
+ grid_puts_len(&default_grid, NameBuff, len, 0, col,
+ hl_combine_attr(attr, win_hl_attr(cwp, HLF_T)));
+ col += len;
+ }
+ if (modified) {
+ grid_puts_len(&default_grid, "+", 1, 0, col++, attr);
+ }
+ grid_putchar(&default_grid, ' ', 0, col++, attr);
+ }
- // Draw each snippet with the specified highlighting.
- grid_puts_line_start(grid, row);
+ room = scol - col + tabwidth - 1;
+ if (room > 0) {
+ // Get buffer name in NameBuff[]
+ get_trans_bufname(cwp->w_buffer);
+ shorten_dir(NameBuff);
+ len = vim_strsize(NameBuff);
+ p = NameBuff;
+ while (len > room) {
+ len -= ptr2cells(p);
+ MB_PTR_ADV(p);
+ }
+ if (len > Columns - col - 1) {
+ len = Columns - col - 1;
+ }
- curattr = attr;
- p = buf;
- for (n = 0; hltab[n].start != NULL; n++) {
- int textlen = (int)(hltab[n].start - p);
- grid_puts_len(grid, (char_u *)p, textlen, row, col, curattr);
- col += vim_strnsize((char_u *)p, textlen);
- p = hltab[n].start;
+ grid_puts_len(&default_grid, p, (int)strlen(p), 0, col, attr);
+ col += len;
+ }
+ grid_putchar(&default_grid, ' ', 0, col++, attr);
+
+ // Store the tab page number in tab_page_click_defs[], so that
+ // jump_to_mouse() knows where each one is.
+ tabcount++;
+ while (scol < col) {
+ tab_page_click_defs[scol++] = (StlClickDefinition) {
+ .type = kStlClickTabSwitch,
+ .tabnr = tabcount,
+ .func = NULL,
+ };
+ }
+ }
- if (hltab[n].userhl == 0) {
- curattr = attr;
- } else if (hltab[n].userhl < 0) {
- curattr = syn_id2attr(-hltab[n].userhl);
- } else if (wp != NULL && wp != curwin && wp->w_status_height != 0) {
- curattr = highlight_stlnc[hltab[n].userhl - 1];
+ if (use_sep_chars) {
+ c = '_';
} else {
- curattr = highlight_user[hltab[n].userhl - 1];
+ c = ' ';
}
- }
- // Make sure to use an empty string instead of p, if p is beyond buf + len.
- grid_puts(grid, p >= buf + len ? (char_u *)"" : (char_u *)p, row, col,
- curattr);
+ grid_fill(&default_grid, 0, 1, col, Columns, c, c, attr_fill);
- grid_puts_line_flush(false);
+ // Draw the 'showcmd' information if 'showcmdloc' == "tabline".
+ if (p_sc && *p_sloc == 't') {
+ const int sc_width = MIN(10, (int)Columns - col - (tabcount > 1) * 3);
- // 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 (sc_width > 0) {
+ grid_puts_len(&default_grid, showcmd_buf, sc_width, 0,
+ Columns - sc_width - (tabcount > 1) * 2, attr_nosel);
+ }
+ }
- if (click_defs == NULL) {
- goto theend;
+ // Put an "X" for closing the current tab if there are several.
+ if (tabcount > 1) {
+ grid_putchar(&default_grid, 'X', 0, Columns - 1, attr_nosel);
+ tab_page_click_defs[Columns - 1] = (StlClickDefinition) {
+ .type = kStlClickTabClose,
+ .tabnr = 999,
+ .func = NULL,
+ };
+ }
}
- 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;
- }
- 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;
- }
+ // Reset the flag here again, in case evaluating 'tabline' causes it to be
+ // set.
+ redraw_tabline = false;
+}
+
+/// Build the 'statuscolumn' string for line "lnum". When "relnum" == -1,
+/// the v:lnum and v:relnum variables don't have to be updated.
+///
+/// @param hlrec HL attributes (can be NULL)
+/// @param stcp Status column attributes (can be NULL)
+/// @return The width of the built status column string for line "lnum"
+int build_statuscol_str(win_T *wp, linenr_T lnum, long relnum, int maxwidth, int fillchar,
+ char *buf, stl_hlrec_t **hlrec, statuscol_T *stcp)
+{
+ bool fillclick = relnum >= 0 && lnum == wp->w_topline;
+
+ if (relnum >= 0) {
+ set_vim_var_nr(VV_LNUM, lnum);
+ set_vim_var_nr(VV_RELNUM, relnum);
}
- while (col < maxwidth) {
- click_defs[col++] = cur_click_def;
+
+ StlClickRecord *clickrec;
+ char *stc = xstrdup(wp->w_p_stc);
+ int width = build_stl_str_hl(wp, buf, MAXPATHL, stc, "statuscolumn", OPT_LOCAL, fillchar,
+ maxwidth, hlrec, fillclick ? &clickrec : NULL, stcp);
+ xfree(stc);
+
+ // Only update click definitions once per window per redraw
+ if (fillclick) {
+ stl_clear_click_defs(wp->w_statuscol_click_defs, wp->w_statuscol_click_defs_size);
+ wp->w_statuscol_click_defs = stl_alloc_click_defs(wp->w_statuscol_click_defs, width,
+ &wp->w_statuscol_click_defs_size);
+ stl_fill_click_defs(wp->w_statuscol_click_defs, clickrec, buf, width, false);
}
-theend:
- entered = false;
+ return width;
}
/// Build a string from the status line items in "fmt".
@@ -646,15 +933,18 @@ theend:
/// 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 opt_name The option name corresponding to "fmt"
+/// @param opt_scope The scope corresponding to "opt_name"
/// @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 tabtab Tab clicks definition (can be NULL)
+/// @param stcp Status column attributes (can be NULL)
///
/// @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)
+int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_name, int opt_scope,
+ int fillchar, int maxwidth, stl_hlrec_t **hltab, StlClickRecord **tabtab,
+ statuscol_T *stcp)
{
static size_t stl_items_len = 20; // Initial value, grows as needed.
static stl_item_t *stl_items = NULL;
@@ -669,6 +959,11 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san
char *usefmt = fmt;
const int save_must_redraw = must_redraw;
const int save_redr_type = curwin->w_redr_type;
+ const bool save_KeyTyped = KeyTyped;
+ // TODO(Bram): find out why using called_emsg_before makes tests fail, does it
+ // matter?
+ // const int called_emsg_before = called_emsg;
+ const int did_emsg_before = did_emsg;
if (stl_items == NULL) {
stl_items = xmalloc(sizeof(stl_item_t) * stl_items_len);
@@ -682,6 +977,11 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san
stl_separator_locations = xmalloc(sizeof(int) * stl_items_len);
}
+ // if "fmt" was set insecurely it needs to be evaluated in the sandbox
+ // "opt_name" will be NULL when caller is nvim_eval_statusline()
+ const int use_sandbox = opt_name ? was_set_insecurely(wp, opt_name, opt_scope)
+ : false;
+
// When the format starts with "%!" then evaluate it as an expression and
// use the result as the actual format string.
if (fmt[0] == '%' && fmt[1] == '!') {
@@ -712,13 +1012,13 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san
}
// Get line & check if empty (cursorpos will show "0-1").
- const char *line_ptr = (char *)ml_get_buf(wp->w_buffer, lnum, false);
+ const char *line_ptr = 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
// efficient than making a copy of the line.
int byteval;
- const size_t len = STRLEN(line_ptr);
+ const size_t len = strlen(line_ptr);
if (wp->w_cursor.col > (colnr_T)len) {
// Line may have changed since checking the cursor column, or the lnum
// was adjusted above.
@@ -829,7 +1129,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san
// so `vim_strsize` will work.
char *t = stl_items[stl_groupitems[groupdepth]].start;
*out_p = NUL;
- long group_len = vim_strsize(t);
+ ptrdiff_t group_len = vim_strsize(t);
// If the group contained internal items
// and the group did not have a minimum width,
@@ -915,7 +1215,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san
}
// If the group is shorter than the minimum width, add padding characters.
} else if (abs(stl_items[stl_groupitems[groupdepth]].minwid) > group_len) {
- long min_group_width = stl_items[stl_groupitems[groupdepth]].minwid;
+ ptrdiff_t 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;
@@ -929,7 +1229,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san
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);
+ group_len = out_end_p - out_p;
}
out_p += group_len;
// }
@@ -1037,7 +1337,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san
}
stl_items[curitem].type = ClickFunc;
stl_items[curitem].start = out_p;
- stl_items[curitem].cmd = xmemdupz(t, (size_t)(fmt_p - t));
+ stl_items[curitem].cmd = tabtab ? xmemdupz(t, (size_t)(fmt_p - t)) : NULL;
stl_items[curitem].minwid = minwid;
fmt_p++;
curitem++;
@@ -1078,7 +1378,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san
// An invalid item was specified.
// Continue processing on the next character of the format string.
- if (vim_strchr(STL_ALL, *fmt_p) == NULL) {
+ if (vim_strchr(STL_ALL, (uint8_t)(*fmt_p)) == NULL) {
fmt_p++;
continue;
}
@@ -1100,17 +1400,17 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san
// get replaced with the fillchar
fillable = false;
if (buf_spname(wp->w_buffer) != NULL) {
- STRLCPY(NameBuff, buf_spname(wp->w_buffer), MAXPATHL);
+ xstrlcpy(NameBuff, buf_spname(wp->w_buffer), MAXPATHL);
} else {
char *t = (opt == STL_FULLPATH) ? wp->w_buffer->b_ffname
: wp->w_buffer->b_fname;
- home_replace(wp->w_buffer, t, (char *)NameBuff, MAXPATHL, true);
+ home_replace(wp->w_buffer, t, NameBuff, MAXPATHL, true);
}
- trans_characters((char *)NameBuff, MAXPATHL);
+ trans_characters(NameBuff, MAXPATHL);
if (opt != STL_FILENAME) {
- str = (char *)NameBuff;
+ str = NameBuff;
} else {
- str = path_tail((char *)NameBuff);
+ str = path_tail(NameBuff);
}
break;
case STL_VIM_EXPR: // '{'
@@ -1191,8 +1491,8 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san
&& strchr((const char *)str, '%') != NULL
&& evaldepth < MAX_STL_EVAL_DEPTH) {
size_t parsed_usefmt = (size_t)(block_start - usefmt);
- size_t str_length = STRLEN(str);
- size_t fmt_length = STRLEN(fmt_p);
+ size_t str_length = strlen(str);
+ size_t fmt_length = strlen(fmt_p);
size_t new_fmt_len = parsed_usefmt + str_length + fmt_length + 3;
char *new_fmt = xmalloc(new_fmt_len * sizeof(char));
char *new_fmt_p = new_fmt;
@@ -1217,8 +1517,14 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san
}
case STL_LINE:
- num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY)
- ? 0L : (long)(wp->w_cursor.lnum);
+ // Overload %l with v:lnum for 'statuscolumn'
+ if (opt_name != NULL && strcmp(opt_name, "statuscolumn") == 0) {
+ if (wp->w_p_nu && !get_vim_var_nr(VV_VIRTNUM)) {
+ num = get_vim_var_nr(VV_LNUM);
+ }
+ } else {
+ num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) ? 0L : (long)(wp->w_cursor.lnum);
+ }
break;
case STL_NUMLINES:
@@ -1255,6 +1561,12 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san
str = buf_tmp;
break;
+ case STL_SHOWCMD:
+ if (p_sc && (opt_name == NULL || strcmp(opt_name, p_sloc) == 0)) {
+ str = showcmd_buf;
+ }
+ break;
+
case STL_ARGLISTSTAT:
fillable = false;
@@ -1278,7 +1590,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san
}
break;
case STL_PAGENUM:
- num = printer_page_num;
+ num = 0;
break;
case STL_BUFNO:
@@ -1310,9 +1622,16 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san
case STL_ROFLAG:
case STL_ROFLAG_ALT:
- itemisflag = true;
- if (wp->w_buffer->b_p_ro) {
- str = (opt == STL_ROFLAG_ALT) ? ",RO" : _("[RO]");
+ // Overload %r with v:relnum for 'statuscolumn'
+ if (opt_name != NULL && strcmp(opt_name, "statuscolumn") == 0) {
+ if (wp->w_p_rnu && !get_vim_var_nr(VV_VIRTNUM)) {
+ num = get_vim_var_nr(VV_RELNUM);
+ }
+ } else {
+ itemisflag = true;
+ if (wp->w_buffer->b_p_ro) {
+ str = (opt == STL_ROFLAG_ALT) ? ",RO" : _("[RO]");
+ }
}
break;
@@ -1324,12 +1643,39 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san
}
break;
+ case STL_FOLDCOL: // 'C' for 'statuscolumn'
+ case STL_SIGNCOL: { // 's' for 'statuscolumn'
+ if (stcp == NULL) {
+ break;
+ }
+
+ bool fold = opt == STL_FOLDCOL;
+ *buf_tmp = NUL;
+ for (int i = 0; i <= SIGN_SHOW_MAX; i++) {
+ char *p = fold ? stcp->fold_text : stcp->sign_text[i];
+ if ((!p || !*p) && *buf_tmp == NUL) {
+ break;
+ }
+ stl_items[curitem].type = Highlight;
+ stl_items[curitem].start = out_p + strlen(buf_tmp);
+ stl_items[curitem].minwid = !p || (fold && i) ? 0 : fold ? stcp->fold_attr
+ : stcp->sign_attr[i];
+ curitem++;
+ if (!p || (fold && i)) {
+ str = buf_tmp;
+ break;
+ }
+ STRCAT(buf_tmp, p);
+ }
+ break;
+ }
+
case STL_FILETYPE:
// Copy the filetype if it is not null and the formatted string will fit
// in the temporary buffer
// (including the brackets and null terminating character)
if (*wp->w_buffer->b_p_ft != NUL
- && STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 3) {
+ && strlen(wp->w_buffer->b_p_ft) < TMPLEN - 3) {
vim_snprintf(buf_tmp, sizeof(buf_tmp), "[%s]",
wp->w_buffer->b_p_ft);
str = buf_tmp;
@@ -1342,11 +1688,11 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san
// in the temporary buffer
// (including the comma and null terminating character)
if (*wp->w_buffer->b_p_ft != NUL
- && STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 2) {
+ && strlen(wp->w_buffer->b_p_ft) < TMPLEN - 2) {
vim_snprintf(buf_tmp, sizeof(buf_tmp), ",%s", wp->w_buffer->b_p_ft);
// Uppercase the file extension
for (char *t = buf_tmp; *t != 0; t++) {
- *t = (char)TOUPPER_LOC(*t);
+ *t = (char)TOUPPER_LOC((uint8_t)(*t));
}
str = buf_tmp;
}
@@ -1562,7 +1908,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san
// Advance the output buffer position to the end of the
// number we just printed
- out_p += STRLEN(out_p);
+ out_p += strlen(out_p);
// Otherwise, there was nothing to print so mark the item as empty
} else {
@@ -1595,6 +1941,10 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san
// What follows is post-processing to handle alignment and highlighting.
int width = vim_strsize(out);
+ // Return truncated width for 'statuscolumn'
+ if (stcp != NULL && width > maxwidth) {
+ stcp->truncate = width - maxwidth;
+ }
if (maxwidth > 0 && width > maxwidth) {
// Result is too long, must truncate somewhere.
int item_idx = 0;
@@ -1642,6 +1992,11 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san
// the truncation point
for (int i = 0; i < itemcnt; i++) {
if (stl_items[i].start > trunc_p) {
+ for (int j = i; j < itemcnt; j++) {
+ if (stl_items[j].type == ClickFunc) {
+ XFREE_CLEAR(stl_items[j].cmd);
+ }
+ }
itemcnt = i;
break;
}
@@ -1670,7 +2025,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san
if (width + 1 < maxwidth) {
// Advance the pointer to the end of the string
- trunc_p = trunc_p + STRLEN(trunc_p);
+ trunc_p = trunc_p + strlen(trunc_p);
}
// Fill up for half a double-wide character.
@@ -1706,7 +2061,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san
// add characters at the separate marker (if there is one) to
// fill up the available space.
} else if (width < maxwidth
- && STRLEN(out) + (size_t)(maxwidth - width) + 1 < outlen) {
+ && strlen(out) + (size_t)(maxwidth - width) + 1 < outlen) {
// Find how many separators there are, which we will use when
// figuring out how many groups there are.
int num_separators = 0;
@@ -1804,5 +2159,18 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san
curwin->w_redr_type = save_redr_type;
}
+ // Check for an error. If there is one the display will be messed up and
+ // might loop redrawing. Avoid that by making the corresponding option
+ // empty.
+ // TODO(Bram): find out why using called_emsg_before makes tests fail, does it
+ // matter?
+ // if (called_emsg > called_emsg_before)
+ if (opt_name && did_emsg > did_emsg_before) {
+ set_string_option_direct(opt_name, -1, "", OPT_FREE | opt_scope, SID_ERROR);
+ }
+
+ // A user function may reset KeyTyped, restore it.
+ KeyTyped = save_KeyTyped;
+
return width;
}
diff --git a/src/nvim/statusline.h b/src/nvim/statusline.h
index 357a9a821f..f7e36f138c 100644
--- a/src/nvim/statusline.h
+++ b/src/nvim/statusline.h
@@ -1,7 +1,16 @@
#ifndef NVIM_STATUSLINE_H
#define NVIM_STATUSLINE_H
+#include <stddef.h>
+
#include "nvim/buffer_defs.h"
+#include "nvim/macros.h"
+#include "nvim/statusline_defs.h"
+
+/// Array defining what should be done when tabline is clicked
+EXTERN StlClickDefinition *tab_page_click_defs INIT(= NULL);
+/// Size of the tab_page_click_defs array
+EXTERN size_t tab_page_click_defs_size INIT(= 0);
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "statusline.h.generated.h"
diff --git a/src/nvim/statusline_defs.h b/src/nvim/statusline_defs.h
new file mode 100644
index 0000000000..eac9dfd690
--- /dev/null
+++ b/src/nvim/statusline_defs.h
@@ -0,0 +1,26 @@
+#ifndef NVIM_STATUSLINE_DEFS_H
+#define NVIM_STATUSLINE_DEFS_H
+
+#include <stddef.h>
+
+#include "nvim/macros.h"
+
+/// 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_STATUSLINE_DEFS_H
diff --git a/src/nvim/strings.c b/src/nvim/strings.c
index 78312c738c..34b3c38103 100644
--- a/src/nvim/strings.c
+++ b/src/nvim/strings.c
@@ -6,121 +6,80 @@
#include <math.h>
#include <stdarg.h>
#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
+#include "auto/config.h"
#include "nvim/ascii.h"
#include "nvim/assert.h"
-#include "nvim/buffer.h"
#include "nvim/charset.h"
-#include "nvim/diff.h"
-#include "nvim/edit.h"
-#include "nvim/eval.h"
#include "nvim/eval/encode.h"
-#include "nvim/ex_cmds.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.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/func_attr.h"
-#include "nvim/getchar.h"
-#include "nvim/mark.h"
+#include "nvim/gettext.h"
+#include "nvim/macros.h"
#include "nvim/math.h"
#include "nvim/mbyte.h"
-#include "nvim/memfile.h"
-#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/move.h"
-#include "nvim/ops.h"
#include "nvim/option.h"
-#include "nvim/os/os.h"
-#include "nvim/os/shell.h"
-#include "nvim/os_unix.h"
-#include "nvim/path.h"
-#include "nvim/quickfix.h"
-#include "nvim/regexp.h"
-#include "nvim/screen.h"
-#include "nvim/search.h"
-#include "nvim/spell.h"
#include "nvim/strings.h"
-#include "nvim/syntax.h"
-#include "nvim/tag.h"
+#include "nvim/types.h"
#include "nvim/vim.h"
-#include "nvim/window.h"
-
-/// Copy "string" into newly allocated memory.
-char_u *vim_strsave(const char_u *string)
- FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
-{
- return (char_u *)xstrdup((char *)string);
-}
/// Copy up to `len` bytes of `string` into newly allocated memory and
/// terminate with a NUL. The allocated memory always has size `len + 1`, even
/// when `string` is shorter.
-char_u *vim_strnsave(const char_u *string, size_t len)
- FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
-{
- // strncpy is intentional: some parts of Vim use `string` shorter than `len`
- // and expect the remainder to be zeroed out.
- 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.
- */
-char_u *vim_strsave_escaped(const char_u *string, const char_u *esc_chars)
+// Same as vim_strsave(), but any characters found in esc_chars are preceded
+// by a backslash.
+char *vim_strsave_escaped(const char *string, const char *esc_chars)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
{
return vim_strsave_escaped_ext(string, esc_chars, '\\', false);
}
-/*
- * Same as vim_strsave_escaped(), but when "bsl" is true also escape
- * characters where rem_backslash() would remove the backslash.
- * Escape the characters with "cc".
- */
-char_u *vim_strsave_escaped_ext(const char_u *string, const char_u *esc_chars, char_u cc, bool bsl)
+// Same as vim_strsave_escaped(), but when "bsl" is true also escape
+// characters where rem_backslash() would remove the backslash.
+// Escape the characters with "cc".
+char *vim_strsave_escaped_ext(const char *string, const char *esc_chars, char cc, bool bsl)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
{
- /*
- * First count the number of backslashes required.
- * Then allocate the memory and insert them.
- */
+ // First count the number of backslashes required.
+ // Then allocate the memory and insert them.
size_t length = 1; // count the trailing NUL
- for (const char_u *p = string; *p; p++) {
- const size_t l = (size_t)(utfc_ptr2len((char *)p));
+ for (const char *p = string; *p; p++) {
+ const size_t l = (size_t)(utfc_ptr2len(p));
if (l > 1) {
length += l; // count a multibyte char
p += l - 1;
continue;
}
- if (vim_strchr((char *)esc_chars, *p) != NULL || (bsl && rem_backslash(p))) {
+ if (vim_strchr(esc_chars, (uint8_t)(*p)) != NULL || (bsl && rem_backslash(p))) {
length++; // count a backslash
}
- ++length; // count an ordinary char
+ length++; // count an ordinary char
}
- 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((char *)p));
+ char *escaped_string = xmalloc(length);
+ char *p2 = escaped_string;
+ for (const char *p = string; *p; p++) {
+ const size_t l = (size_t)(utfc_ptr2len(p));
if (l > 1) {
memcpy(p2, p, l);
p2 += l;
p += l - 1; // skip multibyte char
continue;
}
- if (vim_strchr((char *)esc_chars, *p) != NULL || (bsl && rem_backslash(p))) {
+ if (vim_strchr(esc_chars, (uint8_t)(*p)) != NULL || (bsl && rem_backslash(p))) {
*p2++ = cc;
}
*p2++ = *p;
@@ -178,29 +137,28 @@ char *vim_strnsave_unquoted(const char *const string, const size_t length)
return ret;
}
-/*
- * Escape "string" for use as a shell argument with system().
- * This uses single quotes, except when we know we need to use double quotes
- * (MS-Windows without 'shellslash' set).
- * Escape a newline, depending on the 'shell' option.
- * When "do_special" is true also replace "!", "%", "#" and things starting
- * with "<" like "<cfile>".
- * When "do_newline" is false do not escape newline unless it is csh shell.
- * Returns the result in allocated memory.
- */
-char_u *vim_strsave_shellescape(const char_u *string, bool do_special, bool do_newline)
+/// Escape "string" for use as a shell argument with system().
+/// This uses single quotes, except when we know we need to use double quotes
+/// (MS-Windows without 'shellslash' set).
+/// Escape a newline, depending on the 'shell' option.
+/// When "do_special" is true also replace "!", "%", "#" and things starting
+/// with "<" like "<cfile>".
+/// When "do_newline" is false do not escape newline unless it is csh shell.
+///
+/// @return the result in allocated memory.
+char *vim_strsave_shellescape(const char *string, bool do_special, bool do_newline)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
{
char *d;
- char_u *escaped_string;
+ char *escaped_string;
size_t l;
int csh_like;
bool fish_like;
- /* Only csh and similar shells expand '!' within single quotes. For sh and
- * the like we must not put a backslash before it, it will be taken
- * literally. If do_special is set the '!' will be escaped twice.
- * Csh also needs to have "\n" escaped twice when do_special is set. */
+ // Only csh and similar shells expand '!' within single quotes. For sh and
+ // the like we must not put a backslash before it, it will be taken
+ // literally. If do_special is set the '!' will be escaped twice.
+ // Csh also needs to have "\n" escaped twice when do_special is set.
csh_like = csh_like_shell();
// Fish shell uses '\' as an escape character within single quotes, so '\'
@@ -208,9 +166,9 @@ char_u *vim_strsave_shellescape(const char_u *string, bool do_special, bool do_n
fish_like = fish_like_shell();
// First count the number of extra bytes required.
- size_t length = STRLEN(string) + 3; // two quotes and a trailing NUL
- for (const char_u *p = string; *p != NUL; MB_PTR_ADV(p)) {
-#ifdef WIN32
+ size_t length = strlen(string) + 3; // two quotes and a trailing NUL
+ for (const char *p = string; *p != NUL; MB_PTR_ADV(p)) {
+#ifdef MSWIN
if (!p_ssl) {
if (*p == '"') {
length++; // " -> ""
@@ -222,13 +180,13 @@ char_u *vim_strsave_shellescape(const char_u *string, bool do_special, bool do_n
}
if ((*p == '\n' && (csh_like || do_newline))
|| (*p == '!' && (csh_like || do_special))) {
- ++length; // insert backslash
+ length++; // insert backslash
if (csh_like && do_special) {
- ++length; // insert backslash
+ length++; // insert backslash
}
}
if (do_special && find_cmdline_var(p, &l) >= 0) {
- ++length; // insert backslash
+ length++; // insert backslash
p += l - 1;
}
if (*p == '\\' && fish_like) {
@@ -238,18 +196,18 @@ char_u *vim_strsave_shellescape(const char_u *string, bool do_special, bool do_n
// Allocate memory for the result and fill it.
escaped_string = xmalloc(length);
- d = (char *)escaped_string;
+ d = escaped_string;
// add opening quote
-#ifdef WIN32
+#ifdef MSWIN
if (!p_ssl) {
*d++ = '"';
} else
#endif
*d++ = '\'';
- for (const char *p = (char *)string; *p != NUL;) {
-#ifdef WIN32
+ for (const char *p = string; *p != NUL;) {
+#ifdef MSWIN
if (!p_ssl) {
if (*p == '"') {
*d++ = '"';
@@ -276,7 +234,7 @@ char_u *vim_strsave_shellescape(const char_u *string, bool do_special, bool do_n
*d++ = *p++;
continue;
}
- if (do_special && find_cmdline_var((char_u *)p, &l) >= 0) {
+ if (do_special && find_cmdline_var(p, &l) >= 0) {
*d++ = '\\'; // insert backslash
while (--l != SIZE_MAX) { // copy the var
*d++ = *p++;
@@ -293,7 +251,7 @@ char_u *vim_strsave_shellescape(const char_u *string, bool do_special, bool do_n
}
// add terminating quote and finish with a NUL
-#ifdef WIN32
+#ifdef MSWIN
if (!p_ssl) {
*d++ = '"';
} else
@@ -304,41 +262,35 @@ char_u *vim_strsave_shellescape(const char_u *string, bool do_special, bool do_n
return escaped_string;
}
-/*
- * Like vim_strsave(), but make all characters uppercase.
- * This uses ASCII lower-to-upper case translation, language independent.
- */
-char_u *vim_strsave_up(const char_u *string)
+// Like vim_strsave(), but make all characters uppercase.
+// This uses ASCII lower-to-upper case translation, language independent.
+char *vim_strsave_up(const char *string)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
{
- char_u *p1;
+ char *p1;
- p1 = vim_strsave(string);
+ p1 = xstrdup(string);
vim_strup(p1);
return p1;
}
-/*
- * Like vim_strnsave(), but make all characters uppercase.
- * This uses ASCII lower-to-upper case translation, language independent.
- */
-char_u *vim_strnsave_up(const char_u *string, size_t len)
+/// Like xstrnsave(), but make all characters uppercase.
+/// This uses ASCII lower-to-upper case translation, language independent.
+char *vim_strnsave_up(const char *string, size_t len)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
{
- char_u *p1 = vim_strnsave(string, len);
+ char *p1 = xstrnsave(string, len);
vim_strup(p1);
return p1;
}
-/*
- * ASCII lower-to-upper case translation, language independent.
- */
-void vim_strup(char_u *p)
+// ASCII lower-to-upper case translation, language independent.
+void vim_strup(char *p)
FUNC_ATTR_NONNULL_ALL
{
- char_u c;
- while ((c = *p) != NUL) {
- *p++ = (char_u)(c < 'a' || c > 'z' ? c : c - 0x20);
+ uint8_t c;
+ while ((c = (uint8_t)(*p)) != NUL) {
+ *p++ = (char)(uint8_t)(c < 'a' || c > 'z' ? c : c - 0x20);
}
}
@@ -361,7 +313,7 @@ char *strcase_save(const char *const orig, bool upper)
int l = utf_ptr2len(p);
if (c == 0) {
// overlong sequence, use only the first byte
- c = (char_u)(*p);
+ c = (uint8_t)(*p);
l = 1;
}
int uc = upper ? mb_toupper(c) : mb_tolower(c);
@@ -371,7 +323,7 @@ char *strcase_save(const char *const orig, bool upper)
int newl = utf_char2len(uc);
if (newl != l) {
// TODO(philix): use xrealloc() in strcase_save()
- char *s = xmalloc(STRLEN(res) + (size_t)(1 + newl - l));
+ char *s = xmalloc(strlen(res) + (size_t)(1 + newl - l));
memcpy(s, res, (size_t)(p - res));
STRCPY(s + (p - res) + newl, p + l);
p = s + (p - res);
@@ -386,15 +338,13 @@ char *strcase_save(const char *const orig, bool upper)
return res;
}
-/*
- * delete spaces at the end of a string
- */
-void del_trailing_spaces(char_u *ptr)
+// delete spaces at the end of a string
+void del_trailing_spaces(char *ptr)
FUNC_ATTR_NONNULL_ALL
{
- char_u *q;
+ char *q;
- q = ptr + STRLEN(ptr);
+ q = ptr + strlen(ptr);
while (--q > ptr && ascii_iswhite(q[0]) && q[-1] != '\\' && q[-1] != Ctrl_V) {
*q = NUL;
}
@@ -413,18 +363,16 @@ size_t xstrnlen(const char *s, size_t n)
#endif
#if (!defined(HAVE_STRCASECMP) && !defined(HAVE_STRICMP))
-/*
- * Compare two strings, ignoring case, using current locale.
- * Doesn't work for multi-byte characters.
- * return 0 for match, < 0 for smaller, > 0 for bigger
- */
+// Compare two strings, ignoring case, using current locale.
+// Doesn't work for multi-byte characters.
+// return 0 for match, < 0 for smaller, > 0 for bigger
int vim_stricmp(const char *s1, const char *s2)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE
{
int i;
for (;;) {
- i = (int)TOLOWER_LOC(*s1) - (int)TOLOWER_LOC(*s2);
+ i = (int)TOLOWER_LOC((uint8_t)(*s1)) - (int)TOLOWER_LOC((uint8_t)(*s2));
if (i != 0) {
return i; // this character different
}
@@ -439,18 +387,16 @@ int vim_stricmp(const char *s1, const char *s2)
#endif
#if (!defined(HAVE_STRNCASECMP) && !defined(HAVE_STRNICMP))
-/*
- * Compare two strings, for length "len", ignoring case, using current locale.
- * Doesn't work for multi-byte characters.
- * return 0 for match, < 0 for smaller, > 0 for bigger
- */
+// Compare two strings, for length "len", ignoring case, using current locale.
+// Doesn't work for multi-byte characters.
+// return 0 for match, < 0 for smaller, > 0 for bigger
int vim_strnicmp(const char *s1, const char *s2, size_t len)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE
{
int i;
while (len > 0) {
- i = (int)TOLOWER_LOC(*s1) - (int)TOLOWER_LOC(*s2);
+ i = (int)TOLOWER_LOC((uint8_t)(*s1)) - (int)TOLOWER_LOC((uint8_t)(*s2));
if (i != 0) {
return i; // this character different
}
@@ -488,9 +434,7 @@ char *vim_strchr(const char *const string, const int c)
}
}
-/*
- * Sort an array of strings.
- */
+// Sort an array of strings.
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "strings.c.generated.h"
@@ -498,26 +442,24 @@ char *vim_strchr(const char *const string, const int c)
static int sort_compare(const void *s1, const void *s2)
FUNC_ATTR_NONNULL_ALL
{
- return STRCMP(*(char **)s1, *(char **)s2);
+ return strcmp(*(char **)s1, *(char **)s2);
}
void sort_strings(char **files, int count)
{
- qsort((void *)files, (size_t)count, sizeof(char_u *), sort_compare);
+ qsort((void *)files, (size_t)count, sizeof(char *), sort_compare);
}
-/*
- * Return true if string "s" contains a non-ASCII character (128 or higher).
- * When "s" is NULL false is returned.
- */
-bool has_non_ascii(const char_u *s)
+// Return true if string "s" contains a non-ASCII character (128 or higher).
+// When "s" is NULL false is returned.
+bool has_non_ascii(const char *s)
FUNC_ATTR_PURE
{
- const char_u *p;
+ const char *p;
if (s != NULL) {
- for (p = s; *p != NUL; ++p) {
- if (*p >= 128) {
+ for (p = s; *p != NUL; p++) {
+ if ((uint8_t)(*p) >= 128) {
return true;
}
}
@@ -540,14 +482,12 @@ bool has_non_ascii_len(const char *const s, const size_t len)
return false;
}
-/*
- * Concatenate two strings and return the result in allocated memory.
- */
-char_u *concat_str(const char_u *restrict str1, const char_u *restrict str2)
+/// Concatenate two strings and return the result in allocated memory.
+char *concat_str(const char *restrict str1, const char *restrict str2)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
{
- size_t l = STRLEN(str1);
- char_u *dest = xmalloc(l + STRLEN(str2) + 1);
+ size_t l = strlen(str1);
+ char *dest = xmalloc(l + strlen(str2) + 1);
STRCPY(dest, str1);
STRCPY(dest + l, str2);
return dest;
@@ -646,10 +586,9 @@ static const void *tv_ptr(const typval_T *const tvs, int *const idxp)
if (tvs[idx].v_type == VAR_UNKNOWN) {
emsg(_(e_printf));
return NULL;
- } else {
- (*idxp)++;
- return tvs[idx].vval.v_string;
}
+ (*idxp)++;
+ return tvs[idx].vval.v_string;
}
/// Get float argument from idxp entry in tvs
@@ -1007,18 +946,18 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
- str_arg);
}
if (fmt_spec == 'S') {
- char_u *p1;
+ char *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);
+ for (i = 0, p1 = (char *)str_arg; *p1; p1 += utfc_ptr2len(p1)) {
+ size_t cell = (size_t)utf_ptr2cells(p1);
if (precision_specified && i + cell > precision) {
break;
}
i += cell;
}
- str_arg_l = (size_t)(p1 - (char_u *)str_arg);
+ str_arg_l = (size_t)(p1 - str_arg);
if (min_field_width != 0) {
min_field_width += str_arg_l - i;
}
@@ -1077,13 +1016,11 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
: va_arg(ap, long long)); // NOLINT (runtime/int)
break;
case 'z':
- arg = (tvs
- ? (ptrdiff_t)tv_nr(tvs, &arg_idx)
- : va_arg(ap, ptrdiff_t));
+ arg = (tvs ? (ptrdiff_t)tv_nr(tvs, &arg_idx) : va_arg(ap, ptrdiff_t));
break;
}
if (arg > 0) {
- arg_sign = 1;
+ arg_sign = 1;
} else if (arg < 0) {
arg_sign = -1;
}
@@ -1091,19 +1028,13 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
// unsigned
switch (length_modifier) {
case '\0':
- uarg = (unsigned int)(tvs
- ? tv_nr(tvs, &arg_idx)
- : va_arg(ap, unsigned int));
+ uarg = (unsigned int)(tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, unsigned int));
break;
case 'h':
- uarg = (uint16_t)(tvs
- ? tv_nr(tvs, &arg_idx)
- : va_arg(ap, unsigned int));
+ uarg = (uint16_t)(tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, unsigned int));
break;
case 'l':
- uarg = (tvs
- ? (unsigned long)tv_nr(tvs, &arg_idx)
- : va_arg(ap, unsigned long));
+ uarg = (tvs ? (unsigned long)tv_nr(tvs, &arg_idx) : va_arg(ap, unsigned long));
break;
case '2':
uarg = (uintmax_t)(unsigned long long)( // NOLINT (runtime/int)
@@ -1113,9 +1044,7 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
: va_arg(ap, unsigned long long)); // NOLINT (runtime/int)
break;
case 'z':
- uarg = (tvs
- ? (size_t)tv_nr(tvs, &arg_idx)
- : va_arg(ap, size_t));
+ uarg = (tvs ? (size_t)tv_nr(tvs, &arg_idx) : va_arg(ap, size_t));
break;
}
arg_sign = (uarg != 0);
@@ -1511,15 +1440,15 @@ int kv_do_printf(StringBuilder *str, const char *fmt, ...)
/// Reverse text into allocated memory.
///
/// @return the allocated string.
-char_u *reverse_text(char_u *s)
+char *reverse_text(char *s)
FUNC_ATTR_NONNULL_RET
{
// Reverse the pattern.
- size_t len = STRLEN(s);
- char_u *rev = xmalloc(len + 1);
+ size_t len = strlen(s);
+ char *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);
+ const int mb_len = utfc_ptr2len(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;
@@ -1540,7 +1469,7 @@ char_u *reverse_text(char_u *s)
char *strrep(const char *src, const char *what, const char *rep)
{
char *pos = (char *)src;
- size_t whatlen = STRLEN(what);
+ size_t whatlen = strlen(what);
// Count occurrences
size_t count = 0;
@@ -1553,8 +1482,8 @@ char *strrep(const char *src, const char *what, const char *rep)
return NULL;
}
- size_t replen = STRLEN(rep);
- char *ret = xmalloc(STRLEN(src) + count * (replen - whatlen) + 1);
+ size_t replen = strlen(rep);
+ char *ret = xmalloc(strlen(src) + count * (replen - whatlen) + 1);
char *ptr = ret;
while ((pos = strstr(src, what)) != NULL) {
size_t idx = (size_t)(pos - src);
diff --git a/src/nvim/strings.h b/src/nvim/strings.h
index 9ef1eb5816..6ad9daf5bf 100644
--- a/src/nvim/strings.h
+++ b/src/nvim/strings.h
@@ -5,8 +5,8 @@
#include <stdbool.h>
#include <string.h>
+#include "klib/kvec.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
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 2a3ec56451..05c570e52f 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -1,56 +1,49 @@
// 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
-/*
- * syntax.c: code for syntax highlighting
- */
+// syntax.c: code for syntax highlighting
#include <assert.h>
-#include <ctype.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
-#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
#include "nvim/autocmd.h"
#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
-#include "nvim/cursor_shape.h"
#include "nvim/drawscreen.h"
#include "nvim/eval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/eval/vars.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
-#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
#include "nvim/hashtab.h"
-#include "nvim/highlight.h"
+#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/indent_c.h"
-#include "nvim/keycodes.h"
-#include "nvim/lua/executor.h"
#include "nvim/macros.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/option.h"
+#include "nvim/option_defs.h"
#include "nvim/optionstr.h"
#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/pos.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
-#include "nvim/sign.h"
+#include "nvim/runtime.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
-#include "nvim/syntax_defs.h"
-#include "nvim/terminal.h"
-#include "nvim/ui.h"
+#include "nvim/types.h"
#include "nvim/vim.h"
static bool did_syntax_onoff = false;
@@ -93,22 +86,20 @@ typedef struct syn_pattern {
int16_t *sp_cont_list; // cont. group IDs, if non-zero
int16_t *sp_next_list; // next group IDs, if non-zero
struct sp_syn sp_syn; // struct passed to in_id_list()
- char_u *sp_pattern; // regexp to match, pattern
+ char *sp_pattern; // regexp to match, pattern
regprog_T *sp_prog; // regexp to match, program
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
+ char *scl_name; // syntax cluster name
+ char *scl_name_u; // uppercase of scl_name
int16_t *scl_list; // IDs in this syntax cluster
} syn_cluster_T;
-/*
- * For the current state we need to remember more than just the idx.
- * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
- * (The end positions have the column number of the next char)
- */
+// For the current state we need to remember more than just the idx.
+// When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
+// (The end positions have the column number of the next char)
typedef struct state_item {
int si_idx; // index of syntax pattern or
// KEYWORD_IDX
@@ -133,10 +124,8 @@ typedef struct state_item {
// pattern
} stateitem_T;
-/*
- * Struct to reduce the number of arguments to get_syn_options(), it's used
- * very often.
- */
+// Struct to reduce the number of arguments to get_syn_options(), it's used
+// very often.
typedef struct {
int flags; // flags for contained and transparent
bool keyword; // true for ":syn keyword"
@@ -155,7 +144,7 @@ typedef struct {
proftime_T slowest;
proftime_T average;
int id;
- char_u *pattern;
+ char *pattern;
} time_entry_T;
struct name_list {
@@ -184,9 +173,7 @@ static char *(spo_name_tab[SPO_COUNT]) =
#define NONE_IDX (-2) // value of sp_sync_idx for "NONE"
-/*
- * Flags for b_syn_sync_flags:
- */
+// Flags for b_syn_sync_flags:
#define SF_CCOMMENT 0x01 // sync on a C-style comment
#define SF_MATCH 0x02 // sync by matching a pattern
@@ -194,9 +181,7 @@ static char *(spo_name_tab[SPO_COUNT]) =
#define MAXKEYWLEN 80 // maximum length of a keyword
-/*
- * The attributes of the syntax item that has been recognized.
- */
+// The attributes of the syntax item that has been recognized.
static int current_attr = 0; // attr of current syntax word
static int current_id = 0; // ID of current char for syn_get_id()
static int current_trans_id = 0; // idem, transparency removed
@@ -204,23 +189,19 @@ static int current_flags = 0;
static int current_seqnr = 0;
static int current_sub_char = 0;
-/*
- * Methods of combining two clusters
- */
+// Methods of combining two clusters
#define CLUSTER_REPLACE 1 // replace first list with second
#define CLUSTER_ADD 2 // add second list to first
#define CLUSTER_SUBTRACT 3 // subtract second list from first
#define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
-/*
- * Syntax group IDs have different types:
- * 0 - 19999 normal syntax groups
- * 20000 - 20999 ALLBUT indicator (current_syn_inc_tag added)
- * 21000 - 21999 TOP indicator (current_syn_inc_tag added)
- * 22000 - 22999 CONTAINED indicator (current_syn_inc_tag added)
- * 23000 - 32767 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
- */
+// Syntax group IDs have different types:
+// 0 - 19999 normal syntax groups
+// 20000 - 20999 ALLBUT indicator (current_syn_inc_tag added)
+// 21000 - 21999 TOP indicator (current_syn_inc_tag added)
+// 22000 - 22999 CONTAINED indicator (current_syn_inc_tag added)
+// 23000 - 32767 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
#define SYNID_ALLBUT MAX_HL_ID // syntax group ID for contains=ALLBUT
#define SYNID_TOP 21000 // syntax group ID for contains=TOP
#define SYNID_CONTAINED 22000 // syntax group ID for contains=CONTAINED
@@ -234,33 +215,27 @@ static int current_sub_char = 0;
// instead of passing it to them, we stow it here.
static char **syn_cmdlinep;
-/*
- * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
- * files from leaking into ALLBUT lists, we assign a unique ID to the
- * rules in each ":syn include"'d file.
- */
+// Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
+// files from leaking into ALLBUT lists, we assign a unique ID to the
+// rules in each ":syn include"'d file.
static int current_syn_inc_tag = 0;
static int running_syn_inc_tag = 0;
-/*
- * In a hashtable item "hi_key" points to "keyword" in a keyentry.
- * This avoids adding a pointer to the hashtable item.
- * KE2HIKEY() converts a var pointer to a hashitem key pointer.
- * HIKEY2KE() converts a hashitem key pointer to a var pointer.
- * HI2KE() converts a hashitem pointer to a var pointer.
- */
+// In a hashtable item "hi_key" points to "keyword" in a keyentry.
+// This avoids adding a pointer to the hashtable item.
+// KE2HIKEY() converts a var pointer to a hashitem key pointer.
+// HIKEY2KE() converts a hashitem key pointer to a var pointer.
+// HI2KE() converts a hashitem pointer to a var pointer.
static keyentry_T dumkey;
#define KE2HIKEY(kp) ((kp)->keyword)
-#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
+#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char *)&dumkey)))
#define HI2KE(hi) HIKEY2KE((hi)->hi_key)
// -V:HI2KE:782
-/*
- * To reduce the time spent in keepend(), remember at which level in the state
- * stack the first item with "keepend" is present. When "-1", there is no
- * "keepend" on the stack.
- */
+// To reduce the time spent in keepend(), remember at which level in the state
+// stack the first item with "keepend" is present. When "-1", there is no
+// "keepend" on the stack.
static int keepend_level = -1;
static char msg_no_items[] = N_("No Syntax items defined for this buffer");
@@ -272,13 +247,11 @@ static char msg_no_items[] = N_("No Syntax items defined for this buffer");
static int next_seqnr = 1; // value to use for si_seqnr
-/*
- * The next possible match in the current line for any pattern is remembered,
- * to avoid having to try for a match in each column.
- * If next_match_idx == -1, not tried (in this line) yet.
- * If next_match_col == MAXCOL, no match found in this line.
- * (All end positions have the column of the char after the end)
- */
+// The next possible match in the current line for any pattern is remembered,
+// to avoid having to try for a match in each column.
+// If next_match_idx == -1, not tried (in this line) yet.
+// If next_match_col == MAXCOL, no match found in this line.
+// (All end positions have the column of the char after the end)
static int next_match_col; // column for start of next match
static lpos_T next_match_m_endpos; // position for end of next match
static lpos_T next_match_h_startpos; // pos. for highl. start of next match
@@ -290,17 +263,13 @@ static lpos_T next_match_eoe_pos; // pos. for end of end pattern
static int next_match_end_idx; // ID of group for end pattn or zero
static reg_extmatch_T *next_match_extmatch = NULL;
-/*
- * A state stack is an array of integers or stateitem_T, stored in a
- * garray_T. A state stack is invalid if its itemsize entry is zero.
- */
+// A state stack is an array of integers or stateitem_T, stored in a
+// garray_T. A state stack is invalid if its itemsize entry is zero.
#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
-/*
- * The current state (within the line) of the recognition engine.
- * When current_state.ga_itemsize is 0 the current state is invalid.
- */
+// The current state (within the line) of the recognition engine.
+// When current_state.ga_itemsize is 0 the current state is invalid.
static win_T *syn_win; // current window for highlighting
static buf_T *syn_buf; // current buffer for highlighting
static synblock_T *syn_block; // current buffer for highlighting
@@ -328,13 +297,11 @@ void syn_set_timeout(proftime_T *tm)
syn_tm = tm;
}
-/*
- * Start the syntax recognition for a line. This function is normally called
- * from the screen updating, once for each displayed line.
- * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
- * it. Careful: curbuf and curwin are likely to point to another buffer and
- * window.
- */
+// Start the syntax recognition for a line. This function is normally called
+// from the screen updating, once for each displayed line.
+// The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
+// it. Careful: curbuf and curwin are likely to point to another buffer and
+// window.
void syntax_start(win_T *wp, linenr_T lnum)
{
synstate_T *p;
@@ -348,11 +315,9 @@ void syntax_start(win_T *wp, linenr_T lnum)
current_sub_char = NUL;
- /*
- * After switching buffers, invalidate current_state.
- * Also do this when a change was made, the current state may be invalid
- * then.
- */
+ // After switching buffers, invalidate current_state.
+ // Also do this when a change was made, the current state may be invalid
+ // then.
if (syn_block != wp->w_s
|| syn_buf != wp->w_buffer
|| changedtick != buf_get_changedtick(syn_buf)) {
@@ -363,18 +328,14 @@ void syntax_start(win_T *wp, linenr_T lnum)
changedtick = buf_get_changedtick(syn_buf);
syn_win = wp;
- /*
- * Allocate syntax stack when needed.
- */
+ // Allocate syntax stack when needed.
syn_stack_alloc();
if (syn_block->b_sst_array == NULL) {
return; // out of memory
}
syn_block->b_sst_lasttick = display_tick;
- /*
- * If the state of the end of the previous line is useful, store it.
- */
+ // If the state of the end of the previous line is useful, store it.
if (VALID_STATE(&current_state)
&& current_lnum < lnum
&& current_lnum < syn_buf->b_ml.ml_line_count) {
@@ -384,11 +345,9 @@ void syntax_start(win_T *wp, linenr_T lnum)
(void)store_current_state();
}
- /*
- * If the current_lnum is now the same as "lnum", keep the current
- * state (this happens very often!). Otherwise invalidate
- * current_state and figure it out below.
- */
+ // If the current_lnum is now the same as "lnum", keep the current
+ // state (this happens very often!). Otherwise invalidate
+ // current_state and figure it out below.
if (current_lnum != lnum) {
invalidate_current_state();
}
@@ -396,10 +355,8 @@ void syntax_start(win_T *wp, linenr_T lnum)
invalidate_current_state();
}
- /*
- * Try to synchronize from a saved state in b_sst_array[].
- * Only do this if lnum is not before and not to far beyond a saved state.
- */
+ // Try to synchronize from a saved state in b_sst_array[].
+ // Only do this if lnum is not before and not to far beyond a saved state.
if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL) {
// Find last valid saved state before start_lnum.
for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next) {
@@ -418,10 +375,8 @@ void syntax_start(win_T *wp, linenr_T lnum)
}
}
- /*
- * If "lnum" is before or far beyond a line with a saved state, need to
- * re-synchronize.
- */
+ // If "lnum" is before or far beyond a line with a saved state, need to
+ // re-synchronize.
if (INVALID_STATE(&current_state)) {
syn_sync(wp, lnum, last_valid);
if (current_lnum == 1) {
@@ -436,10 +391,8 @@ void syntax_start(win_T *wp, linenr_T lnum)
first_stored = current_lnum;
}
- /*
- * Advance from the sync point or saved state until the current line.
- * Save some entries for syncing with later on.
- */
+ // Advance from the sync point or saved state until the current line.
+ // Save some entries for syncing with later on.
if (syn_block->b_sst_len <= Rows) {
dist = 999999;
} else {
@@ -506,10 +459,8 @@ void syntax_start(win_T *wp, linenr_T lnum)
syn_start_line();
}
-/*
- * We cannot simply discard growarrays full of state_items or buf_states; we
- * have to manually release their extmatch pointers first.
- */
+// We cannot simply discard growarrays full of state_items or buf_states; we
+// have to manually release their extmatch pointers first.
static void clear_syn_state(synstate_T *p)
{
if (p->sst_stacksize > SST_FIX_STATES) {
@@ -522,24 +473,20 @@ static void clear_syn_state(synstate_T *p)
}
}
-/*
- * Cleanup the current_state stack.
- */
+// Cleanup the current_state stack.
static void clear_current_state(void)
{
#define UNREF_STATEITEM_EXTMATCH(si) unref_extmatch((si)->si_extmatch)
GA_DEEP_CLEAR(&current_state, stateitem_T, UNREF_STATEITEM_EXTMATCH);
}
-/*
- * Try to find a synchronisation point for line "lnum".
- *
- * This sets current_lnum and the current state. One of three methods is
- * used:
- * 1. Search backwards for the end of a C-comment.
- * 2. Search backwards for given sync patterns.
- * 3. Simply start on a given number of lines above "lnum".
- */
+// Try to find a synchronisation point for line "lnum".
+//
+// This sets current_lnum and the current state. One of three methods is
+// used:
+// 1. Search backwards for the end of a C-comment.
+// 2. Search backwards for given sync patterns.
+// 3. Simply start on a given number of lines above "lnum".
static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid)
{
buf_T *curbuf_save;
@@ -552,7 +499,7 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid)
bool had_sync_point;
stateitem_T *cur_si;
synpat_T *spp;
- char_u *line;
+ char *line;
int found_flags = 0;
int found_match_idx = 0;
linenr_T found_current_lnum = 0;
@@ -560,19 +507,15 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid)
lpos_T found_m_endpos;
colnr_T prev_current_col;
- /*
- * Clear any current state that might be hanging around.
- */
+ // Clear any current state that might be hanging around.
invalidate_current_state();
- /*
- * Start at least "minlines" back. Default starting point for parsing is
- * there.
- * Start further back, to avoid that scrolling backwards will result in
- * resyncing for every line. Now it resyncs only one out of N lines,
- * where N is minlines * 1.5, or minlines * 2 if minlines is small.
- * Watch out for overflow when minlines is MAXLNUM.
- */
+ // Start at least "minlines" back. Default starting point for parsing is
+ // there.
+ // Start further back, to avoid that scrolling backwards will result in
+ // resyncing for every line. Now it resyncs only one out of N lines,
+ // where N is minlines * 1.5, or minlines * 2 if minlines is small.
+ // Watch out for overflow when minlines is MAXLNUM.
if (syn_block->b_syn_sync_minlines > start_lnum) {
start_lnum = 1;
} else {
@@ -595,9 +538,7 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid)
}
current_lnum = start_lnum;
- /*
- * 1. Search backwards for the end of a C-style comment.
- */
+ // 1. Search backwards for the end of a C-style comment.
if (syn_block->b_syn_sync_flags & SF_CCOMMENT) {
// Need to make syn_buf the current buffer for a moment, to be able to
// use find_start_comment().
@@ -606,12 +547,10 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid)
curbuf_save = curbuf;
curbuf = syn_buf;
- /*
- * Skip lines that end in a backslash.
- */
- for (; start_lnum > 1; --start_lnum) {
+ // Skip lines that end in a backslash.
+ for (; start_lnum > 1; start_lnum--) {
line = ml_get(start_lnum - 1);
- if (*line == NUL || *(line + STRLEN(line) - 1) != '\\') {
+ if (*line == NUL || *(line + strlen(line) - 1) != '\\') {
break;
}
}
@@ -622,11 +561,9 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid)
wp->w_cursor.lnum = start_lnum;
wp->w_cursor.col = 0;
- /*
- * If the line is inside a comment, need to find the syntax item that
- * defines the comment.
- * Restrict the search for the end of a comment to b_syn_sync_maxlines.
- */
+ // If the line is inside a comment, need to find the syntax item that
+ // defines the comment.
+ // Restrict the search for the end of a comment to b_syn_sync_maxlines.
if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL) {
for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0;) {
if (SYN_ITEMS(syn_block)[idx].sp_syn.id
@@ -644,11 +581,8 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid)
wp->w_cursor = cursor_save;
curwin = curwin_save;
curbuf = curbuf_save;
- }
- /*
- * 2. Search backwards for given sync patterns.
- */
- else if (syn_block->b_syn_sync_flags & SF_MATCH) {
+ } else if (syn_block->b_syn_sync_flags & SF_MATCH) {
+ // 2. Search backwards for given sync patterns.
if (syn_block->b_syn_sync_maxlines != 0
&& start_lnum > syn_block->b_syn_sync_maxlines) {
break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
@@ -675,19 +609,15 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid)
break;
}
- /*
- * Check if the previous line has the line-continuation pattern.
- */
+ // Check if the previous line has the line-continuation pattern.
if (lnum > 1 && syn_match_linecont(lnum - 1)) {
continue;
}
- /*
- * Start with nothing on the state stack
- */
+ // Start with nothing on the state stack
validate_current_state();
- for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum) {
+ for (current_lnum = lnum; current_lnum < end_lnum; current_lnum++) {
syn_start_line();
for (;;) {
had_sync_point = syn_finish_line(true);
@@ -712,10 +642,8 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid)
found_current_lnum = current_lnum;
found_current_col = current_col;
found_m_endpos = cur_si->si_m_endpos;
- /*
- * Continue after the match (be aware of a zero-length
- * match).
- */
+ // Continue after the match (be aware of a zero-length
+ // match).
if (found_m_endpos.lnum > current_lnum) {
current_lnum = found_m_endpos.lnum;
current_col = found_m_endpos.col;
@@ -743,27 +671,21 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid)
}
}
- /*
- * If a sync point was encountered, break here.
- */
+ // If a sync point was encountered, break here.
if (found_flags) {
- /*
- * Put the item that was specified by the sync point on the
- * state stack. If there was no item specified, make the
- * state stack empty.
- */
+ // Put the item that was specified by the sync point on the
+ // state stack. If there was no item specified, make the
+ // state stack empty.
clear_current_state();
if (found_match_idx >= 0) {
push_current_state(found_match_idx);
update_si_attr(current_state.ga_len - 1);
}
- /*
- * When using "grouphere", continue from the sync point
- * match, until the end of the line. Parsing starts at
- * the next line.
- * For "groupthere" the parsing starts at start_lnum.
- */
+ // When using "grouphere", continue from the sync point
+ // match, until the end of the line. Parsing starts at
+ // the next line.
+ // For "groupthere" the parsing starts at start_lnum.
if (found_flags & HL_SYNC_HERE) {
if (!GA_EMPTY(&current_state)) {
cur_si = &CUR_STATE(current_state.ga_len - 1);
@@ -797,56 +719,53 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid)
validate_current_state();
}
-static void save_chartab(char_u *chartab)
+static void save_chartab(char *chartab)
{
- if (syn_block->b_syn_isk != empty_option) {
- memmove(chartab, syn_buf->b_chartab, (size_t)32);
- memmove(syn_buf->b_chartab, syn_win->w_s->b_syn_chartab, (size_t)32);
+ if (syn_block->b_syn_isk == empty_option) {
+ return;
}
+
+ memmove(chartab, syn_buf->b_chartab, (size_t)32);
+ memmove(syn_buf->b_chartab, syn_win->w_s->b_syn_chartab, (size_t)32);
}
-static void restore_chartab(char_u *chartab)
+static void restore_chartab(char *chartab)
{
if (syn_win->w_s->b_syn_isk != empty_option) {
memmove(syn_buf->b_chartab, chartab, (size_t)32);
}
}
-/*
- * Return TRUE if the line-continuation pattern matches in line "lnum".
- */
+/// Return true if the line-continuation pattern matches in line "lnum".
static int syn_match_linecont(linenr_T lnum)
{
- if (syn_block->b_syn_linecont_prog != NULL) {
- regmmatch_T regmatch;
- // chartab array for syn iskeyword
- char_u buf_chartab[32];
- save_chartab(buf_chartab);
-
- regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
- regmatch.regprog = syn_block->b_syn_linecont_prog;
- int r = syn_regexec(&regmatch, lnum, (colnr_T)0,
- IF_SYN_TIME(&syn_block->b_syn_linecont_time));
- syn_block->b_syn_linecont_prog = regmatch.regprog;
-
- restore_chartab(buf_chartab);
- return r;
- }
- return FALSE;
+ if (syn_block->b_syn_linecont_prog == NULL) {
+ return false;
+ }
+
+ regmmatch_T regmatch;
+ // chartab array for syn iskeyword
+ char buf_chartab[32];
+ save_chartab(buf_chartab);
+
+ regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
+ regmatch.regprog = syn_block->b_syn_linecont_prog;
+ int r = syn_regexec(&regmatch, lnum, (colnr_T)0,
+ IF_SYN_TIME(&syn_block->b_syn_linecont_time));
+ syn_block->b_syn_linecont_prog = regmatch.regprog;
+
+ restore_chartab(buf_chartab);
+ return r;
}
-/*
- * Prepare the current state for the start of a line.
- */
+// Prepare the current state for the start of a line.
static void syn_start_line(void)
{
current_finished = false;
current_col = 0;
- /*
- * Need to update the end of a start/skip/end that continues from the
- * previous line and regions that have "keepend".
- */
+ // Need to update the end of a start/skip/end that continues from the
+ // previous line and regions that have "keepend".
if (!GA_EMPTY(&current_state)) {
syn_update_ends(true);
check_state_ends();
@@ -878,23 +797,21 @@ static void syn_update_ends(bool startofline)
cur_si->si_m_endpos.lnum = 0;
cur_si->si_m_endpos.col = 0;
cur_si->si_h_endpos = cur_si->si_m_endpos;
- cur_si->si_ends = TRUE;
+ cur_si->si_ends = true;
}
}
}
- /*
- * Need to update the end of a start/skip/end that continues from the
- * previous line. And regions that have "keepend", because they may
- * influence contained items. If we've just removed "extend"
- * (startofline == 0) then we should update ends of normal regions
- * contained inside "keepend" because "extend" could have extended
- * these "keepend" regions as well as contained normal regions.
- * Then check for items ending in column 0.
- */
+ // Need to update the end of a start/skip/end that continues from the
+ // previous line. And regions that have "keepend", because they may
+ // influence contained items. If we've just removed "extend"
+ // (startofline == 0) then we should update ends of normal regions
+ // contained inside "keepend" because "extend" could have extended
+ // these "keepend" regions as well as contained normal regions.
+ // Then check for items ending in column 0.
int i = current_state.ga_len - 1;
if (keepend_level >= 0) {
- for (; i > keepend_level; --i) {
+ for (; i > keepend_level; i--) {
if (CUR_STATE(i).si_flags & HL_EXTEND) {
break;
}
@@ -925,55 +842,53 @@ static void syn_update_ends(bool startofline)
/////////////////////////////////////////
// Handling of the state stack cache.
-/*
- * EXPLANATION OF THE SYNTAX STATE STACK CACHE
- *
- * To speed up syntax highlighting, the state stack for the start of some
- * lines is cached. These entries can be used to start parsing at that point.
- *
- * The stack is kept in b_sst_array[] for each buffer. There is a list of
- * valid entries. b_sst_first points to the first one, then follow sst_next.
- * The entries are sorted on line number. The first entry is often for line 2
- * (line 1 always starts with an empty stack).
- * There is also a list for free entries. This construction is used to avoid
- * having to allocate and free memory blocks too often.
- *
- * When making changes to the buffer, this is logged in b_mod_*. When calling
- * update_screen() to update the display, it will call
- * syn_stack_apply_changes() for each displayed buffer to adjust the cached
- * entries. The entries which are inside the changed area are removed,
- * because they must be recomputed. Entries below the changed have their line
- * number adjusted for deleted/inserted lines, and have their sst_change_lnum
- * set to indicate that a check must be made if the changed lines would change
- * the cached entry.
- *
- * When later displaying lines, an entry is stored for each line. Displayed
- * lines are likely to be displayed again, in which case the state at the
- * start of the line is needed.
- * For not displayed lines, an entry is stored for every so many lines. These
- * entries will be used e.g., when scrolling backwards. The distance between
- * entries depends on the number of lines in the buffer. For small buffers
- * the distance is fixed at SST_DIST, for large buffers there is a fixed
- * number of entries SST_MAX_ENTRIES, and the distance is computed.
- */
+// EXPLANATION OF THE SYNTAX STATE STACK CACHE
+//
+// To speed up syntax highlighting, the state stack for the start of some
+// lines is cached. These entries can be used to start parsing at that point.
+//
+// The stack is kept in b_sst_array[] for each buffer. There is a list of
+// valid entries. b_sst_first points to the first one, then follow sst_next.
+// The entries are sorted on line number. The first entry is often for line 2
+// (line 1 always starts with an empty stack).
+// There is also a list for free entries. This construction is used to avoid
+// having to allocate and free memory blocks too often.
+//
+// When making changes to the buffer, this is logged in b_mod_*. When calling
+// update_screen() to update the display, it will call
+// syn_stack_apply_changes() for each displayed buffer to adjust the cached
+// entries. The entries which are inside the changed area are removed,
+// because they must be recomputed. Entries below the changed have their line
+// number adjusted for deleted/inserted lines, and have their sst_change_lnum
+// set to indicate that a check must be made if the changed lines would change
+// the cached entry.
+//
+// When later displaying lines, an entry is stored for each line. Displayed
+// lines are likely to be displayed again, in which case the state at the
+// start of the line is needed.
+// For not displayed lines, an entry is stored for every so many lines. These
+// entries will be used e.g., when scrolling backwards. The distance between
+// entries depends on the number of lines in the buffer. For small buffers
+// the distance is fixed at SST_DIST, for large buffers there is a fixed
+// number of entries SST_MAX_ENTRIES, and the distance is computed.
static void syn_stack_free_block(synblock_T *block)
{
synstate_T *p;
- if (block->b_sst_array != NULL) {
- for (p = block->b_sst_first; p != NULL; p = p->sst_next) {
- clear_syn_state(p);
- }
- XFREE_CLEAR(block->b_sst_array);
- block->b_sst_first = NULL;
- block->b_sst_len = 0;
+ if (block->b_sst_array == NULL) {
+ return;
+ }
+
+ for (p = block->b_sst_first; p != NULL; p = p->sst_next) {
+ clear_syn_state(p);
}
+ XFREE_CLEAR(block->b_sst_array);
+ block->b_sst_first = NULL;
+ block->b_sst_len = 0;
}
-/*
- * Free b_sst_array[] for buffer "buf".
- * Used when syntax items changed to force resyncing everywhere.
- */
+// Free b_sst_array[] for buffer "buf".
+// Used when syntax items changed to force resyncing everywhere.
void syn_stack_free_all(synblock_T *block)
{
syn_stack_free_block(block);
@@ -986,12 +901,10 @@ void syn_stack_free_all(synblock_T *block)
}
}
-/*
- * Allocate the syntax state stack for syn_buf when needed.
- * If the number of entries in b_sst_array[] is much too big or a bit too
- * small, reallocate it.
- * Also used to allocate b_sst_array[] for the first time.
- */
+// Allocate the syntax state stack for syn_buf when needed.
+// If the number of entries in b_sst_array[] is much too big or a bit too
+// small, reallocate it.
+// Also used to allocate b_sst_array[] for the first time.
static void syn_stack_alloc(void)
{
synstate_T *to, *from;
@@ -1058,12 +971,10 @@ static void syn_stack_alloc(void)
}
}
-/*
- * Check for changes in a buffer to affect stored syntax states. Uses the
- * b_mod_* fields.
- * Called from update_screen(), before screen is being updated, once for each
- * displayed buffer.
- */
+// Check for changes in a buffer to affect stored syntax states. Uses the
+// b_mod_* fields.
+// Called from update_screen(), before screen is being updated, once for each
+// displayed buffer.
void syn_stack_apply_changes(buf_T *buf)
{
syn_stack_apply_changes_block(&buf->b_s, buf);
@@ -1139,11 +1050,9 @@ static bool syn_stack_cleanup(void)
dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
}
- /*
- * Go through the list to find the "tick" for the oldest entry that can
- * be removed. Set "above" when the "tick" for the oldest entry is above
- * "b_sst_lasttick" (the display tick wraps around).
- */
+ // Go through the list to find the "tick" for the oldest entry that can
+ // be removed. Set "above" when the "tick" for the oldest entry is above
+ // "b_sst_lasttick" (the display tick wraps around).
tick = syn_block->b_sst_lasttick;
bool above = false;
prev = syn_block->b_sst_first;
@@ -1160,10 +1069,8 @@ static bool syn_stack_cleanup(void)
}
}
- /*
- * Go through the list to make the entries for the oldest tick at an
- * interval of several lines.
- */
+ // Go through the list to make the entries for the oldest tick at an
+ // interval of several lines.
prev = syn_block->b_sst_first;
for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next) {
if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum) {
@@ -1177,22 +1084,18 @@ static bool syn_stack_cleanup(void)
return retval;
}
-/*
- * Free the allocated memory for a syn_state item.
- * Move the entry into the free list.
- */
+// Free the allocated memory for a syn_state item.
+// Move the entry into the free list.
static void syn_stack_free_entry(synblock_T *block, synstate_T *p)
{
clear_syn_state(p);
p->sst_next = block->b_sst_firstfree;
block->b_sst_firstfree = p;
- ++block->b_sst_freecount;
+ block->b_sst_freecount++;
}
-/*
- * Find an entry in the list of state stacks at or before "lnum".
- * Returns NULL when there is no entry or the first entry is after "lnum".
- */
+// Find an entry in the list of state stacks at or before "lnum".
+// Returns NULL when there is no entry or the first entry is after "lnum".
static synstate_T *syn_stack_find_entry(linenr_T lnum)
{
synstate_T *p, *prev;
@@ -1209,10 +1112,8 @@ static synstate_T *syn_stack_find_entry(linenr_T lnum)
return prev;
}
-/*
- * Try saving the current state in b_sst_array[].
- * The current state must be valid for the start of the current_lnum line!
- */
+// Try saving the current state in b_sst_array[].
+// The current state must be valid for the start of the current_lnum line!
static synstate_T *store_current_state(void)
{
int i;
@@ -1221,11 +1122,9 @@ static synstate_T *store_current_state(void)
stateitem_T *cur_si;
synstate_T *sp = syn_stack_find_entry(current_lnum);
- /*
- * If the current state contains a start or end pattern that continues
- * from the previous line, we can't use it. Don't store it then.
- */
- for (i = current_state.ga_len - 1; i >= 0; --i) {
+ // If the current state contains a start or end pattern that continues
+ // from the previous line, we can't use it. Don't store it then.
+ for (i = current_state.ga_len - 1; i >= 0; i--) {
cur_si = &CUR_STATE(i);
if (cur_si->si_h_startpos.lnum >= current_lnum
|| cur_si->si_m_endpos.lnum >= current_lnum
@@ -1256,9 +1155,7 @@ static synstate_T *store_current_state(void)
sp = NULL;
}
} else if (sp == NULL || sp->sst_lnum != current_lnum) {
- /*
- * Add a new entry
- */
+ // Add a new entry
// If no free items, cleanup the array first.
if (syn_block->b_sst_freecount == 0) {
(void)syn_stack_cleanup();
@@ -1273,7 +1170,7 @@ static synstate_T *store_current_state(void)
// list, after *sp
p = syn_block->b_sst_firstfree;
syn_block->b_sst_firstfree = p->sst_next;
- --syn_block->b_sst_freecount;
+ syn_block->b_sst_freecount--;
if (sp == NULL) {
// Insert in front of the list
p->sst_next = syn_block->b_sst_first;
@@ -1302,7 +1199,7 @@ static synstate_T *store_current_state(void)
} else {
bp = sp->sst_union.sst_stack;
}
- for (i = 0; i < sp->sst_stacksize; ++i) {
+ for (i = 0; i < sp->sst_stacksize; i++) {
bp[i].bs_idx = CUR_STATE(i).si_idx;
bp[i].bs_flags = (int)CUR_STATE(i).si_flags;
bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
@@ -1318,9 +1215,7 @@ static synstate_T *store_current_state(void)
return sp;
}
-/*
- * Copy a state stack from "from" in b_sst_array[] to current_state;
- */
+// Copy a state stack from "from" in b_sst_array[] to current_state;
static void load_current_state(synstate_T *from)
{
int i;
@@ -1336,7 +1231,7 @@ static void load_current_state(synstate_T *from)
} else {
bp = from->sst_union.sst_stack;
}
- for (i = 0; i < from->sst_stacksize; ++i) {
+ for (i = 0; i < from->sst_stacksize; i++) {
CUR_STATE(i).si_idx = bp[i].bs_idx;
CUR_STATE(i).si_flags = bp[i].bs_flags;
CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
@@ -1345,7 +1240,7 @@ static void load_current_state(synstate_T *from)
if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND)) {
keepend_level = i;
}
- CUR_STATE(i).si_ends = FALSE;
+ CUR_STATE(i).si_ends = false;
CUR_STATE(i).si_m_lnum = 0;
if (CUR_STATE(i).si_idx >= 0) {
CUR_STATE(i).si_next_list =
@@ -1424,20 +1319,21 @@ static bool syn_stack_equal(synstate_T *sp)
return i < 0 ? true : false;
}
-/*
- * We stop parsing syntax above line "lnum". If the stored state at or below
- * this line depended on a change before it, it now depends on the line below
- * the last parsed line.
- * The window looks like this:
- * line which changed
- * displayed line
- * displayed line
- * lnum -> line below window
- */
-void syntax_end_parsing(linenr_T lnum)
+// We stop parsing syntax above line "lnum". If the stored state at or below
+// this line depended on a change before it, it now depends on the line below
+// the last parsed line.
+// The window looks like this:
+// line which changed
+// displayed line
+// displayed line
+// lnum -> line below window
+void syntax_end_parsing(win_T *wp, linenr_T lnum)
{
synstate_T *sp;
+ if (syn_block != wp->w_s) {
+ return; // not the right window
+ }
sp = syn_stack_find_entry(lnum);
if (sp != NULL && sp->sst_lnum < lnum) {
sp = sp->sst_next;
@@ -1448,9 +1344,8 @@ void syntax_end_parsing(linenr_T lnum)
}
}
-/*
- * End of handling of the state stack.
- ****************************************/
+// End of handling of the state stack.
+// **************************************
static void invalidate_current_state(void)
{
@@ -1475,33 +1370,25 @@ bool syntax_check_changed(linenr_T lnum)
bool retval = true;
synstate_T *sp;
- /*
- * Check the state stack when:
- * - lnum is just below the previously syntaxed line.
- * - lnum is not before the lines with saved states.
- * - lnum is not past the lines with saved states.
- * - lnum is at or before the last changed line.
- */
+ // Check the state stack when:
+ // - lnum is just below the previously syntaxed line.
+ // - lnum is not before the lines with saved states.
+ // - lnum is not past the lines with saved states.
+ // - lnum is at or before the last changed line.
if (VALID_STATE(&current_state) && lnum == current_lnum + 1) {
sp = syn_stack_find_entry(lnum);
if (sp != NULL && sp->sst_lnum == lnum) {
- /*
- * finish the previous line (needed when not all of the line was
- * drawn)
- */
+ // finish the previous line (needed when not all of the line was
+ // drawn)
(void)syn_finish_line(false);
- /*
- * Compare the current state with the previously saved state of
- * the line.
- */
+ // Compare the current state with the previously saved state of
+ // the line.
if (syn_stack_equal(sp)) {
retval = false;
}
- /*
- * Store the current state in b_sst_array[] for later use.
- */
+ // Store the current state in b_sst_array[] for later use.
current_lnum++;
(void)store_current_state();
}
@@ -1551,7 +1438,7 @@ static bool syn_finish_line(const bool syncing)
/// "col" is normally 0 for the first use in a line, and increments by one each
/// time. It's allowed to skip characters and to stop before the end of the
/// line. But only a "col" after a previously used column is allowed.
-/// When "can_spell" is not NULL set it to TRUE when spell-checking should be
+/// When "can_spell" is not NULL set it to true when spell-checking should be
/// done.
///
/// @param keep_state keep state of char at "col"
@@ -1589,9 +1476,7 @@ int get_syntax_attr(const colnr_T col, bool *const can_spell, const bool keep_st
validate_current_state();
}
- /*
- * Skip from the current column to "col", get the attributes for "col".
- */
+ // Skip from the current column to "col", get the attributes for "col".
while (current_col <= col) {
attr = syn_current_attr(false, true, can_spell,
current_col == col ? keep_state : false);
@@ -1610,8 +1495,8 @@ int get_syntax_attr(const colnr_T col, bool *const can_spell, const bool keep_st
static int syn_current_attr(const bool syncing, const bool displaying, bool *const can_spell,
const bool keep_state)
{
- lpos_T endpos; // was: char_u *endp;
- lpos_T hl_startpos; // was: int hl_startcol;
+ lpos_T endpos;
+ lpos_T hl_startpos;
lpos_T hl_endpos;
lpos_T eos_pos; // end-of-start match (start region)
lpos_T eoe_pos; // end-of-end pattern
@@ -1627,8 +1512,8 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
regmmatch_T regmatch;
lpos_T pos;
reg_extmatch_T *cur_extmatch = NULL;
- char_u buf_chartab[32]; // chartab array for syn iskeyword
- char_u *line; // current line. NOTE: becomes invalid after
+ char buf_chartab[32]; // chartab array for syn iskeyword
+ char *line; // current line. NOTE: becomes invalid after
// looking for a pattern match!
// variables for zero-width matches that have a "nextgroup" argument
@@ -1636,15 +1521,11 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
bool zero_width_next_list = false;
garray_T zero_width_next_ga;
- /*
- * No character, no attributes! Past end of line?
- * Do try matching with an empty line (could be the start of a region).
- */
+ // No character, no attributes! Past end of line?
+ // Do try matching with an empty line (could be the start of a region).
line = syn_getcurline();
if (line[current_col] == NUL && current_col != 0) {
- /*
- * If we found a match after the last column, use it.
- */
+ // If we found a match after the last column, use it.
if (next_match_idx >= 0 && next_match_col >= (int)current_col
&& next_match_col != MAXCOL) {
(void)push_next_match();
@@ -1661,11 +1542,9 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
current_state_stored = false;
}
- /*
- * When in the previous column there was a match but it could not be used
- * (empty match or already matched in this column) need to try again in
- * the next column.
- */
+ // When in the previous column there was a match but it could not be used
+ // (empty match or already matched in this column) need to try again in
+ // the next column.
if (try_next_column) {
next_match_idx = -1;
try_next_column = false;
@@ -1683,23 +1562,19 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
// use syntax iskeyword option
save_chartab(buf_chartab);
- /*
- * Repeat matching keywords and patterns, to find contained items at the
- * same column. This stops when there are no extra matches at the current
- * column.
- */
+ // Repeat matching keywords and patterns, to find contained items at the
+ // same column. This stops when there are no extra matches at the current
+ // column.
do {
found_match = false;
keep_next_list = false;
int syn_id = 0;
- /*
- * 1. Check for a current state.
- * Only when there is no current state, or if the current state may
- * contain other things, we need to check for keywords and patterns.
- * Always need to check for contained items if some item has the
- * "containedin" argument (takes extra time!).
- */
+ // 1. Check for a current state.
+ // Only when there is no current state, or if the current state may
+ // contain other things, we need to check for keywords and patterns.
+ // Always need to check for contained items if some item has the
+ // "containedin" argument (takes extra time!).
if (current_state.ga_len) {
cur_si = &CUR_STATE(current_state.ga_len - 1);
} else {
@@ -1708,16 +1583,15 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
if (syn_block->b_syn_containedin || cur_si == NULL
|| cur_si->si_cont_list != NULL) {
- /*
- * 2. Check for keywords, if on a keyword char after a non-keyword
- * char. Don't do this when syncing.
- */
+ // 2. Check for keywords, if on a keyword char after a non-keyword
+ // char. Don't do this when syncing.
if (do_keywords) {
line = syn_getcurline();
- const char_u *cur_pos = line + current_col;
+ const char *cur_pos = line + current_col;
if (vim_iswordp_buf(cur_pos, syn_buf)
&& (current_col == 0
- || !vim_iswordp_buf(cur_pos - 1 - utf_head_off(line, cur_pos - 1),
+ || !vim_iswordp_buf(cur_pos - 1 -
+ utf_head_off(line, cur_pos - 1),
syn_buf))) {
syn_id = check_keyword_id(line, (int)current_col, &endcol, &flags,
&next_list, cur_si, &cchar);
@@ -1732,7 +1606,7 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
cur_si->si_m_endpos.col = endcol;
cur_si->si_h_endpos.lnum = current_lnum;
cur_si->si_h_endpos.col = endcol;
- cur_si->si_ends = TRUE;
+ cur_si->si_ends = true;
cur_si->si_end_idx = 0;
cur_si->si_flags = flags;
cur_si->si_seqnr = next_seqnr++;
@@ -1763,21 +1637,15 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
}
}
- /*
- * 3. Check for patterns (only if no keyword found).
- */
+ // 3. Check for patterns (only if no keyword found).
if (syn_id == 0 && syn_block->b_syn_patterns.ga_len) {
- /*
- * If we didn't check for a match yet, or we are past it, check
- * for any match with a pattern.
- */
+ // If we didn't check for a match yet, or we are past it, check
+ // for any match with a pattern.
if (next_match_idx < 0 || next_match_col < (int)current_col) {
- /*
- * Check all relevant patterns for a match at this
- * position. This is complicated, because matching with a
- * pattern takes quite a bit of time, thus we want to
- * avoid doing it when it's not needed.
- */
+ // Check all relevant patterns for a match at this
+ // position. This is complicated, because matching with a
+ // pattern takes quite a bit of time, thus we want to
+ // avoid doing it when it's not needed.
next_match_idx = 0; // no match in this line yet
next_match_col = MAXCOL;
for (int idx = syn_block->b_syn_patterns.ga_len; --idx >= 0;) {
@@ -1787,13 +1655,12 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
&& (spp->sp_type == SPTYPE_MATCH
|| spp->sp_type == SPTYPE_START)
&& (current_next_list != NULL
- ? in_id_list(NULL, current_next_list,
- &spp->sp_syn, 0)
- : (cur_si == NULL
- ? !(spp->sp_flags & HL_CONTAINED)
- : in_id_list(cur_si,
- cur_si->si_cont_list, &spp->sp_syn,
- spp->sp_flags & HL_CONTAINED)))) {
+ ? in_id_list(NULL, current_next_list, &spp->sp_syn, 0)
+ : (cur_si == NULL
+ ? !(spp->sp_flags & HL_CONTAINED)
+ : in_id_list(cur_si,
+ cur_si->si_cont_list, &spp->sp_syn,
+ spp->sp_flags & HL_CONTAINED)))) {
// If we already tried matching in this line, and
// there isn't a match before next_match_col, skip
// this item.
@@ -1819,9 +1686,7 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
continue;
}
- /*
- * Compute the first column of the match.
- */
+ // Compute the first column of the match.
syn_add_start_off(&pos, &regmatch,
spp, SPO_MS_OFF, -1);
if (pos.lnum > current_lnum) {
@@ -1836,19 +1701,15 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
// matches in the current line
spp->sp_startcol = startcol;
- /*
- * If a previously found match starts at a lower
- * column number, don't use this one.
- */
+ // If a previously found match starts at a lower
+ // column number, don't use this one.
if (startcol >= next_match_col) {
continue;
}
- /*
- * If we matched this pattern at this position
- * before, skip it. Must retry in the next
- * column, because it may match from there.
- */
+ // If we matched this pattern at this position
+ // before, skip it. Must retry in the next
+ // column, because it may match from there.
if (did_match_already(idx, &zero_width_next_ga)) {
try_next_column = true;
continue;
@@ -1866,10 +1727,8 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
syn_add_end_off(&eos_pos, &regmatch,
spp, SPO_RS_OFF, 0);
- /*
- * Grab the external submatches before they get
- * overwritten. Reference count doesn't change.
- */
+ // Grab the external submatches before they get
+ // overwritten. Reference count doesn't change.
unref_extmatch(cur_extmatch);
cur_extmatch = re_extmatch_out;
re_extmatch_out = NULL;
@@ -1880,12 +1739,10 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
end_idx = 0;
hl_endpos.lnum = 0;
- /*
- * For a "oneline" the end must be found in the
- * same line too. Search for it after the end of
- * the match with the start pattern. Set the
- * resulting end positions at the same time.
- */
+ // For a "oneline" the end must be found in the
+ // same line too. Search for it after the end of
+ // the match with the start pattern. Set the
+ // resulting end positions at the same time.
if (spp->sp_type == SPTYPE_START
&& (spp->sp_flags & HL_ONELINE)) {
lpos_T startpos;
@@ -1896,23 +1753,18 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
if (endpos.lnum == 0) {
continue; // not found
}
- }
- /*
- * For a "match" the size must be > 0 after the
- * end offset needs has been added. Except when
- * syncing.
- */
- else if (spp->sp_type == SPTYPE_MATCH) {
+ } else if (spp->sp_type == SPTYPE_MATCH) {
+ // For a "match" the size must be > 0 after the
+ // end offset needs has been added. Except when
+ // syncing.
syn_add_end_off(&hl_endpos, &regmatch, spp,
SPO_HE_OFF, 0);
syn_add_end_off(&endpos, &regmatch, spp,
SPO_ME_OFF, 0);
if (endpos.lnum == current_lnum
&& (int)endpos.col + syncing < startcol) {
- /*
- * If an empty string is matched, may need
- * to try matching again at next column.
- */
+ // If an empty string is matched, may need
+ // to try matching again at next column.
if (regmatch.startpos[0].col == regmatch.endpos[0].col) {
try_next_column = true;
}
@@ -1920,9 +1772,8 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
}
}
- /*
- * keep the best match so far in next_match_*
- */
+ // keep the best match so far in next_match_*
+
// Highlighting must start after startpos and end
// before endpos.
if (hl_startpos.lnum == current_lnum
@@ -1947,9 +1798,7 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
}
}
- /*
- * If we found a match at the current column, use it.
- */
+ // If we found a match at the current column, use it.
if (next_match_idx >= 0 && next_match_col == (int)current_col) {
synpat_T *lspp;
@@ -1977,15 +1826,11 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
}
}
- /*
- * Handle searching for nextgroup match.
- */
+ // Handle searching for nextgroup match.
if (current_next_list != NULL && !keep_next_list) {
- /*
- * If a nextgroup was not found, continue looking for one if:
- * - this is an empty line and the "skipempty" option was given
- * - we are on white space and the "skipwhite" option was given
- */
+ // If a nextgroup was not found, continue looking for one if:
+ // - this is an empty line and the "skipempty" option was given
+ // - we are on white space and the "skipwhite" option was given
if (!found_match) {
line = syn_getcurline();
if (((current_next_flags & HL_SKIPWHITE)
@@ -1996,14 +1841,12 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
}
}
- /*
- * If a nextgroup was found: Use it, and continue looking for
- * contained matches.
- * If a nextgroup was not found: Continue looking for a normal
- * match.
- * When did set current_next_list for a zero-width item and no
- * match was found don't loop (would get stuck).
- */
+ // If a nextgroup was found: Use it, and continue looking for
+ // contained matches.
+ // If a nextgroup was not found: Continue looking for a normal
+ // match.
+ // When did set current_next_list for a zero-width item and no
+ // match was found don't loop (would get stuck).
current_next_list = NULL;
next_match_idx = -1;
if (!zero_width_next_list) {
@@ -2014,17 +1857,15 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
restore_chartab(buf_chartab);
- /*
- * Use attributes from the current state, if within its highlighting.
- * If not, use attributes from the current-but-one state, etc.
- */
+ // Use attributes from the current state, if within its highlighting.
+ // If not, use attributes from the current-but-one state, etc.
current_attr = 0;
current_id = 0;
current_trans_id = 0;
current_flags = 0;
current_seqnr = 0;
if (cur_si != NULL) {
- for (int idx = current_state.ga_len - 1; idx >= 0; --idx) {
+ for (int idx = current_state.ga_len - 1; idx >= 0; idx--) {
sip = &CUR_STATE(idx);
if ((current_lnum > sip->si_h_startpos.lnum
|| (current_lnum == sip->si_h_startpos.lnum
@@ -2046,10 +1887,8 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
if (can_spell != NULL) {
struct sp_syn sps;
- /*
- * set "can_spell" to TRUE if spell checking is supposed to be
- * done in the current item.
- */
+ // set "can_spell" to true if spell checking is supposed to be
+ // done in the current item.
if (syn_block->b_spell_cluster_id == 0) {
// There is no @Spell cluster: Do spelling for items without
// @NoSpell cluster.
@@ -2085,14 +1924,12 @@ 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
- * single character match.
- * First check if the current state ends at the current column. It
- * may be for an empty match and a containing item might end in the
- * current column.
- */
+ // 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
+ // single character match.
+ // First check if the current state ends at the current column. It
+ // may be for an empty match and a containing item might end in the
+ // current column.
if (!syncing && !keep_state) {
check_state_ends();
if (!GA_EMPTY(&current_state)
@@ -2152,9 +1989,7 @@ static bool did_match_already(int idx, garray_T *gap)
return false;
}
-/*
- * Push the next match onto the stack.
- */
+// Push the next match onto the stack.
static stateitem_T *push_next_match(void)
{
stateitem_T *cur_si;
@@ -2163,15 +1998,11 @@ static stateitem_T *push_next_match(void)
spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
- /*
- * Push the item in current_state stack;
- */
+ // Push the item in current_state stack;
push_current_state(next_match_idx);
{
- /*
- * If it's a start-skip-end type that crosses lines, figure out how
- * much it continues in this line. Otherwise just fill in the length.
- */
+ // If it's a start-skip-end type that crosses lines, figure out how
+ // much it continues in this line. Otherwise just fill in the length.
cur_si = &CUR_STATE(current_state.ga_len - 1);
cur_si->si_h_startpos = next_match_h_startpos;
cur_si->si_m_startcol = current_col;
@@ -2192,7 +2023,7 @@ static stateitem_T *push_next_match(void)
} else {
cur_si->si_m_endpos = next_match_m_endpos;
cur_si->si_h_endpos = next_match_h_endpos;
- cur_si->si_ends = TRUE;
+ cur_si->si_ends = true;
cur_si->si_flags |= next_match_flags;
cur_si->si_eoe_pos = next_match_eoe_pos;
cur_si->si_end_idx = next_match_end_idx;
@@ -2204,10 +2035,8 @@ static stateitem_T *push_next_match(void)
update_si_attr(current_state.ga_len - 1);
save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
- /*
- * If the start pattern has another highlight group, push another item
- * on the stack for the start pattern.
- */
+ // If the start pattern has another highlight group, push another item
+ // on the stack for the start pattern.
if (spp->sp_type == SPTYPE_START && spp->sp_syn_match_id != 0) {
push_current_state(next_match_idx);
cur_si = &CUR_STATE(current_state.ga_len - 1);
@@ -2216,7 +2045,7 @@ static stateitem_T *push_next_match(void)
cur_si->si_m_lnum = current_lnum;
cur_si->si_m_endpos = next_match_eos_pos;
cur_si->si_h_endpos = next_match_eos_pos;
- cur_si->si_ends = TRUE;
+ cur_si->si_ends = true;
cur_si->si_end_idx = 0;
cur_si->si_flags = HL_MATCH;
cur_si->si_seqnr = next_seqnr++;
@@ -2235,9 +2064,7 @@ static stateitem_T *push_next_match(void)
return cur_si;
}
-/*
- * Check for end of current state (and the states before it).
- */
+// Check for end of current state (and the states before it).
static void check_state_ends(void)
{
stateitem_T *cur_si;
@@ -2249,12 +2076,10 @@ static void check_state_ends(void)
&& (cur_si->si_m_endpos.lnum < current_lnum
|| (cur_si->si_m_endpos.lnum == current_lnum
&& cur_si->si_m_endpos.col <= current_col))) {
- /*
- * If there is an end pattern group ID, highlight the end pattern
- * now. No need to pop the current item from the stack.
- * Only do this if the end pattern continues beyond the current
- * position.
- */
+ // If there is an end pattern group ID, highlight the end pattern
+ // now. No need to pop the current item from the stack.
+ // Only do this if the end pattern continues beyond the current
+ // position.
if (cur_si->si_end_idx
&& (cur_si->si_eoe_pos.lnum > current_lnum
|| (cur_si->si_eoe_pos.lnum == current_lnum
@@ -2331,10 +2156,8 @@ static void check_state_ends(void)
}
}
-/*
- * Update an entry in the current_state stack for a match or region. This
- * fills in si_attr, si_next_list and si_cont_list.
- */
+// Update an entry in the current_state stack for a match or region. This
+// fills in si_attr, si_next_list and si_cont_list.
static void update_si_attr(int idx)
{
stateitem_T *sip = &CUR_STATE(idx);
@@ -2359,11 +2182,9 @@ static void update_si_attr(int idx)
sip->si_cont_list = spp->sp_cont_list;
}
- /*
- * For transparent items, take attr from outer item.
- * Also take cont_list, if there is none.
- * Don't do this for the matchgroup of a start or end pattern.
- */
+ // For transparent items, take attr from outer item.
+ // Also take cont_list, if there is none.
+ // Don't do this for the matchgroup of a start or end pattern.
if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH)) {
if (idx == 0) {
sip->si_attr = 0;
@@ -2382,10 +2203,8 @@ static void update_si_attr(int idx)
}
}
-/*
- * Check the current stack for patterns with "keepend" flag.
- * Propagate the match-end to contained items, until a "skipend" item is found.
- */
+// Check the current stack for patterns with "keepend" flag.
+// Propagate the match-end to contained items, until a "skipend" item is found.
static void check_keepend(void)
{
int i;
@@ -2393,20 +2212,16 @@ static void check_keepend(void)
lpos_T maxpos_h;
stateitem_T *sip;
- /*
- * This check can consume a lot of time; only do it from the level where
- * there really is a keepend.
- */
+ // This check can consume a lot of time; only do it from the level where
+ // there really is a keepend.
if (keepend_level < 0) {
return;
}
- /*
- * Find the last index of an "extend" item. "keepend" items before that
- * won't do anything. If there is no "extend" item "i" will be
- * "keepend_level" and all "keepend" items will work normally.
- */
- for (i = current_state.ga_len - 1; i > keepend_level; --i) {
+ // Find the last index of an "extend" item. "keepend" items before that
+ // won't do anything. If there is no "extend" item "i" will be
+ // "keepend_level" and all "keepend" items will work normally.
+ for (i = current_state.ga_len - 1; i > keepend_level; i--) {
if (CUR_STATE(i).si_flags & HL_EXTEND) {
break;
}
@@ -2416,13 +2231,13 @@ static void check_keepend(void)
maxpos.col = 0;
maxpos_h.lnum = 0;
maxpos_h.col = 0;
- for (; i < current_state.ga_len; ++i) {
+ for (; i < current_state.ga_len; i++) {
sip = &CUR_STATE(i);
if (maxpos.lnum != 0) {
limit_pos_zero(&sip->si_m_endpos, &maxpos);
limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
limit_pos_zero(&sip->si_eoe_pos, &maxpos);
- sip->si_ends = TRUE;
+ sip->si_ends = true;
}
if (sip->si_ends && (sip->si_flags & HL_KEEPEND)) {
if (maxpos.lnum == 0
@@ -2465,10 +2280,8 @@ static void update_si_end(stateitem_T *sip, int startcol, bool force)
return;
}
- /*
- * We need to find the end of the region. It may continue in the next
- * line.
- */
+ // We need to find the end of the region. It may continue in the next
+ // line.
int end_idx = 0;
lpos_T startpos = {
.lnum = current_lnum,
@@ -2482,12 +2295,12 @@ static void update_si_end(stateitem_T *sip, int startcol, bool force)
// No end pattern matched.
if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE) {
// a "oneline" never continues in the next line
- sip->si_ends = TRUE;
+ sip->si_ends = true;
sip->si_m_endpos.lnum = current_lnum;
- sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
+ sip->si_m_endpos.col = (colnr_T)strlen(syn_getcurline());
} else {
// continues in the next line
- sip->si_ends = FALSE;
+ sip->si_ends = false;
sip->si_m_endpos.lnum = 0;
}
sip->si_h_endpos = sip->si_m_endpos;
@@ -2496,15 +2309,13 @@ static void update_si_end(stateitem_T *sip, int startcol, bool force)
sip->si_m_endpos = endpos;
sip->si_h_endpos = hl_endpos;
sip->si_eoe_pos = end_endpos;
- sip->si_ends = TRUE;
+ sip->si_ends = true;
sip->si_end_idx = end_idx;
}
}
-/*
- * Add a new state to the current state stack.
- * It is cleared and the index set to "idx".
- */
+// Add a new state to the current state stack.
+// It is cleared and the index set to "idx".
static void push_current_state(int idx)
{
stateitem_T *p = GA_APPEND_VIA_PTR(stateitem_T, &current_state);
@@ -2512,14 +2323,12 @@ static void push_current_state(int idx)
p->si_idx = idx;
}
-/*
- * Remove a state from the current_state stack.
- */
+// Remove a state from the current_state stack.
static void pop_current_state(void)
{
if (!GA_EMPTY(&current_state)) {
unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
- --current_state.ga_len;
+ current_state.ga_len--;
}
// after the end of a pattern, try matching a keyword or pattern
next_match_idx = -1;
@@ -2556,29 +2365,25 @@ static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_
regmmatch_T regmatch;
regmmatch_T best_regmatch; // startpos/endpos of best match
lpos_T pos;
- char_u *line;
+ char *line;
bool had_match = false;
- char_u buf_chartab[32]; // chartab array for syn option iskeyword
+ char buf_chartab[32]; // chartab array for syn option iskeyword
// just in case we are invoked for a keyword
if (idx < 0) {
return;
}
- /*
- * Check for being called with a START pattern.
- * Can happen with a match that continues to the next line, because it
- * contained a region.
- */
+ // Check for being called with a START pattern.
+ // Can happen with a match that continues to the next line, because it
+ // contained a region.
spp = &(SYN_ITEMS(syn_block)[idx]);
if (spp->sp_type != SPTYPE_START) {
*hl_endpos = *startpos;
return;
}
- /*
- * Find the SKIP or first END pattern after the last START pattern.
- */
+ // Find the SKIP or first END pattern after the last START pattern.
for (;;) {
spp = &(SYN_ITEMS(syn_block)[idx]);
if (spp->sp_type != SPTYPE_START) {
@@ -2587,9 +2392,7 @@ static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_
idx++;
}
- /*
- * Lookup the SKIP pattern (if present)
- */
+ // Lookup the SKIP pattern (if present)
if (spp->sp_type == SPTYPE_SKIP) {
spp_skip = spp;
idx++;
@@ -2609,11 +2412,9 @@ static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_
save_chartab(buf_chartab);
for (;;) {
- /*
- * Find end pattern that matches first after "matchcol".
- */
+ // Find end pattern that matches first after "matchcol".
best_idx = -1;
- for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx) {
+ for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; idx++) {
int lc_col = matchcol;
spp = &(SYN_ITEMS(syn_block)[idx]);
@@ -2640,18 +2441,14 @@ static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_
}
}
- /*
- * If all end patterns have been tried, and there is no match, the
- * item continues until end-of-line.
- */
+ // If all end patterns have been tried, and there is no match, the
+ // item continues until end-of-line.
if (best_idx == -1) {
break;
}
- /*
- * If the skip pattern matches before the end pattern,
- * continue searching after the skip pattern.
- */
+ // If the skip pattern matches before the end pattern,
+ // continue searching after the skip pattern.
if (spp_skip != NULL) {
int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
@@ -2674,7 +2471,7 @@ static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_
}
line = ml_get_buf(syn_buf, startpos->lnum, false);
- int line_len = (int)STRLEN(line);
+ int line_len = (int)strlen(line);
// take care of an empty match or negative offset
if (pos.col <= matchcol) {
@@ -2697,10 +2494,8 @@ static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_
}
}
- /*
- * Match from start pattern to end pattern.
- * Correct for match and highlight offset of end pattern.
- */
+ // Match from start pattern to end pattern.
+ // Correct for match and highlight offset of end pattern.
spp = &(SYN_ITEMS(syn_block)[best_idx]);
syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
// can't end before the start
@@ -2717,9 +2512,7 @@ static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_
// can't end after the match
limit_pos(end_endpos, m_endpos);
- /*
- * If the end group is highlighted differently, adjust the pointers.
- */
+ // If the end group is highlighted differently, adjust the pointers.
if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0) {
*end_idx = best_idx;
if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT))) {
@@ -2764,9 +2557,7 @@ static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_
re_extmatch_in = NULL;
}
-/*
- * Limit "pos" not to be after "limit".
- */
+// Limit "pos" not to be after "limit".
static void limit_pos(lpos_T *pos, lpos_T *limit)
{
if (pos->lnum > limit->lnum) {
@@ -2776,9 +2567,7 @@ static void limit_pos(lpos_T *pos, lpos_T *limit)
}
}
-/*
- * Limit "pos" not to be after "limit", unless pos->lnum is zero.
- */
+// Limit "pos" not to be after "limit", unless pos->lnum is zero.
static void limit_pos_zero(lpos_T *pos, lpos_T *limit)
{
if (pos->lnum == 0) {
@@ -2800,8 +2589,8 @@ static void syn_add_end_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp
{
int col;
int off;
- char_u *base;
- char_u *p;
+ char *base;
+ char *p;
if (spp->sp_off_flags & (1 << idx)) {
result->lnum = regmatch->startpos[0].lnum;
@@ -2844,8 +2633,8 @@ static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *s
{
int col;
int off;
- char_u *base;
- char_u *p;
+ char *base;
+ char *p;
if (spp->sp_off_flags & (1 << (idx + SPO_COUNT))) {
result->lnum = regmatch->endpos[0].lnum;
@@ -2859,7 +2648,7 @@ static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *s
if (result->lnum > syn_buf->b_ml.ml_line_count) {
// a "\n" at the end of the pattern may take us below the last line
result->lnum = syn_buf->b_ml.ml_line_count;
- col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, false));
+ col = (int)strlen(ml_get_buf(syn_buf, result->lnum, false));
}
if (off != 0) {
base = ml_get_buf(syn_buf, result->lnum, false);
@@ -2878,18 +2667,14 @@ static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *s
result->col = col;
}
-/*
- * Get current line in syntax buffer.
- */
-static char_u *syn_getcurline(void)
+/// Get current line in syntax buffer.
+static char *syn_getcurline(void)
{
return ml_get_buf(syn_buf, current_lnum, false);
}
-/*
- * Call vim_regexec() to find a match with "rmp" in "syn_buf".
- * Returns TRUE when there is a match.
- */
+// Call vim_regexec() to find a match with "rmp" in "syn_buf".
+// Returns true when there is a match.
static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st)
{
int timed_out = 0;
@@ -2916,9 +2701,9 @@ static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T
if (profile_cmp(pt, st->slowest) < 0) {
st->slowest = pt;
}
- ++st->count;
+ st->count++;
if (r > 0) {
- ++st->match;
+ st->match++;
}
}
if (timed_out && !syn_win->w_s->b_syn_slow) {
@@ -2929,9 +2714,9 @@ static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T
if (r > 0) {
rmp->startpos[0].lnum += lnum;
rmp->endpos[0].lnum += lnum;
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
/// Check one position in a line for a matching keyword.
@@ -2944,16 +2729,16 @@ static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T
/// @param next_listp return: next_list of matching keyword
/// @param cur_si item at the top of the stack
/// @param ccharp conceal substitution char
-static int check_keyword_id(char_u *const line, const int startcol, int *const endcolp,
+static int check_keyword_id(char *const line, const int startcol, int *const endcolp,
long *const flagsp, int16_t **const next_listp,
stateitem_T *const cur_si, int *const ccharp)
{
// Find first character after the keyword. First character was already
// checked.
- char_u *const kwp = line + startcol;
+ char *const kwp = line + startcol;
int kwlen = 0;
do {
- kwlen += utfc_ptr2len((char *)kwp + kwlen);
+ kwlen += utfc_ptr2len(kwp + kwlen);
} while (vim_iswordp_buf(kwp + kwlen, syn_buf));
if (kwlen > MAXKEYWLEN) {
@@ -2962,8 +2747,8 @@ static int check_keyword_id(char_u *const line, const int startcol, int *const e
// Must make a copy of the keyword, so we can add a NUL and make it
// lowercase.
- char_u keyword[MAXKEYWLEN + 1]; // assume max. keyword len is 80
- STRLCPY(keyword, kwp, kwlen + 1);
+ char keyword[MAXKEYWLEN + 1]; // assume max. keyword len is 80
+ xstrlcpy(keyword, kwp, (size_t)kwlen + 1);
keyentry_T *kp = NULL;
@@ -2994,17 +2779,17 @@ static int check_keyword_id(char_u *const line, const int startcol, int *const e
/// When current_next_list is non-zero accept only that group, otherwise:
/// Accept a not-contained keyword at toplevel.
/// 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)
+static keyentry_T *match_keyword(char *keyword, hashtab_T *ht, stateitem_T *cur_si)
{
- hashitem_T *hi = hash_find(ht, (char *)keyword);
+ hashitem_T *hi = hash_find(ht, keyword);
if (!HASHITEM_EMPTY(hi)) {
for (keyentry_T *kp = HI2KE(hi); kp != NULL; kp = kp->ke_next) {
if (current_next_list != 0
? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
: (cur_si == NULL
- ? !(kp->flags & HL_CONTAINED)
- : in_id_list(cur_si, cur_si->si_cont_list,
- &kp->k_syn, kp->flags & HL_CONTAINED))) {
+ ? !(kp->flags & HL_CONTAINED)
+ : in_id_list(cur_si, cur_si->si_cont_list,
+ &kp->k_syn, kp->flags & HL_CONTAINED))) {
return kp;
}
}
@@ -3012,15 +2797,13 @@ static keyentry_T *match_keyword(char_u *keyword, hashtab_T *ht, stateitem_T *cu
return NULL;
}
-/*
- * Handle ":syntax conceal" command.
- */
+// Handle ":syntax conceal" command.
static void syn_cmd_conceal(exarg_T *eap, int syncing)
{
- char_u *arg = (char_u *)eap->arg;
- char_u *next;
+ char *arg = eap->arg;
+ char *next;
- eap->nextcmd = (char *)find_nextcmd(arg);
+ eap->nextcmd = find_nextcmd(arg);
if (eap->skip) {
return;
}
@@ -3041,15 +2824,13 @@ static void syn_cmd_conceal(exarg_T *eap, int syncing)
}
}
-/*
- * Handle ":syntax case" command.
- */
+/// Handle ":syntax case" command.
static void syn_cmd_case(exarg_T *eap, int syncing)
{
- char_u *arg = (char_u *)eap->arg;
- char_u *next;
+ char *arg = eap->arg;
+ char *next;
- eap->nextcmd = (char *)find_nextcmd(arg);
+ eap->nextcmd = find_nextcmd(arg);
if (eap->skip) {
return;
}
@@ -3073,10 +2854,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 = (char_u *)eap->arg;
- char_u *arg_end;
+ char *arg = eap->arg;
+ char *arg_end;
- eap->nextcmd = (char *)find_nextcmd(arg);
+ eap->nextcmd = find_nextcmd(arg);
if (eap->skip) {
return;
}
@@ -3103,21 +2884,19 @@ static void syn_cmd_foldlevel(exarg_T *eap, int syncing)
return;
}
- arg = (char_u *)skipwhite((char *)arg_end);
+ arg = skipwhite(arg_end);
if (*arg != NUL) {
semsg(_(e_illegal_arg), arg);
}
}
-/*
- * Handle ":syntax spell" command.
- */
+/// Handle ":syntax spell" command.
static void syn_cmd_spell(exarg_T *eap, int syncing)
{
- char_u *arg = (char_u *)eap->arg;
- char_u *next;
+ char *arg = eap->arg;
+ char *next;
- eap->nextcmd = (char *)find_nextcmd(arg);
+ eap->nextcmd = find_nextcmd(arg);
if (eap->skip) {
return;
}
@@ -3143,26 +2922,26 @@ static void syn_cmd_spell(exarg_T *eap, int syncing)
}
// assume spell checking changed, force a redraw
- redraw_later(curwin, NOT_VALID);
+ redraw_later(curwin, UPD_NOT_VALID);
}
/// Handle ":syntax iskeyword" command.
static void syn_cmd_iskeyword(exarg_T *eap, int syncing)
{
- char_u *arg = (char_u *)eap->arg;
- char_u save_chartab[32];
- char_u *save_isk;
+ char *arg = eap->arg;
+ char save_chartab[32];
+ char *save_isk;
if (eap->skip) {
return;
}
- arg = (char_u *)skipwhite((char *)arg);
+ arg = skipwhite(arg);
if (*arg == NUL) {
msg_puts("\n");
if (curwin->w_s->b_syn_isk != empty_option) {
msg_puts("syntax iskeyword ");
- msg_outtrans((char *)curwin->w_s->b_syn_isk);
+ msg_outtrans(curwin->w_s->b_syn_isk);
} else {
msg_outtrans(_("syntax iskeyword not set"));
}
@@ -3173,7 +2952,7 @@ static void syn_cmd_iskeyword(exarg_T *eap, int syncing)
} else {
memmove(save_chartab, curbuf->b_chartab, (size_t)32);
save_isk = curbuf->b_p_isk;
- curbuf->b_p_isk = vim_strsave(arg);
+ curbuf->b_p_isk = xstrdup(arg);
buf_init_chartab(curbuf, false);
memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab, (size_t)32);
@@ -3183,12 +2962,10 @@ static void syn_cmd_iskeyword(exarg_T *eap, int syncing)
curbuf->b_p_isk = save_isk;
}
}
- redraw_later(curwin, NOT_VALID);
+ redraw_later(curwin, UPD_NOT_VALID);
}
-/*
- * Clear all syntax info for one buffer.
- */
+// Clear all syntax info for one buffer.
void syntax_clear(synblock_T *block)
{
block->b_syn_error = false; // clear previous error
@@ -3236,9 +3013,7 @@ void syntax_clear(synblock_T *block)
running_syn_inc_tag = 0;
}
-/*
- * Get rid of ownsyntax for window "wp".
- */
+// Get rid of ownsyntax for window "wp".
void reset_synblock(win_T *wp)
{
if (wp->w_s != &wp->w_buffer->b_s) {
@@ -3248,9 +3023,7 @@ void reset_synblock(win_T *wp)
}
}
-/*
- * Clear syncing info for one buffer.
- */
+// Clear syncing info for one buffer.
static void syntax_sync_clear(void)
{
// free the syntax patterns
@@ -3273,26 +3046,22 @@ static void syntax_sync_clear(void)
syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
}
-/*
- * Remove one pattern from the buffer's pattern list.
- */
+// Remove one pattern from the buffer's pattern list.
static void syn_remove_pattern(synblock_T *block, int idx)
{
synpat_T *spp;
spp = &(SYN_ITEMS(block)[idx]);
if (spp->sp_flags & HL_FOLD) {
- --block->b_syn_folditems;
+ block->b_syn_folditems--;
}
syn_clear_pattern(block, idx);
memmove(spp, spp + 1, sizeof(synpat_T) * (size_t)(block->b_syn_patterns.ga_len - idx - 1));
block->b_syn_patterns.ga_len--;
}
-/*
- * Clear and free one syntax pattern. When clearing all, must be called from
- * last to first!
- */
+// Clear and free one syntax pattern. When clearing all, must be called from
+// last to first!
static void syn_clear_pattern(synblock_T *block, int i)
{
xfree(SYN_ITEMS(block)[i].sp_pattern);
@@ -3305,9 +3074,7 @@ static void syn_clear_pattern(synblock_T *block, int i)
}
}
-/*
- * Clear and free one syntax cluster.
- */
+// Clear and free one syntax cluster.
static void syn_clear_cluster(synblock_T *block, int i)
{
xfree(SYN_CLSTR(block)[i].scl_name);
@@ -3315,34 +3082,28 @@ static void syn_clear_cluster(synblock_T *block, int i)
xfree(SYN_CLSTR(block)[i].scl_list);
}
-/*
- * Handle ":syntax clear" command.
- */
+/// Handle ":syntax clear" command.
static void syn_cmd_clear(exarg_T *eap, int syncing)
{
- char_u *arg = (char_u *)eap->arg;
- char_u *arg_end;
+ char *arg = eap->arg;
+ char *arg_end;
int id;
- eap->nextcmd = (char *)find_nextcmd(arg);
+ eap->nextcmd = find_nextcmd(arg);
if (eap->skip) {
return;
}
- /*
- * We have to disable this within ":syn include @group filename",
- * because otherwise @group would get deleted.
- * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
- * clear".
- */
+ // We have to disable this within ":syn include @group filename",
+ // because otherwise @group would get deleted.
+ // Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
+ // clear".
if (curwin->w_s->b_syn_topgrp != 0) {
return;
}
if (ends_excmd(*arg)) {
- /*
- * No argument: Clear all syntax items.
- */
+ // No argument: Clear all syntax items.
if (syncing) {
syntax_sync_clear();
} else {
@@ -3353,9 +3114,7 @@ static void syn_cmd_clear(exarg_T *eap, int syncing)
do_unlet(S_LEN("w:current_syntax"), true);
}
} else {
- /*
- * Clear the group IDs that are in the argument.
- */
+ // Clear the group IDs that are in the argument.
while (!ends_excmd(*arg)) {
arg_end = skiptowhite(arg);
if (*arg == '@') {
@@ -3363,33 +3122,29 @@ static void syn_cmd_clear(exarg_T *eap, int syncing)
if (id == 0) {
semsg(_("E391: No such syntax cluster: %s"), arg);
break;
- } else {
- // We can't physically delete a cluster without changing
- // the IDs of other clusters, so we do the next best thing
- // and make it empty.
- int scl_id = id - SYNID_CLUSTER;
-
- XFREE_CLEAR(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
}
+ // We can't physically delete a cluster without changing
+ // the IDs of other clusters, so we do the next best thing
+ // and make it empty.
+ int scl_id = id - SYNID_CLUSTER;
+
+ XFREE_CLEAR(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
} else {
- id = syn_name2id_len((char *)arg, (size_t)(arg_end - arg));
+ id = syn_name2id_len(arg, (size_t)(arg_end - arg));
if (id == 0) {
semsg(_(e_nogroup), arg);
break;
- } else {
- syn_clear_one(id, syncing);
}
+ syn_clear_one(id, syncing);
}
- arg = (char_u *)skipwhite((char *)arg_end);
+ arg = skipwhite(arg_end);
}
}
- redraw_curbuf_later(SOME_VALID);
+ redraw_curbuf_later(UPD_SOME_VALID);
syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
}
-/*
- * Clear one syntax group for the current buffer.
- */
+// Clear one syntax group for the current buffer.
static void syn_clear_one(const int id, const bool syncing)
{
synpat_T *spp;
@@ -3410,37 +3165,29 @@ static void syn_clear_one(const int id, const bool syncing)
}
}
-/*
- * Handle ":syntax on" command.
- */
+// Handle ":syntax on" command.
static void syn_cmd_on(exarg_T *eap, int syncing)
{
syn_cmd_onoff(eap, "syntax");
}
-/*
- * Handle ":syntax reset" command.
- * It actually resets highlighting, not syntax.
- */
+// Handle ":syntax reset" command.
+// It actually resets highlighting, not syntax.
static void syn_cmd_reset(exarg_T *eap, int syncing)
{
- eap->nextcmd = (char *)check_nextcmd((char_u *)eap->arg);
+ eap->nextcmd = check_nextcmd(eap->arg);
if (!eap->skip) {
init_highlight(true, true);
}
}
-/*
- * Handle ":syntax manual" command.
- */
+// Handle ":syntax manual" command.
static void syn_cmd_manual(exarg_T *eap, int syncing)
{
syn_cmd_onoff(eap, "manual");
}
-/*
- * Handle ":syntax off" command.
- */
+// Handle ":syntax off" command.
static void syn_cmd_off(exarg_T *eap, int syncing)
{
syn_cmd_onoff(eap, "nosyntax");
@@ -3449,7 +3196,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 = (char *)check_nextcmd((char_u *)eap->arg);
+ eap->nextcmd = check_nextcmd(eap->arg);
if (!eap->skip) {
did_syntax_onoff = true;
char buf[100];
@@ -3471,13 +3218,13 @@ void syn_maybe_enable(void)
/// Handle ":syntax [list]" command: list current syntax words.
///
-/// @param syncing when TRUE: list syncing items
+/// @param syncing when true: list syncing items
static void syn_cmd_list(exarg_T *eap, int syncing)
{
- char_u *arg = (char_u *)eap->arg;
- char_u *arg_end;
+ char *arg = eap->arg;
+ char *arg_end;
- eap->nextcmd = (char *)find_nextcmd(arg);
+ eap->nextcmd = find_nextcmd(arg);
if (eap->skip) {
return;
}
@@ -3520,19 +3267,15 @@ static void syn_cmd_list(exarg_T *eap, int syncing)
msg_puts_title(_("\n--- Syntax items ---"));
}
if (ends_excmd(*arg)) {
- /*
- * No argument: List all group IDs and all syntax clusters.
- */
+ // No argument: List all group IDs and all syntax clusters.
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) {
+ for (int id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; id++) {
syn_list_cluster(id);
}
} else {
- /*
- * List the group IDs and syntax clusters that are in the argument.
- */
+ // List the group IDs and syntax clusters that are in the argument.
while (!ends_excmd(*arg) && !got_int) {
arg_end = skiptowhite(arg);
if (*arg == '@') {
@@ -3543,17 +3286,17 @@ static void syn_cmd_list(exarg_T *eap, int syncing)
syn_list_cluster(id - SYNID_CLUSTER);
}
} else {
- int id = syn_name2id_len((char *)arg, (size_t)(arg_end - arg));
+ int id = syn_name2id_len(arg, (size_t)(arg_end - arg));
if (id == 0) {
semsg(_(e_nogroup), arg);
} else {
syn_list_one(id, syncing, true);
}
}
- arg = (char_u *)skipwhite((char *)arg_end);
+ arg = skipwhite(arg_end);
}
}
- eap->nextcmd = (char *)check_nextcmd(arg);
+ eap->nextcmd = check_nextcmd(arg);
}
static void syn_lines_msg(void)
@@ -3598,8 +3341,7 @@ static int last_matchgroup;
static void syn_list_one(const int id, const bool syncing, const bool link_only)
{
bool did_header = false;
- static struct name_list namelist1[] =
- {
+ static struct name_list namelist1[] = {
{ HL_DISPLAY, "display" },
{ HL_CONTAINED, "contained" },
{ HL_ONELINE, "oneline" },
@@ -3612,8 +3354,7 @@ static void syn_list_one(const int id, const bool syncing, const bool link_only)
{ HL_CONCEALENDS, "concealends" },
{ 0, NULL }
};
- static struct name_list namelist2[] =
- {
+ static struct name_list namelist2[] = {
{ HL_SKIPWHITE, "skipwhite" },
{ HL_SKIPNL, "skipnl" },
{ HL_SKIPEMPTY, "skipempty" },
@@ -3680,8 +3421,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((char *)highlight_group_name(SYN_ITEMS(curwin->w_s)
- [spp->sp_sync_idx].sp_syn.id - 1));
+ msg_outtrans(highlight_group_name(SYN_ITEMS(curwin->w_s)
+ [spp->sp_sync_idx].sp_syn.id - 1));
} else {
msg_puts("NONE");
}
@@ -3694,7 +3435,7 @@ static void syn_list_one(const int id, const bool syncing, const bool link_only)
(void)syn_list_header(did_header, 0, id, true);
msg_puts_attr("links to", attr);
msg_putchar(' ');
- msg_outtrans((char *)highlight_group_name(highlight_link_id(id - 1) - 1));
+ msg_outtrans(highlight_group_name(highlight_link_id(id - 1) - 1));
}
}
@@ -3702,7 +3443,7 @@ static void syn_list_flags(struct name_list *nlist, int flags, int attr)
{
int i;
- for (i = 0; nlist[i].flag != 0; ++i) {
+ for (i = 0; nlist[i].flag != 0; i++) {
if (flags & nlist[i].flag) {
msg_puts_attr(nlist[i].name, attr);
msg_putchar(' ');
@@ -3710,16 +3451,14 @@ static void syn_list_flags(struct name_list *nlist, int flags, int attr)
}
}
-/*
- * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
- */
+// List one syntax cluster, for ":syntax" or "syntax list syntax_name".
static void syn_list_cluster(int id)
{
int endcol = 15;
// slight hack: roughly duplicate the guts of syn_list_header()
msg_putchar('\n');
- msg_outtrans((char *)SYN_CLSTR(curwin->w_s)[id].scl_name);
+ msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
if (msg_col >= endcol) { // output at least one space
endcol = msg_col + 1;
@@ -3756,9 +3495,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((char *)SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
+ msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
} else {
- msg_outtrans((char *)highlight_group_name(*p - 1));
+ msg_outtrans(highlight_group_name(*p - 1));
}
if (p[1]) {
msg_putchar(',');
@@ -3780,7 +3519,7 @@ static void put_pattern(const char *const s, const int c, const synpat_T *const
if (last_matchgroup == 0) {
msg_outtrans("NONE");
} else {
- msg_outtrans((char *)highlight_group_name(last_matchgroup - 1));
+ msg_outtrans(highlight_group_name(last_matchgroup - 1));
}
msg_putchar(' ');
}
@@ -3790,14 +3529,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((char *)spp->sp_pattern, sepchars[i]) != NULL;) {
+ for (i = 0; vim_strchr(spp->sp_pattern, (uint8_t)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((char *)spp->sp_pattern);
+ msg_outtrans(spp->sp_pattern);
msg_putchar(sepchars[i]);
// output any pattern options
@@ -3865,7 +3604,7 @@ static bool syn_list_keywords(const int id, const hashtab_T *const ht, bool did_
|| prev_next_list != kp->next_list) {
force_newline = true;
} else {
- outlen = (int)STRLEN(kp->keyword);
+ outlen = (int)strlen(kp->keyword);
}
// output "contained" and "nextgroup" on each line
if (syn_list_header(did_header, outlen, id, force_newline)) {
@@ -3925,7 +3664,7 @@ static void syn_clear_keyword(int id, hashtab_T *ht)
hash_lock(ht);
todo = (int)ht->ht_used;
- for (hi = ht->ht_array; todo > 0; ++hi) {
+ for (hi = ht->ht_array; todo > 0; hi++) {
if (HASHITEM_EMPTY(hi)) {
continue;
}
@@ -3938,7 +3677,7 @@ static void syn_clear_keyword(int id, hashtab_T *ht)
if (kp_next == NULL) {
hash_remove(ht, hi);
} else {
- hi->hi_key = KE2HIKEY(kp_next);
+ hi->hi_key = (char *)KE2HIKEY(kp_next);
}
} else {
kp_prev->ke_next = kp_next;
@@ -3956,9 +3695,7 @@ static void syn_clear_keyword(int id, hashtab_T *ht)
hash_unlock(ht);
}
-/*
- * Clear a whole keyword table.
- */
+// Clear a whole keyword table.
static void clear_keywtab(hashtab_T *ht)
{
hashitem_T *hi;
@@ -3967,7 +3704,7 @@ static void clear_keywtab(hashtab_T *ht)
keyentry_T *kp_next;
todo = (int)ht->ht_used;
- for (hi = ht->ht_array; todo > 0; ++hi) {
+ for (hi = ht->ht_array; todo > 0; hi++) {
if (!HASHITEM_EMPTY(hi)) {
todo--;
for (kp = HI2KE(hi); kp != NULL; kp = kp_next) {
@@ -3989,16 +3726,17 @@ static void clear_keywtab(hashtab_T *ht)
/// @param flags flags for this keyword
/// @param cont_in_list containedin for this keyword
/// @param next_list nextgroup for this keyword
-static void add_keyword(char_u *const name, const int id, const int flags,
+static void add_keyword(char *const name, const int id, const int flags,
int16_t *const cont_in_list, int16_t *const next_list,
const int conceal_char)
{
- char_u name_folded[MAXKEYWLEN + 1];
- const char_u *const name_ic = (curwin->w_s->b_syn_ic)
- ? str_foldcase(name, (int)STRLEN(name), name_folded, sizeof(name_folded))
+ char name_folded[MAXKEYWLEN + 1];
+ const char *const name_ic = (curwin->w_s->b_syn_ic)
+ ? str_foldcase(name, (int)strlen(name), name_folded,
+ sizeof(name_folded))
: name;
- keyentry_T *const kp = xmalloc(sizeof(keyentry_T) + STRLEN(name_ic));
+ keyentry_T *const kp = xmalloc(sizeof(keyentry_T) + strlen(name_ic));
STRCPY(kp->keyword, name_ic);
kp->k_syn.id = (int16_t)id;
kp->k_syn.inc_tag = current_syn_inc_tag;
@@ -4006,7 +3744,7 @@ static void add_keyword(char_u *const name, const int id, const int flags,
kp->k_char = conceal_char;
kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
if (cont_in_list != NULL) {
- curwin->w_s->b_syn_containedin = TRUE;
+ curwin->w_s->b_syn_containedin = true;
}
kp->next_list = copy_id_list(next_list);
@@ -4015,7 +3753,7 @@ static void add_keyword(char_u *const name, const int id, const int flags,
? &curwin->w_s->b_keywtab_ic
: &curwin->w_s->b_keywtab;
hashitem_T *const hi = hash_lookup(ht, (const char *)kp->keyword,
- STRLEN(kp->keyword), hash);
+ strlen(kp->keyword), hash);
// even though it looks like only the kp->keyword member is
// being used here, vim uses some pointer trickery to get the original
@@ -4028,7 +3766,7 @@ static void add_keyword(char_u *const name, const int id, const int flags,
} else {
// keyword already exists, prepend to list
kp->ke_next = HI2KE(hi);
- hi->hi_key = KE2HIKEY(kp);
+ hi->hi_key = (char *)KE2HIKEY(kp);
}
}
@@ -4039,17 +3777,13 @@ static void add_keyword(char_u *const name, const int id, const int flags,
///
/// @return a pointer to the first argument.
/// Return NULL if the end of the command was found instead of further args.
-static char_u *get_group_name(char_u *arg, char_u **name_end)
+static char *get_group_name(char *arg, char **name_end)
{
- char_u *rest;
-
*name_end = skiptowhite(arg);
- rest = (char_u *)skipwhite((char *)(*name_end));
+ char *rest = skipwhite(*name_end);
- /*
- * Check if there are enough arguments. The first argument may be a
- * pattern, where '|' is allowed, so only check for NUL.
- */
+ // Check if there are enough arguments. The first argument may be a
+ // pattern, where '|' is allowed, so only check for NUL.
if (ends_excmd(*arg) || *rest == NUL) {
return NULL;
}
@@ -4063,13 +3797,13 @@ static char_u *get_group_name(char_u *arg, char_u **name_end)
///
/// @param arg next argument to be checked
/// @param opt various things
-/// @param skip TRUE if skipping over command
+/// @param skip true if skipping over command
///
/// @return a pointer to the next argument (which isn't an option).
/// Return NULL for any error;
-static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_char, int skip)
+static char *get_syn_options(char *arg, syn_opt_arg_T *opt, int *conceal_char, int skip)
{
- char_u *gname_start, *gname;
+ char *gname_start, *gname;
int syn_id;
int len = 0;
char *p;
@@ -4108,11 +3842,9 @@ static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_cha
}
for (;;) {
- /*
- * This is used very often when a large number of keywords is defined.
- * Need to skip quickly when no option name is found.
- * Also avoid tolower(), it's slow.
- */
+ // This is used very often when a large number of keywords is defined.
+ // Need to skip quickly when no option name is found.
+ // Also avoid tolower(), it's slow.
if (strchr(first_letters, *arg) == NULL) {
break;
}
@@ -4120,8 +3852,8 @@ static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_cha
for (fidx = ARRAY_SIZE(flagtab); --fidx >= 0;) {
p = flagtab[fidx].name;
int i;
- for (i = 0, len = 0; p[i] != NUL; i += 2, ++len) {
- if (arg[len] != (char_u)p[i] && arg[len] != (char_u)p[i + 1]) {
+ for (i = 0, len = 0; p[i] != NUL; i += 2, len++) {
+ if (arg[len] != p[i] && arg[len] != p[i + 1]) {
break;
}
}
@@ -4161,16 +3893,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((char *)arg + 6);
- arg += utfc_ptr2len((char *)arg + 6) - 1;
+ *conceal_char = utf_ptr2char(arg + 6);
+ arg += utfc_ptr2len(arg + 6) - 1;
if (!vim_isprintc_strict(*conceal_char)) {
emsg(_("E844: invalid cchar value"));
return NULL;
}
- arg = (char_u *)skipwhite((char *)arg + 7);
+ arg = skipwhite(arg + 7);
} else {
opt->flags |= flagtab[fidx].flags;
- arg = (char_u *)skipwhite((char *)arg + len);
+ arg = skipwhite(arg + len);
if (flagtab[fidx].flags == HL_SYNC_HERE
|| flagtab[fidx].flags == HL_SYNC_THERE) {
@@ -4183,11 +3915,11 @@ static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_cha
if (gname_start == arg) {
return NULL;
}
- gname = vim_strnsave(gname_start, (size_t)(arg - gname_start));
- if (STRCMP(gname, "NONE") == 0) {
+ gname = xstrnsave(gname_start, (size_t)(arg - gname_start));
+ if (strcmp(gname, "NONE") == 0) {
*opt->sync_idx = NONE_IDX;
} else {
- syn_id = syn_name2id((char *)gname);
+ syn_id = syn_name2id(gname);
int i;
for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0;) {
if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
@@ -4204,7 +3936,7 @@ static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_cha
}
xfree(gname);
- arg = (char_u *)skipwhite((char *)arg);
+ arg = skipwhite(arg);
} else if (flagtab[fidx].flags == HL_FOLD
&& foldmethodIsSyntax(curwin)) {
// Need to update folds later.
@@ -4216,11 +3948,9 @@ static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_cha
return arg;
}
-/*
- * Adjustments to syntax item when declared in a ":syn include"'d file.
- * Set the contained flag, and if the item is not already contained, add it
- * to the specified top-level group, if any.
- */
+// Adjustments to syntax item when declared in a ":syn include"'d file.
+// Set the contained flag, and if the item is not already contained, add it
+// to the specified top-level group, if any.
static void syn_incl_toplevel(int id, int *flagsp)
{
if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0) {
@@ -4239,21 +3969,19 @@ static void syn_incl_toplevel(int id, int *flagsp)
}
}
-/*
- * Handle ":syntax include [@{group-name}] filename" command.
- */
+// Handle ":syntax include [@{group-name}] filename" command.
static void syn_cmd_include(exarg_T *eap, int syncing)
{
- char_u *arg = (char_u *)eap->arg;
+ char *arg = eap->arg;
int sgl_id = 1;
- char_u *group_name_end;
- char_u *rest;
+ char *group_name_end;
+ char *rest;
char *errormsg = NULL;
int prev_toplvl_grp;
int prev_syn_inc_tag;
bool source = false;
- eap->nextcmd = (char *)find_nextcmd(arg);
+ eap->nextcmd = find_nextcmd(arg);
if (eap->skip) {
return;
}
@@ -4270,16 +3998,14 @@ static void syn_cmd_include(exarg_T *eap, int syncing)
return;
}
// separate_nextcmd() and expand_filename() depend on this
- eap->arg = (char *)rest;
+ eap->arg = rest;
}
- /*
- * Everything that's left, up to the next command, should be the
- * filename to include.
- */
+ // Everything that's left, up to the next command, should be the
+ // filename to include.
eap->argt |= (EX_XFILE | EX_NOSPC);
separate_nextcmd(eap);
- if (*eap->arg == '<' || *eap->arg == '$' || path_is_absolute((char_u *)eap->arg)) {
+ if (*eap->arg == '<' || *eap->arg == '$' || path_is_absolute(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.
@@ -4292,10 +4018,8 @@ static void syn_cmd_include(exarg_T *eap, int syncing)
}
}
- /*
- * Save and restore the existing top-level grouplist id and ":syn
- * include" tag around the actual inclusion.
- */
+ // Save and restore the existing top-level grouplist id and ":syn
+ // include" tag around the actual inclusion.
if (running_syn_inc_tag >= MAX_SYN_INC_TAG) {
emsg(_("E847: Too many syntax includes"));
return;
@@ -4313,18 +4037,16 @@ static void syn_cmd_include(exarg_T *eap, int syncing)
current_syn_inc_tag = prev_syn_inc_tag;
}
-/*
- * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
- */
+// Handle ":syntax keyword {group-name} [{option}] keyword .." command.
static void syn_cmd_keyword(exarg_T *eap, int syncing)
{
- char_u *arg = (char_u *)eap->arg;
- char_u *group_name_end;
+ char *arg = eap->arg;
+ char *group_name_end;
int syn_id;
- char_u *rest;
- char_u *keyword_copy = NULL;
- char_u *p;
- char_u *kw;
+ char *rest;
+ char *keyword_copy = NULL;
+ char *p;
+ char *kw;
syn_opt_arg_T syn_opt_arg;
int cnt;
int conceal_char = NUL;
@@ -4335,11 +4057,11 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing)
if (eap->skip) {
syn_id = -1;
} else {
- syn_id = syn_check_group((char *)arg, (size_t)(group_name_end - arg));
+ syn_id = syn_check_group(arg, (size_t)(group_name_end - arg));
}
if (syn_id != 0) {
// Allocate a buffer, for removing backslashes in the keyword.
- keyword_copy = xmalloc(STRLEN(rest) + 1);
+ keyword_copy = xmalloc(strlen(rest) + 1);
}
if (keyword_copy != NULL) {
syn_opt_arg.flags = 0;
@@ -4354,7 +4076,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 = (char_u *)skipwhite((char *)rest)) {
+ for (; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest)) {
rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
if (rest == NULL || ends_excmd(*rest)) {
break;
@@ -4375,8 +4097,8 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing)
syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
// 2: Add an entry for each keyword.
- for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1) {
- for (p = (char_u *)vim_strchr((char *)kw, '[');;) {
+ for (kw = keyword_copy; --cnt >= 0; kw += strlen(kw) + 1) {
+ for (p = vim_strchr(kw, '[');;) {
if (p != NULL) {
*p = NUL;
}
@@ -4399,7 +4121,7 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing)
kw = p + 1;
break; // skip over the "]"
}
- const int l = utfc_ptr2len((char *)p + 1);
+ const int l = utfc_ptr2len(p + 1);
memmove(p, p + 1, (size_t)l);
p += l;
@@ -4415,12 +4137,12 @@ error:
}
if (rest != NULL) {
- eap->nextcmd = (char *)check_nextcmd(rest);
+ eap->nextcmd = check_nextcmd(rest);
} else {
semsg(_(e_invarg2), arg);
}
- redraw_curbuf_later(SOME_VALID);
+ redraw_curbuf_later(UPD_SOME_VALID);
syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
}
@@ -4428,12 +4150,11 @@ error:
///
/// Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
///
-/// @param syncing TRUE for ":syntax sync match .. "
+/// @param syncing true for ":syntax sync match .. "
static void syn_cmd_match(exarg_T *eap, int syncing)
{
- char_u *arg = (char_u *)eap->arg;
- char_u *group_name_end;
- char_u *rest;
+ char *arg = eap->arg;
+ char *group_name_end;
synpat_T item; // the item found in the line
int syn_id;
syn_opt_arg_T syn_opt_arg;
@@ -4441,7 +4162,7 @@ static void syn_cmd_match(exarg_T *eap, int syncing)
int conceal_char = NUL;
// Isolate the group name, check for validity
- rest = get_group_name(arg, &group_name_end);
+ char *rest = get_group_name(arg, &group_name_end);
// Get options before the pattern
syn_opt_arg.flags = 0;
@@ -4465,18 +4186,14 @@ static void syn_cmd_match(exarg_T *eap, int syncing)
rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
if (rest != NULL) { // all arguments are valid
- /*
- * Check for trailing command and illegal trailing arguments.
- */
- eap->nextcmd = (char *)check_nextcmd(rest);
+ // Check for trailing command and illegal trailing arguments.
+ eap->nextcmd = check_nextcmd(rest);
if (!ends_excmd(*rest) || eap->skip) {
rest = NULL;
} else {
- if ((syn_id = syn_check_group((char *)arg, (size_t)(group_name_end - arg))) != 0) {
+ if ((syn_id = syn_check_group(arg, (size_t)(group_name_end - arg))) != 0) {
syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
- /*
- * Store the pattern in the syn_items list
- */
+ // Store the pattern in the syn_items list
synpat_T *spp = GA_APPEND_VIA_PTR(synpat_T,
&curwin->w_s->b_syn_patterns);
*spp = item;
@@ -4490,7 +4207,7 @@ static void syn_cmd_match(exarg_T *eap, int syncing)
spp->sp_syn.cont_in_list = syn_opt_arg.cont_in_list;
spp->sp_cchar = conceal_char;
if (syn_opt_arg.cont_in_list != NULL) {
- curwin->w_s->b_syn_containedin = TRUE;
+ curwin->w_s->b_syn_containedin = true;
}
spp->sp_next_list = syn_opt_arg.next_list;
@@ -4499,19 +4216,17 @@ static void syn_cmd_match(exarg_T *eap, int syncing)
curwin->w_s->b_syn_sync_flags |= SF_MATCH;
}
if (syn_opt_arg.flags & HL_FOLD) {
- ++curwin->w_s->b_syn_folditems;
+ curwin->w_s->b_syn_folditems++;
}
- redraw_curbuf_later(SOME_VALID);
+ redraw_curbuf_later(UPD_SOME_VALID);
syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
return; // don't free the progs and patterns now
}
}
}
- /*
- * Something failed, free the allocated memory.
- */
+ // Something failed, free the allocated memory.
vim_regfree(item.sp_prog);
xfree(item.sp_pattern);
xfree(syn_opt_arg.cont_list);
@@ -4526,15 +4241,15 @@ static void syn_cmd_match(exarg_T *eap, int syncing)
/// Handle ":syntax region {group-name} [matchgroup={group-name}]
/// start {start} .. [skip {skip}] end {end} .. [{options}]".
///
-/// @param syncing TRUE for ":syntax sync region .."
+/// @param syncing true for ":syntax sync region .."
static void syn_cmd_region(exarg_T *eap, int syncing)
{
- char_u *arg = (char_u *)eap->arg;
- char_u *group_name_end;
- char_u *rest; // next arg, NULL on error
- char_u *key_end;
- char_u *key = NULL;
- char_u *p;
+ char *arg = eap->arg;
+ char *group_name_end;
+ char *rest; // next arg, NULL on error
+ char *key_end;
+ char *key = NULL;
+ char *p;
int item;
#define ITEM_START 0
#define ITEM_SKIP 1
@@ -4589,13 +4304,13 @@ static void syn_cmd_region(exarg_T *eap, int syncing)
}
xfree(key);
key = vim_strnsave_up(rest, (size_t)(key_end - rest));
- if (STRCMP(key, "MATCHGROUP") == 0) {
+ if (strcmp(key, "MATCHGROUP") == 0) {
item = ITEM_MATCHGROUP;
- } else if (STRCMP(key, "START") == 0) {
+ } else if (strcmp(key, "START") == 0) {
item = ITEM_START;
- } else if (STRCMP(key, "END") == 0) {
+ } else if (strcmp(key, "END") == 0) {
item = ITEM_END;
- } else if (STRCMP(key, "SKIP") == 0) {
+ } else if (strcmp(key, "SKIP") == 0) {
if (pat_ptrs[ITEM_SKIP] != NULL) { // One skip pattern allowed.
illegal = true;
break;
@@ -4604,13 +4319,13 @@ static void syn_cmd_region(exarg_T *eap, int syncing)
} else {
break;
}
- rest = (char_u *)skipwhite((char *)key_end);
+ rest = skipwhite(key_end);
if (*rest != '=') {
rest = NULL;
semsg(_("E398: Missing '=': %s"), arg);
break;
}
- rest = (char_u *)skipwhite((char *)rest + 1);
+ rest = skipwhite(rest + 1);
if (*rest == NUL) {
not_enough = true;
break;
@@ -4618,22 +4333,20 @@ static void syn_cmd_region(exarg_T *eap, int syncing)
if (item == ITEM_MATCHGROUP) {
p = skiptowhite(rest);
- if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip) {
+ if ((p - rest == 4 && strncmp(rest, "NONE", 4) == 0) || eap->skip) {
matchgroup_id = 0;
} else {
- matchgroup_id = syn_check_group((char *)rest, (size_t)(p - rest));
+ matchgroup_id = syn_check_group(rest, (size_t)(p - rest));
if (matchgroup_id == 0) {
illegal = true;
break;
}
}
- rest = (char_u *)skipwhite((char *)p);
+ rest = skipwhite(p);
} else {
- /*
- * Allocate room for a syn_pattern, and link it in the list of
- * syn_patterns for this item, at the start (because the list is
- * used from end to start).
- */
+ // Allocate room for a syn_pattern, and link it in the list of
+ // syn_patterns for this item, at the start (because the list is
+ // used from end to start).
ppp = xmalloc(sizeof(struct pat_ptr));
ppp->pp_next = pat_ptrs[item];
pat_ptrs[item] = ppp;
@@ -4671,22 +4384,18 @@ static void syn_cmd_region(exarg_T *eap, int syncing)
}
if (rest != NULL) {
- /*
- * Check for trailing garbage or command.
- * If OK, add the item.
- */
- eap->nextcmd = (char *)check_nextcmd(rest);
+ // Check for trailing garbage or command.
+ // If OK, add the item.
+ eap->nextcmd = check_nextcmd(rest);
if (!ends_excmd(*rest) || eap->skip) {
rest = NULL;
} else {
ga_grow(&(curwin->w_s->b_syn_patterns), pat_count);
- if ((syn_id = syn_check_group((char *)arg, (size_t)(group_name_end - arg))) != 0) {
+ if ((syn_id = syn_check_group(arg, (size_t)(group_name_end - arg))) != 0) {
syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
- /*
- * Store the start/skip/end in the syn_items list
- */
+ // Store the start/skip/end in the syn_items list
int idx = curwin->w_s->b_syn_patterns.ga_len;
- for (item = ITEM_START; item <= ITEM_END; ++item) {
+ for (item = ITEM_START; item <= ITEM_END; item++) {
for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next) {
SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
@@ -4705,7 +4414,7 @@ static void syn_cmd_region(exarg_T *eap, int syncing)
SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
syn_opt_arg.cont_in_list;
if (syn_opt_arg.cont_in_list != NULL) {
- curwin->w_s->b_syn_containedin = TRUE;
+ curwin->w_s->b_syn_containedin = true;
}
SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
syn_opt_arg.next_list;
@@ -4713,22 +4422,20 @@ static void syn_cmd_region(exarg_T *eap, int syncing)
curwin->w_s->b_syn_patterns.ga_len++;
idx++;
if (syn_opt_arg.flags & HL_FOLD) {
- ++curwin->w_s->b_syn_folditems;
+ curwin->w_s->b_syn_folditems++;
}
}
}
- redraw_curbuf_later(SOME_VALID);
+ redraw_curbuf_later(UPD_SOME_VALID);
syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
success = true; // don't free the progs and patterns now
}
}
}
- /*
- * Free the allocated memory.
- */
- for (item = ITEM_START; item <= ITEM_END; ++item) {
+ // Free the allocated memory.
+ for (item = ITEM_START; item <= ITEM_END; item++) {
for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next) {
if (!success && ppp->pp_synp != NULL) {
vim_regfree(ppp->pp_synp->sp_prog);
@@ -4771,9 +4478,7 @@ static void syn_combine_list(int16_t **const clstr1, int16_t **const clstr2, con
const int16_t *g2;
int16_t *clstr = NULL;
- /*
- * Handle degenerate cases.
- */
+ // Handle degenerate cases.
if (*clstr2 == NULL) {
return;
}
@@ -4809,13 +4514,9 @@ static void syn_combine_list(int16_t **const clstr1, int16_t **const clstr2, con
g2 = *clstr2;
int count = 0;
- /*
- * First, loop through the lists until one of them is empty.
- */
+ // First, loop through the lists until one of them is empty.
while (*g1 && *g2) {
- /*
- * We always want to add from the first list.
- */
+ // We always want to add from the first list.
if (*g1 < *g2) {
if (round == 2) {
clstr[count] = *g1;
@@ -4824,10 +4525,8 @@ static void syn_combine_list(int16_t **const clstr1, int16_t **const clstr2, con
g1++;
continue;
}
- /*
- * We only want to add from the second list if we're adding the
- * lists.
- */
+ // We only want to add from the second list if we're adding the
+ // lists.
if (list_op == CLUSTER_ADD) {
if (round == 2) {
clstr[count] = *g2;
@@ -4840,11 +4539,9 @@ static void syn_combine_list(int16_t **const clstr1, int16_t **const clstr2, con
g2++;
}
- /*
- * Now add the leftovers from whichever list didn't get finished
- * first. As before, we only want to add from the second list if
- * we're adding the lists.
- */
+ // Now add the leftovers from whichever list didn't get finished
+ // first. As before, we only want to add from the second list if
+ // we're adding the lists.
for (; *g1; g1++, count++) {
if (round == 2) {
clstr[count] = *g1;
@@ -4859,10 +4556,8 @@ static void syn_combine_list(int16_t **const clstr1, int16_t **const clstr2, con
}
if (round == 1) {
- /*
- * If the group ended up empty, we don't need to allocate any
- * space for it.
- */
+ // If the group ended up empty, we don't need to allocate any
+ // space for it.
if (count == 0) {
clstr = NULL;
break;
@@ -4872,24 +4567,22 @@ static void syn_combine_list(int16_t **const clstr1, int16_t **const clstr2, con
}
}
- /*
- * Finally, put the new list in place.
- */
+ // Finally, put the new list in place.
xfree(*clstr1);
xfree(*clstr2);
*clstr1 = clstr;
}
-// Lookup a syntax cluster name and return its ID.
-// If it is not found, 0 is returned.
-static int syn_scl_name2id(char_u *name)
+/// Lookup a syntax cluster name and return its ID.
+/// If it is not found, 0 is returned.
+static int syn_scl_name2id(char *name)
{
// Avoid using stricmp() too much, it's slow on some systems
- char_u *name_u = vim_strsave_up(name);
+ char *name_u = vim_strsave_up(name);
int i;
for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0;) {
if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
- && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0) {
+ && strcmp(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0) {
break;
}
}
@@ -4897,25 +4590,24 @@ static int syn_scl_name2id(char_u *name)
return i < 0 ? 0 : i + SYNID_CLUSTER;
}
-/*
- * Like syn_scl_name2id(), but take a pointer + length argument.
- */
-static int syn_scl_namen2id(char_u *linep, int len)
+/// Like syn_scl_name2id(), but take a pointer + length argument.
+static int syn_scl_namen2id(char *linep, int len)
{
- char_u *name = vim_strnsave(linep, (size_t)len);
+ char *name = xstrnsave(linep, (size_t)len);
int id = syn_scl_name2id(name);
xfree(name);
return id;
}
-// Find syntax cluster name in the table and return its ID.
-// The argument is a pointer to the name and the length of the name.
-// If it doesn't exist yet, a new entry is created.
-// Return 0 for failure.
-static int syn_check_cluster(char_u *pp, int len)
+/// Find syntax cluster name in the table and return its ID.
+/// The argument is a pointer to the name and the length of the name.
+/// If it doesn't exist yet, a new entry is created.
+///
+/// @return 0 for failure.
+static int syn_check_cluster(char *pp, int len)
{
- char_u *name = vim_strnsave(pp, (size_t)len);
+ char *name = xstrnsave(pp, (size_t)len);
int id = syn_scl_name2id(name);
if (id == 0) { // doesn't exist yet
id = syn_add_cluster(name);
@@ -4925,14 +4617,13 @@ static int syn_check_cluster(char_u *pp, int len)
return id;
}
-// Add new syntax cluster and return its ID.
-// "name" must be an allocated string, it will be consumed.
-// Return 0 for failure.
-static int syn_add_cluster(char_u *name)
+/// Add new syntax cluster and return its ID.
+/// "name" must be an allocated string, it will be consumed.
+///
+/// @return 0 for failure.
+static int syn_add_cluster(char *name)
{
- /*
- * First call for this growarray: init growing array.
- */
+ // First call for this growarray: init growing array.
if (curwin->w_s->b_syn_clusters.ga_data == NULL) {
curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
ga_set_growsize(&curwin->w_s->b_syn_clusters, 10);
@@ -4962,20 +4653,18 @@ static int syn_add_cluster(char_u *name)
return len + SYNID_CLUSTER;
}
-/*
- * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
- * [add={groupname},..] [remove={groupname},..]".
- */
+// Handle ":syntax cluster {cluster-name} [contains={groupname},..]
+// [add={groupname},..] [remove={groupname},..]".
static void syn_cmd_cluster(exarg_T *eap, int syncing)
{
- char_u *arg = (char_u *)eap->arg;
- char_u *group_name_end;
- char_u *rest;
+ char *arg = eap->arg;
+ char *group_name_end;
+ char *rest;
bool got_clstr = false;
int opt_len;
int list_op;
- eap->nextcmd = (char *)find_nextcmd(arg);
+ eap->nextcmd = find_nextcmd(arg);
if (eap->skip) {
return;
}
@@ -5021,7 +4710,7 @@ static void syn_cmd_cluster(exarg_T *eap, int syncing)
}
if (got_clstr) {
- redraw_curbuf_later(SOME_VALID);
+ redraw_curbuf_later(UPD_SOME_VALID);
syn_stack_free_all(curwin->w_s); // Need to recompute all.
}
}
@@ -5034,21 +4723,18 @@ static void syn_cmd_cluster(exarg_T *eap, int syncing)
}
}
-/*
- * On first call for current buffer: Init growing array.
- */
+// On first call for current buffer: Init growing array.
static void init_syn_patterns(void)
{
curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
ga_set_growsize(&curwin->w_s->b_syn_patterns, 10);
}
-/*
- * Get one pattern for a ":syntax match" or ":syntax region" command.
- * Stores the pattern and program in a synpat_T.
- * Returns a pointer to the next argument, or NULL in case of an error.
- */
-static char_u *get_syn_pattern(char_u *arg, synpat_T *ci)
+/// Get one pattern for a ":syntax match" or ":syntax region" command.
+/// Stores the pattern and program in a synpat_T.
+///
+/// @return a pointer to the next argument, or NULL in case of an error.
+static char *get_syn_pattern(char *arg, synpat_T *ci)
{
char *end;
int *p;
@@ -5060,18 +4746,18 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci)
return NULL;
}
- end = (char *)skip_regexp(arg + 1, *arg, true, NULL);
- if (*end != (char)(*arg)) { // end delimiter not found
+ end = skip_regexp(arg + 1, *arg, true);
+ if (*end != *arg) { // end delimiter not found
semsg(_("E401: Pattern delimiter not found: %s"), arg);
return NULL;
}
// store the pattern and compiled regexp program
- ci->sp_pattern = vim_strnsave(arg + 1, (size_t)(end - (char *)arg) - 1);
+ ci->sp_pattern = xstrnsave(arg + 1, (size_t)(end - arg) - 1);
// Make 'cpoptions' empty, to avoid the 'l' flag
cpo_save = p_cpo;
- p_cpo = "";
- ci->sp_prog = vim_regcomp((char *)ci->sp_pattern, RE_MAGIC);
+ p_cpo = empty_option;
+ ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
p_cpo = cpo_save;
if (ci->sp_prog == NULL) {
@@ -5080,13 +4766,11 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci)
ci->sp_ic = curwin->w_s->b_syn_ic;
syn_clear_time(&ci->sp_time);
- /*
- * Check for a match, highlight or region offset.
- */
+ // Check for a match, highlight or region offset.
end++;
do {
for (idx = SPO_COUNT; --idx >= 0;) {
- if (STRNCMP(end, spo_name_tab[idx], 3) == 0) {
+ if (strncmp(end, spo_name_tab[idx], 3) == 0) {
break;
}
}
@@ -5137,59 +4821,57 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci)
semsg(_("E402: Garbage after pattern: %s"), arg);
return NULL;
}
- return (char_u *)skipwhite(end);
+ return skipwhite(end);
}
-/*
- * Handle ":syntax sync .." command.
- */
+/// Handle ":syntax sync .." command.
static void syn_cmd_sync(exarg_T *eap, int syncing)
{
- char_u *arg_start = (char_u *)eap->arg;
+ char *arg_start = eap->arg;
char *arg_end;
- char_u *key = NULL;
- char_u *next_arg;
+ char *key = NULL;
+ char *next_arg;
int illegal = false;
int finished = false;
char *cpo_save;
if (ends_excmd(*arg_start)) {
- syn_cmd_list(eap, TRUE);
+ syn_cmd_list(eap, true);
return;
}
while (!ends_excmd(*arg_start)) {
- arg_end = (char *)skiptowhite(arg_start);
- next_arg = (char_u *)skipwhite(arg_end);
+ arg_end = skiptowhite(arg_start);
+ next_arg = skipwhite(arg_end);
xfree(key);
- key = vim_strnsave_up(arg_start, (size_t)(arg_end - (char *)arg_start));
- if (STRCMP(key, "CCOMMENT") == 0) {
+ key = vim_strnsave_up(arg_start, (size_t)(arg_end - arg_start));
+ if (strcmp(key, "CCOMMENT") == 0) {
if (!eap->skip) {
curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
}
if (!ends_excmd(*next_arg)) {
- arg_end = (char *)skiptowhite(next_arg);
+ arg_end = skiptowhite(next_arg);
if (!eap->skip) {
curwin->w_s->b_syn_sync_id =
- (int16_t)syn_check_group((char *)next_arg, (size_t)(arg_end - (char *)next_arg));
+ (int16_t)syn_check_group(next_arg, (size_t)(arg_end - next_arg));
}
- next_arg = (char_u *)skipwhite(arg_end);
+ next_arg = skipwhite(arg_end);
} else if (!eap->skip) {
curwin->w_s->b_syn_sync_id = (int16_t)syn_name2id("Comment");
}
- } else if (STRNCMP(key, "LINES", 5) == 0
- || STRNCMP(key, "MINLINES", 8) == 0
- || STRNCMP(key, "MAXLINES", 8) == 0
- || STRNCMP(key, "LINEBREAKS", 10) == 0) {
+ } else if (strncmp(key, "LINES", 5) == 0
+ || strncmp(key, "MINLINES", 8) == 0
+ || strncmp(key, "MAXLINES", 8) == 0
+ || strncmp(key, "LINEBREAKS", 10) == 0) {
if (key[4] == 'S') {
- arg_end = (char *)key + 6;
+ arg_end = key + 6;
} else if (key[0] == 'L') {
- arg_end = (char *)key + 11;
+ arg_end = key + 11;
} else {
- arg_end = (char *)key + 9;
+ arg_end = key + 9;
}
if (arg_end[-1] != '=' || !ascii_isdigit(*arg_end)) {
- illegal = TRUE;
+ illegal = true;
break;
}
linenr_T n = getdigits_int32(&arg_end, false, 0);
@@ -5202,23 +4884,23 @@ static void syn_cmd_sync(exarg_T *eap, int syncing)
curwin->w_s->b_syn_sync_minlines = n;
}
}
- } else if (STRCMP(key, "FROMSTART") == 0) {
+ } else if (strcmp(key, "FROMSTART") == 0) {
if (!eap->skip) {
curwin->w_s->b_syn_sync_minlines = MAXLNUM;
curwin->w_s->b_syn_sync_maxlines = 0;
}
- } else if (STRCMP(key, "LINECONT") == 0) {
+ } else if (strcmp(key, "LINECONT") == 0) {
if (*next_arg == NUL) { // missing pattern
illegal = true;
break;
}
if (curwin->w_s->b_syn_linecont_pat != NULL) {
emsg(_("E403: syntax sync: line continuations pattern specified twice"));
- finished = TRUE;
+ finished = true;
break;
}
- arg_end = (char *)skip_regexp(next_arg + 1, *next_arg, true, NULL);
- if (*arg_end != (char)(*next_arg)) { // end delimiter not found
+ arg_end = skip_regexp(next_arg + 1, *next_arg, true);
+ if (*arg_end != *next_arg) { // end delimiter not found
illegal = true;
break;
}
@@ -5226,14 +4908,14 @@ static void syn_cmd_sync(exarg_T *eap, int syncing)
if (!eap->skip) {
// store the pattern and compiled regexp program
curwin->w_s->b_syn_linecont_pat =
- vim_strnsave(next_arg + 1, (size_t)(arg_end - (char *)next_arg) - 1);
+ xstrnsave(next_arg + 1, (size_t)(arg_end - next_arg) - 1);
curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
// Make 'cpoptions' empty, to avoid the 'l' flag
cpo_save = p_cpo;
- p_cpo = "";
+ p_cpo = empty_option;
curwin->w_s->b_syn_linecont_prog =
- vim_regcomp((char *)curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
+ vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
p_cpo = cpo_save;
syn_clear_time(&curwin->w_s->b_syn_linecont_time);
@@ -5243,19 +4925,19 @@ static void syn_cmd_sync(exarg_T *eap, int syncing)
break;
}
}
- next_arg = (char_u *)skipwhite(arg_end + 1);
+ next_arg = skipwhite(arg_end + 1);
} else {
- eap->arg = (char *)next_arg;
- if (STRCMP(key, "MATCH") == 0) {
- syn_cmd_match(eap, TRUE);
- } else if (STRCMP(key, "REGION") == 0) {
- syn_cmd_region(eap, TRUE);
- } else if (STRCMP(key, "CLEAR") == 0) {
- syn_cmd_clear(eap, TRUE);
+ eap->arg = next_arg;
+ if (strcmp(key, "MATCH") == 0) {
+ syn_cmd_match(eap, true);
+ } else if (strcmp(key, "REGION") == 0) {
+ syn_cmd_region(eap, true);
+ } else if (strcmp(key, "CLEAR") == 0) {
+ syn_cmd_clear(eap, true);
} else {
- illegal = TRUE;
+ illegal = true;
}
- finished = TRUE;
+ finished = true;
break;
}
arg_start = next_arg;
@@ -5264,8 +4946,8 @@ static void syn_cmd_sync(exarg_T *eap, int syncing)
if (illegal) {
semsg(_("E404: Illegal arguments: %s"), arg_start);
} else if (!finished) {
- eap->nextcmd = (char *)check_nextcmd(arg_start);
- redraw_curbuf_later(SOME_VALID);
+ eap->nextcmd = check_nextcmd(arg_start);
+ redraw_curbuf_later(UPD_SOME_VALID);
syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
}
}
@@ -5279,7 +4961,7 @@ static void syn_cmd_sync(exarg_T *eap, int syncing)
/// @param list where to store the resulting list, if not NULL, the list is silently skipped!
///
/// @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)
+static int get_id_list(char **const arg, const int keylen, int16_t **const list, const bool skip)
{
char *p = NULL;
char *end;
@@ -5296,7 +4978,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((char *)(*arg) + keylen);
+ p = skipwhite(*arg + keylen);
if (*p != '=') {
semsg(_("E405: Missing equal sign: %s"), *arg);
break;
@@ -5312,11 +4994,11 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis
do {
for (end = p; *end && !ascii_iswhite(*end) && *end != ','; end++) {}
char *const name = xmalloc((size_t)(end - p) + 3); // leave room for "^$"
- STRLCPY(name + 1, p, end - p + 1);
- if (STRCMP(name + 1, "ALLBUT") == 0
- || STRCMP(name + 1, "ALL") == 0
- || STRCMP(name + 1, "TOP") == 0
- || STRCMP(name + 1, "CONTAINED") == 0) {
+ xstrlcpy(name + 1, p, (size_t)(end - p) + 1);
+ if (strcmp(name + 1, "ALLBUT") == 0
+ || strcmp(name + 1, "ALL") == 0
+ || strcmp(name + 1, "TOP") == 0
+ || strcmp(name + 1, "CONTAINED") == 0) {
if (TOUPPER_ASC(**arg) != 'C') {
semsg(_("E407: %s not allowed here"), name + 1);
failed = true;
@@ -5345,12 +5027,10 @@ 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((char_u *)name + 2, (int)(end - p - 1));
+ id = syn_check_cluster(name + 2, (int)(end - p - 1));
}
} else {
- /*
- * Handle full group name.
- */
+ // Handle full group name.
if (strpbrk(name + 1, "\\.*^$~[") == NULL) {
id = syn_check_group((name + 1), (size_t)(end - p));
} else {
@@ -5364,10 +5044,10 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis
break;
}
- regmatch.rm_ic = TRUE;
+ regmatch.rm_ic = true;
id = 0;
for (int i = highlight_num_groups(); --i >= 0;) {
- if (vim_regexec(&regmatch, (char *)highlight_group_name(i), (colnr_T)0)) {
+ if (vim_regexec(&regmatch, highlight_group_name(i), (colnr_T)0)) {
if (round == 2) {
// Got more items than expected; can happen
// when adding items that match:
@@ -5421,7 +5101,7 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis
}
}
- *arg = (char_u *)p;
+ *arg = p;
if (failed || retval == NULL) {
xfree(retval);
return FAIL;
@@ -5435,9 +5115,7 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis
return OK;
}
-/*
- * Make a copy of an ID list.
- */
+// Make a copy of an ID list.
static int16_t *copy_id_list(const int16_t *const list)
{
if (list == NULL) {
@@ -5472,7 +5150,7 @@ static int in_id_list(stateitem_T *cur_si, int16_t *list, struct sp_syn *ssp, in
static int depth = 0;
int r;
- // If ssp has a "containedin" list and "cur_si" is in it, return TRUE.
+ // If ssp has a "containedin" list and "cur_si" is in it, return true.
if (cur_si != NULL && ssp->cont_in_list != NULL
&& !(cur_si->si_flags & HL_MATCH)) {
// Ignore transparent items without a contains argument. Double check
@@ -5486,54 +5164,48 @@ static int in_id_list(stateitem_T *cur_si, int16_t *list, struct sp_syn *ssp, in
&(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags &
HL_CONTAINED)) {
- return TRUE;
+ return true;
}
}
if (list == NULL) {
- return FALSE;
+ return false;
}
- /*
- * If list is ID_LIST_ALL, we are in a transparent item that isn't
- * inside anything. Only allow not-contained groups.
- */
+ // If list is ID_LIST_ALL, we are in a transparent item that isn't
+ // inside anything. Only allow not-contained groups.
if (list == ID_LIST_ALL) {
return !contained;
}
- /*
- * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
- * contains list. We also require that "id" is at the same ":syn include"
- * level as the list.
- */
+ // If the first item is "ALLBUT", return true if "id" is NOT in the
+ // contains list. We also require that "id" is at the same ":syn include"
+ // level as the list.
item = *list;
if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER) {
if (item < SYNID_TOP) {
// ALL or ALLBUT: accept all groups in the same file
if (item - SYNID_ALLBUT != ssp->inc_tag) {
- return FALSE;
+ return false;
}
} else if (item < SYNID_CONTAINED) {
// TOP: accept all not-contained groups in the same file
if (item - SYNID_TOP != ssp->inc_tag || contained) {
- return FALSE;
+ return false;
}
} else {
// CONTAINED: accept all contained groups in the same file
if (item - SYNID_CONTAINED != ssp->inc_tag || !contained) {
- return FALSE;
+ return false;
}
}
item = *++list;
- retval = FALSE;
+ retval = false;
} else {
- retval = TRUE;
+ retval = true;
}
- /*
- * Return "retval" if id is in the contains list.
- */
+ // Return "retval" if id is in the contains list.
while (item != 0) {
if (item == id) {
return retval;
@@ -5561,8 +5233,7 @@ struct subcommand {
void (*func)(exarg_T *, int); // function to call
};
-static struct subcommand subcommands[] =
-{
+static struct subcommand subcommands[] = {
{ "case", syn_cmd_case },
{ "clear", syn_cmd_clear },
{ "cluster", syn_cmd_cluster },
@@ -5585,21 +5256,19 @@ static struct subcommand subcommands[] =
{ NULL, NULL }
};
-/*
- * ":syntax".
- * This searches the subcommands[] table for the subcommand name, and calls a
- * syntax_subcommand() function to do the rest.
- */
+/// ":syntax".
+/// This searches the subcommands[] table for the subcommand name, and calls a
+/// syntax_subcommand() function to do the rest.
void ex_syntax(exarg_T *eap)
{
- char_u *arg = (char_u *)eap->arg;
- char_u *subcmd_end;
+ char *arg = eap->arg;
+ char *subcmd_end;
syn_cmdlinep = eap->cmdlinep;
// isolate subcommand name
for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); subcmd_end++) {}
- char_u *const subcmd_name = vim_strnsave(arg, (size_t)(subcmd_end - arg));
+ char *const subcmd_name = xstrnsave(arg, (size_t)(subcmd_end - arg));
if (eap->skip) { // skip error messages for all subcommands
emsg_skip++;
}
@@ -5608,8 +5277,8 @@ void ex_syntax(exarg_T *eap)
semsg(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
break;
}
- if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0) {
- eap->arg = skipwhite((char *)subcmd_end);
+ if (strcmp(subcmd_name, subcommands[i].name) == 0) {
+ eap->arg = skipwhite(subcmd_end);
(subcommands[i].func)(eap, false);
break;
}
@@ -5622,14 +5291,14 @@ void ex_syntax(exarg_T *eap)
void ex_ownsyntax(exarg_T *eap)
{
- char_u *old_value;
- char_u *new_value;
+ char *old_value;
+ char *new_value;
if (curwin->w_s == &curwin->w_buffer->b_s) {
curwin->w_s = xcalloc(1, sizeof(synblock_T));
hash_init(&curwin->w_s->b_keywtab);
hash_init(&curwin->w_s->b_keywtab_ic);
- // TODO: Keep the spell checking as it was. NOLINT(readability/todo)
+ // TODO(vim): Keep the spell checking as it was.
curwin->w_p_spell = false; // No spell checking
// make sure option values are "empty_option" instead of NULL
clear_string_option(&curwin->w_s->b_p_spc);
@@ -5642,7 +5311,7 @@ void ex_ownsyntax(exarg_T *eap)
// Save value of b:current_syntax.
old_value = get_var_value("b:current_syntax");
if (old_value != NULL) {
- old_value = vim_strsave(old_value);
+ old_value = xstrdup(old_value);
}
// Apply the "syntax" autocommand event, this finds and loads the syntax file.
@@ -5651,14 +5320,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", (char *)new_value);
+ set_internal_string_var("w:current_syntax", 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", (char *)old_value);
+ set_internal_string_var("b:current_syntax", old_value);
xfree(old_value);
}
}
@@ -5675,22 +5344,19 @@ static enum {
EXP_SUBCMD, // expand ":syn" sub-commands
EXP_CASE, // expand ":syn case" arguments
EXP_SPELL, // expand ":syn spell" arguments
- EXP_SYNC, // expand ":syn sync" arguments
+ EXP_SYNC, // expand ":syn sync" arguments
+ EXP_CLUSTER, // expand ":syn list @cluster" arguments
} expand_what;
-/*
- * Reset include_link, include_default, include_none to 0.
- * Called when we are done expanding.
- */
+// Reset include_link, include_default, include_none to 0.
+// Called when we are done expanding.
void reset_expand_highlight(void)
{
include_link = include_default = include_none = 0;
}
-/*
- * Handle command line completion for :match and :echohl command: Add "None"
- * as highlight group.
- */
+// Handle command line completion for :match and :echohl command: Add "None"
+// as highlight group.
void set_context_in_echohl_cmd(expand_T *xp, const char *arg)
{
xp->xp_context = EXPAND_HIGHLIGHT;
@@ -5698,9 +5364,7 @@ void set_context_in_echohl_cmd(expand_T *xp, const char *arg)
include_none = 1;
}
-/*
- * Handle command line completion for :syntax command.
- */
+// Handle command line completion for :syntax command.
void set_context_in_syntax_cmd(expand_T *xp, const char *arg)
{
// Default: expand subcommands.
@@ -5710,35 +5374,44 @@ void set_context_in_syntax_cmd(expand_T *xp, const char *arg)
include_link = 0;
include_default = 0;
+ if (*arg == NUL) {
+ return;
+ }
+
// (part of) subcommand already typed
- if (*arg != NUL) {
- const char *p = (const char *)skiptowhite((const char_u *)arg);
- if (*p != NUL) { // Past first word.
- 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;
- } else if (STRNICMP(arg, "spell", p - arg) == 0) {
- expand_what = EXP_SPELL;
- } else if (STRNICMP(arg, "sync", p - arg) == 0) {
- expand_what = EXP_SYNC;
- } else if (STRNICMP(arg, "keyword", p - arg) == 0
- || STRNICMP(arg, "region", p - arg) == 0
- || STRNICMP(arg, "match", p - arg) == 0
- || STRNICMP(arg, "list", p - arg) == 0) {
- xp->xp_context = EXPAND_HIGHLIGHT;
- } else {
- xp->xp_context = EXPAND_NOTHING;
- }
+ const char *p = (const char *)skiptowhite(arg);
+ if (*p == NUL) {
+ return;
+ }
+
+ // past first world
+ xp->xp_pattern = skipwhite(p);
+ if (*skiptowhite(xp->xp_pattern) != NUL) {
+ xp->xp_context = EXPAND_NOTHING;
+ } else if (STRNICMP(arg, "case", p - arg) == 0) {
+ expand_what = EXP_CASE;
+ } else if (STRNICMP(arg, "spell", p - arg) == 0) {
+ expand_what = EXP_SPELL;
+ } else if (STRNICMP(arg, "sync", p - arg) == 0) {
+ expand_what = EXP_SYNC;
+ } else if (STRNICMP(arg, "list", p - arg) == 0) {
+ p = skipwhite(p);
+ if (*p == '@') {
+ expand_what = EXP_CLUSTER;
+ } else {
+ xp->xp_context = EXPAND_HIGHLIGHT;
}
+ } else if (STRNICMP(arg, "keyword", p - arg) == 0
+ || STRNICMP(arg, "region", p - arg) == 0
+ || STRNICMP(arg, "match", p - arg) == 0) {
+ xp->xp_context = EXPAND_HIGHLIGHT;
+ } else {
+ xp->xp_context = EXPAND_NOTHING;
}
}
-/*
- * Function given to ExpandGeneric() to obtain the list syntax names for
- * expansion.
- */
+// Function given to ExpandGeneric() to obtain the list syntax names for
+// expansion.
char *get_syntax_name(expand_T *xp, int idx)
{
switch (expand_what) {
@@ -5760,6 +5433,14 @@ char *get_syntax_name(expand_T *xp, int idx)
"maxlines=", "minlines=", "region", NULL };
return sync_args[idx];
}
+ case EXP_CLUSTER:
+ if (idx < curwin->w_s->b_syn_clusters.ga_len) {
+ vim_snprintf(xp->xp_buf, EXPAND_BUF_LEN, "@%s",
+ SYN_CLSTR(curwin->w_s)[idx].scl_name);
+ return xp->xp_buf;
+ } else {
+ return NULL;
+ }
}
return NULL;
}
@@ -5786,12 +5467,10 @@ int syn_get_id(win_T *wp, linenr_T lnum, colnr_T col, int trans, bool *spellp, i
return trans ? current_trans_id : current_id;
}
-/*
- * Get extra information about the syntax item. Must be called right after
- * get_syntax_attr().
- * Stores the current item sequence nr in "*seqnrp".
- * Returns the current flags.
- */
+// Get extra information about the syntax item. Must be called right after
+// get_syntax_attr().
+// Stores the current item sequence nr in "*seqnrp".
+// Returns the current flags.
int get_syntax_info(int *seqnrp)
{
*seqnrp = current_seqnr;
@@ -5815,24 +5494,20 @@ int syn_get_concealed_id(win_T *wp, linenr_T lnum, colnr_T col)
return 0;
}
-/*
- * Return conceal substitution character
- */
+// Return conceal substitution character
int syn_get_sub_char(void)
{
return current_sub_char;
}
-/*
- * Return the syntax ID at position "i" in the current stack.
- * The caller must have called syn_get_id() before to fill the stack.
- * Returns -1 when "i" is out of range.
- */
+// Return the syntax ID at position "i" in the current stack.
+// The caller must have called syn_get_id() before to fill the stack.
+// Returns -1 when "i" is out of range.
int syn_get_stack_item(int i)
{
if (i >= current_state.ga_len) {
// Need to invalidate the state, because we didn't properly finish it
- // for the last character, "keep_state" was TRUE.
+ // for the last character, "keep_state" was true.
invalidate_current_state();
current_col = MAXCOL;
return -1;
@@ -5890,18 +5565,16 @@ int syn_get_foldlevel(win_T *wp, linenr_T lnum)
return level;
}
-/*
- * ":syntime".
- */
+// ":syntime".
void ex_syntime(exarg_T *eap)
{
- if (STRCMP(eap->arg, "on") == 0) {
+ if (strcmp(eap->arg, "on") == 0) {
syn_time_on = true;
- } else if (STRCMP(eap->arg, "off") == 0) {
+ } else if (strcmp(eap->arg, "off") == 0) {
syn_time_on = false;
- } else if (STRCMP(eap->arg, "clear") == 0) {
+ } else if (strcmp(eap->arg, "clear") == 0) {
syntime_clear();
- } else if (STRCMP(eap->arg, "report") == 0) {
+ } else if (strcmp(eap->arg, "report") == 0) {
syntime_report();
} else {
semsg(_(e_invarg2), eap->arg);
@@ -5916,9 +5589,7 @@ static void syn_clear_time(syn_time_T *st)
st->match = 0;
}
-/*
- * Clear the syntax timing for the current buffer.
- */
+// Clear the syntax timing for the current buffer.
static void syntime_clear(void)
{
synpat_T *spp;
@@ -5927,16 +5598,14 @@ static void syntime_clear(void)
msg(_(msg_no_items));
return;
}
- for (int idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx) {
+ for (int idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; idx++) {
spp = &(SYN_ITEMS(curwin->w_s)[idx]);
syn_clear_time(&spp->sp_time);
}
}
-/*
- * Function given to ExpandGeneric() to obtain the possible arguments of the
- * ":syntime {on,off,clear,report}" command.
- */
+// Function given to ExpandGeneric() to obtain the possible arguments of the
+// ":syntime {on,off,clear,report}" command.
char *get_syntime_arg(expand_T *xp, int idx)
{
switch (idx) {
@@ -5960,9 +5629,7 @@ static int syn_compare_syntime(const void *v1, const void *v2)
return profile_cmp(s1->total, s2->total);
}
-/*
- * Clear the syntax timing for the current buffer.
- */
+// Clear the syntax timing for the current buffer.
static void syntime_report(void)
{
if (!syntax_present(curwin)) {
@@ -5976,7 +5643,7 @@ static void syntime_report(void)
proftime_T total_total = profile_zero();
int total_count = 0;
time_entry_T *p;
- for (int idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx) {
+ for (int idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; idx++) {
synpat_T *spp = &(SYN_ITEMS(curwin->w_s)[idx]);
if (spp->sp_time.count > 0) {
p = GA_APPEND_VIA_PTR(time_entry_T, &ga);
@@ -6002,7 +5669,7 @@ static void syntime_report(void)
msg_puts_title(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
msg_puts("\n");
- for (int idx = 0; idx < ga.ga_len && !got_int; ++idx) {
+ for (int idx = 0; idx < ga.ga_len && !got_int; idx++) {
p = ((time_entry_T *)ga.ga_data) + idx;
msg_puts(profile_msg(p->total));
@@ -6020,7 +5687,7 @@ static void syntime_report(void)
msg_puts(profile_msg(p->average));
msg_puts(" ");
msg_advance(50);
- msg_outtrans((char *)highlight_group_name(p->id - 1));
+ msg_outtrans(highlight_group_name(p->id - 1));
msg_puts(" ");
msg_advance(69);
@@ -6030,8 +5697,8 @@ static void syntime_report(void)
} else {
len = Columns - 70;
}
- if (len > (int)STRLEN(p->pattern)) {
- len = (int)STRLEN(p->pattern);
+ if (len > (int)strlen(p->pattern)) {
+ len = (int)strlen(p->pattern);
}
msg_outtrans_len(p->pattern, len);
msg_puts("\n");
diff --git a/src/nvim/syntax.h b/src/nvim/syntax.h
index 0d890314c5..0a63392a04 100644
--- a/src/nvim/syntax.h
+++ b/src/nvim/syntax.h
@@ -6,6 +6,7 @@
#include "nvim/buffer_defs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/globals.h"
+#include "nvim/macros.h"
#define HL_CONTAINED 0x01 // not used on toplevel
#define HL_TRANSP 0x02 // has no highlighting
diff --git a/src/nvim/syntax_defs.h b/src/nvim/syntax_defs.h
index c656f21181..3f3101f7e3 100644
--- a/src/nvim/syntax_defs.h
+++ b/src/nvim/syntax_defs.h
@@ -30,7 +30,7 @@ struct keyentry {
int16_t *next_list; // ID list for next match (if non-zero)
int flags;
int k_char; // conceal substitute character
- char_u keyword[1]; // actually longer
+ char keyword[1]; // actually longer
};
// Struct used to store one state of the state stack.
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 65c56bf01b..197184c181 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -1,34 +1,41 @@
// 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
-/*
- * Code to handle tags and the tag stack
- */
+// Code to handle tags and the tag stack
#include <assert.h>
+#include <ctype.h>
#include <inttypes.h>
#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
#include "nvim/cursor.h"
#include "nvim/drawscreen.h"
-#include "nvim/edit.h"
#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds.h"
+#include "nvim/ex_cmds_defs.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/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/hashtab.h"
#include "nvim/help.h"
-#include "nvim/if_cscope.h"
+#include "nvim/highlight_defs.h"
#include "nvim/input.h"
#include "nvim/insexpand.h"
+#include "nvim/macros.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
@@ -38,49 +45,47 @@
#include "nvim/optionstr.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
+#include "nvim/os/os_defs.h"
#include "nvim/os/time.h"
-#include "nvim/os_unix.h"
#include "nvim/path.h"
+#include "nvim/pos.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/tag.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
#include "nvim/window.h"
-/*
- * Structure to hold pointers to various items in a tag line.
- */
+// Structure to hold pointers to various items in a tag line.
typedef struct tag_pointers {
// filled in by parse_tag_line():
- char_u *tagname; // start of tag name (skip "file:")
- char_u *tagname_end; // char after tag name
- char_u *fname; // first char of file name
- char_u *fname_end; // char after file name
- char_u *command; // first char of command
+ char *tagname; // start of tag name (skip "file:")
+ char *tagname_end; // char after tag name
+ char *fname; // first char of file name
+ char *fname_end; // char after file name
+ char *command; // first char of command
// filled in by parse_match():
- char_u *command_end; // first char after command
- char_u *tag_fname; // file name of the tags file. This is used
+ char *command_end; // first char after command
+ char *tag_fname; // file name of the tags file. This is used
// when 'tr' is set.
- char_u *tagkind; // "kind:" value
- char_u *tagkind_end; // end of tagkind
- char_u *user_data; // user_data string
- char_u *user_data_end; // end of user_data
- linenr_T tagline; // "line:" value
+ char *tagkind; // "kind:" value
+ char *tagkind_end; // end of tagkind
+ char *user_data; // user_data string
+ char *user_data_end; // end of user_data
+ linenr_T tagline; // "line:" value
} tagptrs_T;
-/*
- * Structure to hold info about the tag pattern being used.
- */
+// Structure to hold info about the tag pattern being used.
typedef struct {
- char_u *pat; // the pattern
- int len; // length of pat[]
- char_u *head; // start of pattern head
- int headlen; // length of head[]
- regmatch_T regmatch; // regexp program, may be NULL
+ char *pat; // the pattern
+ int len; // length of pat[]
+ char *head; // start of pattern head
+ int headlen; // length of head[]
+ regmatch_T regmatch; // regexp program, may be NULL
} pat_T;
// The matching tags are first stored in one of the hash tables. In
@@ -88,45 +93,163 @@ typedef struct {
// ht_match[] is used to find duplicates, ga_match[] to keep them in sequence.
// At the end, the matches from ga_match[] are concatenated, to make a list
// sorted on priority.
-#define MT_ST_CUR 0 // static match in current file
-#define MT_GL_CUR 1 // global match in current file
-#define MT_GL_OTH 2 // global match in other file
-#define MT_ST_OTH 3 // static match in other file
-#define MT_IC_OFF 4 // add for icase match
-#define MT_RE_OFF 8 // add for regexp match
-#define MT_MASK 7 // mask for printing priority
-#define MT_COUNT 16
+enum {
+ MT_ST_CUR = 0, // static match in current file
+ MT_GL_CUR = 1, // global match in current file
+ MT_GL_OTH = 2, // global match in other file
+ MT_ST_OTH = 3, // static match in other file
+ MT_IC_OFF = 4, // add for icase match
+ MT_RE_OFF = 8, // add for regexp match
+ MT_MASK = 7, // mask for printing priority
+ MT_COUNT = 16,
+};
static char *mt_names[MT_COUNT/2] =
{ "FSC", "F C", "F ", "FS ", " SC", " C", " ", " S " };
#define NOTAGFILE 99 // return value for jumpto_tag
-static char_u *nofile_fname = NULL; // fname for NOTAGFILE error
+static char *nofile_fname = NULL; // fname for NOTAGFILE error
+
+/// Return values used when reading lines from a tags file.
+typedef enum {
+ TAGS_READ_SUCCESS = 1,
+ TAGS_READ_EOF,
+ TAGS_READ_IGNORE,
+} tags_read_status_T;
+
+/// States used during a tags search
+typedef enum {
+ TS_START, ///< at start of file
+ TS_LINEAR, ///< linear searching forward, till EOF
+ TS_BINARY, ///< binary searching
+ TS_SKIP_BACK, ///< skipping backwards
+ TS_STEP_FORWARD, ///< stepping forwards
+} tagsearch_state_T;
+
+/// Binary search file offsets in a tags file
+typedef struct {
+ off_T low_offset; ///< offset for first char of first line that
+ ///< could match
+ off_T high_offset; ///< offset of char after last line that could
+ ///< match
+ off_T curr_offset; ///< Current file offset in search range
+ off_T curr_offset_used; ///< curr_offset used when skipping back
+ off_T match_offset; ///< Where the binary search found a tag
+ int low_char; ///< first char at low_offset
+ int high_char; ///< first char at high_offset
+} tagsearch_info_T;
+
+/// Return values used when matching tags against a pattern.
+typedef enum {
+ TAG_MATCH_SUCCESS = 1,
+ TAG_MATCH_FAIL,
+ TAG_MATCH_STOP,
+ TAG_MATCH_NEXT,
+} tagmatch_status_T;
+
+/// Arguments used for matching tags read from a tags file against a pattern.
+typedef struct {
+ int matchoff; ///< tag match offset
+ bool match_re; ///< true if the tag matches a regexp
+ bool match_no_ic; ///< true if the tag matches with case
+ bool has_re; ///< regular expression used
+ bool sortic; ///< tags file sorted ignoring case (foldcase)
+ bool sort_error; ///< tags file not sorted
+} findtags_match_args_T;
+
+/// State information used during a tag search
+typedef struct {
+ tagsearch_state_T state; ///< tag search state
+ bool stop_searching; ///< stop when match found or error
+ pat_T *orgpat; ///< holds unconverted pattern info
+ char *lbuf; ///< line buffer
+ int lbuf_size; ///< length of lbuf
+ char *tag_fname; ///< name of the tag file
+ FILE *fp; ///< current tags file pointer
+ int flags; ///< flags used for tag search
+ int tag_file_sorted; ///< !_TAG_FILE_SORTED value
+ bool get_searchpat; ///< used for 'showfulltag'
+ bool help_only; ///< only search for help tags
+ bool did_open; ///< did open a tag file
+ int mincount; ///< MAXCOL: find all matches
+ ///< other: minimal number of matches
+ bool linear; ///< do a linear search
+ vimconv_T vimconv;
+ char help_lang[3]; ///< lang of current tags file
+ int help_pri; ///< help language priority
+ char *help_lang_find; ///< lang to be found
+ bool is_txt; ///< flag of file extension
+ int match_count; ///< number of matches found
+ garray_T ga_match[MT_COUNT]; ///< stores matches in sequence
+ hashtab_T ht_match[MT_COUNT]; ///< stores matches by key
+} findtags_state_T;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "tag.c.generated.h"
#endif
-static char_u *bottommsg = (char_u *)N_("E555: at bottom of tag stack");
-static char_u *topmsg = (char_u *)N_("E556: at top of tag stack");
-static char_u *recurmsg
- = (char_u *)N_("E986: cannot modify the tag stack within tagfunc");
-static char_u *tfu_inv_ret_msg
- = (char_u *)N_("E987: invalid return value from tagfunc");
+static char *bottommsg = N_("E555: at bottom of tag stack");
+static char *topmsg = N_("E556: at top of tag stack");
+static char *recurmsg = N_("E986: cannot modify the tag stack within tagfunc");
+static char *tfu_inv_ret_msg = N_("E987: invalid return value from tagfunc");
+static char e_window_unexpectedly_close_while_searching_for_tags[]
+ = N_("E1299: Window unexpectedly closed while searching for tags");
-static char_u *tagmatchname = NULL; // name of last used tag
+static char *tagmatchname = NULL; // name of last used tag
-/*
- * Tag for preview window is remembered separately, to avoid messing up the
- * normal tagstack.
- */
+// Tag for preview window is remembered separately, to avoid messing up the
+// normal tagstack.
static taggy_T ptag_entry = { NULL, INIT_FMARK, 0, 0, NULL };
static int tfu_in_use = false; // disallow recursive call of tagfunc
+static Callback tfu_cb; // 'tagfunc' callback function
// Used instead of NUL to separate tag fields in the growarrays.
#define TAG_SEP 0x02
+/// Reads the 'tagfunc' option value and convert that to a callback value.
+/// Invoked when the 'tagfunc' option is set. The option value can be a name of
+/// a function (string), or function(<name>) or funcref(<name>) or a lambda.
+void set_tagfunc_option(char **errmsg)
+{
+ callback_free(&tfu_cb);
+ callback_free(&curbuf->b_tfu_cb);
+
+ if (*curbuf->b_p_tfu == NUL) {
+ return;
+ }
+
+ if (option_set_callback_func(curbuf->b_p_tfu, &tfu_cb) == FAIL) {
+ *errmsg = e_invarg;
+ }
+
+ callback_copy(&curbuf->b_tfu_cb, &tfu_cb);
+}
+
+#if defined(EXITFREE)
+void free_tagfunc_option(void)
+{
+ callback_free(&tfu_cb);
+}
+#endif
+
+/// Mark the global 'tagfunc' callback with "copyID" so that it is not garbage
+/// collected.
+bool set_ref_in_tagfunc(int copyID)
+{
+ return set_ref_in_callback(&tfu_cb, copyID, NULL, NULL);
+}
+
+/// Copy the global 'tagfunc' callback function to the buffer-local 'tagfunc'
+/// callback for 'buf'.
+void set_buflocal_tfu_callback(buf_T *buf)
+{
+ callback_free(&buf->b_tfu_cb);
+ if (tfu_cb.type != kCallbackNone) {
+ callback_copy(&buf->b_tfu_cb, &tfu_cb);
+ }
+}
+
/// Jump to tag; handling of tag commands and tag stack
///
/// *tag != NUL: ":tag {tag}", jump to new tag, add to tag stack
@@ -140,16 +263,13 @@ static int tfu_in_use = false; // disallow recursive call of tagfunc
/// type == DT_LAST: jump to last match of same tag
/// type == DT_SELECT: ":tselect [tag]", select tag from a list of all matches
/// type == DT_JUMP: ":tjump [tag]", jump to tag or select tag from a list
-/// type == DT_CSCOPE: use cscope to find the tag
/// type == DT_LTAG: use location list for displaying tag matches
/// type == DT_FREE: free cached matches
///
-/// for cscope, returns true if we jumped to tag or aborted, false otherwise
-///
/// @param tag tag (pattern) to jump to
/// @param forceit :ta with !
/// @param verbose print "tag not found" message
-bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
+void do_tag(char *tag, int type, int count, int forceit, int verbose)
{
taggy_T *tagstack = curwin->w_tagstack;
int tagstackidx = curwin->w_tagstackidx;
@@ -166,13 +286,13 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
int error_cur_match = 0;
int save_pos = false;
fmark_T saved_fmark;
- bool jumped_to_tag = false;
int new_num_matches;
char **new_matches;
int use_tagstack;
int skip_msg = false;
- char_u *buf_ffname = (char_u *)curbuf->b_ffname; // name for priority computation
+ char *buf_ffname = curbuf->b_ffname; // name for priority computation
int use_tfu = 1;
+ char *tofree = NULL;
// remember the matches for the last used tag
static int num_matches = 0;
@@ -182,16 +302,15 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
if (tfu_in_use) {
emsg(_(recurmsg));
- return false;
+ return;
}
#ifdef EXITFREE
if (type == DT_FREE) {
// remove the list of matches
FreeWild(num_matches, matches);
- cs_free_tags();
num_matches = 0;
- return false;
+ return;
}
#endif
@@ -215,7 +334,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
new_tag = true;
if (g_do_tagpreview != 0) {
tagstack_clear_entry(&ptag_entry);
- ptag_entry.tagname = vim_strsave(tag);
+ ptag_entry.tagname = xstrdup(tag);
}
} else {
if (g_do_tagpreview != 0) {
@@ -227,25 +346,21 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
// new pattern, add to the tag stack
if (*tag != NUL
&& (type == DT_TAG || type == DT_SELECT || type == DT_JUMP
- || type == DT_LTAG
- || type == DT_CSCOPE
- )) {
+ || type == DT_LTAG)) {
if (g_do_tagpreview != 0) {
if (ptag_entry.tagname != NULL
- && STRCMP(ptag_entry.tagname, tag) == 0) {
+ && strcmp(ptag_entry.tagname, tag) == 0) {
// Jumping to same tag: keep the current match, so that
// the CursorHold autocommand example works.
cur_match = ptag_entry.cur_match;
cur_fnum = ptag_entry.cur_fnum;
} else {
tagstack_clear_entry(&ptag_entry);
- ptag_entry.tagname = vim_strsave(tag);
+ ptag_entry.tagname = xstrdup(tag);
}
} else {
- /*
- * If the last used entry is not at the top, delete all tag
- * stack entries above it.
- */
+ // If the last used entry is not at the top, delete all tag
+ // stack entries above it.
while (tagstackidx < tagstacklen) {
tagstack_clear_entry(&tagstack[--tagstacklen]);
}
@@ -261,7 +376,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
}
// put the tag name in the tag stack
- tagstack[tagstackidx].tagname = vim_strsave(tag);
+ tagstack[tagstackidx].tagname = xstrdup(tag);
curwin->w_tagstacklen = tagstacklen;
@@ -270,8 +385,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
new_tag = true;
} else {
- if (
- g_do_tagpreview != 0 ? ptag_entry.tagname == NULL :
+ if (g_do_tagpreview != 0 ? ptag_entry.tagname == NULL :
tagstacklen == 0) {
// empty stack
emsg(_(e_tagstack));
@@ -299,10 +413,8 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
// tagstack before it's used.
saved_fmark = tagstack[tagstackidx].fmark;
if (saved_fmark.fnum != curbuf->b_fnum) {
- /*
- * Jump to other file. If this fails (e.g. because the
- * file was changed) keep original position in tag stack.
- */
+ // Jump to other file. If this fails (e.g. because the
+ // file was changed) keep original position in tag stack.
if (buflist_getfile(saved_fmark.fnum, saved_fmark.mark.lnum,
GETF_SETMARK, forceit) == FAIL) {
tagstackidx = oldtagstackidx; // back to old posn
@@ -324,7 +436,6 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
// remove the old list of matches
FreeWild(num_matches, matches);
- cs_free_tags();
num_matches = 0;
tag_freematch();
goto end_do_tag;
@@ -339,11 +450,9 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
// ":tag" (no argument): go to newer pattern
save_pos = true; // save the cursor position below
if ((tagstackidx += count - 1) >= tagstacklen) {
- /*
- * Beyond the last one, just give an error message and
- * go to the last one. Don't store the cursor
- * position.
- */
+ // Beyond the last one, just give an error message and
+ // go to the last one. Don't store the cursor
+ // position.
tagstackidx = tagstacklen - 1;
emsg(_(topmsg));
save_pos = false;
@@ -375,7 +484,6 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
cur_match = count - 1; break;
case DT_SELECT:
case DT_JUMP:
- case DT_CSCOPE:
case DT_LAST:
cur_match = MAXCOL - 1; break;
case DT_NEXT:
@@ -400,9 +508,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
ptag_entry.cur_fnum = cur_fnum;
}
} else {
- /*
- * For ":tag [arg]" or ":tselect" remember position before the jump.
- */
+ // For ":tag [arg]" or ":tselect" remember position before the jump.
saved_fmark = tagstack[tagstackidx].fmark;
if (save_pos) {
tagstack[tagstackidx].fmark.mark = curwin->w_cursor;
@@ -427,32 +533,33 @@ 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 = (char_u *)buf->b_ffname;
+ buf_ffname = buf->b_ffname;
}
}
- /*
- * Repeat searching for tags, when a file has not been found.
- */
+ // Repeat searching for tags, when a file has not been found.
for (;;) {
int other_name;
- char_u *name;
+ char *name;
// When desired match not found yet, try to find it (and others).
if (use_tagstack) {
- name = tagstack[tagstackidx].tagname;
+ // make a copy, the tagstack may change in 'tagfunc'
+ name = xstrdup(tagstack[tagstackidx].tagname);
+ xfree(tofree);
+ tofree = name;
} else if (g_do_tagpreview != 0) {
name = ptag_entry.tagname;
} else {
name = tag;
}
- other_name = (tagmatchname == NULL || STRCMP(tagmatchname, name) != 0);
+ other_name = (tagmatchname == NULL || strcmp(tagmatchname, name) != 0);
if (new_tag
|| (cur_match >= num_matches && max_num_matches != MAXCOL)
|| other_name) {
if (other_name) {
xfree(tagmatchname);
- tagmatchname = vim_strsave(name);
+ tagmatchname = xstrdup(name);
}
if (type == DT_SELECT || type == DT_JUMP
@@ -473,9 +580,6 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
flags = TAG_NOIC;
}
- if (type == DT_CSCOPE) {
- flags = TAG_CSCOPE;
- }
if (verbose) {
flags |= TAG_VERBOSE;
}
@@ -490,6 +594,15 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
// found: all matches found.
}
+ // A tag function may do anything, which may cause various
+ // information to become invalid. At least check for the tagstack
+ // to still be the same.
+ if (tagstack != curwin->w_tagstack) {
+ emsg(_(e_window_unexpectedly_close_while_searching_for_tags));
+ FreeWild(new_num_matches, new_matches);
+ break;
+ }
+
// If there already were some matches for the same name, move them
// to the start. Avoids that the order changes when using
// ":tnext" and jumping to another file.
@@ -501,15 +614,15 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
// Find the position of each old match in the new list. Need
// to use parse_match() to find the tag line.
for (j = 0; j < num_matches; j++) {
- parse_match((char_u *)matches[j], &tagp);
+ parse_match(matches[j], &tagp);
for (i = idx; i < new_num_matches; i++) {
- parse_match((char_u *)new_matches[i], &tagp2);
- if (STRCMP(tagp.tagname, tagp2.tagname) == 0) {
- char_u *p = (char_u *)new_matches[i];
+ parse_match(new_matches[i], &tagp2);
+ if (strcmp(tagp.tagname, tagp2.tagname) == 0) {
+ char *p = new_matches[i];
for (k = i; k > idx; k--) {
new_matches[k] = new_matches[k - 1];
}
- new_matches[idx++] = (char *)p;
+ new_matches[idx++] = p;
break;
}
}
@@ -528,10 +641,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
} else {
bool ask_for_selection = false;
- if (type == DT_CSCOPE && num_matches > 1) {
- cs_print_tags();
- ask_for_selection = true;
- } else if (type == DT_TAG && *tag != NUL) {
+ if (type == DT_TAG && *tag != NUL) {
// If a count is supplied to the ":tag <name>" command, then
// jump to count'th matching tag.
cur_match = count > 0 ? count - 1 : 0;
@@ -555,8 +665,6 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
tagstack[tagstackidx].fmark = saved_fmark;
tagstackidx = prevtagstackidx;
}
- cs_free_tags();
- jumped_to_tag = true;
break;
}
cur_match = i - 1;
@@ -584,12 +692,11 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
tagstack[tagstackidx].cur_fnum = cur_fnum;
// store user-provided data originating from tagfunc
- if (use_tfu && parse_match((char_u *)matches[cur_match], &tagp2) == OK
+ if (use_tfu && parse_match(matches[cur_match], &tagp2) == OK
&& tagp2.user_data) {
XFREE_CLEAR(tagstack[tagstackidx].user_data);
- tagstack[tagstackidx].user_data = vim_strnsave(tagp2.user_data,
- (size_t)(tagp2.user_data_end -
- tagp2.user_data));
+ tagstack[tagstackidx].user_data =
+ xstrnsave(tagp2.user_data, (size_t)(tagp2.user_data_end - tagp2.user_data));
}
tagstackidx++;
@@ -598,21 +705,18 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
ptag_entry.cur_fnum = cur_fnum;
}
- /*
- * Only when going to try the next match, report that the previous
- * file didn't exist. Otherwise an emsg() is given below.
- */
+ // Only when going to try the next match, report that the previous
+ // file didn't exist. Otherwise an emsg() is given below.
if (nofile_fname != NULL && error_cur_match != cur_match) {
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
&& (num_matches > 1 || ic)
&& !skip_msg) {
// Give an indication of the number of matching tags
- snprintf((char *)IObuff, sizeof(IObuff), _("tag %d of %d%s"),
+ snprintf(IObuff, sizeof(IObuff), _("tag %d of %d%s"),
cur_match + 1,
num_matches,
max_num_matches != MAXCOL ? _(" or more") : "");
@@ -624,11 +728,11 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
if (ic) {
msg_attr((const char *)IObuff, HL_ATTR(HLF_W));
} else {
- msg((char *)IObuff);
+ msg(IObuff);
}
msg_scroll = true; // Don't overwrite this message.
} else {
- give_warning((char *)IObuff, ic);
+ give_warning(IObuff, ic);
}
if (ic && !msg_scrolled && msg_silent == 0) {
ui_flush();
@@ -637,13 +741,11 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
}
// Let the SwapExists event know what tag we are jumping to.
- vim_snprintf((char *)IObuff, IOSIZE, ":ta %s\r", name);
- set_vim_var_string(VV_SWAPCOMMAND, (char *)IObuff, -1);
+ vim_snprintf(IObuff, IOSIZE, ":ta %s\r", name);
+ set_vim_var_string(VV_SWAPCOMMAND, IObuff, -1);
- /*
- * Jump to the desired match.
- */
- i = jumpto_tag((char_u *)matches[cur_match], forceit, type != DT_CSCOPE);
+ // Jump to the desired match.
+ i = jumpto_tag(matches[cur_match], forceit, true);
set_vim_var_string(VV_SWAPCOMMAND, NULL, -1);
@@ -673,7 +775,6 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
if (use_tagstack && tagstackidx > curwin->w_tagstacklen) {
tagstackidx = curwin->w_tagstackidx;
}
- jumped_to_tag = true;
}
}
break;
@@ -686,8 +787,7 @@ end_do_tag:
}
postponed_split = 0; // don't split next time
g_do_tagpreview = 0; // don't do tag preview next time
-
- return jumped_to_tag;
+ xfree(tofree);
}
// List all the matching tags.
@@ -696,15 +796,15 @@ static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char
taggy_T *tagstack = curwin->w_tagstack;
int tagstackidx = curwin->w_tagstackidx;
int i;
- char_u *p;
- char_u *command_end;
+ char *p;
+ char *command_end;
tagptrs_T tagp;
int taglen;
int attr;
// Assume that the first match indicates how long the tags can
// be, and align the file names to that.
- parse_match((char_u *)matches[0], &tagp);
+ parse_match(matches[0], &tagp);
taglen = (int)(tagp.tagname_end - tagp.tagname + 2);
if (taglen < 18) {
taglen = 18;
@@ -722,7 +822,7 @@ static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char
msg_puts_attr(_("file\n"), HL_ATTR(HLF_T));
for (i = 0; i < num_matches && !got_int; i++) {
- parse_match((char_u *)matches[i], &tagp);
+ parse_match(matches[i], &tagp);
if (!new_tag && (
(g_do_tagpreview != 0
&& i == ptag_entry.cur_match)
@@ -732,10 +832,10 @@ static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char
} else {
*IObuff = ' ';
}
- vim_snprintf((char *)IObuff + 1, IOSIZE - 1,
+ vim_snprintf(IObuff + 1, IOSIZE - 1,
"%2d %s ", i + 1,
mt_names[matches[i][0] & MT_MASK]);
- msg_puts((char *)IObuff);
+ msg_puts(IObuff);
if (tagp.tagkind != NULL) {
msg_outtrans_len(tagp.tagkind,
(int)(tagp.tagkind_end - tagp.tagkind));
@@ -772,21 +872,21 @@ static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char
}
// skip "file:" without a value (static tag)
- if (STRNCMP(p, "file:", 5) == 0 && ascii_isspace(p[5])) {
+ if (strncmp(p, "file:", 5) == 0 && ascii_isspace(p[5])) {
p += 5;
continue;
}
// skip "kind:<kind>" and "<kind>"
if (p == tagp.tagkind
|| (p + 5 == tagp.tagkind
- && STRNCMP(p, "kind:", 5) == 0)) {
+ && strncmp(p, "kind:", 5) == 0)) {
p = tagp.tagkind_end;
continue;
}
// print all other extra fields
attr = HL_ATTR(HLF_CM);
while (*p && *p != '\r' && *p != '\n') {
- if (msg_col + ptr2cells((char *)p) >= Columns) {
+ if (msg_col + ptr2cells(p) >= Columns) {
msg_putchar('\n');
if (got_int) {
break;
@@ -832,7 +932,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((char *)p)) > Columns) {
+ if (msg_col + (*p == TAB ? 1 : ptr2cells(p)) > Columns) {
msg_putchar('\n');
}
if (got_int) {
@@ -877,14 +977,14 @@ static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char
/// Add the matching tags to the location list for the current
/// window.
-static int add_llist_tags(char_u *tag, int num_matches, char **matches)
+static int add_llist_tags(char *tag, int num_matches, char **matches)
{
list_T *list;
- char_u tag_name[128 + 1];
- char_u *fname;
- char_u *cmd;
+ char tag_name[128 + 1];
+ char *fname;
+ char *cmd;
int i;
- char_u *p;
+ char *p;
tagptrs_T tagp;
fname = xmalloc(MAXPATHL + 1);
@@ -896,14 +996,14 @@ static int add_llist_tags(char_u *tag, int num_matches, char **matches)
long lnum;
dict_T *dict;
- parse_match((char_u *)matches[i], &tagp);
+ parse_match(matches[i], &tagp);
// Save the tag name
len = (int)(tagp.tagname_end - tagp.tagname);
if (len > 128) {
len = 128;
}
- STRLCPY(tag_name, tagp.tagname, len + 1);
+ xstrlcpy(tag_name, tagp.tagname, (size_t)len + 1);
tag_name[len] = NUL;
// Save the tag file name
@@ -911,17 +1011,17 @@ static int add_llist_tags(char_u *tag, int num_matches, char **matches)
if (p == NULL) {
continue;
}
- STRLCPY(fname, p, MAXPATHL);
+ xstrlcpy(fname, p, MAXPATHL);
XFREE_CLEAR(p);
// Get the line number or the search pattern used to locate
// the tag.
lnum = 0;
- if (isdigit(*tagp.command)) {
+ if (isdigit((uint8_t)(*tagp.command))) {
// Line number is used to locate the tag
- lnum = atol((char *)tagp.command);
+ lnum = atol(tagp.command);
} else {
- char_u *cmd_start, *cmd_end;
+ char *cmd_start, *cmd_end;
// Search pattern is used to locate the tag
@@ -969,7 +1069,7 @@ static int add_llist_tags(char_u *tag, int num_matches, char **matches)
if (cmd_len > (CMDBUFFSIZE - 5)) {
cmd_len = CMDBUFFSIZE - 5;
}
- snprintf((char *)cmd + len, (size_t)(CMDBUFFSIZE + 1 - len),
+ snprintf(cmd + len, (size_t)(CMDBUFFSIZE + 1 - len),
"%.*s", cmd_len, cmd_start);
len += cmd_len;
@@ -987,16 +1087,16 @@ static int add_llist_tags(char_u *tag, int num_matches, char **matches)
dict = tv_dict_alloc();
tv_list_append_dict(list, dict);
- tv_dict_add_str(dict, S_LEN("text"), (const char *)tag_name);
+ tv_dict_add_str(dict, S_LEN("text"), tag_name);
tv_dict_add_str(dict, S_LEN("filename"), (const char *)fname);
tv_dict_add_nr(dict, S_LEN("lnum"), lnum);
if (lnum == 0) {
- tv_dict_add_str(dict, S_LEN("pattern"), (const char *)cmd);
+ tv_dict_add_str(dict, S_LEN("pattern"), cmd);
}
}
- vim_snprintf((char *)IObuff, IOSIZE, "ltag %s", tag);
- set_errorlist(curwin, list, ' ', (char *)IObuff, NULL);
+ vim_snprintf(IObuff, IOSIZE, "ltag %s", tag);
+ set_errorlist(curwin, list, ' ', IObuff, NULL);
tv_list_free(list);
XFREE_CLEAR(fname);
@@ -1005,9 +1105,7 @@ static int add_llist_tags(char_u *tag, int num_matches, char **matches)
return OK;
}
-/*
- * Free cached tags.
- */
+// Free cached tags.
void tag_freematch(void)
{
XFREE_CLEAR(tagmatchname);
@@ -1023,20 +1121,18 @@ static void taglen_advance(int l)
}
}
-/*
- * Print the tag stack
- */
+// Print the tag stack
void do_tags(exarg_T *eap)
{
int i;
- char_u *name;
+ char *name;
taggy_T *tagstack = curwin->w_tagstack;
int tagstackidx = curwin->w_tagstackidx;
int tagstacklen = curwin->w_tagstacklen;
// Highlight title
msg_puts_title(_("\n # TO tag FROM line in file/text"));
- for (i = 0; i < tagstacklen; ++i) {
+ for (i = 0; i < tagstacklen; i++) {
if (tagstack[i].tagname != NULL) {
name = fm_getname(&(tagstack[i].fmark), 30);
if (name == NULL) { // file name not available
@@ -1044,35 +1140,32 @@ void do_tags(exarg_T *eap)
}
msg_putchar('\n');
- vim_snprintf((char *)IObuff, IOSIZE, "%c%2d %2d %-15s %5" PRIdLINENR " ",
+ vim_snprintf(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((char *)IObuff);
+ msg_outtrans(IObuff);
msg_outtrans_attr(name, tagstack[i].fmark.fnum == curbuf->b_fnum
? HL_ATTR(HLF_D) : 0);
xfree(name);
}
- ui_flush(); // show one line at a time
}
if (tagstackidx == tagstacklen) { // idx at top of stack
msg_puts("\n>");
}
}
-/*
- * Compare two strings, for length "len", ignoring case the ASCII way.
- * return 0 for match, < 0 for smaller, > 0 for bigger
- * Make sure case is folded to uppercase in comparison (like for 'sort -f')
- */
-static int tag_strnicmp(char_u *s1, char_u *s2, size_t len)
+// Compare two strings, for length "len", ignoring case the ASCII way.
+// return 0 for match, < 0 for smaller, > 0 for bigger
+// Make sure case is folded to uppercase in comparison (like for 'sort -f')
+static int tag_strnicmp(char *s1, char *s2, size_t len)
{
int i;
while (len > 0) {
- i = TOUPPER_ASC(*s1) - TOUPPER_ASC(*s2);
+ i = TOUPPER_ASC((uint8_t)(*s1)) - TOUPPER_ASC((uint8_t)(*s2));
if (i != 0) {
return i; // this character different
}
@@ -1086,9 +1179,7 @@ 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".
- */
+// Extract info from the tag search pattern "pats->pat".
static void prepare_pats(pat_T *pats, int has_re)
{
pats->head = pats->pat;
@@ -1105,7 +1196,8 @@ static void prepare_pats(pat_T *pats, int has_re)
pats->headlen = 0;
} else {
for (pats->headlen = 0; pats->head[pats->headlen] != NUL; pats->headlen++) {
- if (vim_strchr((p_magic ? ".[~*\\$" : "\\$"), pats->head[pats->headlen]) != NULL) {
+ if (vim_strchr(magic_isset() ? ".[~*\\$" : "\\$",
+ (uint8_t)pats->head[pats->headlen]) != NULL) {
break;
}
}
@@ -1116,7 +1208,7 @@ static void prepare_pats(pat_T *pats, int has_re)
}
if (has_re) {
- pats->regmatch.regprog = vim_regcomp((char *)pats->pat, p_magic ? RE_MAGIC : 0);
+ pats->regmatch.regprog = vim_regcomp(pats->pat, magic_isset() ? RE_MAGIC : 0);
} else {
pats->regmatch.regprog = NULL;
}
@@ -1133,8 +1225,7 @@ static void prepare_pats(pat_T *pats, int has_re)
/// @param match_count here the number of tags found will be placed
/// @param flags flags from find_tags (TAG_*)
/// @param buf_ffname name of buffer for priority
-static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int flags,
- char_u *buf_ffname)
+static int find_tagfunc_tags(char *pat, garray_T *ga, int *match_count, int flags, char *buf_ffname)
{
pos_T save_pos;
list_T *taglist;
@@ -1142,7 +1233,7 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl
int result = FAIL;
typval_T args[4];
typval_T rettv;
- char_u flagString[4];
+ char flagString[4];
taggy_T *tag = NULL;
if (curwin->w_tagstacklen > 0) {
@@ -1153,14 +1244,14 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl
}
}
- if (*curbuf->b_p_tfu == NUL) {
+ if (*curbuf->b_p_tfu == NUL || curbuf->b_tfu_cb.type == kCallbackNone) {
return FAIL;
}
args[0].v_type = VAR_STRING;
- args[0].vval.v_string = (char *)pat;
+ args[0].vval.v_string = pat;
args[1].v_type = VAR_STRING;
- args[1].vval.v_string = (char *)flagString;
+ args[1].vval.v_string = flagString;
// create 'info' dict argument
dict_T *const d = tv_dict_alloc_lock(VAR_FIXED);
@@ -1177,14 +1268,14 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl
args[3].v_type = VAR_UNKNOWN;
- vim_snprintf((char *)flagString, sizeof(flagString),
+ vim_snprintf(flagString, sizeof(flagString),
"%s%s%s",
g_tag_at_cursor ? "c": "",
flags & TAG_INS_COMP ? "i": "",
flags & TAG_REGEXP ? "r": "");
save_pos = curwin->w_cursor;
- result = call_vim_function((char *)curbuf->b_p_tfu, 3, args, &rettv);
+ result = callback_call(&curbuf->b_tfu_cb, 3, args, &rettv);
curwin->w_cursor = save_pos; // restore the cursor position
d->dv_refcount--;
@@ -1203,10 +1294,10 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl
taglist = rettv.vval.v_list;
TV_LIST_ITER_CONST(taglist, li, {
- char_u *res_name;
- char_u *res_fname;
- char_u *res_cmd;
- char_u *res_kind;
+ char *res_name;
+ char *res_fname;
+ char *res_cmd;
+ char *res_kind;
int has_extra = 0;
int name_only = flags & TAG_NAMES;
@@ -1222,34 +1313,34 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl
res_kind = NULL;
TV_DICT_ITER(TV_LIST_ITEM_TV(li)->vval.v_dict, di, {
- const char_u *dict_key = di->di_key;
+ const char *dict_key = (char *)di->di_key;
typval_T *tv = &di->di_tv;
if (tv->v_type != VAR_STRING || tv->vval.v_string == NULL) {
continue;
}
- len += STRLEN(tv->vval.v_string) + 1; // Space for "\tVALUE"
- if (!STRCMP(dict_key, "name")) {
- res_name = (char_u *)tv->vval.v_string;
+ len += strlen(tv->vval.v_string) + 1; // Space for "\tVALUE"
+ if (!strcmp(dict_key, "name")) {
+ res_name = tv->vval.v_string;
continue;
}
- if (!STRCMP(dict_key, "filename")) {
- res_fname = (char_u *)tv->vval.v_string;
+ if (!strcmp(dict_key, "filename")) {
+ res_fname = tv->vval.v_string;
continue;
}
- if (!STRCMP(dict_key, "cmd")) {
- res_cmd = (char_u *)tv->vval.v_string;
+ if (!strcmp(dict_key, "cmd")) {
+ res_cmd = tv->vval.v_string;
continue;
}
has_extra = 1;
- if (!STRCMP(dict_key, "kind")) {
- res_kind = (char_u *)tv->vval.v_string;
+ if (!strcmp(dict_key, "kind")) {
+ res_kind = tv->vval.v_string;
continue;
}
// Other elements will be stored as "\tKEY:VALUE"
// Allocate space for the key and the colon
- len += STRLEN(dict_key) + 1;
+ len += strlen(dict_key) + 1;
});
if (has_extra) {
@@ -1261,62 +1352,62 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl
break;
}
- char_u *const mfp = name_only ? vim_strsave(res_name) : xmalloc(len + 2);
+ char *const mfp = name_only ? xstrdup(res_name) : xmalloc(len + 2);
if (!name_only) {
- char_u *p = mfp;
+ char *p = mfp;
*p++ = MT_GL_OTH + 1; // mtt
*p++ = TAG_SEP; // no tag file name
STRCPY(p, res_name);
- p += STRLEN(p);
+ p += strlen(p);
*p++ = TAB;
STRCPY(p, res_fname);
- p += STRLEN(p);
+ p += strlen(p);
*p++ = TAB;
STRCPY(p, res_cmd);
- p += STRLEN(p);
+ p += strlen(p);
if (has_extra) {
STRCPY(p, ";\"");
- p += STRLEN(p);
+ p += strlen(p);
if (res_kind) {
*p++ = TAB;
STRCPY(p, res_kind);
- p += STRLEN(p);
+ p += strlen(p);
}
TV_DICT_ITER(TV_LIST_ITEM_TV(li)->vval.v_dict, di, {
- const char_u *dict_key = di->di_key;
+ const char *dict_key = (char *)di->di_key;
typval_T *tv = &di->di_tv;
if (tv->v_type != VAR_STRING || tv->vval.v_string == NULL) {
continue;
}
- if (!STRCMP(dict_key, "name")) {
+ if (!strcmp(dict_key, "name")) {
continue;
}
- if (!STRCMP(dict_key, "filename")) {
+ if (!strcmp(dict_key, "filename")) {
continue;
}
- if (!STRCMP(dict_key, "cmd")) {
+ if (!strcmp(dict_key, "cmd")) {
continue;
}
- if (!STRCMP(dict_key, "kind")) {
+ if (!strcmp(dict_key, "kind")) {
continue;
}
*p++ = TAB;
STRCPY(p, dict_key);
- p += STRLEN(p);
+ p += strlen(p);
STRCPY(p, ":");
- p += STRLEN(p);
+ p += strlen(p);
STRCPY(p, tv->vval.v_string);
- p += STRLEN(p);
+ p += strlen(p);
});
}
}
@@ -1334,6 +1425,873 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl
return result;
}
+/// Initialize the state used by find_tags()
+static void findtags_state_init(findtags_state_T *st, char *pat, int flags, int mincount)
+{
+ st->tag_fname = xmalloc(MAXPATHL + 1);
+ st->fp = NULL;
+ st->orgpat = xmalloc(sizeof(pat_T));
+ st->orgpat->pat = pat;
+ st->orgpat->len = (int)strlen(pat);
+ st->orgpat->regmatch.regprog = NULL;
+ st->flags = flags;
+ st->tag_file_sorted = NUL;
+ st->help_lang_find = NULL;
+ st->is_txt = false;
+ st->did_open = false;
+ st->help_only = (flags & TAG_HELP);
+ st->get_searchpat = false;
+ st->help_lang[0] = NUL;
+ st->help_pri = 0;
+ st->mincount = mincount;
+ st->lbuf_size = LSIZE;
+ st->lbuf = xmalloc((size_t)st->lbuf_size);
+ st->match_count = 0;
+ st->stop_searching = false;
+
+ for (int mtt = 0; mtt < MT_COUNT; mtt++) {
+ ga_init(&st->ga_match[mtt], sizeof(char *), 100);
+ hash_init(&st->ht_match[mtt]);
+ }
+}
+
+/// Free the state used by find_tags()
+static void findtags_state_free(findtags_state_T *st)
+{
+ xfree(st->tag_fname);
+ xfree(st->lbuf);
+ vim_regfree(st->orgpat->regmatch.regprog);
+ xfree(st->orgpat);
+}
+
+/// Initialize the language and priority used for searching tags in a Vim help
+/// file.
+/// Returns true to process the help file for tags and false to skip the file.
+static bool findtags_in_help_init(findtags_state_T *st)
+{
+ int i;
+
+ // Keep "en" as the language if the file extension is ".txt"
+ if (st->is_txt) {
+ STRCPY(st->help_lang, "en");
+ } else {
+ // Prefer help tags according to 'helplang'. Put the two-letter
+ // language name in help_lang[].
+ i = (int)strlen(st->tag_fname);
+ if (i > 3 && st->tag_fname[i - 3] == '-') {
+ xstrlcpy(st->help_lang, st->tag_fname + i - 2, 3);
+ } else {
+ STRCPY(st->help_lang, "en");
+ }
+ }
+ // When searching for a specific language skip tags files for other
+ // languages.
+ if (st->help_lang_find != NULL
+ && STRICMP(st->help_lang, st->help_lang_find) != 0) {
+ return false;
+ }
+
+ // For CTRL-] in a help file prefer a match with the same language.
+ if ((st->flags & TAG_KEEP_LANG)
+ && st->help_lang_find == NULL
+ && curbuf->b_fname != NULL
+ && (i = (int)strlen(curbuf->b_fname)) > 4
+ && curbuf->b_fname[i - 1] == 'x'
+ && curbuf->b_fname[i - 4] == '.'
+ && STRNICMP(curbuf->b_fname + i - 3, st->help_lang, 2) == 0) {
+ st->help_pri = 0;
+ } else {
+ st->help_pri = 1;
+ char *s;
+ for (s = p_hlg; *s != NUL; s++) {
+ if (STRNICMP(s, st->help_lang, 2) == 0) {
+ break;
+ }
+ st->help_pri++;
+ if ((s = vim_strchr(s, ',')) == NULL) {
+ break;
+ }
+ }
+ if (s == NULL || *s == NUL) {
+ // Language not in 'helplang': use last, prefer English, unless
+ // found already.
+ st->help_pri++;
+ if (STRICMP(st->help_lang, "en") != 0) {
+ st->help_pri++;
+ }
+ }
+ }
+
+ return true;
+}
+
+/// Use the function set in 'tagfunc' (if configured and enabled) to get the
+/// tags.
+/// Return OK if at least 1 tag has been successfully found, NOTDONE if the
+/// 'tagfunc' is not used or the 'tagfunc' returns v:null and FAIL otherwise.
+static int findtags_apply_tfu(findtags_state_T *st, char *pat, char *buf_ffname)
+{
+ const bool use_tfu = ((st->flags & TAG_NO_TAGFUNC) == 0);
+
+ if (!use_tfu || tfu_in_use || *curbuf->b_p_tfu == NUL) {
+ return NOTDONE;
+ }
+
+ tfu_in_use = true;
+ int retval = find_tagfunc_tags(pat, st->ga_match, &st->match_count,
+ st->flags, buf_ffname);
+ tfu_in_use = false;
+
+ return retval;
+}
+
+/// Read the next line from a tags file.
+/// Returns TAGS_READ_SUCCESS if a tags line is successfully read and should be
+/// processed.
+/// Returns TAGS_READ_EOF if the end of file is reached.
+/// Returns TAGS_READ_IGNORE if the current line should be ignored (used when
+/// reached end of a emacs included tags file)
+static tags_read_status_T findtags_get_next_line(findtags_state_T *st, tagsearch_info_T *sinfo_p)
+{
+ int eof;
+ off_T offset;
+
+ // For binary search: compute the next offset to use.
+ if (st->state == TS_BINARY) {
+ offset = sinfo_p->low_offset + ((sinfo_p->high_offset - sinfo_p->low_offset) / 2);
+ if (offset == sinfo_p->curr_offset) {
+ return TAGS_READ_EOF; // End the binary search without a match.
+ } else {
+ sinfo_p->curr_offset = offset;
+ }
+ } else if (st->state == TS_SKIP_BACK) {
+ // Skipping back (after a match during binary search).
+ sinfo_p->curr_offset -= st->lbuf_size * 2;
+ if (sinfo_p->curr_offset < 0) {
+ sinfo_p->curr_offset = 0;
+ rewind(st->fp);
+ st->state = TS_STEP_FORWARD;
+ }
+ }
+
+ // When jumping around in the file, first read a line to find the
+ // start of the next line.
+ if (st->state == TS_BINARY || st->state == TS_SKIP_BACK) {
+ // Adjust the search file offset to the correct position
+ sinfo_p->curr_offset_used = sinfo_p->curr_offset;
+ vim_ignored = vim_fseek(st->fp, sinfo_p->curr_offset, SEEK_SET);
+ eof = vim_fgets(st->lbuf, st->lbuf_size, st->fp);
+ if (!eof && sinfo_p->curr_offset != 0) {
+ sinfo_p->curr_offset = vim_ftell(st->fp);
+ if (sinfo_p->curr_offset == sinfo_p->high_offset) {
+ // oops, gone a bit too far; try from low offset
+ vim_ignored = vim_fseek(st->fp, sinfo_p->low_offset, SEEK_SET);
+ sinfo_p->curr_offset = sinfo_p->low_offset;
+ }
+ eof = vim_fgets(st->lbuf, st->lbuf_size, st->fp);
+ }
+ // skip empty and blank lines
+ while (!eof && vim_isblankline(st->lbuf)) {
+ sinfo_p->curr_offset = vim_ftell(st->fp);
+ eof = vim_fgets(st->lbuf, st->lbuf_size, st->fp);
+ }
+ if (eof) {
+ // Hit end of file. Skip backwards.
+ st->state = TS_SKIP_BACK;
+ sinfo_p->match_offset = vim_ftell(st->fp);
+ sinfo_p->curr_offset = sinfo_p->curr_offset_used;
+ return TAGS_READ_IGNORE;
+ }
+ } else {
+ // Not jumping around in the file: Read the next line.
+
+ // skip empty and blank lines
+ do {
+ eof = vim_fgets(st->lbuf, st->lbuf_size, st->fp);
+ } while (!eof && vim_isblankline(st->lbuf));
+
+ if (eof) {
+ return TAGS_READ_EOF;
+ }
+ }
+
+ return TAGS_READ_SUCCESS;
+}
+
+/// Parse a tags file header line in "st->lbuf".
+/// Returns true if the current line in st->lbuf is not a tags header line and
+/// should be parsed as a regular tag line. Returns false if the line is a
+/// header line and the next header line should be read.
+static bool findtags_hdr_parse(findtags_state_T *st)
+{
+ // Header lines in a tags file start with "!_TAG_"
+ if (strncmp(st->lbuf, "!_TAG_", 6) != 0) {
+ // Non-header item before the header, e.g. "!" itself.
+ return true;
+ }
+
+ // Process the header line.
+ if (strncmp(st->lbuf, "!_TAG_FILE_SORTED\t", 18) == 0) {
+ st->tag_file_sorted = (uint8_t)st->lbuf[18];
+ }
+ if (strncmp(st->lbuf, "!_TAG_FILE_ENCODING\t", 20) == 0) {
+ // Prepare to convert every line from the specified encoding to
+ // 'encoding'.
+ char *p;
+ for (p = st->lbuf + 20; *p > ' ' && *p < 127; p++) {}
+ *p = NUL;
+ convert_setup(&st->vimconv, st->lbuf + 20, p_enc);
+ }
+
+ // Read the next line. Unrecognized flags are ignored.
+ return false;
+}
+
+/// Handler to initialize the state when starting to process a new tags file.
+/// Called in the TS_START state when finding tags from a tags file.
+/// Returns true if the line read from the tags file should be parsed and
+/// false if the line should be ignored.
+static bool findtags_start_state_handler(findtags_state_T *st, bool *sortic,
+ tagsearch_info_T *sinfo_p)
+{
+ const bool noic = (st->flags & TAG_NOIC);
+
+ // The header ends when the line sorts below "!_TAG_". When case is
+ // folded lower case letters sort before "_".
+ if (strncmp(st->lbuf, "!_TAG_", 6) <= 0
+ || (st->lbuf[0] == '!' && ASCII_ISLOWER(st->lbuf[1]))) {
+ return findtags_hdr_parse(st);
+ }
+
+ // Headers ends.
+
+ // When there is no tag head, or ignoring case, need to do a
+ // linear search.
+ // When no "!_TAG_" is found, default to binary search. If
+ // the tag file isn't sorted, the second loop will find it.
+ // When "!_TAG_FILE_SORTED" found: start binary search if
+ // flag set.
+ if (st->linear) {
+ st->state = TS_LINEAR;
+ } else if (st->tag_file_sorted == NUL) {
+ st->state = TS_BINARY;
+ } else if (st->tag_file_sorted == '1') {
+ st->state = TS_BINARY;
+ } else if (st->tag_file_sorted == '2') {
+ st->state = TS_BINARY;
+ *sortic = true;
+ st->orgpat->regmatch.rm_ic = (p_ic || !noic);
+ } else {
+ st->state = TS_LINEAR;
+ }
+
+ if (st->state == TS_BINARY && st->orgpat->regmatch.rm_ic && !*sortic) {
+ // Binary search won't work for ignoring case, use linear
+ // search.
+ st->linear = true;
+ st->state = TS_LINEAR;
+ }
+
+ // When starting a binary search, get the size of the file and
+ // compute the first offset.
+ if (st->state == TS_BINARY) {
+ if (vim_fseek(st->fp, 0, SEEK_END) != 0) {
+ // can't seek, don't use binary search
+ st->state = TS_LINEAR;
+ } else {
+ // Get the tag file size.
+ // Don't use lseek(), it doesn't work
+ // properly on MacOS Catalina.
+ const off_T filesize = vim_ftell(st->fp);
+ vim_ignored = vim_fseek(st->fp, 0, SEEK_SET);
+
+ // Calculate the first read offset in the file. Start
+ // the search in the middle of the file.
+ sinfo_p->low_offset = 0;
+ sinfo_p->low_char = 0;
+ sinfo_p->high_offset = filesize;
+ sinfo_p->curr_offset = 0;
+ sinfo_p->high_char = 0xff;
+ }
+ return false;
+ }
+
+ return true;
+}
+
+/// Parse a tag line read from a tags file.
+/// Also compares the tag name in "tagpp->tagname" with a search pattern in
+/// "st->orgpat->head" as a quick check if the tag may match.
+/// Returns:
+/// - TAG_MATCH_SUCCESS if the tag may match
+/// - TAG_MATCH_FAIL if the tag doesn't match
+/// - TAG_MATCH_NEXT to look for the next matching tag (used in a binary search)
+/// - TAG_MATCH_STOP if all the tags are processed without a match.
+/// Uses the values in "margs" for doing the comparison.
+static tagmatch_status_T findtags_parse_line(findtags_state_T *st, tagptrs_T *tagpp,
+ findtags_match_args_T *margs,
+ tagsearch_info_T *sinfo_p)
+{
+ int status;
+ int i;
+ int cmplen;
+ int tagcmp;
+
+ // Figure out where the different strings are in this line.
+ // For "normal" tags: Do a quick check if the tag matches.
+ // This speeds up tag searching a lot!
+ if (st->orgpat->headlen) {
+ CLEAR_FIELD(*tagpp);
+ tagpp->tagname = st->lbuf;
+ tagpp->tagname_end = vim_strchr(st->lbuf, TAB);
+ if (tagpp->tagname_end == NULL) {
+ // Corrupted tag line.
+ return TAG_MATCH_FAIL;
+ }
+
+ // Skip this line if the length of the tag is different and
+ // there is no regexp, or the tag is too short.
+ cmplen = (int)(tagpp->tagname_end - tagpp->tagname);
+ if (p_tl != 0 && cmplen > p_tl) { // adjust for 'taglength'
+ cmplen = (int)p_tl;
+ }
+ if ((st->flags & TAG_REGEXP) && st->orgpat->headlen < cmplen) {
+ cmplen = st->orgpat->headlen;
+ } else if (st->state == TS_LINEAR && st->orgpat->headlen != cmplen) {
+ return TAG_MATCH_NEXT;
+ }
+
+ if (st->state == TS_BINARY) {
+ // Simplistic check for unsorted tags file.
+ i = (int)tagpp->tagname[0];
+ if (margs->sortic) {
+ i = TOUPPER_ASC(tagpp->tagname[0]);
+ }
+ if (i < sinfo_p->low_char || i > sinfo_p->high_char) {
+ margs->sort_error = true;
+ }
+
+ // Compare the current tag with the searched tag.
+ if (margs->sortic) {
+ tagcmp = tag_strnicmp(tagpp->tagname, st->orgpat->head,
+ (size_t)cmplen);
+ } else {
+ tagcmp = strncmp(tagpp->tagname, st->orgpat->head, (size_t)cmplen);
+ }
+
+ // A match with a shorter tag means to search forward.
+ // A match with a longer tag means to search backward.
+ if (tagcmp == 0) {
+ if (cmplen < st->orgpat->headlen) {
+ tagcmp = -1;
+ } else if (cmplen > st->orgpat->headlen) {
+ tagcmp = 1;
+ }
+ }
+
+ if (tagcmp == 0) {
+ // We've located the tag, now skip back and search
+ // forward until the first matching tag is found.
+ st->state = TS_SKIP_BACK;
+ sinfo_p->match_offset = sinfo_p->curr_offset;
+ return TAG_MATCH_NEXT;
+ }
+ if (tagcmp < 0) {
+ sinfo_p->curr_offset = vim_ftell(st->fp);
+ if (sinfo_p->curr_offset < sinfo_p->high_offset) {
+ sinfo_p->low_offset = sinfo_p->curr_offset;
+ if (margs->sortic) {
+ sinfo_p->low_char = TOUPPER_ASC(tagpp->tagname[0]);
+ } else {
+ sinfo_p->low_char = (uint8_t)tagpp->tagname[0];
+ }
+ return TAG_MATCH_NEXT;
+ }
+ }
+ if (tagcmp > 0 && sinfo_p->curr_offset != sinfo_p->high_offset) {
+ sinfo_p->high_offset = sinfo_p->curr_offset;
+ if (margs->sortic) {
+ sinfo_p->high_char = TOUPPER_ASC(tagpp->tagname[0]);
+ } else {
+ sinfo_p->high_char = (uint8_t)tagpp->tagname[0];
+ }
+ return TAG_MATCH_NEXT;
+ }
+
+ // No match yet and are at the end of the binary search.
+ return TAG_MATCH_STOP;
+ } else if (st->state == TS_SKIP_BACK) {
+ assert(cmplen >= 0);
+ if (mb_strnicmp(tagpp->tagname, st->orgpat->head, (size_t)cmplen) != 0) {
+ st->state = TS_STEP_FORWARD;
+ } else {
+ // Have to skip back more. Restore the curr_offset
+ // used, otherwise we get stuck at a long line.
+ sinfo_p->curr_offset = sinfo_p->curr_offset_used;
+ }
+ return TAG_MATCH_NEXT;
+ } else if (st->state == TS_STEP_FORWARD) {
+ assert(cmplen >= 0);
+ if (mb_strnicmp(tagpp->tagname, st->orgpat->head, (size_t)cmplen) != 0) {
+ if ((off_T)vim_ftell(st->fp) > sinfo_p->match_offset) {
+ return TAG_MATCH_STOP; // past last match
+ } else {
+ return TAG_MATCH_NEXT; // before first match
+ }
+ }
+ } else {
+ // skip this match if it can't match
+ assert(cmplen >= 0);
+ if (mb_strnicmp(tagpp->tagname, st->orgpat->head, (size_t)cmplen) != 0) {
+ return TAG_MATCH_NEXT;
+ }
+ }
+
+ // Can be a matching tag, isolate the file name and command.
+ tagpp->fname = tagpp->tagname_end + 1;
+ tagpp->fname_end = vim_strchr(tagpp->fname, TAB);
+ if (tagpp->fname_end == NULL) {
+ status = FAIL;
+ } else {
+ tagpp->command = tagpp->fname_end + 1;
+ status = OK;
+ }
+ } else {
+ status = parse_tag_line(st->lbuf, tagpp);
+ }
+
+ if (status == FAIL) {
+ return TAG_MATCH_FAIL;
+ }
+
+ return TAG_MATCH_SUCCESS;
+}
+
+/// Initialize the structure used for tag matching.
+static void findtags_matchargs_init(findtags_match_args_T *margs, int flags)
+{
+ margs->matchoff = 0; // match offset
+ margs->match_re = false; // match with regexp
+ margs->match_no_ic = false; // matches with case
+ margs->has_re = (flags & TAG_REGEXP); // regexp used
+ margs->sortic = false; // tag file sorted in nocase
+ margs->sort_error = false; // tags file not sorted
+}
+
+/// Compares the tag name in "tagpp->tagname" with a search pattern in
+/// "st->orgpat->pat".
+/// Returns true if the tag matches, false if the tag doesn't match.
+/// Uses the values in "margs" for doing the comparison.
+static bool findtags_match_tag(findtags_state_T *st, tagptrs_T *tagpp, findtags_match_args_T *margs)
+{
+ bool match = false;
+
+ // First try matching with the pattern literally (also when it is
+ // a regexp).
+ int cmplen = (int)(tagpp->tagname_end - tagpp->tagname);
+ if (p_tl != 0 && cmplen > p_tl) { // adjust for 'taglength'
+ cmplen = (int)p_tl;
+ }
+ // if tag length does not match, don't try comparing
+ if (st->orgpat->len != cmplen) {
+ match = false;
+ } else {
+ if (st->orgpat->regmatch.rm_ic) {
+ assert(cmplen >= 0);
+ match = mb_strnicmp(tagpp->tagname, st->orgpat->pat, (size_t)cmplen) == 0;
+ if (match) {
+ margs->match_no_ic = strncmp(tagpp->tagname, st->orgpat->pat, (size_t)cmplen) == 0;
+ }
+ } else {
+ match = strncmp(tagpp->tagname, st->orgpat->pat, (size_t)cmplen) == 0;
+ }
+ }
+
+ // Has a regexp: Also find tags matching regexp.
+ margs->match_re = false;
+ if (!match && st->orgpat->regmatch.regprog != NULL) {
+ char cc = *tagpp->tagname_end;
+ *tagpp->tagname_end = NUL;
+ match = vim_regexec(&st->orgpat->regmatch, tagpp->tagname, (colnr_T)0);
+ if (match) {
+ margs->matchoff = (int)(st->orgpat->regmatch.startp[0] - tagpp->tagname);
+ if (st->orgpat->regmatch.rm_ic) {
+ st->orgpat->regmatch.rm_ic = false;
+ margs->match_no_ic = vim_regexec(&st->orgpat->regmatch,
+ tagpp->tagname, (colnr_T)0);
+ st->orgpat->regmatch.rm_ic = true;
+ }
+ }
+ *tagpp->tagname_end = cc;
+ margs->match_re = true;
+ }
+
+ return match;
+}
+
+/// Convert the encoding of a line read from a tags file in "st->lbuf".
+/// Converting the pattern from 'enc' to the tags file encoding doesn't work,
+/// because characters are not recognized. The converted line is saved in
+/// st->lbuf.
+static void findtags_string_convert(findtags_state_T *st)
+{
+ char *conv_line = string_convert(&st->vimconv, st->lbuf, NULL);
+ if (conv_line == NULL) {
+ return;
+ }
+
+ // Copy or swap lbuf and conv_line.
+ int len = (int)strlen(conv_line) + 1;
+ if (len > st->lbuf_size) {
+ xfree(st->lbuf);
+ st->lbuf = conv_line;
+ st->lbuf_size = len;
+ } else {
+ STRCPY(st->lbuf, conv_line);
+ xfree(conv_line);
+ }
+}
+
+/// Add a matching tag found in a tags file to st->ht_match and st->ga_match.
+static void findtags_add_match(findtags_state_T *st, tagptrs_T *tagpp, findtags_match_args_T *margs,
+ char *buf_ffname, hash_T *hash)
+{
+ const bool name_only = (st->flags & TAG_NAMES);
+ int mtt;
+ size_t len = 0;
+ bool is_current; // file name matches
+ bool is_static; // current tag line is static
+ char *mfp;
+ char *p;
+ char *s;
+
+ // Decide in which array to store this match.
+ is_current = test_for_current(tagpp->fname, tagpp->fname_end,
+ st->tag_fname, buf_ffname);
+ is_static = test_for_static(tagpp);
+
+ // Decide in which of the sixteen tables to store this match.
+ if (is_static) {
+ if (is_current) {
+ mtt = MT_ST_CUR;
+ } else {
+ mtt = MT_ST_OTH;
+ }
+ } else {
+ if (is_current) {
+ mtt = MT_GL_CUR;
+ } else {
+ mtt = MT_GL_OTH;
+ }
+ }
+ if (st->orgpat->regmatch.rm_ic && !margs->match_no_ic) {
+ mtt += MT_IC_OFF;
+ }
+ if (margs->match_re) {
+ mtt += MT_RE_OFF;
+ }
+
+ // Add the found match in ht_match[mtt] and ga_match[mtt].
+ // Store the info we need later, which depends on the kind of
+ // tags we are dealing with.
+ if (st->help_only) {
+#define ML_EXTRA 3
+ // Append the help-heuristic number after the tagname, for
+ // sorting it later. The heuristic is ignored for
+ // detecting duplicates.
+ // The format is {tagname}@{lang}NUL{heuristic}NUL
+ *tagpp->tagname_end = NUL;
+ len = (size_t)(tagpp->tagname_end - tagpp->tagname);
+ mfp = xmalloc(sizeof(char) + len + 10 + ML_EXTRA + 1);
+
+ p = mfp;
+ STRCPY(p, tagpp->tagname);
+ p[len] = '@';
+ STRCPY(p + len + 1, st->help_lang);
+ snprintf(p + len + 1 + ML_EXTRA, strlen(p) + len + 1 + ML_EXTRA, "%06d",
+ help_heuristic(tagpp->tagname,
+ margs->match_re ? margs->matchoff : 0,
+ !margs->match_no_ic) + st->help_pri);
+
+ *tagpp->tagname_end = TAB;
+ } else if (name_only) {
+ if (st->get_searchpat) {
+ char *temp_end = tagpp->command;
+
+ if (*temp_end == '/') {
+ while (*temp_end && *temp_end != '\r'
+ && *temp_end != '\n'
+ && *temp_end != '$') {
+ temp_end++;
+ }
+ }
+
+ if (tagpp->command + 2 < temp_end) {
+ len = (size_t)(temp_end - tagpp->command - 2);
+ mfp = xmalloc(len + 2);
+ xstrlcpy(mfp, tagpp->command + 2, len + 1);
+ } else {
+ mfp = NULL;
+ }
+ st->get_searchpat = false;
+ } else {
+ len = (size_t)(tagpp->tagname_end - tagpp->tagname);
+ mfp = xmalloc(sizeof(char) + len + 1);
+ xstrlcpy(mfp, tagpp->tagname, len + 1);
+
+ // if wanted, re-read line to get long form too
+ if (State & MODE_INSERT) {
+ st->get_searchpat = p_sft;
+ }
+ }
+ } else {
+ size_t tag_fname_len = strlen(st->tag_fname);
+ // Save the tag in a buffer.
+ // Use 0x02 to separate fields (Can't use NUL, because the
+ // hash key is terminated by NUL).
+ // Emacs tag: <mtt><tag_fname><0x02><ebuf><0x02><lbuf><NUL>
+ // 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 = tag_fname_len + strlen(st->lbuf) + 3;
+ mfp = xmalloc(sizeof(char) + len + 1);
+ p = mfp;
+ p[0] = (char)(mtt + 1);
+ STRCPY(p + 1, st->tag_fname);
+#ifdef BACKSLASH_IN_FILENAME
+ // Ignore differences in slashes, avoid adding
+ // both path/file and path\file.
+ slash_adjust(p + 1);
+#endif
+ p[tag_fname_len + 1] = TAG_SEP;
+ s = p + 1 + tag_fname_len + 1;
+ STRCPY(s, st->lbuf);
+ }
+
+ if (mfp != NULL) {
+ hashitem_T *hi;
+
+ // Don't add identical matches.
+ // "mfp" is used as a hash key, there is a NUL byte to end
+ // the part that matters for comparing, more bytes may
+ // follow after it. E.g. help tags store the priority
+ // after the NUL.
+ *hash = hash_hash(mfp);
+ hi = hash_lookup(&st->ht_match[mtt], (const char *)mfp, strlen(mfp), *hash);
+ if (HASHITEM_EMPTY(hi)) {
+ hash_add_item(&st->ht_match[mtt], hi, mfp, *hash);
+ GA_APPEND(char *, &st->ga_match[mtt], mfp);
+ st->match_count++;
+ } else {
+ // duplicate tag, drop it
+ xfree(mfp);
+ }
+ }
+}
+
+/// Read and get all the tags from file st->tag_fname.
+/// Sets "st->stop_searching" to true to stop searching for additional tags.
+static void findtags_get_all_tags(findtags_state_T *st, findtags_match_args_T *margs,
+ char *buf_ffname)
+{
+ tagptrs_T tagp;
+ tagsearch_info_T search_info;
+ int retval;
+ hash_T hash = 0;
+
+ // This is only to avoid a compiler warning for using search_info
+ // uninitialised.
+ CLEAR_FIELD(search_info);
+
+ // Read and parse the lines in the file one by one
+ for (;;) {
+ // check for CTRL-C typed, more often when jumping around
+ if (st->state == TS_BINARY || st->state == TS_SKIP_BACK) {
+ line_breakcheck();
+ } else {
+ fast_breakcheck();
+ }
+ if ((st->flags & TAG_INS_COMP)) { // Double brackets for gcc
+ ins_compl_check_keys(30, false);
+ }
+ if (got_int || ins_compl_interrupted()) {
+ st->stop_searching = true;
+ break;
+ }
+ // When mincount is TAG_MANY, stop when enough matches have been
+ // found (for completion).
+ if (st->mincount == TAG_MANY && st->match_count >= TAG_MANY) {
+ st->stop_searching = true;
+ break;
+ }
+ if (st->get_searchpat) {
+ goto line_read_in;
+ }
+
+ retval = (int)findtags_get_next_line(st, &search_info);
+ if (retval == TAGS_READ_IGNORE) {
+ continue;
+ }
+ if (retval == TAGS_READ_EOF) {
+ break;
+ }
+
+line_read_in:
+
+ if (st->vimconv.vc_type != CONV_NONE) {
+ findtags_string_convert(st);
+ }
+
+ // When still at the start of the file, check for Emacs tags file
+ // format, and for "not sorted" flag.
+ if (st->state == TS_START) {
+ if (!findtags_start_state_handler(st, &margs->sortic, &search_info)) {
+ continue;
+ }
+ }
+
+ // When the line is too long the NUL will not be in the
+ // last-but-one byte (see vim_fgets()).
+ // Has been reported for Mozilla JS with extremely long names.
+ // In that case we need to increase lbuf_size.
+ if (st->lbuf[st->lbuf_size - 2] != NUL) {
+ st->lbuf_size *= 2;
+ xfree(st->lbuf);
+ st->lbuf = xmalloc((size_t)st->lbuf_size);
+
+ if (st->state == TS_STEP_FORWARD || st->state == TS_LINEAR) {
+ // Seek to the same position to read the same line again
+ vim_ignored = vim_fseek(st->fp, search_info.curr_offset, SEEK_SET);
+ }
+ // this will try the same thing again, make sure the offset is
+ // different
+ search_info.curr_offset = 0;
+ continue;
+ }
+
+ retval = (int)findtags_parse_line(st, &tagp, margs, &search_info);
+ if (retval == TAG_MATCH_NEXT) {
+ continue;
+ }
+ if (retval == TAG_MATCH_STOP) {
+ break;
+ }
+ if (retval == TAG_MATCH_FAIL) {
+ semsg(_("E431: Format error in tags file \"%s\""), st->tag_fname);
+ semsg(_("Before byte %" PRId64), (int64_t)vim_ftell(st->fp));
+ st->stop_searching = true;
+ return;
+ }
+
+ // If a match is found, add it to ht_match[] and ga_match[].
+ if (findtags_match_tag(st, &tagp, margs)) {
+ findtags_add_match(st, &tagp, margs, buf_ffname, &hash);
+ }
+ } // forever
+}
+
+/// Search for tags matching "st->orgpat.pat" in the "st->tag_fname" tags file.
+/// Information needed to search for the tags is in the "st" state structure.
+/// The matching tags are returned in "st". If an error is encountered, then
+/// "st->stop_searching" is set to true.
+static void findtags_in_file(findtags_state_T *st, int flags, char *buf_ffname)
+{
+ findtags_match_args_T margs;
+
+ st->vimconv.vc_type = CONV_NONE;
+ st->tag_file_sorted = NUL;
+ st->fp = NULL;
+ findtags_matchargs_init(&margs, st->flags);
+
+ // A file that doesn't exist is silently ignored. Only when not a
+ // single file is found, an error message is given (further on).
+ if (curbuf->b_help) {
+ if (!findtags_in_help_init(st)) {
+ return;
+ }
+ }
+
+ st->fp = os_fopen(st->tag_fname, "r");
+ if (st->fp == NULL) {
+ return;
+ }
+
+ if (p_verbose >= 5) {
+ verbose_enter();
+ smsg(_("Searching tags file %s"), st->tag_fname);
+ verbose_leave();
+ }
+ st->did_open = true; // remember that we found at least one file
+
+ st->state = TS_START; // we're at the start of the file
+
+ // Read and parse the lines in the file one by one
+ findtags_get_all_tags(st, &margs, buf_ffname);
+
+ if (st->fp != NULL) {
+ fclose(st->fp);
+ st->fp = NULL;
+ }
+ if (st->vimconv.vc_type != CONV_NONE) {
+ convert_setup(&st->vimconv, NULL, NULL);
+ }
+
+ if (margs.sort_error) {
+ semsg(_("E432: Tags file not sorted: %s"), st->tag_fname);
+ }
+
+ // Stop searching if sufficient tags have been found.
+ if (st->match_count >= st->mincount) {
+ st->stop_searching = true;
+ }
+}
+
+/// Copy the tags found by find_tags() to "matchesp".
+/// Returns the number of matches copied.
+static int findtags_copy_matches(findtags_state_T *st, char ***matchesp)
+{
+ const bool name_only = (st->flags & TAG_NAMES);
+ char **matches;
+ int mtt;
+ int i;
+ char *mfp;
+ char *p;
+
+ if (st->match_count > 0) {
+ matches = xmalloc((size_t)st->match_count * sizeof(char *));
+ } else {
+ matches = NULL;
+ }
+ st->match_count = 0;
+ for (mtt = 0; mtt < MT_COUNT; mtt++) {
+ for (i = 0; i < st->ga_match[mtt].ga_len; i++) {
+ mfp = ((char **)(st->ga_match[mtt].ga_data))[i];
+ if (matches == NULL) {
+ xfree(mfp);
+ } else {
+ if (!name_only) {
+ // Change mtt back to zero-based.
+ *mfp = (char)(*mfp - 1);
+
+ // change the TAG_SEP back to NUL
+ for (p = mfp + 1; *p != NUL; p++) {
+ if (*p == TAG_SEP) {
+ *p = NUL;
+ }
+ }
+ }
+ matches[st->match_count++] = mfp;
+ }
+ }
+
+ ga_clear(&st->ga_match[mtt]);
+ hash_clear(&st->ht_match[mtt]);
+ }
+
+ *matchesp = matches;
+ return st->match_count;
+}
+
/// find_tags() - search for tags in tags files
///
/// Return FAIL if search completely failed (*num_matches will be 0, *matchesp
@@ -1357,94 +2315,33 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl
/// TAG_REGEXP use "pat" as a regexp
/// TAG_NOIC don't always ignore case
/// TAG_KEEP_LANG keep language
-/// TAG_CSCOPE use cscope results for tags
/// TAG_NO_TAGFUNC do not call the 'tagfunc' function
///
/// @param pat pattern to search for
/// @param num_matches return: number of matches found
/// @param matchesp return: array of matches found
-/// @param mincount MAXCOL: find all matches other: minimal number of matches */
+/// @param mincount MAXCOL: find all matches
+/// other: minimal number of matches
/// @param buf_ffname name of buffer for priority
-int find_tags(char_u *pat, int *num_matches, char ***matchesp, int flags, int mincount,
- char_u *buf_ffname)
+int find_tags(char *pat, int *num_matches, char ***matchesp, int flags, int mincount,
+ char *buf_ffname)
{
- FILE *fp;
- char_u *lbuf; // line buffer
- int lbuf_size = LSIZE; // length of lbuf
- char_u *tag_fname; // name of tag file
+ findtags_state_T st;
tagname_T tn; // info for get_tagfname()
int first_file; // trying first tag file
- tagptrs_T tagp;
- bool did_open = false; // did open a tag file
- bool stop_searching = false; // stop when match found or error
int retval = FAIL; // return value
- int is_static; // current tag line is static
- int is_current; // file name matches
- bool eof = false; // found end-of-file
- char_u *p;
- char_u *s;
- int i;
- int tag_file_sorted = NUL; // !_TAG_FILE_SORTED value
- struct tag_search_info { // Binary search file offsets
- off_T low_offset; // offset for first char of first line that
- // could match
- off_T high_offset; // offset of char after last line that could
- // match
- off_T curr_offset; // Current file offset in search range
- off_T curr_offset_used; // curr_offset used when skipping back
- off_T match_offset; // Where the binary search found a tag
- int low_char; // first char at low_offset
- int high_char; // first char at high_offset
- } search_info;
- int tagcmp;
- off_T offset;
int round;
- enum {
- TS_START, // at start of file
- TS_LINEAR, // linear searching forward, till EOF
- TS_BINARY, // binary searching
- TS_SKIP_BACK, // skipping backwards
- TS_STEP_FORWARD, // stepping forwards
- } state; // Current search state
- int cmplen;
- int match; // matches
- int match_no_ic = 0; // matches with rm_ic == FALSE
- int match_re; // match with regexp
- int matchoff = 0;
int save_emsg_off;
- char *mfp;
- garray_T ga_match[MT_COUNT]; // stores matches in sequence
- hashtab_T ht_match[MT_COUNT]; // stores matches by key
- hash_T hash = 0;
- int match_count = 0; // number of matches found
- char **matches;
- int mtt;
int help_save;
- int help_pri = 0;
- char_u *help_lang_find = NULL; // lang to be found
- char_u help_lang[3]; // lang of current tags file
- char_u *saved_pat = NULL; // copy of pat[]
- bool is_txt = false;
-
- pat_T orgpat; // holds unconverted pattern info
- vimconv_T vimconv;
+ int i;
+ char *saved_pat = NULL; // copy of pat[]
- int findall = (mincount == MAXCOL || mincount == TAG_MANY);
- // find all matching tags
- bool sort_error = false; // tags file not sorted
- int linear; // do a linear search
- bool sortic = false; // tag file sorted in nocase
- bool line_error = false; // syntax error
+ int findall = (mincount == MAXCOL || mincount == TAG_MANY); // find all matching tags
int has_re = (flags & TAG_REGEXP); // regexp used
- int help_only = (flags & TAG_HELP);
- int name_only = (flags & TAG_NAMES);
int noic = (flags & TAG_NOIC);
- int get_it_again = FALSE;
- int use_cscope = (flags & TAG_CSCOPE);
int verbose = (flags & TAG_VERBOSE);
- int use_tfu = ((flags & TAG_NO_TAGFUNC) == 0);
int save_p_ic = p_ic;
// Change the value of 'ignorecase' according to 'tagcase' for the
@@ -1469,829 +2366,109 @@ int find_tags(char_u *pat, int *num_matches, char ***matchesp, int flags, int mi
}
help_save = curbuf->b_help;
- orgpat.pat = pat;
- orgpat.regmatch.regprog = NULL;
- vimconv.vc_type = CONV_NONE;
-
- /*
- * Allocate memory for the buffers that are used
- */
- 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 *), 100);
- hash_init(&ht_match[mtt]);
- }
- STRCPY(tag_fname, "from cscope"); // for error messages
+ findtags_state_init(&st, pat, flags, mincount);
- /*
- * Initialize a few variables
- */
- if (help_only) { // want tags from help file
+ // Initialize a few variables
+ if (st.help_only) { // want tags from help file
curbuf->b_help = true; // will be restored later
- } else if (use_cscope) {
- // Make sure we don't mix help and cscope, confuses Coverity.
- help_only = false;
- curbuf->b_help = false;
}
- orgpat.len = (int)STRLEN(pat);
if (curbuf->b_help) {
// When "@ab" is specified use only the "ab" language, otherwise
// search all languages.
- 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, (size_t)orgpat.len - 3);
- help_lang_find = &pat[orgpat.len - 2];
- orgpat.pat = saved_pat;
- orgpat.len -= 3;
+ if (st.orgpat->len > 3 && pat[st.orgpat->len - 3] == '@'
+ && ASCII_ISALPHA(pat[st.orgpat->len - 2])
+ && ASCII_ISALPHA(pat[st.orgpat->len - 1])) {
+ saved_pat = xstrnsave(pat, (size_t)st.orgpat->len - 3);
+ st.help_lang_find = &pat[st.orgpat->len - 2];
+ st.orgpat->pat = saved_pat;
+ st.orgpat->len -= 3;
}
}
- if (p_tl != 0 && orgpat.len > p_tl) { // adjust for 'taglength'
- orgpat.len = (int)p_tl;
+ if (p_tl != 0 && st.orgpat->len > p_tl) { // adjust for 'taglength'
+ st.orgpat->len = (int)p_tl;
}
save_emsg_off = emsg_off;
- emsg_off = TRUE; // don't want error for invalid RE here
- prepare_pats(&orgpat, has_re);
+ emsg_off = true; // don't want error for invalid RE here
+ prepare_pats(st.orgpat, has_re);
emsg_off = save_emsg_off;
- if (has_re && orgpat.regmatch.regprog == NULL) {
+ if (has_re && st.orgpat->regmatch.regprog == NULL) {
goto findtag_end;
}
- // This is only to avoid a compiler warning for using search_info
- // uninitialised.
- CLEAR_FIELD(search_info);
+ retval = findtags_apply_tfu(&st, pat, buf_ffname);
+ if (retval != NOTDONE) {
+ goto findtag_end;
+ }
+
+ // re-initialize the default return value
+ retval = FAIL;
- if (*curbuf->b_p_tfu != NUL && use_tfu && !tfu_in_use) {
- tfu_in_use = true;
- retval = find_tagfunc_tags(pat, &ga_match[0], &match_count,
- flags, buf_ffname);
- tfu_in_use = false;
- if (retval != NOTDONE) {
- goto findtag_end;
- }
- }
-
- /*
- * When finding a specified number of matches, first try with matching
- * case, so binary search can be used, and try ignore-case matches in a
- * second loop.
- * When finding all matches, 'tagbsearch' is off, or there is no fixed
- * string to look for, ignore case right away to avoid going though the
- * tags files twice.
- * When the tag file is case-fold sorted, it is either one or the other.
- * Only ignore case when TAG_NOIC not used or 'ignorecase' set.
- */
// Set a flag if the file extension is .txt
if ((flags & TAG_KEEP_LANG)
- && help_lang_find == NULL
+ && st.help_lang_find == NULL
&& curbuf->b_fname != NULL
- && (i = (int)STRLEN(curbuf->b_fname)) > 4
+ && (i = (int)strlen(curbuf->b_fname)) > 4
&& STRICMP(curbuf->b_fname + i - 4, ".txt") == 0) {
- is_txt = true;
- }
- orgpat.regmatch.rm_ic = ((p_ic || !noic)
- && (findall || orgpat.headlen == 0 || !p_tbs));
- for (round = 1; round <= 2; ++round) {
- linear = (orgpat.headlen == 0 || !p_tbs || round == 2);
+ st.is_txt = true;
+ }
+
+ // When finding a specified number of matches, first try with matching
+ // case, so binary search can be used, and try ignore-case matches in a
+ // second loop.
+ // When finding all matches, 'tagbsearch' is off, or there is no fixed
+ // string to look for, ignore case right away to avoid going though the
+ // tags files twice.
+ // When the tag file is case-fold sorted, it is either one or the other.
+ // Only ignore case when TAG_NOIC not used or 'ignorecase' set.
+ st.orgpat->regmatch.rm_ic = ((p_ic || !noic)
+ && (findall || st.orgpat->headlen == 0 || !p_tbs));
+ for (round = 1; round <= 2; round++) {
+ st.linear = (st.orgpat->headlen == 0 || !p_tbs || round == 2);
// Try tag file names from tags option one by one.
for (first_file = true;
- use_cscope || get_tagfname(&tn, first_file, tag_fname) == OK;
+ get_tagfname(&tn, first_file, st.tag_fname) == OK;
first_file = false) {
- // A file that doesn't exist is silently ignored. Only when not a
- // single file is found, an error message is given (further on).
- if (use_cscope) {
- fp = NULL; // avoid GCC warning
- } else {
- if (curbuf->b_help) {
- // Keep en if the file extension is .txt
- if (is_txt) {
- STRCPY(help_lang, "en");
- } else {
- // Prefer help tags according to 'helplang'. Put the
- // two-letter language name in help_lang[].
- i = (int)STRLEN(tag_fname);
- if (i > 3 && tag_fname[i - 3] == '-') {
- STRCPY(help_lang, tag_fname + i - 2);
- } else {
- STRCPY(help_lang, "en");
- }
- }
-
- // When searching for a specific language skip tags files
- // for other languages.
- if (help_lang_find != NULL
- && STRICMP(help_lang, help_lang_find) != 0) {
- continue;
- }
-
- // For CTRL-] in a help file prefer a match with the same
- // language.
- if ((flags & TAG_KEEP_LANG)
- && help_lang_find == NULL
- && curbuf->b_fname != NULL
- && (i = (int)STRLEN(curbuf->b_fname)) > 4
- && curbuf->b_fname[i - 1] == 'x'
- && curbuf->b_fname[i - 4] == '.'
- && STRNICMP(curbuf->b_fname + i - 3, help_lang, 2) == 0) {
- help_pri = 0;
- } else {
- help_pri = 1;
- for (s = p_hlg; *s != NUL; ++s) {
- if (STRNICMP(s, help_lang, 2) == 0) {
- break;
- }
- help_pri++;
- if ((s = (char_u *)vim_strchr((char *)s, ',')) == NULL) {
- break;
- }
- }
- if (s == NULL || *s == NUL) {
- // Language not in 'helplang': use last, prefer English,
- // unless found already.
- help_pri++;
- if (STRICMP(help_lang, "en") != 0) {
- help_pri++;
- }
- }
- }
- }
-
- if ((fp = os_fopen((char *)tag_fname, "r")) == NULL) {
- continue;
- }
-
- if (p_verbose >= 5) {
- verbose_enter();
- smsg(_("Searching tags file %s"), tag_fname);
- verbose_leave();
- }
- }
- did_open = true; // remember that we found at least one file
-
- state = TS_START; // we're at the start of the file
-
- /*
- * Read and parse the lines in the file one by one
- */
- for (;;) {
- // check for CTRL-C typed, more often when jumping around
- if (state == TS_BINARY || state == TS_SKIP_BACK) {
- line_breakcheck();
- } else {
- fast_breakcheck();
- }
- if ((flags & TAG_INS_COMP)) { // Double brackets for gcc
- ins_compl_check_keys(30, false);
- }
- if (got_int || ins_compl_interrupted()) {
- stop_searching = true;
- break;
- }
- // When mincount is TAG_MANY, stop when enough matches have been
- // found (for completion).
- if (mincount == TAG_MANY && match_count >= TAG_MANY) {
- stop_searching = true;
- retval = OK;
- break;
- }
- if (get_it_again) {
- goto line_read_in;
- }
- /*
- * For binary search: compute the next offset to use.
- */
- if (state == TS_BINARY) {
- offset = search_info.low_offset + ((search_info.high_offset
- - search_info.low_offset) / 2);
- if (offset == search_info.curr_offset) {
- break; // End the binary search without a match.
- } else {
- search_info.curr_offset = offset;
- }
- } else if (state == TS_SKIP_BACK) {
- // Skipping back (after a match during binary search).
- search_info.curr_offset -= lbuf_size * 2;
- if (search_info.curr_offset < 0) {
- search_info.curr_offset = 0;
- rewind(fp);
- state = TS_STEP_FORWARD;
- }
- }
-
- /*
- * When jumping around in the file, first read a line to find the
- * start of the next line.
- */
- if (state == TS_BINARY || state == TS_SKIP_BACK) {
- // Adjust the search file offset to the correct position
- search_info.curr_offset_used = search_info.curr_offset;
- vim_fseek(fp, search_info.curr_offset, SEEK_SET);
- eof = vim_fgets(lbuf, lbuf_size, fp);
- if (!eof && search_info.curr_offset != 0) {
- // The explicit cast is to work around a bug in gcc 3.4.2
- // (repeated below).
- search_info.curr_offset = vim_ftell(fp);
- if (search_info.curr_offset == search_info.high_offset) {
- // oops, gone a bit too far; try from low offset
- vim_fseek(fp, search_info.low_offset, SEEK_SET);
- search_info.curr_offset = search_info.low_offset;
- }
- eof = vim_fgets(lbuf, lbuf_size, fp);
- }
- // skip empty and blank lines
- while (!eof && vim_isblankline(lbuf)) {
- search_info.curr_offset = vim_ftell(fp);
- eof = vim_fgets(lbuf, lbuf_size, fp);
- }
- if (eof) {
- // Hit end of file. Skip backwards.
- state = TS_SKIP_BACK;
- search_info.match_offset = vim_ftell(fp);
- search_info.curr_offset = search_info.curr_offset_used;
- continue;
- }
- }
- /*
- * Not jumping around in the file: Read the next line.
- */
- else {
- // skip empty and blank lines
- do {
- eof = use_cscope
- ? cs_fgets(lbuf, lbuf_size)
- : vim_fgets(lbuf, lbuf_size, fp);
- } while (!eof && vim_isblankline(lbuf));
-
- if (eof) {
- break; // end of file
- }
- }
-line_read_in:
-
- if (vimconv.vc_type != CONV_NONE) {
- char_u *conv_line;
- int len;
-
- // Convert every line. Converting the pattern from 'enc' to
- // the tags file encoding doesn't work, because characters are
- // not recognized.
- conv_line = string_convert(&vimconv, lbuf, NULL);
- if (conv_line != NULL) {
- // Copy or swap lbuf and conv_line.
- len = (int)STRLEN(conv_line) + 1;
- if (len > lbuf_size) {
- xfree(lbuf);
- lbuf = conv_line;
- lbuf_size = len;
- } else {
- STRCPY(lbuf, conv_line);
- xfree(conv_line);
- }
- }
- }
-
- /*
- * When still at the start of the file, check for Emacs tags file
- * format, and for "not sorted" flag.
- */
- if (state == TS_START) {
- // The header ends when the line sorts below "!_TAG_". When
- // case is folded lower case letters sort before "_".
- if (STRNCMP(lbuf, "!_TAG_", 6) <= 0
- || (lbuf[0] == '!' && ASCII_ISLOWER(lbuf[1]))) {
- if (STRNCMP(lbuf, "!_TAG_", 6) != 0) {
- // Non-header item before the header, e.g. "!" itself.
- goto parse_line;
- }
-
- /*
- * Read header line.
- */
- if (STRNCMP(lbuf, "!_TAG_FILE_SORTED\t", 18) == 0) {
- tag_file_sorted = lbuf[18];
- }
- 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++) {}
- *p = NUL;
- convert_setup(&vimconv, lbuf + 20, p_enc);
- }
-
- // Read the next line. Unrecognized flags are ignored.
- continue;
- }
-
- // Headers ends.
-
- /*
- * When there is no tag head, or ignoring case, need to do a
- * linear search.
- * When no "!_TAG_" is found, default to binary search. If
- * the tag file isn't sorted, the second loop will find it.
- * When "!_TAG_FILE_SORTED" found: start binary search if
- * flag set.
- * For cscope, it's always linear.
- */
- if (linear || use_cscope) {
- state = TS_LINEAR;
- } else if (tag_file_sorted == NUL) {
- state = TS_BINARY;
- } else if (tag_file_sorted == '1') {
- state = TS_BINARY;
- } else if (tag_file_sorted == '2') {
- state = TS_BINARY;
- sortic = true;
- orgpat.regmatch.rm_ic = (p_ic || !noic);
- } else {
- state = TS_LINEAR;
- }
-
- if (state == TS_BINARY && orgpat.regmatch.rm_ic && !sortic) {
- // Binary search won't work for ignoring case, use linear
- // search.
- linear = true;
- state = TS_LINEAR;
- }
-
- // When starting a binary search, get the size of the file and
- // compute the first offset.
- if (state == TS_BINARY) {
- if (vim_fseek(fp, 0, SEEK_END) != 0) {
- // can't seek, don't use binary search
- state = TS_LINEAR;
- } else {
- // Get the tag file size.
- // Don't use lseek(), it doesn't work
- // properly on MacOS Catalina.
- const off_T filesize = vim_ftell(fp);
- vim_fseek(fp, 0, SEEK_SET);
-
- // Calculate the first read offset in the file. Start
- // the search in the middle of the file.
- search_info.low_offset = 0;
- search_info.low_char = 0;
- search_info.high_offset = filesize;
- search_info.curr_offset = 0;
- search_info.high_char = 0xff;
- }
- continue;
- }
- }
-
-parse_line:
- // When the line is too long the NUL will not be in the
- // last-but-one byte (see vim_fgets()).
- // Has been reported for Mozilla JS with extremely long names.
- // In that case we need to increase lbuf_size.
- if (lbuf[lbuf_size - 2] != NUL && !use_cscope) {
- lbuf_size *= 2;
- xfree(lbuf);
- lbuf = xmalloc((size_t)lbuf_size);
- // this will try the same thing again, make sure the offset is
- // different
- search_info.curr_offset = 0;
- continue;
- }
-
- // Figure out where the different strings are in this line.
- // For "normal" tags: Do a quick check if the tag matches.
- // This speeds up tag searching a lot!
- if (orgpat.headlen) {
- CLEAR_FIELD(tagp);
- tagp.tagname = lbuf;
- tagp.tagname_end = (char_u *)vim_strchr((char *)lbuf, TAB);
- if (tagp.tagname_end == NULL) {
- // Corrupted tag line.
- line_error = true;
- break;
- }
-
- /*
- * Skip this line if the length of the tag is different and
- * there is no regexp, or the tag is too short.
- */
- cmplen = (int)(tagp.tagname_end - tagp.tagname);
- if (p_tl != 0 && cmplen > p_tl) { // adjust for 'taglength'
- cmplen = (int)p_tl;
- }
- if (has_re && orgpat.headlen < cmplen) {
- cmplen = orgpat.headlen;
- } else if (state == TS_LINEAR && orgpat.headlen != cmplen) {
- continue;
- }
-
- if (state == TS_BINARY) {
- /*
- * Simplistic check for unsorted tags file.
- */
- i = (int)tagp.tagname[0];
- if (sortic) {
- i = TOUPPER_ASC(tagp.tagname[0]);
- }
- if (i < search_info.low_char || i > search_info.high_char) {
- sort_error = true;
- }
-
- /*
- * Compare the current tag with the searched tag.
- */
- if (sortic) {
- tagcmp = tag_strnicmp(tagp.tagname, orgpat.head,
- (size_t)cmplen);
- } else {
- tagcmp = STRNCMP(tagp.tagname, orgpat.head, cmplen);
- }
-
- /*
- * A match with a shorter tag means to search forward.
- * A match with a longer tag means to search backward.
- */
- if (tagcmp == 0) {
- if (cmplen < orgpat.headlen) {
- tagcmp = -1;
- } else if (cmplen > orgpat.headlen) {
- tagcmp = 1;
- }
- }
-
- if (tagcmp == 0) {
- // We've located the tag, now skip back and search
- // forward until the first matching tag is found.
- state = TS_SKIP_BACK;
- search_info.match_offset = search_info.curr_offset;
- continue;
- }
- if (tagcmp < 0) {
- search_info.curr_offset = vim_ftell(fp);
- if (search_info.curr_offset < search_info.high_offset) {
- search_info.low_offset = search_info.curr_offset;
- if (sortic) {
- search_info.low_char =
- TOUPPER_ASC(tagp.tagname[0]);
- } else {
- search_info.low_char = tagp.tagname[0];
- }
- continue;
- }
- }
- if (tagcmp > 0
- && search_info.curr_offset != search_info.high_offset) {
- search_info.high_offset = search_info.curr_offset;
- if (sortic) {
- search_info.high_char =
- TOUPPER_ASC(tagp.tagname[0]);
- } else {
- search_info.high_char = tagp.tagname[0];
- }
- continue;
- }
-
- // No match yet and are at the end of the binary search.
- break;
- } else if (state == TS_SKIP_BACK) {
- assert(cmplen >= 0);
- if (mb_strnicmp(tagp.tagname, orgpat.head, (size_t)cmplen) != 0) {
- state = TS_STEP_FORWARD;
- } else {
- // Have to skip back more. Restore the curr_offset
- // used, otherwise we get stuck at a long line.
- search_info.curr_offset = search_info.curr_offset_used;
- }
- continue;
- } else if (state == TS_STEP_FORWARD) {
- assert(cmplen >= 0);
- if (mb_strnicmp(tagp.tagname, orgpat.head, (size_t)cmplen) != 0) {
- if ((off_T)vim_ftell(fp) > search_info.match_offset) {
- break; // past last match
- } else {
- continue; // before first match
- }
- }
- } else {
- // skip this match if it can't match
- assert(cmplen >= 0);
- }
- if (mb_strnicmp(tagp.tagname, orgpat.head, (size_t)cmplen) != 0) {
- continue;
- }
-
- // Can be a matching tag, isolate the file name and command.
- tagp.fname = tagp.tagname_end + 1;
- tagp.fname_end = (char_u *)vim_strchr((char *)tagp.fname, TAB);
- tagp.command = tagp.fname_end + 1;
- if (tagp.fname_end == NULL) {
- i = FAIL;
- } else {
- i = OK;
- }
- } else {
- i = parse_tag_line(lbuf,
- &tagp);
- }
- if (i == FAIL) {
- line_error = true;
- break;
- }
-
- /*
- * First try matching with the pattern literally (also when it is
- * a regexp).
- */
- cmplen = (int)(tagp.tagname_end - tagp.tagname);
- if (p_tl != 0 && cmplen > p_tl) { // adjust for 'taglength'
- cmplen = (int)p_tl;
- }
- // if tag length does not match, don't try comparing
- if (orgpat.len != cmplen) {
- match = FALSE;
- } else {
- if (orgpat.regmatch.rm_ic) {
- assert(cmplen >= 0);
- match = mb_strnicmp(tagp.tagname, orgpat.pat, (size_t)cmplen) == 0;
- if (match) {
- match_no_ic = (STRNCMP(tagp.tagname, orgpat.pat,
- cmplen) == 0);
- }
- } else {
- match = (STRNCMP(tagp.tagname, orgpat.pat, cmplen) == 0);
- }
- }
-
- /*
- * Has a regexp: Also find tags matching regexp.
- */
- match_re = FALSE;
- if (!match && orgpat.regmatch.regprog != NULL) {
- int cc;
-
- cc = *tagp.tagname_end;
- *tagp.tagname_end = NUL;
- 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, (char *)tagp.tagname, (colnr_T)0);
- orgpat.regmatch.rm_ic = 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) {
- size_t len = 0;
-
- if (use_cscope) {
- // Don't change the ordering, always use the same table.
- mtt = MT_GL_OTH;
- } else {
- // Decide in which array to store this match.
- is_current = test_for_current(tagp.fname, tagp.fname_end, tag_fname,
- buf_ffname);
- is_static = test_for_static(&tagp);
-
- // Decide in which of the sixteen tables to store this match.
- if (is_static) {
- if (is_current) {
- mtt = MT_ST_CUR;
- } else {
- mtt = MT_ST_OTH;
- }
- } else {
- if (is_current) {
- mtt = MT_GL_CUR;
- } else {
- mtt = MT_GL_OTH;
- }
- }
- if (orgpat.regmatch.rm_ic && !match_no_ic) {
- mtt += MT_IC_OFF;
- }
- if (match_re) {
- mtt += MT_RE_OFF;
- }
- }
-
- // Add the found match in ht_match[mtt] and ga_match[mtt].
- // Store the info we need later, which depends on the kind of
- // tags we are dealing with.
- if (help_only) {
-#define ML_EXTRA 3
- // Append the help-heuristic number after the tagname, for
- // sorting it later. The heuristic is ignored for
- // detecting duplicates.
- // The format is {tagname}@{lang}NUL{heuristic}NUL
- *tagp.tagname_end = NUL;
- len = (size_t)(tagp.tagname_end - tagp.tagname);
- mfp = xmalloc(sizeof(char) + len + 10 + ML_EXTRA + 1);
-
- p = (char_u *)mfp;
- STRCPY(p, tagp.tagname);
- p[len] = '@';
- STRCPY(p + len + 1, help_lang);
- 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);
-
- *tagp.tagname_end = TAB;
- } else if (name_only) {
- if (get_it_again) {
- char_u *temp_end = tagp.command;
-
- if (*temp_end == '/') {
- while (*temp_end && *temp_end != '\r'
- && *temp_end != '\n'
- && *temp_end != '$') {
- temp_end++;
- }
- }
-
- if (tagp.command + 2 < temp_end) {
- len = (size_t)(temp_end - tagp.command - 2);
- mfp = xmalloc(len + 2);
- STRLCPY(mfp, tagp.command + 2, len + 1);
- } else {
- mfp = NULL;
- }
- get_it_again = false;
- } else {
- len = (size_t)(tagp.tagname_end - tagp.tagname);
- mfp = xmalloc(sizeof(char) + len + 1);
- STRLCPY(mfp, tagp.tagname, len + 1);
-
- // if wanted, re-read line to get long form too
- if (State & MODE_INSERT) {
- get_it_again = p_sft;
- }
- }
- } else {
- size_t tag_fname_len = STRLEN(tag_fname);
- // Save the tag in a buffer.
- // Use 0x02 to separate fields (Can't use NUL, because the
- // hash key is terminated by NUL).
- // Emacs tag: <mtt><tag_fname><0x02><ebuf><0x02><lbuf><NUL>
- // 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 = tag_fname_len + STRLEN(lbuf) + 3;
- mfp = xmalloc(sizeof(char) + len + 1);
- p = (char_u *)mfp;
- p[0] = (char_u)(mtt + 1);
- STRCPY(p + 1, tag_fname);
-#ifdef BACKSLASH_IN_FILENAME
- // Ignore differences in slashes, avoid adding
- // both path/file and path\file.
- slash_adjust(p + 1);
-#endif
- p[tag_fname_len + 1] = TAG_SEP;
- s = p + 1 + tag_fname_len + 1;
- STRCPY(s, lbuf);
- }
-
- if (mfp != NULL) {
- hashitem_T *hi;
-
- // Don't add identical matches.
- // Add all cscope tags, because they are all listed.
- // "mfp" is used as a hash key, there is a NUL byte to end
- // the part that matters for comparing, more bytes may
- // follow after it. E.g. help tags store the priority
- // after the NUL.
- if (use_cscope) {
- hash++;
- } else {
- hash = hash_hash((char_u *)mfp);
- }
- hi = hash_lookup(&ht_match[mtt], (const char *)mfp,
- STRLEN(mfp), hash);
- if (HASHITEM_EMPTY(hi)) {
- hash_add_item(&ht_match[mtt], hi, (char_u *)mfp, hash);
- ga_grow(&ga_match[mtt], 1);
- ((char **)(ga_match[mtt].ga_data))[ga_match[mtt].ga_len++] = mfp;
- match_count++;
- } else {
- // duplicate tag, drop it
- xfree(mfp);
- }
- }
- }
- if (use_cscope && eof) {
- break;
- }
- } // forever
-
- if (line_error) {
- semsg(_("E431: Format error in tags file \"%s\""), tag_fname);
- if (!use_cscope) {
- semsg(_("Before byte %" PRId64), (int64_t)vim_ftell(fp));
- }
- stop_searching = true;
- line_error = false;
- }
-
- if (!use_cscope) {
- fclose(fp);
- }
- if (vimconv.vc_type != CONV_NONE) {
- convert_setup(&vimconv, NULL, NULL);
- }
-
- tag_file_sorted = NUL;
- if (sort_error) {
- semsg(_("E432: Tags file not sorted: %s"), tag_fname);
- sort_error = false;
- }
-
- /*
- * Stop searching if sufficient tags have been found.
- */
- if (match_count >= mincount) {
+ findtags_in_file(&st, flags, buf_ffname);
+ if (st.stop_searching) {
retval = OK;
- stop_searching = true;
- }
-
- if (stop_searching || use_cscope) {
break;
}
} // end of for-each-file loop
- if (!use_cscope) {
- tagname_free(&tn);
- }
+ tagname_free(&tn);
// stop searching when already did a linear search, or when TAG_NOIC
// used, and 'ignorecase' not set or already did case-ignore search
- if (stop_searching || linear || (!p_ic && noic) || orgpat.regmatch.rm_ic) {
- break;
- }
- if (use_cscope) {
+ if (st.stop_searching || st.linear || (!p_ic && noic)
+ || st.orgpat->regmatch.rm_ic) {
break;
}
- orgpat.regmatch.rm_ic = TRUE; // try another time while ignoring case
+
+ // try another time while ignoring case
+ st.orgpat->regmatch.rm_ic = true;
}
- if (!stop_searching) {
- if (!did_open && verbose) { // never opened any tags file
+ if (!st.stop_searching) {
+ if (!st.did_open && verbose) { // never opened any tags file
emsg(_("E433: No tags file"));
}
retval = OK; // It's OK even when no tag found
}
findtag_end:
- xfree(lbuf);
- vim_regfree(orgpat.regmatch.regprog);
- xfree(tag_fname);
+ findtags_state_free(&st);
- /*
- * Move the matches from the ga_match[] arrays into one list of
- * matches. When retval == FAIL, free the matches.
- */
+ // Move the matches from the ga_match[] arrays into one list of
+ // matches. When retval == FAIL, free the matches.
if (retval == FAIL) {
- match_count = 0;
+ st.match_count = 0;
}
- if (match_count > 0) {
- matches = xmalloc((size_t)match_count * sizeof(char *));
- } else {
- matches = NULL;
- }
- match_count = 0;
- for (mtt = 0; mtt < MT_COUNT; mtt++) {
- for (i = 0; i < ga_match[mtt].ga_len; i++) {
- mfp = ((char **)(ga_match[mtt].ga_data))[i];
- if (matches == NULL) {
- xfree(mfp);
- } else {
- if (!name_only) {
- // Change mtt back to zero-based.
- *mfp = (char)(*mfp - 1);
-
- // change the TAG_SEP back to NUL
- for (p = (char_u *)mfp + 1; *p != NUL; p++) {
- if (*p == TAG_SEP) {
- *p = NUL;
- }
- }
- }
- matches[match_count++] = mfp;
- }
- }
-
- ga_clear(&ga_match[mtt]);
- hash_clear(&ht_match[mtt]);
- }
-
- *matchesp = matches;
- *num_matches = match_count;
+ *num_matches = findtags_copy_matches(&st, matchesp);
curbuf->b_help = help_save;
xfree(saved_pat);
@@ -2303,19 +2480,17 @@ findtag_end:
static garray_T tag_fnames = GA_EMPTY_INIT_VALUE;
-/*
- * Callback function for finding all "tags" and "tags-??" files in
- * 'runtimepath' doc directories.
- */
+// Callback function for finding all "tags" and "tags-??" files in
+// 'runtimepath' doc directories.
static void found_tagfile_cb(char *fname, void *cookie)
{
- char_u *const tag_fname = vim_strsave((char_u *)fname);
+ char *const tag_fname = xstrdup(fname);
#ifdef BACKSLASH_IN_FILENAME
slash_adjust(tag_fname);
#endif
simplify_filename(tag_fname);
- GA_APPEND(char_u *, &tag_fnames, tag_fname);
+ GA_APPEND(char *, &tag_fnames, tag_fname);
}
#if defined(EXITFREE)
@@ -2334,25 +2509,23 @@ void free_tag_stuff(void)
/// For help files, use "tags" file only.
///
/// @param tnp holds status info
-/// @param first TRUE when first file name is wanted
+/// @param first true when first file name is wanted
/// @param buf pointer to buffer of MAXPATHL chars
///
/// @return FAIL if no more tag file names, OK otherwise.
-int get_tagfname(tagname_T *tnp, int first, char_u *buf)
+int get_tagfname(tagname_T *tnp, int first, char *buf)
{
- char_u *fname = NULL;
- char_u *r_ptr;
+ char *fname = NULL;
+ char *r_ptr;
if (first) {
CLEAR_POINTER(tnp);
}
if (curbuf->b_help) {
- /*
- * For help files it's done in a completely different way:
- * Find "doc/tags" and "doc/tags-??" in all directories in
- * 'runtimepath'.
- */
+ // For help files it's done in a completely different way:
+ // Find "doc/tags" and "doc/tags-??" in all directories in
+ // 'runtimepath'.
if (first) {
ga_clear_strings(&tag_fnames);
ga_init(&tag_fnames, (int)sizeof(char *), 10);
@@ -2366,7 +2539,7 @@ int get_tagfname(tagname_T *tnp, int first, char_u *buf)
if (tnp->tn_hf_idx > tag_fnames.ga_len || *p_hf == NUL) {
return FAIL;
}
- ++tnp->tn_hf_idx;
+ tnp->tn_hf_idx++;
STRCPY(buf, p_hf);
STRCPY(path_tail((char *)buf), "tags");
#ifdef BACKSLASH_IN_FILENAME
@@ -2375,12 +2548,12 @@ int get_tagfname(tagname_T *tnp, int first, char_u *buf)
simplify_filename(buf);
for (int i = 0; i < tag_fnames.ga_len; i++) {
- if (STRCMP(buf, ((char **)(tag_fnames.ga_data))[i]) == 0) {
+ if (strcmp(buf, ((char **)(tag_fnames.ga_data))[i]) == 0) {
return FAIL; // avoid duplicate file names
}
}
} else {
- STRLCPY(buf, ((char **)(tag_fnames.ga_data))[tnp->tn_hf_idx++], MAXPATHL);
+ xstrlcpy(buf, ((char **)(tag_fnames.ga_data))[tnp->tn_hf_idx++], MAXPATHL);
}
return OK;
}
@@ -2388,16 +2561,14 @@ int get_tagfname(tagname_T *tnp, int first, char_u *buf)
if (first) {
// Init. We make a copy of 'tags', because autocommands may change
// the value without notifying us.
- tnp->tn_tags = vim_strsave((*curbuf->b_p_tags != NUL) ? curbuf->b_p_tags : p_tags);
- tnp->tn_np = (char *)tnp->tn_tags;
+ tnp->tn_tags = xstrdup((*curbuf->b_p_tags != NUL) ? curbuf->b_p_tags : p_tags);
+ tnp->tn_np = tnp->tn_tags;
}
- /*
- * Loop until we have found a file name that can be used.
- * There are two states:
- * tnp->tn_did_filefind_init == FALSE: setup for next part in 'tags'.
- * tnp->tn_did_filefind_init == TRUE: find next file in this part.
- */
+ // Loop until we have found a file name that can be used.
+ // There are two states:
+ // tnp->tn_did_filefind_init == false: setup for next part in 'tags'.
+ // tnp->tn_did_filefind_init == true: find next file in this part.
for (;;) {
if (tnp->tn_did_filefind_init) {
fname = vim_findfile(tnp->tn_search_ctx);
@@ -2405,9 +2576,9 @@ int get_tagfname(tagname_T *tnp, int first, char_u *buf)
break;
}
- tnp->tn_did_filefind_init = FALSE;
+ tnp->tn_did_filefind_init = false;
} else {
- char_u *filename = NULL;
+ char *filename = NULL;
// Stop when used all parts of 'tags'.
if (*tnp->tn_np == NUL) {
@@ -2416,26 +2587,24 @@ int get_tagfname(tagname_T *tnp, int first, char_u *buf)
return FAIL;
}
- /*
- * Copy next file name into buf.
- */
+ // Copy next file name into buf.
buf[0] = NUL;
- (void)copy_option_part(&tnp->tn_np, (char *)buf, MAXPATHL - 1, " ,");
+ (void)copy_option_part(&tnp->tn_np, buf, MAXPATHL - 1, " ,");
r_ptr = vim_findfile_stopdir(buf);
// move the filename one char forward and truncate the
// filepath with a NUL
- filename = (char_u *)path_tail((char *)buf);
+ filename = path_tail(buf);
STRMOVE(filename + 1, filename);
*filename++ = NUL;
tnp->tn_search_ctx = vim_findfile_init(buf, filename,
r_ptr, 100,
- FALSE, // don't free visited list
+ false, // don't free visited list
FINDFILE_FILE, // we search for a file
- tnp->tn_search_ctx, true, (char_u *)curbuf->b_ffname);
+ tnp->tn_search_ctx, true, curbuf->b_ffname);
if (tnp->tn_search_ctx != NULL) {
- tnp->tn_did_filefind_init = TRUE;
+ tnp->tn_did_filefind_init = true;
}
}
}
@@ -2445,9 +2614,7 @@ int get_tagfname(tagname_T *tnp, int first, char_u *buf)
return OK;
}
-/*
- * Free the contents of a tagname_T that was filled by get_tagfname().
- */
+// Free the contents of a tagname_T that was filled by get_tagfname().
void tagname_free(tagname_T *tnp)
{
xfree(tnp->tn_tags);
@@ -2459,18 +2626,18 @@ void tagname_free(tagname_T *tnp)
/// Parse one line from the tags file. Find start/end of tag name, start/end of
/// file name and start of search pattern.
///
-/// If is_etag is TRUE, tagp->fname and tagp->fname_end are not set.
+/// If is_etag is true, tagp->fname and tagp->fname_end are not set.
///
/// @param lbuf line to be parsed
///
/// @return FAIL if there is a format error in this line, OK otherwise.
-static int parse_tag_line(char_u *lbuf, tagptrs_T *tagp)
+static int parse_tag_line(char *lbuf, tagptrs_T *tagp)
{
- char_u *p;
+ char *p;
// Isolate the tagname, from lbuf up to the first white
tagp->tagname = lbuf;
- p = (char_u *)vim_strchr((char *)lbuf, TAB);
+ p = vim_strchr(lbuf, TAB);
if (p == NULL) {
return FAIL;
}
@@ -2481,7 +2648,7 @@ static int parse_tag_line(char_u *lbuf, tagptrs_T *tagp)
p++;
}
tagp->fname = p;
- p = (char_u *)vim_strchr((char *)p, TAB);
+ p = vim_strchr(p, TAB);
if (p == NULL) {
return FAIL;
}
@@ -2499,44 +2666,42 @@ static int parse_tag_line(char_u *lbuf, tagptrs_T *tagp)
return OK;
}
-/*
- * Check if tagname is a static tag
- *
- * Static tags produced by the older ctags program have the format:
- * 'file:tag file /pattern'.
- * This is only recognized when both occurrence of 'file' are the same, to
- * avoid recognizing "string::string" or ":exit".
- *
- * Static tags produced by the new ctags program have the format:
- * 'tag file /pattern/;"<Tab>file:' "
- *
- * Return TRUE if it is a static tag and adjust *tagname to the real tag.
- * Return FALSE if it is not a static tag.
- */
+// Check if tagname is a static tag
+//
+// Static tags produced by the older ctags program have the format:
+// 'file:tag file /pattern'.
+// This is only recognized when both occurrence of 'file' are the same, to
+// avoid recognizing "string::string" or ":exit".
+//
+// Static tags produced by the new ctags program have the format:
+// 'tag file /pattern/;"<Tab>file:' "
+//
+// Return true if it is a static tag and adjust *tagname to the real tag.
+// Return false if it is not a static tag.
static bool test_for_static(tagptrs_T *tagp)
{
- char_u *p;
+ char *p;
// Check for new style static tag ":...<Tab>file:[<Tab>...]"
p = tagp->command;
- while ((p = (char_u *)vim_strchr((char *)p, '\t')) != NULL) {
+ while ((p = vim_strchr(p, '\t')) != NULL) {
p++;
- if (STRNCMP(p, "file:", 5) == 0) {
- return TRUE;
+ if (strncmp(p, "file:", 5) == 0) {
+ return true;
}
}
- return FALSE;
+ return false;
}
-// Returns the length of a matching tag line.
-static size_t matching_line_len(const char_u *const lbuf)
+/// @return the length of a matching tag line.
+static size_t matching_line_len(const char *const lbuf)
{
- const char_u *p = lbuf + 1;
+ const char *p = lbuf + 1;
// does the same thing as parse_match()
- p += STRLEN(p) + 1;
- return (size_t)(p - lbuf) + STRLEN(p);
+ p += strlen(p) + 1;
+ return (size_t)(p - lbuf) + strlen(p);
}
/// Parse a line from a matching tag. Does not change the line itself.
@@ -2550,18 +2715,17 @@ static size_t matching_line_len(const char_u *const lbuf)
/// @param tagp output: pointers into the line
///
/// @return OK or FAIL.
-static int parse_match(char_u *lbuf, tagptrs_T *tagp)
+static int parse_match(char *lbuf, tagptrs_T *tagp)
{
int retval;
- char_u *p;
- char_u *pc, *pt;
+ char *p;
+ char *pc, *pt;
tagp->tag_fname = lbuf + 1;
- lbuf += STRLEN(tagp->tag_fname) + 2;
+ lbuf += strlen(tagp->tag_fname) + 2;
// Find search pattern and the file name for non-etags.
- retval = parse_tag_line(lbuf,
- tagp);
+ retval = parse_tag_line(lbuf, tagp);
tagp->tagkind = NULL;
tagp->user_data = NULL;
@@ -2580,20 +2744,20 @@ 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((char *)p) > 1) {
- if (STRNCMP(p, "kind:", 5) == 0) {
+ while (ASCII_ISALPHA(*p) || utfc_ptr2len(p) > 1) {
+ if (strncmp(p, "kind:", 5) == 0) {
tagp->tagkind = p + 5;
- } else if (STRNCMP(p, "user_data:", 10) == 0) {
+ } else if (strncmp(p, "user_data:", 10) == 0) {
tagp->user_data = p + 10;
- } else if (STRNCMP(p, "line:", 5) == 0) {
- tagp->tagline = atoi((char *)p + 5);
+ } else if (strncmp(p, "line:", 5) == 0) {
+ tagp->tagline = atoi(p + 5);
}
if (tagp->tagkind != NULL && tagp->user_data != NULL) {
break;
}
- pc = (char_u *)vim_strchr((char *)p, ':');
- pt = (char_u *)vim_strchr((char *)p, '\t');
+ pc = vim_strchr(p, ':');
+ pt = vim_strchr(p, '\t');
if (pc == NULL || (pt != NULL && pc > pt)) {
tagp->tagkind = p;
}
@@ -2621,17 +2785,15 @@ static int parse_match(char_u *lbuf, tagptrs_T *tagp)
return retval;
}
-/*
- * Find out the actual file name of a tag. Concatenate the tags file name
- * with the matching tag file name.
- * Returns an allocated string.
- */
-static char_u *tag_full_fname(tagptrs_T *tagp)
+// Find out the actual file name of a tag. Concatenate the tags file name
+// with the matching tag file name.
+// Returns an allocated string.
+static char *tag_full_fname(tagptrs_T *tagp)
{
- int c = *tagp->fname_end;
+ char c = *tagp->fname_end;
*tagp->fname_end = NUL;
- char_u *fullname = expand_tag_fname(tagp->fname, tagp->tag_fname, false);
- *tagp->fname_end = (char_u)c;
+ char *fullname = expand_tag_fname(tagp->fname, tagp->tag_fname, false);
+ *tagp->fname_end = c;
return fullname;
}
@@ -2640,30 +2802,29 @@ static char_u *tag_full_fname(tagptrs_T *tagp)
///
/// @param lbuf_arg line from the tags file for this tag
/// @param forceit :ta with !
-/// @param keep_help keep help flag (FALSE for cscope)
+/// @param keep_help keep help flag
///
/// @return OK for success, NOTAGFILE when file not found, FAIL otherwise.
-static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
+static int jumpto_tag(const char *lbuf_arg, int forceit, int keep_help)
{
- int save_magic;
bool save_p_ws;
int save_p_scs, save_p_ic;
linenr_T save_lnum;
- char_u *str;
- char_u *pbuf; // search pattern buffer
- char_u *pbuf_end;
- char_u *tofree_fname = NULL;
- char_u *fname;
+ char *str;
+ char *pbuf; // search pattern buffer
+ char *pbuf_end;
+ char *tofree_fname = NULL;
+ char *fname;
tagptrs_T tagp;
int retval = FAIL;
int getfile_result = GETFILE_UNUSED;
int search_options;
win_T *curwin_save = NULL;
- char_u *full_fname = NULL;
+ char *full_fname = NULL;
const bool old_KeyTyped = KeyTyped; // getting the file may reset it
const int l_g_do_tagpreview = g_do_tagpreview;
const size_t len = matching_line_len(lbuf_arg) + 1;
- char_u *lbuf = xmalloc(len);
+ char *lbuf = xmalloc(len);
memmove(lbuf, lbuf_arg, len);
pbuf = xmalloc(LSIZE);
@@ -2689,9 +2850,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
*pbuf_end = NUL;
{
- /*
- * Remove the "<Tab>fieldname:value" stuff; we don't need it here.
- */
+ // Remove the "<Tab>fieldname:value" stuff; we don't need it here.
str = pbuf;
if (find_extra(&str) == OK) {
pbuf_end = str;
@@ -2699,24 +2858,19 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
}
}
- /*
- * Expand file name, when needed (for environment variables).
- * If 'tagrelative' option set, may change file name.
- */
+ // Expand file name, when needed (for environment variables).
+ // If 'tagrelative' option set, may change file name.
fname = expand_tag_fname(fname, tagp.tag_fname, true);
tofree_fname = fname; // free() it later
- /*
- * Check if the file with the tag exists before abandoning the current
- * file. Also accept a file name for which there is a matching BufReadCmd
- * autocommand event (e.g., http://sys/file).
- */
+ // Check if the file with the tag exists before abandoning the current
+ // file. Also accept a file name for which there is a matching BufReadCmd
+ // autocommand event (e.g., http://sys/file).
if (!os_path_exists(fname)
- && !has_autocmd(EVENT_BUFREADCMD, (char *)fname,
- NULL)) {
+ && !has_autocmd(EVENT_BUFREADCMD, fname, NULL)) {
retval = NOTAGFILE;
xfree(nofile_fname);
- nofile_fname = vim_strsave(fname);
+ nofile_fname = xstrdup(fname);
goto erret;
}
@@ -2726,19 +2880,15 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
postponed_split = 0; // don't split again below
curwin_save = curwin; // Save current window
- /*
- * If we are reusing a window, we may change dir when
- * entering it (autocommands) so turn the tag filename
- * into a fullpath
- */
+ // If we are reusing a window, we may change dir when
+ // entering it (autocommands) so turn the tag filename
+ // into a fullpath
if (!curwin->w_p_pvw) {
- full_fname = (char_u *)FullName_save((char *)fname, FALSE);
+ full_fname = FullName_save(fname, false);
fname = full_fname;
- /*
- * Make the preview window the current window.
- * Open a preview window when needed.
- */
+ // Make the preview window the current window.
+ // Open a preview window when needed.
prepare_tagpreview(true);
}
}
@@ -2746,7 +2896,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((char *)fname);
+ buf_T *const existing_buf = buflist_findname_exp(fname);
if (existing_buf != NULL) {
const win_T *wp = NULL;
@@ -2791,7 +2941,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, (char *)fname, NULL, true, (linenr_T)0, forceit);
+ getfile_result = getfile(0, fname, NULL, true, (linenr_T)0, forceit);
}
keep_help_flag = false;
@@ -2799,41 +2949,37 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
curwin->w_set_curswant = true;
postponed_split = 0;
- save_magic = p_magic;
- p_magic = false; // always execute with 'nomagic'
+ const optmagic_T save_magic_overruled = magic_overruled;
+ magic_overruled = OPTION_MAGIC_OFF; // always execute with 'nomagic'
// Save value of no_hlsearch, jumping to a tag is not a real search
const bool save_no_hlsearch = no_hlsearch;
- /*
- * If 'cpoptions' contains 't', store the search pattern for the "n"
- * command. If 'cpoptions' does not contain 't', the search pattern
- * is not stored.
- */
+ // If 'cpoptions' contains 't', store the search pattern for the "n"
+ // command. If 'cpoptions' does not contain 't', the search pattern
+ // is not stored.
if (vim_strchr(p_cpo, CPO_TAGPAT) != NULL) {
search_options = 0;
} else {
search_options = SEARCH_KEEP;
}
- /*
- * If the command is a search, try here.
- *
- * Reset 'smartcase' for the search, since the search pattern was not
- * typed by the user.
- * Only use do_search() when there is a full search command, without
- * anything following.
- */
+ // If the command is a search, try here.
+ //
+ // Reset 'smartcase' for the search, since the search pattern was not
+ // typed by the user.
+ // Only use do_search() when there is a full search command, without
+ // anything following.
str = pbuf;
if (pbuf[0] == '/' || pbuf[0] == '?') {
- str = skip_regexp(pbuf + 1, pbuf[0], FALSE, NULL) + 1;
+ str = skip_regexp(pbuf + 1, pbuf[0], false) + 1;
}
if (str > pbuf_end - 1) { // search command with nothing following
save_p_ws = p_ws;
save_p_ic = p_ic;
save_p_scs = p_scs;
p_ws = true; // need 'wrapscan' for backward searches
- p_ic = FALSE; // don't ignore case now
- p_scs = FALSE;
+ p_ic = false; // don't ignore case now
+ p_scs = false;
save_lnum = curwin->w_cursor.lnum;
if (tagp.tagline > 0) {
// start search before line from "line:" field
@@ -2847,11 +2993,9 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
retval = OK;
} else {
int found = 1;
- int cc;
+ char cc;
- /*
- * try again, ignore case now
- */
+ // try again, ignore case now
p_ic = true;
if (!do_search(NULL, pbuf[0], pbuf[0], pbuf + 1, (long)1,
search_options, NULL)) {
@@ -2860,26 +3004,23 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
(void)test_for_static(&tagp);
cc = *tagp.tagname_end;
*tagp.tagname_end = NUL;
- snprintf((char *)pbuf, LSIZE, "^%s\\s\\*(", tagp.tagname);
+ snprintf(pbuf, LSIZE, "^%s\\s\\*(", tagp.tagname);
if (!do_search(NULL, '/', '/', pbuf, (long)1, search_options, NULL)) {
// Guess again: "^char * \<func ("
- snprintf((char *)pbuf, LSIZE, "^\\[#a-zA-Z_]\\.\\*\\<%s\\s\\*(",
+ snprintf(pbuf, LSIZE, "^\\[#a-zA-Z_]\\.\\*\\<%s\\s\\*(",
tagp.tagname);
- if (!do_search(NULL, '/', '/', pbuf, (long)1,
- search_options, NULL)) {
+ if (!do_search(NULL, '/', '/', pbuf, (long)1, search_options, NULL)) {
found = 0;
}
}
- *tagp.tagname_end = (char_u)cc;
+ *tagp.tagname_end = cc;
}
if (found == 0) {
emsg(_("E434: Can't find tag pattern"));
curwin->w_cursor.lnum = save_lnum;
} else {
- /*
- * Only give a message when really guessed, not when 'ic'
- * is set and match found while ignoring case.
- */
+ // Only give a message when really guessed, not when 'ic'
+ // is set and match found while ignoring case.
if (found == 2 || !save_p_ic) {
msg(_("E435: Couldn't find tag, just guessing!"));
if (!msg_scrolled && msg_silent == 0) {
@@ -2904,7 +3045,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
secure = 1;
sandbox++;
curwin->w_cursor.lnum = 1; // start command in line 1
- do_cmdline_cmd((char *)pbuf);
+ do_cmdline_cmd(pbuf);
retval = OK;
// When the command has done something that is not allowed make sure
@@ -2916,7 +3057,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
sandbox--;
}
- p_magic = save_magic;
+ magic_overruled = save_magic_overruled;
// restore no_hlsearch when keeping the old search pattern
if (search_options) {
set_no_hlsearch(save_no_hlsearch);
@@ -2928,10 +3069,8 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
}
if (retval == OK) {
- /*
- * For a help buffer: Put the cursor line at the top of the window,
- * the help subject will be below it.
- */
+ // For a help buffer: Put the cursor line at the top of the window,
+ // the help subject will be below it.
if (curbuf->b_help) {
set_topline(curwin, curwin->w_cursor.lnum);
}
@@ -2944,7 +3083,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
&& curwin != curwin_save && win_valid(curwin_save)) {
// Return cursor to where we were
validate_cursor();
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
win_enter(curwin_save, true);
}
@@ -2967,19 +3106,18 @@ erret:
return retval;
}
-// If "expand" is true, expand wildcards in fname.
-// If 'tagrelative' option set, change fname (name of file containing tag)
-// according to tag_fname (name of tag file containing fname).
-// Returns a pointer to allocated memory.
-static char_u *expand_tag_fname(char_u *fname, char_u *const tag_fname, const bool expand)
+/// If "expand" is true, expand wildcards in fname.
+/// If 'tagrelative' option set, change fname (name of file containing tag)
+/// according to tag_fname (name of tag file containing fname).
+///
+/// @return a pointer to allocated memory.
+static char *expand_tag_fname(char *fname, char *const tag_fname, const bool expand)
{
- char_u *p;
- char_u *expanded_fname = NULL;
+ char *p;
+ char *expanded_fname = NULL;
expand_T xpc;
- /*
- * Expand file name (for environment variables) when needed.
- */
+ // Expand file name (for environment variables) when needed.
if (expand && path_has_wildcard(fname)) {
ExpandInit(&xpc);
xpc.xp_context = EXPAND_FILES;
@@ -2990,20 +3128,17 @@ static char_u *expand_tag_fname(char_u *fname, char_u *const tag_fname, const bo
}
}
- char_u *retval;
+ char *retval;
if ((p_tr || curbuf->b_help)
&& !vim_isAbsName(fname)
- && (p = (char_u *)path_tail((char *)tag_fname)) != tag_fname) {
+ && (p = path_tail(tag_fname)) != tag_fname) {
retval = xmalloc(MAXPATHL);
STRCPY(retval, tag_fname);
- STRLCPY(retval + (p - tag_fname), fname,
- MAXPATHL - (p - tag_fname));
- /*
- * Translate names like "src/a/../b/file.c" into "src/b/file.c".
- */
+ xstrlcpy(retval + (p - tag_fname), fname, (size_t)(MAXPATHL - (p - tag_fname)));
+ // Translate names like "src/a/../b/file.c" into "src/b/file.c".
simplify_filename(retval);
} else {
- retval = vim_strsave(fname);
+ retval = xstrdup(fname);
}
xfree(expanded_fname);
@@ -3011,47 +3146,43 @@ static char_u *expand_tag_fname(char_u *fname, char_u *const tag_fname, const bo
return retval;
}
-/*
- * Check if we have a tag for the buffer with name "buf_ffname".
- * This is a bit slow, because of the full path compare in path_full_compare().
- * Return TRUE if tag for file "fname" if tag file "tag_fname" is for current
- * file.
- */
-static int test_for_current(char_u *fname, char_u *fname_end, char_u *tag_fname, char_u *buf_ffname)
+/// Check if we have a tag for the buffer with name "buf_ffname".
+/// This is a bit slow, because of the full path compare in path_full_compare().
+///
+/// @return true if tag for file "fname" if tag file "tag_fname" is for current
+/// file.
+static int test_for_current(char *fname, char *fname_end, char *tag_fname, char *buf_ffname)
{
- int c;
- int retval = FALSE;
- char_u *fullname;
+ char c;
+ int retval = false;
if (buf_ffname != NULL) { // if the buffer has a name
{
c = *fname_end;
*fname_end = NUL;
}
- fullname = expand_tag_fname(fname, tag_fname, true);
- retval = (path_full_compare((char *)fullname, (char *)buf_ffname, true, true) & kEqualFiles);
+ char *fullname = expand_tag_fname(fname, tag_fname, true);
+ retval = (path_full_compare(fullname, buf_ffname, true, true) & kEqualFiles);
xfree(fullname);
- *fname_end = (char_u)c;
+ *fname_end = c;
}
return retval;
}
-/*
- * Find the end of the tagaddress.
- * Return OK if ";\"" is following, FAIL otherwise.
- */
-static int find_extra(char_u **pp)
+// Find the end of the tagaddress.
+// Return OK if ";\"" is following, FAIL otherwise.
+static int find_extra(char **pp)
{
- char_u *str = *pp;
- char_u first_char = **pp;
+ char *str = *pp;
+ char first_char = **pp;
// Repeat for addresses separated with ';'
for (;;) {
if (ascii_isdigit(*str)) {
- str = (char_u *)skipdigits((char *)str + 1);
+ str = skipdigits(str + 1);
} else if (*str == '/' || *str == '?') {
- str = skip_regexp(str + 1, *str, false, NULL);
+ str = skip_regexp(str + 1, *str, false);
if (*str != first_char) {
str = NULL;
} else {
@@ -3059,7 +3190,7 @@ static int find_extra(char_u **pp)
}
} else {
// not a line number or search string, look for terminator.
- str = (char_u *)strstr((char *)str, "|;\"");
+ str = strstr(str, "|;\"");
if (str != NULL) {
str++;
break;
@@ -3073,7 +3204,7 @@ static int find_extra(char_u **pp)
first_char = *str;
}
- if (str != NULL && STRNCMP(str, ";\"", 2) == 0) {
+ if (str != NULL && strncmp(str, ";\"", 2) == 0) {
*pp = str;
return OK;
}
@@ -3090,11 +3221,11 @@ static void tagstack_clear_entry(taggy_T *item)
}
/// @param tagnames expand tag names
-int expand_tags(int tagnames, char_u *pat, int *num_file, char ***file)
+int expand_tags(int tagnames, char *pat, int *num_file, char ***file)
{
int i;
int extra_flag;
- char_u *name_buf;
+ char *name_buf;
size_t name_buf_size = 100;
tagptrs_T t_p;
int ret;
@@ -3109,11 +3240,11 @@ int expand_tags(int tagnames, char_u *pat, int *num_file, char ***file)
if (pat[0] == '/') {
ret = find_tags(pat + 1, num_file, file,
TAG_REGEXP | extra_flag | TAG_VERBOSE | TAG_NO_TAGFUNC,
- TAG_MANY, (char_u *)curbuf->b_ffname);
+ TAG_MANY, curbuf->b_ffname);
} else {
ret = find_tags(pat, num_file, file,
TAG_REGEXP | extra_flag | TAG_VERBOSE | TAG_NO_TAGFUNC | TAG_NOIC,
- TAG_MANY, (char_u *)curbuf->b_ffname);
+ TAG_MANY, curbuf->b_ffname);
}
if (ret == OK && !tagnames) {
// Reorganize the tags for display and matching as strings of:
@@ -3121,10 +3252,10 @@ int expand_tags(int tagnames, char_u *pat, int *num_file, char ***file)
for (i = 0; i < *num_file; i++) {
size_t len;
- parse_match((char_u *)(*file)[i], &t_p);
+ parse_match((*file)[i], &t_p);
len = (size_t)(t_p.tagname_end - t_p.tagname);
if (len > name_buf_size - 3) {
- char_u *buf;
+ char *buf;
name_buf_size = len + 3;
buf = xrealloc(name_buf, name_buf_size);
@@ -3150,8 +3281,7 @@ int expand_tags(int tagnames, char_u *pat, int *num_file, char ***file)
///
/// @param start start of the value
/// @param end after the value; can be NULL
-static int add_tag_field(dict_T *dict, const char *field_name, const char_u *start,
- const char_u *end)
+static int add_tag_field(dict_T *dict, const char *field_name, const char *start, const char *end)
FUNC_ATTR_NONNULL_ARG(1, 2)
{
int len = 0;
@@ -3166,10 +3296,10 @@ static int add_tag_field(dict_T *dict, const char *field_name, const char_u *sta
}
return FAIL;
}
- char_u *buf = xmalloc(MAXPATHL);
+ char *buf = xmalloc(MAXPATHL);
if (start != NULL) {
if (end == NULL) {
- end = start + STRLEN(start);
+ end = start + strlen(start);
while (end > start && (end[-1] == '\r' || end[-1] == '\n')) {
end--;
}
@@ -3178,22 +3308,21 @@ static int add_tag_field(dict_T *dict, const char *field_name, const char_u *sta
if (len > MAXPATHL - 1) {
len = MAXPATHL - 1;
}
- STRLCPY(buf, start, len + 1);
+ xstrlcpy(buf, start, (size_t)len + 1);
}
buf[len] = NUL;
- retval = tv_dict_add_str(dict, field_name, STRLEN(field_name),
- (const char *)buf);
+ retval = tv_dict_add_str(dict, field_name, strlen(field_name), buf);
xfree(buf);
return retval;
}
/// Add the tags matching the specified pattern "pat" to the list "list"
/// as a dictionary. Use "buf_fname" for priority, unless NULL.
-int get_tags(list_T *list, char_u *pat, char_u *buf_fname)
+int get_tags(list_T *list, char *pat, char *buf_fname)
{
int num_matches, i, ret;
char **matches;
- char_u *full_fname;
+ char *full_fname;
dict_T *dict;
tagptrs_T tp;
bool is_static;
@@ -3202,7 +3331,7 @@ int get_tags(list_T *list, char_u *pat, char_u *buf_fname)
TAG_REGEXP | TAG_NOIC, MAXCOL, buf_fname);
if (ret == OK && num_matches > 0) {
for (i = 0; i < num_matches; i++) {
- int parse_result = parse_match((char_u *)matches[i], &tp);
+ int parse_result = parse_match(matches[i], &tp);
// Avoid an unused variable warning in release builds.
(void)parse_result;
@@ -3211,7 +3340,7 @@ int get_tags(list_T *list, char_u *pat, char_u *buf_fname)
is_static = test_for_static(&tp);
// Skip pseudo-tag lines.
- if (STRNCMP(tp.tagname, "!_TAG_", 6) == 0) {
+ if (strncmp(tp.tagname, "!_TAG_", 6) == 0) {
xfree(matches[i]);
continue;
}
@@ -3232,18 +3361,18 @@ int get_tags(list_T *list, char_u *pat, char_u *buf_fname)
xfree(full_fname);
if (tp.command_end != NULL) {
- for (char_u *p = tp.command_end + 3;
+ for (char *p = tp.command_end + 3;
*p != NUL && *p != '\n' && *p != '\r';
MB_PTR_ADV(p)) {
if (p == tp.tagkind
- || (p + 5 == tp.tagkind && STRNCMP(p, "kind:", 5) == 0)) {
+ || (p + 5 == tp.tagkind && strncmp(p, "kind:", 5) == 0)) {
// skip "kind:<kind>" and "<kind>"
p = tp.tagkind_end - 1;
- } else if (STRNCMP(p, "file:", 5) == 0) {
+ } else if (strncmp(p, "file:", 5) == 0) {
// skip "file:" (static tag)
p += 4;
} else if (!ascii_iswhite(*p)) {
- char_u *s, *n;
+ char *s, *n;
int len;
// Add extra field as a dict entry. Fields are
@@ -3255,17 +3384,17 @@ int get_tags(list_T *list, char_u *pat, char_u *buf_fname)
len = (int)(p - n);
if (*p == ':' && len > 0) {
s = ++p;
- while (*p != NUL && *p >= ' ') {
+ while (*p != NUL && (uint8_t)(*p) >= ' ') {
p++;
}
n[len] = NUL;
- if (add_tag_field(dict, (char *)n, s, p) == FAIL) {
+ if (add_tag_field(dict, n, s, p) == FAIL) {
ret = FAIL;
}
n[len] = ':';
} else {
// Skip field without colon.
- while (*p != NUL && *p >= ' ') {
+ while (*p != NUL && (uint8_t)(*p) >= ' ') {
p++;
}
}
@@ -3351,9 +3480,9 @@ static void tagstack_shift(win_T *wp)
wp->w_tagstacklen--;
}
-// Push a new item to the tag stack
-static void tagstack_push_item(win_T *wp, char_u *tagname, int cur_fnum, int cur_match, pos_T mark,
- int fnum, char_u *user_data)
+/// Push a new item to the tag stack
+static void tagstack_push_item(win_T *wp, char *tagname, int cur_fnum, int cur_match, pos_T mark,
+ int fnum, char *user_data)
{
taggy_T *tagstack = wp->w_tagstack;
int idx = wp->w_tagstacklen; // top of the stack
@@ -3376,13 +3505,13 @@ static void tagstack_push_item(win_T *wp, char_u *tagname, int cur_fnum, int cur
tagstack[idx].user_data = user_data;
}
-// Add a list of items to the tag stack in the specified window
+/// Add a list of items to the tag stack in the specified window
static void tagstack_push_items(win_T *wp, list_T *l)
{
listitem_T *li;
dictitem_T *di;
dict_T *itemdict;
- char_u *tagname;
+ char *tagname;
pos_T mark;
int fnum;
@@ -3401,8 +3530,7 @@ static void tagstack_push_items(win_T *wp, list_T *l)
if (list2fpos(&di->di_tv, &mark, &fnum, NULL, false) != OK) {
continue;
}
- if ((tagname = (char_u *)tv_dict_get_string(itemdict, "tagname", true))
- == NULL) {
+ if ((tagname = tv_dict_get_string(itemdict, "tagname", true)) == NULL) {
continue;
}
@@ -3414,7 +3542,7 @@ static void tagstack_push_items(win_T *wp, list_T *l)
(int)tv_dict_get_number(itemdict, "bufnr"),
(int)tv_dict_get_number(itemdict, "matchnr") - 1,
mark, fnum,
- (char_u *)tv_dict_get_string(itemdict, "user_data", true));
+ tv_dict_get_string(itemdict, "user_data", true));
}
}
@@ -3459,10 +3587,12 @@ int set_tagstack(win_T *wp, const dict_T *d, int action)
if ((di = tv_dict_find(d, "curidx", -1)) != NULL) {
tagstack_set_curidx(wp, (int)tv_get_number(&di->di_tv) - 1);
}
+
if (action == 't') { // truncate the stack
taggy_T *const tagstack = wp->w_tagstack;
const int tagstackidx = wp->w_tagstackidx;
int tagstacklen = wp->w_tagstacklen;
+
// delete all the tag stack entries above the current entry
while (tagstackidx < tagstacklen) {
tagstack_clear_entry(&tagstack[--tagstacklen]);
diff --git a/src/nvim/tag.h b/src/nvim/tag.h
index 0b4039afb6..e08145f727 100644
--- a/src/nvim/tag.h
+++ b/src/nvim/tag.h
@@ -14,7 +14,6 @@
#define DT_SELECT 7 // jump to selection from list
#define DT_HELP 8 // like DT_TAG, but no wildcards
#define DT_JUMP 9 // jump to new tag or selection from list
-#define DT_CSCOPE 10 // cscope find command (like tjump)
#define DT_LTAG 11 // tag using location list
#define DT_FREE 99 // free cached matches
@@ -23,7 +22,6 @@
#define TAG_NAMES 2 // only return name of tag
#define TAG_REGEXP 4 // use tag pattern as regexp
#define TAG_NOIC 8 // don't always ignore case
-#define TAG_CSCOPE 16 // cscope tag
#define TAG_VERBOSE 32 // message verbosity
#define TAG_INS_COMP 64 // Currently doing insert completion
#define TAG_KEEP_LANG 128 // keep current language
@@ -34,7 +32,7 @@
// Structure used for get_tagfname().
typedef struct {
- char_u *tn_tags; // value of 'tags' when starting
+ char *tn_tags; // value of 'tags' when starting
char *tn_np; // current position in tn_tags
int tn_did_filefind_init;
int tn_hf_idx;
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 08fcefaa88..a52a34cd66 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -37,45 +37,57 @@
// Some code from pangoterm http://www.leonerd.org.uk/code/pangoterm
#include <assert.h>
+#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <vterm.h>
+#include <vterm_keycodes.h>
+#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
#include "nvim/autocmd.h"
#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
#include "nvim/change.h"
+#include "nvim/channel.h"
#include "nvim/cursor.h"
+#include "nvim/drawline.h"
#include "nvim/drawscreen.h"
#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
#include "nvim/event/time.h"
-#include "nvim/ex_cmds.h"
#include "nvim/ex_docmd.h"
#include "nvim/getchar.h"
+#include "nvim/globals.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
#include "nvim/keycodes.h"
-#include "nvim/log.h"
#include "nvim/macros.h"
#include "nvim/main.h"
#include "nvim/map.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
-#include "nvim/message.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
+#include "nvim/msgpack_rpc/channel_defs.h"
+#include "nvim/normal.h"
#include "nvim/option.h"
#include "nvim/optionstr.h"
-#include "nvim/os/input.h"
+#include "nvim/pos.h"
+#include "nvim/screen.h"
#include "nvim/state.h"
#include "nvim/terminal.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
-#include "nvim/window.h"
typedef struct terminal_state {
VimState state;
@@ -111,15 +123,19 @@ struct terminal {
// - receive data from libvterm as a result of key presses.
char textbuf[0x1fff];
- ScrollbackLine **sb_buffer; // Scrollback buffer storage for libvterm
- size_t sb_current; // number of rows pushed to sb_buffer
- size_t sb_size; // sb_buffer size
+ ScrollbackLine **sb_buffer; // Scrollback storage.
+ size_t sb_current; // Lines stored in sb_buffer.
+ size_t sb_size; // Capacity of sb_buffer.
// "virtual index" that points to the first sb_buffer row that we need to
// push to the terminal buffer when refreshing the scrollback. When negative,
// it actually points to entries that are no longer in sb_buffer (because the
// window height has increased) and must be deleted from the terminal buffer
int sb_pending;
+ char *title; // VTermStringFragment buffer
+ size_t title_len; // number of rows pushed to sb_buffer
+ size_t title_size; // sb_buffer size
+
// buf_T instance that acts as a "drawing surface" for libvterm
// we can't store a direct reference to the buffer because the
// refresh_timer_cb may be called after the buffer was freed, and there's
@@ -150,7 +166,7 @@ static VTermScreenCallbacks vterm_screen_callbacks = {
.movecursor = term_movecursor,
.settermprop = term_settermprop,
.bell = term_bell,
- .sb_pushline = term_sb_push,
+ .sb_pushline = term_sb_push, // Called before a line goes offscreen.
.sb_popline = term_sb_pop,
};
@@ -230,7 +246,7 @@ 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, buf->b_ffname);
+ buf_set_term_title(buf, buf->b_ffname, strlen(buf->b_ffname));
}
RESET_BINDING(curwin);
// Reset cursor in current window.
@@ -371,7 +387,7 @@ void terminal_check_size(Terminal *term)
// Check if there is a window that displays the terminal and find the maximum width and height.
// Skip the autocommand window which isn't actually displayed.
FOR_ALL_TAB_WINDOWS(tp, wp) {
- if (wp == aucmd_win) {
+ if (is_aucmd_win(wp)) {
continue;
}
if (wp->w_buffer && wp->w_buffer->terminal == term) {
@@ -418,15 +434,15 @@ bool terminal_enter(void)
// placed at end of buffer to "follow" output. #11072
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;
+ char *save_w_p_culopt = NULL;
+ uint8_t save_w_p_culopt_flags = curwin->w_p_culopt_flags;
int save_w_p_cuc = curwin->w_p_cuc;
long save_w_p_so = curwin->w_p_so;
long save_w_p_siso = curwin->w_p_siso;
if (curwin->w_p_cul && curwin->w_p_culopt_flags & CULOPT_NBR) {
- if (STRCMP(curwin->w_p_culopt, "number")) {
+ if (strcmp(curwin->w_p_culopt, "number") != 0) {
save_w_p_culopt = curwin->w_p_culopt;
- curwin->w_p_culopt = (char_u *)xstrdup("number");
+ curwin->w_p_culopt = xstrdup("number");
}
curwin->w_p_culopt_flags = CULOPT_NBR;
} else {
@@ -518,7 +534,19 @@ static int terminal_check(VimState *state)
terminal_check_cursor();
if (must_redraw) {
- update_screen(0);
+ update_screen();
+
+ // Make sure an invoked autocmd doesn't delete the buffer (and the
+ // terminal) under our fingers.
+ curbuf->b_locked++;
+
+ // save and restore curwin and curbuf, in case the autocmd changes them
+ aco_save_T aco;
+ aucmd_prepbuf(&aco, curbuf);
+ apply_autocmds(EVENT_TEXTCHANGEDT, NULL, NULL, false, curbuf);
+ aucmd_restbuf(&aco);
+
+ curbuf->b_locked--;
}
if (need_maketitle) { // Update title in terminal-mode. #7248
@@ -636,6 +664,7 @@ void terminal_destroy(Terminal **termpp)
xfree(term->sb_buffer[i]);
}
xfree(term->sb_buffer);
+ xfree(term->title);
vterm_free(term->vt);
xfree(term);
*termpp = NULL; // coverity[dead-store]
@@ -688,8 +717,8 @@ void terminal_paste(long count, char **y_array, size_t y_size)
return;
}
vterm_keyboard_start_paste(curbuf->terminal->vt);
- size_t buff_len = STRLEN(y_array[0]);
- char_u *buff = xmalloc(buff_len);
+ size_t buff_len = strlen(y_array[0]);
+ char *buff = xmalloc(buff_len);
for (int i = 0; i < count; i++) { // -V756
// feed the lines to the terminal
for (size_t j = 0; j < y_size; j++) {
@@ -697,23 +726,23 @@ void terminal_paste(long count, char **y_array, size_t y_size)
// terminate the previous line
terminal_send(curbuf->terminal, "\n", 1);
}
- size_t len = STRLEN(y_array[j]);
+ size_t len = strlen(y_array[j]);
if (len > buff_len) {
buff = xrealloc(buff, len);
buff_len = len;
}
- char_u *dst = buff;
- char_u *src = (char_u *)y_array[j];
+ char *dst = buff;
+ char *src = y_array[j];
while (*src != '\0') {
- len = (size_t)utf_ptr2len((char *)src);
- int c = utf_ptr2char((char *)src);
+ len = (size_t)utf_ptr2len(src);
+ int c = utf_ptr2char(src);
if (!is_filter_char(c)) {
memcpy(dst, src, len);
dst += len;
}
src += len;
}
- terminal_send(curbuf->terminal, (char *)buff, (size_t)(dst - buff));
+ terminal_send(curbuf->terminal, buff, (size_t)(dst - buff));
}
}
xfree(buff);
@@ -754,6 +783,22 @@ static int get_rgb(VTermState *state, VTermColor color)
return RGB_(color.rgb.red, color.rgb.green, color.rgb.blue);
}
+static int get_underline_hl_flag(VTermScreenCellAttrs attrs)
+{
+ switch (attrs.underline) {
+ case VTERM_UNDERLINE_OFF:
+ return 0;
+ case VTERM_UNDERLINE_SINGLE:
+ return HL_UNDERLINE;
+ case VTERM_UNDERLINE_DOUBLE:
+ return HL_UNDERDOUBLE;
+ case VTERM_UNDERLINE_CURLY:
+ return HL_UNDERCURL;
+ default:
+ return HL_UNDERLINE;
+ }
+}
+
void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, int *term_attrs)
{
int height, width;
@@ -790,7 +835,7 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, int *te
int hl_attrs = (cell.attrs.bold ? HL_BOLD : 0)
| (cell.attrs.italic ? HL_ITALIC : 0)
| (cell.attrs.reverse ? HL_INVERSE : 0)
- | (cell.attrs.underline ? HL_UNDERLINE : 0)
+ | get_underline_hl_flag(cell.attrs)
| (cell.attrs.strike ? HL_STRIKETHROUGH: 0)
| ((fg_indexed && !fg_set) ? HL_FG_INDEXED : 0)
| ((bg_indexed && !bg_set) ? HL_BG_INDEXED : 0);
@@ -858,13 +903,13 @@ static int term_movecursor(VTermPos new, VTermPos old, int visible, void *data)
return 1;
}
-static void buf_set_term_title(buf_T *buf, char *title)
+static void buf_set_term_title(buf_T *buf, const char *title, size_t len)
FUNC_ATTR_NONNULL_ALL
{
Error err = ERROR_INIT;
dict_set_var(buf->b_vars,
STATIC_CSTR_AS_STRING("term_title"),
- STRING_OBJ(cstr_as_string(title)),
+ STRING_OBJ(((String){ .data = (char *)title, .size = len })),
false,
false,
&err);
@@ -887,7 +932,30 @@ static int term_settermprop(VTermProp prop, VTermValue *val, void *data)
case VTERM_PROP_TITLE: {
buf_T *buf = handle_get_buffer(term->buf_handle);
- buf_set_term_title(buf, val->string);
+ VTermStringFragment frag = val->string;
+
+ if (frag.initial && frag.final) {
+ buf_set_term_title(buf, frag.str, frag.len);
+ break;
+ }
+
+ if (frag.initial) {
+ term->title_len = 0;
+ term->title_size = MAX(frag.len, 1024);
+ term->title = xmalloc(sizeof(char *) * term->title_size);
+ } else if (term->title_len + frag.len > term->title_size) {
+ term->title_size *= 2;
+ term->title = xrealloc(term->title, sizeof(char *) * term->title_size);
+ }
+
+ memcpy(term->title + term->title_len, frag.str, frag.len);
+ term->title_len += frag.len;
+
+ if (frag.final) {
+ buf_set_term_title(buf, term->title, term->title_len);
+ xfree(term->title);
+ term->title = NULL;
+ }
break;
}
@@ -908,7 +976,10 @@ static int term_bell(void *data)
return 1;
}
-// Scrollback push handler (from pangoterm).
+/// Scrollback push handler: called just before a line goes offscreen (and libvterm will forget it),
+/// giving us a chance to store it.
+///
+/// Code adapted from pangoterm.
static int term_sb_push(int cols, const VTermScreenCell *cells, void *data)
{
Terminal *term = data;
@@ -1368,14 +1439,14 @@ static bool send_mouse_event(Terminal *term, int c)
int direction = c == K_MOUSEDOWN ? MSCR_DOWN : MSCR_UP;
if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
scroll_redraw(direction, curwin->w_botline - curwin->w_topline);
- } else {
- scroll_redraw(direction, 3L);
+ } else if (p_mousescroll_vert > 0) {
+ scroll_redraw(direction, p_mousescroll_vert);
}
curwin->w_redr_status = true;
curwin = save_curwin;
curbuf = curwin->w_buffer;
- redraw_later(mouse_win, NOT_VALID);
+ redraw_later(mouse_win, UPD_NOT_VALID);
invalidate_terminal(term, -1, -1);
// Only need to exit focus if the scrolled window is the terminal window
return mouse_win == curwin;
diff --git a/src/nvim/testdir/Make_all.mak b/src/nvim/testdir/Make_all.mak
new file mode 100644
index 0000000000..b917877422
--- /dev/null
+++ b/src/nvim/testdir/Make_all.mak
@@ -0,0 +1 @@
+# Tests may depend on the existence of this file.
diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile
index 4641408069..9511b311e6 100644
--- a/src/nvim/testdir/Makefile
+++ b/src/nvim/testdir/Makefile
@@ -11,16 +11,7 @@ ROOT := ../../..
export SHELL := sh
export NVIM_PRG := $(NVIM_PRG)
-export TMPDIR := $(abspath Xtest-tmpdir)
-
-SCRIPTS_DEFAULT = \
- test42.out \
-
-ifneq ($(OS),Windows_NT)
- SCRIPTS_DEFAULTS := $(SCRIPTS_DEFAULT) \
- test49.out \
-
-endif
+export TMPDIR := $(abspath X-test-tmpdir)
ifeq ($(OS),Windows_NT)
FIXFF = fixff
@@ -28,8 +19,6 @@ else
FIXFF =
endif
-SCRIPTS ?= $(SCRIPTS_DEFAULT)
-
# Tests using runtest.vim.
NEW_TESTS_ALOT := test_alot_utf8 test_alot test_alot_latin
NEW_TESTS_IN_ALOT := $(shell sed -n '/^source/ s/^source //; s/\.vim$$//p' $(addsuffix .vim,$(NEW_TESTS_ALOT)))
@@ -66,11 +55,7 @@ else
endif
endif
-ifdef TESTNUM
- SCRIPTS := test$(TESTNUM).out
-endif
-
-nongui: nolog $(FIXFF) $(SCRIPTS) newtests report
+nongui: nolog $(FIXFF) newtests report
.gdbinit:
@echo "[OLDTEST-PREP] Setting up .gdbinit"
@@ -88,8 +73,6 @@ report:
test1.out: $(NVIM_PRG)
-$(SCRIPTS): $(NVIM_PRG) test1.out
-
NO_PLUGINS = --noplugin --headless
# In vim, if the -u command line option is specified, compatible is turned on
# and viminfo is not read. Unlike vim, neovim reads viminfo and requires the
@@ -147,17 +130,6 @@ test1.out: .gdbinit test1.in
@rm -f wrongtermsize
@rm -rf X* viminfo
-%.out: %.in .gdbinit
- @echo "[OLDESTTEST] Running" $*
- @rm -rf $*.failed test.ok $(RM_ON_RUN)
- @mkdir -p $(TMPDIR)
- @cp $*.ok test.ok
- @/bin/sh runnvim.sh --oldesttest $(ROOT) $(NVIM_PRG) $* $(RUN_VIM) $*.in
- @rm -rf X* test.ok viminfo
-
-# Explicit dependencies.
-test49.out: test49.vim
-
nolog:
@echo "[OLDTEST-PREP] Removing test.log and messages"
@rm -f test.log messages
@@ -179,4 +151,4 @@ newtestssilent: $(NEW_TESTS_RES)
@echo "[OLDTEST] Running" $*
@rm -rf $*.failed test.ok $(RM_ON_RUN)
@mkdir -p $(TMPDIR)
- @/bin/sh runnvim.sh $(ROOT) $(NVIM_PRG) $* $(RUN_VIMTEST) $(NO_INITS) -u NONE -S runtest.vim $*.vim
+ @/bin/sh runnvim.sh $(ROOT) $(NVIM_PRG) $* $(RUN_VIMTEST) $(NO_INITS) -u NONE --cmd "set shortmess-=F" -S runtest.vim $*.vim
diff --git a/src/nvim/testdir/check.vim b/src/nvim/testdir/check.vim
index 4107df99d6..680a59006b 100644
--- a/src/nvim/testdir/check.vim
+++ b/src/nvim/testdir/check.vim
@@ -1,11 +1,16 @@
source shared.vim
source term_util.vim
+command -nargs=1 MissingFeature throw 'Skipped: ' .. <args> .. ' feature missing'
+
" Command to check for the presence of a feature.
command -nargs=1 CheckFeature call CheckFeature(<f-args>)
func CheckFeature(name)
+ " if !has(a:name, 1)
+ " throw 'Checking for non-existent feature ' .. a:name
+ " endif
if !has(a:name)
- throw 'Skipped: ' .. a:name .. ' feature missing'
+ MissingFeature a:name
endif
endfunc
@@ -39,6 +44,22 @@ func CheckFunction(name)
endif
endfunc
+" Command to check for the presence of an Ex command
+command -nargs=1 CheckCommand call CheckCommand(<f-args>)
+func CheckCommand(name)
+ if !exists(':' .. a:name)
+ throw 'Skipped: ' .. a:name .. ' command not supported'
+ endif
+endfunc
+
+" Command to check for the presence of a shell command
+command -nargs=1 CheckExecutable call CheckExecutable(<f-args>)
+func CheckExecutable(name)
+ if !executable(a:name)
+ throw 'Skipped: ' .. a:name .. ' program not executable'
+ endif
+endfunc
+
" Command to check for the presence of python. Argument should have been
" obtained with PythonProg()
func CheckPython(name)
@@ -71,12 +92,11 @@ 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'
+" Command to check for running on Linux
+command CheckLinux call CheckLinux()
+func CheckLinux()
+ if !has('linux')
+ throw 'Skipped: only works on Linux'
endif
endfunc
@@ -105,6 +125,14 @@ func CheckCanRunGui()
endif
endfunc
+" Command to Check for an environment variable
+command -nargs=1 CheckEnv call CheckEnv(<f-args>)
+func CheckEnv(name)
+ if empty(eval('$' .. a:name))
+ throw 'Skipped: Environment variable ' .. a:name .. ' is not set'
+ endif
+endfunc
+
" Command to check that we are using the GUI
command CheckGui call CheckGui()
func CheckGui()
@@ -145,6 +173,22 @@ func CheckNotAsan()
endif
endfunc
+" Command to check for not running under valgrind
+command CheckNotValgrind call CheckNotValgrind()
+func CheckNotValgrind()
+ if RunningWithValgrind()
+ throw 'Skipped: does not work well with valgrind'
+ endif
+endfunc
+
+" Command to check for X11 based GUI
+command CheckX11BasedGui call CheckX11BasedGui()
+func CheckX11BasedGui()
+ if !g:x11_based_gui
+ throw 'Skipped: requires X11 based GUI'
+ endif
+endfunc
+
" Command to check for satisfying any of the conditions.
" e.g. CheckAnyOf Feature:bsd Feature:sun Linux
command -nargs=+ CheckAnyOf call CheckAnyOf(<f-args>)
diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim
index fcd3d5724c..3c5699af73 100644
--- a/src/nvim/testdir/runtest.vim
+++ b/src/nvim/testdir/runtest.vim
@@ -105,7 +105,7 @@ set nomore
lang mess C
" Nvim: append runtime from build dir, which contains the generated doc/tags.
-let &runtimepath .= ','.expand($BUILD_DIR).'/runtime/'
+let &runtimepath ..= ',' .. expand($BUILD_DIR) .. '/runtime/'
let s:t_bold = &t_md
let s:t_normal = &t_me
@@ -114,6 +114,13 @@ if has('win32')
let $PROMPT = '$P$G'
endif
+if has('mac')
+ " In MacOS, when starting a shell in a terminal, a bash deprecation warning
+ " message is displayed. This breaks the terminal test. Disable the warning
+ " message.
+ let $BASH_SILENCE_DEPRECATION_WARNING = 1
+endif
+
" Prepare for calling test_garbagecollect_now().
let v:testing = 1
@@ -164,16 +171,19 @@ func RunTheTest(test)
endtry
endif
+ au VimLeavePre * call EarlyExit(g:testfunc)
if a:test =~ 'Test_nocatch_'
" Function handles errors itself. This avoids skipping commands after the
" error.
+ let g:skipped_reason = ''
exe 'call ' . a:test
+ if g:skipped_reason != ''
+ call add(s:messages, ' Skipped')
+ call add(s:skipped, 'SKIPPED ' . a:test . ': ' . g:skipped_reason)
+ endif
else
try
- let s:test = a:test
- au VimLeavePre * call EarlyExit(s:test)
exe 'call ' . a:test
- au! VimLeavePre
catch /^\cskipped/
call add(s:messages, ' Skipped')
call add(s:skipped, 'SKIPPED ' . a:test . ': ' . substitute(v:exception, '^\S*\s\+', '', ''))
@@ -181,6 +191,7 @@ func RunTheTest(test)
call add(v:errors, 'Caught exception in ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
endtry
endif
+ au! VimLeavePre
" In case 'insertmode' was set and something went wrong, make sure it is
" reset to avoid trouble with anything else.
@@ -243,11 +254,11 @@ func AfterTheTest(func_name)
if len(v:errors) > 0
if match(s:may_fail_list, '^' .. a:func_name) >= 0
let s:fail_expected += 1
- call add(s:errors_expected, 'Found errors in ' . s:test . ':')
+ call add(s:errors_expected, 'Found errors in ' . g:testfunc . ':')
call extend(s:errors_expected, v:errors)
else
let s:fail += 1
- call add(s:errors, 'Found errors in ' . s:test . ':')
+ call add(s:errors, 'Found errors in ' . g:testfunc . ':')
call extend(s:errors, v:errors)
endif
let v:errors = []
@@ -408,22 +419,22 @@ endif
let s:may_fail_list = []
if $TEST_MAY_FAIL != ''
- " Split the list at commas and add () to make it match s:test.
+ " Split the list at commas and add () to make it match g:testfunc.
let s:may_fail_list = split($TEST_MAY_FAIL, ',')->map({i, v -> v .. '()'})
endif
" Execute the tests in alphabetical order.
-for s:test in sort(s:tests)
+for g:testfunc in sort(s:tests)
" Silence, please!
set belloff=all
let prev_error = ''
let total_errors = []
- let run_nr = 1
+ let g:run_nr = 1
" A test can set g:test_is_flaky to retry running the test.
let g:test_is_flaky = 0
- call RunTheTest(s:test)
+ call RunTheTest(g:testfunc)
" Repeat a flaky test. Give up when:
" - $TEST_NO_RETRY is not empty
@@ -431,16 +442,16 @@ for s:test in sort(s:tests)
" - it fails five times (with a different message)
if len(v:errors) > 0
\ && $TEST_NO_RETRY == ''
- \ && (index(s:flaky_tests, s:test) >= 0
+ \ && (index(s:flaky_tests, g:testfunc) >= 0
\ || g:test_is_flaky)
while 1
- call add(s:messages, 'Found errors in ' . s:test . ':')
+ call add(s:messages, 'Found errors in ' . g:testfunc . ':')
call extend(s:messages, v:errors)
- call add(total_errors, 'Run ' . run_nr . ':')
+ call add(total_errors, 'Run ' . g:run_nr . ':')
call extend(total_errors, v:errors)
- if run_nr == 5 || prev_error == v:errors[0]
+ if g:run_nr >= 5 || prev_error == v:errors[0]
call add(total_errors, 'Flaky test failed too often, giving up')
let v:errors = total_errors
break
@@ -455,9 +466,9 @@ for s:test in sort(s:tests)
let prev_error = v:errors[0]
let v:errors = []
- let run_nr += 1
+ let g:run_nr += 1
- call RunTheTest(s:test)
+ call RunTheTest(g:testfunc)
if len(v:errors) == 0
" Test passed on rerun.
@@ -466,7 +477,7 @@ for s:test in sort(s:tests)
endwhile
endif
- call AfterTheTest(s:test)
+ call AfterTheTest(g:testfunc)
endfor
call FinishTesting()
diff --git a/test/benchmark/samples/re.freeze.txt b/src/nvim/testdir/samples/re.freeze.txt
index d768c23c5e..d768c23c5e 100644
--- a/test/benchmark/samples/re.freeze.txt
+++ b/src/nvim/testdir/samples/re.freeze.txt
diff --git a/src/nvim/testdir/sautest/autoload/foo.vim b/src/nvim/testdir/sautest/autoload/foo.vim
index 298e7275d8..21d33a0f4d 100644
--- a/src/nvim/testdir/sautest/autoload/foo.vim
+++ b/src/nvim/testdir/sautest/autoload/foo.vim
@@ -9,3 +9,7 @@ endfunc
func foo#addFoo(head)
return a:head .. 'foo'
endfunc
+
+func foo#()
+ return 'empty'
+endfunc
diff --git a/src/nvim/testdir/script_util.vim b/src/nvim/testdir/script_util.vim
index 9913b1dfaf..28d6a621d6 100644
--- a/src/nvim/testdir/script_util.vim
+++ b/src/nvim/testdir/script_util.vim
@@ -48,7 +48,7 @@ endfunc
" delete it afterwards. However, if an exception is thrown the file may remain,
" the caller should call DeleteTheScript() afterwards.
let s:script_name = ''
-function! ExecAsScript(funcname)
+func ExecAsScript(funcname)
" Make a script from the function passed as argument.
let s:script_name = MakeScript(a:funcname)
@@ -56,9 +56,9 @@ function! ExecAsScript(funcname)
exec "source" s:script_name
call delete(s:script_name)
let s:script_name = ''
-endfunction
+endfunc
-function! DeleteTheScript()
+func DeleteTheScript()
if s:script_name
call delete(s:script_name)
let s:script_name = ''
diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim
index 6bc3607b69..f895287469 100644
--- a/src/nvim/testdir/setup.vim
+++ b/src/nvim/testdir/setup.vim
@@ -5,7 +5,7 @@ if exists('s:did_load')
set directory&
set directory^=.
set display=
- set fillchars=vert:\|,fold:-
+ set fillchars=vert:\|,foldsep:\|,fold:-
set formatoptions=tcq
set fsync
set laststatus=1
@@ -45,6 +45,11 @@ mapclear!
aunmenu *
tlunmenu *
+" roughly equivalent to test_setmouse() in Vim
+func Ntest_setmouse(row, col)
+ call nvim_input_mouse('move', '', '', 0, a:row - 1, a:col - 1)
+endfunc
+
" 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 c2809844ac..33f6d9a2d0 100644
--- a/src/nvim/testdir/shared.vim
+++ b/src/nvim/testdir/shared.vim
@@ -9,7 +9,7 @@ source view_util.vim
" {Nvim}
" Filepath captured from output may be truncated, like this:
-" /home/va...estdir/Xtest-tmpdir/nvimxbXN4i/10
+" /home/va...estdir/X-test-tmpdir/nvimxbXN4i/10
" Get last 2 segments, then combine with $TMPDIR.
func! Fix_truncated_tmpfile(fname)
" sanity check
@@ -285,6 +285,12 @@ func GetVimCommand(...)
return cmd
endfunc
+" Return one when it looks like the tests are run with valgrind, which means
+" that everything is much slower.
+func RunningWithValgrind()
+ return GetVimCommand() =~ '\<valgrind\>'
+endfunc
+
" Get the command to run Vim, with --clean instead of "-u NONE".
func GetVimCommandClean()
let cmd = GetVimCommand()
@@ -317,7 +323,6 @@ func RunVim(before, after, arguments)
endfunc
func RunVimPiped(before, after, arguments, pipecmd)
- let $NVIM_LOG_FILE = exists($NVIM_LOG_FILE) ? $NVIM_LOG_FILE : 'Xnvim.log'
let cmd = GetVimCommand()
let args = ''
if len(a:before) > 0
@@ -332,7 +337,9 @@ func RunVimPiped(before, after, arguments, pipecmd)
" Optionally run Vim under valgrind
" let cmd = 'valgrind --tool=memcheck --leak-check=yes --num-callers=25 --log-file=valgrind ' . cmd
- exe "silent !" . a:pipecmd . cmd . args . ' ' . a:arguments
+ let $NVIM_LOG_FILE = exists($NVIM_LOG_FILE) ? $NVIM_LOG_FILE : 'Xnvim.log'
+ " Nvim does not support -Z flag, remove it.
+ exe "silent !" . a:pipecmd . cmd . args . ' ' . substitute(a:arguments, '-Z', '', 'g')
if len(a:before) > 0
call delete('Xbefore.vim')
@@ -364,4 +371,19 @@ func GetMessages()
return msg_list
endfunc
+" Run the list of commands in 'cmds' and look for 'errstr' in exception.
+" Note that assert_fails() cannot be used in some places and this function
+" can be used.
+func AssertException(cmds, errstr)
+ let save_exception = ''
+ try
+ for cmd in a:cmds
+ exe cmd
+ endfor
+ catch
+ let save_exception = v:exception
+ endtry
+ call assert_match(a:errstr, save_exception)
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test42.in b/src/nvim/testdir/test42.in
deleted file mode 100644
index 456f9ddb07..0000000000
--- a/src/nvim/testdir/test42.in
+++ /dev/null
Binary files differ
diff --git a/src/nvim/testdir/test42.ok b/src/nvim/testdir/test42.ok
deleted file mode 100644
index 183430d71c..0000000000
--- a/src/nvim/testdir/test42.ok
+++ /dev/null
Binary files differ
diff --git a/src/nvim/testdir/test49.in b/src/nvim/testdir/test49.in
deleted file mode 100644
index 435e62765b..0000000000
--- a/src/nvim/testdir/test49.in
+++ /dev/null
@@ -1,31 +0,0 @@
-This is a test of the script language.
-
-If after adding a new test, the test output doesn't appear properly in
-test49.failed, try to add one or more "G"s at the line ending in "test.out"
-
-STARTTEST
-:se nomore
-:lang mess C
-:so test49.vim
-:" Go back to this file and append the results from register r.
-:buf test49.in
-G"rp:/^Results/,$w! test.out
-:"
-:" make valgrind happy
-:redir => funclist
-:silent func
-:redir END
-:for line in split(funclist, "\n")
-: let name = matchstr(line, 'function \zs[A-Z]\w*\ze(')
-: if name != ''
-: exe "delfunc " . name
-: endif
-:endfor
-:for v in keys(g:)
-: silent! exe "unlet " . v
-:endfor
-:unlet v
-:qa!
-ENDTEST
-
-Results of test49.vim:
diff --git a/src/nvim/testdir/test49.ok b/src/nvim/testdir/test49.ok
deleted file mode 100644
index 9f283e808b..0000000000
--- a/src/nvim/testdir/test49.ok
+++ /dev/null
@@ -1,56 +0,0 @@
-Results of test49.vim:
-*** Test 18: OK (67224583)
-*** Test 19: OK (69275973)
-*** Test 20: OK (1874575085)
-*** Test 21: OK (147932225)
-*** Test 22: OK (4161)
-*** Test 23: OK (49)
-*** Test 24: OK (41)
-*** Test 27: OK (1996459)
-*** Test 28: OK (1996459)
-*** Test 29: OK (170428555)
-*** Test 30: OK (190905173)
-*** Test 31: OK (190905173)
-*** Test 34: OK (2146584868)
-*** Test 35: OK (2146584868)
-*** Test 36: OK (1071644672)
-*** Test 37: OK (1071644672)
-*** Test 38: OK (357908480)
-*** Test 39: OK (357908480)
-*** Test 40: OK (357908480)
-*** Test 49: OK (179000669)
-*** Test 50: OK (363550045)
-*** Test 52: OK (1247112011)
-*** Test 53: OK (131071)
-*** Test 54: OK (2047)
-*** Test 55: OK (1023)
-*** Test 56: OK (511)
-*** Test 57: OK (2147450880)
-*** Test 58: OK (624945)
-*** Test 59: OK (2038431743)
-*** Test 60: OK (311511339)
-*** Test 61: OK (374889517)
-*** Test 62: OK (286331153)
-*** Test 63: OK (236978127)
-*** Test 64: OK (1499645335)
-*** Test 65: OK (70187)
-*** Test 66: OK (5464)
-*** Test 67: OK (212514423)
-*** Test 68: OK (212514423)
-*** Test 76: OK (1610087935)
-*** Test 77: OK (1388671)
-*** Test 78: OK (134217728)
-*** Test 79: OK (70288929)
-*** Test 80: OK (17895765)
-*** Test 81: OK (387)
-*** Test 82: OK (8454401)
-*** Test 83: OK (2835)
-*** Test 84: OK (934782101)
-*** Test 85: OK (198689)
---- Test 86: No Crash for vimgrep on BufUnload
-*** Test 86: OK (0)
---- Test 88: All tests were run with throwing exceptions on error.
- The $VIMNOERRTHROW control is not configured.
---- Test 88: All tests were run with throwing exceptions on interrupt.
- The $VIMNOINTTHROW control is not configured.
-*** Test 88: OK (50443995)
diff --git a/src/nvim/testdir/test49.vim b/src/nvim/testdir/test49.vim
deleted file mode 100644
index 3ee5e9ff7c..0000000000
--- a/src/nvim/testdir/test49.vim
+++ /dev/null
@@ -1,6724 +0,0 @@
-" Vim script language tests
-" Author: Servatius Brandt <Servatius.Brandt@fujitsu-siemens.com>
-" Last Change: 2019 Nov 03
-
-"-------------------------------------------------------------------------------
-" Test environment {{{1
-"-------------------------------------------------------------------------------
-
-
-" Adding new tests easily. {{{2
-"
-" Writing new tests is eased considerably with the following functions and
-" abbreviations (see "Commands for recording the execution path", "Automatic
-" argument generation").
-"
-" To get the abbreviations, execute the command
-"
-" :let test49_set_env = 1 | source test49.vim
-"
-" To get them always (from src/nvim/testdir), put a line
-"
-" au! BufRead test49.vim let test49_set_env = 1 | source test49.vim
-"
-" into the local .vimrc file in the src/nvim/testdir directory.
-"
-if exists("test49_set_env") && test49_set_env
-
- " Automatic argument generation for the test environment commands.
-
- function! Xsum()
- let addend = substitute(getline("."), '^.*"\s*X:\s*\|^.*', '', "")
- " Evaluate arithmetic expression.
- if addend != ""
- exec "let g:Xsum = g:Xsum + " . addend
- endif
- endfunction
-
- function! Xcheck()
- let g:Xsum=0
- ?XpathINIT?,.call Xsum()
- exec "norm A "
- return g:Xsum
- endfunction
-
- iab Xcheck Xcheck<Space><C-R>=Xcheck()<CR><C-O>x
-
- function! Xcomment(num)
- let str = ""
- let tabwidth = &sts ? &sts : &ts
- let tabs = (48+tabwidth - a:num - virtcol(".")) / tabwidth
- while tabs > 0
- let str = str . "\t"
- let tabs = tabs - 1
- endwhile
- let str = str . '" X:'
- return str
- endfunction
-
- function! Xloop()
- let back = line(".") . "|norm" . virtcol(".") . "|"
- norm 0
- let last = search('X\(loop\|path\)INIT\|Xloop\>', "bW")
- exec back
- let theline = getline(last)
- if theline =~ 'X\(loop\|path\)INIT'
- let num = 1
- else
- let num = 2 * substitute(theline, '.*Xloop\s*\(\d\+\).*', '\1', "")
- endif
- ?X\(loop\|path\)INIT?
- \s/\(XloopINIT!\=\s*\d\+\s\+\)\@<=\(\d\+\)/\=2*submatch(2)/
- exec back
- exec "norm a "
- return num . Xcomment(strlen(num))
- endfunction
-
- iab Xloop Xloop<Space><C-R>=Xloop()<CR><C-O>x
-
- function! Xpath(loopinit)
- let back = line(".") . "|norm" . virtcol(".") . "|"
- norm 0
- let last = search('XpathINIT\|Xpath\>\|XloopINIT', "bW")
- exec back
- let theline = getline(last)
- if theline =~ 'XpathINIT'
- let num = 1
- elseif theline =~ 'Xpath\>'
- let num = 2 * substitute(theline, '.*Xpath\s*\(\d\+\).*', '\1', "")
- else
- let pattern = '.*XloopINIT!\=\s*\(\d\+\)\s*\(\d\+\).*'
- let num = substitute(theline, pattern, '\1', "")
- let factor = substitute(theline, pattern, '\2', "")
- " The "<C-O>x" from the "Xpath" iab and the character triggering its
- " expansion are in the input buffer. Save and clear typeahead so
- " that it is not read away by the call to "input()" below. Restore
- " afterwards.
- call inputsave()
- let loops = input("Number of iterations in previous loop? ")
- call inputrestore()
- while (loops > 0)
- let num = num * factor
- let loops = loops - 1
- endwhile
- endif
- exec "norm a "
- if a:loopinit
- return num . " 1"
- endif
- return num . Xcomment(strlen(num))
- endfunction
-
- iab Xpath Xpath<Space><C-R>=Xpath(0)<CR><C-O>x
- iab XloopINIT XloopINIT<Space><C-R>=Xpath(1)<CR><C-O>x
-
- " Also useful (see ExtraVim below):
- aug ExtraVim
- au!
- au BufEnter <sfile> syn region ExtraVim
- \ start=+^if\s\+ExtraVim(.*)+ end=+^endif+
- \ transparent keepend
- au BufEnter <sfile> syn match ExtraComment /^"/
- \ contained containedin=ExtraVim
- au BufEnter <sfile> hi link ExtraComment vimComment
- aug END
-
- aug Xpath
- au BufEnter <sfile> syn keyword Xpath
- \ XpathINIT Xpath XloopINIT Xloop XloopNEXT Xcheck Xout
- au BufEnter <sfile> hi link Xpath Special
- aug END
-
- do BufEnter <sfile>
-
- " Do not execute the tests when sourcing this file for getting the functions
- " and abbreviations above, which are intended for easily adding new test
- " cases; they are not needed for test execution. Unlet the variable
- " controlling this so that an explicit ":source" command for this file will
- " execute the tests.
- unlet test49_set_env
- finish
-
-endif
-
-
-" Commands for recording the execution path. {{{2
-"
-" The Xpath/Xloop commands can be used for computing the eXecution path by
-" adding (different) powers of 2 from those script lines, for which the
-" execution should be checked. Xloop provides different addends for each
-" execution of a loop. Permitted values are 2^0 to 2^30, so that 31 execution
-" points (multiply counted inside loops) can be tested.
-"
-" Note that the arguments of the following commands can be generated
-" automatically, see below.
-"
-" Usage: {{{3
-"
-" - Use XpathINIT at the beginning of the test.
-"
-" - Use Xpath to check if a line is executed.
-" Argument: power of 2 (decimal).
-"
-" - To check multiple execution of loops use Xloop for automatically
-" computing Xpath values:
-"
-" - Use XloopINIT before the loop.
-" Two arguments:
-" - the first Xpath value (power of 2) to be used (Xnext),
-" - factor for computing a new Xnext value when reexecuting a loop
-" (by a ":continue" or ":endwhile"); this should be 2^n where
-" n is the number of Xloop commands inside the loop.
-" If XloopINIT! is used, the first execution of XloopNEXT is
-" a no-operation.
-"
-" - Use Xloop inside the loop:
-" One argument:
-" The argument and the Xnext value are multiplied to build the
-" next Xpath value. No new Xnext value is prepared. The argument
-" should be 2^(n-1) for the nth Xloop command inside the loop.
-" If the loop has only one Xloop command, the argument can be
-" omitted (default: 1).
-"
-" - Use XloopNEXT before ":continue" and ":endwhile". This computes a new
-" Xnext value for the next execution of the loop by multiplying the old
-" one with the factor specified in the XloopINIT command. No Argument.
-" Alternatively, when XloopINIT! is used, a single XloopNEXT at the
-" beginning of the loop can be used.
-"
-" Nested loops are not supported.
-"
-" - Use Xcheck at end of each test. It prints the test number, the expected
-" execution path value, the test result ("OK" or "FAIL"), and, if the tests
-" fails, the actual execution path.
-" One argument:
-" Expected Xpath/Xloop sum for the correct execution path.
-" In order that this value can be computed automatically, do the
-" following: For each line in the test with an Xpath and Xloop
-" command, add a comment starting with "X:" and specifying an
-" expression that evaluates to the value contributed by this line to
-" the correct execution path. (For copying an Xpath argument of at
-" least two digits into the comment, press <C-P>.) At the end of the
-" test, just type "Xcheck" and press <Esc>.
-"
-" - In order to add additional information to the test output file, use the
-" Xout command. Argument(s) like ":echo".
-"
-" Automatic argument generation: {{{3
-"
-" The arguments of the Xpath, XloopINIT, Xloop, and Xcheck commands can be
-" generated automatically, so that new tests can easily be written without
-" mental arithmetic. The Xcheck argument is computed from the "X:" comments
-" of the preceding Xpath and Xloop commands. See the commands and
-" abbreviations at the beginning of this file.
-"
-" Implementation: {{{3
-" XpathINIT, Xpath, XloopINIT, Xloop, XloopNEXT, Xcheck, Xout.
-"
-" The variants for existing g:ExtraVimResult are needed when executing a script
-" in an extra Vim process, see ExtraVim below.
-
-" EXTRA_VIM_START - do not change or remove this line.
-
-com! XpathINIT let g:Xpath = 0
-
-if exists("g:ExtraVimResult")
- com! -count -bar Xpath exec "!echo <count> >>" . g:ExtraVimResult
-else
- com! -count -bar Xpath let g:Xpath = g:Xpath + <count>
-endif
-
-com! -count -nargs=1 -bang
- \ XloopINIT let g:Xnext = <count> |
- \ let g:Xfactor = <args> |
- \ let g:Xskip = strlen("<bang>")
-
-if exists("g:ExtraVimResult")
- com! -count=1 -bar Xloop exec "!echo " . (g:Xnext * <count>) . " >>" .
- \ g:ExtraVimResult
-else
- com! -count=1 -bar Xloop let g:Xpath = g:Xpath + g:Xnext * <count>
-endif
-
-com! XloopNEXT let g:Xnext = g:Xnext *
- \ (g:Xskip ? 1 : g:Xfactor) |
- \ let g:Xskip = 0
-
-let @r = ""
-let Xtest = 1
-com! -count Xcheck let Xresult = "*** Test " .
- \ (Xtest<10?" ":Xtest<100?" ":"") .
- \ Xtest . ": " . (
- \ (Xpath==<count>) ? "OK (".Xpath.")" :
- \ "FAIL (".Xpath." instead of <count>)"
- \ ) |
- \ let @R = Xresult . "\n" |
- \ echo Xresult |
- \ let Xtest = Xtest + 1
-
-if exists("g:ExtraVimResult")
- com! -nargs=+ Xoutq exec "!echo @R:'" .
- \ substitute(substitute(<q-args>,
- \ "'", '&\\&&', "g"), "\n", "@NL@", "g")
- \ . "' >>" . g:ExtraVimResult
-else
- com! -nargs=+ Xoutq let @R = "--- Test " .
- \ (g:Xtest<10?" ":g:Xtest<100?" ":"") .
- \ g:Xtest . ": " . substitute(<q-args>,
- \ "\n", "&\t ", "g") . "\n"
-endif
-com! -nargs=+ Xout exec 'Xoutq' <args>
-
-" Switch off storing of lines for undoing changes. Speeds things up a little.
-set undolevels=-1
-
-" EXTRA_VIM_STOP - do not change or remove this line.
-
-
-" ExtraVim() - Run a script file in an extra Vim process. {{{2
-"
-" This is useful for testing immediate abortion of the script processing due to
-" an error in a command dynamically enclosed by a :try/:tryend region or when an
-" exception is thrown but not caught or when an interrupt occurs. It can also
-" be used for testing :finish.
-"
-" An interrupt location can be specified by an "INTERRUPT" comment. A number
-" telling how often this location is reached (in a loop or in several function
-" calls) should be specified as argument. When missing, once per script
-" invocation or function call is assumed. INTERRUPT locations are tested by
-" setting a breakpoint in that line and using the ">quit" debug command when
-" the breakpoint is reached. A function for which an INTERRUPT location is
-" specified must be defined before calling it (or executing it as a script by
-" using ExecAsScript below).
-"
-" This function is only called in normal modus ("g:ExtraVimResult" undefined).
-"
-" Tests to be executed as an extra script should be written as follows:
-"
-" column 1 column 1
-" | |
-" v v
-"
-" XpathINIT XpathINIT
-" if ExtraVim() if ExtraVim()
-" ... " ...
-" ... " ...
-" endif endif
-" Xcheck <number> Xcheck <number>
-"
-" Double quotes in column 1 are removed before the script is executed.
-" They should be used if the test has unbalanced conditionals (:if/:endif,
-" :while:/endwhile, :try/:endtry) or for a line with a syntax error. The
-" extra script may use Xpath, XloopINIT, Xloop, XloopNEXT, and Xout as usual.
-"
-" A file name may be specified as argument. All messages of the extra Vim
-" process are then redirected to the file. An existing file is overwritten.
-"
-let ExtraVimCount = 0
-let ExtraVimBase = expand("<sfile>")
-let ExtraVimTestEnv = ""
-"
-function ExtraVim(...)
- " Count how often this function is called.
- let g:ExtraVimCount = g:ExtraVimCount + 1
-
- " Disable folds to prevent that the ranges in the ":write" commands below
- " are extended up to the end of a closed fold. This also speeds things up
- " considerably.
- set nofoldenable
-
- " Open a buffer for this test script and copy the test environment to
- " a temporary file. Take account of parts relevant for the extra script
- " execution only.
- let current_buffnr = bufnr("%")
- execute "view +1" g:ExtraVimBase
- if g:ExtraVimCount == 1
- let g:ExtraVimTestEnv = tempname()
- execute "/E" . "XTRA_VIM_START/+,/E" . "XTRA_VIM_STOP/-w"
- \ g:ExtraVimTestEnv "|']+"
- execute "/E" . "XTRA_VIM_START/+,/E" . "XTRA_VIM_STOP/-w >>"
- \ g:ExtraVimTestEnv "|']+"
- execute "/E" . "XTRA_VIM_START/+,/E" . "XTRA_VIM_STOP/-w >>"
- \ g:ExtraVimTestEnv "|']+"
- execute "/E" . "XTRA_VIM_START/+,/E" . "XTRA_VIM_STOP/-w >>"
- \ g:ExtraVimTestEnv "|']+"
- endif
-
- " Start the extra Vim script with a ":source" command for the test
- " environment. The source line number where the extra script will be
- " appended, needs to be passed as variable "ExtraVimBegin" to the script.
- let extra_script = tempname()
- exec "!echo 'source " . g:ExtraVimTestEnv . "' >" . extra_script
- let extra_begin = 1
-
- " Starting behind the test environment, skip over the first g:ExtraVimCount
- " occurrences of "if ExtraVim()" and copy the following lines up to the
- " matching "endif" to the extra Vim script.
- execute "/E" . "ND_OF_TEST_ENVIRONMENT/"
- exec 'norm ' . g:ExtraVimCount . '/^\s*if\s\+ExtraVim(.*)/+' . "\n"
- execute ".,/^endif/-write >>" . extra_script
-
- " Open a buffer for the extra Vim script, delete all ^", and write the
- " script if was actually modified.
- execute "edit +" . (extra_begin + 1) extra_script
- ,$s/^"//e
- update
-
- " Count the INTERRUPTs and build the breakpoint and quit commands.
- let breakpoints = ""
- let debug_quits = ""
- let in_func = 0
- exec extra_begin
- while search(
- \ '"\s*INTERRUPT\h\@!\|^\s*fu\%[nction]\>!\=\s*\%(\u\|s:\)\w*\s*(\|'
- \ . '^\s*\\\|^\s*endf\%[unction]\>\|'
- \ . '\%(^\s*fu\%[nction]!\=\s*\)\@<!\%(\u\|s:\)\w*\s*(\|'
- \ . 'ExecAsScript\s\+\%(\u\|s:\)\w*',
- \ "W") > 0
- let theline = getline(".")
- if theline =~ '^\s*fu'
- " Function definition.
- let in_func = 1
- let func_start = line(".")
- let func_name = substitute(theline,
- \ '^\s*fu\%[nction]!\=\s*\(\%(\u\|s:\)\w*\).*', '\1', "")
- elseif theline =~ '^\s*endf'
- " End of function definition.
- let in_func = 0
- else
- let finding = substitute(theline, '.*\(\%' . col(".") . 'c.*\)',
- \ '\1', "")
- if finding =~ '^"\s*INTERRUPT\h\@!'
- " Interrupt comment. Compose as many quit commands as
- " specified.
- let cnt = substitute(finding,
- \ '^"\s*INTERRUPT\s*\(\d*\).*$', '\1', "")
- let quits = ""
- while cnt > 0
- " Use "\r" rather than "\n" to separate the quit commands.
- " "\r" is not interpreted as command separator by the ":!"
- " command below but works to separate commands in the
- " external vim.
- let quits = quits . "q\r"
- let cnt = cnt - 1
- endwhile
- if in_func
- " Add the function breakpoint and note the number of quits
- " to be used, if specified, or one for every call else.
- let breakpoints = breakpoints . " -c 'breakadd func " .
- \ (line(".") - func_start) . " " .
- \ func_name . "'"
- if quits != ""
- let debug_quits = debug_quits . quits
- elseif !exists("quits{func_name}")
- let quits{func_name} = "q\r"
- else
- let quits{func_name} = quits{func_name} . "q\r"
- endif
- else
- " Add the file breakpoint and the quits to be used for it.
- let breakpoints = breakpoints . " -c 'breakadd file " .
- \ line(".") . " " . extra_script . "'"
- if quits == ""
- let quits = "q\r"
- endif
- let debug_quits = debug_quits . quits
- endif
- else
- " Add the quits to be used for calling the function or executing
- " it as script file.
- if finding =~ '^ExecAsScript'
- " Sourcing function as script.
- let finding = substitute(finding,
- \ '^ExecAsScript\s\+\(\%(\u\|s:\)\w*\).*', '\1', "")
- else
- " Function call.
- let finding = substitute(finding,
- \ '^\(\%(\u\|s:\)\w*\).*', '\1', "")
- endif
- if exists("quits{finding}")
- let debug_quits = debug_quits . quits{finding}
- endif
- endif
- endif
- endwhile
-
- " Close the buffer for the script and create an (empty) resultfile.
- bwipeout
- let resultfile = tempname()
- exec "!>" . resultfile
-
- " Run the script in an extra vim. Switch to extra modus by passing the
- " resultfile in ExtraVimResult. Redirect messages to the file specified as
- " argument if any. Use ":debuggreedy" so that the commands provided on the
- " pipe are consumed at the debug prompt. Use "-N" to enable command-line
- " continuation ("C" in 'cpo'). Add "nviminfo" to 'viminfo' to avoid
- " messing up the user's viminfo file.
- let redirect = a:0 ?
- \ " -c 'au VimLeave * redir END' -c 'redir\\! >" . a:1 . "'" : ""
- exec "!echo '" . debug_quits . "q' | " .. v:progpath .. " -u NONE -N -es" . redirect .
- \ " -c 'debuggreedy|set viminfo+=nviminfo'" .
- \ " -c 'let ExtraVimBegin = " . extra_begin . "'" .
- \ " -c 'let ExtraVimResult = \"" . resultfile . "\"'" . breakpoints .
- \ " -S " . extra_script
-
- " Build the resulting sum for resultfile and add it to g:Xpath. Add Xout
- " information provided by the extra Vim process to the test output.
- let sum = 0
- exec "edit" resultfile
- let line = 1
- while line <= line("$")
- let theline = getline(line)
- if theline =~ '^@R:'
- exec 'Xout "' . substitute(substitute(
- \ escape(escape(theline, '"'), '\"'),
- \ '^@R:', '', ""), '@NL@', "\n", "g") . '"'
- else
- let sum = sum + getline(line)
- endif
- let line = line + 1
- endwhile
- bwipeout
- let g:Xpath = g:Xpath + sum
-
- " Delete the extra script and the resultfile.
- call delete(extra_script)
- call delete(resultfile)
-
- " Switch back to the buffer that was active when this function was entered.
- exec "buffer" current_buffnr
-
- " Return 0. This protects extra scripts from being run in the main Vim
- " process.
- return 0
-endfunction
-
-
-" ExtraVimThrowpoint() - Relative throwpoint in ExtraVim script {{{2
-"
-" Evaluates v:throwpoint and returns the throwpoint relative to the beginning of
-" an ExtraVim script as passed by ExtraVim() in ExtraVimBegin.
-"
-" EXTRA_VIM_START - do not change or remove this line.
-function ExtraVimThrowpoint()
- if !exists("g:ExtraVimBegin")
- Xout "ExtraVimThrowpoint() used outside ExtraVim() script."
- return v:throwpoint
- endif
-
- if v:throwpoint =~ '^function\>'
- return v:throwpoint
- endif
-
- return "line " .
- \ (substitute(v:throwpoint, '.*, line ', '', "") - g:ExtraVimBegin) .
- \ " of ExtraVim() script"
-endfunction
-" EXTRA_VIM_STOP - do not change or remove this line.
-
-
-" MakeScript() - Make a script file from a function. {{{2
-"
-" Create a script that consists of the body of the function a:funcname.
-" Replace any ":return" by a ":finish", any argument variable by a global
-" variable, and every ":call" by a ":source" for the next following argument
-" 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.
-"
-" In order to execute a function specifying an INTERRUPT location (see ExtraVim)
-" as a script file, use ExecAsScript below.
-"
-" EXTRA_VIM_START - do not change or remove this line.
-function MakeScript(funcname, ...)
- let script = tempname()
- execute "redir! >" . script
- execute "function" a:funcname
- redir END
- execute "edit" script
- " Delete the "function" and the "endfunction" lines. Do not include the
- " word "function" in the pattern since it might be translated if LANG is
- " set. When MakeScript() is being debugged, this deletes also the debugging
- " output of its line 3 and 4.
- exec '1,/.*' . a:funcname . '(.*)/d'
- /^\d*\s*endfunction\>/,$d
- %s/^\d*//e
- %s/return/finish/e
- %s/\<a:\(\h\w*\)/g:\1/ge
- normal gg0
- let cnt = 0
- while search('\<call\s*\%(\u\|s:\)\w*\s*(.*)', 'W') > 0
- let cnt = cnt + 1
- s/\<call\s*\%(\u\|s:\)\w*\s*(.*)/\='source ' . a:{cnt}/
- endwhile
- g/^\s*$/d
- write
- bwipeout
- return script
-endfunction
-" EXTRA_VIM_STOP - do not change or remove this line.
-
-
-" ExecAsScript - Source a temporary script made from a function. {{{2
-"
-" Make a temporary script file from the function a:funcname, ":source" it, and
-" delete it afterwards.
-"
-" When inside ":if ExtraVim()", add a file breakpoint for each INTERRUPT
-" location specified in the function.
-"
-" EXTRA_VIM_START - do not change or remove this line.
-function ExecAsScript(funcname)
- " Make a script from the function passed as argument.
- let script = MakeScript(a:funcname)
-
- " When running in an extra Vim process, add a file breakpoint for each
- " function breakpoint set when the extra Vim process was invoked by
- " ExtraVim().
- if exists("g:ExtraVimResult")
- let bplist = tempname()
- execute "redir! >" . bplist
- breaklist
- redir END
- execute "edit" bplist
- " Get the line number from the function breakpoint. Works also when
- " LANG is set.
- execute 'v/^\s*\d\+\s\+func\s\+' . a:funcname . '\s.*/d'
- %s/^\s*\d\+\s\+func\s\+\%(\u\|s:\)\w*\s\D*\(\d*\).*/\1/e
- let cnt = 0
- while cnt < line("$")
- let cnt = cnt + 1
- if getline(cnt) != ""
- execute "breakadd file" getline(cnt) script
- endif
- endwhile
- bwipeout!
- call delete(bplist)
- endif
-
- " Source and delete the script.
- exec "source" script
- call delete(script)
-endfunction
-
-com! -nargs=1 -bar ExecAsScript call ExecAsScript(<f-args>)
-" EXTRA_VIM_STOP - do not change or remove this line.
-
-
-" END_OF_TEST_ENVIRONMENT - do not change or remove this line.
-
-
-" Tests 1 to 17 were moved to test_vimscript.vim
-let Xtest = 18
-
-"-------------------------------------------------------------------------------
-" Test 18: Interrupt (Ctrl-C pressed) {{{1
-"
-" On an interrupt, the script processing is terminated immediately.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
- if 1
- Xpath 1 " X: 1
- while 1
- Xpath 2 " X: 2
- if 1
- Xpath 4 " X: 4
- "INTERRUPT
- Xpath 8 " X: 0
- break
- finish
- endif | Xpath 16 " X: 0
- Xpath 32 " X: 0
- endwhile | Xpath 64 " X: 0
- Xpath 128 " X: 0
- endif | Xpath 256 " X: 0
- Xpath 512 " X: 0
-endif
-
-if ExtraVim()
- try
- Xpath 1024 " X: 1024
- "INTERRUPT
- Xpath 2048 " X: 0
- endtry | Xpath 4096 " X: 0
- Xpath 8192 " X: 0
-endif
-
-if ExtraVim()
- function! F()
- if 1
- Xpath 16384 " X: 16384
- while 1
- Xpath 32768 " X: 32768
- if 1
- Xpath 65536 " X: 65536
- "INTERRUPT
- Xpath 131072 " X: 0
- break
- return
- endif | Xpath 262144 " X: 0
- Xpath Xpath 524288 " X: 0
- endwhile | Xpath 1048576 " X: 0
- Xpath Xpath 2097152 " X: 0
- endif | Xpath Xpath 4194304 " X: 0
- Xpath Xpath 8388608 " X: 0
- endfunction
-
- call F() | Xpath 16777216 " X: 0
- Xpath 33554432 " X: 0
-endif
-
-if ExtraVim()
- function! G()
- try
- Xpath 67108864 " X: 67108864
- "INTERRUPT
- Xpath 134217728 " X: 0
- endtry | Xpath 268435456 " X: 0
- Xpath 536870912 " X: 0
- endfunction
-
- call G() | Xpath 1073741824 " X: 0
- " The Xpath command does not accept 2^31 (negative); display explicitly:
- exec "!echo 2147483648 >>" . g:ExtraVimResult
- " X: 0
-endif
-
-Xcheck 67224583
-
-
-"-------------------------------------------------------------------------------
-" Test 19: Aborting on errors inside :try/:endtry {{{1
-"
-" An error in a command dynamically enclosed in a :try/:endtry region
-" aborts script processing immediately. It does not matter whether
-" the failing command is outside or inside a function and whether a
-" function has an "abort" attribute.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
- function! F() abort
- Xpath 1 " X: 1
- asdf
- Xpath 2 " X: 0
- endfunction
-
- try
- Xpath 4 " X: 4
- call F()
- Xpath 8 " X: 0
- endtry | Xpath 16 " X: 0
- Xpath 32 " X: 0
-endif
-
-if ExtraVim()
- function! G()
- Xpath 64 " X: 64
- asdf
- Xpath 128 " X: 0
- endfunction
-
- try
- Xpath 256 " X: 256
- call G()
- Xpath 512 " X: 0
- endtry | Xpath 1024 " X: 0
- Xpath 2048 " X: 0
-endif
-
-if ExtraVim()
- try
- Xpath 4096 " X: 4096
- asdf
- Xpath 8192 " X: 0
- endtry | Xpath 16384 " X: 0
- Xpath 32768 " X: 0
-endif
-
-if ExtraVim()
- if 1
- try
- Xpath 65536 " X: 65536
- asdf
- Xpath 131072 " X: 0
- endtry | Xpath 262144 " X: 0
- endif | Xpath 524288 " X: 0
- Xpath 1048576 " X: 0
-endif
-
-if ExtraVim()
- let p = 1
- while p
- let p = 0
- try
- Xpath 2097152 " X: 2097152
- asdf
- Xpath 4194304 " X: 0
- endtry | Xpath 8388608 " X: 0
- endwhile | Xpath 16777216 " X: 0
- Xpath 33554432 " X: 0
-endif
-
-if ExtraVim()
- let p = 1
- while p
- let p = 0
-" try
- Xpath 67108864 " X: 67108864
- endwhile | Xpath 134217728 " X: 0
- Xpath 268435456 " X: 0
-endif
-
-Xcheck 69275973
-"-------------------------------------------------------------------------------
-" Test 20: Aborting on errors after :try/:endtry {{{1
-"
-" When an error occurs after the last active :try/:endtry region has
-" been left, termination behavior is as if no :try/:endtry has been
-" seen.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
- let p = 1
- while p
- let p = 0
- try
- Xpath 1 " X: 1
- endtry
- asdf
- endwhile | Xpath 2 " X: 0
- Xpath 4 " X: 4
-endif
-
-if ExtraVim()
- while 1
- try
- Xpath 8 " X: 8
- break
- Xpath 16 " X: 0
- endtry
- endwhile
- Xpath 32 " X: 32
- asdf
- Xpath 64 " X: 64
-endif
-
-if ExtraVim()
- while 1
- try
- Xpath 128 " X: 128
- break
- Xpath 256 " X: 0
- finally
- Xpath 512 " X: 512
- endtry
- endwhile
- Xpath 1024 " X: 1024
- asdf
- Xpath 2048 " X: 2048
-endif
-
-if ExtraVim()
- while 1
- try
- Xpath 4096 " X: 4096
- finally
- Xpath 8192 " X: 8192
- break
- Xpath 16384 " X: 0
- endtry
- endwhile
- Xpath 32768 " X: 32768
- asdf
- Xpath 65536 " X: 65536
-endif
-
-if ExtraVim()
- let p = 1
- while p
- let p = 0
- try
- Xpath 131072 " X: 131072
- continue
- Xpath 262144 " X: 0
- endtry
- endwhile
- Xpath 524288 " X: 524288
- asdf
- Xpath 1048576 " X: 1048576
-endif
-
-if ExtraVim()
- let p = 1
- while p
- let p = 0
- try
- Xpath 2097152 " X: 2097152
- continue
- Xpath 4194304 " X: 0
- finally
- Xpath 8388608 " X: 8388608
- endtry
- endwhile
- Xpath 16777216 " X: 16777216
- asdf
- Xpath 33554432 " X: 33554432
-endif
-
-if ExtraVim()
- let p = 1
- while p
- let p = 0
- try
- Xpath 67108864 " X: 67108864
- finally
- Xpath 134217728 " X: 134217728
- continue
- Xpath 268435456 " X: 0
- endtry
- endwhile
- Xpath 536870912 " X: 536870912
- asdf
- Xpath 1073741824 " X: 1073741824
-endif
-
-Xcheck 1874575085
-
-
-"-------------------------------------------------------------------------------
-" Test 21: :finally for :try after :continue/:break/:return/:finish {{{1
-"
-" If a :try conditional stays inactive due to a preceding :continue,
-" :break, :return, or :finish, its :finally clause should not be
-" executed.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
- function F()
- let loops = 2
- XloopINIT! 1 256
- while loops > 0
- XloopNEXT
- let loops = loops - 1
- try
- if loops == 1
- Xloop 1 " X: 1
- continue
- Xloop 2 " X: 0
- elseif loops == 0
- Xloop 4 " X: 4*256
- break
- Xloop 8 " X: 0
- endif
-
- try " inactive
- Xloop 16 " X: 0
- finally
- Xloop 32 " X: 0
- endtry
- finally
- Xloop 64 " X: 64 + 64*256
- endtry
- Xloop 128 " X: 0
- endwhile
-
- try
- Xpath 65536 " X: 65536
- return
- Xpath 131072 " X: 0
- try " inactive
- Xpath 262144 " X: 0
- finally
- Xpath 524288 " X: 0
- endtry
- finally
- Xpath 1048576 " X: 1048576
- endtry
- Xpath 2097152 " X: 0
- endfunction
-
- try
- Xpath 4194304 " X: 4194304
- call F()
- Xpath 8388608 " X: 8388608
- finish
- Xpath 16777216 " X: 0
- try " inactive
- Xpath 33554432 " X: 0
- finally
- Xpath 67108864 " X: 0
- endtry
- finally
- Xpath 134217728 " X: 134217728
- endtry
- Xpath 268435456 " X: 0
-endif
-
-Xcheck 147932225
-
-
-"-------------------------------------------------------------------------------
-" Test 22: :finally for a :try after an error/interrupt/:throw {{{1
-"
-" If a :try conditional stays inactive due to a preceding error or
-" interrupt or :throw, its :finally clause should not be executed.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
- function! Error()
- try
- asdf " aborting error, triggering error exception
- endtry
- endfunction
-
- Xpath 1 " X: 1
- call Error()
- Xpath 2 " X: 0
-
- if 1 " not active due to error
- try " not active since :if inactive
- Xpath 4 " X: 0
- finally
- Xpath 8 " X: 0
- endtry
- endif
-
- try " not active due to error
- Xpath 16 " X: 0
- finally
- Xpath 32 " X: 0
- endtry
-endif
-
-if ExtraVim()
- function! Interrupt()
- try
- "INTERRUPT " triggering interrupt exception
- endtry
- endfunction
-
- Xpath 64 " X: 64
- call Interrupt()
- Xpath 128 " X: 0
-
- if 1 " not active due to interrupt
- try " not active since :if inactive
- Xpath 256 " X: 0
- finally
- Xpath 512 " X: 0
- endtry
- endif
-
- try " not active due to interrupt
- Xpath 1024 " X: 0
- finally
- Xpath 2048 " X: 0
- endtry
-endif
-
-if ExtraVim()
- function! Throw()
- throw "xyz"
- endfunction
-
- Xpath 4096 " X: 4096
- call Throw()
- Xpath 8192 " X: 0
-
- if 1 " not active due to :throw
- try " not active since :if inactive
- Xpath 16384 " X: 0
- finally
- Xpath 32768 " X: 0
- endtry
- endif
-
- try " not active due to :throw
- Xpath 65536 " X: 0
- finally
- Xpath 131072 " X: 0
- endtry
-endif
-
-Xcheck 4161
-
-
-"-------------------------------------------------------------------------------
-" Test 23: :catch clauses for a :try after a :throw {{{1
-"
-" If a :try conditional stays inactive due to a preceding :throw,
-" none of its :catch clauses should be executed.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
- try
- Xpath 1 " X: 1
- throw "xyz"
- Xpath 2 " X: 0
-
- if 1 " not active due to :throw
- try " not active since :if inactive
- Xpath 4 " X: 0
- catch /xyz/
- Xpath 8 " X: 0
- endtry
- endif
- catch /xyz/
- Xpath 16 " X: 16
- endtry
-
- Xpath 32 " X: 32
- throw "abc"
- Xpath 64 " X: 0
-
- try " not active due to :throw
- Xpath 128 " X: 0
- catch /abc/
- Xpath 256 " X: 0
- endtry
-endif
-
-Xcheck 49
-
-
-"-------------------------------------------------------------------------------
-" Test 24: :endtry for a :try after a :throw {{{1
-"
-" If a :try conditional stays inactive due to a preceding :throw,
-" its :endtry should not rethrow the exception to the next surrounding
-" active :try conditional.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
- try " try 1
- try " try 2
- Xpath 1 " X: 1
- throw "xyz" " makes try 2 inactive
- Xpath 2 " X: 0
-
- try " try 3
- Xpath 4 " X: 0
- endtry " no rethrow to try 1
- catch /xyz/ " should catch although try 2 inactive
- Xpath 8 " X: 8
- endtry
- catch /xyz/ " try 1 active, but exception already caught
- Xpath 16 " X: 0
- endtry
- Xpath 32 " X: 32
-endif
-
-Xcheck 41
-
-" Tests 25 and 26 were moved to test_trycatch.vim
-let Xtest = 27
-
-
-"-------------------------------------------------------------------------------
-" Test 27: Executing :finally clauses after :return {{{1
-"
-" For a :return command dynamically enclosed in a :try/:endtry region,
-" :finally clauses are executed and the called function is ended.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! F()
- try
- Xpath 1 " X: 1
- try
- Xpath 2 " X: 2
- return
- Xpath 4 " X: 0
- finally
- Xpath 8 " X: 8
- endtry
- Xpath 16 " X: 0
- finally
- Xpath 32 " X: 32
- endtry
- Xpath 64 " X: 0
-endfunction
-
-function! G()
- try
- Xpath 128 " X: 128
- return
- Xpath 256 " X: 0
- finally
- Xpath 512 " X: 512
- call F()
- Xpath 1024 " X: 1024
- endtry
- Xpath 2048 " X: 0
-endfunction
-
-function! H()
- try
- Xpath 4096 " X: 4096
- call G()
- Xpath 8192 " X: 8192
- finally
- Xpath 16384 " X: 16384
- return
- Xpath 32768 " X: 0
- endtry
- Xpath 65536 " X: 0
-endfunction
-
-try
- Xpath 131072 " X: 131072
- call H()
- Xpath 262144 " X: 262144
-finally
- Xpath 524288 " X: 524288
-endtry
-Xpath 1048576 " X: 1048576
-
-Xcheck 1996459
-
-" Leave F, G, and H for execution as scripts in the next test.
-
-
-"-------------------------------------------------------------------------------
-" Test 28: Executing :finally clauses after :finish {{{1
-"
-" For a :finish command dynamically enclosed in a :try/:endtry region,
-" :finally clauses are executed and the sourced file is finished.
-"
-" This test executes the bodies of the functions F, G, and H from the
-" previous test as script files (:return replaced by :finish).
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-let scriptF = MakeScript("F") " X: 1 + 2 + 8 + 32
-let scriptG = MakeScript("G", scriptF) " X: 128 + 512 + 1024
-let scriptH = MakeScript("H", scriptG) " X: 4096 + 8192 + 16384
-
-try
- Xpath 131072 " X: 131072
- exec "source" scriptH
- Xpath 262144 " X: 262144
-finally
- Xpath 524288 " X: 524288
-endtry
-Xpath 1048576 " X: 1048576
-
-call delete(scriptF)
-call delete(scriptG)
-call delete(scriptH)
-unlet scriptF scriptG scriptH
-delfunction F
-delfunction G
-delfunction H
-
-Xcheck 1996459
-
-
-"-------------------------------------------------------------------------------
-" Test 29: Executing :finally clauses on errors {{{1
-"
-" After an error in a command dynamically enclosed in a :try/:endtry
-" region, :finally clauses are executed and the script processing is
-" terminated.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
- function! F()
- while 1
- try
- Xpath 1 " X: 1
- while 1
- try
- Xpath 2 " X: 2
- asdf " error
- Xpath 4 " X: 0
- finally
- Xpath 8 " X: 8
- endtry | Xpath 16 " X: 0
- Xpath 32 " X: 0
- break
- endwhile
- Xpath 64 " X: 0
- finally
- Xpath 128 " X: 128
- endtry | Xpath 256 " X: 0
- Xpath 512 " X: 0
- break
- endwhile
- Xpath 1024 " X: 0
- endfunction
-
- while 1
- try
- Xpath 2048 " X: 2048
- while 1
- call F()
- Xpath 4096 " X: 0
- break
- endwhile | Xpath 8192 " X: 0
- Xpath 16384 " X: 0
- finally
- Xpath 32768 " X: 32768
- endtry | Xpath 65536 " X: 0
- endwhile | Xpath 131072 " X: 0
- Xpath 262144 " X: 0
-endif
-
-if ExtraVim()
- function! G() abort
- if 1
- try
- Xpath 524288 " X: 524288
- asdf " error
- Xpath 1048576 " X: 0
- finally
- Xpath 2097152 " X: 2097152
- endtry | Xpath 4194304 " X: 0
- endif | Xpath 8388608 " X: 0
- Xpath 16777216 " X: 0
- endfunction
-
- if 1
- try
- Xpath 33554432 " X: 33554432
- call G()
- Xpath 67108864 " X: 0
- finally
- Xpath 134217728 " X: 134217728
- endtry | Xpath 268435456 " X: 0
- endif | Xpath 536870912 " X: 0
- Xpath 1073741824 " X: 0
-endif
-
-Xcheck 170428555
-
-
-"-------------------------------------------------------------------------------
-" Test 30: Executing :finally clauses on interrupt {{{1
-"
-" After an interrupt in a command dynamically enclosed in
-" a :try/:endtry region, :finally clauses are executed and the
-" script processing is terminated.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
- XloopINIT 1 16
-
- function! F()
- try
- Xloop 1 " X: 1 + 1*16
- "INTERRUPT
- Xloop 2 " X: 0
- finally
- Xloop 4 " X: 4 + 4*16
- endtry
- Xloop 8 " X: 0
- endfunction
-
- try
- Xpath 256 " X: 256
- try
- Xpath 512 " X: 512
- "INTERRUPT
- Xpath 1024 " X: 0
- finally
- Xpath 2048 " X: 2048
- try
- Xpath 4096 " X: 4096
- try
- Xpath 8192 " X: 8192
- finally
- Xpath 16384 " X: 16384
- try
- Xpath 32768 " X: 32768
- "INTERRUPT
- Xpath 65536 " X: 0
- endtry
- Xpath 131072 " X: 0
- endtry
- Xpath 262144 " X: 0
- endtry
- Xpath 524288 " X: 0
- endtry
- Xpath 1048576 " X: 0
- finally
- Xpath 2097152 " X: 2097152
- try
- Xpath 4194304 " X: 4194304
- call F()
- Xpath 8388608 " X: 0
- finally
- Xpath 16777216 " X: 16777216
- try
- Xpath 33554432 " X: 33554432
- XloopNEXT
- ExecAsScript F
- Xpath 67108864 " X: 0
- finally
- Xpath 134217728 " X: 134217728
- endtry
- Xpath 268435456 " X: 0
- endtry
- Xpath 536870912 " X: 0
- endtry
- Xpath 1073741824 " X: 0
-endif
-
-Xcheck 190905173
-
-
-"-------------------------------------------------------------------------------
-" Test 31: Executing :finally clauses after :throw {{{1
-"
-" After a :throw dynamically enclosed in a :try/:endtry region,
-" :finally clauses are executed and the script processing is
-" terminated.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
- XloopINIT 1 16
-
- function! F()
- try
- Xloop 1 " X: 1 + 1*16
- throw "exception"
- Xloop 2 " X: 0
- finally
- Xloop 4 " X: 4 + 4*16
- endtry
- Xloop 8 " X: 0
- endfunction
-
- try
- Xpath 256 " X: 256
- try
- Xpath 512 " X: 512
- throw "exception"
- Xpath 1024 " X: 0
- finally
- Xpath 2048 " X: 2048
- try
- Xpath 4096 " X: 4096
- try
- Xpath 8192 " X: 8192
- finally
- Xpath 16384 " X: 16384
- try
- Xpath 32768 " X: 32768
- throw "exception"
- Xpath 65536 " X: 0
- endtry
- Xpath 131072 " X: 0
- endtry
- Xpath 262144 " X: 0
- endtry
- Xpath 524288 " X: 0
- endtry
- Xpath 1048576 " X: 0
- finally
- Xpath 2097152 " X: 2097152
- try
- Xpath 4194304 " X: 4194304
- call F()
- Xpath 8388608 " X: 0
- finally
- Xpath 16777216 " X: 16777216
- try
- Xpath 33554432 " X: 33554432
- XloopNEXT
- ExecAsScript F
- Xpath 67108864 " X: 0
- finally
- Xpath 134217728 " X: 134217728
- endtry
- Xpath 268435456 " X: 0
- endtry
- Xpath 536870912 " X: 0
- endtry
- Xpath 1073741824 " X: 0
-endif
-
-Xcheck 190905173
-
-" Tests 32 and 33 were moved to test_trycatch.vim
-let Xtest = 34
-
-
-"-------------------------------------------------------------------------------
-" Test 34: :finally reason discarded by :continue {{{1
-"
-" When a :finally clause is executed due to a :continue, :break,
-" :return, :finish, error, interrupt or :throw, the jump reason is
-" discarded by a :continue in the finally clause.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- XloopINIT! 1 8
-
- function! C(jump)
- XloopNEXT
- let loop = 0
- while loop < 2
- let loop = loop + 1
- if loop == 1
- try
- if a:jump == "continue"
- continue
- elseif a:jump == "break"
- break
- elseif a:jump == "return" || a:jump == "finish"
- return
- elseif a:jump == "error"
- asdf
- elseif a:jump == "interrupt"
- "INTERRUPT
- let dummy = 0
- elseif a:jump == "throw"
- throw "abc"
- endif
- finally
- continue " discards jump that caused the :finally
- Xloop 1 " X: 0
- endtry
- Xloop 2 " X: 0
- elseif loop == 2
- Xloop 4 " X: 4*(1+8+64+512+4096+32768+262144)
- endif
- endwhile
- endfunction
-
- call C("continue")
- Xpath 2097152 " X: 2097152
- call C("break")
- Xpath 4194304 " X: 4194304
- call C("return")
- Xpath 8388608 " X: 8388608
- let g:jump = "finish"
- ExecAsScript C
- unlet g:jump
- Xpath 16777216 " X: 16777216
- try
- call C("error")
- Xpath 33554432 " X: 33554432
- finally
- Xpath 67108864 " X: 67108864
- try
- call C("interrupt")
- Xpath 134217728 " X: 134217728
- finally
- Xpath 268435456 " X: 268435456
- call C("throw")
- Xpath 536870912 " X: 536870912
- endtry
- endtry
- Xpath 1073741824 " X: 1073741824
-
- delfunction C
-
-endif
-
-Xcheck 2146584868
-
-
-"-------------------------------------------------------------------------------
-" Test 35: :finally reason discarded by :break {{{1
-"
-" When a :finally clause is executed due to a :continue, :break,
-" :return, :finish, error, interrupt or :throw, the jump reason is
-" discarded by a :break in the finally clause.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- XloopINIT! 1 8
-
- function! B(jump)
- XloopNEXT
- let loop = 0
- while loop < 2
- let loop = loop + 1
- if loop == 1
- try
- if a:jump == "continue"
- continue
- elseif a:jump == "break"
- break
- elseif a:jump == "return" || a:jump == "finish"
- return
- elseif a:jump == "error"
- asdf
- elseif a:jump == "interrupt"
- "INTERRUPT
- let dummy = 0
- elseif a:jump == "throw"
- throw "abc"
- endif
- finally
- break " discards jump that caused the :finally
- Xloop 1 " X: 0
- endtry
- elseif loop == 2
- Xloop 2 " X: 0
- endif
- endwhile
- Xloop 4 " X: 4*(1+8+64+512+4096+32768+262144)
- endfunction
-
- call B("continue")
- Xpath 2097152 " X: 2097152
- call B("break")
- Xpath 4194304 " X: 4194304
- call B("return")
- Xpath 8388608 " X: 8388608
- let g:jump = "finish"
- ExecAsScript B
- unlet g:jump
- Xpath 16777216 " X: 16777216
- try
- call B("error")
- Xpath 33554432 " X: 33554432
- finally
- Xpath 67108864 " X: 67108864
- try
- call B("interrupt")
- Xpath 134217728 " X: 134217728
- finally
- Xpath 268435456 " X: 268435456
- call B("throw")
- Xpath 536870912 " X: 536870912
- endtry
- endtry
- Xpath 1073741824 " X: 1073741824
-
- delfunction B
-
-endif
-
-Xcheck 2146584868
-
-
-"-------------------------------------------------------------------------------
-" Test 36: :finally reason discarded by :return {{{1
-"
-" When a :finally clause is executed due to a :continue, :break,
-" :return, :finish, error, interrupt or :throw, the jump reason is
-" discarded by a :return in the finally clause.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- XloopINIT! 1 8
-
- function! R(jump, retval) abort
- XloopNEXT
- let loop = 0
- while loop < 2
- let loop = loop + 1
- if loop == 1
- try
- if a:jump == "continue"
- continue
- elseif a:jump == "break"
- break
- elseif a:jump == "return"
- return
- elseif a:jump == "error"
- asdf
- elseif a:jump == "interrupt"
- "INTERRUPT
- let dummy = 0
- elseif a:jump == "throw"
- throw "abc"
- endif
- finally
- return a:retval " discards jump that caused the :finally
- Xloop 1 " X: 0
- endtry
- elseif loop == 2
- Xloop 2 " X: 0
- endif
- endwhile
- Xloop 4 " X: 0
- endfunction
-
- let sum = -R("continue", -8)
- Xpath 2097152 " X: 2097152
- let sum = sum - R("break", -16)
- Xpath 4194304 " X: 4194304
- let sum = sum - R("return", -32)
- Xpath 8388608 " X: 8388608
- try
- let sum = sum - R("error", -64)
- Xpath 16777216 " X: 16777216
- finally
- Xpath 33554432 " X: 33554432
- try
- let sum = sum - R("interrupt", -128)
- Xpath 67108864 " X: 67108864
- finally
- Xpath 134217728 " X: 134217728
- let sum = sum - R("throw", -256)
- Xpath 268435456 " X: 268435456
- endtry
- endtry
- Xpath 536870912 " X: 536870912
-
- let expected = 8 + 16 + 32 + 64 + 128 + 256
- if sum != expected
- Xpath 1073741824 " X: 0
- Xout "sum =" . sum . ", expected: " . expected
- endif
-
- unlet sum expected
- delfunction R
-
-endif
-
-Xcheck 1071644672
-
-
-"-------------------------------------------------------------------------------
-" Test 37: :finally reason discarded by :finish {{{1
-"
-" When a :finally clause is executed due to a :continue, :break,
-" :return, :finish, error, interrupt or :throw, the jump reason is
-" discarded by a :finish in the finally clause.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- XloopINIT! 1 8
-
- function! F(jump) " not executed as function, transformed to a script
- XloopNEXT
- let loop = 0
- while loop < 2
- let loop = loop + 1
- if loop == 1
- try
- if a:jump == "continue"
- continue
- elseif a:jump == "break"
- break
- elseif a:jump == "finish"
- finish
- elseif a:jump == "error"
- asdf
- elseif a:jump == "interrupt"
- "INTERRUPT
- let dummy = 0
- elseif a:jump == "throw"
- throw "abc"
- endif
- finally
- finish " discards jump that caused the :finally
- Xloop 1 " X: 0
- endtry
- elseif loop == 2
- Xloop 2 " X: 0
- endif
- endwhile
- Xloop 4 " X: 0
- endfunction
-
- let scriptF = MakeScript("F")
- delfunction F
-
- let g:jump = "continue"
- exec "source" scriptF
- Xpath 2097152 " X: 2097152
- let g:jump = "break"
- exec "source" scriptF
- Xpath 4194304 " X: 4194304
- let g:jump = "finish"
- exec "source" scriptF
- Xpath 8388608 " X: 8388608
- try
- let g:jump = "error"
- exec "source" scriptF
- Xpath 16777216 " X: 16777216
- finally
- Xpath 33554432 " X: 33554432
- try
- let g:jump = "interrupt"
- exec "source" scriptF
- Xpath 67108864 " X: 67108864
- finally
- Xpath 134217728 " X: 134217728
- try
- let g:jump = "throw"
- exec "source" scriptF
- Xpath 268435456 " X: 268435456
- finally
- Xpath 536870912 " X: 536870912
- endtry
- endtry
- endtry
- unlet g:jump
-
- call delete(scriptF)
- unlet scriptF
-
-endif
-
-Xcheck 1071644672
-
-
-"-------------------------------------------------------------------------------
-" Test 38: :finally reason discarded by an error {{{1
-"
-" When a :finally clause is executed due to a :continue, :break,
-" :return, :finish, error, interrupt or :throw, the jump reason is
-" discarded by an error in the finally clause.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- XloopINIT! 1 4
-
- function! E(jump)
- XloopNEXT
- let loop = 0
- while loop < 2
- let loop = loop + 1
- if loop == 1
- try
- if a:jump == "continue"
- continue
- elseif a:jump == "break"
- break
- elseif a:jump == "return" || a:jump == "finish"
- return
- elseif a:jump == "error"
- asdf
- elseif a:jump == "interrupt"
- "INTERRUPT
- let dummy = 0
- elseif a:jump == "throw"
- throw "abc"
- endif
- finally
- asdf " error; discards jump that caused the :finally
- endtry
- elseif loop == 2
- Xloop 1 " X: 0
- endif
- endwhile
- Xloop 2 " X: 0
- endfunction
-
- try
- Xpath 16384 " X: 16384
- call E("continue")
- Xpath 32768 " X: 0
- finally
- try
- Xpath 65536 " X: 65536
- call E("break")
- Xpath 131072 " X: 0
- finally
- try
- Xpath 262144 " X: 262144
- call E("return")
- Xpath 524288 " X: 0
- finally
- try
- Xpath 1048576 " X: 1048576
- let g:jump = "finish"
- ExecAsScript E
- Xpath 2097152 " X: 0
- finally
- unlet g:jump
- try
- Xpath 4194304 " X: 4194304
- call E("error")
- Xpath 8388608 " X: 0
- finally
- try
- Xpath 16777216 " X: 16777216
- call E("interrupt")
- Xpath 33554432 " X: 0
- finally
- try
- Xpath 67108864 " X: 67108864
- call E("throw")
- Xpath 134217728 " X: 0
- finally
- Xpath 268435456 " X: 268435456
- delfunction E
- endtry
- endtry
- endtry
- endtry
- endtry
- endtry
- endtry
- Xpath 536870912 " X: 0
-
-endif
-
-Xcheck 357908480
-
-
-"-------------------------------------------------------------------------------
-" Test 39: :finally reason discarded by an interrupt {{{1
-"
-" When a :finally clause is executed due to a :continue, :break,
-" :return, :finish, error, interrupt or :throw, the jump reason is
-" discarded by an interrupt in the finally clause.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- XloopINIT! 1 4
-
- function! I(jump)
- XloopNEXT
- let loop = 0
- while loop < 2
- let loop = loop + 1
- if loop == 1
- try
- if a:jump == "continue"
- continue
- elseif a:jump == "break"
- break
- elseif a:jump == "return" || a:jump == "finish"
- return
- elseif a:jump == "error"
- asdf
- elseif a:jump == "interrupt"
- "INTERRUPT
- let dummy = 0
- elseif a:jump == "throw"
- throw "abc"
- endif
- finally
- "INTERRUPT - discards jump that caused the :finally
- let dummy = 0
- endtry
- elseif loop == 2
- Xloop 1 " X: 0
- endif
- endwhile
- Xloop 2 " X: 0
- endfunction
-
- try
- Xpath 16384 " X: 16384
- call I("continue")
- Xpath 32768 " X: 0
- finally
- try
- Xpath 65536 " X: 65536
- call I("break")
- Xpath 131072 " X: 0
- finally
- try
- Xpath 262144 " X: 262144
- call I("return")
- Xpath 524288 " X: 0
- finally
- try
- Xpath 1048576 " X: 1048576
- let g:jump = "finish"
- ExecAsScript I
- Xpath 2097152 " X: 0
- finally
- unlet g:jump
- try
- Xpath 4194304 " X: 4194304
- call I("error")
- Xpath 8388608 " X: 0
- finally
- try
- Xpath 16777216 " X: 16777216
- call I("interrupt")
- Xpath 33554432 " X: 0
- finally
- try
- Xpath 67108864 " X: 67108864
- call I("throw")
- Xpath 134217728 " X: 0
- finally
- Xpath 268435456 " X: 268435456
- delfunction I
- endtry
- endtry
- endtry
- endtry
- endtry
- endtry
- endtry
- Xpath 536870912 " X: 0
-
-endif
-
-Xcheck 357908480
-
-
-"-------------------------------------------------------------------------------
-" Test 40: :finally reason discarded by :throw {{{1
-"
-" When a :finally clause is executed due to a :continue, :break,
-" :return, :finish, error, interrupt or :throw, the jump reason is
-" discarded by a :throw in the finally clause.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- XloopINIT! 1 4
-
- function! T(jump)
- XloopNEXT
- let loop = 0
- while loop < 2
- let loop = loop + 1
- if loop == 1
- try
- if a:jump == "continue"
- continue
- elseif a:jump == "break"
- break
- elseif a:jump == "return" || a:jump == "finish"
- return
- elseif a:jump == "error"
- asdf
- elseif a:jump == "interrupt"
- "INTERRUPT
- let dummy = 0
- elseif a:jump == "throw"
- throw "abc"
- endif
- finally
- throw "xyz" " discards jump that caused the :finally
- endtry
- elseif loop == 2
- Xloop 1 " X: 0
- endif
- endwhile
- Xloop 2 " X: 0
- endfunction
-
- try
- Xpath 16384 " X: 16384
- call T("continue")
- Xpath 32768 " X: 0
- finally
- try
- Xpath 65536 " X: 65536
- call T("break")
- Xpath 131072 " X: 0
- finally
- try
- Xpath 262144 " X: 262144
- call T("return")
- Xpath 524288 " X: 0
- finally
- try
- Xpath 1048576 " X: 1048576
- let g:jump = "finish"
- ExecAsScript T
- Xpath 2097152 " X: 0
- finally
- unlet g:jump
- try
- Xpath 4194304 " X: 4194304
- call T("error")
- Xpath 8388608 " X: 0
- finally
- try
- Xpath 16777216 " X: 16777216
- call T("interrupt")
- Xpath 33554432 " X: 0
- finally
- try
- Xpath 67108864 " X: 67108864
- call T("throw")
- Xpath 134217728 " X: 0
- finally
- Xpath 268435456 " X: 268435456
- delfunction T
- endtry
- endtry
- endtry
- endtry
- endtry
- endtry
- endtry
- Xpath 536870912 " X: 0
-
-endif
-
-Xcheck 357908480
-
-" Tests 41 to 48 were moved to test_trycatch.vim
-let Xtest = 49
-
-
-"-------------------------------------------------------------------------------
-" Test 49: Throwing exceptions across functions {{{1
-"
-" When an exception is thrown but not caught inside a function, the
-" caller is checked for a matching :catch clause.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! C()
- try
- Xpath 1 " X: 1
- throw "arrgh"
- Xpath 2 " X: 0
- catch /arrgh/
- Xpath 4 " X: 4
- endtry
- Xpath 8 " X: 8
-endfunction
-
-XloopINIT! 16 16
-
-function! T1()
- XloopNEXT
- try
- Xloop 1 " X: 16 + 16*16
- throw "arrgh"
- Xloop 2 " X: 0
- finally
- Xloop 4 " X: 64 + 64*16
- endtry
- Xloop 8 " X: 0
-endfunction
-
-function! T2()
- try
- Xpath 4096 " X: 4096
- call T1()
- Xpath 8192 " X: 0
- finally
- Xpath 16384 " X: 16384
- endtry
- Xpath 32768 " X: 0
-endfunction
-
-try
- Xpath 65536 " X: 65536
- call C() " throw and catch
- Xpath 131072 " X: 131072
-catch /.*/
- Xpath 262144 " X: 0
- Xout v:exception "in" v:throwpoint
-endtry
-
-try
- Xpath 524288 " X: 524288
- call T1() " throw, one level
- Xpath 1048576 " X: 0
-catch /arrgh/
- Xpath 2097152 " X: 2097152
-catch /.*/
- Xpath 4194304 " X: 0
- Xout v:exception "in" v:throwpoint
-endtry
-
-try
- Xpath 8388608 " X: 8388608
- call T2() " throw, two levels
- Xpath 16777216 " X: 0
-catch /arrgh/
- Xpath 33554432 " X: 33554432
-catch /.*/
- Xpath 67108864 " X: 0
- Xout v:exception "in" v:throwpoint
-endtry
-Xpath 134217728 " X: 134217728
-
-Xcheck 179000669
-
-" Leave C, T1, and T2 for execution as scripts in the next test.
-
-
-"-------------------------------------------------------------------------------
-" Test 50: Throwing exceptions across script files {{{1
-"
-" When an exception is thrown but not caught inside a script file,
-" the sourcing script or function is checked for a matching :catch
-" clause.
-"
-" This test executes the bodies of the functions C, T1, and T2 from
-" the previous test as script files (:return replaced by :finish).
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-let scriptC = MakeScript("C") " X: 1 + 4 + 8
-delfunction C
-
-XloopINIT! 16 16
-
-let scriptT1 = MakeScript("T1") " X: 16 + 64 + 16*16 + 64*16
-delfunction T1
-
-let scriptT2 = MakeScript("T2", scriptT1) " X: 4096 + 16384
-delfunction T2
-
-function! F()
- try
- Xpath 65536 " X: 65536
- exec "source" g:scriptC
- Xpath 131072 " X: 131072
- catch /.*/
- Xpath 262144 " X: 0
- Xout v:exception "in" v:throwpoint
- endtry
-
- try
- Xpath 524288 " X: 524288
- exec "source" g:scriptT1
- Xpath 1048576 " X: 0
- catch /arrgh/
- Xpath 2097152 " X: 2097152
- catch /.*/
- Xpath 4194304 " X: 0
- Xout v:exception "in" v:throwpoint
- endtry
-endfunction
-
-try
- Xpath 8388608 " X: 8388608
- call F()
- Xpath 16777216 " X: 16777216
- exec "source" scriptT2
- Xpath 33554432 " X: 0
-catch /arrgh/
- Xpath 67108864 " X: 67108864
-catch /.*/
- Xpath 134217728 " X: 0
- Xout v:exception "in" v:throwpoint
-endtry
-Xpath 268435456 " X: 268435456
-
-call delete(scriptC)
-call delete(scriptT1)
-call delete(scriptT2)
-unlet scriptC scriptT1 scriptT2
-delfunction F
-
-Xcheck 363550045
-
-" Test 51 was moved to test_trycatch.vim
-let Xtest = 52
-
-
-"-------------------------------------------------------------------------------
-" Test 52: Uncaught exceptions {{{1
-"
-" When an exception is thrown but not caught, an error message is
-" displayed when the script is terminated. In case of an interrupt
-" or error exception, the normal interrupt or error message(s) are
-" displayed.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-let msgfile = tempname()
-
-function! MESSAGES(...)
- try
- exec "edit" g:msgfile
- catch /^Vim(edit):/
- return 0
- endtry
-
- let english = v:lang == "C" || v:lang =~ '^[Ee]n'
- let match = 1
- norm gg
-
- let num = a:0 / 2
- let cnt = 1
- while cnt <= num
- let enr = a:{2*cnt - 1}
- let emsg= a:{2*cnt}
- let cnt = cnt + 1
-
- if enr == ""
- Xout "TODO: Add message number for:" emsg
- elseif enr == "INT"
- let enr = ""
- endif
- if enr == "" && !english
- continue
- endif
- let pattern = (enr != "") ? enr . ':.*' : ''
- if english
- let pattern = pattern . emsg
- endif
- if !search(pattern, "W")
- let match = 0
- Xout "No match for:" pattern
- endif
- norm $
- endwhile
-
- bwipeout!
- return match
-endfunction
-
-if ExtraVim(msgfile)
- Xpath 1 " X: 1
- throw "arrgh"
-endif
-
-Xpath 2 " X: 2
-if !MESSAGES('E605', "Exception not caught")
- Xpath 4 " X: 0
-endif
-
-if ExtraVim(msgfile)
- try
- Xpath 8 " X: 8
- throw "oops"
- catch /arrgh/
- Xpath 16 " X: 0
- endtry
- Xpath 32 " X: 0
-endif
-
-Xpath 64 " X: 64
-if !MESSAGES('E605', "Exception not caught")
- Xpath 128 " X: 0
-endif
-
-if ExtraVim(msgfile)
- function! T()
- throw "brrr"
- endfunction
-
- try
- Xpath 256 " X: 256
- throw "arrgh"
- catch /.*/
- Xpath 512 " X: 512
- call T()
- endtry
- Xpath 1024 " X: 0
-endif
-
-Xpath 2048 " X: 2048
-if !MESSAGES('E605', "Exception not caught")
- Xpath 4096 " X: 0
-endif
-
-if ExtraVim(msgfile)
- try
- Xpath 8192 " X: 8192
- throw "arrgh"
- finally
- Xpath 16384 " X: 16384
- throw "brrr"
- endtry
- Xpath 32768 " X: 0
-endif
-
-Xpath 65536 " X: 65536
-if !MESSAGES('E605', "Exception not caught")
- Xpath 131072 " X: 0
-endif
-
-if ExtraVim(msgfile)
- try
- Xpath 262144 " X: 262144
- "INTERRUPT
- endtry
- Xpath 524288 " X: 0
-endif
-
-Xpath 1048576 " X: 1048576
-if !MESSAGES('INT', "Interrupted")
- Xpath 2097152 " X: 0
-endif
-
-if ExtraVim(msgfile)
- try
- Xpath 4194304 " X: 4194304
- let x = novar " error E121/E15; exception: E121
- catch /E15:/ " should not catch
- Xpath 8388608 " X: 0
- endtry
- Xpath 16777216 " X: 0
-endif
-
-Xpath 33554432 " X: 33554432
-if !MESSAGES('E121', "Undefined variable", 'E15', "Invalid expression")
- Xpath 67108864 " X: 0
-endif
-
-if ExtraVim(msgfile)
- try
- Xpath 134217728 " X: 134217728
-" unlet novar # " error E108/E488; exception: E488
- catch /E108:/ " should not catch
- Xpath 268435456 " X: 0
- endtry
- Xpath 536870912 " X: 0
-endif
-
-Xpath 1073741824 " X: 1073741824
-if !MESSAGES('E108', "No such variable", 'E488', "Trailing characters")
- " The Xpath command does not accept 2^31 (negative); add explicitly:
- let Xpath = Xpath + 2147483648 " X: 0
-endif
-
-call delete(msgfile)
-unlet msgfile
-
-Xcheck 1247112011
-
-" Leave MESSAGES() for the next tests.
-
-
-"-------------------------------------------------------------------------------
-" Test 53: Nesting errors: :endif/:else/:elseif {{{1
-"
-" For nesting errors of :if conditionals the correct error messages
-" should be given.
-"
-" This test reuses the function MESSAGES() from the previous test.
-" This functions checks the messages in g:msgfile.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-let msgfile = tempname()
-
-if ExtraVim(msgfile)
-" endif
-endif
-if MESSAGES('E580', ":endif without :if")
- Xpath 1 " X: 1
-endif
-
-if ExtraVim(msgfile)
-" while 1
-" endif
-" endwhile
-endif
-if MESSAGES('E580', ":endif without :if")
- Xpath 2 " X: 2
-endif
-
-if ExtraVim(msgfile)
-" try
-" finally
-" endif
-" endtry
-endif
-if MESSAGES('E580', ":endif without :if")
- Xpath 4 " X: 4
-endif
-
-if ExtraVim(msgfile)
-" try
-" endif
-" endtry
-endif
-if MESSAGES('E580', ":endif without :if")
- Xpath 8 " X: 8
-endif
-
-if ExtraVim(msgfile)
-" try
-" throw "a"
-" catch /a/
-" endif
-" endtry
-endif
-if MESSAGES('E580', ":endif without :if")
- Xpath 16 " X: 16
-endif
-
-if ExtraVim(msgfile)
-" else
-endif
-if MESSAGES('E581', ":else without :if")
- Xpath 32 " X: 32
-endif
-
-if ExtraVim(msgfile)
-" while 1
-" else
-" endwhile
-endif
-if MESSAGES('E581', ":else without :if")
- Xpath 64 " X: 64
-endif
-
-if ExtraVim(msgfile)
-" try
-" finally
-" else
-" endtry
-endif
-if MESSAGES('E581', ":else without :if")
- Xpath 128 " X: 128
-endif
-
-if ExtraVim(msgfile)
-" try
-" else
-" endtry
-endif
-if MESSAGES('E581', ":else without :if")
- Xpath 256 " X: 256
-endif
-
-if ExtraVim(msgfile)
-" try
-" throw "a"
-" catch /a/
-" else
-" endtry
-endif
-if MESSAGES('E581', ":else without :if")
- Xpath 512 " X: 512
-endif
-
-if ExtraVim(msgfile)
-" elseif
-endif
-if MESSAGES('E582', ":elseif without :if")
- Xpath 1024 " X: 1024
-endif
-
-if ExtraVim(msgfile)
-" while 1
-" elseif
-" endwhile
-endif
-if MESSAGES('E582', ":elseif without :if")
- Xpath 2048 " X: 2048
-endif
-
-if ExtraVim(msgfile)
-" try
-" finally
-" elseif
-" endtry
-endif
-if MESSAGES('E582', ":elseif without :if")
- Xpath 4096 " X: 4096
-endif
-
-if ExtraVim(msgfile)
-" try
-" elseif
-" endtry
-endif
-if MESSAGES('E582', ":elseif without :if")
- Xpath 8192 " X: 8192
-endif
-
-if ExtraVim(msgfile)
-" try
-" throw "a"
-" catch /a/
-" elseif
-" endtry
-endif
-if MESSAGES('E582', ":elseif without :if")
- Xpath 16384 " X: 16384
-endif
-
-if ExtraVim(msgfile)
-" if 1
-" else
-" else
-" endif
-endif
-if MESSAGES('E583', "multiple :else")
- Xpath 32768 " X: 32768
-endif
-
-if ExtraVim(msgfile)
-" if 1
-" else
-" elseif 1
-" endif
-endif
-if MESSAGES('E584', ":elseif after :else")
- Xpath 65536 " X: 65536
-endif
-
-call delete(msgfile)
-unlet msgfile
-
-Xcheck 131071
-
-" Leave MESSAGES() for the next test.
-
-
-"-------------------------------------------------------------------------------
-" Test 54: Nesting errors: :while/:endwhile {{{1
-"
-" For nesting errors of :while conditionals the correct error messages
-" should be given.
-"
-" This test reuses the function MESSAGES() from the previous test.
-" This functions checks the messages in g:msgfile.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-let msgfile = tempname()
-
-if ExtraVim(msgfile)
-" endwhile
-endif
-if MESSAGES('E588', ":endwhile without :while")
- Xpath 1 " X: 1
-endif
-
-if ExtraVim(msgfile)
-" if 1
-" endwhile
-" endif
-endif
-if MESSAGES('E588', ":endwhile without :while")
- Xpath 2 " X: 2
-endif
-
-if ExtraVim(msgfile)
-" while 1
-" if 1
-" endwhile
-endif
-if MESSAGES('E171', "Missing :endif")
- Xpath 4 " X: 4
-endif
-
-if ExtraVim(msgfile)
-" try
-" finally
-" endwhile
-" endtry
-endif
-if MESSAGES('E588', ":endwhile without :while")
- Xpath 8 " X: 8
-endif
-
-if ExtraVim(msgfile)
-" while 1
-" try
-" finally
-" endwhile
-endif
-if MESSAGES('E600', "Missing :endtry")
- Xpath 16 " X: 16
-endif
-
-if ExtraVim(msgfile)
-" while 1
-" if 1
-" try
-" finally
-" endwhile
-endif
-if MESSAGES('E600', "Missing :endtry")
- Xpath 32 " X: 32
-endif
-
-if ExtraVim(msgfile)
-" while 1
-" try
-" finally
-" if 1
-" endwhile
-endif
-if MESSAGES('E171', "Missing :endif")
- Xpath 64 " X: 64
-endif
-
-if ExtraVim(msgfile)
-" try
-" endwhile
-" endtry
-endif
-if MESSAGES('E588', ":endwhile without :while")
- Xpath 128 " X: 128
-endif
-
-if ExtraVim(msgfile)
-" while 1
-" try
-" endwhile
-" endtry
-" endwhile
-endif
-if MESSAGES('E588', ":endwhile without :while")
- Xpath 256 " X: 256
-endif
-
-if ExtraVim(msgfile)
-" try
-" throw "a"
-" catch /a/
-" endwhile
-" endtry
-endif
-if MESSAGES('E588', ":endwhile without :while")
- Xpath 512 " X: 512
-endif
-
-if ExtraVim(msgfile)
-" while 1
-" try
-" throw "a"
-" catch /a/
-" endwhile
-" endtry
-" endwhile
-endif
-if MESSAGES('E588', ":endwhile without :while")
- Xpath 1024 " X: 1024
-endif
-
-
-call delete(msgfile)
-unlet msgfile
-
-Xcheck 2047
-
-" Leave MESSAGES() for the next test.
-
-
-"-------------------------------------------------------------------------------
-" Test 55: Nesting errors: :continue/:break {{{1
-"
-" For nesting errors of :continue and :break commands the correct
-" error messages should be given.
-"
-" This test reuses the function MESSAGES() from the previous test.
-" This functions checks the messages in g:msgfile.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-let msgfile = tempname()
-
-if ExtraVim(msgfile)
-" continue
-endif
-if MESSAGES('E586', ":continue without :while")
- Xpath 1 " X: 1
-endif
-
-if ExtraVim(msgfile)
-" if 1
-" continue
-" endif
-endif
-if MESSAGES('E586', ":continue without :while")
- Xpath 2 " X: 2
-endif
-
-if ExtraVim(msgfile)
-" try
-" finally
-" continue
-" endtry
-endif
-if MESSAGES('E586', ":continue without :while")
- Xpath 4 " X: 4
-endif
-
-if ExtraVim(msgfile)
-" try
-" continue
-" endtry
-endif
-if MESSAGES('E586', ":continue without :while")
- Xpath 8 " X: 8
-endif
-
-if ExtraVim(msgfile)
-" try
-" throw "a"
-" catch /a/
-" continue
-" endtry
-endif
-if MESSAGES('E586', ":continue without :while")
- Xpath 16 " X: 16
-endif
-
-if ExtraVim(msgfile)
-" break
-endif
-if MESSAGES('E587', ":break without :while")
- Xpath 32 " X: 32
-endif
-
-if ExtraVim(msgfile)
-" if 1
-" break
-" endif
-endif
-if MESSAGES('E587', ":break without :while")
- Xpath 64 " X: 64
-endif
-
-if ExtraVim(msgfile)
-" try
-" finally
-" break
-" endtry
-endif
-if MESSAGES('E587', ":break without :while")
- Xpath 128 " X: 128
-endif
-
-if ExtraVim(msgfile)
-" try
-" break
-" endtry
-endif
-if MESSAGES('E587', ":break without :while")
- Xpath 256 " X: 256
-endif
-
-if ExtraVim(msgfile)
-" try
-" throw "a"
-" catch /a/
-" break
-" endtry
-endif
-if MESSAGES('E587', ":break without :while")
- Xpath 512 " X: 512
-endif
-
-call delete(msgfile)
-unlet msgfile
-
-Xcheck 1023
-
-" Leave MESSAGES() for the next test.
-
-
-"-------------------------------------------------------------------------------
-" Test 56: Nesting errors: :endtry {{{1
-"
-" For nesting errors of :try conditionals the correct error messages
-" should be given.
-"
-" This test reuses the function MESSAGES() from the previous test.
-" This functions checks the messages in g:msgfile.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-let msgfile = tempname()
-
-if ExtraVim(msgfile)
-" endtry
-endif
-if MESSAGES('E602', ":endtry without :try")
- Xpath 1 " X: 1
-endif
-
-if ExtraVim(msgfile)
-" if 1
-" endtry
-" endif
-endif
-if MESSAGES('E602', ":endtry without :try")
- Xpath 2 " X: 2
-endif
-
-if ExtraVim(msgfile)
-" while 1
-" endtry
-" endwhile
-endif
-if MESSAGES('E602', ":endtry without :try")
- Xpath 4 " X: 4
-endif
-
-if ExtraVim(msgfile)
-" try
-" if 1
-" endtry
-endif
-if MESSAGES('E171', "Missing :endif")
- Xpath 8 " X: 8
-endif
-
-if ExtraVim(msgfile)
-" try
-" while 1
-" endtry
-endif
-if MESSAGES('E170', "Missing :endwhile")
- Xpath 16 " X: 16
-endif
-
-if ExtraVim(msgfile)
-" try
-" finally
-" if 1
-" endtry
-endif
-if MESSAGES('E171', "Missing :endif")
- Xpath 32 " X: 32
-endif
-
-if ExtraVim(msgfile)
-" try
-" finally
-" while 1
-" endtry
-endif
-if MESSAGES('E170', "Missing :endwhile")
- Xpath 64 " X: 64
-endif
-
-if ExtraVim(msgfile)
- try
- Xpath 4194304 " X: 4194304
- let x = novar " error E121; exception: E121
- catch /E15:/ " should not catch
- Xpath 8388608 " X: 0
- endtry
- Xpath 16777216 " X: 0
-endif
-
-Xpath 33554432 " X: 33554432
-if !MESSAGES('E121', "Undefined variable")
- Xpath 67108864 " X: 0
-endif
-
-if ExtraVim(msgfile)
-" try
-" throw "a"
-" catch /a/
-" while 1
-" endtry
-endif
-if MESSAGES('E170', "Missing :endwhile")
- Xpath 256 " X: 256
-endif
-
-call delete(msgfile)
-unlet msgfile
-
-delfunction MESSAGES
-
-Xcheck 511
-
-
-"-------------------------------------------------------------------------------
-" Test 57: v:exception and v:throwpoint for user exceptions {{{1
-"
-" v:exception evaluates to the value of the exception that was caught
-" most recently and is not finished. (A caught exception is finished
-" when the next ":catch", ":finally", or ":endtry" is reached.)
-" v:throwpoint evaluates to the script/function name and line number
-" where that exception has been thrown.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! FuncException()
- let g:exception = v:exception
-endfunction
-
-function! FuncThrowpoint()
- let g:throwpoint = v:throwpoint
-endfunction
-
-let scriptException = MakeScript("FuncException")
-let scriptThrowPoint = MakeScript("FuncThrowpoint")
-
-command! CmdException let g:exception = v:exception
-command! CmdThrowpoint let g:throwpoint = v:throwpoint
-
-XloopINIT! 1 2
-
-function! CHECK(n, exception, throwname, throwline)
- XloopNEXT
- let error = 0
- if v:exception != a:exception
- Xout a:n.": v:exception is" v:exception "instead of" a:exception
- let error = 1
- endif
- if v:throwpoint !~ a:throwname
- let name = escape(a:throwname, '\')
- Xout a:n.": v:throwpoint (".v:throwpoint.") does not match" name
- let error = 1
- endif
- if v:throwpoint !~ a:throwline
- let line = escape(a:throwline, '\')
- Xout a:n.": v:throwpoint (".v:throwpoint.") does not match" line
- let error = 1
- endif
- if error
- Xloop 1 " X: 0
- endif
-endfunction
-
-function! T(arg, line)
- if a:line == 2
- throw a:arg " in line 2
- elseif a:line == 4
- throw a:arg " in line 4
- elseif a:line == 6
- throw a:arg " in line 6
- elseif a:line == 8
- throw a:arg " in line 8
- endif
-endfunction
-
-function! G(arg, line)
- call T(a:arg, a:line)
-endfunction
-
-function! F(arg, line)
- call G(a:arg, a:line)
-endfunction
-
-let scriptT = MakeScript("T")
-let scriptG = MakeScript("G", scriptT)
-let scriptF = MakeScript("F", scriptG)
-
-try
- Xpath 32768 " X: 32768
- call F("oops", 2)
-catch /.*/
- Xpath 65536 " X: 65536
- let exception = v:exception
- let throwpoint = v:throwpoint
- call CHECK(1, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>')
- exec "let exception = v:exception"
- exec "let throwpoint = v:throwpoint"
- call CHECK(2, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>')
- CmdException
- CmdThrowpoint
- call CHECK(3, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>')
- call FuncException()
- call FuncThrowpoint()
- call CHECK(4, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>')
- exec "source" scriptException
- exec "source" scriptThrowPoint
- call CHECK(5, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>')
- try
- Xpath 131072 " X: 131072
- call G("arrgh", 4)
- catch /.*/
- Xpath 262144 " X: 262144
- let exception = v:exception
- let throwpoint = v:throwpoint
- call CHECK(6, "arrgh", '\<G\[1]\.\.T\>', '\<4\>')
- try
- Xpath 524288 " X: 524288
- let g:arg = "autsch"
- let g:line = 6
- exec "source" scriptF
- catch /.*/
- Xpath 1048576 " X: 1048576
- let exception = v:exception
- let throwpoint = v:throwpoint
- " Symbolic links in tempname()s are not resolved, whereas resolving
- " is done for v:throwpoint. Resolve the temporary file name for
- " scriptT, so that it can be matched against v:throwpoint.
- call CHECK(7, "autsch", resolve(scriptT), '\<6\>')
- finally
- Xpath 2097152 " X: 2097152
- let exception = v:exception
- let throwpoint = v:throwpoint
- call CHECK(8, "arrgh", '\<G\[1]\.\.T\>', '\<4\>')
- try
- Xpath 4194304 " X: 4194304
- let g:arg = "brrrr"
- let g:line = 8
- exec "source" scriptG
- catch /.*/
- Xpath 8388608 " X: 8388608
- let exception = v:exception
- let throwpoint = v:throwpoint
- " Resolve scriptT for matching it against v:throwpoint.
- call CHECK(9, "brrrr", resolve(scriptT), '\<8\>')
- finally
- Xpath 16777216 " X: 16777216
- let exception = v:exception
- let throwpoint = v:throwpoint
- call CHECK(10, "arrgh", '\<G\[1]\.\.T\>', '\<4\>')
- endtry
- Xpath 33554432 " X: 33554432
- let exception = v:exception
- let throwpoint = v:throwpoint
- call CHECK(11, "arrgh", '\<G\[1]\.\.T\>', '\<4\>')
- endtry
- Xpath 67108864 " X: 67108864
- let exception = v:exception
- let throwpoint = v:throwpoint
- call CHECK(12, "arrgh", '\<G\[1]\.\.T\>', '\<4\>')
- finally
- Xpath 134217728 " X: 134217728
- let exception = v:exception
- let throwpoint = v:throwpoint
- call CHECK(13, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>')
- endtry
- Xpath 268435456 " X: 268435456
- let exception = v:exception
- let throwpoint = v:throwpoint
- call CHECK(14, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>')
-finally
- Xpath 536870912 " X: 536870912
- let exception = v:exception
- let throwpoint = v:throwpoint
- call CHECK(15, "", '^$', '^$')
-endtry
-
-Xpath 1073741824 " X: 1073741824
-
-unlet exception throwpoint
-delfunction FuncException
-delfunction FuncThrowpoint
-call delete(scriptException)
-call delete(scriptThrowPoint)
-unlet scriptException scriptThrowPoint
-delcommand CmdException
-delcommand CmdThrowpoint
-delfunction T
-delfunction G
-delfunction F
-call delete(scriptT)
-call delete(scriptG)
-call delete(scriptF)
-unlet scriptT scriptG scriptF
-
-Xcheck 2147450880
-
-
-"-------------------------------------------------------------------------------
-"
-" Test 58: v:exception and v:throwpoint for error/interrupt exceptions {{{1
-"
-" v:exception and v:throwpoint work also for error and interrupt
-" exceptions.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- function! T(line)
- if a:line == 2
- delfunction T " error (function in use) in line 2
- elseif a:line == 4
- let dummy = 0 " INTERRUPT1 - interrupt in line 4
- endif
- endfunction
-
- while 1
- try
- Xpath 1 " X: 1
- let caught = 0
- call T(2)
- catch /.*/
- let caught = 1
- if v:exception !~ 'Vim(delfunction):'
- Xpath 2 " X: 0
- endif
- if v:throwpoint !~ '\<T\>'
- Xpath 4 " X: 0
- endif
- if v:throwpoint !~ '\<2\>'
- Xpath 8 " X: 0
- endif
- finally
- Xpath 16 " X: 16
- if caught || $VIMNOERRTHROW
- Xpath 32 " X: 32
- endif
- if v:exception != ""
- Xpath 64 " X: 0
- endif
- if v:throwpoint != ""
- Xpath 128 " X: 0
- endif
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- Xpath 256 " X: 256
- if v:exception != ""
- Xpath 512 " X: 0
- endif
- if v:throwpoint != ""
- Xpath 1024 " X: 0
- endif
-
- while 1
- try
- Xpath 2048 " X: 2048
- let caught = 0
- call T(4)
- catch /.*/
- let caught = 1
- if v:exception != 'Vim:Interrupt'
- Xpath 4096 " X: 0
- endif
- if v:throwpoint !~ '\<T\>'
- Xpath 8192 " X: 0
- endif
- if v:throwpoint !~ '\<4\>'
- Xpath 16384 " X: 0
- endif
- finally
- Xpath 32768 " X: 32768
- if caught || $VIMNOINTTHROW
- Xpath 65536 " X: 65536
- endif
- if v:exception != ""
- Xpath 131072 " X: 0
- endif
- if v:throwpoint != ""
- Xpath 262144 " X: 0
- endif
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- Xpath 524288 " X: 524288
- if v:exception != ""
- Xpath 1048576 " X: 0
- endif
- if v:throwpoint != ""
- Xpath 2097152 " X: 0
- endif
-
-endif
-
-Xcheck 624945
-
-
-"-------------------------------------------------------------------------------
-"
-" Test 59: v:exception and v:throwpoint when discarding exceptions {{{1
-"
-" When a :catch clause is left by a ":break" etc or an error or
-" interrupt exception, v:exception and v:throwpoint are reset. They
-" are not affected by an exception that is discarded before being
-" caught.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- XloopINIT! 1 2
-
- let sfile = expand("<sfile>")
-
- function! LineNumber()
- return substitute(substitute(v:throwpoint, g:sfile, '', ""),
- \ '\D*\(\d*\).*', '\1', "")
- endfunction
-
- command! -nargs=1 SetLineNumber
- \ try | throw "line" | catch /.*/ | let <args> = LineNumber() | endtry
-
- " Check v:exception/v:throwpoint against second/fourth parameter if
- " specified, check for being empty else.
- function! CHECK(n, ...)
- XloopNEXT
- let exception = a:0 != 0 ? a:1 : "" " second parameter (optional)
- let emsg = a:0 != 0 ? a:2 : "" " third parameter (optional)
- let line = a:0 != 0 ? a:3 : 0 " fourth parameter (optional)
- let error = 0
- if emsg != ""
- " exception is the error number, emsg the English error message text
- if exception !~ '^E\d\+$'
- Xout "TODO: Add message number for:" emsg
- elseif v:lang == "C" || v:lang =~ '^[Ee]n'
- if exception == "E492" && emsg == "Not an editor command"
- let exception = '^Vim:' . exception . ': ' . emsg
- else
- let exception = '^Vim(\a\+):' . exception . ': ' . emsg
- endif
- else
- if exception == "E492"
- let exception = '^Vim:' . exception
- else
- let exception = '^Vim(\a\+):' . exception
- endif
- endif
- endif
- if exception == "" && v:exception != ""
- Xout a:n.": v:exception is set:" v:exception
- let error = 1
- elseif exception != "" && v:exception !~ exception
- Xout a:n.": v:exception (".v:exception.") does not match" exception
- let error = 1
- endif
- if line == 0 && v:throwpoint != ""
- Xout a:n.": v:throwpoint is set:" v:throwpoint
- let error = 1
- elseif line != 0 && v:throwpoint !~ '\<' . line . '\>'
- Xout a:n.": v:throwpoint (".v:throwpoint.") does not match" line
- let error = 1
- endif
- if !error
- Xloop 1 " X: 2097151
- endif
- endfunction
-
- while 1
- try
- throw "x1"
- catch /.*/
- break
- endtry
- endwhile
- call CHECK(1)
-
- while 1
- try
- throw "x2"
- catch /.*/
- break
- finally
- call CHECK(2)
- endtry
- break
- endwhile
- call CHECK(3)
-
- while 1
- try
- let errcaught = 0
- try
- try
- throw "x3"
- catch /.*/
- SetLineNumber line_before_error
- asdf
- endtry
- catch /.*/
- let errcaught = 1
- call CHECK(4, 'E492', "Not an editor command",
- \ line_before_error + 1)
- endtry
- finally
- if !errcaught && $VIMNOERRTHROW
- call CHECK(4)
- endif
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
- call CHECK(5)
-
- Xpath 2097152 " X: 2097152
-
- while 1
- try
- let intcaught = 0
- try
- try
- throw "x4"
- catch /.*/
- SetLineNumber two_lines_before_interrupt
- "INTERRUPT
- let dummy = 0
- endtry
- catch /.*/
- let intcaught = 1
- call CHECK(6, "Vim:Interrupt", '',
- \ two_lines_before_interrupt + 2)
- endtry
- finally
- if !intcaught && $VIMNOINTTHROW
- call CHECK(6)
- endif
- break " discard interrupt for $VIMNOINTTHROW
- endtry
- endwhile
- call CHECK(7)
-
- Xpath 4194304 " X: 4194304
-
- while 1
- try
- let errcaught = 0
- try
- try
-" if 1
- SetLineNumber line_before_throw
- throw "x5"
- " missing endif
- catch /.*/
- Xpath 8388608 " X: 0
- endtry
- catch /.*/
- let errcaught = 1
- call CHECK(8, 'E171', "Missing :endif", line_before_throw + 3)
- endtry
- finally
- if !errcaught && $VIMNOERRTHROW
- call CHECK(8)
- endif
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
- call CHECK(9)
-
- Xpath 16777216 " X: 16777216
-
- try
- while 1
- try
- throw "x6"
- finally
- break
- endtry
- break
- endwhile
- catch /.*/
- Xpath 33554432 " X: 0
- endtry
- call CHECK(10)
-
- try
- while 1
- try
- throw "x7"
- finally
- break
- endtry
- break
- endwhile
- catch /.*/
- Xpath 67108864 " X: 0
- finally
- call CHECK(11)
- endtry
- call CHECK(12)
-
- while 1
- try
- let errcaught = 0
- try
- try
- throw "x8"
- finally
- SetLineNumber line_before_error
- asdf
- endtry
- catch /.*/
- let errcaught = 1
- call CHECK(13, 'E492', "Not an editor command",
- \ line_before_error + 1)
- endtry
- finally
- if !errcaught && $VIMNOERRTHROW
- call CHECK(13)
- endif
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
- call CHECK(14)
-
- Xpath 134217728 " X: 134217728
-
- while 1
- try
- let intcaught = 0
- try
- try
- throw "x9"
- finally
- SetLineNumber two_lines_before_interrupt
- "INTERRUPT
- endtry
- catch /.*/
- let intcaught = 1
- call CHECK(15, "Vim:Interrupt", '',
- \ two_lines_before_interrupt + 2)
- endtry
- finally
- if !intcaught && $VIMNOINTTHROW
- call CHECK(15)
- endif
- break " discard interrupt for $VIMNOINTTHROW
- endtry
- endwhile
- call CHECK(16)
-
- Xpath 268435456 " X: 268435456
-
- while 1
- try
- let errcaught = 0
- try
- try
-" if 1
- SetLineNumber line_before_throw
- throw "x10"
- " missing endif
- finally
- call CHECK(17)
- endtry
- catch /.*/
- let errcaught = 1
- call CHECK(18, 'E171', "Missing :endif", line_before_throw + 3)
- endtry
- finally
- if !errcaught && $VIMNOERRTHROW
- call CHECK(18)
- endif
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
- call CHECK(19)
-
- Xpath 536870912 " X: 536870912
-
- while 1
- try
- let errcaught = 0
- try
- try
-" if 1
- SetLineNumber line_before_throw
- throw "x11"
- " missing endif
- endtry
- catch /.*/
- let errcaught = 1
- call CHECK(20, 'E171', "Missing :endif", line_before_throw + 3)
- endtry
- finally
- if !errcaught && $VIMNOERRTHROW
- call CHECK(20)
- endif
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
- call CHECK(21)
-
- Xpath 1073741824 " X: 1073741824
-
-endif
-
-Xcheck 2038431743
-
-
-"-------------------------------------------------------------------------------
-"
-" Test 60: (Re)throwing v:exception; :echoerr. {{{1
-"
-" A user exception can be rethrown after catching by throwing
-" v:exception. An error or interrupt exception cannot be rethrown
-" because Vim exceptions cannot be faked. A Vim exception using the
-" value of v:exception can, however, be triggered by the :echoerr
-" command.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-try
- try
- Xpath 1 " X: 1
- throw "oops"
- catch /oops/
- Xpath 2 " X: 2
- throw v:exception " rethrow user exception
- catch /.*/
- Xpath 4 " X: 0
- endtry
-catch /^oops$/ " catches rethrown user exception
- Xpath 8 " X: 8
-catch /.*/
- Xpath 16 " X: 0
-endtry
-
-function! F()
- try
- let caught = 0
- try
- Xpath 32 " X: 32
- write /n/o/n/w/r/i/t/a/b/l/e/_/f/i/l/e
- Xpath 64 " X: 0
- Xout "did_emsg was reset before executing " .
- \ "BufWritePost autocommands."
- catch /^Vim(write):/
- let caught = 1
- throw v:exception " throw error: cannot fake Vim exception
- catch /.*/
- Xpath 128 " X: 0
- finally
- Xpath 256 " X: 256
- if !caught && !$VIMNOERRTHROW
- Xpath 512 " X: 0
- endif
- endtry
- catch /^Vim(throw):/ " catches throw error
- let caught = caught + 1
- catch /.*/
- Xpath 1024 " X: 0
- finally
- Xpath 2048 " X: 2048
- if caught != 2
- if !caught && !$VIMNOERRTHROW
- Xpath 4096 " X: 0
- elseif caught
- Xpath 8192 " X: 0
- endif
- return | " discard error for $VIMNOERRTHROW
- endif
- endtry
-endfunction
-
-call F()
-delfunction F
-
-function! G()
- try
- let caught = 0
- try
- Xpath 16384 " X: 16384
- asdf
- catch /^Vim/ " catch error exception
- let caught = 1
- " Trigger Vim error exception with value specified after :echoerr
- let value = substitute(v:exception, '^Vim\((.*)\)\=:', '', "")
- echoerr value
- catch /.*/
- Xpath 32768 " X: 0
- finally
- Xpath 65536 " X: 65536
- if !caught
- if !$VIMNOERRTHROW
- Xpath 131072 " X: 0
- else
- let value = "Error"
- echoerr value
- endif
- endif
- endtry
- catch /^Vim(echoerr):/
- let caught = caught + 1
- if v:exception !~ value
- Xpath 262144 " X: 0
- endif
- catch /.*/
- Xpath 524288 " X: 0
- finally
- Xpath 1048576 " X: 1048576
- if caught != 2
- if !caught && !$VIMNOERRTHROW
- Xpath 2097152 " X: 0
- elseif caught
- Xpath 4194304 " X: 0
- endif
- return | " discard error for $VIMNOERRTHROW
- endif
- endtry
-endfunction
-
-call G()
-delfunction G
-
-unlet! value caught
-
-if ExtraVim()
- try
- let errcaught = 0
- try
- Xpath 8388608 " X: 8388608
- let intcaught = 0
- "INTERRUPT
- catch /^Vim:/ " catch interrupt exception
- let intcaught = 1
- " Trigger Vim error exception with value specified after :echoerr
- echoerr substitute(v:exception, '^Vim\((.*)\)\=:', '', "")
- catch /.*/
- Xpath 16777216 " X: 0
- finally
- Xpath 33554432 " X: 33554432
- if !intcaught
- if !$VIMNOINTTHROW
- Xpath 67108864 " X: 0
- else
- echoerr "Interrupt"
- endif
- endif
- endtry
- catch /^Vim(echoerr):/
- let errcaught = 1
- if v:exception !~ "Interrupt"
- Xpath 134217728 " X: 0
- endif
- finally
- Xpath 268435456 " X: 268435456
- if !errcaught && !$VIMNOERRTHROW
- Xpath 536870912 " X: 0
- endif
- endtry
-endif
-
-Xcheck 311511339
-
-
-"-------------------------------------------------------------------------------
-" Test 61: Catching interrupt exceptions {{{1
-"
-" When an interrupt occurs inside a :try/:endtry region, an
-" interrupt exception is thrown and can be caught. Its value is
-" "Vim:Interrupt". If the interrupt occurs after an error or a :throw
-" but before a matching :catch is reached, all following :catches of
-" that try block are ignored, but the interrupt exception can be
-" caught by the next surrounding try conditional. An interrupt is
-" ignored when there is a previous interrupt that has not been caught
-" or causes a :finally clause to be executed.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- while 1
- try
- try
- Xpath 1 " X: 1
- let caught = 0
- "INTERRUPT
- Xpath 2 " X: 0
- catch /^Vim:Interrupt$/
- let caught = 1
- finally
- Xpath 4 " X: 4
- if caught || $VIMNOINTTHROW
- Xpath 8 " X: 8
- endif
- endtry
- catch /.*/
- Xpath 16 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard interrupt for $VIMNOINTTHROW
- endtry
- endwhile
-
- while 1
- try
- try
- let caught = 0
- try
- Xpath 32 " X: 32
- asdf
- Xpath 64 " X: 0
- catch /do_not_catch/
- Xpath 128 " X: 0
- catch /.*/ "INTERRUPT - throw interrupt if !$VIMNOERRTHROW
- Xpath 256 " X: 0
- catch /.*/
- Xpath 512 " X: 0
- finally "INTERRUPT - throw interrupt if $VIMNOERRTHROW
- Xpath 1024 " X: 1024
- endtry
- catch /^Vim:Interrupt$/
- let caught = 1
- finally
- Xpath 2048 " X: 2048
- if caught || $VIMNOINTTHROW
- Xpath 4096 " X: 4096
- endif
- endtry
- catch /.*/
- Xpath 8192 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard interrupt for $VIMNOINTTHROW
- endtry
- endwhile
-
- while 1
- try
- try
- let caught = 0
- try
- Xpath 16384 " X: 16384
- throw "x"
- Xpath 32768 " X: 0
- catch /do_not_catch/
- Xpath 65536 " X: 0
- catch /x/ "INTERRUPT
- Xpath 131072 " X: 0
- catch /.*/
- Xpath 262144 " X: 0
- endtry
- catch /^Vim:Interrupt$/
- let caught = 1
- finally
- Xpath 524288 " X: 524288
- if caught || $VIMNOINTTHROW
- Xpath 1048576 " X: 1048576
- endif
- endtry
- catch /.*/
- Xpath 2097152 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard interrupt for $VIMNOINTTHROW
- endtry
- endwhile
-
- while 1
- try
- let caught = 0
- try
- Xpath 4194304 " X: 4194304
- "INTERRUPT
- Xpath 8388608 " X: 0
- catch /do_not_catch/ "INTERRUPT
- Xpath 16777216 " X: 0
- catch /^Vim:Interrupt$/
- let caught = 1
- finally
- Xpath 33554432 " X: 33554432
- if caught || $VIMNOINTTHROW
- Xpath 67108864 " X: 67108864
- endif
- endtry
- catch /.*/
- Xpath 134217728 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard interrupt for $VIMNOINTTHROW
- endtry
- endwhile
-
- Xpath 268435456 " X: 268435456
-
-endif
-
-Xcheck 374889517
-
-
-"-------------------------------------------------------------------------------
-" Test 62: Catching error exceptions {{{1
-"
-" An error inside a :try/:endtry region is converted to an exception
-" and can be caught. The error exception has a "Vim(cmdname):" prefix
-" where cmdname is the name of the failing command, or a "Vim:" prefix
-" if no command name is known. The "Vim" prefixes cannot be faked.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! MSG(enr, emsg)
- let english = v:lang == "C" || v:lang =~ '^[Ee]n'
- if a:enr == ""
- Xout "TODO: Add message number for:" a:emsg
- let v:errmsg = ":" . v:errmsg
- endif
- let match = 1
- if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg)
- let match = 0
- if v:errmsg == ""
- Xout "Message missing."
- else
- let v:errmsg = escape(v:errmsg, '"')
- Xout "Unexpected message:" v:errmsg
- endif
- endif
- return match
-endfunction
-
-while 1
- try
- try
- let caught = 0
- unlet novar
- catch /^Vim(unlet):/
- let caught = 1
- let v:errmsg = substitute(v:exception, '^Vim(unlet):', '', "")
- finally
- Xpath 1 " X: 1
- if !caught && !$VIMNOERRTHROW
- Xpath 2 " X: 0
- endif
- if !MSG('E108', "No such variable")
- Xpath 4 " X: 0
- endif
- endtry
- catch /.*/
- Xpath 8 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-while 1
- try
- try
- let caught = 0
- throw novar " error in :throw
- catch /^Vim(throw):/
- let caught = 1
- let v:errmsg = substitute(v:exception, '^Vim(throw):', '', "")
- finally
- Xpath 16 " X: 16
- if !caught && !$VIMNOERRTHROW
- Xpath 32 " X: 0
- endif
- if caught ? !MSG('E121', "Undefined variable")
- \ : !MSG('E15', "Invalid expression")
- Xpath 64 " X: 0
- endif
- endtry
- catch /.*/
- Xpath 128 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-while 1
- try
- try
- let caught = 0
- throw "Vim:faked" " error: cannot fake Vim exception
- catch /^Vim(throw):/
- let caught = 1
- let v:errmsg = substitute(v:exception, '^Vim(throw):', '', "")
- finally
- Xpath 256 " X: 256
- if !caught && !$VIMNOERRTHROW
- Xpath 512 " X: 0
- endif
- if !MSG('E608', "Cannot :throw exceptions with 'Vim' prefix")
- Xpath 1024 " X: 0
- endif
- endtry
- catch /.*/
- Xpath 2048 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-function! F()
- while 1
- " Missing :endwhile
-endfunction
-
-while 1
- try
- try
- let caught = 0
- call F()
- catch /^Vim(endfunction):/
- let caught = 1
- let v:errmsg = substitute(v:exception, '^Vim(endfunction):', '', "")
- finally
- Xpath 4096 " X: 4096
- if !caught && !$VIMNOERRTHROW
- Xpath 8192 " X: 0
- endif
- if !MSG('E170', "Missing :endwhile")
- Xpath 16384 " X: 0
- endif
- endtry
- catch /.*/
- Xpath 32768 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-while 1
- try
- try
- let caught = 0
- ExecAsScript F
- catch /^Vim:/
- let caught = 1
- let v:errmsg = substitute(v:exception, '^Vim:', '', "")
- finally
- Xpath 65536 " X: 65536
- if !caught && !$VIMNOERRTHROW
- Xpath 131072 " X: 0
- endif
- if !MSG('E170', "Missing :endwhile")
- Xpath 262144 " X: 0
- endif
- endtry
- catch /.*/
- Xpath 524288 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-function! G()
- call G()
-endfunction
-
-while 1
- try
- let mfd_save = &mfd
- set mfd=3
- try
- let caught = 0
- call G()
- catch /^Vim(call):/
- let caught = 1
- let v:errmsg = substitute(v:exception, '^Vim(call):', '', "")
- finally
- Xpath 1048576 " X: 1048576
- if !caught && !$VIMNOERRTHROW
- Xpath 2097152 " X: 0
- endif
- if !MSG('E132', "Function call depth is higher than 'maxfuncdepth'")
- Xpath 4194304 " X: 0
- endif
- endtry
- catch /.*/
- Xpath 8388608 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- let &mfd = mfd_save
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-function! H()
- return H()
-endfunction
-
-while 1
- try
- let mfd_save = &mfd
- set mfd=3
- try
- let caught = 0
- call H()
- catch /^Vim(return):/
- let caught = 1
- let v:errmsg = substitute(v:exception, '^Vim(return):', '', "")
- finally
- Xpath 16777216 " X: 16777216
- if !caught && !$VIMNOERRTHROW
- Xpath 33554432 " X: 0
- endif
- if !MSG('E132', "Function call depth is higher than 'maxfuncdepth'")
- Xpath 67108864 " X: 0
- endif
- endtry
- catch /.*/
- Xpath 134217728 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- let &mfd = mfd_save
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-unlet! caught mfd_save
-delfunction F
-delfunction G
-delfunction H
-Xpath 268435456 " X: 268435456
-
-Xcheck 286331153
-
-" Leave MSG() for the next test.
-
-
-"-------------------------------------------------------------------------------
-" Test 63: Suppressing error exceptions by :silent!. {{{1
-"
-" A :silent! command inside a :try/:endtry region suppresses the
-" conversion of errors to an exception and the immediate abortion on
-" error. When the commands executed by the :silent! themselves open
-" a new :try/:endtry region, conversion of errors to exception and
-" immediate abortion is switched on again - until the next :silent!
-" etc. The :silent! has the effect of setting v:errmsg to the error
-" message text (without displaying it) and continuing with the next
-" script line.
-"
-" When a command triggering autocommands is executed by :silent!
-" inside a :try/:endtry, the autocommand execution is not suppressed
-" on error.
-"
-" This test reuses the function MSG() from the previous test.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-XloopINIT! 1 4
-
-let taken = ""
-
-function! S(n) abort
- XloopNEXT
- let g:taken = g:taken . "E" . a:n
- let v:errmsg = ""
- exec "asdf" . a:n
-
- " Check that ":silent!" continues:
- Xloop 1
-
- " Check that ":silent!" sets "v:errmsg":
- if MSG('E492', "Not an editor command")
- Xloop 2
- endif
-endfunction
-
-function! Foo()
- while 1
- try
- try
- let caught = 0
- " This is not silent:
- call S(3) " X: 0 * 16
- catch /^Vim:/
- let caught = 1
- let errmsg3 = substitute(v:exception, '^Vim:', '', "")
- silent! call S(4) " X: 3 * 64
- finally
- if !caught
- let errmsg3 = v:errmsg
- " Do call S(4) here if not executed in :catch.
- silent! call S(4)
- endif
- Xpath 1048576 " X: 1048576
- if !caught && !$VIMNOERRTHROW
- Xpath 2097152 " X: 0
- endif
- let v:errmsg = errmsg3
- if !MSG('E492', "Not an editor command")
- Xpath 4194304 " X: 0
- endif
- silent! call S(5) " X: 3 * 256
- " Break out of try conditionals that cover ":silent!". This also
- " discards the aborting error when $VIMNOERRTHROW is non-zero.
- break
- endtry
- catch /.*/
- Xpath 8388608 " X: 0
- Xout v:exception "in" v:throwpoint
- endtry
- endwhile
- " This is a double ":silent!" (see caller).
- silent! call S(6) " X: 3 * 1024
-endfunction
-
-function! Bar()
- try
- silent! call S(2) " X: 3 * 4
- " X: 3 * 4096
- silent! execute "call Foo() | call S(7)"
- silent! call S(8) " X: 3 * 16384
- endtry " normal end of try cond that covers ":silent!"
- " This has a ":silent!" from the caller:
- call S(9) " X: 3 * 65536
-endfunction
-
-silent! call S(1) " X: 3 * 1
-silent! call Bar()
-silent! call S(10) " X: 3 * 262144
-
-let expected = "E1E2E3E4E5E6E7E8E9E10"
-if taken != expected
- Xpath 16777216 " X: 0
- Xout "'taken' is" taken "instead of" expected
-endif
-
-augroup TMP
- autocmd BufWritePost * Xpath 33554432 " X: 33554432
-augroup END
-
-Xpath 67108864 " X: 67108864
-write /i/m/p/o/s/s/i/b/l/e
-Xpath 134217728 " X: 134217728
-
-autocmd! TMP
-unlet! caught errmsg3 taken expected
-delfunction S
-delfunction Foo
-delfunction Bar
-delfunction MSG
-
-Xcheck 236978127
-
-
-"-------------------------------------------------------------------------------
-" Test 64: Error exceptions after error, interrupt or :throw {{{1
-"
-" When an error occurs after an interrupt or a :throw but before
-" a matching :catch is reached, all following :catches of that try
-" block are ignored, but the error exception can be caught by the next
-" surrounding try conditional. Any previous error exception is
-" discarded. An error is ignored when there is a previous error that
-" has not been caught.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- while 1
- try
- try
- Xpath 1 " X: 1
- let caught = 0
- while 1
-" if 1
- " Missing :endif
- endwhile " throw error exception
- catch /^Vim(/
- let caught = 1
- finally
- Xpath 2 " X: 2
- if caught || $VIMNOERRTHROW
- Xpath 4 " X: 4
- endif
- endtry
- catch /.*/
- Xpath 8 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- while 1
- try
- try
- Xpath 16 " X: 16
- let caught = 0
- try
-" if 1
- " Missing :endif
- catch /.*/ " throw error exception
- Xpath 32 " X: 0
- catch /.*/
- Xpath 64 " X: 0
- endtry
- catch /^Vim(/
- let caught = 1
- finally
- Xpath 128 " X: 128
- if caught || $VIMNOERRTHROW
- Xpath 256 " X: 256
- endif
- endtry
- catch /.*/
- Xpath 512 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- while 1
- try
- try
- let caught = 0
- try
- Xpath 1024 " X: 1024
- "INTERRUPT
- catch /do_not_catch/
- Xpath 2048 " X: 0
-" if 1
- " Missing :endif
- catch /.*/ " throw error exception
- Xpath 4096 " X: 0
- catch /.*/
- Xpath 8192 " X: 0
- endtry
- catch /^Vim(/
- let caught = 1
- finally
- Xpath 16384 " X: 16384
- if caught || $VIMNOERRTHROW
- Xpath 32768 " X: 32768
- endif
- endtry
- catch /.*/
- Xpath 65536 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- while 1
- try
- try
- let caught = 0
- try
- Xpath 131072 " X: 131072
- throw "x"
- catch /do_not_catch/
- Xpath 262144 " X: 0
-" if 1
- " Missing :endif
- catch /x/ " throw error exception
- Xpath 524288 " X: 0
- catch /.*/
- Xpath 1048576 " X: 0
- endtry
- catch /^Vim(/
- let caught = 1
- finally
- Xpath 2097152 " X: 2097152
- if caught || $VIMNOERRTHROW
- Xpath 4194304 " X: 4194304
- endif
- endtry
- catch /.*/
- Xpath 8388608 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- while 1
- try
- try
- let caught = 0
- Xpath 16777216 " X: 16777216
-" endif " :endif without :if; throw error exception
-" if 1
- " Missing :endif
- catch /do_not_catch/ " ignore new error
- Xpath 33554432 " X: 0
- catch /^Vim(endif):/
- let caught = 1
- catch /^Vim(/
- Xpath 67108864 " X: 0
- finally
- Xpath 134217728 " X: 134217728
- if caught || $VIMNOERRTHROW
- Xpath 268435456 " X: 268435456
- endif
- endtry
- catch /.*/
- Xpath 536870912 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- Xpath 1073741824 " X: 1073741824
-
-endif
-
-Xcheck 1499645335
-
-
-"-------------------------------------------------------------------------------
-" Test 65: Errors in the /pattern/ argument of a :catch {{{1
-"
-" On an error in the /pattern/ argument of a :catch, the :catch does
-" not match. Any following :catches of the same :try/:endtry don't
-" match either. Finally clauses are executed.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! MSG(enr, emsg)
- let english = v:lang == "C" || v:lang =~ '^[Ee]n'
- if a:enr == ""
- Xout "TODO: Add message number for:" a:emsg
- let v:errmsg = ":" . v:errmsg
- endif
- let match = 1
- if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg)
- let match = 0
- if v:errmsg == ""
- Xout "Message missing."
- else
- let v:errmsg = escape(v:errmsg, '"')
- Xout "Unexpected message:" v:errmsg
- endif
- endif
- return match
-endfunction
-
-try
- try
- Xpath 1 " X: 1
- throw "oops"
- catch /^oops$/
- Xpath 2 " X: 2
- catch /\)/ " not checked; exception has already been caught
- Xpath 4 " X: 0
- endtry
- Xpath 8 " X: 8
-catch /.*/
- Xpath 16 " X: 0
- Xout v:exception "in" v:throwpoint
-endtry
-
-function! F()
- try
- let caught = 0
- try
- try
- Xpath 32 " X: 32
- throw "ab"
- catch /abc/ " does not catch
- Xpath 64 " X: 0
- catch /\)/ " error; discards exception
- Xpath 128 " X: 0
- catch /.*/ " not checked
- Xpath 256 " X: 0
- finally
- Xpath 512 " X: 512
- endtry
- Xpath 1024 " X: 0
- catch /^ab$/ " checked, but original exception is discarded
- Xpath 2048 " X: 0
- catch /^Vim(catch):/
- let caught = 1
- let v:errmsg = substitute(v:exception, '^Vim(catch):', '', "")
- finally
- Xpath 4096 " X: 4096
- if !caught && !$VIMNOERRTHROW
- Xpath 8192 " X: 0
- endif
- if !MSG('E475', "Invalid argument")
- Xpath 16384 " X: 0
- endif
- if !caught
- return | " discard error
- endif
- endtry
- catch /.*/
- Xpath 32768 " X: 0
- Xout v:exception "in" v:throwpoint
- endtry
-endfunction
-
-call F()
-Xpath 65536 " X: 65536
-
-delfunction MSG
-delfunction F
-unlet! caught
-
-Xcheck 70187
-
-
-"-------------------------------------------------------------------------------
-" Test 66: Stop range :call on error, interrupt, or :throw {{{1
-"
-" When a function which is multiply called for a range since it
-" doesn't handle the range itself has an error in a command
-" dynamically enclosed by :try/:endtry or gets an interrupt or
-" executes a :throw, no more calls for the remaining lines in the
-" range are made. On an error in a command not dynamically enclosed
-" by :try/:endtry, the function is executed again for the remaining
-" lines in the range.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- let file = tempname()
- exec "edit" file
-
- insert
-line 1
-line 2
-line 3
-.
-
- XloopINIT! 1 2
-
- let taken = ""
- let expected = "G1EF1E(1)F1E(2)F1E(3)G2EF2E(1)G3IF3I(1)G4TF4T(1)G5AF5A(1)"
-
- function! F(reason, n) abort
- let g:taken = g:taken . "F" . a:n .
- \ substitute(a:reason, '\(\l\).*', '\u\1', "") .
- \ "(" . line(".") . ")"
-
- if a:reason == "error"
- asdf
- elseif a:reason == "interrupt"
- "INTERRUPT
- let dummy = 0
- elseif a:reason == "throw"
- throw "xyz"
- elseif a:reason == "aborting error"
- XloopNEXT
- if g:taken != g:expected
- Xloop 1 " X: 0
- Xout "'taken' is" g:taken "instead of" g:expected
- endif
- try
- bwipeout!
- call delete(file)
- asdf
- endtry
- endif
- endfunction
-
- function! G(reason, n)
- let g:taken = g:taken . "G" . a:n .
- \ substitute(a:reason, '\(\l\).*', '\u\1', "")
- 1,3call F(a:reason, a:n)
- endfunction
-
- Xpath 8 " X: 8
- call G("error", 1)
- try
- Xpath 16 " X: 16
- try
- call G("error", 2)
- Xpath 32 " X: 0
- finally
- Xpath 64 " X: 64
- try
- call G("interrupt", 3)
- Xpath 128 " X: 0
- finally
- Xpath 256 " X: 256
- try
- call G("throw", 4)
- Xpath 512 " X: 0
- endtry
- endtry
- endtry
- catch /xyz/
- Xpath 1024 " X: 1024
- catch /.*/
- Xpath 2048 " X: 0
- Xout v:exception "in" ExtraVimThrowpoint()
- endtry
- Xpath 4096 " X: 4096
- call G("aborting error", 5)
- Xpath 8192 " X: 0
- Xout "'taken' is" taken "instead of" expected
-
-endif
-
-Xcheck 5464
-
-
-"-------------------------------------------------------------------------------
-" Test 67: :throw across :call command {{{1
-"
-" On a call command, an exception might be thrown when evaluating the
-" function name, during evaluation of the arguments, or when the
-" function is being executed. The exception can be caught by the
-" caller.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! THROW(x, n)
- if a:n == 1
- Xpath 1 " X: 1
- elseif a:n == 2
- Xpath 2 " X: 2
- elseif a:n == 3
- Xpath 4 " X: 4
- endif
- throw a:x
-endfunction
-
-function! NAME(x, n)
- if a:n == 1
- Xpath 8 " X: 0
- elseif a:n == 2
- Xpath 16 " X: 16
- elseif a:n == 3
- Xpath 32 " X: 32
- elseif a:n == 4
- Xpath 64 " X: 64
- endif
- return a:x
-endfunction
-
-function! ARG(x, n)
- if a:n == 1
- Xpath 128 " X: 0
- elseif a:n == 2
- Xpath 256 " X: 0
- elseif a:n == 3
- Xpath 512 " X: 512
- elseif a:n == 4
- Xpath 1024 " X: 1024
- endif
- return a:x
-endfunction
-
-function! F(x, n)
- if a:n == 2
- Xpath 2048 " X: 0
- elseif a:n == 4
- Xpath 4096 " X: 4096
- endif
-endfunction
-
-while 1
- try
- let error = 0
- let v:errmsg = ""
-
- while 1
- try
- Xpath 8192 " X: 8192
- call {NAME(THROW("name", 1), 1)}(ARG(4711, 1), 1)
- Xpath 16384 " X: 0
- catch /^name$/
- Xpath 32768 " X: 32768
- catch /.*/
- let error = 1
- Xout "1:" v:exception "in" v:throwpoint
- finally
- if !error && $VIMNOERRTHROW && v:errmsg != ""
- let error = 1
- Xout "1:" v:errmsg
- endif
- if error
- Xpath 65536 " X: 0
- endif
- let error = 0
- let v:errmsg = ""
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- while 1
- try
- Xpath 131072 " X: 131072
- call {NAME("F", 2)}(ARG(THROW("arg", 2), 2), 2)
- Xpath 262144 " X: 0
- catch /^arg$/
- Xpath 524288 " X: 524288
- catch /.*/
- let error = 1
- Xout "2:" v:exception "in" v:throwpoint
- finally
- if !error && $VIMNOERRTHROW && v:errmsg != ""
- let error = 1
- Xout "2:" v:errmsg
- endif
- if error
- Xpath 1048576 " X: 0
- endif
- let error = 0
- let v:errmsg = ""
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- while 1
- try
- Xpath 2097152 " X: 2097152
- call {NAME("THROW", 3)}(ARG("call", 3), 3)
- Xpath 4194304 " X: 0
- catch /^call$/
- Xpath 8388608 " X: 8388608
- catch /^0$/ " default return value
- Xpath 16777216 " X: 0
- Xout "3:" v:throwpoint
- catch /.*/
- let error = 1
- Xout "3:" v:exception "in" v:throwpoint
- finally
- if !error && $VIMNOERRTHROW && v:errmsg != ""
- let error = 1
- Xout "3:" v:errmsg
- endif
- if error
- Xpath 33554432 " X: 0
- endif
- let error = 0
- let v:errmsg = ""
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- while 1
- try
- Xpath 67108864 " X: 67108864
- call {NAME("F", 4)}(ARG(4711, 4), 4)
- Xpath 134217728 " X: 134217728
- catch /.*/
- let error = 1
- Xout "4:" v:exception "in" v:throwpoint
- finally
- if !error && $VIMNOERRTHROW && v:errmsg != ""
- let error = 1
- Xout "4:" v:errmsg
- endif
- if error
- Xpath 268435456 " X: 0
- endif
- let error = 0
- let v:errmsg = ""
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- catch /^0$/ " default return value
- Xpath 536870912 " X: 0
- Xout v:throwpoint
- catch /.*/
- let error = 1
- Xout v:exception "in" v:throwpoint
- finally
- if !error && $VIMNOERRTHROW && v:errmsg != ""
- let error = 1
- Xout v:errmsg
- endif
- if error
- Xpath 1073741824 " X: 0
- endif
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-unlet error
-delfunction F
-
-Xcheck 212514423
-
-" Leave THROW(), NAME(), and ARG() for the next test.
-
-
-"-------------------------------------------------------------------------------
-" Test 68: :throw across function calls in expressions {{{1
-"
-" On a function call within an expression, an exception might be
-" thrown when evaluating the function name, during evaluation of the
-" arguments, or when the function is being executed. The exception
-" can be caught by the caller.
-"
-" This test reuses the functions THROW(), NAME(), and ARG() from the
-" previous test.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! F(x, n)
- if a:n == 2
- Xpath 2048 " X: 0
- elseif a:n == 4
- Xpath 4096 " X: 4096
- endif
- return a:x
-endfunction
-
-unlet! var1 var2 var3 var4
-
-while 1
- try
- let error = 0
- let v:errmsg = ""
-
- while 1
- try
- Xpath 8192 " X: 8192
- let var1 = {NAME(THROW("name", 1), 1)}(ARG(4711, 1), 1)
- Xpath 16384 " X: 0
- catch /^name$/
- Xpath 32768 " X: 32768
- catch /.*/
- let error = 1
- Xout "1:" v:exception "in" v:throwpoint
- finally
- if !error && $VIMNOERRTHROW && v:errmsg != ""
- let error = 1
- Xout "1:" v:errmsg
- endif
- if error
- Xpath 65536 " X: 0
- endif
- let error = 0
- let v:errmsg = ""
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- while 1
- try
- Xpath 131072 " X: 131072
- let var2 = {NAME("F", 2)}(ARG(THROW("arg", 2), 2), 2)
- Xpath 262144 " X: 0
- catch /^arg$/
- Xpath 524288 " X: 524288
- catch /.*/
- let error = 1
- Xout "2:" v:exception "in" v:throwpoint
- finally
- if !error && $VIMNOERRTHROW && v:errmsg != ""
- let error = 1
- Xout "2:" v:errmsg
- endif
- if error
- Xpath 1048576 " X: 0
- endif
- let error = 0
- let v:errmsg = ""
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- while 1
- try
- Xpath 2097152 " X: 2097152
- let var3 = {NAME("THROW", 3)}(ARG("call", 3), 3)
- Xpath 4194304 " X: 0
- catch /^call$/
- Xpath 8388608 " X: 8388608
- catch /^0$/ " default return value
- Xpath 16777216 " X: 0
- Xout "3:" v:throwpoint
- catch /.*/
- let error = 1
- Xout "3:" v:exception "in" v:throwpoint
- finally
- if !error && $VIMNOERRTHROW && v:errmsg != ""
- let error = 1
- Xout "3:" v:errmsg
- endif
- if error
- Xpath 33554432 " X: 0
- endif
- let error = 0
- let v:errmsg = ""
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- while 1
- try
- Xpath 67108864 " X: 67108864
- let var4 = {NAME("F", 4)}(ARG(4711, 4), 4)
- Xpath 134217728 " X: 134217728
- catch /.*/
- let error = 1
- Xout "4:" v:exception "in" v:throwpoint
- finally
- if !error && $VIMNOERRTHROW && v:errmsg != ""
- let error = 1
- Xout "4:" v:errmsg
- endif
- if error
- Xpath 268435456 " X: 0
- endif
- let error = 0
- let v:errmsg = ""
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- catch /^0$/ " default return value
- Xpath 536870912 " X: 0
- Xout v:throwpoint
- catch /.*/
- let error = 1
- Xout v:exception "in" v:throwpoint
- finally
- if !error && $VIMNOERRTHROW && v:errmsg != ""
- let error = 1
- Xout v:errmsg
- endif
- if error
- Xpath 1073741824 " X: 0
- endif
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-if exists("var1") || exists("var2") || exists("var3") ||
- \ !exists("var4") || var4 != 4711
- " The Xpath command does not accept 2^31 (negative); add explicitly:
- let Xpath = Xpath + 2147483648 " X: 0
- if exists("var1")
- Xout "var1 =" var1
- endif
- if exists("var2")
- Xout "var2 =" var2
- endif
- if exists("var3")
- Xout "var3 =" var3
- endif
- if !exists("var4")
- Xout "var4 unset"
- elseif var4 != 4711
- Xout "var4 =" var4
- endif
-endif
-
-unlet! error var1 var2 var3 var4
-delfunction THROW
-delfunction NAME
-delfunction ARG
-delfunction F
-
-Xcheck 212514423
-
-" Tests 69 to 75 were moved to test_trycatch.vim
-let Xtest = 76
-
-
-"-------------------------------------------------------------------------------
-" Test 76: Errors, interrupts, :throw during expression evaluation {{{1
-"
-" When a function call made during expression evaluation is aborted
-" due to an error inside a :try/:endtry region or due to an interrupt
-" or a :throw, the expression evaluation is aborted as well. No
-" message is displayed for the cancelled expression evaluation. On an
-" error not inside :try/:endtry, the expression evaluation continues.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- let taken = ""
-
- function! ERR(n)
- let g:taken = g:taken . "E" . a:n
- asdf
- endfunction
-
- function! ERRabort(n) abort
- let g:taken = g:taken . "A" . a:n
- asdf
- endfunction " returns -1; may cause follow-up msg for illegal var/func name
-
- function! WRAP(n, arg)
- let g:taken = g:taken . "W" . a:n
- let g:saved_errmsg = v:errmsg
- return arg
- endfunction
-
- function! INT(n)
- let g:taken = g:taken . "I" . a:n
- "INTERRUPT9
- let dummy = 0
- endfunction
-
- function! THR(n)
- let g:taken = g:taken . "T" . a:n
- throw "should not be caught"
- endfunction
-
- function! CONT(n)
- let g:taken = g:taken . "C" . a:n
- endfunction
-
- function! MSG(n)
- let g:taken = g:taken . "M" . a:n
- let errmsg = (a:n >= 37 && a:n <= 44) ? g:saved_errmsg : v:errmsg
- let msgptn = (a:n >= 10 && a:n <= 27) ? "^$" : "asdf"
- if errmsg !~ msgptn
- let g:taken = g:taken . "x"
- Xout "Expr" a:n.": Unexpected message:" v:errmsg
- endif
- let v:errmsg = ""
- let g:saved_errmsg = ""
- endfunction
-
- let v:errmsg = ""
-
- try
- let t = 1
- XloopINIT 1 2
- while t <= 9
- Xloop 1 " X: 511
- try
- if t == 1
- let v{ERR(t) + CONT(t)} = 0
- elseif t == 2
- let v{ERR(t) + CONT(t)}
- elseif t == 3
- let var = exists('v{ERR(t) + CONT(t)}')
- elseif t == 4
- unlet v{ERR(t) + CONT(t)}
- elseif t == 5
- function F{ERR(t) + CONT(t)}()
- endfunction
- elseif t == 6
- function F{ERR(t) + CONT(t)}
- elseif t == 7
- let var = exists('*F{ERR(t) + CONT(t)}')
- elseif t == 8
- delfunction F{ERR(t) + CONT(t)}
- elseif t == 9
- let var = ERR(t) + CONT(t)
- endif
- catch /asdf/
- " v:errmsg is not set when the error message is converted to an
- " exception. Set it to the original error message.
- let v:errmsg = substitute(v:exception, '^Vim:', '', "")
- catch /^Vim\((\a\+)\)\=:/
- " An error exception has been thrown after the original error.
- let v:errmsg = ""
- finally
- call MSG(t)
- let t = t + 1
- XloopNEXT
- continue " discard an aborting error
- endtry
- endwhile
- catch /.*/
- Xpath 512 " X: 0
- Xout v:exception "in" ExtraVimThrowpoint()
- endtry
-
- try
- let t = 10
- XloopINIT 1024 2
- while t <= 18
- Xloop 1 " X: 1024 * 511
- try
- if t == 10
- let v{INT(t) + CONT(t)} = 0
- elseif t == 11
- let v{INT(t) + CONT(t)}
- elseif t == 12
- let var = exists('v{INT(t) + CONT(t)}')
- elseif t == 13
- unlet v{INT(t) + CONT(t)}
- elseif t == 14
- function F{INT(t) + CONT(t)}()
- endfunction
- elseif t == 15
- function F{INT(t) + CONT(t)}
- elseif t == 16
- let var = exists('*F{INT(t) + CONT(t)}')
- elseif t == 17
- delfunction F{INT(t) + CONT(t)}
- elseif t == 18
- let var = INT(t) + CONT(t)
- endif
- catch /^Vim\((\a\+)\)\=:\(Interrupt\)\@!/
- " An error exception has been triggered after the interrupt.
- let v:errmsg = substitute(v:exception,
- \ '^Vim\((\a\+)\)\=:', '', "")
- finally
- call MSG(t)
- let t = t + 1
- XloopNEXT
- continue " discard interrupt
- endtry
- endwhile
- catch /.*/
- Xpath 524288 " X: 0
- Xout v:exception "in" ExtraVimThrowpoint()
- endtry
-
- try
- let t = 19
- XloopINIT 1048576 2
- while t <= 27
- Xloop 1 " X: 1048576 * 511
- try
- if t == 19
- let v{THR(t) + CONT(t)} = 0
- elseif t == 20
- let v{THR(t) + CONT(t)}
- elseif t == 21
- let var = exists('v{THR(t) + CONT(t)}')
- elseif t == 22
- unlet v{THR(t) + CONT(t)}
- elseif t == 23
- function F{THR(t) + CONT(t)}()
- endfunction
- elseif t == 24
- function F{THR(t) + CONT(t)}
- elseif t == 25
- let var = exists('*F{THR(t) + CONT(t)}')
- elseif t == 26
- delfunction F{THR(t) + CONT(t)}
- elseif t == 27
- let var = THR(t) + CONT(t)
- endif
- catch /^Vim\((\a\+)\)\=:/
- " An error exception has been triggered after the :throw.
- let v:errmsg = substitute(v:exception,
- \ '^Vim\((\a\+)\)\=:', '', "")
- finally
- call MSG(t)
- let t = t + 1
- XloopNEXT
- continue " discard exception
- endtry
- endwhile
- catch /.*/
- Xpath 536870912 " X: 0
- Xout v:exception "in" ExtraVimThrowpoint()
- endtry
-
- let v{ERR(28) + CONT(28)} = 0
- call MSG(28)
- let v{ERR(29) + CONT(29)}
- call MSG(29)
- let var = exists('v{ERR(30) + CONT(30)}')
- call MSG(30)
- unlet v{ERR(31) + CONT(31)}
- call MSG(31)
- function F{ERR(32) + CONT(32)}()
- endfunction
- call MSG(32)
- function F{ERR(33) + CONT(33)}
- call MSG(33)
- let var = exists('*F{ERR(34) + CONT(34)}')
- call MSG(34)
- delfunction F{ERR(35) + CONT(35)}
- call MSG(35)
- let var = ERR(36) + CONT(36)
- call MSG(36)
-
- let saved_errmsg = ""
-
- let v{WRAP(37, ERRabort(37)) + CONT(37)} = 0
- call MSG(37)
- let v{WRAP(38, ERRabort(38)) + CONT(38)}
- call MSG(38)
- let var = exists('v{WRAP(39, ERRabort(39)) + CONT(39)}')
- call MSG(39)
- unlet v{WRAP(40, ERRabort(40)) + CONT(40)}
- call MSG(40)
- function F{WRAP(41, ERRabort(41)) + CONT(41)}()
- endfunction
- call MSG(41)
- function F{WRAP(42, ERRabort(42)) + CONT(42)}
- call MSG(42)
- let var = exists('*F{WRAP(43, ERRabort(43)) + CONT(43)}')
- call MSG(43)
- delfunction F{WRAP(44, ERRabort(44)) + CONT(44)}
- call MSG(44)
- let var = ERRabort(45) + CONT(45)
- call MSG(45)
-
- Xpath 1073741824 " X: 1073741824
-
- let expected = ""
- \ . "E1M1E2M2E3M3E4M4E5M5E6M6E7M7E8M8E9M9"
- \ . "I10M10I11M11I12M12I13M13I14M14I15M15I16M16I17M17I18M18"
- \ . "T19M19T20M20T21M21T22M22T23M23T24M24T25M25T26M26T27M27"
- \ . "E28C28M28E29C29M29E30C30M30E31C31M31E32C32M32E33C33M33"
- \ . "E34C34M34E35C35M35E36C36M36"
- \ . "A37W37C37M37A38W38C38M38A39W39C39M39A40W40C40M40A41W41C41M41"
- \ . "A42W42C42M42A43W43C43M43A44W44C44M44A45C45M45"
-
- if taken != expected
- " The Xpath command does not accept 2^31 (negative); display explicitly:
- exec "!echo 2147483648 >>" . g:ExtraVimResult
- " X: 0
- Xout "'taken' is" taken "instead of" expected
- if substitute(taken,
- \ '\(.*\)E3C3M3x\(.*\)E30C30M30x\(.*\)A39C39M39x\(.*\)',
- \ '\1E3M3\2E30C30M30\3A39C39M39\4',
- \ "") == expected
- Xout "Is ++emsg_skip for var with expr_start non-NULL"
- \ "in f_exists ok?"
- endif
- endif
-
- unlet! v var saved_errmsg taken expected
- call delete(WA_t5)
- call delete(WA_t14)
- call delete(WA_t23)
- unlet! WA_t5 WA_t14 WA_t23
- delfunction WA_t5
- delfunction WA_t14
- delfunction WA_t23
-
-endif
-
-Xcheck 1610087935
-
-
-"-------------------------------------------------------------------------------
-" Test 77: Errors, interrupts, :throw in name{brace-expression} {{{1
-"
-" When a function call made during evaluation of an expression in
-" braces as part of a function name after ":function" is aborted due
-" to an error inside a :try/:endtry region or due to an interrupt or
-" a :throw, the expression evaluation is aborted as well, and the
-" function definition is ignored, skipping all commands to the
-" ":endfunction". On an error not inside :try/:endtry, the expression
-" evaluation continues and the function gets defined, and can be
-" called and deleted.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-XloopINIT 1 4
-
-function! ERR() abort
- Xloop 1 " X: 1 + 4 + 16 + 64
- asdf
-endfunction " returns -1
-
-function! OK()
- Xloop 2 " X: 2 * (1 + 4 + 16)
- let v:errmsg = ""
- return 0
-endfunction
-
-let v:errmsg = ""
-
-Xpath 4096 " X: 4096
-function! F{1 + ERR() + OK()}(arg)
- " F0 should be defined.
- if exists("a:arg") && a:arg == "calling"
- Xpath 8192 " X: 8192
- else
- Xpath 16384 " X: 0
- endif
-endfunction
-if v:errmsg != ""
- Xpath 32768 " X: 0
-endif
-XloopNEXT
-
-Xpath 65536 " X: 65536
-call F{1 + ERR() + OK()}("calling")
-if v:errmsg != ""
- Xpath 131072 " X: 0
-endif
-XloopNEXT
-
-Xpath 262144 " X: 262144
-delfunction F{1 + ERR() + OK()}
-if v:errmsg != ""
- Xpath 524288 " X: 0
-endif
-XloopNEXT
-
-try
- while 1
- let caught = 0
- try
- Xpath 1048576 " X: 1048576
- function! G{1 + ERR() + OK()}(arg)
- " G0 should not be defined, and the function body should be
- " skipped.
- if exists("a:arg") && a:arg == "calling"
- Xpath 2097152 " X: 0
- else
- Xpath 4194304 " X: 0
- endif
- " Use an unmatched ":finally" to check whether the body is
- " skipped when an error occurs in ERR(). This works whether or
- " not the exception is converted to an exception.
- finally
- Xpath 8388608 " X: 0
- Xout "Body of G{1 + ERR() + OK()}() not skipped"
- " Discard the aborting error or exception, and break the
- " while loop.
- break
- " End the try conditional and start a new one to avoid
- " ":catch after :finally" errors.
- endtry
- try
- Xpath 16777216 " X: 0
- endfunction
-
- " When the function was not defined, this won't be reached - whether
- " the body was skipped or not. When the function was defined, it
- " can be called and deleted here.
- Xpath 33554432 " X: 0
- Xout "G0() has been defined"
- XloopNEXT
- try
- call G{1 + ERR() + OK()}("calling")
- catch /.*/
- Xpath 67108864 " X: 0
- endtry
- Xpath 134217728 " X: 0
- XloopNEXT
- try
- delfunction G{1 + ERR() + OK()}
- catch /.*/
- Xpath 268435456 " X: 0
- endtry
- catch /asdf/
- " Jumped to when the function is not defined and the body is
- " skipped.
- let caught = 1
- catch /.*/
- Xpath 536870912 " X: 0
- finally
- if !caught && !$VIMNOERRTHROW
- Xpath 1073741824 " X: 0
- endif
- break " discard error for $VIMNOERRTHROW
- endtry " jumped to when the body is not skipped
- endwhile
-catch /.*/
- " The Xpath command does not accept 2^31 (negative); add explicitly:
- let Xpath = Xpath + 2147483648 " X: 0
- Xout "Body of G{1 + ERR() + OK()}() not skipped, exception caught"
- Xout v:exception "in" v:throwpoint
-endtry
-
-Xcheck 1388671
-
-
-"-------------------------------------------------------------------------------
-" Test 78: Messages on parsing errors in expression evaluation {{{1
-"
-" When an expression evaluation detects a parsing error, an error
-" message is given and converted to an exception, and the expression
-" evaluation is aborted.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- let taken = ""
-
- function! F(n)
- let g:taken = g:taken . "F" . a:n
- endfunction
-
- function! MSG(n, enr, emsg)
- let g:taken = g:taken . "M" . a:n
- let english = v:lang == "C" || v:lang =~ '^[Ee]n'
- if a:enr == ""
- Xout "TODO: Add message number for:" a:emsg
- let v:errmsg = ":" . v:errmsg
- endif
- if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg)
- if v:errmsg == ""
- Xout "Expr" a:n.": Message missing."
- let g:taken = g:taken . "x"
- else
- let v:errmsg = escape(v:errmsg, '"')
- Xout "Expr" a:n.": Unexpected message:" v:errmsg
- Xout "Expected: " . a:enr . ': ' . a:emsg
- let g:taken = g:taken . "X"
- endif
- endif
- endfunction
-
- function! CONT(n)
- let g:taken = g:taken . "C" . a:n
- endfunction
-
- let v:errmsg = ""
- XloopINIT 1 2
-
- try
- let t = 1
- while t <= 14
- let g:taken = g:taken . "T" . t
- let v:errmsg = ""
- try
- let caught = 0
- if t == 1
- let v{novar + CONT(t)} = 0
- elseif t == 2
- let v{novar + CONT(t)}
- elseif t == 3
- let var = exists('v{novar + CONT(t)}')
- elseif t == 4
- unlet v{novar + CONT(t)}
- elseif t == 5
- function F{novar + CONT(t)}()
- endfunction
- elseif t == 6
- function F{novar + CONT(t)}
- elseif t == 7
- let var = exists('*F{novar + CONT(t)}')
- elseif t == 8
- delfunction F{novar + CONT(t)}
- elseif t == 9
- echo novar + CONT(t)
- elseif t == 10
- echo v{novar + CONT(t)}
- elseif t == 11
- echo F{novar + CONT(t)}
- elseif t == 12
- let var = novar + CONT(t)
- elseif t == 13
- let var = v{novar + CONT(t)}
- elseif t == 14
- let var = F{novar + CONT(t)}()
- endif
- catch /^Vim\((\a\+)\)\=:/
- " v:errmsg is not set when the error message is converted to an
- " exception. Set it to the original error message.
- let v:errmsg = substitute(v:exception,
- \ '^Vim\((\a\+)\)\=:', '', "")
- let caught = 1
- finally
- if t <= 8 && t != 3 && t != 7
- call MSG(t, 'E475', 'Invalid argument\>')
- else
- if !caught " no error exceptions ($VIMNOERRTHROW set)
- call MSG(t, 'E15', "Invalid expression")
- else
- call MSG(t, 'E121', "Undefined variable")
- endif
- endif
- let t = t + 1
- XloopNEXT
- continue " discard an aborting error
- endtry
- endwhile
- catch /.*/
- Xloop 1 " X: 0
- Xout t.":" v:exception "in" ExtraVimThrowpoint()
- endtry
-
- function! T(n, expr, enr, emsg)
- try
- let g:taken = g:taken . "T" . a:n
- let v:errmsg = ""
- try
- let caught = 0
- execute "let var = " . a:expr
- catch /^Vim\((\a\+)\)\=:/
- " v:errmsg is not set when the error message is converted to an
- " exception. Set it to the original error message.
- let v:errmsg = substitute(v:exception,
- \ '^Vim\((\a\+)\)\=:', '', "")
- let caught = 1
- finally
- if !caught " no error exceptions ($VIMNOERRTHROW set)
- call MSG(a:n, 'E15', "Invalid expression")
- else
- call MSG(a:n, a:enr, a:emsg)
- endif
- XloopNEXT
- " Discard an aborting error:
- return
- endtry
- catch /.*/
- Xloop 1 " X: 0
- Xout a:n.":" v:exception "in" ExtraVimThrowpoint()
- endtry
- endfunction
-
- call T(15, 'Nofunc() + CONT(15)', 'E117', "Unknown function")
- call T(16, 'F(1 2 + CONT(16))', 'E116', "Invalid arguments")
- call T(17, 'F(1, 2) + CONT(17)', 'E118', "Too many arguments")
- call T(18, 'F() + CONT(18)', 'E119', "Not enough arguments")
- call T(19, '{(1} + CONT(19)', 'E110', "Missing ')'")
- call T(20, '("abc"[1) + CONT(20)', 'E111', "Missing ']'")
- call T(21, '(1 +) + CONT(21)', 'E15', "Invalid expression")
- call T(22, '1 2 + CONT(22)', 'E15', "Invalid expression")
- call T(23, '(1 ? 2) + CONT(23)', 'E109', "Missing ':' after '?'")
- call T(24, '("abc) + CONT(24)', 'E114', "Missing quote")
- call T(25, "('abc) + CONT(25)", 'E115', "Missing quote")
- call T(26, '& + CONT(26)', 'E112', "Option name missing")
- call T(27, '&asdf + CONT(27)', 'E113', "Unknown option")
-
- Xpath 134217728 " X: 134217728
-
- let expected = ""
- \ . "T1M1T2M2T3M3T4M4T5M5T6M6T7M7T8M8T9M9T10M10T11M11T12M12T13M13T14M14"
- \ . "T15M15T16M16T17M17T18M18T19M19T20M20T21M21T22M22T23M23T24M24T25M25"
- \ . "T26M26T27M27"
-
- if taken != expected
- Xpath 268435456 " X: 0
- Xout "'taken' is" taken "instead of" expected
- if substitute(taken, '\(.*\)T3M3x\(.*\)', '\1T3M3\2', "") == expected
- Xout "Is ++emsg_skip for var with expr_start non-NULL"
- \ "in f_exists ok?"
- endif
- endif
-
- unlet! var caught taken expected
- call delete(WA_t5)
- unlet! WA_t5
- delfunction WA_t5
-
-endif
-
-Xcheck 134217728
-
-
-"-------------------------------------------------------------------------------
-" Test 79: Throwing one of several errors for the same command {{{1
-"
-" When several errors appear in a row (for instance during expression
-" evaluation), the first as the most specific one is used when
-" throwing an error exception. If, however, a syntax error is
-" detected afterwards, this one is used for the error exception.
-" On a syntax error, the next command is not executed, on a normal
-" error, however, it is (relevant only in a function without the
-" "abort" flag). v:errmsg is not set.
-"
-" If throwing error exceptions is configured off, v:errmsg is always
-" set to the latest error message, that is, to the more general
-" message or the syntax error, respectively.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-XloopINIT 1 2
-
-function! NEXT(cmd)
- exec a:cmd . " | Xloop 1"
-endfunction
-
-call NEXT('echo novar') " X: 1 * 1 (checks nextcmd)
-XloopNEXT
-call NEXT('let novar #') " X: 0 * 2 (skips nextcmd)
-XloopNEXT
-call NEXT('unlet novar #') " X: 0 * 4 (skips nextcmd)
-XloopNEXT
-call NEXT('let {novar}') " X: 0 * 8 (skips nextcmd)
-XloopNEXT
-call NEXT('unlet{ novar}') " X: 0 * 16 (skips nextcmd)
-
-function! EXEC(cmd)
- exec a:cmd
-endfunction
-
-function! MATCH(expected, msg, enr, emsg)
- let msg = a:msg
- if a:enr == ""
- Xout "TODO: Add message number for:" a:emsg
- let msg = ":" . msg
- endif
- let english = v:lang == "C" || v:lang =~ '^[Ee]n'
- if msg !~ '^'.a:enr.':' || (english && msg !~ a:emsg)
- let match = 0
- if a:expected " no match although expected
- if a:msg == ""
- Xout "Message missing."
- else
- let msg = escape(msg, '"')
- Xout "Unexpected message:" msg
- Xout "Expected:" a:enr . ": " . a:emsg
- endif
- endif
- else
- let match = 1
- if !a:expected " match although not expected
- let msg = escape(msg, '"')
- Xout "Unexpected message:" msg
- Xout "Expected none."
- endif
- endif
- return match
-endfunction
-
-try
-
- while 1 " dummy loop
- try
- let v:errmsg = ""
- let caught = 0
- let thrmsg = ""
- call EXEC('echo novar') " normal error
- catch /^Vim\((\a\+)\)\=:/
- let caught = 1
- let thrmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
- finally
- Xpath 32 " X: 32
- if !caught
- if !$VIMNOERRTHROW
- Xpath 64 " X: 0
- endif
- elseif !MATCH(1, thrmsg, 'E121', "Undefined variable")
- \ || v:errmsg != ""
- Xpath 128 " X: 0
- endif
- if !caught && !MATCH(1, v:errmsg, 'E15', "Invalid expression")
- Xpath 256 " X: 0
- endif
- break " discard error if $VIMNOERRTHROW == 1
- endtry
- endwhile
-
- Xpath 512 " X: 512
- let cmd = "let"
- XloopINIT 1024 32
- while cmd != ""
- try
- let v:errmsg = ""
- let caught = 0
- let thrmsg = ""
- call EXEC(cmd . ' novar #') " normal plus syntax error
- catch /^Vim\((\a\+)\)\=:/
- let caught = 1
- let thrmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
- finally
- Xloop 1 " X: 1024 * (1 + 32)
- if !caught
- if !$VIMNOERRTHROW
- Xloop 2 " X: 0
- endif
- else
- if cmd == "let"
- let match = MATCH(0, thrmsg, 'E121', "Undefined variable")
- elseif cmd == "unlet"
- let match = MATCH(0, thrmsg, 'E108', "No such variable")
- endif
- if match " normal error
- Xloop 4 " X: 0
- endif
- if !MATCH(1, thrmsg, 'E488', "Trailing characters")
- \|| v:errmsg != ""
- " syntax error
- Xloop 8 " X: 0
- endif
- endif
- if !caught && !MATCH(1, v:errmsg, 'E488', "Trailing characters")
- " last error
- Xloop 16 " X: 0
- endif
- if cmd == "let"
- let cmd = "unlet"
- else
- let cmd = ""
- endif
- XloopNEXT
- continue " discard error if $VIMNOERRTHROW == 1
- endtry
- endwhile
-
- Xpath 1048576 " X: 1048576
- let cmd = "let"
- XloopINIT 2097152 32
- while cmd != ""
- try
- let v:errmsg = ""
- let caught = 0
- let thrmsg = ""
- call EXEC(cmd . ' {novar}') " normal plus syntax error
- catch /^Vim\((\a\+)\)\=:/
- let caught = 1
- let thrmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
- finally
- Xloop 1 " X: 2097152 * (1 + 32)
- if !caught
- if !$VIMNOERRTHROW
- Xloop 2 " X: 0
- endif
- else
- if MATCH(0, thrmsg, 'E121', "Undefined variable") " normal error
- Xloop 4 " X: 0
- endif
- if !MATCH(1, thrmsg, 'E475', 'Invalid argument\>')
- \ || v:errmsg != "" " syntax error
- Xloop 8 " X: 0
- endif
- endif
- if !caught && !MATCH(1, v:errmsg, 'E475', 'Invalid argument\>')
- " last error
- Xloop 16 " X: 0
- endif
- if cmd == "let"
- let cmd = "unlet"
- else
- let cmd = ""
- endif
- XloopNEXT
- continue " discard error if $VIMNOERRTHROW == 1
- endtry
- endwhile
-
-catch /.*/
- " The Xpath command does not accept 2^31 (negative); add explicitly:
- let Xpath = Xpath + 2147483648 " X: 0
- Xout v:exception "in" v:throwpoint
-endtry
-
-unlet! next_command thrmsg match
-delfunction NEXT
-delfunction EXEC
-delfunction MATCH
-
-Xcheck 70288929
-
-
-"-------------------------------------------------------------------------------
-" Test 80: Syntax error in expression for illegal :elseif {{{1
-"
-" If there is a syntax error in the expression after an illegal
-" :elseif, an error message is given (or an error exception thrown)
-" for the illegal :elseif rather than the expression error.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! MSG(enr, emsg)
- let english = v:lang == "C" || v:lang =~ '^[Ee]n'
- if a:enr == ""
- Xout "TODO: Add message number for:" a:emsg
- let v:errmsg = ":" . v:errmsg
- endif
- let match = 1
- if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg)
- let match = 0
- if v:errmsg == ""
- Xout "Message missing."
- else
- let v:errmsg = escape(v:errmsg, '"')
- Xout "Unexpected message:" v:errmsg
- endif
- endif
- return match
-endfunction
-
-let v:errmsg = ""
-if 0
-else
-elseif 1 ||| 2
-endif
-Xpath 1 " X: 1
-if !MSG('E584', ":elseif after :else")
- Xpath 2 " X: 0
-endif
-
-let v:errmsg = ""
-if 1
-else
-elseif 1 ||| 2
-endif
-Xpath 4 " X: 4
-if !MSG('E584', ":elseif after :else")
- Xpath 8 " X: 0
-endif
-
-let v:errmsg = ""
-elseif 1 ||| 2
-Xpath 16 " X: 16
-if !MSG('E582', ":elseif without :if")
- Xpath 32 " X: 0
-endif
-
-let v:errmsg = ""
-while 1
- elseif 1 ||| 2
-endwhile
-Xpath 64 " X: 64
-if !MSG('E582', ":elseif without :if")
- Xpath 128 " X: 0
-endif
-
-while 1
- try
- try
- let v:errmsg = ""
- let caught = 0
- if 0
- else
- elseif 1 ||| 2
- endif
- catch /^Vim\((\a\+)\)\=:/
- let caught = 1
- let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
- finally
- Xpath 256 " X: 256
- if !caught && !$VIMNOERRTHROW
- Xpath 512 " X: 0
- endif
- if !MSG('E584', ":elseif after :else")
- Xpath 1024 " X: 0
- endif
- endtry
- catch /.*/
- Xpath 2048 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-while 1
- try
- try
- let v:errmsg = ""
- let caught = 0
- if 1
- else
- elseif 1 ||| 2
- endif
- catch /^Vim\((\a\+)\)\=:/
- let caught = 1
- let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
- finally
- Xpath 4096 " X: 4096
- if !caught && !$VIMNOERRTHROW
- Xpath 8192 " X: 0
- endif
- if !MSG('E584', ":elseif after :else")
- Xpath 16384 " X: 0
- endif
- endtry
- catch /.*/
- Xpath 32768 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-while 1
- try
- try
- let v:errmsg = ""
- let caught = 0
- elseif 1 ||| 2
- catch /^Vim\((\a\+)\)\=:/
- let caught = 1
- let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
- finally
- Xpath 65536 " X: 65536
- if !caught && !$VIMNOERRTHROW
- Xpath 131072 " X: 0
- endif
- if !MSG('E582', ":elseif without :if")
- Xpath 262144 " X: 0
- endif
- endtry
- catch /.*/
- Xpath 524288 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-while 1
- try
- try
- let v:errmsg = ""
- let caught = 0
- while 1
- elseif 1 ||| 2
- endwhile
- catch /^Vim\((\a\+)\)\=:/
- let caught = 1
- let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
- finally
- Xpath 1048576 " X: 1048576
- if !caught && !$VIMNOERRTHROW
- Xpath 2097152 " X: 0
- endif
- if !MSG('E582', ":elseif without :if")
- Xpath 4194304 " X: 0
- endif
- endtry
- catch /.*/
- Xpath 8388608 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-Xpath 16777216 " X: 16777216
-
-unlet! caught
-delfunction MSG
-
-Xcheck 17895765
-
-
-"-------------------------------------------------------------------------------
-" Test 81: Discarding exceptions after an error or interrupt {{{1
-"
-" When an exception is thrown from inside a :try conditional without
-" :catch and :finally clauses and an error or interrupt occurs before
-" the :endtry is reached, the exception is discarded.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
- try
- Xpath 1 " X: 1
- try
- Xpath 2 " X: 2
- throw "arrgh"
- Xpath 4 " X: 0
-" if 1
- Xpath 8 " X: 0
- " error after :throw: missing :endif
- endtry
- Xpath 16 " X: 0
- catch /arrgh/
- Xpath 32 " X: 0
- endtry
- Xpath 64 " X: 0
-endif
-
-if ExtraVim()
- try
- Xpath 128 " X: 128
- try
- Xpath 256 " X: 256
- throw "arrgh"
- Xpath 512 " X: 0
- endtry " INTERRUPT
- Xpath 1024 " X: 0
- catch /arrgh/
- Xpath 2048 " X: 0
- endtry
- Xpath 4096 " X: 0
-endif
-
-Xcheck 387
-
-
-"-------------------------------------------------------------------------------
-" Test 82: Ignoring :catch clauses after an error or interrupt {{{1
-"
-" When an exception is thrown and an error or interrupt occurs before
-" the matching :catch clause is reached, the exception is discarded
-" and the :catch clause is ignored (also for the error or interrupt
-" exception being thrown then).
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
- try
- try
- Xpath 1 " X: 1
- throw "arrgh"
- Xpath 2 " X: 0
-" if 1
- Xpath 4 " X: 0
- " error after :throw: missing :endif
- catch /.*/
- Xpath 8 " X: 0
- Xout v:exception "in" ExtraVimThrowpoint()
- catch /.*/
- Xpath 16 " X: 0
- Xout v:exception "in" ExtraVimThrowpoint()
- endtry
- Xpath 32 " X: 0
- catch /arrgh/
- Xpath 64 " X: 0
- endtry
- Xpath 128 " X: 0
-endif
-
-if ExtraVim()
- function! E()
- try
- try
- Xpath 256 " X: 256
- throw "arrgh"
- Xpath 512 " X: 0
-" if 1
- Xpath 1024 " X: 0
- " error after :throw: missing :endif
- catch /.*/
- Xpath 2048 " X: 0
- Xout v:exception "in" ExtraVimThrowpoint()
- catch /.*/
- Xpath 4096 " X: 0
- Xout v:exception "in" ExtraVimThrowpoint()
- endtry
- Xpath 8192 " X: 0
- catch /arrgh/
- Xpath 16384 " X: 0
- endtry
- endfunction
-
- call E()
- Xpath 32768 " X: 0
-endif
-
-if ExtraVim()
- try
- try
- Xpath 65536 " X: 65536
- throw "arrgh"
- Xpath 131072 " X: 0
- catch /.*/ "INTERRUPT
- Xpath 262144 " X: 0
- Xout v:exception "in" ExtraVimThrowpoint()
- catch /.*/
- Xpath 524288 " X: 0
- Xout v:exception "in" ExtraVimThrowpoint()
- endtry
- Xpath 1048576 " X: 0
- catch /arrgh/
- Xpath 2097152 " X: 0
- endtry
- Xpath 4194304 " X: 0
-endif
-
-if ExtraVim()
- function I()
- try
- try
- Xpath 8388608 " X: 8388608
- throw "arrgh"
- Xpath 16777216 " X: 0
- catch /.*/ "INTERRUPT
- Xpath 33554432 " X: 0
- Xout v:exception "in" ExtraVimThrowpoint()
- catch /.*/
- Xpath 67108864 " X: 0
- Xout v:exception "in" ExtraVimThrowpoint()
- endtry
- Xpath 134217728 " X: 0
- catch /arrgh/
- Xpath 268435456 " X: 0
- endtry
- endfunction
-
- call I()
- Xpath 536870912 " X: 0
-endif
-
-Xcheck 8454401
-
-
-"-------------------------------------------------------------------------------
-" Test 83: Executing :finally clauses after an error or interrupt {{{1
-"
-" When an exception is thrown and an error or interrupt occurs before
-" the :finally of the innermost :try is reached, the exception is
-" discarded and the :finally clause is executed.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
- try
- Xpath 1 " X: 1
- try
- Xpath 2 " X: 2
- throw "arrgh"
- Xpath 4 " X: 0
-" if 1
- Xpath 8 " X: 0
- " error after :throw: missing :endif
- finally
- Xpath 16 " X: 16
- endtry
- Xpath 32 " X: 0
- catch /arrgh/
- Xpath 64 " X: 0
- endtry
- Xpath 128 " X: 0
-endif
-
-if ExtraVim()
- try
- Xpath 256 " X: 256
- try
- Xpath 512 " X: 512
- throw "arrgh"
- Xpath 1024 " X: 0
- finally "INTERRUPT
- Xpath 2048 " X: 2048
- endtry
- Xpath 4096 " X: 0
- catch /arrgh/
- Xpath 8192 " X: 0
- endtry
- Xpath 16384 " X: 0
-endif
-
-Xcheck 2835
-
-
-"-------------------------------------------------------------------------------
-" Test 84: Exceptions in autocommand sequences. {{{1
-"
-" When an exception occurs in a sequence of autocommands for
-" a specific event, the rest of the sequence is not executed. The
-" command that triggered the autocommand execution aborts, and the
-" exception is propagated to the caller.
-"
-" For the FuncUndefined event under a function call expression or
-" :call command, the function is not executed, even when it has
-" been defined by the autocommands before the exception occurred.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- function! INT()
- "INTERRUPT
- let dummy = 0
- endfunction
-
- aug TMP
- autocmd!
-
- autocmd User x1 Xpath 1 " X: 1
- autocmd User x1 throw "x1"
- autocmd User x1 Xpath 2 " X: 0
-
- autocmd User x2 Xpath 4 " X: 4
- autocmd User x2 asdf
- autocmd User x2 Xpath 8 " X: 0
-
- autocmd User x3 Xpath 16 " X: 16
- autocmd User x3 call INT()
- autocmd User x3 Xpath 32 " X: 0
-
- autocmd FuncUndefined U1 function! U1()
- autocmd FuncUndefined U1 Xpath 64 " X: 0
- autocmd FuncUndefined U1 endfunction
- autocmd FuncUndefined U1 Xpath 128 " X: 128
- autocmd FuncUndefined U1 throw "U1"
- autocmd FuncUndefined U1 Xpath 256 " X: 0
-
- autocmd FuncUndefined U2 function! U2()
- autocmd FuncUndefined U2 Xpath 512 " X: 0
- autocmd FuncUndefined U2 endfunction
- autocmd FuncUndefined U2 Xpath 1024 " X: 1024
- autocmd FuncUndefined U2 ASDF
- autocmd FuncUndefined U2 Xpath 2048 " X: 0
-
- autocmd FuncUndefined U3 function! U3()
- autocmd FuncUndefined U3 Xpath 4096 " X: 0
- autocmd FuncUndefined U3 endfunction
- autocmd FuncUndefined U3 Xpath 8192 " X: 8192
- autocmd FuncUndefined U3 call INT()
- autocmd FuncUndefined U3 Xpath 16384 " X: 0
- aug END
-
- try
- try
- Xpath 32768 " X: 32768
- doautocmd User x1
- catch /x1/
- Xpath 65536 " X: 65536
- endtry
-
- while 1
- try
- Xpath 131072 " X: 131072
- let caught = 0
- doautocmd User x2
- catch /asdf/
- let caught = 1
- finally
- Xpath 262144 " X: 262144
- if !caught && !$VIMNOERRTHROW
- Xpath 524288 " X: 0
- " Propagate uncaught error exception,
- else
- " ... but break loop for caught error exception,
- " or discard error and break loop if $VIMNOERRTHROW
- break
- endif
- endtry
- endwhile
-
- while 1
- try
- Xpath 1048576 " X: 1048576
- let caught = 0
- doautocmd User x3
- catch /Vim:Interrupt/
- let caught = 1
- finally
- Xpath 2097152 " X: 2097152
- if !caught && !$VIMNOINTTHROW
- Xpath 4194304 " X: 0
- " Propagate uncaught interrupt exception,
- else
- " ... but break loop for caught interrupt exception,
- " or discard interrupt and break loop if $VIMNOINTTHROW
- break
- endif
- endtry
- endwhile
-
- if exists("*U1") | delfunction U1 | endif
- if exists("*U2") | delfunction U2 | endif
- if exists("*U3") | delfunction U3 | endif
-
- try
- Xpath 8388608 " X: 8388608
- call U1()
- catch /U1/
- Xpath 16777216 " X: 16777216
- endtry
-
- while 1
- try
- Xpath 33554432 " X: 33554432
- let caught = 0
- call U2()
- catch /ASDF/
- let caught = 1
- finally
- Xpath 67108864 " X: 67108864
- if !caught && !$VIMNOERRTHROW
- Xpath 134217728 " X: 0
- " Propagate uncaught error exception,
- else
- " ... but break loop for caught error exception,
- " or discard error and break loop if $VIMNOERRTHROW
- break
- endif
- endtry
- endwhile
-
- while 1
- try
- Xpath 268435456 " X: 268435456
- let caught = 0
- call U3()
- catch /Vim:Interrupt/
- let caught = 1
- finally
- Xpath 536870912 " X: 536870912
- if !caught && !$VIMNOINTTHROW
- Xpath 1073741824 " X: 0
- " Propagate uncaught interrupt exception,
- else
- " ... but break loop for caught interrupt exception,
- " or discard interrupt and break loop if $VIMNOINTTHROW
- break
- endif
- endtry
- endwhile
- catch /.*/
- " The Xpath command does not accept 2^31 (negative); display explicitly:
- exec "!echo 2147483648 >>" . g:ExtraVimResult
- Xout "Caught" v:exception "in" v:throwpoint
- endtry
-
- unlet caught
- delfunction INT
- delfunction U1
- delfunction U2
- delfunction U3
- au! TMP
- aug! TMP
-endif
-
-Xcheck 934782101
-
-
-"-------------------------------------------------------------------------------
-" Test 85: Error exceptions in autocommands for I/O command events {{{1
-"
-" When an I/O command is inside :try/:endtry, autocommands to be
-" executed after it should be skipped on an error (exception) in the
-" command itself or in autocommands to be executed before the command.
-" In the latter case, the I/O command should not be executed either.
-" Example 1: BufWritePre, :write, BufWritePost
-" Example 2: FileReadPre, :read, FileReadPost.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! MSG(enr, emsg)
- let english = v:lang == "C" || v:lang =~ '^[Ee]n'
- if a:enr == ""
- Xout "TODO: Add message number for:" a:emsg
- let v:errmsg = ":" . v:errmsg
- endif
- let match = 1
- if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg)
- let match = 0
- if v:errmsg == ""
- Xout "Message missing."
- else
- let v:errmsg = escape(v:errmsg, '"')
- Xout "Unexpected message:" v:errmsg
- endif
- endif
- return match
-endfunction
-
-" Remove the autocommands for the events specified as arguments in all used
-" autogroups.
-function Delete_autocommands(...)
- let augfile = tempname()
- while 1
- try
- exec "redir >" . augfile
- aug
- redir END
- exec "edit" augfile
- g/^$/d
- norm G$
- let wrap = "w"
- while search('\%( \|^\)\@<=.\{-}\%( \)\@=', wrap) > 0
- let wrap = "W"
- exec "norm y/ \n"
- let argno = 1
- while argno <= a:0
- exec "au!" escape(@", " ") a:{argno}
- let argno = argno + 1
- endwhile
- endwhile
- catch /.*/
- finally
- bwipeout!
- call delete(augfile)
- break " discard errors for $VIMNOERRTHROW
- endtry
- endwhile
-endfunction
-
-call Delete_autocommands("BufWritePre", "BufWritePost")
-
-while 1
- try
- try
- let post = 0
- aug TMP
- au! BufWritePost * let post = 1
- aug END
- let caught = 0
- write /n/o/n/e/x/i/s/t/e/n/t
- catch /^Vim(write):/
- let caught = 1
- let v:errmsg = substitute(v:exception, '^Vim(write):', '', "")
- finally
- Xpath 1 " X: 1
- if !caught && !$VIMNOERRTHROW
- Xpath 2 " X: 0
- endif
- let v:errmsg = substitute(v:errmsg, '^"/n/o/n/e/x/i/s/t/e/n/t" ',
- \ '', "")
- if !MSG('E212', "Can't open file for writing")
- Xpath 4 " X: 0
- endif
- if post
- Xpath 8 " X: 0
- Xout "BufWritePost commands executed after write error"
- endif
- au! TMP
- aug! TMP
- endtry
- catch /.*/
- Xpath 16 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-while 1
- try
- try
- let post = 0
- aug TMP
- au! BufWritePre * asdf
- au! BufWritePost * let post = 1
- aug END
- let tmpfile = tempname()
- let caught = 0
- exec "write" tmpfile
- catch /^Vim\((write)\)\=:/
- let caught = 1
- let v:errmsg = substitute(v:exception, '^Vim\((write)\)\=:', '', "")
- finally
- Xpath 32 " X: 32
- if !caught && !$VIMNOERRTHROW
- Xpath 64 " X: 0
- endif
- let v:errmsg = substitute(v:errmsg, '^"'.tmpfile.'" ', '', "")
- if !MSG('E492', "Not an editor command")
- Xpath 128 " X: 0
- endif
- if filereadable(tmpfile)
- Xpath 256 " X: 0
- Xout ":write command not suppressed after BufWritePre error"
- endif
- if post
- Xpath 512 " X: 0
- Xout "BufWritePost commands executed after BufWritePre error"
- endif
- au! TMP
- aug! TMP
- endtry
- catch /.*/
- Xpath 1024 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-call delete(tmpfile)
-
-call Delete_autocommands("BufWritePre", "BufWritePost",
- \ "BufReadPre", "BufReadPost", "FileReadPre", "FileReadPost")
-
-while 1
- try
- try
- let post = 0
- aug TMP
- au! FileReadPost * let post = 1
- aug END
- let caught = 0
- read /n/o/n/e/x/i/s/t/e/n/t
- catch /^Vim(read):/
- let caught = 1
- let v:errmsg = substitute(v:exception, '^Vim(read):', '', "")
- finally
- Xpath 2048 " X: 2048
- if !caught && !$VIMNOERRTHROW
- Xpath 4096 " X: 0
- endif
- let v:errmsg = substitute(v:errmsg, ' /n/o/n/e/x/i/s/t/e/n/t$',
- \ '', "")
- if !MSG('E484', "Can't open file")
- Xpath 8192 " X: 0
- endif
- if post
- Xpath 16384 " X: 0
- Xout "FileReadPost commands executed after write error"
- endif
- au! TMP
- aug! TMP
- endtry
- catch /.*/
- Xpath 32768 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-while 1
- try
- let infile = tempname()
- let tmpfile = tempname()
- exec "!echo XYZ >" . infile
- exec "edit" tmpfile
- try
- Xpath 65536 " X: 65536
- try
- let post = 0
- aug TMP
- au! FileReadPre * asdf
- au! FileReadPost * let post = 1
- aug END
- let caught = 0
- exec "0read" infile
- catch /^Vim\((read)\)\=:/
- let caught = 1
- let v:errmsg = substitute(v:exception, '^Vim\((read)\)\=:', '',
- \ "")
- finally
- Xpath 131072 " X: 131072
- if !caught && !$VIMNOERRTHROW
- Xpath 262144 " X: 0
- endif
- let v:errmsg = substitute(v:errmsg, ' '.infile.'$', '', "")
- if !MSG('E492', "Not an editor command")
- Xpath 524288 " X: 0
- endif
- if getline("1") == "XYZ"
- Xpath 1048576 " X: 0
- Xout ":read command not suppressed after FileReadPre error"
- endif
- if post
- Xpath 2097152 " X: 0
- Xout "FileReadPost commands executed after " .
- \ "FileReadPre error"
- endif
- au! TMP
- aug! TMP
- endtry
- finally
- bwipeout!
- endtry
- catch /.*/
- Xpath 4194304 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-call delete(infile)
-call delete(tmpfile)
-unlet! caught post infile tmpfile
-delfunction MSG
-delfunction Delete_autocommands
-
-Xcheck 198689
-
-"-------------------------------------------------------------------------------
-" Test 86: setloclist crash {{{1
-"
-" Executing a setloclist() on BufUnload shouldn't crash Vim
-"-------------------------------------------------------------------------------
-
-func F
- au BufUnload * :call setloclist(0, [{'bufnr':1, 'lnum':1, 'col':1, 'text': 'tango down'}])
-
- :lvimgrep /.*/ *.mak
-endfunc
-
-XpathINIT
-
-ExecAsScript F
-
-delfunction F
-Xout "No Crash for vimgrep on BufUnload"
-Xcheck 0
-
-" Test 87 was moved to test_vimscript.vim
-let Xtest = 88
-
-
-"-------------------------------------------------------------------------------
-" Test 88: $VIMNOERRTHROW and $VIMNOINTTHROW support {{{1
-"
-" It is possible to configure Vim for throwing exceptions on error
-" or interrupt, controlled by variables $VIMNOERRTHROW and
-" $VIMNOINTTHROW. This is just for increasing the number of tests.
-" All tests here should run for all four combinations of setting
-" these variables to 0 or 1. The variables are intended for the
-" development phase only. In the final release, Vim should be
-" configured to always use error and interrupt exceptions.
-"
-" The test result is "OK",
-"
-" - if the $VIMNOERRTHROW and the $VIMNOINTTHROW control are not
-" configured and exceptions are thrown on error and on
-" interrupt.
-"
-" - if the $VIMNOERRTHROW or the $VIMNOINTTHROW control is
-" configured and works as intended.
-"
-" What actually happens, is shown in the test output.
-"
-" Otherwise, the test result is "FAIL", and the test output describes
-" the problem.
-"
-" IMPORTANT: This must be the last test because it sets $VIMNOERRTHROW and
-" $VIMNOINTTHROW.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- function! ThrowOnError()
- XloopNEXT
- let caught = 0
- try
- Xloop 1 " X: 1 + 8 + 64
- asdf
- catch /.*/
- let caught = 1 " error exception caught
- finally
- Xloop 2 " X: 2 + 16 + 128
- return caught " discard aborting error
- endtry
- Xloop 4 " X: 0
- endfunction
-
- let quits_skipped = 0
-
- function! ThrowOnInterrupt()
- XloopNEXT
- let caught = 0
- try
- Xloop 1 " X: (1 + 8 + 64) * 512
- "INTERRUPT3
- let dummy = 0
- let g:quits_skipped = g:quits_skipped + 1
- catch /.*/
- let caught = 1 " interrupt exception caught
- finally
- Xloop 2 " X: (2 + 16 + 128) * 512
- return caught " discard interrupt
- endtry
- Xloop 4 " X: 0
- endfunction
-
- function! CheckThrow(Type)
- execute 'return ThrowOn' . a:Type . '()'
- endfunction
-
- function! CheckConfiguration(type) " type is "error" or "interrupt"
-
- let type = a:type
- let Type = substitute(type, '.*', '\u&', "")
- let VAR = '$VIMNO' . substitute(type, '\(...\).*', '\U\1', "") . 'THROW'
-
- if type == "error"
- XloopINIT! 1 8
- elseif type == "interrupt"
- XloopINIT! 512 8
- endif
-
- exec 'let requested_for_tests = exists(VAR) && ' . VAR . ' == 0'
- exec 'let suppressed_for_tests = ' . VAR . ' != 0'
- let used_in_tests = CheckThrow(Type)
-
- exec 'let ' . VAR . ' = 0'
- let request_works = CheckThrow(Type)
-
- exec 'let ' . VAR . ' = 1'
- let suppress_works = !CheckThrow(Type)
-
- if type == "error"
- XloopINIT! 262144 8
- elseif type == "interrupt"
- XloopINIT! 2097152 8
-
- if g:quits_skipped != 0
- Xloop 1 " X: 0*2097152
- Xout "Test environment error. Interrupt breakpoints skipped: "
- \ . g:quits_skipped . ".\n"
- \ . "Cannot check whether interrupt exceptions are thrown."
- return
- endif
- endif
-
- let failure =
- \ !suppressed_for_tests && !used_in_tests
- \ || !request_works
-
- let contradiction =
- \ used_in_tests
- \ ? suppressed_for_tests && !request_works
- \ : !suppressed_for_tests
-
- if failure
- " Failure in configuration.
- Xloop 2 " X: 0 * 2* (262144 + 2097152)
- elseif contradiction
- " Failure in test logic. Should not happen.
- Xloop 4 " X: 0 * 4 * (262144 + 2097152)
- endif
-
- let var_control_configured =
- \ request_works != used_in_tests
- \ || suppress_works == used_in_tests
-
- let var_control_not_configured =
- \ requested_for_tests || suppressed_for_tests
- \ ? request_works && !suppress_works
- \ : request_works == used_in_tests
- \ && suppress_works != used_in_tests
-
- let with = used_in_tests ? "with" : "without"
-
- let set = suppressed_for_tests ? "non-zero" :
- \ requested_for_tests ? "0" : "unset"
-
- let although = contradiction && !var_control_not_configured
- \ ? ",\nalthough "
- \ : ".\n"
-
- let output = "All tests were run " . with . " throwing exceptions on "
- \ . type . although
-
- if !var_control_not_configured
- let output = output . VAR . " was " . set . "."
-
- if !request_works && !requested_for_tests
- let output = output .
- \ "\n" . Type . " exceptions are not thrown when " . VAR .
- \ " is\nset to 0."
- endif
-
- if !suppress_works && (!used_in_tests ||
- \ !request_works &&
- \ !requested_for_tests && !suppressed_for_tests)
- let output = output .
- \ "\n" . Type . " exceptions are thrown when " . VAR .
- \ " is set to 1."
- endif
-
- if !failure && var_control_configured
- let output = output .
- \ "\nRun tests also with " . substitute(VAR, '^\$', '', "")
- \ . "=" . used_in_tests . "."
- \ . "\nThis is for testing in the development phase only."
- \ . " Remove the \n"
- \ . VAR . " control in the final release."
- endif
- else
- let output = output .
- \ "The " . VAR . " control is not configured."
- endif
-
- Xout output
- endfunction
-
- call CheckConfiguration("error")
- Xpath 16777216 " X: 16777216
- call CheckConfiguration("interrupt")
- Xpath 33554432 " X: 33554432
-endif
-
-Xcheck 50443995
-
-" IMPORTANT: No test should be added after this test because it changes
-" $VIMNOERRTHROW and $VIMNOINTTHROW.
-
-
-"-------------------------------------------------------------------------------
-" Modelines {{{1
-" vim: ts=8 sw=4 tw=80 fdm=marker
-"-------------------------------------------------------------------------------
diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim
index a83ef50abc..a3d240f27e 100644
--- a/src/nvim/testdir/test_alot.vim
+++ b/src/nvim/testdir/test_alot.vim
@@ -15,7 +15,6 @@ source test_fnamemodify.vim
source test_ga.vim
source test_glob2regpat.vim
source test_global.vim
-source test_lispwords.vim
source test_move.vim
source test_put.vim
source test_reltime.vim
diff --git a/src/nvim/testdir/test_arglist.vim b/src/nvim/testdir/test_arglist.vim
index 521c3fcd57..fb8b17cd16 100644
--- a/src/nvim/testdir/test_arglist.vim
+++ b/src/nvim/testdir/test_arglist.vim
@@ -1,5 +1,6 @@
" Test argument list commands
+source check.vim
source shared.vim
source term_util.vim
@@ -417,15 +418,19 @@ func Test_argdedupe()
call Reset_arglist()
argdedupe
call assert_equal([], argv())
+
args a a a aa b b a b aa
argdedupe
call assert_equal(['a', 'aa', 'b'], argv())
+
args a b c
argdedupe
call assert_equal(['a', 'b', 'c'], argv())
+
args a
argdedupe
call assert_equal(['a'], argv())
+
args a A b B
argdedupe
if has('fname_case')
@@ -433,11 +438,17 @@ func Test_argdedupe()
else
call assert_equal(['a', 'b'], argv())
endif
+
args a b a c a b
last
argdedupe
next
call assert_equal('c', expand('%:t'))
+
+ args a ./a
+ argdedupe
+ call assert_equal(['a'], argv())
+
%argd
endfunc
@@ -509,18 +520,14 @@ func Test_arglist_autocmd()
new
" redefine arglist; go to Xxx1
next! Xxx1 Xxx2 Xxx3
- " open window for all args; Reading Xxx2 will change the arglist and the
- " third window will get Xxx1:
- " win 1: Xxx1
- " win 2: Xxx2
- " win 3: Xxx1
- all
+ " open window for all args; Reading Xxx2 will try to change the arglist and
+ " that will fail
+ call assert_fails("all", "E1156:")
call assert_equal('test file Xxx1', getline(1))
wincmd w
- wincmd w
- call assert_equal('test file Xxx1', getline(1))
- rewind
call assert_equal('test file Xxx2', getline(1))
+ wincmd w
+ call assert_equal('test file Xxx3', getline(1))
autocmd! BufReadPost Xxx2
enew! | only
@@ -554,11 +561,9 @@ func Test_argdo()
bwipe Xa.c Xb.c Xc.c
endfunc
-" Test for quiting Vim with unedited files in the argument list
+" Test for quitting Vim with unedited files in the argument list
func Test_quit_with_arglist()
- if !CanRunVimInTerminal()
- throw 'Skipped: cannot run vim in terminal'
- endif
+ CheckRunVimInTerminal
let buf = RunVimInTerminal('', {'rows': 6})
call term_sendkeys(buf, ":set nomore\n")
call term_sendkeys(buf, ":args a b c\n")
@@ -591,4 +596,153 @@ func Test_quit_with_arglist()
call delete('.c.swp')
endfunc
+" Test for ":all" not working when in the cmdline window
+func Test_all_not_allowed_from_cmdwin()
+ au BufEnter * all
+ next x
+ " Use try/catch here, somehow assert_fails() doesn't work on MS-Windows
+ " console.
+ let caught = 'no'
+ try
+ exe ":norm! 7q?apat\<CR>"
+ catch /E11:/
+ let caught = 'yes'
+ endtry
+ call assert_equal('yes', caught)
+ au! BufEnter
+endfunc
+
+func Test_clear_arglist_in_all()
+ n 0 00 000 0000 00000 000000
+ au WinNew 0 n 0
+ call assert_fails("all", "E1156")
+ au! *
+endfunc
+
+" Test for the :all command
+func Test_all_command()
+ %argdelete
+
+ " :all command should not close windows with files in the argument list,
+ " but can rearrange the windows.
+ args Xargnew1 Xargnew2
+ %bw!
+ edit Xargold1
+ split Xargnew1
+ let Xargnew1_winid = win_getid()
+ split Xargold2
+ split Xargnew2
+ let Xargnew2_winid = win_getid()
+ split Xargold3
+ all
+ call assert_equal(2, winnr('$'))
+ call assert_equal([Xargnew1_winid, Xargnew2_winid],
+ \ [win_getid(1), win_getid(2)])
+ call assert_equal([bufnr('Xargnew1'), bufnr('Xargnew2')],
+ \ [winbufnr(1), winbufnr(2)])
+
+ " :all command should close windows for files which are not in the
+ " argument list in the current tab page.
+ %bw!
+ edit Xargold1
+ split Xargold2
+ tabedit Xargold3
+ split Xargold4
+ tabedit Xargold5
+ tabfirst
+ all
+ call assert_equal(3, tabpagenr('$'))
+ call assert_equal([bufnr('Xargnew1'), bufnr('Xargnew2')], tabpagebuflist(1))
+ call assert_equal([bufnr('Xargold4'), bufnr('Xargold3')], tabpagebuflist(2))
+ call assert_equal([bufnr('Xargold5')], tabpagebuflist(3))
+
+ " :tab all command should close windows for files which are not in the
+ " argument list across all the tab pages.
+ %bw!
+ edit Xargold1
+ split Xargold2
+ tabedit Xargold3
+ split Xargold4
+ tabedit Xargold5
+ tabfirst
+ args Xargnew1 Xargnew2
+ tab all
+ call assert_equal(2, tabpagenr('$'))
+ call assert_equal([bufnr('Xargnew1')], tabpagebuflist(1))
+ call assert_equal([bufnr('Xargnew2')], tabpagebuflist(2))
+
+ " If a count is specified, then :all should open only that many windows.
+ %bw!
+ args Xargnew1 Xargnew2 Xargnew3 Xargnew4 Xargnew5
+ all 3
+ call assert_equal(3, winnr('$'))
+ call assert_equal([bufnr('Xargnew1'), bufnr('Xargnew2'), bufnr('Xargnew3')],
+ \ [winbufnr(1), winbufnr(2), winbufnr(3)])
+
+ " The :all command should not open more than 'tabpagemax' tab pages.
+ " If there are more files, then they should be opened in the last tab page.
+ %bw!
+ set tabpagemax=3
+ tab all
+ call assert_equal(3, tabpagenr('$'))
+ call assert_equal([bufnr('Xargnew1')], tabpagebuflist(1))
+ call assert_equal([bufnr('Xargnew2')], tabpagebuflist(2))
+ call assert_equal([bufnr('Xargnew3'), bufnr('Xargnew4'), bufnr('Xargnew5')],
+ \ tabpagebuflist(3))
+ set tabpagemax&
+
+ " Without the 'hidden' option, modified buffers should not be closed.
+ args Xargnew1 Xargnew2
+ %bw!
+ edit Xargtemp1
+ call setline(1, 'temp buffer 1')
+ split Xargtemp2
+ call setline(1, 'temp buffer 2')
+ all
+ call assert_equal(4, winnr('$'))
+ call assert_equal([bufnr('Xargtemp2'), bufnr('Xargtemp1'), bufnr('Xargnew1'),
+ \ bufnr('Xargnew2')],
+ \ [winbufnr(1), winbufnr(2), winbufnr(3), winbufnr(4)])
+
+ " With the 'hidden' option set, both modified and unmodified buffers in
+ " closed windows should be hidden.
+ set hidden
+ all
+ call assert_equal(2, winnr('$'))
+ call assert_equal([bufnr('Xargnew1'), bufnr('Xargnew2')],
+ \ [winbufnr(1), winbufnr(2)])
+ call assert_equal([1, 1, 0, 0], [getbufinfo('Xargtemp1')[0].hidden,
+ \ getbufinfo('Xargtemp2')[0].hidden,
+ \ getbufinfo('Xargnew1')[0].hidden,
+ \ getbufinfo('Xargnew2')[0].hidden])
+ set nohidden
+
+ " When 'winheight' is set to a large value, :all should open only one
+ " window.
+ args Xargnew1 Xargnew2 Xargnew3 Xargnew4 Xargnew5
+ %bw!
+ set winheight=9999
+ call assert_fails('all', 'E36:')
+ call assert_equal([1, bufnr('Xargnew1')], [winnr('$'), winbufnr(1)])
+ set winheight&
+
+ " When 'winwidth' is set to a large value, :vert all should open only one
+ " window.
+ %bw!
+ set winwidth=9999
+ call assert_fails('vert all', 'E36:')
+ call assert_equal([1, bufnr('Xargnew1')], [winnr('$'), winbufnr(1)])
+ set winwidth&
+
+ " empty argument list tests
+ %bw!
+ %argdelete
+ call assert_equal('', execute('args'))
+ all
+ call assert_equal(1, winnr('$'))
+
+ %argdelete
+ %bw!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_assert.vim b/src/nvim/testdir/test_assert.vim
index fdd8b0bef6..431908e95c 100644
--- a/src/nvim/testdir/test_assert.vim
+++ b/src/nvim/testdir/test_assert.vim
@@ -48,6 +48,11 @@ func Test_assert_equal()
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)
+
+ " special characters are escaped
+ call assert_equal("\b\e\f\n\t\r\\\x01\x7f", 'x')
+ call assert_match('Expected ''\\b\\e\\f\\n\\t\\r\\\\\\x01\\x7f'' but got ''x''', v:errors[0])
+ call remove(v:errors, 0)
endfunc
func Test_assert_equal_dict()
@@ -146,6 +151,14 @@ func Test_assert_exception()
try
nocommand
catch
+ call assert_equal(1, assert_exception('E12345:'))
+ endtry
+ call assert_match("Expected 'E12345:' but got 'Vim:E492: ", v:errors[0])
+ call remove(v:errors, 0)
+
+ try
+ nocommand
+ catch
try
" illegal argument, get NULL for error
call assert_equal(1, assert_exception([]))
@@ -153,6 +166,10 @@ func Test_assert_exception()
call assert_equal(0, assert_exception('E730:'))
endtry
endtry
+
+ call assert_equal(1, assert_exception('E492:'))
+ call assert_match('v:exception is not set', v:errors[0])
+ call remove(v:errors, 0)
endfunc
func Test_wrong_error_type()
@@ -202,6 +219,14 @@ func Test_assert_fail_fails()
call assert_match("stupid: Expected 'E9876' but got 'E492:", v:errors[0])
call remove(v:errors, 0)
+ call assert_equal(1, assert_fails('xxx', ['E9876']))
+ call assert_match("Expected \\['E9876'\\] but got 'E492:", v:errors[0])
+ call remove(v:errors, 0)
+
+ call assert_equal(1, assert_fails('xxx', ['E492:', 'E9876']))
+ call assert_match("Expected \\['E492:', '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)
@@ -209,6 +234,41 @@ func Test_assert_fail_fails()
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)
+
+ try
+ call assert_equal(1, assert_fails('xxx', []))
+ catch
+ let exp = v:exception
+ endtry
+ call assert_match("E856: assert_fails() second argument", exp)
+
+ try
+ call assert_equal(1, assert_fails('xxx', ['1', '2', '3']))
+ catch
+ let exp = v:exception
+ endtry
+ call assert_match("E856: assert_fails() second argument", exp)
+
+ try
+ call assert_equal(1, assert_fails('xxx', #{one: 1}))
+ catch
+ let exp = v:exception
+ endtry
+ call assert_match("E856: assert_fails() second argument", exp)
+
+ try
+ call assert_equal(1, assert_fails('xxx', 'E492', '', 'burp'))
+ catch
+ let exp = v:exception
+ endtry
+ call assert_match("E1115: assert_fails() fourth argument must be a number", exp)
+
+ try
+ call assert_equal(1, assert_fails('xxx', 'E492', '', 54, 123))
+ catch
+ let exp = v:exception
+ endtry
+ call assert_match("E1116: assert_fails() fifth argument must be a string", exp)
endfunc
func Test_assert_fails_in_try_block()
@@ -278,19 +338,18 @@ func Test_assert_with_msg()
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 Ntest_setmouse(1, 5)
call feedkeys("\<LeftMouse>", "xt")
call assert_equal([0, 1, 5, 0], getpos('.'))
- call test_setmouse(2, 20)
+ call Ntest_setmouse(2, 20)
call feedkeys("\<LeftMouse>", "xt")
call assert_equal([0, 2, 8, 0], getpos('.'))
- call test_setmouse(5, 1)
+ call Ntest_setmouse(5, 1)
call feedkeys("\<LeftMouse>", "xt")
call assert_equal([0, 2, 1, 0], getpos('.'))
bwipe!
diff --git a/src/nvim/testdir/test_autochdir.vim b/src/nvim/testdir/test_autochdir.vim
index 4229095f9f..a8810047a0 100644
--- a/src/nvim/testdir/test_autochdir.vim
+++ b/src/nvim/testdir/test_autochdir.vim
@@ -122,9 +122,7 @@ 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:')
+ call assert_fails('tc *', has('win32') ? 'E480:' : 'E344:')
set nowic
endfunc
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 716511210d..83af0f6be0 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -4,6 +4,7 @@ source shared.vim
source check.vim
source term_util.vim
source screendump.vim
+source load.vim
func s:cleanup_buffers() abort
for bnr in range(1, bufnr('$'))
@@ -20,8 +21,35 @@ func Test_vim_did_enter()
" becomes one.
endfunc
+" Test for the CursorHold autocmd
+func Test_CursorHold_autocmd()
+ CheckRunVimInTerminal
+ call writefile(['one', 'two', 'three'], 'Xfile')
+ let before =<< trim END
+ set updatetime=10
+ au CursorHold * call writefile([line('.')], 'Xoutput', 'a')
+ END
+ call writefile(before, 'Xinit')
+ let buf = RunVimInTerminal('-S Xinit Xfile', {})
+ call term_sendkeys(buf, "G")
+ call term_wait(buf, 20)
+ call term_sendkeys(buf, "gg")
+ call term_wait(buf)
+ call WaitForAssert({-> assert_equal(['1'], readfile('Xoutput')[-1:-1])})
+ call term_sendkeys(buf, "j")
+ call term_wait(buf)
+ call WaitForAssert({-> assert_equal(['1', '2'], readfile('Xoutput')[-2:-1])})
+ call term_sendkeys(buf, "j")
+ call term_wait(buf)
+ call WaitForAssert({-> assert_equal(['1', '2', '3'], readfile('Xoutput')[-3:-1])})
+ call StopVimInTerminal(buf)
+
+ call delete('Xinit')
+ call delete('Xoutput')
+ call delete('Xfile')
+endfunc
+
if has('timers')
- source load.vim
func ExitInsertMode(id)
call feedkeys("\<Esc>")
@@ -148,6 +176,12 @@ func Test_autocmd_bufunload_with_tabnext()
quit
endfunc
+func Test_argdelete_in_next()
+ au BufNew,BufEnter,BufLeave,BufWinEnter * argdel
+ call assert_fails('next a b', 'E1156:')
+ au! BufNew,BufEnter,BufLeave,BufWinEnter *
+endfunc
+
func Test_autocmd_bufwinleave_with_tabfirst()
tabedit
augroup sample
@@ -169,9 +203,7 @@ func Test_autocmd_bufunload_avoiding_SEGV_01()
exe 'autocmd BufUnload <buffer> ' . (lastbuf + 1) . 'bwipeout!'
augroup END
- " Todo: check for E937 generated first
- " call assert_fails('edit bb.txt', 'E937:')
- call assert_fails('edit bb.txt', 'E517:')
+ call assert_fails('edit bb.txt', 'E937:')
autocmd! test_autocmd_bufunload
augroup! test_autocmd_bufunload
@@ -263,6 +295,61 @@ func Test_win_tab_autocmd()
unlet g:record
endfunc
+func Test_WinResized()
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ set scrolloff=0
+ call setline(1, ['111', '222'])
+ vnew
+ call setline(1, ['aaa', 'bbb'])
+ new
+ call setline(1, ['foo', 'bar'])
+
+ let g:resized = 0
+ au WinResized * let g:resized += 1
+
+ func WriteResizedEvent()
+ call writefile([json_encode(v:event)], 'XresizeEvent')
+ endfunc
+ au WinResized * call WriteResizedEvent()
+ END
+ call writefile(lines, 'Xtest_winresized', 'D')
+ let buf = RunVimInTerminal('-S Xtest_winresized', {'rows': 10})
+
+ " redraw now to avoid a redraw after the :echo command
+ call term_sendkeys(buf, ":redraw!\<CR>")
+ call TermWait(buf)
+
+ call term_sendkeys(buf, ":echo g:resized\<CR>")
+ call WaitForAssert({-> assert_match('^0$', term_getline(buf, 10))}, 1000)
+
+ " increase window height, two windows will be reported
+ call term_sendkeys(buf, "\<C-W>+")
+ call TermWait(buf)
+ call term_sendkeys(buf, ":echo g:resized\<CR>")
+ call WaitForAssert({-> assert_match('^1$', term_getline(buf, 10))}, 1000)
+
+ let event = readfile('XresizeEvent')[0]->json_decode()
+ call assert_equal({
+ \ 'windows': [1002, 1001],
+ \ }, event)
+
+ " increase window width, three windows will be reported
+ call term_sendkeys(buf, "\<C-W>>")
+ call TermWait(buf)
+ call term_sendkeys(buf, ":echo g:resized\<CR>")
+ call WaitForAssert({-> assert_match('^2$', term_getline(buf, 10))}, 1000)
+
+ let event = readfile('XresizeEvent')[0]->json_decode()
+ call assert_equal({
+ \ 'windows': [1002, 1001, 1000],
+ \ }, event)
+
+ call delete('XresizeEvent')
+ call StopVimInTerminal(buf)
+endfunc
+
func Test_WinScrolled()
CheckRunVimInTerminal
@@ -273,13 +360,17 @@ func Test_WinScrolled()
endfor
let win_id = win_getid()
let g:matched = v:false
+ func WriteScrollEvent()
+ call writefile([json_encode(v:event)], 'XscrollEvent')
+ endfunc
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>'))
+ au WinScrolled * call WriteScrollEvent()
END
- call writefile(lines, 'Xtest_winscrolled')
+ call writefile(lines, 'Xtest_winscrolled', 'D')
let buf = RunVimInTerminal('-S Xtest_winscrolled', {'rows': 6})
call term_sendkeys(buf, ":echo g:scrolled\<CR>")
@@ -289,15 +380,33 @@ func Test_WinScrolled()
call term_sendkeys(buf, "zlzh:echo g:scrolled\<CR>")
call WaitForAssert({-> assert_match('^2 ', term_getline(buf, 6))}, 1000)
+ let event = readfile('XscrollEvent')[0]->json_decode()
+ call assert_equal({
+ \ 'all': {'leftcol': 1, 'topline': 0, 'topfill': 0, 'width': 0, 'height': 0, 'skipcol': 0},
+ \ '1000': {'leftcol': -1, 'topline': 0, 'topfill': 0, 'width': 0, 'height': 0, 'skipcol': 0}
+ \ }, event)
+
" 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)
+ let event = readfile('XscrollEvent')[0]->json_decode()
+ call assert_equal({
+ \ 'all': {'leftcol': 0, 'topline': 1, 'topfill': 0, 'width': 0, 'height': 0, 'skipcol': 0},
+ \ '1000': {'leftcol': 0, 'topline': -1, 'topfill': 0, 'width': 0, 'height': 0, 'skipcol': 0}
+ \ }, event)
+
" 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)
+ let event = readfile('XscrollEvent')[0]->json_decode()
+ call assert_equal({
+ \ 'all': {'leftcol': 0, 'topline': 1, 'topfill': 0, 'width': 0, 'height': 0, 'skipcol': 0},
+ \ '1000': {'leftcol': 0, 'topline': -1, 'topfill': 0, 'width': 0, 'height': 0, 'skipcol': 0}
+ \ }, event)
+
" 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.
@@ -305,6 +414,12 @@ func Test_WinScrolled()
call term_sendkeys(buf, ":echo g:scrolled\<CR>")
call WaitForAssert({-> assert_match('^8 ', term_getline(buf, 6))}, 1000)
+ let event = readfile('XscrollEvent')[0]->json_decode()
+ call assert_equal({
+ \ 'all': {'leftcol': 5, 'topline': 0, 'topfill': 0, 'width': 0, 'height': 0, 'skipcol': 0},
+ \ '1000': {'leftcol': -5, 'topline': 0, 'topfill': 0, 'width': 0, 'height': 0, 'skipcol': 0}
+ \ }, event)
+
" 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)
@@ -313,8 +428,38 @@ func Test_WinScrolled()
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 delete('XscrollEvent')
+ call StopVimInTerminal(buf)
+endfunc
+
+func Test_WinScrolled_mouse()
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ set nowrap scrolloff=0
+ set mouse=a term=xterm ttymouse=sgr mousetime=200 clipboard=
+ call setline(1, ['foo']->repeat(32))
+ split
+ let g:scrolled = 0
+ au WinScrolled * let g:scrolled += 1
+ END
+ call writefile(lines, 'Xtest_winscrolled_mouse', 'D')
+ let buf = RunVimInTerminal('-S Xtest_winscrolled_mouse', {'rows': 10})
+
+ " With the upper split focused, send a scroll-down event to the unfocused one.
+ call test_setmouse(7, 1)
+ call term_sendkeys(buf, "\<ScrollWheelDown>")
+ call TermWait(buf)
+ call term_sendkeys(buf, ":echo g:scrolled\<CR>")
+ call WaitForAssert({-> assert_match('^1', term_getline(buf, 10))}, 1000)
+
+ " Again, but this time while we're in insert mode.
+ call term_sendkeys(buf, "i\<ScrollWheelDown>\<Esc>")
+ call TermWait(buf)
+ call term_sendkeys(buf, ":echo g:scrolled\<CR>")
+ call WaitForAssert({-> assert_match('^2', term_getline(buf, 10))}, 1000)
+
call StopVimInTerminal(buf)
- call delete('Xtest_winscrolled')
endfunc
func Test_WinScrolled_close_curwin()
@@ -327,7 +472,7 @@ func Test_WinScrolled_close_curwin()
au WinScrolled * close
au VimLeave * call writefile(['123456'], 'Xtestout')
END
- call writefile(lines, 'Xtest_winscrolled_close_curwin')
+ call writefile(lines, 'Xtest_winscrolled_close_curwin', 'D')
let buf = RunVimInTerminal('-S Xtest_winscrolled_close_curwin', {'rows': 6})
" This was using freed memory
@@ -335,12 +480,158 @@ func Test_WinScrolled_close_curwin()
call TermWait(buf)
call StopVimInTerminal(buf)
+ " check the startup script finished to the end
call assert_equal(['123456'], readfile('Xtestout'))
-
- call delete('Xtest_winscrolled_close_curwin')
call delete('Xtestout')
endfunc
+func Test_WinScrolled_once_only()
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ set cmdheight=2
+ call setline(1, ['aaa', 'bbb'])
+ let trigger_count = 0
+ func ShowInfo(id)
+ echo g:trigger_count g:winid winlayout()
+ endfunc
+
+ vsplit
+ split
+ " use a timer to show the info after a redraw
+ au WinScrolled * let trigger_count += 1 | let winid = expand('<amatch>') | call timer_start(100, 'ShowInfo')
+ wincmd j
+ wincmd l
+ END
+ call writefile(lines, 'Xtest_winscrolled_once', 'D')
+ let buf = RunVimInTerminal('-S Xtest_winscrolled_once', #{rows: 10, cols: 60, statusoff: 2})
+
+ call term_sendkeys(buf, "\<C-E>")
+ call VerifyScreenDump(buf, 'Test_winscrolled_once_only_1', {})
+
+ call StopVimInTerminal(buf)
+endfunc
+
+" Check that WinScrolled is not triggered immediately when defined and there
+" are split windows.
+func Test_WinScrolled_not_when_defined()
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ call setline(1, ['aaa', 'bbb'])
+ echo 'nothing happened'
+ func ShowTriggered(id)
+ echo 'triggered'
+ endfunc
+ END
+ call writefile(lines, 'Xtest_winscrolled_not', 'D')
+ let buf = RunVimInTerminal('-S Xtest_winscrolled_not', #{rows: 10, cols: 60, statusoff: 2})
+ call term_sendkeys(buf, ":split\<CR>")
+ call TermWait(buf)
+ " use a timer to show the message after redrawing
+ call term_sendkeys(buf, ":au WinScrolled * call timer_start(100, 'ShowTriggered')\<CR>")
+ call VerifyScreenDump(buf, 'Test_winscrolled_not_when_defined_1', {})
+
+ call term_sendkeys(buf, "\<C-E>")
+ call VerifyScreenDump(buf, 'Test_winscrolled_not_when_defined_2', {})
+
+ call StopVimInTerminal(buf)
+endfunc
+
+func Test_WinScrolled_long_wrapped()
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ set scrolloff=0
+ let height = winheight(0)
+ let width = winwidth(0)
+ let g:scrolled = 0
+ au WinScrolled * let g:scrolled += 1
+ call setline(1, repeat('foo', height * width))
+ call cursor(1, height * width)
+ END
+ call writefile(lines, 'Xtest_winscrolled_long_wrapped', 'D')
+ let buf = RunVimInTerminal('-S Xtest_winscrolled_long_wrapped', {'rows': 6})
+
+ call term_sendkeys(buf, ":echo g:scrolled\<CR>")
+ call WaitForAssert({-> assert_match('^0 ', term_getline(buf, 6))}, 1000)
+
+ call term_sendkeys(buf, 'gj')
+ call term_sendkeys(buf, ":echo g:scrolled\<CR>")
+ call WaitForAssert({-> assert_match('^1 ', term_getline(buf, 6))}, 1000)
+
+ call term_sendkeys(buf, '0')
+ call term_sendkeys(buf, ":echo g:scrolled\<CR>")
+ call WaitForAssert({-> assert_match('^2 ', term_getline(buf, 6))}, 1000)
+
+ call term_sendkeys(buf, '$')
+ call term_sendkeys(buf, ":echo g:scrolled\<CR>")
+ call WaitForAssert({-> assert_match('^3 ', term_getline(buf, 6))}, 1000)
+
+ call StopVimInTerminal(buf)
+endfunc
+
+func Test_WinScrolled_diff()
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ set diffopt+=foldcolumn:0
+ call setline(1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'])
+ vnew
+ call setline(1, ['d', 'e', 'f', 'g', 'h', 'i'])
+ windo diffthis
+ func WriteScrollEvent()
+ call writefile([json_encode(v:event)], 'XscrollEvent')
+ endfunc
+ au WinScrolled * call WriteScrollEvent()
+ END
+ call writefile(lines, 'Xtest_winscrolled_diff', 'D')
+ let buf = RunVimInTerminal('-S Xtest_winscrolled_diff', {'rows': 8})
+
+ call term_sendkeys(buf, "\<C-E>")
+ call WaitForAssert({-> assert_match('^d', term_getline(buf, 3))}, 1000)
+
+ let event = readfile('XscrollEvent')[0]->json_decode()
+ call assert_equal({
+ \ 'all': {'leftcol': 0, 'topline': 1, 'topfill': 1, 'width': 0, 'height': 0, 'skipcol': 0},
+ \ '1000': {'leftcol': 0, 'topline': 1, 'topfill': 0, 'width': 0, 'height': 0, 'skipcol': 0},
+ \ '1001': {'leftcol': 0, 'topline': 0, 'topfill': -1, 'width': 0, 'height': 0, 'skipcol': 0}
+ \ }, event)
+
+ call term_sendkeys(buf, "2\<C-E>")
+ call WaitForAssert({-> assert_match('^f', term_getline(buf, 3))}, 1000)
+
+ let event = readfile('XscrollEvent')[0]->json_decode()
+ call assert_equal({
+ \ 'all': {'leftcol': 0, 'topline': 2, 'topfill': 2, 'width': 0, 'height': 0, 'skipcol': 0},
+ \ '1000': {'leftcol': 0, 'topline': 2, 'topfill': 0, 'width': 0, 'height': 0, 'skipcol': 0},
+ \ '1001': {'leftcol': 0, 'topline': 0, 'topfill': -2, 'width': 0, 'height': 0, 'skipcol': 0}
+ \ }, event)
+
+ call term_sendkeys(buf, "\<C-E>")
+ call WaitForAssert({-> assert_match('^g', term_getline(buf, 3))}, 1000)
+
+ let event = readfile('XscrollEvent')[0]->json_decode()
+ call assert_equal({
+ \ 'all': {'leftcol': 0, 'topline': 2, 'topfill': 0, 'width': 0, 'height': 0, 'skipcol': 0},
+ \ '1000': {'leftcol': 0, 'topline': 1, 'topfill': 0, 'width': 0, 'height': 0, 'skipcol': 0},
+ \ '1001': {'leftcol': 0, 'topline': 1, 'topfill': 0, 'width': 0, 'height': 0, 'skipcol': 0}
+ \ }, event)
+
+ call term_sendkeys(buf, "2\<C-Y>")
+ call WaitForAssert({-> assert_match('^e', term_getline(buf, 3))}, 1000)
+
+ let event = readfile('XscrollEvent')[0]->json_decode()
+ call assert_equal({
+ \ 'all': {'leftcol': 0, 'topline': 3, 'topfill': 1, 'width': 0, 'height': 0, 'skipcol': 0},
+ \ '1000': {'leftcol': 0, 'topline': -2, 'topfill': 0, 'width': 0, 'height': 0, 'skipcol': 0},
+ \ '1001': {'leftcol': 0, 'topline': -1, 'topfill': 1, 'width': 0, 'height': 0, 'skipcol': 0}
+ \ }, event)
+
+ call StopVimInTerminal(buf)
+ call delete('XscrollEvent')
+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.
@@ -414,6 +705,26 @@ func Test_WinClosed_throws_with_tabs()
augroup! test-WinClosed
endfunc
+" This used to trigger WinClosed twice for the same window, and the window's
+" buffer was NULL in the second autocommand.
+func Test_WinClosed_switch_tab()
+ edit Xa
+ split Xb
+ split Xc
+ tab split
+ new
+ augroup test-WinClosed
+ autocmd WinClosed * tabprev | bwipe!
+ augroup END
+ close
+ " Check that the tabline has been fully removed
+ call assert_equal([1, 1], win_screenpos(0))
+
+ autocmd! test-WinClosed
+ augroup! test-WinClosed
+ %bwipe!
+endfunc
+
func s:AddAnAutocmd()
augroup vimBarTest
au BufReadCmd * echo 'hello'
@@ -424,17 +735,20 @@ endfunc
func Test_early_bar()
" test that a bar is recognized before the {event}
call s:AddAnAutocmd()
- augroup vimBarTest | au! | augroup END
+ augroup vimBarTest | au! | let done = 77 | augroup END
call assert_equal(1, len(split(execute('au vimBarTest'), "\n")))
+ call assert_equal(77, done)
call s:AddAnAutocmd()
- augroup vimBarTest| au!| augroup END
+ augroup vimBarTest| au!| let done = 88 | augroup END
call assert_equal(1, len(split(execute('au vimBarTest'), "\n")))
+ call assert_equal(88, done)
" test that a bar is recognized after the {event}
call s:AddAnAutocmd()
- augroup vimBarTest| au!BufReadCmd| augroup END
+ augroup vimBarTest| au!BufReadCmd| let done = 99 | augroup END
call assert_equal(1, len(split(execute('au vimBarTest'), "\n")))
+ call assert_equal(99, done)
" test that a bar is recognized after the {group}
call s:AddAnAutocmd()
@@ -474,6 +788,8 @@ func Test_augroup_warning()
redir END
call assert_notmatch("W19:", res)
au! VimEnter
+
+ call assert_fails('augroup!', 'E471:')
endfunc
func Test_BufReadCmdHelp()
@@ -493,6 +809,28 @@ func Test_BufReadCmdHelpJump()
au! BufReadCmd
endfunc
+" BufReadCmd is triggered for a "nofile" buffer. Check all values.
+func Test_BufReadCmdNofile()
+ for val in ['nofile',
+ \ 'nowrite',
+ \ 'acwrite',
+ \ 'quickfix',
+ \ 'help',
+ "\ 'terminal',
+ \ 'prompt',
+ "\ 'popup',
+ \ ]
+ new somefile
+ exe 'set buftype=' .. val
+ au BufReadCmd somefile call setline(1, 'triggered')
+ edit
+ call assert_equal('triggered', getline(1))
+
+ au! BufReadCmd
+ bwipe!
+ endfor
+endfunc
+
func Test_augroup_deleted()
" This caused a crash before E936 was introduced
augroup x
@@ -587,9 +925,28 @@ func Test_BufEnter()
" On MS-Windows we can't edit the directory, make sure we wipe the right
" buffer.
bwipe! Xdir
-
call delete('Xdir', 'd')
au! BufEnter
+
+ " Editing a "nofile" buffer doesn't read the file but does trigger BufEnter
+ " for historic reasons. Also test other 'buftype' values.
+ for val in ['nofile',
+ \ 'nowrite',
+ \ 'acwrite',
+ \ 'quickfix',
+ \ 'help',
+ "\ 'terminal',
+ \ 'prompt',
+ "\ 'popup',
+ \ ]
+ new somefile
+ exe 'set buftype=' .. val
+ au BufEnter somefile call setline(1, 'some text')
+ edit
+ call assert_equal('some text', getline(1))
+ bwipe!
+ au! BufEnter
+ endfor
endfunc
" Closing a window might cause an endless loop
@@ -1766,6 +2123,21 @@ func Test_BufReadCmd()
au! BufWriteCmd
endfunc
+func Test_BufWriteCmd()
+ autocmd BufWriteCmd Xbufwritecmd let g:written = 1
+ new
+ file Xbufwritecmd
+ set buftype=acwrite
+ call mkdir('Xbufwritecmd')
+ write
+ " BufWriteCmd should be triggered even if a directory has the same name
+ call assert_equal(1, g:written)
+ call delete('Xbufwritecmd', 'd')
+ unlet g:written
+ au! BufWriteCmd
+ bwipe!
+endfunc
+
func SetChangeMarks(start, end)
exe a:start .. 'mark ['
exe a:end .. 'mark ]'
@@ -1860,9 +2232,7 @@ func Test_change_mark_in_autocmds()
endfunc
func Test_Filter_noshelltemp()
- if !executable('cat')
- return
- endif
+ CheckExecutable cat
enew!
call setline(1, ['a', 'b', 'c', 'd'])
@@ -1939,6 +2309,7 @@ endfunc
func Test_autocommand_all_events()
call assert_fails('au * * bwipe', 'E1155:')
call assert_fails('au * x bwipe', 'E1155:')
+ call assert_fails('au! * x bwipe', 'E1155:')
endfunc
func Test_autocmd_user()
@@ -2026,9 +2397,8 @@ function Test_dirchanged_auto()
endfunc
" Test TextChangedI and TextChangedP
-" See test/functional/viml/completion_spec.lua'
func Test_ChangedP()
- CheckFunction test_override
+ throw 'Skipped: use test/functional/editor/completion_spec.lua'
new
call setline(1, ['foo', 'bar', 'foobar'])
call test_override("char_avail", 1)
@@ -2549,7 +2919,7 @@ func Test_autocmd_CmdWinEnter()
call term_sendkeys(buf, "q:")
call term_wait(buf)
call term_sendkeys(buf, ":echo b:dummy_var\<cr>")
- call WaitForAssert({-> assert_match('^This is a dummy', term_getline(buf, 6))}, 1000)
+ call WaitForAssert({-> assert_match('^This is a dummy', term_getline(buf, 6))}, 2000)
call term_sendkeys(buf, ":echo &buftype\<cr>")
call WaitForAssert({-> assert_notmatch('^nofile', term_getline(buf, 6))}, 1000)
call term_sendkeys(buf, ":echo winnr\<cr>")
@@ -2636,6 +3006,17 @@ func Test_FileType_spell()
setglobal spellfile=
endfunc
+" this was wiping out the current buffer and using freed memory
+func Test_SpellFileMissing_bwipe()
+ next 0
+ au SpellFileMissing 0 bwipe
+ call assert_fails('set spell spelllang=0', 'E937:')
+
+ au! SpellFileMissing
+ set nospell spelllang=en
+ bwipe
+endfunc
+
" Test closing a window or editing another buffer from a FileChangedRO handler
" in a readonly buffer
func Test_FileChangedRO_winclose()
@@ -2724,6 +3105,31 @@ func Test_autocmd_FileReadCmd()
delfunc ReadFileCmd
endfunc
+" Test for passing invalid arguments to autocmd
+func Test_autocmd_invalid_args()
+ " Additional character after * for event
+ call assert_fails('autocmd *a Xfile set ff=unix', 'E215:')
+ augroup Test
+ augroup END
+ " Invalid autocmd event
+ call assert_fails('autocmd Bufabc Xfile set ft=vim', 'E216:')
+ " Invalid autocmd event in a autocmd group
+ call assert_fails('autocmd Test Bufabc Xfile set ft=vim', 'E216:')
+ augroup! Test
+ " Execute all autocmds
+ call assert_fails('doautocmd * BufEnter', 'E217:')
+ call assert_fails('augroup! x1a2b3', 'E367:')
+ call assert_fails('autocmd BufNew <buffer=999> pwd', 'E680:')
+ call assert_fails('autocmd BufNew \) set ff=unix', 'E55:')
+endfunc
+
+" Test for deep nesting of autocmds
+func Test_autocmd_deep_nesting()
+ autocmd BufEnter Xfile doautocmd BufEnter Xfile
+ call assert_fails('doautocmd BufEnter Xfile', 'E218:')
+ autocmd! BufEnter Xfile
+endfunc
+
" Tests for SigUSR1 autocmd event, which is only available on posix systems.
func Test_autocmd_sigusr1()
CheckUnix
@@ -2781,7 +3187,7 @@ func Test_BufDelete_changebuf()
augroup END
let save_cpo = &cpo
set cpo+=f
- call assert_fails('r Xfile', 'E484:')
+ call assert_fails('r Xfile', ['E812:', 'E484:'])
call assert_equal('somefile', @%)
let &cpo = save_cpo
augroup TestAuCmd
@@ -2871,6 +3277,15 @@ func Test_Visual_doautoall_redraw()
%bwipe!
endfunc
+" This was using freed memory.
+func Test_BufNew_arglocal()
+ arglocal
+ au BufNew * arglocal
+ call assert_fails('drop xx', 'E1156:')
+
+ au! BufNew
+endfunc
+
func Test_autocmd_closes_window()
au BufNew,BufWinLeave * e %e
file yyy
@@ -3039,19 +3454,29 @@ func Test_mode_changes()
call assert_equal(5, g:nori_to_any)
endif
- if has('cmdwin')
- let g:n_to_c = 0
- au ModeChanged n:c let g:n_to_c += 1
- let g:c_to_n = 0
- au ModeChanged c:n let g:c_to_n += 1
- let g:mode_seq += ['c', 'n', 'c', 'n']
- call feedkeys("q:\<C-C>\<Esc>", 'tnix')
- call assert_equal(len(g:mode_seq) - 1, g:index)
- call assert_equal(2, g:n_to_c)
- call assert_equal(2, g:c_to_n)
- unlet g:n_to_c
- unlet g:c_to_n
- endif
+ let g:n_to_c = 0
+ au ModeChanged n:c let g:n_to_c += 1
+ let g:c_to_n = 0
+ au ModeChanged c:n let g:c_to_n += 1
+ let g:mode_seq += ['c', 'n', 'c', 'n']
+ call feedkeys("q:\<C-C>\<Esc>", 'tnix')
+ call assert_equal(len(g:mode_seq) - 1, g:index)
+ call assert_equal(2, g:n_to_c)
+ call assert_equal(2, g:c_to_n)
+ unlet g:n_to_c
+ unlet g:c_to_n
+
+ let g:n_to_v = 0
+ au ModeChanged n:v let g:n_to_v += 1
+ let g:v_to_n = 0
+ au ModeChanged v:n let g:v_to_n += 1
+ let g:mode_seq += ['v', 'n']
+ call feedkeys("v\<C-C>", 'tnix')
+ call assert_equal(len(g:mode_seq) - 1, g:index)
+ call assert_equal(1, g:n_to_v)
+ call assert_equal(1, g:v_to_n)
+ unlet g:n_to_v
+ unlet g:v_to_n
au! ModeChanged
delfunc TestMode
@@ -3100,4 +3525,48 @@ func Test_noname_autocmd()
augroup! test_noname_autocmd_group
endfunc
+func Test_autocmd_split_dummy()
+ " Autocommand trying to split a window containing a dummy buffer.
+ auto BufReadPre * exe "sbuf " .. expand("<abuf>")
+ " Avoid the "W11" prompt
+ au FileChangedShell * let v:fcs_choice = 'reload'
+ func Xautocmd_changelist()
+ cal writefile(['Xtestfile2:4:4'], 'Xerr')
+ edit Xerr
+ lex 'Xtestfile2:4:4'
+ endfunc
+ call Xautocmd_changelist()
+ " Should get E86, but it doesn't always happen (timing?)
+ silent! call Xautocmd_changelist()
+
+ au! BufReadPre
+ au! FileChangedShell
+ delfunc Xautocmd_changelist
+ bwipe! Xerr
+ call delete('Xerr')
+endfunc
+
+" This was crashing because there was only one window to execute autocommands
+" in.
+func Test_autocmd_nested_setbufvar()
+ CheckFeature python3
+
+ set hidden
+ edit Xaaa
+ edit Xbbb
+ call setline(1, 'bar')
+ enew
+ au BufWriteCmd Xbbb ++nested call setbufvar('Xaaa', '&ft', 'foo') | bw! Xaaa
+ au FileType foo call py3eval('vim.current.buffer.options["cindent"]')
+ wall
+
+ au! BufWriteCmd
+ au! FileType foo
+ set nohidden
+ call delete('Xaaa')
+ call delete('Xbbb')
+ %bwipe!
+endfunc
+
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_autoload.vim b/src/nvim/testdir/test_autoload.vim
index b8c4fa251f..e89fe3943b 100644
--- a/src/nvim/testdir/test_autoload.vim
+++ b/src/nvim/testdir/test_autoload.vim
@@ -10,6 +10,9 @@ func Test_autoload_dict_func()
call assert_equal(1, g:called_foo_bar_echo)
eval 'bar'->g:foo#addFoo()->assert_equal('barfoo')
+
+ " empty name works in legacy script
+ call assert_equal('empty', foo#())
endfunc
func Test_source_autoload()
@@ -17,3 +20,6 @@ func Test_source_autoload()
source sautest/autoload/sourced.vim
call assert_equal(1, g:loaded_sourced_vim)
endfunc
+
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_backup.vim b/src/nvim/testdir/test_backup.vim
index ce2bfe72bc..d304773da4 100644
--- a/src/nvim/testdir/test_backup.vim
+++ b/src/nvim/testdir/test_backup.vim
@@ -1,5 +1,7 @@
" Tests for the backup function
+source check.vim
+
func Test_backup()
set backup backupdir=. backupskip=
new
@@ -17,6 +19,22 @@ func Test_backup()
call delete('Xbackup.txt~')
endfunc
+func Test_backup_backupskip()
+ set backup backupdir=. backupskip=*.txt
+ new
+ call setline(1, ['line1', 'line2'])
+ :f Xbackup.txt
+ :w! Xbackup.txt
+ " backup file is only created after
+ " writing a second time (before overwriting)
+ :w! Xbackup.txt
+ call assert_false(filereadable('Xbackup.txt~'))
+ bw!
+ set backup&vim backupdir&vim backupskip&vim
+ call delete('Xbackup.txt')
+ call delete('Xbackup.txt~')
+endfunc
+
func Test_backup2()
set backup backupdir=.// backupskip=
new
@@ -28,7 +46,7 @@ func Test_backup2()
:w! Xbackup.txt
sp *Xbackup.txt~
call assert_equal(['line1', 'line2', 'line3'], getline(1,'$'))
- let f=expand('%')
+ let f = expand('%')
call assert_match('%testdir%Xbackup.txt\~', f)
bw!
bw!
@@ -48,7 +66,7 @@ func Test_backup2_backupcopy()
:w! Xbackup.txt
sp *Xbackup.txt~
call assert_equal(['line1', 'line2', 'line3'], getline(1,'$'))
- let f=expand('%')
+ let f = expand('%')
call assert_match('%testdir%Xbackup.txt\~', f)
bw!
bw!
@@ -56,3 +74,16 @@ func Test_backup2_backupcopy()
call delete(f)
set backup&vim backupdir&vim backupcopy&vim backupskip&vim
endfunc
+
+" Test for using a non-existing directory as a backup directory
+func Test_non_existing_backupdir()
+ throw 'Skipped: Nvim auto-creates backup directory'
+ set backupdir=./non_existing_dir backupskip=
+ call writefile(['line1'], 'Xfile')
+ new Xfile
+ call assert_fails('write', 'E510:')
+ set backupdir&vim backupskip&vim
+ call delete('Xfile')
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_blob.vim b/src/nvim/testdir/test_blob.vim
index af42b3857d..046acb81e1 100644
--- a/src/nvim/testdir/test_blob.vim
+++ b/src/nvim/testdir/test_blob.vim
@@ -88,6 +88,7 @@ func Test_blob_get_range()
call assert_equal(0z0011223344, b[:])
call assert_equal(0z0011223344, b[:-1])
call assert_equal(0z, b[5:6])
+ call assert_equal(0z0011, b[-10:1])
endfunc
func Test_blob_get()
@@ -208,6 +209,7 @@ func Test_blob_add()
call assert_equal(0z001122, b)
call add(b, '51')
call assert_equal(0z00112233, b)
+ call assert_equal(1, add(v:_null_blob, 0x22))
call assert_fails('call add(b, [9])', 'E745:')
call assert_fails('call add("", 0x01)', 'E897:')
@@ -250,6 +252,7 @@ func Test_blob_func_remove()
call assert_fails("call remove(b, 3, 2)", 'E979:')
call assert_fails("call remove(1, 0)", 'E896:')
call assert_fails("call remove(b, b)", 'E974:')
+ call assert_fails("call remove(b, 1, [])", 'E745:')
call assert_fails("call remove(v:_null_blob, 1, 2)", 'E979:')
" Translated from v8.2.3284
@@ -272,6 +275,7 @@ endfunc
" filter() item in blob
func Test_blob_filter()
+ call assert_equal(v:_null_blob, filter(v:_null_blob, '0'))
call assert_equal(0z, filter(0zDEADBEEF, '0'))
call assert_equal(0zADBEEF, filter(0zDEADBEEF, 'v:val != 0xDE'))
call assert_equal(0zDEADEF, filter(0zDEADBEEF, 'v:val != 0xBE'))
@@ -294,9 +298,12 @@ func Test_blob_index()
call assert_equal(2, index(0zDEADBEEF, 0xBE))
call assert_equal(-1, index(0zDEADBEEF, 0))
call assert_equal(2, index(0z11111111, 0x11, 2))
- call assert_equal(3, index(0z11110111, 0x11, 2))
+ call assert_equal(3, 0z11110111->index(0x11, 2))
call assert_equal(2, index(0z11111111, 0x11, -2))
call assert_equal(3, index(0z11110111, 0x11, -2))
+ call assert_equal(0, index(0z11110111, 0x11, -10))
+ call assert_fails("echo index(0z11110111, 0x11, [])", 'E745:')
+ call assert_equal(-1, index(v:_null_blob, 1))
call assert_fails('call index("asdf", 0)', 'E897:')
endfunc
@@ -313,6 +320,9 @@ func Test_blob_insert()
call assert_fails('call insert(b, -1)', 'E475:')
call assert_fails('call insert(b, 257)', 'E475:')
call assert_fails('call insert(b, 0, [9])', 'E745:')
+ call assert_fails('call insert(b, 0, -20)', 'E475:')
+ call assert_fails('call insert(b, 0, 20)', 'E475:')
+ call assert_fails('call insert(b, [])', 'E745:')
call assert_equal(0, insert(v:_null_blob, 0x33))
" Translated from v8.2.3284
diff --git a/src/nvim/testdir/test_breakindent.vim b/src/nvim/testdir/test_breakindent.vim
index a37751e748..2e377aa434 100644
--- a/src/nvim/testdir/test_breakindent.vim
+++ b/src/nvim/testdir/test_breakindent.vim
@@ -4,11 +4,11 @@
" 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')
- throw 'Skipped: breakindent option not supported'
-endif
+source check.vim
+CheckOption breakindent
source view_util.vim
+source screendump.vim
let s:input ="\tabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP"
@@ -422,6 +422,7 @@ func Test_breakindent11()
let width = strlen(text[1:]) + indent(2) + strlen(&sbr) * 3 " text wraps 3 times
call assert_equal(width, strdisplaywidth(text))
call s:close_windows('set sbr=')
+ call assert_equal(4, strdisplaywidth("\t", 4))
endfunc
func Test_breakindent11_vartabs()
@@ -855,7 +856,7 @@ func Test_breakindent20_list()
" check formatlistpat indent with different list level
" showbreak and sbr
- setl briopt=min:5,sbr,list:-1,shift:2
+ setl briopt=min:5,sbr,list:-1
setl showbreak=>
redraw!
let expect = [
@@ -868,6 +869,44 @@ func Test_breakindent20_list()
\ ]
let lines = s:screen_lines2(1, 6, 20)
call s:compare_lines(expect, lines)
+
+ " check formatlistpat indent with different list level
+ " showbreak sbr and shift
+ setl briopt=min:5,sbr,list:-1,shift:2
+ setl showbreak=>
+ redraw!
+ let expect = [
+ \ "* Congress shall ",
+ \ "> make no law ",
+ \ "*** Congress shall ",
+ \ "> make no law ",
+ \ "**** Congress shall ",
+ \ "> make no law ",
+ \ ]
+ let lines = s:screen_lines2(1, 6, 20)
+ call s:compare_lines(expect, lines)
+
+ " check breakindent works if breakindentopt=list:-1
+ " for a non list content
+ %delete _
+ call setline(1, [' Congress shall make no law',
+ \ ' Congress shall make no law',
+ \ ' Congress shall make no law'])
+ norm! 1gg
+ setl briopt=min:5,list:-1
+ setl showbreak=
+ redraw!
+ let expect = [
+ \ " Congress shall ",
+ \ " make no law ",
+ \ " Congress shall ",
+ \ " make no law ",
+ \ " Congress shall ",
+ \ " make no law ",
+ \ ]
+ let lines = s:screen_lines2(1, 6, 20)
+ call s:compare_lines(expect, lines)
+
call s:close_windows('set breakindent& briopt& linebreak& list& listchars& showbreak&')
endfunc
@@ -876,17 +915,164 @@ endfunc
func Test_window_resize_with_linebreak()
new
53vnew
- set linebreak
- set showbreak=>>
- set breakindent
- set breakindentopt=shift:4
+ setl linebreak
+ setl showbreak=>>
+ setl breakindent
+ setl breakindentopt=shift:4
call setline(1, "\naaaaaaaaa\n\na\naaaaa\n¯aaaaaaaaaa\naaaaaaaaaaaa\naaa\n\"a:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaaaa\"\naaaaaaaa\n\"a")
redraw!
call assert_equal([" >>aa^@\"a: "], ScreenLines(2, 14))
vertical resize 52
redraw!
call assert_equal([" >>aaa^@\"a:"], ScreenLines(2, 14))
+ set linebreak& showbreak& breakindent& breakindentopt&
%bw!
endfunc
+func Test_cursor_position_with_showbreak()
+ CheckScreendump
+
+ let lines =<< trim END
+ vim9script
+ &signcolumn = 'yes'
+ &showbreak = '+ '
+ var leftcol: number = win_getid()->getwininfo()->get(0, {})->get('textoff')
+ repeat('x', &columns - leftcol - 1)->setline(1)
+ 'second line'->setline(2)
+ END
+ call writefile(lines, 'XscriptShowbreak')
+ let buf = RunVimInTerminal('-S XscriptShowbreak', #{rows: 6})
+
+ call term_sendkeys(buf, "AX")
+ call VerifyScreenDump(buf, 'Test_cursor_position_with_showbreak', {})
+
+ call StopVimInTerminal(buf)
+ call delete('XscriptShowbreak')
+endfunc
+
+func Test_no_spurious_match()
+ let s:input = printf('- y %s y %s', repeat('x', 50), repeat('x', 50))
+ call s:test_windows('setl breakindent breakindentopt=list:-1 formatlistpat=^- hls')
+ let @/ = '\%>3v[y]'
+ redraw!
+ call searchcount().total->assert_equal(1)
+ " cleanup
+ set hls&vim
+ let s:input = "\tabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP"
+ bwipeout!
+endfunc
+
+func Test_no_extra_indent()
+ call s:test_windows('setl breakindent breakindentopt=list:-1,min:10')
+ %d
+ let &l:formatlistpat='^\s*\d\+\.\s\+'
+ let text = 'word '
+ let len = text->strcharlen()
+ let line1 = text->repeat((winwidth(0) / len) * 2)
+ let line2 = repeat(' ', 2) .. '1. ' .. line1
+ call setline(1, [line2])
+ redraw!
+ " 1) matches formatlist pattern, so indent
+ let expect = [
+ \ " 1. word word word ",
+ \ " word word word ",
+ \ " word word ",
+ \ "~ ",
+ \ ]
+ let lines = s:screen_lines2(1, 4, 20)
+ call s:compare_lines(expect, lines)
+ " 2) change formatlist pattern
+ " -> indent adjusted
+ let &l:formatlistpat='^\s*\d\+\.'
+ let expect = [
+ \ " 1. word word word ",
+ \ " word word word ",
+ \ " word word ",
+ \ "~ ",
+ \ ]
+ let lines = s:screen_lines2(1, 4, 20)
+ " 3) no local formatlist pattern,
+ " so use global one -> indent
+ let g_flp = &g:flp
+ let &g:formatlistpat='^\s*\d\+\.\s\+'
+ let &l:formatlistpat=''
+ let expect = [
+ \ " 1. word word word ",
+ \ " word word word ",
+ \ " word word ",
+ \ "~ ",
+ \ ]
+ let lines = s:screen_lines2(1, 4, 20)
+ call s:compare_lines(expect, lines)
+ let &g:flp = g_flp
+ let &l:formatlistpat='^\s*\d\+\.'
+ " 4) add something in front, no additional indent
+ norm! gg0
+ exe ":norm! 5iword \<esc>"
+ redraw!
+ let expect = [
+ \ "word word word word ",
+ \ "word 1. word word ",
+ \ "word word word word ",
+ \ "word word ",
+ \ "~ ",
+ \ ]
+ let lines = s:screen_lines2(1, 5, 20)
+ call s:compare_lines(expect, lines)
+ bwipeout!
+endfunc
+
+func Test_breakindent_column()
+ " restore original
+ let s:input ="\tabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP"
+ call s:test_windows('setl breakindent breakindentopt=column:10')
+ redraw!
+ " 1) default: does not indent, too wide :(
+ let expect = [
+ \ " ",
+ \ " abcdefghijklmnop",
+ \ "qrstuvwxyzABCDEFGHIJ",
+ \ "KLMNOP "
+ \ ]
+ let lines = s:screen_lines2(1, 4, 20)
+ call s:compare_lines(expect, lines)
+ " 2) lower min value, so that breakindent works
+ setl breakindentopt+=min:5
+ redraw!
+ let expect = [
+ \ " ",
+ \ " abcdefghijklmnop",
+ \ " qrstuvwxyz",
+ \ " ABCDEFGHIJ",
+ \ " KLMNOP "
+ \ ]
+ let lines = s:screen_lines2(1, 5, 20)
+ " 3) set shift option -> no influence
+ setl breakindentopt+=shift:5
+ redraw!
+ let expect = [
+ \ " ",
+ \ " abcdefghijklmnop",
+ \ " qrstuvwxyz",
+ \ " ABCDEFGHIJ",
+ \ " KLMNOP "
+ \ ]
+ let lines = s:screen_lines2(1, 5, 20)
+ call s:compare_lines(expect, lines)
+ " 4) add showbreak value
+ setl showbreak=++
+ redraw!
+ let expect = [
+ \ " ",
+ \ " abcdefghijklmnop",
+ \ " ++qrstuvwx",
+ \ " ++yzABCDEF",
+ \ " ++GHIJKLMN",
+ \ " ++OP "
+ \ ]
+ let lines = s:screen_lines2(1, 6, 20)
+ call s:compare_lines(expect, lines)
+ bwipeout!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_buffer.vim b/src/nvim/testdir/test_buffer.vim
index 67be3e6747..9220cc17b9 100644
--- a/src/nvim/testdir/test_buffer.vim
+++ b/src/nvim/testdir/test_buffer.vim
@@ -67,16 +67,7 @@ func Test_bunload_with_offset()
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:')
+ call assert_fails('$bunload', 'E90:')
endfunc
" Test for :buffer, :bnext, :bprevious, :brewind, :blast and :bmodified
@@ -147,6 +138,7 @@ func Test_bdelete_cmd()
%bwipe!
call assert_fails('bdelete 5', 'E516:')
call assert_fails('1,1bdelete 1 2', 'E488:')
+ call assert_fails('bdelete \)', 'E55:')
" Deleting a unlisted and unloaded buffer
edit Xfile1
@@ -154,6 +146,24 @@ func Test_bdelete_cmd()
set nobuflisted
enew
call assert_fails('bdelete ' .. bnr, 'E516:')
+
+ " Deleting more than one buffer
+ new Xbuf1
+ new Xbuf2
+ exe 'bdel ' .. bufnr('Xbuf2') .. ' ' .. bufnr('Xbuf1')
+ call assert_equal(1, winnr('$'))
+ call assert_equal(0, getbufinfo('Xbuf1')[0].loaded)
+ call assert_equal(0, getbufinfo('Xbuf2')[0].loaded)
+
+ " Deleting more than one buffer and an invalid buffer
+ new Xbuf1
+ new Xbuf2
+ let cmd = "exe 'bdel ' .. bufnr('Xbuf2') .. ' xxx ' .. bufnr('Xbuf1')"
+ call assert_fails(cmd, 'E94:')
+ call assert_equal(2, winnr('$'))
+ call assert_equal(1, getbufinfo('Xbuf1')[0].loaded)
+ call assert_equal(0, getbufinfo('Xbuf2')[0].loaded)
+
%bwipe!
endfunc
@@ -168,6 +178,194 @@ func Test_buffer_error()
%bwipe
endfunc
+" Test for the status messages displayed when unloading, deleting or wiping
+" out buffers
+func Test_buffer_statusmsg()
+ CheckEnglish
+ set report=1
+ new Xbuf1
+ new Xbuf2
+ let bnr = bufnr()
+ exe "normal 2\<C-G>"
+ call assert_match('buf ' .. bnr .. ':', v:statusmsg)
+ bunload Xbuf1 Xbuf2
+ call assert_equal('2 buffers unloaded', v:statusmsg)
+ bdel Xbuf1 Xbuf2
+ call assert_equal('2 buffers deleted', v:statusmsg)
+ bwipe Xbuf1 Xbuf2
+ call assert_equal('2 buffers wiped out', v:statusmsg)
+ set report&
+endfunc
+
+" Test for quitting the 'swapfile exists' dialog with the split buffer
+" command.
+func Test_buffer_sbuf_cleanup()
+ call writefile([], 'Xfile')
+ " first open the file in a buffer
+ new Xfile
+ let bnr = bufnr()
+ close
+ " create the swap file
+ call writefile([], '.Xfile.swp')
+ " Remove the catch-all that runtest.vim adds
+ au! SwapExists
+ augroup BufTest
+ au!
+ autocmd SwapExists Xfile let v:swapchoice='q'
+ augroup END
+ exe 'sbuf ' . bnr
+ call assert_equal(1, winnr('$'))
+ call assert_equal(0, getbufinfo('Xfile')[0].loaded)
+
+ " test for :sball
+ sball
+ call assert_equal(1, winnr('$'))
+ call assert_equal(0, getbufinfo('Xfile')[0].loaded)
+
+ %bw!
+ set shortmess+=F
+ let v:statusmsg = ''
+ edit Xfile
+ call assert_equal('', v:statusmsg)
+ call assert_equal(1, winnr('$'))
+ call assert_equal(0, getbufinfo('Xfile')[0].loaded)
+ set shortmess&
+
+ call delete('Xfile')
+ call delete('.Xfile.swp')
+ augroup BufTest
+ au!
+ augroup END
+ augroup! BufTest
+endfunc
+
+" Test for deleting a modified buffer with :confirm
+func Test_bdel_with_confirm()
+ " requires a UI to be active
+ throw 'Skipped: use test/functional/legacy/buffer_spec.lua'
+ CheckUnix
+ CheckNotGui
+ CheckFeature dialog_con
+ new
+ call setline(1, 'test')
+ call assert_fails('bdel', 'E89:')
+ call feedkeys('c', 'L')
+ confirm bdel
+ call assert_equal(2, winnr('$'))
+ call assert_equal(1, &modified)
+ call feedkeys('n', 'L')
+ confirm bdel
+ call assert_equal(1, winnr('$'))
+endfunc
+
+" Test for editing another buffer from a modified buffer with :confirm
+func Test_goto_buf_with_confirm()
+ " requires a UI to be active
+ throw 'Skipped: use test/functional/legacy/buffer_spec.lua'
+ CheckUnix
+ CheckNotGui
+ CheckFeature dialog_con
+ new Xfile
+ enew
+ call setline(1, 'test')
+ call assert_fails('b Xfile', 'E37:')
+ call feedkeys('c', 'L')
+ call assert_fails('confirm b Xfile', 'E37:')
+ call assert_equal(1, &modified)
+ call assert_equal('', @%)
+ call feedkeys('y', 'L')
+ call assert_fails('confirm b Xfile', ['', 'E37:'])
+ call assert_equal(1, &modified)
+ call assert_equal('', @%)
+ call feedkeys('n', 'L')
+ confirm b Xfile
+ call assert_equal('Xfile', @%)
+ close!
+endfunc
+
+" Test for splitting buffer with 'switchbuf'
+func Test_buffer_switchbuf()
+ new Xfile
+ wincmd w
+ set switchbuf=useopen
+ sbuf Xfile
+ call assert_equal(1, winnr())
+ call assert_equal(2, winnr('$'))
+ set switchbuf=usetab
+ tabnew
+ sbuf Xfile
+ call assert_equal(1, tabpagenr())
+ call assert_equal(2, tabpagenr('$'))
+ set switchbuf&
+ %bw
+endfunc
+
+" Test for BufAdd autocommand wiping out the buffer
+func Test_bufadd_autocmd_bwipe()
+ %bw!
+ augroup BufAdd_Wipe
+ au!
+ autocmd BufAdd Xfile %bw!
+ augroup END
+ edit Xfile
+ call assert_equal('', @%)
+ call assert_equal(0, bufexists('Xfile'))
+ augroup BufAdd_Wipe
+ au!
+ augroup END
+ augroup! BufAdd_Wipe
+endfunc
+
+" Test for trying to load a buffer with text locked
+" <C-\>e in the command line is used to lock the text
+func Test_load_buf_with_text_locked()
+ new Xfile1
+ edit Xfile2
+ let cmd = ":\<C-\>eexecute(\"normal \<C-O>\")\<CR>\<C-C>"
+ call assert_fails("call feedkeys(cmd, 'xt')", 'E565:')
+ %bw!
+endfunc
+
+" Test for using CTRL-^ to edit the alternative file keeping the cursor
+" position with 'nostartofline'. Also test using the 'buf' command.
+func Test_buffer_edit_altfile()
+ call writefile(repeat(['one two'], 50), 'Xfile1')
+ call writefile(repeat(['five six'], 50), 'Xfile2')
+ set nosol
+ edit Xfile1
+ call cursor(25, 5)
+ edit Xfile2
+ call cursor(30, 4)
+ exe "normal \<C-^>"
+ call assert_equal([0, 25, 5, 0], getpos('.'))
+ exe "normal \<C-^>"
+ call assert_equal([0, 30, 4, 0], getpos('.'))
+ buf Xfile1
+ call assert_equal([0, 25, 5, 0], getpos('.'))
+ buf Xfile2
+ call assert_equal([0, 30, 4, 0], getpos('.'))
+ set sol&
+ call delete('Xfile1')
+ call delete('Xfile2')
+endfunc
+
+" Test for running the :sball command with a maximum window count and a
+" modified buffer
+func Test_sball_with_count()
+ %bw!
+ edit Xfile1
+ call setline(1, ['abc'])
+ new Xfile2
+ new Xfile3
+ new Xfile4
+ 2sball
+ call assert_equal(bufnr('Xfile4'), winbufnr(1))
+ call assert_equal(bufnr('Xfile1'), winbufnr(2))
+ call assert_equal(0, getbufinfo('Xfile2')[0].loaded)
+ call assert_equal(0, getbufinfo('Xfile3')[0].loaded)
+ %bw!
+endfunc
+
func Test_badd_options()
new SomeNewBuffer
setlocal numberwidth=3
@@ -195,12 +393,12 @@ func Test_buffer_scheme()
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},
+ \ #{id: 'ssb0', name: 'test://xyz/foo/ssb0' , match: 1},
+ \ #{id: 'ssb1', name: 'test+abc://xyz/foo/ssb1', match: 0},
+ \ #{id: 'ssb2', name: 'test_abc://xyz/foo/ssb2', match: 0},
+ \ #{id: 'ssb3', name: 'test-abc://xyz/foo/ssb3', match: 1},
+ \ #{id: 'ssb4', name: '-test://xyz/foo/ssb4' , match: 0},
+ \ #{id: 'ssb5', name: 'test-://xyz/foo/ssb5' , match: 0},
\]
for buf in bufnames
new `=buf.name`
@@ -225,6 +423,32 @@ func Test_buf_pattern_invalid()
vsplit 00000000000000000000000000
silent! buf [0--]\&\zs*\zs*e
bwipe!
+
+ " similar case with different code path
+ split 0
+ edit ÿ
+ silent! buf [0--]\&\zs*\zs*0
+ bwipe!
+endfunc
+
+" Test for the 'maxmem' and 'maxmemtot' options
+func Test_buffer_maxmem()
+ " use 1KB per buffer and 2KB for all the buffers
+ " set maxmem=1 maxmemtot=2
+ new
+ let v:errmsg = ''
+ " try opening some files
+ edit test_arglist.vim
+ call assert_equal('test_arglist.vim', bufname())
+ edit test_eval_stuff.vim
+ call assert_equal('test_eval_stuff.vim', bufname())
+ b test_arglist.vim
+ call assert_equal('test_arglist.vim', bufname())
+ b test_eval_stuff.vim
+ call assert_equal('test_eval_stuff.vim', bufname())
+ close
+ call assert_equal('', v:errmsg)
+ " set maxmem& maxmemtot&
endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_bufline.vim b/src/nvim/testdir/test_bufline.vim
index 579d3a5eb5..0468025f55 100644
--- a/src/nvim/testdir/test_bufline.vim
+++ b/src/nvim/testdir/test_bufline.vim
@@ -2,14 +2,18 @@
source shared.vim
source screendump.vim
+source check.vim
func Test_setbufline_getbufline()
+ " similar to Test_set_get_bufline()
new
let b = bufnr('%')
hide
call assert_equal(0, setbufline(b, 1, ['foo', 'bar']))
call assert_equal(['foo'], getbufline(b, 1))
+ call assert_equal('foo', getbufoneline(b, 1))
call assert_equal(['bar'], getbufline(b, '$'))
+ call assert_equal('bar', getbufoneline(b, '$'))
call assert_equal(['foo', 'bar'], getbufline(b, 1, 2))
exe "bd!" b
call assert_equal([], getbufline(b, 1, 2))
@@ -18,13 +22,36 @@ func Test_setbufline_getbufline()
call setline(1, ['a', 'b', 'c'])
let b = bufnr('%')
wincmd w
+
+ call assert_equal(1, setbufline(b, 5, 'x'))
call assert_equal(1, setbufline(b, 5, ['x']))
- call assert_equal(1, setbufline(bufnr('$') + 1, 1, ['x']))
+ call assert_equal(1, setbufline(b, 5, []))
+ call assert_equal(1, setbufline(b, 5, v:_null_list))
+
+ call assert_equal(1, 'x'->setbufline(bufnr('$') + 1, 1))
+ call assert_equal(1, ['x']->setbufline(bufnr('$') + 1, 1))
+ call assert_equal(1, []->setbufline(bufnr('$') + 1, 1))
+ call assert_equal(1, v:_null_list->setbufline(bufnr('$') + 1, 1))
+
+ call assert_equal(['a', 'b', 'c'], getbufline(b, 1, '$'))
+
call assert_equal(0, setbufline(b, 4, ['d', 'e']))
call assert_equal(['c'], b->getbufline(3))
+ call assert_equal('c', b->getbufoneline(3))
call assert_equal(['d'], getbufline(b, 4))
+ call assert_equal('d', getbufoneline(b, 4))
call assert_equal(['e'], getbufline(b, 5))
+ call assert_equal('e', getbufoneline(b, 5))
call assert_equal([], getbufline(b, 6))
+ call assert_equal([], getbufline(b, 2, 1))
+
+ if has('job')
+ call setbufline(b, 2, [function('eval'), #{key: 123}, test_null_job()])
+ call assert_equal(["function('eval')",
+ \ "{'key': 123}",
+ \ "no process"],
+ \ getbufline(b, 2, 4))
+ endif
exe "bwipe! " . b
endfunc
@@ -71,20 +98,53 @@ func Test_appendbufline()
new
let b = bufnr('%')
hide
+
+ new
+ call setline(1, ['line1', 'line2', 'line3'])
+ normal! 2gggg
+ call assert_equal(2, line("''"))
+
call assert_equal(0, appendbufline(b, 0, ['foo', 'bar']))
call assert_equal(['foo'], getbufline(b, 1))
call assert_equal(['bar'], getbufline(b, 2))
call assert_equal(['foo', 'bar'], getbufline(b, 1, 2))
+ call assert_equal(0, appendbufline(b, 0, 'baz'))
+ call assert_equal(['baz', 'foo', 'bar'], getbufline(b, 1, 3))
+
+ " appendbufline() in a hidden buffer shouldn't move marks in current window.
+ call assert_equal(2, line("''"))
+ bwipe!
+
exe "bd!" b
- call assert_equal([], getbufline(b, 1, 2))
+ call assert_equal([], getbufline(b, 1, 3))
split Xtest
call setline(1, ['a', 'b', 'c'])
let b = bufnr('%')
wincmd w
+
+ call assert_equal(1, appendbufline(b, -1, 'x'))
call assert_equal(1, appendbufline(b, -1, ['x']))
+ call assert_equal(1, appendbufline(b, -1, []))
+ call assert_equal(1, appendbufline(b, -1, v:_null_list))
+
+ call assert_equal(1, appendbufline(b, 4, 'x'))
call assert_equal(1, appendbufline(b, 4, ['x']))
+ call assert_equal(1, appendbufline(b, 4, []))
+ call assert_equal(1, appendbufline(b, 4, v:_null_list))
+
+ call assert_equal(1, appendbufline(1234, 1, 'x'))
call assert_equal(1, appendbufline(1234, 1, ['x']))
+ call assert_equal(1, appendbufline(1234, 1, []))
+ call assert_equal(1, appendbufline(1234, 1, v:_null_list))
+
+ call assert_equal(0, appendbufline(b, 1, []))
+ call assert_equal(0, appendbufline(b, 1, v:_null_list))
+ call assert_equal(1, appendbufline(b, 3, []))
+ call assert_equal(1, appendbufline(b, 3, v:_null_list))
+
+ call assert_equal(['a', 'b', 'c'], getbufline(b, 1, '$'))
+
call assert_equal(0, appendbufline(b, 3, ['d', 'e']))
call assert_equal(['c'], getbufline(b, 3))
call assert_equal(['d'], getbufline(b, 4))
@@ -98,10 +158,21 @@ func Test_deletebufline()
let b = bufnr('%')
call setline(1, ['aaa', 'bbb', 'ccc'])
hide
+
+ new
+ call setline(1, ['line1', 'line2', 'line3'])
+ normal! 2gggg
+ call assert_equal(2, line("''"))
+
call assert_equal(0, deletebufline(b, 2))
call assert_equal(['aaa', 'ccc'], getbufline(b, 1, 2))
call assert_equal(0, deletebufline(b, 2, 8))
call assert_equal(['aaa'], getbufline(b, 1, 2))
+
+ " deletebufline() in a hidden buffer shouldn't move marks in current window.
+ call assert_equal(2, line("''"))
+ bwipe!
+
exe "bd!" b
call assert_equal(1, b->deletebufline(1))
@@ -130,9 +201,8 @@ func Test_deletebufline()
endfunc
func Test_appendbufline_redraw()
- if !CanRunVimInTerminal()
- throw 'Skipped: cannot make screendumps'
- endif
+ CheckScreendump
+
let lines =<< trim END
new foo
let winnr = 'foo'->bufwinnr()
@@ -187,4 +257,40 @@ func Test_deletebufline_select_mode()
bwipe!
endfunc
+func Test_setbufline_startup_nofile()
+ let before =<< trim [CODE]
+ set shortmess+=F
+ file Xresult
+ set buftype=nofile
+ call setbufline('', 1, 'success')
+ [CODE]
+ let after =<< trim [CODE]
+ set buftype=
+ write
+ quit
+ [CODE]
+
+ if !RunVim(before, after, '--clean')
+ return
+ endif
+ call assert_equal(['success'], readfile('Xresult'))
+ call delete('Xresult')
+endfunc
+
+" Test that setbufline(), appendbufline() and deletebufline() should fail and
+" return 1 when "textlock" is active.
+func Test_change_bufline_with_textlock()
+ new
+ inoremap <buffer> <expr> <F2> setbufline('', 1, '')
+ call assert_fails("normal a\<F2>", 'E565:')
+ call assert_equal('1', getline(1))
+ inoremap <buffer> <expr> <F2> appendbufline('', 1, '')
+ call assert_fails("normal a\<F2>", 'E565:')
+ call assert_equal('11', getline(1))
+ inoremap <buffer> <expr> <F2> deletebufline('', 1)
+ call assert_fails("normal a\<F2>", 'E565:')
+ call assert_equal('111', getline(1))
+ 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 a1e53df774..2b37f2c7c0 100644
--- a/src/nvim/testdir/test_cd.vim
+++ b/src/nvim/testdir/test_cd.vim
@@ -5,7 +5,7 @@ source check.vim
func Test_cd_large_path()
" This used to crash with a heap write overflow.
- call assert_fails('cd ' . repeat('x', 5000), 'E472:')
+ call assert_fails('cd ' . repeat('x', 5000), 'E344:')
endfunc
func Test_cd_up_and_down()
@@ -45,9 +45,7 @@ func Test_cd_minus()
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_fails('cd /nonexistent', 'E344:')
call assert_equal(path, getcwd())
cd -
call assert_equal(path_dotdot, getcwd())
@@ -68,30 +66,6 @@ func Test_cd_minus()
call delete('Xresult')
endfunc
-func Test_cd_with_cpo_chdir()
- e Xfoo
- call setline(1, 'foo')
- let path = getcwd()
- " set cpo+=.
-
- " :cd should fail when buffer is modified and 'cpo' contains dot.
- " call assert_fails('cd ..', 'E747:')
- call assert_equal(path, getcwd())
-
- " :cd with exclamation mark should succeed.
- cd! ..
- call assert_notequal(path, getcwd())
-
- " :cd should succeed when buffer has been written.
- w!
- exe 'cd ' .. fnameescape(path)
- call assert_equal(path, getcwd())
-
- call delete('Xfoo')
- set cpo&
- bw!
-endfunc
-
" Test for chdir()
func Test_chdir_func()
let topdir = getcwd()
@@ -113,7 +87,7 @@ func Test_chdir_func()
call assert_equal('z', fnamemodify(3->getcwd(2), ':t'))
tabnext | wincmd t
call assert_match('^\[tabpage\] .*/y$', trim(execute('verbose pwd')))
- call chdir('..')
+ eval '..'->chdir()
call assert_equal('Xdir', fnamemodify(getcwd(1, 2), ':t'))
call assert_equal('Xdir', fnamemodify(getcwd(2, 2), ':t'))
call assert_equal('z', fnamemodify(getcwd(3, 2), ':t'))
@@ -127,7 +101,7 @@ func Test_chdir_func()
call assert_equal('testdir', fnamemodify(getcwd(1, 1), ':t'))
" Error case
- call assert_fails("call chdir('dir-abcd')", 'E472:')
+ call assert_fails("call chdir('dir-abcd')", 'E344:')
silent! let d = chdir("dir_abcd")
call assert_equal("", d)
" Should not crash
@@ -259,6 +233,8 @@ endfunc
func Test_getcwd_actual_dir()
CheckFunction test_autochdir
+ CheckOption autochdir
+
let startdir = getcwd()
call mkdir('Xactual')
call test_autochdir()
diff --git a/src/nvim/testdir/test_charsearch.vim b/src/nvim/testdir/test_charsearch.vim
index d386d74f8d..54e0a62ce5 100644
--- a/src/nvim/testdir/test_charsearch.vim
+++ b/src/nvim/testdir/test_charsearch.vim
@@ -43,36 +43,6 @@ func Test_charsearch()
enew!
endfunc
-" Test for t,f,F,T movement commands and 'cpo-;' setting
-func Test_search_cmds()
- enew!
- call append(0, ["aaa two three four", " zzz", "yyy ",
- \ "bbb yee yoo four", "ccc two three four",
- \ "ddd yee yoo four"])
- set cpo-=;
- 1
- normal! 0tt;D
- 2
- normal! 0fz;D
- 3
- normal! $Fy;D
- 4
- normal! $Ty;D
- set cpo+=;
- 5
- normal! 0tt;;D
- 6
- normal! $Ty;;D
-
- call assert_equal('aaa two', getline(1))
- call assert_equal(' z', getline(2))
- call assert_equal('y', getline(3))
- call assert_equal('bbb y', getline(4))
- call assert_equal('ccc', getline(5))
- 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
@@ -81,7 +51,7 @@ func Test_csearch_virtualedit()
normal! tb
call assert_equal([0, 1, 2, 6], getpos('.'))
set virtualedit&
- close!
+ bw!
endfunc
" Test for character search failure in latin1 encoding
@@ -95,7 +65,34 @@ func Test_charsearch_latin1()
call assert_beeps('normal $Fz')
call assert_beeps('normal $Tx')
let &encoding = save_enc
- close!
+ bw!
+endfunc
+
+" Test for using character search to find a multibyte character with composing
+" characters.
+func Test_charsearch_composing_char()
+ new
+ call setline(1, "one two thq\u0328\u0301r\u0328\u0301ree")
+ call feedkeys("fr\u0328\u0301", 'xt')
+ call assert_equal([0, 1, 16, 0, 12], getcurpos())
+
+ " use character search with a multi-byte character followed by a
+ " non-composing character
+ call setline(1, "abc deȉf ghi")
+ call feedkeys("ggcf\u0209\u0210", 'xt')
+ call assert_equal("\u0210f ghi", getline(1))
+ bw!
+endfunc
+
+" Test for character search with 'hkmap'
+func Test_charsearch_hkmap()
+ new
+ set hkmap
+ call setline(1, "ùðáâ÷ëòéïçìêöî")
+ call feedkeys("fë", 'xt')
+ call assert_equal([0, 1, 11, 0, 6], getcurpos())
+ set hkmap&
+ bw!
endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_checkpath.vim b/src/nvim/testdir/test_checkpath.vim
index eff30cf205..f6a3bbce08 100644
--- a/src/nvim/testdir/test_checkpath.vim
+++ b/src/nvim/testdir/test_checkpath.vim
@@ -102,3 +102,20 @@ func Test_checkpath3()
set include&
set includeexpr&
endfunc
+
+" Test for invalid regex in 'include' and 'define' options
+func Test_checkpath_errors()
+ let save_include = &include
+ set include=\\%(
+ call assert_fails('checkpath', 'E53:')
+ let &include = save_include
+
+ let save_define = &define
+ set define=\\%(
+ call assert_fails('dsearch abc', 'E53:')
+ let &define = save_define
+
+ call assert_fails('psearch \%(', 'E53:')
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_clientserver.vim b/src/nvim/testdir/test_clientserver.vim
index edf36b413b..d8b29f6c42 100644
--- a/src/nvim/testdir/test_clientserver.vim
+++ b/src/nvim/testdir/test_clientserver.vim
@@ -2,15 +2,18 @@
source check.vim
CheckFeature job
+
+if !has('clientserver')
+ call assert_fails('call remote_startserver("local")', 'E942:')
+endif
+
CheckFeature clientserver
source shared.vim
func Check_X11_Connection()
if has('x11')
- if empty($DISPLAY)
- throw 'Skipped: $DISPLAY is not set'
- endif
+ CheckEnv DISPLAY
try
call remote_send('xxx', '')
catch
@@ -59,15 +62,16 @@ func Test_client_server()
" the GUI and check that the remote command still works.
" Need to wait for the GUI to start up, otherwise the send hangs in trying
" to send to the terminal window.
- if has('gui_athena') || has('gui_motif')
- " For those GUIs, ignore the 'failed to create input context' error.
+ if has('gui_motif')
+ " For this GUI ignore the 'failed to create input context' error.
call remote_send(name, ":call test_ignore_error('E285') | gui -f\<CR>")
else
call remote_send(name, ":gui -f\<CR>")
endif
" Wait for the server to be up and answering requests.
- sleep 100m
- call WaitForAssert({-> assert_true(name->remote_expr("v:version", "", 1) != "")})
+ " When using valgrind this can be very, very slow.
+ sleep 1
+ call WaitForAssert({-> assert_match('\d', name->remote_expr("v:version", "", 1))}, 10000)
call remote_send(name, ":let testvar = 'maybe'\<CR>")
call WaitForAssert({-> assert_equal('maybe', remote_expr(name, "testvar", "", 2))})
@@ -83,6 +87,7 @@ func Test_client_server()
call writefile(['one', 'two'], 'Xclientfile')
call system(cmd)
call WaitForAssert({-> assert_equal('two', remote_expr(name, "getline(2)", "", 2))})
+ call delete('Xclientfile')
" Expression evaluated locally.
if v:servername == ''
@@ -96,7 +101,7 @@ func Test_client_server()
call remote_send(v:servername, ":let g:testvar2 = 75\<CR>")
call feedkeys('', 'x')
call assert_equal(75, g:testvar2)
- call assert_fails('let v = remote_expr(v:servername, "/2")', 'E449:')
+ call assert_fails('let v = remote_expr(v:servername, "/2")', ['E15:.*/2'])
call remote_send(name, ":call server2client(expand('<client>'), 'got it')\<CR>", 'g:myserverid')
call assert_equal('got it', g:myserverid->remote_read(2))
@@ -136,19 +141,19 @@ func Test_client_server()
" Edit multiple files using --remote
call system(cmd .. ' --remote Xfile1 Xfile2 Xfile3')
- call assert_equal("Xfile1\nXfile2\nXfile3\n", remote_expr(name, 'argv()'))
+ call assert_match(".*Xfile1\n.*Xfile2\n.*Xfile3\n", remote_expr(name, 'argv()'))
eval name->remote_send(":%bw!\<CR>")
" Edit files in separate tab pages
call system(cmd .. ' --remote-tab Xfile1 Xfile2 Xfile3')
- call assert_equal('3', remote_expr(name, 'tabpagenr("$")'))
- call assert_equal('Xfile2', remote_expr(name, 'bufname(tabpagebuflist(2)[0])'))
+ call WaitForAssert({-> assert_equal('3', remote_expr(name, 'tabpagenr("$")'))})
+ call assert_match('.*\<Xfile2', remote_expr(name, 'bufname(tabpagebuflist(2)[0])'))
eval name->remote_send(":%bw!\<CR>")
" Edit a file using --remote-wait
eval name->remote_send(":source $VIMRUNTIME/plugin/rrhelper.vim\<CR>")
call system(cmd .. ' --remote-wait +enew Xfile1')
- call assert_equal("Xfile1", remote_expr(name, 'bufname("#")'))
+ call assert_match('.*\<Xfile1', remote_expr(name, 'bufname("#")'))
eval name->remote_send(":%bw!\<CR>")
" Edit files using --remote-tab-wait
@@ -176,8 +181,12 @@ func Test_client_server()
endif
endtry
+ call assert_fails('call remote_startserver([])', 'E730:')
call assert_fails("let x = remote_peek([])", 'E730:')
- call assert_fails("let x = remote_read('vim10')", 'E277:')
+ call assert_fails("let x = remote_read('vim10')",
+ \ has('unix') ? ['E573:.*vim10'] : 'E277:')
+ call assert_fails("call server2client('abc', 'xyz')",
+ \ has('unix') ? ['E573:.*abc'] : 'E258:')
endfunc
" Uncomment this line to get a debugging log
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index 35886d42c5..cf1d56ae38 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -3,10 +3,24 @@
source check.vim
source screendump.vim
source view_util.vim
+source shared.vim
+
+func SetUp()
+ func SaveLastScreenLine()
+ let g:Sline = Screenline(&lines - 1)
+ return ''
+ endfunc
+ cnoremap <expr> <F4> SaveLastScreenLine()
+endfunc
+
+func TearDown()
+ delfunc SaveLastScreenLine
+ cunmap <F4>
+endfunc
func Test_complete_tab()
call writefile(['testfile'], 'Xtestfile')
- call feedkeys(":e Xtestf\t\r", "tx")
+ call feedkeys(":e Xtest\t\r", "tx")
call assert_equal('testfile', getline(1))
" Pressing <Tab> after '%' completes the current file, also on MS-Windows
@@ -24,6 +38,47 @@ func Test_complete_list()
" used.
call feedkeys(":chistory \<C-D>\<C-B>\"\<CR>", 'xt')
call assert_equal("\"chistory \<C-D>", @:)
+
+ " Test for displaying the tail of the completion matches
+ set wildmode=longest,full
+ call mkdir('Xtest')
+ call writefile([], 'Xtest/a.c')
+ call writefile([], 'Xtest/a.h')
+ let g:Sline = ''
+ call feedkeys(":e Xtest/\<C-D>\<F4>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('a.c a.h', g:Sline)
+ call assert_equal('"e Xtest/', @:)
+ if has('win32')
+ " Test for 'completeslash'
+ set completeslash=backslash
+ call feedkeys(":e Xtest\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e Xtest\', @:)
+ call feedkeys(":e Xtest/\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e Xtest\a.', @:)
+ set completeslash=slash
+ call feedkeys(":e Xtest\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e Xtest/', @:)
+ call feedkeys(":e Xtest\\\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e Xtest/a.', @:)
+ set completeslash&
+ endif
+
+ " Test for displaying the tail with wildcards
+ let g:Sline = ''
+ call feedkeys(":e Xtes?/\<C-D>\<F4>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('Xtest/a.c Xtest/a.h', g:Sline)
+ call assert_equal('"e Xtes?/', @:)
+ let g:Sline = ''
+ call feedkeys(":e Xtes*/\<C-D>\<F4>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('Xtest/a.c Xtest/a.h', g:Sline)
+ call assert_equal('"e Xtes*/', @:)
+ let g:Sline = ''
+ call feedkeys(":e Xtes[/\<C-D>\<F4>\<C-B>\"\<CR>", 'xt')
+ call assert_equal(':e Xtes[/', g:Sline)
+ call assert_equal('"e Xtes[/', @:)
+
+ call delete('Xtest', 'rf')
+ set wildmode&
endfunc
func Test_complete_wildmenu()
@@ -88,14 +143,25 @@ func Test_complete_wildmenu()
call assert_equal('"e Xtestfile3 Xtestfile4', @:)
cd -
+ " test for wildmenumode()
+ cnoremap <expr> <F2> wildmenumode()
+ call feedkeys(":cd Xdir\<Tab>\<F2>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"cd Xdir1/0', @:)
+ call feedkeys(":e Xdir1/\<Tab>\<F2>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"e Xdir1/Xdir2/1', @:)
+ cunmap <F2>
+
+ " Test for canceling the wild menu by pressing <PageDown> or <PageUp>.
+ " After this pressing <Left> or <Right> should not change the selection.
+ call feedkeys(":sign \<Tab>\<PageDown>\<Left>\<Right>\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"sign define', @:)
+ call histadd('cmd', 'TestWildMenu')
+ call feedkeys(":sign \<Tab>\<PageUp>\<Left>\<Right>\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"TestWildMenu', @:)
+
" cleanup
%bwipe
- call delete('Xdir1/Xdir2/Xtestfile4')
- call delete('Xdir1/Xdir2/Xtestfile3')
- call delete('Xdir1/Xtestfile2')
- call delete('Xdir1/Xtestfile1')
- call delete('Xdir1/Xdir2', 'd')
- call delete('Xdir1', 'd')
+ call delete('Xdir1', 'rf')
set nowildmenu
endfunc
@@ -126,13 +192,74 @@ func Test_wildmenu_screendump()
call delete('XTest_wildmenu')
endfunc
+func Test_redraw_in_autocmd()
+ CheckScreendump
+
+ let lines =<< trim END
+ set cmdheight=2
+ autocmd CmdlineChanged * redraw
+ END
+ call writefile(lines, 'XTest_redraw', 'D')
+
+ let buf = RunVimInTerminal('-S XTest_redraw', {'rows': 8})
+ call term_sendkeys(buf, ":for i in range(3)\<CR>")
+ call VerifyScreenDump(buf, 'Test_redraw_in_autocmd_1', {})
+
+ call term_sendkeys(buf, "let i =")
+ call VerifyScreenDump(buf, 'Test_redraw_in_autocmd_2', {})
+
+ " clean up
+ call term_sendkeys(buf, "\<CR>")
+ call StopVimInTerminal(buf)
+endfunc
+
+func Test_redrawstatus_in_autocmd()
+ CheckScreendump
+
+ let lines =<< trim END
+ set laststatus=2
+ set statusline=%=:%{getcmdline()}
+ autocmd CmdlineChanged * redrawstatus
+ END
+ call writefile(lines, 'XTest_redrawstatus', 'D')
+
+ let buf = RunVimInTerminal('-S XTest_redrawstatus', {'rows': 8})
+ " :redrawstatus is postponed if messages have scrolled
+ call term_sendkeys(buf, ":echo \"one\\ntwo\\nthree\\nfour\"\<CR>")
+ call term_sendkeys(buf, ":foobar")
+ call VerifyScreenDump(buf, 'Test_redrawstatus_in_autocmd_1', {})
+ " it is not postponed if messages have not scrolled
+ call term_sendkeys(buf, "\<Esc>:for in in range(3)")
+ call VerifyScreenDump(buf, 'Test_redrawstatus_in_autocmd_2', {})
+ " with cmdheight=1 messages have scrolled when typing :endfor
+ call term_sendkeys(buf, "\<CR>:endfor")
+ call VerifyScreenDump(buf, 'Test_redrawstatus_in_autocmd_3', {})
+ call term_sendkeys(buf, "\<CR>:set cmdheight=2\<CR>")
+ " with cmdheight=2 messages haven't scrolled when typing :for or :endfor
+ call term_sendkeys(buf, ":for in in range(3)")
+ call VerifyScreenDump(buf, 'Test_redrawstatus_in_autocmd_4', {})
+ call term_sendkeys(buf, "\<CR>:endfor")
+ call VerifyScreenDump(buf, 'Test_redrawstatus_in_autocmd_5', {})
+
+ " clean up
+ call term_sendkeys(buf, "\<CR>")
+ call StopVimInTerminal(buf)
+endfunc
+
func Test_changing_cmdheight()
CheckScreendump
let lines =<< trim END
set cmdheight=1 laststatus=2
+ func EchoTwo()
+ set laststatus=2
+ set cmdheight=5
+ echo 'foo'
+ echo 'bar'
+ set cmdheight=1
+ endfunc
END
- call writefile(lines, 'XTest_cmdheight')
+ call writefile(lines, 'XTest_cmdheight', 'D')
let buf = RunVimInTerminal('-S XTest_cmdheight', {'rows': 8})
call term_sendkeys(buf, ":resize -3\<CR>")
@@ -150,20 +277,30 @@ func Test_changing_cmdheight()
call term_sendkeys(buf, ":set cmdheight-=2\<CR>")
call VerifyScreenDump(buf, 'Test_changing_cmdheight_4', {})
- " reducing window size and then setting cmdheight
+ " reducing window size and then setting cmdheight
call term_sendkeys(buf, ":resize -1\<CR>")
call term_sendkeys(buf, ":set cmdheight=1\<CR>")
call VerifyScreenDump(buf, 'Test_changing_cmdheight_5', {})
+ " setting 'cmdheight' works after outputting two messages
+ call term_sendkeys(buf, ":call EchoTwo()\<CR>")
+ call VerifyScreenDump(buf, 'Test_changing_cmdheight_6', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+endfunc
+
+func Test_cmdheight_tabline()
+ CheckScreendump
+
+ let buf = RunVimInTerminal('-c "set ls=2" -c "set stal=2" -c "set cmdheight=1"', {'rows': 6})
+ call VerifyScreenDump(buf, 'Test_cmdheight_tabline_1', {})
+
" clean up
call StopVimInTerminal(buf)
- call delete('XTest_cmdheight')
endfunc
func Test_map_completion()
- if !has('cmdline_compl')
- return
- endif
call feedkeys(":map <unique> <si\<Tab>\<Home>\"\<CR>", 'xt')
call assert_equal('"map <unique> <silent>', getreg(':'))
call feedkeys(":map <script> <un\<Tab>\<Home>\"\<CR>", 'xt')
@@ -234,29 +371,30 @@ func Test_map_completion()
unmap <Left>
" set cpo-=k
+ call assert_fails('call feedkeys(":map \\\\%(\<Tab>\<Home>\"\<CR>", "xt")', 'E53:')
+
unmap <Middle>x
set cpo&vim
endfunc
func Test_match_completion()
- if !has('cmdline_compl')
- return
- endif
hi Aardig ctermfg=green
- call feedkeys(":match \<Tab>\<Home>\"\<CR>", 'xt')
- call assert_equal('"match Aardig', getreg(':'))
+ " call feedkeys(":match \<Tab>\<Home>\"\<CR>", 'xt')
+ call feedkeys(":match A\<Tab>\<Home>\"\<CR>", 'xt')
+ call assert_equal('"match Aardig', @:)
call feedkeys(":match \<S-Tab>\<Home>\"\<CR>", 'xt')
- call assert_equal('"match none', getreg(':'))
+ call assert_equal('"match none', @:)
+ call feedkeys(":match | chist\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"match | chistory', @:)
endfunc
func Test_highlight_completion()
- if !has('cmdline_compl')
- return
- endif
hi Aardig ctermfg=green
- call feedkeys(":hi \<Tab>\<Home>\"\<CR>", 'xt')
+ " call feedkeys(":hi \<Tab>\<Home>\"\<CR>", 'xt')
+ call feedkeys(":hi A\<Tab>\<Home>\"\<CR>", 'xt')
call assert_equal('"hi Aardig', getreg(':'))
- call feedkeys(":hi default \<Tab>\<Home>\"\<CR>", 'xt')
+ " call feedkeys(":hi default \<Tab>\<Home>\"\<CR>", 'xt')
+ call feedkeys(":hi default A\<Tab>\<Home>\"\<CR>", 'xt')
call assert_equal('"hi default Aardig', getreg(':'))
call feedkeys(":hi clear Aa\<Tab>\<Home>\"\<CR>", 'xt')
call assert_equal('"hi clear Aardig', getreg(':'))
@@ -276,39 +414,7 @@ func Test_highlight_completion()
call assert_equal([], getcompletion('A', 'highlight'))
endfunc
-func Test_expr_completion()
- if !has('cmdline_compl')
- return
- endif
- for cmd in [
- \ 'let a = ',
- \ 'const a = ',
- \ 'if',
- \ 'elseif',
- \ 'while',
- \ 'for',
- \ 'echo',
- \ 'echon',
- \ 'execute',
- \ 'echomsg',
- \ 'echoerr',
- \ 'call',
- \ 'return',
- \ 'cexpr',
- \ 'caddexpr',
- \ 'cgetexpr',
- \ 'lexpr',
- \ 'laddexpr',
- \ 'lgetexpr']
- call feedkeys(":" . cmd . " getl\<Tab>\<Home>\"\<CR>", 'xt')
- call assert_equal('"' . cmd . ' getline(', getreg(':'))
- endfor
-endfunc
-
func Test_getcompletion()
- if !has('cmdline_compl')
- return
- endif
let groupcount = len(getcompletion('', 'event'))
call assert_true(groupcount > 0)
let matchcount = len('File'->getcompletion('event'))
@@ -323,6 +429,7 @@ func Test_getcompletion()
call assert_true(matchcount > 0)
let matchcount = len(getcompletion('File.', 'menu'))
call assert_true(matchcount > 0)
+ source $VIMRUNTIME/delmenu.vim
endif
let l = getcompletion('v:n', 'var')
@@ -333,6 +440,8 @@ func Test_getcompletion()
args a.c b.c
let l = getcompletion('', 'arglist')
call assert_equal(['a.c', 'b.c'], l)
+ let l = getcompletion('a.', 'buffer')
+ call assert_equal(['a.c'], l)
%argdelete
let l = getcompletion('', 'augroup')
@@ -388,6 +497,11 @@ func Test_getcompletion()
let l = getcompletion('run', 'file', 1)
call assert_true(index(l, 'runtest.vim') < 0)
set wildignore&
+ " Directory name with space character
+ call mkdir('Xdir with space')
+ call assert_equal(['Xdir with space/'], getcompletion('Xdir\ w', 'shellcmd'))
+ call assert_equal(['./Xdir with space/'], getcompletion('./Xdir', 'shellcmd'))
+ call delete('Xdir with space', 'd')
let l = getcompletion('ha', 'filetype')
call assert_true(index(l, 'hamster') >= 0)
@@ -462,11 +576,17 @@ func Test_getcompletion()
call assert_equal(cmds, l)
let l = getcompletion('list ', 'sign')
call assert_equal(['Testing'], l)
+ let l = getcompletion('de*', 'sign')
+ call assert_equal(['define'], l)
+ let l = getcompletion('p?', 'sign')
+ call assert_equal(['place'], l)
+ let l = getcompletion('j.', 'sign')
+ call assert_equal(['jump'], l)
endif
" Command line completion tests
let l = getcompletion('cd ', 'cmdline')
- call assert_true(index(l, 'sautest/') >= 0)
+ call assert_true(index(l, 'samples/') >= 0)
let l = getcompletion('cd NoMatch', 'cmdline')
call assert_equal([], l)
let l = getcompletion('let v:n', 'cmdline')
@@ -514,10 +634,39 @@ func Test_getcompletion()
call delete('Xtags')
set tags&
+ edit a~b
+ enew
+ call assert_equal(['a~b'], getcompletion('a~', 'buffer'))
+ bw a~b
+
+ if has('unix')
+ edit Xtest\
+ enew
+ call assert_equal(['Xtest\'], getcompletion('Xtest\', 'buffer'))
+ bw Xtest\
+ endif
+
+ call assert_fails("call getcompletion('\\\\@!\\\\@=', 'buffer')", 'E871:')
call assert_fails('call getcompletion("", "burp")', 'E475:')
call assert_fails('call getcompletion("abc", [])', 'E475:')
endfunc
+" Test for getcompletion() with "fuzzy" in 'wildoptions'
+func Test_getcompletion_wildoptions()
+ let save_wildoptions = &wildoptions
+ set wildoptions&
+ let l = getcompletion('space', 'option')
+ call assert_equal([], l)
+ let l = getcompletion('ier', 'command')
+ call assert_equal([], l)
+ set wildoptions=fuzzy
+ let l = getcompletion('space', 'option')
+ call assert_true(index(l, 'backspace') >= 0)
+ let l = getcompletion('ier', 'command')
+ call assert_true(index(l, 'compiler') >= 0)
+ let &wildoptions = save_wildoptions
+endfunc
+
func Test_fullcommand()
let tests = {
\ '': '',
@@ -590,7 +739,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 a/b/fileXname', getreg(':'))
+ call assert_equal('find a/b/fileXname', @:)
bwipe!
call delete('a', 'rf')
endfunc
@@ -738,7 +887,31 @@ func Test_cmdline_complete_user_cmd()
call assert_equal('"Foo blue', @:)
call feedkeys(":Foo b\<Tab>\<Home>\"\<cr>", 'tx')
call assert_equal('"Foo blue', @:)
+ call feedkeys(":Foo a b\<Tab>\<Home>\"\<cr>", 'tx')
+ call assert_equal('"Foo a blue', @:)
+ call feedkeys(":Foo b\\\<Tab>\<Home>\"\<cr>", 'tx')
+ call assert_equal('"Foo b\', @:)
+ call feedkeys(":Foo b\\x\<Tab>\<Home>\"\<cr>", 'tx')
+ call assert_equal('"Foo b\x', @:)
delcommand Foo
+
+ redraw
+ call assert_equal('~', Screenline(&lines - 1))
+ command! FooOne :
+ command! FooTwo :
+
+ set nowildmenu
+ call feedkeys(":Foo\<Tab>\<Home>\"\<cr>", 'tx')
+ call assert_equal('"FooOne', @:)
+ call assert_equal('~', Screenline(&lines - 1))
+
+ call feedkeys(":Foo\<S-Tab>\<Home>\"\<cr>", 'tx')
+ call assert_equal('"FooTwo', @:)
+ call assert_equal('~', Screenline(&lines - 1))
+
+ delcommand FooOne
+ delcommand FooTwo
+ set wildmenu&
endfunc
func Test_complete_user_cmd()
@@ -809,7 +982,7 @@ func Test_cmdline_complete_bang()
endif
endfunc
-funct Test_cmdline_complete_languages()
+func Test_cmdline_complete_languages()
let lang = substitute(execute('language time'), '.*"\(.*\)"$', '\1', '')
call assert_equal(lang, v:lc_time)
@@ -846,10 +1019,8 @@ 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
@@ -926,6 +1097,10 @@ func Test_cmdline_complete_various()
call feedkeys(":all abc\<C-A>\<C-B>\"\<CR>", 'xt')
call assert_equal("\"all abc\<C-A>", @:)
+ " completion for :wincmd with :horizontal modifier
+ call feedkeys(":horizontal wincm\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"horizontal wincmd", @:)
+
" completion for a command with a command modifier
call feedkeys(":topleft new\<C-A>\<C-B>\"\<CR>", 'xt')
call assert_equal("\"topleft new", @:)
@@ -934,14 +1109,6 @@ func Test_cmdline_complete_various()
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>", @:)
@@ -1001,7 +1168,7 @@ func Test_cmdline_complete_various()
map <F3> :ls<CR>
com -nargs=* -complete=mapping MyCmd
call feedkeys(":MyCmd <F\<C-A>\<C-B>\"\<CR>", 'xt')
- call assert_equal('"MyCmd <F3>', @:)
+ call assert_equal('"MyCmd <F3> <F4>', @:)
mapclear
delcom MyCmd
@@ -1025,9 +1192,77 @@ func Test_cmdline_complete_various()
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("', @=)
+ " completion for :language command with an invalid argument
+ call feedkeys(":language dummy \t\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"language dummy \t", @:)
+
+ " completion for commands after a :global command
+ call feedkeys(":g/a\\xb/clearj\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"g/a\xb/clearjumps', @:)
+
+ " completion with ambiguous user defined commands
+ com TCmd1 echo 'TCmd1'
+ com TCmd2 echo 'TCmd2'
+ call feedkeys(":TCmd \t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"TCmd ', @:)
+ delcom TCmd1
+ delcom TCmd2
+
+ " completion after a range followed by a pipe (|) character
+ call feedkeys(":1,10 | chist\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"1,10 | chistory', @:)
+
+ " completion after a :global command
+ call feedkeys(":g/a/chist\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"g/a/chistory', @:)
+ call feedkeys(":g/a\\/chist\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"g/a\\/chist\t", @:)
+
+ " use <Esc> as the 'wildchar' for completion
+ set wildchar=<Esc>
+ call feedkeys(":g/a\\xb/clearj\<Esc>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"g/a\xb/clearjumps', @:)
+ " pressing <esc> twice should cancel the command
+ call feedkeys(":chist\<Esc>\<Esc>", 'xt')
+ call assert_equal('"g/a\xb/clearjumps', @:)
+ set wildchar&
+
+ if has('unix')
+ " should be able to complete a file name that starts with a '~'.
+ call writefile([], '~Xtest')
+ call feedkeys(":e \\~X\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e \~Xtest', @:)
+ call delete('~Xtest')
+
+ " should be able to complete a file name that has a '*'
+ call writefile([], 'Xx*Yy')
+ call feedkeys(":e Xx\*\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e Xx\*Yy', @:)
+ call delete('Xx*Yy')
+
+ " use a literal star
+ call feedkeys(":e \\*\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e \*', @:)
+ endif
+
+ call feedkeys(":py3f\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"py3file', @:)
+endfunc
+
+" Test for 'wildignorecase'
+func Test_cmdline_wildignorecase()
+ CheckUnix
+ call writefile([], 'XTEST')
+ set wildignorecase
+ call feedkeys(":e xt\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e XTEST', @:)
+ call assert_equal(['XTEST'], getcompletion('xt', 'file'))
+ let g:Sline = ''
+ call feedkeys(":e xt\<C-d>\<F4>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e xt', @:)
+ call assert_equal('XTEST', g:Sline)
+ set wildignorecase&
+ call delete('XTEST')
endfunc
func Test_cmdline_write_alternatefile()
@@ -1051,6 +1286,18 @@ func Test_cmdline_write_alternatefile()
bw!
endfunc
+func Test_cmdline_expand_cur_alt_file()
+ enew
+ file http://some.com/file.txt
+ call feedkeys(":e %\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e http://some.com/file.txt', @:)
+ edit another
+ call feedkeys(":e #\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e http://some.com/file.txt', @:)
+ bwipe
+ bwipe http://some.com/file.txt
+endfunc
+
" using a leading backslash here
set cpo+=C
@@ -1072,7 +1319,7 @@ func Test_cmdline_search_range()
call assert_equal('B', getline(2))
let @/ = 'apple'
- call assert_fails('\/print', 'E486:')
+ call assert_fails('\/print', ['E486:.*apple'])
bwipe!
endfunc
@@ -1082,7 +1329,7 @@ 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_equal("'", @:)
call assert_fails("',print", 'E78:')
endfunc
@@ -1188,6 +1435,11 @@ func Test_verbosefile()
let log = readfile('Xlog')
call assert_match("foo\nbar", join(log, "\n"))
call delete('Xlog')
+ call mkdir('Xdir')
+ if !has('win32') " FIXME: no error on Windows, libuv bug?
+ call assert_fails('set verbosefile=Xdir', ['E484:.*Xdir', 'E474:'])
+ endif
+ call delete('Xdir', 'd')
endfunc
func Test_verbose_option()
@@ -1260,6 +1512,16 @@ func Test_cmdline_overstrike()
let &encoding = encoding_save
endfunc
+func Test_cmdwin_bug()
+ let winid = win_getid()
+ sp
+ try
+ call feedkeys("q::call win_gotoid(" .. winid .. ")\<CR>:q\<CR>", 'x!')
+ catch /^Vim\%((\a\+)\)\=:E11/
+ endtry
+ bw!
+endfunc
+
func Test_cmdwin_restore()
CheckScreendump
@@ -1414,6 +1676,7 @@ func Test_cmdline_expand_special()
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
@@ -1428,7 +1691,7 @@ 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:')
+ call assert_fails('call feedkeys("q/:qall\<CR>", "xt")', ['E37:', 'E162:'])
close!
call feedkeys("q/:close\<CR>", "xt")
call assert_equal(1, winnr('$'))
@@ -1442,16 +1705,36 @@ 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:')
+ call assert_fails("silent norm q/g :I\<Esc>", 'E11:')
tabclose!
endfunc
+func Test_cmdwin_interrupted()
+ CheckScreendump
+
+ " aborting the :smile output caused the cmdline window to use the current
+ " buffer.
+ let lines =<< trim [SCRIPT]
+ au WinNew * smile
+ [SCRIPT]
+ call writefile(lines, 'XTest_cmdwin')
+
+ let buf = RunVimInTerminal('-S XTest_cmdwin', {'rows': 18})
+ " open cmdwin
+ call term_sendkeys(buf, "q:")
+ call WaitForAssert({-> assert_match('-- More --', term_getline(buf, 18))})
+ " quit more prompt for :smile command
+ call term_sendkeys(buf, "q")
+ call WaitForAssert({-> assert_match('^$', term_getline(buf, 18))})
+ " execute a simple command
+ call term_sendkeys(buf, "aecho 'done'\<CR>")
+ call VerifyScreenDump(buf, 'Test_cmdwin_interrupted', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('XTest_cmdwin')
+endfunc
+
" Test for backtick expression in the command line
func Test_cmd_backtick()
CheckNotMSWindows " FIXME: see #19297
@@ -1508,6 +1791,53 @@ func Test_cmd_bang_E135()
%bwipe!
endfunc
+func Test_cmd_bang_args()
+ new
+ :.!
+ call assert_equal(0, v:shell_error)
+
+ " Note that below there is one space char after the '!'. This caused a
+ " shell error in the past, see https://github.com/vim/vim/issues/11495.
+ :.!
+ call assert_equal(0, v:shell_error)
+ bwipe!
+
+ CheckUnix
+ :.!pwd
+ call assert_equal(0, v:shell_error)
+ :.! pwd
+ call assert_equal(0, v:shell_error)
+
+ " Note there is one space after 'pwd'.
+ :.! pwd
+ call assert_equal(0, v:shell_error)
+
+ " Note there are two spaces after 'pwd'.
+ :.! pwd
+ call assert_equal(0, v:shell_error)
+ :.!ls ~
+ call assert_equal(0, v:shell_error)
+
+ " Note there is one space char after '~'.
+ :.!ls ~
+ call assert_equal(0, v:shell_error)
+
+ " Note there are two spaces after '~'.
+ :.!ls ~
+ call assert_equal(0, v:shell_error)
+
+ :.!echo "foo"
+ call assert_equal(getline('.'), "foo")
+ :.!echo "foo "
+ call assert_equal(getline('.'), "foo ")
+ :.!echo " foo "
+ call assert_equal(getline('.'), " foo ")
+ :.!echo " foo "
+ call assert_equal(getline('.'), " foo ")
+
+ %bwipe!
+endfunc
+
" Test for using ~ for home directory in cmdline completion matches
func Test_cmdline_expand_home()
call mkdir('Xdir')
@@ -1543,22 +1873,16 @@ func Test_cmdline_ctrl_g()
endfunc
" Test for 'wildmode'
-func Test_wildmode()
+func Wildmode_tests()
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 feedkeys(":MyCmd \t\t\<F4>\<C-B>\"\<CR>", 'xt')
call assert_equal('oneA oneB oneC', g:Sline)
call assert_equal('"MyCmd oneA', @:)
@@ -1574,7 +1898,7 @@ func Test_wildmode()
set wildmode=list:longest
let g:Sline = ''
- call feedkeys(":MyCmd \t\<F2>\<C-B>\"\<CR>", 'xt')
+ call feedkeys(":MyCmd \t\<F4>\<C-B>\"\<CR>", 'xt')
call assert_equal('oneA oneB oneC', g:Sline)
call assert_equal('"MyCmd one', @:)
@@ -1585,27 +1909,67 @@ func Test_wildmode()
" 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', @:)
+ argadd AAA AAAA AAAAA
+ call feedkeys(":buffer a\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"buffer AAA', @:)
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 feedkeys(":b A\t\t\<F4>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('AAA AAAA AAAAA', g:Sline)
call assert_equal('"b A', @:)
+ " when using longest completion match, matches shorter than the argument
+ " should be ignored (happens with :help)
+ set wildmode=longest,full
+ set wildmenu
+ call feedkeys(":help a*\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"help a', @:)
+ " non existing file
+ call feedkeys(":e a1b2y3z4\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e a1b2y3z4', @:)
+ set wildmenu&
+
+ " Test for longest file name completion with 'fileignorecase'
+ " On MS-Windows, file names are case insensitive.
+ if has('unix')
+ call writefile([], 'XTESTfoo')
+ call writefile([], 'Xtestbar')
+ set nofileignorecase
+ call feedkeys(":e XT\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e XTESTfoo', @:)
+ call feedkeys(":e Xt\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e Xtestbar', @:)
+ set fileignorecase
+ call feedkeys(":e XT\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e Xtest', @:)
+ call feedkeys(":e Xt\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e Xtest', @:)
+ set fileignorecase&
+ call delete('XTESTfoo')
+ call delete('Xtestbar')
+ endif
+
%argdelete
delcommand MyCmd
delfunc T
- delfunc SaveScreenLine
- cunmap <F2>
set wildmode&
%bwipe!
endfunc
+func Test_wildmode()
+ " Test with utf-8 encoding
+ call Wildmode_tests()
+
+ " Test with latin1 encoding
+ let save_encoding = &encoding
+ " set encoding=latin1
+ " call Wildmode_tests()
+ let &encoding = save_encoding
+endfunc
+
" Test for interrupting the command-line completion
func Test_interrupt_compl()
func F(lead, cmdl, p)
@@ -1627,6 +1991,14 @@ func Test_interrupt_compl()
endtry
call assert_equal(1, interrupted)
+ let interrupted = 0
+ try
+ call feedkeys(":Tcmd tw\<C-d>\<C-B>\"\<CR>", 'xt')
+ catch /^Vim:Interrupt$/
+ let interrupted = 1
+ endtry
+ call assert_equal(1, interrupted)
+
delcommand Tcmd
delfunc F
set wildmode&
@@ -1683,6 +2055,11 @@ func Test_cmdline_expr()
call assert_equal("\"e \<C-\>\<C-Y>", @:)
endfunc
+" This was making the insert position negative
+func Test_cmdline_expr_register()
+ exe "sil! norm! ?\<C-\>e0\<C-R>0\<Esc>?\<C-\>e0\<CR>"
+endfunc
+
" Test for 'imcmdline' and 'imsearch'
" This test doesn't actually test the input method functionality.
func Test_cmdline_inputmethod()
@@ -1809,8 +2186,45 @@ func Test_read_shellcmd()
endif
endfunc
+" Test for going up and down the directory tree using 'wildmenu'
+func Test_wildmenu_dirstack()
+ CheckUnix
+ %bw!
+ call mkdir('Xdir1/dir2/dir3/dir4', 'p')
+ call writefile([], 'Xdir1/file1_1.txt')
+ call writefile([], 'Xdir1/file1_2.txt')
+ call writefile([], 'Xdir1/dir2/file2_1.txt')
+ call writefile([], 'Xdir1/dir2/file2_2.txt')
+ call writefile([], 'Xdir1/dir2/dir3/file3_1.txt')
+ call writefile([], 'Xdir1/dir2/dir3/file3_2.txt')
+ call writefile([], 'Xdir1/dir2/dir3/dir4/file4_1.txt')
+ call writefile([], 'Xdir1/dir2/dir3/dir4/file4_2.txt')
+ set wildmenu
+
+ cd Xdir1/dir2/dir3/dir4
+ call feedkeys(":e \<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e file4_1.txt', @:)
+ call feedkeys(":e \<Tab>\<Up>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e ../dir4/', @:)
+ call feedkeys(":e \<Tab>\<Up>\<Up>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e ../../dir3/', @:)
+ call feedkeys(":e \<Tab>\<Up>\<Up>\<Up>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e ../../../dir2/', @:)
+ call feedkeys(":e \<Tab>\<Up>\<Up>\<Down>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e ../../dir3/dir4/', @:)
+ call feedkeys(":e \<Tab>\<Up>\<Up>\<Down>\<Down>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e ../../dir3/dir4/file4_1.txt', @:)
+ cd -
+ call feedkeys(":e Xdir1/\<Tab>\<Down>\<Down>\<Down>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e Xdir1/dir2/dir3/dir4/file4_1.txt', @:)
+
+ call delete('Xdir1', 'rf')
+ set wildmenu&
+endfunc
+
" Test for recalling newer or older cmdline from history with <Up>, <Down>,
-" <S-Up>, <S-Down>, <PageUp>, <PageDown>, <C-p>, or <C-n>.
+" <S-Up>, <S-Down>, <PageUp>, <PageDown>, <kPageUp>, <kPageDown>, <C-p>, or
+" <C-n>.
func Test_recalling_cmdline()
CheckFeature cmdline_hist
@@ -1818,17 +2232,18 @@ func Test_recalling_cmdline()
cnoremap <Plug>(save-cmdline) <Cmd>let g:cmdlines += [getcmdline()]<CR>
let histories = [
- \ {'name': 'cmd', 'enter': ':', 'exit': "\<Esc>"},
- \ {'name': 'search', 'enter': '/', 'exit': "\<Esc>"},
- \ {'name': 'expr', 'enter': ":\<C-r>=", 'exit': "\<Esc>\<Esc>"},
- \ {'name': 'input', 'enter': ":call input('')\<CR>", 'exit': "\<CR>"},
+ \ #{name: 'cmd', enter: ':', exit: "\<Esc>"},
+ \ #{name: 'search', enter: '/', exit: "\<Esc>"},
+ \ #{name: 'expr', enter: ":\<C-r>=", exit: "\<Esc>\<Esc>"},
+ \ #{name: 'input', enter: ":call input('')\<CR>", exit: "\<CR>"},
"\ TODO: {'name': 'debug', ...}
\]
let keypairs = [
- \ {'older': "\<Up>", 'newer': "\<Down>", 'prefixmatch': v:true},
- \ {'older': "\<S-Up>", 'newer': "\<S-Down>", 'prefixmatch': v:false},
- \ {'older': "\<PageUp>", 'newer': "\<PageDown>", 'prefixmatch': v:false},
- \ {'older': "\<C-p>", 'newer': "\<C-n>", 'prefixmatch': v:false},
+ \ #{older: "\<Up>", newer: "\<Down>", prefixmatch: v:true},
+ \ #{older: "\<S-Up>", newer: "\<S-Down>", prefixmatch: v:false},
+ \ #{older: "\<PageUp>", newer: "\<PageDown>", prefixmatch: v:false},
+ \ #{older: "\<kPageUp>", newer: "\<kPageDown>", prefixmatch: v:false},
+ \ #{older: "\<C-p>", newer: "\<C-n>", prefixmatch: v:false},
\]
let prefix = 'vi'
for h in histories
@@ -1857,6 +2272,59 @@ func Test_recalling_cmdline()
cunmap <Plug>(save-cmdline)
endfunc
+func Test_cmd_map_cmdlineChanged()
+ let g:log = []
+ cnoremap <F1> l<Cmd><CR>s
+ augroup test
+ autocmd!
+ autocmd CmdlineChanged : let g:log += [getcmdline()]
+ augroup END
+
+ call feedkeys(":\<F1>\<CR>", 'xt')
+ call assert_equal(['l', 'ls'], g:log)
+
+ let @b = 'b'
+ cnoremap <F1> a<C-R>b
+ let g:log = []
+ call feedkeys(":\<F1>\<CR>", 'xt')
+ call assert_equal(['a', 'ab'], g:log)
+
+ unlet g:log
+ cunmap <F1>
+ augroup test
+ autocmd!
+ augroup END
+endfunc
+
+" Test for the 'suffixes' option
+func Test_suffixes_opt()
+ call writefile([], 'Xfile')
+ call writefile([], 'Xfile.c')
+ call writefile([], 'Xfile.o')
+ set suffixes=
+ call feedkeys(":e Xfi*\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e Xfile Xfile.c Xfile.o', @:)
+ call feedkeys(":e Xfi*\<Tab>\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e Xfile.c', @:)
+ set suffixes=.c
+ call feedkeys(":e Xfi*\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e Xfile Xfile.o Xfile.c', @:)
+ call feedkeys(":e Xfi*\<Tab>\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e Xfile.o', @:)
+ set suffixes=,,
+ call feedkeys(":e Xfi*\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e Xfile.c Xfile.o Xfile', @:)
+ call feedkeys(":e Xfi*\<Tab>\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e Xfile.o', @:)
+ set suffixes&
+ " Test for getcompletion() with different patterns
+ call assert_equal(['Xfile', 'Xfile.c', 'Xfile.o'], getcompletion('Xfile', 'file'))
+ call assert_equal(['Xfile'], getcompletion('Xfile$', 'file'))
+ call delete('Xfile')
+ call delete('Xfile.c')
+ call delete('Xfile.o')
+endfunc
+
" Test for using a popup menu for the command line completion matches
" (wildoptions=pum)
func Test_wildmenu_pum()
@@ -1868,39 +2336,63 @@ func Test_wildmenu_pum()
set shm+=I
set noruler
set noshowcmd
+
+ func CmdCompl(a, b, c)
+ return repeat(['aaaa'], 120)
+ endfunc
+ command -nargs=* -complete=customlist,CmdCompl Tcmd
+
+ func MyStatusLine() abort
+ return 'status'
+ endfunc
+ func SetupStatusline()
+ set statusline=%!MyStatusLine()
+ set laststatus=2
+ endfunc
+
+ func MyTabLine()
+ return 'my tab line'
+ endfunc
+ func SetupTabline()
+ set statusline=
+ set tabline=%!MyTabLine()
+ set showtabline=2
+ endfunc
+
+ func DoFeedKeys()
+ let &wildcharm = char2nr("\t")
+ call feedkeys(":edit $VIMRUNTIME/\<Tab>\<Left>\<C-U>ab\<Tab>")
+ endfunc
[CODE]
call writefile(commands, 'Xtest')
let buf = RunVimInTerminal('-S Xtest', #{rows: 10})
call term_sendkeys(buf, ":sign \<Tab>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_01', {})
+ " going down the popup menu using <Down>
call term_sendkeys(buf, "\<Down>\<Down>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_02', {})
+ " going down the popup menu using <C-N>
call term_sendkeys(buf, "\<C-N>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_03', {})
+ " going up the popup menu using <C-P>
call term_sendkeys(buf, "\<C-P>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_04', {})
+ " going up the popup menu using <Up>
call term_sendkeys(buf, "\<Up>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_05', {})
" pressing <C-E> should end completion and go back to the original match
call term_sendkeys(buf, "\<C-E>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_06', {})
" pressing <C-Y> should select the current match and end completion
call term_sendkeys(buf, "\<Tab>\<C-P>\<C-P>\<C-Y>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_07', {})
" With 'wildmode' set to 'longest,full', completing a match should display
@@ -1908,31 +2400,25 @@ func Test_wildmenu_pum()
call term_sendkeys(buf, ":\<C-U>set wildmode=longest,full\<CR>")
call TermWait(buf)
call term_sendkeys(buf, ":sign u\<Tab>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_08', {})
" pressing <Tab> should display the wildmenu
call term_sendkeys(buf, "\<Tab>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_09', {})
" pressing <Tab> second time should select the next entry in the menu
call term_sendkeys(buf, "\<Tab>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_10', {})
call term_sendkeys(buf, ":\<C-U>set wildmode=full\<CR>")
- " " showing popup menu in different columns in the cmdline
+ " showing popup menu in different columns in the cmdline
call term_sendkeys(buf, ":sign define \<Tab>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_11', {})
call term_sendkeys(buf, " \<Tab>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_12', {})
call term_sendkeys(buf, " \<Tab>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_13', {})
" Directory name completion
@@ -1942,95 +2428,77 @@ func Test_wildmenu_pum()
call writefile([], 'Xdir/XdirA/XdirB/XfileC')
call term_sendkeys(buf, "\<C-U>e Xdi\<Tab>\<Tab>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_14', {})
" Pressing <Right> on a directory name should go into that directory
call term_sendkeys(buf, "\<Right>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_15', {})
" Pressing <Left> on a directory name should go to the parent directory
call term_sendkeys(buf, "\<Left>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_16', {})
" Pressing <C-A> when the popup menu is displayed should list all the
- " matches and remove the popup menu
+ " matches but the popup menu should still remain
call term_sendkeys(buf, "\<C-U>sign \<Tab>\<C-A>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_17', {})
" Pressing <C-D> when the popup menu is displayed should remove the popup
" menu
call term_sendkeys(buf, "\<C-U>sign \<Tab>\<C-D>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_18', {})
" Pressing <S-Tab> should open the popup menu with the last entry selected
call term_sendkeys(buf, "\<C-U>\<CR>:sign \<S-Tab>\<C-P>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_19', {})
" Pressing <Esc> should close the popup menu and cancel the cmd line
call term_sendkeys(buf, "\<C-U>\<CR>:sign \<Tab>\<Esc>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_20', {})
" Typing a character when the popup is open, should close the popup
call term_sendkeys(buf, ":sign \<Tab>x")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_21', {})
" When the popup is open, entering the cmdline window should close the popup
call term_sendkeys(buf, "\<C-U>sign \<Tab>\<C-F>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_22', {})
call term_sendkeys(buf, ":q\<CR>")
" After the last popup menu item, <C-N> should show the original string
call term_sendkeys(buf, ":sign u\<Tab>\<C-N>\<C-N>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_23', {})
" Use the popup menu for the command name
call term_sendkeys(buf, "\<C-U>bu\<Tab>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_24', {})
" Pressing the left arrow should remove the popup menu
call term_sendkeys(buf, "\<Left>\<Left>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_25', {})
" Pressing <BS> should remove the popup menu and erase the last character
call term_sendkeys(buf, "\<C-E>\<C-U>sign \<Tab>\<BS>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_26', {})
" Pressing <C-W> should remove the popup menu and erase the previous word
call term_sendkeys(buf, "\<C-E>\<C-U>sign \<Tab>\<C-W>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_27', {})
" Pressing <C-U> should remove the popup menu and erase the entire line
call term_sendkeys(buf, "\<C-E>\<C-U>sign \<Tab>\<C-U>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_28', {})
" Using <C-E> to cancel the popup menu and then pressing <Up> should recall
" the cmdline from history
call term_sendkeys(buf, "sign xyz\<Esc>:sign \<Tab>\<C-E>\<Up>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_29', {})
" Check "list" still works
call term_sendkeys(buf, "\<C-U>set wildmode=longest,list\<CR>")
call term_sendkeys(buf, ":cn\<Tab>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_30', {})
call term_sendkeys(buf, "s")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_31', {})
" Tests a directory name contained full-width characters.
@@ -2041,15 +2509,933 @@ func Test_wildmenu_pum()
call term_sendkeys(buf, "\<C-U>set wildmode&\<CR>")
call term_sendkeys(buf, ":\<C-U>e Xdir/あいう/\<Tab>")
- call TermWait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_32', {})
+ " Pressing <C-A> when the popup menu is displayed should list all the
+ " matches and pressing a key after that should remove the popup menu
+ call term_sendkeys(buf, "\<C-U>set wildmode=full\<CR>")
+ call term_sendkeys(buf, ":sign \<Tab>\<C-A>x")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_33', {})
+
+ " Pressing <C-A> when the popup menu is displayed should list all the
+ " matches and pressing <Left> after that should move the cursor
+ call term_sendkeys(buf, "\<C-U>abc\<Esc>")
+ call term_sendkeys(buf, ":sign \<Tab>\<C-A>\<Left>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_34', {})
+
+ " When <C-A> displays a lot of matches (screen scrolls), all the matches
+ " should be displayed correctly on the screen.
+ call term_sendkeys(buf, "\<End>\<C-U>Tcmd \<Tab>\<C-A>\<Left>\<Left>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_35', {})
+
+ " After using <C-A> to expand all the filename matches, pressing <Up>
+ " should not open the popup menu again.
+ call term_sendkeys(buf, "\<C-E>\<C-U>:cd Xdir/XdirA\<CR>")
+ call term_sendkeys(buf, ":e \<Tab>\<C-A>\<Up>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_36', {})
+ call term_sendkeys(buf, "\<C-E>\<C-U>:cd -\<CR>")
+
+ " After using <C-A> to expand all the matches, pressing <S-Tab> used to
+ " crash Vim
+ call term_sendkeys(buf, ":sign \<Tab>\<C-A>\<S-Tab>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_37', {})
+
+ " After removing the pum the command line is redrawn
+ call term_sendkeys(buf, ":edit foo\<CR>")
+ call term_sendkeys(buf, ":edit bar\<CR>")
+ call term_sendkeys(buf, ":ls\<CR>")
+ call term_sendkeys(buf, ":com\<Tab> ")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_38', {})
+ call term_sendkeys(buf, "\<C-U>\<CR>")
+
+ " Esc still works to abort the command when 'statusline' is set
+ call term_sendkeys(buf, ":call SetupStatusline()\<CR>")
+ call term_sendkeys(buf, ":si\<Tab>")
+ call term_sendkeys(buf, "\<Esc>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_39', {})
+
+ " Esc still works to abort the command when 'tabline' is set
+ call term_sendkeys(buf, ":call SetupTabline()\<CR>")
+ call term_sendkeys(buf, ":si\<Tab>")
+ call term_sendkeys(buf, "\<Esc>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_40', {})
+
+ " popup is cleared also when 'lazyredraw' is set
+ call term_sendkeys(buf, ":set showtabline=1 laststatus=1 lazyredraw\<CR>")
+ call term_sendkeys(buf, ":call DoFeedKeys()\<CR>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_41', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ " Pressing <PageDown> should scroll the menu downward
+ call term_sendkeys(buf, ":sign \<Tab>\<PageDown>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_42', {})
+ call term_sendkeys(buf, "\<PageDown>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_43', {})
+ call term_sendkeys(buf, "\<PageDown>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_44', {})
+ call term_sendkeys(buf, "\<PageDown>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_45', {})
+ call term_sendkeys(buf, "\<C-U>sign \<Tab>\<Down>\<Down>\<PageDown>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_46', {})
+
+ " Pressing <PageUp> should scroll the menu upward
+ call term_sendkeys(buf, "\<C-U>sign \<Tab>\<PageUp>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_47', {})
+ call term_sendkeys(buf, "\<PageUp>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_48', {})
+ call term_sendkeys(buf, "\<PageUp>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_49', {})
+ call term_sendkeys(buf, "\<PageUp>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_50', {})
+
call term_sendkeys(buf, "\<C-U>\<CR>")
call StopVimInTerminal(buf)
call delete('Xtest')
call delete('Xdir', 'rf')
endfunc
+" Test for wildmenumode() with the cmdline popup menu
+func Test_wildmenumode_with_pum()
+ set wildmenu
+ set wildoptions=pum
+ cnoremap <expr> <F2> wildmenumode()
+ call feedkeys(":sign \<Tab>\<F2>\<F2>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"sign define10', @:)
+ call feedkeys(":sign \<Tab>\<C-A>\<F2>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"sign define jump list place undefine unplace0', @:)
+ call feedkeys(":sign \<Tab>\<C-E>\<F2>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"sign 0', @:)
+ call feedkeys(":sign \<Tab>\<C-Y>\<F2>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"sign define0', @:)
+ set nowildmenu wildoptions&
+ cunmap <F2>
+endfunc
+
+" Test for opening the cmdline completion popup menu from the terminal window.
+" The popup menu should be positioned correctly over the status line of the
+" bottom-most window.
+func Test_wildmenu_pum_from_terminal()
+ CheckRunVimInTerminal
+ let python = PythonProg()
+ call CheckPython(python)
+
+ %bw!
+ let cmds = ['set wildmenu wildoptions=pum']
+ let pcmd = python .. ' -c "import sys; sys.stdout.write(sys.stdin.read())"'
+ call add(cmds, "call term_start('" .. pcmd .. "')")
+ call writefile(cmds, 'Xtest')
+ let buf = RunVimInTerminal('-S Xtest', #{rows: 10})
+ call term_sendkeys(buf, "\r\r\r")
+ call term_wait(buf)
+ call term_sendkeys(buf, "\<C-W>:sign \<Tab>")
+ call term_wait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_term_01', {})
+ call term_wait(buf)
+ call StopVimInTerminal(buf)
+ call delete('Xtest')
+endfunc
+
+func Test_wildmenu_pum_clear_entries()
+ CheckRunVimInTerminal
+
+ " This was using freed memory. Run in a terminal to get the pum to update.
+ let lines =<< trim END
+ set wildoptions=pum
+ set wildchar=<C-E>
+ END
+ call writefile(lines, 'XwildmenuTest', 'D')
+ let buf = RunVimInTerminal('-S XwildmenuTest', #{rows: 10})
+
+ call term_sendkeys(buf, ":\<C-E>\<C-E>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_clear_entries_1', {})
+
+ set wildoptions& wildchar&
+endfunc
+
+" Test for completion after a :substitute command followed by a pipe (|)
+" character
+func Test_cmdline_complete_substitute()
+ call feedkeys(":s | \t\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"s | \t", @:)
+ call feedkeys(":s/ | \t\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"s/ | \t", @:)
+ call feedkeys(":s/one | \t\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"s/one | \t", @:)
+ call feedkeys(":s/one/ | \t\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"s/one/ | \t", @:)
+ call feedkeys(":s/one/two | \t\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"s/one/two | \t", @:)
+ call feedkeys(":s/one/two/ | chist\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"s/one/two/ | chistory', @:)
+ call feedkeys(":s/one/two/g \t\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"s/one/two/g \t", @:)
+ call feedkeys(":s/one/two/g | chist\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"s/one/two/g | chistory", @:)
+ call feedkeys(":s/one/t\\/ | \t\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"s/one/t\\/ | \t", @:)
+ call feedkeys(":s/one/t\"o/ | chist\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"s/one/t"o/ | chistory', @:)
+ call feedkeys(":s/one/t|o/ | chist\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"s/one/t|o/ | chistory', @:)
+ call feedkeys(":&\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"&\t", @:)
+endfunc
+
+" Test for the :dlist command completion
+func Test_cmdline_complete_dlist()
+ call feedkeys(":dlist 10 /pat/ a\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"dlist 10 /pat/ a\<C-A>", @:)
+ call feedkeys(":dlist 10 /pat/ \t\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"dlist 10 /pat/ \t", @:)
+ call feedkeys(":dlist 10 /pa\\t/\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"dlist 10 /pa\\t/\t", @:)
+ call feedkeys(":dlist 10 /pat\\\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"dlist 10 /pat\\\t", @:)
+ call feedkeys(":dlist 10 /pat/ | chist\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"dlist 10 /pat/ | chistory", @:)
+endfunc
+
+" argument list (only for :argdel) fuzzy completion
+func Test_fuzzy_completion_arglist()
+ argadd change.py count.py charge.py
+ set wildoptions&
+ call feedkeys(":argdel cge\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"argdel cge', @:)
+ set wildoptions=fuzzy
+ call feedkeys(":argdel cge\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"argdel change.py charge.py', @:)
+ %argdelete
+ set wildoptions&
+endfunc
+
+" autocmd group name fuzzy completion
+func Test_fuzzy_completion_autocmd()
+ set wildoptions&
+ augroup MyFuzzyGroup
+ augroup END
+ call feedkeys(":augroup mfg\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"augroup mfg', @:)
+ call feedkeys(":augroup My*p\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"augroup MyFuzzyGroup', @:)
+ set wildoptions=fuzzy
+ call feedkeys(":augroup mfg\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"augroup MyFuzzyGroup', @:)
+ call feedkeys(":augroup My*p\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"augroup My*p', @:)
+ augroup! MyFuzzyGroup
+ set wildoptions&
+endfunc
+
+" buffer name fuzzy completion
+func Test_fuzzy_completion_bufname()
+ set wildoptions&
+ " Use a long name to reduce the risk of matching a random directory name
+ edit SomeRandomFileWithLetters.txt
+ enew
+ call feedkeys(":b SRFWL\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"b SRFWL', @:)
+ call feedkeys(":b S*FileWithLetters.txt\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"b SomeRandomFileWithLetters.txt', @:)
+ set wildoptions=fuzzy
+ call feedkeys(":b SRFWL\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"b SomeRandomFileWithLetters.txt', @:)
+ call feedkeys(":b S*FileWithLetters.txt\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"b S*FileWithLetters.txt', @:)
+ %bw!
+ set wildoptions&
+endfunc
+
+" buffer name (full path) fuzzy completion
+func Test_fuzzy_completion_bufname_fullpath()
+ CheckUnix
+ set wildoptions&
+ call mkdir('Xcmd/Xstate/Xfile.js', 'p')
+ edit Xcmd/Xstate/Xfile.js
+ cd Xcmd/Xstate
+ enew
+ call feedkeys(":b CmdStateFile\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"b CmdStateFile', @:)
+ set wildoptions=fuzzy
+ call feedkeys(":b CmdStateFile\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_match('Xcmd/Xstate/Xfile.js$', @:)
+ cd -
+ call delete('Xcmd', 'rf')
+ set wildoptions&
+endfunc
+
+" :behave suboptions fuzzy completion
+func Test_fuzzy_completion_behave()
+ set wildoptions&
+ call feedkeys(":behave xm\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"behave xm', @:)
+ call feedkeys(":behave xt*m\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"behave xterm', @:)
+ set wildoptions=fuzzy
+ call feedkeys(":behave xm\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"behave xterm', @:)
+ call feedkeys(":behave xt*m\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"behave xt*m', @:)
+ let g:Sline = ''
+ call feedkeys(":behave win\<C-D>\<F4>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('mswin', g:Sline)
+ call assert_equal('"behave win', @:)
+ set wildoptions&
+endfunc
+
+" " colorscheme name fuzzy completion - NOT supported
+" func Test_fuzzy_completion_colorscheme()
+" endfunc
+
+" built-in command name fuzzy completion
+func Test_fuzzy_completion_cmdname()
+ set wildoptions&
+ call feedkeys(":sbwin\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"sbwin', @:)
+ call feedkeys(":sbr*d\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"sbrewind', @:)
+ set wildoptions=fuzzy
+ call feedkeys(":sbwin\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"sbrewind', @:)
+ call feedkeys(":sbr*d\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"sbr*d', @:)
+ set wildoptions&
+endfunc
+
+" " compiler name fuzzy completion - NOT supported
+" func Test_fuzzy_completion_compiler()
+" endfunc
+
+" :cscope suboptions fuzzy completion
+func Test_fuzzy_completion_cscope()
+ CheckFeature cscope
+ set wildoptions&
+ call feedkeys(":cscope ret\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"cscope ret', @:)
+ call feedkeys(":cscope re*t\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"cscope reset', @:)
+ set wildoptions=fuzzy
+ call feedkeys(":cscope ret\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"cscope reset', @:)
+ call feedkeys(":cscope re*t\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"cscope re*t', @:)
+ set wildoptions&
+endfunc
+
+" :diffget/:diffput buffer name fuzzy completion
+func Test_fuzzy_completion_diff()
+ new SomeBuffer
+ diffthis
+ new OtherBuffer
+ diffthis
+ set wildoptions&
+ call feedkeys(":diffget sbuf\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"diffget sbuf', @:)
+ call feedkeys(":diffput sbuf\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"diffput sbuf', @:)
+ set wildoptions=fuzzy
+ call feedkeys(":diffget sbuf\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"diffget SomeBuffer', @:)
+ call feedkeys(":diffput sbuf\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"diffput SomeBuffer', @:)
+ %bw!
+ set wildoptions&
+endfunc
+
+" " directory name fuzzy completion - NOT supported
+" func Test_fuzzy_completion_dirname()
+" endfunc
+
+" environment variable name fuzzy completion
+func Test_fuzzy_completion_env()
+ set wildoptions&
+ call feedkeys(":echo $VUT\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"echo $VUT', @:)
+ set wildoptions=fuzzy
+ call feedkeys(":echo $VUT\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"echo $VIMRUNTIME', @:)
+ set wildoptions&
+endfunc
+
+" autocmd event fuzzy completion
+func Test_fuzzy_completion_autocmd_event()
+ set wildoptions&
+ call feedkeys(":autocmd BWout\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"autocmd BWout', @:)
+ set wildoptions=fuzzy
+ call feedkeys(":autocmd BWout\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"autocmd BufWipeout', @:)
+ set wildoptions&
+endfunc
+
+" vim expression fuzzy completion
+func Test_fuzzy_completion_expr()
+ let g:PerPlaceCount = 10
+ set wildoptions&
+ call feedkeys(":let c = ppc\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"let c = ppc', @:)
+ set wildoptions=fuzzy
+ call feedkeys(":let c = ppc\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"let c = PerPlaceCount', @:)
+ set wildoptions&
+endfunc
+
+" " file name fuzzy completion - NOT supported
+" func Test_fuzzy_completion_filename()
+" endfunc
+
+" " files in path fuzzy completion - NOT supported
+" func Test_fuzzy_completion_filesinpath()
+" endfunc
+
+" " filetype name fuzzy completion - NOT supported
+" func Test_fuzzy_completion_filetype()
+" endfunc
+
+" user defined function name completion
+func Test_fuzzy_completion_userdefined_func()
+ set wildoptions&
+ call feedkeys(":call Test_f_u_f\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"call Test_f_u_f', @:)
+ set wildoptions=fuzzy
+ call feedkeys(":call Test_f_u_f\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"call Test_fuzzy_completion_userdefined_func()', @:)
+ set wildoptions&
+endfunc
+
+" <SNR> functions should be sorted to the end
+func Test_fuzzy_completion_userdefined_snr_func()
+ func s:Sendmail()
+ endfunc
+ func SendSomemail()
+ endfunc
+ func S1e2n3dmail()
+ endfunc
+ set wildoptions=fuzzy
+ call feedkeys(":call sendmail\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"call SendSomemail() S1e2n3dmail() '
+ \ .. expand("<SID>") .. 'Sendmail()', @:)
+ set wildoptions&
+ delfunc s:Sendmail
+ delfunc SendSomemail
+ delfunc S1e2n3dmail
+endfunc
+
+" user defined command name completion
+func Test_fuzzy_completion_userdefined_cmd()
+ set wildoptions&
+ call feedkeys(":MsFeat\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"MsFeat', @:)
+ set wildoptions=fuzzy
+ call feedkeys(":MsFeat\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"MissingFeature', @:)
+ set wildoptions&
+endfunc
+
+" " :help tag fuzzy completion - NOT supported
+" func Test_fuzzy_completion_helptag()
+" endfunc
+
+" highlight group name fuzzy completion
+func Test_fuzzy_completion_hlgroup()
+ set wildoptions&
+ call feedkeys(":highlight SKey\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"highlight SKey', @:)
+ call feedkeys(":highlight Sp*Key\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"highlight SpecialKey', @:)
+ set wildoptions=fuzzy
+ call feedkeys(":highlight SKey\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"highlight SpecialKey', @:)
+ call feedkeys(":highlight Sp*Key\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"highlight Sp*Key', @:)
+ set wildoptions&
+endfunc
+
+" :history suboptions fuzzy completion
+func Test_fuzzy_completion_history()
+ set wildoptions&
+ call feedkeys(":history dg\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"history dg', @:)
+ call feedkeys(":history se*h\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"history search', @:)
+ set wildoptions=fuzzy
+ call feedkeys(":history dg\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"history debug', @:)
+ call feedkeys(":history se*h\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"history se*h', @:)
+ set wildoptions&
+endfunc
+
+" :language locale name fuzzy completion
+func Test_fuzzy_completion_lang()
+ CheckUnix
+ set wildoptions&
+ call feedkeys(":lang psx\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"lang psx', @:)
+ set wildoptions=fuzzy
+ call feedkeys(":lang psx\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"lang POSIX', @:)
+ set wildoptions&
+endfunc
+
+" :mapclear buffer argument fuzzy completion
+func Test_fuzzy_completion_mapclear()
+ set wildoptions&
+ call feedkeys(":mapclear buf\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"mapclear buf', @:)
+ set wildoptions=fuzzy
+ call feedkeys(":mapclear buf\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"mapclear <buffer>', @:)
+ set wildoptions&
+endfunc
+
+" map name fuzzy completion
+func Test_fuzzy_completion_mapname()
+ " test regex completion works
+ set wildoptions=fuzzy
+ call feedkeys(":cnoremap <ex\<Tab> <esc> \<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"cnoremap <expr> <esc> \<Tab>", @:)
+ nmap <plug>MyLongMap :p<CR>
+ call feedkeys(":nmap MLM\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"nmap <Plug>MyLongMap", @:)
+ call feedkeys(":nmap MLM \<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"nmap MLM \t", @:)
+ call feedkeys(":nmap <F2> one two \<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"nmap <F2> one two \t", @:)
+ " duplicate entries should be removed
+ vmap <plug>MyLongMap :<C-U>#<CR>
+ call feedkeys(":nmap MLM\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"nmap <Plug>MyLongMap", @:)
+ nunmap <plug>MyLongMap
+ vunmap <plug>MyLongMap
+ call feedkeys(":nmap ABC\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"nmap ABC\t", @:)
+ " results should be sorted by best match
+ nmap <Plug>format :
+ nmap <Plug>goformat :
+ nmap <Plug>TestFOrmat :
+ nmap <Plug>fendoff :
+ nmap <Plug>state :
+ nmap <Plug>FendingOff :
+ call feedkeys(":nmap <Plug>fo\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"nmap <Plug>format <Plug>TestFOrmat <Plug>FendingOff <Plug>goformat <Plug>fendoff", @:)
+ nunmap <Plug>format
+ nunmap <Plug>goformat
+ nunmap <Plug>TestFOrmat
+ nunmap <Plug>fendoff
+ nunmap <Plug>state
+ nunmap <Plug>FendingOff
+ set wildoptions&
+endfunc
+
+" abbreviation fuzzy completion
+func Test_fuzzy_completion_abbr()
+ set wildoptions=fuzzy
+ call feedkeys(":iabbr wait\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"iabbr <nowait>", @:)
+ iabbr WaitForCompletion WFC
+ call feedkeys(":iabbr fcl\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"iabbr WaitForCompletion", @:)
+ call feedkeys(":iabbr a1z\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"iabbr a1z\t", @:)
+
+ iunabbrev WaitForCompletion
+ set wildoptions&
+endfunc
+
+" menu name fuzzy completion
+func Test_fuzzy_completion_menu()
+ CheckFeature menu
+
+ source $VIMRUNTIME/menu.vim
+ set wildoptions&
+ call feedkeys(":menu pup\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"menu pup', @:)
+ set wildoptions=fuzzy
+ call feedkeys(":menu pup\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"menu PopUp.', @:)
+
+ set wildoptions&
+ source $VIMRUNTIME/delmenu.vim
+endfunc
+
+" :messages suboptions fuzzy completion
+func Test_fuzzy_completion_messages()
+ set wildoptions&
+ call feedkeys(":messages clr\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"messages clr', @:)
+ set wildoptions=fuzzy
+ call feedkeys(":messages clr\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"messages clear', @:)
+ set wildoptions&
+endfunc
+
+" :set option name fuzzy completion
+func Test_fuzzy_completion_option()
+ set wildoptions&
+ call feedkeys(":set brkopt\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"set brkopt', @:)
+ set wildoptions=fuzzy
+ call feedkeys(":set brkopt\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"set breakindentopt', @:)
+ set wildoptions&
+ call feedkeys(":set fixeol\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"set fixendofline', @:)
+ set wildoptions=fuzzy
+ call feedkeys(":set fixeol\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"set fixendofline', @:)
+ set wildoptions&
+endfunc
+
+" :set <term_option>
+func Test_fuzzy_completion_term_option()
+ throw 'Skipped: Nvim does not support term options'
+ set wildoptions&
+ call feedkeys(":set t_E\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"set t_EC', @:)
+ call feedkeys(":set <t_E\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"set <t_EC>', @:)
+ set wildoptions=fuzzy
+ call feedkeys(":set t_E\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"set t_EC', @:)
+ call feedkeys(":set <t_E\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"set <t_EC>', @:)
+ set wildoptions&
+endfunc
+
+" " :packadd directory name fuzzy completion - NOT supported
+" func Test_fuzzy_completion_packadd()
+" endfunc
+
+" " shell command name fuzzy completion - NOT supported
+" func Test_fuzzy_completion_shellcmd()
+" endfunc
+
+" :sign suboptions fuzzy completion
+func Test_fuzzy_completion_sign()
+ set wildoptions&
+ call feedkeys(":sign ufe\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"sign ufe', @:)
+ set wildoptions=fuzzy
+ call feedkeys(":sign ufe\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"sign undefine', @:)
+ set wildoptions&
+endfunc
+
+" :syntax suboptions fuzzy completion
+func Test_fuzzy_completion_syntax_cmd()
+ set wildoptions&
+ call feedkeys(":syntax kwd\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"syntax kwd', @:)
+ set wildoptions=fuzzy
+ call feedkeys(":syntax kwd\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"syntax keyword', @:)
+ set wildoptions&
+endfunc
+
+" syntax group name fuzzy completion
+func Test_fuzzy_completion_syntax_group()
+ set wildoptions&
+ call feedkeys(":syntax list mpar\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"syntax list mpar', @:)
+ set wildoptions=fuzzy
+ call feedkeys(":syntax list mpar\<Tab>\<C-B>\"\<CR>", 'tx')
+ " Fuzzy match prefers NvimParenthesis over MatchParen
+ " call assert_equal('"syntax list MatchParen', @:)
+ call assert_equal('"syntax list NvimParenthesis', @:)
+ set wildoptions&
+endfunc
+
+" :syntime suboptions fuzzy completion
+func Test_fuzzy_completion_syntime()
+ CheckFeature profile
+ set wildoptions&
+ call feedkeys(":syntime clr\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"syntime clr', @:)
+ set wildoptions=fuzzy
+ call feedkeys(":syntime clr\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"syntime clear', @:)
+ set wildoptions&
+endfunc
+
+" " tag name fuzzy completion - NOT supported
+" func Test_fuzzy_completion_tagname()
+" endfunc
+
+" " tag name and file fuzzy completion - NOT supported
+" func Test_fuzzy_completion_tagfile()
+" endfunc
+
+" " user names fuzzy completion - how to test this functionality?
+" func Test_fuzzy_completion_username()
+" endfunc
+
+" user defined variable name fuzzy completion
+func Test_fuzzy_completion_userdefined_var()
+ let g:SomeVariable=10
+ set wildoptions&
+ call feedkeys(":let SVar\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"let SVar', @:)
+ set wildoptions=fuzzy
+ call feedkeys(":let SVar\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"let SomeVariable', @:)
+ set wildoptions&
+endfunc
+
+" Test for sorting the results by the best match
+func Test_fuzzy_completion_cmd_sort_results()
+ %bw!
+ command T123format :
+ command T123goformat :
+ command T123TestFOrmat :
+ command T123fendoff :
+ command T123state :
+ command T123FendingOff :
+ set wildoptions=fuzzy
+ call feedkeys(":T123fo\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"T123format T123TestFOrmat T123FendingOff T123goformat T123fendoff', @:)
+ delcommand T123format
+ delcommand T123goformat
+ delcommand T123TestFOrmat
+ delcommand T123fendoff
+ delcommand T123state
+ delcommand T123FendingOff
+ %bw
+ set wildoptions&
+endfunc
+
+" Test for fuzzy completion of a command with lower case letters and a number
+func Test_fuzzy_completion_cmd_alnum()
+ command Foo2Bar :
+ set wildoptions=fuzzy
+ call feedkeys(":foo2\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"Foo2Bar', @:)
+ call feedkeys(":foo\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"Foo2Bar', @:)
+ call feedkeys(":bar\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"Foo2Bar', @:)
+ delcommand Foo2Bar
+ set wildoptions&
+endfunc
+
+" Test for command completion for a command starting with 'k'
+func Test_fuzzy_completion_cmd_k()
+ command KillKillKill :
+ set wildoptions&
+ call feedkeys(":killkill\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"killkill\<Tab>", @:)
+ set wildoptions=fuzzy
+ call feedkeys(":killkill\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"KillKillKill', @:)
+ delcom KillKillKill
+ set wildoptions&
+endfunc
+
+" Test for fuzzy completion for user defined custom completion function
+func Test_fuzzy_completion_custom_func()
+ func Tcompl(a, c, p)
+ return "format\ngoformat\nTestFOrmat\nfendoff\nstate"
+ endfunc
+ command -nargs=* -complete=custom,Tcompl Fuzzy :
+ set wildoptions&
+ call feedkeys(":Fuzzy fo\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"Fuzzy format", @:)
+ call feedkeys(":Fuzzy xy\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"Fuzzy xy", @:)
+ call feedkeys(":Fuzzy ttt\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"Fuzzy ttt", @:)
+ set wildoptions=fuzzy
+ call feedkeys(":Fuzzy \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"Fuzzy format goformat TestFOrmat fendoff state", @:)
+ call feedkeys(":Fuzzy fo\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"Fuzzy format TestFOrmat goformat fendoff", @:)
+ call feedkeys(":Fuzzy xy\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"Fuzzy xy", @:)
+ call feedkeys(":Fuzzy ttt\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"Fuzzy TestFOrmat", @:)
+ delcom Fuzzy
+ set wildoptions&
+endfunc
+
+" Test for :breakadd argument completion
+func Test_cmdline_complete_breakadd()
+ call feedkeys(":breakadd \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd expr file func here", @:)
+ call feedkeys(":breakadd \<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd expr", @:)
+ call feedkeys(":breakadd \<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd expr", @:)
+ call feedkeys(":breakadd he\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd here", @:)
+ call feedkeys(":breakadd he\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd here", @:)
+ call feedkeys(":breakadd abc\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd abc", @:)
+ call assert_equal(['expr', 'file', 'func', 'here'], getcompletion('', 'breakpoint'))
+ let l = getcompletion('not', 'breakpoint')
+ call assert_equal([], l)
+
+ " Test for :breakadd file [lnum] <file>
+ call writefile([], 'Xscript')
+ call feedkeys(":breakadd file Xsc\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd file Xscript", @:)
+ call feedkeys(":breakadd file Xsc\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd file Xscript", @:)
+ call feedkeys(":breakadd file 20 Xsc\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd file 20 Xscript", @:)
+ call feedkeys(":breakadd file 20 Xsc\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd file 20 Xscript", @:)
+ call feedkeys(":breakadd file 20x Xsc\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd file 20x Xsc\t", @:)
+ call feedkeys(":breakadd file 20\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd file 20\t", @:)
+ call feedkeys(":breakadd file 20x\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd file 20x\t", @:)
+ call feedkeys(":breakadd file Xscript \<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd file Xscript ", @:)
+ call feedkeys(":breakadd file X1B2C3\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd file X1B2C3", @:)
+ call delete('Xscript')
+
+ " Test for :breakadd func [lnum] <function>
+ func Xbreak_func()
+ endfunc
+ call feedkeys(":breakadd func Xbr\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd func Xbreak_func", @:)
+ call feedkeys(":breakadd func Xbr\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd func Xbreak_func", @:)
+ call feedkeys(":breakadd func 20 Xbr\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd func 20 Xbreak_func", @:)
+ call feedkeys(":breakadd func 20 Xbr\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd func 20 Xbreak_func", @:)
+ call feedkeys(":breakadd func 20x Xbr\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd func 20x Xbr\t", @:)
+ call feedkeys(":breakadd func 20\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd func 20\t", @:)
+ call feedkeys(":breakadd func 20x\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd func 20x\t", @:)
+ call feedkeys(":breakadd func Xbreak_func \<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd func Xbreak_func ", @:)
+ call feedkeys(":breakadd func X1B2C3\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd func X1B2C3", @:)
+ delfunc Xbreak_func
+
+ " Test for :breakadd expr <expression>
+ let g:Xtest_var = 10
+ call feedkeys(":breakadd expr Xtest\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd expr Xtest_var", @:)
+ call feedkeys(":breakadd expr Xtest\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd expr Xtest_var", @:)
+ call feedkeys(":breakadd expr Xtest_var \<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd expr Xtest_var ", @:)
+ call feedkeys(":breakadd expr X1B2C3\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd expr X1B2C3", @:)
+ unlet g:Xtest_var
+
+ " Test for :breakadd here
+ call feedkeys(":breakadd here Xtest\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd here Xtest", @:)
+ call feedkeys(":breakadd here Xtest\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd here Xtest", @:)
+ call feedkeys(":breakadd here \<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakadd here ", @:)
+endfunc
+
+" Test for :breakdel argument completion
+func Test_cmdline_complete_breakdel()
+ call feedkeys(":breakdel \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakdel file func here", @:)
+ call feedkeys(":breakdel \<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakdel file", @:)
+ call feedkeys(":breakdel \<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakdel file", @:)
+ call feedkeys(":breakdel he\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakdel here", @:)
+ call feedkeys(":breakdel he\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakdel here", @:)
+ call feedkeys(":breakdel abc\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakdel abc", @:)
+
+ " Test for :breakdel file [lnum] <file>
+ call writefile([], 'Xscript')
+ call feedkeys(":breakdel file Xsc\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakdel file Xscript", @:)
+ call feedkeys(":breakdel file Xsc\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakdel file Xscript", @:)
+ call feedkeys(":breakdel file 20 Xsc\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakdel file 20 Xscript", @:)
+ call feedkeys(":breakdel file 20 Xsc\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakdel file 20 Xscript", @:)
+ call feedkeys(":breakdel file 20x Xsc\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakdel file 20x Xsc\t", @:)
+ call feedkeys(":breakdel file 20\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakdel file 20\t", @:)
+ call feedkeys(":breakdel file 20x\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakdel file 20x\t", @:)
+ call feedkeys(":breakdel file Xscript \<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakdel file Xscript ", @:)
+ call feedkeys(":breakdel file X1B2C3\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakdel file X1B2C3", @:)
+ call delete('Xscript')
+
+ " Test for :breakdel func [lnum] <function>
+ func Xbreak_func()
+ endfunc
+ call feedkeys(":breakdel func Xbr\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakdel func Xbreak_func", @:)
+ call feedkeys(":breakdel func Xbr\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakdel func Xbreak_func", @:)
+ call feedkeys(":breakdel func 20 Xbr\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakdel func 20 Xbreak_func", @:)
+ call feedkeys(":breakdel func 20 Xbr\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakdel func 20 Xbreak_func", @:)
+ call feedkeys(":breakdel func 20x Xbr\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakdel func 20x Xbr\t", @:)
+ call feedkeys(":breakdel func 20\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakdel func 20\t", @:)
+ call feedkeys(":breakdel func 20x\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakdel func 20x\t", @:)
+ call feedkeys(":breakdel func Xbreak_func \<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakdel func Xbreak_func ", @:)
+ call feedkeys(":breakdel func X1B2C3\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakdel func X1B2C3", @:)
+ delfunc Xbreak_func
+
+ " Test for :breakdel here
+ call feedkeys(":breakdel here Xtest\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakdel here Xtest", @:)
+ call feedkeys(":breakdel here Xtest\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakdel here Xtest", @:)
+ call feedkeys(":breakdel here \<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"breakdel here ", @:)
+endfunc
+
+" Test for :scriptnames argument completion
+func Test_cmdline_complete_scriptnames()
+ set wildmenu
+ call writefile(['let a = 1'], 'Xa1b2c3.vim')
+ source Xa1b2c3.vim
+ call feedkeys(":script \<Tab>\<Left>\<Left>\<C-B>\"\<CR>", 'tx')
+ call assert_match("\"script .*Xa1b2c3.vim$", @:)
+ call feedkeys(":script \<Tab>\<Left>\<Left>\<C-B>\"\<CR>", 'tx')
+ call assert_match("\"script .*Xa1b2c3.vim$", @:)
+ call feedkeys(":script b2c3\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"script b2c3", @:)
+ call feedkeys(":script 2\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_match("\"script 2\<Tab>$", @:)
+ call feedkeys(":script \<Tab>\<Left>\<Left> \<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_match("\"script .*Xa1b2c3.vim $", @:)
+ call feedkeys(":script \<Tab>\<Left>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"script ", @:)
+ call assert_match('Xa1b2c3.vim$', getcompletion('.*Xa1b2.*', 'scriptnames')[0])
+ call assert_equal([], getcompletion('Xa1b2', 'scriptnames'))
+ new
+ call feedkeys(":script \<Tab>\<Left>\<Left>\<CR>", 'tx')
+ call assert_equal('Xa1b2c3.vim', fnamemodify(@%, ':t'))
+ bw!
+ call delete('Xa1b2c3.vim')
+ set wildmenu&
+endfunc
+
" this was going over the end of IObuff
func Test_report_error_with_composing()
let caught = 'no'
@@ -2074,6 +3460,16 @@ func Test_cmdline_complete_substitute_short()
endfor
endfunc
+" Test for :! shell command argument completion
+func Test_cmdline_complete_bang_cmd_argument()
+ set wildoptions=fuzzy
+ call feedkeys(":!vim test_cmdline.\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"!vim test_cmdline.vim', @:)
+ set wildoptions&
+ call feedkeys(":!vim test_cmdline.\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"!vim test_cmdline.vim', @:)
+endfunc
+
func Check_completion()
call assert_equal('let a', getcmdline())
call assert_equal(6, getcmdpos())
@@ -2129,4 +3525,57 @@ func Test_wildmenu_pum_disable_while_shown()
set wildoptions& wildmenu&
endfunc
+func Test_setcmdline()
+ func SetText(text, pos)
+ autocmd CmdlineChanged * let g:cmdtype = expand('<afile>')
+ call assert_equal(0, setcmdline(a:text))
+ call assert_equal(a:text, getcmdline())
+ call assert_equal(len(a:text) + 1, getcmdpos())
+ call assert_equal(getcmdtype(), g:cmdtype)
+ unlet g:cmdtype
+ autocmd! CmdlineChanged
+
+ call assert_equal(0, setcmdline(a:text, a:pos))
+ call assert_equal(a:text, getcmdline())
+ call assert_equal(a:pos, getcmdpos())
+
+ call assert_fails('call setcmdline("' .. a:text .. '", -1)', 'E487:')
+ call assert_fails('call setcmdline({}, 0)', 'E1174:')
+ call assert_fails('call setcmdline("' .. a:text .. '", {})', 'E1210:')
+
+ return ''
+ endfunc
+
+ call feedkeys(":\<C-R>=SetText('set rtp?', 2)\<CR>\<CR>", 'xt')
+ call assert_equal('set rtp?', @:)
+
+ call feedkeys(":let g:str = input('? ')\<CR>", 't')
+ call feedkeys("\<C-R>=SetText('foo', 4)\<CR>\<CR>", 'xt')
+ call assert_equal('foo', g:str)
+ unlet g:str
+
+ delfunc SetText
+
+ " setcmdline() returns 1 when not editing the command line.
+ call assert_equal(1, 'foo'->setcmdline())
+
+ " Called in custom function
+ func CustomComplete(A, L, P)
+ call assert_equal(0, setcmdline("DoCmd "))
+ return "January\nFebruary\nMars\n"
+ endfunc
+
+ com! -nargs=* -complete=custom,CustomComplete DoCmd :
+ call feedkeys(":DoCmd \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"DoCmd January February Mars', @:)
+ delcom DoCmd
+ delfunc CustomComplete
+
+ " Called in <expr>
+ cnoremap <expr>a setcmdline('let foo=')
+ call feedkeys(":a\<CR>", 'tx')
+ call assert_equal('let foo=0', @:)
+ cunmap a
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_comments.vim b/src/nvim/testdir/test_comments.vim
new file mode 100644
index 0000000000..c34b85c42d
--- /dev/null
+++ b/src/nvim/testdir/test_comments.vim
@@ -0,0 +1,277 @@
+" Tests for the various flags in the 'comments' option
+
+" 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, '$'))
+ close!
+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, '$'))
+ close!
+endfunc
+
+" Test for the 'f' flag in 'comments' (only the first line has a comment
+" string)
+func Test_comment_firstline()
+ 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, '$'))
+ close!
+endfunc
+
+" Test for the 's', 'm' and 'e' flags in 'comments'
+" Test for automatically adding comment leaders in insert mode
+func Test_comment_threepiece()
+ 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 '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, '$'))
+ close!
+endfunc
+
+" Test for the 'O' flag in 'comments'
+func Test_comment_O()
+ new
+ setlocal comments=Ob:* fo+=ro
+ exe "normal i* B\nD\<C-C>kOA\<C-C>joC"
+ let expected =<< trim END
+ A
+ * B
+ * C
+ * D
+ END
+ call assert_equal(expected, getline(1, '$'))
+ close!
+endfunc
+
+" Test for using a multibyte character as a comment leader
+func Test_comment_multibyte_leader()
+ new
+ let t =<< trim END
+ {
+ X
+ Xa
+ XaY
+ XY
+ XYZ
+ X Y
+ X YZ
+ XX
+ XXa
+ XXY
+ }
+ END
+ call setline(1, t)
+ call cursor(2, 1)
+
+ set tw=2 fo=cqm comments=n:X
+ exe "normal gqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgq"
+ let t =<< trim END
+ X
+ Xa
+ XaY
+ XY
+ XYZ
+ X Y
+ X YZ
+ XX
+ XXa
+ XXY
+ END
+ exe "normal o\n" . join(t, "\n")
+
+ let expected =<< trim END
+ {
+ X
+ Xa
+ Xa
+ XY
+ XY
+ XY
+ XZ
+ X Y
+ X Y
+ X Z
+ XX
+ XXa
+ XXY
+
+ X
+ Xa
+ Xa
+ XY
+ XY
+ XY
+ XZ
+ X Y
+ X Y
+ X Z
+ XX
+ XXa
+ XXY
+ }
+ END
+ call assert_equal(expected, getline(1, '$'))
+
+ set tw& fo& comments&
+ close!
+endfunc
+
+" Test for a space character in 'comments' setting
+func Test_comment_space()
+ new
+ setlocal comments=b:\ > fo+=ro
+ exe "normal i> B\nD\<C-C>ggOA\<C-C>joC"
+ exe "normal Go > F\nH\<C-C>kOE\<C-C>joG"
+ let expected =<< trim END
+ A
+ > B
+ C
+ D
+ > E
+ > F
+ > G
+ > H
+ END
+ call assert_equal(expected, getline(1, '$'))
+ close!
+endfunc
+
+" Test for formatting lines with and without comments
+func Test_comment_format_lines()
+ new
+ call setline(1, ['one', '/* two */', 'three'])
+ normal gggqG
+ call assert_equal(['one', '/* two */', 'three'], getline(1, '$'))
+ close!
+endfunc
+
+" Test for using 'a' in 'formatoptions' with comments
+func Test_comment_autoformat()
+ new
+ setlocal formatoptions+=a
+ call feedkeys("a- one\n- two\n", 'xt')
+ call assert_equal(['- one', '- two', ''], getline(1, '$'))
+
+ %d
+ call feedkeys("a\none\n", 'xt')
+ call assert_equal(['', 'one', ''], getline(1, '$'))
+
+ setlocal formatoptions+=aw
+ %d
+ call feedkeys("aone \ntwo\n", 'xt')
+ call assert_equal(['one two', ''], getline(1, '$'))
+
+ %d
+ call feedkeys("aone\ntwo\n", 'xt')
+ call assert_equal(['one', 'two', ''], getline(1, '$'))
+
+ close!
+endfunc
+
+" Test for joining lines with comments ('j' flag in 'formatoptions')
+func Test_comment_join_lines_fo_j()
+ new
+ setlocal fo+=j comments=://
+ call setline(1, ['i++; // comment1', ' // comment2'])
+ normal J
+ call assert_equal('i++; // comment1 comment2', getline(1))
+ setlocal fo-=j
+ call setline(1, ['i++; // comment1', ' // comment2'])
+ normal J
+ call assert_equal('i++; // comment1 // comment2', getline(1))
+ " Test with nested comments
+ setlocal fo+=j comments=n:>,n:)
+ call setline(1, ['i++; > ) > ) comment1', ' > ) comment2'])
+ normal J
+ call assert_equal('i++; > ) > ) comment1 comment2', getline(1))
+ close!
+endfunc
+
+" Test for formatting lines where only the first line has a comment.
+func Test_comment_format_firstline_comment()
+ new
+ setlocal formatoptions=tcq
+ call setline(1, ['- one two', 'three'])
+ normal gggqG
+ call assert_equal(['- one two three'], getline(1, '$'))
+
+ %d
+ call setline(1, ['- one', '- two'])
+ normal gggqG
+ call assert_equal(['- one', '- two'], getline(1, '$'))
+ close!
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_compiler.vim b/src/nvim/testdir/test_compiler.vim
index 3dc8710d63..ec7d143030 100644
--- a/src/nvim/testdir/test_compiler.vim
+++ b/src/nvim/testdir/test_compiler.vim
@@ -1,9 +1,11 @@
" Test the :compiler command
+source check.vim
+source shared.vim
+
func Test_compiler()
- if !executable('perl')
- return
- endif
+ CheckExecutable perl
+ CheckFeature quickfix
" $LANG changes the output of Perl.
if $LANG != ''
diff --git a/src/nvim/testdir/test_const.vim b/src/nvim/testdir/test_const.vim
index 0d064617a5..4a6c8779dd 100644
--- a/src/nvim/testdir/test_const.vim
+++ b/src/nvim/testdir/test_const.vim
@@ -194,6 +194,14 @@ func Test_lockvar()
if 0 | lockvar x | endif
let x = 'again'
+
+ let val = [1, 2, 3]
+ lockvar 0 val
+ let val[0] = 9
+ call assert_equal([9, 2, 3], val)
+ call add(val, 4)
+ call assert_equal([9, 2, 3, 4], val)
+ call assert_fails('let val = [4, 5, 6]', 'E1122:')
endfunc
@@ -231,6 +239,14 @@ func Test_const_with_special_variables()
call assert_fails('const &filetype = "vim"', 'E996:')
call assert_fails('const &l:filetype = "vim"', 'E996:')
call assert_fails('const &g:encoding = "utf-8"', 'E996:')
+
+ call assert_fails('const [a, $CONST_FOO] = [369, "abc"]', 'E996:')
+ call assert_equal(369, a)
+ call assert_equal(v:null, getenv("CONST_FOO"))
+
+ call assert_fails('const [b; $CONST_FOO] = [246, 2, "abc"]', 'E996:')
+ call assert_equal(246, b)
+ call assert_equal(v:null, getenv("CONST_FOO"))
endfunc
func Test_const_with_eval_name()
@@ -274,3 +290,5 @@ func Test_lock_depth_is_2()
const d2 = #{a: 0, b: lvar, c: 4}
let d2.b[1] = 'd'
endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_cpoptions.vim b/src/nvim/testdir/test_cpoptions.vim
new file mode 100644
index 0000000000..b9307ab30b
--- /dev/null
+++ b/src/nvim/testdir/test_cpoptions.vim
@@ -0,0 +1,927 @@
+" Test for the various 'cpoptions' (cpo) flags
+
+source check.vim
+source shared.vim
+source view_util.vim
+
+" Test for the 'a' flag in 'cpo'. Reading a file should set the alternate
+" file name.
+func Test_cpo_a()
+ let save_cpo = &cpo
+ call writefile(['one'], 'XfileCpoA')
+ " Wipe out all the buffers, so that the alternate file is empty
+ edit Xfoo | %bw
+ set cpo-=a
+ new
+ read XfileCpoA
+ call assert_equal('', @#)
+ %d
+ set cpo+=a
+ read XfileCpoA
+ call assert_equal('XfileCpoA', @#)
+ close!
+ call delete('XfileCpoA')
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'A' flag in 'cpo'. Writing a file should set the alternate
+" file name.
+func Test_cpo_A()
+ let save_cpo = &cpo
+ " Wipe out all the buffers, so that the alternate file is empty
+ edit Xfoo | %bw
+ set cpo-=A
+ new Xfile1
+ write Xfile2
+ call assert_equal('', @#)
+ %bw
+ call delete('Xfile2')
+ new Xfile1
+ set cpo+=A
+ write Xfile2
+ call assert_equal('Xfile2', @#)
+ close!
+ call delete('Xfile2')
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'b' flag in 'cpo'. "\|" at the end of a map command is
+" recognized as the end of the map.
+func Test_cpo_b()
+ let save_cpo = &cpo
+ set cpo+=b
+ nnoremap <F5> :pwd\<CR>\|let i = 1
+ call assert_equal(':pwd\<CR>\', maparg('<F5>'))
+ nunmap <F5>
+ exe "nnoremap <F5> :pwd\<C-V>|let i = 1"
+ call assert_equal(':pwd|let i = 1', maparg('<F5>'))
+ nunmap <F5>
+ set cpo-=b
+ nnoremap <F5> :pwd\<CR>\|let i = 1
+ call assert_equal(':pwd\<CR>|let i = 1', maparg('<F5>'))
+ let &cpo = save_cpo
+ nunmap <F5>
+endfunc
+
+" Test for the 'B' flag in 'cpo'. A backslash in mappings, abbreviations, user
+" commands and menu commands has no special meaning.
+func Test_cpo_B()
+ let save_cpo = &cpo
+ new
+ set cpo-=B
+ iabbr <buffer> abc ab\<BS>d
+ exe "normal iabc "
+ call assert_equal('ab<BS>d ', getline(1))
+ %d
+ set cpo+=B
+ iabbr <buffer> abc ab\<BS>d
+ exe "normal iabc "
+ call assert_equal('abd ', getline(1))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'c' flag in 'cpo'.
+func Test_cpo_c()
+ let save_cpo = &cpo
+ set cpo+=c
+ new
+ call setline(1, ' abababababab')
+ exe "normal gg/abab\<CR>"
+ call assert_equal(3, searchcount().total)
+ set cpo-=c
+ exe "normal gg/abab\<CR>"
+ call assert_equal(5, searchcount().total)
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'C' flag in 'cpo' (line continuation)
+func Test_cpo_C()
+ let save_cpo = &cpo
+ call writefile(['let l = [', '\ 1,', '\ 2]'], 'XfileCpoC')
+ set cpo-=C
+ source XfileCpoC
+ call assert_equal([1, 2], g:l)
+ set cpo+=C
+ call assert_fails('source XfileCpoC', ['E697:', 'E10:'])
+ call delete('XfileCpoC')
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'd' flag in 'cpo' (tags relative to the current file)
+func Test_cpo_d()
+ let save_cpo = &cpo
+ call mkdir('XdirCpoD')
+ call writefile(["one\tXfile1\t/^one$/"], 'tags')
+ call writefile(["two\tXfile2\t/^two$/"], 'XdirCpoD/tags')
+ set tags=./tags
+ set cpo-=d
+ edit XdirCpoD/Xfile
+ call assert_equal('two', taglist('.*')[0].name)
+ set cpo+=d
+ call assert_equal('one', taglist('.*')[0].name)
+ %bw!
+ call delete('tags')
+ call delete('XdirCpoD', 'rf')
+ set tags&
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'D' flag in 'cpo' (digraph after a r, f or t)
+func Test_cpo_D()
+ CheckFeature digraphs
+ let save_cpo = &cpo
+ new
+ set cpo-=D
+ call setline(1, 'abcdefgh|')
+ exe "norm! 1gg0f\<c-k>!!"
+ call assert_equal(9, col('.'))
+ set cpo+=D
+ exe "norm! 1gg0f\<c-k>!!"
+ call assert_equal(1, col('.'))
+ set cpo-=D
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'e' flag in 'cpo'
+func Test_cpo_e()
+ let save_cpo = &cpo
+ let @a='let i = 45'
+ set cpo+=e
+ call feedkeys(":@a\<CR>", 'xt')
+ call assert_equal(45, i)
+ set cpo-=e
+ call feedkeys(":@a\<CR>6\<CR>", 'xt')
+ call assert_equal(456, i)
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'E' flag in 'cpo' with yank, change, delete, etc. operators
+func Test_cpo_E()
+ new
+ call setline(1, '')
+ set cpo+=E
+ " yank an empty line
+ call assert_beeps('normal "ayl')
+ " change an empty line
+ call assert_beeps('normal lcTa')
+ call assert_beeps('normal 0c0')
+ " delete an empty line
+ call assert_beeps('normal D')
+ call assert_beeps('normal dl')
+ call assert_equal('', getline(1))
+ " change case of an empty line
+ call assert_beeps('normal gul')
+ call assert_beeps('normal gUl')
+ " replace a character
+ call assert_beeps('normal vrx')
+ " increment and decrement
+ call assert_beeps('exe "normal v\<C-A>"')
+ call assert_beeps('exe "normal v\<C-X>"')
+ set cpo-=E
+ close!
+endfunc
+
+" Test for the 'f' flag in 'cpo' (read in an empty buffer sets the file name)
+func Test_cpo_f()
+ let save_cpo = &cpo
+ new
+ set cpo-=f
+ read test_cpoptions.vim
+ call assert_equal('', @%)
+ %d
+ set cpo+=f
+ read test_cpoptions.vim
+ call assert_equal('test_cpoptions.vim', @%)
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'F' flag in 'cpo' (write in an empty buffer sets the file name)
+func Test_cpo_F()
+ let save_cpo = &cpo
+ new
+ set cpo-=F
+ write XfileCpoF
+ call assert_equal('', @%)
+ call delete('XfileCpoF')
+ set cpo+=F
+ write XfileCpoF
+ call assert_equal('XfileCpoF', @%)
+ close!
+ call delete('XfileCpoF')
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'g' flag in 'cpo' (jump to line 1 when re-editing a file)
+func Test_cpo_g()
+ throw 'Skipped: Nvim does not support cpoptions flag "g"'
+ let save_cpo = &cpo
+ new test_cpoptions.vim
+ set cpo-=g
+ normal 20G
+ edit
+ call assert_equal(20, line('.'))
+ set cpo+=g
+ edit
+ call assert_equal(1, line('.'))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for inserting text in a line with only spaces ('H' flag in 'cpoptions')
+func Test_cpo_H()
+ throw 'Skipped: Nvim does not support cpoptions flag "H"'
+ let save_cpo = &cpo
+ new
+ set cpo-=H
+ 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))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" TODO: Add a test for the 'i' flag in 'cpo'
+" Interrupting the reading of a file will leave it modified.
+
+" Test for the 'I' flag in 'cpo' (deleting autoindent when using arrow keys)
+func Test_cpo_I()
+ let save_cpo = &cpo
+ new
+ setlocal autoindent
+ set cpo+=I
+ exe "normal i one\<CR>\<Up>"
+ call assert_equal(' ', getline(2))
+ set cpo-=I
+ %d
+ exe "normal i one\<CR>\<Up>"
+ call assert_equal('', getline(2))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'j' flag in 'cpo' is in the test_join.vim file.
+
+" Test for the 'J' flag in 'cpo' (two spaces after a sentence)
+func Test_cpo_J()
+ let save_cpo = &cpo
+ new
+ set cpo-=J
+ call setline(1, 'one. two! three? four."'' five.)]')
+ normal 0
+ for colnr in [6, 12, 19, 28, 34]
+ normal )
+ call assert_equal(colnr, col('.'))
+ endfor
+ for colnr in [28, 19, 12, 6, 1]
+ normal (
+ call assert_equal(colnr, col('.'))
+ endfor
+ set cpo+=J
+ normal 0
+ for colnr in [12, 28, 34]
+ normal )
+ call assert_equal(colnr, col('.'))
+ endfor
+ for colnr in [28, 12, 1]
+ normal (
+ call assert_equal(colnr, col('.'))
+ endfor
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" TODO: Add a test for the 'k' flag in 'cpo'.
+" Disable the recognition of raw key codes in mappings, abbreviations, and the
+" "to" part of menu commands.
+
+" TODO: Add a test for the 'K' flag in 'cpo'.
+" Don't wait for a key code to complete when it is halfway a mapping.
+
+" Test for the 'l' flag in 'cpo' (backslash in a [] range)
+func Test_cpo_l()
+ let save_cpo = &cpo
+ new
+ call setline(1, ['', "a\tc" .. '\t'])
+ set cpo-=l
+ exe 'normal gg/[\t]' .. "\<CR>"
+ call assert_equal([2, 8], [col('.'), virtcol('.')])
+ set cpo+=l
+ exe 'normal gg/[\t]' .. "\<CR>"
+ call assert_equal([4, 10], [col('.'), virtcol('.')])
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for inserting tab in virtual replace mode ('L' flag in 'cpoptions')
+func Test_cpo_L()
+ let save_cpo = &cpo
+ new
+ set cpo-=L
+ 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))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" TODO: Add a test for the 'm' flag in 'cpo'.
+" When included, a showmatch will always wait half a second. When not
+" included, a showmatch will wait half a second or until a character is typed.
+
+" Test for the 'M' flag in 'cpo' (% with escape parenthesis)
+func Test_cpo_M()
+ let save_cpo = &cpo
+ new
+ call setline(1, ['( \( )', '\( ( \)'])
+
+ set cpo-=M
+ call cursor(1, 1)
+ normal %
+ call assert_equal(6, col('.'))
+ call cursor(1, 4)
+ call assert_beeps('normal %')
+ call cursor(2, 2)
+ normal %
+ call assert_equal(7, col('.'))
+ call cursor(2, 4)
+ call assert_beeps('normal %')
+
+ set cpo+=M
+ call cursor(1, 4)
+ normal %
+ call assert_equal(6, col('.'))
+ call cursor(1, 1)
+ call assert_beeps('normal %')
+ call cursor(2, 4)
+ normal %
+ call assert_equal(7, col('.'))
+ call cursor(2, 1)
+ call assert_beeps('normal %')
+
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'n' flag in 'cpo' (using number column for wrapped lines)
+func Test_cpo_n()
+ let save_cpo = &cpo
+ new
+ call setline(1, repeat('a', &columns))
+ setlocal number
+ set cpo-=n
+ redraw!
+ call assert_equal(' aaaa', Screenline(2))
+ set cpo+=n
+ redraw!
+ call assert_equal('aaaa', Screenline(2))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'o' flag in 'cpo' (line offset to search command)
+func Test_cpo_o()
+ let save_cpo = &cpo
+ new
+ call setline(1, ['', 'one', 'two', 'three', 'one', 'two', 'three'])
+ set cpo-=o
+ exe "normal /one/+2\<CR>"
+ normal n
+ call assert_equal(7, line('.'))
+ set cpo+=o
+ exe "normal /one/+2\<CR>"
+ normal n
+ call assert_equal(5, line('.'))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'O' flag in 'cpo' (overwriting an existing file)
+func Test_cpo_O()
+ let save_cpo = &cpo
+ new XfileCpoO
+ call setline(1, 'one')
+ call writefile(['two'], 'XfileCpoO')
+ set cpo-=O
+ call assert_fails('write', 'E13:')
+ set cpo+=O
+ write
+ call assert_equal(['one'], readfile('XfileCpoO'))
+ close!
+ call delete('XfileCpoO')
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'p' flag in 'cpo' is in the test_lispwords.vim file.
+
+" Test for the 'P' flag in 'cpo' (appending to a file sets the current file
+" name)
+func Test_cpo_P()
+ let save_cpo = &cpo
+ call writefile([], 'XfileCpoP')
+ new
+ call setline(1, 'one')
+ set cpo+=F
+ set cpo-=P
+ write >> XfileCpoP
+ call assert_equal('', @%)
+ set cpo+=P
+ write >> XfileCpoP
+ call assert_equal('XfileCpoP', @%)
+ close!
+ call delete('XfileCpoP')
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'q' flag in 'cpo' (joining multiple lines)
+func Test_cpo_q()
+ let save_cpo = &cpo
+ new
+ call setline(1, ['one', 'two', 'three', 'four', 'five'])
+ set cpo-=q
+ normal gg4J
+ call assert_equal(14, col('.'))
+ %d
+ call setline(1, ['one', 'two', 'three', 'four', 'five'])
+ set cpo+=q
+ normal gg4J
+ call assert_equal(4, col('.'))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'r' flag in 'cpo' (redo command with a search motion)
+func Test_cpo_r()
+ let save_cpo = &cpo
+ new
+ call setline(1, repeat(['one two three four'], 2))
+ set cpo-=r
+ exe "normal ggc/two\<CR>abc "
+ let @/ = 'three'
+ normal 2G.
+ call assert_equal('abc two three four', getline(2))
+ %d
+ call setline(1, repeat(['one two three four'], 2))
+ set cpo+=r
+ exe "normal ggc/two\<CR>abc "
+ let @/ = 'three'
+ normal 2G.
+ call assert_equal('abc three four', getline(2))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'R' flag in 'cpo' (clear marks after a filter command)
+func Test_cpo_R()
+ CheckUnix
+ let save_cpo = &cpo
+ new
+ call setline(1, ['three', 'one', 'two'])
+ set cpo-=R
+ 3mark r
+ %!sort
+ call assert_equal(3, line("'r"))
+ %d
+ call setline(1, ['three', 'one', 'two'])
+ set cpo+=R
+ 3mark r
+ %!sort
+ call assert_equal(0, line("'r"))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" TODO: Add a test for the 's' flag in 'cpo'.
+" Set buffer options when entering the buffer for the first time. If not
+" present the options are set when the buffer is created.
+
+" Test for the 'S' flag in 'cpo' (copying buffer options)
+func Test_cpo_S()
+ let save_cpo = &cpo
+ new Xfile1
+ set noautoindent
+ new Xfile2
+ set cpo-=S
+ set autoindent
+ wincmd p
+ call assert_equal(0, &autoindent)
+ wincmd p
+ call assert_equal(1, &autoindent)
+ set cpo+=S
+ wincmd p
+ call assert_equal(1, &autoindent)
+ set noautoindent
+ wincmd p
+ call assert_equal(0, &autoindent)
+ wincmd t
+ close!
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 't' flag in 'cpo' is in the test_tagjump.vim file.
+
+" Test for the 'u' flag in 'cpo' (Vi-compatible undo)
+func Test_cpo_u()
+ let save_cpo = &cpo
+ new
+ set cpo-=u
+ exe "normal iabc\<C-G>udef\<C-G>ughi"
+ normal uu
+ call assert_equal('abc', getline(1))
+ %d
+ set cpo+=u
+ exe "normal iabc\<C-G>udef\<C-G>ughi"
+ normal uu
+ call assert_equal('abcdefghi', getline(1))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" TODO: Add a test for the 'v' flag in 'cpo'.
+" Backspaced characters remain visible on the screen in Insert mode.
+
+" Test for the 'w' flag in 'cpo' ('cw' on a blank character changes only one
+" character)
+func Test_cpo_w()
+ throw 'Skipped: Nvim does not support cpoptions flag "w"'
+ let save_cpo = &cpo
+ new
+ set cpo+=w
+ call setline(1, 'here are some words')
+ norm! 1gg0elcwZZZ
+ call assert_equal('hereZZZ are some words', getline('.'))
+ norm! 1gg2elcWYYY
+ call assert_equal('hereZZZ areYYY some words', getline('.'))
+ set cpo-=w
+ call setline(1, 'here are some words')
+ norm! 1gg0elcwZZZ
+ call assert_equal('hereZZZare some words', getline('.'))
+ norm! 1gg2elcWYYY
+ call assert_equal('hereZZZare someYYYwords', getline('.'))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'W' flag in 'cpo' is in the test_writefile.vim file
+
+" Test for the 'x' flag in 'cpo' (Esc on command-line executes command)
+func Test_cpo_x()
+ let save_cpo = &cpo
+ set cpo-=x
+ let i = 1
+ call feedkeys(":let i=10\<Esc>", 'xt')
+ call assert_equal(1, i)
+ set cpo+=x
+ call feedkeys(":let i=10\<Esc>", 'xt')
+ call assert_equal(10, i)
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'X' flag in 'cpo' ('R' with a count)
+func Test_cpo_X()
+ let save_cpo = &cpo
+ new
+ call setline(1, 'aaaaaa')
+ set cpo-=X
+ normal gg4Rx
+ call assert_equal('xxxxaa', getline(1))
+ normal ggRy
+ normal 4.
+ call assert_equal('yyyyaa', getline(1))
+ call setline(1, 'aaaaaa')
+ set cpo+=X
+ normal gg4Rx
+ call assert_equal('xxxxaaaaa', getline(1))
+ normal ggRy
+ normal 4.
+ call assert_equal('yyyyxxxaaaaa', getline(1))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'y' flag in 'cpo' (repeating a yank command)
+func Test_cpo_y()
+ let save_cpo = &cpo
+ new
+ call setline(1, ['one', 'two'])
+ set cpo-=y
+ normal ggyy
+ normal 2G.
+ call assert_equal("one\n", @")
+ %d
+ call setline(1, ['one', 'two'])
+ set cpo+=y
+ normal ggyy
+ normal 2G.
+ call assert_equal("two\n", @")
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'Z' flag in 'cpo' (write! resets 'readonly')
+func Test_cpo_Z()
+ let save_cpo = &cpo
+ call writefile([], 'XfileCpoZ')
+ new XfileCpoZ
+ setlocal readonly
+ set cpo-=Z
+ write!
+ call assert_equal(0, &readonly)
+ set cpo+=Z
+ setlocal readonly
+ write!
+ call assert_equal(1, &readonly)
+ close!
+ call delete('XfileCpoZ')
+ let &cpo = save_cpo
+endfunc
+
+" Test for the '!' flag in 'cpo' is in the test_normal.vim file
+
+" Test for displaying dollar when changing text ('$' flag in 'cpoptions')
+func Test_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 the '%' flag in 'cpo' (parenthesis matching inside strings)
+func Test_cpo_percent()
+ let save_cpo = &cpo
+ new
+ call setline(1, ' if (strcmp("ab)cd(", s))')
+ set cpo-=%
+ normal 8|%
+ call assert_equal(28, col('.'))
+ normal 15|%
+ call assert_equal(27, col('.'))
+ normal 27|%
+ call assert_equal(15, col('.'))
+ call assert_beeps("normal 19|%")
+ call assert_beeps("normal 22|%")
+ set cpo+=%
+ normal 8|%
+ call assert_equal(28, col('.'))
+ normal 15|%
+ call assert_equal(19, col('.'))
+ normal 27|%
+ call assert_equal(22, col('.'))
+ normal 19|%
+ call assert_equal(15, col('.'))
+ normal 22|%
+ call assert_equal(27, col('.'))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for cursor movement with '-' in 'cpoptions'
+func Test_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:')
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the '+' flag in 'cpo' ('write file' command resets the 'modified'
+" flag)
+func Test_cpo_plus()
+ let save_cpo = &cpo
+ call writefile([], 'XfileCpoPlus')
+ new XfileCpoPlus
+ call setline(1, 'foo')
+ write X1
+ call assert_equal(1, &modified)
+ set cpo+=+
+ write X2
+ call assert_equal(0, &modified)
+ close!
+ call delete('XfileCpoPlus')
+ call delete('X1')
+ call delete('X2')
+ let &cpo = save_cpo
+endfunc
+
+" Test for the '*' flag in 'cpo' (':*' is same as ':@')
+func Test_cpo_star()
+ throw 'Skipped: Nvim does not support cpoptions flag "*"'
+ let save_cpo = &cpo
+ let x = 0
+ new
+ set cpo-=*
+ let @a = 'let x += 1'
+ call assert_fails('*a', 'E20:')
+ set cpo+=*
+ *a
+ call assert_equal(1, x)
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the '<' flag in 'cpo' is in the test_mapping.vim file
+
+" Test for the '>' flag in 'cpo' (use a new line when appending to a register)
+func Test_cpo_gt()
+ let save_cpo = &cpo
+ new
+ call setline(1, 'one two')
+ set cpo-=>
+ let @r = ''
+ normal gg"Rye
+ normal "Rye
+ call assert_equal("oneone", @r)
+ set cpo+=>
+ let @r = ''
+ normal gg"Rye
+ normal "Rye
+ call assert_equal("\none\none", @r)
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the ';' flag in 'cpo'
+" Test for t,f,F,T movement commands and 'cpo-;' setting
+func Test_cpo_semicolon()
+ let save_cpo = &cpo
+ new
+ call append(0, ["aaa two three four", " zzz", "yyy ",
+ \ "bbb yee yoo four", "ccc two three four",
+ \ "ddd yee yoo four"])
+ set cpo-=;
+ 1
+ normal! 0tt;D
+ 2
+ normal! 0fz;D
+ 3
+ normal! $Fy;D
+ 4
+ normal! $Ty;D
+ set cpo+=;
+ 5
+ normal! 0tt;;D
+ 6
+ normal! $Ty;;D
+
+ call assert_equal('aaa two', getline(1))
+ call assert_equal(' z', getline(2))
+ call assert_equal('y', getline(3))
+ call assert_equal('bbb y', getline(4))
+ call assert_equal('ccc', getline(5))
+ call assert_equal('ddd yee y', getline(6))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the '#' flag in 'cpo' (count before 'D', 'o' and 'O' operators)
+func Test_cpo_hash()
+ throw 'Skipped: Nvim does not support cpoptions flag "#"'
+ let save_cpo = &cpo
+ new
+ set cpo-=#
+ call setline(1, ['one', 'two', 'three'])
+ normal gg2D
+ call assert_equal(['three'], getline(1, '$'))
+ normal gg2ofour
+ call assert_equal(['three', 'four', 'four'], getline(1, '$'))
+ normal gg2Otwo
+ call assert_equal(['two', 'two', 'three', 'four', 'four'], getline(1, '$'))
+ %d
+ set cpo+=#
+ call setline(1, ['one', 'two', 'three'])
+ normal gg2D
+ call assert_equal(['', 'two', 'three'], getline(1, '$'))
+ normal gg2oone
+ call assert_equal(['', 'one', 'two', 'three'], getline(1, '$'))
+ normal gg2Ozero
+ call assert_equal(['zero', '', 'one', 'two', 'three'], getline(1, '$'))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the '&' flag in 'cpo'. The swap file is kept when a buffer is still
+" loaded and ':preserve' is used.
+func Test_cpo_ampersand()
+ throw 'Skipped: Nvim does not support cpoptions flag "&"'
+ call writefile(['one'], 'XfileCpoAmp')
+ let after =<< trim [CODE]
+ set cpo+=&
+ preserve
+ qall
+ [CODE]
+ if RunVim([], after, 'XfileCpoAmp')
+ call assert_equal(1, filereadable('.XfileCpoAmp.swp'))
+ call delete('.XfileCpoAmp.swp')
+ endif
+ call delete('XfileCpoAmp')
+endfunc
+
+" Test for the '\' flag in 'cpo' (backslash in a [] range in a search pattern)
+func Test_cpo_backslash()
+ throw 'Skipped: Nvim does not support cpoptions flag "\"'
+ let save_cpo = &cpo
+ new
+ call setline(1, ['', " \\-string"])
+ set cpo-=\
+ exe 'normal gg/[ \-]' .. "\<CR>n"
+ call assert_equal(3, col('.'))
+ set cpo+=\
+ exe 'normal gg/[ \-]' .. "\<CR>n"
+ call assert_equal(2, col('.'))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the '/' flag in 'cpo' is in the test_substitute.vim file
+
+" Test for the '{' flag in 'cpo' (the "{" and "}" commands stop at a {
+" character at the start of a line)
+func Test_cpo_brace()
+ throw 'Skipped: Nvim does not support cpoptions flag "{"'
+ let save_cpo = &cpo
+ new
+ call setline(1, ['', '{', ' int i;', '}', ''])
+ set cpo-={
+ normal gg}
+ call assert_equal(5, line('.'))
+ normal G{
+ call assert_equal(1, line('.'))
+ set cpo+={
+ normal gg}
+ call assert_equal(2, line('.'))
+ normal G{
+ call assert_equal(2, line('.'))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the '.' flag in 'cpo' (:cd command fails if the current buffer is
+" modified)
+func Test_cpo_dot()
+ throw 'Skipped: Nvim does not support cpoptions flag "."'
+ let save_cpo = &cpo
+ new Xfoo
+ call setline(1, 'foo')
+ let save_dir = getcwd()
+ set cpo+=.
+
+ " :cd should fail when buffer is modified and 'cpo' contains dot.
+ call assert_fails('cd ..', 'E747:')
+ call assert_equal(save_dir, getcwd())
+
+ " :cd with exclamation mark should succeed.
+ cd! ..
+ call assert_notequal(save_dir, getcwd())
+
+ " :cd should succeed when buffer has been written.
+ w!
+ exe 'cd ' .. fnameescape(save_dir)
+ call assert_equal(save_dir, getcwd())
+
+ call delete('Xfoo')
+ set cpo&
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_cscope.vim b/src/nvim/testdir/test_cscope.vim
deleted file mode 100644
index 76ea35fa10..0000000000
--- a/src/nvim/testdir/test_cscope.vim
+++ /dev/null
@@ -1,344 +0,0 @@
-" Test for cscope commands.
-
-source check.vim
-CheckFeature cscope
-CheckFeature quickfix
-
-if !executable('cscope')
- throw 'Skipped: cscope program missing'
-endif
-
-func CscopeSetupOrClean(setup)
- if a:setup
- noa sp samples/memfile_test.c
- saveas! Xmemfile_test.c
- call system('cscope -bk -fXcscope.out Xmemfile_test.c')
- call system('cscope -bk -fXcscope2.out Xmemfile_test.c')
- cscope add Xcscope.out
- set cscopequickfix=s-,g-,d-,c-,t-,e-,f-,i-,a-
- else
- cscope kill -1
- for file in ['Xcscope.out', 'Xcscope2.out', 'Xmemfile_test.c']
- call delete(file)
- endfo
- endif
-endfunc
-
-func Test_cscopeWithCscopeConnections()
- call CscopeSetupOrClean(1)
- " Test: E568: duplicate cscope database not added
- try
- set nocscopeverbose
- cscope add Xcscope.out
- set cscopeverbose
- catch
- call assert_report('exception thrown')
- endtry
- call assert_fails('cscope add', 'E560')
- call assert_fails('cscope add Xcscope.out', 'E568')
- call assert_fails('cscope add doesnotexist.out', 'E563')
- if has('unix')
- call assert_fails('cscope add /dev/null', 'E564:')
- endif
-
- " Test: Find this C-Symbol
- for cmd in ['cs find s main', 'cs find 0 main']
- let a = execute(cmd)
- " Test where it moves the cursor
- call assert_equal('main(void)', getline('.'))
- " Test the output of the :cs command
- call assert_match('\n(1 of 1): <<main>> main(void )', a)
- endfor
-
- " Test: Find this definition
- for cmd in ['cs find g test_mf_hash',
- \ 'cs find 1 test_mf_hash',
- \ 'cs find 1 test_mf_hash'] " leading space ignored.
- exe cmd
- call assert_equal(['', '/*', ' * Test mf_hash_*() functions.', ' */', ' static void', 'test_mf_hash(void)', '{'], getline(line('.')-5, line('.')+1))
- endfor
-
- " Test: Find functions called by this function
- for cmd in ['cs find d test_mf_hash', 'cs find 2 test_mf_hash']
- let a = execute(cmd)
- call assert_match('\n(1 of 42): <<mf_hash_init>> mf_hash_init(&ht);', a)
- call assert_equal(' mf_hash_init(&ht);', getline('.'))
- endfor
-
- " Test: Find functions calling this function
- for cmd in ['cs find c test_mf_hash', 'cs find 3 test_mf_hash']
- let a = execute(cmd)
- call assert_match('\n(1 of 1): <<main>> test_mf_hash();', a)
- call assert_equal(' test_mf_hash();', getline('.'))
- endfor
-
- " Test: Find this text string
- for cmd in ['cs find t Bram', 'cs find 4 Bram']
- let a = execute(cmd)
- call assert_match('(1 of 1): <<<unknown>>> \* VIM - Vi IMproved^Iby Bram Moolenaar', a)
- call assert_equal(' * VIM - Vi IMproved by Bram Moolenaar', getline('.'))
- endfor
-
- " Test: Find this egrep pattern
- " test all matches returned by cscope
- for cmd in ['cs find e ^\#includ.', 'cs find 6 ^\#includ.']
- let a = execute(cmd)
- call assert_match('\n(1 of 3): <<<unknown>>> #include <assert.h>', a)
- call assert_equal('#include <assert.h>', getline('.'))
- cnext
- call assert_equal('#include "main.c"', getline('.'))
- cnext
- call assert_equal('#include "memfile.c"', getline('.'))
- call assert_fails('cnext', 'E553:')
- endfor
-
- " Test: Find the same egrep pattern using lcscope this time.
- let a = execute('lcs find e ^\#includ.')
- call assert_match('\n(1 of 3): <<<unknown>>> #include <assert.h>', a)
- call assert_equal('#include <assert.h>', getline('.'))
- lnext
- call assert_equal('#include "main.c"', getline('.'))
- lnext
- call assert_equal('#include "memfile.c"', getline('.'))
- call assert_fails('lnext', 'E553:')
-
- " Test: Find this file
- 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\+B')
- call assert_equal('Xmemfile_test.c', @%)
- endfor
-
- " Test: Find files #including this file
- for cmd in ['cs find i assert.h', 'cs find 8 assert.h']
- enew
- 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\+B')
- call assert_equal('(1 of 1): <<global>> #include <assert.h>', alines[2])
- call assert_equal('#include <assert.h>', getline('.'))
- endfor
-
- " Test: Invalid find command
- call assert_fails('cs find', 'E560:')
- call assert_fails('cs find x', 'E560:')
-
- if has('float')
- " Test: Find places where this symbol is assigned a value
- " this needs a cscope >= 15.8
- " unfortunately, Travis has cscope version 15.7
- let cscope_version = systemlist('cscope --version')[0]
- let cs_version = str2float(matchstr(cscope_version, '\d\+\(\.\d\+\)\?'))
- if cs_version >= 15.8
- for cmd in ['cs find a item', 'cs find 9 item']
- let a = execute(cmd)
- call assert_equal(['', '(1 of 4): <<test_mf_hash>> item = (mf_hashitem_T *)lalloc_clear(sizeof(*item), FALSE);'], split(a, '\n', 1))
- call assert_equal(' item = (mf_hashitem_T *)lalloc_clear(sizeof(*item), FALSE);', getline('.'))
- cnext
- call assert_equal(' item = mf_hash_find(&ht, key);', getline('.'))
- cnext
- call assert_equal(' item = mf_hash_find(&ht, key);', getline('.'))
- cnext
- call assert_equal(' item = mf_hash_find(&ht, key);', getline('.'))
- endfor
- endif
- endif
-
- " Test: leading whitespace is not removed for cscope find text
- let a = execute('cscope find t test_mf_hash')
- call assert_equal(['', '(1 of 1): <<<unknown>>> test_mf_hash();'], split(a, '\n', 1))
- call assert_equal(' test_mf_hash();', getline('.'))
-
- " Test: test with scscope
- let a = execute('scs find t Bram')
- call assert_match('(1 of 1): <<<unknown>>> \* VIM - Vi IMproved^Iby Bram Moolenaar', a)
- call assert_equal(' * VIM - Vi IMproved by Bram Moolenaar', getline('.'))
-
- " Test: cscope help
- for cmd in ['cs', 'cs help', 'cs xxx']
- let a = execute(cmd)
- call assert_match('^cscope commands:\n', a)
- call assert_match('\nadd :', a)
- call assert_match('\nfind :', a)
- call assert_match('\nhelp : Show this message', a)
- call assert_match('\nkill : Kill a connection', a)
- call assert_match('\nreset: Reinit all connections', a)
- call assert_match('\nshow : Show connections', a)
- endfor
- let a = execute('scscope help')
- call assert_match('This cscope command does not support splitting the window\.', a)
-
- " Test: reset connections
- let a = execute('cscope reset')
- call assert_match('\nAdded cscope database.*Xcscope.out (#0)', a)
- call assert_match('\nAll cscope databases reset', a)
-
- " Test: cscope show
- let a = execute('cscope show')
- call assert_match('\n 0 \d\+.*Xcscope.out\s*<none>', a)
-
- " Test: cstag and 'csto' option
- set csto=0
- let a = execute('cstag TEST_COUNT')
- call assert_match('(1 of 1): <<TEST_COUNT>> #define TEST_COUNT 50000', a)
- call assert_equal('#define TEST_COUNT 50000', getline('.'))
- call assert_fails('cstag DOES_NOT_EXIST', 'E257:')
- set csto=1
- let a = execute('cstag index_to_key')
- call assert_match('(1 of 1): <<index_to_key>> #define index_to_key(i) ((i) ^ 15167)', a)
- call assert_equal('#define index_to_key(i) ((i) ^ 15167)', getline('.'))
- call assert_fails('cstag DOES_NOT_EXIST', 'E257:')
- call assert_fails('cstag', 'E562:')
- let save_tags = &tags
- set tags=
- call assert_fails('cstag DOES_NOT_EXIST', 'E257:')
- let a = execute('cstag index_to_key')
- call assert_match('(1 of 1): <<index_to_key>> #define index_to_key(i) ((i) ^ 15167)', a)
- let &tags = save_tags
-
- " Test: 'cst' option
- set nocst
- call assert_fails('tag TEST_COUNT', 'E426:')
- set cst
- let a = execute('tag TEST_COUNT')
- call assert_match('(1 of 1): <<TEST_COUNT>> #define TEST_COUNT 50000', a)
- call assert_equal('#define TEST_COUNT 50000', getline('.'))
- let a = execute('tags')
- call assert_match('1 1 TEST_COUNT\s\+\d\+\s\+#define index_to_key', a)
-
- " Test: 'cscoperelative'
- call mkdir('Xcscoperelative')
- cd Xcscoperelative
- let a = execute('cs find g test_mf_hash')
- call assert_notequal('test_mf_hash(void)', getline('.'))
- set cscoperelative
- let a = execute('cs find g test_mf_hash')
- call assert_equal('test_mf_hash(void)', getline('.'))
- set nocscoperelative
- cd ..
- call delete('Xcscoperelative', 'd')
-
- " Test: E259: no match found
- call assert_fails('cscope find g DOES_NOT_EXIST', 'E259:')
-
- " Test: this should trigger call to cs_print_tags()
- " Unclear how to check result though, we just exercise the code.
- set cst cscopequickfix=s0
- call feedkeys(":cs find s main\<CR>", 't')
-
- " Test: cscope kill
- call assert_fails('cscope kill', 'E560:')
- call assert_fails('cscope kill 2', 'E261:')
- call assert_fails('cscope kill xxx', 'E261:')
-
- let a = execute('cscope kill 0')
- call assert_match('cscope connection 0 closed', a)
-
- cscope add Xcscope.out
- let a = execute('cscope kill Xcscope.out')
- call assert_match('cscope connection Xcscope.out closed', a)
-
- cscope add Xcscope.out .
- let a = execute('cscope kill -1')
- call assert_match('cscope connection .*Xcscope.out closed', a)
- let a = execute('cscope kill -1')
- call assert_equal('', a)
-
- " Test: 'csprg' option
- call assert_equal('cscope', &csprg)
- set csprg=doesnotexist
- call assert_fails('cscope add Xcscope2.out', 'E609:')
- set csprg=cscope
-
- " Test: multiple cscope connections
- cscope add Xcscope.out
- cscope add Xcscope2.out . -C
- let a = execute('cscope show')
- call assert_match('\n 0 \d\+.*Xcscope.out\s*<none>', a)
- call assert_match('\n 1 \d\+.*Xcscope2.out\s*\.', a)
-
- " Test: test Ex command line completion
- call feedkeys(":cs \<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_equal('"cs add find help kill reset show', @:)
-
- call feedkeys(":scs \<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_equal('"scs find', @:)
-
- call feedkeys(":cs find \<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_equal('"cs find a c d e f g i s t', @:)
-
- call feedkeys(":cs kill \<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_equal('"cs kill -1 0 1', @:)
-
- call feedkeys(":cs add Xcscope\<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_equal('"cs add Xcscope.out Xcscope2.out', @:)
-
- " Test: cscope_connection()
- call assert_equal(cscope_connection(), 1)
- call assert_equal(cscope_connection(0, 'out'), 1)
- call assert_equal(cscope_connection(0, 'xxx'), 1)
-
- call assert_equal(cscope_connection(1, 'out'), 1)
- call assert_equal(cscope_connection(1, 'xxx'), 0)
-
- call assert_equal(cscope_connection(2, 'out'), 0)
- call assert_equal(cscope_connection(2, getcwd() .. '/Xcscope.out', 1), 1)
-
- call assert_equal(cscope_connection(3, 'xxx', '..'), 0)
- call assert_equal(cscope_connection(3, 'out', 'xxx'), 0)
- call assert_equal(cscope_connection(3, 'out', '.'), 1)
-
- call assert_equal(cscope_connection(4, 'out', '.'), 0)
-
- call assert_equal(cscope_connection(5, 'out'), 0)
- call assert_equal(cscope_connection(-1, 'out'), 0)
-
- call CscopeSetupOrClean(0)
-endfunc
-
-" Test ":cs add {dir}" (add the {dir}/cscope.out database)
-func Test_cscope_add_dir()
- call mkdir('Xcscopedir', 'p')
-
- " Cscope doesn't handle symlinks, so this needs to be resolved in case a
- " shadow directory is being used.
- let memfile = resolve('./samples/memfile_test.c')
- call system('cscope -bk -fXcscopedir/cscope.out ' . memfile)
-
- cs add Xcscopedir
- let a = execute('cscope show')
- let lines = split(a, "\n", 1)
- call assert_equal(3, len(lines))
- call assert_equal(' # pid database name prepend path', lines[0])
- call assert_equal('', lines[1])
- call assert_match('^ 0 \d\+.*Xcscopedir/cscope.out\s\+<none>$', lines[2])
-
- cs kill -1
- call delete('Xcscopedir/cscope.out')
- call assert_fails('cs add Xcscopedir', 'E563:')
-
- call delete('Xcscopedir', 'd')
-endfunc
-
-func Test_cscopequickfix()
- set cscopequickfix=s-,g-,d+,c-,t+,e-,f0,i-,a-
- call assert_equal('s-,g-,d+,c-,t+,e-,f0,i-,a-', &cscopequickfix)
-
- call assert_fails('set cscopequickfix=x-', 'E474:')
- call assert_fails('set cscopequickfix=s', 'E474:')
- call assert_fails('set cscopequickfix=s7', 'E474:')
- call assert_fails('set cscopequickfix=s-a', 'E474:')
-endfunc
-
-func Test_withoutCscopeConnection()
- call assert_equal(cscope_connection(), 0)
-
- call assert_fails('cscope find s main', 'E567:')
- let a = execute('cscope show')
- call assert_match('no cscope connections', a)
-endfunc
-
-
-" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_cursor_func.vim b/src/nvim/testdir/test_cursor_func.vim
index 3b8a5f27ad..bb8e7cd5c5 100644
--- a/src/nvim/testdir/test_cursor_func.vim
+++ b/src/nvim/testdir/test_cursor_func.vim
@@ -1,7 +1,10 @@
" Tests for cursor() and other functions that get/set the cursor position
+source check.vim
+
func Test_wrong_arguments()
call assert_fails('call cursor(1. 3)', 'E474:')
+ call assert_fails('call cursor(v:_null_list)', 'E474:')
endfunc
func Test_move_cursor()
@@ -22,7 +25,7 @@ func Test_move_cursor()
call cursor(3, 0)
call assert_equal([3, 1, 0, 1], getcurpos()[1:])
" below last line goes to last line
- call cursor(9, 1)
+ eval [9, 1]->cursor()
call assert_equal([4, 1, 0, 1], getcurpos()[1:])
" pass string arguments
call cursor('3', '3')
@@ -32,7 +35,7 @@ func Test_move_cursor()
call cursor(1, 1, 1)
call assert_equal([1, 1, 1], getcurpos()[1:3])
- call assert_equal(-1, cursor(-1, -1))
+ call assert_fails('call cursor(-1, -1)', 'E475:')
quit!
endfunc
@@ -82,34 +85,85 @@ func Test_screenpos()
let winid = win_getid()
let [winrow, wincol] = win_screenpos(winid)
call assert_equal({'row': winrow,
- \ 'col': wincol + 0,
- \ 'curscol': wincol + 7,
- \ 'endcol': wincol + 7}, winid->screenpos(1, 1))
+ \ 'col': wincol + 0,
+ \ 'curscol': wincol + 7,
+ \ 'endcol': wincol + 7}, winid->screenpos(1, 1))
call assert_equal({'row': winrow,
- \ 'col': wincol + 13,
- \ 'curscol': wincol + 13,
- \ 'endcol': wincol + 13}, winid->screenpos(1, 7))
+ \ 'col': wincol + 13,
+ \ 'curscol': wincol + 13,
+ \ 'endcol': wincol + 13}, winid->screenpos(1, 7))
call assert_equal({'row': winrow + 2,
- \ 'col': wincol + 1,
- \ 'curscol': wincol + 1,
- \ 'endcol': wincol + 1}, screenpos(winid, 2, 22))
+ \ 'col': wincol + 1,
+ \ 'curscol': wincol + 1,
+ \ 'endcol': wincol + 1}, screenpos(winid, 2, 22))
setlocal number
call assert_equal({'row': winrow + 3,
- \ 'col': wincol + 9,
- \ 'curscol': wincol + 9,
- \ 'endcol': wincol + 9}, screenpos(winid, 2, 22))
+ \ 'col': wincol + 9,
+ \ 'curscol': wincol + 9,
+ \ 'endcol': wincol + 9}, screenpos(winid, 2, 22))
+
+ let wininfo = getwininfo(winid)[0]
+ call setline(3, ['x']->repeat(wininfo.height))
+ call setline(line('$') + 1, 'x'->repeat(wininfo.width * 3))
+ setlocal nonumber display=lastline so=0
+ exe "normal G\<C-Y>\<C-Y>"
+ redraw
+ call assert_equal({'row': winrow + wininfo.height - 1,
+ \ 'col': wincol + 7,
+ \ 'curscol': wincol + 7,
+ \ 'endcol': wincol + 7}, winid->screenpos(line('$'), 8))
+ call assert_equal({'row': 0, 'col': 0, 'curscol': 0, 'endcol': 0},
+ \ winid->screenpos(line('$'), 22))
+
close
call assert_equal({}, screenpos(999, 1, 1))
+
bwipe!
+ set display&
- call assert_equal({'col': 1, 'row': 1, 'endcol': 1, 'curscol': 1}, screenpos(win_getid(), 1, 1))
+ call assert_equal(#{col: 1, row: 1, endcol: 1, curscol: 1}, screenpos(win_getid(), 1, 1))
" nmenu WinBar.TEST :
setlocal winbar=TEST
- call assert_equal({'col': 1, 'row': 2, 'endcol': 1, 'curscol': 1}, screenpos(win_getid(), 1, 1))
+ call assert_equal(#{col: 1, row: 2, endcol: 1, curscol: 1}, screenpos(win_getid(), 1, 1))
" nunmenu WinBar.TEST
setlocal winbar&
endfunc
+func Test_screenpos_fold()
+ CheckFeature folding
+
+ enew!
+ call setline(1, range(10))
+ 3,5fold
+ redraw
+ call assert_equal(2, screenpos(1, 2, 1).row)
+ call assert_equal(#{col: 1, row: 3, endcol: 1, curscol: 1}, screenpos(1, 3, 1))
+ call assert_equal(#{col: 1, row: 3, endcol: 1, curscol: 1}, screenpos(1, 4, 1))
+ call assert_equal(#{col: 1, row: 3, endcol: 1, curscol: 1}, screenpos(1, 5, 1))
+ setlocal number
+ call assert_equal(#{col: 5, row: 3, endcol: 5, curscol: 5}, screenpos(1, 3, 1))
+ call assert_equal(#{col: 5, row: 3, endcol: 5, curscol: 5}, screenpos(1, 4, 1))
+ call assert_equal(#{col: 5, row: 3, endcol: 5, curscol: 5}, screenpos(1, 5, 1))
+ call assert_equal(4, screenpos(1, 6, 1).row)
+ bwipe!
+endfunc
+
+func Test_screenpos_diff()
+ CheckFeature diff
+
+ enew!
+ call setline(1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'])
+ vnew
+ call setline(1, ['a', 'b', 'c', 'g', 'h', 'i'])
+ windo diffthis
+ wincmd w
+ call assert_equal(#{col: 3, row: 7, endcol: 3, curscol: 3}, screenpos(0, 4, 1))
+
+ windo diffoff
+ bwipe!
+ bwipe!
+endfunc
+
func Test_screenpos_number()
rightbelow new
rightbelow 73vsplit
@@ -121,6 +175,9 @@ func Test_screenpos_number()
let pos = screenpos(winid, 1, 66)
call assert_equal(winrow, pos.row)
call assert_equal(wincol + 66 + 3, pos.col)
+
+ call assert_fails('echo screenpos(0, 2, 1)', 'E966:')
+
close
bwipe!
endfunc
@@ -241,8 +298,9 @@ endfunc
" Test for the charcol() function
func Test_charcol()
- call assert_fails('call charcol({})', 'E731:')
- call assert_equal(0, charcol(0))
+ call assert_fails('call charcol({})', 'E1222:')
+ call assert_fails('call charcol(".", [])', 'E1210:')
+ call assert_fails('call charcol(0)', 'E1222:')
new
call setline(1, ['', "01\tà4è678", 'Ⅵ', '012345678'])
@@ -298,6 +356,25 @@ func Test_charcol()
call assert_equal([1, 10, 2, 10, 7], g:InsertCurrentCol)
iunmap <F3>
+ " Test for getting the column number in another window.
+ let winid = win_getid()
+ new
+ call win_execute(winid, 'normal 1G')
+ call assert_equal(1, charcol('.', winid))
+ call assert_equal(1, charcol('$', winid))
+ call win_execute(winid, 'normal 2G6l')
+ call assert_equal(7, charcol('.', winid))
+ call assert_equal(10, charcol('$', winid))
+
+ " calling from another tab page also works
+ tabnew
+ call assert_equal(7, charcol('.', winid))
+ call assert_equal(10, charcol('$', winid))
+ tabclose
+
+ " unknown window ID
+ call assert_equal(0, charcol('.', 10001))
+
%bw!
endfunc
@@ -353,8 +430,14 @@ func Test_setcursorcharpos()
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([0, 7, 0])
+ call assert_equal([2, 9], [line('.'), col('.')])
+ call setcursorcharpos(0, 7, 0)
+ call assert_equal([2, 9], [line('.'), col('.')])
+
call setcursorcharpos(3, 4)
call assert_equal([3, 1], [line('.'), col('.')])
call setcursorcharpos([3, 1])
@@ -373,4 +456,26 @@ func Test_setcursorcharpos()
%bw!
endfunc
+" Test for virtcol2col()
+func Test_virtcol2col()
+ new
+ call setline(1, ["a\tb\tc"])
+ call assert_equal(1, virtcol2col(0, 1, 1))
+ call assert_equal(2, virtcol2col(0, 1, 2))
+ call assert_equal(2, virtcol2col(0, 1, 8))
+ call assert_equal(3, virtcol2col(0, 1, 9))
+ call assert_equal(4, virtcol2col(0, 1, 10))
+ call assert_equal(4, virtcol2col(0, 1, 16))
+ call assert_equal(5, virtcol2col(0, 1, 17))
+ call assert_equal(-1, virtcol2col(10, 1, 1))
+ call assert_equal(-1, virtcol2col(0, 10, 1))
+ call assert_equal(-1, virtcol2col(0, -1, 1))
+ call assert_equal(-1, virtcol2col(0, 1, -1))
+ call assert_equal(5, virtcol2col(0, 1, 20))
+ call assert_fails('echo virtcol2col("0", 1, 20)', 'E1210:')
+ call assert_fails('echo virtcol2col(0, "1", 20)', 'E1210:')
+ call assert_fails('echo virtcol2col(0, 1, "1")', 'E1210:')
+ 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 e85e9304a3..70f39a8601 100644
--- a/src/nvim/testdir/test_cursorline.vim
+++ b/src/nvim/testdir/test_cursorline.vim
@@ -3,26 +3,26 @@
source check.vim
source screendump.vim
-function! s:screen_attr(lnum) abort
+func s:screen_attr(lnum) abort
return map(range(1, 8), 'screenattr(a:lnum, v:val)')
-endfunction
+endfunc
-function! s:test_windows(h, w) abort
+func s:test_windows(h, w) abort
call NewWindow(a:h, a:w)
-endfunction
+endfunc
-function! s:close_windows() abort
+func s:close_windows() abort
call CloseWindow()
-endfunction
+endfunc
-function! s:new_hi() abort
+func s:new_hi() abort
redir => save_hi
silent! hi CursorLineNr
redir END
let save_hi = join(split(substitute(save_hi, '\s*xxx\s*', ' ', ''), "\n"), '')
exe 'hi' save_hi 'ctermbg=0 guibg=Black'
return save_hi
-endfunction
+endfunc
func Test_cursorline_highlight1()
let save_hi = s:new_hi()
@@ -319,7 +319,7 @@ func Test_cursorline_cursorbind_horizontal_scroll()
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')
+ \ ' 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
diff --git a/src/nvim/testdir/test_debugger.vim b/src/nvim/testdir/test_debugger.vim
index e038c0096a..f5177c8fb2 100644
--- a/src/nvim/testdir/test_debugger.vim
+++ b/src/nvim/testdir/test_debugger.vim
@@ -15,14 +15,18 @@ func CheckCWD()
endfunc
command! -nargs=0 -bar CheckCWD call CheckCWD()
+" "options" argument can contain:
+" 'msec' - time to wait for a match
+" 'match' - "pattern" to use "lines" as pattern instead of text
func CheckDbgOutput(buf, lines, options = {})
" Verify the expected output
let lnum = 20 - len(a:lines)
+ let msec = get(a:options, 'msec', 1000)
for l in a:lines
if get(a:options, 'match', 'equal') ==# 'pattern'
- call WaitForAssert({-> assert_match(l, term_getline(a:buf, lnum))}, 200)
+ call WaitForAssert({-> assert_match(l, term_getline(a:buf, lnum))}, msec)
else
- call WaitForAssert({-> assert_equal(l, term_getline(a:buf, lnum))}, 200)
+ call WaitForAssert({-> assert_equal(l, term_getline(a:buf, lnum))}, msec)
endif
let lnum += 1
endfor
@@ -32,7 +36,7 @@ endfunc
" If the expected output argument is supplied, then check for it.
func RunDbgCmd(buf, cmd, ...)
call term_sendkeys(a:buf, a:cmd . "\r")
- call term_wait(a:buf)
+ call term_wait(a:buf, 20)
if a:0 != 0
let options = #{match: 'equal'}
@@ -198,7 +202,7 @@ func Test_Debugger()
" Start a debug session, so that reading the last line from the terminal
" works properly.
- call RunDbgCmd(buf, ':debug echo Foo()')
+ call RunDbgCmd(buf, ':debug echo Foo()', ['cmd: echo Foo()'])
" No breakpoints
call RunDbgCmd(buf, 'breakl', ['No breakpoints defined'])
@@ -814,9 +818,10 @@ func Test_Backtrace_CmdLine()
\ '-S Xtest1.vim -c "debug call GlobalFunction()"',
\ {'wait_for_ruler': 0})
- " Need to wait for the vim-in-terminal to be ready
+ " Need to wait for the vim-in-terminal to be ready.
+ " With valgrind this can take quite long.
call CheckDbgOutput(buf, ['command line',
- \ 'cmd: call GlobalFunction()'])
+ \ 'cmd: call GlobalFunction()'], #{msec: 5000})
" At this point the ontly thing in the stack is the cmdline
call RunDbgCmd(buf, 'backtrace', [
@@ -967,14 +972,14 @@ func Test_debug_backtrace_level()
" set a breakpoint and source file1.vim
let buf = RunVimInTerminal(
\ '-c "breakadd file 1 Xtest1.vim" -S Xtest1.vim',
- \ #{ wait_for_ruler: 0 } )
+ \ #{wait_for_ruler: 0})
call CheckDbgOutput(buf, [
\ 'Breakpoint in "' .. file1 .. '" line 1',
\ 'Entering Debug mode. Type "cont" to continue.',
\ 'command line..script ' .. file1,
\ 'line 1: let s:file1_var = ''file1'''
- \ ])
+ \ ], #{msec: 5000})
" step through the initial declarations
call RunDbgCmd(buf, 'step', [ 'line 2: let g:global_var = ''global''' ] )
@@ -1140,7 +1145,6 @@ func Test_breakpt_endif_intr()
let caught_intr = 0
debuggreedy
call feedkeys(":call F()\<CR>quit\<CR>", "xt")
- call F()
catch /^Vim:Interrupt$/
call assert_match('\.F, line 4', v:throwpoint)
let caught_intr = 1
@@ -1171,7 +1175,6 @@ func Test_breakpt_else_intr()
let caught_intr = 0
debuggreedy
call feedkeys(":call F()\<CR>quit\<CR>", "xt")
- call F()
catch /^Vim:Interrupt$/
call assert_match('\.F, line 4', v:throwpoint)
let caught_intr = 1
@@ -1200,7 +1203,6 @@ func Test_breakpt_endwhile_intr()
let caught_intr = 0
debuggreedy
call feedkeys(":call F()\<CR>quit\<CR>", "xt")
- call F()
catch /^Vim:Interrupt$/
call assert_match('\.F, line 4', v:throwpoint)
let caught_intr = 1
@@ -1212,38 +1214,24 @@ func Test_breakpt_endwhile_intr()
delfunc F
endfunc
-" Test for setting a breakpoint on an :endtry where an exception is pending to
-" be processed and then quit the script. This should generate an interrupt and
-" the thrown exception should be ignored.
-func Test_breakpt_endtry_intr()
- func F()
- try
- let g:Xpath ..= 'a'
- throw "abc"
- endtry
- invalid_command
+" Test for setting a breakpoint on a script local function
+func Test_breakpt_scriptlocal_func()
+ let g:Xpath = ''
+ func s:G()
+ let g:Xpath ..= 'a'
endfunc
- let g:Xpath = ''
- breakadd func 4 F
- try
- let caught_intr = 0
- let caught_abc = 0
- debuggreedy
- call feedkeys(":call F()\<CR>quit\<CR>", "xt")
- call F()
- catch /abc/
- let caught_abc = 1
- catch /^Vim:Interrupt$/
- call assert_match('\.F, line 4', v:throwpoint)
- let caught_intr = 1
- endtry
+ let funcname = expand("<SID>") .. "G"
+ exe "breakadd func 1 " .. funcname
+ debuggreedy
+ redir => output
+ call feedkeys(":call " .. funcname .. "()\<CR>c\<CR>", "xt")
+ redir END
0debuggreedy
- call assert_equal(1, caught_intr)
- call assert_equal(0, caught_abc)
+ call assert_match('Breakpoint in "' .. funcname .. '" line 1', output)
call assert_equal('a', g:Xpath)
breakdel *
- delfunc F
+ exe "delfunc " .. funcname
endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_delete.vim b/src/nvim/testdir/test_delete.vim
index b23a3bd025..6b49f153c6 100644
--- a/src/nvim/testdir/test_delete.vim
+++ b/src/nvim/testdir/test_delete.vim
@@ -1,5 +1,7 @@
" Test for delete().
+source check.vim
+
func Test_file_delete()
split Xfile
call setline(1, ['a', 'b'])
@@ -41,9 +43,7 @@ func Test_recursive_delete()
endfunc
func Test_symlink_delete()
- if !has('unix')
- return
- endif
+ CheckUnix
split Xfile
call setline(1, ['a', 'b'])
wq
@@ -56,9 +56,7 @@ func Test_symlink_delete()
endfunc
func Test_symlink_dir_delete()
- if !has('unix')
- return
- endif
+ CheckUnix
call mkdir('Xdir1')
silent !ln -s Xdir1 Xlink
call assert_true(isdirectory('Xdir1'))
@@ -70,9 +68,7 @@ func Test_symlink_dir_delete()
endfunc
func Test_symlink_recursive_delete()
- if !has('unix')
- return
- endif
+ CheckUnix
call mkdir('Xdir3')
call mkdir('Xdir3/subdir')
call mkdir('Xdir4')
diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim
index ea453b7174..0049398776 100644
--- a/src/nvim/testdir/test_diffmode.vim
+++ b/src/nvim/testdir/test_diffmode.vim
@@ -1,4 +1,5 @@
" Tests for diff mode
+
source shared.vim
source screendump.vim
source check.vim
@@ -243,6 +244,36 @@ func Test_diffput_two()
bwipe! b
endfunc
+" Test for :diffget/:diffput with a range that is inside a diff chunk
+func Test_diffget_diffput_range()
+ call setline(1, range(1, 10))
+ new
+ call setline(1, range(11, 20))
+ windo diffthis
+ 3,5diffget
+ call assert_equal(['13', '14', '15'], getline(3, 5))
+ call setline(1, range(1, 10))
+ 4,8diffput
+ wincmd p
+ call assert_equal(['13', '4', '5', '6', '7', '8', '19'], getline(3, 9))
+ %bw!
+endfunc
+
+" Test for :diffget/:diffput with an empty buffer and a non-empty buffer
+func Test_diffget_diffput_empty_buffer()
+ %d _
+ new
+ call setline(1, 'one')
+ windo diffthis
+ diffget
+ call assert_equal(['one'], getline(1, '$'))
+ %d _
+ diffput
+ wincmd p
+ call assert_equal([''], getline(1, '$'))
+ %bw!
+endfunc
+
" :diffput and :diffget completes names of buffers which
" are in diff mode and which are different then current buffer.
" No completion when the current window is not in diff mode.
@@ -621,9 +652,7 @@ func Test_diff_move_to()
endfunc
func Test_diffexpr()
- if !executable('diff')
- return
- endif
+ CheckExecutable diff
func DiffExpr()
" Prepend some text to check diff type detection
@@ -647,17 +676,31 @@ func Test_diffexpr()
call assert_equal(normattr, screenattr(1, 1))
call assert_equal(normattr, screenattr(2, 1))
call assert_notequal(normattr, screenattr(3, 1))
+ diffoff!
+ " Try using an non-existing function for 'diffexpr'.
+ set diffexpr=NewDiffFunc()
+ call assert_fails('windo diffthis', ['E117:', 'E97:'])
diffoff!
+
+ " Using a script-local function
+ func s:NewDiffExpr()
+ endfunc
+ set diffexpr=s:NewDiffExpr()
+ call assert_equal(expand('<SID>') .. 'NewDiffExpr()', &diffexpr)
+ set diffexpr=<SID>NewDiffExpr()
+ call assert_equal(expand('<SID>') .. 'NewDiffExpr()', &diffexpr)
+
%bwipe!
set diffexpr& diffopt&
+ delfunc DiffExpr
+ delfunc s:NewDiffExpr
endfunc
func Test_diffpatch()
" The patch program on MS-Windows may fail or hang.
- if !executable('patch') || !has('unix')
- return
- endif
+ CheckExecutable patch
+ CheckUnix
new
insert
***************
@@ -744,17 +787,13 @@ func Test_diff_hlID()
call diff_hlID(-1, 1)->synIDattr("name")->assert_equal("")
- call assert_equal(diff_hlID(1, 1), hlID("DiffChange"))
call diff_hlID(1, 1)->synIDattr("name")->assert_equal("DiffChange")
- call assert_equal(diff_hlID(1, 2), hlID("DiffText"))
call diff_hlID(1, 2)->synIDattr("name")->assert_equal("DiffText")
call diff_hlID(2, 1)->synIDattr("name")->assert_equal("")
- call assert_equal(diff_hlID(3, 1), hlID("DiffAdd"))
call diff_hlID(3, 1)->synIDattr("name")->assert_equal("DiffAdd")
- call diff_hlID(4, 1)->synIDattr("name")->assert_equal("")
+ eval 4->diff_hlID(1)->synIDattr("name")->assert_equal("")
wincmd w
- call assert_equal(diff_hlID(1, 1), hlID("DiffChange"))
call assert_equal(synIDattr(diff_hlID(1, 1), "name"), "DiffChange")
call assert_equal(synIDattr(diff_hlID(2, 1), "name"), "")
call assert_equal(synIDattr(diff_hlID(3, 1), "name"), "")
@@ -1202,6 +1241,42 @@ func Test_diff_maintains_change_mark()
delfunc DiffMaintainsChangeMark
endfunc
+" Test for 'patchexpr'
+func Test_patchexpr()
+ let g:patch_args = []
+ func TPatch()
+ call add(g:patch_args, readfile(v:fname_in))
+ call add(g:patch_args, readfile(v:fname_diff))
+ call writefile(['output file'], v:fname_out)
+ endfunc
+ set patchexpr=TPatch()
+
+ call writefile(['input file'], 'Xinput')
+ call writefile(['diff file'], 'Xdiff')
+ %bwipe!
+ edit Xinput
+ diffpatch Xdiff
+ call assert_equal('output file', getline(1))
+ call assert_equal('Xinput.new', bufname())
+ call assert_equal(2, winnr('$'))
+ call assert_true(&diff)
+
+ " Using a script-local function
+ func s:NewPatchExpr()
+ endfunc
+ set patchexpr=s:NewPatchExpr()
+ call assert_equal(expand('<SID>') .. 'NewPatchExpr()', &patchexpr)
+ set patchexpr=<SID>NewPatchExpr()
+ call assert_equal(expand('<SID>') .. 'NewPatchExpr()', &patchexpr)
+
+ call delete('Xinput')
+ call delete('Xdiff')
+ set patchexpr&
+ delfunc TPatch
+ delfunc s:NewPatchExpr
+ %bwipe!
+endfunc
+
func Test_diff_rnu()
CheckScreendump
@@ -1295,6 +1370,89 @@ func Test_diff_filler_cursorcolumn()
call delete('Xtest_diff_cuc')
endfunc
+" Test for adding/removing lines inside diff chunks, between diff chunks
+" and before diff chunks
+func Test_diff_modify_chunks()
+ enew!
+ let w2_id = win_getid()
+ call setline(1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'])
+ new
+ let w1_id = win_getid()
+ call setline(1, ['a', '2', '3', 'd', 'e', 'f', '7', '8', 'i'])
+ windo diffthis
+
+ " remove a line between two diff chunks and create a new diff chunk
+ call win_gotoid(w2_id)
+ 5d
+ call win_gotoid(w1_id)
+ call diff_hlID(5, 1)->synIDattr('name')->assert_equal('DiffAdd')
+
+ " add a line between two diff chunks
+ call win_gotoid(w2_id)
+ normal! 4Goe
+ call win_gotoid(w1_id)
+ call diff_hlID(4, 1)->synIDattr('name')->assert_equal('')
+ call diff_hlID(5, 1)->synIDattr('name')->assert_equal('')
+
+ " remove all the lines in a diff chunk.
+ call win_gotoid(w2_id)
+ 7,8d
+ call win_gotoid(w1_id)
+ let hl = range(1, 9)->map({_, lnum -> diff_hlID(lnum, 1)->synIDattr('name')})
+ call assert_equal(['', 'DiffText', 'DiffText', '', '', '', 'DiffAdd',
+ \ 'DiffAdd', ''], hl)
+
+ " remove lines from one diff chunk to just before the next diff chunk
+ call win_gotoid(w2_id)
+ call setline(1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'])
+ 2,6d
+ call win_gotoid(w1_id)
+ let hl = range(1, 9)->map({_, lnum -> diff_hlID(lnum, 1)->synIDattr('name')})
+ call assert_equal(['', 'DiffText', 'DiffText', 'DiffAdd', 'DiffAdd',
+ \ 'DiffAdd', 'DiffAdd', 'DiffAdd', ''], hl)
+
+ " remove lines just before the top of a diff chunk
+ call win_gotoid(w2_id)
+ call setline(1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'])
+ 5,6d
+ call win_gotoid(w1_id)
+ let hl = range(1, 9)->map({_, lnum -> diff_hlID(lnum, 1)->synIDattr('name')})
+ call assert_equal(['', 'DiffText', 'DiffText', '', 'DiffText', 'DiffText',
+ \ 'DiffAdd', 'DiffAdd', ''], hl)
+
+ " remove line after the end of a diff chunk
+ call win_gotoid(w2_id)
+ call setline(1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'])
+ 4d
+ call win_gotoid(w1_id)
+ let hl = range(1, 9)->map({_, lnum -> diff_hlID(lnum, 1)->synIDattr('name')})
+ call assert_equal(['', 'DiffText', 'DiffText', 'DiffAdd', '', '', 'DiffText',
+ \ 'DiffText', ''], hl)
+
+ " remove lines starting from the end of one diff chunk and ending inside
+ " another diff chunk
+ call win_gotoid(w2_id)
+ call setline(1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'])
+ 4,7d
+ call win_gotoid(w1_id)
+ let hl = range(1, 9)->map({_, lnum -> diff_hlID(lnum, 1)->synIDattr('name')})
+ call assert_equal(['', 'DiffText', 'DiffText', 'DiffText', 'DiffAdd',
+ \ 'DiffAdd', 'DiffAdd', 'DiffAdd', ''], hl)
+
+ " removing the only remaining diff chunk should make the files equal
+ call win_gotoid(w2_id)
+ call setline(1, ['a', '2', '3', 'x', 'd', 'e', 'f', 'x', '7', '8', 'i'])
+ 8d
+ let hl = range(1, 10)->map({_, lnum -> diff_hlID(lnum, 1)->synIDattr('name')})
+ call assert_equal(['', '', '', 'DiffAdd', '', '', '', '', '', ''], hl)
+ call win_gotoid(w2_id)
+ 4d
+ call win_gotoid(w1_id)
+ let hl = range(1, 9)->map({_, lnum -> diff_hlID(lnum, 1)->synIDattr('name')})
+ call assert_equal(['', '', '', '', '', '', '', '', ''], hl)
+
+ %bw!
+endfunc
func Test_diff_binary()
CheckScreendump
diff --git a/src/nvim/testdir/test_digraph.vim b/src/nvim/testdir/test_digraph.vim
index acc34e5e7c..f08dff8605 100644
--- a/src/nvim/testdir/test_digraph.vim
+++ b/src/nvim/testdir/test_digraph.vim
@@ -211,6 +211,8 @@ func Test_digraphs()
call Put_Dig("00")
call Put_Dig("el")
call assert_equal(['␀', 'ü', '∞', 'l'], getline(line('.')-3,line('.')))
+ call assert_fails('exe "digraph a\<Esc> 100"', 'E104:')
+ call assert_fails('exe "digraph \<Esc>a 100"', 'E104:')
call assert_fails('digraph xy z', 'E39:')
call assert_fails('digraph x', 'E1214:')
bw!
@@ -491,6 +493,17 @@ func Test_show_digraph_cp1251()
bwipe!
endfunc
+" Test for error in a keymap file
+func Test_loadkeymap_error()
+ if !has('keymap')
+ return
+ endif
+ call assert_fails('loadkeymap', 'E105:')
+ call writefile(['loadkeymap', 'a'], 'Xkeymap')
+ call assert_fails('source Xkeymap', 'E791:')
+ call delete('Xkeymap')
+endfunc
+
" Test for the characters displayed on the screen when entering a digraph
func Test_entering_digraph()
CheckRunVimInTerminal
diff --git a/src/nvim/testdir/test_display.vim b/src/nvim/testdir/test_display.vim
index 6938abbc28..b642f39c9f 100644
--- a/src/nvim/testdir/test_display.vim
+++ b/src/nvim/testdir/test_display.vim
@@ -195,8 +195,6 @@ func Test_edit_long_file_name()
call VerifyScreenDump(buf, 'Test_long_file_name_1', {})
- call term_sendkeys(buf, ":q\<cr>")
-
" clean up
call StopVimInTerminal(buf)
call delete(longName)
@@ -228,7 +226,6 @@ endfunc
" Test for scrolling that modifies buffer during visual block
func Test_visual_block_scroll()
- " See test/functional/legacy/visual_mode_spec.lua
CheckScreendump
let lines =<< trim END
@@ -239,7 +236,7 @@ func Test_visual_block_scroll()
END
let filename = 'Xvisualblockmodifiedscroll'
- call writefile(lines, filename)
+ call writefile(lines, filename, 'D')
let buf = RunVimInTerminal('-S '.filename, #{rows: 7})
call term_sendkeys(buf, "V\<C-D>\<C-D>")
@@ -247,11 +244,40 @@ func Test_visual_block_scroll()
call VerifyScreenDump(buf, 'Test_display_visual_block_scroll', {})
call StopVimInTerminal(buf)
- call delete(filename)
+endfunc
+
+" Test for clearing paren highlight when switching buffers
+func Test_matchparen_clear_highlight()
+ CheckScreendump
+
+ let lines =<< trim END
+ source $VIMRUNTIME/plugin/matchparen.vim
+ set hidden
+ call setline(1, ['()'])
+ normal 0
+
+ func OtherBuffer()
+ enew
+ exe "normal iaa\<Esc>0"
+ endfunc
+ END
+ call writefile(lines, 'XMatchparenClear', 'D')
+ let buf = RunVimInTerminal('-S XMatchparenClear', #{rows: 5})
+ call VerifyScreenDump(buf, 'Test_matchparen_clear_highlight_1', {})
+
+ call term_sendkeys(buf, ":call OtherBuffer()\<CR>:\<Esc>")
+ call VerifyScreenDump(buf, 'Test_matchparen_clear_highlight_2', {})
+
+ call term_sendkeys(buf, "\<C-^>:\<Esc>")
+ call VerifyScreenDump(buf, 'Test_matchparen_clear_highlight_1', {})
+
+ call term_sendkeys(buf, "\<C-^>:\<Esc>")
+ call VerifyScreenDump(buf, 'Test_matchparen_clear_highlight_2', {})
+
+ call StopVimInTerminal(buf)
endfunc
func Test_display_scroll_at_topline()
- " See test/functional/legacy/display_spec.lua
CheckScreendump
let buf = RunVimInTerminal('', #{cols: 20})
@@ -309,6 +335,88 @@ func Test_eob_fillchars()
close
endfunc
+" Test for 'foldopen', 'foldclose' and 'foldsep' in 'fillchars'
+func Test_fold_fillchars()
+ new
+ set fdc=2 foldenable foldmethod=manual
+ call setline(1, ['one', 'two', 'three', 'four', 'five'])
+ 2,4fold
+ " First check for the default setting for a closed fold
+ let lines = ScreenLines([1, 3], 8)
+ let expected = [
+ \ ' one ',
+ \ '+ +-- 3',
+ \ ' five '
+ \ ]
+ call assert_equal(expected, lines)
+ normal 2Gzo
+ " check the characters for an open fold
+ let lines = ScreenLines([1, 5], 8)
+ let expected = [
+ \ ' one ',
+ \ '- two ',
+ \ '| three ',
+ \ '| four ',
+ \ ' five '
+ \ ]
+ call assert_equal(expected, lines)
+
+ " change the setting
+ set fillchars=vert:\|,fold:-,eob:~,foldopen:[,foldclose:],foldsep:-
+
+ " check the characters for an open fold
+ let lines = ScreenLines([1, 5], 8)
+ let expected = [
+ \ ' one ',
+ \ '[ two ',
+ \ '- three ',
+ \ '- four ',
+ \ ' five '
+ \ ]
+ call assert_equal(expected, lines)
+
+ " check the characters for a closed fold
+ normal 2Gzc
+ let lines = ScreenLines([1, 3], 8)
+ let expected = [
+ \ ' one ',
+ \ '] +-- 3',
+ \ ' five '
+ \ ]
+ call assert_equal(expected, lines)
+
+ %bw!
+ set fillchars& fdc& foldmethod& foldenable&
+endfunc
+
+func Test_local_fillchars()
+ CheckScreendump
+
+ let lines =<< trim END
+ call setline(1, ['window 1']->repeat(3))
+ setlocal fillchars=stl:1,stlnc:a,vert:=,eob:x
+ vnew
+ call setline(1, ['window 2']->repeat(3))
+ setlocal fillchars=stl:2,stlnc:b,vert:+,eob:y
+ new
+ wincmd J
+ call setline(1, ['window 3']->repeat(3))
+ setlocal fillchars=stl:3,stlnc:c,vert:<,eob:z
+ vnew
+ call setline(1, ['window 4']->repeat(3))
+ setlocal fillchars=stl:4,stlnc:d,vert:>,eob:o
+ END
+ call writefile(lines, 'Xdisplayfillchars')
+ let buf = RunVimInTerminal('-S Xdisplayfillchars', #{rows: 12})
+ call VerifyScreenDump(buf, 'Test_display_fillchars_1', {})
+
+ call term_sendkeys(buf, ":wincmd k\r")
+ call VerifyScreenDump(buf, 'Test_display_fillchars_2', {})
+
+ call StopVimInTerminal(buf)
+ call delete('Xdisplayfillchars')
+endfunc
+
func Test_display_linebreak_breakat()
new
vert resize 25
@@ -325,30 +433,49 @@ func Test_display_linebreak_breakat()
let &breakat=_breakat
endfunc
-func Test_display_lastline()
- CheckScreendump
-
+func Run_Test_display_lastline(euro)
let lines =<< trim END
- call setline(1, ['aaa', 'b'->repeat(100)])
+ call setline(1, ['aaa', 'b'->repeat(200)])
set display=truncate
+
vsplit
100wincmd <
END
- call writefile(lines, 'XdispLastline')
+ if a:euro != ''
+ let lines[2] = 'set fillchars=vert:\|,lastline:€'
+ endif
+ call writefile(lines, 'XdispLastline', 'D')
let buf = RunVimInTerminal('-S XdispLastline', #{rows: 10})
- call VerifyScreenDump(buf, 'Test_display_lastline_1', {})
+ call VerifyScreenDump(buf, $'Test_display_lastline_{a:euro}1', {})
call term_sendkeys(buf, ":set display=lastline\<CR>")
- call VerifyScreenDump(buf, 'Test_display_lastline_2', {})
+ call VerifyScreenDump(buf, $'Test_display_lastline_{a:euro}2', {})
call term_sendkeys(buf, ":100wincmd >\<CR>")
- call VerifyScreenDump(buf, 'Test_display_lastline_3', {})
+ call VerifyScreenDump(buf, $'Test_display_lastline_{a:euro}3', {})
call term_sendkeys(buf, ":set display=truncate\<CR>")
- call VerifyScreenDump(buf, 'Test_display_lastline_4', {})
+ call VerifyScreenDump(buf, $'Test_display_lastline_{a:euro}4', {})
+
+ call term_sendkeys(buf, ":close\<CR>")
+ call term_sendkeys(buf, ":3split\<CR>")
+ call VerifyScreenDump(buf, $'Test_display_lastline_{a:euro}5', {})
+
+ call term_sendkeys(buf, ":close\<CR>")
+ call term_sendkeys(buf, ":2vsplit\<CR>")
+ call VerifyScreenDump(buf, $'Test_display_lastline_{a:euro}6', {})
call StopVimInTerminal(buf)
- call delete('XdispLastline')
+endfunc
+
+func Test_display_lastline()
+ CheckScreendump
+
+ call Run_Test_display_lastline('')
+ call Run_Test_display_lastline('euro_')
+
+ call assert_fails(':set fillchars=lastline:', 'E474:')
+ call assert_fails(':set fillchars=lastline:〇', 'E474:')
endfunc
diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim
index e26bbdc5be..fd54f77ccb 100644
--- a/src/nvim/testdir/test_edit.vim
+++ b/src/nvim/testdir/test_edit.vim
@@ -330,6 +330,16 @@ func Test_edit_11_indentexpr()
set cinkeys&vim indentkeys&vim
set nocindent indentexpr=
delfu Do_Indent
+
+ " Using a script-local function
+ func s:NewIndentExpr()
+ endfunc
+ set indentexpr=s:NewIndentExpr()
+ call assert_equal(expand('<SID>') .. 'NewIndentExpr()', &indentexpr)
+ set indentexpr=<SID>NewIndentExpr()
+ call assert_equal(expand('<SID>') .. 'NewIndentExpr()', &indentexpr)
+ set indentexpr&
+
bw!
endfunc
@@ -713,23 +723,32 @@ endfunc
func Test_edit_CTRL_N()
" Check keyword completion
- new
- set complete=.
- call setline(1, ['INFER', 'loWER', '', '', ])
- call cursor(3, 1)
- call feedkeys("Ai\<c-n>\<cr>\<esc>", "tnix")
- call feedkeys("ILO\<c-n>\<cr>\<esc>", 'tnix')
- call assert_equal(['INFER', 'loWER', 'i', 'LO', '', ''], getline(1, '$'))
- %d
- call setline(1, ['INFER', 'loWER', '', '', ])
- call cursor(3, 1)
- set ignorecase infercase
- call feedkeys("Ii\<c-n>\<cr>\<esc>", "tnix")
- call feedkeys("ILO\<c-n>\<cr>\<esc>", 'tnix')
- call assert_equal(['INFER', 'loWER', 'infer', 'LOWER', '', ''], getline(1, '$'))
-
- set noignorecase noinfercase complete&
- bw!
+ " for e in ['latin1', 'utf-8']
+ for e in ['utf-8']
+ exe 'set encoding=' .. e
+ new
+ set complete=.
+ call setline(1, ['INFER', 'loWER', '', '', ])
+ call cursor(3, 1)
+ call feedkeys("Ai\<c-n>\<cr>\<esc>", "tnix")
+ call feedkeys("ILO\<c-n>\<cr>\<esc>", 'tnix')
+ call assert_equal(['INFER', 'loWER', 'i', 'LO', '', ''], getline(1, '$'), e)
+ %d
+ call setline(1, ['INFER', 'loWER', '', '', ])
+ call cursor(3, 1)
+ set ignorecase infercase
+ call feedkeys("Ii\<c-n>\<cr>\<esc>", "tnix")
+ call feedkeys("ILO\<c-n>\<cr>\<esc>", 'tnix')
+ call assert_equal(['INFER', 'loWER', 'infer', 'LOWER', '', ''], getline(1, '$'), e)
+ set noignorecase noinfercase
+ %d
+ call setline(1, ['one word', 'two word'])
+ exe "normal! Goo\<C-P>\<C-X>\<C-P>"
+ call assert_equal('one word', getline(3))
+ %d
+ set complete&
+ bw!
+ endfor
endfunc
func Test_edit_CTRL_O()
@@ -893,6 +912,24 @@ func Test_edit_CTRL_T()
bw!
endfunc
+" Test thesaurus completion with different encodings
+func Test_thesaurus_complete_with_encoding()
+ call writefile(['angry furious mad enraged'], 'Xthesaurus')
+ set thesaurus=Xthesaurus
+ " for e in ['latin1', 'utf-8']
+ for e in ['utf-8']
+ exe 'set encoding=' .. e
+ new
+ call setline(1, 'mad')
+ call cursor(1, 1)
+ call feedkeys("A\<c-x>\<c-t>\<cr>\<esc>", 'tnix')
+ call assert_equal(['mad', ''], getline(1, '$'))
+ bw!
+ endfor
+ set thesaurus=
+ call delete('Xthesaurus')
+endfunc
+
" Test 'thesaurusfunc'
func MyThesaurus(findstart, base)
let mythesaurus = [
@@ -1201,15 +1238,11 @@ func Test_edit_MOUSE()
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 Ntest_setmouse(4, 3)
call feedkeys("A\<LeftMouse>\<esc>", 'tnix')
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 Ntest_setmouse(5, 3)
call feedkeys("A\<RightMouse>\<esc>\<esc>", 'tnix')
call assert_equal([0, 28, 2, 0], getpos('.'))
set mousemodel&
@@ -1410,9 +1443,7 @@ endfunc
func Test_edit_rightleft()
" Cursor in rightleft mode moves differently
- if !exists("+rightleft")
- return
- endif
+ CheckFeature rightleft
call NewWindow(10, 20)
call setline(1, ['abc', 'def', 'ghi'])
call cursor(1, 2)
@@ -1457,6 +1488,13 @@ func Test_edit_rightleft()
\" ihg",
\" ~"]
call assert_equal(join(expect, "\n"), join(lines, "\n"))
+ %d _
+ " call test_override('redraw_flag', 1)
+ " call test_override('char_avail', 1)
+ call feedkeys("a\<C-V>x41", "xt")
+ redraw!
+ call assert_equal(repeat(' ', 19) .. 'A', Screenline(1))
+ " call test_override('ALL', 0)
set norightleft
bw!
endfunc
@@ -1701,40 +1739,6 @@ func Test_edit_illegal_filename()
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
@@ -1842,7 +1846,8 @@ endfunc
" Test for editing a file without read permission
func Test_edit_file_no_read_perm()
CheckUnix
- CheckNotBSD
+ CheckNotRoot
+
call writefile(['one', 'two'], 'Xfile')
call setfperm('Xfile', '-w-------')
new
@@ -1868,17 +1873,117 @@ func Test_edit_insertmode_ex_edit()
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 WaitForAssert({-> 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))
+ call WaitForAssert({-> assert_notmatch('^-- INSERT --\s*$', term_getline(buf, 6))})
" clean up
call StopVimInTerminal(buf)
call delete('Xtest_edit_insertmode_ex_edit')
endfunc
+" Pressing escape in 'insertmode' should beep
+" FIXME: Execute this later, when using valgrind it makes the next test
+" Test_edit_insertmode_ex_edit() fail.
+func Test_z_edit_insertmode_esc_beeps()
+ new
+ " set insertmode
+ " call assert_beeps("call feedkeys(\"one\<Esc>\", 'xt')")
+ set insertmode&
+ " unsupported "CTRL-G l" command should beep in insert mode.
+ call assert_beeps("normal i\<C-G>l")
+ bwipe!
+endfunc
+
+" Test for 'hkmap' and 'hkmapp'
+func Test_edit_hkmap()
+ CheckFeature rightleft
+ if has('win32') && !has('gui')
+ " Test fails on the MS-Windows terminal version
+ return
+ endif
+ new
+
+ set revins hkmap
+ let str = 'abcdefghijklmnopqrstuvwxyz'
+ let str ..= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ let str ..= '`/'',.;'
+ call feedkeys('i' .. str, 'xt')
+ let expected = "óõú,.;"
+ let expected ..= "ZYXWVUTSRQPONMLKJIHGFEDCBA"
+ let expected ..= "æèñ'äåàãø/ôíîöêìçïéòë÷âáðù"
+ call assert_equal(expected, getline(1))
+
+ %d
+ set revins hkmap hkmapp
+ let str = 'abcdefghijklmnopqrstuvwxyz'
+ let str ..= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ call feedkeys('i' .. str, 'xt')
+ let expected = "õYXWVUTSRQóOïíLKJIHGFEDêBA"
+ let expected ..= "öòXùåèúæø'ôñðîì÷çéäâóǟãëáà"
+ call assert_equal(expected, getline(1))
+
+ set revins& hkmap& hkmapp&
+ close!
+endfunc
+
+" Test for 'allowrevins' and using CTRL-_ in insert mode
+func Test_edit_allowrevins()
+ CheckFeature rightleft
+ new
+ set allowrevins
+ call feedkeys("iABC\<C-_>DEF\<C-_>GHI", 'xt')
+ call assert_equal('ABCFEDGHI', getline(1))
+ set allowrevins&
+ close!
+endfunc
+
+" Test for inserting a register in insert mode using CTRL-R
+func Test_edit_insert_reg()
+ throw 'Skipped: use test/functional/legacy/edit_spec.lua'
+ new
+ let g:Line = ''
+ func SaveFirstLine()
+ let g:Line = Screenline(1)
+ return 'r'
+ endfunc
+ inoremap <expr> <buffer> <F2> SaveFirstLine()
+ call test_override('redraw_flag', 1)
+ call test_override('char_avail', 1)
+ let @r = 'sample'
+ call feedkeys("a\<C-R>=SaveFirstLine()\<CR>", "xt")
+ call assert_equal('"', g:Line)
+ call test_override('ALL', 0)
+ close!
+endfunc
+
+" When a character is inserted at the last position of the last line in a
+" window, the window contents should be scrolled one line up. If the top line
+" is part of a fold, then the entire fold should be scrolled up.
+func Test_edit_lastline_scroll()
+ new
+ let h = winheight(0)
+ let lines = ['one', 'two', 'three']
+ let lines += repeat(['vim'], h - 4)
+ call setline(1, lines)
+ call setline(h, repeat('x', winwidth(0) - 1))
+ call feedkeys("GAx", 'xt')
+ redraw!
+ call assert_equal(h - 1, winline())
+ call assert_equal(2, line('w0'))
+
+ " scroll with a fold
+ 1,2fold
+ normal gg
+ call setline(h + 1, repeat('x', winwidth(0) - 1))
+ call feedkeys("GAx", 'xt')
+ redraw!
+ call assert_equal(h - 1, winline())
+ call assert_equal(3, line('w0'))
+
+ close!
+endfunc
+
func Test_edit_browse()
" in the GUI this opens a file picker, we only test the terminal behavior
CheckNotGui
@@ -1904,4 +2009,76 @@ func Test_read_invalid()
set encoding=utf-8
endfunc
+" Test for the 'revins' option
+func Test_edit_revins()
+ CheckFeature rightleft
+ new
+ set revins
+ exe "normal! ione\ttwo three"
+ call assert_equal("eerht owt\teno", getline(1))
+ call setline(1, "one\ttwo three")
+ normal! gg$bi a
+ call assert_equal("one\ttwo a three", getline(1))
+ exe "normal! $bi\<BS>\<BS>"
+ call assert_equal("one\ttwo a ree", getline(1))
+ exe "normal! 0wi\<C-W>"
+ call assert_equal("one\t a ree", getline(1))
+ exe "normal! 0wi\<C-U>"
+ call assert_equal("one\t ", getline(1))
+ " newline in insert mode starts at the end of the line
+ call setline(1, 'one two three')
+ exe "normal! wi\nfour"
+ call assert_equal(['one two three', 'ruof'], getline(1, '$'))
+ set revins&
+ bw!
+endfunc
+
+" Test for getting the character of the line below after "p"
+func Test_edit_put_CTRL_E()
+ " set encoding=latin1
+ new
+ let @" = ''
+ sil! norm orggRx
+ sil! norm pr
+ call assert_equal(['r', 'r'], getline(1, 2))
+ bwipe!
+ set encoding=utf-8
+endfunc
+
+" Test toggling of input method. See :help i_CTRL-^
+func Test_edit_CTRL_hat()
+ CheckFeature xim
+
+ " FIXME: test fails with Motif GUI.
+ " test also fails when running in the GUI.
+ CheckFeature gui_gtk
+ CheckNotGui
+
+ new
+
+ call assert_equal(0, &iminsert)
+ call feedkeys("i\<C-^>", 'xt')
+ call assert_equal(2, &iminsert)
+ call feedkeys("i\<C-^>", 'xt')
+ call assert_equal(0, &iminsert)
+
+ bwipe!
+endfunc
+
+" Weird long file name was going over the end of NameBuff
+func Test_edit_overlong_file_name()
+ CheckUnix
+
+ file 0000000000000000000000000000
+ file %%%%%%%%%%%%%%%%%%%%%%%%%%
+ file %%%%%%
+ set readonly
+ set ls=2
+
+ redraw!
+ set noreadonly ls&
+ bwipe!
+endfunc
+
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_escaped_glob.vim b/src/nvim/testdir/test_escaped_glob.vim
index 1a4fd8bdab..9f53c76a2c 100644
--- a/src/nvim/testdir/test_escaped_glob.vim
+++ b/src/nvim/testdir/test_escaped_glob.vim
@@ -1,7 +1,7 @@
" Test whether glob()/globpath() return correct results with certain escaped
" characters.
-function SetUp()
+func SetUp()
" consistent sorting of file names
set nofileignorecase
endfunction
diff --git a/src/nvim/testdir/test_eval_stuff.vim b/src/nvim/testdir/test_eval_stuff.vim
index eff1376d3c..46482c34a1 100644
--- a/src/nvim/testdir/test_eval_stuff.vim
+++ b/src/nvim/testdir/test_eval_stuff.vim
@@ -1,5 +1,8 @@
" Tests for various eval things.
+source view_util.vim
+source shared.vim
+
function s:foo() abort
try
return [] == 0
@@ -17,13 +20,8 @@ func Test_nocatch_restore_silent_emsg()
throw 1
catch
endtry
- echoerr 'wrong'
- let c1 = nr2char(screenchar(&lines, 1))
- let c2 = nr2char(screenchar(&lines, 2))
- let c3 = nr2char(screenchar(&lines, 3))
- let c4 = nr2char(screenchar(&lines, 4))
- let c5 = nr2char(screenchar(&lines, 5))
- call assert_equal('wrong', c1 . c2 . c3 . c4 . c5)
+ echoerr 'wrong again'
+ call assert_equal('wrong again', ScreenLine(&lines))
endfunc
func Test_mkdir_p()
@@ -87,22 +85,54 @@ func Test_for_over_null_string()
let &enc = save_enc
endfunc
+func Test_for_invalid_line_count()
+ let lines =<< trim END
+ 111111111111111111111111 for line in ['one']
+ endfor
+ END
+ call writefile(lines, 'XinvalidFor')
+ " only test that this doesn't crash
+ call RunVim([], [], '-u NONE -e -s -S XinvalidFor -c qa')
+
+ call delete('XinvalidFor')
+endfunc
+
func Test_readfile_binary()
new
call setline(1, ['one', 'two', 'three'])
setlocal ff=dos
- silent write XReadfile
- let lines = readfile('XReadfile')
+ silent write XReadfile_bin
+ let lines = 'XReadfile_bin'->readfile()
call assert_equal(['one', 'two', 'three'], lines)
- let lines = readfile('XReadfile', '', 2)
+ let lines = readfile('XReadfile_bin', '', 2)
call assert_equal(['one', 'two'], lines)
- let lines = readfile('XReadfile', 'b')
+ let lines = readfile('XReadfile_bin', 'b')
call assert_equal(["one\r", "two\r", "three\r", ""], lines)
- let lines = readfile('XReadfile', 'b', 2)
+ let lines = readfile('XReadfile_bin', 'b', 2)
call assert_equal(["one\r", "two\r"], lines)
bwipe!
- call delete('XReadfile')
+ call delete('XReadfile_bin')
+endfunc
+
+func Test_readfile_binary_empty()
+ call writefile([], 'Xempty-file')
+ " This used to compare uninitialized memory in Vim <= 8.2.4065
+ call assert_equal([''], readfile('Xempty-file', 'b'))
+ call delete('Xempty-file')
+endfunc
+
+func Test_readfile_bom()
+ call writefile(["\ufeffFOO", "FOO\ufeffBAR"], 'XReadfile_bom')
+ call assert_equal(['FOO', 'FOOBAR'], readfile('XReadfile_bom'))
+ call delete('XReadfile_bom')
+endfunc
+
+func Test_readfile_max()
+ call writefile(range(1, 4), 'XReadfile_max')
+ call assert_equal(['1', '2'], readfile('XReadfile_max', '', 2))
+ call assert_equal(['3', '4'], readfile('XReadfile_max', '', -2))
+ call delete('XReadfile_max')
endfunc
func Test_let_errmsg()
@@ -332,6 +362,11 @@ func Test_curly_assignment()
unlet g:gvar
endfunc
+func Test_deep_recursion()
+ " this was running out of stack
+ call assert_fails("exe 'if ' .. repeat('(', 1002)", 'E1169: Expression too recursive: ((')
+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,
diff --git a/src/nvim/testdir/test_ex_mode.vim b/src/nvim/testdir/test_ex_mode.vim
index 2f734cba26..93100732ed 100644
--- a/src/nvim/testdir/test_ex_mode.vim
+++ b/src/nvim/testdir/test_ex_mode.vim
@@ -97,7 +97,6 @@ func Test_Ex_substitute()
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
diff --git a/src/nvim/testdir/test_excmd.vim b/src/nvim/testdir/test_excmd.vim
index dac7a6989d..44bed890f5 100644
--- a/src/nvim/testdir/test_excmd.vim
+++ b/src/nvim/testdir/test_excmd.vim
@@ -78,6 +78,14 @@ func Test_file_cmd()
call assert_fails('3file', 'E474:')
call assert_fails('0,0file', 'E474:')
call assert_fails('0file abc', 'E474:')
+ if !has('win32')
+ " Change the name of the buffer to the same name
+ new Xfile1
+ file Xfile1
+ call assert_equal('Xfile1', @%)
+ call assert_equal('Xfile1', @#)
+ bw!
+ endif
endfunc
" Test for the :drop command
@@ -230,7 +238,6 @@ 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:')
@@ -477,20 +484,23 @@ func Test_winsize_cmd()
endfunc
" Test for the :redir command
+" NOTE: if you run tests as root this will fail. Don't run tests as root!
func Test_redir_cmd()
call assert_fails('redir @@', 'E475:')
call assert_fails('redir abc', 'E475:')
+ call assert_fails('redir => 1abc', 'E474:')
+ call assert_fails('redir => a b', 'E488:')
+ call assert_fails('redir => abc[1]', 'E121:')
+ let b = 0zFF
+ call assert_fails('redir =>> b', 'E734:')
+ unlet b
+
if has('unix')
+ " Redirecting to a directory name
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
@@ -503,6 +513,16 @@ func Test_redir_cmd()
call assert_equal('blue sky', color)
endfunc
+func Test_redir_cmd_readonly()
+ CheckNotRoot
+
+ " Redirecting to a read-only file
+ call writefile([], 'Xfile')
+ call setfperm('Xfile', 'r--r--r--')
+ call assert_fails('redir! > Xfile', 'E190:')
+ call delete('Xfile')
+endfunc
+
" Test for the :filetype command
func Test_filetype_cmd()
call assert_fails('filetype abc', 'E475:')
@@ -568,10 +588,12 @@ endfunc
" Test for the :verbose command
func Test_verbose_cmd()
- call assert_equal([' verbose=1'], split(execute('verbose set vbs'), "\n"))
+ set verbose=3
+ call assert_match(' verbose=1\n\s*Last set from ', 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"))
+ set verbose=0
+ call assert_match(' verbose=4\n\s*Last set from .*\n verbose=0',
+ \ execute("4verbose set verbose | set verbose"))
endfunc
" Test for the :delete command and the related abbreviated commands
@@ -661,12 +683,24 @@ func Sandbox_tests()
if has('unix')
call assert_fails('cd `pwd`', 'E48:')
endif
+ " some options cannot be changed in a sandbox
+ call assert_fails('set exrc', 'E48:')
+ call assert_fails('set cdpath', 'E48:')
+ if has('xim') && has('gui_gtk')
+ call assert_fails('set imstyle', 'E48:')
+ endif
endfunc
func Test_sandbox()
sandbox call Sandbox_tests()
endfunc
+func Test_command_not_implemented_E319()
+ if !has('mzscheme')
+ call assert_fails('mzscheme', 'E319:')
+ endif
+endfunc
+
func Test_not_break_expression_register()
call setreg('=', '1+1')
if 0
diff --git a/src/nvim/testdir/test_exists.vim b/src/nvim/testdir/test_exists.vim
index 471c77853d..62c66192ef 100644
--- a/src/nvim/testdir/test_exists.vim
+++ b/src/nvim/testdir/test_exists.vim
@@ -68,6 +68,10 @@ func Test_exists()
" Existing environment variable
let $EDITOR_NAME = 'Vim Editor'
call assert_equal(1, exists('$EDITOR_NAME'))
+ if has('unix')
+ " ${name} environment variables are supported only on Unix-like systems
+ call assert_equal(1, exists('${VIM}'))
+ endif
" Non-existing environment variable
call assert_equal(0, exists('$NON_ENV_VAR'))
@@ -323,3 +327,5 @@ endfunc
func Test_exists_funcarg()
call FuncArg_Tests("arg1", "arg2")
endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_exit.vim b/src/nvim/testdir/test_exit.vim
index 37be293950..6dbfb7047c 100644
--- a/src/nvim/testdir/test_exit.vim
+++ b/src/nvim/testdir/test_exit.vim
@@ -117,6 +117,7 @@ func Test_exit_error_reading_input()
CheckNotMSWindows
" The early exit causes memory not to be freed somehow
CheckNotAsan
+ CheckNotValgrind
call writefile([":au VimLeave * call writefile(['l = ' .. v:exiting], 'Xtestout')", ":tabnew", "q:"], 'Xscript', 'b')
diff --git a/src/nvim/testdir/test_expand.vim b/src/nvim/testdir/test_expand.vim
index ce414e4b11..4f5bb67d21 100644
--- a/src/nvim/testdir/test_expand.vim
+++ b/src/nvim/testdir/test_expand.vim
@@ -90,14 +90,26 @@ func Test_expandcmd()
" Test for expression expansion `=
let $FOO= "blue"
call assert_equal("blue sky", expandcmd("`=$FOO .. ' sky'`"))
+ let x = expandcmd("`=axbycz`")
+ call assert_equal('`=axbycz`', x)
+ call assert_fails('let x = expandcmd("`=axbycz`", #{errmsg: 1})', 'E121:')
+ let x = expandcmd("`=axbycz`", #{abc: []})
+ call assert_equal('`=axbycz`', x)
" Test for env variable with spaces
let $FOO= "foo bar baz"
call assert_equal("e foo bar baz", expandcmd("e $FOO"))
- if has('unix')
- " test for using the shell to expand a command argument
- call assert_equal('{1..4}', expandcmd('{1..4}'))
+ if has('unix') && executable('bash')
+ " test for using the shell to expand a command argument.
+ " only bash supports the {..} syntax
+ set shell=bash
+ let x = expandcmd('{1..4}')
+ call assert_equal('{1..4}', x)
+ call assert_fails("let x = expandcmd('{1..4}', #{errmsg: v:true})", 'E77:')
+ let x = expandcmd('{1..4}', #{error: v:true})
+ call assert_equal('{1..4}', x)
+ set shell&
endif
unlet $FOO
@@ -116,13 +128,21 @@ func Test_source_sfile()
:call assert_equal('edit <cword>', expandcmd("edit <cword>"))
:call assert_equal('edit <cexpr>', expandcmd("edit <cexpr>"))
:call assert_fails('autocmd User MyCmd echo "<sfile>"', 'E498:')
+ :
+ :call assert_equal('', expand('<script>'))
+ :verbose echo expand('<script>')
+ :call add(v:errors, v:errmsg)
+ :verbose echo expand('<sfile>')
+ :call add(v:errors, v:errmsg)
:call writefile(v:errors, 'Xresult')
:qall!
-
[SCRIPT]
call writefile(lines, 'Xscript')
if RunVim([], [], '--clean -s Xscript')
- call assert_equal([], readfile('Xresult'))
+ call assert_equal([
+ \ 'E1274: No script file name to substitute for "<script>"',
+ \ 'E498: no :source file name to substitute for "<sfile>"'],
+ \ readfile('Xresult'))
endif
call delete('Xscript')
call delete('Xresult')
@@ -147,4 +167,63 @@ func Test_expandcmd_shell_nonomatch()
call assert_equal('$*', expandcmd('$*'))
endfunc
+func Test_expand_script_source()
+ let lines0 =<< trim [SCRIPT]
+ call extend(g:script_level, [expand('<script>:t')])
+ so Xscript1
+ func F0()
+ call extend(g:func_level, [expand('<script>:t')])
+ endfunc
+
+ au User * call extend(g:au_level, [expand('<script>:t')])
+ [SCRIPT]
+
+ let lines1 =<< trim [SCRIPT]
+ call extend(g:script_level, [expand('<script>:t')])
+ so Xscript2
+ func F1()
+ call extend(g:func_level, [expand('<script>:t')])
+ endfunc
+
+ au User * call extend(g:au_level, [expand('<script>:t')])
+ [SCRIPT]
+
+ let lines2 =<< trim [SCRIPT]
+ call extend(g:script_level, [expand('<script>:t')])
+ func F2()
+ call extend(g:func_level, [expand('<script>:t')])
+ endfunc
+
+ au User * call extend(g:au_level, [expand('<script>:t')])
+ [SCRIPT]
+
+ call writefile(lines0, 'Xscript0')
+ call writefile(lines1, 'Xscript1')
+ call writefile(lines2, 'Xscript2')
+
+ " Check the expansion of <script> at different levels.
+ let g:script_level = []
+ let g:func_level = []
+ let g:au_level = []
+
+ so Xscript0
+ call F0()
+ call F1()
+ call F2()
+ doautocmd User
+
+ call assert_equal(['Xscript0', 'Xscript1', 'Xscript2'], g:script_level)
+ call assert_equal(['Xscript0', 'Xscript1', 'Xscript2'], g:func_level)
+ call assert_equal(['Xscript2', 'Xscript1', 'Xscript0'], g:au_level)
+
+ unlet g:script_level g:func_level
+ delfunc F0
+ delfunc F1
+ delfunc F2
+
+ call delete('Xscript0')
+ call delete('Xscript1')
+ call delete('Xscript2')
+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 df01d84f19..454d76f0aa 100644
--- a/src/nvim/testdir/test_expand_func.vim
+++ b/src/nvim/testdir/test_expand_func.vim
@@ -107,10 +107,15 @@ endfunc
func Test_expand()
new
- call assert_equal("", expand('%:S'))
+ 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.
+ call assert_equal("", expand('%'))
+ set verbose=1
+ call assert_equal("", expand('%'))
+ set verbose=0
+ call assert_equal("", expand('%:p'))
quit
endfunc
@@ -134,6 +139,7 @@ func Test_expand_wildignore()
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))
+ call assert_fails("call expand('*', [])", 'E745:')
set wildignore&
endfunc
diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim
index 5b10e691e5..47f7f5eb0e 100644
--- a/src/nvim/testdir/test_expr.vim
+++ b/src/nvim/testdir/test_expr.vim
@@ -1,5 +1,7 @@
" Tests for expressions.
+source check.vim
+
func Test_equal()
let base = {}
func base.method()
@@ -44,6 +46,7 @@ func Test_dict()
call assert_equal('zero', d[0])
call assert_true(has_key(d, ''))
call assert_true(has_key(d, 'a'))
+ call assert_fails("let i = has_key([], 'a')", 'E715:')
let d[''] = 'none'
let d['a'] = 'aaa'
@@ -62,6 +65,8 @@ func Test_strgetchar()
call assert_equal(-1, strgetchar('axb', -1))
call assert_equal(-1, strgetchar('axb', 3))
call assert_equal(-1, strgetchar('', 0))
+ call assert_fails("let c=strgetchar([], 1)", 'E730:')
+ call assert_fails("let c=strgetchar('axb', [])", 'E745:')
endfunc
func Test_strcharpart()
@@ -76,22 +81,27 @@ func Test_strcharpart()
call assert_equal('', strcharpart('axb', -2, 2))
call assert_equal('a', strcharpart('axb', -1, 2))
+
+ call assert_equal('edit', "editor"[-10:3])
+endfunc
+
+func Test_getreg_empty_list()
+ call assert_equal('', getreg('x'))
+ call assert_equal([], getreg('x', 1, 1))
+ let x = getreg('x', 1, 1)
+ let y = x
+ call add(x, 'foo')
+ call assert_equal(['foo'], y)
+ call assert_fails('call getreg([])', 'E730:')
endfunc
func Test_loop_over_null_list()
- let null_list = submatch(1, 1)
+ let null_list = v:_null_list
for i in null_list
call assert_report('should not get here')
endfor
endfunc
-func Test_compare_null_dict()
- call assert_fails('let x = v:_null_dict[10]')
- call assert_equal({}, {})
- call assert_equal(v:_null_dict, v:_null_dict)
- call assert_notequal({}, v:_null_dict)
-endfunc
-
func Test_set_reg_null_list()
call setreg('x', v:_null_list)
endfunc
@@ -478,49 +488,6 @@ function Test_max_min_errors()
call assert_fails('call min(v:true)', 'min()')
endfunc
-func Test_substitute_expr()
- let g:val = 'XXX'
- call assert_equal('XXX', substitute('yyy', 'y*', '\=g:val', ''))
- call assert_equal('XXX', substitute('yyy', 'y*', {-> g:val}, ''))
- call assert_equal("-\u1b \uf2-", substitute("-%1b %f2-", '%\(\x\x\)',
- \ '\=nr2char("0x" . submatch(1))', 'g'))
- call assert_equal("-\u1b \uf2-", substitute("-%1b %f2-", '%\(\x\x\)',
- \ {-> nr2char("0x" . submatch(1))}, 'g'))
-
- call assert_equal('231', substitute('123', '\(.\)\(.\)\(.\)',
- \ {-> submatch(2) . submatch(3) . submatch(1)}, ''))
-
- func Recurse()
- return substitute('yyy', 'y\(.\)y', {-> submatch(1)}, '')
- endfunc
- " recursive call works
- call assert_equal('-y-x-', substitute('xxx', 'x\(.\)x', {-> '-' . Recurse() . '-' . submatch(1) . '-'}, ''))
-endfunc
-
-func Test_invalid_submatch()
- " This was causing invalid memory access in Vim-7.4.2232 and older
- call assert_fails("call substitute('x', '.', {-> submatch(10)}, '')", 'E935:')
-endfunc
-
-func Test_substitute_expr_arg()
- call assert_equal('123456789-123456789=', substitute('123456789',
- \ '\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)',
- \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
-
- call assert_equal('123456-123456=789', substitute('123456789',
- \ '\(.\)\(.\)\(.\)\(a*\)\(n*\)\(.\)\(.\)\(.\)\(x*\)',
- \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
-
- call assert_equal('123456789-123456789x=', substitute('123456789',
- \ '\(.\)\(.\)\(.*\)',
- \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . 'x' . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
-
- call assert_fails("call substitute('xxx', '.', {m -> string(add(m, 'x'))}, '')", 'E742:')
- call assert_fails("call substitute('xxx', '.', {m -> string(insert(m, 'x'))}, '')", 'E742:')
- call assert_fails("call substitute('xxx', '.', {m -> string(extend(m, ['x']))}, '')", 'E742:')
- call assert_fails("call substitute('xxx', '.', {m -> string(remove(m, 1))}, '')", 'E742:')
-endfunc
-
func Test_function_with_funcref()
let s:f = function('type')
let s:fref = function(s:f)
@@ -529,6 +496,14 @@ func Test_function_with_funcref()
call assert_fails("call function('foo()')", 'E475:')
call assert_fails("call function('foo()')", 'foo()')
+ call assert_fails("function('')", 'E129:')
+
+ let Len = {s -> strlen(s)}
+ call assert_equal(6, Len('foobar'))
+ let name = string(Len)
+ " can evaluate "function('<lambda>99')"
+ call execute('let Ref = ' .. name)
+ call assert_equal(4, Ref('text'))
endfunc
func Test_funcref()
@@ -547,6 +522,22 @@ func Test_funcref()
call assert_fails('echo funcref("{")', 'E475:')
let OneByRef = funcref("One", repeat(["foo"], 20))
call assert_fails('let OneByRef = funcref("One", repeat(["foo"], 21))', 'E118:')
+ call assert_fails('echo function("min") =~ function("min")', 'E694:')
+endfunc
+
+" Test for calling function() and funcref() outside of a Vim script context.
+func Test_function_outside_script()
+ let cleanup =<< trim END
+ call writefile([execute('messages')], 'Xtest.out')
+ qall
+ END
+ call writefile(cleanup, 'Xverify.vim')
+ call RunVim([], [], "-c \"echo function('s:abc')\" -S Xverify.vim")
+ call assert_match('E81: Using <SID> not in a', readfile('Xtest.out')[0])
+ call RunVim([], [], "-c \"echo funcref('s:abc')\" -S Xverify.vim")
+ call assert_match('E81: Using <SID> not in a', readfile('Xtest.out')[0])
+ call delete('Xtest.out')
+ call delete('Xverify.vim')
endfunc
func Test_setmatches()
@@ -560,6 +551,7 @@ func Test_setmatches()
endif
eval set->setmatches()
call assert_equal(exp, getmatches())
+ call assert_fails('let m = setmatches([], [])', 'E745:')
endfunc
func Test_empty_concatenate()
@@ -586,3 +578,99 @@ func Test_eval_after_if()
if 0 | eval SetVal('a') | endif | call SetVal('b')
call assert_equal('b', s:val)
endfunc
+
+func Test_divide_by_zero()
+ " only tests that this doesn't crash, the result is not important
+ echo 0 / 0
+ echo 0 / 0 / -1
+endfunc
+
+" Test for command-line completion of expressions
+func Test_expr_completion()
+ CheckFeature cmdline_compl
+ for cmd in [
+ \ 'let a = ',
+ \ 'const a = ',
+ \ 'if',
+ \ 'elseif',
+ \ 'while',
+ \ 'for',
+ \ 'echo',
+ \ 'echon',
+ \ 'execute',
+ \ 'echomsg',
+ \ 'echoerr',
+ \ 'call',
+ \ 'return',
+ \ 'cexpr',
+ \ 'caddexpr',
+ \ 'cgetexpr',
+ \ 'lexpr',
+ \ 'laddexpr',
+ \ 'lgetexpr']
+ call feedkeys(":" . cmd . " getl\<Tab>\<Home>\"\<CR>", 'xt')
+ call assert_equal('"' . cmd . ' getline(', getreg(':'))
+ endfor
+
+ " completion for the expression register
+ call feedkeys(":\"\<C-R>=float2\t\"\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"float2nr("', @=)
+
+ " completion for window local variables
+ let w:wvar1 = 10
+ let w:wvar2 = 10
+ call feedkeys(":echo w:wvar\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"echo w:wvar1 w:wvar2', @:)
+ unlet w:wvar1 w:wvar2
+
+ " completion for tab local variables
+ let t:tvar1 = 10
+ let t:tvar2 = 10
+ call feedkeys(":echo t:tvar\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"echo t:tvar1 t:tvar2', @:)
+ unlet t:tvar1 t:tvar2
+
+ " completion for variables
+ let g:tvar1 = 1
+ let g:tvar2 = 2
+ call feedkeys(":let g:tv\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"let g:tvar1 g:tvar2', @:)
+ " completion for variables after a ||
+ call feedkeys(":echo 1 || g:tv\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"echo 1 || g:tvar1 g:tvar2', @:)
+
+ " completion for options
+ call feedkeys(":echo &compat\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"echo &compatible', @:)
+ call feedkeys(":echo 1 && &compat\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"echo 1 && &compatible', @:)
+ call feedkeys(":echo &g:equala\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"echo &g:equalalways', @:)
+
+ " completion for string
+ call feedkeys(":echo \"Hello\\ World\"\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"echo \"Hello\\ World\"\<C-A>", @:)
+ call feedkeys(":echo 'Hello World'\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"echo 'Hello World'\<C-A>", @:)
+
+ " completion for command after a |
+ call feedkeys(":echo 'Hello' | cwin\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"echo 'Hello' | cwindow", @:)
+
+ " completion for environment variable
+ let $X_VIM_TEST_COMPLETE_ENV = 'foo'
+ call feedkeys(":let $X_VIM_TEST_COMPLETE_E\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_match('"let $X_VIM_TEST_COMPLETE_ENV', @:)
+ unlet $X_VIM_TEST_COMPLETE_ENV
+endfunc
+
+" Test for errors in expression evaluation
+func Test_expr_eval_error()
+ call assert_fails("let i = 'abc' . []", 'E730:')
+ call assert_fails("let l = [] + 10", 'E745:')
+ call assert_fails("let v = 10 + []", 'E745:')
+ call assert_fails("let v = 10 / []", 'E745:')
+ call assert_fails("let v = -{}", 'E728:')
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_filechanged.vim b/src/nvim/testdir/test_filechanged.vim
index b77f02afd1..fef0eb732f 100644
--- a/src/nvim/testdir/test_filechanged.vim
+++ b/src/nvim/testdir/test_filechanged.vim
@@ -140,7 +140,8 @@ func Test_FileChangedShell_edit()
endfunc
func Test_FileChangedShell_edit_dialog()
- throw 'Skipped: requires a UI to be active'
+ " requires a UI to be active
+ throw 'Skipped: use test/functional/legacy/filechanged_spec.lua'
CheckNotGui
CheckUnix " Using low level feedkeys() does not work on MS-Windows.
@@ -190,7 +191,8 @@ func Test_FileChangedShell_edit_dialog()
endfunc
func Test_file_changed_dialog()
- throw 'Skipped: requires a UI to be active'
+ " requires a UI to be active
+ throw 'Skipped: use test/functional/legacy/filechanged_spec.lua'
CheckUnix
CheckNotGui
au! FileChangedShell
diff --git a/src/nvim/testdir/test_fileformat.vim b/src/nvim/testdir/test_fileformat.vim
index 81127ea59a..8d727a68c4 100644
--- a/src/nvim/testdir/test_fileformat.vim
+++ b/src/nvim/testdir/test_fileformat.vim
@@ -31,6 +31,15 @@ func Test_fileformat_autocommand()
bw!
endfunc
+func Test_fileformat_nomodifiable()
+ new
+ setlocal nomodifiable
+
+ call assert_fails('set fileformat=latin1', 'E21:')
+
+ bw
+endfunc
+
" Convert the contents of a file into a literal string
func s:file2str(fname)
let b = readfile(a:fname, 'B')
diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index e3a8370661..cddb1349f5 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -88,15 +88,17 @@ let s:filename_checks = {
\ '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'],
+ \ 'blueprint': ['file.blp'],
\ 'bsdl': ['file.bsd', 'file.bsdl'],
\ 'bst': ['file.bst'],
- \ 'bzl': ['file.bazel', 'file.bzl', 'WORKSPACE'],
+ \ 'bzl': ['file.bazel', 'file.bzl', 'WORKSPACE', 'WORKSPACE.bzlmod'],
\ 'bzr': ['bzr_log.any', 'bzr_log.file'],
\ 'c': ['enlightenment/file.cfg', 'file.qc', 'file.c', 'some-enlightenment/file.cfg'],
\ 'cabal': ['file.cabal'],
\ 'cabalconfig': ['cabal.config'],
\ 'cabalproject': ['cabal.project', 'cabal.project.local'],
\ 'calendar': ['calendar', '/.calendar/file', '/share/calendar/any/calendar.file', '/share/calendar/calendar.file', 'any/share/calendar/any/calendar.file', 'any/share/calendar/calendar.file'],
+ \ 'capnp': ['file.capnp'],
\ 'catalog': ['catalog', 'sgml.catalogfile', 'sgml.catalog', 'sgml.catalog-file'],
\ 'cdl': ['file.cdl'],
\ 'cdrdaoconf': ['/etc/cdrdao.conf', '/etc/defaults/cdrdao', '/etc/default/cdrdao', '.cdrdao', 'any/etc/cdrdao.conf', 'any/etc/default/cdrdao', 'any/etc/defaults/cdrdao'],
@@ -107,6 +109,7 @@ let s:filename_checks = {
\ 'ch': ['file.chf'],
\ 'chaiscript': ['file.chai'],
\ 'chaskell': ['file.chs'],
+ \ 'chatito': ['file.chatito'],
\ 'chill': ['file..ch'],
\ 'chordpro': ['file.chopro', 'file.crd', 'file.cho', 'file.crdpro', 'file.chordpro'],
\ 'cl': ['file.eni'],
@@ -120,10 +123,11 @@ let s:filename_checks = {
\ 'conaryrecipe': ['file.recipe'],
\ 'conf': ['auto.master'],
\ 'config': ['configure.in', 'configure.ac', '/etc/hostname.file', 'any/etc/hostname.file'],
- \ 'confini': ['/etc/pacman.conf', 'any/etc/pacman.conf', 'mpv.conf'],
+ \ 'confini': ['/etc/pacman.conf', 'any/etc/pacman.conf', 'mpv.conf', 'any/.aws/config', 'any/.aws/credentials'],
\ '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'],
+ \ 'cqlang': ['file.cql'],
\ 'crm': ['file.crm'],
\ 'crontab': ['crontab', 'crontab.file', '/etc/cron.d/file', 'any/etc/cron.d/file'],
\ 'cs': ['file.cs', 'file.csx'],
@@ -159,7 +163,7 @@ let s:filename_checks = {
\ 'dnsmasq': ['/etc/dnsmasq.conf', '/etc/dnsmasq.d/file', 'any/etc/dnsmasq.conf', 'any/etc/dnsmasq.d/file'],
\ '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'],
+ \ 'dosini': ['/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'],
@@ -171,6 +175,7 @@ let s:filename_checks = {
\ 'dylanlid': ['file.lid'],
\ 'ecd': ['file.ecd'],
\ 'edif': ['file.edf', 'file.edif', 'file.edo'],
+ \ 'editorconfig': ['.editorconfig'],
\ 'eelixir': ['file.eex', 'file.leex'],
\ 'elinks': ['elinks.conf'],
\ 'elixir': ['file.ex', 'file.exs', 'mix.lock'],
@@ -201,6 +206,7 @@ let s:filename_checks = {
\ 'fpcmake': ['file.fpc'],
\ 'framescript': ['file.fsl'],
\ 'freebasic': ['file.fb'],
+ \ 'fsh': ['file.fsh'],
\ 'fsharp': ['file.fs', 'file.fsi', 'file.fsx'],
\ 'fstab': ['fstab', 'mtab'],
\ 'fusion': ['file.fusion'],
@@ -209,11 +215,14 @@ let s:filename_checks = {
\ 'gdmo': ['file.mo', 'file.gdmo'],
\ 'gdresource': ['file.tscn', 'file.tres'],
\ 'gdscript': ['file.gd'],
+ \ 'gdshader': ['file.gdshader', 'file.shader'],
\ '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'],
+ \ 'gitattributes': ['file.git/info/attributes', '.gitattributes', '/.config/git/attributes', '/etc/gitattributes', '/usr/local/etc/gitattributes', 'some.git/info/attributes'],
\ 'gitcommit': ['COMMIT_EDITMSG', 'MERGE_MSG', 'TAG_EDITMSG', 'NOTES_EDITMSG', 'EDIT_DESCRIPTION'],
\ 'gitconfig': ['file.git/config', 'file.git/config.worktree', 'file.git/worktrees/x/config.worktree', '.gitconfig', '.gitmodules', 'file.git/modules//config', '/.config/git/config', '/etc/gitconfig', '/usr/local/etc/gitconfig', '/etc/gitconfig.d/file', 'any/etc/gitconfig.d/file', '/.gitconfig.d/file', 'any/.config/git/config', 'any/.gitconfig.d/file', 'some.git/config', 'some.git/modules/any/config'],
+ \ 'gitignore': ['file.git/info/exclude', '.gitignore', '/.config/git/ignore', 'some.git/info/exclude'],
\ 'gitolite': ['gitolite.conf', '/gitolite-admin/conf/file', 'any/gitolite-admin/conf/file'],
\ 'gitrebase': ['git-rebase-todo'],
\ 'gitsendemail': ['.gitsendemail.msg.xxxxxx'],
@@ -224,6 +233,7 @@ let s:filename_checks = {
\ 'gnuplot': ['file.gpi', '.gnuplot'],
\ 'go': ['file.go'],
\ 'gomod': ['go.mod'],
+ \ 'gosum': ['go.sum'],
\ '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'],
@@ -235,6 +245,7 @@ let s:filename_checks = {
\ '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'],
+ \ 'gyp': ['file.gyp', 'file.gypi'],
\ 'hack': ['file.hack', 'file.hackpartial'],
\ 'haml': ['file.haml'],
\ 'hamster': ['file.hsm'],
@@ -250,6 +261,7 @@ let s:filename_checks = {
\ 'hex': ['file.hex', 'file.h32'],
\ 'hgcommit': ['hg-editor-file.txt'],
\ 'hjson': ['file.hjson'],
+ \ 'hlsplaylist': ['file.m3u', 'file.m3u8'],
\ 'hog': ['file.hog', 'snort.conf', 'vision.conf'],
\ 'hollywood': ['file.hws'],
\ 'hoon': ['file.hoon'],
@@ -274,16 +286,18 @@ let s:filename_checks = {
\ 'jam': ['file.jpl', 'file.jpr', 'JAM-file.file', 'JAM.file', 'Prl-file.file', 'Prl.file'],
\ 'java': ['file.java', 'file.jav'],
\ 'javacc': ['file.jj', 'file.jjt'],
- \ 'javascript': ['file.js', 'file.javascript', 'file.es', 'file.mjs', 'file.cjs'],
+ \ 'javascript': ['file.js', 'file.jsm', 'file.javascript', 'file.es', 'file.mjs', 'file.cjs'],
\ 'javascript.glimmer': ['file.gjs'],
\ 'javascriptreact': ['file.jsx'],
\ 'jess': ['file.clp'],
\ 'jgraph': ['file.jgr'],
+ \ 'jq': ['file.jq'],
\ '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'],
+ \ 'jproperties': ['file.properties', 'file.properties_xx', 'file.properties_xx_xx', 'some.properties_xx_xx_file', 'org.eclipse.xyz.prefs'],
+ \ 'json': ['file.json', 'file.jsonp', 'file.json-patch', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb', '.prettierrc', '.firebaserc', 'file.slnf'],
\ 'json5': ['file.json5'],
- \ 'jsonc': ['file.jsonc'],
+ \ 'jsonc': ['file.jsonc', '.babelrc', '.eslintrc', '.jsfmtrc', '.jshintrc', '.hintrc', '.swrc', 'jsconfig.json', 'tsconfig.json', 'tsconfig.test.json', 'tsconfig-test.json'],
+ \ 'jsonnet': ['file.jsonnet', 'file.libsonnet'],
\ 'jsp': ['file.jsp'],
\ 'julia': ['file.jl'],
\ 'kconfig': ['Kconfig', 'Kconfig.debug', 'Kconfig.file'],
@@ -320,8 +334,9 @@ let s:filename_checks = {
\ 'lpc': ['file.lpc', 'file.ulpc'],
\ 'lsl': ['file.lsl'],
\ 'lss': ['file.lss'],
- \ 'lua': ['file.lua', 'file.rockspec', 'file.nse'],
+ \ 'lua': ['file.lua', 'file.rockspec', 'file.nse', '.luacheckrc'],
\ 'lynx': ['lynx.cfg'],
+ \ 'lyrics': ['file.lrc'],
\ 'm3build': ['m3makefile', 'm3overrides'],
\ 'm3quake': ['file.quake', 'cm3.cfg'],
\ 'm4': ['file.at'],
@@ -340,6 +355,7 @@ let s:filename_checks = {
\ 'maxima': ['file.demo', 'file.dmt', 'file.dm1', 'file.dm2', 'file.dm3',
\ 'file.wxm', 'maxima-init.mac'],
\ 'mel': ['file.mel'],
+ \ 'mermaid': ['file.mmd', 'file.mmdc', 'file.mermaid'],
\ 'meson': ['meson.build', 'meson_options.txt'],
\ 'messages': ['/log/auth', '/log/cron', '/log/daemon', '/log/debug', '/log/kern', '/log/lpr', '/log/mail', '/log/messages', '/log/news/news', '/log/syslog', '/log/user',
\ '/log/auth.log', '/log/cron.log', '/log/daemon.log', '/log/debug.log', '/log/kern.log', '/log/lpr.log', '/log/mail.log', '/log/messages.log', '/log/news/news.log', '/log/syslog.log', '/log/user.log',
@@ -357,7 +373,7 @@ 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'],
+ \ 'modula3': ['file.m3', 'file.mg', 'file.i3', 'file.ig', 'file.lm3'],
\ 'monk': ['file.isc', 'file.monk', 'file.ssc', 'file.tsc'],
\ 'moo': ['file.moo'],
\ 'moonscript': ['file.moon'],
@@ -378,12 +394,14 @@ let s:filename_checks = {
\ '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'],
+ \ 'nim': ['file.nim', 'file.nims', 'file.nimble'],
\ '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'],
\ 'obj': ['file.obj'],
+ \ 'obse': ['file.obl', 'file.obse', 'file.oblivion', 'file.obscript'],
\ 'ocaml': ['file.ml', 'file.mli', 'file.mll', 'file.mly', '.ocamlinit', 'file.mlt', 'file.mlp', 'file.mlip', 'file.mli.cppo', 'file.ml.cppo'],
\ 'occam': ['file.occ'],
\ 'octave': ['octaverc', '.octaverc', 'octave.conf'],
@@ -391,6 +409,7 @@ let s:filename_checks = {
\ 'opam': ['opam', 'file.opam', 'file.opam.template'],
\ 'openroad': ['file.or'],
\ 'openscad': ['file.scad'],
+ \ 'openvpn': ['file.ovpn', '/etc/openvpn/client/client.conf', '/usr/share/openvpn/examples/server.conf'],
\ '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'],
@@ -403,10 +422,10 @@ let s:filename_checks = {
\ 'pccts': ['file.g'],
\ 'pcmk': ['file.pcmk'],
\ 'pdf': ['file.pdf'],
- \ 'perl': ['file.plx', 'file.al', 'file.psgi', 'gitolite.rc', '.gitolite.rc', 'example.gitolite.rc'],
+ \ 'perl': ['file.plx', 'file.al', 'file.psgi', 'gitolite.rc', '.gitolite.rc', 'example.gitolite.rc', '.latexmkrc', 'latexmkrc'],
\ 'pf': ['pf.conf'],
- \ 'pfmain': ['main.cf'],
- \ 'php': ['file.php', 'file.php9', 'file.phtml', 'file.ctp', 'file.phpt'],
+ \ 'pfmain': ['main.cf', 'main.cf.proto'],
+ \ 'php': ['file.php', 'file.php9', 'file.phtml', 'file.ctp', 'file.phpt', 'file.theme'],
\ 'pike': ['file.pike', 'file.pmod'],
\ 'pilrc': ['file.rcp'],
\ 'pine': ['.pinerc', 'pinerc', '.pinercex', 'pinercex'],
@@ -417,6 +436,7 @@ let s:filename_checks = {
\ 'plsql': ['file.pls', 'file.plsql'],
\ 'po': ['file.po', 'file.pot'],
\ 'pod': ['file.pod'],
+ \ 'poefilter': ['file.filter'],
\ 'poke': ['file.pk'],
\ 'postscr': ['file.ps', 'file.pfa', 'file.afm', 'file.eps', 'file.epsf', 'file.epsi', 'file.ai'],
\ 'pov': ['file.pov'],
@@ -443,7 +463,7 @@ let s:filename_checks = {
\ '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'],
\ 'quarto': ['file.qmd'],
- \ 'r': ['file.r'],
+ \ 'r': ['file.r', '.Rprofile', 'Rprofile', 'Rprofile.site'],
\ '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'],
@@ -507,9 +527,11 @@ let s:filename_checks = {
\ 'slrnrc': ['.slrnrc'],
\ 'slrnsc': ['file.score'],
\ 'sm': ['sendmail.cf'],
+ \ 'smali': ['file.smali'],
\ 'smarty': ['file.tpl'],
\ 'smcl': ['file.hlp', 'file.ihlp', 'file.smcl'],
\ 'smith': ['file.smt', 'file.smith'],
+ \ 'smithy': ['file.smithy'],
\ 'sml': ['file.sml'],
\ 'snobol4': ['file.sno', 'file.spt'],
\ 'solidity': ['file.sol'],
@@ -525,13 +547,15 @@ let s:filename_checks = {
\ 'squid': ['squid.conf'],
\ 'squirrel': ['file.nut'],
\ 'srec': ['file.s19', 'file.s28', 'file.s37', 'file.mot', 'file.srec'],
+ \ 'srt': ['file.srt'],
+ \ 'ssa': ['file.ass', 'file.ssa'],
\ 'sshconfig': ['ssh_config', '/.ssh/config', '/etc/ssh/ssh_config.d/file.conf', 'any/etc/ssh/ssh_config.d/file.conf', 'any/.ssh/config', 'any/.ssh/file.conf'],
\ 'sshdconfig': ['sshd_config', '/etc/ssh/sshd_config.d/file.conf', 'any/etc/ssh/sshd_config.d/file.conf'],
\ 'st': ['file.st'],
\ 'stata': ['file.ado', 'file.do', 'file.imata', 'file.mata'],
\ 'stp': ['file.stp'],
\ 'sudoers': ['any/etc/sudoers', 'sudoers.tmp', '/etc/sudoers', 'any/etc/sudoers.d/file'],
- \ 'supercollider': ['file.quark'],
+ \ 'supercollider': ['file.quark'],
\ 'surface': ['file.sface'],
\ 'svelte': ['file.svelte'],
\ 'svg': ['file.svg'],
@@ -557,6 +581,7 @@ let s:filename_checks = {
\ 'texmf': ['texmf.cnf'],
\ 'text': ['file.text', 'file.txt', 'README', 'LICENSE', 'COPYING', 'AUTHORS', '/usr/share/doc/bash-completion/AUTHORS', '/etc/apt/apt.conf.d/README', '/etc/Muttrc.d/README'],
\ 'tf': ['file.tf', '.tfrc', 'tfrc'],
+ \ 'thrift': ['file.thrift'],
\ 'tidy': ['.tidyrc', 'tidyrc', 'tidy.conf'],
\ 'tilde': ['file.t.html'],
\ 'tla': ['file.tla'],
@@ -572,6 +597,7 @@ let s:filename_checks = {
\ 'tssop': ['file.tssop'],
\ 'tsv': ['file.tsv'],
\ 'twig': ['file.twig'],
+ \ 'typescript': ['file.mts', 'file.cts'],
\ 'typescript.glimmer': ['file.gts'],
\ 'typescriptreact': ['file.tsx'],
\ 'uc': ['file.uc'],
@@ -588,11 +614,16 @@ let s:filename_checks = {
\ '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'],
+ \ 'vdf': ['file.vdf'],
+ \ 'vdmpp': ['file.vpp', 'file.vdmpp'],
+ \ 'vdmrt': ['file.vdmrt'],
+ \ 'vdmsl': ['file.vdm', 'file.vdmsl'],
\ 'vera': ['file.vr', 'file.vri', 'file.vrh'],
\ 'verilog': ['file.v'],
\ 'verilogams': ['file.va', 'file.vams'],
\ 'vgrindefs': ['vgrindefs'],
\ 'vhdl': ['file.hdl', 'file.vhd', 'file.vhdl', 'file.vbe', 'file.vst', 'file.vhdl_123', 'file.vho', 'some.vhdl_1', 'some.vhdl_1-file'],
+ \ 'vhs': ['file.tape'],
\ 'vim': ['file.vim', 'file.vba', '.exrc', '_exrc', 'some-vimrc', 'some-vimrc-file', 'vimrc', 'vimrc-file'],
\ 'viminfo': ['.viminfo', '_viminfo'],
\ 'vmasm': ['file.mar'],
@@ -601,6 +632,7 @@ let s:filename_checks = {
\ 'vroom': ['file.vroom'],
\ 'vue': ['file.vue'],
\ 'wast': ['file.wast', 'file.wat'],
+ \ 'wdl': ['file.wdl'],
\ 'webmacro': ['file.wm'],
\ 'wget': ['.wgetrc', 'wgetrc'],
\ 'wget2': ['.wget2rc', 'wget2rc'],
@@ -623,12 +655,13 @@ let s:filename_checks = {
\ 'xsd': ['file.xsd'],
\ 'xslt': ['file.xsl', 'file.xslt'],
\ 'yacc': ['file.yy', 'file.yxx', 'file.y++'],
- \ 'yaml': ['file.yaml', 'file.yml'],
+ \ 'yaml': ['file.yaml', 'file.yml', '.clang-format', '.clang-tidy'],
\ 'yang': ['file.yang'],
\ 'z8a': ['file.z8a'],
\ 'zig': ['file.zig'],
\ 'zimbu': ['file.zu'],
\ 'zimbutempl': ['file.zut'],
+ \ 'zir': ['file.zir'],
\ 'zsh': ['.zprofile', '/etc/zprofile', '.zfbfmarks', 'file.zsh', '.zcompdump', '.zlogin', '.zlogout', '.zshenv', '.zshrc', '.zcompdump-file', '.zlog', '.zlog-file', '.zsh', '.zsh-file', 'any/etc/zprofile', 'zlog', 'zlog-file', 'zsh', 'zsh-file'],
\
\ 'help': [$VIMRUNTIME . '/doc/help.txt'],
@@ -682,6 +715,13 @@ let s:script_checks = {
\ ['__libc_start_main and something']],
\ 'clojure': [['#!/path/clojure']],
\ 'scala': [['#!/path/scala']],
+ \ 'sh': [['#!/path/sh'],
+ \ ['#!/path/bash'],
+ \ ['#!/path/bash2'],
+ \ ['#!/path/dash'],
+ \ ['#!/path/ksh'],
+ \ ['#!/path/ksh93']],
+ \ 'csh': [['#!/path/csh']],
\ 'tcsh': [['#!/path/tcsh']],
\ 'zsh': [['#!/path/zsh']],
\ 'tcl': [['#!/path/tclsh'],
@@ -918,7 +958,9 @@ func Test_d_file()
call assert_equal('d', &filetype)
bwipe!
+ " clean up
filetype off
+ call delete('Xfile.d')
endfunc
func Test_dat_file()
@@ -1346,7 +1388,7 @@ func Test_mod_file()
unlet g:filetype_mod
bwipe!
- " RAPID header start with a line containing only "%%%",
+ " RAPID header start with a line containing only "%%%",
" but is not always present.
call writefile(['%%%'], 'modfile.mod')
split modfile.mod
@@ -1362,7 +1404,7 @@ func Test_mod_file()
bwipe!
call delete('modfile.Mod')
- " RAPID is not case sensitive, embedded spaces, sysmodule,
+ " 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
@@ -1490,7 +1532,7 @@ func Test_prg_file()
unlet g:filetype_prg
bwipe!
- " RAPID header start with a line containing only "%%%",
+ " RAPID header start with a line containing only "%%%",
" but is not always present.
call writefile(['%%%'], 'prgfile.prg')
split prgfile.prg
@@ -1506,7 +1548,7 @@ func Test_prg_file()
bwipe!
call delete('prgfile.Prg')
- " RAPID is not case sensitive, embedded spaces, sysmodule,
+ " 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
@@ -1521,13 +1563,6 @@ endfunc
func Test_sc_file()
filetype on
- " 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')
-
" SC classes are defined with '+ Class {}'
call writefile(['+ SCNvim {', '*methodArgs {|method|'], 'srcfile.sc')
split srcfile.sc
@@ -1617,7 +1652,7 @@ func Test_sys_file()
unlet g:filetype_sys
bwipe!
- " RAPID header start with a line containing only "%%%",
+ " RAPID header start with a line containing only "%%%",
" but is not always present.
call writefile(['%%%'], 'sysfile.sys')
split sysfile.sys
@@ -1633,7 +1668,7 @@ func Test_sys_file()
bwipe!
call delete('sysfile.Sys')
- " RAPID is not case sensitive, embedded spaces, sysmodule,
+ " 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
@@ -1647,17 +1682,45 @@ endfunc
func Test_tex_file()
filetype on
- " only tests one case, should do more
+ call writefile(['%& pdflatex'], 'Xfile.tex')
+ split Xfile.tex
+ call assert_equal('tex', &filetype)
+ bwipe
+
+ call writefile(['\newcommand{\test}{some text}'], 'Xfile.tex')
+ split Xfile.tex
+ call assert_equal('tex', &filetype)
+ bwipe
+
+ " tex_flavor is unset
+ call writefile(['%& plain'], 'Xfile.tex')
+ split Xfile.tex
+ call assert_equal('plaintex', &filetype)
+ bwipe
+
+ let g:tex_flavor = 'plain'
+ call writefile(['just some text'], 'Xfile.tex')
+ split Xfile.tex
+ call assert_equal('plaintex', &filetype)
+ bwipe
+
let lines =<< trim END
- % This is a sentence.
+ % This is a comment.
- This is a sentence.
+ \usemodule[translate]
END
- call writefile(lines, "Xfile.tex")
+ call writefile(lines, 'Xfile.tex')
split Xfile.tex
- call assert_equal('plaintex', &filetype)
+ call assert_equal('context', &filetype)
bwipe
+ let g:tex_flavor = 'context'
+ call writefile(['just some text'], 'Xfile.tex')
+ split Xfile.tex
+ call assert_equal('context', &filetype)
+ bwipe
+ unlet g:tex_flavor
+
call delete('Xfile.tex')
filetype off
endfunc
@@ -1748,6 +1811,11 @@ func Test_cls_file()
call assert_equal('tex', &filetype)
bwipe!
+ call writefile(['\NeedsTeXFormat{LaTeX2e}'], 'Xfile.cls')
+ split Xfile.cls
+ call assert_equal('tex', &filetype)
+ bwipe!
+
" Rexx
call writefile(['# rexx'], 'Xfile.cls')
@@ -1820,6 +1888,44 @@ func Test_sig_file()
filetype off
endfunc
+" Test dist#ft#FTsil()
+func Test_sil_file()
+ filetype on
+
+ split Xfile.sil
+ call assert_equal('sil', &filetype)
+ bwipe!
+
+ let lines =<< trim END
+ // valid
+ let protoErasedPathA = \ABCProtocol.a
+
+ // also valid
+ let protoErasedPathA =
+ \ABCProtocol.a
+ END
+ call writefile(lines, 'Xfile.sil')
+
+ split Xfile.sil
+ call assert_equal('sil', &filetype)
+ bwipe!
+
+ " SILE
+
+ call writefile(['% some comment'], 'Xfile.sil')
+ split Xfile.sil
+ call assert_equal('sile', &filetype)
+ bwipe!
+
+ call writefile(['\begin[papersize=a6]{document}foo\end{document}'], 'Xfile.sil')
+ split Xfile.sil
+ call assert_equal('sile', &filetype)
+ bwipe!
+
+ call delete('Xfile.sil')
+ filetype off
+endfunc
+
func Test_inc_file()
filetype on
@@ -1899,4 +2005,36 @@ func Test_inc_file()
filetype off
endfunc
+func Test_lsl_file()
+ filetype on
+
+ call writefile(['looks like Linden Scripting Language'], 'Xfile.lsl')
+ split Xfile.lsl
+ call assert_equal('lsl', &filetype)
+ bwipe!
+
+ " Test dist#ft#FTlsl()
+
+ let g:filetype_lsl = 'larch'
+ split Xfile.lsl
+ call assert_equal('larch', &filetype)
+ bwipe!
+ unlet g:filetype_lsl
+
+ " Larch Shared Language
+
+ call writefile(['% larch comment'], 'Xfile.lsl')
+ split Xfile.lsl
+ call assert_equal('larch', &filetype)
+ bwipe!
+
+ call writefile(['foo: trait'], 'Xfile.lsl')
+ split Xfile.lsl
+ call assert_equal('larch', &filetype)
+ bwipe!
+
+ call delete('Xfile.lsl')
+ filetype off
+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 1cd3a2287b..c75177ea39 100644
--- a/src/nvim/testdir/test_filter_map.vim
+++ b/src/nvim/testdir/test_filter_map.vim
@@ -86,6 +86,13 @@ func Test_map_filter_fails()
call assert_fails('call filter([1], "42 +")', 'E15:')
call assert_fails("let l = map('abc', '\"> \" . v:val')", 'E896:')
call assert_fails("let l = filter('abc', '\"> \" . v:val')", 'E896:')
+ call assert_fails("let l = filter([1, 2, 3], '{}')", 'E728:')
+ call assert_fails("let l = filter({'k' : 10}, '{}')", 'E728:')
+ call assert_fails("let l = filter([1, 2], {})", 'E731:')
+ call assert_equal(v:_null_list, filter(v:_null_list, 0))
+ call assert_equal(v:_null_dict, filter(v:_null_dict, 0))
+ call assert_equal(v:_null_list, map(v:_null_list, '"> " .. v:val'))
+ call assert_equal(v:_null_dict, map(v:_null_dict, '"> " .. v:val'))
endfunc
func Test_map_and_modify()
diff --git a/src/nvim/testdir/test_fixeol.vim b/src/nvim/testdir/test_fixeol.vim
index 32cb059e26..41d47d6a06 100644
--- a/src/nvim/testdir/test_fixeol.vim
+++ b/src/nvim/testdir/test_fixeol.vim
@@ -1,16 +1,17 @@
-" Tests for 'fixeol' and 'eol'
+" Tests for 'fixeol', 'eof' and 'eol'
+
func Test_fixeol()
" first write two test files – with and without trailing EOL
" use Unix fileformat for consistency
set ff=unix
enew!
- call setline('.', 'with eol')
+ call setline('.', 'with eol or eof')
w! XXEol
enew!
- set noeol nofixeol
- call setline('.', 'without eol')
+ set noeof noeol nofixeol
+ call setline('.', 'without eol or eof')
w! XXNoEol
- set eol fixeol
+ set eol eof fixeol
bwipe XXEol XXNoEol
" try editing files with 'fixeol' disabled
@@ -33,16 +34,85 @@ func Test_fixeol()
w >>XXTestEol
w >>XXTestNoEol
- call assert_equal(['with eol', 'END'], readfile('XXEol'))
- call assert_equal(['without eolEND'], readfile('XXNoEol'))
- call assert_equal(['with eol', 'stays eol', 'END'], readfile('XXTestEol'))
- call assert_equal(['without eol', 'stays withoutEND'],
+ call assert_equal(['with eol or eof', 'END'], readfile('XXEol'))
+ call assert_equal(['without eol or eofEND'], readfile('XXNoEol'))
+ call assert_equal(['with eol or eof', 'stays eol', 'END'], readfile('XXTestEol'))
+ call assert_equal(['without eol or eof', 'stays withoutEND'],
\ readfile('XXTestNoEol'))
call delete('XXEol')
call delete('XXNoEol')
call delete('XXTestEol')
call delete('XXTestNoEol')
- set ff& fixeol& eol&
+ set ff& fixeol& eof& eol&
+ enew!
+endfunc
+
+func Test_eof()
+ let data = 0z68656c6c6f.0d0a.776f726c64 " "hello\r\nworld"
+
+ " 1. Eol, Eof
+ " read
+ call writefile(data + 0z0d0a.1a, 'XXEolEof')
+ e! XXEolEof
+ call assert_equal(['hello', 'world'], getline(1, 2))
+ call assert_equal([1, 1], [&eol, &eof])
+ " write
+ set fixeol
+ w!
+ call assert_equal(data + 0z0d0a, readblob('XXEolEof'))
+ set nofixeol
+ w!
+ call assert_equal(data + 0z0d0a.1a, readblob('XXEolEof'))
+
+ " 2. NoEol, Eof
+ " read
+ call writefile(data + 0z1a, 'XXNoEolEof')
+ e! XXNoEolEof
+ call assert_equal(['hello', 'world'], getline(1, 2))
+ call assert_equal([0, 1], [&eol, &eof])
+ " write
+ set fixeol
+ w!
+ call assert_equal(data + 0z0d0a, readblob('XXNoEolEof'))
+ set nofixeol
+ w!
+ call assert_equal(data + 0z1a, readblob('XXNoEolEof'))
+
+ " 3. Eol, NoEof
+ " read
+ call writefile(data + 0z0d0a, 'XXEolNoEof')
+ e! XXEolNoEof
+ call assert_equal(['hello', 'world'], getline(1, 2))
+ call assert_equal([1, 0], [&eol, &eof])
+ " write
+ set fixeol
+ w!
+ call assert_equal(data + 0z0d0a, readblob('XXEolNoEof'))
+ set nofixeol
+ w!
+ call assert_equal(data + 0z0d0a, readblob('XXEolNoEof'))
+
+ " 4. NoEol, NoEof
+ " read
+ call writefile(data, 'XXNoEolNoEof')
+ e! XXNoEolNoEof
+ call assert_equal(['hello', 'world'], getline(1, 2))
+ call assert_equal([0, 0], [&eol, &eof])
+ " write
+ set fixeol
+ w!
+ call assert_equal(data + 0z0d0a, readblob('XXNoEolNoEof'))
+ set nofixeol
+ w!
+ call assert_equal(data, readblob('XXNoEolNoEof'))
+
+ call delete('XXEolEof')
+ call delete('XXNoEolEof')
+ call delete('XXEolNoEof')
+ call delete('XXNoEolNoEof')
+ set ff& fixeol& eof& eol&
enew!
endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_fnamemodify.vim b/src/nvim/testdir/test_fnamemodify.vim
index 5ae2a5ee17..258a2093bd 100644
--- a/src/nvim/testdir/test_fnamemodify.vim
+++ b/src/nvim/testdir/test_fnamemodify.vim
@@ -11,6 +11,7 @@ func Test_fnamemodify()
call assert_equal('/', fnamemodify('.', ':p')[-1:])
call assert_equal('r', fnamemodify('.', ':p:h')[-1:])
call assert_equal('t', fnamemodify('test.out', ':p')[-1:])
+ call assert_equal($HOME .. "/foo" , fnamemodify('~/foo', ':p'))
call assert_equal('test.out', fnamemodify('test.out', ':.'))
call assert_equal('a', fnamemodify('../testdir/a', ':.'))
call assert_equal('~/testdir/test.out', fnamemodify('test.out', ':~'))
@@ -95,4 +96,9 @@ func Test_fnamemodify_er()
call assert_equal('', fnamemodify(v:_null_string, v:_null_string))
endfunc
+func Test_fnamemodify_fail()
+ call assert_fails('call fnamemodify({}, ":p")', 'E731:')
+ call assert_fails('call fnamemodify("x", {})', 'E731:')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim
index 327f0f73f2..19415286ad 100644
--- a/src/nvim/testdir/test_fold.vim
+++ b/src/nvim/testdir/test_fold.vim
@@ -1,5 +1,6 @@
" Test for folding
+source check.vim
source view_util.vim
source screendump.vim
@@ -71,6 +72,54 @@ func Test_address_fold()
quit!
endfunc
+func Test_address_offsets()
+ " check the help for :range-closed-fold
+ enew
+ call setline(1, [
+ \ '1 one',
+ \ '2 two',
+ \ '3 three',
+ \ '4 four FOLDED',
+ \ '5 five FOLDED',
+ \ '6 six',
+ \ '7 seven',
+ \ '8 eight',
+ \])
+ set foldmethod=manual
+ normal 4Gvjzf
+ 3,4+2yank
+ call assert_equal([
+ \ '3 three',
+ \ '4 four FOLDED',
+ \ '5 five FOLDED',
+ \ '6 six',
+ \ '7 seven',
+ \ ], getreg(0,1,1))
+
+ enew!
+ call setline(1, [
+ \ '1 one',
+ \ '2 two',
+ \ '3 three FOLDED',
+ \ '4 four FOLDED',
+ \ '5 five FOLDED',
+ \ '6 six FOLDED',
+ \ '7 seven',
+ \ '8 eight',
+ \])
+ normal 3Gv3jzf
+ 2,4-1yank
+ call assert_equal([
+ \ '2 two',
+ \ '3 three FOLDED',
+ \ '4 four FOLDED',
+ \ '5 five FOLDED',
+ \ '6 six FOLDED',
+ \ ], getreg(0,1,1))
+
+ bwipe!
+endfunc
+
func Test_indent_fold()
new
call setline(1, ['', 'a', ' b', ' c'])
@@ -93,10 +142,44 @@ func Test_indent_fold2()
bw!
endfunc
+" Test for fold indent with indents greater than 'foldnestmax'
+func Test_indent_fold_max()
+ new
+ setlocal foldmethod=indent
+ setlocal shiftwidth=2
+ " 'foldnestmax' default value is 20
+ call setline(1, "\t\t\t\t\t\ta")
+ call assert_equal(20, foldlevel(1))
+ setlocal foldnestmax=10
+ call assert_equal(10, foldlevel(1))
+ setlocal foldnestmax=-1
+ call assert_equal(0, foldlevel(1))
+ bw!
+endfunc
+
+func Test_indent_fold_tabstop()
+ call setline(1, ['0', ' 1', ' 1', "\t2", "\t2"])
+ setlocal shiftwidth=4
+ setlocal foldcolumn=1
+ setlocal foldlevel=2
+ setlocal foldmethod=indent
+ redraw
+ call assert_equal('2 2', ScreenLines(5, 10)[0])
+ vsplit
+ windo diffthis
+ botright new
+ " This 'tabstop' value should not be used for folding in other buffers.
+ setlocal tabstop=4
+ diffoff!
+ redraw
+ call assert_equal('2 2', ScreenLines(5, 10)[0])
+
+ bwipe!
+ bwipe!
+endfunc
+
func Test_manual_fold_with_filter()
- if !executable('cat')
- return
- endif
+ CheckExecutable cat
for type in ['manual', 'marker']
exe 'set foldmethod=' . type
new
@@ -418,27 +501,6 @@ func Test_move_folds_around_indent()
bw!
endfunc
-" test for patch 7.3.637
-" Cannot catch the error caused by a foldopen when there is no fold.
-func Test_foldopen_exception()
- enew!
- let a = 'No error caught'
- try
- foldopen
- catch
- let a = matchstr(v:exception,'^[^ ]*')
- endtry
- call assert_equal('Vim(foldopen):E490:', a)
-
- let a = 'No error caught'
- try
- foobar
- catch
- let a = matchstr(v:exception,'^[^ ]*')
- endtry
- call assert_match('E492:', a)
-endfunc
-
func Test_folddoopen_folddoclosed()
new
call setline(1, range(1, 9))
@@ -492,11 +554,24 @@ func Test_fold_error()
bw!
endfunc
+func Test_foldtext_recursive()
+ new
+ call setline(1, ['{{{', 'some text', '}}}'])
+ setlocal foldenable foldmethod=marker foldtext=foldtextresult(v\:foldstart)
+ " This was crashing because of endless recursion.
+ 2foldclose
+ redraw
+ call assert_equal(1, foldlevel(2))
+ call assert_equal(1, foldclosed(2))
+ call assert_equal(3, foldclosedend(2))
+ bwipe!
+endfunc
+
" Various fold related tests
" Basic test if a fold can be created, opened, moving to the end and closed
func Test_fold_manual()
- enew!
+ new
set fdm=manual
let content = ['1 aa', '2 bb', '3 cc']
@@ -511,13 +586,67 @@ func Test_fold_manual()
normal zc
call assert_equal('1 aa', getline(foldclosed('.')))
+ " Create a fold inside a closed fold after setting 'foldlevel'
+ %d _
+ call setline(1, range(1, 5))
+ 1,5fold
+ normal zR
+ 2,4fold
+ set foldlevel=1
+ 3fold
+ call assert_equal([1, 3, 3, 3, 1], map(range(1, 5), {->foldlevel(v:val)}))
+ set foldlevel&
+
+ " Create overlapping folds (at the start and at the end)
+ normal zE
+ 2,3fold
+ normal zR
+ 3,4fold
+ call assert_equal([0, 2, 2, 1, 0], map(range(1, 5), {->foldlevel(v:val)}))
+ normal zE
+ 3,4fold
+ normal zR
+ 2,3fold
+ call assert_equal([0, 1, 2, 2, 0], map(range(1, 5), {->foldlevel(v:val)}))
+
+ " Create a nested fold across two non-adjoining folds
+ %d _
+ call setline(1, range(1, 7))
+ 1,2fold
+ normal zR
+ 4,5fold
+ normal zR
+ 6,7fold
+ normal zR
+ 1,5fold
+ call assert_equal([2, 2, 1, 2, 2, 1, 1],
+ \ map(range(1, 7), {->foldlevel(v:val)}))
+
+ " A newly created nested fold should be closed
+ %d _
+ call setline(1, range(1, 6))
+ 1,6fold
+ normal zR
+ 3,4fold
+ normal zR
+ 2,5fold
+ call assert_equal([1, 2, 3, 3, 2, 1], map(range(1, 6), {->foldlevel(v:val)}))
+ call assert_equal(2, foldclosed(4))
+ call assert_equal(5, foldclosedend(4))
+
+ " Test zO, zC and zA on a line with no folds.
+ normal zE
+ call assert_fails('normal zO', 'E490:')
+ call assert_fails('normal zC', 'E490:')
+ call assert_fails('normal zA', 'E490:')
+
set fdm&
- enew!
+ bw!
endfunc
" test folding with markers.
func Test_fold_marker()
- enew!
+ new
set fdm=marker fdl=1 fdc=3
let content = ['4 dd {{{', '5 ee {{{ }}}', '6 ff }}}']
@@ -531,13 +660,22 @@ func Test_fold_marker()
normal kYpj
call assert_equal(0, foldlevel('.'))
+ " Use only closing fold marker (without and with a count)
+ set fdl&
+ %d _
+ call setline(1, ['one }}}', 'two'])
+ call assert_equal([0, 0], [foldlevel(1), foldlevel(2)])
+ %d _
+ call setline(1, ['one }}}4', 'two'])
+ call assert_equal([4, 3], [foldlevel(1), foldlevel(2)])
+
set fdm& fdl& fdc&
- enew!
+ bw!
endfunc
" test create fold markers with C filetype
func Test_fold_create_marker_in_C()
- enew!
+ bw!
set fdm=marker fdl=9
set filetype=c
@@ -562,12 +700,12 @@ func Test_fold_create_marker_in_C()
endfor
set fdm& fdl&
- enew!
+ bw!
endfunc
" test folding with indent
func Test_fold_indent()
- enew!
+ new
set fdm=indent sw=2
let content = ['1 aa', '2 bb', '3 cc']
@@ -579,16 +717,14 @@ func Test_fold_indent()
call assert_equal(1, foldlevel('.'))
set fdm& sw&
- enew!
+ bw!
endfunc
" test syntax folding
func Test_fold_syntax()
- if !has('syntax')
- return
- endif
+ CheckFeature syntax
- enew!
+ new
set fdm=syntax fdl=0
syn region Hup start="dd" end="ii" fold contains=Fd1,Fd2,Fd3
@@ -612,7 +748,7 @@ func Test_fold_syntax()
syn clear Fd1 Fd2 Fd3 Hup
set fdm& fdl&
- enew!
+ bw!
endfunc
func Flvl()
@@ -631,7 +767,7 @@ endfun
" test expression folding
func Test_fold_expr()
- enew!
+ new
set fdm=expr fde=Flvl()
let content = ['1 aa',
@@ -659,14 +795,14 @@ func Test_fold_expr()
call assert_equal(0, foldlevel('.'))
set fdm& fde&
- enew!
+ bw!
endfunc
" Bug with fdm=indent and moving folds
" Moving a fold a few times, messes up the folds below the moved fold.
" Fixed by 7.4.700
func Test_fold_move()
- enew!
+ new
set fdm=indent sw=2 fdl=0
let content = ['', '', 'Line1', ' Line2', ' Line3',
@@ -686,24 +822,33 @@ func Test_fold_move()
call assert_equal('+-- 2 lines: Line8', 10->foldtextresult())
set fdm& sw& fdl&
- enew!
+ bw!
endfunc
-func Test_foldtext_recursive()
+" test for patch 7.3.637
+" Cannot catch the error caused by a foldopen when there is no fold.
+func Test_foldopen_exception()
new
- call setline(1, ['{{{', 'some text', '}}}'])
- setlocal foldenable foldmethod=marker foldtext=foldtextresult(v\:foldstart)
- " This was crashing because of endless recursion.
- 2foldclose
- redraw
- call assert_equal(1, foldlevel(2))
- call assert_equal(1, foldclosed(2))
- call assert_equal(3, foldclosedend(2))
- bwipe!
+ let a = 'No error caught'
+ try
+ foldopen
+ catch
+ let a = matchstr(v:exception,'^[^ ]*')
+ endtry
+ call assert_equal('Vim(foldopen):E490:', a)
+
+ let a = 'No error caught'
+ try
+ foobar
+ catch
+ let a = matchstr(v:exception,'^[^ ]*')
+ endtry
+ call assert_match('E492:', a)
+ bw!
endfunc
func Test_fold_last_line_with_pagedown()
- enew!
+ new
set fdm=manual
let expect = '+-- 11 lines: 9---'
@@ -723,13 +868,11 @@ func Test_fold_last_line_with_pagedown()
call assert_equal(expect, ScreenLines(1, len(expect))[0])
set fdm&
- enew!
+ bw!
endfunc
func Test_folds_with_rnu()
- if !CanRunVimInTerminal()
- throw 'Skipped: cannot make screendumps'
- endif
+ CheckScreendump
call writefile([
\ 'set fdm=marker rnu foldcolumn=2',
@@ -816,6 +959,55 @@ func Test_fold_delete_first_line()
set foldmethod&
endfunc
+" Add a test for deleting the outer fold of a nested fold and promoting the
+" inner folds to one level up with already a fold at that level following the
+" nested fold.
+func Test_fold_delete_recursive_fold()
+ new
+ call setline(1, range(1, 7))
+ 2,3fold
+ normal zR
+ 4,5fold
+ normal zR
+ 1,5fold
+ normal zR
+ 6,7fold
+ normal zR
+ normal 1Gzd
+ normal 1Gzj
+ call assert_equal(2, line('.'))
+ normal zj
+ call assert_equal(4, line('.'))
+ normal zj
+ call assert_equal(6, line('.'))
+ bw!
+endfunc
+
+" Test for errors in 'foldexpr'
+func Test_fold_expr_error()
+ new
+ call setline(1, ['one', 'two', 'three'])
+ " In a window with no folds, foldlevel() should return 0
+ call assert_equal(0, foldlevel(1))
+
+ " Return a list from the expression
+ set foldexpr=[]
+ set foldmethod=expr
+ for i in range(3)
+ call assert_equal(0, foldlevel(i))
+ endfor
+
+ " expression error
+ set foldexpr=[{]
+ set foldmethod=expr
+ for i in range(3)
+ call assert_equal(0, foldlevel(i))
+ endfor
+
+ set foldmethod& foldexpr&
+ close!
+endfunc
+
func Test_undo_fold_deletion()
new
set fdm=marker
@@ -899,6 +1091,264 @@ func Test_fold_relative_move()
call assert_equal(2, winline())
set fdm& sw& wrap& tw&
+ bw!
+endfunc
+
+" Test for calling foldlevel() from a fold expression
+let g:FoldLevels = []
+func FoldExpr1(lnum)
+ let f = [a:lnum]
+ for i in range(1, line('$'))
+ call add(f, foldlevel(i))
+ endfor
+ call add(g:FoldLevels, f)
+ return getline(a:lnum)[0] == "\t"
+endfunc
+
+func Test_foldexpr_foldlevel()
+ new
+ call setline(1, ['one', "\ttwo", "\tthree"])
+ setlocal foldmethod=expr
+ setlocal foldexpr=FoldExpr1(v:lnum)
+ setlocal foldenable
+ setlocal foldcolumn=3
+ redraw!
+ call assert_equal([[1, -1, -1, -1], [2, -1, -1, -1], [3, 0, 1, -1]],
+ \ g:FoldLevels)
+ set foldmethod& foldexpr& foldenable& foldcolumn&
+ bw!
+endfunc
+
+" Test for returning different values from a fold expression
+func FoldExpr2(lnum)
+ if a:lnum == 1 || a:lnum == 4
+ return -2
+ elseif a:lnum == 2
+ return 'a1'
+ elseif a:lnum == 3
+ return 's4'
+ endif
+ return '='
+endfunc
+
+func Test_foldexpr_2()
+ new
+ call setline(1, ['one', 'two', 'three', 'four'])
+ setlocal foldexpr=FoldExpr2(v:lnum)
+ setlocal foldmethod=expr
+ call assert_equal([0, 1, 1, 0], [foldlevel(1), foldlevel(2), foldlevel(3),
+ \ foldlevel(4)])
+ bw!
+endfunc
+
+" Test for the 'foldclose' option
+func Test_foldclose_opt()
+ CheckScreendump
+
+ let lines =<< trim END
+ set foldmethod=manual foldclose=all foldopen=all
+ call setline(1, ['one', 'two', 'three', 'four'])
+ 2,3fold
+ func XsaveFoldLevels()
+ redraw!
+ call writefile([json_encode([foldclosed(1), foldclosed(2), foldclosed(3),
+ \ foldclosed(4)])], 'Xoutput', 'a')
+ endfunc
+ END
+ call writefile(lines, 'Xscript')
+ let rows = 10
+ let buf = RunVimInTerminal('-S Xscript', {'rows': rows})
+ call term_wait(buf)
+ call term_sendkeys(buf, ":set noruler\n")
+ call term_wait(buf)
+ call term_sendkeys(buf, ":call XsaveFoldLevels()\n")
+ call term_sendkeys(buf, "2G")
+ call WaitForAssert({-> assert_equal('two', term_getline(buf, 2))})
+ call term_sendkeys(buf, ":call XsaveFoldLevels()\n")
+ call term_sendkeys(buf, "4G")
+ call WaitForAssert({-> assert_equal('four', term_getline(buf, 3))})
+ call term_sendkeys(buf, ":call XsaveFoldLevels()\n")
+ call term_sendkeys(buf, "3G")
+ call WaitForAssert({-> assert_equal('three', term_getline(buf, 3))})
+ call term_sendkeys(buf, ":call XsaveFoldLevels()\n")
+ call term_sendkeys(buf, "1G")
+ call WaitForAssert({-> assert_equal('four', term_getline(buf, 3))})
+ call term_sendkeys(buf, ":call XsaveFoldLevels()\n")
+ call term_sendkeys(buf, "2G")
+ call WaitForAssert({-> assert_equal('two', term_getline(buf, 2))})
+ call term_sendkeys(buf, "k")
+ call WaitForAssert({-> assert_equal('four', term_getline(buf, 3))})
+
+ " clean up
+ call StopVimInTerminal(buf)
+
+ call assert_equal(['[-1,2,2,-1]', '[-1,-1,-1,-1]', '[-1,2,2,-1]',
+ \ '[-1,-1,-1,-1]', '[-1,2,2,-1]'], readfile('Xoutput'))
+ call delete('Xscript')
+ call delete('Xoutput')
+endfunc
+
+" Test for foldtextresult()
+func Test_foldtextresult()
+ new
+ call assert_equal('', foldtextresult(-1))
+ call assert_equal('', foldtextresult(0))
+ call assert_equal('', foldtextresult(1))
+ call setline(1, ['one', 'two', 'three', 'four'])
+ 2,3fold
+ call assert_equal('', foldtextresult(1))
+ call assert_equal('+-- 2 lines: two', foldtextresult(2))
+ setlocal foldtext=
+ call assert_equal('+-- 2 lines folded ', foldtextresult(2))
+
+ " Fold text for a C comment fold
+ %d _
+ setlocal foldtext&
+ call setline(1, ['', '/*', ' * Comment', ' */', ''])
+ 2,4fold
+ call assert_equal('+-- 3 lines: Comment', foldtextresult(2))
+
+ bw!
+endfunc
+
+" Test for merging two recursive folds when an intermediate line with no fold
+" is removed
+func Test_fold_merge_recursive()
+ new
+ call setline(1, [' one', ' two', 'xxxx', ' three',
+ \ ' four', "\tfive"])
+ setlocal foldmethod=indent shiftwidth=2
+ 3d_
+ %foldclose
+ call assert_equal([1, 5], [foldclosed(5), foldclosedend(1)])
+ bw!
+endfunc
+
+" Test for moving a line which is the start of a fold from a recursive fold to
+" outside. The fold length should reduce.
+func Test_fold_move_foldlevel()
+ new
+ call setline(1, ['a{{{', 'b{{{', 'c{{{', 'd}}}', 'e}}}', 'f}}}', 'g'])
+ setlocal foldmethod=marker
+ normal zR
+ call assert_equal([3, 2, 1], [foldlevel(4), foldlevel(5), foldlevel(6)])
+ 3move 7
+ call assert_equal([2, 1, 0], [foldlevel(3), foldlevel(4), foldlevel(5)])
+ call assert_equal(1, foldlevel(7))
+
+ " Move a line from outside a fold to inside the fold.
+ %d _
+ call setline(1, ['a', 'b{{{', 'c}}}'])
+ normal zR
+ 1move 2
+ call assert_equal([1, 1, 1], [foldlevel(1), foldlevel(2), foldlevel(3)])
+
+ " Move the start of one fold to inside another fold
+ %d _
+ call setline(1, ['a', 'b{{{', 'c}}}', 'd{{{', 'e}}}'])
+ normal zR
+ call assert_equal([0, 1, 1, 1, 1], [foldlevel(1), foldlevel(2),
+ \ foldlevel(3), foldlevel(4), foldlevel(5)])
+ 1,2move 4
+ call assert_equal([0, 1, 1, 2, 2], [foldlevel(1), foldlevel(2),
+ \ foldlevel(3), foldlevel(4), foldlevel(5)])
+
+ bw!
+endfunc
+
+" Test for using zj and zk to move downwards and upwards to the start and end
+" of the next fold.
+" Test for using [z and ]z in a closed fold to jump to the beginning and end
+" of the fold.
+func Test_fold_jump()
+ new
+ call setline(1, ["\t1", "\t2", "\t\t3", "\t\t4", "\t\t\t5", "\t\t\t6", "\t\t7", "\t\t8", "\t9", "\t10"])
+ setlocal foldmethod=indent
+ normal zR
+ normal zj
+ call assert_equal(3, line('.'))
+ normal zj
+ call assert_equal(5, line('.'))
+ call assert_beeps('normal zj')
+ call assert_equal(5, line('.'))
+ call assert_beeps('normal 9Gzj')
+ call assert_equal(9, line('.'))
+ normal Gzk
+ call assert_equal(8, line('.'))
+ normal zk
+ call assert_equal(6, line('.'))
+ call assert_beeps('normal zk')
+ call assert_equal(6, line('.'))
+ call assert_beeps('normal 2Gzk')
+ call assert_equal(2, line('.'))
+
+ " Using [z or ]z in a closed fold should not move the cursor
+ %d _
+ call setline(1, ["1", "\t2", "\t3", "\t4", "\t5", "\t6", "7"])
+ normal zR4Gzc
+ call assert_equal(4, line('.'))
+ call assert_beeps('normal [z')
+ call assert_equal(4, line('.'))
+ call assert_beeps('normal ]z')
+ call assert_equal(4, line('.'))
+ bw!
+endfunc
+
+" Test for using a script-local function for 'foldexpr'
+func Test_foldexpr_scriptlocal_func()
+ func! s:FoldFunc()
+ let g:FoldLnum = v:lnum
+ endfunc
+ new | only
+ call setline(1, 'abc')
+ let g:FoldLnum = 0
+ set foldmethod=expr foldexpr=s:FoldFunc()
+ redraw!
+ call assert_equal(expand('<SID>') .. 'FoldFunc()', &foldexpr)
+ call assert_equal(1, g:FoldLnum)
+ set foldmethod& foldexpr=
+ bw!
+ new | only
+ call setline(1, 'abc')
+ let g:FoldLnum = 0
+ set foldmethod=expr foldexpr=<SID>FoldFunc()
+ redraw!
+ call assert_equal(expand('<SID>') .. 'FoldFunc()', &foldexpr)
+ call assert_equal(1, g:FoldLnum)
+ set foldmethod& foldexpr=
+ delfunc s:FoldFunc
+ bw!
+endfunc
+
+" Test for using a script-local function for 'foldtext'
+func Test_foldtext_scriptlocal_func()
+ func! s:FoldText()
+ let g:FoldTextArgs = [v:foldstart, v:foldend]
+ return foldtext()
+ endfunc
+ new | only
+ call setline(1, range(50))
+ let g:FoldTextArgs = []
+ set foldmethod=manual
+ set foldtext=s:FoldText()
+ norm! 4Gzf4j
+ redraw!
+ call assert_equal(expand('<SID>') .. 'FoldText()', &foldtext)
+ call assert_equal([4, 8], g:FoldTextArgs)
+ set foldtext&
+ bw!
+ new | only
+ call setline(1, range(50))
+ let g:FoldTextArgs = []
+ set foldmethod=manual
+ set foldtext=<SID>FoldText()
+ norm! 8Gzf4j
+ redraw!
+ call assert_equal(expand('<SID>') .. 'FoldText()', &foldtext)
+ call assert_equal([8, 12], g:FoldTextArgs)
+ set foldtext&
+ bw!
+ delfunc s:FoldText
endfunc
" Make sure a fold containing a nested fold is split correctly when using
@@ -989,4 +1439,61 @@ func Test_indent_append_blank_small_fold_close()
bw!
endfunc
+func Test_sort_closed_fold()
+ CheckExecutable sort
+
+ call setline(1, [
+ \ 'Section 1',
+ \ ' how',
+ \ ' now',
+ \ ' brown',
+ \ ' cow',
+ \ 'Section 2',
+ \ ' how',
+ \ ' now',
+ \ ' brown',
+ \ ' cow',
+ \])
+ setlocal foldmethod=indent sw=3
+ normal 2G
+
+ " The "!!" expands to ".,.+3" and must only sort four lines
+ call feedkeys("!!sort\<CR>", 'xt')
+ call assert_equal([
+ \ 'Section 1',
+ \ ' brown',
+ \ ' cow',
+ \ ' how',
+ \ ' now',
+ \ 'Section 2',
+ \ ' how',
+ \ ' now',
+ \ ' brown',
+ \ ' cow',
+ \ ], getline(1, 10))
+
+ bwipe!
+endfunc
+
+func Test_indent_with_L_command()
+ " The "L" command moved the cursor to line zero, causing the text saved for
+ " undo to use line number -1, which caused trouble for undo later.
+ new
+ sil! norm 8R V{zf8=Lu
+ bwipe!
+endfunc
+
+" Make sure that when there is a fold at the bottom of the buffer and a newline
+" character is appended to the line, the fold gets expanded (instead of the new
+" line not being part of the fold).
+func Test_expand_fold_at_bottom_of_buffer()
+ new
+ " create a fold on the only line
+ fold
+ execute "normal A\<CR>"
+ call assert_equal([1, 1], range(1, 2)->map('foldlevel(v:val)'))
+
+ bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index 44b6f0373e..500c30c76b 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -2,6 +2,9 @@
source shared.vim
source check.vim
+source term_util.vim
+source screendump.vim
+source vim9.vim
" Must be done first, since the alternate buffer must be unset.
func Test_00_bufexists()
@@ -19,6 +22,27 @@ func Test_00_bufexists()
call assert_equal(0, bufexists('Xfoo'))
endfunc
+func Test_has()
+ throw 'Skipped: Nvim has removed some features'
+ call assert_equal(1, has('eval'))
+ call assert_equal(1, has('eval', 1))
+
+ if has('unix')
+ call assert_equal(1, or(has('ttyin'), 1))
+ call assert_equal(0, and(has('ttyout'), 0))
+ call assert_equal(1, has('multi_byte_encoding'))
+ endif
+ call assert_equal(1, has('vcon', 1))
+ call assert_equal(1, has('mouse_gpm_enabled', 1))
+
+ call assert_equal(0, has('nonexistent'))
+ call assert_equal(0, has('nonexistent', 1))
+
+ " Will we ever have patch 9999?
+ let ver = 'patch-' .. v:version / 100 .. '.' .. v:version % 100 .. '.9999'
+ call assert_equal(0, has(ver))
+endfunc
+
func Test_empty()
call assert_equal(1, empty(''))
call assert_equal(0, empty('a'))
@@ -67,9 +91,11 @@ func Test_len()
call assert_equal(2, len('ab'))
call assert_equal(0, len([]))
+ call assert_equal(0, len(v:_null_list))
call assert_equal(2, len([2, 1]))
call assert_equal(0, len({}))
+ call assert_equal(0, len(v:_null_dict))
call assert_equal(2, len({'a': 1, 'b': 2}))
" call assert_fails('call len(v:none)', 'E701:')
@@ -87,6 +113,10 @@ func Test_max()
call assert_fails('call max(1)', 'E712:')
" call assert_fails('call max(v:none)', 'E712:')
+
+ " check we only get one error
+ call assert_fails('call max([#{}, [1]])', ['E728:', 'E728:'])
+ call assert_fails('call max(#{a: {}, b: [1]})', ['E728:', 'E728:'])
endfunc
func Test_min()
@@ -100,6 +130,11 @@ func Test_min()
call assert_fails('call min(1)', 'E712:')
" call assert_fails('call min(v:none)', 'E712:')
+ call assert_fails('call min([1, {}])', 'E728:')
+
+ " check we only get one error
+ call assert_fails('call min([[1], #{}])', ['E745:', 'E745:'])
+ call assert_fails('call min(#{a: [1], b: #{}})', ['E745:', 'E745:'])
endfunc
func Test_strwidth()
@@ -188,7 +223,7 @@ func Test_str2nr()
if has('float')
call assert_fails('call str2nr(1.2)', 'E806:')
endif
- call assert_fails('call str2nr(10, [])', 'E474:')
+ call assert_fails('call str2nr(10, [])', 'E745:')
endfunc
func Test_strftime()
@@ -381,6 +416,8 @@ func Test_strpart()
call assert_equal('abcdefg', 'abcdefg'->strpart(-2))
call assert_equal('fg', strpart('abcdefg', 5, 4))
call assert_equal('defg', strpart('abcdefg', 3))
+ call assert_equal('', strpart('abcdefg', 10))
+ call assert_fails("let s=strpart('abcdef', [])", 'E745:')
call assert_equal('lép', strpart('éléphant', 2, 4))
call assert_equal('léphant', strpart('éléphant', 2))
@@ -462,6 +499,12 @@ func Test_tolower()
" invalid memory.
call tolower("\xC0\x80\xC0")
call tolower("123\xC0\x80\xC0")
+
+ " Test in latin1 encoding
+ let save_enc = &encoding
+ " set encoding=latin1
+ call assert_equal("abc", tolower("ABC"))
+ let &encoding = save_enc
endfunc
func Test_toupper()
@@ -533,11 +576,27 @@ func Test_toupper()
" invalid memory.
call toupper("\xC0\x80\xC0")
call toupper("123\xC0\x80\xC0")
+
+ " Test in latin1 encoding
+ let save_enc = &encoding
+ " set encoding=latin1
+ call assert_equal("ABC", toupper("abc"))
+ let &encoding = save_enc
endfunc
func Test_tr()
call assert_equal('foo', tr('bar', 'bar', 'foo'))
call assert_equal('zxy', 'cab'->tr('abc', 'xyz'))
+ call assert_fails("let s=tr([], 'abc', 'def')", 'E730:')
+ call assert_fails("let s=tr('abc', [], 'def')", 'E730:')
+ call assert_fails("let s=tr('abc', 'abc', [])", 'E730:')
+ call assert_fails("let s=tr('abcd', 'abcd', 'def')", 'E475:')
+ " set encoding=latin1
+ call assert_fails("let s=tr('abcd', 'abcd', 'def')", 'E475:')
+ call assert_equal('hEllO', tr('hello', 'eo', 'EO'))
+ call assert_equal('hello', tr('hello', 'xy', 'ab'))
+ call assert_fails('call tr("abc", "123", "₁₂")', 'E475:')
+ set encoding=utf8
endfunc
" Tests for the mode() function
@@ -752,13 +811,41 @@ func Test_mode()
delfunction OperatorFunc
endfunc
+" Test for append()
func Test_append()
enew!
split
call append(0, ["foo"])
+ call append(1, [])
+ call append(1, v:_null_list)
+ call assert_equal(['foo', ''], getline(1, '$'))
split
only
undo
+ undo
+
+ " Using $ instead of '$' must give an error
+ call assert_fails("call append($, 'foobar')", 'E116:')
+
+ call assert_fails("call append({}, '')", ['E728:', 'E728:'])
+endfunc
+
+" Test for setline()
+func Test_setline()
+ new
+ call setline(0, ["foo"])
+ call setline(0, [])
+ call setline(0, v:_null_list)
+ call setline(1, ["bar"])
+ call setline(1, [])
+ call setline(1, v:_null_list)
+ call setline(2, [])
+ call setline(2, v:_null_list)
+ call setline(3, [])
+ call setline(3, v:_null_list)
+ call setline(2, ["baz"])
+ call assert_equal(['bar', 'baz'], getline(1, '$'))
+ close!
endfunc
func Test_getbufvar()
@@ -792,6 +879,10 @@ func Test_getbufvar()
call assert_equal(0, getbufvar(bnr, '&autoindent'))
call assert_equal(0, getbufvar(bnr, '&autoindent', 1))
+ " Set and get a buffer-local variable
+ call setbufvar(bnr, 'bufvar_test', ['one', 'two'])
+ call assert_equal(['one', 'two'], getbufvar(bnr, 'bufvar_test'))
+
" Open new window with forced option values
set fileformats=unix,dos
new ++ff=dos ++bin ++enc=iso-8859-2
@@ -830,6 +921,8 @@ func Test_stridx()
call assert_equal(-1, stridx('hello', 'l', 10))
call assert_equal(2, stridx('hello', 'll'))
call assert_equal(-1, stridx('hello', 'hello world'))
+ call assert_fails("let n=stridx('hello', [])", 'E730:')
+ call assert_fails("let n=stridx([], 'l')", 'E730:')
endfunc
func Test_strridx()
@@ -846,6 +939,8 @@ func Test_strridx()
call assert_equal(-1, strridx('hello', 'l', -1))
call assert_equal(2, strridx('hello', 'll'))
call assert_equal(-1, strridx('hello', 'hello world'))
+ call assert_fails("let n=strridx('hello', [])", 'E730:')
+ call assert_fails("let n=strridx([], 'l')", 'E730:')
endfunc
func Test_match_func()
@@ -855,6 +950,13 @@ func Test_match_func()
call assert_equal(-1, match('testing', 'ing', 8))
call assert_equal(1, match(['vim', 'testing', 'execute'], 'ing'))
call assert_equal(-1, match(['vim', 'testing', 'execute'], 'img'))
+ call assert_fails("let x=match('vim', [])", 'E730:')
+ call assert_equal(3, match(['a', 'b', 'c', 'a'], 'a', 1))
+ call assert_equal(-1, match(['a', 'b', 'c', 'a'], 'a', 5))
+ call assert_equal(4, match('testing', 'ing', -1))
+ call assert_fails("let x=match('testing', 'ing', 0, [])", 'E745:')
+ call assert_equal(-1, match(v:_null_list, 2))
+ call assert_equal(-1, match('abc', '\\%('))
endfunc
func Test_matchend()
@@ -961,6 +1063,7 @@ func Test_byte2line_line2byte()
bw!
endfunc
+" Test for byteidx() and byteidxcomp() functions
func Test_byteidx()
let a = '.é.' " one char of two bytes
call assert_equal(0, byteidx(a, 0))
@@ -980,6 +1083,7 @@ func Test_byteidx()
call assert_equal(4, b->byteidx(2))
call assert_equal(5, b->byteidx(3))
call assert_equal(-1, b->byteidx(4))
+ call assert_fails("call byteidx([], 0)", 'E730:')
call assert_equal(0, b->byteidxcomp(0))
call assert_equal(1, b->byteidxcomp(1))
@@ -987,6 +1091,7 @@ func Test_byteidx()
call assert_equal(4, b->byteidxcomp(3))
call assert_equal(5, b->byteidxcomp(4))
call assert_equal(-1, b->byteidxcomp(5))
+ call assert_fails("call byteidxcomp([], 0)", 'E730:')
endfunc
" Test for charidx()
@@ -997,7 +1102,9 @@ func Test_charidx()
call assert_equal(2, charidx(a, 4))
call assert_equal(3, charidx(a, 7))
call assert_equal(-1, charidx(a, 8))
+ call assert_equal(-1, charidx(a, -1))
call assert_equal(-1, charidx('', 0))
+ call assert_equal(-1, charidx(v:_null_string, 0))
" count composing characters
call assert_equal(0, charidx(a, 0, 1))
@@ -1010,8 +1117,8 @@ func Test_charidx()
call assert_fails('let x = charidx([], 1)', 'E474:')
call assert_fails('let x = charidx("abc", [])', 'E474:')
call assert_fails('let x = charidx("abc", 1, [])', 'E474:')
- call assert_fails('let x = charidx("abc", 1, -1)', 'E474:')
- call assert_fails('let x = charidx("abc", 1, 2)', 'E474:')
+ call assert_fails('let x = charidx("abc", 1, -1)', 'E1023:')
+ call assert_fails('let x = charidx("abc", 1, 2)', 'E1023:')
endfunc
func Test_count()
@@ -1096,6 +1203,10 @@ func Test_filewritable()
call assert_equal(0, filewritable('doesnotexist'))
+ call mkdir('Xdir')
+ call assert_equal(2, filewritable('Xdir'))
+ call delete('Xdir', 'd')
+
call delete('Xfilewritable')
bw!
endfunc
@@ -1179,6 +1290,7 @@ func Test_col()
norm gg4|mx6|mY2|
call assert_equal(2, col('.'))
call assert_equal(7, col('$'))
+ call assert_equal(2, col('v'))
call assert_equal(4, col("'x"))
call assert_equal(6, col("'Y"))
call assert_equal(2, [1, 2]->col())
@@ -1189,6 +1301,47 @@ func Test_col()
call assert_equal(0, col([2, '$']))
call assert_equal(0, col([1, 100]))
call assert_equal(0, col([1]))
+ call assert_equal(0, col(v:_null_list))
+ call assert_fails('let c = col({})', 'E1222:')
+ call assert_fails('let c = col(".", [])', 'E1210:')
+
+ " test for getting the visual start column
+ func T()
+ let g:Vcol = col('v')
+ return ''
+ endfunc
+ let g:Vcol = 0
+ xmap <expr> <F2> T()
+ exe "normal gg3|ve\<F2>"
+ call assert_equal(3, g:Vcol)
+ xunmap <F2>
+ delfunc T
+
+ " Test for the visual line start and end marks '< and '>
+ call setline(1, ['one', 'one two', 'one two three'])
+ "normal! ggVG
+ call feedkeys("ggVG\<Esc>", 'xt')
+ call assert_equal(1, col("'<"))
+ call assert_equal(14, col("'>"))
+ " Delete the last line of the visually selected region
+ $d
+ call assert_notequal(14, col("'>"))
+
+ " Test with 'virtualedit'
+ set virtualedit=all
+ call cursor(1, 10)
+ call assert_equal(4, col('.'))
+ set virtualedit&
+
+ " Test for getting the column number in another window
+ let winid = win_getid()
+ new
+ call win_execute(winid, 'normal 1G$')
+ call assert_equal(3, col('.', winid))
+ call win_execute(winid, 'normal 2G')
+ call assert_equal(8, col('$', winid))
+ call assert_equal(0, col('.', 5001))
+
bw!
endfunc
@@ -1232,12 +1385,15 @@ 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)
+ if has('gui_running')
+ call assert_fails('let v=inputdialog([], "xx")', 'E730:')
+ call assert_fails('let v=inputdialog("Q", [])', 'E730:')
+ else
+ 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)
+ endif
endfunc
" Test for inputlist()
@@ -1270,26 +1426,39 @@ func Test_inputlist()
call assert_equal(2, c)
" Use mouse to make a selection
- " call test_setmouse(&lines - 3, 2)
- call nvim_input_mouse('left', 'press', '', 0, &lines - 4, 1) " set mouse position
- call getchar() " discard mouse event but keep mouse position
+ call Ntest_setmouse(&lines - 3, 2)
call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>\<LeftMouse>", 'tx')
call assert_equal(1, c)
" Mouse click outside of the list
- " call test_setmouse(&lines - 6, 2)
- call nvim_input_mouse('left', 'press', '', 0, &lines - 7, 1) " set mouse position
- call getchar() " discard mouse event but keep mouse position
+ call Ntest_setmouse(&lines - 6, 2)
call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>\<LeftMouse>", 'tx')
call assert_equal(-2, c)
call assert_fails('call inputlist("")', 'E686:')
endfunc
+func Test_range_inputlist()
+ " flush out any garbage left in the buffer
+ while getchar(0)
+ endwhile
+
+ call feedkeys(":let result = inputlist(range(10))\<CR>1\<CR>", 'x')
+ call assert_equal(1, result)
+ call feedkeys(":let result = inputlist(range(3, 10))\<CR>1\<CR>", 'x')
+ call assert_equal(1, result)
+
+ unlet result
+endfunc
+
func Test_balloon_show()
CheckFeature balloon_eval
" This won't do anything but must not crash either.
call balloon_show('hi!')
+ if !has('gui_running')
+ call balloon_show(range(3))
+ call balloon_show([])
+ endif
endfunc
func Test_shellescape()
@@ -1420,12 +1589,15 @@ func Test_trim()
call assert_equal("vim", trim(" vim ", " ", 0))
call assert_equal("vim ", trim(" vim ", " ", 1))
call assert_equal(" vim", trim(" vim ", " ", 2))
- call assert_fails('call trim(" vim ", " ", [])', 'E745:')
- call assert_fails('call trim(" vim ", " ", -1)', 'E475:')
- call assert_fails('call trim(" vim ", " ", 3)', 'E475:')
+ call assert_fails('eval trim(" vim ", " ", [])', 'E745:')
+ call assert_fails('eval trim(" vim ", " ", -1)', 'E475:')
+ call assert_fails('eval trim(" vim ", " ", 3)', 'E475:')
+ call assert_fails('eval trim(" vim ", 0)', 'E475:')
let chars = join(map(range(1, 0x20) + [0xa0], {n -> n->nr2char()}), '')
call assert_equal("x", trim(chars . "x" . chars))
+
+ call assert_fails('let c=trim([])', 'E730:')
endfunc
" Test for reg_recording() and reg_executing()
@@ -1534,13 +1706,12 @@ func Test_getchar()
call assert_equal(0, getchar(0))
call setline(1, 'xxxx')
- " call test_setmouse(1, 3)
- " let v:mouse_win = 9
- " let v:mouse_winid = 9
- " let v:mouse_lnum = 9
- " let v:mouse_col = 9
- " call feedkeys("\<S-LeftMouse>", '')
- call nvim_input_mouse('left', 'press', 'S', 0, 0, 2)
+ call Ntest_setmouse(1, 3)
+ let v:mouse_win = 9
+ let v:mouse_winid = 9
+ let v:mouse_lnum = 9
+ let v:mouse_col = 9
+ call feedkeys("\<S-LeftMouse>", '')
call assert_equal("\<S-LeftMouse>", getchar())
call assert_equal(1, v:mouse_win)
call assert_equal(win_getid(1), v:mouse_winid)
@@ -1598,11 +1769,11 @@ func Test_libcall_libcallnr()
call assert_equal(4, 'abcd'->libcallnr(libc, 'strlen'))
call assert_equal(char2nr('A'), char2nr('a')->libcallnr(libc, 'toupper'))
- call assert_fails("call libcall(libc, 'Xdoesnotexist_', '')", 'E364:')
- call assert_fails("call libcallnr(libc, 'Xdoesnotexist_', '')", 'E364:')
+ call assert_fails("call libcall(libc, 'Xdoesnotexist_', '')", ['', 'E364:'])
+ call assert_fails("call libcallnr(libc, 'Xdoesnotexist_', '')", ['', 'E364:'])
- call assert_fails("call libcall('Xdoesnotexist_', 'getenv', 'HOME')", 'E364:')
- call assert_fails("call libcallnr('Xdoesnotexist_', 'strlen', 'abcd')", 'E364:')
+ call assert_fails("call libcall('Xdoesnotexist_', 'getenv', 'HOME')", ['', 'E364:'])
+ call assert_fails("call libcallnr('Xdoesnotexist_', 'strlen', 'abcd')", ['', 'E364:'])
endfunc
sandbox function Fsandbox()
@@ -1619,6 +1790,10 @@ func Test_func_sandbox()
call assert_fails('call Fsandbox()', 'E48:')
delfunc Fsandbox
+
+ " From a sandbox try to set a predefined variable (which cannot be modified
+ " from a sandbox)
+ call assert_fails('sandbox let v:lnum = 10', 'E794:')
endfunc
func EditAnotherFile()
@@ -1697,6 +1872,62 @@ func Test_platform_name()
endif
endfunc
+" Test confirm({msg} [, {choices} [, {default} [, {type}]]])
+func Test_confirm()
+ " requires a UI to be active
+ throw 'Skipped: use test/functional/vimscript/input_spec.lua'
+ CheckUnix
+ CheckNotGui
+
+ call feedkeys('o', 'L')
+ let a = confirm('Press O to proceed')
+ call assert_equal(1, a)
+
+ call feedkeys('y', 'L')
+ let a = 'Are you sure?'->confirm("&Yes\n&No")
+ call assert_equal(1, a)
+
+ call feedkeys('n', 'L')
+ let a = confirm('Are you sure?', "&Yes\n&No")
+ call assert_equal(2, a)
+
+ " confirm() should return 0 when pressing CTRL-C.
+ call feedkeys("\<C-C>", 'L')
+ let a = confirm('Are you sure?', "&Yes\n&No")
+ call assert_equal(0, a)
+
+ " <Esc> requires another character to avoid it being seen as the start of an
+ " escape sequence. Zero should be harmless.
+ eval "\<Esc>0"->feedkeys('L')
+ let a = confirm('Are you sure?', "&Yes\n&No")
+ call assert_equal(0, a)
+
+ " Default choice is returned when pressing <CR>.
+ call feedkeys("\<CR>", 'L')
+ let a = confirm('Are you sure?', "&Yes\n&No")
+ call assert_equal(1, a)
+
+ call feedkeys("\<CR>", 'L')
+ let a = confirm('Are you sure?', "&Yes\n&No", 2)
+ call assert_equal(2, a)
+
+ call feedkeys("\<CR>", 'L')
+ let a = confirm('Are you sure?', "&Yes\n&No", 0)
+ call assert_equal(0, a)
+
+ " Test with the {type} 4th argument
+ for type in ['Error', 'Question', 'Info', 'Warning', 'Generic']
+ call feedkeys('y', 'L')
+ let a = confirm('Are you sure?', "&Yes\n&No\n", 1, type)
+ call assert_equal(1, a)
+ endfor
+
+ call assert_fails('call confirm([])', 'E730:')
+ call assert_fails('call confirm("Are you sure?", [])', 'E730:')
+ call assert_fails('call confirm("Are you sure?", "&Yes\n&No\n", [])', 'E745:')
+ call assert_fails('call confirm("Are you sure?", "&Yes\n&No\n", 0, [])', 'E730:')
+endfunc
+
func Test_readdir()
call mkdir('Xdir')
call writefile([], 'Xdir/foo.txt')
@@ -1724,7 +1955,7 @@ func Test_readdir()
let files = readdir('Xdir', {x -> len(add(l, x)) == 2 ? -1 : 1})
call assert_equal(1, len(files))
- call delete('Xdir', 'rf')
+ eval 'Xdir'->delete('rf')
endfunc
func Test_delete_rf()
@@ -1756,6 +1987,7 @@ func Test_call()
call assert_equal(3, 'len'->call([123]))
call assert_fails("call call('len', 123)", 'E714:')
call assert_equal(0, call('', []))
+ call assert_equal(0, call('len', v:_null_list))
function Mylen() dict
return len(self.data)
@@ -1763,10 +1995,15 @@ func Test_call()
let mydict = {'data': [0, 1, 2, 3], 'len': function("Mylen")}
eval mydict.len->call([], mydict)->assert_equal(4)
call assert_fails("call call('Mylen', [], 0)", 'E715:')
+ call assert_fails('call foo', 'E107:')
endfunc
func Test_char2nr()
call assert_equal(12354, char2nr('あ', 1))
+ call assert_equal(120, 'x'->char2nr())
+ " set encoding=latin1
+ call assert_equal(120, 'x'->char2nr())
+ set encoding=utf-8
endfunc
func Test_charclass()
@@ -1819,17 +2056,329 @@ func Test_bufadd_bufload()
exe 'bwipe ' .. buf2
call assert_equal(0, bufexists(buf2))
+ " When 'buftype' is "nofile" then bufload() does not read the file.
+ " Other values too.
+ for val in [['nofile', 0],
+ \ ['nowrite', 1],
+ \ ['acwrite', 1],
+ \ ['quickfix', 0],
+ \ ['help', 1],
+ "\ ['terminal', 0],
+ \ ['prompt', 0],
+ "\ ['popup', 0],
+ \ ]
+ bwipe! XotherName
+ let buf = bufadd('XotherName')
+ call setbufvar(buf, '&bt', val[0])
+ call bufload(buf)
+ call assert_equal(val[1] ? ['some', 'text'] : [''], getbufline(buf, 1, '$'), val[0])
+ endfor
+
bwipe someName
bwipe XotherName
call assert_equal(0, bufexists('someName'))
call delete('XotherName')
endfunc
+func Test_range()
+ " destructuring
+ let [x, y] = range(2)
+ call assert_equal([0, 1], [x, y])
+
+ " index
+ call assert_equal(4, range(1, 10)[3])
+
+ " add()
+ call assert_equal([0, 1, 2, 3], add(range(3), 3))
+ call assert_equal([0, 1, 2, [0, 1, 2]], add([0, 1, 2], range(3)))
+ call assert_equal([0, 1, 2, [0, 1, 2]], add(range(3), range(3)))
+
+ " append()
+ new
+ call append('.', range(5))
+ call assert_equal(['', '0', '1', '2', '3', '4'], getline(1, '$'))
+ bwipe!
+
+ " appendbufline()
+ new
+ call appendbufline(bufnr(''), '.', range(5))
+ call assert_equal(['0', '1', '2', '3', '4', ''], getline(1, '$'))
+ bwipe!
+
+ " call()
+ func TwoArgs(a, b)
+ return [a:a, a:b]
+ endfunc
+ call assert_equal([0, 1], call('TwoArgs', range(2)))
+
+ " col()
+ new
+ call setline(1, ['foo', 'bar'])
+ call assert_equal(2, col(range(1, 2)))
+ bwipe!
+
+ " complete()
+ execute "normal! a\<C-r>=[complete(col('.'), range(10)), ''][1]\<CR>"
+ " complete_info()
+ execute "normal! a\<C-r>=[complete(col('.'), range(10)), ''][1]\<CR>\<C-r>=[complete_info(range(5)), ''][1]\<CR>"
+
+ " copy()
+ call assert_equal([1, 2, 3], copy(range(1, 3)))
+
+ " count()
+ call assert_equal(0, count(range(0), 3))
+ call assert_equal(0, count(range(2), 3))
+ call assert_equal(1, count(range(5), 3))
+
+ " cursor()
+ new
+ call setline(1, ['aaa', 'bbb', 'ccc'])
+ call cursor(range(1, 2))
+ call assert_equal([2, 1], [col('.'), line('.')])
+ bwipe!
+
+ " deepcopy()
+ call assert_equal([1, 2, 3], deepcopy(range(1, 3)))
+
+ " empty()
+ call assert_true(empty(range(0)))
+ call assert_false(empty(range(2)))
+
+ " execute()
+ new
+ call setline(1, ['aaa', 'bbb', 'ccc'])
+ call execute(range(3))
+ call assert_equal(2, line('.'))
+ bwipe!
+
+ " extend()
+ call assert_equal([1, 2, 3, 4], extend([1], range(2, 4)))
+ call assert_equal([1, 2, 3, 4], extend(range(1, 1), range(2, 4)))
+ call assert_equal([1, 2, 3, 4], extend(range(1, 1), [2, 3, 4]))
+
+ " filter()
+ call assert_equal([1, 3], filter(range(5), 'v:val % 2'))
+
+ " funcref()
+ call assert_equal([0, 1], funcref('TwoArgs', range(2))())
+
+ " function()
+ call assert_equal([0, 1], function('TwoArgs', range(2))())
+
+ " garbagecollect()
+ let thelist = [1, range(2), 3]
+ let otherlist = range(3)
+ call test_garbagecollect_now()
+
+ " get()
+ call assert_equal(4, get(range(1, 10), 3))
+ call assert_equal(-1, get(range(1, 10), 42, -1))
+
+ " index()
+ call assert_equal(1, index(range(1, 5), 2))
+ call assert_fails("echo index([1, 2], 1, [])", 'E745:')
+
+ " insert()
+ call assert_equal([42, 1, 2, 3, 4, 5], insert(range(1, 5), 42))
+ call assert_equal([42, 1, 2, 3, 4, 5], insert(range(1, 5), 42, 0))
+ call assert_equal([1, 42, 2, 3, 4, 5], insert(range(1, 5), 42, 1))
+ call assert_equal([1, 2, 3, 4, 42, 5], insert(range(1, 5), 42, 4))
+ call assert_equal([1, 2, 3, 4, 42, 5], insert(range(1, 5), 42, -1))
+ call assert_equal([1, 2, 3, 4, 5, 42], insert(range(1, 5), 42, 5))
+
+ " join()
+ call assert_equal('0 1 2 3 4', join(range(5)))
+
+ " json_encode()
+ " call assert_equal('[0,1,2,3]', json_encode(range(4)))
+ call assert_equal('[0, 1, 2, 3]', json_encode(range(4)))
+
+ " len()
+ call assert_equal(0, len(range(0)))
+ call assert_equal(2, len(range(2)))
+ call assert_equal(5, len(range(0, 12, 3)))
+ call assert_equal(4, len(range(3, 0, -1)))
+
+ " list2str()
+ call assert_equal('ABC', list2str(range(65, 67)))
+ call assert_fails('let s = list2str(5)', 'E474:')
+
+ " lock()
+ let thelist = range(5)
+ lockvar thelist
+
+ " map()
+ call assert_equal([0, 2, 4, 6, 8], map(range(5), 'v:val * 2'))
+
+ " match()
+ call assert_equal(3, match(range(5), 3))
+
+ " matchaddpos()
+ highlight MyGreenGroup ctermbg=green guibg=green
+ call matchaddpos('MyGreenGroup', range(line('.'), line('.')))
+
+ " matchend()
+ call assert_equal(4, matchend(range(5), '4'))
+ call assert_equal(3, matchend(range(1, 5), '4'))
+ call assert_equal(-1, matchend(range(1, 5), '42'))
+
+ " matchstrpos()
+ call assert_equal(['4', 4, 0, 1], matchstrpos(range(5), '4'))
+ call assert_equal(['4', 3, 0, 1], matchstrpos(range(1, 5), '4'))
+ call assert_equal(['', -1, -1, -1], matchstrpos(range(1, 5), '42'))
+
+ " max() reverse()
+ call assert_equal(0, max(range(0)))
+ call assert_equal(0, max(range(10, 9)))
+ call assert_equal(9, max(range(10)))
+ call assert_equal(18, max(range(0, 20, 3)))
+ call assert_equal(20, max(range(20, 0, -3)))
+ call assert_equal(99999, max(range(100000)))
+ call assert_equal(99999, max(range(99999, 0, -1)))
+ call assert_equal(99999, max(reverse(range(100000))))
+ call assert_equal(99999, max(reverse(range(99999, 0, -1))))
+
+ " min() reverse()
+ call assert_equal(0, min(range(0)))
+ call assert_equal(0, min(range(10, 9)))
+ call assert_equal(5, min(range(5, 10)))
+ call assert_equal(5, min(range(5, 10, 3)))
+ call assert_equal(2, min(range(20, 0, -3)))
+ call assert_equal(0, min(range(100000)))
+ call assert_equal(0, min(range(99999, 0, -1)))
+ call assert_equal(0, min(reverse(range(100000))))
+ call assert_equal(0, min(reverse(range(99999, 0, -1))))
+
+ " remove()
+ call assert_equal(1, remove(range(1, 10), 0))
+ call assert_equal(2, remove(range(1, 10), 1))
+ call assert_equal(9, remove(range(1, 10), 8))
+ call assert_equal(10, remove(range(1, 10), 9))
+ call assert_equal(10, remove(range(1, 10), -1))
+ call assert_equal([3, 4, 5], remove(range(1, 10), 2, 4))
+
+ " repeat()
+ call assert_equal([0, 1, 2, 0, 1, 2], repeat(range(3), 2))
+ call assert_equal([0, 1, 2], repeat(range(3), 1))
+ call assert_equal([], repeat(range(3), 0))
+ call assert_equal([], repeat(range(5, 4), 2))
+ call assert_equal([], repeat(range(5, 4), 0))
+
+ " reverse()
+ call assert_equal([2, 1, 0], reverse(range(3)))
+ call assert_equal([0, 1, 2, 3], reverse(range(3, 0, -1)))
+ call assert_equal([9, 8, 7, 6, 5, 4, 3, 2, 1, 0], reverse(range(10)))
+ call assert_equal([20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10], reverse(range(10, 20)))
+ call assert_equal([16, 13, 10], reverse(range(10, 18, 3)))
+ call assert_equal([19, 16, 13, 10], reverse(range(10, 19, 3)))
+ call assert_equal([19, 16, 13, 10], reverse(range(10, 20, 3)))
+ call assert_equal([11, 14, 17, 20], reverse(range(20, 10, -3)))
+ call assert_equal([], reverse(range(0)))
+
+ " TODO: setpos()
+ " new
+ " call setline(1, repeat([''], bufnr('')))
+ " call setline(bufnr('') + 1, repeat('x', bufnr('') * 2 + 6))
+ " call setpos('x', range(bufnr(''), bufnr('') + 3))
+ " bwipe!
+
+ " setreg()
+ call setreg('a', range(3))
+ call assert_equal("0\n1\n2\n", getreg('a'))
+
+ " settagstack()
+ call settagstack(1, #{items : range(4)})
+
+ " sign_define()
+ call assert_fails("call sign_define(range(5))", "E715:")
+ call assert_fails("call sign_placelist(range(5))", "E715:")
+
+ " sign_undefine()
+ " call assert_fails("call sign_undefine(range(5))", "E908:")
+ call assert_fails("call sign_undefine(range(5))", "E155:")
+
+ " sign_unplacelist()
+ call assert_fails("call sign_unplacelist(range(5))", "E715:")
+
+ " sort()
+ call assert_equal([0, 1, 2, 3, 4, 5], sort(range(5, 0, -1)))
+
+ " string()
+ call assert_equal('[0, 1, 2, 3, 4]', string(range(5)))
+
+ " taglist() with 'tagfunc'
+ func TagFunc(pattern, flags, info)
+ return range(10)
+ endfunc
+ set tagfunc=TagFunc
+ call assert_fails("call taglist('asdf')", 'E987:')
+ set tagfunc=
+
+ " term_start()
+ if has('terminal') && has('termguicolors')
+ call assert_fails('call term_start(range(3, 4))', 'E474:')
+ let g:terminal_ansi_colors = range(16)
+ if has('win32')
+ let cmd = "cmd /c dir"
+ else
+ let cmd = "ls"
+ endif
+ call assert_fails('call term_start("' .. cmd .. '", #{term_finish: "close"})', 'E475:')
+ unlet g:terminal_ansi_colors
+ endif
+
+ " type()
+ call assert_equal(v:t_list, type(range(5)))
+
+ " uniq()
+ call assert_equal([0, 1, 2, 3, 4], uniq(range(5)))
+
+ " errors
+ call assert_fails('let x=range(2, 8, 0)', 'E726:')
+ call assert_fails('let x=range(3, 1)', 'E727:')
+ call assert_fails('let x=range(1, 3, -2)', 'E727:')
+ call assert_fails('let x=range([])', 'E745:')
+ call assert_fails('let x=range(1, [])', 'E745:')
+ call assert_fails('let x=range(1, 4, [])', 'E745:')
+endfunc
+
+func Test_garbagecollect_now_fails()
+ let v:testing = 0
+ call assert_fails('call test_garbagecollect_now()', 'E1142:')
+ let v:testing = 1
+endfunc
+
" Test for the eval() function
func Test_eval()
call assert_fails("call eval('5 a')", 'E488:')
endfunc
+" Test for the keytrans() function
+func Test_keytrans()
+ call assert_equal('<Space>', keytrans(' '))
+ call assert_equal('<lt>', keytrans('<'))
+ call assert_equal('<lt>Tab>', keytrans('<Tab>'))
+ call assert_equal('<Tab>', keytrans("\<Tab>"))
+ call assert_equal('<C-V>', keytrans("\<C-V>"))
+ call assert_equal('<BS>', keytrans("\<BS>"))
+ call assert_equal('<Home>', keytrans("\<Home>"))
+ call assert_equal('<C-Home>', keytrans("\<C-Home>"))
+ call assert_equal('<M-Home>', keytrans("\<M-Home>"))
+ call assert_equal('<C-Space>', keytrans("\<C-Space>"))
+ call assert_equal('<M-Space>', keytrans("\<*M-Space>"))
+ call assert_equal('<M-x>', "\<*M-x>"->keytrans())
+ call assert_equal('<C-I>', "\<*C-I>"->keytrans())
+ call assert_equal('<S-3>', "\<*S-3>"->keytrans())
+ call assert_equal('π', 'π'->keytrans())
+ call assert_equal('<M-π>', "\<M-π>"->keytrans())
+ call assert_equal('ě', 'ě'->keytrans())
+ call assert_equal('<M-ě>', "\<M-ě>"->keytrans())
+ call assert_equal('', ''->keytrans())
+ call assert_equal('', v:_null_string->keytrans())
+ call assert_fails('call keytrans(1)', 'E1174:')
+ call assert_fails('call keytrans()', 'E119:')
+endfunc
+
" Test for the nr2char() function
func Test_nr2char()
" set encoding=latin1
@@ -1842,6 +2391,13 @@ func Test_nr2char()
call assert_equal("\x80\xfc\b" .. nr2char(0x40000000), eval('"\<M-' .. nr2char(0x40000000) .. '>"'))
endfunc
+" Test for screenattr(), screenchar() and screenchars() functions
+func Test_screen_functions()
+ call assert_equal(-1, screenattr(-1, -1))
+ call assert_equal(-1, screenchar(-1, -1))
+ call assert_equal([], screenchars(-1, -1))
+endfunc
+
" Test for getcurpos() and setpos()
func Test_getcurpos_setpos()
new
@@ -1872,9 +2428,7 @@ 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 Ntest_setmouse(1, 1)
call assert_equal(#{
\ screenrow: 1,
\ screencol: 1,
@@ -1884,9 +2438,7 @@ func Test_getmousepos()
\ 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 Ntest_setmouse(1, 25)
call assert_equal(#{
\ screenrow: 1,
\ screencol: 25,
@@ -1896,9 +2448,7 @@ func Test_getmousepos()
\ 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 Ntest_setmouse(1, 50)
call assert_equal(#{
\ screenrow: 1,
\ screencol: 50,
@@ -1911,9 +2461,7 @@ func Test_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 Ntest_setmouse(2, 25)
call assert_equal(#{
\ screenrow: 2,
\ screencol: 25,
@@ -1923,9 +2471,7 @@ func Test_getmousepos()
\ 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 Ntest_setmouse(2, 50)
call assert_equal(#{
\ screenrow: 2,
\ screencol: 50,
@@ -1938,6 +2484,28 @@ func Test_getmousepos()
bwipe!
endfunc
+" Test for glob()
+func Test_glob()
+ call assert_equal('', glob(v:_null_string))
+ call assert_equal('', globpath(v:_null_string, v:_null_string))
+ call assert_fails("let x = globpath(&rtp, 'syntax/c.vim', [])", 'E745:')
+
+ call writefile([], 'Xglob1')
+ call writefile([], 'XGLOB2')
+ set wildignorecase
+ " Sort output of glob() otherwise we end up with different
+ " ordering depending on whether file system is case-sensitive.
+ call assert_equal(['XGLOB2', 'Xglob1'], sort(glob('Xglob[12]', 0, 1)))
+ " wildignorecase shall be applied even when the pattern contains no wildcards.
+ call assert_equal('XGLOB2', glob('xglob2'))
+ set wildignorecase&
+
+ call delete('Xglob1')
+ call delete('XGLOB2')
+
+ call assert_fails("call glob('*', 0, {})", 'E728:')
+endfunc
+
func HasDefault(msg = 'msg')
return a:msg
endfunc
@@ -1946,4 +2514,46 @@ func Test_default_arg_value()
call assert_equal('msg', HasDefault())
endfunc
+" Test for gettext()
+func Test_gettext()
+ call assert_fails('call gettext(1)', 'E475:')
+endfunc
+
+func Test_builtin_check()
+ call assert_fails('let g:["trim"] = {x -> " " .. x}', 'E704:')
+ call assert_fails('let g:.trim = {x -> " " .. x}', 'E704:')
+ call assert_fails('let l:["trim"] = {x -> " " .. x}', 'E704:')
+ call assert_fails('let l:.trim = {x -> " " .. x}', 'E704:')
+ let lines =<< trim END
+ vim9script
+ var s:trim = (x) => " " .. x
+ END
+ call CheckScriptFailure(lines, 'E704:')
+
+ call assert_fails('call extend(g:, #{foo: { -> "foo" }})', 'E704:')
+ let g:bar = 123
+ call extend(g:, #{bar: { -> "foo" }}, "keep")
+ call assert_fails('call extend(g:, #{bar: { -> "foo" }}, "force")', 'E704:')
+ unlet g:bar
+
+ call assert_fails('call extend(l:, #{foo: { -> "foo" }})', 'E704:')
+ let bar = 123
+ call extend(l:, #{bar: { -> "foo" }}, "keep")
+ call assert_fails('call extend(l:, #{bar: { -> "foo" }}, "force")', 'E704:')
+ unlet bar
+
+ call assert_fails('call extend(g:, #{foo: function("extend")})', 'E704:')
+ let g:bar = 123
+ call extend(g:, #{bar: function("extend")}, "keep")
+ call assert_fails('call extend(g:, #{bar: function("extend")}, "force")', 'E704:')
+ unlet g:bar
+
+ call assert_fails('call extend(l:, #{foo: function("extend")})', 'E704:')
+ let bar = 123
+ call extend(l:, #{bar: function("extend")}, "keep")
+ call assert_fails('call extend(l:, #{bar: function("extend")}, "force")', 'E704:')
+ unlet bar
+endfunc
+
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_getcwd.vim b/src/nvim/testdir/test_getcwd.vim
index a75583cd2c..073f8303dc 100644
--- a/src/nvim/testdir/test_getcwd.vim
+++ b/src/nvim/testdir/test_getcwd.vim
@@ -24,7 +24,7 @@ endfunc
" Do all test in a separate window to avoid E211 when we recursively
" delete the Xtopdir directory during cleanup
-function SetUp()
+func SetUp()
set visualbell
set nocp viminfo+=nviminfo
diff --git a/src/nvim/testdir/test_getvar.vim b/src/nvim/testdir/test_getvar.vim
index 5a96548893..e6b6341fce 100644
--- a/src/nvim/testdir/test_getvar.vim
+++ b/src/nvim/testdir/test_getvar.vim
@@ -133,11 +133,20 @@ func Test_get_lambda()
call assert_equal([], get(l:L, 'args'))
endfunc
+func s:FooBar()
+endfunc
+
" get({func}, {what} [, {default}])
func Test_get_func()
let l:F = function('tr')
call assert_equal('tr', get(l:F, 'name'))
call assert_equal(l:F, get(l:F, 'func'))
+
+ let Fb_func = function('s:FooBar')
+ call assert_match('<SNR>\d\+_FooBar', get(Fb_func, 'name'))
+ let Fb_ref = funcref('s:FooBar')
+ call assert_match('<SNR>\d\+_FooBar', get(Fb_ref, 'name'))
+
call assert_equal({'func has': 'no dict'}, get(l:F, 'dict', {'func has': 'no dict'}))
call assert_equal(0, get(l:F, 'dict'))
call assert_equal([], get(l:F, 'args'))
diff --git a/src/nvim/testdir/test_gf.vim b/src/nvim/testdir/test_gf.vim
index b2bb189688..e369645328 100644
--- a/src/nvim/testdir/test_gf.vim
+++ b/src/nvim/testdir/test_gf.vim
@@ -226,6 +226,32 @@ func Test_gf_includeexpr()
delfunc IncFunc
endfunc
+" Test for using a script-local function for 'includeexpr'
+func Test_includeexpr_scriptlocal_func()
+ func! s:IncludeFunc()
+ let g:IncludeFname = v:fname
+ return ''
+ endfunc
+ set includeexpr=s:IncludeFunc()
+ call assert_equal(expand('<SID>') .. 'IncludeFunc()', &includeexpr)
+ new | only
+ call setline(1, 'TestFile1')
+ let g:IncludeFname = ''
+ call assert_fails('normal! gf', 'E447:')
+ call assert_equal('TestFile1', g:IncludeFname)
+ bw!
+ set includeexpr=<SID>IncludeFunc()
+ call assert_equal(expand('<SID>') .. 'IncludeFunc()', &includeexpr)
+ new | only
+ call setline(1, 'TestFile2')
+ let g:IncludeFname = ''
+ call assert_fails('normal! gf', 'E447:')
+ call assert_equal('TestFile2', g:IncludeFname)
+ set includeexpr&
+ delfunc s:IncludeFunc
+ bw!
+endfunc
+
" Check that expanding directories can handle more than 255 entries.
func Test_gf_subdirs_wildcard()
let cwd = getcwd()
diff --git a/src/nvim/testdir/test_global.vim b/src/nvim/testdir/test_global.vim
index cb6851250c..44a8784348 100644
--- a/src/nvim/testdir/test_global.vim
+++ b/src/nvim/testdir/test_global.vim
@@ -39,7 +39,7 @@ endfunc
func Test_global_error()
call assert_fails('g\\a', 'E10:')
call assert_fails('g', 'E148:')
- call assert_fails('g/\(/y', 'E476:')
+ call assert_fails('g/\(/y', 'E54:')
endfunc
" Test for printing lines using :g with different search patterns
@@ -72,6 +72,18 @@ func Test_global_print()
close!
endfunc
+func Test_global_empty_pattern()
+ " populate history
+ silent g/hello/
+
+ redir @a
+ g//
+ redir END
+
+ call assert_match('Pattern not found: hello', @a)
+ " ^~~~~ this was previously empty
+endfunc
+
" Test for global command with newline character
func Test_global_newline()
new
@@ -91,6 +103,7 @@ endfunc
" Test for interrupting :global using Ctrl-C
func Test_interrupt_global()
CheckRunVimInTerminal
+
let lines =<< trim END
cnoremap ; <Cmd>sleep 10<CR>
call setline(1, repeat(['foo'], 5))
@@ -100,14 +113,14 @@ func Test_interrupt_global()
call term_sendkeys(buf, ":g/foo/norm :\<C-V>;\<CR>")
" Wait for :sleep to start
- call term_wait(buf)
+ call TermWait(buf, 100)
call term_sendkeys(buf, "\<C-C>")
call WaitForAssert({-> assert_match('Interrupted', term_getline(buf, 6))}, 1000)
" Also test in Ex mode
call term_sendkeys(buf, "gQg/foo/norm :\<C-V>;\<CR>")
" Wait for :sleep to start
- call term_wait(buf)
+ call TermWait(buf, 100)
call term_sendkeys(buf, "\<C-C>")
call WaitForAssert({-> assert_match('Interrupted', term_getline(buf, 5))}, 1000)
diff --git a/src/nvim/testdir/test_gui.vim b/src/nvim/testdir/test_gui.vim
new file mode 100644
index 0000000000..c3f1f3163a
--- /dev/null
+++ b/src/nvim/testdir/test_gui.vim
@@ -0,0 +1,43 @@
+
+func Test_colorscheme()
+ " call assert_equal('16777216', &t_Co)
+
+ let colorscheme_saved = exists('g:colors_name') ? g:colors_name : 'default'
+ let g:color_count = 0
+ augroup TestColors
+ au!
+ au ColorScheme * let g:color_count += 1
+ \ | let g:after_colors = g:color_count
+ \ | let g:color_after = expand('<amatch>')
+ au ColorSchemePre * let g:color_count += 1
+ \ | let g:before_colors = g:color_count
+ \ | let g:color_pre = expand('<amatch>')
+ augroup END
+
+ colorscheme torte
+ redraw!
+ call assert_equal('dark', &background)
+ call assert_equal(1, g:before_colors)
+ call assert_equal(2, g:after_colors)
+ call assert_equal('torte', g:color_pre)
+ call assert_equal('torte', g:color_after)
+ call assert_equal("\ntorte", execute('colorscheme'))
+
+ let a = substitute(execute('hi Search'), "\n\\s\\+", ' ', 'g')
+ " FIXME: temporarily check less while the colorscheme changes
+ " call assert_match("\nSearch xxx term=reverse cterm=reverse ctermfg=196 ctermbg=16 gui=reverse guifg=#ff0000 guibg=#000000", a)
+ " call assert_match("\nSearch xxx term=reverse ", a)
+
+ call assert_fails('colorscheme does_not_exist', 'E185:')
+ call assert_equal('does_not_exist', g:color_pre)
+ call assert_equal('torte', g:color_after)
+
+ exec 'colorscheme' colorscheme_saved
+ augroup TestColors
+ au!
+ augroup END
+ unlet g:color_count g:after_colors g:before_colors
+ redraw!
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_hardcopy.vim b/src/nvim/testdir/test_hardcopy.vim
deleted file mode 100644
index e390bd5cc8..0000000000
--- a/src/nvim/testdir/test_hardcopy.vim
+++ /dev/null
@@ -1,204 +0,0 @@
-" Test :hardcopy
-
-source check.vim
-
-func Test_printoptions()
- edit test_hardcopy.vim
- syn on
-
- for opt in ['left:5in,right:10pt,top:8mm,bottom:2pc',
- \ 'left:2in,top:30pt,right:16mm,bottom:3pc',
- \ 'header:3,syntax:y,number:y,wrap:n',
- \ 'header:3,syntax:n,number:y,wrap:y',
- \ 'header:0,syntax:a,number:y,wrap:y',
- \ 'duplex:short,collate:n,jobsplit:y,portrait:n',
- \ 'duplex:long,collate:y,jobsplit:n,portrait:y',
- \ 'duplex:off,collate:y,jobsplit:y,portrait:y',
- \ 'paper:10x14',
- \ 'paper:A3',
- \ 'paper:A4',
- \ 'paper:A5',
- \ 'paper:B4',
- \ 'paper:B5',
- \ 'paper:executive',
- \ 'paper:folio',
- \ 'paper:ledger',
- \ 'paper:legal',
- \ 'paper:letter',
- \ 'paper:quarto',
- \ 'paper:statement',
- \ 'paper:tabloid',
- \ 'formfeed:y',
- \ '']
- exe 'set printoptions=' .. opt
- if has('postscript')
- 1,50hardcopy > Xhardcopy_printoptions
- let lines = readfile('Xhardcopy_printoptions')
- call assert_true(len(lines) > 20, opt)
- call assert_true(lines[0] =~ 'PS-Adobe', opt)
- call delete('Xhardcopy_printoptions')
- endif
- endfor
-
- call assert_fails('set printoptions=paper', 'E550:')
- call assert_fails('set printoptions=shredder:on', 'E551:')
- call assert_fails('set printoptions=left:no', 'E552:')
- set printoptions&
- bwipe
-endfunc
-
-func Test_printmbfont()
- " Print a help page which contains tabs, underlines (etc) to recover more code.
- help syntax.txt
- syn on
-
- for opt in [':WadaMin-Regular,b:WadaMin-Bold,i:WadaMin-Italic,o:WadaMin-Bold-Italic,c:yes,a:no',
- \ '']
- exe 'set printmbfont=' .. opt
- if has('postscript')
- hardcopy > Xhardcopy_printmbfont
- let lines = readfile('Xhardcopy_printmbfont')
- call assert_true(len(lines) > 20, opt)
- call assert_true(lines[0] =~ 'PS-Adobe', opt)
- call delete('Xhardcopy_printmbfont')
- endif
- endfor
- set printmbfont&
- bwipe
-endfunc
-
-func Test_printmbcharset()
- CheckFeature postscript
-
- " digraph.txt has plenty of non-latin1 characters.
- help digraph.txt
- set printmbcharset=ISO10646 printencoding=utf-8
- for courier in ['yes', 'no']
- for ascii in ['yes', 'no']
- exe 'set printmbfont=r:WadaMin-Regular,b:WadaMin-Bold,i:WadaMin-Italic,o:WadaMin-BoldItalic'
- \ .. ',c:' .. courier .. ',a:' .. ascii
- hardcopy > Xhardcopy_printmbcharset
- let lines = readfile('Xhardcopy_printmbcharset')
- call assert_true(len(lines) > 20)
- call assert_true(lines[0] =~ 'PS-Adobe')
- endfor
- endfor
-
- set printmbcharset=does-not-exist printencoding=utf-8 printmbfont=r:WadaMin-Regular
- call assert_fails('hardcopy > Xhardcopy_printmbcharset', 'E456:')
-
- set printmbcharset=GB_2312-80 printencoding=utf-8 printmbfont=r:WadaMin-Regular
- call assert_fails('hardcopy > Xhardcopy_printmbcharset', 'E673:')
-
- set printmbcharset=ISO10646 printencoding=utf-8 printmbfont=
- call assert_fails('hardcopy > Xhardcopy_printmbcharset', 'E675:')
-
- call delete('Xhardcopy_printmbcharset')
- set printmbcharset& printencoding& printmbfont&
- bwipe
-endfunc
-
-func Test_printexpr()
- CheckFeature postscript
-
- " Not a very useful printexpr value, but enough to test
- " hardcopy with 'printexpr'.
- function PrintFile(fname)
- call writefile(['Test printexpr: ' .. v:cmdarg],
- \ 'Xhardcopy_printexpr')
- call delete(a:fname)
- return 0
- endfunc
- set printexpr=PrintFile(v:fname_in)
-
- help help
- hardcopy dummy args
- call assert_equal(['Test printexpr: dummy args'],
- \ readfile('Xhardcopy_printexpr'))
- call delete('Xhardcopy_printexpr')
-
- " Function returns 1 to test print failure.
- function PrintFails(fname)
- call delete(a:fname)
- return 1
- endfunc
- set printexpr=PrintFails(v:fname_in)
- call assert_fails('hardcopy', 'E365:')
-
- set printexpr&
- bwipe
-endfunc
-
-func Test_errors()
- CheckFeature postscript
-
- edit test_hardcopy.vim
- call assert_fails('hardcopy >', 'E324:')
- bwipe
-endfunc
-
-func Test_dark_background()
- edit test_hardcopy.vim
- syn on
-
- for bg in ['dark', 'light']
- exe 'set background=' .. bg
-
- if has('postscript')
- hardcopy > Xhardcopy_dark_background
- let lines = readfile('Xhardcopy_dark_background')
- call assert_true(len(lines) > 20)
- call assert_true(lines[0] =~ 'PS-Adobe')
- call delete('Xhardcopy_dark_background')
- endif
- endfor
-
- set background&
- bwipe
-endfun
-
-func Test_empty_buffer()
- CheckFeature postscript
-
- new
- call assert_equal("\nNo text to be printed", execute('hardcopy'))
- bwipe
-endfunc
-
-func Test_printheader_parsing()
- " Only test that this doesn't throw an error.
- set printheader=%<%f\ %h%m%r%=%-14.(%l,%c%V%)\ %P
- set printheader=%<%f%h%m%r%=%b\ 0x%B\ \ %l,%c%V\ %P
- set printheader=%<%f%=\ [%1*%M%*%n%R%H]\ %-19(%3l,%02c%03V%)%O'%02b'
- set printheader=...%r%{VarExists('b:gzflag','\ [GZ]')}%h...
- set printheader=
- set printheader&
-endfunc
-
-func Test_fname_with_spaces()
- CheckFeature postscript
-
- split t\ e\ s\ t.txt
- call setline(1, ['just', 'some', 'text'])
- hardcopy > %.ps
- call assert_true(filereadable('t e s t.txt.ps'))
- call delete('t e s t.txt.ps')
- bwipe!
-endfunc
-
-func Test_illegal_byte()
- CheckFeature postscript
- if &enc != 'utf-8'
- return
- endif
-
- new
- " conversion of 0xff will fail, this used to cause a crash
- call setline(1, "\xff")
- hardcopy >Xpstest
-
- bwipe!
- call delete('Xpstest')
-endfunc
-
-" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_help.vim b/src/nvim/testdir/test_help.vim
index dbb36facee..08dd3dcb9a 100644
--- a/src/nvim/testdir/test_help.vim
+++ b/src/nvim/testdir/test_help.vim
@@ -1,5 +1,23 @@
" Tests for :help
+source check.vim
+source vim9.vim
+
+func SetUp()
+ let s:vimruntime = $VIMRUNTIME
+ let s:runtimepath = &runtimepath
+ " Set $VIMRUNTIME to $BUILD_DIR/runtime and remove the original $VIMRUNTIME
+ " path from &runtimepath so that ":h local-additions" won't pick up builtin
+ " help files.
+ let $VIMRUNTIME = expand($BUILD_DIR) .. '/runtime'
+ set runtimepath-=../../../runtime
+endfunc
+
+func TearDown()
+ let $VIMRUNTIME = s:vimruntime
+ let &runtimepath = s:runtimepath
+endfunc
+
func Test_help_restore_snapshot()
help
set buftype=
@@ -79,16 +97,42 @@ func Test_help_local_additions()
call writefile(['*mydoc-ext.txt* my extended awesome doc'], 'Xruntime/doc/mydoc-ext.txt')
let rtp_save = &rtp
set rtp+=./Xruntime
- help
- 1
- call search('mydoc.txt')
- call assert_equal('|mydoc.txt| my awesome doc', getline('.'))
- 1
- call search('mydoc-ext.txt')
- call assert_equal('|mydoc-ext.txt| my extended awesome doc', getline('.'))
+ help local-additions
+ let lines = getline(line(".") + 1, search("^$") - 1)
+ call assert_equal([
+ \ '|mydoc-ext.txt| my extended awesome doc',
+ \ '|mydoc.txt| my awesome doc'
+ \ ], lines)
+ call delete('Xruntime/doc/mydoc-ext.txt')
+ close
+
+ call mkdir('Xruntime-ja/doc', 'p')
+ call writefile(["local-additions\thelp.jax\t/*local-additions*"], 'Xruntime-ja/doc/tags-ja')
+ call writefile(['*help.txt* This is jax file', '',
+ \ 'LOCAL ADDITIONS: *local-additions*', ''], 'Xruntime-ja/doc/help.jax')
+ call writefile(['*work.txt* This is jax file'], 'Xruntime-ja/doc/work.jax')
+ call writefile(['*work2.txt* This is jax file'], 'Xruntime-ja/doc/work2.jax')
+ set rtp+=./Xruntime-ja
+
+ help local-additions@en
+ let lines = getline(line(".") + 1, search("^$") - 1)
+ call assert_equal([
+ \ '|mydoc.txt| my awesome doc'
+ \ ], lines)
+ close
+
+ help local-additions@ja
+ let lines = getline(line(".") + 1, search("^$") - 1)
+ call assert_equal([
+ \ '|mydoc.txt| my awesome doc',
+ \ '|help.txt| This is jax file',
+ \ '|work.txt| This is jax file',
+ \ '|work2.txt| This is jax file',
+ \ ], lines)
close
call delete('Xruntime', 'rf')
+ call delete('Xruntime-ja', 'rf')
let &rtp = rtp_save
endfunc
@@ -98,6 +142,7 @@ func Test_help_completion()
endfunc
" Test for the :helptags command
+" NOTE: if you run tests as root this will fail. Don't run tests as root!
func Test_helptag_cmd()
call mkdir('Xdir/a/doc', 'p')
@@ -111,28 +156,12 @@ func Test_helptag_cmd()
call assert_equal(["help-tags\ttags\t1"], readfile('Xdir/tags'))
call delete('Xdir/tags')
- " The following tests fail on FreeBSD for some reason
- if has('unix') && !has('bsd')
- " Read-only tags file
- call mkdir('Xdir/doc', 'p')
- call writefile([''], 'Xdir/doc/tags')
- call writefile([], 'Xdir/doc/sample.txt')
- call setfperm('Xdir/doc/tags', 'r-xr--r--')
- call assert_fails('helptags Xdir/doc', 'E152:', getfperm('Xdir/doc/tags'))
-
- let rtp = &rtp
- let &rtp = 'Xdir'
- helptags ALL
- let &rtp = rtp
-
- call delete('Xdir/doc/tags')
-
- " No permission to read the help file
- call setfperm('Xdir/a/doc/sample.txt', '-w-------')
- call assert_fails('helptags Xdir', 'E153:', getfperm('Xdir/a/doc/sample.txt'))
- call delete('Xdir/a/doc/sample.txt')
- call delete('Xdir/tags')
- endif
+ " Test parsing tags
+ call writefile(['*tag1*', 'Example: >', ' *notag*', 'Example end: *tag2*'],
+ \ 'Xdir/a/doc/sample.txt')
+ helptags Xdir
+ call assert_equal(["tag1\ta/doc/sample.txt\t/*tag1*",
+ \ "tag2\ta/doc/sample.txt\t/*tag2*"], readfile('Xdir/tags'))
" Duplicate tags in the help file
call writefile(['*tag1*', '*tag1*', '*tag2*'], 'Xdir/a/doc/sample.txt')
@@ -141,6 +170,43 @@ func Test_helptag_cmd()
call delete('Xdir', 'rf')
endfunc
+func Test_helptag_cmd_readonly()
+ CheckUnix
+ CheckNotRoot
+
+ " Read-only tags file
+ call mkdir('Xdir/doc', 'p')
+ call writefile([''], 'Xdir/doc/tags')
+ call writefile([], 'Xdir/doc/sample.txt')
+ call setfperm('Xdir/doc/tags', 'r-xr--r--')
+ call assert_fails('helptags Xdir/doc', 'E152:', getfperm('Xdir/doc/tags'))
+
+ let rtp = &rtp
+ let &rtp = 'Xdir'
+ helptags ALL
+ let &rtp = rtp
+
+ call delete('Xdir/doc/tags')
+
+ " No permission to read the help file
+ call mkdir('Xdir/b/doc', 'p')
+ call writefile([], 'Xdir/b/doc/sample.txt')
+ call setfperm('Xdir/b/doc/sample.txt', '-w-------')
+ call assert_fails('helptags Xdir', 'E153:', getfperm('Xdir/b/doc/sample.txt'))
+ call delete('Xdir', 'rf')
+endfunc
+
+" Test for setting the 'helpheight' option in the help window
+func Test_help_window_height()
+ let &cmdheight = &lines - 23
+ set helpheight=10
+ help
+ set helpheight=14
+ call assert_equal(14, winheight(0))
+ set helpheight& cmdheight=1
+ close
+endfunc
+
func Test_help_long_argument()
try
exe 'help \%' .. repeat('0', 1021)
@@ -149,5 +215,15 @@ func Test_help_long_argument()
endtry
endfunc
+func Test_help_using_visual_match()
+ let lines =<< trim END
+ call setline(1, ' ')
+ /^
+ exe "normal \<C-V>\<C-V>"
+ h5\%V€]
+ END
+ call CheckScriptFailure(lines, 'E149:')
+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 e84726bbfc..eae1a241e3 100644
--- a/src/nvim/testdir/test_help_tagjump.vim
+++ b/src/nvim/testdir/test_help_tagjump.vim
@@ -1,17 +1,5 @@
" Tests for :help! {subject}
-func SetUp()
- " v:progpath is …/build/bin/nvim and we need …/build/runtime
- " to be added to &rtp
- let builddir = fnamemodify(exepath(v:progpath), ':h:h')
- let s:rtp = &rtp
- let &rtp .= printf(',%s/runtime', builddir)
-endfunc
-
-func TearDown()
- let &rtp = s:rtp
-endfunc
-
func Test_help_tagjump()
help
call assert_equal("help", &filetype)
@@ -38,18 +26,18 @@ func Test_help_tagjump()
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 ??
+ " help sm?le
+ help ch?ckhealth
call assert_equal("help", &filetype)
- call assert_true(getline('.') =~ '\*g??\*')
+ " call assert_true(getline('.') =~ '\*:smile\*')
+ call assert_true(getline('.') =~ '\*:checkhealth\*')
helpclose
- help ch?ckhealth
+ help ??
call assert_equal("help", &filetype)
- call assert_true(getline('.') =~ '\*:checkhealth\*')
+ " *??* tag needs patch 8.2.1794
+ " call assert_true(getline('.') =~ '\*??\*')
+ call assert_true(getline('.') =~ '\*g??\*')
helpclose
help :?
diff --git a/src/nvim/testdir/test_hide.vim b/src/nvim/testdir/test_hide.vim
index 41b1a4ad7c..b3ce395523 100644
--- a/src/nvim/testdir/test_hide.vim
+++ b/src/nvim/testdir/test_hide.vim
@@ -1,6 +1,6 @@
" Tests for :hide command/modifier and 'hidden' option
-function SetUp()
+func SetUp()
let s:save_hidden = &hidden
let s:save_bufhidden = &bufhidden
let s:save_autowrite = &autowrite
diff --git a/src/nvim/testdir/test_highlight.vim b/src/nvim/testdir/test_highlight.vim
index efdf44a0d6..8a102f2e65 100644
--- a/src/nvim/testdir/test_highlight.vim
+++ b/src/nvim/testdir/test_highlight.vim
@@ -536,9 +536,7 @@ func Test_termguicolors()
endfunc
func Test_cursorline_after_yank()
- if !CanRunVimInTerminal()
- throw 'Skipped: cannot make screendumps'
- endif
+ CheckScreendump
call writefile([
\ 'set cul rnu',
@@ -578,9 +576,7 @@ func Test_put_before_cursorline()
endfunc
func Test_cursorline_with_visualmode()
- if !CanRunVimInTerminal()
- throw 'Skipped: cannot make screendumps'
- endif
+ CheckScreendump
call writefile([
\ 'set cul',
@@ -722,7 +718,7 @@ func Test_1_highlight_Normalgroup_exists()
elseif has('gui_gtk2') || has('gui_gnome') || has('gui_gtk3')
" expect is DEFAULT_FONT of gui_gtk_x11.c
call assert_match('hi Normal\s*font=Monospace 10', hlNormal)
- elseif has('gui_motif') || has('gui_athena')
+ elseif has('gui_motif')
" expect is DEFAULT_FONT of gui_x11.c
call assert_match('hi Normal\s*font=7x13', hlNormal)
elseif has('win32')
@@ -731,7 +727,8 @@ func Test_1_highlight_Normalgroup_exists()
endif
endfunc
-function Test_no_space_before_xxx()
+" Do this test last, sometimes restoring the columns doesn't work
+func Test_z_no_space_before_xxx()
" Note: we need to create this highlight group in the test because it does not exist in Neovim
execute('hi StatusLineTermNC ctermfg=green')
let l:org_columns = &columns
@@ -739,13 +736,23 @@ function Test_no_space_before_xxx()
let l:hi_StatusLineTermNC = join(split(execute('hi StatusLineTermNC')))
call assert_match('StatusLineTermNC xxx', l:hi_StatusLineTermNC)
let &columns = l:org_columns
-endfunction
+endfunc
" Test for :highlight command errors
func Test_highlight_cmd_errors()
if has('gui_running') || has('nvim')
+ " This test doesn't fail in the MS-Windows console version.
+ call assert_fails('hi Xcomment ctermfg=fg', 'E419:')
+ call assert_fails('hi Xcomment ctermfg=bg', 'E420:')
call assert_fails('hi ' .. repeat('a', 201) .. ' ctermfg=black', 'E1249:')
endif
+
+ " Try using a very long terminal code. Define a dummy terminal code for this
+ " test.
+ let &t_fo = "\<Esc>1;"
+ let c = repeat("t_fo,", 100) . "t_fo"
+ " call assert_fails('exe "hi Xgroup1 start=" . c', 'E422:')
+ let &t_fo = ""
endfunc
" Test for using RGB color values in a highlight group
@@ -812,11 +819,11 @@ func Test_highlight_clear_restores_context()
let patContextDefault = fnamemodify(scriptContextDefault, ':t') .. ' line 1'
let patContextRelink = fnamemodify(scriptContextRelink, ':t') .. ' line 2'
- exec "source" scriptContextDefault
+ exec 'source ' .. scriptContextDefault
let hlContextDefault = execute("verbose hi Context")
call assert_match(patContextDefault, hlContextDefault)
- exec "source" scriptContextRelink
+ exec 'source ' .. scriptContextRelink
let hlContextRelink = execute("verbose hi Context")
call assert_match(patContextRelink, hlContextRelink)
diff --git a/src/nvim/testdir/test_history.vim b/src/nvim/testdir/test_history.vim
index feb521e232..f1c31dee04 100644
--- a/src/nvim/testdir/test_history.vim
+++ b/src/nvim/testdir/test_history.vim
@@ -95,6 +95,23 @@ function Test_History()
call assert_fails('call histnr([])', 'E730:')
call assert_fails('history xyz', 'E488:')
call assert_fails('history ,abc', 'E488:')
+ call assert_fails('call histdel(":", "\\%(")', 'E53:')
+endfunction
+
+function Test_history_truncates_long_entry()
+ " History entry short enough to fit on the screen should not be truncated.
+ call histadd(':', 'echo x' .. repeat('y', &columns - 17) .. 'z')
+ let a = execute('history : -1')
+
+ call assert_match("^\n # cmd history\n"
+ \ .. "> *\\d\\+ echo x" .. repeat('y', &columns - 17) .. 'z$', a)
+
+ " Long history entry should be truncated to fit on the screen, with, '...'
+ " inserted in the string to indicate the that there is truncation.
+ call histadd(':', 'echo x' .. repeat('y', &columns - 16) .. 'z')
+ let a = execute('history : -1')
+ call assert_match("^\n # cmd history\n"
+ \ .. "> *\\d\\+ echo xy\\+\.\.\.y\\+z$", a)
endfunction
function Test_Search_history_window()
diff --git a/src/nvim/testdir/test_increment.vim b/src/nvim/testdir/test_increment.vim
index 2559654f25..3c2b88ef9f 100644
--- a/src/nvim/testdir/test_increment.vim
+++ b/src/nvim/testdir/test_increment.vim
@@ -476,6 +476,10 @@ func Test_visual_increment_20()
exec "norm! \<C-A>"
call assert_equal(["b"], getline(1, '$'))
call assert_equal([0, 1, 1, 0], getpos('.'))
+ " decrement a and A and increment z and Z
+ call setline(1, ['a', 'A', 'z', 'Z'])
+ exe "normal 1G\<C-X>2G\<C-X>3G\<C-A>4G\<C-A>"
+ call assert_equal(['a', 'A', 'z', 'Z'], getline(1, '$'))
endfunc
" 21) block-wise increment on part of hexadecimal
@@ -566,12 +570,14 @@ endfunc
" 1) <ctrl-a>
" 0b11111111111111111111111111111111
func Test_visual_increment_26()
- set nrformats+=alpha
+ set nrformats+=bin
call setline(1, ["0b11111111111111111111111111111110"])
exec "norm! \<C-V>$\<C-A>"
call assert_equal(["0b11111111111111111111111111111111"], getline(1, '$'))
call assert_equal([0, 1, 1, 0], getpos('.'))
- set nrformats-=alpha
+ exec "norm! \<C-V>$\<C-X>"
+ call assert_equal(["0b11111111111111111111111111111110"], getline(1, '$'))
+ set nrformats-=bin
endfunc
" 27) increment with 'rightreft', if supported
@@ -772,7 +778,6 @@ func Test_normal_increment_03()
endfunc
func Test_increment_empty_line()
- new
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))
@@ -783,8 +788,13 @@ func Test_increment_empty_line()
exe "normal! c\<C-A>l"
exe "normal! c\<C-X>l"
call assert_equal('one two', getline(1))
+endfunc
- bwipe!
+" Try incrementing/decrementing a non-digit/alpha character
+func Test_increment_special_char()
+ call setline(1, '!')
+ call assert_beeps("normal \<C-A>")
+ call assert_beeps("normal \<C-X>")
endfunc
" Try incrementing/decrementing a number when nrformats contains unsigned
@@ -867,4 +877,21 @@ func Test_normal_increment_with_virtualedit()
set virtualedit&
endfunc
+" Test for incrementing a signed hexadecimal and octal number
+func Test_normal_increment_signed_hexoct_nr()
+ new
+ " negative sign before a hex number should be ignored
+ call setline(1, ["-0x9"])
+ exe "norm \<C-A>"
+ call assert_equal(["-0xa"], getline(1, '$'))
+ exe "norm \<C-X>"
+ call assert_equal(["-0x9"], getline(1, '$'))
+ call setline(1, ["-007"])
+ exe "norm \<C-A>"
+ call assert_equal(["-010"], getline(1, '$'))
+ exe "norm \<C-X>"
+ call assert_equal(["-007"], getline(1, '$'))
+ bw!
+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 179218e48a..ec1379a378 100644
--- a/src/nvim/testdir/test_ins_complete.vim
+++ b/src/nvim/testdir/test_ins_complete.vim
@@ -1,5 +1,8 @@
+" Test for insert completion
+
source screendump.vim
source check.vim
+source vim9.vim
" Test for insert expansion
func Test_ins_complete()
@@ -44,11 +47,11 @@ func Test_ins_complete()
exe "normal o\<C-X>\<C-P>\<C-P>\<C-X>\<C-X>\<C-N>\<C-X>\<C-N>\<C-N>"
call assert_equal('run1 run2', getline('.'))
- set cpt=.,w,i
+ set cpt=.,\ ,w,i
" i-add-expands and switches to local
exe "normal OM\<C-N>\<C-X>\<C-N>\<C-X>\<C-N>\<C-X>\<C-X>\<C-X>\<C-P>"
call assert_equal("Makefile\tto\trun3", getline('.'))
- " add-expands lines (it would end in an empty line if it didn't ignored
+ " add-expands lines (it would end in an empty line if it didn't ignore
" itself)
exe "normal o\<C-X>\<C-L>\<C-X>\<C-L>\<C-P>\<C-P>"
call assert_equal("Makefile\tto\trun3", getline('.'))
@@ -68,6 +71,11 @@ func Test_ins_complete()
call assert_equal('Xtest11.one', getline('.'))
normal ddk
+ " Test for expanding a non-existing filename
+ exe "normal oa1b2X3Y4\<C-X>\<C-F>"
+ call assert_equal('a1b2X3Y4', getline('.'))
+ normal ddk
+
set cpt=w
" checks make_cyclic in other window
exe "normal oST\<C-N>\<C-P>\<C-P>\<C-P>\<C-P>"
@@ -183,17 +191,17 @@ func s:CompleteDone_CompleteFuncDict( findstart, base )
endif
return {
- \ 'words': [
- \ {
- \ 'word': 'aword',
- \ 'abbr': 'wrd',
- \ 'menu': 'extra text',
- \ 'info': 'words are cool',
- \ 'kind': 'W',
- \ 'user_data': ['one', 'two']
- \ }
- \ ]
- \ }
+ \ 'words': [
+ \ {
+ \ 'word': 'aword',
+ \ 'abbr': 'wrd',
+ \ 'menu': 'extra text',
+ \ 'info': 'words are cool',
+ \ 'kind': 'W',
+ \ 'user_data': ['one', 'two']
+ \ }
+ \ ]
+ \ }
endfunc
func s:CompleteDone_CheckCompletedItemNone()
@@ -253,16 +261,16 @@ func s:CompleteDone_CompleteFuncDictNoUserData(findstart, base)
endif
return {
- \ 'words': [
- \ {
- \ 'word': 'aword',
- \ 'abbr': 'wrd',
- \ 'menu': 'extra text',
- \ 'info': 'words are cool',
- \ 'kind': 'W'
- \ }
- \ ]
- \ }
+ \ 'words': [
+ \ {
+ \ 'word': 'aword',
+ \ 'abbr': 'wrd',
+ \ 'menu': 'extra text',
+ \ 'info': 'words are cool',
+ \ 'kind': 'W',
+ \ }
+ \ ]
+ \ }
endfunc
func s:CompleteDone_CheckCompletedItemDictNoUserData()
@@ -573,6 +581,33 @@ func Test_pum_with_folds_two_tabs()
call delete('Xpumscript')
endfunc
+func Test_pum_with_preview_win()
+ CheckScreendump
+
+ let lines =<< trim END
+ funct Omni_test(findstart, base)
+ if a:findstart
+ return col(".") - 1
+ endif
+ return [#{word: "one", info: "1info"}, #{word: "two", info: "2info"}, #{word: "three", info: "3info"}]
+ endfunc
+ set omnifunc=Omni_test
+ set completeopt+=longest
+ END
+
+ call writefile(lines, 'Xpreviewscript')
+ let buf = RunVimInTerminal('-S Xpreviewscript', #{rows: 12})
+ call term_wait(buf, 100)
+ call term_sendkeys(buf, "Gi\<C-X>\<C-O>")
+ call term_wait(buf, 200)
+ call term_sendkeys(buf, "\<C-N>")
+ call VerifyScreenDump(buf, 'Test_pum_with_preview_win', {})
+
+ call term_sendkeys(buf, "\<Esc>")
+ call StopVimInTerminal(buf)
+ call delete('Xpreviewscript')
+endfunc
+
" Test for inserting the tag search pattern in insert mode
func Test_ins_compl_tag_sft()
call writefile([
@@ -682,6 +717,42 @@ func Test_complete_func_error()
call assert_equal([], complete_info(['items']).items)
endfunc
+" Test for recursively starting completion mode using complete()
+func Test_recursive_complete_func()
+ func ListColors()
+ call complete(5, ["red", "blue"])
+ return ''
+ endfunc
+ new
+ call setline(1, ['a1', 'a2'])
+ set complete=.
+ exe "normal Goa\<C-X>\<C-L>\<C-R>=ListColors()\<CR>\<C-N>"
+ call assert_equal('a2blue', getline(3))
+ delfunc ListColors
+ bw!
+endfunc
+
+" Test for using complete() with completeopt+=longest
+func Test_complete_with_longest()
+ new
+ inoremap <buffer> <f3> <cmd>call complete(1, ["iaax", "iaay", "iaaz"])<cr>
+
+ " default: insert first match
+ set completeopt&
+ call setline(1, ['i'])
+ exe "normal Aa\<f3>\<esc>"
+ call assert_equal('iaax', getline(1))
+
+ " with longest: insert longest prefix
+ set completeopt+=longest
+ call setline(1, ['i'])
+ exe "normal Aa\<f3>\<esc>"
+ call assert_equal('iaa', getline(1))
+ set completeopt&
+ bwipe!
+endfunc
+
+
" Test for completing words following a completed word in a line
func Test_complete_wrapscan()
" complete words from another buffer
@@ -721,6 +792,17 @@ func Test_complete_across_line()
close!
endfunc
+" Test for completing words with a '.' at the end of a word.
+func Test_complete_joinspaces()
+ new
+ call setline(1, ['one two.', 'three. four'])
+ set joinspaces
+ exe "normal Goon\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>"
+ call assert_equal("one two. three. four", getline(3))
+ set joinspaces&
+ bw!
+endfunc
+
" Test for using CTRL-L to add one character when completing matching
func Test_complete_add_onechar()
new
@@ -741,6 +823,39 @@ func Test_complete_add_onechar()
close!
endfunc
+" Test for using CTRL-X CTRL-L to complete whole lines lines
+func Test_complete_wholeline()
+ new
+ " complete one-line
+ call setline(1, ['a1', 'a2'])
+ exe "normal ggoa\<C-X>\<C-L>"
+ call assert_equal(['a1', 'a1', 'a2'], getline(1, '$'))
+ " go to the next match (wrapping around the buffer)
+ exe "normal 2GCa\<C-X>\<C-L>\<C-N>"
+ call assert_equal(['a1', 'a', 'a2'], getline(1, '$'))
+ " go to the next match
+ exe "normal 2GCa\<C-X>\<C-L>\<C-N>\<C-N>"
+ call assert_equal(['a1', 'a2', 'a2'], getline(1, '$'))
+ exe "normal 2GCa\<C-X>\<C-L>\<C-N>\<C-N>\<C-N>"
+ call assert_equal(['a1', 'a1', 'a2'], getline(1, '$'))
+ " repeat the test using CTRL-L
+ " go to the next match (wrapping around the buffer)
+ exe "normal 2GCa\<C-X>\<C-L>\<C-L>"
+ call assert_equal(['a1', 'a2', 'a2'], getline(1, '$'))
+ " go to the next match
+ exe "normal 2GCa\<C-X>\<C-L>\<C-L>\<C-L>"
+ call assert_equal(['a1', 'a', 'a2'], getline(1, '$'))
+ exe "normal 2GCa\<C-X>\<C-L>\<C-L>\<C-L>\<C-L>"
+ call assert_equal(['a1', 'a1', 'a2'], getline(1, '$'))
+ %d
+ " use CTRL-X CTRL-L to add one more line
+ call setline(1, ['a1', 'b1'])
+ setlocal complete=.
+ exe "normal ggOa\<C-X>\<C-L>\<C-X>\<C-L>\<C-X>\<C-L>"
+ call assert_equal(['a1', 'b1', '', 'a1', 'b1'], getline(1, '$'))
+ bw!
+endfunc
+
" Test insert completion with 'cindent' (adjust the indent)
func Test_complete_with_cindent()
new
@@ -829,6 +944,25 @@ func Test_complete_stop()
close!
endfunc
+" Test for typing CTRL-R in insert completion mode to insert a register
+" content.
+func Test_complete_reginsert()
+ new
+ call setline(1, ['a1', 'a12', 'a123', 'a1234'])
+
+ " if a valid CTRL-X mode key is returned from <C-R>=, then it should be
+ " processed. Otherwise, CTRL-X mode should be stopped and the key should be
+ " inserted.
+ exe "normal Goa\<C-P>\<C-R>=\"\\<C-P>\"\<CR>"
+ call assert_equal('a123', getline(5))
+ let @r = "\<C-P>\<C-P>"
+ exe "normal GCa\<C-P>\<C-R>r"
+ call assert_equal('a12', getline(5))
+ exe "normal GCa\<C-P>\<C-R>=\"x\"\<CR>"
+ call assert_equal('a1234x', getline(5))
+ bw!
+endfunc
+
func Test_issue_7021()
CheckMSWindows
@@ -842,6 +976,322 @@ func Test_issue_7021()
set completeslash=
endfunc
+" Test for 'longest' setting in 'completeopt' with latin1 and utf-8 encodings
+func Test_complete_longest_match()
+ " for e in ['latin1', 'utf-8']
+ for e in ['utf-8']
+ exe 'set encoding=' .. e
+ new
+ set complete=.
+ set completeopt=menu,longest
+ call setline(1, ['pfx_a1', 'pfx_a12', 'pfx_a123', 'pfx_b1'])
+ exe "normal Gopfx\<C-P>"
+ call assert_equal('pfx_', getline(5))
+ bw!
+ endfor
+
+ " Test for completing additional words with longest match set
+ new
+ call setline(1, ['abc1', 'abd2'])
+ exe "normal Goab\<C-P>\<C-X>\<C-P>"
+ call assert_equal('ab', getline(3))
+ bw!
+ set complete& completeopt&
+endfunc
+
+" Test for removing the first displayed completion match and selecting the
+" match just before that.
+func Test_complete_erase_firstmatch()
+ new
+ call setline(1, ['a12', 'a34', 'a56'])
+ set complete=.
+ exe "normal Goa\<C-P>\<BS>\<BS>3\<CR>"
+ call assert_equal('a34', getline('$'))
+ set complete&
+ bw!
+endfunc
+
+" Test for completing words from unloaded buffers
+func Test_complete_from_unloadedbuf()
+ call writefile(['abc'], "Xfile1")
+ call writefile(['def'], "Xfile2")
+ edit Xfile1
+ edit Xfile2
+ new | close
+ enew
+ bunload Xfile1 Xfile2
+ set complete=u
+ " complete from an unloaded buffer
+ exe "normal! ia\<C-P>"
+ call assert_equal('abc', getline(1))
+ exe "normal! od\<C-P>"
+ call assert_equal('def', getline(2))
+ set complete&
+ %bw!
+ call delete("Xfile1")
+ call delete("Xfile2")
+endfunc
+
+" Test for completing whole lines from unloaded buffers
+func Test_complete_wholeline_unloadedbuf()
+ call writefile(['a line1', 'a line2', 'a line3'], "Xfile1")
+ edit Xfile1
+ enew
+ set complete=u
+ exe "normal! ia\<C-X>\<C-L>\<C-P>"
+ call assert_equal('a line2', getline(1))
+ %d
+ " completing from an unlisted buffer should fail
+ bdel Xfile1
+ exe "normal! ia\<C-X>\<C-L>\<C-P>"
+ call assert_equal('a', getline(1))
+ set complete&
+ %bw!
+ call delete("Xfile1")
+endfunc
+
+" Test for completing words from unlisted buffers
+func Test_complete_from_unlistedbuf()
+ call writefile(['abc'], "Xfile1")
+ call writefile(['def'], "Xfile2")
+ edit Xfile1
+ edit Xfile2
+ new | close
+ bdel Xfile1 Xfile2
+ set complete=U
+ " complete from an unlisted buffer
+ exe "normal! ia\<C-P>"
+ call assert_equal('abc', getline(1))
+ exe "normal! od\<C-P>"
+ call assert_equal('def', getline(2))
+ set complete&
+ %bw!
+ call delete("Xfile1")
+ call delete("Xfile2")
+endfunc
+
+" Test for completing whole lines from unlisted buffers
+func Test_complete_wholeline_unlistedbuf()
+ call writefile(['a line1', 'a line2', 'a line3'], "Xfile1")
+ edit Xfile1
+ enew
+ set complete=U
+ " completing from a unloaded buffer should fail
+ exe "normal! ia\<C-X>\<C-L>\<C-P>"
+ call assert_equal('a', getline(1))
+ %d
+ bdel Xfile1
+ exe "normal! ia\<C-X>\<C-L>\<C-P>"
+ call assert_equal('a line2', getline(1))
+ set complete&
+ %bw!
+ call delete("Xfile1")
+endfunc
+
+" Test for adding a multibyte character using CTRL-L in completion mode
+func Test_complete_mbyte_char_add()
+ new
+ set complete=.
+ call setline(1, 'abė')
+ exe "normal! oa\<C-P>\<BS>\<BS>\<C-L>\<C-L>"
+ call assert_equal('abė', getline(2))
+ " Test for a leader with multibyte character
+ %d
+ call setline(1, 'abėĕ')
+ exe "normal! oabė\<C-P>"
+ call assert_equal('abėĕ', getline(2))
+ bw!
+endfunc
+
+" Test for using <C-X><C-P> for local expansion even if 'complete' is set to
+" not to complete matches from the local buffer. Also test using multiple
+" <C-X> to cancel the current completion mode.
+func Test_complete_local_expansion()
+ new
+ set complete=t
+ call setline(1, ['abc', 'def'])
+ exe "normal! Go\<C-X>\<C-P>"
+ call assert_equal("def", getline(3))
+ exe "normal! Go\<C-P>"
+ call assert_equal("", getline(4))
+ exe "normal! Go\<C-X>\<C-N>"
+ call assert_equal("abc", getline(5))
+ exe "normal! Go\<C-N>"
+ call assert_equal("", getline(6))
+
+ " use multiple <C-X> to cancel the previous completion mode
+ exe "normal! Go\<C-P>\<C-X>\<C-P>"
+ call assert_equal("", getline(7))
+ exe "normal! Go\<C-P>\<C-X>\<C-X>\<C-P>"
+ call assert_equal("", getline(8))
+ exe "normal! Go\<C-P>\<C-X>\<C-X>\<C-X>\<C-P>"
+ call assert_equal("abc", getline(9))
+
+ " interrupt the current completion mode
+ set completeopt=menu,noinsert
+ exe "normal! Go\<C-X>\<C-F>\<C-X>\<C-X>\<C-P>\<C-Y>"
+ call assert_equal("abc", getline(10))
+
+ " when only one <C-X> is used to interrupt, do normal expansion
+ exe "normal! Go\<C-X>\<C-F>\<C-X>\<C-P>"
+ call assert_equal("", getline(11))
+ set completeopt&
+
+ " using two <C-X> in non-completion mode and restarting the same mode
+ exe "normal! God\<C-X>\<C-X>\<C-P>\<C-X>\<C-X>\<C-P>\<C-Y>"
+ call assert_equal("def", getline(12))
+
+ " test for adding a match from the original empty text
+ %d
+ call setline(1, 'abc def g')
+ exe "normal! o\<C-X>\<C-P>\<C-N>\<C-X>\<C-P>"
+ call assert_equal('def', getline(2))
+ exe "normal! 0C\<C-X>\<C-N>\<C-P>\<C-X>\<C-N>"
+ call assert_equal('abc', getline(2))
+
+ bw!
+endfunc
+
+" Test for undoing changes after a insert-mode completion
+func Test_complete_undo()
+ new
+ set complete=.
+ " undo with 'ignorecase'
+ call setline(1, ['ABOVE', 'BELOW'])
+ set ignorecase
+ exe "normal! Goab\<C-G>u\<C-P>"
+ call assert_equal("ABOVE", getline(3))
+ undo
+ call assert_equal("ab", getline(3))
+ set ignorecase&
+ %d
+ " undo with longest match
+ set completeopt=menu,longest
+ call setline(1, ['above', 'about'])
+ exe "normal! Goa\<C-G>u\<C-P>"
+ call assert_equal("abo", getline(3))
+ undo
+ call assert_equal("a", getline(3))
+ set completeopt&
+ %d
+ " undo for line completion
+ call setline(1, ['above that change', 'below that change'])
+ exe "normal! Goabove\<C-G>u\<C-X>\<C-L>"
+ call assert_equal("above that change", getline(3))
+ undo
+ call assert_equal("above", getline(3))
+
+ bw!
+endfunc
+
+" Test for completing a very long word
+func Test_complete_long_word()
+ set complete&
+ new
+ call setline(1, repeat('x', 950) .. ' one two three')
+ exe "normal! Gox\<C-X>\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>"
+ call assert_equal(repeat('x', 950) .. ' one two three', getline(2))
+ %d
+ " should fail when more than 950 characters are in a word
+ call setline(1, repeat('x', 951) .. ' one two three')
+ exe "normal! Gox\<C-X>\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>"
+ call assert_equal(repeat('x', 951), getline(2))
+
+ " Test for adding a very long word to an existing completion
+ %d
+ call setline(1, ['abc', repeat('x', 1016) .. '012345'])
+ exe "normal! Goab\<C-P>\<C-X>\<C-P>"
+ call assert_equal('abc ' .. repeat('x', 1016) .. '0123', getline(3))
+ bw!
+endfunc
+
+" Test for some fields in the complete items used by complete()
+func Test_complete_items()
+ func CompleteItems(idx)
+ let items = [[#{word: "one", dup: 1, user_data: 'u1'}, #{word: "one", dup: 1, user_data: 'u2'}],
+ \ [#{word: "one", dup: 0, user_data: 'u3'}, #{word: "one", dup: 0, user_data: 'u4'}],
+ \ [#{word: "one", icase: 1, user_data: 'u7'}, #{word: "oNE", icase: 1, user_data: 'u8'}],
+ \ [#{user_data: 'u9'}],
+ \ [#{word: "", user_data: 'u10'}],
+ \ [#{word: "", empty: 1, user_data: 'u11'}]]
+ call complete(col('.'), items[a:idx])
+ return ''
+ endfunc
+ new
+ exe "normal! i\<C-R>=CompleteItems(0)\<CR>\<C-N>\<C-Y>"
+ call assert_equal('u2', v:completed_item.user_data)
+ call assert_equal('one', getline(1))
+ exe "normal! o\<C-R>=CompleteItems(1)\<CR>\<C-Y>"
+ call assert_equal('u3', v:completed_item.user_data)
+ call assert_equal('one', getline(2))
+ exe "normal! o\<C-R>=CompleteItems(1)\<CR>\<C-N>"
+ call assert_equal('', getline(3))
+ set completeopt=menu,noinsert
+ exe "normal! o\<C-R>=CompleteItems(2)\<CR>one\<C-N>\<C-Y>"
+ call assert_equal('oNE', getline(4))
+ call assert_equal('u8', v:completed_item.user_data)
+ set completeopt&
+ exe "normal! o\<C-R>=CompleteItems(3)\<CR>"
+ call assert_equal('', getline(5))
+ exe "normal! o\<C-R>=CompleteItems(4)\<CR>"
+ call assert_equal('', getline(6))
+ exe "normal! o\<C-R>=CompleteItems(5)\<CR>"
+ call assert_equal('', getline(7))
+ call assert_equal('u11', v:completed_item.user_data)
+ " pass invalid argument to complete()
+ let cmd = "normal! o\<C-R>=complete(1, [[]])\<CR>"
+ call assert_fails('exe cmd', 'E730:')
+ bw!
+ delfunc CompleteItems
+endfunc
+
+" Test for the "refresh" item in the dict returned by an insert completion
+" function
+func Test_complete_item_refresh_always()
+ let g:CallCount = 0
+ func! Tcomplete(findstart, base)
+ if a:findstart
+ " locate the start of the word
+ let line = getline('.')
+ let start = col('.') - 1
+ while start > 0 && line[start - 1] =~ '\a'
+ let start -= 1
+ endwhile
+ return start
+ else
+ let g:CallCount += 1
+ let res = ["update1", "update12", "update123"]
+ return #{words: res, refresh: 'always'}
+ endif
+ endfunc
+ new
+ set completeopt=menu,longest
+ set completefunc=Tcomplete
+ exe "normal! iup\<C-X>\<C-U>\<BS>\<BS>\<BS>\<BS>\<BS>"
+ call assert_equal('up', getline(1))
+ call assert_equal(2, g:CallCount)
+ set completeopt&
+ set completefunc&
+ bw!
+ delfunc Tcomplete
+endfunc
+
+" Test for completing from a thesaurus file without read permission
+func Test_complete_unreadable_thesaurus_file()
+ CheckUnix
+ CheckNotRoot
+
+ call writefile(['about', 'above'], 'Xfile')
+ call setfperm('Xfile', '---r--r--')
+ new
+ set complete=sXfile
+ exe "normal! ia\<C-P>"
+ call assert_equal('a', getline(1))
+ bw!
+ call delete('Xfile')
+ set complete&
+endfunc
+
" Test to ensure 'Scanning...' messages are not recorded in messages history
func Test_z1_complete_no_history()
new
@@ -857,7 +1307,7 @@ endfunc
" A mapping is not used for the key after CTRL-X.
func Test_no_mapping_for_ctrl_x_key()
new
- inoremap <C-K> <Cmd>let was_mapped = 'yes'<CR>
+ inoremap <buffer> <C-K> <Cmd>let was_mapped = 'yes'<CR>
setlocal dictionary=README.txt
call feedkeys("aexam\<C-X>\<C-K> ", 'xt')
call assert_equal('example ', getline(1))
@@ -865,6 +1315,813 @@ func Test_no_mapping_for_ctrl_x_key()
bwipe!
endfunc
+" Test for different ways of setting the 'completefunc' option
+func Test_completefunc_callback()
+ func CompleteFunc1(callnr, findstart, base)
+ call add(g:CompleteFunc1Args, [a:callnr, a:findstart, a:base])
+ return a:findstart ? 0 : []
+ endfunc
+ func CompleteFunc2(findstart, base)
+ call add(g:CompleteFunc2Args, [a:findstart, a:base])
+ return a:findstart ? 0 : []
+ endfunc
+
+ let lines =<< trim END
+ #" Test for using a global function name
+ LET &completefunc = 'g:CompleteFunc2'
+ new
+ call setline(1, 'global')
+ LET g:CompleteFunc2Args = []
+ call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
+ call assert_equal([[1, ''], [0, 'global']], g:CompleteFunc2Args)
+ bw!
+
+ #" Test for using a function()
+ set completefunc=function('g:CompleteFunc1',\ [10])
+ new
+ call setline(1, 'one')
+ LET g:CompleteFunc1Args = []
+ call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
+ call assert_equal([[10, 1, ''], [10, 0, 'one']], g:CompleteFunc1Args)
+ bw!
+
+ #" Using a funcref variable to set 'completefunc'
+ VAR Fn = function('g:CompleteFunc1', [11])
+ LET &completefunc = Fn
+ new
+ call setline(1, 'two')
+ LET g:CompleteFunc1Args = []
+ call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
+ call assert_equal([[11, 1, ''], [11, 0, 'two']], g:CompleteFunc1Args)
+ bw!
+
+ #" Using string(funcref_variable) to set 'completefunc'
+ LET Fn = function('g:CompleteFunc1', [12])
+ LET &completefunc = string(Fn)
+ new
+ call setline(1, 'two')
+ LET g:CompleteFunc1Args = []
+ call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
+ call assert_equal([[12, 1, ''], [12, 0, 'two']], g:CompleteFunc1Args)
+ bw!
+
+ #" Test for using a funcref()
+ set completefunc=funcref('g:CompleteFunc1',\ [13])
+ new
+ call setline(1, 'three')
+ LET g:CompleteFunc1Args = []
+ call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
+ call assert_equal([[13, 1, ''], [13, 0, 'three']], g:CompleteFunc1Args)
+ bw!
+
+ #" Using a funcref variable to set 'completefunc'
+ LET Fn = funcref('g:CompleteFunc1', [14])
+ LET &completefunc = Fn
+ new
+ call setline(1, 'four')
+ LET g:CompleteFunc1Args = []
+ call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
+ call assert_equal([[14, 1, ''], [14, 0, 'four']], g:CompleteFunc1Args)
+ bw!
+
+ #" Using a string(funcref_variable) to set 'completefunc'
+ LET Fn = funcref('g:CompleteFunc1', [15])
+ LET &completefunc = string(Fn)
+ new
+ call setline(1, 'four')
+ LET g:CompleteFunc1Args = []
+ call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
+ call assert_equal([[15, 1, ''], [15, 0, 'four']], g:CompleteFunc1Args)
+ bw!
+
+ #" Test for using a lambda function with set
+ VAR optval = "LSTART a, b LMIDDLE CompleteFunc1(16, a, b) LEND"
+ LET optval = substitute(optval, ' ', '\\ ', 'g')
+ exe "set completefunc=" .. optval
+ new
+ call setline(1, 'five')
+ LET g:CompleteFunc1Args = []
+ call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
+ call assert_equal([[16, 1, ''], [16, 0, 'five']], g:CompleteFunc1Args)
+ bw!
+
+ #" Set 'completefunc' to a lambda expression
+ LET &completefunc = LSTART a, b LMIDDLE CompleteFunc1(17, a, b) LEND
+ new
+ call setline(1, 'six')
+ LET g:CompleteFunc1Args = []
+ call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
+ call assert_equal([[17, 1, ''], [17, 0, 'six']], g:CompleteFunc1Args)
+ bw!
+
+ #" Set 'completefunc' to string(lambda_expression)
+ LET &completefunc = 'LSTART a, b LMIDDLE CompleteFunc1(18, a, b) LEND'
+ new
+ call setline(1, 'six')
+ LET g:CompleteFunc1Args = []
+ call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
+ call assert_equal([[18, 1, ''], [18, 0, 'six']], g:CompleteFunc1Args)
+ bw!
+
+ #" Set 'completefunc' to a variable with a lambda expression
+ VAR Lambda = LSTART a, b LMIDDLE CompleteFunc1(19, a, b) LEND
+ LET &completefunc = Lambda
+ new
+ call setline(1, 'seven')
+ LET g:CompleteFunc1Args = []
+ call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
+ call assert_equal([[19, 1, ''], [19, 0, 'seven']], g:CompleteFunc1Args)
+ bw!
+
+ #" Set 'completefunc' to a string(variable with a lambda expression)
+ LET Lambda = LSTART a, b LMIDDLE CompleteFunc1(20, a, b) LEND
+ LET &completefunc = string(Lambda)
+ new
+ call setline(1, 'seven')
+ LET g:CompleteFunc1Args = []
+ call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
+ call assert_equal([[20, 1, ''], [20, 0, 'seven']], g:CompleteFunc1Args)
+ bw!
+
+ #" Test for using a lambda function with incorrect return value
+ LET Lambda = LSTART a, b LMIDDLE strlen(a) LEND
+ LET &completefunc = Lambda
+ new
+ call setline(1, 'eight')
+ call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
+ bw!
+
+ #" Test for clearing the 'completefunc' option
+ set completefunc=''
+ set completefunc&
+ call assert_fails("set completefunc=function('abc')", "E700:")
+ call assert_fails("set completefunc=funcref('abc')", "E700:")
+
+ #" set 'completefunc' to a non-existing function
+ set completefunc=CompleteFunc2
+ call setline(1, 'five')
+ call assert_fails("set completefunc=function('NonExistingFunc')", 'E700:')
+ call assert_fails("LET &completefunc = function('NonExistingFunc')", 'E700:')
+ LET g:CompleteFunc2Args = []
+ call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
+ call assert_equal([[1, ''], [0, 'five']], g:CompleteFunc2Args)
+ bw!
+ END
+ call CheckLegacyAndVim9Success(lines)
+
+ " Test for using a script-local function name
+ func s:CompleteFunc3(findstart, base)
+ call add(g:CompleteFunc3Args, [a:findstart, a:base])
+ return a:findstart ? 0 : []
+ endfunc
+ set completefunc=s:CompleteFunc3
+ new
+ call setline(1, 'script1')
+ let g:CompleteFunc3Args = []
+ call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
+ call assert_equal([[1, ''], [0, 'script1']], g:CompleteFunc3Args)
+ bw!
+
+ let &completefunc = 's:CompleteFunc3'
+ new
+ call setline(1, 'script2')
+ let g:CompleteFunc3Args = []
+ call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
+ call assert_equal([[1, ''], [0, 'script2']], g:CompleteFunc3Args)
+ bw!
+ delfunc s:CompleteFunc3
+
+ " invalid return value
+ let &completefunc = {a -> 'abc'}
+ call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
+
+ " Using Vim9 lambda expression in legacy context should fail
+ " set completefunc=(a,\ b)\ =>\ CompleteFunc1(21,\ a,\ b)
+ new | only
+ let g:CompleteFunc1Args = []
+ " call assert_fails('call feedkeys("A\<C-X>\<C-U>\<Esc>", "x")', 'E117:')
+ call assert_equal([], g:CompleteFunc1Args)
+
+ " set 'completefunc' to a partial with dict. This used to cause a crash.
+ func SetCompleteFunc()
+ let params = {'complete': function('g:DictCompleteFunc')}
+ let &completefunc = params.complete
+ endfunc
+ func g:DictCompleteFunc(_) dict
+ endfunc
+ call SetCompleteFunc()
+ new
+ call SetCompleteFunc()
+ bw
+ call test_garbagecollect_now()
+ new
+ set completefunc=
+ wincmd w
+ set completefunc=
+ %bw!
+ delfunc g:DictCompleteFunc
+ delfunc SetCompleteFunc
+
+ " Vim9 tests
+ let lines =<< trim END
+ vim9script
+
+ def Vim9CompleteFunc(callnr: number, findstart: number, base: string): any
+ add(g:Vim9completeFuncArgs, [callnr, findstart, base])
+ return findstart ? 0 : []
+ enddef
+
+ # Test for using a def function with completefunc
+ set completefunc=function('Vim9CompleteFunc',\ [60])
+ new | only
+ setline(1, 'one')
+ g:Vim9completeFuncArgs = []
+ feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
+ assert_equal([[60, 1, ''], [60, 0, 'one']], g:Vim9completeFuncArgs)
+ bw!
+
+ # Test for using a global function name
+ &completefunc = g:CompleteFunc2
+ new | only
+ setline(1, 'two')
+ g:CompleteFunc2Args = []
+ feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
+ assert_equal([[1, ''], [0, 'two']], g:CompleteFunc2Args)
+ bw!
+
+ # Test for using a script-local function name
+ def s:LocalCompleteFunc(findstart: number, base: string): any
+ add(g:LocalCompleteFuncArgs, [findstart, base])
+ return findstart ? 0 : []
+ enddef
+ &completefunc = s:LocalCompleteFunc
+ new | only
+ setline(1, 'three')
+ g:LocalCompleteFuncArgs = []
+ feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
+ assert_equal([[1, ''], [0, 'three']], g:LocalCompleteFuncArgs)
+ bw!
+ END
+ call CheckScriptSuccess(lines)
+
+ " cleanup
+ set completefunc&
+ delfunc CompleteFunc1
+ delfunc CompleteFunc2
+ unlet g:CompleteFunc1Args g:CompleteFunc2Args
+ %bw!
+endfunc
+
+" Test for different ways of setting the 'omnifunc' option
+func Test_omnifunc_callback()
+ func OmniFunc1(callnr, findstart, base)
+ call add(g:OmniFunc1Args, [a:callnr, a:findstart, a:base])
+ return a:findstart ? 0 : []
+ endfunc
+ func OmniFunc2(findstart, base)
+ call add(g:OmniFunc2Args, [a:findstart, a:base])
+ return a:findstart ? 0 : []
+ endfunc
+
+ let lines =<< trim END
+ #" Test for using a function name
+ LET &omnifunc = 'g:OmniFunc2'
+ new
+ call setline(1, 'zero')
+ LET g:OmniFunc2Args = []
+ call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
+ call assert_equal([[1, ''], [0, 'zero']], g:OmniFunc2Args)
+ bw!
+
+ #" Test for using a function()
+ set omnifunc=function('g:OmniFunc1',\ [10])
+ new
+ call setline(1, 'one')
+ LET g:OmniFunc1Args = []
+ call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
+ call assert_equal([[10, 1, ''], [10, 0, 'one']], g:OmniFunc1Args)
+ bw!
+
+ #" Using a funcref variable to set 'omnifunc'
+ VAR Fn = function('g:OmniFunc1', [11])
+ LET &omnifunc = Fn
+ new
+ call setline(1, 'two')
+ LET g:OmniFunc1Args = []
+ call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
+ call assert_equal([[11, 1, ''], [11, 0, 'two']], g:OmniFunc1Args)
+ bw!
+
+ #" Using a string(funcref_variable) to set 'omnifunc'
+ LET Fn = function('g:OmniFunc1', [12])
+ LET &omnifunc = string(Fn)
+ new
+ call setline(1, 'two')
+ LET g:OmniFunc1Args = []
+ call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
+ call assert_equal([[12, 1, ''], [12, 0, 'two']], g:OmniFunc1Args)
+ bw!
+
+ #" Test for using a funcref()
+ set omnifunc=funcref('g:OmniFunc1',\ [13])
+ new
+ call setline(1, 'three')
+ LET g:OmniFunc1Args = []
+ call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
+ call assert_equal([[13, 1, ''], [13, 0, 'three']], g:OmniFunc1Args)
+ bw!
+
+ #" Use let to set 'omnifunc' to a funcref
+ LET Fn = funcref('g:OmniFunc1', [14])
+ LET &omnifunc = Fn
+ new
+ call setline(1, 'four')
+ LET g:OmniFunc1Args = []
+ call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
+ call assert_equal([[14, 1, ''], [14, 0, 'four']], g:OmniFunc1Args)
+ bw!
+
+ #" Using a string(funcref) to set 'omnifunc'
+ LET Fn = funcref("g:OmniFunc1", [15])
+ LET &omnifunc = string(Fn)
+ new
+ call setline(1, 'four')
+ LET g:OmniFunc1Args = []
+ call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
+ call assert_equal([[15, 1, ''], [15, 0, 'four']], g:OmniFunc1Args)
+ bw!
+
+ #" Test for using a lambda function with set
+ VAR optval = "LSTART a, b LMIDDLE OmniFunc1(16, a, b) LEND"
+ LET optval = substitute(optval, ' ', '\\ ', 'g')
+ exe "set omnifunc=" .. optval
+ new
+ call setline(1, 'five')
+ LET g:OmniFunc1Args = []
+ call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
+ call assert_equal([[16, 1, ''], [16, 0, 'five']], g:OmniFunc1Args)
+ bw!
+
+ #" Set 'omnifunc' to a lambda expression
+ LET &omnifunc = LSTART a, b LMIDDLE OmniFunc1(17, a, b) LEND
+ new
+ call setline(1, 'six')
+ LET g:OmniFunc1Args = []
+ call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
+ call assert_equal([[17, 1, ''], [17, 0, 'six']], g:OmniFunc1Args)
+ bw!
+
+ #" Set 'omnifunc' to a string(lambda_expression)
+ LET &omnifunc = 'LSTART a, b LMIDDLE OmniFunc1(18, a, b) LEND'
+ new
+ call setline(1, 'six')
+ LET g:OmniFunc1Args = []
+ call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
+ call assert_equal([[18, 1, ''], [18, 0, 'six']], g:OmniFunc1Args)
+ bw!
+
+ #" Set 'omnifunc' to a variable with a lambda expression
+ VAR Lambda = LSTART a, b LMIDDLE OmniFunc1(19, a, b) LEND
+ LET &omnifunc = Lambda
+ new
+ call setline(1, 'seven')
+ LET g:OmniFunc1Args = []
+ call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
+ call assert_equal([[19, 1, ''], [19, 0, 'seven']], g:OmniFunc1Args)
+ bw!
+
+ #" Set 'omnifunc' to a string(variable with a lambda expression)
+ LET Lambda = LSTART a, b LMIDDLE OmniFunc1(20, a, b) LEND
+ LET &omnifunc = string(Lambda)
+ new
+ call setline(1, 'seven')
+ LET g:OmniFunc1Args = []
+ call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
+ call assert_equal([[20, 1, ''], [20, 0, 'seven']], g:OmniFunc1Args)
+ bw!
+
+ #" Test for using a lambda function with incorrect return value
+ LET Lambda = LSTART a, b LMIDDLE strlen(a) LEND
+ LET &omnifunc = Lambda
+ new
+ call setline(1, 'eight')
+ call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
+ bw!
+
+ #" Test for clearing the 'omnifunc' option
+ set omnifunc=''
+ set omnifunc&
+ call assert_fails("set omnifunc=function('abc')", "E700:")
+ call assert_fails("set omnifunc=funcref('abc')", "E700:")
+
+ #" set 'omnifunc' to a non-existing function
+ set omnifunc=OmniFunc2
+ call setline(1, 'nine')
+ call assert_fails("set omnifunc=function('NonExistingFunc')", 'E700:')
+ call assert_fails("LET &omnifunc = function('NonExistingFunc')", 'E700:')
+ LET g:OmniFunc2Args = []
+ call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
+ call assert_equal([[1, ''], [0, 'nine']], g:OmniFunc2Args)
+ bw!
+ END
+ call CheckLegacyAndVim9Success(lines)
+
+ " Test for using a script-local function name
+ func s:OmniFunc3(findstart, base)
+ call add(g:OmniFunc3Args, [a:findstart, a:base])
+ return a:findstart ? 0 : []
+ endfunc
+ set omnifunc=s:OmniFunc3
+ new
+ call setline(1, 'script1')
+ let g:OmniFunc3Args = []
+ call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
+ call assert_equal([[1, ''], [0, 'script1']], g:OmniFunc3Args)
+ bw!
+
+ let &omnifunc = 's:OmniFunc3'
+ new
+ call setline(1, 'script2')
+ let g:OmniFunc3Args = []
+ call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
+ call assert_equal([[1, ''], [0, 'script2']], g:OmniFunc3Args)
+ bw!
+ delfunc s:OmniFunc3
+
+ " invalid return value
+ let &omnifunc = {a -> 'abc'}
+ call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
+
+ " Using Vim9 lambda expression in legacy context should fail
+ " set omnifunc=(a,\ b)\ =>\ OmniFunc1(21,\ a,\ b)
+ new | only
+ let g:OmniFunc1Args = []
+ " call assert_fails('call feedkeys("A\<C-X>\<C-O>\<Esc>", "x")', 'E117:')
+ call assert_equal([], g:OmniFunc1Args)
+
+ " set 'omnifunc' to a partial with dict. This used to cause a crash.
+ func SetOmniFunc()
+ let params = {'omni': function('g:DictOmniFunc')}
+ let &omnifunc = params.omni
+ endfunc
+ func g:DictOmniFunc(_) dict
+ endfunc
+ call SetOmniFunc()
+ new
+ call SetOmniFunc()
+ bw
+ call test_garbagecollect_now()
+ new
+ set omnifunc=
+ wincmd w
+ set omnifunc=
+ %bw!
+ delfunc g:DictOmniFunc
+ delfunc SetOmniFunc
+
+ " Vim9 tests
+ let lines =<< trim END
+ vim9script
+
+ def Vim9omniFunc(callnr: number, findstart: number, base: string): any
+ add(g:Vim9omniFunc_Args, [callnr, findstart, base])
+ return findstart ? 0 : []
+ enddef
+
+ # Test for using a def function with omnifunc
+ set omnifunc=function('Vim9omniFunc',\ [60])
+ new | only
+ setline(1, 'one')
+ g:Vim9omniFunc_Args = []
+ feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
+ assert_equal([[60, 1, ''], [60, 0, 'one']], g:Vim9omniFunc_Args)
+ bw!
+
+ # Test for using a global function name
+ &omnifunc = g:OmniFunc2
+ new | only
+ setline(1, 'two')
+ g:OmniFunc2Args = []
+ feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
+ assert_equal([[1, ''], [0, 'two']], g:OmniFunc2Args)
+ bw!
+
+ # Test for using a script-local function name
+ def s:LocalOmniFunc(findstart: number, base: string): any
+ add(g:LocalOmniFuncArgs, [findstart, base])
+ return findstart ? 0 : []
+ enddef
+ &omnifunc = s:LocalOmniFunc
+ new | only
+ setline(1, 'three')
+ g:LocalOmniFuncArgs = []
+ feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
+ assert_equal([[1, ''], [0, 'three']], g:LocalOmniFuncArgs)
+ bw!
+ END
+ call CheckScriptSuccess(lines)
+
+ " cleanup
+ set omnifunc&
+ delfunc OmniFunc1
+ delfunc OmniFunc2
+ unlet g:OmniFunc1Args g:OmniFunc2Args
+ %bw!
+endfunc
+
+" Test for different ways of setting the 'thesaurusfunc' option
+func Test_thesaurusfunc_callback()
+ func TsrFunc1(callnr, findstart, base)
+ call add(g:TsrFunc1Args, [a:callnr, a:findstart, a:base])
+ return a:findstart ? 0 : []
+ endfunc
+ func TsrFunc2(findstart, base)
+ call add(g:TsrFunc2Args, [a:findstart, a:base])
+ return a:findstart ? 0 : ['sunday']
+ endfunc
+
+ let lines =<< trim END
+ #" Test for using a function name
+ LET &thesaurusfunc = 'g:TsrFunc2'
+ new
+ call setline(1, 'zero')
+ LET g:TsrFunc2Args = []
+ call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
+ call assert_equal([[1, ''], [0, 'zero']], g:TsrFunc2Args)
+ bw!
+
+ #" Test for using a function()
+ set thesaurusfunc=function('g:TsrFunc1',\ [10])
+ new
+ call setline(1, 'one')
+ LET g:TsrFunc1Args = []
+ call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
+ call assert_equal([[10, 1, ''], [10, 0, 'one']], g:TsrFunc1Args)
+ bw!
+
+ #" Using a funcref variable to set 'thesaurusfunc'
+ VAR Fn = function('g:TsrFunc1', [11])
+ LET &thesaurusfunc = Fn
+ new
+ call setline(1, 'two')
+ LET g:TsrFunc1Args = []
+ call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
+ call assert_equal([[11, 1, ''], [11, 0, 'two']], g:TsrFunc1Args)
+ bw!
+
+ #" Using a string(funcref_variable) to set 'thesaurusfunc'
+ LET Fn = function('g:TsrFunc1', [12])
+ LET &thesaurusfunc = string(Fn)
+ new
+ call setline(1, 'two')
+ LET g:TsrFunc1Args = []
+ call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
+ call assert_equal([[12, 1, ''], [12, 0, 'two']], g:TsrFunc1Args)
+ bw!
+
+ #" Test for using a funcref()
+ set thesaurusfunc=funcref('g:TsrFunc1',\ [13])
+ new
+ call setline(1, 'three')
+ LET g:TsrFunc1Args = []
+ call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
+ call assert_equal([[13, 1, ''], [13, 0, 'three']], g:TsrFunc1Args)
+ bw!
+
+ #" Using a funcref variable to set 'thesaurusfunc'
+ LET Fn = funcref('g:TsrFunc1', [14])
+ LET &thesaurusfunc = Fn
+ new
+ call setline(1, 'four')
+ LET g:TsrFunc1Args = []
+ call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
+ call assert_equal([[14, 1, ''], [14, 0, 'four']], g:TsrFunc1Args)
+ bw!
+
+ #" Using a string(funcref_variable) to set 'thesaurusfunc'
+ LET Fn = funcref('g:TsrFunc1', [15])
+ LET &thesaurusfunc = string(Fn)
+ new
+ call setline(1, 'four')
+ LET g:TsrFunc1Args = []
+ call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
+ call assert_equal([[15, 1, ''], [15, 0, 'four']], g:TsrFunc1Args)
+ bw!
+
+ #" Test for using a lambda function
+ VAR optval = "LSTART a, b LMIDDLE TsrFunc1(16, a, b) LEND"
+ LET optval = substitute(optval, ' ', '\\ ', 'g')
+ exe "set thesaurusfunc=" .. optval
+ new
+ call setline(1, 'five')
+ LET g:TsrFunc1Args = []
+ call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
+ call assert_equal([[16, 1, ''], [16, 0, 'five']], g:TsrFunc1Args)
+ bw!
+
+ #" Test for using a lambda function with set
+ LET &thesaurusfunc = LSTART a, b LMIDDLE TsrFunc1(17, a, b) LEND
+ new
+ call setline(1, 'six')
+ LET g:TsrFunc1Args = []
+ call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
+ call assert_equal([[17, 1, ''], [17, 0, 'six']], g:TsrFunc1Args)
+ bw!
+
+ #" Set 'thesaurusfunc' to a string(lambda expression)
+ LET &thesaurusfunc = 'LSTART a, b LMIDDLE TsrFunc1(18, a, b) LEND'
+ new
+ call setline(1, 'six')
+ LET g:TsrFunc1Args = []
+ call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
+ call assert_equal([[18, 1, ''], [18, 0, 'six']], g:TsrFunc1Args)
+ bw!
+
+ #" Set 'thesaurusfunc' to a variable with a lambda expression
+ VAR Lambda = LSTART a, b LMIDDLE TsrFunc1(19, a, b) LEND
+ LET &thesaurusfunc = Lambda
+ new
+ call setline(1, 'seven')
+ LET g:TsrFunc1Args = []
+ call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
+ call assert_equal([[19, 1, ''], [19, 0, 'seven']], g:TsrFunc1Args)
+ bw!
+
+ #" Set 'thesaurusfunc' to a string(variable with a lambda expression)
+ LET Lambda = LSTART a, b LMIDDLE TsrFunc1(20, a, b) LEND
+ LET &thesaurusfunc = string(Lambda)
+ new
+ call setline(1, 'seven')
+ LET g:TsrFunc1Args = []
+ call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
+ call assert_equal([[20, 1, ''], [20, 0, 'seven']], g:TsrFunc1Args)
+ bw!
+
+ #" Test for using a lambda function with incorrect return value
+ LET Lambda = LSTART a, b LMIDDLE strlen(a) LEND
+ LET &thesaurusfunc = Lambda
+ new
+ call setline(1, 'eight')
+ call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
+ bw!
+
+ #" Test for clearing the 'thesaurusfunc' option
+ set thesaurusfunc=''
+ set thesaurusfunc&
+ call assert_fails("set thesaurusfunc=function('abc')", "E700:")
+ call assert_fails("set thesaurusfunc=funcref('abc')", "E700:")
+
+ #" set 'thesaurusfunc' to a non-existing function
+ set thesaurusfunc=TsrFunc2
+ call setline(1, 'ten')
+ call assert_fails("set thesaurusfunc=function('NonExistingFunc')", 'E700:')
+ call assert_fails("LET &thesaurusfunc = function('NonExistingFunc')", 'E700:')
+ LET g:TsrFunc2Args = []
+ call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
+ call assert_equal([[1, ''], [0, 'ten']], g:TsrFunc2Args)
+ bw!
+
+ #" Use a buffer-local value and a global value
+ set thesaurusfunc&
+ setlocal thesaurusfunc=function('g:TsrFunc1',\ [22])
+ call setline(1, 'sun')
+ LET g:TsrFunc1Args = []
+ call feedkeys("A\<C-X>\<C-T>\<Esc>", "x")
+ call assert_equal('sun', getline(1))
+ call assert_equal([[22, 1, ''], [22, 0, 'sun']], g:TsrFunc1Args)
+ new
+ call setline(1, 'sun')
+ LET g:TsrFunc1Args = []
+ call feedkeys("A\<C-X>\<C-T>\<Esc>", "x")
+ call assert_equal('sun', getline(1))
+ call assert_equal([], g:TsrFunc1Args)
+ set thesaurusfunc=function('g:TsrFunc1',\ [23])
+ wincmd w
+ call setline(1, 'sun')
+ LET g:TsrFunc1Args = []
+ call feedkeys("A\<C-X>\<C-T>\<Esc>", "x")
+ call assert_equal('sun', getline(1))
+ call assert_equal([[22, 1, ''], [22, 0, 'sun']], g:TsrFunc1Args)
+ :%bw!
+ END
+ call CheckLegacyAndVim9Success(lines)
+
+ " Test for using a script-local function name
+ func s:TsrFunc3(findstart, base)
+ call add(g:TsrFunc3Args, [a:findstart, a:base])
+ return a:findstart ? 0 : []
+ endfunc
+ set tsrfu=s:TsrFunc3
+ new
+ call setline(1, 'script1')
+ let g:TsrFunc3Args = []
+ call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
+ call assert_equal([[1, ''], [0, 'script1']], g:TsrFunc3Args)
+ bw!
+
+ let &tsrfu = 's:TsrFunc3'
+ new
+ call setline(1, 'script2')
+ let g:TsrFunc3Args = []
+ call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
+ call assert_equal([[1, ''], [0, 'script2']], g:TsrFunc3Args)
+ bw!
+ delfunc s:TsrFunc3
+
+ " invalid return value
+ let &thesaurusfunc = {a -> 'abc'}
+ call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
+
+ " Using Vim9 lambda expression in legacy context should fail
+ " set thesaurusfunc=(a,\ b)\ =>\ TsrFunc1(21,\ a,\ b)
+ new | only
+ let g:TsrFunc1Args = []
+ " call assert_fails('call feedkeys("A\<C-X>\<C-T>\<Esc>", "x")', 'E117:')
+ call assert_equal([], g:TsrFunc1Args)
+ bw!
+
+ " set 'thesaurusfunc' to a partial with dict. This used to cause a crash.
+ func SetTsrFunc()
+ let params = {'thesaurus': function('g:DictTsrFunc')}
+ let &thesaurusfunc = params.thesaurus
+ endfunc
+ func g:DictTsrFunc(_) dict
+ endfunc
+ call SetTsrFunc()
+ new
+ call SetTsrFunc()
+ bw
+ call test_garbagecollect_now()
+ new
+ set thesaurusfunc=
+ wincmd w
+ %bw!
+ delfunc SetTsrFunc
+
+ " set buffer-local 'thesaurusfunc' to a partial with dict. This used to
+ " cause a crash.
+ func SetLocalTsrFunc()
+ let params = {'thesaurus': function('g:DictTsrFunc')}
+ let &l:thesaurusfunc = params.thesaurus
+ endfunc
+ call SetLocalTsrFunc()
+ call test_garbagecollect_now()
+ call SetLocalTsrFunc()
+ set thesaurusfunc=
+ bw!
+ delfunc g:DictTsrFunc
+ delfunc SetLocalTsrFunc
+
+ " Vim9 tests
+ let lines =<< trim END
+ vim9script
+
+ def Vim9tsrFunc(callnr: number, findstart: number, base: string): any
+ add(g:Vim9tsrFunc_Args, [callnr, findstart, base])
+ return findstart ? 0 : []
+ enddef
+
+ # Test for using a def function with thesaurusfunc
+ set thesaurusfunc=function('Vim9tsrFunc',\ [60])
+ new | only
+ setline(1, 'one')
+ g:Vim9tsrFunc_Args = []
+ feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
+ assert_equal([[60, 1, ''], [60, 0, 'one']], g:Vim9tsrFunc_Args)
+ bw!
+
+ # Test for using a global function name
+ &thesaurusfunc = g:TsrFunc2
+ new | only
+ setline(1, 'two')
+ g:TsrFunc2Args = []
+ feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
+ assert_equal([[1, ''], [0, 'two']], g:TsrFunc2Args)
+ bw!
+
+ # Test for using a script-local function name
+ def s:LocalTsrFunc(findstart: number, base: string): any
+ add(g:LocalTsrFuncArgs, [findstart, base])
+ return findstart ? 0 : []
+ enddef
+ &thesaurusfunc = s:LocalTsrFunc
+ new | only
+ setline(1, 'three')
+ g:LocalTsrFuncArgs = []
+ feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
+ assert_equal([[1, ''], [0, 'three']], g:LocalTsrFuncArgs)
+ bw!
+ END
+ call CheckScriptSuccess(lines)
+
+ " cleanup
+ set thesaurusfunc&
+ delfunc TsrFunc1
+ delfunc TsrFunc2
+ unlet g:TsrFunc1Args g:TsrFunc2Args
+ %bw!
+endfunc
+
func FooBarComplete(findstart, base)
if a:findstart
return col('.') - 1
@@ -884,4 +2141,52 @@ func Test_complete_smartindent()
delfunction! FooBarComplete
endfunc
+func Test_complete_overrun()
+ " this was going past the end of the copied text
+ new
+ sil norm si”0s0 
+ bwipe!
+endfunc
+
+func Test_infercase_very_long_line()
+ " this was truncating the line when inferring case
+ new
+ let longLine = "blah "->repeat(300)
+ let verylongLine = "blah "->repeat(400)
+ call setline(1, verylongLine)
+ call setline(2, longLine)
+ set ic infercase
+ exe "normal 2Go\<C-X>\<C-L>\<Esc>"
+ call assert_equal(longLine, getline(3))
+
+ " check that the too long text is NUL terminated
+ %del
+ norm o
+ norm 1987ax
+ exec "norm ox\<C-X>\<C-L>"
+ call assert_equal(repeat('x', 1987), getline(3))
+
+ bwipe!
+ set noic noinfercase
+endfunc
+
+func Test_ins_complete_add()
+ " this was reading past the end of allocated memory
+ new
+ norm o
+ norm 7o€€
+ sil! norm o
+
+ bwipe!
+endfunc
+
+func Test_ins_complete_end_of_line()
+ " this was reading past the end of the line
+ new
+ norm 8o€ý 
+ sil! norm o
+
+ bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_lambda.vim b/src/nvim/testdir/test_lambda.vim
index c1fe47d1c9..025eb016a8 100644
--- a/src/nvim/testdir/test_lambda.vim
+++ b/src/nvim/testdir/test_lambda.vim
@@ -62,7 +62,8 @@ endfunc
function Test_lambda_fails()
call assert_equal(3, {a, b -> a + b}(1, 2))
call assert_fails('echo {a, a -> a + a}(1, 2)', 'E853:')
- call assert_fails('echo {a, b -> a + b)}(1, 2)', 'E15:')
+ call assert_fails('echo {a, b -> a + b)}(1, 2)', 'E451:')
+ echo assert_fails('echo 10->{a -> a + 2}', 'E107:')
endfunc
func Test_not_lambda()
@@ -125,7 +126,7 @@ func Test_lambda_closure_counter()
endfunc
let l:F = s:foo()
- call garbagecollect()
+ call test_garbagecollect_now()
call assert_equal(1, l:F())
call assert_equal(2, l:F())
call assert_equal(3, l:F())
@@ -208,9 +209,9 @@ func Test_lambda_circular_reference()
endfunc
call s:Foo()
- call garbagecollect()
+ call test_garbagecollect_now()
let i = 0 | while i < 10000 | call s:Foo() | let i+= 1 | endwhile
- call garbagecollect()
+ call test_garbagecollect_now()
endfunc
func Test_lambda_combination()
@@ -239,11 +240,16 @@ func Test_closure_counter()
endfunc
let l:F = s:foo()
- call garbagecollect()
+ call test_garbagecollect_now()
call assert_equal(1, l:F())
call assert_equal(2, l:F())
call assert_equal(3, l:F())
call assert_equal(4, l:F())
+
+ call assert_match("^\n function <SNR>\\d\\+_bar() closure"
+ \ .. "\n1 let x += 1"
+ \ .. "\n2 return x"
+ \ .. "\n endfunction$", execute('func s:bar'))
endfunc
func Test_closure_unlet()
@@ -257,7 +263,7 @@ func Test_closure_unlet()
endfunc
call assert_false(has_key(s:foo(), 'x'))
- call garbagecollect()
+ call test_garbagecollect_now()
endfunc
func LambdaFoo()
@@ -294,7 +300,7 @@ func Test_named_function_closure()
endfunc
call Afoo()
call assert_equal(14, s:Abar())
- call garbagecollect()
+ call test_garbagecollect_now()
call assert_equal(14, s:Abar())
endfunc
@@ -308,3 +314,21 @@ func Test_lambda_error()
" This was causing a crash
call assert_fails('ec{@{->{d->()()', 'E15')
endfunc
+
+func Test_closure_error()
+ let l =<< trim END
+ func F1() closure
+ return 1
+ endfunc
+ END
+ call writefile(l, 'Xscript')
+ let caught_932 = 0
+ try
+ source Xscript
+ catch /E932:/
+ let caught_932 = 1
+ endtry
+ call assert_equal(1, caught_932)
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_langmap.vim b/src/nvim/testdir/test_langmap.vim
index 4f831aa40b..aaed77e109 100644
--- a/src/nvim/testdir/test_langmap.vim
+++ b/src/nvim/testdir/test_langmap.vim
@@ -49,6 +49,41 @@ func Test_langmap()
call feedkeys(';', 'tx')
call assert_equal(5, col('.'))
+ set langmap=RL
+ let g:counter = 0
+ nnoremap L;L <Cmd>let g:counter += 1<CR>
+ nnoremap <C-L> <Cmd>throw 'This mapping should not be triggered'<CR>
+
+ " 'langmap' is applied to keys without modifiers when matching a mapping
+ call feedkeys('R;R', 'tx')
+ call assert_equal(1, g:counter)
+ nunmap L;L
+ unlet g:counter
+
+ delete
+ call assert_equal('', getline(1))
+ undo
+ call assert_equal('Hello World', getline(1))
+ " 'langmap' does not change Ctrl-R to Ctrl-L for consistency
+ call feedkeys("\<*C-R>", 'tx')
+ call assert_equal('', getline(1))
+
+ set langmap=6L
+ undo
+ setlocal bufhidden=hide
+ let oldbuf = bufnr()
+ enew
+ call assert_notequal(oldbuf, bufnr())
+ " 'langmap' does not change Ctrl-6 to Ctrl-L for consistency
+ " Ctrl-6 becomes Ctrl-^ after merging the Ctrl modifier
+ call feedkeys("\<*C-6>", 'tx')
+ call assert_equal(oldbuf, bufnr())
+ setlocal bufhidden&
+
+ nunmap <C-L>
+
set langmap&
quit!
endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_legacy_filetype.vim b/src/nvim/testdir/test_legacy_filetype.vim
deleted file mode 100644
index 772faaadb0..0000000000
--- a/src/nvim/testdir/test_legacy_filetype.vim
+++ /dev/null
@@ -1,4 +0,0 @@
-let g:do_legacy_filetype = 1
-filetype on
-
-source test_filetype.vim
diff --git a/src/nvim/testdir/test_let.vim b/src/nvim/testdir/test_let.vim
index 6cb736a38a..35745e9c6a 100644
--- a/src/nvim/testdir/test_let.vim
+++ b/src/nvim/testdir/test_let.vim
@@ -6,6 +6,10 @@ func Test_let()
let Test104#numvar = function('tr')
call assert_equal("function('tr')", string(Test104#numvar))
+ let foo#tr = function('tr')
+ call assert_equal("function('tr')", string(foo#tr))
+ unlet foo#tr
+
let a = 1
let b = 2
@@ -25,9 +29,62 @@ func Test_let()
let s = "\na #1\nb #2"
call assert_equal(s, out)
+ " Test for displaying a string variable
+ let s = 'vim'
+ let out = execute('let s')
+ let s = "\ns vim"
+ call assert_equal(s, out)
+
+ " Test for displaying a list variable
+ let l = [1, 2]
+ let out = execute('let l')
+ let s = "\nl [1, 2]"
+ call assert_equal(s, out)
+
+ " Test for displaying a dict variable
+ let d = {'k' : 'v'}
+ let out = execute('let d')
+ let s = "\nd {'k': 'v'}"
+ call assert_equal(s, out)
+
+ " Test for displaying a function reference variable
+ let F = function('min')
+ let out = execute('let F')
+ let s = "\nF *min()"
+ call assert_equal(s, out)
+
let x = 0
if 0 | let x = 1 | endif
call assert_equal(0, x)
+
+ " Display a list item using an out of range index
+ let l = [10]
+ call assert_fails('let l[1]', 'E684:')
+
+ " List special variable dictionaries
+ let g:Test_Global_Var = 5
+ call assert_match("\nTest_Global_Var #5", execute('let g:'))
+ unlet g:Test_Global_Var
+
+ let b:Test_Buf_Var = 8
+ call assert_match("\nb:Test_Buf_Var #8", execute('let b:'))
+ unlet b:Test_Buf_Var
+
+ let w:Test_Win_Var = 'foo'
+ call assert_equal("\nw:Test_Win_Var foo", execute('let w:'))
+ unlet w:Test_Win_Var
+
+ let t:Test_Tab_Var = 'bar'
+ call assert_equal("\nt:Test_Tab_Var bar", execute('let t:'))
+ unlet t:Test_Tab_Var
+
+ let s:Test_Script_Var = [7]
+ call assert_match("\ns:Test_Script_Var \\[7]", execute('let s:'))
+ unlet s:Test_Script_Var
+
+ let l:Test_Local_Var = {'k' : 5}
+ call assert_match("\nl:Test_Local_Var {'k': 5}", execute('let l:'))
+ call assert_match("v:errors []", execute('let v:'))
endfunc
func s:set_arg1(a) abort
@@ -201,16 +258,68 @@ func Test_let_option_error()
let &fillchars = _w
endfunc
+" Errors with the :let statement
func Test_let_errors()
let s = 'abcd'
call assert_fails('let s[1] = 5', 'E689:')
let l = [1, 2, 3]
call assert_fails('let l[:] = 5', 'E709:')
+
+ call assert_fails('let x:lnum=5', ['E121:', 'E488:'])
+ call assert_fails('let v:=5', 'E461:')
+ call assert_fails('let [a]', 'E474:')
+ call assert_fails('let [a, b] = [', 'E697:')
+ call assert_fails('let [a, b] = [10, 20', 'E696:')
+ call assert_fails('let [a, b] = 10', 'E714:')
+ call assert_fails('let [a, , b] = [10, 20]', 'E475:')
+ call assert_fails('let [a, b&] = [10, 20]', 'E475:')
+ call assert_fails('let $ = 10', 'E475:')
+ call assert_fails('let $FOO[1] = "abc"', 'E18:')
+ call assert_fails('let &buftype[1] = "nofile"', 'E18:')
+ let s = "var"
+ let var = 1
+ call assert_fails('let var += [1,2]', 'E734:')
+ call assert_fails('let {s}.1 = 2', 'E1203:')
+ call assert_fails('let a[1] = 5', 'E121:')
+ let l = [[1,2]]
+ call assert_fails('let l[:][0] = [5]', 'E708:')
+ let d = {'k' : 4}
+ call assert_fails('let d.# = 5', 'E488:')
+ call assert_fails('let d.m += 5', 'E716:')
+ call assert_fails('let m = d[{]', 'E15:')
+ let l = [1, 2]
+ call assert_fails('let l[2] = 0', 'E684:')
+ call assert_fails('let l[0:1] = [1, 2, 3]', 'E710:')
+ call assert_fails('let l[-2:-3] = [3, 4]', 'E684:')
+ call assert_fails('let l[0:4] = [5, 6]', 'E711:')
+ call assert_fails('let l -= 2', 'E734:')
+ call assert_fails('let l += 2', 'E734:')
+ call assert_fails('let g:["a;b"] = 10', 'E461:')
+ call assert_fails('let g:.min = function("max")', 'E704:')
+ call assert_fails('let g:cos = "" | let g:.cos = {-> 42}', 'E704:')
+ if has('channel')
+ let ch = test_null_channel()
+ call assert_fails('let ch += 1', 'E734:')
+ endif
+ call assert_fails('let name = "a" .. "b",', 'E488: Trailing characters: ,')
+
+ " This test works only when the language is English
+ if v:lang == "C" || v:lang =~ '^[Ee]n'
+ call assert_fails('let [a ; b;] = [10, 20]',
+ \ 'Double ; in list of variables')
+ endif
endfunc
func Test_let_heredoc_fails()
call assert_fails('let v =<< marker', 'E991:')
+ try
+ exe "let v =<< TEXT | abc | TEXT"
+ call assert_report('No exception thrown')
+ catch /E488:/
+ catch
+ call assert_report("Caught exception: " .. v:exception)
+ endtry
let text =<< trim END
func WrongSyntax()
@@ -243,6 +352,10 @@ func Test_let_heredoc_fails()
call writefile(text, 'XheredocBadMarker')
call assert_fails('source XheredocBadMarker', 'E221:')
call delete('XheredocBadMarker')
+
+ call writefile(['let v =<< TEXT', 'abc'], 'XheredocMissingMarker')
+ call assert_fails('source XheredocMissingMarker', 'E990:')
+ call delete('XheredocMissingMarker')
endfunc
func Test_let_heredoc_trim_no_indent_marker()
@@ -361,3 +474,5 @@ E
END
call assert_equal([' x', ' \y', ' z'], [a, b, c])
endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_lispwords.vim b/src/nvim/testdir/test_lispindent.vim
index 4144fb0521..2d6060bba3 100644
--- a/src/nvim/testdir/test_lispwords.vim
+++ b/src/nvim/testdir/test_lispindent.vim
@@ -86,6 +86,37 @@ func Test_lisp_indent()
set nolisp
endfunc
+func Test_lispindent_negative()
+ " in legacy script there is no error
+ call assert_equal(-1, lispindent(-1))
+endfunc
+
+func Test_lispindent_with_indentexpr()
+ enew
+ setl ai lisp nocin indentexpr=11
+ exe "normal a(x\<CR>1\<CR>2)\<Esc>"
+ let expected = ['(x', ' 1', ' 2)']
+ call assert_equal(expected, getline(1, 3))
+ " with Lisp indenting the first line is not indented
+ normal 1G=G
+ call assert_equal(expected, getline(1, 3))
+
+ %del
+ setl lispoptions=expr:1 indentexpr=5
+ exe "normal a(x\<CR>1\<CR>2)\<Esc>"
+ let expected_expr = ['(x', ' 1', ' 2)']
+ call assert_equal(expected_expr, getline(1, 3))
+ normal 2G2<<=G
+ call assert_equal(expected_expr, getline(1, 3))
+
+ setl lispoptions=expr:0
+ " with Lisp indenting the first line is not indented
+ normal 1G3<<=G
+ call assert_equal(expected, getline(1, 3))
+
+ bwipe!
+endfunc
+
func Test_lisp_indent_works()
" This was reading beyond the end of the line
new
diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim
index 2f4e1db4a1..9ecd83265a 100644
--- a/src/nvim/testdir/test_listdict.vim
+++ b/src/nvim/testdir/test_listdict.vim
@@ -31,6 +31,12 @@ func Test_list_slice()
call assert_equal([1, 'as''d', [1, 2, function('strlen')]], l[:-2])
call assert_equal([1, 'as''d', [1, 2, function('strlen')], {'a': 1}], l[0:8])
call assert_equal([], l[8:-1])
+ call assert_equal([], l[0:-10])
+ " perform an operation on a list slice
+ let l = [1, 2, 3]
+ let l[:1] += [1, 2]
+ let l[2:] -= [1]
+ call assert_equal([2, 4, 2], l)
endfunc
" List identity
@@ -104,6 +110,8 @@ func Test_list_range_assign()
let l = [0]
let l[:] = [1, 2]
call assert_equal([1, 2], l)
+ let l[-4:-1] = [5, 6]
+ call assert_equal([5, 6], l)
endfunc
" Test removing items in list
@@ -143,6 +151,20 @@ func Test_list_func_remove()
call assert_fails("call remove(l, l)", 'E745:')
endfunc
+" List add() function
+func Test_list_add()
+ let l = []
+ call add(l, 1)
+ call add(l, [2, 3])
+ call add(l, [])
+ call add(l, v:_null_list)
+ call add(l, {'k' : 3})
+ call add(l, {})
+ call add(l, v:_null_dict)
+ call assert_equal([1, [2, 3], [], [], {'k' : 3}, {}, {}], l)
+ " call assert_equal(1, add(v:_null_list, 4))
+endfunc
+
" Tests for Dictionary type
func Test_dict()
@@ -165,6 +187,26 @@ func Test_dict()
call assert_equal({'c': 'ccc', '1': 99, 'b': [1, 2, function('strlen')], '3': 33, '-1': {'a': 1}}, d)
call filter(d, 'v:key =~ ''[ac391]''')
call assert_equal({'c': 'ccc', '1': 99, '3': 33, '-1': {'a': 1}}, d)
+
+ " duplicate key
+ call assert_fails("let d = {'k' : 10, 'k' : 20}", 'E721:')
+ " missing comma
+ call assert_fails("let d = {'k' : 10 'k' : 20}", 'E722:')
+ " missing curly brace
+ call assert_fails("let d = {'k' : 10,", 'E723:')
+ " invalid key
+ call assert_fails('let d = #{++ : 10}', 'E15:')
+ " wrong type for key
+ call assert_fails('let d={[] : 10}', 'E730:')
+ " undefined variable as value
+ call assert_fails("let d={'k' : i}", 'E121:')
+
+ " allow key starting with number at the start, not a curly expression
+ call assert_equal({'1foo': 77}, #{1foo: 77})
+
+ " #{expr} is not a curly expression
+ let x = 'x'
+ call assert_equal(#{g: x}, #{g:x})
endfunc
" Dictionary identity
@@ -247,6 +289,16 @@ func Test_dict_func()
call assert_equal('xxx3', Fn('xxx'))
endfunc
+func Test_dict_assign()
+ let d = {}
+ let d.1 = 1
+ let d._ = 2
+ call assert_equal({'1': 1, '_': 2}, d)
+
+ let n = 0
+ call assert_fails('let n.key = 3', 'E1203: Dot can only be used on a dictionary: n.key = 3')
+endfunc
+
" Function in script-local List or Dict
func Test_script_local_dict_func()
let g:dict = {}
@@ -259,7 +311,7 @@ func Test_script_local_dict_func()
unlet g:dict
endfunc
-" Test removing items in la dictionary
+" Test removing items in a dictionary
func Test_dict_func_remove()
let d = {1:'a', 2:'b', 3:'c'}
call assert_equal('b', remove(d, 2))
@@ -294,17 +346,18 @@ func Test_dict_deepcopy()
let l = [4, d, 6]
let d[3] = l
let dc = deepcopy(d)
- call assert_fails('call deepcopy(d, 1)', 'E698')
+ call assert_fails('call deepcopy(d, 1)', 'E698:')
let l2 = [0, l, l, 3]
let l[1] = l2
let l3 = deepcopy(l2)
call assert_true(l3[1] is l3[2])
+ call assert_fails("call deepcopy([1, 2], 2)", 'E1023:')
endfunc
" Locked variables
func Test_list_locked_var()
let expected = [
- \ [['0000-000', 'ppppppp'],
+ \ [['1000-000', 'ppppppF'],
\ ['0000-000', 'ppppppp'],
\ ['0000-000', 'ppppppp']],
\ [['1000-000', 'ppppppF'],
@@ -331,7 +384,7 @@ func Test_list_locked_var()
exe "unlockvar " . depth . " l"
endif
let ps = islocked("l").islocked("l[1]").islocked("l[1][1]").islocked("l[1][1][0]").'-'.islocked("l[2]").islocked("l[2]['6']").islocked("l[2]['6'][7]")
- call assert_equal(expected[depth][u][0], ps)
+ call assert_equal(expected[depth][u][0], ps, 'depth: ' .. depth)
let ps = ''
try
let l[1][1][0] = 99
@@ -375,15 +428,20 @@ func Test_list_locked_var()
catch
let ps .= 'F'
endtry
- call assert_equal(expected[depth][u][1], ps)
+ call assert_equal(expected[depth][u][1], ps, 'depth: ' .. depth)
endfor
endfor
+ call assert_fails("let x=islocked('a b')", 'E488:')
+ let mylist = [1, 2, 3]
+ call assert_fails("let x = islocked('mylist[1:2]')", 'E786:')
+ let mydict = {'k' : 'v'}
+ call assert_fails("let x = islocked('mydict.a')", 'E716:')
endfunc
" Unletting locked variables
func Test_list_locked_var_unlet()
let expected = [
- \ [['0000-000', 'ppppppp'],
+ \ [['1000-000', 'ppppppp'],
\ ['0000-000', 'ppppppp'],
\ ['0000-000', 'ppppppp']],
\ [['1000-000', 'ppFppFp'],
@@ -411,7 +469,7 @@ func Test_list_locked_var_unlet()
exe "unlockvar " . depth . " l"
endif
let ps = islocked("l").islocked("l[1]").islocked("l[1][1]").islocked("l[1][1][0]").'-'.islocked("l[2]").islocked("l[2]['6']").islocked("l[2]['6'][7]")
- call assert_equal(expected[depth][u][0], ps)
+ call assert_equal(expected[depth][u][0], ps, 'depth: ' .. depth)
let ps = ''
try
unlet l[2]['6'][7]
@@ -458,6 +516,11 @@ func Test_list_locked_var_unlet()
call assert_equal(expected[depth][u][1], ps)
endfor
endfor
+ " Deleting a list range should fail if the range is locked
+ let l = [1, 2, 3, 4]
+ lockvar l[1:2]
+ call assert_fails('unlet l[1:2]', 'E741:')
+ unlet l
endfunc
" Locked variables and :unlet or list / dict functions
@@ -567,6 +630,18 @@ func Test_let_lock_list()
unlet l
endfunc
+" Locking part of the list
+func Test_let_lock_list_items()
+ let l = [1, 2, 3, 4]
+ lockvar l[2:]
+ call assert_equal(0, islocked('l[0]'))
+ call assert_equal(1, islocked('l[2]'))
+ call assert_equal(1, islocked('l[3]'))
+ call assert_fails('let l[2] = 10', 'E741:')
+ call assert_fails('let l[3] = 20', 'E741:')
+ unlet l
+endfunc
+
" lockvar/islocked() triggering script autoloading
func Test_lockvar_script_autoload()
let old_rtp = &rtp
@@ -598,26 +673,35 @@ func Test_func_arg_list()
call s:arg_list_test(1, 2, [3, 4], {5: 6})
endfunc
+func Test_dict_item_locked()
+endfunc
+
" Tests for reverse(), sort(), uniq()
func Test_reverse_sort_uniq()
let l = ['-0', 'A11', 2, 2, 'xaaa', 4, 'foo', 'foo6', 'foo', [0, 1, 2], 'x8', [0, 1, 2], 1.5]
call assert_equal(['-0', 'A11', 2, 'xaaa', 4, 'foo', 'foo6', 'foo', [0, 1, 2], 'x8', [0, 1, 2], 1.5], uniq(copy(l)))
call assert_equal([1.5, [0, 1, 2], 'x8', [0, 1, 2], 'foo', 'foo6', 'foo', 4, 'xaaa', 2, 2, 'A11', '-0'], reverse(l))
call assert_equal([1.5, [0, 1, 2], 'x8', [0, 1, 2], 'foo', 'foo6', 'foo', 4, 'xaaa', 2, 2, 'A11', '-0'], reverse(reverse(l)))
- call assert_equal(['-0', 'A11', 'foo', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 2, 4, [0, 1, 2], [0, 1, 2]], sort(l))
- call assert_equal([[0, 1, 2], [0, 1, 2], 4, 2, 2, 1.5, 'xaaa', 'x8', 'foo6', 'foo', 'foo', 'A11', '-0'], reverse(sort(l)))
- call assert_equal(['-0', 'A11', 'foo', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 2, 4, [0, 1, 2], [0, 1, 2]], sort(reverse(sort(l))))
- call assert_equal(['-0', 'A11', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 4, [0, 1, 2]], uniq(sort(l)))
-
- let l=[7, 9, 'one', 18, 12, 22, 'two', 10.0e-16, -1, 'three', 0xff, 0.22, 'four']
- call assert_equal([-1, 'one', 'two', 'three', 'four', 1.0e-15, 0.22, 7, 9, 12, 18, 22, 255], sort(copy(l), 'n'))
-
- let l=[7, 9, 18, 12, 22, 10.0e-16, -1, 0xff, 0, -0, 0.22, 'bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', {}, []]
- call assert_equal(['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l), 1))
- call assert_equal(['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l), 'i'))
- call assert_equal(['BAR', 'Bar', 'FOO', 'FOOBAR', 'Foo', 'bar', 'foo', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l)))
+ if has('float')
+ call assert_equal(['-0', 'A11', 'foo', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 2, 4, [0, 1, 2], [0, 1, 2]], sort(l))
+ call assert_equal([[0, 1, 2], [0, 1, 2], 4, 2, 2, 1.5, 'xaaa', 'x8', 'foo6', 'foo', 'foo', 'A11', '-0'], reverse(sort(l)))
+ call assert_equal(['-0', 'A11', 'foo', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 2, 4, [0, 1, 2], [0, 1, 2]], sort(reverse(sort(l))))
+ call assert_equal(['-0', 'A11', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 4, [0, 1, 2]], uniq(sort(l)))
+
+ let l = [7, 9, 'one', 18, 12, 22, 'two', 10.0e-16, -1, 'three', 0xff, 0.22, 'four']
+ call assert_equal([-1, 'one', 'two', 'three', 'four', 1.0e-15, 0.22, 7, 9, 12, 18, 22, 255], sort(copy(l), 'n'))
+
+ let l = [7, 9, 18, 12, 22, 10.0e-16, -1, 0xff, 0, -0, 0.22, 'bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', {}, []]
+ call assert_equal(['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l), 1))
+ call assert_equal(['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l), 'i'))
+ call assert_equal(['BAR', 'Bar', 'FOO', 'FOOBAR', 'Foo', 'bar', 'foo', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l)))
+ endif
call assert_fails('call reverse("")', 'E899:')
+ call assert_fails('call uniq([1, 2], {x, y -> []})', 'E745:')
+ call assert_fails("call sort([1, 2], function('min'), 1)", "E715:")
+ call assert_fails("call sort([1, 2], function('invalid_func'))", "E700:")
+ call assert_fails("call sort([1, 2], function('min'))", "E118:")
endfunc
" reduce a list or a blob
@@ -665,7 +749,7 @@ func Test_reduce()
call assert_equal(42, reduce(v:_null_blob, function('add'), 42))
endfunc
-" splitting a string to a List
+" splitting a string to a List using split()
func Test_str_split()
call assert_equal(['aa', 'bb'], split(' aa bb '))
call assert_equal(['aa', 'bb'], split(' aa bb ', '\W\+', 0))
@@ -676,6 +760,9 @@ func Test_str_split()
call assert_equal(['aa', '', 'bb', 'cc', ''], split('aa,,bb, cc,', ',\s*', 1))
call assert_equal(['a', 'b', 'c'], split('abc', '\zs'))
call assert_equal(['', 'a', '', 'b', '', 'c', ''], split('abc', '\zs', 1))
+ call assert_fails("call split('abc', [])", 'E730:')
+ call assert_fails("call split('abc', 'b', [])", 'E745:')
+ call assert_equal(['abc'], split('abc', '\\%('))
endfunc
" compare recursively linked list and dict
@@ -687,6 +774,12 @@ func Test_listdict_compare()
call assert_true(d == d)
call assert_false(l != deepcopy(l))
call assert_false(d != deepcopy(d))
+
+ " comparison errors
+ call assert_fails('echo [1, 2] =~ {}', 'E691:')
+ call assert_fails('echo [1, 2] =~ [1, 2]', 'E692:')
+ call assert_fails('echo {} =~ 5', 'E735:')
+ call assert_fails('echo {} =~ {}', 'E736:')
endfunc
" compare complex recursively linked list and dict
@@ -860,6 +953,67 @@ func Test_scope_dict()
call s:check_scope_dict('v', v:true)
endfunc
+" Test for deep nesting of lists (> 100)
+func Test_deep_nested_list()
+ let deep_list = []
+ let l = deep_list
+ for i in range(102)
+ let newlist = []
+ call add(l, newlist)
+ let l = newlist
+ endfor
+ call add(l, 102)
+
+ call assert_fails('let m = deepcopy(deep_list)', 'E698:')
+ call assert_fails('lockvar 110 deep_list', 'E743:')
+ call assert_fails('unlockvar 110 deep_list', 'E743:')
+ " Nvim implements :echo very differently
+ " call assert_fails('let x = execute("echo deep_list")', 'E724:')
+ call test_garbagecollect_now()
+ unlet deep_list
+endfunc
+
+" Test for deep nesting of dicts (> 100)
+func Test_deep_nested_dict()
+ let deep_dict = {}
+ let d = deep_dict
+ for i in range(102)
+ let newdict = {}
+ let d.k = newdict
+ let d = newdict
+ endfor
+ let d.k = 'v'
+
+ call assert_fails('let m = deepcopy(deep_dict)', 'E698:')
+ call assert_fails('lockvar 110 deep_dict', 'E743:')
+ call assert_fails('unlockvar 110 deep_dict', 'E743:')
+ " Nvim implements :echo very differently
+ " call assert_fails('let x = execute("echo deep_dict")', 'E724:')
+ call test_garbagecollect_now()
+ unlet deep_dict
+endfunc
+
+" List and dict indexing tests
+func Test_listdict_index()
+ call assert_fails('echo function("min")[0]', 'E695:')
+ call assert_fails('echo v:true[0]', 'E909:')
+ let d = {'k' : 10}
+ call assert_fails('echo d.', 'E15:')
+ call assert_fails('echo d[1:2]', 'E719:')
+ call assert_fails("let v = [4, 6][{-> 1}]", 'E729:')
+ call assert_fails("let v = range(5)[2:[]]", 'E730:')
+ call assert_fails("let v = range(5)[2:{-> 2}(]", ['E15:', 'E116:'])
+ call assert_fails("let v = range(5)[2:3", 'E111:')
+ call assert_fails("let l = insert([1,2,3], 4, 10)", 'E684:')
+ call assert_fails("let l = insert([1,2,3], 4, -10)", 'E684:')
+ call assert_fails("let l = insert([1,2,3], 4, [])", 'E745:')
+ let l = [1, 2, 3]
+ call assert_fails("let l[i] = 3", 'E121:')
+ call assert_fails("let l[1.1] = 4", 'E806:')
+ call assert_fails("let l[:i] = [4, 5]", 'E121:')
+ call assert_fails("let l[:3.2] = [4, 5]", 'E806:')
+endfunc
+
" Test for a null list
func Test_null_list()
let l = v:_null_list
@@ -896,3 +1050,21 @@ func Test_null_list()
call assert_equal(1, islocked('l'))
unlockvar l
endfunc
+
+" Test for a null dict
+func Test_null_dict()
+ call assert_equal(v:_null_dict, v:_null_dict)
+ let d = v:_null_dict
+ call assert_equal({}, d)
+ call assert_equal(0, len(d))
+ call assert_equal(1, empty(d))
+ call assert_equal(0, items(d))
+ call assert_equal(0, keys(d))
+ call assert_equal(0, values(d))
+ call assert_false(has_key(d, 'k'))
+ call assert_equal('{}', string(d))
+ call assert_fails('let x = v:_null_dict[10]')
+ call assert_equal({}, {})
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_listlbr.vim b/src/nvim/testdir/test_listlbr.vim
index affa0f96fa..1cbdba5d76 100644
--- a/src/nvim/testdir/test_listlbr.vim
+++ b/src/nvim/testdir/test_listlbr.vim
@@ -7,6 +7,7 @@ CheckOption linebreak
CheckFeature conceal
source view_util.vim
+source screendump.vim
function s:screen_lines(lnum, width) abort
return ScreenLines(a:lnum, a:width)
@@ -133,6 +134,45 @@ func Test_linebreak_with_visual_operations()
call s:close_windows()
endfunc
+" Test that cursor is drawn at correct position after an operator when
+" 'linebreak' is enabled.
+func Test_linebreak_reset_restore()
+ CheckScreendump
+
+ " f_wincol() calls validate_cursor()
+ let lines =<< trim END
+ set linebreak showcmd noshowmode formatexpr=wincol()-wincol()
+ call setline(1, repeat('a', &columns - 10) .. ' bbbbbbbbbb c')
+ END
+ call writefile(lines, 'XlbrResetRestore', 'D')
+ let buf = RunVimInTerminal('-S XlbrResetRestore', {'rows': 8})
+
+ call term_sendkeys(buf, '$v$')
+ call WaitForAssert({-> assert_equal(13, term_getcursor(buf)[1])})
+ call term_sendkeys(buf, 'zo')
+ call WaitForAssert({-> assert_equal(12, term_getcursor(buf)[1])})
+
+ call term_sendkeys(buf, '$v$')
+ call WaitForAssert({-> assert_equal(13, term_getcursor(buf)[1])})
+ call term_sendkeys(buf, 'gq')
+ call WaitForAssert({-> assert_equal(12, term_getcursor(buf)[1])})
+
+ call term_sendkeys(buf, "$\<C-V>$")
+ call WaitForAssert({-> assert_equal(13, term_getcursor(buf)[1])})
+ call term_sendkeys(buf, 'I')
+ call WaitForAssert({-> assert_equal(12, term_getcursor(buf)[1])})
+
+ call term_sendkeys(buf, "\<Esc>$v$")
+ call WaitForAssert({-> assert_equal(13, term_getcursor(buf)[1])})
+ call term_sendkeys(buf, 's')
+ call WaitForAssert({-> assert_equal(12, term_getcursor(buf)[1])})
+ call VerifyScreenDump(buf, 'Test_linebreak_reset_restore_1', {})
+
+ " clean up
+ call term_sendkeys(buf, "\<Esc>")
+ call StopVimInTerminal(buf)
+endfunc
+
func Test_virtual_block()
call s:test_windows('setl sbr=+')
call setline(1, [
diff --git a/src/nvim/testdir/test_makeencoding.vim b/src/nvim/testdir/test_makeencoding.vim
index c53c07d991..e297bdc228 100644
--- a/src/nvim/testdir/test_makeencoding.vim
+++ b/src/nvim/testdir/test_makeencoding.vim
@@ -107,3 +107,19 @@ func Test_make()
lclose
endfor
endfunc
+
+" Test for an error file with a long line that needs an encoding conversion
+func Test_longline_conversion()
+ new
+ call setline(1, ['Xfile:10:' .. repeat("\xe0", 2000)])
+ write ++enc=latin1 Xerr.out
+ bw!
+ set errorformat&
+ set makeencoding=latin1
+ cfile Xerr.out
+ call assert_equal(repeat("\u00e0", 2000), getqflist()[0].text)
+ call delete('Xerr.out')
+ set makeencoding&
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_maparg.vim b/src/nvim/testdir/test_maparg.vim
index dad4c81a7b..f903f5b934 100644
--- a/src/nvim/testdir/test_maparg.vim
+++ b/src/nvim/testdir/test_maparg.vim
@@ -158,11 +158,11 @@ func Test_range_map()
call assert_equal("abcd", getline(1))
endfunc
-func One_mapset_test(keys)
- exe 'nnoremap ' .. a:keys .. ' original<CR>'
+func One_mapset_test(keys, rhs)
+ exe 'nnoremap ' .. a:keys .. ' ' .. a:rhs
let orig = maparg(a:keys, 'n', 0, 1)
call assert_equal(a:keys, orig.lhs)
- call assert_equal('original<CR>', orig.rhs)
+ call assert_equal(a:rhs, orig.rhs)
call assert_equal('n', orig.mode)
exe 'nunmap ' .. a:keys
@@ -172,15 +172,16 @@ func One_mapset_test(keys)
call mapset('n', 0, orig)
let d = maparg(a:keys, 'n', 0, 1)
call assert_equal(a:keys, d.lhs)
- call assert_equal('original<CR>', d.rhs)
+ call assert_equal(a:rhs, d.rhs)
call assert_equal('n', d.mode)
exe 'nunmap ' .. a:keys
endfunc
func Test_mapset()
- call One_mapset_test('K')
- call One_mapset_test('<F3>')
+ call One_mapset_test('K', 'original<CR>')
+ call One_mapset_test('<F3>', 'original<CR>')
+ call One_mapset_test('<F3>', '<lt>Nop>')
" Check <> key conversion
new
@@ -203,6 +204,26 @@ func Test_mapset()
iunmap K
+ " Test that <Nop> is restored properly
+ inoremap K <Nop>
+ call feedkeys("SK\<Esc>", 'xt')
+ call assert_equal('', getline(1))
+
+ let orig = maparg('K', 'i', 0, 1)
+ call assert_equal('K', orig.lhs)
+ call assert_equal('<Nop>', orig.rhs)
+ call assert_equal('i', orig.mode)
+
+ inoremap K foo
+ call feedkeys("SK\<Esc>", 'xt')
+ call assert_equal('foo', getline(1))
+
+ call mapset('i', 0, orig)
+ call feedkeys("SK\<Esc>", 'xt')
+ call assert_equal('', getline(1))
+
+ iunmap K
+
" Test literal <CR> using a backslash
let cpo_save = &cpo
set cpo-=B
@@ -248,6 +269,8 @@ func Test_mapset()
bwipe!
call assert_fails('call mapset([], v:false, {})', 'E730:')
+ call assert_fails('call mapset("i", 0, "")', 'E715:')
+ call assert_fails('call mapset("i", 0, {})', 'E460:')
endfunc
func Check_ctrlb_map(d, check_alt)
diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim
index e1d0b9a9ba..5c5a65d4ca 100644
--- a/src/nvim/testdir/test_mapping.vim
+++ b/src/nvim/testdir/test_mapping.vim
@@ -395,7 +395,9 @@ func Test_motionforce_omap()
endfunc
func Test_error_in_map_expr()
- if !has('terminal') || (has('win32') && has('gui_running'))
+ " Unlike CheckRunVimInTerminal this does work in a win32 console
+ CheckFeature terminal
+ if has('win32') && has('gui_running')
throw 'Skipped: cannot run Vim in a terminal window'
endif
@@ -765,6 +767,11 @@ func Test_mapcomplete()
mapclear
endfunc
+func GetAbbrText()
+ unabbr hola
+ return 'hello'
+endfunc
+
" Test for <expr> in abbreviation
func Test_expr_abbr()
new
@@ -777,10 +784,17 @@ func Test_expr_abbr()
" invalid <expr> abbreviation
abbr <expr> hte GetAbbr()
call assert_fails('normal ihte ', 'E117:')
- call assert_equal(' ', getline(1))
+ call assert_equal('', getline(1))
unabbr <expr> hte
- close!
+ " evaluating the expression deletes the abbreviation
+ abbr <expr> hola GetAbbrText()
+ call assert_equal('GetAbbrText()', maparg('hola', 'i', '1'))
+ call feedkeys("ahola \<Esc>", 'xt')
+ call assert_equal('hello ', getline('.'))
+ call assert_equal('', maparg('hola', 'i', '1'))
+
+ bwipe!
endfunc
" Test for storing mappings in different modes in a vimrc file
@@ -975,6 +989,21 @@ func Test_abbreviate_multi_byte()
bwipe!
endfunc
+" Test for abbreviations with 'latin1' encoding
+func Test_abbreviate_latin1_encoding()
+ " set encoding=latin1
+ call assert_fails('abbr ab#$c ABC', 'E474:')
+ new
+ iabbr <buffer> #i #include
+ iabbr <buffer> ## #enddef
+ exe "normal i#i\<C-]>"
+ call assert_equal('#include', getline(1))
+ exe "normal 0Di##\<C-]>"
+ call assert_equal('#enddef', getline(1))
+ %bw!
+ set encoding=utf-8
+endfunc
++
" Test for <Plug> always being mapped, even when used with "noremap".
func Test_plug_remap()
let g:foo = 0
@@ -1006,15 +1035,14 @@ func Test_plug_remap()
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)
+ call Ntest_setmouse(1, 1)
return "\<LeftMouse>"
endfunc
func DragExpr()
- call test_setmouse(1, 2)
+ call Ntest_setmouse(1, 2)
return "\<LeftDrag>"
endfunc
nnoremap <expr> <F2> ClickExpr()
@@ -1034,16 +1062,39 @@ func Test_mouse_drag_mapped_start_select()
set mouse&
endfunc
+func Test_mouse_drag_statusline()
+ set laststatus=2
+ set mouse=a
+ func ClickExpr()
+ call Ntest_setmouse(&lines - 1, 1)
+ return "\<LeftMouse>"
+ endfunc
+ func DragExpr()
+ call Ntest_setmouse(&lines - 2, 1)
+ return "\<LeftDrag>"
+ endfunc
+ nnoremap <expr> <F2> ClickExpr()
+ nnoremap <expr> <F3> DragExpr()
+
+ " this was causing a crash in win_drag_status_line()
+ call feedkeys("\<F2>:tabnew\<CR>\<F3>", 'tx')
+
+ nunmap <F2>
+ nunmap <F3>
+ delfunc ClickExpr
+ delfunc DragExpr
+ set laststatus& 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)
+ call Ntest_setmouse(1, 1)
return "\<LeftMouse>"
endfunc
func DragExpr()
- call test_setmouse(1, 2)
+ call Ntest_setmouse(1, 2)
return "\<LeftDrag>"
endfunc
inoremap <expr> <F2> ClickExpr()
@@ -1129,4 +1180,14 @@ func Test_map_after_timed_out_nop()
call delete('Xtest_map_after_timed_out_nop')
endfunc
+func Test_using_past_typeahead()
+ nnoremap :00 0
+ exe "norm :set \x80\xfb0=0\<CR>"
+ exe "sil norm :0\x0f\<C-U>\<CR>"
+
+ exe "norm :set \x80\xfb0=\<CR>"
+ nunmap :00
+endfunc
+
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_marks.vim b/src/nvim/testdir/test_marks.vim
index 74e63d9d69..a7ccca498c 100644
--- a/src/nvim/testdir/test_marks.vim
+++ b/src/nvim/testdir/test_marks.vim
@@ -91,6 +91,17 @@ func Test_setpos()
call assert_equal([0, 1, 21341234, 0], getpos("'a"))
call assert_equal(4, virtcol("'a"))
+ " Test with invalid buffer number, line number and column number
+ call cursor(2, 2)
+ call setpos('.', [-1, 1, 1, 0])
+ call assert_equal([2, 2], [line('.'), col('.')])
+ call setpos('.', [0, -1, 1, 0])
+ call assert_equal([2, 2], [line('.'), col('.')])
+ call setpos('.', [0, 1, -1, 0])
+ call assert_equal([2, 2], [line('.'), col('.')])
+
+ call assert_fails("call setpos('ab', [0, 1, 1, 0])", 'E474:')
+
bwipe!
call win_gotoid(twowin)
bwipe!
@@ -293,4 +304,17 @@ func Test_getmarklist()
close!
endfunc
+" This was using freed memory
+func Test_jump_mark_autocmd()
+ next 00
+ edit 0
+ sargument
+ au BufEnter 0 all
+ sil norm 
+
+ au! BufEnter
+ bwipe!
+endfunc
+
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_match.vim b/src/nvim/testdir/test_match.vim
index 70271aa32f..600b6132a9 100644
--- a/src/nvim/testdir/test_match.vim
+++ b/src/nvim/testdir/test_match.vim
@@ -2,6 +2,7 @@
" matchaddpos(), matcharg(), matchdelete(), and setmatches().
source screendump.vim
+source check.vim
function Test_match()
highlight MyGroup1 term=bold ctermbg=red guibg=red
@@ -35,8 +36,8 @@ function Test_match()
let m1 = matchadd("MyGroup1", "TODO")
let m2 = matchadd("MyGroup2", "FIXME", 42)
let m3 = matchadd("MyGroup3", "XXX", 60, 17)
- let ans = [{'group': 'MyGroup1', 'pattern': 'TODO', 'priority': 10, 'id': 4},
- \ {'group': 'MyGroup2', 'pattern': 'FIXME', 'priority': 42, 'id': 5},
+ let ans = [{'group': 'MyGroup1', 'pattern': 'TODO', 'priority': 10, 'id': 1000},
+ \ {'group': 'MyGroup2', 'pattern': 'FIXME', 'priority': 42, 'id': 1001},
\ {'group': 'MyGroup3', 'pattern': 'XXX', 'priority': 60, 'id': 17}]
call assert_equal(ans, getmatches())
@@ -117,7 +118,7 @@ function Test_match()
call clearmatches()
call setline(1, 'abcdΣabcdef')
- eval "MyGroup1"->matchaddpos([[1, 4, 2], [1, 9, 2]])
+ eval "MyGroup1"->matchaddpos([[1, 4, 2], [1, 9, 2]], 10, 42)
1
redraw!
let v1 = screenattr(1, 1)
@@ -128,7 +129,7 @@ function Test_match()
let v8 = screenattr(1, 8)
let v9 = screenattr(1, 9)
let v10 = screenattr(1, 10)
- call assert_equal([{'group': 'MyGroup1', 'id': 11, 'priority': 10, 'pos1': [1, 4, 2], 'pos2': [1, 9, 2]}], getmatches())
+ call assert_equal([{'group': 'MyGroup1', 'id': 42, 'priority': 10, 'pos1': [1, 4, 2], 'pos2': [1, 9, 2]}], getmatches())
call assert_notequal(v1, v4)
call assert_equal(v5, v4)
call assert_equal(v6, v1)
@@ -142,7 +143,7 @@ function Test_match()
let m=getmatches()
call clearmatches()
call setmatches(m)
- call assert_equal([{'group': 'MyGroup1', 'id': 11, 'priority': 10, 'pos1': [1, 4, 2], 'pos2': [1,9, 2]}, {'group': 'MyGroup1', 'pattern': '\%2lmatchadd', 'priority': 10, 'id': 12}], getmatches())
+ call assert_equal([{'group': 'MyGroup1', 'id': 42, 'priority': 10, 'pos1': [1, 4, 2], 'pos2': [1,9, 2]}, {'group': 'MyGroup1', 'pattern': '\%2lmatchadd', 'priority': 10, 'id': 1106}], getmatches())
highlight MyGroup1 NONE
highlight MyGroup2 NONE
@@ -159,12 +160,14 @@ endfunc
func Test_matchadd_error()
call clearmatches()
" Nvim: not an error anymore:
+ " call assert_fails("call matchadd('GroupDoesNotExist', 'X')", 'E28:')
call matchadd('GroupDoesNotExist', 'X')
- call assert_equal([{'group': 'GroupDoesNotExist', 'pattern': 'X', 'priority': 10, 'id': 13}], getmatches())
- call assert_fails("call matchadd('Search', '\\(')", 'E475:')
+ call assert_equal([{'group': 'GroupDoesNotExist', 'pattern': 'X', 'priority': 10, 'id': 1206}], getmatches())
+ call assert_fails("call matchadd('Search', '\\(')", 'E54:')
call assert_fails("call matchadd('Search', 'XXX', 1, 123, 1)", 'E715:')
call assert_fails("call matchadd('Error', 'XXX', 1, 3)", 'E798:')
call assert_fails("call matchadd('Error', 'XXX', 1, 0)", 'E799:')
+ call assert_fails("call matchadd('Error', 'XXX', [], 0)", 'E745:')
endfunc
func Test_matchaddpos()
@@ -219,6 +222,21 @@ func Test_matchaddpos()
set hlsearch&
endfunc
+" Add 12 match positions (previously the limit was 8 positions).
+func Test_matchaddpos_dump()
+ CheckScreendump
+
+ let lines =<< trim END
+ call setline(1, ['1234567890123']->repeat(14))
+ call matchaddpos('Search', range(1, 12)->map({i, v -> [v, v]}))
+ END
+ call writefile(lines, 'Xmatchaddpos', 'D')
+ let buf = RunVimInTerminal('-S Xmatchaddpos', #{rows: 14})
+ call VerifyScreenDump(buf, 'Test_matchaddpos_1', {})
+
+ call StopVimInTerminal(buf)
+endfunc
+
func Test_matchaddpos_otherwin()
syntax on
new
@@ -237,8 +255,8 @@ func Test_matchaddpos_otherwin()
let savematches = getmatches(winid)
let expect = [
- \ {'group': 'Search', 'pattern': '4', 'priority': 10, 'id': 4},
- \ {'group': 'Error', 'id': 5, 'priority': 10, 'pos1': [1, 2, 1], 'pos2': [2, 2, 1]},
+ \ {'group': 'Search', 'pattern': '4', 'priority': 10, 'id': 1000},
+ \ {'group': 'Error', 'id': 1001, 'priority': 10, 'pos1': [1, 2, 1], 'pos2': [2, 2, 1]},
\]
call assert_equal(expect, savematches)
@@ -289,7 +307,10 @@ func Test_matchaddpos_error()
call assert_fails("call matchaddpos('Error', [1], 1, 123, 1)", 'E715:')
call assert_fails("call matchaddpos('Error', [1], 1, 5, {'window':12345})", 'E957:')
" Why doesn't the following error have an error code E...?
+ " call assert_fails("call matchaddpos('Error', [{}])", 'E290:')
call assert_fails("call matchaddpos('Error', [{}])", 'E5031:')
+ call assert_equal(-1, matchaddpos('Error', v:_null_list))
+ call assert_fails("call matchaddpos('Error', [1], [], 1)", 'E745:')
endfunc
func OtherWindowCommon()
@@ -306,9 +327,8 @@ func OtherWindowCommon()
endfunc
func Test_matchdelete_other_window()
- if !CanRunVimInTerminal()
- throw 'Skipped: cannot make screendumps'
- endif
+ CheckScreendump
+
let buf = OtherWindowCommon()
call term_sendkeys(buf, ":call matchdelete(mid, winid)\<CR>")
call VerifyScreenDump(buf, 'Test_matchdelete_1', {})
@@ -323,9 +343,7 @@ func Test_matchdelete_error()
endfunc
func Test_matchclear_other_window()
- if !CanRunVimInTerminal()
- throw 'Skipped: cannot make screendumps'
- endif
+ CheckRunVimInTerminal
let buf = OtherWindowCommon()
call term_sendkeys(buf, ":call clearmatches(winid)\<CR>")
call VerifyScreenDump(buf, 'Test_matchclear_1', {})
@@ -335,9 +353,7 @@ func Test_matchclear_other_window()
endfunc
func Test_matchadd_other_window()
- if !CanRunVimInTerminal()
- throw 'Skipped: cannot make screendumps'
- endif
+ CheckRunVimInTerminal
let buf = OtherWindowCommon()
call term_sendkeys(buf, ":call matchadd('Search', 'Hello', 1, -1, #{window: winid})\<CR>")
call term_sendkeys(buf, ":\<CR>")
@@ -347,5 +363,80 @@ func Test_matchadd_other_window()
call delete('XscriptMatchCommon')
endfunc
+func Test_match_in_linebreak()
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ set breakindent linebreak breakat+=]
+ call printf('%s]%s', repeat('x', 50), repeat('x', 70))->setline(1)
+ call matchaddpos('ErrorMsg', [[1, 51]])
+ END
+ call writefile(lines, 'XscriptMatchLinebreak')
+ let buf = RunVimInTerminal('-S XscriptMatchLinebreak', #{rows: 10})
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_match_linebreak', {})
+
+ call StopVimInTerminal(buf)
+ call delete('XscriptMatchLinebreak')
+endfunc
+
+func Test_match_with_incsearch()
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ set incsearch
+ call setline(1, range(20))
+ call matchaddpos('ErrorMsg', [3])
+ END
+ call writefile(lines, 'XmatchWithIncsearch')
+ let buf = RunVimInTerminal('-S XmatchWithIncsearch', #{rows: 6})
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_match_with_incsearch_1', {})
+
+ call term_sendkeys(buf, ":s/0")
+ call VerifyScreenDump(buf, 'Test_match_with_incsearch_2', {})
+
+ call term_sendkeys(buf, "\<CR>")
+ call StopVimInTerminal(buf)
+ call delete('XmatchWithIncsearch')
+endfunc
+
+" Test for deleting matches outside of the screen redraw top/bottom lines
+" This should cause a redraw of those lines.
+func Test_matchdelete_redraw()
+ new
+ call setline(1, range(1, 500))
+ call cursor(250, 1)
+ let m1 = matchaddpos('Search', [[250]])
+ let m2 = matchaddpos('Search', [[10], [450]])
+ redraw!
+ let m3 = matchaddpos('Search', [[240], [260]])
+ call matchdelete(m2)
+ let m = getmatches()
+ call assert_equal(2, len(m))
+ call assert_equal([250], m[0].pos1)
+ redraw!
+ call matchdelete(m1)
+ call assert_equal(1, len(getmatches()))
+ bw!
+endfunc
+
+func Test_match_tab_with_linebreak()
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ set linebreak
+ call setline(1, "\tix")
+ call matchadd('ErrorMsg', '\t')
+ END
+ call writefile(lines, 'XscriptMatchTabLinebreak')
+ let buf = RunVimInTerminal('-S XscriptMatchTabLinebreak', #{rows: 10})
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_match_tab_linebreak', {})
+
+ call StopVimInTerminal(buf)
+ call delete('XscriptMatchTabLinebreak')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_matchfuzzy.vim b/src/nvim/testdir/test_matchfuzzy.vim
index c836bc87aa..b46550fbc3 100644
--- a/src/nvim/testdir/test_matchfuzzy.vim
+++ b/src/nvim/testdir/test_matchfuzzy.vim
@@ -6,9 +6,7 @@ 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('call matchfuzzy(["abc"], [])', 'E730:')
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'))
@@ -75,12 +73,9 @@ func Test_matchfuzzy()
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', {'text_cb' : []})", 'E6000:')
+ call assert_fails("let x = matchfuzzy(l, 'foo', {'key' : []})", 'E730:')
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
@@ -155,12 +150,9 @@ func Test_matchfuzzypos()
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', {'text_cb' : []})", 'E6000:')
+ call assert_fails("let x = matchfuzzypos(l, 'foo', {'key' : []})", 'E730:')
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
diff --git a/src/nvim/testdir/test_menu.vim b/src/nvim/testdir/test_menu.vim
index 75992d3313..a1121632e6 100644
--- a/src/nvim/testdir/test_menu.vim
+++ b/src/nvim/testdir/test_menu.vim
@@ -153,7 +153,7 @@ func Test_menu_errors()
call assert_fails('menu Test.Foo.Bar', 'E327:')
call assert_fails('cmenu Test.Foo', 'E328:')
call assert_fails('emenu x Test.Foo', 'E475:')
- call assert_fails('emenu Test.Foo.Bar', 'E334:')
+ call assert_fails('emenu Test.Foo.Bar', 'E327:')
call assert_fails('menutranslate Test', 'E474:')
silent! unmenu Foo
@@ -252,6 +252,7 @@ func Test_menu_info()
nmenu Test.abc <Nop>
call assert_equal('<Nop>', menu_info('Test.abc').rhs)
call assert_fails('call menu_info([])', 'E730:')
+ call assert_fails('call menu_info("", [])', 'E730:')
nunmenu Test
" Test for defining menus in different modes
@@ -429,7 +430,7 @@ func Test_menu_special()
nunmenu Test.Sign
endfunc
-" Test for "icon=filname" in a toolbar
+" Test for "icon=filename" in a toolbar
func Test_menu_icon()
CheckFeature toolbar
nmenu icon=myicon.xpm Toolbar.Foo :echo "Foo"<CR>
@@ -481,6 +482,35 @@ func Test_popup_menu()
unmenu PopUp
endfunc
+" Test for MenuPopup autocommand
+func Test_autocmd_MenuPopup()
+ CheckNotGui
+
+ set mouse=a
+ set mousemodel=popup
+ aunmenu *
+ autocmd MenuPopup * exe printf(
+ \ 'anoremenu PopUp.Foo <Cmd>let g:res = ["%s", "%s"]<CR>',
+ \ expand('<afile>'), expand('<amatch>'))
+
+ call feedkeys("\<RightMouse>\<Down>\<CR>", 'tnix')
+ call assert_equal(['n', 'n'], g:res)
+
+ call feedkeys("v\<RightMouse>\<Down>\<CR>\<Esc>", 'tnix')
+ call assert_equal(['v', 'v'], g:res)
+
+ call feedkeys("gh\<RightMouse>\<Down>\<CR>\<Esc>", 'tnix')
+ call assert_equal(['s', 's'], g:res)
+
+ call feedkeys("i\<RightMouse>\<Down>\<CR>\<Esc>", 'tnix')
+ call assert_equal(['i', 'i'], g:res)
+
+ autocmd! MenuPopup
+ aunmenu PopUp.Foo
+ unlet g:res
+ set mouse& mousemodel&
+endfunc
+
" Test for listing the menus using the :menu command
func Test_show_menus()
" In the GUI, tear-off menu items are present in the output below
diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim
index 3a607ff533..bfdebdac79 100644
--- a/src/nvim/testdir/test_messages.vim
+++ b/src/nvim/testdir/test_messages.vim
@@ -171,6 +171,38 @@ func Test_echospace()
set ruler& showcmd&
endfunc
+func Test_warning_scroll()
+ CheckRunVimInTerminal
+ let lines =<< trim END
+ call test_override('ui_delay', 50)
+ set noruler
+ set readonly
+ undo
+ END
+ call writefile(lines, 'XTestWarningScroll', 'D')
+ let buf = RunVimInTerminal('', #{rows: 8})
+
+ " When the warning comes from a script, messages are scrolled so that the
+ " stacktrace is visible.
+ call term_sendkeys(buf, ":source XTestWarningScroll\n")
+ " only match the final colon in the line that shows the source
+ call WaitForAssert({-> assert_match(':$', term_getline(buf, 5))})
+ call WaitForAssert({-> assert_equal('line 4:W10: Warning: Changing a readonly file', term_getline(buf, 6))})
+ call WaitForAssert({-> assert_equal('Already at oldest change', term_getline(buf, 7))})
+ call WaitForAssert({-> assert_equal('Press ENTER or type command to continue', term_getline(buf, 8))})
+ call term_sendkeys(buf, "\n")
+
+ " When the warning does not come from a script, messages are not scrolled.
+ call term_sendkeys(buf, ":enew\n")
+ call term_sendkeys(buf, ":set readonly\n")
+ call term_sendkeys(buf, 'u')
+ call WaitForAssert({-> assert_equal('W10: Warning: Changing a readonly file', term_getline(buf, 8))})
+ call WaitForAssert({-> assert_equal('Already at oldest change', term_getline(buf, 8))})
+
+ " clean up
+ call StopVimInTerminal(buf)
+endfunc
+
" Test more-prompt (see :help more-prompt).
func Test_message_more()
CheckRunVimInTerminal
@@ -284,6 +316,66 @@ func Test_message_more()
call StopVimInTerminal(buf)
endfunc
+" Test more-prompt scrollback
+func Test_message_more_scrollback()
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ set t_ut=
+ hi Normal ctermfg=15 ctermbg=0
+ for i in range(100)
+ echo i
+ endfor
+ END
+ call writefile(lines, 'XmoreScrollback', 'D')
+ let buf = RunVimInTerminal('-S XmoreScrollback', {'rows': 10})
+ call VerifyScreenDump(buf, 'Test_more_scrollback_1', {})
+
+ call term_sendkeys(buf, 'f')
+ call TermWait(buf)
+ call term_sendkeys(buf, 'b')
+ call VerifyScreenDump(buf, 'Test_more_scrollback_2', {})
+
+ call term_sendkeys(buf, 'q')
+ call TermWait(buf)
+ call StopVimInTerminal(buf)
+endfunc
+
+" Test verbose message before echo command
+func Test_echo_verbose_system()
+ CheckRunVimInTerminal
+ CheckUnix
+
+ let buf = RunVimInTerminal('', {'rows': 10})
+ call term_sendkeys(buf, ":4 verbose echo system('seq 20')\<CR>")
+ " Note that the screendump is filtered to remove the name of the temp file
+ call VerifyScreenDump(buf, 'Test_verbose_system_1', {})
+
+ " display a page and go back, results in exactly the same view
+ call term_sendkeys(buf, ' ')
+ call TermWait(buf, 50)
+ call term_sendkeys(buf, 'b')
+ call VerifyScreenDump(buf, 'Test_verbose_system_1', {})
+
+ " do the same with 'cmdheight' set to 2
+ call term_sendkeys(buf, 'q')
+ call TermWait(buf)
+ call term_sendkeys(buf, ":set ch=2\<CR>")
+ call TermWait(buf)
+ call term_sendkeys(buf, ":4 verbose echo system('seq 20')\<CR>")
+ call VerifyScreenDump(buf, 'Test_verbose_system_2', {})
+
+ call term_sendkeys(buf, ' ')
+ call TermWait(buf, 50)
+ call term_sendkeys(buf, 'b')
+ call VerifyScreenDump(buf, 'Test_verbose_system_2', {})
+
+ call term_sendkeys(buf, 'q')
+ call TermWait(buf)
+ call StopVimInTerminal(buf)
+endfunc
+
+
func Test_ask_yesno()
CheckRunVimInTerminal
let buf = RunVimInTerminal('', {'rows': 6})
@@ -325,14 +417,14 @@ func Test_quit_long_message()
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 writefile(content, 'Xtest_quit_message', 'D')
+ let buf = RunVimInTerminal('-S Xtest_quit_message', #{rows: 10, wait_for_ruler: 0})
+ call WaitForAssert({-> assert_match('^-- More --', term_getline(buf, 10))})
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
@@ -398,7 +490,7 @@ func Test_cmdheight_zero()
" Check change/restore cmdheight when macro
call feedkeys("qa", "xt")
- call assert_equal(1, &cmdheight)
+ call assert_equal(0, &cmdheight)
call feedkeys("q", "xt")
call assert_equal(0, &cmdheight)
diff --git a/src/nvim/testdir/test_method.vim b/src/nvim/testdir/test_method.vim
index cdf688b857..ca3b736429 100644
--- a/src/nvim/testdir/test_method.vim
+++ b/src/nvim/testdir/test_method.vim
@@ -35,6 +35,7 @@ func Test_list_method()
call assert_equal(v:t_list, l->type())
call assert_equal([1, 2, 3], [1, 1, 2, 3, 3]->uniq())
call assert_fails('eval l->values()', 'E715:')
+ call assert_fails('echo []->len', 'E107:')
endfunc
func Test_dict_method()
@@ -130,9 +131,9 @@ func Test_method_syntax()
eval [1, 2, 3]
\ ->sort(
\ )
- call assert_fails('eval [1, 2, 3]-> sort()', 'E260:')
+ call assert_fails('eval [1, 2, 3]-> sort()', 'E15:')
call assert_fails('eval [1, 2, 3]->sort ()', 'E274:')
- call assert_fails('eval [1, 2, 3]-> sort ()', 'E260:')
+ call assert_fails('eval [1, 2, 3]-> sort ()', 'E15:')
endfunc
func Test_method_lambda()
@@ -152,6 +153,22 @@ endfunc
func Test_method_not_supported()
call assert_fails('eval 123->changenr()', 'E276:')
+ call assert_fails('echo "abc"->invalidfunc()', 'E117:')
+ " Test for too many or too few arguments to a method
+ call assert_fails('let n="abc"->len(2)', 'E118:')
+ call assert_fails('let n=10->setwinvar()', 'E119:')
endfunc
-" vim: shiftwidth=2 sts=2 expandtab
+" Test for passing optional arguments to methods
+func Test_method_args()
+ let v:errors = []
+ let n = 10->assert_inrange(1, 5, "Test_assert_inrange")
+ if v:errors[0] !~ 'Test_assert_inrange'
+ call assert_report(v:errors[0])
+ else
+ " Test passed
+ let v:errors = []
+ endif
+endfunc
+
+" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim
index 8ec408e62e..972397cb91 100644
--- a/src/nvim/testdir/test_mksession.vim
+++ b/src/nvim/testdir/test_mksession.vim
@@ -851,9 +851,7 @@ 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')
+ let cont = readblob(fname)
set sessionoptions-=options
mksession Xtestsession
bwipe!
@@ -942,6 +940,19 @@ func Test_mkvimrc()
endfor
call s:ClearMappings()
+
+ " the 'pastetoggle', 'wildchar' and 'wildcharm' option values should be
+ " stored as key names in the vimrc file
+ set pastetoggle=<F5>
+ set wildchar=<F6>
+ set wildcharm=<F7>
+ call assert_fails('mkvimrc Xtestvimrc')
+ mkvimrc! Xtestvimrc
+ call assert_notequal(-1, index(readfile('Xtestvimrc'), 'set pastetoggle=<F5>'))
+ call assert_notequal(-1, index(readfile('Xtestvimrc'), 'set wildchar=<F6>'))
+ call assert_notequal(-1, index(readfile('Xtestvimrc'), 'set wildcharm=<F7>'))
+ set pastetoggle& wildchar& wildcharm&
+
call delete('Xtestvimrc')
endfunc
@@ -979,6 +990,38 @@ func Test_altfile()
call assert_equal('Xtwoalt', bufname('#'))
only
bwipe!
+ call delete('Xtest_altfile')
+endfunc
+
+" Test for creating views with manual folds
+func Test_mkview_manual_fold()
+ call writefile(range(1,10), 'Xfile')
+ new Xfile
+ " create recursive folds
+ 5,6fold
+ 4,7fold
+ mkview Xview
+ normal zE
+ source Xview
+ call assert_equal([-1, 4, 4, 4, 4, -1], [foldclosed(3), foldclosed(4),
+ \ foldclosed(5), foldclosed(6), foldclosed(7), foldclosed(8)])
+ " open one level of fold
+ 4foldopen
+ mkview! Xview
+ normal zE
+ source Xview
+ call assert_equal([-1, -1, 5, 5, -1, -1], [foldclosed(3), foldclosed(4),
+ \ foldclosed(5), foldclosed(6), foldclosed(7), foldclosed(8)])
+ " open all the folds
+ %foldopen!
+ mkview! Xview
+ normal zE
+ source Xview
+ call assert_equal([-1, -1, -1, -1, -1, -1], [foldclosed(3), foldclosed(4),
+ \ foldclosed(5), foldclosed(6), foldclosed(7), foldclosed(8)])
+ call delete('Xfile')
+ call delete('Xview')
+ bw!
endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_modeline.vim b/src/nvim/testdir/test_modeline.vim
index b3fe79f545..613722fdbd 100644
--- a/src/nvim/testdir/test_modeline.vim
+++ b/src/nvim/testdir/test_modeline.vim
@@ -1,5 +1,7 @@
" Tests for parsing the modeline.
+source check.vim
+
func Test_modeline_invalid()
" This was reading allocated memory in the past.
call writefile(['vi:0', 'nothing'], 'Xmodeline')
@@ -281,6 +283,88 @@ func Test_modeline_fails_modelineexpr()
call s:modeline_fails('titlestring', 'titlestring=Something()', 'E992:')
endfunc
+func Test_modeline_setoption_verbose()
+ let modeline = &modeline
+ set modeline
+
+ let lines =<< trim END
+ 1 vim:ts=2
+ 2 two
+ 3 three
+ 4 four
+ 5 five
+ 6 six
+ 7 seven
+ 8 eight
+ END
+ call writefile(lines, 'Xmodeline')
+ edit Xmodeline
+ let info = split(execute('verbose set tabstop?'), "\n")
+ call assert_match('^\s*Last set from modeline line 1$', info[-1])
+ bwipe!
+
+ let lines =<< trim END
+ 1 one
+ 2 two
+ 3 three
+ 4 vim:ts=4
+ 5 five
+ 6 six
+ 7 seven
+ 8 eight
+ END
+ call writefile(lines, 'Xmodeline')
+ edit Xmodeline
+ let info = split(execute('verbose set tabstop?'), "\n")
+ call assert_match('^\s*Last set from modeline line 4$', info[-1])
+ bwipe!
+
+ let lines =<< trim END
+ 1 one
+ 2 two
+ 3 three
+ 4 four
+ 5 five
+ 6 six
+ 7 seven
+ 8 vim:ts=8
+ END
+ call writefile(lines, 'Xmodeline')
+ edit Xmodeline
+ let info = split(execute('verbose set tabstop?'), "\n")
+ call assert_match('^\s*Last set from modeline line 8$', info[-1])
+ bwipe!
+
+ let &modeline = modeline
+ call delete('Xmodeline')
+endfunc
+
+" Test for the 'modeline' default value in compatible and non-compatible modes
+" for root and non-root accounts
+func Test_modeline_default()
+ " set compatible
+ " call assert_false(&modeline)
+ set nocompatible
+ call assert_equal(IsRoot() ? 0 : 1, &modeline)
+ " set compatible&vi
+ " call assert_false(&modeline)
+ set compatible&vim
+ call assert_equal(IsRoot() ? 0 : 1, &modeline)
+ set compatible& modeline&
+endfunc
+
+" Some options cannot be set from the modeline when 'diff' option is set
+func Test_modeline_diff_buffer()
+ call writefile(['vim: diff foldmethod=marker wrap'], 'Xfile')
+ set foldmethod& nowrap
+ new Xfile
+ call assert_equal('manual', &foldmethod)
+ call assert_false(&wrap)
+ set wrap&
+ call delete('Xfile')
+ bw
+endfunc
+
func Test_modeline_disable()
set modeline
call writefile(['vim: sw=2', 'vim: nomodeline', 'vim: sw=3'], 'Xmodeline_disable')
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index 2092b508ea..7c90b444e5 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -3,6 +3,7 @@
source shared.vim
source check.vim
source view_util.vim
+source vim9.vim
source screendump.vim
func Setup_NewWindow()
@@ -35,14 +36,14 @@ func CountSpaces(type, ...)
else
silent exe "normal! `[v`]y"
endif
- let g:a=strlen(substitute(@@, '[^ ]', '', 'g'))
+ let g:a = strlen(substitute(@@, '[^ ]', '', 'g'))
let &selection = sel_save
let @@ = reg_save
endfunc
func OpfuncDummy(type, ...)
" for testing operatorfunc
- let g:opt=&linebreak
+ let g:opt = &linebreak
if a:0 " Invoked from Visual mode, use gv command.
silent exe "normal! gvy"
@@ -53,7 +54,7 @@ func OpfuncDummy(type, ...)
endif
" Create a new dummy window
new
- let g:bufnr=bufnr('%')
+ let g:bufnr = bufnr('%')
endfunc
func Test_normal00_optrans()
@@ -120,6 +121,39 @@ func Test_normal01_keymodel()
call feedkeys("Vkk\<Up>yy", 'tx')
call assert_equal(['47', '48', '49', '50'], getreg(0, 0, 1))
+ " Test for using special keys to start visual selection
+ %d
+ call setline(1, ['red fox tail', 'red fox tail', 'red fox tail'])
+ set keymodel=startsel
+ " Test for <S-PageUp> and <S-PageDown>
+ call cursor(1, 1)
+ call feedkeys("\<S-PageDown>y", 'xt')
+ call assert_equal([0, 1, 1, 0], getpos("'<"))
+ call assert_equal([0, 3, 1, 0], getpos("'>"))
+ call feedkeys("Gz\<CR>8|\<S-PageUp>y", 'xt')
+ call assert_equal([0, 2, 1, 0], getpos("'<"))
+ call assert_equal([0, 3, 8, 0], getpos("'>"))
+ " Test for <S-C-Home> and <S-C-End>
+ call cursor(2, 12)
+ call feedkeys("\<S-C-Home>y", 'xt')
+ call assert_equal([0, 1, 1, 0], getpos("'<"))
+ call assert_equal([0, 2, 12, 0], getpos("'>"))
+ call cursor(1, 4)
+ call feedkeys("\<S-C-End>y", 'xt')
+ call assert_equal([0, 1, 4, 0], getpos("'<"))
+ call assert_equal([0, 3, 13, 0], getpos("'>"))
+ " Test for <S-C-Left> and <S-C-Right>
+ call cursor(2, 5)
+ call feedkeys("\<S-C-Right>y", 'xt')
+ call assert_equal([0, 2, 5, 0], getpos("'<"))
+ call assert_equal([0, 2, 9, 0], getpos("'>"))
+ call cursor(2, 9)
+ call feedkeys("\<S-C-Left>y", 'xt')
+ call assert_equal([0, 2, 5, 0], getpos("'<"))
+ call assert_equal([0, 2, 9, 0], getpos("'>"))
+
+ set keymodel&
+
" clean up
bw!
endfunc
@@ -140,16 +174,15 @@ func Test_normal03_join()
$
:j 10
call assert_equal('100', getline('.'))
+ call assert_beeps('normal GVJ')
" clean up
bw!
endfunc
+" basic filter test
func Test_normal04_filter()
- " basic filter test
" only test on non windows platform
- if has('win32')
- return
- endif
+ CheckNotMSWindows
call Setup_NewWindow()
1
call feedkeys("!!sed -e 's/^/| /'\n", 'tx')
@@ -222,12 +255,49 @@ func Test_normal_formatexpr_returns_nonzero()
close!
endfunc
+" Test for using a script-local function for 'formatexpr'
+func Test_formatexpr_scriptlocal_func()
+ func! s:Format()
+ let g:FormatArgs = [v:lnum, v:count]
+ endfunc
+ set formatexpr=s:Format()
+ call assert_equal(expand('<SID>') .. 'Format()', &formatexpr)
+ new | only
+ call setline(1, range(1, 40))
+ let g:FormatArgs = []
+ normal! 2GVjgq
+ call assert_equal([2, 2], g:FormatArgs)
+ bw!
+ set formatexpr=<SID>Format()
+ call assert_equal(expand('<SID>') .. 'Format()', &formatexpr)
+ new | only
+ call setline(1, range(1, 40))
+ let g:FormatArgs = []
+ normal! 4GVjgq
+ call assert_equal([4, 2], g:FormatArgs)
+ bw!
+ let &formatexpr = 's:Format()'
+ new | only
+ call setline(1, range(1, 40))
+ let g:FormatArgs = []
+ normal! 6GVjgq
+ call assert_equal([6, 2], g:FormatArgs)
+ bw!
+ let &formatexpr = '<SID>Format()'
+ new | only
+ call setline(1, range(1, 40))
+ let g:FormatArgs = []
+ normal! 8GVjgq
+ call assert_equal([8, 2], g:FormatArgs)
+ setlocal formatexpr=
+ delfunc s:Format
+ bw!
+endfunc
+
+" basic test for formatprg
func Test_normal06_formatprg()
- " basic test for formatprg
" only test on non windows platform
- if has('win32')
- return
- endif
+ CheckNotMSWindows
" uses sed to number non-empty lines
call writefile(['#!/bin/sh', 'sed ''/./=''|sed ''/./{', 'N', 's/\n/ /', '}'''], 'Xsed_format.sh')
@@ -240,16 +310,24 @@ func Test_normal06_formatprg()
set formatprg=./Xsed_format.sh
norm! gggqG
call assert_equal(expected, getline(1, '$'))
- bw!
+ %d
- 10new
call setline(1, text)
set formatprg=donothing
setlocal formatprg=./Xsed_format.sh
norm! gggqG
call assert_equal(expected, getline(1, '$'))
- bw!
+ %d
+
+ " Check for the command-line ranges added to 'formatprg'
+ set formatprg=cat
+ call setline(1, ['one', 'two', 'three', 'four', 'five'])
+ call feedkeys('gggqG', 'xt')
+ call assert_equal('.,$!cat', @:)
+ call feedkeys('2Ggq2j', 'xt')
+ call assert_equal('.,.+2!cat', @:)
+ bw!
" clean up
set formatprg=
setlocal formatprg=
@@ -263,18 +341,16 @@ func Test_normal07_internalfmt()
10new
call setline(1, list)
set tw=12
- norm! gggqG
+ norm! ggVGgq
call assert_equal(['1 2 3', '4 5 6', '7 8 9', '10 11 '], getline(1, '$'))
" clean up
set tw=0
bw!
endfunc
+" basic tests for foldopen/folddelete
func Test_normal08_fold()
- " basic tests for foldopen/folddelete
- if !has("folding")
- return
- endif
+ CheckFeature folding
call Setup_NewWindow()
50
setl foldenable fdm=marker
@@ -352,70 +428,6 @@ func Test_normal09a_operatorfunc()
norm V10j,,
call assert_equal(22, g:a)
- " Use a lambda function for 'opfunc'
- unmap <buffer> ,,
- call cursor(1, 1)
- let g:a=0
- nmap <buffer><silent> ,, :set opfunc={type\ ->\ CountSpaces(type)}<CR>g@
- vmap <buffer><silent> ,, :<C-U>call CountSpaces(visualmode(), 1)<CR>
- 50
- norm V2j,,
- call assert_equal(6, g:a)
- norm V,,
- call assert_equal(2, g:a)
- norm ,,l
- call assert_equal(0, g:a)
- 50
- exe "norm 0\<c-v>10j2l,,"
- call assert_equal(11, g:a)
- 50
- norm V10j,,
- call assert_equal(22, g:a)
-
- " use a partial function for 'opfunc'
- let g:OpVal = 0
- func! Test_opfunc1(x, y, type)
- let g:OpVal = a:x + a:y
- endfunc
- set opfunc=function('Test_opfunc1',\ [5,\ 7])
- normal! g@l
- call assert_equal(12, g:OpVal)
- " delete the function and try to use g@
- delfunc Test_opfunc1
- call test_garbagecollect_now()
- call assert_fails('normal! g@l', 'E117:')
- set opfunc=
-
- " use a funcref for 'opfunc'
- let g:OpVal = 0
- func! Test_opfunc2(x, y, type)
- let g:OpVal = a:x + a:y
- endfunc
- set opfunc=funcref('Test_opfunc2',\ [4,\ 3])
- normal! g@l
- call assert_equal(7, g:OpVal)
- " delete the function and try to use g@
- delfunc Test_opfunc2
- call test_garbagecollect_now()
- call assert_fails('normal! g@l', 'E933:')
- set opfunc=
-
- " Try to use a function with two arguments for 'operatorfunc'
- let g:OpVal = 0
- func! Test_opfunc3(x, y)
- let g:OpVal = 4
- endfunc
- set opfunc=Test_opfunc3
- call assert_fails('normal! g@l', 'E119:')
- call assert_equal(0, g:OpVal)
- set opfunc=
- delfunc Test_opfunc3
- unlet g:OpVal
-
- " Try to use a lambda function with two arguments for 'operatorfunc'
- set opfunc={x,\ y\ ->\ 'done'}
- call assert_fails('normal! g@l', 'E119:')
-
" clean up
unmap <buffer> ,,
set opfunc=
@@ -484,6 +496,227 @@ func Test_normal09c_operatorfunc()
set operatorfunc=
endfunc
+" Test for different ways of setting the 'operatorfunc' option
+func Test_opfunc_callback()
+ new
+ func OpFunc1(callnr, type)
+ let g:OpFunc1Args = [a:callnr, a:type]
+ endfunc
+ func OpFunc2(type)
+ let g:OpFunc2Args = [a:type]
+ endfunc
+
+ let lines =<< trim END
+ #" Test for using a function name
+ LET &opfunc = 'g:OpFunc2'
+ LET g:OpFunc2Args = []
+ normal! g@l
+ call assert_equal(['char'], g:OpFunc2Args)
+
+ #" Test for using a function()
+ set opfunc=function('g:OpFunc1',\ [10])
+ LET g:OpFunc1Args = []
+ normal! g@l
+ call assert_equal([10, 'char'], g:OpFunc1Args)
+
+ #" Using a funcref variable to set 'operatorfunc'
+ VAR Fn = function('g:OpFunc1', [11])
+ LET &opfunc = Fn
+ LET g:OpFunc1Args = []
+ normal! g@l
+ call assert_equal([11, 'char'], g:OpFunc1Args)
+
+ #" Using a string(funcref_variable) to set 'operatorfunc'
+ LET Fn = function('g:OpFunc1', [12])
+ LET &operatorfunc = string(Fn)
+ LET g:OpFunc1Args = []
+ normal! g@l
+ call assert_equal([12, 'char'], g:OpFunc1Args)
+
+ #" Test for using a funcref()
+ set operatorfunc=funcref('g:OpFunc1',\ [13])
+ LET g:OpFunc1Args = []
+ normal! g@l
+ call assert_equal([13, 'char'], g:OpFunc1Args)
+
+ #" Using a funcref variable to set 'operatorfunc'
+ LET Fn = funcref('g:OpFunc1', [14])
+ LET &opfunc = Fn
+ LET g:OpFunc1Args = []
+ normal! g@l
+ call assert_equal([14, 'char'], g:OpFunc1Args)
+
+ #" Using a string(funcref_variable) to set 'operatorfunc'
+ LET Fn = funcref('g:OpFunc1', [15])
+ LET &opfunc = string(Fn)
+ LET g:OpFunc1Args = []
+ normal! g@l
+ call assert_equal([15, 'char'], g:OpFunc1Args)
+
+ #" Test for using a lambda function using set
+ VAR optval = "LSTART a LMIDDLE OpFunc1(16, a) LEND"
+ LET optval = substitute(optval, ' ', '\\ ', 'g')
+ exe "set opfunc=" .. optval
+ LET g:OpFunc1Args = []
+ normal! g@l
+ call assert_equal([16, 'char'], g:OpFunc1Args)
+
+ #" Test for using a lambda function using LET
+ LET &opfunc = LSTART a LMIDDLE OpFunc1(17, a) LEND
+ LET g:OpFunc1Args = []
+ normal! g@l
+ call assert_equal([17, 'char'], g:OpFunc1Args)
+
+ #" Set 'operatorfunc' to a string(lambda expression)
+ LET &opfunc = 'LSTART a LMIDDLE OpFunc1(18, a) LEND'
+ LET g:OpFunc1Args = []
+ normal! g@l
+ call assert_equal([18, 'char'], g:OpFunc1Args)
+
+ #" Set 'operatorfunc' to a variable with a lambda expression
+ VAR Lambda = LSTART a LMIDDLE OpFunc1(19, a) LEND
+ LET &opfunc = Lambda
+ LET g:OpFunc1Args = []
+ normal! g@l
+ call assert_equal([19, 'char'], g:OpFunc1Args)
+
+ #" Set 'operatorfunc' to a string(variable with a lambda expression)
+ LET Lambda = LSTART a LMIDDLE OpFunc1(20, a) LEND
+ LET &opfunc = string(Lambda)
+ LET g:OpFunc1Args = []
+ normal! g@l
+ call assert_equal([20, 'char'], g:OpFunc1Args)
+
+ #" Try to use 'operatorfunc' after the function is deleted
+ func g:TmpOpFunc1(type)
+ let g:TmpOpFunc1Args = [21, a:type]
+ endfunc
+ LET &opfunc = function('g:TmpOpFunc1')
+ delfunc g:TmpOpFunc1
+ call test_garbagecollect_now()
+ LET g:TmpOpFunc1Args = []
+ call assert_fails('normal! g@l', 'E117:')
+ call assert_equal([], g:TmpOpFunc1Args)
+
+ #" Try to use a function with two arguments for 'operatorfunc'
+ func g:TmpOpFunc2(x, y)
+ let g:TmpOpFunc2Args = [a:x, a:y]
+ endfunc
+ set opfunc=TmpOpFunc2
+ LET g:TmpOpFunc2Args = []
+ call assert_fails('normal! g@l', 'E119:')
+ call assert_equal([], g:TmpOpFunc2Args)
+ delfunc TmpOpFunc2
+
+ #" Try to use a lambda function with two arguments for 'operatorfunc'
+ LET &opfunc = LSTART a, b LMIDDLE OpFunc1(22, b) LEND
+ LET g:OpFunc1Args = []
+ call assert_fails('normal! g@l', 'E119:')
+ call assert_equal([], g:OpFunc1Args)
+
+ #" Test for clearing the 'operatorfunc' option
+ set opfunc=''
+ set opfunc&
+ call assert_fails("set opfunc=function('abc')", "E700:")
+ call assert_fails("set opfunc=funcref('abc')", "E700:")
+
+ #" set 'operatorfunc' to a non-existing function
+ LET &opfunc = function('g:OpFunc1', [23])
+ call assert_fails("set opfunc=function('NonExistingFunc')", 'E700:')
+ call assert_fails("LET &opfunc = function('NonExistingFunc')", 'E700:')
+ LET g:OpFunc1Args = []
+ normal! g@l
+ call assert_equal([23, 'char'], g:OpFunc1Args)
+ END
+ call CheckTransLegacySuccess(lines)
+
+ " Test for using a script-local function name
+ func s:OpFunc3(type)
+ let g:OpFunc3Args = [a:type]
+ endfunc
+ set opfunc=s:OpFunc3
+ let g:OpFunc3Args = []
+ normal! g@l
+ call assert_equal(['char'], g:OpFunc3Args)
+
+ let &opfunc = 's:OpFunc3'
+ let g:OpFunc3Args = []
+ normal! g@l
+ call assert_equal(['char'], g:OpFunc3Args)
+ delfunc s:OpFunc3
+
+ " Using Vim9 lambda expression in legacy context should fail
+ " set opfunc=(a)\ =>\ OpFunc1(24,\ a)
+ let g:OpFunc1Args = []
+ " call assert_fails('normal! g@l', 'E117:')
+ call assert_equal([], g:OpFunc1Args)
+
+ " set 'operatorfunc' to a partial with dict. This used to cause a crash.
+ func SetOpFunc()
+ let operator = {'execute': function('OperatorExecute')}
+ let &opfunc = operator.execute
+ endfunc
+ func OperatorExecute(_) dict
+ endfunc
+ call SetOpFunc()
+ call test_garbagecollect_now()
+ set operatorfunc=
+ delfunc SetOpFunc
+ delfunc OperatorExecute
+
+ " Vim9 tests
+ let lines =<< trim END
+ vim9script
+
+ def g:Vim9opFunc(val: number, type: string): void
+ g:OpFunc1Args = [val, type]
+ enddef
+
+ # Test for using a def function with opfunc
+ set opfunc=function('g:Vim9opFunc',\ [60])
+ g:OpFunc1Args = []
+ normal! g@l
+ assert_equal([60, 'char'], g:OpFunc1Args)
+
+ # Test for using a global function name
+ &opfunc = g:OpFunc2
+ g:OpFunc2Args = []
+ normal! g@l
+ assert_equal(['char'], g:OpFunc2Args)
+ bw!
+
+ # Test for using a script-local function name
+ def s:LocalOpFunc(type: string): void
+ g:LocalOpFuncArgs = [type]
+ enddef
+ &opfunc = s:LocalOpFunc
+ g:LocalOpFuncArgs = []
+ normal! g@l
+ assert_equal(['char'], g:LocalOpFuncArgs)
+ bw!
+ END
+ call CheckScriptSuccess(lines)
+
+ " setting 'opfunc' to a script local function outside of a script context
+ " should fail
+ let cleanup =<< trim END
+ call writefile([execute('messages')], 'Xtest.out')
+ qall
+ END
+ call writefile(cleanup, 'Xverify.vim')
+ call RunVim([], [], "-c \"set opfunc=s:abc\" -S Xverify.vim")
+ call assert_match('E81: Using <SID> not in a', readfile('Xtest.out')[0])
+ call delete('Xtest.out')
+ call delete('Xverify.vim')
+
+ " cleanup
+ set opfunc&
+ delfunc OpFunc1
+ delfunc OpFunc2
+ unlet g:OpFunc1Args g:OpFunc2Args
+ %bw!
+endfunc
+
func Test_normal10_expand()
" Test for expand()
10new
@@ -506,6 +739,14 @@ func Test_normal10_expand()
call assert_equal(expected[i], expand('<cexpr>'), 'i == ' . i)
endfor
+ " Test for <cexpr> in state.val and ptr->val
+ call setline(1, 'x = state.val;')
+ call cursor(1, 10)
+ call assert_equal('state.val', expand('<cexpr>'))
+ call setline(1, 'x = ptr->val;')
+ call cursor(1, 9)
+ call assert_equal('ptr->val', expand('<cexpr>'))
+
if executable('echo')
" Test expand(`...`) i.e. backticks command expansion.
" MS-Windows has a trailing space.
@@ -514,11 +755,25 @@ func Test_normal10_expand()
" Test expand(`=...`) i.e. backticks expression expansion
call assert_equal('5', expand('`=2+3`'))
+ call assert_equal('3.14', expand('`=3.14`'))
" clean up
bw!
endfunc
+" Test for expand() in latin1 encoding
+func Test_normal_expand_latin1()
+ new
+ let save_enc = &encoding
+ " set encoding=latin1
+ call setline(1, 'val = item->color;')
+ call cursor(1, 11)
+ call assert_equal('color', expand("<cword>"))
+ call assert_equal('item->color', expand("<cexpr>"))
+ let &encoding = save_enc
+ bw!
+endfunc
+
func Test_normal11_showcmd()
" test for 'showcmd'
10new
@@ -543,6 +798,13 @@ func Test_normal11_showcmd()
redraw!
call assert_match('1-3$', Screenline(&lines))
call feedkeys("v", 'xt')
+ " test for visually selecting the end of line
+ call setline(1, ["foobar"])
+ call feedkeys("$vl", 'xt')
+ redraw!
+ call assert_match('2$', Screenline(&lines))
+ call feedkeys("y", 'xt')
+ call assert_equal("r\n", @")
bw!
endfunc
@@ -1024,6 +1286,22 @@ func Test_vert_scroll_cmds()
close!
endfunc
+func Test_scroll_in_ex_mode()
+ " This was using invalid memory because w_botline was invalid.
+ let lines =<< trim END
+ diffsplit
+ norm os00(
+ call writefile(['done'], 'Xdone')
+ qa!
+ END
+ call writefile(lines, 'Xscript')
+ call assert_equal(1, RunVim([], [], '--clean -X -Z -e -s -S Xscript'))
+ call assert_equal(['done'], readfile('Xdone'))
+
+ call delete('Xscript')
+ call delete('Xdone')
+endfunc
+
" Test for the 'sidescroll' option
func Test_sidescroll_opt()
new
@@ -1431,10 +1709,8 @@ func Test_normal18_z_fold()
endfunc
func Test_normal20_exmode()
- if !has("unix")
- " Reading from redirected file doesn't work on MS-Windows
- return
- endif
+ " Reading from redirected file doesn't work on MS-Windows
+ CheckNotMSWindows
call writefile(['1a', 'foo', 'bar', '.', 'w! Xfile2', 'q!'], 'Xscript')
call writefile(['1', '2'], 'Xfile')
call system(GetVimCommand() .. ' -e -s < Xscript Xfile')
@@ -1455,6 +1731,7 @@ func Test_normal21_nv_hat()
edit Xfoo | %bw
call assert_fails(':buffer #', 'E86')
call assert_fails(':execute "normal! \<C-^>"', 'E23')
+ call assert_fails("normal i\<C-R>#", 'E23:')
" Test for the expected behavior when switching between two named buffers.
edit Xfoo | edit Xbar
@@ -1586,7 +1863,7 @@ func Test_normal23_K()
call setline(1, '---')
call assert_fails('normal! ggv2lK', 'E349:')
call setline(1, ['abc', 'xyz'])
- call assert_fails("normal! gg2lv2h\<C-]>", 'E426:')
+ call assert_fails("normal! gg2lv2h\<C-]>", 'E433:')
call assert_beeps("normal! ggVjK")
" clean up
@@ -2061,6 +2338,16 @@ func Test_normal30_changecase()
call assert_equal(['aaaaaa', 'AAAAaa'], getline(1, 2))
set whichwrap&
+ " try changing the case with a double byte encoding (DBCS)
+ %bw!
+ let enc = &enc
+ " set encoding=cp932
+ call setline(1, "\u8470")
+ normal ~
+ normal gU$gu$gUgUg~g~gugu
+ call assert_equal("\u8470", getline(1))
+ let &encoding = enc
+
" clean up
bw!
endfunc
@@ -2152,6 +2439,19 @@ func Test_normal31_r_cmd()
" r command should fail in operator pending mode
call assert_beeps('normal! cr')
+ " replace a tab character in visual mode
+ %d
+ call setline(1, ["a\tb", "c\td", "e\tf"])
+ normal gglvjjrx
+ call assert_equal(['axx', 'xxx', 'xxf'], getline(1, '$'))
+
+ " replace with a multibyte character (with multiple composing characters)
+ %d
+ new
+ call setline(1, 'aaa')
+ exe "normal $ra\u0328\u0301"
+ call assert_equal("aaa\u0328\u0301", getline(1))
+
" clean up
set noautoindent
bw!
@@ -2176,9 +2476,7 @@ endfunc
" 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
+ CheckFeature jumplist
call Setup_NewWindow()
" Test for g`
clearjumps
@@ -2637,7 +2935,6 @@ endfunc
" Test for cw cW ce
func Test_normal39_cw()
" Test for cw and cW on whitespace
- " and cpo+=w setting
new
set tw=0
call append(0, 'here are some words')
@@ -2645,14 +2942,6 @@ func Test_normal39_cw()
call assert_equal('hereZZZare some words', getline('.'))
norm! 1gg0elcWYYY
call assert_equal('hereZZZareYYYsome words', getline('.'))
- " Nvim: no "w" flag in 'cpoptions'.
- " set cpo+=w
- " call setline(1, 'here are some words')
- " norm! 1gg0elcwZZZ
- " call assert_equal('hereZZZ are some words', getline('.'))
- " norm! 1gg2elcWYYY
- " call assert_equal('hereZZZ areYYY some words', getline('.'))
- set cpo-=w
norm! 2gg0cwfoo
call assert_equal('foo', getline('.'))
@@ -2812,24 +3101,6 @@ func Test_normal47_visual_buf_wipe()
set nomodified
endfunc
-func Test_normal47_autocmd()
- " disabled, does not seem to be possible currently
- throw "Skipped: not possible to test cursorhold autocmd while waiting for input in normal_cmd"
- new
- call append(0, repeat('-',20))
- au CursorHold * call feedkeys('2l', '')
- 1
- set updatetime=20
- " should delete 12 chars (d12l)
- call feedkeys('d1', '!')
- call assert_equal('--------', getline(1))
-
- " clean up
- au! CursorHold
- set updatetime=4000
- bw!
-endfunc
-
func Test_normal48_wincmd()
new
exe "norm! \<c-w>c"
@@ -2847,9 +3118,8 @@ func Test_normal49_counts()
endfunc
func Test_normal50_commandline()
- if !has("timers") || !has("cmdline_hist")
- return
- endif
+ CheckFeature timers
+ CheckFeature cmdline_hist
func! DoTimerWork(id)
call assert_equal('[Command Line]', bufname(''))
" should fail, with E11, but does fail with E23?
@@ -2878,9 +3148,7 @@ func Test_normal50_commandline()
endfunc
func Test_normal51_FileChangedRO()
- if !has("autocmd")
- return
- endif
+ CheckFeature autocmd
call writefile(['foo'], 'Xreadonly.log')
new Xreadonly.log
setl ro
@@ -2895,9 +3163,7 @@ func Test_normal51_FileChangedRO()
endfunc
func Test_normal52_rl()
- if !has("rightleft")
- return
- endif
+ CheckFeature rightleft
new
call setline(1, 'abcde fghij klmnopq')
norm! 1gg$
@@ -2929,22 +3195,6 @@ func Test_normal52_rl()
bw!
endfunc
-func Test_normal53_digraph()
- if !has('digraphs')
- return
- endif
- new
- call setline(1, 'abcdefgh|')
- exe "norm! 1gg0f\<c-k>!!"
- call assert_equal(9, col('.'))
- set cpo+=D
- exe "norm! 1gg0f\<c-k>!!"
- call assert_equal(1, col('.'))
-
- set cpo-=D
- bw!
-endfunc
-
func Test_normal54_Ctrl_bsl()
new
call setline(1, 'abcdefghijklmn')
@@ -3265,46 +3515,6 @@ func Test_normal_gk_gj()
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
@@ -3369,6 +3579,31 @@ func Test_normal_colon_op()
close!
endfunc
+" Test for d and D commands
+func Test_normal_delete_cmd()
+ new
+ " D in an empty line
+ call setline(1, '')
+ normal D
+ call assert_equal('', getline(1))
+ " D in an empty line in virtualedit mode
+ set virtualedit=all
+ normal D
+ call assert_equal('', getline(1))
+ set virtualedit&
+ " delete to a readonly register
+ call setline(1, ['abcd'])
+ call assert_beeps('normal ":d2l')
+
+ " D and d with 'nomodifiable'
+ call setline(1, ['abcd'])
+ setlocal nomodifiable
+ call assert_fails('normal D', 'E21:')
+ call assert_fails('normal d$', 'E21:')
+
+ close!
+endfunc
+
" Test for deleting or changing characters across lines with 'whichwrap'
" containing 's'. Should count <EOL> as one character.
func Test_normal_op_across_lines()
@@ -3476,6 +3711,27 @@ func Test_normal_percent_jump()
close!
endfunc
+" Test for << and >> commands to shift text by 'shiftwidth'
+func Test_normal_shift_rightleft()
+ new
+ call setline(1, ['one', '', "\t", ' two', "\tthree", ' four'])
+ set shiftwidth=2 tabstop=8
+ normal gg6>>
+ call assert_equal([' one', '', "\t ", ' two', "\t three", "\tfour"],
+ \ getline(1, '$'))
+ normal ggVG2>>
+ call assert_equal([' one', '', "\t ", "\ttwo",
+ \ "\t three", "\t four"], getline(1, '$'))
+ normal gg6<<
+ call assert_equal([' one', '', "\t ", ' two', "\t three",
+ \ "\t four"], getline(1, '$'))
+ normal ggVG2<<
+ call assert_equal(['one', '', "\t", ' two', "\tthree", ' four'],
+ \ getline(1, '$'))
+ set shiftwidth& tabstop&
+ bw!
+endfunc
+
" Some commands like yy, cc, dd, >>, << and !! accept a count after
" typing the first letter of the command.
func Test_normal_count_after_operator()
@@ -3539,4 +3795,37 @@ func Test_normal_count_out_of_range()
bwipe!
endfunc
+" Test that mouse shape is restored to Normal mode after failed "c" operation.
+func Test_mouse_shape_after_failed_change()
+ CheckFeature mouseshape
+ CheckCanRunGui
+
+ let lines =<< trim END
+ set mouseshape+=o:busy
+ setlocal nomodifiable
+ let g:mouse_shapes = []
+
+ func SaveMouseShape(timer)
+ let g:mouse_shapes += [getmouseshape()]
+ endfunc
+
+ func SaveAndQuit(timer)
+ call writefile(g:mouse_shapes, 'Xmouseshapes')
+ quit
+ endfunc
+
+ call timer_start(50, {_ -> feedkeys('c')})
+ call timer_start(100, 'SaveMouseShape')
+ call timer_start(150, {_ -> feedkeys('c')})
+ call timer_start(200, 'SaveMouseShape')
+ call timer_start(250, 'SaveAndQuit')
+ END
+ call writefile(lines, 'Xmouseshape.vim', 'D')
+ call RunVim([], [], "-g -S Xmouseshape.vim")
+ sleep 300m
+ call assert_equal(['busy', 'arrow'], readfile('Xmouseshapes'))
+
+ call delete('Xmouseshapes')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim
index fdfc1c0f89..f51de94bac 100644
--- a/src/nvim/testdir/test_options.vim
+++ b/src/nvim/testdir/test_options.vim
@@ -22,6 +22,21 @@ func Test_whichwrap()
set whichwrap=h,h,h
call assert_equal('h', &whichwrap)
+ " For compatibility with Vim 3.0 and before, number values are also
+ " supported for 'whichwrap'
+ set whichwrap=1
+ call assert_equal('b', &whichwrap)
+ set whichwrap=2
+ call assert_equal('s', &whichwrap)
+ set whichwrap=4
+ call assert_equal('h,l', &whichwrap)
+ set whichwrap=8
+ call assert_equal('<,>', &whichwrap)
+ set whichwrap=16
+ call assert_equal('[,]', &whichwrap)
+ set whichwrap=31
+ call assert_equal('b,s,h,l,<,>,[,]', &whichwrap)
+
set whichwrap&
endfunc
@@ -142,6 +157,20 @@ func Test_path_keep_commas()
set path&
endfunc
+func Test_path_too_long()
+ exe 'set path=' .. repeat('x', 10000)
+ call assert_fails('find x', 'E854:')
+ set path&
+endfunc
+
+func Test_signcolumn()
+ CheckFeature signs
+ call assert_equal("auto", &signcolumn)
+ set signcolumn=yes
+ set signcolumn=no
+ call assert_fails('set signcolumn=nope')
+endfunc
+
func Test_filetype_valid()
set ft=valid_name
call assert_equal("valid_name", &filetype)
@@ -234,6 +263,7 @@ func Test_complete()
new
call feedkeys("i\<C-N>\<Esc>", 'xt')
bwipe!
+ call assert_fails('set complete=ix', 'E535:')
set complete&
endfun
@@ -247,7 +277,7 @@ func Test_set_completion()
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>
+ " Expand boolean 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')
call assert_equal('"set nodiff digraph', @:)
@@ -266,6 +296,15 @@ func Test_set_completion()
call feedkeys(":set fileencodings:\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"set fileencodings:ucs-bom,utf-8,default,latin1', @:)
+ " Expand key codes.
+ " call feedkeys(":set <H\<C-A>\<C-B>\"\<CR>", 'tx')
+ " call assert_equal('"set <Help> <Home>', @:)
+
+ " Expand terminal options.
+ " call feedkeys(":set t_A\<C-A>\<C-B>\"\<CR>", 'tx')
+ " call assert_equal('"set t_AB t_AF t_AU t_AL', @:)
+ " call assert_fails('call feedkeys(":set <t_afoo>=\<C-A>\<CR>", "xt")', 'E474:')
+
" Expand directories.
call feedkeys(":set cdpath=./\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_match('./samples/ ', @:)
@@ -361,10 +400,20 @@ func Test_set_errors()
call assert_fails('set winminwidth=10 winwidth=9', 'E592:')
call assert_fails("set showbreak=\x01", 'E595:')
call assert_fails('set t_foo=', 'E846:')
+ call assert_fails('set tabstop??', 'E488:')
+ call assert_fails('set wrapscan!!', 'E488:')
+ call assert_fails('set tabstop&&', 'E488:')
+ call assert_fails('set wrapscan<<', 'E488:')
+ call assert_fails('set wrapscan=1', 'E474:')
+ call assert_fails('set autoindent@', 'E488:')
+ call assert_fails('set wildchar=<abc>', 'E474:')
+ call assert_fails('set cmdheight=1a', 'E521:')
+ call assert_fails('set invcmdheight', 'E474:')
if has('python') || has('python3')
call assert_fails('set pyxversion=6', 'E474:')
endif
call assert_fails("let &tabstop='ab'", 'E521:')
+ call assert_fails('set spellcapcheck=%\\(', 'E54:')
call assert_fails('set sessionoptions=curdir,sesdir', 'E474:')
call assert_fails('set foldmarker={{{,', 'E474:')
call assert_fails('set sessionoptions=sesdir,curdir', 'E474:')
@@ -384,6 +433,7 @@ func Test_set_errors()
set nomodifiable
call assert_fails('set fileencoding=latin1', 'E21:')
set modifiable&
+ " call assert_fails('set t_#-&', 'E522:')
endfunc
func CheckWasSet(name)
@@ -397,9 +447,8 @@ endfunc
" Must be executed before other tests that set 'term'.
func Test_000_term_option_verbose()
- if has('nvim') || has('gui_running')
- return
- endif
+ throw "Skipped: Nvim does not support setting 'term'"
+ CheckNotGui
call CheckWasNotSet('t_cm')
@@ -431,32 +480,37 @@ func Test_copy_context()
endfunc
func Test_set_ttytype()
- " Nvim does not support 'ttytype'.
- if !has('nvim') && !has('gui_running') && has('unix')
- " Setting 'ttytype' used to cause a double-free when exiting vim and
- " when vim is compiled with -DEXITFREE.
- set ttytype=ansi
- call assert_equal('ansi', &ttytype)
- call assert_equal(&ttytype, &term)
- set ttytype=xterm
- call assert_equal('xterm', &ttytype)
- call assert_equal(&ttytype, &term)
- try
- set ttytype=
- call assert_report('set ttytype= did not fail')
- catch /E529/
- endtry
-
- " Some systems accept any terminal name and return dumb settings,
- " check for failure of finding the entry and for missing 'cm' entry.
- try
- set ttytype=xxx
- call assert_report('set ttytype=xxx did not fail')
- catch /E522\|E437/
- endtry
-
- set ttytype&
- call assert_equal(&ttytype, &term)
+ throw "Skipped: Nvim does not support 'ttytype'"
+ CheckUnix
+ CheckNotGui
+
+ " Setting 'ttytype' used to cause a double-free when exiting vim and
+ " when vim is compiled with -DEXITFREE.
+ set ttytype=ansi
+ call assert_equal('ansi', &ttytype)
+ call assert_equal(&ttytype, &term)
+ set ttytype=xterm
+ call assert_equal('xterm', &ttytype)
+ call assert_equal(&ttytype, &term)
+ try
+ set ttytype=
+ call assert_report('set ttytype= did not fail')
+ catch /E529/
+ endtry
+
+ " Some systems accept any terminal name and return dumb settings,
+ " check for failure of finding the entry and for missing 'cm' entry.
+ try
+ set ttytype=xxx
+ call assert_report('set ttytype=xxx did not fail')
+ catch /E522\|E437/
+ endtry
+
+ set ttytype&
+ call assert_equal(&ttytype, &term)
+
+ if has('gui') && !has('gui_running')
+ call assert_fails('set term=gui', 'E531:')
endif
endfunc
@@ -774,7 +828,13 @@ func Test_shell()
CheckUnix
let save_shell = &shell
set shell=
- call assert_fails('shell', 'E91:')
+ let caught_e91 = 0
+ try
+ shell
+ catch /E91:/
+ let caught_e91 = 1
+ endtry
+ call assert_equal(1, caught_e91)
let &shell = save_shell
endfunc
@@ -833,6 +893,107 @@ func Test_debug_option()
set debug&
endfunc
+" Test for the default CDPATH option
+func Test_opt_default_cdpath()
+ let after =<< trim [CODE]
+ call assert_equal(',/path/to/dir1,/path/to/dir2', &cdpath)
+ call writefile(v:errors, 'Xtestout')
+ qall
+ [CODE]
+ if has('unix')
+ let $CDPATH='/path/to/dir1:/path/to/dir2'
+ else
+ let $CDPATH='/path/to/dir1;/path/to/dir2'
+ endif
+ if RunVim([], after, '')
+ call assert_equal([], readfile('Xtestout'))
+ call delete('Xtestout')
+ endif
+endfunc
+
+" Test for setting keycodes using set
+func Test_opt_set_keycode()
+ call assert_fails('set <t_k1=l', 'E474:')
+ call assert_fails('set <Home=l', 'E474:')
+ set <t_k9>=abcd
+ " call assert_equal('abcd', &t_k9)
+ set <t_k9>&
+ set <F9>=xyz
+ " call assert_equal('xyz', &t_k9)
+ set <t_k9>&
+endfunc
+
+" Test for changing options in a sandbox
+func Test_opt_sandbox()
+ for opt in ['backupdir', 'cdpath', 'exrc']
+ call assert_fails('sandbox set ' .. opt .. '?', 'E48:')
+ endfor
+endfunc
+
+" Test for setting an option with local value to global value
+func Test_opt_local_to_global()
+ setglobal equalprg=gprg
+ setlocal equalprg=lprg
+ call assert_equal('gprg', &g:equalprg)
+ call assert_equal('lprg', &l:equalprg)
+ call assert_equal('lprg', &equalprg)
+ set equalprg<
+ call assert_equal('', &l:equalprg)
+ call assert_equal('gprg', &equalprg)
+ setglobal equalprg=gnewprg
+ setlocal equalprg=lnewprg
+ setlocal equalprg<
+ call assert_equal('gnewprg', &l:equalprg)
+ call assert_equal('gnewprg', &equalprg)
+ set equalprg&
+
+ " Test for setting the global/local value of a boolean option
+ setglobal autoread
+ setlocal noautoread
+ call assert_false(&autoread)
+ set autoread<
+ call assert_true(&autoread)
+ setglobal noautoread
+ setlocal autoread
+ setlocal autoread<
+ call assert_false(&autoread)
+ set autoread&
+endfunc
+
+func Test_set_in_sandbox()
+ " Some boolean options cannot be set in sandbox, some can.
+ call assert_fails('sandbox set modelineexpr', 'E48:')
+ sandbox set number
+ call assert_true(&number)
+ set number&
+
+ " Some boolean options cannot be set in sandbox, some can.
+ if has('python') || has('python3')
+ call assert_fails('sandbox set pyxversion=3', 'E48:')
+ endif
+ sandbox set tabstop=4
+ call assert_equal(4, &tabstop)
+ set tabstop&
+
+ " Some string options cannot be set in sandbox, some can.
+ call assert_fails('sandbox set backupdir=/tmp', 'E48:')
+ sandbox set filetype=perl
+ call assert_equal('perl', &filetype)
+ set filetype&
+endfunc
+
+" Test for incrementing, decrementing and multiplying a number option value
+func Test_opt_num_op()
+ set shiftwidth=4
+ set sw+=2
+ call assert_equal(6, &sw)
+ set sw-=2
+ call assert_equal(4, &sw)
+ set sw^=2
+ call assert_equal(8, &sw)
+ set shiftwidth&
+endfunc
+
" Test for setting option values using v:false and v:true
func Test_opt_boolean()
set number&
@@ -969,6 +1130,93 @@ func Test_opt_reset_scroll()
call delete('Xscroll')
endfunc
+" Check that VIM_POSIX env variable influences default value of 'cpo' and 'shm'
+func Test_VIM_POSIX()
+ throw 'Skipped: Nvim does not support $VIM_POSIX'
+ let saved_VIM_POSIX = getenv("VIM_POSIX")
+
+ call setenv('VIM_POSIX', "1")
+ let after =<< trim [CODE]
+ call writefile([&cpo, &shm], 'X_VIM_POSIX')
+ qall
+ [CODE]
+ if RunVim([], after, '')
+ call assert_equal(['aAbBcCdDeEfFgHiIjJkKlLmMnoOpPqrRsStuvwWxXyZ$!%*-+<>#{|&/\.;',
+ \ 'AS'], readfile('X_VIM_POSIX'))
+ endif
+
+ call setenv('VIM_POSIX', v:null)
+ let after =<< trim [CODE]
+ call writefile([&cpo, &shm], 'X_VIM_POSIX')
+ qall
+ [CODE]
+ if RunVim([], after, '')
+ call assert_equal(['aAbBcCdDeEfFgHiIjJkKlLmMnoOpPqrRsStuvwWxXyZ$!%*-+<>;',
+ \ 'S'], readfile('X_VIM_POSIX'))
+ endif
+
+ call delete('X_VIM_POSIX')
+ call setenv('VIM_POSIX', saved_VIM_POSIX)
+endfunc
+
+" Test for setting an option to a Vi or Vim default
+func Test_opt_default()
+ throw 'Skipped: Nvim has different defaults'
+ set formatoptions&vi
+ call assert_equal('vt', &formatoptions)
+ set formatoptions&vim
+ call assert_equal('tcq', &formatoptions)
+endfunc
+
+" Test for the 'cmdheight' option
+func Test_cmdheight()
+ %bw!
+ let ht = &lines
+ set cmdheight=9999
+ call assert_equal(1, winheight(0))
+ call assert_equal(ht - 1, &cmdheight)
+ set cmdheight&
+endfunc
+
+" To specify a control character as a option value, '^' can be used
+func Test_opt_control_char()
+ set wildchar=^v
+ call assert_equal("\<C-V>", nr2char(&wildchar))
+ set wildcharm=^r
+ call assert_equal("\<C-R>", nr2char(&wildcharm))
+ " Bug: This doesn't work for the 'cedit' and 'termwinkey' options
+ set wildchar& wildcharm&
+endfunc
+
+" Test for the 'errorbells' option
+func Test_opt_errorbells()
+ set errorbells
+ call assert_beeps('s/a1b2/x1y2/')
+ set noerrorbells
+endfunc
+
+func Test_opt_scrolljump()
+ help
+ resize 10
+
+ " Test with positive 'scrolljump'.
+ set scrolljump=2
+ norm! Lj
+ call assert_equal({'lnum':11, 'leftcol':0, 'col':0, 'topfill':0,
+ \ 'topline':3, 'coladd':0, 'skipcol':0, 'curswant':0},
+ \ winsaveview())
+
+ " Test with negative 'scrolljump' (percentage of window height).
+ set scrolljump=-40
+ norm! ggLj
+ call assert_equal({'lnum':11, 'leftcol':0, 'col':0, 'topfill':0,
+ \ 'topline':5, 'coladd':0, 'skipcol':0, 'curswant':0},
+ \ winsaveview())
+
+ set scrolljump&
+ bw
+endfunc
+
" Test for the 'cdhome' option
func Test_opt_cdhome()
if has('unix') || has('vms')
@@ -990,6 +1238,30 @@ func Test_opt_cdhome()
set cdhome&
endfunc
+func Test_set_completion_2()
+ CheckOption termguicolors
+
+ " Test default option completion
+ set wildoptions=
+ call feedkeys(":set termg\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"set termguicolors', @:)
+
+ call feedkeys(":set notermg\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"set notermguicolors', @:)
+
+ " Test fuzzy option completion
+ set wildoptions=fuzzy
+ call feedkeys(":set termg\<C-A>\<C-B>\"\<CR>", 'tx')
+ " Nvim doesn't have 'termencoding'
+ " call assert_equal('"set termguicolors termencoding', @:)
+ call assert_equal('"set termguicolors', @:)
+
+ call feedkeys(":set notermg\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"set notermguicolors', @:)
+
+ set wildoptions=
+endfunc
+
func Test_switchbuf_reset()
set switchbuf=useopen
sblast
@@ -1003,4 +1275,30 @@ func Test_switchbuf_reset()
only!
endfunc
+" :set empty string for global 'keywordprg' falls back to ":help"
+func Test_keywordprg_empty()
+ let k = &keywordprg
+ set keywordprg=man
+ call assert_equal('man', &keywordprg)
+ set keywordprg=
+ call assert_equal(':help', &keywordprg)
+ set keywordprg=man
+ call assert_equal('man', &keywordprg)
+ call assert_equal("\n keywordprg=:help", execute('set kp= kp?'))
+ let &keywordprg = k
+endfunc
+
+" check that the very first buffer created does not have 'endoffile' set
+func Test_endoffile_default()
+ let after =<< trim [CODE]
+ call writefile([execute('set eof?')], 'Xtestout')
+ qall!
+ [CODE]
+ if RunVim([], after, '')
+ call assert_equal(["\nnoendoffile"], readfile('Xtestout'))
+ endif
+ call delete('Xtestout')
+endfunc
+
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_partial.vim b/src/nvim/testdir/test_partial.vim
index 8c90f21600..3020668f1b 100644
--- a/src/nvim/testdir/test_partial.vim
+++ b/src/nvim/testdir/test_partial.vim
@@ -82,6 +82,9 @@ func Test_partial_dict()
let dict = {"tr": function('tr', ['hello', 'h', 'H'])}
call assert_equal("Hello", dict.tr())
+
+ call assert_fails("let F=function('setloclist', 10)", "E923:")
+ call assert_fails("let F=function('setloclist', [], [])", "E922:")
endfunc
func Test_partial_implicit()
@@ -354,3 +357,5 @@ func Test_compare_partials()
call assert_true(F1 isnot# F1d1) " Partial /= non-partial
call assert_true(d1.f1 isnot# d1.f1) " handle_subscript creates new partial each time
endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_paste.vim b/src/nvim/testdir/test_paste.vim
new file mode 100644
index 0000000000..dad3c2c6a0
--- /dev/null
+++ b/src/nvim/testdir/test_paste.vim
@@ -0,0 +1,76 @@
+
+" Test for 'pastetoggle'
+func Test_pastetoggle()
+ new
+ set pastetoggle=<F4>
+ set nopaste
+ call feedkeys("iHello\<F4>", 'xt')
+ call assert_true(&paste)
+ call feedkeys("i\<F4>", 'xt')
+ call assert_false(&paste)
+ call assert_equal('Hello', getline(1))
+ " command-line completion for 'pastetoggle' value
+ call feedkeys(":set pastetoggle=\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"set pastetoggle=<F4>', @:)
+ set pastetoggle&
+ bwipe!
+endfunc
+
+" Test for restoring option values when 'paste' is disabled
+func Test_paste_opt_restore()
+ set autoindent expandtab ruler showmatch
+ if has('rightleft')
+ set revins hkmap
+ endif
+ set smarttab softtabstop=3 textwidth=27 wrapmargin=12
+ if has('vartabs')
+ set varsofttabstop=10,20
+ endif
+
+ " enabling 'paste' should reset the above options
+ set paste
+ call assert_false(&autoindent)
+ call assert_false(&expandtab)
+ if has('rightleft')
+ call assert_false(&revins)
+ call assert_false(&hkmap)
+ endif
+ call assert_false(&ruler)
+ call assert_false(&showmatch)
+ call assert_false(&smarttab)
+ call assert_equal(0, &softtabstop)
+ call assert_equal(0, &textwidth)
+ call assert_equal(0, &wrapmargin)
+ if has('vartabs')
+ call assert_equal('', &varsofttabstop)
+ endif
+
+ " disabling 'paste' should restore the option values
+ set nopaste
+ call assert_true(&autoindent)
+ call assert_true(&expandtab)
+ if has('rightleft')
+ call assert_true(&revins)
+ call assert_true(&hkmap)
+ endif
+ call assert_true(&ruler)
+ call assert_true(&showmatch)
+ call assert_true(&smarttab)
+ call assert_equal(3, &softtabstop)
+ call assert_equal(27, &textwidth)
+ call assert_equal(12, &wrapmargin)
+ if has('vartabs')
+ call assert_equal('10,20', &varsofttabstop)
+ endif
+
+ set autoindent& expandtab& ruler& showmatch&
+ if has('rightleft')
+ set revins& hkmap&
+ endif
+ set smarttab& softtabstop& textwidth& wrapmargin&
+ if has('vartabs')
+ set varsofttabstop&
+ endif
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_popup.vim b/src/nvim/testdir/test_popup.vim
index 3d1e3fa6db..791cce4431 100644
--- a/src/nvim/testdir/test_popup.vim
+++ b/src/nvim/testdir/test_popup.vim
@@ -359,7 +359,7 @@ func Test_completefunc_opens_new_window_one()
/^one
call assert_fails('call feedkeys("A\<C-X>\<C-U>\<C-N>\<Esc>", "x")', 'E565:')
call assert_equal(winid, win_getid())
- call assert_equal('oneDEF', getline(1))
+ call assert_equal('onedef', getline(1))
q!
endfunc
@@ -384,9 +384,7 @@ func Test_completefunc_opens_new_window_two()
/^two
call assert_fails('call feedkeys("A\<C-X>\<C-U>\<C-N>\<Esc>", "x")', 'E565:')
call assert_equal(winid, win_getid())
- " v8.2.1919 hasn't been ported yet
- " call assert_equal('twodef', getline(1))
- call assert_equal('twoDEF', getline(1))
+ call assert_equal('twodef', getline(1))
q!
endfunc
@@ -677,9 +675,9 @@ func Test_complete_CTRLN_startofbuffer()
endfunc
func Test_popup_and_window_resize()
- if !has('terminal') || has('gui_running')
- return
- endif
+ CheckFeature terminal
+ CheckNotGui
+
let h = winheight(0)
if h < 15
return
@@ -864,7 +862,6 @@ func Test_popup_position()
endfunc
func Test_popup_command()
- CheckScreendump
CheckFeature menu
menu Test.Foo Foo
@@ -872,6 +869,23 @@ func Test_popup_command()
call assert_fails('popup Test.Foo.X', 'E327:')
call assert_fails('popup Foo', 'E337:')
unmenu Test.Foo
+endfunc
+
+func Test_popup_command_dump()
+ CheckFeature menu
+ CheckScreendump
+
+ let script =<< trim END
+ func StartTimer()
+ call timer_start(100, {-> ChangeMenu()})
+ endfunc
+ func ChangeMenu()
+ aunmenu PopUp.&Paste
+ nnoremenu 1.40 PopUp.&Paste :echomsg "pasted"<CR>
+ echomsg 'changed'
+ endfunc
+ END
+ call writefile(script, 'XtimerScript')
let lines =<< trim END
one two three four five
@@ -879,12 +893,12 @@ func Test_popup_command()
one more two three four five
END
call writefile(lines, 'Xtest')
- let buf = RunVimInTerminal('Xtest', {})
+ let buf = RunVimInTerminal('-S XtimerScript Xtest', {})
call term_sendkeys(buf, ":source $VIMRUNTIME/menu.vim\<CR>")
call term_sendkeys(buf, "/X\<CR>:popup PopUp\<CR>")
call VerifyScreenDump(buf, 'Test_popup_command_01', {})
- " Select a word
+ " go to the Paste entry in the menu
call term_sendkeys(buf, "jj")
call VerifyScreenDump(buf, 'Test_popup_command_02', {})
@@ -893,8 +907,20 @@ func Test_popup_command()
call VerifyScreenDump(buf, 'Test_popup_command_03', {})
call term_sendkeys(buf, "\<Esc>")
+
+ " Set a timer to change a menu entry while it's displayed. The text should
+ " not change but the command does. Making the screendump also verifies that
+ " "changed" shows up, which means the timer triggered
+ call term_sendkeys(buf, "/X\<CR>:call StartTimer() | popup PopUp\<CR>")
+ call VerifyScreenDump(buf, 'Test_popup_command_04', {})
+
+ " Select the Paste entry, executes the changed menu item.
+ call term_sendkeys(buf, "jj\<CR>")
+ call VerifyScreenDump(buf, 'Test_popup_command_05', {})
+
call StopVimInTerminal(buf)
call delete('Xtest')
+ call delete('XtimerScript')
endfunc
func Test_popup_complete_backwards()
@@ -948,9 +974,9 @@ func Test_complete_o_tab()
endfunc
func Test_menu_only_exists_in_terminal()
- if !exists(':tlmenu') || has('gui_running')
- return
- endif
+ CheckCommand tlmenu
+ CheckNotGui
+
tlnoremenu &Edit.&Paste<Tab>"+gP <C-W>"+
aunmenu *
try
@@ -1129,15 +1155,15 @@ func Test_CompleteChanged()
autocmd! AAAAA_Group
set complete& completeopt&
- delfunc! OnPumchange
+ delfunc! OnPumChange
bw!
endfunc
-function! GetPumPosition()
+func GetPumPosition()
call assert_true( pumvisible() )
let g:pum_pos = pum_getpos()
return ''
-endfunction
+endfunc
func Test_pum_getpos()
new
diff --git a/src/nvim/testdir/test_preview.vim b/src/nvim/testdir/test_preview.vim
index 6c4ae414d3..b7b908e761 100644
--- a/src/nvim/testdir/test_preview.vim
+++ b/src/nvim/testdir/test_preview.vim
@@ -1,5 +1,8 @@
" Tests for the preview window
+source check.vim
+CheckFeature quickfix
+
func Test_Psearch()
" this used to cause ml_get errors
help
@@ -13,6 +16,8 @@ func Test_Psearch()
endfunc
func Test_window_preview()
+ CheckFeature quickfix
+
" Open a preview window
pedit Xa
call assert_equal(2, winnr('$'))
@@ -32,6 +37,8 @@ func Test_window_preview()
endfunc
func Test_window_preview_from_help()
+ CheckFeature quickfix
+
filetype on
call writefile(['/* some C code */'], 'Xpreview.c')
help
diff --git a/src/nvim/testdir/test_profile.vim b/src/nvim/testdir/test_profile.vim
index fdb6f13e2b..9165f7bace 100644
--- a/src/nvim/testdir/test_profile.vim
+++ b/src/nvim/testdir/test_profile.vim
@@ -40,8 +40,8 @@ func Test_profile_func()
call writefile(lines, 'Xprofile_func.vim')
call system(GetVimCommand()
\ . ' -es --clean'
- \ . ' -c "so Xprofile_func.vim"'
- \ . ' -c "qall!"')
+ \ . ' --cmd "so Xprofile_func.vim"'
+ \ . ' --cmd "qall!"')
call assert_equal(0, v:shell_error)
let lines = readfile('Xprofile_func.log')
@@ -403,6 +403,47 @@ func Test_profile_completion()
call feedkeys(":profile start test_prof\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_match('^"profile start.* test_profile\.vim', @:)
+
+ call feedkeys(":profile file test_prof\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_match('"profile file test_profile\.vim', @:)
+ call feedkeys(":profile file test_prof\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_match('"profile file test_profile\.vim', @:)
+ call feedkeys(":profile file test_prof \<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_match('"profile file test_prof ', @:)
+ call feedkeys(":profile file X1B2C3\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_match('"profile file X1B2C3', @:)
+
+ func Xprof_test()
+ endfunc
+ call feedkeys(":profile func Xprof\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"profile func Xprof_test', @:)
+ call feedkeys(":profile func Xprof\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"profile func Xprof_test', @:)
+ call feedkeys(":profile func Xprof \<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"profile func Xprof ', @:)
+ call feedkeys(":profile func X1B2C3\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"profile func X1B2C3', @:)
+
+ call feedkeys(":profdel \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"profdel file func', @:)
+ call feedkeys(":profdel fu\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"profdel func', @:)
+ call feedkeys(":profdel he\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"profdel he', @:)
+ call feedkeys(":profdel here \<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"profdel here ', @:)
+ call feedkeys(":profdel file test_prof\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"profdel file test_profile.vim', @:)
+ call feedkeys(":profdel file X1B2C3\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"profdel file X1B2C3', @:)
+ call feedkeys(":profdel func Xprof\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"profdel func Xprof_test', @:)
+ call feedkeys(":profdel func Xprof_test \<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"profdel func Xprof_test ', @:)
+ call feedkeys(":profdel func X1B2C3\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"profdel func X1B2C3', @:)
+
+ delfunc Xprof_test
endfunc
func Test_profile_errors()
@@ -475,7 +516,7 @@ func Test_profdel_func()
call Foo3()
[CODE]
call writefile(lines, 'Xprofile_file.vim')
- call system(GetVimCommandClean() . ' -es -c "so Xprofile_file.vim" -c q')
+ call system(GetVimCommandClean() . ' -es --cmd "so Xprofile_file.vim" --cmd q')
call assert_equal(0, v:shell_error)
let lines = readfile('Xprofile_file.log')
diff --git a/src/nvim/testdir/test_prompt_buffer.vim b/src/nvim/testdir/test_prompt_buffer.vim
index 8f94a8572b..b8f6c5240c 100644
--- a/src/nvim/testdir/test_prompt_buffer.vim
+++ b/src/nvim/testdir/test_prompt_buffer.vim
@@ -180,6 +180,8 @@ func Test_prompt_buffer_edit()
call assert_beeps('normal! S')
call assert_beeps("normal! \<C-A>")
call assert_beeps("normal! \<C-X>")
+ call assert_beeps("normal! dp")
+ call assert_beeps("normal! do")
" pressing CTRL-W in the prompt buffer should trigger the window commands
call assert_equal(1, winnr())
exe "normal A\<C-W>\<C-W>"
@@ -223,7 +225,7 @@ func Test_prompt_buffer_getbufinfo()
%bwipe!
endfunc
-function! Test_prompt_while_writing_to_hidden_buffer()
+func Test_prompt_while_writing_to_hidden_buffer()
throw 'skipped: TODO'
call CanTestPromptBuffer()
CheckUnix
diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim
index f6d573d76b..8dc4173d60 100644
--- a/src/nvim/testdir/test_quickfix.vim
+++ b/src/nvim/testdir/test_quickfix.vim
@@ -1,6 +1,7 @@
" Test for the quickfix feature.
source check.vim
+source vim9.vim
CheckFeature quickfix
source screendump.vim
@@ -85,6 +86,12 @@ func s:setup_commands(cchar)
endif
endfunc
+" This must be run before any error lists are created.
+func Test_AA_cc_no_errors()
+ call assert_fails('cc', 'E42:')
+ call assert_fails('ll', 'E42:')
+endfunc
+
" Tests for the :clist and :llist commands
func XlistTests(cchar)
call s:setup_commands(a:cchar)
@@ -98,9 +105,15 @@ func XlistTests(cchar)
call assert_true(v:errmsg ==# 'E42: No Errors')
" Populate the list and then try
- Xgetexpr ['non-error 1', 'Xtestfile1:1:3:Line1',
- \ 'non-error 2', 'Xtestfile2:2:2:Line2',
- \ 'non-error| 3', 'Xtestfile3:3:1:Line3']
+ let lines =<< trim END
+ non-error 1
+ Xtestfile1:1:3:Line1
+ non-error 2
+ Xtestfile2:2:2:Line2
+ non-error| 3
+ Xtestfile3:3:1:Line3
+ END
+ Xgetexpr lines
" List only valid entries
let l = split(execute('Xlist', ''), "\n")
@@ -255,6 +268,7 @@ func XwindowTests(cchar)
" Opening the location list window without any errors should fail
if a:cchar == 'l'
call assert_fails('lopen', 'E776:')
+ call assert_fails('lwindow', 'E776:')
endif
" Create a list with no valid entries
@@ -265,8 +279,12 @@ func XwindowTests(cchar)
call assert_true(winnr('$') == 1)
" Create a list with valid entries
- Xgetexpr ['Xtestfile1:1:3:Line1', 'Xtestfile2:2:2:Line2',
- \ 'Xtestfile3:3:1:Line3']
+ let lines =<< trim END
+ Xtestfile1:1:3:Line1
+ Xtestfile2:2:2:Line2
+ Xtestfile3:3:1:Line3
+ END
+ Xgetexpr lines
" Open the window
Xwindow
@@ -329,8 +347,12 @@ func XwindowTests(cchar)
if a:cchar == 'c'
" Opening the quickfix window in multiple tab pages should reuse the
" quickfix buffer
- Xgetexpr ['Xtestfile1:1:3:Line1', 'Xtestfile2:2:2:Line2',
- \ 'Xtestfile3:3:1:Line3']
+ let lines =<< trim END
+ Xtestfile1:1:3:Line1
+ Xtestfile2:2:2:Line2
+ Xtestfile3:3:1:Line3
+ END
+ Xgetexpr lines
Xopen
let qfbufnum = bufnr('%')
tabnew
@@ -365,14 +387,16 @@ func Test_copenHeight_tabline()
set tabline& showtabline&
endfunc
-
" Tests for the :cfile, :lfile, :caddfile, :laddfile, :cgetfile and :lgetfile
" commands.
func XfileTests(cchar)
call s:setup_commands(a:cchar)
- call writefile(['Xtestfile1:700:10:Line 700',
- \ 'Xtestfile2:800:15:Line 800'], 'Xqftestfile1')
+ let lines =<< trim END
+ Xtestfile1:700:10:Line 700
+ Xtestfile2:800:15:Line 800
+ END
+ call writefile(lines, 'Xqftestfile1')
enew!
Xfile Xqftestfile1
@@ -396,8 +420,11 @@ func XfileTests(cchar)
call assert_true(len(l) == 3 &&
\ l[2].lnum == 900 && l[2].col == 30 && l[2].text ==# 'Line 900')
- call writefile(['Xtestfile1:222:77:Line 222',
- \ 'Xtestfile2:333:88:Line 333'], 'Xqftestfile1')
+ let lines =<< trim END
+ Xtestfile1:222:77:Line 222
+ Xtestfile2:333:88:Line 333
+ END
+ call writefile(lines, 'Xqftestfile1')
enew!
Xgetfile Xqftestfile1
@@ -427,8 +454,11 @@ func XbufferTests(cchar)
call s:setup_commands(a:cchar)
enew!
- silent! call setline(1, ['Xtestfile7:700:10:Line 700',
- \ 'Xtestfile8:800:15:Line 800'])
+ let lines =<< trim END
+ Xtestfile7:700:10:Line 700
+ Xtestfile8:800:15:Line 800
+ END
+ silent! call setline(1, lines)
Xbuffer!
let l = g:Xgetlist()
call assert_true(len(l) == 2 &&
@@ -436,8 +466,11 @@ func XbufferTests(cchar)
\ l[1].lnum == 800 && l[1].col == 15 && l[1].text ==# 'Line 800')
enew!
- silent! call setline(1, ['Xtestfile9:900:55:Line 900',
- \ 'Xtestfile10:950:66:Line 950'])
+ let lines =<< trim END
+ Xtestfile9:900:55:Line 900
+ Xtestfile10:950:66:Line 950
+ END
+ silent! call setline(1, lines)
Xgetbuffer
let l = g:Xgetlist()
call assert_true(len(l) == 2 &&
@@ -445,8 +478,11 @@ func XbufferTests(cchar)
\ l[1].lnum == 950 && l[1].col == 66 && l[1].text ==# 'Line 950')
enew!
- silent! call setline(1, ['Xtestfile11:700:20:Line 700',
- \ 'Xtestfile12:750:25:Line 750'])
+ let lines =<< trim END
+ Xtestfile11:700:20:Line 700
+ Xtestfile12:750:25:Line 750
+ END
+ silent! call setline(1, lines)
Xaddbuffer
let l = g:Xgetlist()
call assert_true(len(l) == 4 &&
@@ -516,12 +552,15 @@ func Xtest_browse(cchar)
call s:create_test_file('Xqftestfile1')
call s:create_test_file('Xqftestfile2')
- Xgetexpr ['Xqftestfile1:5:Line5',
- \ 'Xqftestfile1:6:Line6',
- \ 'Xqftestfile2:10:Line10',
- \ 'Xqftestfile2:11:Line11',
- \ 'RegularLine1',
- \ 'RegularLine2']
+ let lines =<< trim END
+ Xqftestfile1:5:Line5
+ Xqftestfile1:6:Line6
+ Xqftestfile2:10:Line10
+ Xqftestfile2:11:Line11
+ RegularLine1
+ RegularLine2
+ END
+ Xgetexpr lines
Xfirst
call assert_fails('-5Xcc', 'E16:')
@@ -571,10 +610,13 @@ func Xtest_browse(cchar)
call assert_equal(5, line('.'))
" Jumping to an error from the error window using cc command
- Xgetexpr ['Xqftestfile1:5:Line5',
- \ 'Xqftestfile1:6:Line6',
- \ 'Xqftestfile2:10:Line10',
- \ 'Xqftestfile2:11:Line11']
+ let lines =<< trim END
+ Xqftestfile1:5:Line5
+ Xqftestfile1:6:Line6
+ Xqftestfile2:10:Line10
+ Xqftestfile2:11:Line11
+ END
+ Xgetexpr lines
Xopen
10Xcc
call assert_equal(11, line('.'))
@@ -699,7 +741,7 @@ func s:test_xhelpgrep(cchar)
" Search for non existing help string
call assert_fails('Xhelpgrep a1b2c3', 'E480:')
" Invalid regular expression
- call assert_fails('Xhelpgrep \@<!', 'E480:')
+ call assert_fails('Xhelpgrep \@<!', 'E866:')
endfunc
func Test_helpgrep()
@@ -708,6 +750,35 @@ func Test_helpgrep()
call s:test_xhelpgrep('l')
endfunc
+" When running the :helpgrep command, if an autocmd modifies the 'cpoptions'
+" value, then Vim crashes. (issue fixed by 7.2b-004 and 8.2.4453)
+func Test_helpgrep_restore_cpo_aucmd()
+ let save_cpo = &cpo
+ augroup QF_Test
+ au!
+ autocmd BufNew * set cpo=acd
+ augroup END
+
+ helpgrep quickfix
+ call assert_equal('acd', &cpo)
+ %bw!
+
+ set cpo&vim
+ augroup QF_Test
+ au!
+ autocmd BufReadPost * set cpo=
+ augroup END
+
+ helpgrep buffer
+ call assert_equal('', &cpo)
+
+ augroup QF_Test
+ au!
+ augroup END
+ %bw!
+ let &cpo = save_cpo
+endfunc
+
func Test_errortitle()
augroup QfBufWinEnter
au!
@@ -1073,20 +1144,21 @@ func s:dir_stack_tests(cchar)
let save_efm=&efm
set efm=%DEntering\ dir\ '%f',%f:%l:%m,%XLeaving\ dir\ '%f'
- 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'"
- \]
+ let lines =<< trim END
+ 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'
+ END
Xexpr ""
for l in lines
@@ -1120,19 +1192,19 @@ func Test_efm_dirstack()
call mkdir('dir1/c')
call mkdir('dir2')
- 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"
- \ ]
-
+ let lines =<< trim END
+ 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
+ END
call writefile(lines, 'habits1.txt')
call writefile(lines, 'dir1/a/habits2.txt')
call writefile(lines, 'dir1/a/b/habits3.txt')
@@ -1158,7 +1230,13 @@ func Xefm_ignore_continuations(cchar)
\ '%-Wignored %m %l,' .
\ '%+Cmore ignored %m %l,' .
\ '%Zignored end'
- Xgetexpr ['ignored warning 1', 'more ignored continuation 2', 'ignored end', 'error resync 4']
+ let lines =<< trim END
+ ignored warning 1
+ more ignored continuation 2
+ ignored end
+ error resync 4
+ END
+ Xgetexpr lines
let l = map(g:Xgetlist(), '[v:val.text, v:val.valid, v:val.lnum, v:val.type]')
call assert_equal([['resync', 1, 4, 'E']], l)
@@ -1204,8 +1282,14 @@ func Xinvalid_efm_Tests(cchar)
set efm=
call assert_fails('Xexpr "abc.txt:1:Hello world"', 'E378:')
+ " Empty directory name. When there is an error in parsing new entries, make
+ " sure the previous quickfix list is made the current list.
+ set efm&
+ cexpr ["one", "two"]
+ let qf_id = getqflist(#{id: 0}).id
set efm=%DEntering\ dir\ abc,%f:%l:%m
call assert_fails('Xexpr ["Entering dir abc", "abc.txt:1:Hello world"]', 'E379:')
+ call assert_equal(qf_id, getqflist(#{id: 0}).id)
let &efm = save_efm
endfunc
@@ -1396,8 +1480,14 @@ func Test_efm_error_type()
" error type
set efm=%f:%l:%t:%m
- cexpr ["Xfile1:10:E:msg1", "Xfile1:20:W:msg2", "Xfile1:30:I:msg3",
- \ "Xfile1:40:N:msg4", "Xfile1:50:R:msg5"]
+ let lines =<< trim END
+ Xfile1:10:E:msg1
+ Xfile1:20:W:msg2
+ Xfile1:30:I:msg3
+ Xfile1:40:N:msg4
+ Xfile1:50:R:msg5
+ END
+ cexpr lines
let output = split(execute('clist'), "\n")
call assert_equal([
\ ' 1 Xfile1:10 error: msg1',
@@ -1408,8 +1498,14 @@ func Test_efm_error_type()
" error type and a error number
set efm=%f:%l:%t:%n:%m
- cexpr ["Xfile1:10:E:2:msg1", "Xfile1:20:W:4:msg2", "Xfile1:30:I:6:msg3",
- \ "Xfile1:40:N:8:msg4", "Xfile1:50:R:3:msg5"]
+ let lines =<< trim END
+ Xfile1:10:E:2:msg1
+ Xfile1:20:W:4:msg2
+ Xfile1:30:I:6:msg3
+ Xfile1:40:N:8:msg4
+ Xfile1:50:R:3:msg5
+ END
+ cexpr lines
let output = split(execute('clist'), "\n")
call assert_equal([
\ ' 1 Xfile1:10 error 2: msg1',
@@ -1434,8 +1530,13 @@ func Test_efm_end_lnum_col()
" 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 lines =<< trim END
+ 1)msg1
+ Xfile1:14-24:1-2
+ 2)msg2
+ Xfile1:24-34:3-4
+ END
+ cexpr lines
let output = split(execute('clist'), "\n")
call assert_equal([
\ ' 1 Xfile1:14-24 col 1-2 error 1: msg1',
@@ -1459,7 +1560,7 @@ func XquickfixChangedByAutocmd(cchar)
endfunc
endif
- augroup testgroup
+ augroup QF_Test
au!
autocmd BufReadCmd test_changed.txt call ReadFunc()
augroup END
@@ -1473,7 +1574,24 @@ func XquickfixChangedByAutocmd(cchar)
endfor
call assert_fails('Xrewind', ErrorNr . ':')
- augroup! testgroup
+ augroup QF_Test
+ au!
+ augroup END
+
+ if a:cchar == 'c'
+ cexpr ["Xtest1:1:Line"]
+ cwindow
+ only
+ augroup QF_Test
+ au!
+ autocmd WinEnter * call setqflist([], 'f')
+ augroup END
+ call assert_fails('exe "normal \<CR>"', 'E925:')
+ augroup QF_Test
+ au!
+ augroup END
+ endif
+ %bw!
endfunc
func Test_quickfix_was_changed_by_autocmd()
@@ -1611,6 +1729,9 @@ func SetXlistTests(cchar, bnum)
\ " {'bufnr':999, 'lnum':5}])", 'E92:')
call g:Xsetlist([[1, 2,3]])
call assert_equal(0, len(g:Xgetlist()))
+ call assert_fails('call g:Xsetlist([], [])', 'E928:')
+ call g:Xsetlist([v:_null_dict])
+ call assert_equal([], g:Xgetlist())
endfunc
func Test_setqflist()
@@ -1825,12 +1946,12 @@ func Test_cgetfile_on_long_lines()
" Problematic values if the line is longer than 4096 bytes. Then 1024 bytes
" are read at a time.
for len in [4078, 4079, 4080, 5102, 5103, 5104, 6126, 6127, 6128, 7150, 7151, 7152]
- let lines = [
- \ '/tmp/file1:1:1:aaa',
- \ '/tmp/file2:1:1:%s',
- \ '/tmp/file3:1:1:bbb',
- \ '/tmp/file4:1:1:ccc',
- \ ]
+ let lines =<< trim END
+ /tmp/file1:1:1:aaa
+ /tmp/file2:1:1:%s
+ /tmp/file3:1:1:bbb
+ /tmp/file4:1:1:ccc
+ END
let lines[1] = substitute(lines[1], '%s', repeat('x', len), '')
call writefile(lines, 'Xcqetfile.txt')
cgetfile Xcqetfile.txt
@@ -1857,12 +1978,15 @@ func Test_switchbuf()
let file1_winid = win_getid()
new Xqftestfile2
let file2_winid = win_getid()
- cgetexpr ['Xqftestfile1:5:Line5',
- \ 'Xqftestfile1:6:Line6',
- \ 'Xqftestfile2:10:Line10',
- \ 'Xqftestfile2:11:Line11',
- \ 'Xqftestfile3:15:Line15',
- \ 'Xqftestfile3:16:Line16']
+ let lines =<< trim END
+ Xqftestfile1:5:Line5
+ Xqftestfile1:6:Line6
+ Xqftestfile2:10:Line10
+ Xqftestfile2:11:Line11
+ Xqftestfile3:15:Line15
+ Xqftestfile3:16:Line16
+ END
+ cgetexpr lines
new
let winid = win_getid()
@@ -2524,21 +2648,23 @@ func Test_Autocmd()
silent! cexpr non_existing_func()
silent! caddexpr non_existing_func()
silent! cgetexpr non_existing_func()
- let l = ['precexpr',
- \ 'postcexpr',
- \ 'precaddexpr',
- \ 'postcaddexpr',
- \ 'precgetexpr',
- \ 'postcgetexpr',
- \ 'precexpr',
- \ 'postcexpr',
- \ 'precaddexpr',
- \ 'postcaddexpr',
- \ 'precgetexpr',
- \ 'postcgetexpr',
- \ 'precexpr',
- \ 'precaddexpr',
- \ 'precgetexpr']
+ let l =<< trim END
+ precexpr
+ postcexpr
+ precaddexpr
+ postcaddexpr
+ precgetexpr
+ postcgetexpr
+ precexpr
+ postcexpr
+ precaddexpr
+ postcaddexpr
+ precgetexpr
+ postcgetexpr
+ precexpr
+ precaddexpr
+ precgetexpr
+ END
call assert_equal(l, g:acmds)
let g:acmds = []
@@ -2556,15 +2682,17 @@ func Test_Autocmd()
exe 'silent! cgetbuffer ' . bnum
exe 'silent! caddbuffer ' . bnum
enew!
- let l = ['precbuffer',
- \ 'postcbuffer',
- \ 'precgetbuffer',
- \ 'postcgetbuffer',
- \ 'precaddbuffer',
- \ 'postcaddbuffer',
- \ 'precbuffer',
- \ 'precgetbuffer',
- \ 'precaddbuffer']
+ let l =<< trim END
+ precbuffer
+ postcbuffer
+ precgetbuffer
+ postcgetbuffer
+ precaddbuffer
+ postcaddbuffer
+ precbuffer
+ precgetbuffer
+ precaddbuffer
+ END
call assert_equal(l, g:acmds)
call writefile(['Xtest:1:Line1'], 'Xtest')
@@ -2579,24 +2707,26 @@ func Test_Autocmd()
silent! cfile do_not_exist
silent! caddfile do_not_exist
silent! cgetfile do_not_exist
- let l = ['precfile',
- \ 'postcfile',
- \ 'precaddfile',
- \ 'postcaddfile',
- \ 'precgetfile',
- \ 'postcgetfile',
- \ 'precfile',
- \ 'postcfile',
- \ 'precaddfile',
- \ 'postcaddfile',
- \ 'precgetfile',
- \ 'postcgetfile',
- \ 'precfile',
- \ 'postcfile',
- \ 'precaddfile',
- \ 'postcaddfile',
- \ 'precgetfile',
- \ 'postcgetfile']
+ let l =<< trim END
+ precfile
+ postcfile
+ precaddfile
+ postcaddfile
+ precgetfile
+ postcgetfile
+ precfile
+ postcfile
+ precaddfile
+ postcaddfile
+ precgetfile
+ postcgetfile
+ precfile
+ postcfile
+ precaddfile
+ postcaddfile
+ precgetfile
+ postcgetfile
+ END
call assert_equal(l, g:acmds)
let g:acmds = []
@@ -2609,20 +2739,22 @@ func Test_Autocmd()
set makeprg=
silent! make
set makeprg&
- let l = ['prehelpgrep',
- \ 'posthelpgrep',
- \ 'prehelpgrep',
- \ 'posthelpgrep',
- \ 'previmgrep',
- \ 'postvimgrep',
- \ 'previmgrepadd',
- \ 'postvimgrepadd',
- \ 'previmgrep',
- \ 'postvimgrep',
- \ 'previmgrepadd',
- \ 'postvimgrepadd',
- \ 'premake',
- \ 'postmake']
+ let l =<< trim END
+ prehelpgrep
+ posthelpgrep
+ prehelpgrep
+ posthelpgrep
+ previmgrep
+ postvimgrep
+ previmgrepadd
+ postvimgrepadd
+ previmgrep
+ postvimgrep
+ previmgrepadd
+ postvimgrepadd
+ premake
+ postmake
+ END
call assert_equal(l, g:acmds)
if has('unix')
@@ -2642,22 +2774,24 @@ func Test_Autocmd()
silent lgrep Grep_Autocmd_Text test_quickfix.vim
silent lgrepadd GrepAdd_Autocmd_Text test_quickfix.vim
set grepprg&vim
- let l = ['pregrep',
- \ 'postgrep',
- \ 'pregrepadd',
- \ 'postgrepadd',
- \ 'pregrep',
- \ 'postgrep',
- \ 'pregrepadd',
- \ 'postgrepadd',
- \ 'pregrep',
- \ 'postgrep',
- \ 'pregrepadd',
- \ 'postgrepadd',
- \ 'prelgrep',
- \ 'postlgrep',
- \ 'prelgrepadd',
- \ 'postlgrepadd']
+ let l =<< trim END
+ pregrep
+ postgrep
+ pregrepadd
+ postgrepadd
+ pregrep
+ postgrep
+ pregrepadd
+ postgrepadd
+ pregrep
+ postgrep
+ pregrepadd
+ postgrepadd
+ prelgrep
+ postlgrep
+ prelgrepadd
+ postlgrepadd
+ END
call assert_equal(l, g:acmds)
endif
@@ -2816,15 +2950,16 @@ func Test_cwindow_highlight()
CheckScreendump
let lines =<< trim END
- call setline(1, ['some', 'text', 'with', 'matches'])
- write XCwindow
- vimgrep e XCwindow
- redraw
- cwindow 4
+ call setline(1, ['some', 'text', 'with', 'matches'])
+ write XCwindow
+ vimgrep e XCwindow
+ redraw
+ cwindow 4
END
call writefile(lines, 'XtestCwindow')
let buf = RunVimInTerminal('-S XtestCwindow', #{rows: 12})
call VerifyScreenDump(buf, 'Test_quickfix_cwindow_1', {})
+
call term_sendkeys(buf, ":cnext\<CR>")
call VerifyScreenDump(buf, 'Test_quickfix_cwindow_2', {})
@@ -2837,10 +2972,13 @@ endfunc
func XvimgrepTests(cchar)
call s:setup_commands(a:cchar)
- call writefile(['Editor:VIM vim',
- \ 'Editor:Emacs EmAcS',
- \ 'Editor:Notepad NOTEPAD'], 'Xtestfile1')
- call writefile(['Linux', 'MacOS', 'MS-Windows'], 'Xtestfile2')
+ let lines =<< trim END
+ Editor:VIM vim
+ Editor:Emacs EmAcS
+ Editor:Notepad NOTEPAD
+ END
+ call writefile(lines, 'Xtestfile1')
+ call writefile(['Linux', 'macOS', 'MS-Windows'], 'Xtestfile2')
" Error cases
call assert_fails('Xvimgrep /abc *', 'E682:')
@@ -2854,7 +2992,7 @@ func XvimgrepTests(cchar)
Xexpr ""
Xvimgrepadd Notepad Xtestfile1
- Xvimgrepadd MacOS Xtestfile2
+ Xvimgrepadd macOS Xtestfile2
let l = g:Xgetlist()
call assert_equal(2, len(l))
call assert_equal('Editor:Notepad NOTEPAD', l[0].text)
@@ -2884,6 +3022,19 @@ func XvimgrepTests(cchar)
call assert_equal(0, getbufinfo('Xtestfile1')[0].loaded)
call assert_equal([], getbufinfo('Xtestfile2'))
+ " Test for opening the dummy buffer used by vimgrep in a window. The new
+ " window should be closed
+ %bw!
+ augroup QF_Test
+ au!
+ autocmd BufReadPre * exe "sb " .. expand("<abuf>")
+ augroup END
+ call assert_fails("Xvimgrep /sublime/ Xtestfile1", 'E480:')
+ call assert_equal(1, winnr('$'))
+ augroup QF_Test
+ au!
+ augroup END
+
call delete('Xtestfile1')
call delete('Xtestfile2')
endfunc
@@ -3087,7 +3238,7 @@ func Test_cclose_from_copen()
endfunc
func Test_cclose_in_autocmd()
- " Problem is only triggered if "starting" is zero, so that the OptionsSet
+ " Problem is only triggered if "starting" is zero, so that the OptionSet
" event will be triggered.
" call test_override('starting', 1)
augroup QF_Test
@@ -3102,7 +3253,7 @@ func Test_cclose_in_autocmd()
" call test_override('starting', 0)
endfunc
-" Check that ":file" without an argument is possible even when curbuf is locked
+" Check that ":file" without an argument is possible even when "curbuf->b_ro_locked"
" is set.
func Test_file_from_copen()
" Works without argument.
@@ -3148,6 +3299,21 @@ func Test_resize_from_copen()
endtry
endfunc
+func Test_filetype_autocmd()
+ " this changes the location list while it is in use to fill a buffer
+ lexpr ''
+ lopen
+ augroup FT_loclist
+ au FileType * call setloclist(0, [], 'f')
+ augroup END
+ silent! lolder
+ lexpr ''
+
+ augroup FT_loclist
+ au! FileType
+ augroup END
+endfunc
+
func Test_vimgrep_with_textlock()
new
@@ -3291,9 +3457,9 @@ func Xmultidirstack_tests(cchar)
let l1 = g:Xgetlist({'nr':1, 'items':1})
let l2 = g:Xgetlist({'nr':2, 'items':1})
- call assert_equal(expand('Xone/a/one.txt'), bufname(l1.items[1].bufnr))
+ call assert_equal('Xone/a/one.txt', bufname(l1.items[1].bufnr))
call assert_equal(3, l1.items[1].lnum)
- call assert_equal(expand('Xtwo/a/two.txt'), bufname(l2.items[1].bufnr))
+ call assert_equal('Xtwo/a/two.txt', bufname(l2.items[1].bufnr))
call assert_equal(5, l2.items[1].lnum)
endfunc
@@ -3337,14 +3503,15 @@ func Xmultifilestack_tests(cchar)
" error line ends with a file stack.
let efm_val = 'Error\ l%l\ in\ %f,'
let efm_val .= '%-P%>(%f%r,Error\ l%l\ in\ %m,%-Q)%r'
- let l = g:Xgetlist({'lines' : [
- \ '(one.txt',
- \ 'Error l4 in one.txt',
- \ ') (two.txt',
- \ 'Error l6 in two.txt',
- \ ')',
- \ 'Error l8 in one.txt'
- \ ], 'efm' : efm_val})
+ let lines =<< trim END
+ (one.txt
+ Error l4 in one.txt
+ ) (two.txt
+ Error l6 in two.txt
+ )
+ Error l8 in one.txt
+ END
+ let l = g:Xgetlist({'lines': lines, 'efm' : efm_val})
call assert_equal(3, len(l.items))
call assert_equal('one.txt', bufname(l.items[0].bufnr))
call assert_equal(4, l.items[0].lnum)
@@ -3622,7 +3789,15 @@ func Xqfjump_tests(cchar)
call g:Xsetlist([], 'f')
setlocal buftype=nofile
new
- call g:Xsetlist([], ' ', {'lines' : ['F1:1:1:Line1', 'F1:2:2:Line2', 'F2:1:1:Line1', 'F2:2:2:Line2', 'F3:1:1:Line1', 'F3:2:2:Line2']})
+ let lines =<< trim END
+ F1:1:1:Line1
+ F1:2:2:Line2
+ F2:1:1:Line1
+ F2:2:2:Line2
+ F3:1:1:Line1
+ F3:2:2:Line2
+ END
+ call g:Xsetlist([], ' ', {'lines': lines})
Xopen
let winid = win_getid()
wincmd p
@@ -3758,6 +3933,22 @@ func Xgetlist_empty_tests(cchar)
endif
endfunc
+func Test_empty_list_quickfixtextfunc()
+ " This was crashing. Can only reproduce by running it in a separate Vim
+ " instance.
+ let lines =<< trim END
+ func s:Func(o)
+ cgetexpr '0'
+ endfunc
+ cope
+ let &quickfixtextfunc = 's:Func'
+ cgetfile [ex
+ END
+ call writefile(lines, 'Xquickfixtextfunc')
+ call RunVim([], [], '-e -s -S Xquickfixtextfunc -c qa')
+ call delete('Xquickfixtextfunc')
+endfunc
+
func Test_getqflist()
call Xgetlist_empty_tests('c')
call Xgetlist_empty_tests('l')
@@ -3943,8 +4134,8 @@ endfunc
func Test_lvimgrep_crash2()
au BufNewFile x sfind
- call assert_fails('lvimgrep x x', 'E480:')
- call assert_fails('lvimgrep x x x', 'E480:')
+ call assert_fails('lvimgrep x x', 'E471:')
+ call assert_fails('lvimgrep x x x', 'E471:')
au! BufNewFile
endfunc
@@ -4055,14 +4246,19 @@ endfunc
" The following test used to crash Vim
func Test_lhelpgrep_autocmd()
lhelpgrep quickfix
- autocmd QuickFixCmdPost * call setloclist(0, [], 'f')
+ augroup QF_Test
+ au!
+ autocmd QuickFixCmdPost * call setloclist(0, [], 'f')
+ augroup END
lhelpgrep buffer
call assert_equal('help', &filetype)
call assert_equal(0, getloclist(0, {'nr' : '$'}).nr)
lhelpgrep tabpage
call assert_equal('help', &filetype)
call assert_equal(1, getloclist(0, {'nr' : '$'}).nr)
- au! QuickFixCmdPost
+ augroup QF_Test
+ au!
+ augroup END
new | only
augroup QF_Test
@@ -4075,7 +4271,7 @@ func Test_lhelpgrep_autocmd()
wincmd w
call assert_fails('helpgrep quickfix', 'E925:')
augroup QF_Test
- au! BufEnter
+ au!
augroup END
new | only
@@ -4085,7 +4281,7 @@ func Test_lhelpgrep_autocmd()
augroup END
call assert_fails('helpgrep quickfix', 'E925:')
augroup QF_Test
- au! BufEnter
+ au!
augroup END
new | only
@@ -4095,10 +4291,43 @@ func Test_lhelpgrep_autocmd()
augroup END
call assert_fails('lhelpgrep quickfix', 'E926:')
augroup QF_Test
- au! BufEnter
+ au!
augroup END
+ " Replace the contents of a help window location list when it is still in
+ " use.
new | only
+ lhelpgrep quickfix
+ wincmd w
+ augroup QF_Test
+ au!
+ autocmd WinEnter * call setloclist(0, [], 'r')
+ augroup END
+ call assert_fails('lhelpgrep win_getid', 'E926:')
+ augroup QF_Test
+ au!
+ augroup END
+
+ %bw!
+endfunc
+
+" The following test used to crash Vim
+func Test_lhelpgrep_autocmd_free_loclist()
+ %bw!
+ lhelpgrep quickfix
+ wincmd w
+ augroup QF_Test
+ au!
+ autocmd WinEnter * call setloclist(0, [], 'f')
+ augroup END
+ lhelpgrep win_getid
+ wincmd w
+ wincmd w
+ wincmd w
+ augroup QF_Test
+ au!
+ augroup END
+ %bw!
endfunc
" Test for shortening/simplifying the file name when opening the
@@ -4816,9 +5045,20 @@ func Xtest_below(cchar)
endif
" Test for lines with multiple quickfix entries
- Xexpr ["X1:5:L5", "X2:5:1:L5_1", "X2:5:2:L5_2", "X2:5:3:L5_3",
- \ "X2:10:1:L10_1", "X2:10:2:L10_2", "X2:10:3:L10_3",
- \ "X2:15:1:L15_1", "X2:15:2:L15_2", "X2:15:3:L15_3", "X3:3:L3"]
+ let lines =<< trim END
+ X1:5:L5
+ X2:5:1:L5_1
+ X2:5:2:L5_2
+ X2:5:3:L5_3
+ X2:10:1:L10_1
+ X2:10:2:L10_2
+ X2:10:3:L10_3
+ X2:15:1:L15_1
+ X2:15:2:L15_2
+ X2:15:3:L15_3
+ X3:3:L3
+ END
+ Xexpr lines
edit +1 X2
Xbelow 2
call assert_equal(['X2', 10, 1], [@%, line('.'), col('.')])
@@ -4882,33 +5122,32 @@ func Test_cbelow()
endfunc
func Test_quickfix_count()
- let commands = [
- \ 'cNext',
- \ 'cNfile',
- \ 'cabove',
- \ 'cbelow',
- \ 'cfirst',
- \ 'clast',
- \ 'cnewer',
- \ 'cnext',
- \ 'cnfile',
- \ 'colder',
- \ 'cprevious',
- \ 'crewind',
- \
- \ 'lNext',
- \ 'lNfile',
- \ 'labove',
- \ 'lbelow',
- \ 'lfirst',
- \ 'llast',
- \ 'lnewer',
- \ 'lnext',
- \ 'lnfile',
- \ 'lolder',
- \ 'lprevious',
- \ 'lrewind',
- \ ]
+ let commands =<< trim END
+ cNext
+ cNfile
+ cabove
+ cbelow
+ cfirst
+ clast
+ cnewer
+ cnext
+ cnfile
+ colder
+ cprevious
+ crewind
+ lNext
+ lNfile
+ labove
+ lbelow
+ lfirst
+ llast
+ lnewer
+ lnext
+ lnfile
+ lolder
+ lprevious
+ lrewind
+ END
for cmd in commands
call assert_fails('-1' .. cmd, 'E16:')
call assert_fails('.' .. cmd, 'E16:')
@@ -5090,6 +5329,29 @@ func Test_lhelpgrep_from_help_window()
new | only!
endfunc
+" Test for the crash fixed by 7.3.715
+func Test_setloclist_crash()
+ %bw!
+ let g:BufNum = bufnr()
+ augroup QF_Test
+ au!
+ au BufUnload * call setloclist(0, [{'bufnr':g:BufNum, 'lnum':1, 'col':1, 'text': 'tango down'}])
+ augroup END
+
+ try
+ lvimgrep /.*/ *.mak
+ catch /E926:/
+ endtry
+ call assert_equal('tango down', getloclist(0, {'items' : 0}).items[0].text)
+ call assert_equal(1, getloclist(0, {'size' : 0}).size)
+
+ augroup QF_Test
+ au!
+ augroup END
+ unlet g:BufNum
+ %bw!
+endfunc
+
" Test for adding an invalid entry with the quickfix window open and making
" sure that the window contents are not changed
func Test_add_invalid_entry_with_qf_window()
@@ -5289,6 +5551,7 @@ func Xtest_getqflist_by_idx(cchar)
call assert_equal('L20', l[0].text)
call assert_equal([], g:Xgetlist({'idx' : -1, 'items' : 0}).items)
call assert_equal([], g:Xgetlist({'idx' : 3, 'items' : 0}).items)
+ call assert_equal({}, g:Xgetlist(#{idx: "abc"}))
%bwipe!
endfunc
@@ -5347,6 +5610,19 @@ func Xtest_qftextfunc(cchar)
call assert_equal('F1|10 col 2-7| green', getline(1))
call assert_equal('F1|20-25 col 4-8| blue', getline(2))
Xclose
+
+ set efm=%f:%l:%c:%m
+ set quickfixtextfunc=Tqfexpr
+ " Update the list with only the cwindow
+ Xwindow
+ only
+ call g:Xsetlist([
+ \ { 'filename': 'F2', 'lnum': 20, 'col': 2,
+ \ 'end_col': 7, 'text': 'red'}
+ \ ])
+ call assert_equal(['F2-L20C2-red'], getline(1, '$'))
+ new
+ Xclose
set efm&
set quickfixtextfunc&
@@ -5470,6 +5746,155 @@ func Test_qftextfunc()
call Xtest_qftextfunc('l')
endfunc
+func Test_qftextfunc_callback()
+ let lines =<< trim END
+ set efm=%f:%l:%c:%m
+
+ #" Test for using a function name
+ LET &qftf = 'g:Tqfexpr'
+ cexpr "F0:0:0:L0"
+ copen
+ call assert_equal('F0-L0C0-L0', getline(1))
+ cclose
+
+ #" Test for using a function()
+ set qftf=function('g:Tqfexpr')
+ cexpr "F1:1:1:L1"
+ copen
+ call assert_equal('F1-L1C1-L1', getline(1))
+ cclose
+
+ #" Using a funcref variable to set 'quickfixtextfunc'
+ VAR Fn = function('g:Tqfexpr')
+ LET &qftf = Fn
+ cexpr "F2:2:2:L2"
+ copen
+ call assert_equal('F2-L2C2-L2', getline(1))
+ cclose
+
+ #" Using string(funcref_variable) to set 'quickfixtextfunc'
+ LET Fn = function('g:Tqfexpr')
+ LET &qftf = string(Fn)
+ cexpr "F3:3:3:L3"
+ copen
+ call assert_equal('F3-L3C3-L3', getline(1))
+ cclose
+
+ #" Test for using a funcref()
+ set qftf=funcref('g:Tqfexpr')
+ cexpr "F4:4:4:L4"
+ copen
+ call assert_equal('F4-L4C4-L4', getline(1))
+ cclose
+
+ #" Using a funcref variable to set 'quickfixtextfunc'
+ LET Fn = funcref('g:Tqfexpr')
+ LET &qftf = Fn
+ cexpr "F5:5:5:L5"
+ copen
+ call assert_equal('F5-L5C5-L5', getline(1))
+ cclose
+
+ #" Using a string(funcref_variable) to set 'quickfixtextfunc'
+ LET Fn = funcref('g:Tqfexpr')
+ LET &qftf = string(Fn)
+ cexpr "F5:5:5:L5"
+ copen
+ call assert_equal('F5-L5C5-L5', getline(1))
+ cclose
+
+ #" Test for using a lambda function with set
+ VAR optval = "LSTART a LMIDDLE Tqfexpr(a) LEND"
+ LET optval = substitute(optval, ' ', '\\ ', 'g')
+ exe "set qftf=" .. optval
+ cexpr "F6:6:6:L6"
+ copen
+ call assert_equal('F6-L6C6-L6', getline(1))
+ cclose
+
+ #" Set 'quickfixtextfunc' to a lambda expression
+ LET &qftf = LSTART a LMIDDLE Tqfexpr(a) LEND
+ cexpr "F7:7:7:L7"
+ copen
+ call assert_equal('F7-L7C7-L7', getline(1))
+ cclose
+
+ #" Set 'quickfixtextfunc' to string(lambda_expression)
+ LET &qftf = "LSTART a LMIDDLE Tqfexpr(a) LEND"
+ cexpr "F8:8:8:L8"
+ copen
+ call assert_equal('F8-L8C8-L8', getline(1))
+ cclose
+
+ #" Set 'quickfixtextfunc' to a variable with a lambda expression
+ VAR Lambda = LSTART a LMIDDLE Tqfexpr(a) LEND
+ LET &qftf = Lambda
+ cexpr "F9:9:9:L9"
+ copen
+ call assert_equal('F9-L9C9-L9', getline(1))
+ cclose
+
+ #" Set 'quickfixtextfunc' to a string(variable with a lambda expression)
+ LET Lambda = LSTART a LMIDDLE Tqfexpr(a) LEND
+ LET &qftf = string(Lambda)
+ cexpr "F9:9:9:L9"
+ copen
+ call assert_equal('F9-L9C9-L9', getline(1))
+ cclose
+ END
+ call CheckLegacyAndVim9Success(lines)
+
+ " Test for using a script-local function name
+ func s:TqfFunc2(info)
+ let g:TqfFunc2Args = [a:info.start_idx, a:info.end_idx]
+ return ''
+ endfunc
+ let g:TqfFunc2Args = []
+ set quickfixtextfunc=s:TqfFunc2
+ cexpr "F10:10:10:L10"
+ cclose
+ call assert_equal([1, 1], g:TqfFunc2Args)
+
+ let &quickfixtextfunc = 's:TqfFunc2'
+ cexpr "F11:11:11:L11"
+ cclose
+ call assert_equal([1, 1], g:TqfFunc2Args)
+ delfunc s:TqfFunc2
+
+ " set 'quickfixtextfunc' to a partial with dict. This used to cause a crash.
+ func SetQftfFunc()
+ let params = {'qftf': function('g:DictQftfFunc')}
+ let &quickfixtextfunc = params.qftf
+ endfunc
+ func g:DictQftfFunc(_) dict
+ endfunc
+ call SetQftfFunc()
+ new
+ call SetQftfFunc()
+ bw
+ call test_garbagecollect_now()
+ new
+ set qftf=
+ wincmd w
+ set qftf=
+ :%bw!
+
+ " set per-quickfix list 'quickfixtextfunc' to a partial with dict. This used
+ " to cause a crash.
+ let &qftf = ''
+ func SetLocalQftfFunc()
+ let params = {'qftf': function('g:DictQftfFunc')}
+ call setqflist([], 'a', {'quickfixtextfunc' : params.qftf})
+ endfunc
+ call SetLocalQftfFunc()
+ call test_garbagecollect_now()
+ call setqflist([], 'a', {'quickfixtextfunc' : ''})
+ delfunc g:DictQftfFunc
+ delfunc SetQftfFunc
+ delfunc SetLocalQftfFunc
+ set efm&
+endfunc
+
" Test for updating a location list for some other window and check that
" 'qftextfunc' uses the correct location list.
func Test_qftextfunc_other_loclist()
@@ -5535,9 +5960,12 @@ func Test_locationlist_open_in_newtab()
%bwipe!
- lgetexpr ['Xqftestfile1:5:Line5',
- \ 'Xqftestfile2:10:Line10',
- \ 'Xqftestfile3:16:Line16']
+ let lines =<< trim END
+ Xqftestfile1:5:Line5
+ Xqftestfile2:10:Line10
+ Xqftestfile3:16:Line16
+ END
+ lgetexpr lines
silent! llast
call assert_equal(1, tabpagenr('$'))
@@ -5578,6 +6006,21 @@ func Test_win_gettype()
lclose
endfunc
+fun Test_vimgrep_nomatch()
+ call XexprTests('c')
+ call g:Xsetlist([{'lnum':10,'text':'Line1'}])
+ copen
+ if has("win32")
+ call assert_fails('vimgrep foo *.zzz', 'E479:')
+ let expected = [{'lnum': 10, 'bufnr': 0, 'end_lnum': 0, 'pattern': '', 'valid': 0, 'vcol': 0, 'nr': 0, 'module': '', 'type': '', 'end_col': 0, 'col': 0, 'text': 'Line1'}]
+ else
+ call assert_fails('vimgrep foo *.zzz', 'E480:')
+ let expected = []
+ endif
+ call assert_equal(expected, getqflist())
+ cclose
+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).
@@ -5646,7 +6089,7 @@ func Test_lopen_bwipe_all()
qall!
END
call writefile(lines, 'Xscript')
- if RunVim([], [], '--clean -n -S Xscript')
+ if RunVim([], [], '-u NONE -n -X -Z -e -m -s -S Xscript')
call assert_equal(['done'], readfile('Xresult'))
endif
@@ -5654,5 +6097,140 @@ func Test_lopen_bwipe_all()
call delete('Xresult')
endfunc
+" Test for calling setqflist() function recursively
+func Test_recursive_setqflist()
+ augroup QF_Test
+ au!
+ autocmd BufWinEnter quickfix call setqflist([], 'r')
+ augroup END
+
+ copen
+ call assert_fails("call setqflist([], 'a')", 'E952:')
+
+ augroup QF_Test
+ au!
+ augroup END
+ %bw!
+endfunc
+
+" Test for failure to create a new window when selecting a file from the
+" quickfix window
+func Test_cwindow_newwin_fails()
+ cgetexpr ["Xfile1:10:L10", "Xfile1:20:L20"]
+ cwindow
+ only
+ let qf_wid = win_getid()
+ " create the maximum number of scratch windows
+ let hor_win_count = (&lines - 1)/2
+ let hor_split_count = hor_win_count - 1
+ for s in range(1, hor_split_count) | new | set buftype=nofile | endfor
+ call win_gotoid(qf_wid)
+ call assert_fails('exe "normal \<CR>"', 'E36:')
+ %bw!
+endfunc
+
+" Test for updating the location list when only the location list window is
+" present and the corresponding file window is closed.
+func Test_loclist_update_with_llwin_only()
+ %bw!
+ new
+ wincmd w
+ lexpr ["Xfile1:1:Line1"]
+ lopen
+ wincmd p
+ close
+ call setloclist(2, [], 'r', {'lines': ["Xtest2:2:Line2"]})
+ call assert_equal(['Xtest2|2| Line2'], getbufline(winbufnr(2), 1, '$'))
+ %bw!
+endfunc
+
+" Test for getting the quickfix list after a buffer with an error is wiped out
+func Test_getqflist_wiped_out_buffer()
+ %bw!
+ cexpr ["Xtest1:34:Wiped out"]
+ let bnum = bufnr('Xtest1')
+ call assert_equal(bnum, getqflist()[0].bufnr)
+ bw Xtest1
+ call assert_equal(0, getqflist()[0].bufnr)
+ %bw!
+endfunc
+
+" Test for the status message that is displayed when opening a new quickfix
+" list
+func Test_qflist_statusmsg()
+ cexpr "1\n2"
+ cexpr "1\n2\n3\ntest_quickfix.vim:1:msg"
+ call assert_equal('(4 of 4): msg', v:statusmsg)
+ call setqflist([], 'f')
+ %bw!
+
+ " When creating a new quickfix list, if an autocmd changes the quickfix list
+ " in the stack, then an error message should be displayed.
+ augroup QF_Test
+ au!
+ au BufEnter test_quickfix.vim colder
+ augroup END
+ cexpr "1\n2"
+ call assert_fails('cexpr "1\n2\n3\ntest_quickfix.vim:1:msg"', 'E925:')
+ call setqflist([], 'f')
+ augroup QF_Test
+ au!
+ augroup END
+ %bw!
+
+ augroup QF_Test
+ au!
+ au BufEnter test_quickfix.vim caddexpr "4"
+ augroup END
+ call assert_fails('cexpr "1\n2\n3\ntest_quickfix.vim:1:msg"', 'E925:')
+ call setqflist([], 'f')
+ augroup QF_Test
+ au!
+ augroup END
+ %bw!
+endfunc
+
+func Test_quickfixtextfunc_recursive()
+ func s:QFTfunc(o)
+ cgete '0'
+ endfunc
+ copen
+ let &quickfixtextfunc = 's:QFTfunc'
+ cex ""
+
+ let &quickfixtextfunc = ''
+ cclose
+endfunc
+
+" Test for replacing the location list from an autocmd. This used to cause a
+" read from freed memory.
+func Test_loclist_replace_autocmd()
+ %bw!
+ call setloclist(0, [], 'f')
+ let s:bufnr = bufnr()
+ cal setloclist(0, [{'0': 0, '': ''}])
+ au BufEnter * cal setloclist(1, [{'t': ''}, {'bufnr': s:bufnr}], 'r')
+ lopen
+ try
+ exe "norm j\<CR>"
+ catch
+ endtry
+ lnext
+ %bw!
+ call setloclist(0, [], 'f')
+endfunc
+
+func s:QfTf(_)
+endfunc
+
+func Test_setqflist_cb_arg()
+ " This was changing the callback name in the dictionary.
+ let d = #{quickfixtextfunc: 's:QfTf'}
+ call setqflist([], 'a', d)
+ call assert_equal('s:QfTf', d.quickfixtextfunc)
+
+ call setqflist([], 'f')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_quotestar.vim b/src/nvim/testdir/test_quotestar.vim
index 93865869fa..6a6719da8b 100644
--- a/src/nvim/testdir/test_quotestar.vim
+++ b/src/nvim/testdir/test_quotestar.vim
@@ -98,16 +98,17 @@ func Do_test_quotestar_for_x11()
" Running in a terminal and the GUI is available: Tell the server to open
" the GUI and check that the remote command still works.
- " Need to wait for the GUI to start up, otherwise the send hangs in trying
- " to send to the terminal window.
- if has('gui_athena') || has('gui_motif')
+ if has('gui_motif')
" For those GUIs, ignore the 'failed to create input context' error.
call remote_send(name, ":call test_ignore_error('E285') | gui -f\<CR>")
else
call remote_send(name, ":gui -f\<CR>")
endif
" Wait for the server in the GUI to be up and answering requests.
+ " First need to wait for the GUI to start up, otherwise the send hangs in
+ " trying to send to the terminal window.
" On some systems and with valgrind this can be very slow.
+ sleep 1
call WaitForAssert({-> assert_match("1", remote_expr(name, "has('gui_running')", "", 1))}, 10000)
call remote_send(name, ":let @* = 'maybe'\<CR>")
diff --git a/src/nvim/testdir/test_random.vim b/src/nvim/testdir/test_random.vim
index 6d3f7dcfd9..5fdbfe9cd8 100644
--- a/src/nvim/testdir/test_random.vim
+++ b/src/nvim/testdir/test_random.vim
@@ -1,5 +1,8 @@
" Tests for srand() and rand()
+source check.vim
+source shared.vim
+
func Test_Rand()
let r = srand(123456789)
call assert_equal([1573771921, 319883699, 2742014374, 1324369493], r)
@@ -9,18 +12,9 @@ func Test_Rand()
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
+ " using /dev/urandom or used time, result is different each time
+ call assert_notequal(s, srand())
" Nvim does not support test_srand_seed
" call test_srand_seed(123456789)
@@ -33,13 +27,11 @@ func Test_Rand()
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)
+ call assert_fails('echo rand([1, 2, 3])', 'E730:')
+ call assert_fails('echo rand([[1], 2, 3, 4])', 'E730:')
+ call assert_fails('echo rand([1, [2], 3, 4])', 'E730:')
+ call assert_fails('echo rand([1, 2, [3], 4])', 'E730:')
+ call assert_fails('echo rand([1, 2, 3, [4]])', 'E730:')
endfunc
func Test_issue_5587()
@@ -48,4 +40,20 @@ func Test_issue_5587()
call rand()
endfunc
+func Test_srand()
+ CheckNotGui
+
+ let cmd = GetVimCommand() .. ' -V -es -c "echo rand()" -c qa!'
+ let bad = 0
+ for _ in range(10)
+ echo cmd
+ let result1 = system(cmd)
+ let result2 = system(cmd)
+ if result1 ==# result2
+ let bad += 1
+ endif
+ endfor
+ call assert_inrange(0, 4, bad)
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_recover.vim b/src/nvim/testdir/test_recover.vim
index fc073cacd2..92e22687af 100644
--- a/src/nvim/testdir/test_recover.vim
+++ b/src/nvim/testdir/test_recover.vim
@@ -1,5 +1,7 @@
" Test :recover
+source check.vim
+
func Test_recover_root_dir()
" This used to access invalid memory.
split Xtest
@@ -23,6 +25,21 @@ func Test_recover_root_dir()
set dir&
endfunc
+" Make a copy of the current swap file to "Xswap".
+" Return the name of the swap file.
+func CopySwapfile()
+ preserve
+ " get the name of the swap file
+ let swname = split(execute("swapname"))[0]
+ let swname = substitute(swname, '[[:blank:][:cntrl:]]*\(.\{-}\)[[:blank:][:cntrl:]]*$', '\1', '')
+ " make a copy of the swap file in Xswap
+ set binary
+ exe 'sp ' . swname
+ w! Xswap
+ set nobinary
+ return swname
+endfunc
+
" Inserts 10000 lines with text to fill the swap file with two levels of pointer
" blocks. Then recovers from the swap file and checks all text is restored.
"
@@ -40,15 +57,9 @@ func Test_swap_file()
let i += 1
endwhile
$delete
- preserve
- " get the name of the swap file
- let swname = split(execute("swapname"))[0]
- let swname = substitute(swname, '[[:blank:][:cntrl:]]*\(.\{-}\)[[:blank:][:cntrl:]]*$', '\1', '')
- " make a copy of the swap file in Xswap
- set binary
- exe 'sp ' . swname
- w! Xswap
- set nobinary
+
+ let swname = CopySwapfile()
+
new
only!
bwipe! Xtest
@@ -69,3 +80,388 @@ func Test_swap_file()
set undolevels&
enew! | only
endfunc
+
+func Test_nocatch_process_still_running()
+ let g:skipped_reason = 'test_override() is N/A'
+ return
+ " sysinfo.uptime probably only works on Linux
+ if !has('linux')
+ let g:skipped_reason = 'only works on Linux'
+ return
+ endif
+ " the GUI dialog can't be handled
+ if has('gui_running')
+ let g:skipped_reason = 'only works in the terminal'
+ return
+ endif
+
+ " don't intercept existing swap file here
+ au! SwapExists
+
+ " Edit a file and grab its swapfile.
+ edit Xswaptest
+ call setline(1, ['a', 'b', 'c'])
+ let swname = CopySwapfile()
+
+ " Forget we edited this file
+ new
+ only!
+ bwipe! Xswaptest
+
+ call rename('Xswap', swname)
+ call feedkeys('e', 'tL')
+ redir => editOutput
+ edit Xswaptest
+ redir END
+ call assert_match('E325: ATTENTION', editOutput)
+ call assert_match('file name: .*Xswaptest', editOutput)
+ call assert_match('process ID: \d* (STILL RUNNING)', editOutput)
+
+ " Forget we edited this file
+ new
+ only!
+ bwipe! Xswaptest
+
+ " pretend we rebooted
+ call test_override("uptime", 0)
+ sleep 1
+
+ call feedkeys('e', 'tL')
+ redir => editOutput
+ edit Xswaptest
+ redir END
+ call assert_match('E325: ATTENTION', editOutput)
+ call assert_notmatch('(STILL RUNNING)', editOutput)
+
+ call test_override("ALL", 0)
+ call delete(swname)
+endfunc
+
+" Test for :recover with multiple swap files
+func Test_recover_multiple_swap_files()
+ CheckUnix
+ new Xfile1
+ call setline(1, ['a', 'b', 'c'])
+ preserve
+ let b = readblob(swapname(''))
+ call writefile(b, '.Xfile1.swm')
+ call writefile(b, '.Xfile1.swn')
+ call writefile(b, '.Xfile1.swo')
+ %bw!
+ call feedkeys(":recover Xfile1\<CR>3\<CR>q", 'xt')
+ call assert_equal(['a', 'b', 'c'], getline(1, '$'))
+ " try using out-of-range number to select a swap file
+ bw!
+ call feedkeys(":recover Xfile1\<CR>4\<CR>q", 'xt')
+ call assert_equal('Xfile1', @%)
+ call assert_equal([''], getline(1, '$'))
+ bw!
+ call feedkeys(":recover Xfile1\<CR>0\<CR>q", 'xt')
+ call assert_equal('Xfile1', @%)
+ call assert_equal([''], getline(1, '$'))
+ bw!
+
+ call delete('.Xfile1.swm')
+ call delete('.Xfile1.swn')
+ call delete('.Xfile1.swo')
+endfunc
+
+" Test for :recover using an empty swap file
+func Test_recover_empty_swap_file()
+ CheckUnix
+ call writefile([], '.Xfile1.swp')
+ let msg = execute('recover Xfile1')
+ call assert_match('Unable to read block 0 from .Xfile1.swp', msg)
+ call assert_equal('Xfile1', @%)
+ bw!
+
+ " make sure there are no old swap files laying around
+ for f in glob('.sw?', 0, 1)
+ call delete(f)
+ endfor
+
+ " :recover from an empty buffer
+ call assert_fails('recover', 'E305:')
+ call delete('.Xfile1.swp')
+endfunc
+
+" Test for :recover using a corrupted swap file
+" Refer to the comments in the memline.c file for the swap file headers
+" definition.
+func Test_recover_corrupted_swap_file()
+ CheckUnix
+
+ " recover using a partial swap file
+ call writefile(0z1234, '.Xfile1.swp')
+ call assert_fails('recover Xfile1', 'E295:')
+ bw!
+
+ " recover using invalid content in the swap file
+ call writefile([repeat('1', 2*1024)], '.Xfile1.swp')
+ call assert_fails('recover Xfile1', 'E307:')
+ call delete('.Xfile1.swp')
+
+ " :recover using a swap file with a corrupted header
+ edit Xfile1
+ preserve
+ let sn = swapname('')
+ let b = readblob(sn)
+ let save_b = copy(b)
+ bw!
+
+ " Not all fields are written in a system-independent manner. Detect whether
+ " the test is running on a little or big-endian system, so the correct
+ " corruption values can be set.
+ " The B0_MAGIC_LONG field may be 32-bit or 64-bit, depending on the system,
+ " even though the value stored is only 32-bits. Therefore, need to check
+ " both the high and low 32-bits to compute these values.
+ let little_endian = (b[1008:1011] == 0z33323130) || (b[1012:1015] == 0z33323130)
+ let system_64bit = little_endian ? (b[1012:1015] == 0z00000000) : (b[1008:1011] == 0z00000000)
+
+ " clear the B0_MAGIC_LONG field
+ if system_64bit
+ let b[1008:1015] = 0z00000000.00000000
+ else
+ let b[1008:1011] = 0z00000000
+ endif
+ call writefile(b, sn)
+ let msg = execute('recover Xfile1')
+ call assert_match('the file has been damaged', msg)
+ call assert_equal('Xfile1', @%)
+ call assert_equal([''], getline(1, '$'))
+ bw!
+
+ " reduce the page size
+ let b = copy(save_b)
+ let b[12:15] = 0z00010000
+ call writefile(b, sn)
+ let msg = execute('recover Xfile1')
+ call assert_match('page size is smaller than minimum value', msg)
+ call assert_equal('Xfile1', @%)
+ call assert_equal([''], getline(1, '$'))
+ bw!
+
+ " clear the pointer ID
+ let b = copy(save_b)
+ let b[4096:4097] = 0z0000
+ call writefile(b, sn)
+ call assert_fails('recover Xfile1', 'E310:')
+ call assert_equal('Xfile1', @%)
+ call assert_equal([''], getline(1, '$'))
+ bw!
+
+ " set the number of pointers in a pointer block to zero
+ let b = copy(save_b)
+ let b[4098:4099] = 0z0000
+ call writefile(b, sn)
+ call assert_fails('recover Xfile1', 'E312:')
+ call assert_equal('Xfile1', @%)
+ call assert_equal(['???EMPTY BLOCK'], getline(1, '$'))
+ bw!
+
+ " set the block number in a pointer entry to a negative number
+ let b = copy(save_b)
+ if system_64bit
+ let b[4104:4111] = little_endian ? 0z00000000.00000080 : 0z80000000.00000000
+ else
+ let b[4104:4107] = little_endian ? 0z00000080 : 0z80000000
+ endif
+ call writefile(b, sn)
+ call assert_fails('recover Xfile1', 'E312:')
+ call assert_equal('Xfile1', @%)
+ call assert_equal(['???LINES MISSING'], getline(1, '$'))
+ bw!
+
+ " clear the data block ID
+ let b = copy(save_b)
+ let b[8192:8193] = 0z0000
+ call writefile(b, sn)
+ call assert_fails('recover Xfile1', 'E312:')
+ call assert_equal('Xfile1', @%)
+ call assert_equal(['???BLOCK MISSING'], getline(1, '$'))
+ bw!
+
+ " set the number of lines in the data block to zero
+ let b = copy(save_b)
+ if system_64bit
+ let b[8208:8215] = 0z00000000.00000000
+ else
+ let b[8208:8211] = 0z00000000
+ endif
+ call writefile(b, sn)
+ call assert_fails('recover Xfile1', 'E312:')
+ call assert_equal('Xfile1', @%)
+ call assert_equal(['??? from here until ???END lines may have been inserted/deleted',
+ \ '???END'], getline(1, '$'))
+ bw!
+
+ " use an invalid text start for the lines in a data block
+ let b = copy(save_b)
+ if system_64bit
+ let b[8216:8219] = 0z00000000
+ else
+ let b[8212:8215] = 0z00000000
+ endif
+ call writefile(b, sn)
+ call assert_fails('recover Xfile1', 'E312:')
+ call assert_equal('Xfile1', @%)
+ call assert_equal(['???'], getline(1, '$'))
+ bw!
+
+ " use an incorrect text end (db_txt_end) for the data block
+ let b = copy(save_b)
+ let b[8204:8207] = little_endian ? 0z80000000 : 0z00000080
+ call writefile(b, sn)
+ call assert_fails('recover Xfile1', 'E312:')
+ call assert_equal('Xfile1', @%)
+ call assert_equal(['??? from here until ???END lines may be messed up', '',
+ \ '???END'], getline(1, '$'))
+ bw!
+
+ " remove the data block
+ let b = copy(save_b)
+ call writefile(b[:8191], sn)
+ call assert_fails('recover Xfile1', 'E312:')
+ call assert_equal('Xfile1', @%)
+ call assert_equal(['???MANY LINES MISSING'], getline(1, '$'))
+
+ bw!
+ call delete(sn)
+endfunc
+
+" Test for :recover using an encrypted swap file
+func Test_recover_encrypted_swap_file()
+ CheckFeature cryptv
+ CheckUnix
+
+ " Recover an encrypted file from the swap file without the original file
+ new Xfile1
+ call feedkeys(":X\<CR>vim\<CR>vim\<CR>", 'xt')
+ call setline(1, ['aaa', 'bbb', 'ccc'])
+ preserve
+ let b = readblob('.Xfile1.swp')
+ call writefile(b, '.Xfile1.swm')
+ bw!
+ call feedkeys(":recover Xfile1\<CR>vim\<CR>\<CR>", 'xt')
+ call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$'))
+ bw!
+ call delete('.Xfile1.swm')
+
+ " Recover an encrypted file from the swap file with the original file
+ new Xfile1
+ call feedkeys(":X\<CR>vim\<CR>vim\<CR>", 'xt')
+ call setline(1, ['aaa', 'bbb', 'ccc'])
+ update
+ call setline(1, ['111', '222', '333'])
+ preserve
+ let b = readblob('.Xfile1.swp')
+ call writefile(b, '.Xfile1.swm')
+ bw!
+ call feedkeys(":recover Xfile1\<CR>vim\<CR>\<CR>", 'xt')
+ call assert_equal(['111', '222', '333'], getline(1, '$'))
+ call assert_true(&modified)
+ bw!
+ call delete('.Xfile1.swm')
+ call delete('Xfile1')
+endfunc
+
+" Test for :recover using a unreadable swap file
+func Test_recover_unreadble_swap_file()
+ CheckUnix
+ CheckNotRoot
+ new Xfile1
+ let b = readblob('.Xfile1.swp')
+ call writefile(b, '.Xfile1.swm')
+ bw!
+ call setfperm('.Xfile1.swm', '-w-------')
+ call assert_fails('recover Xfile1', 'E306:')
+ call delete('.Xfile1.swm')
+endfunc
+
+" Test for using :recover when the original file and the swap file have the
+" same contents.
+func Test_recover_unmodified_file()
+ CheckUnix
+ call writefile(['aaa', 'bbb', 'ccc'], 'Xfile1')
+ edit Xfile1
+ preserve
+ let b = readblob('.Xfile1.swp')
+ %bw!
+ call writefile(b, '.Xfile1.swz')
+ let msg = execute('recover Xfile1')
+ call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$'))
+ call assert_false(&modified)
+ call assert_match('Buffer contents equals file contents', msg)
+ bw!
+ call delete('Xfile1')
+ call delete('.Xfile1.swz')
+endfunc
+
+" Test for recovering a file when editing a symbolically linked file
+func Test_recover_symbolic_link()
+ CheckUnix
+ call writefile(['aaa', 'bbb', 'ccc'], 'Xfile1')
+ silent !ln -s Xfile1 Xfile2
+ edit Xfile2
+ call assert_equal('.Xfile1.swp', fnamemodify(swapname(''), ':t'))
+ preserve
+ let b = readblob('.Xfile1.swp')
+ %bw!
+ call writefile([], 'Xfile1')
+ call writefile(b, '.Xfile1.swp')
+ silent! recover Xfile2
+ call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$'))
+ call assert_true(&modified)
+ update
+ %bw!
+ call assert_equal(['aaa', 'bbb', 'ccc'], readfile('Xfile1'))
+ call delete('Xfile1')
+ call delete('Xfile2')
+ call delete('.Xfile1.swp')
+endfunc
+
+" Test for recovering a file when an autocmd moves the cursor to an invalid
+" line. This used to result in an internal error (E315) which is fixed
+" by 8.2.2966.
+func Test_recover_invalid_cursor_pos()
+ call writefile([], 'Xfile1')
+ edit Xfile1
+ preserve
+ let b = readblob('.Xfile1.swp')
+ bw!
+ augroup Test
+ au!
+ au BufReadPost Xfile1 normal! 3G
+ augroup END
+ call writefile(range(1, 3), 'Xfile1')
+ call writefile(b, '.Xfile1.swp')
+ try
+ recover Xfile1
+ catch /E308:/
+ " this test is for the :E315 internal error.
+ " ignore the 'E308: Original file may have been changed' error
+ endtry
+ redraw!
+ augroup Test
+ au!
+ augroup END
+ augroup! Test
+ call delete('Xfile1')
+ call delete('.Xfile1.swp')
+endfunc
+
+" Test for recovering a buffer without a name
+func Test_noname_buffer()
+ new
+ call setline(1, ['one', 'two'])
+ preserve
+ let sn = swapname('')
+ let b = readblob(sn)
+ bw!
+ call writefile(b, sn)
+ exe "recover " .. sn
+ call assert_equal(['one', 'two'], getline(1, '$'))
+ call delete(sn)
+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 d08a980787..ece6ae518e 100644
--- a/src/nvim/testdir/test_regexp_latin.vim
+++ b/src/nvim/testdir/test_regexp_latin.vim
@@ -101,8 +101,33 @@ func Test_multi_failure()
set re=2
call assert_fails('/a**', 'E871:')
call assert_fails('/a*\+', 'E871:')
- call assert_fails('/a\{a}', 'E870:')
+ call assert_fails('/a\{a}', 'E554:')
+ set re=0
+endfunc
+
+func Test_column_success_failure()
+ new
+ call setline(1, 'xbar')
+
+ set re=1
+ %s/\%>0v./A/
+ call assert_equal('Abar', getline(1))
+ call assert_fails('/\%v', 'E71:')
+ call assert_fails('/\%>v', 'E71:')
+ call assert_fails('/\%c', 'E71:')
+ call assert_fails('/\%<c', 'E71:')
+ call assert_fails('/\%l', 'E71:')
+ set re=2
+ %s/\%>0v./B/
+ call assert_equal('Bbar', getline(1))
+ call assert_fails('/\%v', 'E1273:')
+ call assert_fails('/\%>v', 'E1273:')
+ call assert_fails('/\%c', 'E1273:')
+ call assert_fails('/\%<c', 'E1273:')
+ call assert_fails('/\%l', 'E1273:')
+
set re=0
+ bwipe!
endfunc
func Test_recursive_addstate()
@@ -146,6 +171,10 @@ 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 escape character in a string
+ call add(tl, [2, '.\e.', "one\<Esc>two", "e\<Esc>t"])
+ " match backspace character in a string
+ call add(tl, [2, '.\b.', "one\<C-H>two", "e\<C-H>t"])
" match newline character in a string
call add(tl, [2, 'o\nb', "foo\nbar", "o\nb"])
@@ -895,6 +924,8 @@ func Test_regexp_error()
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:')
+ call assert_fails("call matchstr('abcd', '\\%o841\\%o142')", 'E678:')
+ call assert_equal('', matchstr('abcd', '\%o181\%o142'))
endfunc
" Test for using the last substitute string pattern (~)
@@ -1027,6 +1058,28 @@ func Test_using_invalid_visual_position()
bwipe!
endfunc
+func Test_using_two_engines_pattern()
+ new
+ call setline(1, ['foobar=0', 'foobar=1', 'foobar=2'])
+ " \%#= at the end of the pattern
+ for i in range(0, 2)
+ for j in range(0, 2)
+ exe "set re=" .. i
+ call cursor(j + 1, 7)
+ call assert_fails("%s/foobar\\%#=" .. j, 'E1281:')
+ endfor
+ endfor
+ set re=0
+
+ " \%#= at the start of the pattern
+ for i in range(0, 2)
+ call cursor(i + 1, 7)
+ exe ":%s/\\%#=" .. i .. "foobar=" .. i .. "/xx"
+ endfor
+ call assert_equal(['xx', 'xx', 'xx'], getline(1, '$'))
+ bwipe!
+endfunc
+
func Test_recursive_substitute_expr()
new
func Repl()
diff --git a/src/nvim/testdir/test_regexp_utf8.vim b/src/nvim/testdir/test_regexp_utf8.vim
index 14b9724d67..2253242a7c 100644
--- a/src/nvim/testdir/test_regexp_utf8.vim
+++ b/src/nvim/testdir/test_regexp_utf8.vim
@@ -258,8 +258,7 @@ func Test_multibyte_chars()
" When there is no match use only the first two items.
let tl = []
- " Multi-byte character tests. These will fail unless vim is compiled
- " with Multibyte (FEAT_MBYTE) or BIG/HUGE features.
+ " Multi-byte character tests.
call add(tl, [2, '[[:alpha:][=a=]]\+', '879 aiaãâaiuvna ', 'aiaãâaiuvna'])
call add(tl, [2, '[[=a=]]\+', 'ddaãâbcd', 'aãâ']) " equivalence classes
call add(tl, [2, '[^ม ]\+', 'มม oijasoifjos ifjoisj f osij j มมมมม abcd', 'oijasoifjos'])
diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim
index 11dd3badb6..bbf1aa53b5 100644
--- a/src/nvim/testdir/test_registers.vim
+++ b/src/nvim/testdir/test_registers.vim
@@ -263,8 +263,16 @@ func Test_get_register()
call assert_equal('', getreg("\<C-F>"))
call assert_equal('', getreg("\<C-W>"))
call assert_equal('', getreg("\<C-L>"))
+ " Change the last used register to '"' for the next test
+ normal! ""yy
+ let @" = 'happy'
+ call assert_equal('happy', getreg())
+ call assert_equal('happy', getreg(''))
call assert_equal('', getregtype('!'))
+ call assert_fails('echo getregtype([])', 'E730:')
+ call assert_equal('v', getregtype())
+ call assert_equal('v', getregtype(''))
" Test for inserting an invalid register content
call assert_beeps('exe "normal i\<C-R>!"')
@@ -277,7 +285,9 @@ func Test_get_register()
" 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
+ " Nvim: no trailing CR because of #6137
+ " call assert_equal("a\rb\r", histget(':', -1))
+ call assert_equal("a\rb", histget(':', -1))
call assert_fails('let r = getreg("=", [])', 'E745:')
call assert_fails('let r = getreg("=", 1, [])', 'E745:')
@@ -289,6 +299,7 @@ endfunc
func Test_set_register()
call assert_fails("call setreg('#', 200)", 'E86:')
+ " call assert_fails("call setreg('a', test_unknown())", 'E908:')
edit Xfile_alt_1
let b1 = bufnr('')
@@ -349,6 +360,12 @@ func Test_set_register()
normal 0".gP
call assert_equal('abcabcabc', getline(1))
+ let @"=''
+ call setreg('', '1')
+ call assert_equal('1', @")
+ call setreg('@', '2')
+ call assert_equal('2', @")
+
enew!
endfunc
@@ -474,6 +491,21 @@ func Test_get_reginfo()
let info = getreginfo('"')
call assert_equal('z', info.points_to)
+ let @a="a1b2"
+ nnoremap <F2> <Cmd>let g:RegInfo = getreginfo()<CR>
+ exe "normal \"a\<F2>"
+ call assert_equal({'regcontents': ['a1b2'], 'isunnamed': v:false,
+ \ 'regtype': 'v'}, g:RegInfo)
+ nunmap <F2>
+ unlet g:RegInfo
+
+ " The type of "isunnamed" was VAR_SPECIAL but should be VAR_BOOL. Can only
+ " be noticed when using json_encod().
+ call setreg('a', 'foo')
+ let reginfo = getreginfo('a')
+ let expected = #{regcontents: ['foo'], isunnamed: v:false, regtype: 'v'}
+ call assert_equal(json_encode(expected), json_encode(reginfo))
+
bwipe!
endfunc
diff --git a/src/nvim/testdir/test_reltime.vim b/src/nvim/testdir/test_reltime.vim
index b381f1ddbb..f4ce5de118 100644
--- a/src/nvim/testdir/test_reltime.vim
+++ b/src/nvim/testdir/test_reltime.vim
@@ -24,4 +24,8 @@ func Test_reltime()
call assert_true(reltimefloat(differs) < 0.1)
call assert_true(reltimefloat(differs) > 0.0)
+ call assert_equal(0, reltime({}))
+ call assert_equal(0, reltime({}, {}))
endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_retab.vim b/src/nvim/testdir/test_retab.vim
index 1650a03876..a4f95053c0 100644
--- a/src/nvim/testdir/test_retab.vim
+++ b/src/nvim/testdir/test_retab.vim
@@ -1,4 +1,7 @@
" Test :retab
+
+source check.vim
+
func SetUp()
new
call setline(1, "\ta \t b c ")
@@ -81,19 +84,32 @@ func Test_retab_error()
call assert_fails('ret 80000000000000000000', 'E475:')
endfunc
+func RetabLoop()
+ while 1
+ set ts=4000
+ retab 4
+ endwhile
+endfunc
+
func Test_retab_endless()
- new
+ " inside try/catch we can catch the error message
call setline(1, "\t0\t")
let caught = 'no'
try
- while 1
- set ts=4000
- retab 4
- endwhile
- catch /E1240/
- let caught = 'yes'
+ call RetabLoop()
+ catch /E1240:/
+ let caught = v:exception
endtry
- bwipe!
+ call assert_match('E1240:', caught)
+
+ set tabstop&
+endfunc
+
+func Test_nocatch_retab_endless()
+ " when not inside try/catch an interrupt is generated to get out of loops
+ call setline(1, "\t0\t")
+ call assert_fails('call RetabLoop()', ['E1240:', 'Interrupted'])
+
set tabstop&
endfunc
diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim
index 3d1bbfb726..885043accf 100644
--- a/src/nvim/testdir/test_search.vim
+++ b/src/nvim/testdir/test_search.vim
@@ -294,6 +294,9 @@ func Test_searchpair()
new
call setline(1, ['other code', 'here [', ' [', ' " cursor here', ' ]]'])
+ " should not give an error for using "42"
+ call assert_equal(0, searchpair('a', 'b', 'c', '', 42))
+
4
call assert_equal(3, searchpair('\[', '', ']', 'bW'))
call assert_equal([0, 3, 2, 0], getpos('.'))
@@ -897,9 +900,7 @@ func Test_incsearch_cmdline_modifier()
endfunc
func Test_incsearch_scrolling()
- if !CanRunVimInTerminal()
- throw 'Skipped: cannot make screendumps'
- endif
+ CheckRunVimInTerminal
call assert_equal(0, &scrolloff)
call writefile([
\ 'let dots = repeat(".", 120)',
@@ -956,24 +957,24 @@ func Test_incsearch_search_dump()
call delete('Xis_search_script')
endfunc
-func Test_hlsearch_block_visual_match()
+func Test_hlsearch_dump()
+ CheckOption hlsearch
CheckScreendump
- let lines =<< trim END
- set hlsearch
- call setline(1, ['aa', 'bbbb', 'cccccc'])
- END
- call writefile(lines, 'Xhlsearch_block')
- let buf = RunVimInTerminal('-S Xhlsearch_block', {'rows': 9, 'cols': 60})
+ call writefile([
+ \ 'set hlsearch cursorline',
+ \ 'call setline(1, ["xxx", "xxx", "xxx"])',
+ \ '/.*',
+ \ '2',
+ \ ], 'Xhlsearch_script')
+ let buf = RunVimInTerminal('-S Xhlsearch_script', {'rows': 6, 'cols': 50})
+ call VerifyScreenDump(buf, 'Test_hlsearch_1', {})
- call term_sendkeys(buf, "G\<C-V>$kk\<Esc>")
- sleep 100m
- call term_sendkeys(buf, "/\\%V\<CR>")
- sleep 100m
- call VerifyScreenDump(buf, 'Test_hlsearch_block_visual_match', {})
+ call term_sendkeys(buf, "/\\_.*\<CR>")
+ call VerifyScreenDump(buf, 'Test_hlsearch_2', {})
call StopVimInTerminal(buf)
- call delete('Xhlsearch_block')
+ call delete('Xhlsearch_script')
endfunc
func Test_hlsearch_and_visual()
@@ -996,6 +997,26 @@ func Test_hlsearch_and_visual()
call delete('Xhlvisual_script')
endfunc
+func Test_hlsearch_block_visual_match()
+ CheckScreendump
+
+ let lines =<< trim END
+ set hlsearch
+ call setline(1, ['aa', 'bbbb', 'cccccc'])
+ END
+ call writefile(lines, 'Xhlsearch_block')
+ let buf = RunVimInTerminal('-S Xhlsearch_block', {'rows': 9, 'cols': 60})
+
+ call term_sendkeys(buf, "G\<C-V>$kk\<Esc>")
+ sleep 100m
+ call term_sendkeys(buf, "/\\%V\<CR>")
+ sleep 100m
+ call VerifyScreenDump(buf, 'Test_hlsearch_block_visual_match', {})
+
+ call StopVimInTerminal(buf)
+ call delete('Xhlsearch_block')
+endfunc
+
func Test_incsearch_substitute()
CheckFunction test_override
CheckOption incsearch
@@ -1017,6 +1038,21 @@ func Test_incsearch_substitute()
call Incsearch_cleanup()
endfunc
+func Test_incsearch_substitute_long_line()
+ CheckFunction test_override
+ new
+ call test_override("char_avail", 1)
+ set incsearch
+
+ call repeat('x', 100000)->setline(1)
+ call feedkeys(':s/\%c', 'xt')
+ redraw
+ call feedkeys("\<Esc>", 'xt')
+
+ call Incsearch_cleanup()
+ bwipe!
+endfunc
+
func Test_hlsearch_cursearch()
CheckScreendump
@@ -1341,18 +1377,19 @@ func Test_subst_word_under_cursor()
set noincsearch
endfunc
-func Test_incsearch_substitute_long_line()
- CheckFunction test_override
- new
- call test_override("char_avail", 1)
- set incsearch
-
- call repeat('x', 100000)->setline(1)
- call feedkeys(':s/\%c', 'xt')
- redraw
- call feedkeys("\<Esc>", 'xt')
+func Test_search_skip_all_matches()
+ enew
+ call setline(1, ['no match here',
+ \ 'match this line',
+ \ 'nope',
+ \ 'match in this line',
+ \ 'last line',
+ \ ])
+ call cursor(1, 1)
+ let lnum = search('this', '', 0, 0, 'getline(".") =~ "this line"')
+ " Only check that no match is found. Previously it searched forever.
+ call assert_equal(0, lnum)
- call Incsearch_cleanup()
bwipe!
endfunc
@@ -1629,8 +1666,8 @@ func Test_search_with_no_last_pat()
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 assert_fails('g//p', ['E35:', 'E476:'])
+ call assert_fails('v//p', ['E35:', 'E476:'])
call writefile(v:errors, 'Xresult')
qall!
[SCRIPT]
@@ -1651,8 +1688,8 @@ func Test_search_tilde_pat()
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:')
+ call assert_fails('exe "normal /~\<CR>"', ['E33:', 'E383:'])
+ call assert_fails('exe "normal ?~\<CR>"', ['E33:', 'E383:'])
set regexpengine&
call writefile(v:errors, 'Xresult')
qall!
@@ -1732,6 +1769,25 @@ func Test_invalid_regexp()
call assert_fails("call search('\\%#=3ab')", 'E864:')
endfunc
+" Test for searching a very complex pattern in a string. Should switch the
+" regexp engine from NFA to the old engine.
+func Test_regexp_switch_engine()
+ let l = readfile('samples/re.freeze.txt')
+ let v = substitute(l[4], '..\@<!', '', '')
+ call assert_equal(l[4], v)
+endfunc
+
+" Test for the \%V atom to search within visually selected text
+func Test_search_in_visual_area()
+ new
+ call setline(1, ['foo bar1', 'foo bar2', 'foo bar3', 'foo bar4'])
+ exe "normal 2GVjo/\\%Vbar\<CR>\<Esc>"
+ call assert_equal([2, 5], [line('.'), col('.')])
+ exe "normal 2GVj$?\\%Vbar\<CR>\<Esc>"
+ call assert_equal([3, 5], [line('.'), col('.')])
+ close!
+endfunc
+
" Test for searching with 'smartcase' and 'ignorecase'
func Test_search_smartcase()
new
@@ -1776,7 +1832,7 @@ func Test_search_smartcase_utf8()
set ignorecase& smartcase&
let &encoding = save_enc
- close!
+ bwipe!
endfunc
" Test searching past the end of a file
@@ -1785,7 +1841,29 @@ func Test_search_past_eof()
call setline(1, ['Line'])
exe "normal /\\n\\zs\<CR>"
call assert_equal([1, 4], [line('.'), col('.')])
- close!
+ bwipe!
+endfunc
+
+" Test setting the start of the match and still finding a next match in the
+" same line.
+func Test_search_set_start_same_line()
+ new
+ set cpo-=c
+
+ call setline(1, ['1', '2', '3 .', '4', '5'])
+ exe "normal /\\_s\\zs\\S\<CR>"
+ call assert_equal([2, 1], [line('.'), col('.')])
+ exe 'normal n'
+ call assert_equal([3, 1], [line('.'), col('.')])
+ exe 'normal n'
+ call assert_equal([3, 3], [line('.'), col('.')])
+ exe 'normal n'
+ call assert_equal([4, 1], [line('.'), col('.')])
+ exe 'normal n'
+ call assert_equal([5, 1], [line('.'), col('.')])
+
+ set cpo+=c
+ bwipe!
endfunc
" Test for various search offsets
@@ -1907,6 +1985,100 @@ func Test_incsearch_highlighting_newline()
bw
endfunc
+func Test_incsearch_substitute_dump2()
+ CheckOption incsearch
+ CheckScreendump
+
+ call writefile([
+ \ 'set incsearch hlsearch scrolloff=0',
+ \ 'for n in range(1, 4)',
+ \ ' call setline(n, "foo " . n)',
+ \ 'endfor',
+ \ 'call setline(5, "abc|def")',
+ \ '3',
+ \ ], 'Xis_subst_script2')
+ let buf = RunVimInTerminal('-S Xis_subst_script2', {'rows': 9, 'cols': 70})
+
+ call term_sendkeys(buf, ':%s/\vabc|')
+ sleep 100m
+ call VerifyScreenDump(buf, 'Test_incsearch_sub_01', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ " The following should not be highlighted
+ call term_sendkeys(buf, ':1,5s/\v|')
+ sleep 100m
+ call VerifyScreenDump(buf, 'Test_incsearch_sub_02', {})
+
+
+ call StopVimInTerminal(buf)
+ call delete('Xis_subst_script2')
+endfunc
+
+func Test_pattern_is_uppercase_smartcase()
+ new
+ let input=['abc', 'ABC', 'Abc', 'abC']
+ call setline(1, input)
+ call cursor(1,1)
+ " default, matches firstline
+ %s/abc//g
+ call assert_equal(['', 'ABC', 'Abc', 'abC'],
+ \ getline(1, '$'))
+
+ set smartcase ignorecase
+ sil %d
+ call setline(1, input)
+ call cursor(1,1)
+ " with smartcase and incsearch set, matches everything
+ %s/abc//g
+ call assert_equal(['', '', '', ''], getline(1, '$'))
+
+ sil %d
+ call setline(1, input)
+ call cursor(1,1)
+ " with smartcase and incsearch set and found an uppercase letter,
+ " match only that.
+ %s/abC//g
+ call assert_equal(['abc', 'ABC', 'Abc', ''],
+ \ getline(1, '$'))
+
+ sil %d
+ call setline(1, input)
+ call cursor(1,1)
+ exe "norm! vG$\<esc>"
+ " \%V should not be detected as uppercase letter
+ %s/\%Vabc//g
+ call assert_equal(['', '', '', ''], getline(1, '$'))
+
+ call setline(1, input)
+ call cursor(1,1)
+ exe "norm! vG$\<esc>"
+ " \v%V should not be detected as uppercase letter
+ %s/\v%Vabc//g
+ call assert_equal(['', '', '', ''], getline(1, '$'))
+
+ call setline(1, input)
+ call cursor(1,1)
+ exe "norm! vG$\<esc>"
+ " \v%VabC should be detected as uppercase letter
+ %s/\v%VabC//g
+ call assert_equal(['abc', 'ABC', 'Abc', ''],
+ \ getline(1, '$'))
+
+ call setline(1, input)
+ call cursor(1,1)
+ " \Vabc should match everything
+ %s/\Vabc//g
+ call assert_equal(['', '', '', ''], getline(1, '$'))
+
+ call setline(1, input + ['_abc'])
+ " _ matches normally
+ %s/\v_.*//g
+ call assert_equal(['abc', 'ABC', 'Abc', 'abC', ''], getline(1, '$'))
+
+ set smartcase& ignorecase&
+ bw!
+endfunc
+
func Test_no_last_search_pattern()
CheckOption incsearch
diff --git a/src/nvim/testdir/test_search_stat.vim b/src/nvim/testdir/test_search_stat.vim
index 89e09cf85b..77bd50ada2 100644
--- a/src/nvim/testdir/test_search_stat.vim
+++ b/src/nvim/testdir/test_search_stat.vim
@@ -260,6 +260,14 @@ endfunc
func Test_searchcount_fails()
call assert_fails('echo searchcount("boo!")', 'E715:')
+ call assert_fails('echo searchcount({"timeout" : []})', 'E745:')
+ call assert_fails('echo searchcount({"maxcount" : []})', 'E745:')
+ call assert_fails('echo searchcount({"pattern" : []})', 'E730:')
+ call assert_fails('echo searchcount({"pos" : 1})', 'E475:')
+ call assert_fails('echo searchcount({"pos" : [1]})', 'E475:')
+ call assert_fails('echo searchcount({"pos" : [[], 2, 3]})', 'E745:')
+ call assert_fails('echo searchcount({"pos" : [1, [], 3]})', 'E745:')
+ call assert_fails('echo searchcount({"pos" : [1, 2, []]})', 'E745:')
endfunc
func Test_searchcount_in_statusline()
diff --git a/src/nvim/testdir/test_selectmode.vim b/src/nvim/testdir/test_selectmode.vim
index f2cab45450..041f0592f1 100644
--- a/src/nvim/testdir/test_selectmode.vim
+++ b/src/nvim/testdir/test_selectmode.vim
@@ -34,6 +34,9 @@ func Test_selectmode_start()
set selectmode=cmd
call feedkeys('gvabc', 'xt')
call assert_equal('abctdef', getline(1))
+ " arrow keys without shift should not start selection
+ call feedkeys("A\<Home>\<Right>\<Left>ro", 'xt')
+ call assert_equal('roabctdef', getline(1))
set selectmode= keymodel=
bw!
endfunc
diff --git a/src/nvim/testdir/test_set.vim b/src/nvim/testdir/test_set.vim
index 2b1e9eeee0..7215772a00 100644
--- a/src/nvim/testdir/test_set.vim
+++ b/src/nvim/testdir/test_set.vim
@@ -26,4 +26,23 @@ function Test_set_add()
let &wig = wig_save
endfunction
+
+" :set, :setlocal, :setglobal without arguments show values of options.
+func Test_set_no_arg()
+ set textwidth=79
+ let a = execute('set')
+ call assert_match("^\n--- Options ---\n.*textwidth=79\\>", a)
+ set textwidth&
+
+ setlocal textwidth=78
+ let a = execute('setlocal')
+ call assert_match("^\n--- Local option values ---\n.*textwidth=78\\>", a)
+ setlocal textwidth&
+
+ setglobal textwidth=77
+ let a = execute('setglobal')
+ call assert_match("^\n--- Global option values ---\n.*textwidth=77\\>", a)
+ setglobal textwidth&
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_shell.vim b/src/nvim/testdir/test_shell.vim
new file mode 100644
index 0000000000..8b9c7a5b12
--- /dev/null
+++ b/src/nvim/testdir/test_shell.vim
@@ -0,0 +1,209 @@
+" Test for the shell related options ('shell', 'shellcmdflag', 'shellpipe',
+" 'shellquote', 'shellredir', 'shellxescape', and 'shellxquote')
+
+source check.vim
+source shared.vim
+
+func Test_shell_options()
+ " The expected value of 'shellcmdflag', 'shellpipe', 'shellquote',
+ " 'shellredir', 'shellxescape', 'shellxquote' for the supported shells.
+ let shells = []
+ if has('unix')
+ let shells += [['sh', '-c', '2>&1| tee', '', '>%s 2>&1', '', ''],
+ \ ['ksh', '-c', '2>&1| tee', '', '>%s 2>&1', '', ''],
+ \ ['mksh', '-c', '2>&1| tee', '', '>%s 2>&1', '', ''],
+ \ ['zsh', '-c', '2>&1| tee', '', '>%s 2>&1', '', ''],
+ \ ['zsh-beta', '-c', '2>&1| tee', '', '>%s 2>&1', '', ''],
+ \ ['bash', '-c', '2>&1| tee', '', '>%s 2>&1', '', ''],
+ \ ['fish', '-c', '2>&1| tee', '', '>%s 2>&1', '', ''],
+ \ ['ash', '-c', '2>&1| tee', '', '>%s 2>&1', '', ''],
+ \ ['dash', '-c', '2>&1| tee', '', '>%s 2>&1', '', ''],
+ \ ['csh', '-c', '|& tee', '', '>&', '', ''],
+ \ ['tcsh', '-c', '|& tee', '', '>&', '', '']]
+ endif
+ if has('win32')
+ let shells += [['cmd', '/s /c', '>%s 2>&1', '', '>%s 2>&1', '', '"']]
+ endif
+
+ " start a new Vim instance with 'shell' set to each of the supported shells
+ " and check the default shell option settings
+ let after =<< trim END
+ let l = [&shell, &shellcmdflag, &shellpipe, &shellquote]
+ let l += [&shellredir, &shellxescape, &shellxquote]
+ call writefile([json_encode(l)], 'Xtestout')
+ qall!
+ END
+ for e in shells
+ if RunVim([], after, '--cmd "set shell=' .. e[0] .. '"')
+ call assert_equal(e, json_decode(readfile('Xtestout')[0]))
+ endif
+ endfor
+
+ " Test shellescape() for each of the shells.
+ for e in shells
+ exe 'set shell=' .. e[0]
+ if e[0] =~# '.*csh$' || e[0] =~# '.*csh.exe$'
+ let str1 = "'cmd \"arg1\" '\\''arg2'\\'' \\!%#'"
+ let str2 = "'cmd \"arg1\" '\\''arg2'\\'' \\\\!\\%\\#'"
+ elseif e[0] =~# '.*powershell$' || e[0] =~# '.*powershell.exe$'
+ let str1 = "'cmd \"arg1\" ''arg2'' !%#'"
+ let str2 = "'cmd \"arg1\" ''arg2'' \\!\\%\\#'"
+ else
+ let str1 = "'cmd \"arg1\" '\\''arg2'\\'' !%#'"
+ let str2 = "'cmd \"arg1\" '\\''arg2'\\'' \\!\\%\\#'"
+ endif
+ call assert_equal(str1, shellescape("cmd \"arg1\" 'arg2' !%#"), e[0])
+ call assert_equal(str2, shellescape("cmd \"arg1\" 'arg2' !%#", 1), e[0])
+
+ " Try running an external command with the shell.
+ if executable(e[0])
+ " set the shell options for the current 'shell'
+ let [&shellcmdflag, &shellpipe, &shellquote, &shellredir,
+ \ &shellxescape, &shellxquote] = e[1:6]
+ new
+ r !echo hello
+ call assert_equal('hello', substitute(getline(2), '\W', '', 'g'), e[0])
+ bwipe!
+ endif
+ endfor
+ set shell& shellcmdflag& shellpipe& shellquote&
+ set shellredir& shellxescape& shellxquote&
+ call delete('Xtestout')
+endfunc
+
+" Test for the 'shell' option
+func Test_shell()
+ throw 'Skipped: Nvim missing :shell currently'
+ CheckUnix
+ let save_shell = &shell
+ set shell=
+ let caught_e91 = 0
+ try
+ shell
+ catch /E91:/
+ let caught_e91 = 1
+ endtry
+ call assert_equal(1, caught_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 'shellescape' option
+func Test_shellescape()
+ let save_shell = &shell
+ set shell=bash
+ call assert_equal("'text'", shellescape('text'))
+ call assert_equal("'te\"xt'", 'te"xt'->shellescape())
+ call assert_equal("'te'\\''xt'", shellescape("te'xt"))
+
+ call assert_equal("'te%xt'", shellescape("te%xt"))
+ call assert_equal("'te\\%xt'", shellescape("te%xt", 1))
+ call assert_equal("'te#xt'", shellescape("te#xt"))
+ call assert_equal("'te\\#xt'", shellescape("te#xt", 1))
+ call assert_equal("'te!xt'", shellescape("te!xt"))
+ call assert_equal("'te\\!xt'", shellescape("te!xt", 1))
+
+ call assert_equal("'te\nxt'", shellescape("te\nxt"))
+ call assert_equal("'te\\\nxt'", shellescape("te\nxt", 1))
+ set shell=tcsh
+ call assert_equal("'te\\!xt'", shellescape("te!xt"))
+ call assert_equal("'te\\\\!xt'", shellescape("te!xt", 1))
+ call assert_equal("'te\\\nxt'", shellescape("te\nxt"))
+ call assert_equal("'te\\\\\nxt'", shellescape("te\nxt", 1))
+
+ let &shell = save_shell
+endfunc
+
+" Test for 'shellslash'
+func Test_shellslash()
+ CheckOption shellslash
+ let save_shellslash = &shellslash
+ " The shell and cmdflag, and expected slash in tempname with shellslash set or
+ " unset. The assert checks the file separator before the leafname.
+ " ".*\\\\[^\\\\]*$"
+ let shells = [['cmd', '/c', '/', '/'],
+ \ ['powershell', '-Command', '/', '/'],
+ \ ['sh', '-c', '/', '/']]
+ for e in shells
+ exe 'set shell=' .. e[0] .. ' | set shellcmdflag=' .. e[1]
+ set noshellslash
+ let file = tempname()
+ call assert_match('^.\+' .. e[2] .. '[^' .. e[2] .. ']\+$', file, e[0] .. ' ' .. e[1] .. ' nossl')
+ set shellslash
+ let file = tempname()
+ call assert_match('^.\+' .. e[3] .. '[^' .. e[3] .. ']\+$', file, e[0] .. ' ' .. e[1] .. ' ssl')
+ endfor
+ let &shellslash = save_shellslash
+endfunc
+
+" Test for 'shellxquote'
+func Test_shellxquote()
+ CheckUnix
+
+ let save_shell = &shell
+ let save_sxq = &shellxquote
+ let save_sxe = &shellxescape
+
+ call writefile(['#!/bin/sh', 'echo "Cmd: [$*]" > Xlog'], 'Xtestshell')
+ call setfperm('Xtestshell', "r-x------")
+ set shell=./Xtestshell
+
+ set shellxquote=\\"
+ call feedkeys(":!pwd\<CR>\<CR>", 'xt')
+ call assert_equal(['Cmd: [-c "pwd"]'], readfile('Xlog'))
+
+ set shellxquote=(
+ call feedkeys(":!pwd\<CR>\<CR>", 'xt')
+ call assert_equal(['Cmd: [-c (pwd)]'], readfile('Xlog'))
+
+ set shellxquote=\\"(
+ call feedkeys(":!pwd\<CR>\<CR>", 'xt')
+ call assert_equal(['Cmd: [-c "(pwd)"]'], readfile('Xlog'))
+
+ set shellxescape=\"&<<()@^
+ set shellxquote=(
+ call feedkeys(":!pwd\"&<<{}@^\<CR>\<CR>", 'xt')
+ call assert_equal(['Cmd: [-c (pwd^"^&^<^<{}^@^^)]'], readfile('Xlog'))
+
+ let &shell = save_shell
+ let &shellxquote = save_sxq
+ let &shellxescape = save_sxe
+ call delete('Xtestshell')
+ call delete('Xlog')
+endfunc
+
+" Test for using the shell set in the $SHELL environment variable
+func Test_set_shell()
+ let after =<< trim [CODE]
+ call writefile([&shell], "Xtestout")
+ quit!
+ [CODE]
+
+ if has('win32')
+ let $SHELL = 'C:\with space\cmd.exe'
+ let expected = '"C:\with space\cmd.exe"'
+ else
+ let $SHELL = '/bin/with space/sh'
+ let expected = '"/bin/with space/sh"'
+ endif
+
+ if RunVimPiped([], after, '', '')
+ let lines = readfile('Xtestout')
+ call assert_equal(expected, lines[0])
+ endif
+ call delete('Xtestout')
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_signals.vim b/src/nvim/testdir/test_signals.vim
index 338c0d79ff..c291c68e0d 100644
--- a/src/nvim/testdir/test_signals.vim
+++ b/src/nvim/testdir/test_signals.vim
@@ -16,8 +16,9 @@ endfunc
" Test signal WINCH (window resize signal)
func Test_signal_WINCH()
throw 'skipped: Nvim cannot avoid terminal resize'
- if has('gui_running') || !HasSignal('WINCH')
- return
+ CheckNotGui
+ if !HasSignal('WINCH')
+ throw 'Skipped: WINCH signal not supported'
endif
" We do not actually want to change the size of the terminal.
@@ -52,7 +53,7 @@ endfunc
" Test signal PWR, which should update the swap file.
func Test_signal_PWR()
if !HasSignal('PWR')
- return
+ throw 'Skipped: PWR signal not supported'
endif
" Set a very large 'updatetime' and 'updatecount', so that we can be sure
@@ -78,6 +79,33 @@ func Test_signal_PWR()
set updatetime& updatecount&
endfunc
+" Test signal INT. Handler sets got_int. It should be like typing CTRL-C.
+func Test_signal_INT()
+ CheckRunVimInTerminal
+ if !HasSignal('INT')
+ throw 'Skipped: INT signal not supported'
+ endif
+
+ " Skip the rest of the test when running with valgrind as signal INT is not
+ " received somehow by Vim when running with valgrind.
+ let cmd = GetVimCommand()
+ if cmd =~ 'valgrind'
+ throw 'Skipped: cannot test signal INT with valgrind'
+ endif
+
+ let buf = RunVimInTerminal('', {'rows': 6})
+ let pid_vim = term_getjob(buf)->job_info().process
+
+ " Check that an endless loop in Vim is interrupted by signal INT.
+ call term_sendkeys(buf, ":while 1 | endwhile\n")
+ call WaitForAssert({-> assert_equal(':while 1 | endwhile', term_getline(buf, 6))})
+ exe 'silent !kill -s INT ' .. pid_vim
+ call term_sendkeys(buf, ":call setline(1, 'INTERUPTED')\n")
+ call WaitForAssert({-> assert_equal('INTERUPTED', term_getline(buf, 1))})
+
+ call StopVimInTerminal(buf)
+endfunc
+
" Test a deadly signal.
"
" There are several deadly signals: SISEGV, SIBUS, SIGTERM...
@@ -91,9 +119,7 @@ func Test_deadly_signal_TERM()
if !HasSignal('TERM')
throw 'Skipped: TERM signal not supported'
endif
- if !CanRunVimInTerminal()
- throw 'Skipped: cannot run vim in terminal'
- endif
+ CheckRunVimInTerminal
let cmd = GetVimCommand()
if cmd =~ 'valgrind'
throw 'Skipped: cannot test signal TERM with valgrind'
@@ -128,8 +154,7 @@ func Test_deadly_signal_TERM()
call assert_equal(['foo'], getline(1, '$'))
let result = readfile('XautoOut')
- call assert_match('VimLeavePre triggered', result[0])
- call assert_match('VimLeave triggered', result[1])
+ call assert_equal(["VimLeavePre triggered", "VimLeave triggered"], result)
%bwipe!
call delete('.Xsig_TERM.swp')
diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim
index ff9ba3d8ed..8311955a15 100644
--- a/src/nvim/testdir/test_signs.vim
+++ b/src/nvim/testdir/test_signs.vim
@@ -15,13 +15,13 @@ func Test_sign()
" the icon name when listing signs.
sign define Sign1 text=x
- call Sign_command_ignore_error('sign define Sign2 text=xy texthl=Title linehl=Error culhl=Search icon=../../pixmaps/stock_vim_find_help.png')
+ call Sign_command_ignore_error('sign define Sign2 text=xy texthl=Title linehl=Error culhl=Search numhl=Number icon=../../pixmaps/stock_vim_find_help.png')
" Test listing signs.
let a=execute('sign list')
call assert_match('^\nsign Sign1 text=x \nsign Sign2 ' .
\ 'icon=../../pixmaps/stock_vim_find_help.png .*text=xy ' .
- \ 'linehl=Error texthl=Title culhl=Search$', a)
+ \ 'linehl=Error texthl=Title culhl=Search numhl=Number$', a)
let a=execute('sign list Sign1')
call assert_equal("\nsign Sign1 text=x ", a)
@@ -127,26 +127,34 @@ func Test_sign()
call assert_fails("sign define Sign4 text=\\ ab linehl=Comment", 'E239:')
" an empty highlight argument for an existing sign clears it
- sign define SignY texthl=TextHl culhl=CulHl linehl=LineHl
+ sign define SignY texthl=TextHl culhl=CulHl linehl=LineHl numhl=NumHl
let sl = sign_getdefined('SignY')[0]
call assert_equal('TextHl', sl.texthl)
call assert_equal('CulHl', sl.culhl)
call assert_equal('LineHl', sl.linehl)
+ call assert_equal('NumHl', sl.numhl)
- sign define SignY texthl= culhl=CulHl linehl=LineHl
+ sign define SignY texthl= culhl=CulHl linehl=LineHl numhl=NumHl
let sl = sign_getdefined('SignY')[0]
call assert_false(has_key(sl, 'texthl'))
call assert_equal('CulHl', sl.culhl)
call assert_equal('LineHl', sl.linehl)
+ call assert_equal('NumHl', sl.numhl)
sign define SignY linehl=
let sl = sign_getdefined('SignY')[0]
call assert_false(has_key(sl, 'linehl'))
call assert_equal('CulHl', sl.culhl)
+ call assert_equal('NumHl', sl.numhl)
sign define SignY culhl=
let sl = sign_getdefined('SignY')[0]
call assert_false(has_key(sl, 'culhl'))
+ call assert_equal('NumHl', sl.numhl)
+
+ sign define SignY numhl=
+ let sl = sign_getdefined('SignY')[0]
+ call assert_false(has_key(sl, 'numhl'))
sign undefine SignY
@@ -158,7 +166,7 @@ func Test_sign()
sign define Sign5 text=X\ linehl=Comment
sign undefine Sign5
- sign define Sign5 linehl=Comment text=X\
+ sign define Sign5 linehl=Comment text=X\
sign undefine Sign5
" define sign with backslash
@@ -417,8 +425,8 @@ func Test_sign_funcs()
let attr = {'text' : '=>', 'linehl' : 'Search', 'texthl' : 'Error',
\ 'culhl': 'Visual', 'numhl': 'Number'}
call assert_equal(0, "sign1"->sign_define(attr))
- call assert_equal([{'name' : 'sign1', 'texthl' : 'Error', 'linehl': 'Search',
- \ 'culhl': 'Visual', 'numhl': 'Number', 'text' : '=>'}],
+ call assert_equal([{'name' : 'sign1', 'texthl' : 'Error', 'linehl' : 'Search',
+ \ 'culhl' : 'Visual', 'numhl': 'Number', 'text' : '=>'}],
\ sign_getdefined())
" Define a new sign without attributes and then update it
@@ -483,13 +491,13 @@ func Test_sign_funcs()
call assert_fails('call sign_place(5, "", "sign1", "@", {"lnum" : 10})',
\ 'E158:')
call assert_fails('call sign_place(5, "", "sign1", [], {"lnum" : 10})',
- \ 'E158:')
+ \ 'E730:')
call assert_fails('call sign_place(21, "", "sign1", "Xsign",
\ {"lnum" : -1})', 'E474:')
call assert_fails('call sign_place(22, "", "sign1", "Xsign",
\ {"lnum" : 0})', 'E474:')
call assert_fails('call sign_place(22, "", "sign1", "Xsign",
- \ {"lnum" : []})', 'E474:')
+ \ {"lnum" : []})', 'E745:')
call assert_equal(-1, sign_place(1, "*", "sign1", "Xsign", {"lnum" : 10}))
" Tests for sign_getplaced()
@@ -535,9 +543,9 @@ func Test_sign_funcs()
call assert_equal(15, sign_place(15, '', 'sign1', 'Xsign', {'lnum' : 20}))
call assert_equal(15, sign_place(15, '', 'sign2', 'Xsign'))
call assert_equal([{'bufnr' : bufnr(''), 'signs' :
- \ [{'id' : 15, 'group' : '', 'lnum' : 20, 'name' : 'sign2',
- \ 'priority' : 10}]}],
- \ sign_getplaced())
+ \ [{'id' : 15, 'group' : '', 'lnum' : 20, 'name' : 'sign2',
+ \ 'priority' : 10}]}],
+ \ sign_getplaced())
" Tests for sign_undefine()
call assert_equal(0, sign_undefine("sign1"))
@@ -1165,7 +1173,7 @@ func Test_sign_unplace()
call delete("Xsign2")
endfunc
-" Tests for auto-generating the sign identifier
+" Tests for auto-generating the sign identifier.
func Test_aaa_sign_id_autogen()
enew | only
call sign_unplace('*')
@@ -1650,10 +1658,34 @@ func Test_sign_lnum_adjust()
" changes made by this function.
let &undolevels=&undolevels
+ " Nvim: make sign adjustment when deleting lines match Vim
+ set signcolumn=yes:1
+
" Delete the line with the sign
call deletebufline('', 4)
let l = sign_getplaced(bufnr(''))
- call assert_equal(0, len(l[0].signs))
+ call assert_equal(4, l[0].signs[0].lnum)
+
+ " Undo the delete operation
+ undo
+ let l = sign_getplaced(bufnr(''))
+ call assert_equal(5, l[0].signs[0].lnum)
+
+ " Break the undo
+ let &undolevels=&undolevels
+
+ " Delete few lines at the end of the buffer including the line with the sign
+ " Sign line number should not change (as it is placed outside of the buffer)
+ call deletebufline('', 3, 6)
+ let l = sign_getplaced(bufnr(''))
+ call assert_equal(5, l[0].signs[0].lnum)
+
+ " Undo the delete operation. Sign should be restored to the previous line
+ undo
+ let l = sign_getplaced(bufnr(''))
+ call assert_equal(5, l[0].signs[0].lnum)
+
+ set signcolumn&
sign unplace * group=*
sign undefine sign1
@@ -1731,7 +1763,7 @@ func Test_sign_jump_func()
call assert_fails("call sign_jump(5, 'g5', 'foo')", 'E157:')
call assert_fails('call sign_jump([], "", "foo")', 'E745:')
call assert_fails('call sign_jump(2, [], "foo")', 'E730:')
- call assert_fails('call sign_jump(2, "", {})', 'E158:')
+ call assert_fails('call sign_jump(2, "", {})', 'E731:')
call assert_fails('call sign_jump(2, "", "baz")', 'E158:')
sign unplace * group=*
@@ -1741,9 +1773,7 @@ endfunc
" Test for correct cursor position after the sign column appears or disappears.
func Test_sign_cursor_position()
- if !CanRunVimInTerminal()
- throw 'Skipped: cannot make screendumps'
- endif
+ CheckRunVimInTerminal
let lines =<< trim END
call setline(1, [repeat('x', 75), 'mmmm', 'yyyy'])
@@ -1790,8 +1820,8 @@ func Test_sign_numcol()
set number
set signcolumn=number
sign define sign1 text==>
- sign place 10 line=1 name=sign1
sign define sign2 text=V
+ sign place 10 line=1 name=sign1
redraw!
call assert_equal("=> 01234", s:ScreenLine(1, 1, 8))
diff --git a/src/nvim/testdir/test_sleep.vim b/src/nvim/testdir/test_sleep.vim
index f71855fd4b..a428f380b0 100644
--- a/src/nvim/testdir/test_sleep.vim
+++ b/src/nvim/testdir/test_sleep.vim
@@ -21,6 +21,7 @@ func! Test_sleep_bang()
call s:assert_takes_longer('sl 50m', 50)
call s:assert_takes_longer('sl! 50m', 50)
call s:assert_takes_longer('1sleep', 1000)
+ call s:assert_takes_longer('normal 1gs', 1000)
endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_sort.vim b/src/nvim/testdir/test_sort.vim
index 9895ad754c..534393b724 100644
--- a/src/nvim/testdir/test_sort.vim
+++ b/src/nvim/testdir/test_sort.vim
@@ -80,7 +80,7 @@ func Test_sort_default()
call assert_equal(['2', 'A', 'AA', 'a', 1, 3.3], sort([3.3, 1, "2", "A", "a", "AA"], ''))
call assert_equal(['2', 'A', 'AA', 'a', 1, 3.3], sort([3.3, 1, "2", "A", "a", "AA"], 0))
call assert_equal(['2', 'A', 'a', 'AA', 1, 3.3], sort([3.3, 1, "2", "A", "a", "AA"], 1))
- call assert_fails('call sort([3.3, 1, "2"], 3)', "E474")
+ call assert_fails('call sort([3.3, 1, "2"], 3)', "E474:")
endfunc
" Tests for the ":sort" command.
@@ -1360,7 +1360,8 @@ func Test_sort_cmd()
call setline(1, ['line1', 'line2'])
call assert_fails('sort no', 'E474:')
call assert_fails('sort c', 'E475:')
- call assert_fails('sort #pat%', 'E682:')
+ call assert_fails('sort #pat%', 'E654:')
+ call assert_fails('sort /\%(/', 'E53:')
enew!
endfunc
diff --git a/src/nvim/testdir/test_source.vim b/src/nvim/testdir/test_source.vim
index ba6fd5ad95..d4d96e36bf 100644
--- a/src/nvim/testdir/test_source.vim
+++ b/src/nvim/testdir/test_source.vim
@@ -1,5 +1,8 @@
" Tests for the :source command.
+source check.vim
+source view_util.vim
+
func Test_source_autocmd()
call writefile([
\ 'let did_source = 1',
@@ -87,4 +90,24 @@ func Test_source_autocmd_sfile()
call delete('Xscript.vim')
endfunc
+func Test_source_error()
+ call assert_fails('scriptencoding utf-8', 'E167:')
+ call assert_fails('finish', 'E168:')
+ " call assert_fails('scriptversion 2', 'E984:')
+endfunc
+
+" Test for sourcing a script recursively
+func Test_nested_script()
+ CheckRunVimInTerminal
+ call writefile([':source! Xscript.vim', ''], 'Xscript.vim')
+ let buf = RunVimInTerminal('', {'rows': 6})
+ call term_wait(buf)
+ call term_sendkeys(buf, ":set noruler\n")
+ call term_sendkeys(buf, ":source! Xscript.vim\n")
+ call term_wait(buf)
+ call WaitForAssert({-> assert_match('E22: Scripts nested too deep\s*', term_getline(buf, 6))})
+ call delete('Xscript.vim')
+ call StopVimInTerminal(buf)
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim
index 8ab8204b10..c840e834b9 100644
--- a/src/nvim/testdir/test_spell.vim
+++ b/src/nvim/testdir/test_spell.vim
@@ -147,7 +147,7 @@ func Test_spell_file_missing()
augroup TestSpellFileMissing
autocmd! SpellFileMissing * bwipe
augroup END
- call assert_fails('set spell spelllang=ab_cd', 'E797:')
+ call assert_fails('set spell spelllang=ab_cd', 'E937:')
" clean up
augroup TestSpellFileMissing
@@ -159,6 +159,19 @@ func Test_spell_file_missing()
%bwipe!
endfunc
+func Test_spell_file_missing_bwipe()
+ " this was using a window that was wiped out in a SpellFileMissing autocmd
+ set spelllang=xy
+ au SpellFileMissing * n0
+ set spell
+ au SpellFileMissing * bw
+ snext somefile
+
+ au! SpellFileMissing
+ bwipe!
+ set nospell spelllang=en
+endfunc
+
func Test_spelldump()
" In case the spell file is not found avoid getting the download dialog, we
" would get stuck at the prompt.
@@ -329,6 +342,11 @@ func Test_spellsuggest()
call assert_equal(['Third'], spellsuggest('THird', 1))
call assert_equal(['All'], spellsuggest('ALl', 1))
+ " Special suggestion for repeated 'the the'.
+ call assert_inrange(0, 2, index(spellsuggest('the the', 3), 'the'))
+ call assert_inrange(0, 2, index(spellsuggest('the the', 3), 'the'))
+ call assert_inrange(0, 2, index(spellsuggest('The the', 3), 'The'))
+
call assert_fails("call spellsuggest('maxch', [])", 'E745:')
call assert_fails("call spellsuggest('maxch', 2, [])", 'E745:')
@@ -474,6 +492,35 @@ func Test_spellsuggest_option_expr()
bwipe!
endfunc
+" Test for 'spellsuggest' expr errrors
+func Test_spellsuggest_expr_errors()
+ " 'spellsuggest'
+ func MySuggest()
+ return range(3)
+ endfunc
+ set spell spellsuggest=expr:MySuggest()
+ call assert_equal([], spellsuggest('baord', 3))
+
+ " Test for 'spellsuggest' expression returning a non-list value
+ func! MySuggest2()
+ return 'good'
+ endfunc
+ set spellsuggest=expr:MySuggest2()
+ call assert_equal([], spellsuggest('baord'))
+
+ " Test for 'spellsuggest' expression returning a list with dict values
+ func! MySuggest3()
+ return [[{}, {}]]
+ endfunc
+ set spellsuggest=expr:MySuggest3()
+ call assert_fails("call spellsuggest('baord')", 'E731:')
+
+ set nospell spellsuggest&
+ delfunc MySuggest
+ delfunc MySuggest2
+ delfunc MySuggest3
+endfunc
+
func Test_spellsuggest_timeout()
set spellsuggest=timeout:30
set spellsuggest=timeout:-123
@@ -484,8 +531,23 @@ func Test_spellsuggest_timeout()
call assert_fails('set spellsuggest=timeout:--9', 'E474:')
endfunc
+func Test_spellsuggest_visual_end_of_line()
+ let enc_save = &encoding
+ " set encoding=iso8859
+
+ " This was reading beyond the end of the line.
+ norm R00000000000
+ sil norm 0
+ sil! norm i00000)
+ sil! norm i00000)
+ call feedkeys("\<CR>")
+ norm z=
+
+ let &encoding = enc_save
+endfunc
+
func Test_spellinfo()
- throw 'skipped: Nvim does not support enc=latin1'
+ throw 'Skipped: Nvim does not support enc=latin1'
new
let runtime = substitute($VIMRUNTIME, '\\', '/', 'g')
@@ -1401,3 +1463,5 @@ let g:test_data_aff_sal = [
\"SAL ZZ- _",
\"SAL Z S",
\ ]
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim
index 39fafbf7b4..1ee1d0dfe3 100644
--- a/src/nvim/testdir/test_startup.vim
+++ b/src/nvim/testdir/test_startup.vim
@@ -267,10 +267,9 @@ endfunc
" Test the -V[N] argument to set the 'verbose' option to [N]
func Test_V_arg()
- if has('gui_running')
- " Can't catch the output of gvim.
- return
- endif
+ " Can't catch the output of gvim.
+ CheckNotGui
+
let out = system(GetVimCommand() . ' --clean -es -X -V0 -c "set verbose?" -cq')
call assert_equal(" verbose=0\n", out)
@@ -431,7 +430,7 @@ endfunction
" Test the -reverse and +reverse arguments (for GUI only).
func Test_reverse()
CheckCanRunGui
- CheckAnyOf Feature:gui_gtk Feature:gui_motif Feature:gui_athena
+ CheckAnyOf Feature:gui_gtk Feature:gui_motif
let after =<< trim [CODE]
call writefile([&background], "Xtest_reverse")
@@ -452,7 +451,7 @@ endfunc
" Test the -background and -foreground arguments (for GUI only).
func Test_background_foreground()
CheckCanRunGui
- CheckAnyOf Feature:gui_gtk Feature:gui_motif Feature:gui_athena
+ CheckAnyOf Feature:gui_gtk Feature:gui_motif
" Is there a better way to check the effect of -background & -foreground
" other than merely looking at &background (dark or light)?
@@ -479,7 +478,7 @@ func Test_font()
if has('gui_gtk')
let font = 'Courier 14'
- elseif has('gui_motif') || has('gui_athena')
+ elseif has('gui_motif')
let font = '-misc-fixed-bold-*'
else
throw 'Skipped: test does not set a valid font for this GUI'
@@ -501,10 +500,10 @@ endfunc
" Test the -geometry argument (for GUI only).
func Test_geometry()
CheckCanRunGui
- CheckAnyOf Feature:gui_gtk Feature:gui_motif Feature:gui_athena
+ CheckAnyOf Feature:gui_gtk Feature:gui_motif
- if has('gui_motif') || has('gui_athena')
- " FIXME: With GUI Athena or Motif, the value of getwinposx(),
+ if has('gui_motif')
+ " FIXME: With GUI Motif the value of getwinposx(),
" getwinposy() and getwinpos() do not match exactly the
" value given in -geometry. Why?
" So only check &columns and &lines for those GUIs.
@@ -521,9 +520,18 @@ func Test_geometry()
call writefile([&columns, &lines, getwinposx(), getwinposy(), string(getwinpos())], "Xtest_geometry")
qall
[CODE]
- if RunVim([], after, '-f -g -geometry 31x13+41+43')
+ " Some window managers have a bar at the top that pushes windows down,
+ " need to use at least 130, let's do 150
+ if RunVim([], after, '-f -g -geometry 31x13+41+150')
let lines = readfile('Xtest_geometry')
- call assert_equal(['31', '13', '41', '43', '[41, 43]'], lines)
+ " Depending on the GUI library and the windowing system the final size
+ " might be a bit different, allow for some tolerance. Tuned based on
+ " actual failures.
+ call assert_inrange(31, 35, str2nr(lines[0]))
+ call assert_equal('13', lines[1])
+ call assert_equal('41', lines[2])
+ call assert_equal('150', lines[3])
+ call assert_equal('[41, 150]', lines[4])
endif
endif
@@ -533,7 +541,7 @@ endfunc
" Test the -iconic argument (for GUI only).
func Test_iconic()
CheckCanRunGui
- CheckAnyOf Feature:gui_gtk Feature:gui_motif Feature:gui_athena
+ CheckAnyOf Feature:gui_gtk Feature:gui_motif
call RunVim([], [], '-f -g -iconic -cq')
@@ -543,10 +551,9 @@ endfunc
func Test_invalid_args()
- if !has('unix') || has('gui_running')
- " can't get output of Vim.
- return
- endif
+ " must be able to get the output of Vim.
+ CheckUnix
+ CheckNotGui
for opt in ['-Y', '--does-not-exist']
let out = split(system(GetVimCommand() .. ' ' .. opt), "\n")
@@ -603,7 +610,7 @@ func Test_invalid_args()
call assert_equal(0, v:shell_error)
if has('quickfix')
- " Detect invalid repeated arguments '-t foo -t foo", '-q foo -q foo'.
+ " Detect invalid repeated arguments '-t foo -t foo', '-q foo -q foo'.
for opt in ['-t', '-q']
let out = split(system(GetVimCommand() .. repeat(' ' .. opt .. ' foo', 2)), "\n")
call assert_equal(1, v:shell_error)
@@ -622,6 +629,12 @@ func Test_invalid_args()
endfor
if has('gui_gtk')
+ let out = split(system(GetVimCommand() .. ' --socketid'), "\n")
+ call assert_equal(1, v:shell_error)
+ call assert_match('^VIM - Vi IMproved .* (.*)$', out[0])
+ call assert_equal('Argument missing after: "--socketid"', out[1])
+ call assert_equal('More info with: "vim -h"', out[2])
+
for opt in ['--socketid x', '--socketid 0xg']
let out = split(system(GetVimCommand() .. ' ' .. opt), "\n")
call assert_equal(1, v:shell_error)
@@ -629,6 +642,7 @@ func Test_invalid_args()
call assert_equal('Invalid argument for: "--socketid"', out[1])
call assert_equal('More info with: "vim -h"', out[2])
endfor
+
endif
endfunc
@@ -711,27 +725,6 @@ func Test_read_stdin()
call delete('Xtestout')
endfunc
-func Test_set_shell()
- let after =<< trim [CODE]
- call writefile([&shell], "Xtestout")
- quit!
- [CODE]
-
- if has('win32')
- let $SHELL = 'C:\with space\cmd.exe'
- let expected = '"C:\with space\cmd.exe"'
- else
- let $SHELL = '/bin/with space/sh'
- let expected = '"/bin/with space/sh"'
- endif
-
- if RunVimPiped([], after, '', '')
- let lines = readfile('Xtestout')
- call assert_equal(expected, lines[0])
- endif
- call delete('Xtestout')
-endfunc
-
func Test_progpath()
" Tests normally run with "./vim" or "../vim", these must have been expanded
" to a full path.
@@ -747,10 +740,9 @@ func Test_progpath()
endfunc
func Test_silent_ex_mode()
- if !has('unix') || has('gui_running')
- " can't get output of Vim.
- return
- endif
+ " must be able to get the output of Vim.
+ CheckUnix
+ CheckNotGui
" This caused an ml_get error.
let out = system(GetVimCommand() . ' -u NONE -es -c''set verbose=1|h|exe "%norm\<c-y>\<c-d>"'' -c cq')
@@ -758,10 +750,9 @@ func Test_silent_ex_mode()
endfunc
func Test_default_term()
- if !has('unix') || has('gui_running')
- " can't get output of Vim.
- return
- endif
+ " must be able to get the output of Vim.
+ CheckUnix
+ CheckNotGui
let save_term = $TERM
let $TERM = 'unknownxxx'
@@ -796,10 +787,17 @@ func Test_zzz_startinsert()
call delete('Xtestout')
endfunc
+func Test_issue_3969()
+ " Can't catch the output of gvim.
+ CheckNotGui
+
+ " Check that message is not truncated.
+ let out = system(GetVimCommand() . ' -es -X -V1 -c "echon ''hello''" -cq')
+ call assert_equal('hello', out)
+endfunc
+
func Test_start_with_tabs()
- if !CanRunVimInTerminal()
- return
- endif
+ CheckRunVimInTerminal
let buf = RunVimInTerminal('-p a b c', {})
call VerifyScreenDump(buf, 'Test_start_with_tabs', {})
@@ -855,7 +853,7 @@ func Test_t_arg()
call writefile([' first', ' second', ' third'], 'Xfile1')
for t_arg in ['-t second', '-tsecond']
- if RunVim(before, after, '-t second')
+ if RunVim(before, after, t_arg)
call assert_equal(['Xfile1:L2C5'], readfile('Xtestout'), t_arg)
call delete('Xtestout')
endif
@@ -943,6 +941,7 @@ endfunc
" Test for enabling the lisp mode on startup
func Test_l_arg()
+ throw 'Skipped: Nvim -l arg differs from Vim'
let after =<< trim [CODE]
let s = 'lisp=' .. &lisp .. ', showmatch=' .. &showmatch
call writefile([s], 'Xtestout')
@@ -956,9 +955,7 @@ endfunc
" Test for specifying a non-existing vimrc file using "-u"
func Test_missing_vimrc()
- if !CanRunVimInTerminal()
- throw 'Skipped: cannot run vim in terminal'
- endif
+ CheckRunVimInTerminal
let after =<< trim [CODE]
call assert_match('^E282:', v:errmsg)
call writefile(v:errors, 'Xtestout')
@@ -1016,6 +1013,7 @@ endfunc
" Test for using the 'exrc' option
func Test_exrc()
+ throw 'Skipped: Nvim requires user input for the exrc option'
let after =<< trim [CODE]
call assert_equal(1, &exrc)
call assert_equal(1, &secure)
@@ -1045,7 +1043,7 @@ func Test_io_not_a_terminal()
\ 'Vim: Warning: Input is not from a terminal'], l)
endfunc
-" Test for --not-a-term avoiding escape codes.
+" Test for not being a term avoiding escape codes.
func Test_not_a_term()
CheckUnix
CheckNotGui
@@ -1056,18 +1054,14 @@ func Test_not_a_term()
let redir = &shellredir .. ' Xvimout'
endif
- " Without --not-a-term there are a few escape sequences.
- " This will take 2 seconds because of the missing --not-a-term
+ " As nvim checks the environment by itself there will be no escape sequences
+ " This will also happen to take two (2) seconds.
let cmd = GetVimProg() .. ' --cmd quit ' .. redir
exe "silent !" . cmd
- call assert_match("\<Esc>", readfile('Xvimout')->join())
+ call assert_notmatch("\e", readfile('Xvimout')->join())
call delete('Xvimout')
- " With --not-a-term there are no escape sequences.
- let cmd = GetVimProg() .. ' --not-a-term --cmd quit ' .. redir
- exe "silent !" . cmd
- call assert_notmatch("\<Esc>", readfile('Xvimout')->join())
- call delete('Xvimout')
+ " --not-a-term flag has thus been deleted
endfunc
@@ -1271,4 +1265,19 @@ func Test_progname()
call delete('Xprogname', 'd')
endfunc
+" Test for doing a write from .vimrc
+func Test_write_in_vimrc()
+ call writefile(['silent! write'], 'Xvimrc')
+ let after =<< trim [CODE]
+ call assert_match('E32: ', v:errmsg)
+ call writefile(v:errors, 'Xtestout')
+ qall
+ [CODE]
+ if RunVim([], after, '-u Xvimrc')
+ call assert_equal([], readfile('Xtestout'))
+ call delete('Xtestout')
+ endif
+ call delete('Xvimrc')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_startup_utf8.vim b/src/nvim/testdir/test_startup_utf8.vim
index bb4304396e..2ee6ecc41d 100644
--- a/src/nvim/testdir/test_startup_utf8.vim
+++ b/src/nvim/testdir/test_startup_utf8.vim
@@ -63,9 +63,7 @@ func Test_read_fifo_utf8()
endfunc
func Test_detect_ambiwidth()
- if !CanRunVimInTerminal()
- throw 'Skipped: cannot run Vim in a terminal window'
- endif
+ CheckRunVimInTerminal
" Use the title termcap entries to output the escape sequence.
call writefile([
diff --git a/src/nvim/testdir/test_statusline.vim b/src/nvim/testdir/test_statusline.vim
index 6bde052442..990c852ccd 100644
--- a/src/nvim/testdir/test_statusline.vim
+++ b/src/nvim/testdir/test_statusline.vim
@@ -565,4 +565,45 @@ func Test_statusline_highlight_truncate()
call delete('XTest_statusline')
endfunc
+func Test_statusline_showcmd()
+ CheckScreendump
+
+ let lines =<< trim END
+ func MyStatusLine()
+ return '%S'
+ endfunc
+
+ set laststatus=2
+ set statusline=%!MyStatusLine()
+ set showcmdloc=statusline
+ call setline(1, ['a', 'b', 'c'])
+ set foldopen+=jump
+ 1,2fold
+ 3
+ END
+ call writefile(lines, 'XTest_statusline', 'D')
+
+ let buf = RunVimInTerminal('-S XTest_statusline', {'rows': 6})
+
+ call term_sendkeys(buf, "g")
+ call VerifyScreenDump(buf, 'Test_statusline_showcmd_1', {})
+
+ " typing "gg" should open the fold
+ call term_sendkeys(buf, "g")
+ call VerifyScreenDump(buf, 'Test_statusline_showcmd_2', {})
+
+ call term_sendkeys(buf, "\<C-V>Gl")
+ call VerifyScreenDump(buf, 'Test_statusline_showcmd_3', {})
+
+ call term_sendkeys(buf, "\<Esc>1234")
+ call VerifyScreenDump(buf, 'Test_statusline_showcmd_4', {})
+
+ call term_sendkeys(buf, "\<Esc>:set statusline=\<CR>")
+ call term_sendkeys(buf, ":\<CR>")
+ call term_sendkeys(buf, "1234")
+ call VerifyScreenDump(buf, 'Test_statusline_showcmd_5', {})
+
+ call StopVimInTerminal(buf)
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim
index b3a80072d9..c99a0d456d 100644
--- a/src/nvim/testdir/test_substitute.vim
+++ b/src/nvim/testdir/test_substitute.vim
@@ -1,6 +1,8 @@
" Tests for the substitute (:s) command
source shared.vim
+source check.vim
+source screendump.vim
func Test_multiline_subst()
enew!
@@ -444,13 +446,19 @@ func Test_substitute_errors()
call assert_fails('s/FOO/bar/', 'E486:')
call assert_fails('s/foo/bar/@', 'E488:')
- call assert_fails('s/\(/bar/', 'E476:')
+ call assert_fails('s/\(/bar/', 'E54:')
call assert_fails('s afooabara', 'E146:')
call assert_fails('s\\a', 'E10:')
setl nomodifiable
call assert_fails('s/foo/bar/', 'E21:')
+ call assert_fails("let s=substitute([], 'a', 'A', 'g')", 'E730:')
+ call assert_fails("let s=substitute('abcda', [], 'A', 'g')", 'E730:')
+ call assert_fails("let s=substitute('abcda', 'a', [], 'g')", 'E730:')
+ call assert_fails("let s=substitute('abcda', 'a', 'A', [])", 'E730:')
+ call assert_fails("let s=substitute('abc', '\\%(', 'A', 'g')", 'E53:')
+
bwipe!
endfunc
@@ -486,6 +494,9 @@ func Test_sub_replace_1()
call assert_equal("x\<C-M>x", substitute('xXx', 'X', "\r", ''))
call assert_equal("YyyY", substitute('Y', 'Y', '\L\uyYy\l\EY', ''))
call assert_equal("zZZz", substitute('Z', 'Z', '\U\lZzZ\u\Ez', ''))
+ " \v or \V after $
+ call assert_equal('abxx', substitute('abcd', 'xy$\v|cd$', 'xx', ''))
+ call assert_equal('abxx', substitute('abcd', 'xy$\V\|cd\$', 'xx', ''))
endfunc
func Test_sub_replace_2()
@@ -637,12 +648,16 @@ endfunc
func SubReplacer(text, submatches)
return a:text .. a:submatches[0] .. a:text
endfunc
+func SubReplacerVar(text, ...)
+ return a:text .. a:1[0] .. a:text
+endfunc
func SubReplacer20(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, submatches)
return a:t3 .. a:submatches[0] .. a:t11
endfunc
func Test_substitute_partial()
call assert_equal('1foo2foo3', substitute('123', '2', function('SubReplacer', ['foo']), 'g'))
+ call assert_equal('1foo2foo3', substitute('123', '2', function('SubReplacerVar', ['foo']), 'g'))
" 19 arguments plus one is just OK
let Replacer = function('SubReplacer20', repeat(['foo'], 19))
@@ -668,6 +683,21 @@ func Test_sub_cmd_9()
bw!
endfunc
+func Test_sub_highlight_zero_match()
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ call setline(1, ['one', 'two', 'three'])
+ END
+ call writefile(lines, 'XscriptSubHighlight', 'D')
+ let buf = RunVimInTerminal('-S XscriptSubHighlight', #{rows: 8, cols: 60})
+ call term_sendkeys(buf, ":%s/^/ /c\<CR>")
+ call VerifyScreenDump(buf, 'Test_sub_highlight_zer_match_1', {})
+
+ call term_sendkeys(buf, "\<Esc>")
+ call StopVimInTerminal(buf)
+endfunc
+
func Test_nocatch_sub_failure_handling()
" normal error results in all replacements
func Foo()
@@ -812,9 +842,9 @@ endfunc
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 assert_fails('s//abc/g', 'E35:')
+ call assert_fails('s\/bar', 'E35:')
+ call assert_fails('s\&bar&', 'E33:')
call writefile(v:errors, 'Xresult')
qall!
[SCRIPT]
@@ -841,6 +871,40 @@ endfunc
func Test_substitute()
call assert_equal('a1a2a3a', substitute('123', '\zs', 'a', 'g'))
+ " Substitute with special keys
+ call assert_equal("a\<End>c", substitute('abc', "a.c", "a\<End>c", ''))
+endfunc
+
+func Test_substitute_expr()
+ let g:val = 'XXX'
+ call assert_equal('XXX', substitute('yyy', 'y*', '\=g:val', ''))
+ call assert_equal('XXX', substitute('yyy', 'y*', {-> g:val}, ''))
+ call assert_equal("-\u1b \uf2-", substitute("-%1b %f2-", '%\(\x\x\)',
+ \ '\=nr2char("0x" . submatch(1))', 'g'))
+ call assert_equal("-\u1b \uf2-", substitute("-%1b %f2-", '%\(\x\x\)',
+ \ {-> nr2char("0x" . submatch(1))}, 'g'))
+
+ call assert_equal('231', substitute('123', '\(.\)\(.\)\(.\)',
+ \ {-> submatch(2) . submatch(3) . submatch(1)}, ''))
+
+ func Recurse()
+ return substitute('yyy', 'y\(.\)y', {-> submatch(1)}, '')
+ endfunc
+ " recursive call works
+ call assert_equal('-y-x-', substitute('xxx', 'x\(.\)x', {-> '-' . Recurse() . '-' . submatch(1) . '-'}, ''))
+
+ call assert_fails("let s=submatch([])", 'E745:')
+ call assert_fails("let s=submatch(2, [])", 'E745:')
+endfunc
+
+func Test_invalid_submatch()
+ " This was causing invalid memory access in Vim-7.4.2232 and older
+ call assert_fails("call substitute('x', '.', {-> submatch(10)}, '')", 'E935:')
+ call assert_fails('eval submatch(-1)', 'E935:')
+ call assert_equal('', submatch(0))
+ call assert_equal('', submatch(1))
+ call assert_equal([], submatch(0, 1))
+ call assert_equal([], submatch(1, 1))
endfunc
func Test_submatch_list_concatenate()
@@ -849,6 +913,44 @@ func Test_submatch_list_concatenate()
call substitute('A1', pat, Rep, '')->assert_equal("[['A1'], ['1']]")
endfunc
+func Test_substitute_expr_arg()
+ call assert_equal('123456789-123456789=', substitute('123456789',
+ \ '\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)',
+ \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
+
+ call assert_equal('123456-123456=789', substitute('123456789',
+ \ '\(.\)\(.\)\(.\)\(a*\)\(n*\)\(.\)\(.\)\(.\)\(x*\)',
+ \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
+
+ call assert_equal('123456789-123456789x=', substitute('123456789',
+ \ '\(.\)\(.\)\(.*\)',
+ \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . 'x' . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
+
+ call assert_fails("call substitute('xxx', '.', {m -> string(add(m, 'x'))}, '')", 'E742:')
+ call assert_fails("call substitute('xxx', '.', {m -> string(insert(m, 'x'))}, '')", 'E742:')
+ call assert_fails("call substitute('xxx', '.', {m -> string(extend(m, ['x']))}, '')", 'E742:')
+ call assert_fails("call substitute('xxx', '.', {m -> string(remove(m, 1))}, '')", 'E742:')
+endfunc
+
+" Test for using a function to supply the substitute string
+func Test_substitute_using_func()
+ func Xfunc()
+ return '1234'
+ endfunc
+ call assert_equal('a1234f', substitute('abcdef', 'b..e',
+ \ function("Xfunc"), ''))
+ delfunc Xfunc
+endfunc
+
+" Test for using submatch() with a multiline match
+func Test_substitute_multiline_submatch()
+ new
+ call setline(1, ['line1', 'line2', 'line3', 'line4'])
+ %s/^line1\(\_.\+\)line4$/\=submatch(1)/
+ call assert_equal(['', 'line2', 'line3', ''], getline(1, '$'))
+ close!
+endfunc
+
func Test_substitute_skipped_range()
new
if 0
@@ -974,6 +1076,19 @@ func Test_sub_open_cmdline_win()
call delete('Xresult')
endfunc
+" This was editing a script file from the expression
+func Test_sub_edit_scriptfile()
+ new
+ norm o0000000000000000000000000000000000000000000000000000
+ func EditScript()
+ silent! scr! Xfile
+ endfunc
+ s/\%')/\=EditScript()
+
+ delfunc EditScript
+ bwipe!
+endfunc
+
" Test for the 2-letter and 3-letter :substitute commands
func Test_substitute_short_cmd()
new
diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim
index 923e1cbf50..cf46b4c5bd 100644
--- a/src/nvim/testdir/test_swap.vim
+++ b/src/nvim/testdir/test_swap.vim
@@ -2,6 +2,7 @@
source check.vim
source shared.vim
+source term_util.vim
func s:swapname()
return trim(execute('swapname'))
@@ -202,8 +203,8 @@ func Test_swapfile_delete()
" 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
+ " deleted. Process 0x3fffffff most likely does not exist.
+ let swapfile_bytes[24:27] = 0zffffff3f
call writefile(swapfile_bytes, swapfile_name)
let s:swapname = ''
split XswapfileText
@@ -374,24 +375,26 @@ func Test_swap_prompt_splitwin()
call WaitForAssert({-> assert_match('^1$', term_getline(buf, 20))})
call StopVimInTerminal(buf)
- " This caused Vim to crash when typing "q".
- " TODO: it does not actually reproduce the crash.
- call writefile(['au BufAdd * set virtualedit=all'], 'Xvimrc')
-
- let buf = RunVimInTerminal('-u Xvimrc Xfile1', {'rows': 20, 'wait_for_ruler': 0})
- call TermWait(buf)
- call WaitForAssert({-> assert_match('^\[O\]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort:', term_getline(buf, 20))})
+ " This caused Vim to crash when typing "q" at the swap file prompt.
+ let buf = RunVimInTerminal('-c "au bufadd * let foo_w = wincol()"', {'rows': 18})
+ call term_sendkeys(buf, ":e Xfile1\<CR>")
+ call WaitForAssert({-> assert_match('More', term_getline(buf, 18))})
+ call term_sendkeys(buf, " ")
+ call WaitForAssert({-> assert_match('^\[O\]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort:', term_getline(buf, 18))})
call term_sendkeys(buf, "q")
+ call TermWait(buf)
+ " check that Vim is still running
+ call term_sendkeys(buf, ":echo 'hello'\<CR>")
+ call WaitForAssert({-> assert_match('^hello', term_getline(buf, 18))})
+ call term_sendkeys(buf, ":%bwipe!\<CR>")
+ call StopVimInTerminal(buf)
%bwipe!
call delete('Xfile1')
- call delete('Xvimrc')
endfunc
func Test_swap_symlink()
- if !has("unix")
- return
- endif
+ CheckUnix
call writefile(['text'], 'Xtestfile')
silent !ln -s -f Xtestfile Xtestlink
@@ -418,6 +421,161 @@ func Test_swap_symlink()
call delete('Xswapdir', 'rf')
endfunc
+func s:get_unused_pid(base)
+ if has('job')
+ " Execute 'echo' as a temporary job, and return its pid as an unused pid.
+ if has('win32')
+ let cmd = 'cmd /c echo'
+ else
+ let cmd = 'echo'
+ endif
+ let j = job_start(cmd)
+ while job_status(j) ==# 'run'
+ sleep 10m
+ endwhile
+ if job_status(j) ==# 'dead'
+ return job_info(j).process
+ endif
+ endif
+ " Must add four for MS-Windows to see it as a different one.
+ return a:base + 4
+endfunc
+
+func s:blob_to_pid(b)
+ return a:b[3] * 16777216 + a:b[2] * 65536 + a:b[1] * 256 + a:b[0]
+endfunc
+
+func s:pid_to_blob(i)
+ let b = 0z
+ let b[0] = and(a:i, 0xff)
+ let b[1] = and(a:i / 256, 0xff)
+ let b[2] = and(a:i / 65536, 0xff)
+ let b[3] = and(a:i / 16777216, 0xff)
+ return b
+endfunc
+
+func Test_swap_auto_delete()
+ " 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')
+
+ " Forget about the file, recreate the swap file, then edit it again. The
+ " swap file should be automatically deleted.
+ bwipe!
+ " Change the process ID to avoid the "still running" warning.
+ let swapfile_bytes[24:27] = s:pid_to_blob(s:get_unused_pid(
+ \ s:blob_to_pid(swapfile_bytes[24:27])))
+ call writefile(swapfile_bytes, swapfile_name)
+ edit Xtest.scr
+ " will end up using the same swap file after deleting the existing one
+ call assert_equal(swapfile_name, swapname('%'))
+ bwipe!
+
+ " create the swap file again, but change the host name so that it won't be
+ " deleted
+ autocmd! SwapExists
+ augroup test_swap_recover_ext
+ autocmd!
+ autocmd SwapExists * let v:swapchoice = 'e'
+ augroup END
+
+ " change the host name
+ let swapfile_bytes[28 + 40] = swapfile_bytes[28 + 40] + 2
+ call writefile(swapfile_bytes, swapfile_name)
+ edit Xtest.scr
+ call assert_equal(1, filereadable(swapfile_name))
+ " will use another same swap file name
+ call assert_notequal(swapfile_name, swapname('%'))
+ bwipe!
+
+ call delete('Xtest.scr')
+ call delete(swapfile_name)
+ augroup test_swap_recover_ext
+ autocmd!
+ augroup END
+ augroup! test_swap_recover_ext
+endfunc
+
+" Test for renaming a buffer when the swap file is deleted out-of-band
+func Test_missing_swap_file()
+ CheckUnix
+ new Xfile2
+ call delete(swapname(''))
+ call assert_fails('file Xfile3', 'E301:')
+ call assert_equal('Xfile3', bufname())
+ call assert_true(bufexists('Xfile2'))
+ call assert_true(bufexists('Xfile3'))
+ %bw!
+endfunc
+
+" Test for :preserve command
+func Test_preserve()
+ new Xfile4
+ setlocal noswapfile
+ call assert_fails('preserve', 'E313:')
+ bw!
+endfunc
+
+" Test for the v:swapchoice variable
+func Test_swapchoice()
+ call writefile(['aaa', 'bbb'], 'Xfile5')
+ edit Xfile5
+ preserve
+ let swapfname = swapname('')
+ let b = readblob(swapfname)
+ bw!
+ call writefile(b, swapfname)
+
+ autocmd! SwapExists
+
+ " Test for v:swapchoice = 'o' (readonly)
+ augroup test_swapchoice
+ autocmd!
+ autocmd SwapExists * let v:swapchoice = 'o'
+ augroup END
+ edit Xfile5
+ call assert_true(&readonly)
+ call assert_equal(['aaa', 'bbb'], getline(1, '$'))
+ %bw!
+ call assert_true(filereadable(swapfname))
+
+ " Test for v:swapchoice = 'a' (abort)
+ augroup test_swapchoice
+ autocmd!
+ autocmd SwapExists * let v:swapchoice = 'a'
+ augroup END
+ try
+ edit Xfile5
+ catch /^Vim:Interrupt$/
+ endtry
+ call assert_equal('', @%)
+ call assert_true(bufexists('Xfile5'))
+ %bw!
+ call assert_true(filereadable(swapfname))
+
+ " Test for v:swapchoice = 'd' (delete)
+ augroup test_swapchoice
+ autocmd!
+ autocmd SwapExists * let v:swapchoice = 'd'
+ augroup END
+ edit Xfile5
+ call assert_equal('Xfile5', @%)
+ %bw!
+ call assert_false(filereadable(swapfname))
+
+ call delete('Xfile5')
+ call delete(swapfname)
+ augroup test_swapchoice
+ autocmd!
+ augroup END
+ augroup! test_swapchoice
+endfunc
+
func Test_no_swap_file()
call assert_equal("\nNo swap file", execute('swapname'))
endfunc
diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim
index 7ba0149971..45230c4208 100644
--- a/src/nvim/testdir/test_syntax.vim
+++ b/src/nvim/testdir/test_syntax.vim
@@ -113,6 +113,9 @@ func Test_syntime()
let a = execute('syntime report')
call assert_equal("\nNo Syntax items defined for this buffer", a)
+ let a = execute('syntime clear')
+ call assert_equal("\nNo Syntax items defined for this buffer", a)
+
view samples/memfile_test.c
setfiletype cpp
redraw
@@ -171,6 +174,10 @@ func Test_syntax_list()
let a = execute('syntax list')
call assert_equal("\nNo Syntax items defined for this buffer", a)
+ syntax keyword Type int containedin=g1 skipwhite skipempty skipnl nextgroup=Abc
+ let exp = "Type xxx containedin=g1 nextgroup=Abc skipnl skipwhite skipempty int"
+ call assert_equal(exp, split(execute("syntax list"), "\n")[1])
+
bd
endfunc
@@ -188,22 +195,26 @@ func Test_syntax_completion()
call assert_equal('"syn sync ccomment clear fromstart linebreaks= linecont lines= match maxlines= minlines= region', @:)
" Check that clearing "Aap" avoids it showing up before Boolean.
- hi Aap ctermfg=blue
+ hi @Aap ctermfg=blue
call feedkeys(":syn list \<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_match('^"syn list Aap Boolean Character ', @:)
- hi clear Aap
+ call assert_match('^"syn list @Aap @boolean @character ', @:)
+ hi clear @Aap
call feedkeys(":syn list \<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_match('^"syn list Boolean Character ', @:)
+ call assert_match('^"syn list @boolean @character ', @:)
call feedkeys(":syn match \<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_match('^"syn match Boolean Character ', @:)
+ call assert_match('^"syn match @boolean @character ', @:)
+
+ syn cluster Aax contains=Aap
+ call feedkeys(":syn list @A\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_match('^"syn list @Aax', @:)
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', @:)
+ call assert_equal('"echohl NonText Normal NormalFloat none', @:)
endfunc
func Test_syntax_arg_skipped()
@@ -343,6 +354,18 @@ func Test_syntax_arg_skipped()
syn clear
endfunc
+" Check for an error. Used when multiple errors are thrown and we are checking
+" for an earliest error.
+func AssertFails(cmd, errcode)
+ let save_exception = ''
+ try
+ exe a:cmd
+ catch
+ let save_exception = v:exception
+ endtry
+ call assert_match(a:errcode, save_exception)
+endfunc
+
func Test_syntax_invalid_arg()
call assert_fails('syntax case asdf', 'E390:')
if has('conceal')
@@ -350,11 +373,51 @@ func Test_syntax_invalid_arg()
endif
call assert_fails('syntax spell asdf', 'E390:')
call assert_fails('syntax clear @ABCD', 'E391:')
- call assert_fails('syntax include @Xxx', 'E397:')
- call assert_fails('syntax region X start="{"', 'E399:')
+ call assert_fails('syntax include random_file', 'E484:')
+ call assert_fails('syntax include <afile>', 'E495:')
call assert_fails('syntax sync x', 'E404:')
call assert_fails('syntax keyword Abc a[', 'E789:')
call assert_fails('syntax keyword Abc a[bc]d', 'E890:')
+ call assert_fails('syntax cluster Abc add=A add=', 'E406:')
+
+ " Test for too many \z\( and unmatched \z\(
+ " Not able to use assert_fails() here because both E50:/E879: and E475:
+ " messages are emitted.
+ set regexpengine=1
+ call AssertFails("syntax region MyRegion start='\\z\\(' end='\\*/'", 'E52:')
+
+ let cmd = "syntax region MyRegion start='"
+ let cmd ..= repeat("\\z\\(.\\)", 10) .. "' end='\*/'"
+ call AssertFails(cmd, 'E50:')
+
+ set regexpengine=2
+ call AssertFails("syntax region MyRegion start='\\z\\(' end='\\*/'", 'E54:')
+
+ let cmd = "syntax region MyRegion start='"
+ let cmd ..= repeat("\\z\\(.\\)", 10) .. "' end='\*/'"
+ call AssertFails(cmd, 'E879:')
+ set regexpengine&
+
+ call AssertFails('syntax keyword cMyItem grouphere G1', 'E393:')
+ call AssertFails('syntax sync match Abc grouphere MyItem "abc"', 'E394:')
+ call AssertFails('syn keyword Type contains int', 'E395:')
+ call assert_fails('syntax include @Xxx', 'E397:')
+ call AssertFails('syntax region X start', 'E398:')
+ call assert_fails('syntax region X start="{"', 'E399:')
+ call AssertFails('syntax cluster contains=Abc', 'E400:')
+ call AssertFails("syntax match Character /'.'", 'E401:')
+ call AssertFails("syntax match Character /'.'/a", 'E402:')
+ call assert_fails('syntax sync linecont /\%(/', 'E53:')
+ call assert_fails('syntax sync linecont /pat', 'E404:')
+ call assert_fails('syntax sync linecont', 'E404:')
+ call assert_fails('syntax sync linecont /pat1/ linecont /pat2/', 'E403:')
+ call assert_fails('syntax sync minlines=a', 'E404:')
+ call AssertFails('syntax match ABC /x/ contains=', 'E406:')
+ call AssertFails("syntax match Character contains /'.'/", 'E405:')
+ call AssertFails('syntax match ccFoo "Foo" nextgroup=ALLBUT,F', 'E407:')
+ call AssertFails('syntax region Block start="{" contains=F,ALLBUT', 'E408:')
+ call AssertFails("syntax match Characters contains=a.*x /'.'/", 'E409:')
+ call assert_fails('syntax match Search /abc/ contains=ALLBUT,/\%(/', 'E53:')
endfunc
func Test_syn_sync()
@@ -382,6 +445,7 @@ func Test_syn_clear()
hi clear Foo
call assert_equal('Foo', synIDattr(hlID("Foo"), "name"))
hi clear Bar
+ call assert_fails('syntax clear invalid_syngroup', 'E28:')
endfunc
func Test_invalid_name()
@@ -389,7 +453,7 @@ func Test_invalid_name()
syn keyword Nop yes
call assert_fails("syntax keyword Wr\x17ong bar", 'E669:')
syntax keyword @Wrong bar
- call assert_match('W18:', execute('1messages'))
+ call assert_fails("syntax keyword @#Wrong bar", 'E5248:')
syn clear
hi clear Nop
hi clear @Wrong
@@ -475,15 +539,16 @@ func Test_conceal()
call assert_match('16 ', ScreenLines(2, 7)[0])
call assert_equal([[0, '', 0], [1, '', 1], [1, '', 1], [1, '', 2], [1, '', 2], [0, '', 0]], map(range(1, 6), 'synconcealed(2, v:val)'))
+ call AssertFails("syntax match Entity '&amp;' conceal cchar=\<Tab>", 'E844:')
+
syn clear
set conceallevel&
bw!
endfunc
func Test_bg_detection()
- if has('gui_running')
- return
- endif
+ CheckNotGui
+
" auto-detection of &bg, make sure sure it isn't set anywhere before
" this test
hi Normal ctermbg=0
@@ -560,15 +625,15 @@ func Test_synstack_synIDtrans()
call assert_equal(['cComment', 'cTodo'], map(synstack(line("."), col(".")), 'synIDattr(v:val, "name")'))
call assert_equal(['Comment', 'Todo'], map(synstack(line("."), col(".")), 'synIDattr(synIDtrans(v:val), "name")'))
+ call assert_fails("let n=synIDtrans([])", 'E745:')
+
syn clear
bw!
endfunc
" Check highlighting for a small piece of C code with a screen dump.
func Test_syntax_c()
- if !CanRunVimInTerminal()
- throw 'Skipped: cannot make screendumps'
- endif
+ CheckRunVimInTerminal
call writefile([
\ '/* comment line at the top */',
\ 'int main(int argc, char **argv) { // another comment',
@@ -603,6 +668,24 @@ func Test_syntax_c()
call delete('Xtest.c')
endfun
+" Test \z(...) along with \z1
+func Test_syn_zsub()
+ new
+ syntax on
+ call setline(1, 'xxx start foo xxx not end foo xxx end foo xxx')
+ let l:expected = ' ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ '
+
+ for l:re in [0, 1, 2]
+ " Example taken from :help :syn-ext-match
+ syntax region Z start="start \z(\I\i*\)" skip="not end \z1" end="end \z1"
+ eval AssertHighlightGroups(1, 1, l:expected, 1, 'regexp=' .. l:re)
+ syntax clear Z
+ endfor
+
+ set re&
+ bw!
+endfunc
+
" Using \z() in a region with NFA failing should not crash.
func Test_syn_wrong_z_one()
new
diff --git a/src/nvim/testdir/test_system.vim b/src/nvim/testdir/test_system.vim
index 18692f42c9..6c8373b335 100644
--- a/src/nvim/testdir/test_system.vim
+++ b/src/nvim/testdir/test_system.vim
@@ -141,3 +141,5 @@ func Test_system_with_shell_quote()
call delete('Xdir with spaces', 'rf')
endtry
endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_tabline.vim b/src/nvim/testdir/test_tabline.vim
index e58a412c5a..d9bef09067 100644
--- a/src/nvim/testdir/test_tabline.vim
+++ b/src/nvim/testdir/test_tabline.vim
@@ -1,6 +1,9 @@
" Test for tabline
source shared.vim
+source view_util.vim
+source check.vim
+source screendump.vim
func TablineWithCaughtError()
let s:func_in_tabline_called = 1
@@ -147,4 +150,58 @@ func Test_tabline_20_format_items_no_overrun()
set showtabline& tabline&
endfunc
+func Test_mouse_click_in_tab()
+ " This used to crash because TabPageIdxs[] was not initialized
+ let lines =<< trim END
+ tabnew
+ set mouse=a
+ exe "norm \<LeftMouse>"
+ END
+ call writefile(lines, 'Xclickscript')
+ call RunVim([], [], "-e -s -S Xclickscript -c qa")
+
+ call delete('Xclickscript')
+endfunc
+
+func Test_tabline_showcmd()
+ CheckScreendump
+
+ let lines =<< trim END
+ func MyTabLine()
+ return '%S'
+ endfunc
+
+ set showtabline=2
+ set tabline=%!MyTabLine()
+ set showcmdloc=tabline
+ call setline(1, ['a', 'b', 'c'])
+ set foldopen+=jump
+ 1,2fold
+ 3
+ END
+ call writefile(lines, 'XTest_tabline', 'D')
+
+ let buf = RunVimInTerminal('-S XTest_tabline', {'rows': 6})
+
+ call term_sendkeys(buf, "g")
+ call VerifyScreenDump(buf, 'Test_tabline_showcmd_1', {})
+
+ " typing "gg" should open the fold
+ call term_sendkeys(buf, "g")
+ call VerifyScreenDump(buf, 'Test_tabline_showcmd_2', {})
+
+ call term_sendkeys(buf, "\<C-V>Gl")
+ call VerifyScreenDump(buf, 'Test_tabline_showcmd_3', {})
+
+ call term_sendkeys(buf, "\<Esc>1234")
+ call VerifyScreenDump(buf, 'Test_tabline_showcmd_4', {})
+
+ call term_sendkeys(buf, "\<Esc>:set tabline=\<CR>")
+ call term_sendkeys(buf, ":\<CR>")
+ call term_sendkeys(buf, "1234")
+ call VerifyScreenDump(buf, 'Test_tabline_showcmd_5', {})
+
+ call StopVimInTerminal(buf)
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim
index 6d468ec9de..b97aa409d8 100644
--- a/src/nvim/testdir/test_tabpage.vim
+++ b/src/nvim/testdir/test_tabpage.vim
@@ -591,9 +591,7 @@ func Test_tabs()
endfunc
func Test_tabpage_cmdheight()
- if !CanRunVimInTerminal()
- throw 'Skipped: cannot make screendumps'
- endif
+ CheckRunVimInTerminal
call writefile([
\ 'set laststatus=2',
\ 'set cmdheight=2',
@@ -623,6 +621,17 @@ func Test_tabpage_close_cmdwin()
tabonly
endfunc
+" Pressing <C-PageUp> in insert mode should go to the previous tab page
+" and <C-PageDown> should go to the next tab page
+func Test_tabpage_Ctrl_Pageup()
+ tabnew
+ call feedkeys("i\<C-PageUp>", 'xt')
+ call assert_equal(1, tabpagenr())
+ call feedkeys("i\<C-PageDown>", 'xt')
+ call assert_equal(2, tabpagenr())
+ %bw!
+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.
diff --git a/src/nvim/testdir/test_tagfunc.vim b/src/nvim/testdir/test_tagfunc.vim
index ffc1d63b90..cba96d3504 100644
--- a/src/nvim/testdir/test_tagfunc.vim
+++ b/src/nvim/testdir/test_tagfunc.vim
@@ -1,5 +1,9 @@
" Test 'tagfunc'
+source vim9.vim
+source check.vim
+source screendump.vim
+
func TagFunc(pat, flag, info)
let g:tagfunc_args = [a:pat, a:flag, a:info]
let tags = []
@@ -85,7 +89,7 @@ func Test_tagfunc()
return v:null
endfunc
set tags= tfu=NullTagFunc
- call assert_fails('tag nothing', 'E426')
+ call assert_fails('tag nothing', 'E433')
delf NullTagFunc
bwipe!
@@ -117,4 +121,297 @@ func Test_tagfunc_settagstack()
delfunc Mytagfunc2
endfunc
+" Script local tagfunc callback function
+func s:ScriptLocalTagFunc(pat, flags, info)
+ let g:ScriptLocalFuncArgs = [a:pat, a:flags, a:info]
+ return v:null
+endfunc
+
+" Test for different ways of setting the 'tagfunc' option
+func Test_tagfunc_callback()
+ func TagFunc1(callnr, pat, flags, info)
+ let g:TagFunc1Args = [a:callnr, a:pat, a:flags, a:info]
+ return v:null
+ endfunc
+ func TagFunc2(pat, flags, info)
+ let g:TagFunc2Args = [a:pat, a:flags, a:info]
+ return v:null
+ endfunc
+
+ let lines =<< trim END
+ #" Test for using a function name
+ LET &tagfunc = 'g:TagFunc2'
+ new
+ LET g:TagFunc2Args = []
+ call assert_fails('tag a10', 'E433:')
+ call assert_equal(['a10', '', {}], g:TagFunc2Args)
+ bw!
+
+ #" Test for using a function()
+ set tagfunc=function('g:TagFunc1',\ [10])
+ new
+ LET g:TagFunc1Args = []
+ call assert_fails('tag a11', 'E433:')
+ call assert_equal([10, 'a11', '', {}], g:TagFunc1Args)
+ bw!
+
+ #" Using a funcref variable to set 'tagfunc'
+ VAR Fn = function('g:TagFunc1', [11])
+ LET &tagfunc = Fn
+ new
+ LET g:TagFunc1Args = []
+ call assert_fails('tag a12', 'E433:')
+ call assert_equal([11, 'a12', '', {}], g:TagFunc1Args)
+ bw!
+
+ #" Using a string(funcref_variable) to set 'tagfunc'
+ LET Fn = function('g:TagFunc1', [12])
+ LET &tagfunc = string(Fn)
+ new
+ LET g:TagFunc1Args = []
+ call assert_fails('tag a12', 'E433:')
+ call assert_equal([12, 'a12', '', {}], g:TagFunc1Args)
+ bw!
+
+ #" Test for using a funcref()
+ set tagfunc=funcref('g:TagFunc1',\ [13])
+ new
+ LET g:TagFunc1Args = []
+ call assert_fails('tag a13', 'E433:')
+ call assert_equal([13, 'a13', '', {}], g:TagFunc1Args)
+ bw!
+
+ #" Using a funcref variable to set 'tagfunc'
+ LET Fn = funcref('g:TagFunc1', [14])
+ LET &tagfunc = Fn
+ new
+ LET g:TagFunc1Args = []
+ call assert_fails('tag a14', 'E433:')
+ call assert_equal([14, 'a14', '', {}], g:TagFunc1Args)
+ bw!
+
+ #" Using a string(funcref_variable) to set 'tagfunc'
+ LET Fn = funcref('g:TagFunc1', [15])
+ LET &tagfunc = string(Fn)
+ new
+ LET g:TagFunc1Args = []
+ call assert_fails('tag a14', 'E433:')
+ call assert_equal([15, 'a14', '', {}], g:TagFunc1Args)
+ bw!
+
+ #" Test for using a lambda function
+ VAR optval = "LSTART a, b, c LMIDDLE TagFunc1(16, a, b, c) LEND"
+ LET optval = substitute(optval, ' ', '\\ ', 'g')
+ exe "set tagfunc=" .. optval
+ new
+ LET g:TagFunc1Args = []
+ call assert_fails('tag a17', 'E433:')
+ call assert_equal([16, 'a17', '', {}], g:TagFunc1Args)
+ bw!
+
+ #" Set 'tagfunc' to a lambda expression
+ LET &tagfunc = LSTART a, b, c LMIDDLE TagFunc1(17, a, b, c) LEND
+ new
+ LET g:TagFunc1Args = []
+ call assert_fails('tag a18', 'E433:')
+ call assert_equal([17, 'a18', '', {}], g:TagFunc1Args)
+ bw!
+
+ #" Set 'tagfunc' to a string(lambda expression)
+ LET &tagfunc = 'LSTART a, b, c LMIDDLE TagFunc1(18, a, b, c) LEND'
+ new
+ LET g:TagFunc1Args = []
+ call assert_fails('tag a18', 'E433:')
+ call assert_equal([18, 'a18', '', {}], g:TagFunc1Args)
+ bw!
+
+ #" Set 'tagfunc' to a variable with a lambda expression
+ VAR Lambda = LSTART a, b, c LMIDDLE TagFunc1(19, a, b, c) LEND
+ LET &tagfunc = Lambda
+ new
+ LET g:TagFunc1Args = []
+ call assert_fails("tag a19", "E433:")
+ call assert_equal([19, 'a19', '', {}], g:TagFunc1Args)
+ bw!
+
+ #" Set 'tagfunc' to a string(variable with a lambda expression)
+ LET Lambda = LSTART a, b, c LMIDDLE TagFunc1(20, a, b, c) LEND
+ LET &tagfunc = string(Lambda)
+ new
+ LET g:TagFunc1Args = []
+ call assert_fails("tag a19", "E433:")
+ call assert_equal([20, 'a19', '', {}], g:TagFunc1Args)
+ bw!
+
+ #" Test for using a lambda function with incorrect return value
+ LET Lambda = LSTART a, b, c LMIDDLE strlen(a) LEND
+ LET &tagfunc = string(Lambda)
+ new
+ call assert_fails("tag a20", "E987:")
+ bw!
+
+ #" Test for clearing the 'tagfunc' option
+ set tagfunc=''
+ set tagfunc&
+ call assert_fails("set tagfunc=function('abc')", "E700:")
+ call assert_fails("set tagfunc=funcref('abc')", "E700:")
+
+ #" set 'tagfunc' to a non-existing function
+ LET &tagfunc = function('g:TagFunc2', [21])
+ LET g:TagFunc2Args = []
+ call assert_fails("set tagfunc=function('NonExistingFunc')", 'E700:')
+ call assert_fails("LET &tagfunc = function('NonExistingFunc')", 'E700:')
+ call assert_fails("tag axb123", 'E426:')
+ call assert_equal([], g:TagFunc2Args)
+ bw!
+ END
+ call CheckLegacyAndVim9Success(lines)
+
+ " Test for using a script-local function name
+ func s:TagFunc3(pat, flags, info)
+ let g:TagFunc3Args = [a:pat, a:flags, a:info]
+ return v:null
+ endfunc
+ set tagfunc=s:TagFunc3
+ new
+ let g:TagFunc3Args = []
+ call assert_fails('tag a21', 'E433:')
+ call assert_equal(['a21', '', {}], g:TagFunc3Args)
+ bw!
+ let &tagfunc = 's:TagFunc3'
+ new
+ let g:TagFunc3Args = []
+ call assert_fails('tag a22', 'E433:')
+ call assert_equal(['a22', '', {}], g:TagFunc3Args)
+ bw!
+ delfunc s:TagFunc3
+
+ " invalid return value
+ let &tagfunc = "{a -> 'abc'}"
+ call assert_fails("echo taglist('a')", "E987:")
+
+ " Using Vim9 lambda expression in legacy context should fail
+ " set tagfunc=(a,\ b,\ c)\ =>\ g:TagFunc1(21,\ a,\ b,\ c)
+ new
+ let g:TagFunc1Args = []
+ " call assert_fails("tag a17", "E117:")
+ call assert_equal([], g:TagFunc1Args)
+ bw!
+
+ " Test for using a script local function
+ set tagfunc=<SID>ScriptLocalTagFunc
+ new
+ let g:ScriptLocalFuncArgs = []
+ call assert_fails('tag a15', 'E433:')
+ call assert_equal(['a15', '', {}], g:ScriptLocalFuncArgs)
+ bw!
+
+ " Test for using a script local funcref variable
+ let Fn = function("s:ScriptLocalTagFunc")
+ let &tagfunc= Fn
+ new
+ let g:ScriptLocalFuncArgs = []
+ call assert_fails('tag a16', 'E433:')
+ call assert_equal(['a16', '', {}], g:ScriptLocalFuncArgs)
+ bw!
+
+ " Test for using a string(script local funcref variable)
+ let Fn = function("s:ScriptLocalTagFunc")
+ let &tagfunc= string(Fn)
+ new
+ let g:ScriptLocalFuncArgs = []
+ call assert_fails('tag a16', 'E433:')
+ call assert_equal(['a16', '', {}], g:ScriptLocalFuncArgs)
+ bw!
+
+ " set 'tagfunc' to a partial with dict. This used to cause a crash.
+ func SetTagFunc()
+ let params = {'tagfn': function('g:DictTagFunc')}
+ let &tagfunc = params.tagfn
+ endfunc
+ func g:DictTagFunc(_) dict
+ endfunc
+ call SetTagFunc()
+ new
+ call SetTagFunc()
+ bw
+ call test_garbagecollect_now()
+ new
+ set tagfunc=
+ wincmd w
+ set tagfunc=
+ :%bw!
+ delfunc g:DictTagFunc
+ delfunc SetTagFunc
+
+ " Vim9 tests
+ let lines =<< trim END
+ vim9script
+
+ def Vim9tagFunc(callnr: number, pat: string, flags: string, info: dict<any>): any
+ g:Vim9tagFuncArgs = [callnr, pat, flags, info]
+ return null
+ enddef
+
+ # Test for using a def function with completefunc
+ set tagfunc=function('Vim9tagFunc',\ [60])
+ new
+ g:Vim9tagFuncArgs = []
+ assert_fails('tag a10', 'E433:')
+ assert_equal([60, 'a10', '', {}], g:Vim9tagFuncArgs)
+
+ # Test for using a global function name
+ &tagfunc = g:TagFunc2
+ new
+ g:TagFunc2Args = []
+ assert_fails('tag a11', 'E433:')
+ assert_equal(['a11', '', {}], g:TagFunc2Args)
+ bw!
+
+ # Test for using a script-local function name
+ def s:LocalTagFunc(pat: string, flags: string, info: dict<any> ): any
+ g:LocalTagFuncArgs = [pat, flags, info]
+ return null
+ enddef
+ &tagfunc = s:LocalTagFunc
+ new
+ g:LocalTagFuncArgs = []
+ assert_fails('tag a12', 'E433:')
+ assert_equal(['a12', '', {}], g:LocalTagFuncArgs)
+ bw!
+ END
+ call CheckScriptSuccess(lines)
+
+ " cleanup
+ delfunc TagFunc1
+ delfunc TagFunc2
+ set tagfunc&
+ %bw!
+endfunc
+
+func Test_tagfunc_wipes_buffer()
+ func g:Tag0unc0(t,f,o)
+ bwipe
+ endfunc
+ set tagfunc=g:Tag0unc0
+ new
+ cal assert_fails('tag 0', 'E987:')
+
+ delfunc g:Tag0unc0
+ set tagfunc=
+endfunc
+
+func Test_tagfunc_closes_window()
+ split any
+ func MytagfuncClose(pat, flags, info)
+ close
+ return [{'name' : 'mytag', 'filename' : 'Xtest', 'cmd' : '1'}]
+ endfunc
+ set tagfunc=MytagfuncClose
+ call assert_fails('tag xyz', 'E1299:')
+
+ set tagfunc=
+endfunc
+
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim
index 04c0218f74..be60a3535c 100644
--- a/src/nvim/testdir/test_tagjump.vim
+++ b/src/nvim/testdir/test_tagjump.vim
@@ -8,7 +8,7 @@ func Test_ptag_with_notagstack()
CheckFeature quickfix
set notagstack
- call assert_fails('ptag does_not_exist_tag_name', 'E426')
+ call assert_fails('ptag does_not_exist_tag_name', 'E433')
set tagstack&vim
endfunc
@@ -184,6 +184,10 @@ function Test_keyword_jump()
call search("start")
exe "normal! 5\<C-W>\<C-I>"
call assert_equal(" start OK if found this line", getline('.'))
+
+ " invalid tag search pattern
+ call assert_fails('tag /\%(/', 'E426:')
+
enew! | only
call delete('Xtestfile')
call delete('Xinclude')
@@ -227,15 +231,13 @@ func Test_tag_symbolic()
endfunc
" Tests for tag search with !_TAG_FILE_ENCODING.
-" Depends on the test83-tags2 and test83-tags3 files.
func Test_tag_file_encoding()
- throw 'skipped: Nvim removed test83-tags2, test83-tags3'
if has('vms')
- return
+ throw 'Skipped: does not work on VMS'
endif
if !has('iconv') || iconv("\x82\x60", "cp932", "utf-8") != "\uff21"
- return
+ throw 'Skipped: iconv does not work'
endif
let save_enc = &encoding
@@ -260,18 +262,31 @@ func Test_tag_file_encoding()
" case2:
new
- set tags=test83-tags2
+ let content = ['!_TAG_FILE_ENCODING cp932 //',
+ \ "\x82`\x82a\x82b Xtags2.txt /\x82`\x82a\x82b"]
+ call writefile(content, 'Xtags')
+ set tags=Xtags
tag /.BC
call assert_equal('Xtags2.txt', expand('%:t'))
call assert_equal('ABC', getline('.'))
+ call delete('Xtags')
close
" case3:
new
- set tags=test83-tags3
+ let contents = [
+ \ "!_TAG_FILE_SORTED 1 //",
+ \ "!_TAG_FILE_ENCODING cp932 //"]
+ for i in range(1, 100)
+ call add(contents, 'abc' .. i
+ \ .. " Xtags3.txt /\x82`\x82a\x82b")
+ endfor
+ call writefile(contents, 'Xtags')
+ set tags=Xtags
tag abc50
call assert_equal('Xtags3.txt', expand('%:t'))
call assert_equal('ABC', getline('.'))
+ call delete('Xtags')
close
set tags&
@@ -282,6 +297,7 @@ func Test_tag_file_encoding()
call delete('Xtags1')
endfunc
+" Test for emacs-style tags file (TAGS)
func Test_tagjump_etags()
if !has('emacs_tags')
return
@@ -322,6 +338,7 @@ func Test_tagjump_etags()
\ ], 'Xtags2')
tag main
call assert_equal(2, line('.'))
+ call assert_fails('tag bar', 'E426:')
" corrupted tag line
call writefile([
@@ -345,7 +362,28 @@ func Test_tagjump_etags()
\ "Xmain.c,64",
\ ";;;;\x7f1,0",
\ ], 'Xtags')
- call assert_fails('tag foo', 'E426:')
+ call assert_fails('tag foo', 'E431:')
+
+ " end of file after a CTRL-L line
+ call writefile([
+ \ "\x0c",
+ \ "Xmain.c,64",
+ \ "void foo() {}\x7ffoo\x011,0",
+ \ "\x0c",
+ \ ], 'Xtags')
+ call assert_fails('tag main', 'E426:')
+
+ " error in an included tags file
+ call writefile([
+ \ "\x0c",
+ \ "Xtags2,include"
+ \ ], 'Xtags')
+ call writefile([
+ \ "\x0c",
+ \ "Xmain.c,64",
+ \ "void foo() {}",
+ \ ], 'Xtags2')
+ call assert_fails('tag foo', 'E431:')
call delete('Xtags')
call delete('Xtags2')
@@ -371,6 +409,7 @@ func Test_getsettagstack()
call assert_fails("call settagstack(1, {'items' : 10})", 'E714')
call assert_fails("call settagstack(1, {'items' : []}, 10)", 'E928')
call assert_fails("call settagstack(1, {'items' : []}, 'b')", 'E962')
+ call assert_equal(-1, settagstack(0, v:_null_dict))
set tags=Xtags
call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
@@ -766,11 +805,11 @@ endfunc
" Test for an unsorted tags file
func Test_tag_sort()
- call writefile([
+ let l = [
\ "first\tXfoo\t1",
\ "ten\tXfoo\t3",
- \ "six\tXfoo\t2"],
- \ 'Xtags')
+ \ "six\tXfoo\t2"]
+ call writefile(l, 'Xtags')
set tags=Xtags
let code =<< trim [CODE]
int first() {}
@@ -781,7 +820,14 @@ func Test_tag_sort()
call assert_fails('tag first', 'E432:')
+ " When multiple tag files are not sorted, then message should be displayed
+ " multiple times
+ call writefile(l, 'Xtags2')
+ set tags=Xtags,Xtags2
+ call assert_fails('tag first', ['E432:', 'E432:'])
+
call delete('Xtags')
+ call delete('Xtags2')
call delete('Xfoo')
set tags&
%bwipe
@@ -1099,7 +1145,7 @@ 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:"],
+ \ "first\tXfoo\t2" .. ';"' .. "\tkind:v\ttyperef:typename:char\tfile:"],
\ 'Xtags')
set tags=Xtags
@@ -1422,4 +1468,148 @@ func Test_tag_length()
set tags& taglength&
endfunc
+" Tests for errors in a tags file
+func Test_tagfile_errors()
+ set tags=Xtags
+
+ " missing search pattern or line number for a tag
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "foo\tXfile\t"], 'Xtags', 'b')
+ call writefile(['foo'], 'Xfile')
+
+ enew
+ tag foo
+ call assert_equal('', @%)
+ let caught_431 = v:false
+ try
+ eval taglist('.*')
+ catch /:E431:/
+ let caught_431 = v:true
+ endtry
+ call assert_equal(v:true, caught_431)
+
+ " tag name and file name are not separated by a tab
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "foo Xfile 1"], 'Xtags')
+ call assert_fails('tag foo', 'E431:')
+
+ " file name and search pattern are not separated by a tab
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "foo\tXfile 1;"], 'Xtags')
+ call assert_fails('tag foo', 'E431:')
+
+ call delete('Xtags')
+ call delete('Xfile')
+ set tags&
+endfunc
+
+" When :stag fails to open the file, should close the new window
+func Test_stag_close_window_on_error()
+ new | only
+ set tags=Xtags
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "foo\tXfile\t1"], 'Xtags')
+ call writefile(['foo'], 'Xfile')
+ call writefile([], '.Xfile.swp')
+ " Remove the catch-all that runtest.vim adds
+ au! SwapExists
+ augroup StagTest
+ au!
+ autocmd SwapExists Xfile let v:swapchoice='q'
+ augroup END
+
+ stag foo
+ call assert_equal(1, winnr('$'))
+ call assert_equal('', @%)
+
+ augroup StagTest
+ au!
+ augroup END
+ call delete('Xfile')
+ call delete('.Xfile.swp')
+ set tags&
+endfunc
+
+" Test for 'tagbsearch' (binary search)
+func Test_tagbsearch()
+ " If a tags file header says the tags are sorted, but the tags are actually
+ " unsorted, then binary search should fail and linear search should work.
+ call writefile([
+ \ "!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "!_TAG_FILE_SORTED\t1\t/0=unsorted, 1=sorted, 2=foldcase/",
+ \ "third\tXfoo\t3",
+ \ "second\tXfoo\t2",
+ \ "first\tXfoo\t1"],
+ \ 'Xtags')
+ set tags=Xtags
+ let code =<< trim [CODE]
+ int first() {}
+ int second() {}
+ int third() {}
+ [CODE]
+ call writefile(code, 'Xfoo')
+
+ enew
+ set tagbsearch
+ call assert_fails('tag first', 'E426:')
+ call assert_equal('', bufname())
+ call assert_fails('tag second', 'E426:')
+ call assert_equal('', bufname())
+ tag third
+ call assert_equal('Xfoo', bufname())
+ call assert_equal(3, line('.'))
+ %bw!
+
+ set notagbsearch
+ tag first
+ call assert_equal('Xfoo', bufname())
+ call assert_equal(1, line('.'))
+ enew
+ tag second
+ call assert_equal('Xfoo', bufname())
+ call assert_equal(2, line('.'))
+ enew
+ tag third
+ call assert_equal('Xfoo', bufname())
+ call assert_equal(3, line('.'))
+ %bw!
+
+ " If a tags file header says the tags are unsorted, but the tags are
+ " actually sorted, then binary search should work.
+ call writefile([
+ \ "!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "!_TAG_FILE_SORTED\t0\t/0=unsorted, 1=sorted, 2=foldcase/",
+ \ "first\tXfoo\t1",
+ \ "second\tXfoo\t2",
+ \ "third\tXfoo\t3"],
+ \ 'Xtags')
+
+ set tagbsearch
+ tag first
+ call assert_equal('Xfoo', bufname())
+ call assert_equal(1, line('.'))
+ enew
+ tag second
+ call assert_equal('Xfoo', bufname())
+ call assert_equal(2, line('.'))
+ enew
+ tag third
+ call assert_equal('Xfoo', bufname())
+ call assert_equal(3, line('.'))
+ %bw!
+
+ " Binary search fails on EOF
+ call writefile([
+ \ "!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "!_TAG_FILE_SORTED\t1\t/0=unsorted, 1=sorted, 2=foldcase/",
+ \ "bar\tXfoo\t1",
+ \ "foo\tXfoo\t2"],
+ \ 'Xtags')
+ call assert_fails('tag bbb', 'E426:')
+
+ call delete('Xtags')
+ call delete('Xfoo')
+ set tags& tagbsearch&
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_taglist.vim b/src/nvim/testdir/test_taglist.vim
index be46773256..0387ef2bd8 100644
--- a/src/nvim/testdir/test_taglist.vim
+++ b/src/nvim/testdir/test_taglist.vim
@@ -36,6 +36,14 @@ func Test_taglist()
call assert_equal('d', cmd[0]['kind'])
call assert_equal('call cursor(3, 4)', cmd[0]['cmd'])
+ " Use characters with value > 127 in the tag extra field.
+ call writefile([
+ \ "vFoo\tXfoo\t4" .. ';"' .. "\ttypename:int\ta£££\tv",
+ \ ], 'Xtags')
+ call assert_equal('v', taglist('vFoo')[0].kind)
+
+ call assert_fails("let l=taglist([])", 'E730:')
+
call delete('Xtags')
set tags&
bwipe
@@ -82,13 +90,11 @@ func Test_taglist_ctags_etags()
endfunc
func Test_tags_too_long()
- call assert_fails('tag ' . repeat('x', 1020), 'E426')
+ call assert_fails('tag ' . repeat('x', 1020), ['E433', 'E426'])
tags
endfunc
func Test_tagfiles()
- " Nvim: different default for 'tags'.
- set tags=./tags,tags
call assert_equal([], tagfiles())
call writefile(["FFoo\tXfoo\t1"], 'Xtags1')
@@ -221,6 +227,11 @@ func Test_format_error()
endtry
call assert_true(caught_exception)
+ " no field after the filename for a tag
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "foo\tXfile"], 'Xtags')
+ call assert_fails("echo taglist('foo')", 'E431:')
+
set tags&
call delete('Xtags')
endfunc
@@ -241,4 +252,30 @@ func Test_tag_complete_wildoptions()
set tags&
endfunc
+func Test_tag_complete_with_overlong_line()
+ let tagslines =<< trim END
+ !_TAG_FILE_FORMAT 2 //
+ !_TAG_FILE_SORTED 1 //
+ !_TAG_FILE_ENCODING utf-8 //
+ inboundGSV a 1;" r
+ inboundGovernor a 2;" kind:⊢ type:forall (muxMode :: MuxMode) socket peerAddr versionNumber m a b. (MonadAsync m, MonadCatch m, MonadEvaluate m, MonadThrow m, MonadThrow (STM m), MonadTime m, MonadTimer m, MonadMask m, Ord peerAddr, HasResponder muxMode ~ True) => Tracer m (RemoteTransitionTrace peerAddr) -> Tracer m (InboundGovernorTrace peerAddr) -> ServerControlChannel muxMode peerAddr ByteString m a b -> DiffTime -> MuxConnectionManager muxMode socket peerAddr versionNumber ByteString m a b -> StrictTVar m InboundGovernorObservableState -> m Void
+ inboundGovernorCounters a 3;" kind:⊢ type:InboundGovernorState muxMode peerAddr m a b -> InboundGovernorCounters
+ END
+ call writefile(tagslines, 'Xtags')
+ set tags=Xtags
+
+ " try with binary search
+ set tagbsearch
+ call feedkeys(":tag inbou\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"tag inboundGSV inboundGovernor inboundGovernorCounters', @:)
+ " try with linear search
+ set notagbsearch
+ call feedkeys(":tag inbou\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"tag inboundGSV inboundGovernor inboundGovernorCounters', @:)
+ set tagbsearch&
+
+ call delete('Xtags')
+ 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
index eda485c512..99bc2d1d37 100644
--- a/src/nvim/testdir/test_termcodes.vim
+++ b/src/nvim/testdir/test_termcodes.vim
@@ -1,4 +1,30 @@
+" Test for translation of special key codes (<xF1>, <xF2>, etc.)
+func Test_Keycode_Translation()
+ let keycodes = [
+ \ ["<xUp>", "<Up>"],
+ \ ["<xDown>", "<Down>"],
+ \ ["<xLeft>", "<Left>"],
+ \ ["<xRight>", "<Right>"],
+ \ ["<xHome>", "<Home>"],
+ \ ["<xEnd>", "<End>"],
+ \ ["<zHome>", "<Home>"],
+ \ ["<zEnd>", "<End>"],
+ \ ["<xF1>", "<F1>"],
+ \ ["<xF2>", "<F2>"],
+ \ ["<xF3>", "<F3>"],
+ \ ["<xF4>", "<F4>"],
+ \ ["<S-xF1>", "<S-F1>"],
+ \ ["<S-xF2>", "<S-F2>"],
+ \ ["<S-xF3>", "<S-F3>"],
+ \ ["<S-xF4>", "<S-F4>"]]
+ for [k1, k2] in keycodes
+ exe "nnoremap " .. k1 .. " 2wx"
+ call assert_true(maparg(k1, 'n', 0, 1).lhs == k2)
+ exe "nunmap " .. k1
+ endfor
+endfunc
+
" Test for terminal keycodes that doesn't have termcap entries
func Test_special_term_keycodes()
new
diff --git a/src/nvim/testdir/test_textformat.vim b/src/nvim/testdir/test_textformat.vim
index 0fc56083aa..7a13de12f4 100644
--- a/src/nvim/testdir/test_textformat.vim
+++ b/src/nvim/testdir/test_textformat.vim
@@ -962,78 +962,6 @@ func Test_tw_2_fo_tm_noai()
bwipe!
endfunc
-func Test_tw_2_fo_cqm_com()
- new
- let t =<< trim END
- {
- X
- Xa
- XaY
- XY
- XYZ
- X Y
- X YZ
- XX
- XXa
- XXY
- }
- END
- call setline(1, t)
- call cursor(2, 1)
-
- set tw=2 fo=cqm comments=n:X
- exe "normal gqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgq"
- let t =<< trim END
- X
- Xa
- XaY
- XY
- XYZ
- X Y
- X YZ
- XX
- XXa
- XXY
- END
- exe "normal o\n" . join(t, "\n")
-
- let expected =<< trim END
- {
- X
- Xa
- Xa
- XY
- XY
- XY
- XZ
- X Y
- X Y
- X Z
- XX
- XXa
- XXY
-
- X
- Xa
- Xa
- XY
- XY
- XY
- XZ
- X Y
- X Y
- X Z
- XX
- XXa
- XXY
- }
- END
- call assert_equal(expected, getline(1, '$'))
-
- set tw& fo& comments&
- bwipe!
-endfunc
-
func Test_tw_2_fo_tm_replace()
new
let t =<< trim END
@@ -1116,6 +1044,22 @@ func Test_empty_matchpairs()
bwipe!
endfunc
+func Test_mps_error()
+ let encoding_save = &encoding
+
+ " for e in ['utf-8', 'latin1']
+ for e in ['utf-8']
+ exe 'set encoding=' .. e
+
+ call assert_fails('set mps=<:', 'E474:', e)
+ call assert_fails('set mps=:>', 'E474:', e)
+ call assert_fails('set mps=<>', 'E474:', e)
+ call assert_fails('set mps=<:>_', 'E474:', e)
+ endfor
+
+ let &encoding = encoding_save
+endfunc
+
" Test for ra on multi-byte characters
func Test_ra_multibyte()
new
@@ -1161,140 +1105,6 @@ func Test_whichwrap_multi_byte()
bwipe!
endfunc
-" 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 space character in 'comments' setting
-func Test_comment_space()
- new
- setlocal comments=b:\ > fo+=ro
- exe "normal i> B\nD\<C-C>ggOA\<C-C>joC"
- exe "normal Go > F\nH\<C-C>kOE\<C-C>joG"
- let expected =<< trim END
- A
- > B
- C
- D
- > E
- > F
- > G
- > H
- END
- call assert_equal(expected, getline(1, '$'))
- %bw!
-endfunc
-
-" Test for the 'O' flag in 'comments'
-func Test_comment_O()
- new
- setlocal comments=Ob:* fo+=ro
- exe "normal i* B\nD\<C-C>kOA\<C-C>joC"
- let expected =<< trim END
- A
- * B
- * C
- * D
- END
- call assert_equal(expected, getline(1, '$'))
- %bw!
-endfunc
-
" Test for 'a' and 'w' flags in 'formatoptions'
func Test_fo_a_w()
new
@@ -1302,6 +1112,20 @@ func Test_fo_a_w()
call feedkeys("iabc abc a abc\<Esc>k0weade", 'xt')
call assert_equal(['abc abcde ', 'a abc'], getline(1, '$'))
+ " when a line ends with space, it is not broken up.
+ %d
+ call feedkeys("ione two to ", 'xt')
+ call assert_equal('one two to ', getline(1))
+
+ " when a line ends with spaces and backspace is used in the next line, the
+ " last space in the previous line should be removed.
+ %d
+ set backspace=indent,eol,start
+ call setline(1, ['one ', 'two'])
+ exe "normal 2Gi\<BS>"
+ call assert_equal(['one two'], getline(1, '$'))
+ set backspace&
+
" Test for 'a', 'w' and '1' options.
setlocal textwidth=0
setlocal fo=1aw
@@ -1334,25 +1158,6 @@ func Test_fo_a_w()
%bw!
endfunc
-" Test for 'j' flag in 'formatoptions'
-func Test_fo_j()
- new
- setlocal fo+=j comments=://
- call setline(1, ['i++; // comment1', ' // comment2'])
- normal J
- call assert_equal('i++; // comment1 comment2', getline(1))
- setlocal fo-=j
- call setline(1, ['i++; // comment1', ' // comment2'])
- normal J
- call assert_equal('i++; // comment1 // comment2', getline(1))
- " Test with nested comments
- setlocal fo+=j comments=n:>,n:)
- call setline(1, ['i++; > ) > ) comment1', ' > ) comment2'])
- normal J
- call assert_equal('i++; > ) > ) comment1 comment2', getline(1))
- %bw!
-endfunc
-
" Test for formatting lines using gq in visual mode
func Test_visual_gq_format()
new
@@ -1487,53 +1292,6 @@ func Test_fo_2()
close!
endfunc
-" Test for formatting lines where only the first line has a comment.
-func Test_fo_gq_with_firstline_comment()
- new
- setlocal formatoptions=tcq
- call setline(1, ['- one two', 'three'])
- normal gggqG
- call assert_equal(['- one two three'], getline(1, '$'))
-
- %d
- call setline(1, ['- one', '- two'])
- normal gggqG
- call assert_equal(['- one', '- two'], getline(1, '$'))
- close!
-endfunc
-
-" Test for trying to join a comment line with a non-comment line
-func Test_join_comments()
- new
- call setline(1, ['one', '/* two */', 'three'])
- normal gggqG
- call assert_equal(['one', '/* two */', 'three'], getline(1, '$'))
- close!
-endfunc
-
-" Test for using 'a' in 'formatoptions' with comments
-func Test_autoformat_comments()
- new
- setlocal formatoptions+=a
- call feedkeys("a- one\n- two\n", 'xt')
- call assert_equal(['- one', '- two', ''], getline(1, '$'))
-
- %d
- call feedkeys("a\none\n", 'xt')
- call assert_equal(['', 'one', ''], getline(1, '$'))
-
- setlocal formatoptions+=aw
- %d
- call feedkeys("aone \ntwo\n", 'xt')
- call assert_equal(['one two', ''], getline(1, '$'))
-
- %d
- call feedkeys("aone\ntwo\n", 'xt')
- call assert_equal(['one', 'two', ''], getline(1, '$'))
-
- close!
-endfunc
-
" This was leaving the cursor after the end of a line. Complicated way to
" have the problem show up with valgrind.
func Test_correct_cursor_position()
diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim
index 6adf503f14..f94ee6c9f3 100644
--- a/src/nvim/testdir/test_timers.vim
+++ b/src/nvim/testdir/test_timers.vim
@@ -3,6 +3,7 @@
source check.vim
CheckFeature timers
+source screendump.vim
source shared.vim
source term_util.vim
source load.vim
@@ -46,9 +47,6 @@ endfunc
func Test_timer_repeat_many()
let g:val = 0
let timer = timer_start(50, 'MyHandler', {'repeat': -1})
- if has('mac')
- sleep 200m
- endif
sleep 200m
call timer_stop(timer)
call assert_inrange((has('mac') ? 1 : 2), LoadAdjust(5), g:val)
@@ -266,8 +264,9 @@ endfunc
func Test_timer_peek_and_get_char()
if !has('unix') && !has('gui_running')
- return
+ throw 'Skipped: cannot feed low-level input'
endif
+
call timer_start(0, 'FeedAndPeek')
let intr = timer_start(100, 'Interrupt')
let c = getchar()
@@ -277,9 +276,9 @@ endfunc
func Test_timer_getchar_zero()
if has('win32') && !has('gui_running')
- " Console: no low-level input
- return
+ throw 'Skipped: cannot feed low-level input'
endif
+ CheckFunction reltimefloat
" Measure the elapsed time to avoid a hang when it fails.
let start = reltime()
@@ -305,9 +304,7 @@ func Test_timer_ex_mode()
endfunc
func Test_timer_restore_count()
- if !CanRunVimInTerminal()
- throw 'Skipped: cannot run Vim in a terminal window'
- endif
+ CheckRunVimInTerminal
" Check that v:count is saved and restored, not changed by a timer.
call writefile([
\ 'nnoremap <expr><silent> L v:count ? v:count . "l" : "l"',
@@ -349,11 +346,13 @@ func Test_nocatch_timer_garbage_collect()
let a = {'foo', 'bar'}
endfunc
func FeedChar(id)
- call feedkeys('x', 't')
+ call feedkeys(":\<CR>", 't')
endfunc
call timer_start(300, 'FeedChar')
call timer_start(100, 'CauseAnError')
- let x = getchar()
+ let x = getchar() " wait for error in timer
+ let x = getchar(0) " read any remaining chars
+ let x = getchar(0)
set ut&
call test_override('no_wait_return', 1)
@@ -409,6 +408,30 @@ func Test_timer_invalid_callback()
call assert_fails('call timer_start(0, "0")', 'E921')
endfunc
+func Test_timer_changing_function_list()
+ CheckRunVimInTerminal
+
+ " Create a large number of functions. Should get the "more" prompt.
+ " The typing "G" triggers the timer, which changes the function table.
+ let lines =<< trim END
+ for func in map(range(1,99), "'Func' .. v:val")
+ exe "func " .. func .. "()"
+ endfunc
+ endfor
+ au CmdlineLeave : call timer_start(0, {-> 0})
+ END
+ call writefile(lines, 'XTest_timerchange')
+ let buf = RunVimInTerminal('-S XTest_timerchange', #{rows: 10})
+ call term_sendkeys(buf, ":fu\<CR>")
+ call WaitForAssert({-> assert_match('-- More --', term_getline(buf, 10))})
+ call term_sendkeys(buf, "G")
+ call WaitForAssert({-> assert_match('E454', term_getline(buf, 9))})
+ call term_sendkeys(buf, "\<Esc>")
+
+ call StopVimInTerminal(buf)
+ call delete('XTest_timerchange')
+endfunc
+
func Test_timer_using_win_execute_undo_sync()
let bufnr1 = bufnr()
new
diff --git a/src/nvim/testdir/test_trycatch.vim b/src/nvim/testdir/test_trycatch.vim
index 646594e482..ef20e03126 100644
--- a/src/nvim/testdir/test_trycatch.vim
+++ b/src/nvim/testdir/test_trycatch.vim
@@ -3,6 +3,7 @@
source check.vim
source shared.vim
+source vim9.vim
"-------------------------------------------------------------------------------
" Test environment {{{1
@@ -2000,15 +2001,34 @@ endfunc
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('catch abc', 'E654:')
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 | for i in range(5) | endif | endtry', 'E580:')
call assert_fails('try | while v:true | endtry', 'E170:')
call assert_fails('try | if v:true | endtry', 'E171:')
+
+ " this was using a negative index in cstack[]
+ let lines =<< trim END
+ try
+ for
+ if
+ endwhile
+ if
+ finally
+ END
+ call CheckScriptFailure(lines, 'E690:')
+
+ let lines =<< trim END
+ try
+ for
+ if
+ endwhile
+ if
+ endtry
+ END
+ call CheckScriptFailure(lines, 'E690:')
endfunc
" Test for verbose messages with :try :catch, and :finally {{{1
@@ -2027,16 +2047,179 @@ func Test_try_catch_verbose()
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'
- \ ]
+ \ '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"))
+
+ " Test for verbose messages displayed when an exception is discarded
+ redir => msg
+ try
+ try
+ throw 'abc'
+ finally
+ throw 'xyz'
+ endtry
+ catch
+ endtry
+ redir END
+ let expected = [
+ \ 'Exception thrown: abc', '',
+ \ 'Exception made pending: abc', '',
+ \ 'Exception thrown: xyz', '',
+ \ 'Exception discarded: abc', '',
+ \ 'Exception caught: xyz', '',
+ \ 'Exception finished: xyz']
+ call assert_equal(expected, split(msg, "\n"))
+
+ " Test for messages displayed when :throw is resumed after :finally
+ redir => msg
+ try
+ try
+ throw 'abc'
+ finally
+ endtry
+ catch
+ endtry
+ redir END
+ let expected = [
+ \ 'Exception thrown: abc', '',
+ \ 'Exception made pending: abc', '',
+ \ 'Exception resumed: abc', '',
+ \ 'Exception caught: abc', '',
+ \ 'Exception finished: abc']
+ call assert_equal(expected, split(msg, "\n"))
+
+ " Test for messages displayed when :break is resumed after :finally
+ redir => msg
+ for i in range(1)
+ try
+ break
+ finally
+ endtry
+ endfor
+ redir END
+ let expected = [':break made pending', '', ':break resumed']
call assert_equal(expected, split(msg, "\n"))
+
+ " Test for messages displayed when :continue is resumed after :finally
+ redir => msg
+ for i in range(1)
+ try
+ continue
+ finally
+ endtry
+ endfor
+ redir END
+ let expected = [':continue made pending', '', ':continue resumed']
+ call assert_equal(expected, split(msg, "\n"))
+
+ " Test for messages displayed when :return is resumed after :finally
+ func Xtest()
+ try
+ return 'vim'
+ finally
+ endtry
+ endfunc
+ redir => msg
+ call Xtest()
+ redir END
+ let expected = [
+ \ 'calling Xtest()', '',
+ \ ':return vim made pending', '',
+ \ ':return vim resumed', '',
+ \ 'Xtest returning ''vim''', '',
+ \ 'continuing in Test_try_catch_verbose']
+ call assert_equal(expected, split(msg, "\n"))
+ delfunc Xtest
+
+ " Test for messages displayed when :finish is resumed after :finally
+ call writefile(['try', 'finish', 'finally', 'endtry'], 'Xscript')
+ redir => msg
+ source Xscript
+ redir END
+ let expected = [
+ \ ':finish made pending', '',
+ \ ':finish resumed', '',
+ \ 'finished sourcing Xscript',
+ \ 'continuing in Test_try_catch_verbose']
+ call assert_equal(expected, split(msg, "\n")[1:])
+ call delete('Xscript')
+
+ " Test for messages displayed when a pending :continue is discarded by an
+ " exception in a finally handler
+ redir => msg
+ try
+ for i in range(1)
+ try
+ continue
+ finally
+ throw 'abc'
+ endtry
+ endfor
+ catch
+ endtry
+ redir END
+ let expected = [
+ \ ':continue made pending', '',
+ \ 'Exception thrown: abc', '',
+ \ ':continue discarded', '',
+ \ 'Exception caught: abc', '',
+ \ 'Exception finished: abc']
+ call assert_equal(expected, split(msg, "\n"))
+
set verbose&
endfunc
+" Test for throwing an exception from a BufEnter autocmd {{{1
+func Test_BufEnter_exception()
+ augroup bufenter_exception
+ au!
+ autocmd BufEnter Xfile1 throw 'abc'
+ augroup END
+
+ let caught_abc = 0
+ try
+ sp Xfile1
+ catch /^abc/
+ let caught_abc = 1
+ endtry
+ call assert_equal(1, caught_abc)
+ call assert_equal(1, winnr('$'))
+
+ augroup bufenter_exception
+ au!
+ augroup END
+ augroup! bufenter_exception
+ %bwipe!
+
+ " Test for recursively throwing exceptions in autocmds
+ augroup bufenter_exception
+ au!
+ autocmd BufEnter Xfile1 throw 'bufenter'
+ autocmd BufLeave Xfile1 throw 'bufleave'
+ augroup END
+
+ let ex_count = 0
+ try
+ try
+ sp Xfile1
+ catch /^bufenter/
+ let ex_count += 1
+ endtry
+ catch /^bufleave/
+ let ex_count += 10
+ endtry
+ call assert_equal(10, ex_count)
+ call assert_equal(2, winnr('$'))
+
+ augroup bufenter_exception
+ au!
+ augroup END
+ augroup! bufenter_exception
+ %bwipe!
+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
@@ -2060,6 +2243,23 @@ func Test_user_command_throw_in_function_call()
unlet g:caught
endfunc
+" Test that after reporting an uncaught exception there is no error for a
+" missing :endif
+func Test_after_exception_no_endif_error()
+ function Throw()
+ throw "Failure"
+ endfunction
+
+ function Foo()
+ if 1
+ call Throw()
+ endif
+ endfunction
+ call assert_fails('call Foo()', ['E605:', 'E605:'])
+ delfunc Throw
+ delfunc Foo
+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
diff --git a/src/nvim/testdir/test_undo.vim b/src/nvim/testdir/test_undo.vim
index eb47af08d7..ee8b52caaf 100644
--- a/src/nvim/testdir/test_undo.vim
+++ b/src/nvim/testdir/test_undo.vim
@@ -3,6 +3,9 @@
" undo-able pieces. Do that by setting 'undolevels'.
" Also tests :earlier and :later.
+source check.vim
+source screendump.vim
+
func Test_undotree()
new
@@ -773,4 +776,30 @@ func Test_undo_mark()
bwipe!
endfunc
+func Test_undo_after_write()
+ " use a terminal to make undo work like when text is typed
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ edit Xtestfile.txt
+ set undolevels=100 undofile
+ imap . <Cmd>write<CR>
+ write
+ END
+ call writefile(lines, 'Xtest_undo_after_write', 'D')
+ let buf = RunVimInTerminal('-S Xtest_undo_after_write', #{rows: 6})
+
+ call term_sendkeys(buf, "Otest.\<CR>boo!!!\<Esc>")
+ sleep 100m
+ call term_sendkeys(buf, "u")
+ call VerifyScreenDump(buf, 'Test_undo_after_write_1', {})
+
+ call term_sendkeys(buf, "u")
+ call VerifyScreenDump(buf, 'Test_undo_after_write_2', {})
+
+ call StopVimInTerminal(buf)
+ call delete('Xtestfile.txt')
+endfunc
+
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_unlet.vim b/src/nvim/testdir/test_unlet.vim
index b02bdaab3b..4779d17906 100644
--- a/src/nvim/testdir/test_unlet.vim
+++ b/src/nvim/testdir/test_unlet.vim
@@ -1,12 +1,9 @@
" Tests for :unlet
func Test_read_only()
- try
- " this caused a crash
- unlet v:count
- catch
- call assert_true(v:exception =~ ':E795:')
- endtry
+ " these caused a crash
+ call assert_fails('unlet v:count', 'E795:')
+ call assert_fails('unlet v:errmsg', 'E795:')
endfunc
func Test_existing()
@@ -18,15 +15,19 @@ endfunc
func Test_not_existing()
unlet! does_not_exist
- try
- unlet does_not_exist
- catch
- call assert_true(v:exception =~ ':E108:')
- endtry
+ call assert_fails('unlet does_not_exist', 'E108:')
endfunc
func Test_unlet_fails()
call assert_fails('unlet v:["count"]', 'E46:')
+ call assert_fails('unlet $', 'E475:')
+ let v = {}
+ call assert_fails('unlet v[:]', 'E719:')
+ let l = []
+ call assert_fails("unlet l['k'", 'E111:')
+ let d = {'k' : 1}
+ call assert_fails("unlet d.k2", 'E716:')
+ call assert_fails("unlet {a};", 'E488:')
endfunc
func Test_unlet_env()
@@ -62,3 +63,5 @@ func Test_unlet_complete()
call feedkeys(":unlet $FOO\t\n", 'tx')
call assert_true(!exists('$FOOBAR') || empty($FOOBAR))
endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_user_func.vim b/src/nvim/testdir/test_user_func.vim
index 5231ef7b4f..4742293ed5 100644
--- a/src/nvim/testdir/test_user_func.vim
+++ b/src/nvim/testdir/test_user_func.vim
@@ -3,6 +3,9 @@
" Also test that a builtin function cannot be replaced.
" Also test for regression when calling arbitrary expression.
+source check.vim
+source shared.vim
+
func Table(title, ...)
let ret = a:title
let idx = 1
@@ -83,10 +86,14 @@ func Test_user_func()
normal o[(one again
call assert_equal('1. one again', getline('.'))
+ " Try to overwrite a function in the global (g:) scope
call assert_equal(3, max([1, 2, 3]))
call assert_fails("call extend(g:, {'max': function('min')})", 'E704')
call assert_equal(3, max([1, 2, 3]))
+ " Try to overwrite an user defined function with a function reference
+ call assert_fails("let Expr1 = function('min')", 'E705:')
+
" Regression: the first line below used to throw ?E110: Missing ')'?
" Second is here just to prove that this line is correct when not skipping
" rhs of &&.
@@ -142,8 +149,8 @@ func Test_default_arg()
call assert_equal(res.optional, 2)
call assert_equal(res['0'], 1)
- call assert_fails("call MakeBadFunc()", 'E989')
- call assert_fails("fu F(a=1 ,) | endf", 'E475')
+ call assert_fails("call MakeBadFunc()", 'E989:')
+ call assert_fails("fu F(a=1 ,) | endf", 'E1068:')
" Since neovim does not have v:none, the ability to use the default
" argument with the intermediate argument set to v:none has been omitted.
@@ -156,6 +163,16 @@ func Test_default_arg()
\ .. "1 return deepcopy(a:)\n"
\ .. " endfunction",
\ execute('func Args2'))
+
+ " Error in default argument expression
+ let l =<< trim END
+ func F1(x = y)
+ return a:x * 2
+ endfunc
+ echo F1()
+ END
+ let @a = l->join("\n")
+ call assert_fails("exe @a", 'E121:')
endfunc
func s:addFoo(lead)
@@ -169,3 +186,319 @@ endfunc
func Test_failed_call_in_try()
try | call UnknownFunc() | catch | endtry
endfunc
+
+" Test for listing user-defined functions
+func Test_function_list()
+ call assert_fails("function Xabc", 'E123:')
+endfunc
+
+" Test for <sfile>, <slnum> in a function
+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
+
+" Test trailing text after :endfunction {{{1
+func Test_endfunction_trailing()
+ call assert_false(exists('*Xtest'))
+
+ exe "func Xtest()\necho 'hello'\nendfunc\nlet done = 'yes'"
+ call assert_true(exists('*Xtest'))
+ call assert_equal('yes', done)
+ delfunc Xtest
+ unlet done
+
+ exe "func Xtest()\necho 'hello'\nendfunc|let done = 'yes'"
+ call assert_true(exists('*Xtest'))
+ call assert_equal('yes', done)
+ delfunc Xtest
+ unlet done
+
+ " trailing line break
+ exe "func Xtest()\necho 'hello'\nendfunc\n"
+ call assert_true(exists('*Xtest'))
+ delfunc Xtest
+
+ set verbose=1
+ exe "func Xtest()\necho 'hello'\nendfunc \" garbage"
+ call assert_notmatch('W22:', split(execute('1messages'), "\n")[0])
+ call assert_true(exists('*Xtest'))
+ delfunc Xtest
+
+ exe "func Xtest()\necho 'hello'\nendfunc garbage"
+ call assert_match('W22:', split(execute('1messages'), "\n")[0])
+ call assert_true(exists('*Xtest'))
+ delfunc Xtest
+ set verbose=0
+
+ func Xtest(a1, a2)
+ echo a:a1 .. a:a2
+ endfunc
+ set verbose=15
+ redir @a
+ call Xtest(123, repeat('x', 100))
+ redir END
+ call assert_match('calling Xtest(123, ''xxxxxxx.*x\.\.\.x.*xxxx'')', getreg('a'))
+ delfunc Xtest
+ set verbose=0
+
+ function Foo()
+ echo 'hello'
+ endfunction | echo 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
+ delfunc Foo
+endfunc
+
+func Test_delfunction_force()
+ delfunc! Xtest
+ delfunc! Xtest
+ func Xtest()
+ echo 'nothing'
+ endfunc
+ delfunc! Xtest
+ delfunc! Xtest
+
+ " Try deleting the current function
+ call assert_fails('delfunc Test_delfunction_force', 'E131:')
+endfunc
+
+func Test_function_defined_line()
+ CheckNotGui
+
+ let lines =<< trim [CODE]
+ " F1
+ func F1()
+ " F2
+ func F2()
+ "
+ "
+ "
+ return
+ endfunc
+ " F3
+ execute "func F3()\n\n\n\nreturn\nendfunc"
+ " F4
+ execute "func F4()\n
+ \\n
+ \\n
+ \\n
+ \return\n
+ \endfunc"
+ endfunc
+ " F5
+ execute "func F5()\n\n\n\nreturn\nendfunc"
+ " F6
+ execute "func F6()\n
+ \\n
+ \\n
+ \\n
+ \return\n
+ \endfunc"
+ call F1()
+ verbose func F1
+ verbose func F2
+ verbose func F3
+ verbose func F4
+ verbose func F5
+ verbose func F6
+ qall!
+ [CODE]
+
+ call writefile(lines, '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:]]*')
+ call assert_match(' line 2$', m)
+
+ let m = matchstr(res, 'function F2()[^[:print:]]*[[:print:]]*')
+ call assert_match(' line 4$', m)
+
+ let m = matchstr(res, 'function F3()[^[:print:]]*[[:print:]]*')
+ call assert_match(' line 11$', m)
+
+ let m = matchstr(res, 'function F4()[^[:print:]]*[[:print:]]*')
+ call assert_match(' line 13$', m)
+
+ let m = matchstr(res, 'function F5()[^[:print:]]*[[:print:]]*')
+ call assert_match(' line 21$', m)
+
+ let m = matchstr(res, 'function F6()[^[:print:]]*[[:print:]]*')
+ call assert_match(' line 23$', m)
+
+ call delete('Xtest.vim')
+endfunc
+
+" Test for defining a function reference in the global scope
+func Test_add_funcref_to_global_scope()
+ let x = g:
+ let caught_E862 = 0
+ try
+ func x.Xfunc()
+ return 1
+ endfunc
+ catch /E862:/
+ let caught_E862 = 1
+ endtry
+ call assert_equal(1, caught_E862)
+endfunc
+
+func Test_funccall_garbage_collect()
+ func Func(x, ...)
+ call add(a:x, a:000)
+ endfunc
+ call Func([], [])
+ " Must not crash cause by invalid freeing
+ call test_garbagecollect_now()
+ call assert_true(v:true)
+ delfunc Func
+endfunc
+
+" Test for script-local function
+func <SID>DoLast()
+ call append(line('$'), "last line")
+endfunc
+
+func s:DoNothing()
+ call append(line('$'), "nothing line")
+endfunc
+
+func Test_script_local_func()
+ set nocp nomore viminfo+=nviminfo
+ new
+ nnoremap <buffer> _x :call <SID>DoNothing()<bar>call <SID>DoLast()<bar>delfunc <SID>DoNothing<bar>delfunc <SID>DoLast<cr>
+
+ normal _x
+ call assert_equal('nothing line', getline(2))
+ call assert_equal('last line', getline(3))
+ close!
+
+ " Try to call a script local function in global scope
+ let lines =<< trim [CODE]
+ :call assert_fails('call s:Xfunc()', 'E81:')
+ :call assert_fails('let x = call("<SID>Xfunc", [])', 'E120:')
+ :call writefile(v:errors, 'Xresult')
+ :qall
+
+ [CODE]
+ call writefile(lines, 'Xscript')
+ if RunVim([], [], '-s Xscript')
+ call assert_equal([], readfile('Xresult'))
+ endif
+ call delete('Xresult')
+ call delete('Xscript')
+endfunc
+
+" Test for errors in defining new functions
+func Test_func_def_error()
+ call assert_fails('func Xfunc abc ()', 'E124:')
+ call assert_fails('func Xfunc(', 'E125:')
+ call assert_fails('func xfunc()', 'E128:')
+
+ " Try to redefine a function that is in use
+ let caught_E127 = 0
+ try
+ func! Test_func_def_error()
+ endfunc
+ catch /E127:/
+ let caught_E127 = 1
+ endtry
+ call assert_equal(1, caught_E127)
+
+ " Try to define a function in a dict twice
+ let d = {}
+ let lines =<< trim END
+ func d.F1()
+ return 1
+ endfunc
+ END
+ let l = join(lines, "\n") . "\n"
+ exe l
+ call assert_fails('exe l', 'E717:')
+
+ " Define an autoload function with an incorrect file name
+ call writefile(['func foo#Bar()', 'return 1', 'endfunc'], 'Xscript')
+ call assert_fails('source Xscript', 'E746:')
+ call delete('Xscript')
+
+ " Try to list functions using an invalid search pattern
+ call assert_fails('function /\%(/', 'E53:')
+endfunc
+
+" Test for deleting a function
+func Test_del_func()
+ call assert_fails('delfunction Xabc', 'E130:')
+ let d = {'a' : 10}
+ call assert_fails('delfunc d.a', 'E718:')
+ func d.fn()
+ return 1
+ endfunc
+
+ " cannot delete the dict function by number
+ let nr = substitute(execute('echo d'), '.*function(''\(\d\+\)'').*', '\1', '')
+ call assert_fails('delfunction g:' .. nr, 'E475: Invalid argument: g:')
+
+ delfunc d.fn
+ call assert_equal({'a' : 10}, d)
+endfunc
+
+" Test for calling return outside of a function
+func Test_return_outside_func()
+ call writefile(['return 10'], 'Xscript')
+ call assert_fails('source Xscript', 'E133:')
+ call delete('Xscript')
+endfunc
+
+" Test for errors in calling a function
+func Test_func_arg_error()
+ " Too many arguments
+ call assert_fails("call call('min', range(1,20))", 'E118:')
+ call assert_fails("call call('min', range(1,21))", 'E699:')
+ call assert_fails('echo min(0,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,0,1)',
+ \ 'E740:')
+
+ " Missing dict argument
+ func Xfunc() dict
+ return 1
+ endfunc
+ call assert_fails('call Xfunc()', 'E725:')
+ delfunc Xfunc
+endfunc
+
+func Test_func_dict()
+ let mydict = {'a': 'b'}
+ function mydict.somefunc() dict
+ return len(self)
+ endfunc
+
+ call assert_equal("{'a': 'b', 'somefunc': function('3')}", string(mydict))
+ call assert_equal(2, mydict.somefunc())
+ call assert_match("^\n function \\d\\\+() dict"
+ \ .. "\n1 return len(self)"
+ \ .. "\n endfunction$", execute('func mydict.somefunc'))
+ call assert_fails('call mydict.nonexist()', 'E716:')
+endfunc
+
+func Test_func_range()
+ new
+ call setline(1, range(1, 8))
+ func FuncRange() range
+ echo a:firstline
+ echo a:lastline
+ endfunc
+ 3
+ call assert_equal("\n3\n3", execute('call FuncRange()'))
+ call assert_equal("\n4\n6", execute('4,6 call FuncRange()'))
+ call assert_equal("\n function FuncRange() range"
+ \ .. "\n1 echo a:firstline"
+ \ .. "\n2 echo a:lastline"
+ \ .. "\n endfunction",
+ \ execute('function FuncRange'))
+
+ 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 e37fe43b22..6910361345 100644
--- a/src/nvim/testdir/test_usercommands.vim
+++ b/src/nvim/testdir/test_usercommands.vim
@@ -79,6 +79,19 @@ function Test_cmdmods()
call assert_equal('silent!', g:mods)
tab MyCmd
call assert_equal('tab', g:mods)
+ 0tab MyCmd
+ call assert_equal('0tab', g:mods)
+ tab split
+ tab MyCmd
+ call assert_equal('tab', g:mods)
+ 1tab MyCmd
+ call assert_equal('1tab', g:mods)
+ tabprev
+ tab MyCmd
+ call assert_equal('tab', g:mods)
+ 2tab MyCmd
+ call assert_equal('2tab', g:mods)
+ 2tabclose
topleft MyCmd
call assert_equal('topleft', g:mods)
to MyCmd
@@ -101,6 +114,10 @@ function Test_cmdmods()
call assert_equal('vertical', g:mods)
vert MyCmd
call assert_equal('vertical', g:mods)
+ horizontal MyCmd
+ call assert_equal('horizontal', g:mods)
+ hor MyCmd
+ call assert_equal('horizontal', g:mods)
aboveleft belowright botright browse confirm hide keepalt keepjumps
\ keepmarks keeppatterns lockmarks noautocmd noswapfile silent
@@ -306,6 +323,11 @@ func Test_CmdErrors()
call assert_fails('com DoCmd :', 'E174:')
comclear
call assert_fails('delcom DoCmd', 'E184:')
+
+ " These used to leak memory
+ call assert_fails('com! -complete=custom,CustomComplete _ :', 'E182:')
+ call assert_fails('com! -complete=custom,CustomComplete docmd :', 'E183:')
+ call assert_fails('com! -complete=custom,CustomComplete -xxx DoCmd :', 'E181:')
endfunc
func CustomComplete(A, L, P)
@@ -313,7 +335,7 @@ func CustomComplete(A, L, P)
endfunc
func CustomCompleteList(A, L, P)
- return [ "Monday", "Tuesday", "Wednesday", {}]
+ return [ "Monday", "Tuesday", "Wednesday", {}, v:_null_string]
endfunc
func Test_CmdCompletion()
@@ -332,6 +354,14 @@ func Test_CmdCompletion()
call feedkeys(":com -complete=co\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"com -complete=color command compiler', @:)
+ " try completion for unsupported argument values
+ call feedkeys(":com -newarg=\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"com -newarg=\t", @:)
+
+ " command completion after the name in a user defined command
+ call feedkeys(":com MyCmd chist\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"com MyCmd chistory", @:)
+
command! DoCmd1 :
command! DoCmd2 :
call feedkeys(":com \<C-A>\<C-B>\"\<CR>", 'tx')
@@ -343,6 +373,10 @@ func Test_CmdCompletion()
call feedkeys(":delcom DoC\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"delcom DoCmd1 DoCmd2', @:)
+ " try argument completion for a command without completion
+ call feedkeys(":DoCmd1 \<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal("\"DoCmd1 \t", @:)
+
delcom DoCmd1
call feedkeys(":delcom DoC\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"delcom DoCmd2', @:)
@@ -361,6 +395,21 @@ func Test_CmdCompletion()
call feedkeys(":DoCmd \<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"DoCmd mswin xterm', @:)
+ " Test for file name completion
+ com! -nargs=1 -complete=file DoCmd :
+ call feedkeys(":DoCmd READM\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"DoCmd README.txt', @:)
+
+ " Test for buffer name completion
+ com! -nargs=1 -complete=buffer DoCmd :
+ let bnum = bufadd('BufForUserCmd')
+ call setbufvar(bnum, '&buflisted', 1)
+ call feedkeys(":DoCmd BufFor\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"DoCmd BufForUserCmd', @:)
+ bwipe BufForUserCmd
+ call feedkeys(":DoCmd BufFor\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"DoCmd BufFor', @:)
+
com! -nargs=* -complete=custom,CustomComplete DoCmd :
call feedkeys(":DoCmd \<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"DoCmd January February Mars', @:)
@@ -383,6 +432,17 @@ func Test_CmdCompletion()
com! -nargs=? -complete=custom,min DoCmd
call assert_fails("call feedkeys(':DoCmd \t', 'tx')", 'E118:')
+ " custom completion for a pattern with a backslash
+ let g:ArgLead = ''
+ func! CustCompl(A, L, P)
+ let g:ArgLead = a:A
+ return ['one', 'two', 'three']
+ endfunc
+ com! -nargs=? -complete=customlist,CustCompl DoCmd
+ call feedkeys(":DoCmd a\\\t", 'xt')
+ call assert_equal('a\', g:ArgLead)
+ delfunc CustCompl
+
delcom DoCmd
endfunc
@@ -600,6 +660,27 @@ func Test_command_list()
call assert_equal("\nNo user-defined commands found", execute('command'))
endfunc
+" Test for a custom user completion returning the wrong value type
+func Test_usercmd_custom()
+ func T1(a, c, p)
+ return "a\nb\n"
+ endfunc
+ command -nargs=* -complete=customlist,T1 TCmd1
+ call feedkeys(":TCmd1 \<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"TCmd1 ', @:)
+ delcommand TCmd1
+ delfunc T1
+
+ func T2(a, c, p)
+ return {}
+ endfunc
+ command -nargs=* -complete=customlist,T2 TCmd2
+ call feedkeys(":TCmd2 \<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"TCmd2 ', @:)
+ delcommand TCmd2
+ delfunc T2
+endfunc
+
func Test_delcommand_buffer()
command Global echo 'global'
command -buffer OneBuffer echo 'one'
@@ -640,5 +721,30 @@ func Test_recursive_define()
endwhile
endfunc
+" Test for using buffer-local ambiguous user-defined commands
+func Test_buflocal_ambiguous_usercmd()
+ new
+ command -buffer -nargs=1 -complete=sign TestCmd1 echo "Hello"
+ command -buffer -nargs=1 -complete=sign TestCmd2 echo "World"
+
+ call assert_fails("call feedkeys(':TestCmd\<CR>', 'xt')", 'E464:')
+ call feedkeys(":TestCmd \<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"TestCmd ', @:)
+
+ delcommand TestCmd1
+ delcommand TestCmd2
+ bw!
+endfunc
+
+" Test for using a multibyte character in a user command
+func Test_multibyte_in_usercmd()
+ command SubJapanesePeriodToDot exe "%s/\u3002/./g"
+ new
+ call setline(1, "Hello\u3002")
+ SubJapanesePeriodToDot
+ call assert_equal('Hello.', getline(1))
+ bw!
+ delcommand SubJapanesePeriodToDot
+endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_utf8.vim b/src/nvim/testdir/test_utf8.vim
index ab3503c282..e5f6d68720 100644
--- a/src/nvim/testdir/test_utf8.vim
+++ b/src/nvim/testdir/test_utf8.vim
@@ -2,6 +2,7 @@
source check.vim
source view_util.vim
+source screendump.vim
" Visual block Insert adjusts for multi-byte char
func Test_visual_block_insert()
@@ -12,7 +13,7 @@ func Test_visual_block_insert()
bwipeout!
endfunc
-" Test for built-in function strchars()
+" Test for built-in functions strchars() and strcharlen()
func Test_strchars()
let inp = ["a", "あいa", "A\u20dd", "A\u20dd\u20dd", "\u20dd"]
let exp = [[1, 1, 1], [3, 3, 3], [2, 2, 1], [3, 3, 1], [1, 1, 1]]
@@ -21,6 +22,15 @@ func Test_strchars()
call assert_equal(exp[i][1], inp[i]->strchars(0))
call assert_equal(exp[i][2], strchars(inp[i], 1))
endfor
+
+ let exp = [1, 3, 1, 1, 1]
+ for i in range(len(inp))
+ call assert_equal(exp[i], inp[i]->strcharlen())
+ call assert_equal(exp[i], strcharlen(inp[i]))
+ endfor
+
+ call assert_fails("let v=strchars('abc', [])", 'E745:')
+ call assert_fails("let v=strchars('abc', 2)", 'E1023:')
endfunc
" Test for customlist completion
@@ -131,9 +141,13 @@ func Test_list2str_str2list_latin1()
let save_encoding = &encoding
" set encoding=latin1
-
+
let lres = str2list(s, 1)
let sres = list2str(l, 1)
+ call assert_equal([65, 66, 67], str2list("ABC"))
+
+ " Try converting a list to a string in latin-1 encoding
+ call assert_equal([1, 2, 3], str2list(list2str([1, 2, 3])))
let &encoding = save_encoding
call assert_equal(l, lres)
@@ -153,6 +167,39 @@ func Test_setcellwidths()
call assert_equal(2, strwidth("\u1339"))
call assert_equal(1, strwidth("\u133a"))
+ for aw in ['single', 'double']
+ exe 'set ambiwidth=' . aw
+ " Handle \u0080 to \u009F as control chars even on MS-Windows.
+ set isprint=@,161-255
+
+ call setcellwidths([])
+ " Control chars
+ call assert_equal(4, strwidth("\u0081"))
+ call assert_equal(6, strwidth("\uFEFF"))
+ " Ambiguous width chars
+ call assert_equal((aw == 'single') ? 1 : 2, strwidth("\u00A1"))
+ call assert_equal((aw == 'single') ? 1 : 2, strwidth("\u2010"))
+
+ call setcellwidths([[0x81, 0x81, 1], [0xA1, 0xA1, 1],
+ \ [0x2010, 0x2010, 1], [0xFEFF, 0xFEFF, 1]])
+ " Control chars
+ call assert_equal(4, strwidth("\u0081"))
+ call assert_equal(6, strwidth("\uFEFF"))
+ " Ambiguous width chars
+ call assert_equal(1, strwidth("\u00A1"))
+ call assert_equal(1, strwidth("\u2010"))
+
+ call setcellwidths([[0x81, 0x81, 2], [0xA1, 0xA1, 2],
+ \ [0x2010, 0x2010, 2], [0xFEFF, 0xFEFF, 2]])
+ " Control chars
+ call assert_equal(4, strwidth("\u0081"))
+ call assert_equal(6, strwidth("\uFEFF"))
+ " Ambiguous width chars
+ call assert_equal(2, strwidth("\u00A1"))
+ call assert_equal(2, strwidth("\u2010"))
+ endfor
+ set ambiwidth& isprint&
+
call setcellwidths([])
call assert_fails('call setcellwidths(1)', 'E714:')
@@ -185,6 +232,42 @@ func Test_setcellwidths()
call setcellwidths([])
endfunc
+func Test_getcellwidths()
+ call setcellwidths([])
+ call assert_equal([], getcellwidths())
+
+ let widthlist = [
+ \ [0x1330, 0x1330, 2],
+ \ [9999, 10000, 1],
+ \ [0x1337, 0x1339, 2],
+ \]
+ let widthlistsorted = [
+ \ [0x1330, 0x1330, 2],
+ \ [0x1337, 0x1339, 2],
+ \ [9999, 10000, 1],
+ \]
+ call setcellwidths(widthlist)
+ call assert_equal(widthlistsorted, getcellwidths())
+
+ call setcellwidths([])
+endfunc
+
+func Test_setcellwidths_dump()
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ call setline(1, "\ue5ffDesktop")
+ END
+ call writefile(lines, 'XCellwidths', 'D')
+ let buf = RunVimInTerminal('-S XCellwidths', {'rows': 6})
+ call VerifyScreenDump(buf, 'Test_setcellwidths_dump_1', {})
+
+ call term_sendkeys(buf, ":call setcellwidths([[0xe5ff, 0xe5ff, 2]])\<CR>")
+ call VerifyScreenDump(buf, 'Test_setcellwidths_dump_2', {})
+
+ call StopVimInTerminal(buf)
+endfunc
+
func Test_print_overlong()
" Text with more composing characters than MB_MAXBYTES.
new
diff --git a/src/nvim/testdir/test_vartabs.vim b/src/nvim/testdir/test_vartabs.vim
index 68fe15ff93..e12c71d521 100644
--- a/src/nvim/testdir/test_vartabs.vim
+++ b/src/nvim/testdir/test_vartabs.vim
@@ -97,7 +97,7 @@ func Test_vartabs()
.retab!
call assert_equal("\t\t\t\tl", getline(1))
- " Test for 'retab' with same vlaues as vts
+ " Test for 'retab' with same values as vts
set ts=8 sts=0 vts=5,3,6,2 vsts=
exe "norm! S l"
.retab! 5,3,6,2
@@ -379,6 +379,8 @@ func Test_vartabs_shiftwidth()
let lines = ScreenLines([1, 3], winwidth(0))
call s:compare_lines(expect4, lines)
+ call assert_fails('call shiftwidth([])', 'E745:')
+
" cleanup
bw!
bw!
@@ -429,4 +431,18 @@ func Test_varsofttabstop()
close!
endfunc
+" Setting 'shiftwidth' to a negative value, should set it to either the value
+" of 'tabstop' (if 'vartabstop' is not set) or to the first value in
+" 'vartabstop'
+func Test_shiftwidth_vartabstop()
+ throw 'Skipped: Nvim removed this behavior in #6377'
+ setlocal tabstop=7 vartabstop=
+ call assert_fails('set shiftwidth=-1', 'E487:')
+ call assert_equal(7, &shiftwidth)
+ setlocal tabstop=7 vartabstop=5,7,10
+ call assert_fails('set shiftwidth=-1', 'E487:')
+ call assert_equal(5, &shiftwidth)
+ setlocal shiftwidth& vartabstop& tabstop&
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_viminfo.vim b/src/nvim/testdir/test_viminfo.vim
new file mode 100644
index 0000000000..e792db90ab
--- /dev/null
+++ b/src/nvim/testdir/test_viminfo.vim
@@ -0,0 +1,26 @@
+
+" Test for errors in setting 'viminfo'
+func Test_viminfo_option_error()
+ " Missing number
+ call assert_fails('set viminfo=\"', 'E526:')
+ for c in split("'/:<@s", '\zs')
+ call assert_fails('set viminfo=' .. c, 'E526:')
+ endfor
+
+ " Missing comma
+ call assert_fails('set viminfo=%10!', 'E527:')
+ call assert_fails('set viminfo=!%10', 'E527:')
+ call assert_fails('set viminfo=h%10', 'E527:')
+ call assert_fails('set viminfo=c%10', 'E527:')
+ call assert_fails('set viminfo=:10%10', 'E527:')
+
+ " Missing ' setting
+ call assert_fails('set viminfo=%10', 'E528:')
+endfunc
+
+func Test_viminfo_oldfiles_newfile()
+ let v:oldfiles = v:_null_list
+ call assert_equal("\nNo old files", execute('oldfiles'))
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim
index 0f204cdd0c..b0c4baf7c2 100644
--- a/src/nvim/testdir/test_vimscript.vim
+++ b/src/nvim/testdir/test_vimscript.vim
@@ -1,16 +1,15 @@
" Test various aspects of the Vim script language.
-" Most of this was formerly in test49.
+" Most of this was formerly in test49.vim (developed by Servatius Brandt
+" <Servatius.Brandt@fujitsu-siemens.com>)
source check.vim
source shared.vim
+source script_util.vim
"-------------------------------------------------------------------------------
" Test environment {{{1
"-------------------------------------------------------------------------------
-com! XpathINIT let g:Xpath = ''
-com! -nargs=1 -bar Xpath let g:Xpath = g:Xpath . <args>
-
" Append a message to the "messages" file
func Xout(text)
split messages
@@ -20,67 +19,31 @@ endfunc
com! -nargs=1 Xout call Xout(<args>)
-" MakeScript() - Make a script file from a function. {{{2
-"
-" Create a script that consists of the body of the function a:funcname.
-" Replace any ":return" by a ":finish", any argument variable by a global
-" variable, and every ":call" by a ":source" for the next following argument
-" 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.
-func MakeScript(funcname, ...)
- let script = tempname()
- execute "redir! >" . script
- execute "function" a:funcname
- redir END
- execute "edit" script
- " Delete the "function" and the "endfunction" lines. Do not include the
- " word "function" in the pattern since it might be translated if LANG is
- " set. When MakeScript() is being debugged, this deletes also the debugging
- " output of its line 3 and 4.
- exec '1,/.*' . a:funcname . '(.*)/d'
- /^\d*\s*endfunction\>/,$d
- %s/^\d*//e
- %s/return/finish/e
- %s/\<a:\(\h\w*\)/g:\1/ge
- normal gg0
- let cnt = 0
- while search('\<call\s*\%(\u\|s:\)\w*\s*(.*)', 'W') > 0
- let cnt = cnt + 1
- s/\<call\s*\%(\u\|s:\)\w*\s*(.*)/\='source ' . a:{cnt}/
- endwhile
- g/^\s*$/d
- write
- bwipeout
- return script
-endfunc
-
-" ExecAsScript - Source a temporary script made from a function. {{{2
-"
-" Make a temporary script file from the function a:funcname, ":source" it, and
-" delete it afterwards. However, if an exception is thrown the file may remain,
-" the caller should call DeleteTheScript() afterwards.
-let s:script_name = ''
-function! ExecAsScript(funcname)
- " Make a script from the function passed as argument.
- let s:script_name = MakeScript(a:funcname)
-
- " Source and delete the script.
- exec "source" s:script_name
- call delete(s:script_name)
- let s:script_name = ''
-endfunction
-
-function! DeleteTheScript()
- if s:script_name
- call delete(s:script_name)
- let s:script_name = ''
- endif
+" Create a new instance of Vim and run the commands in 'test' and then 'verify'
+" The commands in 'test' are expected to store the test results in the Xtest.out
+" file. If the test passes successfully, then Xtest.out should be empty.
+func RunInNewVim(test, verify)
+ let init =<< trim END
+ set cpo-=C " support line-continuation in sourced script
+ source script_util.vim
+ XpathINIT
+ XloopINIT
+ END
+ let cleanup =<< trim END
+ call writefile(v:errors, 'Xtest.out')
+ qall
+ END
+ call writefile(init, 'Xtest.vim')
+ call writefile(a:test, 'Xtest.vim', 'a')
+ call writefile(a:verify, 'Xverify.vim')
+ call writefile(cleanup, 'Xverify.vim', 'a')
+ call RunVim([], [], "-S Xtest.vim -S Xverify.vim")
+ call assert_equal([], readfile('Xtest.out'))
+ call delete('Xtest.out')
+ call delete('Xtest.vim')
+ call delete('Xverify.vim')
endfunc
-com! -nargs=1 -bar ExecAsScript call ExecAsScript(<f-args>)
-
-
"-------------------------------------------------------------------------------
" Test 1: :endwhile in function {{{1
"
@@ -90,7 +53,7 @@ com! -nargs=1 -bar ExecAsScript call ExecAsScript(<f-args>)
" tests will hang.
"-------------------------------------------------------------------------------
-function! T1_F()
+func T1_F()
Xpath 'a'
let first = 1
while 1
@@ -104,9 +67,9 @@ function! T1_F()
return
endif
endwhile
-endfunction
+endfunc
-function! T1_G()
+func T1_G()
Xpath 'h'
let first = 1
while 1
@@ -121,7 +84,7 @@ function! T1_G()
endif
if 1 " unmatched :if
endwhile
-endfunction
+endfunc
func Test_endwhile_function()
XpathINIT
@@ -175,7 +138,7 @@ endfunc
" Test 3: :if, :elseif, :while, :continue, :break {{{1
"-------------------------------------------------------------------------------
-function Test_if_while()
+func Test_if_while()
XpathINIT
if 1
Xpath 'a'
@@ -231,11 +194,21 @@ function Test_if_while()
call assert_equal('ab3j3b2c2b1f1h1km', g:Xpath)
endfunc
+" Check double quote after skipped "elseif" does not give error E15
+func Test_skipped_elseif()
+ if "foo" ==? "foo"
+ let result = "first"
+ elseif "foo" ==? "foo"
+ let result = "second"
+ endif
+ call assert_equal('first', result)
+endfunc
+
"-------------------------------------------------------------------------------
" Test 4: :return {{{1
"-------------------------------------------------------------------------------
-function! T4_F()
+func T4_F()
if 1
Xpath 'a'
let loops = 3
@@ -253,15 +226,15 @@ function! T4_F()
else
Xpath 'g'
endif
-endfunction
+endfunc
-function Test_return()
+func Test_return()
XpathINIT
call T4_F()
Xpath '4'
call assert_equal('ab3e3b2c24', g:Xpath)
-endfunction
+endfunc
"-------------------------------------------------------------------------------
@@ -271,14 +244,14 @@ endfunction
" test as a script file (:return replaced by :finish).
"-------------------------------------------------------------------------------
-function Test_finish()
+func Test_finish()
XpathINIT
ExecAsScript T4_F
Xpath '5'
call DeleteTheScript()
call assert_equal('ab3e3b2c25', g:Xpath)
-endfunction
+endfunc
@@ -412,7 +385,7 @@ delfunction G31
delfunction G32
delfunction G33
-function Test_defining_functions()
+func Test_defining_functions()
call assert_equal('ade2ie3ibcg0h1g1h2g2h3fg0h1g1h2g2h3m', g:test6_result)
call assert_equal('F1G1F2G21G22G23F3G31G32G33', g:test6_calls)
endfunc
@@ -476,7 +449,7 @@ endfunc
XpathINIT
-function! T8_F()
+func T8_F()
if 1
Xpath 'a'
while 1
@@ -508,9 +481,9 @@ function! T8_F()
return novar " returns (default return value 0)
Xpath 'q'
return 1 " not reached
-endfunction
+endfunc
-function! T8_G() abort
+func T8_G() abort
if 1
Xpath 'r'
while 1
@@ -524,9 +497,9 @@ function! T8_G() abort
Xpath 'x'
return -4 " not reached
-endfunction
+endfunc
-function! T8_H() abort
+func T8_H() abort
while 1
Xpath 'A'
if 1
@@ -540,7 +513,7 @@ function! T8_H() abort
Xpath 'F'
return -4 " not reached
-endfunction
+endfunc
" Aborted functions (T8_G and T8_H) return -1.
let g:test8_sum = (T8_F() + 1) - 4 * T8_G() - 8 * T8_H()
@@ -567,7 +540,7 @@ endfunc
XpathINIT
-function! F() abort
+func F() abort
Xpath 'a'
let result = G() " not aborted
Xpath 'b'
@@ -575,30 +548,30 @@ function! F() abort
Xpath 'c'
endif
return 1
-endfunction
+endfunc
-function! G() " no abort attribute
+func G() " no abort attribute
Xpath 'd'
if H() != -1 " aborted
Xpath 'e'
endif
Xpath 'f'
return 2
-endfunction
+endfunc
-function! H() abort
+func H() abort
Xpath 'g'
call I() " aborted
Xpath 'h'
return 4
-endfunction
+endfunc
-function! I() abort
+func I() abort
Xpath 'i'
asdf " error
Xpath 'j'
return 8
-endfunction
+endfunc
if F() != 1
Xpath 'k'
@@ -626,7 +599,7 @@ endfunc
XpathINIT
-function! MSG(enr, emsg)
+func MSG(enr, emsg)
let english = v:lang == "C" || v:lang =~ '^[Ee]n'
if a:enr == ""
Xout "TODO: Add message number for:" a:emsg
@@ -710,10 +683,10 @@ XpathINIT
let calls = 0
-function! P(num)
+func P(num)
let g:calls = g:calls + a:num " side effect on call
return 0
-endfunction
+endfunc
if 1
Xpath 'a'
@@ -756,23 +729,23 @@ endfunc
XpathINIT
-function! NULL()
+func NULL()
Xpath 'a'
return 0
-endfunction
+endfunc
-function! ZERO()
+func ZERO()
Xpath 'b'
return 0
-endfunction
+endfunc
-function! F0()
+func! F0()
Xpath 'c'
-endfunction
+endfunc
-function! F1(arg)
+func! F1(arg)
Xpath 'e'
-endfunction
+endfunc
let V0 = 1
@@ -1092,7 +1065,5387 @@ func Test_unmatched_if_in_while()
endfunc
"-------------------------------------------------------------------------------
+" Test 18: Interrupt (Ctrl-C pressed) {{{1
+"
+" On an interrupt, the script processing is terminated immediately.
+"-------------------------------------------------------------------------------
+
+func Test_interrupt_while_if()
+ let test =<< trim [CODE]
+ try
+ if 1
+ Xpath 'a'
+ while 1
+ Xpath 'b'
+ if 1
+ Xpath 'c'
+ call interrupt()
+ call assert_report('should not get here')
+ break
+ finish
+ endif | call assert_report('should not get here')
+ call assert_report('should not get here')
+ endwhile | call assert_report('should not get here')
+ call assert_report('should not get here')
+ endif | call assert_report('should not get here')
+ call assert_report('should not get here')
+ catch /^Vim:Interrupt$/
+ Xpath 'd'
+ endtry | Xpath 'e'
+ Xpath 'f'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcdef', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_interrupt_try()
+ let test =<< trim [CODE]
+ try
+ try
+ Xpath 'a'
+ call interrupt()
+ call assert_report('should not get here')
+ endtry | call assert_report('should not get here')
+ call assert_report('should not get here')
+ catch /^Vim:Interrupt$/
+ Xpath 'b'
+ endtry | Xpath 'c'
+ Xpath 'd'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcd', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_interrupt_func_while_if()
+ let test =<< trim [CODE]
+ func F()
+ if 1
+ Xpath 'a'
+ while 1
+ Xpath 'b'
+ if 1
+ Xpath 'c'
+ call interrupt()
+ call assert_report('should not get here')
+ break
+ return
+ endif | call assert_report('should not get here')
+ call assert_report('should not get here')
+ endwhile | call assert_report('should not get here')
+ call assert_report('should not get here')
+ endif | call assert_report('should not get here')
+ call assert_report('should not get here')
+ endfunc
+
+ Xpath 'd'
+ try
+ call F() | call assert_report('should not get here')
+ catch /^Vim:Interrupt$/
+ Xpath 'e'
+ endtry | Xpath 'f'
+ Xpath 'g'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('dabcefg', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_interrupt_func_try()
+ let test =<< trim [CODE]
+ func G()
+ try
+ Xpath 'a'
+ call interrupt()
+ call assert_report('should not get here')
+ endtry | call assert_report('should not get here')
+ call assert_report('should not get here')
+ endfunc
+
+ Xpath 'b'
+ try
+ call G() | call assert_report('should not get here')
+ catch /^Vim:Interrupt$/
+ Xpath 'c'
+ endtry | Xpath 'd'
+ Xpath 'e'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('bacde', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 19: Aborting on errors inside :try/:endtry {{{1
+"
+" An error in a command dynamically enclosed in a :try/:endtry region
+" aborts script processing immediately. It does not matter whether
+" the failing command is outside or inside a function and whether a
+" function has an "abort" attribute.
+"-------------------------------------------------------------------------------
+
+func Test_try_error_abort_1()
+ let test =<< trim [CODE]
+ func F() abort
+ Xpath 'a'
+ asdf
+ call assert_report('should not get here')
+ endfunc
+
+ try
+ Xpath 'b'
+ call F()
+ call assert_report('should not get here')
+ endtry | call assert_report('should not get here')
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('ba', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_try_error_abort_2()
+ let test =<< trim [CODE]
+ func G()
+ Xpath 'a'
+ asdf
+ call assert_report('should not get here')
+ endfunc
+
+ try
+ Xpath 'b'
+ call G()
+ call assert_report('should not get here')
+ endtry | call assert_report('should not get here')
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('ba', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_try_error_abort_3()
+ let test =<< trim [CODE]
+ try
+ Xpath 'a'
+ asdf
+ call assert_report('should not get here')
+ endtry | call assert_report('should not get here')
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('a', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_try_error_abort_4()
+ let test =<< trim [CODE]
+ if 1
+ try
+ Xpath 'a'
+ asdf
+ call assert_report('should not get here')
+ endtry | call assert_report('should not get here')
+ endif | call assert_report('should not get here')
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('a', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_try_error_abort_5()
+ let test =<< trim [CODE]
+ let p = 1
+ while p
+ let p = 0
+ try
+ Xpath 'a'
+ asdf
+ call assert_report('should not get here')
+ endtry | call assert_report('should not get here')
+ endwhile | call assert_report('should not get here')
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('a', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_try_error_abort_6()
+ let test =<< trim [CODE]
+ let p = 1
+ Xpath 'a'
+ while p
+ Xpath 'b'
+ let p = 0
+ try
+ Xpath 'c'
+ endwhile | call assert_report('should not get here')
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abc', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 20: Aborting on errors after :try/:endtry {{{1
+"
+" When an error occurs after the last active :try/:endtry region has
+" been left, termination behavior is as if no :try/:endtry has been
+" seen.
+"-------------------------------------------------------------------------------
+
+func Test_error_after_try_1()
+ let test =<< trim [CODE]
+ let p = 1
+ while p
+ let p = 0
+ Xpath 'a'
+ try
+ Xpath 'b'
+ endtry
+ asdf
+ call assert_report('should not get here')
+ endwhile | call assert_report('should not get here')
+ Xpath 'c'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abc', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_error_after_try_2()
+ let test =<< trim [CODE]
+ while 1
+ try
+ Xpath 'a'
+ break
+ call assert_report('should not get here')
+ endtry
+ endwhile
+ Xpath 'b'
+ asdf
+ Xpath 'c'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abc', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_error_after_try_3()
+ let test =<< trim [CODE]
+ while 1
+ try
+ Xpath 'a'
+ break
+ call assert_report('should not get here')
+ finally
+ Xpath 'b'
+ endtry
+ endwhile
+ Xpath 'c'
+ asdf
+ Xpath 'd'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcd', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_error_after_try_4()
+ let test =<< trim [CODE]
+ while 1
+ try
+ Xpath 'a'
+ finally
+ Xpath 'b'
+ break
+ call assert_report('should not get here')
+ endtry
+ endwhile
+ Xpath 'c'
+ asdf
+ Xpath 'd'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcd', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_error_after_try_5()
+ let test =<< trim [CODE]
+ let p = 1
+ while p
+ let p = 0
+ try
+ Xpath 'a'
+ continue
+ call assert_report('should not get here')
+ endtry
+ endwhile
+ Xpath 'b'
+ asdf
+ Xpath 'c'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abc', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_error_after_try_6()
+ let test =<< trim [CODE]
+ let p = 1
+ while p
+ let p = 0
+ try
+ Xpath 'a'
+ continue
+ call assert_report('should not get here')
+ finally
+ Xpath 'b'
+ endtry
+ endwhile
+ Xpath 'c'
+ asdf
+ Xpath 'd'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcd', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_error_after_try_7()
+ let test =<< trim [CODE]
+ let p = 1
+ while p
+ let p = 0
+ try
+ Xpath 'a'
+ finally
+ Xpath 'b'
+ continue
+ call assert_report('should not get here')
+ endtry
+ endwhile
+ Xpath 'c'
+ asdf
+ Xpath 'd'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcd', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 21: :finally for :try after :continue/:break/:return/:finish {{{1
+"
+" If a :try conditional stays inactive due to a preceding :continue,
+" :break, :return, or :finish, its :finally clause should not be
+" executed.
+"-------------------------------------------------------------------------------
+
+func Test_finally_after_loop_ctrl_statement()
+ let test =<< trim [CODE]
+ func F()
+ let loops = 2
+ while loops > 0
+ XloopNEXT
+ let loops = loops - 1
+ try
+ if loops == 1
+ Xloop 'a'
+ continue
+ call assert_report('should not get here')
+ elseif loops == 0
+ Xloop 'b'
+ break
+ call assert_report('should not get here')
+ endif
+
+ try " inactive
+ call assert_report('should not get here')
+ finally
+ call assert_report('should not get here')
+ endtry
+ finally
+ Xloop 'c'
+ endtry
+ call assert_report('should not get here')
+ endwhile
+
+ try
+ Xpath 'd'
+ return
+ call assert_report('should not get here')
+ try " inactive
+ call assert_report('should not get here')
+ finally
+ call assert_report('should not get here')
+ endtry
+ finally
+ Xpath 'e'
+ endtry
+ call assert_report('should not get here')
+ endfunc
+
+ try
+ Xpath 'f'
+ call F()
+ Xpath 'g'
+ finish
+ call assert_report('should not get here')
+ try " inactive
+ call assert_report('should not get here')
+ finally
+ call assert_report('should not get here')
+ endtry
+ finally
+ Xpath 'h'
+ endtry
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('fa2c2b3c3degh', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 22: :finally for a :try after an error/interrupt/:throw {{{1
+"
+" If a :try conditional stays inactive due to a preceding error or
+" interrupt or :throw, its :finally clause should not be executed.
+"-------------------------------------------------------------------------------
+
+func Test_finally_after_error_in_func()
+ let test =<< trim [CODE]
+ func Error()
+ try
+ Xpath 'b'
+ asdf " aborting error, triggering error exception
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ endfunc
+
+ Xpath 'a'
+ call Error()
+ call assert_report('should not get here')
+
+ if 1 " not active due to error
+ try " not active since :if inactive
+ call assert_report('should not get here')
+ finally
+ call assert_report('should not get here')
+ endtry
+ endif
+
+ try " not active due to error
+ call assert_report('should not get here')
+ finally
+ call assert_report('should not get here')
+ endtry
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('ab', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_finally_after_interrupt()
+ let test =<< trim [CODE]
+ func Interrupt()
+ try
+ Xpath 'a'
+ call interrupt() " triggering interrupt exception
+ call assert_report('should not get here')
+ endtry
+ endfunc
+
+ Xpath 'b'
+ try
+ call Interrupt()
+ catch /^Vim:Interrupt$/
+ Xpath 'c'
+ finish
+ endtry
+ call assert_report('should not get here')
+
+ if 1 " not active due to interrupt
+ try " not active since :if inactive
+ call assert_report('should not get here')
+ finally
+ call assert_report('should not get here')
+ endtry
+ endif
+
+ try " not active due to interrupt
+ call assert_report('should not get here')
+ finally
+ call assert_report('should not get here')
+ endtry
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('bac', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_finally_after_throw()
+ let test =<< trim [CODE]
+ func Throw()
+ Xpath 'a'
+ throw 'xyz'
+ endfunc
+
+ Xpath 'b'
+ call Throw()
+ call assert_report('should not get here')
+
+ if 1 " not active due to :throw
+ try " not active since :if inactive
+ call assert_report('should not get here')
+ finally
+ call assert_report('should not get here')
+ endtry
+ endif
+
+ try " not active due to :throw
+ call assert_report('should not get here')
+ finally
+ call assert_report('should not get here')
+ endtry
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('ba', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 23: :catch clauses for a :try after a :throw {{{1
+"
+" If a :try conditional stays inactive due to a preceding :throw,
+" none of its :catch clauses should be executed.
+"-------------------------------------------------------------------------------
+
+func Test_catch_after_throw()
+ let test =<< trim [CODE]
+ try
+ Xpath 'a'
+ throw "xyz"
+ call assert_report('should not get here')
+
+ if 1 " not active due to :throw
+ try " not active since :if inactive
+ call assert_report('should not get here')
+ catch /xyz/
+ call assert_report('should not get here')
+ endtry
+ endif
+ catch /xyz/
+ Xpath 'b'
+ endtry
+
+ Xpath 'c'
+ throw "abc"
+ call assert_report('should not get here')
+
+ try " not active due to :throw
+ call assert_report('should not get here')
+ catch /abc/
+ call assert_report('should not get here')
+ endtry
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abc', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 24: :endtry for a :try after a :throw {{{1
+"
+" If a :try conditional stays inactive due to a preceding :throw,
+" its :endtry should not rethrow the exception to the next surrounding
+" active :try conditional.
+"-------------------------------------------------------------------------------
+
+func Test_endtry_after_throw()
+ let test =<< trim [CODE]
+ try " try 1
+ try " try 2
+ Xpath 'a'
+ throw "xyz" " makes try 2 inactive
+ call assert_report('should not get here')
+
+ try " try 3
+ call assert_report('should not get here')
+ endtry " no rethrow to try 1
+ catch /xyz/ " should catch although try 2 inactive
+ Xpath 'b'
+ endtry
+ catch /xyz/ " try 1 active, but exception already caught
+ call assert_report('should not get here')
+ endtry
+ Xpath 'c'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abc', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 27: Executing :finally clauses after :return {{{1
+"
+" For a :return command dynamically enclosed in a :try/:endtry region,
+" :finally clauses are executed and the called function is ended.
+"-------------------------------------------------------------------------------
+
+func T27_F()
+ try
+ Xpath 'a'
+ try
+ Xpath 'b'
+ return
+ call assert_report('should not get here')
+ finally
+ Xpath 'c'
+ endtry
+ Xpath 'd'
+ finally
+ Xpath 'e'
+ endtry
+ call assert_report('should not get here')
+endfunc
+
+func T27_G()
+ try
+ Xpath 'f'
+ return
+ call assert_report('should not get here')
+ finally
+ Xpath 'g'
+ call T27_F()
+ Xpath 'h'
+ endtry
+ call assert_report('should not get here')
+endfunc
+
+func T27_H()
+ try
+ Xpath 'i'
+ call T27_G()
+ Xpath 'j'
+ finally
+ Xpath 'k'
+ return
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+endfunction
+
+func Test_finally_after_return()
+ XpathINIT
+ try
+ Xpath 'l'
+ call T27_H()
+ Xpath 'm'
+ finally
+ Xpath 'n'
+ endtry
+ call assert_equal('lifgabcehjkmn', g:Xpath)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 28: Executing :finally clauses after :finish {{{1
+"
+" For a :finish command dynamically enclosed in a :try/:endtry region,
+" :finally clauses are executed and the sourced file is finished.
+"
+" This test executes the bodies of the functions F, G, and H from the
+" previous test as script files (:return replaced by :finish).
+"-------------------------------------------------------------------------------
+
+func Test_finally_after_finish()
+ XpathINIT
+
+ let scriptF = MakeScript("T27_F")
+ let scriptG = MakeScript("T27_G", scriptF)
+ let scriptH = MakeScript("T27_H", scriptG)
+
+ try
+ Xpath 'A'
+ exec "source" scriptH
+ Xpath 'B'
+ finally
+ Xpath 'C'
+ endtry
+ Xpath 'D'
+ call assert_equal('AifgabcehjkBCD', g:Xpath)
+ call delete(scriptF)
+ call delete(scriptG)
+ call delete(scriptH)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 29: Executing :finally clauses on errors {{{1
+"
+" After an error in a command dynamically enclosed in a :try/:endtry
+" region, :finally clauses are executed and the script processing is
+" terminated.
+"-------------------------------------------------------------------------------
+
+func Test_finally_after_error_1()
+ let test =<< trim [CODE]
+ func F()
+ while 1
+ try
+ Xpath 'a'
+ while 1
+ try
+ Xpath 'b'
+ asdf " error
+ call assert_report('should not get here')
+ finally
+ Xpath 'c'
+ endtry | call assert_report('should not get here')
+ call assert_report('should not get here')
+ break
+ endwhile
+ call assert_report('should not get here')
+ finally
+ Xpath 'd'
+ endtry | call assert_report('should not get here')
+ call assert_report('should not get here')
+ break
+ endwhile
+ call assert_report('should not get here')
+ endfunc
+
+ while 1
+ try
+ Xpath 'e'
+ while 1
+ call F()
+ call assert_report('should not get here')
+ break
+ endwhile | call assert_report('should not get here')
+ call assert_report('should not get here')
+ finally
+ Xpath 'f'
+ endtry | call assert_report('should not get here')
+ endwhile | call assert_report('should not get here')
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('eabcdf', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_finally_after_error_2()
+ let test =<< trim [CODE]
+ func G() abort
+ if 1
+ try
+ Xpath 'a'
+ asdf " error
+ call assert_report('should not get here')
+ finally
+ Xpath 'b'
+ endtry | Xpath 'c'
+ endif | Xpath 'd'
+ call assert_report('should not get here')
+ endfunc
+
+ if 1
+ try
+ Xpath 'e'
+ call G()
+ call assert_report('should not get here')
+ finally
+ Xpath 'f'
+ endtry | call assert_report('should not get here')
+ endif | call assert_report('should not get here')
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('eabf', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 30: Executing :finally clauses on interrupt {{{1
+"
+" After an interrupt in a command dynamically enclosed in
+" a :try/:endtry region, :finally clauses are executed and the
+" script processing is terminated.
+"-------------------------------------------------------------------------------
+
+func Test_finally_on_interrupt()
+ let test =<< trim [CODE]
+ func F()
+ try
+ Xloop 'a'
+ call interrupt()
+ call assert_report('should not get here')
+ finally
+ Xloop 'b'
+ endtry
+ call assert_report('should not get here')
+ endfunc
+
+ try
+ try
+ Xpath 'c'
+ try
+ Xpath 'd'
+ call interrupt()
+ call assert_report('should not get here')
+ finally
+ Xpath 'e'
+ try
+ Xpath 'f'
+ try
+ Xpath 'g'
+ finally
+ Xpath 'h'
+ try
+ Xpath 'i'
+ call interrupt()
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ finally
+ Xpath 'j'
+ try
+ Xpath 'k'
+ call F()
+ call assert_report('should not get here')
+ finally
+ Xpath 'l'
+ try
+ Xpath 'm'
+ XloopNEXT
+ ExecAsScript F
+ call assert_report('should not get here')
+ finally
+ Xpath 'n'
+ endtry
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ catch /^Vim:Interrupt$/
+ Xpath 'o'
+ endtry
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('cdefghijka1b1lma2b2no', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 31: Executing :finally clauses after :throw {{{1
+"
+" After a :throw dynamically enclosed in a :try/:endtry region,
+" :finally clauses are executed and the script processing is
+" terminated.
+"-------------------------------------------------------------------------------
+
+func Test_finally_after_throw_2()
+ let test =<< trim [CODE]
+ func F()
+ try
+ Xloop 'a'
+ throw "exception"
+ call assert_report('should not get here')
+ finally
+ Xloop 'b'
+ endtry
+ call assert_report('should not get here')
+ endfunc
+
+ try
+ Xpath 'c'
+ try
+ Xpath 'd'
+ throw "exception"
+ call assert_report('should not get here')
+ finally
+ Xpath 'e'
+ try
+ Xpath 'f'
+ try
+ Xpath 'g'
+ finally
+ Xpath 'h'
+ try
+ Xpath 'i'
+ throw "exception"
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ finally
+ Xpath 'j'
+ try
+ Xpath 'k'
+ call F()
+ call assert_report('should not get here')
+ finally
+ Xpath 'l'
+ try
+ Xpath 'm'
+ XloopNEXT
+ ExecAsScript F
+ call assert_report('should not get here')
+ finally
+ Xpath 'n'
+ endtry
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('cdefghijka1b1lma2b2n', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 34: :finally reason discarded by :continue {{{1
+"
+" When a :finally clause is executed due to a :continue, :break,
+" :return, :finish, error, interrupt or :throw, the jump reason is
+" discarded by a :continue in the finally clause.
+"-------------------------------------------------------------------------------
+
+func Test_finally_after_continue()
+ let test =<< trim [CODE]
+ func C(jump)
+ XloopNEXT
+ let loop = 0
+ while loop < 2
+ let loop = loop + 1
+ if loop == 1
+ try
+ if a:jump == "continue"
+ continue
+ elseif a:jump == "break"
+ break
+ elseif a:jump == "return" || a:jump == "finish"
+ return
+ elseif a:jump == "error"
+ asdf
+ elseif a:jump == "interrupt"
+ call interrupt()
+ let dummy = 0
+ elseif a:jump == "throw"
+ throw "abc"
+ endif
+ finally
+ continue " discards jump that caused the :finally
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ elseif loop == 2
+ Xloop 'a'
+ endif
+ endwhile
+ endfunc
+
+ call C("continue")
+ Xpath 'b'
+ call C("break")
+ Xpath 'c'
+ call C("return")
+ Xpath 'd'
+ let g:jump = "finish"
+ ExecAsScript C
+ unlet g:jump
+ Xpath 'e'
+ try
+ call C("error")
+ Xpath 'f'
+ finally
+ Xpath 'g'
+ try
+ call C("interrupt")
+ Xpath 'h'
+ finally
+ Xpath 'i'
+ call C("throw")
+ Xpath 'j'
+ endtry
+ endtry
+ Xpath 'k'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('a2ba3ca4da5ea6fga7hia8jk', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 35: :finally reason discarded by :break {{{1
+"
+" When a :finally clause is executed due to a :continue, :break,
+" :return, :finish, error, interrupt or :throw, the jump reason is
+" discarded by a :break in the finally clause.
+"-------------------------------------------------------------------------------
+
+func Test_finally_discard_by_break()
+ let test =<< trim [CODE]
+ func B(jump)
+ XloopNEXT
+ let loop = 0
+ while loop < 2
+ let loop = loop + 1
+ if loop == 1
+ try
+ if a:jump == "continue"
+ continue
+ elseif a:jump == "break"
+ break
+ elseif a:jump == "return" || a:jump == "finish"
+ return
+ elseif a:jump == "error"
+ asdf
+ elseif a:jump == "interrupt"
+ call interrupt()
+ let dummy = 0
+ elseif a:jump == "throw"
+ throw "abc"
+ endif
+ finally
+ break " discards jump that caused the :finally
+ call assert_report('should not get here')
+ endtry
+ elseif loop == 2
+ call assert_report('should not get here')
+ endif
+ endwhile
+ Xloop 'a'
+ endfunc
+
+ call B("continue")
+ Xpath 'b'
+ call B("break")
+ Xpath 'c'
+ call B("return")
+ Xpath 'd'
+ let g:jump = "finish"
+ ExecAsScript B
+ unlet g:jump
+ Xpath 'e'
+ try
+ call B("error")
+ Xpath 'f'
+ finally
+ Xpath 'g'
+ try
+ call B("interrupt")
+ Xpath 'h'
+ finally
+ Xpath 'i'
+ call B("throw")
+ Xpath 'j'
+ endtry
+ endtry
+ Xpath 'k'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('a2ba3ca4da5ea6fga7hia8jk', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 36: :finally reason discarded by :return {{{1
+"
+" When a :finally clause is executed due to a :continue, :break,
+" :return, :finish, error, interrupt or :throw, the jump reason is
+" discarded by a :return in the finally clause.
+"-------------------------------------------------------------------------------
+
+func Test_finally_discard_by_return()
+ let test =<< trim [CODE]
+ func R(jump, retval) abort
+ let loop = 0
+ while loop < 2
+ let loop = loop + 1
+ if loop == 1
+ try
+ if a:jump == "continue"
+ continue
+ elseif a:jump == "break"
+ break
+ elseif a:jump == "return"
+ return
+ elseif a:jump == "error"
+ asdf
+ elseif a:jump == "interrupt"
+ call interrupt()
+ let dummy = 0
+ elseif a:jump == "throw"
+ throw "abc"
+ endif
+ finally
+ return a:retval " discards jump that caused the :finally
+ call assert_report('should not get here')
+ endtry
+ elseif loop == 2
+ call assert_report('should not get here')
+ endif
+ endwhile
+ call assert_report('should not get here')
+ endfunc
+
+ let sum = -R("continue", -8)
+ Xpath 'a'
+ let sum = sum - R("break", -16)
+ Xpath 'b'
+ let sum = sum - R("return", -32)
+ Xpath 'c'
+ try
+ let sum = sum - R("error", -64)
+ Xpath 'd'
+ finally
+ Xpath 'e'
+ try
+ let sum = sum - R("interrupt", -128)
+ Xpath 'f'
+ finally
+ Xpath 'g'
+ let sum = sum - R("throw", -256)
+ Xpath 'h'
+ endtry
+ endtry
+ Xpath 'i'
+
+ let expected = 8 + 16 + 32 + 64 + 128 + 256
+ call assert_equal(sum, expected)
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcdefghi', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 37: :finally reason discarded by :finish {{{1
+"
+" When a :finally clause is executed due to a :continue, :break,
+" :return, :finish, error, interrupt or :throw, the jump reason is
+" discarded by a :finish in the finally clause.
+"-------------------------------------------------------------------------------
+
+func Test_finally_discard_by_finish()
+ let test =<< trim [CODE]
+ func F(jump) " not executed as function, transformed to a script
+ let loop = 0
+ while loop < 2
+ let loop = loop + 1
+ if loop == 1
+ try
+ if a:jump == "continue"
+ continue
+ elseif a:jump == "break"
+ break
+ elseif a:jump == "finish"
+ finish
+ elseif a:jump == "error"
+ asdf
+ elseif a:jump == "interrupt"
+ call interrupt()
+ let dummy = 0
+ elseif a:jump == "throw"
+ throw "abc"
+ endif
+ finally
+ finish " discards jump that caused the :finally
+ call assert_report('should not get here')
+ endtry
+ elseif loop == 2
+ call assert_report('should not get here')
+ endif
+ endwhile
+ call assert_report('should not get here')
+ endfunc
+
+ let scriptF = MakeScript("F")
+ delfunction F
+
+ let g:jump = "continue"
+ exec "source" scriptF
+ Xpath 'a'
+ let g:jump = "break"
+ exec "source" scriptF
+ Xpath 'b'
+ let g:jump = "finish"
+ exec "source" scriptF
+ Xpath 'c'
+ try
+ let g:jump = "error"
+ exec "source" scriptF
+ Xpath 'd'
+ finally
+ Xpath 'e'
+ try
+ let g:jump = "interrupt"
+ exec "source" scriptF
+ Xpath 'f'
+ finally
+ Xpath 'g'
+ try
+ let g:jump = "throw"
+ exec "source" scriptF
+ Xpath 'h'
+ finally
+ Xpath 'i'
+ endtry
+ endtry
+ endtry
+ unlet g:jump
+ call delete(scriptF)
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcdefghi', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 38: :finally reason discarded by an error {{{1
+"
+" When a :finally clause is executed due to a :continue, :break,
+" :return, :finish, error, interrupt or :throw, the jump reason is
+" discarded by an error in the finally clause.
+"-------------------------------------------------------------------------------
+
+func Test_finally_discard_by_error()
+ let test =<< trim [CODE]
+ func E(jump)
+ let loop = 0
+ while loop < 2
+ let loop = loop + 1
+ if loop == 1
+ try
+ if a:jump == "continue"
+ continue
+ elseif a:jump == "break"
+ break
+ elseif a:jump == "return" || a:jump == "finish"
+ return
+ elseif a:jump == "error"
+ asdf
+ elseif a:jump == "interrupt"
+ call interrupt()
+ let dummy = 0
+ elseif a:jump == "throw"
+ throw "abc"
+ endif
+ finally
+ asdf " error; discards jump that caused the :finally
+ endtry
+ elseif loop == 2
+ call assert_report('should not get here')
+ endif
+ endwhile
+ call assert_report('should not get here')
+ endfunc
+
+ try
+ Xpath 'a'
+ call E("continue")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'b'
+ call E("break")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'c'
+ call E("return")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'd'
+ let g:jump = "finish"
+ ExecAsScript E
+ call assert_report('should not get here')
+ finally
+ unlet g:jump
+ try
+ Xpath 'e'
+ call E("error")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'f'
+ call E("interrupt")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'g'
+ call E("throw")
+ call assert_report('should not get here')
+ finally
+ Xpath 'h'
+ delfunction E
+ endtry
+ endtry
+ endtry
+ endtry
+ endtry
+ endtry
+ endtry
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcdefgh', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 39: :finally reason discarded by an interrupt {{{1
+"
+" When a :finally clause is executed due to a :continue, :break,
+" :return, :finish, error, interrupt or :throw, the jump reason is
+" discarded by an interrupt in the finally clause.
+"-------------------------------------------------------------------------------
+
+func Test_finally_discarded_by_interrupt()
+ let test =<< trim [CODE]
+ func I(jump)
+ let loop = 0
+ while loop < 2
+ let loop = loop + 1
+ if loop == 1
+ try
+ if a:jump == "continue"
+ continue
+ elseif a:jump == "break"
+ break
+ elseif a:jump == "return" || a:jump == "finish"
+ return
+ elseif a:jump == "error"
+ asdf
+ elseif a:jump == "interrupt"
+ call interrupt()
+ let dummy = 0
+ elseif a:jump == "throw"
+ throw "abc"
+ endif
+ finally
+ call interrupt()
+ let dummy = 0
+ endtry
+ elseif loop == 2
+ call assert_report('should not get here')
+ endif
+ endwhile
+ call assert_report('should not get here')
+ endfunc
+
+ try
+ try
+ Xpath 'a'
+ call I("continue")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'b'
+ call I("break")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'c'
+ call I("return")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'd'
+ let g:jump = "finish"
+ ExecAsScript I
+ call assert_report('should not get here')
+ finally
+ unlet g:jump
+ try
+ Xpath 'e'
+ call I("error")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'f'
+ call I("interrupt")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'g'
+ call I("throw")
+ call assert_report('should not get here')
+ finally
+ Xpath 'h'
+ delfunction I
+ endtry
+ endtry
+ endtry
+ endtry
+ endtry
+ endtry
+ endtry
+ call assert_report('should not get here')
+ catch /^Vim:Interrupt$/
+ Xpath 'A'
+ endtry
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcdefghA', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 40: :finally reason discarded by :throw {{{1
+"
+" When a :finally clause is executed due to a :continue, :break,
+" :return, :finish, error, interrupt or :throw, the jump reason is
+" discarded by a :throw in the finally clause.
+"-------------------------------------------------------------------------------
+
+func Test_finally_discard_by_throw()
+ let test =<< trim [CODE]
+ func T(jump)
+ let loop = 0
+ while loop < 2
+ let loop = loop + 1
+ if loop == 1
+ try
+ if a:jump == "continue"
+ continue
+ elseif a:jump == "break"
+ break
+ elseif a:jump == "return" || a:jump == "finish"
+ return
+ elseif a:jump == "error"
+ asdf
+ elseif a:jump == "interrupt"
+ call interrupt()
+ let dummy = 0
+ elseif a:jump == "throw"
+ throw "abc"
+ endif
+ finally
+ throw "xyz" " discards jump that caused the :finally
+ endtry
+ elseif loop == 2
+ call assert_report('should not get here')
+ endif
+ endwhile
+ call assert_report('should not get here')
+ endfunc
+
+ try
+ Xpath 'a'
+ call T("continue")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'b'
+ call T("break")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'c'
+ call T("return")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'd'
+ let g:jump = "finish"
+ ExecAsScript T
+ call assert_report('should not get here')
+ finally
+ unlet g:jump
+ try
+ Xpath 'e'
+ call T("error")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'f'
+ call T("interrupt")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'g'
+ call T("throw")
+ call assert_report('should not get here')
+ finally
+ Xpath 'h'
+ delfunction T
+ endtry
+ endtry
+ endtry
+ endtry
+ endtry
+ endtry
+ endtry
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcdefgh', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 49: Throwing exceptions across functions {{{1
+"
+" When an exception is thrown but not caught inside a function, the
+" caller is checked for a matching :catch clause.
+"-------------------------------------------------------------------------------
+
+func T49_C()
+ try
+ Xpath 'a'
+ throw "arrgh"
+ call assert_report('should not get here')
+ catch /arrgh/
+ Xpath 'b'
+ endtry
+ Xpath 'c'
+endfunc
+
+func T49_T1()
+ XloopNEXT
+ try
+ Xloop 'd'
+ throw "arrgh"
+ call assert_report('should not get here')
+ finally
+ Xloop 'e'
+ endtry
+ Xloop 'f'
+endfunc
+
+func T49_T2()
+ try
+ Xpath 'g'
+ call T49_T1()
+ call assert_report('should not get here')
+ finally
+ Xpath 'h'
+ endtry
+ call assert_report('should not get here')
+endfunc
+
+func Test_throw_exception_across_funcs()
+ XpathINIT
+ XloopINIT
+ try
+ Xpath 'i'
+ call T49_C() " throw and catch
+ Xpath 'j'
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+
+ try
+ Xpath 'k'
+ call T49_T1() " throw, one level
+ call assert_report('should not get here')
+ catch /arrgh/
+ Xpath 'l'
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+
+ try
+ Xpath 'm'
+ call T49_T2() " throw, two levels
+ call assert_report('should not get here')
+ catch /arrgh/
+ Xpath 'n'
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ Xpath 'o'
+
+ call assert_equal('iabcjkd2e2lmgd3e3hno', g:Xpath)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 50: Throwing exceptions across script files {{{1
+"
+" When an exception is thrown but not caught inside a script file,
+" the sourcing script or function is checked for a matching :catch
+" clause.
+"
+" This test executes the bodies of the functions C, T1, and T2 from
+" the previous test as script files (:return replaced by :finish).
+"-------------------------------------------------------------------------------
+
+func T50_F()
+ try
+ Xpath 'A'
+ exec "source" g:scriptC
+ Xpath 'B'
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+
+ try
+ Xpath 'C'
+ exec "source" g:scriptT1
+ call assert_report('should not get here')
+ catch /arrgh/
+ Xpath 'D'
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+endfunc
+
+func Test_throw_across_script()
+ XpathINIT
+ XloopINIT
+ let g:scriptC = MakeScript("T49_C")
+ let g:scriptT1 = MakeScript("T49_T1")
+ let scriptT2 = MakeScript("T49_T2", g:scriptT1)
+
+ try
+ Xpath 'E'
+ call T50_F()
+ Xpath 'F'
+ exec "source" scriptT2
+ call assert_report('should not get here')
+ catch /arrgh/
+ Xpath 'G'
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ Xpath 'H'
+ call assert_equal('EAabcBCd2e2DFgd3e3hGH', g:Xpath)
+
+ call delete(g:scriptC)
+ call delete(g:scriptT1)
+ call delete(scriptT2)
+ unlet g:scriptC g:scriptT1 scriptT2
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 52: Uncaught exceptions {{{1
+"
+" When an exception is thrown but not caught, an error message is
+" displayed when the script is terminated. In case of an interrupt
+" or error exception, the normal interrupt or error message(s) are
+" displayed.
+"-------------------------------------------------------------------------------
+
+func Test_uncaught_exception_1()
+ CheckEnglish
+
+ let test =<< trim [CODE]
+ Xpath 'a'
+ throw "arrgh"
+ call assert_report('should not get here')`
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('E605: Exception not caught: arrgh', v:errmsg)
+ call assert_equal('a', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_uncaught_exception_2()
+ CheckEnglish
+
+ let test =<< trim [CODE]
+ try
+ Xpath 'a'
+ throw "oops"
+ call assert_report('should not get here')`
+ catch /arrgh/
+ call assert_report('should not get here')`
+ endtry
+ call assert_report('should not get here')`
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('E605: Exception not caught: oops', v:errmsg)
+ call assert_equal('a', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_uncaught_exception_3()
+ CheckEnglish
+
+ let test =<< trim [CODE]
+ func T()
+ Xpath 'c'
+ throw "brrr"
+ call assert_report('should not get here')`
+ endfunc
+
+ try
+ Xpath 'a'
+ throw "arrgh"
+ call assert_report('should not get here')`
+ catch /.*/
+ Xpath 'b'
+ call T()
+ call assert_report('should not get here')`
+ endtry
+ call assert_report('should not get here')`
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('E605: Exception not caught: brrr', v:errmsg)
+ call assert_equal('abc', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_uncaught_exception_4()
+ CheckEnglish
+
+ let test =<< trim [CODE]
+ try
+ Xpath 'a'
+ throw "arrgh"
+ call assert_report('should not get here')`
+ finally
+ Xpath 'b'
+ throw "brrr"
+ call assert_report('should not get here')`
+ endtry
+ call assert_report('should not get here')`
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('E605: Exception not caught: brrr', v:errmsg)
+ call assert_equal('ab', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_uncaught_exception_5()
+ CheckEnglish
+
+ " Need to catch and handle interrupt, otherwise the test will wait for the
+ " user to press <Enter> to continue
+ let test =<< trim [CODE]
+ try
+ try
+ Xpath 'a'
+ call interrupt()
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ catch /^Vim:Interrupt$/
+ Xpath 'b'
+ endtry
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('ab', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_uncaught_exception_6()
+ CheckEnglish
+
+ let test =<< trim [CODE]
+ try
+ Xpath 'a'
+ let x = novar " error E121; exception: E121
+ catch /E15:/ " should not catch
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('a', g:Xpath)
+ call assert_equal('E121: Undefined variable: novar', v:errmsg)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_uncaught_exception_7()
+ CheckEnglish
+
+ let test =<< trim [CODE]
+ try
+ Xpath 'a'
+ " error E108/E488; exception: E488
+ unlet novar #
+ catch /E108:/ " should not catch
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('a', g:Xpath)
+ call assert_equal('E488: Trailing characters: #', v:errmsg)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 53: Nesting errors: :endif/:else/:elseif {{{1
+"
+" For nesting errors of :if conditionals the correct error messages
+" should be given.
+"-------------------------------------------------------------------------------
+
+func Test_nested_if_else_errors()
+ CheckEnglish
+
+ " :endif without :if
+ let code =<< trim END
+ endif
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if')
+
+ " :endif without :if
+ let code =<< trim END
+ while 1
+ endif
+ endwhile
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if')
+
+ " :endif without :if
+ let code =<< trim END
+ try
+ finally
+ endif
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if')
+
+ " :endif without :if
+ let code =<< trim END
+ try
+ endif
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if')
+
+ " :endif without :if
+ let code =<< trim END
+ try
+ throw "a"
+ catch /a/
+ endif
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if')
+
+ " :else without :if
+ let code =<< trim END
+ else
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if')
+
+ " :else without :if
+ let code =<< trim END
+ while 1
+ else
+ endwhile
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if')
+
+ " :else without :if
+ let code =<< trim END
+ try
+ finally
+ else
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if')
+
+ " :else without :if
+ let code =<< trim END
+ try
+ else
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if')
+
+ " :else without :if
+ let code =<< trim END
+ try
+ throw "a"
+ catch /a/
+ else
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if')
+
+ " :elseif without :if
+ let code =<< trim END
+ elseif 1
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if')
+
+ " :elseif without :if
+ let code =<< trim END
+ while 1
+ elseif 1
+ endwhile
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if')
+
+ " :elseif without :if
+ let code =<< trim END
+ try
+ finally
+ elseif 1
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if')
+
+ " :elseif without :if
+ let code =<< trim END
+ try
+ elseif 1
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if')
+
+ " :elseif without :if
+ let code =<< trim END
+ try
+ throw "a"
+ catch /a/
+ elseif 1
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if')
+
+ " multiple :else
+ let code =<< trim END
+ if 1
+ else
+ else
+ endif
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(else):E583: multiple :else')
+
+ " :elseif after :else
+ let code =<< trim END
+ if 1
+ else
+ elseif 1
+ endif
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(elseif):E584: :elseif after :else')
+
+ call delete('Xtest')
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 54: Nesting errors: :while/:endwhile {{{1
+"
+" For nesting errors of :while conditionals the correct error messages
+" should be given.
+"
+" This test reuses the function MESSAGES() from the previous test.
+" This functions checks the messages in g:msgfile.
"-------------------------------------------------------------------------------
+
+func Test_nested_while_error()
+ CheckEnglish
+
+ " :endwhile without :while
+ let code =<< trim END
+ endwhile
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')
+
+ " :endwhile without :while
+ let code =<< trim END
+ if 1
+ endwhile
+ endif
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')
+
+ " Missing :endif
+ let code =<< trim END
+ while 1
+ if 1
+ endwhile
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endwhile):E171: Missing :endif')
+
+ " :endwhile without :while
+ let code =<< trim END
+ try
+ finally
+ endwhile
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')
+
+ " Missing :endtry
+ let code =<< trim END
+ while 1
+ try
+ finally
+ endwhile
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endwhile):E600: Missing :endtry')
+
+ " Missing :endtry
+ let code =<< trim END
+ while 1
+ if 1
+ try
+ finally
+ endwhile
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endwhile):E600: Missing :endtry')
+
+ " Missing :endif
+ let code =<< trim END
+ while 1
+ try
+ finally
+ if 1
+ endwhile
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endwhile):E171: Missing :endif')
+
+ " :endwhile without :while
+ let code =<< trim END
+ try
+ endwhile
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')
+
+ " :endwhile without :while
+ let code =<< trim END
+ while 1
+ try
+ endwhile
+ endtry
+ endwhile
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')
+
+ " :endwhile without :while
+ let code =<< trim END
+ try
+ throw "a"
+ catch /a/
+ endwhile
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')
+
+ " :endwhile without :while
+ let code =<< trim END
+ while 1
+ try
+ throw "a"
+ catch /a/
+ endwhile
+ endtry
+ endwhile
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')
+
+ call delete('Xtest')
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 55: Nesting errors: :continue/:break {{{1
+"
+" For nesting errors of :continue and :break commands the correct
+" error messages should be given.
+"
+" This test reuses the function MESSAGES() from the previous test.
+" This functions checks the messages in g:msgfile.
+"-------------------------------------------------------------------------------
+
+func Test_nested_cont_break_error()
+ CheckEnglish
+
+ " :continue without :while
+ let code =<< trim END
+ continue
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for')
+
+ " :continue without :while
+ let code =<< trim END
+ if 1
+ continue
+ endif
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for')
+
+ " :continue without :while
+ let code =<< trim END
+ try
+ finally
+ continue
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for')
+
+ " :continue without :while
+ let code =<< trim END
+ try
+ continue
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for')
+
+ " :continue without :while
+ let code =<< trim END
+ try
+ throw "a"
+ catch /a/
+ continue
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for')
+
+ " :break without :while
+ let code =<< trim END
+ break
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for')
+
+ " :break without :while
+ let code =<< trim END
+ if 1
+ break
+ endif
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for')
+
+ " :break without :while
+ let code =<< trim END
+ try
+ finally
+ break
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for')
+
+ " :break without :while
+ let code =<< trim END
+ try
+ break
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for')
+
+ " :break without :while
+ let code =<< trim END
+ try
+ throw "a"
+ catch /a/
+ break
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for')
+
+ call delete('Xtest')
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 56: Nesting errors: :endtry {{{1
+"
+" For nesting errors of :try conditionals the correct error messages
+" should be given.
+"
+" This test reuses the function MESSAGES() from the previous test.
+" This functions checks the messages in g:msgfile.
+"-------------------------------------------------------------------------------
+
+func Test_nested_endtry_error()
+ CheckEnglish
+
+ " :endtry without :try
+ let code =<< trim END
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endtry):E602: :endtry without :try')
+
+ " :endtry without :try
+ let code =<< trim END
+ if 1
+ endtry
+ endif
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endtry):E602: :endtry without :try')
+
+ " :endtry without :try
+ let code =<< trim END
+ while 1
+ endtry
+ endwhile
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endtry):E602: :endtry without :try')
+
+ " Missing :endif
+ let code =<< trim END
+ try
+ if 1
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endtry):E171: Missing :endif')
+
+ " Missing :endwhile
+ let code =<< trim END
+ try
+ while 1
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endtry):E170: Missing :endwhile')
+
+ " Missing :endif
+ let code =<< trim END
+ try
+ finally
+ if 1
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endtry):E171: Missing :endif')
+
+ " Missing :endwhile
+ let code =<< trim END
+ try
+ finally
+ while 1
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endtry):E170: Missing :endwhile')
+
+ " Missing :endif
+ let code =<< trim END
+ try
+ throw "a"
+ catch /a/
+ if 1
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endtry):E171: Missing :endif')
+
+ " Missing :endwhile
+ let code =<< trim END
+ try
+ throw "a"
+ catch /a/
+ while 1
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endtry):E170: Missing :endwhile')
+
+ call delete('Xtest')
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 57: v:exception and v:throwpoint for user exceptions {{{1
+"
+" v:exception evaluates to the value of the exception that was caught
+" most recently and is not finished. (A caught exception is finished
+" when the next ":catch", ":finally", or ":endtry" is reached.)
+" v:throwpoint evaluates to the script/function name and line number
+" where that exception has been thrown.
+"-------------------------------------------------------------------------------
+
+func Test_user_exception_info()
+ CheckEnglish
+
+ XpathINIT
+ XloopINIT
+
+ func FuncException()
+ let g:exception = v:exception
+ endfunc
+
+ func FuncThrowpoint()
+ let g:throwpoint = v:throwpoint
+ endfunc
+
+ let scriptException = MakeScript("FuncException")
+ let scriptThrowPoint = MakeScript("FuncThrowpoint")
+
+ command! CmdException let g:exception = v:exception
+ command! CmdThrowpoint let g:throwpoint = v:throwpoint
+
+ func T(arg, line)
+ if a:line == 2
+ throw a:arg " in line 2
+ elseif a:line == 4
+ throw a:arg " in line 4
+ elseif a:line == 6
+ throw a:arg " in line 6
+ elseif a:line == 8
+ throw a:arg " in line 8
+ endif
+ endfunc
+
+ func G(arg, line)
+ call T(a:arg, a:line)
+ endfunc
+
+ func F(arg, line)
+ call G(a:arg, a:line)
+ endfunc
+
+ let scriptT = MakeScript("T")
+ let scriptG = MakeScript("G", scriptT)
+ let scriptF = MakeScript("F", scriptG)
+
+ try
+ Xpath 'a'
+ call F("oops", 2)
+ catch /.*/
+ Xpath 'b'
+ let exception = v:exception
+ let throwpoint = v:throwpoint
+ call assert_equal("oops", v:exception)
+ call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint)
+ call assert_match('\<2\>', v:throwpoint)
+
+ exec "let exception = v:exception"
+ exec "let throwpoint = v:throwpoint"
+ call assert_equal("oops", v:exception)
+ call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint)
+ call assert_match('\<2\>', v:throwpoint)
+
+ CmdException
+ CmdThrowpoint
+ call assert_equal("oops", v:exception)
+ call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint)
+ call assert_match('\<2\>', v:throwpoint)
+
+ call FuncException()
+ call FuncThrowpoint()
+ call assert_equal("oops", v:exception)
+ call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint)
+ call assert_match('\<2\>', v:throwpoint)
+
+ exec "source" scriptException
+ exec "source" scriptThrowPoint
+ call assert_equal("oops", v:exception)
+ call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint)
+ call assert_match('\<2\>', v:throwpoint)
+
+ try
+ Xpath 'c'
+ call G("arrgh", 4)
+ catch /.*/
+ Xpath 'd'
+ let exception = v:exception
+ let throwpoint = v:throwpoint
+ call assert_equal("arrgh", v:exception)
+ call assert_match('\<G\[1]\.\.T\>', v:throwpoint)
+ call assert_match('\<4\>', v:throwpoint)
+
+ try
+ Xpath 'e'
+ let g:arg = "autsch"
+ let g:line = 6
+ exec "source" scriptF
+ catch /.*/
+ Xpath 'f'
+ let exception = v:exception
+ let throwpoint = v:throwpoint
+ call assert_equal("autsch", v:exception)
+ call assert_match(fnamemodify(scriptT, ':t'), v:throwpoint)
+ call assert_match('\<6\>', v:throwpoint)
+ finally
+ Xpath 'g'
+ let exception = v:exception
+ let throwpoint = v:throwpoint
+ call assert_equal("arrgh", v:exception)
+ call assert_match('\<G\[1]\.\.T\>', v:throwpoint)
+ call assert_match('\<4\>', v:throwpoint)
+ try
+ Xpath 'h'
+ let g:arg = "brrrr"
+ let g:line = 8
+ exec "source" scriptG
+ catch /.*/
+ Xpath 'i'
+ let exception = v:exception
+ let throwpoint = v:throwpoint
+ " Resolve scriptT for matching it against v:throwpoint.
+ call assert_equal("brrrr", v:exception)
+ call assert_match(fnamemodify(scriptT, ':t'), v:throwpoint)
+ call assert_match('\<8\>', v:throwpoint)
+ finally
+ Xpath 'j'
+ let exception = v:exception
+ let throwpoint = v:throwpoint
+ call assert_equal("arrgh", v:exception)
+ call assert_match('\<G\[1]\.\.T\>', v:throwpoint)
+ call assert_match('\<4\>', v:throwpoint)
+ endtry
+ Xpath 'k'
+ let exception = v:exception
+ let throwpoint = v:throwpoint
+ call assert_equal("arrgh", v:exception)
+ call assert_match('\<G\[1]\.\.T\>', v:throwpoint)
+ call assert_match('\<4\>', v:throwpoint)
+ endtry
+ Xpath 'l'
+ let exception = v:exception
+ let throwpoint = v:throwpoint
+ call assert_equal("arrgh", v:exception)
+ call assert_match('\<G\[1]\.\.T\>', v:throwpoint)
+ call assert_match('\<4\>', v:throwpoint)
+ finally
+ Xpath 'm'
+ let exception = v:exception
+ let throwpoint = v:throwpoint
+ call assert_equal("oops", v:exception)
+ call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint)
+ call assert_match('\<2\>', v:throwpoint)
+ endtry
+ Xpath 'n'
+ let exception = v:exception
+ let throwpoint = v:throwpoint
+ call assert_equal("oops", v:exception)
+ call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint)
+ call assert_match('\<2\>', v:throwpoint)
+ finally
+ Xpath 'o'
+ let exception = v:exception
+ let throwpoint = v:throwpoint
+ call assert_equal("", v:exception)
+ call assert_match('^$', v:throwpoint)
+ call assert_match('^$', v:throwpoint)
+ endtry
+
+ call assert_equal('abcdefghijklmno', g:Xpath)
+
+ unlet exception throwpoint
+ delfunction FuncException
+ delfunction FuncThrowpoint
+ call delete(scriptException)
+ call delete(scriptThrowPoint)
+ unlet scriptException scriptThrowPoint
+ delcommand CmdException
+ delcommand CmdThrowpoint
+ delfunction T
+ delfunction G
+ delfunction F
+ call delete(scriptT)
+ call delete(scriptG)
+ call delete(scriptF)
+ unlet scriptT scriptG scriptF
+endfunc
+
+"-------------------------------------------------------------------------------
+"
+" Test 58: v:exception and v:throwpoint for error/interrupt exceptions {{{1
+"
+" v:exception and v:throwpoint work also for error and interrupt
+" exceptions.
+"-------------------------------------------------------------------------------
+
+func Test_execption_info_for_error()
+ CheckEnglish
+
+ let test =<< trim [CODE]
+ func T(line)
+ if a:line == 2
+ delfunction T " error (function in use) in line 2
+ elseif a:line == 4
+ call interrupt()
+ endif
+ endfunc
+
+ while 1
+ try
+ Xpath 'a'
+ call T(2)
+ call assert_report('should not get here')
+ catch /.*/
+ Xpath 'b'
+ if v:exception !~ 'Vim(delfunction):'
+ call assert_report('should not get here')
+ endif
+ if v:throwpoint !~ '\<T\>'
+ call assert_report('should not get here')
+ endif
+ if v:throwpoint !~ '\<2\>'
+ call assert_report('should not get here')
+ endif
+ finally
+ Xpath 'c'
+ if v:exception != ""
+ call assert_report('should not get here')
+ endif
+ if v:throwpoint != ""
+ call assert_report('should not get here')
+ endif
+ break
+ endtry
+ endwhile
+
+ Xpath 'd'
+ if v:exception != ""
+ call assert_report('should not get here')
+ endif
+ if v:throwpoint != ""
+ call assert_report('should not get here')
+ endif
+
+ while 1
+ try
+ Xpath 'e'
+ call T(4)
+ call assert_report('should not get here')
+ catch /.*/
+ Xpath 'f'
+ if v:exception != 'Vim:Interrupt'
+ call assert_report('should not get here')
+ endif
+ if v:throwpoint !~ 'function T'
+ call assert_report('should not get here')
+ endif
+ if v:throwpoint !~ '\<4\>'
+ call assert_report('should not get here')
+ endif
+ finally
+ Xpath 'g'
+ if v:exception != ""
+ call assert_report('should not get here')
+ endif
+ if v:throwpoint != ""
+ call assert_report('should not get here')
+ endif
+ break
+ endtry
+ endwhile
+
+ Xpath 'h'
+ if v:exception != ""
+ call assert_report('should not get here')
+ endif
+ if v:throwpoint != ""
+ call assert_report('should not get here')
+ endif
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcdefgh', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+"
+" Test 59: v:exception and v:throwpoint when discarding exceptions {{{1
+"
+" When a :catch clause is left by a ":break" etc or an error or
+" interrupt exception, v:exception and v:throwpoint are reset. They
+" are not affected by an exception that is discarded before being
+" caught.
+"-------------------------------------------------------------------------------
+func Test_exception_info_on_discard()
+ CheckEnglish
+
+ let test =<< trim [CODE]
+ let sfile = expand("<sfile>")
+
+ while 1
+ try
+ throw "x1"
+ catch /.*/
+ break
+ endtry
+ endwhile
+ call assert_equal('', v:exception)
+ call assert_equal('', v:throwpoint)
+
+ while 1
+ try
+ throw "x2"
+ catch /.*/
+ break
+ finally
+ call assert_equal('', v:exception)
+ call assert_equal('', v:throwpoint)
+ endtry
+ break
+ endwhile
+ call assert_equal('', v:exception)
+ call assert_equal('', v:throwpoint)
+
+ while 1
+ try
+ let errcaught = 0
+ try
+ try
+ throw "x3"
+ catch /.*/
+ let lnum = expand("<sflnum>")
+ asdf
+ endtry
+ catch /.*/
+ let errcaught = 1
+ call assert_match('Vim:E492: Not an editor command:', v:exception)
+ call assert_match('line ' .. (lnum + 1), v:throwpoint)
+ endtry
+ finally
+ call assert_equal(1, errcaught)
+ break
+ endtry
+ endwhile
+ call assert_equal('', v:exception)
+ call assert_equal('', v:throwpoint)
+
+ Xpath 'a'
+
+ while 1
+ try
+ let intcaught = 0
+ try
+ try
+ throw "x4"
+ catch /.*/
+ let lnum = expand("<sflnum>")
+ call interrupt()
+ endtry
+ catch /.*/
+ let intcaught = 1
+ call assert_match('Vim:Interrupt', v:exception)
+ call assert_match('line ' .. (lnum + 1), v:throwpoint)
+ endtry
+ finally
+ call assert_equal(1, intcaught)
+ break
+ endtry
+ endwhile
+ call assert_equal('', v:exception)
+ call assert_equal('', v:throwpoint)
+
+ Xpath 'b'
+
+ while 1
+ try
+ let errcaught = 0
+ try
+ try
+ if 1
+ let lnum = expand("<sflnum>")
+ throw "x5"
+ " missing endif
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ catch /.*/
+ let errcaught = 1
+ call assert_match('Vim(catch):E171: Missing :endif:', v:exception)
+ call assert_match('line ' .. (lnum + 3), v:throwpoint)
+ endtry
+ finally
+ call assert_equal(1, errcaught)
+ break
+ endtry
+ endwhile
+ call assert_equal('', v:exception)
+ call assert_equal('', v:throwpoint)
+
+ Xpath 'c'
+
+ try
+ while 1
+ try
+ throw "x6"
+ finally
+ break
+ endtry
+ break
+ endwhile
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ call assert_equal('', v:exception)
+ call assert_equal('', v:throwpoint)
+
+ try
+ while 1
+ try
+ throw "x7"
+ finally
+ break
+ endtry
+ break
+ endwhile
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ call assert_equal('', v:exception)
+ call assert_equal('', v:throwpoint)
+ endtry
+ call assert_equal('', v:exception)
+ call assert_equal('', v:throwpoint)
+
+ while 1
+ try
+ let errcaught = 0
+ try
+ try
+ throw "x8"
+ finally
+ let lnum = expand("<sflnum>")
+ asdf
+ endtry
+ catch /.*/
+ let errcaught = 1
+ call assert_match('Vim:E492: Not an editor command:', v:exception)
+ call assert_match('line ' .. (lnum + 1), v:throwpoint)
+ endtry
+ finally
+ call assert_equal(1, errcaught)
+ break
+ endtry
+ endwhile
+ call assert_equal('', v:exception)
+ call assert_equal('', v:throwpoint)
+
+ Xpath 'd'
+
+ while 1
+ try
+ let intcaught = 0
+ try
+ try
+ throw "x9"
+ finally
+ let lnum = expand("<sflnum>")
+ call interrupt()
+ endtry
+ catch /.*/
+ let intcaught = 1
+ call assert_match('Vim:Interrupt', v:exception)
+ call assert_match('line ' .. (lnum + 1), v:throwpoint)
+ endtry
+ finally
+ call assert_equal(1, intcaught)
+ break
+ endtry
+ endwhile
+ call assert_equal('', v:exception)
+ call assert_equal('', v:throwpoint)
+
+ Xpath 'e'
+
+ while 1
+ try
+ let errcaught = 0
+ try
+ try
+ if 1
+ let lnum = expand("<sflnum>")
+ throw "x10"
+ " missing endif
+ finally
+ call assert_equal('', v:exception)
+ call assert_equal('', v:throwpoint)
+ endtry
+ catch /.*/
+ let errcaught = 1
+ call assert_match('Vim(finally):E171: Missing :endif:', v:exception)
+ call assert_match('line ' .. (lnum + 3), v:throwpoint)
+ endtry
+ finally
+ call assert_equal(1, errcaught)
+ break
+ endtry
+ endwhile
+ call assert_equal('', v:exception)
+ call assert_equal('', v:throwpoint)
+
+ Xpath 'f'
+
+ while 1
+ try
+ let errcaught = 0
+ try
+ try
+ if 1
+ let lnum = expand("<sflnum>")
+ throw "x11"
+ " missing endif
+ endtry
+ catch /.*/
+ let errcaught = 1
+ call assert_match('Vim(endtry):E171: Missing :endif:', v:exception)
+ call assert_match('line ' .. (lnum + 3), v:throwpoint)
+ endtry
+ finally
+ call assert_equal(1, errcaught)
+ break
+ endtry
+ endwhile
+ call assert_equal('', v:exception)
+ call assert_equal('', v:throwpoint)
+
+ Xpath 'g'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcdefg', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+"
+" Test 60: (Re)throwing v:exception; :echoerr. {{{1
+"
+" A user exception can be rethrown after catching by throwing
+" v:exception. An error or interrupt exception cannot be rethrown
+" because Vim exceptions cannot be faked. A Vim exception using the
+" value of v:exception can, however, be triggered by the :echoerr
+" command.
+"-------------------------------------------------------------------------------
+
+func Test_rethrow_exception_1()
+ XpathINIT
+ try
+ try
+ Xpath 'a'
+ throw "oops"
+ catch /oops/
+ Xpath 'b'
+ throw v:exception " rethrow user exception
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ catch /^oops$/ " catches rethrown user exception
+ Xpath 'c'
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ call assert_equal('abc', g:Xpath)
+endfunc
+
+func Test_rethrow_exception_2()
+ XpathINIT
+ try
+ let caught = 0
+ try
+ Xpath 'a'
+ write /n/o/n/w/r/i/t/a/b/l/e/_/f/i/l/e
+ call assert_report('should not get here')
+ catch /^Vim(write):/
+ let caught = 1
+ throw v:exception " throw error: cannot fake Vim exception
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'b'
+ call assert_equal(1, caught)
+ endtry
+ catch /^Vim(throw):/ " catches throw error
+ let caught = caught + 1
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'c'
+ call assert_equal(2, caught)
+ endtry
+ call assert_equal('abc', g:Xpath)
+endfunc
+
+func Test_rethrow_exception_3()
+ XpathINIT
+ try
+ let caught = 0
+ try
+ Xpath 'a'
+ asdf
+ catch /^Vim/ " catch error exception
+ let caught = 1
+ " Trigger Vim error exception with value specified after :echoerr
+ let value = substitute(v:exception, '^Vim\((.*)\)\=:', '', "")
+ echoerr value
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'b'
+ call assert_equal(1, caught)
+ endtry
+ catch /^Vim(echoerr):/
+ let caught = caught + 1
+ call assert_match(value, v:exception)
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'c'
+ call assert_equal(2, caught)
+ endtry
+ call assert_equal('abc', g:Xpath)
+endfunc
+
+func Test_rethrow_exception_3()
+ XpathINIT
+ try
+ let errcaught = 0
+ try
+ Xpath 'a'
+ let intcaught = 0
+ call interrupt()
+ catch /^Vim:/ " catch interrupt exception
+ let intcaught = 1
+ " Trigger Vim error exception with value specified after :echoerr
+ echoerr substitute(v:exception, '^Vim\((.*)\)\=:', '', "")
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'b'
+ call assert_equal(1, intcaught)
+ endtry
+ catch /^Vim(echoerr):/
+ let errcaught = 1
+ call assert_match('Interrupt', v:exception)
+ finally
+ Xpath 'c'
+ call assert_equal(1, errcaught)
+ endtry
+ call assert_equal('abc', g:Xpath)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 61: Catching interrupt exceptions {{{1
+"
+" When an interrupt occurs inside a :try/:endtry region, an
+" interrupt exception is thrown and can be caught. Its value is
+" "Vim:Interrupt". If the interrupt occurs after an error or a :throw
+" but before a matching :catch is reached, all following :catches of
+" that try block are ignored, but the interrupt exception can be
+" caught by the next surrounding try conditional. An interrupt is
+" ignored when there is a previous interrupt that has not been caught
+" or causes a :finally clause to be executed.
+"-------------------------------------------------------------------------------
+
+func Test_catch_intr_exception()
+ let test =<< trim [CODE]
+ while 1
+ try
+ try
+ Xpath 'a'
+ call interrupt()
+ call assert_report('should not get here')
+ catch /^Vim:Interrupt$/
+ Xpath 'b'
+ finally
+ Xpath 'c'
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'd'
+ break
+ endtry
+ endwhile
+
+ while 1
+ try
+ try
+ try
+ Xpath 'e'
+ asdf
+ call assert_report('should not get here')
+ catch /do_not_catch/
+ call assert_report('should not get here')
+ catch /.*/
+ Xpath 'f'
+ call interrupt()
+ call assert_report('should not get here')
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'g'
+ call interrupt()
+ call assert_report('should not get here')
+ endtry
+ catch /^Vim:Interrupt$/
+ Xpath 'h'
+ finally
+ Xpath 'i'
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'j'
+ break
+ endtry
+ endwhile
+
+ while 1
+ try
+ try
+ try
+ Xpath 'k'
+ throw "x"
+ call assert_report('should not get here')
+ catch /do_not_catch/
+ call assert_report('should not get here')
+ catch /x/
+ Xpath 'l'
+ call interrupt()
+ call assert_report('should not get here')
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ catch /^Vim:Interrupt$/
+ Xpath 'm'
+ finally
+ Xpath 'n'
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'o'
+ break
+ endtry
+ endwhile
+
+ while 1
+ try
+ try
+ Xpath 'p'
+ call interrupt()
+ call assert_report('should not get here')
+ catch /do_not_catch/
+ call interrupt()
+ call assert_report('should not get here')
+ catch /^Vim:Interrupt$/
+ Xpath 'q'
+ finally
+ Xpath 'r'
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 's'
+ break
+ endtry
+ endwhile
+
+ Xpath 't'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcdefghijklmnopqrst', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 62: Catching error exceptions {{{1
+"
+" An error inside a :try/:endtry region is converted to an exception
+" and can be caught. The error exception has a "Vim(cmdname):" prefix
+" where cmdname is the name of the failing command, or a "Vim:" prefix
+" if no command name is known. The "Vim" prefixes cannot be faked.
+"-------------------------------------------------------------------------------
+
+func Test_catch_err_exception_1()
+ XpathINIT
+ while 1
+ try
+ try
+ let caught = 0
+ unlet novar
+ catch /^Vim(unlet):/
+ Xpath 'a'
+ let caught = 1
+ let v:errmsg = substitute(v:exception, '^Vim(unlet):', '', "")
+ finally
+ Xpath 'b'
+ call assert_equal(1, caught)
+ call assert_match('E108: No such variable: "novar"', v:errmsg)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'c'
+ break
+ endtry
+ call assert_report('should not get here')
+ endwhile
+ call assert_equal('abc', g:Xpath)
+endfunc
+
+func Test_catch_err_exception_2()
+ XpathINIT
+ while 1
+ try
+ try
+ let caught = 0
+ throw novar " error in :throw
+ catch /^Vim(throw):/
+ Xpath 'a'
+ let caught = 1
+ let v:errmsg = substitute(v:exception, '^Vim(throw):', '', "")
+ finally
+ Xpath 'b'
+ call assert_equal(1, caught)
+ call assert_match('E121: Undefined variable: novar', v:errmsg)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'c'
+ break
+ endtry
+ call assert_report('should not get here')
+ endwhile
+ call assert_equal('abc', g:Xpath)
+endfunc
+
+func Test_catch_err_exception_3()
+ XpathINIT
+ while 1
+ try
+ try
+ let caught = 0
+ throw "Vim:faked" " error: cannot fake Vim exception
+ catch /^Vim(throw):/
+ Xpath 'a'
+ let caught = 1
+ let v:errmsg = substitute(v:exception, '^Vim(throw):', '', "")
+ finally
+ Xpath 'b'
+ call assert_equal(1, caught)
+ call assert_match("E608: Cannot :throw exceptions with 'Vim' prefix",
+ \ v:errmsg)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'c'
+ break
+ endtry
+ call assert_report('should not get here')
+ endwhile
+ call assert_equal('abc', g:Xpath)
+endfunc
+
+func Test_catch_err_exception_4()
+ XpathINIT
+ func F()
+ while 1
+ " Missing :endwhile
+ endfunc
+
+ while 1
+ try
+ try
+ let caught = 0
+ call F()
+ catch /^Vim(endfunction):/
+ Xpath 'a'
+ let caught = 1
+ let v:errmsg = substitute(v:exception, '^Vim(endfunction):', '', "")
+ finally
+ Xpath 'b'
+ call assert_equal(1, caught)
+ call assert_match("E170: Missing :endwhile", v:errmsg)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'c'
+ break
+ endtry
+ call assert_report('should not get here')
+ endwhile
+ call assert_equal('abc', g:Xpath)
+ delfunc F
+endfunc
+
+func Test_catch_err_exception_5()
+ XpathINIT
+ func F()
+ while 1
+ " Missing :endwhile
+ endfunc
+
+ while 1
+ try
+ try
+ let caught = 0
+ ExecAsScript F
+ catch /^Vim:/
+ Xpath 'a'
+ let caught = 1
+ let v:errmsg = substitute(v:exception, '^Vim:', '', "")
+ finally
+ Xpath 'b'
+ call assert_equal(1, caught)
+ call assert_match("E170: Missing :endwhile", v:errmsg)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'c'
+ break
+ endtry
+ call assert_report('should not get here')
+ endwhile
+ call assert_equal('abc', g:Xpath)
+ delfunc F
+endfunc
+
+func Test_catch_err_exception_6()
+ XpathINIT
+ func G()
+ call G()
+ endfunc
+
+ while 1
+ try
+ let mfd_save = &mfd
+ set mfd=3
+ try
+ let caught = 0
+ call G()
+ catch /^Vim(call):/
+ Xpath 'a'
+ let caught = 1
+ let v:errmsg = substitute(v:exception, '^Vim(call):', '', "")
+ finally
+ Xpath 'b'
+ call assert_equal(1, caught)
+ call assert_match("E132: Function call depth is higher than 'maxfuncdepth'", v:errmsg)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'c'
+ let &mfd = mfd_save
+ break
+ endtry
+ call assert_report('should not get here')
+ endwhile
+ call assert_equal('abc', g:Xpath)
+ delfunc G
+endfunc
+
+func Test_catch_err_exception_7()
+ XpathINIT
+ func H()
+ return H()
+ endfunc
+
+ while 1
+ try
+ let mfd_save = &mfd
+ set mfd=3
+ try
+ let caught = 0
+ call H()
+ catch /^Vim(return):/
+ Xpath 'a'
+ let caught = 1
+ let v:errmsg = substitute(v:exception, '^Vim(return):', '', "")
+ finally
+ Xpath 'b'
+ call assert_equal(1, caught)
+ call assert_match("E132: Function call depth is higher than 'maxfuncdepth'", v:errmsg)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'c'
+ let &mfd = mfd_save
+ break " discard error for $VIMNOERRTHROW
+ endtry
+ call assert_report('should not get here')
+ endwhile
+
+ call assert_equal('abc', g:Xpath)
+ delfunc H
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 63: Suppressing error exceptions by :silent!. {{{1
+"
+" A :silent! command inside a :try/:endtry region suppresses the
+" conversion of errors to an exception and the immediate abortion on
+" error. When the commands executed by the :silent! themselves open
+" a new :try/:endtry region, conversion of errors to exception and
+" immediate abortion is switched on again - until the next :silent!
+" etc. The :silent! has the effect of setting v:errmsg to the error
+" message text (without displaying it) and continuing with the next
+" script line.
+"
+" When a command triggering autocommands is executed by :silent!
+" inside a :try/:endtry, the autocommand execution is not suppressed
+" on error.
+"
+" This test reuses the function MSG() from the previous test.
+"-------------------------------------------------------------------------------
+
+func Test_silent_exception()
+ XpathINIT
+ XloopINIT
+ let g:taken = ""
+
+ func S(n) abort
+ XloopNEXT
+ let g:taken = g:taken . "E" . a:n
+ let v:errmsg = ""
+ exec "asdf" . a:n
+
+ " Check that ":silent!" continues:
+ Xloop 'a'
+
+ " Check that ":silent!" sets "v:errmsg":
+ call assert_match("E492: Not an editor command", v:errmsg)
+ endfunc
+
+ func Foo()
+ while 1
+ try
+ try
+ let caught = 0
+ " This is not silent:
+ call S(3)
+ catch /^Vim:/
+ Xpath 'b'
+ let caught = 1
+ let errmsg3 = substitute(v:exception, '^Vim:', '', "")
+ silent! call S(4)
+ finally
+ call assert_equal(1, caught)
+ Xpath 'c'
+ call assert_match("E492: Not an editor command", errmsg3)
+ silent! call S(5)
+ " Break out of try conditionals that cover ":silent!". This also
+ " discards the aborting error when $VIMNOERRTHROW is non-zero.
+ break
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ endwhile
+ " This is a double ":silent!" (see caller).
+ silent! call S(6)
+ endfunc
+
+ func Bar()
+ try
+ silent! call S(2)
+ silent! execute "call Foo() | call S(7)"
+ silent! call S(8)
+ endtry " normal end of try cond that covers ":silent!"
+ " This has a ":silent!" from the caller:
+ call S(9)
+ endfunc
+
+ silent! call S(1)
+ silent! call Bar()
+ silent! call S(10)
+
+ call assert_equal("E1E2E3E4E5E6E7E8E9E10", g:taken)
+
+ augroup TMP
+ au!
+ autocmd BufWritePost * Xpath 'd'
+ augroup END
+
+ Xpath 'e'
+ silent! write /i/m/p/o/s/s/i/b/l/e
+ Xpath 'f'
+
+ call assert_equal('a2a3ba5ca6a7a8a9a10a11edf', g:Xpath)
+
+ augroup TMP
+ au!
+ augroup END
+ augroup! TMP
+ delfunction S
+ delfunction Foo
+ delfunction Bar
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 64: Error exceptions after error, interrupt or :throw {{{1
+"
+" When an error occurs after an interrupt or a :throw but before
+" a matching :catch is reached, all following :catches of that try
+" block are ignored, but the error exception can be caught by the next
+" surrounding try conditional. Any previous error exception is
+" discarded. An error is ignored when there is a previous error that
+" has not been caught.
+"-------------------------------------------------------------------------------
+
+func Test_exception_after_error_1()
+ XpathINIT
+ while 1
+ try
+ try
+ Xpath 'a'
+ let caught = 0
+ while 1
+ if 1
+ " Missing :endif
+ endwhile " throw error exception
+ catch /^Vim(/
+ Xpath 'b'
+ let caught = 1
+ finally
+ Xpath 'c'
+ call assert_equal(1, caught)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'd'
+ break
+ endtry
+ call assert_report('should not get here')
+ endwhile
+ call assert_equal('abcd', g:Xpath)
+endfunc
+
+func Test_exception_after_error_2()
+ XpathINIT
+ while 1
+ try
+ try
+ Xpath 'a'
+ let caught = 0
+ try
+ if 1
+ " Missing :endif
+ catch /.*/ " throw error exception
+ call assert_report('should not get here')
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ catch /^Vim(/
+ Xpath 'b'
+ let caught = 1
+ finally
+ Xpath 'c'
+ call assert_equal(1, caught)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'd'
+ break
+ endtry
+ call assert_report('should not get here')
+ endwhile
+ call assert_equal('abcd', g:Xpath)
+endfunc
+
+func Test_exception_after_error_3()
+ XpathINIT
+ while 1
+ try
+ try
+ let caught = 0
+ try
+ Xpath 'a'
+ call interrupt()
+ catch /do_not_catch/
+ call assert_report('should not get here')
+ if 1
+ " Missing :endif
+ catch /.*/ " throw error exception
+ call assert_report('should not get here')
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ catch /^Vim(/
+ Xpath 'b'
+ let caught = 1
+ finally
+ Xpath 'c'
+ call assert_equal(1, caught)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'd'
+ break
+ endtry
+ call assert_report('should not get here')
+ endwhile
+ call assert_equal('abcd', g:Xpath)
+endfunc
+
+func Test_exception_after_error_4()
+ XpathINIT
+ while 1
+ try
+ try
+ let caught = 0
+ try
+ Xpath 'a'
+ throw "x"
+ catch /do_not_catch/
+ call assert_report('should not get here')
+ if 1
+ " Missing :endif
+ catch /x/ " throw error exception
+ call assert_report('should not get here')
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ catch /^Vim(/
+ Xpath 'b'
+ let caught = 1
+ finally
+ Xpath 'c'
+ call assert_equal(1, caught)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'd'
+ break
+ endtry
+ call assert_report('should not get here')
+ endwhile
+ call assert_equal('abcd', g:Xpath)
+endfunc
+
+func Test_exception_after_error_5()
+ XpathINIT
+ while 1
+ try
+ try
+ let caught = 0
+ Xpath 'a'
+ endif " :endif without :if; throw error exception
+ if 1
+ " Missing :endif
+ catch /do_not_catch/ " ignore new error
+ call assert_report('should not get here')
+ catch /^Vim(endif):/
+ Xpath 'b'
+ let caught = 1
+ catch /^Vim(/
+ call assert_report('should not get here')
+ finally
+ Xpath 'c'
+ call assert_equal(1, caught)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'd'
+ break
+ endtry
+ call assert_report('should not get here')
+ endwhile
+ call assert_equal('abcd', g:Xpath)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 65: Errors in the /pattern/ argument of a :catch {{{1
+"
+" On an error in the /pattern/ argument of a :catch, the :catch does
+" not match. Any following :catches of the same :try/:endtry don't
+" match either. Finally clauses are executed.
+"-------------------------------------------------------------------------------
+
+func Test_catch_pattern_error()
+ CheckEnglish
+ XpathINIT
+
+ try
+ try
+ Xpath 'a'
+ throw "oops"
+ catch /^oops$/
+ Xpath 'b'
+ catch /\)/ " not checked; exception has already been caught
+ call assert_report('should not get here')
+ endtry
+ Xpath 'c'
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ call assert_equal('abc', g:Xpath)
+
+ XpathINIT
+ func F()
+ try
+ try
+ try
+ Xpath 'a'
+ throw "ab"
+ catch /abc/ " does not catch
+ call assert_report('should not get here')
+ catch /\)/ " error; discards exception
+ call assert_report('should not get here')
+ catch /.*/ " not checked
+ call assert_report('should not get here')
+ finally
+ Xpath 'b'
+ endtry
+ call assert_report('should not get here')
+ catch /^ab$/ " checked, but original exception is discarded
+ call assert_report('should not get here')
+ catch /^Vim(catch):/
+ Xpath 'c'
+ call assert_match('Vim(catch):E475: Invalid argument:', v:exception)
+ finally
+ Xpath 'd'
+ endtry
+ Xpath 'e'
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ Xpath 'f'
+ endfunc
+
+ call F()
+ call assert_equal('abcdef', g:Xpath)
+
+ delfunc F
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 66: Stop range :call on error, interrupt, or :throw {{{1
+"
+" When a function which is multiply called for a range since it
+" doesn't handle the range itself has an error in a command
+" dynamically enclosed by :try/:endtry or gets an interrupt or
+" executes a :throw, no more calls for the remaining lines in the
+" range are made. On an error in a command not dynamically enclosed
+" by :try/:endtry, the function is executed again for the remaining
+" lines in the range.
+"-------------------------------------------------------------------------------
+
+func Test_stop_range_on_error()
+ let test =<< trim [CODE]
+ let file = tempname()
+ exec "edit" file
+ call setline(1, ['line 1', 'line 2', 'line 3'])
+ let taken = ""
+ let expected = "G1EF1E(1)F1E(2)F1E(3)G2EF2E(1)G3IF3I(1)G4TF4T(1)G5AF5A(1)"
+
+ func F(reason, n) abort
+ let g:taken = g:taken .. "F" .. a:n ..
+ \ substitute(a:reason, '\(\l\).*', '\u\1', "") ..
+ \ "(" .. line(".") .. ")"
+
+ if a:reason == "error"
+ asdf
+ elseif a:reason == "interrupt"
+ call interrupt()
+ elseif a:reason == "throw"
+ throw "xyz"
+ elseif a:reason == "aborting error"
+ XloopNEXT
+ call assert_equal(g:taken, g:expected)
+ try
+ bwipeout!
+ call delete(g:file)
+ asdf
+ endtry
+ endif
+ endfunc
+
+ func G(reason, n)
+ let g:taken = g:taken .. "G" .. a:n ..
+ \ substitute(a:reason, '\(\l\).*', '\u\1', "")
+ 1,3call F(a:reason, a:n)
+ endfunc
+
+ Xpath 'a'
+ call G("error", 1)
+ try
+ Xpath 'b'
+ try
+ call G("error", 2)
+ call assert_report('should not get here')
+ finally
+ Xpath 'c'
+ try
+ call G("interrupt", 3)
+ call assert_report('should not get here')
+ finally
+ Xpath 'd'
+ try
+ call G("throw", 4)
+ call assert_report('should not get here')
+ endtry
+ endtry
+ endtry
+ catch /xyz/
+ Xpath 'e'
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ Xpath 'f'
+ call G("aborting error", 5)
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcdef', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 67: :throw across :call command {{{1
+"
+" On a call command, an exception might be thrown when evaluating the
+" function name, during evaluation of the arguments, or when the
+" function is being executed. The exception can be caught by the
+" caller.
+"-------------------------------------------------------------------------------
+
+func THROW(x, n)
+ if a:n == 1
+ Xpath 'A'
+ elseif a:n == 2
+ Xpath 'B'
+ elseif a:n == 3
+ Xpath 'C'
+ endif
+ throw a:x
+endfunc
+
+func NAME(x, n)
+ if a:n == 1
+ call assert_report('should not get here')
+ elseif a:n == 2
+ Xpath 'D'
+ elseif a:n == 3
+ Xpath 'E'
+ elseif a:n == 4
+ Xpath 'F'
+ endif
+ return a:x
+endfunc
+
+func ARG(x, n)
+ if a:n == 1
+ call assert_report('should not get here')
+ elseif a:n == 2
+ call assert_report('should not get here')
+ elseif a:n == 3
+ Xpath 'G'
+ elseif a:n == 4
+ Xpath 'I'
+ endif
+ return a:x
+endfunc
+
+func Test_throw_across_call_cmd()
+ XpathINIT
+
+ func F(x, n)
+ if a:n == 2
+ call assert_report('should not get here')
+ elseif a:n == 4
+ Xpath 'a'
+ endif
+ endfunc
+
+ while 1
+ try
+ let v:errmsg = ""
+
+ while 1
+ try
+ Xpath 'b'
+ call {NAME(THROW("name", 1), 1)}(ARG(4711, 1), 1)
+ call assert_report('should not get here')
+ catch /^name$/
+ Xpath 'c'
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ call assert_equal("", v:errmsg)
+ let v:errmsg = ""
+ break
+ endtry
+ endwhile
+
+ while 1
+ try
+ Xpath 'd'
+ call {NAME("F", 2)}(ARG(THROW("arg", 2), 2), 2)
+ call assert_report('should not get here')
+ catch /^arg$/
+ Xpath 'e'
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ call assert_equal("", v:errmsg)
+ let v:errmsg = ""
+ break
+ endtry
+ endwhile
+
+ while 1
+ try
+ Xpath 'f'
+ call {NAME("THROW", 3)}(ARG("call", 3), 3)
+ call assert_report('should not get here')
+ catch /^call$/
+ Xpath 'g'
+ catch /^0$/ " default return value
+ call assert_report('should not get here')
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ call assert_equal("", v:errmsg)
+ let v:errmsg = ""
+ break
+ endtry
+ endwhile
+
+ while 1
+ try
+ Xpath 'h'
+ call {NAME("F", 4)}(ARG(4711, 4), 4)
+ Xpath 'i'
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ call assert_equal("", v:errmsg)
+ let v:errmsg = ""
+ break
+ endtry
+ endwhile
+
+ catch /^0$/ " default return value
+ call assert_report('should not get here')
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ call assert_equal("", v:errmsg)
+ let v:errmsg = ""
+ break
+ endtry
+ endwhile
+
+ call assert_equal('bAcdDBefEGCghFIai', g:Xpath)
+ delfunction F
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 68: :throw across function calls in expressions {{{1
+"
+" On a function call within an expression, an exception might be
+" thrown when evaluating the function name, during evaluation of the
+" arguments, or when the function is being executed. The exception
+" can be caught by the caller.
+"
+" This test reuses the functions THROW(), NAME(), and ARG() from the
+" previous test.
+"-------------------------------------------------------------------------------
+
+func Test_throw_across_call_expr()
+ XpathINIT
+
+ func F(x, n)
+ if a:n == 2
+ call assert_report('should not get here')
+ elseif a:n == 4
+ Xpath 'a'
+ endif
+ return a:x
+ endfunction
+
+ while 1
+ try
+ let error = 0
+ let v:errmsg = ""
+
+ while 1
+ try
+ Xpath 'b'
+ let var1 = {NAME(THROW("name", 1), 1)}(ARG(4711, 1), 1)
+ call assert_report('should not get here')
+ catch /^name$/
+ Xpath 'c'
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ call assert_equal("", v:errmsg)
+ let v:errmsg = ""
+ break
+ endtry
+ endwhile
+ call assert_true(!exists('var1'))
+
+ while 1
+ try
+ Xpath 'd'
+ let var2 = {NAME("F", 2)}(ARG(THROW("arg", 2), 2), 2)
+ call assert_report('should not get here')
+ catch /^arg$/
+ Xpath 'e'
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ call assert_equal("", v:errmsg)
+ let v:errmsg = ""
+ break
+ endtry
+ endwhile
+ call assert_true(!exists('var2'))
+
+ while 1
+ try
+ Xpath 'f'
+ let var3 = {NAME("THROW", 3)}(ARG("call", 3), 3)
+ call assert_report('should not get here')
+ catch /^call$/
+ Xpath 'g'
+ catch /^0$/ " default return value
+ call assert_report('should not get here')
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ call assert_equal("", v:errmsg)
+ let v:errmsg = ""
+ break
+ endtry
+ endwhile
+ call assert_true(!exists('var3'))
+
+ while 1
+ try
+ Xpath 'h'
+ let var4 = {NAME("F", 4)}(ARG(4711, 4), 4)
+ Xpath 'i'
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ call assert_equal("", v:errmsg)
+ let v:errmsg = ""
+ break
+ endtry
+ endwhile
+ call assert_true(exists('var4') && var4 == 4711)
+
+ catch /^0$/ " default return value
+ call assert_report('should not get here')
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ call assert_equal("", v:errmsg)
+ break
+ endtry
+ endwhile
+
+ call assert_equal('bAcdDBefEGCghFIai', g:Xpath)
+ delfunc F
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 76: Errors, interrupts, :throw during expression evaluation {{{1
+"
+" When a function call made during expression evaluation is aborted
+" due to an error inside a :try/:endtry region or due to an interrupt
+" or a :throw, the expression evaluation is aborted as well. No
+" message is displayed for the cancelled expression evaluation. On an
+" error not inside :try/:endtry, the expression evaluation continues.
+"-------------------------------------------------------------------------------
+
+func Test_expr_eval_error()
+ let test =<< trim [CODE]
+ let taken = ""
+
+ func ERR(n)
+ let g:taken = g:taken .. "E" .. a:n
+ asdf
+ endfunc
+
+ func ERRabort(n) abort
+ let g:taken = g:taken .. "A" .. a:n
+ asdf
+ endfunc " returns -1; may cause follow-up msg for illegal var/func name
+
+ func WRAP(n, arg)
+ let g:taken = g:taken .. "W" .. a:n
+ let g:saved_errmsg = v:errmsg
+ return arg
+ endfunc
+
+ func INT(n)
+ let g:taken = g:taken .. "I" .. a:n
+ call interrupt()
+ endfunc
+
+ func THR(n)
+ let g:taken = g:taken .. "T" .. a:n
+ throw "should not be caught"
+ endfunc
+
+ func CONT(n)
+ let g:taken = g:taken .. "C" .. a:n
+ endfunc
+
+ func MSG(n)
+ let g:taken = g:taken .. "M" .. a:n
+ let errmsg = (a:n >= 37 && a:n <= 44) ? g:saved_errmsg : v:errmsg
+ let msgptn = (a:n >= 10 && a:n <= 27) ? "^$" : "asdf"
+ call assert_match(msgptn, errmsg)
+ let v:errmsg = ""
+ let g:saved_errmsg = ""
+ endfunc
+
+ let v:errmsg = ""
+
+ try
+ let t = 1
+ while t <= 9
+ Xloop 'a'
+ try
+ if t == 1
+ let v{ERR(t) + CONT(t)} = 0
+ elseif t == 2
+ let v{ERR(t) + CONT(t)}
+ elseif t == 3
+ let var = exists('v{ERR(t) + CONT(t)}')
+ elseif t == 4
+ unlet v{ERR(t) + CONT(t)}
+ elseif t == 5
+ function F{ERR(t) + CONT(t)}()
+ endfunction
+ elseif t == 6
+ function F{ERR(t) + CONT(t)}
+ elseif t == 7
+ let var = exists('*F{ERR(t) + CONT(t)}')
+ elseif t == 8
+ delfunction F{ERR(t) + CONT(t)}
+ elseif t == 9
+ let var = ERR(t) + CONT(t)
+ endif
+ catch /asdf/
+ " v:errmsg is not set when the error message is converted to an
+ " exception. Set it to the original error message.
+ let v:errmsg = substitute(v:exception, '^Vim:', '', "")
+ catch /^Vim\((\a\+)\)\=:/
+ " An error exception has been thrown after the original error.
+ let v:errmsg = ""
+ finally
+ call MSG(t)
+ let t = t + 1
+ XloopNEXT
+ continue " discard an aborting error
+ endtry
+ endwhile
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+
+ try
+ let t = 10
+ while t <= 18
+ Xloop 'b'
+ try
+ if t == 10
+ let v{INT(t) + CONT(t)} = 0
+ elseif t == 11
+ let v{INT(t) + CONT(t)}
+ elseif t == 12
+ let var = exists('v{INT(t) + CONT(t)}')
+ elseif t == 13
+ unlet v{INT(t) + CONT(t)}
+ elseif t == 14
+ function F{INT(t) + CONT(t)}()
+ endfunction
+ elseif t == 15
+ function F{INT(t) + CONT(t)}
+ elseif t == 16
+ let var = exists('*F{INT(t) + CONT(t)}')
+ elseif t == 17
+ delfunction F{INT(t) + CONT(t)}
+ elseif t == 18
+ let var = INT(t) + CONT(t)
+ endif
+ catch /^Vim\((\a\+)\)\=:\(Interrupt\)\@!/
+ " An error exception has been triggered after the interrupt.
+ let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
+ finally
+ call MSG(t)
+ let t = t + 1
+ XloopNEXT
+ continue " discard interrupt
+ endtry
+ endwhile
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+
+ try
+ let t = 19
+ while t <= 27
+ Xloop 'c'
+ try
+ if t == 19
+ let v{THR(t) + CONT(t)} = 0
+ elseif t == 20
+ let v{THR(t) + CONT(t)}
+ elseif t == 21
+ let var = exists('v{THR(t) + CONT(t)}')
+ elseif t == 22
+ unlet v{THR(t) + CONT(t)}
+ elseif t == 23
+ function F{THR(t) + CONT(t)}()
+ endfunction
+ elseif t == 24
+ function F{THR(t) + CONT(t)}
+ elseif t == 25
+ let var = exists('*F{THR(t) + CONT(t)}')
+ elseif t == 26
+ delfunction F{THR(t) + CONT(t)}
+ elseif t == 27
+ let var = THR(t) + CONT(t)
+ endif
+ catch /^Vim\((\a\+)\)\=:/
+ " An error exception has been triggered after the :throw.
+ let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
+ finally
+ call MSG(t)
+ let t = t + 1
+ XloopNEXT
+ continue " discard exception
+ endtry
+ endwhile
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+
+ let v{ERR(28) + CONT(28)} = 0
+ call MSG(28)
+ let v{ERR(29) + CONT(29)}
+ call MSG(29)
+ let var = exists('v{ERR(30) + CONT(30)}')
+ call MSG(30)
+ unlet v{ERR(31) + CONT(31)}
+ call MSG(31)
+ function F{ERR(32) + CONT(32)}()
+ endfunction
+ call MSG(32)
+ function F{ERR(33) + CONT(33)}
+ call MSG(33)
+ let var = exists('*F{ERR(34) + CONT(34)}')
+ call MSG(34)
+ delfunction F{ERR(35) + CONT(35)}
+ call MSG(35)
+ let var = ERR(36) + CONT(36)
+ call MSG(36)
+
+ let saved_errmsg = ""
+
+ let v{WRAP(37, ERRabort(37)) + CONT(37)} = 0
+ call MSG(37)
+ let v{WRAP(38, ERRabort(38)) + CONT(38)}
+ call MSG(38)
+ let var = exists('v{WRAP(39, ERRabort(39)) + CONT(39)}')
+ call MSG(39)
+ unlet v{WRAP(40, ERRabort(40)) + CONT(40)}
+ call MSG(40)
+ function F{WRAP(41, ERRabort(41)) + CONT(41)}()
+ endfunction
+ call MSG(41)
+ function F{WRAP(42, ERRabort(42)) + CONT(42)}
+ call MSG(42)
+ let var = exists('*F{WRAP(43, ERRabort(43)) + CONT(43)}')
+ call MSG(43)
+ delfunction F{WRAP(44, ERRabort(44)) + CONT(44)}
+ call MSG(44)
+ let var = ERRabort(45) + CONT(45)
+ call MSG(45)
+ Xpath 'd'
+
+ let expected = ""
+ \ .. "E1M1E2M2E3M3E4M4E5M5E6M6E7M7E8M8E9M9"
+ \ .. "I10M10I11M11I12M12I13M13I14M14I15M15I16M16I17M17I18M18"
+ \ .. "T19M19T20M20T21M21T22M22T23M23T24M24T25M25T26M26T27M27"
+ \ .. "E28C28M28E29C29M29E30C30M30E31C31M31E32C32M32E33C33M33"
+ \ .. "E34C34M34E35C35M35E36C36M36"
+ \ .. "A37W37C37M37A38W38C38M38A39W39C39M39A40W40C40M40A41W41C41M41"
+ \ .. "A42W42C42M42A43W43C43M43A44W44C44M44A45C45M45"
+ call assert_equal(expected, taken)
+ [CODE]
+ let verify =<< trim [CODE]
+ let expected = "a1a2a3a4a5a6a7a8a9"
+ \ .. "b10b11b12b13b14b15b16b17b18"
+ \ .. "c19c20c21c22c23c24c25c26c27d"
+ call assert_equal(expected, g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 77: Errors, interrupts, :throw in name{brace-expression} {{{1
+"
+" When a function call made during evaluation of an expression in
+" braces as part of a function name after ":function" is aborted due
+" to an error inside a :try/:endtry region or due to an interrupt or
+" a :throw, the expression evaluation is aborted as well, and the
+" function definition is ignored, skipping all commands to the
+" ":endfunction". On an error not inside :try/:endtry, the expression
+" evaluation continues and the function gets defined, and can be
+" called and deleted.
+"-------------------------------------------------------------------------------
+func Test_brace_expr_error()
+ let test =<< trim [CODE]
+ func ERR() abort
+ Xloop 'a'
+ asdf
+ endfunc " returns -1
+
+ func OK()
+ Xloop 'b'
+ let v:errmsg = ""
+ return 0
+ endfunc
+
+ let v:errmsg = ""
+
+ Xpath 'c'
+ func F{1 + ERR() + OK()}(arg)
+ " F0 should be defined.
+ if exists("a:arg") && a:arg == "calling"
+ Xpath 'd'
+ else
+ call assert_report('should not get here')
+ endif
+ endfunction
+ call assert_equal("", v:errmsg)
+ XloopNEXT
+
+ Xpath 'e'
+ call F{1 + ERR() + OK()}("calling")
+ call assert_equal("", v:errmsg)
+ XloopNEXT
+
+ Xpath 'f'
+ delfunction F{1 + ERR() + OK()}
+ call assert_equal("", v:errmsg)
+ XloopNEXT
+
+ try
+ while 1
+ try
+ Xpath 'g'
+ func G{1 + ERR() + OK()}(arg)
+ " G0 should not be defined, and the function body should be
+ " skipped.
+ call assert_report('should not get here')
+ " Use an unmatched ":finally" to check whether the body is
+ " skipped when an error occurs in ERR(). This works whether or
+ " not the exception is converted to an exception.
+ finally
+ call assert_report('should not get here')
+ endtry
+ try
+ call assert_report('should not get here')
+ endfunction
+
+ call assert_report('should not get here')
+ catch /asdf/
+ " Jumped to when the function is not defined and the body is
+ " skipped.
+ Xpath 'h'
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'i'
+ break
+ endtry " jumped to when the body is not skipped
+ endwhile
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('ca1b1ea2b2dfa3b3ga4hi', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 78: Messages on parsing errors in expression evaluation {{{1
+"
+" When an expression evaluation detects a parsing error, an error
+" message is given and converted to an exception, and the expression
+" evaluation is aborted.
+"-------------------------------------------------------------------------------
+func Test_expr_eval_error_msg()
+ CheckEnglish
+
+ let test =<< trim [CODE]
+ let taken = ""
+
+ func F(n)
+ let g:taken = g:taken . "F" . a:n
+ endfunc
+
+ func MSG(n, enr, emsg)
+ let g:taken = g:taken . "M" . a:n
+ call assert_match('^' .. a:enr .. ':', v:errmsg)
+ call assert_match(a:emsg, v:errmsg)
+ endfunc
+
+ func CONT(n)
+ let g:taken = g:taken . "C" . a:n
+ endfunc
+
+ let v:errmsg = ""
+ try
+ let t = 1
+ while t <= 14
+ let g:taken = g:taken . "T" . t
+ let v:errmsg = ""
+ try
+ if t == 1
+ let v{novar + CONT(t)} = 0
+ elseif t == 2
+ let v{novar + CONT(t)}
+ elseif t == 3
+ let var = exists('v{novar + CONT(t)}')
+ elseif t == 4
+ unlet v{novar + CONT(t)}
+ elseif t == 5
+ function F{novar + CONT(t)}()
+ endfunction
+ elseif t == 6
+ function F{novar + CONT(t)}
+ elseif t == 7
+ let var = exists('*F{novar + CONT(t)}')
+ elseif t == 8
+ delfunction F{novar + CONT(t)}
+ elseif t == 9
+ echo novar + CONT(t)
+ elseif t == 10
+ echo v{novar + CONT(t)}
+ elseif t == 11
+ echo F{novar + CONT(t)}
+ elseif t == 12
+ let var = novar + CONT(t)
+ elseif t == 13
+ let var = v{novar + CONT(t)}
+ elseif t == 14
+ let var = F{novar + CONT(t)}()
+ endif
+ catch /^Vim\((\a\+)\)\=:/
+ Xloop 'a'
+ " v:errmsg is not set when the error message is converted to an
+ " exception. Set it to the original error message.
+ let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
+ finally
+ Xloop 'b'
+ if t <= 8 && t != 3 && t != 7
+ call MSG(t, 'E475', 'Invalid argument\>')
+ else
+ call MSG(t, 'E121', "Undefined variable")
+ endif
+ let t = t + 1
+ XloopNEXT
+ continue " discard an aborting error
+ endtry
+ endwhile
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+
+ func T(n, expr, enr, emsg)
+ try
+ let g:taken = g:taken . "T" . a:n
+ let v:errmsg = ""
+ try
+ execute "let var = " . a:expr
+ catch /^Vim\((\a\+)\)\=:/
+ Xloop 'c'
+ " v:errmsg is not set when the error message is converted to an
+ " exception. Set it to the original error message.
+ let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
+ finally
+ Xloop 'd'
+ call MSG(a:n, a:enr, a:emsg)
+ XloopNEXT
+ " Discard an aborting error:
+ return
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ endfunc
+
+ call T(15, 'Nofunc() + CONT(15)', 'E117', "Unknown function")
+ call T(16, 'F(1 2 + CONT(16))', 'E116', "Invalid arguments")
+ call T(17, 'F(1, 2) + CONT(17)', 'E118', "Too many arguments")
+ call T(18, 'F() + CONT(18)', 'E119', "Not enough arguments")
+ call T(19, '{(1} + CONT(19)', 'E110', "Missing ')'")
+ call T(20, '("abc"[1) + CONT(20)', 'E111', "Missing ']'")
+ call T(21, '(1 +) + CONT(21)', 'E15', "Invalid expression")
+ call T(22, '1 2 + CONT(22)', 'E488', "Trailing characters: 2 +")
+ call T(23, '(1 ? 2) + CONT(23)', 'E109', "Missing ':' after '?'")
+ call T(24, '("abc) + CONT(24)', 'E114', "Missing quote")
+ call T(25, "('abc) + CONT(25)", 'E115', "Missing quote")
+ call T(26, '& + CONT(26)', 'E112', "Option name missing")
+ call T(27, '&asdf + CONT(27)', 'E113', "Unknown option")
+
+ let expected = ""
+ \ .. "T1M1T2M2T3M3T4M4T5M5T6M6T7M7T8M8T9M9T10M10T11M11T12M12T13M13T14M14"
+ \ .. "T15M15T16M16T17M17T18M18T19M19T20M20T21M21T22M22T23M23T24M24T25M25"
+ \ .. "T26M26T27M27"
+
+ call assert_equal(expected, taken)
+ [CODE]
+ let verify =<< trim [CODE]
+ let expected = "a1b1a2b2a3b3a4b4a5b5a6b6a7b7a8b8a9b9a10b10a11b11a12b12"
+ \ .. "a13b13a14b14c15d15c16d16c17d17c18d18c19d19c20d20"
+ \ .. "c21d21c22d22c23d23c24d24c25d25c26d26c27d27"
+ call assert_equal(expected, g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 79: Throwing one of several errors for the same command {{{1
+"
+" When several errors appear in a row (for instance during expression
+" evaluation), the first as the most specific one is used when
+" throwing an error exception. If, however, a syntax error is
+" detected afterwards, this one is used for the error exception.
+" On a syntax error, the next command is not executed, on a normal
+" error, however, it is (relevant only in a function without the
+" "abort" flag). v:errmsg is not set.
+"
+" If throwing error exceptions is configured off, v:errmsg is always
+" set to the latest error message, that is, to the more general
+" message or the syntax error, respectively.
+"-------------------------------------------------------------------------------
+func Test_throw_multi_error()
+ CheckEnglish
+
+ let test =<< trim [CODE]
+ func NEXT(cmd)
+ exec a:cmd . " | Xloop 'a'"
+ endfun
+
+ call NEXT('echo novar') " (checks nextcmd)
+ XloopNEXT
+ call NEXT('let novar #') " (skips nextcmd)
+ XloopNEXT
+ call NEXT('unlet novar #') " (skips nextcmd)
+ XloopNEXT
+ call NEXT('let {novar}') " (skips nextcmd)
+ XloopNEXT
+ call NEXT('unlet{ novar}') " (skips nextcmd)
+
+ call assert_equal('a1', g:Xpath)
+ XpathINIT
+ XloopINIT
+
+ func EXEC(cmd)
+ exec a:cmd
+ endfunc
+
+ try
+ while 1 " dummy loop
+ try
+ let v:errmsg = ""
+ call EXEC('echo novar') " normal error
+ catch /^Vim\((\a\+)\)\=:/
+ Xpath 'b'
+ call assert_match('E121: Undefined variable: novar', v:exception)
+ finally
+ Xpath 'c'
+ call assert_equal("", v:errmsg)
+ break
+ endtry
+ endwhile
+
+ Xpath 'd'
+ let cmd = "let"
+ while cmd != ""
+ try
+ let v:errmsg = ""
+ call EXEC(cmd . ' novar #') " normal plus syntax error
+ catch /^Vim\((\a\+)\)\=:/
+ Xloop 'e'
+ call assert_match('E488: Trailing characters', v:exception)
+ finally
+ Xloop 'f'
+ call assert_equal("", v:errmsg)
+ if cmd == "let"
+ let cmd = "unlet"
+ else
+ let cmd = ""
+ endif
+ XloopNEXT
+ continue
+ endtry
+ endwhile
+
+ Xpath 'g'
+ let cmd = "let"
+ while cmd != ""
+ try
+ let v:errmsg = ""
+ call EXEC(cmd . ' {novar}') " normal plus syntax error
+ catch /^Vim\((\a\+)\)\=:/
+ Xloop 'h'
+ call assert_match('E475: Invalid argument: {novar}', v:exception)
+ finally
+ Xloop 'i'
+ call assert_equal("", v:errmsg)
+ if cmd == "let"
+ let cmd = "unlet"
+ else
+ let cmd = ""
+ endif
+ XloopNEXT
+ continue
+ endtry
+ endwhile
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ Xpath 'j'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('bcde1f1e2f2gh3i3h4i4j', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 80: Syntax error in expression for illegal :elseif {{{1
+"
+" If there is a syntax error in the expression after an illegal
+" :elseif, an error message is given (or an error exception thrown)
+" for the illegal :elseif rather than the expression error.
+"-------------------------------------------------------------------------------
+func Test_if_syntax_error()
+ CheckEnglish
+
+ let test =<< trim [CODE]
+ let v:errmsg = ""
+ if 0
+ else
+ elseif 1 ||| 2
+ endif
+ Xpath 'a'
+ call assert_match('E584: :elseif after :else', v:errmsg)
+
+ let v:errmsg = ""
+ if 1
+ else
+ elseif 1 ||| 2
+ endif
+ Xpath 'b'
+ call assert_match('E584: :elseif after :else', v:errmsg)
+
+ let v:errmsg = ""
+ elseif 1 ||| 2
+ Xpath 'c'
+ call assert_match('E582: :elseif without :if', v:errmsg)
+
+ let v:errmsg = ""
+ while 1
+ elseif 1 ||| 2
+ endwhile
+ Xpath 'd'
+ call assert_match('E582: :elseif without :if', v:errmsg)
+
+ while 1
+ try
+ try
+ let v:errmsg = ""
+ if 0
+ else
+ elseif 1 ||| 2
+ endif
+ catch /^Vim\((\a\+)\)\=:/
+ Xpath 'e'
+ call assert_match('E584: :elseif after :else', v:exception)
+ finally
+ Xpath 'f'
+ call assert_equal("", v:errmsg)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'g'
+ break
+ endtry
+ endwhile
+
+ while 1
+ try
+ try
+ let v:errmsg = ""
+ if 1
+ else
+ elseif 1 ||| 2
+ endif
+ catch /^Vim\((\a\+)\)\=:/
+ Xpath 'h'
+ call assert_match('E584: :elseif after :else', v:exception)
+ finally
+ Xpath 'i'
+ call assert_equal("", v:errmsg)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'j'
+ break
+ endtry
+ endwhile
+
+ while 1
+ try
+ try
+ let v:errmsg = ""
+ elseif 1 ||| 2
+ catch /^Vim\((\a\+)\)\=:/
+ Xpath 'k'
+ call assert_match('E582: :elseif without :if', v:exception)
+ finally
+ Xpath 'l'
+ call assert_equal("", v:errmsg)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'm'
+ break
+ endtry
+ endwhile
+
+ while 1
+ try
+ try
+ let v:errmsg = ""
+ while 1
+ elseif 1 ||| 2
+ endwhile
+ catch /^Vim\((\a\+)\)\=:/
+ Xpath 'n'
+ call assert_match('E582: :elseif without :if', v:exception)
+ finally
+ Xpath 'o'
+ call assert_equal("", v:errmsg)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'p'
+ break
+ endtry
+ endwhile
+ Xpath 'q'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcdefghijklmnopq', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 81: Discarding exceptions after an error or interrupt {{{1
+"
+" When an exception is thrown from inside a :try conditional without
+" :catch and :finally clauses and an error or interrupt occurs before
+" the :endtry is reached, the exception is discarded.
+"-------------------------------------------------------------------------------
+
+func Test_discard_exception_after_error_1()
+ let test =<< trim [CODE]
+ try
+ Xpath 'a'
+ try
+ Xpath 'b'
+ throw "arrgh"
+ call assert_report('should not get here')
+ if 1
+ call assert_report('should not get here')
+ " error after :throw: missing :endif
+ endtry
+ call assert_report('should not get here')
+ catch /arrgh/
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('ab', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+" interrupt the code before the endtry is invoked
+func Test_discard_exception_after_error_2()
+ XpathINIT
+ let lines =<< trim [CODE]
+ try
+ Xpath 'a'
+ try
+ Xpath 'b'
+ throw "arrgh"
+ call assert_report('should not get here')
+ endtry " interrupt here
+ call assert_report('should not get here')
+ catch /arrgh/
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ [CODE]
+ call writefile(lines, 'Xscript')
+
+ breakadd file 7 Xscript
+ try
+ let caught_intr = 0
+ debuggreedy
+ call feedkeys(":source Xscript\<CR>quit\<CR>", "xt")
+ catch /^Vim:Interrupt$/
+ call assert_match('Xscript, line 7', v:throwpoint)
+ let caught_intr = 1
+ endtry
+ 0debuggreedy
+ call assert_equal(1, caught_intr)
+ call assert_equal('ab', g:Xpath)
+ breakdel *
+ call delete('Xscript')
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 82: Ignoring :catch clauses after an error or interrupt {{{1
+"
+" When an exception is thrown and an error or interrupt occurs before
+" the matching :catch clause is reached, the exception is discarded
+" and the :catch clause is ignored (also for the error or interrupt
+" exception being thrown then).
+"-------------------------------------------------------------------------------
+
+func Test_ignore_catch_after_error_1()
+ let test =<< trim [CODE]
+ try
+ try
+ Xpath 'a'
+ throw "arrgh"
+ call assert_report('should not get here')
+ if 1
+ call assert_report('should not get here')
+ " error after :throw: missing :endif
+ catch /.*/
+ call assert_report('should not get here')
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ catch /arrgh/
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('a', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_ignore_catch_after_error_2()
+ let test =<< trim [CODE]
+ func E()
+ try
+ try
+ Xpath 'a'
+ throw "arrgh"
+ call assert_report('should not get here')
+ if 1
+ call assert_report('should not get here')
+ " error after :throw: missing :endif
+ catch /.*/
+ call assert_report('should not get here')
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ catch /arrgh/
+ call assert_report('should not get here')
+ endtry
+ endfunc
+
+ call E()
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('a', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+" interrupt right before a catch is invoked in a script
+func Test_ignore_catch_after_intr_1()
+ XpathINIT
+ let lines =<< trim [CODE]
+ try
+ try
+ Xpath 'a'
+ throw "arrgh"
+ call assert_report('should not get here')
+ catch /.*/ " interrupt here
+ call assert_report('should not get here')
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ catch /arrgh/
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ [CODE]
+ call writefile(lines, 'Xscript')
+
+ breakadd file 6 Xscript
+ try
+ let caught_intr = 0
+ debuggreedy
+ call feedkeys(":source Xscript\<CR>quit\<CR>", "xt")
+ catch /^Vim:Interrupt$/
+ call assert_match('Xscript, line 6', v:throwpoint)
+ let caught_intr = 1
+ endtry
+ 0debuggreedy
+ call assert_equal(1, caught_intr)
+ call assert_equal('a', g:Xpath)
+ breakdel *
+ call delete('Xscript')
+endfunc
+
+" interrupt right before a catch is invoked inside a function.
+func Test_ignore_catch_after_intr_2()
+ XpathINIT
+ func F()
+ try
+ try
+ Xpath 'a'
+ throw "arrgh"
+ call assert_report('should not get here')
+ catch /.*/ " interrupt here
+ call assert_report('should not get here')
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ catch /arrgh/
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ endfunc
+
+ breakadd func 6 F
+ try
+ let caught_intr = 0
+ debuggreedy
+ call feedkeys(":call F()\<CR>quit\<CR>", "xt")
+ catch /^Vim:Interrupt$/
+ call assert_match('\.F, line 6', v:throwpoint)
+ let caught_intr = 1
+ endtry
+ 0debuggreedy
+ call assert_equal(1, caught_intr)
+ call assert_equal('a', g:Xpath)
+ breakdel *
+ delfunc F
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 83: Executing :finally clauses after an error or interrupt {{{1
+"
+" When an exception is thrown and an error or interrupt occurs before
+" the :finally of the innermost :try is reached, the exception is
+" discarded and the :finally clause is executed.
+"-------------------------------------------------------------------------------
+
+func Test_finally_after_error()
+ let test =<< trim [CODE]
+ try
+ Xpath 'a'
+ try
+ Xpath 'b'
+ throw "arrgh"
+ call assert_report('should not get here')
+ if 1
+ call assert_report('should not get here')
+ " error after :throw: missing :endif
+ finally
+ Xpath 'c'
+ endtry
+ call assert_report('should not get here')
+ catch /arrgh/
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abc', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+" interrupt the code right before the finally is invoked
+func Test_finally_after_intr()
+ XpathINIT
+ let lines =<< trim [CODE]
+ try
+ Xpath 'a'
+ try
+ Xpath 'b'
+ throw "arrgh"
+ call assert_report('should not get here')
+ finally " interrupt here
+ Xpath 'c'
+ endtry
+ call assert_report('should not get here')
+ catch /arrgh/
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ [CODE]
+ call writefile(lines, 'Xscript')
+
+ breakadd file 7 Xscript
+ try
+ let caught_intr = 0
+ debuggreedy
+ call feedkeys(":source Xscript\<CR>quit\<CR>", "xt")
+ catch /^Vim:Interrupt$/
+ call assert_match('Xscript, line 7', v:throwpoint)
+ let caught_intr = 1
+ endtry
+ 0debuggreedy
+ call assert_equal(1, caught_intr)
+ call assert_equal('abc', g:Xpath)
+ breakdel *
+ call delete('Xscript')
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 84: Exceptions in autocommand sequences. {{{1
+"
+" When an exception occurs in a sequence of autocommands for
+" a specific event, the rest of the sequence is not executed. The
+" command that triggered the autocommand execution aborts, and the
+" exception is propagated to the caller.
+"
+" For the FuncUndefined event under a function call expression or
+" :call command, the function is not executed, even when it has
+" been defined by the autocommands before the exception occurred.
+"-------------------------------------------------------------------------------
+
+func Test_autocmd_exception()
+ let test =<< trim [CODE]
+ func INT()
+ call interrupt()
+ endfunc
+
+ aug TMP
+ autocmd!
+
+ autocmd User x1 Xpath 'a'
+ autocmd User x1 throw "x1"
+ autocmd User x1 call assert_report('should not get here')
+
+ autocmd User x2 Xpath 'b'
+ autocmd User x2 asdf
+ autocmd User x2 call assert_report('should not get here')
+
+ autocmd User x3 Xpath 'c'
+ autocmd User x3 call INT()
+ autocmd User x3 call assert_report('should not get here')
+
+ autocmd FuncUndefined U1 func U1()
+ autocmd FuncUndefined U1 call assert_report('should not get here')
+ autocmd FuncUndefined U1 endfunc
+ autocmd FuncUndefined U1 Xpath 'd'
+ autocmd FuncUndefined U1 throw "U1"
+ autocmd FuncUndefined U1 call assert_report('should not get here')
+
+ autocmd FuncUndefined U2 func U2()
+ autocmd FuncUndefined U2 call assert_report('should not get here')
+ autocmd FuncUndefined U2 endfunc
+ autocmd FuncUndefined U2 Xpath 'e'
+ autocmd FuncUndefined U2 ASDF
+ autocmd FuncUndefined U2 call assert_report('should not get here')
+
+ autocmd FuncUndefined U3 func U3()
+ autocmd FuncUndefined U3 call assert_report('should not get here')
+ autocmd FuncUndefined U3 endfunc
+ autocmd FuncUndefined U3 Xpath 'f'
+ autocmd FuncUndefined U3 call INT()
+ autocmd FuncUndefined U3 call assert_report('should not get here')
+ aug END
+
+ try
+ try
+ Xpath 'g'
+ doautocmd User x1
+ catch /x1/
+ Xpath 'h'
+ endtry
+
+ while 1
+ try
+ Xpath 'i'
+ doautocmd User x2
+ catch /asdf/
+ Xpath 'j'
+ finally
+ Xpath 'k'
+ break
+ endtry
+ endwhile
+
+ while 1
+ try
+ Xpath 'l'
+ doautocmd User x3
+ catch /Vim:Interrupt/
+ Xpath 'm'
+ finally
+ Xpath 'n'
+ " ... but break loop for caught interrupt exception,
+ " or discard interrupt and break loop if $VIMNOINTTHROW
+ break
+ endtry
+ endwhile
+
+ if exists("*U1") | delfunction U1 | endif
+ if exists("*U2") | delfunction U2 | endif
+ if exists("*U3") | delfunction U3 | endif
+
+ try
+ Xpath 'o'
+ call U1()
+ catch /U1/
+ Xpath 'p'
+ endtry
+
+ while 1
+ try
+ Xpath 'q'
+ call U2()
+ catch /ASDF/
+ Xpath 'r'
+ finally
+ Xpath 's'
+ " ... but break loop for caught error exception,
+ " or discard error and break loop if $VIMNOERRTHROW
+ break
+ endtry
+ endwhile
+
+ while 1
+ try
+ Xpath 't'
+ call U3()
+ catch /Vim:Interrupt/
+ Xpath 'u'
+ finally
+ Xpath 'v'
+ " ... but break loop for caught interrupt exception,
+ " or discard interrupt and break loop if $VIMNOINTTHROW
+ break
+ endtry
+ endwhile
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ Xpath 'w'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('gahibjklcmnodpqerstfuvw', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 85: Error exceptions in autocommands for I/O command events {{{1
+"
+" When an I/O command is inside :try/:endtry, autocommands to be
+" executed after it should be skipped on an error (exception) in the
+" command itself or in autocommands to be executed before the command.
+" In the latter case, the I/O command should not be executed either.
+" Example 1: BufWritePre, :write, BufWritePost
+" Example 2: FileReadPre, :read, FileReadPost.
+"-------------------------------------------------------------------------------
+
+func Test_autocmd_error_io_exception()
+ let test =<< trim [CODE]
+ " Remove the autocommands for the events specified as arguments in all used
+ " autogroups.
+ func Delete_autocommands(...)
+ let augfile = tempname()
+ while 1
+ try
+ exec "redir >" . augfile
+ aug
+ redir END
+ exec "edit" augfile
+ g/^$/d
+ norm G$
+ let wrap = "w"
+ while search('\%( \|^\)\@<=.\{-}\%( \)\@=', wrap) > 0
+ let wrap = "W"
+ exec "norm y/ \n"
+ let argno = 1
+ while argno <= a:0
+ exec "au!" escape(@", " ") a:{argno}
+ let argno = argno + 1
+ endwhile
+ endwhile
+ catch /.*/
+ finally
+ bwipeout!
+ call delete(augfile)
+ break
+ endtry
+ endwhile
+ endfunc
+
+ call Delete_autocommands("BufWritePre", "BufWritePost")
+
+ while 1
+ try
+ try
+ let post = 0
+ aug TMP
+ au! BufWritePost * let post = 1
+ aug END
+ write /n/o/n/e/x/i/s/t/e/n/t
+ catch /^Vim(write):/
+ Xpath 'a'
+ call assert_match("E212: Can't open file for writing", v:exception)
+ finally
+ Xpath 'b'
+ call assert_equal(0, post)
+ au! TMP
+ aug! TMP
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'c'
+ break
+ endtry
+ endwhile
+
+ while 1
+ try
+ try
+ let post = 0
+ aug TMP
+ au! BufWritePre * asdf
+ au! BufWritePost * let post = 1
+ aug END
+ let tmpfile = tempname()
+ exec "write" tmpfile
+ catch /^Vim\((write)\)\=:/
+ Xpath 'd'
+ call assert_match('E492: Not an editor command', v:exception)
+ finally
+ Xpath 'e'
+ if filereadable(tmpfile)
+ call assert_report('should not get here')
+ endif
+ call assert_equal(0, post)
+ au! TMP
+ aug! TMP
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'f'
+ break
+ endtry
+ endwhile
+
+ call delete(tmpfile)
+
+ call Delete_autocommands("BufWritePre", "BufWritePost",
+ \ "BufReadPre", "BufReadPost", "FileReadPre", "FileReadPost")
+
+ while 1
+ try
+ try
+ let post = 0
+ aug TMP
+ au! FileReadPost * let post = 1
+ aug END
+ let caught = 0
+ read /n/o/n/e/x/i/s/t/e/n/t
+ catch /^Vim(read):/
+ Xpath 'g'
+ call assert_match("E484: Can't open file", v:exception)
+ finally
+ Xpath 'h'
+ call assert_equal(0, post)
+ au! TMP
+ aug! TMP
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'i'
+ break
+ endtry
+ endwhile
+
+ while 1
+ try
+ let infile = tempname()
+ let tmpfile = tempname()
+ call writefile(["XYZ"], infile)
+ exec "edit" tmpfile
+ try
+ Xpath 'j'
+ try
+ let post = 0
+ aug TMP
+ au! FileReadPre * asdf
+ au! FileReadPost * let post = 1
+ aug END
+ exec "0read" infile
+ catch /^Vim\((read)\)\=:/
+ Xpath 'k'
+ call assert_match('E492: Not an editor command', v:exception)
+ finally
+ Xpath 'l'
+ if getline("1") == "XYZ"
+ call assert_report('should not get here')
+ endif
+ call assert_equal(0, post)
+ au! TMP
+ aug! TMP
+ endtry
+ finally
+ Xpath 'm'
+ bwipeout!
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'n'
+ break
+ endtry
+ endwhile
+
+ call delete(infile)
+ call delete(tmpfile)
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcdefghijklmn', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
"-------------------------------------------------------------------------------
" Test 87 using (expr) ? funcref : funcref {{{1
"
@@ -1370,10 +6723,10 @@ endfunc
" Test 95: lines of :append, :change, :insert {{{1
"-------------------------------------------------------------------------------
-function! DefineFunction(name, body)
+func DefineFunction(name, body)
let func = join(['function! ' . a:name . '()'] + a:body + ['endfunction'], "\n")
exec func
-endfunction
+endfunc
func Test_script_lines()
" :append
@@ -1443,7 +6796,7 @@ endfunc
" Undefined behavior was detected by ubsan with line continuation
" after an empty line.
"-------------------------------------------------------------------------------
-func Test_script_emty_line_continuation()
+func Test_script_empty_line_continuation()
\
endfunc
@@ -1495,55 +6848,6 @@ func Test_bitwise_functions()
call assert_fails("call invert({})", 'E728:')
endfunc
-" Test trailing text after :endfunction {{{1
-func Test_endfunction_trailing()
- call assert_false(exists('*Xtest'))
-
- exe "func Xtest()\necho 'hello'\nendfunc\nlet done = 'yes'"
- call assert_true(exists('*Xtest'))
- call assert_equal('yes', done)
- delfunc Xtest
- unlet done
-
- exe "func Xtest()\necho 'hello'\nendfunc|let done = 'yes'"
- call assert_true(exists('*Xtest'))
- call assert_equal('yes', done)
- delfunc Xtest
- unlet done
-
- " trailing line break
- exe "func Xtest()\necho 'hello'\nendfunc\n"
- call assert_true(exists('*Xtest'))
- delfunc Xtest
-
- set verbose=1
- exe "func Xtest()\necho 'hello'\nendfunc \" garbage"
- call assert_notmatch('W22:', split(execute('1messages'), "\n")[0])
- call assert_true(exists('*Xtest'))
- delfunc Xtest
-
- exe "func Xtest()\necho 'hello'\nendfunc garbage"
- call assert_match('W22:', split(execute('1messages'), "\n")[0])
- call assert_true(exists('*Xtest'))
- delfunc Xtest
- set verbose=0
-
- function Foo()
- echo 'hello'
- endfunction | echo 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
- delfunc Foo
-endfunc
-
-func Test_delfunction_force()
- delfunc! Xtest
- delfunc! Xtest
- func Xtest()
- echo 'nothing'
- endfunc
- delfunc! Xtest
- delfunc! Xtest
-endfunc
-
" Test using bang after user command {{{1
func Test_user_command_with_bang()
command -bang Nieuw let nieuw = 1
@@ -1553,26 +6857,6 @@ func Test_user_command_with_bang()
delcommand Nieuw
endfunc
-" Test for script-local function
-func <SID>DoLast()
- call append(line('$'), "last line")
-endfunc
-
-func s:DoNothing()
- call append(line('$'), "nothing line")
-endfunc
-
-func Test_script_local_func()
- set nocp nomore viminfo+=nviminfo
- new
- nnoremap <buffer> _x :call <SID>DoNothing()<bar>call <SID>DoLast()<bar>delfunc <SID>DoNothing<bar>delfunc <SID>DoLast<cr>
-
- normal _x
- call assert_equal('nothing line', getline(2))
- call assert_equal('last line', getline(3))
- enew! | close
-endfunc
-
func Test_script_expand_sfile()
let lines =<< trim END
func s:snr()
@@ -1653,6 +6937,22 @@ func Test_compound_assignment_operators()
call assert_equal(4.2, x)
call assert_fails('let x %= 0.5', 'E734')
call assert_fails('let x .= "f"', 'E734')
+ let x = !3.14
+ call assert_equal(0.0, x)
+
+ " integer and float operations
+ let x = 1
+ let x *= 2.1
+ call assert_equal(2.1, x)
+ let x = 1
+ let x /= 0.25
+ call assert_equal(4.0, x)
+ let x = 1
+ call assert_fails('let x %= 0.25', 'E734:')
+ let x = 1
+ call assert_fails('let x .= 0.25', 'E734:')
+ let x = 1.0
+ call assert_fails('let x += [1.1]', 'E734:')
endif
" Test for environment variable
@@ -1716,87 +7016,6 @@ func Test_unlet_env()
call assert_equal('', $TESTVAR)
endfunc
-func Test_funccall_garbage_collect()
- func Func(x, ...)
- call add(a:x, a:000)
- endfunc
- call Func([], [])
- " Must not crash cause by invalid freeing
- call test_garbagecollect_now()
- call assert_true(v:true)
- delfunc Func
-endfunc
-
-func Test_function_defined_line()
- if has('gui_running')
- " Can't catch the output of gvim.
- return
- endif
-
- let lines =<< trim [CODE]
- " F1
- func F1()
- " F2
- func F2()
- "
- "
- "
- return
- endfunc
- " F3
- execute "func F3()\n\n\n\nreturn\nendfunc"
- " F4
- execute "func F4()\n
- \\n
- \\n
- \\n
- \return\n
- \endfunc"
- endfunc
- " F5
- execute "func F5()\n\n\n\nreturn\nendfunc"
- " F6
- execute "func F6()\n
- \\n
- \\n
- \\n
- \return\n
- \endfunc"
- call F1()
- verbose func F1
- verbose func F2
- verbose func F3
- verbose func F4
- verbose func F5
- verbose func F6
- qall!
- [CODE]
-
- call writefile(lines, '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:]]*')
- call assert_match(' line 2$', m)
-
- let m = matchstr(res, 'function F2()[^[:print:]]*[[:print:]]*')
- call assert_match(' line 4$', m)
-
- let m = matchstr(res, 'function F3()[^[:print:]]*[[:print:]]*')
- call assert_match(' line 11$', m)
-
- let m = matchstr(res, 'function F4()[^[:print:]]*[[:print:]]*')
- call assert_match(' line 13$', m)
-
- let m = matchstr(res, 'function F5()[^[:print:]]*[[:print:]]*')
- call assert_match(' line 21$', m)
-
- let m = matchstr(res, 'function F6()[^[:print:]]*[[:print:]]*')
- call assert_match(' line 23$', m)
-
- 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')
@@ -1829,15 +7048,19 @@ func Test_missing_end()
endtry
call assert_equal(1, caught_e733)
+ " Using endfunc with :if
+ call assert_fails('exe "if 1 | endfunc | endif"', 'E193:')
+
" Missing 'in' in a :for statement
call assert_fails('for i range(1) | endfor', 'E690:')
+
+ " Incorrect number of variables in for
+ call assert_fails('for [i,] in range(3) | endfor', 'E475:')
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
+ CheckRunVimInTerminal
let lines =<< trim [SCRIPT]
" Deep nesting of if ... endif
@@ -1875,6 +7098,15 @@ func Test_deep_nest()
@a
let @a = ''
endfunc
+
+ " Deep nesting of function ... endfunction
+ func Test5()
+ let @a = join(repeat(['function X()'], 51), "\n")
+ let @a ..= "\necho v:true\n"
+ let @a ..= join(repeat(['endfunction'], 51), "\n")
+ @a
+ let @a = ''
+ endfunc
[SCRIPT]
call writefile(lines, 'Xscript')
@@ -1882,20 +7114,31 @@ func Test_deep_nest()
" Deep nesting of if ... endif
call term_sendkeys(buf, ":call Test1()\n")
+ call term_wait(buf)
call WaitForAssert({-> assert_match('^E579:', term_getline(buf, 5))})
" Deep nesting of for ... endfor
call term_sendkeys(buf, ":call Test2()\n")
+ call term_wait(buf)
call WaitForAssert({-> assert_match('^E585:', term_getline(buf, 5))})
" Deep nesting of while ... endwhile
call term_sendkeys(buf, ":call Test3()\n")
+ call term_wait(buf)
call WaitForAssert({-> assert_match('^E585:', term_getline(buf, 5))})
" Deep nesting of try ... endtry
call term_sendkeys(buf, ":call Test4()\n")
+ call term_wait(buf)
call WaitForAssert({-> assert_match('^E601:', term_getline(buf, 5))})
+ " Deep nesting of function ... endfunction
+ call term_sendkeys(buf, ":call Test5()\n")
+ call term_wait(buf)
+ call WaitForAssert({-> assert_match('^E1058:', term_getline(buf, 4))})
+ call term_sendkeys(buf, "\<C-C>\n")
+ call term_wait(buf)
+
"let l = ''
"for i in range(1, 6)
" let l ..= term_getline(buf, i) . "\n"
@@ -1906,14 +7149,113 @@ func Test_deep_nest()
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
+" Test for errors in converting to float from various types {{{1
+func Test_float_conversion_errors()
+ if has('float')
+ call assert_fails('let x = 4.0 % 2.0', 'E804')
+ call assert_fails('echo 1.1[0]', 'E806')
+ call assert_fails('echo sort([function("min"), 1], "f")', 'E891:')
+ call assert_fails('echo 3.2 == "vim"', 'E892:')
+ call assert_fails('echo sort([[], 1], "f")', 'E893:')
+ call assert_fails('echo sort([{}, 1], "f")', 'E894:')
+ call assert_fails('echo 3.2 == v:true', 'E362:')
+ " call assert_fails('echo 3.2 == v:none', 'E907:')
+ endif
+endfunc
+
+" invalid function names {{{1
+func Test_invalid_function_names()
+ " function name not starting with capital
+ let caught_e128 = 0
+ try
+ func! g:test()
+ echo "test"
+ endfunc
+ catch /E128:/
+ let caught_e128 = 1
+ endtry
+ call assert_equal(1, caught_e128)
+
+ " function name includes a colon
+ let caught_e884 = 0
+ try
+ func! b:test()
+ echo "test"
+ endfunc
+ catch /E884:/
+ let caught_e884 = 1
+ endtry
+ call assert_equal(1, caught_e884)
+
+ " function name followed by #
+ let caught_e128 = 0
+ try
+ func! test2() "#
+ echo "test2"
+ endfunc
+ catch /E128:/
+ let caught_e128 = 1
+ endtry
+ call assert_equal(1, caught_e128)
+
+ " function name starting with/without "g:", buffer-local funcref.
+ function! g:Foo(n)
+ return 'called Foo(' . a:n . ')'
+ endfunction
+ let b:my_func = function('Foo')
+ call assert_equal('called Foo(1)', b:my_func(1))
+ call assert_equal('called Foo(2)', g:Foo(2))
+ call assert_equal('called Foo(3)', Foo(3))
+ delfunc g:Foo
+
+ " script-local function used in Funcref must exist.
+ let lines =<< trim END
+ func s:Testje()
+ return "foo"
+ endfunc
+ let Bar = function('s:Testje')
+ call assert_equal(0, exists('s:Testje'))
+ call assert_equal(1, exists('*s:Testje'))
+ call assert_equal(1, exists('Bar'))
+ call assert_equal(1, exists('*Bar'))
+ END
+ call writefile(lines, 'Xscript')
+ source Xscript
+ call delete('Xscript')
+endfunc
+
+" substring and variable name {{{1
+func Test_substring_var()
+ let str = 'abcdef'
+ let n = 3
+ call assert_equal('def', str[n:])
+ call assert_equal('abcd', str[:n])
+ call assert_equal('d', str[n:n])
+ unlet n
+ let nn = 3
+ call assert_equal('def', str[nn:])
+ call assert_equal('abcd', str[:nn])
+ call assert_equal('d', str[nn:nn])
+ unlet nn
+ let b:nn = 4
+ call assert_equal('ef', str[b:nn:])
+ call assert_equal('abcde', str[:b:nn])
+ call assert_equal('e', str[b:nn:b:nn])
+ unlet b:nn
+endfunc
+
+" Test using s: with a typed command {{{1
+func Test_typed_script_var()
+ CheckRunVimInTerminal
+
+ let buf = RunVimInTerminal('', {'rows': 6})
+
+ " Deep nesting of if ... endif
+ call term_sendkeys(buf, ":echo get(s:, 'foo', 'x')\n")
+ call TermWait(buf)
+ call WaitForAssert({-> assert_match('^E116:', term_getline(buf, 5))})
+
+ call StopVimInTerminal(buf)
endfunc
func Test_for_over_string()
diff --git a/src/nvim/testdir/test_virtualedit.vim b/src/nvim/testdir/test_virtualedit.vim
index 522ca17675..2bf8c3fc77 100644
--- a/src/nvim/testdir/test_virtualedit.vim
+++ b/src/nvim/testdir/test_virtualedit.vim
@@ -80,6 +80,10 @@ func Test_edit_change()
call setline(1, "\t⒌")
normal Cx
call assert_equal('x', getline(1))
+ " Do a visual block change
+ call setline(1, ['a', 'b', 'c'])
+ exe "normal gg3l\<C-V>2jcx"
+ call assert_equal(['a x', 'b x', 'c x'], getline(1, '$'))
bwipe!
set virtualedit=
endfunc
@@ -289,6 +293,16 @@ func Test_replace_after_eol()
call append(0, '"r"')
normal gg$5lrxa
call assert_equal('"r" x', getline(1))
+ " visual block replace
+ %d _
+ call setline(1, ['a', '', 'b'])
+ exe "normal 2l\<C-V>2jrx"
+ call assert_equal(['a x', ' x', 'b x'], getline(1, '$'))
+ " visual characterwise selection replace after eol
+ %d _
+ call setline(1, 'a')
+ normal 4lv2lrx
+ call assert_equal('a xxx', getline(1))
bwipe!
set virtualedit=
endfunc
@@ -346,6 +360,48 @@ func Test_yank_paste_small_del_reg()
set virtualedit=
endfunc
+" Test for delete that breaks a tab into spaces
+func Test_delete_break_tab()
+ new
+ call setline(1, "one\ttwo")
+ set virtualedit=all
+ normal v3ld
+ call assert_equal(' two', getline(1))
+ set virtualedit&
+ close!
+endfunc
+
+" Test for using <BS>, <C-W> and <C-U> in virtual edit mode
+" to erase character, word and line.
+func Test_ve_backspace()
+ new
+ call setline(1, 'sample')
+ set virtualedit=all
+ set backspace=indent,eol,start
+ exe "normal 15|i\<BS>\<BS>"
+ call assert_equal([0, 1, 7, 5], getpos('.'))
+ exe "normal 15|i\<C-W>"
+ call assert_equal([0, 1, 6, 0], getpos('.'))
+ exe "normal 15|i\<C-U>"
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+ set backspace&
+ set virtualedit&
+ close!
+endfunc
+
+" Test for delete (x) on EOL character and after EOL
+func Test_delete_past_eol()
+ new
+ call setline(1, "ab")
+ set virtualedit=all
+ exe "normal 2lx"
+ call assert_equal('ab', getline(1))
+ exe "normal 10lx"
+ call assert_equal('ab', getline(1))
+ set virtualedit&
+ bw!
+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'
@@ -481,4 +537,53 @@ func Test_global_local_virtualedit()
set virtualedit&
endfunc
+func Test_virtualedit_mouse()
+ let save_mouse = &mouse
+ set mouse=a
+ set virtualedit=all
+ new
+
+ call setline(1, ["text\tword"])
+ redraw
+ call Ntest_setmouse(1, 4)
+ call feedkeys("\<LeftMouse>", "xt")
+ call assert_equal([0, 1, 4, 0, 4], getcurpos())
+ call Ntest_setmouse(1, 5)
+ call feedkeys("\<LeftMouse>", "xt")
+ call assert_equal([0, 1, 5, 0, 5], getcurpos())
+ call Ntest_setmouse(1, 6)
+ call feedkeys("\<LeftMouse>", "xt")
+ call assert_equal([0, 1, 5, 1, 6], getcurpos())
+ call Ntest_setmouse(1, 7)
+ call feedkeys("\<LeftMouse>", "xt")
+ call assert_equal([0, 1, 5, 2, 7], getcurpos())
+ call Ntest_setmouse(1, 8)
+ call feedkeys("\<LeftMouse>", "xt")
+ call assert_equal([0, 1, 5, 3, 8], getcurpos())
+ call Ntest_setmouse(1, 9)
+ call feedkeys("\<LeftMouse>", "xt")
+ call assert_equal([0, 1, 6, 0, 9], getcurpos())
+ call Ntest_setmouse(1, 15)
+ call feedkeys("\<LeftMouse>", "xt")
+ call assert_equal([0, 1, 10, 2, 15], getcurpos())
+
+ bwipe!
+ let &mouse = save_mouse
+ set virtualedit&
+endfunc
+
+" this was replacing the NUL at the end of the line
+func Test_virtualedit_replace_after_tab()
+ new
+ s/\v/ 0
+ set ve=all
+ let @" = ''
+ sil! norm vPvr0
+
+ call assert_equal("\t0", getline(1))
+ set ve&
+ bwipe!
+endfunc
+
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim
index 9c1ad0c099..14d62089cf 100644
--- a/src/nvim/testdir/test_visual.vim
+++ b/src/nvim/testdir/test_visual.vim
@@ -225,6 +225,15 @@ func Test_virtual_replace()
exe "normal iabcdefghijklmnopqrst\<Esc>0gRAB\tIJKLMNO\tQR"
call assert_equal(['AB......CDEFGHI.Jkl',
\ 'AB IJKLMNO QRst'], getline(12, 13))
+
+ " Test inserting Tab with 'noexpandtab' and 'softabstop' set to 4
+ %d
+ call setline(1, 'aaaaaaaaaaaaa')
+ set softtabstop=4
+ exe "normal gggR\<Tab>\<Tab>x"
+ call assert_equal("\txaaaa", getline(1))
+ set softtabstop&
+
enew!
set noai bs&vim
if exists('save_t_kD')
@@ -474,6 +483,18 @@ func Test_visual_block_put()
bw!
endfunc
+func Test_visual_block_put_invalid()
+ enew!
+ behave mswin
+ norm yy
+ norm v)Ps/^/
+ " this was causing the column to become negative
+ silent norm ggv)P
+
+ bwipe!
+ behave xterm
+endfunc
+
" Visual modes (v V CTRL-V) followed by an operator; count; repeating
func Test_visual_mode_op()
new
@@ -1269,7 +1290,7 @@ func Test_visual_block_with_virtualedit()
endfunc
func Test_visual_block_ctrl_w_f()
- " Emtpy block selected in new buffer should not result in an error.
+ " Empty block selected in new buffer should not result in an error.
au! BufNew foo sil norm f
edit foo
@@ -1317,6 +1338,18 @@ func Test_visual_reselect_with_count()
call delete('XvisualReselect')
endfunc
+func Test_visual_reselect_exclusive()
+ new
+ call setline(1, ['abcde', 'abcde'])
+ set selection=exclusive
+ normal 1G0viwd
+ normal 2G01vd
+ call assert_equal(['', ''], getline(1, 2))
+
+ set selection&
+ bwipe!
+endfunc
+
func Test_visual_block_insert_round_off()
new
" The number of characters are tuned to fill a 4096 byte allocated block,
@@ -1492,5 +1525,4 @@ func Test_switch_buffer_ends_visual_mode()
exe 'bwipe!' buf2
endfunc
-
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_winbuf_close.vim b/src/nvim/testdir/test_winbuf_close.vim
index f4878c2397..26b4ba8778 100644
--- a/src/nvim/testdir/test_winbuf_close.vim
+++ b/src/nvim/testdir/test_winbuf_close.vim
@@ -115,7 +115,7 @@ func Test_winbuf_close()
call assert_equal('Xtest2', bufname('%'))
quit!
call assert_equal('Xtest3', bufname('%'))
- call assert_fails('silent! quit!', 'E162')
+ call assert_fails('silent! quit!', 'E37')
call assert_equal('Xtest1', bufname('%'))
call delete('Xtest1')
@@ -192,7 +192,23 @@ func Test_tabwin_close()
call win_execute(l:wid, 'close')
" Should not crash.
call assert_true(v:true)
- %bwipe!
+
+ " This tests closing a window in another tab, while leaving the tab open
+ " i.e. two windows in another tab.
+ tabedit
+ let w:this_win = 42
+ new
+ let othertab_wid = win_getid()
+ tabprevious
+ call win_execute(othertab_wid, 'q')
+ " drawing the tabline helps check that the other tab's windows and buffers
+ " are still valid
+ redrawtabline
+ " but to be certain, ensure we can focus the other tab too
+ tabnext
+ call assert_equal(42, w:this_win)
+
+ bwipe!
endfunc
" Test when closing a split window (above/below) restores space to the window
diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim
index 83a3216534..c25b1f1157 100644
--- a/src/nvim/testdir/test_window_cmd.vim
+++ b/src/nvim/testdir/test_window_cmd.vim
@@ -1,5 +1,8 @@
" Tests for window cmd (:wincmd, :split, :vsplit, :resize and etc...)
+source check.vim
+source screendump.vim
+
func Test_window_cmd_ls0_with_split()
set ls=0
set splitbelow
@@ -343,6 +346,46 @@ func Test_window_height()
bw Xa Xb Xc
endfunc
+func Test_wincmd_equal()
+ edit Xone
+ below split Xtwo
+ rightbelow vsplit Xthree
+ call assert_equal('Xone', bufname(winbufnr(1)))
+ call assert_equal('Xtwo', bufname(winbufnr(2)))
+ call assert_equal('Xthree', bufname(winbufnr(3)))
+
+ " Xone and Xtwo should be about the same height
+ let [wh1, wh2] = [winheight(1), winheight(2)]
+ call assert_inrange(wh1 - 1, wh1 + 1, wh2)
+ " Xtwo and Xthree should be about the same width
+ let [ww2, ww3] = [winwidth(2), winwidth(3)]
+ call assert_inrange(ww2 - 1, ww2 + 1, ww3)
+
+ 1wincmd w
+ 10wincmd _
+ 2wincmd w
+ 20wincmd |
+ call assert_equal(10, winheight(1))
+ call assert_equal(20, winwidth(2))
+
+ " equalizing horizontally doesn't change the heights
+ hor wincmd =
+ call assert_equal(10, winheight(1))
+ let [ww2, ww3] = [winwidth(2), winwidth(3)]
+ call assert_inrange(ww2 - 1, ww2 + 1, ww3)
+
+ 2wincmd w
+ 20wincmd |
+ call assert_equal(20, winwidth(2))
+ " equalizing vertically doesn't change the widths
+ vert wincmd =
+ call assert_equal(20, winwidth(2))
+ let [wh1, wh2] = [winheight(1), winheight(2)]
+ call assert_inrange(wh1 - 1, wh1 + 1, wh2)
+
+ bwipe Xone Xtwo Xthree
+endfunc
+
func Test_window_width()
e Xa
vsplit Xb
@@ -393,7 +436,15 @@ func Test_window_width()
call assert_inrange(ww1, ww1 + 1, ww2)
call assert_inrange(ww3, ww3 + 1, ww2)
- bw Xa Xb Xc
+ " when the current window width is less than the new 'winwidth', the current
+ " window width should be increased.
+ enew | only
+ split
+ 10vnew
+ set winwidth=15
+ call assert_equal(15, winwidth(0))
+
+ %bw!
endfunc
func Test_equalalways_on_close()
@@ -450,10 +501,13 @@ func Test_win_screenpos()
call assert_equal([1, 32], win_screenpos(2))
call assert_equal([12, 1], win_screenpos(3))
call assert_equal([0, 0], win_screenpos(4))
+ call assert_fails('let l = win_screenpos([])', 'E745:')
only
endfunc
func Test_window_jump_tag()
+ CheckFeature quickfix
+
help
/iccf
call assert_match('^|iccf|', getline('.'))
@@ -491,6 +545,7 @@ func Test_window_newtab()
call assert_equal(2, tabpagenr('$'))
call assert_equal(['Xb', 'Xa'], map(tabpagebuflist(1), 'bufname(v:val)'))
call assert_equal(['Xc' ], map(2->tabpagebuflist(), 'bufname(v:val)'))
+ call assert_equal(['Xc' ], map(tabpagebuflist(), 'bufname(v:val)'))
%bw!
endfunc
@@ -541,6 +596,11 @@ func Test_window_contents()
call assert_equal(59, line("w0"))
call assert_equal('59 ', s3)
+ %d
+ call setline(1, ['one', 'two', 'three'])
+ call assert_equal(1, line('w0'))
+ call assert_equal(3, line('w$'))
+
bwipeout!
call test_garbagecollect_now()
endfunc
@@ -677,6 +737,7 @@ func Test_relative_cursor_position_in_one_line_window()
only!
bwipe!
+ call assert_fails('call winrestview(v:_null_dict)', 'E474:')
endfunc
func Test_relative_cursor_position_after_move_and_resize()
@@ -853,6 +914,10 @@ func Test_winnr()
call assert_fails("echo winnr('ll')", 'E15:')
call assert_fails("echo winnr('5')", 'E15:')
call assert_equal(4, winnr('0h'))
+ call assert_fails("let w = winnr([])", 'E730:')
+ call assert_equal('unknown', win_gettype(-1))
+ call assert_equal(-1, winheight(-1))
+ call assert_equal(-1, winwidth(-1))
tabnew
call assert_equal(8, tabpagewinnr(1, 'j'))
@@ -873,9 +938,12 @@ func Test_winrestview()
call assert_equal(view, winsaveview())
bwipe!
+ call assert_fails('call winrestview(v:_null_dict)', 'E474:')
endfunc
func Test_win_splitmove()
+ CheckFeature quickfix
+
edit a
leftabove split b
leftabove vsplit c
@@ -901,6 +969,7 @@ func Test_win_splitmove()
call assert_equal(bufname(winbufnr(2)), 'b')
call assert_equal(bufname(winbufnr(3)), 'a')
call assert_equal(bufname(winbufnr(4)), 'd')
+ call assert_fails('call win_splitmove(winnr(), winnr("k"), v:_null_dict)', 'E474:')
only | bd
call assert_fails('call win_splitmove(winnr(), 123)', 'E957:')
@@ -1073,6 +1142,18 @@ func Test_split_cmds_with_no_room()
call Run_noroom_for_newwindow_test('v')
endfunc
+" Test for various wincmd failures
+func Test_wincmd_fails()
+ only!
+ call assert_beeps("normal \<C-W>w")
+ call assert_beeps("normal \<C-W>p")
+ call assert_beeps("normal \<C-W>gk")
+ call assert_beeps("normal \<C-W>r")
+ call assert_beeps("normal \<C-W>K")
+ call assert_beeps("normal \<C-W>H")
+ call assert_beeps("normal \<C-W>2gt")
+endfunc
+
func Test_window_resize()
" Vertical :resize (absolute, relative, min and max size).
vsplit
@@ -1158,7 +1239,7 @@ endfunc
" Test for jumping to a vertical/horizontal neighbor window based on the
" current cursor position
-func Test_window_goto_neightbor()
+func Test_window_goto_neighbor()
%bw!
" Vertical window movement
@@ -1337,17 +1418,20 @@ func Test_win_move_separator()
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})
@@ -1355,6 +1439,13 @@ func Test_win_move_separator()
call assert_false(win_move_separator(id, 1))
call assert_equal(w, winwidth(id))
call nvim_win_close(id, 1)
+
+ " check that using another tabpage fails without crash
+ let id = win_getid()
+ tabnew
+ call assert_fails('call win_move_separator(id, -1)', 'E1308:')
+ tabclose
+
%bwipe!
endfunc
@@ -1403,17 +1494,20 @@ func Test_win_move_statusline()
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})
@@ -1421,7 +1515,319 @@ func Test_win_move_statusline()
call assert_false(win_move_statusline(id, 1))
call assert_equal(h, winheight(id))
call nvim_win_close(id, 1)
+
+ " check that using another tabpage fails without crash
+ let id = win_getid()
+ tabnew
+ call assert_fails('call win_move_statusline(id, -1)', 'E1308:')
+ tabclose
+
%bwipe!
endfunc
+func Test_win_equal_last_status()
+ let save_lines = &lines
+ set lines=20
+ set splitbelow
+ set laststatus=0
+
+ split | split | quit
+ call assert_equal(winheight(1), winheight(2))
+
+ let &lines = save_lines
+ set splitbelow&
+ set laststatus&
+endfunc
+
+" Test "screen" and "cursor" values for 'splitkeep' with a sequence of
+" split operations for various options: with and without a winbar,
+" tabline, for each possible value of 'laststatus', 'scrolloff',
+" 'equalalways', and with the cursor at the top, middle and bottom.
+func Test_splitkeep_options()
+ " disallow window resizing
+ " let save_WS = &t_WS
+ " set t_WS=
+
+ let gui = has("gui_running")
+ inoremap <expr> c "<cmd>copen<bar>wincmd k<CR>"
+ for run in range(0, 20)
+ let &splitkeep = run > 10 ? 'topline' : 'screen'
+ let &scrolloff = (!(run % 4) ? 0 : run)
+ let &laststatus = (run % 3)
+ let &splitbelow = (run % 3)
+ let &equalalways = (run % 2)
+ " Nvim: both windows have a winbar after splitting
+ " let wsb = (run % 2) && &splitbelow
+ let wsb = 0
+ let tl = (gui ? 0 : ((run % 5) ? 1 : 0))
+ let pos = !(run % 3) ? 'H' : ((run % 2) ? 'M' : 'L')
+ tabnew | tabonly! | redraw
+ execute (run % 5) ? 'tabnew' : ''
+ " execute (run % 2) ? 'nnoremenu 1.10 WinBar.Test :echo' : ''
+ let &winbar = (run % 2) ? '%f' : ''
+ call setline(1, range(1, 256))
+ " No scroll for restore_snapshot
+ norm G
+ try
+ copen | close | colder
+ catch /E380/
+ endtry
+ call assert_equal(257 - winheight(0), line("w0"))
+
+ " No scroll for firstwin horizontal split
+ execute 'norm gg' . pos
+ split | redraw | wincmd k
+ call assert_equal(1, line("w0"))
+ call assert_equal(&scroll, winheight(0) / 2)
+ wincmd j
+ call assert_equal(&spk == 'topline' ? 1 : win_screenpos(0)[0] - tl - wsb, line("w0"))
+
+ " No scroll when resizing windows
+ wincmd k | resize +2 | redraw
+ call assert_equal(1, line("w0"))
+ wincmd j
+ call assert_equal(&spk == 'topline' ? 1 : win_screenpos(0)[0] - tl - wsb, line("w0"))
+
+ " No scroll when dragging statusline
+ call win_move_statusline(1, -3)
+ call assert_equal(&spk == 'topline' ? 1 : win_screenpos(0)[0] - tl - wsb, line("w0"))
+ wincmd k
+ call assert_equal(1, line("w0"))
+
+ " No scroll when changing shellsize
+ set lines+=2
+ call assert_equal(1, line("w0"))
+ wincmd j
+ call assert_equal(&spk == 'topline' ? 1 : win_screenpos(0)[0] - tl - wsb, line("w0"))
+ set lines-=2
+ call assert_equal(&spk == 'topline' ? 1 : win_screenpos(0)[0] - tl - wsb, line("w0"))
+ wincmd k
+ call assert_equal(1, line("w0"))
+
+ " No scroll when equalizing windows
+ wincmd =
+ call assert_equal(1, line("w0"))
+ wincmd j
+ call assert_equal(&spk == 'topline' ? 1 : win_screenpos(0)[0] - tl - wsb, line("w0"))
+ wincmd k
+ call assert_equal(1, line("w0"))
+
+ " No scroll in windows split multiple times
+ vsplit | split | 4wincmd w
+ call assert_equal(&spk == 'topline' ? 1 : win_screenpos(0)[0] - tl - wsb, line("w0"))
+ 1wincmd w | quit | wincmd l | split
+ call assert_equal(&spk == 'topline' ? 1 : win_screenpos(0)[0] - tl - wsb, line("w0"))
+ wincmd j
+ call assert_equal(&spk == 'topline' ? 1 : win_screenpos(0)[0] - tl - wsb, line("w0"))
+
+ " No scroll in small window
+ 2wincmd w | only | 5split | wincmd k
+ call assert_equal(1, line("w0"))
+ wincmd j
+ call assert_equal(&spk == 'topline' ? 1 : win_screenpos(0)[0] - tl - wsb, line("w0"))
+
+ " No scroll for vertical split
+ quit | vsplit | wincmd l
+ call assert_equal(1, line("w0"))
+ wincmd h
+ call assert_equal(1, line("w0"))
+
+ " No scroll in windows split and quit multiple times
+ quit | redraw | split | split | quit | redraw
+ call assert_equal(&spk == 'topline' ? 1 : win_screenpos(0)[0] - tl - wsb, line("w0"))
+
+ " No scroll for new buffer
+ 1wincmd w | only | copen | wincmd k
+ call assert_equal(1, line("w0"))
+ only
+ call assert_equal(1, line("w0"))
+ above copen | wincmd j
+ call assert_equal(&spk == 'topline' ? 1 : win_screenpos(0)[0] - tl, line("w0"))
+
+ " No scroll when opening cmdwin, and no cursor move when closing cmdwin.
+ only | norm ggL
+ let curpos = getcurpos()
+ norm q:
+ call assert_equal(1, line("w0"))
+ call assert_equal(curpos, getcurpos())
+
+ " Scroll when cursor becomes invalid in insert mode
+ norm Lic
+ call assert_equal(curpos, getcurpos())
+
+ " No scroll when topline not equal to 1
+ only | execute "norm gg5\<C-e>" | split | wincmd k
+ call assert_equal(6, line("w0"))
+ wincmd j
+ call assert_equal(&spk == 'topline' ? 6 : 5 + win_screenpos(0)[0] - tl - wsb, line("w0"))
+ endfor
+
+ tabnew | tabonly! | %bwipeout!
+ iunmap c
+ set scrolloff&
+ set splitbelow&
+ set laststatus&
+ set equalalways&
+ set splitkeep&
+ " let &t_WS = save_WS
+endfunc
+
+function Test_splitkeep_cmdwin_cursor_position()
+ set splitkeep=screen
+ call setline(1, range(&lines))
+
+ " No scroll when cursor is at near bottom of window and cusor position
+ " recompution (done by line('w0') in this test) happens while in cmdwin.
+ normal! G
+ let firstline = line('w0')
+ autocmd CmdwinEnter * ++once autocmd WinEnter * ++once call line('w0')
+ execute "normal! q:\<C-w>q"
+ redraw!
+ call assert_equal(firstline, line('w0'))
+
+ " User script can change cursor position successfully while in cmdwin and it
+ " shouldn't be changed when closing cmdwin.
+ execute "normal! Gq:\<Cmd>call win_execute(winnr('#')->win_getid(), 'call cursor(1, 1)')\<CR>\<C-w>q"
+ call assert_equal(1, line('.'))
+ call assert_equal(1, col('.'))
+
+ execute "normal! Gq:\<Cmd>autocmd WinEnter * ++once call cursor(1, 1)\<CR>\<C-w>q"
+ call assert_equal(1, line('.'))
+ call assert_equal(1, col('.'))
+
+ %bwipeout!
+ set splitkeep&
+endfunction
+
+function Test_splitkeep_misc()
+ set splitkeep=screen
+ set splitbelow
+
+ call setline(1, range(1, &lines))
+ norm Gzz
+ let top = line('w0')
+ " No scroll when aucmd_win is opened
+ call setbufvar(bufnr("test", 1) , '&buftype', 'nofile')
+ call assert_equal(top, line('w0'))
+ " No scroll when tab is changed/closed
+ tab help | close
+ call assert_equal(top, line('w0'))
+ " No scroll when help is closed and buffer line count < window height
+ norm ggdG
+ call setline(1, range(1, &lines - 10))
+ norm G
+ let top = line('w0')
+ help | quit
+ call assert_equal(top, line('w0'))
+ " No error when resizing window in autocmd and buffer length changed
+ autocmd FileType qf exe "resize" line('$')
+ cexpr getline(1, '$')
+ copen
+ wincmd p
+ norm dd
+ cexpr getline(1, '$')
+
+ %bwipeout!
+ set splitbelow&
+ set splitkeep&
+endfunc
+
+function Test_splitkeep_callback()
+ CheckScreendump
+ let lines =<< trim END
+ set splitkeep=screen
+ call setline(1, range(&lines))
+ function C1(a, b)
+ split | wincmd p
+ endfunction
+ function C2(a, b)
+ close | split
+ endfunction
+ nn j <cmd>call job_start([&sh, &shcf, "true"], { 'exit_cb': 'C1' })<CR>
+ nn t <cmd>call popup_create(term_start([&sh, &shcf, "true"],
+ \ { 'hidden': 1, 'exit_cb': 'C2' }), {})<CR>
+ END
+ call writefile(lines, 'XTestSplitkeepCallback', 'D')
+ let buf = RunVimInTerminal('-S XTestSplitkeepCallback', #{rows: 8})
+
+ call term_sendkeys(buf, "j")
+ call VerifyScreenDump(buf, 'Test_splitkeep_callback_1', {})
+
+ call term_sendkeys(buf, ":quit\<CR>Ht")
+ call VerifyScreenDump(buf, 'Test_splitkeep_callback_2', {})
+
+ call term_sendkeys(buf, ":set sb\<CR>:quit\<CR>Gj")
+ call VerifyScreenDump(buf, 'Test_splitkeep_callback_3', {})
+
+ call term_sendkeys(buf, ":quit\<CR>Gt")
+ call VerifyScreenDump(buf, 'Test_splitkeep_callback_4', {})
+
+ call StopVimInTerminal(buf)
+endfunc
+
+function Test_splitkeep_fold()
+ CheckScreendump
+
+ let lines =<< trim END
+ set splitkeep=screen
+ set foldmethod=marker
+ set number
+ let line = 1
+ for n in range(1, &lines)
+ call setline(line, ['int FuncName() {/*{{{*/', 1, 2, 3, 4, 5, '}/*}}}*/',
+ \ 'after fold'])
+ let line += 8
+ endfor
+ END
+ call writefile(lines, 'XTestSplitkeepFold', 'D')
+ let buf = RunVimInTerminal('-S XTestSplitkeepFold', #{rows: 10})
+
+ call term_sendkeys(buf, "L:wincmd s\<CR>")
+ call VerifyScreenDump(buf, 'Test_splitkeep_fold_1', {})
+
+ call term_sendkeys(buf, ":quit\<CR>")
+ call VerifyScreenDump(buf, 'Test_splitkeep_fold_2', {})
+
+ call term_sendkeys(buf, "H:below split\<CR>")
+ call VerifyScreenDump(buf, 'Test_splitkeep_fold_3', {})
+
+ call term_sendkeys(buf, ":wincmd k\<CR>:quit\<CR>")
+ call VerifyScreenDump(buf, 'Test_splitkeep_fold_4', {})
+
+ call StopVimInTerminal(buf)
+endfunction
+
+function Test_splitkeep_status()
+ CheckScreendump
+
+ let lines =<< trim END
+ call setline(1, ['a', 'b', 'c'])
+ set nomodified
+ set splitkeep=screen
+ let win = winnr()
+ wincmd s
+ wincmd j
+ END
+ call writefile(lines, 'XTestSplitkeepStatus', 'D')
+ let buf = RunVimInTerminal('-S XTestSplitkeepStatus', #{rows: 10})
+
+ call term_sendkeys(buf, ":call win_move_statusline(win, 1)\<CR>")
+ call VerifyScreenDump(buf, 'Test_splitkeep_status_1', {})
+
+ call StopVimInTerminal(buf)
+endfunction
+
+function Test_new_help_window_on_error()
+ help change.txt
+ execute "normal! /CTRL-@\<CR>"
+ silent! execute "normal! \<C-W>]"
+
+ let wincount = winnr('$')
+ help 'mod'
+
+ call assert_equal(wincount, winnr('$'))
+ call assert_equal(expand("<cword>"), "'mod'")
+endfunction
+
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_window_id.vim b/src/nvim/testdir/test_window_id.vim
index 8bf4ede350..396a49b55f 100644
--- a/src/nvim/testdir/test_window_id.vim
+++ b/src/nvim/testdir/test_window_id.vim
@@ -1,5 +1,7 @@
" Test using the window ID.
+source check.vim
+
func Test_win_getid()
edit one
let id1 = win_getid()
@@ -90,10 +92,16 @@ func Test_win_getid()
split
call assert_equal(sort([id5, win_getid()]), sort(win_findbuf(bufnr5)))
+ call assert_fails('let w = win_getid([])', 'E745:')
+ call assert_equal(0, win_getid(-1))
+ call assert_equal(-1, win_getid(1, -1))
+
only!
endfunc
func Test_win_getid_curtab()
+ CheckFeature quickfix
+
tabedit X
tabfirst
copen
@@ -127,4 +135,8 @@ func Test_winlayout()
let w2 = win_getid()
call assert_equal(['leaf', w2], 2->winlayout())
tabclose
+
+ call assert_equal([], winlayout(-1))
endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim
index a8735bcaf1..6019cee193 100644
--- a/src/nvim/testdir/test_writefile.vim
+++ b/src/nvim/testdir/test_writefile.vim
@@ -57,7 +57,29 @@ func Test_writefile_fails_conversion()
call assert_fails('write ++enc=cp932', 'E513:')
call assert_equal(contents, readfile('Xfile'))
+ " With 'backupcopy' set, if there is a conversion error, the backup file is
+ " still created.
+ set backupcopy=yes writebackup& backup&
+ call delete('Xfile' .. &backupext)
+ call assert_fails('write ++enc=cp932', 'E513:')
+ call assert_equal(contents, readfile('Xfile'))
+ call assert_equal(contents, readfile('Xfile' .. &backupext))
+ set backupcopy&
+ %bw!
+
+ " Conversion error during write
+ new
+ call setline(1, ["\U10000000"])
+ let output = execute('write! ++enc=utf-16 Xfile')
+ call assert_match('CONVERSION ERROR', output)
+ let output = execute('write! ++enc=ucs-2 Xfile')
+ call assert_match('CONVERSION ERROR', output)
+ call delete('Xfilz~')
+ call delete('Xfily~')
+ %bw!
+
call delete('Xfile')
+ call delete('Xfile' .. &backupext)
bwipe!
set backup& writebackup& backupdir&vim backupskip&vim
endfunc
@@ -136,9 +158,7 @@ func Test_writefile_sync_arg()
endfunc
func Test_writefile_sync_dev_stdout()
- if !has('unix')
- return
- endif
+ CheckUnix
if filewritable('/dev/stdout')
" Just check that this doesn't cause an error.
call writefile(['one'], '/dev/stdout', 's')
@@ -216,6 +236,12 @@ func Test_saveas()
syntax off
%bw!
call delete('Xsaveas.pl')
+
+ " :saveas fails for "nofile" buffer
+ set buftype=nofile
+ call assert_fails('saveas Xsafile', 'E676: No matching autocommands for buftype=nofile buffer')
+
+ bwipe!
endfunc
func Test_write_errors()
@@ -254,6 +280,225 @@ func Test_write_errors()
close
call delete('Xfile')
+
+ " Nvim treats NULL list/blob more like empty list/blob
+ " call writefile(v:_null_list, 'Xfile')
+ " call assert_false(filereadable('Xfile'))
+ " call writefile(v:_null_blob, 'Xfile')
+ " call assert_false(filereadable('Xfile'))
+ call assert_fails('call writefile([], "")', 'E482:')
+
+ " very long file name
+ let long_fname = repeat('n', 5000)
+ call assert_fails('exe "w " .. long_fname', 'E75:')
+ call assert_fails('call writefile([], long_fname)', 'E482:')
+
+ " Test for writing to a block device on Unix-like systems
+ if has('unix') && getfperm('/dev/loop0') != ''
+ \ && getftype('/dev/loop0') == 'bdev' && !IsRoot()
+ new
+ edit /dev/loop0
+ call assert_fails('write', 'E503: ')
+ call assert_fails('write!', 'E503: ')
+ close!
+ endif
+endfunc
+
+" Test for writing to a file which is modified after Vim read it
+func Test_write_file_mtime()
+ CheckEnglish
+ CheckRunVimInTerminal
+
+ " First read the file into a buffer
+ call writefile(["Line1", "Line2"], 'Xfile')
+ let old_ftime = getftime('Xfile')
+ let buf = RunVimInTerminal('Xfile', #{rows : 10})
+ call term_wait(buf)
+ call term_sendkeys(buf, ":set noswapfile\<CR>")
+ call term_wait(buf)
+
+ " Modify the file directly. Make sure the file modification time is
+ " different. Note that on Linux/Unix, the file is considered modified
+ " outside, only if the difference is 2 seconds or more
+ sleep 1
+ call writefile(["Line3", "Line4"], 'Xfile')
+ let new_ftime = getftime('Xfile')
+ while new_ftime - old_ftime < 2
+ sleep 100m
+ call writefile(["Line3", "Line4"], 'Xfile')
+ let new_ftime = getftime('Xfile')
+ endwhile
+
+ " Try to overwrite the file and check for the prompt
+ call term_sendkeys(buf, ":w\<CR>")
+ call term_wait(buf)
+ call WaitForAssert({-> assert_equal("WARNING: The file has been changed since reading it!!!", term_getline(buf, 9))})
+ call assert_equal("Do you really want to write to it (y/n)?",
+ \ term_getline(buf, 10))
+ call term_sendkeys(buf, "n\<CR>")
+ call term_wait(buf)
+ call assert_equal(new_ftime, getftime('Xfile'))
+ call term_sendkeys(buf, ":w\<CR>")
+ call term_wait(buf)
+ call term_sendkeys(buf, "y\<CR>")
+ call term_wait(buf)
+ call WaitForAssert({-> assert_equal('Line2', readfile('Xfile')[1])})
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('Xfile')
+endfunc
+
+" Test for an autocmd unloading a buffer during a write command
+func Test_write_autocmd_unloadbuf_lockmark()
+ augroup WriteTest
+ autocmd BufWritePre Xfile enew | write
+ augroup END
+ e Xfile
+ call assert_fails('lockmarks write', ['E32', 'E203:'])
+ augroup WriteTest
+ au!
+ augroup END
+ augroup! WriteTest
+endfunc
+
+" Test for writing a buffer with 'acwrite' but without autocmds
+func Test_write_acwrite_error()
+ new Xfile
+ call setline(1, ['line1', 'line2', 'line3'])
+ set buftype=acwrite
+ call assert_fails('write', 'E676:')
+ call assert_fails('1,2write!', 'E676:')
+ call assert_fails('w >>', 'E676:')
+ close!
+endfunc
+
+" Test for adding and removing lines from an autocmd when writing a buffer
+func Test_write_autocmd_add_remove_lines()
+ new Xfile
+ call setline(1, ['aaa', 'bbb', 'ccc', 'ddd'])
+
+ " Autocmd deleting lines from the file when writing a partial file
+ augroup WriteTest2
+ au!
+ autocmd FileWritePre Xfile 1,2d
+ augroup END
+ call assert_fails('2,3w!', 'E204:')
+
+ " Autocmd adding lines to a file when writing a partial file
+ augroup WriteTest2
+ au!
+ autocmd FileWritePre Xfile call append(0, ['xxx', 'yyy'])
+ augroup END
+ %d
+ call setline(1, ['aaa', 'bbb', 'ccc', 'ddd'])
+ 1,2w!
+ call assert_equal(['xxx', 'yyy', 'aaa', 'bbb'], readfile('Xfile'))
+
+ " Autocmd deleting lines from the file when writing the whole file
+ augroup WriteTest2
+ au!
+ autocmd BufWritePre Xfile 1,2d
+ augroup END
+ %d
+ call setline(1, ['aaa', 'bbb', 'ccc', 'ddd'])
+ w
+ call assert_equal(['ccc', 'ddd'], readfile('Xfile'))
+
+ augroup WriteTest2
+ au!
+ augroup END
+ augroup! WriteTest2
+
+ close!
+ call delete('Xfile')
+endfunc
+
+" Test for writing to a readonly file
+func Test_write_readonly()
+ call writefile([], 'Xfile')
+ call setfperm('Xfile', "r--------")
+ edit Xfile
+ set noreadonly backupskip=
+ call assert_fails('write', 'E505:')
+ let save_cpo = &cpo
+ set cpo+=W
+ call assert_fails('write!', 'E504:')
+ let &cpo = save_cpo
+ call setline(1, ['line1'])
+ write!
+ call assert_equal(['line1'], readfile('Xfile'))
+
+ " Auto-saving a readonly file should fail with 'autowriteall'
+ %bw!
+ e Xfile
+ set noreadonly autowriteall
+ call setline(1, ['aaaa'])
+ call assert_fails('n', 'E505:')
+ set cpo+=W
+ call assert_fails('n', 'E504:')
+ set cpo-=W
+ set autowriteall&
+
+ set backupskip&
+ call delete('Xfile')
+ %bw!
+endfunc
+
+" Test for 'patchmode'
+func Test_patchmode()
+ call writefile(['one'], 'Xfile')
+ set patchmode=.orig nobackup backupskip= writebackup
+ new Xfile
+ call setline(1, 'two')
+ " first write should create the .orig file
+ write
+ call assert_equal(['one'], readfile('Xfile.orig'))
+ call setline(1, 'three')
+ " subsequent writes should not create/modify the .orig file
+ write
+ call assert_equal(['one'], readfile('Xfile.orig'))
+
+ " use 'patchmode' with 'nobackup' and 'nowritebackup' to create an empty
+ " original file
+ call delete('Xfile')
+ call delete('Xfile.orig')
+ %bw!
+ set patchmode=.orig nobackup nowritebackup
+ edit Xfile
+ call setline(1, ['xxx'])
+ write
+ call assert_equal(['xxx'], readfile('Xfile'))
+ call assert_equal([], readfile('Xfile.orig'))
+
+ set patchmode& backup& backupskip& writebackup&
+ call delete('Xfile')
+ call delete('Xfile.orig')
+endfunc
+
+" Test for writing to a file in a readonly directory
+" NOTE: if you run tests as root this will fail. Don't run tests as root!
+func Test_write_readonly_dir()
+ " On MS-Windows, modifying files in a read-only directory is allowed.
+ CheckUnix
+ " Root can do it too.
+ CheckNotRoot
+
+ call mkdir('Xdir/')
+ call writefile(['one'], 'Xdir/Xfile1')
+ call setfperm('Xdir', 'r-xr--r--')
+ " try to create a new file in the directory
+ new Xdir/Xfile2
+ call setline(1, 'two')
+ call assert_fails('write', 'E212:')
+ " try to create a backup file in the directory
+ edit! Xdir/Xfile1
+ set backupdir=./Xdir backupskip=
+ set patchmode=.orig
+ call assert_fails('write', 'E509:')
+ call setfperm('Xdir', 'rwxr--r--')
+ call delete('Xdir', 'rf')
+ set backupdir& backupskip& patchmode&
endfunc
" Test for writing a file using invalid file encoding
@@ -266,15 +511,15 @@ endfunc
" Tests for reading and writing files with conversion for Win32.
func Test_write_file_encoding()
- throw 'skipped: Nvim does not support :w ++enc=cp1251'
+ throw 'Skipped: Nvim does not support encoding=latin1'
CheckMSWindows
let save_encoding = &encoding
let save_fileencodings = &fileencodings
- set encoding& fileencodings&
+ set encoding=latin1 fileencodings&
let text =<< trim END
- 1 utf-8 text: Для Vim version 6.2. Последнее изменение: 1970 Jan 01
- 2 cp1251 text: Äëÿ Vim version 6.2. Ïîñëåäíåå èçìåíåíèå: 1970 Jan 01
- 3 cp866 text: „«ï Vim version 6.2. ®á«¥¤­¥¥ ¨§¬¥­¥­¨¥: 1970 Jan 01
+ 1 utf-8 text: Для Vim version 6.2. Последнее изменение: 1970 Jan 01
+ 2 cp1251 text: Vim version 6.2. : 1970 Jan 01
+ 3 cp866 text: Vim version 6.2. ᫥ : 1970 Jan 01
END
call writefile(text, 'Xfile')
edit Xfile
@@ -289,9 +534,9 @@ func Test_write_file_encoding()
.w ++enc=cp866 >> Xtest
.w! ++enc=utf-8 Xutf8
let expected =<< trim END
- 1 utf-8 text: Для Vim version 6.2. Последнее изменение: 1970 Jan 01
- 1 utf-8 text: Äëÿ Vim version 6.2. Ïîñëåäíåå èçìåíåíèå: 1970 Jan 01
- 1 utf-8 text: „«ï Vim version 6.2. ®á«¥¤­¥¥ ¨§¬¥­¥­¨¥: 1970 Jan 01
+ 1 utf-8 text: Для Vim version 6.2. Последнее изменение: 1970 Jan 01
+ 1 utf-8 text: Vim version 6.2. : 1970 Jan 01
+ 1 utf-8 text: Vim version 6.2. ᫥ : 1970 Jan 01
END
call assert_equal(expected, readfile('Xtest'))
@@ -302,9 +547,9 @@ func Test_write_file_encoding()
.w ++enc=cp866 >> Xtest
.w! ++enc=cp1251 Xcp1251
let expected =<< trim END
- 2 cp1251 text: Для Vim version 6.2. Последнее изменение: 1970 Jan 01
- 2 cp1251 text: Äëÿ Vim version 6.2. Ïîñëåäíåå èçìåíåíèå: 1970 Jan 01
- 2 cp1251 text: „«ï Vim version 6.2. ®á«¥¤­¥¥ ¨§¬¥­¥­¨¥: 1970 Jan 01
+ 2 cp1251 text: Для Vim version 6.2. Последнее изменение: 1970 Jan 01
+ 2 cp1251 text: Vim version 6.2. : 1970 Jan 01
+ 2 cp1251 text: Vim version 6.2. ᫥ : 1970 Jan 01
END
call assert_equal(expected, readfile('Xtest'))
@@ -315,9 +560,9 @@ func Test_write_file_encoding()
.w ++enc=cp866 >> Xtest
.w! ++enc=cp866 Xcp866
let expected =<< trim END
- 3 cp866 text: Для Vim version 6.2. Последнее изменение: 1970 Jan 01
- 3 cp866 text: Äëÿ Vim version 6.2. Ïîñëåäíåå èçìåíåíèå: 1970 Jan 01
- 3 cp866 text: „«ï Vim version 6.2. ®á«¥¤­¥¥ ¨§¬¥­¥­¨¥: 1970 Jan 01
+ 3 cp866 text: Для Vim version 6.2. Последнее изменение: 1970 Jan 01
+ 3 cp866 text: Vim version 6.2. : 1970 Jan 01
+ 3 cp866 text: Vim version 6.2. ᫥ : 1970 Jan 01
END
call assert_equal(expected, readfile('Xtest'))
@@ -331,9 +576,9 @@ func Test_write_file_encoding()
e Xcp866
.w ++enc=utf-8 >> Xtest
let expected =<< trim END
- 1 utf-8 text: Для Vim version 6.2. Последнее изменение: 1970 Jan 01
- 2 cp1251 text: Для Vim version 6.2. Последнее изменение: 1970 Jan 01
- 3 cp866 text: Для Vim version 6.2. Последнее изменение: 1970 Jan 01
+ 1 utf-8 text: Для Vim version 6.2. Последнее изменение: 1970 Jan 01
+ 2 cp1251 text: Для Vim version 6.2. Последнее изменение: 1970 Jan 01
+ 3 cp866 text: Для Vim version 6.2. Последнее изменение: 1970 Jan 01
END
call assert_equal(expected, readfile('Xtest'))
@@ -347,9 +592,9 @@ func Test_write_file_encoding()
e Xcp866
.w ++enc=cp1251 >> Xtest
let expected =<< trim END
- 1 utf-8 text: Äëÿ Vim version 6.2. Ïîñëåäíåå èçìåíåíèå: 1970 Jan 01
- 2 cp1251 text: Äëÿ Vim version 6.2. Ïîñëåäíåå èçìåíåíèå: 1970 Jan 01
- 3 cp866 text: Äëÿ Vim version 6.2. Ïîñëåäíåå èçìåíåíèå: 1970 Jan 01
+ 1 utf-8 text: Vim version 6.2. : 1970 Jan 01
+ 2 cp1251 text: Vim version 6.2. : 1970 Jan 01
+ 3 cp866 text: Vim version 6.2. : 1970 Jan 01
END
call assert_equal(expected, readfile('Xtest'))
@@ -363,9 +608,9 @@ func Test_write_file_encoding()
e Xcp866
.w ++enc=cp866 >> Xtest
let expected =<< trim END
- 1 utf-8 text: „«ï Vim version 6.2. ®á«¥¤­¥¥ ¨§¬¥­¥­¨¥: 1970 Jan 01
- 2 cp1251 text: „«ï Vim version 6.2. ®á«¥¤­¥¥ ¨§¬¥­¥­¨¥: 1970 Jan 01
- 3 cp866 text: „«ï Vim version 6.2. ®á«¥¤­¥¥ ¨§¬¥­¥­¨¥: 1970 Jan 01
+ 1 utf-8 text: Vim version 6.2. ᫥ : 1970 Jan 01
+ 2 cp1251 text: Vim version 6.2. ᫥ : 1970 Jan 01
+ 3 cp866 text: Vim version 6.2. ᫥ : 1970 Jan 01
END
call assert_equal(expected, readfile('Xtest'))
@@ -523,10 +768,177 @@ func Test_read_write_bin()
call assert_equal(0z6E6F656F6C0A, readfile('XNoEolSetEol', 'B'))
call delete('XNoEolSetEol')
- set ff&
+ set ff& fixeol&
bwipe! XNoEolSetEol
endfunc
+" Test for the 'backupcopy' option when writing files
+func Test_backupcopy()
+ CheckUnix
+ set backupskip=
+ " With the default 'backupcopy' setting, saving a symbolic link file
+ " should not break the link.
+ set backupcopy&
+ call writefile(['1111'], 'Xfile1')
+ silent !ln -s Xfile1 Xfile2
+ new Xfile2
+ call setline(1, ['2222'])
+ write
+ close
+ call assert_equal(['2222'], readfile('Xfile1'))
+ call assert_equal('Xfile1', resolve('Xfile2'))
+ call assert_equal('link', getftype('Xfile2'))
+ call delete('Xfile1')
+ call delete('Xfile2')
+
+ " With the 'backupcopy' set to 'breaksymlink', saving a symbolic link file
+ " should break the link.
+ set backupcopy=yes,breaksymlink
+ call writefile(['1111'], 'Xfile1')
+ silent !ln -s Xfile1 Xfile2
+ new Xfile2
+ call setline(1, ['2222'])
+ write
+ close
+ call assert_equal(['1111'], readfile('Xfile1'))
+ call assert_equal(['2222'], readfile('Xfile2'))
+ call assert_equal('Xfile2', resolve('Xfile2'))
+ call assert_equal('file', getftype('Xfile2'))
+ call delete('Xfile1')
+ call delete('Xfile2')
+ set backupcopy&
+
+ " With the default 'backupcopy' setting, saving a hard link file
+ " should not break the link.
+ set backupcopy&
+ call writefile(['1111'], 'Xfile1')
+ silent !ln Xfile1 Xfile2
+ new Xfile2
+ call setline(1, ['2222'])
+ write
+ close
+ call assert_equal(['2222'], readfile('Xfile1'))
+ call delete('Xfile1')
+ call delete('Xfile2')
+
+ " With the 'backupcopy' set to 'breaksymlink', saving a hard link file
+ " should break the link.
+ set backupcopy=yes,breakhardlink
+ call writefile(['1111'], 'Xfile1')
+ silent !ln Xfile1 Xfile2
+ new Xfile2
+ call setline(1, ['2222'])
+ write
+ call assert_equal(['1111'], readfile('Xfile1'))
+ call assert_equal(['2222'], readfile('Xfile2'))
+ call delete('Xfile1')
+ call delete('Xfile2')
+
+ " If a backup file is already present, then a slightly modified filename
+ " should be used as the backup file. Try with 'backupcopy' set to 'yes' and
+ " 'no'.
+ %bw
+ call writefile(['aaaa'], 'Xfile')
+ call writefile(['bbbb'], 'Xfile.bak')
+ set backupcopy=yes backupext=.bak
+ new Xfile
+ call setline(1, ['cccc'])
+ write
+ close
+ call assert_equal(['cccc'], readfile('Xfile'))
+ call assert_equal(['bbbb'], readfile('Xfile.bak'))
+ set backupcopy=no backupext=.bak
+ new Xfile
+ call setline(1, ['dddd'])
+ write
+ close
+ call assert_equal(['dddd'], readfile('Xfile'))
+ call assert_equal(['bbbb'], readfile('Xfile.bak'))
+ call delete('Xfile')
+ call delete('Xfile.bak')
+
+ " Write to a device file (in Unix-like systems) which cannot be backed up.
+ if has('unix')
+ set writebackup backupcopy=yes nobackup
+ new
+ call setline(1, ['aaaa'])
+ let output = execute('write! /dev/null')
+ call assert_match('"/dev/null" \[Device]', output)
+ close
+ set writebackup backupcopy=no nobackup
+ new
+ call setline(1, ['aaaa'])
+ let output = execute('write! /dev/null')
+ call assert_match('"/dev/null" \[Device]', output)
+ close
+ set backup writebackup& backupcopy&
+ new
+ call setline(1, ['aaaa'])
+ let output = execute('write! /dev/null')
+ call assert_match('"/dev/null" \[Device]', output)
+ close
+ endif
+
+ set backupcopy& backupskip& backupext& backup&
+endfunc
+
+" Test for writing a file with 'encoding' set to 'utf-16'
+func Test_write_utf16()
+ new
+ call setline(1, ["\U00010001"])
+ write ++enc=utf-16 Xfile
+ bw!
+ call assert_equal(0zD800DC01, readfile('Xfile', 'B')[0:3])
+ call delete('Xfile')
+endfunc
+
+" Test for trying to save a backup file when the backup file is a symbolic
+" link to the original file. The backup file should not be modified.
+func Test_write_backup_symlink()
+ CheckUnix
+ call mkdir('Xbackup')
+ let save_backupdir = &backupdir
+ set backupdir=.,./Xbackup
+ call writefile(['1111'], 'Xfile')
+ silent !ln -s Xfile Xfile.bak
+
+ new Xfile
+ set backup backupcopy=yes backupext=.bak
+ write
+ call assert_equal('link', getftype('Xfile.bak'))
+ call assert_equal('Xfile', resolve('Xfile.bak'))
+ " backup file should be created in the 'backup' directory
+ if !has('bsd')
+ " This check fails on FreeBSD
+ call assert_true(filereadable('./Xbackup/Xfile.bak'))
+ endif
+ set backup& backupcopy& backupext&
+ %bw
+
+ call delete('Xfile')
+ call delete('Xfile.bak')
+ call delete('Xbackup', 'rf')
+ let &backupdir = save_backupdir
+endfunc
+
+" Test for ':write ++bin' and ':write ++nobin'
+func Test_write_binary_file()
+ " create a file without an eol/eof character
+ call writefile(0z616161, 'Xfile1', 'b')
+ new Xfile1
+ write ++bin Xfile2
+ write ++nobin Xfile3
+ call assert_equal(0z616161, readblob('Xfile2'))
+ if has('win32')
+ call assert_equal(0z6161610D.0A, readblob('Xfile3'))
+ else
+ call assert_equal(0z6161610A, readblob('Xfile3'))
+ endif
+ call delete('Xfile1')
+ call delete('Xfile2')
+ call delete('Xfile3')
+endfunc
+
" Check that buffer is written before triggering QuitPre
func Test_wq_quitpre_autocommand()
edit Xsomefile
diff --git a/src/nvim/testdir/view_util.vim b/src/nvim/testdir/view_util.vim
index 1cdce21602..a4d0e56af9 100644
--- a/src/nvim/testdir/view_util.vim
+++ b/src/nvim/testdir/view_util.vim
@@ -19,7 +19,7 @@ endfunc
" Get text on the screen, including composing characters.
" ScreenLines(lnum, width) or
" ScreenLines([start, end], width)
-function! ScreenLines(lnum, width) abort
+func ScreenLines(lnum, width) abort
redraw!
if type(a:lnum) == v:t_list
let start = a:lnum[0]
@@ -33,9 +33,9 @@ function! ScreenLines(lnum, width) abort
let lines += [join(map(range(1, a:width), 'screenstring(l, v:val)'), '')]
endfor
return lines
-endfunction
+endfunc
-function! ScreenAttrs(lnum, width) abort
+func ScreenAttrs(lnum, width) abort
redraw!
if type(a:lnum) == v:t_list
let start = a:lnum[0]
@@ -49,16 +49,16 @@ function! ScreenAttrs(lnum, width) abort
let attrs += [map(range(1, a:width), 'screenattr(l, v:val)')]
endfor
return attrs
-endfunction
+endfunc
-function! NewWindow(height, width) abort
+func NewWindow(height, width) abort
exe a:height . 'new'
exe a:width . 'vsp'
set winfixwidth winfixheight
redraw!
-endfunction
+endfunc
-function! CloseWindow() abort
+func CloseWindow() abort
bw!
redraw!
-endfunction
+endfunc
diff --git a/src/nvim/testdir/vim9.vim b/src/nvim/testdir/vim9.vim
new file mode 100644
index 0000000000..3c0ff2b2dd
--- /dev/null
+++ b/src/nvim/testdir/vim9.vim
@@ -0,0 +1,112 @@
+
+" Use a different file name for each run.
+let s:sequence = 1
+
+func CheckScriptFailure(lines, error, lnum = -3)
+ if get(a:lines, 0, '') ==# 'vim9script'
+ return
+ endif
+ let cwd = getcwd()
+ let fname = 'XScriptFailure' .. s:sequence
+ let s:sequence += 1
+ call writefile(a:lines, fname)
+ try
+ call assert_fails('so ' .. fname, a:error, a:lines, a:lnum)
+ finally
+ call chdir(cwd)
+ call delete(fname)
+ endtry
+endfunc
+
+func CheckScriptSuccess(lines)
+ if get(a:lines, 0, '') ==# 'vim9script'
+ return
+ endif
+ let cwd = getcwd()
+ let fname = 'XScriptSuccess' .. s:sequence
+ let s:sequence += 1
+ call writefile(a:lines, fname)
+ try
+ exe 'so ' .. fname
+ finally
+ call chdir(cwd)
+ call delete(fname)
+ endtry
+endfunc
+
+" Check that "lines" inside a legacy function has no error.
+func CheckLegacySuccess(lines)
+ let cwd = getcwd()
+ let fname = 'XlegacySuccess' .. s:sequence
+ let s:sequence += 1
+ call writefile(['func Func()'] + a:lines + ['endfunc'], fname)
+ try
+ exe 'so ' .. fname
+ call Func()
+ finally
+ delfunc! Func
+ call chdir(cwd)
+ call delete(fname)
+ endtry
+endfunc
+
+" Check that "lines" inside a legacy function results in the expected error
+func CheckLegacyFailure(lines, error)
+ let cwd = getcwd()
+ let fname = 'XlegacyFails' .. s:sequence
+ let s:sequence += 1
+ call writefile(['func Func()'] + a:lines + ['endfunc', 'call Func()'], fname)
+ try
+ call assert_fails('so ' .. fname, a:error)
+ finally
+ delfunc! Func
+ call chdir(cwd)
+ call delete(fname)
+ endtry
+endfunc
+
+" Execute "lines" in a legacy function, translated as in
+" CheckLegacyAndVim9Success()
+func CheckTransLegacySuccess(lines)
+ let legacylines = a:lines->deepcopy()->map({_, v ->
+ \ v->substitute('\<VAR\>', 'let', 'g')
+ \ ->substitute('\<LET\>', 'let', 'g')
+ \ ->substitute('\<LSTART\>', '{', 'g')
+ \ ->substitute('\<LMIDDLE\>', '->', 'g')
+ \ ->substitute('\<LEND\>', '}', 'g')
+ \ ->substitute('\<TRUE\>', '1', 'g')
+ \ ->substitute('\<FALSE\>', '0', 'g')
+ \ ->substitute('#"', ' "', 'g')
+ \ })
+ call CheckLegacySuccess(legacylines)
+endfunc
+
+" Execute "lines" in a legacy function
+" Use 'VAR' for a declaration.
+" Use 'LET' for an assignment
+" Use ' #"' for a comment
+" Use LSTART arg LMIDDLE expr LEND for lambda
+" Use 'TRUE' for 1
+" Use 'FALSE' for 0
+func CheckLegacyAndVim9Success(lines)
+ call CheckTransLegacySuccess(a:lines)
+endfunc
+
+" Execute "lines" in a legacy function
+" Use 'VAR' for a declaration.
+" Use 'LET' for an assignment
+" Use ' #"' for a comment
+func CheckLegacyAndVim9Failure(lines, error)
+ if type(a:error) == type('string')
+ let legacyError = error
+ else
+ let legacyError = error[0]
+ endif
+
+ let legacylines = a:lines->deepcopy()->map({_, v ->
+ \ v->substitute('\<VAR\>', 'let', 'g')
+ \ ->substitute('\<LET\>', 'let', 'g')
+ \ ->substitute('#"', ' "', 'g')
+ \ })
+ call CheckLegacyFailure(legacylines, legacyError)
+endfunc
diff --git a/src/nvim/testing.c b/src/nvim/testing.c
index e70e9f2cbd..edf92c78ac 100644
--- a/src/nvim/testing.c
+++ b/src/nvim/testing.c
@@ -3,17 +3,46 @@
// testing.c: Support for tests
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "nvim/ascii.h"
#include "nvim/eval.h"
#include "nvim/eval/encode.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/ex_docmd.h"
+#include "nvim/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/hashtab.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
#include "nvim/os/os.h"
#include "nvim/runtime.h"
+#include "nvim/strings.h"
#include "nvim/testing.h"
+#include "nvim/types.h"
+#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "testing.c.generated.h"
#endif
+static char e_assert_fails_second_arg[]
+ = N_("E856: assert_fails() second argument must be a string or a list with one or two strings");
+static char e_assert_fails_fourth_argument[]
+ = N_("E1115: assert_fails() fourth argument must be a number");
+static char e_assert_fails_fifth_argument[]
+ = N_("E1116: assert_fails() fifth argument must be a string");
+static char e_calling_test_garbagecollect_now_while_v_testing_is_not_set[]
+ = N_("E1142: Calling test_garbagecollect_now() while v:testing is not set");
+
/// Prepare "gap" for an assert error and add the sourcing position.
static void prepare_assert_error(garray_T *gap)
{
@@ -39,15 +68,15 @@ static void prepare_assert_error(garray_T *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)
+static void ga_concat_esc(garray_T *gap, const char *p, int clen)
FUNC_ATTR_NONNULL_ALL
{
- char_u buf[NUMBUFLEN];
+ char buf[NUMBUFLEN];
if (clen > 1) {
memmove(buf, p, (size_t)clen);
buf[clen] = NUL;
- ga_concat(gap, (char *)buf);
+ ga_concat(gap, buf);
} else {
switch (*p) {
case BS:
@@ -65,11 +94,11 @@ static void ga_concat_esc(garray_T *gap, const char_u *p, int clen)
case '\\':
ga_concat(gap, "\\\\"); break;
default:
- if (*p < ' ') {
- vim_snprintf((char *)buf, NUMBUFLEN, "\\x%02x", *p);
- ga_concat(gap, (char *)buf);
+ if ((uint8_t)(*p) < ' ' || *p == 0x7f) {
+ vim_snprintf(buf, NUMBUFLEN, "\\x%02x", *p);
+ ga_concat(gap, buf);
} else {
- ga_append(gap, (char)(*p));
+ ga_append(gap, (uint8_t)(*p));
}
break;
}
@@ -78,22 +107,22 @@ static void ga_concat_esc(garray_T *gap, const char_u *p, int clen)
/// 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)
+static void ga_concat_shorten_esc(garray_T *gap, const char *str)
FUNC_ATTR_NONNULL_ARG(1)
{
- char_u buf[NUMBUFLEN];
+ char buf[NUMBUFLEN];
if (str == NULL) {
ga_concat(gap, "NULL");
return;
}
- for (const char_u *p = str; *p != NUL; p++) {
+ for (const char *p = str; *p != NUL; p++) {
int same_len = 1;
- const char_u *s = p;
+ const char *s = p;
const int c = mb_ptr2char_adv(&s);
const int clen = (int)(s - p);
- while (*s != NUL && c == utf_ptr2char((char *)s)) {
+ while (*s != NUL && c == utf_ptr2char(s)) {
same_len++;
s += clen;
}
@@ -101,8 +130,8 @@ static void ga_concat_shorten_esc(garray_T *gap, const char_u *str)
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);
+ vim_snprintf(buf, NUMBUFLEN, "%d", same_len);
+ ga_concat(gap, buf);
ga_concat(gap, " times]");
p = s - 1;
} else {
@@ -112,18 +141,21 @@ static void ga_concat_shorten_esc(garray_T *gap, const char_u *str)
}
/// 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,
+static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char *exp_str,
typval_T *exp_tv_arg, typval_T *got_tv_arg, assert_type_T atype)
{
- char_u *tofree;
+ char *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);
+ if (opt_msg_tv->v_type != VAR_UNKNOWN
+ && !(opt_msg_tv->v_type == VAR_STRING
+ && (opt_msg_tv->vval.v_string == NULL
+ || *opt_msg_tv->vval.v_string == NUL))) {
+ tofree = encode_tv2echo(opt_msg_tv, NULL);
+ ga_concat(gap, tofree);
xfree(tofree);
ga_concat(gap, ": ");
}
@@ -156,7 +188,7 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_s
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);
+ 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) {
@@ -177,7 +209,7 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_s
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);
+ 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);
}
@@ -186,7 +218,7 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_s
}
}
- tofree = (char_u *)encode_tv2string(exp_tv, NULL);
+ tofree = encode_tv2string(exp_tv, NULL);
ga_concat_shorten_esc(gap, tofree);
xfree(tofree);
} else {
@@ -201,7 +233,7 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_s
} else {
ga_concat(gap, " but got ");
}
- tofree = (char_u *)encode_tv2string(got_tv, NULL);
+ tofree = encode_tv2string(got_tv, NULL);
ga_concat_shorten_esc(gap, tofree);
xfree(tofree);
@@ -241,13 +273,12 @@ static int assert_match_common(typval_T *argvars, assert_type_T atype)
{
char buf1[NUMBUFLEN];
char buf2[NUMBUFLEN];
+ const int called_emsg_before = called_emsg;
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)) {
+ if (called_emsg == called_emsg_before
+ && pattern_match(pat, text, false) != (atype == ASSERT_MATCH)) {
garray_T ga;
prepare_assert_error(&ga);
fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1], atype);
@@ -275,7 +306,7 @@ static int assert_bool(typval_T *argvars, bool is_true)
: kBoolVarFalse)))) {
prepare_assert_error(&ga);
fill_assert_error(&ga, &argvars[1],
- (char_u *)(is_true ? "True" : "False"),
+ is_true ? "True" : "False",
NULL, &argvars[0], ASSERT_OTHER);
assert_error(&ga);
ga_clear(&ga);
@@ -326,19 +357,19 @@ static int assert_beeps(typval_T *argvars, bool no_beep)
}
/// "assert_beeps(cmd [, error])" function
-void f_assert_beeps(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_assert_beeps(typval_T *argvars, typval_T *rettv, EvalFuncData 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)
+void f_assert_nobeep(typval_T *argvars, typval_T *rettv, EvalFuncData 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)
+void f_assert_equal(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = assert_equal_common(argvars, ASSERT_EQUAL);
}
@@ -348,11 +379,12 @@ static int assert_equalfile(typval_T *argvars)
{
char buf1[NUMBUFLEN];
char buf2[NUMBUFLEN];
+ const int called_emsg_before = called_emsg;
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) {
+ if (called_emsg > called_emsg_before) {
return 0;
}
@@ -362,12 +394,12 @@ static int assert_equalfile(typval_T *argvars)
char line2[200];
ptrdiff_t lineidx = 0;
if (fd1 == NULL) {
- snprintf((char *)IObuff, IOSIZE, (char *)e_notread, fname1);
+ snprintf(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);
+ snprintf(IObuff, IOSIZE, (char *)e_notread, fname2);
} else {
int64_t linecount = 1;
for (int64_t count = 0;; count++) {
@@ -386,7 +418,7 @@ static int assert_equalfile(typval_T *argvars)
line2[lineidx] = (char)c2;
lineidx++;
if (c1 != c2) {
- snprintf((char *)IObuff, IOSIZE,
+ snprintf(IObuff, IOSIZE,
"difference at byte %" PRId64 ", line %" PRId64,
count, linecount);
break;
@@ -413,13 +445,13 @@ static int assert_equalfile(typval_T *argvars)
xfree(tofree);
ga_concat(&ga, ": ");
}
- ga_concat(&ga, (char *)IObuff);
+ ga_concat(&ga, IObuff);
if (lineidx > 0) {
line1[lineidx] = NUL;
line2[lineidx] = NUL;
ga_concat(&ga, " after \"");
ga_concat(&ga, line1);
- if (STRCMP(line1, line2) != 0) {
+ if (strcmp(line1, line2) != 0) {
ga_concat(&ga, "\" vs \"");
ga_concat(&ga, line2);
}
@@ -433,19 +465,19 @@ static int assert_equalfile(typval_T *argvars)
}
/// "assert_equalfile(fname-one, fname-two[, msg])" function
-void f_assert_equalfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_assert_equalfile(typval_T *argvars, typval_T *rettv, EvalFuncData 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)
+void f_assert_notequal(typval_T *argvars, typval_T *rettv, EvalFuncData 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)
+void f_assert_exception(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
garray_T ga;
@@ -457,7 +489,7 @@ void f_assert_exception(typval_T *argvars, typval_T *rettv, FunPtr fptr)
ga_clear(&ga);
rettv->vval.v_number = 1;
} else if (error != NULL
- && strstr((char *)get_vim_var_str(VV_EXCEPTION), error) == NULL) {
+ && strstr(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);
@@ -468,17 +500,19 @@ void f_assert_exception(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "assert_fails(cmd [, error [, msg]])" function
-void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_assert_fails(typval_T *argvars, typval_T *rettv, EvalFuncData 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;
+ char *wrong_arg_msg = NULL;
// trylevel must be zero for a ":throw" command to be considered failed
trylevel = 0;
suppress_errthrow = true;
- emsg_silent = true;
+ in_assert_fails = true;
+ no_wait_return++;
do_cmdline_cmd(cmd);
if (called_emsg == called_emsg_before) {
@@ -490,13 +524,75 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr)
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);
+ const char *expected;
+ bool error_found = false;
+ int error_found_index = 1;
+ char *actual = emsg_assert_fails_msg == NULL ? "[unknown]" : emsg_assert_fails_msg;
+
+ if (argvars[1].v_type == VAR_STRING) {
+ expected = tv_get_string_buf_chk(&argvars[1], buf);
+ error_found = expected == NULL || strstr(actual, expected) == NULL;
+ } else if (argvars[1].v_type == VAR_LIST) {
+ const list_T *const list = argvars[1].vval.v_list;
+ if (list == NULL || tv_list_len(list) < 1 || tv_list_len(list) > 2) {
+ wrong_arg_msg = e_assert_fails_second_arg;
+ goto theend;
+ }
+ const typval_T *tv = TV_LIST_ITEM_TV(tv_list_first(list));
+ expected = tv_get_string_buf_chk(tv, buf);
+ if (!pattern_match(expected, actual, false)) {
+ error_found = true;
+ } else if (tv_list_len(list) == 2) {
+ tv = TV_LIST_ITEM_TV(tv_list_last(list));
+ actual = get_vim_var_str(VV_ERRMSG);
+ expected = tv_get_string_buf_chk(tv, buf);
+ if (!pattern_match(expected, actual, false)) {
+ error_found = true;
+ }
+ }
+ } else {
+ wrong_arg_msg = e_assert_fails_second_arg;
+ goto theend;
+ }
+
+ if (!error_found && argvars[2].v_type != VAR_UNKNOWN
+ && argvars[3].v_type != VAR_UNKNOWN) {
+ if (argvars[3].v_type != VAR_NUMBER) {
+ wrong_arg_msg = e_assert_fails_fourth_argument;
+ goto theend;
+ } else if (argvars[3].vval.v_number >= 0
+ && argvars[3].vval.v_number != emsg_assert_fails_lnum) {
+ error_found = true;
+ error_found_index = 3;
+ }
+ if (!error_found && argvars[4].v_type != VAR_UNKNOWN) {
+ if (argvars[4].v_type != VAR_STRING) {
+ wrong_arg_msg = e_assert_fails_fifth_argument;
+ goto theend;
+ } else if (argvars[4].vval.v_string != NULL
+ && !pattern_match(argvars[4].vval.v_string,
+ emsg_assert_fails_context, false)) {
+ error_found = true;
+ error_found_index = 4;
+ }
+ }
+ }
- if (error == NULL
- || strstr((char *)get_vim_var_str(VV_ERRMSG), error) == NULL) {
+ if (error_found) {
+ typval_T actual_tv;
prepare_assert_error(&ga);
- fill_assert_error(&ga, &argvars[2], NULL, &argvars[1],
- get_vim_var_tv(VV_ERRMSG), ASSERT_OTHER);
+ if (error_found_index == 3) {
+ actual_tv.v_type = VAR_NUMBER;
+ actual_tv.vval.v_number = emsg_assert_fails_lnum;
+ } else if (error_found_index == 4) {
+ actual_tv.v_type = VAR_STRING;
+ actual_tv.vval.v_string = emsg_assert_fails_context;
+ } else {
+ actual_tv.v_type = VAR_STRING;
+ actual_tv.vval.v_string = actual;
+ }
+ fill_assert_error(&ga, &argvars[2], NULL,
+ &argvars[error_found_index], &actual_tv, ASSERT_OTHER);
ga_concat(&ga, ": ");
assert_append_cmd_or_arg(&ga, argvars, cmd);
assert_error(&ga);
@@ -505,15 +601,27 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
+theend:
trylevel = save_trylevel;
suppress_errthrow = false;
- emsg_silent = false;
+ in_assert_fails = false;
+ did_emsg = false;
+ got_int = false;
+ msg_col = 0;
+ no_wait_return--;
+ need_wait_return = false;
emsg_on_display = false;
+ msg_reset_scroll();
+ lines_left = Rows;
+ XFREE_CLEAR(emsg_assert_fails_msg);
set_vim_var_string(VV_ERRMSG, NULL, 0);
+ if (wrong_arg_msg != NULL) {
+ emsg(_(wrong_arg_msg));
+ }
}
// "assert_false(actual[, msg])" function
-void f_assert_false(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_assert_false(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = assert_bool(argvars, false);
}
@@ -534,8 +642,8 @@ static int assert_inrange(typval_T *argvars)
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);
+ char *const tofree = encode_tv2string(&argvars[3], NULL);
+ ga_concat(&ga, tofree);
xfree(tofree);
} else {
char msg[80];
@@ -563,7 +671,7 @@ static int assert_inrange(typval_T *argvars)
vim_snprintf(msg, sizeof(msg),
"range %" PRIdVARNUMBER " - %" PRIdVARNUMBER ",",
lower, upper); // -V576
- fill_assert_error(&ga, &argvars[3], (char_u *)msg, NULL, &argvars[2],
+ fill_assert_error(&ga, &argvars[3], msg, NULL, &argvars[2],
ASSERT_INRANGE);
assert_error(&ga);
ga_clear(&ga);
@@ -574,25 +682,25 @@ static int assert_inrange(typval_T *argvars)
}
/// "assert_inrange(lower, upper[, msg])" function
-void f_assert_inrange(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_assert_inrange(typval_T *argvars, typval_T *rettv, EvalFuncData 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)
+void f_assert_match(typval_T *argvars, typval_T *rettv, EvalFuncData 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)
+void f_assert_notmatch(typval_T *argvars, typval_T *rettv, EvalFuncData 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)
+void f_assert_report(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
garray_T ga;
@@ -604,21 +712,25 @@ void f_assert_report(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "assert_true(actual[, msg])" function
-void f_assert_true(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_assert_true(typval_T *argvars, typval_T *rettv, EvalFuncData 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)
+void f_test_garbagecollect_now(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
// This is dangerous, any Lists and Dicts used internally may be freed
// while still in use.
- garbage_collect(true);
+ if (!get_vim_var_nr(VV_TESTING)) {
+ emsg(_(e_calling_test_garbagecollect_now_while_v_testing_is_not_set));
+ } else {
+ garbage_collect(true);
+ }
}
/// "test_write_list_log()" function
-void f_test_write_list_log(typval_T *const argvars, typval_T *const rettv, FunPtr fptr)
+void f_test_write_list_log(typval_T *const argvars, typval_T *const rettv, EvalFuncData fptr)
{
const char *const fname = tv_get_string_chk(&argvars[0]);
if (fname == NULL) {
diff --git a/src/nvim/textformat.c b/src/nvim/textformat.c
new file mode 100644
index 0000000000..fbea1ccfb7
--- /dev/null
+++ b/src/nvim/textformat.c
@@ -0,0 +1,1138 @@
+// 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
+
+// textformat.c: text formatting functions
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
+#include "nvim/change.h"
+#include "nvim/charset.h"
+#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
+#include "nvim/edit.h"
+#include "nvim/eval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
+#include "nvim/getchar.h"
+#include "nvim/globals.h"
+#include "nvim/indent.h"
+#include "nvim/indent_c.h"
+#include "nvim/macros.h"
+#include "nvim/mark.h"
+#include "nvim/mbyte.h"
+#include "nvim/memline.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
+#include "nvim/move.h"
+#include "nvim/normal.h"
+#include "nvim/ops.h"
+#include "nvim/option.h"
+#include "nvim/os/input.h"
+#include "nvim/pos.h"
+#include "nvim/search.h"
+#include "nvim/strings.h"
+#include "nvim/textformat.h"
+#include "nvim/textobject.h"
+#include "nvim/types.h"
+#include "nvim/undo.h"
+#include "nvim/vim.h"
+#include "nvim/window.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "textformat.c.generated.h"
+#endif
+
+static bool did_add_space = false; ///< auto_format() added an extra space
+ ///< under the cursor
+
+#define WHITECHAR(cc) (ascii_iswhite(cc) \
+ && !utf_iscomposing(utf_ptr2char((char *)get_cursor_pos_ptr() + 1)))
+
+/// Return true if format option 'x' is in effect.
+/// Take care of no formatting when 'paste' is set.
+bool has_format_option(int x)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ if (p_paste) {
+ return false;
+ }
+ return vim_strchr(curbuf->b_p_fo, x) != NULL;
+}
+
+/// Format text at the current insert position.
+///
+/// If the INSCHAR_COM_LIST flag is present, then the value of second_indent
+/// will be the comment leader length sent to open_line().
+///
+/// @param c character to be inserted (can be NUL)
+void internal_format(int textwidth, int second_indent, int flags, bool format_only, int c)
+{
+ int cc;
+ int save_char = NUL;
+ bool haveto_redraw = false;
+ const bool fo_ins_blank = has_format_option(FO_INS_BLANK);
+ const bool fo_multibyte = has_format_option(FO_MBYTE_BREAK);
+ const bool fo_rigor_tw = has_format_option(FO_RIGOROUS_TW);
+ const bool fo_white_par = has_format_option(FO_WHITE_PAR);
+ bool first_line = true;
+ colnr_T leader_len;
+ bool no_leader = false;
+ int do_comments = (flags & INSCHAR_DO_COM);
+ int has_lbr = curwin->w_p_lbr;
+
+ // make sure win_lbr_chartabsize() counts correctly
+ curwin->w_p_lbr = false;
+
+ // When 'ai' is off we don't want a space under the cursor to be
+ // deleted. Replace it with an 'x' temporarily.
+ if (!curbuf->b_p_ai
+ && !(State & VREPLACE_FLAG)) {
+ cc = gchar_cursor();
+ if (ascii_iswhite(cc)) {
+ save_char = cc;
+ pchar_cursor('x');
+ }
+ }
+
+ // Repeat breaking lines, until the current line is not too long.
+ while (!got_int) {
+ int startcol; // Cursor column at entry
+ int wantcol; // column at textwidth border
+ int foundcol; // column for start of spaces
+ int end_foundcol = 0; // column for start of word
+ colnr_T len;
+ colnr_T virtcol;
+ int orig_col = 0;
+ char *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());
+ if (virtcol <= (colnr_T)textwidth) {
+ break;
+ }
+
+ if (no_leader) {
+ do_comments = false;
+ } else if (!(flags & INSCHAR_FORMAT)
+ && has_format_option(FO_WRAP_COMS)) {
+ do_comments = true;
+ }
+
+ // Don't break until after the comment leader
+ if (do_comments) {
+ char *line = get_cursor_line_ptr();
+ leader_len = get_leader_len(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(line + comment_start, NULL, false, true);
+ if (leader_len != 0) {
+ leader_len += comment_start;
+ }
+ }
+ }
+ } else {
+ leader_len = 0;
+ }
+
+ // If the line doesn't start with a comment leader, then don't
+ // start one in a following broken line. Avoids that a %word
+ // moved to the start of the next line causes all following lines
+ // to start with %.
+ if (leader_len == 0) {
+ no_leader = true;
+ }
+ if (!(flags & INSCHAR_FORMAT)
+ && leader_len == 0
+ && !has_format_option(FO_WRAP)) {
+ break;
+ }
+ if ((startcol = curwin->w_cursor.col) == 0) {
+ break;
+ }
+
+ // find column of textwidth border
+ coladvance((colnr_T)textwidth);
+ wantcol = curwin->w_cursor.col;
+
+ curwin->w_cursor.col = startcol;
+ foundcol = 0;
+ int skip_pos = 0;
+
+ // Find position to break at.
+ // Stop at first entered white when 'formatoptions' has 'v'
+ while ((!fo_ins_blank && !has_format_option(FO_INS_VI))
+ || (flags & INSCHAR_FORMAT)
+ || curwin->w_cursor.lnum != Insstart.lnum
+ || curwin->w_cursor.col >= Insstart.col) {
+ if (curwin->w_cursor.col == startcol && c != NUL) {
+ cc = c;
+ } else {
+ cc = gchar_cursor();
+ }
+ if (WHITECHAR(cc)) {
+ // remember position of blank just before text
+ end_col = curwin->w_cursor.col;
+
+ // find start of sequence of blanks
+ int wcc = 0; // counter for whitespace chars
+ while (curwin->w_cursor.col > 0 && WHITECHAR(cc)) {
+ dec_cursor();
+ cc = gchar_cursor();
+
+ // Increment count of how many whitespace chars in this
+ // group; we only need to know if it's more than one.
+ if (wcc < 2) {
+ wcc++;
+ }
+ }
+ if (curwin->w_cursor.col == 0 && WHITECHAR(cc)) {
+ break; // only spaces in front of text
+ }
+
+ // Don't break after a period when 'formatoptions' has 'p' and
+ // there are less than two spaces.
+ if (has_format_option(FO_PERIOD_ABBR) && cc == '.' && wcc < 2) {
+ continue;
+ }
+
+ // Don't break until after the comment leader
+ if (curwin->w_cursor.col < leader_len) {
+ break;
+ }
+
+ if (has_format_option(FO_ONE_LETTER)) {
+ // do not break after one-letter words
+ if (curwin->w_cursor.col == 0) {
+ break; // one-letter word at begin
+ }
+ // do not break "#a b" when 'tw' is 2
+ if (curwin->w_cursor.col <= leader_len) {
+ break;
+ }
+ col = curwin->w_cursor.col;
+ dec_cursor();
+ cc = gchar_cursor();
+
+ if (WHITECHAR(cc)) {
+ continue; // one-letter, continue
+ }
+ curwin->w_cursor.col = col;
+ }
+
+ inc_cursor();
+
+ end_foundcol = end_col + 1;
+ foundcol = curwin->w_cursor.col;
+ if (curwin->w_cursor.col <= (colnr_T)wantcol) {
+ break;
+ }
+ } else if ((cc >= 0x100 || !utf_allow_break_before(cc)) && fo_multibyte) {
+ int ncc;
+ bool allow_break;
+
+ // Break after or before a multi-byte character.
+ if (curwin->w_cursor.col != startcol) {
+ // Don't break until after the comment leader
+ if (curwin->w_cursor.col < leader_len) {
+ break;
+ }
+ col = curwin->w_cursor.col;
+ inc_cursor();
+ ncc = gchar_cursor();
+ allow_break = utf_allow_break(cc, ncc);
+
+ // If we have already checked this position, skip!
+ if (curwin->w_cursor.col != skip_pos && allow_break) {
+ foundcol = curwin->w_cursor.col;
+ end_foundcol = foundcol;
+ if (curwin->w_cursor.col <= (colnr_T)wantcol) {
+ break;
+ }
+ }
+ curwin->w_cursor.col = col;
+ }
+
+ if (curwin->w_cursor.col == 0) {
+ break;
+ }
+
+ ncc = cc;
+ col = curwin->w_cursor.col;
+
+ dec_cursor();
+ cc = gchar_cursor();
+
+ if (WHITECHAR(cc)) {
+ continue; // break with space
+ }
+ // Don't break until after the comment leader.
+ if (curwin->w_cursor.col < leader_len) {
+ break;
+ }
+
+ curwin->w_cursor.col = col;
+ skip_pos = curwin->w_cursor.col;
+
+ allow_break = utf_allow_break(cc, ncc);
+
+ // Must handle this to respect line break prohibition.
+ if (allow_break) {
+ foundcol = curwin->w_cursor.col;
+ end_foundcol = foundcol;
+ }
+ if (curwin->w_cursor.col <= (colnr_T)wantcol) {
+ const bool ncc_allow_break = utf_allow_break_before(ncc);
+
+ if (allow_break) {
+ break;
+ }
+ if (!ncc_allow_break && !fo_rigor_tw) {
+ // Enable at most 1 punct hang outside of textwidth.
+ if (curwin->w_cursor.col == startcol) {
+ // We are inserting a non-breakable char, postpone
+ // line break check to next insert.
+ end_foundcol = foundcol = 0;
+ break;
+ }
+
+ // Neither cc nor ncc is NUL if we are here, so
+ // it's safe to inc_cursor.
+ col = curwin->w_cursor.col;
+
+ inc_cursor();
+ cc = ncc;
+ ncc = gchar_cursor();
+ // handle insert
+ ncc = (ncc != NUL) ? ncc : c;
+
+ allow_break = utf_allow_break(cc, ncc);
+
+ if (allow_break) {
+ // Break only when we are not at end of line.
+ end_foundcol = foundcol = ncc == NUL? 0 : curwin->w_cursor.col;
+ break;
+ }
+ curwin->w_cursor.col = col;
+ }
+ }
+ }
+ if (curwin->w_cursor.col == 0) {
+ break;
+ }
+ dec_cursor();
+ }
+
+ if (foundcol == 0) { // no spaces, cannot break line
+ curwin->w_cursor.col = startcol;
+ break;
+ }
+
+ // Going to break the line, remove any "$" now.
+ undisplay_dollar();
+
+ // 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 {
+ replace_offset = startcol - end_foundcol;
+ }
+
+ // adjust startcol for spaces that will be deleted and
+ // characters that will remain on top line
+ curwin->w_cursor.col = foundcol;
+ while ((cc = gchar_cursor(), WHITECHAR(cc))
+ && (!fo_white_par || curwin->w_cursor.col < startcol)) {
+ inc_cursor();
+ }
+ startcol -= curwin->w_cursor.col;
+ if (startcol < 0) {
+ startcol = 0;
+ }
+
+ if (State & VREPLACE_FLAG) {
+ // 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 = xstrdup(get_cursor_pos_ptr());
+ curwin->w_cursor.col = orig_col;
+ saved_text[startcol] = NUL;
+
+ // Backspace over characters that will move to the next line
+ if (!fo_white_par) {
+ backspace_until_column(foundcol);
+ }
+ } else {
+ // put cursor after pos. to break line
+ if (!fo_white_par) {
+ curwin->w_cursor.col = foundcol;
+ }
+ }
+
+ // Split the line just before the margin.
+ // Only insert/delete lines, but don't really redraw the window.
+ 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),
+ &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)) {
+ // This section is for auto-wrap of numeric lists. When not
+ // in insert mode (i.e. format_lines()), the INSCHAR_COM_LIST
+ // flag will be set and open_line() will handle it (as seen
+ // above). The code here (and in get_number_indent()) will
+ // recognize comments if needed...
+ if (second_indent < 0 && has_format_option(FO_Q_NUMBER)) {
+ second_indent = get_number_indent(curwin->w_cursor.lnum - 1);
+ }
+ if (second_indent >= 0) {
+ if (State & VREPLACE_FLAG) {
+ change_indent(INDENT_SET, second_indent, false, NUL, true);
+ } else if (leader_len > 0 && second_indent - leader_len > 0) {
+ int padding = second_indent - leader_len;
+
+ // We started at the first_line of a numbered list
+ // that has a comment. the open_line() function has
+ // inserted the proper comment leader and positioned
+ // the cursor at the end of the split line. Now we
+ // add the additional whitespace needed after the
+ // comment leader for the numbered list.
+ for (int i = 0; i < padding; i++) {
+ ins_str(" ");
+ }
+ changed_bytes(curwin->w_cursor.lnum, leader_len);
+ } else {
+ (void)set_indent(second_indent, SIN_CHANGED);
+ }
+ }
+ }
+ first_line = false;
+ }
+
+ if (State & VREPLACE_FLAG) {
+ // 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 {
+ // Check if cursor is not past the NUL off the line, cindent
+ // may have added or removed indent.
+ curwin->w_cursor.col += startcol;
+ len = (colnr_T)strlen(get_cursor_line_ptr());
+ if (curwin->w_cursor.col > len) {
+ curwin->w_cursor.col = len;
+ }
+ }
+
+ haveto_redraw = true;
+ set_can_cindent(true);
+ // moved the cursor, don't autoindent or cindent now
+ did_ai = false;
+ did_si = false;
+ can_si = false;
+ can_si_back = false;
+ line_breakcheck();
+ }
+
+ if (save_char != NUL) { // put back space after cursor
+ pchar_cursor((char_u)save_char);
+ }
+
+ curwin->w_p_lbr = has_lbr;
+
+ if (!format_only && haveto_redraw) {
+ update_topline(curwin);
+ redraw_curbuf_later(UPD_VALID);
+ }
+}
+
+/// 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 **leader_flags, bool do_comments)
+{
+ char *flags = NULL; // init for GCC
+ char *ptr;
+
+ ptr = ml_get(lnum);
+ if (do_comments) {
+ *leader_len = get_leader_len(ptr, leader_flags, false, true);
+ } else {
+ *leader_len = 0;
+ }
+
+ if (*leader_len > 0) {
+ // Search for 'e' flag in comment leader flags.
+ flags = *leader_flags;
+ while (*flags && *flags != ':' && *flags != COM_END) {
+ flags++;
+ }
+ }
+
+ return *skipwhite(ptr + *leader_len) == NUL
+ || (*leader_len > 0 && *flags == COM_END)
+ || startPS(lnum, NUL, false);
+}
+
+/// @return true if line "lnum" ends in a white character.
+static bool ends_in_white(linenr_T lnum)
+{
+ char *s = ml_get(lnum);
+ size_t l;
+
+ if (*s == NUL) {
+ return false;
+ }
+ l = strlen(s) - 1;
+ return ascii_iswhite((uint8_t)s[l]);
+}
+
+/// @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 bool same_leader(linenr_T lnum, int leader1_len, char *leader1_flags, int leader2_len,
+ char *leader2_flags)
+{
+ int idx1 = 0, idx2 = 0;
+ char *p;
+ char *line1;
+ char *line2;
+
+ if (leader1_len == 0) {
+ return leader2_len == 0;
+ }
+
+ // If first leader has 'f' flag, the lines can be joined only if the
+ // second line does not have a leader.
+ // If first leader has 'e' flag, the lines can never be joined.
+ // If first leader has 's' flag, the lines can only be joined if there is
+ // some text after it and the second line has the 'm' flag.
+ if (leader1_flags != NULL) {
+ for (p = leader1_flags; *p && *p != ':'; p++) {
+ if (*p == COM_FIRST) {
+ return leader2_len == 0;
+ }
+ if (*p == COM_END) {
+ return false;
+ }
+ if (*p == COM_START) {
+ int line_len = (int)strlen(ml_get(lnum));
+ if (line_len <= leader1_len) {
+ return false;
+ }
+ if (leader2_flags == NULL || leader2_len == 0) {
+ return false;
+ }
+ for (p = leader2_flags; *p && *p != ':'; p++) {
+ if (*p == COM_MIDDLE) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+ }
+
+ // Get current line and next line, compare the leaders.
+ // The first line has to be saved, only one line can be locked at a time.
+ line1 = xstrdup(ml_get(lnum));
+ 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])) {
+ if (line1[idx1++] != line2[idx2]) {
+ break;
+ }
+ } else {
+ while (ascii_iswhite(line1[idx1])) {
+ idx1++;
+ }
+ }
+ }
+ xfree(line1);
+
+ return idx2 == leader2_len && idx1 == leader1_len;
+}
+
+/// Used for auto-formatting.
+///
+/// @return true when a paragraph starts in line "lnum".
+/// false when the previous line is in the same paragraph.
+static bool paragraph_start(linenr_T lnum)
+{
+ char *p;
+ int leader_len = 0; // leader len of current line
+ char *leader_flags = NULL; // flags for leader of current line
+ int next_leader_len = 0; // leader len of next line
+ char *next_leader_flags = NULL; // flags for leader of next line
+
+ if (lnum <= 1) {
+ return true; // start of the file
+ }
+ p = ml_get(lnum - 1);
+ if (*p == NUL) {
+ return true; // after empty line
+ }
+ const bool do_comments = has_format_option(FO_Q_COMS); // format comments
+ if (fmt_check_par(lnum - 1, &leader_len, &leader_flags, do_comments)) {
+ return true; // after non-paragraph line
+ }
+
+ if (fmt_check_par(lnum, &next_leader_len, &next_leader_flags, do_comments)) {
+ return true; // "lnum" is not a paragraph line
+ }
+
+ if (has_format_option(FO_WHITE_PAR) && !ends_in_white(lnum - 1)) {
+ return true; // missing trailing space in previous line.
+ }
+ if (has_format_option(FO_Q_NUMBER) && (get_number_indent(lnum) > 0)) {
+ return true; // numbered item starts in "lnum".
+ }
+ if (!same_leader(lnum - 1, leader_len, leader_flags,
+ next_leader_len, next_leader_flags)) {
+ return true; // change of comment leader.
+ }
+ return false;
+}
+
+/// Called after inserting or deleting text: When 'formatoptions' includes the
+/// 'a' flag format from the current line until the end of the paragraph.
+/// Keep the cursor at the same position relative to the text.
+/// The caller must have saved the cursor line for undo, following ones will be
+/// saved here.
+///
+/// @param trailblank when true also format with trailing blank
+/// @param prev_line may start in previous line
+void auto_format(bool trailblank, bool prev_line)
+{
+ pos_T pos;
+ colnr_T len;
+ char *old;
+ char *new, *pnew;
+ int wasatend;
+ int cc;
+
+ if (!has_format_option(FO_AUTO)) {
+ return;
+ }
+
+ pos = curwin->w_cursor;
+ old = get_cursor_line_ptr();
+
+ // may remove added space
+ check_auto_format(false);
+
+ // Don't format in Insert mode when the cursor is on a trailing blank, the
+ // user might insert normal text next. Also skip formatting when "1" is
+ // in 'formatoptions' and there is a single character before the cursor.
+ // Otherwise the line would be broken and when typing another non-white
+ // next they are not joined back together.
+ wasatend = (pos.col == (colnr_T)strlen(old));
+ if (*old != NUL && !trailblank && wasatend) {
+ dec_cursor();
+ cc = gchar_cursor();
+ if (!WHITECHAR(cc) && curwin->w_cursor.col > 0
+ && has_format_option(FO_ONE_LETTER)) {
+ dec_cursor();
+ }
+ cc = gchar_cursor();
+ if (WHITECHAR(cc)) {
+ curwin->w_cursor = pos;
+ return;
+ }
+ curwin->w_cursor = pos;
+ }
+
+ // 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) {
+ return;
+ }
+
+ // May start formatting in a previous line, so that after "x" a word is
+ // moved to the previous line if it fits there now. Only when this is not
+ // the start of a paragraph.
+ if (prev_line && !paragraph_start(curwin->w_cursor.lnum)) {
+ curwin->w_cursor.lnum--;
+ if (u_save_cursor() == FAIL) {
+ return;
+ }
+ }
+
+ // Do the formatting and restore the cursor position. "saved_cursor" will
+ // be adjusted for the text formatting.
+ saved_cursor = pos;
+ format_lines((linenr_T) - 1, false);
+ curwin->w_cursor = saved_cursor;
+ saved_cursor.lnum = 0;
+
+ if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
+ // "cannot happen"
+ curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
+ coladvance(MAXCOL);
+ } else {
+ check_cursor_col();
+ }
+
+ // Insert mode: If the cursor is now after the end of the line while it
+ // previously wasn't, the line was broken. Because of the rule above we
+ // need to add a space when 'w' is in 'formatoptions' to keep a paragraph
+ // formatted.
+ if (!wasatend && has_format_option(FO_WHITE_PAR)) {
+ new = get_cursor_line_ptr();
+ len = (colnr_T)strlen(new);
+ if (curwin->w_cursor.col == len) {
+ pnew = xstrnsave(new, (size_t)len + 2);
+ pnew[len] = ' ';
+ pnew[len + 1] = NUL;
+ ml_replace(curwin->w_cursor.lnum, pnew, false);
+ // remove the space later
+ did_add_space = true;
+ } else {
+ // may remove added space
+ check_auto_format(false);
+ }
+ }
+
+ check_cursor();
+}
+
+/// When an extra space was added to continue a paragraph for auto-formatting,
+/// delete it now. The space must be under the cursor, just after the insert
+/// position.
+///
+/// @param end_insert true when ending Insert mode
+void check_auto_format(bool end_insert)
+{
+ int c = ' ';
+ int cc;
+
+ if (did_add_space) {
+ cc = gchar_cursor();
+ if (!WHITECHAR(cc)) {
+ // Somehow the space was removed already.
+ did_add_space = false;
+ } else {
+ if (!end_insert) {
+ inc_cursor();
+ c = gchar_cursor();
+ dec_cursor();
+ }
+ if (c != NUL) {
+ // The space is no longer at the end of the line, delete it.
+ del_char(false);
+ did_add_space = false;
+ }
+ }
+ }
+}
+
+/// Find out textwidth to be used for formatting:
+/// if 'textwidth' option is set, use it
+/// else if 'wrapmargin' option is set, use curwin->w_width_inner-'wrapmargin'
+/// if invalid value, use 0.
+/// Set default to window width (maximum 79) for "gq" operator.
+///
+/// @param ff force formatting (for "gq" command)
+int comp_textwidth(bool ff)
+{
+ 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 - (int)curbuf->b_p_wm;
+ if (cmdwin_type != 0) {
+ textwidth -= 1;
+ }
+ textwidth -= win_fdccol_count(curwin);
+ textwidth -= win_signcol_count(curwin);
+
+ if (curwin->w_p_nu || curwin->w_p_rnu) {
+ textwidth -= 8;
+ }
+ }
+ if (textwidth < 0) {
+ textwidth = 0;
+ }
+ if (ff && textwidth == 0) {
+ textwidth = curwin->w_width_inner - 1;
+ if (textwidth > 79) {
+ textwidth = 79;
+ }
+ }
+ return textwidth;
+}
+
+/// Implementation of the format operator 'gq'.
+///
+/// @param keep_cursor keep cursor on same text char
+void op_format(oparg_T *oap, bool keep_cursor)
+{
+ 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.
+ curwin->w_cursor = oap->cursor_start;
+
+ if (u_save((linenr_T)(oap->start.lnum - 1),
+ (linenr_T)(oap->end.lnum + 1)) == FAIL) {
+ return;
+ }
+ curwin->w_cursor = oap->start;
+
+ if (oap->is_VIsual) {
+ // When there is no change: need to remove the Visual selection
+ redraw_curbuf_later(UPD_INVERTED);
+ }
+
+ 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).
+ if (keep_cursor) {
+ saved_cursor = oap->cursor_start;
+ }
+
+ format_lines((linenr_T)oap->line_count, keep_cursor);
+
+ // Leave the cursor at the first non-blank of the last formatted line.
+ // If the cursor was moved one line back (e.g. with "Q}") go to the next
+ // line, so "." will do the next lines.
+ if (oap->end_adjusted && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
+ curwin->w_cursor.lnum++;
+ }
+ beginline(BL_WHITE | BL_FIX);
+ old_line_count = curbuf->b_ml.ml_line_count - old_line_count;
+ msgmore(old_line_count);
+
+ 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;
+ saved_cursor.lnum = 0;
+
+ // formatting may have made the cursor position invalid
+ check_cursor();
+ }
+
+ if (oap->is_VIsual) {
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_old_cursor_lnum != 0) {
+ // When lines have been inserted or deleted, adjust the end of
+ // the Visual area to be redrawn.
+ if (wp->w_old_cursor_lnum > wp->w_old_visual_lnum) {
+ wp->w_old_cursor_lnum += old_line_count;
+ } else {
+ wp->w_old_visual_lnum += old_line_count;
+ }
+ }
+ }
+ }
+}
+
+/// Implementation of the format operator 'gq' for when using 'formatexpr'.
+void op_formatexpr(oparg_T *oap)
+{
+ if (oap->is_VIsual) {
+ // When there is no change: need to remove the Visual selection
+ redraw_curbuf_later(UPD_INVERTED);
+ }
+
+ if (fex_format(oap->start.lnum, oap->line_count, NUL) != 0) {
+ // As documented: when 'formatexpr' returns non-zero fall back to
+ // internal formatting.
+ op_format(oap, false);
+ }
+}
+
+/// @param c character to be inserted
+int fex_format(linenr_T lnum, long count, int c)
+{
+ int use_sandbox = was_set_insecurely(curwin, "formatexpr", OPT_LOCAL);
+ int r;
+
+ // Set v:lnum to the first line number and v:count to the number of lines.
+ // Set v:char to the character to be inserted (can be NUL).
+ set_vim_var_nr(VV_LNUM, (varnumber_T)lnum);
+ set_vim_var_nr(VV_COUNT, (varnumber_T)count);
+ set_vim_var_char(c);
+
+ // Make a copy, the option could be changed while calling it.
+ char *fex = xstrdup(curbuf->b_p_fex);
+ // Evaluate the function.
+ if (use_sandbox) {
+ sandbox++;
+ }
+ r = (int)eval_to_number(fex);
+ if (use_sandbox) {
+ sandbox--;
+ }
+
+ set_vim_var_string(VV_CHAR, NULL, -1);
+ xfree(fex);
+
+ return r;
+}
+
+/// @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.
+///
+/// @param avoid_fex don't use 'formatexpr'
+void format_lines(linenr_T line_count, bool avoid_fex)
+{
+ bool is_not_par; // current line not part of parag.
+ bool next_is_not_par; // next line not part of paragraph
+ bool is_end_par; // at end of paragraph
+ bool prev_is_end_par = false; // prev. line not part of parag.
+ bool next_is_start_par = false;
+ int leader_len = 0; // leader len of current line
+ int next_leader_len; // leader len of next line
+ char *leader_flags = NULL; // flags for leader of current line
+ char *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;
+
+ // length of a line to force formatting: 3 * 'tw'
+ const int max_len = comp_textwidth(true) * 3;
+
+ // check for 'q', '2' and '1' in 'formatoptions'
+ const bool do_comments = has_format_option(FO_Q_COMS); // format comments
+ int do_comments_list = 0; // format comments with 'n' or '2'
+ const bool do_second_indent = has_format_option(FO_Q_SECOND);
+ const bool do_number_indent = has_format_option(FO_Q_NUMBER);
+ const bool do_trail_white = has_format_option(FO_WHITE_PAR);
+
+ // Get info about the previous and current line.
+ if (curwin->w_cursor.lnum > 1) {
+ is_not_par = fmt_check_par(curwin->w_cursor.lnum - 1,
+ &leader_len, &leader_flags, do_comments);
+ } else {
+ is_not_par = true;
+ }
+ next_is_not_par = fmt_check_par(curwin->w_cursor.lnum,
+ &next_leader_len, &next_leader_flags, do_comments);
+ is_end_par = (is_not_par || next_is_not_par);
+ if (!is_end_par && do_trail_white) {
+ is_end_par = !ends_in_white(curwin->w_cursor.lnum - 1);
+ }
+
+ curwin->w_cursor.lnum--;
+ for (count = line_count; count != 0 && !got_int; count--) {
+ // Advance to next paragraph.
+ if (advance) {
+ curwin->w_cursor.lnum++;
+ prev_is_end_par = is_end_par;
+ is_not_par = next_is_not_par;
+ leader_len = next_leader_len;
+ leader_flags = next_leader_flags;
+ }
+
+ // The last line to be formatted.
+ if (count == 1 || curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count) {
+ next_is_not_par = true;
+ next_leader_len = 0;
+ next_leader_flags = NULL;
+ } else {
+ next_is_not_par = fmt_check_par(curwin->w_cursor.lnum + 1,
+ &next_leader_len, &next_leader_flags, do_comments);
+ if (do_number_indent) {
+ next_is_start_par =
+ (get_number_indent(curwin->w_cursor.lnum + 1) > 0);
+ }
+ }
+ advance = true;
+ is_end_par = (is_not_par || next_is_not_par || next_is_start_par);
+ if (!is_end_par && do_trail_white) {
+ is_end_par = !ends_in_white(curwin->w_cursor.lnum);
+ }
+
+ // Skip lines that are not in a paragraph.
+ if (is_not_par) {
+ if (line_count < 0) {
+ break;
+ }
+ } else {
+ // For the first line of a paragraph, check indent of second line.
+ // Don't do this for comments and empty lines.
+ if (first_par_line
+ && (do_second_indent || do_number_indent)
+ && prev_is_end_par
+ && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
+ if (do_second_indent && !LINEEMPTY(curwin->w_cursor.lnum + 1)) {
+ if (leader_len == 0 && next_leader_len == 0) {
+ // no comment found
+ second_indent =
+ get_indent_lnum(curwin->w_cursor.lnum + 1);
+ } else {
+ second_indent = next_leader_len;
+ do_comments_list = 1;
+ }
+ } else if (do_number_indent) {
+ if (leader_len == 0 && next_leader_len == 0) {
+ // no comment found
+ second_indent =
+ get_number_indent(curwin->w_cursor.lnum);
+ } else {
+ // get_number_indent() is now "comment aware"...
+ second_indent =
+ get_number_indent(curwin->w_cursor.lnum);
+ do_comments_list = 1;
+ }
+ }
+ }
+
+ // When the comment leader changes, it's the end of the paragraph.
+ if (curwin->w_cursor.lnum >= curbuf->b_ml.ml_line_count
+ || !same_leader(curwin->w_cursor.lnum,
+ leader_len, leader_flags,
+ next_leader_len,
+ next_leader_flags)) {
+ // 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;
+ }
+ }
+
+ // If we have got to the end of a paragraph, or the line is
+ // getting long, format it.
+ if (is_end_par || force_format) {
+ if (need_set_indent) {
+ 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 = 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 = MODE_INSERT; // for open_line()
+ smd_save = p_smd;
+ p_smd = false;
+ insertchar(NUL, INSCHAR_FORMAT
+ + (do_comments ? INSCHAR_DO_COM : 0)
+ + (do_comments && do_comments_list ? INSCHAR_COM_LIST : 0)
+ + (avoid_fex ? INSCHAR_NO_FEX : 0), second_indent);
+ State = old_State;
+ p_smd = smd_save;
+ second_indent = -1;
+ // at end of par.: need to set indent of next par.
+ need_set_indent = is_end_par;
+ if (is_end_par) {
+ // When called with a negative line count, break at the
+ // end of the paragraph.
+ if (line_count < 0) {
+ break;
+ }
+ first_par_line = true;
+ }
+ force_format = false;
+ }
+
+ // When still in same paragraph, join the lines together. But
+ // first delete the leader from the second line.
+ if (!is_end_par) {
+ advance = false;
+ curwin->w_cursor.lnum++;
+ curwin->w_cursor.col = 0;
+ if (line_count < 0 && u_save_cursor() == FAIL) {
+ break;
+ }
+ if (next_leader_len > 0) {
+ (void)del_bytes(next_leader_len, false, false);
+ mark_col_adjust(curwin->w_cursor.lnum, (colnr_T)0, 0L,
+ (long)-next_leader_len, 0);
+ } else if (second_indent > 0) { // the "leader" for FO_Q_SECOND
+ int indent = (int)getwhitecols_curline();
+
+ if (indent > 0) {
+ (void)del_bytes(indent, false, false);
+ mark_col_adjust(curwin->w_cursor.lnum,
+ (colnr_T)0, 0L, (long)-indent, 0);
+ }
+ }
+ curwin->w_cursor.lnum--;
+ if (do_join(2, true, false, false, false) == FAIL) {
+ beep_flush();
+ break;
+ }
+ first_par_line = false;
+ // If the line is getting long, format it next time
+ if (strlen(get_cursor_line_ptr()) > (size_t)max_len) {
+ force_format = true;
+ } else {
+ force_format = false;
+ }
+ }
+ }
+ line_breakcheck();
+ }
+}
diff --git a/src/nvim/textformat.h b/src/nvim/textformat.h
new file mode 100644
index 0000000000..fcc9c2d6f4
--- /dev/null
+++ b/src/nvim/textformat.h
@@ -0,0 +1,10 @@
+#ifndef NVIM_TEXTFORMAT_H
+#define NVIM_TEXTFORMAT_H
+
+#include "nvim/normal.h"
+#include "nvim/pos.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "textformat.h.generated.h"
+#endif
+#endif // NVIM_TEXTFORMAT_H
diff --git a/src/nvim/textobject.c b/src/nvim/textobject.c
new file mode 100644
index 0000000000..8e786c271c
--- /dev/null
+++ b/src/nvim/textobject.c
@@ -0,0 +1,1750 @@
+// 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
+
+// textobject.c: functions for text objects
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
+#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
+#include "nvim/edit.h"
+#include "nvim/eval/funcs.h"
+#include "nvim/fold.h"
+#include "nvim/globals.h"
+#include "nvim/indent.h"
+#include "nvim/macros.h"
+#include "nvim/mark.h"
+#include "nvim/mbyte.h"
+#include "nvim/memline.h"
+#include "nvim/memory.h"
+#include "nvim/normal.h"
+#include "nvim/option_defs.h"
+#include "nvim/pos.h"
+#include "nvim/screen.h"
+#include "nvim/search.h"
+#include "nvim/strings.h"
+#include "nvim/textobject.h"
+#include "nvim/vim.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "textobject.c.generated.h"
+#endif
+
+/// Find the start of the next sentence, searching in the direction specified
+/// by the "dir" argument. The cursor is positioned on the start of the next
+/// sentence when found. If the next sentence is found, return OK. Return FAIL
+/// otherwise. See ":h sentence" for the precise definition of a "sentence"
+/// text object.
+int findsent(Direction dir, long count)
+{
+ pos_T pos, tpos;
+ int c;
+ int (*func)(pos_T *);
+ bool noskip = false; // do not skip blanks
+
+ pos = curwin->w_cursor;
+ if (dir == FORWARD) {
+ func = incl;
+ } else {
+ func = decl;
+ }
+
+ while (count--) {
+ const pos_T prev_pos = pos;
+
+ // if on an empty line, skip up to a non-empty line
+ if (gchar_pos(&pos) == NUL) {
+ do {
+ if ((*func)(&pos) == -1) {
+ break;
+ }
+ } while (gchar_pos(&pos) == NUL);
+ if (dir == FORWARD) {
+ goto found;
+ }
+ // if on the start of a paragraph or a section and searching forward,
+ // go to the next line
+ } else if (dir == FORWARD && pos.col == 0
+ && startPS(pos.lnum, NUL, false)) {
+ if (pos.lnum == curbuf->b_ml.ml_line_count) {
+ return FAIL;
+ }
+ pos.lnum++;
+ goto found;
+ } else if (dir == BACKWARD) {
+ decl(&pos);
+ }
+
+ // go back to the previous non-white non-punctuation character
+ bool found_dot = false;
+ while (c = gchar_pos(&pos), ascii_iswhite(c)
+ || vim_strchr(".!?)]\"'", c) != NULL) {
+ tpos = pos;
+ if (decl(&tpos) == -1 || (LINEEMPTY(tpos.lnum) && dir == FORWARD)) {
+ break;
+ }
+ if (found_dot) {
+ break;
+ }
+ if (vim_strchr(".!?", c) != NULL) {
+ found_dot = true;
+ }
+ if (vim_strchr(")]\"'", c) != NULL
+ && vim_strchr(".!?)]\"'", gchar_pos(&tpos)) == NULL) {
+ break;
+ }
+ decl(&pos);
+ }
+
+ // remember the line where the search started
+ const int startlnum = pos.lnum;
+ const bool cpo_J = vim_strchr(p_cpo, CPO_ENDOFSENT) != NULL;
+
+ for (;;) { // find end of sentence
+ c = gchar_pos(&pos);
+ if (c == NUL || (pos.col == 0 && startPS(pos.lnum, NUL, false))) {
+ if (dir == BACKWARD && pos.lnum != startlnum) {
+ pos.lnum++;
+ }
+ break;
+ }
+ if (c == '.' || c == '!' || c == '?') {
+ tpos = pos;
+ do {
+ if ((c = inc(&tpos)) == -1) {
+ break;
+ }
+ } 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) == ' '))) {
+ pos = tpos;
+ if (gchar_pos(&pos) == NUL) { // skip NUL at EOL
+ inc(&pos);
+ }
+ break;
+ }
+ }
+ if ((*func)(&pos) == -1) {
+ if (count) {
+ return FAIL;
+ }
+ noskip = true;
+ break;
+ }
+ }
+found:
+ // skip white space
+ while (!noskip && ((c = gchar_pos(&pos)) == ' ' || c == '\t')) {
+ if (incl(&pos) == -1) {
+ break;
+ }
+ }
+
+ if (equalpos(prev_pos, pos)) {
+ // didn't actually move, advance one character and try again
+ if ((*func)(&pos) == -1) {
+ if (count) {
+ return FAIL;
+ }
+ break;
+ }
+ count++;
+ }
+ }
+
+ setpcmark();
+ curwin->w_cursor = pos;
+ return OK;
+}
+
+/// Find the next paragraph or section in direction 'dir'.
+/// Paragraphs are currently supposed to be separated by empty lines.
+/// If 'what' is NUL we go to the next paragraph.
+/// If 'what' is '{' or '}' we go to the next section.
+/// If 'both' is true also stop at '}'.
+///
+/// @param pincl Return: true if last char is to be included
+///
+/// @return true if the next paragraph or section was found.
+bool findpar(bool *pincl, int dir, long count, int what, bool both)
+{
+ linenr_T curr;
+ bool did_skip; // true after separating lines have been skipped
+ bool first; // true on first line
+ linenr_T fold_first; // first line of a closed fold
+ linenr_T fold_last; // last line of a closed fold
+ bool fold_skipped; // true if a closed fold was skipped this
+ // iteration
+
+ curr = curwin->w_cursor.lnum;
+
+ while (count--) {
+ did_skip = false;
+ for (first = true;; first = false) {
+ if (*ml_get(curr) != NUL) {
+ did_skip = true;
+ }
+
+ // skip folded lines
+ fold_skipped = false;
+ if (first && hasFolding(curr, &fold_first, &fold_last)) {
+ curr = ((dir > 0) ? fold_last : fold_first) + dir;
+ fold_skipped = true;
+ }
+
+ if (!first && did_skip && startPS(curr, what, both)) {
+ break;
+ }
+
+ if (fold_skipped) {
+ curr -= dir;
+ }
+ if ((curr += dir) < 1 || curr > curbuf->b_ml.ml_line_count) {
+ if (count) {
+ return false;
+ }
+ curr -= dir;
+ break;
+ }
+ }
+ }
+ setpcmark();
+ if (both && *ml_get(curr) == '}') { // include line with '}'
+ curr++;
+ }
+ curwin->w_cursor.lnum = curr;
+ if (curr == curbuf->b_ml.ml_line_count && what != '}') {
+ char *line = ml_get(curr);
+
+ // Put the cursor on the last character in the last line and make the
+ // motion inclusive.
+ if ((curwin->w_cursor.col = (colnr_T)strlen(line)) != 0) {
+ curwin->w_cursor.col--;
+ curwin->w_cursor.col -= utf_head_off(line, line + curwin->w_cursor.col);
+ *pincl = true;
+ }
+ } else {
+ curwin->w_cursor.col = 0;
+ }
+ return true;
+}
+
+/// check if the string 's' is a nroff macro that is in option 'opt'
+static bool inmacro(char *opt, const char *s)
+{
+ char *macro;
+
+ for (macro = opt; macro[0]; macro++) {
+ // Accept two characters in the option being equal to two characters
+ // in the line. A space in the option matches with a space in the
+ // line or the line having ended.
+ if ((macro[0] == s[0]
+ || (macro[0] == ' '
+ && (s[0] == NUL || s[0] == ' ')))
+ && (macro[1] == s[1]
+ || ((macro[1] == NUL || macro[1] == ' ')
+ && (s[0] == NUL || s[1] == NUL || s[1] == ' ')))) {
+ break;
+ }
+ macro++;
+ if (macro[0] == NUL) {
+ break;
+ }
+ }
+ return macro[0] != NUL;
+}
+
+/// startPS: return true if line 'lnum' is the start of a section or paragraph.
+/// If 'para' is '{' or '}' only check for sections.
+/// If 'both' is true also stop at '}'
+bool startPS(linenr_T lnum, int para, bool both)
+{
+ char *s;
+
+ s = ml_get(lnum);
+ if ((uint8_t)(*s) == para || *s == '\f' || (both && *s == '}')) {
+ return true;
+ }
+ if (*s == '.' && (inmacro(p_sections, s + 1)
+ || (!para && inmacro(p_para, s + 1)))) {
+ return true;
+ }
+ return false;
+}
+
+// The following routines do the word searches performed by the 'w', 'W',
+// 'b', 'B', 'e', and 'E' commands.
+
+// To perform these searches, characters are placed into one of three
+// classes, and transitions between classes determine word boundaries.
+//
+// The classes are:
+//
+// 0 - white space
+// 1 - punctuation
+// 2 or higher - keyword characters (letters, digits and underscore)
+
+static bool cls_bigword; ///< true for "W", "B" or "E"
+
+/// cls() - returns the class of character at curwin->w_cursor
+///
+/// If a 'W', 'B', or 'E' motion is being done (cls_bigword == true), chars
+/// from class 2 and higher are reported as class 1 since only white space
+/// boundaries are of interest.
+static int cls(void)
+{
+ int c;
+
+ c = gchar_cursor();
+ if (c == ' ' || c == '\t' || c == NUL) {
+ return 0;
+ }
+
+ c = utf_class(c);
+
+ // If cls_bigword is true, report all non-blanks as class 1.
+ if (c != 0 && cls_bigword) {
+ return 1;
+ }
+ return c;
+}
+
+/// fwd_word(count, type, eol) - move forward one word
+///
+/// @return FAIL if the cursor was already at the end of the file.
+/// If eol is true, last word stops at end of line (for operators).
+///
+/// @param bigword "W", "E" or "B"
+int fwd_word(long count, bool bigword, bool eol)
+{
+ int sclass; // starting class
+ int i;
+ int last_line;
+
+ curwin->w_cursor.coladd = 0;
+ cls_bigword = bigword;
+ while (--count >= 0) {
+ // When inside a range of folded lines, move to the last char of the
+ // last line.
+ if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum)) {
+ coladvance(MAXCOL);
+ }
+ sclass = cls();
+
+ // We always move at least one character, unless on the last
+ // character in the buffer.
+ last_line = (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count);
+ i = inc_cursor();
+ if (i == -1 || (i >= 1 && last_line)) { // started at last char in file
+ return FAIL;
+ }
+ if (i >= 1 && eol && count == 0) { // started at last char in line
+ return OK;
+ }
+
+ // Go one char past end of current word (if any)
+ if (sclass != 0) {
+ while (cls() == sclass) {
+ i = inc_cursor();
+ if (i == -1 || (i >= 1 && eol && count == 0)) {
+ return OK;
+ }
+ }
+ }
+
+ // go to next non-white
+ while (cls() == 0) {
+ // We'll stop if we land on a blank line
+ if (curwin->w_cursor.col == 0 && *get_cursor_line_ptr() == NUL) {
+ break;
+ }
+
+ i = inc_cursor();
+ if (i == -1 || (i >= 1 && eol && count == 0)) {
+ return OK;
+ }
+ }
+ }
+ return OK;
+}
+
+/// bck_word() - move backward 'count' words
+///
+/// If stop is true and we are already on the start of a word, move one less.
+///
+/// Returns FAIL if top of the file was reached.
+int bck_word(long count, bool bigword, bool stop)
+{
+ int sclass; // starting class
+
+ curwin->w_cursor.coladd = 0;
+ cls_bigword = bigword;
+ while (--count >= 0) {
+ // When inside a range of folded lines, move to the first char of the
+ // first line.
+ if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL)) {
+ curwin->w_cursor.col = 0;
+ }
+ sclass = cls();
+ if (dec_cursor() == -1) { // started at start of file
+ return FAIL;
+ }
+
+ if (!stop || sclass == cls() || sclass == 0) {
+ // Skip white space before the word.
+ // Stop on an empty line.
+ while (cls() == 0) {
+ if (curwin->w_cursor.col == 0
+ && LINEEMPTY(curwin->w_cursor.lnum)) {
+ goto finished;
+ }
+ if (dec_cursor() == -1) { // hit start of file, stop here
+ return OK;
+ }
+ }
+
+ // Move backward to start of this word.
+ if (skip_chars(cls(), BACKWARD)) {
+ return OK;
+ }
+ }
+
+ inc_cursor(); // overshot - forward one
+finished:
+ stop = false;
+ }
+ return OK;
+}
+
+/// end_word() - move to the end of the word
+///
+/// There is an apparent bug in the 'e' motion of the real vi. At least on the
+/// System V Release 3 version for the 80386. Unlike 'b' and 'w', the 'e'
+/// motion crosses blank lines. When the real vi crosses a blank line in an
+/// 'e' motion, the cursor is placed on the FIRST character of the next
+/// non-blank line. The 'E' command, however, works correctly. Since this
+/// appears to be a bug, I have not duplicated it here.
+///
+/// Returns FAIL if end of the file was reached.
+///
+/// If stop is true and we are already on the end of a word, move one less.
+/// If empty is true stop on an empty line.
+int end_word(long count, bool bigword, bool stop, bool empty)
+{
+ int sclass; // starting class
+
+ curwin->w_cursor.coladd = 0;
+ cls_bigword = bigword;
+ while (--count >= 0) {
+ // When inside a range of folded lines, move to the last char of the
+ // last line.
+ if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum)) {
+ coladvance(MAXCOL);
+ }
+ sclass = cls();
+ if (inc_cursor() == -1) {
+ return FAIL;
+ }
+
+ // If we're in the middle of a word, we just have to move to the end
+ // of it.
+ if (cls() == sclass && sclass != 0) {
+ // Move forward to end of the current word
+ if (skip_chars(sclass, FORWARD)) {
+ return FAIL;
+ }
+ } else if (!stop || sclass == 0) {
+ // We were at the end of a word. Go to the end of the next word.
+ // First skip white space, if 'empty' is true, stop at empty line.
+ while (cls() == 0) {
+ if (empty && curwin->w_cursor.col == 0
+ && LINEEMPTY(curwin->w_cursor.lnum)) {
+ goto finished;
+ }
+ if (inc_cursor() == -1) { // hit end of file, stop here
+ return FAIL;
+ }
+ }
+
+ // Move forward to the end of this word.
+ if (skip_chars(cls(), FORWARD)) {
+ return FAIL;
+ }
+ }
+ dec_cursor(); // overshot - one char backward
+finished:
+ stop = false; // we move only one word less
+ }
+ return OK;
+}
+
+/// Move back to the end of the word.
+///
+/// @param bigword true for "B"
+/// @param eol if true, then stop at end of line.
+///
+/// @return FAIL if start of the file was reached.
+int bckend_word(long count, bool bigword, bool eol)
+{
+ int sclass; // starting class
+ int i;
+
+ curwin->w_cursor.coladd = 0;
+ cls_bigword = bigword;
+ while (--count >= 0) {
+ sclass = cls();
+ if ((i = dec_cursor()) == -1) {
+ return FAIL;
+ }
+ if (eol && i == 1) {
+ return OK;
+ }
+
+ // Move backward to before the start of this word.
+ if (sclass != 0) {
+ while (cls() == sclass) {
+ if ((i = dec_cursor()) == -1 || (eol && i == 1)) {
+ return OK;
+ }
+ }
+ }
+
+ // Move backward to end of the previous word
+ while (cls() == 0) {
+ if (curwin->w_cursor.col == 0 && LINEEMPTY(curwin->w_cursor.lnum)) {
+ break;
+ }
+ if ((i = dec_cursor()) == -1 || (eol && i == 1)) {
+ return OK;
+ }
+ }
+ }
+ return OK;
+}
+
+/// Skip a row of characters of the same class.
+///
+/// @return true when end-of-file reached, false otherwise.
+static bool skip_chars(int cclass, int dir)
+{
+ while (cls() == cclass) {
+ if ((dir == FORWARD ? inc_cursor() : dec_cursor()) == -1) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/// Go back to the start of the word or the start of white space
+static void back_in_line(void)
+{
+ int sclass; // starting class
+
+ sclass = cls();
+ for (;;) {
+ if (curwin->w_cursor.col == 0) { // stop at start of line
+ break;
+ }
+ dec_cursor();
+ if (cls() != sclass) { // stop at start of word
+ inc_cursor();
+ break;
+ }
+ }
+}
+
+static void find_first_blank(pos_T *posp)
+{
+ int c;
+
+ while (decl(posp) != -1) {
+ c = gchar_pos(posp);
+ if (!ascii_iswhite(c)) {
+ incl(posp);
+ break;
+ }
+ }
+}
+
+/// Skip count/2 sentences and count/2 separating white spaces.
+///
+/// @param at_start_sent cursor is at start of sentence
+static void findsent_forward(long count, bool at_start_sent)
+{
+ while (count--) {
+ findsent(FORWARD, 1L);
+ if (at_start_sent) {
+ find_first_blank(&curwin->w_cursor);
+ }
+ if (count == 0 || at_start_sent) {
+ decl(&curwin->w_cursor);
+ }
+ at_start_sent = !at_start_sent;
+ }
+}
+
+/// Find word under cursor, cursor at end.
+/// Used while an operator is pending, and in Visual mode.
+///
+/// @param include true: include word and white space
+/// @param bigword false == word, true == WORD
+int current_word(oparg_T *oap, long count, bool include, bool bigword)
+{
+ pos_T start_pos;
+ pos_T pos;
+ bool inclusive = true;
+ int include_white = false;
+
+ cls_bigword = bigword;
+ clearpos(&start_pos);
+
+ // Correct cursor when 'selection' is exclusive
+ if (VIsual_active && *p_sel == 'e' && lt(VIsual, curwin->w_cursor)) {
+ dec_cursor();
+ }
+
+ // When Visual mode is not active, or when the VIsual area is only one
+ // character, select the word and/or white space under the cursor.
+ if (!VIsual_active || equalpos(curwin->w_cursor, VIsual)) {
+ // Go to start of current word or white space.
+ back_in_line();
+ start_pos = curwin->w_cursor;
+
+ // If the start is on white space, and white space should be included
+ // (" word"), or start is not on white space, and white space should
+ // not be included ("word"), find end of word.
+ if ((cls() == 0) == include) {
+ if (end_word(1L, bigword, true, true) == FAIL) {
+ return FAIL;
+ }
+ } else {
+ // If the start is not on white space, and white space should be
+ // included ("word "), or start is on white space and white
+ // space should not be included (" "), find start of word.
+ // If we end up in the first column of the next line (single char
+ // word) back up to end of the line.
+ fwd_word(1L, bigword, true);
+ if (curwin->w_cursor.col == 0) {
+ decl(&curwin->w_cursor);
+ } else {
+ oneleft();
+ }
+
+ if (include) {
+ include_white = true;
+ }
+ }
+
+ if (VIsual_active) {
+ // should do something when inclusive == false !
+ VIsual = start_pos;
+ redraw_curbuf_later(UPD_INVERTED); // update the inversion
+ } else {
+ oap->start = start_pos;
+ oap->motion_type = kMTCharWise;
+ }
+ count--;
+ }
+
+ // When count is still > 0, extend with more objects.
+ while (count > 0) {
+ inclusive = true;
+ if (VIsual_active && lt(curwin->w_cursor, VIsual)) {
+ // In Visual mode, with cursor at start: move cursor back.
+ if (decl(&curwin->w_cursor) == -1) {
+ return FAIL;
+ }
+ if (include != (cls() != 0)) {
+ if (bck_word(1L, bigword, true) == FAIL) {
+ return FAIL;
+ }
+ } else {
+ if (bckend_word(1L, bigword, true) == FAIL) {
+ return FAIL;
+ }
+ (void)incl(&curwin->w_cursor);
+ }
+ } else {
+ // Move cursor forward one word and/or white area.
+ if (incl(&curwin->w_cursor) == -1) {
+ return FAIL;
+ }
+ if (include != (cls() == 0)) {
+ if (fwd_word(1L, bigword, true) == FAIL && count > 1) {
+ return FAIL;
+ }
+ // If end is just past a new-line, we don't want to include
+ // the first character on the line.
+ // Put cursor on last char of white.
+ if (oneleft() == FAIL) {
+ inclusive = false;
+ }
+ } else {
+ if (end_word(1L, bigword, true, true) == FAIL) {
+ return FAIL;
+ }
+ }
+ }
+ count--;
+ }
+
+ if (include_white && (cls() != 0
+ || (curwin->w_cursor.col == 0 && !inclusive))) {
+ // If we don't include white space at the end, move the start
+ // to include some white space there. This makes "daw" work
+ // better on the last word in a sentence (and "2daw" on last-but-one
+ // word). Also when "2daw" deletes "word." at the end of the line
+ // (cursor is at start of next line).
+ // But don't delete white space at start of line (indent).
+ pos = curwin->w_cursor; // save cursor position
+ curwin->w_cursor = start_pos;
+ if (oneleft() == OK) {
+ back_in_line();
+ if (cls() == 0 && curwin->w_cursor.col > 0) {
+ if (VIsual_active) {
+ VIsual = curwin->w_cursor;
+ } else {
+ oap->start = curwin->w_cursor;
+ }
+ }
+ }
+ curwin->w_cursor = pos; // put cursor back at end
+ }
+
+ if (VIsual_active) {
+ if (*p_sel == 'e' && inclusive && ltoreq(VIsual, curwin->w_cursor)) {
+ inc_cursor();
+ }
+ if (VIsual_mode == 'V') {
+ VIsual_mode = 'v';
+ redraw_cmdline = true; // show mode later
+ }
+ } else {
+ oap->inclusive = inclusive;
+ }
+
+ return OK;
+}
+
+/// Find sentence(s) under the cursor, cursor at end.
+/// When Visual active, extend it by one or more sentences.
+int current_sent(oparg_T *oap, long count, bool include)
+{
+ pos_T start_pos;
+ pos_T pos;
+ bool start_blank;
+ int c;
+ bool at_start_sent;
+ long ncount;
+
+ start_pos = curwin->w_cursor;
+ pos = start_pos;
+ findsent(FORWARD, 1L); // Find start of next sentence.
+
+ // When the Visual area is bigger than one character: Extend it.
+ if (VIsual_active && !equalpos(start_pos, VIsual)) {
+extend:
+ if (lt(start_pos, VIsual)) {
+ // Cursor at start of Visual area.
+ // Find out where we are:
+ // - in the white space before a sentence
+ // - in a sentence or just after it
+ // - at the start of a sentence
+ at_start_sent = true;
+ decl(&pos);
+ while (lt(pos, curwin->w_cursor)) {
+ c = gchar_pos(&pos);
+ if (!ascii_iswhite(c)) {
+ at_start_sent = false;
+ break;
+ }
+ incl(&pos);
+ }
+ if (!at_start_sent) {
+ findsent(BACKWARD, 1L);
+ if (equalpos(curwin->w_cursor, start_pos)) {
+ at_start_sent = true; // exactly at start of sentence
+ } else {
+ // inside a sentence, go to its end (start of next)
+ findsent(FORWARD, 1L);
+ }
+ }
+ if (include) { // "as" gets twice as much as "is"
+ count *= 2;
+ }
+ while (count--) {
+ if (at_start_sent) {
+ find_first_blank(&curwin->w_cursor);
+ }
+ c = gchar_cursor();
+ if (!at_start_sent || (!include && !ascii_iswhite(c))) {
+ findsent(BACKWARD, 1L);
+ }
+ at_start_sent = !at_start_sent;
+ }
+ } else {
+ // Cursor at end of Visual area.
+ // Find out where we are:
+ // - just before a sentence
+ // - just before or in the white space before a sentence
+ // - in a sentence
+ incl(&pos);
+ at_start_sent = true;
+ if (!equalpos(pos, curwin->w_cursor)) { // not just before a sentence
+ at_start_sent = false;
+ while (lt(pos, curwin->w_cursor)) {
+ c = gchar_pos(&pos);
+ if (!ascii_iswhite(c)) {
+ at_start_sent = true;
+ break;
+ }
+ incl(&pos);
+ }
+ if (at_start_sent) { // in the sentence
+ findsent(BACKWARD, 1L);
+ } else { // in/before white before a sentence
+ curwin->w_cursor = start_pos;
+ }
+ }
+
+ if (include) { // "as" gets twice as much as "is"
+ count *= 2;
+ }
+ findsent_forward(count, at_start_sent);
+ if (*p_sel == 'e') {
+ curwin->w_cursor.col++;
+ }
+ }
+ return OK;
+ }
+
+ // If the cursor started on a blank, check if it is just before the start
+ // of the next sentence.
+ while (c = gchar_pos(&pos), ascii_iswhite(c)) {
+ incl(&pos);
+ }
+ if (equalpos(pos, curwin->w_cursor)) {
+ start_blank = true;
+ find_first_blank(&start_pos); // go back to first blank
+ } else {
+ start_blank = false;
+ findsent(BACKWARD, 1L);
+ start_pos = curwin->w_cursor;
+ }
+ if (include) {
+ ncount = count * 2;
+ } else {
+ ncount = count;
+ if (start_blank) {
+ ncount--;
+ }
+ }
+ if (ncount > 0) {
+ findsent_forward(ncount, true);
+ } else {
+ decl(&curwin->w_cursor);
+ }
+
+ if (include) {
+ // If the blank in front of the sentence is included, exclude the
+ // blanks at the end of the sentence, go back to the first blank.
+ // If there are no trailing blanks, try to include leading blanks.
+ if (start_blank) {
+ find_first_blank(&curwin->w_cursor);
+ c = gchar_pos(&curwin->w_cursor);
+ if (ascii_iswhite(c)) {
+ decl(&curwin->w_cursor);
+ }
+ } else if (c = gchar_cursor(), !ascii_iswhite(c)) {
+ find_first_blank(&start_pos);
+ }
+ }
+
+ if (VIsual_active) {
+ // Avoid getting stuck with "is" on a single space before a sentence.
+ if (equalpos(start_pos, curwin->w_cursor)) {
+ goto extend;
+ }
+ if (*p_sel == 'e') {
+ curwin->w_cursor.col++;
+ }
+ VIsual = start_pos;
+ VIsual_mode = 'v';
+ redraw_cmdline = true; // show mode later
+ redraw_curbuf_later(UPD_INVERTED); // update the inversion
+ } else {
+ // include a newline after the sentence, if there is one
+ if (incl(&curwin->w_cursor) == -1) {
+ oap->inclusive = true;
+ } else {
+ oap->inclusive = false;
+ }
+ oap->start = start_pos;
+ oap->motion_type = kMTCharWise;
+ }
+ return OK;
+}
+
+/// Find block under the cursor, cursor at end.
+/// "what" and "other" are two matching parenthesis/brace/etc.
+///
+/// @param include true == include white space
+/// @param what '(', '{', etc.
+/// @param other ')', '}', etc.
+int current_block(oparg_T *oap, long count, bool include, int what, int other)
+{
+ pos_T old_pos;
+ pos_T *pos = NULL;
+ pos_T start_pos;
+ pos_T *end_pos;
+ pos_T old_start, old_end;
+ char *save_cpo;
+ bool sol = false; // '{' at start of line
+
+ old_pos = curwin->w_cursor;
+ old_end = curwin->w_cursor; // remember where we started
+ old_start = old_end;
+
+ // If we start on '(', '{', ')', '}', etc., use the whole block inclusive.
+ if (!VIsual_active || equalpos(VIsual, curwin->w_cursor)) {
+ setpcmark();
+ if (what == '{') { // ignore indent
+ while (inindent(1)) {
+ if (inc_cursor() != 0) {
+ break;
+ }
+ }
+ }
+ if (gchar_cursor() == what) {
+ // cursor on '(' or '{', move cursor just after it
+ curwin->w_cursor.col++;
+ }
+ } else if (lt(VIsual, curwin->w_cursor)) {
+ old_start = VIsual;
+ curwin->w_cursor = VIsual; // cursor at low end of Visual
+ } else {
+ old_end = VIsual;
+ }
+
+ // Search backwards for unclosed '(', '{', etc..
+ // Put this position in start_pos.
+ // Ignore quotes here. Keep the "M" flag in 'cpo', as that is what the
+ // user wants.
+ save_cpo = p_cpo;
+ 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) {
+ break;
+ }
+ curwin->w_cursor = *pos;
+ start_pos = *pos; // the findmatch for end_pos will overwrite *pos
+ }
+ } else {
+ while (count-- > 0) {
+ if ((pos = findmatchlimit(NULL, what, FM_FORWARD, 0)) == NULL) {
+ break;
+ }
+ curwin->w_cursor = *pos;
+ start_pos = *pos; // the findmatch for end_pos will overwrite *pos
+ }
+ }
+ p_cpo = save_cpo;
+
+ // Search for matching ')', '}', etc.
+ // Put this position in curwin->w_cursor.
+ if (pos == NULL || (end_pos = findmatch(NULL, other)) == NULL) {
+ curwin->w_cursor = old_pos;
+ return FAIL;
+ }
+ curwin->w_cursor = *end_pos;
+
+ // Try to exclude the '(', '{', ')', '}', etc. when "include" is false.
+ // If the ending '}', ')' or ']' is only preceded by indent, skip that
+ // indent. But only if the resulting area is not smaller than what we
+ // started with.
+ while (!include) {
+ incl(&start_pos);
+ sol = (curwin->w_cursor.col == 0);
+ decl(&curwin->w_cursor);
+ while (inindent(1)) {
+ sol = true;
+ if (decl(&curwin->w_cursor) != 0) {
+ break;
+ }
+ }
+
+ // 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);
+ if ((pos = findmatch(NULL, what)) == NULL) {
+ curwin->w_cursor = old_pos;
+ return FAIL;
+ }
+ start_pos = *pos;
+ curwin->w_cursor = *pos;
+ if ((end_pos = findmatch(NULL, other)) == NULL) {
+ curwin->w_cursor = old_pos;
+ return FAIL;
+ }
+ curwin->w_cursor = *end_pos;
+ } else {
+ break;
+ }
+ }
+
+ if (VIsual_active) {
+ if (*p_sel == 'e') {
+ inc(&curwin->w_cursor);
+ }
+ if (sol && gchar_cursor() != NUL) {
+ inc(&curwin->w_cursor); // include the line break
+ }
+ VIsual = start_pos;
+ VIsual_mode = 'v';
+ redraw_curbuf_later(UPD_INVERTED); // update the inversion
+ showmode();
+ } else {
+ oap->start = start_pos;
+ oap->motion_type = kMTCharWise;
+ oap->inclusive = false;
+ if (sol) {
+ incl(&curwin->w_cursor);
+ } else if (ltoreq(start_pos, curwin->w_cursor)) {
+ // Include the character under the cursor.
+ oap->inclusive = true;
+ } else {
+ // End is before the start (no text in between <>, [], etc.): don't
+ // operate on any text.
+ curwin->w_cursor = start_pos;
+ }
+ }
+
+ 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/>".
+static bool in_html_tag(bool end_tag)
+{
+ char *line = get_cursor_line_ptr();
+ char *p;
+ int c;
+ int lc = NUL;
+ pos_T pos;
+
+ for (p = line + curwin->w_cursor.col; p > line;) {
+ if (*p == '<') { // find '<' under/before cursor
+ break;
+ }
+ MB_PTR_BACK(line, p);
+ if (*p == '>') { // find '>' before cursor
+ break;
+ }
+ }
+ if (*p != '<') {
+ return false;
+ }
+
+ pos.lnum = curwin->w_cursor.lnum;
+ pos.col = (colnr_T)(p - line);
+
+ MB_PTR_ADV(p);
+ if (end_tag) {
+ // check that there is a '/' after the '<'
+ return *p == '/';
+ }
+
+ // check that there is no '/' after the '<'
+ if (*p == '/') {
+ return false;
+ }
+
+ // check that the matching '>' is not preceded by '/'
+ for (;;) {
+ if (inc(&pos) < 0) {
+ return false;
+ }
+ c = (uint8_t)(*ml_get_pos(&pos));
+ if (c == '>') {
+ break;
+ }
+ lc = c;
+ }
+ return lc != '/';
+}
+
+/// Find tag block under the cursor, cursor at end.
+///
+/// @param include true == include white space
+int current_tagblock(oparg_T *oap, long count_arg, bool include)
+{
+ long count = count_arg;
+ pos_T old_pos;
+ pos_T start_pos;
+ pos_T end_pos;
+ pos_T old_start, old_end;
+ char *p;
+ char *cp;
+ int len;
+ bool do_include = include;
+ bool save_p_ws = p_ws;
+ int retval = FAIL;
+ int is_inclusive = true;
+
+ p_ws = false;
+
+ old_pos = curwin->w_cursor;
+ old_end = curwin->w_cursor; // remember where we started
+ old_start = old_end;
+ if (!VIsual_active || *p_sel == 'e') {
+ decl(&old_end); // old_end is inclusive
+ }
+
+ // If we start on "<aaa>" select that block.
+ if (!VIsual_active || equalpos(VIsual, curwin->w_cursor)) {
+ setpcmark();
+
+ // ignore indent
+ while (inindent(1)) {
+ if (inc_cursor() != 0) {
+ break;
+ }
+ }
+
+ if (in_html_tag(false)) {
+ // cursor on start tag, move to its '>'
+ while (*get_cursor_pos_ptr() != '>') {
+ if (inc_cursor() < 0) {
+ break;
+ }
+ }
+ } else if (in_html_tag(true)) {
+ // cursor on end tag, move to just before it
+ while (*get_cursor_pos_ptr() != '<') {
+ if (dec_cursor() < 0) {
+ break;
+ }
+ }
+ dec_cursor();
+ old_end = curwin->w_cursor;
+ }
+ } else if (lt(VIsual, curwin->w_cursor)) {
+ old_start = VIsual;
+ curwin->w_cursor = VIsual; // cursor at low end of Visual
+ } else {
+ old_end = VIsual;
+ }
+
+again:
+ // Search backwards for unclosed "<aaa>".
+ // Put this position in start_pos.
+ for (long n = 0; n < count; n++) {
+ if (do_searchpair("<[^ \t>/!]\\+\\%(\\_s\\_[^>]\\{-}[^/]>\\|$\\|\\_s\\=>\\)",
+ "",
+ "</[^>]*>", BACKWARD, NULL, 0,
+ NULL, (linenr_T)0, 0L) <= 0) {
+ curwin->w_cursor = old_pos;
+ goto theend;
+ }
+ }
+ start_pos = curwin->w_cursor;
+
+ // Search for matching "</aaa>". First isolate the "aaa".
+ inc_cursor();
+ p = get_cursor_pos_ptr();
+ for (cp = p;
+ *cp != NUL && *cp != '>' && !ascii_iswhite(*cp);
+ MB_PTR_ADV(cp)) {}
+ len = (int)(cp - p);
+ if (len == 0) {
+ curwin->w_cursor = old_pos;
+ goto theend;
+ }
+ const size_t spat_len = (size_t)len + 39;
+ char *const spat = xmalloc(spat_len);
+ const size_t epat_len = (size_t)len + 9;
+ char *const epat = xmalloc(epat_len);
+ snprintf(spat, spat_len,
+ "<%.*s\\>\\%%(\\_s\\_[^>]\\{-}\\_[^/]>\\|\\_s\\?>\\)\\c", len, p);
+ snprintf(epat, epat_len, "</%.*s>\\c", len, p);
+
+ const int r = (int)do_searchpair(spat, "", epat, FORWARD, NULL, 0, NULL, (linenr_T)0, 0L);
+
+ xfree(spat);
+ xfree(epat);
+
+ if (r < 1 || lt(curwin->w_cursor, old_end)) {
+ // Can't find other end or it's before the previous end. Could be a
+ // HTML tag that doesn't have a matching end. Search backwards for
+ // another starting tag.
+ count = 1;
+ curwin->w_cursor = start_pos;
+ goto again;
+ }
+
+ if (do_include) {
+ // Include up to the '>'.
+ while (*get_cursor_pos_ptr() != '>') {
+ if (inc_cursor() < 0) {
+ break;
+ }
+ }
+ } else {
+ char *c = get_cursor_pos_ptr();
+ // Exclude the '<' of the end tag.
+ // If the closing tag is on new line, do not decrement cursor, but make
+ // operation exclusive, so that the linefeed will be selected
+ if (*c == '<' && !VIsual_active && curwin->w_cursor.col == 0) {
+ // do not decrement cursor
+ is_inclusive = false;
+ } else if (*c == '<') {
+ dec_cursor();
+ }
+ }
+ end_pos = curwin->w_cursor;
+
+ if (!do_include) {
+ // Exclude the start tag.
+ curwin->w_cursor = start_pos;
+ while (inc_cursor() >= 0) {
+ if (*get_cursor_pos_ptr() == '>') {
+ inc_cursor();
+ start_pos = curwin->w_cursor;
+ break;
+ }
+ }
+ curwin->w_cursor = end_pos;
+
+ // If we are in Visual mode and now have the same text as before set
+ // "do_include" and try again.
+ if (VIsual_active
+ && equalpos(start_pos, old_start)
+ && equalpos(end_pos, old_end)) {
+ do_include = true;
+ curwin->w_cursor = old_start;
+ count = count_arg;
+ goto again;
+ }
+ }
+
+ if (VIsual_active) {
+ // If the end is before the start there is no text between tags, select
+ // the char under the cursor.
+ if (lt(end_pos, start_pos)) {
+ curwin->w_cursor = start_pos;
+ } else if (*p_sel == 'e') {
+ inc_cursor();
+ }
+ VIsual = start_pos;
+ VIsual_mode = 'v';
+ redraw_curbuf_later(UPD_INVERTED); // update the inversion
+ showmode();
+ } else {
+ oap->start = start_pos;
+ oap->motion_type = kMTCharWise;
+ if (lt(end_pos, start_pos)) {
+ // End is before the start: there is no text between tags; operate
+ // on an empty area.
+ curwin->w_cursor = start_pos;
+ oap->inclusive = false;
+ } else {
+ oap->inclusive = is_inclusive;
+ }
+ }
+ retval = OK;
+
+theend:
+ p_ws = save_p_ws;
+ return retval;
+}
+
+/// @param include true == include white space
+/// @param type 'p' for paragraph, 'S' for section
+int current_par(oparg_T *oap, long count, bool include, int type)
+{
+ linenr_T start_lnum;
+ linenr_T end_lnum;
+ int white_in_front;
+ int dir;
+ int start_is_white;
+ int prev_start_is_white;
+ int retval = OK;
+ int do_white = false;
+ int t;
+ int i;
+
+ if (type == 'S') { // not implemented yet
+ return FAIL;
+ }
+
+ start_lnum = curwin->w_cursor.lnum;
+
+ // When visual area is more than one line: extend it.
+ if (VIsual_active && start_lnum != VIsual.lnum) {
+extend:
+ if (start_lnum < VIsual.lnum) {
+ dir = BACKWARD;
+ } else {
+ dir = FORWARD;
+ }
+ for (i = (int)count; --i >= 0;) {
+ if (start_lnum ==
+ (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count)) {
+ retval = FAIL;
+ break;
+ }
+
+ prev_start_is_white = -1;
+ for (t = 0; t < 2; t++) {
+ start_lnum += dir;
+ start_is_white = linewhite(start_lnum);
+ if (prev_start_is_white == start_is_white) {
+ start_lnum -= dir;
+ break;
+ }
+ for (;;) {
+ if (start_lnum == (dir == BACKWARD
+ ? 1 : curbuf->b_ml.ml_line_count)) {
+ break;
+ }
+ if (start_is_white != linewhite(start_lnum + dir)
+ || (!start_is_white
+ && startPS(start_lnum + (dir > 0
+ ? 1 : 0), 0, 0))) {
+ break;
+ }
+ start_lnum += dir;
+ }
+ if (!include) {
+ break;
+ }
+ if (start_lnum == (dir == BACKWARD
+ ? 1 : curbuf->b_ml.ml_line_count)) {
+ break;
+ }
+ prev_start_is_white = start_is_white;
+ }
+ }
+ curwin->w_cursor.lnum = start_lnum;
+ curwin->w_cursor.col = 0;
+ return retval;
+ }
+
+ // First move back to the start_lnum of the paragraph or white lines
+ white_in_front = linewhite(start_lnum);
+ while (start_lnum > 1) {
+ if (white_in_front) { // stop at first white line
+ if (!linewhite(start_lnum - 1)) {
+ break;
+ }
+ } else { // stop at first non-white line of start of paragraph
+ if (linewhite(start_lnum - 1) || startPS(start_lnum, 0, 0)) {
+ break;
+ }
+ }
+ start_lnum--;
+ }
+
+ // Move past the end of any white lines.
+ end_lnum = start_lnum;
+ while (end_lnum <= curbuf->b_ml.ml_line_count && linewhite(end_lnum)) {
+ end_lnum++;
+ }
+
+ end_lnum--;
+ i = (int)count;
+ if (!include && white_in_front) {
+ i--;
+ }
+ while (i--) {
+ if (end_lnum == curbuf->b_ml.ml_line_count) {
+ return FAIL;
+ }
+
+ if (!include) {
+ do_white = linewhite(end_lnum + 1);
+ }
+
+ if (include || !do_white) {
+ end_lnum++;
+ // skip to end of paragraph
+ while (end_lnum < curbuf->b_ml.ml_line_count
+ && !linewhite(end_lnum + 1)
+ && !startPS(end_lnum + 1, 0, 0)) {
+ end_lnum++;
+ }
+ }
+
+ if (i == 0 && white_in_front && include) {
+ break;
+ }
+
+ // skip to end of white lines after paragraph
+ if (include || do_white) {
+ while (end_lnum < curbuf->b_ml.ml_line_count
+ && linewhite(end_lnum + 1)) {
+ end_lnum++;
+ }
+ }
+ }
+
+ // If there are no empty lines at the end, try to find some empty lines at
+ // the start (unless that has been done already).
+ if (!white_in_front && !linewhite(end_lnum) && include) {
+ while (start_lnum > 1 && linewhite(start_lnum - 1)) {
+ start_lnum--;
+ }
+ }
+
+ if (VIsual_active) {
+ // Problem: when doing "Vipipip" nothing happens in a single white
+ // line, we get stuck there. Trap this here.
+ if (VIsual_mode == 'V' && start_lnum == curwin->w_cursor.lnum) {
+ goto extend;
+ }
+ if (VIsual.lnum != start_lnum) {
+ VIsual.lnum = start_lnum;
+ VIsual.col = 0;
+ }
+ VIsual_mode = 'V';
+ redraw_curbuf_later(UPD_INVERTED); // update the inversion
+ showmode();
+ } else {
+ oap->start.lnum = start_lnum;
+ oap->start.col = 0;
+ oap->motion_type = kMTLineWise;
+ }
+ curwin->w_cursor.lnum = end_lnum;
+ curwin->w_cursor.col = 0;
+
+ 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.
+///
+/// @param escape escape characters, can be NULL
+///
+/// @return column number of "quotechar" or -1 when not found.
+static int find_next_quote(char *line, int col, int quotechar, char *escape)
+{
+ int c;
+
+ for (;;) {
+ c = (uint8_t)line[col];
+ if (c == NUL) {
+ return -1;
+ } else if (escape != NULL && vim_strchr(escape, c)) {
+ col++;
+ if (line[col] == NUL) {
+ return -1;
+ }
+ } else if (c == quotechar) {
+ break;
+ }
+ col += utfc_ptr2len(line + col);
+ }
+ return col;
+}
+
+/// Search backwards in "line" from column "col_start" to find "quotechar".
+/// Quote character escaped by one of the characters in "escape" is not counted
+/// as a quote.
+///
+/// @param escape escape characters, can be NULL
+///
+/// @return the found column or zero.
+static int find_prev_quote(char *line, int col_start, int quotechar, char *escape)
+{
+ int n;
+
+ while (col_start > 0) {
+ col_start--;
+ col_start -= utf_head_off(line, line + col_start);
+ n = 0;
+ if (escape != NULL) {
+ while (col_start - n > 0 && vim_strchr(escape,
+ (uint8_t)line[col_start - n - 1]) != NULL) {
+ n++;
+ }
+ }
+ if (n & 1) {
+ col_start -= n; // uneven number of escape chars, skip it
+ } else if ((uint8_t)line[col_start] == quotechar) {
+ break;
+ }
+ }
+ return col_start;
+}
+
+/// Find quote under the cursor, cursor at end.
+///
+/// @param include true == include quote char
+/// @param quotechar Quote character
+///
+/// @return true if found, else false.
+bool current_quote(oparg_T *oap, long count, bool include, int quotechar)
+ FUNC_ATTR_NONNULL_ALL
+{
+ char *line = get_cursor_line_ptr();
+ int col_end;
+ int col_start = curwin->w_cursor.col;
+ bool inclusive = false;
+ bool vis_empty = true; // Visual selection <= 1 char
+ bool vis_bef_curs = false; // Visual starts before cursor
+ bool did_exclusive_adj = false; // adjusted pos for 'selection'
+ bool inside_quotes = false; // Looks like "i'" done before
+ bool selected_quote = false; // Has quote inside selection
+ int i;
+ bool restore_vis_bef = false; // restore VIsual on abort
+
+ // When 'selection' is "exclusive" move the cursor to where it would be
+ // with 'selection' "inclusive", so that the logic is the same for both.
+ // The cursor then is moved forward after adjusting the area.
+ if (VIsual_active) {
+ // this only works within one line
+ if (VIsual.lnum != curwin->w_cursor.lnum) {
+ return false;
+ }
+
+ vis_bef_curs = lt(VIsual, curwin->w_cursor);
+ vis_empty = equalpos(VIsual, curwin->w_cursor);
+ if (*p_sel == 'e') {
+ if (vis_bef_curs) {
+ dec_cursor();
+ did_exclusive_adj = true;
+ } else if (!vis_empty) {
+ dec(&VIsual);
+ did_exclusive_adj = true;
+ }
+ vis_empty = equalpos(VIsual, curwin->w_cursor);
+ if (!vis_bef_curs && !vis_empty) {
+ // VIsual needs to be start of Visual selection.
+ pos_T t = curwin->w_cursor;
+
+ curwin->w_cursor = VIsual;
+ VIsual = t;
+ vis_bef_curs = true;
+ restore_vis_bef = true;
+ }
+ }
+ }
+
+ if (!vis_empty) {
+ // Check if the existing selection exactly spans the text inside
+ // quotes.
+ if (vis_bef_curs) {
+ inside_quotes = VIsual.col > 0
+ && (uint8_t)line[VIsual.col - 1] == quotechar
+ && line[curwin->w_cursor.col] != NUL
+ && (uint8_t)line[curwin->w_cursor.col + 1] == quotechar;
+ i = VIsual.col;
+ col_end = curwin->w_cursor.col;
+ } else {
+ inside_quotes = curwin->w_cursor.col > 0
+ && (uint8_t)line[curwin->w_cursor.col - 1] == quotechar
+ && line[VIsual.col] != NUL
+ && (uint8_t)line[VIsual.col + 1] == quotechar;
+ i = curwin->w_cursor.col;
+ col_end = VIsual.col;
+ }
+
+ // 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 ((uint8_t)line[i++] == quotechar) {
+ selected_quote = true;
+ break;
+ }
+ }
+ }
+
+ if (!vis_empty && (uint8_t)line[col_start] == quotechar) {
+ // Already selecting something and on a quote character. Find the
+ // next quoted string.
+ if (vis_bef_curs) {
+ // Assume we are on a closing quote: move to after the next
+ // opening quote.
+ col_start = find_next_quote(line, col_start + 1, quotechar, NULL);
+ if (col_start < 0) {
+ goto abort_search;
+ }
+ col_end = find_next_quote(line, col_start + 1, quotechar, curbuf->b_p_qe);
+ if (col_end < 0) {
+ // We were on a starting quote perhaps?
+ col_end = col_start;
+ col_start = curwin->w_cursor.col;
+ }
+ } else {
+ col_end = find_prev_quote(line, col_start, quotechar, NULL);
+ if ((uint8_t)line[col_end] != quotechar) {
+ goto abort_search;
+ }
+ col_start = find_prev_quote(line, col_end, quotechar, curbuf->b_p_qe);
+ if ((uint8_t)line[col_start] != quotechar) {
+ // We were on an ending quote perhaps?
+ col_start = col_end;
+ col_end = curwin->w_cursor.col;
+ }
+ }
+ } else if ((uint8_t)line[col_start] == quotechar || !vis_empty) {
+ int first_col = col_start;
+
+ if (!vis_empty) {
+ if (vis_bef_curs) {
+ first_col = find_next_quote(line, col_start, quotechar, NULL);
+ } else {
+ first_col = find_prev_quote(line, col_start, quotechar, NULL);
+ }
+ }
+ // The cursor is on a quote, we don't know if it's the opening or
+ // closing quote. Search from the start of the line to find out.
+ // Also do this when there is a Visual area, a' may leave the cursor
+ // in between two strings.
+ col_start = 0;
+ for (;;) {
+ // Find open quote character.
+ col_start = find_next_quote(line, col_start, quotechar, NULL);
+ if (col_start < 0 || col_start > first_col) {
+ goto abort_search;
+ }
+ // Find close quote character.
+ col_end = find_next_quote(line, col_start + 1, quotechar, curbuf->b_p_qe);
+ if (col_end < 0) {
+ goto abort_search;
+ }
+ // If is cursor between start and end quote character, it is
+ // target text object.
+ if (col_start <= first_col && first_col <= col_end) {
+ break;
+ }
+ col_start = col_end + 1;
+ }
+ } else {
+ // Search backward for a starting quote.
+ col_start = find_prev_quote(line, col_start, quotechar, curbuf->b_p_qe);
+ if ((uint8_t)line[col_start] != quotechar) {
+ // No quote before the cursor, look after the cursor.
+ col_start = find_next_quote(line, col_start, quotechar, NULL);
+ if (col_start < 0) {
+ goto abort_search;
+ }
+ }
+
+ // Find close quote character.
+ col_end = find_next_quote(line, col_start + 1, quotechar, curbuf->b_p_qe);
+ if (col_end < 0) {
+ goto abort_search;
+ }
+ }
+
+ // When "include" is true, include spaces after closing quote or before
+ // the starting quote.
+ if (include) {
+ if (ascii_iswhite(line[col_end + 1])) {
+ while (ascii_iswhite(line[col_end + 1])) {
+ col_end++;
+ }
+ } else {
+ while (col_start > 0 && ascii_iswhite(line[col_start - 1])) {
+ col_start--;
+ }
+ }
+ }
+
+ // 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++;
+ }
+ curwin->w_cursor.col = col_start;
+ if (VIsual_active) {
+ // Set the start of the Visual area when the Visual area was empty, we
+ // were just inside quotes or the Visual area didn't start at a quote
+ // and didn't include a quote.
+ if (vis_empty
+ || (vis_bef_curs
+ && !selected_quote
+ && (inside_quotes
+ || ((uint8_t)line[VIsual.col] != quotechar
+ && (VIsual.col == 0
+ || (uint8_t)line[VIsual.col - 1] != quotechar))))) {
+ VIsual = curwin->w_cursor;
+ redraw_curbuf_later(UPD_INVERTED);
+ }
+ } else {
+ oap->start = curwin->w_cursor;
+ oap->motion_type = kMTCharWise;
+ }
+
+ // Set end position.
+ curwin->w_cursor.col = col_end;
+ if ((include || count > 1
+ // After vi" another i" must include the ".
+ || (!vis_empty && inside_quotes)) && inc_cursor() == 2) {
+ inclusive = true;
+ }
+ if (VIsual_active) {
+ if (vis_empty || vis_bef_curs) {
+ // decrement cursor when 'selection' is not exclusive
+ if (*p_sel != 'e') {
+ dec_cursor();
+ }
+ } else {
+ // Cursor is at start of Visual area. Set the end of the Visual
+ // area when it was just inside quotes or it didn't end at a
+ // quote.
+ if (inside_quotes
+ || (!selected_quote
+ && (uint8_t)line[VIsual.col] != quotechar
+ && (line[VIsual.col] == NUL
+ || (uint8_t)line[VIsual.col + 1] != quotechar))) {
+ dec_cursor();
+ VIsual = curwin->w_cursor;
+ }
+ curwin->w_cursor.col = col_start;
+ }
+ if (VIsual_mode == 'V') {
+ VIsual_mode = 'v';
+ redraw_cmdline = true; // show mode later
+ }
+ } else {
+ // Set inclusive and other oap's flags.
+ oap->inclusive = inclusive;
+ }
+
+ return true;
+
+abort_search:
+ if (VIsual_active && *p_sel == 'e') {
+ if (did_exclusive_adj) {
+ inc_cursor();
+ }
+ if (restore_vis_bef) {
+ pos_T t = curwin->w_cursor;
+
+ curwin->w_cursor = VIsual;
+ VIsual = t;
+ }
+ }
+ return false;
+}
diff --git a/src/nvim/textobject.h b/src/nvim/textobject.h
new file mode 100644
index 0000000000..e31557f297
--- /dev/null
+++ b/src/nvim/textobject.h
@@ -0,0 +1,11 @@
+#ifndef NVIM_TEXTOBJECT_H
+#define NVIM_TEXTOBJECT_H
+
+#include "nvim/normal.h"
+#include "nvim/pos.h"
+#include "nvim/vim.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "textobject.h.generated.h"
+#endif
+#endif // NVIM_TEXTOBJECT_H
diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c
index 61a59bcf06..2cb39ab26b 100644
--- a/src/nvim/tui/input.c
+++ b/src/nvim/tui/input.c
@@ -1,21 +1,30 @@
// 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
-#include "nvim/api/vim.h"
#include "nvim/ascii.h"
-#include "nvim/autocmd.h"
#include "nvim/charset.h"
-#include "nvim/ex_docmd.h"
+#include "nvim/event/defs.h"
+#include "nvim/log.h"
#include "nvim/macros.h"
#include "nvim/main.h"
+#include "nvim/map.h"
+#include "nvim/memory.h"
#include "nvim/option.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/tui/input.h"
+#include "nvim/tui/input_defs.h"
#include "nvim/tui/tui.h"
-#include "nvim/vim.h"
-#ifdef WIN32
+#include "nvim/types.h"
+#include "nvim/ui_client.h"
+#ifdef MSWIN
# include "nvim/os/os_win_console.h"
#endif
#include "nvim/event/rstream.h"
@@ -140,33 +149,17 @@ void tinput_init(TermInput *input, Loop *loop)
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
-#ifdef WIN32
- if (!os_isatty(input->in_fd)) {
- input->in_fd = os_get_conin_fd();
- }
-#else
- if (!os_isatty(input->in_fd) && os_isatty(STDERR_FILENO)) {
- input->in_fd = STDERR_FILENO;
- }
-#endif
- input_global_fd_init(input->in_fd);
+ input->in_fd = STDIN_FILENO;
const char *term = os_getenv("TERM");
if (!term) {
term = ""; // termkey_new_abstract assumes non-null (#2745)
}
-#if TERMKEY_VERSION_MAJOR > 0 || TERMKEY_VERSION_MINOR > 18
input->tk = termkey_new_abstract(term,
TERMKEY_FLAG_UTF8 | TERMKEY_FLAG_NOSTART);
- termkey_hook_terminfo_getstr(input->tk, input->tk_ti_hook_fn, NULL);
+ termkey_hook_terminfo_getstr(input->tk, input->tk_ti_hook_fn, input);
termkey_start(input->tk);
-#else
- input->tk = termkey_new_abstract(term, TERMKEY_FLAG_UTF8);
-#endif
int curflags = termkey_get_canonflags(input->tk);
termkey_set_canonflags(input->tk, curflags | TERMKEY_CANON_DELBS);
@@ -211,16 +204,12 @@ static void tinput_wait_enqueue(void **argv)
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,
- keys.data, keys.size, (intptr_t)input->paste);
- }
+ MAXSIZE_TEMP_ARRAY(args, 3);
+ ADD_C(args, STRING_OBJ(keys)); // 'data'
+ ADD_C(args, BOOLEAN_OBJ(true)); // 'crlf'
+ ADD_C(args, INTEGER_OBJ(input->paste)); // 'phase'
+ rpc_send_event(ui_client_channel_id, "nvim_paste", args);
+ api_free_string(keys);
if (input->paste == 1) {
// Paste phase: "continue"
input->paste = 2;
@@ -229,60 +218,22 @@ static void tinput_wait_enqueue(void **argv)
} 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);
- }
- if (consumed) {
- rbuffer_consumed(input->key_buffer, consumed);
- }
+ MAXSIZE_TEMP_ARRAY(args, 1);
+ ADD_C(args, STRING_OBJ(keys));
+ // NOTE: This is non-blocking and won't check partially processed input,
+ // but should be fine as all big sends are handled with nvim_paste, not nvim_input
+ rpc_send_event(ui_client_channel_id, "nvim_input", args);
+ rbuffer_consumed(input->key_buffer, len);
rbuffer_reset(input->key_buffer);
- if (consumed < len) {
- break;
- }
}
}
- uv_mutex_lock(&input->key_buffer_mutex);
- input->waiting = false;
- uv_cond_signal(&input->key_buffer_cond);
- uv_mutex_unlock(&input->key_buffer_mutex);
-}
-
-static void tinput_paste_event(void **argv)
-{
- String keys = { .data = argv[0], .size = (size_t)argv[1] };
- intptr_t phase = (intptr_t)argv[2];
-
- Error err = ERROR_INIT;
- nvim_paste(keys, true, phase, &err);
- if (ERROR_SET(&err)) {
- semsg("paste: %s", err.msg);
- api_clear_error(&err);
- }
-
- api_free_string(keys);
}
static void tinput_flush(TermInput *input, bool wait_until_empty)
{
size_t drain_boundary = wait_until_empty ? 0 : 0xff;
do {
- uv_mutex_lock(&input->key_buffer_mutex);
- loop_schedule_fast(&main_loop, event_create(tinput_wait_enqueue, 1, input));
- input->waiting = true;
- while (input->waiting) {
- uv_cond_wait(&input->key_buffer_cond, &input->key_buffer_mutex);
- }
- uv_mutex_unlock(&input->key_buffer_mutex);
+ tinput_wait_enqueue((void **)&input);
} while (rbuffer_size(input->key_buffer) > drain_boundary);
}
@@ -328,15 +279,14 @@ static void forward_simple_utf8(TermInput *input, TermKeyKey *key)
&& 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++;
+ }
+ while (*ptr) {
+ if (*ptr == '<') {
+ len += (size_t)snprintf(buf + len, sizeof(buf) - len, "<lt>");
+ } else {
+ buf[len++] = *ptr;
}
+ ptr++;
}
tinput_enqueue(input, buf, len);
@@ -359,21 +309,20 @@ static void forward_modified_utf8(TermInput *input, TermKeyKey *key)
(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;
- }
+ }
+ // 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;
}
}
@@ -398,8 +347,16 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key)
button = last_pressed_button;
}
- if (button == 0 || (ev != TERMKEY_MOUSE_PRESS && ev != TERMKEY_MOUSE_DRAG
- && ev != TERMKEY_MOUSE_RELEASE)) {
+ if (ev == TERMKEY_MOUSE_UNKNOWN && !(key->code.mouse[0] & 0x20)) {
+ int code = key->code.mouse[0] & ~0x3c;
+ if (code == 66 || code == 67) {
+ ev = TERMKEY_MOUSE_PRESS;
+ button = code - 60;
+ }
+ }
+
+ if ((button == 0 && ev != TERMKEY_MOUSE_RELEASE)
+ || (ev != TERMKEY_MOUSE_PRESS && ev != TERMKEY_MOUSE_DRAG && ev != TERMKEY_MOUSE_RELEASE)) {
return;
}
@@ -431,8 +388,11 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key)
if (button == 4) {
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "ScrollWheelUp");
} else if (button == 5) {
- len += (size_t)snprintf(buf + len, sizeof(buf) - len,
- "ScrollWheelDown");
+ len += (size_t)snprintf(buf + len, sizeof(buf) - len, "ScrollWheelDown");
+ } else if (button == 6) {
+ len += (size_t)snprintf(buf + len, sizeof(buf) - len, "ScrollWheelLeft");
+ } else if (button == 7) {
+ len += (size_t)snprintf(buf + len, sizeof(buf) - len, "ScrollWheelRight");
} else {
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Mouse");
last_pressed_button = button;
@@ -442,7 +402,8 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key)
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Drag");
break;
case TERMKEY_MOUSE_RELEASE:
- len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Release");
+ len += (size_t)snprintf(buf + len, sizeof(buf) - len, button ? "Release" : "MouseMove");
+ last_pressed_button = 0;
break;
case TERMKEY_MOUSE_UNKNOWN:
abort();
@@ -552,7 +513,10 @@ 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);
- autocmd_schedule_focusgained(focus_gained);
+
+ MAXSIZE_TEMP_ARRAY(args, 1);
+ ADD_C(args, BOOLEAN_OBJ(focus_gained));
+ rpc_send_event(ui_client_channel_id, "nvim_ui_set_focus", args);
return true;
}
return false;
@@ -599,10 +563,14 @@ static HandleState handle_bracketed_paste(TermInput *input)
return kNotApplicable;
}
-static void set_bg_deferred(void **argv)
+static void set_bg(char *bgvalue)
{
- char *bgvalue = argv[0];
- set_tty_background(bgvalue);
+ if (ui_client_attached) {
+ MAXSIZE_TEMP_ARRAY(args, 2);
+ ADD_C(args, STRING_OBJ(cstr_as_string("term_background")));
+ ADD_C(args, STRING_OBJ(cstr_as_string(bgvalue)));
+ rpc_send_event(ui_client_channel_id, "nvim_ui_set_option", args);
+ }
}
// During startup, tui.c requests the background color (see `ext.get_bg`).
@@ -684,10 +652,11 @@ static HandleState handle_background_color(TermInput *input)
double g = (double)rgb[1] / (double)rgb_max[1];
double b = (double)rgb[2] / (double)rgb_max[2];
double luminance = (0.299 * r) + (0.587 * g) + (0.114 * b); // CCIR 601
- char *bgvalue = luminance < 0.5 ? "dark" : "light";
+ bool is_dark = luminance < 0.5;
+ char *bgvalue = is_dark ? "dark" : "light";
DLOG("bg response: %s", bgvalue);
- loop_schedule_deferred(&main_loop,
- event_create(set_bg_deferred, 1, bgvalue));
+ ui_client_bg_response = is_dark ? kTrue : kFalse;
+ set_bg(bgvalue);
input->waiting_for_bg_response = 0;
} else if (!done && !bad) {
// An incomplete sequence was found, waiting for the next input.
diff --git a/src/nvim/tui/input.h b/src/nvim/tui/input.h
index 51df57938c..5df108b107 100644
--- a/src/nvim/tui/input.h
+++ b/src/nvim/tui/input.h
@@ -2,10 +2,14 @@
#define NVIM_TUI_INPUT_H
#include <stdbool.h>
+#include <stdint.h>
#include <termkey.h>
+#include <uv.h>
+#include "nvim/event/loop.h"
#include "nvim/event/stream.h"
#include "nvim/event/time.h"
+#include "nvim/rbuffer.h"
#include "nvim/tui/input_defs.h"
#include "nvim/tui/tui.h"
@@ -26,9 +30,7 @@ typedef struct term_input {
ExtkeysType extkeys_type;
long ttimeoutlen;
TermKey *tk;
-#if TERMKEY_VERSION_MAJOR > 0 || TERMKEY_VERSION_MINOR > 18
TermKey_Terminfo_Getstr_Hook *tk_ti_hook_fn; ///< libtermkey terminfo hook
-#endif
TimeWatcher timer_handle;
Loop *loop;
Stream read_stream;
diff --git a/src/nvim/tui/terminfo.c b/src/nvim/tui/terminfo.c
index ce48059b94..d630a34ce2 100644
--- a/src/nvim/tui/terminfo.c
+++ b/src/nvim/tui/terminfo.c
@@ -7,15 +7,20 @@
#include <string.h>
#include <unibilium.h>
-#include "nvim/globals.h"
-#include "nvim/log.h"
+#include "klib/kvec.h"
+#include "nvim/api/private/defs.h"
+#include "nvim/api/private/helpers.h"
+#include "nvim/ascii.h"
+#include "nvim/charset.h"
#include "nvim/memory.h"
-#include "nvim/message.h"
-#include "nvim/option.h"
-#include "nvim/os/os.h"
+#include "nvim/strings.h"
#include "nvim/tui/terminfo.h"
#include "nvim/tui/terminfo_defs.h"
+#ifdef __FreeBSD__
+# include "nvim/os/os.h"
+#endif
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "tui/terminfo.c.generated.h"
#endif
@@ -145,82 +150,80 @@ unibi_term *terminfo_from_builtin(const char *term, char **termname)
/// Serves a similar purpose as Vim `:set termcap` (removed in Nvim).
///
/// @note adapted from unibilium unibi-dump.c
-void terminfo_info_msg(const unibi_term *const ut)
+/// @return allocated string
+String terminfo_info_msg(const unibi_term *ut, const char *termname)
{
- if (exiting) {
- return;
- }
- msg_puts_title("\n\n--- Terminal info --- {{{\n");
+ StringBuilder data = KV_INITIAL_VALUE;
- char *term;
- get_tty_option("term", &term);
- msg_printf_attr(0, "&term: %s\n", term);
- msg_printf_attr(0, "Description: %s\n", unibi_get_name(ut));
+ kv_printf(data, "&term: %s\n", termname);
+ kv_printf(data, "Description: %s\n", unibi_get_name(ut));
const char **a = unibi_get_aliases(ut);
if (*a) {
- msg_puts("Aliases: ");
+ kv_printf(data, "Aliases: ");
do {
- msg_printf_attr(0, "%s%s\n", *a, a[1] ? " | " : "");
+ kv_printf(data, "%s%s\n", *a, a[1] ? " | " : "");
a++;
} while (*a);
}
- msg_puts("Boolean capabilities:\n");
+ kv_printf(data, "Boolean capabilities:\n");
for (enum unibi_boolean i = unibi_boolean_begin_ + 1;
i < unibi_boolean_end_; i++) {
- msg_printf_attr(0, " %-25s %-10s = %s\n", unibi_name_bool(i),
- unibi_short_name_bool(i),
- unibi_get_bool(ut, i) ? "true" : "false");
+ kv_printf(data, " %-25s %-10s = %s\n", unibi_name_bool(i),
+ unibi_short_name_bool(i),
+ unibi_get_bool(ut, i) ? "true" : "false");
}
- msg_puts("Numeric capabilities:\n");
+ kv_printf(data, "Numeric capabilities:\n");
for (enum unibi_numeric i = unibi_numeric_begin_ + 1;
i < unibi_numeric_end_; i++) {
int n = unibi_get_num(ut, i); // -1 means "empty"
- msg_printf_attr(0, " %-25s %-10s = %d\n", unibi_name_num(i),
- unibi_short_name_num(i), n);
+ kv_printf(data, " %-25s %-10s = %d\n", unibi_name_num(i),
+ unibi_short_name_num(i), n);
}
- msg_puts("String capabilities:\n");
+ kv_printf(data, "String capabilities:\n");
for (enum unibi_string i = unibi_string_begin_ + 1;
i < unibi_string_end_; i++) {
const char *s = unibi_get_str(ut, i);
if (s) {
- msg_printf_attr(0, " %-25s %-10s = ", unibi_name_str(i),
- unibi_short_name_str(i));
+ kv_printf(data, " %-25s %-10s = ", unibi_name_str(i),
+ unibi_short_name_str(i));
// Most of these strings will contain escape sequences.
- msg_outtrans_special((char_u *)s, false, 0);
- msg_putchar('\n');
+ kv_transstr(&data, s, false);
+ kv_push(data, '\n');
}
}
if (unibi_count_ext_bool(ut)) {
- msg_puts("Extended boolean capabilities:\n");
+ kv_printf(data, "Extended boolean capabilities:\n");
for (size_t i = 0; i < unibi_count_ext_bool(ut); i++) {
- msg_printf_attr(0, " %-25s = %s\n",
- unibi_get_ext_bool_name(ut, i),
- unibi_get_ext_bool(ut, i) ? "true" : "false");
+ kv_printf(data, " %-25s = %s\n",
+ unibi_get_ext_bool_name(ut, i),
+ unibi_get_ext_bool(ut, i) ? "true" : "false");
}
}
if (unibi_count_ext_num(ut)) {
- msg_puts("Extended numeric capabilities:\n");
+ kv_printf(data, "Extended numeric capabilities:\n");
for (size_t i = 0; i < unibi_count_ext_num(ut); i++) {
- msg_printf_attr(0, " %-25s = %d\n",
- unibi_get_ext_num_name(ut, i),
- unibi_get_ext_num(ut, i));
+ kv_printf(data, " %-25s = %d\n",
+ unibi_get_ext_num_name(ut, i),
+ unibi_get_ext_num(ut, i));
}
}
if (unibi_count_ext_str(ut)) {
- msg_puts("Extended string capabilities:\n");
+ kv_printf(data, "Extended string capabilities:\n");
for (size_t i = 0; i < unibi_count_ext_str(ut); i++) {
- msg_printf_attr(0, " %-25s = ", unibi_get_ext_str_name(ut, i));
- msg_outtrans_special((char_u *)unibi_get_ext_str(ut, i), false, 0);
- msg_putchar('\n');
+ kv_printf(data, " %-25s = ", unibi_get_ext_str_name(ut, i));
+ // NOTE: unibi_get_ext_str(ut, i) might be NULL, as termcap
+ // might include junk data on mac os. kv_transstr will handle this.
+ kv_transstr(&data, unibi_get_ext_str(ut, i), false);
+ kv_push(data, '\n');
}
}
+ kv_push(data, NUL);
- msg_puts("}}}\n");
- xfree(term);
+ return cbuf_as_string(data.items, data.size - 1);
}
diff --git a/src/nvim/tui/terminfo.h b/src/nvim/tui/terminfo.h
index 099df8967f..178d384457 100644
--- a/src/nvim/tui/terminfo.h
+++ b/src/nvim/tui/terminfo.h
@@ -3,6 +3,8 @@
#include <unibilium.h>
+#include "nvim/api/private/defs.h"
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "tui/terminfo.h.generated.h"
#endif
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index 38e8c15762..a50e44f7a3 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -1,48 +1,48 @@
// 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
-// Terminal UI functions. Invoked (by ui_bridge.c) on the TUI thread.
+// Terminal UI functions. Invoked (by ui_client.c) on the UI process.
#include <assert.h>
-#include <limits.h>
+#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <unibilium.h>
#include <uv.h>
-#if defined(HAVE_TERMIOS_H)
-# include <termios.h>
-#endif
+#include "auto/config.h"
+#include "klib/kvec.h"
+#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
-#include "nvim/api/vim.h"
#include "nvim/ascii.h"
+#include "nvim/cursor_shape.h"
+#include "nvim/event/defs.h"
#include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
#include "nvim/event/signal.h"
-#include "nvim/highlight.h"
-#include "nvim/lib/kvec.h"
+#include "nvim/event/stream.h"
+#include "nvim/globals.h"
+#include "nvim/grid_defs.h"
+#include "nvim/highlight_defs.h"
#include "nvim/log.h"
+#include "nvim/macros.h"
#include "nvim/main.h"
-#include "nvim/map.h"
+#include "nvim/mbyte.h"
#include "nvim/memory.h"
-#include "nvim/option.h"
+#include "nvim/msgpack_rpc/channel.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
-#include "nvim/os/signal.h"
-#include "nvim/os/tty.h"
-#include "nvim/ui.h"
-#include "nvim/vim.h"
-#ifdef WIN32
+#include "nvim/ui_client.h"
+#ifdef MSWIN
# include "nvim/os/os_win_console.h"
#endif
-#include "nvim/cursor_shape.h"
-#include "nvim/macros.h"
-#include "nvim/strings.h"
-#include "nvim/syntax.h"
#include "nvim/tui/input.h"
#include "nvim/tui/terminfo.h"
#include "nvim/tui/tui.h"
#include "nvim/ugrid.h"
-#include "nvim/ui_bridge.h"
+#include "nvim/ui.h"
// Space reserved in two output buffers to make the cursor normal or invisible
// when flushing. No existing terminal will require 32 bytes to do that.
@@ -63,8 +63,21 @@
do { \
(var) = unibi_var_from_num((num)); \
} while (0)
+# define UNIBI_SET_STR_VAR(var, str) \
+ do { \
+ (var) = unibi_var_from_str((str)); \
+ } while (0)
#else
-# define UNIBI_SET_NUM_VAR(var, num) (var).i = (num);
+# define UNIBI_SET_NUM_VAR(var, num) \
+ do { \
+ (var).p = NULL; \
+ (var).i = (num); \
+ } while (0)
+# define UNIBI_SET_STR_VAR(var, str) \
+ do { \
+ (var).i = INT_MIN; \
+ (var).p = str; \
+ } while (0)
#endif
typedef struct {
@@ -72,7 +85,6 @@ typedef struct {
} Rect;
struct TUIData {
- UIBridgeData *bridge;
Loop *loop;
unibi_var_t params[9];
char buf[OUTBUF_SIZE];
@@ -83,13 +95,14 @@ struct TUIData {
TermInput input;
uv_loop_t write_loop;
unibi_term *ut;
+ char *term; // value of $TERM
union {
uv_tty_t tty;
uv_pipe_t pipe;
} output_handle;
bool out_isatty;
- SignalWatcher winch_handle, cont_handle;
- bool cont_received;
+ SignalWatcher winch_handle;
+ uv_timer_t startup_delay_timer;
UGrid grid;
kvec_t(Rect) invalid_regions;
int row, col;
@@ -103,8 +116,10 @@ struct TUIData {
bool immediate_wrap_after_last_column;
bool bce;
bool mouse_enabled;
+ bool mouse_move_enabled;
bool busy, is_invisible, want_invisible;
bool cork, overflow;
+ bool set_cursor_color_as_str;
bool cursor_color_changed;
bool is_starting;
FILE *screenshot;
@@ -115,11 +130,14 @@ struct TUIData {
bool default_attr;
bool can_clear_attr;
ModeShape showing_mode;
+ Integer verbose;
struct {
int enable_mouse, disable_mouse;
+ int enable_mouse_move, disable_mouse_move;
int enable_bracketed_paste, disable_bracketed_paste;
int enable_lr_margin, disable_lr_margin;
int enter_strikethrough_mode;
+ int enter_altfont_mode;
int set_rgb_foreground, set_rgb_background;
int set_cursor_color;
int reset_cursor_color;
@@ -135,153 +153,143 @@ struct TUIData {
int get_extkeys;
} unibi_ext;
char *space_buf;
+ bool stopped;
+ int width;
+ int height;
+ bool rgb;
};
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().
- ui->stop = tui_stop;
- ui->grid_resize = tui_grid_resize;
- ui->grid_clear = tui_grid_clear;
- ui->grid_cursor_goto = tui_grid_cursor_goto;
- ui->mode_info_set = tui_mode_info_set;
- ui->update_menu = tui_update_menu;
- ui->busy_start = tui_busy_start;
- ui->busy_stop = tui_busy_stop;
- ui->mouse_on = tui_mouse_on;
- ui->mouse_off = tui_mouse_off;
- ui->mode_change = tui_mode_change;
- ui->grid_scroll = tui_grid_scroll;
- ui->hl_attr_define = tui_hl_attr_define;
- ui->bell = tui_bell;
- ui->visual_bell = tui_visual_bell;
- ui->default_colors_set = tui_default_colors_set;
- ui->flush = tui_flush;
- ui->suspend = tui_suspend;
- ui->set_title = tui_set_title;
- ui->set_icon = tui_set_icon;
- ui->screenshot = tui_screenshot;
- ui->option_set = tui_option_set;
- ui->raw_line = tui_raw_line;
-
- CLEAR_FIELD(ui->ui_ext);
- ui->ui_ext[kUILinegrid] = true;
- ui->ui_ext[kUITermColors] = true;
-
- 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;
+TUIData *tui_start(int *width, int *height, char **term)
+{
+ TUIData *tui = xcalloc(1, sizeof(TUIData));
+ tui->is_starting = true;
+ tui->screenshot = NULL;
+ tui->stopped = false;
+ tui->loop = &main_loop;
+ kv_init(tui->invalid_regions);
+ signal_watcher_init(tui->loop, &tui->winch_handle, tui);
+
+ // TODO(bfredl): zero hl is empty, send this explicitly?
+ kv_push(tui->attrs, HLATTRS_INIT);
+
+ tui->input.tk_ti_hook_fn = tui_tk_ti_getstr;
+ tinput_init(&tui->input, &main_loop);
+ ugrid_init(&tui->grid);
+ tui_terminal_start(tui);
+
+ uv_timer_init(&tui->loop->uv, &tui->startup_delay_timer);
+ tui->startup_delay_timer.data = tui;
+ uv_timer_start(&tui->startup_delay_timer, after_startup_cb,
+ 100, 0);
+
+ loop_poll_events(&main_loop, 1);
+ *width = tui->width;
+ *height = tui->height;
+ *term = tui->term;
+ return tui;
+}
+
+void tui_enable_extkeys(TUIData *tui)
+{
+ TermInput input = tui->input;
+ unibi_term *ut = tui->ut;
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");
+ tui->unibi_ext.enable_extended_keys = (int)unibi_add_ext_str(ut, "ext.enable_extended_keys",
+ "\x1b[>1u");
+ tui->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");
+ tui->unibi_ext.enable_extended_keys = (int)unibi_add_ext_str(ut, "ext.enable_extended_keys",
+ "\x1b[>4;2m");
+ tui->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);
+ unibi_out_ext(tui, tui->unibi_ext.enable_extended_keys);
}
-static size_t unibi_pre_fmt_str(TUIData *data, unsigned int unibi_index, char *buf, size_t len)
+static size_t unibi_pre_fmt_str(TUIData *tui, unsigned int unibi_index, char *buf, size_t len)
{
- const char *str = unibi_get_str(data->ut, unibi_index);
+ const char *str = unibi_get_str(tui->ut, unibi_index);
if (!str) {
return 0U;
}
- return unibi_run(str, data->params, buf, len);
-}
-
-static void termname_set_event(void **argv)
-{
- char *termname = argv[0];
- set_tty_option("term", termname);
- // Do not free termname, it is freed by set_tty_option.
-}
-
-static void terminfo_start(UI *ui)
-{
- TUIData *data = ui->data;
- data->scroll_region_is_full_screen = true;
- data->bufpos = 0;
- data->default_attr = false;
- data->can_clear_attr = false;
- data->is_invisible = true;
- data->want_invisible = false;
- data->busy = false;
- data->cork = false;
- data->overflow = false;
- data->cursor_color_changed = false;
- data->showing_mode = SHAPE_IDX_N;
- data->unibi_ext.enable_mouse = -1;
- data->unibi_ext.disable_mouse = -1;
- data->unibi_ext.set_cursor_color = -1;
- data->unibi_ext.reset_cursor_color = -1;
- data->unibi_ext.enable_bracketed_paste = -1;
- data->unibi_ext.disable_bracketed_paste = -1;
- data->unibi_ext.enter_strikethrough_mode = -1;
- data->unibi_ext.enable_lr_margin = -1;
- data->unibi_ext.disable_lr_margin = -1;
- data->unibi_ext.enable_focus_reporting = -1;
- data->unibi_ext.disable_focus_reporting = -1;
- data->unibi_ext.resize_screen = -1;
- data->unibi_ext.reset_scroll_region = -1;
- data->unibi_ext.set_cursor_style = -1;
- 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;
+ return unibi_run(str, tui->params, buf, len);
+}
+
+static void terminfo_start(TUIData *tui)
+{
+ tui->scroll_region_is_full_screen = true;
+ tui->bufpos = 0;
+ tui->default_attr = false;
+ tui->can_clear_attr = false;
+ tui->is_invisible = true;
+ tui->want_invisible = false;
+ tui->busy = false;
+ tui->cork = false;
+ tui->overflow = false;
+ tui->set_cursor_color_as_str = false;
+ tui->cursor_color_changed = false;
+ tui->showing_mode = SHAPE_IDX_N;
+ tui->unibi_ext.enable_mouse = -1;
+ tui->unibi_ext.disable_mouse = -1;
+ tui->unibi_ext.enable_mouse_move = -1;
+ tui->unibi_ext.disable_mouse_move = -1;
+ tui->unibi_ext.set_cursor_color = -1;
+ tui->unibi_ext.reset_cursor_color = -1;
+ tui->unibi_ext.enable_bracketed_paste = -1;
+ tui->unibi_ext.disable_bracketed_paste = -1;
+ tui->unibi_ext.enter_strikethrough_mode = -1;
+ tui->unibi_ext.enter_altfont_mode = -1;
+ tui->unibi_ext.enable_lr_margin = -1;
+ tui->unibi_ext.disable_lr_margin = -1;
+ tui->unibi_ext.enable_focus_reporting = -1;
+ tui->unibi_ext.disable_focus_reporting = -1;
+ tui->unibi_ext.resize_screen = -1;
+ tui->unibi_ext.reset_scroll_region = -1;
+ tui->unibi_ext.set_cursor_style = -1;
+ tui->unibi_ext.reset_cursor_style = -1;
+ tui->unibi_ext.get_bg = -1;
+ tui->unibi_ext.set_underline_color = -1;
+ tui->unibi_ext.enable_extended_keys = -1;
+ tui->unibi_ext.disable_extended_keys = -1;
+ tui->unibi_ext.get_extkeys = -1;
+ tui->out_fd = STDOUT_FILENO;
+ tui->out_isatty = os_isatty(tui->out_fd);
+ tui->input.tui_data = tui;
const char *term = os_getenv("TERM");
-#ifdef WIN32
- os_tty_guess_term(&term, data->out_fd);
+#ifdef MSWIN
+ os_tty_guess_term(&term, tui->out_fd);
os_setenv("TERM", term, 1);
// Old os_getenv() pointer is invalid after os_setenv(), fetch it again.
term = os_getenv("TERM");
#endif
// Set up unibilium/terminfo.
- char *termname = NULL;
if (term) {
- os_env_var_lock();
- data->ut = unibi_from_term(term);
- os_env_var_unlock();
- if (data->ut) {
- termname = xstrdup(term);
+ tui->ut = unibi_from_term(term);
+ if (tui->ut) {
+ if (!tui->term) {
+ tui->term = xstrdup(term);
+ }
}
}
- if (!data->ut) {
- data->ut = terminfo_from_builtin(term, &termname);
+ if (!tui->ut) {
+ tui->ut = terminfo_from_builtin(term, &tui->term);
}
- // Update 'term' option.
- loop_schedule_deferred(&main_loop,
- event_create(termname_set_event, 1, termname));
// None of the following work over SSH; see :help TERM .
const char *colorterm = os_getenv("COLORTERM");
@@ -298,59 +306,59 @@ static void terminfo_start(UI *ui)
long konsolev = konsolev_env ? strtol(konsolev_env, NULL, 10)
: (konsole ? 1 : 0);
- patch_terminfo_bugs(data, term, colorterm, vtev, konsolev, iterm_env, nsterm);
- augment_terminfo(data, term, vtev, konsolev, iterm_env, nsterm);
- data->can_change_scroll_region =
- !!unibi_get_str(data->ut, unibi_change_scroll_region);
- data->can_set_lr_margin =
- !!unibi_get_str(data->ut, unibi_set_lr_margin);
- data->can_set_left_right_margin =
- !!unibi_get_str(data->ut, unibi_set_left_margin_parm)
- && !!unibi_get_str(data->ut, unibi_set_right_margin_parm);
- data->can_scroll =
- !!unibi_get_str(data->ut, unibi_delete_line)
- && !!unibi_get_str(data->ut, unibi_parm_delete_line)
- && !!unibi_get_str(data->ut, unibi_insert_line)
- && !!unibi_get_str(data->ut, unibi_parm_insert_line);
- data->can_erase_chars = !!unibi_get_str(data->ut, unibi_erase_chars);
- data->immediate_wrap_after_last_column =
+ patch_terminfo_bugs(tui, term, colorterm, vtev, konsolev, iterm_env, nsterm);
+ augment_terminfo(tui, term, vtev, konsolev, iterm_env, nsterm);
+ tui->can_change_scroll_region =
+ !!unibi_get_str(tui->ut, unibi_change_scroll_region);
+ tui->can_set_lr_margin =
+ !!unibi_get_str(tui->ut, unibi_set_lr_margin);
+ tui->can_set_left_right_margin =
+ !!unibi_get_str(tui->ut, unibi_set_left_margin_parm)
+ && !!unibi_get_str(tui->ut, unibi_set_right_margin_parm);
+ tui->can_scroll =
+ !!unibi_get_str(tui->ut, unibi_delete_line)
+ && !!unibi_get_str(tui->ut, unibi_parm_delete_line)
+ && !!unibi_get_str(tui->ut, unibi_insert_line)
+ && !!unibi_get_str(tui->ut, unibi_parm_insert_line);
+ tui->can_erase_chars = !!unibi_get_str(tui->ut, unibi_erase_chars);
+ tui->immediate_wrap_after_last_column =
terminfo_is_term_family(term, "conemu")
|| terminfo_is_term_family(term, "cygwin")
|| terminfo_is_term_family(term, "win32con")
|| terminfo_is_term_family(term, "interix");
- data->bce = unibi_get_bool(data->ut, unibi_back_color_erase);
- data->normlen = unibi_pre_fmt_str(data, unibi_cursor_normal,
- data->norm, sizeof data->norm);
- data->invislen = unibi_pre_fmt_str(data, unibi_cursor_invisible,
- data->invis, sizeof data->invis);
+ tui->bce = unibi_get_bool(tui->ut, unibi_back_color_erase);
+ tui->normlen = unibi_pre_fmt_str(tui, unibi_cursor_normal,
+ tui->norm, sizeof tui->norm);
+ tui->invislen = unibi_pre_fmt_str(tui, unibi_cursor_invisible,
+ tui->invis, sizeof tui->invis);
// Set 't_Co' from the result of unibilium & fix_terminfo.
- t_colors = unibi_get_num(data->ut, unibi_max_colors);
+ t_colors = unibi_get_num(tui->ut, unibi_max_colors);
// Enter alternate screen, save title, and clear.
// NOTE: Do this *before* changing terminal settings. #6433
- unibi_out(ui, unibi_enter_ca_mode);
+ unibi_out(tui, unibi_enter_ca_mode);
// Save title/icon to the "stack". #4063
- unibi_out_ext(ui, data->unibi_ext.save_title);
- unibi_out(ui, unibi_keypad_xmit);
- unibi_out(ui, unibi_clear_screen);
+ unibi_out_ext(tui, tui->unibi_ext.save_title);
+ unibi_out(tui, unibi_keypad_xmit);
+ unibi_out(tui, unibi_clear_screen);
// Ask the terminal to send us the background color.
- data->input.waiting_for_bg_response = 5;
- unibi_out_ext(ui, data->unibi_ext.get_bg);
+ tui->input.waiting_for_bg_response = 5;
+ unibi_out_ext(tui, tui->unibi_ext.get_bg);
// Enable bracketed paste
- unibi_out_ext(ui, data->unibi_ext.enable_bracketed_paste);
+ unibi_out_ext(tui, tui->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);
+ tui->input.waiting_for_csiu_response = 5;
+ unibi_out_ext(tui, tui->unibi_ext.get_extkeys);
int ret;
- uv_loop_init(&data->write_loop);
- if (data->out_isatty) {
- ret = uv_tty_init(&data->write_loop, &data->output_handle.tty, data->out_fd, 0);
+ uv_loop_init(&tui->write_loop);
+ if (tui->out_isatty) {
+ ret = uv_tty_init(&tui->write_loop, &tui->output_handle.tty, tui->out_fd, 0);
if (ret) {
ELOG("uv_tty_init failed: %s", uv_strerror(ret));
}
-#ifdef WIN32
- ret = uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_RAW);
+#ifdef MSWIN
+ ret = uv_tty_set_mode(&tui->output_handle.tty, UV_TTY_MODE_RAW);
if (ret) {
ELOG("uv_tty_set_mode failed: %s", uv_strerror(ret));
}
@@ -358,7 +366,7 @@ static void terminfo_start(UI *ui)
int retry_count = 10;
// A signal may cause uv_tty_set_mode() to fail (e.g., SIGCONT). Retry a
// few times. #12322
- while ((ret = uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_IO)) == UV_EINTR
+ while ((ret = uv_tty_set_mode(&tui->output_handle.tty, UV_TTY_MODE_IO)) == UV_EINTR
&& retry_count > 0) {
retry_count--;
}
@@ -367,193 +375,135 @@ static void terminfo_start(UI *ui)
}
#endif
} else {
- ret = uv_pipe_init(&data->write_loop, &data->output_handle.pipe, 0);
+ ret = uv_pipe_init(&tui->write_loop, &tui->output_handle.pipe, 0);
if (ret) {
ELOG("uv_pipe_init failed: %s", uv_strerror(ret));
}
- ret = uv_pipe_open(&data->output_handle.pipe, data->out_fd);
+ ret = uv_pipe_open(&tui->output_handle.pipe, tui->out_fd);
if (ret) {
ELOG("uv_pipe_open failed: %s", uv_strerror(ret));
}
}
- flush_buf(ui);
+ flush_buf(tui);
}
-static void terminfo_stop(UI *ui)
+static void terminfo_stop(TUIData *tui)
{
- TUIData *data = ui->data;
// Destroy output stuff
- tui_mode_change(ui, (String)STRING_INIT, SHAPE_IDX_N);
- tui_mouse_off(ui);
- unibi_out(ui, unibi_exit_attribute_mode);
+ tui_mode_change(tui, (String)STRING_INIT, SHAPE_IDX_N);
+ tui_mouse_off(tui);
+ unibi_out(tui, unibi_exit_attribute_mode);
// Reset cursor to normal before exiting alternate screen.
- unibi_out(ui, unibi_cursor_normal);
- unibi_out(ui, unibi_keypad_local);
+ unibi_out(tui, unibi_cursor_normal);
+ unibi_out(tui, 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);
+ unibi_out_ext(tui, tui->unibi_ext.disable_extended_keys);
+ unibi_out(tui, unibi_exit_ca_mode);
// Restore title/icon from the "stack". #4063
- unibi_out_ext(ui, data->unibi_ext.restore_title);
- if (data->cursor_color_changed) {
- unibi_out_ext(ui, data->unibi_ext.reset_cursor_color);
+ unibi_out_ext(tui, tui->unibi_ext.restore_title);
+ if (tui->cursor_color_changed) {
+ unibi_out_ext(tui, tui->unibi_ext.reset_cursor_color);
}
// Disable bracketed paste
- unibi_out_ext(ui, data->unibi_ext.disable_bracketed_paste);
+ unibi_out_ext(tui, tui->unibi_ext.disable_bracketed_paste);
// Disable focus reporting
- unibi_out_ext(ui, data->unibi_ext.disable_focus_reporting);
- flush_buf(ui);
+ unibi_out_ext(tui, tui->unibi_ext.disable_focus_reporting);
+ flush_buf(tui);
uv_tty_reset_mode();
- uv_close((uv_handle_t *)&data->output_handle, NULL);
- uv_run(&data->write_loop, UV_RUN_DEFAULT);
- if (uv_loop_close(&data->write_loop)) {
+ uv_close((uv_handle_t *)&tui->output_handle, NULL);
+ uv_run(&tui->write_loop, UV_RUN_DEFAULT);
+ if (uv_loop_close(&tui->write_loop)) {
abort();
}
- unibi_destroy(data->ut);
+ unibi_destroy(tui->ut);
}
-static void tui_terminal_start(UI *ui)
+static void tui_terminal_start(TUIData *tui)
{
- TUIData *data = ui->data;
- data->print_attr_id = -1;
- ugrid_init(&data->grid);
- terminfo_start(ui);
- tui_guess_size(ui);
- signal_watcher_start(&data->winch_handle, sigwinch_cb, SIGWINCH);
- tinput_start(&data->input);
+ tui->print_attr_id = -1;
+ terminfo_start(tui);
+ tui_guess_size(tui);
+ signal_watcher_start(&tui->winch_handle, sigwinch_cb, SIGWINCH);
+ tinput_start(&tui->input);
}
-static void tui_terminal_after_startup(UI *ui)
- FUNC_ATTR_NONNULL_ALL
+static void after_startup_cb(uv_timer_t *handle)
{
- TUIData *data = ui->data;
+ TUIData *tui = handle->data;
+ tui_terminal_after_startup(tui);
+}
+static void tui_terminal_after_startup(TUIData *tui)
+ FUNC_ATTR_NONNULL_ALL
+{
// Emit this after Nvim startup, not during. This works around a tmux
// 2.3 bug(?) which caused slow drawing during startup. #7649
- unibi_out_ext(ui, data->unibi_ext.enable_focus_reporting);
- flush_buf(ui);
+ unibi_out_ext(tui, tui->unibi_ext.enable_focus_reporting);
+ flush_buf(tui);
}
-static void tui_terminal_stop(UI *ui)
+/// stop the terminal but allow it to restart later (like after suspend)
+static void tui_terminal_stop(TUIData *tui)
{
- TUIData *data = ui->data;
- if (uv_is_closing(STRUCT_CAST(uv_handle_t, &data->output_handle))) {
+ if (uv_is_closing(STRUCT_CAST(uv_handle_t, &tui->output_handle))) {
// Race between SIGCONT (tui.c) and SIGHUP (os/signal.c)? #8075
ELOG("TUI already stopped (race?)");
- ui->data = NULL; // Flag UI as "stopped".
+ tui->stopped = true;
return;
}
- tinput_stop(&data->input);
- signal_watcher_stop(&data->winch_handle);
- terminfo_stop(ui);
- ugrid_free(&data->grid);
+ tinput_stop(&tui->input);
+ signal_watcher_stop(&tui->winch_handle);
+ terminfo_stop(tui);
}
-static void tui_stop(UI *ui)
+void tui_stop(TUIData *tui)
{
- tui_terminal_stop(ui);
- ui->data = NULL; // Flag UI as "stopped".
+ tui_terminal_stop(tui);
+ stream_set_blocking(tui->input.in_fd, true); // normalize stream (#2598)
+ tinput_destroy(&tui->input);
+ tui->stopped = true;
+ signal_watcher_close(&tui->winch_handle, NULL);
+ uv_close((uv_handle_t *)&tui->startup_delay_timer, NULL);
}
/// Returns true if UI `ui` is stopped.
-static bool tui_is_stopped(UI *ui)
-{
- return ui->data == NULL;
-}
-
-/// Main function of the TUI thread.
-static void tui_main(UIBridgeData *bridge, UI *ui)
-{
- Loop tui_loop;
- loop_init(&tui_loop, NULL);
- TUIData *data = xcalloc(1, sizeof(TUIData));
- ui->data = data;
- data->bridge = bridge;
- data->loop = &tui_loop;
- data->is_starting = true;
- data->screenshot = NULL;
- kv_init(data->invalid_regions);
- signal_watcher_init(data->loop, &data->winch_handle, ui);
- signal_watcher_init(data->loop, &data->cont_handle, data);
-#ifdef UNIX
- signal_watcher_start(&data->cont_handle, sigcont_cb, SIGCONT);
-#endif
-
- // TODO(bfredl): zero hl is empty, send this explicitly?
- kv_push(data->attrs, HLATTRS_INIT);
-
-#if TERMKEY_VERSION_MAJOR > 0 || TERMKEY_VERSION_MINOR > 18
- data->input.tk_ti_hook_fn = tui_tk_ti_getstr;
-#endif
- tinput_init(&data->input, &tui_loop);
- tui_terminal_start(ui);
-
- // Allow main thread to continue, we are ready to handle UI callbacks.
- CONTINUE(bridge);
-
- loop_schedule_deferred(&main_loop,
- event_create(show_termcap_event, 1, data->ut));
-
- // "Active" loop: first ~100 ms of startup.
- for (size_t ms = 0; ms < 100 && !tui_is_stopped(ui);) {
- ms += (loop_poll_events(&tui_loop, 20) ? 20 : 1);
- }
- if (!tui_is_stopped(ui)) {
- tui_terminal_after_startup(ui);
- }
- // "Passive" (I/O-driven) loop: TUI thread "main loop".
- while (!tui_is_stopped(ui)) {
- loop_poll_events(&tui_loop, -1); // tui_loop.events is never processed
- }
-
- ui_bridge_stopped(bridge);
- tinput_destroy(&data->input);
- signal_watcher_stop(&data->cont_handle);
- signal_watcher_close(&data->cont_handle, NULL);
- signal_watcher_close(&data->winch_handle, NULL);
- loop_close(&tui_loop, false);
- kv_destroy(data->invalid_regions);
- kv_destroy(data->attrs);
- xfree(data->space_buf);
- xfree(data);
-}
-
-/// Handoff point between the main (ui_bridge) thread and the TUI thread.
-static void tui_scheduler(Event event, void *d)
+static bool tui_is_stopped(TUIData *tui)
{
- UI *ui = d;
- TUIData *data = ui->data;
- loop_schedule_fast(data->loop, event); // `tui_loop` local to tui_main().
+ return tui->stopped;
}
-#ifdef UNIX
-static void sigcont_cb(SignalWatcher *watcher, int signum, void *data)
+#ifdef EXITFREE
+void tui_free_all_mem(TUIData *tui)
{
- ((TUIData *)data)->cont_received = true;
+ ugrid_free(&tui->grid);
+ kv_destroy(tui->invalid_regions);
+ kv_destroy(tui->attrs);
+ xfree(tui->space_buf);
+ xfree(tui->term);
+ xfree(tui);
}
#endif
-static void sigwinch_cb(SignalWatcher *watcher, int signum, void *data)
+static void sigwinch_cb(SignalWatcher *watcher, int signum, void *cbdata)
{
got_winch++;
- UI *ui = data;
- if (tui_is_stopped(ui)) {
+ TUIData *tui = cbdata;
+ if (tui_is_stopped(tui)) {
return;
}
- tui_guess_size(ui);
- ui_schedule_refresh();
+ tui_guess_size(tui);
}
-static bool attrs_differ(UI *ui, int id1, int id2, bool rgb)
+static bool attrs_differ(TUIData *tui, int id1, int id2, bool rgb)
{
- TUIData *data = ui->data;
if (id1 == id2) {
return false;
} else if (id1 < 0 || id2 < 0) {
return true;
}
- HlAttrs a1 = kv_A(data->attrs, (size_t)id1);
- HlAttrs a2 = kv_A(data->attrs, (size_t)id2);
+ HlAttrs a1 = kv_A(tui->attrs, (size_t)id1);
+ HlAttrs a2 = kv_A(tui->attrs, (size_t)id2);
if (rgb) {
return a1.rgb_fg_color != a2.rgb_fg_color
@@ -564,42 +514,42 @@ 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_ANY_UNDERLINE
+ || (a1.cterm_ae_attr & HL_UNDERLINE_MASK
&& a1.rgb_sp_color != a2.rgb_sp_color);
}
}
-static void update_attrs(UI *ui, int attr_id)
+static void update_attrs(TUIData *tui, int attr_id)
{
- TUIData *data = ui->data;
-
- if (!attrs_differ(ui, attr_id, data->print_attr_id, ui->rgb)) {
- data->print_attr_id = attr_id;
+ if (!attrs_differ(tui, attr_id, tui->print_attr_id, tui->rgb)) {
+ tui->print_attr_id = attr_id;
return;
}
- data->print_attr_id = attr_id;
- HlAttrs attrs = kv_A(data->attrs, (size_t)attr_id);
- int attr = ui->rgb ? attrs.rgb_ae_attr : attrs.cterm_ae_attr;
+ tui->print_attr_id = attr_id;
+ HlAttrs attrs = kv_A(tui->attrs, (size_t)attr_id);
+ int attr = tui->rgb ? attrs.rgb_ae_attr : attrs.cterm_ae_attr;
bool bold = attr & HL_BOLD;
bool italic = attr & HL_ITALIC;
bool reverse = attr & HL_INVERSE;
bool standout = attr & HL_STANDOUT;
bool strikethrough = attr & HL_STRIKETHROUGH;
+ bool altfont = attr & HL_ALTFONT;
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;
+ if (tui->unibi_ext.set_underline_style != -1) {
+ int ul = attr & HL_UNDERLINE_MASK;
+ underline = ul == HL_UNDERLINE;
+ undercurl = ul == HL_UNDERCURL;
+ underdouble = ul == HL_UNDERDOUBLE;
+ underdashed = ul == HL_UNDERDASHED;
+ underdotted = ul == HL_UNDERDOTTED;
} else {
- underline = attr & HL_ANY_UNDERLINE;
+ underline = attr & HL_UNDERLINE_MASK;
undercurl = false;
underdouble = false;
underdotted = false;
@@ -609,126 +559,128 @@ static void update_attrs(UI *ui, int attr_id)
bool has_any_underline = undercurl || underline
|| underdouble || underdotted || underdashed;
- if (unibi_get_str(data->ut, unibi_set_attributes)) {
+ if (unibi_get_str(tui->ut, unibi_set_attributes)) {
if (bold || reverse || underline || standout) {
- UNIBI_SET_NUM_VAR(data->params[0], standout);
- UNIBI_SET_NUM_VAR(data->params[1], underline);
- UNIBI_SET_NUM_VAR(data->params[2], reverse);
- UNIBI_SET_NUM_VAR(data->params[3], 0); // blink
- UNIBI_SET_NUM_VAR(data->params[4], 0); // dim
- UNIBI_SET_NUM_VAR(data->params[5], bold);
- UNIBI_SET_NUM_VAR(data->params[6], 0); // blank
- UNIBI_SET_NUM_VAR(data->params[7], 0); // protect
- UNIBI_SET_NUM_VAR(data->params[8], 0); // alternate character set
- unibi_out(ui, unibi_set_attributes);
- } else if (!data->default_attr) {
- unibi_out(ui, unibi_exit_attribute_mode);
+ UNIBI_SET_NUM_VAR(tui->params[0], standout);
+ UNIBI_SET_NUM_VAR(tui->params[1], underline);
+ UNIBI_SET_NUM_VAR(tui->params[2], reverse);
+ UNIBI_SET_NUM_VAR(tui->params[3], 0); // blink
+ UNIBI_SET_NUM_VAR(tui->params[4], 0); // dim
+ UNIBI_SET_NUM_VAR(tui->params[5], bold);
+ UNIBI_SET_NUM_VAR(tui->params[6], 0); // blank
+ UNIBI_SET_NUM_VAR(tui->params[7], 0); // protect
+ UNIBI_SET_NUM_VAR(tui->params[8], 0); // alternate character set
+ unibi_out(tui, unibi_set_attributes);
+ } else if (!tui->default_attr) {
+ unibi_out(tui, unibi_exit_attribute_mode);
}
} else {
- if (!data->default_attr) {
- unibi_out(ui, unibi_exit_attribute_mode);
+ if (!tui->default_attr) {
+ unibi_out(tui, unibi_exit_attribute_mode);
}
if (bold) {
- unibi_out(ui, unibi_enter_bold_mode);
+ unibi_out(tui, unibi_enter_bold_mode);
}
if (underline) {
- unibi_out(ui, unibi_enter_underline_mode);
+ unibi_out(tui, unibi_enter_underline_mode);
}
if (standout) {
- unibi_out(ui, unibi_enter_standout_mode);
+ unibi_out(tui, unibi_enter_standout_mode);
}
if (reverse) {
- unibi_out(ui, unibi_enter_reverse_mode);
+ unibi_out(tui, unibi_enter_reverse_mode);
}
}
if (italic) {
- unibi_out(ui, unibi_enter_italics_mode);
+ unibi_out(tui, unibi_enter_italics_mode);
}
- if (strikethrough && data->unibi_ext.enter_strikethrough_mode != -1) {
- unibi_out_ext(ui, data->unibi_ext.enter_strikethrough_mode);
+ if (altfont && tui->unibi_ext.enter_altfont_mode != -1) {
+ unibi_out_ext(tui, tui->unibi_ext.enter_altfont_mode);
}
- if (undercurl && data->unibi_ext.set_underline_style != -1) {
- UNIBI_SET_NUM_VAR(data->params[0], 3);
- unibi_out_ext(ui, data->unibi_ext.set_underline_style);
+ if (strikethrough && tui->unibi_ext.enter_strikethrough_mode != -1) {
+ unibi_out_ext(tui, tui->unibi_ext.enter_strikethrough_mode);
}
- 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 (undercurl && tui->unibi_ext.set_underline_style != -1) {
+ UNIBI_SET_NUM_VAR(tui->params[0], 3);
+ unibi_out_ext(tui, tui->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 (underdouble && tui->unibi_ext.set_underline_style != -1) {
+ UNIBI_SET_NUM_VAR(tui->params[0], 2);
+ unibi_out_ext(tui, tui->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 (underdotted && tui->unibi_ext.set_underline_style != -1) {
+ UNIBI_SET_NUM_VAR(tui->params[0], 4);
+ unibi_out_ext(tui, tui->unibi_ext.set_underline_style);
+ }
+ if (underdashed && tui->unibi_ext.set_underline_style != -1) {
+ UNIBI_SET_NUM_VAR(tui->params[0], 5);
+ unibi_out_ext(tui, tui->unibi_ext.set_underline_style);
}
- if (has_any_underline && data->unibi_ext.set_underline_color != -1) {
+ if (has_any_underline && tui->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
- UNIBI_SET_NUM_VAR(data->params[1], (color >> 8) & 0xff); // green
- UNIBI_SET_NUM_VAR(data->params[2], color & 0xff); // blue
- unibi_out_ext(ui, data->unibi_ext.set_underline_color);
+ UNIBI_SET_NUM_VAR(tui->params[0], (color >> 16) & 0xff); // red
+ UNIBI_SET_NUM_VAR(tui->params[1], (color >> 8) & 0xff); // green
+ UNIBI_SET_NUM_VAR(tui->params[2], color & 0xff); // blue
+ unibi_out_ext(tui, tui->unibi_ext.set_underline_color);
}
}
int fg, bg;
- if (ui->rgb && !(attr & HL_FG_INDEXED)) {
+ if (tui->rgb && !(attr & HL_FG_INDEXED)) {
fg = ((attrs.rgb_fg_color != -1)
- ? attrs.rgb_fg_color : data->clear_attrs.rgb_fg_color);
+ ? attrs.rgb_fg_color : tui->clear_attrs.rgb_fg_color);
if (fg != -1) {
- UNIBI_SET_NUM_VAR(data->params[0], (fg >> 16) & 0xff); // red
- UNIBI_SET_NUM_VAR(data->params[1], (fg >> 8) & 0xff); // green
- UNIBI_SET_NUM_VAR(data->params[2], fg & 0xff); // blue
- unibi_out_ext(ui, data->unibi_ext.set_rgb_foreground);
+ UNIBI_SET_NUM_VAR(tui->params[0], (fg >> 16) & 0xff); // red
+ UNIBI_SET_NUM_VAR(tui->params[1], (fg >> 8) & 0xff); // green
+ UNIBI_SET_NUM_VAR(tui->params[2], fg & 0xff); // blue
+ unibi_out_ext(tui, tui->unibi_ext.set_rgb_foreground);
}
} else {
fg = (attrs.cterm_fg_color
- ? attrs.cterm_fg_color - 1 : (data->clear_attrs.cterm_fg_color - 1));
+ ? attrs.cterm_fg_color - 1 : (tui->clear_attrs.cterm_fg_color - 1));
if (fg != -1) {
- UNIBI_SET_NUM_VAR(data->params[0], fg);
- unibi_out(ui, unibi_set_a_foreground);
+ UNIBI_SET_NUM_VAR(tui->params[0], fg);
+ unibi_out(tui, unibi_set_a_foreground);
}
}
- if (ui->rgb && !(attr & HL_BG_INDEXED)) {
+ if (tui->rgb && !(attr & HL_BG_INDEXED)) {
bg = ((attrs.rgb_bg_color != -1)
- ? attrs.rgb_bg_color : data->clear_attrs.rgb_bg_color);
+ ? attrs.rgb_bg_color : tui->clear_attrs.rgb_bg_color);
if (bg != -1) {
- UNIBI_SET_NUM_VAR(data->params[0], (bg >> 16) & 0xff); // red
- UNIBI_SET_NUM_VAR(data->params[1], (bg >> 8) & 0xff); // green
- UNIBI_SET_NUM_VAR(data->params[2], bg & 0xff); // blue
- unibi_out_ext(ui, data->unibi_ext.set_rgb_background);
+ UNIBI_SET_NUM_VAR(tui->params[0], (bg >> 16) & 0xff); // red
+ UNIBI_SET_NUM_VAR(tui->params[1], (bg >> 8) & 0xff); // green
+ UNIBI_SET_NUM_VAR(tui->params[2], bg & 0xff); // blue
+ unibi_out_ext(tui, tui->unibi_ext.set_rgb_background);
}
} else {
bg = (attrs.cterm_bg_color
- ? attrs.cterm_bg_color - 1 : (data->clear_attrs.cterm_bg_color - 1));
+ ? attrs.cterm_bg_color - 1 : (tui->clear_attrs.cterm_bg_color - 1));
if (bg != -1) {
- UNIBI_SET_NUM_VAR(data->params[0], bg);
- unibi_out(ui, unibi_set_a_background);
+ UNIBI_SET_NUM_VAR(tui->params[0], bg);
+ unibi_out(tui, unibi_set_a_background);
}
}
- data->default_attr = fg == -1 && bg == -1
- && !bold && !italic && !has_any_underline && !reverse && !standout
- && !strikethrough;
+ tui->default_attr = fg == -1 && bg == -1
+ && !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 && !has_any_underline
- && !strikethrough && (data->bce || bg == -1);
+ tui->can_clear_attr = !reverse && !standout && !has_any_underline
+ && !strikethrough && (tui->bce || bg == -1);
}
-static void final_column_wrap(UI *ui)
+static void final_column_wrap(TUIData *tui)
{
- TUIData *data = ui->data;
- UGrid *grid = &data->grid;
- if (grid->row != -1 && grid->col == ui->width) {
+ UGrid *grid = &tui->grid;
+ if (grid->row != -1 && grid->col == tui->width) {
grid->col = 0;
- if (grid->row < MIN(ui->height, grid->height - 1)) {
+ if (grid->row < MIN(tui->height, grid->height - 1)) {
grid->row++;
}
}
@@ -737,33 +689,31 @@ static void final_column_wrap(UI *ui)
/// It is undocumented, but in the majority of terminals and terminal emulators
/// printing at the right margin does not cause an automatic wrap until the
/// next character is printed, holding the cursor in place until then.
-static void print_cell(UI *ui, UCell *ptr)
+static void print_cell(TUIData *tui, UCell *ptr)
{
- TUIData *data = ui->data;
- UGrid *grid = &data->grid;
- if (!data->immediate_wrap_after_last_column) {
+ UGrid *grid = &tui->grid;
+ if (!tui->immediate_wrap_after_last_column) {
// Printing the next character finally advances the cursor.
- final_column_wrap(ui);
+ final_column_wrap(tui);
}
- update_attrs(ui, ptr->attr);
- out(ui, ptr->data, strlen(ptr->data));
+ update_attrs(tui, ptr->attr);
+ out(tui, ptr->data, strlen(ptr->data));
grid->col++;
- if (data->immediate_wrap_after_last_column) {
+ if (tui->immediate_wrap_after_last_column) {
// Printing at the right margin immediately advances the cursor.
- final_column_wrap(ui);
+ final_column_wrap(tui);
}
}
-static bool cheap_to_print(UI *ui, int row, int col, int next)
+static bool cheap_to_print(TUIData *tui, int row, int col, int next)
{
- TUIData *data = ui->data;
- UGrid *grid = &data->grid;
+ UGrid *grid = &tui->grid;
UCell *cell = grid->cells[row] + col;
while (next) {
next--;
- if (attrs_differ(ui, cell->attr,
- data->print_attr_id, ui->rgb)) {
- if (data->default_attr) {
+ if (attrs_differ(tui, cell->attr,
+ tui->print_attr_id, tui->rgb)) {
+ if (tui->default_attr) {
return false;
}
}
@@ -784,15 +734,14 @@ static bool cheap_to_print(UI *ui, int row, int col, int next)
/// (ASCII 0/12) means the same thing and does not mean home. VT, CVT, and
/// TAB also stop at software-defined tabulation stops, not at a fixed set
/// of row/column positions.
-static void cursor_goto(UI *ui, int row, int col)
+static void cursor_goto(TUIData *tui, int row, int col)
{
- TUIData *data = ui->data;
- UGrid *grid = &data->grid;
+ UGrid *grid = &tui->grid;
if (row == grid->row && col == grid->col) {
return;
}
if (0 == row && 0 == col) {
- unibi_out(ui, unibi_cursor_home);
+ unibi_out(tui, unibi_cursor_home);
ugrid_goto(grid, row, col);
return;
}
@@ -801,12 +750,12 @@ static void cursor_goto(UI *ui, int row, int col)
}
if (0 == col ? col != grid->col :
row != grid->row ? false :
- 1 == col ? 2 < grid->col && cheap_to_print(ui, grid->row, 0, col) :
- 2 == col ? 5 < grid->col && cheap_to_print(ui, grid->row, 0, col) :
+ 1 == col ? 2 < grid->col && cheap_to_print(tui, grid->row, 0, col) :
+ 2 == col ? 5 < grid->col && cheap_to_print(tui, grid->row, 0, col) :
false) {
// Motion to left margin from anywhere else, or CR + printing chars is
// even less expensive than using BSes or CUB.
- unibi_out(ui, unibi_carriage_return);
+ unibi_out(tui, unibi_carriage_return);
ugrid_goto(grid, grid->row, 0);
}
if (row == grid->row) {
@@ -816,15 +765,15 @@ static void cursor_goto(UI *ui, int row, int col)
// motion calculations have OBOEs that cannot be compensated for,
// because two terminals that claim to be the same will implement
// different cursor positioning rules.
- && (data->immediate_wrap_after_last_column || grid->col < ui->width)) {
+ && (tui->immediate_wrap_after_last_column || grid->col < tui->width)) {
int n = grid->col - col;
if (n <= 4) { // This might be just BS, so it is considered really cheap.
while (n--) {
- unibi_out(ui, unibi_cursor_left);
+ unibi_out(tui, unibi_cursor_left);
}
} else {
- UNIBI_SET_NUM_VAR(data->params[0], n);
- unibi_out(ui, unibi_parm_left_cursor);
+ UNIBI_SET_NUM_VAR(tui->params[0], n);
+ unibi_out(tui, unibi_parm_left_cursor);
}
ugrid_goto(grid, row, col);
return;
@@ -832,11 +781,11 @@ static void cursor_goto(UI *ui, int row, int col)
int n = col - grid->col;
if (n <= 2) {
while (n--) {
- unibi_out(ui, unibi_cursor_right);
+ unibi_out(tui, unibi_cursor_right);
}
} else {
- UNIBI_SET_NUM_VAR(data->params[0], n);
- unibi_out(ui, unibi_parm_right_cursor);
+ UNIBI_SET_NUM_VAR(tui->params[0], n);
+ unibi_out(tui, unibi_parm_right_cursor);
}
ugrid_goto(grid, row, col);
return;
@@ -847,11 +796,11 @@ static void cursor_goto(UI *ui, int row, int col)
int n = row - grid->row;
if (n <= 4) { // This might be just LF, so it is considered really cheap.
while (n--) {
- unibi_out(ui, unibi_cursor_down);
+ unibi_out(tui, unibi_cursor_down);
}
} else {
- UNIBI_SET_NUM_VAR(data->params[0], n);
- unibi_out(ui, unibi_parm_down_cursor);
+ UNIBI_SET_NUM_VAR(tui->params[0], n);
+ unibi_out(tui, unibi_parm_down_cursor);
}
ugrid_goto(grid, row, col);
return;
@@ -859,11 +808,11 @@ static void cursor_goto(UI *ui, int row, int col)
int n = grid->row - row;
if (n <= 2) {
while (n--) {
- unibi_out(ui, unibi_cursor_up);
+ unibi_out(tui, unibi_cursor_up);
}
} else {
- UNIBI_SET_NUM_VAR(data->params[0], n);
- unibi_out(ui, unibi_parm_up_cursor);
+ UNIBI_SET_NUM_VAR(tui->params[0], n);
+ unibi_out(tui, unibi_parm_up_cursor);
}
ugrid_goto(grid, row, col);
return;
@@ -871,20 +820,19 @@ static void cursor_goto(UI *ui, int row, int col)
}
safe_move:
- unibi_goto(ui, row, col);
+ unibi_goto(tui, row, col);
ugrid_goto(grid, row, col);
}
-static void print_spaces(UI *ui, int width)
+static void print_spaces(TUIData *tui, int width)
{
- TUIData *data = ui->data;
- UGrid *grid = &data->grid;
+ UGrid *grid = &tui->grid;
- out(ui, data->space_buf, (size_t)width);
+ out(tui, tui->space_buf, (size_t)width);
grid->col += width;
- if (data->immediate_wrap_after_last_column) {
+ if (tui->immediate_wrap_after_last_column) {
// Printing at the right margin immediately advances the cursor.
- final_column_wrap(ui);
+ final_column_wrap(tui);
}
}
@@ -893,28 +841,27 @@ static void print_spaces(UI *ui, int width)
///
/// @param is_doublewidth whether the character is double-width on the grid.
/// If true and the character is ambiguous-width, clear two cells.
-static void print_cell_at_pos(UI *ui, int row, int col, UCell *cell, bool is_doublewidth)
+static void print_cell_at_pos(TUIData *tui, int row, int col, UCell *cell, bool is_doublewidth)
{
- TUIData *data = ui->data;
- UGrid *grid = &data->grid;
+ UGrid *grid = &tui->grid;
if (grid->row == -1 && cell->data[0] == NUL) {
// If cursor needs to repositioned and there is nothing to print, don't move cursor.
return;
}
- cursor_goto(ui, row, col);
+ cursor_goto(tui, row, col);
bool is_ambiwidth = utf_ambiguous_width(utf_ptr2char(cell->data));
if (is_ambiwidth && is_doublewidth) {
// Clear the two screen cells.
// If the character is single-width in the host terminal it won't change the second cell.
- update_attrs(ui, cell->attr);
- print_spaces(ui, 2);
- cursor_goto(ui, row, col);
+ update_attrs(tui, cell->attr);
+ print_spaces(tui, 2);
+ cursor_goto(tui, row, col);
}
- print_cell(ui, cell);
+ print_cell(tui, cell);
if (is_ambiwidth) {
// Force repositioning cursor after printing an ambiguous-width character.
@@ -922,120 +869,116 @@ static void print_cell_at_pos(UI *ui, int row, int col, UCell *cell, bool is_dou
}
}
-static void clear_region(UI *ui, int top, int bot, int left, int right, int attr_id)
+static void clear_region(TUIData *tui, int top, int bot, int left, int right, int attr_id)
{
- TUIData *data = ui->data;
- UGrid *grid = &data->grid;
+ UGrid *grid = &tui->grid;
- update_attrs(ui, attr_id);
+ update_attrs(tui, attr_id);
// Background is set to the default color and the right edge matches the
// screen end, try to use terminal codes for clearing the requested area.
- if (data->can_clear_attr
- && left == 0 && right == ui->width && bot == ui->height) {
+ if (tui->can_clear_attr
+ && left == 0 && right == tui->width && bot == tui->height) {
if (top == 0) {
- unibi_out(ui, unibi_clear_screen);
+ unibi_out(tui, unibi_clear_screen);
ugrid_goto(grid, top, left);
} else {
- cursor_goto(ui, top, 0);
- unibi_out(ui, unibi_clr_eos);
+ cursor_goto(tui, top, 0);
+ unibi_out(tui, unibi_clr_eos);
}
} else {
int width = right - left;
// iterate through each line and clear
for (int row = top; row < bot; row++) {
- cursor_goto(ui, row, left);
- if (data->can_clear_attr && right == ui->width) {
- unibi_out(ui, unibi_clr_eol);
- } else if (data->can_erase_chars && data->can_clear_attr && width >= 5) {
- UNIBI_SET_NUM_VAR(data->params[0], width);
- unibi_out(ui, unibi_erase_chars);
+ cursor_goto(tui, row, left);
+ if (tui->can_clear_attr && right == tui->width) {
+ unibi_out(tui, unibi_clr_eol);
+ } else if (tui->can_erase_chars && tui->can_clear_attr && width >= 5) {
+ UNIBI_SET_NUM_VAR(tui->params[0], width);
+ unibi_out(tui, unibi_erase_chars);
} else {
- print_spaces(ui, width);
+ print_spaces(tui, width);
}
}
}
}
-static void set_scroll_region(UI *ui, int top, int bot, int left, int right)
+static void set_scroll_region(TUIData *tui, int top, int bot, int left, int right)
{
- TUIData *data = ui->data;
- UGrid *grid = &data->grid;
+ UGrid *grid = &tui->grid;
- UNIBI_SET_NUM_VAR(data->params[0], top);
- UNIBI_SET_NUM_VAR(data->params[1], bot);
- unibi_out(ui, unibi_change_scroll_region);
- if (left != 0 || right != ui->width - 1) {
- unibi_out_ext(ui, data->unibi_ext.enable_lr_margin);
- if (data->can_set_lr_margin) {
- UNIBI_SET_NUM_VAR(data->params[0], left);
- UNIBI_SET_NUM_VAR(data->params[1], right);
- unibi_out(ui, unibi_set_lr_margin);
+ UNIBI_SET_NUM_VAR(tui->params[0], top);
+ UNIBI_SET_NUM_VAR(tui->params[1], bot);
+ unibi_out(tui, unibi_change_scroll_region);
+ if (left != 0 || right != tui->width - 1) {
+ unibi_out_ext(tui, tui->unibi_ext.enable_lr_margin);
+ if (tui->can_set_lr_margin) {
+ UNIBI_SET_NUM_VAR(tui->params[0], left);
+ UNIBI_SET_NUM_VAR(tui->params[1], right);
+ unibi_out(tui, unibi_set_lr_margin);
} else {
- UNIBI_SET_NUM_VAR(data->params[0], left);
- unibi_out(ui, unibi_set_left_margin_parm);
- UNIBI_SET_NUM_VAR(data->params[0], right);
- unibi_out(ui, unibi_set_right_margin_parm);
+ UNIBI_SET_NUM_VAR(tui->params[0], left);
+ unibi_out(tui, unibi_set_left_margin_parm);
+ UNIBI_SET_NUM_VAR(tui->params[0], right);
+ unibi_out(tui, unibi_set_right_margin_parm);
}
}
grid->row = -1;
}
-static void reset_scroll_region(UI *ui, bool fullwidth)
+static void reset_scroll_region(TUIData *tui, bool fullwidth)
{
- TUIData *data = ui->data;
- UGrid *grid = &data->grid;
+ UGrid *grid = &tui->grid;
- if (0 <= data->unibi_ext.reset_scroll_region) {
- unibi_out_ext(ui, data->unibi_ext.reset_scroll_region);
+ if (0 <= tui->unibi_ext.reset_scroll_region) {
+ unibi_out_ext(tui, tui->unibi_ext.reset_scroll_region);
} else {
- UNIBI_SET_NUM_VAR(data->params[0], 0);
- UNIBI_SET_NUM_VAR(data->params[1], ui->height - 1);
- unibi_out(ui, unibi_change_scroll_region);
+ UNIBI_SET_NUM_VAR(tui->params[0], 0);
+ UNIBI_SET_NUM_VAR(tui->params[1], tui->height - 1);
+ unibi_out(tui, unibi_change_scroll_region);
}
if (!fullwidth) {
- if (data->can_set_lr_margin) {
- UNIBI_SET_NUM_VAR(data->params[0], 0);
- UNIBI_SET_NUM_VAR(data->params[1], ui->width - 1);
- unibi_out(ui, unibi_set_lr_margin);
+ if (tui->can_set_lr_margin) {
+ UNIBI_SET_NUM_VAR(tui->params[0], 0);
+ UNIBI_SET_NUM_VAR(tui->params[1], tui->width - 1);
+ unibi_out(tui, unibi_set_lr_margin);
} else {
- UNIBI_SET_NUM_VAR(data->params[0], 0);
- unibi_out(ui, unibi_set_left_margin_parm);
- UNIBI_SET_NUM_VAR(data->params[0], ui->width - 1);
- unibi_out(ui, unibi_set_right_margin_parm);
+ UNIBI_SET_NUM_VAR(tui->params[0], 0);
+ unibi_out(tui, unibi_set_left_margin_parm);
+ UNIBI_SET_NUM_VAR(tui->params[0], tui->width - 1);
+ unibi_out(tui, unibi_set_right_margin_parm);
}
- unibi_out_ext(ui, data->unibi_ext.disable_lr_margin);
+ unibi_out_ext(tui, tui->unibi_ext.disable_lr_margin);
}
grid->row = -1;
}
-static void tui_grid_resize(UI *ui, Integer g, Integer width, Integer height)
+void tui_grid_resize(TUIData *tui, Integer g, Integer width, Integer height)
{
- TUIData *data = ui->data;
- UGrid *grid = &data->grid;
+ UGrid *grid = &tui->grid;
ugrid_resize(grid, (int)width, (int)height);
- xfree(data->space_buf);
- data->space_buf = xmalloc((size_t)width * sizeof(*data->space_buf));
- memset(data->space_buf, ' ', (size_t)width);
+ xfree(tui->space_buf);
+ tui->space_buf = xmalloc((size_t)width * sizeof(*tui->space_buf));
+ memset(tui->space_buf, ' ', (size_t)width);
// resize might not always be followed by a clear before flush
// so clip the invalid region
- for (size_t i = 0; i < kv_size(data->invalid_regions); i++) {
- Rect *r = &kv_A(data->invalid_regions, i);
+ for (size_t i = 0; i < kv_size(tui->invalid_regions); i++) {
+ Rect *r = &kv_A(tui->invalid_regions, i);
r->bot = MIN(r->bot, grid->height);
r->right = MIN(r->right, grid->width);
}
- if (!got_winch && !data->is_starting) {
+ if (!got_winch && !tui->is_starting) {
// Resize the _host_ terminal.
- UNIBI_SET_NUM_VAR(data->params[0], (int)height);
- UNIBI_SET_NUM_VAR(data->params[1], (int)width);
- unibi_out_ext(ui, data->unibi_ext.resize_screen);
+ UNIBI_SET_NUM_VAR(tui->params[0], (int)height);
+ UNIBI_SET_NUM_VAR(tui->params[1], (int)width);
+ unibi_out_ext(tui, tui->unibi_ext.resize_screen);
// DECSLPP does not reset the scroll region.
- if (data->scroll_region_is_full_screen) {
- reset_scroll_region(ui, ui->width == grid->width);
+ if (tui->scroll_region_is_full_screen) {
+ reset_scroll_region(tui, tui->width == grid->width);
}
} else { // Already handled the SIGWINCH signal; avoid double-resize.
got_winch = got_winch > 0 ? got_winch - 1 : 0;
@@ -1043,22 +986,19 @@ static void tui_grid_resize(UI *ui, Integer g, Integer width, Integer height)
}
}
-static void tui_grid_clear(UI *ui, Integer g)
+void tui_grid_clear(TUIData *tui, Integer g)
{
- TUIData *data = ui->data;
- UGrid *grid = &data->grid;
+ UGrid *grid = &tui->grid;
ugrid_clear(grid);
- kv_size(data->invalid_regions) = 0;
- clear_region(ui, 0, grid->height, 0, grid->width, 0);
+ kv_size(tui->invalid_regions) = 0;
+ clear_region(tui, 0, grid->height, 0, grid->width, 0);
}
-static void tui_grid_cursor_goto(UI *ui, Integer grid, Integer row, Integer col)
+void tui_grid_cursor_goto(TUIData *tui, Integer grid, Integer row, Integer col)
{
- TUIData *data = ui->data;
-
// cursor position is validated in tui_flush
- data->row = (int)row;
- data->col = (int)col;
+ tui->row = (int)row;
+ tui->col = (int)col;
}
CursorShape tui_cursor_decode_shape(const char *shape_str)
@@ -1098,13 +1038,12 @@ static cursorentry_T decode_cursor_entry(Dictionary args)
return r;
}
-static void tui_mode_info_set(UI *ui, bool guicursor_enabled, Array args)
+void tui_mode_info_set(TUIData *tui, bool guicursor_enabled, Array args)
{
cursor_style_enabled = guicursor_enabled;
if (!guicursor_enabled) {
return; // Do not send cursor style control codes.
}
- TUIData *data = ui->data;
assert(args.size);
@@ -1112,72 +1051,81 @@ static void tui_mode_info_set(UI *ui, bool guicursor_enabled, Array args)
for (size_t i = 0; i < args.size; i++) {
assert(args.items[i].type == kObjectTypeDictionary);
cursorentry_T r = decode_cursor_entry(args.items[i].data.dictionary);
- data->cursor_shapes[i] = r;
+ tui->cursor_shapes[i] = r;
}
- tui_set_mode(ui, data->showing_mode);
+ tui_set_mode(tui, tui->showing_mode);
}
-static void tui_update_menu(UI *ui)
+void tui_update_menu(TUIData *tui)
{
// Do nothing; menus are for GUI only
}
-static void tui_busy_start(UI *ui)
+void tui_busy_start(TUIData *tui)
{
- ((TUIData *)ui->data)->busy = true;
+ tui->busy = true;
}
-static void tui_busy_stop(UI *ui)
+void tui_busy_stop(TUIData *tui)
{
- ((TUIData *)ui->data)->busy = false;
+ tui->busy = false;
}
-static void tui_mouse_on(UI *ui)
+void tui_mouse_on(TUIData *tui)
{
- TUIData *data = ui->data;
- if (!data->mouse_enabled) {
- unibi_out_ext(ui, data->unibi_ext.enable_mouse);
- data->mouse_enabled = true;
+ if (!tui->mouse_enabled) {
+ unibi_out_ext(tui, tui->unibi_ext.enable_mouse);
+ if (tui->mouse_move_enabled) {
+ unibi_out_ext(tui, tui->unibi_ext.enable_mouse_move);
+ }
+ tui->mouse_enabled = true;
}
}
-static void tui_mouse_off(UI *ui)
+void tui_mouse_off(TUIData *tui)
{
- TUIData *data = ui->data;
- if (data->mouse_enabled) {
- unibi_out_ext(ui, data->unibi_ext.disable_mouse);
- data->mouse_enabled = false;
+ if (tui->mouse_enabled) {
+ if (tui->mouse_move_enabled) {
+ unibi_out_ext(tui, tui->unibi_ext.disable_mouse_move);
+ }
+ unibi_out_ext(tui, tui->unibi_ext.disable_mouse);
+ tui->mouse_enabled = false;
}
}
-static void tui_set_mode(UI *ui, ModeShape mode)
+void tui_set_mode(TUIData *tui, ModeShape mode)
{
if (!cursor_style_enabled) {
return;
}
- TUIData *data = ui->data;
- cursorentry_T c = data->cursor_shapes[mode];
+ cursorentry_T c = tui->cursor_shapes[mode];
- if (c.id != 0 && c.id < (int)kv_size(data->attrs) && ui->rgb) {
- HlAttrs aep = kv_A(data->attrs, c.id);
+ if (c.id != 0 && c.id < (int)kv_size(tui->attrs) && tui->rgb) {
+ HlAttrs aep = kv_A(tui->attrs, c.id);
- data->want_invisible = aep.hl_blend == 100;
- if (data->want_invisible) {
- unibi_out(ui, unibi_cursor_invisible);
+ tui->want_invisible = aep.hl_blend == 100;
+ if (tui->want_invisible) {
+ unibi_out(tui, unibi_cursor_invisible);
} else if (aep.rgb_ae_attr & HL_INVERSE) {
// We interpret "inverse" as "default" (no termcode for "inverse"...).
// Hopefully the user's default cursor color is inverse.
- unibi_out_ext(ui, data->unibi_ext.reset_cursor_color);
+ unibi_out_ext(tui, tui->unibi_ext.reset_cursor_color);
} else {
- UNIBI_SET_NUM_VAR(data->params[0], aep.rgb_bg_color);
- unibi_out_ext(ui, data->unibi_ext.set_cursor_color);
- data->cursor_color_changed = true;
+ if (tui->set_cursor_color_as_str) {
+ char hexbuf[8];
+ snprintf(hexbuf, 7 + 1, "#%06x", aep.rgb_bg_color);
+ UNIBI_SET_STR_VAR(tui->params[0], hexbuf);
+ } else {
+ UNIBI_SET_NUM_VAR(tui->params[0], aep.rgb_bg_color);
+ }
+ unibi_out_ext(tui, tui->unibi_ext.set_cursor_color);
+ tui->cursor_color_changed = true;
}
} else if (c.id == 0) {
// No cursor color for this mode; reset to default.
- data->want_invisible = false;
- unibi_out_ext(ui, data->unibi_ext.reset_cursor_color);
+ tui->want_invisible = false;
+ unibi_out_ext(tui, tui->unibi_ext.reset_cursor_color);
}
int shape;
@@ -1191,83 +1139,86 @@ static void tui_set_mode(UI *ui, ModeShape mode)
case SHAPE_VER:
shape = 5; break;
}
- UNIBI_SET_NUM_VAR(data->params[0], shape + (int)(c.blinkon == 0));
- unibi_out_ext(ui, data->unibi_ext.set_cursor_style);
+ UNIBI_SET_NUM_VAR(tui->params[0], shape + (int)(c.blinkon == 0));
+ unibi_out_ext(tui, tui->unibi_ext.set_cursor_style);
}
/// @param mode editor mode
-static void tui_mode_change(UI *ui, String mode, Integer mode_idx)
+void tui_mode_change(TUIData *tui, String mode, Integer mode_idx)
{
- TUIData *data = ui->data;
#ifdef UNIX
// If stdin is not a TTY, the LHS of pipe may change the state of the TTY
// after calling uv_tty_set_mode. So, set the mode of the TTY again here.
// #13073
- if (data->is_starting && data->input.in_fd == STDERR_FILENO) {
- int ret = uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_NORMAL);
+ if (tui->is_starting && tui->input.in_fd == STDERR_FILENO) {
+ int ret = uv_tty_set_mode(&tui->output_handle.tty, UV_TTY_MODE_NORMAL);
if (ret) {
ELOG("uv_tty_set_mode failed: %s", uv_strerror(ret));
}
- ret = uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_IO);
+ ret = uv_tty_set_mode(&tui->output_handle.tty, UV_TTY_MODE_IO);
if (ret) {
ELOG("uv_tty_set_mode failed: %s", uv_strerror(ret));
}
}
#endif
- tui_set_mode(ui, (ModeShape)mode_idx);
- data->is_starting = false; // mode entered, no longer starting
- data->showing_mode = (ModeShape)mode_idx;
+ tui_set_mode(tui, (ModeShape)mode_idx);
+ if (tui->is_starting) {
+ if (tui->verbose >= 3) {
+ show_verbose_terminfo(tui);
+ }
+ }
+ tui->is_starting = false; // mode entered, no longer starting
+ tui->showing_mode = (ModeShape)mode_idx;
}
-static void tui_grid_scroll(UI *ui, Integer g, Integer startrow, // -V751
- Integer endrow, Integer startcol, Integer endcol, Integer rows,
- Integer cols FUNC_ATTR_UNUSED)
+void tui_grid_scroll(TUIData *tui, Integer g, Integer startrow, // -V751
+ Integer endrow, Integer startcol, Integer endcol, Integer rows,
+ Integer cols FUNC_ATTR_UNUSED)
{
- TUIData *data = ui->data;
- UGrid *grid = &data->grid;
+ UGrid *grid = &tui->grid;
int top = (int)startrow, bot = (int)endrow - 1;
int left = (int)startcol, right = (int)endcol - 1;
- bool fullwidth = left == 0 && right == ui->width - 1;
- data->scroll_region_is_full_screen = fullwidth
- && top == 0 && bot == ui->height - 1;
+ bool fullwidth = left == 0 && right == tui->width - 1;
+ tui->scroll_region_is_full_screen = fullwidth
+ && top == 0 && bot == tui->height - 1;
ugrid_scroll(grid, top, bot, left, right, (int)rows);
- bool can_scroll = data->can_scroll
- && (data->scroll_region_is_full_screen
- || (data->can_change_scroll_region
- && ((left == 0 && right == ui->width - 1)
- || data->can_set_lr_margin
- || data->can_set_left_right_margin)));
+ bool can_scroll = tui->can_scroll
+ && (tui->scroll_region_is_full_screen
+ || (tui->can_change_scroll_region
+ && ((left == 0 && right == tui->width - 1)
+ || tui->can_set_lr_margin
+ || tui->can_set_left_right_margin)));
if (can_scroll) {
// Change terminal scroll region and move cursor to the top
- if (!data->scroll_region_is_full_screen) {
- set_scroll_region(ui, top, bot, left, right);
+ if (!tui->scroll_region_is_full_screen) {
+ set_scroll_region(tui, top, bot, left, right);
}
- cursor_goto(ui, top, left);
- update_attrs(ui, 0);
+ cursor_goto(tui, top, left);
+ update_attrs(tui, 0);
if (rows > 0) {
if (rows == 1) {
- unibi_out(ui, unibi_delete_line);
+ unibi_out(tui, unibi_delete_line);
} else {
- UNIBI_SET_NUM_VAR(data->params[0], (int)rows);
- unibi_out(ui, unibi_parm_delete_line);
+ UNIBI_SET_NUM_VAR(tui->params[0], (int)rows);
+ unibi_out(tui, unibi_parm_delete_line);
}
} else {
if (rows == -1) {
- unibi_out(ui, unibi_insert_line);
+ unibi_out(tui, unibi_insert_line);
} else {
- UNIBI_SET_NUM_VAR(data->params[0], -(int)rows);
- unibi_out(ui, unibi_parm_insert_line);
+ UNIBI_SET_NUM_VAR(tui->params[0], -(int)rows);
+ unibi_out(tui, unibi_parm_insert_line);
}
}
// Restore terminal scroll region and cursor
- if (!data->scroll_region_is_full_screen) {
- reset_scroll_region(ui, fullwidth);
+ if (!tui->scroll_region_is_full_screen) {
+ reset_scroll_region(tui, fullwidth);
}
} else {
// Mark the moved region as invalid for redrawing later
@@ -1276,47 +1227,46 @@ static void tui_grid_scroll(UI *ui, Integer g, Integer startrow, // -V751
} else {
startrow = startrow - rows;
}
- invalidate(ui, (int)startrow, (int)endrow, (int)startcol, (int)endcol);
+ invalidate(tui, (int)startrow, (int)endrow, (int)startcol, (int)endcol);
}
}
-static void tui_hl_attr_define(UI *ui, Integer id, HlAttrs attrs, HlAttrs cterm_attrs, Array info)
+void tui_hl_attr_define(TUIData *tui, Integer id, HlAttrs attrs, HlAttrs cterm_attrs, Array info)
{
- TUIData *data = ui->data;
- kv_a(data->attrs, (size_t)id) = attrs;
+ attrs.cterm_ae_attr = cterm_attrs.cterm_ae_attr;
+ attrs.cterm_fg_color = cterm_attrs.cterm_fg_color;
+ attrs.cterm_bg_color = cterm_attrs.cterm_bg_color;
+ kv_a(tui->attrs, (size_t)id) = attrs;
}
-static void tui_bell(UI *ui)
+void tui_bell(TUIData *tui)
{
- unibi_out(ui, unibi_bell);
+ unibi_out(tui, unibi_bell);
}
-static void tui_visual_bell(UI *ui)
+void tui_visual_bell(TUIData *tui)
{
- unibi_out(ui, unibi_flash_screen);
+ unibi_out(tui, unibi_flash_screen);
}
-static void tui_default_colors_set(UI *ui, Integer rgb_fg, Integer rgb_bg, Integer rgb_sp,
- Integer cterm_fg, Integer cterm_bg)
+void tui_default_colors_set(TUIData *tui, Integer rgb_fg, Integer rgb_bg, Integer rgb_sp,
+ Integer cterm_fg, Integer cterm_bg)
{
- TUIData *data = ui->data;
-
- data->clear_attrs.rgb_fg_color = (int)rgb_fg;
- data->clear_attrs.rgb_bg_color = (int)rgb_bg;
- data->clear_attrs.rgb_sp_color = (int)rgb_sp;
- data->clear_attrs.cterm_fg_color = (int)cterm_fg;
- data->clear_attrs.cterm_bg_color = (int)cterm_bg;
+ tui->clear_attrs.rgb_fg_color = (int)rgb_fg;
+ tui->clear_attrs.rgb_bg_color = (int)rgb_bg;
+ tui->clear_attrs.rgb_sp_color = (int)rgb_sp;
+ tui->clear_attrs.cterm_fg_color = (int)cterm_fg;
+ tui->clear_attrs.cterm_bg_color = (int)cterm_bg;
- data->print_attr_id = -1;
- invalidate(ui, 0, data->grid.height, 0, data->grid.width);
+ tui->print_attr_id = -1;
+ invalidate(tui, 0, tui->grid.height, 0, tui->grid.width);
}
-static void tui_flush(UI *ui)
+void tui_flush(TUIData *tui)
{
- TUIData *data = ui->data;
- UGrid *grid = &data->grid;
+ UGrid *grid = &tui->grid;
- size_t nrevents = loop_size(data->loop);
+ size_t nrevents = loop_size(tui->loop);
if (nrevents > TOO_MANY_EVENTS) {
WLOG("TUI event-queue flooded (thread_events=%zu); purging", nrevents);
// Back-pressure: UI events may accumulate much faster than the terminal
@@ -1324,12 +1274,12 @@ static void tui_flush(UI *ui)
// wait for the TUI event-queue to drain, and if there are ~millions of
// events in the queue, it could take hours. Clearing the queue allows the
// UI to recover. #1234 #5396
- loop_purge(data->loop);
- tui_busy_stop(ui); // avoid hidden cursor
+ loop_purge(tui->loop);
+ tui_busy_stop(tui); // avoid hidden cursor
}
- while (kv_size(data->invalid_regions)) {
- Rect r = kv_pop(data->invalid_regions);
+ while (kv_size(tui->invalid_regions)) {
+ Rect r = kv_pop(tui->invalid_regions);
assert(r.bot <= grid->height && r.right <= grid->width);
for (int row = r.top; row < r.bot; row++) {
@@ -1344,155 +1294,175 @@ static void tui_flush(UI *ui)
}
UGRID_FOREACH_CELL(grid, row, r.left, clear_col, {
- print_cell_at_pos(ui, row, curcol, cell,
+ print_cell_at_pos(tui, row, curcol, cell,
curcol < clear_col - 1 && (cell + 1)->data[0] == NUL);
});
if (clear_col < r.right) {
- clear_region(ui, row, row + 1, clear_col, r.right, clear_attr);
+ clear_region(tui, row, row + 1, clear_col, r.right, clear_attr);
}
}
}
- cursor_goto(ui, data->row, data->col);
+ cursor_goto(tui, tui->row, tui->col);
- flush_buf(ui);
+ flush_buf(tui);
}
/// Dumps termcap info to the messages area, if 'verbose' >= 3.
-static void show_termcap_event(void **argv)
+static void show_verbose_terminfo(TUIData *tui)
{
- if (p_verbose < 3) {
- return;
- }
- const unibi_term *const ut = argv[0];
+ const unibi_term *const ut = tui->ut;
if (!ut) {
abort();
}
- verbose_enter();
- // XXX: (future) if unibi_term is modified (e.g. after a terminal
- // query-response) this is a race condition.
- terminfo_info_msg(ut);
- verbose_leave();
- verbose_stop(); // flush now
+
+ Array chunks = ARRAY_DICT_INIT;
+ Array title = ARRAY_DICT_INIT;
+ ADD(title, STRING_OBJ(cstr_to_string("\n\n--- Terminal info --- {{{\n")));
+ ADD(title, STRING_OBJ(cstr_to_string("Title")));
+ ADD(chunks, ARRAY_OBJ(title));
+ Array info = ARRAY_DICT_INIT;
+ String str = terminfo_info_msg(ut, tui->term);
+ ADD(info, STRING_OBJ(str));
+ ADD(chunks, ARRAY_OBJ(info));
+ Array end_fold = ARRAY_DICT_INIT;
+ ADD(end_fold, STRING_OBJ(cstr_to_string("}}}\n")));
+ ADD(end_fold, STRING_OBJ(cstr_to_string("Title")));
+ ADD(chunks, ARRAY_OBJ(end_fold));
+
+ Array args = ARRAY_DICT_INIT;
+ ADD(args, ARRAY_OBJ(chunks));
+ ADD(args, BOOLEAN_OBJ(true)); // history
+ Dictionary opts = ARRAY_DICT_INIT;
+ PUT(opts, "verbose", BOOLEAN_OBJ(true));
+ ADD(args, DICTIONARY_OBJ(opts));
+ rpc_send_event(ui_client_channel_id, "nvim_echo", args);
+ api_free_array(args);
}
#ifdef UNIX
static void suspend_event(void **argv)
{
- UI *ui = argv[0];
- TUIData *data = ui->data;
- bool enable_mouse = data->mouse_enabled;
- tui_terminal_stop(ui);
- data->cont_received = false;
- stream_set_blocking(input_global_fd(), true); // normalize stream (#2598)
- signal_stop();
+ TUIData *tui = argv[0];
+ bool enable_mouse = tui->mouse_enabled;
+ tui_terminal_stop(tui);
+ stream_set_blocking(tui->input.in_fd, true); // normalize stream (#2598)
+
kill(0, SIGTSTP);
- signal_start();
- while (!data->cont_received) {
- // poll the event loop until SIGCONT is received
- loop_poll_events(data->loop, -1);
- }
- tui_terminal_start(ui);
- tui_terminal_after_startup(ui);
+
+ tui_terminal_start(tui);
+ tui_terminal_after_startup(tui);
if (enable_mouse) {
- tui_mouse_on(ui);
+ tui_mouse_on(tui);
}
- stream_set_blocking(input_global_fd(), false); // libuv expects this
- // resume the main thread
- CONTINUE(data->bridge);
+ stream_set_blocking(tui->input.in_fd, false); // libuv expects this
}
#endif
-static void tui_suspend(UI *ui)
+void tui_suspend(TUIData *tui)
{
+// on a non-UNIX system, this is a no-op
#ifdef UNIX
- TUIData *data = ui->data;
// kill(0, SIGTSTP) won't stop the UI thread, so we must poll for SIGCONT
// before continuing. This is done in another callback to avoid
// loop_poll_events recursion
- multiqueue_put_event(data->loop->fast_events,
- event_create(suspend_event, 1, ui));
+ multiqueue_put_event(resize_events,
+ event_create(suspend_event, 1, tui));
#endif
}
-static void tui_set_title(UI *ui, String title)
+void tui_set_title(TUIData *tui, String title)
{
- TUIData *data = ui->data;
- if (!(title.data && unibi_get_str(data->ut, unibi_to_status_line)
- && unibi_get_str(data->ut, unibi_from_status_line))) {
+ if (!(title.data && unibi_get_str(tui->ut, unibi_to_status_line)
+ && unibi_get_str(tui->ut, unibi_from_status_line))) {
return;
}
- unibi_out(ui, unibi_to_status_line);
- out(ui, title.data, title.size);
- unibi_out(ui, unibi_from_status_line);
+ unibi_out(tui, unibi_to_status_line);
+ out(tui, title.data, title.size);
+ unibi_out(tui, unibi_from_status_line);
}
-static void tui_set_icon(UI *ui, String icon)
+void tui_set_icon(TUIData *tui, String icon)
{}
-static void tui_screenshot(UI *ui, String path)
+void tui_screenshot(TUIData *tui, String path)
{
- TUIData *data = ui->data;
- UGrid *grid = &data->grid;
- flush_buf(ui);
+ UGrid *grid = &tui->grid;
+ flush_buf(tui);
grid->row = 0;
grid->col = 0;
FILE *f = fopen(path.data, "w");
- data->screenshot = f;
+ tui->screenshot = f;
fprintf(f, "%d,%d\n", grid->height, grid->width);
- unibi_out(ui, unibi_clear_screen);
+ unibi_out(tui, unibi_clear_screen);
for (int i = 0; i < grid->height; i++) {
- cursor_goto(ui, i, 0);
+ cursor_goto(tui, i, 0);
for (int j = 0; j < grid->width; j++) {
- print_cell(ui, &grid->cells[i][j]);
+ print_cell(tui, &grid->cells[i][j]);
}
}
- flush_buf(ui);
- data->screenshot = NULL;
+ flush_buf(tui);
+ tui->screenshot = NULL;
fclose(f);
}
-static void tui_option_set(UI *ui, String name, Object value)
+void tui_option_set(TUIData *tui, String name, Object value)
{
- TUIData *data = ui->data;
- if (strequal(name.data, "termguicolors")) {
- ui->rgb = value.data.boolean;
+ if (strequal(name.data, "mousemoveevent")) {
+ if (tui->mouse_move_enabled != value.data.boolean) {
+ if (tui->mouse_enabled) {
+ tui_mouse_off(tui);
+ tui->mouse_move_enabled = value.data.boolean;
+ tui_mouse_on(tui);
+ } else {
+ tui->mouse_move_enabled = value.data.boolean;
+ }
+ }
+ } else if (strequal(name.data, "termguicolors")) {
+ tui->rgb = value.data.boolean;
+ tui->print_attr_id = -1;
+ invalidate(tui, 0, tui->grid.height, 0, tui->grid.width);
- data->print_attr_id = -1;
- invalidate(ui, 0, data->grid.height, 0, data->grid.width);
+ if (ui_client_channel_id) {
+ MAXSIZE_TEMP_ARRAY(args, 2);
+ ADD_C(args, STRING_OBJ(cstr_as_string("rgb")));
+ ADD_C(args, BOOLEAN_OBJ(value.data.boolean));
+ rpc_send_event(ui_client_channel_id, "nvim_ui_set_option", args);
+ }
} else if (strequal(name.data, "ttimeout")) {
- data->input.ttimeout = value.data.boolean;
+ tui->input.ttimeout = value.data.boolean;
} else if (strequal(name.data, "ttimeoutlen")) {
- data->input.ttimeoutlen = (long)value.data.integer;
+ tui->input.ttimeoutlen = (long)value.data.integer;
+ } else if (strequal(name.data, "verbose")) {
+ tui->verbose = value.data.integer;
}
}
-static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol, Integer endcol,
- Integer clearcol, Integer clearattr, LineFlags flags, const schar_T *chunk,
- const sattr_T *attrs)
+void tui_raw_line(TUIData *tui, Integer g, Integer linerow, Integer startcol, Integer endcol,
+ Integer clearcol, Integer clearattr, LineFlags flags, const schar_T *chunk,
+ const sattr_T *attrs)
{
- TUIData *data = ui->data;
- UGrid *grid = &data->grid;
+ UGrid *grid = &tui->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));
+ assert((size_t)attrs[c - startcol] < kv_size(tui->attrs));
grid->cells[linerow][c].attr = attrs[c - startcol];
}
UGRID_FOREACH_CELL(grid, (int)linerow, (int)startcol, (int)endcol, {
- print_cell_at_pos(ui, (int)linerow, curcol, cell,
+ print_cell_at_pos(tui, (int)linerow, curcol, cell,
curcol < endcol - 1 && (cell + 1)->data[0] == NUL);
});
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(tui, (int)linerow, (int)linerow + 1, (int)endcol, (int)clearcol,
(int)clearattr);
}
- if (flags & kLineFlagWrap && ui->width == grid->width
+ if (flags & kLineFlagWrap && tui->width == grid->width
&& linerow + 1 < grid->height) {
// Only do line wrapping if the grid width is equal to the terminal
// width and the line continuation is within the grid.
@@ -1500,23 +1470,22 @@ static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol, I
if (endcol != grid->width) {
// Print the last char of the row, if we haven't already done so.
int size = grid->cells[linerow][grid->width - 1].data[0] == NUL ? 2 : 1;
- print_cell_at_pos(ui, (int)linerow, grid->width - size,
+ print_cell_at_pos(tui, (int)linerow, grid->width - size,
&grid->cells[linerow][grid->width - size], size == 2);
}
// Wrap the cursor over to the next line. The next line will be
// printed immediately without an intervening newline.
- final_column_wrap(ui);
+ final_column_wrap(tui);
}
}
-static void invalidate(UI *ui, int top, int bot, int left, int right)
+static void invalidate(TUIData *tui, int top, int bot, int left, int right)
{
- TUIData *data = ui->data;
Rect *intersects = NULL;
- for (size_t i = 0; i < kv_size(data->invalid_regions); i++) {
- Rect *r = &kv_A(data->invalid_regions, i);
+ for (size_t i = 0; i < kv_size(tui->invalid_regions); i++) {
+ Rect *r = &kv_A(tui->invalid_regions, i);
// adjacent regions are treated as overlapping
if (!(top > r->bot || bot < r->top)
&& !(left > r->right || right < r->left)) {
@@ -1534,20 +1503,19 @@ static void invalidate(UI *ui, int top, int bot, int left, int right)
intersects->right = MAX(right, intersects->right);
} else {
// Else just add a new entry;
- kv_push(data->invalid_regions, ((Rect) { top, bot, left, right }));
+ kv_push(tui->invalid_regions, ((Rect) { top, bot, left, right }));
}
}
/// Tries to get the user's wanted dimensions (columns and rows) for the entire
/// application (i.e., the host terminal).
-static void tui_guess_size(UI *ui)
+void tui_guess_size(TUIData *tui)
{
- TUIData *data = ui->data;
int width = 0, height = 0;
// 1 - try from a system call(ioctl/TIOCGWINSZ on unix)
- if (data->out_isatty
- && !uv_tty_get_winsize(&data->output_handle.tty, &width, &height)) {
+ if (tui->out_isatty
+ && !uv_tty_get_winsize(&tui->output_handle.tty, &width, &height)) {
goto end;
}
@@ -1562,8 +1530,8 @@ static void tui_guess_size(UI *ui)
}
// 3 - read from terminfo if available
- height = unibi_get_num(data->ut, unibi_lines);
- width = unibi_get_num(data->ut, unibi_columns);
+ height = unibi_get_num(tui->ut, unibi_lines);
+ width = unibi_get_num(tui->ut, unibi_columns);
end:
if (width <= 0 || height <= 0) {
@@ -1572,45 +1540,49 @@ static void tui_guess_size(UI *ui)
height = DFLT_ROWS;
}
- data->bridge->bridge.width = ui->width = width;
- data->bridge->bridge.height = ui->height = height;
+ if (tui->width != width || tui->height != height) {
+ tui->width = width;
+ tui->height = height;
+
+ ui_client_set_size(width, height);
+ }
}
-static void unibi_goto(UI *ui, int row, int col)
+static void unibi_goto(TUIData *tui, int row, int col)
{
- TUIData *data = ui->data;
- UNIBI_SET_NUM_VAR(data->params[0], row);
- UNIBI_SET_NUM_VAR(data->params[1], col);
- unibi_out(ui, unibi_cursor_address);
+ UNIBI_SET_NUM_VAR(tui->params[0], row);
+ UNIBI_SET_NUM_VAR(tui->params[1], col);
+ unibi_out(tui, unibi_cursor_address);
}
#define UNIBI_OUT(fn) \
do { \
- TUIData *data = ui->data; \
const char *str = NULL; \
if (unibi_index >= 0) { \
- str = fn(data->ut, (unsigned)unibi_index); \
+ str = fn(tui->ut, (unsigned)unibi_index); \
} \
if (str) { \
unibi_var_t vars[26 + 26]; \
- size_t orig_pos = data->bufpos; \
+ unibi_var_t params[9]; \
+ size_t orig_pos = tui->bufpos; \
memset(&vars, 0, sizeof(vars)); \
- data->cork = true; \
+ tui->cork = true; \
retry: \
- unibi_format(vars, vars + 26, str, data->params, out, ui, NULL, NULL); \
- if (data->overflow) { \
- data->bufpos = orig_pos; \
- flush_buf(ui); \
+ memcpy(params, tui->params, sizeof(params)); \
+ unibi_format(vars, vars + 26, str, params, out, tui, pad, tui); \
+ if (tui->overflow) { \
+ tui->bufpos = orig_pos; \
+ flush_buf(tui); \
goto retry; \
} \
- data->cork = false; \
+ tui->cork = false; \
} \
} while (0)
-static void unibi_out(UI *ui, int unibi_index)
+static void unibi_out(TUIData *tui, int unibi_index)
{
UNIBI_OUT(unibi_get_str);
}
-static void unibi_out_ext(UI *ui, int unibi_index)
+static void unibi_out_ext(TUIData *tui, int unibi_index)
{
UNIBI_OUT(unibi_get_ext_str);
}
@@ -1618,25 +1590,47 @@ static void unibi_out_ext(UI *ui, int unibi_index)
static void out(void *ctx, const char *str, size_t len)
{
- UI *ui = ctx;
- TUIData *data = ui->data;
- size_t available = sizeof(data->buf) - data->bufpos;
+ TUIData *tui = ctx;
+ size_t available = sizeof(tui->buf) - tui->bufpos;
- if (data->cork && data->overflow) {
+ if (tui->cork && tui->overflow) {
return;
}
if (len > available) {
- if (data->cork) {
- data->overflow = true;
+ if (tui->cork) {
+ // Called by unibi_format(): avoid flush_buf() halfway an escape sequence.
+ tui->overflow = true;
return;
- } else {
- flush_buf(ui);
}
+ flush_buf(tui);
+ }
+
+ memcpy(tui->buf + tui->bufpos, str, len);
+ tui->bufpos += len;
+}
+
+/// Called by unibi_format() for padding instructions.
+/// The following parameter descriptions are extracted from unibi_format(3) and terminfo(5).
+///
+/// @param ctx the same as `ctx2` passed to unibi_format()
+/// @param delay the delay in tenths of milliseconds
+/// @param scale padding is proportional to the number of lines affected
+/// @param force padding is mandatory
+static void pad(void *ctx, size_t delay, int scale FUNC_ATTR_UNUSED, int force)
+{
+ if (!force) {
+ return;
+ }
+
+ TUIData *tui = ctx;
+
+ if (tui->overflow) {
+ return;
}
- memcpy(data->buf + data->bufpos, str, len);
- data->bufpos += len;
+ flush_buf(tui);
+ uv_sleep((unsigned int)(delay/10));
}
static void unibi_set_if_empty(unibi_term *ut, enum unibi_string str, const char *val)
@@ -1673,10 +1667,10 @@ static int unibi_find_ext_bool(unibi_term *ut, const char *name)
/// Patches the terminfo records after loading from system or built-in db.
/// Several entries in terminfo are known to be deficient or outright wrong;
/// and several terminal emulators falsely announce incorrect terminal types.
-static void patch_terminfo_bugs(TUIData *data, const char *term, const char *colorterm,
+static void patch_terminfo_bugs(TUIData *tui, const char *term, const char *colorterm,
long vte_version, long konsolev, bool iterm_env, bool nsterm)
{
- unibi_term *ut = data->ut;
+ unibi_term *ut = tui->ut;
const char *xterm_version = os_getenv("XTERM_VERSION");
#if 0 // We don't need to identify this specifically, for now.
bool roxterm = !!os_getenv("ROXTERM_ID");
@@ -1791,7 +1785,7 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col
}
}
-#ifdef WIN32
+#ifdef MSWIN
// XXX: workaround libuv implicit LF => CRLF conversion. #10558
unibi_set_str(ut, unibi_cursor_down, "\x1b[B");
#endif
@@ -1873,14 +1867,14 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col
#define XTERM_SETAB_16 \
"\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e39%;m"
- data->unibi_ext.get_bg = (int)unibi_add_ext_str(ut, "ext.get_bg",
- "\x1b]11;?\x07");
+ tui->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");
+ tui->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) {
@@ -1911,14 +1905,14 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col
// Blacklist of terminals that cannot be trusted to report DECSCUSR support.
if (!(st || (vte_version != 0 && vte_version < 3900) || konsolev)) {
- data->unibi_ext.reset_cursor_style = unibi_find_ext_str(ut, "Se");
- data->unibi_ext.set_cursor_style = unibi_find_ext_str(ut, "Ss");
+ tui->unibi_ext.reset_cursor_style = unibi_find_ext_str(ut, "Se");
+ tui->unibi_ext.set_cursor_style = unibi_find_ext_str(ut, "Ss");
}
// Dickey ncurses terminfo includes Ss/Se capabilities since 2011-07-14. So
// adding them to terminal types, that have such control sequences but lack
// the correct terminfo entries, is a fixup, not an augmentation.
- if (-1 == data->unibi_ext.set_cursor_style) {
+ if (-1 == tui->unibi_ext.set_cursor_style) {
// DECSCUSR (cursor shape) is widely supported.
// https://github.com/gnachman/iTerm2/pull/92
if ((!bsdvt && (!konsolev || konsolev >= 180770))
@@ -1944,59 +1938,59 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col
// Example: console-terminal-emulator from the nosh toolset.
|| (linuxvt
&& (xterm_version || (vte_version > 0) || colorterm)))) {
- data->unibi_ext.set_cursor_style =
+ tui->unibi_ext.set_cursor_style =
(int)unibi_add_ext_str(ut, "Ss", "\x1b[%p1%d q");
- if (-1 == data->unibi_ext.reset_cursor_style) {
- data->unibi_ext.reset_cursor_style = (int)unibi_add_ext_str(ut, "Se",
- "");
+ if (-1 == tui->unibi_ext.reset_cursor_style) {
+ tui->unibi_ext.reset_cursor_style = (int)unibi_add_ext_str(ut, "Se",
+ "");
}
- unibi_set_ext_str(ut, (size_t)data->unibi_ext.reset_cursor_style,
+ unibi_set_ext_str(ut, (size_t)tui->unibi_ext.reset_cursor_style,
"\x1b[ q");
} else if (linuxvt) {
// Linux uses an idiosyncratic escape code to set the cursor shape and
// does not support DECSCUSR.
// See http://linuxgazette.net/137/anonymous.html for more info
- data->unibi_ext.set_cursor_style = (int)unibi_add_ext_str(ut, "Ss",
- "\x1b[?"
- "%?"
- // The parameter passed to Ss is the DECSCUSR parameter, so the
- // terminal capability has to translate into the Linux idiosyncratic
- // parameter.
- //
- // linuxvt only supports block and underline. It is also only
- // possible to have a steady block (no steady underline)
- "%p1%{2}%<" "%t%{8}" // blink block
- "%e%p1%{2}%=" "%t%{112}" // steady block
- "%e%p1%{3}%=" "%t%{4}" // blink underline (set to half block)
- "%e%p1%{4}%=" "%t%{4}" // steady underline
- "%e%p1%{5}%=" "%t%{2}" // blink bar (set to underline)
- "%e%p1%{6}%=" "%t%{2}" // steady bar
- "%e%{0}" // anything else
- "%;" "%dc");
- if (-1 == data->unibi_ext.reset_cursor_style) {
- data->unibi_ext.reset_cursor_style = (int)unibi_add_ext_str(ut, "Se",
- "");
+ tui->unibi_ext.set_cursor_style = (int)unibi_add_ext_str(ut, "Ss",
+ "\x1b[?"
+ "%?"
+ // The parameter passed to Ss is the DECSCUSR parameter, so the
+ // terminal capability has to translate into the Linux idiosyncratic
+ // parameter.
+ //
+ // linuxvt only supports block and underline. It is also only
+ // possible to have a steady block (no steady underline)
+ "%p1%{2}%<" "%t%{8}" // blink block
+ "%e%p1%{2}%=" "%t%{112}" // steady block
+ "%e%p1%{3}%=" "%t%{4}" // blink underline (set to half block)
+ "%e%p1%{4}%=" "%t%{4}" // steady underline
+ "%e%p1%{5}%=" "%t%{2}" // blink bar (set to underline)
+ "%e%p1%{6}%=" "%t%{2}" // steady bar
+ "%e%{0}" // anything else
+ "%;" "%dc");
+ if (-1 == tui->unibi_ext.reset_cursor_style) {
+ tui->unibi_ext.reset_cursor_style = (int)unibi_add_ext_str(ut, "Se",
+ "");
}
- unibi_set_ext_str(ut, (size_t)data->unibi_ext.reset_cursor_style,
+ unibi_set_ext_str(ut, (size_t)tui->unibi_ext.reset_cursor_style,
"\x1b[?c");
} else if (konsolev > 0 && konsolev < 180770) {
// Konsole before version 18.07.70: set up a nonce profile. This has
// side effects on temporary font resizing. #6798
- data->unibi_ext.set_cursor_style = (int)unibi_add_ext_str(ut, "Ss",
- TMUX_WRAP(tmux,
- "\x1b]50;CursorShape=%?"
- "%p1%{3}%<" "%t%{0}" // block
- "%e%p1%{5}%<" "%t%{2}" // underline
- "%e%{1}" // everything else is bar
- "%;%d;BlinkingCursorEnabled=%?"
- "%p1%{1}%<" "%t%{1}" // Fortunately if we exclude zero as special,
- "%e%p1%{1}%&" // in all other cases we can treat bit #0 as a flag.
- "%;%d\x07"));
- if (-1 == data->unibi_ext.reset_cursor_style) {
- data->unibi_ext.reset_cursor_style = (int)unibi_add_ext_str(ut, "Se",
- "");
+ tui->unibi_ext.set_cursor_style = (int)unibi_add_ext_str(ut, "Ss",
+ TMUX_WRAP(tmux,
+ "\x1b]50;CursorShape=%?"
+ "%p1%{3}%<" "%t%{0}" // block
+ "%e%p1%{5}%<" "%t%{2}" // underline
+ "%e%{1}" // everything else is bar
+ "%;%d;BlinkingCursorEnabled=%?"
+ "%p1%{1}%<" "%t%{1}" // Fortunately if we exclude zero as special,
+ "%e%p1%{1}%&" // in all other cases we can treat bit #0 as a flag.
+ "%;%d\x07"));
+ if (-1 == tui->unibi_ext.reset_cursor_style) {
+ tui->unibi_ext.reset_cursor_style = (int)unibi_add_ext_str(ut, "Se",
+ "");
}
- unibi_set_ext_str(ut, (size_t)data->unibi_ext.reset_cursor_style,
+ unibi_set_ext_str(ut, (size_t)tui->unibi_ext.reset_cursor_style,
"\x1b]50;\x07");
}
}
@@ -2004,10 +1998,10 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col
/// This adds stuff that is not in standard terminfo as extended unibilium
/// capabilities.
-static void augment_terminfo(TUIData *data, const char *term, long vte_version, long konsolev,
+static void augment_terminfo(TUIData *tui, const char *term, long vte_version, long konsolev,
bool iterm_env, bool nsterm)
{
- unibi_term *ut = data->ut;
+ unibi_term *ut = tui->ut;
bool xterm = terminfo_is_term_family(term, "xterm")
// Treat Terminal.app as generic xterm-like, for now.
|| nsterm;
@@ -2037,24 +2031,29 @@ static void augment_terminfo(TUIData *data, const char *term, long vte_version,
|| konsolev // per commentary in VT102Emulation.cpp
|| teraterm // per TeraTerm "Supported Control Functions" doco
|| rxvt) { // per command.C
- data->unibi_ext.resize_screen = (int)unibi_add_ext_str(ut,
- "ext.resize_screen",
- "\x1b[8;%p1%d;%p2%dt");
+ tui->unibi_ext.resize_screen = (int)unibi_add_ext_str(ut,
+ "ext.resize_screen",
+ "\x1b[8;%p1%d;%p2%dt");
}
if (putty || xterm || hterm || rxvt) {
- data->unibi_ext.reset_scroll_region = (int)unibi_add_ext_str(ut,
- "ext.reset_scroll_region",
- "\x1b[r");
+ tui->unibi_ext.reset_scroll_region = (int)unibi_add_ext_str(ut,
+ "ext.reset_scroll_region",
+ "\x1b[r");
}
// terminfo describes strikethrough modes as rmxx/smxx with respect
// to the ECMA-48 strikeout/crossed-out attributes.
- data->unibi_ext.enter_strikethrough_mode = unibi_find_ext_str(ut, "smxx");
+ tui->unibi_ext.enter_strikethrough_mode = unibi_find_ext_str(ut, "smxx");
+
+ // It should be pretty safe to always enable this, as terminals will ignore
+ // unrecognised SGR numbers.
+ tui->unibi_ext.enter_altfont_mode = (int)unibi_add_ext_str(ut, "ext.enter_altfont_mode",
+ "\x1b[11m");
// Dickey ncurses terminfo does not include the setrgbf and setrgbb
// capabilities, proposed by Rüdiger Sonderfeld on 2013-10-15. Adding
// them here when terminfo lacks them is an augmentation, not a fixup.
- // https://gist.github.com/XVilka/8346728
+ // https://github.com/termstandard/colors
// At this time (2017-07-12) it seems like all terminals that support rgb
// color codes can use semicolons in the terminal code and be fine.
@@ -2069,106 +2068,119 @@ static void augment_terminfo(TUIData *data, const char *term, long vte_version,
// per http://invisible-island.net/xterm/xterm.log.html#xterm_282
|| true_xterm);
- data->unibi_ext.set_rgb_foreground = unibi_find_ext_str(ut, "setrgbf");
- if (-1 == data->unibi_ext.set_rgb_foreground) {
+ tui->unibi_ext.set_rgb_foreground = unibi_find_ext_str(ut, "setrgbf");
+ if (-1 == tui->unibi_ext.set_rgb_foreground) {
if (has_colon_rgb) {
- data->unibi_ext.set_rgb_foreground = (int)unibi_add_ext_str(ut, "setrgbf",
- "\x1b[38:2:%p1%d:%p2%d:%p3%dm");
+ tui->unibi_ext.set_rgb_foreground = (int)unibi_add_ext_str(ut, "setrgbf",
+ "\x1b[38:2:%p1%d:%p2%d:%p3%dm");
} else {
- data->unibi_ext.set_rgb_foreground = (int)unibi_add_ext_str(ut, "setrgbf",
- "\x1b[38;2;%p1%d;%p2%d;%p3%dm");
+ tui->unibi_ext.set_rgb_foreground = (int)unibi_add_ext_str(ut, "setrgbf",
+ "\x1b[38;2;%p1%d;%p2%d;%p3%dm");
}
}
- data->unibi_ext.set_rgb_background = unibi_find_ext_str(ut, "setrgbb");
- if (-1 == data->unibi_ext.set_rgb_background) {
+ tui->unibi_ext.set_rgb_background = unibi_find_ext_str(ut, "setrgbb");
+ if (-1 == tui->unibi_ext.set_rgb_background) {
if (has_colon_rgb) {
- data->unibi_ext.set_rgb_background = (int)unibi_add_ext_str(ut, "setrgbb",
- "\x1b[48:2:%p1%d:%p2%d:%p3%dm");
+ tui->unibi_ext.set_rgb_background = (int)unibi_add_ext_str(ut, "setrgbb",
+ "\x1b[48:2:%p1%d:%p2%d:%p3%dm");
} else {
- data->unibi_ext.set_rgb_background = (int)unibi_add_ext_str(ut, "setrgbb",
- "\x1b[48;2;%p1%d;%p2%d;%p3%dm");
+ tui->unibi_ext.set_rgb_background = (int)unibi_add_ext_str(ut, "setrgbb",
+ "\x1b[48;2;%p1%d;%p2%d;%p3%dm");
}
}
- data->unibi_ext.set_cursor_color = unibi_find_ext_str(ut, "Cs");
- if (-1 == data->unibi_ext.set_cursor_color) {
+ tui->unibi_ext.set_cursor_color = unibi_find_ext_str(ut, "Cs");
+ if (-1 == tui->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 =
+ tui->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");
+ tui->unibi_ext.set_cursor_color = (int)unibi_add_ext_str(ut, "ext.set_cursor_color",
+ "\033]12;%p1%s\007");
}
}
- if (-1 != data->unibi_ext.set_cursor_color) {
- 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");
+ if (-1 != tui->unibi_ext.set_cursor_color) {
+ // Some terminals supporting cursor color changing specify their Cs
+ // capability to take a string parameter. Others take a numeric parameter.
+ // If and only if the format string contains `%s` we assume a string
+ // parameter. #20628
+ const char *set_cursor_color =
+ unibi_get_ext_str(ut, (unsigned)tui->unibi_ext.set_cursor_color);
+ if (set_cursor_color) {
+ tui->set_cursor_color_as_str = strstr(set_cursor_color, "%s") != NULL;
+ }
+
+ tui->unibi_ext.reset_cursor_color = unibi_find_ext_str(ut, "Cr");
+ if (-1 == tui->unibi_ext.reset_cursor_color) {
+ tui->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");
- data->unibi_ext.restore_title = (int)unibi_add_ext_str(ut, "ext.restore_title", "\x1b[23;0t");
+ tui->unibi_ext.save_title = (int)unibi_add_ext_str(ut, "ext.save_title", "\x1b[22;0t");
+ tui->unibi_ext.restore_title = (int)unibi_add_ext_str(ut, "ext.restore_title", "\x1b[23;0t");
/// Terminals usually ignore unrecognized private modes, and there is no
/// known ambiguity with these. So we just set them unconditionally.
- data->unibi_ext.enable_lr_margin =
+ tui->unibi_ext.enable_lr_margin =
(int)unibi_add_ext_str(ut, "ext.enable_lr_margin", "\x1b[?69h");
- data->unibi_ext.disable_lr_margin = (int)unibi_add_ext_str(ut, "ext.disable_lr_margin",
- "\x1b[?69l");
- data->unibi_ext.enable_bracketed_paste = (int)unibi_add_ext_str(ut, "ext.enable_bpaste",
- "\x1b[?2004h");
- data->unibi_ext.disable_bracketed_paste = (int)unibi_add_ext_str(ut, "ext.disable_bpaste",
- "\x1b[?2004l");
+ tui->unibi_ext.disable_lr_margin = (int)unibi_add_ext_str(ut, "ext.disable_lr_margin",
+ "\x1b[?69l");
+ tui->unibi_ext.enable_bracketed_paste = (int)unibi_add_ext_str(ut, "ext.enable_bpaste",
+ "\x1b[?2004h");
+ tui->unibi_ext.disable_bracketed_paste = (int)unibi_add_ext_str(ut, "ext.disable_bpaste",
+ "\x1b[?2004l");
// For urxvt send BOTH xterm and old urxvt sequences. #8695
- data->unibi_ext.enable_focus_reporting = (int)unibi_add_ext_str(ut, "ext.enable_focus",
- rxvt ? "\x1b[?1004h\x1b]777;focus;on\x7" : "\x1b[?1004h");
- data->unibi_ext.disable_focus_reporting = (int)unibi_add_ext_str(ut, "ext.disable_focus",
- rxvt ? "\x1b[?1004l\x1b]777;focus;off\x7" : "\x1b[?1004l");
- data->unibi_ext.enable_mouse = (int)unibi_add_ext_str(ut, "ext.enable_mouse",
- "\x1b[?1002h\x1b[?1006h");
- data->unibi_ext.disable_mouse = (int)unibi_add_ext_str(ut, "ext.disable_mouse",
- "\x1b[?1002l\x1b[?1006l");
+ tui->unibi_ext.enable_focus_reporting = (int)unibi_add_ext_str(ut, "ext.enable_focus",
+ rxvt ? "\x1b[?1004h\x1b]777;focus;on\x7" : "\x1b[?1004h");
+ tui->unibi_ext.disable_focus_reporting = (int)unibi_add_ext_str(ut, "ext.disable_focus",
+ rxvt ? "\x1b[?1004l\x1b]777;focus;off\x7" : "\x1b[?1004l");
+ tui->unibi_ext.enable_mouse = (int)unibi_add_ext_str(ut, "ext.enable_mouse",
+ "\x1b[?1002h\x1b[?1006h");
+ tui->unibi_ext.disable_mouse = (int)unibi_add_ext_str(ut, "ext.disable_mouse",
+ "\x1b[?1002l\x1b[?1006l");
+ tui->unibi_ext.enable_mouse_move = (int)unibi_add_ext_str(ut, "ext.enable_mouse_move",
+ "\x1b[?1003h");
+ tui->unibi_ext.disable_mouse_move = (int)unibi_add_ext_str(ut, "ext.disable_mouse_move",
+ "\x1b[?1003l");
// Extended underline.
// terminfo will have Smulx for this (but no support for colors yet).
- data->unibi_ext.set_underline_style = unibi_find_ext_str(ut, "Smulx");
- if (data->unibi_ext.set_underline_style == -1) {
+ tui->unibi_ext.set_underline_style = unibi_find_ext_str(ut, "Smulx");
+ if (tui->unibi_ext.set_underline_style == -1) {
int ext_bool_Su = unibi_find_ext_bool(ut, "Su"); // used by kitty
- if (vte_version >= 5102
+ if (vte_version >= 5102 || konsolev >= 221170
|| (ext_bool_Su != -1
&& unibi_get_ext_bool(ut, (size_t)ext_bool_Su))) {
- data->unibi_ext.set_underline_style = (int)unibi_add_ext_str(ut, "ext.set_underline_style",
- "\x1b[4:%p1%dm");
+ tui->unibi_ext.set_underline_style = (int)unibi_add_ext_str(ut, "ext.set_underline_style",
+ "\x1b[4:%p1%dm");
}
}
- if (data->unibi_ext.set_underline_style != -1) {
+ if (tui->unibi_ext.set_underline_style != -1) {
// Only support colon syntax. #9270
- 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");
+ tui->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;
+ tui->input.extkeys_type = kExtkeysXterm;
}
}
-static void flush_buf(UI *ui)
+static void flush_buf(TUIData *tui)
{
uv_write_t req;
uv_buf_t bufs[3];
uv_buf_t *bufp = &bufs[0];
- TUIData *data = ui->data;
// The content of the output for each condition is shown in the following
- // table. Therefore, if data->bufpos == 0 and N/A or invis + norm, there is
+ // table. Therefore, if tui->bufpos == 0 and N/A or invis + norm, there is
// no need to output it.
//
// | is_invisible | !is_invisible
@@ -2180,73 +2192,72 @@ static void flush_buf(UI *ui)
// | !want_invisible | norm | invis + norm
// ------+-----------------+--------------+---------------
//
- if (data->bufpos <= 0
- && ((data->is_invisible && data->busy)
- || (data->is_invisible && !data->busy && data->want_invisible)
- || (!data->is_invisible && !data->busy && !data->want_invisible))) {
+ if (tui->bufpos <= 0
+ && ((tui->is_invisible && tui->busy)
+ || (tui->is_invisible && !tui->busy && tui->want_invisible)
+ || (!tui->is_invisible && !tui->busy && !tui->want_invisible))) {
return;
}
- if (!data->is_invisible) {
+ if (!tui->is_invisible) {
// cursor is visible. Write a "cursor invisible" command before writing the
// buffer.
- bufp->base = data->invis;
- bufp->len = UV_BUF_LEN(data->invislen);
+ bufp->base = tui->invis;
+ bufp->len = UV_BUF_LEN(tui->invislen);
bufp++;
- data->is_invisible = true;
+ tui->is_invisible = true;
}
- if (data->bufpos > 0) {
- bufp->base = data->buf;
- bufp->len = UV_BUF_LEN(data->bufpos);
+ if (tui->bufpos > 0) {
+ bufp->base = tui->buf;
+ bufp->len = UV_BUF_LEN(tui->bufpos);
bufp++;
}
- if (!data->busy) {
- assert(data->is_invisible);
+ if (!tui->busy) {
+ assert(tui->is_invisible);
// not busy and the cursor is invisible. Write a "cursor normal" command
// after writing the buffer.
- if (!data->want_invisible) {
- bufp->base = data->norm;
- bufp->len = UV_BUF_LEN(data->normlen);
+ if (!tui->want_invisible) {
+ bufp->base = tui->norm;
+ bufp->len = UV_BUF_LEN(tui->normlen);
bufp++;
- data->is_invisible = false;
+ tui->is_invisible = false;
}
}
- if (data->screenshot) {
+ if (tui->screenshot) {
for (size_t i = 0; i < (size_t)(bufp - bufs); i++) {
- fwrite(bufs[i].base, bufs[i].len, 1, data->screenshot);
+ fwrite(bufs[i].base, bufs[i].len, 1, tui->screenshot);
}
} else {
- int ret = uv_write(&req, STRUCT_CAST(uv_stream_t, &data->output_handle),
+ int ret = uv_write(&req, STRUCT_CAST(uv_stream_t, &tui->output_handle),
bufs, (unsigned)(bufp - bufs), NULL);
if (ret) {
ELOG("uv_write failed: %s", uv_strerror(ret));
}
- uv_run(&data->write_loop, UV_RUN_DEFAULT);
+ uv_run(&tui->write_loop, UV_RUN_DEFAULT);
}
- data->bufpos = 0;
- data->overflow = false;
+ tui->bufpos = 0;
+ tui->overflow = false;
}
-#if TERMKEY_VERSION_MAJOR > 0 || TERMKEY_VERSION_MINOR > 18
/// Try to get "kbs" code from stty because "the terminfo kbs entry is extremely
/// unreliable." (Vim, Bash, and tmux also do this.)
///
/// @see tmux/tty-keys.c fe4e9470bb504357d073320f5d305b22663ee3fd
/// @see https://bugzilla.redhat.com/show_bug.cgi?id=142659
-static const char *tui_get_stty_erase(void)
+static const char *tui_get_stty_erase(int fd)
{
static char stty_erase[2] = { 0 };
-# if defined(HAVE_TERMIOS_H)
+#if defined(HAVE_TERMIOS_H)
struct termios t;
- if (tcgetattr(input_global_fd(), &t) != -1) {
+ if (tcgetattr(fd, &t) != -1) {
stty_erase[0] = (char)t.c_cc[VERASE];
stty_erase[1] = '\0';
DLOG("stty/termios:erase=%s", stty_erase);
}
-# endif
+#endif
return stty_erase;
}
@@ -2254,9 +2265,10 @@ static const char *tui_get_stty_erase(void)
/// @see TermInput.tk_ti_hook_fn
static const char *tui_tk_ti_getstr(const char *name, const char *value, void *data)
{
+ TermInput *input = data;
static const char *stty_erase = NULL;
if (stty_erase == NULL) {
- stty_erase = tui_get_stty_erase();
+ stty_erase = tui_get_stty_erase(input->in_fd);
}
if (strequal(name, "key_backspace")) {
@@ -2280,4 +2292,3 @@ static const char *tui_tk_ti_getstr(const char *name, const char *value, void *d
return value;
}
-#endif
diff --git a/src/nvim/types.h b/src/nvim/types.h
index 477102276c..d90c623955 100644
--- a/src/nvim/types.h
+++ b/src/nvim/types.h
@@ -22,7 +22,16 @@ typedef int handle_T;
// absent callback etc.
typedef int LuaRef;
-typedef void (*FunPtr)(void);
+/// Type used for VimL VAR_FLOAT values
+typedef double float_T;
+
+typedef struct MsgpackRpcRequestHandler MsgpackRpcRequestHandler;
+
+typedef union {
+ float_T (*float_func)(float_T);
+ const MsgpackRpcRequestHandler *api_handler;
+ void *nullptr;
+} EvalFuncData;
typedef handle_T NS;
@@ -36,6 +45,9 @@ typedef enum {
kTrue = 1,
} TriState;
+#define TRISTATE_TO_BOOL(val, \
+ default) ((val) == kTrue ? true : ((val) == kFalse ? false : (default)))
+
typedef struct Decoration Decoration;
#endif // NVIM_TYPES_H
diff --git a/src/nvim/ugrid.c b/src/nvim/ugrid.c
index d96da3f2bb..be1746983c 100644
--- a/src/nvim/ugrid.c
+++ b/src/nvim/ugrid.c
@@ -2,14 +2,10 @@
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include <assert.h>
-#include <limits.h>
-#include <stdbool.h>
-#include <stdio.h>
+#include <string.h>
-#include "nvim/assert.h"
+#include "nvim/memory.h"
#include "nvim/ugrid.h"
-#include "nvim/ui.h"
-#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ugrid.c.generated.h"
diff --git a/src/nvim/ugrid.h b/src/nvim/ugrid.h
index ae11153c61..a85a6fb4a8 100644
--- a/src/nvim/ugrid.h
+++ b/src/nvim/ugrid.h
@@ -2,8 +2,12 @@
#define NVIM_UGRID_H
#include "nvim/globals.h"
+#include "nvim/grid_defs.h"
#include "nvim/ui.h"
+struct ucell;
+struct ugrid;
+
typedef struct ucell UCell;
typedef struct ugrid UGrid;
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index da671a3ad1..9f1cb87eb0 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -2,47 +2,40 @@
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include <assert.h>
-#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
#include <string.h>
+#include "klib/kvec.h"
+#include "nvim/api/private/helpers.h"
+#include "nvim/api/ui.h"
#include "nvim/ascii.h"
#include "nvim/autocmd.h"
-#include "nvim/charset.h"
-#include "nvim/cursor.h"
+#include "nvim/buffer_defs.h"
#include "nvim/cursor_shape.h"
-#include "nvim/diff.h"
#include "nvim/drawscreen.h"
-#include "nvim/event/loop.h"
#include "nvim/ex_getln.h"
-#include "nvim/fold.h"
-#include "nvim/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
#include "nvim/grid.h"
#include "nvim/highlight.h"
+#include "nvim/highlight_defs.h"
#include "nvim/log.h"
-#include "nvim/main.h"
-#include "nvim/mbyte.h"
+#include "nvim/lua/executor.h"
+#include "nvim/map.h"
#include "nvim/memory.h"
-#include "nvim/move.h"
-#include "nvim/msgpack_rpc/channel.h"
-#include "nvim/normal.h"
+#include "nvim/message.h"
#include "nvim/option.h"
-#include "nvim/os/input.h"
-#include "nvim/os/signal.h"
#include "nvim/os/time.h"
-#include "nvim/os_unix.h"
-#include "nvim/popupmenu.h"
+#include "nvim/strings.h"
#include "nvim/ui.h"
+#include "nvim/ui_client.h"
#include "nvim/ui_compositor.h"
#include "nvim/vim.h"
#include "nvim/window.h"
-#ifdef FEAT_TUI
-# include "nvim/tui/tui.h"
-#else
-# include "nvim/msgpack_rpc/server.h"
-#endif
-#include "nvim/api/private/helpers.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ui.c.generated.h"
@@ -61,6 +54,9 @@ static bool pending_mode_info_update = false;
static bool pending_mode_update = false;
static handle_T cursor_grid_handle = DEFAULT_GRID_HANDLE;
+static PMap(uint32_t) ui_event_cbs = MAP_INIT;
+bool ui_cb_ext[kUIExtCount]; ///< Internalized UI capabilities.
+
static bool has_mouse = false;
static int pending_has_mouse = -1;
@@ -102,8 +98,8 @@ static char uilog_last_event[1024] = { 0 };
bool any_call = false; \
for (size_t i = 0; i < ui_count; i++) { \
UI *ui = uis[i]; \
- if (ui->funname && (cond)) { \
- ui->funname(__VA_ARGS__); \
+ if ((cond)) { \
+ remote_ui_##funname(__VA_ARGS__); \
any_call = true; \
} \
} \
@@ -116,10 +112,6 @@ static char uilog_last_event[1024] = { 0 };
# include "ui_events_call.generated.h"
#endif
-#ifndef EXITFREE
-# undef entered_free_all_mem
-#endif
-
void ui_init(void)
{
default_grid.handle = 1;
@@ -128,36 +120,25 @@ void ui_init(void)
kv_ensure_space(call_buf, 16);
}
+#ifdef EXITFREE
void ui_free_all_mem(void)
{
kv_destroy(call_buf);
-}
-void ui_builtin_start(void)
-{
-#ifdef FEAT_TUI
- tui_start();
-#else
- fprintf(stderr, "Nvim headless-mode started.\n");
- size_t len;
- char **addrs = server_address_list(&len);
- if (addrs != NULL) {
- fprintf(stderr, "Listening on:\n");
- for (size_t i = 0; i < len; i++) {
- fprintf(stderr, "\t%s\n", addrs[i]);
- }
- xfree(addrs);
- }
- fprintf(stderr, "Press CTRL+C to exit.\n");
-#endif
+ UIEventCallback *event_cb;
+ map_foreach_value(&ui_event_cbs, event_cb, {
+ free_ui_event_callback(event_cb);
+ })
+ pmap_destroy(uint32_t)(&ui_event_cbs);
}
+#endif
bool ui_rgb_attached(void)
{
if (!headless_mode && p_tgc) {
return true;
}
- for (size_t i = 1; i < ui_count; i++) {
+ for (size_t i = 0; i < ui_count; i++) {
if (uis[i]->rgb) {
return true;
}
@@ -168,7 +149,7 @@ bool ui_rgb_attached(void)
/// Returns true if any UI requested `override=true`.
bool ui_override(void)
{
- for (size_t i = 1; i < ui_count; i++) {
+ for (size_t i = 0; i < ui_count; i++) {
if (uis[i]->override) {
return true;
}
@@ -178,17 +159,21 @@ bool ui_override(void)
bool ui_active(void)
{
- return ui_count > 1;
+ return ui_count > 0;
}
void ui_refresh(void)
{
+ if (ui_client_channel_id) {
+ abort();
+ }
+
if (!ui_active()) {
return;
}
if (updating_screen) {
- deferred_refresh_event(NULL);
+ ui_schedule_refresh();
return;
}
@@ -212,6 +197,9 @@ void ui_refresh(void)
pending_cursor_update = true;
for (UIExtension i = 0; (int)i < kUIExtCount; i++) {
+ if (i < kUIGlobalCount) {
+ ext_widgets[i] |= ui_cb_ext[i];
+ }
ui_ext[i] = ext_widgets[i];
if (i < kUIGlobalCount) {
ui_call_option_set(cstr_as_string((char *)ui_ext_names[i]),
@@ -221,20 +209,13 @@ void ui_refresh(void)
ui_default_colors_set();
- 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);
- }
+ int save_p_lz = p_lz;
+ p_lz = false; // convince redrawing() to return true ...
+ screen_resize(width, height);
+ p_lz = save_p_lz;
if (ext_widgets[kUIMessages]) {
- p_ch = 0;
+ set_option_value("cmdheight", 0L, NULL, 0);
command_height();
}
ui_mode_info_set();
@@ -246,7 +227,7 @@ void ui_refresh(void)
int ui_pum_get_height(void)
{
int pum_height = 0;
- for (size_t i = 1; i < ui_count; i++) {
+ for (size_t i = 0; i < ui_count; i++) {
int ui_pum_height = uis[i]->pum_nlines;
if (ui_pum_height) {
pum_height =
@@ -258,7 +239,7 @@ int ui_pum_get_height(void)
bool ui_pum_get_pos(double *pwidth, double *pheight, double *prow, double *pcol)
{
- for (size_t i = 1; i < ui_count; i++) {
+ for (size_t i = 0; i < ui_count; i++) {
if (!uis[i]->pum_pos) {
continue;
}
@@ -278,10 +259,6 @@ static void ui_refresh_event(void **argv)
void ui_schedule_refresh(void)
{
- loop_schedule_fast(&main_loop, event_create(deferred_refresh_event, 0));
-}
-static void deferred_refresh_event(void **argv)
-{
multiqueue_put(resize_events, ui_refresh_event, 0);
}
@@ -312,34 +289,36 @@ void vim_beep(unsigned val)
{
called_vim_beep = true;
- if (emsg_silent == 0) {
- if (!((bo_flags & val) || (bo_flags & BO_ALL))) {
- static int beeps = 0;
- static uint64_t start_time = 0;
+ if (emsg_silent != 0 || in_assert_fails) {
+ return;
+ }
- // Only beep up to three times per half a second,
- // otherwise a sequence of beeps would freeze Vim.
- if (start_time == 0 || os_hrtime() - start_time > 500000000u) {
- beeps = 0;
- start_time = os_hrtime();
- }
- beeps++;
- if (beeps <= 3) {
- if (p_vb) {
- ui_call_visual_bell();
- } else {
- ui_call_bell();
- }
+ if (!((bo_flags & val) || (bo_flags & BO_ALL))) {
+ static int beeps = 0;
+ static uint64_t start_time = 0;
+
+ // Only beep up to three times per half a second,
+ // otherwise a sequence of beeps would freeze Vim.
+ if (start_time == 0 || os_hrtime() - start_time > 500000000U) {
+ beeps = 0;
+ start_time = os_hrtime();
+ }
+ beeps++;
+ if (beeps <= 3) {
+ if (p_vb) {
+ ui_call_visual_bell();
+ } else {
+ ui_call_bell();
}
}
+ }
- // When 'debug' contains "beep" produce a message. If we are sourcing
- // a script or executing a function give the user a hint where the beep
- // comes from.
- if (vim_strchr((char *)p_debug, 'e') != NULL) {
- msg_source(HL_ATTR(HLF_W));
- msg_attr(_("Beep!"), HL_ATTR(HLF_W));
- }
+ // When 'debug' contains "beep" produce a message. If we are sourcing
+ // a script or executing a function give the user a hint where the beep
+ // comes from.
+ if (vim_strchr(p_debug, 'e') != NULL) {
+ msg_source(HL_ATTR(HLF_W));
+ msg_attr(_("Beep!"), HL_ATTR(HLF_W));
}
}
@@ -369,10 +348,7 @@ void ui_attach_impl(UI *ui, uint64_t chanid)
}
ui_refresh();
- bool is_compositor = (ui == uis[0]);
- if (!is_compositor) {
- do_autocmd_uienter(chanid, true);
- }
+ do_autocmd_uienter(chanid, true);
}
void ui_detach_impl(UI *ui, uint64_t chanid)
@@ -417,9 +393,9 @@ void ui_set_ext_option(UI *ui, UIExtension ext, bool active)
ui_refresh();
return;
}
- if (ui->option_set && (ui_ext_names[ext][0] != '_' || active)) {
- ui->option_set(ui, cstr_as_string((char *)ui_ext_names[ext]),
- BOOLEAN_OBJ(active));
+ if (ui_ext_names[ext][0] != '_' || active) {
+ remote_ui_option_set(ui, cstr_as_string((char *)ui_ext_names[ext]),
+ BOOLEAN_OBJ(active));
}
if (ext == kUITermColors) {
ui_default_colors_set();
@@ -449,7 +425,7 @@ void ui_line(ScreenGrid *grid, int row, int startcol, int endcol, int clearcol,
MIN(clearcol, (int)grid->cols - 1));
ui_call_flush();
uint64_t wd = (uint64_t)labs(p_wd);
- os_microdelay(wd * 1000u, true);
+ os_microdelay(wd * 1000U, true);
pending_cursor_update = true; // restore the cursor later
}
}
@@ -503,11 +479,12 @@ handle_T ui_cursor_grid(void)
void ui_flush(void)
{
+ assert(!ui_client_channel_id);
if (!ui_active()) {
return;
}
cmdline_ui_flush();
- win_ui_flush();
+ win_ui_flush(false);
msg_ext_ui_flush();
msg_scroll_flush();
@@ -517,11 +494,10 @@ void ui_flush(void)
}
if (pending_mode_info_update) {
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);
- arena_mem_free(arena_finish(&arena), &ui_ext_fixblk);
+ arena_mem_free(arena_finish(&arena));
pending_mode_info_update = false;
}
if (pending_mode_update && !starting) {
@@ -566,7 +542,7 @@ void ui_check_mouse(void)
// - 'a' is in 'mouse' and "c" is in MOUSE_A, or
// - the current buffer is a help file and 'h' is in 'mouse' and we are in a
// normal editing mode (not at hit-return message).
- for (char_u *p = p_mouse; *p; p++) {
+ for (char *p = p_mouse; *p; p++) {
switch (*p) {
case 'a':
if (vim_strchr(MOUSE_A, checkfor) != NULL) {
@@ -612,16 +588,10 @@ bool ui_has(UIExtension ext)
return ui_ext[ext];
}
-/// Returns true if the UI has messages area.
-bool ui_has_messages(void)
-{
- return p_ch > 0 || ui_has(kUIMessages);
-}
-
Array ui_array(void)
{
Array all_uis = ARRAY_DICT_INIT;
- for (size_t i = 1; i < ui_count; i++) {
+ for (size_t i = 0; i < ui_count; i++) {
UI *ui = uis[i];
Dictionary info = ARRAY_DICT_INIT;
PUT(info, "width", INTEGER_OBJ(ui->width));
@@ -633,7 +603,7 @@ Array ui_array(void)
PUT(info, ui_ext_names[j], BOOLEAN_OBJ(ui->ui_ext[j]));
}
}
- ui->inspect(ui, &info);
+ remote_ui_inspect(ui, &info);
ADD(all_uis, DICTIONARY_OBJ(info));
}
return all_uis;
@@ -666,3 +636,75 @@ void ui_grid_resize(handle_T grid_handle, int width, int height, Error *error)
win_set_inner_size(wp, true);
}
}
+
+void ui_call_event(char *name, Array args)
+{
+ UIEventCallback *event_cb;
+ bool handled = false;
+ map_foreach_value(&ui_event_cbs, event_cb, {
+ Error err = ERROR_INIT;
+ Object res = nlua_call_ref(event_cb->cb, name, args, false, &err);
+ if (res.type == kObjectTypeBoolean && res.data.boolean == true) {
+ handled = true;
+ }
+ if (ERROR_SET(&err)) {
+ ELOG("Error while executing ui_comp_event callback: %s", err.msg);
+ }
+ api_clear_error(&err);
+ })
+
+ if (!handled) {
+ UI_CALL(true, event, ui, name, args);
+ }
+}
+
+void ui_cb_update_ext(void)
+{
+ memset(ui_cb_ext, 0, ARRAY_SIZE(ui_cb_ext));
+
+ for (size_t i = 0; i < kUIGlobalCount; i++) {
+ UIEventCallback *event_cb;
+
+ map_foreach_value(&ui_event_cbs, event_cb, {
+ if (event_cb->ext_widgets[i]) {
+ ui_cb_ext[i] = true;
+ break;
+ }
+ })
+ }
+}
+
+void free_ui_event_callback(UIEventCallback *event_cb)
+{
+ api_free_luaref(event_cb->cb);
+ xfree(event_cb);
+}
+
+void ui_add_cb(uint32_t ns_id, LuaRef cb, bool *ext_widgets)
+{
+ UIEventCallback *event_cb = xcalloc(1, sizeof(UIEventCallback));
+ event_cb->cb = cb;
+ memcpy(event_cb->ext_widgets, ext_widgets, ARRAY_SIZE(event_cb->ext_widgets));
+ if (event_cb->ext_widgets[kUIMessages]) {
+ event_cb->ext_widgets[kUICmdline] = true;
+ }
+
+ UIEventCallback **item = (UIEventCallback **)pmap_ref(uint32_t)(&ui_event_cbs, ns_id, true);
+ if (*item) {
+ free_ui_event_callback(*item);
+ }
+ *item = event_cb;
+
+ ui_cb_update_ext();
+ ui_refresh();
+}
+
+void ui_remove_cb(uint32_t ns_id)
+{
+ if (pmap_has(uint32_t)(&ui_event_cbs, ns_id)) {
+ free_ui_event_callback(pmap_get(uint32_t)(&ui_event_cbs, ns_id));
+ pmap_del(uint32_t)(&ui_event_cbs, ns_id);
+ }
+ ui_cb_update_ext();
+ ui_refresh();
+}
diff --git a/src/nvim/ui.h b/src/nvim/ui.h
index 7dd2f5bce3..9140a9f1f3 100644
--- a/src/nvim/ui.h
+++ b/src/nvim/ui.h
@@ -6,9 +6,14 @@
#include <stdint.h>
#include "nvim/api/private/defs.h"
+#include "nvim/event/multiqueue.h"
#include "nvim/globals.h"
#include "nvim/highlight_defs.h"
+#include "nvim/macros.h"
#include "nvim/memory.h"
+#include "nvim/types.h"
+
+struct ui_t;
typedef enum {
kUICmdline = 0,
@@ -47,7 +52,40 @@ enum {
typedef int LineFlags;
-EXTERN ArenaMem ui_ext_fixblk INIT(= NULL);
+typedef struct {
+ uint64_t channel_id;
+
+#define UI_BUF_SIZE 4096 ///< total buffer size for pending msgpack data.
+ /// guaranteed 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 specific 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.
+
+ // Position of legacy cursor, used both for drawing and visible user cursor.
+ Integer client_row, client_col;
+ bool wildmenu_active;
+} UIData;
struct ui_t {
bool rgb;
@@ -62,20 +100,22 @@ struct ui_t {
double pum_col;
double pum_height;
double pum_width;
- void *data;
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "ui_events.generated.h"
-#endif
-
- void (*inspect)(UI *ui, Dictionary *info);
+ // TODO(bfredl): integrate into struct!
+ UIData data[1];
};
+typedef struct ui_event_callback {
+ LuaRef cb;
+ bool ext_widgets[kUIGlobalCount];
+} UIEventCallback;
+
+// uncrustify:off
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ui.h.generated.h"
-
# include "ui_events_call.h.generated.h"
#endif
+// uncrustify:on
EXTERN MultiQueue *resize_events;
#endif // NVIM_UI_H
diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c
deleted file mode 100644
index 84098e9476..0000000000
--- a/src/nvim/ui_bridge.c
+++ /dev/null
@@ -1,223 +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
-
-// UI wrapper that sends requests to the UI thread.
-// Used by the built-in TUI and libnvim-based UIs.
-
-#include <assert.h>
-#include <limits.h>
-#include <stdbool.h>
-#include <stdio.h>
-
-#include "nvim/api/private/helpers.h"
-#include "nvim/log.h"
-#include "nvim/main.h"
-#include "nvim/memory.h"
-#include "nvim/ugrid.h"
-#include "nvim/ui.h"
-#include "nvim/ui_bridge.h"
-#include "nvim/vim.h"
-
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "ui_bridge.c.generated.h"
-#endif
-
-#define UI(b) (((UIBridgeData *)b)->ui)
-
-// Schedule a function call on the UI bridge thread.
-#define UI_BRIDGE_CALL(ui, name, argc, ...) \
- ((UIBridgeData *)ui)->scheduler(event_create(ui_bridge_##name##_event, argc, __VA_ARGS__), UI(ui))
-
-#define INT2PTR(i) ((void *)(intptr_t)i)
-#define PTR2INT(p) ((Integer)(intptr_t)p)
-
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "ui_events_bridge.generated.h"
-#endif
-
-UI *ui_bridge_attach(UI *ui, ui_main_fn ui_main, event_scheduler scheduler)
-{
- UIBridgeData *rv = xcalloc(1, sizeof(UIBridgeData));
- rv->ui = ui;
- rv->bridge.rgb = ui->rgb;
- rv->bridge.width = ui->width;
- rv->bridge.height = ui->height;
- rv->bridge.stop = ui_bridge_stop;
- rv->bridge.grid_resize = ui_bridge_grid_resize;
- rv->bridge.grid_clear = ui_bridge_grid_clear;
- rv->bridge.grid_cursor_goto = ui_bridge_grid_cursor_goto;
- rv->bridge.mode_info_set = ui_bridge_mode_info_set;
- rv->bridge.update_menu = ui_bridge_update_menu;
- rv->bridge.busy_start = ui_bridge_busy_start;
- rv->bridge.busy_stop = ui_bridge_busy_stop;
- rv->bridge.mouse_on = ui_bridge_mouse_on;
- rv->bridge.mouse_off = ui_bridge_mouse_off;
- rv->bridge.mode_change = ui_bridge_mode_change;
- rv->bridge.grid_scroll = ui_bridge_grid_scroll;
- rv->bridge.hl_attr_define = ui_bridge_hl_attr_define;
- rv->bridge.bell = ui_bridge_bell;
- rv->bridge.visual_bell = ui_bridge_visual_bell;
- rv->bridge.default_colors_set = ui_bridge_default_colors_set;
- rv->bridge.flush = ui_bridge_flush;
- rv->bridge.suspend = ui_bridge_suspend;
- rv->bridge.set_title = ui_bridge_set_title;
- rv->bridge.set_icon = ui_bridge_set_icon;
- rv->bridge.screenshot = ui_bridge_screenshot;
- rv->bridge.option_set = ui_bridge_option_set;
- rv->bridge.raw_line = ui_bridge_raw_line;
- rv->bridge.inspect = ui_bridge_inspect;
- rv->scheduler = scheduler;
-
- for (UIExtension i = 0; (int)i < kUIExtCount; i++) {
- rv->bridge.ui_ext[i] = ui->ui_ext[i];
- }
-
- rv->ui_main = ui_main;
- uv_mutex_init(&rv->mutex);
- uv_cond_init(&rv->cond);
- uv_mutex_lock(&rv->mutex);
- rv->ready = false;
-
- if (uv_thread_create(&rv->ui_thread, ui_thread_run, rv)) {
- abort();
- }
-
- // Suspend the main thread until CONTINUE is called by the UI thread.
- while (!rv->ready) {
- uv_cond_wait(&rv->cond, &rv->mutex);
- }
- uv_mutex_unlock(&rv->mutex);
-
- ui_attach_impl(&rv->bridge, 0);
-
- return &rv->bridge;
-}
-
-void ui_bridge_stopped(UIBridgeData *bridge)
-{
- uv_mutex_lock(&bridge->mutex);
- bridge->stopped = true;
- uv_mutex_unlock(&bridge->mutex);
-}
-
-static void ui_thread_run(void *data)
-{
- UIBridgeData *bridge = data;
- bridge->ui_main(bridge, bridge->ui);
-}
-
-static void ui_bridge_stop(UI *b)
-{
- // Detach bridge first, so that "stop" is the last event the TUI loop
- // receives from the main thread. #8041
- ui_detach_impl(b, 0);
-
- UIBridgeData *bridge = (UIBridgeData *)b;
- bool stopped = bridge->stopped = false;
- UI_BRIDGE_CALL(b, stop, 1, b);
- for (;;) {
- uv_mutex_lock(&bridge->mutex);
- stopped = bridge->stopped;
- uv_mutex_unlock(&bridge->mutex);
- if (stopped) { // -V547
- break;
- }
- // TODO(justinmk): Remove this. Use a cond-wait above. #9274
- loop_poll_events(&main_loop, 10); // Process one event.
- }
- uv_thread_join(&bridge->ui_thread);
- uv_mutex_destroy(&bridge->mutex);
- uv_cond_destroy(&bridge->cond);
- xfree(bridge->ui); // Threads joined, now safe to free UI container. #7922
- xfree(b);
-}
-static void ui_bridge_stop_event(void **argv)
-{
- UI *ui = UI(argv[0]);
- ui->stop(ui);
-}
-
-static void ui_bridge_hl_attr_define(UI *ui, Integer id, HlAttrs attrs, HlAttrs cterm_attrs,
- Array info)
-{
- HlAttrs *a = xmalloc(sizeof(HlAttrs));
- *a = attrs;
- UI_BRIDGE_CALL(ui, hl_attr_define, 3, ui, INT2PTR(id), a);
-}
-static void ui_bridge_hl_attr_define_event(void **argv)
-{
- UI *ui = UI(argv[0]);
- Array info = ARRAY_DICT_INIT;
- ui->hl_attr_define(ui, PTR2INT(argv[1]), *((HlAttrs *)argv[2]),
- *((HlAttrs *)argv[2]), info);
- xfree(argv[2]);
-}
-
-static void ui_bridge_raw_line_event(void **argv)
-{
- UI *ui = UI(argv[0]);
- ui->raw_line(ui, PTR2INT(argv[1]), PTR2INT(argv[2]), PTR2INT(argv[3]),
- PTR2INT(argv[4]), PTR2INT(argv[5]), PTR2INT(argv[6]),
- (LineFlags)PTR2INT(argv[7]), argv[8], argv[9]);
- xfree(argv[8]);
- xfree(argv[9]);
-}
-static void ui_bridge_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Integer endcol,
- Integer clearcol, Integer clearattr, LineFlags flags,
- const schar_T *chunk, const sattr_T *attrs)
-{
- 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),
- INT2PTR(startcol), INT2PTR(endcol), INT2PTR(clearcol),
- INT2PTR(clearattr), INT2PTR(flags), c, hl);
-}
-
-static void ui_bridge_suspend(UI *b)
-{
- UIBridgeData *data = (UIBridgeData *)b;
- uv_mutex_lock(&data->mutex);
- UI_BRIDGE_CALL(b, suspend, 1, b);
- data->ready = false;
- // Suspend the main thread until CONTINUE is called by the UI thread.
- while (!data->ready) {
- uv_cond_wait(&data->cond, &data->mutex);
- }
- uv_mutex_unlock(&data->mutex);
-}
-static void ui_bridge_suspend_event(void **argv)
-{
- UI *ui = UI(argv[0]);
- ui->suspend(ui);
-}
-
-static void ui_bridge_option_set(UI *ui, String name, Object value)
-{
- String copy_name = copy_string(name);
- Object *copy_value = xmalloc(sizeof(Object));
- *copy_value = copy_object(value);
- UI_BRIDGE_CALL(ui, option_set, 4, ui, copy_name.data,
- INT2PTR(copy_name.size), copy_value);
- // TODO(bfredl): when/if TUI/bridge teardown is refactored to use events, the
- // commit that introduced this special case can be reverted.
- // For now this is needed for nvim_list_uis().
- if (strequal(name.data, "termguicolors")) {
- ui->rgb = value.data.boolean;
- }
-}
-static void ui_bridge_option_set_event(void **argv)
-{
- UI *ui = UI(argv[0]);
- String name = (String){ .data = argv[1], .size = (size_t)argv[2] };
- Object value = *(Object *)argv[3];
- ui->option_set(ui, name, value);
- api_free_string(name);
- api_free_object(value);
- xfree(argv[3]);
-}
-
-static void ui_bridge_inspect(UI *ui, Dictionary *info)
-{
- PUT(*info, "chan", INTEGER_OBJ(0));
-}
diff --git a/src/nvim/ui_bridge.h b/src/nvim/ui_bridge.h
deleted file mode 100644
index c18600a857..0000000000
--- a/src/nvim/ui_bridge.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Bridge for communication between a UI thread and nvim core.
-// Used by the built-in TUI and libnvim-based UIs.
-#ifndef NVIM_UI_BRIDGE_H
-#define NVIM_UI_BRIDGE_H
-
-#include <uv.h>
-
-#include "nvim/event/defs.h"
-#include "nvim/ui.h"
-
-typedef struct ui_bridge_data UIBridgeData;
-typedef void (*ui_main_fn)(UIBridgeData *bridge, UI *ui);
-struct ui_bridge_data {
- UI bridge; // actual UI passed to ui_attach
- UI *ui; // UI pointer that will have its callback called in
- // another thread
- event_scheduler scheduler;
- uv_thread_t ui_thread;
- ui_main_fn ui_main;
- uv_mutex_t mutex;
- uv_cond_t cond;
- // When the UI thread is called, the main thread will suspend until
- // the call returns. This flag is used as a condition for the main
- // thread to continue.
- bool ready;
- // When a stop request is sent from the main thread, it must wait until the UI
- // thread finishes handling all events. This flag is set by the UI thread as a
- // signal that it will no longer send messages to the main thread.
- bool stopped;
-};
-
-#define CONTINUE(b) \
- do { \
- UIBridgeData *d = (UIBridgeData *)b; \
- uv_mutex_lock(&d->mutex); \
- d->ready = true; \
- uv_cond_signal(&d->cond); \
- uv_mutex_unlock(&d->mutex); \
- } while (0)
-
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "ui_bridge.h.generated.h"
-#endif
-#endif // NVIM_UI_BRIDGE_H
diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c
index a586fec3bf..378c0e4831 100644
--- a/src/nvim/ui_client.c
+++ b/src/nvim/ui_client.c
@@ -1,44 +1,126 @@
// 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 <stdlib.h>
-#include "nvim/api/private/dispatch.h"
#include "nvim/api/private/helpers.h"
+#include "nvim/channel.h"
+#include "nvim/eval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/event/loop.h"
+#include "nvim/globals.h"
#include "nvim/highlight.h"
#include "nvim/log.h"
-#include "nvim/map.h"
+#include "nvim/main.h"
+#include "nvim/memory.h"
#include "nvim/msgpack_rpc/channel.h"
-#include "nvim/screen.h"
+#include "nvim/msgpack_rpc/channel_defs.h"
+#include "nvim/os/os_defs.h"
+#include "nvim/tui/tui.h"
#include "nvim/ui.h"
#include "nvim/ui_client.h"
-#include "nvim/vim.h"
+static TUIData *tui = NULL;
+
+// uncrustify:off
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ui_client.c.generated.h"
-
# include "ui_events_client.generated.h"
#endif
+// uncrustify:on
+//
-void ui_client_init(uint64_t chan)
+uint64_t ui_client_start_server(int argc, char **argv)
{
- Array args = ARRAY_DICT_INIT;
- int width = Columns;
- int height = Rows;
- Dictionary opts = ARRAY_DICT_INIT;
+ varnumber_T exit_status;
+ char **args = xmalloc(((size_t)(2 + argc)) * sizeof(char *));
+ int args_idx = 0;
+ args[args_idx++] = xstrdup((const char *)get_vim_var_str(VV_PROGPATH));
+ args[args_idx++] = xstrdup("--embed");
+ for (int i = 1; i < argc; i++) {
+ args[args_idx++] = xstrdup(argv[i]);
+ }
+ args[args_idx++] = NULL;
+
+ CallbackReader on_err = CALLBACK_READER_INIT;
+ on_err.fwd_err = true;
+
+ Channel *channel = channel_job_start(args, CALLBACK_READER_INIT,
+ on_err, CALLBACK_NONE,
+ false, true, true, false, kChannelStdinPipe,
+ NULL, 0, 0, NULL, &exit_status);
+
+ // If stdin is not a pty, it is forwarded to the client.
+ // Replace stdin in the TUI process with the tty fd.
+ if (ui_client_forward_stdin) {
+ close(0);
+#ifdef MSWIN
+ os_open_conin_fd();
+#else
+ dup(stderr_isatty ? STDERR_FILENO : STDOUT_FILENO);
+#endif
+ }
- PUT(opts, "rgb", BOOLEAN_OBJ(true));
- PUT(opts, "ext_linegrid", BOOLEAN_OBJ(true));
- PUT(opts, "ext_termcolors", BOOLEAN_OBJ(true));
+ return channel->id;
+}
- ADD(args, INTEGER_OBJ((int)width));
- ADD(args, INTEGER_OBJ((int)height));
- ADD(args, DICTIONARY_OBJ(opts));
+void ui_client_run(bool remote_ui)
+ FUNC_ATTR_NORETURN
+{
+ int width, height;
+ char *term;
+ tui = tui_start(&width, &height, &term);
+
+ MAXSIZE_TEMP_ARRAY(args, 3);
+ ADD_C(args, INTEGER_OBJ(width));
+ ADD_C(args, INTEGER_OBJ(height));
+
+ MAXSIZE_TEMP_DICT(opts, 9);
+ PUT_C(opts, "rgb", BOOLEAN_OBJ(true));
+ PUT_C(opts, "ext_linegrid", BOOLEAN_OBJ(true));
+ PUT_C(opts, "ext_termcolors", BOOLEAN_OBJ(true));
+ if (term) {
+ PUT(opts, "term_name", STRING_OBJ(cstr_to_string(term)));
+ }
+ if (ui_client_bg_response != kNone) {
+ bool is_dark = (ui_client_bg_response == kTrue);
+ PUT_C(opts, "term_background", STRING_OBJ(cstr_as_string(is_dark ? "dark" : "light")));
+ }
+ PUT_C(opts, "term_colors", INTEGER_OBJ(t_colors));
+ if (!remote_ui) {
+ PUT_C(opts, "stdin_tty", BOOLEAN_OBJ(stdin_isatty));
+ PUT_C(opts, "stdout_tty", BOOLEAN_OBJ(stdout_isatty));
+ if (ui_client_forward_stdin) {
+ PUT_C(opts, "stdin_fd", INTEGER_OBJ(UI_CLIENT_STDIN_FD));
+ }
+ }
+ ADD_C(args, DICTIONARY_OBJ(opts));
+
+ rpc_send_event(ui_client_channel_id, "nvim_ui_attach", args);
+ ui_client_attached = true;
- rpc_send_event(chan, "nvim_ui_attach", args);
- ui_client_channel_id = chan;
+ // os_exit() will be invoked when the client channel detaches
+ while (true) {
+ LOOP_PROCESS_EVENTS(&main_loop, resize_events, -1);
+ }
+}
+
+void ui_client_stop(void)
+{
+ tui_stop(tui);
+}
+
+void ui_client_set_size(int width, int height)
+{
+ // The currently known size will be sent when attaching
+ if (ui_client_attached) {
+ MAXSIZE_TEMP_ARRAY(args, 2);
+ ADD_C(args, INTEGER_OBJ((int)width));
+ ADD_C(args, INTEGER_OBJ((int)height));
+ rpc_send_event(ui_client_channel_id, "nvim_ui_try_resize", args);
+ }
}
UIClientHandler ui_client_get_redraw_handler(const char *name, size_t name_len, Error *error)
@@ -55,26 +137,12 @@ UIClientHandler ui_client_get_redraw_handler(const char *name, size_t name_len,
/// async 'redraw' events, which are expected when nvim acts as an ui client.
/// get handled in msgpack_rpc/unpacker.c and directly dispatched to handlers
/// of specific ui events, like ui_client_event_grid_resize and so on.
-Object handle_ui_client_redraw(uint64_t channel_id, Array args, Error *error)
+Object handle_ui_client_redraw(uint64_t channel_id, Array args, Arena *arena, Error *error)
{
api_set_error(error, kErrorTypeValidation, "'redraw' cannot be sent as a request");
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;
@@ -83,7 +151,7 @@ static HlAttrs ui_client_dict2hlattrs(Dictionary d, bool rgb)
// TODO(bfredl): log "err"
return HLATTRS_INIT;
}
- return dict2hlattrs(&dict, true, NULL, &err);
+ return dict2hlattrs(&dict, rgb, NULL, &err);
}
void ui_client_event_grid_resize(Array args)
@@ -99,7 +167,7 @@ void ui_client_event_grid_resize(Array args)
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);
+ tui_grid_resize(tui, grid, width, height);
if (grid_line_buf_size < (size_t)width) {
xfree(grid_line_buf_char);
@@ -125,6 +193,6 @@ void ui_client_event_raw_line(GridLineEvent *g)
// TODO(hlpr98): Accommodate other LineFlags when included in grid_line
LineFlags lineflags = 0;
- ui_call_raw_line(grid, row, startcol, endcol, clearcol, g->cur_attr, lineflags,
- (const schar_T *)grid_line_buf_char, grid_line_buf_attr);
+ tui_raw_line(tui, grid, row, startcol, endcol, clearcol, g->cur_attr, lineflags,
+ (const schar_T *)grid_line_buf_char, grid_line_buf_attr);
}
diff --git a/src/nvim/ui_client.h b/src/nvim/ui_client.h
index 311dafaa0b..7e5f847039 100644
--- a/src/nvim/ui_client.h
+++ b/src/nvim/ui_client.h
@@ -1,8 +1,14 @@
#ifndef NVIM_UI_CLIENT_H
#define NVIM_UI_CLIENT_H
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
#include "nvim/api/private/defs.h"
#include "nvim/grid_defs.h"
+#include "nvim/macros.h"
+#include "nvim/types.h"
typedef struct {
const char *name;
@@ -14,10 +20,29 @@ EXTERN size_t grid_line_buf_size INIT(= 0);
EXTERN schar_T *grid_line_buf_char INIT(= NULL);
EXTERN sattr_T *grid_line_buf_attr INIT(= NULL);
+// ID of the ui client channel. If zero, the client is not running.
+EXTERN uint64_t ui_client_channel_id INIT(= 0);
+
+// TODO(bfredl): the current structure for how tui and ui_client.c communicate is a bit awkward.
+// This will be restructured as part of The UI Devirtualization Project.
+
+/// Whether ui client has sent nvim_ui_attach yet
+EXTERN bool ui_client_attached INIT(= false);
+
+/// Whether ui client has gotten a response about the bg color of the terminal,
+/// kTrue=dark, kFalse=light, kNone=no response yet
+EXTERN TriState ui_client_bg_response INIT(= kNone);
+
+/// The ui client should forward its stdin to the nvim process
+/// by convention, this uses fd=3 (next free number after stdio)
+EXTERN bool ui_client_forward_stdin INIT(= false);
+
+#define UI_CLIENT_STDIN_FD 3
+// uncrustify:off
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ui_client.h.generated.h"
-
# include "ui_events_client.h.generated.h"
#endif
+// uncrustify:on
#endif // NVIM_UI_CLIENT_H
diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c
index 2216e25db9..9ff9eabff8 100644
--- a/src/nvim/ui_compositor.c
+++ b/src/nvim/ui_compositor.c
@@ -7,24 +7,28 @@
// Layer-based compositing: https://en.wikipedia.org/wiki/Digital_compositing
#include <assert.h>
+#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
-#include "nvim/api/private/helpers.h"
+#include "klib/kvec.h"
+#include "nvim/api/private/defs.h"
#include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
+#include "nvim/globals.h"
#include "nvim/grid.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
-#include "nvim/lib/kvec.h"
#include "nvim/log.h"
-#include "nvim/lua/executor.h"
-#include "nvim/main.h"
+#include "nvim/macros.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/os/os.h"
-#include "nvim/popupmenu.h"
-#include "nvim/ugrid.h"
+#include "nvim/option_defs.h"
+#include "nvim/os/time.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/ui_compositor.h"
#include "nvim/vim.h"
@@ -33,7 +37,6 @@
# include "ui_compositor.c.generated.h"
#endif
-static UI *compositor = NULL;
static int composed_uis = 0;
kvec_t(ScreenGrid *) layers = KV_INITIAL_VALUE;
@@ -58,33 +61,8 @@ static int dbghl_normal, dbghl_clear, dbghl_composed, dbghl_recompose;
void ui_comp_init(void)
{
- if (compositor != NULL) {
- return;
- }
- compositor = xcalloc(1, sizeof(UI));
-
- compositor->rgb = true;
- compositor->grid_resize = ui_comp_grid_resize;
- compositor->grid_scroll = ui_comp_grid_scroll;
- compositor->grid_cursor_goto = ui_comp_grid_cursor_goto;
- compositor->raw_line = ui_comp_raw_line;
- compositor->msg_set_pos = ui_comp_msg_set_pos;
-
- // Be unopinionated: will be attached together with a "real" ui anyway
- compositor->width = INT_MAX;
- compositor->height = INT_MAX;
- for (UIExtension i = 0; (int)i < kUIExtCount; i++) {
- compositor->ui_ext[i] = true;
- }
-
- // TODO(bfredl): this will be more complicated if we implement
- // hlstate per UI (i e reduce hl ids for non-hlstate UIs)
- compositor->ui_ext[kUIHlState] = false;
-
kv_push(layers, &default_grid);
curgrid = &default_grid;
-
- ui_attach_impl(compositor, 0);
}
void ui_comp_syn_init(void)
@@ -122,7 +100,7 @@ bool ui_comp_should_draw(void)
///
/// TODO(bfredl): later on the compositor should just use win_float_pos events,
/// though that will require slight event order adjustment: emit the win_pos
-/// events in the beginning of update_screen(0), rather than in ui_flush()
+/// events in the beginning of update_screen(), rather than in ui_flush()
bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width, bool valid,
bool on_top)
{
@@ -237,7 +215,7 @@ bool ui_comp_set_grid(handle_T handle)
return false;
}
-static void ui_comp_raise_grid(ScreenGrid *grid, size_t new_index)
+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++) {
@@ -257,7 +235,7 @@ static void ui_comp_raise_grid(ScreenGrid *grid, size_t new_index)
}
}
-static void ui_comp_grid_cursor_goto(UI *ui, Integer grid_handle, Integer r, Integer c)
+void ui_comp_grid_cursor_goto(Integer grid_handle, Integer r, Integer c)
{
if (!ui_comp_should_draw() || !ui_comp_set_grid((int)grid_handle)) {
return;
@@ -469,6 +447,10 @@ static void compose_debug(Integer startrow, Integer endrow, Integer startcol, In
endcol = MIN(endcol, default_grid.cols);
int attr = syn_id2attr(syn_id);
+ if (delay) {
+ debug_delay(endrow - startrow);
+ }
+
for (int row = (int)startrow; row < endrow; row++) {
ui_composed_call_raw_line(1, row, startcol, startcol, endcol, attr, false,
(const schar_T *)linebuf,
@@ -485,7 +467,7 @@ static void debug_delay(Integer lines)
ui_call_flush();
uint64_t wd = (uint64_t)labs(p_wd);
uint64_t factor = (uint64_t)MAX(MIN(lines, 5), 1);
- os_microdelay(factor * wd * 1000u, true);
+ os_microdelay(factor * wd * 1000U, true);
}
static void compose_area(Integer startrow, Integer endrow, Integer startcol, Integer endcol)
@@ -513,9 +495,9 @@ void ui_comp_compose_grid(ScreenGrid *grid)
}
}
-static void ui_comp_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Integer endcol,
- Integer clearcol, Integer clearattr, LineFlags flags,
- const schar_T *chunk, const sattr_T *attrs)
+void ui_comp_raw_line(Integer grid, Integer row, Integer startcol, Integer endcol, Integer clearcol,
+ Integer clearattr, LineFlags flags, const schar_T *chunk,
+ const sattr_T *attrs)
{
if (!ui_comp_should_draw() || !ui_comp_set_grid((int)grid)) {
return;
@@ -570,22 +552,23 @@ static void ui_comp_raw_line(UI *ui, Integer grid, Integer row, Integer startcol
/// The screen is invalid and will soon be cleared
///
/// Don't redraw floats until screen is cleared
-void ui_comp_set_screen_valid(bool valid)
+bool ui_comp_set_screen_valid(bool valid)
{
+ bool old_val = valid_screen;
valid_screen = valid;
if (!valid) {
msg_sep_row = -1;
}
+ return old_val;
}
-static void ui_comp_msg_set_pos(UI *ui, Integer grid, Integer row, Boolean scrolled,
- String sep_char)
+void ui_comp_msg_set_pos(Integer grid, Integer row, Boolean scrolled, String sep_char)
{
msg_grid.comp_row = (int)row;
if (scrolled && row > 0) {
msg_sep_row = (int)row - 1;
if (sep_char.data) {
- STRLCPY(msg_sep_char, sep_char.data, sizeof(msg_sep_char));
+ xstrlcpy(msg_sep_char, sep_char.data, sizeof(msg_sep_char));
}
} else {
msg_sep_row = -1;
@@ -594,7 +577,7 @@ 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.cols);
} else if (row < msg_current_row && ui_comp_should_draw()
- && msg_current_row < Rows) {
+ && (msg_current_row < Rows || (scrolled && !msg_was_scrolled))) {
int delta = msg_current_row - (int)row;
if (msg_grid.blending) {
int first_row = MAX((int)row - (scrolled?1:0), 0);
@@ -623,8 +606,8 @@ static bool curgrid_covered_above(int row)
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,
- Integer right, Integer rows, Integer cols)
+void ui_comp_grid_scroll(Integer grid, Integer top, Integer bot, Integer left, Integer right,
+ Integer rows, Integer cols)
{
if (!ui_comp_should_draw() || !ui_comp_set_grid((int)grid)) {
return;
@@ -658,7 +641,7 @@ static void ui_comp_grid_scroll(UI *ui, Integer grid, Integer top, Integer bot,
}
}
-static void ui_comp_grid_resize(UI *ui, Integer grid, Integer width, Integer height)
+void ui_comp_grid_resize(Integer grid, Integer width, Integer height)
{
if (grid == 1) {
ui_composed_call_grid_resize(1, width, height);
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
index 75a09b244c..2b0bb1d243 100644
--- a/src/nvim/undo.c
+++ b/src/nvim/undo.c
@@ -76,41 +76,56 @@
#include <assert.h>
#include <fcntl.h>
#include <inttypes.h>
-#include <limits.h>
#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
+#include <time.h>
+#include <uv.h>
#include "auto/config.h"
+#include "klib/kvec.h"
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/buffer_updates.h"
#include "nvim/change.h"
#include "nvim/cursor.h"
#include "nvim/drawscreen.h"
#include "nvim/edit.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_getln.h"
#include "nvim/extmark.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
-#include "nvim/lib/kvec.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/highlight_defs.h"
+#include "nvim/macros.h"
#include "nvim/mark.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/option.h"
+#include "nvim/os/fs_defs.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
+#include "nvim/os/os_defs.h"
#include "nvim/os/time.h"
-#include "nvim/os_unix.h"
#include "nvim/path.h"
-#include "nvim/pos.h" // MAXLNUM
+#include "nvim/pos.h"
+#include "nvim/screen.h"
#include "nvim/sha256.h"
#include "nvim/state.h"
#include "nvim/strings.h"
#include "nvim/types.h"
#include "nvim/undo.h"
+#include "nvim/undo_defs.h"
+#include "nvim/vim.h"
/// Structure passed around between undofile functions.
typedef struct {
@@ -558,7 +573,7 @@ int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, int re
}
if (size > 0) {
- uep->ue_array = xmalloc(sizeof(char_u *) * (size_t)size);
+ uep->ue_array = xmalloc(sizeof(char *) * (size_t)size);
linenr_T lnum;
long i;
for (i = 0, lnum = top + 1; i < size; i++) {
@@ -609,20 +624,20 @@ int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, int re
// extra fields for uhp
#define UHP_SAVE_NR 1
-static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");
+static char e_not_open[] = N_("E828: Cannot open undo file for writing: %s");
/// Compute the hash for a buffer text into hash[UNDO_HASH_SIZE].
///
/// @param[in] buf The buffer used to compute the hash
/// @param[in] hash Array of size UNDO_HASH_SIZE in which to store the value of
/// the hash
-void u_compute_hash(buf_T *buf, char_u *hash)
+void u_compute_hash(buf_T *buf, uint8_t *hash)
{
context_sha256_T ctx;
sha256_start(&ctx);
for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
- char_u *p = ml_get_buf(buf, lnum, false);
- sha256_update(&ctx, p, (uint32_t)(STRLEN(p) + 1));
+ char *p = ml_get_buf(buf, lnum, false);
+ sha256_update(&ctx, (uint8_t *)p, strlen(p) + 1);
}
sha256_finish(&ctx, hash);
}
@@ -651,7 +666,7 @@ char *u_get_undo_file_name(const char *const buf_ffname, const bool reading)
char fname_buf[MAXPATHL];
// Expand symlink in the file name, so that we put the undo file with the
// actual file instead of with the symlink.
- if (resolve_symlink((const char_u *)ffname, (char_u *)fname_buf) == OK) {
+ if (resolve_symlink(ffname, fname_buf) == OK) {
ffname = fname_buf;
}
#endif
@@ -662,7 +677,7 @@ char *u_get_undo_file_name(const char *const buf_ffname, const bool reading)
// Loop over 'undodir'. When reading find the first file that exists.
// When not reading use the first directory that exists or ".".
- char *dirp = (char *)p_udir;
+ char *dirp = p_udir;
while (*dirp != NUL) {
size_t dir_len = copy_option_part(&dirp, dir_name, MAXPATHL, ",");
if (dir_len == 1 && dir_name[0] == '.') {
@@ -685,7 +700,7 @@ char *u_get_undo_file_name(const char *const buf_ffname, const bool reading)
*p-- = NUL;
}
- bool has_directory = os_isdir((char_u *)dir_name);
+ bool has_directory = os_isdir(dir_name);
if (!has_directory && *dirp == NUL && !reading) {
// Last directory in the list does not exist, create it.
int ret;
@@ -713,7 +728,7 @@ char *u_get_undo_file_name(const char *const buf_ffname, const bool reading)
// When reading check if the file exists.
if (undo_file_name != NULL
- && (!reading || os_path_exists((char_u *)undo_file_name))) {
+ && (!reading || os_path_exists(undo_file_name))) {
break;
}
XFREE_CLEAR(undo_file_name);
@@ -750,7 +765,7 @@ static void u_free_uhp(u_header_T *uhp)
/// @param hash The hash of the buffer contents
//
/// @returns false in case of an error.
-static bool serialize_header(bufinfo_T *bi, char_u *hash)
+static bool serialize_header(bufinfo_T *bi, uint8_t *hash)
FUNC_ATTR_NONNULL_ALL
{
buf_T *buf = bi->bi_buf;
@@ -771,9 +786,9 @@ static bool serialize_header(bufinfo_T *bi, char_u *hash)
// Write buffer-specific data.
undo_write_bytes(bi, (uintmax_t)buf->b_ml.ml_line_count, 4);
- size_t len = buf->b_u_line_ptr ? STRLEN(buf->b_u_line_ptr) : 0;
+ size_t len = buf->b_u_line_ptr ? strlen(buf->b_u_line_ptr) : 0;
undo_write_bytes(bi, len, 4);
- if (len > 0 && !undo_write(bi, buf->b_u_line_ptr, len)) {
+ if (len > 0 && !undo_write(bi, (uint8_t *)buf->b_u_line_ptr, len)) {
return false;
}
undo_write_bytes(bi, (uintmax_t)buf->b_u_line_lnum, 4);
@@ -1034,11 +1049,11 @@ static bool serialize_uep(bufinfo_T *bi, u_entry_T *uep)
undo_write_bytes(bi, (uintmax_t)uep->ue_size, 4);
for (size_t i = 0; i < (size_t)uep->ue_size; i++) {
- size_t len = STRLEN(uep->ue_array[i]);
+ size_t len = strlen(uep->ue_array[i]);
if (!undo_write_bytes(bi, len, 4)) {
return false;
}
- if (len > 0 && !undo_write(bi, uep->ue_array[i], len)) {
+ if (len > 0 && !undo_write(bi, (uint8_t *)uep->ue_array[i], len)) {
return false;
}
}
@@ -1057,18 +1072,18 @@ static u_entry_T *unserialize_uep(bufinfo_T *bi, bool *error, const char *file_n
uep->ue_lcount = undo_read_4c(bi);
uep->ue_size = undo_read_4c(bi);
- char_u **array = NULL;
+ char **array = NULL;
if (uep->ue_size > 0) {
- if ((size_t)uep->ue_size < SIZE_MAX / sizeof(char_u *)) { // -V547
- array = xmalloc(sizeof(char_u *) * (size_t)uep->ue_size);
- memset(array, 0, sizeof(char_u *) * (size_t)uep->ue_size);
+ if ((size_t)uep->ue_size < SIZE_MAX / sizeof(char *)) { // -V547
+ array = xmalloc(sizeof(char *) * (size_t)uep->ue_size);
+ memset(array, 0, sizeof(char *) * (size_t)uep->ue_size);
}
}
uep->ue_array = array;
for (size_t i = 0; i < (size_t)uep->ue_size; i++) {
int line_len = undo_read_4c(bi);
- char_u *line;
+ char *line;
if (line_len >= 0) {
line = undo_read_string(bi, (size_t)line_len);
} else {
@@ -1135,7 +1150,7 @@ static void unserialize_visualinfo(bufinfo_T *bi, visualinfo_T *info)
/// @param[in] buf Buffer for which undo file is written.
/// @param[in] hash Hash value of the buffer text. Must have #UNDO_HASH_SIZE
/// size.
-void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, char_u *const hash)
+void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, uint8_t *const hash)
FUNC_ATTR_NONNULL_ARG(3, 4)
{
char *file_name;
@@ -1177,7 +1192,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf,
// If the undo file already exists, verify that it actually is an undo
// file, and delete it.
- if (os_path_exists((char_u *)file_name)) {
+ if (os_path_exists(file_name)) {
if (name == NULL || !forceit) {
// Check we can read it and it's an undo file.
fd = os_open(file_name, O_RDONLY, 0);
@@ -1194,7 +1209,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf,
}
goto theend;
} else {
- char_u mbuf[UF_START_MAGIC_LEN];
+ char mbuf[UF_START_MAGIC_LEN];
ssize_t len = read_eintr(fd, mbuf, UF_START_MAGIC_LEN);
close(fd);
if (len < UF_START_MAGIC_LEN
@@ -1327,9 +1342,9 @@ write_error:
#ifdef HAVE_ACL
if (buf->b_ffname != NULL) {
// For systems that support ACL: get the ACL from the original file.
- vim_acl_T acl = mch_get_acl((char_u *)buf->b_ffname);
- mch_set_acl((char_u *)file_name, acl);
- mch_free_acl(acl);
+ vim_acl_T acl = os_get_acl(buf->b_ffname);
+ os_set_acl(file_name, acl);
+ os_free_acl(acl);
}
#endif
@@ -1344,11 +1359,11 @@ theend:
/// a bit more verbose.
/// Otherwise use curbuf->b_ffname to generate the undo file name.
/// "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
-void u_read_undo(char *name, const char_u *hash, const char_u *orig_name FUNC_ATTR_UNUSED)
+void u_read_undo(char *name, const uint8_t *hash, const char *orig_name FUNC_ATTR_UNUSED)
FUNC_ATTR_NONNULL_ARG(2)
{
u_header_T **uhp_table = NULL;
- char_u *line_ptr = NULL;
+ char *line_ptr = NULL;
char *file_name;
if (name == NULL) {
@@ -1362,7 +1377,7 @@ void u_read_undo(char *name, const char_u *hash, const char_u *orig_name FUNC_AT
// owner of the text file or equal to the current user.
FileInfo file_info_orig;
FileInfo file_info_undo;
- if (os_fileinfo((const char *)orig_name, &file_info_orig)
+ if (os_fileinfo(orig_name, &file_info_orig)
&& os_fileinfo(file_name, &file_info_undo)
&& file_info_orig.stat.st_uid != file_info_undo.stat.st_uid
&& file_info_undo.stat.st_uid != getuid()) {
@@ -1399,7 +1414,7 @@ void u_read_undo(char *name, const char_u *hash, const char_u *orig_name FUNC_AT
};
// Read the undo file header.
- char_u magic_buf[UF_START_MAGIC_LEN];
+ char magic_buf[UF_START_MAGIC_LEN];
if (fread(magic_buf, UF_START_MAGIC_LEN, 1, fp) != 1
|| memcmp(magic_buf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0) {
semsg(_("E823: Not an undo file: %s"), file_name);
@@ -1411,7 +1426,7 @@ void u_read_undo(char *name, const char_u *hash, const char_u *orig_name FUNC_AT
goto error;
}
- char_u read_hash[UNDO_HASH_SIZE];
+ uint8_t read_hash[UNDO_HASH_SIZE];
if (!undo_read(&bi, read_hash, UNDO_HASH_SIZE)) {
corruption_error("hash", file_name);
goto error;
@@ -1728,10 +1743,10 @@ static bool undo_read(bufinfo_T *bi, uint8_t *buffer, size_t size)
/// @param len can be zero to allocate an empty line.
///
/// @returns a pointer to allocated memory or NULL in case of an error.
-static uint8_t *undo_read_string(bufinfo_T *bi, size_t len)
+static char *undo_read_string(bufinfo_T *bi, size_t len)
{
- uint8_t *ptr = xmallocz(len);
- if (len > 0 && !undo_read(bi, ptr, len)) {
+ char *ptr = xmallocz(len);
+ if (len > 0 && !undo_read(bi, (uint8_t *)ptr, len)) {
xfree(ptr);
return NULL;
}
@@ -1771,16 +1786,16 @@ void u_redo(int count)
/// Undo and remove the branch from the undo tree.
/// Also moves the cursor (as a "normal" undo would).
-bool u_undo_and_forget(int count)
+///
+/// @param do_buf_event If `true`, send the changedtick with the buffer updates
+bool u_undo_and_forget(int count, bool do_buf_event)
{
if (curbuf->b_u_synced == false) {
u_sync(true);
count = 1;
}
undo_undoes = true;
- u_doit(count, true,
- // Don't send nvim_buf_lines_event for u_undo_and_forget().
- false);
+ u_doit(count, true, do_buf_event);
if (curbuf->b_u_curhead == NULL) {
// nothing was undone.
@@ -2223,7 +2238,7 @@ target_zero:
/// @param do_buf_event If `true`, send buffer updates.
static void u_undoredo(int undo, bool do_buf_event)
{
- char_u **newarray = NULL;
+ char **newarray = NULL;
linenr_T newlnum = MAXLNUM;
u_entry_T *nuep;
u_entry_T *newlist = NULL;
@@ -2283,7 +2298,7 @@ static void u_undoredo(int undo, bool do_buf_event)
// line.
long i;
for (i = 0; i < newsize && i < oldsize; i++) {
- if (STRCMP(uep->ue_array[i], ml_get(top + 1 + (linenr_T)i)) != 0) {
+ if (strcmp(uep->ue_array[i], ml_get(top + 1 + (linenr_T)i)) != 0) {
break;
}
}
@@ -2301,11 +2316,11 @@ static void u_undoredo(int undo, bool do_buf_event)
// delete the lines between top and bot and save them in newarray
if (oldsize > 0) {
- newarray = xmalloc(sizeof(char_u *) * (size_t)oldsize);
+ newarray = xmalloc(sizeof(char *) * (size_t)oldsize);
// delete backwards, it goes faster in most cases
long i;
linenr_T lnum;
- for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum) {
+ for (lnum = bot - 1, i = oldsize; --i >= 0; lnum--) {
// what can we do when we run out of memory?
newarray[i] = u_save_line(lnum);
// remember we deleted the last line in the buffer, and a
@@ -2327,13 +2342,13 @@ static void u_undoredo(int undo, bool do_buf_event)
// If the file is empty, there is an empty line 1 that we
// should get rid of, by replacing it with the new line
if (empty_buffer && lnum == 0) {
- ml_replace((linenr_T)1, (char *)uep->ue_array[i], true);
+ ml_replace((linenr_T)1, uep->ue_array[i], true);
} else {
- ml_append(lnum, (char *)uep->ue_array[i], (colnr_T)0, false);
+ ml_append(lnum, uep->ue_array[i], (colnr_T)0, false);
}
xfree(uep->ue_array[i]);
}
- xfree((char_u *)uep->ue_array);
+ xfree(uep->ue_array);
}
// Adjust marks
@@ -2550,7 +2565,7 @@ static void u_undo_end(bool did_undo, bool absolute, bool quiet)
uhp = curbuf->b_u_newhead;
}
- char_u msgbuf[80];
+ char msgbuf[80];
if (uhp == NULL) {
*msgbuf = NUL;
} else {
@@ -2560,7 +2575,7 @@ static void u_undo_end(bool did_undo, bool absolute, bool quiet)
{
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_buffer == curbuf && wp->w_p_cole > 0) {
- redraw_later(wp, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
}
}
}
@@ -2579,21 +2594,25 @@ static void u_undo_end(bool did_undo, bool absolute, bool quiet)
}
/// Put the timestamp of an undo header in "buf[buflen]" in a nice format.
-void undo_fmt_time(char_u *buf, size_t buflen, time_t tt)
+void undo_fmt_time(char *buf, size_t buflen, time_t tt)
{
if (time(NULL) - tt >= 100) {
struct tm curtime;
os_localtime_r(&tt, &curtime);
+ size_t n;
if (time(NULL) - tt < (60L * 60L * 12L)) {
// within 12 hours
- (void)strftime((char *)buf, buflen, "%H:%M:%S", &curtime);
+ n = strftime(buf, buflen, "%H:%M:%S", &curtime);
} else {
// longer ago
- (void)strftime((char *)buf, buflen, "%Y/%m/%d %H:%M:%S", &curtime);
+ n = strftime(buf, buflen, "%Y/%m/%d %H:%M:%S", &curtime);
+ }
+ if (n == 0) {
+ buf[0] = NUL;
}
} else {
int64_t seconds = time(NULL) - tt;
- vim_snprintf((char *)buf, buflen,
+ vim_snprintf(buf, buflen,
NGETTEXT("%" PRId64 " second ago",
"%" PRId64 " seconds ago", (uint32_t)seconds),
seconds);
@@ -2635,15 +2654,15 @@ void ex_undolist(exarg_T *eap)
while (uhp != NULL) {
if (uhp->uh_prev.ptr == NULL && uhp->uh_walk != nomark
&& uhp->uh_walk != mark) {
- vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7d ", uhp->uh_seq, changes);
- undo_fmt_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff), uhp->uh_time);
+ vim_snprintf(IObuff, IOSIZE, "%6ld %7d ", uhp->uh_seq, changes);
+ undo_fmt_time(IObuff + strlen(IObuff), IOSIZE - strlen(IObuff), uhp->uh_time);
if (uhp->uh_save_nr > 0) {
- while (STRLEN(IObuff) < 33) {
+ while (strlen(IObuff) < 33) {
STRCAT(IObuff, " ");
}
- vim_snprintf_add((char *)IObuff, IOSIZE, " %3ld", uhp->uh_save_nr);
+ vim_snprintf_add(IObuff, IOSIZE, " %3ld", uhp->uh_save_nr);
}
- GA_APPEND(char_u *, &ga, vim_strsave(IObuff));
+ GA_APPEND(char *, &ga, xstrdup(IObuff));
}
uhp->uh_walk = mark;
@@ -2713,9 +2732,8 @@ void ex_undojoin(exarg_T *eap)
}
if (get_undolevel(curbuf) < 0) {
return; // no entries, nothing to do
- } else {
- curbuf->b_u_synced = false; // Append next change to last entry
}
+ curbuf->b_u_synced = false; // Append next change to last entry
}
/// Called after writing or reloading the file and setting b_changed to false.
@@ -2744,7 +2762,7 @@ void u_find_first_changed(void)
linenr_T lnum;
for (lnum = 1; lnum < curbuf->b_ml.ml_line_count
&& lnum <= uep->ue_size; lnum++) {
- if (STRCMP(ml_get_buf(curbuf, lnum, false), uep->ue_array[lnum - 1]) != 0) {
+ if (strcmp(ml_get_buf(curbuf, lnum, false), uep->ue_array[lnum - 1]) != 0) {
clearpos(&(uhp->uh_cursor));
uhp->uh_cursor.lnum = lnum;
return;
@@ -2917,7 +2935,7 @@ static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp)
#ifdef U_DEBUG
uhp->uh_magic = 0;
#endif
- xfree((char_u *)uhp);
+ xfree(uhp);
buf->b_u_numhead--;
}
@@ -2927,11 +2945,11 @@ static void u_freeentry(u_entry_T *uep, long n)
while (n > 0) {
xfree(uep->ue_array[--n]);
}
- xfree((char_u *)uep->ue_array);
+ xfree(uep->ue_array);
#ifdef U_DEBUG
uep->ue_magic = 0;
#endif
- xfree((char_u *)uep);
+ xfree(uep);
}
/// invalidate the undo buffer; called when storage has already been released
@@ -2991,11 +3009,11 @@ void u_undoline(void)
return;
}
- char_u *oldp = u_save_line(curbuf->b_u_line_lnum);
- ml_replace(curbuf->b_u_line_lnum, (char *)curbuf->b_u_line_ptr, true);
+ char *oldp = u_save_line(curbuf->b_u_line_lnum);
+ ml_replace(curbuf->b_u_line_lnum, 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),
- (colnr_T)STRLEN(curbuf->b_u_line_ptr), kExtmarkUndo);
+ 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;
@@ -3025,7 +3043,7 @@ void u_blockfree(buf_T *buf)
/// Allocate memory and copy curbuf line into it.
///
/// @param lnum the line to copy
-static char_u *u_save_line(linenr_T lnum)
+static char *u_save_line(linenr_T lnum)
{
return u_save_line_buf(curbuf, lnum);
}
@@ -3034,9 +3052,9 @@ static char_u *u_save_line(linenr_T lnum)
///
/// @param lnum line to copy
/// @param buf buffer to copy from
-static char_u *u_save_line_buf(buf_T *buf, linenr_T lnum)
+static char *u_save_line_buf(buf_T *buf, linenr_T lnum)
{
- return vim_strsave(ml_get_buf(buf, lnum, false));
+ return xstrdup(ml_get_buf(buf, lnum, false));
}
/// Check if the 'modified' flag is set, or 'ff' has changed (only need to
diff --git a/src/nvim/undo_defs.h b/src/nvim/undo_defs.h
index 4b64f97919..7c065c540b 100644
--- a/src/nvim/undo_defs.h
+++ b/src/nvim/undo_defs.h
@@ -21,11 +21,11 @@ typedef struct {
typedef struct u_entry u_entry_T;
struct u_entry {
- u_entry_T *ue_next; // pointer to next entry in list
+ u_entry_T *ue_next; // pointer to next entry in list
linenr_T ue_top; // number of line above undo block
linenr_T ue_bot; // number of line below undo block
linenr_T ue_lcount; // linecount when u_save called
- char_u **ue_array; // array of lines in undo block
+ char **ue_array; // array of lines in undo block
long ue_size; // number of lines in ue_array
#ifdef U_DEBUG
int ue_magic; // magic number to check allocation
diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c
index 59b8d10200..31cb1e8936 100644
--- a/src/nvim/usercmd.c
+++ b/src/nvim/usercmd.c
@@ -6,19 +6,36 @@
#include <assert.h>
#include <inttypes.h>
#include <stdbool.h>
-#include <stdlib.h>
+#include <stdio.h>
#include <string.h>
+#include "auto/config.h"
+#include "lauxlib.h"
+#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/eval.h"
#include "nvim/ex_docmd.h"
#include "nvim/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/highlight_defs.h"
+#include "nvim/keycodes.h"
#include "nvim/lua/executor.h"
+#include "nvim/macros.h"
+#include "nvim/mapping.h"
+#include "nvim/mbyte.h"
+#include "nvim/memory.h"
+#include "nvim/menu.h"
+#include "nvim/message.h"
+#include "nvim/option_defs.h"
#include "nvim/os/input.h"
#include "nvim/runtime.h"
+#include "nvim/strings.h"
#include "nvim/usercmd.h"
+#include "nvim/vim.h"
#include "nvim/window.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -36,8 +53,7 @@ static char e_no_such_user_defined_command_in_current_buffer_str[]
/// List of names for completion for ":command" with the EXPAND_ flag.
/// Must be alphabetical for completion.
-static const char *command_complete[] =
-{
+static const char *command_complete[] = {
[EXPAND_ARGLIST] = "arglist",
[EXPAND_AUGROUP] = "augroup",
[EXPAND_BEHAVE] = "behave",
@@ -46,7 +62,6 @@ static const char *command_complete[] =
[EXPAND_COLORS] = "color",
[EXPAND_COMMANDS] = "command",
[EXPAND_COMPILER] = "compiler",
- [EXPAND_CSCOPE] = "cscope",
[EXPAND_USER_DEFINED] = "custom",
[EXPAND_USER_LIST] = "customlist",
[EXPAND_USER_LUA] = "<Lua function>",
@@ -80,6 +95,8 @@ static const char *command_complete[] =
[EXPAND_TAGS_LISTFILES] = "tag_listfiles",
[EXPAND_USER] = "user",
[EXPAND_USER_VARS] = "var",
+ [EXPAND_BREAKPOINT] = "breakpoint",
+ [EXPAND_SCRIPTNAMES] = "scriptnames",
};
/// List of names of address types. Must be alphabetical for completion.
@@ -87,8 +104,7 @@ static struct {
cmd_addr_T expand;
char *name;
char *shortname;
-} addr_type_complete[] =
-{
+} addr_type_complete[] = {
{ ADDR_ARGUMENTS, "arguments", "arg" },
{ ADDR_LINES, "lines", "line" },
{ ADDR_LOADED_BUFFERS, "loaded_buffers", "load" },
@@ -106,7 +122,7 @@ static struct {
/// Return NULL if there is no matching command.
///
/// @param *p end of the command (possibly including count)
-/// @param full set to TRUE for a full match
+/// @param full set to true for a full match
/// @param xp used for completion, NULL otherwise
/// @param complp completion flags or NULL
char *find_ucmd(exarg_T *eap, char *p, int *full, expand_T *xp, int *complp)
@@ -127,7 +143,7 @@ char *find_ucmd(exarg_T *eap, char *p, int *full, expand_T *xp, int *complp)
for (j = 0; j < gap->ga_len; j++) {
uc = USER_CMD_GA(gap, j);
cp = eap->cmd;
- np = (char *)uc->uc_name;
+ np = uc->uc_name;
k = 0;
while (k < len && *np != NUL && *cp++ == *np++) {
k++;
@@ -167,7 +183,7 @@ char *find_ucmd(exarg_T *eap, char *p, int *full, expand_T *xp, int *complp)
}
if (xp != NULL) {
xp->xp_luaref = uc->uc_compl_luaref;
- xp->xp_arg = (char *)uc->uc_compl_arg;
+ xp->xp_arg = uc->uc_compl_arg;
xp->xp_script_ctx = uc->uc_script_ctx;
xp->xp_script_ctx.sc_lnum += SOURCING_LNUM;
}
@@ -208,6 +224,7 @@ char *find_ucmd(exarg_T *eap, char *p, int *full, expand_T *xp, int *complp)
return p;
}
+/// Set completion context for :command
const char *set_context_in_user_cmd(expand_T *xp, const char *arg_in)
{
const char *arg = arg_in;
@@ -216,7 +233,7 @@ const char *set_context_in_user_cmd(expand_T *xp, const char *arg_in)
// Check for attributes
while (*arg == '-') {
arg++; // Skip "-".
- p = (const char *)skiptowhite((const char_u *)arg);
+ p = (const char *)skiptowhite(arg);
if (*p == NUL) {
// Cursor is still in the attribute.
p = strchr(arg, '=');
@@ -248,7 +265,7 @@ const char *set_context_in_user_cmd(expand_T *xp, const char *arg_in)
}
// After the attributes comes the new command name.
- p = (const char *)skiptowhite((const char_u *)arg);
+ p = (const char *)skiptowhite(arg);
if (*p == NUL) {
xp->xp_context = EXPAND_USER_COMMANDS;
xp->xp_pattern = (char *)arg;
@@ -259,6 +276,47 @@ const char *set_context_in_user_cmd(expand_T *xp, const char *arg_in)
return (const char *)skipwhite(p);
}
+/// Set the completion context for the argument of a user defined command.
+const char *set_context_in_user_cmdarg(const char *cmd FUNC_ATTR_UNUSED, const char *arg,
+ uint32_t argt, int context, expand_T *xp, bool forceit)
+{
+ if (context == EXPAND_NOTHING) {
+ return NULL;
+ }
+
+ if (argt & EX_XFILE) {
+ // EX_XFILE: file names are handled above.
+ xp->xp_context = context;
+ return NULL;
+ }
+
+ if (context == EXPAND_MENUS) {
+ return (const char *)set_context_in_menu_cmd(xp, cmd, (char *)arg, forceit);
+ }
+ if (context == EXPAND_COMMANDS) {
+ return arg;
+ }
+ if (context == EXPAND_MAPPINGS) {
+ return (const char *)set_context_in_map_cmd(xp, "map", (char *)arg, forceit, false, false,
+ CMD_map);
+ }
+ // Find start of last argument.
+ const char *p = arg;
+ while (*p) {
+ if (*p == ' ') {
+ // argument starts after a space
+ arg = p + 1;
+ } else if (*p == '\\' && *(p + 1) != NUL) {
+ p++; // skip over escaped character
+ }
+ MB_PTR_ADV(p);
+ }
+ xp->xp_pattern = (char *)arg;
+ xp->xp_context = context;
+
+ return NULL;
+}
+
char *expand_user_command_name(int idx)
{
return get_user_commands(NULL, idx - CMD_SIZE);
@@ -272,15 +330,15 @@ char *get_user_commands(expand_T *xp FUNC_ATTR_UNUSED, int idx)
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 USER_CMD_GA(&buf->b_ucmds, idx)->uc_name;
}
idx -= buf->b_ucmds.ga_len;
if (idx < ucmds.ga_len) {
- char *name = (char *)USER_CMD(idx)->uc_name;
+ char *name = USER_CMD(idx)->uc_name;
for (int i = 0; i < buf->b_ucmds.ga_len; i++) {
- if (STRCMP(name, USER_CMD_GA(&buf->b_ucmds, i)->uc_name) == 0) {
+ if (strcmp(name, USER_CMD_GA(&buf->b_ucmds, i)->uc_name) == 0) {
// global command is overruled by buffer-local one
return "";
}
@@ -297,14 +355,14 @@ char *get_user_commands(expand_T *xp FUNC_ATTR_UNUSED, int idx)
char *get_user_command_name(int idx, int cmdidx)
{
if (cmdidx == CMD_USER && idx < ucmds.ga_len) {
- return (char *)USER_CMD(idx)->uc_name;
+ return 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 USER_CMD_GA(&buf->b_ucmds, idx)->uc_name;
}
}
return NULL;
@@ -346,9 +404,8 @@ static char *get_command_complete(int arg)
{
if (arg >= (int)(ARRAY_SIZE(command_complete))) {
return NULL;
- } else {
- return (char *)command_complete[arg];
}
+ return (char *)command_complete[arg];
}
/// Function given to ExpandGeneric() to obtain the list of values for -complete.
@@ -360,9 +417,8 @@ char *get_user_cmd_complete(expand_T *xp, int idx)
char *cmd_compl = get_command_complete(idx);
if (cmd_compl == NULL) {
return "";
- } else {
- return cmd_compl;
}
+ return cmd_compl;
}
int cmdcomplete_str_to_type(const char *complete_str)
@@ -396,7 +452,7 @@ static void uc_list(char *name, size_t name_len)
// Skip commands which don't match the requested prefix and
// commands filtered out.
- if (STRNCMP(name, cmd->uc_name, name_len) != 0
+ if (strncmp(name, cmd->uc_name, name_len) != 0
|| message_filtered(cmd->uc_name)) {
continue;
}
@@ -435,7 +491,7 @@ static void uc_list(char *name, size_t name_len)
}
msg_outtrans_attr(cmd->uc_name, HL_ATTR(HLF_D));
- len = (int)STRLEN(cmd->uc_name) + 4;
+ len = (int)strlen(cmd->uc_name) + 4;
do {
msg_putchar(' ');
@@ -474,16 +530,16 @@ static void uc_list(char *name, size_t name_len)
if (a & (EX_RANGE | EX_COUNT)) {
if (a & EX_COUNT) {
// -count=N
- snprintf((char *)IObuff + len, IOSIZE, "%" PRId64 "c",
+ snprintf(IObuff + len, IOSIZE, "%" PRId64 "c",
(int64_t)cmd->uc_def);
- len += (int)STRLEN(IObuff + len);
+ len += (int)strlen(IObuff + len);
} else if (a & EX_DFLALL) {
IObuff[len++] = '%';
} else if (cmd->uc_def >= 0) {
// -range=N
- snprintf((char *)IObuff + len, IOSIZE, "%" PRId64 "",
+ snprintf(IObuff + len, IOSIZE, "%" PRId64 "",
(int64_t)cmd->uc_def);
- len += (int)STRLEN(IObuff + len);
+ len += (int)strlen(IObuff + len);
} else {
IObuff[len++] = '.';
}
@@ -498,7 +554,7 @@ static void uc_list(char *name, size_t name_len)
if (addr_type_complete[j].expand != ADDR_LINES
&& addr_type_complete[j].expand == cmd->uc_addr_type) {
STRCPY(IObuff + len, addr_type_complete[j].shortname);
- len += (int)STRLEN(IObuff + len);
+ len += (int)strlen(IObuff + len);
break;
}
}
@@ -511,7 +567,7 @@ static void uc_list(char *name, size_t name_len)
char *cmd_compl = get_command_complete(cmd->uc_compl);
if (cmd_compl != NULL) {
STRCPY(IObuff + len, get_command_complete(cmd->uc_compl));
- len += (int)STRLEN(IObuff + len);
+ len += (int)strlen(IObuff + len);
}
do {
@@ -519,7 +575,7 @@ static void uc_list(char *name, size_t name_len)
} while (len < 25 - over);
IObuff[len] = '\0';
- msg_outtrans((char *)IObuff);
+ msg_outtrans(IObuff);
if (cmd->uc_luaref != LUA_NOREF) {
char *fn = nlua_funcref_str(cmd->uc_luaref);
@@ -559,8 +615,8 @@ int parse_addr_type_arg(char *value, int vallen, cmd_addr_T *addr_type_arg)
int i, a, b;
for (i = 0; addr_type_complete[i].expand != ADDR_NONE; i++) {
- a = (int)STRLEN(addr_type_complete[i].name) == vallen;
- b = STRNCMP(value, addr_type_complete[i].name, vallen) == 0;
+ a = (int)strlen(addr_type_complete[i].name) == vallen;
+ b = strncmp(value, addr_type_complete[i].name, (size_t)vallen) == 0;
if (a && b) {
*addr_type_arg = addr_type_complete[i].expand;
break;
@@ -607,8 +663,8 @@ int parse_compl_arg(const char *value, int vallen, int *complp, uint32_t *argt,
if (get_command_complete(i) == NULL) {
continue;
}
- if ((int)STRLEN(command_complete[i]) == valend
- && STRNCMP(value, command_complete[i], valend) == 0) {
+ if ((int)strlen(command_complete[i]) == valend
+ && strncmp(value, command_complete[i], (size_t)valend) == 0) {
*complp = i;
if (i == EXPAND_BUFFERS) {
*argt |= EX_BUFNAME;
@@ -643,7 +699,7 @@ int parse_compl_arg(const char *value, int vallen, int *complp, uint32_t *argt,
}
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)
+ char **compl_arg, cmd_addr_T *addr_type_arg)
FUNC_ATTR_NONNULL_ALL
{
char *p;
@@ -754,7 +810,7 @@ invalid_count:
return FAIL;
}
- if (parse_compl_arg(val, (int)vallen, complp, argt, (char **)compl_arg)
+ if (parse_compl_arg(val, (int)vallen, complp, argt, compl_arg)
== FAIL) {
return FAIL;
}
@@ -816,7 +872,7 @@ int uc_add_command(char *name, size_t name_len, const char *rep, uint32_t argt,
char *rep_buf = NULL;
garray_T *gap;
- replace_termcodes(rep, STRLEN(rep), &rep_buf, 0, NULL, 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 = xstrdup(rep);
@@ -837,8 +893,8 @@ int uc_add_command(char *name, size_t name_len, const char *rep, uint32_t argt,
size_t len;
cmd = USER_CMD_GA(gap, i);
- len = STRLEN(cmd->uc_name);
- cmp = STRNCMP(name, cmd->uc_name, name_len);
+ len = strlen(cmd->uc_name);
+ cmp = strncmp(name, cmd->uc_name, name_len);
if (cmp == 0) {
if (name_len < len) {
cmp = -1;
@@ -883,17 +939,17 @@ int uc_add_command(char *name, size_t name_len, const char *rep, uint32_t argt,
gap->ga_len++;
- cmd->uc_name = (char_u *)p;
+ cmd->uc_name = p;
}
- cmd->uc_rep = (char_u *)rep_buf;
+ cmd->uc_rep = 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;
nlua_set_sctx(&cmd->uc_script_ctx);
- cmd->uc_compl_arg = (char_u *)compl_arg;
+ cmd->uc_compl_arg = compl_arg;
cmd->uc_compl_luaref = compl_luaref;
cmd->uc_preview_luaref = preview_luaref;
cmd->uc_addr_type = addr_type;
@@ -930,10 +986,10 @@ void ex_command(exarg_T *eap)
// Check for attributes
while (*p == '-') {
p++;
- end = (char *)skiptowhite((char_u *)p);
- if (uc_scan_attr(p, (size_t)(end - p), &argt, &def, &flags, &compl, (char_u **)&compl_arg,
+ end = skiptowhite(p);
+ if (uc_scan_attr(p, (size_t)(end - p), &argt, &def, &flags, &compl, &compl_arg,
&addr_type_arg) == FAIL) {
- return;
+ goto theend;
}
p = skipwhite(end);
}
@@ -943,7 +999,7 @@ void ex_command(exarg_T *eap)
end = uc_validate_name(name);
if (!end) {
emsg(_("E182: Invalid command name"));
- return;
+ goto theend;
}
name_len = (size_t)(end - name);
@@ -954,14 +1010,19 @@ void ex_command(exarg_T *eap)
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) {
+ } else if (name_len <= 4 && strncmp(name, "Next", name_len) == 0) {
emsg(_("E841: Reserved name, cannot be used for user defined command"));
} else if (compl > 0 && (argt & EX_EXTRA) == 0) {
emsg(_(e_complete_used_without_allowing_arguments));
} else {
uc_add_command(name, name_len, p, argt, def, flags, compl, compl_arg, LUA_NOREF, LUA_NOREF,
addr_type_arg, LUA_NOREF, eap->forceit);
+
+ return; // success
}
+
+theend:
+ xfree(compl_arg);
}
/// ":comclear"
@@ -997,7 +1058,7 @@ void ex_delcommand(exarg_T *eap)
const char *arg = eap->arg;
bool buffer_only = false;
- if (STRNCMP(arg, "-buffer", 7) == 0 && ascii_iswhite(arg[7])) {
+ if (strncmp(arg, "-buffer", 7) == 0 && ascii_iswhite(arg[7])) {
buffer_only = true;
arg = skipwhite(arg + 7);
}
@@ -1006,7 +1067,7 @@ void ex_delcommand(exarg_T *eap)
for (;;) {
for (i = 0; i < gap->ga_len; i++) {
cmd = USER_CMD_GA(gap, i);
- res = STRCMP(arg, cmd->uc_name);
+ res = strcmp(arg, cmd->uc_name);
if (res <= 0) {
break;
}
@@ -1061,11 +1122,11 @@ bool uc_split_args_iter(const char *arg, size_t arglen, size_t *end, char *buf,
buf[l++] = arg[++pos];
} else {
buf[l++] = arg[pos];
- if (ascii_iswhite(arg[pos + 1])) {
- *end = pos + 1;
- *len = l;
- return false;
- }
+ }
+ if (ascii_iswhite(arg[pos + 1])) {
+ *end = pos + 1;
+ *len = l;
+ return false;
}
}
@@ -1078,7 +1139,7 @@ bool uc_split_args_iter(const char *arg, size_t arglen, size_t *end, char *buf,
}
/// 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)
+static char *uc_split_args(char *arg, char **args, const size_t *arglens, size_t argc, size_t *lenp)
{
char *buf;
char *p;
@@ -1198,7 +1259,7 @@ static char *uc_split_args(char *arg, char **args, size_t *arglens, size_t argc,
static size_t add_cmd_modifier(char *buf, char *mod_str, bool *multi_mods)
{
- size_t result = STRLEN(mod_str);
+ size_t result = strlen(mod_str);
if (*multi_mods) {
result++;
}
@@ -1218,7 +1279,7 @@ static size_t add_cmd_modifier(char *buf, char *mod_str, bool *multi_mods)
/// 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 add_win_cmd_modifiers(char *buf, const cmdmod_T *cmod, bool *multi_mods)
{
size_t result = 0;
@@ -1237,8 +1298,18 @@ size_t add_win_cmd_modifers(char *buf, const cmdmod_T *cmod, bool *multi_mods)
// :tab
if (cmod->cmod_tab > 0) {
- result += add_cmd_modifier(buf, "tab", multi_mods);
+ int tabnr = cmod->cmod_tab - 1;
+ if (tabnr == tabpage_index(curtab)) {
+ // For compatibility, don't add a tabpage number if it is the same
+ // as the default number for :tab.
+ result += add_cmd_modifier(buf, "tab", multi_mods);
+ } else {
+ char tab_buf[NUMBUFLEN + 3];
+ snprintf(tab_buf, sizeof(tab_buf), "%dtab", tabnr);
+ result += add_cmd_modifier(buf, tab_buf, multi_mods);
+ }
}
+
// :topleft
if (cmod->cmod_split & WSP_TOP) {
result += add_cmd_modifier(buf, "topleft", multi_mods);
@@ -1247,6 +1318,10 @@ size_t add_win_cmd_modifers(char *buf, const cmdmod_T *cmod, bool *multi_mods)
if (cmod->cmod_split & WSP_VERT) {
result += add_cmd_modifier(buf, "vertical", multi_mods);
}
+ // :horizontal
+ if (cmod->cmod_split & WSP_HOR) {
+ result += add_cmd_modifier(buf, "horizontal", multi_mods);
+ }
return result;
}
@@ -1304,12 +1379,12 @@ size_t uc_mods(char *buf, const cmdmod_T *cmod, bool quote)
result += add_cmd_modifier(buf, "verbose", &multi_mods);
} else {
char verbose_buf[NUMBUFLEN];
- snprintf(verbose_buf, NUMBUFLEN, "%dverbose", verbose_value);
+ snprintf(verbose_buf, sizeof(verbose_buf), "%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);
+ result += add_win_cmd_modifiers(buf, cmod, &multi_mods);
if (quote && buf != NULL) {
buf += result - 2;
@@ -1349,7 +1424,7 @@ static size_t uc_check_code(char *code, size_t len, char *buf, ucmd_T *cmd, exar
ct_NONE,
} type = ct_NONE;
- if ((vim_strchr("qQfF", *p) != NULL) && p[1] == '-') {
+ if ((vim_strchr("qQfF", (uint8_t)(*p)) != NULL) && p[1] == '-') {
quote = (*p == 'q' || *p == 'Q') ? 1 : 2;
p += 2;
l -= 2;
@@ -1357,7 +1432,7 @@ static size_t uc_check_code(char *code, size_t len, char *buf, ucmd_T *cmd, exar
l++;
if (l <= 1) {
- type = ct_NONE;
+ // type = ct_NONE;
} else if (STRNICMP(p, "args>", l) == 0) {
type = ct_ARGS;
} else if (STRNICMP(p, "bang>", l) == 0) {
@@ -1401,13 +1476,13 @@ static size_t uc_check_code(char *code, size_t len, char *buf, ucmd_T *cmd, exar
switch (quote) {
case 0: // No quoting, no splitting
- result = STRLEN(eap->arg);
+ result = strlen(eap->arg);
if (buf != NULL) {
STRCPY(buf, eap->arg);
}
break;
case 1: // Quote, but don't split
- result = STRLEN(eap->arg) + 2;
+ result = strlen(eap->arg) + 2;
for (p = eap->arg; *p; p++) {
if (*p == '\\' || *p == '"') {
result++;
@@ -1471,7 +1546,7 @@ static size_t uc_check_code(char *code, size_t len, char *buf, ucmd_T *cmd, exar
size_t num_len;
snprintf(num_buf, sizeof(num_buf), "%" PRId64, (int64_t)num);
- num_len = STRLEN(num_buf);
+ num_len = strlen(num_buf);
result = num_len;
if (quote) {
@@ -1569,7 +1644,7 @@ int do_ucmd(exarg_T *eap, bool preview)
// Second round: copy result into "buf".
buf = NULL;
for (;;) {
- p = (char *)cmd->uc_rep; // source
+ p = cmd->uc_rep; // source
q = buf; // destination
totlen = 0;
@@ -1579,10 +1654,10 @@ int do_ucmd(exarg_T *eap, bool preview)
end = vim_strchr(start + 1, '>');
}
if (buf != NULL) {
- for (ksp = p; *ksp != NUL && (char_u)(*ksp) != K_SPECIAL; ksp++) {}
- if ((char_u)(*ksp) == K_SPECIAL
+ for (ksp = p; *ksp != NUL && (uint8_t)(*ksp) != K_SPECIAL; ksp++) {}
+ if ((uint8_t)(*ksp) == K_SPECIAL
&& (start == NULL || ksp < start || end == NULL)
- && ((char_u)ksp[1] == KS_SPECIAL && ksp[2] == KE_FILLER)) {
+ && ((uint8_t)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.
@@ -1633,7 +1708,7 @@ int do_ucmd(exarg_T *eap, bool preview)
break;
}
- totlen += STRLEN(p); // Add on the trailing characters
+ totlen += strlen(p); // Add on the trailing characters
buf = xmalloc(totlen + 1);
}
diff --git a/src/nvim/usercmd.h b/src/nvim/usercmd.h
index 637862b216..b6bf6c1e33 100644
--- a/src/nvim/usercmd.h
+++ b/src/nvim/usercmd.h
@@ -1,17 +1,22 @@
#ifndef NVIM_USERCMD_H
#define NVIM_USERCMD_H
+#include <stdint.h>
+
+#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds_defs.h"
+#include "nvim/garray.h"
+#include "nvim/types.h"
typedef struct ucmd {
- char_u *uc_name; // The command name
+ char *uc_name; // The command name
uint32_t uc_argt; // The argument type
- char_u *uc_rep; // The command's replacement string
+ char *uc_rep; // The command's replacement string
long uc_def; // The default value for a range/count
int uc_compl; // completion type
cmd_addr_T uc_addr_type; // The command's address type
sctx_T uc_script_ctx; // SCTX where the command was defined
- char_u *uc_compl_arg; // completion argument if any
+ char *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
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 0667243bc3..417e5116a5 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -7,34 +7,43 @@
/// Vim originated from Stevie version 3.6 (Fish disk 217) by GRWalter (Fred).
#include <assert.h>
-#include <inttypes.h>
#include <limits.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "auto/config.h"
+#include "auto/versiondef.h" // version info generated by the build system
+#include "auto/versiondef_git.h"
+#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/drawscreen.h"
+#include "nvim/ex_cmds_defs.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
#include "nvim/grid.h"
-#include "nvim/iconv.h"
+#include "nvim/highlight_defs.h"
#include "nvim/lua/executor.h"
-#include "nvim/memline.h"
+#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
+#include "nvim/option_defs.h"
+#include "nvim/os/os_defs.h"
#include "nvim/strings.h"
#include "nvim/version.h"
#include "nvim/vim.h"
-// version info generated by the build system
-#include "auto/versiondef.h"
-
// for ":version", ":intro", and "nvim --version"
#ifndef NVIM_VERSION_MEDIUM
# define NVIM_VERSION_MEDIUM "v" STR(NVIM_VERSION_MAJOR) \
"." STR(NVIM_VERSION_MINOR) "." STR(NVIM_VERSION_PATCH) \
NVIM_VERSION_PRERELEASE
#endif
-#define NVIM_VERSION_LONG "NVIM " NVIM_VERSION_MEDIUM
+#define NVIM_VERSION_LONG "NVIM " NVIM_VERSION_MEDIUM // NOLINT(bugprone-suspicious-missing-comma)
char *Version = VIM_VERSION_SHORT;
char *longVersion = NVIM_VERSION_LONG;
@@ -55,29 +64,593 @@ static char *features[] = {
"-acl",
#endif
-#if defined(HAVE_ICONV)
- "+iconv",
-#else
- "-iconv",
-#endif
-
-#ifdef FEAT_TUI
"+tui",
-#else
- "-tui",
-#endif
NULL
};
// clang-format off
static const int included_patches[] = {
- 1850,
+ 2424,
+ 2423,
+ 2422,
+ 2421,
+ // 2420,
+ 2419,
+ // 2418,
+ 2417,
+ 2416,
+ // 2415,
+ 2414,
+ 2413,
+ 2412,
+ 2411,
+ 2410,
+ 2409,
+ 2408,
+ 2407,
+ 2406,
+ 2405,
+ 2404,
+ // 2403,
+ 2402,
+ 2401,
+ 2400,
+ // 2399,
+ 2398,
+ 2397,
+ 2396,
+ 2395,
+ 2394,
+ 2393,
+ 2392,
+ 2391,
+ 2390,
+ 2389,
+ 2388,
+ 2387,
+ // 2386,
+ 2385,
+ 2384,
+ 2383,
+ 2382,
+ // 2381,
+ 2380,
+ 2379,
+ 2378,
+ 2377,
+ 2376,
+ 2375,
+ 2374,
+ // 2373,
+ 2372,
+ // 2371,
+ 2370,
+ // 2369,
+ 2368,
+ 2367,
+ 2366,
+ 2365,
+ 2364,
+ 2363,
+ // 2362,
+ 2361,
+ 2360,
+ 2359,
+ 2358,
+ 2357,
+ 2356,
+ 2355,
+ 2354,
+ 2353,
+ 2352,
+ // 2351,
+ 2350,
+ 2349,
+ 2348,
+ 2347,
+ 2346,
+ 2345,
+ 2344,
+ 2343,
+ 2342,
+ 2341,
+ 2340,
+ 2339,
+ 2338,
+ // 2337,
+ 2336,
+ 2335,
+ // 2334,
+ 2333,
+ 2332,
+ 2331,
+ 2330,
+ 2329,
+ 2328,
+ 2327,
+ 2326,
+ 2325,
+ // 2324,
+ 2323,
+ 2322,
+ 2321,
+ 2320,
+ 2319,
+ 2318,
+ 2317,
+ 2316,
+ 2315,
+ 2314,
+ 2313,
+ 2312,
+ 2311,
+ 2310,
+ 2309,
+ // 2308,
+ // 2307,
+ 2306,
+ 2305,
+ 2304,
+ 2303,
+ 2302,
+ 2301,
+ // 2300,
+ // 2299,
+ // 2298,
+ 2297,
+ // 2296,
+ // 2295,
+ 2294,
+ 2293,
+ // 2292,
+ 2291,
+ 2290,
+ 2289,
+ // 2288,
+ // 2287,
+ // 2286,
+ 2285,
+ 2284,
+ 2283,
+ 2282,
+ 2281,
+ 2280,
+ // 2279,
+ 2278,
+ // 2277,
+ // 2276,
+ 2275,
+ 2274,
+ // 2273,
+ 2272,
+ 2271,
+ 2270,
+ 2269,
+ 2268,
+ 2267,
+ // 2266,
+ // 2265,
+ 2264,
+ 2263,
+ 2262,
+ 2261,
+ 2260,
+ 2259,
+ 2258,
+ 2257,
+ 2256,
+ 2255,
+ 2254,
+ 2253,
+ 2252,
+ // 2251,
+ // 2250,
+ 2249,
+ 2248,
+ 2247,
+ 2246,
+ 2245,
+ 2244,
+ 2243,
+ 2242,
+ // 2241,
+ // 2240,
+ 2239,
+ 2238,
+ 2237,
+ 2236,
+ 2235,
+ 2234,
+ 2233,
+ // 2232,
+ 2231,
+ // 2230,
+ 2229,
+ 2228,
+ 2227,
+ 2226,
+ 2225,
+ 2224,
+ 2223,
+ 2222,
+ 2221,
+ 2220,
+ // 2219,
+ 2218,
+ 2217,
+ // 2216,
+ // 2215,
+ 2214,
+ // 2213,
+ 2212,
+ // 2211,
+ // 2210,
+ 2209,
+ // 2208,
+ 2207,
+ 2206,
+ 2205,
+ 2204,
+ 2203,
+ 2202,
+ 2201,
+ // 2200,
+ 2199,
+ 2198,
+ 2197,
+ 2196,
+ // 2195,
+ // 2194,
+ // 2193,
+ // 2192,
+ 2191,
+ 2190,
+ // 2189,
+ 2188,
+ 2187,
+ 2186,
+ 2185,
+ 2184,
+ 2183,
+ 2182,
+ // 2181,
+ 2180,
+ 2179,
+ 2178,
+ 2177,
+ // 2176,
+ 2175,
+ 2174,
+ 2173,
+ 2172,
+ 2171,
+ 2170,
+ 2169,
+ 2168,
+ 2167,
+ 2166,
+ 2165,
+ // 2164,
+ 2163,
+ 2162,
+ 2161,
+ 2160,
+ 2159,
+ // 2158,
+ 2157,
+ // 2156,
+ // 2155,
+ 2154,
+ // 2153,
+ 2152,
+ 2151,
+ 2150,
+ 2149,
+ // 2148,
+ 2147,
+ // 2146,
+ 2145,
+ 2144,
+ 2143,
+ // 2142,
+ 2141,
+ 2140,
+ // 2139,
+ 2138,
+ 2137,
+ 2136,
+ 2135,
+ 2134,
+ 2133,
+ 2132,
+ 2131,
+ 2130,
+ 2129,
+ 2128,
+ // 2127,
+ 2126,
+ 2125,
+ 2124,
+ 2123,
+ 2122,
+ // 2121,
+ 2120,
+ 2119,
+ 2118,
+ 2117,
+ 2116,
+ 2115,
+ // 2114,
+ 2113,
+ 2112,
+ 2111,
+ // 2110,
+ // 2109,
+ 2108,
+ // 2107,
+ 2106,
+ 2105,
+ 2104,
+ 2103,
+ 2102,
+ 2101,
+ 2100,
+ // 2099,
+ 2098,
+ 2097,
+ 2096,
+ 2095,
+ // 2094,
+ // 2093,
+ // 2092,
+ 2091,
+ 2090,
+ 2089,
+ 2088,
+ 2087,
+ 2086,
+ // 2085,
+ 2084,
+ 2083,
+ 2082,
+ 2081,
+ // 2080,
+ 2079,
+ 2078,
+ // 2077,
+ // 2076,
+ 2075,
+ 2074,
+ 2073,
+ 2072,
+ // 2071,
+ // 2070,
+ // 2069,
+ // 2068,
+ // 2067,
+ // 2066,
+ 2065,
+ 2064,
+ 2063,
+ // 2062,
+ 2061,
+ 2060,
+ 2059,
+ 2058,
+ 2057,
+ 2056,
+ 2055,
+ 2054,
+ // 2053,
+ 2052,
+ 2051,
+ 2050,
+ 2049,
+ // 2048,
+ // 2047,
+ // 2046,
+ 2045,
+ // 2044,
+ 2043,
+ 2042,
+ 2041,
+ // 2040,
+ // 2039,
+ 2038,
+ 2037,
+ 2036,
+ 2035,
+ 2034,
+ 2033,
+ // 2032,
+ 2031,
+ 2030,
+ 2029,
+ 2028,
+ 2027,
+ 2026,
+ 2025,
+ 2024,
+ 2023,
+ // 2022,
+ // 2021,
+ 2020,
+ 2019,
+ 2018,
+ 2017,
+ 2016,
+ 2015,
+ 2014,
+ 2013,
+ 2012,
+ 2011,
+ 2010,
+ // 2009,
+ // 2008,
+ 2007,
+ 2006,
+ 2005,
+ // 2004,
+ 2003,
+ 2002,
+ 2001,
+ 2000,
+ // 1999,
+ // 1998,
+ // 1997,
+ // 1996,
+ 1995,
+ 1994,
+ // 1993,
+ 1992,
+ 1991,
+ 1990,
+ // 1989,
+ 1988,
+ // 1987,
+ // 1986,
+ // 1985,
+ 1984,
+ 1983,
+ // 1982,
+ // 1981,
+ 1980,
+ // 1979,
+ // 1978,
+ 1977,
+ 1976,
+ 1975,
+ 1974,
+ 1973,
+ 1972,
+ 1971,
+ 1970,
+ // 1969,
+ // 1968,
+ 1967,
+ 1966,
+ 1965,
+ // 1964,
+ // 1963,
+ 1962,
+ 1961,
+ 1960,
+ // 1959,
+ 1958,
+ // 1957,
+ 1956,
+ 1955,
+ // 1954,
+ 1953,
+ 1952,
+ 1951,
+ 1950,
+ 1949,
+ 1948,
+ 1947,
+ 1946,
+ // 1945,
+ // 1944,
+ // 1943,
+ 1942,
+ 1941,
+ // 1940,
+ // 1939,
+ 1938,
+ 1937,
+ // 1936,
+ 1935,
+ // 1934,
+ 1933,
+ 1932,
+ 1931,
+ 1930,
+ // 1929,
+ // 1928,
+ 1927,
+ 1926,
+ 1925,
+ 1924,
+ 1923,
+ 1922,
+ 1921,
+ // 1920,
+ // 1919,
+ // 1918,
+ // 1917,
+ 1916,
+ 1915,
+ 1914,
+ 1913,
+ 1912,
+ 1911,
+ 1910,
+ 1909,
+ // 1908,
+ // 1907,
+ // 1906,
+ // 1905,
+ // 1904,
+ 1903,
+ // 1902,
+ 1901,
+ 1900,
+ 1899,
+ 1898,
+ 1897,
+ 1896,
+ 1895,
+ 1894,
+ 1893,
+ // 1892,
+ // 1891,
+ 1890,
+ 1889,
+ 1888,
+ 1887,
+ 1886,
+ 1885,
+ // 1884,
+ 1883,
+ // 1882,
+ 1881,
+ // 1880,
+ 1879,
+ 1878,
+ 1877,
+ 1876,
+ 1875,
+ // 1874,
+ 1873,
+ 1872,
+ // 1871,
+ 1870,
+ 1869,
+ 1868,
+ 1867,
+ // 1866,
+ 1865,
+ 1864,
+ 1863,
+ 1862,
+ 1861,
+ 1860,
+ 1859,
+ 1858,
+ 1857,
+ 1856,
+ 1855,
+ 1854,
+ // 1853,
+ 1852,
+ // 1851,
+ // 1850,
1849,
1848,
1847,
1846,
- 1845,
- 1844,
+ // 1845,
+ // 1844,
1843,
1842,
1841,
@@ -94,7 +667,7 @@ static const int included_patches[] = {
1830,
1829,
1828,
- 1827,
+ // 1827,
1826,
1825,
1824,
@@ -102,58 +675,58 @@ static const int included_patches[] = {
1822,
1821,
1820,
- 1819,
+ // 1819,
1818,
1817,
1816,
1815,
- 1814,
- 1813,
+ // 1814,
+ // 1813,
1812,
- 1811,
- 1810,
+ // 1811,
+ // 1810,
1809,
1808,
1807,
1806,
1805,
- // 1804,
+ 1804,
1803,
- 1802,
+ // 1802,
1801,
1800,
- 1799,
+ // 1799,
1798,
1797,
1796,
1795,
- // 1794,
+ 1794,
1793,
1792,
1791,
1790,
- 1789,
+ // 1789,
1788,
- 1787,
- 1786,
+ // 1787,
+ // 1786,
1785,
- 1784,
+ // 1784,
1783,
1782,
1781,
1780,
- 1779,
- 1778,
+ // 1779,
+ // 1778,
1777,
1776,
1775,
1774,
- 1773,
+ // 1773,
1772,
1771,
- 1770,
+ // 1770,
1769,
- 1768,
+ // 1768,
1767,
1766,
1765,
@@ -166,34 +739,34 @@ static const int included_patches[] = {
1758,
1757,
1756,
- 1755,
- 1754,
- 1753,
- 1752,
- 1751,
+ // 1755,
+ // 1754,
+ // 1753,
+ // 1752,
+ // 1751,
1750,
1749,
1748,
1747,
1746,
1745,
- // 1744,
- // 1743,
+ 1744,
+ 1743,
1742,
1741,
1740,
1739,
1738,
1737,
- 1736,
+ // 1736,
1735,
1734,
- 1733,
- // 1732,
+ // 1733,
+ 1732,
1731,
- 1730,
+ // 1730,
1729,
- 1728,
+ // 1728,
1727,
1726,
1725,
@@ -202,27 +775,27 @@ static const int included_patches[] = {
1722,
1721,
1720,
- 1719,
- 1718,
+ // 1719,
+ // 1718,
1717,
1716,
1715,
- 1714,
- 1713,
+ // 1714,
+ // 1713,
1712,
- 1711,
- 1710,
- 1709,
+ // 1711,
+ // 1710,
+ // 1709,
1708,
- 1707,
+ // 1707,
1706,
1705,
1704,
- 1703,
+ // 1703,
1702,
1701,
- 1700,
- 1699,
+ // 1700,
+ // 1699,
1698,
1697,
1696,
@@ -231,7 +804,7 @@ static const int included_patches[] = {
1693,
1692,
1691,
- 1690,
+ // 1690,
1689,
1688,
1687,
@@ -240,140 +813,140 @@ static const int included_patches[] = {
1684,
1683,
1682,
- 1681,
+ // 1681,
1680,
1679,
- 1678,
+ // 1678,
1677,
- 1676,
- 1675,
+ // 1676,
+ // 1675,
1674,
- 1673,
+ // 1673,
1672,
1671,
1670,
1669,
1668,
1667,
- 1666,
- 1665,
+ // 1666,
+ // 1665,
1664,
- 1663,
+ // 1663,
1662,
1661,
1660,
- 1659,
+ // 1659,
1658,
1657,
- 1656,
- 1655,
+ // 1656,
+ // 1655,
1654,
1653,
1652,
1651,
1650,
- 1649,
+ // 1649,
1648,
1647,
- 1646,
+ // 1646,
1645,
1644,
1643,
1642,
- 1641,
+ // 1641,
1640,
1639,
1638,
1637,
- 1636,
+ // 1636,
1635,
1634,
1633,
1632,
1631,
1630,
- 1629,
- 1628,
+ // 1629,
+ // 1628,
1627,
- 1626,
+ // 1626,
1625,
1624,
1623,
- 1622,
+ // 1622,
1621,
- 1620,
+ // 1620,
1619,
1618,
- 1617,
+ // 1617,
1616,
- 1615,
+ // 1615,
1614,
1613,
- 1612,
+ // 1612,
1611,
1610,
- 1609,
+ // 1609,
1608,
- 1607,
+ // 1607,
1606,
1605,
1604,
- 1603,
- 1602,
+ // 1603,
+ // 1602,
1601,
- 1600,
- 1599,
+ // 1600,
+ // 1599,
1598,
- 1597,
- 1596,
+ // 1597,
+ // 1596,
1595,
1594,
1593,
- // 1592,
+ 1592,
1591,
1590,
- 1589,
+ // 1589,
1588,
1587,
- 1586,
+ // 1586,
1585,
- 1584,
- 1583,
+ // 1584,
+ // 1583,
1582,
1581,
- 1580,
+ // 1580,
1579,
1578,
- 1577,
+ // 1577,
1576,
1575,
- 1574,
- 1573,
+ // 1574,
+ // 1573,
1572,
- 1571,
+ // 1571,
1570,
1569,
1568,
1567,
1566,
- 1565,
+ // 1565,
1564,
1563,
- 1562,
- 1561,
- 1560,
- 1559,
- 1558,
+ // 1562,
+ // 1561,
+ // 1560,
+ // 1559,
+ // 1558,
1557,
1556,
- 1555,
+ // 1555,
1554,
- 1553,
+ // 1553,
1552,
- 1551,
- 1550,
+ // 1551,
+ // 1550,
1549,
- 1548,
+ // 1548,
1547,
1546,
1545,
@@ -383,59 +956,59 @@ static const int included_patches[] = {
1541,
1540,
1539,
- 1538,
- 1537,
+ // 1538,
+ // 1537,
1536,
1535,
- 1534,
+ // 1534,
1533,
1532,
1531,
1530,
1529,
1528,
- 1527,
- 1526,
- 1525,
+ // 1527,
+ // 1526,
+ // 1525,
1524,
- 1523,
- 1522,
- 1521,
- 1520,
+ // 1523,
+ // 1522,
+ // 1521,
+ // 1520,
1519,
- 1518,
- 1517,
+ // 1518,
+ // 1517,
1516,
- 1515,
+ // 1515,
1514,
- 1513,
+ // 1513,
1512,
- 1511,
+ // 1511,
1510,
1509,
- 1508,
+ // 1508,
1507,
1506,
1505,
1504,
1503,
- 1502,
+ // 1502,
1501,
1500,
- 1499,
+ // 1499,
1498,
- 1497,
- 1496,
- 1495,
- 1494,
- 1493,
+ // 1497,
+ // 1496,
+ // 1495,
+ // 1494,
+ // 1493,
1492,
1491,
1490,
1489,
1488,
1487,
- 1486,
+ // 1486,
1485,
1484,
1483,
@@ -448,7 +1021,7 @@ static const int included_patches[] = {
1476,
1475,
1474,
- 1473,
+ // 1473,
1472,
1471,
1470,
@@ -458,83 +1031,83 @@ static const int included_patches[] = {
1466,
1465,
1464,
- 1463,
+ // 1463,
1462,
1461,
- 1460,
- 1459,
+ // 1460,
+ // 1459,
1458,
1457,
1456,
- 1455,
+ // 1455,
1454,
- 1453,
- 1452,
- 1451,
- 1450,
- 1449,
- 1448,
- 1447,
- 1446,
- 1445,
- 1444,
- 1443,
- 1442,
- 1441,
+ // 1453,
+ // 1452,
+ // 1451,
+ // 1450,
+ // 1449,
+ // 1448,
+ // 1447,
+ // 1446,
+ // 1445,
+ // 1444,
+ // 1443,
+ // 1442,
+ // 1441,
1440,
1439,
- 1438,
+ // 1438,
1437,
1436,
1435,
1434,
1433,
- 1432,
- 1431,
- 1430,
- 1429,
- 1428,
- 1427,
- 1426,
+ // 1432,
+ // 1431,
+ // 1430,
+ // 1429,
+ // 1428,
+ // 1427,
+ // 1426,
1425,
1424,
- 1423,
- 1422,
- 1421,
- 1420,
- 1419,
+ // 1423,
+ // 1422,
+ // 1421,
+ // 1420,
+ // 1419,
1418,
- 1417,
- 1416,
+ // 1417,
+ // 1416,
1415,
- 1414,
- 1413,
+ // 1414,
+ // 1413,
1412,
1411,
- 1410,
+ // 1410,
1409,
- 1408,
- 1407,
- 1406,
- 1405,
+ // 1408,
+ // 1407,
+ // 1406,
+ // 1405,
1404,
1403,
- 1402,
+ // 1402,
1401,
- 1400,
- 1399,
+ // 1400,
+ // 1399,
1398,
1397,
- 1396,
- 1395,
+ // 1396,
+ // 1395,
1394,
1393,
1392,
- 1391,
+ // 1391,
1390,
- 1389,
- 1388,
- 1387,
+ // 1389,
+ // 1388,
+ // 1387,
1386,
1385,
1384,
@@ -545,7 +1118,7 @@ static const int included_patches[] = {
1379,
1378,
1377,
- 1376,
+ // 1376,
1375,
1374,
1373,
@@ -557,12 +1130,12 @@ static const int included_patches[] = {
1367,
1366,
1365,
- 1364,
+ // 1364,
1363,
1362,
1361,
1360,
- 1359,
+ // 1359,
1358,
1357,
1356,
@@ -570,38 +1143,38 @@ static const int included_patches[] = {
1354,
1353,
1352,
- 1351,
+ // 1351,
1350,
1349,
1348,
1347,
1346,
1345,
- 1344,
- 1343,
+ // 1344,
+ // 1343,
1342,
- 1341,
- 1340,
+ // 1341,
+ // 1340,
1339,
1338,
- 1337,
+ // 1337,
1336,
- 1335,
+ // 1335,
1334,
- 1333,
- 1332,
+ // 1333,
+ // 1332,
1331,
1330,
- 1329,
- 1328,
+ // 1329,
+ // 1328,
1327,
- 1326,
+ // 1326,
1325,
1324,
1323,
1322,
- 1321,
- 1320,
+ // 1321,
+ // 1320,
1319,
1318,
1317,
@@ -621,7 +1194,7 @@ static const int included_patches[] = {
1303,
1302,
1301,
- // 1300,
+ 1300,
1299,
1298,
1297,
@@ -641,11 +1214,11 @@ static const int included_patches[] = {
1283,
1282,
1281,
- 1280,
+ // 1280,
1279,
- 1278,
+ // 1278,
1277,
- 1276,
+ // 1276,
1275,
1274,
1273,
@@ -654,15 +1227,15 @@ static const int included_patches[] = {
1270,
1269,
1268,
- 1267,
+ // 1267,
1266,
- 1265,
+ // 1265,
1264,
1263,
1262,
1261,
1260,
- 1259,
+ // 1259,
1258,
1257,
1256,
@@ -695,15 +1268,15 @@ static const int included_patches[] = {
1229,
1228,
1227,
- 1226,
+ // 1226,
1225,
- 1224,
+ // 1224,
1223,
1222,
1221,
1220,
1219,
- 1218,
+ // 1218,
1217,
1216,
1215,
@@ -785,10 +1358,10 @@ static const int included_patches[] = {
1139,
1138,
1137,
- 1136,
+ // 1136,
1135,
1134,
- 1133,
+ // 1133,
1132,
1131,
1130,
@@ -815,10 +1388,10 @@ static const int included_patches[] = {
1109,
1108,
1107,
- 1106,
+ // 1106,
1105,
1104,
- 1103,
+ // 1103,
1102,
1101,
1100,
@@ -831,14 +1404,14 @@ static const int included_patches[] = {
1093,
1092,
1091,
- 1090,
+ // 1090,
1089,
1088,
1087,
1086,
1085,
1084,
- 1083,
+ // 1083,
1082,
1081,
1080,
@@ -877,8 +1450,8 @@ static const int included_patches[] = {
1047,
1046,
1045,
- 1044,
- 1043,
+ // 1044,
+ // 1043,
1042,
1041,
1040,
@@ -886,14 +1459,14 @@ static const int included_patches[] = {
1038,
1037,
1036,
- 1035,
+ // 1035,
1034,
1033,
1032,
1031,
1030,
1029,
- 1028,
+ // 1028,
1027,
1026,
1025,
@@ -902,7 +1475,7 @@ static const int included_patches[] = {
1022,
1021,
1020,
- 1019,
+ // 1019,
1018,
1017,
1016,
@@ -917,7 +1490,7 @@ static const int included_patches[] = {
1007,
1006,
1005,
- 1004,
+ // 1004,
1003,
1002,
1001,
@@ -944,15 +1517,15 @@ static const int included_patches[] = {
980,
979,
978,
- 977,
+ // 977,
976,
975,
974,
973,
972,
971,
- 970,
- 969,
+ // 970,
+ // 969,
968,
967,
966,
@@ -968,7 +1541,7 @@ static const int included_patches[] = {
956,
955,
954,
- 953,
+ // 953,
952,
951,
950,
@@ -981,7 +1554,7 @@ static const int included_patches[] = {
943,
942,
941,
- 940,
+ // 940,
939,
938,
937,
@@ -993,7 +1566,7 @@ static const int included_patches[] = {
931,
930,
929,
- 928,
+ // 928,
927,
926,
925,
@@ -1003,19 +1576,19 @@ static const int included_patches[] = {
921,
920,
919,
- 918,
+ // 918,
917,
916,
915,
- 914,
+ // 914,
913,
912,
911,
910,
- 909,
+ // 909,
908,
907,
- 906,
+ // 906,
905,
904,
903,
@@ -1026,12 +1599,12 @@ static const int included_patches[] = {
898,
897,
896,
- 895,
- 894,
+ // 895,
+ // 894,
893,
892,
891,
- 890,
+ // 890,
889,
888,
887,
@@ -1041,30 +1614,30 @@ static const int included_patches[] = {
883,
882,
881,
- 880,
- 879,
+ // 880,
+ // 879,
878,
877,
- 876,
+ // 876,
875,
874,
873,
872,
871,
- 870,
+ // 870,
869,
868,
867,
866,
865,
864,
- 863,
+ // 863,
862,
861,
860,
859,
858,
- 857,
+ // 857,
856,
855,
854,
@@ -1076,7 +1649,7 @@ static const int included_patches[] = {
848,
847,
846,
- 845,
+ // 845,
844,
843,
842,
@@ -1097,16 +1670,16 @@ static const int included_patches[] = {
827,
826,
825,
- 824,
+ // 824,
823,
822,
821,
- 820,
+ // 820,
819,
818,
817,
816,
- 815,
+ // 815,
814,
813,
812,
@@ -1151,16 +1724,16 @@ static const int included_patches[] = {
773,
772,
771,
- 770,
+ // 770,
769,
- 768,
+ // 768,
767,
766,
765,
764,
763,
762,
- 761,
+ // 761,
760,
759,
758,
@@ -1211,14 +1784,14 @@ static const int included_patches[] = {
713,
712,
711,
- 710,
+ // 710,
709,
708,
- 707,
+ // 707,
706,
705,
704,
- 703,
+ // 703,
702,
701,
700,
@@ -1227,26 +1800,26 @@ static const int included_patches[] = {
697,
696,
695,
- 694,
+ // 694,
693,
692,
- 691,
- 690,
- 689,
- 688,
+ // 691,
+ // 690,
+ // 689,
+ // 688,
687,
686,
685,
- 684,
+ // 684,
683,
- 682,
- 681,
+ // 682,
+ // 681,
680,
679,
678,
677,
- 676,
- 675,
+ // 676,
+ // 675,
674,
673,
672,
@@ -1254,11 +1827,11 @@ static const int included_patches[] = {
670,
669,
668,
- 667,
+ // 667,
666,
- 665,
+ // 665,
664,
- 663,
+ // 663,
662,
661,
660,
@@ -1266,8 +1839,8 @@ static const int included_patches[] = {
658,
657,
656,
- 655,
- 654,
+ // 655,
+ // 654,
653,
652,
651,
@@ -1278,16 +1851,16 @@ static const int included_patches[] = {
646,
645,
644,
- 643,
+ // 643,
642,
641,
640,
- 639,
- 638,
+ // 639,
+ // 638,
637,
- 636,
+ // 636,
635,
- 634,
+ // 634,
633,
632,
631,
@@ -1309,7 +1882,7 @@ static const int included_patches[] = {
615,
614,
613,
- 612,
+ // 612,
611,
610,
609,
@@ -1320,7 +1893,7 @@ static const int included_patches[] = {
604,
603,
602,
- 601,
+ // 601,
600,
599,
598,
@@ -1339,10 +1912,10 @@ static const int included_patches[] = {
585,
584,
583,
- 582,
+ // 582,
581,
580,
- 579,
+ // 579,
578,
577,
576,
@@ -1496,7 +2069,7 @@ static const int included_patches[] = {
428,
427,
426,
- 425,
+ // 425,
424,
423,
422,
@@ -1554,7 +2127,7 @@ static const int included_patches[] = {
370,
369,
368,
- 367,
+ // 367,
366,
365,
364,
@@ -1593,7 +2166,7 @@ static const int included_patches[] = {
331,
330,
329,
- 328,
+ // 328,
327,
326,
325,
@@ -1609,7 +2182,7 @@ static const int included_patches[] = {
315,
314,
313,
- 312,
+ // 312,
311,
310,
309,
@@ -1617,7 +2190,7 @@ static const int included_patches[] = {
307,
306,
305,
- 304,
+ // 304,
303,
302,
301,
@@ -1763,7 +2336,7 @@ static const int included_patches[] = {
161,
160,
159,
- 158,
+ // 158,
157,
156,
155,
@@ -1834,7 +2407,7 @@ static const int included_patches[] = {
90,
89,
88,
- 87,
+ // 87,
86,
85,
84,
@@ -1849,21 +2422,21 @@ static const int included_patches[] = {
75,
74,
73,
- 72,
+ // 72,
71,
70,
69,
68,
67,
66,
- 65,
+ // 65,
64,
63,
- 62,
+ // 62,
61,
60,
59,
- 58,
+ // 58,
57,
56,
55,
@@ -1871,8 +2444,8 @@ static const int included_patches[] = {
53,
52,
51,
- 50,
- 49,
+ // 50,
+ // 49,
48,
47,
46,
@@ -1921,7 +2494,7 @@ static const int included_patches[] = {
3,
2,
1,
- 0,
+ // 0,
};
// clang-format on
@@ -2016,7 +2589,7 @@ void ex_version(exarg_T *eap)
/// Output a string for the version message. If it's going to wrap, output a
/// newline, unless the message is too long to fit on the screen anyway.
-/// When "wrap" is TRUE wrap the string in [].
+/// When "wrap" is true wrap the string in [].
/// @param s
/// @param wrap
static void version_msg_wrap(char *s, int wrap)
@@ -2072,7 +2645,7 @@ void list_in_columns(char **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((char *)items[i]) + (i == current ? 2 : 0);
+ int l = vim_strsize(items[i]) + (i == current ? 2 : 0);
if (l > width) {
width = l;
@@ -2084,7 +2657,7 @@ void list_in_columns(char **items, int size, int current)
if (Columns < width) {
// Not enough screen columns - show one per line
for (i = 0; i < item_count; i++) {
- version_msg_wrap((char *)items[i], i == current);
+ version_msg_wrap(items[i], i == current);
if (msg_col > 0 && i < item_count - 1) {
msg_putchar('\n');
}
@@ -2106,7 +2679,7 @@ void list_in_columns(char **items, int size, int current)
if (idx == current) {
msg_putchar('[');
}
- msg_puts((char *)items[idx]);
+ msg_puts(items[idx]);
if (idx == current) {
msg_putchar(']');
}
@@ -2200,7 +2773,7 @@ void maybe_intro_message(void)
if (buf_is_empty(curbuf)
&& (curbuf->b_fname == NULL)
&& (firstwin->w_next == NULL)
- && (vim_strchr((char *)p_shm, SHM_INTRO) == NULL)) {
+ && (vim_strchr(p_shm, SHM_INTRO) == NULL)) {
intro_message(false);
}
}
@@ -2209,7 +2782,7 @@ void maybe_intro_message(void)
/// Only used when starting Vim on an empty file, without a file name.
/// Or with the ":intro" command (for Sven :-).
///
-/// @param colon TRUE for ":intro"
+/// @param colon true for ":intro"
void intro_message(int colon)
{
int i;
@@ -2217,17 +2790,21 @@ void intro_message(int colon)
long blanklines;
int sponsor;
char *p;
+ char *mesg;
+ int mesg_size;
static char *(lines[]) = {
N_(NVIM_VERSION_LONG),
"",
N_("Nvim is open source and freely distributable"),
- N_("https://neovim.io/#chat"),
+ "https://neovim.io/#chat",
"",
N_("type :help nvim<Enter> if you are new! "),
N_("type :checkhealth<Enter> to optimize Nvim"),
N_("type :q<Enter> to exit "),
N_("type :help<Enter> for help "),
"",
+ N_("type :help news<Enter> to see changes in v%s.%s"),
+ "",
N_("Help poor children in Uganda!"),
N_("type :help iccf<Enter> for information "),
};
@@ -2236,7 +2813,7 @@ void intro_message(int colon)
size_t lines_size = ARRAY_SIZE(lines);
assert(lines_size <= LONG_MAX);
- blanklines = Rows - ((long)lines_size - 1l);
+ blanklines = Rows - ((long)lines_size - 1L);
// Don't overwrite a statusline. Depends on 'cmdheight'.
if (p_ls > 1) {
@@ -2256,27 +2833,50 @@ void intro_message(int colon)
row = blanklines / 2;
if (((row >= 2) && (Columns >= 50)) || colon) {
- for (i = 0; i < (int)ARRAY_SIZE(lines); ++i) {
+ for (i = 0; i < (int)ARRAY_SIZE(lines); i++) {
p = lines[i];
+ mesg = NULL;
+ mesg_size = 0;
+
+ if (strstr(p, "news") != NULL) {
+ p = _(p);
+ mesg_size = snprintf(NULL, 0, p,
+ STR(NVIM_VERSION_MAJOR), STR(NVIM_VERSION_MINOR));
+ assert(mesg_size > 0);
+ mesg = xmallocz((size_t)mesg_size);
+ snprintf(mesg, (size_t)mesg_size + 1, p,
+ STR(NVIM_VERSION_MAJOR), STR(NVIM_VERSION_MINOR));
+ }
if (sponsor != 0) {
if (strstr(p, "children") != NULL) {
- p = sponsor < 0
- ? N_("Sponsor Vim development!")
- : N_("Become a registered Vim user!");
- } else if (strstr(p, "iccf") != NULL) {
- p = sponsor < 0
- ? N_("type :help sponsor<Enter> for information ")
- : N_("type :help register<Enter> for information ");
- } else if (strstr(p, "Orphans") != NULL) {
- p = N_("menu Help->Sponsor/Register for information ");
+ mesg = sponsor < 0
+ ? _("Sponsor Vim development!")
+ : _("Become a registered Vim user!");
+ }
+ if (strstr(p, "iccf") != NULL) {
+ mesg = sponsor < 0
+ ? _("type :help sponsor<Enter> for information ")
+ : _("type :help register<Enter> for information ");
+ }
+ }
+
+ if (mesg == NULL) {
+ if (*p != NUL) {
+ mesg = _(p);
+ } else {
+ mesg = "";
}
}
- if (*p != NUL) {
- do_intro_line(row, (char_u *)_(p), 0);
+ if (*mesg != NUL) {
+ do_intro_line(row, mesg, 0);
}
row++;
+
+ if (mesg_size > 0) {
+ XFREE_CLEAR(mesg);
+ }
}
}
@@ -2287,15 +2887,14 @@ void intro_message(int colon)
}
}
-static void do_intro_line(long row, char_u *mesg, int attr)
+static void do_intro_line(long row, char *mesg, int attr)
{
- long col;
- char_u *p;
+ char *p;
int l;
int clen;
// Center the message horizontally.
- col = vim_strsize((char *)mesg);
+ long col = vim_strsize(mesg);
col = (Columns - col) / 2;
@@ -2310,8 +2909,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((char *)p + l);
- l += utfc_ptr2len((char *)p + l) - 1;
+ clen += ptr2cells(p + l);
+ l += utfc_ptr2len(p + l) - 1;
}
assert(row <= INT_MAX && col <= INT_MAX);
grid_puts_len(&default_grid, p, l, (int)row, (int)col,
@@ -2325,7 +2924,8 @@ static void do_intro_line(long row, char_u *mesg, int attr)
/// @param eap
void ex_intro(exarg_T *eap)
{
+ // TODO(bfredl): use msg_grid instead!
screenclear();
- intro_message(TRUE);
- wait_return(TRUE);
+ intro_message(true);
+ wait_return(true);
}
diff --git a/src/nvim/version.h b/src/nvim/version.h
index a19e863152..484350edee 100644
--- a/src/nvim/version.h
+++ b/src/nvim/version.h
@@ -14,7 +14,7 @@ extern char *longVersion;
// Values that change for a new release
#define VIM_VERSION_MAJOR 8
-#define VIM_VERSION_MINOR 0
+#define VIM_VERSION_MINOR 1
// Values based on the above
#define VIM_VERSION_MAJOR_STR STR(VIM_VERSION_MAJOR)
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index 09b949bb20..c4baa911f1 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -1,7 +1,7 @@
#ifndef NVIM_VIM_H
#define NVIM_VIM_H
-#include "nvim/pos.h" // for linenr_T, MAXCOL, etc...
+#include "nvim/pos.h"
#include "nvim/types.h"
// Some defines from the old feature.h
@@ -138,7 +138,6 @@ enum {
EXPAND_USER_LIST,
EXPAND_USER_LUA,
EXPAND_SHELLCMD,
- EXPAND_CSCOPE,
EXPAND_SIGN,
EXPAND_PROFILE,
EXPAND_BEHAVE,
@@ -155,6 +154,8 @@ enum {
EXPAND_MAPCLEAR,
EXPAND_ARGLIST,
EXPAND_DIFF_BUFFERS,
+ EXPAND_BREAKPOINT,
+ EXPAND_SCRIPTNAMES,
EXPAND_CHECKHEALTH,
EXPAND_LUA,
};
@@ -167,15 +168,6 @@ enum {
#define MIN_SWAP_PAGE_SIZE 1048
#define MAX_SWAP_PAGE_SIZE 50000
-// Boolean constants
-
-#ifndef TRUE
-# define FALSE 0 // note: this is an int, not a long!
-# define TRUE 1
-#endif
-
-#define MAYBE 2 // sometimes used for a variant on TRUE
-
#define STATUS_HEIGHT 1 // height of a status line under a window
#define QF_WINHEIGHT 10 // default height for quickfix window
@@ -205,17 +197,11 @@ enum { FOLD_TEXT_LEN = 51, }; //!< buffer size for get_foldtext()
// defines to avoid typecasts from (char_u *) to (char *) and back
// (vim_strchr() is now in strings.c)
-#define STRLEN(s) strlen((char *)(s))
-#ifdef HAVE_STRNLEN
-# define STRNLEN(s, n) strnlen((char *)(s), (size_t)(n))
-#else
-# define STRNLEN(s, n) xstrnlen((char *)(s), (size_t)(n))
+#ifndef HAVE_STRNLEN
+# define strnlen xstrnlen // Older versions of SunOS may not have strnlen
#endif
-#define STRCPY(d, s) strcpy((char *)(d), (char *)(s))
-#define STRNCPY(d, s, n) strncpy((char *)(d), (char *)(s), (size_t)(n))
-#define STRLCPY(d, s, n) xstrlcpy((char *)(d), (char *)(s), (size_t)(n))
-#define STRCMP(d, s) strcmp((char *)(d), (char *)(s))
-#define STRNCMP(d, s, n) strncmp((char *)(d), (char *)(s), (size_t)(n))
+
+#define STRCPY(d, s) strcpy((char *)(d), (char *)(s)) // NOLINT(runtime/printf)
#ifdef HAVE_STRCASECMP
# define STRICMP(d, s) strcasecmp((char *)(d), (char *)(s))
#else
@@ -227,7 +213,7 @@ enum { FOLD_TEXT_LEN = 51, }; //!< buffer size for get_foldtext()
#endif
// Like strcpy() but allows overlapped source and destination.
-#define STRMOVE(d, s) memmove((d), (s), STRLEN(s) + 1)
+#define STRMOVE(d, s) memmove((d), (s), strlen(s) + 1)
#ifdef HAVE_STRNCASECMP
# define STRNICMP(d, s, n) strncasecmp((char *)(d), (char *)(s), (size_t)(n))
@@ -239,11 +225,7 @@ enum { FOLD_TEXT_LEN = 51, }; //!< buffer size for get_foldtext()
# endif
#endif
-#define STRRCHR(s, c) (char_u *)strrchr((const char *)(s), (c))
-
-#define STRCAT(d, s) strcat((char *)(d), (char *)(s))
-#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 STRCAT(d, s) strcat((char *)(d), (char *)(s)) // NOLINT(runtime/printf)
// Character used as separated in autoload function/variable names.
#define AUTOLOAD_CHAR '#'
@@ -254,27 +236,8 @@ enum { FOLD_TEXT_LEN = 51, }; //!< buffer size for get_foldtext()
// destination and mess up the screen.
#define PERROR(msg) (void)semsg("%s: %s", (msg), strerror(errno))
-#define SHOWCMD_COLS 10 // columns needed by shown command
-
#include "nvim/path.h"
-/// Compare file names
-///
-/// On some systems case in a file name does not matter, on others it does.
-///
-/// @note Does not account for maximum name lengths and things like "../dir",
-/// thus it is not 100% accurate. OS may also use different algorithm for
-/// case-insensitive comparison.
-///
-/// @param[in] x First file name to compare.
-/// @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), \
- (const char *)(y), \
- (size_t)(n))
-
// Enums need a typecast to be used as array index.
#define HL_ATTR(n) hl_attr_active[(int)(n)]
@@ -283,13 +246,11 @@ enum { FOLD_TEXT_LEN = 51, }; //!< buffer size for get_foldtext()
/// plus six following composing characters of three bytes each.
#define MB_MAXBYTES 21
-// This has to go after the include of proto.h, as proto/gui.pro declares
-// functions of these names. The declarations would break if the defines had
-// been seen at that stage. But it must be before globals.h, where error_ga
-// is declared.
-#ifndef WIN32
-# define mch_errmsg(str) fprintf(stderr, "%s", (str))
-# define mch_msg(str) printf("%s", (str))
+#ifndef MSWIN
+/// Headless (no UI) error message handler.
+# define os_errmsg(str) fprintf(stderr, "%s", (str))
+/// Headless (no UI) message handler.
+# define os_msg(str) printf("%s", (str))
#endif
#include "nvim/buffer_defs.h" // buffer and windows
diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c
index 387b9d61f2..53224f2ee9 100644
--- a/src/nvim/viml/parser/expressions.c
+++ b/src/nvim/viml/parser/expressions.c
@@ -53,21 +53,25 @@
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
+#include "klib/kvec.h"
#include "nvim/ascii.h"
#include "nvim/assert.h"
#include "nvim/charset.h"
#include "nvim/eval/typval.h"
-#include "nvim/lib/kvec.h"
+#include "nvim/gettext.h"
+#include "nvim/keycodes.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/types.h"
#include "nvim/vim.h"
#include "nvim/viml/parser/expressions.h"
#include "nvim/viml/parser/parser.h"
-#define VIM_STR2NR(s, ...) vim_str2nr((const char_u *)(s), __VA_ARGS__)
-
typedef kvec_withinit_t(ExprASTNode **, 16) ExprASTStack;
/// Which nodes may be wanted
@@ -371,7 +375,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 +393,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[] = {
@@ -630,8 +634,8 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags)
GET_CCS(ret, pline);
ret.data.cmp.inv = (schar == '<');
ret.data.cmp.type = ((ret.data.cmp.inv ^ haseqsign)
- ? kExprCmpGreaterOrEqual
- : kExprCmpGreater);
+ ? kExprCmpGreaterOrEqual
+ : kExprCmpGreater);
break;
}
@@ -696,8 +700,7 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags)
// Everything else is not valid.
default:
- ret.len = (size_t)utfc_ptr2len_len((const char_u *)pline.data,
- (int)pline.size);
+ ret.len = (size_t)utfc_ptr2len_len(pline.data, (int)pline.size);
ret.type = kExprLexInvalid;
ret.data.err.type = kExprLexPlainIdentifier;
ret.data.err.msg = _("E15: Unidentified character: %.*s");
@@ -1822,8 +1825,8 @@ static void parse_quoted_string(ParserState *const pstate, ExprASTNode *const no
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);
+ const size_t special_len = trans_special(&p, (size_t)(e - p),
+ v_p, flags, false, NULL);
if (special_len != 0) {
v_p += special_len;
} else {
@@ -1966,8 +1969,8 @@ ExprAST viml_pexpr_parse(ParserState *const pstate, const int flags)
|| ((*kv_Z(ast_stack, 1))->type != kExprNodeConcat
&& ((*kv_Z(ast_stack, 1))->type
!= kExprNodeConcatOrSubscript))))
- ? kELFlagAllowFloat
- : 0));
+ ? kELFlagAllowFloat
+ : 0));
LexExprToken cur_token = viml_pexpr_next_token(pstate,
want_node_to_lexer_flags[want_node] |
lexer_additional_flags);
@@ -2034,9 +2037,9 @@ viml_pexpr_parse_process_token:
const bool node_is_key = (
is_concat_or_subscript
&& (cur_token.type == kExprLexPlainIdentifier
- ? (!cur_token.data.var.autoload
- && cur_token.data.var.scope == kExprVarScopeMissing)
- : (cur_token.type == kExprLexNumber))
+ ? (!cur_token.data.var.autoload
+ && cur_token.data.var.scope == kExprVarScopeMissing)
+ : (cur_token.type == kExprLexNumber))
&& prev_token.type != kExprLexSpacing);
if (is_concat_or_subscript && !node_is_key) {
// Note: in Vim "d. a" (this is the reason behind `prev_token.type !=
@@ -2123,6 +2126,22 @@ viml_pexpr_parse_process_token:
assert(kv_size(pt_stack));
const ExprASTParseType cur_pt = kv_last(pt_stack);
assert(lambda_node == NULL || cur_pt == kEPTLambdaArguments);
+#define SIMPLE_UB_OP(op) \
+ case kExprLex##op: { \
+ if (want_node == kENodeValue) { \
+ /* Value level: assume unary operator. */ \
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeUnary##op); \
+ *top_node_p = cur_node; \
+ kvi_push(ast_stack, &cur_node->children); \
+ HL_CUR_TOKEN(Unary##op); \
+ } else { \
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeBinary##op); \
+ ADD_OP_NODE(cur_node); \
+ HL_CUR_TOKEN(Binary##op); \
+ } \
+ want_node = kENodeValue; \
+ break; \
+ }
switch (tok_type) {
case kExprLexMissing:
case kExprLexSpacing:
@@ -2144,22 +2163,6 @@ viml_pexpr_parse_process_token:
HL_CUR_TOKEN(Register);
break;
}
-#define SIMPLE_UB_OP(op) \
- case kExprLex##op: { \
- if (want_node == kENodeValue) { \
- /* Value level: assume unary operator. */ \
- NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeUnary##op); \
- *top_node_p = cur_node; \
- kvi_push(ast_stack, &cur_node->children); \
- HL_CUR_TOKEN(Unary##op); \
- } else { \
- NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeBinary##op); \
- ADD_OP_NODE(cur_node); \
- HL_CUR_TOKEN(Binary##op); \
- } \
- want_node = kENodeValue; \
- break; \
- }
SIMPLE_UB_OP(Plus)
SIMPLE_UB_OP(Minus)
#undef SIMPLE_UB_OP
@@ -2494,7 +2497,6 @@ viml_pexpr_parse_bracket_closing_error:
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeListLiteral);
*top_node_p = cur_node;
kvi_push(ast_stack, &cur_node->children);
- want_node = kENodeValue;
if (cur_pt == kEPTAssignment) {
// Additional assignment parse type allows to easily forbid nested
// lists.
@@ -2710,14 +2712,14 @@ viml_pexpr_parse_figure_brace_closing_error:
break;
case kExprLexPlainIdentifier: {
const ExprVarScope scope = (cur_token.type == kExprLexInvalid
- ? kExprVarScopeMissing
- : cur_token.data.var.scope);
+ ? kExprVarScopeMissing
+ : cur_token.data.var.scope);
if (want_node == kENodeValue) {
want_node = kENodeOperator;
NEW_NODE_WITH_CUR_POS(cur_node,
(node_is_key
- ? kExprNodePlainKey
- : kExprNodePlainIdentifier));
+ ? kExprNodePlainKey
+ : kExprNodePlainIdentifier));
cur_node->data.var.scope = scope;
const size_t scope_shift = (scope == kExprVarScopeMissing ? 0 : 2);
cur_node->data.var.ident = (pline.data + cur_token.start.col
@@ -2735,8 +2737,8 @@ viml_pexpr_parse_figure_brace_closing_error:
scope_shift),
cur_token.len - scope_shift,
(node_is_key
- ? HL(IdentifierKey)
- : HL(IdentifierName)));
+ ? HL(IdentifierKey)
+ : HL(IdentifierName)));
} else {
if (scope == kExprVarScopeMissing) {
// uncrustify:off
@@ -2905,15 +2907,15 @@ viml_pexpr_parse_no_paren_closing_error: {}
// different error numbers: "E114: Missing quote" and
// "E115: Missing quote".
ERROR_FROM_TOKEN_AND_MSG(cur_token, (is_double
- ? _("E114: Missing double quote: %.*s")
- : _("E115: Missing single quote: %.*s")));
+ ? _("E114: Missing double quote: %.*s")
+ : _("E115: Missing single quote: %.*s")));
}
if (want_node == kENodeOperator) {
OP_MISSING;
}
NEW_NODE_WITH_CUR_POS(cur_node, (is_double
- ? kExprNodeDoubleQuotedString
- : kExprNodeSingleQuotedString));
+ ? kExprNodeDoubleQuotedString
+ : kExprNodeSingleQuotedString));
*top_node_p = cur_node;
parse_quoted_string(pstate, cur_node, cur_token, &ast_stack, is_invalid);
want_node = kENodeOperator;
diff --git a/src/nvim/viml/parser/expressions.h b/src/nvim/viml/parser/expressions.h
index 9d0bc9d468..6fe6a784a0 100644
--- a/src/nvim/viml/parser/expressions.h
+++ b/src/nvim/viml/parser/expressions.h
@@ -6,9 +6,12 @@
#include <stdint.h>
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/types.h"
#include "nvim/viml/parser/parser.h"
+struct expr_ast_node;
+
// Defines whether to ignore case:
// == kCCStrategyUseOption
// ==# kCCStrategyMatchCase
@@ -357,7 +360,7 @@ typedef struct {
int arg_len;
} ExprASTError;
-/// Structure representing complety AST for one expression
+/// Structure representing complete AST for one expression
typedef struct {
/// When AST is not correct this message will be printed.
///
diff --git a/src/nvim/viml/parser/parser.c b/src/nvim/viml/parser/parser.c
index a41b750e76..1547feba90 100644
--- a/src/nvim/viml/parser/parser.c
+++ b/src/nvim/viml/parser/parser.c
@@ -4,7 +4,7 @@
#include "nvim/viml/parser/parser.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "viml/parser/parser.c.generated.h"
+# include "viml/parser/parser.c.generated.h" // IWYU pragma: export
#endif
void parser_simple_get_line(void *cookie, ParserLine *ret_pline)
diff --git a/src/nvim/viml/parser/parser.h b/src/nvim/viml/parser/parser.h
index b8835127e7..f387301c2d 100644
--- a/src/nvim/viml/parser/parser.h
+++ b/src/nvim/viml/parser/parser.h
@@ -5,9 +5,10 @@
#include <stdbool.h>
#include <stddef.h>
+#include "klib/kvec.h"
#include "nvim/func_attr.h"
-#include "nvim/lib/kvec.h"
#include "nvim/mbyte.h"
+#include "nvim/mbyte_defs.h"
#include "nvim/memory.h"
/// One parsed line
@@ -81,8 +82,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 *ret_pstate, ParserLineGetter get_line,
+ void *cookie, ParserHighlight *colors)
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ARG(1, 2);
/// Initialize a new parser state instance
@@ -109,7 +110,7 @@ static inline void viml_parser_init(ParserState *const ret_pstate, const ParserL
kvi_init(ret_pstate->stack);
}
-static inline void viml_parser_destroy(ParserState *const pstate)
+static inline void viml_parser_destroy(ParserState *pstate)
REAL_FATTR_NONNULL_ALL REAL_FATTR_ALWAYS_INLINE;
/// Free all memory allocated by the parser on heap
@@ -127,8 +128,7 @@ static inline void viml_parser_destroy(ParserState *const pstate)
kvi_destroy(pstate->stack);
}
-static inline void viml_preader_get_line(ParserInputReader *const preader,
- ParserLine *const ret_pline)
+static inline void viml_preader_get_line(ParserInputReader *preader, ParserLine *ret_pline)
REAL_FATTR_NONNULL_ALL;
/// Get one line from ParserInputReader
@@ -142,9 +142,7 @@ static inline void viml_preader_get_line(ParserInputReader *const preader,
.allocated = true,
.size = pline.size,
};
- cpline.data = (char *)string_convert(&preader->conv,
- (char_u *)pline.data,
- &cpline.size);
+ cpline.data = string_convert(&preader->conv, (char *)pline.data, &cpline.size);
if (pline.allocated) {
xfree((void *)pline.data);
}
@@ -154,8 +152,7 @@ static inline void viml_preader_get_line(ParserInputReader *const preader,
*ret_pline = pline;
}
-static inline bool viml_parser_get_remaining_line(ParserState *const pstate,
- ParserLine *const ret_pline)
+static inline bool viml_parser_get_remaining_line(ParserState *pstate, ParserLine *ret_pline)
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL;
/// Get currently parsed line, shifted to pstate->pos.col
@@ -180,8 +177,7 @@ static inline bool viml_parser_get_remaining_line(ParserState *const pstate,
return ret_pline->data != NULL;
}
-static inline void viml_parser_advance(ParserState *const pstate,
- const size_t len)
+static inline void viml_parser_advance(ParserState *pstate, size_t len)
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL;
/// Advance position by a given number of bytes
@@ -202,10 +198,8 @@ static inline void viml_parser_advance(ParserState *const pstate, const size_t l
}
}
-static inline void viml_parser_highlight(ParserState *const pstate,
- const ParserPosition start,
- const size_t end_col,
- const char *const group)
+static inline void viml_parser_highlight(ParserState *pstate, ParserPosition start, size_t len,
+ const char *group)
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL;
/// Record highlighting of some region of text
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 68c5b42e30..05f84b5a91 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -2,21 +2,34 @@
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include <assert.h>
+#include <ctype.h>
#include <inttypes.h>
+#include <limits.h>
#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "klib/kvec.h"
+#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
-#include "nvim/api/vim.h"
#include "nvim/arglist.h"
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/decoration.h"
#include "nvim/diff.h"
#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/eval/vars.h"
+#include "nvim/eval/window.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_cmds2.h"
#include "nvim/ex_docmd.h"
@@ -27,15 +40,19 @@
#include "nvim/fold.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
#include "nvim/hashtab.h"
-#include "nvim/highlight.h"
+#include "nvim/keycodes.h"
+#include "nvim/macros.h"
#include "nvim/main.h"
-#include "nvim/mapping.h"
+#include "nvim/map.h"
+#include "nvim/mapping.h" // IWYU pragma: keep (langmap_adjust_mb)
#include "nvim/mark.h"
#include "nvim/match.h"
-#include "nvim/memline.h"
+#include "nvim/mbyte.h"
+#include "nvim/memline_defs.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/mouse.h"
@@ -44,16 +61,19 @@
#include "nvim/option.h"
#include "nvim/optionstr.h"
#include "nvim/os/os.h"
-#include "nvim/os_unix.h"
+#include "nvim/os/os_defs.h"
#include "nvim/path.h"
#include "nvim/plines.h"
+#include "nvim/pos.h"
#include "nvim/quickfix.h"
-#include "nvim/regexp.h"
+#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/state.h"
+#include "nvim/statusline.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/terminal.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/ui_compositor.h"
#include "nvim/undo.h"
@@ -125,15 +145,10 @@ win_T *prevwin_curwin(void)
/// @param xchar extra char from ":wincmd gx" or NUL
void do_window(int nchar, long Prenum, int xchar)
{
- long Prenum1;
- win_T *wp;
- char *ptr;
- linenr_T lnum = -1;
int type = FIND_DEFINE;
- size_t len;
char cbuf[40];
- Prenum1 = Prenum == 0 ? 1 : Prenum;
+ long Prenum1 = Prenum == 0 ? 1 : Prenum;
#define CHECK_CMDWIN \
do { \
@@ -215,7 +230,7 @@ newwindow:
case Ctrl_Q:
case 'q':
reset_VIsual_and_resel(); // stop Visual mode
- cmd_with_count("quit", (char_u *)cbuf, sizeof(cbuf), Prenum);
+ cmd_with_count("quit", cbuf, sizeof(cbuf), Prenum);
do_cmdline_cmd(cbuf);
break;
@@ -223,7 +238,7 @@ newwindow:
case Ctrl_C:
case 'c':
reset_VIsual_and_resel(); // stop Visual mode
- cmd_with_count("close", (char_u *)cbuf, sizeof(cbuf), Prenum);
+ cmd_with_count("close", cbuf, sizeof(cbuf), Prenum);
do_cmdline_cmd(cbuf);
break;
@@ -236,8 +251,8 @@ newwindow:
break;
// cursor to preview window
- case 'P':
- wp = NULL;
+ case 'P': {
+ win_T *wp = NULL;
FOR_ALL_WINDOWS_IN_TAB(wp2, curtab) {
if (wp2->w_p_pvw) {
wp = wp2;
@@ -250,13 +265,14 @@ newwindow:
win_goto(wp);
}
break;
+ }
// close all but current window
case Ctrl_O:
case 'o':
CHECK_CMDWIN;
reset_VIsual_and_resel(); // stop Visual mode
- cmd_with_count("only", (char_u *)cbuf, sizeof(cbuf), Prenum);
+ cmd_with_count("only", cbuf, sizeof(cbuf), Prenum);
do_cmdline_cmd(cbuf);
break;
@@ -269,13 +285,13 @@ newwindow:
if (ONE_WINDOW && Prenum != 1) { // just one window
beep_flush();
} else {
+ win_T *wp;
if (Prenum) { // go to specified window
for (wp = firstwin; --Prenum > 0;) {
if (wp->w_next == NULL) {
break;
- } else {
- wp = wp->w_next;
}
+ wp = wp->w_next;
}
} else {
if (nchar == 'W') { // go to previous window
@@ -346,7 +362,7 @@ newwindow:
// First create a new tab with the window, then go back to
// the old tab and close the window there.
- wp = curwin;
+ win_T *wp = curwin;
if (win_new_tabpage((int)Prenum, NULL) == OK
&& valid_tabpage(oldtab)) {
newtab = curtab;
@@ -417,10 +433,12 @@ newwindow:
| ((nchar == 'H' || nchar == 'K') ? WSP_TOP : WSP_BOT));
break;
- // make all windows the same height
- case '=':
- win_equal(NULL, false, 'b');
+ // make all windows the same width and/or height
+ case '=': {
+ int mod = cmdmod.cmod_split & (WSP_VERT | WSP_HOR);
+ win_equal(NULL, false, mod == WSP_VERT ? 'v' : mod == WSP_HOR ? 'h' : 'b');
break;
+ }
// increase current window height
case '+':
@@ -479,16 +497,18 @@ newwindow:
// Execute the command right here, required when
// "wincmd ]" was used in a function.
do_nv_ident(Ctrl_RSB, NUL);
+ postponed_split = 0;
break;
// edit file name under cursor in a new window
case 'f':
case 'F':
- case Ctrl_F:
+ case Ctrl_F: {
wingotofile:
CHECK_CMDWIN;
- ptr = (char *)grab_file_name(Prenum1, &lnum);
+ linenr_T lnum = -1;
+ char *ptr = grab_file_name(Prenum1, &lnum);
if (ptr != NULL) {
tabpage_T *oldtab = curtab;
win_T *oldwin = curwin;
@@ -508,6 +528,7 @@ wingotofile:
xfree(ptr);
}
break;
+ }
// Go to the first occurrence of the identifier under cursor along path in a
// new window -- webb
@@ -516,8 +537,10 @@ wingotofile:
type = FIND_ANY;
FALLTHROUGH;
case 'd': // Go to definition, using 'define'
- case Ctrl_D:
+ case Ctrl_D: {
CHECK_CMDWIN;
+ size_t len;
+ char *ptr;
if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0) {
break;
}
@@ -525,11 +548,12 @@ wingotofile:
// Make a copy, if the line was changed it will be freed.
ptr = xstrnsave(ptr, len);
- find_pattern_in_path((char_u *)ptr, 0, len, true, Prenum == 0,
+ find_pattern_in_path(ptr, 0, len, true, Prenum == 0,
type, Prenum1, ACTION_SPLIT, 1, MAXLNUM);
xfree(ptr);
curwin->w_set_curswant = true;
break;
+ }
// Quickfix window only: view the result under the cursor in a new split.
case K_KENTER:
@@ -552,6 +576,7 @@ wingotofile:
no_mapping--;
allow_keys--;
(void)add_to_showcmd(xchar);
+
switch (xchar) {
case '}':
xchar = Ctrl_RSB;
@@ -573,6 +598,7 @@ wingotofile:
// Execute the command right here, required when
// "wincmd g}" was used in a function.
do_nv_ident('g', xchar);
+ postponed_split = 0;
break;
case 'f': // CTRL-W gf: "gf" in a new tab page
@@ -580,6 +606,7 @@ wingotofile:
cmdmod.cmod_tab = tabpage_index(curtab) + 1;
nchar = xchar;
goto wingotofile;
+
case 't': // CTRL-W gt: go to next tab page
goto_tabpage((int)Prenum);
break;
@@ -622,12 +649,12 @@ wingotofile:
}
}
-static void cmd_with_count(char *cmd, char_u *bufp, size_t bufsize, int64_t Prenum)
+static void cmd_with_count(char *cmd, char *bufp, size_t bufsize, int64_t Prenum)
{
- size_t len = STRLCPY(bufp, cmd, bufsize);
+ size_t len = xstrlcpy(bufp, cmd, bufsize);
if (Prenum > 0 && len < bufsize) {
- vim_snprintf((char *)bufp + len, bufsize - len, "%" PRId64, Prenum);
+ vim_snprintf(bufp + len, bufsize - len, "%" PRId64, Prenum);
}
}
@@ -635,11 +662,13 @@ void win_set_buf(Window window, Buffer buffer, bool noautocmd, Error *err)
{
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);
if (!win || !buf) {
return;
}
+
+ tabpage_T *tab = win_find_tabpage(win);
+
if (noautocmd) {
block_autocmds();
}
@@ -712,7 +741,7 @@ win_T *win_new_float(win_T *wp, bool last, FloatConfig fconfig, Error *err)
win_config_float(wp, fconfig);
win_set_inner_size(wp, true);
wp->w_pos_changed = true;
- redraw_later(wp, VALID);
+ redraw_later(wp, UPD_VALID);
return wp;
}
@@ -727,38 +756,44 @@ void win_set_minimal_style(win_T *wp)
// Hide EOB region: use " " fillchar and cleared highlighting
if (wp->w_p_fcs_chars.eob != ' ') {
- char_u *old = wp->w_p_fcs;
+ char *old = wp->w_p_fcs;
wp->w_p_fcs = ((*old == NUL)
- ? (char_u *)xstrdup("eob: ")
- : concat_str(old, (char_u *)",eob: "));
+ ? xstrdup("eob: ")
+ : concat_str(old, ",eob: "));
free_string_option(old);
}
// TODO(bfredl): this could use a highlight namespace directly,
- // and avoid pecularities around window options
- char_u *old = wp->w_p_winhl;
+ // and avoid peculiarities around window options
+ char *old = wp->w_p_winhl;
wp->w_p_winhl = ((*old == NUL)
- ? (char_u *)xstrdup("EndOfBuffer:")
- : concat_str(old, (char_u *)",EndOfBuffer:"));
+ ? xstrdup("EndOfBuffer:")
+ : concat_str(old, ",EndOfBuffer:"));
free_string_option(old);
parse_winhl_opt(wp);
// signcolumn: use 'auto'
- if (wp->w_p_scl[0] != 'a' || STRLEN(wp->w_p_scl) >= 8) {
+ if (wp->w_p_scl[0] != 'a' || strlen(wp->w_p_scl) >= 8) {
free_string_option(wp->w_p_scl);
- wp->w_p_scl = (char_u *)xstrdup("auto");
+ wp->w_p_scl = xstrdup("auto");
}
// foldcolumn: use '0'
if (wp->w_p_fdc[0] != '0') {
free_string_option(wp->w_p_fdc);
- wp->w_p_fdc = (char_u *)xstrdup("0");
+ wp->w_p_fdc = xstrdup("0");
}
// colorcolumn: cleared
if (wp->w_p_cc != NULL && *wp->w_p_cc != NUL) {
free_string_option(wp->w_p_cc);
- wp->w_p_cc = (char_u *)xstrdup("");
+ wp->w_p_cc = xstrdup("");
+ }
+
+ // statuscolumn: cleared
+ if (wp->w_p_stc != NULL && *wp->w_p_stc != NUL) {
+ free_string_option(wp->w_p_stc);
+ wp->w_p_stc = xstrdup("");
}
}
@@ -772,13 +807,22 @@ void win_config_float(win_T *wp, FloatConfig fconfig)
fconfig.row += curwin->w_wrow;
fconfig.col += curwin->w_wcol;
fconfig.window = curwin->handle;
+ } else if (fconfig.relative == kFloatRelativeMouse) {
+ int row = mouse_row, col = mouse_col, grid = mouse_grid;
+ win_T *mouse_win = mouse_find_win(&grid, &row, &col);
+ if (mouse_win != NULL) {
+ fconfig.relative = kFloatRelativeWindow;
+ fconfig.row += row;
+ fconfig.col += col;
+ fconfig.window = mouse_win->handle;
+ }
}
bool change_external = fconfig.external != wp->w_float_config.external;
bool change_border = (fconfig.border != wp->w_float_config.border
|| memcmp(fconfig.border_hl_ids,
wp->w_float_config.border_hl_ids,
- sizeof fconfig.border_hl_ids));
+ sizeof fconfig.border_hl_ids) != 0);
wp->w_float_config = fconfig;
@@ -797,12 +841,12 @@ void win_config_float(win_T *wp, FloatConfig fconfig)
}
win_set_inner_size(wp, true);
- must_redraw = MAX(must_redraw, VALID);
+ must_redraw = MAX(must_redraw, UPD_VALID);
wp->w_pos_changed = true;
if (change_external || change_border) {
wp->w_hl_needs_update = true;
- redraw_later(wp, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
}
// compute initial position
@@ -819,16 +863,16 @@ void win_config_float(win_T *wp, FloatConfig fconfig)
grid_adjust(&grid, &row_off, &col_off);
row += row_off;
col += col_off;
+ if (wp->w_float_config.bufpos.lnum >= 0) {
+ pos_T pos = { wp->w_float_config.bufpos.lnum + 1,
+ wp->w_float_config.bufpos.col, 0 };
+ int trow, tcol, tcolc, tcole;
+ textpos2screenpos(parent, &pos, &trow, &tcol, &tcolc, &tcole, true);
+ row += trow - 1;
+ col += tcol - 1;
+ }
}
api_clear_error(&dummy);
- if (wp->w_float_config.bufpos.lnum >= 0) {
- pos_T pos = { wp->w_float_config.bufpos.lnum + 1,
- wp->w_float_config.bufpos.col, 0 };
- int trow, tcol, tcolc, tcole;
- textpos2screenpos(wp, &pos, &trow, &tcol, &tcolc, &tcole, true);
- row += trow - 1;
- col += tcol - 1;
- }
wp->w_winrow = row;
wp->w_wincol = col;
} else {
@@ -839,7 +883,7 @@ void win_config_float(win_T *wp, FloatConfig fconfig)
// changing border style while keeping border only requires redrawing border
if (fconfig.border) {
wp->w_redr_border = true;
- redraw_later(wp, VALID);
+ redraw_later(wp, UPD_VALID);
}
}
@@ -864,13 +908,13 @@ int win_fdccol_count(win_T *wp)
const int fdccol = fdc[4] == ':' ? fdc[5] - '0' : 1;
int needed_fdccols = getDeepestNesting(wp);
return MIN(fdccol, needed_fdccols);
- } else {
- return fdc[0] - '0';
}
+ return fdc[0] - '0';
}
-void ui_ext_win_position(win_T *wp)
+void ui_ext_win_position(win_T *wp, bool validate)
{
+ wp->w_pos_changed = false;
if (!wp->w_floating) {
ui_call_win_pos(wp->w_grid_alloc.handle, wp->handle, wp->w_winrow,
wp->w_wincol, wp->w_width, wp->w_height);
@@ -908,6 +952,11 @@ void ui_ext_win_position(win_T *wp)
grid->handle, row, col, c.focusable,
wp->w_grid_alloc.zindex);
} else {
+ bool valid = (wp->w_redr_type == 0);
+ if (!valid && !validate) {
+ wp->w_pos_changed = true;
+ return;
+ }
// TODO(bfredl): ideally, compositor should work like any multigrid UI
// and use standard win_pos events.
bool east = c.anchor & kFloatAnchorEast;
@@ -921,14 +970,13 @@ void ui_ext_win_position(win_T *wp)
comp_col = MAX(MIN(comp_col, Columns - wp->w_width_outer), 0);
wp->w_winrow = comp_row;
wp->w_wincol = comp_col;
- bool valid = (wp->w_redr_type == 0);
ui_comp_put_grid(&wp->w_grid_alloc, comp_row, comp_col,
wp->w_height_outer, wp->w_width_outer, valid, false);
ui_check_cursor_grid(wp->w_grid_alloc.handle);
wp->w_grid_alloc.focusable = wp->w_float_config.focusable;
if (!valid) {
wp->w_grid_alloc.valid = false;
- redraw_later(wp, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
}
}
} else {
@@ -968,21 +1016,19 @@ static int check_split_disallowed(void)
return OK;
}
-/*
- * split the current window, implements CTRL-W s and :split
- *
- * "size" is the height or width for the new window, 0 to use half of current
- * height or width.
- *
- * "flags":
- * WSP_ROOM: require enough room for new window
- * WSP_VERT: vertical split.
- * 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
- */
+// split the current window, implements CTRL-W s and :split
+//
+// "size" is the height or width for the new window, 0 to use half of current
+// height or width.
+//
+// "flags":
+// WSP_ROOM: require enough room for new window
+// WSP_VERT: vertical split.
+// 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) {
@@ -1012,35 +1058,20 @@ int win_split(int size, int flags)
return win_split_ins(size, flags, NULL, 0);
}
-/*
- * When "new_wp" is NULL: split the current window in two.
- * When "new_wp" is not NULL: insert this window at the far
- * top/left/right/bottom.
- * return FAIL for failure, OK otherwise
- */
+// When "new_wp" is NULL: split the current window in two.
+// When "new_wp" is not NULL: insert this window at the far
+// top/left/right/bottom.
+// return FAIL for failure, OK otherwise
int win_split_ins(int size, int flags, win_T *new_wp, int dir)
{
win_T *wp = new_wp;
- win_T *oldwin;
- int new_size = size;
- int i;
- int need_status = 0;
- bool do_equal = false;
- int needed;
- int available;
- int oldwin_height = 0;
- int layout;
- frame_T *frp, *curfrp, *frp2, *prevfrp;
- int before;
- int minheight;
- int wmh1;
- bool did_set_fraction = false;
- // aucmd_win should always remain floating
- if (new_wp != NULL && new_wp == aucmd_win) {
+ // aucmd_win[] should always remain floating
+ if (new_wp != NULL && is_aucmd_win(new_wp)) {
return FAIL;
}
+ win_T *oldwin;
if (flags & WSP_TOP) {
oldwin = firstwin;
} else if (flags & WSP_BOT || curwin->w_floating) {
@@ -1050,6 +1081,8 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
oldwin = curwin;
}
+ int need_status = 0;
+ int new_size = size;
bool new_in_layout = (new_wp == NULL || new_wp->w_floating);
// add a status line when p_ls == 1 and splitting the first window
@@ -1061,32 +1094,33 @@ 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;
-
- layout = FR_ROW;
+ bool do_equal = false;
+ int oldwin_height = 0;
+ const int layout = flags & WSP_VERT ? FR_ROW : FR_COL;
+ bool did_set_fraction = false;
- /*
- * Check if we are able to split the current window and compute its
- * width.
- */
+ if (flags & WSP_VERT) {
+ // Check if we are able to split the current window and compute its
+ // width.
// Current window requires at least 1 space.
- wmw1 = (p_wmw == 0 ? 1 : (int)p_wmw);
- needed = wmw1 + 1;
+ int wmw1 = (p_wmw == 0 ? 1 : (int)p_wmw);
+ int needed = wmw1 + 1;
if (flags & WSP_ROOM) {
needed += (int)p_wiw - wmw1;
}
+ int minwidth;
+ int available;
if (flags & (WSP_BOT | WSP_TOP)) {
minwidth = frame_minwidth(topframe, NOWIN);
available = topframe->fr_width;
needed += minwidth;
} else if (p_ea) {
minwidth = frame_minwidth(oldwin->w_frame, NOWIN);
- prevfrp = oldwin->w_frame;
- for (frp = oldwin->w_frame->fr_parent; frp != NULL;
+ frame_T *prevfrp = oldwin->w_frame;
+ for (frame_T *frp = oldwin->w_frame->fr_parent; frp != NULL;
frp = frp->fr_parent) {
if (frp->fr_layout == FR_ROW) {
+ frame_T *frp2;
FOR_ALL_FRAMES(frp2, frp->fr_child) {
if (frp2 != prevfrp) {
minwidth += frame_minwidth(frp2, NOWIN);
@@ -1132,7 +1166,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
// is wider than one of the split windows.
if (!do_equal && p_ea && size == 0 && *p_ead != 'v'
&& oldwin->w_frame->fr_parent != NULL) {
- frp = oldwin->w_frame->fr_parent->fr_child;
+ frame_T *frp = oldwin->w_frame->fr_parent->fr_child;
while (frp != NULL) {
if (frp->fr_win != oldwin && frp->fr_win != NULL
&& (frp->fr_win->w_width > new_size
@@ -1145,27 +1179,28 @@ 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 plus space for the window bar.
- wmh1 = MAX((int)p_wmh, 1) + oldwin->w_winbar_height;
- needed = wmh1 + STATUS_HEIGHT;
+ int wmh1 = MAX((int)p_wmh, 1) + oldwin->w_winbar_height;
+ int needed = wmh1 + STATUS_HEIGHT;
if (flags & WSP_ROOM) {
needed += (int)p_wh - wmh1 + oldwin->w_winbar_height;
}
if (p_ch < 1) {
needed += 1; // Adjust for cmdheight=0.
}
+ int minheight;
+ int available;
if (flags & (WSP_BOT | WSP_TOP)) {
minheight = frame_minheight(topframe, NOWIN) + need_status;
available = topframe->fr_height;
needed += minheight;
} 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) {
+ frame_T *prevfrp = oldwin->w_frame;
+ for (frame_T *frp = oldwin->w_frame->fr_parent; frp != NULL; frp = frp->fr_parent) {
if (frp->fr_layout == FR_COL) {
+ frame_T *frp2;
FOR_ALL_FRAMES(frp2, frp->fr_child) {
if (frp2 != prevfrp) {
minheight += frame_minheight(frp2, NOWIN);
@@ -1228,7 +1263,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
if (!do_equal && p_ea && size == 0
&& *p_ead != 'h'
&& oldwin->w_frame->fr_parent != NULL) {
- frp = oldwin->w_frame->fr_parent->fr_child;
+ frame_T *frp = oldwin->w_frame->fr_parent->fr_child;
while (frp != NULL) {
if (frp->fr_win != oldwin && frp->fr_win != NULL
&& (frp->fr_win->w_height > new_size
@@ -1241,9 +1276,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
}
}
- /*
- * allocate new window structure and link it in the window list
- */
+ // allocate new window structure and link it in the window list
if ((flags & WSP_TOP) == 0
&& ((flags & WSP_BOT)
|| (flags & WSP_BELOW)
@@ -1280,9 +1313,10 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
CLEAR_FIELD(wp->w_border_adj);
}
- /*
- * Reorganise the tree of frames to insert the new window.
- */
+ int before;
+ frame_T *curfrp;
+
+ // Reorganise the tree of frames to insert the new window.
if (flags & (WSP_TOP | WSP_BOT)) {
if ((topframe->fr_layout == FR_COL && (flags & WSP_VERT) == 0)
|| (topframe->fr_layout == FR_ROW && (flags & WSP_VERT) != 0)) {
@@ -1299,9 +1333,9 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
} else {
curfrp = oldwin->w_frame;
if (flags & WSP_BELOW) {
- before = FALSE;
+ before = false;
} else if (flags & WSP_ABOVE) {
- before = TRUE;
+ before = true;
} else if (flags & WSP_VERT) {
before = !p_spr;
} else {
@@ -1310,7 +1344,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
}
if (curfrp->fr_parent == NULL || curfrp->fr_parent->fr_layout != layout) {
// Need to create a new frame in the tree to make a branch.
- frp = xcalloc(1, sizeof(frame_T));
+ frame_T *frp = xcalloc(1, sizeof(frame_T));
*frp = *curfrp;
curfrp->fr_layout = (char)layout;
frp->fr_parent = curfrp;
@@ -1328,6 +1362,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
}
}
+ frame_T *frp;
if (new_wp == NULL) {
frp = wp->w_frame;
} else {
@@ -1474,8 +1509,8 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
// Both windows need redrawing. Update all status lines, in case they
// show something related to the window count or position.
- redraw_later(wp, NOT_VALID);
- redraw_later(oldwin, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
+ redraw_later(oldwin, UPD_NOT_VALID);
status_redraw_all();
if (need_status) {
@@ -1487,13 +1522,15 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
msg_col = 0; // put position back at start of line
}
- /*
- * equalize the window sizes.
- */
+ // equalize the window sizes.
if (do_equal || dir != 0) {
win_equal(wp, true, (flags & WSP_VERT) ? (dir == 'v' ? 'b' : 'h') : (dir == 'h' ? 'b' : 'v'));
+ } else if (*p_spk != 'c' && !is_aucmd_win(wp)) {
+ win_fix_scroll(false);
}
+ int i;
+
// Don't change the window height/width to 'winheight' / 'winwidth' if a
// size was given.
if (flags & WSP_VERT) {
@@ -1525,17 +1562,13 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
return OK;
}
-/*
- * Initialize window "newp" from window "oldp".
- * Used when splitting a window and when creating a new tab page.
- * The windows will both edit the same buffer.
- * WSP_NEWLOC may be specified in flags to prevent the location list from
- * being copied.
- */
+// Initialize window "newp" from window "oldp".
+// Used when splitting a window and when creating a new tab page.
+// The windows will both edit the same buffer.
+// WSP_NEWLOC may be specified in flags to prevent the location list from
+// being copied.
static void win_init(win_T *newp, win_T *oldp, int flags)
{
- int i;
-
newp->w_buffer = oldp->w_buffer;
newp->w_s = &(oldp->w_buffer->b_s);
oldp->w_buffer->b_nwindows++;
@@ -1565,15 +1598,21 @@ static void win_init(win_T *newp, win_T *oldp, int flags)
newp->w_prevdir = (oldp->w_prevdir == NULL)
? NULL : xstrdup(oldp->w_prevdir);
+ if (*p_spk != 'c') {
+ newp->w_botline = oldp->w_botline;
+ newp->w_prev_height = oldp->w_height;
+ newp->w_prev_winrow = oldp->w_winrow;
+ }
+
// copy tagstack and folds
- for (i = 0; i < oldp->w_tagstacklen; i++) {
+ for (int i = 0; i < oldp->w_tagstacklen; i++) {
taggy_T *tag = &newp->w_tagstack[i];
*tag = oldp->w_tagstack[i];
if (tag->tagname != NULL) {
- tag->tagname = vim_strsave(tag->tagname);
+ tag->tagname = xstrdup(tag->tagname);
}
if (tag->user_data != NULL) {
- tag->user_data = vim_strsave(tag->user_data);
+ tag->user_data = xstrdup(tag->user_data);
}
}
newp->w_tagstackidx = oldp->w_tagstackidx;
@@ -1589,15 +1628,13 @@ static void win_init(win_T *newp, win_T *oldp, int flags)
newp->w_winbar_height = oldp->w_winbar_height;
}
-/*
- * Initialize window "newp" from window "old".
- * Only the essential things are copied.
- */
+// Initialize window "newp" from window "old".
+// Only the essential things are copied.
static void win_init_some(win_T *newp, win_T *oldp)
{
// Use the same argument list.
newp->w_alist = oldp->w_alist;
- ++newp->w_alist->al_refcount;
+ newp->w_alist->al_refcount++;
newp->w_arg_idx = oldp->w_arg_idx;
// copy options from existing window
@@ -1627,11 +1664,20 @@ bool win_valid_floating(const win_T *win)
/// @param win window to check
bool win_valid(const win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
+ return tabpage_win_valid(curtab, win);
+}
+
+/// Check if "win" is a pointer to an existing window in tabpage "tp".
+///
+/// @param win window to check
+static bool tabpage_win_valid(const tabpage_T *tp, const win_T *win)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
if (win == NULL) {
return false;
}
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ FOR_ALL_WINDOWS_IN_TAB(wp, tp) {
if (wp == win) {
return true;
}
@@ -1669,9 +1715,7 @@ bool win_valid_any_tab(win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
return false;
}
-/*
- * Return the number of windows.
- */
+// Return the number of windows.
int win_count(void)
{
int count = 0;
@@ -1692,7 +1736,6 @@ int win_count(void)
int make_windows(int count, bool vertical)
{
int maxcount;
- int todo;
if (vertical) {
// Each window needs at least 'winminwidth' lines and a separator column.
@@ -1722,8 +1765,10 @@ int make_windows(int count, bool vertical)
// when putting the buffers in the windows.
block_autocmds();
+ int todo;
+
// todo is number of windows left to create
- for (todo = count - 1; todo > 0; --todo) {
+ for (todo = count - 1; todo > 0; todo--) {
if (vertical) {
if (win_split(curwin->w_width - (curwin->w_width - todo)
/ (todo + 1) - 1, WSP_VERT | WSP_ABOVE) == FAIL) {
@@ -1744,17 +1789,9 @@ int make_windows(int count, bool vertical)
return count - todo;
}
-/*
- * Exchange current and next window
- */
+// Exchange current and next window
static void win_exchange(long Prenum)
{
- frame_T *frp;
- frame_T *frp2;
- win_T *wp;
- win_T *wp2;
- int temp;
-
if (curwin->w_floating) {
emsg(e_floatexchange);
return;
@@ -1766,9 +1803,9 @@ static void win_exchange(long Prenum)
return;
}
- /*
- * find window to exchange with
- */
+ frame_T *frp;
+
+ // find window to exchange with
if (Prenum) {
frp = curwin->w_frame->fr_parent->fr_child;
while (frp != NULL && --Prenum > 0) {
@@ -1785,18 +1822,16 @@ static void win_exchange(long Prenum)
if (frp == NULL || frp->fr_win == NULL || frp->fr_win == curwin) {
return;
}
- wp = frp->fr_win;
-
- /*
- * 1. remove curwin from the list. Remember after which window it was in wp2
- * 2. insert curwin before wp in the list
- * if wp != wp2
- * 3. remove wp from the list
- * 4. insert wp after wp2
- * 5. exchange the status line height, winbar height, hsep height and vsep width.
- */
- wp2 = curwin->w_prev;
- frp2 = curwin->w_frame->fr_prev;
+ win_T *wp = frp->fr_win;
+
+ // 1. remove curwin from the list. Remember after which window it was in wp2
+ // 2. insert curwin before wp in the list
+ // if wp != wp2
+ // 3. remove wp from the list
+ // 4. insert wp after wp2
+ // 5. exchange the status line height, winbar height, hsep height and vsep width.
+ win_T *wp2 = curwin->w_prev;
+ frame_T *frp2 = curwin->w_frame->fr_prev;
if (wp->w_prev != curwin) {
win_remove(curwin, NULL);
frame_remove(curwin->w_frame);
@@ -1813,7 +1848,7 @@ static void win_exchange(long Prenum)
frame_append(frp2, wp->w_frame);
}
}
- temp = curwin->w_status_height;
+ int temp = curwin->w_status_height;
curwin->w_status_height = wp->w_status_height;
wp->w_status_height = temp;
temp = curwin->w_vsep_width;
@@ -1837,19 +1872,14 @@ static void win_exchange(long Prenum)
}
win_enter(wp, true);
- redraw_later(curwin, NOT_VALID);
- redraw_later(wp, NOT_VALID);
+ redraw_later(curwin, UPD_NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
}
// rotate windows: if upwards true the second window becomes the first one
// if upwards false the first window becomes the second one
static void win_rotate(bool upwards, int count)
{
- win_T *wp1;
- win_T *wp2;
- frame_T *frp;
- int n;
-
if (curwin->w_floating) {
emsg(e_floatexchange);
return;
@@ -1862,6 +1892,7 @@ static void win_rotate(bool upwards, int count)
}
// Check if all frames in this row/col have one window.
+ frame_T *frp;
FOR_ALL_FRAMES(frp, curwin->w_frame->fr_parent->fr_child) {
if (frp->fr_win == NULL) {
emsg(_("E443: Cannot rotate when another window is split"));
@@ -1869,6 +1900,9 @@ static void win_rotate(bool upwards, int count)
}
}
+ win_T *wp1;
+ win_T *wp2;
+
while (count--) {
if (upwards) { // first window becomes last window
// remove first window/frame from the list
@@ -1901,7 +1935,7 @@ static void win_rotate(bool upwards, int count)
}
// exchange status height, winbar height, hsep height and vsep width of old and new last window
- n = wp2->w_status_height;
+ int n = wp2->w_status_height;
wp2->w_status_height = wp1->w_status_height;
wp1->w_status_height = n;
n = wp2->w_hsep_height;
@@ -1922,12 +1956,10 @@ static void win_rotate(bool upwards, int count)
wp1->w_pos_changed = true;
wp2->w_pos_changed = true;
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
}
-/*
- * Move the current window to the very top/bottom/left/right of the screen.
- */
+// Move the current window to the very top/bottom/left/right of the screen.
static void win_totop(int size, int flags)
{
int dir = 0;
@@ -1937,7 +1969,7 @@ static void win_totop(int size, int flags)
beep_flush();
return;
}
- if (curwin == aucmd_win) {
+ if (is_aucmd_win(curwin)) {
return;
}
if (check_split_disallowed() == FAIL) {
@@ -1971,14 +2003,10 @@ static void win_totop(int size, int flags)
}
}
-/*
- * Move window "win1" to below/right of "win2" and make "win1" the current
- * window. Only works within the same frame!
- */
+// Move window "win1" to below/right of "win2" and make "win1" the current
+// window. Only works within the same frame!
void win_move_after(win_T *win1, win_T *win2)
{
- int height;
-
// check if the arguments are reasonable
if (win1 == win2) {
return;
@@ -1994,7 +2022,7 @@ void win_move_after(win_T *win1, win_T *win2)
// 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;
+ int height = win1->w_prev->w_status_height;
win1->w_prev->w_status_height = win1->w_status_height;
win1->w_status_height = height;
@@ -2011,7 +2039,7 @@ void win_move_after(win_T *win1, win_T *win2)
win1->w_frame->fr_width += 1;
}
} else if (win2 == lastwin) {
- height = win1->w_status_height;
+ int height = win1->w_status_height;
win1->w_status_height = win2->w_status_height;
win2->w_status_height = height;
@@ -2034,7 +2062,7 @@ void win_move_after(win_T *win1, win_T *win2)
frame_append(win2->w_frame, win1->w_frame);
(void)win_comp_pos(); // recompute w_winrow for all windows
- redraw_later(curwin, NOT_VALID);
+ redraw_later(curwin, UPD_NOT_VALID);
}
win_enter(win1, false);
@@ -2074,7 +2102,7 @@ static int get_maximum_wincount(frame_T *fr, int height)
}
/// Make all windows the same height.
-///'next_curwin' will soon be the current window, make sure it has enough rows.
+/// 'next_curwin' will soon be the current window, make sure it has enough rows.
///
/// @param next_curwin pointer to current window to be or NULL
/// @param current do only frame with current window
@@ -2082,11 +2110,14 @@ static int get_maximum_wincount(frame_T *fr, int height)
void win_equal(win_T *next_curwin, bool current, int dir)
{
if (dir == 0) {
- dir = *p_ead;
+ dir = (unsigned char)(*p_ead);
}
win_equal_rec(next_curwin == NULL ? curwin : next_curwin, current,
topframe, dir, 0, tabline_height(),
Columns, topframe->fr_height);
+ if (*p_spk != 'c' && !is_aucmd_win(next_curwin)) {
+ win_fix_scroll(true);
+ }
}
/// Set a frame to a new position and height, spreading the available room
@@ -2105,15 +2136,11 @@ void win_equal(win_T *next_curwin, bool current, int dir)
static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int dir, int col,
int row, int width, int height)
{
- int n, m;
int extra_sep = 0;
- int wincount, totwincount = 0;
- frame_T *fr;
+ int totwincount = 0;
int next_curwin_size = 0;
int room = 0;
- int new_size;
int has_next_curwin = 0;
- bool hnc;
if (topfr->fr_layout == FR_LEAF) {
// Set the width/height of this frame.
@@ -2125,7 +2152,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
frame_new_height(topfr, height, false, false);
topfr->fr_win->w_wincol = col;
frame_new_width(topfr, width, false, false);
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
}
} else if (topfr->fr_layout == FR_ROW) {
topfr->fr_width = width;
@@ -2134,7 +2161,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
if (dir != 'v') { // equalize frame widths
// Compute the maximum number of windows horizontally in this
// frame.
- n = frame_minwidth(topfr, NOWIN);
+ int n = frame_minwidth(topfr, NOWIN);
// add one for the rightmost window, it doesn't have a separator
if (col + width == Columns) {
extra_sep = 1;
@@ -2144,18 +2171,17 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
totwincount = (n + extra_sep) / ((int)p_wmw + 1);
has_next_curwin = frame_has_win(topfr, next_curwin);
- /*
- * Compute width for "next_curwin" window and room available for
- * other windows.
- * "m" is the minimal width when counting p_wiw for "next_curwin".
- */
- m = frame_minwidth(topfr, next_curwin);
+ // Compute width for "next_curwin" window and room available for
+ // other windows.
+ // "m" is the minimal width when counting p_wiw for "next_curwin".
+ int m = frame_minwidth(topfr, next_curwin);
room = width - m;
if (room < 0) {
next_curwin_size = (int)p_wiw + room;
room = 0;
} else {
next_curwin_size = -1;
+ frame_T *fr;
FOR_ALL_FRAMES(fr, topfr->fr_child) {
if (!frame_fixed_width(fr)) {
continue;
@@ -2163,7 +2189,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
// If 'winfixwidth' set keep the window width if possible.
// Watch out for this window being the next_curwin.
n = frame_minwidth(fr, NOWIN);
- new_size = fr->fr_width;
+ int new_size = fr->fr_width;
if (frame_has_win(fr, next_curwin)) {
room += (int)p_wiw - (int)p_wmw;
next_curwin_size = 0;
@@ -2200,12 +2226,14 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
}
if (has_next_curwin) {
- --totwincount; // don't count curwin
+ totwincount--; // don't count curwin
}
}
+ frame_T *fr;
FOR_ALL_FRAMES(fr, topfr->fr_child) {
- wincount = 1;
+ int wincount = 1;
+ int new_size;
if (fr->fr_next == NULL) {
// last frame gets all that remains (avoid roundoff error)
new_size = width;
@@ -2216,14 +2244,10 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
wincount = 0; // doesn't count as a sizeable window
} else {
// Compute the maximum number of windows horiz. in "fr".
- n = frame_minwidth(fr, NOWIN);
+ int n = frame_minwidth(fr, NOWIN);
wincount = (n + (fr->fr_next == NULL ? extra_sep : 0)) / ((int)p_wmw + 1);
- m = frame_minwidth(fr, next_curwin);
- if (has_next_curwin) {
- hnc = frame_has_win(fr, next_curwin);
- } else {
- hnc = false;
- }
+ int m = frame_minwidth(fr, next_curwin);
+ bool hnc = has_next_curwin && frame_has_win(fr, next_curwin);
if (hnc) { // don't count next_curwin
wincount--;
}
@@ -2260,9 +2284,9 @@ 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);
+ int n = frame_minheight(topfr, NOWIN);
// add one for the bottom window if it doesn't have a statusline or separator
- if (row + height == cmdline_row && p_ls == 0) {
+ if (row + height >= cmdline_row && p_ls == 0) {
extra_sep = STATUS_HEIGHT;
} else if (global_stl_height() > 0) {
extra_sep = 1;
@@ -2272,12 +2296,10 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
totwincount = get_maximum_wincount(topfr, n + extra_sep);
has_next_curwin = frame_has_win(topfr, next_curwin);
- /*
- * Compute height for "next_curwin" window and room available for
- * other windows.
- * "m" is the minimal height when counting p_wh for "next_curwin".
- */
- m = frame_minheight(topfr, next_curwin);
+ // Compute height for "next_curwin" window and room available for
+ // other windows.
+ // "m" is the minimal height when counting p_wh for "next_curwin".
+ int m = frame_minheight(topfr, next_curwin);
room = height - m;
if (room < 0) {
// The room is less than 'winheight', use all space for the
@@ -2286,6 +2308,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
room = 0;
} else {
next_curwin_size = -1;
+ frame_T *fr;
FOR_ALL_FRAMES(fr, topfr->fr_child) {
if (!frame_fixed_height(fr)) {
continue;
@@ -2293,7 +2316,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
// If 'winfixheight' set keep the window height if possible.
// Watch out for this window being the next_curwin.
n = frame_minheight(fr, NOWIN);
- new_size = fr->fr_height;
+ int new_size = fr->fr_height;
if (frame_has_win(fr, next_curwin)) {
room += (int)p_wh - (int)p_wmh;
next_curwin_size = 0;
@@ -2330,12 +2353,14 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
}
if (has_next_curwin) {
- --totwincount; // don't count curwin
+ totwincount--; // don't count curwin
}
}
+ frame_T *fr;
FOR_ALL_FRAMES(fr, topfr->fr_child) {
- wincount = 1;
+ int new_size;
+ int wincount = 1;
if (fr->fr_next == NULL) {
// last frame gets all that remains (avoid roundoff error)
new_size = height;
@@ -2346,14 +2371,10 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
wincount = 0; // doesn't count as a sizeable window
} else {
// Compute the maximum number of windows vert. in "fr".
- n = frame_minheight(fr, NOWIN);
+ int n = frame_minheight(fr, NOWIN);
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);
- } else {
- hnc = false;
- }
+ int m = frame_minheight(fr, next_curwin);
+ bool hnc = has_next_curwin && frame_has_win(fr, next_curwin);
if (hnc) { // don't count next_curwin
wincount--;
}
@@ -2436,7 +2457,7 @@ void entering_window(win_T *const win)
void win_init_empty(win_T *wp)
{
- redraw_later(wp, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
wp->w_lines_valid = 0;
wp->w_cursor.lnum = 1;
wp->w_curswant = wp->w_cursor.col = 0;
@@ -2463,14 +2484,11 @@ void curwin_init(void)
/// @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++;
// 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));) {
+ for (win_T *wp = lastwin; wp != NULL && (is_aucmd_win(lastwin) || !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, false) == FAIL) {
@@ -2485,11 +2503,14 @@ void close_windows(buf_T *buf, bool keep_curwin)
}
}
+ tabpage_T *nexttp;
+
// Also check windows in other tab pages.
- for (tp = first_tabpage; tp != NULL; tp = nexttp) {
+ for (tabpage_T *tp = first_tabpage; tp != NULL; tp = nexttp) {
nexttp = tp->tp_next;
if (tp != curtab) {
- FOR_ALL_WINDOWS_IN_TAB(wp, tp) {
+ // Start from tp_lastwin to close floating windows with the same buffer first.
+ for (win_T *wp = tp->tp_lastwin; wp != NULL; wp = wp->w_prev) {
if (wp->w_buffer == buf
&& !(wp->w_closing || wp->w_buffer->b_locked > 0)) {
win_close_othertab(wp, false, tp);
@@ -2504,31 +2525,25 @@ void close_windows(buf_T *buf, bool keep_curwin)
}
RedrawingDisabled--;
-
- redraw_tabline = true;
- if (h != tabline_height()) {
- win_new_screen_rows();
- }
}
-/// Check that the specified window is the last one.
-/// @param win counted even if floating
-///
-/// @return true if the specified window is the only window that exists,
-/// false if there is another, possibly in another tab page.
+/// Check if "win" is the last non-floating window that exists.
bool last_window(win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
return one_window(win) && first_tabpage->tp_next == NULL;
}
-/// 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
+/// Check if "win" is the only non-floating window in the current tabpage.
+bool one_window(win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
+ if (win->w_floating) {
+ return false;
+ }
+
bool seen_one = false;
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp != aucmd_win && (!wp->w_floating || wp == counted_float)) {
+ if (!wp->w_floating) {
if (seen_one) {
return false;
}
@@ -2558,7 +2573,7 @@ bool last_nonfloat(win_T *wp) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
/// @return true if all floating windows can be closed
static bool can_close_floating_windows(void)
{
- assert(lastwin != aucmd_win);
+ assert(!is_aucmd_win(lastwin));
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);
@@ -2592,30 +2607,22 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, tabpage_T *prev
free_buf = false;
}
- /*
- * Closing the last window in a tab page. First go to another tab
- * page and then close the window and the tab page. This avoids that
- * curwin and curtab are invalid while we are freeing memory, they may
- * be used in GUI events.
- * Don't trigger autocommands yet, they may use wrong values, so do
- * that below.
- */
+ // Closing the last window in a tab page. First go to another tab
+ // page and then close the window and the tab page. This avoids that
+ // curwin and curtab are invalid while we are freeing memory, they may
+ // be used in GUI events.
+ // Don't trigger autocommands yet, they may use wrong values, so do
+ // that below.
goto_tabpage_tp(alt_tabpage(), false, true);
- redraw_tabline = true;
// save index for tabclosed event
- char_u prev_idx[NUMBUFLEN];
- sprintf((char *)prev_idx, "%i", tabpage_index(prev_curtab));
+ char prev_idx[NUMBUFLEN];
+ snprintf(prev_idx, NUMBUFLEN, "%i", tabpage_index(prev_curtab));
// Safety check: Autocommands may have closed the window when jumping
// to the other tab page.
if (valid_tabpage(prev_curtab) && prev_curtab->tp_firstwin == win) {
- int h = tabline_height();
-
win_close_othertab(win, free_buf, prev_curtab);
- if (h != tabline_height()) {
- win_new_screen_rows();
- }
}
entering_window(curwin);
@@ -2671,11 +2678,6 @@ static void win_close_buffer(win_T *win, bool free_buf, bool abort_if_last)
// Returns FAIL when the window was not closed.
int win_close(win_T *win, bool free_buf, bool force)
{
- win_T *wp;
- bool other_buffer = false;
- bool close_curwin = false;
- int dir;
- bool help_window = false;
tabpage_T *prev_curtab = curtab;
frame_T *win_frame = win->w_floating ? NULL : win->w_frame->fr_parent;
const bool had_diffmode = win->w_p_diff;
@@ -2689,12 +2691,12 @@ int win_close(win_T *win, bool free_buf, bool force)
|| (win->w_buffer != NULL && win->w_buffer->b_locked > 0)) {
return FAIL; // window is already being closed
}
- if (win == aucmd_win) {
+ if (is_aucmd_win(win)) {
emsg(_(e_autocmd_close));
return FAIL;
}
if (lastwin->w_floating && one_window(win)) {
- if (lastwin == aucmd_win) {
+ if (is_aucmd_win(lastwin)) {
emsg(_("E814: Cannot close window, only autocmd window would remain"));
return FAIL;
}
@@ -2720,6 +2722,8 @@ int win_close(win_T *win, bool free_buf, bool force)
return FAIL;
}
+ bool help_window = false;
+
// When closing the help window, try restoring a snapshot after closing
// the window. Otherwise clear the snapshot, it's now invalid.
if (bt_help(win->w_buffer)) {
@@ -2728,6 +2732,9 @@ int win_close(win_T *win, bool free_buf, bool force)
clear_snapshot(curtab, SNAP_HELP_IDX);
}
+ win_T *wp;
+ bool other_buffer = false;
+
if (win == curwin) {
leaving_window(curwin);
@@ -2743,10 +2750,8 @@ int win_close(win_T *win, bool free_buf, bool force)
}
}
- /*
- * Be careful: If autocommands delete the window or cause this window
- * to be the last one left, return now.
- */
+ // Be careful: If autocommands delete the window or cause this window
+ // to be the last one left, return now.
if (wp->w_buffer != curbuf) {
reset_VIsual_and_resel(); // stop Visual mode
@@ -2776,27 +2781,6 @@ int win_close(win_T *win, bool free_buf, bool force)
}
}
- bool was_floating = win->w_floating;
- if (ui_has(kUIMultigrid)) {
- ui_call_win_close(win->w_grid_alloc.handle);
- }
-
- if (win->w_floating) {
- ui_comp_remove_grid(&win->w_grid_alloc);
- if (win->w_float_config.external) {
- for (tabpage_T *tp = first_tabpage; tp != NULL; tp = tp->tp_next) {
- if (tp == curtab) {
- continue;
- }
- if (tp->tp_curwin == win) {
- // NB: an autocmd can still abort the closing of this window,
- // bur carring out this change anyway shouldn't be a catastrophe.
- tp->tp_curwin = tp->tp_firstwin;
- }
- }
- }
- }
-
// Fire WinClosed just before starting to free window-related resources.
do_autocmd_winclosed(win);
// autocmd may have freed the window already.
@@ -2821,7 +2805,10 @@ int win_close(win_T *win, bool free_buf, bool force)
if (curtab != prev_curtab && win_valid_any_tab(win)
&& win->w_buffer == NULL) {
// Need to close the window anyway, since the buffer is NULL.
+ // Don't trigger autocmds with a NULL buffer.
+ block_autocmds();
win_close_othertab(win, false, prev_curtab);
+ unblock_autocmds();
return FAIL;
}
@@ -2839,8 +2826,31 @@ int win_close(win_T *win, bool free_buf, bool force)
// let terminal buffers know that this window dimensions may be ignored
win->w_closing = true;
+ bool was_floating = win->w_floating;
+ if (ui_has(kUIMultigrid)) {
+ ui_call_win_close(win->w_grid_alloc.handle);
+ }
+
+ if (win->w_floating) {
+ ui_comp_remove_grid(&win->w_grid_alloc);
+ assert(first_tabpage != NULL); // suppress clang "Dereference of NULL pointer"
+ if (win->w_float_config.external) {
+ for (tabpage_T *tp = first_tabpage; tp != NULL; tp = tp->tp_next) {
+ if (tp == curtab) {
+ continue;
+ }
+ if (tp->tp_curwin == win) {
+ // NB: an autocmd can still abort the closing of this window,
+ // bur carring out this change anyway shouldn't be a catastrophe.
+ tp->tp_curwin = tp->tp_firstwin;
+ }
+ }
+ }
+ }
+
// Free the memory used for the window and get the window that received
// the screen space.
+ int dir;
wp = win_free_mem(win, &dir, NULL);
if (help_window) {
@@ -2852,16 +2862,16 @@ int win_close(win_T *win, bool free_buf, bool force)
}
}
+ bool close_curwin = false;
+
// Make sure curwin isn't invalid. It can cause severe trouble when
// printing an error message. For win_equal() curbuf needs to be valid
// too.
if (win == curwin) {
curwin = wp;
if (wp->w_p_pvw || bt_quickfix(wp->w_buffer)) {
- /*
- * If the cursor goes to the preview or the quickfix window, try
- * finding another window to go to.
- */
+ // If the cursor goes to the preview or the quickfix window, try
+ // finding another window to go to.
for (;;) {
if (wp->w_next == NULL) {
wp = firstwin;
@@ -2886,12 +2896,20 @@ int win_close(win_T *win, bool free_buf, bool force)
}
if (!was_floating) {
+ // If last window has a status line now and we don't want one,
+ // remove the status line. Do this before win_equal(), because
+ // it may change the height of a window.
+ last_status(false);
+
if (!curwin->w_floating && p_ea && (*p_ead == 'b' || *p_ead == dir)) {
// If the frame of the closed window contains the new current window,
// only resize that frame. Otherwise resize all windows.
win_equal(curwin, curwin->w_frame->fr_parent == win_frame, dir);
} else {
(void)win_comp_pos();
+ if (*p_spk != 'c') {
+ win_fix_scroll(false);
+ }
}
}
@@ -2906,12 +2924,6 @@ int win_close(win_T *win, bool free_buf, bool force)
split_disallowed--;
- /*
- * If last window has a status line now and we don't want one,
- * remove the status line.
- */
- last_status(false);
-
// After closing the help window, try restoring the window layout from
// before it was opened.
if (help_window) {
@@ -2935,7 +2947,10 @@ int win_close(win_T *win, bool free_buf, bool force)
}
curwin->w_pos_changed = true;
- redraw_all_later(NOT_VALID);
+ if (!was_floating) {
+ // TODO(bfredl): how about no?
+ redraw_all_later(UPD_NOT_VALID);
+ }
return OK;
}
@@ -2953,19 +2968,13 @@ static void do_autocmd_winclosed(win_T *win)
recursive = false;
}
-/*
- * Close window "win" in tab page "tp", which is not the current tab page.
- * This may be the last window in that tab page and result in closing the tab,
- * thus "tp" may become invalid!
- * Caller must check if buffer is hidden and whether the tabline needs to be
- * updated.
- */
+// Close window "win" in tab page "tp", which is not the current tab page.
+// This may be the last window in that tab page and result in closing the tab,
+// thus "tp" may become invalid!
+// Caller must check if buffer is hidden and whether the tabline needs to be
+// updated.
void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
{
- int dir;
- tabpage_T *ptp = NULL;
- bool free_tp = false;
-
// Get here with win->w_buffer == NULL when win_close() detects the tab page
// changed.
if (win->w_closing
@@ -2985,6 +2994,8 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, false, true);
}
+ tabpage_T *ptp = NULL;
+
// 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) {}
@@ -3013,6 +3024,8 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
}
}
+ bool free_tp = false;
+
// When closing the last window in a tab page remove the tab page.
if (tp->tp_firstwin == tp->tp_lastwin) {
char prev_idx[NUMBUFLEN];
@@ -3020,6 +3033,8 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
vim_snprintf(prev_idx, NUMBUFLEN, "%i", tabpage_index(tp));
}
+ int h = tabline_height();
+
if (tp == first_tabpage) {
first_tabpage = tp->tp_next;
} else {
@@ -3034,6 +3049,10 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
ptp->tp_next = tp->tp_next;
}
free_tp = true;
+ redraw_tabline = true;
+ if (h != tabline_height()) {
+ win_new_screen_rows();
+ }
if (has_event(EVENT_TABCLOSED)) {
apply_autocmds(EVENT_TABCLOSED, prev_idx, prev_idx, false, win->w_buffer);
@@ -3041,6 +3060,7 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
}
// Free the memory used for the window.
+ int dir;
win_free_mem(win, &dir, tp);
if (free_tp) {
@@ -3056,32 +3076,36 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
/// @return a pointer to the window that got the freed up space.
static win_T *win_free_mem(win_T *win, int *dirp, tabpage_T *tp)
{
- frame_T *frp;
win_T *wp;
+ tabpage_T *win_tp = tp == NULL ? curtab : tp;
if (!win->w_floating) {
// Remove the window and its frame from the tree of frames.
- frp = win->w_frame;
+ frame_T *frp = win->w_frame;
wp = winframe_remove(win, dirp, tp);
xfree(frp);
} else {
*dirp = 'h'; // Dummy value.
- if (win_valid(prevwin) && prevwin != win) {
- wp = prevwin;
+ if (tp == NULL) {
+ if (win_valid(prevwin) && prevwin != win) {
+ wp = prevwin;
+ } else {
+ wp = firstwin;
+ }
} else {
- wp = firstwin;
+ if (tabpage_win_valid(tp, tp->tp_prevwin) && tp->tp_prevwin != win) {
+ wp = tp->tp_prevwin;
+ } else {
+ wp = tp->tp_firstwin;
+ }
}
}
win_free(win, tp);
- // When deleting the current window of another tab page select a new
- // current window.
- if (tp != NULL && win == tp->tp_curwin) {
- if (win_valid(tp->tp_prevwin) && tp->tp_prevwin != win) {
- tp->tp_curwin = tp->tp_prevwin;
- } else {
- tp->tp_curwin = tp->tp_firstwin;
- }
+ // When deleting the current window in the tab, select a new current
+ // window.
+ if (win == win_tp->tp_curwin) {
+ win_tp->tp_curwin = wp;
}
return wp;
@@ -3090,30 +3114,37 @@ static win_T *win_free_mem(win_T *win, int *dirp, tabpage_T *tp)
#if defined(EXITFREE)
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);
+ tabpage_close(true);
}
while (lastwin != NULL && lastwin->w_floating) {
win_T *wp = lastwin;
win_remove(lastwin, NULL);
+ int dummy;
(void)win_free_mem(wp, &dummy, NULL);
- if (wp == aucmd_win) {
- aucmd_win = NULL;
+ for (int i = 0; i < AUCMD_WIN_COUNT; i++) {
+ if (aucmd_win[i].auc_win == wp) {
+ aucmd_win[i].auc_win = NULL;
+ }
}
}
- if (aucmd_win != NULL) {
- (void)win_free_mem(aucmd_win, &dummy, NULL);
- aucmd_win = NULL;
+ for (int i = 0; i < AUCMD_WIN_COUNT; i++) {
+ if (aucmd_win[i].auc_win != NULL) {
+ int dummy;
+ (void)win_free_mem(aucmd_win[i].auc_win, &dummy, NULL);
+ aucmd_win[i].auc_win = NULL;
+ }
}
+ kv_destroy(aucmd_win_vec);
+
while (firstwin != NULL) {
+ int dummy;
(void)win_free_mem(firstwin, &dummy, NULL);
}
@@ -3132,22 +3163,16 @@ void win_free_all(void)
/// @return a pointer to the window that got the freed up space.
win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp)
{
- frame_T *frp, *frp2, *frp3;
- frame_T *frp_close = win->w_frame;
- win_T *wp;
-
- /*
- * If there is only one window there is nothing to remove.
- */
+ // If there is only one window there is nothing to remove.
if (tp == NULL ? ONE_WINDOW : tp->tp_firstwin == tp->tp_lastwin) {
return NULL;
}
- /*
- * Remove the window from its frame.
- */
- frp2 = win_altframe(win, tp);
- wp = frame2win(frp2);
+ frame_T *frp_close = win->w_frame;
+
+ // Remove the window from its frame.
+ frame_T *frp2 = win_altframe(win, tp);
+ win_T *wp = frame2win(frp2);
// Remove this frame from the list of frames.
frame_remove(frp_close);
@@ -3157,8 +3182,8 @@ win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp)
// (as close to the closed frame as possible) to distribute the height
// to.
if (frp2->fr_win != NULL && frp2->fr_win->w_p_wfh) {
- frp = frp_close->fr_prev;
- frp3 = frp_close->fr_next;
+ frame_T *frp = frp_close->fr_prev;
+ frame_T *frp3 = frp_close->fr_next;
while (frp != NULL || frp3 != NULL) {
if (frp != NULL) {
if (!frame_fixed_height(frp)) {
@@ -3186,8 +3211,8 @@ win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp)
// (as close to the closed frame as possible) to distribute the width
// to.
if (frp2->fr_win != NULL && frp2->fr_win->w_p_wfw) {
- frp = frp_close->fr_prev;
- frp3 = frp_close->fr_next;
+ frame_T *frp = frp_close->fr_prev;
+ frame_T *frp3 = frp_close->fr_next;
while (frp != NULL || frp3 != NULL) {
if (frp != NULL) {
if (!frame_fixed_width(frp)) {
@@ -3226,6 +3251,7 @@ win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp)
// and remove it.
frp2->fr_parent->fr_layout = frp2->fr_layout;
frp2->fr_parent->fr_child = frp2->fr_child;
+ frame_T *frp;
FOR_ALL_FRAMES(frp, frp2->fr_child) {
frp->fr_parent = frp2->fr_parent;
}
@@ -3251,6 +3277,7 @@ win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp)
if (frp->fr_prev != NULL) {
frp->fr_prev->fr_next = frp->fr_child;
}
+ frame_T *frp3;
for (frp3 = frp->fr_child;; frp3 = frp3->fr_next) {
frp3->fr_parent = frp2;
if (frp3->fr_next == NULL) {
@@ -3283,13 +3310,11 @@ win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp)
/// is left over after "win" is closed.
static frame_T *win_altframe(win_T *win, tabpage_T *tp)
{
- frame_T *frp;
-
if (tp == NULL ? ONE_WINDOW : tp->tp_firstwin == tp->tp_lastwin) {
return alt_tabpage()->tp_curwin->w_frame;
}
- frp = win->w_frame;
+ frame_T *frp = win->w_frame;
if (frp->fr_prev == NULL) {
return frp->fr_next;
@@ -3332,26 +3357,21 @@ static frame_T *win_altframe(win_T *win, tabpage_T *tp)
return target_fr;
}
-/*
- * Return the tabpage that will be used if the current one is closed.
- */
+// Return the tabpage that will be used if the current one is closed.
static tabpage_T *alt_tabpage(void)
{
- tabpage_T *tp;
-
// Use the next tab page if possible.
if (curtab->tp_next != NULL) {
return curtab->tp_next;
}
// Find the last but one tab page.
+ tabpage_T *tp;
for (tp = first_tabpage; tp->tp_next != curtab; tp = tp->tp_next) {}
return tp;
}
-/*
- * Find the left-upper window in frame "frp".
- */
+// Find the left-upper window in frame "frp".
win_T *frame2win(frame_T *frp)
{
while (frp->fr_win == NULL) {
@@ -3384,8 +3404,7 @@ static bool frame_has_win(const frame_T *frp, const win_T *wp)
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) {
+ for (frame_T *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;
}
@@ -3401,19 +3420,15 @@ static bool is_bottom_win(win_T *wp)
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.
- wp = topfrp->fr_win;
+ win_T *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) {
+ frame_T *frp;
do {
// All frames in this row get the same new height.
FOR_ALL_FRAMES(frp, topfrp->fr_child) {
@@ -3429,7 +3444,7 @@ void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wfh)
// Complicated case: Resize a column of frames. Resize the bottom
// frame first, frames above that when needed.
- frp = topfrp->fr_child;
+ frame_T *frp = topfrp->fr_child;
if (wfh) {
// Advance past frames with one window with 'wfh' set.
while (frame_fixed_height(frp)) {
@@ -3452,11 +3467,11 @@ void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wfh)
}
}
- extra_lines = height - topfrp->fr_height;
+ int extra_lines = height - topfrp->fr_height;
if (extra_lines < 0) {
// reduce height of contained frames, bottom or top frame first
while (frp != NULL) {
- h = frame_minheight(frp, NULL);
+ int h = frame_minheight(frp, NULL);
if (frp->fr_height + extra_lines < h) {
extra_lines += frp->fr_height - h;
frame_new_height(frp, h, topfirst, wfh);
@@ -3555,16 +3570,12 @@ static bool frame_fixed_width(frame_T *frp)
return true;
}
-/*
- * Add a status line to windows at the bottom of "frp".
- * Note: Does not check if there is room!
- */
+// Add a status line to windows at the bottom of "frp".
+// Note: Does not check if there is room!
static void frame_add_statusline(frame_T *frp)
{
- win_T *wp;
-
if (frp->fr_layout == FR_LEAF) {
- wp = frp->fr_win;
+ win_T *wp = frp->fr_win;
if (wp->w_status_height == 0) {
if (wp->w_height - STATUS_HEIGHT >= 0) { // don't make it negative
wp->w_height -= STATUS_HEIGHT;
@@ -3592,15 +3603,11 @@ static void frame_add_statusline(frame_T *frp)
/// may cause the width not to be set.
static void frame_new_width(frame_T *topfrp, int width, bool leftfirst, bool wfw)
{
- frame_T *frp;
- int extra_cols;
- int w;
- win_T *wp;
-
if (topfrp->fr_layout == FR_LEAF) {
// Simple case: just one window.
- wp = topfrp->fr_win;
+ win_T *wp = topfrp->fr_win;
// Find out if there are any windows right of this one.
+ frame_T *frp;
for (frp = topfrp; frp->fr_parent != NULL; frp = frp->fr_parent) {
if (frp->fr_parent->fr_layout == FR_ROW && frp->fr_next != NULL) {
break;
@@ -3611,6 +3618,7 @@ static void frame_new_width(frame_T *topfrp, int width, bool leftfirst, bool wfw
}
win_new_width(wp, width - wp->w_vsep_width);
} else if (topfrp->fr_layout == FR_COL) {
+ frame_T *frp;
do {
// All frames in this column get the same new width.
FOR_ALL_FRAMES(frp, topfrp->fr_child) {
@@ -3626,7 +3634,7 @@ static void frame_new_width(frame_T *topfrp, int width, bool leftfirst, bool wfw
// Complicated case: Resize a row of frames. Resize the rightmost
// frame first, frames left of it when needed.
- frp = topfrp->fr_child;
+ frame_T *frp = topfrp->fr_child;
if (wfw) {
// Advance past frames with one window with 'wfw' set.
while (frame_fixed_width(frp)) {
@@ -3649,11 +3657,11 @@ static void frame_new_width(frame_T *topfrp, int width, bool leftfirst, bool wfw
}
}
- extra_cols = width - topfrp->fr_width;
+ int extra_cols = width - topfrp->fr_width;
if (extra_cols < 0) {
// reduce frame width, rightmost frame first
while (frp != NULL) {
- w = frame_minwidth(frp, NULL);
+ int w = frame_minwidth(frp, NULL);
if (frp->fr_width + extra_cols < w) {
extra_cols += frp->fr_width - w;
frame_new_width(frp, w, leftfirst, wfw);
@@ -3689,13 +3697,11 @@ static void frame_new_width(frame_T *topfrp, int width, bool leftfirst, bool wfw
static void frame_add_vsep(const frame_T *frp)
FUNC_ATTR_NONNULL_ARG(1)
{
- win_T *wp;
-
if (frp->fr_layout == FR_LEAF) {
- wp = frp->fr_win;
+ win_T *wp = frp->fr_win;
if (wp->w_vsep_width == 0) {
if (wp->w_width > 0) { // don't make it negative
- --wp->w_width;
+ wp->w_width--;
}
wp->w_vsep_width = 1;
}
@@ -3720,10 +3726,8 @@ static void frame_add_vsep(const frame_T *frp)
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;
+ win_T *wp = frp->fr_win;
if (wp->w_hsep_height == 0) {
if (wp->w_height > 0) { // don't make it negative
wp->w_height++;
@@ -3746,17 +3750,13 @@ static void frame_add_hsep(const frame_T *frp)
}
}
-/*
- * Set frame width from the window it contains.
- */
+// Set frame width from the window it contains.
static void frame_fix_width(win_T *wp)
{
wp->w_frame->fr_width = wp->w_width + wp->w_vsep_width;
}
-/*
- * Set frame height from the window it contains.
- */
+// Set frame height from the window it contains.
static void frame_fix_height(win_T *wp)
FUNC_ATTR_NONNULL_ALL
{
@@ -3768,9 +3768,7 @@ static void frame_fix_height(win_T *wp)
/// 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;
int m;
- int n;
if (topfrp->fr_win != NULL) {
// Combined height of window bar and separator column or status line.
@@ -3791,8 +3789,9 @@ static int frame_minheight(frame_T *topfrp, win_T *next_curwin)
} else if (topfrp->fr_layout == FR_ROW) {
// get the minimal height from each frame in this row
m = 0;
+ frame_T *frp;
FOR_ALL_FRAMES(frp, topfrp->fr_child) {
- n = frame_minheight(frp, next_curwin);
+ int n = frame_minheight(frp, next_curwin);
if (n > m) {
m = n;
}
@@ -3800,6 +3799,7 @@ static int frame_minheight(frame_T *topfrp, win_T *next_curwin)
} else {
// Add up the minimal heights for all frames in this column.
m = 0;
+ frame_T *frp;
FOR_ALL_FRAMES(frp, topfrp->fr_child) {
m += frame_minheight(frp, next_curwin);
}
@@ -3816,8 +3816,7 @@ static int frame_minheight(frame_T *topfrp, win_T *next_curwin)
/// @param next_curwin use p_wh and p_wiw for next_curwin
static int frame_minwidth(frame_T *topfrp, win_T *next_curwin)
{
- frame_T *frp;
- int m, n;
+ int m;
if (topfrp->fr_win != NULL) {
if (topfrp->fr_win == next_curwin) {
@@ -3833,8 +3832,9 @@ static int frame_minwidth(frame_T *topfrp, win_T *next_curwin)
} else if (topfrp->fr_layout == FR_COL) {
// get the minimal width from each frame in this column
m = 0;
+ frame_T *frp;
FOR_ALL_FRAMES(frp, topfrp->fr_child) {
- n = frame_minwidth(frp, next_curwin);
+ int n = frame_minwidth(frp, next_curwin);
if (n > m) {
m = n;
}
@@ -3842,6 +3842,7 @@ static int frame_minwidth(frame_T *topfrp, win_T *next_curwin)
} else {
// Add up the minimal widths for all frames in this row.
m = 0;
+ frame_T *frp;
FOR_ALL_FRAMES(frp, topfrp->fr_child) {
m += frame_minwidth(frp, next_curwin);
}
@@ -3859,10 +3860,6 @@ static int frame_minwidth(frame_T *topfrp, win_T *next_curwin)
/// @param forceit always hide all other windows
void close_others(int message, int forceit)
{
- win_T *wp;
- win_T *nextwp;
- int r;
-
if (curwin->w_floating) {
if (message && !autocmd_busy) {
emsg(e_floatonly);
@@ -3879,14 +3876,15 @@ void close_others(int message, int forceit)
}
// Be very careful here: autocommands may change the window layout.
- for (wp = firstwin; win_valid(wp); wp = nextwp) {
+ win_T *nextwp;
+ for (win_T *wp = firstwin; win_valid(wp); wp = nextwp) {
nextwp = wp->w_next;
if (wp == curwin) { // don't close current window
continue;
}
// Check if it's allowed to abandon this window
- r = can_abandon(wp->w_buffer, forceit);
+ int r = can_abandon(wp->w_buffer, forceit);
if (!win_valid(wp)) { // autocommands messed wp up
nextwp = firstwin;
continue;
@@ -3911,48 +3909,59 @@ void close_others(int message, int forceit)
}
}
-/*
- * Allocate the first window and put an empty buffer in it.
- * Called from main().
- *
- * Return FAIL when something goes wrong.
- */
-int win_alloc_first(void)
+/// Store the relevant window pointers for tab page "tp". To be used before
+/// use_tabpage().
+void unuse_tabpage(tabpage_T *tp)
+{
+ tp->tp_topframe = topframe;
+ tp->tp_firstwin = firstwin;
+ tp->tp_lastwin = lastwin;
+ tp->tp_curwin = curwin;
+}
+
+/// Set the relevant pointers to use tab page "tp". May want to call
+/// unuse_tabpage() first.
+void use_tabpage(tabpage_T *tp)
+{
+ curtab = tp;
+ topframe = curtab->tp_topframe;
+ firstwin = curtab->tp_firstwin;
+ lastwin = curtab->tp_lastwin;
+ curwin = curtab->tp_curwin;
+}
+
+// Allocate the first window and put an empty buffer in it.
+// Only called from main().
+void win_alloc_first(void)
{
if (win_alloc_firstwin(NULL) == FAIL) {
- return FAIL;
+ // allocating first buffer before any autocmds should not fail.
+ abort();
}
first_tabpage = alloc_tabpage();
- first_tabpage->tp_topframe = topframe;
curtab = first_tabpage;
- curtab->tp_firstwin = firstwin;
- curtab->tp_lastwin = lastwin;
- curtab->tp_curwin = curwin;
-
- return OK;
+ unuse_tabpage(first_tabpage);
}
-// Init `aucmd_win`. This can only be done after the first window
+// Init `aucmd_win[idx]`. This can only be done after the first window
// is fully initialized, thus it can't be in win_alloc_first().
-void win_alloc_aucmd_win(void)
+void win_alloc_aucmd_win(int idx)
{
Error err = ERROR_INIT;
FloatConfig fconfig = FLOAT_CONFIG_INIT;
fconfig.width = Columns;
fconfig.height = 5;
fconfig.focusable = false;
- aucmd_win = win_new_float(NULL, true, fconfig, &err);
- aucmd_win->w_buffer->b_nwindows--;
- RESET_BINDING(aucmd_win);
+ aucmd_win[idx].auc_win = win_new_float(NULL, true, fconfig, &err);
+ aucmd_win[idx].auc_win->w_buffer->b_nwindows--;
+ RESET_BINDING(aucmd_win[idx].auc_win);
}
-/*
- * Allocate the first window or the first window in a new tab page.
- * When "oldwin" is NULL create an empty buffer for it.
- * When "oldwin" is not NULL copy info from it to the new window.
- * Return FAIL when something goes wrong (out of memory).
- */
+// Allocate the first window or the first window in a new tab page.
+// When "oldwin" is NULL create an empty buffer for it.
+// When "oldwin" is not NULL copy info from it to the new window.
+// Return FAIL when something goes wrong (out of memory).
static int win_alloc_firstwin(win_T *oldwin)
{
curwin = win_alloc(NULL, false);
@@ -3984,9 +3993,7 @@ static int win_alloc_firstwin(win_T *oldwin)
return OK;
}
-/*
- * Create a frame for window "wp".
- */
+// Create a frame for window "wp".
static void new_frame(win_T *wp)
{
frame_T *frp = xcalloc(1, sizeof(frame_T));
@@ -3996,12 +4003,11 @@ static void new_frame(win_T *wp)
frp->fr_win = wp;
}
-/*
- * Initialize the window and frame size to the maximum.
- */
+// Initialize the window and frame size to the maximum.
void win_init_size(void)
{
firstwin->w_height = (int)ROWS_AVAIL;
+ firstwin->w_prev_height = (int)ROWS_AVAIL;
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;
@@ -4012,9 +4018,7 @@ void win_init_size(void)
topframe->fr_width = Columns;
}
-/*
- * Allocate a new tabpage_T and init the values.
- */
+// Allocate a new tabpage_T and init the values.
static tabpage_T *alloc_tabpage(void)
{
static int last_tp_handle = 0;
@@ -4025,7 +4029,7 @@ static tabpage_T *alloc_tabpage(void)
// Init t: variables.
tp->tp_vars = tv_dict_alloc();
init_var_dict(tp->tp_vars, &tp->tp_winvar, VAR_SCOPE);
- tp->tp_diff_invalid = TRUE;
+ tp->tp_diff_invalid = true;
tp->tp_ch_used = p_ch;
return tp;
@@ -4033,11 +4037,9 @@ static tabpage_T *alloc_tabpage(void)
void free_tabpage(tabpage_T *tp)
{
- int idx;
-
pmap_del(handle_T)(&tabpage_handles, tp->handle);
diff_clear(tp);
- for (idx = 0; idx < SNAP_COUNT; ++idx) {
+ for (int idx = 0; idx < SNAP_COUNT; idx++) {
clear_snapshot(tp, idx);
}
vars_clear(&tp->tp_vars->dv_hashtab); // free all t: variables
@@ -4061,18 +4063,16 @@ void free_tabpage(tabpage_T *tp)
/// tabpage in case of 0.
/// @param filename Will be passed to apply_autocmds().
/// @return Was the new tabpage created successfully? FAIL or OK.
-int win_new_tabpage(int after, char_u *filename)
+int win_new_tabpage(int after, char *filename)
{
tabpage_T *old_curtab = curtab;
- tabpage_T *newtp;
- int n;
if (cmdwin_type != 0) {
emsg(_(e_cmdwin));
return FAIL;
}
- newtp = alloc_tabpage();
+ tabpage_T *newtp = alloc_tabpage();
// Remember the current windows in this Tab page.
if (leave_tabpage(curbuf, true) == FAIL) {
@@ -4097,7 +4097,7 @@ int win_new_tabpage(int after, char_u *filename)
if (after > 0) {
// Put new tab page before tab page "after".
- n = 2;
+ int n = 2;
for (tp = first_tabpage; tp->tp_next != NULL
&& n < after; tp = tp->tp_next) {
n++;
@@ -4110,12 +4110,13 @@ int win_new_tabpage(int after, char_u *filename)
win_init_size();
firstwin->w_winrow = tabline_height();
+ firstwin->w_prev_winrow = firstwin->w_winrow;
win_comp_scroll(curwin);
newtp->tp_topframe = topframe;
last_status(false);
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
tabpage_check_windows(old_curtab);
@@ -4125,7 +4126,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, (char *)filename, (char *)filename, false, curbuf);
+ apply_autocmds(EVENT_TABNEW, filename, filename, false, curbuf);
apply_autocmds(EVENT_TABENTER, NULL, NULL, false, curbuf);
return OK;
@@ -4136,11 +4137,9 @@ int win_new_tabpage(int after, char_u *filename)
return FAIL;
}
-/*
- * Open a new tab page if ":tab cmd" was used. It will edit the same buffer,
- * like with ":split".
- * Returns OK if a new tab page was created, FAIL otherwise.
- */
+// Open a new tab page if ":tab cmd" was used. It will edit the same buffer,
+// like with ":split".
+// Returns OK if a new tab page was created, FAIL otherwise.
int may_open_tabpage(void)
{
int n = (cmdmod.cmod_tab == 0) ? postponed_split_tab : cmdmod.cmod_tab;
@@ -4153,27 +4152,23 @@ int may_open_tabpage(void)
return FAIL;
}
-/*
- * Create up to "maxcount" tabpages with empty windows.
- * Returns the number of resulting tab pages.
- */
+// Create up to "maxcount" tabpages with empty windows.
+// Returns the number of resulting tab pages.
int make_tabpages(int maxcount)
{
int count = maxcount;
- int todo;
// Limit to 'tabpagemax' tabs.
if (count > p_tpm) {
count = (int)p_tpm;
}
- /*
- * Don't execute autocommands while creating the tab pages. Must do that
- * when putting the buffers in the windows.
- */
+ // Don't execute autocommands while creating the tab pages. Must do that
+ // when putting the buffers in the windows.
block_autocmds();
- for (todo = count - 1; todo > 0; --todo) {
+ int todo;
+ for (todo = count - 1; todo > 0; todo--) {
if (win_new_tabpage(0, NULL) == FAIL) {
break;
}
@@ -4237,9 +4232,7 @@ void close_tabpage(tabpage_T *tab)
free_tabpage(tab);
}
-/*
- * Find tab page "n" (first one is 1). Returns NULL when not found.
- */
+// Find tab page "n" (first one is 1). Returns NULL when not found.
tabpage_T *find_tabpage(int n)
{
tabpage_T *tp;
@@ -4251,10 +4244,8 @@ tabpage_T *find_tabpage(int n)
return tp;
}
-/*
- * Get index of tab page "tp". First one has index 1.
- * When not found returns number of tab pages plus one.
- */
+// Get index of tab page "tp". First one has index 1.
+// When not found returns number of tab pages plus one.
int tabpage_index(tabpage_T *ftp)
{
int i = 1;
@@ -4296,12 +4287,16 @@ static int leave_tabpage(buf_T *new_curbuf, bool trigger_leave_autocmds)
return FAIL;
}
}
+
+ reset_dragwin();
tp->tp_curwin = curwin;
tp->tp_prevwin = prevwin;
tp->tp_firstwin = firstwin;
tp->tp_lastwin = lastwin;
tp->tp_old_Rows_avail = ROWS_AVAIL;
- tp->tp_old_Columns = Columns;
+ if (tp->tp_old_Columns != -1) {
+ tp->tp_old_Columns = Columns;
+ }
firstwin = NULL;
lastwin = NULL;
return OK;
@@ -4319,10 +4314,7 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_a
win_T *next_prevwin = tp->tp_prevwin;
tabpage_T *old_curtab = curtab;
- curtab = tp;
- firstwin = tp->tp_firstwin;
- lastwin = tp->tp_lastwin;
- topframe = tp->tp_topframe;
+ use_tabpage(tp);
if (old_curtab != curtab) {
tabpage_check_windows(old_curtab);
@@ -4343,23 +4335,37 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_a
// Use the stored value of p_ch, so that it can be different for each tab page.
if (p_ch != curtab->tp_ch_used) {
clear_cmdline = true;
+ if (msg_grid.chars && p_ch < curtab->tp_ch_used) {
+ // TODO(bfredl): a bit expensive, should be enough to invalidate the
+ // region between the old and the new p_ch.
+ grid_invalidate(&msg_grid);
+ }
}
p_ch = curtab->tp_ch_used;
// When cmdheight is changed in a tab page with '<C-w>-', cmdline_row is
// changed but p_ch and tp_ch_used are not changed. Thus we also need to
// check cmdline_row.
- if ((row < cmdline_row) && (cmdline_row <= Rows - p_ch)) {
+ if (row < cmdline_row && cmdline_row <= Rows - p_ch) {
clear_cmdline = true;
}
+ // If there was a click in a window, it won't be usable for a following
+ // drag.
+ reset_dragwin();
+
// The tabpage line may have appeared or disappeared, may need to resize the frames for that.
// When the Vim window was resized or ROWS_AVAIL changed need to update frame sizes too.
if (curtab->tp_old_Rows_avail != ROWS_AVAIL || (old_off != firstwin->w_winrow)) {
win_new_screen_rows();
}
- if (curtab->tp_old_Columns != Columns && starting == 0) {
- win_new_screen_cols(); // update window widths
+ if (curtab->tp_old_Columns != Columns) {
+ if (starting == 0) {
+ win_new_screen_cols(); // update window widths
+ curtab->tp_old_Columns = Columns;
+ } else {
+ curtab->tp_old_Columns = -1; // update window widths later
+ }
}
lastused_tabpage = old_curtab;
@@ -4373,7 +4379,7 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_a
}
}
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
}
/// tells external UI that windows and inline floats in old_curtab are invisible
@@ -4405,16 +4411,10 @@ static void tabpage_check_windows(tabpage_T *old_curtab)
}
}
-/*
- * Go to tab page "n". For ":tab N" and "Ngt".
- * When "n" is 9999 go to the last tab page.
- */
+// Go to tab page "n". For ":tab N" and "Ngt".
+// When "n" is 9999 go to the last tab page.
void goto_tabpage(int n)
{
- tabpage_T *tp = NULL; // shut up compiler
- tabpage_T *ttp;
- int i;
-
if (text_locked()) {
// Not allowed when editing the command line.
text_locked_msg();
@@ -4429,6 +4429,8 @@ void goto_tabpage(int n)
return;
}
+ tabpage_T *tp = NULL; // shut up compiler
+
if (n == 0) {
// No count, go to next tab page, wrap around end.
if (curtab->tp_next == NULL) {
@@ -4439,8 +4441,8 @@ void goto_tabpage(int n)
} else if (n < 0) {
// "gT": go to previous tab page, wrap around end. "N gT" repeats
// this N times.
- ttp = curtab;
- for (i = n; i < 0; ++i) {
+ tabpage_T *ttp = curtab;
+ for (int i = n; i < 0; i++) {
for (tp = first_tabpage; tp->tp_next != ttp && tp->tp_next != NULL;
tp = tp->tp_next) {}
ttp = tp;
@@ -4474,6 +4476,7 @@ void goto_tabpage_tp(tabpage_T *tp, bool trigger_enter_autocmds, bool trigger_le
// Don't repeat a message in another tab page.
set_keep_msg(NULL, 0);
+ skip_win_fix_scroll = true;
if (tp != curtab && leave_tabpage(tp->tp_curwin->w_buffer,
trigger_leave_autocmds) == OK) {
if (valid_tabpage(tp)) {
@@ -4484,6 +4487,7 @@ void goto_tabpage_tp(tabpage_T *tp, bool trigger_enter_autocmds, bool trigger_le
trigger_leave_autocmds);
}
}
+ skip_win_fix_scroll = false;
}
/// Go to the last accessed tab page, if there is one.
@@ -4497,10 +4501,8 @@ bool goto_tabpage_lastused(void)
return false;
}
-/*
- * Enter window "wp" in tab page "tp".
- * Also updates the GUI tab.
- */
+// Enter window "wp" in tab page "tp".
+// Also updates the GUI tab.
void goto_tabpage_win(tabpage_T *tp, win_T *wp)
{
goto_tabpage_tp(tp, true, true);
@@ -4512,16 +4514,15 @@ void goto_tabpage_win(tabpage_T *tp, win_T *wp)
// Move the current tab page to after tab page "nr".
void tabpage_move(int nr)
{
- int n = 1;
- tabpage_T *tp;
- tabpage_T *tp_dst;
-
assert(curtab != NULL);
if (first_tabpage->tp_next == NULL) {
return;
}
+ int n = 1;
+ tabpage_T *tp;
+
for (tp = first_tabpage; tp->tp_next != NULL && n < nr; tp = tp->tp_next) {
n++;
}
@@ -4531,7 +4532,7 @@ void tabpage_move(int nr)
return;
}
- tp_dst = tp;
+ tabpage_T *tp_dst = tp;
// Remove the current tab page from the list of tab pages.
if (curtab == first_tabpage) {
@@ -4563,13 +4564,11 @@ void tabpage_move(int nr)
redraw_tabline = true;
}
-/*
- * Go to another window.
- * When jumping to another buffer, stop Visual mode. Do this before
- * changing windows so we can yank the selection into the '*' register.
- * When jumping to another window on the same buffer, adjust its cursor
- * position to keep the same Visual area.
- */
+// Go to another window.
+// When jumping to another buffer, stop Visual mode. Do this before
+// changing windows so we can yank the selection into the '*' register.
+// When jumping to another window on the same buffer, adjust its cursor
+// position to keep the same Visual area.
void win_goto(win_T *wp)
{
win_T *owp = curwin;
@@ -4596,9 +4595,7 @@ void win_goto(win_T *wp)
}
}
-/*
- * Find the tabpage for window "win".
- */
+// Find the tabpage for window "win".
tabpage_T *win_find_tabpage(win_T *win)
{
FOR_ALL_TAB_WINDOWS(tp, wp) {
@@ -4620,22 +4617,17 @@ tabpage_T *win_find_tabpage(win_T *win)
/// @return found window
win_T *win_vert_neighbor(tabpage_T *tp, win_T *wp, bool up, long count)
{
- frame_T *fr;
- frame_T *nfr;
- frame_T *foundfr;
-
- foundfr = wp->w_frame;
+ frame_T *foundfr = wp->w_frame;
if (wp->w_floating) {
return win_valid(prevwin) && !prevwin->w_floating ? prevwin : firstwin;
}
while (count--) {
- /*
- * First go upwards in the tree of frames until we find an upwards or
- * downwards neighbor.
- */
- fr = foundfr;
+ frame_T *nfr;
+ // First go upwards in the tree of frames until we find an upwards or
+ // downwards neighbor.
+ frame_T *fr = foundfr;
for (;;) {
if (fr == tp->tp_topframe) {
goto end;
@@ -4651,9 +4643,7 @@ win_T *win_vert_neighbor(tabpage_T *tp, win_T *wp, bool up, long count)
fr = fr->fr_parent;
}
- /*
- * Now go downwards to find the bottom or top frame in it.
- */
+ // Now go downwards to find the bottom or top frame in it.
for (;;) {
if (nfr->fr_layout == FR_LEAF) {
foundfr = nfr;
@@ -4703,22 +4693,17 @@ static void win_goto_ver(bool up, long count)
/// @return found window
win_T *win_horz_neighbor(tabpage_T *tp, win_T *wp, bool left, long count)
{
- frame_T *fr;
- frame_T *nfr;
- frame_T *foundfr;
-
- foundfr = wp->w_frame;
+ frame_T *foundfr = wp->w_frame;
if (wp->w_floating) {
return win_valid(prevwin) && !prevwin->w_floating ? prevwin : firstwin;
}
while (count--) {
- /*
- * First go upwards in the tree of frames until we find a left or
- * right neighbor.
- */
- fr = foundfr;
+ frame_T *nfr;
+ // First go upwards in the tree of frames until we find a left or
+ // right neighbor.
+ frame_T *fr = foundfr;
for (;;) {
if (fr == tp->tp_topframe) {
goto end;
@@ -4734,9 +4719,7 @@ win_T *win_horz_neighbor(tabpage_T *tp, win_T *wp, bool left, long count)
fr = fr->fr_parent;
}
- /*
- * Now go downwards to find the leftmost or rightmost frame in it.
- */
+ // Now go downwards to find the leftmost or rightmost frame in it.
for (;;) {
if (nfr->fr_layout == FR_LEAF) {
foundfr = nfr;
@@ -4828,7 +4811,9 @@ static void win_enter_ext(win_T *const wp, const int flags)
// Might need to scroll the old window before switching, e.g., when the
// cursor was moved.
- update_topline(curwin);
+ if (*p_spk == 'c') {
+ update_topline(curwin);
+ }
// may have to copy the buffer options when 'cpo' contains 'S'
if (wp->w_buffer != curbuf) {
@@ -4836,7 +4821,7 @@ static void win_enter_ext(win_T *const wp, const int flags)
}
if (!curwin_invalid) {
prevwin = curwin; // remember for CTRL-W p
- curwin->w_redr_status = TRUE;
+ curwin->w_redr_status = true;
}
curwin = wp;
curbuf = wp->w_buffer;
@@ -4845,7 +4830,11 @@ static void win_enter_ext(win_T *const wp, const int flags)
if (!virtual_active()) {
curwin->w_cursor.coladd = 0;
}
- changed_line_abv_curs(); // assume cursor position needs updating
+ if (*p_spk == 'c') {
+ changed_line_abv_curs(); // assume cursor position needs updating
+ } else {
+ win_fix_cursor(true);
+ }
fix_current_dir();
@@ -4867,7 +4856,7 @@ static void win_enter_ext(win_T *const wp, const int flags)
curwin->w_redr_status = true;
redraw_tabline = true;
if (restart_edit) {
- redraw_later(curwin, VALID); // causes status line redraw
+ redraw_later(curwin, UPD_VALID); // causes status line redraw
}
// change background color according to NormalNC,
@@ -4875,11 +4864,11 @@ static void win_enter_ext(win_T *const wp, const int flags)
if (curwin->w_hl_attr_normal != curwin->w_hl_attr_normalnc) {
// TODO(bfredl): eventually we should be smart enough
// to only recompose the window, not redraw it.
- redraw_later(curwin, NOT_VALID);
+ redraw_later(curwin, UPD_NOT_VALID);
}
if (prevwin) {
if (prevwin->w_hl_attr_normal != prevwin->w_hl_attr_normalnc) {
- redraw_later(prevwin, NOT_VALID);
+ redraw_later(prevwin, UPD_NOT_VALID);
}
}
@@ -4907,7 +4896,7 @@ void fix_current_dir(void)
// New directory is either the local directory of the window, tab or NULL.
char *new_dir = curwin->w_localdir ? curwin->w_localdir : curtab->tp_localdir;
char cwd[MAXPATHL];
- if (os_dirname((char_u *)cwd, MAXPATHL) != OK) {
+ if (os_dirname(cwd, MAXPATHL) != OK) {
cwd[0] = NUL;
}
@@ -4957,12 +4946,11 @@ win_T *buf_jump_open_win(buf_T *buf)
if (curwin->w_buffer == buf) {
win_enter(curwin, false);
return curwin;
- } else {
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer == buf) {
- win_enter(wp, false);
- return wp;
- }
+ }
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_buffer == buf) {
+ win_enter(wp, false);
+ return wp;
}
}
@@ -4970,7 +4958,7 @@ win_T *buf_jump_open_win(buf_T *buf)
}
/// Jump to the first open window in any tab page that contains buffer "buf",
-/// if one exists.
+/// if one exists. First search in the windows present in the current tab page.
/// @return the found window, or NULL.
win_T *buf_jump_open_tab(buf_T *buf)
{
@@ -5029,9 +5017,7 @@ static win_T *win_alloc(win_T *after, bool hidden)
// initialized yet. gui_create_scrollbar() may trigger a FocusGained
// event.
block_autocmds();
- /*
- * link the window in the window list
- */
+ // link the window in the window list
if (!hidden) {
win_append(after, new_wp);
}
@@ -5061,8 +5047,7 @@ static win_T *win_alloc(win_T *after, bool hidden)
foldInitWin(new_wp);
unblock_autocmds();
- new_wp->w_match_head = NULL;
- new_wp->w_next_match_id = 4;
+ new_wp->w_next_match_id = 1000; // up to 1000 can be picked by the user
return new_wp;
}
@@ -5081,9 +5066,6 @@ void free_wininfo(wininfo_T *wip, buf_T *bp)
/// @param tp tab page "win" is in, NULL for current
static void win_free(win_T *wp, tabpage_T *tp)
{
- int i;
- wininfo_T *wip;
-
pmap_del(handle_T)(&window_handles, wp->handle);
clearFolding(wp);
@@ -5115,7 +5097,7 @@ static void win_free(win_T *wp, tabpage_T *tp)
xfree(wp->w_lines);
- for (i = 0; i < wp->w_tagstacklen; i++) {
+ for (int i = 0; i < wp->w_tagstacklen; i++) {
xfree(wp->w_tagstack[i].tagname);
xfree(wp->w_tagstack[i].user_data);
}
@@ -5123,16 +5105,19 @@ 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, (long)wp->w_status_click_defs_size);
+ 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, (long)wp->w_winbar_click_defs_size);
+ stl_clear_click_defs(wp->w_winbar_click_defs, wp->w_winbar_click_defs_size);
xfree(wp->w_winbar_click_defs);
+ stl_clear_click_defs(wp->w_statuscol_click_defs, wp->w_statuscol_click_defs_size);
+ xfree(wp->w_statuscol_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) {
- for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) {
+ for (wininfo_T *wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) {
if (wip->wi_win == wp) {
wininfo_T *wip2;
@@ -5160,6 +5145,9 @@ static void win_free(win_T *wp, tabpage_T *tp)
}
}
+ // free the border title text
+ clear_virttext(&wp->w_float_config.title_chunks);
+
clear_matches(wp);
free_jumplist(wp);
@@ -5170,7 +5158,7 @@ static void win_free(win_T *wp, tabpage_T *tp)
win_free_grid(wp, false);
- if (wp != aucmd_win) {
+ if (win_valid_any_tab(wp)) {
win_remove(wp, tp);
}
if (autocmd_busy) {
@@ -5195,18 +5183,11 @@ void win_free_grid(win_T *wp, bool reinit)
}
}
-/*
- * Append window "wp" in the window list after window "after".
- */
+// Append window "wp" in the window list after window "after".
void win_append(win_T *after, win_T *wp)
{
- win_T *before;
-
- if (after == NULL) { // after NULL is in front of the first
- before = firstwin;
- } else {
- before = after->w_next;
- }
+ // after NULL is in front of the first
+ win_T *before = after == NULL ? firstwin : after->w_next;
wp->w_next = before;
wp->w_prev = after;
@@ -5243,9 +5224,7 @@ void win_remove(win_T *wp, tabpage_T *tp)
}
}
-/*
- * Append frame "frp" in a frame list after frame "after".
- */
+// Append frame "frp" in a frame list after frame "after".
static void frame_append(frame_T *after, frame_T *frp)
{
frp->fr_next = after->fr_next;
@@ -5256,9 +5235,7 @@ static void frame_append(frame_T *after, frame_T *frp)
frp->fr_prev = after;
}
-/*
- * Insert frame "frp" in a frame list before frame "before".
- */
+// Insert frame "frp" in a frame list before frame "before".
static void frame_insert(frame_T *before, frame_T *frp)
{
frp->fr_next = before;
@@ -5271,9 +5248,7 @@ static void frame_insert(frame_T *before, frame_T *frp)
}
}
-/*
- * Remove a frame from a frame list.
- */
+// Remove a frame from a frame list.
static void frame_remove(frame_T *frp)
{
if (frp->fr_prev != NULL) {
@@ -5294,7 +5269,7 @@ void win_new_screensize(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)) {
+ if (p_window == old_Rows - 1 || (old_Rows == 0 && !option_was_set("window"))) {
p_window = Rows - 1;
}
old_Rows = Rows;
@@ -5330,6 +5305,10 @@ void win_new_screen_rows(void)
win_reconfig_floats(); // The size of floats might change
compute_cmdrow();
curtab->tp_ch_used = p_ch;
+
+ if (*p_spk != 'c' && !skip_win_fix_scroll) {
+ win_fix_scroll(true);
+ }
}
/// Called from win_new_screensize() after Columns changed.
@@ -5350,40 +5329,290 @@ void win_new_screen_cols(void)
win_reconfig_floats(); // The size of floats might change
}
-/// Trigger WinScrolled for "curwin" if needed.
-void may_trigger_winscrolled(void)
+/// Make a snapshot of all the window scroll positions and sizes of the current
+/// tab page.
+void snapshot_windows_scroll_size(void)
{
- static bool recursive = false;
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ wp->w_last_topline = wp->w_topline;
+ wp->w_last_topfill = wp->w_topfill;
+ wp->w_last_leftcol = wp->w_leftcol;
+ wp->w_last_skipcol = wp->w_skipcol;
+ wp->w_last_width = wp->w_width;
+ wp->w_last_height = wp->w_height;
+ }
+}
- if (recursive || !has_event(EVENT_WINSCROLLED)) {
- return;
+static bool did_initial_scroll_size_snapshot = false;
+
+void may_make_initial_scroll_size_snapshot(void)
+{
+ if (!did_initial_scroll_size_snapshot) {
+ did_initial_scroll_size_snapshot = true;
+ snapshot_windows_scroll_size();
}
+}
- 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);
+/// Create a dictionary with information about size and scroll changes in a
+/// window.
+/// Returns the dictionary with refcount set to one.
+/// Returns NULL on internal error.
+static dict_T *make_win_info_dict(int width, int height, int topline, int topfill, int leftcol,
+ int skipcol)
+{
+ dict_T *const d = tv_dict_alloc();
+ d->dv_refcount = 1;
+
+ // not actually looping, for breaking out on error
+ while (1) {
+ typval_T tv = {
+ .v_lock = VAR_UNLOCKED,
+ .v_type = VAR_NUMBER,
+ };
+
+ tv.vval.v_number = width;
+ if (tv_dict_add_tv(d, S_LEN("width"), &tv) == FAIL) {
+ break;
+ }
+ tv.vval.v_number = height;
+ if (tv_dict_add_tv(d, S_LEN("height"), &tv) == FAIL) {
+ break;
+ }
+ tv.vval.v_number = topline;
+ if (tv_dict_add_tv(d, S_LEN("topline"), &tv) == FAIL) {
+ break;
+ }
+ tv.vval.v_number = topfill;
+ if (tv_dict_add_tv(d, S_LEN("topfill"), &tv) == FAIL) {
+ break;
+ }
+ tv.vval.v_number = leftcol;
+ if (tv_dict_add_tv(d, S_LEN("leftcol"), &tv) == FAIL) {
+ break;
+ }
+ tv.vval.v_number = skipcol;
+ if (tv_dict_add_tv(d, S_LEN("skipcol"), &tv) == FAIL) {
+ break;
+ }
+ return d;
+ }
+ tv_dict_unref(d);
+ return NULL;
+}
- recursive = true;
- apply_autocmds(EVENT_WINSCROLLED, winid, winid, false, wp->w_buffer);
- recursive = false;
+/// Return values of check_window_scroll_resize():
+enum {
+ CWSR_SCROLLED = 1, ///< at least one window scrolled
+ CWSR_RESIZED = 2, ///< at least one window size changed
+};
+
+/// This function is used for three purposes:
+/// 1. Goes over all windows in the current tab page and returns:
+/// 0 no scrolling and no size changes found
+/// CWSR_SCROLLED at least one window scrolled
+/// CWSR_RESIZED at least one window changed size
+/// CWSR_SCROLLED + CWSR_RESIZED both
+/// "size_count" is set to the nr of windows with size changes.
+/// "first_scroll_win" is set to the first window with any relevant changes.
+/// "first_size_win" is set to the first window with size changes.
+///
+/// 2. When the first three arguments are NULL and "winlist" is not NULL,
+/// "winlist" is set to the list of window IDs with size changes.
+///
+/// 3. When the first three arguments are NULL and "v_event" is not NULL,
+/// information about changed windows is added to "v_event".
+static int check_window_scroll_resize(int *size_count, win_T **first_scroll_win,
+ win_T **first_size_win, list_T *winlist, dict_T *v_event)
+{
+ int result = 0;
+ // int listidx = 0;
+ int tot_width = 0;
+ int tot_height = 0;
+ int tot_topline = 0;
+ int tot_topfill = 0;
+ int tot_leftcol = 0;
+ int tot_skipcol = 0;
- // an autocmd may close the window, "wp" may be invalid now
- if (win_valid_any_tab(wp)) {
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ // Skip floating windows that do not have a snapshot (usually because they are newly-created),
+ // as unlike split windows, creating floating windows do not cause other windows to resize.
+ if (wp->w_floating && wp->w_last_topline == 0) {
wp->w_last_topline = wp->w_topline;
+ wp->w_last_topfill = wp->w_topfill;
wp->w_last_leftcol = wp->w_leftcol;
+ wp->w_last_skipcol = wp->w_skipcol;
wp->w_last_width = wp->w_width;
wp->w_last_height = wp->w_height;
+ continue;
+ }
+
+ const bool size_changed = wp->w_last_width != wp->w_width
+ || wp->w_last_height != wp->w_height;
+ if (size_changed) {
+ result |= CWSR_RESIZED;
+ if (winlist != NULL) {
+ // Add this window to the list of changed windows.
+ typval_T tv = {
+ .v_lock = VAR_UNLOCKED,
+ .v_type = VAR_NUMBER,
+ .vval.v_number = wp->handle,
+ };
+ // tv_list_set_item(winlist, listidx++, &tv);
+ tv_list_append_owned_tv(winlist, tv);
+ } else if (size_count != NULL) {
+ assert(first_size_win != NULL && first_scroll_win != NULL);
+ (*size_count)++;
+ if (*first_size_win == NULL) {
+ *first_size_win = wp;
+ }
+ // For WinScrolled the first window with a size change is used
+ // even when it didn't scroll.
+ if (*first_scroll_win == NULL) {
+ *first_scroll_win = wp;
+ }
+ }
+ }
+
+ const bool scroll_changed = wp->w_last_topline != wp->w_topline
+ || wp->w_last_topfill != wp->w_topfill
+ || wp->w_last_leftcol != wp->w_leftcol
+ || wp->w_last_skipcol != wp->w_skipcol;
+ if (scroll_changed) {
+ result |= CWSR_SCROLLED;
+ if (first_scroll_win != NULL && *first_scroll_win == NULL) {
+ *first_scroll_win = wp;
+ }
+ }
+
+ if ((size_changed || scroll_changed) && v_event != NULL) {
+ // Add info about this window to the v:event dictionary.
+ int width = wp->w_width - wp->w_last_width;
+ int height = wp->w_height - wp->w_last_height;
+ int topline = wp->w_topline - wp->w_last_topline;
+ int topfill = wp->w_topfill - wp->w_last_topfill;
+ int leftcol = wp->w_leftcol - wp->w_last_leftcol;
+ int skipcol = wp->w_skipcol - wp->w_last_skipcol;
+ dict_T *d = make_win_info_dict(width, height, topline,
+ topfill, leftcol, skipcol);
+ if (d == NULL) {
+ break;
+ }
+ char winid[NUMBUFLEN];
+ int key_len = vim_snprintf(winid, sizeof(winid), "%d", wp->handle);
+ if (tv_dict_add_dict(v_event, winid, (size_t)key_len, d) == FAIL) {
+ tv_dict_unref(d);
+ break;
+ }
+ d->dv_refcount--;
+
+ tot_width += abs(width);
+ tot_height += abs(height);
+ tot_topline += abs(topline);
+ tot_topfill += abs(topfill);
+ tot_leftcol += abs(leftcol);
+ tot_skipcol += abs(skipcol);
+ }
+ }
+
+ if (v_event != NULL) {
+ dict_T *alldict = make_win_info_dict(tot_width, tot_height, tot_topline,
+ tot_topfill, tot_leftcol, tot_skipcol);
+ if (alldict != NULL) {
+ if (tv_dict_add_dict(v_event, S_LEN("all"), alldict) == FAIL) {
+ tv_dict_unref(alldict);
+ } else {
+ alldict->dv_refcount--;
+ }
}
}
+
+ return result;
}
-/*
- * Save the size of all windows in "gap".
- */
+/// Trigger WinScrolled and/or WinResized if any window in the current tab page
+/// scrolled or changed size.
+void may_trigger_win_scrolled_resized(void)
+{
+ static bool recursive = false;
+ const bool do_resize = has_event(EVENT_WINRESIZED);
+ const bool do_scroll = has_event(EVENT_WINSCROLLED);
+
+ if (recursive
+ || !(do_scroll || do_resize)
+ || !did_initial_scroll_size_snapshot) {
+ return;
+ }
+
+ int size_count = 0;
+ win_T *first_scroll_win = NULL, *first_size_win = NULL;
+ int cwsr = check_window_scroll_resize(&size_count,
+ &first_scroll_win, &first_size_win,
+ NULL, NULL);
+ int trigger_resize = do_resize && size_count > 0;
+ int trigger_scroll = do_scroll && cwsr != 0;
+ if (!trigger_resize && !trigger_scroll) {
+ return; // no relevant changes
+ }
+
+ list_T *windows_list = NULL;
+ if (trigger_resize) {
+ // Create the list for v:event.windows before making the snapshot.
+ // windows_list = tv_list_alloc_with_items(size_count);
+ windows_list = tv_list_alloc(size_count);
+ (void)check_window_scroll_resize(NULL, NULL, NULL, windows_list, NULL);
+ }
+
+ dict_T *scroll_dict = NULL;
+ if (trigger_scroll) {
+ // Create the dict with entries for v:event before making the snapshot.
+ scroll_dict = tv_dict_alloc();
+ scroll_dict->dv_refcount = 1;
+ (void)check_window_scroll_resize(NULL, NULL, NULL, NULL, scroll_dict);
+ }
+
+ // WinScrolled/WinResized are triggered only once, even when multiple
+ // windows scrolled or changed size. Store the current values before
+ // triggering the event, if a scroll or resize happens as a side effect
+ // then WinScrolled/WinResized is triggered for that later.
+ snapshot_windows_scroll_size();
+
+ recursive = true;
+
+ // If both are to be triggered do WinResized first.
+ if (trigger_resize) {
+ save_v_event_T save_v_event;
+ dict_T *v_event = get_v_event(&save_v_event);
+
+ if (tv_dict_add_list(v_event, S_LEN("windows"), windows_list) == OK) {
+ tv_dict_set_keys_readonly(v_event);
+
+ char winid[NUMBUFLEN];
+ vim_snprintf(winid, sizeof(winid), "%d", first_size_win->handle);
+ apply_autocmds(EVENT_WINRESIZED, winid, winid, false, first_size_win->w_buffer);
+ }
+ restore_v_event(v_event, &save_v_event);
+ }
+
+ if (trigger_scroll) {
+ save_v_event_T save_v_event;
+ dict_T *v_event = get_v_event(&save_v_event);
+
+ // Move the entries from scroll_dict to v_event.
+ tv_dict_extend(v_event, scroll_dict, "move");
+ tv_dict_set_keys_readonly(v_event);
+ tv_dict_unref(scroll_dict);
+
+ char winid[NUMBUFLEN];
+ vim_snprintf(winid, sizeof(winid), "%d", first_scroll_win->handle);
+ apply_autocmds(EVENT_WINSCROLLED, winid, winid, false, first_scroll_win->w_buffer);
+
+ restore_v_event(v_event, &save_v_event);
+ }
+
+ recursive = false;
+}
+
+// Save the size of all windows in "gap".
void win_size_save(garray_T *gap)
{
ga_init(gap, (int)sizeof(int), 1);
@@ -5450,27 +5679,20 @@ void win_reconfig_floats(void)
}
}
-/*
- * Update the position of the windows in frame "topfrp", using the width and
- * height of the frames.
- * "*row" and "*col" are the top-left position of the frame. They are updated
- * to the bottom-right position plus one.
- */
+// Update the position of the windows in frame "topfrp", using the width and
+// height of the frames.
+// "*row" and "*col" are the top-left position of the frame. They are updated
+// to the bottom-right position plus one.
static void frame_comp_pos(frame_T *topfrp, int *row, int *col)
{
- win_T *wp;
- frame_T *frp;
- int startcol;
- int startrow;
-
- wp = topfrp->fr_win;
+ win_T *wp = topfrp->fr_win;
if (wp != NULL) {
if (wp->w_winrow != *row
|| wp->w_wincol != *col) {
// position changed, redraw
wp->w_winrow = *row;
wp->w_wincol = *col;
- redraw_later(wp, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
wp->w_redr_status = true;
wp->w_pos_changed = true;
}
@@ -5478,8 +5700,9 @@ static void frame_comp_pos(frame_T *topfrp, int *row, int *col)
*row += h > topfrp->fr_height ? topfrp->fr_height : h;
*col += wp->w_width + wp->w_vsep_width;
} else {
- startrow = *row;
- startcol = *col;
+ int startrow = *row;
+ int startcol = *col;
+ frame_T *frp;
FOR_ALL_FRAMES(frp, topfrp->fr_child) {
if (topfrp->fr_layout == FR_ROW) {
*row = startrow; // all frames are at the same row
@@ -5491,19 +5714,15 @@ 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.
- */
+// Set current window height and take care of repositioning other windows to
+// fit around it.
void win_setheight(int height)
{
win_setheight_win(height, curwin);
}
-/*
- * Set the window height of window "win" and take care of repositioning other
- * windows to fit around it.
- */
+// Set the window height of window "win" and take care of repositioning other
+// windows to fit around it.
void win_setheight_win(int height, win_T *win)
{
// Always keep current window at least one line high, even when 'winminheight' is zero.
@@ -5513,7 +5732,7 @@ void win_setheight_win(int height, win_T *win)
if (win->w_floating) {
win->w_float_config.height = height;
win_config_float(win, win->w_float_config);
- redraw_later(win, NOT_VALID);
+ redraw_later(win, UPD_VALID);
} else {
frame_setheight(win->w_frame, height + win->w_hsep_height + win->w_status_height);
@@ -5533,34 +5752,29 @@ void win_setheight_win(int height, win_T *win)
curtab->tp_ch_used = p_ch;
msg_row = row;
msg_col = 0;
- redraw_all_later(NOT_VALID);
+
+ if (*p_spk != 'c') {
+ win_fix_scroll(true);
+ }
+
+ redraw_all_later(UPD_NOT_VALID);
redraw_cmdline = true;
}
}
-/*
- * 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
- * the are in the same FR_ROW frame.
- *
- * Strategy:
- * If the frame is part of a FR_COL frame, try fitting the frame in that
- * frame. If that doesn't work (the FR_COL frame is too small), recursively
- * go to containing frames to resize them and make room.
- * If the frame is part of a FR_ROW frame, all frames must be resized as well.
- * Check for the minimal height of the FR_ROW frame.
- * At the top level we can also use change the command line height.
- */
+// 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
+// the are in the same FR_ROW frame.
+//
+// Strategy:
+// If the frame is part of a FR_COL frame, try fitting the frame in that
+// frame. If that doesn't work (the FR_COL frame is too small), recursively
+// go to containing frames to resize them and make room.
+// If the frame is part of a FR_ROW frame, all frames must be resized as well.
+// Check for the minimal height of the FR_ROW frame.
+// At the top level we can also use change the command line height.
static void frame_setheight(frame_T *curfrp, int height)
{
- int room; // total number of lines available
- int take; // number of lines taken from other windows
- int room_cmdline; // lines available from cmdline
- int run;
- frame_T *frp;
- int h;
- int room_reserved;
-
// If the height already is the desired value, nothing to do.
if (curfrp->fr_height == height) {
return;
@@ -5568,11 +5782,15 @@ static void frame_setheight(frame_T *curfrp, int height)
if (curfrp->fr_parent == NULL) {
// topframe: can only change the command line height
+ // Avoid doing so with external messages.
+ if (ui_has(kUIMessages)) {
+ return;
+ }
if (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 = (int)MIN((p_ch > 0 ? ROWS_AVAIL + (p_ch - 1) : ROWS_AVAIL), height);
+ // `cmdheight` doesn't go below 1 if it wasn't set to 0 explicitly.
+ height = (int)MIN(ROWS_AVAIL + p_ch - !p_ch_was_zero, height);
}
if (height > 0) {
frame_new_height(curfrp, height, false, false);
@@ -5580,25 +5798,27 @@ static void frame_setheight(frame_T *curfrp, int height)
} else if (curfrp->fr_parent->fr_layout == FR_ROW) {
// Row of frames: Also need to resize frames left and right of this
// one. First check for the minimal height of these.
- h = frame_minheight(curfrp->fr_parent, NULL);
+ int h = frame_minheight(curfrp->fr_parent, NULL);
if (height < h) {
height = h;
}
frame_setheight(curfrp->fr_parent, height);
} else {
- /*
- * Column of frames: try to change only frames in this column.
- */
- /*
- * Do this twice:
- * 1: compute room available, if it's not enough try resizing the
- * containing frame.
- * 2: compute the room available and adjust the height to it.
- * Try not to reduce the height of a window with 'winfixheight' set.
- */
- for (run = 1; run <= 2; ++run) {
+ // Column of frames: try to change only frames in this column.
+
+ int room; // total number of lines available
+ int room_cmdline; // lines available from cmdline
+ int room_reserved;
+
+ // Do this twice:
+ // 1: compute room available, if it's not enough try resizing the
+ // containing frame.
+ // 2: compute the room available and adjust the height to it.
+ // Try not to reduce the height of a window with 'winfixheight' set.
+ for (int run = 1; run <= 2; run++) {
room = 0;
room_reserved = 0;
+ frame_T *frp;
FOR_ALL_FRAMES(frp, curfrp->fr_parent->fr_child) {
if (frp != curfrp
&& frp->fr_win != NULL
@@ -5630,14 +5850,12 @@ static void frame_setheight(frame_T *curfrp, int height)
}
frame_setheight(curfrp->fr_parent, height
+ frame_minheight(curfrp->fr_parent, NOWIN) - (int)p_wmh - 1);
- //NOTREACHED
+ // NOTREACHED
}
- /*
- * Compute the number of lines we will take from others frames (can be
- * negative!).
- */
- take = height - curfrp->fr_height;
+ // Compute the number of lines we will take from others frames (can be
+ // negative!).
+ int take = height - curfrp->fr_height;
// If there is not enough room, also reduce the height of a window
// with 'winfixheight' set.
@@ -5659,24 +5877,19 @@ static void frame_setheight(frame_T *curfrp, int height)
topframe->fr_height += room_cmdline;
}
- /*
- * set the current frame to the new height
- */
+ // set the current frame to the new height
frame_new_height(curfrp, height, false, false);
- /*
- * First take lines from the frames after the current frame. If
- * that is not enough, takes lines from frames above the current
- * frame.
- */
- for (run = 0; run < 2; ++run) {
- if (run == 0) {
- frp = curfrp->fr_next; // 1st run: start with next window
- } else {
- frp = curfrp->fr_prev; // 2nd run: start with prev window
- }
+ // First take lines from the frames after the current frame. If
+ // that is not enough, takes lines from frames above the current
+ // frame.
+ for (int run = 0; run < 2; run++) {
+ // 1st run: start with next window
+ // 2nd run: start with prev window
+ frame_T *frp = run == 0 ? curfrp->fr_next : curfrp->fr_prev;
+
while (frp != NULL && take != 0) {
- h = frame_minheight(frp, NULL);
+ int h = frame_minheight(frp, NULL);
if (room_reserved > 0
&& frp->fr_win != NULL
&& frp->fr_win->w_p_wfh) {
@@ -5709,10 +5922,8 @@ static void frame_setheight(frame_T *curfrp, int height)
}
}
-/*
- * Set current window width and take care of repositioning other windows to
- * fit around it.
- */
+// Set current window width and take care of repositioning other windows to
+// fit around it.
void win_setwidth(int width)
{
win_setwidth_win(width, curwin);
@@ -5735,32 +5946,23 @@ void win_setwidth_win(int width, win_T *wp)
if (wp->w_floating) {
wp->w_float_config.width = width;
win_config_float(wp, wp->w_float_config);
- redraw_later(wp, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
} else {
frame_setwidth(wp->w_frame, width + wp->w_vsep_width);
// recompute the window positions
(void)win_comp_pos();
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
}
}
-/*
- * Set the width of a frame to "width" and take care that all frames and
- * windows inside it are resized. Also resize frames above and below if the
- * are in the same FR_ROW frame.
- *
- * Strategy is similar to frame_setheight().
- */
+// Set the width of a frame to "width" and take care that all frames and
+// windows inside it are resized. Also resize frames above and below if the
+// are in the same FR_ROW frame.
+//
+// Strategy is similar to frame_setheight().
static void frame_setwidth(frame_T *curfrp, int width)
{
- int room; // total number of lines available
- int take; // number of lines taken from other windows
- int run;
- frame_T *frp;
- int w;
- int room_reserved;
-
// If the width already is the desired value, nothing to do.
if (curfrp->fr_width == width) {
return;
@@ -5774,23 +5976,25 @@ static void frame_setwidth(frame_T *curfrp, int width)
if (curfrp->fr_parent->fr_layout == FR_COL) {
// Column of frames: Also need to resize frames above and below of
// this one. First check for the minimal width of these.
- w = frame_minwidth(curfrp->fr_parent, NULL);
+ int w = frame_minwidth(curfrp->fr_parent, NULL);
if (width < w) {
width = w;
}
frame_setwidth(curfrp->fr_parent, width);
} else {
- /*
- * Row of frames: try to change only frames in this row.
- *
- * Do this twice:
- * 1: compute room available, if it's not enough try resizing the
- * containing frame.
- * 2: compute the room available and adjust the width to it.
- */
- for (run = 1; run <= 2; ++run) {
+ // Row of frames: try to change only frames in this row.
+ //
+ // Do this twice:
+ // 1: compute room available, if it's not enough try resizing the
+ // containing frame.
+ // 2: compute the room available and adjust the width to it.
+
+ int room; // total number of lines available
+ int room_reserved;
+ for (int run = 1; run <= 2; run++) {
room = 0;
room_reserved = 0;
+ frame_T *frp;
FOR_ALL_FRAMES(frp, curfrp->fr_parent->fr_child) {
if (frp != curfrp
&& frp->fr_win != NULL
@@ -5814,11 +6018,9 @@ static void frame_setwidth(frame_T *curfrp, int width)
+ frame_minwidth(curfrp->fr_parent, NOWIN) - (int)p_wmw - 1);
}
- /*
- * Compute the number of lines we will take from others frames (can be
- * negative!).
- */
- take = width - curfrp->fr_width;
+ // Compute the number of lines we will take from others frames (can be
+ // negative!).
+ int take = width - curfrp->fr_width;
// If there is not enough room, also reduce the width of a window
// with 'winfixwidth' set.
@@ -5831,24 +6033,19 @@ static void frame_setwidth(frame_T *curfrp, int width)
room_reserved = 0;
}
- /*
- * set the current frame to the new width
- */
+ // set the current frame to the new width
frame_new_width(curfrp, width, false, false);
- /*
- * First take lines from the frames right of the current frame. If
- * that is not enough, takes lines from frames left of the current
- * frame.
- */
- for (run = 0; run < 2; ++run) {
- if (run == 0) {
- frp = curfrp->fr_next; // 1st run: start with next window
- } else {
- frp = curfrp->fr_prev; // 2nd run: start with prev window
- }
+ // First take lines from the frames right of the current frame. If
+ // that is not enough, takes lines from frames left of the current
+ // frame.
+ for (int run = 0; run < 2; run++) {
+ // 1st run: start with next window
+ // 2nd run: start with prev window
+ frame_T *frp = run == 0 ? curfrp->fr_next : curfrp->fr_prev;
+
while (frp != NULL && take != 0) {
- w = frame_minwidth(frp, NULL);
+ int w = frame_minwidth(frp, NULL);
if (room_reserved > 0
&& frp->fr_win != NULL
&& frp->fr_win->w_p_wfw) {
@@ -5924,22 +6121,14 @@ void win_setminwidth(void)
/// Status line of dragwin is dragged "offset" lines down (negative is up).
void win_drag_status_line(win_T *dragwin, int offset)
{
- frame_T *curfr;
- frame_T *fr;
- int room;
- int row;
- bool up; // if true, drag status line up, otherwise down
- int n;
- static bool p_ch_was_zero = false;
+ frame_T *fr = dragwin->w_frame;
- // If the user explicitly set 'cmdheight' to zero, then allow for dragging
- // the status line making it zero again.
- if (p_ch == 0) {
- p_ch_was_zero = true;
+ // Avoid changing command line height with external messages.
+ if (fr->fr_next == NULL && ui_has(kUIMessages)) {
+ return;
}
- fr = dragwin->w_frame;
- curfr = fr;
+ frame_T *curfr = fr;
if (fr != topframe) { // more than one window
fr = fr->fr_parent;
// When the parent frame is not a column of frames, its parent should
@@ -5964,8 +6153,10 @@ void win_drag_status_line(win_T *dragwin, int offset)
}
}
- if (offset < 0) { // drag up
- up = true;
+ int room;
+ const bool up = offset < 0; // if true, drag status line up, otherwise down
+
+ if (up) { // drag up
offset = -offset;
// sum up the room of the current frame and above it
if (fr == curfr) {
@@ -5982,7 +6173,6 @@ void win_drag_status_line(win_T *dragwin, int offset)
}
fr = curfr->fr_next; // put fr at frame that grows
} else { // drag down
- up = false;
// Only dragging the last status line can reduce p_ch.
room = Rows - cmdline_row;
if (curfr->fr_next != NULL) {
@@ -6007,10 +6197,8 @@ void win_drag_status_line(win_T *dragwin, int offset)
return;
}
- /*
- * Grow frame fr by "offset" lines.
- * Doesn't happen when dragging the last status line up.
- */
+ // Grow frame fr by "offset" lines.
+ // Doesn't happen when dragging the last status line up.
if (fr != NULL) {
frame_new_height(fr, fr->fr_height + offset, up, false);
}
@@ -6020,11 +6208,9 @@ void win_drag_status_line(win_T *dragwin, int offset)
} else {
fr = curfr->fr_next; // next frame gets smaller
}
- /*
- * Now make the other frames smaller.
- */
+ // Now make the other frames smaller.
while (fr != NULL && offset > 0) {
- n = frame_minheight(fr, NULL);
+ int n = frame_minheight(fr, NULL);
if (fr->fr_height - offset <= n) {
offset -= fr->fr_height - n;
frame_new_height(fr, n, !up, false);
@@ -6038,7 +6224,7 @@ void win_drag_status_line(win_T *dragwin, int offset)
fr = fr->fr_next;
}
}
- row = win_comp_pos();
+ int row = win_comp_pos();
grid_fill(&default_grid, row, cmdline_row, 0, Columns, ' ', ' ', 0);
if (msg_grid.chars) {
clear_cmdline = true;
@@ -6046,26 +6232,23 @@ void win_drag_status_line(win_T *dragwin, int offset)
cmdline_row = row;
p_ch = MAX(Rows - cmdline_row, p_ch_was_zero ? 0 : 1);
curtab->tp_ch_used = p_ch;
- redraw_all_later(SOME_VALID);
+
+ if (*p_spk != 'c') {
+ win_fix_scroll(true);
+ }
+
+ redraw_all_later(UPD_SOME_VALID);
showmode();
}
-/*
- * Separator line of dragwin is dragged "offset" lines right (negative is left).
- */
+// Separator line of dragwin is dragged "offset" lines right (negative is left).
void win_drag_vsep_line(win_T *dragwin, int offset)
{
- frame_T *curfr;
- frame_T *fr;
- int room;
- bool left; // if true, drag separator line left, otherwise right
- int n;
-
- fr = dragwin->w_frame;
+ frame_T *fr = dragwin->w_frame;
if (fr == topframe) { // only one window (cannot happen?)
return;
}
- curfr = fr;
+ frame_T *curfr = fr;
fr = fr->fr_parent;
// When the parent frame is not a row of frames, its parent should be.
if (fr->fr_layout != FR_ROW) {
@@ -6090,8 +6273,10 @@ void win_drag_vsep_line(win_T *dragwin, int offset)
}
}
- if (offset < 0) { // drag left
- left = true;
+ int room;
+ const bool left = offset < 0; // if true, drag separator line left, otherwise right
+
+ if (left) { // drag left
offset = -offset;
// sum up the room of the current frame and left of it
room = 0;
@@ -6103,7 +6288,6 @@ void win_drag_vsep_line(win_T *dragwin, int offset)
}
fr = curfr->fr_next; // put fr at frame that grows
} else { // drag right
- left = false;
// sum up the room of frames right of the current one
room = 0;
FOR_ALL_FRAMES(fr, curfr->fr_next) {
@@ -6138,7 +6322,7 @@ void win_drag_vsep_line(win_T *dragwin, int offset)
fr = curfr->fr_next; // next frame gets smaller
}
while (fr != NULL && offset > 0) {
- n = frame_minwidth(fr, NULL);
+ int n = frame_minwidth(fr, NULL);
if (fr->fr_width - offset <= n) {
offset -= fr->fr_width - n;
frame_new_width(fr, n, !left, false);
@@ -6153,7 +6337,7 @@ void win_drag_vsep_line(win_T *dragwin, int offset)
}
}
(void)win_comp_pos();
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
}
#define FRACTION_MULT 16384L
@@ -6170,6 +6354,98 @@ void set_fraction(win_T *wp)
}
}
+/// Handle scroll position for 'splitkeep'. Replaces scroll_to_fraction()
+/// call from win_set_inner_size(). Instead we iterate over all windows in a
+/// tabpage and calculate the new scroll position.
+/// TODO(luukvbaal): Ensure this also works with wrapped lines.
+/// Requires topline to be able to be set to a bufferline with some
+/// offset(row-wise scrolling/smoothscroll).
+void win_fix_scroll(int resize)
+{
+ linenr_T lnum;
+
+ skip_update_topline = true;
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ // Skip when window height has not changed or when floating.
+ if (!wp->w_floating && wp->w_height != wp->w_prev_height) {
+ // If window has moved update botline to keep the same screenlines.
+ if (*p_spk == 's' && wp->w_winrow != wp->w_prev_winrow
+ && wp->w_botline - 1 <= wp->w_buffer->b_ml.ml_line_count) {
+ lnum = wp->w_cursor.lnum;
+ int diff = (wp->w_winrow - wp->w_prev_winrow)
+ + (wp->w_height - wp->w_prev_height);
+ wp->w_cursor.lnum = wp->w_botline - 1;
+ // Add difference in height and row to botline.
+ if (diff > 0) {
+ cursor_down_inner(wp, diff);
+ } else {
+ cursor_up_inner(wp, -diff);
+ }
+ // Bring the new cursor position to the bottom of the screen.
+ wp->w_fraction = FRACTION_MULT;
+ scroll_to_fraction(wp, wp->w_prev_height);
+ wp->w_cursor.lnum = lnum;
+ } else if (wp == curwin) {
+ wp->w_valid &= ~VALID_CROW;
+ }
+ invalidate_botline_win(wp);
+ validate_botline(wp);
+ }
+ wp->w_prev_height = wp->w_height;
+ wp->w_prev_winrow = wp->w_winrow;
+ }
+ skip_update_topline = false;
+ // Ensure cursor is valid when not in normal mode or when resized.
+ if (!(get_real_state() & (MODE_NORMAL|MODE_CMDLINE|MODE_TERMINAL))) {
+ win_fix_cursor(false);
+ } else if (resize) {
+ win_fix_cursor(true);
+ }
+}
+
+/// Make sure the cursor position is valid for 'splitkeep'.
+/// If it is not, put the cursor position in the jumplist and move it.
+/// If we are not in normal mode, scroll to make valid instead.
+static void win_fix_cursor(int normal)
+{
+ win_T *wp = curwin;
+ long so = get_scrolloff_value(wp);
+ linenr_T nlnum = 0;
+ linenr_T lnum = wp->w_cursor.lnum;
+
+ if (wp->w_buffer->b_ml.ml_line_count < wp->w_height
+ || skip_win_fix_cursor) {
+ return;
+ }
+
+ // Determine valid cursor range.
+ so = MIN(wp->w_height_inner / 2, so);
+ wp->w_cursor.lnum = wp->w_topline;
+ linenr_T top = cursor_down_inner(wp, so);
+ wp->w_cursor.lnum = wp->w_botline - 1;
+ linenr_T bot = cursor_up_inner(wp, so);
+ wp->w_cursor.lnum = lnum;
+ // Check if cursor position is above or below valid cursor range.
+ if (lnum > bot && (wp->w_botline - wp->w_buffer->b_ml.ml_line_count) != 1) {
+ nlnum = bot;
+ } else if (lnum < top && wp->w_topline != 1) {
+ nlnum = (so == wp->w_height / 2) ? bot : top;
+ }
+
+ if (nlnum) { // Cursor is invalid for current scroll position.
+ if (normal) {
+ // Save to jumplist and set cursor to avoid scrolling.
+ setmark('\'');
+ wp->w_cursor.lnum = nlnum;
+ } else {
+ // Scroll instead when not in normal mode.
+ wp->w_fraction = (nlnum == bot) ? FRACTION_MULT : 0;
+ scroll_to_fraction(wp, wp->w_prev_height);
+ validate_botline(curwin);
+ }
+ }
+}
+
// Set the height of a window.
// "height" excludes any window toolbar.
// This takes care of the things inside the window, not what happens to the
@@ -6192,8 +6468,6 @@ void win_new_height(win_T *wp, int height)
void scroll_to_fraction(win_T *wp, int prev_height)
{
- linenr_T lnum;
- int sline, line_size;
int height = wp->w_height_inner;
// Don't change w_topline in any of these cases:
@@ -6205,17 +6479,15 @@ void scroll_to_fraction(win_T *wp, int prev_height)
&& (!wp->w_p_scb || wp == curwin)
&& (height < wp->w_buffer->b_ml.ml_line_count
|| wp->w_topline > 1)) {
- /*
- * Find a value for w_topline that shows the cursor at the same
- * relative position in the window as before (more or less).
- */
- lnum = wp->w_cursor.lnum;
+ // Find a value for w_topline that shows the cursor at the same
+ // relative position in the window as before (more or less).
+ linenr_T lnum = wp->w_cursor.lnum;
if (lnum < 1) { // can happen when starting up
lnum = 1;
}
wp->w_wrow = (int)((long)wp->w_fraction * (long)height - 1L) / FRACTION_MULT;
- line_size = plines_win_col(wp, lnum, (long)(wp->w_cursor.col)) - 1;
- sline = wp->w_wrow - line_size;
+ int line_size = plines_win_col(wp, lnum, (long)(wp->w_cursor.col)) - 1;
+ int sline = wp->w_wrow - line_size;
if (sline >= 0) {
// Make sure the whole cursor line is visible, if possible.
@@ -6228,11 +6500,9 @@ void scroll_to_fraction(win_T *wp, int prev_height)
}
if (sline < 0) {
- /*
- * Cursor line would go off top of screen if w_wrow was this high.
- * Make cursor line the first line in the window. If not enough
- * room use w_skipcol;
- */
+ // Cursor line would go off top of screen if w_wrow was this high.
+ // Make cursor line the first line in the window. If not enough
+ // room use w_skipcol;
wp->w_wrow = line_size;
if (wp->w_wrow >= wp->w_height_inner
&& (wp->w_width_inner - win_col_off(wp)) > 0) {
@@ -6264,10 +6534,8 @@ void scroll_to_fraction(win_T *wp, int prev_height)
}
if (sline < 0) {
- /*
- * Line we want at top would go off top of screen. Use next
- * line instead.
- */
+ // Line we want at top would go off top of screen. Use next
+ // line instead.
(void)hasFoldingWin(wp, lnum, NULL, &lnum, true, NULL);
lnum++;
wp->w_wrow -= line_size + sline;
@@ -6290,9 +6558,7 @@ void scroll_to_fraction(win_T *wp, int prev_height)
wp->w_prev_fraction_row = wp->w_wrow;
}
- win_comp_scroll(wp);
- redraw_later(wp, SOME_VALID);
- wp->w_redr_status = true;
+ redraw_later(wp, UPD_SOME_VALID);
invalidate_botline_win(wp);
}
@@ -6311,7 +6577,7 @@ void win_set_inner_size(win_T *wp, bool valid_cursor)
if (height != prev_height) {
if (height > 0 && valid_cursor) {
- if (wp == curwin) {
+ if (wp == curwin && *p_spk == 'c') {
// w_wrow needs to be valid. When setting 'laststatus' this may
// call win_new_height() recursively.
validate_cursor();
@@ -6325,14 +6591,14 @@ void win_set_inner_size(win_T *wp, bool valid_cursor)
}
wp->w_skipcol = 0;
wp->w_height_inner = height;
+ win_comp_scroll(wp);
// There is no point in adjusting the scroll position when exiting. Some
// values might be invalid.
- // Skip scroll_to_fraction() when 'cmdheight' was set to one from zero.
- if (!exiting && !made_cmdheight_nonzero && valid_cursor) {
+ if (valid_cursor && !exiting && *p_spk == 'c') {
scroll_to_fraction(wp, prev_height);
}
- redraw_later(wp, NOT_VALID); // SOME_VALID??
+ redraw_later(wp, UPD_SOME_VALID);
}
if (width != wp->w_width_inner) {
@@ -6342,11 +6608,13 @@ void win_set_inner_size(win_T *wp, bool valid_cursor)
changed_line_abv_curs_win(wp);
invalidate_botline_win(wp);
if (wp == curwin) {
+ skip_update_topline = (*p_spk != 'c');
update_topline(wp);
curs_columns(wp, true); // validate w_wrow
+ skip_update_topline = false;
}
}
- redraw_later(wp, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
}
if (wp->w_buffer->terminal) {
@@ -6357,6 +6625,7 @@ void win_set_inner_size(win_T *wp, bool valid_cursor)
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];
+ wp->w_redr_status = true;
}
static int win_border_height(win_T *wp)
@@ -6373,17 +6642,15 @@ static int win_border_width(win_T *wp)
void win_new_width(win_T *wp, int width)
{
wp->w_width = width;
- win_set_inner_size(wp, true);
-
- wp->w_redr_status = true;
wp->w_pos_changed = true;
+ win_set_inner_size(wp, true);
}
void win_comp_scroll(win_T *wp)
{
const long old_w_p_scr = wp->w_p_scr;
- wp->w_p_scr = wp->w_height / 2;
+ wp->w_p_scr = wp->w_height_inner / 2;
if (wp->w_p_scr == 0) {
wp->w_p_scr = 1;
}
@@ -6394,13 +6661,9 @@ void win_comp_scroll(win_T *wp)
}
}
-/*
- * command_height: called whenever p_ch has been changed
- */
+/// command_height: called whenever p_ch has been changed.
void command_height(void)
{
- int h;
- frame_T *frp;
int old_p_ch = (int)curtab->tp_ch_used;
// Use the value of p_ch that we remembered. This is needed for when the
@@ -6408,11 +6671,8 @@ void command_height(void)
// p_ch was changed in another tab page.
curtab->tp_ch_used = p_ch;
- // If the space for the command line is already more than 'cmdheight' there
- // is nothing to do (window size must have decreased).
- if (p_ch > old_p_ch && cmdline_row <= Rows - p_ch) {
- return;
- }
+ // Update cmdline_row to what it should be: just below the last window.
+ cmdline_row = topframe->fr_height + tabline_height() + global_stl_height();
// If cmdline_row is smaller than what it is supposed to be for 'cmdheight'
// then set old_p_ch to what it would be, so that the windows get resized
@@ -6422,7 +6682,7 @@ void command_height(void)
}
// Find bottom frame with width of screen.
- frp = lastwin_nofloating()->w_frame;
+ frame_T *frp = lastwin_nofloating()->w_frame;
while (frp->fr_width != Columns && frp->fr_parent != NULL) {
frp = frp->fr_parent;
}
@@ -6445,7 +6705,7 @@ void command_height(void)
cmdline_row = Rows - (int)p_ch;
break;
}
- h = frp->fr_height - frame_minheight(frp, NULL);
+ int h = frp->fr_height - frame_minheight(frp, NULL);
if (h > p_ch - old_p_ch) {
h = (int)p_ch - old_p_ch;
}
@@ -6479,10 +6739,8 @@ void command_height(void)
}
}
-/*
- * Resize frame "frp" to be "n" lines higher (negative for less high).
- * Also resize the frames it is contained in.
- */
+// Resize frame "frp" to be "n" lines higher (negative for less high).
+// Also resize the frames it is contained in.
static void frame_add_height(frame_T *frp, int n)
{
frame_new_height(frp, frp->fr_height + n, false, false);
@@ -6495,12 +6753,10 @@ static void frame_add_height(frame_T *frp, int n)
}
}
-/*
- * Get the file name at the cursor.
- * If Visual mode is active, use the selected text if it's in one line.
- * Returns the name in allocated memory, NULL for failure.
- */
-char_u *grab_file_name(long count, linenr_T *file_lnum)
+// Get the file name at the cursor.
+// If Visual mode is active, use the selected text if it's in one line.
+// Returns the name in allocated memory, NULL for failure.
+char *grab_file_name(long count, linenr_T *file_lnum)
{
int options = FNAME_MESS | FNAME_EXP | FNAME_REL | FNAME_UNESC;
if (VIsual_active) {
@@ -6510,33 +6766,31 @@ char_u *grab_file_name(long count, linenr_T *file_lnum)
return NULL;
}
// Only recognize ":123" here
- if (file_lnum != NULL && ptr[len] == ':' && isdigit(ptr[len + 1])) {
+ if (file_lnum != NULL && ptr[len] == ':' && isdigit((uint8_t)ptr[len + 1])) {
char *p = ptr + len + 1;
*file_lnum = (linenr_T)getdigits_long(&p, false, 0);
}
- return find_file_name_in_path((char_u *)ptr, len, options, count, (char_u *)curbuf->b_ffname);
+ return find_file_name_in_path(ptr, len, options, count, curbuf->b_ffname);
}
return file_name_at_cursor(options | FNAME_HYP, count, file_lnum);
}
-/*
- * Return the file name under or after the cursor.
- *
- * The 'path' option is searched if the file name is not absolute.
- * The string returned has been alloc'ed and should be freed by the caller.
- * NULL is returned if the file name or file is not found.
- *
- * options:
- * FNAME_MESS give error messages
- * FNAME_EXP expand to path
- * FNAME_HYP check for hypertext link
- * FNAME_INCL apply "includeexpr"
- */
-char_u *file_name_at_cursor(int options, long count, linenr_T *file_lnum)
+// Return the file name under or after the cursor.
+//
+// The 'path' option is searched if the file name is not absolute.
+// The string returned has been alloc'ed and should be freed by the caller.
+// NULL is returned if the file name or file is not found.
+//
+// options:
+// FNAME_MESS give error messages
+// FNAME_EXP expand to path
+// FNAME_HYP check for hypertext link
+// FNAME_INCL apply "includeexpr"
+char *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, (char_u *)curbuf->b_ffname,
+ curwin->w_cursor.col, options, count, curbuf->b_ffname,
file_lnum);
}
@@ -6544,19 +6798,12 @@ char_u *file_name_at_cursor(int options, long count, linenr_T *file_lnum)
/// @param file_lnum line number after the file name
///
/// @return the name of the file under or after ptr[col]. Otherwise like file_name_at_cursor().
-char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u *rel_fname,
- linenr_T *file_lnum)
+char *file_name_in_line(char *line, int col, int options, long count, char *rel_fname,
+ linenr_T *file_lnum)
{
- char *ptr;
- size_t len;
- bool in_type = true;
- bool is_url = false;
-
- /*
- * search forward for what could be the start of a file name
- */
- ptr = (char *)line + col;
- while (*ptr != NUL && !vim_isfilec(*ptr)) {
+ // search forward for what could be the start of a file name
+ char *ptr = line + col;
+ while (*ptr != NUL && !vim_isfilec((uint8_t)(*ptr))) {
MB_PTR_ADV(ptr);
}
if (*ptr == NUL) { // nothing found
@@ -6566,28 +6813,29 @@ char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u
return NULL;
}
- /*
- * Search backward for first char of the file name.
- * Go one char back to ":" before "//" even when ':' is not in 'isfname'.
- */
- while ((char_u *)ptr > line) {
- if ((len = (size_t)(utf_head_off(line, (char_u *)ptr - 1))) > 0) {
+ size_t len;
+ bool in_type = true;
+ bool is_url = false;
+
+ // 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) {
ptr -= len + 1;
- } else if (vim_isfilec(ptr[-1]) || ((options & FNAME_HYP) && path_is_url(ptr - 1))) {
+ } else if (vim_isfilec((uint8_t)ptr[-1])
+ || ((options & FNAME_HYP) && path_is_url(ptr - 1))) {
ptr--;
} else {
break;
}
}
- /*
- * Search forward for the last char of the file name.
- * Also allow "://" when ':' is not in 'isfname'.
- */
+ // Search forward for the last char of the file name.
+ // Also allow ":/" when ':' is not in 'isfname'.
len = 0;
- while (vim_isfilec(ptr[len]) || (ptr[len] == '\\' && ptr[len + 1] == ' ')
+ while (vim_isfilec((uint8_t)ptr[len]) || (ptr[len] == '\\' && ptr[len + 1] == ' ')
|| ((options & FNAME_HYP) && path_is_url(ptr + len))
- || (is_url && vim_strchr(":?&=", ptr[len]) != NULL)) {
+ || (is_url && vim_strchr(":?&=", (uint8_t)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')
@@ -6606,43 +6854,40 @@ char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u
len += (size_t)(utfc_ptr2len(ptr + len));
}
- /*
- * If there is trailing punctuation, remove it.
- * But don't remove "..", could be a directory name.
- */
- if (len > 2 && vim_strchr(".,:;!", ptr[len - 1]) != NULL
+ // If there is trailing punctuation, remove it.
+ // But don't remove "..", could be a directory name.
+ if (len > 2 && vim_strchr(".,:;!", (uint8_t)ptr[len - 1]) != NULL
&& ptr[len - 2] != '.') {
len--;
}
if (file_lnum != NULL) {
- char *p;
const char *line_english = " line ";
const char *line_transl = _(line_msg);
// Get the number after the file name and a separator character.
// Also accept " line 999" with and without the same translation as
// used in last_set_msg().
- p = ptr + len;
- if (STRNCMP(p, line_english, STRLEN(line_english)) == 0) {
- p += STRLEN(line_english);
- } else if (STRNCMP(p, line_transl, STRLEN(line_transl)) == 0) {
- p += STRLEN(line_transl);
+ char *p = ptr + len;
+ if (strncmp(p, line_english, strlen(line_english)) == 0) {
+ p += strlen(line_english);
+ } else if (strncmp(p, line_transl, strlen(line_transl)) == 0) {
+ p += strlen(line_transl);
} else {
p = skipwhite(p);
}
if (*p != NUL) {
- if (!isdigit(*p)) {
+ if (!isdigit((uint8_t)(*p))) {
p++; // skip the separator
}
p = skipwhite(p);
- if (isdigit(*p)) {
+ if (isdigit((uint8_t)(*p))) {
*file_lnum = (linenr_T)getdigits_long(&p, false, 0);
}
}
}
- return find_file_name_in_path((char_u *)ptr, len, options, count, rel_fname);
+ return find_file_name_in_path(ptr, len, options, count, rel_fname);
}
/// Add or remove a status line from window(s), according to the
@@ -6667,7 +6912,7 @@ static void win_remove_status_line(win_T *wp, bool add_hsep)
}
comp_col();
- stl_clear_click_defs(wp->w_status_click_defs, (long)wp->w_status_click_defs_size);
+ 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;
@@ -6726,23 +6971,19 @@ static bool resize_frame_for_winbar(frame_T *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();
}
+ 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;
+ win_T *wp = fr->fr_win;
bool is_last = is_bottom_win(wp);
if (is_last) {
@@ -6756,6 +6997,10 @@ static void last_status_rec(frame_T *fr, bool statusline, bool is_stl_global)
}
comp_col();
}
+ // Set prev_height when difference is due to 'laststatus'.
+ if (abs(wp->w_height - wp->w_prev_height) == 1) {
+ wp->w_prev_height = wp->w_height;
+ }
} 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
@@ -6766,9 +7011,9 @@ static void last_status_rec(frame_T *fr, bool statusline, bool is_stl_global)
wp->w_hsep_height = 0;
comp_col();
}
- redraw_all_later(SOME_VALID);
} else {
// For a column or row frame, recursively call this function for all child frames
+ frame_T *fp;
FOR_ALL_FRAMES(fp, fr->fr_child) {
last_status_rec(fp, statusline, is_stl_global);
}
@@ -6799,11 +7044,10 @@ int set_winbar_win(win_T *wp, bool make_room, bool valid_cursor)
}
wp->w_winbar_height = winbar_height;
win_set_inner_size(wp, valid_cursor);
- 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, (long)wp->w_winbar_click_defs_size);
+ 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;
@@ -6889,7 +7133,7 @@ bool only_one_window(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_buffer != NULL
&& (!((bt_help(wp->w_buffer) && !bt_help(curbuf)) || wp->w_floating
- || wp->w_p_pvw) || wp == curwin) && wp != aucmd_win) {
+ || wp->w_p_pvw) || wp == curwin) && !is_aucmd_win(wp)) {
count++;
}
}
@@ -6963,21 +7207,17 @@ void reset_lnums(void)
}
}
-/*
- * A snapshot of the window sizes, to restore them after closing the help
- * window.
- * Only these fields are used:
- * fr_layout
- * fr_width
- * fr_height
- * fr_next
- * fr_child
- * fr_win (only valid for the old curwin, NULL otherwise)
- */
+// A snapshot of the window sizes, to restore them after closing the help
+// window.
+// Only these fields are used:
+// fr_layout
+// fr_width
+// fr_height
+// fr_next
+// fr_child
+// fr_win (only valid for the old curwin, NULL otherwise)
-/*
- * Create a snapshot of the current frame sizes.
- */
+// Create a snapshot of the current frame sizes.
void make_snapshot(int idx)
{
clear_snapshot(curtab, idx);
@@ -7001,9 +7241,7 @@ static void make_snapshot_rec(frame_T *fr, frame_T **frp)
}
}
-/*
- * Remove any existing snapshot.
- */
+// Remove any existing snapshot.
static void clear_snapshot(tabpage_T *tp, int idx)
{
clear_snapshot_rec(tp->tp_snapshot[idx]);
@@ -7055,18 +7293,16 @@ static win_T *get_snapshot_curwin(int idx)
/// @param close_curwin closing current window
void restore_snapshot(int idx, int close_curwin)
{
- win_T *wp;
-
if (curtab->tp_snapshot[idx] != NULL
&& curtab->tp_snapshot[idx]->fr_width == topframe->fr_width
&& curtab->tp_snapshot[idx]->fr_height == topframe->fr_height
&& check_snapshot_rec(curtab->tp_snapshot[idx], topframe) == OK) {
- wp = restore_snapshot_rec(curtab->tp_snapshot[idx], topframe);
+ win_T *wp = restore_snapshot_rec(curtab->tp_snapshot[idx], topframe);
(void)win_comp_pos();
if (wp != NULL && close_curwin) {
win_goto(wp);
}
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
}
clear_snapshot(curtab, idx);
}
@@ -7088,15 +7324,12 @@ static int check_snapshot_rec(frame_T *sn, frame_T *fr)
return OK;
}
-/*
- * Copy the size of snapshot frame "sn" to frame "fr". Do the same for all
- * following frames and children.
- * Returns a pointer to the old current window, or NULL.
- */
+// Copy the size of snapshot frame "sn" to frame "fr". Do the same for all
+// following frames and children.
+// Returns a pointer to the old current window, or NULL.
static win_T *restore_snapshot_rec(frame_T *sn, frame_T *fr)
{
win_T *wp = NULL;
- win_T *wp2;
fr->fr_height = sn->fr_height;
fr->fr_width = sn->fr_width;
@@ -7106,13 +7339,13 @@ static win_T *restore_snapshot_rec(frame_T *sn, frame_T *fr)
wp = sn->fr_win;
}
if (sn->fr_next != NULL) {
- wp2 = restore_snapshot_rec(sn->fr_next, fr->fr_next);
+ win_T *wp2 = restore_snapshot_rec(sn->fr_next, fr->fr_next);
if (wp2 != NULL) {
wp = wp2;
}
}
if (sn->fr_child != NULL) {
- wp2 = restore_snapshot_rec(sn->fr_child, fr->fr_child);
+ win_T *wp2 = restore_snapshot_rec(sn->fr_child, fr->fr_child);
if (wp2 != NULL) {
wp = wp2;
}
@@ -7120,114 +7353,6 @@ static win_T *restore_snapshot_rec(frame_T *sn, frame_T *fr)
return wp;
}
-/// 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.
-///
-/// @param no_display if true the display won't be affected, no redraw is
-/// triggered, another tabpage access is limited.
-///
-/// @return FAIL if switching to "win" failed.
-int switch_win(switchwin_T *switchwin, win_T *win, tabpage_T *tp, bool no_display)
-{
- block_autocmds();
- return switch_win_noblock(switchwin, win, tp, no_display);
-}
-
-// As switch_win() but without blocking autocommands.
-int switch_win_noblock(switchwin_T *switchwin, win_T *win, tabpage_T *tp, bool no_display)
-{
- CLEAR_POINTER(switchwin);
- 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) {
- switchwin->sw_curtab = curtab;
- if (no_display) {
- curtab->tp_firstwin = firstwin;
- curtab->tp_lastwin = lastwin;
- curtab = tp;
- firstwin = curtab->tp_firstwin;
- lastwin = curtab->tp_lastwin;
- } else {
- goto_tabpage_tp(tp, false, false);
- }
- }
- if (!win_valid(win)) {
- return FAIL;
- }
- curwin = win;
- curbuf = curwin->w_buffer;
- return OK;
-}
-
-// 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(switchwin_T *switchwin, bool no_display)
-{
- restore_win_noblock(switchwin, no_display);
- unblock_autocmds();
-}
-
-// As restore_win() but without unblocking autocommands.
-void restore_win_noblock(switchwin_T *switchwin, bool no_display)
-{
- if (switchwin->sw_curtab != NULL && valid_tabpage(switchwin->sw_curtab)) {
- if (no_display) {
- curtab->tp_firstwin = firstwin;
- curtab->tp_lastwin = lastwin;
- curtab = switchwin->sw_curtab;
- firstwin = curtab->tp_firstwin;
- lastwin = curtab->tp_lastwin;
- } else {
- goto_tabpage_tp(switchwin->sw_curtab, false, false);
- }
- }
-
- 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;
- }
-}
-
-/// Make "buf" the current buffer.
-///
-/// restore_buffer() MUST be called to undo.
-/// No autocommands will be executed. Use aucmd_prepbuf() if there are any.
-void switch_buffer(bufref_T *save_curbuf, buf_T *buf)
-{
- block_autocmds();
- set_bufref(save_curbuf, curbuf);
- curbuf->b_nwindows--;
- curbuf = buf;
- curwin->w_buffer = buf;
- curbuf->b_nwindows++;
-}
-
-/// Restore the current buffer after using switch_buffer().
-void restore_buffer(bufref_T *save_curbuf)
-{
- unblock_autocmds();
- // Check for valid buffer, just in case.
- if (bufref_valid(save_curbuf)) {
- curbuf->b_nwindows--;
- curwin->w_buffer = save_curbuf->br_buf;
- curbuf = save_curbuf->br_buf;
- curbuf->b_nwindows++;
- }
-}
-
/// Check that "topfrp" and its children are at the right height.
///
/// @param topfrp top frame pointer
@@ -7281,17 +7406,14 @@ static int int_cmp(const void *a, const void *b)
/// @return error message, NULL if it's OK.
char *check_colorcolumn(win_T *wp)
{
- char *s;
- int col;
- unsigned int count = 0;
- int color_cols[256];
- int j = 0;
-
if (wp->w_buffer == NULL) {
return NULL; // buffer was closed
}
- for (s = (char *)wp->w_p_cc; *s != NUL && count < 255;) {
+ unsigned int count = 0;
+ int color_cols[256];
+ for (char *s = wp->w_p_cc; *s != NUL && count < 255;) {
+ int col;
if (*s == '-' || *s == '+') {
// -N and +N: add to 'textwidth'
col = (*s == '-') ? -1 : 1;
@@ -7340,6 +7462,7 @@ skip:
// win_line()
qsort(color_cols, count, sizeof(int), int_cmp);
+ int j = 0;
for (unsigned int i = 0; i < count; i++) {
// skip duplicates
if (j == 0 || wp->w_p_cc_cols[j - 1] != color_cols[i]) {
@@ -7352,56 +7475,6 @@ skip:
return NULL; // no error
}
-int win_getid(typval_T *argvars)
-{
- if (argvars[0].v_type == VAR_UNKNOWN) {
- return curwin->handle;
- }
- int winnr = (int)tv_get_number(&argvars[0]);
- win_T *wp;
- if (winnr > 0) {
- if (argvars[1].v_type == VAR_UNKNOWN) {
- wp = firstwin;
- } else {
- tabpage_T *tp = NULL;
- int tabnr = (int)tv_get_number(&argvars[1]);
- FOR_ALL_TABS(tp2) {
- if (--tabnr == 0) {
- tp = tp2;
- break;
- }
- }
- if (tp == NULL) {
- return -1;
- }
- if (tp == curtab) {
- wp = firstwin;
- } else {
- wp = tp->tp_firstwin;
- }
- }
- for (; wp != NULL; wp = wp->w_next) {
- if (--winnr == 0) {
- return wp->handle;
- }
- }
- }
- return 0;
-}
-
-int win_gotoid(typval_T *argvars)
-{
- int id = (int)tv_get_number(&argvars[0]);
-
- FOR_ALL_TAB_WINDOWS(tp, wp) {
- if (wp->handle == id) {
- goto_tabpage_win(tp, wp);
- return 1;
- }
- }
- return 0;
-}
-
void win_get_tabwin(handle_T id, int *tabnr, int *winnr)
{
*tabnr = 0;
@@ -7422,109 +7495,16 @@ void win_get_tabwin(handle_T id, int *tabnr, int *winnr)
}
}
-void win_id2tabwin(typval_T *const argvars, typval_T *const rettv)
-{
- int winnr = 1;
- int tabnr = 1;
- handle_T id = (handle_T)tv_get_number(&argvars[0]);
-
- win_get_tabwin(id, &tabnr, &winnr);
-
- list_T *const list = tv_list_alloc_ret(rettv, 2);
- tv_list_append_number(list, tabnr);
- tv_list_append_number(list, winnr);
-}
-
-win_T *win_id2wp(int id)
-{
- return win_id2wp_tp(id, NULL);
-}
-
-// Return the window and tab pointer of window "id".
-win_T *win_id2wp_tp(int id, tabpage_T **tpp)
-{
- FOR_ALL_TAB_WINDOWS(tp, wp) {
- if (wp->handle == id) {
- if (tpp != NULL) {
- *tpp = tp;
- }
- return wp;
- }
- }
-
- return NULL;
-}
-
-int win_id2win(typval_T *argvars)
-{
- int nr = 1;
- int id = (int)tv_get_number(&argvars[0]);
-
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->handle == id) {
- return nr;
- }
- nr++;
- }
- return 0;
-}
-
-void win_findbuf(typval_T *argvars, list_T *list)
-{
- int bufnr = (int)tv_get_number(&argvars[0]);
-
- FOR_ALL_TAB_WINDOWS(tp, wp) {
- if (wp->w_buffer->b_fnum == bufnr) {
- tv_list_append_number(list, wp->handle);
- }
- }
-}
-
-// Get the layout of the given tab page for winlayout().
-void get_framelayout(const frame_T *fr, list_T *l, bool outer)
-{
- list_T *fr_list;
-
- if (fr == NULL) {
- return;
- }
-
- if (outer) {
- // outermost call from f_winlayout()
- fr_list = l;
- } else {
- fr_list = tv_list_alloc(2);
- tv_list_append_list(l, fr_list);
- }
-
- if (fr->fr_layout == FR_LEAF) {
- if (fr->fr_win != NULL) {
- tv_list_append_string(fr_list, "leaf", -1);
- tv_list_append_number(fr_list, fr->fr_win->handle);
- }
- } else {
- tv_list_append_string(fr_list, fr->fr_layout == FR_ROW ? "row" : "col", -1);
-
- list_T *const win_list = tv_list_alloc(kListLenUnknown);
- tv_list_append_list(fr_list, win_list);
- const frame_T *child = fr->fr_child;
- while (child != NULL) {
- get_framelayout(child, win_list, false);
- child = child->fr_next;
- }
- }
-}
-
-void win_ui_flush(void)
+void win_ui_flush(bool validate)
{
FOR_ALL_TAB_WINDOWS(tp, wp) {
if (wp->w_pos_changed && wp->w_grid_alloc.chars != NULL) {
if (tp == curtab) {
- ui_ext_win_position(wp);
+ ui_ext_win_position(wp, validate);
} else {
ui_call_win_hide(wp->w_grid_alloc.handle);
+ wp->w_pos_changed = false;
}
- wp->w_pos_changed = false;
}
if (tp == curtab) {
ui_ext_win_viewport(wp);
diff --git a/src/nvim/window.h b/src/nvim/window.h
index b75b8abd9b..4ab2bea60a 100644
--- a/src/nvim/window.h
+++ b/src/nvim/window.h
@@ -2,10 +2,15 @@
#define NVIM_WINDOW_H
#include <stdbool.h>
+#include <stddef.h>
+#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
+#include "nvim/macros.h"
#include "nvim/mark.h"
#include "nvim/os/os.h"
+#include "nvim/os/os_defs.h"
+#include "nvim/vim.h"
// Values for file_name_in_line()
#define FNAME_MESS 1 // give error message
@@ -17,75 +22,22 @@
#define FNAME_UNESC 32 // remove backslashes used for escaping
// 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
-#define WSP_BOT 8 // window at bottom-right of shell
-#define WSP_HELP 16 // creating the help window
-#define WSP_BELOW 32 // put new window below/right
-#define WSP_ABOVE 64 // put new window above/left
-#define WSP_NEWLOC 128 // don't copy location list
+#define WSP_ROOM 0x01 // require enough room
+#define WSP_VERT 0x02 // split/equalize vertically
+#define WSP_HOR 0x04 // equalize horizontally
+#define WSP_TOP 0x08 // window at top-left of shell
+#define WSP_BOT 0x10 // window at bottom-right of shell
+#define WSP_HELP 0x20 // creating the help window
+#define WSP_BELOW 0x40 // put new window below/right
+#define WSP_ABOVE 0x80 // put new window above/left
+#define WSP_NEWLOC 0x100 // don't copy location list
// 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)
-
+// Set to true if 'cmdheight' was explicitly set to 0.
+EXTERN bool p_ch_was_zero INIT(= false);
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "window.h.generated.h"
#endif
diff --git a/src/uncrustify.cfg b/src/uncrustify.cfg
index 2b9396e635..f9e6617d40 100644
--- a/src/uncrustify.cfg
+++ b/src/uncrustify.cfg
@@ -125,7 +125,7 @@ sp_before_assign = ignore # ignore/add/remove/force/not_defined
# Add or remove space after assignment operator '=', '+=', etc.
#
# Overrides sp_assign.
-sp_after_assign = ignore # ignore/add/remove/force/not_defined
+sp_after_assign = force # ignore/add/remove/force/not_defined
# Add or remove space in 'enum {'.
#
@@ -1642,7 +1642,7 @@ nl_end_of_file = force # ignore/add/remove/force/not_defined
nl_end_of_file_min = 1 # unsigned number
# Add or remove newline between '=' and '{'.
-nl_assign_brace = ignore # ignore/add/remove/force/not_defined
+nl_assign_brace = remove # ignore/add/remove/force/not_defined
# (D) Add or remove newline between '=' and '['.
nl_assign_square = ignore # ignore/add/remove/force/not_defined
@@ -1714,7 +1714,7 @@ nl_try_brace = ignore # ignore/add/remove/force/not_defined
nl_getset_brace = ignore # ignore/add/remove/force/not_defined
# Add or remove newline between 'for' and '{'.
-nl_for_brace = ignore # ignore/add/remove/force/not_defined
+nl_for_brace = remove # ignore/add/remove/force/not_defined
# Add or remove newline before the '{' of a 'catch' statement, as in
# 'catch (decl) <here> {'.
@@ -1738,7 +1738,7 @@ nl_brace_square = 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
+nl_while_brace = remove # ignore/add/remove/force/not_defined
# (D) Add or remove newline between 'scope (x)' and '{'.
nl_scope_brace = ignore # ignore/add/remove/force/not_defined
@@ -1763,7 +1763,7 @@ nl_do_brace = remove # 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
+nl_switch_brace = remove # ignore/add/remove/force/not_defined
# Add or remove newline between 'synchronized' and '{'.
nl_synchronized_brace = ignore # ignore/add/remove/force/not_defined
@@ -1777,7 +1777,7 @@ nl_multi_line_cond = false # true/false
# Add a newline after '(' if an if/for/while/switch condition spans multiple
# lines
-nl_multi_line_sparen_open = ignore # ignore/add/remove/force/not_defined
+nl_multi_line_sparen_open = remove # ignore/add/remove/force/not_defined
# Add a newline before ')' if an if/for/while/switch condition spans multiple
# lines. Overrides nl_before_if_closing_paren if both are specified.
@@ -1908,7 +1908,7 @@ nl_func_paren_empty = ignore # ignore/add/remove/force/not_defined
# Add or remove newline between a function name and the opening '(' in the
# definition.
-nl_func_def_paren = ignore # ignore/add/remove/force/not_defined
+nl_func_def_paren = remove # ignore/add/remove/force/not_defined
# Overrides nl_func_def_paren for functions with no parameters.
nl_func_def_paren_empty = ignore # ignore/add/remove/force/not_defined
@@ -1941,7 +1941,7 @@ nl_func_decl_start_multi_line = false # true/false
nl_func_def_start_multi_line = false # true/false
# Add or remove newline after each ',' in a function declaration.
-nl_func_decl_args = ignore # ignore/add/remove/force/not_defined
+nl_func_decl_args = remove # ignore/add/remove/force/not_defined
# Add or remove newline after each ',' in a function definition.
nl_func_def_args = remove # ignore/add/remove/force/not_defined
@@ -1958,7 +1958,7 @@ nl_func_decl_args_multi_line = false # true/false
nl_func_def_args_multi_line = false # true/false
# Add or remove newline before the ')' in a function declaration.
-nl_func_decl_end = ignore # ignore/add/remove/force/not_defined
+nl_func_decl_end = remove # ignore/add/remove/force/not_defined
# Add or remove newline before the ')' in a function definition.
nl_func_def_end = remove # ignore/add/remove/force/not_defined
@@ -1991,7 +1991,7 @@ nl_func_call_empty = ignore # ignore/add/remove/force/not_defined
nl_func_call_start = remove # ignore/add/remove/force/not_defined
# Whether to add a newline before ')' in a function call.
-nl_func_call_end = ignore # ignore/add/remove/force/not_defined
+nl_func_call_end = remove # ignore/add/remove/force/not_defined
# Whether to add a newline after '(' in a function call if '(' and ')' are in
# different lines.
@@ -3515,5 +3515,5 @@ set QUESTION REAL_FATTR_CONST
set QUESTION REAL_FATTR_NONNULL_ALL
set QUESTION REAL_FATTR_PURE
set QUESTION REAL_FATTR_WARN_UNUSED_RESULT
-# option(s) with 'not default' value: 102
+# option(s) with 'not default' value: 112
#
diff --git a/src/unicode/CaseFolding.txt b/src/unicode/CaseFolding.txt
index 932ace29e6..65aa0fcd6b 100644
--- a/src/unicode/CaseFolding.txt
+++ b/src/unicode/CaseFolding.txt
@@ -1,11 +1,11 @@
-# CaseFolding-14.0.0.txt
-# Date: 2021-03-08, 19:35:41 GMT
-# © 2021 Unicode®, Inc.
+# CaseFolding-15.0.0.txt
+# Date: 2022-02-02, 23:35:35 GMT
+# © 2022 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/
#
# Case Folding Properties
#
diff --git a/src/unicode/Copyright.txt b/src/unicode/Copyright.txt
index bfae4154b6..9d281d674a 100644
--- a/src/unicode/Copyright.txt
+++ b/src/unicode/Copyright.txt
@@ -2,7 +2,7 @@ COPYRIGHT AND PERMISSION NOTICE
Copyright © 1991-2015 Unicode, Inc. All rights reserved.
Distributed under the Terms of Use in
-http://www.unicode.org/copyright.html.
+https://www.unicode.org/copyright.html.
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Unicode data files and any associated documentation
@@ -34,4 +34,4 @@ PERFORMANCE OF THE DATA FILES OR SOFTWARE.
Except as contained in this notice, the name of a copyright holder
shall not be used in advertising or otherwise to promote the sale,
use or other dealings in these Data Files or Software without prior
-written authorization of the copyright holder. \ No newline at end of file
+written authorization of the copyright holder.
diff --git a/src/unicode/EastAsianWidth.txt b/src/unicode/EastAsianWidth.txt
index e04f705178..38b7076c02 100644
--- a/src/unicode/EastAsianWidth.txt
+++ b/src/unicode/EastAsianWidth.txt
@@ -1,6 +1,6 @@
-# EastAsianWidth-14.0.0.txt
-# Date: 2021-07-06, 09:58:53 GMT [KW, LI]
-# © 2021 Unicode®, Inc.
+# EastAsianWidth-15.0.0.txt
+# Date: 2022-05-24, 17:40:20 GMT [KW, LI]
+# © 2022 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 https://www.unicode.org/terms_of_use.html
#
@@ -534,6 +534,7 @@
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
0CF1..0CF2;N # Lo [2] KANNADA SIGN JIHVAMULIYA..KANNADA SIGN UPADHMANIYA
+0CF3;N # Mc KANNADA SIGN COMBINING ANUSVARA ABOVE RIGHT
0D00..0D01;N # Mn [2] MALAYALAM SIGN COMBINING ANUSVARA ABOVE..MALAYALAM SIGN CANDRABINDU
0D02..0D03;N # Mc [2] MALAYALAM SIGN ANUSVARA..MALAYALAM SIGN VISARGA
0D04..0D0C;N # Lo [9] MALAYALAM LETTER VEDIC ANUSVARA..MALAYALAM LETTER VOCALIC L
@@ -595,7 +596,7 @@
0EBD;N # Lo LAO SEMIVOWEL SIGN NYO
0EC0..0EC4;N # Lo [5] LAO VOWEL SIGN E..LAO VOWEL SIGN AI
0EC6;N # Lm LAO KO LA
-0EC8..0ECD;N # Mn [6] LAO TONE MAI EK..LAO NIGGAHITA
+0EC8..0ECE;N # Mn [7] LAO TONE MAI EK..LAO YAMAKKAN
0ED0..0ED9;N # Nd [10] LAO DIGIT ZERO..LAO DIGIT NINE
0EDC..0EDF;N # Lo [4] LAO HO NO..LAO LETTER KHMU NYO
0F00;N # Lo TIBETAN SYLLABLE OM
@@ -1946,6 +1947,7 @@ FFFD;A # So REPLACEMENT CHARACTER
10EAB..10EAC;N # Mn [2] YEZIDI COMBINING HAMZA MARK..YEZIDI COMBINING MADDA MARK
10EAD;N # Pd YEZIDI HYPHENATION MARK
10EB0..10EB1;N # Lo [2] YEZIDI LETTER LAM WITH DOT ABOVE..YEZIDI LETTER YOT WITH CIRCUMFLEX ABOVE
+10EFD..10EFF;N # Mn [3] ARABIC SMALL LOW WORD SAKTA..ARABIC SMALL LOW WORD MADDA
10F00..10F1C;N # Lo [29] OLD SOGDIAN LETTER ALEPH..OLD SOGDIAN LETTER FINAL TAW WITH VERTICAL TAIL
10F1D..10F26;N # No [10] OLD SOGDIAN NUMBER ONE..OLD SOGDIAN FRACTION ONE HALF
10F27;N # Lo OLD SOGDIAN LIGATURE AYIN-DALETH
@@ -2028,6 +2030,8 @@ FFFD;A # So REPLACEMENT CHARACTER
11236..11237;N # Mn [2] KHOJKI SIGN NUKTA..KHOJKI SIGN SHADDA
11238..1123D;N # Po [6] KHOJKI DANDA..KHOJKI ABBREVIATION SIGN
1123E;N # Mn KHOJKI SIGN SUKUN
+1123F..11240;N # Lo [2] KHOJKI LETTER QA..KHOJKI LETTER SHORT I
+11241;N # Mn KHOJKI VOWEL SIGN VOCALIC R
11280..11286;N # Lo [7] MULTANI LETTER A..MULTANI LETTER GA
11288;N # Lo MULTANI LETTER GHA
1128A..1128D;N # Lo [4] MULTANI LETTER CA..MULTANI LETTER JJA
@@ -2190,6 +2194,7 @@ FFFD;A # So REPLACEMENT CHARACTER
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
+11B00..11B09;N # Po [10] DEVANAGARI HEAD MARK..DEVANAGARI SIGN MINDU
11C00..11C08;N # Lo [9] BHAIKSUKI LETTER A..BHAIKSUKI LETTER VOCALIC L
11C0A..11C2E;N # Lo [37] BHAIKSUKI LETTER E..BHAIKSUKI LETTER HA
11C2F;N # Mc BHAIKSUKI VOWEL SIGN AA
@@ -2235,6 +2240,19 @@ FFFD;A # So REPLACEMENT CHARACTER
11EF3..11EF4;N # Mn [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U
11EF5..11EF6;N # Mc [2] MAKASAR VOWEL SIGN E..MAKASAR VOWEL SIGN O
11EF7..11EF8;N # Po [2] MAKASAR PASSIMBANG..MAKASAR END OF SECTION
+11F00..11F01;N # Mn [2] KAWI SIGN CANDRABINDU..KAWI SIGN ANUSVARA
+11F02;N # Lo KAWI SIGN REPHA
+11F03;N # Mc KAWI SIGN VISARGA
+11F04..11F10;N # Lo [13] KAWI LETTER A..KAWI LETTER O
+11F12..11F33;N # Lo [34] KAWI LETTER KA..KAWI LETTER JNYA
+11F34..11F35;N # Mc [2] KAWI VOWEL SIGN AA..KAWI VOWEL SIGN ALTERNATE AA
+11F36..11F3A;N # Mn [5] KAWI VOWEL SIGN I..KAWI VOWEL SIGN VOCALIC R
+11F3E..11F3F;N # Mc [2] KAWI VOWEL SIGN E..KAWI VOWEL SIGN AI
+11F40;N # Mn KAWI VOWEL SIGN EU
+11F41;N # Mc KAWI SIGN KILLER
+11F42;N # Mn KAWI CONJOINER
+11F43..11F4F;N # Po [13] KAWI DANDA..KAWI PUNCTUATION CLOSING SPIRAL
+11F50..11F59;N # Nd [10] KAWI DIGIT ZERO..KAWI DIGIT NINE
11FB0;N # Lo LISU LETTER YHA
11FC0..11FD4;N # No [21] TAMIL FRACTION ONE THREE-HUNDRED-AND-TWENTIETH..TAMIL FRACTION DOWNSCALING FACTOR KIIZH
11FD5..11FDC;N # So [8] TAMIL SIGN NEL..TAMIL SIGN MUKKURUNI
@@ -2247,8 +2265,11 @@ FFFD;A # So REPLACEMENT CHARACTER
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
+13000..1342F;N # Lo [1072] EGYPTIAN HIEROGLYPH A001..EGYPTIAN HIEROGLYPH V011D
+13430..1343F;N # Cf [16] EGYPTIAN HIEROGLYPH VERTICAL JOINER..EGYPTIAN HIEROGLYPH END WALLED ENCLOSURE
+13440;N # Mn EGYPTIAN HIEROGLYPH MIRROR HORIZONTALLY
+13441..13446;N # Lo [6] EGYPTIAN HIEROGLYPH FULL BLANK..EGYPTIAN HIEROGLYPH WIDE LOST SIGN
+13447..13455;N # Mn [15] EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT TOP START..EGYPTIAN HIEROGLYPH MODIFIER DAMAGED
14400..14646;N # Lo [583] ANATOLIAN HIEROGLYPH A001..ANATOLIAN HIEROGLYPH A530
16800..16A38;N # Lo [569] BAMUM LETTER PHASE-A NGKUE MFON..BAMUM LETTER PHASE-F VUEQ
16A40..16A5E;N # Lo [31] MRO LETTER TA..MRO LETTER TEK
@@ -2293,7 +2314,9 @@ FFFD;A # So REPLACEMENT CHARACTER
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..1B122;W # Lo [35] HENTAIGANA LETTER RE-3..KATAKANA LETTER ARCHAIC WU
+1B132;W # Lo HIRAGANA LETTER SMALL KO
1B150..1B152;W # Lo [3] HIRAGANA LETTER SMALL WI..HIRAGANA LETTER SMALL WO
+1B155;W # Lo KATAKANA LETTER SMALL KO
1B164..1B167;W # Lo [4] KATAKANA LETTER SMALL WI..KATAKANA LETTER SMALL N
1B170..1B2FB;W # Lo [396] NUSHU CHARACTER-1B170..NUSHU CHARACTER-1B2FB
1BC00..1BC6A;N # Lo [107] DUPLOYAN LETTER H..DUPLOYAN LETTER VOCALIC M
@@ -2324,6 +2347,7 @@ FFFD;A # So REPLACEMENT CHARACTER
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
+1D2C0..1D2D3;N # No [20] KAKTOVIK NUMERAL ZERO..KAKTOVIK NUMERAL NINETEEN
1D2E0..1D2F3;N # No [20] MAYAN NUMERAL ZERO..MAYAN NUMERAL NINETEEN
1D300..1D356;N # So [87] MONOGRAM FOR EARTH..TETRAGRAM FOR FOSTERING
1D360..1D378;N # No [25] COUNTING ROD UNIT DIGIT ONE..TALLY MARK FIVE
@@ -2383,11 +2407,14 @@ FFFD;A # So REPLACEMENT CHARACTER
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
+1DF25..1DF2A;N # Ll [6] LATIN SMALL LETTER D WITH MID-HEIGHT LEFT HOOK..LATIN SMALL LETTER T WITH MID-HEIGHT LEFT HOOK
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
1E023..1E024;N # Mn [2] COMBINING GLAGOLITIC LETTER YU..COMBINING GLAGOLITIC LETTER SMALL YUS
1E026..1E02A;N # Mn [5] COMBINING GLAGOLITIC LETTER YO..COMBINING GLAGOLITIC LETTER FITA
+1E030..1E06D;N # Lm [62] MODIFIER LETTER CYRILLIC SMALL A..MODIFIER LETTER CYRILLIC SMALL STRAIGHT U WITH STROKE
+1E08F;N # Mn COMBINING CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
1E100..1E12C;N # Lo [45] NYIAKENG PUACHUE HMONG LETTER MA..NYIAKENG PUACHUE HMONG LETTER W
1E130..1E136;N # Mn [7] NYIAKENG PUACHUE HMONG TONE-B..NYIAKENG PUACHUE HMONG TONE-D
1E137..1E13D;N # Lm [7] NYIAKENG PUACHUE HMONG SIGN FOR PERSON..NYIAKENG PUACHUE HMONG SYLLABLE LENGTHENER
@@ -2400,6 +2427,10 @@ FFFD;A # So REPLACEMENT CHARACTER
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
+1E4D0..1E4EA;N # Lo [27] NAG MUNDARI LETTER O..NAG MUNDARI LETTER ELL
+1E4EB;N # Lm NAG MUNDARI SIGN OJOD
+1E4EC..1E4EF;N # Mn [4] NAG MUNDARI SIGN MUHOR..NAG MUNDARI SIGN SUTUH
+1E4F0..1E4F9;N # Nd [10] NAG MUNDARI DIGIT ZERO..NAG MUNDARI DIGIT NINE
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
@@ -2528,13 +2559,14 @@ 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
+1F6DC..1F6DF;W # So [4] WIRELESS..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
1F6F4..1F6FC;W # So [9] SCOOTER..ROLLER SKATE
-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
+1F700..1F776;N # So [119] ALCHEMICAL SYMBOL FOR QUINTESSENCE..LUNAR ECLIPSE
+1F77B..1F77F;N # So [5] HAUMEA..ORCUS
+1F780..1F7D9;N # So [90] BLACK LEFT-POINTING ISOSCELES RIGHT TRIANGLE..NINE POINTED WHITE STAR
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
@@ -2551,22 +2583,20 @@ FFFD;A # So REPLACEMENT CHARACTER
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..1FA7C;W # So [5] DROP OF BLOOD..CRUTCH
-1FA80..1FA86;W # So [7] YO-YO..NESTING DOLLS
-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
+1FA70..1FA7C;W # So [13] BALLET SHOES..CRUTCH
+1FA80..1FA88;W # So [9] YO-YO..FLUTE
+1FA90..1FABD;W # So [46] RINGED PLANET..WING
+1FABF..1FAC5;W # So [7] GOOSE..PERSON WITH CROWN
+1FACE..1FADB;W # So [14] MOOSE..PEA POD
+1FAE0..1FAE8;W # So [9] MELTING FACE..SHAKING FACE
+1FAF0..1FAF8;W # So [9] HAND WITH INDEX FINGER AND THUMB CROSSED..RIGHTWARDS PUSHING HAND
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..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>
+2A700..2B739;W # Lo [4154] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B739
+2B73A..2B73F;W # Cn [6] <reserved-2B73A>..<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
@@ -2577,7 +2607,9 @@ FFFD;A # So REPLACEMENT CHARACTER
2FA1E..2FA1F;W # Cn [2] <reserved-2FA1E>..<reserved-2FA1F>
2FA20..2FFFD;W # Cn [1502] <reserved-2FA20>..<reserved-2FFFD>
30000..3134A;W # Lo [4939] CJK UNIFIED IDEOGRAPH-30000..CJK UNIFIED IDEOGRAPH-3134A
-3134B..3FFFD;W # Cn [60595] <reserved-3134B>..<reserved-3FFFD>
+3134B..3134F;W # Cn [5] <reserved-3134B>..<reserved-3134F>
+31350..323AF;W # Lo [4192] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-323AF
+323B0..3FFFD;W # Cn [56398] <reserved-323B0>..<reserved-3FFFD>
E0001;N # Cf LANGUAGE TAG
E0020..E007F;N # Cf [96] TAG SPACE..CANCEL TAG
E0100..E01EF;A # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
diff --git a/src/unicode/UnicodeData.txt b/src/unicode/UnicodeData.txt
index b5abef7ed4..ea963a7162 100644
--- a/src/unicode/UnicodeData.txt
+++ b/src/unicode/UnicodeData.txt
@@ -2975,6 +2975,7 @@
0CEF;KANNADA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
0CF1;KANNADA SIGN JIHVAMULIYA;Lo;0;L;;;;;N;;;;;
0CF2;KANNADA SIGN UPADHMANIYA;Lo;0;L;;;;;N;;;;;
+0CF3;KANNADA SIGN COMBINING ANUSVARA ABOVE RIGHT;Mc;0;L;;;;;N;;;;;
0D00;MALAYALAM SIGN COMBINING ANUSVARA ABOVE;Mn;0;NSM;;;;;N;;;;;
0D01;MALAYALAM SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
0D02;MALAYALAM SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
@@ -3339,6 +3340,7 @@
0ECB;LAO TONE MAI CATAWA;Mn;122;NSM;;;;;N;;;;;
0ECC;LAO CANCELLATION MARK;Mn;0;NSM;;;;;N;;;;;
0ECD;LAO NIGGAHITA;Mn;0;NSM;;;;;N;;;;;
+0ECE;LAO YAMAKKAN;Mn;0;NSM;;;;;N;;;;;
0ED0;LAO DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
0ED1;LAO DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
0ED2;LAO DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
@@ -19393,6 +19395,9 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
10EAD;YEZIDI HYPHENATION MARK;Pd;0;R;;;;;N;;;;;
10EB0;YEZIDI LETTER LAM WITH DOT ABOVE;Lo;0;R;;;;;N;;;;;
10EB1;YEZIDI LETTER YOT WITH CIRCUMFLEX ABOVE;Lo;0;R;;;;;N;;;;;
+10EFD;ARABIC SMALL LOW WORD SAKTA;Mn;220;NSM;;;;;N;;;;;
+10EFE;ARABIC SMALL LOW WORD QASR;Mn;220;NSM;;;;;N;;;;;
+10EFF;ARABIC SMALL LOW WORD MADDA;Mn;220;NSM;;;;;N;;;;;
10F00;OLD SOGDIAN LETTER ALEPH;Lo;0;R;;;;;N;;;;;
10F01;OLD SOGDIAN LETTER FINAL ALEPH;Lo;0;R;;;;;N;;;;;
10F02;OLD SOGDIAN LETTER BETH;Lo;0;R;;;;;N;;;;;
@@ -20058,6 +20063,9 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1123C;KHOJKI DOUBLE SECTION MARK;Po;0;L;;;;;N;;;;;
1123D;KHOJKI ABBREVIATION SIGN;Po;0;L;;;;;N;;;;;
1123E;KHOJKI SIGN SUKUN;Mn;0;NSM;;;;;N;;;;;
+1123F;KHOJKI LETTER QA;Lo;0;L;;;;;N;;;;;
+11240;KHOJKI LETTER SHORT I;Lo;0;L;;;;;N;;;;;
+11241;KHOJKI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
11280;MULTANI LETTER A;Lo;0;L;;;;;N;;;;;
11281;MULTANI LETTER I;Lo;0;L;;;;;N;;;;;
11282;MULTANI LETTER U;Lo;0;L;;;;;N;;;;;
@@ -21256,6 +21264,16 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
11AF6;PAU CIN HAU LOW-FALLING TONE LONG FINAL;Lo;0;L;;;;;N;;;;;
11AF7;PAU CIN HAU LOW-FALLING TONE FINAL;Lo;0;L;;;;;N;;;;;
11AF8;PAU CIN HAU GLOTTAL STOP FINAL;Lo;0;L;;;;;N;;;;;
+11B00;DEVANAGARI HEAD MARK;Po;0;L;;;;;N;;;;;
+11B01;DEVANAGARI HEAD MARK WITH HEADSTROKE;Po;0;L;;;;;N;;;;;
+11B02;DEVANAGARI SIGN BHALE;Po;0;L;;;;;N;;;;;
+11B03;DEVANAGARI SIGN BHALE WITH HOOK;Po;0;L;;;;;N;;;;;
+11B04;DEVANAGARI SIGN EXTENDED BHALE;Po;0;L;;;;;N;;;;;
+11B05;DEVANAGARI SIGN EXTENDED BHALE WITH HOOK;Po;0;L;;;;;N;;;;;
+11B06;DEVANAGARI SIGN WESTERN FIVE-LIKE BHALE;Po;0;L;;;;;N;;;;;
+11B07;DEVANAGARI SIGN WESTERN NINE-LIKE BHALE;Po;0;L;;;;;N;;;;;
+11B08;DEVANAGARI SIGN REVERSED NINE-LIKE BHALE;Po;0;L;;;;;N;;;;;
+11B09;DEVANAGARI SIGN MINDU;Po;0;L;;;;;N;;;;;
11C00;BHAIKSUKI LETTER A;Lo;0;L;;;;;N;;;;;
11C01;BHAIKSUKI LETTER AA;Lo;0;L;;;;;N;;;;;
11C02;BHAIKSUKI LETTER I;Lo;0;L;;;;;N;;;;;
@@ -21584,6 +21602,92 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
11EF6;MAKASAR VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
11EF7;MAKASAR PASSIMBANG;Po;0;L;;;;;N;;;;;
11EF8;MAKASAR END OF SECTION;Po;0;L;;;;;N;;;;;
+11F00;KAWI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+11F01;KAWI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+11F02;KAWI SIGN REPHA;Lo;0;L;;;;;N;;;;;
+11F03;KAWI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+11F04;KAWI LETTER A;Lo;0;L;;;;;N;;;;;
+11F05;KAWI LETTER AA;Lo;0;L;;;;;N;;;;;
+11F06;KAWI LETTER I;Lo;0;L;;;;;N;;;;;
+11F07;KAWI LETTER II;Lo;0;L;;;;;N;;;;;
+11F08;KAWI LETTER U;Lo;0;L;;;;;N;;;;;
+11F09;KAWI LETTER UU;Lo;0;L;;;;;N;;;;;
+11F0A;KAWI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+11F0B;KAWI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+11F0C;KAWI LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+11F0D;KAWI LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+11F0E;KAWI LETTER E;Lo;0;L;;;;;N;;;;;
+11F0F;KAWI LETTER AI;Lo;0;L;;;;;N;;;;;
+11F10;KAWI LETTER O;Lo;0;L;;;;;N;;;;;
+11F12;KAWI LETTER KA;Lo;0;L;;;;;N;;;;;
+11F13;KAWI LETTER KHA;Lo;0;L;;;;;N;;;;;
+11F14;KAWI LETTER GA;Lo;0;L;;;;;N;;;;;
+11F15;KAWI LETTER GHA;Lo;0;L;;;;;N;;;;;
+11F16;KAWI LETTER NGA;Lo;0;L;;;;;N;;;;;
+11F17;KAWI LETTER CA;Lo;0;L;;;;;N;;;;;
+11F18;KAWI LETTER CHA;Lo;0;L;;;;;N;;;;;
+11F19;KAWI LETTER JA;Lo;0;L;;;;;N;;;;;
+11F1A;KAWI LETTER JHA;Lo;0;L;;;;;N;;;;;
+11F1B;KAWI LETTER NYA;Lo;0;L;;;;;N;;;;;
+11F1C;KAWI LETTER TTA;Lo;0;L;;;;;N;;;;;
+11F1D;KAWI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+11F1E;KAWI LETTER DDA;Lo;0;L;;;;;N;;;;;
+11F1F;KAWI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+11F20;KAWI LETTER NNA;Lo;0;L;;;;;N;;;;;
+11F21;KAWI LETTER TA;Lo;0;L;;;;;N;;;;;
+11F22;KAWI LETTER THA;Lo;0;L;;;;;N;;;;;
+11F23;KAWI LETTER DA;Lo;0;L;;;;;N;;;;;
+11F24;KAWI LETTER DHA;Lo;0;L;;;;;N;;;;;
+11F25;KAWI LETTER NA;Lo;0;L;;;;;N;;;;;
+11F26;KAWI LETTER PA;Lo;0;L;;;;;N;;;;;
+11F27;KAWI LETTER PHA;Lo;0;L;;;;;N;;;;;
+11F28;KAWI LETTER BA;Lo;0;L;;;;;N;;;;;
+11F29;KAWI LETTER BHA;Lo;0;L;;;;;N;;;;;
+11F2A;KAWI LETTER MA;Lo;0;L;;;;;N;;;;;
+11F2B;KAWI LETTER YA;Lo;0;L;;;;;N;;;;;
+11F2C;KAWI LETTER RA;Lo;0;L;;;;;N;;;;;
+11F2D;KAWI LETTER LA;Lo;0;L;;;;;N;;;;;
+11F2E;KAWI LETTER WA;Lo;0;L;;;;;N;;;;;
+11F2F;KAWI LETTER SHA;Lo;0;L;;;;;N;;;;;
+11F30;KAWI LETTER SSA;Lo;0;L;;;;;N;;;;;
+11F31;KAWI LETTER SA;Lo;0;L;;;;;N;;;;;
+11F32;KAWI LETTER HA;Lo;0;L;;;;;N;;;;;
+11F33;KAWI LETTER JNYA;Lo;0;L;;;;;N;;;;;
+11F34;KAWI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+11F35;KAWI VOWEL SIGN ALTERNATE AA;Mc;0;L;;;;;N;;;;;
+11F36;KAWI VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+11F37;KAWI VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+11F38;KAWI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+11F39;KAWI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+11F3A;KAWI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+11F3E;KAWI VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+11F3F;KAWI VOWEL SIGN AI;Mc;0;L;;;;;N;;;;;
+11F40;KAWI VOWEL SIGN EU;Mn;0;NSM;;;;;N;;;;;
+11F41;KAWI SIGN KILLER;Mc;9;L;;;;;N;;;;;
+11F42;KAWI CONJOINER;Mn;9;NSM;;;;;N;;;;;
+11F43;KAWI DANDA;Po;0;L;;;;;N;;;;;
+11F44;KAWI DOUBLE DANDA;Po;0;L;;;;;N;;;;;
+11F45;KAWI PUNCTUATION SECTION MARKER;Po;0;L;;;;;N;;;;;
+11F46;KAWI PUNCTUATION ALTERNATE SECTION MARKER;Po;0;L;;;;;N;;;;;
+11F47;KAWI PUNCTUATION FLOWER;Po;0;L;;;;;N;;;;;
+11F48;KAWI PUNCTUATION SPACE FILLER;Po;0;L;;;;;N;;;;;
+11F49;KAWI PUNCTUATION DOT;Po;0;L;;;;;N;;;;;
+11F4A;KAWI PUNCTUATION DOUBLE DOT;Po;0;L;;;;;N;;;;;
+11F4B;KAWI PUNCTUATION TRIPLE DOT;Po;0;L;;;;;N;;;;;
+11F4C;KAWI PUNCTUATION CIRCLE;Po;0;L;;;;;N;;;;;
+11F4D;KAWI PUNCTUATION FILLED CIRCLE;Po;0;L;;;;;N;;;;;
+11F4E;KAWI PUNCTUATION SPIRAL;Po;0;L;;;;;N;;;;;
+11F4F;KAWI PUNCTUATION CLOSING SPIRAL;Po;0;L;;;;;N;;;;;
+11F50;KAWI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+11F51;KAWI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+11F52;KAWI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+11F53;KAWI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+11F54;KAWI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+11F55;KAWI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+11F56;KAWI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+11F57;KAWI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+11F58;KAWI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+11F59;KAWI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
11FB0;LISU LETTER YHA;Lo;0;L;;;;;N;;;;;
11FC0;TAMIL FRACTION ONE THREE-HUNDRED-AND-TWENTIETH;No;0;L;;;;1/320;N;;;;;
11FC1;TAMIL FRACTION ONE ONE-HUNDRED-AND-SIXTIETH;No;0;L;;;;1/160;N;;;;;
@@ -24040,6 +24144,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1342C;EGYPTIAN HIEROGLYPH AA030;Lo;0;L;;;;;N;;;;;
1342D;EGYPTIAN HIEROGLYPH AA031;Lo;0;L;;;;;N;;;;;
1342E;EGYPTIAN HIEROGLYPH AA032;Lo;0;L;;;;;N;;;;;
+1342F;EGYPTIAN HIEROGLYPH V011D;Lo;0;L;;;;;N;;;;;
13430;EGYPTIAN HIEROGLYPH VERTICAL JOINER;Cf;0;L;;;;;N;;;;;
13431;EGYPTIAN HIEROGLYPH HORIZONTAL JOINER;Cf;0;L;;;;;N;;;;;
13432;EGYPTIAN HIEROGLYPH INSERT AT TOP START;Cf;0;L;;;;;N;;;;;
@@ -24049,6 +24154,35 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
13436;EGYPTIAN HIEROGLYPH OVERLAY MIDDLE;Cf;0;L;;;;;N;;;;;
13437;EGYPTIAN HIEROGLYPH BEGIN SEGMENT;Cf;0;L;;;;;N;;;;;
13438;EGYPTIAN HIEROGLYPH END SEGMENT;Cf;0;L;;;;;N;;;;;
+13439;EGYPTIAN HIEROGLYPH INSERT AT MIDDLE;Cf;0;L;;;;;N;;;;;
+1343A;EGYPTIAN HIEROGLYPH INSERT AT TOP;Cf;0;L;;;;;N;;;;;
+1343B;EGYPTIAN HIEROGLYPH INSERT AT BOTTOM;Cf;0;L;;;;;N;;;;;
+1343C;EGYPTIAN HIEROGLYPH BEGIN ENCLOSURE;Cf;0;L;;;;;N;;;;;
+1343D;EGYPTIAN HIEROGLYPH END ENCLOSURE;Cf;0;L;;;;;N;;;;;
+1343E;EGYPTIAN HIEROGLYPH BEGIN WALLED ENCLOSURE;Cf;0;L;;;;;N;;;;;
+1343F;EGYPTIAN HIEROGLYPH END WALLED ENCLOSURE;Cf;0;L;;;;;N;;;;;
+13440;EGYPTIAN HIEROGLYPH MIRROR HORIZONTALLY;Mn;0;NSM;;;;;N;;;;;
+13441;EGYPTIAN HIEROGLYPH FULL BLANK;Lo;0;L;;;;;N;;;;;
+13442;EGYPTIAN HIEROGLYPH HALF BLANK;Lo;0;L;;;;;N;;;;;
+13443;EGYPTIAN HIEROGLYPH LOST SIGN;Lo;0;L;;;;;N;;;;;
+13444;EGYPTIAN HIEROGLYPH HALF LOST SIGN;Lo;0;L;;;;;N;;;;;
+13445;EGYPTIAN HIEROGLYPH TALL LOST SIGN;Lo;0;L;;;;;N;;;;;
+13446;EGYPTIAN HIEROGLYPH WIDE LOST SIGN;Lo;0;L;;;;;N;;;;;
+13447;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT TOP START;Mn;0;NSM;;;;;N;;;;;
+13448;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT BOTTOM START;Mn;0;NSM;;;;;N;;;;;
+13449;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT START;Mn;0;NSM;;;;;N;;;;;
+1344A;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT TOP END;Mn;0;NSM;;;;;N;;;;;
+1344B;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT TOP;Mn;0;NSM;;;;;N;;;;;
+1344C;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT BOTTOM START AND TOP END;Mn;0;NSM;;;;;N;;;;;
+1344D;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT START AND TOP;Mn;0;NSM;;;;;N;;;;;
+1344E;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT BOTTOM END;Mn;0;NSM;;;;;N;;;;;
+1344F;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT TOP START AND BOTTOM END;Mn;0;NSM;;;;;N;;;;;
+13450;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT BOTTOM;Mn;0;NSM;;;;;N;;;;;
+13451;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT START AND BOTTOM;Mn;0;NSM;;;;;N;;;;;
+13452;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT END;Mn;0;NSM;;;;;N;;;;;
+13453;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT TOP AND END;Mn;0;NSM;;;;;N;;;;;
+13454;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT BOTTOM AND END;Mn;0;NSM;;;;;N;;;;;
+13455;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED;Mn;0;NSM;;;;;N;;;;;
14400;ANATOLIAN HIEROGLYPH A001;Lo;0;L;;;;;N;;;;;
14401;ANATOLIAN HIEROGLYPH A002;Lo;0;L;;;;;N;;;;;
14402;ANATOLIAN HIEROGLYPH A003;Lo;0;L;;;;;N;;;;;
@@ -27289,9 +27423,11 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;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;;;;;
+1B132;HIRAGANA LETTER SMALL KO;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;;;;;
+1B155;KATAKANA LETTER SMALL KO;Lo;0;L;;;;;N;;;;;
1B164;KATAKANA LETTER SMALL WI;Lo;0;L;;;;;N;;;;;
1B165;KATAKANA LETTER SMALL WE;Lo;0;L;;;;;N;;;;;
1B166;KATAKANA LETTER SMALL WO;Lo;0;L;;;;;N;;;;;
@@ -28573,6 +28709,26 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1D243;COMBINING GREEK MUSICAL TETRASEME;Mn;230;NSM;;;;;N;;;;;
1D244;COMBINING GREEK MUSICAL PENTASEME;Mn;230;NSM;;;;;N;;;;;
1D245;GREEK MUSICAL LEIMMA;So;0;ON;;;;;N;;;;;
+1D2C0;KAKTOVIK NUMERAL ZERO;No;0;L;;;;0;N;;;;;
+1D2C1;KAKTOVIK NUMERAL ONE;No;0;L;;;;1;N;;;;;
+1D2C2;KAKTOVIK NUMERAL TWO;No;0;L;;;;2;N;;;;;
+1D2C3;KAKTOVIK NUMERAL THREE;No;0;L;;;;3;N;;;;;
+1D2C4;KAKTOVIK NUMERAL FOUR;No;0;L;;;;4;N;;;;;
+1D2C5;KAKTOVIK NUMERAL FIVE;No;0;L;;;;5;N;;;;;
+1D2C6;KAKTOVIK NUMERAL SIX;No;0;L;;;;6;N;;;;;
+1D2C7;KAKTOVIK NUMERAL SEVEN;No;0;L;;;;7;N;;;;;
+1D2C8;KAKTOVIK NUMERAL EIGHT;No;0;L;;;;8;N;;;;;
+1D2C9;KAKTOVIK NUMERAL NINE;No;0;L;;;;9;N;;;;;
+1D2CA;KAKTOVIK NUMERAL TEN;No;0;L;;;;10;N;;;;;
+1D2CB;KAKTOVIK NUMERAL ELEVEN;No;0;L;;;;11;N;;;;;
+1D2CC;KAKTOVIK NUMERAL TWELVE;No;0;L;;;;12;N;;;;;
+1D2CD;KAKTOVIK NUMERAL THIRTEEN;No;0;L;;;;13;N;;;;;
+1D2CE;KAKTOVIK NUMERAL FOURTEEN;No;0;L;;;;14;N;;;;;
+1D2CF;KAKTOVIK NUMERAL FIFTEEN;No;0;L;;;;15;N;;;;;
+1D2D0;KAKTOVIK NUMERAL SIXTEEN;No;0;L;;;;16;N;;;;;
+1D2D1;KAKTOVIK NUMERAL SEVENTEEN;No;0;L;;;;17;N;;;;;
+1D2D2;KAKTOVIK NUMERAL EIGHTEEN;No;0;L;;;;18;N;;;;;
+1D2D3;KAKTOVIK NUMERAL NINETEEN;No;0;L;;;;19;N;;;;;
1D2E0;MAYAN NUMERAL ZERO;No;0;L;;;;0;N;;;;;
1D2E1;MAYAN NUMERAL ONE;No;0;L;;;;1;N;;;;;
1D2E2;MAYAN NUMERAL TWO;No;0;L;;;;2;N;;;;;
@@ -30404,6 +30560,12 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;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;;;;;
+1DF25;LATIN SMALL LETTER D WITH MID-HEIGHT LEFT HOOK;Ll;0;L;;;;;N;;;;;
+1DF26;LATIN SMALL LETTER L WITH MID-HEIGHT LEFT HOOK;Ll;0;L;;;;;N;;;;;
+1DF27;LATIN SMALL LETTER N WITH MID-HEIGHT LEFT HOOK;Ll;0;L;;;;;N;;;;;
+1DF28;LATIN SMALL LETTER R WITH MID-HEIGHT LEFT HOOK;Ll;0;L;;;;;N;;;;;
+1DF29;LATIN SMALL LETTER S WITH MID-HEIGHT LEFT HOOK;Ll;0;L;;;;;N;;;;;
+1DF2A;LATIN SMALL LETTER T WITH MID-HEIGHT LEFT HOOK;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;;;;;
@@ -30442,6 +30604,69 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1E028;COMBINING GLAGOLITIC LETTER BIG YUS;Mn;230;NSM;;;;;N;;;;;
1E029;COMBINING GLAGOLITIC LETTER IOTATED BIG YUS;Mn;230;NSM;;;;;N;;;;;
1E02A;COMBINING GLAGOLITIC LETTER FITA;Mn;230;NSM;;;;;N;;;;;
+1E030;MODIFIER LETTER CYRILLIC SMALL A;Lm;0;L;<super> 0430;;;;N;;;;;
+1E031;MODIFIER LETTER CYRILLIC SMALL BE;Lm;0;L;<super> 0431;;;;N;;;;;
+1E032;MODIFIER LETTER CYRILLIC SMALL VE;Lm;0;L;<super> 0432;;;;N;;;;;
+1E033;MODIFIER LETTER CYRILLIC SMALL GHE;Lm;0;L;<super> 0433;;;;N;;;;;
+1E034;MODIFIER LETTER CYRILLIC SMALL DE;Lm;0;L;<super> 0434;;;;N;;;;;
+1E035;MODIFIER LETTER CYRILLIC SMALL IE;Lm;0;L;<super> 0435;;;;N;;;;;
+1E036;MODIFIER LETTER CYRILLIC SMALL ZHE;Lm;0;L;<super> 0436;;;;N;;;;;
+1E037;MODIFIER LETTER CYRILLIC SMALL ZE;Lm;0;L;<super> 0437;;;;N;;;;;
+1E038;MODIFIER LETTER CYRILLIC SMALL I;Lm;0;L;<super> 0438;;;;N;;;;;
+1E039;MODIFIER LETTER CYRILLIC SMALL KA;Lm;0;L;<super> 043A;;;;N;;;;;
+1E03A;MODIFIER LETTER CYRILLIC SMALL EL;Lm;0;L;<super> 043B;;;;N;;;;;
+1E03B;MODIFIER LETTER CYRILLIC SMALL EM;Lm;0;L;<super> 043C;;;;N;;;;;
+1E03C;MODIFIER LETTER CYRILLIC SMALL O;Lm;0;L;<super> 043E;;;;N;;;;;
+1E03D;MODIFIER LETTER CYRILLIC SMALL PE;Lm;0;L;<super> 043F;;;;N;;;;;
+1E03E;MODIFIER LETTER CYRILLIC SMALL ER;Lm;0;L;<super> 0440;;;;N;;;;;
+1E03F;MODIFIER LETTER CYRILLIC SMALL ES;Lm;0;L;<super> 0441;;;;N;;;;;
+1E040;MODIFIER LETTER CYRILLIC SMALL TE;Lm;0;L;<super> 0442;;;;N;;;;;
+1E041;MODIFIER LETTER CYRILLIC SMALL U;Lm;0;L;<super> 0443;;;;N;;;;;
+1E042;MODIFIER LETTER CYRILLIC SMALL EF;Lm;0;L;<super> 0444;;;;N;;;;;
+1E043;MODIFIER LETTER CYRILLIC SMALL HA;Lm;0;L;<super> 0445;;;;N;;;;;
+1E044;MODIFIER LETTER CYRILLIC SMALL TSE;Lm;0;L;<super> 0446;;;;N;;;;;
+1E045;MODIFIER LETTER CYRILLIC SMALL CHE;Lm;0;L;<super> 0447;;;;N;;;;;
+1E046;MODIFIER LETTER CYRILLIC SMALL SHA;Lm;0;L;<super> 0448;;;;N;;;;;
+1E047;MODIFIER LETTER CYRILLIC SMALL YERU;Lm;0;L;<super> 044B;;;;N;;;;;
+1E048;MODIFIER LETTER CYRILLIC SMALL E;Lm;0;L;<super> 044D;;;;N;;;;;
+1E049;MODIFIER LETTER CYRILLIC SMALL YU;Lm;0;L;<super> 044E;;;;N;;;;;
+1E04A;MODIFIER LETTER CYRILLIC SMALL DZZE;Lm;0;L;<super> A689;;;;N;;;;;
+1E04B;MODIFIER LETTER CYRILLIC SMALL SCHWA;Lm;0;L;<super> 04D9;;;;N;;;;;
+1E04C;MODIFIER LETTER CYRILLIC SMALL BYELORUSSIAN-UKRAINIAN I;Lm;0;L;<super> 0456;;;;N;;;;;
+1E04D;MODIFIER LETTER CYRILLIC SMALL JE;Lm;0;L;<super> 0458;;;;N;;;;;
+1E04E;MODIFIER LETTER CYRILLIC SMALL BARRED O;Lm;0;L;<super> 04E9;;;;N;;;;;
+1E04F;MODIFIER LETTER CYRILLIC SMALL STRAIGHT U;Lm;0;L;<super> 04AF;;;;N;;;;;
+1E050;MODIFIER LETTER CYRILLIC SMALL PALOCHKA;Lm;0;L;<super> 04CF;;;;N;;;;;
+1E051;CYRILLIC SUBSCRIPT SMALL LETTER A;Lm;0;L;<sub> 0430;;;;N;;;;;
+1E052;CYRILLIC SUBSCRIPT SMALL LETTER BE;Lm;0;L;<sub> 0431;;;;N;;;;;
+1E053;CYRILLIC SUBSCRIPT SMALL LETTER VE;Lm;0;L;<sub> 0432;;;;N;;;;;
+1E054;CYRILLIC SUBSCRIPT SMALL LETTER GHE;Lm;0;L;<sub> 0433;;;;N;;;;;
+1E055;CYRILLIC SUBSCRIPT SMALL LETTER DE;Lm;0;L;<sub> 0434;;;;N;;;;;
+1E056;CYRILLIC SUBSCRIPT SMALL LETTER IE;Lm;0;L;<sub> 0435;;;;N;;;;;
+1E057;CYRILLIC SUBSCRIPT SMALL LETTER ZHE;Lm;0;L;<sub> 0436;;;;N;;;;;
+1E058;CYRILLIC SUBSCRIPT SMALL LETTER ZE;Lm;0;L;<sub> 0437;;;;N;;;;;
+1E059;CYRILLIC SUBSCRIPT SMALL LETTER I;Lm;0;L;<sub> 0438;;;;N;;;;;
+1E05A;CYRILLIC SUBSCRIPT SMALL LETTER KA;Lm;0;L;<sub> 043A;;;;N;;;;;
+1E05B;CYRILLIC SUBSCRIPT SMALL LETTER EL;Lm;0;L;<sub> 043B;;;;N;;;;;
+1E05C;CYRILLIC SUBSCRIPT SMALL LETTER O;Lm;0;L;<sub> 043E;;;;N;;;;;
+1E05D;CYRILLIC SUBSCRIPT SMALL LETTER PE;Lm;0;L;<sub> 043F;;;;N;;;;;
+1E05E;CYRILLIC SUBSCRIPT SMALL LETTER ES;Lm;0;L;<sub> 0441;;;;N;;;;;
+1E05F;CYRILLIC SUBSCRIPT SMALL LETTER U;Lm;0;L;<sub> 0443;;;;N;;;;;
+1E060;CYRILLIC SUBSCRIPT SMALL LETTER EF;Lm;0;L;<sub> 0444;;;;N;;;;;
+1E061;CYRILLIC SUBSCRIPT SMALL LETTER HA;Lm;0;L;<sub> 0445;;;;N;;;;;
+1E062;CYRILLIC SUBSCRIPT SMALL LETTER TSE;Lm;0;L;<sub> 0446;;;;N;;;;;
+1E063;CYRILLIC SUBSCRIPT SMALL LETTER CHE;Lm;0;L;<sub> 0447;;;;N;;;;;
+1E064;CYRILLIC SUBSCRIPT SMALL LETTER SHA;Lm;0;L;<sub> 0448;;;;N;;;;;
+1E065;CYRILLIC SUBSCRIPT SMALL LETTER HARD SIGN;Lm;0;L;<sub> 044A;;;;N;;;;;
+1E066;CYRILLIC SUBSCRIPT SMALL LETTER YERU;Lm;0;L;<sub> 044B;;;;N;;;;;
+1E067;CYRILLIC SUBSCRIPT SMALL LETTER GHE WITH UPTURN;Lm;0;L;<sub> 0491;;;;N;;;;;
+1E068;CYRILLIC SUBSCRIPT SMALL LETTER BYELORUSSIAN-UKRAINIAN I;Lm;0;L;<sub> 0456;;;;N;;;;;
+1E069;CYRILLIC SUBSCRIPT SMALL LETTER DZE;Lm;0;L;<sub> 0455;;;;N;;;;;
+1E06A;CYRILLIC SUBSCRIPT SMALL LETTER DZHE;Lm;0;L;<sub> 045F;;;;N;;;;;
+1E06B;MODIFIER LETTER CYRILLIC SMALL ES WITH DESCENDER;Lm;0;L;<super> 04AB;;;;N;;;;;
+1E06C;MODIFIER LETTER CYRILLIC SMALL YERU WITH BACK YER;Lm;0;L;<super> A651;;;;N;;;;;
+1E06D;MODIFIER LETTER CYRILLIC SMALL STRAIGHT U WITH STROKE;Lm;0;L;<super> 04B1;;;;N;;;;;
+1E08F;COMBINING CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I;Mn;230;NSM;;;;;N;;;;;
1E100;NYIAKENG PUACHUE HMONG LETTER MA;Lo;0;L;;;;;N;;;;;
1E101;NYIAKENG PUACHUE HMONG LETTER TSA;Lo;0;L;;;;;N;;;;;
1E102;NYIAKENG PUACHUE HMONG LETTER NTA;Lo;0;L;;;;;N;;;;;
@@ -30603,6 +30828,48 @@ 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;;;;;
+1E4D0;NAG MUNDARI LETTER O;Lo;0;L;;;;;N;;;;;
+1E4D1;NAG MUNDARI LETTER OP;Lo;0;L;;;;;N;;;;;
+1E4D2;NAG MUNDARI LETTER OL;Lo;0;L;;;;;N;;;;;
+1E4D3;NAG MUNDARI LETTER OY;Lo;0;L;;;;;N;;;;;
+1E4D4;NAG MUNDARI LETTER ONG;Lo;0;L;;;;;N;;;;;
+1E4D5;NAG MUNDARI LETTER A;Lo;0;L;;;;;N;;;;;
+1E4D6;NAG MUNDARI LETTER AJ;Lo;0;L;;;;;N;;;;;
+1E4D7;NAG MUNDARI LETTER AB;Lo;0;L;;;;;N;;;;;
+1E4D8;NAG MUNDARI LETTER ANY;Lo;0;L;;;;;N;;;;;
+1E4D9;NAG MUNDARI LETTER AH;Lo;0;L;;;;;N;;;;;
+1E4DA;NAG MUNDARI LETTER I;Lo;0;L;;;;;N;;;;;
+1E4DB;NAG MUNDARI LETTER IS;Lo;0;L;;;;;N;;;;;
+1E4DC;NAG MUNDARI LETTER IDD;Lo;0;L;;;;;N;;;;;
+1E4DD;NAG MUNDARI LETTER IT;Lo;0;L;;;;;N;;;;;
+1E4DE;NAG MUNDARI LETTER IH;Lo;0;L;;;;;N;;;;;
+1E4DF;NAG MUNDARI LETTER U;Lo;0;L;;;;;N;;;;;
+1E4E0;NAG MUNDARI LETTER UC;Lo;0;L;;;;;N;;;;;
+1E4E1;NAG MUNDARI LETTER UD;Lo;0;L;;;;;N;;;;;
+1E4E2;NAG MUNDARI LETTER UK;Lo;0;L;;;;;N;;;;;
+1E4E3;NAG MUNDARI LETTER UR;Lo;0;L;;;;;N;;;;;
+1E4E4;NAG MUNDARI LETTER E;Lo;0;L;;;;;N;;;;;
+1E4E5;NAG MUNDARI LETTER ENN;Lo;0;L;;;;;N;;;;;
+1E4E6;NAG MUNDARI LETTER EG;Lo;0;L;;;;;N;;;;;
+1E4E7;NAG MUNDARI LETTER EM;Lo;0;L;;;;;N;;;;;
+1E4E8;NAG MUNDARI LETTER EN;Lo;0;L;;;;;N;;;;;
+1E4E9;NAG MUNDARI LETTER ETT;Lo;0;L;;;;;N;;;;;
+1E4EA;NAG MUNDARI LETTER ELL;Lo;0;L;;;;;N;;;;;
+1E4EB;NAG MUNDARI SIGN OJOD;Lm;0;L;;;;;N;;;;;
+1E4EC;NAG MUNDARI SIGN MUHOR;Mn;232;NSM;;;;;N;;;;;
+1E4ED;NAG MUNDARI SIGN TOYOR;Mn;232;NSM;;;;;N;;;;;
+1E4EE;NAG MUNDARI SIGN IKIR;Mn;220;NSM;;;;;N;;;;;
+1E4EF;NAG MUNDARI SIGN SUTUH;Mn;230;NSM;;;;;N;;;;;
+1E4F0;NAG MUNDARI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+1E4F1;NAG MUNDARI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+1E4F2;NAG MUNDARI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+1E4F3;NAG MUNDARI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1E4F4;NAG MUNDARI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1E4F5;NAG MUNDARI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1E4F6;NAG MUNDARI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1E4F7;NAG MUNDARI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1E4F8;NAG MUNDARI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1E4F9;NAG MUNDARI DIGIT NINE;Nd;0;L;;9;9;9;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;;;;;
@@ -32678,6 +32945,7 @@ 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;;;;;
+1F6DC;WIRELESS;So;0;ON;;;;;N;;;;;
1F6DD;PLAYGROUND SLIDE;So;0;ON;;;;;N;;;;;
1F6DE;WHEEL;So;0;ON;;;;;N;;;;;
1F6DF;RING BUOY;So;0;ON;;;;;N;;;;;
@@ -32823,6 +33091,14 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F771;ALCHEMICAL SYMBOL FOR MONTH;So;0;ON;;;;;N;;;;;
1F772;ALCHEMICAL SYMBOL FOR HALF DRAM;So;0;ON;;;;;N;;;;;
1F773;ALCHEMICAL SYMBOL FOR HALF OUNCE;So;0;ON;;;;;N;;;;;
+1F774;LOT OF FORTUNE;So;0;ON;;;;;N;;;;;
+1F775;OCCULTATION;So;0;ON;;;;;N;;;;;
+1F776;LUNAR ECLIPSE;So;0;ON;;;;;N;;;;;
+1F77B;HAUMEA;So;0;ON;;;;;N;;;;;
+1F77C;MAKEMAKE;So;0;ON;;;;;N;;;;;
+1F77D;GONGGONG;So;0;ON;;;;;N;;;;;
+1F77E;QUAOAR;So;0;ON;;;;;N;;;;;
+1F77F;ORCUS;So;0;ON;;;;;N;;;;;
1F780;BLACK LEFT-POINTING ISOSCELES RIGHT TRIANGLE;So;0;ON;;;;;N;;;;;
1F781;BLACK UP-POINTING ISOSCELES RIGHT TRIANGLE;So;0;ON;;;;;N;;;;;
1F782;BLACK RIGHT-POINTING ISOSCELES RIGHT TRIANGLE;So;0;ON;;;;;N;;;;;
@@ -32912,6 +33188,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F7D6;NEGATIVE CIRCLED TRIANGLE;So;0;ON;;;;;N;;;;;
1F7D7;CIRCLED SQUARE;So;0;ON;;;;;N;;;;;
1F7D8;NEGATIVE CIRCLED SQUARE;So;0;ON;;;;;N;;;;;
+1F7D9;NINE POINTED WHITE STAR;So;0;ON;;;;;N;;;;;
1F7E0;LARGE ORANGE CIRCLE;So;0;ON;;;;;N;;;;;
1F7E1;LARGE YELLOW CIRCLE;So;0;ON;;;;;N;;;;;
1F7E2;LARGE GREEN CIRCLE;So;0;ON;;;;;N;;;;;
@@ -33434,6 +33711,9 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1FA72;BRIEFS;So;0;ON;;;;;N;;;;;
1FA73;SHORTS;So;0;ON;;;;;N;;;;;
1FA74;THONG SANDAL;So;0;ON;;;;;N;;;;;
+1FA75;LIGHT BLUE HEART;So;0;ON;;;;;N;;;;;
+1FA76;GREY HEART;So;0;ON;;;;;N;;;;;
+1FA77;PINK HEART;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;;;;;
@@ -33446,6 +33726,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1FA84;MAGIC WAND;So;0;ON;;;;;N;;;;;
1FA85;PINATA;So;0;ON;;;;;N;;;;;
1FA86;NESTING DOLLS;So;0;ON;;;;;N;;;;;
+1FA87;MARACAS;So;0;ON;;;;;N;;;;;
+1FA88;FLUTE;So;0;ON;;;;;N;;;;;
1FA90;RINGED PLANET;So;0;ON;;;;;N;;;;;
1FA91;CHAIR;So;0;ON;;;;;N;;;;;
1FA92;RAZOR;So;0;ON;;;;;N;;;;;
@@ -33475,6 +33757,9 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1FAAA;IDENTIFICATION CARD;So;0;ON;;;;;N;;;;;
1FAAB;LOW BATTERY;So;0;ON;;;;;N;;;;;
1FAAC;HAMSA;So;0;ON;;;;;N;;;;;
+1FAAD;FOLDING HAND FAN;So;0;ON;;;;;N;;;;;
+1FAAE;HAIR PICK;So;0;ON;;;;;N;;;;;
+1FAAF;KHANDA;So;0;ON;;;;;N;;;;;
1FAB0;FLY;So;0;ON;;;;;N;;;;;
1FAB1;WORM;So;0;ON;;;;;N;;;;;
1FAB2;BEETLE;So;0;ON;;;;;N;;;;;
@@ -33486,12 +33771,18 @@ FFFD;REPLACEMENT CHARACTER;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;;;;;
+1FABB;HYACINTH;So;0;ON;;;;;N;;;;;
+1FABC;JELLYFISH;So;0;ON;;;;;N;;;;;
+1FABD;WING;So;0;ON;;;;;N;;;;;
+1FABF;GOOSE;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;;;;;
+1FACE;MOOSE;So;0;ON;;;;;N;;;;;
+1FACF;DONKEY;So;0;ON;;;;;N;;;;;
1FAD0;BLUEBERRIES;So;0;ON;;;;;N;;;;;
1FAD1;BELL PEPPER;So;0;ON;;;;;N;;;;;
1FAD2;OLIVE;So;0;ON;;;;;N;;;;;
@@ -33502,6 +33793,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1FAD7;POURING LIQUID;So;0;ON;;;;;N;;;;;
1FAD8;BEANS;So;0;ON;;;;;N;;;;;
1FAD9;JAR;So;0;ON;;;;;N;;;;;
+1FADA;GINGER ROOT;So;0;ON;;;;;N;;;;;
+1FADB;PEA POD;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;;;;;
@@ -33510,6 +33803,7 @@ FFFD;REPLACEMENT CHARACTER;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;;;;;
+1FAE8;SHAKING FACE;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;;;;;
@@ -33517,6 +33811,8 @@ FFFD;REPLACEMENT CHARACTER;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;;;;;
+1FAF7;LEFTWARDS PUSHING HAND;So;0;ON;;;;;N;;;;;
+1FAF8;RIGHTWARDS PUSHING HAND;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;;;;;
@@ -33732,7 +34028,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
20000;<CJK Ideograph Extension B, First>;Lo;0;L;;;;;N;;;;;
2A6DF;<CJK Ideograph Extension B, Last>;Lo;0;L;;;;;N;;;;;
2A700;<CJK Ideograph Extension C, First>;Lo;0;L;;;;;N;;;;;
-2B738;<CJK Ideograph Extension C, Last>;Lo;0;L;;;;;N;;;;;
+2B739;<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;;;;;
@@ -34283,6 +34579,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
2FA1D;CJK COMPATIBILITY IDEOGRAPH-2FA1D;Lo;0;L;2A600;;;;N;;;;;
30000;<CJK Ideograph Extension G, First>;Lo;0;L;;;;;N;;;;;
3134A;<CJK Ideograph Extension G, Last>;Lo;0;L;;;;;N;;;;;
+31350;<CJK Ideograph Extension H, First>;Lo;0;L;;;;;N;;;;;
+323AF;<CJK Ideograph Extension H, Last>;Lo;0;L;;;;;N;;;;;
E0001;LANGUAGE TAG;Cf;0;BN;;;;;N;;;;;
E0020;TAG SPACE;Cf;0;BN;;;;;N;;;;;
E0021;TAG EXCLAMATION MARK;Cf;0;BN;;;;;N;;;;;
diff --git a/src/unicode/emoji-data.txt b/src/unicode/emoji-data.txt
index 7806c7ab53..999a436779 100644
--- a/src/unicode/emoji-data.txt
+++ b/src/unicode/emoji-data.txt
@@ -1,13 +1,13 @@
-# emoji-data-14.0.0.txt
-# Date: 2021-08-26, 17:22:22 GMT
-# © 2021 Unicode®, Inc.
+# emoji-data.txt
+# Date: 2022-08-02, 00:26:10 GMT
+# © 2022 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
#
# Emoji Data for UTS #51
-# Used with Emoji Version 14.0 and subsequent minor revisions (if any)
+# Used with Emoji Version 15.0 and subsequent minor revisions (if any)
#
-# For documentation and usage, see http://www.unicode.org/reports/tr51
+# For documentation and usage, see https://www.unicode.org/reports/tr51
#
# Format:
# <codepoint(s)> ; <property> # <comments>
@@ -19,8 +19,7 @@
# ================================================
-# All omitted code points have Emoji=No
-# @missing: 0000..10FFFF ; Emoji ; No
+# All omitted code points have Emoji=No
0023 ; Emoji # E0.0 [1] (#️) hash sign
002A ; Emoji # E0.0 [1] (*️) asterisk
@@ -341,6 +340,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
+1F6DC ; Emoji # E15.0 [1] (🛜) wireless
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
@@ -401,28 +401,36 @@
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
+1FA75..1FA77 ; Emoji # E15.0 [3] (🩵..🩷) light blue heart..pink heart
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
+1FA87..1FA88 ; Emoji # E15.0 [2] (🪇..🪈) maracas..flute
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
+1FAAD..1FAAF ; Emoji # E15.0 [3] (🪭..🪯) folding hand fan..khanda
1FAB0..1FAB6 ; Emoji # E13.0 [7] (🪰..🪶) fly..feather
1FAB7..1FABA ; Emoji # E14.0 [4] (🪷..🪺) lotus..nest with eggs
+1FABB..1FABD ; Emoji # E15.0 [3] (🪻..🪽) hyacinth..wing
+1FABF ; Emoji # E15.0 [1] (🪿) goose
1FAC0..1FAC2 ; Emoji # E13.0 [3] (🫀..🫂) anatomical heart..people hugging
1FAC3..1FAC5 ; Emoji # E14.0 [3] (🫃..🫅) pregnant man..person with crown
+1FACE..1FACF ; Emoji # E15.0 [2] (🫎..🫏) moose..donkey
1FAD0..1FAD6 ; Emoji # E13.0 [7] (🫐..🫖) blueberries..teapot
1FAD7..1FAD9 ; Emoji # E14.0 [3] (🫗..🫙) pouring liquid..jar
+1FADA..1FADB ; Emoji # E15.0 [2] (🫚..🫛) ginger root..pea pod
1FAE0..1FAE7 ; Emoji # E14.0 [8] (🫠..🫧) melting face..bubbles
+1FAE8 ; Emoji # E15.0 [1] (🫨) shaking face
1FAF0..1FAF6 ; Emoji # E14.0 [7] (🫰..🫶) hand with index finger and thumb crossed..heart hands
+1FAF7..1FAF8 ; Emoji # E15.0 [2] (🫷..🫸) leftwards pushing hand..rightwards pushing hand
-# Total elements: 1404
+# Total elements: 1424
# ================================================
-# All omitted code points have Emoji_Presentation=No
-# @missing: 0000..10FFFF ; Emoji_Presentation ; No
+# All omitted code points have Emoji_Presentation=No
231A..231B ; Emoji_Presentation # E0.6 [2] (⌚..⌛) watch..hourglass done
23E9..23EC ; Emoji_Presentation # E0.6 [4] (⏩..⏬) fast-forward button..fast down button
@@ -625,6 +633,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
+1F6DC ; Emoji_Presentation # E15.0 [1] (🛜) wireless
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
@@ -681,28 +690,36 @@
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
+1FA75..1FA77 ; Emoji_Presentation # E15.0 [3] (🩵..🩷) light blue heart..pink heart
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
+1FA87..1FA88 ; Emoji_Presentation # E15.0 [2] (🪇..🪈) maracas..flute
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
+1FAAD..1FAAF ; Emoji_Presentation # E15.0 [3] (🪭..🪯) folding hand fan..khanda
1FAB0..1FAB6 ; Emoji_Presentation # E13.0 [7] (🪰..🪶) fly..feather
1FAB7..1FABA ; Emoji_Presentation # E14.0 [4] (🪷..🪺) lotus..nest with eggs
+1FABB..1FABD ; Emoji_Presentation # E15.0 [3] (🪻..🪽) hyacinth..wing
+1FABF ; Emoji_Presentation # E15.0 [1] (🪿) goose
1FAC0..1FAC2 ; Emoji_Presentation # E13.0 [3] (🫀..🫂) anatomical heart..people hugging
1FAC3..1FAC5 ; Emoji_Presentation # E14.0 [3] (🫃..🫅) pregnant man..person with crown
+1FACE..1FACF ; Emoji_Presentation # E15.0 [2] (🫎..🫏) moose..donkey
1FAD0..1FAD6 ; Emoji_Presentation # E13.0 [7] (🫐..🫖) blueberries..teapot
1FAD7..1FAD9 ; Emoji_Presentation # E14.0 [3] (🫗..🫙) pouring liquid..jar
+1FADA..1FADB ; Emoji_Presentation # E15.0 [2] (🫚..🫛) ginger root..pea pod
1FAE0..1FAE7 ; Emoji_Presentation # E14.0 [8] (🫠..🫧) melting face..bubbles
+1FAE8 ; Emoji_Presentation # E15.0 [1] (🫨) shaking face
1FAF0..1FAF6 ; Emoji_Presentation # E14.0 [7] (🫰..🫶) hand with index finger and thumb crossed..heart hands
+1FAF7..1FAF8 ; Emoji_Presentation # E15.0 [2] (🫷..🫸) leftwards pushing hand..rightwards pushing hand
-# Total elements: 1185
+# Total elements: 1205
# ================================================
-# All omitted code points have Emoji_Modifier=No
-# @missing: 0000..10FFFF ; Emoji_Modifier ; No
+# All omitted code points have Emoji_Modifier=No
1F3FB..1F3FF ; Emoji_Modifier # E1.0 [5] (🏻..🏿) light skin tone..dark skin tone
@@ -710,8 +727,7 @@
# ================================================
-# All omitted code points have Emoji_Modifier_Base=No
-# @missing: 0000..10FFFF ; Emoji_Modifier_Base ; No
+# All omitted code points have Emoji_Modifier_Base=No
261D ; Emoji_Modifier_Base # E0.6 [1] (☝️) index pointing up
26F9 ; Emoji_Modifier_Base # E0.7 [1] (⛹️) person bouncing ball
@@ -762,13 +778,13 @@
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
+1FAF7..1FAF8 ; Emoji_Modifier_Base # E15.0 [2] (🫷..🫸) leftwards pushing hand..rightwards pushing hand
-# Total elements: 132
+# Total elements: 134
# ================================================
-# All omitted code points have Emoji_Component=No
-# @missing: 0000..10FFFF ; Emoji_Component ; No
+# All omitted code points have Emoji_Component=No
0023 ; Emoji_Component # E0.0 [1] (#️) hash sign
002A ; Emoji_Component # E0.0 [1] (*️) asterisk
@@ -785,8 +801,7 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c
# ================================================
-# All omitted code points have Extended_Pictographic=No
-# @missing: 0000..10FFFF ; Extended_Pictographic ; No
+# All omitted code points have Extended_Pictographic=No
00A9 ; Extended_Pictographic# E0.6 [1] (©️) copyright
00AE ; Extended_Pictographic# E0.6 [1] (®️) registered
@@ -1190,7 +1205,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..1F6DC ; Extended_Pictographic# E0.0 [5] (🛘..🛜) <reserved-1F6D8>..<reserved-1F6DC>
+1F6D8..1F6DB ; Extended_Pictographic# E0.0 [4] (🛘..🛛) <reserved-1F6D8>..<reserved-1F6DB>
+1F6DC ; Extended_Pictographic# E15.0 [1] (🛜) wireless
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
@@ -1207,7 +1223,7 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c
1F6FA ; Extended_Pictographic# E12.0 [1] (🛺) auto rickshaw
1F6FB..1F6FC ; Extended_Pictographic# E13.0 [2] (🛻..🛼) pickup truck..roller skate
1F6FD..1F6FF ; Extended_Pictographic# E0.0 [3] (🛽..🛿) <reserved-1F6FD>..<reserved-1F6FF>
-1F774..1F77F ; Extended_Pictographic# E0.0 [12] (🝴..🝿) <reserved-1F774>..<reserved-1F77F>
+1F774..1F77F ; Extended_Pictographic# E0.0 [12] (🝴..🝿) LOT OF FORTUNE..ORCUS
1F7D5..1F7DF ; Extended_Pictographic# E0.0 [11] (🟕..🟟) CIRCLED TRIANGLE..<reserved-1F7DF>
1F7E0..1F7EB ; Extended_Pictographic# E12.0 [12] (🟠..🟫) orange circle..brown square
1F7EC..1F7EF ; Extended_Pictographic# E0.0 [4] (🟬..🟯) <reserved-1F7EC>..<reserved-1F7EF>
@@ -1266,30 +1282,37 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c
1FA00..1FA6F ; Extended_Pictographic# E0.0 [112] (🨀..🩯) NEUTRAL CHESS KING..<reserved-1FA6F>
1FA70..1FA73 ; Extended_Pictographic# E12.0 [4] (🩰..🩳) ballet shoes..shorts
1FA74 ; Extended_Pictographic# E13.0 [1] (🩴) thong sandal
-1FA75..1FA77 ; Extended_Pictographic# E0.0 [3] (🩵..🩷) <reserved-1FA75>..<reserved-1FA77>
+1FA75..1FA77 ; Extended_Pictographic# E15.0 [3] (🩵..🩷) light blue heart..pink heart
1FA78..1FA7A ; Extended_Pictographic# E12.0 [3] (🩸..🩺) drop of blood..stethoscope
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>
+1FA87..1FA88 ; Extended_Pictographic# E15.0 [2] (🪇..🪈) maracas..flute
+1FA89..1FA8F ; Extended_Pictographic# E0.0 [7] (🪉..🪏) <reserved-1FA89>..<reserved-1FA8F>
1FA90..1FA95 ; Extended_Pictographic# E12.0 [6] (🪐..🪕) ringed planet..banjo
1FA96..1FAA8 ; Extended_Pictographic# E13.0 [19] (🪖..🪨) military helmet..rock
1FAA9..1FAAC ; Extended_Pictographic# E14.0 [4] (🪩..🪬) mirror ball..hamsa
-1FAAD..1FAAF ; Extended_Pictographic# E0.0 [3] (🪭..🪯) <reserved-1FAAD>..<reserved-1FAAF>
+1FAAD..1FAAF ; Extended_Pictographic# E15.0 [3] (🪭..🪯) folding hand fan..khanda
1FAB0..1FAB6 ; Extended_Pictographic# E13.0 [7] (🪰..🪶) fly..feather
1FAB7..1FABA ; Extended_Pictographic# E14.0 [4] (🪷..🪺) lotus..nest with eggs
-1FABB..1FABF ; Extended_Pictographic# E0.0 [5] (🪻..🪿) <reserved-1FABB>..<reserved-1FABF>
+1FABB..1FABD ; Extended_Pictographic# E15.0 [3] (🪻..🪽) hyacinth..wing
+1FABE ; Extended_Pictographic# E0.0 [1] (🪾) <reserved-1FABE>
+1FABF ; Extended_Pictographic# E15.0 [1] (🪿) goose
1FAC0..1FAC2 ; Extended_Pictographic# E13.0 [3] (🫀..🫂) anatomical heart..people hugging
1FAC3..1FAC5 ; Extended_Pictographic# E14.0 [3] (🫃..🫅) pregnant man..person with crown
-1FAC6..1FACF ; Extended_Pictographic# E0.0 [10] (🫆..🫏) <reserved-1FAC6>..<reserved-1FACF>
+1FAC6..1FACD ; Extended_Pictographic# E0.0 [8] (🫆..🫍) <reserved-1FAC6>..<reserved-1FACD>
+1FACE..1FACF ; Extended_Pictographic# E15.0 [2] (🫎..🫏) moose..donkey
1FAD0..1FAD6 ; Extended_Pictographic# E13.0 [7] (🫐..🫖) blueberries..teapot
1FAD7..1FAD9 ; Extended_Pictographic# E14.0 [3] (🫗..🫙) pouring liquid..jar
-1FADA..1FADF ; Extended_Pictographic# E0.0 [6] (🫚..🫟) <reserved-1FADA>..<reserved-1FADF>
+1FADA..1FADB ; Extended_Pictographic# E15.0 [2] (🫚..🫛) ginger root..pea pod
+1FADC..1FADF ; Extended_Pictographic# E0.0 [4] (🫜..🫟) <reserved-1FADC>..<reserved-1FADF>
1FAE0..1FAE7 ; Extended_Pictographic# E14.0 [8] (🫠..🫧) melting face..bubbles
-1FAE8..1FAEF ; Extended_Pictographic# E0.0 [8] (🫨..🫯) <reserved-1FAE8>..<reserved-1FAEF>
+1FAE8 ; Extended_Pictographic# E15.0 [1] (🫨) shaking face
+1FAE9..1FAEF ; Extended_Pictographic# E0.0 [7] (🫩..🫯) <reserved-1FAE9>..<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>
+1FAF7..1FAF8 ; Extended_Pictographic# E15.0 [2] (🫷..🫸) leftwards pushing hand..rightwards pushing hand
+1FAF9..1FAFF ; Extended_Pictographic# E0.0 [7] (🫹..🫿) <reserved-1FAF9>..<reserved-1FAFF>
1FC00..1FFFD ; Extended_Pictographic# E0.0[1022] (🰀..🿽) <reserved-1FC00>..<reserved-1FFFD>
# Total elements: 3537
diff --git a/test/benchmark/bench_re_freeze_spec.lua b/test/benchmark/bench_regexp_spec.lua
index ea41953014..903af5f574 100644
--- a/test/benchmark/bench_re_freeze_spec.lua
+++ b/test/benchmark/bench_regexp_spec.lua
@@ -1,4 +1,4 @@
--- Test for benchmarking RE engine.
+-- Test for benchmarking the RE engine.
local helpers = require('test.functional.helpers')(after_each)
local insert, source = helpers.insert, helpers.source
@@ -7,19 +7,19 @@ local clear, command = helpers.clear, helpers.command
-- Temporary file for gathering benchmarking results for each regexp engine.
local result_file = 'benchmark.out'
-- Fixture containing an HTML fragment that can make a search appear to freeze.
-local sample_file = 'test/benchmark/samples/re.freeze.txt'
+local sample_file = 'src/nvim/testdir/samples/re.freeze.txt'
-- Vim script code that does both the work and the benchmarking of that work.
local measure_cmd =
[[call Measure(%d, ']] .. sample_file .. [[', '\s\+\%%#\@<!$', '+5')]]
local measure_script = [[
- func! Measure(re, file, pattern, arg)
- let sstart=reltime()
+ func Measure(re, file, pattern, arg)
+ let sstart = reltime()
- execute 'set re=' . a:re
+ execute 'set re=' .. a:re
execute 'split' a:arg a:file
call search(a:pattern, '', '', 10000)
- q!
+ quit!
$put =printf('file: %s, re: %d, time: %s', a:file, a:re, reltimestr(reltime(sstart)))
endfunc]]
diff --git a/test/cmakeconfig/paths.lua.in b/test/cmakeconfig/paths.lua.in
index e3979981ba..6be238d838 100644
--- a/test/cmakeconfig/paths.lua.in
+++ b/test/cmakeconfig/paths.lua.in
@@ -6,7 +6,6 @@ for p in ("${TEST_INCLUDE_DIRS}" .. ";"):gmatch("[^;]+") do
end
module.test_build_dir = "${CMAKE_BINARY_DIR}"
-module.test_include_path = module.test_build_dir .. "/test/includes/post"
module.test_libnvim_path = "${TEST_LIBNVIM_PATH}"
module.test_source_path = "${CMAKE_SOURCE_DIR}"
module.test_lua_prg = "${LUA_PRG}"
diff --git a/runtime/lua/vim/compat.lua b/test/compat.lua
index 2c9786d491..2c9786d491 100644
--- a/runtime/lua/vim/compat.lua
+++ b/test/compat.lua
diff --git a/test/functional/api/autocmd_spec.lua b/test/functional/api/autocmd_spec.lua
index a923f5df0e..22a1311ee9 100644
--- a/test/functional/api/autocmd_spec.lua
+++ b/test/functional/api/autocmd_spec.lua
@@ -14,14 +14,14 @@ 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", {
+ it('"command" and "callback" are mutually exclusive', function()
+ local rv = pcall_err(meths.create_autocmd, "BufReadPost", {
pattern = "*.py,*.pyi",
command = "echo 'Should Have Errored",
- callback = "not allowed",
+ callback = "NotAllowed",
})
- eq(false, ok)
+ eq("specify either 'callback' or 'command', not both", rv)
end)
it('doesnt leak when you use ++once', function()
@@ -60,13 +60,13 @@ describe('autocmd api', function()
end)
it('does not allow passing buffer and patterns', function()
- local ok = pcall(meths.create_autocmd, "Filetype", {
+ local rv = pcall_err(meths.create_autocmd, "Filetype", {
command = "let g:called = g:called + 1",
buffer = 0,
pattern = "*.py",
})
- eq(false, ok)
+ eq("cannot pass both: 'pattern' and 'buffer' for the same autocmd", rv)
end)
it('does not allow passing invalid buffers', function()
@@ -613,6 +613,20 @@ describe('autocmd api', function()
eq(false, success)
matches("'group' must be a string or an integer", code)
end)
+
+ it('raises error for invalid pattern array', function()
+ local success, code = unpack(meths.exec_lua([[
+ return {pcall(function()
+ vim.api.nvim_create_autocmd("FileType", {
+ pattern = {{}},
+ command = "echo 'hello'",
+ })
+ end)}
+ ]], {}))
+
+ eq(false, success)
+ matches("All entries in 'pattern' must be strings", code)
+ end)
end)
describe('patterns', function()
diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua
index 8f6fc666c9..6b13729994 100644
--- a/test/functional/api/buffer_spec.lua
+++ b/test/functional/api/buffer_spec.lua
@@ -3,6 +3,7 @@ local Screen = require('test.functional.ui.screen')
local clear, nvim, buffer = helpers.clear, helpers.nvim, helpers.buffer
local curbuf, curwin, eq = helpers.curbuf, helpers.curwin, helpers.eq
local curbufmeths, ok = helpers.curbufmeths, helpers.ok
+local describe_lua_and_rpc = helpers.describe_lua_and_rpc(describe)
local meths = helpers.meths
local funcs = helpers.funcs
local request = helpers.request
@@ -185,12 +186,13 @@ describe('api/buf', function()
end)
end)
- describe('nvim_buf_get_lines, nvim_buf_set_lines', function()
- local get_lines, set_lines = curbufmeths.get_lines, curbufmeths.set_lines
- local line_count = curbufmeths.line_count
+ describe_lua_and_rpc('nvim_buf_get_lines, nvim_buf_set_lines', function(api)
+ local get_lines = api.curbufmeths.get_lines
+ local set_lines = api.curbufmeths.set_lines
+ local line_count = api.curbufmeths.line_count
it('fails correctly when input is not valid', function()
- eq(1, curbufmeths.get_number())
+ eq(1, api.curbufmeths.get_number())
eq([[String cannot contain newlines]],
pcall_err(bufmeths.set_lines, 1, 1, 2, false, {'b\na'}))
end)
@@ -198,7 +200,7 @@ describe('api/buf', function()
it("fails if 'nomodifiable'", function()
command('set nomodifiable')
eq([[Buffer is not 'modifiable']],
- pcall_err(bufmeths.set_lines, 1, 1, 2, false, {'a','b'}))
+ pcall_err(api.bufmeths.set_lines, 1, 1, 2, false, {'a','b'}))
end)
it('has correct line_count when inserting and deleting', function()
@@ -354,7 +356,7 @@ describe('api/buf', function()
Who would win?
A real window
with proper text]])
- local buf = meths.create_buf(false,true)
+ local buf = api.meths.create_buf(false,true)
screen:expect([[
Who would win? |
A real window |
@@ -363,7 +365,7 @@ describe('api/buf', function()
|
]])
- meths.buf_set_lines(buf, 0, -1, true, {'or some', 'scratchy text'})
+ api.meths.buf_set_lines(buf, 0, -1, true, {'or some', 'scratchy text'})
feed('i') -- provoke redraw
screen:expect([[
Who would win? |
@@ -379,15 +381,15 @@ describe('api/buf', function()
visible buffer line 1
line 2
]])
- local hiddenbuf = meths.create_buf(false,true)
+ local hiddenbuf = api.meths.create_buf(false,true)
command('vsplit')
command('vsplit')
feed('<c-w>l<c-w>l<c-w>l')
eq(3, funcs.winnr())
feed('<c-w>h')
eq(2, funcs.winnr())
- meths.buf_set_lines(hiddenbuf, 0, -1, true,
- {'hidden buffer line 1', 'line 2'})
+ api.meths.buf_set_lines(hiddenbuf, 0, -1, true,
+ {'hidden buffer line 1', 'line 2'})
feed('<c-w>p')
eq(3, funcs.winnr())
end)
@@ -579,13 +581,13 @@ describe('api/buf', function()
end)
end)
- describe('nvim_buf_get_text', function()
- local get_text = curbufmeths.get_text
-
+ describe_lua_and_rpc('nvim_buf_get_text', function(api)
+ local get_text = api.curbufmeths.get_text
before_each(function()
insert([[
hello foo!
- text]])
+ text
+ more]])
end)
it('works', function()
@@ -593,16 +595,17 @@ describe('api/buf', function()
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({'foo!', 'tex'}, get_text(-3, 6, -2, 3, {}))
eq({''}, get_text(0, 18, 0, 20, {}))
- eq({'ext'}, get_text(-1, 1, -1, 4, {}))
+ eq({'ext'}, get_text(-2, 1, -2, 4, {}))
+ eq({'hello foo!', 'text', 'm'}, get_text(0, 0, 2, 1, {}))
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, {}))
+ eq('Index out of bounds', pcall_err(get_text, 2, 0, 4, 0, {}))
+ eq('Index out of bounds', pcall_err(get_text, -4, 0, 0, 0, {}))
+ eq('Index out of bounds', pcall_err(get_text, 0, 0, 3, 0, {}))
+ eq('Index out of bounds', pcall_err(get_text, 0, 0, -4, 0, {}))
-- no ml_get errors should happen #19017
eq('', meths.get_vvar('errmsg'))
end)
diff --git a/test/functional/api/buffer_updates_spec.lua b/test/functional/api/buffer_updates_spec.lua
index 3d257e9477..d5f06c8f1f 100644
--- a/test/functional/api/buffer_updates_spec.lua
+++ b/test/functional/api/buffer_updates_spec.lua
@@ -810,7 +810,7 @@ describe('API: buffer events:', function()
local newlines = args[5]
-- Size of the contained nvim instance is 23 lines, this might change
- -- with the test setup. Note updates are continguous.
+ -- with the test setup. Note updates are contiguous.
assert(#newlines <= 23)
for i = 1,#newlines do
diff --git a/test/functional/api/command_spec.lua b/test/functional/api/command_spec.lua
index 890710b6e6..d0fb26edc7 100644
--- a/test/functional/api/command_spec.lua
+++ b/test/functional/api/command_spec.lua
@@ -114,8 +114,9 @@ describe('nvim_create_user_command', function()
]]
eq({
- args = [[this is a\ test]],
- fargs = {"this", "is", "a test"},
+ name = "CommandWithLuaCallback",
+ args = [[this\ is a\ test]],
+ fargs = {"this ", "is", "a test"},
bang = false,
line1 = 1,
line2 = 1,
@@ -125,6 +126,7 @@ describe('nvim_create_user_command', function()
confirm = false,
emsg_silent = false,
hide = false,
+ horizontal = false,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -135,7 +137,7 @@ describe('nvim_create_user_command', function()
sandbox = false,
silent = false,
split = "",
- tab = 0,
+ tab = -1,
unsilent = false,
verbose = -1,
vertical = false,
@@ -144,11 +146,12 @@ describe('nvim_create_user_command', function()
count = 2,
reg = "",
}, exec_lua [=[
- vim.api.nvim_command([[CommandWithLuaCallback this is a\ test]])
+ vim.api.nvim_command([[CommandWithLuaCallback this\ is a\ test]])
return result
]=])
eq({
+ name = "CommandWithLuaCallback",
args = [[this includes\ a backslash: \\]],
fargs = {"this", "includes a", "backslash:", "\\"},
bang = false,
@@ -160,6 +163,7 @@ describe('nvim_create_user_command', function()
confirm = false,
emsg_silent = false,
hide = false,
+ horizontal = false,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -170,7 +174,7 @@ describe('nvim_create_user_command', function()
sandbox = false,
silent = false,
split = "",
- tab = 0,
+ tab = -1,
unsilent = false,
verbose = -1,
vertical = false,
@@ -184,6 +188,7 @@ describe('nvim_create_user_command', function()
]=])
eq({
+ name = "CommandWithLuaCallback",
args = "a\\b",
fargs = {"a\\b"},
bang = false,
@@ -195,6 +200,7 @@ describe('nvim_create_user_command', function()
confirm = false,
emsg_silent = false,
hide = false,
+ horizontal = false,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -205,7 +211,7 @@ describe('nvim_create_user_command', function()
sandbox = false,
silent = false,
split = "",
- tab = 0,
+ tab = -1,
unsilent = false,
verbose = -1,
vertical = false,
@@ -219,17 +225,19 @@ describe('nvim_create_user_command', function()
]=])
eq({
+ name = "CommandWithLuaCallback",
args = 'h\tey ',
fargs = {[[h]], [[ey]]},
bang = true,
line1 = 10,
line2 = 10,
- mods = "confirm unsilent botright",
+ mods = "confirm unsilent botright horizontal",
smods = {
browse = false,
confirm = true,
emsg_silent = false,
hide = false,
+ horizontal = true,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -240,7 +248,7 @@ describe('nvim_create_user_command', function()
sandbox = false,
silent = false,
split = "botright",
- tab = 0,
+ tab = -1,
unsilent = true,
verbose = -1,
vertical = false,
@@ -249,11 +257,12 @@ describe('nvim_create_user_command', function()
count = 10,
reg = "",
}, exec_lua [=[
- vim.api.nvim_command('unsilent botright confirm 10CommandWithLuaCallback! h\tey ')
+ vim.api.nvim_command('unsilent horizontal botright confirm 10CommandWithLuaCallback! h\tey ')
return result
]=])
eq({
+ name = "CommandWithLuaCallback",
args = "h",
fargs = {"h"},
bang = false,
@@ -265,6 +274,7 @@ describe('nvim_create_user_command', function()
confirm = false,
emsg_silent = false,
hide = false,
+ horizontal = false,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -275,7 +285,7 @@ describe('nvim_create_user_command', function()
sandbox = false,
silent = false,
split = "",
- tab = 0,
+ tab = -1,
unsilent = false,
verbose = -1,
vertical = false,
@@ -289,6 +299,7 @@ describe('nvim_create_user_command', function()
]])
eq({
+ name = "CommandWithLuaCallback",
args = "",
fargs = {}, -- fargs works without args
bang = false,
@@ -300,6 +311,7 @@ describe('nvim_create_user_command', function()
confirm = false,
emsg_silent = false,
hide = false,
+ horizontal = false,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -310,7 +322,7 @@ describe('nvim_create_user_command', function()
sandbox = false,
silent = false,
split = "",
- tab = 0,
+ tab = -1,
unsilent = false,
verbose = -1,
vertical = false,
@@ -336,6 +348,7 @@ describe('nvim_create_user_command', function()
]]
eq({
+ name = "CommandWithOneOrNoArg",
args = "hello I'm one argument",
fargs = {"hello I'm one argument"}, -- Doesn't split args
bang = false,
@@ -347,6 +360,7 @@ describe('nvim_create_user_command', function()
confirm = false,
emsg_silent = false,
hide = false,
+ horizontal = false,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -357,7 +371,7 @@ describe('nvim_create_user_command', function()
sandbox = false,
silent = false,
split = "",
- tab = 0,
+ tab = -1,
unsilent = false,
verbose = -1,
vertical = false,
@@ -372,6 +386,7 @@ describe('nvim_create_user_command', function()
-- f-args is an empty table if no args were passed
eq({
+ name = "CommandWithOneOrNoArg",
args = "",
fargs = {},
bang = false,
@@ -383,6 +398,7 @@ describe('nvim_create_user_command', function()
confirm = false,
emsg_silent = false,
hide = false,
+ horizontal = false,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -393,7 +409,7 @@ describe('nvim_create_user_command', function()
sandbox = false,
silent = false,
split = "",
- tab = 0,
+ tab = -1,
unsilent = false,
verbose = -1,
vertical = false,
@@ -415,9 +431,11 @@ describe('nvim_create_user_command', function()
nargs = 0,
bang = true,
count = 2,
+ register = true,
})
]]
eq({
+ name = "CommandWithNoArgs",
args = "",
fargs = {},
bang = false,
@@ -429,6 +447,7 @@ describe('nvim_create_user_command', function()
confirm = false,
emsg_silent = false,
hide = false,
+ horizontal = false,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -439,7 +458,7 @@ describe('nvim_create_user_command', function()
sandbox = false,
silent = false,
split = "",
- tab = 0,
+ tab = -1,
unsilent = false,
verbose = -1,
vertical = false,
@@ -451,6 +470,43 @@ describe('nvim_create_user_command', function()
vim.cmd('CommandWithNoArgs')
return result
]])
+ -- register can be specified
+ eq({
+ name = "CommandWithNoArgs",
+ args = "",
+ fargs = {},
+ bang = false,
+ line1 = 1,
+ line2 = 1,
+ mods = "",
+ smods = {
+ browse = false,
+ confirm = false,
+ emsg_silent = false,
+ hide = false,
+ horizontal = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = false,
+ split = "",
+ tab = -1,
+ unsilent = false,
+ verbose = -1,
+ vertical = false,
+ },
+ range = 0,
+ count = 2,
+ reg = "+",
+ }, exec_lua [[
+ vim.cmd('CommandWithNoArgs +')
+ return result
+ ]])
end)
@@ -514,8 +570,29 @@ describe('nvim_create_user_command', function()
vim.api.nvim_cmd({ cmd = 'echo', args = { '&verbose' }, mods = opts.smods }, {})
end, {})
]]
-
eq("3", meths.cmd({ cmd = 'MyEcho', mods = { verbose = 3 } }, { output = true }))
+
+ eq(1, #meths.list_tabpages())
+ exec_lua[[
+ vim.api.nvim_create_user_command('MySplit', function(opts)
+ vim.api.nvim_cmd({ cmd = 'split', mods = opts.smods }, {})
+ end, {})
+ ]]
+ meths.cmd({ cmd = 'MySplit' }, {})
+ eq(1, #meths.list_tabpages())
+ eq(2, #meths.list_wins())
+ meths.cmd({ cmd = 'MySplit', mods = { tab = 1 } }, {})
+ eq(2, #meths.list_tabpages())
+ eq(2, funcs.tabpagenr())
+ meths.cmd({ cmd = 'MySplit', mods = { tab = 1 } }, {})
+ eq(3, #meths.list_tabpages())
+ eq(2, funcs.tabpagenr())
+ meths.cmd({ cmd = 'MySplit', mods = { tab = 3 } }, {})
+ eq(4, #meths.list_tabpages())
+ eq(4, funcs.tabpagenr())
+ meths.cmd({ cmd = 'MySplit', mods = { tab = 0 } }, {})
+ eq(5, #meths.list_tabpages())
+ eq(1, funcs.tabpagenr())
end)
end)
diff --git a/test/functional/api/extmark_spec.lua b/test/functional/api/extmark_spec.lua
index bc8d811c6d..00f5b25b8a 100644
--- a/test/functional/api/extmark_spec.lua
+++ b/test/functional/api/extmark_spec.lua
@@ -1046,7 +1046,7 @@ describe('API/extmarks', function()
check_undo_redo(ns, marks[3], 0, 4, 0, 8)
end)
- it('substitions over multiple lines with newline in pattern', function()
+ it('substitutes over multiple lines with newline in pattern', function()
feed('A<cr>67890<cr>xx<esc>')
set_extmark(ns, marks[1], 0, 3)
set_extmark(ns, marks[2], 0, 4)
@@ -1078,7 +1078,7 @@ describe('API/extmarks', function()
check_undo_redo(ns, marks[6], 1, 2, 0, 5)
end)
- it('substitions with multiple newlines in pattern', function()
+ it('substitutes with multiple newlines in pattern', function()
feed('A<cr>67890<cr>xx<esc>')
set_extmark(ns, marks[1], 0, 4)
set_extmark(ns, marks[2], 0, 5)
@@ -1093,7 +1093,7 @@ describe('API/extmarks', function()
check_undo_redo(ns, marks[5], 2, 0, 0, 6)
end)
- it('substitions over multiple lines with replace in substition', function()
+ it('substitutes over multiple lines with replace in substitution', function()
feed('A<cr>67890<cr>xx<esc>')
set_extmark(ns, marks[1], 0, 1)
set_extmark(ns, marks[2], 0, 2)
@@ -1111,7 +1111,7 @@ describe('API/extmarks', function()
eq({1, 3}, get_extmark_by_id(ns, marks[3]))
end)
- it('substitions over multiple lines with replace in substition', function()
+ it('substitutes over multiple lines with replace in substitution', function()
feed('A<cr>x3<cr>xx<esc>')
set_extmark(ns, marks[1], 1, 0)
set_extmark(ns, marks[2], 1, 1)
@@ -1122,7 +1122,7 @@ describe('API/extmarks', function()
check_undo_redo(ns, marks[3], 1, 2, 2, 0)
end)
- it('substitions over multiple lines with replace in substition', function()
+ it('substitutes over multiple lines with replace in substitution', function()
feed('A<cr>x3<cr>xx<esc>')
set_extmark(ns, marks[1], 0, 1)
set_extmark(ns, marks[2], 0, 2)
@@ -1140,7 +1140,7 @@ describe('API/extmarks', function()
check_undo_redo(ns, marks[3], 0, 4, 1, 3)
end)
- it('substitions with newline in match and sub, delta is 0', function()
+ it('substitutes with newline in match and sub, delta is 0', function()
feed('A<cr>67890<cr>xx<esc>')
set_extmark(ns, marks[1], 0, 3)
set_extmark(ns, marks[2], 0, 4)
@@ -1157,7 +1157,7 @@ describe('API/extmarks', function()
check_undo_redo(ns, marks[6], 2, 0, 2, 0)
end)
- it('substitions with newline in match and sub, delta > 0', function()
+ it('substitutes with newline in match and sub, delta > 0', function()
feed('A<cr>67890<cr>xx<esc>')
set_extmark(ns, marks[1], 0, 3)
set_extmark(ns, marks[2], 0, 4)
@@ -1174,7 +1174,7 @@ describe('API/extmarks', function()
check_undo_redo(ns, marks[6], 2, 0, 3, 0)
end)
- it('substitions with newline in match and sub, delta < 0', function()
+ it('substitutes with newline in match and sub, delta < 0', function()
feed('A<cr>67890<cr>xx<cr>xx<esc>')
set_extmark(ns, marks[1], 0, 3)
set_extmark(ns, marks[2], 0, 4)
@@ -1193,7 +1193,7 @@ describe('API/extmarks', function()
check_undo_redo(ns, marks[7], 3, 0, 2, 0)
end)
- it('substitions with backrefs, newline inserted into sub', function()
+ it('substitutes with backrefs, newline inserted into sub', function()
feed('A<cr>67890<cr>xx<cr>xx<esc>')
set_extmark(ns, marks[1], 0, 3)
set_extmark(ns, marks[2], 0, 4)
@@ -1210,7 +1210,7 @@ describe('API/extmarks', function()
check_undo_redo(ns, marks[6], 2, 0, 3, 0)
end)
- it('substitions a ^', function()
+ it('substitutes a ^', function()
set_extmark(ns, marks[1], 0, 0)
set_extmark(ns, marks[2], 0, 1)
feed([[:s:^:x<cr>]])
@@ -1397,7 +1397,7 @@ describe('API/extmarks', function()
eq({{id, 1, 0}}, bufmeths.get_extmarks(buf, ns, 0, -1, {}))
end)
- it('does not crash with append/delete/undo seqence', function()
+ it('does not crash with append/delete/undo sequence', function()
meths.exec([[
let ns = nvim_create_namespace('myplugin')
call nvim_buf_set_extmark(0, ns, 0, 0, {})
diff --git a/test/functional/api/highlight_spec.lua b/test/functional/api/highlight_spec.lua
index 2730f7e23d..5941d4c68b 100644
--- a/test/functional/api/highlight_spec.lua
+++ b/test/functional/api/highlight_spec.lua
@@ -11,6 +11,9 @@ local ok = helpers.ok
local assert_alive = helpers.assert_alive
describe('API: highlight',function()
+ clear()
+ Screen.new() -- initialize Screen.colors
+
local expected_rgb = {
background = Screen.colors.Yellow,
foreground = Screen.colors.Red,
@@ -29,13 +32,16 @@ describe('API: highlight',function()
italic = true,
reverse = true,
underline = true,
- undercurl = true,
- underdouble = true,
- underdotted = true,
- underdashed = true,
strikethrough = true,
+ altfont = true,
nocombine = true,
}
+ local expected_undercurl = {
+ background = Screen.colors.Yellow,
+ foreground = Screen.colors.Red,
+ special = Screen.colors.Blue,
+ undercurl = true,
+ }
before_each(function()
clear()
@@ -56,9 +62,13 @@ describe('API: highlight',function()
eq('Invalid highlight id: 30000', string.match(emsg, 'Invalid.*'))
-- Test all highlight properties.
- command('hi NewHighlight gui=underline,bold,undercurl,underdouble,underdotted,underdashed,italic,reverse,strikethrough,nocombine')
+ command('hi NewHighlight gui=underline,bold,italic,reverse,strikethrough,altfont,nocombine')
eq(expected_rgb2, nvim("get_hl_by_id", hl_id, true))
+ -- Test undercurl
+ command('hi NewHighlight gui=undercurl')
+ eq(expected_undercurl, nvim("get_hl_by_id", hl_id, true))
+
-- Test nil argument.
err, emsg = pcall(meths.get_hl_by_id, { nil }, false)
eq(false, err)
@@ -204,17 +214,14 @@ describe("API: set highlight", function()
bold = true,
italic = true,
reverse = true,
- undercurl = true,
underline = true,
- underdashed = true,
- underdotted = true,
- underdouble = true,
strikethrough = true,
+ altfont = true,
cterm = {
italic = true,
reverse = true,
- undercurl = true,
strikethrough = true,
+ altfont = true,
nocombine = true,
}
}
@@ -224,20 +231,17 @@ describe("API: set highlight", function()
bold = true,
italic = true,
reverse = true,
- undercurl = true,
underline = true,
- underdashed = true,
- underdotted = true,
- underdouble = true,
strikethrough = true,
+ altfont = true,
}
local highlight3_result_cterm = {
background = highlight_color.ctermbg,
foreground = highlight_color.ctermfg,
italic = true,
reverse = true,
- undercurl = true,
strikethrough = true,
+ altfont = true,
nocombine = true,
}
@@ -293,7 +297,7 @@ describe("API: set highlight", function()
exec_capture('highlight Test_hl'))
meths.set_hl(0, 'Test_hl2', highlight3_config)
- eq('Test_hl2 xxx cterm=undercurl,italic,reverse,strikethrough,nocombine ctermfg=8 ctermbg=15 gui=bold,underline,undercurl,underdouble,underdotted,underdashed,italic,reverse,strikethrough guifg=#ff0000 guibg=#0032aa',
+ eq('Test_hl2 xxx cterm=italic,reverse,strikethrough,altfont,nocombine ctermfg=8 ctermbg=15 gui=bold,underline,italic,reverse,strikethrough,altfont guifg=#ff0000 guibg=#0032aa',
exec_capture('highlight Test_hl2'))
-- Colors are stored with the name they are defined, but
@@ -354,4 +358,9 @@ describe("API: set highlight", function()
meths.set_hl(0, 'Normal', {fg='#000083', bg='#0000F3'})
eq({foreground = 131, background = 243}, nvim("get_hl_by_name", 'Normal', true))
end)
+
+ it('does not segfault on invalid group name #20009', function()
+ eq('Invalid highlight name: foo bar', pcall_err(meths.set_hl, 0, 'foo bar', {bold = true}))
+ assert_alive()
+ end)
end)
diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua
index a93a4544ff..5be4425162 100644
--- a/test/functional/api/keymap_spec.lua
+++ b/test/functional/api/keymap_spec.lua
@@ -6,6 +6,7 @@ local command = helpers.command
local curbufmeths = helpers.curbufmeths
local eq, neq = helpers.eq, helpers.neq
local exec_lua = helpers.exec_lua
+local exec = helpers.exec
local feed = helpers.feed
local funcs = helpers.funcs
local meths = helpers.meths
@@ -336,21 +337,26 @@ describe('nvim_get_keymap', function()
end)
it('can handle lua mappings', function()
- eq(0, exec_lua [[
+ eq(0, exec_lua([[
GlobalCount = 0
- vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
return GlobalCount
- ]])
+ ]]))
feed('asdf\n')
- eq(1, exec_lua[[return GlobalCount]])
+ eq(1, exec_lua([[return GlobalCount]]))
- eq(2, exec_lua[[
+ eq(2, exec_lua([[
vim.api.nvim_get_keymap('n')[1].callback()
return GlobalCount
+ ]]))
+
+ exec([[
+ call nvim_get_keymap('n')[0].callback()
]])
+ eq(3, exec_lua([[return GlobalCount]]))
+
local mapargs = meths.get_keymap('n')
- assert(type(mapargs[1].callback) == 'number', 'callback is not luaref number')
mapargs[1].callback = nil
eq({
lhs='asdf',
@@ -834,17 +840,29 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
end)
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 %d+>"))
+ eq(0, exec_lua([[
+ GlobalCount = 0
+ vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ return GlobalCount
+ ]]))
+
+ assert.truthy(string.match(funcs.maparg('asdf', 'n'), "^<Lua %d+>"))
+
local mapargs = funcs.maparg('asdf', 'n', false, true)
- assert(type(mapargs.callback) == 'number', 'callback is not luaref number')
mapargs.callback = nil
mapargs.lhsraw = nil
mapargs.lhsrawalt = nil
eq(generate_mapargs('n', 'asdf', nil, {sid=sid_lua}), mapargs)
+
+ eq(1, exec_lua([[
+ vim.fn.maparg('asdf', 'n', false, true).callback()
+ return GlobalCount
+ ]]))
+
+ exec([[
+ call maparg('asdf', 'n', v:false, v:true).callback()
+ ]])
+ eq(2, exec_lua([[return GlobalCount]]))
end)
it('can make lua expr mappings replacing keycodes', function()
@@ -867,7 +885,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
eq({'<space>'}, meths.buf_get_lines(0, 0, -1, false))
end)
- it('lua expr mapping returning nil is equivalent to returnig an empty string', function()
+ it('lua expr mapping returning nil is equivalent to returning an empty string', function()
exec_lua [[
vim.api.nvim_set_keymap ('i', 'aa', '', {callback = function() return nil end, expr = true })
]]
@@ -1048,7 +1066,7 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function()
eq({'rhs'}, bufmeths.get_lines(0, 0, 1, 1))
end)
- it("does not crash when setting keymap in a non-existing buffer #13541", function()
+ it("does not crash when setting mapping in a non-existing buffer #13541", function()
pcall_err(bufmeths.set_keymap, 100, '', 'lsh', 'irhs<Esc>', {})
helpers.assert_alive()
end)
diff --git a/test/functional/api/proc_spec.lua b/test/functional/api/proc_spec.lua
index 0fbf58a8e7..2028a8fba5 100644
--- a/test/functional/api/proc_spec.lua
+++ b/test/functional/api/proc_spec.lua
@@ -3,12 +3,12 @@ local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
local eq = helpers.eq
local funcs = helpers.funcs
-local iswin = helpers.iswin
local neq = helpers.neq
local nvim_argv = helpers.nvim_argv
local request = helpers.request
local retry = helpers.retry
local NIL = helpers.NIL
+local is_os = helpers.is_os
describe('API', function()
before_each(clear)
@@ -19,26 +19,26 @@ describe('API', function()
-- Might be non-zero already (left-over from some other test?),
-- but this is not what is tested here.
- local initial_childs = request('nvim_get_proc_children', this_pid)
+ local initial_children = request('nvim_get_proc_children', this_pid)
local job1 = funcs.jobstart(nvim_argv)
retry(nil, nil, function()
- eq(#initial_childs + 1, #request('nvim_get_proc_children', this_pid))
+ eq(#initial_children + 1, #request('nvim_get_proc_children', this_pid))
end)
local job2 = funcs.jobstart(nvim_argv)
retry(nil, nil, function()
- eq(#initial_childs + 2, #request('nvim_get_proc_children', this_pid))
+ eq(#initial_children + 2, #request('nvim_get_proc_children', this_pid))
end)
funcs.jobstop(job1)
retry(nil, nil, function()
- eq(#initial_childs + 1, #request('nvim_get_proc_children', this_pid))
+ eq(#initial_children + 1, #request('nvim_get_proc_children', this_pid))
end)
funcs.jobstop(job2)
retry(nil, nil, function()
- eq(#initial_childs, #request('nvim_get_proc_children', this_pid))
+ eq(#initial_children, #request('nvim_get_proc_children', this_pid))
end)
end)
@@ -62,7 +62,7 @@ describe('API', function()
it('returns process info', function()
local pid = funcs.getpid()
local pinfo = request('nvim_get_proc', pid)
- eq((iswin() and 'nvim.exe' or 'nvim'), pinfo.name)
+ eq((is_os('win') and 'nvim.exe' or 'nvim'), pinfo.name)
eq(pid, pinfo.pid)
eq('number', type(pinfo.ppid))
neq(pid, pinfo.ppid)
diff --git a/test/functional/api/server_notifications_spec.lua b/test/functional/api/server_notifications_spec.lua
index 1c00f001ff..53642858b2 100644
--- a/test/functional/api/server_notifications_spec.lua
+++ b/test/functional/api/server_notifications_spec.lua
@@ -1,12 +1,16 @@
local helpers = require('test.functional.helpers')(after_each)
+local assert_log = helpers.assert_log
local eq, clear, eval, command, nvim, next_msg =
helpers.eq, helpers.clear, helpers.eval, helpers.command, helpers.nvim,
helpers.next_msg
local meths = helpers.meths
local exec_lua = helpers.exec_lua
local retry = helpers.retry
-local isCI = helpers.isCI
+local is_ci = helpers.is_ci
local assert_alive = helpers.assert_alive
+local skip = helpers.skip
+
+local testlog = 'Xtest-server-notify-log'
describe('notify', function()
local channel
@@ -16,6 +20,10 @@ describe('notify', function()
channel = nvim('get_api_info')[1]
end)
+ after_each(function()
+ os.remove(testlog)
+ end)
+
describe('passing a valid channel id', function()
it('sends the notification/args to the corresponding channel', function()
eval('rpcnotify('..channel..', "test-event", 1, 2, 3)')
@@ -71,20 +79,18 @@ describe('notify', function()
end)
it('unsubscribe non-existing event #8745', function()
+ clear{env={
+ NVIM_LOG_FILE=testlog,
+ }}
nvim('subscribe', 'event1')
nvim('unsubscribe', 'doesnotexist')
+ assert_log("tried to unsubscribe unknown event 'doesnotexist'", testlog, 10)
nvim('unsubscribe', 'event1')
assert_alive()
end)
it('cancels stale events on channel close', function()
- if isCI() then
- pending('hangs on CI #14083 #15251')
- return
- elseif helpers.skip_fragile(pending) then
- return
- end
- if helpers.pending_win32(pending) then return end
+ skip(is_ci(), 'hangs on CI #14083 #15251')
local catchan = eval("jobstart(['cat'], {'rpc': v:true})")
local catpath = eval('exepath("cat")')
eq({id=catchan, argv={catpath}, stream='job', mode='rpc', client = {}}, exec_lua ([[
diff --git a/test/functional/api/server_requests_spec.lua b/test/functional/api/server_requests_spec.lua
index 00a4dd041d..ceff390dc5 100644
--- a/test/functional/api/server_requests_spec.lua
+++ b/test/functional/api/server_requests_spec.lua
@@ -250,7 +250,7 @@ describe('server -> client', function()
pcall(funcs.jobstop, jobid)
end)
- if helpers.pending_win32(pending) then return end
+ if helpers.skip(helpers.is_os('win')) then return end
it('rpc and text stderr can be combined', function()
local status, rv = pcall(funcs.rpcrequest, jobid, 'poll')
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index 72a03c409a..8fcdd9620b 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -1,6 +1,7 @@
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local lfs = require('lfs')
+local luv = require('luv')
local fmt = string.format
local assert_alive = helpers.assert_alive
@@ -11,9 +12,9 @@ local exec = helpers.exec
local eval = helpers.eval
local expect = helpers.expect
local funcs = helpers.funcs
-local iswin = helpers.iswin
local meths = helpers.meths
local matches = helpers.matches
+local pesc = helpers.pesc
local mkdir_p = helpers.mkdir_p
local ok, nvim_async, feed = helpers.ok, helpers.nvim_async, helpers.feed
local is_os = helpers.is_os
@@ -28,6 +29,7 @@ local exec_lua = helpers.exec_lua
local exc_exec = helpers.exc_exec
local insert = helpers.insert
local expect_exit = helpers.expect_exit
+local skip = helpers.skip
local pcall_err = helpers.pcall_err
local format_string = helpers.format_string
@@ -164,7 +166,7 @@ describe('API', function()
echo nvim_exec('echo Avast_ye_hades(''ahoy!'')', 1)
]], true))
- eq('Vim(call):E5555: API call: Vim(echo):E121: Undefined variable: s:pirate',
+ matches('Vim%(echo%):E121: Undefined variable: s:pirate$',
pcall_err(request, 'nvim_exec', [[
let s:pirate = 'script-scoped varrrrr'
call nvim_exec('echo s:pirate', 1)
@@ -206,12 +208,12 @@ describe('API', function()
end)
it('execution error', function()
- eq('Vim:E492: Not an editor command: bogus_command',
+ eq('nvim_exec(): Vim:E492: Not an editor command: bogus_command',
pcall_err(request, 'nvim_exec', 'bogus_command', false))
eq('', nvim('eval', 'v:errmsg')) -- v:errmsg was not updated.
eq('', eval('v:exception'))
- eq('Vim(buffer):E86: Buffer 23487 does not exist',
+ eq('nvim_exec(): Vim(buffer):E86: Buffer 23487 does not exist',
pcall_err(request, 'nvim_exec', 'buffer 23487', false))
eq('', eval('v:errmsg')) -- v:errmsg was not updated.
eq('', eval('v:exception'))
@@ -397,7 +399,7 @@ describe('API', function()
end)
it('returns shell |:!| output', function()
- local win_lf = iswin() and '\r' or ''
+ local win_lf = is_os('win') and '\r' or ''
eq(':!echo foo\r\n\nfoo'..win_lf..'\n', nvim('command_output', [[!echo foo]]))
end)
@@ -483,7 +485,7 @@ describe('API', function()
throw 'wtf'
endfunction
]])
- eq('wtf', pcall_err(request, 'nvim_call_function', 'Foo', {}))
+ eq('function Foo, line 1: wtf', pcall_err(request, 'nvim_call_function', 'Foo', {}))
eq('', eval('v:exception'))
eq('', eval('v:errmsg')) -- v:errmsg was not updated.
end)
@@ -602,10 +604,10 @@ describe('API', function()
eq([[Error loading lua: [string "<nvim>"]:0: unexpected symbol]],
pcall_err(meths.exec_lua, 'aa=bb\0', {}))
- eq([[Error executing lua: [string "<nvim>"]:0: attempt to call global 'bork' (a nil value)]],
+ eq([[attempt to call global 'bork' (a nil value)]],
pcall_err(meths.exec_lua, 'bork()', {}))
- eq('Error executing lua: [string "<nvim>"]:0: did\nthe\nfail',
+ eq('did\nthe\nfail',
pcall_err(meths.exec_lua, 'error("did\\nthe\\nfail")', {}))
end)
@@ -1106,6 +1108,14 @@ describe('API', function()
nvim('paste', 'a', true, -1)
eq('a', funcs.getcmdline())
end)
+ it('pasted text is saved in cmdline history when <CR> comes from mapping #20957', function()
+ command('cnoremap <CR> <CR>')
+ feed(':')
+ nvim('paste', 'echo', true, -1)
+ eq('', funcs.histget(':'))
+ feed('<CR>')
+ eq('echo', funcs.histget(':'))
+ end)
it('pasting with empty last chunk in Cmdline mode', function()
local screen = Screen.new(20, 4)
screen:attach()
@@ -1138,7 +1148,7 @@ describe('API', function()
end)
it('vim.paste() failure', function()
nvim('exec_lua', 'vim.paste = (function(lines, phase) error("fake fail") end)', {})
- eq([[Error executing lua: [string "<nvim>"]:0: fake fail]],
+ eq('fake fail',
pcall_err(request, 'nvim_paste', 'line 1\nline 2\nline 3', false, 1))
end)
end)
@@ -1797,9 +1807,11 @@ describe('API', function()
},
['jumps'] = eval(([[
- filter(map(getjumplist()[0], 'filter(
- { "f": expand("#".v:val.bufnr.":p"), "l": v:val.lnum },
- { k, v -> k != "l" || v != 1 })'), '!empty(v:val.f)')
+ filter(map(add(
+ getjumplist()[0], { 'bufnr': bufnr('%'), 'lnum': getcurpos()[1] }),
+ 'filter(
+ { "f": expand("#".v:val.bufnr.":p"), "l": v:val.lnum },
+ { k, v -> k != "l" || v != 1 })'), '!empty(v:val.f)')
]]):gsub('\n', '')),
['bufs'] = eval([[
@@ -1903,11 +1915,32 @@ describe('API', function()
end)
end)
+ describe('nvim_out_write', function()
+ it('prints long messages correctly #20534', function()
+ exec([[
+ set more
+ redir => g:out
+ silent! call nvim_out_write('a')
+ silent! call nvim_out_write('a')
+ silent! call nvim_out_write('a')
+ silent! call nvim_out_write("\n")
+ silent! call nvim_out_write('a')
+ silent! call nvim_out_write('a')
+ silent! call nvim_out_write(repeat('a', 5000) .. "\n")
+ silent! call nvim_out_write('a')
+ silent! call nvim_out_write('a')
+ silent! call nvim_out_write('a')
+ silent! call nvim_out_write("\n")
+ redir END
+ ]])
+ eq('\naaa\n' .. ('a'):rep(5002) .. '\naaa', meths.get_var('out'))
+ end)
+ end)
+
describe('nvim_err_write', function()
local screen
before_each(function()
- clear()
screen = Screen.new(40, 8)
screen:attach()
screen:set_default_attr_ids({
@@ -2093,7 +2126,7 @@ describe('API', function()
pty='?',
}
local event = meths.get_var("opened_event")
- if not iswin() then
+ if not is_os('win') then
info.pty = event.info.pty
neq(nil, string.match(info.pty, "^/dev/"))
end
@@ -2109,7 +2142,7 @@ describe('API', function()
stream = 'job',
id = 4,
argv = (
- iswin() and {
+ is_os('win') and {
eval('&shell'),
'/s',
'/c',
@@ -2131,7 +2164,7 @@ describe('API', function()
-- :terminal with args + stopped process.
eq(1, eval('jobstop(&channel)'))
eval('jobwait([&channel], 1000)') -- Wait.
- expected2.pty = (iswin() and '?' or '') -- pty stream was closed.
+ expected2.pty = (is_os('win') and '?' or '') -- pty stream was closed.
eq(expected2, eval('nvim_get_chan_info(&channel)'))
end)
end)
@@ -2254,7 +2287,7 @@ describe('API', function()
eq({'a', '', 'b'}, meths.list_runtime_paths())
meths.set_option('runtimepath', ',a,b')
eq({'', 'a', 'b'}, meths.list_runtime_paths())
- -- trailing , is ignored, use ,, if you really really want $CWD
+ -- Trailing "," is ignored. Use ",," if you really really want CWD.
meths.set_option('runtimepath', 'a,b,')
eq({'a', 'b'}, meths.list_runtime_paths())
meths.set_option('runtimepath', 'a,b,,')
@@ -2292,12 +2325,6 @@ describe('API', function()
meths.set_option('isident', '')
end)
- local it_maybe_pending = it
- if helpers.isCI() and os.getenv('CONFIGURATION') == 'MSVC_32' then
- -- For "works with &opt" (flaky on MSVC_32), but not easy to skip alone. #10241
- it_maybe_pending = pending
- end
-
local function simplify_east_api_node(line, east_api_node)
if east_api_node == NIL then
return nil
@@ -2494,7 +2521,7 @@ describe('API', function()
end
end
require('test.unit.viml.expressions.parser_tests')(
- it_maybe_pending, _check_parsing, hl, fmtn)
+ it, _check_parsing, hl, fmtn)
end)
describe('nvim_list_uis', function()
@@ -2698,7 +2725,7 @@ 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
+ skip(is_os('win'))
eq("Vim:E220: Missing }.", pcall_err(meths.get_runtime_file, "{", false))
@@ -2732,8 +2759,8 @@ describe('API', function()
it('should have information about window options', function()
eq({
- allows_duplicates = true,
- commalist = false;
+ allows_duplicates = false,
+ commalist = true;
default = "";
flaglist = false;
global_local = false;
@@ -3017,7 +3044,7 @@ describe('API', function()
meths.buf_set_mark(buf, 'F', 2, 2, {})
meths.buf_set_name(buf, "mybuf")
local mark = meths.get_mark('F', {})
- -- Compare the path tail ony
+ -- Compare the path tail only
assert(string.find(mark[4], "mybuf$"))
eq({2, 2, buf.id, mark[4]}, mark)
end)
@@ -3101,6 +3128,24 @@ describe('API', function()
eq('E539: Illegal character <}>',
pcall_err(meths.eval_statusline, '%{%}', {}))
end)
+ it('supports various items', function()
+ eq({ str = '0', width = 1 },
+ meths.eval_statusline('%l', { maxwidth = 5 }))
+ command('set readonly')
+ eq({ str = '[RO]', width = 4 },
+ meths.eval_statusline('%r', { maxwidth = 5 }))
+ local screen = Screen.new(80, 24)
+ screen:attach()
+ command('set showcmd')
+ feed('1234')
+ screen:expect({any = '1234'})
+ eq({ str = '1234', width = 4 },
+ meths.eval_statusline('%S', { maxwidth = 5 }))
+ feed('56')
+ screen:expect({any = '123456'})
+ eq({ str = '<3456', width = 5 },
+ meths.eval_statusline('%S', { maxwidth = 5 }))
+ end)
describe('highlight parsing', function()
it('works', function()
eq({
@@ -3168,6 +3213,17 @@ describe('API', function()
'TextWithNoHighlight%#WarningMsg#TextWithWarningHighlight',
{ use_winbar = true, highlights = true }))
end)
+ it('no memory leak with click functions', function()
+ meths.eval_statusline('%@ClickFunc@StatusLineStringWithClickFunc%T', {})
+ eq({
+ str = 'StatusLineStringWithClickFunc',
+ width = 29
+ },
+ meths.eval_statusline(
+ '%@ClickFunc@StatusLineStringWithClickFunc%T',
+ {})
+ )
+ end)
end)
end)
describe('nvim_parse_cmd', function()
@@ -3176,9 +3232,6 @@ describe('API', function()
cmd = 'echo',
args = { 'foo' },
bang = false,
- range = {},
- count = -1,
- reg = '',
addr = 'none',
magic = {
file = false,
@@ -3195,6 +3248,7 @@ describe('API', function()
force = false
},
hide = false,
+ horizontal = false,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -3205,7 +3259,7 @@ describe('API', function()
sandbox = false,
silent = false,
split = "",
- tab = 0,
+ tab = -1,
unsilent = false,
verbose = -1,
vertical = false,
@@ -3218,8 +3272,6 @@ describe('API', function()
args = { '/math.random/math.max/' },
bang = false,
range = { 4, 6 },
- count = -1,
- reg = '',
addr = 'line',
magic = {
file = false,
@@ -3236,6 +3288,7 @@ describe('API', function()
force = false
},
hide = false,
+ horizontal = false,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -3246,7 +3299,7 @@ describe('API', function()
sandbox = false,
silent = false,
split = "",
- tab = 0,
+ tab = -1,
unsilent = false,
verbose = -1,
vertical = false,
@@ -3260,7 +3313,6 @@ describe('API', function()
bang = false,
range = { 1 },
count = 1,
- reg = '',
addr = 'buf',
magic = {
file = false,
@@ -3277,6 +3329,7 @@ describe('API', function()
force = false
},
hide = false,
+ horizontal = false,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -3287,7 +3340,7 @@ describe('API', function()
sandbox = false,
silent = false,
split = "",
- tab = 0,
+ tab = -1,
unsilent = false,
verbose = -1,
vertical = false,
@@ -3300,7 +3353,6 @@ describe('API', function()
args = {},
bang = false,
range = {},
- count = -1,
reg = '+',
addr = 'line',
magic = {
@@ -3318,6 +3370,7 @@ describe('API', function()
force = false
},
hide = false,
+ horizontal = false,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -3328,12 +3381,51 @@ describe('API', function()
sandbox = false,
silent = false,
split = "",
- tab = 0,
+ tab = -1,
unsilent = false,
verbose = -1,
vertical = false,
}
}, meths.parse_cmd('put +', {}))
+ eq({
+ cmd = 'put',
+ args = {},
+ bang = false,
+ range = {},
+ 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,
+ horizontal = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = false,
+ split = "",
+ tab = -1,
+ unsilent = false,
+ verbose = -1,
+ vertical = false,
+ }
+ }, meths.parse_cmd('put', {}))
end)
it('works with range, count and register', function()
eq({
@@ -3359,6 +3451,7 @@ describe('API', function()
force = false
},
hide = false,
+ horizontal = false,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -3369,7 +3462,7 @@ describe('API', function()
sandbox = false,
silent = false,
split = "",
- tab = 0,
+ tab = -1,
unsilent = false,
verbose = -1,
vertical = false,
@@ -3382,8 +3475,6 @@ describe('API', function()
args = {},
bang = true,
range = {},
- count = -1,
- reg = '',
addr = 'line',
magic = {
file = true,
@@ -3400,6 +3491,7 @@ describe('API', function()
force = false
},
hide = false,
+ horizontal = false,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -3410,7 +3502,7 @@ describe('API', function()
sandbox = false,
silent = false,
split = "",
- tab = 0,
+ tab = -1,
unsilent = false,
verbose = -1,
vertical = false,
@@ -3423,8 +3515,6 @@ describe('API', function()
args = { 'foo.txt' },
bang = false,
range = {},
- count = -1,
- reg = '',
addr = '?',
magic = {
file = true,
@@ -3441,6 +3531,7 @@ describe('API', function()
force = false
},
hide = false,
+ horizontal = true,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -3451,19 +3542,17 @@ describe('API', function()
sandbox = false,
silent = true,
split = "topleft",
- tab = 2,
+ tab = 1,
unsilent = false,
verbose = 15,
vertical = false,
},
- }, meths.parse_cmd('15verbose silent! aboveleft topleft tab filter /foo/ split foo.txt', {}))
+ }, meths.parse_cmd('15verbose silent! horizontal topleft tab filter /foo/ split foo.txt', {}))
eq({
cmd = 'split',
args = { 'foo.txt' },
bang = false,
range = {},
- count = -1,
- reg = '',
addr = '?',
magic = {
file = true,
@@ -3480,6 +3569,7 @@ describe('API', function()
force = true
},
hide = false,
+ horizontal = false,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -3495,7 +3585,7 @@ describe('API', function()
verbose = 0,
vertical = false,
},
- }, meths.parse_cmd('0verbose unsilent botright confirm filter! /foo/ split foo.txt', {}))
+ }, meths.parse_cmd('0verbose unsilent botright 0tab confirm filter! /foo/ split foo.txt', {}))
end)
it('works with user commands', function()
command('command -bang -nargs=+ -range -addr=lines MyCommand echo foo')
@@ -3504,8 +3594,6 @@ describe('API', function()
args = { 'test', 'it' },
bang = true,
range = { 4, 6 },
- count = -1,
- reg = '',
addr = 'line',
magic = {
file = false,
@@ -3522,6 +3610,7 @@ describe('API', function()
force = false
},
hide = false,
+ horizontal = false,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -3532,7 +3621,7 @@ describe('API', function()
sandbox = false,
silent = false,
split = "",
- tab = 0,
+ tab = -1,
unsilent = false,
verbose = -1,
vertical = false,
@@ -3545,8 +3634,6 @@ describe('API', function()
args = { 'a.txt' },
bang = false,
range = {},
- count = -1,
- reg = '',
addr = 'arg',
magic = {
file = true,
@@ -3563,6 +3650,7 @@ describe('API', function()
force = false
},
hide = false,
+ horizontal = false,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -3573,7 +3661,7 @@ describe('API', function()
sandbox = false,
silent = false,
split = "",
- tab = 0,
+ tab = -1,
unsilent = false,
verbose = -1,
vertical = false,
@@ -3586,9 +3674,6 @@ describe('API', function()
cmd = 'MyCommand',
args = { 'test it' },
bang = false,
- range = {},
- count = -1,
- reg = '',
addr = 'none',
magic = {
file = false,
@@ -3605,6 +3690,7 @@ describe('API', function()
force = false
},
hide = false,
+ horizontal = false,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -3615,7 +3701,7 @@ describe('API', function()
sandbox = false,
silent = false,
split = "",
- tab = 0,
+ tab = -1,
unsilent = false,
verbose = -1,
vertical = false,
@@ -3668,6 +3754,59 @@ describe('API', function()
:^ |
]])
end)
+ it('does not move cursor or change search history/pattern #19878 #19890', function()
+ meths.buf_set_lines(0, 0, -1, true, {'foo', 'bar', 'foo', 'bar'})
+ eq({1, 0}, meths.win_get_cursor(0))
+ eq('', funcs.getreg('/'))
+ eq('', funcs.histget('search'))
+ feed(':') -- call the API in cmdline mode to test whether it changes search history
+ eq({
+ cmd = 'normal',
+ args = {'x'},
+ bang = true,
+ range = {3, 4},
+ addr = 'line',
+ magic = {
+ file = false,
+ bar = false,
+ },
+ nargs = '+',
+ nextcmd = '',
+ mods = {
+ browse = false,
+ confirm = false,
+ emsg_silent = false,
+ filter = {
+ pattern = "",
+ force = false,
+ },
+ hide = false,
+ horizontal = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = false,
+ split = "",
+ tab = -1,
+ unsilent = false,
+ verbose = -1,
+ vertical = false,
+ }
+ }, meths.parse_cmd('+2;/bar/normal! x', {}))
+ eq({1, 0}, meths.win_get_cursor(0))
+ eq('', funcs.getreg('/'))
+ eq('', funcs.histget('search'))
+ end)
+ it('result can be used directly by nvim_cmd #20051', function()
+ eq("foo", meths.cmd(meths.parse_cmd('echo "foo"', {}), { output = true }))
+ meths.cmd(meths.parse_cmd("set cursorline", {}), {})
+ eq(true, meths.get_option_value("cursorline", {}))
+ end)
end)
describe('nvim_cmd', function()
it('works', function ()
@@ -3745,15 +3884,28 @@ describe('API', function()
eq("", meths.cmd({ cmd = "Foo", bang = false }, { output = true }))
end)
it('works with modifiers', function()
- -- with :silent output is still captured
+ -- with silent = true output is still captured
eq('1',
meths.cmd({ cmd = 'echomsg', args = { '1' }, mods = { silent = true } },
{ output = true }))
- -- with :silent message isn't added to message history
+ -- but message isn't added to message history
eq('', meths.cmd({ cmd = 'messages' }, { output = true }))
+
meths.create_user_command("Foo", 'set verbose', {})
eq(" verbose=1", meths.cmd({ cmd = "Foo", mods = { verbose = 1 } }, { output = true }))
+
+ meths.create_user_command("Mods", "echo '<mods>'", {})
+ eq('keepmarks keeppatterns silent 3verbose aboveleft horizontal',
+ meths.cmd({ cmd = "Mods", mods = {
+ horizontal = true,
+ keepmarks = true,
+ keeppatterns = true,
+ silent = true,
+ split = 'aboveleft',
+ verbose = 3,
+ } }, { 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 } } },
@@ -3761,6 +3913,13 @@ describe('API', function()
eq(' 2 %a "bar.txt" line 1',
meths.cmd({ cmd = "buffers", mods = { filter = { pattern = "foo", force = true } } },
{ output = true }))
+
+ -- with emsg_silent = true error is suppresed
+ feed([[:lua vim.api.nvim_cmd({ cmd = 'call', mods = { emsg_silent = true } }, {})<CR>]])
+ eq('', meths.cmd({ cmd = 'messages' }, { output = true }))
+ -- error from the next command typed is not suppressed #21420
+ feed(':call<CR><CR>')
+ eq('E471: Argument required', meths.cmd({ cmd = 'messages' }, { output = true }))
end)
it('works with magic.file', function()
exec_lua([[
@@ -3841,11 +4000,23 @@ describe('API', function()
eq({'aa'}, meths.buf_get_lines(0, 0, 1, false))
assert_alive()
end)
+ it('supports filename expansion', function()
+ meths.cmd({ cmd = 'argadd', args = { '%:p:h:t', '%:p:h:t' } }, {})
+ local arg = funcs.expand('%:p:h:t')
+ eq({ arg, arg }, funcs.argv())
+ end)
it("'make' command works when argument count isn't 1 #19696", function()
command('set makeprg=echo')
- meths.cmd({ cmd = 'make' }, {})
+ command('set shellquote=')
+ matches('^:!echo ',
+ meths.cmd({ cmd = 'make' }, { output = true }))
assert_alive()
- meths.cmd({ cmd = 'make', args = { 'foo', 'bar' } }, {})
+ matches('^:!echo foo bar',
+ meths.cmd({ cmd = 'make', args = { 'foo', 'bar' } }, { output = true }))
+ assert_alive()
+ local arg_pesc = pesc(funcs.expand('%:p:h:t'))
+ matches(('^:!echo %s %s'):format(arg_pesc, arg_pesc),
+ meths.cmd({ cmd = 'make', args = { '%:p:h:t', '%:p:h:t' } }, { output = true }))
assert_alive()
end)
it('doesn\'t display messages when output=true', function()
@@ -3878,5 +4049,23 @@ describe('API', function()
15 |
]]}
end)
+ it('works with non-String args', function()
+ eq('2', meths.cmd({cmd = 'echo', args = {2}}, {output = true}))
+ eq('1', meths.cmd({cmd = 'echo', args = {true}}, {output = true}))
+ end)
+ describe('first argument as count', function()
+ before_each(clear)
+
+ it('works', function()
+ command('vsplit | enew')
+ meths.cmd({cmd = 'bdelete', args = {meths.get_current_buf()}}, {})
+ eq(1, meths.get_current_buf().id)
+ end)
+ it('works with :sleep using milliseconds', function()
+ local start = luv.now()
+ meths.cmd({cmd = 'sleep', args = {'100m'}}, {})
+ ok(luv.now() - start <= 300)
+ end)
+ end)
end)
end)
diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua
index 901d24327c..ecab6a4713 100644
--- a/test/functional/api/window_spec.lua
+++ b/test/functional/api/window_spec.lua
@@ -7,6 +7,7 @@ local clear, nvim, curbuf, curbuf_contents, window, curwin, eq, neq,
helpers.tabpage
local poke_eventloop = helpers.poke_eventloop
local curwinmeths = helpers.curwinmeths
+local exec = helpers.exec
local funcs = helpers.funcs
local request = helpers.request
local NIL = helpers.NIL
@@ -15,25 +16,6 @@ local command = helpers.command
local pcall_err = helpers.pcall_err
local assert_alive = helpers.assert_alive
--- check if str is visible at the beginning of some line
-local function is_visible(str)
- local slen = string.len(str)
- local nlines = eval("&lines")
- for i = 1,nlines do
- local iseq = true
- for j = 1,slen do
- if string.byte(str,j) ~= eval("screenchar("..i..","..j..")") then
- iseq = false
- break
- end
- end
- if iseq then
- return true
- end
- end
- return false
-end
-
describe('API/win', function()
before_each(clear)
@@ -79,27 +61,61 @@ describe('API/win', function()
end)
it('updates the screen, and also when the window is unfocused', function()
+ local screen = Screen.new(30, 9)
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue},
+ [2] = {bold = true, reverse = true};
+ [3] = {reverse = true};
+ })
+ screen:attach()
+
insert("prologue")
feed('100o<esc>')
insert("epilogue")
local win = curwin()
feed('gg')
- poke_eventloop() -- let nvim process the 'gg' command
+ screen:expect{grid=[[
+ ^prologue |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ ]]}
-- cursor position is at beginning
eq({1, 0}, window('get_cursor', win))
- eq(true, is_visible("prologue"))
- eq(false, is_visible("epilogue"))
-- move cursor to end
window('set_cursor', win, {101, 0})
- eq(false, is_visible("prologue"))
- eq(true, is_visible("epilogue"))
+ screen:expect{grid=[[
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ ^epilogue |
+ |
+ ]]}
-- move cursor to the beginning again
window('set_cursor', win, {1, 0})
- eq(true, is_visible("prologue"))
- eq(false, is_visible("epilogue"))
+ screen:expect{grid=[[
+ ^prologue |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ ]]}
-- move focus to new window
nvim('command',"new")
@@ -107,18 +123,45 @@ describe('API/win', function()
-- sanity check, cursor position is kept
eq({1, 0}, window('get_cursor', win))
- eq(true, is_visible("prologue"))
- eq(false, is_visible("epilogue"))
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {2:[No Name] }|
+ prologue |
+ |
+ |
+ {3:[No Name] [+] }|
+ |
+ ]]}
-- move cursor to end
window('set_cursor', win, {101, 0})
- eq(false, is_visible("prologue"))
- eq(true, is_visible("epilogue"))
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {2:[No Name] }|
+ |
+ |
+ epilogue |
+ {3:[No Name] [+] }|
+ |
+ ]]}
-- move cursor to the beginning again
window('set_cursor', win, {1, 0})
- eq(true, is_visible("prologue"))
- eq(false, is_visible("epilogue"))
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {2:[No Name] }|
+ prologue |
+ |
+ |
+ {3:[No Name] [+] }|
+ |
+ ]]}
-- curwin didn't change back
neq(win, curwin())
@@ -187,6 +230,46 @@ describe('API/win', function()
|
]])
end)
+
+ it('updates cursorcolumn 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}, -- CursorColumn
+ [3] = {bold = true, reverse = true}, -- StatusLine
+ [4] = {reverse = true}, -- StatusLineNC
+ })
+ screen:attach()
+ command('set cursorcolumn')
+ insert([[
+ aaa
+ bbb
+ ccc
+ ddd]])
+ local oldwin = curwin()
+ command('vsplit')
+ screen:expect([[
+ aa{2:a} │aa{2:a} |
+ bb{2:b} │bb{2:b} |
+ cc{2:c} │cc{2:c} |
+ dd^d │ddd |
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {3:[No Name] [+] }{4:[No Name] [+] }|
+ |
+ ]])
+ window('set_cursor', oldwin, {2, 0})
+ screen:expect([[
+ aa{2:a} │{2:a}aa |
+ bb{2:b} │bbb |
+ cc{2:c} │{2:c}cc |
+ dd^d │{2:d}dd |
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {3:[No Name] [+] }{4:[No Name] [+] }|
+ |
+ ]])
+ end)
end)
describe('{get,set}_height', function()
@@ -201,6 +284,22 @@ describe('API/win', function()
window('set_height', nvim('list_wins')[2], 2)
eq(2, window('get_height', nvim('list_wins')[2]))
end)
+
+ it('do not cause ml_get errors with foldmethod=expr #19989', function()
+ insert([[
+ aaaaa
+ bbbbb
+ ccccc]])
+ command('set foldmethod=expr')
+ exec([[
+ new
+ let w = nvim_get_current_win()
+ wincmd w
+ call nvim_win_set_height(w, 5)
+ ]])
+ feed('l')
+ eq('', meths.get_vvar('errmsg'))
+ end)
end)
describe('{get,set}_width', function()
@@ -215,6 +314,22 @@ describe('API/win', function()
window('set_width', nvim('list_wins')[2], 2)
eq(2, window('get_width', nvim('list_wins')[2]))
end)
+
+ it('do not cause ml_get errors with foldmethod=expr #19989', function()
+ insert([[
+ aaaaa
+ bbbbb
+ ccccc]])
+ command('set foldmethod=expr')
+ exec([[
+ vnew
+ let w = nvim_get_current_win()
+ wincmd w
+ call nvim_win_set_width(w, 5)
+ ]])
+ feed('l')
+ eq('', meths.get_vvar('errmsg'))
+ end)
end)
describe('{get,set,del}_var', function()
@@ -408,6 +523,8 @@ describe('API/win', function()
it('closing current (float) window of another tabpage #15313', function()
command('tabedit')
+ command('botright split')
+ local prevwin = curwin().id
eq(2, eval('tabpagenr()'))
local win = meths.open_win(0, true, {
relative='editor', row=10, col=10, width=50, height=10
@@ -417,7 +534,7 @@ describe('API/win', function()
eq(1, eval('tabpagenr()'))
meths.win_close(win, false)
- eq(1001, meths.tabpage_get_win(tab).id)
+ eq(prevwin, meths.tabpage_get_win(tab).id)
assert_alive()
end)
end)
diff --git a/test/functional/autocmd/autocmd_spec.lua b/test/functional/autocmd/autocmd_spec.lua
index 90254b7415..fb5bab445c 100644
--- a/test/functional/autocmd/autocmd_spec.lua
+++ b/test/functional/autocmd/autocmd_spec.lua
@@ -9,6 +9,7 @@ local neq = helpers.neq
local eval = helpers.eval
local feed = helpers.feed
local clear = helpers.clear
+local matches = helpers.matches
local meths = helpers.meths
local pcall_err = helpers.pcall_err
local funcs = helpers.funcs
@@ -424,17 +425,50 @@ describe('autocmd', function()
end)
it('gives E814 when there are no other floating windows', function()
- eq('Vim(close):E814: Cannot close window, only autocmd window would remain',
+ eq('BufAdd Autocommands for "Xa.txt": 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',
+ eq('BufAdd Autocommands for "Xa.txt": Vim(close):E814: Cannot close window, only autocmd window would remain',
pcall_err(command, 'doautoall BufAdd'))
end)
end)
+ it('closing `aucmd_win` using API gives E813', function()
+ exec_lua([[
+ vim.cmd('tabnew')
+ _G.buf = vim.api.nvim_create_buf(true, true)
+ ]])
+ matches('Vim:E813: Cannot close autocmd window$', pcall_err(exec_lua, [[
+ vim.api.nvim_buf_call(_G.buf, function()
+ local win = vim.api.nvim_get_current_win()
+ vim.api.nvim_win_close(win, true)
+ end)
+ ]]))
+ matches('Vim:E813: Cannot close autocmd window$', pcall_err(exec_lua, [[
+ vim.api.nvim_buf_call(_G.buf, function()
+ local win = vim.api.nvim_get_current_win()
+ vim.cmd('tabnext')
+ vim.api.nvim_win_close(win, true)
+ end)
+ ]]))
+ matches('Vim:E813: Cannot close autocmd window$', pcall_err(exec_lua, [[
+ vim.api.nvim_buf_call(_G.buf, function()
+ local win = vim.api.nvim_get_current_win()
+ vim.api.nvim_win_hide(win)
+ end)
+ ]]))
+ matches('Vim:E813: Cannot close autocmd window$', pcall_err(exec_lua, [[
+ vim.api.nvim_buf_call(_G.buf, function()
+ local win = vim.api.nvim_get_current_win()
+ vim.cmd('tabnext')
+ vim.api.nvim_win_hide(win)
+ end)
+ ]]))
+ end)
+
it(':doautocmd does not warn "No matching autocommands" #10689', function()
local screen = Screen.new(32, 3)
screen:attach()
@@ -476,14 +510,14 @@ describe('autocmd', function()
it('during RecordingLeave event', function()
command([[autocmd RecordingLeave * let v:event.regname = '']])
- eq('Vim(let):E46: Cannot change read-only variable "v:event.regname"',
+ eq('RecordingLeave Autocommands for "*": 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"',
+ eq('TermClose Autocommands for "*": Vim(let):E46: Cannot change read-only variable "v:event.status"',
pcall_err(command, 'bdelete!'))
end)
end)
diff --git a/test/functional/autocmd/cmdline_spec.lua b/test/functional/autocmd/cmdline_spec.lua
index 8ec06dc148..82fb9b9444 100644
--- a/test/functional/autocmd/cmdline_spec.lua
+++ b/test/functional/autocmd/cmdline_spec.lua
@@ -73,7 +73,7 @@ describe('cmdline autocommands', function()
{1:~ }|
{4: }|
: |
- {2:E5500: autocmd has thrown an exception: Vim(echoerr):FAIL} |
+ {2:CmdlineEnter Autocommands for "*": Vim(echoerr):FAIL} |
:^ |
]])
@@ -82,9 +82,9 @@ describe('cmdline autocommands', function()
|
{4: }|
: |
- {2:E5500: autocmd has thrown an exception: Vim(echoerr):FAIL} |
+ {2:CmdlineEnter Autocommands for "*": Vim(echoerr):FAIL} |
:put ='lorem ipsum' |
- {2:E5500: autocmd has thrown an exception: Vim(echoerr):very error} |
+ {2:CmdlineLeave Autocommands for "*": Vim(echoerr):very error} |
|
{3:Press ENTER or type command to continue}^ |
]])
@@ -111,9 +111,9 @@ describe('cmdline autocommands', function()
lorem ipsum |
{4: }|
: |
- {2:E5500: autocmd has thrown an exception: Vim(echoerr):FAIL} |
+ {2:CmdlineEnter Autocommands for "*": Vim(echoerr):FAIL} |
:put ='lorem ipsum' |
- {2:E5500: autocmd has thrown an exception: Vim(echoerr):change erreor} |
+ {2:CmdlineChanged Autocommands for "*": Vim(echoerr):change erreor} |
:put ='lorem ipsum'^ |
]])
@@ -123,9 +123,9 @@ describe('cmdline autocommands', function()
lorem ipsum |
{4: }|
: |
- {2:E5500: autocmd has thrown an exception: Vim(echoerr):FAIL} |
+ {2:CmdlineEnter Autocommands for "*": Vim(echoerr):FAIL} |
:put ='lorem ipsum' |
- {2:E5500: autocmd has thrown an exception: Vim(echoerr):change erreor} |
+ {2:CmdlineChanged Autocommands for "*": Vim(echoerr):change erreor} |
:put ='lorem ipsum^' |
]])
@@ -134,22 +134,22 @@ describe('cmdline autocommands', function()
screen:expect([[
{4: }|
: |
- {2:E5500: autocmd has thrown an exception: Vim(echoerr):FAIL} |
+ {2:CmdlineEnter Autocommands for "*": Vim(echoerr):FAIL} |
:put ='lorem ipsum' |
- {2:E5500: autocmd has thrown an exception: Vim(echoerr):change erreor} |
+ {2:CmdlineChanged Autocommands for "*": Vim(echoerr):change erreor} |
:put ='lorem ipsum.' |
- {2:E5500: autocmd has thrown an exception: Vim(echoerr):change erreor} |
+ {2:CmdlineChanged Autocommands for "*": Vim(echoerr):change erreor} |
:put ='lorem ipsum.^' |
]])
feed('<cr>')
screen:expect([[
:put ='lorem ipsum' |
- {2:E5500: autocmd has thrown an exception: Vim(echoerr):change erreor} |
+ {2:CmdlineChanged Autocommands for "*": Vim(echoerr):change erreor} |
:put ='lorem ipsum.' |
- {2:E5500: autocmd has thrown an exception: Vim(echoerr):change erreor} |
+ {2:CmdlineChanged Autocommands for "*": Vim(echoerr):change erreor} |
:put ='lorem ipsum.' |
- {2:E5500: autocmd has thrown an exception: Vim(echoerr):very error} |
+ {2:CmdlineLeave Autocommands for "*": Vim(echoerr):very error} |
|
{3:Press ENTER or type command to continue}^ |
]])
@@ -185,6 +185,14 @@ describe('cmdline autocommands', function()
eq({'notification', 'CmdlineLeave', {{cmdtype='=', cmdlevel=2, abort=false}}}, next_msg())
end)
+ it('no crash with recursive use of v:event #19484', function()
+ command('autocmd CmdlineEnter * normal :')
+ feed(':')
+ eq({'notification', 'CmdlineEnter', {{cmdtype=':', cmdlevel=1}}}, next_msg())
+ feed('<CR>')
+ eq({'notification', 'CmdlineLeave', {{cmdtype=':', cmdlevel=1, abort=false}}}, next_msg())
+ end)
+
it('supports CmdlineChanged' ,function()
command("autocmd CmdlineChanged * call rpcnotify(g:channel, 'CmdlineChanged', v:event, getcmdline())")
feed(':')
@@ -215,7 +223,6 @@ describe('cmdline autocommands', function()
eq({'notification', 'CmdlineChanged', {{cmdtype='=', cmdlevel=2}, "1+1"}}, next_msg())
feed('<cr>')
eq({'notification', 'CmdlineLeave', {{cmdtype='=', cmdlevel=2, abort=false}}}, next_msg())
- eq({'notification', 'CmdlineChanged', {{cmdtype=':', cmdlevel=1}, "let x = "}}, next_msg())
eq({'notification', 'CmdlineChanged', {{cmdtype=':', cmdlevel=1}, "let x = 2"}}, next_msg())
feed('<cr>')
eq({'notification', 'CmdlineLeave', {{cmdtype=':', cmdlevel=1, abort=false}}}, next_msg())
diff --git a/test/functional/autocmd/cursorhold_spec.lua b/test/functional/autocmd/cursorhold_spec.lua
index 506b688853..b04bd5233a 100644
--- a/test/functional/autocmd/cursorhold_spec.lua
+++ b/test/functional/autocmd/cursorhold_spec.lua
@@ -2,30 +2,82 @@ local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
local eq = helpers.eq
-local eval = helpers.eval
local feed = helpers.feed
local retry = helpers.retry
-local source = helpers.source
+local exec = helpers.source
local sleep = helpers.sleep
+local meths = helpers.meths
-describe('CursorHoldI', function()
- before_each(clear)
+before_each(clear)
+
+describe('CursorHold', function()
+ before_each(function()
+ exec([[
+ let g:cursorhold = 0
+ augroup test
+ au CursorHold * let g:cursorhold += 1
+ augroup END
+ ]])
+ end)
+
+ it('is triggered correctly #12587', function()
+ local function test_cursorhold(fn, early)
+ local ut = 2
+ -- if testing with small 'updatetime' fails, double its value and test again
+ retry(10, nil, function()
+ ut = ut * 2
+ meths.set_option('updatetime', ut)
+ feed('0') -- reset did_cursorhold
+ meths.set_var('cursorhold', 0)
+ sleep(ut / 4)
+ fn()
+ eq(0, meths.get_var('cursorhold'))
+ sleep(ut / 2)
+ fn()
+ eq(0, meths.get_var('cursorhold'))
+ sleep(ut / 2)
+ eq(early, meths.get_var('cursorhold'))
+ sleep(ut / 4 * 3)
+ eq(1, meths.get_var('cursorhold'))
+ end)
+ end
+ local ignore_key = meths.replace_termcodes('<Ignore>', true, true, true)
+ test_cursorhold(function() end, 1)
+ test_cursorhold(function() feed('') end, 1)
+ test_cursorhold(function() meths.feedkeys('', 'n', true) end, 1)
+ test_cursorhold(function() feed('<Ignore>') end, 0)
+ test_cursorhold(function() meths.feedkeys(ignore_key, 'n', true) end, 0)
+ end)
+
+ it("reducing 'updatetime' while waiting for CursorHold #20241", function()
+ meths.set_option('updatetime', 10000)
+ feed('0') -- reset did_cursorhold
+ meths.set_var('cursorhold', 0)
+ sleep(50)
+ eq(0, meths.get_var('cursorhold'))
+ meths.set_option('updatetime', 20)
+ sleep(10)
+ eq(1, meths.get_var('cursorhold'))
+ end)
+end)
+
+describe('CursorHoldI', function()
-- NOTE: since this test uses RPC it is not necessary to trigger the initial
-- issue (#3757) via timer's or RPC callbacks in the first place.
it('is triggered after input', function()
- source([[
- set updatetime=1
+ exec([[
+ set updatetime=1
- let g:cursorhold = 0
- augroup test
- au CursorHoldI * let g:cursorhold += 1
- augroup END
+ let g:cursorhold = 0
+ augroup test
+ au CursorHoldI * let g:cursorhold += 1
+ augroup END
]])
feed('ifoo')
retry(5, nil, function()
sleep(1)
- eq(1, eval('g:cursorhold'))
+ eq(1, meths.get_var('cursorhold'))
end)
end)
end)
diff --git a/test/functional/autocmd/dirchanged_spec.lua b/test/functional/autocmd/dirchanged_spec.lua
index 45dc06b39b..828cffa460 100644
--- a/test/functional/autocmd/dirchanged_spec.lua
+++ b/test/functional/autocmd/dirchanged_spec.lua
@@ -1,12 +1,12 @@
local lfs = require('lfs')
-local h = require('test.functional.helpers')(after_each)
+local helpers = require('test.functional.helpers')(after_each)
-local clear = h.clear
-local command = h.command
-local eq = h.eq
-local eval = h.eval
-local request = h.request
-local iswin = h.iswin
+local clear = helpers.clear
+local command = helpers.command
+local eq = helpers.eq
+local eval = helpers.eval
+local request = helpers.request
+local is_os = helpers.is_os
describe('autocmd DirChanged and DirChangedPre', function()
local curdir = string.gsub(lfs.currentdir(), '\\', '/')
@@ -21,8 +21,8 @@ describe('autocmd DirChanged and DirChangedPre', function()
curdir .. '\\XTEST-FUNCTIONAL-AUTOCMD-DIRCHANGED.DIR3',
}
- setup(function() for _, dir in pairs(dirs) do h.mkdir(dir) end end)
- teardown(function() for _, dir in pairs(dirs) do h.rmdir(dir) end end)
+ setup(function() for _, dir in pairs(dirs) do helpers.mkdir(dir) end end)
+ teardown(function() for _, dir in pairs(dirs) do helpers.rmdir(dir) end end)
before_each(function()
clear()
@@ -159,7 +159,7 @@ describe('autocmd DirChanged and DirChangedPre', function()
eq(1, eval('g:cdprecount'))
eq(1, eval('g:cdcount'))
- if iswin() then
+ if is_os('win') then
command('lcd '..win_dirs[1])
eq({}, eval('g:evpre'))
eq({}, eval('g:ev'))
@@ -182,7 +182,7 @@ describe('autocmd DirChanged and DirChangedPre', function()
eq(2, eval('g:cdprecount'))
eq(2, eval('g:cdcount'))
- if iswin() then
+ if is_os('win') then
command('tcd '..win_dirs[2])
eq({}, eval('g:evpre'))
eq({}, eval('g:ev'))
@@ -204,7 +204,7 @@ describe('autocmd DirChanged and DirChangedPre', function()
eq(3, eval('g:cdprecount'))
eq(3, eval('g:cdcount'))
- if iswin() then
+ if is_os('win') then
command('cd '..win_dirs[3])
eq({}, eval('g:evpre'))
eq({}, eval('g:ev'))
@@ -229,7 +229,7 @@ describe('autocmd DirChanged and DirChangedPre', function()
eq(4, eval('g:cdprecount'))
eq(4, eval('g:cdcount'))
- if iswin() then
+ if is_os('win') then
command('split '..win_dirs[1]..'/baz')
eq({}, eval('g:evpre'))
eq({}, eval('g:ev'))
@@ -278,7 +278,7 @@ describe('autocmd DirChanged and DirChangedPre', function()
eq(9, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
- if iswin() then
+ if is_os('win') 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
diff --git a/test/functional/autocmd/focus_spec.lua b/test/functional/autocmd/focus_spec.lua
index e3c9e1f9ee..d7a87e17ed 100644
--- a/test/functional/autocmd/focus_spec.lua
+++ b/test/functional/autocmd/focus_spec.lua
@@ -6,7 +6,7 @@ local nvim_prog = helpers.nvim_prog
local feed_command = helpers.feed_command
local feed_data = thelpers.feed_data
-if helpers.pending_win32(pending) then return end
+if helpers.skip(helpers.is_os('win')) then return end
describe('autoread TUI FocusGained/FocusLost', function()
local f1 = 'xtest-foo'
@@ -33,18 +33,37 @@ describe('autoread TUI FocusGained/FocusLost', function()
helpers.write_file(path, '')
lfs.touch(path, os.time() - 10)
- feed_command('edit '..path)
- feed_data('\027[O')
screen:expect{grid=[[
{1: } |
{4:~ }|
{4:~ }|
{4:~ }|
+ {5:[No Name] }|
+ |
+ {3:-- TERMINAL --} |
+ ]]}
+ feed_command('edit '..path)
+ screen:expect{grid=[[
+ {1: } |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
{5:xtest-foo }|
:edit xtest-foo |
{3:-- TERMINAL --} |
]]}
+ feed_data('\027[O')
+ feed_data('\027[O')
+ screen:expect{grid=[[
+ {1: } |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {5:xtest-foo }|
+ :edit xtest-foo |
+ {3:-- TERMINAL --} |
+ ]], unchanged=true}
helpers.write_file(path, expected_addition)
diff --git a/test/functional/autocmd/signal_spec.lua b/test/functional/autocmd/signal_spec.lua
index d4f65cc61d..738064090a 100644
--- a/test/functional/autocmd/signal_spec.lua
+++ b/test/functional/autocmd/signal_spec.lua
@@ -5,11 +5,10 @@ local command = helpers.command
local eq = helpers.eq
local funcs = helpers.funcs
local next_msg = helpers.next_msg
+local is_os = helpers.is_os
+local skip = helpers.skip
-if helpers.pending_win32(pending) then
- -- Only applies to POSIX systems.
- return
-end
+if skip(is_os('win'), 'Only applies to POSIX systems') then return end
local function posix_kill(signame, pid)
os.execute('kill -s '..signame..' -- '..pid..' >/dev/null')
diff --git a/test/functional/autocmd/termxx_spec.lua b/test/functional/autocmd/termxx_spec.lua
index 859c2ebf44..0a33f1b2ac 100644
--- a/test/functional/autocmd/termxx_spec.lua
+++ b/test/functional/autocmd/termxx_spec.lua
@@ -5,11 +5,13 @@ 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 matches = helpers.matches
local ok = helpers.ok
local feed = helpers.feed
local pcall_err = helpers.pcall_err
local assert_alive = helpers.assert_alive
-local iswin = helpers.iswin
+local skip = helpers.skip
+local is_os = helpers.is_os
describe('autocmd TermClose', function()
before_each(function()
@@ -22,7 +24,8 @@ describe('autocmd TermClose', function()
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!'))
+ matches('^TermClose Autocommands for "%*": Vim%(bdelete%):E937: Attempt to delete a buffer that is in use: term://',
+ pcall_err(command, 'bdelete!'))
assert_alive()
end
@@ -45,7 +48,7 @@ describe('autocmd TermClose', function()
end)
it('triggers when long-running terminal job gets stopped', function()
- nvim('set_option', 'shell', iswin() and 'cmd.exe' or 'sh')
+ nvim('set_option', 'shell', is_os('win') and 'cmd.exe' or 'sh')
command('autocmd TermClose * let g:test_termclose = 23')
command('terminal')
command('call jobstop(b:terminal_job_id)')
@@ -53,7 +56,7 @@ describe('autocmd TermClose', function()
end)
it('kills job trapping SIGTERM', function()
- if iswin() then return end
+ skip(is_os('win'))
nvim('set_option', 'shell', 'sh')
nvim('set_option', 'shellcmdflag', '-c')
command([[ let g:test_job = jobstart('trap "" TERM && echo 1 && sleep 60', { ]]
@@ -73,7 +76,7 @@ describe('autocmd TermClose', function()
end)
it('kills PTY job trapping SIGHUP and SIGTERM', function()
- if iswin() then return end
+ skip(is_os('win'))
nvim('set_option', 'shell', 'sh')
nvim('set_option', 'shellcmdflag', '-c')
command([[ let g:test_job = jobstart('trap "" HUP TERM && echo 1 && sleep 60', { ]]
diff --git a/test/functional/autocmd/win_scrolled_resized_spec.lua b/test/functional/autocmd/win_scrolled_resized_spec.lua
new file mode 100644
index 0000000000..4957f56dd4
--- /dev/null
+++ b/test/functional/autocmd/win_scrolled_resized_spec.lua
@@ -0,0 +1,329 @@
+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 exec = helpers.exec
+local command = helpers.command
+local feed = helpers.feed
+local meths = helpers.meths
+local assert_alive = helpers.assert_alive
+
+before_each(clear)
+
+describe('WinResized', function()
+ -- oldtest: Test_WinResized()
+ it('works', function()
+ exec([[
+ set scrolloff=0
+ call setline(1, ['111', '222'])
+ vnew
+ call setline(1, ['aaa', 'bbb'])
+ new
+ call setline(1, ['foo', 'bar'])
+
+ let g:resized = 0
+ au WinResized * let g:resized += 1
+ au WinResized * let g:v_event = deepcopy(v:event)
+ ]])
+ eq(0, eval('g:resized'))
+
+ -- increase window height, two windows will be reported
+ feed('<C-W>+')
+ eq(1, eval('g:resized'))
+ eq({windows = {1002, 1001}}, eval('g:v_event'))
+
+ -- increase window width, three windows will be reported
+ feed('<C-W>>')
+ eq(2, eval('g:resized'))
+ eq({windows = {1002, 1001, 1000}}, eval('g:v_event'))
+ end)
+end)
+
+describe('WinScrolled', function()
+ 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))
+ exec([[
+ 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>'))
+ au WinScrolled * let g:v_event = deepcopy(v:event)
+ ]])
+ 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()
+ 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'))
+ eq({
+ all = {leftcol = 0, topline = 1, topfill = 0, width = 0, height = 0, skipcol = 0},
+ ['1000'] = {leftcol = 0, topline = 1, topfill = 0, width = 0, height = 0, skipcol = 0},
+ }, eval('g:v_event'))
+
+ feed('<C-Y>')
+ eq(2, eval('g:scrolled'))
+ eq({
+ all = {leftcol = 0, topline = 1, topfill = 0, width = 0, height = 0, skipcol = 0},
+ ['1000'] = {leftcol = 0, topline = -1, topfill = 0, width = 0, height = 0, skipcol = 0},
+ }, eval('g:v_event'))
+ end)
+
+ it('is triggered by scrolling horizontally', function()
+ 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'))
+ eq({
+ all = {leftcol = 1, topline = 0, topfill = 0, width = 0, height = 0, skipcol = 0},
+ ['1000'] = {leftcol = 1, topline = 0, topfill = 0, width = 0, height = 0, skipcol = 0},
+ }, eval('g:v_event'))
+
+ feed('zh')
+ eq(2, eval('g:scrolled'))
+ eq({
+ all = {leftcol = 1, topline = 0, topfill = 0, width = 0, height = 0, skipcol = 0},
+ ['1000'] = {leftcol = -1, topline = 0, topfill = 0, width = 0, height = 0, skipcol = 0},
+ }, eval('g:v_event'))
+ end)
+
+ 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'))
+ eq({
+ all = {leftcol = 1, topline = 0, topfill = 0, width = 0, height = 0, skipcol = 0},
+ ['1000'] = {leftcol = 1, topline = 0, topfill = 0, width = 0, height = 0, skipcol = 0},
+ }, eval('g:v_event'))
+
+ feed('zl')
+ eq(2, eval('g:scrolled'))
+ eq({
+ all = {leftcol = 1, topline = 0, topfill = 0, width = 0, height = 0, skipcol = 0},
+ ['1000'] = {leftcol = 1, topline = 0, topfill = 0, width = 0, height = 0, skipcol = 0},
+ }, eval('g:v_event'))
+
+ feed('h')
+ eq(3, eval('g:scrolled'))
+ eq({
+ all = {leftcol = 1, topline = 0, topfill = 0, width = 0, height = 0, skipcol = 0},
+ ['1000'] = {leftcol = -1, topline = 0, topfill = 0, width = 0, height = 0, skipcol = 0},
+ }, eval('g:v_event'))
+
+ feed('zh')
+ eq(4, eval('g:scrolled'))
+ eq({
+ all = {leftcol = 1, topline = 0, topfill = 0, width = 0, height = 0, skipcol = 0},
+ ['1000'] = {leftcol = -1, topline = 0, topfill = 0, width = 0, height = 0, skipcol = 0},
+ }, eval('g:v_event'))
+ end)
+
+ -- oldtest: Test_WinScrolled_long_wrapped()
+ it('is triggered by scrolling on a long wrapped line #19968', function()
+ local height = meths.win_get_height(0)
+ local width = meths.win_get_width(0)
+ meths.buf_set_lines(0, 0, -1, true, {('foo'):rep(height * width)})
+ meths.win_set_cursor(0, {1, height * width - 1})
+ eq(0, eval('g:scrolled'))
+
+ feed('gj')
+ eq(1, eval('g:scrolled'))
+ eq({
+ all = {leftcol = 0, topline = 0, topfill = 0, width = 0, height = 0, skipcol = width},
+ ['1000'] = {leftcol = 0, topline = 0, topfill = 0, width = 0, height = 0, skipcol = width},
+ }, eval('g:v_event'))
+
+ feed('0')
+ eq(2, eval('g:scrolled'))
+ eq({
+ all = {leftcol = 0, topline = 0, topfill = 0, width = 0, height = 0, skipcol = width},
+ ['1000'] = {leftcol = 0, topline = 0, topfill = 0, width = 0, height = 0, skipcol = -width},
+ }, eval('g:v_event'))
+
+ feed('$')
+ eq(3, eval('g:scrolled'))
+ end)
+
+ 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('M')
+ eq(0, eval('g:scrolled'))
+
+ feed('i<C-X><C-E><Esc>')
+ eq(1, eval('g:scrolled'))
+ eq({
+ all = {leftcol = 0, topline = 1, topfill = 0, width = 0, height = 0, skipcol = 0},
+ ['1000'] = {leftcol = 0, topline = 1, topfill = 0, width = 0, height = 0, skipcol = 0},
+ }, eval('g:v_event'))
+
+ feed('i<C-X><C-Y><Esc>')
+ eq(2, eval('g:scrolled'))
+ eq({
+ all = {leftcol = 0, topline = 1, topfill = 0, width = 0, height = 0, skipcol = 0},
+ ['1000'] = {leftcol = 0, topline = -1, topfill = 0, width = 0, height = 0, skipcol = 0},
+ }, eval('g:v_event'))
+
+ feed('L')
+ eq(2, eval('g:scrolled'))
+
+ feed('A<CR><Esc>')
+ eq(3, eval('g:scrolled'))
+ eq({
+ all = {leftcol = 0, topline = 1, topfill = 0, width = 0, height = 0, skipcol = 0},
+ ['1000'] = {leftcol = 0, topline = 1, topfill = 0, width = 0, height = 0, skipcol = 0},
+ }, eval('g:v_event'))
+ end)
+end)
+
+describe('WinScrolled', function()
+ -- oldtest: Test_WinScrolled_mouse()
+ it('is triggered by mouse scrolling in another window', function()
+ local screen = Screen.new(75, 10)
+ screen:attach()
+ exec([[
+ set nowrap scrolloff=0
+ set mouse=a
+ call setline(1, ['foo']->repeat(32))
+ split
+ let g:scrolled = 0
+ au WinScrolled * let g:scrolled += 1
+ ]])
+ eq(0, eval('g:scrolled'))
+
+ -- With the upper split focused, send a scroll-down event to the unfocused one.
+ meths.input_mouse('wheel', 'down', '', 0, 6, 0)
+ eq(1, eval('g:scrolled'))
+
+ -- Again, but this time while we're in insert mode.
+ feed('i')
+ meths.input_mouse('wheel', 'down', '', 0, 6, 0)
+ feed('<Esc>')
+ eq(2, eval('g:scrolled'))
+ end)
+
+ -- oldtest: Test_WinScrolled_close_curwin()
+ it('closing window does not cause use-after-free #13265', function()
+ exec([[
+ set nowrap scrolloff=0
+ call setline(1, ['aaa', 'bbb'])
+ vsplit
+ au WinScrolled * close
+ ]])
+
+ -- This was using freed memory
+ feed('<C-E>')
+ assert_alive()
+ end)
+
+ -- oldtest: Test_WinScrolled_diff()
+ it('is triggered for both windows when scrolling in diff mode', function()
+ exec([[
+ set diffopt+=foldcolumn:0
+ call setline(1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'])
+ vnew
+ call setline(1, ['d', 'e', 'f', 'g', 'h', 'i'])
+ windo diffthis
+ au WinScrolled * let g:v_event = deepcopy(v:event)
+ ]])
+
+ feed('<C-E>')
+ eq({
+ all = {leftcol = 0, topline = 1, topfill = 1, width = 0, height = 0, skipcol = 0},
+ ['1000'] = {leftcol = 0, topline = 1, topfill = 0, width = 0, height = 0, skipcol = 0},
+ ['1001'] = {leftcol = 0, topline = 0, topfill = -1, width = 0, height = 0, skipcol = 0},
+ }, eval('g:v_event'))
+
+ feed('2<C-E>')
+ eq({
+ all = {leftcol = 0, topline = 2, topfill = 2, width = 0, height = 0, skipcol = 0},
+ ['1000'] = {leftcol = 0, topline = 2, topfill = 0, width = 0, height = 0, skipcol = 0},
+ ['1001'] = {leftcol = 0, topline = 0, topfill = -2, width = 0, height = 0, skipcol = 0},
+ }, eval('g:v_event'))
+
+ feed('<C-E>')
+ eq({
+ all = {leftcol = 0, topline = 2, topfill = 0, width = 0, height = 0, skipcol = 0},
+ ['1000'] = {leftcol = 0, topline = 1, topfill = 0, width = 0, height = 0, skipcol = 0},
+ ['1001'] = {leftcol = 0, topline = 1, topfill = 0, width = 0, height = 0, skipcol = 0},
+ }, eval('g:v_event'))
+
+ feed('2<C-Y>')
+ eq({
+ all = {leftcol = 0, topline = 3, topfill = 1, width = 0, height = 0, skipcol = 0},
+ ['1000'] = {leftcol = 0, topline = -2, topfill = 0, width = 0, height = 0, skipcol = 0},
+ ['1001'] = {leftcol = 0, topline = -1, topfill = 1, width = 0, height = 0, skipcol = 0},
+ }, eval('g:v_event'))
+ end)
+
+ it('is triggered by mouse scrolling in unfocused floating window #18222', function()
+ local screen = Screen.new(80, 24)
+ screen:attach()
+
+ exec([[
+ let g:scrolled = 0
+ autocmd WinScrolled * let g:scrolled += 1
+ autocmd WinScrolled * let g:amatch = expand('<amatch>')
+ autocmd WinScrolled * let g:v_event = deepcopy(v:event)
+ ]])
+ eq(0, eval('g:scrolled'))
+
+ local buf = meths.create_buf(true, true)
+ meths.buf_set_lines(buf, 0, -1, false, {'a', 'b', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n'})
+ local win = meths.open_win(buf, false, {
+ height = 5,
+ width = 10,
+ col = 0,
+ row = 1,
+ relative = 'editor',
+ style = 'minimal'
+ })
+ local winid_str = tostring(win.id)
+ -- WinScrolled should not be triggered when creating a new floating window
+ eq(0, eval('g:scrolled'))
+
+ meths.input_mouse('wheel', 'down', '', 0, 3, 3)
+ eq(1, eval('g:scrolled'))
+ eq(winid_str, eval('g:amatch'))
+ eq({
+ all = {leftcol = 0, topline = 3, topfill = 0, width = 0, height = 0, skipcol = 0},
+ [winid_str] = {leftcol = 0, topline = 3, topfill = 0, width = 0, height = 0, skipcol = 0},
+ }, eval('g:v_event'))
+
+ meths.input_mouse('wheel', 'up', '', 0, 3, 3)
+ eq(2, eval('g:scrolled'))
+ eq(tostring(win.id), eval('g:amatch'))
+ eq({
+ all = {leftcol = 0, topline = 3, topfill = 0, width = 0, height = 0, skipcol = 0},
+ [winid_str] = {leftcol = 0, topline = -3, topfill = 0, width = 0, height = 0, skipcol = 0},
+ }, eval('g:v_event'))
+ end)
+end)
diff --git a/test/functional/autocmd/winscrolled_spec.lua b/test/functional/autocmd/winscrolled_spec.lua
deleted file mode 100644
index 5c1b758961..0000000000
--- a/test/functional/autocmd/winscrolled_spec.lua
+++ /dev/null
@@ -1,85 +0,0 @@
-local helpers = require('test.functional.helpers')(after_each)
-
-local clear = helpers.clear
-local eq = helpers.eq
-local eval = helpers.eval
-local command = helpers.command
-local feed = helpers.feed
-local meths = helpers.meths
-local assert_alive = helpers.assert_alive
-
-before_each(clear)
-
-describe('WinScrolled', function()
- 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()
- 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()
- 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 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 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 ca52404d3b..8275575c24 100644
--- a/test/functional/core/channels_spec.lua
+++ b/test/functional/core/channels_spec.lua
@@ -1,5 +1,4 @@
local helpers = require('test.functional.helpers')(after_each)
-local uname = helpers.uname
local clear, eq, eval, next_msg, ok, source = helpers.clear, helpers.eq,
helpers.eval, helpers.next_msg, helpers.ok, helpers.source
local command, funcs, meths = helpers.command, helpers.funcs, helpers.meths
@@ -12,6 +11,7 @@ local retry = helpers.retry
local expect_twostreams = helpers.expect_twostreams
local assert_alive = helpers.assert_alive
local pcall_err = helpers.pcall_err
+local skip = helpers.skip
describe('channels', function()
local init = [[
@@ -145,7 +145,7 @@ describe('channels', function()
end
it('can use stdio channel with pty', function()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
source([[
let g:job_opts = {
\ 'on_stdout': function('OnEvent'),
@@ -178,8 +178,7 @@ describe('channels', function()
command("call chansend(id, 'incomplet\004')")
- local is_bsd = not not string.find(uname(), 'bsd')
- local bsdlike = is_bsd or is_os('mac')
+ local bsdlike = is_os('bsd') or is_os('mac')
local extra = bsdlike and "^D\008\008" or ""
expect_twoline(id, "stdout",
"incomplet"..extra, "[1, ['incomplet'], 'stdin']", true)
@@ -199,7 +198,7 @@ describe('channels', function()
it('stdio channel can use rpc and stderr simultaneously', function()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
source([[
let g:job_opts = {
\ 'on_stderr': function('OnEvent'),
diff --git a/test/functional/core/exit_spec.lua b/test/functional/core/exit_spec.lua
index 8cad7adfa6..05a69e1992 100644
--- a/test/functional/core/exit_spec.lua
+++ b/test/functional/core/exit_spec.lua
@@ -89,14 +89,14 @@ describe(':cquit', function()
end)
it('exits with redir msg for multiple exit codes after :cquit 1 2', function()
- test_cq('cquit 1 2', nil, 'Vim(cquit):E488: Trailing characters: 2: cquit 1 2')
+ test_cq('cquit 1 2', nil, 'nvim_exec(): Vim(cquit):E488: Trailing characters: 2: cquit 1 2')
end)
it('exits with redir msg for non-number exit code after :cquit X', function()
- test_cq('cquit X', nil, 'Vim(cquit):E488: Trailing characters: X: cquit X')
+ test_cq('cquit X', nil, 'nvim_exec(): Vim(cquit):E488: Trailing characters: X: cquit X')
end)
it('exits with redir msg for negative exit code after :cquit -1', function()
- test_cq('cquit -1', nil, 'Vim(cquit):E488: Trailing characters: -1: cquit -1')
+ test_cq('cquit -1', nil, 'nvim_exec(): Vim(cquit):E488: Trailing characters: -1: cquit -1')
end)
end)
diff --git a/test/functional/core/fileio_spec.lua b/test/functional/core/fileio_spec.lua
index a4d22685e8..4e9891a4de 100644
--- a/test/functional/core/fileio_spec.lua
+++ b/test/functional/core/fileio_spec.lua
@@ -1,3 +1,4 @@
+local lfs = require('lfs')
local helpers = require('test.functional.helpers')(after_each)
local assert_log = helpers.assert_log
@@ -5,6 +6,7 @@ local assert_nolog = helpers.assert_nolog
local clear = helpers.clear
local command = helpers.command
local eq = helpers.eq
+local neq = helpers.neq
local ok = helpers.ok
local feed = helpers.feed
local funcs = helpers.funcs
@@ -19,22 +21,28 @@ 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 check_close = helpers.check_close
local expect_exit = helpers.expect_exit
local write_file = helpers.write_file
+local Screen = require('test.functional.ui.screen')
+local feed_command = helpers.feed_command
+local skip = helpers.skip
+local is_os = helpers.is_os
+local is_ci = helpers.is_ci
describe('fileio', function()
before_each(function()
end)
after_each(function()
- expect_exit(command, ':qall!')
+ check_close()
os.remove('Xtest_startup_shada')
os.remove('Xtest_startup_file1')
os.remove('Xtest_startup_file1~')
os.remove('Xtest_startup_file2')
os.remove('Xtest_тест.md')
os.remove('Xtest-u8-int-max')
+ os.remove('Xtest-overwrite-forced')
rmdir('Xtest_startup_swapdir')
rmdir('Xtest_backupdir')
end)
@@ -83,6 +91,7 @@ describe('fileio', function()
end)
it('backup #9709', function()
+ skip(is_ci('cirrus'))
clear({ args={ '-i', 'Xtest_startup_shada',
'--cmd', 'set directory=Xtest_startup_swapdir' } })
@@ -102,6 +111,7 @@ describe('fileio', function()
end)
it('backup with full path #11214', function()
+ skip(is_ci('cirrus'))
clear()
mkdir('Xtest_backupdir')
command('set backup')
@@ -114,7 +124,7 @@ describe('fileio', function()
-- Backup filename = fullpath, separators replaced with "%".
local backup_file_name = string.gsub(currentdir()..'/Xtest_startup_file1',
- iswin() and '[:/\\]' or '/', '%%') .. '~'
+ is_os('win') and '[:/\\]' or '/', '%%') .. '~'
local foo_contents = trim(read_file('Xtest_backupdir/'..backup_file_name))
local foobar_contents = trim(read_file('Xtest_startup_file1'))
@@ -122,6 +132,53 @@ describe('fileio', function()
eq('foo', foo_contents);
end)
+ it('backup symlinked files #11349', function()
+ skip(is_ci('cirrus'))
+ clear()
+
+ local initial_content = 'foo'
+ local link_file_name = 'Xtest_startup_file2'
+ local backup_file_name = link_file_name .. '~'
+
+ write_file('Xtest_startup_file1', initial_content, false)
+ lfs.link('Xtest_startup_file1', link_file_name, true)
+ command('set backup')
+ command('set backupcopy=yes')
+ command('edit ' .. link_file_name)
+ feed('Abar<esc>')
+ command('write')
+
+ local backup_raw = read_file(backup_file_name)
+ neq(nil, backup_raw, "Expected backup file " .. backup_file_name .. "to exist but did not")
+ eq(initial_content, trim(backup_raw), 'Expected backup to contain original contents')
+ end)
+
+
+ it('backup symlinked files in first available backupdir #11349', function()
+ skip(is_ci('cirrus'))
+ clear()
+
+ local initial_content = 'foo'
+ local backup_dir = 'Xtest_backupdir'
+ local sep = helpers.get_pathsep()
+ local link_file_name = 'Xtest_startup_file2'
+ local backup_file_name = backup_dir .. sep .. link_file_name .. '~'
+
+ write_file('Xtest_startup_file1', initial_content, false)
+ lfs.link('Xtest_startup_file1', link_file_name, true)
+ mkdir(backup_dir)
+ command('set backup')
+ command('set backupcopy=yes')
+ command('set backupdir=.__this_does_not_exist__,' .. backup_dir)
+ command('edit ' .. link_file_name)
+ feed('Abar<esc>')
+ command('write')
+
+ local backup_raw = read_file(backup_file_name)
+ neq(nil, backup_raw, "Expected backup file " .. backup_file_name .. " to exist but did not")
+ eq(initial_content, trim(backup_raw), 'Expected backup to contain original contents')
+ end)
+
it('readfile() on multibyte filename #10586', function()
clear()
local text = {
@@ -144,6 +201,61 @@ describe('fileio', function()
command('edit ++enc=utf32 Xtest-u8-int-max')
assert_alive()
end)
+
+ it(':w! does not show "file has been changed" warning', function()
+ clear()
+ write_file("Xtest-overwrite-forced", 'foobar')
+ command('set nofixendofline')
+ local screen = Screen.new(40,4)
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ [2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ [3] = {bold = true, foreground = Screen.colors.SeaGreen4}
+ })
+ screen:attach()
+ command("set shortmess-=F")
+
+ command("e Xtest-overwrite-forced")
+ screen:expect([[
+ ^foobar |
+ {1:~ }|
+ {1:~ }|
+ "Xtest-overwrite-forced" [noeol] 1L, 6B |
+ ]])
+
+ -- Get current unix time.
+ local cur_unix_time = os.time(os.date("!*t"))
+ local future_time = cur_unix_time + 999999
+ -- Set the file's access/update time to be
+ -- greater than the time at which it was created.
+ local uv = require("luv")
+ uv.fs_utime('Xtest-overwrite-forced', future_time, future_time)
+ -- use async feed_command because nvim basically hangs on the prompt
+ feed_command("w")
+ screen:expect([[
+ {2:WARNING: The file has been changed since}|
+ {2: reading it!!!} |
+ {3:Do you really want to write to it (y/n)^?}|
+ |
+ ]])
+
+ feed("n")
+ feed("<cr>")
+ screen:expect([[
+ ^foobar |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ -- Use a screen test because the warning does not set v:errmsg.
+ command("w!")
+ screen:expect([[
+ ^foobar |
+ {1:~ }|
+ {1:~ }|
+ <erwrite-forced" [noeol] 1L, 6B written |
+ ]])
+ end)
end)
describe('tmpdir', function()
@@ -159,7 +271,7 @@ describe('tmpdir', function()
end)
after_each(function()
- expect_exit(command, ':qall!')
+ check_close()
os.remove(testlog)
end)
@@ -183,14 +295,10 @@ describe('tmpdir', function()
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)
+ assert_log('tempdir root not a directory', testlog, 100)
-- "…/nvim.<user>/" has wrong permissions:
- if iswin() then
- return -- TODO(justinmk): need setfperm/getfperm on Windows. #8244
- end
+ skip(is_os('win'), 'TODO(justinmk): need setfperm/getfperm on Windows. #8244')
os.remove(testlog)
os.remove(tmproot)
mkdir(tmproot)
@@ -198,9 +306,7 @@ describe('tmpdir', function()
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)
+ assert_log('tempdir root has invalid permissions', testlog, 100)
end)
it('too long', function()
diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua
index 02ff18bdda..1bae626b98 100644
--- a/test/functional/core/job_spec.lua
+++ b/test/functional/core/job_spec.lua
@@ -13,7 +13,6 @@ local retry = helpers.retry
local meths = helpers.meths
local NIL = helpers.NIL
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
@@ -23,6 +22,8 @@ local expect_msg_seq = helpers.expect_msg_seq
local pcall_err = helpers.pcall_err
local matches = helpers.matches
local Screen = require('test.functional.ui.screen')
+local skip = helpers.skip
+local is_os = helpers.is_os
describe('jobs', function()
local channel
@@ -55,7 +56,7 @@ describe('jobs', function()
it('must specify env option as a dict', function()
command("let g:job_opts.env = v:true")
local _, err = pcall(function()
- if iswin() then
+ if is_os('win') then
nvim('command', "let j = jobstart('set', g:job_opts)")
else
nvim('command', "let j = jobstart('env', g:job_opts)")
@@ -68,7 +69,7 @@ describe('jobs', function()
nvim('command', "let $VAR = 'abc'")
nvim('command', "let $TOTO = 'goodbye world'")
nvim('command', "let g:job_opts.env = {'TOTO': 'hello world'}")
- if iswin() then
+ if is_os('win') then
nvim('command', [[call jobstart('echo %TOTO% %VAR%', g:job_opts)]])
else
nvim('command', [[call jobstart('echo $TOTO $VAR', g:job_opts)]])
@@ -87,12 +88,12 @@ describe('jobs', function()
end)
it('append environment with pty #env', function()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
nvim('command', "let $VAR = 'abc'")
nvim('command', "let $TOTO = 'goodbye world'")
nvim('command', "let g:job_opts.pty = v:true")
nvim('command', "let g:job_opts.env = {'TOTO': 'hello world'}")
- if iswin() then
+ if is_os('win') then
nvim('command', [[call jobstart('echo %TOTO% %VAR%', g:job_opts)]])
else
nvim('command', [[call jobstart('echo $TOTO $VAR', g:job_opts)]])
@@ -122,7 +123,7 @@ describe('jobs', function()
--
-- Rather than expecting a completely empty environment, ensure that $VAR
-- is *not* in the environment but $TOTO is.
- if iswin() then
+ if is_os('win') then
nvim('command', [[call jobstart('echo %TOTO% %VAR%', g:job_opts)]])
expect_msg_seq({
{'notification', 'stdout', {0, {'hello world %VAR%', ''}}}
@@ -141,7 +142,7 @@ describe('jobs', function()
-- Since $Toto is being set in the job, it should take precedence over the
-- global $TOTO on Windows
nvim('command', "let g:job_opts = {'env': {'Toto': 'def'}, 'stdout_buffered': v:true}")
- if iswin() then
+ if is_os('win') then
nvim('command', [[let j = jobstart('set | find /I "toto="', g:job_opts)]])
else
nvim('command', [[let j = jobstart('env | grep -i toto=', g:job_opts)]])
@@ -150,7 +151,7 @@ describe('jobs', function()
nvim('command', "let g:output = Normalize(g:job_opts.stdout)")
local actual = eval('g:output')
local expected
- if iswin() then
+ if is_os('win') then
-- Toto is normalized to TOTO so we can detect duplicates, and because
-- Windows doesn't care about case
expected = {'TOTO=def', ''}
@@ -164,7 +165,7 @@ describe('jobs', function()
it('uses &shell and &shellcmdflag if passed a string', function()
nvim('command', "let $VAR = 'abc'")
- if iswin() then
+ if is_os('win') then
nvim('command', "let j = jobstart('echo %VAR%', g:job_opts)")
else
nvim('command', "let j = jobstart('echo $VAR', g:job_opts)")
@@ -176,7 +177,7 @@ describe('jobs', function()
it('changes to given / directory', function()
nvim('command', "let g:job_opts.cwd = '/'")
- if iswin() then
+ if is_os('win') then
nvim('command', "let j = jobstart('cd', g:job_opts)")
else
nvim('command', "let j = jobstart('pwd', g:job_opts)")
@@ -191,7 +192,7 @@ describe('jobs', function()
local dir = eval("resolve(tempname())"):gsub("/", get_pathsep())
mkdir(dir)
nvim('command', "let g:job_opts.cwd = '" .. dir .. "'")
- if iswin() then
+ if is_os('win') then
nvim('command', "let j = jobstart('cd', g:job_opts)")
else
nvim('command', "let j = jobstart('pwd', g:job_opts)")
@@ -215,7 +216,7 @@ describe('jobs', function()
local dir = eval('resolve(tempname())."-bogus"')
local _, err = pcall(function()
nvim('command', "let g:job_opts.cwd = '" .. dir .. "'")
- if iswin() then
+ if is_os('win') then
nvim('command', "let j = jobstart('cd', g:job_opts)")
else
nvim('command', "let j = jobstart('pwd', g:job_opts)")
@@ -225,7 +226,7 @@ describe('jobs', function()
end)
it('error on non-executable `cwd`', function()
- if iswin() then return end -- N/A for Windows
+ skip(is_os('win'), 'Not applicable for Windows')
local dir = 'Xtest_not_executable_dir'
mkdir(dir)
@@ -248,7 +249,7 @@ describe('jobs', function()
end
local executable_jobid = new_job()
- local exe = iswin() and './test/functional/fixtures' or './test/functional/fixtures/non_executable.txt'
+ local exe = is_os('win') and './test/functional/fixtures' or './test/functional/fixtures/non_executable.txt'
eq("Vim:E475: Invalid value for argument cmd: '"..exe.."' is not executable",
pcall_err(eval, "jobstart(['"..exe.."'])"))
eq("", eval("v:errmsg"))
@@ -702,7 +703,7 @@ describe('jobs', function()
describe('jobwait', function()
before_each(function()
- if iswin() then
+ if is_os('win') then
helpers.set_shell_powershell()
end
end)
@@ -786,7 +787,7 @@ describe('jobs', function()
feed_command('call rpcnotify(g:channel, "ready") | '..
'call rpcnotify(g:channel, "wait", '..
'jobwait([jobstart("'..
- (iswin() and 'Start-Sleep 10' or 'sleep 10')..
+ (is_os('win') and 'Start-Sleep 10' or 'sleep 10')..
'; exit 55")]))')
eq({'notification', 'ready', {}}, next_msg())
feed('<c-c>')
@@ -797,7 +798,7 @@ describe('jobs', function()
feed_command('call rpcnotify(g:channel, "ready") | '..
'call rpcnotify(g:channel, "wait", '..
'jobwait([jobstart("'..
- (iswin() and 'Start-Sleep 10' or 'sleep 10')..
+ (is_os('win') and 'Start-Sleep 10' or 'sleep 10')..
'; exit 55")], 10000))')
eq({'notification', 'ready', {}}, next_msg())
feed('<c-c>')
@@ -805,7 +806,7 @@ describe('jobs', function()
end)
it('can be called recursively', function()
- if helpers.pending_win32(pending) then return end -- TODO: Need `cat`.
+ skip(is_os('win'), "TODO: Need `cat`")
source([[
let g:opts = {}
let g:counter = 0
@@ -930,7 +931,7 @@ describe('jobs', function()
-- ..c.."', '-c', '"..c.."'])")
-- Create child with several descendants.
- if iswin() then
+ if is_os('win') then
source([[
function! s:formatprocs(pid, prefix)
let result = ''
@@ -979,13 +980,13 @@ describe('jobs', function()
endfunction
]])
end
- local sleep_cmd = (iswin()
+ local sleep_cmd = (is_os('win')
and 'ping -n 31 127.0.0.1'
or 'sleep 30')
local j = eval("jobstart('"..sleep_cmd..' | '..sleep_cmd..' | '..sleep_cmd.."')")
local ppid = funcs.jobpid(j)
local children
- if iswin() then
+ if is_os('win') then
local status, result = pcall(retry, nil, nil, function()
children = meths.get_proc_children(ppid)
-- On Windows conhost.exe may exist, and
@@ -1006,7 +1007,7 @@ describe('jobs', function()
-- Assert that nvim_get_proc() sees the children.
for _, child_pid in ipairs(children) do
local info = meths.get_proc(child_pid)
- -- eq((iswin() and 'nvim.exe' or 'nvim'), info.name)
+ -- eq((is_os('win') and 'nvim.exe' or 'nvim'), info.name)
eq(ppid, info.ppid)
end
-- Kill the root of the tree.
@@ -1027,7 +1028,7 @@ describe('jobs', function()
end)
describe('running tty-test program', function()
- if helpers.pending_win32(pending) then return end
+ if skip(is_os('win')) then return end
local function next_chunk()
local rv
while true do
@@ -1124,7 +1125,7 @@ describe("pty process teardown", function()
end)
it("does not prevent/delay exit. #4798 #4900", function()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
-- Use a nested nvim (in :term) to test without --headless.
feed_command(":terminal '"..helpers.nvim_prog
.."' -u NONE -i NONE --cmd '"..nvim_set.."' "
diff --git a/test/functional/core/log_spec.lua b/test/functional/core/log_spec.lua
index 3b1ccd9559..f682df4155 100644
--- a/test/functional/core/log_spec.lua
+++ b/test/functional/core/log_spec.lua
@@ -6,7 +6,6 @@ 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'
@@ -40,9 +39,7 @@ describe('log', function()
}})
local tid = _G._nvim_test_id
- retry(nil, 1000, function()
- assert_log(tid..'%.%d+%.%d +server_init:%d+: test log message', testlog, 100)
- end)
+ assert_log(tid..'%.%d+%.%d +server_init:%d+: test log message', testlog, 100)
exec_lua([[
local j1 = vim.fn.jobstart({ vim.v.progpath, '-es', '-V1', '+foochild', '+qa!' }, vim.empty_dict())
@@ -50,8 +47,6 @@ describe('log', function()
]])
-- 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)
+ assert_log('%.%d+%.%d/c +server_init:%d+: test log message', testlog, 100)
end)
end)
diff --git a/test/functional/core/main_spec.lua b/test/functional/core/main_spec.lua
index f6fb859ccc..ab11e14a67 100644
--- a/test/functional/core/main_spec.lua
+++ b/test/functional/core/main_spec.lua
@@ -9,6 +9,8 @@ local clear = helpers.clear
local funcs = helpers.funcs
local nvim_prog_abs = helpers.nvim_prog_abs
local write_file = helpers.write_file
+local is_os = helpers.is_os
+local skip = helpers.skip
describe('Command-line option', function()
describe('-s', function()
@@ -49,12 +51,12 @@ describe('Command-line option', function()
eq(#('100500\n'), attrs.size)
end)
it('does not crash after reading from stdin in non-headless mode', function()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
local screen = Screen.new(40, 8)
screen:attach()
local args = {
nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE',
- '--cmd', 'set noswapfile shortmess+=IFW fileformats=unix',
+ '--cmd', '"set noswapfile shortmess+=IFW fileformats=unix"',
'-s', '-'
}
diff --git a/test/functional/core/path_spec.lua b/test/functional/core/path_spec.lua
index 669bc99136..a786887bbd 100644
--- a/test/functional/core/path_spec.lua
+++ b/test/functional/core/path_spec.lua
@@ -3,14 +3,16 @@ local clear = helpers.clear
local eq = helpers.eq
local eval = helpers.eval
local command = helpers.command
-local iswin = helpers.iswin
+local insert = helpers.insert
+local feed = helpers.feed
+local is_os = helpers.is_os
describe('path collapse', function()
local targetdir
local expected_path
local function join_path(...)
- local pathsep = (iswin() and '\\' or '/')
+ local pathsep = (is_os('win') and '\\' or '/')
return table.concat({...}, pathsep)
end
@@ -54,3 +56,15 @@ describe('path collapse', function()
eq(expected_path, eval('expand("%:p")'))
end)
end)
+
+describe('file search', function()
+ before_each(clear)
+
+ it('find multibyte file name in line #20517', function()
+ command('cd test/functional/fixtures')
+ insert('filename_with_unicode_ααα')
+ eq('', eval('expand("%")'))
+ feed('gf')
+ eq('filename_with_unicode_ααα', eval('expand("%:t")'))
+ end)
+end)
diff --git a/test/functional/core/remote_spec.lua b/test/functional/core/remote_spec.lua
index d7bd075eb2..846d79abf3 100644
--- a/test/functional/core/remote_spec.lua
+++ b/test/functional/core/remote_spec.lua
@@ -3,6 +3,7 @@ local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
local command = helpers.command
local eq = helpers.eq
+local exec_lua = helpers.exec_lua
local expect = helpers.expect
local funcs = helpers.funcs
local insert = helpers.insert
@@ -48,8 +49,8 @@ describe('Remote', function()
-- 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}))
+ -- Call jobstart() and jobwait() in the same RPC request to reduce flakiness.
+ eq({ 0 }, exec_lua([[return vim.fn.jobwait({ vim.fn.jobstart(...) })]], client_argv))
client_starter:close()
set_session(server)
end
@@ -121,8 +122,8 @@ describe('Remote', function()
-- 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}))
+ -- Call jobstart() and jobwait() in the same RPC request to reduce flakiness.
+ eq({ 2 }, exec_lua([[return vim.fn.jobwait({ vim.fn.jobstart(...) })]], bogus_argv))
end
it('bogus subcommand', function()
run_and_check_exit_code('--remote-bogus')
diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua
index 4f9df4010e..1be5de6488 100644
--- a/test/functional/core/startup_spec.lua
+++ b/test/functional/core/startup_spec.lua
@@ -12,6 +12,7 @@ local eval = helpers.eval
local exec_lua = helpers.exec_lua
local feed = helpers.feed
local funcs = helpers.funcs
+local pesc = helpers.pesc
local mkdir = helpers.mkdir
local mkdir_p = helpers.mkdir_p
local nvim_prog = helpers.nvim_prog
@@ -20,11 +21,12 @@ local read_file = helpers.read_file
local retry = helpers.retry
local rmdir = helpers.rmdir
local sleep = helpers.sleep
-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 is_os = helpers.is_os
+local dedent = helpers.dedent
local testfile = 'Xtest_startuptime'
after_each(function()
@@ -41,10 +43,36 @@ describe('startup', function()
it('--startuptime', function()
clear({ args = {'--startuptime', testfile}})
- retry(nil, 1000, function()
- assert_log('sourcing', testfile, 100)
- assert_log("require%('vim%._editor'%)", testfile, 100)
- end)
+ assert_log('sourcing', testfile, 100)
+ assert_log("require%('vim%._editor'%)", testfile, 100)
+ end)
+
+ it('-D does not hang #12647', function()
+ clear()
+ local screen
+ 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. |
+ nvim_exec() |
+ cmd: aunmenu * |
+ > |
+ |
+ ]])
+ command([[call chansend(g:id, "cont\n")]])
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ [No Name] |
+ |
+ |
+ ]])
end)
end)
@@ -57,6 +85,124 @@ describe('startup', function()
os.remove('Xtest_startup_ttyout')
end)
+ describe('-l Lua', function()
+ local function assert_l_out(expected, nvim_args, lua_args, script, input)
+ local args = { nvim_prog }
+ vim.list_extend(args, nvim_args or {})
+ vim.list_extend(args, { '-l', (script or 'test/functional/fixtures/startup.lua') })
+ vim.list_extend(args, lua_args or {})
+ local out = funcs.system(args, input):gsub('\r\n', '\n')
+ return eq(dedent(expected), out)
+ end
+
+ it('failure modes', function()
+ -- nvim -l <empty>
+ matches('nvim%.?e?x?e?: Argument missing after: "%-l"', funcs.system({ nvim_prog, '-l' }))
+ eq(1, eval('v:shell_error'))
+ end)
+
+ it('os.exit() sets Nvim exitcode', function()
+ -- nvim -l foo.lua -arg1 -- a b c
+ assert_l_out([[
+ bufs:
+ nvim args: 7
+ lua args: { "-arg1", "--exitcode", "73", "--arg2",
+ [0] = "test/functional/fixtures/startup.lua"
+ }]],
+ {},
+ { '-arg1', "--exitcode", "73", '--arg2' }
+ )
+ eq(73, eval('v:shell_error'))
+ end)
+
+ it('Lua-error sets Nvim exitcode', function()
+ eq(0, eval('v:shell_error'))
+ matches('E5113: .* my pearls!!',
+ funcs.system({ nvim_prog, '-l', 'test/functional/fixtures/startup-fail.lua' }))
+ eq(1, eval('v:shell_error'))
+ matches('E5113: .* %[string "error%("whoa"%)"%]:1: whoa',
+ funcs.system({ nvim_prog, '-l', '-' }, 'error("whoa")'))
+ eq(1, eval('v:shell_error'))
+ end)
+
+ it('executes stdin "-"', function()
+ assert_l_out('arg0=- args=2 whoa',
+ nil,
+ { 'arg1', 'arg 2' },
+ '-',
+ "print(('arg0=%s args=%d %s'):format(_G.arg[0], #_G.arg, 'whoa'))")
+ assert_l_out('biiig input: 1000042',
+ nil,
+ nil,
+ '-',
+ ('print("biiig input: "..("%s"):len())'):format(string.rep('x', (1000 * 1000) + 42)))
+ eq(0, eval('v:shell_error'))
+ end)
+
+ it('sets _G.arg', function()
+ -- nvim -l foo.lua [args]
+ assert_l_out([[
+ bufs:
+ nvim args: 7
+ lua args: { "-arg1", "--arg2", "--", "arg3",
+ [0] = "test/functional/fixtures/startup.lua"
+ }]],
+ {},
+ { '-arg1', '--arg2', '--', 'arg3' }
+ )
+ eq(0, eval('v:shell_error'))
+
+ -- nvim file1 file2 -l foo.lua -arg1 -- file3 file4
+ assert_l_out([[
+ bufs: file1 file2
+ nvim args: 10
+ lua args: { "-arg1", "arg 2", "--", "file3", "file4",
+ [0] = "test/functional/fixtures/startup.lua"
+ }]],
+ { 'file1', 'file2', },
+ { '-arg1', 'arg 2', '--', 'file3', 'file4' }
+ )
+ eq(0, eval('v:shell_error'))
+
+ -- nvim -l foo.lua <vim args>
+ assert_l_out([[
+ bufs:
+ nvim args: 5
+ lua args: { "-c", "set wrap?",
+ [0] = "test/functional/fixtures/startup.lua"
+ }]],
+ {},
+ { '-c', 'set wrap?' }
+ )
+ eq(0, eval('v:shell_error'))
+
+ -- nvim <vim args> -l foo.lua <vim args>
+ assert_l_out(
+ -- luacheck: ignore 611 (Line contains only whitespaces)
+ [[
+ wrap
+
+ bufs:
+ nvim args: 7
+ lua args: { "-c", "set wrap?",
+ [0] = "test/functional/fixtures/startup.lua"
+ }]],
+ { '-c', 'set wrap?' },
+ { '-c', 'set wrap?' }
+ )
+ eq(0, eval('v:shell_error'))
+ end)
+
+ it('disables swapfile/shada/config/plugins', function()
+ assert_l_out('updatecount=0 shadafile=NONE loadplugins=false scriptnames=1',
+ nil,
+ nil,
+ '-',
+ [[print(('updatecount=%d shadafile=%s loadplugins=%s scriptnames=%d'):format(
+ vim.o.updatecount, vim.o.shadafile, tostring(vim.o.loadplugins), math.max(1, #vim.fn.split(vim.fn.execute('scriptnames'),'\n'))))]])
+ end)
+ end)
+
it('pipe at both ends: has("ttyin")==0 has("ttyout")==0', function()
-- system() puts a pipe at both ends.
local out = funcs.system({ nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless',
@@ -65,6 +211,7 @@ describe('startup', function()
'+q' })
eq('0 0', out)
end)
+
it('with --embed: has("ttyin")==0 has("ttyout")==0', function()
local screen = Screen.new(25, 3)
-- Remote UI connected by --embed.
@@ -76,10 +223,11 @@ describe('startup', function()
0 0 |
]])
end)
+
it('in a TTY: has("ttyin")==1 has("ttyout")==1', function()
local screen = Screen.new(25, 4)
screen:attach()
- if iswin() then
+ if is_os('win') then
command([[set shellcmdflag=/s\ /c shellxquote=\"]])
end
-- Running in :terminal
@@ -94,8 +242,9 @@ describe('startup', function()
|
]])
end)
+
it('output to pipe: has("ttyin")==1 has("ttyout")==0', function()
- if iswin() then
+ if is_os('win') then
command([[set shellcmdflag=/s\ /c shellxquote=\"]])
end
-- Running in :terminal
@@ -110,8 +259,9 @@ describe('startup', function()
read_file('Xtest_startup_ttyout'))
end)
end)
+
it('input from pipe: has("ttyin")==0 has("ttyout")==1', function()
- if iswin() then
+ if is_os('win') then
command([[set shellcmdflag=/s\ /c shellxquote=\"]])
end
-- Running in :terminal
@@ -127,10 +277,11 @@ describe('startup', function()
read_file('Xtest_startup_ttyout'))
end)
end)
+
it('input from pipe (implicit) #7679', function()
local screen = Screen.new(25, 4)
screen:attach()
- if iswin() then
+ if is_os('win') then
command([[set shellcmdflag=/s\ /c shellxquote=\"]])
end
-- Running in :terminal
@@ -146,6 +297,7 @@ describe('startup', function()
|
]])
end)
+
it('input from pipe + file args #7679', function()
eq('ohyeah\r\n0 0 bufs=3',
funcs.system({nvim_prog, '-n', '-u', 'NONE', '-i', 'NONE', '--headless',
@@ -237,11 +389,11 @@ describe('startup', function()
it('-es/-Es disables swapfile, user config #8540', function()
for _,arg in ipairs({'-es', '-Es'}) do
local out = funcs.system({nvim_prog, arg,
- '+set swapfile? updatecount? shada?',
+ '+set swapfile? updatecount? shadafile?',
"+put =execute('scriptnames')", '+%print'})
local line1 = string.match(out, '^.-\n')
-- updatecount=0 means swapfile was disabled.
- eq(" swapfile updatecount=0 shada=!,'100,<50,s10,h\n", line1)
+ eq(" swapfile updatecount=0 shadafile=\n", line1)
-- Standard plugins were loaded, but not user config.
eq('health.vim', string.match(out, 'health.vim'))
eq(nil, string.match(out, 'init.vim'))
@@ -265,11 +417,13 @@ describe('startup', function()
{ 'put =mode(1)', 'print', '' }))
end)
- it('fails on --embed with -es/-Es', function()
- matches('nvim[.exe]*: %-%-embed conflicts with %-es/%-Es',
+ it('fails on --embed with -es/-Es/-l', function()
+ matches('nvim[.exe]*: %-%-embed conflicts with %-es/%-Es/%-l',
funcs.system({nvim_prog, '--embed', '-es' }))
- matches('nvim[.exe]*: %-%-embed conflicts with %-es/%-Es',
+ matches('nvim[.exe]*: %-%-embed conflicts with %-es/%-Es/%-l',
funcs.system({nvim_prog, '--embed', '-Es' }))
+ matches('nvim[.exe]*: %-%-embed conflicts with %-es/%-Es/%-l',
+ funcs.system({nvim_prog, '--embed', '-l', 'foo.lua' }))
end)
it('does not crash if --embed is given twice', function()
@@ -354,7 +508,9 @@ describe('startup', function()
local function pack_clear(cmd)
-- add packages after config dir in rtp but before config/after
- clear{args={'--cmd', 'set packpath=test/functional/fixtures', '--cmd', 'let paths=split(&rtp, ",")', '--cmd', 'let &rtp = paths[0]..",test/functional/fixtures,test/functional/fixtures/middle,"..join(paths[1:],",")', '--cmd', cmd}, env={XDG_CONFIG_HOME='test/functional/fixtures/'}}
+ clear{args={'--cmd', 'set packpath=test/functional/fixtures', '--cmd', 'let paths=split(&rtp, ",")', '--cmd', 'let &rtp = paths[0]..",test/functional/fixtures,test/functional/fixtures/middle,"..join(paths[1:],",")', '--cmd', cmd}, env={XDG_CONFIG_HOME='test/functional/fixtures/'},
+ args_rm={'runtimepath'},
+ }
end
@@ -462,6 +618,19 @@ describe('startup', function()
clear{args={'--cmd', 'set packpath^=test/functional/fixtures', '--cmd', [[ lua _G.test_loadorder = {} vim.cmd "runtime! filen.lua" ]]}, env={XDG_CONFIG_HOME='test/functional/fixtures/'}}
eq({'ordinary', 'FANCY', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]])
end)
+
+ it('window widths are correct when modelines set &columns with tabpages', function()
+ write_file('tab1.noft', 'vim: columns=81')
+ write_file('tab2.noft', 'vim: columns=81')
+ finally(function()
+ os.remove('tab1.noft')
+ os.remove('tab2.noft')
+ end)
+ clear({args = {'-p', 'tab1.noft', 'tab2.noft'}})
+ eq(81, meths.win_get_width(0))
+ command('tabnext')
+ eq(81, meths.win_get_width(0))
+ end)
end)
describe('sysinit', function()
@@ -516,32 +685,6 @@ describe('sysinit', function()
eval('printf("loaded %d xdg %d vim %d", g:loaded, get(g:, "xdg", 0), get(g:, "vim", 0))'))
end)
- it('fixed hang issue with -D (#12647)', function()
- local screen
- 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. |
- nvim_exec() |
- cmd: aunmenu * |
- > |
- <" -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|
- |
- ]])
- end)
end)
describe('user config init', function()
@@ -574,7 +717,81 @@ describe('user config init', function()
eq(funcs.fnamemodify(init_lua_path, ':p'), eval('$MYVIMRC'))
end)
- describe 'with explicitly provided config'(function()
+ describe('with existing .exrc in cwd', function()
+ local exrc_path = '.exrc'
+ local xstate = 'Xstate'
+
+ local function setup_exrc_file(filename)
+ exrc_path = filename
+
+ if string.find(exrc_path, "%.lua$") then
+ write_file(exrc_path, string.format([[
+ vim.g.exrc_file = "%s"
+ ]], exrc_path))
+ else
+ write_file(exrc_path, string.format([[
+ let g:exrc_file = "%s"
+ ]], exrc_path))
+ end
+ end
+
+ before_each(function()
+ write_file(init_lua_path, [[
+ vim.o.exrc = true
+ vim.g.exrc_file = '---'
+ ]])
+ mkdir_p(xstate .. pathsep .. (is_os('win') and 'nvim-data' or 'nvim'))
+ end)
+
+ after_each(function()
+ os.remove(exrc_path)
+ rmdir(xstate)
+ end)
+
+ for _, filename in ipairs({ '.exrc', '.nvimrc', '.nvim.lua' }) do
+ it('loads ' .. filename, function ()
+ setup_exrc_file(filename)
+
+ clear{ args_rm = {'-u'}, env={ XDG_CONFIG_HOME=xconfig, XDG_STATE_HOME=xstate } }
+ -- The 'exrc' file is not trusted, and the prompt is skipped because there is no UI.
+ eq('---', eval('g:exrc_file'))
+
+ local screen = Screen.new(50, 8)
+ screen:attach()
+ funcs.termopen({nvim_prog})
+ screen:expect({ any = pesc('[i]gnore, (v)iew, (d)eny, (a)llow:') })
+ -- `i` to enter Terminal mode, `a` to allow
+ feed('ia')
+ screen:expect([[
+ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ [No Name] 0,0-1 All|
+ |
+ -- TERMINAL -- |
+ ]])
+ feed(':echo g:exrc_file<CR>')
+ screen:expect(string.format([[
+ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ [No Name] 0,0-1 All|
+ %s%s|
+ -- TERMINAL -- |
+ ]], filename, string.rep(' ', 50 - #filename)))
+
+ clear{ args_rm = {'-u'}, env={ XDG_CONFIG_HOME=xconfig, XDG_STATE_HOME=xstate } }
+ -- The 'exrc' file is now trusted.
+ eq(filename, eval('g:exrc_file'))
+ end)
+ end
+ end)
+
+ describe('with explicitly provided config', function()
local custom_lua_path = table.concat({xhome, 'custom.lua'}, pathsep)
before_each(function()
write_file(custom_lua_path, [[
@@ -589,7 +806,7 @@ describe('user config init', function()
end)
end)
- describe 'VIMRC also exists'(function()
+ describe('VIMRC also exists', function()
before_each(function()
write_file(table.concat({xconfig, 'nvim', 'init.vim'}, pathsep), [[
let g:vim_rc = 1
@@ -635,7 +852,7 @@ describe('runtime:', function()
end)
it('loads plugin/*.lua from start packages', function()
- local plugin_path = table.concat({xconfig, 'nvim', 'pack', 'catagory',
+ local plugin_path = table.concat({xconfig, 'nvim', 'pack', 'category',
'start', 'test_plugin'}, pathsep)
local plugin_folder_path = table.concat({plugin_path, 'plugin'}, pathsep)
local plugin_file_path = table.concat({plugin_folder_path, 'plugin.lua'},
@@ -663,7 +880,7 @@ describe('runtime:', function()
end)
it('loads plugin/*.lua from site packages', function()
- local nvimdata = iswin() and "nvim-data" or "nvim"
+ local nvimdata = is_os('win') and "nvim-data" or "nvim"
local plugin_path = table.concat({xdata, nvimdata, 'site', 'pack', 'xa', 'start', 'yb'}, pathsep)
local plugin_folder_path = table.concat({plugin_path, 'plugin'}, pathsep)
local plugin_after_path = table.concat({plugin_path, 'after', 'plugin'}, pathsep)
diff --git a/test/functional/editor/K_spec.lua b/test/functional/editor/K_spec.lua
index 8ad81ac3d6..3b5580540f 100644
--- a/test/functional/editor/K_spec.lua
+++ b/test/functional/editor/K_spec.lua
@@ -1,6 +1,6 @@
local helpers = require('test.functional.helpers')(after_each)
-local eq, clear, eval, feed, retry =
- helpers.eq, helpers.clear, helpers.eval, helpers.feed, helpers.retry
+local eq, clear, eval, feed, meths, retry =
+ helpers.eq, helpers.clear, helpers.eval, helpers.feed, helpers.meths, helpers.retry
describe('K', function()
local test_file = 'K_spec_out'
@@ -58,4 +58,11 @@ describe('K', function()
helpers.neq(bufnr, eval('bufnr()'))
end)
+ it('empty string falls back to :help #19298', function()
+ meths.set_option('keywordprg', '')
+ meths.buf_set_lines(0, 0, -1, true, {'doesnotexist'})
+ feed('K')
+ eq('E149: Sorry, no help for doesnotexist', meths.get_vvar('errmsg'))
+ end)
+
end)
diff --git a/test/functional/editor/completion_spec.lua b/test/functional/editor/completion_spec.lua
index 6cdac3c079..22857efe5b 100644
--- a/test/functional/editor/completion_spec.lua
+++ b/test/functional/editor/completion_spec.lua
@@ -935,6 +935,9 @@ describe('completion', function()
eq({'api'}, funcs.getcompletion('vim.ap', 'lua'))
eq({'tbl_filter'}, funcs.getcompletion('vim.tbl_fil', 'lua'))
eq({'vim'}, funcs.getcompletion('print(vi', 'lua'))
+ -- fuzzy completion is not supported, so the result should be the same
+ command('set wildoptions+=fuzzy')
+ eq({'vim'}, funcs.getcompletion('vi', 'lua'))
end)
end)
@@ -1029,7 +1032,8 @@ describe('completion', function()
]])
end)
- it('TextChangedP autocommand', function()
+ -- oldtest: Test_ChangedP()
+ it('TextChangedI and TextChangedP autocommands', function()
curbufmeths.set_lines(0, 1, false, { 'foo', 'bar', 'foobar'})
source([[
set complete=. completeopt=menuone
@@ -1128,6 +1132,49 @@ describe('completion', function()
call cursor(4, 1)
]])
+ -- v:event.size should be set with ext_popupmenu #20646
+ screen:set_option('ext_popupmenu', true)
+ feed('Sf<C-N>')
+ screen:expect({grid = [[
+ foo |
+ bar |
+ foobar |
+ f^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {3:-- Keyword completion (^N^P) }{5:Back at original} |
+ ]], popupmenu = {
+ anchor = { 1, 3, 0 },
+ items = { { "foo", "", "", "" }, { "foobar", "", "", "" } },
+ pos = -1
+ }})
+ eq({completed_item = {}, width = 0,
+ height = 2, size = 2,
+ col = 0, row = 4, scrollbar = false},
+ eval('g:event'))
+ feed('oob')
+ screen:expect({grid = [[
+ foo |
+ bar |
+ foobar |
+ foob^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {3:-- Keyword completion (^N^P) }{5:Back at original} |
+ ]], popupmenu = {
+ anchor = { 1, 3, 0 },
+ items = { { "foobar", "", "", "" } },
+ pos = -1
+ }})
+ eq({completed_item = {}, width = 0,
+ height = 1, size = 1,
+ col = 0, row = 4, scrollbar = false},
+ eval('g:event'))
+ feed('<Esc>')
+ screen:set_option('ext_popupmenu', false)
+
feed('Sf<C-N>')
screen:expect([[
foo |
diff --git a/test/functional/editor/mark_spec.lua b/test/functional/editor/mark_spec.lua
index 2440867c6e..b3b190ef79 100644
--- a/test/functional/editor/mark_spec.lua
+++ b/test/functional/editor/mark_spec.lua
@@ -40,59 +40,59 @@ describe('named marks', function()
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)
+ eq("nvim_exec(): 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)
+ eq("nvim_exec(): 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)
+ eq("nvim_exec(): 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)
+ eq("nvim_exec(): 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)
+ eq("nvim_exec(): 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)
+ eq("nvim_exec(): Vim(normal):E20: Mark not set", err)
err = pcall_err(helpers.exec_capture, "normal! '.")
- eq("Vim(normal):E20: Mark not set", err)
+ eq("nvim_exec(): 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)
+ eq("nvim_exec(): Vim(normal):E20: Mark not set", err)
err = pcall_err(helpers.exec_capture, "normal! `>")
- eq("Vim(normal):E20: Mark not set", err)
+ eq("nvim_exec(): 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)
+ eq("nvim_exec(): 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)
+ eq("nvim_exec(): Vim(normal):E20: Mark not set", err)
end)
it("can move to them using '", function()
@@ -153,7 +153,7 @@ describe('named marks', function()
command("next")
command("bw! " .. file1 )
local err = pcall_err(helpers.exec_capture, "normal! 'A")
- eq("Vim(normal):E92: Buffer 1 not found", err)
+ eq("nvim_exec(): Vim(normal):E92: Buffer 1 not found", err)
os.remove(file1)
end)
@@ -330,7 +330,7 @@ describe('named marks view', function()
os.remove(file2)
end)
- it('is restored', function()
+ it('is restored in normal mode but not op-pending mode', function()
local screen = Screen.new(5, 8)
screen:attach()
command("edit " .. file1)
@@ -358,6 +358,18 @@ describe('named marks view', function()
8 line |
|
]])
+ -- not in op-pending mode #20886
+ feed("ggj=`a")
+ screen:expect([[
+ 1 line |
+ ^2 line |
+ 3 line |
+ 4 line |
+ 5 line |
+ 6 line |
+ 7 line |
+ |
+ ]])
end)
it('is restored across files', function()
diff --git a/test/functional/editor/mode_insert_spec.lua b/test/functional/editor/mode_insert_spec.lua
index e3d3cdbd85..cd51a65be3 100644
--- a/test/functional/editor/mode_insert_spec.lua
+++ b/test/functional/editor/mode_insert_spec.lua
@@ -6,12 +6,20 @@ local expect = helpers.expect
local command = helpers.command
local eq = helpers.eq
local eval = helpers.eval
+local curbuf_contents = helpers.curbuf_contents
describe('insert-mode', function()
before_each(function()
clear()
end)
+ it('indents only once after "!" keys #12894', function()
+ command('let counter = []')
+ command('set indentexpr=len(add(counter,0))')
+ feed('i<C-F>x')
+ eq(' x', curbuf_contents())
+ end)
+
it('CTRL-@', function()
-- Inserts last-inserted text, leaves insert-mode.
insert('hello')
diff --git a/test/functional/editor/tabpage_spec.lua b/test/functional/editor/tabpage_spec.lua
index 849a02c28b..a7f629a76b 100644
--- a/test/functional/editor/tabpage_spec.lua
+++ b/test/functional/editor/tabpage_spec.lua
@@ -9,6 +9,9 @@ local feed = helpers.feed
local eval = helpers.eval
local exec = helpers.exec
local funcs = helpers.funcs
+local meths = helpers.meths
+local curwin = helpers.curwin
+local assert_alive = helpers.assert_alive
describe('tabpage', function()
before_each(clear)
@@ -54,6 +57,45 @@ describe('tabpage', function()
neq(999, eval('g:win_closed'))
end)
+ it('no segfault with strange WinClosed autocommand #20290', function()
+ pcall(exec, [[
+ set nohidden
+ edit Xa
+ split Xb
+ tab split
+ new
+ autocmd WinClosed * tabprev | bwipe!
+ close
+ ]])
+ assert_alive()
+ end)
+
+ it('nvim_win_close and nvim_win_hide update tabline #20285', function()
+ eq(1, #meths.list_tabpages())
+ eq({1, 1}, funcs.win_screenpos(0))
+ local win1 = curwin().id
+
+ command('tabnew')
+ eq(2, #meths.list_tabpages())
+ eq({2, 1}, funcs.win_screenpos(0))
+ local win2 = curwin().id
+
+ meths.win_close(win1, true)
+ eq(win2, curwin().id)
+ eq(1, #meths.list_tabpages())
+ eq({1, 1}, funcs.win_screenpos(0))
+
+ command('tabnew')
+ eq(2, #meths.list_tabpages())
+ eq({2, 1}, funcs.win_screenpos(0))
+ local win3 = curwin().id
+
+ meths.win_hide(win2)
+ eq(win3, curwin().id)
+ eq(1, #meths.list_tabpages())
+ eq({1, 1}, funcs.win_screenpos(0))
+ end)
+
it('switching tabpage after setting laststatus=3 #19591', function()
local screen = Screen.new(40, 8)
screen:set_default_attr_ids({
@@ -102,4 +144,10 @@ describe('tabpage', function()
command(' silent :keepalt :: ::: silent! -2 tabmove')
eq(1, funcs.nvim_tabpage_get_number(0))
end)
+
+ it(':tabs does not overflow IObuff with long path with comma #20850', function()
+ meths.buf_set_name(0, ('x'):rep(1024) .. ',' .. ('x'):rep(1024))
+ command('tabs')
+ assert_alive()
+ end)
end)
diff --git a/test/functional/editor/undo_spec.lua b/test/functional/editor/undo_spec.lua
index a041428cdc..d66ab352ef 100644
--- a/test/functional/editor/undo_spec.lua
+++ b/test/functional/editor/undo_spec.lua
@@ -9,6 +9,8 @@ local feed = helpers.feed
local feed_command = helpers.feed_command
local insert = helpers.insert
local funcs = helpers.funcs
+local exec = helpers.exec
+local exec_lua = helpers.exec_lua
local function lastmessage()
local messages = funcs.split(funcs.execute('messages'), '\n')
@@ -67,6 +69,79 @@ describe('u CTRL-R g- g+', function()
undo_and_redo(4, 'u', '<C-r>', '1')
undo_and_redo(4, 'g-', 'g+', '1')
end)
+
+ describe('undo works correctly when writing in Insert mode', function()
+ before_each(function()
+ exec([[
+ edit Xtestfile.txt
+ set undolevels=100 undofile
+ write
+ ]])
+ end)
+
+ after_each(function()
+ command('bwipe!')
+ os.remove('Xtestfile.txt')
+ os.remove('Xtestfile.txt.un~')
+ end)
+
+ -- oldtest: Test_undo_after_write()
+ it('using <Cmd> mapping', function()
+ command('imap . <Cmd>write<CR>')
+ feed('Otest.<CR>boo!!!<Esc>')
+ expect([[
+ test
+ boo!!!
+ ]])
+
+ feed('u')
+ expect([[
+ test
+ ]])
+
+ feed('u')
+ expect('')
+ end)
+
+ it('using Lua mapping', function()
+ exec_lua([[
+ vim.api.nvim_set_keymap('i', '.', '', {callback = function()
+ vim.cmd('write')
+ end})
+ ]])
+ feed('Otest.<CR>boo!!!<Esc>')
+ expect([[
+ test
+ boo!!!
+ ]])
+
+ feed('u')
+ expect([[
+ test
+ ]])
+
+ feed('u')
+ expect('')
+ end)
+
+ it('using RPC call', function()
+ feed('Otest')
+ command('write')
+ feed('<CR>boo!!!<Esc>')
+ expect([[
+ test
+ boo!!!
+ ]])
+
+ feed('u')
+ expect([[
+ test
+ ]])
+
+ feed('u')
+ expect('')
+ end)
+ end)
end)
describe(':undo! command', function()
diff --git a/test/functional/ex_cmds/cd_spec.lua b/test/functional/ex_cmds/cd_spec.lua
index 42a811f5da..5ed71651c7 100644
--- a/test/functional/ex_cmds/cd_spec.lua
+++ b/test/functional/ex_cmds/cd_spec.lua
@@ -9,6 +9,8 @@ local clear = helpers.clear
local command = helpers.command
local exc_exec = helpers.exc_exec
local pathsep = helpers.get_pathsep()
+local skip = helpers.skip
+local is_os = helpers.is_os
-- These directories will be created for testing
local directories = {
@@ -279,9 +281,7 @@ describe("getcwd()", function ()
end)
it("returns empty string if working directory does not exist", function()
- if helpers.iswin() then
- return
- end
+ skip(is_os('win'))
command("cd "..directories.global)
command("call delete('../"..directories.global.."', 'd')")
eq("", helpers.eval("getcwd()"))
diff --git a/test/functional/ex_cmds/dict_notifications_spec.lua b/test/functional/ex_cmds/dict_notifications_spec.lua
index 21adcf37da..afa6b519d5 100644
--- a/test/functional/ex_cmds/dict_notifications_spec.lua
+++ b/test/functional/ex_cmds/dict_notifications_spec.lua
@@ -4,6 +4,7 @@ local clear, nvim, source = helpers.clear, helpers.nvim, helpers.source
local insert = helpers.insert
local eq, next_msg = helpers.eq, helpers.next_msg
local exc_exec = helpers.exc_exec
+local exec_lua = helpers.exec_lua
local command = helpers.command
local eval = helpers.eval
@@ -21,6 +22,8 @@ describe('VimL dictionary notifications', function()
-- t:) and a dictionary variable, so we generate them in the following
-- function.
local function gentests(dict_expr, dict_init)
+ local is_g = dict_expr == 'g:'
+
local function update(opval, key)
if not key then
key = 'watched'
@@ -32,6 +35,28 @@ describe('VimL dictionary notifications', function()
end
end
+ local function update_with_api(opval, key)
+ if not key then
+ key = 'watched'
+ end
+ if opval == '' then
+ exec_lua(('vim.api.nvim_del_var(\'%s\')'):format(key))
+ else
+ exec_lua(('vim.api.nvim_set_var(\'%s\', %s)'):format(key, opval))
+ end
+ end
+
+ local function update_with_vim_g(opval, key)
+ if not key then
+ key = 'watched'
+ end
+ if opval == '' then
+ exec_lua(('vim.g.%s = nil'):format(key))
+ else
+ exec_lua(('vim.g.%s %s'):format(key, opval))
+ end
+ end
+
local function verify_echo()
-- helper to verify that no notifications are sent after certain change
-- to a dict
@@ -76,6 +101,18 @@ describe('VimL dictionary notifications', function()
update('', 'watched2')
update('')
verify_echo()
+ if is_g then
+ update_with_api('"test"')
+ update_with_api('"test2"', 'watched2')
+ update_with_api('', 'watched2')
+ update_with_api('')
+ verify_echo()
+ update_with_vim_g('= "test"')
+ update_with_vim_g('= "test2"', 'watched2')
+ update_with_vim_g('', 'watched2')
+ update_with_vim_g('')
+ verify_echo()
+ end
end)
it('is not triggered when unwatched keys are updated', function()
@@ -83,6 +120,16 @@ describe('VimL dictionary notifications', function()
update('.= "noop2"', 'unwatched')
update('', 'unwatched')
verify_echo()
+ if is_g then
+ update_with_api('"noop"', 'unwatched')
+ update_with_api('vim.g.unwatched .. "noop2"', 'unwatched')
+ update_with_api('', 'unwatched')
+ verify_echo()
+ update_with_vim_g('= "noop"', 'unwatched')
+ update_with_vim_g('= vim.g.unwatched .. "noop2"', 'unwatched')
+ update_with_vim_g('', 'unwatched')
+ verify_echo()
+ end
end)
it('is triggered by remove()', function()
@@ -92,6 +139,22 @@ describe('VimL dictionary notifications', function()
verify_value({old = 'test'})
end)
+ if is_g then
+ it('is triggered by remove() when updated with nvim_*_var', function()
+ update_with_api('"test"')
+ verify_value({new = 'test'})
+ nvim('command', 'call remove('..dict_expr..', "watched")')
+ verify_value({old = 'test'})
+ end)
+
+ it('is triggered by remove() when updated with vim.g', function()
+ update_with_vim_g('= "test"')
+ verify_value({new = 'test'})
+ nvim('command', 'call remove('..dict_expr..', "watched")')
+ verify_value({old = 'test'})
+ end)
+ end
+
it('is triggered by extend()', function()
update('= "xtend"')
verify_value({new = 'xtend'})
diff --git a/test/functional/ex_cmds/map_spec.lua b/test/functional/ex_cmds/map_spec.lua
index c6bdd017bd..ec912053b2 100644
--- a/test/functional/ex_cmds/map_spec.lua
+++ b/test/functional/ex_cmds/map_spec.lua
@@ -3,6 +3,7 @@ local Screen = require('test.functional.ui.screen')
local eq = helpers.eq
local exec = helpers.exec
+local exec_capture = helpers.exec_capture
local feed = helpers.feed
local meths = helpers.meths
local clear = helpers.clear
@@ -30,12 +31,12 @@ describe(':*map', function()
expect('-foo-')
end)
- it('shows <nop> as mapping rhs', function()
+ it('shows <Nop> as mapping rhs', function()
command('nmap asdf <Nop>')
eq([[
n asdf <Nop>]],
- helpers.exec_capture('nmap asdf'))
+ exec_capture('nmap asdf'))
end)
it('mappings with description can be filtered', function()
@@ -48,7 +49,7 @@ n asdf3 qwert
do the other thing
n asdf1 qwert
do the one thing]],
- helpers.exec_capture('filter the nmap'))
+ exec_capture('filter the nmap'))
end)
it('<Plug> mappings ignore nore', function()
@@ -84,6 +85,12 @@ n asdf1 qwert
eq(2, meths.eval('x'))
eq('Some te', eval("getline('.')"))
end)
+
+ it(':unmap with rhs works when lhs is in another bucket #21530', function()
+ command('map F <Plug>Foo')
+ command('unmap <Plug>Foo')
+ eq('\nNo mapping found', exec_capture('map F'))
+ end)
end)
describe('Screen', function()
@@ -161,6 +168,7 @@ describe('Screen', function()
]])
end)
+ -- oldtest: Test_expr_map_restore_cursor()
it('cursor is restored after :map <expr> which redraws statusline vim-patch:8.1.2336', function()
exec([[
call setline(1, ['one', 'two', 'three'])
@@ -246,6 +254,7 @@ describe('Screen', function()
]])
end)
+ -- oldtest: Test_map_listing()
it('listing mappings clears command line vim-patch:8.2.4401', function()
screen:try_resize(40, 5)
command('nmap a b')
diff --git a/test/functional/ex_cmds/mksession_spec.lua b/test/functional/ex_cmds/mksession_spec.lua
index ee8da2932d..0a0c7ca410 100644
--- a/test/functional/ex_cmds/mksession_spec.lua
+++ b/test/functional/ex_cmds/mksession_spec.lua
@@ -5,7 +5,6 @@ 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
@@ -14,6 +13,8 @@ local pesc = helpers.pesc
local rmdir = helpers.rmdir
local sleep = helpers.sleep
local meths = helpers.meths
+local skip = helpers.skip
+local is_os = helpers.is_os
local file_prefix = 'Xtest-functional-ex_cmds-mksession_spec'
@@ -177,7 +178,7 @@ describe(':mksession', function()
command('cd ' .. cwd_dir)
command('mksession ' .. session_path)
command('%bwipeout!')
- if iswin() then
+ if is_os('win') then
sleep(100) -- Make sure all child processes have exited.
end
@@ -188,16 +189,13 @@ describe(':mksession', function()
local expected_cwd = cwd_dir .. '/' .. tab_dir
matches('^term://' .. pesc(expected_cwd) .. '//%d+:', funcs.expand('%'))
command('%bwipeout!')
- if iswin() then
+ if is_os('win') 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
+ skip(is_os('win'), 'N/A for Windows')
local screen
local cwd_dir = funcs.fnamemodify('.', ':p:~'):gsub([[[\/]*$]], '')
diff --git a/test/functional/ex_cmds/normal_spec.lua b/test/functional/ex_cmds/normal_spec.lua
index f6e7dd2b3a..009f1d6516 100644
--- a/test/functional/ex_cmds/normal_spec.lua
+++ b/test/functional/ex_cmds/normal_spec.lua
@@ -1,6 +1,7 @@
local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
local command = helpers.command
+local funcs = helpers.funcs
local feed = helpers.feed
local expect = helpers.expect
local eq = helpers.eq
@@ -8,20 +9,30 @@ local eval = helpers.eval
before_each(clear)
-describe(':normal', function()
+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()
+ it('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()
+ it('gQinsert does not hang #17980', function()
command('normal! gQinsert')
expect('')
end)
+
+ it('can stop Visual mode without closing cmdwin vim-patch:9.0.0234', function()
+ feed('q:')
+ feed('v')
+ eq('v', funcs.mode(1))
+ eq(':', funcs.getcmdwintype())
+ command('normal! \027')
+ eq('n', funcs.mode(1))
+ eq(':', funcs.getcmdwintype())
+ end)
end)
diff --git a/test/functional/ex_cmds/quickfix_commands_spec.lua b/test/functional/ex_cmds/quickfix_commands_spec.lua
index 94b7fa1a84..4d9d8eeb90 100644
--- a/test/functional/ex_cmds/quickfix_commands_spec.lua
+++ b/test/functional/ex_cmds/quickfix_commands_spec.lua
@@ -1,5 +1,7 @@
local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local feed = helpers.feed
local eq = helpers.eq
local clear = helpers.clear
local funcs = helpers.funcs
@@ -123,3 +125,22 @@ describe('quickfix', function()
os.remove(file)
end)
end)
+
+it(':vimgrep can specify Unicode pattern without delimiters', function()
+ eq('Vim(vimgrep):E480: No match: →', exc_exec('vimgrep → test/functional/fixtures/tty-test.c'))
+ local screen = Screen.new(40, 6)
+ screen:set_default_attr_ids({
+ [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [1] = {reverse = true}, -- IncSearch
+ })
+ screen:attach()
+ feed('i→<Esc>:vimgrep →')
+ screen:expect([[
+ {1:→} |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ :vimgrep →^ |
+ ]])
+end)
diff --git a/test/functional/ex_cmds/quit_spec.lua b/test/functional/ex_cmds/quit_spec.lua
index fe138a24c8..3680801dae 100644
--- a/test/functional/ex_cmds/quit_spec.lua
+++ b/test/functional/ex_cmds/quit_spec.lua
@@ -7,7 +7,7 @@ describe(':qa', function()
end)
it('verify #3334', function()
- -- just testing if 'qa' passed as a program argument wont result in memory
+ -- just testing if 'qa' passed as a program argument won't result in memory
-- errors
end)
end)
diff --git a/test/functional/ex_cmds/source_spec.lua b/test/functional/ex_cmds/source_spec.lua
index 163ded43f9..64c3464be7 100644
--- a/test/functional/ex_cmds/source_spec.lua
+++ b/test/functional/ex_cmds/source_spec.lua
@@ -14,9 +14,9 @@ local eval = helpers.eval
local exec_capture = helpers.exec_capture
local neq = helpers.neq
local matches = helpers.matches
-local iswin = helpers.iswin
local mkdir = helpers.mkdir
local rmdir = helpers.rmdir
+local is_os = helpers.is_os
describe(':source', function()
before_each(function()
@@ -44,7 +44,7 @@ describe(':source', function()
end)
it("changing 'shellslash' changes the result of expand()", function()
- if not iswin() then
+ if not is_os('win') then
pending("'shellslash' only works on Windows")
return
end
@@ -104,7 +104,7 @@ describe(':source', function()
eq("0zBEEFCAFE", meths.exec('echo d', true))
exec('set cpoptions+=C')
- eq('Vim(let):E15: Invalid expression: #{', exc_exec('source'))
+ eq('Vim(let):E723: Missing end of Dictionary \'}\': ', exc_exec('source'))
end)
it('selection in current buffer', function()
@@ -138,7 +138,7 @@ describe(':source', function()
eq('Vim(echo):E117: Unknown function: s:C', exc_exec('echo D()'))
exec('set cpoptions+=C')
- eq('Vim(let):E15: Invalid expression: #{', exc_exec("'<,'>source"))
+ eq('Vim(let):E723: Missing end of Dictionary \'}\': ', exc_exec("'<,'>source"))
end)
it('does not break if current buffer is modified while sourced', function()
@@ -166,6 +166,7 @@ describe(':source', function()
vim.g.sourced_lua = 1
vim.g.sfile_value = vim.fn.expand('<sfile>')
vim.g.stack_value = vim.fn.expand('<stack>')
+ vim.g.script_value = vim.fn.expand('<script>')
]])
command('set shellslash')
@@ -173,6 +174,7 @@ describe(':source', function()
eq(1, eval('g:sourced_lua'))
matches([[/test%.lua$]], meths.get_var('sfile_value'))
matches([[/test%.lua$]], meths.get_var('stack_value'))
+ matches([[/test%.lua$]], meths.get_var('script_value'))
os.remove(test_file)
end)
@@ -214,6 +216,7 @@ describe(':source', function()
"\ 2]=]
vim.g.sfile_value = vim.fn.expand('<sfile>')
vim.g.stack_value = vim.fn.expand('<stack>')
+ vim.g.script_value = vim.fn.expand('<script>')
]])
command('edit '..test_file)
@@ -223,6 +226,7 @@ describe(':source', function()
eq(' \\ 1\n "\\ 2', exec_lua('return _G.a'))
eq(':source (no file)', meths.get_var('sfile_value'))
eq(':source (no file)', meths.get_var('stack_value'))
+ eq(':source (no file)', meths.get_var('script_value'))
os.remove(test_file)
end)
diff --git a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua
index 4d984af41e..8eed00c973 100644
--- a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua
+++ b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua
@@ -1,15 +1,19 @@
local Screen = require('test.functional.ui.screen')
local helpers = require('test.functional.helpers')(after_each)
local lfs = require('lfs')
-local eq, eval, expect, source =
- helpers.eq, helpers.eval, helpers.expect, helpers.source
+local luv = require('luv')
+local eq, eval, expect, exec =
+ helpers.eq, helpers.eval, helpers.expect, helpers.exec
local assert_alive = helpers.assert_alive
local clear = helpers.clear
local command = helpers.command
local feed = helpers.feed
+local funcs = helpers.funcs
local nvim_prog = helpers.nvim_prog
local ok = helpers.ok
local rmdir = helpers.rmdir
+local new_argv = helpers.new_argv
+local pesc = helpers.pesc
local os_kill = helpers.os_kill
local set_session = helpers.set_session
local spawn = helpers.spawn
@@ -55,11 +59,11 @@ describe(':preserve', function()
set swapfile fileformat=unix undolevels=-1
]]
- source(init)
+ exec(init)
command('edit! '..testfile)
feed('isometext<esc>')
command('preserve')
- source('redir => g:swapname | silent swapname | redir END')
+ exec('redir => g:swapname | silent swapname | redir END')
local swappath1 = eval('g:swapname')
@@ -69,12 +73,12 @@ describe(':preserve', function()
true)
set_session(nvim2)
- source(init)
+ exec(init)
-- Use the "SwapExists" event to choose the (R)ecover choice at the dialog.
command('autocmd SwapExists * let v:swapchoice = "r"')
command('silent edit! '..testfile)
- source('redir => g:swapname | silent swapname | redir END')
+ exec('redir => g:swapname | silent swapname | redir END')
local swappath2 = eval('g:swapname')
@@ -92,25 +96,28 @@ end)
describe('swapfile detection', function()
local swapdir = lfs.currentdir()..'/Xtest_swapdialog_dir'
+ local nvim0
+ -- Put swapdir at the start of the 'directory' list. #1836
+ -- Note: `set swapfile` *must* go after `set directory`: otherwise it may
+ -- attempt to create a swapfile in different directory.
+ local init = [[
+ set directory^=]]..swapdir:gsub([[\]], [[\\]])..[[//
+ set swapfile fileformat=unix nomodified undolevels=-1 nohidden
+ ]]
before_each(function()
- clear()
+ nvim0 = spawn(new_argv())
+ set_session(nvim0)
rmdir(swapdir)
lfs.mkdir(swapdir)
end)
after_each(function()
+ set_session(nvim0)
command('%bwipeout!')
rmdir(swapdir)
end)
it('always show swapfile dialog #8840 #9027', function()
local testfile = 'Xtest_swapdialog_file1'
- -- Put swapdir at the start of the 'directory' list. #1836
- -- Note: `set swapfile` *must* go after `set directory`: otherwise it may
- -- attempt to create a swapfile in different directory.
- local init = [[
- set directory^=]]..swapdir:gsub([[\]], [[\\]])..[[//
- set swapfile fileformat=unix undolevels=-1 hidden
- ]]
local expected_no_dialog = '^'..(' '):rep(256)..'|\n'
for _=1,37 do
@@ -119,19 +126,17 @@ describe('swapfile detection', function()
expected_no_dialog = expected_no_dialog..testfile..(' '):rep(216)..'0,0-1 All|\n'
expected_no_dialog = expected_no_dialog..(' '):rep(256)..'|\n'
- source(init)
+ exec(init)
command('edit! '..testfile)
feed('isometext<esc>')
command('preserve')
- os_kill(eval('getpid()'))
-- Start another Nvim instance.
- local nvim2 = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed'},
- true)
+ local nvim2 = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed'}, true, nil, true)
set_session(nvim2)
local screen2 = Screen.new(256, 40)
screen2:attach()
- source(init)
+ exec(init)
-- With shortmess+=F
command('set shortmess+=F')
@@ -176,5 +181,163 @@ describe('swapfile detection', function()
}
})
feed('<cr>')
+
+ nvim2:close()
+ end)
+
+ -- oldtest: Test_swap_prompt_splitwin()
+ it('selecting "q" in the attention prompt', function()
+ exec(init)
+ command('edit Xfile1')
+ command('preserve') -- should help to make sure the swap file exists
+
+ local screen = Screen.new(75, 18)
+ screen:set_default_attr_ids({
+ [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [1] = {bold = true, foreground = Screen.colors.SeaGreen}, -- MoreMsg
+ })
+
+ local nvim1 = spawn(new_argv(), true, nil, true)
+ set_session(nvim1)
+ screen:attach()
+ exec(init)
+ feed(':split Xfile1\n')
+ screen:expect({
+ any = pesc('{1:[O]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort: }^')
+ })
+ feed('q')
+ feed(':<CR>')
+ screen:expect([[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ : |
+ ]])
+ nvim1:close()
+
+ local nvim2 = spawn(new_argv(), true, nil, true)
+ set_session(nvim2)
+ screen:attach()
+ exec(init)
+ command('set more')
+ command('au bufadd * let foo_w = wincol()')
+ feed(':e Xfile1<CR>')
+ screen:expect({any = pesc('{1:-- More --}^')})
+ feed('<Space>')
+ screen:expect({
+ any = pesc('{1:[O]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort: }^')
+ })
+ feed('q')
+ command([[echo 'hello']])
+ screen:expect([[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ hello |
+ ]])
+ nvim2:close()
+ end)
+
+ -- oldtest: Test_nocatch_process_still_running()
+ it('allows deleting swapfile created before boot vim-patch:8.2.2586', function()
+ local screen = Screen.new(75, 30)
+ screen:set_default_attr_ids({
+ [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [1] = {bold = true, foreground = Screen.colors.SeaGreen}, -- MoreMsg
+ [2] = {background = Screen.colors.Red, foreground = Screen.colors.White}, -- ErrorMsg
+ })
+ screen:attach()
+
+ exec(init)
+ command('set nohidden')
+
+ exec([=[
+ " Make a copy of the current swap file to "Xswap".
+ " Return the name of the swap file.
+ func CopySwapfile()
+ preserve
+ " get the name of the swap file
+ let swname = split(execute("swapname"))[0]
+ let swname = substitute(swname, '[[:blank:][:cntrl:]]*\(.\{-}\)[[:blank:][:cntrl:]]*$', '\1', '')
+ " make a copy of the swap file in Xswap
+ set binary
+ exe 'sp ' . fnameescape(swname)
+ w! Xswap
+ set nobinary
+ return swname
+ endfunc
+ ]=])
+
+ -- Edit a file and grab its swapfile.
+ exec([[
+ edit Xswaptest
+ call setline(1, ['a', 'b', 'c'])
+ ]])
+ local swname = funcs.CopySwapfile()
+
+ -- Forget we edited this file
+ exec([[
+ new
+ only!
+ bwipe! Xswaptest
+ ]])
+
+ os.rename('Xswap', swname)
+
+ feed(':edit Xswaptest<CR>')
+ screen:expect({any = table.concat({
+ pesc('{2:E325: ATTENTION}'),
+ 'file name: .*Xswaptest',
+ 'process ID: %d* %(STILL RUNNING%)',
+ pesc('{1:[O]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort: }^'),
+ }, '.*')})
+
+ feed('e')
+
+ -- Forget we edited this file
+ exec([[
+ new
+ only!
+ bwipe! Xswaptest
+ ]])
+
+ -- pretend that the swapfile was created before boot
+ lfs.touch(swname, os.time() - luv.uptime() - 10)
+
+ feed(':edit Xswaptest<CR>')
+ screen:expect({any = table.concat({
+ pesc('{2:E325: ATTENTION}'),
+ pesc('{1:[O]pen Read-Only, (E)dit anyway, (R)ecover, (D)elete it, (Q)uit, (A)bort: }^'),
+ }, '.*')})
+
+ feed('e')
end)
end)
diff --git a/test/functional/ex_cmds/trust_spec.lua b/test/functional/ex_cmds/trust_spec.lua
new file mode 100644
index 0000000000..10ee02a790
--- /dev/null
+++ b/test/functional/ex_cmds/trust_spec.lua
@@ -0,0 +1,176 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+
+local eq = helpers.eq
+local clear = helpers.clear
+local command = helpers.command
+local pathsep = helpers.get_pathsep()
+local is_os = helpers.is_os
+local funcs = helpers.funcs
+
+describe(':trust', function()
+ local xstate = 'Xstate'
+
+ setup(function()
+ helpers.mkdir_p(xstate .. pathsep .. (is_os('win') and 'nvim-data' or 'nvim'))
+ end)
+
+ teardown(function()
+ helpers.rmdir(xstate)
+ end)
+
+ before_each(function()
+ helpers.write_file('test_file', 'test')
+ clear{env={XDG_STATE_HOME=xstate}}
+ end)
+
+ after_each(function()
+ os.remove('test_file')
+ end)
+
+ it('trust then deny then remove a file using current buffer', function()
+ local screen = Screen.new(80, 8)
+ screen:attach()
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ })
+
+ local cwd = funcs.getcwd()
+ local hash = funcs.sha256(helpers.read_file('test_file'))
+
+ command('edit test_file')
+ command('trust')
+ screen:expect([[
+ ^test |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ "]] .. cwd .. pathsep .. [[test_file" trusted.{MATCH:%s+}|
+ ]])
+ local trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust')
+ eq(string.format('%s %s', hash, cwd .. pathsep .. 'test_file'), vim.trim(trust))
+
+ command('trust ++deny')
+ screen:expect([[
+ ^test |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ "]] .. cwd .. pathsep .. [[test_file" denied.{MATCH:%s+}|
+ ]])
+ trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust')
+ eq(string.format('! %s', cwd .. pathsep .. 'test_file'), vim.trim(trust))
+
+ command('trust ++remove')
+ screen:expect([[
+ ^test |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ "]] .. cwd .. pathsep .. [[test_file" removed.{MATCH:%s+}|
+ ]])
+ trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust')
+ eq(string.format(''), vim.trim(trust))
+ end)
+
+ it('deny then trust then remove a file using current buffer', function()
+ local screen = Screen.new(80, 8)
+ screen:attach()
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ })
+
+ local cwd = funcs.getcwd()
+ local hash = funcs.sha256(helpers.read_file('test_file'))
+
+ command('edit test_file')
+ command('trust ++deny')
+ screen:expect([[
+ ^test |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ "]] .. cwd .. pathsep .. [[test_file" denied.{MATCH:%s+}|
+ ]])
+ local trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust')
+ eq(string.format('! %s', cwd .. pathsep .. 'test_file'), vim.trim(trust))
+
+ command('trust')
+ screen:expect([[
+ ^test |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ "]] .. cwd .. pathsep .. [[test_file" trusted.{MATCH:%s+}|
+ ]])
+ trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust')
+ eq(string.format('%s %s', hash, cwd .. pathsep .. 'test_file'), vim.trim(trust))
+
+ command('trust ++remove')
+ screen:expect([[
+ ^test |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ "]] .. cwd .. pathsep .. [[test_file" removed.{MATCH:%s+}|
+ ]])
+ trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust')
+ eq(string.format(''), vim.trim(trust))
+ end)
+
+ it('deny then remove a file using file path', function()
+ local screen = Screen.new(80, 8)
+ screen:attach()
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ })
+
+ local cwd = funcs.getcwd()
+
+ command('trust ++deny test_file')
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ "]] .. cwd .. pathsep .. [[test_file" denied.{MATCH:%s+}|
+ ]])
+ local trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust')
+ eq(string.format('! %s', cwd .. pathsep .. 'test_file'), vim.trim(trust))
+
+ command('trust ++remove test_file')
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ "]] .. cwd .. pathsep .. [[test_file" removed.{MATCH:%s+}|
+ ]])
+ trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust')
+ eq(string.format(''), vim.trim(trust))
+ end)
+end)
diff --git a/test/functional/ex_cmds/verbose_spec.lua b/test/functional/ex_cmds/verbose_spec.lua
index e6f67ef18e..000e746f1c 100644
--- a/test/functional/ex_cmds/verbose_spec.lua
+++ b/test/functional/ex_cmds/verbose_spec.lua
@@ -77,7 +77,7 @@ nohlsearch
script_location), result)
end)
- it('"Last set" for keymap set by Lua', function()
+ it('"Last set" for mapping set by Lua', function()
local result = exec_capture(':verbose map <leader>key1')
eq(string.format([[
@@ -86,7 +86,7 @@ n \key1 * :echo "test"<CR>
script_location), result)
end)
- it('"Last set" for keymap set by vim.keymap', function()
+ it('"Last set" for mapping set by vim.keymap', function()
local result = exec_capture(':verbose map <leader>key2')
eq(string.format([[
diff --git a/test/functional/ex_cmds/write_spec.lua b/test/functional/ex_cmds/write_spec.lua
index 32fe397c03..1ccd27875e 100644
--- a/test/functional/ex_cmds/write_spec.lua
+++ b/test/functional/ex_cmds/write_spec.lua
@@ -8,7 +8,9 @@ local command = helpers.command
local feed_command = helpers.feed_command
local funcs = helpers.funcs
local meths = helpers.meths
-local iswin = helpers.iswin
+local skip = helpers.skip
+local is_os = helpers.is_os
+local is_ci = helpers.is_ci
local fname = 'Xtest-functional-ex_cmds-write'
local fname_bak = fname .. '~'
@@ -19,6 +21,9 @@ describe(':write', function()
os.remove('test_bkc_file.txt')
os.remove('test_bkc_link.txt')
os.remove('test_fifo')
+ os.remove('test/write/p_opt.txt')
+ os.remove('test/write')
+ os.remove('test')
os.remove(fname)
os.remove(fname_bak)
os.remove(fname_broken)
@@ -34,7 +39,7 @@ describe(':write', function()
it('&backupcopy=auto preserves symlinks', function()
command('set backupcopy=auto')
write_file('test_bkc_file.txt', 'content0')
- if iswin() then
+ if is_os('win') then
command("silent !mklink test_bkc_link.txt test_bkc_file.txt")
else
command("silent !ln -s test_bkc_file.txt test_bkc_link.txt")
@@ -52,9 +57,10 @@ describe(':write', function()
end)
it('&backupcopy=no replaces symlink with new file', function()
+ skip(is_ci('cirrus'))
command('set backupcopy=no')
write_file('test_bkc_file.txt', 'content0')
- if iswin() then
+ if is_os('win') then
command("silent !mklink test_bkc_link.txt test_bkc_file.txt")
else
command("silent !ln -s test_bkc_file.txt test_bkc_link.txt")
@@ -73,7 +79,7 @@ describe(':write', function()
it("appends FIFO file", function()
-- mkfifo creates read-only .lnk files on Windows
- if iswin() or eval("executable('mkfifo')") == 0 then
+ if is_os('win') or eval("executable('mkfifo')") == 0 then
pending('missing "mkfifo" command')
end
@@ -90,11 +96,36 @@ describe(':write', function()
fifo:close()
end)
+ it("++p creates missing parent directories", function()
+ eq(0, eval("filereadable('p_opt.txt')"))
+ command("write ++p p_opt.txt")
+ eq(1, eval("filereadable('p_opt.txt')"))
+ os.remove("p_opt.txt")
+
+ eq(0, eval("filereadable('p_opt.txt')"))
+ command("write ++p ./p_opt.txt")
+ eq(1, eval("filereadable('p_opt.txt')"))
+ os.remove("p_opt.txt")
+
+ eq(0, eval("filereadable('test/write/p_opt.txt')"))
+ command("write ++p test/write/p_opt.txt")
+ eq(1, eval("filereadable('test/write/p_opt.txt')"))
+
+ eq(('Vim(write):E32: No file name'), pcall_err(command, 'write ++p test_write/'))
+ if not is_os('win') then
+ eq(('Vim(write):E17: "'..funcs.fnamemodify('.', ':p:h')..'" is a directory'),
+ pcall_err(command, 'write ++p .'))
+ eq(('Vim(write):E17: "'..funcs.fnamemodify('.', ':p:h')..'" is a directory'),
+ pcall_err(command, 'write ++p ./'))
+ end
+ end)
+
it('errors out correctly', function()
+ skip(is_ci('cirrus'))
command('let $HOME=""')
eq(funcs.fnamemodify('.', ':p:h'), funcs.fnamemodify('.', ':p:h:~'))
-- Message from check_overwrite
- if not iswin() then
+ if not is_os('win') then
eq(('Vim(write):E17: "'..funcs.fnamemodify('.', ':p:h')..'" is a directory'),
pcall_err(command, 'write .'))
end
@@ -113,7 +144,7 @@ describe(':write', function()
funcs.setfperm(fname, 'r--------')
eq('Vim(write):E505: "Xtest-functional-ex_cmds-write" is read-only (add ! to override)',
pcall_err(command, 'write'))
- if iswin() then
+ if is_os('win') then
eq(0, os.execute('del /q/f ' .. fname))
eq(0, os.execute('rd /q/s ' .. fname_bak))
else
@@ -121,8 +152,7 @@ describe(':write', function()
eq(true, os.remove(fname_bak))
end
write_file(fname_bak, 'TTYX')
- -- FIXME: exc_exec('write!') outputs 0 in Windows
- if iswin() then return end
+ skip(is_os('win'), [[FIXME: exc_exec('write!') outputs 0 in Windows]])
lfs.link(fname_bak .. ('/xxxxx'):rep(20), fname, true)
eq('Vim(write):E166: Can\'t open linked file for writing',
pcall_err(command, 'write!'))
diff --git a/test/functional/ex_cmds/wviminfo_spec.lua b/test/functional/ex_cmds/wviminfo_spec.lua
index 7c00daf1d7..861a977ea6 100644
--- a/test/functional/ex_cmds/wviminfo_spec.lua
+++ b/test/functional/ex_cmds/wviminfo_spec.lua
@@ -3,14 +3,14 @@ local lfs = require('lfs')
local clear = helpers.clear
local command, eq, neq, write_file =
helpers.command, helpers.eq, helpers.neq, helpers.write_file
-local iswin = helpers.iswin
local read_file = helpers.read_file
+local is_os = helpers.is_os
describe(':wshada', function()
local shada_file = 'wshada_test'
before_each(function()
- clear{args={'-i', iswin() and 'nul' or '/dev/null',
+ clear{args={'-i', is_os('win') and 'nul' or '/dev/null',
-- Need 'swapfile' for these tests.
'--cmd', 'set swapfile undodir=. directory=. viewdir=. backupdir=. belloff= noshowcmd noruler'},
args_rm={'-n', '-i', '--cmd'}}
diff --git a/test/functional/fixtures/CMakeLists.txt b/test/functional/fixtures/CMakeLists.txt
index a5410c2f8c..6e64b1e4dc 100644
--- a/test/functional/fixtures/CMakeLists.txt
+++ b/test/functional/fixtures/CMakeLists.txt
@@ -1,14 +1,23 @@
-add_executable(tty-test EXCLUDE_FROM_ALL tty-test.c)
-target_link_libraries(tty-test ${LIBUV_LIBRARIES})
+add_library(test_lib INTERFACE)
+if(MINGW)
+ target_link_libraries(test_lib INTERFACE -municode)
+endif()
+if(WIN32)
+ target_compile_definitions(test_lib INTERFACE MSWIN)
+endif()
+target_link_libraries(test_lib INTERFACE nvim)
+add_executable(tty-test EXCLUDE_FROM_ALL tty-test.c)
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(MINGW)
- set_target_properties(printenv-test PROPERTIES LINK_FLAGS -municode)
-endif()
-
add_executable(streams-test EXCLUDE_FROM_ALL streams-test.c)
-target_link_libraries(streams-test ${LIBUV_LIBRARIES})
+
+target_link_libraries(tty-test PRIVATE test_lib)
+target_link_libraries(shell-test PRIVATE test_lib)
+target_link_libraries(pwsh-test PRIVATE test_lib)
+target_link_libraries(printargs-test PRIVATE test_lib)
+target_link_libraries(printenv-test PRIVATE test_lib)
+target_link_libraries(streams-test PRIVATE test_lib)
diff --git a/test/functional/fixtures/api_level_10.mpack b/test/functional/fixtures/api_level_10.mpack
new file mode 100644
index 0000000000..2a1f51045d
--- /dev/null
+++ b/test/functional/fixtures/api_level_10.mpack
Binary files differ
diff --git a/test/functional/fixtures/filename_with_unicode_ααα b/test/functional/fixtures/filename_with_unicode_ααα
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/functional/fixtures/filename_with_unicode_ααα
diff --git a/test/functional/fixtures/printenv-test.c b/test/functional/fixtures/printenv-test.c
index 0e68129543..295b4f04c3 100644
--- a/test/functional/fixtures/printenv-test.c
+++ b/test/functional/fixtures/printenv-test.c
@@ -3,13 +3,13 @@
#include <stdio.h>
-#ifdef WIN32
+#ifdef MSWIN
# include <windows.h>
#else
# include <stdlib.h>
#endif
-#ifdef WIN32
+#ifdef MSWIN
int wmain(int argc, wchar_t **argv)
#else
int main(int argc, char **argv)
@@ -19,7 +19,7 @@ int main(int argc, char **argv)
return 1;
}
-#ifdef WIN32
+#ifdef MSWIN
wchar_t *value = _wgetenv(argv[1]);
if (value == NULL) {
return 1;
diff --git a/test/functional/fixtures/start/nvim-leftpad/lua/async_leftpad.lua b/test/functional/fixtures/start/nvim-leftpad/lua/async_leftpad.lua
index a312572c5b..45226ce24b 100644
--- a/test/functional/fixtures/start/nvim-leftpad/lua/async_leftpad.lua
+++ b/test/functional/fixtures/start/nvim-leftpad/lua/async_leftpad.lua
@@ -1,3 +1,5 @@
return function (val, res)
- vim.loop.new_async(function() _G[res] = require'leftpad'(val) end):send()
+ local handle
+ handle = vim.loop.new_async(function() _G[res] = require'leftpad'(val) handle:close() end)
+ handle:send()
end
diff --git a/test/functional/fixtures/startup-fail.lua b/test/functional/fixtures/startup-fail.lua
new file mode 100644
index 0000000000..adcfe2a201
--- /dev/null
+++ b/test/functional/fixtures/startup-fail.lua
@@ -0,0 +1,7 @@
+-- Test "nvim -l foo.lua …" with a Lua error.
+
+local function main()
+ error('my pearls!!')
+end
+
+main()
diff --git a/test/functional/fixtures/startup.lua b/test/functional/fixtures/startup.lua
new file mode 100644
index 0000000000..d0e60309bd
--- /dev/null
+++ b/test/functional/fixtures/startup.lua
@@ -0,0 +1,35 @@
+-- Test "nvim -l foo.lua …"
+
+local function printbufs()
+ local bufs = ''
+ for _, v in ipairs(vim.api.nvim_list_bufs()) do
+ local b = vim.fn.bufname(v)
+ if b:len() > 0 then
+ bufs = ('%s %s'):format(bufs, b)
+ end
+ end
+ print(('bufs:%s'):format(bufs))
+end
+
+local function parseargs(args)
+ local exitcode = nil
+ for i = 1, #args do
+ if args[i] == '--exitcode' then
+ exitcode = tonumber(args[i + 1])
+ end
+ end
+ return exitcode
+end
+
+local function main()
+ printbufs()
+ print('nvim args:', #vim.v.argv)
+ print('lua args:', vim.inspect(_G.arg))
+
+ local exitcode = parseargs(_G.arg)
+ if type(exitcode) == 'number' then
+ os.exit(exitcode)
+ end
+end
+
+main()
diff --git a/test/functional/fixtures/tty-test.c b/test/functional/fixtures/tty-test.c
index 4438b73a22..6ee7715021 100644
--- a/test/functional/fixtures/tty-test.c
+++ b/test/functional/fixtures/tty-test.c
@@ -5,7 +5,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <uv.h>
-#ifdef _WIN32
+#ifdef MSWIN
# include <windows.h>
#else
# include <unistd.h>
@@ -23,7 +23,7 @@ uv_tty_t tty_out;
bool owns_tty(void); // silence -Wmissing-prototypes
bool owns_tty(void)
{
-#ifdef _WIN32
+#ifdef MSWIN
// XXX: We need to make proper detect owns tty
// HWND consoleWnd = GetConsoleWindow();
// DWORD dwProcessId;
@@ -38,14 +38,14 @@ bool owns_tty(void)
static void walk_cb(uv_handle_t *handle, void *arg)
{
if (!uv_is_closing(handle)) {
-#ifdef WIN32
+#ifdef MSWIN
uv_tty_set_mode(&tty, UV_TTY_MODE_NORMAL);
#endif
uv_close(handle, NULL);
}
}
-#ifndef WIN32
+#ifndef MSWIN
static void sig_handler(int signum)
{
switch (signum) {
@@ -64,7 +64,7 @@ static void sig_handler(int signum)
}
#endif
-#ifdef WIN32
+#ifdef MSWIN
static void sigwinch_cb(uv_signal_t *handle, int signum)
{
int width, height;
@@ -102,7 +102,7 @@ static void read_cb(uv_stream_t *stream, ssize_t cnt, const uv_buf_t *buf)
uv_write_t req;
uv_buf_t b = {
.base = buf->base,
-#ifdef WIN32
+#ifdef MSWIN
.len = (ULONG)cnt
#else
.len = (size_t)cnt
@@ -171,7 +171,7 @@ int main(int argc, char **argv)
uv_prepare_t prepare;
uv_prepare_init(uv_default_loop(), &prepare);
uv_prepare_start(&prepare, prepare_cb);
-#ifndef WIN32
+#ifndef MSWIN
uv_tty_init(uv_default_loop(), &tty, fileno(stderr), 1);
#else
uv_tty_init(uv_default_loop(), &tty, fileno(stdin), 1);
@@ -182,7 +182,7 @@ int main(int argc, char **argv)
uv_tty_set_mode(&tty, UV_TTY_MODE_RAW);
tty.data = &interrupted;
uv_read_start(STRUCT_CAST(uv_stream_t, &tty), alloc_cb, read_cb);
-#ifndef WIN32
+#ifndef MSWIN
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
@@ -196,7 +196,7 @@ int main(int argc, char **argv)
#endif
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
-#ifndef WIN32
+#ifndef MSWIN
// XXX: Without this the SIGHUP handler is skipped on some systems.
sleep(100);
#endif
diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua
index 981cfc306e..6400db9f87 100644
--- a/test/functional/helpers.lua
+++ b/test/functional/helpers.lua
@@ -1,5 +1,4 @@
require('coxpcall')
-local busted = require('busted')
local luv = require('luv')
local lfs = require('lfs')
local mpack = require('mpack')
@@ -29,6 +28,7 @@ local module = {
}
local start_dir = lfs.currentdir()
+local runtime_set = 'set runtimepath^=./build/lib/nvim/'
module.nvim_prog = (
os.getenv('NVIM_PRG')
or global_helpers.test_build_dir .. '/bin/nvim'
@@ -40,6 +40,8 @@ module.nvim_set = (
..' belloff= wildoptions-=pum joinspaces noshowcmd noruler nomore redrawdebug=invalid')
module.nvim_argv = {
module.nvim_prog, '-u', 'NONE', '-i', 'NONE',
+ -- XXX: find treesitter parsers.
+ '--cmd', runtime_set,
'--cmd', module.nvim_set,
'--cmd', 'mapclear',
'--cmd', 'mapclear!',
@@ -51,7 +53,6 @@ if module.nvim_dir == module.nvim_prog then
module.nvim_dir = "."
end
-local iswin = global_helpers.iswin
local prepend_argv
if os.getenv('VALGRIND') then
@@ -109,6 +110,10 @@ function module.request(method, ...)
return rv
end
+function module.request_lua(method, ...)
+ return module.exec_lua([[return vim.api[...](select(2, ...))]], method, ...)
+end
+
function module.next_msg(timeout)
return session:next_message(timeout and timeout or 10000)
end
@@ -237,7 +242,7 @@ function module.run_session(lsession, request_cb, notification_cb, setup_cb, tim
end
loop_running = true
- session:run(on_request, on_notification, on_setup, timeout)
+ lsession:run(on_request, on_notification, on_setup, timeout)
loop_running = false
if last_error then
local err = last_error
@@ -245,7 +250,7 @@ function module.run_session(lsession, request_cb, notification_cb, setup_cb, tim
error(err)
end
- return session.eof_err
+ return lsession.eof_err
end
function module.run(request_cb, notification_cb, setup_cb, timeout)
@@ -272,7 +277,6 @@ function module.command(cmd)
module.request('nvim_command', cmd)
end
-
-- Use for commands which expect nvim to quit.
-- The first argument can also be a timeout.
function module.expect_exit(fn_or_timeout, ...)
@@ -297,12 +301,18 @@ function module.eval(expr)
return module.request('nvim_eval', expr)
end
--- Executes a VimL function.
+-- Executes a VimL function via RPC.
-- Fails on VimL error, but does not update v:errmsg.
function module.call(name, ...)
return module.request('nvim_call_function', name, {...})
end
+-- Executes a VimL function via Lua.
+-- Fails on VimL error, but does not update v:errmsg.
+function module.call_lua(name, ...)
+ return module.exec_lua([[return vim.call(...)]], name, ...)
+end
+
-- Sends user input to Nvim.
-- Does not fail on VimL error, but v:errmsg will be updated.
local function nvim_feed(input)
@@ -345,14 +355,17 @@ end
-- Removes Nvim startup args from `args` matching items in `args_rm`.
--
--- "-u", "-i", "--cmd" are treated specially: their "values" are also removed.
+-- - Special case: "-u", "-i", "--cmd" are treated specially: their "values" are also removed.
+-- - Special case: "runtimepath" will remove only { '--cmd', 'set runtimepath^=…', }
+--
-- Example:
-- args={'--headless', '-u', 'NONE'}
-- args_rm={'--cmd', '-u'}
-- Result:
-- {'--headless'}
--
--- All cases are removed.
+-- All matching cases are removed.
+--
-- Example:
-- args={'--cmd', 'foo', '-N', '--cmd', 'bar'}
-- args_rm={'--cmd', '-u'}
@@ -373,6 +386,9 @@ local function remove_args(args, args_rm)
last = ''
elseif tbl_contains(args_rm, arg) then
last = arg
+ elseif arg == runtime_set and tbl_contains(args_rm, 'runtimepath') then
+ table.remove(new_args) -- Remove the preceding "--cmd".
+ last = ''
else
table.insert(new_args, arg)
end
@@ -380,10 +396,27 @@ local function remove_args(args, args_rm)
return new_args
end
+function module.check_close()
+ if not session then
+ return
+ end
+ local start_time = luv.now()
+ session:close()
+ luv.update_time() -- Update cached value of luv.now() (libuv: uv_now()).
+ local end_time = luv.now()
+ local delta = end_time - start_time
+ if delta > 500 then
+ print("nvim took " .. delta .. " milliseconds to exit after last test\n"..
+ "This indicates a likely problem with the test even if it passed!\n")
+ io.stdout:flush()
+ end
+ session = nil
+end
+
--- @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()
+ if not keep then
+ module.check_close()
end
local child_stream = ChildProcessStream.spawn(
@@ -400,28 +433,6 @@ function module.connect(file_or_address)
return Session.new(stream)
end
--- Calls fn() until it succeeds, up to `max` times or until `max_ms`
--- milliseconds have passed.
-function module.retry(max, max_ms, fn)
- assert(max == nil or max > 0)
- assert(max_ms == nil or max_ms > 0)
- local tries = 1
- local timeout = (max_ms and max_ms or 10000)
- local start_time = luv.now()
- while true do
- local status, result = pcall(fn)
- if status then
- return result
- end
- luv.update_time() -- Update cached value of luv.now() (libuv: uv_now()).
- if (max and tries >= max) or (luv.now() - start_time > timeout) then
- busted.fail(string.format("retry() attempts: %d\n%s", tries, tostring(result)), 2)
- end
- tries = tries + 1
- luv.sleep(20) -- Avoid hot loop...
- end
-end
-
-- Starts a new global Nvim session.
--
-- Parameters are interpreted as startup args, OR a map with these keys:
@@ -435,8 +446,14 @@ end
-- clear('-e')
-- clear{args={'-e'}, args_rm={'-i'}, env={TERM=term}}
function module.clear(...)
+ module.set_session(module.spawn_argv(false, ...))
+end
+
+-- same params as clear, but does returns the session instead
+-- of replacing the default session
+function module.spawn_argv(keep, ...)
local argv, env, io_extra = module.new_argv(...)
- module.set_session(module.spawn(argv, nil, env, nil, io_extra))
+ return module.spawn(argv, nil, env, keep, io_extra)
end
-- Builds an argument list for use in clear().
@@ -526,7 +543,7 @@ function module.source(code)
end
function module.has_powershell()
- return module.eval('executable("'..(iswin() and 'powershell' or 'pwsh')..'")') == 1
+ return module.eval('executable("'..(is_os('win') and 'powershell' or 'pwsh')..'")') == 1
end
--- Sets Nvim shell to powershell.
@@ -539,23 +556,31 @@ function module.set_shell_powershell(fake)
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'}
+ local shell = found and (is_os('win') and 'powershell' or 'pwsh') or module.testprg('pwsh-test')
+ local set_encoding = '[Console]::InputEncoding=[Console]::OutputEncoding=[System.Text.UTF8Encoding]::new();'
+ local cmd = set_encoding..'Remove-Item -Force '..table.concat(is_os('win')
+ and {'alias:cat', 'alias:echo', 'alias:sleep', 'alias:sort'}
or {'alias:echo'}, ',')..';'
module.exec([[
let &shell = ']]..shell..[['
set shellquote= shellxquote=
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'
+ let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode'
]])
return found
end
-function module.nvim(method, ...)
- return module.request('nvim_'..method, ...)
+function module.create_callindex(func)
+ local table = {}
+ setmetatable(table, {
+ __index = function(tbl, arg1)
+ local ret = function(...) return func(arg1, ...) end
+ tbl[arg1] = ret
+ return ret
+ end,
+ })
+ return table
end
local function ui(method, ...)
@@ -566,23 +591,83 @@ function module.nvim_async(method, ...)
session:notify('nvim_'..method, ...)
end
-function module.buffer(method, ...)
- return module.request('nvim_buf_'..method, ...)
-end
+module.async_meths = module.create_callindex(module.nvim_async)
+module.uimeths = module.create_callindex(ui)
-function module.window(method, ...)
- return module.request('nvim_win_'..method, ...)
-end
+local function create_api(request, call)
+ local m = {}
+ function m.nvim(method, ...)
+ return request('nvim_'..method, ...)
+ end
+
+ function m.buffer(method, ...)
+ return request('nvim_buf_'..method, ...)
+ end
+
+ function m.window(method, ...)
+ return request('nvim_win_'..method, ...)
+ end
+
+ function m.tabpage(method, ...)
+ return request('nvim_tabpage_'..method, ...)
+ end
+
+ function m.curbuf(method, ...)
+ if not method then
+ return m.nvim('get_current_buf')
+ end
+ return m.buffer(method, 0, ...)
+ end
+
+ function m.curwin(method, ...)
+ if not method then
+ return m.nvim('get_current_win')
+ end
+ return m.window(method, 0, ...)
+ end
+
+ function m.curtab(method, ...)
+ if not method then
+ return m.nvim('get_current_tabpage')
+ end
+ return m.tabpage(method, 0, ...)
+ end
+
+ m.funcs = module.create_callindex(call)
+ m.meths = module.create_callindex(m.nvim)
+ m.bufmeths = module.create_callindex(m.buffer)
+ m.winmeths = module.create_callindex(m.window)
+ m.tabmeths = module.create_callindex(m.tabpage)
+ m.curbufmeths = module.create_callindex(m.curbuf)
+ m.curwinmeths = module.create_callindex(m.curwin)
+ m.curtabmeths = module.create_callindex(m.curtab)
-function module.tabpage(method, ...)
- return module.request('nvim_tabpage_'..method, ...)
+ return m
end
-function module.curbuf(method, ...)
- if not method then
- return module.nvim('get_current_buf')
+module.rpc = {
+ api = create_api(module.request, module.call),
+}
+
+module.lua = {
+ api = create_api(module.request_lua, module.call_lua),
+}
+
+module.describe_lua_and_rpc = function(describe)
+ return function(what, tests)
+ local function d(flavour)
+ describe(string.format('%s (%s)', what, flavour), function(...)
+ return tests(module[flavour].api, ...)
+ end)
+ end
+
+ d('rpc')
+ d('lua')
end
- return module.buffer(method, 0, ...)
+end
+
+for name, fn in pairs(module.rpc.api) do
+ module[name] = fn
end
function module.poke_eventloop()
@@ -601,20 +686,6 @@ function module.curbuf_contents()
return table.concat(module.curbuf('get_lines', 0, -1, true), '\n')
end
-function module.curwin(method, ...)
- if not method then
- return module.nvim('get_current_win')
- end
- return module.window(method, 0, ...)
-end
-
-function module.curtab(method, ...)
- if not method then
- return module.nvim('get_current_tabpage')
- end
- return module.tabpage(method, 0, ...)
-end
-
function module.expect(contents)
return eq(dedent(contents), module.curbuf_contents())
end
@@ -730,40 +801,16 @@ function module.exc_exec(cmd)
return ret
end
-function module.create_callindex(func)
- local table = {}
- setmetatable(table, {
- __index = function(tbl, arg1)
- local ret = function(...) return func(arg1, ...) end
- tbl[arg1] = ret
- return ret
- end,
- })
- return table
-end
-
--- Helper to skip tests. Returns true in Windows systems.
--- pending_fn is pending() from busted
-function module.pending_win32(pending_fn)
- if iswin() then
- if pending_fn ~= nil then
- pending_fn('FIXME: Windows', function() end)
- end
+function module.skip(cond, reason)
+ if cond then
+ local pending = getfenv(2).pending
+ pending(reason or 'FIXME')
return true
else
return false
end
end
-function module.pending_c_parser(pending_fn)
- local status, _ = unpack(module.exec_lua([[ return {pcall(vim.treesitter.require_language, 'c')} ]]))
- if not status then
- pending_fn 'no C parser, skipping'
- return true
- end
- return false
-end
-
-- Calls pending() and returns `true` if the system is too slow to
-- run fragile or expensive tests. Else returns `false`.
function module.skip_fragile(pending_fn, cond)
@@ -780,17 +827,6 @@ function module.skip_fragile(pending_fn, cond)
return false
end
-module.funcs = module.create_callindex(module.call)
-module.meths = module.create_callindex(module.nvim)
-module.async_meths = module.create_callindex(module.nvim_async)
-module.uimeths = module.create_callindex(ui)
-module.bufmeths = module.create_callindex(module.buffer)
-module.winmeths = module.create_callindex(module.window)
-module.tabmeths = module.create_callindex(module.tabpage)
-module.curbufmeths = module.create_callindex(module.curbuf)
-module.curwinmeths = module.create_callindex(module.curwin)
-module.curtabmeths = module.create_callindex(module.curtab)
-
function module.exec(code)
return module.meths.exec(code, false)
end
@@ -804,13 +840,13 @@ function module.exec_lua(code, ...)
end
function module.get_pathsep()
- return iswin() and '\\' or '/'
+ return is_os('win') 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 '/'
+ return is_os('win') and (module.nvim_dir:sub(1,2)..pathsep) or '/'
end
--- Gets the full `…/build/bin/{name}` path of a test program produced by
@@ -818,7 +854,7 @@ end
---
--- @param name (string) Name of the test program.
function module.testprg(name)
- local ext = module.iswin() and '.exe' or ''
+ local ext = module.is_os('win') and '.exe' or ''
return ('%s/%s%s'):format(module.nvim_dir, name, ext)
end
@@ -845,7 +881,7 @@ function module.missing_provider(provider)
end
function module.alter_slashes(obj)
- if not iswin() then
+ if not is_os('win') then
return obj
end
if type(obj) == 'string' then
@@ -863,7 +899,7 @@ function module.alter_slashes(obj)
end
local load_factor = 1
-if global_helpers.isCI() then
+if global_helpers.is_ci() then
-- Compute load factor only once (but outside of any tests).
module.clear()
module.request('nvim_command', 'source src/nvim/testdir/load.vim')
@@ -896,14 +932,14 @@ end
-- Kill process with given pid
function module.os_kill(pid)
- return os.execute((iswin()
+ return os.execute((is_os('win')
and 'taskkill /f /t /pid '..pid..' > nul'
or 'kill -9 '..pid..' > /dev/null'))
end
-- Create folder with non existing parents
function module.mkdir_p(path)
- return os.execute((iswin()
+ return os.execute((is_os('win')
and 'mkdir '..path
or 'mkdir -p '..path))
end
diff --git a/test/functional/legacy/006_argument_list_spec.lua b/test/functional/legacy/006_argument_list_spec.lua
deleted file mode 100644
index d269bf8ec9..0000000000
--- a/test/functional/legacy/006_argument_list_spec.lua
+++ /dev/null
@@ -1,85 +0,0 @@
--- Test for autocommand that redefines the argument list, when doing ":all".
-
-local helpers = require('test.functional.helpers')(after_each)
-local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert
-local command, dedent, eq = helpers.command, helpers.dedent, helpers.eq
-local curbuf_contents = helpers.curbuf_contents
-local poke_eventloop = helpers.poke_eventloop
-
-describe('argument list', function()
- setup(clear)
-
- it('is working', function()
- insert([[
- start of test file Xxx
- this is a test
- this is a test
- this is a test
- this is a test
- end of test file Xxx]])
- poke_eventloop()
-
- command('au BufReadPost Xxx2 next Xxx2 Xxx1')
- command('/^start of')
-
- -- Write test file Xxx1
- feed('A1<Esc>:.,/end of/w! Xxx1<cr>')
-
- -- Write test file Xxx2
- feed('$r2:.,/end of/w! Xxx2<cr>')
-
- -- Write test file Xxx3
- feed('$r3:.,/end of/w! Xxx3<cr>')
- poke_eventloop()
-
- -- Redefine arglist; go to Xxx1
- command('next! Xxx1 Xxx2 Xxx3')
-
- -- Open window for all args
- command('all')
-
- -- Write contents of Xxx1
- command('%yank A')
-
- -- Append contents of last window (Xxx1)
- feed('')
- poke_eventloop()
- command('%yank A')
-
- -- should now be in Xxx2
- command('rew')
-
- -- Append contents of Xxx2
- command('%yank A')
-
- command('%d')
- command('0put=@a')
- command('$d')
-
- eq(dedent([[
- start of test file Xxx1
- this is a test
- this is a test
- this is a test
- this is a test
- end of test file Xxx
- start of test file Xxx1
- this is a test
- this is a test
- this is a test
- this is a test
- end of test file Xxx
- start of test file Xxx2
- this is a test
- this is a test
- this is a test
- this is a test
- end of test file Xxx]]), curbuf_contents())
- end)
-
- teardown(function()
- os.remove('Xxx1')
- os.remove('Xxx2')
- os.remove('Xxx3')
- end)
-end)
diff --git a/test/functional/legacy/011_autocommands_spec.lua b/test/functional/legacy/011_autocommands_spec.lua
index 0fa9290f3c..7ae851467f 100644
--- a/test/functional/legacy/011_autocommands_spec.lua
+++ b/test/functional/legacy/011_autocommands_spec.lua
@@ -18,11 +18,11 @@ local clear, feed_command, expect, eq, neq, dedent, write_file, feed =
helpers.clear, helpers.feed_command, helpers.expect, helpers.eq, helpers.neq,
helpers.dedent, helpers.write_file, helpers.feed
local command = helpers.command
-local iswin = helpers.iswin
local read_file = helpers.read_file
+local is_os = helpers.is_os
local function has_gzip()
- local null = iswin() and 'nul' or '/dev/null'
+ local null = is_os('win') and 'nul' or '/dev/null'
return os.execute('gzip --help >' .. null .. ' 2>&1') == 0
end
diff --git a/test/functional/legacy/025_jump_tag_hidden_spec.lua b/test/functional/legacy/025_jump_tag_hidden_spec.lua
index dd89a3680e..15bd56a601 100644
--- a/test/functional/legacy/025_jump_tag_hidden_spec.lua
+++ b/test/functional/legacy/025_jump_tag_hidden_spec.lua
@@ -23,7 +23,7 @@ describe('jump to a tag with hidden set', function()
feed_command('set hidden')
-- Create a link from test25.dir to the current directory.
- if helpers.iswin() then
+ if helpers.is_os('win') then
feed_command('!rd /q/s test25.dir')
feed_command('!mklink /j test25.dir .')
else
@@ -33,7 +33,7 @@ describe('jump to a tag with hidden set', function()
-- Create tags.text, with the current directory name inserted.
feed_command('/tags line')
- feed_command('r !' .. (helpers.iswin() and 'cd' or 'pwd'))
+ feed_command('r !' .. (helpers.is_os('win') and 'cd' or 'pwd'))
feed('d$/test<cr>')
feed('hP:.w! tags.test<cr>')
@@ -44,7 +44,7 @@ describe('jump to a tag with hidden set', function()
feed('G<C-]> x:yank a<cr>')
feed_command("call delete('tags.test')")
feed_command("call delete('Xxx')")
- if helpers.iswin() then
+ if helpers.is_os('win') then
feed_command('!rd /q test25.dir')
else
feed_command('!rm -f test25.dir')
diff --git a/test/functional/legacy/030_fileformats_spec.lua b/test/functional/legacy/030_fileformats_spec.lua
index 15dbd05cf5..e88afd9c47 100644
--- a/test/functional/legacy/030_fileformats_spec.lua
+++ b/test/functional/legacy/030_fileformats_spec.lua
@@ -255,7 +255,7 @@ describe('fileformats option', function()
-- Assert buffer contents. This has to be done manually as
-- helpers.expect() calls helpers.dedent() which messes up the white space
- -- and carrige returns.
+ -- and carriage returns.
eq(
'unix\n'..
'unix\n'..
diff --git a/test/functional/legacy/045_folding_spec.lua b/test/functional/legacy/045_folding_spec.lua
deleted file mode 100644
index 7d7856fd37..0000000000
--- a/test/functional/legacy/045_folding_spec.lua
+++ /dev/null
@@ -1,214 +0,0 @@
--- Tests for folding.
-local Screen = require('test.functional.ui.screen')
-
-local helpers = require('test.functional.helpers')(after_each)
-local feed, insert, feed_command, expect_any =
- helpers.feed, helpers.insert, helpers.feed_command, helpers.expect_any
-
-describe('folding', function()
- local screen
-
- before_each(function()
- helpers.clear()
-
- screen = Screen.new(20, 8)
- screen:attach()
- end)
-
- it('creation, opening, moving (to the end) and closing', function()
- insert([[
- 1 aa
- 2 bb
- 3 cc
- last
- ]])
-
- -- Basic test if a fold can be created, opened, moving to the end and
- -- closed.
- feed_command('1')
- feed('zf2j')
- feed_command('call append("$", "manual " . getline(foldclosed(".")))')
- feed('zo')
- feed_command('call append("$", foldclosed("."))')
- feed(']z')
- feed_command('call append("$", getline("."))')
- feed('zc')
- feed_command('call append("$", getline(foldclosed(".")))')
-
- expect_any([[
- manual 1 aa
- -1
- 3 cc
- 1 aa]])
- end)
-
- it("foldmethod=marker", function()
- screen:try_resize(20, 10)
- insert([[
- dd {{{
- ee {{{ }}}
- ff }}}
- ]])
- feed_command('set fdm=marker fdl=1')
- feed_command('2')
- feed_command('call append("$", "line 2 foldlevel=" . foldlevel("."))')
- feed('[z')
- feed_command('call append("$", foldlevel("."))')
- feed('jo{{ <esc>r{jj') -- writes '{{{' and moves 2 lines bot
- feed_command('call append("$", foldlevel("."))')
- feed('kYpj')
- feed_command('call append("$", foldlevel("."))')
-
- helpers.poke_eventloop()
- screen:expect([[
- dd {{{ |
- ee {{{ }}} |
- {{{ |
- ff }}} |
- ff }}} |
- ^ |
- line 2 foldlevel=2 |
- 1 |
- 1 |
- |
- ]])
-
- end)
-
- it("foldmethod=indent", function()
- screen:try_resize(20, 8)
- feed_command('set fdm=indent sw=2')
- insert([[
- aa
- bb
- cc
- last
- ]])
- feed_command('call append("$", "foldlevel line3=" . foldlevel(3))')
- feed_command('call append("$", foldlevel(2))')
- feed('zR')
-
- helpers.poke_eventloop()
- screen:expect([[
- aa |
- bb |
- cc |
- last |
- ^ |
- foldlevel line3=2 |
- 1 |
- |
- ]])
- end)
-
- it("foldmethod=syntax", function()
- screen:try_resize(35, 15)
- insert([[
- 1 aa
- 2 bb
- 3 cc
- 4 dd {{{
- 5 ee {{{ }}}
- 6 ff }}}
- 7 gg
- 8 hh
- 9 ii
- a jj
- b kk
- last]])
- feed_command('set fdm=syntax fdl=0')
- feed_command('syn region Hup start="dd" end="ii" fold contains=Fd1,Fd2,Fd3')
- feed_command('syn region Fd1 start="ee" end="ff" fold contained')
- feed_command('syn region Fd2 start="gg" end="hh" fold contained')
- feed_command('syn region Fd3 start="commentstart" end="commentend" fold contained')
- feed('Gzk')
- feed_command('call append("$", "folding " . getline("."))')
- feed('k')
- feed_command('call append("$", getline("."))')
- feed('jAcommentstart <esc>Acommentend<esc>')
- feed_command('set fdl=1')
- feed('3j')
- feed_command('call append("$", getline("."))')
- feed_command('set fdl=0')
- feed('zO<C-L>j') -- <C-L> redraws screen
- feed_command('call append("$", getline("."))')
- feed_command('set fdl=0')
- expect_any([[
- folding 9 ii
- 3 cc
- 9 ii
- a jj]])
- end)
-
- it("foldmethod=expression", function()
- insert([[
- 1 aa
- 2 bb
- 3 cc
- 4 dd {{{
- 5 ee {{{ }}}
- 6 ff }}}
- 7 gg
- 8 hh
- 9 ii
- a jj
- b kk
- last ]])
-
- feed_command([[
- fun Flvl()
- let l = getline(v:lnum)
- if l =~ "bb$"
- return 2
- elseif l =~ "gg$"
- return "s1"
- elseif l =~ "ii$"
- return ">2"
- elseif l =~ "kk$"
- return "0"
- endif
- return "="
- endfun
- ]])
- feed_command('set fdm=expr fde=Flvl()')
- feed_command('/bb$')
- feed_command('call append("$", "expr " . foldlevel("."))')
- feed_command('/hh$')
- feed_command('call append("$", foldlevel("."))')
- feed_command('/ii$')
- feed_command('call append("$", foldlevel("."))')
- feed_command('/kk$')
- feed_command('call append("$", foldlevel("."))')
-
- expect_any([[
- expr 2
- 1
- 2
- 0]])
- end)
-
- it('can be opened after :move', function()
- -- luacheck: ignore
- screen:try_resize(35, 8)
- insert([[
- Test fdm=indent and :move bug END
- line2
- Test fdm=indent START
- line3
- line4]])
- feed_command('set noai nosta ')
- feed_command('set fdm=indent')
- feed_command('1m1')
- feed('2jzc')
- feed_command('m0')
- feed('zR')
-
- expect_any([[
- Test fdm=indent START
- line3
- line4
- Test fdm=indent and :move bug END
- line2]])
- end)
-end)
-
diff --git a/test/functional/legacy/055_list_and_dict_types_spec.lua b/test/functional/legacy/055_list_and_dict_types_spec.lua
index 4d71a526c1..75294b3786 100644
--- a/test/functional/legacy/055_list_and_dict_types_spec.lua
+++ b/test/functional/legacy/055_list_and_dict_types_spec.lua
@@ -330,214 +330,6 @@ describe('list and dictionary types', function()
same list: 1]])
end)
- it('locked variables (part 1)', function()
- source([=[
- let l = []
- for depth in range(5)
- $put ='depth is ' . depth
- for u in range(3)
- unlet l
- let l = [0, [1, [2, 3]], {4: 5, 6: {7: 8}}]
- exe "lockvar " . depth . " l"
- if u == 1
- exe "unlockvar l"
- elseif u == 2
- exe "unlockvar " . depth . " l"
- endif
- let ps = islocked("l") . islocked("l[1]") . islocked("l[1][1]") .
- \ islocked("l[1][1][0]") . '-' . islocked("l[2]") .
- \ islocked("l[2]['6']") . islocked("l[2]['6'][7]")
- $put =ps
- let ps = ''
- try
- let l[1][1][0] = 99
- let ps .= 'p'
- catch
- let ps .= 'F'
- endtry
- try
- let l[1][1] = [99]
- let ps .= 'p'
- catch
- let ps .= 'F'
- endtry
- try
- let l[1] = [99]
- let ps .= 'p'
- catch
- let ps .= 'F'
- endtry
- try
- let l[2]['6'][7] = 99
- let ps .= 'p'
- catch
- let ps .= 'F'
- endtry
- try
- let l[2][6] = {99: 99}
- let ps .= 'p'
- catch
- let ps .= 'F'
- endtry
- try
- let l[2] = {99: 99}
- let ps .= 'p'
- catch
- let ps .= 'F'
- endtry
- try
- let l = [99]
- let ps .= 'p'
- catch
- let ps .= 'F'
- endtry
- $put =ps
- endfor
- endfor]=])
- expect([[
-
- depth is 0
- 0000-000
- ppppppp
- 0000-000
- ppppppp
- 0000-000
- ppppppp
- depth is 1
- 1000-000
- ppppppF
- 0000-000
- ppppppp
- 0000-000
- ppppppp
- depth is 2
- 1100-100
- ppFppFF
- 0000-000
- ppppppp
- 0000-000
- ppppppp
- depth is 3
- 1110-110
- pFFpFFF
- 0010-010
- pFppFpp
- 0000-000
- ppppppp
- depth is 4
- 1111-111
- FFFFFFF
- 0011-011
- FFpFFpp
- 0000-000
- ppppppp]])
- end)
-
- -- TODO In the original test the 5th line of this source() call was used.
- -- But now the test only passes if I comment it.
- it('unletting locked variables', function()
- source([=[
- let l = []
- for depth in range(5)
- $put ='depth is ' . depth
- for u in range(3)
- "unlet l
- let l = [0, [1, [2, 3]], {4: 5, 6: {7: 8}}]
- exe "lockvar " . depth . " l"
- if u == 1
- exe "unlockvar l"
- elseif u == 2
- exe "unlockvar " . depth . " l"
- endif
- let ps = islocked("l") . islocked("l[1]") . islocked("l[1][1]") .
- \ islocked("l[1][1][0]") . '-' . islocked("l[2]") .
- \ islocked("l[2]['6']") . islocked("l[2]['6'][7]")
- $put =ps
- let ps = ''
- try
- unlet l[2]['6'][7]
- let ps .= 'p'
- catch
- let ps .= 'F'
- endtry
- try
- unlet l[2][6]
- let ps .= 'p'
- catch
- let ps .= 'F'
- endtry
- try
- unlet l[2]
- let ps .= 'p'
- catch
- let ps .= 'F'
- endtry
- try
- unlet l[1][1][0]
- let ps .= 'p'
- catch
- let ps .= 'F'
- endtry
- try
- unlet l[1][1]
- let ps .= 'p'
- catch
- let ps .= 'F'
- endtry
- try
- unlet l[1]
- let ps .= 'p'
- catch
- let ps .= 'F'
- endtry
- try
- unlet l
- let ps .= 'p'
- catch
- let ps .= 'F'
- endtry
- $put =ps
- endfor
- endfor]=])
- expect([[
-
- depth is 0
- 0000-000
- ppppppp
- 0000-000
- ppppppp
- 0000-000
- ppppppp
- depth is 1
- 1000-000
- ppFppFp
- 0000-000
- ppppppp
- 0000-000
- ppppppp
- depth is 2
- 1100-100
- pFFpFFp
- 0000-000
- ppppppp
- 0000-000
- ppppppp
- depth is 3
- 1110-110
- FFFFFFp
- 0010-010
- FppFppp
- 0000-000
- ppppppp
- depth is 4
- 1111-111
- FFFFFFp
- 0011-011
- FppFppp
- 0000-000
- ppppppp]])
- end)
-
it('locked variables and :unlet or list / dict functions', function()
source([[
$put ='Locks and commands or functions:'
@@ -676,30 +468,6 @@ describe('list and dictionary types', function()
['a', 'b', 3]]=])
end)
- it('locked variables (part 2)', function()
- feed_command(
- 'let l = [1, 2, 3, 4]',
- 'lockvar! l',
- '$put =string(l)',
- 'unlockvar l[1]',
- 'unlet l[0:1]',
- '$put =string(l)',
- 'unlet l[1:2]',
- '$put =string(l)',
- 'unlockvar l[1]',
- 'let l[0:1] = [0, 1]',
- '$put =string(l)',
- 'let l[1:2] = [0, 1]',
- '$put =string(l)')
- expect([=[
-
- [1, 2, 3, 4]
- [1, 2, 3, 4]
- [1, 2, 3, 4]
- [1, 2, 3, 4]
- [1, 2, 3, 4]]=])
- end)
-
it(':lockvar/islocked() triggering script autoloading.', function()
source([[
set rtp+=test/functional/fixtures
@@ -907,7 +675,7 @@ describe('list and dictionary types', function()
feed('o<C-R>=a<CR><esc>')
feed_command('lang C')
feed_command('redir => a')
- -- The test failes if this is not in one line.
+ -- The test fails if this is not in one line.
feed_command("try|foobar|catch|let a = matchstr(v:exception,'^[^ ]*')|endtry")
feed_command('redir END')
feed('o<C-R>=a<CR><esc>')
diff --git a/test/functional/legacy/063_match_and_matchadd_spec.lua b/test/functional/legacy/063_match_and_matchadd_spec.lua
index d164d9c02f..235a826640 100644
--- a/test/functional/legacy/063_match_and_matchadd_spec.lua
+++ b/test/functional/legacy/063_match_and_matchadd_spec.lua
@@ -3,10 +3,8 @@
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
-local eval, clear, command = helpers.eval, helpers.clear, helpers.command
-local eq, neq = helpers.eq, helpers.neq
+local clear, command = helpers.clear, helpers.command
local insert = helpers.insert
-local pcall_err = helpers.pcall_err
describe('063: Test for ":match", "matchadd()" and related functions', function()
setup(clear)
@@ -19,105 +17,9 @@ describe('063: Test for ":match", "matchadd()" and related functions', function(
[1] = {background = Screen.colors.Red},
})
- -- Check that "matcharg()" returns the correct group and pattern if a match
- -- is defined.
command("highlight MyGroup1 term=bold ctermbg=red guibg=red")
command("highlight MyGroup2 term=italic ctermbg=green guibg=green")
command("highlight MyGroup3 term=underline ctermbg=blue guibg=blue")
- command("match MyGroup1 /TODO/")
- command("2match MyGroup2 /FIXME/")
- command("3match MyGroup3 /XXX/")
- eq({'MyGroup1', 'TODO'}, eval('matcharg(1)'))
- eq({'MyGroup2', 'FIXME'}, eval('matcharg(2)'))
- eq({'MyGroup3', 'XXX'}, eval('matcharg(3)'))
-
- -- Check that "matcharg()" returns an empty list if the argument is not 1,
- -- 2 or 3 (only 0 and 4 are tested).
- eq({}, eval('matcharg(0)'))
- eq({}, eval('matcharg(4)'))
-
- -- Check that "matcharg()" returns ['', ''] if a match is not defined.
- command("match")
- command("2match")
- command("3match")
- eq({'', ''}, eval('matcharg(1)'))
- eq({'', ''}, eval('matcharg(2)'))
- eq({'', ''}, eval('matcharg(3)'))
-
- -- Check that "matchadd()" and "getmatches()" agree on added matches and
- -- that default values apply.
- command("let m1 = matchadd('MyGroup1', 'TODO')")
- command("let m2 = matchadd('MyGroup2', 'FIXME', 42)")
- command("let m3 = matchadd('MyGroup3', 'XXX', 60, 17)")
- eq({{group = 'MyGroup1', pattern = 'TODO', priority = 10, id = 4},
- {group = 'MyGroup2', pattern = 'FIXME', priority = 42, id = 5},
- {group = 'MyGroup3', pattern = 'XXX', priority = 60, id = 17}},
- eval('getmatches()'))
-
- -- Check that "matchdelete()" deletes the matches defined in the previous
- -- test correctly.
- command("call matchdelete(m1)")
- command("call matchdelete(m2)")
- command("call matchdelete(m3)")
- eq({}, eval('getmatches()'))
-
- --- Check that "matchdelete()" returns 0 if successful and otherwise -1.
- command("let m = matchadd('MyGroup1', 'TODO')")
- eq(0, eval('matchdelete(m)'))
-
- -- matchdelete throws error and returns -1 on failure
- neq(true, pcall(function() eval('matchdelete(42)') end))
- eq('Vim(let):E803: ID not found: 42', pcall_err(command, 'let r2 = matchdelete(42)'))
-
- -- Check that "clearmatches()" clears all matches defined by ":match" and
- -- "matchadd()".
- command("let m1 = matchadd('MyGroup1', 'TODO')")
- command("let m2 = matchadd('MyGroup2', 'FIXME', 42)")
- command("let m3 = matchadd('MyGroup3', 'XXX', 60, 17)")
- command("match MyGroup1 /COFFEE/")
- command("2match MyGroup2 /HUMPPA/")
- command("3match MyGroup3 /VIM/")
- command("call clearmatches()")
- eq({}, eval('getmatches()'))
-
- -- Check that "setmatches()" restores a list of matches saved by
- -- "getmatches()" without changes. (Matches with equal priority must also
- -- remain in the same order.)
- command("let m1 = matchadd('MyGroup1', 'TODO')")
- command("let m2 = matchadd('MyGroup2', 'FIXME', 42)")
- command("let m3 = matchadd('MyGroup3', 'XXX', 60, 17)")
- command("match MyGroup1 /COFFEE/")
- command("2match MyGroup2 /HUMPPA/")
- command("3match MyGroup3 /VIM/")
- command("let ml = getmatches()")
- local ml = eval("ml")
- command("call clearmatches()")
- command("call setmatches(ml)")
- eq(ml, eval('getmatches()'))
-
- -- Check that "setmatches()" can correctly restore the matches from matchaddpos()
- command("call clearmatches()")
- command("call setmatches(ml)")
- eq(ml, eval('getmatches()'))
-
- -- Check that "setmatches()" will not add two matches with the same ID. The
- -- expected behaviour (for now) is to add the first match but not the
- -- second and to return -1.
- eq('Vim(let):E801: ID already taken: 1',
- pcall_err(command, "let r1 = setmatches([{'group': 'MyGroup1', 'pattern': 'TODO', 'priority': 10, 'id': 1}, {'group': 'MyGroup2', 'pattern': 'FIXME', 'priority': 10, 'id': 1}])"))
- eq({{group = 'MyGroup1', pattern = 'TODO', priority = 10, id = 1}}, eval('getmatches()'))
-
- -- Check that "setmatches()" returns 0 if successful and otherwise -1.
- -- (A range of valid and invalid input values are tried out to generate the
- -- return values.)
- eq(0,eval("setmatches([])"))
- eq(0,eval("setmatches([{'group': 'MyGroup1', 'pattern': 'TODO', 'priority': 10, 'id': 1}])"))
- command("call clearmatches()")
- eq('Vim(let):E714: List required', pcall_err(command, 'let rf1 = setmatches(0)'))
- eq('Vim(let):E474: List item 0 is either not a dictionary or an empty one',
- pcall_err(command, 'let rf2 = setmatches([0])'))
- eq('Vim(let):E474: List item 0 is missing one of the required keys',
- pcall_err(command, "let rf3 = setmatches([{'wrong key': 'wrong value'}])"))
-- Check that "matchaddpos()" positions matches correctly
insert('abcdefghijklmnopq')
diff --git a/test/functional/legacy/072_undo_file_spec.lua b/test/functional/legacy/072_undo_file_spec.lua
index b4927e779e..80665027c3 100644
--- a/test/functional/legacy/072_undo_file_spec.lua
+++ b/test/functional/legacy/072_undo_file_spec.lua
@@ -69,7 +69,7 @@ describe('72', function()
feed_command('set undofile ul=100')
feed('uuuuuu:w >>test.out<cr>')
- ---- Open the output to see if it meets the expections
+ ---- Open the output to see if it meets the expectations
feed_command('e! test.out')
-- Assert buffer contents.
diff --git a/test/functional/legacy/083_tag_search_with_file_encoding_spec.lua b/test/functional/legacy/083_tag_search_with_file_encoding_spec.lua
index e94b46ca66..54620c7104 100644
--- a/test/functional/legacy/083_tag_search_with_file_encoding_spec.lua
+++ b/test/functional/legacy/083_tag_search_with_file_encoding_spec.lua
@@ -26,7 +26,7 @@ describe('tag search with !_TAG_FILE_ENCODING', function()
'!_TAG_FILE_ENCODING cp932 //\n' ..
'\130`\130a\130b Xtags2.txt /\130`\130a\130b\n'
)
- -- The last file is very long but repetetive and can be generated on the
+ -- The last file is very long but repetitive and can be generated on the
-- fly.
local text = helpers.dedent([[
!_TAG_FILE_SORTED 1 //
diff --git a/test/functional/legacy/097_glob_path_spec.lua b/test/functional/legacy/097_glob_path_spec.lua
index dd5a26ad3b..a62dc4d4c8 100644
--- a/test/functional/legacy/097_glob_path_spec.lua
+++ b/test/functional/legacy/097_glob_path_spec.lua
@@ -10,7 +10,7 @@ describe('glob() and globpath()', function()
setup(clear)
setup(function()
- if helpers.iswin() then
+ if helpers.is_os('win') then
os.execute("md sautest\\autoload")
os.execute(".>sautest\\autoload\\Test104.vim 2>nul")
os.execute(".>sautest\\autoload\\footest.vim 2>nul")
@@ -28,7 +28,7 @@ describe('glob() and globpath()', function()
-- Consistent sorting of file names
command('set nofileignorecase')
- if helpers.iswin() then
+ if helpers.is_os('win') then
command([[$put =glob('Xxx{')]])
command([[$put =glob('Xxx$')]])
@@ -72,7 +72,7 @@ describe('glob() and globpath()', function()
end)
teardown(function()
- if helpers.iswin() then
+ if helpers.is_os('win') then
os.execute('del /q/f Xxx{ Xxx$')
os.execute('rd /q /s sautest')
else
diff --git a/test/functional/legacy/arglist_spec.lua b/test/functional/legacy/arglist_spec.lua
index 4d9e88c446..a15809907b 100644
--- a/test/functional/legacy/arglist_spec.lua
+++ b/test/functional/legacy/arglist_spec.lua
@@ -3,7 +3,6 @@
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 expect_exit = helpers.expect_exit
local feed = helpers.feed
local pcall_err = helpers.pcall_err
@@ -11,233 +10,6 @@ local pcall_err = helpers.pcall_err
describe('argument list commands', function()
before_each(clear)
- local function init_abc()
- command('args a b c')
- command('next')
- end
-
- local function reset_arglist()
- command('arga a | %argd')
- end
-
- local function assert_fails(cmd, err)
- neq(nil, exc_exec(cmd):find(err))
- end
-
- it('test that argidx() works', function()
- command('args a b c')
- command('last')
- eq(2, eval('argidx()'))
- command('%argdelete')
- eq(0, eval('argidx()'))
-
- command('args a b c')
- eq(0, eval('argidx()'))
- command('next')
- eq(1, eval('argidx()'))
- command('next')
- eq(2, eval('argidx()'))
- command('1argdelete')
- eq(1, eval('argidx()'))
- command('1argdelete')
- eq(0, eval('argidx()'))
- command('1argdelete')
- eq(0, eval('argidx()'))
- end)
-
- it('test that argadd() works', function()
- command('%argdelete')
- command('argadd a b c')
- eq(0, eval('argidx()'))
-
- command('%argdelete')
- command('argadd a')
- eq(0, eval('argidx()'))
- command('argadd b c d')
- eq(0, eval('argidx()'))
-
- init_abc()
- command('argadd x')
- eq({'a', 'b', 'x', 'c'}, eval('argv()'))
- eq(1, eval('argidx()'))
-
- init_abc()
- command('0argadd x')
- eq({'x', 'a', 'b', 'c'}, eval('argv()'))
- eq(2, eval('argidx()'))
-
- init_abc()
- command('1argadd x')
- eq({'a', 'x', 'b', 'c'}, eval('argv()'))
- eq(2, eval('argidx()'))
-
- init_abc()
- command('$argadd x')
- eq({'a', 'b', 'c', 'x'}, eval('argv()'))
- eq(1, eval('argidx()'))
-
- init_abc()
- command('$argadd x')
- command('+2argadd y')
- eq({'a', 'b', 'c', 'x', 'y'}, eval('argv()'))
- eq(1, eval('argidx()'))
-
- command('%argd')
- command('edit d')
- command('arga')
- eq(1, eval('len(argv())'))
- eq('d', eval('get(argv(), 0, "")'))
-
- command('%argd')
- command('new')
- command('arga')
- eq(0, eval('len(argv())'))
- end)
-
- it('test for 0argadd and 0argedit', function()
- reset_arglist()
-
- command('arga a b c d')
- command('2argu')
- command('0arga added')
- eq({'added', 'a', 'b', 'c', 'd'}, eval('argv()'))
-
- command('%argd')
- command('arga a b c d')
- command('2argu')
- command('0arge edited')
- eq({'edited', 'a', 'b', 'c', 'd'}, eval('argv()'))
-
- command('2argu')
- command('arga third')
- eq({'edited', 'a', 'third', 'b', 'c', 'd'}, eval('argv()'))
- end)
-
- it('test for argc()', function()
- reset_arglist()
- eq(0, eval('argc()'))
- command('argadd a b')
- eq(2, eval('argc()'))
- end)
-
- it('test for arglistid()', function()
- reset_arglist()
- command('arga a b')
- eq(0, eval('arglistid()'))
- command('split')
- command('arglocal')
- eq(1, eval('arglistid()'))
- command('tabnew | tabfirst')
- eq(0, eval('arglistid(2)'))
- eq(1, eval('arglistid(1, 1)'))
- eq(0, eval('arglistid(2, 1)'))
- eq(1, eval('arglistid(1, 2)'))
- command('tabonly | only | enew!')
- command('argglobal')
- eq(0, eval('arglistid()'))
- end)
-
- it('test for argv()', function()
- reset_arglist()
- eq({}, eval('argv()'))
- eq('', eval('argv(2)'))
- command('argadd a b c d')
- eq('c', eval('argv(2)'))
- end)
-
- it('test for :argedit command', function()
- reset_arglist()
- command('argedit a')
- eq({'a'}, eval('argv()'))
- eq('a', eval('expand("%:t")'))
- command('argedit b')
- eq({'a', 'b'}, eval('argv()'))
- eq('b', eval('expand("%:t")'))
- command('argedit a')
- eq({'a', 'b', 'a'}, eval('argv()'))
- eq('a', eval('expand("%:t")'))
- command('argedit c')
- eq({'a', 'b', 'a', 'c'}, eval('argv()'))
- command('0argedit x')
- eq({'x', 'a', 'b', 'a', 'c'}, eval('argv()'))
- command('set nohidden')
- command('enew! | set modified')
- assert_fails('argedit y', 'E37:')
- command('argedit! y')
- eq({'x', 'y', 'y', 'a', 'b', 'a', 'c'}, eval('argv()'))
- command('set hidden')
- command('%argd')
- command('argedit a b')
- eq({'a', 'b'}, eval('argv()'))
- end)
-
- it('test for :argdelete command', function()
- reset_arglist()
- command('args aa a aaa b bb')
- command('argdelete a*')
- eq({'b', 'bb'}, eval('argv()'))
- eq('aa', eval('expand("%:t")'))
- command('last')
- command('argdelete %')
- eq({'b'}, eval('argv()'))
- assert_fails('argdelete', 'E610:')
- assert_fails('1,100argdelete', 'E16:')
- reset_arglist()
- command('args a b c d')
- command('next')
- command('argdel')
- eq({'a', 'c', 'd'}, eval('argv()'))
- command('%argdel')
- end)
-
- it('test for the :next, :prev, :first, :last, :rewind commands', function()
- reset_arglist()
- command('args a b c d')
- command('last')
- eq(3, eval('argidx()'))
- assert_fails('next', 'E165:')
- command('prev')
- eq(2, eval('argidx()'))
- command('Next')
- eq(1, eval('argidx()'))
- command('first')
- eq(0, eval('argidx()'))
- assert_fails('prev', 'E164:')
- command('3next')
- eq(3, eval('argidx()'))
- command('rewind')
- eq(0, eval('argidx()'))
- 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')")
- command("call writefile(['test file Xxx2'], 'Xxx2')")
- command("call writefile(['test file Xxx3'], 'Xxx3')")
-
- command('new')
- -- redefine arglist; go to Xxx1
- command('next! Xxx1 Xxx2 Xxx3')
- -- open window for all args
- command('all')
- eq('test file Xxx1', eval('getline(1)'))
- command('wincmd w')
- command('wincmd w')
- eq('test file Xxx1', eval('getline(1)'))
- -- should now be in Xxx2
- command('rewind')
- eq('test file Xxx2', eval('getline(1)'))
-
- command('autocmd! BufReadPost Xxx2')
- command('enew! | only')
- command("call delete('Xxx1')")
- command("call delete('Xxx2')")
- command("call delete('Xxx3')")
- 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')
diff --git a/test/functional/legacy/autocmd_option_spec.lua b/test/functional/legacy/autocmd_option_spec.lua
index 5e586d3a6a..e00b468c16 100644
--- a/test/functional/legacy/autocmd_option_spec.lua
+++ b/test/functional/legacy/autocmd_option_spec.lua
@@ -211,7 +211,7 @@ describe('au OptionSet', function()
expected_combination({'backup', 0, 0, '', 1, 'local', 'setlocal'})
end)
- it('should trigger if the current buffer is different from the targetted buffer', function()
+ it('should trigger if the current buffer is different from the targeted buffer', function()
local new_buffer = make_buffer()
local new_bufnr = buf.get_number(new_buffer)
@@ -590,7 +590,7 @@ describe('au OptionSet', function()
expected_combination({'backup', 0, 0, '', 1, 'local', 'setlocal'})
end)
- it('should trigger if the current buffer is different from the targetted buffer', function()
+ it('should trigger if the current buffer is different from the targeted buffer', function()
set_hook('buftype')
local new_buffer = make_buffer()
@@ -616,7 +616,7 @@ describe('au OptionSet', function()
expected_combination({'backup', 0, 0, '', 1, 'local', 'setlocal'})
end)
- it('should not trigger if the current window is different from the targetted window', function()
+ it('should not trigger if the current window is different from the targeted window', function()
set_hook('cursorcolumn')
local new_winnr = get_new_window_number()
diff --git a/test/functional/legacy/breakindent_spec.lua b/test/functional/legacy/breakindent_spec.lua
new file mode 100644
index 0000000000..d7779684a4
--- /dev/null
+++ b/test/functional/legacy/breakindent_spec.lua
@@ -0,0 +1,44 @@
+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('breakindent', function()
+ -- oldtest: Test_cursor_position_with_showbreak()
+ it('cursor shown at correct position with showbreak', function()
+ local screen = Screen.new(75, 6)
+ screen:set_default_attr_ids({
+ [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [1] = {background = Screen.colors.Grey, foreground = Screen.colors.DarkBlue}, -- SignColumn
+ [2] = {bold = true}, -- ModeMsg
+ })
+ screen:attach()
+ exec([[
+ let &signcolumn = 'yes'
+ let &showbreak = '+'
+ let leftcol = win_getid()->getwininfo()->get(0, {})->get('textoff')
+ eval repeat('x', &columns - leftcol - 1)->setline(1)
+ eval 'second line'->setline(2)
+ ]])
+ screen:expect([[
+ {1: }^xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
+ {1: }second line |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ feed('AX')
+ screen:expect([[
+ {1: }xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxX|
+ {1: }^second line |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {2:-- INSERT --} |
+ ]])
+ end)
+end)
diff --git a/test/functional/legacy/buffer_spec.lua b/test/functional/legacy/buffer_spec.lua
new file mode 100644
index 0000000000..acaa9a51f1
--- /dev/null
+++ b/test/functional/legacy/buffer_spec.lua
@@ -0,0 +1,59 @@
+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('buffer', function()
+ before_each(function()
+ clear()
+ meths.ui_attach(80, 24, {})
+ meths.set_option('hidden', false)
+ end)
+
+ it('deleting a modified buffer with :confirm', function()
+ source([[
+ func Test_bdel_with_confirm()
+ new
+ call setline(1, 'test')
+ call assert_fails('bdel', 'E89:')
+ call nvim_input('c')
+ confirm bdel
+ call assert_equal(2, winnr('$'))
+ call assert_equal(1, &modified)
+ call nvim_input('n')
+ confirm bdel
+ call assert_equal(1, winnr('$'))
+ endfunc
+ ]])
+ call('Test_bdel_with_confirm')
+ expected_empty()
+ end)
+
+ it('editing another buffer from a modified buffer with :confirm', function()
+ source([[
+ func Test_goto_buf_with_confirm()
+ new Xfile
+ enew
+ call setline(1, 'test')
+ call assert_fails('b Xfile', 'E37:')
+ call nvim_input('c')
+ call assert_fails('confirm b Xfile', 'E37:')
+ call assert_equal(1, &modified)
+ call assert_equal('', @%)
+ call nvim_input('y')
+ call assert_fails('confirm b Xfile', 'E37:')
+ call assert_equal(1, &modified)
+ call assert_equal('', @%)
+ call nvim_input('n')
+ confirm b Xfile
+ call assert_equal('Xfile', @%)
+ close!
+ endfunc
+ ]])
+ call('Test_goto_buf_with_confirm')
+ expected_empty()
+ end)
+end)
diff --git a/test/functional/legacy/cmdline_spec.lua b/test/functional/legacy/cmdline_spec.lua
index cf02636890..2fceb6a132 100644
--- a/test/functional/legacy/cmdline_spec.lua
+++ b/test/functional/legacy/cmdline_spec.lua
@@ -1,9 +1,12 @@
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 feed_command = helpers.feed_command
local exec = helpers.exec
+local meths = helpers.meths
+local pesc = helpers.pesc
describe('cmdline', function()
before_each(clear)
@@ -18,8 +21,6 @@ describe('cmdline', function()
[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([[
@@ -60,7 +61,7 @@ describe('cmdline', function()
{4:~ }|
|
|
- :tabnew |
+ |
]]}
feed [[gt]]
@@ -140,4 +141,180 @@ describe('cmdline', function()
:^ |
]])
end)
+
+ -- oldtest: Test_redraw_in_autocmd()
+ it('cmdline cursor position is correct after :redraw with cmdheight=2', function()
+ local screen = Screen.new(30, 6)
+ screen:set_default_attr_ids({
+ [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ })
+ screen:attach()
+ exec([[
+ set cmdheight=2
+ autocmd CmdlineChanged * redraw
+ ]])
+ feed(':for i in range(3)<CR>')
+ screen:expect([[
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ :for i in range(3) |
+ : ^ |
+ ]])
+ feed(':let i =')
+ -- Note: this may still be considered broken, ref #18140
+ screen:expect([[
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ : :let i =^ |
+ |
+ ]])
+ end)
+
+ it("setting 'cmdheight' works after outputting two messages vim-patch:9.0.0665", function()
+ local screen = Screen.new(60, 8)
+ screen:set_default_attr_ids({
+ [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [1] = {bold = true, reverse = true}, -- StatusLine
+ })
+ screen:attach()
+ exec([[
+ set cmdheight=1 laststatus=2
+ func EchoTwo()
+ set laststatus=2
+ set cmdheight=5
+ echo 'foo'
+ echo 'bar'
+ set cmdheight=1
+ endfunc
+ ]])
+ feed(':call EchoTwo()')
+ screen:expect([[
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {1:[No Name] }|
+ :call EchoTwo()^ |
+ ]])
+ feed('<CR>')
+ screen:expect([[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {1:[No Name] }|
+ |
+ ]])
+ end)
+
+ -- oldtest: Test_cmdheight_tabline()
+ it("changing 'cmdheight' when there is a tabline", function()
+ local screen = Screen.new(60, 8)
+ screen:set_default_attr_ids({
+ [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [1] = {bold = true, reverse = true}, -- StatusLine
+ [2] = {bold = true}, -- TabLineSel
+ [3] = {reverse = true}, -- TabLineFill
+ })
+ screen:attach()
+ meths.set_option('laststatus', 2)
+ meths.set_option('showtabline', 2)
+ meths.set_option('cmdheight', 1)
+ screen:expect([[
+ {2: [No Name] }{3: }|
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {1:[No Name] }|
+ |
+ ]])
+ end)
+end)
+
+describe('cmdwin', function()
+ before_each(clear)
+
+ -- oldtest: Test_cmdwin_interrupted()
+ it('still uses a new buffer when interrupting more prompt on open', function()
+ local screen = Screen.new(30, 16)
+ screen:set_default_attr_ids({
+ [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [1] = {bold = true, reverse = true}, -- StatusLine
+ [2] = {reverse = true}, -- StatusLineNC
+ [3] = {bold = true, foreground = Screen.colors.SeaGreen}, -- MoreMsg
+ [4] = {bold = true}, -- ModeMsg
+ })
+ screen:attach()
+ command('set more')
+ command('autocmd WinNew * highlight')
+ feed('q:')
+ screen:expect({any = pesc('{3:-- More --}^')})
+ feed('q')
+ screen:expect([[
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {2:[No Name] }|
+ {0::}^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {1:[Command Line] }|
+ |
+ ]])
+ feed([[aecho 'done']])
+ screen:expect([[
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {2:[No Name] }|
+ {0::}echo 'done'^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {1:[Command Line] }|
+ {4:-- INSERT --} |
+ ]])
+ feed('<CR>')
+ screen:expect([[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ done |
+ ]])
+ end)
end)
diff --git a/test/functional/legacy/delete_spec.lua b/test/functional/legacy/delete_spec.lua
index 4ba4c8d356..cefcd6c6c4 100644
--- a/test/functional/legacy/delete_spec.lua
+++ b/test/functional/legacy/delete_spec.lua
@@ -48,7 +48,7 @@ describe('Test for delete()', function()
it('symlink directory delete', function()
command("call mkdir('Xdir1')")
- if helpers.iswin() then
+ if helpers.is_os('win') then
command("silent !mklink /j Xlink Xdir1")
else
command("silent !ln -s Xdir1 Xlink")
diff --git a/test/functional/legacy/digraph_spec.lua b/test/functional/legacy/digraph_spec.lua
new file mode 100644
index 0000000000..0cb0bb84be
--- /dev/null
+++ b/test/functional/legacy/digraph_spec.lua
@@ -0,0 +1,46 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear = helpers.clear
+local feed = helpers.feed
+
+before_each(clear)
+
+describe('digraph', function()
+ -- oldtest: Test_entering_digraph()
+ it('characters displayed on the screen', function()
+ local screen = Screen.new(10, 6)
+ screen:set_default_attr_ids({
+ [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [1] = {foreground = Screen.colors.Blue}, -- SpecialKey
+ [2] = {bold = true}, -- ModeMsg
+ })
+ screen:attach()
+ feed('i<C-K>')
+ screen:expect([[
+ {1:^?} |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {2:-- INSERT -} |
+ ]])
+ feed('1')
+ screen:expect([[
+ {1:^1} |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {2:-- INSERT -} |
+ ]])
+ feed('2')
+ screen:expect([[
+ ½^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {2:-- INSERT -} |
+ ]])
+ end)
+end)
diff --git a/test/functional/legacy/display_spec.lua b/test/functional/legacy/display_spec.lua
index f0ffaf2c48..f9b78f5dcd 100644
--- a/test/functional/legacy/display_spec.lua
+++ b/test/functional/legacy/display_spec.lua
@@ -9,6 +9,7 @@ local command = helpers.command
describe('display', function()
before_each(clear)
+ -- oldtest: Test_display_scroll_at_topline()
it('scroll when modified at topline vim-patch:8.2.1488', function()
local screen = Screen.new(20, 4)
screen:attach()
@@ -26,6 +27,7 @@ describe('display', function()
]])
end)
+ -- oldtest: Test_display_scroll_update_visual()
it('scrolling when modified at topline in Visual mode vim-patch:8.2.4626', function()
local screen = Screen.new(60, 8)
screen:attach()
@@ -56,8 +58,53 @@ describe('display', function()
]])
end)
- it('@@@ in the last line shows correctly in a narrow window vim-patch:8.2.4718', function()
- local screen = Screen.new(60, 10)
+ -- oldtest: Test_matchparen_clear_highlight()
+ it('matchparen highlight is cleared when switching buffer', function()
+ local screen = Screen.new(20, 5)
+ screen:set_default_attr_ids({
+ [0] = {bold = true, foreground = Screen.colors.Blue},
+ [1] = {background = Screen.colors.Cyan},
+ })
+ screen:attach()
+
+ local screen1 = [[
+ {1:^()} |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]]
+ local screen2 = [[
+ ^aa |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]]
+
+ exec([[
+ source $VIMRUNTIME/plugin/matchparen.vim
+ set hidden
+ call setline(1, ['()'])
+ normal 0
+ ]])
+ screen:expect(screen1)
+
+ exec([[
+ enew
+ exe "normal iaa\<Esc>0"
+ ]])
+ screen:expect(screen2)
+
+ feed('<C-^>')
+ screen:expect(screen1)
+
+ feed('<C-^>')
+ screen:expect(screen2)
+ end)
+
+ local function run_test_display_lastline(euro)
+ local screen = Screen.new(75, 10)
screen:set_default_attr_ids({
[1] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
[2] = {bold = true, reverse = true}, -- StatusLine
@@ -65,39 +112,86 @@ describe('display', function()
})
screen:attach()
exec([[
- call setline(1, ['aaa', 'b'->repeat(100)])
+ call setline(1, ['aaa', 'b'->repeat(200)])
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] [+] }|
- |
- ]])
+ local fillchar = '@'
+ if euro then
+ command('set fillchars=lastline:€')
+ fillchar = '€'
+ end
+ screen:expect((([[
+ ^a│aaa |
+ a│bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ a│bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ b│bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb |
+ b│{1:~ }|
+ b│{1:~ }|
+ b│{1:~ }|
+ {1:@}│{1:~ }|
+ {2:< }{3:[No Name] [+] }|
+ |
+ ]]):gsub('@', fillchar)))
+
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:<}|
- |
- ]])
+ screen:expect((([[
+ ^aaa │a|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│a|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│a|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb │b|
+ {1:~ }│b|
+ {1:~ }│b|
+ {1:~ }│b|
+ {1:~ }│{1:@}|
+ {2:[No Name] [+] }{3:<}|
+ |
+ ]]):gsub('@', fillchar)))
+
command('set display=truncate')
screen:expect_unchanged()
+
+ command('close')
+ command('3split')
+ screen:expect((([[
+ ^aaa |
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ {1:@@@ }|
+ {2:[No Name] [+] }|
+ aaa |
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb |
+ {3:[No Name] [+] }|
+ |
+ ]]):gsub('@', fillchar)))
+
+ command('close')
+ command('2vsplit')
+ screen:expect((([[
+ ^aa│aaa |
+ a │bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ bb│bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ bb│bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb |
+ bb│{1:~ }|
+ bb│{1:~ }|
+ bb│{1:~ }|
+ {1:@@}│{1:~ }|
+ {2:< }{3:[No Name] [+] }|
+ |
+ ]]):gsub('@', fillchar)))
+ end
+
+ -- oldtest: Test_display_lastline()
+ it('display "lastline" works correctly', function()
+ run_test_display_lastline()
+ end)
+ it('display "lastline" works correctly with multibyte fillchar', function()
+ run_test_display_lastline(true)
end)
end)
diff --git a/test/functional/legacy/edit_spec.lua b/test/functional/legacy/edit_spec.lua
index 7fc5f11a79..362d33a0fd 100644
--- a/test/functional/legacy/edit_spec.lua
+++ b/test/functional/legacy/edit_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
local expect = helpers.expect
@@ -7,20 +8,51 @@ local sleep = helpers.sleep
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))
+describe('edit', function()
+ -- 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)
+
+ -- oldtest: Test_edit_insert_reg()
+ it('inserting a register using CTRL-R', function()
+ local screen = Screen.new(10, 6)
+ screen:set_default_attr_ids({
+ [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [1] = {foreground = Screen.colors.Blue}, -- SpecialKey
+ [2] = {bold = true}, -- ModeMsg
+ })
+ screen:attach()
+ feed('a<C-R>')
+ screen:expect([[
+ {1:^"} |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {2:-- INSERT -} |
+ ]])
+ feed('=')
+ screen:expect([[
+ {1:"} |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ =^ |
+ ]])
+ end)
end)
diff --git a/test/functional/legacy/eval_spec.lua b/test/functional/legacy/eval_spec.lua
index 05d853622e..b5e45a86c1 100644
--- a/test/functional/legacy/eval_spec.lua
+++ b/test/functional/legacy/eval_spec.lua
@@ -639,7 +639,7 @@ describe('eval', function()
end)
it('function name includes a colon', function()
- eq('Vim(function):E128: Function name must start with a capital or "s:": b:test()\\nendfunction',
+ eq('Vim(function):E884: Function name cannot contain a colon: b:test()\\nendfunction',
exc_exec(dedent([[
function! b:test()
endfunction]])))
diff --git a/test/functional/legacy/excmd_spec.lua b/test/functional/legacy/excmd_spec.lua
index ece88d26bd..eb480a6689 100644
--- a/test/functional/legacy/excmd_spec.lua
+++ b/test/functional/legacy/excmd_spec.lua
@@ -7,12 +7,12 @@ local exec_lua = helpers.exec_lua
local expect_exit = helpers.expect_exit
local feed = helpers.feed
local funcs = helpers.funcs
-local iswin = helpers.iswin
local meths = helpers.meths
local read_file = helpers.read_file
local source = helpers.source
local eq = helpers.eq
local write_file = helpers.write_file
+local is_os = helpers.is_os
local function sizeoflong()
if not exec_lua('return pcall(require, "ffi")') then
@@ -376,7 +376,7 @@ describe(':confirm command dialog', function()
{3:(Y)es, [N]o: }^ |
]])
feed('Y')
- if iswin() then
+ if is_os('win') then
screen:expect([[
foobar |
{0:~ }|
@@ -416,7 +416,7 @@ describe(':confirm command dialog', function()
{3:(Y)es, [N]o: }^ |
]])
feed('Y')
- if iswin() then
+ if is_os('win') then
screen:expect([[
foobar |
{1: }|
@@ -493,7 +493,7 @@ describe(':confirm command dialog', function()
{3:(Y)es, [N]o: }^ |
]])
feed('Y')
- if iswin() then
+ if is_os('win') then
screen:expect([[
a |
b |
diff --git a/test/functional/legacy/filechanged_spec.lua b/test/functional/legacy/filechanged_spec.lua
index 1f23528d61..cea1d6ac30 100644
--- a/test/functional/legacy/filechanged_spec.lua
+++ b/test/functional/legacy/filechanged_spec.lua
@@ -1,6 +1,8 @@
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 is_os = helpers.is_os
+local skip = helpers.skip
local function expected_empty()
eq({}, meths.get_vvar('errors'))
@@ -15,7 +17,7 @@ describe('file changed dialog', function()
end)
it('works', function()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
source([[
func Test_file_changed_dialog()
au! FileChangedShell
diff --git a/test/functional/legacy/fold_spec.lua b/test/functional/legacy/fold_spec.lua
new file mode 100644
index 0000000000..83513a3f94
--- /dev/null
+++ b/test/functional/legacy/fold_spec.lua
@@ -0,0 +1,335 @@
+-- Tests for folding.
+local Screen = require('test.functional.ui.screen')
+
+local helpers = require('test.functional.helpers')(after_each)
+local feed, insert, feed_command, expect_any =
+ helpers.feed, helpers.insert, helpers.feed_command, helpers.expect_any
+local command = helpers.command
+local exec = helpers.exec
+
+describe('folding', function()
+ local screen
+
+ before_each(function()
+ helpers.clear()
+
+ screen = Screen.new(45, 8)
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [2] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGrey}, -- Folded
+ [3] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.Grey}, -- FoldColumn
+ [4] = {foreground = Screen.colors.Brown}, -- LineNr
+ })
+ screen:attach()
+ end)
+
+ it('creation, opening, moving (to the end) and closing', function()
+ insert([[
+ 1 aa
+ 2 bb
+ 3 cc
+ last
+ ]])
+
+ -- Basic test if a fold can be created, opened, moving to the end and
+ -- closed.
+ feed_command('1')
+ feed('zf2j')
+ feed_command('call append("$", "manual " . getline(foldclosed(".")))')
+ feed('zo')
+ feed_command('call append("$", foldclosed("."))')
+ feed(']z')
+ feed_command('call append("$", getline("."))')
+ feed('zc')
+ feed_command('call append("$", getline(foldclosed(".")))')
+
+ expect_any([[
+ manual 1 aa
+ -1
+ 3 cc
+ 1 aa]])
+ end)
+
+ it("foldmethod=marker", function()
+ screen:try_resize(20, 10)
+ insert([[
+ dd {{{
+ ee {{{ }}}
+ ff }}}
+ ]])
+ feed_command('set fdm=marker fdl=1')
+ feed_command('2')
+ feed_command('call append("$", "line 2 foldlevel=" . foldlevel("."))')
+ feed('[z')
+ feed_command('call append("$", foldlevel("."))')
+ feed('jo{{ <esc>r{jj') -- writes '{{{' and moves 2 lines bot
+ feed_command('call append("$", foldlevel("."))')
+ feed('kYpj')
+ feed_command('call append("$", foldlevel("."))')
+
+ helpers.poke_eventloop()
+ screen:expect([[
+ dd {{{ |
+ ee {{{ }}} |
+ {{{ |
+ ff }}} |
+ ff }}} |
+ ^ |
+ line 2 foldlevel=2 |
+ 1 |
+ 1 |
+ |
+ ]])
+
+ end)
+
+ it("foldmethod=indent", function()
+ screen:try_resize(20, 8)
+ feed_command('set fdm=indent sw=2')
+ insert([[
+ aa
+ bb
+ cc
+ last
+ ]])
+ feed_command('call append("$", "foldlevel line3=" . foldlevel(3))')
+ feed_command('call append("$", foldlevel(2))')
+ feed('zR')
+
+ helpers.poke_eventloop()
+ screen:expect([[
+ aa |
+ bb |
+ cc |
+ last |
+ ^ |
+ foldlevel line3=2 |
+ 1 |
+ |
+ ]])
+ end)
+
+ it("foldmethod=syntax", function()
+ screen:try_resize(35, 15)
+ insert([[
+ 1 aa
+ 2 bb
+ 3 cc
+ 4 dd {{{
+ 5 ee {{{ }}}
+ 6 ff }}}
+ 7 gg
+ 8 hh
+ 9 ii
+ a jj
+ b kk
+ last]])
+ feed_command('set fdm=syntax fdl=0')
+ feed_command('syn region Hup start="dd" end="ii" fold contains=Fd1,Fd2,Fd3')
+ feed_command('syn region Fd1 start="ee" end="ff" fold contained')
+ feed_command('syn region Fd2 start="gg" end="hh" fold contained')
+ feed_command('syn region Fd3 start="commentstart" end="commentend" fold contained')
+ feed('Gzk')
+ feed_command('call append("$", "folding " . getline("."))')
+ feed('k')
+ feed_command('call append("$", getline("."))')
+ feed('jAcommentstart <esc>Acommentend<esc>')
+ feed_command('set fdl=1')
+ feed('3j')
+ feed_command('call append("$", getline("."))')
+ feed_command('set fdl=0')
+ feed('zO<C-L>j') -- <C-L> redraws screen
+ feed_command('call append("$", getline("."))')
+ feed_command('set fdl=0')
+ expect_any([[
+ folding 9 ii
+ 3 cc
+ 9 ii
+ a jj]])
+ end)
+
+ it("foldmethod=expression", function()
+ insert([[
+ 1 aa
+ 2 bb
+ 3 cc
+ 4 dd {{{
+ 5 ee {{{ }}}
+ 6 ff }}}
+ 7 gg
+ 8 hh
+ 9 ii
+ a jj
+ b kk
+ last ]])
+
+ feed_command([[
+ fun Flvl()
+ let l = getline(v:lnum)
+ if l =~ "bb$"
+ return 2
+ elseif l =~ "gg$"
+ return "s1"
+ elseif l =~ "ii$"
+ return ">2"
+ elseif l =~ "kk$"
+ return "0"
+ endif
+ return "="
+ endfun
+ ]])
+ feed_command('set fdm=expr fde=Flvl()')
+ feed_command('/bb$')
+ feed_command('call append("$", "expr " . foldlevel("."))')
+ feed_command('/hh$')
+ feed_command('call append("$", foldlevel("."))')
+ feed_command('/ii$')
+ feed_command('call append("$", foldlevel("."))')
+ feed_command('/kk$')
+ feed_command('call append("$", foldlevel("."))')
+
+ expect_any([[
+ expr 2
+ 1
+ 2
+ 0]])
+ end)
+
+ it('can be opened after :move', function()
+ -- luacheck: ignore
+ screen:try_resize(35, 8)
+ insert([[
+ Test fdm=indent and :move bug END
+ line2
+ Test fdm=indent START
+ line3
+ line4]])
+ feed_command('set noai nosta ')
+ feed_command('set fdm=indent')
+ feed_command('1m1')
+ feed('2jzc')
+ feed_command('m0')
+ feed('zR')
+
+ expect_any([[
+ Test fdm=indent START
+ line3
+ line4
+ Test fdm=indent and :move bug END
+ line2]])
+ end)
+
+ -- oldtest: Test_folds_with_rnu()
+ it('with relative line numbers', function()
+ command('set fdm=marker rnu foldcolumn=2')
+ command('call setline(1, ["{{{1", "nline 1", "{{{1", "line 2"])')
+
+ screen:expect([[
+ {3:+ }{4: 0 }{2:^+-- 2 lines: ·························}|
+ {3:+ }{4: 1 }{2:+-- 2 lines: ·························}|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ feed("j")
+ screen:expect([[
+ {3:+ }{4: 1 }{2:+-- 2 lines: ·························}|
+ {3:+ }{4: 0 }{2:^+-- 2 lines: ·························}|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end)
+
+ -- oldtest: Test_foldclose_opt()
+ it('foldclose=all', function()
+ exec([[
+ set foldmethod=manual foldclose=all foldopen=all
+ call setline(1, ['one', 'two', 'three', 'four'])
+ 2,3fold
+ ]])
+
+ screen:expect([[
+ ^one |
+ {2:+-- 2 lines: two····························}|
+ four |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ feed('2G')
+ screen:expect([[
+ one |
+ ^two |
+ three |
+ four |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ feed('4G')
+ screen:expect([[
+ one |
+ {2:+-- 2 lines: two····························}|
+ ^four |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ feed('3G')
+ screen:expect([[
+ one |
+ two |
+ ^three |
+ four |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ feed('1G')
+ screen:expect([[
+ ^one |
+ {2:+-- 2 lines: two····························}|
+ four |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ feed('2G')
+ screen:expect([[
+ one |
+ ^two |
+ three |
+ four |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ feed('k')
+ screen:expect([[
+ ^one |
+ {2:+-- 2 lines: two····························}|
+ four |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end)
+end)
diff --git a/test/functional/legacy/gf_spec.lua b/test/functional/legacy/gf_spec.lua
index f1b1790ba1..9f725446be 100644
--- a/test/functional/legacy/gf_spec.lua
+++ b/test/functional/legacy/gf_spec.lua
@@ -10,6 +10,7 @@ describe('gf', function()
it('is not allowed when buffer is locked', function()
command('au OptionSet diff norm! gf')
command([[call setline(1, ['Xfile1', 'line2', 'line3', 'line4'])]])
- eq('Vim(normal):E788: Not allowed to edit another buffer now', pcall_err(command, 'diffthis'))
+ eq('OptionSet Autocommands for "diff": Vim(normal):E788: Not allowed to edit another buffer now',
+ pcall_err(command, 'diffthis'))
end)
end)
diff --git a/test/functional/legacy/increment_spec.lua b/test/functional/legacy/increment_spec.lua
index d51f9a2e02..d35f4bdae6 100644
--- a/test/functional/legacy/increment_spec.lua
+++ b/test/functional/legacy/increment_spec.lua
@@ -285,7 +285,7 @@ describe('Ctrl-A/Ctrl-X on visual selections', function()
" 1
" 1
" 1
- " Expexted:
+ " Expected:
" 1) g Ctrl-A on block selected indented lines
" 2
" 1
diff --git a/test/functional/legacy/listlbr_spec.lua b/test/functional/legacy/listlbr_spec.lua
index f70d55f4a3..d4f11a61c2 100644
--- a/test/functional/legacy/listlbr_spec.lua
+++ b/test/functional/legacy/listlbr_spec.lua
@@ -1,11 +1,12 @@
-- Test for linebreak and list option (non-utf8)
local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
local feed, insert, source = helpers.feed, helpers.insert, helpers.source
local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect
describe('listlbr', function()
- setup(clear)
+ before_each(clear)
-- luacheck: ignore 621 (Indentation)
-- luacheck: ignore 611 (Line contains only whitespaces)
@@ -195,4 +196,97 @@ describe('listlbr', function()
aa>-----a-$
~ ]])
end)
+
+ -- oldtest: Test_linebreak_reset_restore()
+ it('cursor position is drawn correctly after operator', function()
+ local screen = Screen.new(60, 6)
+ screen:set_default_attr_ids({
+ [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [1] = {background = Screen.colors.LightGrey}, -- Visual
+ [2] = {background = Screen.colors.Red, foreground = Screen.colors.White}, -- ErrorMsg
+ })
+ screen:attach()
+
+ -- f_wincol() calls validate_cursor()
+ source([[
+ set linebreak showcmd noshowmode formatexpr=wincol()-wincol()
+ call setline(1, repeat('a', &columns - 10) .. ' bbbbbbbbbb c')
+ ]])
+
+ feed('$v$')
+ screen:expect([[
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa |
+ bbbbbbbbbb {1:c}^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ 2 |
+ ]])
+ feed('zo')
+ screen:expect([[
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa |
+ bbbbbbbbbb ^c |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {2:E490: No fold found} |
+ ]])
+
+ feed('$v$')
+ screen:expect([[
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa |
+ bbbbbbbbbb {1:c}^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {2:E490: No fold found} 2 |
+ ]])
+ feed('gq')
+ screen:expect([[
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa |
+ bbbbbbbbbb ^c |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {2:E490: No fold found} |
+ ]])
+
+ feed('$<C-V>$')
+ screen:expect([[
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa |
+ bbbbbbbbbb {1:c}^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {2:E490: No fold found} 1x2 |
+ ]])
+ feed('I')
+ screen:expect([[
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa |
+ bbbbbbbbbb ^c |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {2:E490: No fold found} |
+ ]])
+
+ feed('<Esc>$v$')
+ screen:expect([[
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa |
+ bbbbbbbbbb {1:c}^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {2:E490: No fold found} 2 |
+ ]])
+ feed('s')
+ screen:expect([[
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa |
+ bbbbbbbbbb ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {2:E490: No fold found} |
+ ]])
+ end)
end)
diff --git a/test/functional/legacy/match_spec.lua b/test/functional/legacy/match_spec.lua
new file mode 100644
index 0000000000..b6e45c396c
--- /dev/null
+++ b/test/functional/legacy/match_spec.lua
@@ -0,0 +1,126 @@
+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('matchaddpos()', function()
+ -- oldtest: Test_matchaddpos_dump()
+ it('can add more than 8 match positions vim-patch:9.0.0620', function()
+ local screen = Screen.new(60, 14)
+ screen:set_default_attr_ids({
+ [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [1] = {background = Screen.colors.Yellow}, -- Search
+ })
+ screen:attach()
+ exec([[
+ call setline(1, ['1234567890123']->repeat(14))
+ call matchaddpos('Search', range(1, 12)->map({i, v -> [v, v]}))
+ ]])
+ screen:expect([[
+ {1:^1}234567890123 |
+ 1{1:2}34567890123 |
+ 12{1:3}4567890123 |
+ 123{1:4}567890123 |
+ 1234{1:5}67890123 |
+ 12345{1:6}7890123 |
+ 123456{1:7}890123 |
+ 1234567{1:8}90123 |
+ 12345678{1:9}0123 |
+ 123456789{1:0}123 |
+ 1234567890{1:1}23 |
+ 12345678901{1:2}3 |
+ 1234567890123 |
+ |
+ ]])
+ end)
+end)
+
+describe('match highlighting', function()
+ -- oldtest: Test_match_in_linebreak()
+ it('does not continue in linebreak vim-patch:8.2.3698', function()
+ local screen = Screen.new(75, 10)
+ screen:set_default_attr_ids({
+ [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [1] = {background = Screen.colors.Red, foreground = Screen.colors.White}, -- ErrorMsg
+ })
+ screen:attach()
+ exec([=[
+ set breakindent linebreak breakat+=]
+ call printf('%s]%s', repeat('x', 50), repeat('x', 70))->setline(1)
+ call matchaddpos('ErrorMsg', [[1, 51]])
+ ]=])
+ screen:expect([[
+ ^xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx{1:]} |
+ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end)
+
+ it('is shown with incsearch vim-patch:8.2.3940', function()
+ local screen = Screen.new(75, 6)
+ screen:set_default_attr_ids({
+ [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [1] = {background = Screen.colors.Yellow}, -- Search
+ [2] = {background = Screen.colors.Red, foreground = Screen.colors.White}, -- ErrorMsg
+ })
+ screen:attach()
+ exec([[
+ set incsearch
+ call setline(1, range(20))
+ call matchaddpos('ErrorMsg', [3])
+ ]])
+ screen:expect([[
+ ^0 |
+ 1 |
+ {2:2} |
+ 3 |
+ 4 |
+ |
+ ]])
+ feed(':s/0')
+ screen:expect([[
+ {1:0} |
+ 1 |
+ {2:2} |
+ 3 |
+ 4 |
+ :s/0^ |
+ ]])
+ end)
+
+ it('on a Tab vim-patch:8.2.4062', function()
+ local screen = Screen.new(75, 10)
+ screen:set_default_attr_ids({
+ [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [1] = {background = Screen.colors.Red, foreground = Screen.colors.White}, -- ErrorMsg
+ })
+ screen:attach()
+ exec([[
+ set linebreak
+ call setline(1, "\tix")
+ call matchadd('ErrorMsg', '\t')
+ ]])
+ screen:expect([[
+ {1: ^ }ix |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end)
+end)
diff --git a/test/functional/legacy/memory_usage_spec.lua b/test/functional/legacy/memory_usage_spec.lua
index eec89aa919..59839157ea 100644
--- a/test/functional/legacy/memory_usage_spec.lua
+++ b/test/functional/legacy/memory_usage_spec.lua
@@ -3,15 +3,14 @@ local clear = helpers.clear
local eval = helpers.eval
local eq = helpers.eq
local feed_command = helpers.feed_command
-local iswin = helpers.iswin
local retry = helpers.retry
local ok = helpers.ok
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 is_os = helpers.is_os
+local is_ci = helpers.is_ci
local function isasan()
local version = eval('execute("version")')
@@ -22,8 +21,8 @@ clear()
if isasan() then
pending('ASAN build is difficult to estimate memory usage', function() end)
return
-elseif iswin() then
- if isCI('github') then
+elseif is_os('win') then
+ if is_ci('github') then
pending('Windows runners in Github Actions do not have a stable environment to estimate memory usage', function() end)
return
elseif eval("executable('wmic')") == 0 then
@@ -38,7 +37,7 @@ end
local monitor_memory_usage = {
memory_usage = function(self)
local handle
- if iswin() then
+ if is_os('win') then
handle = io.popen('wmic process where processid=' ..self.pid..' get WorkingSetSize')
else
handle = io.popen('ps -o rss= -p '..self.pid)
@@ -167,9 +166,9 @@ describe('memory usage', function()
local last = monitor_memory_usage(pid)
-- The usage may be a bit less than the last value, use 80%.
-- Allow for 20% tolerance at the upper limit. That's very permissive, but
- -- otherwise the test fails sometimes. On Sourcehut CI with FreeBSD we need to
- -- be even much more permissive.
- local upper_multiplier = uname() == 'freebsd' and 19 or 12
+ -- otherwise the test fails sometimes. On FreeBSD we need to be even much
+ -- more permissive.
+ local upper_multiplier = is_os('freebsd') and 19 or 12
local lower = before.last * 8 / 10
local upper = load_adjust((after.max + (after.last - before.last)) * upper_multiplier / 10)
check_result({before=before, after=after, last=last},
@@ -179,7 +178,7 @@ describe('memory usage', function()
end)
it('releases memory when closing windows when folds exist', function()
- if helpers.is_os('mac') then
+ if is_os('mac') then
pending('macOS memory compression causes flakiness')
end
local pid = eval('getpid()')
diff --git a/test/functional/legacy/messages_spec.lua b/test/functional/legacy/messages_spec.lua
index 159cf7a551..71a53c8381 100644
--- a/test/functional/legacy/messages_spec.lua
+++ b/test/functional/legacy/messages_spec.lua
@@ -4,27 +4,65 @@ local clear = helpers.clear
local command = helpers.command
local exec = helpers.exec
local feed = helpers.feed
+local meths = helpers.meths
+local nvim_dir = helpers.nvim_dir
before_each(clear)
describe('messages', function()
local screen
+ -- oldtest: Test_warning_scroll()
+ it('a warning causes scrolling if and only if it has a stacktrace', 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
+ [3] = {foreground = Screen.colors.Red}, -- WarningMsg
+ })
+ screen:attach()
+
+ -- When the warning comes from a script, messages are scrolled so that the
+ -- stacktrace is visible.
+ -- It is a bit hard to assert the screen when sourcing a script, so skip this part.
+
+ -- When the warning does not come from a script, messages are not scrolled.
+ command('enew')
+ command('set readonly')
+ feed('u')
+ screen:expect({grid = [[
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {3:W10: Warning: Changing a readonly file}^ |
+ ]], timeout = 500})
+ screen:expect([[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ Already at oldest change |
+ ]])
+ end)
+
describe('more prompt', function()
before_each(function()
+ command('set more')
+ end)
+
+ -- oldtest: Test_message_more()
+ it('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] = {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(':%pfoo<C-H><C-H><C-H>#')
@@ -313,16 +351,139 @@ describe('messages', function()
]])
end)
+ -- oldtest: Test_echo_verbose_system()
+ it('verbose message before echo command', function()
+ screen = Screen.new(60, 10)
+ screen:set_default_attr_ids({
+ [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [1] = {bold = true, foreground = Screen.colors.SeaGreen}, -- MoreMsg
+ })
+ screen:attach()
+
+ command('cd '..nvim_dir)
+ meths.set_option('shell', './shell-test')
+ meths.set_option('shellcmdflag', 'REP 20')
+ meths.set_option('shellxquote', '') -- win: avoid extra quotes
+
+ -- display a page and go back, results in exactly the same view
+ feed([[:4 verbose echo system('foo')<CR>]])
+ screen:expect([[
+ Executing command: "'./shell-test' 'REP' '20' 'foo'" |
+ |
+ 0: foo |
+ 1: foo |
+ 2: foo |
+ 3: foo |
+ 4: foo |
+ 5: foo |
+ 6: foo |
+ {1:-- More --}^ |
+ ]])
+ feed('<Space>')
+ screen:expect([[
+ 7: foo |
+ 8: foo |
+ 9: foo |
+ 10: foo |
+ 11: foo |
+ 12: foo |
+ 13: foo |
+ 14: foo |
+ 15: foo |
+ {1:-- More --}^ |
+ ]])
+ feed('b')
+ screen:expect([[
+ Executing command: "'./shell-test' 'REP' '20' 'foo'" |
+ |
+ 0: foo |
+ 1: foo |
+ 2: foo |
+ 3: foo |
+ 4: foo |
+ 5: foo |
+ 6: foo |
+ {1:-- More --}^ |
+ ]])
+
+ -- do the same with 'cmdheight' set to 2
+ feed('q')
+ command('set ch=2')
+ command('mode') -- FIXME: bottom is invalid after scrolling
+ screen:expect([[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ |
+ ]])
+ feed([[:4 verbose echo system('foo')<CR>]])
+ screen:expect([[
+ Executing command: "'./shell-test' 'REP' '20' 'foo'" |
+ |
+ 0: foo |
+ 1: foo |
+ 2: foo |
+ 3: foo |
+ 4: foo |
+ 5: foo |
+ 6: foo |
+ {1:-- More --}^ |
+ ]])
+ feed('<Space>')
+ screen:expect([[
+ 7: foo |
+ 8: foo |
+ 9: foo |
+ 10: foo |
+ 11: foo |
+ 12: foo |
+ 13: foo |
+ 14: foo |
+ 15: foo |
+ {1:-- More --}^ |
+ ]])
+ feed('b')
+ screen:expect([[
+ Executing command: "'./shell-test' 'REP' '20' 'foo'" |
+ |
+ 0: foo |
+ 1: foo |
+ 2: foo |
+ 3: foo |
+ 4: foo |
+ 5: foo |
+ 6: foo |
+ {1:-- More --}^ |
+ ]])
+ end)
+
-- oldtest: Test_quit_long_message()
it('with control characters can be quit vim-patch:8.2.1844', function()
- screen:try_resize(40, 6)
+ screen = Screen.new(40, 10)
+ 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.Blue}, -- SpecialKey
+ })
+ screen:attach()
+
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|
+ 0{2:^A}1{2:^A}2{2:^A}3{2:^A}4{2:^A}5{2:^A}6{2:^A}7{2:^A}8{2:^A}9{2:^A}10{2:^A}11{2:^A}12|
+ {2:^A}13{2:^A}14{2:^A}15{2:^A}16{2:^A}17{2:^A}18{2:^A}19{2:^A}20{2:^A}21{2:^A}22|
+ {2:^A}23{2:^A}24{2:^A}25{2:^A}26{2:^A}27{2:^A}28{2:^A}29{2:^A}30{2:^A}31{2:^A}32|
+ {2:^A}33{2:^A}34{2:^A}35{2:^A}36{2:^A}37{2:^A}38{2:^A}39{2:^A}40{2:^A}41{2:^A}42|
+ {2:^A}43{2:^A}44{2:^A}45{2:^A}46{2:^A}47{2:^A}48{2:^A}49{2:^A}50{2:^A}51{2:^A}52|
+ {2:^A}53{2:^A}54{2:^A}55{2:^A}56{2:^A}57{2:^A}58{2:^A}59{2:^A}60{2:^A}61{2:^A}62|
+ {2:^A}63{2:^A}64{2:^A}65{2:^A}66{2:^A}67{2:^A}68{2:^A}69{2:^A}70{2:^A}71{2:^A}72|
+ {2:^A}73{2:^A}74{2:^A}75{2:^A}76{2:^A}77{2:^A}78{2:^A}79{2:^A}80{2:^A}81{2:^A}82|
+ {2:^A}83{2:^A}84{2:^A}85{2:^A}86{2:^A}87{2:^A}88{2:^A}89{2:^A}90{2:^A}91{2:^A}92|
{1:-- More --}^ |
]])
feed('q')
@@ -332,6 +493,10 @@ describe('messages', function()
{0:~ }|
{0:~ }|
{0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
|
]])
end)
diff --git a/test/functional/legacy/search_spec.lua b/test/functional/legacy/search_spec.lua
index 67991f5d48..3f1f85cf28 100644
--- a/test/functional/legacy/search_spec.lua
+++ b/test/functional/legacy/search_spec.lua
@@ -14,7 +14,7 @@ describe('search cmdline', function()
before_each(function()
clear()
- command('set nohlsearch')
+ command('set nohlsearch inccommand=')
screen = Screen.new(20, 3)
screen:attach()
screen:set_default_attr_ids({
@@ -472,8 +472,8 @@ describe('search cmdline', function()
funcs.winsaveview())
end)
+ -- oldtest: Test_search_cmdline4().
it("CTRL-G with 'incsearch' and ? goes in the right direction", function()
- -- oldtest: Test_search_cmdline4().
screen:try_resize(40, 4)
command('enew!')
funcs.setline(1, {' 1 the first', ' 2 the second', ' 3 the third'})
@@ -573,8 +573,8 @@ describe('search cmdline', function()
]])
end)
+ -- oldtest: Test_incsearch_sort_dump().
it('incsearch works with :sort', function()
- -- oldtest: Test_incsearch_sort_dump().
screen:try_resize(20, 4)
command('set incsearch hlsearch scrolloff=0')
funcs.setline(1, {'another one 2', 'that one 3', 'the one 1'})
@@ -589,8 +589,8 @@ describe('search cmdline', function()
feed('<esc>')
end)
+ -- oldtest: Test_incsearch_vimgrep_dump().
it('incsearch works with :vimgrep family', function()
- -- oldtest: Test_incsearch_vimgrep_dump().
screen:try_resize(30, 4)
command('set incsearch hlsearch scrolloff=0')
funcs.setline(1, {'another one 2', 'that one 3', 'the one 1'})
@@ -640,11 +640,74 @@ describe('search cmdline', function()
]])
feed('<esc>')
end)
+
+ -- oldtest: Test_incsearch_substitute_dump2()
+ it('detects empty pattern properly vim-patch:8.2.2295', function()
+ screen:try_resize(70, 6)
+ exec([[
+ set incsearch hlsearch scrolloff=0
+ for n in range(1, 4)
+ call setline(n, "foo " . n)
+ endfor
+ call setline(5, "abc|def")
+ 3
+ ]])
+
+ feed([[:%s/\vabc|]])
+ screen:expect([[
+ foo 1 |
+ foo 2 |
+ foo 3 |
+ foo 4 |
+ abc|def |
+ :%s/\vabc|^ |
+ ]])
+ feed('<Esc>')
+
+ -- The following should not be highlighted
+ feed([[:1,5s/\v|]])
+ screen:expect([[
+ foo 1 |
+ foo 2 |
+ foo 3 |
+ foo 4 |
+ abc|def |
+ :1,5s/\v|^ |
+ ]])
+ end)
end)
describe('Search highlight', function()
before_each(clear)
- it('Search highlight is combined with Visual highlight vim-patch:8.2.2797', function()
+
+ -- oldtest: Test_hlsearch_dump()
+ it('beyond line end vim-patch:8.2.2542', function()
+ local screen = Screen.new(50, 6)
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [2] = {background = Screen.colors.Yellow}, -- Search
+ [3] = {background = Screen.colors.Grey90}, -- CursorLine
+ })
+ screen:attach()
+ exec([[
+ set hlsearch noincsearch cursorline
+ call setline(1, ["xxx", "xxx", "xxx"])
+ /.*
+ 2
+ ]])
+ feed([[/\_.*<CR>]])
+ screen:expect([[
+ {2:xxx } |
+ {2:xxx } |
+ {2:^xxx }{3: }|
+ {1:~ }|
+ {1:~ }|
+ /\_.* |
+ ]])
+ end)
+
+ -- oldtest: Test_hlsearch_and_visual()
+ it('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
diff --git a/test/functional/legacy/search_stat_spec.lua b/test/functional/legacy/search_stat_spec.lua
index c2ca393a56..9fcf798836 100644
--- a/test/functional/legacy/search_stat_spec.lua
+++ b/test/functional/legacy/search_stat_spec.lua
@@ -17,6 +17,7 @@ describe('search stat', function()
screen:attach()
end)
+ -- oldtest: Test_search_stat_screendump()
it('right spacing with silent mapping vim-patch:8.1.1970', function()
exec([[
set shortmess-=S
@@ -57,6 +58,7 @@ describe('search stat', function()
]])
end)
+ -- oldtest: Test_search_stat_foldopen()
it('when only match is in fold vim-patch:8.2.0840', function()
exec([[
set shortmess-=S
@@ -86,6 +88,7 @@ describe('search stat', function()
screen:expect_unchanged()
end)
+ -- oldtest: Test_search_stat_then_gd()
it('is cleared by gd and gD vim-patch:8.2.3583', function()
exec([[
call setline(1, ['int cat;', 'int dog;', 'cat = dog;'])
@@ -120,6 +123,7 @@ describe('search stat', function()
]])
end)
+ -- oldtest: Test_search_stat_and_incsearch()
it('is not broken by calling searchcount() in tabline vim-patch:8.2.4378', function()
exec([[
call setline(1, ['abc--c', '--------abc', '--abc'])
diff --git a/test/functional/legacy/source_spec.lua b/test/functional/legacy/source_spec.lua
new file mode 100644
index 0000000000..f31521607d
--- /dev/null
+++ b/test/functional/legacy/source_spec.lua
@@ -0,0 +1,32 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear = helpers.clear
+local feed = helpers.feed
+local write_file = helpers.write_file
+
+before_each(clear)
+
+describe(':source!', function()
+ -- oldtest: Test_nested_script()
+ it('gives E22 when scripts nested too deep', function()
+ write_file('Xscript.vim', [[
+ :source! Xscript.vim
+ ]])
+ local screen = Screen.new(75, 6)
+ screen:set_default_attr_ids({
+ [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [1] = {background = Screen.colors.Red, foreground = Screen.colors.White}, -- ErrorMsg
+ })
+ screen:attach()
+ feed(':source! Xscript.vim\n')
+ screen:expect([[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {1:E22: Scripts nested too deep} |
+ ]])
+ os.remove('Xscript.vim')
+ end)
+end)
diff --git a/test/functional/legacy/statusline_spec.lua b/test/functional/legacy/statusline_spec.lua
index e2b30a7c82..c5b17f8749 100644
--- a/test/functional/legacy/statusline_spec.lua
+++ b/test/functional/legacy/statusline_spec.lua
@@ -68,4 +68,87 @@ describe('statusline', function()
|
]])
end)
+
+ -- oldtest: Test_statusline_showcmd()
+ it('showcmdloc=statusline works', function()
+ screen:set_default_attr_ids({
+ [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [1] = {background = Screen.colors.LightGrey}, -- Visual
+ [2] = {bold = true}, -- MoreMsg
+ [3] = {bold = true, reverse = true}, -- StatusLine
+ [5] = {background = Screen.colors.LightGrey, foreground = Screen.colors.DarkBlue}, -- Folded
+ })
+ exec([[
+ func MyStatusLine()
+ return '%S'
+ endfunc
+
+ set showcmd
+ set laststatus=2
+ set statusline=%S
+ set showcmdloc=statusline
+ call setline(1, ['a', 'b', 'c'])
+ set foldopen+=jump
+ 1,2fold
+ 3
+ ]])
+
+ feed('g')
+ screen:expect([[
+ {5:+-- 2 lines: a···································}|
+ ^c |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {3:g }|
+ |
+ ]])
+
+ -- typing "gg" should open the fold
+ feed('g')
+ screen:expect([[
+ ^a |
+ b |
+ c |
+ {0:~ }|
+ {0:~ }|
+ {3: }|
+ |
+ ]])
+
+ feed('<C-V>Gl')
+ screen:expect([[
+ {1:a} |
+ {1:b} |
+ {1:c}^ |
+ {0:~ }|
+ {0:~ }|
+ {3:3x2 }|
+ {2:-- VISUAL BLOCK --} |
+ ]])
+
+ feed('<Esc>1234')
+ screen:expect([[
+ a |
+ b |
+ ^c |
+ {0:~ }|
+ {0:~ }|
+ {3:1234 }|
+ |
+ ]])
+
+ feed('<Esc>:set statusline=<CR>')
+ feed(':<CR>')
+ feed('1234')
+ screen:expect([[
+ a |
+ b |
+ ^c |
+ {0:~ }|
+ {0:~ }|
+ {3:[No Name] [+] 1234 }|
+ : |
+ ]])
+ end)
end)
diff --git a/test/functional/legacy/080_substitute_spec.lua b/test/functional/legacy/substitute_spec.lua
index faeb61e3af..f3ce343680 100644
--- a/test/functional/legacy/080_substitute_spec.lua
+++ b/test/functional/legacy/substitute_spec.lua
@@ -3,11 +3,13 @@
-- Test for *:s%* on :substitute.
local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
local feed, insert = helpers.feed, helpers.insert
+local exec = helpers.exec
local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect
local eq, eval = helpers.eq, helpers.eval
-describe('substitue()', function()
+describe('substitute()', function()
before_each(clear)
-- The original test contained several TEST_X lines to delimit different
@@ -132,7 +134,7 @@ describe('substitue()', function()
end)
end)
-describe(':substitue', function()
+describe(':substitute', function()
before_each(clear)
it('with \\ze and \\zs and confirmation dialog (TEST_8)', function()
@@ -159,4 +161,29 @@ describe(':substitue', function()
feed('yyq') -- For the dialog of the previous :s command.
expect('XXx')
end)
+
+ it('first char is highlighted with confirmation dialog and empty match', function()
+ local screen = Screen.new(60, 8)
+ screen:set_default_attr_ids({
+ [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [1] = {reverse = true}, -- IncSearch
+ [2] = {bold = true, foreground = Screen.colors.SeaGreen}, -- MoreMsg
+ })
+ screen:attach()
+ exec([[
+ set nohlsearch noincsearch
+ call setline(1, ['one', 'two', 'three'])
+ ]])
+ feed(':%s/^/ /c<CR>')
+ screen:expect([[
+ {1:o}ne |
+ two |
+ three |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {2:replace with (y/n/a/q/l/^E/^Y)?}^ |
+ ]])
+ end)
end)
diff --git a/test/functional/legacy/syn_attr_spec.lua b/test/functional/legacy/syn_attr_spec.lua
index 06e8427e27..e6573da5d3 100644
--- a/test/functional/legacy/syn_attr_spec.lua
+++ b/test/functional/legacy/syn_attr_spec.lua
@@ -4,10 +4,10 @@ local command = helpers.command
local eq = helpers.eq
local eval = helpers.eval
-before_each(clear)
-
-- oldtest: Test_missing_attr()
-it('synIDattr() works', function()
+describe('synIDattr()', function()
+ setup(clear)
+
local bool_attrs = {
'bold',
'italic',
@@ -22,39 +22,55 @@ it('synIDattr() works', function()
'nocombine',
}
- command('hi Mine cterm=NONE gui=NONE')
- eq('Mine', eval([[synIDattr(hlID("Mine"), "name")]]))
- for _, mode in ipairs({'cterm', 'gui'}) do
- eq('', eval(([[synIDattr("Mine"->hlID(), "bg", '%s')]]):format(mode)))
- eq('', eval(([[synIDattr("Mine"->hlID(), "fg", '%s')]]):format(mode)))
- eq('', eval(([[synIDattr("Mine"->hlID(), "sp", '%s')]]):format(mode)))
- for _, attr in ipairs(bool_attrs) do
- eq('', eval(([[synIDattr(hlID("Mine"), "%s", '%s')]]):format(attr, mode)))
- eq('', eval(([[synIDattr(hlID("Mine"), "%s", '%s')]]):format(attr, mode)))
- eq('', eval(([[synIDattr(hlID("Mine"), "%s", '%s')]]):format(attr, mode)))
+ describe(':hi Mine cterm=NONE gui=NONE', function()
+ setup(function()
+ command(':hi Mine cterm=NONE gui=NONE')
+ end)
+
+ it('"name"', function()
+ eq('Mine', eval([[synIDattr(hlID("Mine"), "name")]]))
+ end)
+
+ local function none_test(attr, mode)
+ it(('"%s"'):format(attr), function()
+ eq('', eval(([[synIDattr(hlID("Mine"), "%s", '%s')]]):format(attr, mode)))
+ end)
end
- eq('', eval(([[synIDattr(hlID("Mine"), "inverse", '%s')]]):format(mode)))
+
+ for _, mode in ipairs({'cterm', 'gui'}) do
+ describe(('"%s"'):format(mode), function()
+ for _, attr in ipairs(bool_attrs) do
+ none_test(attr, mode)
+ end
+ for _, attr in ipairs({'inverse', 'bg', 'fg', 'sp'}) do
+ none_test(attr, mode)
+ end
+ end)
+ end
+ end)
+
+ local function attr_test(attr1, attr2)
+ local cmd = (':hi Mine cterm=%s gui=%s'):format(attr1, attr2)
+ it(cmd, function()
+ command(cmd)
+ eq('1', eval(([[synIDattr("Mine"->hlID(), "%s", 'cterm')]]):format(attr1)))
+ eq('', eval(([[synIDattr(hlID("Mine"), "%s", 'cterm')]]):format(attr2)))
+ eq('', eval(([[synIDattr("Mine"->hlID(), "%s", 'gui')]]):format(attr1)))
+ eq('1', eval(([[synIDattr(hlID("Mine"), "%s", 'gui')]]):format(attr2)))
+ end)
end
for i, attr1 in ipairs(bool_attrs) do
local attr2 = bool_attrs[i - 1] or bool_attrs[#bool_attrs]
-
- command(('hi Mine cterm=%s gui=%s'):format(attr1, attr2))
- eq('1', eval(([[synIDattr(hlID("Mine"), "%s", 'cterm')]]):format(attr1)))
- eq('', eval(([[synIDattr(hlID("Mine"), "%s", 'cterm')]]):format(attr2)))
- eq('', eval(([[synIDattr("Mine"->hlID(), "%s", 'gui')]]):format(attr1)))
- eq('1', eval(([[synIDattr("Mine"->hlID(), "%s", 'gui')]]):format(attr2)))
-
- command(('hi Mine cterm=%s gui=%s'):format(attr2, attr1))
- eq('', eval(([[synIDattr("Mine"->hlID(), "%s", 'cterm')]]):format(attr1)))
- eq('1', eval(([[synIDattr("Mine"->hlID(), "%s", 'cterm')]]):format(attr2)))
- eq('1', eval(([[synIDattr(hlID("Mine"), "%s", 'gui')]]):format(attr1)))
- eq('', eval(([[synIDattr(hlID("Mine"), "%s", 'gui')]]):format(attr2)))
+ attr_test(attr1, attr2)
+ attr_test(attr2, attr1)
end
- command('hi Mine cterm=reverse gui=inverse')
- eq('1', eval([[synIDattr(hlID("Mine"), "reverse", 'cterm')]]))
- eq('1', eval([[synIDattr(hlID("Mine"), "inverse", 'cterm')]]))
- eq('1', eval([[synIDattr(hlID("Mine"), "reverse", 'gui')]]))
- eq('1', eval([[synIDattr(hlID("Mine"), "inverse", 'gui')]]))
+ it(':hi Mine cterm=reverse gui=inverse', function()
+ command(':hi Mine cterm=reverse gui=inverse')
+ eq('1', eval([[synIDattr(hlID("Mine"), "reverse", 'cterm')]]))
+ eq('1', eval([[synIDattr(hlID("Mine"), "inverse", 'cterm')]]))
+ eq('1', eval([[synIDattr(hlID("Mine"), "reverse", 'gui')]]))
+ eq('1', eval([[synIDattr(hlID("Mine"), "inverse", 'gui')]]))
+ end)
end)
diff --git a/test/functional/legacy/tabline_spec.lua b/test/functional/legacy/tabline_spec.lua
new file mode 100644
index 0000000000..6b368d1857
--- /dev/null
+++ b/test/functional/legacy/tabline_spec.lua
@@ -0,0 +1,100 @@
+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('tabline', function()
+ local screen
+
+ before_each(function()
+ screen = Screen.new(50, 7)
+ screen:attach()
+ end)
+
+ -- oldtest: Test_tabline_showcmd()
+ it('showcmdloc=tabline works', function()
+ screen:set_default_attr_ids({
+ [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [1] = {background = Screen.colors.LightGrey}, -- Visual
+ [2] = {bold = true}, -- MoreMsg, TabLineSel
+ [3] = {reverse = true}, -- TabLineFill
+ [4] = {background = Screen.colors.LightGrey, underline = true}, -- TabLine
+ [5] = {background = Screen.colors.LightGrey, foreground = Screen.colors.DarkBlue}, -- Folded
+ })
+ exec([[
+ func MyTabLine()
+ return '%S'
+ endfunc
+
+ set showcmd
+ set showtabline=2
+ set tabline=%!MyTabLine()
+ set showcmdloc=tabline
+ call setline(1, ['a', 'b', 'c'])
+ set foldopen+=jump
+ 1,2fold
+ 3
+ ]])
+
+ feed('g')
+ screen:expect([[
+ {3:g }|
+ {5:+-- 2 lines: a···································}|
+ ^c |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ -- typing "gg" should open the fold
+ feed('g')
+ screen:expect([[
+ {3: }|
+ ^a |
+ b |
+ c |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ feed('<C-V>Gl')
+ screen:expect([[
+ {3:3x2 }|
+ {1:a} |
+ {1:b} |
+ {1:c}^ |
+ {0:~ }|
+ {0:~ }|
+ {2:-- VISUAL BLOCK --} |
+ ]])
+
+ feed('<Esc>1234')
+ screen:expect([[
+ {3:1234 }|
+ a |
+ b |
+ ^c |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ feed('<Esc>:set tabline=<CR>')
+ feed(':<CR>')
+ feed('1234')
+ screen:expect([[
+ {2: + [No Name] }{3: }{4:1234}{3: }|
+ a |
+ b |
+ ^c |
+ {0:~ }|
+ {0:~ }|
+ : |
+ ]])
+ end)
+end)
diff --git a/test/functional/legacy/vimscript_spec.lua b/test/functional/legacy/vimscript_spec.lua
new file mode 100644
index 0000000000..f59a87f824
--- /dev/null
+++ b/test/functional/legacy/vimscript_spec.lua
@@ -0,0 +1,90 @@
+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
+local meths = helpers.meths
+
+before_each(clear)
+
+describe('Vim script', function()
+ -- oldtest: Test_deep_nest()
+ it('Error when if/for/while/try/function is nested too deep',function()
+ local screen = Screen.new(80, 24)
+ screen:attach()
+ meths.set_option('laststatus', 2)
+ exec([[
+ " 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
+
+ " Deep nesting of function ... endfunction
+ func Test5()
+ let @a = join(repeat(['function X()'], 51), "\n")
+ let @a ..= "\necho v:true\n"
+ let @a ..= join(repeat(['endfunction'], 51), "\n")
+ @a
+ let @a = ''
+ endfunc
+ ]])
+ screen:expect({any = '%[No Name%]'})
+ feed(':call Test1()<CR>')
+ screen:expect({any = 'E579: '})
+ feed('<C-C>')
+ screen:expect({any = '%[No Name%]'})
+ feed(':call Test2()<CR>')
+ screen:expect({any = 'E585: '})
+ feed('<C-C>')
+ screen:expect({any = '%[No Name%]'})
+ feed(':call Test3()<CR>')
+ screen:expect({any = 'E585: '})
+ feed('<C-C>')
+ screen:expect({any = '%[No Name%]'})
+ feed(':call Test4()<CR>')
+ screen:expect({any = 'E601: '})
+ feed('<C-C>')
+ screen:expect({any = '%[No Name%]'})
+ feed(':call Test5()<CR>')
+ screen:expect({any = 'E1058: '})
+ end)
+
+ -- oldtest: Test_typed_script_var()
+ it('using s: with a typed command', function()
+ local screen = Screen.new(80, 24)
+ screen:attach()
+ feed(":echo get(s:, 'foo', 'x')\n")
+ screen:expect({any = 'E116: '})
+ end)
+end)
diff --git a/test/functional/legacy/visual_mode_spec.lua b/test/functional/legacy/visual_mode_spec.lua
index 8b5dd0c2dc..1a08fb4c0e 100644
--- a/test/functional/legacy/visual_mode_spec.lua
+++ b/test/functional/legacy/visual_mode_spec.lua
@@ -1,31 +1,28 @@
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
-local call = helpers.call
local clear = helpers.clear
local feed = helpers.feed
-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
+before_each(clear)
+describe('visual line mode', function()
+ -- oldtest: Test_visual_block_scroll()
it('redraws properly after scrolling with matchparen loaded and scrolloff=1', function()
- clear{args={'-u', 'NORC'}}
- screen = Screen.new(30, 7)
+ local screen = Screen.new(30, 7)
screen:attach()
screen:set_default_attr_ids({
[1] = {bold = true},
[2] = {background = Screen.colors.LightGrey},
})
- eq(1, meths.get_var('loaded_matchparen'))
- feed_command('set scrolloff=1')
- funcs.setline(1, {'a', 'b', 'c', 'd', 'e', '', '{', '}', '{', 'f', 'g', '}'})
- call('cursor', 5, 1)
+ exec([[
+ source $VIMRUNTIME/plugin/matchparen.vim
+ set scrolloff=1
+ call setline(1, ['a', 'b', 'c', 'd', 'e', '', '{', '}', '{', 'f', 'g', '}'])
+ call cursor(5, 1)
+ ]])
feed('V<c-d><c-d>')
screen:expect([[
@@ -41,8 +38,8 @@ describe('visual line mode', function()
end)
describe('visual block mode', function()
+ -- oldtest: Test_visual_block_with_virtualedit()
it('shows selection correctly with virtualedit=block', function()
- clear()
local screen = Screen.new(30, 7)
screen:set_default_attr_ids({
[1] = {bold = true}, -- ModeMsg
diff --git a/test/functional/legacy/window_cmd_spec.lua b/test/functional/legacy/window_cmd_spec.lua
new file mode 100644
index 0000000000..0e9775060d
--- /dev/null
+++ b/test/functional/legacy/window_cmd_spec.lua
@@ -0,0 +1,227 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear = helpers.clear
+local exec = helpers.exec
+local exec_lua = helpers.exec_lua
+local feed = helpers.feed
+
+describe('splitkeep', function()
+ local screen
+
+ before_each(function()
+ clear('--cmd', 'set splitkeep=screen')
+ screen = Screen.new()
+ screen:attach()
+ end)
+
+ -- oldtest: Test_splitkeep_callback()
+ it('does not scroll when split in callback', function()
+ exec([[
+ call setline(1, range(&lines))
+ function C1(a, b, c)
+ split | wincmd p
+ endfunction
+ function C2(a, b, c)
+ close | split
+ endfunction
+ ]])
+ exec_lua([[
+ vim.api.nvim_set_keymap("n", "j", "", { callback = function()
+ vim.cmd("call jobstart([&sh, &shcf, 'true'], { 'on_exit': 'C1' })")
+ end
+ })]])
+ exec_lua([[
+ vim.api.nvim_set_keymap("n", "t", "", { callback = function()
+ vim.api.nvim_set_current_win(
+ vim.api.nvim_open_win(vim.api.nvim_create_buf(false, {}), false, {
+ width = 10,
+ relative = "cursor",
+ height = 4,
+ row = 0,
+ col = 0,
+ }))
+ vim.cmd("call termopen([&sh, &shcf, 'true'], { 'on_exit': 'C2' })")
+ end
+ })]])
+ feed('j')
+ screen:expect([[
+ 0 |
+ 1 |
+ 2 |
+ 3 |
+ 4 |
+ 5 |
+ [No Name] [+] |
+ ^7 |
+ 8 |
+ 9 |
+ 10 |
+ 11 |
+ [No Name] [+] |
+ |
+ ]])
+ feed(':quit<CR>Ht')
+ screen:expect([[
+ ^0 |
+ 1 |
+ 2 |
+ 3 |
+ 4 |
+ 5 |
+ [No Name] [+] |
+ 7 |
+ 8 |
+ 9 |
+ 10 |
+ 11 |
+ [No Name] [+] |
+ :quit |
+ ]])
+ feed(':set sb<CR>:quit<CR>Gj')
+ screen:expect([[
+ 1 |
+ 2 |
+ 3 |
+ 4 |
+ ^5 |
+ [No Name] [+] |
+ 7 |
+ 8 |
+ 9 |
+ 10 |
+ 11 |
+ 12 |
+ [No Name] [+] |
+ :quit |
+ ]])
+ feed(':quit<CR>Gt')
+ screen:expect([[
+ 1 |
+ 2 |
+ 3 |
+ 4 |
+ 5 |
+ [No Name] [+] |
+ 7 |
+ 8 |
+ 9 |
+ 10 |
+ 11 |
+ ^12 |
+ [No Name] [+] |
+ :quit |
+ ]])
+ end)
+
+ -- oldtest: Test_splitkeep_fold()
+ it('does not scroll when window has closed folds', function()
+ exec([[
+ set splitkeep=screen
+ set foldmethod=marker
+ set number
+ let line = 1
+ for n in range(1, &lines)
+ call setline(line, ['int FuncName() {/*{{{*/', 1, 2, 3, 4, 5, '}/*}}}*/',
+ \ 'after fold'])
+ let line += 8
+ endfor
+ ]])
+ feed('L:wincmd s<CR>')
+ screen:expect([[
+ 1 +-- 7 lines: int FuncName() {···················|
+ 8 after fold |
+ 9 +-- 7 lines: int FuncName() {···················|
+ 16 after fold |
+ 17 +-- 7 lines: int FuncName() {···················|
+ 24 ^after fold |
+ [No Name] [+] |
+ 32 after fold |
+ 33 +-- 7 lines: int FuncName() {···················|
+ 40 after fold |
+ 41 +-- 7 lines: int FuncName() {···················|
+ 48 after fold |
+ [No Name] [+] |
+ :wincmd s |
+ ]])
+ feed(':quit<CR>')
+ screen:expect([[
+ 1 +-- 7 lines: int FuncName() {···················|
+ 8 after fold |
+ 9 +-- 7 lines: int FuncName() {···················|
+ 16 after fold |
+ 17 +-- 7 lines: int FuncName() {···················|
+ 24 after fold |
+ 25 +-- 7 lines: int FuncName() {···················|
+ 32 after fold |
+ 33 +-- 7 lines: int FuncName() {···················|
+ 40 after fold |
+ 41 +-- 7 lines: int FuncName() {···················|
+ 48 after fold |
+ 49 ^+-- 7 lines: int FuncName() {···················|
+ :quit |
+ ]])
+ feed('H:below split<CR>')
+ screen:expect([[
+ 1 +-- 7 lines: int FuncName() {···················|
+ 8 after fold |
+ 9 +-- 7 lines: int FuncName() {···················|
+ 16 after fold |
+ 17 +-- 7 lines: int FuncName() {···················|
+ [No Name] [+] |
+ 25 ^+-- 7 lines: int FuncName() {···················|
+ 32 after fold |
+ 33 +-- 7 lines: int FuncName() {···················|
+ 40 after fold |
+ 41 +-- 7 lines: int FuncName() {···················|
+ 48 after fold |
+ [No Name] [+] |
+ :below split |
+ ]])
+ feed(':wincmd k<CR>:quit<CR>')
+ screen:expect([[
+ 1 +-- 7 lines: int FuncName() {···················|
+ 8 after fold |
+ 9 +-- 7 lines: int FuncName() {···················|
+ 16 after fold |
+ 17 +-- 7 lines: int FuncName() {···················|
+ 24 after fold |
+ 25 ^+-- 7 lines: int FuncName() {···················|
+ 32 after fold |
+ 33 +-- 7 lines: int FuncName() {···················|
+ 40 after fold |
+ 41 +-- 7 lines: int FuncName() {···················|
+ 48 after fold |
+ 49 +-- 7 lines: int FuncName() {···················|
+ :quit |
+ ]])
+ end)
+
+ -- oldtest: Test_splitkeep_status()
+ it('does not scroll when split in callback', function()
+ exec([[
+ call setline(1, ['a', 'b', 'c'])
+ set nomodified
+ set splitkeep=screen
+ let win = winnr()
+ wincmd s
+ wincmd j
+ ]])
+ feed(':call win_move_statusline(win, 1)<CR>')
+ screen:expect([[
+ a |
+ b |
+ c |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ [No Name] |
+ ^a |
+ b |
+ c |
+ ~ |
+ [No Name] |
+ |
+ ]])
+ end)
+end)
diff --git a/test/functional/lua/api_spec.lua b/test/functional/lua/api_spec.lua
index f173a15d32..03832978a4 100644
--- a/test/functional/lua/api_spec.lua
+++ b/test/functional/lua/api_spec.lua
@@ -166,7 +166,7 @@ describe('luaeval(vim.api.…)', function()
eq({v={}}, funcs.luaeval('vim.api.nvim__id_dictionary({v={[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2}})'))
-- If API requests dictionary, then empty table will be the one. This is not
- -- the case normally because empty table is an empty arrray.
+ -- the case normally because empty table is an empty array.
eq({}, funcs.luaeval('vim.api.nvim__id_dictionary({})'))
eq(4, eval([[type(luaeval('vim.api.nvim__id_dictionary({})'))]]))
end)
diff --git a/test/functional/lua/buffer_updates_spec.lua b/test/functional/lua/buffer_updates_spec.lua
index 10de45274c..2fd44b8b5f 100644
--- a/test/functional/lua/buffer_updates_spec.lua
+++ b/test/functional/lua/buffer_updates_spec.lua
@@ -118,6 +118,24 @@ describe('lua buffer event callbacks: on_lines', function()
}
tick = tick + 1
+ tick = tick + 1
+ command('redo')
+ check_events {
+ { "test1", "lines", 1, tick, 3, 5, 4, 32 };
+ { "test2", "lines", 1, tick, 3, 5, 4, 32 };
+ { "test2", "changedtick", 1, tick+1 };
+ }
+ tick = tick + 1
+
+ tick = tick + 1
+ command('undo!')
+ check_events {
+ { "test1", "lines", 1, tick, 3, 4, 5, 13 };
+ { "test2", "lines", 1, tick, 3, 4, 5, 13 };
+ { "test2", "changedtick", 1, tick+1 };
+ }
+ tick = tick + 1
+
-- simulate next callback returning true
exec_lua("test_unreg = 'test1'")
@@ -315,7 +333,7 @@ describe('lua: nvim_buf_attach on_bytes', function()
start_txt = meths.buf_get_lines(0, 0, -1, true)
end
local shadowbytes = table.concat(start_txt, '\n') .. '\n'
- -- TODO: while we are brewing the real strong coffe,
+ -- TODO: while we are brewing the real strong coffee,
-- verify should check buf_get_offset after every check_events
if verify then
local len = meths.buf_get_offset(0, meths.buf_line_count(0))
diff --git a/test/functional/lua/diagnostic_spec.lua b/test/functional/lua/diagnostic_spec.lua
index f9647f5b6a..d364986ad7 100644
--- a/test/functional/lua/diagnostic_spec.lua
+++ b/test/functional/lua/diagnostic_spec.lua
@@ -16,7 +16,7 @@ describe('vim.diagnostic', function()
exec_lua [[
require('vim.diagnostic')
- function make_diagnostic(msg, x1, y1, x2, y2, severity, source)
+ function make_diagnostic(msg, x1, y1, x2, y2, severity, source, code)
return {
lnum = x1,
col = y1,
@@ -25,23 +25,24 @@ describe('vim.diagnostic', function()
message = msg,
severity = severity,
source = source,
+ code = code,
}
end
- function make_error(msg, x1, y1, x2, y2, source)
- return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.ERROR, source)
+ function make_error(msg, x1, y1, x2, y2, source, code)
+ return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.ERROR, source, code)
end
- function make_warning(msg, x1, y1, x2, y2, source)
- return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.WARN, source)
+ function make_warning(msg, x1, y1, x2, y2, source, code)
+ return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.WARN, source, code)
end
- function make_info(msg, x1, y1, x2, y2, source)
- return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.INFO, source)
+ function make_info(msg, x1, y1, x2, y2, source, code)
+ return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.INFO, source, code)
end
- function make_hint(msg, x1, y1, x2, y2, source)
- return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.HINT, source)
+ function make_hint(msg, x1, y1, x2, y2, source, code)
+ return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.HINT, source, code)
end
function count_diagnostics(bufnr, severity, namespace)
@@ -89,20 +90,25 @@ describe('vim.diagnostic', function()
'DiagnosticFloatingError',
'DiagnosticFloatingHint',
'DiagnosticFloatingInfo',
+ 'DiagnosticFloatingOk',
'DiagnosticFloatingWarn',
'DiagnosticHint',
'DiagnosticInfo',
+ 'DiagnosticOk',
'DiagnosticSignError',
'DiagnosticSignHint',
'DiagnosticSignInfo',
+ 'DiagnosticSignOk',
'DiagnosticSignWarn',
'DiagnosticUnderlineError',
'DiagnosticUnderlineHint',
'DiagnosticUnderlineInfo',
+ 'DiagnosticUnderlineOk',
'DiagnosticUnderlineWarn',
'DiagnosticVirtualTextError',
'DiagnosticVirtualTextHint',
'DiagnosticVirtualTextInfo',
+ 'DiagnosticVirtualTextOk',
'DiagnosticVirtualTextWarn',
'DiagnosticWarn',
}, exec_lua([[return vim.fn.getcompletion('Diagnostic', 'highlight')]]))
@@ -128,6 +134,55 @@ describe('vim.diagnostic', function()
eq('Diagnostic #1', result[1].message)
end)
+ it('removes diagnostics from the cache when a buffer is removed', function()
+ eq(2, exec_lua [[
+ vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
+ local other_bufnr = vim.fn.bufadd('test | test')
+ local lines = vim.api.nvim_buf_get_lines(diagnostic_bufnr, 0, -1, true)
+ vim.api.nvim_buf_set_lines(other_bufnr, 0, 1, false, lines)
+ vim.cmd('bunload! ' .. other_bufnr)
+
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
+ make_error('Diagnostic #1', 1, 1, 1, 1),
+ make_error('Diagnostic #2', 2, 1, 2, 1),
+ })
+ vim.diagnostic.set(diagnostic_ns, other_bufnr, {
+ make_error('Diagnostic #3', 3, 1, 3, 1),
+ })
+ vim.api.nvim_set_current_buf(other_bufnr)
+ vim.opt_local.buflisted = true
+ vim.cmd('bwipeout!')
+ return #vim.diagnostic.get()
+ ]])
+ eq(2, exec_lua [[
+ vim.api.nvim_set_current_buf(diagnostic_bufnr)
+ vim.opt_local.buflisted = false
+ return #vim.diagnostic.get()
+ ]])
+ eq(0, exec_lua [[
+ vim.cmd('bwipeout!')
+ return #vim.diagnostic.get()
+ ]])
+ end)
+
+ it('removes diagnostic from stale cache on reset', function()
+ local diagnostics = exec_lua [[
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
+ make_error('Diagnostic #1', 1, 1, 1, 1),
+ make_error('Diagnostic #2', 2, 1, 2, 1),
+ })
+ local other_bufnr = vim.fn.bufadd('test | test')
+ vim.cmd('noautocmd bwipeout! ' .. diagnostic_bufnr)
+ return vim.diagnostic.get(diagnostic_bufnr)
+ ]]
+ eq(2, #diagnostics)
+ diagnostics = exec_lua [[
+ vim.diagnostic.reset()
+ return vim.diagnostic.get()
+ ]]
+ eq(0, #diagnostics)
+ end)
+
it('resolves buffer number 0 to the current buffer', function()
eq(2, exec_lua [[
vim.api.nvim_set_current_buf(diagnostic_bufnr)
@@ -1129,6 +1184,44 @@ end)
eq(" some_linter: 👀 Warning", result[1][2][1])
eq(" another_linter: 🔥 Error", result[2][2][1])
end)
+
+ it('can add a suffix to virtual text', function()
+ eq(' Some error ✘', exec_lua [[
+ local diagnostics = {
+ make_error('Some error', 0, 0, 0, 0),
+ }
+
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics, {
+ underline = false,
+ virtual_text = {
+ prefix = '',
+ suffix = ' ✘',
+ }
+ })
+
+ local extmarks = get_virt_text_extmarks(diagnostic_ns)
+ local virt_text = extmarks[1][4].virt_text[2][1]
+ return virt_text
+ ]])
+
+ eq(' Some error [err-code]', exec_lua [[
+ local diagnostics = {
+ make_error('Some error', 0, 0, 0, 0, nil, 'err-code'),
+ }
+
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics, {
+ underline = false,
+ virtual_text = {
+ prefix = '',
+ suffix = function(diag) return string.format(' [%s]', diag.code) end,
+ }
+ })
+
+ local extmarks = get_virt_text_extmarks(diagnostic_ns)
+ local virt_text = extmarks[1][4].virt_text[2][1]
+ return virt_text
+ ]])
+ end)
end)
describe('set()', function()
@@ -1745,10 +1838,55 @@ end)
return lines
]])
- eq("Error executing lua: .../diagnostic.lua:0: prefix: expected 'string' or 'table' or 'function', got 42",
+ eq(".../diagnostic.lua:0: prefix: expected string|table|function, got number",
pcall_err(exec_lua, [[ vim.diagnostic.open_float({ prefix = 42 }) ]]))
end)
+ it('can add a suffix to diagnostics', function()
+ -- Default is to render the diagnostic error code
+ eq({'1. Syntax error [code-x]', '2. Some warning [code-y]'}, exec_lua [[
+ local diagnostics = {
+ make_error("Syntax error", 0, 1, 0, 3, nil, "code-x"),
+ make_warning("Some warning", 1, 1, 1, 3, nil, "code-y"),
+ }
+ vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
+ local float_bufnr, winnr = vim.diagnostic.open_float({header = false, scope = "buffer"})
+ local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
+ vim.api.nvim_win_close(winnr, true)
+ return lines
+ ]])
+
+ eq({'1. Syntax error', '2. Some warning'}, exec_lua [[
+ local diagnostics = {
+ make_error("Syntax error", 0, 1, 0, 3, nil, "code-x"),
+ make_warning("Some warning", 1, 1, 1, 3, nil, "code-y"),
+ }
+ vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
+ local float_bufnr, winnr = vim.diagnostic.open_float({header = false, scope = "buffer", suffix = ""})
+ local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
+ vim.api.nvim_win_close(winnr, true)
+ return lines
+ ]])
+
+ -- Suffix is rendered on the last line of a multiline diagnostic
+ eq({'1. Syntax error', ' More context [code-x]'}, exec_lua [[
+ local diagnostics = {
+ make_error("Syntax error\nMore context", 0, 1, 0, 3, nil, "code-x"),
+ }
+ vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
+ local float_bufnr, winnr = vim.diagnostic.open_float({header = false, scope = "buffer"})
+ local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
+ vim.api.nvim_win_close(winnr, true)
+ return lines
+ ]])
+
+ eq(".../diagnostic.lua:0: suffix: expected string|table|function, got number",
+ pcall_err(exec_lua, [[ vim.diagnostic.open_float({ suffix = 42 }) ]]))
+ end)
+
it('works with the old signature', function()
eq({'1. Syntax error'}, exec_lua [[
local diagnostics = {
@@ -1952,19 +2090,26 @@ end)
end)
it('triggers the autocommand when diagnostics are set', function()
- eq(true, exec_lua [[
+ eq({true, 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 = +expand("<abuf>")')
+ local triggered = {}
+ vim.api.nvim_create_autocmd('DiagnosticChanged', {
+ callback = function(args)
+ triggered = {args.buf, #args.data.diagnostics}
+ end,
+ })
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 == diagnostic_bufnr
+ return {
+ triggered[1] == diagnostic_bufnr,
+ triggered[2] == 1,
+ }
]])
end)
@@ -1979,5 +2124,31 @@ end)
return vim.g.diagnostic_autocmd_triggered == diagnostic_bufnr
]])
end)
+
+ it("checks if diagnostics are disabled in a buffer", function()
+ eq({true, true, true , true}, exec_lua [[
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
+ make_error('Diagnostic #1', 1, 1, 1, 1),
+ })
+ vim.api.nvim_set_current_buf(diagnostic_bufnr)
+ vim.diagnostic.disable()
+ return {
+ vim.diagnostic.is_disabled(),
+ vim.diagnostic.is_disabled(diagnostic_bufnr),
+ vim.diagnostic.is_disabled(diagnostic_bufnr, diagnostic_ns),
+ vim.diagnostic.is_disabled(_, diagnostic_ns),
+ }
+ ]])
+
+ eq({false, false, false , false}, exec_lua [[
+ vim.diagnostic.enable()
+ return {
+ vim.diagnostic.is_disabled(),
+ vim.diagnostic.is_disabled(diagnostic_bufnr),
+ vim.diagnostic.is_disabled(diagnostic_bufnr, diagnostic_ns),
+ vim.diagnostic.is_disabled(_, diagnostic_ns),
+ }
+ ]])
+ end)
end)
end)
diff --git a/test/functional/lua/ffi_spec.lua b/test/functional/lua/ffi_spec.lua
index 80c01a2b8c..18b13a8959 100644
--- a/test/functional/lua/ffi_spec.lua
+++ b/test/functional/lua/ffi_spec.lua
@@ -29,32 +29,37 @@ describe('ffi.cdef', function()
typedef struct window_S win_T;
typedef struct {} stl_hlrec_t;
typedef struct {} StlClickRecord;
+ typedef struct {} statuscol_T;
typedef struct {} Error;
win_T *find_window_by_handle(int Window, Error *err);
int build_stl_str_hl(
win_T *wp,
- char_u *out,
+ char *out,
size_t outlen,
- char_u *fmt,
- int use_sandbox,
- char_u fillchar,
+ char *fmt,
+ char *opt_name,
+ int opt_scope,
+ int fillchar,
int maxwidth,
stl_hlrec_t **hltab,
- StlClickRecord **tabtab
+ StlClickRecord **tabtab,
+ statuscol_T *scp
);
]]
return ffi.C.build_stl_str_hl(
ffi.C.find_window_by_handle(0, ffi.new('Error')),
- ffi.new('char_u[1024]'),
+ ffi.new('char[1024]'),
1024,
- ffi.cast('char_u*', 'StatusLineOfLength20'),
+ ffi.cast('char*', 'StatusLineOfLength20'),
+ nil,
0,
0,
0,
nil,
+ nil,
nil
)
]=])
diff --git a/test/functional/lua/filetype_spec.lua b/test/functional/lua/filetype_spec.lua
index be57b2db31..2a7be53164 100644
--- a/test/functional/lua/filetype_spec.lua
+++ b/test/functional/lua/filetype_spec.lua
@@ -1,6 +1,7 @@
local helpers = require('test.functional.helpers')(after_each)
local exec_lua = helpers.exec_lua
local eq = helpers.eq
+local meths = helpers.meths
local clear = helpers.clear
local pathroot = helpers.pathroot
local command = helpers.command
@@ -94,3 +95,10 @@ describe('vim.filetype', function()
]])
end)
end)
+
+describe('filetype.lua', function()
+ it('does not override user autocommands that set filetype #20333', function()
+ clear({args={'--clean', '--cmd', 'autocmd BufRead *.md set filetype=notmarkdown', 'README.md'}})
+ eq('notmarkdown', meths.buf_get_option(0, 'filetype'))
+ end)
+end)
diff --git a/test/functional/lua/fs_spec.lua b/test/functional/lua/fs_spec.lua
index 2bcc84db0f..642d36f63a 100644
--- a/test/functional/lua/fs_spec.lua
+++ b/test/functional/lua/fs_spec.lua
@@ -1,4 +1,5 @@
local helpers = require('test.functional.helpers')(after_each)
+local lfs = require('lfs')
local clear = helpers.clear
local exec_lua = helpers.exec_lua
@@ -7,10 +8,43 @@ 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 is_os = helpers.is_os
-local nvim_prog_basename = iswin() and 'nvim.exe' or 'nvim'
+local nvim_prog_basename = is_os('win') and 'nvim.exe' or 'nvim'
+
+local test_basename_dirname_eq = {
+ '~/foo/',
+ '~/foo',
+ '~/foo/bar.lua',
+ 'foo.lua',
+ ' ',
+ '',
+ '.',
+ '..',
+ '../',
+ '~',
+ '/usr/bin',
+ '/usr/bin/gcc',
+ '/',
+ '/usr/',
+ '/usr',
+ 'c:/usr',
+ 'c:/',
+ 'c:',
+ 'c:/users/foo',
+ 'c:/users/foo/bar.lua',
+ 'c:/users/foo/bar/../',
+}
+
+local tests_windows_paths = {
+ 'c:\\usr',
+ 'c:\\',
+ 'c:',
+ 'c:\\users\\foo',
+ 'c:\\users\\foo\\bar.lua',
+ 'c:\\users\\foo\\bar\\..\\',
+}
before_each(clear)
@@ -41,19 +75,73 @@ describe('vim.fs', function()
local nvim_dir = ...
return vim.fs.dirname(nvim_dir)
]], nvim_dir))
+
+ local function test_paths(paths)
+ for _, path in ipairs(paths) do
+ eq(
+ exec_lua([[
+ local path = ...
+ return vim.fn.fnamemodify(path,':h'):gsub('\\', '/')
+ ]], path),
+ exec_lua([[
+ local path = ...
+ return vim.fs.dirname(path)
+ ]], path),
+ path
+ )
+ end
+ end
+
+ test_paths(test_basename_dirname_eq)
+ if is_os('win') then
+ test_paths(tests_windows_paths)
+ end
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))
+
+ local function test_paths(paths)
+ for _, path in ipairs(paths) do
+ eq(
+ exec_lua([[
+ local path = ...
+ return vim.fn.fnamemodify(path,':t'):gsub('\\', '/')
+ ]], path),
+ exec_lua([[
+ local path = ...
+ return vim.fs.basename(path)
+ ]], path),
+ path
+ )
+ end
+ end
+
+ test_paths(test_basename_dirname_eq)
+ if is_os('win') then
+ test_paths(tests_windows_paths)
+ end
end)
end)
describe('dir()', function()
+ before_each(function()
+ lfs.mkdir('testd')
+ lfs.mkdir('testd/a')
+ lfs.mkdir('testd/a/b')
+ lfs.mkdir('testd/a/b/c')
+ end)
+
+ after_each(function()
+ rmdir('testd')
+ end)
+
it('works', function()
eq(true, exec_lua([[
local dir, nvim = ...
@@ -65,6 +153,71 @@ describe('vim.fs', function()
return false
]], nvim_dir, nvim_prog_basename))
end)
+
+ it('works with opts.depth and opts.skip', function()
+ io.open('testd/a1', 'w'):close()
+ io.open('testd/b1', 'w'):close()
+ io.open('testd/c1', 'w'):close()
+ io.open('testd/a/a2', 'w'):close()
+ io.open('testd/a/b2', 'w'):close()
+ io.open('testd/a/c2', 'w'):close()
+ io.open('testd/a/b/a3', 'w'):close()
+ io.open('testd/a/b/b3', 'w'):close()
+ io.open('testd/a/b/c3', 'w'):close()
+ io.open('testd/a/b/c/a4', 'w'):close()
+ io.open('testd/a/b/c/b4', 'w'):close()
+ io.open('testd/a/b/c/c4', 'w'):close()
+
+ local function run(dir, depth, skip)
+ local r = exec_lua([[
+ local dir, depth, skip = ...
+ local r = {}
+ local skip_f
+ if skip then
+ skip_f = function(n)
+ if vim.tbl_contains(skip or {}, n) then
+ return false
+ end
+ end
+ end
+ for name, type_ in vim.fs.dir(dir, { depth = depth, skip = skip_f }) do
+ r[name] = type_
+ end
+ return r
+ ]], dir, depth, skip)
+ return r
+ end
+
+ local exp = {}
+
+ exp['a1'] = 'file'
+ exp['b1'] = 'file'
+ exp['c1'] = 'file'
+ exp['a'] = 'directory'
+
+ eq(exp, run('testd', 1))
+
+ exp['a/a2'] = 'file'
+ exp['a/b2'] = 'file'
+ exp['a/c2'] = 'file'
+ exp['a/b'] = 'directory'
+
+ eq(exp, run('testd', 2))
+
+ exp['a/b/a3'] = 'file'
+ exp['a/b/b3'] = 'file'
+ exp['a/b/c3'] = 'file'
+ exp['a/b/c'] = 'directory'
+
+ eq(exp, run('testd', 3))
+ eq(exp, run('testd', 999, {'a/b/c'}))
+
+ exp['a/b/c/a4'] = 'file'
+ exp['a/b/c/b4'] = 'file'
+ exp['a/b/c/c4'] = 'file'
+
+ eq(exp, run('testd', 999))
+ end)
end)
describe('find()', function()
@@ -77,6 +230,28 @@ describe('vim.fs', function()
local dir, nvim = ...
return vim.fs.find(nvim, { path = dir, type = 'file' })
]], test_build_dir, nvim_prog_basename))
+ eq({nvim_dir}, exec_lua([[
+ local dir = ...
+ local parent, name = dir:match('^(.*/)([^/]+)$')
+ return vim.fs.find(name, { path = parent, upward = true, type = 'directory' })
+ ]], nvim_dir))
+ end)
+
+ it('accepts predicate as names', function()
+ eq({test_build_dir}, exec_lua([[
+ local dir = ...
+ local opts = { path = dir, upward = true, type = 'directory' }
+ return vim.fs.find(function(x) return x == 'build' end, opts)
+ ]], nvim_dir))
+ eq({nvim_prog}, exec_lua([[
+ local dir, nvim = ...
+ return vim.fs.find(function(x) return x == nvim end, { path = dir, type = 'file' })
+ ]], test_build_dir, nvim_prog_basename))
+ eq({}, exec_lua([[
+ local dir = ...
+ local opts = { path = dir, upward = true, type = 'directory' }
+ return vim.fs.find(function(x) return x == 'no-match' end, opts)
+ ]], nvim_dir))
end)
end)
@@ -85,7 +260,7 @@ describe('vim.fs', function()
eq('C:/Users/jdoe', exec_lua [[ return vim.fs.normalize('C:\\Users\\jdoe') ]])
end)
it('works with ~', function()
- if iswin() then
+ if is_os('win') then
pending([[$HOME does not exist on Windows ¯\_(ツ)_/¯]])
end
eq(os.getenv('HOME') .. '/src/foo', exec_lua [[ return vim.fs.normalize('~/src/foo') ]])
diff --git a/test/functional/lua/help_spec.lua b/test/functional/lua/help_spec.lua
new file mode 100644
index 0000000000..b396e2ba30
--- /dev/null
+++ b/test/functional/lua/help_spec.lua
@@ -0,0 +1,50 @@
+-- Tests for gen_help_html.lua. Validates :help tags/links and HTML doc generation.
+--
+-- TODO: extract parts of gen_help_html.lua into Nvim stdlib?
+
+local helpers = require('test.functional.helpers')(after_each)
+local clear = helpers.clear
+local exec_lua = helpers.exec_lua
+local eq = helpers.eq
+local ok = helpers.ok
+
+describe(':help docs', function()
+ before_each(clear)
+ it('validate', function()
+ -- If this test fails, try these steps (in order):
+ -- 1. Fix/cleanup the :help docs.
+ -- 2. Fix the parser: https://github.com/neovim/tree-sitter-vimdoc
+ -- 3. File a parser bug, and adjust the tolerance of this test in the meantime.
+
+ local rv = exec_lua([[return require('scripts.gen_help_html').validate('./build/runtime/doc')]])
+ -- Check that we actually found helpfiles.
+ ok(rv.helpfiles > 100, '>100 :help files', rv.helpfiles)
+ eq({}, rv.invalid_links, 'invalid tags in :help docs')
+ eq({}, rv.invalid_urls, 'invalid URLs in :help docs')
+ -- Check that parse errors did not increase wildly.
+ -- TODO: Fix all parse errors in :help files.
+ ok(rv.err_count < 250, '<250 parse errors', rv.err_count)
+ end)
+
+ it('gen_help_html.lua generates HTML', function()
+ -- 1. Test that gen_help_html.lua actually works.
+ -- 2. Test that parse errors did not increase wildly. Because we explicitly test only a few
+ -- :help files, we can be precise about the tolerances here.
+
+ local tmpdir = exec_lua('return vim.fs.dirname(vim.fn.tempname())')
+ -- Because gen() is slow (~30s), this test is limited to a few files.
+ local rv = exec_lua([[
+ local to_dir = ...
+ return require('scripts.gen_help_html').gen(
+ './build/runtime/doc',
+ to_dir,
+ { 'pi_health.txt', 'help.txt', 'index.txt', 'nvim.txt', }
+ )
+ ]],
+ tmpdir
+ )
+ eq(4, #rv.helpfiles)
+ eq(0, rv.err_count, 'parse errors in :help docs')
+ eq({}, rv.invalid_links, 'invalid tags in :help docs')
+ end)
+end)
diff --git a/test/functional/lua/inspector_spec.lua b/test/functional/lua/inspector_spec.lua
new file mode 100644
index 0000000000..5e488bb082
--- /dev/null
+++ b/test/functional/lua/inspector_spec.lua
@@ -0,0 +1,56 @@
+local helpers = require('test.functional.helpers')(after_each)
+local exec_lua = helpers.exec_lua
+local eq = helpers.eq
+local eval = helpers.eval
+local clear = helpers.clear
+
+describe('vim.inspect_pos', function()
+ before_each(function()
+ clear()
+ end)
+
+ it('it returns items', function()
+ local ret = exec_lua([[
+ local buf = vim.api.nvim_create_buf(true, false)
+ vim.api.nvim_set_current_buf(buf)
+ vim.api.nvim_buf_set_lines(0, 0, -1, false, {"local a = 123"})
+ vim.api.nvim_buf_set_option(buf, "filetype", "lua")
+ vim.cmd("syntax on")
+ return {buf, vim.inspect_pos(0, 0, 10)}
+ ]])
+ local buf, items = unpack(ret)
+ eq('', eval('v:errmsg'))
+ eq({
+ buffer = buf,
+ col = 10,
+ row = 0,
+ extmarks = {},
+ treesitter = {},
+ semantic_tokens = {},
+ syntax = {
+ {
+ hl_group = 'luaNumber',
+ hl_group_link = 'Constant',
+ },
+ },
+ }, items)
+ end)
+end)
+
+describe('vim.show_pos', function()
+ before_each(function()
+ clear()
+ end)
+
+ it('it does not error', function()
+ exec_lua([[
+ local buf = vim.api.nvim_create_buf(true, false)
+ vim.api.nvim_set_current_buf(buf)
+ vim.api.nvim_buf_set_lines(0, 0, -1, false, {"local a = 123"})
+ vim.api.nvim_buf_set_option(buf, "filetype", "lua")
+ vim.cmd("syntax on")
+ return {buf, vim.show_pos(0, 0, 10)}
+ ]])
+ eq('', eval('v:errmsg'))
+ end)
+end)
diff --git a/test/functional/lua/overrides_spec.lua b/test/functional/lua/overrides_spec.lua
index 32c1615a45..3f107811ae 100644
--- a/test/functional/lua/overrides_spec.lua
+++ b/test/functional/lua/overrides_spec.lua
@@ -8,12 +8,12 @@ local feed = helpers.feed
local clear = helpers.clear
local funcs = helpers.funcs
local meths = helpers.meths
-local iswin = helpers.iswin
local command = helpers.command
local write_file = helpers.write_file
local exec_capture = helpers.exec_capture
local exec_lua = helpers.exec_lua
local pcall_err = helpers.pcall_err
+local is_os = helpers.is_os
local screen
@@ -135,7 +135,7 @@ describe('print', function()
print("very slow")
vim.api.nvim_command("sleep 1m") -- force deferred event processing
end
- ]], (iswin() and "timeout 1") or "sleep 0.1")
+ ]], (is_os('win') and "timeout 1") or "sleep 0.1")
eq('very slow\nvery fast', exec_capture('lua test()'))
end)
end)
diff --git a/test/functional/lua/runtime_spec.lua b/test/functional/lua/runtime_spec.lua
index e9c34c9228..b659f2eacb 100644
--- a/test/functional/lua/runtime_spec.lua
+++ b/test/functional/lua/runtime_spec.lua
@@ -4,6 +4,7 @@ local clear = helpers.clear
local eq = helpers.eq
local eval = helpers.eval
local exec = helpers.exec
+local funcs = helpers.funcs
local mkdir_p = helpers.mkdir_p
local rmdir = helpers.rmdir
local write_file = helpers.write_file
@@ -29,6 +30,7 @@ describe('runtime:', function()
after_each(function()
rmdir(plug_dir)
+ exec('bwipe!')
end)
describe('colors', function()
@@ -39,6 +41,8 @@ describe('runtime:', function()
mkdir_p(colorscheme_folder)
write_file(colorscheme_file, [[vim.g.lua_colorscheme = 1]])
+ eq({'new_colorscheme'}, funcs.getcompletion('new_c', 'color'))
+
exec('colorscheme new_colorscheme')
eq(1, eval('g:lua_colorscheme'))
@@ -64,23 +68,25 @@ describe('runtime:', function()
it('loads lua compilers', function()
local compiler_file = compiler_folder .. sep .. 'new_compiler.lua'
mkdir_p(compiler_folder)
- write_file(compiler_file, [[vim.g.lua_compiler = 1]])
+ write_file(compiler_file, [[vim.b.lua_compiler = 1]])
+
+ eq({'new_compiler'}, funcs.getcompletion('new_c', 'compiler'))
exec('compiler new_compiler')
- eq(1, eval('g:lua_compiler'))
+ eq(1, eval('b:lua_compiler'))
rmdir(compiler_folder)
end)
it('loads vim compilers when both lua and vim version exist', function()
local compiler_file = compiler_folder .. sep .. 'new_compiler'
mkdir_p(compiler_folder)
- write_file(compiler_file..'.vim', [[let g:compiler = 'vim']])
- write_file(compiler_file..'.lua', [[vim.g.compiler = 'lua']])
+ write_file(compiler_file..'.vim', [[let b:compiler = 'vim']])
+ write_file(compiler_file..'.lua', [[vim.b.compiler = 'lua']])
exec('compiler new_compiler')
- eq('vim', eval('g:compiler'))
+ eq('vim', eval('b:compiler'))
rmdir(compiler_folder)
end)
end)
@@ -91,10 +97,12 @@ describe('runtime:', function()
it('loads lua ftplugins', function()
local ftplugin_file = table.concat({ftplugin_folder , 'new-ft.lua'}, sep)
mkdir_p(ftplugin_folder)
- write_file(ftplugin_file , [[vim.g.lua_ftplugin = 1]])
+ write_file(ftplugin_file , [[vim.b.lua_ftplugin = 1]])
+
+ eq({'new-ft'}, funcs.getcompletion('new-f', 'filetype'))
exec [[set filetype=new-ft]]
- eq(1, eval('g:lua_ftplugin'))
+ eq(1, eval('b:lua_ftplugin'))
rmdir(ftplugin_folder)
end)
end)
@@ -105,10 +113,12 @@ describe('runtime:', function()
it('loads lua indents', function()
local indent_file = table.concat({indent_folder , 'new-ft.lua'}, sep)
mkdir_p(indent_folder)
- write_file(indent_file , [[vim.g.lua_indent = 1]])
+ write_file(indent_file , [[vim.b.lua_indent = 1]])
+
+ eq({'new-ft'}, funcs.getcompletion('new-f', 'filetype'))
exec [[set filetype=new-ft]]
- eq(1, eval('g:lua_indent'))
+ eq(1, eval('b:lua_indent'))
rmdir(indent_folder)
end)
end)
@@ -116,24 +126,32 @@ describe('runtime:', function()
describe('syntax', function()
local syntax_folder = table.concat({plug_dir, 'syntax'}, sep)
- it('loads lua syntaxes on filetype change', function()
+ before_each(function()
local syntax_file = table.concat({syntax_folder , 'my-lang.lua'}, sep)
mkdir_p(syntax_folder)
- write_file(syntax_file , [[vim.g.lua_syntax = 1]])
+ write_file(syntax_file , [[vim.b.current_syntax = 'my-lang']])
+ exec([[let b:current_syntax = '']])
+ end)
+ it('loads lua syntaxes on filetype change', function()
exec('set filetype=my-lang')
- eq(1, eval('g:lua_syntax'))
- rmdir(syntax_folder)
+ eq('my-lang', eval('b:current_syntax'))
end)
it('loads lua syntaxes on syntax change', function()
- local syntax_file = table.concat({syntax_folder , 'my-lang.lua'}, sep)
- mkdir_p(syntax_folder)
- write_file(syntax_file , [[vim.g.lua_syntax = 5]])
-
exec('set syntax=my-lang')
- eq(5, eval('g:lua_syntax'))
- rmdir(syntax_folder)
+ eq('my-lang', eval('b:current_syntax'))
+ end)
+
+ it('loads lua syntaxes for :ownsyntax', function()
+ exec('ownsyntax my-lang')
+ eq('my-lang', eval('w:current_syntax'))
+ eq('', eval('b:current_syntax'))
+ end)
+
+ it('lua syntaxes are included in cmdline completion', function()
+ eq({'my-lang'}, funcs.getcompletion('my-l', 'filetype'))
+ eq({'my-lang'}, funcs.getcompletion('my-l', 'syntax'))
end)
end)
diff --git a/test/functional/lua/secure_spec.lua b/test/functional/lua/secure_spec.lua
new file mode 100644
index 0000000000..2647b2be46
--- /dev/null
+++ b/test/functional/lua/secure_spec.lua
@@ -0,0 +1,284 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+
+local eq = helpers.eq
+local clear = helpers.clear
+local command = helpers.command
+local pathsep = helpers.get_pathsep()
+local is_os = helpers.is_os
+local curbufmeths = helpers.curbufmeths
+local exec_lua = helpers.exec_lua
+local feed_command = helpers.feed_command
+local feed = helpers.feed
+local funcs = helpers.funcs
+local pcall_err = helpers.pcall_err
+local matches = helpers.matches
+
+describe('vim.secure', function()
+ describe('read()', function()
+ local xstate = 'Xstate'
+
+ setup(function()
+ helpers.mkdir_p(xstate .. pathsep .. (is_os('win') and 'nvim-data' or 'nvim'))
+ end)
+
+ teardown(function()
+ helpers.rmdir(xstate)
+ end)
+
+ before_each(function()
+ helpers.write_file('Xfile', [[
+ let g:foobar = 42
+ ]])
+ clear{env={XDG_STATE_HOME=xstate}}
+ end)
+
+ after_each(function()
+ os.remove('Xfile')
+ helpers.rmdir(xstate)
+ end)
+
+ it('works', function()
+ local screen = Screen.new(80, 8)
+ screen:attach()
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ [2] = {bold = true, reverse = true},
+ [3] = {bold = true, foreground = Screen.colors.SeaGreen},
+ [4] = {reverse = true},
+ })
+
+ local cwd = funcs.getcwd()
+
+ -- Need to use feed_command instead of exec_lua because of the confirmation prompt
+ feed_command([[lua vim.secure.read('Xfile')]])
+ screen:expect{grid=[[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2: }|
+ :lua vim.secure.read('Xfile') |
+ {3:]] .. cwd .. pathsep .. [[Xfile is untrusted}{MATCH:%s+}|
+ {3:[i]gnore, (v)iew, (d)eny, (a)llow: }^ |
+ ]]}
+ feed('d')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ local trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust')
+ eq(string.format('! %s', cwd .. pathsep .. 'Xfile'), vim.trim(trust))
+ eq(helpers.NIL, exec_lua([[return vim.secure.read('Xfile')]]))
+
+ os.remove(funcs.stdpath('state') .. pathsep .. 'trust')
+
+ feed_command([[lua vim.secure.read('Xfile')]])
+ screen:expect{grid=[[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2: }|
+ :lua vim.secure.read('Xfile') |
+ {3:]] .. cwd .. pathsep .. [[Xfile is untrusted}{MATCH:%s+}|
+ {3:[i]gnore, (v)iew, (d)eny, (a)llow: }^ |
+ ]]}
+ feed('a')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ local hash = funcs.sha256(helpers.read_file('Xfile'))
+ trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust')
+ eq(string.format('%s %s', hash, cwd .. pathsep .. 'Xfile'), vim.trim(trust))
+ eq(helpers.NIL, exec_lua([[vim.secure.read('Xfile')]]))
+
+ os.remove(funcs.stdpath('state') .. pathsep .. 'trust')
+
+ feed_command([[lua vim.secure.read('Xfile')]])
+ screen:expect{grid=[[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2: }|
+ :lua vim.secure.read('Xfile') |
+ {3:]] .. cwd .. pathsep .. [[Xfile is untrusted}{MATCH:%s+}|
+ {3:[i]gnore, (v)iew, (d)eny, (a)llow: }^ |
+ ]]}
+ feed('i')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ -- Trust database is not updated
+ trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust')
+ eq(nil, trust)
+
+ feed_command([[lua vim.secure.read('Xfile')]])
+ screen:expect{grid=[[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2: }|
+ :lua vim.secure.read('Xfile') |
+ {3:]] .. cwd .. pathsep .. [[Xfile is untrusted}{MATCH:%s+}|
+ {3:[i]gnore, (v)iew, (d)eny, (a)llow: }^ |
+ ]]}
+ feed('v')
+ screen:expect{grid=[[
+ ^let g:foobar = 42 |
+ {1:~ }|
+ {1:~ }|
+ {2:]] .. funcs.fnamemodify(cwd, ':~') .. pathsep .. [[Xfile [RO]{MATCH:%s+}|
+ |
+ {1:~ }|
+ {4:[No Name] }|
+ |
+ ]]}
+
+ -- Trust database is not updated
+ trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust')
+ eq(nil, trust)
+
+ -- Cannot write file
+ pcall_err(command, 'write')
+ eq(true, curbufmeths.get_option('readonly'))
+ end)
+ end)
+
+ describe('trust()', function()
+ local xstate = 'Xstate'
+
+ setup(function()
+ helpers.mkdir_p(xstate .. pathsep .. (is_os('win') and 'nvim-data' or 'nvim'))
+ end)
+
+ teardown(function()
+ helpers.rmdir(xstate)
+ end)
+
+ before_each(function()
+ helpers.write_file('test_file', 'test')
+ end)
+
+ after_each(function()
+ os.remove('test_file')
+ end)
+
+ it('returns error when passing both path and bufnr', function()
+ matches('"path" and "bufnr" are mutually exclusive',
+ pcall_err(exec_lua, [[vim.secure.trust({action='deny', bufnr=0, path='test_file'})]]))
+ end)
+
+ it('returns error when passing neither path or bufnr', function()
+ matches('one of "path" or "bufnr" is required',
+ pcall_err(exec_lua, [[vim.secure.trust({action='deny'})]]))
+ end)
+
+ it('trust then deny then remove a file using bufnr', function()
+ local cwd = funcs.getcwd()
+ local hash = funcs.sha256(helpers.read_file('test_file'))
+ local full_path = cwd .. pathsep .. 'test_file'
+
+ command('edit test_file')
+ eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='allow', bufnr=0})}]]))
+ local trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust')
+ eq(string.format('%s %s', hash, full_path), vim.trim(trust))
+
+ eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='deny', bufnr=0})}]]))
+ trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust')
+ eq(string.format('! %s', full_path), vim.trim(trust))
+
+ eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='remove', bufnr=0})}]]))
+ trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust')
+ eq('', vim.trim(trust))
+ end)
+
+ it('deny then trust then remove a file using bufnr', function()
+ local cwd = funcs.getcwd()
+ local hash = funcs.sha256(helpers.read_file('test_file'))
+ local full_path = cwd .. pathsep .. 'test_file'
+
+ command('edit test_file')
+ eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='deny', bufnr=0})}]]))
+ local trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust')
+ eq(string.format('! %s', full_path), vim.trim(trust))
+
+ eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='allow', bufnr=0})}]]))
+ trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust')
+ eq(string.format('%s %s', hash, full_path), vim.trim(trust))
+
+ eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='remove', bufnr=0})}]]))
+ trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust')
+ eq('', vim.trim(trust))
+ end)
+
+ it('trust using bufnr then deny then remove a file using path', function()
+ local cwd = funcs.getcwd()
+ local hash = funcs.sha256(helpers.read_file('test_file'))
+ local full_path = cwd .. pathsep .. 'test_file'
+
+ command('edit test_file')
+ eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='allow', bufnr=0})}]]))
+ local trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust')
+ eq(string.format('%s %s', hash, full_path), vim.trim(trust))
+
+ eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='deny', path='test_file'})}]]))
+ trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust')
+ eq(string.format('! %s', full_path), vim.trim(trust))
+
+ eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='remove', path='test_file'})}]]))
+ trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust')
+ eq('', vim.trim(trust))
+ end)
+
+ it('deny then trust then remove a file using bufnr', function()
+ local cwd = funcs.getcwd()
+ local hash = funcs.sha256(helpers.read_file('test_file'))
+ local full_path = cwd .. pathsep .. 'test_file'
+
+ command('edit test_file')
+ eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='deny', path='test_file'})}]]))
+ local trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust')
+ eq(string.format('! %s', full_path), vim.trim(trust))
+
+ eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='allow', bufnr=0})}]]))
+ trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust')
+ eq(string.format('%s %s', hash, full_path), vim.trim(trust))
+
+ eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='remove', path='test_file'})}]]))
+ trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust')
+ eq('', vim.trim(trust))
+ end)
+
+ it('trust returns error when buffer not associated to file', function()
+ command('new')
+ eq({false, 'buffer is not associated with a file'},
+ exec_lua([[return {vim.secure.trust({action='allow', bufnr=0})}]]))
+ end)
+ end)
+end)
diff --git a/test/functional/lua/spell_spec.lua b/test/functional/lua/spell_spec.lua
index 7e831f16a7..b3de6a0912 100644
--- a/test/functional/lua/spell_spec.lua
+++ b/test/functional/lua/spell_spec.lua
@@ -15,7 +15,7 @@ describe('vim.spell', function()
end
it('can handle nil', function()
- eq([[Error executing lua: [string "<nvim>"]:0: bad argument #1 to 'check' (expected string)]],
+ eq([[bad argument #1 to 'check' (expected string)]],
pcall_err(exec_lua, [[vim.spell.check(nil)]]))
end)
diff --git a/test/functional/lua/thread_spec.lua b/test/functional/lua/thread_spec.lua
index e183ce3a57..c7f2783cf3 100644
--- a/test/functional/lua/thread_spec.lua
+++ b/test/functional/lua/thread_spec.lua
@@ -272,7 +272,7 @@ describe('threadpool', function()
work:queue({})
]])
- eq([[Error executing lua: [string "<nvim>"]:0: Error: thread arg not support type 'function' at 1]],
+ eq([[Error: thread arg not support type 'function' at 1]],
status)
end)
diff --git a/test/functional/lua/ui_event_spec.lua b/test/functional/lua/ui_event_spec.lua
new file mode 100644
index 0000000000..6481da900e
--- /dev/null
+++ b/test/functional/lua/ui_event_spec.lua
@@ -0,0 +1,147 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local eq = helpers.eq
+local exec_lua = helpers.exec_lua
+local clear = helpers.clear
+local feed = helpers.feed
+local funcs = helpers.funcs
+local inspect = require'vim.inspect'
+
+describe('vim.ui_attach', function()
+ local screen
+ before_each(function()
+ clear()
+ exec_lua [[
+ ns = vim.api.nvim_create_namespace 'testspace'
+ events = {}
+ function on_event(event, ...)
+ events[#events+1] = {event, ...}
+ return true
+ end
+
+ function get_events()
+ local ret_events = events
+ events = {}
+ return ret_events
+ end
+ ]]
+
+ screen = Screen.new(40,5)
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue1};
+ [2] = {bold = true};
+ [3] = {background = Screen.colors.Grey};
+ [4] = {background = Screen.colors.LightMagenta};
+ })
+ screen:attach()
+ end)
+
+ local function expect_events(expected)
+ local evs = exec_lua "return get_events(...)"
+ eq(expected, evs, inspect(evs))
+ end
+
+ it('can receive popupmenu events', function()
+ exec_lua [[ vim.ui_attach(ns, {ext_popupmenu=true}, on_event) ]]
+ feed('ifo')
+ screen:expect{grid=[[
+ fo^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]]}
+
+ funcs.complete(1, {'food', 'foobar', 'foo'})
+ screen:expect{grid=[[
+ food^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]]}
+ expect_events {
+ { "popupmenu_show", { { "food", "", "", "" }, { "foobar", "", "", "" }, { "foo", "", "", "" } }, 0, 0, 0, 1 };
+ }
+
+ feed '<c-n>'
+ screen:expect{grid=[[
+ foobar^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]]}
+ expect_events {
+ { "popupmenu_select", 1 };
+ }
+
+ feed '<c-y>'
+ screen:expect{grid=[[
+ foobar^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]], intermediate=true}
+ expect_events {
+ { "popupmenu_hide" };
+ }
+
+ -- vim.ui_detach() stops events, and reenables builtin pum immediately
+ exec_lua [[
+ vim.ui_detach(ns)
+ vim.fn.complete(1, {'food', 'foobar', 'foo'})
+ ]]
+
+ screen:expect{grid=[[
+ food^ |
+ {3:food }{1: }|
+ {4:foobar }{1: }|
+ {4:foo }{1: }|
+ {2:-- INSERT --} |
+ ]]}
+ expect_events {
+ }
+
+ end)
+
+ it('does not crash on exit', function()
+ helpers.funcs.system({
+ helpers.nvim_prog,
+ '-u', 'NONE',
+ '-i', 'NONE',
+ '--cmd', [[ lua ns = vim.api.nvim_create_namespace 'testspace' ]],
+ '--cmd', [[ lua vim.ui_attach(ns, {ext_popupmenu=true}, function() end) ]],
+ '--cmd', 'quitall!',
+ })
+ eq(0, helpers.eval('v:shell_error'))
+ end)
+
+ it('can receive accurate message kinds even if they are history', function()
+ exec_lua([[
+ vim.cmd.echomsg("'message1'")
+ print('message2')
+ vim.ui_attach(ns, { ext_messages = true }, on_event)
+ vim.cmd.echomsg("'message3'")
+ ]])
+ feed(':messages<cr>')
+ feed('<cr>')
+
+ local actual = exec_lua([[
+ return vim.tbl_filter(function (event)
+ return event[1] == "msg_history_show"
+ end, events)
+ ]])
+ eq({
+ {
+ 'msg_history_show',
+ {
+ { 'echomsg', { { 0, 'message1' } } },
+ { '', { { 0, 'message2' } } },
+ { 'echomsg', { { 0, 'message3' } } },
+ },
+ },
+ }, actual, inspect(actual))
+ end)
+end)
diff --git a/test/functional/lua/ui_spec.lua b/test/functional/lua/ui_spec.lua
index 3fcb2dec8d..9ee99b4905 100644
--- a/test/functional/lua/ui_spec.lua
+++ b/test/functional/lua/ui_spec.lua
@@ -4,6 +4,7 @@ local exec_lua = helpers.exec_lua
local clear = helpers.clear
local feed = helpers.feed
local eval = helpers.eval
+local poke_eventloop = helpers.poke_eventloop
describe('vim.ui', function()
before_each(function()
@@ -83,5 +84,50 @@ describe('vim.ui', function()
feed('abcdefg<cr>')
eq('abcdefg', exec_lua('return result'))
end)
+
+ it('can input empty text #18144', function()
+ feed(':lua vim.ui.input({}, function(input) result = input end)<cr>')
+ feed('<cr>')
+ eq('', exec_lua('return result'))
+ end)
+
+ it('can input empty text with cancelreturn opt #18144', function()
+ feed(':lua vim.ui.input({ cancelreturn = "CANCEL" }, function(input) result = input end)<cr>')
+ feed('<cr>')
+ eq('', exec_lua('return result'))
+ end)
+
+ it('can return nil when aborted with ESC #18144', function()
+ feed(':lua result = "on_confirm not called"<cr>')
+ feed(':lua vim.ui.input({}, function(input) result = input end)<cr>')
+ feed('Inputted Text<esc>')
+ -- Note: When `result == nil`, exec_lua('returns result') returns vim.NIL
+ eq(true, exec_lua('return (nil == result)'))
+ end)
+
+ it('can return opts.cacelreturn when aborted with ESC with cancelreturn opt #18144', function()
+ feed(':lua result = "on_confirm not called"<cr>')
+ feed(':lua vim.ui.input({ cancelreturn = "CANCEL" }, function(input) result = input end)<cr>')
+ feed('Inputted Text<esc>')
+ eq('CANCEL', exec_lua('return result'))
+ end)
+
+ it('can return nil when interrupted with Ctrl-C #18144', function()
+ feed(':lua result = "on_confirm not called"<cr>')
+ feed(':lua vim.ui.input({}, function(input) result = input end)<cr>')
+ poke_eventloop() -- This is needed because Ctrl-C flushes input
+ feed('Inputted Text<c-c>')
+ eq(true, exec_lua('return (nil == result)'))
+ end)
+
+ it('can return the identical object when an arbitrary opts.cancelreturn object is given', function()
+ feed(':lua fn = function() return 42 end<CR>')
+ eq(42, exec_lua('return fn()'))
+ feed(':lua vim.ui.input({ cancelreturn = fn }, function(input) result = input end)<cr>')
+ feed('cancel<esc>')
+ eq(true, exec_lua('return (result == fn)'))
+ eq(42, exec_lua('return result()'))
+ end)
+
end)
end)
diff --git a/test/functional/lua/uri_spec.lua b/test/functional/lua/uri_spec.lua
index 4635f17557..416e9e1f02 100644
--- a/test/functional/lua/uri_spec.lua
+++ b/test/functional/lua/uri_spec.lua
@@ -2,6 +2,8 @@ local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
local exec_lua = helpers.exec_lua
local eq = helpers.eq
+local is_os = helpers.is_os
+local skip = helpers.skip
local write_file = require('test.helpers').write_file
describe('URI methods', function()
@@ -11,7 +13,7 @@ describe('URI methods', function()
describe('file path to uri', function()
describe('encode Unix file path', function()
- it('file path includes only ascii charactors', function()
+ it('file path includes only ascii characters', function()
exec_lua("filepath = '/Foo/Bar/Baz.txt'")
eq('file:///Foo/Bar/Baz.txt', exec_lua("return vim.uri_from_fname(filepath)"))
@@ -23,7 +25,7 @@ describe('URI methods', function()
eq('file:///Foo%20/Bar/Baz.txt', exec_lua("return vim.uri_from_fname(filepath)"))
end)
- it('file path including Unicode charactors', function()
+ it('file path including Unicode characters', function()
exec_lua("filepath = '/xy/åäö/ɧ/汉语/↥/🤦/🦄/å/بِيَّ.txt'")
-- The URI encoding should be case-insensitive
@@ -32,7 +34,7 @@ describe('URI methods', function()
end)
describe('encode Windows filepath', function()
- it('file path includes only ascii charactors', function()
+ it('file path includes only ascii characters', function()
exec_lua([[filepath = 'C:\\Foo\\Bar\\Baz.txt']])
eq('file:///C:/Foo/Bar/Baz.txt', exec_lua("return vim.uri_from_fname(filepath)"))
@@ -44,7 +46,7 @@ describe('URI methods', function()
eq('file:///C:/Foo%20/Bar/Baz.txt', exec_lua("return vim.uri_from_fname(filepath)"))
end)
- it('file path including Unicode charactors', function()
+ it('file path including Unicode characters', function()
exec_lua([[filepath = 'C:\\xy\\åäö\\ɧ\\汉语\\↥\\🤦\\🦄\\å\\بِيَّ.txt']])
eq('file:///C:/xy/%c3%a5%c3%a4%c3%b6/%c9%a7/%e6%b1%89%e8%af%ad/%e2%86%a5/%f0%9f%a4%a6/%f0%9f%a6%84/a%cc%8a/%d8%a8%d9%90%d9%8a%d9%8e%d9%91.txt', exec_lua("return vim.uri_from_fname(filepath)"))
@@ -72,7 +74,7 @@ describe('URI methods', function()
eq('/Foo /Bar/Baz.txt', exec_lua("return vim.uri_to_fname(uri)"))
end)
- it('file path including Unicode charactors', function()
+ it('file path including Unicode characters', function()
local test_case = [[
local uri = 'file:///xy/%C3%A5%C3%A4%C3%B6/%C9%A7/%E6%B1%89%E8%AF%AD/%E2%86%A5/%F0%9F%A4%A6/%F0%9F%A6%84/a%CC%8A/%D8%A8%D9%90%D9%8A%D9%8E%D9%91.txt'
return vim.uri_to_fname(uri)
@@ -83,7 +85,7 @@ describe('URI methods', function()
end)
describe('decode Windows filepath', function()
- it('file path includes only ascii charactors', function()
+ it('file path includes only ascii characters', function()
local test_case = [[
local uri = 'file:///C:/Foo/Bar/Baz.txt'
return vim.uri_to_fname(uri)
@@ -119,7 +121,7 @@ describe('URI methods', function()
eq('C:\\Foo \\Bar\\Baz.txt', exec_lua(test_case))
end)
- it('file path including Unicode charactors', function()
+ it('file path including Unicode characters', function()
local test_case = [[
local uri = 'file:///C:/xy/%C3%A5%C3%A4%C3%B6/%C9%A7/%E6%B1%89%E8%AF%AD/%E2%86%A5/%F0%9F%A4%A6/%F0%9F%A6%84/a%CC%8A/%D8%A8%D9%90%D9%8A%D9%8E%D9%91.txt'
return vim.uri_to_fname(uri)
@@ -167,7 +169,7 @@ describe('URI methods', function()
describe('uri from bufnr', function()
it('Windows paths should not be treated as uris', function()
- if not helpers.iswin() then return end
+ skip(not is_os('win'), "Not applicable on non-Windows")
local file = helpers.tmpname()
write_file(file, 'Test content')
diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua
index 2b249b7a69..867f366d06 100644
--- a/test/functional/lua/vim_spec.lua
+++ b/test/functional/lua/vim_spec.lua
@@ -2,6 +2,7 @@
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
+local nvim_prog = helpers.nvim_prog
local funcs = helpers.funcs
local meths = helpers.meths
local command = helpers.command
@@ -22,8 +23,8 @@ local remove_trace = helpers.remove_trace
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
+local assert_alive = helpers.assert_alive
describe('lua stdlib', function()
before_each(clear)
@@ -158,28 +159,32 @@ describe('lua stdlib', function()
end)
it("vim.str_utfindex/str_byteindex", function()
- exec_lua([[_G.test_text = "xy åäö ɧ 汉语 ↥ 🤦x🦄 å بِيَّ"]])
- local indicies32 = {[0]=0,1,2,3,5,7,9,10,12,13,16,19,20,23,24,28,29,33,34,35,37,38,40,42,44,46,48}
- local indicies16 = {[0]=0,1,2,3,5,7,9,10,12,13,16,19,20,23,24,28,28,29,33,33,34,35,37,38,40,42,44,46,48}
- for i,k in pairs(indicies32) do
+ exec_lua([[_G.test_text = "xy åäö ɧ 汉语 ↥ 🤦x🦄 å بِيَّ\000ъ"]])
+ local indices32 = {[0]=0,1,2,3,5,7,9,10,12,13,16,19,20,23,24,28,29,33,34,35,37,38,40,42,44,46,48,49,51}
+ local indices16 = {[0]=0,1,2,3,5,7,9,10,12,13,16,19,20,23,24,28,28,29,33,33,34,35,37,38,40,42,44,46,48,49,51}
+ for i,k in pairs(indices32) do
eq(k, exec_lua("return vim.str_byteindex(_G.test_text, ...)", i), i)
end
- for i,k in pairs(indicies16) do
+ for i,k in pairs(indices16) do
eq(k, exec_lua("return vim.str_byteindex(_G.test_text, ..., true)", i), i)
end
+ eq("index out of range", pcall_err(exec_lua, "return vim.str_byteindex(_G.test_text, ...)", #indices32 + 1))
+ eq("index out of range", pcall_err(exec_lua, "return vim.str_byteindex(_G.test_text, ..., true)", #indices16 + 1))
local i32, i16 = 0, 0
- for k = 0,48 do
- if indicies32[i32] < k then
+ local len = 51
+ for k = 0,len do
+ if indices32[i32] < k then
i32 = i32 + 1
end
- if indicies16[i16] < k then
+ if indices16[i16] < k then
i16 = i16 + 1
- if indicies16[i16+1] == indicies16[i16] then
+ if indices16[i16+1] == indices16[i16] then
i16 = i16 + 1
end
end
eq({i32, i16}, exec_lua("return {vim.str_utfindex(_G.test_text, ...)}", k), k)
end
+ eq("index out of range", pcall_err(exec_lua, "return vim.str_utfindex(_G.test_text, ...)", len + 1))
end)
it("vim.str_utf_start", function()
@@ -414,6 +419,12 @@ describe('lua stdlib', function()
return getmetatable(t2) == mt
]]))
+ ok(exec_lua([[
+ local t1 = {a = vim.NIL}
+ local t2 = vim.deepcopy(t1)
+ return t2.a == vim.NIL
+ ]]))
+
matches('Cannot deepcopy object of type thread',
pcall_err(exec_lua, [[
local thread = coroutine.create(function () return 0 end)
@@ -425,6 +436,8 @@ describe('lua stdlib', function()
it('vim.pesc', function()
eq('foo%-bar', exec_lua([[return vim.pesc('foo-bar')]]))
eq('foo%%%-bar', exec_lua([[return vim.pesc(vim.pesc('foo-bar'))]]))
+ -- pesc() returns one result. #20751
+ eq({'x'}, exec_lua([[return {vim.pesc('x')}]]))
-- Validates args.
matches('s: expected string, got number',
@@ -499,6 +512,8 @@ describe('lua stdlib', function()
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({})"))
+ eq(1, exec_lua("return select('#', vim.tbl_get({}))"))
+ eq(1, exec_lua("return select('#', vim.tbl_get({ nested = {} }, 'nested', 'missing_key'))"))
end)
it('vim.tbl_extend', function()
@@ -748,6 +763,20 @@ describe('lua stdlib', function()
pcall_err(exec_lua, code))
end)
+ it('vim.spairs', function()
+ local res = ''
+ local table = {
+ ccc=1,
+ bbb=2,
+ ddd=3,
+ aaa=4
+ }
+ for key, _ in vim.spairs(table) do
+ res = res .. key
+ end
+ matches('aaabbbcccddd', res)
+ end)
+
it('vim.call, vim.fn', function()
eq(true, exec_lua([[return vim.call('sin', 0.0) == 0.0 ]]))
eq(true, exec_lua([[return vim.fn.sin(0.0) == 0.0 ]]))
@@ -788,6 +817,11 @@ describe('lua stdlib', function()
local x = vim.fn.VarArg(function() return 'foo' end, function() return 'bar' end)
return #x == 2 and x[1]() == 'foo' and x[2]() == 'bar'
]]))
+
+ -- Test for #20211
+ eq('a (b) c', exec_lua([[
+ return vim.fn.substitute('a b c', 'b', function(m) return '(' .. m[1] .. ')' end, 'g')
+ ]]))
end)
it('vim.fn should error when calling API function', function()
@@ -896,7 +930,7 @@ describe('lua stdlib', function()
]]))
-- vim.empty_dict() gives new value each time
- -- equality is not overriden (still by ref)
+ -- equality is not overridden (still by ref)
-- non-empty table uses the usual heuristics (ignores the tag)
eq({false, {"foo"}, {namey="bar"}}, exec_lua([[
local aa = vim.empty_dict()
@@ -1006,11 +1040,11 @@ describe('lua stdlib', function()
eq('hi', funcs.luaeval "vim.g.testing")
eq(123, funcs.luaeval "vim.g.other")
eq(5120.1, funcs.luaeval "vim.g.floaty")
- eq(NIL, funcs.luaeval "vim.g.nonexistant")
+ eq(NIL, funcs.luaeval "vim.g.nonexistent")
eq(NIL, funcs.luaeval "vim.g.nullvar")
-- lost over RPC, so test locally:
eq({false, true}, exec_lua [[
- return {vim.g.nonexistant == vim.NIL, vim.g.nullvar == vim.NIL}
+ return {vim.g.nonexistent == vim.NIL, vim.g.nullvar == vim.NIL}
]])
eq({hello="world"}, funcs.luaeval "vim.g.to_delete")
@@ -1029,6 +1063,7 @@ describe('lua stdlib', function()
vim.g.AddCounter = add_counter
vim.g.GetCounter = get_counter
vim.g.funcs = {add = add_counter, get = get_counter}
+ vim.g.AddParens = function(s) return '(' .. s .. ')' end
]]
eq(0, eval('g:GetCounter()'))
@@ -1044,6 +1079,7 @@ describe('lua stdlib', function()
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()]]))
+ eq('((foo))', eval([['foo'->AddParens()->AddParens()]]))
exec_lua [[
local counter = 0
@@ -1052,6 +1088,7 @@ describe('lua stdlib', function()
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})
+ vim.api.nvim_set_var('AddParens', function(s) return '(' .. s .. ')' end)
]]
eq(0, eval('g:GetCounter()'))
@@ -1067,6 +1104,7 @@ describe('lua stdlib', function()
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()]]))
+ eq('((foo))', eval([['foo'->AddParens()->AddParens()]]))
exec([[
function Test()
@@ -1109,12 +1147,12 @@ describe('lua stdlib', function()
eq('bye', funcs.luaeval "vim.b[BUF].testing")
eq(123, funcs.luaeval "vim.b.other")
eq(5120.1, funcs.luaeval "vim.b.floaty")
- eq(NIL, funcs.luaeval "vim.b.nonexistant")
- eq(NIL, funcs.luaeval "vim.b[BUF].nonexistant")
+ eq(NIL, funcs.luaeval "vim.b.nonexistent")
+ eq(NIL, funcs.luaeval "vim.b[BUF].nonexistent")
eq(NIL, funcs.luaeval "vim.b.nullvar")
-- lost over RPC, so test locally:
eq({false, true}, exec_lua [[
- return {vim.b.nonexistant == vim.NIL, vim.b.nullvar == vim.NIL}
+ return {vim.b.nonexistent == vim.NIL, vim.b.nullvar == vim.NIL}
]])
matches([[attempt to index .* nil value]],
@@ -1133,6 +1171,7 @@ describe('lua stdlib', function()
vim.b.AddCounter = add_counter
vim.b.GetCounter = get_counter
vim.b.funcs = {add = add_counter, get = get_counter}
+ vim.b.AddParens = function(s) return '(' .. s .. ')' end
]]
eq(0, eval('b:GetCounter()'))
@@ -1148,6 +1187,7 @@ describe('lua stdlib', function()
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()]]))
+ eq('((foo))', eval([['foo'->b:AddParens()->b:AddParens()]]))
exec_lua [[
local counter = 0
@@ -1156,6 +1196,7 @@ describe('lua stdlib', function()
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})
+ vim.api.nvim_buf_set_var(0, 'AddParens', function(s) return '(' .. s .. ')' end)
]]
eq(0, eval('b:GetCounter()'))
@@ -1171,6 +1212,7 @@ describe('lua stdlib', function()
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()]]))
+ eq('((foo))', eval([['foo'->b:AddParens()->b:AddParens()]]))
exec([[
function Test()
@@ -1189,7 +1231,7 @@ describe('lua stdlib', function()
eq(NIL, funcs.luaeval "vim.b.testing")
eq(NIL, funcs.luaeval "vim.b.other")
- eq(NIL, funcs.luaeval "vim.b.nonexistant")
+ eq(NIL, funcs.luaeval "vim.b.nonexistent")
end)
it('vim.w', function()
@@ -1208,8 +1250,8 @@ describe('lua stdlib', function()
eq('hi', funcs.luaeval "vim.w.testing")
eq('bye', funcs.luaeval "vim.w[WIN].testing")
eq(123, funcs.luaeval "vim.w.other")
- eq(NIL, funcs.luaeval "vim.w.nonexistant")
- eq(NIL, funcs.luaeval "vim.w[WIN].nonexistant")
+ eq(NIL, funcs.luaeval "vim.w.nonexistent")
+ eq(NIL, funcs.luaeval "vim.w[WIN].nonexistent")
matches([[attempt to index .* nil value]],
pcall_err(exec_lua, 'return vim.w[WIN][0].testing'))
@@ -1227,6 +1269,7 @@ describe('lua stdlib', function()
vim.w.AddCounter = add_counter
vim.w.GetCounter = get_counter
vim.w.funcs = {add = add_counter, get = get_counter}
+ vim.w.AddParens = function(s) return '(' .. s .. ')' end
]]
eq(0, eval('w:GetCounter()'))
@@ -1242,6 +1285,7 @@ describe('lua stdlib', function()
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()]]))
+ eq('((foo))', eval([['foo'->w:AddParens()->w:AddParens()]]))
exec_lua [[
local counter = 0
@@ -1250,6 +1294,7 @@ describe('lua stdlib', function()
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})
+ vim.api.nvim_win_set_var(0, 'AddParens', function(s) return '(' .. s .. ')' end)
]]
eq(0, eval('w:GetCounter()'))
@@ -1265,6 +1310,7 @@ describe('lua stdlib', function()
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()]]))
+ eq('((foo))', eval([['foo'->w:AddParens()->w:AddParens()]]))
exec([[
function Test()
@@ -1283,7 +1329,7 @@ describe('lua stdlib', function()
eq(NIL, funcs.luaeval "vim.w.testing")
eq(NIL, funcs.luaeval "vim.w.other")
- eq(NIL, funcs.luaeval "vim.w.nonexistant")
+ eq(NIL, funcs.luaeval "vim.w.nonexistent")
end)
it('vim.t', function()
@@ -1295,10 +1341,10 @@ describe('lua stdlib', function()
eq('hi', funcs.luaeval "vim.t.testing")
eq(123, funcs.luaeval "vim.t.other")
- eq(NIL, funcs.luaeval "vim.t.nonexistant")
+ eq(NIL, funcs.luaeval "vim.t.nonexistent")
eq('hi', funcs.luaeval "vim.t[0].testing")
eq(123, funcs.luaeval "vim.t[0].other")
- eq(NIL, funcs.luaeval "vim.t[0].nonexistant")
+ eq(NIL, funcs.luaeval "vim.t[0].nonexistent")
matches([[attempt to index .* nil value]],
pcall_err(exec_lua, 'return vim.t[0][0].testing'))
@@ -1316,6 +1362,7 @@ describe('lua stdlib', function()
vim.t.AddCounter = add_counter
vim.t.GetCounter = get_counter
vim.t.funcs = {add = add_counter, get = get_counter}
+ vim.t.AddParens = function(s) return '(' .. s .. ')' end
]]
eq(0, eval('t:GetCounter()'))
@@ -1331,6 +1378,7 @@ describe('lua stdlib', function()
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()]]))
+ eq('((foo))', eval([['foo'->t:AddParens()->t:AddParens()]]))
exec_lua [[
local counter = 0
@@ -1339,6 +1387,7 @@ describe('lua stdlib', function()
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})
+ vim.api.nvim_tabpage_set_var(0, 'AddParens', function(s) return '(' .. s .. ')' end)
]]
eq(0, eval('t:GetCounter()'))
@@ -1354,6 +1403,7 @@ describe('lua stdlib', function()
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()]]))
+ eq('((foo))', eval([['foo'->t:AddParens()->t:AddParens()]]))
exec_lua [[
vim.cmd "tabnew"
@@ -1361,15 +1411,27 @@ describe('lua stdlib', function()
eq(NIL, funcs.luaeval "vim.t.testing")
eq(NIL, funcs.luaeval "vim.t.other")
- eq(NIL, funcs.luaeval "vim.t.nonexistant")
+ eq(NIL, funcs.luaeval "vim.t.nonexistent")
end)
it('vim.env', function()
- exec_lua [[
- vim.fn.setenv("A", 123)
- ]]
- eq('123', funcs.luaeval "vim.env.A")
- eq(true, funcs.luaeval "vim.env.B == nil")
+ exec_lua([[vim.fn.setenv('A', 123)]])
+ eq('123', funcs.luaeval('vim.env.A'))
+ exec_lua([[vim.env.A = 456]])
+ eq('456', funcs.luaeval('vim.env.A'))
+ exec_lua([[vim.env.A = nil]])
+ eq(NIL, funcs.luaeval('vim.env.A'))
+
+ eq(true, funcs.luaeval('vim.env.B == nil'))
+
+ command([[let $HOME = 'foo']])
+ eq('foo', funcs.expand('~'))
+ eq('foo', funcs.luaeval('vim.env.HOME'))
+ exec_lua([[vim.env.HOME = nil]])
+ eq('foo', funcs.expand('~'))
+ exec_lua([[vim.env.HOME = 'bar']])
+ eq('bar', funcs.expand('~'))
+ eq('bar', funcs.luaeval('vim.env.HOME'))
end)
it('vim.v', function()
@@ -1396,7 +1458,7 @@ describe('lua stdlib', function()
]]
eq('', funcs.luaeval "vim.bo.filetype")
eq(true, funcs.luaeval "vim.bo[BUF].modifiable")
- matches("unknown option 'nosuchopt'$",
+ matches("no such option: 'nosuchopt'$",
pcall_err(exec_lua, 'return vim.bo.nosuchopt'))
matches("Expected lua string$",
pcall_err(exec_lua, 'return vim.bo[0][0].autoread'))
@@ -1417,7 +1479,7 @@ 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("unknown option 'notanopt'$",
+ matches("no such option: 'notanopt'$",
pcall_err(exec_lua, 'return vim.wo.notanopt'))
matches("Expected lua string$",
pcall_err(exec_lua, 'return vim.wo[0][0].list'))
@@ -2141,6 +2203,22 @@ describe('lua stdlib', function()
end)
end) -- vim.opt
+ describe('opt_local', function()
+ it('should be able to append to an array list type option', function()
+ eq({ "foo,bar,baz,qux" }, exec_lua [[
+ local result = {}
+
+ vim.opt.tags = "foo,bar"
+ vim.opt_local.tags:append("baz")
+ vim.opt_local.tags:append("qux")
+
+ table.insert(result, vim.bo.tags)
+
+ return result
+ ]])
+ end)
+ end)
+
it('vim.cmd', function()
exec_lua [[
vim.cmd "autocmd BufNew * ++once lua BUF = vim.fn.expand('<abuf>')"
@@ -2173,6 +2251,10 @@ describe('lua stdlib', function()
eq({3,7}, exec_lua[[return {re1:match_line(0, 1, 1, 8)}]])
eq({}, exec_lua[[return {re1:match_line(0, 1, 1, 7)}]])
eq({0,3}, exec_lua[[return {re1:match_line(0, 1, 0, 7)}]])
+
+ -- vim.regex() error inside :silent! should not crash. #20546
+ command([[silent! lua vim.regex('\\z')]])
+ assert_alive()
end)
it('vim.defer_fn', function()
@@ -2185,13 +2267,19 @@ describe('lua stdlib', function()
eq(true, exec_lua[[return vim.g.test]])
end)
- it('vim.region', function()
- insert(helpers.dedent( [[
- text tααt tααt text
- text tαxt txtα tex
- text tαxt tαxt
- ]]))
- eq({5,15}, exec_lua[[ return vim.region(0,{1,5},{1,14},'v',true)[1] ]])
+ describe('vim.region', function()
+ it('charwise', function()
+ insert(helpers.dedent( [[
+ text tααt tααt text
+ text tαxt txtα tex
+ text tαxt tαxt
+ ]]))
+ eq({5,15}, exec_lua[[ return vim.region(0,{1,5},{1,14},'v',true)[1] ]])
+ end)
+ it('blockwise', function()
+ insert([[αα]])
+ eq({0,5}, exec_lua[[ return vim.region(0,{0,0},{0,4},'3',true)[0] ]])
+ end)
end)
describe('vim.on_key', function()
@@ -2623,6 +2711,46 @@ describe('lua stdlib', function()
a.nvim_buf_call(a.nvim_create_buf(false, true), function() vim.cmd "redraw" end)
]]
end)
+
+ it('can be nested crazily with hidden buffers', function()
+ eq(true, exec_lua([[
+ local function scratch_buf_call(fn)
+ local buf = vim.api.nvim_create_buf(false, true)
+ vim.api.nvim_buf_set_option(buf, 'cindent', true)
+ return vim.api.nvim_buf_call(buf, function()
+ return vim.api.nvim_get_current_buf() == buf
+ and vim.api.nvim_buf_get_option(buf, 'cindent')
+ and fn()
+ end) and vim.api.nvim_buf_delete(buf, {}) == nil
+ end
+
+ return scratch_buf_call(function()
+ return scratch_buf_call(function()
+ return scratch_buf_call(function()
+ return scratch_buf_call(function()
+ return scratch_buf_call(function()
+ return scratch_buf_call(function()
+ return scratch_buf_call(function()
+ return scratch_buf_call(function()
+ return scratch_buf_call(function()
+ return scratch_buf_call(function()
+ return scratch_buf_call(function()
+ return scratch_buf_call(function()
+ return true
+ end)
+ end)
+ end)
+ end)
+ end)
+ end)
+ end)
+ end)
+ end)
+ end)
+ end)
+ end)
+ ]]))
+ end)
end)
describe('vim.api.nvim_win_call', function()
@@ -2721,6 +2849,57 @@ describe('lua stdlib', function()
]]
end)
end)
+
+ describe('vim.iconv', function()
+ it('can convert strings', function()
+ eq('hello', exec_lua[[
+ return vim.iconv('hello', 'latin1', 'utf-8')
+ ]])
+ end)
+
+ it('can validate arguments', function()
+ eq({false, 'Expected at least 3 arguments'}, exec_lua[[
+ return {pcall(vim.iconv, 'hello')}
+ ]])
+
+ eq({false, 'bad argument #3 to \'?\' (expected string)'}, exec_lua[[
+ return {pcall(vim.iconv, 'hello', 'utf-8', true)}
+ ]])
+ end)
+
+ it('can handle bad encodings', function()
+ eq(NIL, exec_lua[[
+ return vim.iconv('hello', 'foo', 'bar')
+ ]])
+ end)
+
+ it('can handle strings with NUL bytes', function()
+ eq(7, exec_lua[[
+ local a = string.char(97, 98, 99, 0, 100, 101, 102) -- abc\0def
+ return string.len(vim.iconv(a, 'latin1', 'utf-8'))
+ ]])
+ end)
+
+ end)
+
+ describe("vim.defaulttable", function()
+ it("creates nested table by default", function()
+ eq({ b = {c = 1 } }, exec_lua[[
+ local a = vim.defaulttable()
+ a.b.c = 1
+ return a
+ ]])
+ end)
+
+ it("allows to create default objects", function()
+ eq({ b = 1 }, exec_lua[[
+ local a = vim.defaulttable(function() return 0 end)
+ a.b = a.b + 1
+ return a
+ ]])
+ end)
+ end)
+
end)
describe('lua: builtin modules', function()
@@ -2745,9 +2924,14 @@ describe('lua: builtin modules', function()
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}]])
+ it('fails when disabled without runtime', function()
+ clear()
+ command("let $VIMRUNTIME='fixtures/a'")
+ -- Use system([nvim,…]) instead of clear() to avoid stderr noise. #21844
+ local out = funcs.system({nvim_prog, '--clean', '--luamod-dev',
+ [[+call nvim_exec_lua('return vim.tbl_count {x=1,y=2}')]], '+qa!'}):gsub('\r\n', '\n')
+ eq(1, eval('v:shell_error'))
+ matches("'vim%.shared' not found", out)
end)
end)
diff --git a/test/functional/lua/xdiff_spec.lua b/test/functional/lua/xdiff_spec.lua
index d55268fc78..3121ac051f 100644
--- a/test/functional/lua/xdiff_spec.lua
+++ b/test/functional/lua/xdiff_spec.lua
@@ -74,11 +74,13 @@ describe('xdiff bindings', function()
end)
it('with error callback', function()
- exec_lua([[on_hunk = function(sa, ca, sb, cb)
+ exec_lua[[
+ on_hunk = function(sa, ca, sb, cb)
error('ERROR1')
- end]])
+ end
+ ]]
- eq([[Error executing lua: [string "<nvim>"]:0: error running function on_hunk: [string "<nvim>"]:0: ERROR1]],
+ eq([[error running function on_hunk: [string "<nvim>"]:0: ERROR1]],
pcall_err(exec_lua, [[vim.diff(a1, b1, {on_hunk = on_hunk})]]))
end)
@@ -135,19 +137,19 @@ describe('xdiff bindings', function()
end)
it('can handle bad args', function()
- eq([[Error executing lua: [string "<nvim>"]:0: Expected at least 2 arguments]],
+ eq([[Expected at least 2 arguments]],
pcall_err(exec_lua, [[vim.diff('a')]]))
- eq([[Error executing lua: [string "<nvim>"]:0: bad argument #1 to 'diff' (expected string)]],
+ eq([[bad argument #1 to 'diff' (expected string)]],
pcall_err(exec_lua, [[vim.diff(1, 2)]]))
- eq([[Error executing lua: [string "<nvim>"]:0: bad argument #3 to 'diff' (expected table)]],
+ eq([[bad argument #3 to 'diff' (expected table)]],
pcall_err(exec_lua, [[vim.diff('a', 'b', true)]]))
- eq([[Error executing lua: [string "<nvim>"]:0: unexpected key: bad_key]],
+ eq([[unexpected key: bad_key]],
pcall_err(exec_lua, [[vim.diff('a', 'b', { bad_key = true })]]))
- eq([[Error executing lua: [string "<nvim>"]:0: on_hunk is not a function]],
+ eq([[on_hunk is not a function]],
pcall_err(exec_lua, [[vim.diff('a', 'b', { on_hunk = true })]]))
end)
diff --git a/test/functional/options/autochdir_spec.lua b/test/functional/options/autochdir_spec.lua
index 74959a8e76..0b6fe9533c 100644
--- a/test/functional/options/autochdir_spec.lua
+++ b/test/functional/options/autochdir_spec.lua
@@ -16,7 +16,7 @@ describe("'autochdir'", function()
-- 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, funcs.getcwd())
+ eq(helpers.is_os('win') and expected:gsub('/', '\\') or expected, funcs.getcwd())
end)
it('is not overwritten by getwinvar() call #17609',function()
diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua
index 4e2f2ab63e..84ec43f4cb 100644
--- a/test/functional/options/defaults_spec.lua
+++ b/test/functional/options/defaults_spec.lua
@@ -3,6 +3,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 meths = helpers.meths
local command = helpers.command
local clear = helpers.clear
@@ -12,13 +13,15 @@ local eq = helpers.eq
local ok = helpers.ok
local funcs = helpers.funcs
local insert = helpers.insert
-local iswin = helpers.iswin
local neq = helpers.neq
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
+local is_os = helpers.is_os
+
+local testlog = 'Xtest-defaults-log'
describe('startup defaults', function()
describe(':filetype', function()
@@ -220,7 +223,9 @@ describe('startup defaults', function()
end)
it("'packpath'", function()
- clear()
+ clear{
+ args_rm={'runtimepath'},
+ }
-- Defaults to &runtimepath.
eq(meths.get_option('runtimepath'), meths.get_option('packpath'))
@@ -238,7 +243,7 @@ describe('startup defaults', function()
describe('$NVIM_LOG_FILE', function()
local xdgdir = 'Xtest-startup-xdg-logpath'
- local xdgstatedir = iswin() and xdgdir..'/nvim-data' or xdgdir..'/nvim'
+ local xdgstatedir = is_os('win') and xdgdir..'/nvim-data' or xdgdir..'/nvim'
after_each(function()
os.remove('Xtest-logpath')
rmdir(xdgdir)
@@ -273,11 +278,15 @@ describe('XDG defaults', function()
-- Need separate describe() blocks to not run clear() twice.
-- Do not put before_each() here for the same reasons.
+ after_each(function()
+ os.remove(testlog)
+ end)
+
it("&runtimepath data-dir matches stdpath('data') #9910", function()
clear()
local rtp = eval('split(&runtimepath, ",")')
local rv = {}
- local expected = (iswin()
+ local expected = (is_os('win')
and { [[\nvim-data\site]], [[\nvim-data\site\after]], }
or { '/nvim/site', '/nvim/site/after', })
@@ -325,28 +334,35 @@ describe('XDG defaults', function()
return vimruntime, libdir
end
- 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 ''
+ local env_sep = is_os('win') and ';' or ':'
+ local data_dir = is_os('win') and 'nvim-data' or 'nvim'
+ local state_dir = is_os('win') and 'nvim-data' or 'nvim'
+ local root_path = is_os('win') and 'C:' or ''
describe('with too long XDG variables', function()
before_each(function()
- clear({env={
- XDG_CONFIG_HOME=(root_path .. ('/x'):rep(4096)),
- XDG_CONFIG_DIRS=(root_path .. ('/a'):rep(2048)
- .. 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)),
+ clear({
+ args_rm={'runtimepath'},
+ env={
+ NVIM_LOG_FILE=testlog,
+ XDG_CONFIG_HOME=(root_path .. ('/x'):rep(4096)),
+ XDG_CONFIG_DIRS=(root_path .. ('/a'):rep(2048)
+ .. 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)),
}})
end)
it('are correctly set', function()
+ if not is_os('win') then
+ assert_log('Failed to start server: no such file or directory: /X/X/X', testlog, 10)
+ end
+
local vimruntime, libdir = vimruntime_and_libdir()
eq(((root_path .. ('/x'):rep(4096) .. '/nvim'
@@ -405,17 +421,24 @@ describe('XDG defaults', function()
describe('with XDG variables that can be expanded', function()
before_each(function()
- clear({env={
- 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',
+ clear({
+ args_rm={'runtimepath'},
+ env={
+ NVIM_LOG_FILE=testlog,
+ 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)
it('are not expanded', function()
+ if not is_os('win') then
+ assert_log('Failed to start server: no such file or directory: %$XDG_RUNTIME_DIR%/', testlog, 10)
+ end
+
local vimruntime, libdir = vimruntime_and_libdir()
eq((('$XDG_DATA_HOME/nvim'
.. ',$XDG_DATA_DIRS/nvim'
@@ -478,18 +501,20 @@ describe('XDG defaults', function()
describe('with commas', function()
before_each(function()
- clear({env={
- XDG_CONFIG_HOME=', , ,',
- XDG_CONFIG_DIRS=',-,-,' .. env_sep .. '-,-,-',
- XDG_DATA_HOME=',=,=,',
- XDG_STATE_HOME=',=,=,',
- XDG_DATA_DIRS=',≡,≡,' .. env_sep .. '≡,≡,≡',
+ clear({
+ args_rm={'runtimepath'},
+ env={
+ XDG_CONFIG_HOME=', , ,',
+ XDG_CONFIG_DIRS=',-,-,' .. env_sep .. '-,-,-',
+ XDG_DATA_HOME=',=,=,',
+ XDG_STATE_HOME=',=,=,',
+ XDG_DATA_DIRS=',≡,≡,' .. env_sep .. '≡,≡,≡',
}})
end)
it('are escaped properly', function()
local vimruntime, libdir = vimruntime_and_libdir()
- local path_sep = iswin() and '\\' or '/'
+ local path_sep = is_os('win') and '\\' or '/'
eq(('\\, \\, \\,' .. path_sep .. 'nvim'
.. ',\\,-\\,-\\,' .. path_sep .. 'nvim'
.. ',-\\,-\\,-' .. path_sep .. 'nvim'
@@ -541,9 +566,9 @@ end)
describe('stdpath()', function()
-- Windows appends 'nvim-data' instead of just 'nvim' to prevent collisions
-- 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 ':'
+ local datadir = is_os('win') and 'nvim-data' or 'nvim'
+ local statedir = is_os('win') and 'nvim-data' or 'nvim'
+ local env_sep = is_os('win') and ';' or ':'
it('acceptance', function()
clear() -- Do not explicitly set any env vars.
@@ -696,7 +721,7 @@ describe('stdpath()', function()
context('returns a List', function()
-- Some OS specific variables the system would have set.
local function base_env()
- if iswin() then
+ if is_os('win') then
return {
HOME='C:\\Users\\docwhat', -- technically, is not a usual PATH
HOMEDRIVE='C:',
diff --git a/test/functional/options/keymap_spec.lua b/test/functional/options/keymap_spec.lua
index a814c35a39..4fdc6ef4be 100644
--- a/test/functional/options/keymap_spec.lua
+++ b/test/functional/options/keymap_spec.lua
@@ -184,7 +184,7 @@ describe("'keymap' / :lmap", function()
feed('il<esc>')
expect('alllaaa')
end)
- -- This is a problem introduced when introducting :lmap and macro
+ -- This is a problem introduced when introducing :lmap and macro
-- compatibility. There are no plans to fix this as the complexity involved
-- seems too great.
pending('mappings not applied to macro replay of :lnoremap', function()
diff --git a/test/functional/options/mousescroll_spec.lua b/test/functional/options/mousescroll_spec.lua
index 2c9b2d175e..5bff45a836 100644
--- a/test/functional/options/mousescroll_spec.lua
+++ b/test/functional/options/mousescroll_spec.lua
@@ -97,6 +97,24 @@ describe("'mousescroll'", function()
eq(10, screencol())
scroll('left')
eq(10, screencol())
+
+ -- vertical scrolling is still disabled with non-zero 'scrolloff' value
+ command('set scrolloff=1')
+
+ eq(10, screenrow())
+ scroll('up')
+ eq(10, screenrow())
+ scroll('down')
+ eq(10, screenrow())
+
+ -- also in insert mode
+ feed('i')
+
+ eq(10, screenrow())
+ scroll('up')
+ eq(10, screenrow())
+ scroll('down')
+ eq(10, screenrow())
end)
local test_vertical_scrolling = function()
diff --git a/test/functional/plugin/ccomplete_spec.lua b/test/functional/plugin/ccomplete_spec.lua
new file mode 100644
index 0000000000..903f16fc73
--- /dev/null
+++ b/test/functional/plugin/ccomplete_spec.lua
@@ -0,0 +1,61 @@
+local helpers = require('test.functional.helpers')(after_each)
+local clear = helpers.clear
+local command = helpers.command
+local eq = helpers.eq
+local eval = helpers.eval
+local feed = helpers.feed
+local write_file = helpers.write_file
+
+describe('ccomplete#Complete', function()
+ setup(function()
+ -- Realistic tags generated from neovim source tree using `ctags -R *`
+ write_file(
+ 'Xtags',
+ [[
+augroup_del src/nvim/autocmd.c /^void augroup_del(char *name, bool stupid_legacy_mode)$/;" f typeref:typename:void
+augroup_exists src/nvim/autocmd.c /^bool augroup_exists(const char *name)$/;" f typeref:typename:bool
+augroup_find src/nvim/autocmd.c /^int augroup_find(const char *name)$/;" f typeref:typename:int
+aupat_get_buflocal_nr src/nvim/autocmd.c /^int aupat_get_buflocal_nr(char *pat, int patlen)$/;" f typeref:typename:int
+aupat_is_buflocal src/nvim/autocmd.c /^bool aupat_is_buflocal(char *pat, int patlen)$/;" f typeref:typename:bool
+expand_get_augroup_name src/nvim/autocmd.c /^char *expand_get_augroup_name(expand_T *xp, int idx)$/;" f typeref:typename:char *
+expand_get_event_name src/nvim/autocmd.c /^char *expand_get_event_name(expand_T *xp, int idx)$/;" f typeref:typename:char *
+]]
+ )
+ end)
+
+ before_each(function()
+ clear()
+ command('set tags=Xtags')
+ end)
+
+ teardown(function()
+ os.remove('Xtags')
+ end)
+
+ it('can complete from Xtags', function()
+ local completed = eval('ccomplete#Complete(0, "a")')
+ eq(5, #completed)
+ eq('augroup_del(', completed[1].word)
+ eq('f', completed[1].kind)
+
+ local aupat = eval('ccomplete#Complete(0, "aupat")')
+ eq(2, #aupat)
+ eq('aupat_get_buflocal_nr(', aupat[1].word)
+ eq('f', aupat[1].kind)
+ end)
+
+ it('does not error when returning no matches', function()
+ local completed = eval('ccomplete#Complete(0, "doesnotmatch")')
+ eq({}, completed)
+ end)
+
+ it('can find the beginning of a word for C', function()
+ command('set filetype=c')
+ feed('i int something = augroup')
+ local result = eval('ccomplete#Complete(1, "")')
+ eq(#' int something = ', result)
+
+ local completed = eval('ccomplete#Complete(0, "augroup")')
+ eq(3, #completed)
+ end)
+end)
diff --git a/test/functional/plugin/cfilter_spec.lua b/test/functional/plugin/cfilter_spec.lua
new file mode 100644
index 0000000000..8b1e75b495
--- /dev/null
+++ b/test/functional/plugin/cfilter_spec.lua
@@ -0,0 +1,106 @@
+local helpers = require('test.functional.helpers')(after_each)
+local clear = helpers.clear
+local command = helpers.command
+local eq = helpers.eq
+local funcs = helpers.funcs
+
+describe('cfilter.lua', function()
+ before_each(function()
+ clear()
+ command('packadd cfilter')
+ end)
+
+ for _, list in ipairs({
+ {
+ name = 'Cfilter',
+ get = funcs.getqflist,
+ set = funcs.setqflist,
+ },
+ {
+ name = 'Lfilter',
+ get = function()
+ return funcs.getloclist(0)
+ end,
+ set = function(items)
+ return funcs.setloclist(0, items)
+ end,
+ },
+ }) do
+ local filter = function(s, bang)
+ if not bang then
+ bang = ''
+ else
+ bang = '!'
+ end
+
+ command(string.format('%s%s %s', list.name, bang, s))
+ end
+
+ describe((':%s'):format(list.name), function()
+ it('does not error on empty list', function()
+ filter('nothing')
+ eq({}, funcs.getqflist())
+ end)
+
+ it('requires an argument', function()
+ local ok = pcall(filter, '')
+ eq(false, ok)
+ end)
+
+ local test = function(name, s, res, map, bang)
+ it(('%s (%s)'):format(name, s), function()
+ list.set({
+ { filename = 'foo', lnum = 1, text = 'bar' },
+ { filename = 'foo', lnum = 2, text = 'baz' },
+ { filename = 'foo', lnum = 3, text = 'zed' },
+ })
+
+ filter(s, bang)
+
+ local got = list.get()
+ if map then
+ got = map(got)
+ end
+ eq(res, got)
+ end)
+ end
+
+ local toname = function(qflist)
+ return funcs.map(qflist, 'v:val.text')
+ end
+
+ test('filters with no matches', 'does not match', {})
+
+ test('filters with matches', 'ba', { 'bar', 'baz' }, toname)
+ test('filters with matches', 'z', { 'baz', 'zed' }, toname)
+ test('filters with matches', '^z', { 'zed' }, toname)
+ test('filters with not matches', '^z', { 'bar', 'baz' }, toname, true)
+
+ it('also supports using the / register', function()
+ list.set({
+ { filename = 'foo', lnum = 1, text = 'bar' },
+ { filename = 'foo', lnum = 2, text = 'baz' },
+ { filename = 'foo', lnum = 3, text = 'zed' },
+ })
+
+ funcs.setreg('/', 'ba')
+ filter('/')
+
+ eq({ 'bar', 'baz' }, toname(list.get()))
+ end)
+
+ it('also supports using the / register with bang', function()
+ list.set({
+ { filename = 'foo', lnum = 1, text = 'bar' },
+ { filename = 'foo', lnum = 2, text = 'baz' },
+ { filename = 'foo', lnum = 3, text = 'zed' },
+ })
+
+ funcs.setreg('/', 'ba')
+ filter('/', true)
+
+ eq({ 'zed' }, toname(list.get()))
+ end)
+ end)
+ end
+end)
diff --git a/test/functional/plugin/editorconfig_spec.lua b/test/functional/plugin/editorconfig_spec.lua
new file mode 100644
index 0000000000..e6a2550aba
--- /dev/null
+++ b/test/functional/plugin/editorconfig_spec.lua
@@ -0,0 +1,210 @@
+local helpers = require('test.functional.helpers')(after_each)
+local clear = helpers.clear
+local command = helpers.command
+local eq = helpers.eq
+local pathsep = helpers.get_pathsep()
+local curbufmeths = helpers.curbufmeths
+local funcs = helpers.funcs
+local meths = helpers.meths
+
+local testdir = 'Xtest-editorconfig'
+
+local function test_case(name, expected)
+ local filename = testdir .. pathsep .. name
+ command('edit ' .. filename)
+ for opt, val in pairs(expected) do
+ eq(val, curbufmeths.get_option(opt), name)
+ end
+end
+
+setup(function()
+ helpers.mkdir_p(testdir)
+ helpers.write_file(
+ testdir .. pathsep .. '.editorconfig',
+ [[
+ root = true
+
+ [3_space.txt]
+ indent_style = space
+ indent_size = 3
+ tab_width = 3
+
+ [4_space.py]
+ indent_style = space
+ indent_size = 4
+ tab_width = 8
+
+ [space.txt]
+ indent_style = space
+ indent_size = tab
+
+ [tab.txt]
+ indent_style = tab
+
+ [4_tab.txt]
+ indent_style = tab
+ indent_size = 4
+ tab_width = 4
+
+ [4_tab_width_of_8.txt]
+ indent_style = tab
+ indent_size = 4
+ tab_width = 8
+
+ [lf.txt]
+ end_of_line = lf
+
+ [crlf.txt]
+ end_of_line = crlf
+
+ [cr.txt]
+ end_of_line = cr
+
+ [utf-8.txt]
+ charset = utf-8
+
+ [utf-8-bom.txt]
+ charset = utf-8-bom
+
+ [utf-16be.txt]
+ charset = utf-16be
+
+ [utf-16le.txt]
+ charset = utf-16le
+
+ [latin1.txt]
+ charset = latin1
+
+ [with_newline.txt]
+ insert_final_newline = true
+
+ [without_newline.txt]
+ insert_final_newline = false
+
+ [trim.txt]
+ trim_trailing_whitespace = true
+
+ [no_trim.txt]
+ trim_trailing_whitespace = false
+
+ [max_line_length.txt]
+ max_line_length = 42
+ ]]
+ )
+end)
+
+teardown(function()
+ helpers.rmdir(testdir)
+end)
+
+describe('editorconfig', function()
+ before_each(function()
+ -- Remove -u NONE so that plugins (i.e. editorconfig.lua) are loaded
+ clear({ args_rm = { '-u' } })
+ end)
+
+ it('sets indent options', function()
+ test_case('3_space.txt', {
+ expandtab = true,
+ shiftwidth = 3,
+ softtabstop = -1,
+ tabstop = 3,
+ })
+
+ test_case('4_space.py', {
+ expandtab = true,
+ shiftwidth = 4,
+ softtabstop = -1,
+ tabstop = 8,
+ })
+
+ test_case('space.txt', {
+ expandtab = true,
+ shiftwidth = 0,
+ softtabstop = 0,
+ })
+
+ test_case('tab.txt', {
+ expandtab = false,
+ shiftwidth = 0,
+ softtabstop = 0,
+ })
+
+ test_case('4_tab.txt', {
+ expandtab = false,
+ shiftwidth = 4,
+ softtabstop = -1,
+ tabstop = 4,
+ })
+
+ test_case('4_tab_width_of_8.txt', {
+ expandtab = false,
+ shiftwidth = 4,
+ softtabstop = -1,
+ tabstop = 8,
+ })
+ end)
+
+ it('sets end-of-line options', function()
+ test_case('lf.txt', { fileformat = 'unix' })
+ test_case('crlf.txt', { fileformat = 'dos' })
+ test_case('cr.txt', { fileformat = 'mac' })
+ end)
+
+ it('sets encoding options', function()
+ test_case('utf-8.txt', { fileencoding = 'utf-8', bomb = false })
+ test_case('utf-8-bom.txt', { fileencoding = 'utf-8', bomb = true })
+ test_case('utf-16be.txt', { fileencoding = 'utf-16', bomb = false })
+ test_case('utf-16le.txt', { fileencoding = 'utf-16le', bomb = false })
+ test_case('latin1.txt', { fileencoding = 'latin1', bomb = false })
+ end)
+
+ it('sets newline options', function()
+ test_case('with_newline.txt', { fixendofline = true, endofline = true })
+ test_case('without_newline.txt', { fixendofline = false, endofline = false })
+ end)
+
+ it('respects trim_trailing_whitespace', function()
+ local filename = testdir .. pathsep .. 'trim.txt'
+ -- luacheck: push ignore 613
+ local untrimmed = [[
+This line ends in whitespace
+So does this one
+And this one
+But not this one
+]]
+ -- luacheck: pop
+ local trimmed = untrimmed:gsub('%s+\n', '\n')
+
+ helpers.write_file(filename, untrimmed)
+ command('edit ' .. filename)
+ command('write')
+ command('bdelete')
+ eq(trimmed, helpers.read_file(filename))
+
+ filename = testdir .. pathsep .. 'no_trim.txt'
+ helpers.write_file(filename, untrimmed)
+ command('edit ' .. filename)
+ command('write')
+ command('bdelete')
+ eq(untrimmed, helpers.read_file(filename))
+ end)
+
+ it('sets textwidth', function()
+ test_case('max_line_length.txt', { textwidth = 42 })
+ end)
+
+ it('can be disabled globally', function()
+ meths.set_var('editorconfig', false)
+ meths.set_option_value('shiftwidth', 42, {})
+ test_case('3_space.txt', { shiftwidth = 42 })
+ end)
+
+ it('can be disabled per-buffer', function()
+ meths.set_option_value('shiftwidth', 42, {})
+ local bufnr = funcs.bufadd(testdir .. pathsep .. '3_space.txt')
+ meths.buf_set_var(bufnr, 'editorconfig', false)
+ test_case('3_space.txt', { shiftwidth = 42 })
+ test_case('4_space.py', { shiftwidth = 4 })
+ end)
+end)
diff --git a/test/functional/plugin/health_spec.lua b/test/functional/plugin/health_spec.lua
index ba66117fb1..97d32313e5 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, neq = helpers.eq, helpers.neq
+local eq, neq, matches = helpers.eq, helpers.neq, helpers.matches
local getcompletion = helpers.funcs.getcompletion
describe(':checkhealth', function()
@@ -29,8 +29,7 @@ describe(':checkhealth', function()
-- Do this after startup, otherwise it just breaks $VIMRUNTIME.
command("let $VIM='zub'")
command("checkhealth nvim")
- eq("ERROR: $VIM is invalid: zub",
- string.match(curbuf_contents(), "ERROR: $VIM .* zub"))
+ matches('ERROR $VIM .* zub', curbuf_contents())
end)
it('completions can be listed via getcompletion()', function()
clear()
@@ -56,21 +55,22 @@ describe('health.vim', function()
command("checkhealth full_render")
helpers.expect([[
+ ==============================================================================
full_render: health#full_render#check
- ========================================================================
- ## report 1
- - OK: life is fine
- - WARNING: no what installed
- - ADVICE:
- - pip what
- - make what
-
- ## report 2
- - INFO: stuff is stable
- - ERROR: why no hardcopy
- - ADVICE:
- - :help |:hardcopy|
- - :help |:TOhtml|
+
+ report 1 ~
+ - OK life is fine
+ - WARNING no what installed
+ - ADVICE:
+ - pip what
+ - make what
+
+ report 2 ~
+ - stuff is stable
+ - ERROR why no hardcopy
+ - ADVICE:
+ - :help |:hardcopy|
+ - :help |:TOhtml|
]])
end)
@@ -78,26 +78,29 @@ describe('health.vim', function()
command("checkhealth success1 success2 test_plug")
helpers.expect([[
+ ==============================================================================
success1: health#success1#check
- ========================================================================
- ## report 1
- - OK: everything is fine
- ## report 2
- - OK: nothing to see here
+ report 1 ~
+ - OK everything is fine
+
+ report 2 ~
+ - OK nothing to see here
+ ==============================================================================
success2: health#success2#check
- ========================================================================
- ## another 1
- - OK: ok
+ another 1 ~
+ - OK ok
+
+ ==============================================================================
test_plug: require("test_plug.health").check()
- ========================================================================
- ## report 1
- - OK: everything is fine
- ## report 2
- - OK: nothing to see here
+ report 1 ~
+ - OK everything is fine
+
+ report 2 ~
+ - OK nothing to see here
]])
end)
@@ -107,13 +110,14 @@ describe('health.vim', function()
-- and the Lua healthcheck is used instead.
helpers.expect([[
+ ==============================================================================
test_plug: require("test_plug.health").check()
- ========================================================================
- ## report 1
- - OK: everything is fine
- ## report 2
- - OK: nothing to see here
+ report 1 ~
+ - OK everything is fine
+
+ report 2 ~
+ - OK nothing to see here
]])
end)
@@ -121,13 +125,14 @@ describe('health.vim', function()
command("checkhealth test_plug.submodule")
helpers.expect([[
+ ==============================================================================
test_plug.submodule: require("test_plug.submodule.health").check()
- ========================================================================
- ## report 1
- - OK: everything is fine
- ## report 2
- - OK: nothing to see here
+ report 1 ~
+ - OK everything is fine
+
+ report 2 ~
+ - OK nothing to see here
]])
end)
@@ -138,30 +143,34 @@ describe('health.vim', function()
local received = table.concat(buf_lines, '\n', 1, #buf_lines - 5)
local expected = helpers.dedent([[
+ ==============================================================================
test_plug: require("test_plug.health").check()
- ========================================================================
- ## report 1
- - OK: everything is fine
- ## report 2
- - OK: nothing to see here
+ report 1 ~
+ - OK everything is fine
+
+ report 2 ~
+ - OK nothing to see here
+ ==============================================================================
test_plug.submodule: require("test_plug.submodule.health").check()
- ========================================================================
- ## report 1
- - OK: everything is fine
- ## report 2
- - OK: nothing to see here
+ report 1 ~
+ - OK everything is fine
+ 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.
+ - 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:
- function health#check, line 20]])
+
+ - ERROR Failed to run healthcheck for "test_plug.submodule_failed" plugin. Exception:
+ function health#check, line 25]])
eq(expected, received)
end)
@@ -169,11 +178,12 @@ describe('health.vim', function()
command("checkhealth broken")
helpers.expect([[
+ ==============================================================================
broken: health#broken#check
- ========================================================================
- - ERROR: Failed to run healthcheck for "broken" plugin. Exception:
- function health#check[20]..health#broken#check, line 1
- caused an error
+
+ - ERROR Failed to run healthcheck for "broken" plugin. Exception:
+ function health#check[25]..health#broken#check, line 1
+ caused an error
]])
end)
@@ -181,9 +191,10 @@ describe('health.vim', 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.
+
+ - ERROR The healthcheck report for "test_plug.submodule_empty" plugin is empty.
]])
end)
@@ -198,38 +209,38 @@ describe('health.vim', function()
local expected = global_helpers.dedent([[
+ ==============================================================================
test_plug.submodule_failed: require("test_plug.submodule_failed.health").check()
- ========================================================================
- - ERROR: Failed to run healthcheck for "test_plug.submodule_failed" plugin. Exception:
- function health#check, line 20]])
+
+ - ERROR Failed to run healthcheck for "test_plug.submodule_failed" plugin. Exception:
+ function health#check, line 25]])
eq(expected, received)
end)
it("highlights OK, ERROR", function()
- local screen = Screen.new(72, 10)
+ local screen = Screen.new(50, 12)
screen:attach()
screen:set_default_attr_ids({
Ok = { foreground = Screen.colors.Grey3, background = 6291200 },
Error = { foreground = Screen.colors.Grey100, background = Screen.colors.Red },
- Heading = { bold=true, foreground=Screen.colors.Magenta },
- Heading2 = { foreground = Screen.colors.SlateBlue },
- Bar = { foreground = 0x6a0dad },
- Bullet = { bold=true, foreground=Screen.colors.Brown },
+ Heading = { foreground = tonumber('0x6a0dad') },
+ Bar = { foreground = Screen.colors.LightGrey, background = Screen.colors.DarkGrey },
})
command("checkhealth foo success1")
- command("1tabclose")
- command("set laststatus=0")
+ command("set nowrap laststatus=0")
screen:expect{grid=[[
- ^ |
- {Heading:foo: } |
- {Bar:========================================================================}|
- {Bullet: -} {Error:ERROR}: No healthcheck found for "foo" plugin. |
- |
- {Heading:success1: health#success1#check} |
- {Bar:========================================================================}|
- {Heading2:##}{Heading: report 1} |
- {Bullet: -} {Ok:OK}: everything is fine |
- |
+ ^ |
+ {Bar:──────────────────────────────────────────────────}|
+ {Heading:foo: } |
+ |
+ - {Error:ERROR} No healthcheck found for "foo" plugin. |
+ |
+ {Bar:──────────────────────────────────────────────────}|
+ {Heading:success1: health#success1#check} |
+ |
+ {Heading:report 1} |
+ - {Ok:OK} everything is fine |
+ |
]]}
end)
@@ -238,9 +249,10 @@ describe('health.vim', function()
-- luacheck: ignore 613
helpers.expect([[
+ ==============================================================================
non_existent_healthcheck:
- ========================================================================
- - ERROR: No healthcheck found for "non_existent_healthcheck" plugin.
+
+ - ERROR No healthcheck found for "non_existent_healthcheck" plugin.
]])
end)
diff --git a/test/functional/plugin/lsp/codelens_spec.lua b/test/functional/plugin/lsp/codelens_spec.lua
index ecc2f579b8..3d7a15a191 100644
--- a/test/functional/plugin/lsp/codelens_spec.lua
+++ b/test/functional/plugin/lsp/codelens_spec.lua
@@ -58,6 +58,41 @@ describe('vim.lsp.codelens', function()
]], bufnr)
eq({[1] = {'Lens1', 'LspCodeLens'}}, virtual_text_chunks)
+ end)
+
+ it('can clear all lens', function()
+ local fake_uri = "file:///fake/uri"
+ local bufnr = exec_lua([[
+ fake_uri = ...
+ local bufnr = vim.uri_to_bufnr(fake_uri)
+ local lines = {'So', 'many', 'lines'}
+ vim.fn.bufload(bufnr)
+ vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
+ return bufnr
+ ]], fake_uri)
+
+ exec_lua([[
+ local bufnr = ...
+ local lenses = {
+ {
+ range = {
+ start = { line = 0, character = 0, },
+ ['end'] = { line = 0, character = 0 }
+ },
+ command = { title = 'Lens1', command = 'Dummy' }
+ },
+ }
+ vim.lsp.codelens.on_codelens(nil, lenses, {method='textDocument/codeLens', client_id=1, bufnr=bufnr})
+ ]], bufnr)
+
+ local stored_lenses = exec_lua('return vim.lsp.codelens.get(...)', bufnr)
+ eq(1, #stored_lenses)
+
+ exec_lua([[
+ vim.lsp.codelens.clear()
+ ]])
+ stored_lenses = exec_lua('return vim.lsp.codelens.get(...)', bufnr)
+ eq(0, #stored_lenses)
end)
end)
diff --git a/test/functional/plugin/lsp/helpers.lua b/test/functional/plugin/lsp/helpers.lua
new file mode 100644
index 0000000000..028ccb9e2c
--- /dev/null
+++ b/test/functional/plugin/lsp/helpers.lua
@@ -0,0 +1,176 @@
+local helpers = require('test.functional.helpers')(nil)
+
+local clear = helpers.clear
+local exec_lua = helpers.exec_lua
+local run = helpers.run
+local stop = helpers.stop
+local NIL = helpers.NIL
+
+local M = {}
+
+function M.clear_notrace()
+ -- problem: here be dragons
+ -- solution: don't look too closely for dragons
+ clear {env={
+ NVIM_LUA_NOTRACK="1";
+ VIMRUNTIME=os.getenv"VIMRUNTIME";
+ }}
+end
+
+M.create_server_definition = [[
+ function _create_server(opts)
+ opts = opts or {}
+ local server = {}
+ server.messages = {}
+
+ function server.cmd(dispatchers)
+ local closing = false
+ local handlers = opts.handlers or {}
+ local srv = {}
+
+ function srv.request(method, params, callback)
+ table.insert(server.messages, {
+ method = method,
+ params = params,
+ })
+ local handler = handlers[method]
+ if handler then
+ local response, err = handler(method, params)
+ callback(err, response)
+ elseif method == 'initialize' then
+ callback(nil, {
+ capabilities = opts.capabilities or {}
+ })
+ elseif method == 'shutdown' then
+ callback(nil, nil)
+ end
+ local request_id = #server.messages
+ return true, request_id
+ end
+
+ function srv.notify(method, params)
+ table.insert(server.messages, {
+ method = method,
+ params = params
+ })
+ if method == 'exit' then
+ dispatchers.on_exit(0, 15)
+ end
+ end
+
+ function srv.is_closing()
+ return closing
+ end
+
+ function srv.terminate()
+ closing = true
+ end
+
+ return srv
+ end
+
+ return server
+ end
+]]
+
+-- Fake LSP server.
+M.fake_lsp_code = 'test/functional/fixtures/fake-lsp-server.lua'
+M.fake_lsp_logfile = 'Xtest-fake-lsp.log'
+
+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, settings = ...
+ TEST_RPC_CLIENT_ID = lsp.start_client {
+ cmd_env = {
+ NVIM_LOG_FILE = logfile;
+ NVIM_LUA_NOTRACK = "1";
+ };
+ cmd = {
+ vim.v.progpath, '-Es', '-u', 'NONE', '--headless',
+ "-c", string.format("lua TEST_NAME = %q", test_name),
+ "-c", string.format("lua TIMEOUT = %d", timeout),
+ "-c", "luafile "..fixture_filename,
+ };
+ handlers = setmetatable({}, {
+ __index = function(t, method)
+ return function(...)
+ return vim.rpcrequest(1, 'handler', ...)
+ end
+ end;
+ });
+ workspace_folders = {{
+ uri = 'file://' .. vim.loop.cwd(),
+ name = 'test_folder',
+ }};
+ on_init = function(client, result)
+ TEST_RPC_CLIENT = client
+ vim.rpcrequest(1, "init", result)
+ end;
+ flags = {
+ 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, M.fake_lsp_code, M.fake_lsp_logfile, timeout_ms or 1e3, options or {}, settings or {})
+end
+
+function M.test_rpc_server(config)
+ if config.test_name then
+ M.clear_notrace()
+ fake_lsp_server_setup(config.test_name, config.timeout_ms or 1e3, config.options, config.settings)
+ end
+ local client = setmetatable({}, {
+ __index = function(_, name)
+ -- Workaround for not being able to yield() inside __index for Lua 5.1 :(
+ -- Otherwise I would just return the value here.
+ return function(...)
+ return exec_lua([=[
+ local name = ...
+ if type(TEST_RPC_CLIENT[name]) == 'function' then
+ return TEST_RPC_CLIENT[name](select(2, ...))
+ else
+ return TEST_RPC_CLIENT[name]
+ end
+ ]=], name, ...)
+ end
+ end;
+ })
+ local code, signal
+ local function on_request(method, args)
+ if method == "init" then
+ if config.on_init then
+ config.on_init(client, unpack(args))
+ end
+ return NIL
+ end
+ if method == 'handler' then
+ if config.on_handler then
+ config.on_handler(unpack(args))
+ end
+ end
+ return NIL
+ end
+ local function on_notify(method, args)
+ if method == 'exit' then
+ code, signal = unpack(args)
+ return stop()
+ end
+ end
+ -- TODO specify timeout?
+ -- run(on_request, on_notify, config.on_setup, 1000)
+ run(on_request, on_notify, config.on_setup)
+ if config.on_exit then
+ config.on_exit(code, signal)
+ end
+ stop()
+ if config.test_name then
+ exec_lua("vim.api.nvim_exec_autocmds('VimLeavePre', { modeline = false })")
+ end
+end
+
+return M
diff --git a/test/functional/plugin/lsp/semantic_tokens_spec.lua b/test/functional/plugin/lsp/semantic_tokens_spec.lua
new file mode 100644
index 0000000000..9c1ba86fe1
--- /dev/null
+++ b/test/functional/plugin/lsp/semantic_tokens_spec.lua
@@ -0,0 +1,1219 @@
+local helpers = require('test.functional.helpers')(after_each)
+local lsp_helpers = require('test.functional.plugin.lsp.helpers')
+local Screen = require('test.functional.ui.screen')
+
+local command = helpers.command
+local dedent = helpers.dedent
+local eq = helpers.eq
+local exec_lua = helpers.exec_lua
+local feed = helpers.feed
+local feed_command = helpers.feed_command
+local insert = helpers.insert
+local matches = helpers.matches
+
+local clear_notrace = lsp_helpers.clear_notrace
+local create_server_definition = lsp_helpers.create_server_definition
+
+before_each(function()
+ clear_notrace()
+end)
+
+after_each(function()
+ exec_lua("vim.api.nvim_exec_autocmds('VimLeavePre', { modeline = false })")
+end)
+
+describe('semantic token highlighting', function()
+
+ describe('general', function()
+ local text = dedent([[
+ #include <iostream>
+
+ int main()
+ {
+ int x;
+ #ifdef __cplusplus
+ std::cout << x << "\n";
+ #else
+ printf("%d\n", x);
+ #endif
+ }
+ }]])
+
+ local legend = [[{
+ "tokenTypes": [
+ "variable", "variable", "parameter", "function", "method", "function", "property", "variable", "class", "interface", "enum", "enumMember", "type", "type", "unknown", "namespace", "typeParameter", "concept", "type", "macro", "comment"
+ ],
+ "tokenModifiers": [
+ "declaration", "deprecated", "deduced", "readonly", "static", "abstract", "virtual", "dependentName", "defaultLibrary", "usedAsMutableReference", "functionScope", "classScope", "fileScope", "globalScope"
+ ]
+ }]]
+
+ local response = [[{
+ "data": [ 2, 4, 4, 3, 8193, 2, 8, 1, 1, 1025, 1, 7, 11, 19, 8192, 1, 4, 3, 15, 8448, 0, 5, 4, 0, 8448, 0, 8, 1, 1, 1024, 1, 0, 5, 20, 0, 1, 0, 22, 20, 0, 1, 0, 6, 20, 0 ],
+ "resultId": 1
+ }]]
+
+ local edit_response = [[{
+ "edits": [ {"data": [ 2, 8, 1, 3, 8193, 1, 7, 11, 19, 8192, 1, 4, 3, 15, 8448, 0, 5, 4, 0, 8448, 0, 8, 1, 3, 8192 ], "deleteCount": 25, "start": 5 } ],
+ "resultId":"2"
+ }]]
+
+ local screen
+ before_each(function()
+ screen = Screen.new(40, 16)
+ screen:attach()
+ screen:set_default_attr_ids {
+ [1] = { bold = true, foreground = Screen.colors.Blue1 };
+ [2] = { foreground = Screen.colors.DarkCyan };
+ [3] = { foreground = Screen.colors.SlateBlue };
+ [4] = { bold = true, foreground = Screen.colors.SeaGreen };
+ [5] = { foreground = tonumber('0x6a0dad') };
+ [6] = { foreground = Screen.colors.Blue1 };
+ [7] = { bold = true, foreground = Screen.colors.DarkCyan };
+ [8] = { bold = true, foreground = Screen.colors.SlateBlue };
+ }
+ command([[ hi link @namespace Type ]])
+ command([[ hi link @function Special ]])
+ command([[ hi @declaration gui=bold ]])
+
+ exec_lua(create_server_definition)
+ exec_lua([[
+ local legend, response, edit_response = ...
+ server = _create_server({
+ capabilities = {
+ semanticTokensProvider = {
+ full = { delta = true },
+ legend = vim.fn.json_decode(legend),
+ },
+ },
+ handlers = {
+ ['textDocument/semanticTokens/full'] = function()
+ return vim.fn.json_decode(response)
+ end,
+ ['textDocument/semanticTokens/full/delta'] = function()
+ return vim.fn.json_decode(edit_response)
+ end,
+ }
+ })
+ ]], legend, response, edit_response)
+ end)
+
+ it('buffer is highlighted when attached', function()
+ exec_lua([[
+ bufnr = vim.api.nvim_get_current_buf()
+ vim.api.nvim_win_set_buf(0, bufnr)
+ client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
+ ]])
+
+ insert(text)
+
+ screen:expect { grid = [[
+ #include <iostream> |
+ |
+ int {8:main}() |
+ { |
+ int {7:x}; |
+ #ifdef {5:__cplusplus} |
+ {4:std}::{2:cout} << {2:x} << "\n"; |
+ {6:#else} |
+ {6: printf("%d\n", x);} |
+ {6:#endif} |
+ } |
+ ^} |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]] }
+ end)
+
+ it('buffer is unhighlighted when client is detached', function()
+ exec_lua([[
+ bufnr = vim.api.nvim_get_current_buf()
+ vim.api.nvim_win_set_buf(0, bufnr)
+ client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
+ ]])
+
+ insert(text)
+
+ exec_lua([[
+ vim.notify = function() end
+ vim.lsp.buf_detach_client(bufnr, client_id)
+ ]])
+
+ screen:expect { grid = [[
+ #include <iostream> |
+ |
+ int main() |
+ { |
+ int x; |
+ #ifdef __cplusplus |
+ std::cout << x << "\n"; |
+ #else |
+ printf("%d\n", x); |
+ #endif |
+ } |
+ ^} |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]] }
+ end)
+
+ it('buffer is highlighted and unhighlighted when semantic token highlighting is started and stopped'
+ , function()
+ exec_lua([[
+ bufnr = vim.api.nvim_get_current_buf()
+ vim.api.nvim_win_set_buf(0, bufnr)
+ client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
+ ]])
+
+ insert(text)
+
+ exec_lua([[
+ vim.notify = function() end
+ vim.lsp.semantic_tokens.stop(bufnr, client_id)
+ ]])
+
+ screen:expect { grid = [[
+ #include <iostream> |
+ |
+ int main() |
+ { |
+ int x; |
+ #ifdef __cplusplus |
+ std::cout << x << "\n"; |
+ #else |
+ printf("%d\n", x); |
+ #endif |
+ } |
+ ^} |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]] }
+
+ exec_lua([[
+ vim.lsp.semantic_tokens.start(bufnr, client_id)
+ ]])
+
+ screen:expect { grid = [[
+ #include <iostream> |
+ |
+ int {8:main}() |
+ { |
+ int {7:x}; |
+ #ifdef {5:__cplusplus} |
+ {4:std}::{2:cout} << {2:x} << "\n"; |
+ {6:#else} |
+ {6: printf("%d\n", x);} |
+ {6:#endif} |
+ } |
+ ^} |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]] }
+ end)
+
+ it('buffer is re-highlighted when force refreshed', function()
+ exec_lua([[
+ bufnr = vim.api.nvim_get_current_buf()
+ vim.api.nvim_win_set_buf(0, bufnr)
+ client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
+ ]])
+
+ insert(text)
+
+ screen:expect { grid = [[
+ #include <iostream> |
+ |
+ int {8:main}() |
+ { |
+ int {7:x}; |
+ #ifdef {5:__cplusplus} |
+ {4:std}::{2:cout} << {2:x} << "\n"; |
+ {6:#else} |
+ {6: printf("%d\n", x);} |
+ {6:#endif} |
+ } |
+ ^} |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]] }
+
+ exec_lua([[
+ vim.lsp.semantic_tokens.force_refresh(bufnr)
+ ]])
+
+ screen:expect { grid = [[
+ #include <iostream> |
+ |
+ int {8:main}() |
+ { |
+ int {7:x}; |
+ #ifdef {5:__cplusplus} |
+ {4:std}::{2:cout} << {2:x} << "\n"; |
+ {6:#else} |
+ {6: printf("%d\n", x);} |
+ {6:#endif} |
+ } |
+ ^} |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], unchanged = true }
+
+ local messages = exec_lua('return server.messages')
+ local token_request_count = 0
+ for _, message in ipairs(messages) do
+ assert(message.method ~= 'textDocument/semanticTokens/full/delta', 'delta request received')
+ if message.method == 'textDocument/semanticTokens/full' then
+ token_request_count = token_request_count + 1
+ end
+ end
+ eq(2, token_request_count)
+ end)
+
+ it('destroys the highlighter if the buffer is deleted', function()
+ exec_lua([[
+ bufnr = vim.api.nvim_get_current_buf()
+ vim.api.nvim_win_set_buf(0, bufnr)
+ client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
+ ]])
+
+ insert(text)
+
+ local highlighters = exec_lua([[
+ vim.api.nvim_buf_delete(bufnr, { force = true })
+ local semantic_tokens = vim.lsp.semantic_tokens
+ return semantic_tokens.__STHighlighter.active
+ ]])
+
+ eq({}, highlighters)
+ end)
+
+ it('updates highlights with delta request on buffer change', function()
+ exec_lua([[
+ bufnr = vim.api.nvim_get_current_buf()
+ vim.api.nvim_win_set_buf(0, bufnr)
+ client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
+ ]])
+
+ insert(text)
+ screen:expect { grid = [[
+ #include <iostream> |
+ |
+ int {8:main}() |
+ { |
+ int {7:x}; |
+ #ifdef {5:__cplusplus} |
+ {4:std}::{2:cout} << {2:x} << "\n"; |
+ {6:#else} |
+ {6: printf("%d\n", x);} |
+ {6:#endif} |
+ } |
+ ^} |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]] }
+ feed_command('%s/int x/int x()/')
+ feed_command('noh')
+ screen:expect { grid = [[
+ #include <iostream> |
+ |
+ int {8:main}() |
+ { |
+ ^int {8:x}(); |
+ #ifdef {5:__cplusplus} |
+ {4:std}::{2:cout} << {3:x} << "\n"; |
+ {6:#else} |
+ {6: printf("%d\n", x);} |
+ {6:#endif} |
+ } |
+ } |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :noh |
+ ]] }
+ end)
+
+ it('prevents starting semantic token highlighting with invalid conditions', function()
+ exec_lua([[
+ bufnr = vim.api.nvim_get_current_buf()
+ vim.api.nvim_win_set_buf(0, bufnr)
+ client_id = vim.lsp.start_client({ name = 'dummy', cmd = server.cmd })
+ notifications = {}
+ vim.notify = function(...) table.insert(notifications, 1, {...}) end
+ ]])
+ eq(false, exec_lua("return vim.lsp.buf_is_attached(bufnr, client_id)"))
+
+ insert(text)
+
+ local notifications = exec_lua([[
+ vim.lsp.semantic_tokens.start(bufnr, client_id)
+ return notifications
+ ]])
+ matches('%[LSP%] Client with id %d not attached to buffer %d', notifications[1][1])
+
+ notifications = exec_lua([[
+ vim.lsp.semantic_tokens.start(bufnr, client_id + 1)
+ return notifications
+ ]])
+ matches('%[LSP%] No client with id %d', notifications[1][1])
+ end)
+
+ it('opt-out: does not activate semantic token highlighting if disabled in client attach',
+ function()
+ exec_lua([[
+ bufnr = vim.api.nvim_get_current_buf()
+ vim.api.nvim_win_set_buf(0, bufnr)
+ client_id = vim.lsp.start({
+ name = 'dummy',
+ cmd = server.cmd,
+ on_attach = vim.schedule_wrap(function(client, bufnr)
+ client.server_capabilities.semanticTokensProvider = nil
+ end),
+ })
+ ]])
+ eq(true, exec_lua("return vim.lsp.buf_is_attached(bufnr, client_id)"))
+
+ insert(text)
+
+ screen:expect { grid = [[
+ #include <iostream> |
+ |
+ int main() |
+ { |
+ int x; |
+ #ifdef __cplusplus |
+ std::cout << x << "\n"; |
+ #else |
+ printf("%d\n", x); |
+ #endif |
+ } |
+ ^} |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]] }
+
+ local notifications = exec_lua([[
+ local notifications = {}
+ vim.notify = function(...) table.insert(notifications, 1, {...}) end
+ vim.lsp.semantic_tokens.start(bufnr, client_id)
+ return notifications
+ ]])
+ eq('[LSP] Server does not support semantic tokens', notifications[1][1])
+
+ screen:expect { grid = [[
+ #include <iostream> |
+ |
+ int main() |
+ { |
+ int x; |
+ #ifdef __cplusplus |
+ std::cout << x << "\n"; |
+ #else |
+ printf("%d\n", x); |
+ #endif |
+ } |
+ ^} |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], unchanged = true }
+ end)
+
+ it('ignores null responses from the server', function()
+ exec_lua([[
+ local legend, response, edit_response = ...
+ server2 = _create_server({
+ capabilities = {
+ semanticTokensProvider = {
+ full = { delta = false },
+ },
+ },
+ handlers = {
+ ['textDocument/semanticTokens/full'] = function()
+ return nil
+ end,
+ ['textDocument/semanticTokens/full/delta'] = function()
+ return nil
+ end,
+ }
+ })
+ bufnr = vim.api.nvim_get_current_buf()
+ vim.api.nvim_win_set_buf(0, bufnr)
+ client_id = vim.lsp.start({ name = 'dummy', cmd = server2.cmd })
+ ]])
+ eq(true, exec_lua("return vim.lsp.buf_is_attached(bufnr, client_id)"))
+
+ insert(text)
+
+ screen:expect { grid = [[
+ #include <iostream> |
+ |
+ int main() |
+ { |
+ int x; |
+ #ifdef __cplusplus |
+ std::cout << x << "\n"; |
+ #else |
+ printf("%d\n", x); |
+ #endif |
+ } |
+ ^} |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]] }
+ end)
+
+ it('does not send delta requests if not supported by server', function()
+ exec_lua([[
+ local legend, response, edit_response = ...
+ server2 = _create_server({
+ capabilities = {
+ semanticTokensProvider = {
+ full = { delta = false },
+ legend = vim.fn.json_decode(legend),
+ },
+ },
+ handlers = {
+ ['textDocument/semanticTokens/full'] = function()
+ return vim.fn.json_decode(response)
+ end,
+ ['textDocument/semanticTokens/full/delta'] = function()
+ return vim.fn.json_decode(edit_response)
+ end,
+ }
+ })
+ bufnr = vim.api.nvim_get_current_buf()
+ vim.api.nvim_win_set_buf(0, bufnr)
+ client_id = vim.lsp.start({ name = 'dummy', cmd = server2.cmd })
+ ]], legend, response, edit_response)
+
+ insert(text)
+ screen:expect { grid = [[
+ #include <iostream> |
+ |
+ int {8:main}() |
+ { |
+ int {7:x}; |
+ #ifdef {5:__cplusplus} |
+ {4:std}::{2:cout} << {2:x} << "\n"; |
+ {6:#else} |
+ {6: printf("%d\n", x);} |
+ {6:#endif} |
+ } |
+ ^} |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]] }
+ feed_command('%s/int x/int x()/')
+ feed_command('noh')
+
+ -- the highlights don't change because our fake server sent the exact
+ -- same result for the same method (the full request). "x" would have
+ -- changed to highlight index 3 had we sent a delta request
+ screen:expect { grid = [[
+ #include <iostream> |
+ |
+ int {8:main}() |
+ { |
+ ^int {7:x}(); |
+ #ifdef {5:__cplusplus} |
+ {4:std}::{2:cout} << {2:x} << "\n"; |
+ {6:#else} |
+ {6: printf("%d\n", x);} |
+ {6:#endif} |
+ } |
+ } |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :noh |
+ ]] }
+ local messages = exec_lua('return server2.messages')
+ local token_request_count = 0
+ for _, message in ipairs(messages) do
+ assert(message.method ~= 'textDocument/semanticTokens/full/delta', 'delta request received')
+ if message.method == 'textDocument/semanticTokens/full' then
+ token_request_count = token_request_count + 1
+ end
+ end
+ eq(2, token_request_count)
+ end)
+ end)
+
+ describe('token array decoding', function()
+ for _, test in ipairs({
+ {
+ it = 'clangd-15 on C',
+ text = [[char* foo = "\n";]],
+ response = [[{"data": [0, 6, 3, 0, 8193], "resultId": "1"}]],
+ legend = [[{
+ "tokenTypes": [
+ "variable", "variable", "parameter", "function", "method", "function", "property", "variable", "class", "interface", "enum", "enumMember", "type", "type", "unknown", "namespace", "typeParameter", "concept", "type", "macro", "comment"
+ ],
+ "tokenModifiers": [
+ "declaration", "deprecated", "deduced", "readonly", "static", "abstract", "virtual", "dependentName", "defaultLibrary", "usedAsMutableReference", "functionScope", "classScope", "fileScope", "globalScope"
+ ]
+ }]],
+ expected = {
+ {
+ line = 0,
+ modifiers = {
+ 'declaration',
+ 'globalScope',
+ },
+ start_col = 6,
+ end_col = 9,
+ type = 'variable',
+ extmark_added = true,
+ },
+ },
+ },
+ {
+ it = 'clangd-15 on C++',
+ text = [[#include <iostream>
+int main()
+{
+ #ifdef __cplusplus
+ const int x = 1;
+ std::cout << x << std::endl;
+ #else
+ comment
+ #endif
+}]],
+ response = [[{"data": [1, 4, 4, 3, 8193, 2, 9, 11, 19, 8192, 1, 12, 1, 1, 1033, 1, 2, 3, 15, 8448, 0, 5, 4, 0, 8448, 0, 8, 1, 1, 1032, 0, 5, 3, 15, 8448, 0, 5, 4, 3, 8448, 1, 0, 7, 20, 0, 1, 0, 11, 20, 0, 1, 0, 8, 20, 0], "resultId": "1"}]],
+ legend = [[{
+ "tokenTypes": [
+ "variable", "variable", "parameter", "function", "method", "function", "property", "variable", "class", "interface", "enum", "enumMember", "type", "type", "unknown", "namespace", "typeParameter", "concept", "type", "macro", "comment"
+ ],
+ "tokenModifiers": [
+ "declaration", "deprecated", "deduced", "readonly", "static", "abstract", "virtual", "dependentName", "defaultLibrary", "usedAsMutableReference", "functionScope", "classScope", "fileScope", "globalScope"
+ ]
+ }]],
+ expected = {
+ { -- main
+ line = 1,
+ modifiers = { 'declaration', 'globalScope' },
+ start_col = 4,
+ end_col = 8,
+ type = 'function',
+ extmark_added = true,
+ },
+ { -- __cplusplus
+ line = 3,
+ modifiers = { 'globalScope' },
+ start_col = 9,
+ end_col = 20,
+ type = 'macro',
+ extmark_added = true,
+ },
+ { -- x
+ line = 4,
+ modifiers = { 'declaration', 'readonly', 'functionScope' },
+ start_col = 12,
+ end_col = 13,
+ type = 'variable',
+ extmark_added = true,
+ },
+ { -- std
+ line = 5,
+ modifiers = { 'defaultLibrary', 'globalScope' },
+ start_col = 2,
+ end_col = 5,
+ type = 'namespace',
+ extmark_added = true,
+ },
+ { -- cout
+ line = 5,
+ modifiers = { 'defaultLibrary', 'globalScope' },
+ start_col = 7,
+ end_col = 11,
+ type = 'variable',
+ extmark_added = true,
+ },
+ { -- x
+ line = 5,
+ modifiers = { 'readonly', 'functionScope' },
+ start_col = 15,
+ end_col = 16,
+ type = 'variable',
+ extmark_added = true,
+ },
+ { -- std
+ line = 5,
+ modifiers = { 'defaultLibrary', 'globalScope' },
+ start_col = 20,
+ end_col = 23,
+ type = 'namespace',
+ extmark_added = true,
+ },
+ { -- endl
+ line = 5,
+ modifiers = { 'defaultLibrary', 'globalScope' },
+ start_col = 25,
+ end_col = 29,
+ type = 'function',
+ extmark_added = true,
+ },
+ { -- #else comment #endif
+ line = 6,
+ modifiers = {},
+ start_col = 0,
+ end_col = 7,
+ type = 'comment',
+ extmark_added = true,
+ },
+ {
+ line = 7,
+ modifiers = {},
+ start_col = 0,
+ end_col = 11,
+ type = 'comment',
+ extmark_added = true,
+ },
+ {
+ line = 8,
+ modifiers = {},
+ start_col = 0,
+ end_col = 8,
+ type = 'comment',
+ extmark_added = true,
+ },
+ },
+ },
+ {
+ it = 'sumneko_lua',
+ text = [[-- comment
+local a = 1
+b = "as"]],
+ response = [[{"data": [0, 0, 10, 17, 0, 1, 6, 1, 8, 1, 1, 0, 1, 8, 8]}]],
+ legend = [[{
+ "tokenTypes": [
+ "namespace", "type", "class", "enum", "interface", "struct", "typeParameter", "parameter", "variable", "property", "enumMember", "event", "function", "method", "macro", "keyword", "modifier", "comment", "string", "number", "regexp", "operator"
+ ],
+ "tokenModifiers": [
+ "declaration", "definition", "readonly", "static", "deprecated", "abstract", "async", "modification", "documentation", "defaultLibrary"
+ ]
+ }]],
+ expected = {
+ {
+ line = 0,
+ modifiers = {},
+ start_col = 0,
+ end_col = 10,
+ type = 'comment', -- comment
+ extmark_added = true,
+ },
+ {
+ line = 1,
+ modifiers = { 'declaration' }, -- a
+ start_col = 6,
+ end_col = 7,
+ type = 'variable',
+ extmark_added = true,
+ },
+ {
+ line = 2,
+ modifiers = { 'static' }, -- b (global)
+ start_col = 0,
+ end_col = 1,
+ type = 'variable',
+ extmark_added = true,
+ },
+ },
+ },
+ {
+ it = 'rust-analyzer',
+ text = [[pub fn main() {
+ break rust;
+ /// what?
+}
+]],
+ response = [[{"data": [0, 0, 3, 1, 0, 0, 4, 2, 1, 0, 0, 3, 4, 14, 524290, 0, 4, 1, 45, 0, 0, 1, 1, 45, 0, 0, 2, 1, 26, 0, 1, 4, 5, 1, 8192, 0, 6, 4, 52, 0, 0, 4, 1, 48, 0, 1, 4, 9, 0, 1, 1, 0, 1, 26, 0], "resultId": "1"}]],
+ legend = [[{
+ "tokenTypes": [
+ "comment", "keyword", "string", "number", "regexp", "operator", "namespace", "type", "struct", "class", "interface", "enum", "enumMember", "typeParameter", "function", "method", "property", "macro", "variable",
+ "parameter", "angle", "arithmetic", "attribute", "attributeBracket", "bitwise", "boolean", "brace", "bracket", "builtinAttribute", "builtinType", "character", "colon", "comma", "comparison", "constParameter", "derive",
+ "dot", "escapeSequence", "formatSpecifier", "generic", "label", "lifetime", "logical", "macroBang", "operator", "parenthesis", "punctuation", "selfKeyword", "semicolon", "typeAlias", "toolModule", "union", "unresolvedReference"
+ ],
+ "tokenModifiers": [
+ "documentation", "declaration", "definition", "static", "abstract", "deprecated", "readonly", "defaultLibrary", "async", "attribute", "callable", "constant", "consuming", "controlFlow", "crateRoot", "injected", "intraDocLink",
+ "library", "mutable", "public", "reference", "trait", "unsafe"
+ ]
+ }]],
+ expected = {
+ {
+ line = 0,
+ modifiers = {},
+ start_col = 0,
+ end_col = 3, -- pub
+ type = 'keyword',
+ extmark_added = true,
+ },
+ {
+ line = 0,
+ modifiers = {},
+ start_col = 4,
+ end_col = 6, -- fn
+ type = 'keyword',
+ extmark_added = true,
+ },
+ {
+ line = 0,
+ modifiers = { 'declaration', 'public' },
+ start_col = 7,
+ end_col = 11, -- main
+ type = 'function',
+ extmark_added = true,
+ },
+ {
+ line = 0,
+ modifiers = {},
+ start_col = 11,
+ end_col = 12,
+ type = 'parenthesis',
+ extmark_added = true,
+ },
+ {
+ line = 0,
+ modifiers = {},
+ start_col = 12,
+ end_col = 13,
+ type = 'parenthesis',
+ extmark_added = true,
+ },
+ {
+ line = 0,
+ modifiers = {},
+ start_col = 14,
+ end_col = 15,
+ type = 'brace',
+ extmark_added = true,
+ },
+ {
+ line = 1,
+ modifiers = { 'controlFlow' },
+ start_col = 4,
+ end_col = 9, -- break
+ type = 'keyword',
+ extmark_added = true,
+ },
+ {
+ line = 1,
+ modifiers = {},
+ start_col = 10,
+ end_col = 13, -- rust
+ type = 'unresolvedReference',
+ extmark_added = true,
+ },
+ {
+ line = 1,
+ modifiers = {},
+ start_col = 13,
+ end_col = 13,
+ type = 'semicolon',
+ extmark_added = true,
+ },
+ {
+ line = 2,
+ modifiers = { 'documentation' },
+ start_col = 4,
+ end_col = 11,
+ type = 'comment', -- /// what?
+ extmark_added = true,
+ },
+ {
+ line = 3,
+ modifiers = {},
+ start_col = 0,
+ end_col = 1,
+ type = 'brace',
+ extmark_added = true,
+ },
+ },
+ },
+ }) do
+ it(test.it, function()
+ exec_lua(create_server_definition)
+ exec_lua([[
+ local legend, resp = ...
+ server = _create_server({
+ capabilities = {
+ semanticTokensProvider = {
+ full = { delta = false },
+ legend = vim.fn.json_decode(legend),
+ },
+ },
+ handlers = {
+ ['textDocument/semanticTokens/full'] = function()
+ return vim.fn.json_decode(resp)
+ end,
+ }
+ })
+ bufnr = vim.api.nvim_get_current_buf()
+ vim.api.nvim_win_set_buf(0, bufnr)
+ client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
+ ]], test.legend, test.response)
+
+ insert(test.text)
+
+ local highlights = exec_lua([[
+ local semantic_tokens = vim.lsp.semantic_tokens
+ return semantic_tokens.__STHighlighter.active[bufnr].client_state[client_id].current_result.highlights
+ ]])
+ eq(test.expected, highlights)
+ end)
+ end
+ end)
+
+ describe('token decoding with deltas', function()
+ for _, test in ipairs({
+ {
+ it = 'semantic_tokens_delta: clangd-15 on C',
+ legend = [[{
+ "tokenTypes": [
+ "variable", "variable", "parameter", "function", "method", "function", "property", "variable", "class", "interface", "enum", "enumMember", "type", "type", "unknown", "namespace", "typeParameter", "concept", "type", "macro", "comment"
+ ],
+ "tokenModifiers": [
+ "declaration", "deprecated", "deduced", "readonly", "static", "abstract", "virtual", "dependentName", "defaultLibrary", "usedAsMutableReference", "functionScope", "classScope", "fileScope", "globalScope"
+ ]
+ }]],
+ text1 = [[char* foo = "\n";]],
+ edit = [[ggO<Esc>]],
+ response1 = [[{"data": [0, 6, 3, 0, 8193], "resultId": "1"}]],
+ response2 = [[{"edits": [{ "start": 0, "deleteCount": 1, "data": [1] }], "resultId": "2"}]],
+ expected1 = {
+ {
+ line = 0,
+ modifiers = {
+ 'declaration',
+ 'globalScope',
+ },
+ start_col = 6,
+ end_col = 9,
+ type = 'variable',
+ extmark_added = true,
+ }
+ },
+ expected2 = {
+ {
+ line = 1,
+ modifiers = {
+ 'declaration',
+ 'globalScope',
+ },
+ start_col = 6,
+ end_col = 9,
+ type = 'variable',
+ extmark_added = true,
+ }
+ },
+ },
+ {
+ it = 'response with multiple delta edits',
+ legend = [[{
+ "tokenTypes": [
+ "variable", "variable", "parameter", "function", "method", "function", "property", "variable", "class", "interface", "enum", "enumMember", "type", "type", "unknown", "namespace", "typeParameter", "concept", "type", "macro", "comment"
+ ],
+ "tokenModifiers": [
+ "declaration", "deprecated", "deduced", "readonly", "static", "abstract", "virtual", "dependentName", "defaultLibrary", "usedAsMutableReference", "functionScope", "classScope", "fileScope", "globalScope"
+ ]
+ }]],
+ text1 = dedent([[
+ #include <iostream>
+
+ int main()
+ {
+ int x;
+ #ifdef __cplusplus
+ std::cout << x << "\n";
+ #else
+ printf("%d\n", x);
+ #endif
+ }]]),
+ text2 = [[#include <iostream>
+
+int main()
+{
+ int x();
+ double y;
+#ifdef __cplusplus
+ std::cout << x << "\n";
+#else
+ printf("%d\n", x);
+#endif
+}]],
+ response1 = [[{
+ "data": [ 2, 4, 4, 3, 8193, 2, 8, 1, 1, 1025, 1, 7, 11, 19, 8192, 1, 4, 3, 15, 8448, 0, 5, 4, 0, 8448, 0, 8, 1, 1, 1024, 1, 0, 5, 20, 0, 1, 0, 22, 20, 0, 1, 0, 6, 20, 0 ],
+ "resultId": 1
+ }]],
+ response2 = [[{
+ "edits": [ {"data": [ 2, 8, 1, 3, 8193, 1, 11, 1, 1, 1025 ], "deleteCount": 5, "start": 5}, {"data": [ 0, 8, 1, 3, 8192 ], "deleteCount": 5, "start": 25 } ],
+ "resultId":"2"
+ }]],
+ expected1 = {
+ {
+ line = 2,
+ start_col = 4,
+ end_col = 8,
+ modifiers = { 'declaration', 'globalScope' },
+ type = 'function',
+ extmark_added = true,
+ },
+ {
+ line = 4,
+ start_col = 8,
+ end_col = 9,
+ modifiers = { 'declaration', 'functionScope' },
+ type = 'variable',
+ extmark_added = true,
+ },
+ {
+ line = 5,
+ start_col = 7,
+ end_col = 18,
+ modifiers = { 'globalScope' },
+ type = 'macro',
+ extmark_added = true,
+ },
+ {
+ line = 6,
+ start_col = 4,
+ end_col = 7,
+ modifiers = { 'defaultLibrary', 'globalScope' },
+ type = 'namespace',
+ extmark_added = true,
+ },
+ {
+ line = 6,
+ start_col = 9,
+ end_col = 13,
+ modifiers = { 'defaultLibrary', 'globalScope' },
+ type = 'variable',
+ extmark_added = true,
+ },
+ {
+ line = 6,
+ start_col = 17,
+ end_col = 18,
+ extmark_added = true,
+ modifiers = { 'functionScope' },
+ type = 'variable',
+ },
+ {
+ line = 7,
+ start_col = 0,
+ end_col = 5,
+ extmark_added = true,
+ modifiers = {},
+ type = 'comment',
+ },
+ {
+ line = 8,
+ end_col = 22,
+ modifiers = {},
+ start_col = 0,
+ type = 'comment',
+ extmark_added = true,
+ },
+ {
+ line = 9,
+ start_col = 0,
+ end_col = 6,
+ modifiers = {},
+ type = 'comment',
+ extmark_added = true,
+ }
+ },
+ expected2 = {
+ {
+ line = 2,
+ start_col = 4,
+ end_col = 8,
+ modifiers = { 'declaration', 'globalScope' },
+ type = 'function',
+ extmark_added = true,
+ },
+ {
+ line = 4,
+ start_col = 8,
+ end_col = 9,
+ modifiers = { 'declaration', 'globalScope' },
+ type = 'function',
+ extmark_added = true,
+ },
+ {
+ line = 5,
+ end_col = 12,
+ start_col = 11,
+ modifiers = { 'declaration', 'functionScope' },
+ type = 'variable',
+ extmark_added = true,
+ },
+ {
+ line = 6,
+ start_col = 7,
+ end_col = 18,
+ modifiers = { 'globalScope' },
+ type = 'macro',
+ extmark_added = true,
+ },
+ {
+ line = 7,
+ start_col = 4,
+ end_col = 7,
+ modifiers = { 'defaultLibrary', 'globalScope' },
+ type = 'namespace',
+ extmark_added = true,
+ },
+ {
+ line = 7,
+ start_col = 9,
+ end_col = 13,
+ modifiers = { 'defaultLibrary', 'globalScope' },
+ type = 'variable',
+ extmark_added = true,
+ },
+ {
+ line = 7,
+ start_col = 17,
+ end_col = 18,
+ extmark_added = true,
+ modifiers = { 'globalScope' },
+ type = 'function',
+ },
+ {
+ line = 8,
+ start_col = 0,
+ end_col = 5,
+ extmark_added = true,
+ modifiers = {},
+ type = 'comment',
+ },
+ {
+ line = 9,
+ end_col = 22,
+ modifiers = {},
+ start_col = 0,
+ type = 'comment',
+ extmark_added = true,
+ },
+ {
+ line = 10,
+ start_col = 0,
+ end_col = 6,
+ modifiers = {},
+ type = 'comment',
+ extmark_added = true,
+ }
+ },
+ },
+ {
+ it = 'optional token_edit.data on deletion',
+ legend = [[{
+ "tokenTypes": [
+ "comment", "keyword", "operator", "string", "number", "regexp", "type", "class", "interface", "enum", "enumMember", "typeParameter", "function", "method", "property", "variable", "parameter", "module", "intrinsic", "selfParameter", "clsParameter", "magicFunction", "builtinConstant", "parenthesis", "curlybrace", "bracket", "colon", "semicolon", "arrow"
+ ],
+ "tokenModifiers": [
+ "declaration", "static", "abstract", "async", "documentation", "typeHint", "typeHintComment", "readonly", "decorator", "builtin"
+ ]
+ }]],
+ text1 = [[string = "test"]],
+ text2 = [[]],
+ response1 = [[{"data": [0, 0, 6, 15, 1], "resultId": "1"}]],
+ response2 = [[{"edits": [{ "start": 0, "deleteCount": 5 }], "resultId": "2"}]],
+ expected1 = {
+ {
+ line = 0,
+ modifiers = {
+ 'declaration',
+ },
+ start_col = 0,
+ end_col = 6,
+ type = 'variable',
+ extmark_added = true,
+ }
+ },
+ expected2 = {
+ },
+ },
+ }) do
+ it(test.it, function()
+ exec_lua(create_server_definition)
+ exec_lua([[
+ local legend, resp1, resp2 = ...
+ server = _create_server({
+ capabilities = {
+ semanticTokensProvider = {
+ full = { delta = true },
+ legend = vim.fn.json_decode(legend),
+ },
+ },
+ handlers = {
+ ['textDocument/semanticTokens/full'] = function()
+ return vim.fn.json_decode(resp1)
+ end,
+ ['textDocument/semanticTokens/full/delta'] = function()
+ return vim.fn.json_decode(resp2)
+ end,
+ }
+ })
+ bufnr = vim.api.nvim_get_current_buf()
+ vim.api.nvim_win_set_buf(0, bufnr)
+ client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
+
+ -- speed up vim.api.nvim_buf_set_lines calls by changing debounce to 10 for these tests
+ semantic_tokens = vim.lsp.semantic_tokens
+ vim.schedule(function()
+ semantic_tokens.stop(bufnr, client_id)
+ semantic_tokens.start(bufnr, client_id, { debounce = 10 })
+ end)
+ ]], test.legend, test.response1, test.response2)
+
+ insert(test.text1)
+
+ local highlights = exec_lua([[
+ return semantic_tokens.__STHighlighter.active[bufnr].client_state[client_id].current_result.highlights
+ ]])
+
+ eq(test.expected1, highlights)
+
+ if test.edit then
+ feed(test.edit)
+ else
+ exec_lua([[
+ local text = ...
+ vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, vim.fn.split(text, "\n"))
+ vim.wait(15) -- wait fot debounce
+ ]], test.text2)
+ end
+
+ highlights = exec_lua([[
+ return semantic_tokens.__STHighlighter.active[bufnr].client_state[client_id].current_result.highlights
+ ]])
+
+ eq(test.expected2, highlights)
+ end)
+ end
+ end)
+end)
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
index cd7415de90..5229022564 100644
--- a/test/functional/plugin/lsp_spec.lua
+++ b/test/functional/plugin/lsp_spec.lua
@@ -1,8 +1,9 @@
local helpers = require('test.functional.helpers')(after_each)
+local lsp_helpers = require('test.functional.plugin.lsp.helpers')
local assert_log = helpers.assert_log
-local clear = helpers.clear
local buf_lines = helpers.buf_lines
+local clear = helpers.clear
local command = helpers.command
local dedent = helpers.dedent
local exec_lua = helpers.exec_lua
@@ -14,133 +15,28 @@ local pesc = helpers.pesc
local insert = helpers.insert
local funcs = helpers.funcs
local retry = helpers.retry
+local stop = helpers.stop
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 is_ci = helpers.is_ci
local meths = helpers.meths
+local is_os = helpers.is_os
+local skip = helpers.skip
--- Use these to get access to a coroutine so that I can run async tests and use
--- yield.
-local run, stop = helpers.run, helpers.stop
+local clear_notrace = lsp_helpers.clear_notrace
+local create_server_definition = lsp_helpers.create_server_definition
+local fake_lsp_code = lsp_helpers.fake_lsp_code
+local fake_lsp_logfile = lsp_helpers.fake_lsp_logfile
+local test_rpc_server = lsp_helpers.test_rpc_server
-- TODO(justinmk): hangs on Windows https://github.com/neovim/neovim/pull/11837
-if helpers.pending_win32(pending) then return end
-
--- Fake LSP server.
-local fake_lsp_code = 'test/functional/fixtures/fake-lsp-server.lua'
-local fake_lsp_logfile = 'Xtest-fake-lsp.log'
+if skip(is_os('win')) then return end
teardown(function()
os.remove(fake_lsp_logfile)
end)
-local function clear_notrace()
- -- problem: here be dragons
- -- solution: don't look for dragons to closely
- clear {env={
- NVIM_LUA_NOTRACK="1";
- VIMRUNTIME=os.getenv"VIMRUNTIME";
- }}
-end
-
-
-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, settings = ...
- TEST_RPC_CLIENT_ID = lsp.start_client {
- cmd_env = {
- NVIM_LOG_FILE = logfile;
- NVIM_LUA_NOTRACK = "1";
- };
- cmd = {
- vim.v.progpath, '-Es', '-u', 'NONE', '--headless',
- "-c", string.format("lua TEST_NAME = %q", test_name),
- "-c", string.format("lua TIMEOUT = %d", timeout),
- "-c", "luafile "..fixture_filename,
- };
- handlers = setmetatable({}, {
- __index = function(t, method)
- return function(...)
- return vim.rpcrequest(1, 'handler', ...)
- end
- end;
- });
- workspace_folders = {{
- uri = 'file://' .. vim.loop.cwd(),
- name = 'test_folder',
- }};
- on_init = function(client, result)
- TEST_RPC_CLIENT = client
- vim.rpcrequest(1, "init", result)
- end;
- flags = {
- 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 {}, 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, config.settings)
- end
- local client = setmetatable({}, {
- __index = function(_, name)
- -- Workaround for not being able to yield() inside __index for Lua 5.1 :(
- -- Otherwise I would just return the value here.
- return function(...)
- return exec_lua([=[
- local name = ...
- if type(TEST_RPC_CLIENT[name]) == 'function' then
- return TEST_RPC_CLIENT[name](select(2, ...))
- else
- return TEST_RPC_CLIENT[name]
- end
- ]=], name, ...)
- end
- end;
- })
- local code, signal
- local function on_request(method, args)
- if method == "init" then
- if config.on_init then
- config.on_init(client, unpack(args))
- end
- return NIL
- end
- if method == 'handler' then
- if config.on_handler then
- config.on_handler(unpack(args))
- end
- end
- return NIL
- end
- local function on_notify(method, args)
- if method == 'exit' then
- code, signal = unpack(args)
- return stop()
- end
- end
- -- TODO specify timeout?
- -- run(on_request, on_notify, config.on_setup, 1000)
- run(on_request, on_notify, config.on_setup)
- if config.on_exit then
- config.on_exit(code, signal)
- end
- stop()
- if config.test_name then
- exec_lua("vim.api.nvim_exec_autocmds('VimLeavePre', { modeline = false })")
- end
-end
-
describe('LSP', function()
before_each(function()
clear_notrace()
@@ -236,9 +132,9 @@ describe('LSP', function()
end)
it('should invalid cmd argument', function()
- eq('Error executing lua: .../lsp.lua:0: cmd: expected list, got nvim',
+ eq('.../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',
+ eq('.../lsp.lua:0: cmd argument: expected string, got number',
pcall_err(_cmd_parts, {'nvim', 1}))
end)
end)
@@ -316,7 +212,7 @@ describe('LSP', function()
end)
it('should succeed with manual shutdown', function()
- if isCI() then
+ if is_ci() then
pending('hangs the build on CI #14028, re-enable with freeze timeout #14204')
return
elseif helpers.skip_fragile(pending) then
@@ -418,6 +314,31 @@ describe('LSP', function()
}
end)
+ it('should detach buffer on bufwipe', function()
+ clear()
+ exec_lua(create_server_definition)
+ local result = exec_lua([[
+ local server = _create_server()
+ local bufnr = vim.api.nvim_create_buf(false, true)
+ vim.api.nvim_set_current_buf(bufnr)
+ local client_id = vim.lsp.start({ name = 'detach-dummy', cmd = server.cmd })
+ assert(client_id, "lsp.start must return client_id")
+ local client = vim.lsp.get_client_by_id(client_id)
+ local num_attached_before = vim.tbl_count(client.attached_buffers)
+ vim.api.nvim_buf_delete(bufnr, { force = true })
+ local num_attached_after = vim.tbl_count(client.attached_buffers)
+ return {
+ bufnr = bufnr,
+ client_id = client_id,
+ num_attached_before = num_attached_before,
+ num_attached_after = num_attached_after,
+ }
+ ]])
+ eq(true, result ~= nil, "exec_lua must return result")
+ eq(1, result.num_attached_before)
+ eq(0, result.num_attached_after)
+ end)
+
it('client should return settings via workspace/configuration handler', function()
local expected_handlers = {
{NIL, {}, {method="shutdown", client_id=1}};
@@ -535,6 +456,70 @@ describe('LSP', function()
}
end)
+ it('BufWritePre does not send notifications if server lacks willSave capabilities', function()
+ clear()
+ exec_lua(create_server_definition)
+ local messages = exec_lua([[
+ local server = _create_server({
+ capabilities = {
+ textDocumentSync = {
+ willSave = false,
+ willSaveWaitUntil = false,
+ }
+ },
+ })
+ local client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
+ local buf = vim.api.nvim_get_current_buf()
+ vim.api.nvim_exec_autocmds('BufWritePre', { buffer = buf, modeline = false })
+ vim.lsp.stop_client(client_id)
+ return server.messages
+ ]])
+ eq(#messages, 4)
+ eq(messages[1].method, 'initialize')
+ eq(messages[2].method, 'initialized')
+ eq(messages[3].method, 'shutdown')
+ eq(messages[4].method, 'exit')
+ end)
+
+ it('BufWritePre sends willSave / willSaveWaitUntil, applies textEdits', function()
+ clear()
+ exec_lua(create_server_definition)
+ local result = exec_lua([[
+ local server = _create_server({
+ capabilities = {
+ textDocumentSync = {
+ willSave = true,
+ willSaveWaitUntil = true,
+ }
+ },
+ handlers = {
+ ['textDocument/willSaveWaitUntil'] = function()
+ local text_edit = {
+ range = {
+ start = { line = 0, character = 0 },
+ ['end'] = { line = 0, character = 0 },
+ },
+ newText = 'Hello'
+ }
+ return { text_edit, }
+ end
+ },
+ })
+ local buf = vim.api.nvim_get_current_buf()
+ local client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
+ vim.api.nvim_exec_autocmds('BufWritePre', { buffer = buf, modeline = false })
+ vim.lsp.stop_client(client_id)
+ return {
+ messages = server.messages,
+ lines = vim.api.nvim_buf_get_lines(buf, 0, -1, true)
+ }
+ ]])
+ local messages = result.messages
+ eq('textDocument/willSave', messages[3].method)
+ eq('textDocument/willSaveWaitUntil', messages[4].method)
+ eq({'Hello'}, result.lines)
+ end)
+
it('saveas sends didOpen if filename changed', function()
local expected_handlers = {
{ NIL, {}, { method = 'shutdown', client_id = 1 } },
@@ -1682,6 +1667,46 @@ describe('LSP', function()
end)
end)
+ describe('apply_text_edits regression tests for #20116', function()
+ before_each(function()
+ insert(dedent([[
+ Test line one
+ Test line two 21 char]]))
+ end)
+ describe('with LSP end column out of bounds and start column at 0', function()
+ it('applies edits at the end of the buffer', function()
+ local edits = {
+ make_edit(0, 0, 1, 22, {'#include "whatever.h"\r\n#include <algorithm>\r'});
+ }
+ exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-8")
+ eq({'#include "whatever.h"', '#include <algorithm>'}, buf_lines(1))
+ end)
+ it('applies edits in the middle of the buffer', function()
+ local edits = {
+ make_edit(0, 0, 0, 22, {'#include "whatever.h"\r\n#include <algorithm>\r'});
+ }
+ exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-8")
+ eq({'#include "whatever.h"', '#include <algorithm>', 'Test line two 21 char'}, buf_lines(1))
+ end)
+ end)
+ describe('with LSP end column out of bounds and start column NOT at 0', function()
+ it('applies edits at the end of the buffer', function()
+ local edits = {
+ make_edit(0, 2, 1, 22, {'#include "whatever.h"\r\n#include <algorithm>\r'});
+ }
+ exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-8")
+ eq({'Te#include "whatever.h"', '#include <algorithm>'}, buf_lines(1))
+ end)
+ it('applies edits in the middle of the buffer', function()
+ local edits = {
+ make_edit(0, 2, 0, 22, {'#include "whatever.h"\r\n#include <algorithm>\r'});
+ }
+ exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-8")
+ eq({'Te#include "whatever.h"', '#include <algorithm>', 'Test line two 21 char'}, buf_lines(1))
+ end)
+ end)
+ end)
+
describe('apply_text_document_edit', function()
local target_bufnr
local text_document_edit = function(editVersion)
@@ -1900,6 +1925,22 @@ describe('LSP', function()
exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit, 'utf-16')
eq(true, exec_lua('return vim.loop.fs_stat(...) ~= nil', tmpfile))
end)
+ it('Supports file creation in folder that needs to be created with CreateFile payload', function()
+ local tmpfile = helpers.tmpname()
+ os.remove(tmpfile) -- Should not exist, only interested in a tmpname
+ tmpfile = tmpfile .. '/dummy/x/'
+ local uri = exec_lua('return vim.uri_from_fname(...)', tmpfile)
+ local edit = {
+ documentChanges = {
+ {
+ kind = 'create',
+ uri = uri,
+ },
+ }
+ }
+ 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()
local tmpfile = helpers.tmpname()
write_file(tmpfile, 'Dummy content')
@@ -2377,7 +2418,7 @@ describe('LSP', function()
},
uri = "file:///test_a"
},
- contanerName = "TestAContainer"
+ containerName = "TestAContainer"
},
{
deprecated = false,
@@ -2396,7 +2437,7 @@ describe('LSP', function()
},
uri = "file:///test_b"
},
- contanerName = "TestBContainer"
+ containerName = "TestBContainer"
}
}
return vim.lsp.util.symbols_to_items(sym_info, nil)
@@ -2493,7 +2534,7 @@ describe('LSP', function()
local mark = funcs.nvim_buf_get_mark(target_bufnr, "'")
eq({ 1, 0 }, mark)
- funcs.nvim_win_set_cursor(0, {2, 3})
+ funcs.nvim_win_set_cursor(0, { 2, 3 })
jump(location(0, 9, 0, 9))
mark = funcs.nvim_buf_get_mark(target_bufnr, "'")
@@ -2501,6 +2542,193 @@ describe('LSP', function()
end)
end)
+ describe('lsp.util.show_document', function()
+ local target_bufnr
+ local target_bufnr2
+
+ before_each(function()
+ target_bufnr = exec_lua([[
+ local bufnr = vim.uri_to_bufnr("file:///fake/uri")
+ local lines = {"1st line of text", "å å ɧ 汉语 ↥ 🤦 🦄"}
+ vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines)
+ return bufnr
+ ]])
+
+ target_bufnr2 = exec_lua([[
+ local bufnr = vim.uri_to_bufnr("file:///fake/uri2")
+ local lines = {"1st line of text", "å å ɧ 汉语 ↥ 🤦 🦄"}
+ vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines)
+ return bufnr
+ ]])
+ end)
+
+ local location = function(start_line, start_char, end_line, end_char, second_uri)
+ return {
+ uri = second_uri and 'file:///fake/uri2' or 'file:///fake/uri',
+ range = {
+ start = { line = start_line, character = start_char },
+ ['end'] = { line = end_line, character = end_char },
+ },
+ }
+ end
+
+ local show_document = function(msg, focus, reuse_win)
+ eq(
+ true,
+ exec_lua(
+ 'return vim.lsp.util.show_document(...)',
+ msg,
+ 'utf-16',
+ { reuse_win = reuse_win, focus = focus }
+ )
+ )
+ if focus == true or focus == nil then
+ eq(target_bufnr, exec_lua([[return vim.fn.bufnr('%')]]))
+ end
+ return {
+ line = exec_lua([[return vim.fn.line('.')]]),
+ col = exec_lua([[return vim.fn.col('.')]]),
+ }
+ end
+
+ it('jumps to a Location if focus is true', function()
+ local pos = show_document(location(0, 9, 0, 9), true, true)
+ eq(1, pos.line)
+ eq(10, pos.col)
+ end)
+
+ it('jumps to a Location if focus is true via handler', function()
+ exec_lua(create_server_definition)
+ local result = exec_lua([[
+ local server = _create_server()
+ local client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
+ local result = {
+ uri = 'file:///fake/uri',
+ selection = {
+ start = { line = 0, character = 9 },
+ ['end'] = { line = 0, character = 9 }
+ },
+ takeFocus = true,
+ }
+ local ctx = {
+ client_id = client_id,
+ method = 'window/showDocument',
+ }
+ vim.lsp.handlers['window/showDocument'](nil, result, ctx)
+ vim.lsp.stop_client(client_id)
+ return {
+ cursor = vim.api.nvim_win_get_cursor(0)
+ }
+ ]])
+ eq(1, result.cursor[1])
+ eq(9, result.cursor[2])
+ end)
+
+ it('jumps to a Location if focus not set', function()
+ local pos = show_document(location(0, 9, 0, 9), nil, true)
+ eq(1, pos.line)
+ eq(10, pos.col)
+ end)
+
+ it('does not add current position to jumplist if not focus', function()
+ funcs.nvim_win_set_buf(0, target_bufnr)
+ local mark = funcs.nvim_buf_get_mark(target_bufnr, "'")
+ eq({ 1, 0 }, mark)
+
+ funcs.nvim_win_set_cursor(0, { 2, 3 })
+ show_document(location(0, 9, 0, 9), false, true)
+ show_document(location(0, 9, 0, 9, true), false, true)
+
+ mark = funcs.nvim_buf_get_mark(target_bufnr, "'")
+ eq({ 1, 0 }, mark)
+ end)
+
+ it('does not change cursor position if not focus and not reuse_win', function()
+ funcs.nvim_win_set_buf(0, target_bufnr)
+ local cursor = funcs.nvim_win_get_cursor(0)
+
+ show_document(location(0, 9, 0, 9), false, false)
+ eq(cursor, funcs.nvim_win_get_cursor(0))
+ end)
+
+ it('does not change window if not focus', function()
+ funcs.nvim_win_set_buf(0, target_bufnr)
+ local win = funcs.nvim_get_current_win()
+
+ -- same document/bufnr
+ show_document(location(0, 9, 0, 9), false, true)
+ eq(win, funcs.nvim_get_current_win())
+
+ -- different document/bufnr, new window/split
+ show_document(location(0, 9, 0, 9, true), false, true)
+ eq(2, #funcs.nvim_list_wins())
+ eq(win, funcs.nvim_get_current_win())
+ end)
+
+ it("respects 'reuse_win' parameter", function()
+ funcs.nvim_win_set_buf(0, target_bufnr)
+
+ -- does not create a new window if the buffer is already open
+ show_document(location(0, 9, 0, 9), false, true)
+ eq(1, #funcs.nvim_list_wins())
+
+ -- creates a new window even if the buffer is already open
+ show_document(location(0, 9, 0, 9), false, false)
+ eq(2, #funcs.nvim_list_wins())
+ end)
+
+ it('correctly sets the cursor of the split if range is given without focus', function()
+ funcs.nvim_win_set_buf(0, target_bufnr)
+
+ show_document(location(0, 9, 0, 9, true), false, true)
+
+ local wins = funcs.nvim_list_wins()
+ eq(2, #wins)
+ table.sort(wins)
+
+ eq({ 1, 0 }, funcs.nvim_win_get_cursor(wins[1]))
+ eq({ 1, 9 }, funcs.nvim_win_get_cursor(wins[2]))
+ end)
+
+ it('does not change cursor of the split if not range and not focus', function()
+ funcs.nvim_win_set_buf(0, target_bufnr)
+ funcs.nvim_win_set_cursor(0, { 2, 3 })
+
+ exec_lua([[vim.cmd.new()]])
+ funcs.nvim_win_set_buf(0, target_bufnr2)
+ funcs.nvim_win_set_cursor(0, { 2, 3 })
+
+ show_document({ uri = 'file:///fake/uri2' }, false, true)
+
+ local wins = funcs.nvim_list_wins()
+ eq(2, #wins)
+ eq({ 2, 3 }, funcs.nvim_win_get_cursor(wins[1]))
+ eq({ 2, 3 }, funcs.nvim_win_get_cursor(wins[2]))
+ end)
+
+ it('respects existing buffers', function()
+ funcs.nvim_win_set_buf(0, target_bufnr)
+ local win = funcs.nvim_get_current_win()
+
+ exec_lua([[vim.cmd.new()]])
+ funcs.nvim_win_set_buf(0, target_bufnr2)
+ funcs.nvim_win_set_cursor(0, { 2, 3 })
+ local split = funcs.nvim_get_current_win()
+
+ -- reuse win for open document/bufnr if called from split
+ show_document(location(0, 9, 0, 9, true), false, true)
+ eq({ 1, 9 }, funcs.nvim_win_get_cursor(split))
+ eq(2, #funcs.nvim_list_wins())
+
+ funcs.nvim_set_current_win(win)
+
+ -- reuse win for open document/bufnr if called outside the split
+ show_document(location(0, 9, 0, 9, true), false, true)
+ eq({ 1, 9 }, funcs.nvim_win_get_cursor(split))
+ eq(2, #funcs.nvim_list_wins())
+ end)
+ end)
+
describe('lsp.util._make_floating_popup_size', function()
before_each(function()
exec_lua [[ contents =
@@ -3180,5 +3408,159 @@ describe('LSP', function()
end,
}
end)
+ it('format formats range in visual mode', function()
+ exec_lua(create_server_definition)
+ local result = exec_lua([[
+ local server = _create_server({ capabilities = {
+ documentFormattingProvider = true,
+ documentRangeFormattingProvider = true,
+ }})
+ local bufnr = vim.api.nvim_get_current_buf()
+ local client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
+ vim.api.nvim_win_set_buf(0, bufnr)
+ vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, {'foo', 'bar'})
+ vim.api.nvim_win_set_cursor(0, { 1, 0 })
+ vim.cmd.normal('v')
+ vim.api.nvim_win_set_cursor(0, { 2, 3 })
+ vim.lsp.buf.format({ bufnr = bufnr, false })
+ return server.messages
+ ]])
+ eq("textDocument/rangeFormatting", result[3].method)
+ local expected_range = {
+ start = { line = 0, character = 0 },
+ ['end'] = { line = 1, character = 4 },
+ }
+ eq(expected_range, result[3].params.range)
+ end)
+ it('Aborts with notify if no clients support requested method', function()
+ exec_lua(create_server_definition)
+ exec_lua([[
+ vim.notify = function(msg, _)
+ notify_msg = msg
+ end
+ ]])
+ local fail_msg = "[LSP] Format request failed, no matching language servers."
+ local function check_notify(name, formatting, range_formatting)
+ local timeout_msg = "[LSP][" .. name .. "] timeout"
+ exec_lua([[
+ local formatting, range_formatting, name = ...
+ local server = _create_server({ capabilities = {
+ documentFormattingProvider = formatting,
+ documentRangeFormattingProvider = range_formatting,
+ }})
+ vim.lsp.start({ name = name, cmd = server.cmd })
+ notify_msg = nil
+ vim.lsp.buf.format({ name = name, timeout_ms = 1 })
+ ]], formatting, range_formatting, name)
+ eq(formatting and timeout_msg or fail_msg, exec_lua('return notify_msg'))
+ exec_lua([[
+ notify_msg = nil
+ vim.lsp.buf.format({ name = name, timeout_ms = 1, range = {start={1, 0}, ['end']={1, 0}}})
+ ]])
+ eq(range_formatting and timeout_msg or fail_msg, exec_lua('return notify_msg'))
+ end
+ check_notify("none", false, false)
+ check_notify("formatting", true, false)
+ check_notify("rangeFormatting", false, true)
+ check_notify("both", true, true)
+ end)
+ end)
+ describe('cmd', function()
+ it('can connect to lsp server via rpc.connect', function()
+ local result = exec_lua [[
+ local uv = vim.loop
+ local server = uv.new_tcp()
+ local init = nil
+ server:bind('127.0.0.1', 0)
+ server:listen(127, function(err)
+ assert(not err, err)
+ local socket = uv.new_tcp()
+ server:accept(socket)
+ socket:read_start(require('vim.lsp.rpc').create_read_loop(function(body)
+ init = body
+ socket:close()
+ end))
+ end)
+ local port = server:getsockname().port
+ vim.lsp.start({ name = 'dummy', cmd = vim.lsp.rpc.connect('127.0.0.1', port) })
+ vim.wait(1000, function() return init ~= nil end)
+ assert(init, "server must receive `initialize` request")
+ server:close()
+ server:shutdown()
+ return vim.json.decode(init)
+ ]]
+ eq(result.method, "initialize")
+ end)
+ end)
+
+ describe('handlers', function()
+ it('handler can return false as response', function()
+ local result = exec_lua [[
+ local uv = vim.loop
+ local server = uv.new_tcp()
+ local messages = {}
+ local responses = {}
+ server:bind('127.0.0.1', 0)
+ server:listen(127, function(err)
+ assert(not err, err)
+ local socket = uv.new_tcp()
+ server:accept(socket)
+ socket:read_start(require('vim.lsp.rpc').create_read_loop(function(body)
+ local payload = vim.json.decode(body)
+ if payload.method then
+ table.insert(messages, payload.method)
+ if payload.method == 'initialize' then
+ local msg = vim.json.encode({
+ id = payload.id,
+ jsonrpc = '2.0',
+ result = {
+ capabilities = {}
+ },
+ })
+ socket:write(table.concat({'Content-Length: ', tostring(#msg), '\r\n\r\n', msg}))
+ elseif payload.method == 'initialized' then
+ local msg = vim.json.encode({
+ id = 10,
+ jsonrpc = '2.0',
+ method = 'dummy',
+ params = {},
+ })
+ socket:write(table.concat({'Content-Length: ', tostring(#msg), '\r\n\r\n', msg}))
+ end
+ else
+ table.insert(responses, payload)
+ socket:close()
+ end
+ end))
+ end)
+ local port = server:getsockname().port
+ local handler_called = false
+ vim.lsp.handlers['dummy'] = function(err, result)
+ handler_called = true
+ return false
+ end
+ local client_id = vim.lsp.start({ name = 'dummy', cmd = vim.lsp.rpc.connect('127.0.0.1', port) })
+ local client = vim.lsp.get_client_by_id(client_id)
+ vim.wait(1000, function() return #messages == 2 and handler_called and #responses == 1 end)
+ server:close()
+ server:shutdown()
+ return {
+ messages = messages,
+ handler_called = handler_called,
+ responses = responses }
+ ]]
+ local expected = {
+ messages = { 'initialize', 'initialized' },
+ handler_called = true,
+ responses = {
+ {
+ id = 10,
+ jsonrpc = '2.0',
+ result = false
+ }
+ }
+ }
+ eq(expected, result)
+ end)
end)
end)
diff --git a/test/functional/plugin/man_spec.lua b/test/functional/plugin/man_spec.lua
index c8da5a711f..c6c7d2b03d 100644
--- a/test/functional/plugin/man_spec.lua
+++ b/test/functional/plugin/man_spec.lua
@@ -1,10 +1,21 @@
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 command, rawfeed = helpers.command, helpers.rawfeed
local clear = helpers.clear
+local exec_lua = helpers.exec_lua
local funcs = helpers.funcs
local nvim_prog = helpers.nvim_prog
local matches = helpers.matches
+local write_file = helpers.write_file
+local tmpname = helpers.tmpname
+local skip = helpers.skip
+local is_ci = helpers.is_ci
+
+clear()
+if funcs.executable('man') == 0 then
+ pending('missing "man" command', function() end)
+ return
+end
describe(':Man', function()
before_each(function()
@@ -44,7 +55,7 @@ describe(':Man', function()
|
]]}
- eval('man#init_pager()')
+ exec_lua[[require'man'.init_pager()]]
screen:expect([[
^this {b:is} {b:a} test |
@@ -68,7 +79,7 @@ describe(':Man', function()
|
]=]}
- eval('man#init_pager()')
+ exec_lua[[require'man'.init_pager()]]
screen:expect([[
^this {b:is }{bi:a }{biu:test} |
@@ -83,7 +94,7 @@ describe(':Man', function()
rawfeed([[
ithis i<C-v><C-h>is<C-v><C-h>s あ<C-v><C-h>あ test
with _<C-v><C-h>ö_<C-v><C-h>v_<C-v><C-h>e_<C-v><C-h>r_<C-v><C-h>s_<C-v><C-h>t_<C-v><C-h>r_<C-v><C-h>u_<C-v><C-h>̃_<C-v><C-h>c_<C-v><C-h>k te<C-v><ESC>[3mxt¶<C-v><ESC>[0m<ESC>]])
- eval('man#init_pager()')
+ exec_lua[[require'man'.init_pager()]]
screen:expect([[
^this {b:is} {b:あ} test |
@@ -99,7 +110,7 @@ describe(':Man', function()
i_<C-v><C-h>_b<C-v><C-h>be<C-v><C-h>eg<C-v><C-h>gi<C-v><C-h>in<C-v><C-h>ns<C-v><C-h>s
m<C-v><C-h>mi<C-v><C-h>id<C-v><C-h>d_<C-v><C-h>_d<C-v><C-h>dl<C-v><C-h>le<C-v><C-h>e
_<C-v><C-h>m_<C-v><C-h>i_<C-v><C-h>d_<C-v><C-h>__<C-v><C-h>d_<C-v><C-h>l_<C-v><C-h>e<ESC>]])
- eval('man#init_pager()')
+ exec_lua[[require'man'.init_pager()]]
screen:expect([[
{b:^_begins} |
@@ -115,7 +126,7 @@ describe(':Man', function()
i· ·<C-v><C-h>·
+<C-v><C-h>o
+<C-v><C-h>+<C-v><C-h>o<C-v><C-h>o double<ESC>]])
- eval('man#init_pager()')
+ exec_lua[[require'man'.init_pager()]]
screen:expect([[
^· {b:·} |
@@ -132,7 +143,7 @@ describe(':Man', function()
<C-v><C-[>[44m 4 <C-v><C-[>[45m 5 <C-v><C-[>[46m 6 <C-v><C-[>[47m 7 <C-v><C-[>[100m 8 <C-v><C-[>[101m 9
<C-v><C-[>[102m 10 <C-v><C-[>[103m 11 <C-v><C-[>[104m 12 <C-v><C-[>[105m 13 <C-v><C-[>[106m 14 <C-v><C-[>[107m 15
<C-v><C-[>[48:5:16m 16 <ESC>]])
- eval('man#init_pager()')
+ exec_lua[[require'man'.init_pager()]]
screen:expect([[
^ 0 1 2 3 |
@@ -149,4 +160,17 @@ describe(':Man', function()
local args = {nvim_prog, '--headless', '+autocmd VimLeave * echo "quit works!!"', '+Man!', '+call nvim_input("q")'}
matches('quit works!!', funcs.system(args, {'manpage contents'}))
end)
+
+ it('reports non-existent man pages for absolute paths', function()
+ skip(is_ci('cirrus'))
+ local actual_file = tmpname()
+ -- actual_file must be an absolute path to an existent file for us to test against it
+ matches('^/.+', actual_file)
+ write_file(actual_file, '')
+ local args = {nvim_prog, '--headless', '+:Man ' .. actual_file, '+q'}
+ matches(('Error detected while processing command line:\r\n' ..
+ 'man.lua: "no manual entry for %s"'):format(actual_file),
+ funcs.system(args, {''}))
+ os.remove(actual_file)
+ end)
end)
diff --git a/test/functional/plugin/shada_spec.lua b/test/functional/plugin/shada_spec.lua
index 6f22f865e6..93cf6d2b77 100644
--- a/test/functional/plugin/shada_spec.lua
+++ b/test/functional/plugin/shada_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 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,
@@ -2139,7 +2140,7 @@ end)
describe('plugin/shada.vim', function()
local epoch = os.date('%Y-%m-%dT%H:%M:%S', 0)
- local eol = helpers.iswin() and '\r\n' or '\n'
+ local eol = helpers.is_os('win') and '\r\n' or '\n'
before_each(function()
-- Note: reset() is called explicitly in each test.
os.remove(fname)
@@ -2538,13 +2539,26 @@ describe('ftplugin/shada.vim', function()
end)
describe('syntax/shada.vim', function()
- local epoch = os.date('%Y-%m-%dT%H:%M:%S', 0)
+ local epoch = os.date('!%Y-%m-%dT%H:%M:%S', 0)
before_each(reset)
it('works', function()
nvim_command('syntax on')
nvim_command('setlocal syntax=shada')
nvim_command('set laststatus&')
+ local screen = Screen.new(60, 37)
+ screen:set_default_attr_ids {
+ [1] = {bold = true, foreground = Screen.colors.Brown};
+ [2] = {foreground = tonumber('0x6a0dad')};
+ [3] = {foreground = Screen.colors.Fuchsia};
+ [4] = {foreground = Screen.colors.Blue1};
+ [5] = {bold = true, foreground = Screen.colors.SeaGreen4};
+ [6] = {foreground = Screen.colors.SlateBlue};
+ [7] = {bold = true, reverse = true};
+ [8] = {bold = true, foreground = Screen.colors.Blue};
+ }
+ screen:attach()
+
curbuf('set_lines', 0, 1, true, {
'Header with timestamp ' .. epoch .. ':',
' % Key Value',
@@ -2580,6 +2594,46 @@ describe('syntax/shada.vim', function()
' % Key Description________ Value',
' + se place cursor at end TRUE',
})
+ screen:expect{grid=[=[
+ {1:^Header} with timestamp 1970{1:-}01{1:-}01{1:T}00{1::}00{1::}00: |
+ {2: % Key Value} |
+ {1: +} {3:t } {1:"}{3:test}{1:"} |
+ {1:Jump} with timestamp 1970{1:-}01{1:-}01{1:T}00{1::}00{1::}00: |
+ {2: % Key________ Description Value} |
+ {1: +} {3:n } {4:name } {3:'A'} |
+ {1: +} {3:f } {4:file name } {1:["}{3:foo}{1:"]} |
+ {1: +} {3:l } {4:line number} {3:2} |
+ {1: +} {3:c } {4:column } {3:-200} |
+ {1:Register} with timestamp 1970{1:-}01{1:-}01{1:T}00{1::}00{1::}00: |
+ {2: % Key Description Value} |
+ {1: +} {3:rc } {4:contents } {1:@} |
+ {1: | -} {1:{"}{3:abcdefghijklmnopqrstuvwxyz}{1:":} {3:1.0}{1:}} |
+ {1: +} {3:rt } {4:type } {1:CHARACTERWISE} |
+ {1: +} {3:rt } {4:type } {1:LINEWISE} |
+ {1: +} {3:rt } {4:type } {1:BLOCKWISE} |
+ {1:Replacement string} with timestamp 1970{1:-}01{1:-}01{1:T}00{1::}00{1::}00: |
+ {2: @ Description__________ Value} |
+ {1: -} {4::s replacement string} {1:CMD} |
+ {1: -} {4::s replacement string} {1:SEARCH} |
+ {1: -} {4::s replacement string} {1:EXPR} |
+ {1: -} {4::s replacement string} {1:INPUT} |
+ {1: -} {4::s replacement string} {1:DEBUG} |
+ {1:Buffer list} with timestamp 1970{1:-}01{1:-}01{1:T}00{1::}00{1::}00: |
+ {4: # Expected array of maps} |
+ = {1:[{="}{3:a}{1:":} {1:+(}{5:10}{1:)"}{3:ac}{6:\0}{3:df}{6:\n}{3:gi}{6:\"}{3:tt\.}{1:",} {1:TRUE:} {1:FALSE},} {1:[NIL,} {1:+(}{5:-}|
+ {5:10}{1:)""]]} |
+ {1:Buffer list} with timestamp 1970{1:-}01{1:-}01{1:T}00{1::}00{1::}00: |
+ {2: % Key Description Value} |
+ |
+ {2: % Key Description Value} |
+ {1:Header} with timestamp 1970{1:-}01{1:-}01{1:T}00{1::}00{1::}00: |
+ {2: % Key Description________ Value} |
+ {1: +} {3:se } {4:place cursor at end} {1:TRUE} |
+ {8:~ }|
+ {7:[No Name] [+] }|
+ |
+ ]=]}
+
nvim_command([[
function GetSyntax()
let lines = []
@@ -2613,7 +2667,7 @@ describe('syntax/shada.vim', function()
year = htsnum(os.date('%Y', 0)),
month = htsnum(os.date('%m', 0)),
day = htsnum(os.date('%d', 0)),
- hour = htsnum(os.date('%H', 0)),
+ hour = htsnum(os.date('!%H', 0)),
minute = htsnum(os.date('%M', 0)),
second = htsnum(os.date('%S', 0)),
}
@@ -2768,9 +2822,8 @@ describe('syntax/shada.vim', function()
{{'ShaDaEntryArray', 'ShaDaMsgpackShaDaKeyword'}, 'INPUT'},
},
{
- {{'ShaDaEntryArrayEntryStart'}, ' - '},
- {{'ShaDaEntryArrayDescription'}, ':s replacement string '},
- {{'ShaDaMsgpackShaDaKeyword'}, 'DEBUG'},
+ as(), ad(':s replacement string '),
+ {{'ShaDaEntryArray', 'ShaDaMsgpackShaDaKeyword'}, 'DEBUG'},
},
{
hname('Buffer list'), h(' with timestamp '),
@@ -2872,10 +2925,10 @@ describe('syntax/shada.vim', function()
mlh(' % Key Description________ Value'),
},
{
- {{'ShaDaEntryMapLongEntryStart'}, ' + '},
- {{'ShaDaEntryMapLongKey'}, 'se '},
- {{'ShaDaEntryMapLongDescription'}, 'place cursor at end '},
- {{'ShaDaMsgpackKeyword'}, 'TRUE'},
+ mles(' + '),
+ mlk('se '),
+ mld('place cursor at end '),
+ {{'ShaDaEntryMapLong', 'ShaDaMsgpackKeyword'}, 'TRUE'},
},
}
eq(exp, act)
diff --git a/test/functional/preload.lua b/test/functional/preload.lua
index 74f03eaecf..6a5a2e907e 100644
--- a/test/functional/preload.lua
+++ b/test/functional/preload.lua
@@ -2,10 +2,11 @@
-- 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
+require('test.functional.ui.screen')
local busted = require("busted")
+local is_os = helpers.is_os
-if iswin() then
+if is_os('win') then
local ffi = require('ffi')
ffi.cdef[[
typedef int errno_t;
diff --git a/test/functional/provider/clipboard_spec.lua b/test/functional/provider/clipboard_spec.lua
index fbaef3ae00..2c5185a974 100644
--- a/test/functional/provider/clipboard_spec.lua
+++ b/test/functional/provider/clipboard_spec.lua
@@ -106,8 +106,53 @@ describe('clipboard', function()
basic_register_test()
end)
- it('`:redir @+>` with invalid g:clipboard shows exactly one error #7184',
- function()
+ it('using "+ in Normal mode with invalid g:clipboard always shows error', function()
+ insert('a')
+ command("let g:clipboard = 'bogus'")
+ feed('"+yl')
+ screen:expect([[
+ ^a |
+ {0:~ }|
+ {0:~ }|
+ clipboard: No provider. Try ":checkhealth" or ":h clipboard". |
+ ]])
+ feed('"+p')
+ screen:expect([[
+ a^a |
+ {0:~ }|
+ {0:~ }|
+ clipboard: No provider. Try ":checkhealth" or ":h clipboard". |
+ ]])
+ end)
+
+ it('using clipboard=unnamedplus with invalid g:clipboard shows error once', function()
+ insert('a')
+ command("let g:clipboard = 'bogus'")
+ command('set clipboard=unnamedplus')
+ feed('yl')
+ screen:expect([[
+ ^a |
+ {0:~ }|
+ {0:~ }|
+ clipboard: No provider. Try ":checkhealth" or ":h clipboard". |
+ ]])
+ feed(':<CR>')
+ screen:expect([[
+ ^a |
+ {0:~ }|
+ {0:~ }|
+ : |
+ ]])
+ feed('p')
+ screen:expect([[
+ a^a |
+ {0:~ }|
+ {0:~ }|
+ : |
+ ]])
+ end)
+
+ it('`:redir @+>` with invalid g:clipboard shows exactly one error #7184', function()
command("let g:clipboard = 'bogus'")
feed_command('redir @+> | :silent echo system("cat CONTRIBUTING.md") | redir END')
screen:expect([[
@@ -118,8 +163,7 @@ describe('clipboard', function()
]])
end)
- it('`:redir @+>|bogus_cmd|redir END` + invalid g:clipboard must not recurse #7184',
- function()
+ it('`:redir @+>|bogus_cmd|redir END` + invalid g:clipboard must not recurse #7184', function()
command("let g:clipboard = 'bogus'")
feed_command('redir @+> | bogus_cmd | redir END')
screen:expect{grid=[[
@@ -310,18 +354,18 @@ describe('clipboard (with fake clipboard.vim)', function()
insert([[
text:
first line
- secound line
+ second line
third line]])
feed('G"+dd"*dddd"+p"*pp')
expect([[
text:
third line
- secound line
+ second line
first line]])
-- linewise selection should be encoded as an extra newline
eq({{'third line', ''}, 'V'}, eval("g:test_clip['+']"))
- eq({{'secound line', ''}, 'V'}, eval("g:test_clip['*']"))
+ eq({{'second line', ''}, 'V'}, eval("g:test_clip['*']"))
end)
it('handles null bytes when pasting and in getreg', function()
@@ -477,7 +521,7 @@ describe('clipboard (with fake clipboard.vim)', function()
expect("indeed star")
end)
- it('unamed operations work even if the provider fails', function()
+ it('unnamed operations work even if the provider fails', function()
insert('the text')
feed('yy')
feed_command("let g:cliperror = 1")
@@ -511,7 +555,7 @@ describe('clipboard (with fake clipboard.vim)', function()
eq('textstar', meths.get_current_line())
end)
- it('Block paste works currectly', function()
+ it('Block paste works correctly', function()
insert([[
aabbcc
ddeeff
@@ -559,7 +603,7 @@ describe('clipboard (with fake clipboard.vim)', function()
eq({{'really unnamed', ''}, 'V'}, eval("g:test_clip['+']"))
eq({{'really unnamed', ''}, 'V'}, eval("g:test_clip['*']"))
- -- unnamedplus takes predecence when pasting
+ -- unnamedplus takes precedence when pasting
eq('+', eval('v:register'))
feed_command("let g:test_clip['+'] = ['the plus','']")
feed_command("let g:test_clip['*'] = ['the star','']")
diff --git a/test/functional/provider/perl_spec.lua b/test/functional/provider/perl_spec.lua
index aff5e36e24..ce92831f4c 100644
--- a/test/functional/provider/perl_spec.lua
+++ b/test/functional/provider/perl_spec.lua
@@ -9,6 +9,8 @@ local curbufmeths = helpers.curbufmeths
local insert = helpers.insert
local expect = helpers.expect
local feed = helpers.feed
+local is_os = helpers.is_os
+local skip = helpers.skip
do
clear()
@@ -24,7 +26,7 @@ before_each(function()
end)
describe('legacy perl provider', function()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
it('feature test', function()
eq(1, eval('has("perl")'))
@@ -68,7 +70,7 @@ describe('legacy perl provider', function()
end)
describe('perl provider', function()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
teardown(function ()
os.remove('Xtest-perl-hello.pl')
os.remove('Xtest-perl-hello-plugin.pl')
diff --git a/test/functional/shada/compatibility_spec.lua b/test/functional/shada/compatibility_spec.lua
index a5ef60d91f..fb656735dd 100644
--- a/test/functional/shada/compatibility_spec.lua
+++ b/test/functional/shada/compatibility_spec.lua
@@ -12,7 +12,7 @@ local wshada, sdrcmd, shada_fname = get_shada_rw('Xtest-functional-shada-compati
local mock_file_path = '/a/b/'
local mock_file_path2 = '/d/e/'
-if helpers.iswin() then
+if helpers.is_os('win') then
mock_file_path = 'C:/a/'
mock_file_path2 = 'C:/d/'
end
diff --git a/test/functional/shada/merging_spec.lua b/test/functional/shada/merging_spec.lua
index 2d44b0a950..da2fbbe029 100644
--- a/test/functional/shada/merging_spec.lua
+++ b/test/functional/shada/merging_spec.lua
@@ -14,7 +14,7 @@ local wshada, sdrcmd, shada_fname =
get_shada_rw('Xtest-functional-shada-merging.shada')
local mock_file_path = '/a/b/'
-if helpers.iswin() then
+if helpers.is_os('win') then
mock_file_path = 'C:/a/'
end
diff --git a/test/functional/shada/shada_spec.lua b/test/functional/shada/shada_spec.lua
index d10a2facbb..88a99d9b55 100644
--- a/test/functional/shada/shada_spec.lua
+++ b/test/functional/shada/shada_spec.lua
@@ -5,6 +5,8 @@ local meths, nvim_command, funcs, eq =
local write_file, spawn, set_session, nvim_prog, exc_exec =
helpers.write_file, helpers.spawn, helpers.set_session, helpers.nvim_prog,
helpers.exc_exec
+local is_os = helpers.is_os
+local skip = helpers.skip
local lfs = require('lfs')
local paths = require('test.cmakeconfig.paths')
@@ -238,8 +240,17 @@ describe('ShaDa support code', function()
eq('', meths.get_option('shada'))
end)
+ it('setting &shada gives proper error message on missing number', function()
+ eq([[Vim(set):E526: Missing number after <">: shada="]],
+ exc_exec([[set shada=\"]]))
+ for _, c in ipairs({"'", "/", ":", "<", "@", "s"}) do
+ eq(([[Vim(set):E526: Missing number after <%s>: shada=%s]]):format(c, c),
+ exc_exec(([[set shada=%s]]):format(c)))
+ end
+ end)
+
it('does not crash when ShaDa file directory is not writable', function()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
funcs.mkdir(dirname, '', 0)
eq(0, funcs.filewritable(dirname))
diff --git a/test/functional/terminal/altscreen_spec.lua b/test/functional/terminal/altscreen_spec.lua
index 155a156d15..cbe5e06005 100644
--- a/test/functional/terminal/altscreen_spec.lua
+++ b/test/functional/terminal/altscreen_spec.lua
@@ -6,7 +6,7 @@ local feed_data = thelpers.feed_data
local enter_altscreen = thelpers.enter_altscreen
local exit_altscreen = thelpers.exit_altscreen
-if helpers.pending_win32(pending) then return end
+if helpers.skip(helpers.is_os('win')) then return end
describe(':terminal altscreen', function()
local screen
@@ -126,13 +126,13 @@ describe(':terminal altscreen', function()
wait_removal()
feed('<c-\\><c-n>4k')
screen:expect([[
- ^line3 |
+ ^ |
|
|
rows: 4, cols: 50 |
|
]])
- eq(8, curbuf('line_count'))
+ eq(9, curbuf('line_count'))
end)
describe('and after exit', function()
@@ -142,15 +142,11 @@ describe(':terminal altscreen', function()
end)
it('restore buffer state', function()
- -- FIXME(tarruda): Note that the last line was lost after restoring the
- -- screen. This is a libvterm bug: When the main screen is restored it
- -- seems to "cut" lines that would have been left below the new visible
- -- screen.
screen:expect([[
- line4 |
line5 |
line6 |
line7 |
+ line8 |
{3:-- TERMINAL --} |
]])
end)
diff --git a/test/functional/terminal/api_spec.lua b/test/functional/terminal/api_spec.lua
index 5305b8af9c..724791343d 100644
--- a/test/functional/terminal/api_spec.lua
+++ b/test/functional/terminal/api_spec.lua
@@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each)
local child_session = require('test.functional.terminal.helpers')
local ok = helpers.ok
-if helpers.pending_win32(pending) then return end
+if helpers.skip(helpers.is_os('win')) then return end
describe('api', function()
local screen
@@ -19,6 +19,16 @@ describe('api', function()
end)
it("qa! RPC request during insert-mode", function()
+ screen:expect{grid=[[
+ {1: } |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ |
+ {3:-- TERMINAL --} |
+ ]]}
+
-- Start the socket from the child nvim.
child_session.feed_data(":echo serverstart('"..socket_name.."')\n")
@@ -67,4 +77,3 @@ describe('api', function()
socket_session1:request("nvim_command", "qa!")
end)
end)
-
diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua
index 23430a620b..9c8b983ff7 100644
--- a/test/functional/terminal/buffer_spec.lua
+++ b/test/functional/terminal/buffer_spec.lua
@@ -4,6 +4,7 @@ local assert_alive = helpers.assert_alive
local feed, clear, nvim = helpers.feed, helpers.clear, helpers.nvim
local poke_eventloop = helpers.poke_eventloop
local eval, feed_command, source = helpers.eval, helpers.feed_command, helpers.source
+local pcall_err = helpers.pcall_err
local eq, neq = helpers.eq, helpers.neq
local meths = helpers.meths
local retry = helpers.retry
@@ -14,6 +15,8 @@ local matches = helpers.matches
local exec_lua = helpers.exec_lua
local sleep = helpers.sleep
local funcs = helpers.funcs
+local is_os = helpers.is_os
+local skip = helpers.skip
describe(':terminal buffer', function()
local screen
@@ -199,7 +202,7 @@ describe(':terminal buffer', function()
-- Save the buffer number of the terminal for later testing.
local tbuf = eval('bufnr("%")')
- local exitcmd = helpers.iswin()
+ local exitcmd = helpers.is_os('win')
and "['cmd', '/c', 'exit']"
or "['sh', '-c', 'exit']"
source([[
@@ -261,7 +264,7 @@ describe(':terminal buffer', function()
end)
it('it works with set rightleft #11438', function()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
local columns = eval('&columns')
feed(string.rep('a', columns))
command('set rightleft')
@@ -339,6 +342,11 @@ describe(':terminal buffer', function()
]]}
eq('t', funcs.mode(1))
end)
+
+ it('writing to an existing file with :w fails #13549', function()
+ eq('Vim(write):E13: File exists (add ! to override)',
+ pcall_err(command, 'write test/functional/fixtures/tty-test.c'))
+ end)
end)
describe('No heap-buffer-overflow when using', function()
@@ -404,6 +412,14 @@ describe('on_lines does not emit out-of-bounds line indexes when', function()
feed_command('bdelete!')
eq('', exec_lua([[return _G.cb_error]]))
end)
+
+ it('runs TextChangedT event', function()
+ meths.set_var('called', 0)
+ command('autocmd TextChangedT * ++once let g:called = 1')
+ feed_command('terminal')
+ feed('iaa')
+ eq(1, meths.get_var('called'))
+ end)
end)
it('terminal truncates number of composing characters to 5', function()
diff --git a/test/functional/terminal/channel_spec.lua b/test/functional/terminal/channel_spec.lua
index b5f3c2bd31..2ca7cdb0a2 100644
--- a/test/functional/terminal/channel_spec.lua
+++ b/test/functional/terminal/channel_spec.lua
@@ -7,6 +7,7 @@ local command = helpers.command
local pcall_err = helpers.pcall_err
local feed = helpers.feed
local poke_eventloop = helpers.poke_eventloop
+local is_os = helpers.is_os
describe('terminal channel is closed and later released if', function()
local screen
@@ -92,3 +93,17 @@ describe('terminal channel is closed and later released if', function()
eq(chans - 1, eval('len(nvim_list_chans())'))
end)
end)
+
+it('chansend sends lines to terminal channel in proper order', function()
+ clear()
+ local screen = Screen.new(100, 20)
+ screen:attach()
+ local shells = is_os('win') and {'cmd.exe', 'pwsh.exe -nop', 'powershell.exe -nop'} or {'sh'}
+ for _, sh in ipairs(shells) do
+ command([[bdelete! | let id = termopen(']] .. sh .. [[')]])
+ command([[call chansend(id, ['echo "hello"', 'echo "world"', ''])]])
+ screen:expect{
+ any=[[echo "hello".*echo "world"]]
+ }
+ end
+end)
diff --git a/test/functional/terminal/cursor_spec.lua b/test/functional/terminal/cursor_spec.lua
index 2d1c790d2f..98ac03211a 100644
--- a/test/functional/terminal/cursor_spec.lua
+++ b/test/functional/terminal/cursor_spec.lua
@@ -9,6 +9,8 @@ local matches = helpers.matches
local feed_command = helpers.feed_command
local hide_cursor = thelpers.hide_cursor
local show_cursor = thelpers.show_cursor
+local is_os = helpers.is_os
+local skip = helpers.skip
describe(':terminal cursor', function()
local screen
@@ -88,7 +90,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
+ skip(is_os('win'))
hide_cursor()
screen:expect([[
tty ready |
@@ -361,7 +363,7 @@ describe('buffer cursor position is correct in terminal without number column',
end)
describe('in a line with single-cell composed multibyte characters and no trailing spaces,', function()
- if helpers.pending_win32(pending) then return end -- These tests fail on Windows. Encoding problem?
+ if skip(is_os('win'), "Encoding problem?") then return end
before_each(function()
setup_ex_register('µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳')
@@ -444,7 +446,7 @@ describe('buffer cursor position is correct in terminal without number column',
end)
describe('in a line with double-cell multibyte characters and no trailing spaces,', function()
- if helpers.pending_win32(pending) then return end -- These tests fail on Windows. Encoding problem?
+ skip(is_os('win'), "Encoding problem?")
before_each(function()
setup_ex_register('哦哦哦哦哦哦哦哦')
@@ -741,7 +743,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
end)
describe('in a line with single-cell composed multibyte characters and no trailing spaces,', function()
- if helpers.pending_win32(pending) then return end -- These tests fail on Windows. Encoding problem?
+ if skip(is_os('win'), "Encoding problem?") then return end
before_each(function()
setup_ex_register('µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳')
@@ -824,7 +826,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
end)
describe('in a line with double-cell multibyte characters and no trailing spaces,', function()
- if helpers.pending_win32(pending) then return end -- These tests fail on Windows. Encoding problem?
+ skip(is_os('win'), "Encoding problem?")
before_each(function()
setup_ex_register('哦哦哦哦哦哦哦哦')
diff --git a/test/functional/terminal/edit_spec.lua b/test/functional/terminal/edit_spec.lua
index aeb4b7cc2e..80287bb3d0 100644
--- a/test/functional/terminal/edit_spec.lua
+++ b/test/functional/terminal/edit_spec.lua
@@ -36,7 +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
+ if helpers.skip(helpers.is_os('win')) 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 23b69319f0..6b7e93a864 100644
--- a/test/functional/terminal/ex_terminal_spec.lua
+++ b/test/functional/terminal/ex_terminal_spec.lua
@@ -8,8 +8,10 @@ local feed_command, eval = helpers.feed_command, helpers.eval
local funcs = helpers.funcs
local retry = helpers.retry
local ok = helpers.ok
-local iswin = helpers.iswin
local command = helpers.command
+local skip = helpers.skip
+local is_os = helpers.is_os
+local is_ci = helpers.is_ci
describe(':terminal', function()
local screen
@@ -45,8 +47,8 @@ describe(':terminal', function()
end)
it("reads output buffer on terminal reporting #4151", function()
- if helpers.pending_win32(pending) then return end
- if iswin() then
+ skip(is_ci('cirrus') or is_os('win'))
+ if is_os('win') then
feed_command([[terminal powershell -NoProfile -NoLogo -Command Write-Host -NoNewline "\"$([char]27)[6n\""; Start-Sleep -Milliseconds 500 ]])
else
feed_command([[terminal printf '\e[6n'; sleep 0.5 ]])
@@ -55,7 +57,7 @@ describe(':terminal', function()
end)
it("in normal-mode :split does not move cursor", function()
- if iswin() then
+ if is_os('win') then
feed_command([[terminal for /L \\%I in (1,0,2) do ( echo foo & ping -w 100 -n 1 127.0.0.1 > nul )]])
else
feed_command([[terminal while true; do echo foo; sleep .1; done]])
@@ -142,7 +144,7 @@ describe(':terminal (with fake shell)', function()
end
it('with no argument, acts like termopen()', function()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
terminal_with_fake_shell()
retry(nil, 4 * screen.timeout, function()
screen:expect([[
@@ -166,7 +168,7 @@ describe(':terminal (with fake shell)', function()
end)
it("with no argument, but 'shell' has arguments, acts like termopen()", function()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
nvim('set_option', 'shell', testprg('shell-test')..' -t jeff')
terminal_with_fake_shell()
screen:expect([[
@@ -178,7 +180,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
+ skip(is_os('win'))
command('set shellxquote=') -- win: avoid extra quotes
terminal_with_fake_shell('echo hi')
screen:expect([[
@@ -190,7 +192,7 @@ describe(':terminal (with fake shell)', function()
end)
it("executes a given command through the shell, when 'shell' has arguments", function()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
nvim('set_option', 'shell', testprg('shell-test')..' -t jeff')
command('set shellxquote=') -- win: avoid extra quotes
terminal_with_fake_shell('echo hi')
@@ -203,7 +205,7 @@ describe(':terminal (with fake shell)', function()
end)
it('allows quotes and slashes', function()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
command('set shellxquote=') -- win: avoid extra quotes
terminal_with_fake_shell([[echo 'hello' \ "world"]])
screen:expect([[
@@ -240,7 +242,7 @@ describe(':terminal (with fake shell)', function()
end)
it('works with :find', function()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
terminal_with_fake_shell()
screen:expect([[
^ready $ |
@@ -251,7 +253,7 @@ describe(':terminal (with fake shell)', function()
eq('term://', string.match(eval('bufname("%")'), "^term://"))
feed([[<C-\><C-N>]])
feed_command([[find */shadacat.py]])
- if iswin() then
+ if is_os('win') then
eq('scripts\\shadacat.py', eval('bufname("%")'))
else
eq('scripts/shadacat.py', eval('bufname("%")'))
@@ -259,7 +261,7 @@ describe(':terminal (with fake shell)', function()
end)
it('works with gf', function()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
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 bcfd3559e6..7247361649 100644
--- a/test/functional/terminal/helpers.lua
+++ b/test/functional/terminal/helpers.lua
@@ -4,19 +4,31 @@
local helpers = require('test.functional.helpers')(nil)
local Screen = require('test.functional.ui.screen')
local testprg = helpers.testprg
+local exec_lua = helpers.exec_lua
local feed_command, nvim = helpers.feed_command, helpers.nvim
local function feed_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)')
+ if type(data) == 'table' then
+ data = table.concat(data, '\n')
+ end
+ exec_lua('vim.api.nvim_chan_send(vim.b.terminal_job_id, ...)', data)
end
local function feed_termcode(data)
- -- feed with the job API
- nvim('command', 'call jobsend(b:terminal_job_id, "\\x1b'..data..'")')
+ feed_data('\027' .. data)
+end
+
+local function make_lua_executor(session)
+ return function(code, ...)
+ local status, rv = session:request('nvim_exec_lua', code, {...})
+ if not status then
+ session:stop()
+ error(rv[2])
+ end
+ return rv
+ end
end
+
-- some helpers for controlling the terminal. the codes were taken from
-- infocmp xterm-256color which is less what libvterm understands
-- civis/cnorm
@@ -31,6 +43,8 @@ local function set_bg(num) feed_termcode('[48;5;'..num..'m') end
local function set_bold() feed_termcode('[1m') end
local function set_italic() feed_termcode('[3m') end
local function set_underline() feed_termcode('[4m') end
+local function set_underdouble() feed_termcode('[4:2m') end
+local function set_undercurl() feed_termcode('[4:3m') end
local function set_strikethrough() feed_termcode('[9m') end
local function clear_attrs() feed_termcode('[0;10m') end
-- mouse
@@ -60,7 +74,10 @@ local function screen_setup(extra_rows, command, cols, opts)
[9] = {foreground = 4},
[10] = {foreground = 121}, -- "Press ENTER" in embedded :terminal session.
[11] = {foreground = tonumber('0x00000b')},
- [12] = {reverse = true, foreground = tonumber('0x000079')},
+ [12] = {underline = true},
+ [13] = {underline = true, reverse = true},
+ [14] = {underline = true, reverse = true, bold = true},
+ [15] = {underline = true, foreground = 12},
})
screen:attach(opts or {rgb=false})
@@ -107,6 +124,7 @@ end
return {
feed_data = feed_data,
feed_termcode = feed_termcode,
+ make_lua_executor = make_lua_executor,
hide_cursor = hide_cursor,
show_cursor = show_cursor,
enter_altscreen = enter_altscreen,
@@ -116,6 +134,8 @@ return {
set_bold = set_bold,
set_italic = set_italic,
set_underline = set_underline,
+ set_underdouble = set_underdouble,
+ set_undercurl = set_undercurl,
set_strikethrough = set_strikethrough,
clear_attrs = clear_attrs,
enable_mouse = enable_mouse,
diff --git a/test/functional/terminal/highlight_spec.lua b/test/functional/terminal/highlight_spec.lua
index 28ca07d815..2ac45771d4 100644
--- a/test/functional/terminal/highlight_spec.lua
+++ b/test/functional/terminal/highlight_spec.lua
@@ -7,6 +7,8 @@ local nvim_prog_abs = helpers.nvim_prog_abs
local eq, eval = helpers.eq, helpers.eval
local funcs = helpers.funcs
local nvim_set = helpers.nvim_set
+local is_os = helpers.is_os
+local skip = helpers.skip
describe(':terminal highlight', function()
local screen
@@ -26,6 +28,8 @@ describe(':terminal highlight', function()
[9] = {foreground = 130},
[10] = {reverse = true},
[11] = {background = 11},
+ [12] = {bold = true, underdouble = true},
+ [13] = {italic = true, undercurl = true},
})
screen:attach({rgb=false})
command(("enew | call termopen(['%s'])"):format(testprg('tty-test')))
@@ -56,7 +60,7 @@ describe(':terminal highlight', function()
end)
local function pass_attrs()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
screen:expect(sub([[
tty ready |
{NUM:text}text{10: } |
@@ -71,7 +75,7 @@ describe(':terminal highlight', function()
it('will pass the corresponding attributes', pass_attrs)
it('will pass the corresponding attributes on scrollback', function()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
pass_attrs()
local lines = {}
for i = 1, 8 do
@@ -114,6 +118,14 @@ describe(':terminal highlight', function()
thelpers.set_underline()
thelpers.set_strikethrough()
end)
+ descr('bold and underdouble', 12, function()
+ thelpers.set_bold()
+ thelpers.set_underdouble()
+ end)
+ descr('italics and undercurl', 13, function()
+ thelpers.set_italic()
+ thelpers.set_undercurl()
+ end)
end)
it(':terminal highlight has lower precedence than editor #9964', function()
@@ -187,7 +199,7 @@ describe(':terminal highlight forwarding', function()
end)
it('will handle cterm and rgb attributes', function()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
thelpers.set_fg(3)
thelpers.feed_data('text')
thelpers.feed_termcode('[38:2:255:128:0m')
@@ -239,7 +251,7 @@ describe(':terminal highlight with custom palette', function()
end)
it('will use the custom color', function()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
thelpers.set_fg(3)
thelpers.feed_data('text')
thelpers.clear_attrs()
diff --git a/test/functional/terminal/mouse_spec.lua b/test/functional/terminal/mouse_spec.lua
index 6e2c851df7..50c8f5e7df 100644
--- a/test/functional/terminal/mouse_spec.lua
+++ b/test/functional/terminal/mouse_spec.lua
@@ -3,6 +3,8 @@ local thelpers = require('test.functional.terminal.helpers')
local clear, eq, eval = helpers.clear, helpers.eq, helpers.eval
local feed, nvim, command = helpers.feed, helpers.nvim, helpers.command
local feed_data = thelpers.feed_data
+local is_os = helpers.is_os
+local skip = helpers.skip
describe(':terminal mouse', function()
local screen
@@ -66,7 +68,7 @@ describe(':terminal mouse', function()
end)
it('does not leave terminal mode on left-release', function()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
feed('<LeftRelease>')
eq('t', eval('mode(1)'))
end)
@@ -87,7 +89,7 @@ describe(':terminal mouse', function()
end)
it('will forward mouse press, drag and release to the program', function()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
feed('<LeftMouse><1,2>')
screen:expect([[
line27 |
@@ -131,7 +133,7 @@ describe(':terminal mouse', function()
end)
it('will forward mouse scroll to the program', function()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
feed('<ScrollWheelUp><0,0>')
screen:expect([[
line27 |
@@ -145,7 +147,7 @@ describe(':terminal mouse', function()
end)
it('dragging and scrolling do not interfere with each other', function()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
feed('<LeftMouse><1,2>')
screen:expect([[
line27 |
@@ -199,7 +201,7 @@ describe(':terminal mouse', function()
end)
it('will forward mouse clicks to the program with the correct even if set nu', function()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
command('set number')
-- When the display area such as a number is clicked, it returns to the
-- normal mode.
@@ -230,7 +232,7 @@ describe(':terminal mouse', function()
end)
describe('with a split window and other buffer', function()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
before_each(function()
feed('<c-\\><c-n>:vsp<cr>')
screen:expect([[
@@ -287,7 +289,7 @@ describe(':terminal mouse', function()
]])
end)
- it('wont lose focus if another window is scrolled', function()
+ it("won't lose focus if another window is scrolled", function()
feed('<ScrollWheelUp><4,0><ScrollWheelUp><4,0>')
screen:expect([[
{7: 21 }line │line30 |
@@ -310,6 +312,34 @@ describe(':terminal mouse', function()
]])
end)
+ it("scrolling another window respects 'mousescroll'", function()
+ command('set mousescroll=ver:1')
+ feed('<ScrollWheelUp><4,0>')
+ screen:expect([[
+ {7: 26 }line │line30 |
+ {7: 27 }line │rows: 5, cols: 25 |
+ {7: 28 }line │rows: 5, cols: 24 |
+ {7: 29 }line │mouse enabled |
+ {7: 30 }line │{1: } |
+ ========== ========== |
+ {3:-- TERMINAL --} |
+ ]])
+ command('set mousescroll=ver:10')
+ feed('<ScrollWheelUp><4,0>')
+ screen:expect([[
+ {7: 16 }line │line30 |
+ {7: 17 }line │rows: 5, cols: 25 |
+ {7: 18 }line │rows: 5, cols: 24 |
+ {7: 19 }line │mouse enabled |
+ {7: 20 }line │{1: } |
+ ========== ========== |
+ {3:-- TERMINAL --} |
+ ]])
+ command('set mousescroll=ver:0')
+ feed('<ScrollWheelUp><4,0>')
+ screen:expect_unchanged()
+ end)
+
it('will lose focus if another window is clicked', function()
feed('<LeftMouse><5,1>')
screen:expect([[
diff --git a/test/functional/terminal/scrollback_spec.lua b/test/functional/terminal/scrollback_spec.lua
index b491cb2735..a4899c8219 100644
--- a/test/functional/terminal/scrollback_spec.lua
+++ b/test/functional/terminal/scrollback_spec.lua
@@ -3,7 +3,6 @@ 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, 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
@@ -15,6 +14,8 @@ local feed_data = thelpers.feed_data
local pcall_err = helpers.pcall_err
local exec_lua = helpers.exec_lua
local assert_alive = helpers.assert_alive
+local skip = helpers.skip
+local is_os = helpers.is_os
describe(':terminal scrollback', function()
local screen
@@ -139,7 +140,7 @@ describe(':terminal scrollback', function()
describe('and height decreased by 1', function()
- if helpers.pending_win32(pending) then return end
+ if skip(is_os('win')) then return end
local function will_hide_top_line()
feed([[<C-\><C-N>]])
screen:try_resize(screen._width - 2, screen._height - 1)
@@ -185,7 +186,7 @@ describe(':terminal scrollback', function()
-- XXX: Can't test this reliably on Windows unless the cursor is _moved_
-- by the resize. http://docs.libuv.org/en/v1.x/signal.html
-- See also: https://github.com/rprichard/winpty/issues/110
- if helpers.pending_win32(pending) then return end
+ if skip(is_os('win')) then return end
describe('and the height is decreased by 2', function()
before_each(function()
@@ -264,7 +265,7 @@ describe(':terminal scrollback', function()
-- XXX: Can't test this reliably on Windows unless the cursor is _moved_
-- by the resize. http://docs.libuv.org/en/v1.x/signal.html
-- See also: https://github.com/rprichard/winpty/issues/110
- if helpers.pending_win32(pending) then return end
+ if skip(is_os('win')) then return end
local function pop_then_push()
screen:try_resize(screen._width, screen._height + 1)
screen:expect([[
@@ -346,7 +347,7 @@ 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
+ skip(is_os('win'))
clear()
local screen = Screen.new(30, 7)
screen:attach({rgb=false})
@@ -396,21 +397,21 @@ describe("'scrollback' option", function()
it('set to 0 behaves as 1', function()
local screen
- if iswin() then
+ if is_os('win') then
screen = thelpers.screen_setup(nil, "['cmd.exe']", 30)
else
screen = thelpers.screen_setup(nil, "['sh']", 30)
end
curbufmeths.set_option('scrollback', 0)
- feed_data(('%s REP 31 line%s'):format(testprg('shell-test'), iswin() and '\r' or '\n'))
+ feed_data(('%s REP 31 line%s'):format(testprg('shell-test'), is_os('win') and '\r' or '\n'))
screen:expect{any='30: line '}
retry(nil, nil, function() expect_lines(7) end)
end)
it('deletes lines (only) if necessary', function()
local screen
- if iswin() then
+ if is_os('win') then
command([[let $PROMPT='$$']])
screen = thelpers.screen_setup(nil, "['cmd.exe']", 30)
else
@@ -423,7 +424,7 @@ describe("'scrollback' option", function()
-- Wait for prompt.
screen:expect{any='%$'}
- feed_data(('%s REP 31 line%s'):format(testprg('shell-test'), iswin() and '\r' or '\n'))
+ feed_data(('%s REP 31 line%s'):format(testprg('shell-test'), is_os('win') and '\r' or '\n'))
screen:expect{any='30: line '}
retry(nil, nil, function() expect_lines(33, 2) end)
@@ -436,8 +437,8 @@ describe("'scrollback' option", function()
-- 'scrollback' option is synchronized with the internal sb_buffer.
command('sleep 100m')
- feed_data(('%s REP 41 line%s'):format(testprg('shell-test'), iswin() and '\r' or '\n'))
- if iswin() then
+ feed_data(('%s REP 41 line%s'):format(testprg('shell-test'), is_os('win') and '\r' or '\n'))
+ if is_os('win') then
screen:expect{grid=[[
37: line |
38: line |
@@ -461,8 +462,8 @@ describe("'scrollback' option", function()
expect_lines(58)
-- Verify off-screen state
- 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)"))
+ matches((is_os('win') and '^36: line[ ]*$' or '^35: line[ ]*$'), eval("getline(line('w0') - 1)"))
+ matches((is_os('win') and '^27: line[ ]*$' or '^26: line[ ]*$'), eval("getline(line('w0') - 10)"))
end)
it('deletes extra lines immediately', function()
@@ -606,7 +607,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
+ skip(is_os('win'))
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 99f69ef556..b28728057f 100644
--- a/test/functional/terminal/tui_spec.lua
+++ b/test/functional/terminal/tui_spec.lua
@@ -5,15 +5,15 @@
-- http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Bracketed-Paste-Mode
local helpers = require('test.functional.helpers')(after_each)
-local uname = helpers.uname
local thelpers = require('test.functional.terminal.helpers')
local Screen = require('test.functional.ui.screen')
-local assert_alive = helpers.assert_alive
local eq = helpers.eq
local feed_command = helpers.feed_command
local feed_data = thelpers.feed_data
local clear = helpers.clear
local command = helpers.command
+local dedent = helpers.dedent
+local exec = helpers.exec
local testprg = helpers.testprg
local retry = helpers.retry
local nvim_prog = helpers.nvim_prog
@@ -21,16 +21,25 @@ local nvim_set = helpers.nvim_set
local ok = helpers.ok
local read_file = helpers.read_file
local funcs = helpers.funcs
+local meths = helpers.meths
+local is_ci = helpers.is_ci
+local is_os = helpers.is_os
+local new_pipename = helpers.new_pipename
+local spawn_argv = helpers.spawn_argv
+local set_session = helpers.set_session
+local feed = helpers.feed
+local eval = helpers.eval
-if helpers.pending_win32(pending) then return end
+if helpers.skip(helpers.is_os('win')) then return end
describe('TUI', function()
local screen
local child_session
+ local child_exec_lua
before_each(function()
clear()
- local child_server = helpers.new_pipename()
+ local child_server = new_pipename()
screen = thelpers.screen_setup(0,
string.format([=[["%s", "--listen", "%s", "-u", "NONE", "-i", "NONE", "--cmd", "%s laststatus=2 background=dark"]]=],
nvim_prog, child_server, nvim_set))
@@ -44,6 +53,7 @@ describe('TUI', function()
{3:-- TERMINAL --} |
]])
child_session = helpers.connect(child_server)
+ child_exec_lua = thelpers.make_lua_executor(child_session)
end)
-- Wait for mode in the child Nvim (avoid "typeahead race" #10826).
@@ -66,7 +76,16 @@ describe('TUI', function()
it('rapid resize #7572 #7628', function()
-- Need buffer rows to provoke the behavior.
- feed_data(":edit test/functional/fixtures/bigfile.txt:")
+ feed_data(":edit test/functional/fixtures/bigfile.txt\n")
+ screen:expect([[
+ {1:0}000;<control>;Cc;0;BN;;;;;N;NULL;;;; |
+ 0001;<control>;Cc;0;BN;;;;;N;START OF HEADING;;;; |
+ 0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; |
+ 0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; |
+ {5:test/functional/fixtures/bigfile.txt }|
+ :edit test/functional/fixtures/bigfile.txt |
+ {3:-- TERMINAL --} |
+ ]])
command('call jobresize(b:terminal_job_id, 58, 9)')
command('call jobresize(b:terminal_job_id, 62, 13)')
command('call jobresize(b:terminal_job_id, 100, 42)')
@@ -83,25 +102,9 @@ describe('TUI', function()
command('call jobresize(b:terminal_job_id, 1, 4)')
screen:try_resize(57, 17)
command('call jobresize(b:terminal_job_id, 57, 17)')
- 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 --} |
- ]])
+ retry(nil, nil, function()
+ eq({true, 57}, {child_session:request('nvim_win_get_width', 0)})
+ end)
end)
it('accepts resize while pager is active', function()
@@ -297,6 +300,199 @@ describe('TUI', function()
]], attrs)
end)
+ it('accepts mouse wheel events #19992', function()
+ child_session:request('nvim_command', [[
+ set number nostartofline nowrap mousescroll=hor:1,ver:1
+ call setline(1, repeat([join(range(10), '----')], 10))
+ vsplit
+ ]])
+ screen:expect([[
+ {11: 1 }{1:0}----1----2----3----4│{11: 1 }0----1----2----3----|
+ {11: 2 }0----1----2----3----4│{11: 2 }0----1----2----3----|
+ {11: 3 }0----1----2----3----4│{11: 3 }0----1----2----3----|
+ {11: 4 }0----1----2----3----4│{11: 4 }0----1----2----3----|
+ {5:[No Name] [+] }{1:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ -- <ScrollWheelDown> in active window
+ feed_data('\027[<65;8;1M')
+ screen:expect([[
+ {11: 2 }{1:0}----1----2----3----4│{11: 1 }0----1----2----3----|
+ {11: 3 }0----1----2----3----4│{11: 2 }0----1----2----3----|
+ {11: 4 }0----1----2----3----4│{11: 3 }0----1----2----3----|
+ {11: 5 }0----1----2----3----4│{11: 4 }0----1----2----3----|
+ {5:[No Name] [+] }{1:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ -- <ScrollWheelDown> in inactive window
+ feed_data('\027[<65;48;1M')
+ screen:expect([[
+ {11: 2 }{1:0}----1----2----3----4│{11: 2 }0----1----2----3----|
+ {11: 3 }0----1----2----3----4│{11: 3 }0----1----2----3----|
+ {11: 4 }0----1----2----3----4│{11: 4 }0----1----2----3----|
+ {11: 5 }0----1----2----3----4│{11: 5 }0----1----2----3----|
+ {5:[No Name] [+] }{1:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ -- <ScrollWheelRight> in active window
+ feed_data('\027[<67;8;1M')
+ screen:expect([[
+ {11: 2 }{1:-}---1----2----3----4-│{11: 2 }0----1----2----3----|
+ {11: 3 }----1----2----3----4-│{11: 3 }0----1----2----3----|
+ {11: 4 }----1----2----3----4-│{11: 4 }0----1----2----3----|
+ {11: 5 }----1----2----3----4-│{11: 5 }0----1----2----3----|
+ {5:[No Name] [+] }{1:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ -- <ScrollWheelRight> in inactive window
+ feed_data('\027[<67;48;1M')
+ screen:expect([[
+ {11: 2 }{1:-}---1----2----3----4-│{11: 2 }----1----2----3----4|
+ {11: 3 }----1----2----3----4-│{11: 3 }----1----2----3----4|
+ {11: 4 }----1----2----3----4-│{11: 4 }----1----2----3----4|
+ {11: 5 }----1----2----3----4-│{11: 5 }----1----2----3----4|
+ {5:[No Name] [+] }{1:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ -- <S-ScrollWheelDown> in active window
+ feed_data('\027[<69;8;1M')
+ screen:expect([[
+ {11: 5 }{1:-}---1----2----3----4-│{11: 2 }----1----2----3----4|
+ {11: 6 }----1----2----3----4-│{11: 3 }----1----2----3----4|
+ {11: 7 }----1----2----3----4-│{11: 4 }----1----2----3----4|
+ {11: 8 }----1----2----3----4-│{11: 5 }----1----2----3----4|
+ {5:[No Name] [+] }{1:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ -- <S-ScrollWheelDown> in inactive window
+ feed_data('\027[<69;48;1M')
+ screen:expect([[
+ {11: 5 }{1:-}---1----2----3----4-│{11: 5 }----1----2----3----4|
+ {11: 6 }----1----2----3----4-│{11: 6 }----1----2----3----4|
+ {11: 7 }----1----2----3----4-│{11: 7 }----1----2----3----4|
+ {11: 8 }----1----2----3----4-│{11: 8 }----1----2----3----4|
+ {5:[No Name] [+] }{1:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ -- <S-ScrollWheelRight> in active window
+ feed_data('\027[<71;8;1M')
+ screen:expect([[
+ {11: 5 }{1:-}---6----7----8----9 │{11: 5 }----1----2----3----4|
+ {11: 6 }----6----7----8----9 │{11: 6 }----1----2----3----4|
+ {11: 7 }----6----7----8----9 │{11: 7 }----1----2----3----4|
+ {11: 8 }----6----7----8----9 │{11: 8 }----1----2----3----4|
+ {5:[No Name] [+] }{1:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ -- <S-ScrollWheelRight> in inactive window
+ feed_data('\027[<71;48;1M')
+ screen:expect([[
+ {11: 5 }{1:-}---6----7----8----9 │{11: 5 }5----6----7----8----|
+ {11: 6 }----6----7----8----9 │{11: 6 }5----6----7----8----|
+ {11: 7 }----6----7----8----9 │{11: 7 }5----6----7----8----|
+ {11: 8 }----6----7----8----9 │{11: 8 }5----6----7----8----|
+ {5:[No Name] [+] }{1:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ -- <ScrollWheelUp> in active window
+ feed_data('\027[<64;8;1M')
+ screen:expect([[
+ {11: 4 }----6----7----8----9 │{11: 5 }5----6----7----8----|
+ {11: 5 }{1:-}---6----7----8----9 │{11: 6 }5----6----7----8----|
+ {11: 6 }----6----7----8----9 │{11: 7 }5----6----7----8----|
+ {11: 7 }----6----7----8----9 │{11: 8 }5----6----7----8----|
+ {5:[No Name] [+] }{1:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ -- <ScrollWheelUp> in inactive window
+ feed_data('\027[<64;48;1M')
+ screen:expect([[
+ {11: 4 }----6----7----8----9 │{11: 4 }5----6----7----8----|
+ {11: 5 }{1:-}---6----7----8----9 │{11: 5 }5----6----7----8----|
+ {11: 6 }----6----7----8----9 │{11: 6 }5----6----7----8----|
+ {11: 7 }----6----7----8----9 │{11: 7 }5----6----7----8----|
+ {5:[No Name] [+] }{1:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ -- <ScrollWheelLeft> in active window
+ feed_data('\027[<66;8;1M')
+ screen:expect([[
+ {11: 4 }5----6----7----8----9│{11: 4 }5----6----7----8----|
+ {11: 5 }5{1:-}---6----7----8----9│{11: 5 }5----6----7----8----|
+ {11: 6 }5----6----7----8----9│{11: 6 }5----6----7----8----|
+ {11: 7 }5----6----7----8----9│{11: 7 }5----6----7----8----|
+ {5:[No Name] [+] }{1:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ -- <ScrollWheelLeft> in inactive window
+ feed_data('\027[<66;48;1M')
+ screen:expect([[
+ {11: 4 }5----6----7----8----9│{11: 4 }-5----6----7----8---|
+ {11: 5 }5{1:-}---6----7----8----9│{11: 5 }-5----6----7----8---|
+ {11: 6 }5----6----7----8----9│{11: 6 }-5----6----7----8---|
+ {11: 7 }5----6----7----8----9│{11: 7 }-5----6----7----8---|
+ {5:[No Name] [+] }{1:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ -- <S-ScrollWheelUp> in active window
+ feed_data('\027[<68;8;1M')
+ screen:expect([[
+ {11: 1 }5----6----7----8----9│{11: 4 }-5----6----7----8---|
+ {11: 2 }5----6----7----8----9│{11: 5 }-5----6----7----8---|
+ {11: 3 }5----6----7----8----9│{11: 6 }-5----6----7----8---|
+ {11: 4 }5{1:-}---6----7----8----9│{11: 7 }-5----6----7----8---|
+ {5:[No Name] [+] }{1:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ -- <S-ScrollWheelUp> in inactive window
+ feed_data('\027[<68;48;1M')
+ screen:expect([[
+ {11: 1 }5----6----7----8----9│{11: 1 }-5----6----7----8---|
+ {11: 2 }5----6----7----8----9│{11: 2 }-5----6----7----8---|
+ {11: 3 }5----6----7----8----9│{11: 3 }-5----6----7----8---|
+ {11: 4 }5{1:-}---6----7----8----9│{11: 4 }-5----6----7----8---|
+ {5:[No Name] [+] }{1:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ -- <S-ScrollWheelLeft> in active window
+ feed_data('\027[<70;8;1M')
+ screen:expect([[
+ {11: 1 }0----1----2----3----4│{11: 1 }-5----6----7----8---|
+ {11: 2 }0----1----2----3----4│{11: 2 }-5----6----7----8---|
+ {11: 3 }0----1----2----3----4│{11: 3 }-5----6----7----8---|
+ {11: 4 }0----1----2----3----{1:4}│{11: 4 }-5----6----7----8---|
+ {5:[No Name] [+] }{1:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ -- <S-ScrollWheelLeft> in inactive window
+ feed_data('\027[<70;48;1M')
+ screen:expect([[
+ {11: 1 }0----1----2----3----4│{11: 1 }0----1----2----3----|
+ {11: 2 }0----1----2----3----4│{11: 2 }0----1----2----3----|
+ {11: 3 }0----1----2----3----4│{11: 3 }0----1----2----3----|
+ {11: 4 }0----1----2----3----{1:4}│{11: 4 }0----1----2----3----|
+ {5:[No Name] [+] }{1:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ end)
+
it('accepts keypad keys from kitty keyboard protocol #19180', function()
feed_data('i')
feed_data(funcs.nr2char(57399)) -- KP_0
@@ -440,37 +636,83 @@ describe('TUI', function()
tabnew
highlight Tabline ctermbg=NONE ctermfg=NONE cterm=underline
]])
- local attrs = screen:get_default_attr_ids()
- attrs[11] = {underline = true}
screen:expect([[
- {11: + [No Name] + [No Name] }{3: [No Name] }{1: }{11:X}|
+ {12: + [No Name] + [No Name] }{3: [No Name] }{1: }{12: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}|
+ {12: + [No Name] }{3: + [No Name] }{12: [No Name] }{1: }{12: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}|
+ {12: + [No Name] + [No Name] }{3: [No Name] }{1: }{12:X}|
{1: } |
{4:~ }|
{4:~ }|
{5:[No Name] }|
|
{3:-- TERMINAL --} |
- ]], attrs)
+ ]])
+ end)
+
+ it('mouse events work with right-click menu', function()
+ child_session:request('nvim_command', [[
+ 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>
+ highlight Pmenu ctermbg=NONE ctermfg=NONE cterm=underline,reverse
+ highlight PmenuSel ctermbg=NONE ctermfg=NONE cterm=underline,reverse,bold
+ ]])
+ meths.input_mouse('right', 'press', '', 0, 0, 4)
+ screen:expect([[
+ {1:p}opup menu test |
+ {4:~ }{13: foo }{4: }|
+ {4:~ }{13: bar }{4: }|
+ {4:~ }{13: baz }{4: }|
+ {5:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ meths.input_mouse('right', 'release', '', 0, 0, 4)
+ screen:expect_unchanged()
+ meths.input_mouse('move', '', '', 0, 3, 6)
+ screen:expect([[
+ {1:p}opup menu test |
+ {4:~ }{13: foo }{4: }|
+ {4:~ }{13: bar }{4: }|
+ {4:~ }{14: baz }{4: }|
+ {5:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ meths.input_mouse('left', 'press', '', 0, 2, 6)
+ screen:expect([[
+ {1:p}opup menu test |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] [+] }|
+ :let g:menustr = 'bar' |
+ {3:-- TERMINAL --} |
+ ]])
+ meths.input_mouse('left', 'release', '', 0, 2, 6)
+ screen:expect_unchanged()
end)
it('paste: Insert mode', function()
@@ -574,12 +816,11 @@ describe('TUI', function()
end)
it('paste: terminal mode', function()
- if os.getenv('GITHUB_ACTIONS') ~= nil then
+ if is_ci('github') then
pending("tty-test complains about not owning the terminal -- actions/runner#241")
- return
end
- feed_data(':set statusline=^^^^^^^\n')
- feed_data(':terminal '..testprg('tty-test')..'\n')
+ child_exec_lua('vim.o.statusline="^^^^^^^"')
+ child_exec_lua('vim.cmd.terminal(...)', testprg('tty-test'))
feed_data('i')
screen:expect{grid=[[
tty ready |
@@ -841,8 +1082,7 @@ describe('TUI', function()
wait_for_mode('i')
-- "bracketed paste"
feed_data('\027[200~'..expected..'\027[201~')
- -- FIXME: Data race between the two feeds
- if uname() == 'freebsd' then screen:sleep(1) end
+ expect_child_buf_lines({expected})
feed_data(' end')
expected = expected..' end'
screen:expect([[
@@ -961,6 +1201,15 @@ describe('TUI', function()
it('paste: split "start paste" code', function()
feed_data('i')
+ screen:expect{grid=[[
+ {1: } |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] }|
+ {3:-- INSERT --} |
+ {3:-- TERMINAL --} |
+ ]]}
-- Send split "start paste" sequence.
feed_data('\027[2')
feed_data('00~pasted from terminal\027[201~')
@@ -977,6 +1226,15 @@ describe('TUI', function()
it('paste: split "stop paste" code', function()
feed_data('i')
+ screen:expect{grid=[[
+ {1: } |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] }|
+ {3:-- INSERT --} |
+ {3:-- TERMINAL --} |
+ ]]}
-- Send split "stop paste" sequence.
feed_data('\027[200~pasted from terminal\027[20')
feed_data('1~')
@@ -1002,6 +1260,15 @@ describe('TUI', function()
end)(vim.paste)
]], {})
feed_data('i')
+ screen:expect{grid=[[
+ {1: } |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] }|
+ {3:-- INSERT --} |
+ {3:-- TERMINAL --} |
+ ]]}
feed_data('\027[200~pasted') -- phase 1
screen:expect([[
pasted{1: } |
@@ -1041,6 +1308,7 @@ describe('TUI', function()
[7] = {reverse = true, foreground = Screen.colors.SeaGreen4},
[8] = {foreground = Screen.colors.SeaGreen4},
[9] = {bold = true, foreground = Screen.colors.Blue1},
+ [10] = {foreground = Screen.colors.Blue},
})
feed_data(':hi SpecialKey ctermfg=3 guifg=SeaGreen\n')
@@ -1061,9 +1329,9 @@ describe('TUI', function()
feed_data(':set termguicolors\n')
screen:expect([[
{7:^}{8:G} |
- {9:~ }|
- {9:~ }|
- {9:~ }|
+ {9:~}{10: }|
+ {9:~}{10: }|
+ {9:~}{10: }|
{3:[No Name] [+] }|
:set termguicolors |
{4:-- TERMINAL --} |
@@ -1082,9 +1350,8 @@ describe('TUI', function()
end)
it('forwards :term palette colors with termguicolors', function()
- if os.getenv('GITHUB_ACTIONS') ~= nil then
+ if is_ci('github') then
pending("tty-test complains about not owning the terminal -- actions/runner#241")
- return
end
screen:set_rgb_cterm(true)
screen:set_default_attr_ids({
@@ -1095,12 +1362,9 @@ describe('TUI', function()
[5] = {{foreground = tonumber('0xff8000')}, {}},
})
- feed_data(':set statusline=^^^^^^^\n')
- feed_data(':set termguicolors\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')
+ child_exec_lua('vim.o.statusline="^^^^^^^"')
+ child_exec_lua('vim.o.termguicolors=true')
+ child_exec_lua('vim.cmd.terminal(...)', testprg('tty-test'))
screen:expect{grid=[[
{1:t}ty ready |
|
@@ -1139,7 +1403,7 @@ describe('TUI', function()
|
{4:~ }|
{5: }|
- [[['chan', 0], ['height', 6], ['override', v:false|
+ [[['chan', 1], ['height', 6], ['override', v:false|
], ['rgb', v:false], ['width', 50]]] |
{10:Press ENTER or type command to continue}{1: } |
{3:-- TERMINAL --} |
@@ -1147,18 +1411,14 @@ describe('TUI', function()
end)
it('allows grid to assume wider ambiguous-width characters than host terminal #19686', function()
- child_session:request('nvim_buf_set_lines', 0, 0, 0, true, { ('℃'):rep(60), ('℃'):rep(60) })
+ child_session:request('nvim_buf_set_lines', 0, 0, -1, true, { ('℃'):rep(60), ('℃'):rep(60) })
child_session:request('nvim_win_set_option', 0, 'cursorline', true)
child_session:request('nvim_win_set_option', 0, 'list', true)
child_session:request('nvim_win_set_option', 0, 'listchars', 'eol:$')
- local attrs = screen:get_default_attr_ids()
- attrs[11] = {underline = true} -- CursorLine
- attrs[12] = {underline = true, reverse = true} -- CursorLine and TermCursor
- attrs[13] = {underline = true, foreground = 12} -- CursorLine and NonText
feed_data('gg')
local singlewidth_screen = [[
- {12:℃}{11:℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃}|
- {11:℃℃℃℃℃℃℃℃℃℃}{13:$}{11: }|
+ {13:℃}{12:℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃}|
+ {12:℃℃℃℃℃℃℃℃℃℃}{15:$}{12: }|
℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃|
℃℃℃℃℃℃℃℃℃℃{4:$} |
{5:[No Name] [+] }|
@@ -1168,23 +1428,83 @@ describe('TUI', function()
-- When grid assumes "℃" to be double-width but host terminal assumes it to be single-width, the
-- second cell of "℃" is a space and the attributes of the "℃" are applied to it.
local doublewidth_screen = [[
- {12:℃}{11: ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ }|
- {11:℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ }|
- {11:℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ }{13:$}{11: }|
+ {13:℃}{12: ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ }|
+ {12:℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ }|
+ {12:℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ }{15:$}{12: }|
℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ >{4:@@@}|
{5:[No Name] [+] }|
|
{3:-- TERMINAL --} |
]]
- screen:expect(singlewidth_screen, attrs)
+ screen:expect(singlewidth_screen)
child_session:request('nvim_set_option', 'ambiwidth', 'double')
- screen:expect(doublewidth_screen, attrs)
+ screen:expect(doublewidth_screen)
child_session:request('nvim_set_option', 'ambiwidth', 'single')
- screen:expect(singlewidth_screen, attrs)
+ screen:expect(singlewidth_screen)
child_session:request('nvim_call_function', 'setcellwidths', {{{0x2103, 0x2103, 2}}})
- screen:expect(doublewidth_screen, attrs)
+ screen:expect(doublewidth_screen)
child_session:request('nvim_call_function', 'setcellwidths', {{{0x2103, 0x2103, 1}}})
- screen:expect(singlewidth_screen, attrs)
+ screen:expect(singlewidth_screen)
+ end)
+
+ it('draws correctly when cursor_address overflows #21643', function()
+ helpers.skip(helpers.is_os('mac'), 'FIXME: crashes/errors on macOS')
+ screen:try_resize(77, 834)
+ retry(nil, nil, function()
+ eq({true, 831}, {child_session:request('nvim_win_get_height', 0)})
+ end)
+ -- Use full screen message so that redrawing afterwards is more deterministic.
+ child_session:notify('nvim_command', 'intro')
+ screen:expect({any = 'Nvim'})
+ -- Going to top-left corner needs 3 bytes.
+ -- Setting underline attribute needs 9 bytes.
+ -- With screen width 77, 63857 characters need 829 full screen lines.
+ -- Drawing each full screen line needs 77 + 2 = 79 bytes (2 bytes for CR LF).
+ -- The incomplete screen line needs 24 + 3 = 27 bytes.
+ -- The whole line needs 3 + 9 + 79 * 829 + 27 = 65530 bytes.
+ -- The cursor_address that comes after will overflow the 65535-byte buffer.
+ local line = ('a'):rep(63857) .. '℃'
+ child_session:notify('nvim_exec_lua', [[
+ vim.api.nvim_buf_set_lines(0, 0, -1, true, {...})
+ vim.o.cursorline = true
+ ]], {line, 'b'})
+ -- Close the :intro message and redraw the lines.
+ feed_data('\n')
+ screen:expect(
+ '{13:a}{12:' .. ('a'):rep(76) .. '}|\n'
+ .. ('{12:' .. ('a'):rep(77) .. '}|\n'):rep(828)
+ .. '{12:' .. ('a'):rep(24) .. '℃' .. (' '):rep(52) .. '}|\n' .. dedent([[
+ b |
+ {5:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |]]))
+ end)
+
+ it('visual bell (padding) does not crash #21610', function()
+ feed_data ':set visualbell\n'
+ screen:expect{grid=[[
+ {1: } |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] }|
+ :set visualbell |
+ {3:-- TERMINAL --} |
+ ]]}
+
+ -- move left is enough to invoke the bell
+ feed_data 'h'
+ -- visual change to show we process events after this
+ feed_data 'i'
+ screen:expect{grid=[[
+ {1: } |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] }|
+ {3:-- INSERT --} |
+ {3:-- TERMINAL --} |
+ ]]}
end)
end)
@@ -1194,6 +1514,35 @@ describe('TUI', function()
os.remove('testF')
end)
+ it('resize at startup #17285 #15044 #11330', function()
+ local screen = Screen.new(50, 10)
+ screen:set_default_attr_ids({
+ [1] = {reverse = true},
+ [2] = {bold = true, foreground = Screen.colors.Blue},
+ [3] = {bold = true},
+ [4] = {foreground = tonumber('0x4040ff'), fg_indexed = true},
+ [5] = {bold = true, reverse = true},
+ })
+ screen:attach()
+ exec([[
+ 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([[
+ ^ │ |
+ {2:~ }│{4:~ }|
+ {2:~ }│{4:~ }|
+ {2:~ }│{4:~ }|
+ {2:~ }│{4:~ }|
+ {2:~ }│{4:~ }|
+ {2:~ }│{5:[No Name] 0,0-1 All}|
+ {2:~ }│ |
+ {5:new }{MATCH:<.*[/\]nvim }|
+ |
+ ]])
+ end)
+
it('with non-tty (pipe) stdout/stderr', function()
local screen = thelpers.screen_setup(0, '"'..nvim_prog
..' -u NONE -i NONE --cmd \'set noswapfile noshowcmd noruler\' --cmd \'normal iabc\' > /dev/null 2>&1 && cat testF && rm testF"')
@@ -1212,6 +1561,15 @@ describe('TUI', function()
it('<C-h> #10134', function()
local screen = thelpers.screen_setup(0, '["'..nvim_prog
..[[", "-u", "NONE", "-i", "NONE", "--cmd", "set noruler", "--cmd", ':nnoremap <C-h> :echomsg "\<C-h\>"<CR>']]..']')
+ screen:expect{grid=[[
+ {1: } |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] }|
+ |
+ {3:-- TERMINAL --} |
+ ]]}
command([[call chansend(b:terminal_job_id, "\<C-h>")]])
screen:expect([[
@@ -1238,6 +1596,15 @@ describe('TUI UIEnter/UILeave', function()
..[[, "--cmd", "autocmd VimEnter * :call add(g:evs, 'VimEnter')"]]
..']'
)
+ screen:expect{grid=[[
+ {1: } |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] }|
+ |
+ {3:-- TERMINAL --} |
+ ]]}
feed_data(":echo g:evs\n")
screen:expect{grid=[[
{1: } |
@@ -1258,61 +1625,88 @@ describe('TUI FocusGained/FocusLost', function()
clear()
screen = thelpers.screen_setup(0, '["'..nvim_prog
..'", "-u", "NONE", "-i", "NONE", "--cmd", "set noswapfile noshowcmd noruler"]')
- feed_data(":autocmd FocusGained * echo 'gained'\n")
- feed_data(":autocmd FocusLost * echo 'lost'\n")
- feed_data("\034\016") -- CTRL-\ CTRL-N
- end)
-
- it('in normal-mode', function()
- retry(2, 3 * screen.timeout, function()
- feed_data('\027[I')
- screen:expect([[
+ screen:expect{grid=[[
{1: } |
{4:~ }|
{4:~ }|
{4:~ }|
{5:[No Name] }|
- gained |
+ |
{3:-- TERMINAL --} |
- ]])
+ ]]}
+ feed_data(":autocmd FocusGained * echo 'gained'\n")
+ feed_data(":autocmd FocusLost * echo 'lost'\n")
+ feed_data("\034\016") -- CTRL-\ CTRL-N
+ end)
- feed_data('\027[O')
- screen:expect([[
+ it('in normal-mode', function()
+ screen:expect{grid=[[
{1: } |
{4:~ }|
{4:~ }|
{4:~ }|
{5:[No Name] }|
- lost |
+ :autocmd FocusLost * echo 'lost' |
{3:-- TERMINAL --} |
- ]])
+ ]]}
+ retry(2, 3 * screen.timeout, function()
+ feed_data('\027[I')
+ screen:expect([[
+ {1: } |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] }|
+ gained |
+ {3:-- TERMINAL --} |
+ ]])
+
+ feed_data('\027[O')
+ screen:expect([[
+ {1: } |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] }|
+ lost |
+ {3:-- TERMINAL --} |
+ ]])
end)
end)
it('in insert-mode', function()
feed_command('set noshowmode')
feed_data('i')
- retry(2, 3 * screen.timeout, function()
- feed_data('\027[I')
- screen:expect([[
- {1: } |
- {4:~ }|
- {4:~ }|
- {4:~ }|
- {5:[No Name] }|
- gained |
- {3:-- TERMINAL --} |
- ]])
- feed_data('\027[O')
- screen:expect([[
+ screen:expect{grid=[[
{1: } |
{4:~ }|
{4:~ }|
{4:~ }|
{5:[No Name] }|
- lost |
+ :set noshowmode |
{3:-- TERMINAL --} |
- ]])
+ ]]}
+ retry(2, 3 * screen.timeout, function()
+ feed_data('\027[I')
+ screen:expect([[
+ {1: } |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] }|
+ gained |
+ {3:-- TERMINAL --} |
+ ]])
+ feed_data('\027[O')
+ screen:expect([[
+ {1: } |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] }|
+ lost |
+ {3:-- TERMINAL --} |
+ ]])
end)
end)
@@ -1349,6 +1743,15 @@ describe('TUI FocusGained/FocusLost', function()
feed_data(":autocmd!\n")
feed_data(":autocmd FocusLost * call append(line('$'), 'lost')\n")
feed_data(":autocmd FocusGained * call append(line('$'), 'gained')\n")
+ screen:expect{grid=[[
+ {1: } |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] }|
+ |
+ {3:-- TERMINAL --} |
+ ]]}
retry(2, 3 * screen.timeout, function()
-- Enter cmdline-mode.
feed_data(':')
@@ -1407,9 +1810,18 @@ describe('TUI FocusGained/FocusLost', function()
feed_data(":echom 'msg1'|echom 'msg2'|echom 'msg3'|echom 'msg4'|echom 'msg5'\n")
-- Execute :messages to provoke the press-enter prompt.
feed_data(":messages\n")
+ screen:expect{grid=[[
+ msg1 |
+ msg2 |
+ msg3 |
+ msg4 |
+ msg5 |
+ {10:Press ENTER or type command to continue}{1: } |
+ {3:-- TERMINAL --} |
+ ]]}
feed_data('\027[I')
feed_data('\027[I')
- screen:expect([[
+ screen:expect{grid=[[
msg1 |
msg2 |
msg3 |
@@ -1417,7 +1829,7 @@ describe('TUI FocusGained/FocusLost', function()
msg5 |
{10:Press ENTER or type command to continue}{1: } |
{3:-- TERMINAL --} |
- ]])
+ ]], unchanged=true}
end)
end)
@@ -1425,7 +1837,6 @@ end)
-- does not initialize the TUI.
describe("TUI 't_Co' (terminal colors)", function()
local screen
- local is_freebsd = (uname() == 'freebsd')
local function assert_term_colors(term, colorterm, maxcolors)
helpers.clear({env={TERM=term}, args={}})
@@ -1528,7 +1939,7 @@ describe("TUI 't_Co' (terminal colors)", function()
-- which is raised to 16 by COLORTERM.
it("TERM=screen no COLORTERM uses 8/256 colors", function()
- if is_freebsd then
+ if is_os('freebsd') then
assert_term_colors("screen", nil, 256)
else
assert_term_colors("screen", nil, 8)
@@ -1536,7 +1947,7 @@ describe("TUI 't_Co' (terminal colors)", function()
end)
it("TERM=screen COLORTERM=screen uses 16/256 colors", function()
- if is_freebsd then
+ if is_os('freebsd') then
assert_term_colors("screen", "screen", 256)
else
assert_term_colors("screen", "screen", 16)
@@ -1699,8 +2110,6 @@ end)
-- does not initialize the TUI.
describe("TUI 'term' option", function()
local screen
- local is_bsd = not not string.find(uname(), 'bsd')
- local is_macos = not not string.find(uname(), 'darwin')
local function assert_term(term_envvar, term_expected)
clear()
@@ -1726,11 +2135,11 @@ describe("TUI 'term' option", function()
end)
it('gets system-provided term if $TERM is valid', function()
- if uname() == "openbsd" then
+ if is_os('openbsd') then
assert_term("xterm", "xterm")
- elseif is_bsd then -- BSD lacks terminfo, builtin is always used.
+ elseif is_os('bsd') then -- BSD lacks terminfo, builtin is always used.
assert_term("xterm", "builtin_xterm")
- elseif is_macos then
+ elseif is_os('mac') then
local status, _ = pcall(assert_term, "xterm", "xterm")
if not status then
pending("macOS: unibilium could not find terminfo")
@@ -1788,7 +2197,7 @@ describe("TUI", function()
retry(nil, 3000, function() -- Wait for log file to be flushed.
local log = read_file('Xtest_tui_verbose_log') or ''
- eq('--- Terminal info --- {{{\n', string.match(log, '%-%-%- Terminal.-\n'))
+ eq('--- Terminal info --- {{{\n', string.match(log, '%-%-%- Terminal.-\n')) -- }}}
ok(#log > 50)
end)
end)
@@ -1912,3 +2321,163 @@ describe('TUI bg color', function()
screen:expect{any='new_bg=dark'}
end)
end)
+
+-- These tests require `thelpers` because --headless/--embed
+-- does not initialize the TUI.
+describe("TUI as a client", function()
+
+ it("connects to remote instance (with its own TUI)", function()
+ local server_super = spawn_argv(false) -- equivalent to clear()
+ local client_super = spawn_argv(true)
+
+ set_session(server_super)
+ local server_pipe = new_pipename()
+ local screen_server = thelpers.screen_setup(0,
+ string.format([=[["%s", "--listen", "%s", "-u", "NONE", "-i", "NONE", "--cmd", "%s laststatus=2 background=dark"]]=],
+ nvim_prog, server_pipe, nvim_set))
+
+ feed_data("iHello, World")
+ screen_server:expect{grid=[[
+ Hello, World{1: } |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] [+] }|
+ {3:-- INSERT --} |
+ {3:-- TERMINAL --} |
+ ]]}
+ feed_data("\027")
+ screen_server:expect{grid=[[
+ Hello, Worl{1:d} |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]]}
+
+ set_session(client_super)
+ local screen_client = thelpers.screen_setup(0,
+ string.format([=[["%s", "-u", "NONE", "-i", "NONE", "--server", "%s", "--remote-ui"]]=],
+ nvim_prog, server_pipe))
+
+ screen_client:expect{grid=[[
+ Hello, Worl{1:d} |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]]}
+
+ feed_data(":q!\n")
+
+ server_super:close()
+ client_super:close()
+ end)
+
+ it("connects to remote instance (--headless)", function()
+ local server = helpers.spawn_argv(false) -- equivalent to clear()
+ local client_super = spawn_argv(true)
+
+ set_session(server)
+ local server_pipe = eval'v:servername'
+ feed'iHalloj!<esc>'
+
+ set_session(client_super)
+ local screen = thelpers.screen_setup(0,
+ string.format([=[["%s", "-u", "NONE", "-i", "NONE", "--server", "%s", "--remote-ui"]]=],
+ nvim_prog, server_pipe))
+
+ screen:expect{grid=[[
+ Halloj{1:!} |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ |
+ {3:-- TERMINAL --} |
+ ]]}
+
+ client_super:close()
+ server:close()
+ end)
+
+
+ it("throws error when no server exists", function()
+ clear()
+ local screen = thelpers.screen_setup(0,
+ string.format([=[["%s", "-u", "NONE", "-i", "NONE", "--server", "127.0.0.1:2436546", "--remote-ui"]]=],
+ nvim_prog), 60)
+
+ screen:expect([[
+ Remote ui failed to start: {MATCH:.*}|
+ |
+ [Process exited 1]{1: } |
+ |
+ |
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ end)
+
+ it("exits when server quits", function()
+ local server_super = spawn_argv(false) -- equivalent to clear()
+ local client_super = spawn_argv(true)
+
+ set_session(server_super)
+ local server_pipe = new_pipename()
+ local screen_server = thelpers.screen_setup(0,
+ string.format([=[["%s", "--listen", "%s", "-u", "NONE", "-i", "NONE", "--cmd", "%s laststatus=2 background=dark"]]=],
+ nvim_prog, server_pipe, nvim_set))
+
+ feed_data("iHello, World")
+ screen_server:expect{grid=[[
+ Hello, World{1: } |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] [+] }|
+ {3:-- INSERT --} |
+ {3:-- TERMINAL --} |
+ ]]}
+ feed_data("\027")
+ screen_server:expect{grid=[[
+ Hello, Worl{1:d} |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]]}
+
+ set_session(client_super)
+ local screen_client = thelpers.screen_setup(0,
+ string.format([=[["%s", "-u", "NONE", "-i", "NONE", "--server", "%s", "--remote-ui"]]=],
+ nvim_prog, server_pipe))
+
+ screen_client:expect{grid=[[
+ Hello, Worl{1:d} |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]]}
+
+ -- quitting the server
+ set_session(server_super)
+ feed_data(":q!\n")
+ screen_server:expect({any="Process exited 0"})
+
+ -- assert that client has exited
+ screen_client:expect({any="Process exited 0"})
+
+ server_super:close()
+ client_super:close()
+ end)
+end)
diff --git a/test/functional/terminal/window_spec.lua b/test/functional/terminal/window_spec.lua
index 0d3295cf32..80e9d78400 100644
--- a/test/functional/terminal/window_spec.lua
+++ b/test/functional/terminal/window_spec.lua
@@ -3,11 +3,12 @@ local thelpers = require('test.functional.terminal.helpers')
local feed_data = thelpers.feed_data
local feed, clear = helpers.feed, helpers.clear
local poke_eventloop = helpers.poke_eventloop
-local iswin = helpers.iswin
local command = helpers.command
local retry = helpers.retry
local eq = helpers.eq
local eval = helpers.eval
+local skip = helpers.skip
+local is_os = helpers.is_os
describe(':terminal window', function()
local screen
@@ -18,7 +19,7 @@ describe(':terminal window', function()
end)
it('sets topline correctly #8556', function()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
-- Test has hardcoded assumptions of dimensions.
eq(7, eval('&lines'))
feed_data('\n\n\n') -- Add blank lines.
@@ -54,9 +55,7 @@ describe(':terminal window', function()
{3:-- TERMINAL --} |
]])
- if iswin() then
- return -- win: :terminal resize is unreliable #7007
- end
+ skip(is_os('win'), 'win: :terminal resize is unreliable #7007')
-- numberwidth=9
feed([[<C-\><C-N>]])
@@ -172,7 +171,7 @@ describe(':terminal with multigrid', function()
]])
screen:try_resize_grid(2, 20, 10)
- if iswin() then
+ if is_os('win') then
screen:expect{any="rows: 10, cols: 20"}
else
screen:expect([[
@@ -201,7 +200,7 @@ describe(':terminal with multigrid', function()
end
screen:try_resize_grid(2, 70, 3)
- if iswin() then
+ if is_os('win') then
screen:expect{any="rows: 3, cols: 70"}
else
screen:expect([[
@@ -223,7 +222,7 @@ describe(':terminal with multigrid', function()
end
screen:try_resize_grid(2, 0, 0)
- if iswin() then
+ if is_os('win') then
screen:expect{any="rows: 6, cols: 50"}
else
screen:expect([[
diff --git a/test/functional/terminal/window_split_tab_spec.lua b/test/functional/terminal/window_split_tab_spec.lua
index b62d173cea..1d77e1e92e 100644
--- a/test/functional/terminal/window_split_tab_spec.lua
+++ b/test/functional/terminal/window_split_tab_spec.lua
@@ -8,9 +8,9 @@ local command = helpers.command
local eq = helpers.eq
local eval = helpers.eval
local meths = helpers.meths
-local iswin = helpers.iswin
local sleep = helpers.sleep
local retry = helpers.retry
+local is_os = helpers.is_os
describe(':terminal', function()
local screen
@@ -96,7 +96,7 @@ describe(':terminal', function()
local w1, h1 = screen._width - 3, screen._height - 2
local w2, h2 = w1 - 6, h1 - 3
- if iswin() then
+ if is_os('win') then
-- win: SIGWINCH is unreliable, use a weaker test. #7506
retry(3, 30000, function()
screen:try_resize(w1, h1)
diff --git a/test/functional/treesitter/highlight_spec.lua b/test/functional/treesitter/highlight_spec.lua
index 5ec0a8a060..2a2311c0fa 100644
--- a/test/functional/treesitter/highlight_spec.lua
+++ b/test/functional/treesitter/highlight_spec.lua
@@ -5,12 +5,14 @@ local clear = helpers.clear
local insert = helpers.insert
local exec_lua = helpers.exec_lua
local feed = helpers.feed
-local pending_c_parser = helpers.pending_c_parser
+local command = helpers.command
+local meths = helpers.meths
+local eq = helpers.eq
before_each(clear)
local hl_query = [[
- (ERROR) @ErrorMsg
+ (ERROR) @error
"if" @keyword
"else" @keyword
@@ -23,23 +25,24 @@ local hl_query = [[
"enum" @type
"extern" @type
- (string_literal) @string.nonexistent-specializer-for-string.should-fallback-to-string
+ ; nonexistent specializer for string should fallback to string
+ (string_literal) @string.nonexistent_specializer
(number_literal) @number
(char_literal) @string
(type_identifier) @type
- ((type_identifier) @Special (#eq? @Special "LuaRef"))
+ ((type_identifier) @constant.builtin (#eq? @constant.builtin "LuaRef"))
(primitive_type) @type
(sized_type_specifier) @type
; Use lua regexes
- ((identifier) @Identifier (#contains? @Identifier "lua_"))
+ ((identifier) @function (#contains? @function "lua_"))
((identifier) @Constant (#lua-match? @Constant "^[A-Z_]+$"))
- ((identifier) @Normal (#vim-match? @Constant "^lstate$"))
+ ((identifier) @Normal (#vim-match? @Normal "^lstate$"))
- ((binary_expression left: (identifier) @WarningMsg.left right: (identifier) @WarningMsg.right) (#eq? @WarningMsg.left @WarningMsg.right))
+ ((binary_expression left: (identifier) @warning.left right: (identifier) @warning.right) (#eq? @warning.left @warning.right))
(comment) @comment
]]
@@ -103,11 +106,11 @@ describe('treesitter highlighting', function()
}
exec_lua([[ hl_query = ... ]], hl_query)
+ command [[ hi link @error ErrorMsg ]]
+ command [[ hi link @warning WarningMsg ]]
end)
it('is updated with edits', function()
- if pending_c_parser(pending) then return end
-
insert(hl_text)
screen:expect{grid=[[
/// Schedule Lua callback on main loop's event queue |
@@ -271,8 +274,6 @@ describe('treesitter highlighting', function()
end)
it('is updated with :sort', function()
- if pending_c_parser(pending) then return end
-
insert(test_text)
exec_lua [[
local parser = vim.treesitter.get_parser(0, "c")
@@ -346,8 +347,6 @@ describe('treesitter highlighting', function()
end)
it("supports with custom parser", function()
- if pending_c_parser(pending) then return end
-
screen:set_default_attr_ids {
[1] = {bold = true, foreground = Screen.colors.SeaGreen4};
}
@@ -412,8 +411,6 @@ describe('treesitter highlighting', function()
end)
it("supports injected languages", function()
- if pending_c_parser(pending) then return end
-
insert([[
int x = INT_MAX;
#define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y))
@@ -474,8 +471,6 @@ describe('treesitter highlighting', function()
end)
it("supports overriding queries, like ", function()
- if pending_c_parser(pending) then return end
-
insert([[
int x = INT_MAX;
#define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y))
@@ -515,8 +510,6 @@ describe('treesitter highlighting', function()
end)
it("supports highlighting with custom highlight groups", function()
- if pending_c_parser(pending) then return end
-
insert(hl_text)
exec_lua [[
@@ -547,7 +540,7 @@ describe('treesitter highlighting', function()
-- This will change ONLY the literal strings to look like comments
-- The only literal string is the "vim.schedule: expected function" in this test.
- exec_lua [[vim.cmd("highlight link cString comment")]]
+ exec_lua [[vim.cmd("highlight link @string.nonexistent_specializer comment")]]
screen:expect{grid=[[
{2:/// Schedule Lua callback on main loop's event queue} |
{3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) |
@@ -572,8 +565,6 @@ describe('treesitter highlighting', function()
end)
it("supports highlighting with priority", function()
- if pending_c_parser(pending) then return end
-
insert([[
int x = INT_MAX;
#define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y))
@@ -589,9 +580,9 @@ describe('treesitter highlighting', function()
-- expect everything to have Error highlight
screen:expect{grid=[[
{12:int}{8: x = INT_MAX;} |
- {8:#define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y))}|
- {8:#define foo void main() { \} |
- {8: return 42; \} |
+ {8:#define READ_STRING(x, y) (}{12:char_u}{8: *)read_string((x), (}{12:size_t}{8:)(y))}|
+ {8:#define foo }{12:void}{8: main() { \} |
+ {8: }{12:return}{8: 42; \} |
{8: }} |
^ |
{1:~ }|
@@ -612,11 +603,14 @@ describe('treesitter highlighting', function()
-- bold will not be overwritten at the moment
[12] = {background = Screen.colors.Red, bold = true, foreground = Screen.colors.Grey100};
}}
+
+ eq({
+ {capture='Error', metadata = { priority='101' }, lang='c' };
+ {capture='type', metadata = { }, lang='c' };
+ }, exec_lua [[ return vim.treesitter.get_captures_at_pos(0, 0, 2) ]])
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?";
]])
@@ -642,11 +636,13 @@ describe('treesitter highlighting', function()
|
]]}
+ command [[
+ hi link @foo.bar Type
+ hi link @foo String
+ ]]
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"}})
]]
@@ -670,10 +666,32 @@ describe('treesitter highlighting', function()
{1:~ }|
|
]]}
+
+ -- clearing specialization reactivates fallback
+ command [[ hi clear @foo.bar ]]
+ screen:expect{grid=[[
+ {5: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.
@@ -712,32 +730,26 @@ describe('treesitter highlighting', function()
]]}
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")
- ]]
-
+ it("@foo.bar groups has the correct fallback behavior", function()
+ local get_hl = function(name) return meths.get_hl_by_name(name,1).foreground end
+ meths.set_hl(0, "@foo", {fg = 1})
+ meths.set_hl(0, "@foo.bar", {fg = 2})
+ meths.set_hl(0, "@foo.bar.baz", {fg = 3})
+
+ eq(1, get_hl"@foo")
+ eq(1, get_hl"@foo.a.b.c.d")
+ eq(2, get_hl"@foo.bar")
+ eq(2, get_hl"@foo.bar.a.b.c.d")
+ eq(3, get_hl"@foo.bar.baz")
+ eq(3, get_hl"@foo.bar.baz.d")
+
+ -- lookup is case insensitive
+ eq(2, get_hl"@FOO.BAR.SPAM")
+
+ meths.set_hl(0, "@foo.missing.exists", {fg = 3})
+ eq(1, get_hl"@foo.missing")
+ eq(3, get_hl"@foo.missing.exists")
+ eq(3, get_hl"@foo.missing.exists.bar")
+ eq(nil, get_hl"@total.nonsense.but.a.lot.of.dots")
end)
end)
diff --git a/test/functional/treesitter/language_spec.lua b/test/functional/treesitter/language_spec.lua
index 30585be328..f95b05a1cc 100644
--- a/test/functional/treesitter/language_spec.lua
+++ b/test/functional/treesitter/language_spec.lua
@@ -6,31 +6,32 @@ local command = helpers.command
local exec_lua = helpers.exec_lua
local pcall_err = helpers.pcall_err
local matches = helpers.matches
-local pending_c_parser = helpers.pending_c_parser
+local insert = helpers.insert
before_each(clear)
-describe('treesitter API', function()
+describe('treesitter language API', function()
-- error tests not requiring a parser library
it('handles missing language', function()
- eq("Error executing lua: .../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers",
+ eq(".../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers",
pcall_err(exec_lua, "parser = vim.treesitter.get_parser(0, 'borklang')"))
-- actual message depends on platform
- matches("Error executing lua: Failed to load parser: uv_dlopen: .+",
+ matches("Failed to load parser for language 'borklang': uv_dlopen: .+",
pcall_err(exec_lua, "parser = vim.treesitter.require_language('borklang', 'borkbork.so')"))
-- Should not throw an error when silent
eq(false, exec_lua("return vim.treesitter.require_language('borklang', nil, true)"))
eq(false, exec_lua("return vim.treesitter.require_language('borklang', 'borkbork.so', true)"))
- eq("Error executing lua: .../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers",
+ eq(".../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers",
pcall_err(exec_lua, "parser = vim.treesitter.inspect_language('borklang')"))
+
+ matches("Failed to load parser: uv_dlsym: .+",
+ pcall_err(exec_lua, 'vim.treesitter.require_language("c", nil, false, "borklang")'))
end)
it('inspects language', function()
- if pending_c_parser(pending) then return end
-
local keys, fields, symbols = unpack(exec_lua([[
local lang = vim.treesitter.inspect_language('c')
local keys, symbols = {}, {}
@@ -70,14 +71,41 @@ describe('treesitter API', function()
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",
+ eq(".../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers",
pcall_err(exec_lua, "new_parser = vim.treesitter.get_parser(0)"))
end)
+
+ it('retrieve the tree given a range', function ()
+ insert([[
+ int main() {
+ int x = 3;
+ }]])
+
+ exec_lua([[
+ langtree = vim.treesitter.get_parser(0, "c")
+ tree = langtree:tree_for_range({1, 3, 1, 3})
+ ]])
+
+ eq('<node translation_unit>', exec_lua('return tostring(tree:root())'))
+ end)
+
+ it('retrieve the node given a range', function ()
+ insert([[
+ int main() {
+ int x = 3;
+ }]])
+
+ exec_lua([[
+ langtree = vim.treesitter.get_parser(0, "c")
+ node = langtree:named_node_for_range({1, 3, 1, 3})
+ ]])
+
+ eq('<node primitive_type>', exec_lua('return tostring(node)'))
+ end)
end)
diff --git a/test/functional/treesitter/node_spec.lua b/test/functional/treesitter/node_spec.lua
index 21c287644e..a82dce47b7 100644
--- a/test/functional/treesitter/node_spec.lua
+++ b/test/functional/treesitter/node_spec.lua
@@ -4,7 +4,6 @@ local clear = helpers.clear
local eq = helpers.eq
local exec_lua = helpers.exec_lua
local insert = helpers.insert
-local pending_c_parser = helpers.pending_c_parser
before_each(clear)
@@ -15,10 +14,6 @@ end
describe('treesitter node API', function()
clear()
- if pending_c_parser(pending) then
- return
- end
-
it('can move between siblings', function()
insert([[
int main(int x, int y, int z) {
@@ -59,4 +54,53 @@ describe('treesitter node API', function()
exec_lua 'node = node:prev_named_sibling()'
eq('int x', lua_eval('node_text(node)'))
end)
+
+ it('can retrieve the children of a node', function()
+ insert([[
+ int main() {
+ int x = 3;
+ }]])
+
+ local len = exec_lua([[
+ tree = vim.treesitter.get_parser(0, "c"):parse()[1]
+ node = tree:root():child(0)
+ children = node:named_children()
+
+ return #children
+ ]])
+
+ eq(3, len)
+ eq('<node compound_statement>', lua_eval('tostring(children[3])'))
+ end)
+
+ it('can retrieve the tree root given a node', function()
+ insert([[
+ int main() {
+ int x = 3;
+ }]])
+
+ exec_lua([[
+ tree = vim.treesitter.get_parser(0, "c"):parse()[1]
+ root = tree:root()
+ node = root:child(0):child(2)
+ ]])
+
+ eq(lua_eval('tostring(root)'), lua_eval('tostring(node:root())'))
+ end)
+
+ it('can compute the byte length of a node', function()
+ insert([[
+ int main() {
+ int x = 3;
+ }]])
+
+ exec_lua([[
+ tree = vim.treesitter.get_parser(0, "c"):parse()[1]
+ root = tree:root()
+ child = root:child(0):child(0)
+ ]])
+
+ eq(28, lua_eval('root:byte_length()'))
+ eq(3, lua_eval('child:byte_length()'))
+ end)
end)
diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua
index 7f3b0e770a..f006ad4539 100644
--- a/test/functional/treesitter/parser_spec.lua
+++ b/test/functional/treesitter/parser_spec.lua
@@ -5,17 +5,15 @@ local eq = helpers.eq
local insert = helpers.insert
local exec_lua = helpers.exec_lua
local feed = helpers.feed
-local pending_c_parser = helpers.pending_c_parser
+local is_os = helpers.is_os
+local skip = helpers.skip
before_each(clear)
describe('treesitter parser API', function()
clear()
- if pending_c_parser(pending) then return end
it('parses buffer', function()
- if helpers.pending_win32(pending) then return end
-
insert([[
int main() {
int x = 3;
@@ -249,7 +247,6 @@ void ui_refresh(void)
end)
it('supports getting text of multiline node', function()
- if pending_c_parser(pending) then return end
insert(test_text)
local res = exec_lua([[
local parser = vim.treesitter.get_parser(0, "c")
@@ -687,7 +684,7 @@ int x = INT_MAX;
end)
it("should not inject bad languages", function()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
exec_lua([=[
vim.treesitter.add_directive("inject-bad!", function(match, _, _, pred, metadata)
metadata.language = "{"
diff --git a/test/functional/treesitter/utils_spec.lua b/test/functional/treesitter/utils_spec.lua
new file mode 100644
index 0000000000..7f5a864c3d
--- /dev/null
+++ b/test/functional/treesitter/utils_spec.lua
@@ -0,0 +1,31 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local clear = helpers.clear
+local insert = helpers.insert
+local eq = helpers.eq
+local exec_lua = helpers.exec_lua
+
+before_each(clear)
+
+describe('treesitter utils', function()
+ before_each(clear)
+
+ it('can find an ancestor', function()
+
+ insert([[
+ int main() {
+ int x = 3;
+ }]])
+
+ exec_lua([[
+ parser = vim.treesitter.get_parser(0, "c")
+ tree = parser:parse()[1]
+ root = tree:root()
+ ancestor = root:child(0)
+ child = ancestor:child(0)
+ ]])
+
+ eq(true, exec_lua('return vim.treesitter.is_ancestor(ancestor, child)'))
+ eq(false, exec_lua('return vim.treesitter.is_ancestor(child, ancestor)'))
+ end)
+end)
diff --git a/test/functional/ui/bufhl_spec.lua b/test/functional/ui/bufhl_spec.lua
index 7c0831bd09..46bfae8de2 100644
--- a/test/functional/ui/bufhl_spec.lua
+++ b/test/functional/ui/bufhl_spec.lua
@@ -762,7 +762,7 @@ describe('Buffer highlighting', function()
local s1 = {{'Köttbullar', 'Comment'}, {'Kräuterbutter'}}
local s2 = {{'こんにちは', 'Comment'}}
- -- TODO: only a virtual text from the same ns curretly overrides
+ -- TODO: only a virtual text from the same ns currently overrides
-- an existing virtual text. We might add a prioritation system.
set_virtual_text(id1, 0, s1, {})
eq({{1, 0, 0, {
diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua
index fa5771a8b3..eb5de693bd 100644
--- a/test/functional/ui/cmdline_highlight_spec.lua
+++ b/test/functional/ui/cmdline_highlight_spec.lua
@@ -61,7 +61,7 @@ before_each(function()
endwhile
return ret
endfunction
- function SplittedMultibyteStart(cmdline)
+ function SplitMultibyteStart(cmdline)
let ret = []
let i = 0
while i < len(a:cmdline)
@@ -77,7 +77,7 @@ before_each(function()
endwhile
return ret
endfunction
- function SplittedMultibyteEnd(cmdline)
+ function SplitMultibyteEnd(cmdline)
let ret = []
let i = 0
while i < len(a:cmdline)
@@ -296,7 +296,7 @@ describe('Command-line coloring', function()
end
it('does the right thing when hl start appears to split multibyte char',
function()
- set_color_cb('SplittedMultibyteStart')
+ set_color_cb('SplitMultibyteStart')
start_prompt('echo "«')
screen:expect{grid=[[
|
@@ -322,7 +322,7 @@ describe('Command-line coloring', function()
end)
it('does the right thing when hl end appears to split multibyte char',
function()
- set_color_cb('SplittedMultibyteEnd')
+ set_color_cb('SplitMultibyteEnd')
start_prompt('echo "«')
screen:expect([[
|
@@ -335,17 +335,17 @@ describe('Command-line coloring', function()
:echo "«^ |
]])
end)
- it('does the right thing when errorring', function()
+ it('does the right thing when erroring', function()
set_color_cb('Echoerring')
start_prompt('e')
screen:expect([[
|
{EOB:~ }|
- {EOB:~ }|
{MSEP: }|
: |
{ERR:E5407: Callback has thrown an exception:}|
- {ERR: Vim(echoerr):HERE} |
+ {ERR: function DoPrompt[3]..Echoerring, line }|
+ {ERR:1: Vim(echoerr):HERE} |
:e^ |
]])
end)
@@ -400,16 +400,16 @@ describe('Command-line coloring', function()
screen:expect([[
|
{EOB:~ }|
- {EOB:~ }|
{MSEP: }|
: |
{ERR:E5407: Callback has thrown an exception:}|
+ {ERR: function DoPrompt[3]..Throwing, line 1:}|
{ERR: ABC} |
:e^ |
]])
end)
it('stops executing callback after a number of errors', function()
- set_color_cb('SplittedMultibyteStart')
+ set_color_cb('SplitMultibyteStart')
start_prompt('let x = "«»«»«»«»«»"')
screen:expect([[
|
@@ -772,7 +772,7 @@ describe('Ex commands coloring', function()
]])
end)
it('still executes command-line even if errored out', function()
- meths.set_var('Nvim_color_cmdline', 'SplittedMultibyteStart')
+ meths.set_var('Nvim_color_cmdline', 'SplitMultibyteStart')
feed(':let x = "«"\n')
eq('«', meths.get_var('x'))
local msg = 'E5405: Chunk 0 start 10 splits multibyte character'
diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua
index db13647cc6..1c9ac7f7ba 100644
--- a/test/functional/ui/cmdline_spec.lua
+++ b/test/functional/ui/cmdline_spec.lua
@@ -4,9 +4,12 @@ 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 poke_eventloop = helpers.poke_eventloop
+local exec = helpers.exec
local eval = helpers.eval
local eq = helpers.eq
+local is_os = helpers.is_os
+local meths = helpers.meths
local function new_screen(opt)
local screen = Screen.new(25, 5)
@@ -716,7 +719,7 @@ describe('cmdline redraw', function()
end)
it('with <Cmd>', function()
- if string.find(uname(), 'bsd') then
+ if is_os('bsd') then
pending('FIXME #10804')
end
command('cmap a <Cmd>call sin(0)<CR>') -- no-op
@@ -821,16 +824,27 @@ describe('statusline is redrawn on entering cmdline', function()
]]}
end)
- it('but not with scrolled messages', function()
- command('set statusline=%{mode()}')
- screen:try_resize(35,10)
+ it('with scrolled messages', function()
+ screen:try_resize(35,14)
+ exec([[
+ let g:count = 0
+ autocmd CmdlineEnter * let g:count += 1
+ split
+ resize 1
+ setlocal statusline=%{mode()}%{g:count}
+ setlocal winbar=%{mode()}%{g:count}
+ ]])
feed(':echoerr doesnotexist<cr>')
screen:expect{grid=[[
+ {9:c1 }|
+ |
+ {3:c1 }|
|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
+ {1:~ }|
{3: }|
{4:E121: Undefined variable: doesnotex}|
{4:ist} |
@@ -839,8 +853,12 @@ describe('statusline is redrawn on entering cmdline', function()
]]}
feed(':echoerr doesnotexist<cr>')
screen:expect{grid=[[
+ {9:c2 }|
+ |
+ {3:c2 }|
|
{1:~ }|
+ {1:~ }|
{3: }|
{4:E121: Undefined variable: doesnotex}|
{4:ist} |
@@ -853,6 +871,10 @@ describe('statusline is redrawn on entering cmdline', function()
feed(':echoerr doesnotexist<cr>')
screen:expect{grid=[[
+ {9:c3 }|
+ |
+ {3:c3 }|
+ {3: }|
{4:E121: Undefined variable: doesnotex}|
{4:ist} |
{5:Press ENTER or type command to cont}|
@@ -867,7 +889,10 @@ describe('statusline is redrawn on entering cmdline', function()
feed('<cr>')
screen:expect{grid=[[
+ {9:n3 }|
^ |
+ {3:n3 }|
+ |
{1:~ }|
{1:~ }|
{1:~ }|
@@ -875,7 +900,8 @@ describe('statusline is redrawn on entering cmdline', function()
{1:~ }|
{1:~ }|
{1:~ }|
- {3:n }|
+ {1:~ }|
+ {2:[No Name] }|
|
]]}
end)
@@ -934,6 +960,15 @@ describe('cmdheight=0', function()
before_each(function()
clear()
screen = Screen.new(25, 5)
+ screen:set_default_attr_ids {
+ [1] = {bold = true, foreground = Screen.colors.Blue};
+ [2] = {bold = true, reverse = true};
+ [3] = {bold = true};
+ [4] = {foreground = Screen.colors.White, background = Screen.colors.Red};
+ [5] = {foreground = Screen.colors.SeaGreen4, bold = true};
+ [6] = {reverse = true};
+ [7] = {background = Screen.colors.Yellow};
+ }
screen:attach()
end)
@@ -941,9 +976,9 @@ describe('cmdheight=0', function()
command("set cmdheight=1 noruler laststatus=2")
screen:expect{grid=[[
^ |
- ~ |
- ~ |
- [No Name] |
+ {1:~ }|
+ {1:~ }|
+ {2:[No Name] }|
|
]]}
end)
@@ -952,10 +987,10 @@ describe('cmdheight=0', function()
command("set cmdheight=0 noruler laststatus=2")
screen:expect{grid=[[
^ |
- ~ |
- ~ |
- ~ |
- [No Name] |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:[No Name] }|
]]}
end)
@@ -963,10 +998,10 @@ describe('cmdheight=0', function()
command("set cmdheight=0 ruler laststatus=0")
screen:expect{grid=[[
^ |
- ~ |
- ~ |
- ~ |
- ~ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
]]}
end)
@@ -975,10 +1010,10 @@ describe('cmdheight=0', function()
feed('i')
screen:expect{grid=[[
^ |
- ~ |
- ~ |
- ~ |
- ~ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
]], showmode={}}
feed('<Esc>')
eq(0, eval('&cmdheight'))
@@ -989,10 +1024,10 @@ describe('cmdheight=0', function()
feed('i')
screen:expect{grid=[[
^ |
- ~ |
- ~ |
- ~ |
- ~ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
]], showmode={}}
feed('<Esc>')
eq(0, eval('&cmdheight'))
@@ -1003,10 +1038,10 @@ describe('cmdheight=0', function()
feed('i')
screen:expect{grid=[[
^ |
- ~ |
- ~ |
- ~ |
- -- INSERT -- |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {3:-- INSERT --} |
]]}
feed('<Esc>')
eq(1, eval('&cmdheight'))
@@ -1017,19 +1052,19 @@ describe('cmdheight=0', function()
feed(':')
screen:expect{grid=[[
|
- ~ |
- ~ |
- ~ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
:^ |
]]}
- eq(1, eval('&cmdheight'))
+ eq(0, eval('&cmdheight'))
feed('<cr>')
screen:expect{grid=[[
^ |
- ~ |
- ~ |
- ~ |
- ~ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
]], showmode={}}
eq(0, eval('&cmdheight'))
end)
@@ -1039,19 +1074,19 @@ describe('cmdheight=0', function()
feed(':call input("foo >")<cr>')
screen:expect{grid=[[
|
- ~ |
- ~ |
- ~ |
+ {1:~ }|
+ {2: }|
+ :call input("foo >") |
foo >^ |
]]}
- eq(1, eval('&cmdheight'))
+ eq(0, eval('&cmdheight'))
feed('<cr>')
screen:expect{grid=[[
^ |
- ~ |
- ~ |
- ~ |
- ~ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
]], showmode={}}
eq(0, eval('&cmdheight'))
end)
@@ -1060,35 +1095,35 @@ describe('cmdheight=0', 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^ |
+ {2: }|
+ :split |
+ {4:E36: Not enough room} |
+ {5:Press ENTER or type comma}|
+ {5:nd to continue}^ |
]]}
feed('<CR>')
screen:expect{grid=[[
- foo |
+ {3:foo }|
^ |
- ~ |
- ~ |
- [No Name] |
+ {1:~ }|
+ {1:~ }|
+ {2:[No Name] }|
]]}
feed(':')
screen:expect{grid=[[
- foo |
+ {3:foo }|
|
- ~ |
- [No Name] |
+ {1:~ }|
+ {1:~ }|
:^ |
]]}
feed('<Esc>')
screen:expect{grid=[[
- foo |
+ {3:foo }|
^ |
- ~ |
- ~ |
- [No Name] |
+ {1:~ }|
+ {1:~ }|
+ {2:[No Name] }|
]], showmode={}}
eq(0, eval('&cmdheight'))
@@ -1100,19 +1135,19 @@ describe('cmdheight=0', function()
feed('qq')
screen:expect{grid=[[
^ |
- ~ |
- ~ |
- ~ |
- recording @q |
- ]], showmode={}}
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]]}
feed('q')
screen:expect{grid=[[
^ |
- ~ |
- ~ |
- ~ |
- ~ |
- ]], showmode={}}
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], unchanged=true}
end)
it("when substitute text", function()
@@ -1120,28 +1155,28 @@ describe('cmdheight=0', function()
feed('ifoo<ESC>')
screen:expect{grid=[[
fo^o |
- ~ |
- ~ |
- ~ |
- [No Name] [+] |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:[No Name] [+] }|
]]}
feed(':%s/foo/bar/gc<CR>')
screen:expect{grid=[[
- foo |
- ~ |
- ~ |
- [No Name] [+] |
- replace wi...q/l/^E/^Y)?^ |
+ {6:foo} |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {5:replace wi...q/l/^E/^Y)?}^ |
]]}
feed('y')
screen:expect{grid=[[
^bar |
- ~ |
- ~ |
- ~ |
- [No Name] [+] |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:[No Name] [+] }|
]]}
assert_alive()
@@ -1152,4 +1187,223 @@ describe('cmdheight=0', function()
feed('<C-w>+')
eq(0, eval('&cmdheight'))
end)
+
+ it("with non-silent mappings with cmdline", function()
+ command("set cmdheight=0")
+ command("map <f3> :nohlsearch<cr>")
+ feed('iaabbaa<esc>/aa<cr>')
+ screen:expect{grid=[[
+ {7:^aa}bb{7:aa} |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]]}
+
+ feed('<f3>')
+ screen:expect{grid=[[
+ ^aabbaa |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]]}
+ end)
+
+ it('with silent! at startup', function()
+ clear{args={'-c', 'set cmdheight=0', '-c', 'autocmd VimEnter * silent! call Foo()'}}
+ screen:attach()
+ -- doesn't crash while not displaying silent! error message
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]]}
+ end)
+
+ it('with multigrid', function()
+ clear{args={'--cmd', 'set cmdheight=0'}}
+ screen:attach{ext_multigrid=true}
+ screen:expect{grid=[[
+ ## grid 1
+ [2:-------------------------]|
+ [2:-------------------------]|
+ [2:-------------------------]|
+ [2:-------------------------]|
+ [2:-------------------------]|
+ ## grid 2
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ ]], win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1};
+ }}
+
+ feed '/p'
+ screen:expect{grid=[[
+ ## grid 1
+ [2:-------------------------]|
+ [2:-------------------------]|
+ [2:-------------------------]|
+ [2:-------------------------]|
+ [3:-------------------------]|
+ ## grid 2
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ /p^ |
+ ]], win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1};
+ }}
+ end)
+
+ it('winbar is redrawn on entering cmdline and :redrawstatus #20336', function()
+ exec([[
+ set cmdheight=0
+ set winbar=%{mode()}%=:%{getcmdline()}
+ ]])
+ feed(':')
+ screen:expect([[
+ {3:c :}|
+ |
+ {1:~ }|
+ {1:~ }|
+ :^ |
+ ]])
+ feed('echo')
+ -- not redrawn yet
+ screen:expect([[
+ {3:c :}|
+ |
+ {1:~ }|
+ {1:~ }|
+ :echo^ |
+ ]])
+ command('redrawstatus')
+ screen:expect([[
+ {3:c :echo}|
+ |
+ {1:~ }|
+ {1:~ }|
+ :echo^ |
+ ]])
+ end)
+
+ it('window equalization with laststatus=0 #20367', function()
+ screen:try_resize(60, 9)
+ command('set cmdheight=0 laststatus=0')
+ command('vsplit')
+ screen:expect([[
+ ^ │ |
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ ]])
+ feed(':')
+ command('split')
+ feed('<Esc>')
+ screen:expect([[
+ ^ │ |
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {2:[No Name] }│{1:~ }|
+ │{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ ]])
+ command('resize 2')
+ screen:expect([[
+ ^ │ |
+ {1:~ }│{1:~ }|
+ {2:[No Name] }│{1:~ }|
+ │{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ ]])
+ feed(':')
+ command('wincmd =')
+ feed('<Esc>')
+ screen:expect([[
+ ^ │ |
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {2:[No Name] }│{1:~ }|
+ │{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ ]])
+ end)
+
+ it('no assert failure with showcmd', function()
+ command('set showcmd cmdheight=0')
+ feed('d')
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ assert_alive()
+ end)
+
+ it('can only be resized to 0 if set explicitly', function()
+ command('set laststatus=2')
+ command('resize +1')
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {2:[No Name] }|
+ |
+ ]])
+ command('set cmdheight=0')
+ command('resize -1')
+ command('resize +1')
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:[No Name] }|
+ ]])
+ end)
+
+ it("cannot be resized at all with external messages", function()
+ clear()
+ screen = new_screen({rgb=true, ext_messages=true})
+ command('set laststatus=2 mouse=a')
+ command('resize -1')
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {3:[No Name] }|
+ ]])
+ meths.input_mouse('left', 'press', '', 0, 6, 10)
+ poke_eventloop()
+ meths.input_mouse('left', 'drag', '', 0, 5, 10)
+ screen:expect_unchanged()
+ end)
end)
diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua
index 03cd4bfd06..e261f0dfab 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 = 62 end
end
-- Assert the new expectation.
diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua
index 789f1c6487..489c33d8b1 100644
--- a/test/functional/ui/decorations_spec.lua
+++ b/test/functional/ui/decorations_spec.lua
@@ -31,6 +31,8 @@ describe('decorations providers', function()
[12] = {foreground = tonumber('0x990000')};
[13] = {background = Screen.colors.LightBlue};
[14] = {background = Screen.colors.WebGray, foreground = Screen.colors.DarkBlue};
+ [15] = {special = Screen.colors.Blue1, undercurl = true},
+ [16] = {special = Screen.colors.Red, undercurl = true},
}
end)
@@ -56,7 +58,7 @@ describe('decorations providers', function()
a.nvim_set_decoration_provider(_G.ns1, {
on_start = on_do; on_buf = on_do;
on_win = on_do; on_line = on_do;
- on_end = on_do;
+ on_end = on_do; _on_spell_nav = on_do;
})
return _G.ns1
]])
@@ -95,7 +97,7 @@ describe('decorations providers', function()
|
]]}
check_trace {
- { "start", 4, 40 };
+ { "start", 4 };
{ "win", 1000, 1, 0, 8 };
{ "line", 1000, 1, 0 };
{ "line", 1000, 1, 1 };
@@ -119,7 +121,7 @@ describe('decorations providers', function()
|
]]}
check_trace {
- { "start", 5, 10 };
+ { "start", 5 };
{ "buf", 1 };
{ "win", 1000, 1, 0, 8 };
{ "line", 1000, 1, 6 };
@@ -156,6 +158,120 @@ describe('decorations providers', function()
]]}
end)
+ it('can indicate spellchecked points', function()
+ exec [[
+ set spell
+ set spelloptions=noplainbuffer
+ syntax off
+ ]]
+
+ insert [[
+ I am well written text.
+ i am not capitalized.
+ I am a speling mistakke.
+ ]]
+
+ setup_provider [[
+ local ns = a.nvim_create_namespace "spell"
+ beamtrace = {}
+ local function on_do(kind, ...)
+ if kind == 'win' or kind == 'spell' then
+ a.nvim_buf_set_extmark(0, ns, 0, 0, {
+ end_row = 2,
+ end_col = 23,
+ spell = true,
+ priority = 20,
+ ephemeral = true
+ })
+ end
+ table.insert(beamtrace, {kind, ...})
+ end
+ ]]
+
+ check_trace {
+ { "start", 5 };
+ { "win", 1000, 1, 0, 5 };
+ { "line", 1000, 1, 0 };
+ { "line", 1000, 1, 1 };
+ { "line", 1000, 1, 2 };
+ { "line", 1000, 1, 3 };
+ { "end", 5 };
+ }
+
+ feed "gg0"
+
+ screen:expect{grid=[[
+ ^I am well written text. |
+ {15:i} am not capitalized. |
+ I am a {16:speling} {16:mistakke}. |
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ feed "]s"
+ check_trace {
+ { "spell", 1000, 1, 1, 0, 1, -1 };
+ }
+ screen:expect{grid=[[
+ I am well written text. |
+ {15:^i} am not capitalized. |
+ I am a {16:speling} {16:mistakke}. |
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ feed "]s"
+ check_trace {
+ { "spell", 1000, 1, 2, 7, 2, -1 };
+ }
+ screen:expect{grid=[[
+ I am well written text. |
+ {15:i} am not capitalized. |
+ I am a {16:^speling} {16:mistakke}. |
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ -- spell=false with lower priority doesn't disable spell
+ local ns = meths.create_namespace "spell"
+ local id = helpers.curbufmeths.set_extmark(ns, 0, 0, { priority = 30, end_row = 2, end_col = 23, spell = false })
+
+ screen:expect{grid=[[
+ I am well written text. |
+ i am not capitalized. |
+ I am a ^speling mistakke. |
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ -- spell=false with higher priority does disable spell
+ helpers.curbufmeths.set_extmark(ns, 0, 0, { id = id, priority = 10, end_row = 2, end_col = 23, spell = false })
+
+ screen:expect{grid=[[
+ I am well written text. |
+ {15:i} am not capitalized. |
+ I am a {16:^speling} {16:mistakke}. |
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ end)
+
it('can predefine highlights', function()
screen:try_resize(40, 16)
insert(mulholland)
@@ -484,6 +600,7 @@ describe('extmark decorations', function()
[24] = {bold = true};
[25] = {background = Screen.colors.LightRed};
[26] = {background=Screen.colors.DarkGrey, foreground=Screen.colors.LightGrey};
+ [27] = {background = Screen.colors.Plum1};
}
ns = meths.create_namespace 'test'
@@ -606,15 +723,15 @@ end]]
screen:expect{grid=[[
{5:^for} _,item {5:in} {6:ipairs}(items) {5:do} |
- {5:local} text, hl_id_cell, count = unpack(item) |
- {5:if} hl_id_cell ~= {13:nil} {5:then} |
- hl_id = hl_id_cell |
+ {5:local} text, hl_id_cell, count {5:=} unpack(item) |
+ {5:if} hl_id_cell {5:~=} {13:nil} {5:then} |
+ hl_id {5:=} hl_id_cell |
{5:end} |
- {5:for} _ = {13:1}, (count {5:or} {13:1}) {5:do} |
- {5:local} cell = line[colpos] |
- cell.text = text |
- cell.hl_id = hl_id |
- colpos = colpos+{13:1} |
+ {5:for} _ {5:=} {13:1}, (count {5:or} {13:1}) {5:do} |
+ {5:local} cell {5:=} line[colpos] |
+ cell.text {5:=} text |
+ cell.hl_id {5:=} hl_id |
+ colpos {5:=} colpos{5:+}{13:1} |
{5:end} |
{5:end} |
{1:~ }|
@@ -633,15 +750,15 @@ end]]
screen:expect{grid=[[
{5:^for} _,item {5:in} {6:ipairs}(items) {5:do} |
- {5:l}{8:blen}{7:dy}{10:e}{7:text}{10:h}{7:-}{10:_}{7:here}ell, count = unpack(item) |
- {5:i}{12:c}{11:ombining color} {13:nil} {5:then} |
+ {5:l}{8:blen}{7:dy}{10:e}{7:text}{10:h}{7:-}{10:_}{7:here}ell, count {5:=} unpack(item) |
+ {5:i}{12:c}{11:ombining col}{12:or} {13:nil} {5:then} |
{11:replacing color}d_cell |
{5:e}{8:bl}{7:endy}{10: }{7:text}{10: }{7:-}{10: }{7:here} |
- {5:f}{12:co}{11:mbini}{16:n}{11:g color}t {5:or} {13:1}) {5:do} |
+ {5:f}{12:co}{11:mbi}{12:n}{11:i}{16:n}{11:g color}t {5:or} {13:1}) {5:do} |
{11:replacing color} line[colpos] |
- cell.text = text |
- cell.hl_id = hl_id |
- colpos = colpos+{13:1} |
+ cell.text {5:=} text |
+ cell.hl_id {5:=} hl_id |
+ colpos {5:=} colpos{5:+}{13:1} |
{5:end} |
{5:end} |
{1:~ }|
@@ -652,15 +769,15 @@ end]]
feed 'V5G'
screen:expect{grid=[[
{17:for}{18: _,item }{17:in}{18: }{19:ipairs}{18:(items) }{17:do} |
- {18: }{17:l}{20:blen}{21:dy}{22:e}{21:text}{22:h}{21:-}{22:_}{21:here}{18:ell, count = unpack(item)} |
- {18: }{17:i}{12:c}{11:ombining color}{18: }{23:nil}{18: }{17:then} |
+ {18: }{17:l}{20:blen}{21:dy}{22:e}{21:text}{22:h}{21:-}{22:_}{21:here}{18:ell, count }{17:=}{18: unpack(item)} |
+ {18: }{17:i}{12:c}{11:ombining col}{12:or}{18: }{23:nil}{18: }{17:then} |
{18: }{11:replacing color}{18:d_cell} |
{18: }{5:^e}{17:nd} |
- {5:f}{12:co}{11:mbini}{16:n}{11:g color}t {5:or} {13:1}) {5:do} |
+ {5:f}{12:co}{11:mbi}{12:n}{11:i}{16:n}{11:g color}t {5:or} {13:1}) {5:do} |
{11:replacing color} line[colpos] |
- cell.text = text |
- cell.hl_id = hl_id |
- colpos = colpos+{13:1} |
+ cell.text {5:=} text |
+ cell.hl_id {5:=} hl_id |
+ colpos {5:=} colpos{5:+}{13:1} |
{5:end} |
{5:end} |
{1:~ }|
@@ -671,15 +788,15 @@ end]]
feed 'jj'
screen:expect{grid=[[
{17:for}{18: _,item }{17:in}{18: }{19:ipairs}{18:(items) }{17:do} |
- {18: }{17:l}{20:blen}{21:dy}{22:e}{21:text}{22:h}{21:-}{22:_}{21:here}{18:ell, count = unpack(item)} |
- {18: }{17:i}{12:c}{11:ombining color}{18: }{23:nil}{18: }{17:then} |
+ {18: }{17:l}{20:blen}{21:dy}{22:e}{21:text}{22:h}{21:-}{22:_}{21:here}{18:ell, count }{17:=}{18: unpack(item)} |
+ {18: }{17:i}{12:c}{11:ombining col}{12:or}{18: }{23:nil}{18: }{17:then} |
{18: }{11:replacing color}{18:d_cell} |
{18: }{17:end} |
- {18: }{17:for}{18: _ = }{23:1}{18:, (count }{17:or}{18: }{23:1}{18:) }{17:do} |
- {18: }^ {18: }{17:local}{18: cell = line[colpos]} |
- cell.text = text |
- cell.hl_id = hl_id |
- colpos = colpos+{13:1} |
+ {18: }{17:for}{18: _ }{17:=}{18: }{23:1}{18:, (count }{17:or}{18: }{23:1}{18:) }{17:do} |
+ {18: }^ {18: }{17:local}{18: cell }{17:=}{18: line[colpos]} |
+ cell.text {5:=} text |
+ cell.hl_id {5:=} hl_id |
+ colpos {5:=} colpos{5:+}{13:1} |
{5:end} |
{5:end} |
{1:~ }|
@@ -879,6 +996,55 @@ end]]
|
]])
end)
+
+ it('avoids redraw issue #20651', function()
+ exec_lua[[
+ vim.cmd.normal'10oXXX'
+ vim.cmd.normal'gg'
+ local ns = vim.api.nvim_create_namespace('ns')
+
+ local bufnr = vim.api.nvim_create_buf(false, true)
+ vim.api.nvim_open_win(bufnr, false, { relative = 'win', height = 1, width = 1, row = 0, col = 0 })
+
+ vim.api.nvim_create_autocmd('CursorMoved', { callback = function()
+ local row = vim.api.nvim_win_get_cursor(0)[1] - 1
+ vim.api.nvim_buf_set_extmark(0, ns, row, 0, { id = 1 })
+ vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, {})
+ vim.schedule(function()
+ vim.api.nvim_buf_set_extmark(0, ns, row, 0, {
+ id = 1,
+ virt_text = {{'HELLO', 'Normal'}},
+ })
+ end)
+ end
+ })
+ ]]
+
+ for _ = 1, 3 do
+ helpers.sleep(10)
+ feed 'j'
+ end
+
+ screen:expect{grid=[[
+ {27: } |
+ XXX |
+ XXX |
+ ^XXX HELLO |
+ XXX |
+ XXX |
+ XXX |
+ XXX |
+ XXX |
+ XXX |
+ XXX |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ end)
+
end)
describe('decorations: virtual lines', function()
@@ -985,7 +1151,7 @@ if (h->n_buckets < new_n_buckets) { // expand
]]}
meths.buf_set_extmark(0, ns, 5, 0, {
- virt_lines = { {{"^^ REVIEW:", "Todo"}, {" new_vals variable seems unneccesary?", "Comment"}} };
+ virt_lines = { {{"^^ REVIEW:", "Todo"}, {" new_vals variable seems unnecessary?", "Comment"}} };
})
-- TODO: what about the cursor??
screen:expect{grid=[[
@@ -998,7 +1164,7 @@ if (h->n_buckets < new_n_buckets) { // expand
if (kh_is_map && val_size) { |
^char *new_vals = {3:krealloc}( h->vals_buf, new_n_|
buckets * val_size); |
- {5:^^ REVIEW:}{6: new_vals variable seems unneccesary?} |
+ {5:^^ REVIEW:}{6: new_vals variable seems unnecessary?} |
h->vals_buf = new_vals; |
|
]]}
@@ -1201,6 +1367,110 @@ if (h->n_buckets < new_n_buckets) { // expand
]]}
end)
+ it('works beyond end of the buffer with virt_lines_above', function()
+ insert(example_text)
+ feed 'G'
+
+ screen:expect{grid=[[
+ if (h->n_buckets < new_n_buckets) { // expand |
+ khkey_t *new_keys = (khkey_t *)krealloc((void *)|
+ h->keys, new_n_buckets * sizeof(khkey_t)); |
+ h->keys = new_keys; |
+ if (kh_is_map && val_size) { |
+ char *new_vals = krealloc( h->vals_buf, new_n_|
+ buckets * val_size); |
+ h->vals_buf = new_vals; |
+ } |
+ ^} |
+ {1:~ }|
+ |
+ ]]}
+
+ local id = meths.buf_set_extmark(0, ns, 8, 0, {
+ virt_lines={{{"Grugg"}}};
+ virt_lines_above = true,
+ })
+
+ screen:expect{grid=[[
+ if (h->n_buckets < new_n_buckets) { // expand |
+ khkey_t *new_keys = (khkey_t *)krealloc((void *)|
+ h->keys, new_n_buckets * sizeof(khkey_t)); |
+ h->keys = new_keys; |
+ if (kh_is_map && val_size) { |
+ char *new_vals = krealloc( h->vals_buf, new_n_|
+ buckets * val_size); |
+ h->vals_buf = new_vals; |
+ } |
+ ^} |
+ Grugg |
+ |
+ ]]}
+
+ feed('dd')
+ screen:expect{grid=[[
+ if (h->n_buckets < new_n_buckets) { // expand |
+ khkey_t *new_keys = (khkey_t *)krealloc((void *)|
+ h->keys, new_n_buckets * sizeof(khkey_t)); |
+ h->keys = new_keys; |
+ if (kh_is_map && val_size) { |
+ char *new_vals = krealloc( h->vals_buf, new_n_|
+ buckets * val_size); |
+ h->vals_buf = new_vals; |
+ ^} |
+ Grugg |
+ {1:~ }|
+ |
+ ]]}
+
+ feed('dk')
+ screen:expect{grid=[[
+ if (h->n_buckets < new_n_buckets) { // expand |
+ khkey_t *new_keys = (khkey_t *)krealloc((void *)|
+ h->keys, new_n_buckets * sizeof(khkey_t)); |
+ h->keys = new_keys; |
+ if (kh_is_map && val_size) { |
+ ^char *new_vals = krealloc( h->vals_buf, new_n_|
+ buckets * val_size); |
+ Grugg |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ feed('dgg')
+ screen:expect{grid=[[
+ ^ |
+ Grugg |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ --No lines in buffer-- |
+ ]]}
+
+ meths.buf_del_extmark(0, ns, id)
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ --No lines in buffer-- |
+ ]]}
+ 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')
@@ -1470,6 +1740,38 @@ if (h->n_buckets < new_n_buckets) { // expand
} |
|
]]}
+
+ command 'set number'
+ screen:expect{grid=[[
+ {9: 1 }^if (h->n_buckets < new_n_buckets) { // expand |
+ {9: 2 } khkey_t *new_keys = (khkey_t *)krealloc((voi|
+ {9: }d *)h->keys, new_n_buckets * sizeof(khkey_t));|
+ {9: }{1:>>}{2: very tabby}text with tabs |
+ {9: 3 } h->keys = new_keys; |
+ {9: 4 } if (kh_is_map && val_size) { |
+ {9: 5 } char *new_vals = krealloc( h->vals_buf, ne|
+ {9: }w_n_buckets * val_size); |
+ {9: 6 } h->vals_buf = new_vals; |
+ {9: 7 } } |
+ {9: 8 }} |
+ |
+ ]]}
+
+ command 'set tabstop&'
+ screen:expect{grid=[[
+ {9: 1 }^if (h->n_buckets < new_n_buckets) { // expand |
+ {9: 2 } khkey_t *new_keys = (khkey_t *)krealloc((voi|
+ {9: }d *)h->keys, new_n_buckets * sizeof(khkey_t));|
+ {9: }{1:>>}{2: very tabby}text with tabs |
+ {9: 3 } h->keys = new_keys; |
+ {9: 4 } if (kh_is_map && val_size) { |
+ {9: 5 } char *new_vals = krealloc( h->vals_buf, ne|
+ {9: }w_n_buckets * val_size); |
+ {9: 6 } h->vals_buf = new_vals; |
+ {9: 7 } } |
+ {9: 8 }} |
+ |
+ ]]}
end)
end)
@@ -1869,4 +2171,39 @@ describe('decorations: virt_text', function()
]]}
end)
+ it('redraws correctly when re-using extmark ids', function()
+ command 'normal 5ohello'
+
+ screen:expect{grid=[[
+ |
+ hello |
+ hello |
+ hello |
+ hello |
+ hell^o |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ |
+ ]]}
+
+ local ns = meths.create_namespace('ns')
+ for row = 1, 5 do
+ meths.buf_set_extmark(0, ns, row, 0, { id = 1, virt_text = {{'world', 'Normal'}} })
+ end
+
+ screen:expect{grid=[[
+ |
+ hello |
+ hello |
+ hello |
+ hello |
+ hell^o world |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ |
+ ]]}
+ end)
+
end)
diff --git a/test/functional/ui/diff_spec.lua b/test/functional/ui/diff_spec.lua
index 36dc5addcd..dbdf3823ec 100644
--- a/test/functional/ui/diff_spec.lua
+++ b/test/functional/ui/diff_spec.lua
@@ -8,6 +8,8 @@ local insert = helpers.insert
local write_file = helpers.write_file
local dedent = helpers.dedent
local exec = helpers.exec
+local eq = helpers.eq
+local meths = helpers.meths
describe('Diff mode screen', function()
local fname = 'Xtest-functional-diff-screen-1'
@@ -1073,6 +1075,182 @@ int main(int argc, char **argv)
:e |
]])
end)
+
+ describe('line matching diff algorithm', function()
+ setup(function()
+ local f1 = [[if __name__ == "__main__":
+ import sys
+ app = QWidgets.QApplication(sys.args)
+ MainWindow = QtWidgets.QMainWindow()
+ ui = UI_MainWindow()
+ ui.setupUI(MainWindow)
+ MainWindow.show()
+ sys.exit(app.exec_())]]
+ write_file(fname, f1, false)
+ local f2 = [[if __name__ == "__main__":
+ import sys
+ comment these things
+ #app = QWidgets.QApplication(sys.args)
+ #MainWindow = QtWidgets.QMainWindow()
+ add a completely different line here
+ #ui = UI_MainWindow()
+ add another new line
+ ui.setupUI(MainWindow)
+ MainWindow.show()
+ sys.exit(app.exec_())]]
+ write_file(fname_2, f2, false)
+ end)
+
+ it('diffopt+=linematch:20', function()
+ reread()
+ feed(':set diffopt=internal,filler<cr>')
+ screen:expect([[
+ {1: }^if __name__ == "__│{1: }if __name__ == "_|
+ {1: } import sys │{1: } import sys |
+ {1: }{9: }{8:app = QWidgets}│{1: }{9: }{8:comment these}|
+ {1: }{9: }{8:MainWindow = Q}│{1: }{9: }{8:#app = QWidge}|
+ {1: }{9: }{8:ui = UI_}{9:MainWi}│{1: }{9: }{8:#MainWindow =}|
+ {1: }{2:------------------}│{1: }{4: add a complet}|
+ {1: }{2:------------------}│{1: }{4: #ui = UI_Main}|
+ {1: }{2:------------------}│{1: }{4: add another n}|
+ {1: } ui.setupUI(Mai│{1: } ui.setupUI(Ma|
+ {1: } MainWindow.sho│{1: } MainWindow.sh|
+ {1: } sys.exit(app.e│{1: } sys.exit(app.|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ :set diffopt=internal,filler |
+ ]])
+
+ feed('G')
+ feed(':set diffopt+=linematch:20<cr>')
+ screen:expect([[
+ {1: }if __name__ == "__│{1: }if __name__ == "_|
+ {1: } import sys │{1: } import sys |
+ {1: }{2:------------------}│{1: }{4: comment these}|
+ {1: }{9: app = QWidgets}│{1: }{9: }{8:#}{9:app = QWidge}|
+ {1: }{9: MainWindow = Q}│{1: }{9: }{8:#}{9:MainWindow =}|
+ {1: }{2:------------------}│{1: }{4: add a complet}|
+ {1: }{9: ui = UI_MainWi}│{1: }{9: }{8:#}{9:ui = UI_Main}|
+ {1: }{2:------------------}│{1: }{4: add another n}|
+ {1: } ui.setupUI(Mai│{1: } ui.setupUI(Ma|
+ {1: } MainWindow.sho│{1: } MainWindow.sh|
+ {1: } ^sys.exit(app.e│{1: } sys.exit(app.|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ :set diffopt+=linematch:20 |
+ ]])
+ end)
+ end)
+
+ describe('line matching diff algorithm with icase', function()
+ setup(function()
+ local f1 = [[DDD
+_aa]]
+ write_file(fname, f1, false)
+ local f2 = [[DDD
+AAA
+ccca]]
+ write_file(fname_2, f2, false)
+ end)
+ it('diffopt+=linematch:20,icase', function()
+ reread()
+ feed(':set diffopt=internal,filler,linematch:20<cr>')
+ screen:expect([[
+ {1: }^DDD │{1: }DDD |
+ {1: }{2:------------------}│{1: }{4:AAA }|
+ {1: }{8:_a}{9:a }│{1: }{8:ccc}{9:a }|
+ {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 }|
+ |
+ ]])
+ feed(':set diffopt+=icase<cr>')
+ screen:expect([[
+ {1: }^DDD │{1: }DDD |
+ {1: }{8:_}{9:aa }│{1: }{8:A}{9:AA }|
+ {1: }{2:------------------}│{1: }{4:ccca }|
+ {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+=icase |
+ ]])
+ end)
+ end)
+
+ describe('line matching diff algorithm with iwhiteall', function()
+ setup(function()
+ local f1 = [[BB
+ AAA]]
+ write_file(fname, f1, false)
+ local f2 = [[BB
+ AAB
+AAAB]]
+ write_file(fname_2, f2, false)
+ end)
+ it('diffopt+=linematch:20,iwhiteall', function()
+ reread()
+ feed(':set diffopt=internal,filler,linematch:20<cr>')
+ screen:expect{grid=[[
+ {1: }^BB │{1: }BB |
+ {1: }{9: AA}{8:A}{9: }│{1: }{9: AA}{8:B}{9: }|
+ {1: }{2:------------------}│{1: }{4:AAAB }|
+ {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 }|
+ |
+ ]]}
+ feed(':set diffopt+=iwhiteall<cr>')
+ screen:expect{grid=[[
+ {1: }^BB │{1: }BB |
+ {1: }{2:------------------}│{1: }{4: AAB }|
+ {1: }{9: AAA }│{1: }{9:AAA}{8:B}{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+=iwhiteall |
+ ]]}
+ end)
+ end)
end)
it('win_update redraws lines properly', function()
@@ -1315,6 +1493,26 @@ it('Align the filler lines when changing text in diff mode', function()
]]}
end)
+it("diff mode doesn't restore invalid 'foldcolumn' value #21647", function()
+ clear()
+ local screen = Screen.new(60, 6)
+ screen:set_default_attr_ids({
+ [0] = {foreground = Screen.colors.Blue, bold = true};
+ })
+ screen:attach()
+ eq('0', meths.get_option_value('foldcolumn', {}))
+ command('diffsplit | bd')
+ screen:expect([[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ eq('0', meths.get_option_value('foldcolumn', {}))
+end)
+
-- oldtest: Test_diff_binary()
it('diff mode works properly if file contains NUL bytes vim-patch:8.2.3925', function()
clear()
diff --git a/test/functional/ui/embed_spec.lua b/test/functional/ui/embed_spec.lua
index 92f5beebf5..cd2b48213d 100644
--- a/test/functional/ui/embed_spec.lua
+++ b/test/functional/ui/embed_spec.lua
@@ -51,7 +51,7 @@ local function test_embed(ext_linegrid)
end)
it("doesn't erase output when setting color scheme", function()
- if 'openbsd' == helpers.uname() then
+ if helpers.is_os('openbsd') then
pending('FIXME #10804')
end
startup('--cmd', 'echoerr "foo"', '--cmd', 'color default', '--cmd', 'echoerr "bar"')
@@ -82,20 +82,6 @@ local function test_embed(ext_linegrid)
eq(Screen.colors.Green, screen.default_colors.rgb_bg)
end}
end)
-
- it("set display-=msgsep before first redraw", function()
- startup('--cmd', 'set display-=msgsep')
- screen:expect{grid=[[
- ^ |
- {3:~ }|
- {3:~ }|
- {3:~ }|
- {3:~ }|
- {3:~ }|
- {3:~ }|
- |
- ]]}
- end)
end
describe('--embed UI on startup (ext_linegrid=true)', function() test_embed(true) end)
diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua
index 5967b630f6..6759510ad1 100644
--- a/test/functional/ui/float_spec.lua
+++ b/test/functional/ui/float_spec.lua
@@ -25,36 +25,6 @@ describe('float window', function()
clear()
command('hi VertSplit gui=reverse')
end)
- local attrs = {
- [0] = {bold=true, foreground=Screen.colors.Blue},
- [1] = {background = Screen.colors.LightMagenta},
- [2] = {background = Screen.colors.LightMagenta, bold = true, foreground = Screen.colors.Blue1},
- [3] = {bold = true},
- [4] = {bold = true, reverse = true},
- [5] = {reverse = true},
- [6] = {background = Screen.colors.LightMagenta, bold = true, reverse = true},
- [7] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
- [8] = {bold = true, foreground = Screen.colors.SeaGreen4},
- [9] = {background = Screen.colors.LightGrey, underline = true},
- [10] = {background = Screen.colors.LightGrey, underline = true, bold = true, foreground = Screen.colors.Magenta},
- [11] = {bold = true, foreground = Screen.colors.Magenta},
- [12] = {background = Screen.colors.Red, bold = true, foreground = Screen.colors.Blue1},
- [13] = {background = Screen.colors.WebGray},
- [14] = {foreground = Screen.colors.Brown},
- [15] = {background = Screen.colors.Grey20},
- [16] = {background = Screen.colors.Grey20, bold = true, foreground = Screen.colors.Blue1},
- [17] = {background = Screen.colors.Yellow},
- [18] = {foreground = Screen.colors.Brown, background = Screen.colors.Grey20},
- [19] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.WebGray},
- [20] = {bold = true, foreground = Screen.colors.Brown},
- [21] = {background = Screen.colors.Gray90},
- [22] = {background = Screen.colors.LightRed},
- [23] = {foreground = Screen.colors.Black, background = Screen.colors.White};
- [24] = {foreground = Screen.colors.Black, background = Screen.colors.Grey80};
- [25] = {blend = 100, background = Screen.colors.Gray0};
- [26] = {blend = 80, background = Screen.colors.Gray0};
- [27] = {background = Screen.colors.LightGray};
- }
it('behavior', function()
-- Create three windows and test that ":wincmd <direction>" changes to the
@@ -93,7 +63,7 @@ describe('float window', function()
end)
it('closed immediately by autocmd #11383', function()
- eq('Error executing lua: [string "<nvim>"]:0: Window was closed immediately',
+ eq('Window was closed immediately',
pcall_err(exec_lua, [[
local a = vim.api
local function crashes(contents)
@@ -118,7 +88,7 @@ describe('float window', function()
end)
it('closed immediately by autocmd after win_enter #15548', function()
- eq('Error executing lua: [string "<nvim>"]:0: Window was closed immediately',
+ eq('Window was closed immediately',
pcall_err(exec_lua, [[
vim.cmd "autocmd BufLeave * ++once quit!"
local buf = vim.api.nvim_create_buf(true, true)
@@ -198,6 +168,29 @@ describe('float window', function()
eq(7, pos[2])
end)
+ it('opened with correct position relative to the mouse', function()
+ meths.input_mouse('left', 'press', '', 0, 10, 10)
+ local pos = exec_lua([[
+ local bufnr = vim.api.nvim_create_buf(false, true)
+
+ local opts = {
+ width = 10,
+ height = 10,
+ col = 1,
+ row = 2,
+ relative = 'mouse',
+ style = 'minimal'
+ }
+
+ local win_id = vim.api.nvim_open_win(bufnr, false, opts)
+
+ return vim.api.nvim_win_get_position(win_id)
+ ]])
+
+ eq(12, pos[1])
+ eq(11, pos[2])
+ end)
+
it('opened with correct position relative to the cursor', function()
local pos = exec_lua([[
local bufnr = vim.api.nvim_create_buf(false, true)
@@ -421,6 +414,15 @@ describe('float window', function()
eq(winids, eval('winids'))
end)
+ it('no crash with bufpos and non-existent window', function()
+ command('new')
+ local closed_win = meths.get_current_win().id
+ command('close')
+ local buf = meths.create_buf(false,false)
+ meths.open_win(buf, true, {relative='win', win=closed_win, width=1, height=1, bufpos={0,0}})
+ assert_alive()
+ end)
+
it("no segfault when setting minimal style after clearing local 'fillchars' #19510", function()
local float_opts = {relative = 'editor', row = 1, col = 1, width = 1, height = 1}
local float_win = meths.open_win(0, true, float_opts)
@@ -430,6 +432,13 @@ describe('float window', function()
assert_alive()
end)
+ it("'scroll' is computed correctly when opening float with splitkeep=screen #20684", function()
+ meths.set_option('splitkeep', 'screen')
+ local float_opts = {relative = 'editor', row = 1, col = 1, width = 10, height = 10}
+ local float_win = meths.open_win(0, true, float_opts)
+ eq(5, meths.win_get_option(float_win, 'scroll'))
+ 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
@@ -569,7 +578,7 @@ describe('float window', function()
end)
end)
- describe('with mulitple tabpages but only one listed buffer,', function()
+ describe('with multiple 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()
@@ -596,6 +605,11 @@ describe('float window', function()
meths.buf_delete(old_buf, {force = true})
eq(old_win, curwin().id)
end)
+ it('if called from non-floating window in another tabpage', function()
+ command('tab split')
+ eq(3, #meths.list_tabpages())
+ meths.buf_delete(old_buf, {force = true})
+ 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()')
@@ -712,10 +726,41 @@ describe('float window', function()
end)
local function with_ext_multigrid(multigrid)
- local screen
+ local screen, attrs
before_each(function()
screen = Screen.new(40,7)
screen:attach {ext_multigrid=multigrid}
+ attrs = {
+ [0] = {bold=true, foreground=Screen.colors.Blue},
+ [1] = {background = Screen.colors.LightMagenta},
+ [2] = {background = Screen.colors.LightMagenta, bold = true, foreground = Screen.colors.Blue1},
+ [3] = {bold = true},
+ [4] = {bold = true, reverse = true},
+ [5] = {reverse = true},
+ [6] = {background = Screen.colors.LightMagenta, bold = true, reverse = true},
+ [7] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ [8] = {bold = true, foreground = Screen.colors.SeaGreen4},
+ [9] = {background = Screen.colors.LightGrey, underline = true},
+ [10] = {background = Screen.colors.LightGrey, underline = true, bold = true, foreground = Screen.colors.Magenta},
+ [11] = {bold = true, foreground = Screen.colors.Magenta},
+ [12] = {background = Screen.colors.Red, bold = true, foreground = Screen.colors.Blue1},
+ [13] = {background = Screen.colors.WebGray},
+ [14] = {foreground = Screen.colors.Brown},
+ [15] = {background = Screen.colors.Grey20},
+ [16] = {background = Screen.colors.Grey20, bold = true, foreground = Screen.colors.Blue1},
+ [17] = {background = Screen.colors.Yellow},
+ [18] = {foreground = Screen.colors.Brown, background = Screen.colors.Grey20},
+ [19] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.WebGray},
+ [20] = {bold = true, foreground = Screen.colors.Brown},
+ [21] = {background = Screen.colors.Gray90},
+ [22] = {background = Screen.colors.LightRed},
+ [23] = {foreground = Screen.colors.Black, background = Screen.colors.White};
+ [24] = {foreground = Screen.colors.Black, background = Screen.colors.Grey80};
+ [25] = {blend = 100, background = Screen.colors.Gray0};
+ [26] = {blend = 80, background = Screen.colors.Gray0};
+ [27] = {background = Screen.colors.LightGray};
+ [28] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGray};
+ }
screen:set_default_attr_ids(attrs)
end)
@@ -1307,6 +1352,54 @@ describe('float window', function()
end
end)
+ it("would not break 'minimal' style with statuscolumn set", function()
+ command('set number')
+ command('set signcolumn=yes')
+ command('set colorcolumn=1')
+ command('set cursorline')
+ command('set foldcolumn=1')
+ command('set statuscolumn=%l%s%C')
+ command('hi NormalFloat guibg=#333333')
+ feed('ix<cr>y<cr><esc>gg')
+ meths.open_win(0, false, {relative='editor', width=20, height=4, row=4, col=10, style='minimal'})
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [3:----------------------------------------]|
+ ## grid 2
+ {20:1}{19: }{20: }{22:^x}{21: }|
+ {14:2}{19: }{14: }{22:y} |
+ {14:3}{19: }{14: }{22: } |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ ## grid 4
+ {15:x }|
+ {15:y }|
+ {15: }|
+ {15: }|
+ ]], float_pos={[4] = {{id = 1001}, "NW", 1, 4, 10, true}}}
+ else
+ screen:expect{grid=[[
+ {20:1}{19: }{20: }{22:^x}{21: }|
+ {14:2}{19: }{14: }{22:y} |
+ {14:3}{19: }{14: }{22: } {15:x } |
+ {0:~ }{15:y }{0: }|
+ {0:~ }{15: }{0: }|
+ {0:~ }{15: }{0: }|
+ |
+ ]]}
+ end
+ end)
+
it('can have border', function()
local buf = meths.create_buf(false, false)
meths.buf_set_lines(buf, 0, -1, true, {' halloj! ',
@@ -1704,6 +1797,222 @@ describe('float window', function()
end
end)
+ it('validates title title_pos', function()
+ local buf = meths.create_buf(false,false)
+ eq("title requires border to be set",
+ pcall_err(meths.open_win,buf, false, {
+ relative='editor', width=9, height=2, row=2, col=5, title='Title',
+ }))
+ eq("title_pos requires title to be set",
+ pcall_err(meths.open_win,buf, false, {
+ relative='editor', width=9, height=2, row=2, col=5,
+ border='single', title_pos='left',
+ }))
+ end)
+
+ it('validate title_pos in nvim_win_get_config', function()
+ local title_pos = exec_lua([[
+ local bufnr = vim.api.nvim_create_buf(false, false)
+ local opts = {
+ relative = 'editor',
+ col = 2,
+ row = 5,
+ height = 2,
+ width = 9,
+ border = 'double',
+ title = 'Test',
+ title_pos = 'center'
+ }
+
+ local win_id = vim.api.nvim_open_win(bufnr, true, opts)
+ return vim.api.nvim_win_get_config(win_id).title_pos
+ ]])
+
+ eq('center', title_pos)
+ end)
+
+
+ it('border with title', function()
+ local buf = meths.create_buf(false, false)
+ meths.buf_set_lines(buf, 0, -1, true, {' halloj! ',
+ ' BORDAA '})
+ local win = meths.open_win(buf, false, {
+ relative='editor', width=9, height=2, row=2, col=5, border="double",
+ title = "Left",title_pos = "left",
+ })
+
+ 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:╔}{11:Left}{5:═════╗}|
+ {5:║}{1: halloj! }{5:║}|
+ {5:║}{1: BORDAA }{5:║}|
+ {5:╚═════════╝}|
+ ]], float_pos={
+ [5] = { { id = 1002 }, "NW", 1, 2, 5, true }
+ }, win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1};
+ [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2};
+ }}
+ else
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {0:~ }{5:╔}{11:Left}{5:═════╗}{0: }|
+ {0:~ }{5:║}{1: halloj! }{5:║}{0: }|
+ {0:~ }{5:║}{1: BORDAA }{5:║}{0: }|
+ {0:~ }{5:╚═════════╝}{0: }|
+ |
+ ]]}
+ end
+
+ meths.win_set_config(win, {title= "Center",title_pos="center"})
+ 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:╔═}{11:Center}{5:══╗}|
+ {5:║}{1: halloj! }{5:║}|
+ {5:║}{1: BORDAA }{5:║}|
+ {5:╚═════════╝}|
+ ]], float_pos={
+ [5] = { { id = 1002 }, "NW", 1, 2, 5, true }
+ }, win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1};
+ [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2};
+ }}
+ else
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {0:~ }{5:╔═}{11:Center}{5:══╗}{0: }|
+ {0:~ }{5:║}{1: halloj! }{5:║}{0: }|
+ {0:~ }{5:║}{1: BORDAA }{5:║}{0: }|
+ {0:~ }{5:╚═════════╝}{0: }|
+ |
+ ]]}
+ end
+
+ meths.win_set_config(win, {title= "Right",title_pos="right"})
+ 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:╔════}{11:Right}{5:╗}|
+ {5:║}{1: halloj! }{5:║}|
+ {5:║}{1: BORDAA }{5:║}|
+ {5:╚═════════╝}|
+ ]], float_pos={
+ [5] = { { id = 1002 }, "NW", 1, 2, 5, true }
+ }, win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1};
+ [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2};
+ }}
+ else
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {0:~ }{5:╔════}{11:Right}{5:╗}{0: }|
+ {0:~ }{5:║}{1: halloj! }{5:║}{0: }|
+ {0:~ }{5:║}{1: BORDAA }{5:║}{0: }|
+ {0:~ }{5:╚═════════╝}{0: }|
+ |
+ ]]}
+ end
+
+ meths.win_set_config(win, {title= { {"🦄"},{"BB"}},title_pos="right"})
+ 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:╔═════}🦄BB{5:╗}|
+ {5:║}{1: halloj! }{5:║}|
+ {5:║}{1: BORDAA }{5:║}|
+ {5:╚═════════╝}|
+ ]], float_pos={
+ [5] = { { id = 1002 }, "NW", 1, 2, 5, true }
+ }, win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1};
+ [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2};
+ }}
+ else
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {0:~ }{5:╔═════}🦄BB{5:╗}{0: }|
+ {0:~ }{5:║}{1: halloj! }{5:║}{0: }|
+ {0:~ }{5:║}{1: BORDAA }{5:║}{0: }|
+ {0:~ }{5:╚═════════╝}{0: }|
+ |
+ ]]}
+ end
+ end)
+
it('terminates border on edge of viewport when window extends past viewport', function()
local buf = meths.create_buf(false, false)
meths.open_win(buf, false, {relative='editor', width=40, height=7, row=0, col=0, border="single"})
@@ -2756,7 +3065,7 @@ describe('float window', function()
}, "NW", 2, 1, 32, true }
}}
else
- -- note: appears misalinged due to cursor
+ -- note: appears misaligned due to cursor
screen:expect{grid=[[
^example text that is wider than the window |
{1:some info! } |
@@ -2862,6 +3171,66 @@ describe('float window', function()
]]}
end
+ command('set laststatus=0')
+ command('botright vnew')
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----]{5:│}[6:--------------------]|
+ [2:----]{5:│}[6:--------------------]|
+ [2:----]{5:│}[6:--------------------]|
+ [2:----]{5:│}[6:--------------------]|
+ [2:----]{5:│}[6:--------------------]|
+ [2:----]{5:│}[6:--------------------]|
+ [2:----]{5:│}[6:--------------------]|
+ [2:----]{5:│}[6:--------------------]|
+ [2:----]{5:│}[6:--------------------]|
+ [3:-------------------------]|
+ ## grid 2
+ exam|
+ ple |
+ text|
+ tha|
+ t is|
+ wid|
+ er t|
+ han |
+ the |
+ ## grid 3
+ |
+ ## grid 5
+ {1:some info! }|
+ ## grid 6
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ]], float_pos={
+ [5] = { {
+ id = 1002
+ }, "SW", 2, 8, 0, true }
+ }}
+ else
+ screen:expect{grid=[[
+ exam{5:│}^ |
+ ple {5:│}{0:~ }|
+ text{5:│}{0:~ }|
+ tha{5:│}{0:~ }|
+ t is{5:│}{0:~ }|
+ wid{5:│}{0:~ }|
+ er t{5:│}{0:~ }|
+ {1:some info! }{0: }|
+ the {5:│}{0:~ }|
+ |
+ ]]}
+ end
+ command('close')
+
meths.win_set_config(win, {relative='win', bufpos={1,32}, anchor='NW', col=-2})
if multigrid then
screen:expect{grid=[[
@@ -2957,6 +3326,54 @@ describe('float window', function()
|
]]}
end
+
+ command('%fold')
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:-------------------------]|
+ [2:-------------------------]|
+ [2:-------------------------]|
+ [2:-------------------------]|
+ [2:-------------------------]|
+ [2:-------------------------]|
+ [2:-------------------------]|
+ [2:-------------------------]|
+ [2:-------------------------]|
+ [3:-------------------------]|
+ ## grid 2
+ {28:^+-- 5 lines: just some··}|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ ## grid 5
+ {1:some info! }|
+ ]], float_pos={
+ [5] = { {
+ id = 1002
+ }, "NW", 2, 2, 0, true }
+ }}
+ else
+ screen:expect{grid=[[
+ {28:^+-- 5 lines: just some··}|
+ {0:~ }|
+ {1:some info! }{0: }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]]}
+ end
end)
it('validates cursor even when window is not entered', function()
@@ -8147,6 +8564,394 @@ describe('float window', function()
]]}
end
end)
+
+ it('it can be resized with messages and cmdheight=0 #20106', function()
+ screen:try_resize(40,9)
+ command 'set cmdheight=0'
+ local buf = meths.create_buf(false,true)
+ local win = meths.open_win(buf, false, {relative='editor', width=40, height=4, anchor='SW', row=9, col=0, style='minimal', border="single", noautocmd=true})
+
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ ## grid 2
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ ## grid 5
+ {5:┌────────────────────────────────────────┐}|
+ {5:│}{1: }{5:│}|
+ {5:│}{1: }{5:│}|
+ {5:│}{1: }{5:│}|
+ {5:│}{1: }{5:│}|
+ {5:└────────────────────────────────────────┘}|
+ ]], float_pos={
+ [5] = {{id = 1002}, "SW", 1, 9, 0, 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 = 2, curline = 0, curcol = 0, linecount = 1};
+ }}
+ else
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {5:┌──────────────────────────────────────┐}|
+ {5:│}{1: }{5:│}|
+ {5:│}{1: }{5:│}|
+ {5:│}{1: }{5:│}|
+ {5:│}{1: }{5:│}|
+ {5:└──────────────────────────────────────┘}|
+ ]]}
+ end
+
+ exec_lua([[
+ local win = ...
+ vim.api.nvim_win_set_height(win, 2)
+ vim.api.nvim_echo({ { "" } }, false, {})
+ ]], win)
+
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ ## grid 2
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ ## grid 5
+ {5:┌────────────────────────────────────────┐}|
+ {5:│}{1: }{5:│}|
+ {5:│}{1: }{5:│}|
+ {5:└────────────────────────────────────────┘}|
+ ]], float_pos={
+ [5] = {{id = 1002}, "SW", 1, 9, 0, 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 = 2, curline = 0, curcol = 0, linecount = 1};
+ }}
+ else
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {5:┌──────────────────────────────────────┐}|
+ {5:│}{1: }{5:│}|
+ {5:│}{1: }{5:│}|
+ {5:└──────────────────────────────────────┘}|
+ ]]}
+
+ end
+
+ meths.win_close(win, true)
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ ## grid 2
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ ]], win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1};
+ }}
+ else
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ]]}
+ end
+ end)
+
+ it('it can be resized with messages and cmdheight=1', function()
+ screen:try_resize(40,9)
+ local buf = meths.create_buf(false,true)
+ local win = meths.open_win(buf, false, {relative='editor', width=40, height=4, anchor='SW', row=8, col=0, style='minimal', border="single", noautocmd=true})
+
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [3:----------------------------------------]|
+ ## grid 2
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ ## grid 5
+ {5:┌────────────────────────────────────────┐}|
+ {5:│}{1: }{5:│}|
+ {5:│}{1: }{5:│}|
+ {5:│}{1: }{5:│}|
+ {5:│}{1: }{5:│}|
+ {5:└────────────────────────────────────────┘}|
+ ]], float_pos={
+ [5] = {{id = 1002}, "SW", 1, 8, 0, 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 = 2, curline = 0, curcol = 0, linecount = 1};
+ }}
+ else
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {5:┌──────────────────────────────────────┐}|
+ {5:│}{1: }{5:│}|
+ {5:│}{1: }{5:│}|
+ {5:│}{1: }{5:│}|
+ {5:│}{1: }{5:│}|
+ {5:└──────────────────────────────────────┘}|
+ |
+ ]]}
+ end
+
+ exec_lua([[
+ -- echo prompt is blocking, so schedule
+ local win = ...
+ vim.schedule(function()
+ vim.api.nvim_win_set_height(win, 2)
+ vim.api.nvim_echo({ { "\n" } }, false, {})
+ end)
+ ]], win)
+
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [3:----------------------------------------]|
+ [3:----------------------------------------]|
+ ## grid 2
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ {8:Press ENTER or type command to continue}^ |
+ ## grid 5
+ {5:┌────────────────────────────────────────┐}|
+ {5:│}{1: }{5:│}|
+ {5:│}{1: }{5:│}|
+ {5:│}{1: }{5:│}|
+ {5:│}{1: }{5:│}|
+ {5:└────────────────────────────────────────┘}|
+ ]], float_pos={
+ [5] = {{id = 1002}, "SW", 1, 8, 0, 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 = 2, curline = 0, curcol = 0, linecount = 1};
+ }}
+ else
+ screen:expect{grid=[[
+ |
+ {0:~ }|
+ {5:┌──────────────────────────────────────┐}|
+ {5:│}{1: }{5:│}|
+ {5:│}{1: }{5:│}|
+ {5:│}{1: }{5:│}|
+ {4: }|
+ |
+ {8:Press ENTER or type command to continue}^ |
+ ]]}
+ end
+
+ feed('<cr>')
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [3:----------------------------------------]|
+ ## grid 2
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ ## grid 5
+ {5:┌────────────────────────────────────────┐}|
+ {5:│}{1: }{5:│}|
+ {5:│}{1: }{5:│}|
+ {5:└────────────────────────────────────────┘}|
+ ]], float_pos={
+ [5] = {{id = 1002}, "SW", 1, 8, 0, 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 = 2, curline = 0, curcol = 0, linecount = 1};
+ }}
+ else
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {5:┌──────────────────────────────────────┐}|
+ {5:│}{1: }{5:│}|
+ {5:│}{1: }{5:│}|
+ {5:└──────────────────────────────────────┘}|
+ |
+ ]]}
+ end
+
+ meths.win_close(win, true)
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [3:----------------------------------------]|
+ ## grid 2
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ ]], win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1};
+ }}
+
+ else
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]]}
+ end
+ end)
+
+ describe('no crash after moving and closing float window #21547', function()
+ local function test_float_move_close(cmd)
+ local float_opts = {relative = 'editor', row = 1, col = 1, width = 10, height = 10}
+ meths.open_win(meths.create_buf(false, false), true, float_opts)
+ if multigrid then
+ screen:expect({float_pos = {[4] = {{id = 1001}, 'NW', 1, 1, 1, true}}})
+ end
+ command(cmd)
+ exec_lua([[
+ vim.api.nvim_win_set_config(0, {relative = 'editor', row = 2, col = 2})
+ vim.api.nvim_win_close(0, {})
+ vim.api.nvim_echo({{''}}, false, {})
+ ]])
+ if multigrid then
+ screen:expect({float_pos = {}})
+ end
+ assert_alive()
+ end
+
+ it('if WinClosed autocommand flushes UI', function()
+ test_float_move_close('autocmd WinClosed * ++once redraw')
+ end)
+
+ it('if closing buffer flushes UI', function()
+ test_float_move_close('autocmd BufWinLeave * ++once redraw')
+ 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 6bb8bb81c6..46a478c1ea 100644
--- a/test/functional/ui/fold_spec.lua
+++ b/test/functional/ui/fold_spec.lua
@@ -4,9 +4,11 @@ local clear, feed, eq = helpers.clear, helpers.feed, helpers.eq
local command = helpers.command
local feed_command = helpers.feed_command
local insert = helpers.insert
+local expect = helpers.expect
local funcs = helpers.funcs
local meths = helpers.meths
-local source = helpers.source
+local exec = helpers.exec
+local exec_lua = helpers.exec_lua
local assert_alive = helpers.assert_alive
@@ -198,50 +200,6 @@ describe("folded lines", function()
end
end)
- it("highlighting with relative line numbers", function()
- command("set relativenumber cursorline cursorlineopt=number foldmethod=marker")
- feed_command("set foldcolumn=2")
- funcs.setline(1, '{{{1')
- funcs.setline(2, 'line 1')
- funcs.setline(3, '{{{1')
- funcs.setline(4, 'line 2')
- feed("j")
- if multigrid then
- screen:expect([[
- ## grid 1
- [2:---------------------------------------------]|
- [2:---------------------------------------------]|
- [2:---------------------------------------------]|
- [2:---------------------------------------------]|
- [2:---------------------------------------------]|
- [2:---------------------------------------------]|
- [2:---------------------------------------------]|
- [3:---------------------------------------------]|
- ## grid 2
- {7:+ }{8: 1 }{5:+-- 2 lines: ·························}|
- {7:+ }{9: 0 }{5:^+-- 2 lines: ·························}|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- ## grid 3
- :set foldcolumn=2 |
- ]])
- else
- screen:expect([[
- {7:+ }{8: 1 }{5:+-- 2 lines: ·························}|
- {7:+ }{9: 0 }{5:^+-- 2 lines: ·························}|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- :set foldcolumn=2 |
- ]])
- end
- end)
-
it("work with spell", function()
command("set spell")
insert(content1)
@@ -1713,7 +1671,7 @@ describe("folded lines", function()
end)
it('does not crash when foldtext is longer than columns #12988', function()
- source([[
+ exec([[
function! MyFoldText() abort
return repeat('-', &columns + 100)
endfunction
@@ -1760,7 +1718,7 @@ describe("folded lines", function()
it('work correctly with :move #18668', function()
screen:try_resize(45, 12)
- source([[
+ exec([[
set foldmethod=expr foldexpr=indent(v:lnum)
let content = ['', '', 'Line1', ' Line2', ' Line3',
\ 'Line4', ' Line5', ' Line6',
@@ -1895,6 +1853,128 @@ describe("folded lines", function()
]])
end
end)
+
+ it('fold attached virtual lines are drawn correctly #21837', function()
+ funcs.setline(1, 'line 1')
+ funcs.setline(2, 'line 2')
+ funcs.setline(3, 'line 3')
+ funcs.setline(4, 'line 4')
+ feed("zfj")
+ exec_lua([[
+ local ns = vim.api.nvim_create_namespace("ns")
+ vim.api.nvim_buf_set_extmark(0, ns, 0, 0, { virt_lines_above = true, virt_lines = {{{"virt_line above line 1", ""}}} })
+ vim.api.nvim_buf_set_extmark(0, ns, 1, 0, { virt_lines = {{{"virt_line below line 2", ""}}} })
+ vim.api.nvim_buf_set_extmark(0, ns, 2, 0, { virt_lines_above = true, virt_lines = {{{"virt_line above line 3", ""}}} })
+ vim.api.nvim_buf_set_extmark(0, ns, 3, 0, { virt_lines = {{{"virt_line below line 4", ""}}} })
+ ]])
+ if multigrid then
+ screen:expect([[
+ ## grid 1
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [3:---------------------------------------------]|
+ ## grid 2
+ {5:^+-- 2 lines: line 1·························}|
+ virt_line above line 3 |
+ line 3 |
+ line 4 |
+ virt_line below line 4 |
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ |
+ ]])
+ else
+ screen:expect([[
+ {5:^+-- 2 lines: line 1·························}|
+ virt_line above line 3 |
+ line 3 |
+ line 4 |
+ virt_line below line 4 |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end
+
+ feed('jzfj')
+ if multigrid then
+ screen:expect([[
+ ## grid 1
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [3:---------------------------------------------]|
+ ## grid 2
+ {5:+-- 2 lines: line 1·························}|
+ {5:^+-- 2 lines: line 3·························}|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ |
+ ]])
+ else
+ screen:expect([[
+ {5:+-- 2 lines: line 1·························}|
+ {5:^+-- 2 lines: line 3·························}|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end
+
+ feed('kzo<C-Y>')
+ funcs.setline(5, 'line 5')
+ if multigrid then
+ screen:expect([[
+ ## grid 1
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [3:---------------------------------------------]|
+ ## grid 2
+ virt_line above line 1 |
+ ^line 1 |
+ line 2 |
+ virt_line below line 2 |
+ {5:+-- 2 lines: line 3·························}|
+ line 5 |
+ {1:~ }|
+ ## grid 3
+ |
+ ]])
+ else
+ screen:expect([[
+ virt_line above line 1 |
+ ^line 1 |
+ line 2 |
+ virt_line below line 2 |
+ {5:+-- 2 lines: line 3·························}|
+ line 5 |
+ {1:~ }|
+ |
+ ]])
+ end
+ end)
end
describe("with ext_multigrid", function()
@@ -1911,4 +1991,19 @@ describe("folded lines", function()
command('%delete')
eq(0, funcs.foldlevel(1))
end)
+
+ it('multibyte fold markers work #20438', function()
+ meths.win_set_option(0, 'foldmethod', 'marker')
+ meths.win_set_option(0, 'foldmarker', '«,»')
+ insert([[
+ bbbbb
+ bbbbb
+ bbbbb]])
+ feed('zfgg')
+ expect([[
+ bbbbb/*«*/
+ bbbbb
+ bbbbb/*»*/]])
+ eq(1, funcs.foldlevel(1))
+ end)
end)
diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua
index 4e3d62509c..288c2a214f 100644
--- a/test/functional/ui/highlight_spec.lua
+++ b/test/functional/ui/highlight_spec.lua
@@ -6,6 +6,7 @@ local command, exec = helpers.command, helpers.exec
local eval = helpers.eval
local feed_command, eq = helpers.feed_command, helpers.eq
local curbufmeths = helpers.curbufmeths
+local funcs = helpers.funcs
local meths = helpers.meths
describe('colorscheme compatibility', function()
@@ -13,7 +14,9 @@ describe('colorscheme compatibility', function()
clear()
end)
- it('t_Co is set to 256 by default', function()
+ it('&t_Co exists and is set to 256 by default', function()
+ eq(1, funcs.exists('&t_Co'))
+ eq(1, funcs.exists('+t_Co'))
eq('256', eval('&t_Co'))
end)
end)
@@ -1048,6 +1051,7 @@ describe('CursorLine and CursorLineNr highlights', function()
]])
end)
+ -- oldtest: Test_cursorline_after_yank()
it('always updated. vim-patch:8.1.0849', function()
local screen = Screen.new(50,5)
screen:set_default_attr_ids({
@@ -1081,6 +1085,7 @@ describe('CursorLine and CursorLineNr highlights', function()
]])
end)
+ -- oldtest: Test_cursorline_with_visualmode()
it('with visual area. vim-patch:8.1.1001', function()
local screen = Screen.new(50,5)
screen:set_default_attr_ids({
@@ -1108,6 +1113,7 @@ describe('CursorLine and CursorLineNr highlights', function()
]])
end)
+ -- oldtest: Test_cursorline_callback()
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({
@@ -1237,6 +1243,7 @@ describe('CursorLine and CursorLineNr highlights', function()
})
end)
+ -- oldtest: Test_diff_with_cursorline_number()
it('CursorLineNr shows correctly just below filler lines', function()
local screen = Screen.new(50,12)
screen:set_default_attr_ids({
@@ -1357,6 +1364,7 @@ describe('CursorColumn highlight', function()
]])
end)
+ -- oldtest: Test_cursorcolumn_callback()
it('is updated if cursor is moved from timer', function()
exec([[
call setline(1, ['aaaaa', 'bbbbb', 'ccccc', 'ddddd'])
@@ -1398,7 +1406,7 @@ describe('ColorColumn highlight', function()
before_each(function()
clear()
screen = Screen.new(40, 15)
- Screen:set_default_attr_ids({
+ screen:set_default_attr_ids({
[1] = {background = Screen.colors.LightRed}, -- ColorColumn
[2] = {background = Screen.colors.Grey90}, -- CursorLine
[3] = {foreground = Screen.colors.Brown}, -- LineNr
@@ -1412,6 +1420,7 @@ describe('ColorColumn highlight', function()
screen:attach()
end)
+ -- oldtest: Test_colorcolumn()
it('when entering a buffer vim-patch:8.1.2073', function()
exec([[
set nohidden
@@ -1443,6 +1452,7 @@ describe('ColorColumn highlight', function()
]])
end)
+ -- oldtest: Test_colorcolumn_bri()
it("in 'breakindent' vim-patch:8.2.1689", function()
exec([[
call setline(1, 'The quick brown fox jumped over the lazy dogs')
@@ -1467,6 +1477,7 @@ describe('ColorColumn highlight', function()
]])
end)
+ -- oldtest: Test_colorcolumn_sbr()
it("in 'showbreak' vim-patch:8.2.1689", function()
exec([[
call setline(1, 'The quick brown fox jumped over the lazy dogs')
@@ -1560,17 +1571,6 @@ describe("MsgSeparator highlight and msgsep fillchar", function()
1 %a "[No Name]" line 1 |
{3:Press ENTER or type command to continue}^ |
]])
-
- -- when display doesn't contain msgsep, these options have no effect
- feed_command("set display-=msgsep")
- feed_command("ls")
- screen:expect([[
- {1:~ }|
- {1:~ }|
- :ls |
- 1 %a "[No Name]" line 1 |
- {3:Press ENTER or type command to continue}^ |
- ]])
end)
it("and MsgArea", function()
@@ -1697,6 +1697,7 @@ describe("'number' and 'relativenumber' highlight", function()
]])
end)
+ -- oldtest: Test_relativenumber_callback()
it('relative number highlight is updated if cursor is moved from timer', function()
local screen = Screen.new(50, 8)
screen:set_default_attr_ids({
@@ -1745,38 +1746,40 @@ describe("'winhighlight' highlight", function()
clear()
screen = Screen.new(20,8)
screen:attach()
- screen:set_default_attr_ids({
- [0] = {bold=true, foreground=Screen.colors.Blue},
- [1] = {background = Screen.colors.DarkBlue},
- [2] = {background = Screen.colors.DarkBlue, bold = true, foreground = Screen.colors.Blue1},
- [3] = {bold = true, reverse = true},
- [4] = {reverse = true},
- [5] = {background = Screen.colors.DarkGreen},
- [6] = {background = Screen.colors.DarkGreen, bold = true, foreground = Screen.colors.Blue1},
- [7] = {background = Screen.colors.DarkMagenta},
- [8] = {background = Screen.colors.DarkMagenta, bold = true, foreground = Screen.colors.Blue1},
- [9] = {foreground = Screen.colors.Brown},
- [10] = {foreground = Screen.colors.Brown, background = Screen.colors.DarkBlue},
- [11] = {background = Screen.colors.DarkBlue, bold = true, reverse = true},
- [12] = {background = Screen.colors.DarkGreen, reverse = true},
- [13] = {background = Screen.colors.Magenta4, reverse = true},
- [14] = {background = Screen.colors.DarkBlue, reverse = true},
- [15] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
- [16] = {foreground = Screen.colors.Blue1},
- [17] = {background = Screen.colors.LightRed},
- [18] = {background = Screen.colors.Gray90},
- [19] = {foreground = Screen.colors.LightGrey, background = Screen.colors.DarkGray},
- [20] = {background = Screen.colors.LightGrey, underline = true},
- [21] = {bold = true},
- [22] = {bold = true, foreground = Screen.colors.SeaGreen4},
- [23] = {background = Screen.colors.LightMagenta},
- [24] = {background = Screen.colors.WebGray},
- [25] = {bold = true, foreground = Screen.colors.Green1},
- [26] = {background = Screen.colors.Red},
- [27] = {background = Screen.colors.DarkBlue, bold = true, foreground = Screen.colors.Green1},
- [28] = {bold = true, foreground = Screen.colors.Brown},
+ screen:set_default_attr_ids {
+ [0] = {bold=true, foreground=Screen.colors.Blue};
+ [1] = {background = Screen.colors.DarkBlue};
+ [2] = {background = Screen.colors.DarkBlue, bold = true, foreground = Screen.colors.Blue1};
+ [3] = {bold = true, reverse = true};
+ [4] = {reverse = true};
+ [5] = {background = Screen.colors.DarkGreen};
+ [6] = {background = Screen.colors.DarkGreen, bold = true, foreground = Screen.colors.Blue1};
+ [7] = {background = Screen.colors.DarkMagenta};
+ [8] = {background = Screen.colors.DarkMagenta, bold = true, foreground = Screen.colors.Blue1};
+ [9] = {foreground = Screen.colors.Brown};
+ [10] = {foreground = Screen.colors.Brown, background = Screen.colors.DarkBlue};
+ [11] = {background = Screen.colors.DarkBlue, bold = true, reverse = true};
+ [12] = {background = Screen.colors.DarkGreen, reverse = true};
+ [13] = {background = Screen.colors.Magenta4, reverse = true};
+ [14] = {background = Screen.colors.DarkBlue, reverse = true};
+ [15] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red};
+ [16] = {foreground = Screen.colors.Blue1};
+ [17] = {background = Screen.colors.LightRed};
+ [18] = {background = Screen.colors.Gray90};
+ [19] = {foreground = Screen.colors.LightGrey, background = Screen.colors.DarkGray};
+ [20] = {background = Screen.colors.LightGrey, underline = true};
+ [21] = {bold = true};
+ [22] = {bold = true, foreground = Screen.colors.SeaGreen4};
+ [23] = {background = Screen.colors.LightMagenta};
+ [24] = {background = Screen.colors.WebGray};
+ [25] = {bold = true, foreground = Screen.colors.Green1};
+ [26] = {background = Screen.colors.Red};
+ [27] = {background = Screen.colors.DarkBlue, bold = true, foreground = Screen.colors.Green1};
+ [28] = {bold = true, foreground = Screen.colors.Brown};
[29] = {foreground = Screen.colors.Blue1, background = Screen.colors.Red, bold = true};
- })
+ [30] = {background = tonumber('0xff8800')};
+ [31] = {background = tonumber('0xff8800'), bold = true, foreground = Screen.colors.Blue};
+ }
command("hi Background1 guibg=DarkBlue")
command("hi Background2 guibg=DarkGreen")
end)
@@ -2021,6 +2024,33 @@ describe("'winhighlight' highlight", function()
]])
end)
+ it('updates background to changed linked group', function()
+ command("split")
+ command("setlocal winhl=Normal:FancyGroup") -- does not yet exist
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {3:[No Name] }|
+ |
+ {0:~ }|
+ {4:[No Name] }|
+ |
+ ]]}
+
+ command("hi FancyGroup guibg=#FF8800") -- nice orange
+ screen:expect{grid=[[
+ {30:^ }|
+ {31:~ }|
+ {31:~ }|
+ {3:[No Name] }|
+ |
+ {0:~ }|
+ {4:[No Name] }|
+ |
+ ]]}
+ end)
+
it('background applies also to non-text', function()
command('set sidescroll=0')
insert('Lorem ipsum dolor sit amet ')
@@ -2333,6 +2363,51 @@ describe("'winhighlight' highlight", function()
helpers.assert_alive()
end)
+
+ it('can redraw statusline on cursor movement', function()
+ screen:try_resize(40, 8)
+ exec [[
+ set statusline=%f%=%#Background1#%l,%c%V\ %P
+ split
+ ]]
+ insert [[
+ some text
+ more text]]
+ screen:expect{grid=[[
+ some text |
+ more tex^t |
+ {0:~ }|
+ {3:[No Name] }{1:2,9 All}|
+ some text |
+ more text |
+ {4:[No Name] }{1:1,1 All}|
+ |
+ ]]}
+
+ command 'set winhl=Background1:Background2'
+ screen:expect{grid=[[
+ some text |
+ more tex^t |
+ {0:~ }|
+ {3:[No Name] }{5:2,9 All}|
+ some text |
+ more text |
+ {4:[No Name] }{1:1,1 All}|
+ |
+ ]]}
+
+ feed 'k'
+ screen:expect{grid=[[
+ some tex^t |
+ more text |
+ {0:~ }|
+ {3:[No Name] }{5:1,9 All}|
+ some text |
+ more text |
+ {4:[No Name] }{1:1,1 All}|
+ |
+ ]]}
+ end)
end)
describe('highlight namespaces', function()
diff --git a/test/functional/ui/hlstate_spec.lua b/test/functional/ui/hlstate_spec.lua
index dc74d6d401..55f873e827 100644
--- a/test/functional/ui/hlstate_spec.lua
+++ b/test/functional/ui/hlstate_spec.lua
@@ -4,9 +4,10 @@ local Screen = require('test.functional.ui.screen')
local clear, insert = helpers.clear, helpers.insert
local command = helpers.command
local meths = helpers.meths
-local iswin = helpers.iswin
local testprg = helpers.testprg
local thelpers = require('test.functional.terminal.helpers')
+local skip = helpers.skip
+local is_os = helpers.is_os
describe('ext_hlstate detailed highlights', function()
local screen
@@ -182,7 +183,7 @@ describe('ext_hlstate detailed highlights', function()
end)
it("work with :terminal", function()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
screen:set_default_attr_ids({
[1] = {{}, {{hi_name = "TermCursorNC", ui_name = "TermCursorNC", kind = "ui"}}},
@@ -211,7 +212,7 @@ describe('ext_hlstate detailed highlights', function()
thelpers.set_bold()
thelpers.feed_data('z\n')
-- TODO(bfredl): check if this distinction makes sense
- if iswin() then
+ if is_os('win') then
screen:expect([[
^tty ready |
x {5:y z} |
@@ -237,7 +238,7 @@ describe('ext_hlstate detailed highlights', function()
thelpers.feed_termcode("[A")
thelpers.feed_termcode("[2C")
- if iswin() then
+ if is_os('win') then
screen:expect([[
^tty ready |
x {6:y}{5: z} |
diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua
index 9ca4673efe..6fbf9b72c8 100644
--- a/test/functional/ui/inccommand_spec.lua
+++ b/test/functional/ui/inccommand_spec.lua
@@ -1190,6 +1190,8 @@ describe(":substitute, inccommand=split", function()
end)
it("deactivates if 'redrawtime' is exceeded #5602", function()
+ -- prevent redraws from 'incsearch'
+ meths.set_option('incsearch', false)
-- Assert that 'inccommand' is ENABLED initially.
eq("split", eval("&inccommand"))
-- Set 'redrawtime' to minimal value, to ensure timeout is triggered.
@@ -2972,6 +2974,59 @@ it(':substitute with inccommand, does not crash if range contains invalid marks'
]])
end)
+it(':substitute with inccommand, no unnecessary redraw if preview is not shown', function()
+ local screen = Screen.new(60, 6)
+ clear()
+ common_setup(screen, 'split', 'test')
+ feed(':ls<CR>')
+ screen:expect([[
+ test |
+ {15:~ }|
+ {11: }|
+ :ls |
+ 1 %a + "[No Name]" line 1 |
+ {13:Press ENTER or type command to continue}^ |
+ ]])
+ feed(':s')
+ -- no unnecessary redraw, so messages are still shown
+ screen:expect([[
+ test |
+ {15:~ }|
+ {11: }|
+ :ls |
+ 1 %a + "[No Name]" line 1 |
+ :s^ |
+ ]])
+ feed('o')
+ screen:expect([[
+ test |
+ {15:~ }|
+ {11: }|
+ :ls |
+ 1 %a + "[No Name]" line 1 |
+ :so^ |
+ ]])
+ feed('<BS>')
+ screen:expect([[
+ test |
+ {15:~ }|
+ {11: }|
+ :ls |
+ 1 %a + "[No Name]" line 1 |
+ :s^ |
+ ]])
+ feed('/test')
+ -- now inccommand is shown, so screen is redrawn
+ screen:expect([[
+ {12:test} |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :s/test^ |
+ ]])
+end)
+
it(":substitute doesn't crash with inccommand, if undo is empty #12932", function()
local screen = Screen.new(10,5)
clear()
@@ -2992,6 +3047,43 @@ it(":substitute doesn't crash with inccommand, if undo is empty #12932", functio
assert_alive()
end)
+it(':substitute with inccommand works properly if undo is not synced #20029', function()
+ local screen = Screen.new(30, 6)
+ clear()
+ common_setup(screen, 'nosplit', 'foo\nbar\nbaz')
+ meths.set_keymap('x', '<F2>', '<Esc>`<Oaaaaa asdf<Esc>`>obbbbb asdf<Esc>V`<k:s/asdf/', {})
+ feed('gg0<C-V>lljj<F2>')
+ screen:expect([[
+ aaaaa |
+ foo |
+ bar |
+ baz |
+ bbbbb |
+ :'<,'>s/asdf/^ |
+ ]])
+ feed('hjkl')
+ screen:expect([[
+ aaaaa {12:hjkl} |
+ foo |
+ bar |
+ baz |
+ bbbbb {12:hjkl} |
+ :'<,'>s/asdf/hjkl^ |
+ ]])
+ feed('<CR>')
+ expect([[
+ aaaaa hjkl
+ foo
+ bar
+ baz
+ bbbbb hjkl]])
+ feed('u')
+ expect([[
+ foo
+ bar
+ baz]])
+end)
+
it('long :%s/ with inccommand does not collapse cmdline', function()
local screen = Screen.new(10,5)
clear()
diff --git a/test/functional/ui/inccommand_user_spec.lua b/test/functional/ui/inccommand_user_spec.lua
index 0b25d4f8d2..43e9b94feb 100644
--- a/test/functional/ui/inccommand_user_spec.lua
+++ b/test/functional/ui/inccommand_user_spec.lua
@@ -220,7 +220,7 @@ local setup_replace_cmd = [[
end
-- ":<range>Replace <pat1> <pat2>"
- -- Replaces all occurences of <pat1> in <range> with <pat2>
+ -- Replaces all occurrences of <pat1> in <range> with <pat2>
vim.api.nvim_create_user_command(
'Replace',
replace,
diff --git a/test/functional/ui/linematch_spec.lua b/test/functional/ui/linematch_spec.lua
new file mode 100644
index 0000000000..697677aa67
--- /dev/null
+++ b/test/functional/ui/linematch_spec.lua
@@ -0,0 +1,995 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+
+local feed = helpers.feed
+local clear = helpers.clear
+local write_file = helpers.write_file
+
+describe('Diff mode screen with 3 diffs open', function()
+ local fname = 'Xtest-functional-diff-screen-1'
+ local fname_2 = fname .. '.2'
+ local fname_3 = fname .. '.3'
+ local screen
+
+ local reread = function()
+ feed(':e<cr><c-w>w:e<cr><c-w>w:e<cr><c-w>w')
+ end
+
+ setup(function()
+ clear()
+ os.remove(fname)
+ os.remove(fname_2)
+ os.remove(fname_3)
+ end)
+
+ teardown(function()
+ os.remove(fname)
+ os.remove(fname_2)
+ os.remove(fname_3)
+ end)
+
+ before_each(function()
+ clear()
+ feed(':set diffopt+=linematch:30<cr>')
+ feed(':e ' .. fname .. '<cr>')
+ feed(':vnew ' .. fname_2 .. '<cr>')
+ feed(':vnew ' .. fname_3 .. '<cr>')
+ feed(':windo diffthis<cr>')
+
+ screen = Screen.new(100, 16)
+ screen:attach()
+ screen:set_default_attr_ids({
+ [1] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.Gray};
+ [2] = {foreground = Screen.colors.Blue1, bold = true, background = Screen.colors.LightCyan1};
+ [3] = {reverse = true};
+ [4] = {background = Screen.colors.LightBlue};
+ [5] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGray};
+ [6] = {foreground = Screen.colors.Blue1, bold = true};
+ [7] = {reverse = true, bold = true};
+ [8] = {background = Screen.colors.Red1, bold = true};
+ [10] = {foreground = Screen.colors.Brown};
+ [9] = {background = Screen.colors.Plum1};
+ })
+ feed('<c-w>=')
+ feed(':windo set nu!<cr>')
+ end)
+
+ describe('setup the diff screen to look like a merge conflict with 3 files in diff mode', function()
+ before_each(function()
+
+ local f1 = [[
+
+ common line
+ AAA
+ AAA
+ AAA
+ ]]
+ local f2 = [[
+
+ common line
+ <<<<<<< HEAD
+ AAA
+ AAA
+ AAA
+ =======
+ BBB
+ BBB
+ BBB
+ >>>>>>> branch1
+ ]]
+ local f3 = [[
+
+ common line
+ BBB
+ BBB
+ BBB
+ ]]
+
+ write_file(fname, f1, false)
+ write_file(fname_2, f2, false)
+ write_file(fname_3, f3, false)
+ reread()
+ end)
+
+ it('get from window 1', function()
+ feed('1<c-w>w')
+ feed(':2,6diffget screen-1.2<cr>')
+ screen:expect([[
+ {1: }{10: 1 }^ │{1: }{10: 1 } │{1: }{10: 1 } |
+ {1: }{10: 2 }common line │{1: }{10: 2 }common line │{1: }{10: 2 }common line |
+ {1: }{10: 3 }{9:<<<<<<< HEAD }│{1: }{10: 3 }{9:<<<<<<< HEAD }│{1: }{10: }{2:---------------------------}|
+ {1: }{10: 4 } AAA │{1: }{10: 4 } AAA │{1: }{10: 3 } AAA |
+ {1: }{10: 5 } AAA │{1: }{10: 5 } AAA │{1: }{10: 4 } AAA |
+ {1: }{10: 6 } AAA │{1: }{10: 6 } AAA │{1: }{10: 5 } AAA |
+ {1: }{10: 7 }{9:======= }│{1: }{10: 7 }{9:======= }│{1: }{10: }{2:---------------------------}|
+ {1: }{10: 8 }{9: BBB }│{1: }{10: 8 }{9: BBB }│{1: }{10: }{2:---------------------------}|
+ {1: }{10: 9 }{9: BBB }│{1: }{10: 9 }{9: BBB }│{1: }{10: }{2:---------------------------}|
+ {1: }{10: 10 }{9: BBB }│{1: }{10: 10 }{9: BBB }│{1: }{10: }{2:---------------------------}|
+ {1: }{10: 11 }{9:>>>>>>> branch1 }│{1: }{10: 11 }{9:>>>>>>> branch1 }│{1: }{10: }{2:---------------------------}|
+ {1: }{10: 12 } │{1: }{10: 12 } │{1: }{10: 6 } |
+ {6:~ }│{6:~ }│{6:~ }|
+ {6:~ }│{6:~ }│{6:~ }|
+ {7:<-functional-diff-screen-1.3 [+] }{3:<est-functional-diff-screen-1.2 Xtest-functional-diff-screen-1 }|
+ :2,6diffget screen-1.2 |
+ ]])
+ end)
+
+ it('get from window 2', function()
+ feed('2<c-w>w')
+ feed(':5,7diffget screen-1.3<cr>')
+ screen:expect([[
+ {1: }{10: 1 } │{1: }{10: 1 }^ │{1: }{10: 1 } |
+ {1: }{10: 2 }common line │{1: }{10: 2 }common line │{1: }{10: 2 }common line |
+ {1: }{10: }{2:---------------------------}│{1: }{10: 3 }{4:<<<<<<< HEAD }│{1: }{10: }{2:---------------------------}|
+ {1: }{10: }{2:---------------------------}│{1: }{10: 4 }{9: AAA }│{1: }{10: 3 }{9: AAA }|
+ {1: }{10: 3 }{9: BBB }│{1: }{10: 5 }{9: BBB }│{1: }{10: }{2:---------------------------}|
+ {1: }{10: 4 }{9: }{8:BBB}{9: }│{1: }{10: 6 }{9: }{8:BBB}{9: }│{1: }{10: 4 }{9: }{8:AAA}{9: }|
+ {1: }{10: 5 }{9: }{8:BBB}{9: }│{1: }{10: 7 }{9: }{8:BBB}{9: }│{1: }{10: 5 }{9: }{8:AAA}{9: }|
+ {1: }{10: }{2:---------------------------}│{1: }{10: 8 }{4:>>>>>>> branch1 }│{1: }{10: }{2:---------------------------}|
+ {1: }{10: 6 } │{1: }{10: 9 } │{1: }{10: 6 } |
+ {6:~ }│{6:~ }│{6:~ }|
+ {6:~ }│{6:~ }│{6:~ }|
+ {6:~ }│{6:~ }│{6:~ }|
+ {6:~ }│{6:~ }│{6:~ }|
+ {6:~ }│{6:~ }│{6:~ }|
+ {3:<test-functional-diff-screen-1.3 }{7:<functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }|
+ :5,7diffget screen-1.3 |
+ ]])
+ end)
+
+ it('get from window 3', function()
+ feed('3<c-w>w')
+ feed(':5,6diffget screen-1.2<cr>')
+ screen:expect([[
+ {1: }{10: 1 } │{1: }{10: 1 } │{1: }{10: 1 }^ |
+ {1: }{10: 2 }common line │{1: }{10: 2 }common line │{1: }{10: 2 }common line |
+ {1: }{10: }{2:---------------------------}│{1: }{10: 3 }{4:<<<<<<< HEAD }│{1: }{10: }{2:---------------------------}|
+ {1: }{10: }{2:---------------------------}│{1: }{10: 4 }{9: AAA }│{1: }{10: 3 }{9: AAA }|
+ {1: }{10: }{2:---------------------------}│{1: }{10: 5 }{9: AAA }│{1: }{10: 4 }{9: AAA }|
+ {1: }{10: }{2:---------------------------}│{1: }{10: 6 }{9: AAA }│{1: }{10: 5 }{9: AAA }|
+ {1: }{10: }{2:---------------------------}│{1: }{10: 7 }{9:======= }│{1: }{10: 6 }{9:======= }|
+ {1: }{10: 3 } BBB │{1: }{10: 8 } BBB │{1: }{10: 7 } BBB |
+ {1: }{10: 4 } BBB │{1: }{10: 9 } BBB │{1: }{10: 8 } BBB |
+ {1: }{10: 5 } BBB │{1: }{10: 10 } BBB │{1: }{10: 9 } BBB |
+ {1: }{10: }{2:---------------------------}│{1: }{10: 11 }{9:>>>>>>> branch1 }│{1: }{10: 10 }{9:>>>>>>> branch1 }|
+ {1: }{10: 6 } │{1: }{10: 12 } │{1: }{10: 11 } |
+ {6:~ }│{6:~ }│{6:~ }|
+ {6:~ }│{6:~ }│{6:~ }|
+ {3:<test-functional-diff-screen-1.3 <est-functional-diff-screen-1.2 }{7:<st-functional-diff-screen-1 [+] }|
+ :5,6diffget screen-1.2 |
+ ]])
+ end)
+
+ it('put from window 2 - part', function()
+ feed('2<c-w>w')
+ feed(':6,8diffput screen-1<cr>')
+ screen:expect([[
+ {1: }{10: 1 } │{1: }{10: 1 }^ │{1: }{10: 1 } |
+ {1: }{10: 2 }common line │{1: }{10: 2 }common line │{1: }{10: 2 }common line |
+ {1: }{10: }{2:---------------------------}│{1: }{10: 3 }{4:<<<<<<< HEAD }│{1: }{10: }{2:---------------------------}|
+ {1: }{10: }{2:---------------------------}│{1: }{10: 4 }{9: AAA }│{1: }{10: 3 }{9: AAA }|
+ {1: }{10: }{2:---------------------------}│{1: }{10: 5 }{9: AAA }│{1: }{10: 4 }{9: AAA }|
+ {1: }{10: }{2:---------------------------}│{1: }{10: 6 }{9: AAA }│{1: }{10: 5 }{9: AAA }|
+ {1: }{10: }{2:---------------------------}│{1: }{10: 7 }{9:======= }│{1: }{10: 6 }{9:======= }|
+ {1: }{10: 3 }{9: BBB }│{1: }{10: 8 }{9: BBB }│{1: }{10: }{2:---------------------------}|
+ {1: }{10: 4 }{9: BBB }│{1: }{10: 9 }{9: BBB }│{1: }{10: }{2:---------------------------}|
+ {1: }{10: 5 } BBB │{1: }{10: 10 } BBB │{1: }{10: 7 } BBB |
+ {1: }{10: }{2:---------------------------}│{1: }{10: 11 }{4:>>>>>>> branch1 }│{1: }{10: }{2:---------------------------}|
+ {1: }{10: 6 } │{1: }{10: 12 } │{1: }{10: 8 } |
+ {6:~ }│{6:~ }│{6:~ }|
+ {6:~ }│{6:~ }│{6:~ }|
+ {3:<test-functional-diff-screen-1.3 }{7:<est-functional-diff-screen-1.2 }{3:<st-functional-diff-screen-1 [+] }|
+ :6,8diffput screen-1 |
+ ]])
+
+ end)
+ it('put from window 2 - part to end', function()
+ feed('2<c-w>w')
+ feed(':6,11diffput screen-1<cr>')
+ screen:expect([[
+ {1: }{10: 1 } │{1: }{10: 1 }^ │{1: }{10: 1 } |
+ {1: }{10: 2 }common line │{1: }{10: 2 }common line │{1: }{10: 2 }common line |
+ {1: }{10: }{2:---------------------------}│{1: }{10: 3 }{4:<<<<<<< HEAD }│{1: }{10: }{2:---------------------------}|
+ {1: }{10: }{2:---------------------------}│{1: }{10: 4 }{9: AAA }│{1: }{10: 3 }{9: AAA }|
+ {1: }{10: }{2:---------------------------}│{1: }{10: 5 }{9: AAA }│{1: }{10: 4 }{9: AAA }|
+ {1: }{10: }{2:---------------------------}│{1: }{10: 6 }{9: AAA }│{1: }{10: 5 }{9: AAA }|
+ {1: }{10: }{2:---------------------------}│{1: }{10: 7 }{9:======= }│{1: }{10: 6 }{9:======= }|
+ {1: }{10: 3 } BBB │{1: }{10: 8 } BBB │{1: }{10: 7 } BBB |
+ {1: }{10: 4 } BBB │{1: }{10: 9 } BBB │{1: }{10: 8 } BBB |
+ {1: }{10: 5 } BBB │{1: }{10: 10 } BBB │{1: }{10: 9 } BBB |
+ {1: }{10: }{2:---------------------------}│{1: }{10: 11 }{9:>>>>>>> branch1 }│{1: }{10: 10 }{9:>>>>>>> branch1 }|
+ {1: }{10: 6 } │{1: }{10: 12 } │{1: }{10: 11 } |
+ {6:~ }│{6:~ }│{6:~ }|
+ {6:~ }│{6:~ }│{6:~ }|
+ {3:<test-functional-diff-screen-1.3 }{7:<est-functional-diff-screen-1.2 }{3:<st-functional-diff-screen-1 [+] }|
+ :6,11diffput screen-1 |
+ ]])
+
+ end)
+ end)
+end)
+
+describe('Diff mode screen with 2 diffs open', function()
+ local fname = 'Xtest-functional-diff-screen-1'
+ local fname_2 = fname .. '.2'
+ local screen
+
+ local reread = function()
+ feed(':e<cr><c-w>w:e<cr><c-w>w:e<cr><c-w>w')
+ end
+
+ setup(function()
+ clear()
+ os.remove(fname)
+ os.remove(fname_2)
+ end)
+
+ teardown(function()
+ os.remove(fname)
+ os.remove(fname_2)
+ end)
+
+ before_each(function()
+ clear()
+ feed(':e ' .. fname .. '<cr>')
+ feed(':vnew ' .. fname_2 .. '<cr>')
+ feed(':windo diffthis<cr>')
+
+ screen = Screen.new(100, 20)
+ screen:attach()
+ screen:set_default_attr_ids({
+ [1] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.Gray};
+ [2] = {foreground = Screen.colors.Blue1, bold = true, background = Screen.colors.LightCyan1};
+ [3] = {reverse = true};
+ [4] = {background = Screen.colors.LightBlue};
+ [5] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGray};
+ [6] = {foreground = Screen.colors.Blue1, bold = true};
+ [7] = {reverse = true, bold = true};
+ [8] = {background = Screen.colors.Red1, bold = true};
+ [10] = {foreground = Screen.colors.Brown};
+ [9] = {background = Screen.colors.Plum1};
+ })
+ feed('<c-w>=')
+ feed(':windo set nu!<cr>')
+ end)
+
+ describe('setup a diff with 2 files and set linematch:30', function()
+ before_each(function()
+ feed(':set diffopt+=linematch:30<cr>')
+ local f1 = [[
+
+common line
+common line
+
+DEFabc
+xyz
+xyz
+xyz
+DEFabc
+DEFabc
+DEFabc
+common line
+common line
+DEF
+common line
+DEF
+something
+ ]]
+ local f2 = [[
+
+common line
+common line
+
+ABCabc
+ABCabc
+ABCabc
+ABCabc
+common line
+common line
+common line
+something
+ ]]
+ write_file(fname, f1, false)
+ write_file(fname_2, f2, false)
+ reread()
+ end)
+
+ it('get from window 1 from line 5 to 9', function()
+ feed('1<c-w>w')
+ feed(':5,9diffget<cr>')
+ screen:expect([[
+ {1:+ }{10: 1 }{5:^+-- 7 lines: common line··················}│{1:+ }{10: 1 }{5:+-- 7 lines: common line···················}|
+ {1: }{10: 8 }xyz │{1: }{10: 8 }xyz |
+ {1: }{10: 9 }DEFabc │{1: }{10: 9 }DEFabc |
+ {1: }{10: 10 }DEFabc │{1: }{10: 10 }DEFabc |
+ {1: }{10: 11 }DEFabc │{1: }{10: 11 }DEFabc |
+ {1: }{10: 12 }common line │{1: }{10: 12 }common line |
+ {1: }{10: 13 }common line │{1: }{10: 13 }common line |
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }|
+ {1: }{10: 14 }common line │{1: }{10: 15 }common line |
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }|
+ {1: }{10: 15 }something │{1: }{10: 17 }something |
+ {1: }{10: 16 } │{1: }{10: 18 } |
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {7:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }|
+ :5,9diffget |
+ ]])
+ end)
+ it('get from window 2 from line 5 to 10', function()
+ feed('2<c-w>w')
+ feed(':5,10diffget<cr>')
+ screen:expect([[
+ {1:- }{10: 1 } │{1:- }{10: 1 }^ |
+ {1: }{10: 2 }common line │{1: }{10: 2 }common line |
+ {1: }{10: 3 }common line │{1: }{10: 3 }common line |
+ {1: }{10: 4 } │{1: }{10: 4 } |
+ {1: }{10: 5 }ABCabc │{1: }{10: 5 }ABCabc |
+ {1: }{10: 6 }ABCabc │{1: }{10: 6 }ABCabc |
+ {1: }{10: 7 }ABCabc │{1: }{10: 7 }ABCabc |
+ {1: }{10: 8 }{8:ABC}{9:abc }│{1: }{10: 8 }{8:DEF}{9:abc }|
+ {1: }{10: 9 }common line │{1: }{10: 9 }common line |
+ {1: }{10: 10 }common line │{1: }{10: 10 }common line |
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 11 }{4:DEF }|
+ {1: }{10: 11 }common line │{1: }{10: 12 }common line |
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 13 }{4:DEF }|
+ {1: }{10: 12 }something │{1: }{10: 14 }something |
+ {1: }{10: 13 } │{1: }{10: 15 } |
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {3:Xtest-functional-diff-screen-1.2 }{7:Xtest-functional-diff-screen-1 [+] }|
+ :5,10diffget |
+ ]])
+ end)
+ it('get all from window 2', function()
+ feed('2<c-w>w')
+ feed(':4,17diffget<cr>')
+ screen:expect([[
+ {1: }{10: 1 } │{1: }{10: 1 }^ |
+ {1: }{10: 2 }common line │{1: }{10: 2 }common line |
+ {1: }{10: 3 }common line │{1: }{10: 3 }common line |
+ {1: }{10: 4 } │{1: }{10: 4 } |
+ {1: }{10: 5 }ABCabc │{1: }{10: 5 }ABCabc |
+ {1: }{10: 6 }ABCabc │{1: }{10: 6 }ABCabc |
+ {1: }{10: 7 }ABCabc │{1: }{10: 7 }ABCabc |
+ {1: }{10: 8 }ABCabc │{1: }{10: 8 }ABCabc |
+ {1: }{10: 9 }common line │{1: }{10: 9 }common line |
+ {1: }{10: 10 }common line │{1: }{10: 10 }common line |
+ {1: }{10: 11 }common line │{1: }{10: 11 }common line |
+ {1: }{10: 12 }something │{1: }{10: 12 }something |
+ {1: }{10: 13 } │{1: }{10: 13 } |
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {3:Xtest-functional-diff-screen-1.2 }{7:Xtest-functional-diff-screen-1 [+] }|
+ :4,17diffget |
+ ]])
+
+ end)
+ it('get all from window 1', function()
+ feed('1<c-w>w')
+ feed(':4,12diffget<cr>')
+ screen:expect([[
+ {1: }{10: 1 }^ │{1: }{10: 1 } |
+ {1: }{10: 2 }common line │{1: }{10: 2 }common line |
+ {1: }{10: 3 }common line │{1: }{10: 3 }common line |
+ {1: }{10: 4 } │{1: }{10: 4 } |
+ {1: }{10: 5 }DEFabc │{1: }{10: 5 }DEFabc |
+ {1: }{10: 6 }xyz │{1: }{10: 6 }xyz |
+ {1: }{10: 7 }xyz │{1: }{10: 7 }xyz |
+ {1: }{10: 8 }xyz │{1: }{10: 8 }xyz |
+ {1: }{10: 9 }DEFabc │{1: }{10: 9 }DEFabc |
+ {1: }{10: 10 }DEFabc │{1: }{10: 10 }DEFabc |
+ {1: }{10: 11 }DEFabc │{1: }{10: 11 }DEFabc |
+ {1: }{10: 12 }common line │{1: }{10: 12 }common line |
+ {1: }{10: 13 }common line │{1: }{10: 13 }common line |
+ {1: }{10: 14 }DEF │{1: }{10: 14 }DEF |
+ {1: }{10: 15 }common line │{1: }{10: 15 }common line |
+ {1: }{10: 16 }DEF │{1: }{10: 16 }DEF |
+ {1: }{10: 17 }something │{1: }{10: 17 }something |
+ {1: }{10: 18 } │{1: }{10: 18 } |
+ {7:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }|
+ :4,12diffget |
+ ]])
+ end)
+ it('get from window 1 using do 1 line 5', function()
+ feed('1<c-w>w')
+ feed('5gg')
+ feed('do')
+ screen:expect([[
+ {1: }{10: 1 } │{1: }{10: 1 } |
+ {1: }{10: 2 }common line │{1: }{10: 2 }common line |
+ {1: }{10: 3 }common line │{1: }{10: 3 }common line |
+ {1: }{10: 4 } │{1: }{10: 4 } |
+ {1: }{10: 5 }^DEFabc │{1: }{10: 5 }DEFabc |
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }|
+ {1: }{10: 6 }{8:ABC}{9:abc }│{1: }{10: 9 }{8:DEF}{9:abc }|
+ {1: }{10: 7 }{8:ABC}{9:abc }│{1: }{10: 10 }{8:DEF}{9:abc }|
+ {1: }{10: 8 }{8:ABC}{9:abc }│{1: }{10: 11 }{8:DEF}{9:abc }|
+ {1: }{10: 9 }common line │{1: }{10: 12 }common line |
+ {1: }{10: 10 }common line │{1: }{10: 13 }common line |
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }|
+ {1: }{10: 11 }common line │{1: }{10: 15 }common line |
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }|
+ {1: }{10: 12 }something │{1: }{10: 17 }something |
+ {1: }{10: 13 } │{1: }{10: 18 } |
+ {7:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }|
+ :e |
+ ]])
+ end)
+ it('get from window 1 using do 2 line 6', function()
+ feed('1<c-w>w')
+ feed('6gg')
+ feed('do')
+ screen:expect([[
+ {1: }{10: 1 } │{1: }{10: 1 } |
+ {1: }{10: 2 }common line │{1: }{10: 2 }common line |
+ {1: }{10: 3 }common line │{1: }{10: 3 }common line |
+ {1: }{10: 4 } │{1: }{10: 4 } |
+ {1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }|
+ {1: }{10: 6 }^DEFabc │{1: }{10: 9 }DEFabc |
+ {1: }{10: 7 }DEFabc │{1: }{10: 10 }DEFabc |
+ {1: }{10: 8 }DEFabc │{1: }{10: 11 }DEFabc |
+ {1: }{10: 9 }common line │{1: }{10: 12 }common line |
+ {1: }{10: 10 }common line │{1: }{10: 13 }common line |
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }|
+ {1: }{10: 11 }common line │{1: }{10: 15 }common line |
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }|
+ {1: }{10: 12 }something │{1: }{10: 17 }something |
+ {1: }{10: 13 } │{1: }{10: 18 } |
+ {7:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }|
+ :e |
+ ]])
+ end)
+ it('get from window 1 using do 2 line 7', function()
+ feed('1<c-w>w')
+ feed('7gg')
+ feed('do')
+ screen:expect([[
+ {1: }{10: 1 } │{1: }{10: 1 } |
+ {1: }{10: 2 }common line │{1: }{10: 2 }common line |
+ {1: }{10: 3 }common line │{1: }{10: 3 }common line |
+ {1: }{10: 4 } │{1: }{10: 4 } |
+ {1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }|
+ {1: }{10: 6 }DEFabc │{1: }{10: 9 }DEFabc |
+ {1: }{10: 7 }^DEFabc │{1: }{10: 10 }DEFabc |
+ {1: }{10: 8 }DEFabc │{1: }{10: 11 }DEFabc |
+ {1: }{10: 9 }common line │{1: }{10: 12 }common line |
+ {1: }{10: 10 }common line │{1: }{10: 13 }common line |
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }|
+ {1: }{10: 11 }common line │{1: }{10: 15 }common line |
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }|
+ {1: }{10: 12 }something │{1: }{10: 17 }something |
+ {1: }{10: 13 } │{1: }{10: 18 } |
+ {7:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }|
+ :e |
+ ]])
+ end)
+ it('get from window 1 using do 2 line 11', function()
+ feed('1<c-w>w')
+ feed('11gg')
+ feed('do')
+ screen:expect([[
+ {1: }{10: 1 } │{1: }{10: 1 } |
+ {1: }{10: 2 }common line │{1: }{10: 2 }common line |
+ {1: }{10: 3 }common line │{1: }{10: 3 }common line |
+ {1: }{10: 4 } │{1: }{10: 4 } |
+ {1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }|
+ {1: }{10: 6 }{8:ABC}{9:abc }│{1: }{10: 9 }{8:DEF}{9:abc }|
+ {1: }{10: 7 }{8:ABC}{9:abc }│{1: }{10: 10 }{8:DEF}{9:abc }|
+ {1: }{10: 8 }{8:ABC}{9:abc }│{1: }{10: 11 }{8:DEF}{9:abc }|
+ {1: }{10: 9 }common line │{1: }{10: 12 }common line |
+ {1: }{10: 10 }common line │{1: }{10: 13 }common line |
+ {1: }{10: 11 }DEF │{1: }{10: 14 }DEF |
+ {1: }{10: 12 }^common line │{1: }{10: 15 }common line |
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }|
+ {1: }{10: 13 }something │{1: }{10: 17 }something |
+ {1: }{10: 14 } │{1: }{10: 18 } |
+ {7:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }|
+ :e |
+ ]])
+ end)
+ it('get from window 1 using do 2 line 12', function()
+ feed('1<c-w>w')
+ feed('12gg')
+ feed('do')
+ screen:expect([[
+ {1: }{10: 1 } │{1: }{10: 1 } |
+ {1: }{10: 2 }common line │{1: }{10: 2 }common line |
+ {1: }{10: 3 }common line │{1: }{10: 3 }common line |
+ {1: }{10: 4 } │{1: }{10: 4 } |
+ {1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }|
+ {1: }{10: 6 }{8:ABC}{9:abc }│{1: }{10: 9 }{8:DEF}{9:abc }|
+ {1: }{10: 7 }{8:ABC}{9:abc }│{1: }{10: 10 }{8:DEF}{9:abc }|
+ {1: }{10: 8 }{8:ABC}{9:abc }│{1: }{10: 11 }{8:DEF}{9:abc }|
+ {1: }{10: 9 }common line │{1: }{10: 12 }common line |
+ {1: }{10: 10 }common line │{1: }{10: 13 }common line |
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }|
+ {1: }{10: 11 }common line │{1: }{10: 15 }common line |
+ {1: }{10: 12 }DEF │{1: }{10: 16 }DEF |
+ {1: }{10: 13 }^something │{1: }{10: 17 }something |
+ {1: }{10: 14 } │{1: }{10: 18 } |
+ {7:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }|
+ :e |
+ ]])
+ end)
+ it('put from window 1 using dp 1 line 5', function()
+ feed('1<c-w>w')
+ feed('5gg')
+ feed('dp')
+ screen:expect([[
+ {1: }{10: 1 } │{1: }{10: 1 } |
+ {1: }{10: 2 }common line │{1: }{10: 2 }common line |
+ {1: }{10: 3 }common line │{1: }{10: 3 }common line |
+ {1: }{10: 4 } │{1: }{10: 4 } |
+ {1: }{10: 5 }^ABCabc │{1: }{10: 5 }ABCabc |
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }|
+ {1: }{10: 6 }{8:ABC}{9:abc }│{1: }{10: 9 }{8:DEF}{9:abc }|
+ {1: }{10: 7 }{8:ABC}{9:abc }│{1: }{10: 10 }{8:DEF}{9:abc }|
+ {1: }{10: 8 }{8:ABC}{9:abc }│{1: }{10: 11 }{8:DEF}{9:abc }|
+ {1: }{10: 9 }common line │{1: }{10: 12 }common line |
+ {1: }{10: 10 }common line │{1: }{10: 13 }common line |
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }|
+ {1: }{10: 11 }common line │{1: }{10: 15 }common line |
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }|
+ {1: }{10: 12 }something │{1: }{10: 17 }something |
+ {1: }{10: 13 } │{1: }{10: 18 } |
+ {7:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 [+] }|
+ :e |
+ ]])
+ end)
+ it('put from window 1 using dp 2 line 6', function()
+ feed('1<c-w>w')
+ feed('6gg')
+ feed('dp')
+ screen:expect([[
+ {1: }{10: 1 } │{1: }{10: 1 } |
+ {1: }{10: 2 }common line │{1: }{10: 2 }common line |
+ {1: }{10: 3 }common line │{1: }{10: 3 }common line |
+ {1: }{10: 4 } │{1: }{10: 4 } |
+ {1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }|
+ {1: }{10: 6 }^ABCabc │{1: }{10: 9 }ABCabc |
+ {1: }{10: 7 }ABCabc │{1: }{10: 10 }ABCabc |
+ {1: }{10: 8 }ABCabc │{1: }{10: 11 }ABCabc |
+ {1: }{10: 9 }common line │{1: }{10: 12 }common line |
+ {1: }{10: 10 }common line │{1: }{10: 13 }common line |
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }|
+ {1: }{10: 11 }common line │{1: }{10: 15 }common line |
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }|
+ {1: }{10: 12 }something │{1: }{10: 17 }something |
+ {1: }{10: 13 } │{1: }{10: 18 } |
+ {7:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 [+] }|
+ :e |
+ ]])
+ end)
+ it('put from window 1 using dp 2 line 7', function()
+ feed('1<c-w>w')
+ feed('7gg')
+ feed('dp')
+ screen:expect([[
+ {1: }{10: 1 } │{1: }{10: 1 } |
+ {1: }{10: 2 }common line │{1: }{10: 2 }common line |
+ {1: }{10: 3 }common line │{1: }{10: 3 }common line |
+ {1: }{10: 4 } │{1: }{10: 4 } |
+ {1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }|
+ {1: }{10: 6 }ABCabc │{1: }{10: 9 }ABCabc |
+ {1: }{10: 7 }^ABCabc │{1: }{10: 10 }ABCabc |
+ {1: }{10: 8 }ABCabc │{1: }{10: 11 }ABCabc |
+ {1: }{10: 9 }common line │{1: }{10: 12 }common line |
+ {1: }{10: 10 }common line │{1: }{10: 13 }common line |
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }|
+ {1: }{10: 11 }common line │{1: }{10: 15 }common line |
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }|
+ {1: }{10: 12 }something │{1: }{10: 17 }something |
+ {1: }{10: 13 } │{1: }{10: 18 } |
+ {7:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 [+] }|
+ :e |
+ ]])
+ end)
+ it('put from window 1 using dp 2 line 11', function()
+ feed('1<c-w>w')
+ feed('11gg')
+ feed('dp')
+ screen:expect([[
+ {1: }{10: 1 } │{1: }{10: 1 } |
+ {1: }{10: 2 }common line │{1: }{10: 2 }common line |
+ {1: }{10: 3 }common line │{1: }{10: 3 }common line |
+ {1: }{10: 4 } │{1: }{10: 4 } |
+ {1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }|
+ {1: }{10: 6 }{8:ABC}{9:abc }│{1: }{10: 9 }{8:DEF}{9:abc }|
+ {1: }{10: 7 }{8:ABC}{9:abc }│{1: }{10: 10 }{8:DEF}{9:abc }|
+ {1: }{10: 8 }{8:ABC}{9:abc }│{1: }{10: 11 }{8:DEF}{9:abc }|
+ {1: }{10: 9 }common line │{1: }{10: 12 }common line |
+ {1: }{10: 10 }common line │{1: }{10: 13 }common line |
+ {1: }{10: 11 }^common line │{1: }{10: 14 }common line |
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 15 }{4:DEF }|
+ {1: }{10: 12 }something │{1: }{10: 16 }something |
+ {1: }{10: 13 } │{1: }{10: 17 } |
+ {6:~ }│{6:~ }|
+ {7:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 [+] }|
+ :e |
+ ]])
+ end)
+ it('put from window 1 using dp 2 line 12', function()
+ feed('1<c-w>w')
+ feed('12gg')
+ feed('dp')
+ screen:expect([[
+ {1: }{10: 1 } │{1: }{10: 1 } |
+ {1: }{10: 2 }common line │{1: }{10: 2 }common line |
+ {1: }{10: 3 }common line │{1: }{10: 3 }common line |
+ {1: }{10: 4 } │{1: }{10: 4 } |
+ {1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }|
+ {1: }{10: 6 }{8:ABC}{9:abc }│{1: }{10: 9 }{8:DEF}{9:abc }|
+ {1: }{10: 7 }{8:ABC}{9:abc }│{1: }{10: 10 }{8:DEF}{9:abc }|
+ {1: }{10: 8 }{8:ABC}{9:abc }│{1: }{10: 11 }{8:DEF}{9:abc }|
+ {1: }{10: 9 }common line │{1: }{10: 12 }common line |
+ {1: }{10: 10 }common line │{1: }{10: 13 }common line |
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }|
+ {1: }{10: 11 }common line │{1: }{10: 15 }common line |
+ {1: }{10: 12 }^something │{1: }{10: 16 }something |
+ {1: }{10: 13 } │{1: }{10: 17 } |
+ {6:~ }│{6:~ }|
+ {7:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 [+] }|
+ :e |
+ ]])
+ end)
+ it('put from window 2 using dp line 6', function()
+ feed('2<c-w>w')
+ feed('6gg')
+ feed('dp')
+ screen:expect([[
+ {1: }{10: 1 } │{1: }{10: 1 } |
+ {1: }{10: 2 }common line │{1: }{10: 2 }common line |
+ {1: }{10: 3 }common line │{1: }{10: 3 }common line |
+ {1: }{10: 4 } │{1: }{10: 4 } |
+ {1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }|
+ {1: }{10: 6 }xyz │{1: }{10: 6 }^xyz |
+ {1: }{10: 7 }xyz │{1: }{10: 7 }xyz |
+ {1: }{10: 8 }xyz │{1: }{10: 8 }xyz |
+ {1: }{10: 9 }{8:ABC}{9:abc }│{1: }{10: 9 }{8:DEF}{9:abc }|
+ {1: }{10: 10 }{8:ABC}{9:abc }│{1: }{10: 10 }{8:DEF}{9:abc }|
+ {1: }{10: 11 }{8:ABC}{9:abc }│{1: }{10: 11 }{8:DEF}{9:abc }|
+ {1: }{10: 12 }common line │{1: }{10: 12 }common line |
+ {1: }{10: 13 }common line │{1: }{10: 13 }common line |
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }|
+ {1: }{10: 14 }common line │{1: }{10: 15 }common line |
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }|
+ {1: }{10: 15 }something │{1: }{10: 17 }something |
+ {1: }{10: 16 } │{1: }{10: 18 } |
+ {3:Xtest-functional-diff-screen-1.2 [+] }{7:Xtest-functional-diff-screen-1 }|
+ :e |
+ ]])
+ end)
+ it('put from window 2 using dp line 8', function()
+ feed('2<c-w>w')
+ feed('8gg')
+ feed('dp')
+ screen:expect([[
+ {1: }{10: 1 } │{1: }{10: 1 } |
+ {1: }{10: 2 }common line │{1: }{10: 2 }common line |
+ {1: }{10: 3 }common line │{1: }{10: 3 }common line |
+ {1: }{10: 4 } │{1: }{10: 4 } |
+ {1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }|
+ {1: }{10: 6 }xyz │{1: }{10: 6 }xyz |
+ {1: }{10: 7 }xyz │{1: }{10: 7 }xyz |
+ {1: }{10: 8 }xyz │{1: }{10: 8 }^xyz |
+ {1: }{10: 9 }{8:ABC}{9:abc }│{1: }{10: 9 }{8:DEF}{9:abc }|
+ {1: }{10: 10 }{8:ABC}{9:abc }│{1: }{10: 10 }{8:DEF}{9:abc }|
+ {1: }{10: 11 }{8:ABC}{9:abc }│{1: }{10: 11 }{8:DEF}{9:abc }|
+ {1: }{10: 12 }common line │{1: }{10: 12 }common line |
+ {1: }{10: 13 }common line │{1: }{10: 13 }common line |
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }|
+ {1: }{10: 14 }common line │{1: }{10: 15 }common line |
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }|
+ {1: }{10: 15 }something │{1: }{10: 17 }something |
+ {1: }{10: 16 } │{1: }{10: 18 } |
+ {3:Xtest-functional-diff-screen-1.2 [+] }{7:Xtest-functional-diff-screen-1 }|
+ :e |
+ ]])
+ end)
+ it('put from window 2 using dp line 9', function()
+ feed('2<c-w>w')
+ feed('9gg')
+ feed('dp')
+ screen:expect([[
+ {1: }{10: 1 } │{1: }{10: 1 } |
+ {1: }{10: 2 }common line │{1: }{10: 2 }common line |
+ {1: }{10: 3 }common line │{1: }{10: 3 }common line |
+ {1: }{10: 4 } │{1: }{10: 4 } |
+ {1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }|
+ {1: }{10: 6 }DEFabc │{1: }{10: 9 }^DEFabc |
+ {1: }{10: 7 }DEFabc │{1: }{10: 10 }DEFabc |
+ {1: }{10: 8 }DEFabc │{1: }{10: 11 }DEFabc |
+ {1: }{10: 9 }common line │{1: }{10: 12 }common line |
+ {1: }{10: 10 }common line │{1: }{10: 13 }common line |
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }|
+ {1: }{10: 11 }common line │{1: }{10: 15 }common line |
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }|
+ {1: }{10: 12 }something │{1: }{10: 17 }something |
+ {1: }{10: 13 } │{1: }{10: 18 } |
+ {3:Xtest-functional-diff-screen-1.2 [+] }{7:Xtest-functional-diff-screen-1 }|
+ :e |
+ ]])
+ end)
+ it('put from window 2 using dp line 17', function()
+ feed('2<c-w>w')
+ feed('17gg')
+ feed('dp')
+ screen:expect([[
+ {1: }{10: 1 } │{1: }{10: 1 } |
+ {1: }{10: 2 }common line │{1: }{10: 2 }common line |
+ {1: }{10: 3 }common line │{1: }{10: 3 }common line |
+ {1: }{10: 4 } │{1: }{10: 4 } |
+ {1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }|
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }|
+ {1: }{10: 6 }{8:ABC}{9:abc }│{1: }{10: 9 }{8:DEF}{9:abc }|
+ {1: }{10: 7 }{8:ABC}{9:abc }│{1: }{10: 10 }{8:DEF}{9:abc }|
+ {1: }{10: 8 }{8:ABC}{9:abc }│{1: }{10: 11 }{8:DEF}{9:abc }|
+ {1: }{10: 9 }common line │{1: }{10: 12 }common line |
+ {1: }{10: 10 }common line │{1: }{10: 13 }common line |
+ {1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }|
+ {1: }{10: 11 }common line │{1: }{10: 15 }common line |
+ {1: }{10: 12 }DEF │{1: }{10: 16 }DEF |
+ {1: }{10: 13 }something │{1: }{10: 17 }^something |
+ {1: }{10: 14 } │{1: }{10: 18 } |
+ {3:Xtest-functional-diff-screen-1.2 [+] }{7:Xtest-functional-diff-screen-1 }|
+ :e |
+ ]])
+
+ end)
+ end)
+ describe('setup a diff with 2 files and set linematch:10', function()
+ before_each(function()
+ feed(':set diffopt+=linematch:10<cr>')
+ local f1 = [[
+common line
+HIL
+
+aABCabc
+aABCabc
+aABCabc
+aABCabc
+common line
+HIL
+common line
+something
+ ]]
+ local f2 = [[
+common line
+DEF
+GHI
+something
+
+aDEFabc
+xyz
+xyz
+xyz
+aDEFabc
+aDEFabc
+aDEFabc
+common line
+DEF
+GHI
+something else
+common line
+something
+ ]]
+ write_file(fname, f1, false)
+ write_file(fname_2, f2, false)
+ reread()
+ end)
+
+ it('enable linematch for the longest diff block by increasing the number argument passed to linematch', function()
+ feed('1<c-w>w')
+ -- linematch is disabled for the longest diff because it's combined line length is over 10
+ screen:expect([[
+ {1: }{10: 1 }^common line │{1: }{10: 1 }common line |
+ {1: }{10: 2 }{4:DEF }│{1: }{10: }{2:--------------------------------------------}|
+ {1: }{10: 3 }{8:GHI}{9: }│{1: }{10: 2 }{8:HIL}{9: }|
+ {1: }{10: 4 }{4:something }│{1: }{10: }{2:--------------------------------------------}|
+ {1: }{10: 5 } │{1: }{10: 3 } |
+ {1: }{10: 6 }{9:a}{8:DEF}{9:abc }│{1: }{10: 4 }{9:a}{8:ABC}{9:abc }|
+ {1: }{10: 7 }{8:xyz}{9: }│{1: }{10: 5 }{8:aABCabc}{9: }|
+ {1: }{10: 8 }{8:xyz}{9: }│{1: }{10: 6 }{8:aABCabc}{9: }|
+ {1: }{10: 9 }{8:xyz}{9: }│{1: }{10: 7 }{8:aABCabc}{9: }|
+ {1: }{10: 10 }{4:aDEFabc }│{1: }{10: }{2:--------------------------------------------}|
+ {1: }{10: 11 }{4:aDEFabc }│{1: }{10: }{2:--------------------------------------------}|
+ {1: }{10: 12 }{4:aDEFabc }│{1: }{10: }{2:--------------------------------------------}|
+ {1: }{10: 13 }common line │{1: }{10: 8 }common line |
+ {1: }{10: 14 }{4:DEF }│{1: }{10: }{2:--------------------------------------------}|
+ {1: }{10: 15 }{8:GHI}{9: }│{1: }{10: 9 }{8:HIL}{9: }|
+ {1: }{10: 16 }{4:something else }│{1: }{10: }{2:--------------------------------------------}|
+ {1: }{10: 17 }common line │{1: }{10: 10 }common line |
+ {1: }{10: 18 }something │{1: }{10: 11 }something |
+ {7:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 }|
+ :e |
+ ]])
+ -- enable it by increasing the number
+ feed(":set diffopt-=linematch:10<cr>")
+ feed(":set diffopt+=linematch:30<cr>")
+ screen:expect([[
+ {1: }{10: 1 }^common line │{1: }{10: 1 }common line |
+ {1: }{10: 2 }{4:DEF }│{1: }{10: }{2:--------------------------------------------}|
+ {1: }{10: 3 }{8:GHI}{9: }│{1: }{10: 2 }{8:HIL}{9: }|
+ {1: }{10: 4 }{4:something }│{1: }{10: }{2:--------------------------------------------}|
+ {1: }{10: 5 } │{1: }{10: 3 } |
+ {1: }{10: 6 }{9:a}{8:DEF}{9:abc }│{1: }{10: 4 }{9:a}{8:ABC}{9:abc }|
+ {1: }{10: 7 }{4:xyz }│{1: }{10: }{2:--------------------------------------------}|
+ {1: }{10: 8 }{4:xyz }│{1: }{10: }{2:--------------------------------------------}|
+ {1: }{10: 9 }{4:xyz }│{1: }{10: }{2:--------------------------------------------}|
+ {1: }{10: 10 }{9:a}{8:DEF}{9:abc }│{1: }{10: 5 }{9:a}{8:ABC}{9:abc }|
+ {1: }{10: 11 }{9:a}{8:DEF}{9:abc }│{1: }{10: 6 }{9:a}{8:ABC}{9:abc }|
+ {1: }{10: 12 }{9:a}{8:DEF}{9:abc }│{1: }{10: 7 }{9:a}{8:ABC}{9:abc }|
+ {1: }{10: 13 }common line │{1: }{10: 8 }common line |
+ {1: }{10: 14 }{4:DEF }│{1: }{10: }{2:--------------------------------------------}|
+ {1: }{10: 15 }{8:GHI}{9: }│{1: }{10: 9 }{8:HIL}{9: }|
+ {1: }{10: 16 }{4:something else }│{1: }{10: }{2:--------------------------------------------}|
+ {1: }{10: 17 }common line │{1: }{10: 10 }common line |
+ {1: }{10: 18 }something │{1: }{10: 11 }something |
+ {7:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 }|
+ :set diffopt+=linematch:30 |
+ ]])
+ end)
+ it('get all from second window', function()
+ feed('2<c-w>w')
+ feed(':1,12diffget<cr>')
+ screen:expect([[
+ {1: }{10: 1 }common line │{1: }{10: 1 }^common line |
+ {1: }{10: 2 }DEF │{1: }{10: 2 }DEF |
+ {1: }{10: 3 }GHI │{1: }{10: 3 }GHI |
+ {1: }{10: 4 }something │{1: }{10: 4 }something |
+ {1: }{10: 5 } │{1: }{10: 5 } |
+ {1: }{10: 6 }aDEFabc │{1: }{10: 6 }aDEFabc |
+ {1: }{10: 7 }xyz │{1: }{10: 7 }xyz |
+ {1: }{10: 8 }xyz │{1: }{10: 8 }xyz |
+ {1: }{10: 9 }xyz │{1: }{10: 9 }xyz |
+ {1: }{10: 10 }aDEFabc │{1: }{10: 10 }aDEFabc |
+ {1: }{10: 11 }aDEFabc │{1: }{10: 11 }aDEFabc |
+ {1: }{10: 12 }aDEFabc │{1: }{10: 12 }aDEFabc |
+ {1: }{10: 13 }common line │{1: }{10: 13 }common line |
+ {1: }{10: 14 }DEF │{1: }{10: 14 }DEF |
+ {1: }{10: 15 }GHI │{1: }{10: 15 }GHI |
+ {1: }{10: 16 }something else │{1: }{10: 16 }something else |
+ {1: }{10: 17 }common line │{1: }{10: 17 }common line |
+ {1: }{10: 18 }something │{1: }{10: 18 }something |
+ {3:Xtest-functional-diff-screen-1.2 }{7:Xtest-functional-diff-screen-1 [+] }|
+ :1,12diffget |
+ ]])
+ end)
+ it('get all from first window', function()
+ feed('1<c-w>w')
+ feed(':1,19diffget<cr>')
+ screen:expect([[
+ {1: }{10: 1 }^common line │{1: }{10: 1 }common line |
+ {1: }{10: 2 }HIL │{1: }{10: 2 }HIL |
+ {1: }{10: 3 } │{1: }{10: 3 } |
+ {1: }{10: 4 }aABCabc │{1: }{10: 4 }aABCabc |
+ {1: }{10: 5 }aABCabc │{1: }{10: 5 }aABCabc |
+ {1: }{10: 6 }aABCabc │{1: }{10: 6 }aABCabc |
+ {1: }{10: 7 }aABCabc │{1: }{10: 7 }aABCabc |
+ {1: }{10: 8 }common line │{1: }{10: 8 }common line |
+ {1: }{10: 9 }HIL │{1: }{10: 9 }HIL |
+ {1: }{10: 10 }common line │{1: }{10: 10 }common line |
+ {1: }{10: 11 }something │{1: }{10: 11 }something |
+ {1: }{10: 12 } │{1: }{10: 12 } |
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {7:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }|
+ :1,19diffget |
+ ]])
+ end)
+ it('get part of the non linematched diff block in window 2 line 7 - 8 (non line matched block)', function()
+ feed('2<c-w>w')
+ feed(':7,8diffget<cr>')
+ screen:expect([[
+ {1: }{10: 1 }common line │{1: }{10: 1 }^common line |
+ {1: }{10: 2 }{4:DEF }│{1: }{10: }{2:--------------------------------------------}|
+ {1: }{10: 3 }{8:GHI}{9: }│{1: }{10: 2 }{8:HIL}{9: }|
+ {1: }{10: 4 }{4:something }│{1: }{10: }{2:--------------------------------------------}|
+ {1: }{10: 5 } │{1: }{10: 3 } |
+ {1: }{10: 6 }{9:a}{8:DEF}{9:abc }│{1: }{10: 4 }{9:a}{8:ABC}{9:abc }|
+ {1: }{10: 7 }{8:xyz}{9: }│{1: }{10: 5 }{8:aABCabc}{9: }|
+ {1: }{10: 8 }{8:xyz}{9: }│{1: }{10: 6 }{8:aABCabc}{9: }|
+ {1: }{10: 9 }xyz │{1: }{10: 7 }xyz |
+ {1: }{10: 10 }aDEFabc │{1: }{10: 8 }aDEFabc |
+ {1: }{10: 11 }aDEFabc │{1: }{10: 9 }aDEFabc |
+ {1: }{10: 12 }aDEFabc │{1: }{10: 10 }aDEFabc |
+ {1: }{10: 13 }common line │{1: }{10: 11 }common line |
+ {1: }{10: 14 }{4:DEF }│{1: }{10: }{2:--------------------------------------------}|
+ {1: }{10: 15 }{8:GHI}{9: }│{1: }{10: 12 }{8:HIL}{9: }|
+ {1: }{10: 16 }{4:something else }│{1: }{10: }{2:--------------------------------------------}|
+ {1: }{10: 17 }common line │{1: }{10: 13 }common line |
+ {1: }{10: 18 }something │{1: }{10: 14 }something |
+ {3:Xtest-functional-diff-screen-1.2 }{7:Xtest-functional-diff-screen-1 [+] }|
+ :7,8diffget |
+ ]])
+ end)
+ it('get part of the non linematched diff block in window 2 line 8 - 10 (line matched block)', function()
+ feed('2<c-w>w')
+ feed(':8,10diffget<cr>')
+ screen:expect([[
+ {1: }{10: 1 }common line │{1: }{10: 1 }^common line |
+ {1: }{10: 2 }{4:DEF }│{1: }{10: }{2:--------------------------------------------}|
+ {1: }{10: 3 }{8:GHI}{9: }│{1: }{10: 2 }{8:HIL}{9: }|
+ {1: }{10: 4 }{4:something }│{1: }{10: }{2:--------------------------------------------}|
+ {1: }{10: 5 } │{1: }{10: 3 } |
+ {1: }{10: 6 }{9:a}{8:DEF}{9:abc }│{1: }{10: 4 }{9:a}{8:ABC}{9:abc }|
+ {1: }{10: 7 }{8:xyz}{9: }│{1: }{10: 5 }{8:aABCabc}{9: }|
+ {1: }{10: 8 }{8:xyz}{9: }│{1: }{10: 6 }{8:aABCabc}{9: }|
+ {1: }{10: 9 }{8:xyz}{9: }│{1: }{10: 7 }{8:aABCabc}{9: }|
+ {1: }{10: 10 }{4:aDEFabc }│{1: }{10: }{2:--------------------------------------------}|
+ {1: }{10: 11 }{4:aDEFabc }│{1: }{10: }{2:--------------------------------------------}|
+ {1: }{10: 12 }{4:aDEFabc }│{1: }{10: }{2:--------------------------------------------}|
+ {1: }{10: 13 }common line │{1: }{10: 8 }common line |
+ {1: }{10: 14 }DEF │{1: }{10: 9 }DEF |
+ {1: }{10: 15 }GHI │{1: }{10: 10 }GHI |
+ {1: }{10: 16 }something else │{1: }{10: 11 }something else |
+ {1: }{10: 17 }common line │{1: }{10: 12 }common line |
+ {1: }{10: 18 }something │{1: }{10: 13 }something |
+ {3:Xtest-functional-diff-screen-1.2 }{7:Xtest-functional-diff-screen-1 [+] }|
+ :8,10diffget |
+ ]])
+ end)
+ end)
+end)
+
+describe('regressions', function()
+ local screen
+
+ it("doesn't crash with long lines", function()
+ clear()
+ feed(':set diffopt+=linematch:30<cr>')
+ screen = Screen.new(100, 20)
+ screen:attach()
+ -- line must be greater than MATCH_CHAR_MAX_LEN
+ helpers.curbufmeths.set_lines(0, -1, false, { string.rep('a', 1000)..'hello' })
+ helpers.exec 'vnew'
+ helpers.curbufmeths.set_lines(0, -1, false, { string.rep('a', 1010)..'world' })
+ helpers.exec 'windo diffthis'
+ end)
+end)
diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua
index 2cff7c1cf4..3052a74f38 100644
--- a/test/functional/ui/messages_spec.lua
+++ b/test/functional/ui/messages_spec.lua
@@ -9,10 +9,13 @@ local meths = helpers.meths
local async_meths = helpers.async_meths
local test_build_dir = helpers.test_build_dir
local nvim_prog = helpers.nvim_prog
-local iswin = helpers.iswin
+local exec = helpers.exec
local exc_exec = helpers.exc_exec
local exec_lua = helpers.exec_lua
local poke_eventloop = helpers.poke_eventloop
+local assert_alive = helpers.assert_alive
+local is_os = helpers.is_os
+local is_ci = helpers.is_ci
describe('ui/ext_messages', function()
local screen
@@ -869,7 +872,7 @@ stack traceback:
{1:~ }|
{1:~ }|
]], messages={
- { content = { { "wow, ", 7 }, { "such\n\nvery ", 2 }, { "color", 10 } }, kind = "" }
+ { content = { { "wow, ", 7 }, { "such\n\nvery ", 2 }, { "color", 10 } }, kind = "echomsg" }
}}
feed ':ls<cr>'
@@ -880,7 +883,7 @@ stack traceback:
{1:~ }|
{1:~ }|
]], messages={
- { content = { { '\n 1 %a "[No Name]" line 1' } }, kind = "echomsg" }
+ { content = { { '\n 1 %a "[No Name]" line 1' } }, kind = "" }
}}
feed ':messages<cr>'
@@ -905,6 +908,13 @@ stack traceback:
{1:~ }|
]]}
end)
+
+ it('does not truncate messages', function()
+ command('write Xtest')
+ screen:expect({messages={
+ {content = { { '"Xtest" [New] 0L, 0B written' } }, kind = "" }
+ }})
+ end)
end)
describe('ui/builtin messages', function()
@@ -1077,10 +1087,10 @@ 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. Original report
+ it('redraws UPD_NOT_VALID correctly after message', function()
+ -- edge case: only one window was set UPD_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.
+ -- window UPD_NOT_VALID and another to show a long message.
command("set more")
feed(':new<cr><c-w><c-w>')
screen:expect{grid=[[
@@ -1200,28 +1210,6 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim
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. |
@@ -1256,6 +1244,45 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim
bar^ |
]])
end)
+
+ it('consecutive calls to win_move_statusline() work after multiline message #21014',function()
+ async_meths.exec([[
+ echo "\n"
+ call win_move_statusline(0, -4)
+ call win_move_statusline(0, 4)
+ ]], false)
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {3: }|
+ |
+ {4:Press ENTER or type command to continue}^ |
+ ]])
+ feed('<CR>')
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ eq(1, meths.get_option('cmdheight'))
+ end)
+end)
+
+it('calling screenstring() after redrawing between messages without UI #20999', function()
+ clear()
+ exec([[
+ echo repeat('a', 100)
+ redraw
+ echo "\n"
+ call screenstring(1, 1)
+ ]])
+ assert_alive()
end)
describe('ui/ext_messages', function()
@@ -1285,7 +1312,6 @@ describe('ui/ext_messages', function()
{1:~ }|
{1:~ }|
{1:~ }|
- {1:~ }|
{MATCH:.*}|
{1:~ }|
{1:~ }Nvim is open source and freely distributable{1: }|
@@ -1296,6 +1322,8 @@ describe('ui/ext_messages', function()
{1:~ }type :q{5:<Enter>} to exit {1: }|
{1:~ }type :help{5:<Enter>} for help {1: }|
{1:~ }|
+ {1:~ }type :help news{5:<Enter>} to see changes in v{MATCH:%d+%.%d+}|
+ {1:~ }|
{MATCH:.*}|
{MATCH:.*}|
{1:~ }|
@@ -1303,7 +1331,6 @@ describe('ui/ext_messages', function()
{1:~ }|
{1:~ }|
{1:~ }|
- {1:~ }|
]])
feed("<c-l>")
@@ -1341,7 +1368,6 @@ describe('ui/ext_messages', function()
|
|
|
- |
{MATCH:.*}|
|
Nvim is open source and freely distributable |
@@ -1352,6 +1378,8 @@ describe('ui/ext_messages', function()
type :q{5:<Enter>} to exit |
type :help{5:<Enter>} for help |
|
+ type :help news{5:<Enter>} to see changes in v{MATCH:%d+%.%d+}|
+ |
{MATCH:.*}|
{MATCH:.*}|
|
@@ -1359,7 +1387,6 @@ describe('ui/ext_messages', function()
|
|
|
- |
]], messages={
{content = { { "Press ENTER or type command to continue", 4 } }, kind = "return_prompt" }
}}
@@ -1432,7 +1459,6 @@ describe('ui/ext_messages', function()
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>")
@@ -1477,7 +1503,7 @@ describe('ui/msg_puts_printf', function()
screen = Screen.new(25, 5)
screen:attach()
- if iswin() then
+ if is_os('win') then
if os.execute('chcp 932 > NUL 2>&1') ~= 0 then
pending('missing japanese language features', function() end)
return
@@ -1488,7 +1514,7 @@ describe('ui/msg_puts_printf', function()
if (exc_exec('lang ja_JP.UTF-8') ~= 0) then
pending('Locale ja_JP.UTF-8 not supported', function() end)
return
- elseif helpers.isCI() then
+ elseif is_ci() then
-- Fails non--Windows CI. Message catalog directory issue?
pending('fails on unix CI', function() end)
return
@@ -2020,4 +2046,55 @@ aliquip ex ea commodo consequat.]])
|
]]}
end)
+
+ it('with cmdheight=0 does not crash with g<', function()
+ command('set cmdheight=0')
+ feed(':ls<cr>')
+ screen:expect{grid=[[
+ |
+ {1:~ }|
+ {12: }|
+ :ls |
+ 1 %a "[No Name]" |
+ line 1 |
+ {4:Press ENTER or type command to cont}|
+ {4:inue}^ |
+ ]]}
+
+ feed('<cr>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]]}
+
+ feed('g<lt>')
+ screen:expect{grid=[[
+ |
+ {1:~ }|
+ {12: }|
+ :ls |
+ 1 %a "[No Name]" |
+ line 1 |
+ {4:Press ENTER or type command to cont}|
+ {4:inue}^ |
+ ]]}
+
+ feed('<cr>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]]}
+ end)
end)
diff --git a/test/functional/ui/mode_spec.lua b/test/functional/ui/mode_spec.lua
index 9390f268b3..cf4eb034e0 100644
--- a/test/functional/ui/mode_spec.lua
+++ b/test/functional/ui/mode_spec.lua
@@ -17,6 +17,7 @@ describe('ui mode_change event', function()
[1] = {bold=true, reverse=true},
[2] = {bold=true},
[3] = {reverse=true},
+ [4] = {background=Screen.colors.Red, foreground=Screen.colors.White}, -- ErrorMsg
})
end)
@@ -43,6 +44,25 @@ describe('ui mode_change event', function()
{0:~ }|
|
]], mode="normal"}
+
+ screen:try_resize(50, 4)
+ command('set nomodifiable')
+
+ feed('c')
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]], mode="operator"}
+
+ feed('c')
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {4:E21: Cannot make changes, 'modifiable' is off} |
+ ]], mode="normal"}
end)
it('works in insert mode', function()
diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua
index 9896b11218..f705678bd5 100644
--- a/test/functional/ui/mouse_spec.lua
+++ b/test/functional/ui/mouse_spec.lua
@@ -4,6 +4,7 @@ local clear, feed, meths = helpers.clear, helpers.feed, helpers.meths
local insert, feed_command = helpers.insert, helpers.feed_command
local eq, funcs = helpers.eq, helpers.funcs
local command = helpers.command
+local exec = helpers.exec
describe('ui/mouse/input', function()
local screen
@@ -648,8 +649,10 @@ describe('ui/mouse/input', function()
]]}
end)
- it('two clicks will select the word and enter VISUAL', function()
- feed('<LeftMouse><2,2><LeftMouse><2,2>')
+ it('two clicks will enter VISUAL and dragging selects words', function()
+ feed('<LeftMouse><2,2>')
+ feed('<LeftRelease><2,2>')
+ feed('<LeftMouse><2,2>')
screen:expect([[
testing |
mouse |
@@ -657,10 +660,38 @@ describe('ui/mouse/input', function()
{0:~ }|
{2:-- VISUAL --} |
]])
+ feed('<LeftDrag><0,1>')
+ screen:expect([[
+ testing |
+ ^m{1:ouse} |
+ {1:support} and selection |
+ {0:~ }|
+ {2:-- VISUAL --} |
+ ]])
+ feed('<LeftDrag><4,0>')
+ screen:expect([[
+ ^t{1:esting} |
+ {1:mouse} |
+ {1:support} and selection |
+ {0:~ }|
+ {2:-- VISUAL --} |
+ ]])
+ feed('<LeftDrag><14,2>')
+ screen:expect([[
+ testing |
+ mouse |
+ {1:support and selectio}^n |
+ {0:~ }|
+ {2:-- VISUAL --} |
+ ]])
end)
- it('three clicks will select the line and enter VISUAL LINE', function()
- feed('<LeftMouse><2,2><LeftMouse><2,2><LeftMouse><2,2>')
+ it('three clicks will enter VISUAL LINE and dragging selects lines', function()
+ feed('<LeftMouse><2,2>')
+ feed('<LeftRelease><2,2>')
+ feed('<LeftMouse><2,2>')
+ feed('<LeftRelease><2,2>')
+ feed('<LeftMouse><2,2>')
screen:expect([[
testing |
mouse |
@@ -668,10 +699,40 @@ describe('ui/mouse/input', function()
{0:~ }|
{2:-- VISUAL LINE --} |
]])
+ feed('<LeftDrag><0,1>')
+ screen:expect([[
+ testing |
+ ^m{1:ouse} |
+ {1:support and selection} |
+ {0:~ }|
+ {2:-- VISUAL LINE --} |
+ ]])
+ feed('<LeftDrag><4,0>')
+ screen:expect([[
+ {1:test}^i{1:ng} |
+ {1:mouse} |
+ {1:support and selection} |
+ {0:~ }|
+ {2:-- VISUAL LINE --} |
+ ]])
+ feed('<LeftDrag><14,2>')
+ screen:expect([[
+ testing |
+ mouse |
+ {1:support and se}^l{1:ection} |
+ {0:~ }|
+ {2:-- VISUAL LINE --} |
+ ]])
end)
- it('four clicks will enter VISUAL BLOCK', function()
- feed('<LeftMouse><2,2><LeftMouse><2,2><LeftMouse><2,2><LeftMouse><2,2>')
+ it('four clicks will enter VISUAL BLOCK and dragging selects blockwise', function()
+ feed('<LeftMouse><2,2>')
+ feed('<LeftRelease><2,2>')
+ feed('<LeftMouse><2,2>')
+ feed('<LeftRelease><2,2>')
+ feed('<LeftMouse><2,2>')
+ feed('<LeftRelease><2,2>')
+ feed('<LeftMouse><2,2>')
screen:expect([[
testing |
mouse |
@@ -679,6 +740,30 @@ describe('ui/mouse/input', function()
{0:~ }|
{2:-- VISUAL BLOCK --} |
]])
+ feed('<LeftDrag><0,1>')
+ screen:expect([[
+ testing |
+ ^m{1:ou}se |
+ {1:sup}port and selection |
+ {0:~ }|
+ {2:-- VISUAL BLOCK --} |
+ ]])
+ feed('<LeftDrag><4,0>')
+ screen:expect([[
+ te{1:st}^ing |
+ mo{1:use} |
+ su{1:ppo}rt and selection |
+ {0:~ }|
+ {2:-- VISUAL BLOCK --} |
+ ]])
+ feed('<LeftDrag><14,2>')
+ screen:expect([[
+ testing |
+ mouse |
+ su{1:pport and se}^lection |
+ {0:~ }|
+ {2:-- VISUAL BLOCK --} |
+ ]])
end)
it('right click extends visual selection to the clicked location', function()
@@ -883,6 +968,49 @@ describe('ui/mouse/input', function()
]])
end)
+ it("'sidescrolloff' applies to horizontal scrolling", function()
+ command('set nowrap')
+ command('set sidescrolloff=4')
+
+ feed("I <esc>020ib<esc>0")
+ screen:expect([[
+ testing |
+ mouse |
+ ^bbbbbbbbbbbbbbbbbbbb supp|
+ {0:~ }|
+ |
+ ]])
+
+ meths.input_mouse('wheel', 'right', '', 0, 0, 27)
+ screen:expect([[
+ g |
+ |
+ bbbb^bbbbbbbbbb support an|
+ {0:~ }|
+ |
+ ]])
+
+ -- window-local 'sidescrolloff' should override global value. #21162
+ command('setlocal sidescrolloff=2')
+ feed('0')
+ screen:expect([[
+ testing |
+ mouse |
+ ^bbbbbbbbbbbbbbbbbbbb supp|
+ {0:~ }|
+ |
+ ]])
+
+ meths.input_mouse('wheel', 'right', '', 0, 0, 27)
+ screen:expect([[
+ g |
+ |
+ bb^bbbbbbbbbbbb support an|
+ {0:~ }|
+ |
+ ]])
+ end)
+
describe('on concealed text', function()
-- Helpful for reading the test expectations:
-- :match Error /\^/
@@ -1585,9 +1713,151 @@ describe('ui/mouse/input', function()
eq(0, meths.get_var('mouse_up2'))
end)
- it('feeding <MouseMove> does not use uninitialized memory #19480', function()
+ it('<MouseMove> is not translated into multiclicks and can be mapped', function()
+ meths.set_var('mouse_move', 0)
+ meths.set_var('mouse_move2', 0)
+ command('nnoremap <MouseMove> <Cmd>let g:mouse_move += 1<CR>')
+ command('nnoremap <2-MouseMove> <Cmd>let g:mouse_move2 += 1<CR>')
+ feed('<MouseMove><0,0>')
+ feed('<MouseMove><0,0>')
+ meths.input_mouse('move', '', '', 0, 0, 0)
+ meths.input_mouse('move', '', '', 0, 0, 0)
+ eq(4, meths.get_var('mouse_move'))
+ eq(0, meths.get_var('mouse_move2'))
+ end)
+
+ it('feeding <MouseMove> in Normal mode does not use uninitialized memory #19480', function()
feed('<MouseMove>')
helpers.poke_eventloop()
helpers.assert_alive()
end)
+
+ it('mousemodel=popup_setpos', function()
+ screen:try_resize(80, 24)
+ exec([[
+ 5new
+ call setline(1, ['the dish ran away with the spoon',
+ \ 'the cow jumped over the moon' ])
+
+ set mouse=a mousemodel=popup_setpos
+
+ aunmenu PopUp
+ nmenu PopUp.foo :let g:menustr = 'foo'<CR>
+ nmenu PopUp.bar :let g:menustr = 'bar'<CR>
+ nmenu PopUp.baz :let g:menustr = 'baz'<CR>
+ vmenu PopUp.foo y:<C-U>let g:menustr = 'foo'<CR>
+ vmenu PopUp.bar y:<C-U>let g:menustr = 'bar'<CR>
+ vmenu PopUp.baz y:<C-U>let g:menustr = 'baz'<CR>
+ ]])
+
+ meths.win_set_cursor(0, {1, 0})
+ meths.input_mouse('right', 'press', '', 0, 0, 4)
+ meths.input_mouse('right', 'release', '', 0, 0, 4)
+ feed('<Down><Down><CR>')
+ eq('bar', meths.get_var('menustr'))
+ eq({1, 4}, meths.win_get_cursor(0))
+
+ -- Test for right click in visual mode inside the selection
+ funcs.setreg('"', '')
+ meths.win_set_cursor(0, {1, 9})
+ feed('vee')
+ meths.input_mouse('right', 'press', '', 0, 0, 11)
+ meths.input_mouse('right', 'release', '', 0, 0, 11)
+ feed('<Down><CR>')
+ eq({1, 9}, meths.win_get_cursor(0))
+ eq('ran away', funcs.getreg('"'))
+
+ -- Test for right click in visual mode right before the selection
+ funcs.setreg('"', '')
+ meths.win_set_cursor(0, {1, 9})
+ feed('vee')
+ meths.input_mouse('right', 'press', '', 0, 0, 8)
+ meths.input_mouse('right', 'release', '', 0, 0, 8)
+ feed('<Down><CR>')
+ eq({1, 8}, meths.win_get_cursor(0))
+ eq('', funcs.getreg('"'))
+
+ -- Test for right click in visual mode right after the selection
+ funcs.setreg('"', '')
+ meths.win_set_cursor(0, {1, 9})
+ feed('vee')
+ meths.input_mouse('right', 'press', '', 0, 0, 17)
+ meths.input_mouse('right', 'release', '', 0, 0, 17)
+ feed('<Down><CR>')
+ eq({1, 17}, meths.win_get_cursor(0))
+ eq('', funcs.getreg('"'))
+
+ -- Test for right click in block-wise visual mode inside the selection
+ funcs.setreg('"', '')
+ meths.win_set_cursor(0, {1, 15})
+ feed('<C-V>j3l')
+ meths.input_mouse('right', 'press', '', 0, 1, 16)
+ meths.input_mouse('right', 'release', '', 0, 1, 16)
+ feed('<Down><CR>')
+ eq({1, 15}, meths.win_get_cursor(0))
+ eq('\0224', funcs.getregtype('"'))
+
+ -- Test for right click in block-wise visual mode outside the selection
+ funcs.setreg('"', '')
+ meths.win_set_cursor(0, {1, 15})
+ feed('<C-V>j3l')
+ meths.input_mouse('right', 'press', '', 0, 1, 1)
+ meths.input_mouse('right', 'release', '', 0, 1, 1)
+ feed('<Down><CR>')
+ eq({2, 1}, meths.win_get_cursor(0))
+ eq('v', funcs.getregtype('"'))
+ eq('', funcs.getreg('"'))
+
+ -- Test for right click in line-wise visual mode inside the selection
+ funcs.setreg('"', '')
+ meths.win_set_cursor(0, {1, 15})
+ feed('V')
+ meths.input_mouse('right', 'press', '', 0, 0, 9)
+ meths.input_mouse('right', 'release', '', 0, 0, 9)
+ feed('<Down><CR>')
+ eq({1, 0}, meths.win_get_cursor(0)) -- After yanking, the cursor goes to 1,1
+ eq('V', funcs.getregtype('"'))
+ eq(1, #funcs.getreg('"', 1, true))
+
+ -- Test for right click in multi-line line-wise visual mode inside the selection
+ funcs.setreg('"', '')
+ meths.win_set_cursor(0, {1, 15})
+ feed('Vj')
+ meths.input_mouse('right', 'press', '', 0, 1, 19)
+ meths.input_mouse('right', 'release', '', 0, 1, 19)
+ feed('<Down><CR>')
+ eq({1, 0}, meths.win_get_cursor(0)) -- After yanking, the cursor goes to 1,1
+ eq('V', funcs.getregtype('"'))
+ eq(2, #funcs.getreg('"', 1, true))
+
+ -- Test for right click in line-wise visual mode outside the selection
+ funcs.setreg('"', '')
+ meths.win_set_cursor(0, {1, 15})
+ feed('V')
+ meths.input_mouse('right', 'press', '', 0, 1, 9)
+ meths.input_mouse('right', 'release', '', 0, 1, 9)
+ feed('<Down><CR>')
+ eq({2, 9}, meths.win_get_cursor(0))
+ eq('', funcs.getreg('"'))
+
+ -- Try clicking on the status line
+ funcs.setreg('"', '')
+ meths.win_set_cursor(0, {1, 9})
+ feed('vee')
+ meths.input_mouse('right', 'press', '', 0, 5, 1)
+ meths.input_mouse('right', 'release', '', 0, 5, 1)
+ feed('<Down><CR>')
+ eq({1, 9}, meths.win_get_cursor(0))
+ eq('ran away', funcs.getreg('"'))
+
+ -- Try clicking outside the window
+ funcs.setreg('"', '')
+ meths.win_set_cursor(0, {2, 1})
+ feed('vee')
+ meths.input_mouse('right', 'press', '', 0, 6, 1)
+ meths.input_mouse('right', 'release', '', 0, 6, 1)
+ feed('<Down><CR>')
+ eq(2, funcs.winnr())
+ eq('', funcs.getreg('"'))
+ end)
end)
diff --git a/test/functional/ui/multigrid_spec.lua b/test/functional/ui/multigrid_spec.lua
index b30aa67fd3..71adeb42a4 100644
--- a/test/functional/ui/multigrid_spec.lua
+++ b/test/functional/ui/multigrid_spec.lua
@@ -3,7 +3,9 @@ local Screen = require('test.functional.ui.screen')
local clear = helpers.clear
local feed, command, insert = helpers.feed, helpers.command, helpers.insert
local eq = helpers.eq
+local funcs = helpers.funcs
local meths = helpers.meths
+local curwin = helpers.curwin
local poke_eventloop = helpers.poke_eventloop
@@ -871,6 +873,15 @@ describe('ext_multigrid', function()
before_each(function()
screen:try_resize_grid(2, 60, 20)
end)
+
+ it('winwidth() winheight() getwininfo() return inner width and height #19743', function()
+ eq(60, funcs.winwidth(0))
+ eq(20, funcs.winheight(0))
+ local win_info = funcs.getwininfo(curwin().id)[1]
+ eq(60, win_info.width)
+ eq(20, win_info.height)
+ end)
+
it('gets written till grid width', function()
insert(('a'):rep(60).."\n")
@@ -2370,8 +2381,7 @@ describe('ext_multigrid', function()
end)
it('with winbar', function()
- command 'split'
- command 'setlocal winbar=very\\ bar'
+ command('split')
screen:expect{grid=[[
## grid 1
[4:-----------------------------------------------------]|
@@ -2397,16 +2407,100 @@ describe('ext_multigrid', function()
## grid 3
|
## grid 4
- {7:very bar }|
^ |
{1:~ }|
{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};
+ }}
+
+ -- XXX: hack to get notifications. Could use next_msg() also.
+ local orig_handle_win_pos = screen._handle_win_pos
+ local win_pos = {}
+ function screen._handle_win_pos(self, grid, win, startrow, startcol, width, height)
+ table.insert(win_pos, {grid, win, startrow, startcol, width, height})
+ orig_handle_win_pos(self, grid, win, startrow, startcol, width, height)
+ end
+
+ 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};
+ }}
+ eq({}, win_pos)
+
+ command('setlocal winbar=')
+ 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
+ ^ |
+ {1:~ }|
+ {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};
}}
+ eq({}, win_pos)
end)
it('with winbar dragging statusline with mouse works correctly', function()
diff --git a/test/functional/ui/options_spec.lua b/test/functional/ui/options_spec.lua
index 8d7c404637..9d20229ce1 100644
--- a/test/functional/ui/options_spec.lua
+++ b/test/functional/ui/options_spec.lua
@@ -19,10 +19,12 @@ describe('UI receives option updates', function()
linespace=0,
pumblend=0,
mousefocus=false,
+ mousemoveevent=false,
showtabline=1,
termguicolors=false,
ttimeout=true,
ttimeoutlen=50,
+ verbose=0,
ext_cmdline=false,
ext_popupmenu=false,
ext_tabline=false,
@@ -131,6 +133,12 @@ describe('UI receives option updates', function()
eq(expected, screen.options)
end)
+ command("set mousemoveevent")
+ expected.mousemoveevent = true
+ screen:expect(function()
+ eq(expected, screen.options)
+ end)
+
command("set nottimeout")
expected.ttimeout = false
screen:expect(function()
@@ -187,7 +195,7 @@ end)
describe('UI can set terminal option', function()
local screen
before_each(function()
- -- by default we implicity "--cmd 'set bg=light'" which ruins everything
+ -- by default we implicitly "--cmd 'set bg=light'" which ruins everything
clear{args_rm={'--cmd'}}
screen = Screen.new(20,5)
end)
diff --git a/test/functional/ui/output_spec.lua b/test/functional/ui/output_spec.lua
index 9bb067ed8e..223844405e 100644
--- a/test/functional/ui/output_spec.lua
+++ b/test/functional/ui/output_spec.lua
@@ -6,13 +6,14 @@ local mkdir, write_file, rmdir = helpers.mkdir, helpers.write_file, helpers.rmdi
local eq = helpers.eq
local feed = helpers.feed
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
+local skip = helpers.skip
+local is_os = helpers.is_os
describe("shell command :!", function()
local screen
@@ -36,7 +37,7 @@ describe("shell command :!", function()
end)
it("displays output without LF/EOF. #4646 #4569 #3772", function()
- if helpers.pending_win32(pending) then return end
+ skip(is_os('win'))
-- NOTE: We use a child nvim (within a :term buffer)
-- to avoid triggering a UI flush.
child_session.feed_data(":!printf foo; sleep 200\n")
@@ -52,9 +53,7 @@ describe("shell command :!", function()
end)
it("throttles shell-command output greater than ~10KB", function()
- if 'openbsd' == helpers.uname() then
- pending('FIXME #10804')
- end
+ skip(is_os('openbsd'), 'FIXME #10804')
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.
@@ -96,9 +95,7 @@ describe("shell command :!", function()
end)
it('handles control codes', function()
- if iswin() then
- pending('missing printf')
- end
+ skip(is_os('win'), 'missing printf')
local screen = Screen.new(50, 4)
screen:set_default_attr_ids {
[1] = {bold = true, reverse = true};
@@ -173,10 +170,10 @@ describe("shell command :!", function()
end)
it("doesn't truncate Last line of shell output #3269", function()
- command(helpers.iswin()
+ command(is_os('win')
and [[nnoremap <silent>\l :!dir /b bang_filter_spec<cr>]]
or [[nnoremap <silent>\l :!ls bang_filter_spec<cr>]])
- local result = (helpers.iswin()
+ local result = (is_os('win')
and [[:!dir /b bang_filter_spec]]
or [[:!ls bang_filter_spec ]])
feed([[\l]])
@@ -215,7 +212,7 @@ describe("shell command :!", function()
it('handles multibyte sequences split over buffer boundaries', function()
command('cd '..nvim_dir)
- local cmd = iswin() and '!shell-test UTF-8 ' or '!./shell-test UTF-8'
+ local cmd = is_os('win') 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([[
@@ -265,7 +262,7 @@ describe("shell command :!", function()
|
Press ENTER or type command to continue^ |
]])
- if iswin() then
+ if is_os('win') then
feed_command([[!& 'cmd.exe' /c 'echo $a']])
screen:expect([[
:!& 'cmd.exe' /c 'echo $a' |
diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua
index e11cd1e859..c681453294 100644
--- a/test/functional/ui/popupmenu_spec.lua
+++ b/test/functional/ui/popupmenu_spec.lua
@@ -259,174 +259,339 @@ describe('ui/ext_popupmenu', function()
{2:-- INSERT --} |
]])
- command('imap <f1> <cmd>call nvim_select_popupmenu_item(2,v:true,v:false,{})<cr>')
- command('imap <f2> <cmd>call nvim_select_popupmenu_item(-1,v:false,v:false,{})<cr>')
- command('imap <f3> <cmd>call nvim_select_popupmenu_item(1,v:false,v:true,{})<cr>')
- feed('<C-r>=TestComplete()<CR>')
- screen:expect{grid=[[
+ command('set wildmenu')
+ command('set wildoptions=pum')
+ local expected_wildpum = {
+ { "define", "", "", "" },
+ { "jump", "", "", "" },
+ { "list", "", "", "" },
+ { "place", "", "", "" },
+ { "undefine", "", "", "" },
+ { "unplace", "", "", "" },
+ }
+ feed('<Esc>:sign <Tab>')
+ screen:expect({grid = [[
+ |
|
- foo^ |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
- {2:-- INSERT --} |
- ]], popupmenu={
- items=expected,
- pos=0,
- anchor={1,1,0},
- }}
+ :sign define^ |
+ ]], popupmenu = {
+ items = expected_wildpum,
+ pos = 0,
+ anchor = { 1, 7, 6 },
+ }})
- feed('<f1>')
- screen:expect{grid=[[
+ meths.select_popupmenu_item(-1, true, false, {})
+ screen:expect({grid = [[
+ |
|
- spam^ |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
- {2:-- INSERT --} |
- ]], popupmenu={
- items=expected,
- pos=2,
- anchor={1,1,0},
- }}
+ :sign ^ |
+ ]], popupmenu = {
+ items = expected_wildpum,
+ pos = -1,
+ anchor = { 1, 7, 6 },
+ }})
- feed('<f2>')
- screen:expect{grid=[[
+ meths.select_popupmenu_item(5, true, false, {})
+ screen:expect({grid = [[
+ |
|
- spam^ |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
- {2:-- INSERT --} |
- ]], popupmenu={
- items=expected,
- pos=-1,
- anchor={1,1,0},
- }}
+ :sign unplace^ |
+ ]], popupmenu = {
+ items = expected_wildpum,
+ pos = 5,
+ anchor = { 1, 7, 6 },
+ }})
- feed('<f3>')
- screen:expect([[
+ meths.select_popupmenu_item(-1, true, true, {})
+ screen:expect({grid = [[
+ |
|
- bar^ |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
- {2:-- INSERT --} |
- ]])
+ :sign ^ |
+ ]]})
- -- also should work for builtin popupmenu
- screen:set_option('ext_popupmenu', false)
- feed('<C-r>=TestComplete()<CR>')
- screen:expect([[
+ feed('<Tab>')
+ screen:expect({grid = [[
|
- 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 --} |
- ]])
+ {1:~ }|
+ :sign define^ |
+ ]], popupmenu = {
+ items = expected_wildpum,
+ pos = 0,
+ anchor = { 1, 7, 6 },
+ }})
- feed('<f3>')
- screen:expect([[
+ meths.select_popupmenu_item(5, true, true, {})
+ screen:expect({grid = [[
+ |
|
- bar^ |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
- {2:-- INSERT --} |
- ]])
+ :sign unplace^ |
+ ]]})
- 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 --} |
- ]])
+ local function test_pum_select_mappings()
+ screen:set_option('ext_popupmenu', true)
+ feed('<Esc>A<C-r>=TestComplete()<CR>')
+ screen:expect{grid=[[
+ |
+ foo^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]], popupmenu={
+ items=expected,
+ pos=0,
+ anchor={1,1,0},
+ }}
- feed('<f1>')
- screen:expect([[
- |
- spam^ |
- {7:fo x the foo }{1: }|
- {7:bar }{1: }|
- {6:spam }{1: }|
- {1:~ }|
- {1:~ }|
- {2:-- INSERT --} |
- ]])
+ feed('<f1>')
+ screen:expect{grid=[[
+ |
+ spam^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]], popupmenu={
+ items=expected,
+ pos=2,
+ anchor={1,1,0},
+ }}
- feed('<f2>')
- screen:expect([[
- |
- spam^ |
- {7:fo x the foo }{1: }|
- {7:bar }{1: }|
- {7:spam }{1: }|
- {1:~ }|
- {1:~ }|
- {2:-- INSERT --} |
- ]])
+ feed('<f2>')
+ screen:expect{grid=[[
+ |
+ spam^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]], popupmenu={
+ items=expected,
+ pos=-1,
+ anchor={1,1,0},
+ }}
- feed('<f3>')
- screen:expect([[
- |
- bar^ |
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {2:-- INSERT --} |
+ feed('<f3>')
+ screen:expect([[
+ |
+ bar^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
+
+ feed('<Esc>:sign <Tab>')
+ screen:expect({grid = [[
+ |
+ bar |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :sign define^ |
+ ]], popupmenu = {
+ items = expected_wildpum,
+ pos = 0,
+ anchor = { 1, 7, 6 },
+ }})
+
+ feed('<f1>')
+ screen:expect({grid = [[
+ |
+ bar |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :sign list^ |
+ ]], popupmenu = {
+ items = expected_wildpum,
+ pos = 2,
+ anchor = { 1, 7, 6 },
+ }})
+
+ feed('<f2>')
+ screen:expect({grid = [[
+ |
+ bar |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :sign ^ |
+ ]], popupmenu = {
+ items = expected_wildpum,
+ pos = -1,
+ anchor = { 1, 7, 6 },
+ }})
+
+ feed('<f3>')
+ screen:expect({grid = [[
+ |
+ bar |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :sign jump^ |
+ ]]})
+
+ -- also should work for builtin popupmenu
+ screen:set_option('ext_popupmenu', false)
+ feed('<Esc>A<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>:sign <Tab>')
+ screen:expect([[
+ |
+ bar {6: define } |
+ {1:~ }{7: jump }{1: }|
+ {1:~ }{7: list }{1: }|
+ {1:~ }{7: place }{1: }|
+ {1:~ }{7: undefine }{1: }|
+ {1:~ }{7: unplace }{1: }|
+ :sign define^ |
+ ]])
+
+ feed('<f1>')
+ screen:expect([[
+ |
+ bar {7: define } |
+ {1:~ }{7: jump }{1: }|
+ {1:~ }{6: list }{1: }|
+ {1:~ }{7: place }{1: }|
+ {1:~ }{7: undefine }{1: }|
+ {1:~ }{7: unplace }{1: }|
+ :sign list^ |
+ ]])
+
+ feed('<f2>')
+ screen:expect([[
+ |
+ bar {7: define } |
+ {1:~ }{7: jump }{1: }|
+ {1:~ }{7: list }{1: }|
+ {1:~ }{7: place }{1: }|
+ {1:~ }{7: undefine }{1: }|
+ {1:~ }{7: unplace }{1: }|
+ :sign ^ |
+ ]])
+
+ feed('<f3>')
+ screen:expect([[
+ |
+ bar |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :sign jump^ |
+ ]])
+ end
+
+ command('map! <f1> <cmd>call nvim_select_popupmenu_item(2,v:true,v:false,{})<cr>')
+ command('map! <f2> <cmd>call nvim_select_popupmenu_item(-1,v:false,v:false,{})<cr>')
+ command('map! <f3> <cmd>call nvim_select_popupmenu_item(1,v:false,v:true,{})<cr>')
+ test_pum_select_mappings()
+
+ command('unmap! <f1>')
+ command('unmap! <f2>')
+ command('unmap! <f3>')
+ exec_lua([[
+ vim.keymap.set('!', '<f1>', function() vim.api.nvim_select_popupmenu_item(2, true, false, {}) end)
+ vim.keymap.set('!', '<f2>', function() vim.api.nvim_select_popupmenu_item(-1, false, false, {}) end)
+ vim.keymap.set('!', '<f3>', function() vim.api.nvim_select_popupmenu_item(1, false, true, {}) end)
]])
+ test_pum_select_mappings()
feed('<esc>ddiaa bb cc<cr>')
feed('<c-x><c-n>')
@@ -780,6 +945,82 @@ describe('ui/ext_popupmenu', function()
}}
end)
+
+ it('does not interfere with mousemodel=popup', function()
+ exec([[
+ 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>
+ ]])
+ feed('o<C-r>=TestComplete()<CR>')
+ screen:expect{grid=[[
+ |
+ foo^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]], popupmenu={
+ items=expected,
+ pos=0,
+ anchor={1,1,0},
+ }}
+
+ feed('<c-p>')
+ screen:expect{grid=[[
+ |
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]], popupmenu={
+ items=expected,
+ pos=-1,
+ anchor={1,1,0},
+ }}
+
+ feed('<esc>')
+ screen:expect{grid=[[
+ |
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+ feed('<RightMouse><0,0>')
+ screen:expect([[
+ |
+ {7:^foo } |
+ {7:bar }{1: }|
+ {7:baz }{1: }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ feed('<esc>')
+ screen:expect([[
+ |
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end)
end)
@@ -802,6 +1043,8 @@ describe('builtin popupmenu', function()
[4] = {bold = true, reverse = true},
[5] = {bold = true, foreground = Screen.colors.SeaGreen},
[6] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ [7] = {background = Screen.colors.Yellow}, -- Search
+ [8] = {foreground = Screen.colors.Red},
})
end)
@@ -949,6 +1192,66 @@ describe('builtin popupmenu', function()
]])
end)
+ -- oldtest: Test_pum_with_preview_win()
+ it('preview window opened during completion', function()
+ exec([[
+ funct Omni_test(findstart, base)
+ if a:findstart
+ return col(".") - 1
+ endif
+ return [#{word: "one", info: "1info"}, #{word: "two", info: "2info"}, #{word: "three", info: "3info"}]
+ endfunc
+ set omnifunc=Omni_test
+ set completeopt+=longest
+ ]])
+ feed('Gi<C-X><C-O>')
+ screen:expect([[
+ ^ |
+ {n:one }{1: }|
+ {n:two }{1: }|
+ {n:three }{1: }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- }{8:Back at original} |
+ ]])
+ feed('<C-N>')
+ screen:expect([[
+ 1info |
+ |
+ {1:~ }|
+ {3:[Scratch] [Preview] }|
+ one^ |
+ {s:one }{1: }|
+ {n:two }{1: }|
+ {n:three }{1: }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {4:[No Name] [+] }|
+ {2:-- }{5:match 1 of 3} |
+ ]])
+ end)
+
it('with vsplits', function()
insert('aaa aab aac\n')
feed(':vsplit<cr>')
@@ -1990,6 +2293,22 @@ describe('builtin popupmenu', function()
efine unplace^ |
]])
+ -- Pressing <Left> after that should move the cursor
+ feed('<Left>')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {4: }|
+ :sign define jump list place und|
+ efine unplac^e |
+ ]])
+ feed('<End>')
+
-- Pressing <C-D> when the popup menu is displayed should remove the popup
-- menu
feed('<C-U>sign <Tab><C-D>')
@@ -2206,8 +2525,130 @@ describe('builtin popupmenu', function()
{1:~ }{n: xyz }{1: }|
:e あいう/123^ |
]])
+ feed('<Esc>')
- feed('<esc>')
+ -- Pressing <PageDown> should scroll the menu downward
+ feed(':sign <Tab><PageDown>')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }{n: define }{1: }|
+ {1:~ }{n: jump }{1: }|
+ {1:~ }{n: list }{1: }|
+ {1:~ }{n: place }{1: }|
+ {1:~ }{s: undefine }{1: }|
+ {1:~ }{n: unplace }{1: }|
+ :sign undefine^ |
+ ]])
+ feed('<PageDown>')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }{n: define }{1: }|
+ {1:~ }{n: jump }{1: }|
+ {1:~ }{n: list }{1: }|
+ {1:~ }{n: place }{1: }|
+ {1:~ }{n: undefine }{1: }|
+ {1:~ }{s: unplace }{1: }|
+ :sign unplace^ |
+ ]])
+ feed('<PageDown>')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }{n: define }{1: }|
+ {1:~ }{n: jump }{1: }|
+ {1:~ }{n: list }{1: }|
+ {1:~ }{n: place }{1: }|
+ {1:~ }{n: undefine }{1: }|
+ {1:~ }{n: unplace }{1: }|
+ :sign ^ |
+ ]])
+ feed('<PageDown>')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }{s: define }{1: }|
+ {1:~ }{n: jump }{1: }|
+ {1:~ }{n: list }{1: }|
+ {1:~ }{n: place }{1: }|
+ {1:~ }{n: undefine }{1: }|
+ {1:~ }{n: unplace }{1: }|
+ :sign define^ |
+ ]])
+ feed('<C-U>sign <Tab><Right><Right><PageDown>')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }{n: define }{1: }|
+ {1:~ }{n: jump }{1: }|
+ {1:~ }{n: list }{1: }|
+ {1:~ }{n: place }{1: }|
+ {1:~ }{n: undefine }{1: }|
+ {1:~ }{s: unplace }{1: }|
+ :sign unplace^ |
+ ]])
+
+ -- Pressing <PageUp> should scroll the menu upward
+ feed('<C-U>sign <Tab><PageUp>')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }{n: define }{1: }|
+ {1:~ }{n: jump }{1: }|
+ {1:~ }{n: list }{1: }|
+ {1:~ }{n: place }{1: }|
+ {1:~ }{n: undefine }{1: }|
+ {1:~ }{n: unplace }{1: }|
+ :sign ^ |
+ ]])
+ feed('<PageUp>')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }{n: define }{1: }|
+ {1:~ }{n: jump }{1: }|
+ {1:~ }{n: list }{1: }|
+ {1:~ }{n: place }{1: }|
+ {1:~ }{n: undefine }{1: }|
+ {1:~ }{s: unplace }{1: }|
+ :sign unplace^ |
+ ]])
+ feed('<PageUp>')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }{n: define }{1: }|
+ {1:~ }{s: jump }{1: }|
+ {1:~ }{n: list }{1: }|
+ {1:~ }{n: place }{1: }|
+ {1:~ }{n: undefine }{1: }|
+ {1:~ }{n: unplace }{1: }|
+ :sign jump^ |
+ ]])
+ feed('<PageUp>')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }{s: define }{1: }|
+ {1:~ }{n: jump }{1: }|
+ {1:~ }{n: list }{1: }|
+ {1:~ }{n: place }{1: }|
+ {1:~ }{n: undefine }{1: }|
+ {1:~ }{n: unplace }{1: }|
+ :sign define^ |
+ ]])
+
+ feed('<Esc>')
-- check positioning with multibyte char in pattern
command("e långfile1")
@@ -2304,7 +2745,7 @@ describe('builtin popupmenu', function()
]])
end)
- it('wildoptions=pum with scrolled messages ', function()
+ it('wildoptions=pum with scrolled messages', function()
screen:try_resize(40,10)
command('set wildmenu')
command('set wildoptions=pum')
@@ -2401,6 +2842,49 @@ describe('builtin popupmenu', function()
]]}
end)
+ it('wildoptions=pum with a wrapped line in buffer vim-patch:8.2.4655', function()
+ screen:try_resize(32, 10)
+ meths.buf_set_lines(0, 0, -1, true, { ('a'):rep(100) })
+ command('set wildoptions+=pum')
+ feed('$')
+ feed(':sign <Tab>')
+ screen:expect([[
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ aaaa {s: define } |
+ {1:~ }{n: jump }{1: }|
+ {1:~ }{n: list }{1: }|
+ {1:~ }{n: place }{1: }|
+ {1:~ }{n: undefine }{1: }|
+ {1:~ }{n: unplace }{1: }|
+ :sign define^ |
+ ]])
+ end)
+
+ -- oldtest: Test_wildmenu_pum_clear_entries()
+ it('wildoptions=pum when using Ctrl-E as wildchar vim-patch:9.0.1030', function()
+ screen:try_resize(30, 10)
+ exec([[
+ set wildoptions=pum
+ set wildchar=<C-E>
+ ]])
+ feed(':sign <C-E><C-E>')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }{s: define }{1: }|
+ {1:~ }{n: jump }{1: }|
+ {1:~ }{n: list }{1: }|
+ {1:~ }{n: place }{1: }|
+ {1:~ }{n: undefine }{1: }|
+ {1:~ }{n: unplace }{1: }|
+ :sign define^ |
+ ]])
+ assert_alive()
+ end)
+
it("'pumblend' RGB-color", function()
screen:try_resize(60,14)
screen:set_default_attr_ids({
@@ -2739,7 +3223,7 @@ describe('builtin popupmenu', function()
menu PopUp.bar :let g:menustr = 'bar'<CR>
menu PopUp.baz :let g:menustr = 'baz'<CR>
]])
- meths.input_mouse('right', 'press', '', 0, 0, 4)
+ feed('<RightMouse><4,0>')
screen:expect([[
^popup menu test |
{1:~ }{n: foo }{1: }|
@@ -2776,7 +3260,7 @@ describe('builtin popupmenu', function()
:let g:menustr = 'bar' |
]])
eq('bar', meths.get_var('menustr'))
- meths.input_mouse('right', 'press', '', 0, 1, 20)
+ feed('<RightMouse><20,1>')
screen:expect([[
^popup menu test |
{1:~ }|
@@ -2785,7 +3269,7 @@ describe('builtin popupmenu', function()
{1:~ }{n: baz }{1: }|
:let g:menustr = 'bar' |
]])
- meths.input_mouse('left', 'press', '', 0, 4, 22)
+ feed('<LeftMouse><22,4>')
screen:expect([[
^popup menu test |
{1:~ }|
@@ -2795,7 +3279,7 @@ describe('builtin popupmenu', function()
:let g:menustr = 'baz' |
]])
eq('baz', meths.get_var('menustr'))
- meths.input_mouse('right', 'press', '', 0, 0, 4)
+ feed('<RightMouse><4,0>')
screen:expect([[
^popup menu test |
{1:~ }{n: foo }{1: }|
@@ -2804,7 +3288,7 @@ describe('builtin popupmenu', function()
{1:~ }|
:let g:menustr = 'baz' |
]])
- meths.input_mouse('right', 'drag', '', 0, 3, 6)
+ feed('<RightDrag><6,3>')
screen:expect([[
^popup menu test |
{1:~ }{n: foo }{1: }|
@@ -2813,7 +3297,7 @@ describe('builtin popupmenu', function()
{1:~ }|
:let g:menustr = 'baz' |
]])
- meths.input_mouse('right', 'release', '', 0, 1, 6)
+ feed('<RightRelease><6,1>')
screen:expect([[
^popup menu test |
{1:~ }|
@@ -2823,6 +3307,188 @@ describe('builtin popupmenu', function()
:let g:menustr = 'foo' |
]])
eq('foo', meths.get_var('menustr'))
+ eq(false, screen.options.mousemoveevent)
+ feed('<RightMouse><4,0>')
+ screen:expect([[
+ ^popup menu test |
+ {1:~ }{n: foo }{1: }|
+ {1:~ }{n: bar }{1: }|
+ {1:~ }{n: baz }{1: }|
+ {1:~ }|
+ :let g:menustr = 'foo' |
+ ]])
+ eq(true, screen.options.mousemoveevent)
+ feed('<MouseMove><6,3>')
+ screen:expect([[
+ ^popup menu test |
+ {1:~ }{n: foo }{1: }|
+ {1:~ }{n: bar }{1: }|
+ {1:~ }{s: baz }{1: }|
+ {1:~ }|
+ :let g:menustr = 'foo' |
+ ]])
+ eq(true, screen.options.mousemoveevent)
+ feed('<LeftMouse><6,2>')
+ screen:expect([[
+ ^popup menu test |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :let g:menustr = 'bar' |
+ ]])
+ eq(false, screen.options.mousemoveevent)
+ eq('bar', meths.get_var('menustr'))
+ end)
+
+ -- oldtest: Test_popup_command_dump()
+ it(':popup command', function()
+ exec([[
+ func ChangeMenu()
+ aunmenu PopUp.&Paste
+ nnoremenu 1.40 PopUp.&Paste :echomsg "pasted"<CR>
+ echomsg 'changed'
+ return "\<Ignore>"
+ endfunc
+
+ let lines =<< trim END
+ one two three four five
+ and one two Xthree four five
+ one more two three four five
+ END
+ call setline(1, lines)
+
+ aunmenu *
+ source $VIMRUNTIME/menu.vim
+ ]])
+ feed('/X<CR>:popup PopUp<CR>')
+ screen:expect([[
+ one two three four five |
+ and one two {7:^X}three four five |
+ one more tw{n: Undo } |
+ {1:~ }{n: }{1: }|
+ {1:~ }{n: Paste }{1: }|
+ {1:~ }{n: }{1: }|
+ {1:~ }{n: Select Word }{1: }|
+ {1:~ }{n: Select Sentence }{1: }|
+ {1:~ }{n: Select Paragraph }{1: }|
+ {1:~ }{n: Select Line }{1: }|
+ {1:~ }{n: Select Block }{1: }|
+ {1:~ }{n: Select All }{1: }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :popup PopUp |
+ ]])
+
+ -- go to the Paste entry in the menu
+ feed('jj')
+ screen:expect([[
+ one two three four five |
+ and one two {7:^X}three four five |
+ one more tw{n: Undo } |
+ {1:~ }{n: }{1: }|
+ {1:~ }{s: Paste }{1: }|
+ {1:~ }{n: }{1: }|
+ {1:~ }{n: Select Word }{1: }|
+ {1:~ }{n: Select Sentence }{1: }|
+ {1:~ }{n: Select Paragraph }{1: }|
+ {1:~ }{n: Select Line }{1: }|
+ {1:~ }{n: Select Block }{1: }|
+ {1:~ }{n: Select All }{1: }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :popup PopUp |
+ ]])
+
+ -- Select a word
+ feed('j')
+ screen:expect([[
+ one two three four five |
+ and one two {7:^X}three four five |
+ one more tw{n: Undo } |
+ {1:~ }{n: }{1: }|
+ {1:~ }{n: Paste }{1: }|
+ {1:~ }{n: }{1: }|
+ {1:~ }{s: Select Word }{1: }|
+ {1:~ }{n: Select Sentence }{1: }|
+ {1:~ }{n: Select Paragraph }{1: }|
+ {1:~ }{n: Select Line }{1: }|
+ {1:~ }{n: Select Block }{1: }|
+ {1:~ }{n: Select All }{1: }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :popup PopUp |
+ ]])
+
+ feed('<Esc>')
+
+ -- Set an <expr> mapping to change a menu entry while it's displayed.
+ -- The text should not change but the command does.
+ -- Also verify that "changed" shows up, which means the mapping triggered.
+ command('nnoremap <expr> <F2> ChangeMenu()')
+ feed('/X<CR>:popup PopUp<CR><F2>')
+ screen:expect([[
+ one two three four five |
+ and one two {7:^X}three four five |
+ one more tw{n: Undo } |
+ {1:~ }{n: }{1: }|
+ {1:~ }{n: Paste }{1: }|
+ {1:~ }{n: }{1: }|
+ {1:~ }{n: Select Word }{1: }|
+ {1:~ }{n: Select Sentence }{1: }|
+ {1:~ }{n: Select Paragraph }{1: }|
+ {1:~ }{n: Select Line }{1: }|
+ {1:~ }{n: Select Block }{1: }|
+ {1:~ }{n: Select All }{1: }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ changed |
+ ]])
+
+ -- Select the Paste entry, executes the changed menu item.
+ feed('jj<CR>')
+ screen:expect([[
+ one two three four five |
+ and one two {7:^X}three four five |
+ one more two three four five |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ pasted |
+ ]])
end)
end)
@@ -3031,5 +3697,72 @@ describe('builtin popupmenu with ui/ext_multigrid', function()
:let g:menustr = 'foo' |
]]})
eq('foo', meths.get_var('menustr'))
+ eq(false, screen.options.mousemoveevent)
+ 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 = 'foo' |
+ ## grid 4
+ {n: foo }|
+ {n: bar }|
+ {n: baz }|
+ ]], float_pos={[4] = {{id = -1}, 'NW', 2, 1, 3, false, 100}}})
+ eq(true, screen.options.mousemoveevent)
+ meths.input_mouse('move', '', '', 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 = 'foo' |
+ ## grid 4
+ {n: foo }|
+ {n: bar }|
+ {s: baz }|
+ ]], float_pos={[4] = {{id = -1}, 'NW', 2, 1, 3, false, 100}}})
+ eq(true, screen.options.mousemoveevent)
+ meths.input_mouse('left', 'press', '', 2, 2, 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 = 'bar' |
+ ]]})
+ eq(false, screen.options.mousemoveevent)
+ eq('bar', meths.get_var('menustr'))
end)
end)
diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua
index 6ee9e7b393..3b9cce0e6f 100644
--- a/test/functional/ui/screen.lua
+++ b/test/functional/ui/screen.lua
@@ -101,13 +101,10 @@ end
local default_screen_timeout = default_timeout_factor * 3500
-do
- local spawn, nvim_prog = helpers.spawn, helpers.nvim_prog
- local session = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '-N', '--embed'})
+function Screen._init_colors(session)
local status, rv = session:request('nvim_get_color_map')
if not status then
- print('failed to get color map')
- os.exit(1)
+ error('failed to get color map')
end
local colors = rv
local colornames = {}
@@ -116,12 +113,15 @@ do
-- this is just a helper to get any canonical name of a color
colornames[rgb] = name
end
- session:close()
Screen.colors = colors
Screen.colornames = colornames
end
function Screen.new(width, height)
+ if not Screen.colors then
+ Screen._init_colors(get_session())
+ end
+
if not width then
width = 53
end
@@ -519,7 +519,7 @@ function Screen:_wait(check, flags)
end
assert(timeout >= minimal_timeout)
- local did_miminal_timeout = false
+ local did_minimal_timeout = false
local function notification_cb(method, args)
assert(method == 'redraw', string.format(
@@ -536,7 +536,7 @@ function Screen:_wait(check, flags)
if not err then
success_seen = true
- if did_miminal_timeout then
+ if did_minimal_timeout then
self._session:stop()
end
elseif success_seen and #args > 0 then
@@ -558,7 +558,7 @@ function Screen:_wait(check, flags)
end
if not success_seen and not eof then
- did_miminal_timeout = true
+ did_minimal_timeout = true
eof = run_session(self._session, flags.request_cb, notification_cb, nil, timeout-minimal_timeout)
end
@@ -769,6 +769,7 @@ end
function Screen:_handle_grid_cursor_goto(grid, row, col)
self._cursor.grid = grid
+ assert(row >= 0 and col >= 0)
self._cursor.row = row + 1
self._cursor.col = col + 1
end
@@ -1549,7 +1550,8 @@ function Screen:_get_attr_id(attr_state, attrs, hl_id)
attr_state.modified = true
return id
end
- return "UNEXPECTED "..self:_pprint_attrs(self._attr_table[hl_id][1])
+ local kind = self._options.rgb and 1 or 2
+ return "UNEXPECTED "..self:_pprint_attrs(self._attr_table[hl_id][kind])
else
if self:_equal_attrs(attrs, {}) then
-- ignore this attrs
diff --git a/test/functional/ui/screen_basic_spec.lua b/test/functional/ui/screen_basic_spec.lua
index 6c872e52d3..3bd2289a73 100644
--- a/test/functional/ui/screen_basic_spec.lua
+++ b/test/functional/ui/screen_basic_spec.lua
@@ -5,8 +5,8 @@ local feed, command = helpers.feed, helpers.command
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
+local is_os = helpers.is_os
describe('screen', function()
local screen
@@ -128,18 +128,18 @@ 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 = (is_os('win') and 'myfile (C:\\mydir) - NVIM' or 'myfile (/mydir) - NVIM')
command('set title')
- command(iswin() and 'file C:\\mydir\\myfile' or 'file /mydir/myfile')
+ command(is_os('win') 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 file1 = is_os('win') and 'C:\\mydir\\myfile1' or '/mydir/myfile1'
+ local file2 = is_os('win') and 'C:\\mydir\\myfile2' or '/mydir/myfile2'
+ local expected = (is_os('win') and 'myfile1 (C:\\mydir) - NVIM' or 'myfile1 (/mydir) - NVIM')
local buf2
before_each(function()
@@ -682,30 +682,7 @@ local function screen_tests(linegrid)
]])
end)
- it('execute command with multi-line output without msgsep', function()
- command("set display-=msgsep")
- feed(':ls<cr>')
- screen:expect([[
- {0:~ }|
- {0:~ }|
- {0:~ }|
- {0:~ }|
- {0:~ }|
- {0:~ }|
- {0:~ }|
- {0:~ }|
- {0:~ }|
- {0:~ }|
- {0:~ }|
- :ls |
- 1 %a "[No Name]" line 1 |
- {7:Press ENTER or type command to continue}^ |
- ]])
- feed('<cr>') -- skip the "Press ENTER..." state or tests will hang
- end)
-
- it('execute command with multi-line output and with msgsep', function()
- command("set display+=msgsep")
+ it('execute command with multi-line output', function()
feed(':ls<cr>')
screen:expect([[
|
@@ -917,6 +894,31 @@ local function screen_tests(linegrid)
:ls^ |
]])
end)
+
+ it('VimResized autocommand does not cause invalid UI events #20692 #20759', function()
+ feed('<Esc>')
+ command([[autocmd VimResized * redrawtabline]])
+ command([[autocmd VimResized * lua vim.api.nvim_echo({ { 'Hello' } }, false, {})]])
+ command([[autocmd VimResized * let g:echospace = v:echospace]])
+ meths.set_option('showtabline', 2)
+ screen:expect([[
+ {2: + [No Name] }{3: }|
+ resiz^e |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ screen:try_resize(30, 6)
+ screen:expect([[
+ {2: + [No Name] }{3: }|
+ resiz^e |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ eq(29, meths.get_var('echospace'))
+ end)
end)
describe('press enter', function()
@@ -1051,38 +1053,35 @@ describe('Screen default colors', function()
end)
end)
-
-describe('screen with msgsep deactivated on startup', function()
- local screen
-
- before_each(function()
- clear('--cmd', 'set display-=msgsep')
- screen = Screen.new()
- screen:attach()
- screen:set_default_attr_ids {
- [0] = {bold=true, foreground=255};
- [7] = {bold = true, foreground = Screen.colors.SeaGreen};
- }
- end)
-
- it('execute command with multi-line output', function()
- feed ':ls<cr>'
- screen:expect([[
- {0:~ }|
- {0:~ }|
- {0:~ }|
- {0:~ }|
- {0:~ }|
- {0:~ }|
- {0:~ }|
- {0:~ }|
- {0:~ }|
- {0:~ }|
- {0:~ }|
- :ls |
- 1 %a "[No Name]" line 1 |
- {7:Press ENTER or type command to continue}^ |
- ]])
- feed '<cr>' -- skip the "Press ENTER..." state or tests will hang
- end)
+it('CTRL-F or CTRL-B scrolls a page after UI attach/resize #20605', function()
+ clear()
+ local screen = Screen.new(100, 100)
+ screen:attach()
+ eq(100, meths.get_option('lines'))
+ eq(99, meths.get_option('window'))
+ eq(99, meths.win_get_height(0))
+ feed('1000o<Esc>')
+ eq(903, funcs.line('w0'))
+ feed('<C-B>')
+ eq(806, funcs.line('w0'))
+ feed('<C-B>')
+ eq(709, funcs.line('w0'))
+ feed('<C-F>')
+ eq(806, funcs.line('w0'))
+ feed('<C-F>')
+ eq(903, funcs.line('w0'))
+ feed('G')
+ screen:try_resize(50, 50)
+ eq(50, meths.get_option('lines'))
+ eq(49, meths.get_option('window'))
+ eq(49, meths.win_get_height(0))
+ eq(953, funcs.line('w0'))
+ feed('<C-B>')
+ eq(906, funcs.line('w0'))
+ feed('<C-B>')
+ eq(859, funcs.line('w0'))
+ feed('<C-F>')
+ eq(906, funcs.line('w0'))
+ feed('<C-F>')
+ eq(953, funcs.line('w0'))
end)
diff --git a/test/functional/ui/searchhl_spec.lua b/test/functional/ui/searchhl_spec.lua
index c5c88323a2..18bbb56a61 100644
--- a/test/functional/ui/searchhl_spec.lua
+++ b/test/functional/ui/searchhl_spec.lua
@@ -221,10 +221,10 @@ describe('search highlighting', function()
feed('gg/foo\\nbar<CR>')
screen:expect([[
one |
- {2:^foo} |
+ {2:^foo } |
{2:bar} |
baz |
- {1:foo} |
+ {1:foo } |
{1:bar} |
/foo\nbar |
]])
@@ -232,20 +232,20 @@ describe('search highlighting', function()
feed('gg/efg\\nhij<CR>')
screen:expect([[
--- |
- abcd{2:^efg} |
+ abcd{2:^efg } |
{2:hij}kl |
--- |
- abcd{1:efg} |
+ abcd{1:efg } |
{1:hij}kl |
/efg\nhij |
]])
feed('n')
screen:expect([[
--- |
- abcd{1:efg} |
+ abcd{1:efg } |
{1:hij}kl |
--- |
- abcd{2:^efg} |
+ abcd{2:^efg } |
{2:hij}kl |
/efg\nhij |
]])
@@ -548,9 +548,9 @@ describe('search highlighting', function()
feed('/line\\na<cr>')
screen:expect([[
|
- a repeated {2:^line} |
- {2:a} repeated {2:line} |
- {2:a} repeated {2:line} |
+ a repeated {2:^line } |
+ {2:a} repeated {2:line } |
+ {2:a} repeated {2:line } |
{2:a} repeated line |
{1:~ }|
{4:search hit BOTTOM, continuing at TOP} |
@@ -560,9 +560,9 @@ describe('search highlighting', function()
feed('4Grb')
screen:expect([[
|
- a repeated {2:line} |
+ a repeated {2:line } |
{2:a} repeated line |
- ^b repeated {2:line} |
+ ^b repeated {2:line } |
{2:a} repeated line |
{1:~ }|
{4:search hit BOTTOM, continuing at TOP} |
diff --git a/test/functional/ui/sign_spec.lua b/test/functional/ui/sign_spec.lua
index dbc92ca222..7dcd4cff25 100644
--- a/test/functional/ui/sign_spec.lua
+++ b/test/functional/ui/sign_spec.lua
@@ -2,6 +2,7 @@ local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local clear, feed, command = helpers.clear, helpers.feed, helpers.command
local source = helpers.source
+local meths = helpers.meths
describe('Signs', function()
local screen
@@ -155,9 +156,12 @@ describe('Signs', function()
{0:~ }|
|
]])
+ -- Check that 'statuscolumn' correctly applies numhl
+ command('set statuscolumn=%s%=%l\\ ')
+ screen:expect_unchanged()
end)
- it('higlights the cursorline sign with culhl', function()
+ it('highlights the cursorline sign with culhl', function()
feed('ia<cr>b<cr>c<esc>')
command('sign define piet text=>> texthl=Search culhl=ErrorMsg')
command('sign place 1 line=1 name=piet buffer=1')
@@ -232,11 +236,13 @@ describe('Signs', function()
|
]])
command('set cursorlineopt=number')
+ command('hi! link SignColumn IncSearch')
+ feed('Go<esc>2G')
screen:expect([[
{1:>>}a |
{8:>>}^b |
{1:>>}c |
- {0:~ }|
+ {5: } |
{0:~ }|
{0:~ }|
{0:~ }|
@@ -248,6 +254,9 @@ describe('Signs', function()
{0:~ }|
|
]])
+ -- Check that 'statuscolumn' cursorline/signcolumn highlights are the same (#21726)
+ command('set statuscolumn=%s')
+ screen:expect_unchanged()
end)
it('multiple signs #9295', function()
@@ -592,4 +601,88 @@ describe('Signs', function()
]])
end)
end)
+
+ it('signcolumn width is updated when removing all signs after deleting lines', function()
+ meths.buf_set_lines(0, 0, 1, true, {'a', 'b', 'c', 'd', 'e'})
+ command('sign define piet text=>>')
+ command('sign place 10001 line=1 name=piet')
+ command('sign place 10002 line=5 name=piet')
+ command('2delete')
+ command('sign unplace 10001')
+ screen:expect([[
+ {2: }a |
+ {2: }^c |
+ {2: }d |
+ >>e |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ command('sign unplace 10002')
+ screen:expect([[
+ a |
+ ^c |
+ d |
+ e |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end)
+
+ it('signcolumn width is updated when removing all signs after inserting lines', function()
+ meths.buf_set_lines(0, 0, 1, true, {'a', 'b', 'c', 'd', 'e'})
+ command('sign define piet text=>>')
+ command('sign place 10001 line=1 name=piet')
+ command('sign place 10002 line=5 name=piet')
+ command('copy .')
+ command('sign unplace 10001')
+ screen:expect([[
+ {2: }a |
+ {2: }^a |
+ {2: }b |
+ {2: }c |
+ {2: }d |
+ >>e |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ command('sign unplace 10002')
+ screen:expect([[
+ a |
+ ^a |
+ b |
+ c |
+ d |
+ e |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end)
end)
diff --git a/test/functional/ui/spell_spec.lua b/test/functional/ui/spell_spec.lua
index de77100cc0..361f83d1ce 100644
--- a/test/functional/ui/spell_spec.lua
+++ b/test/functional/ui/spell_spec.lua
@@ -5,8 +5,8 @@ local Screen = require('test.functional.ui.screen')
local clear = helpers.clear
local feed = helpers.feed
local insert = helpers.insert
-local uname = helpers.uname
local command = helpers.command
+local is_os = helpers.is_os
describe("'spell'", function()
local screen
@@ -19,11 +19,15 @@ describe("'spell'", function()
[0] = {bold=true, foreground=Screen.colors.Blue},
[1] = {special = Screen.colors.Red, undercurl = true},
[2] = {special = Screen.colors.Blue1, undercurl = true},
+ [3] = {foreground = tonumber('0x6a0dad')},
+ [4] = {foreground = Screen.colors.Magenta},
+ [5] = {bold = true, foreground = Screen.colors.SeaGreen},
+ [6] = {foreground = Screen.colors.Red},
})
end)
it('joins long lines #7937', function()
- if uname() == 'openbsd' then pending('FIXME #12104', function() end) return end
+ if is_os('openbsd') then pending('FIXME #12104', function() end) return end
command('set spell')
insert([[
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
@@ -47,6 +51,7 @@ describe("'spell'", function()
end)
+ -- oldtest: Test_spell_screendump()
it('has correct highlight at start of line', function()
insert([[
"This is some text without any spell errors. Everything",
@@ -66,6 +71,96 @@ describe("'spell'", function()
"with missing caps here.", |
^ |
|
- ]])
+ ]])
+ end)
+
+ it('"noplainbuffer" and syntax #20385', function()
+ command('set filetype=c')
+ command('syntax on')
+ command('set spell')
+ insert([[
+ #include <stdbool.h>
+ bool func(void);]])
+ screen:expect([[
+ {3:#include }{4:<stdbool.h>} |
+ {5:bool} func({5:void})^; |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ feed('[s')
+ screen:expect([[
+ {3:#include }{4:<stdbool.h>} |
+ {5:bool} func({5:void})^; |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {6:search hit TOP, continuing at BOTTOM} |
+ ]])
+ -- "noplainbuffer" shouldn't change spellchecking behavior with syntax enabled
+ command('set spelloptions+=noplainbuffer')
+ screen:expect_unchanged()
+ feed(']s')
+ screen:expect([[
+ {3:#include }{4:<stdbool.h>} |
+ {5:bool} func({5:void})^; |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {6:search hit BOTTOM, continuing at TOP} |
+ ]])
+ -- no spellchecking with "noplainbuffer" and syntax disabled
+ command('syntax off')
+ screen:expect([[
+ #include <stdbool.h> |
+ bool func(void)^; |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {6:search hit BOTTOM, continuing at TOP} |
+ ]])
+ feed('[s')
+ screen:expect([[
+ #include <stdbool.h> |
+ bool func(void)^; |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {6:search hit TOP, continuing at BOTTOM} |
+ ]])
+ -- everything is spellchecked without "noplainbuffer" with syntax disabled
+ command('set spelloptions&')
+ screen:expect([[
+ #include <{1:stdbool}.h> |
+ {1:bool} {1:func}(void)^; |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {6:search hit TOP, continuing at BOTTOM} |
+ ]])
+ feed(']s')
+ screen:expect([[
+ #include <{1:^stdbool}.h> |
+ {1:bool} {1:func}(void); |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {6:search hit BOTTOM, continuing at TOP} |
+ ]])
end)
end)
diff --git a/test/functional/ui/statuscolumn_spec.lua b/test/functional/ui/statuscolumn_spec.lua
new file mode 100644
index 0000000000..ae3b95fb0f
--- /dev/null
+++ b/test/functional/ui/statuscolumn_spec.lua
@@ -0,0 +1,462 @@
+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 exec_lua = helpers.exec_lua
+local feed = helpers.feed
+local meths = helpers.meths
+local pcall_err = helpers.pcall_err
+
+describe('statuscolumn', function()
+ local screen
+ before_each(function()
+ clear('--cmd', 'set number nuw=1 | call setline(1, repeat(["aaaaa"], 16)) | norm GM')
+ screen = Screen.new()
+ screen:attach()
+ end)
+
+ it("fails with invalid 'statuscolumn'", function()
+ command([[set stc=%{v:relnum?v:relnum:(v:lnum==5?invalid:v:lnum)}\ ]])
+ screen:expect([[
+ 4 aaaaa |
+ 3 aaaaa |
+ 2 aaaaa |
+ 1 aaaaa |
+ 8 ^aaaaa |
+ 1 aaaaa |
+ 2 aaaaa |
+ 3 aaaaa |
+ 4 aaaaa |
+ 5 aaaaa |
+ 6 aaaaa |
+ 7 aaaaa |
+ 8 aaaaa |
+ |
+ ]])
+ command('norm 5G')
+ eq('Vim(redraw):E121: Undefined variable: invalid', pcall_err(command, 'redraw!'))
+ eq('', eval('&statuscolumn'))
+ end)
+
+ it("widens with irregular 'statuscolumn' width", function()
+ command([[set stc=%{v:relnum?v:relnum:(v:lnum==5?'bbbbb':v:lnum)}]])
+ command('norm 5G | redraw!')
+ screen:expect([[
+ 1 aaaaa |
+ bbbbba^eaaa |
+ 1 aaaaa |
+ 2 aaaaa |
+ 3 aaaaa |
+ 4 aaaaa |
+ 5 aaaaa |
+ 6 aaaaa |
+ 7 aaaaa |
+ 8 aaaaa |
+ 9 aaaaa |
+ 10 aaaaa |
+ 11 aaaaa |
+ |
+ ]])
+ end)
+
+ it("works with 'number' and 'relativenumber'", function()
+ command([[set stc=%{&nu?v:lnum:''}%=%{&rnu?'\ '.v:relnum:''}│]])
+ screen:expect([[
+ 4 │aaaaa |
+ 5 │aaaaa |
+ 6 │aaaaa |
+ 7 │aaaaa |
+ 8 │^aaaaa |
+ 9 │aaaaa |
+ 10│aaaaa |
+ 11│aaaaa |
+ 12│aaaaa |
+ 13│aaaaa |
+ 14│aaaaa |
+ 15│aaaaa |
+ 16│aaaaa |
+ |
+ ]])
+ command([[set stc=%l%=%{&rnu?'\ ':''}%r│]])
+ screen:expect_unchanged()
+ command([[set stc=%{&nu?v:lnum:''}%=%{&rnu?'\ '.v:relnum:''}│]])
+ command('set relativenumber')
+ screen:expect([[
+ 4 4│aaaaa |
+ 5 3│aaaaa |
+ 6 2│aaaaa |
+ 7 1│aaaaa |
+ 8 0│^aaaaa |
+ 9 1│aaaaa |
+ 10 2│aaaaa |
+ 11 3│aaaaa |
+ 12 4│aaaaa |
+ 13 5│aaaaa |
+ 14 6│aaaaa |
+ 15 7│aaaaa |
+ 16 8│aaaaa |
+ |
+ ]])
+ command([[set stc=%l%=%{&rnu?'\ ':''}%r│]])
+ screen:expect_unchanged()
+ command([[set stc=%{&nu?v:lnum:''}%=%{&rnu?'\ '.v:relnum:''}│]])
+ command('norm 12GH')
+ screen:expect([[
+ 4 0│^aaaaa |
+ 5 1│aaaaa |
+ 6 2│aaaaa |
+ 7 3│aaaaa |
+ 8 4│aaaaa |
+ 9 5│aaaaa |
+ 10 6│aaaaa |
+ 11 7│aaaaa |
+ 12 8│aaaaa |
+ 13 9│aaaaa |
+ 14 10│aaaaa |
+ 15 11│aaaaa |
+ 16 12│aaaaa |
+ |
+ ]])
+ command([[set stc=%l%=%{&rnu?'\ ':''}%r│]])
+ screen:expect_unchanged()
+ command([[set stc=%{&nu?v:lnum:''}%=%{&rnu?'\ '.v:relnum:''}│]])
+ end)
+
+ it("works with highlighted 'statuscolumn'", function()
+ command([[set stc=%#NonText#%{&nu?v:lnum:''}]] ..
+ [[%=%{&rnu&&(v:lnum%2)?'\ '.v:relnum:''}]] ..
+ [[%#LineNr#%{&rnu&&!(v:lnum%2)?'\ '.v:relnum:''}│]])
+ screen:set_default_attr_ids({
+ [0] = {bold = true, foreground = Screen.colors.Blue},
+ [1] = {foreground = Screen.colors.Brown},
+ })
+ screen:expect([[
+ {0:4 }{1:│}aaaaa |
+ {0:5 }{1:│}aaaaa |
+ {0:6 }{1:│}aaaaa |
+ {0:7 }{1:│}aaaaa |
+ {0:8 }{1:│}^aaaaa |
+ {0:9 }{1:│}aaaaa |
+ {0:10}{1:│}aaaaa |
+ {0:11}{1:│}aaaaa |
+ {0:12}{1:│}aaaaa |
+ {0:13}{1:│}aaaaa |
+ {0:14}{1:│}aaaaa |
+ {0:15}{1:│}aaaaa |
+ {0:16}{1:│}aaaaa |
+ |
+ ]])
+ command('set relativenumber')
+ screen:expect([[
+ {0:4 }{1: 4│}aaaaa |
+ {0:5 3}{1:│}aaaaa |
+ {0:6 }{1: 2│}aaaaa |
+ {0:7 1}{1:│}aaaaa |
+ {0:8 }{1: 0│}^aaaaa |
+ {0:9 1}{1:│}aaaaa |
+ {0:10}{1: 2│}aaaaa |
+ {0:11 3}{1:│}aaaaa |
+ {0:12}{1: 4│}aaaaa |
+ {0:13 5}{1:│}aaaaa |
+ {0:14}{1: 6│}aaaaa |
+ {0:15 7}{1:│}aaaaa |
+ {0:16}{1: 8│}aaaaa |
+ |
+ ]])
+ command('set nonumber')
+ screen:expect([[
+ {1:4│}aaaaa |
+ {0:3}{1:│}aaaaa |
+ {1:2│}aaaaa |
+ {0:1}{1:│}aaaaa |
+ {1:0│}^aaaaa |
+ {0:1}{1:│}aaaaa |
+ {1:2│}aaaaa |
+ {0:3}{1:│}aaaaa |
+ {1:4│}aaaaa |
+ {0:5}{1:│}aaaaa |
+ {1:6│}aaaaa |
+ {0:7}{1:│}aaaaa |
+ {1:8│}aaaaa |
+ |
+ ]])
+ end)
+
+ it('works with wrapped lines, signs and folds', function()
+ command([[set stc=%C%s%=%{v:virtnum?'':v:lnum}│\ ]])
+ command("call setline(1,repeat([repeat('aaaaa',10)],16))")
+ screen:set_default_attr_ids({
+ [0] = {bold = true, foreground = Screen.colors.Blue},
+ [1] = {foreground = Screen.colors.Brown},
+ [2] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.WebGrey},
+ [3] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGrey},
+ [4] = {bold = true, foreground = Screen.colors.Brown},
+ [5] = {background = Screen.colors.Grey90},
+ })
+ screen:expect([[
+ {1: 4│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {1: │ }a |
+ {1: 5│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {1: │ }a |
+ {1: 6│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {1: │ }a |
+ {1: 7│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {1: │ }a |
+ {1: 8│ }^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {1: │ }a |
+ {1: 9│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {1: │ }a |
+ {1:10│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa{0:@@@}|
+ |
+ ]])
+ command("set stc=%C%s%=%l│\\ ")
+ screen:expect_unchanged()
+ command('set signcolumn=auto:2 foldcolumn=auto')
+ command('sign define piet1 text=>> texthl=LineNr')
+ command('sign define piet2 text=>! texthl=NonText')
+ command('sign place 1 line=4 name=piet1 buffer=1')
+ command('sign place 2 line=5 name=piet2 buffer=1')
+ command('sign place 3 line=6 name=piet1 buffer=1')
+ command('sign place 4 line=6 name=piet2 buffer=1')
+ screen:expect([[
+ {1:>>}{2: }{1: 4│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │ }aaaaa |
+ {0:>!}{2: }{1: 5│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │ }aaaaa |
+ {1:>>}{0:>!}{1: 6│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │ }aaaaa |
+ {2: }{1: 7│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │ }aaaaa |
+ {2: }{1: 8│ }^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │ }aaaaa |
+ {2: }{1: 9│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │ }aaaaa |
+ {2: }{1:10│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa{0:@@@}|
+ |
+ ]])
+ command('norm zf$')
+ -- Check that alignment works properly with signs after %=
+ command([[set stc=%C%=%{v:virtnum?'':v:lnum}│%s\ ]])
+ screen:expect([[
+ {2: }{1: 4│>>}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │}{2: }{1: }aaaaaa |
+ {2: }{1: 5│}{0:>!}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │}{2: }{1: }aaaaaa |
+ {2: }{1: 6│>>}{0:>!}{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │}{2: }{1: }aaaaaa |
+ {2: }{1: 7│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │}{2: }{1: }aaaaaa |
+ {2:+}{1: 8│}{2: }{1: }{3:^+-- 1 line: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}|
+ {2: }{1: 9│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │}{2: }{1: }aaaaaa |
+ {2: }{1:10│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │}{2: }{1: }aaaaaa |
+ |
+ ]])
+ command('set cursorline')
+ screen:expect([[
+ {2: }{1: 4│>>}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │}{2: }{1: }aaaaaa |
+ {2: }{1: 5│}{0:>!}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │}{2: }{1: }aaaaaa |
+ {2: }{1: 6│>>}{0:>!}{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │}{2: }{1: }aaaaaa |
+ {2: }{1: 7│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │}{2: }{1: }aaaaaa |
+ {2:+}{4: 8│}{2: }{4: }{5:^+-- 1 line: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}|
+ {2: }{1: 9│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │}{2: }{1: }aaaaaa |
+ {2: }{1:10│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │}{2: }{1: }aaaaaa |
+ |
+ ]])
+ -- v:lnum is the same value on wrapped lines
+ command([[set stc=%C%=%{v:lnum}│%s\ ]])
+ screen:expect([[
+ {2: }{1: 4│>>}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: 4│}{2: }{1: }aaaaaa |
+ {2: }{1: 5│}{0:>!}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: 5│}{2: }{1: }aaaaaa |
+ {2: }{1: 6│>>}{0:>!}{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: 6│}{2: }{1: }aaaaaa |
+ {2: }{1: 7│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: 7│}{2: }{1: }aaaaaa |
+ {2:+}{4: 8│}{2: }{4: }{5:^+-- 1 line: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}|
+ {2: }{1: 9│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: 9│}{2: }{1: }aaaaaa |
+ {2: }{1:10│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1:10│}{2: }{1: }aaaaaa |
+ |
+ ]])
+ -- v:relnum is the same value on wrapped lines
+ command([[set stc=%C%=\ %{v:relnum}│%s\ ]])
+ screen:expect([[
+ {2: }{1: 4│>>}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: 4│}{2: }{1: }aaaaaa |
+ {2: }{1: 3│}{0:>!}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: 3│}{2: }{1: }aaaaaa |
+ {2: }{1: 2│>>}{0:>!}{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: 2│}{2: }{1: }aaaaaa |
+ {2: }{1: 1│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: 1│}{2: }{1: }aaaaaa |
+ {2:+}{4: 0│}{2: }{4: }{5:^+-- 1 line: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}|
+ {2: }{1: 1│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: 1│}{2: }{1: }aaaaaa |
+ {2: }{1: 2│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: 2│}{2: }{1: }aaaaaa |
+ |
+ ]])
+ command([[set stc=%C%=\ %{v:virtnum?'':v:relnum}│%s\ ]])
+ screen:expect([[
+ {2: }{1: 4│>>}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │}{2: }{1: }aaaaaa |
+ {2: }{1: 3│}{0:>!}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │}{2: }{1: }aaaaaa |
+ {2: }{1: 2│>>}{0:>!}{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │}{2: }{1: }aaaaaa |
+ {2: }{1: 1│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │}{2: }{1: }aaaaaa |
+ {2:+}{4: 0│}{2: }{4: }{5:^+-- 1 line: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}|
+ {2: }{1: 1│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │}{2: }{1: }aaaaaa |
+ {2: }{1: 2│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │}{2: }{1: }aaaaaa |
+ |
+ ]])
+ -- Up to 9 signs in a line
+ command('set signcolumn=auto:9 foldcolumn=auto')
+ command('sign place 5 line=6 name=piet1 buffer=1')
+ command('sign place 6 line=6 name=piet2 buffer=1')
+ command('sign place 7 line=6 name=piet1 buffer=1')
+ command('sign place 8 line=6 name=piet2 buffer=1')
+ command('sign place 9 line=6 name=piet1 buffer=1')
+ command('sign place 10 line=6 name=piet2 buffer=1')
+ command('sign place 11 line=6 name=piet1 buffer=1')
+ screen:expect([[
+ {2: }{1: 4│>>}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │}{2: }{1: }aaaaaaaaaaaaaaaaaaaa |
+ {2: }{1: 3│}{0:>!}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │}{2: }{1: }aaaaaaaaaaaaaaaaaaaa |
+ {2: }{1: 2│>>}{0:>!}{1:>>}{0:>!}{1:>>}{0:>!}{1:>>}{0:>!}{1:>> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │}{2: }{1: }aaaaaaaaaaaaaaaaaaaa |
+ {2: }{1: 1│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │}{2: }{1: }aaaaaaaaaaaaaaaaaaaa |
+ {2:+}{4: 0│}{2: }{4: }{5:^+-- 1 line: aaaaaaaaaaaaaaaaa}|
+ {2: }{1: 1│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │}{2: }{1: }aaaaaaaaaaaaaaaaaaaa |
+ {2: }{1: 2│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {2: }{1: │}{2: }{1: }aaaaaaaaaaaaaaaaaaaa |
+ |
+ ]])
+ -- Status column is re-evaluated for virt_lines, buffer line, and wrapped line
+ exec_lua([[
+ local ns = vim.api.nvim_create_namespace("ns")
+ vim.api.nvim_buf_set_extmark(0, ns, 5, 0, {
+ virt_lines_above = true, virt_lines = {{{"virt_line above", ""}}} })
+ vim.api.nvim_buf_set_extmark(0, ns, 4, 0, { virt_lines = {{{"virt_line", ""}}} })
+ ]])
+ command('set foldcolumn=0 signcolumn=no')
+ command([[set stc=%{v:virtnum<0?'virtual':(!v:virtnum?'buffer':'wrapped')}%=%{'\ '.v:virtnum.'\ '.v:lnum}]])
+ screen:expect([[
+ {1:buffer 0 4}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {1:wrapped 1 4}aaaaaaaa |
+ {1:buffer 0 5}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {1:wrapped 1 5}aaaaaaaa |
+ {1:virtual-2 5}virt_line |
+ {1:virtual-2 5}virt_line above |
+ {1:buffer 0 6}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {1:wrapped 1 6}aaaaaaaa |
+ {1:buffer 0 7}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {1:wrapped 1 7}aaaaaaaa |
+ {4:buffer 0 8}{5:^+-- 1 line: aaaaaaaaaaaaaaaaaaaaaaaaaaaaa}|
+ {1:buffer 0 9}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {1:wrapped 1 9}aaaaaaaa |
+ |
+ ]])
+ end)
+
+ it("works with 'statuscolumn' clicks", function()
+ command('set mousemodel=extend')
+ command([[
+ function! MyClickFunc(minwid, clicks, button, mods)
+ let g:testvar = printf("%d %d %s %d", a:minwid, a:clicks, a:button, getmousepos().line)
+ if a:mods !=# ' '
+ let g:testvar ..= '(' .. a:mods .. ')'
+ endif
+ endfunction
+ set stc=%0@MyClickFunc@%=%l%T
+ ]])
+ meths.input_mouse('left', 'press', '', 0, 0, 0)
+ eq('0 1 l 4', eval("g:testvar"))
+ meths.input_mouse('left', 'press', '', 0, 0, 0)
+ eq('0 2 l 4', eval("g:testvar"))
+ meths.input_mouse('left', 'press', '', 0, 0, 0)
+ eq('0 3 l 4', eval("g:testvar"))
+ meths.input_mouse('left', 'press', '', 0, 0, 0)
+ eq('0 4 l 4', eval("g:testvar"))
+ meths.input_mouse('right', 'press', '', 0, 3, 0)
+ eq('0 1 r 7', eval("g:testvar"))
+ meths.input_mouse('right', 'press', '', 0, 3, 0)
+ eq('0 2 r 7', eval("g:testvar"))
+ meths.input_mouse('right', 'press', '', 0, 3, 0)
+ eq('0 3 r 7', eval("g:testvar"))
+ meths.input_mouse('right', 'press', '', 0, 3, 0)
+ eq('0 4 r 7', eval("g:testvar"))
+ command('set laststatus=2 winbar=%f')
+ command('let g:testvar=""')
+ -- Check that winbar click doesn't register as statuscolumn click
+ meths.input_mouse('right', 'press', '', 0, 0, 0)
+ eq('', eval("g:testvar"))
+ -- Check that statusline click doesn't register as statuscolumn click
+ meths.input_mouse('right', 'press', '', 0, 12, 0)
+ eq('', eval("g:testvar"))
+ end)
+
+ it('click labels do not leak memory', function()
+ command([[
+ set laststatus=2
+ setlocal statuscolumn=%0@MyClickFunc@abcd%T
+ 4vsplit
+ setlocal statusline=abcd
+ redrawstatus
+ setlocal statusline=
+ only
+ redraw
+ ]])
+ end)
+
+ it('works with foldcolumn', function()
+ -- Fits maximum multibyte foldcolumn #21759
+ command([[set stc=%C%=%l\ fdc=9 fillchars=foldsep:𒀀]])
+ for _ = 0,8 do command('norm zfjzo') end
+ -- 'statuscolumn' is not drawn for `virt_lines_leftcol` lines
+ exec_lua([[
+ local ns = vim.api.nvim_create_namespace("ns")
+ vim.api.nvim_buf_set_extmark(0, ns, 6, 0, {
+ virt_lines_leftcol = true, virt_lines = {{{"virt", ""}}} })
+ vim.api.nvim_buf_set_extmark(0, ns, 7, 0, {
+ virt_lines_leftcol = true, virt_lines = {{{"virt", ""}}} })
+ ]])
+ feed('lh') -- force update wcol/row
+ screen:expect([[
+ 4 aaaaa |
+ 5 aaaaa |
+ 6 aaaaa |
+ 7 aaaaa |
+ virt |
+ --------- 8 ^aaaaa |
+ virt |
+ 𒀀𒀀𒀀𒀀𒀀𒀀𒀀𒀀𒀀 9 aaaaa |
+ 10 aaaaa |
+ 11 aaaaa |
+ 12 aaaaa |
+ 13 aaaaa |
+ 14 aaaaa |
+ |
+ ]])
+ command('set stc=') -- also for the default sign column
+ screen:expect_unchanged()
+ end)
+end)
diff --git a/test/functional/ui/statusline_spec.lua b/test/functional/ui/statusline_spec.lua
index add5144e1b..1c184ff27d 100644
--- a/test/functional/ui/statusline_spec.lua
+++ b/test/functional/ui/statusline_spec.lua
@@ -164,6 +164,24 @@ describe('statusline clicks', function()
meths.input_mouse('right', 'press', '', 0, 6, 5)
eq('0 1 r', eval("g:testvar"))
end)
+
+ it('no memory leak with zero-width click labels', function()
+ command([[
+ let &stl = '%@Test@%T%@MyClickFunc@%=%T%@Test@'
+ ]])
+ meths.input_mouse('left', 'press', '', 0, 6, 0)
+ eq('0 1 l', eval("g:testvar"))
+ meths.input_mouse('right', 'press', '', 0, 6, 39)
+ eq('0 1 r', eval("g:testvar"))
+ end)
+
+ it('no memory leak with truncated click labels', function()
+ command([[
+ let &stl = '%@MyClickFunc@foo%X' .. repeat('a', 40) .. '%<t%@Test@bar%X%@Test@baz'
+ ]])
+ meths.input_mouse('left', 'press', '', 0, 6, 2)
+ eq('0 1 l', eval("g:testvar"))
+ end)
end)
describe('global statusline', function()
@@ -178,6 +196,7 @@ describe('global statusline', function()
[2] = {bold = true, reverse = true};
[3] = {bold = true};
[4] = {reverse = true};
+ [5] = {bold = true, foreground = Screen.colors.Fuchsia};
})
command('set laststatus=3')
command('set ruler')
@@ -398,6 +417,106 @@ describe('global statusline', function()
meths.input_mouse('left', 'drag', '', 0, 14, 10)
eq(1, meths.get_option('cmdheight'))
end)
+
+ it('cmdline row is correct after setting cmdheight #20514', function()
+ command('botright split test/functional/fixtures/bigfile.txt')
+ meths.set_option('cmdheight', 1)
+ feed('L')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ────────────────────────────────────────────────────────────|
+ 0000;<control>;Cc;0;BN;;;;;N;NULL;;;; |
+ 0001;<control>;Cc;0;BN;;;;;N;START OF HEADING;;;; |
+ 0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; |
+ 0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; |
+ 0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;; |
+ 0005;<control>;Cc;0;BN;;;;;N;ENQUIRY;;;; |
+ ^0006;<control>;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; |
+ {2:test/functional/fixtures/bigfile.txt 7,1 Top}|
+ |
+ ]])
+ feed('j')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ────────────────────────────────────────────────────────────|
+ 0001;<control>;Cc;0;BN;;;;;N;START OF HEADING;;;; |
+ 0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; |
+ 0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; |
+ 0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;; |
+ 0005;<control>;Cc;0;BN;;;;;N;ENQUIRY;;;; |
+ 0006;<control>;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; |
+ ^0007;<control>;Cc;0;BN;;;;;N;BELL;;;; |
+ {2:test/functional/fixtures/bigfile.txt 8,1 0%}|
+ |
+ ]])
+ meths.set_option('showtabline', 2)
+ screen:expect([[
+ {3: }{5:2}{3: t/f/f/bigfile.txt }{4: }|
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ────────────────────────────────────────────────────────────|
+ 0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; |
+ 0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; |
+ 0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;; |
+ 0005;<control>;Cc;0;BN;;;;;N;ENQUIRY;;;; |
+ 0006;<control>;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; |
+ ^0007;<control>;Cc;0;BN;;;;;N;BELL;;;; |
+ {2:test/functional/fixtures/bigfile.txt 8,1 0%}|
+ |
+ ]])
+ meths.set_option('cmdheight', 0)
+ screen:expect([[
+ {3: }{5:2}{3: t/f/f/bigfile.txt }{4: }|
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ────────────────────────────────────────────────────────────|
+ 0001;<control>;Cc;0;BN;;;;;N;START OF HEADING;;;; |
+ 0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; |
+ 0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; |
+ 0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;; |
+ 0005;<control>;Cc;0;BN;;;;;N;ENQUIRY;;;; |
+ 0006;<control>;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; |
+ ^0007;<control>;Cc;0;BN;;;;;N;BELL;;;; |
+ {2:test/functional/fixtures/bigfile.txt 8,1 0%}|
+ ]])
+ meths.set_option('cmdheight', 1)
+ screen:expect([[
+ {3: }{5:2}{3: t/f/f/bigfile.txt }{4: }|
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ────────────────────────────────────────────────────────────|
+ 0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; |
+ 0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; |
+ 0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;; |
+ 0005;<control>;Cc;0;BN;;;;;N;ENQUIRY;;;; |
+ 0006;<control>;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; |
+ ^0007;<control>;Cc;0;BN;;;;;N;BELL;;;; |
+ {2:test/functional/fixtures/bigfile.txt 8,1 0%}|
+ |
+ ]])
+ end)
end)
it('statusline does not crash if it has Arabic characters #19447', function()
@@ -444,3 +563,29 @@ it('statusline is redrawn with :resize from <Cmd> mapping #19629', function()
|
]])
end)
+
+it('showcmdloc=statusline does not show if statusline is too narrow', function()
+ clear()
+ local screen = Screen.new(40, 8)
+ screen:set_default_attr_ids({
+ [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [1] = {bold = true, reverse = true}, -- StatusLine
+ [2] = {reverse = true}, -- StatusLineNC
+ })
+ screen:attach()
+ command('set showcmd')
+ command('set showcmdloc=statusline')
+ command('1vsplit')
+ screen:expect([[
+ ^ │ |
+ {0:~}│{0:~ }|
+ {0:~}│{0:~ }|
+ {0:~}│{0:~ }|
+ {0:~}│{0:~ }|
+ {0:~}│{0:~ }|
+ {1:< }{2:[No Name] }|
+ |
+ ]])
+ feed('1234')
+ screen:expect_unchanged()
+end)
diff --git a/test/functional/ui/syntax_conceal_spec.lua b/test/functional/ui/syntax_conceal_spec.lua
index f790597140..1391985823 100644
--- a/test/functional/ui/syntax_conceal_spec.lua
+++ b/test/functional/ui/syntax_conceal_spec.lua
@@ -4,7 +4,7 @@ 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
+local exec = helpers.exec
describe('Screen', function()
local screen
@@ -947,7 +947,7 @@ describe('Screen', function()
{0:~ }|
|
]]}
- eq({{2, 0, {{'c', 0, 3}}}}, grid_lines)
+ eq({{2, 0, {{'c', 0, 3}, {' ', 0, 50}}}, {3, 0, {{' ', 0, 53}}}}, grid_lines)
end)
it('K_EVENT should not cause extra redraws with concealcursor #13196', function()
@@ -994,31 +994,39 @@ describe('Screen', function()
{0:~ }|
|
]]}
- eq({{2, 0, {{'c', 0, 3}}}}, grid_lines)
+ eq({{2, 0, {{'c', 0, 3}, {' ', 0, 50}}}}, grid_lines)
+ grid_lines = {}
poke_eventloop() -- causes K_EVENT key
screen:expect_unchanged()
- eq({{2, 0, {{'c', 0, 3}}}}, grid_lines)
+ eq({}, grid_lines) -- no redraw was done
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()
- expect_exit(command, ':qall!')
- os.remove('Xcolesearch')
- end)
-
- it('has the correct cursor column', function()
+ describe('concealed line has the correct cursor column', function()
+ -- oldtest: Test_cursor_column_in_concealed_line_after_window_scroll()
+ it('after window scroll', function()
insert([[
- 3split
- let m = matchadd('Conceal', '=')
- setl conceallevel=2 concealcursor=nc
- normal gg
- "==expr==
- ]])
+ 3split
+ let m = matchadd('Conceal', '=')
+ setl conceallevel=2 concealcursor=nc
+ normal gg
+ "==expr==]])
+ feed('gg')
+ command('file Xcolesearch')
+ command('set nomodified')
- command('write Xcolesearch')
- feed(":so %<CR>")
+ command('so')
+ screen:expect{grid=[[
+ ^3split |
+ let m matchadd('Conceal', '') |
+ setl conceallevel2 concealcursornc |
+ {2:Xcolesearch }|
+ 3split |
+ let m = matchadd('Conceal', '=') |
+ setl conceallevel=2 concealcursor=nc |
+ normal gg |
+ {3:Xcolesearch }|
+ |
+ ]]}
-- Jump to something that is beyond the bottom of the window,
-- so there's a scroll down.
@@ -1032,13 +1040,42 @@ describe('Screen', function()
normal gg |
"{5:^expr} |
{2:Xcolesearch }|
+ 3split |
+ let m = matchadd('Conceal', '=') |
+ setl conceallevel=2 concealcursor=nc |
normal gg |
- "=={5:expr}== |
- |
- {0:~ }|
{3:Xcolesearch }|
/expr |
]]}
end)
+
+ -- oldtest: Test_cursor_column_in_concealed_line_after_leftcol_change()
+ it('after leftcol change', function()
+ exec([[
+ 0put = 'ab' .. repeat('-', &columns) .. 'c'
+ call matchadd('Conceal', '-')
+ set nowrap ss=0 cole=3 cocu=n
+ ]])
+
+ -- Go to the end of the line (3 columns beyond the end of the screen).
+ -- Horizontal scroll would center the cursor in the screen line, but conceal
+ -- makes it go to screen column 1.
+ feed('$')
+
+ -- Are the concealed parts of the current line really hidden?
+ -- Is the window's cursor column properly updated for conceal?
+ screen:expect{grid=[[
+ ^c |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]]}
+ end)
end)
end)
diff --git a/test/functional/ui/tabline_spec.lua b/test/functional/ui/tabline_spec.lua
index 809486d4db..2cdec62d01 100644
--- a/test/functional/ui/tabline_spec.lua
+++ b/test/functional/ui/tabline_spec.lua
@@ -84,3 +84,45 @@ describe('ui/ext_tabline', function()
end}
end)
end)
+
+describe("tabline", function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = Screen.new(42, 5)
+ screen:attach()
+ end)
+
+ it('redraws when tabline option is set', function()
+ command('set tabline=asdf')
+ command('set showtabline=2')
+ screen:expect{grid=[[
+ {1:asdf }|
+ ^ |
+ {2:~ }|
+ {2:~ }|
+ |
+ ]], attr_ids={
+ [1] = {reverse = true};
+ [2] = {bold = true, foreground = Screen.colors.Blue1};
+ }}
+ command('set tabline=jkl')
+ screen:expect{grid=[[
+ {1:jkl }|
+ ^ |
+ {2:~ }|
+ {2:~ }|
+ |
+ ]], attr_ids={
+ [1] = {reverse = true};
+ [2] = {bold = true, foreground = Screen.colors.Blue};
+ }}
+ end)
+
+ it('click definitions do not leak memory #21765', function()
+ command('set tabline=%@MyClickFunc@MyClickText%T')
+ command('set showtabline=2')
+ command('redrawtabline')
+ end)
+end)
diff --git a/test/functional/ui/wildmode_spec.lua b/test/functional/ui/wildmode_spec.lua
index 98398bc7a1..50466c9473 100644
--- a/test/functional/ui/wildmode_spec.lua
+++ b/test/functional/ui/wildmode_spec.lua
@@ -1,13 +1,13 @@
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local clear, feed, command = helpers.clear, helpers.feed, helpers.command
-local iswin = helpers.iswin
local funcs = helpers.funcs
local meths = helpers.meths
local eq = helpers.eq
local eval = helpers.eval
local retry = helpers.retry
local testprg = helpers.testprg
+local is_os = helpers.is_os
describe("'wildmenu'", function()
local screen
@@ -159,7 +159,7 @@ describe("'wildmenu'", function()
-- must wait the full timeout. So make it reasonable.
screen.timeout = 1000
- if not iswin() then
+ if not is_os('win') then
command('set shell=sh') -- Need a predictable "$" prompt.
command('let $PS1 = "$"')
end
@@ -169,7 +169,7 @@ describe("'wildmenu'", function()
-- Check for a shell prompt to verify that the terminal loaded.
retry(nil, nil, function()
- if iswin() then
+ if is_os('win') then
eq('Microsoft', eval("matchstr(join(getline(1, '$')), 'Microsoft')"))
else
eq('$', eval([[matchstr(getline(1), '\$')]]))
@@ -184,11 +184,10 @@ describe("'wildmenu'", function()
screen:expect_unchanged()
end)
- it('wildmode=list,full and display+=msgsep interaction #10092', function()
+ it('wildmode=list,full and messages interaction #10092', function()
-- Need more than 5 rows, else tabline is covered and will be redrawn.
screen:try_resize(25, 7)
- command('set display+=msgsep')
command('set wildmenu wildmode=list,full')
command('set showtabline=2')
feed(':set wildm<tab>')
@@ -223,44 +222,6 @@ describe("'wildmenu'", function()
]])
end)
- it('wildmode=list,full and display-=msgsep interaction', function()
- -- Need more than 5 rows, else tabline is covered and will be redrawn.
- screen:try_resize(25, 7)
-
- command('set display-=msgsep')
- command('set wildmenu wildmode=list,full')
- feed(':set wildm<tab>')
- screen:expect([[
- ~ |
- ~ |
- ~ |
- ~ |
- :set wildm |
- wildmenu wildmode |
- :set wildm^ |
- ]])
- feed('<tab>') -- trigger wildmode full
- screen:expect([[
- ~ |
- ~ |
- ~ |
- :set wildm |
- wildmenu wildmode |
- wildmenu wildmode |
- :set wildmenu^ |
- ]])
- feed('<Esc>')
- screen:expect([[
- ^ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- |
- ]])
- end)
-
it('wildmode=longest,list', function()
-- Need more than 5 rows, else tabline is covered and will be redrawn.
screen:try_resize(25, 7)
@@ -365,7 +326,6 @@ describe("'wildmenu'", function()
screen:try_resize(25, 7)
command('set laststatus=2')
- command('set display+=msgsep')
feed(':set wildm')
feed('<c-d>')
screen:expect([[
@@ -461,20 +421,20 @@ end)
describe('command line completion', function()
local screen
before_each(function()
+ clear()
screen = Screen.new(40, 5)
screen:set_default_attr_ids({
[1] = {bold = true, foreground = Screen.colors.Blue1},
[2] = {foreground = Screen.colors.Grey0, background = Screen.colors.Yellow},
[3] = {bold = true, reverse = true},
})
+ screen:attach()
end)
after_each(function()
os.remove('Xtest-functional-viml-compl-dir')
end)
it('lists directories with empty PATH', function()
- clear()
- screen:attach()
local tmp = funcs.tempname()
command('e '.. tmp)
command('cd %:h')
@@ -491,8 +451,6 @@ describe('command line completion', function()
end)
it('completes env var names #9681', function()
- clear()
- screen:attach()
command('let $XTEST_1 = "foo" | let $XTEST_2 = "bar"')
command('set wildmenu wildmode=full')
feed(':!echo $XTEST_<tab>')
@@ -521,6 +479,58 @@ describe('command line completion', function()
:!echo $XTEST_1AaあB^ |
]])
end)
+
+ it('does not leak memory with <S-Tab> with wildmenu and only one match #19874', function()
+ meths.set_option('wildmenu', true)
+ meths.set_option('wildmode', 'full')
+ meths.set_option('wildoptions', 'pum')
+
+ feed(':sign unpla<S-Tab>')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :sign unplace^ |
+ ]])
+
+ feed('<Space>buff<Tab>')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :sign unplace buffer=^ |
+ ]])
+ end)
+
+ it('does not show matches with <S-Tab> without wildmenu with wildmode=full', function()
+ meths.set_option('wildmenu', false)
+ meths.set_option('wildmode', 'full')
+
+ feed(':sign <S-Tab>')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :sign unplace^ |
+ ]])
+ end)
+
+ it('shows matches with <S-Tab> without wildmenu with wildmode=list', function()
+ meths.set_option('wildmenu', false)
+ meths.set_option('wildmode', 'list')
+
+ feed(':sign <S-Tab>')
+ screen:expect([[
+ {3: }|
+ :sign define |
+ define list undefine |
+ jump place unplace |
+ :sign unplace^ |
+ ]])
+ end)
end)
describe('ui/ext_wildmenu', function()
diff --git a/test/functional/ui/winbar_spec.lua b/test/functional/ui/winbar_spec.lua
index 8976c4371f..ece27ec3ff 100644
--- a/test/functional/ui/winbar_spec.lua
+++ b/test/functional/ui/winbar_spec.lua
@@ -7,6 +7,8 @@ local meths = helpers.meths
local eq = helpers.eq
local poke_eventloop = helpers.poke_eventloop
local feed = helpers.feed
+local funcs = helpers.funcs
+local curwin = helpers.curwin
local pcall_err = helpers.pcall_err
describe('winbar', function()
@@ -48,6 +50,11 @@ describe('winbar', function()
{3:~ }|
|
]])
+ -- winbar is excluded from the heights returned by winheight() and getwininfo()
+ eq(11, funcs.winheight(0))
+ local win_info = funcs.getwininfo(curwin().id)[1]
+ eq(11, win_info.height)
+ eq(1, win_info.winbar)
end)
it('works with custom \'fillchars\' value', function()
diff --git a/test/functional/vimscript/api_functions_spec.lua b/test/functional/vimscript/api_functions_spec.lua
index 8ca245f61a..c032ac3030 100644
--- a/test/functional/vimscript/api_functions_spec.lua
+++ b/test/functional/vimscript/api_functions_spec.lua
@@ -5,6 +5,7 @@ local neq, eq, command = helpers.neq, helpers.eq, helpers.command
local clear, curbufmeths = helpers.clear, helpers.curbufmeths
local exc_exec, expect, eval = helpers.exc_exec, helpers.expect, helpers.eval
local insert, pcall_err = helpers.insert, helpers.pcall_err
+local matches = helpers.matches
local meths = helpers.meths
describe('eval-API', function()
@@ -49,7 +50,7 @@ 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: E565: Not allowed to change text or change window',
+ matches('Vim%(call%):E5555: API call: E565: Not allowed to change text or change window$',
pcall_err(command, "normal! yy"))
end)
diff --git a/test/functional/vimscript/container_functions_spec.lua b/test/functional/vimscript/container_functions_spec.lua
index 04a3248c49..5bef3fce05 100644
--- a/test/functional/vimscript/container_functions_spec.lua
+++ b/test/functional/vimscript/container_functions_spec.lua
@@ -8,7 +8,7 @@ local clear = helpers.clear
before_each(clear)
describe('extend()', function()
- it('suceeds to extend list with itself', function()
+ it('succeeds to extend list with itself', function()
meths.set_var('l', {1, {}})
eq({1, {}, 1, {}}, eval('extend(l, l)'))
eq({1, {}, 1, {}}, meths.get_var('l'))
diff --git a/test/functional/vimscript/ctx_functions_spec.lua b/test/functional/vimscript/ctx_functions_spec.lua
index d92a81c55b..5ee84a6d13 100644
--- a/test/functional/vimscript/ctx_functions_spec.lua
+++ b/test/functional/vimscript/ctx_functions_spec.lua
@@ -173,9 +173,9 @@ describe('context functions', function()
call('SaveSFuncs')
call('DeleteSFuncs')
- eq('Vim(call):E117: Unknown function: s:greet',
+ eq('function Greet, line 1: Vim(call):E117: Unknown function: s:greet',
pcall_err(command, [[call Greet('World')]]))
- eq('Vim(call):E117: Unknown function: s:greet_all',
+ eq('function GreetAll, line 1: Vim(call):E117: Unknown function: s:greet_all',
pcall_err(command, [[call GreetAll('World', 'One', 'Two', 'Three')]]))
call('RestoreFuncs')
@@ -287,9 +287,11 @@ describe('context functions', function()
local with_jumps = {
['jumps'] = eval(([[
- filter(map(getjumplist()[0], 'filter(
- { "f": expand("#".v:val.bufnr.":p"), "l": v:val.lnum },
- { k, v -> k != "l" || v != 1 })'), '!empty(v:val.f)')
+ filter(map(add(
+ getjumplist()[0], { 'bufnr': bufnr('%'), 'lnum': getcurpos()[1] }),
+ 'filter(
+ { "f": expand("#".v:val.bufnr.":p"), "l": v:val.lnum },
+ { k, v -> k != "l" || v != 1 })'), '!empty(v:val.f)')
]]):gsub('\n', ''))
}
diff --git a/test/functional/vimscript/eval_spec.lua b/test/functional/vimscript/eval_spec.lua
index 0c2ca8de78..a8a901042b 100644
--- a/test/functional/vimscript/eval_spec.lua
+++ b/test/functional/vimscript/eval_spec.lua
@@ -10,16 +10,22 @@
-- test/functional/vimscript/functions_spec.lua
local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
local lfs = require('lfs')
local clear = helpers.clear
local eq = helpers.eq
local exc_exec = helpers.exc_exec
+local exec_lua = helpers.exec_lua
+local exec_capture = helpers.exec_capture
local eval = helpers.eval
local command = helpers.command
local write_file = helpers.write_file
local meths = helpers.meths
local sleep = helpers.sleep
+local matches = helpers.matches
+local pcall_err = helpers.pcall_err
+local assert_alive = helpers.assert_alive
local poke_eventloop = helpers.poke_eventloop
local feed = helpers.feed
@@ -65,13 +71,13 @@ describe("backtick expansion", function()
end)
it("with default 'shell'", function()
- if helpers.iswin() then
+ if helpers.is_os('win') then
command(":silent args `dir /b *2`")
else
command(":silent args `echo ***2`")
end
eq({ "file2", }, eval("argv()"))
- if helpers.iswin() then
+ if helpers.is_os('win') then
command(":silent args `dir /s/b *4`")
eq({ "subdir\\file4", }, eval("map(argv(), 'fnamemodify(v:val, \":.\")')"))
else
@@ -144,3 +150,73 @@ describe('List support code', function()
end
end)
end)
+
+describe("uncaught exception", function()
+ before_each(clear)
+ after_each(function()
+ os.remove('throw1.vim')
+ os.remove('throw2.vim')
+ os.remove('throw3.vim')
+ end)
+
+ it('is not forgotten #13490', function()
+ command('autocmd BufWinEnter * throw "i am error"')
+ eq('i am error', exc_exec('try | new | endtry'))
+
+ -- Like Vim, throwing here aborts the processing of the script, but does not stop :runtime!
+ -- from processing the others.
+ -- Only the first thrown exception should be rethrown from the :try below, though.
+ for i = 1, 3 do
+ write_file('throw' .. i .. '.vim', ([[
+ let result ..= '%d'
+ throw 'throw%d'
+ let result ..= 'X'
+ ]]):format(i, i))
+ end
+ command('set runtimepath+=. | let result = ""')
+ eq('throw1', exc_exec('try | runtime! throw*.vim | endtry'))
+ eq('123', eval('result'))
+ end)
+end)
+
+describe('listing functions using :function', function()
+ before_each(clear)
+
+ it('works for lambda functions with <lambda> #20466', function()
+ command('let A = {-> 1}')
+ local num = exec_capture('echo A'):match("function%('<lambda>(%d+)'%)")
+ eq(([[
+ function <lambda>%s(...)
+1 return 1
+ endfunction]]):format(num), exec_capture(('function <lambda>%s'):format(num)))
+ end)
+
+ -- FIXME: If the same function is deleted, the crash still happens. #20790
+ it('does not crash if another function is deleted while listing', function()
+ local screen = Screen.new(80, 24)
+ screen:attach()
+ matches('Vim%(function%):E454: function list was modified', pcall_err(exec_lua, [=[
+ vim.cmd([[
+ func Func1()
+ endfunc
+ func Func2()
+ endfunc
+ func Func3()
+ endfunc
+ ]])
+
+ local ns = vim.api.nvim_create_namespace('test')
+
+ vim.ui_attach(ns, { ext_messages = true }, function(event, _, content)
+ if event == 'msg_show' and content[1][2] == 'function Func1()' then
+ vim.cmd('delfunc Func3')
+ end
+ end)
+
+ vim.cmd('function')
+
+ vim.ui_detach(ns)
+ ]=]))
+ assert_alive()
+ end)
+end)
diff --git a/test/functional/vimscript/executable_spec.lua b/test/functional/vimscript/executable_spec.lua
index b4162b2336..43e4a29e1a 100644
--- a/test/functional/vimscript/executable_spec.lua
+++ b/test/functional/vimscript/executable_spec.lua
@@ -1,15 +1,16 @@
local helpers = require('test.functional.helpers')(after_each)
-local eq, clear, call, iswin, write_file, command =
- helpers.eq, helpers.clear, helpers.call, helpers.iswin, helpers.write_file,
+local eq, clear, call, write_file, command =
+ helpers.eq, helpers.clear, helpers.call, helpers.write_file,
helpers.command
local exc_exec = helpers.exc_exec
local eval = helpers.eval
+local is_os = helpers.is_os
describe('executable()', function()
before_each(clear)
it('returns 1 for commands in $PATH', function()
- local exe = iswin() and 'ping' or 'ls'
+ local exe = is_os('win') and 'ping' or 'ls'
eq(1, call('executable', exe))
command('let $PATH = fnamemodify("./test/functional/fixtures/bin", ":p")')
eq(1, call('executable', 'null'))
@@ -17,7 +18,7 @@ describe('executable()', function()
eq(1, call('executable', 'false'))
end)
- if iswin() then
+ if is_os('win') 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'), ':.'))
@@ -34,11 +35,13 @@ describe('executable()', function()
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..')'))
+ eq('Vim(call):E1174: String required for argument 1',
+ exc_exec('call executable('..input..')'))
end
command('let $PATH = fnamemodify("./test/functional/fixtures/bin", ":p")')
for _, input in ipairs({'v:null', 'v:true', 'v:false'}) do
- eq('Vim(call):E928: String required', exc_exec('call executable('..input..')'))
+ eq('Vim(call):E1174: String required for argument 1',
+ exc_exec('call executable('..input..')'))
end
end)
@@ -54,8 +57,8 @@ describe('executable()', function()
-- Some executable in build/bin/, *not* in $PATH nor CWD.
local sibling_exe = 'printargs-test'
-- Windows: siblings are in Nvim's "pseudo-$PATH".
- local expected = iswin() and 1 or 0
- if iswin() then
+ local expected = is_os('win') and 1 or 0
+ if is_os('win') then
eq('arg1=lemon;arg2=sky;arg3=tree;',
call('system', sibling_exe..' lemon sky tree'))
end
@@ -67,7 +70,7 @@ describe('executable()', function()
clear()
write_file('Xtest_not_executable', 'non-executable file')
write_file('Xtest_executable', 'executable file (exec-bit set)')
- if not iswin() then -- N/A for Windows.
+ if not is_os('win') then -- N/A for Windows.
call('system', {'chmod', '-x', 'Xtest_not_executable'})
call('system', {'chmod', '+x', 'Xtest_executable'})
end
@@ -88,14 +91,17 @@ describe('executable()', function()
end)
it('set, qualified as a path', function()
- local expected = iswin() and 0 or 1
+ local expected = is_os('win') and 0 or 1
eq(expected, call('executable', './Xtest_executable'))
end)
end)
end)
describe('executable() (Windows)', function()
- if not iswin() then return end -- N/A for Unix.
+ if not is_os('win') then
+ pending('N/A for non-windows')
+ return
+ end
local exts = {'bat', 'exe', 'com', 'cmd'}
setup(function()
diff --git a/test/functional/vimscript/execute_spec.lua b/test/functional/vimscript/execute_spec.lua
index a733b098f5..5fe3d787cb 100644
--- a/test/functional/vimscript/execute_spec.lua
+++ b/test/functional/vimscript/execute_spec.lua
@@ -8,7 +8,7 @@ local funcs = helpers.funcs
local Screen = require('test.functional.ui.screen')
local command = helpers.command
local feed = helpers.feed
-local iswin = helpers.iswin
+local is_os = helpers.is_os
describe('execute()', function()
before_each(clear)
@@ -265,7 +265,7 @@ describe('execute()', function()
-- This deviates from vim behavior, but is consistent
-- with how nvim currently displays the output.
it('captures shell-command output', function()
- local win_lf = iswin() and '\13' or ''
+ local win_lf = is_os('win') and '\13' or ''
eq('\n:!echo foo\r\n\nfoo'..win_lf..'\n', funcs.execute('!echo foo'))
end)
diff --git a/test/functional/vimscript/exepath_spec.lua b/test/functional/vimscript/exepath_spec.lua
index bbca954511..056f67e0ad 100644
--- a/test/functional/vimscript/exepath_spec.lua
+++ b/test/functional/vimscript/exepath_spec.lua
@@ -1,19 +1,20 @@
local helpers = require('test.functional.helpers')(after_each)
-local eq, clear, call, iswin =
- helpers.eq, helpers.clear, helpers.call, helpers.iswin
+local eq, clear, call =
+ helpers.eq, helpers.clear, helpers.call
local command = helpers.command
local exc_exec = helpers.exc_exec
local matches = helpers.matches
+local is_os = helpers.is_os
describe('exepath()', function()
before_each(clear)
it('returns 1 for commands in $PATH', function()
- local exe = iswin() and 'ping' or 'ls'
- local ext_pat = iswin() and '%.EXE$' or '$'
+ local exe = is_os('win') and 'ping' or 'ls'
+ local ext_pat = is_os('win') and '%.EXE$' or '$'
matches(exe .. ext_pat, call('exepath', exe))
command('let $PATH = fnamemodify("./test/functional/fixtures/bin", ":p")')
- ext_pat = iswin() and '%.CMD$' or '$'
+ ext_pat = is_os('win') and '%.CMD$' or '$'
matches('null' .. ext_pat, call('exepath', 'null'))
matches('true' .. ext_pat, call('exepath', 'true'))
matches('false' .. ext_pat, call('exepath', 'false'))
@@ -21,16 +22,16 @@ describe('exepath()', function()
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 exepath('..input..')'))
+ eq('Vim(call):E1174: String required for argument 1', exc_exec('call exepath('..input..')'))
end
- eq('Vim(call):E1142: Non-empty string required', exc_exec('call exepath("")'))
+ eq('Vim(call):E1175: Non-empty string required for argument 1', exc_exec('call exepath("")'))
command('let $PATH = fnamemodify("./test/functional/fixtures/bin", ":p")')
for _, input in ipairs({'v:null', 'v:true', 'v:false'}) do
- eq('Vim(call):E928: String required', exc_exec('call exepath('..input..')'))
+ eq('Vim(call):E1174: String required for argument 1', exc_exec('call exepath('..input..')'))
end
end)
- if iswin() then
+ if is_os('win') then
it('append extension if omitted', function()
local filename = 'cmd'
local pathext = '.exe'
diff --git a/test/functional/vimscript/fnamemodify_spec.lua b/test/functional/vimscript/fnamemodify_spec.lua
index d54a6db417..c3ecdd853c 100644
--- a/test/functional/vimscript/fnamemodify_spec.lua
+++ b/test/functional/vimscript/fnamemodify_spec.lua
@@ -1,12 +1,12 @@
local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
local eq = helpers.eq
-local iswin = helpers.iswin
local fnamemodify = helpers.funcs.fnamemodify
local getcwd = helpers.funcs.getcwd
local command = helpers.command
local write_file = helpers.write_file
local alter_slashes = helpers.alter_slashes
+local is_os = helpers.is_os
local function eq_slashconvert(expected, got)
eq(alter_slashes(expected), alter_slashes(got))
@@ -27,7 +27,7 @@ describe('fnamemodify()', function()
local root = helpers.pathroot()
eq(root, fnamemodify([[/]], ':p:h'))
eq(root, fnamemodify([[/]], ':p'))
- if iswin() then
+ if is_os('win') then
eq(root, fnamemodify([[\]], ':p:h'))
eq(root, fnamemodify([[\]], ':p'))
command('set shellslash')
@@ -114,7 +114,7 @@ describe('fnamemodify()', function()
it('handles shell escape', function()
local expected
- if iswin() then
+ if is_os('win') then
-- we expand with double-quotes on Windows
expected = [["hello there! quote ' newline]] .. '\n' .. [["]]
else
diff --git a/test/functional/vimscript/functions_spec.lua b/test/functional/vimscript/functions_spec.lua
index 20c1400030..09b3334989 100644
--- a/test/functional/vimscript/functions_spec.lua
+++ b/test/functional/vimscript/functions_spec.lua
@@ -9,12 +9,12 @@ local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
local eval = helpers.eval
-local iswin = helpers.iswin
local matches = helpers.matches
+local is_os = helpers.is_os
before_each(clear)
it('windowsversion()', function()
clear()
- matches(iswin() and '^%d+%.%d+$' or '^$', eval('windowsversion()'))
+ matches(is_os('win') and '^%d+%.%d+$' or '^$', eval('windowsversion()'))
end)
diff --git a/test/functional/vimscript/has_spec.lua b/test/functional/vimscript/has_spec.lua
index 4d9b226434..2e26d603b3 100644
--- a/test/functional/vimscript/has_spec.lua
+++ b/test/functional/vimscript/has_spec.lua
@@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each)
local eq = helpers.eq
local clear = helpers.clear
local funcs = helpers.funcs
-local iswin = helpers.iswin
+local is_os = helpers.is_os
describe('has()', function()
before_each(clear)
@@ -51,7 +51,7 @@ describe('has()', function()
end)
it('"unnamedplus"', function()
- if (not iswin()) and funcs.has("clipboard") == 1 then
+ if (not is_os('win')) and funcs.has("clipboard") == 1 then
eq(1, funcs.has("unnamedplus"))
else
eq(0, funcs.has("unnamedplus"))
diff --git a/test/functional/vimscript/hostname_spec.lua b/test/functional/vimscript/hostname_spec.lua
index 6112cf64e3..7d4baa7213 100644
--- a/test/functional/vimscript/hostname_spec.lua
+++ b/test/functional/vimscript/hostname_spec.lua
@@ -3,7 +3,7 @@ local eq = helpers.eq
local ok = helpers.ok
local call = helpers.call
local clear = helpers.clear
-local iswin = helpers.iswin
+local is_os = helpers.is_os
describe('hostname()', function()
before_each(clear)
@@ -13,8 +13,8 @@ describe('hostname()', function()
ok(string.len(actual) > 0)
if call('executable', 'hostname') == 1 then
local expected = string.gsub(call('system', 'hostname'), '[\n\r]', '')
- eq((iswin() and expected:upper() or expected),
- (iswin() and actual:upper() or actual))
+ eq((is_os('win') and expected:upper() or expected),
+ (is_os('win') and actual:upper() or actual))
end
end)
end)
diff --git a/test/functional/vimscript/input_spec.lua b/test/functional/vimscript/input_spec.lua
index 554d15e550..f50b39c2c5 100644
--- a/test/functional/vimscript/input_spec.lua
+++ b/test/functional/vimscript/input_spec.lua
@@ -8,7 +8,8 @@ local clear = helpers.clear
local source = helpers.source
local command = helpers.command
local exc_exec = helpers.exc_exec
-local nvim_async = helpers.nvim_async
+local pcall_err = helpers.pcall_err
+local async_meths = helpers.async_meths
local NIL = helpers.NIL
local screen
@@ -449,6 +450,78 @@ describe('inputdialog()', function()
end)
describe('confirm()', function()
+ -- oldtest: Test_confirm()
+ it('works', function()
+ meths.set_option('more', false) -- Avoid hit-enter prompt
+ meths.set_option('laststatus', 2)
+ -- screen:expect() calls are needed to avoid feeding input too early
+ screen:expect({any = '%[No Name%]'})
+
+ async_meths.command([[let a = confirm('Press O to proceed')]])
+ screen:expect({any = '{CONFIRM:.+: }'})
+ feed('o')
+ screen:expect({any = '%[No Name%]'})
+ eq(1, meths.get_var('a'))
+
+ async_meths.command([[let a = 'Are you sure?'->confirm("&Yes\n&No")]])
+ screen:expect({any = '{CONFIRM:.+: }'})
+ feed('y')
+ screen:expect({any = '%[No Name%]'})
+ eq(1, meths.get_var('a'))
+
+ async_meths.command([[let a = confirm('Are you sure?', "&Yes\n&No")]])
+ screen:expect({any = '{CONFIRM:.+: }'})
+ feed('n')
+ screen:expect({any = '%[No Name%]'})
+ eq(2, meths.get_var('a'))
+
+ -- Not possible to match Vim's CTRL-C test here as CTRL-C always sets got_int in Nvim.
+
+ -- confirm() should return 0 when pressing ESC.
+ async_meths.command([[let a = confirm('Are you sure?', "&Yes\n&No")]])
+ screen:expect({any = '{CONFIRM:.+: }'})
+ feed('<Esc>')
+ screen:expect({any = '%[No Name%]'})
+ eq(0, meths.get_var('a'))
+
+ -- Default choice is returned when pressing <CR>.
+ async_meths.command([[let a = confirm('Are you sure?', "&Yes\n&No")]])
+ screen:expect({any = '{CONFIRM:.+: }'})
+ feed('<CR>')
+ screen:expect({any = '%[No Name%]'})
+ eq(1, meths.get_var('a'))
+
+ async_meths.command([[let a = confirm('Are you sure?', "&Yes\n&No", 2)]])
+ screen:expect({any = '{CONFIRM:.+: }'})
+ feed('<CR>')
+ screen:expect({any = '%[No Name%]'})
+ eq(2, meths.get_var('a'))
+
+ async_meths.command([[let a = confirm('Are you sure?', "&Yes\n&No", 0)]])
+ screen:expect({any = '{CONFIRM:.+: }'})
+ feed('<CR>')
+ screen:expect({any = '%[No Name%]'})
+ eq(0, meths.get_var('a'))
+
+ -- Test with the {type} 4th argument
+ for _, type in ipairs({'Error', 'Question', 'Info', 'Warning', 'Generic'}) do
+ async_meths.command(([[let a = confirm('Are you sure?', "&Yes\n&No", 1, '%s')]]):format(type))
+ screen:expect({any = '{CONFIRM:.+: }'})
+ feed('y')
+ screen:expect({any = '%[No Name%]'})
+ eq(1, meths.get_var('a'))
+ end
+
+ eq('Vim(call):E730: using List as a String',
+ pcall_err(command, 'call confirm([])'))
+ eq('Vim(call):E730: using List as a String',
+ pcall_err(command, 'call confirm("Are you sure?", [])'))
+ eq('Vim(call):E745: Using a List as a Number',
+ pcall_err(command, 'call confirm("Are you sure?", "&Yes\n&No\n", [])'))
+ eq('Vim(call):E730: using List as a String',
+ pcall_err(command, 'call confirm("Are you sure?", "&Yes\n&No\n", 0, [])'))
+ end)
+
it("shows dialog even if :silent #8788", function()
command("autocmd BufNewFile * call confirm('test')")
@@ -483,7 +556,7 @@ describe('confirm()', function()
feed(':call nvim_command("edit x")<cr>')
check_and_clear(':call nvim_command("edit |\n')
- nvim_async('command', 'edit x')
+ async_meths.command('edit x')
check_and_clear(' |\n')
end)
end)
diff --git a/test/functional/vimscript/json_functions_spec.lua b/test/functional/vimscript/json_functions_spec.lua
index 5d1597f53d..70d0934756 100644
--- a/test/functional/vimscript/json_functions_spec.lua
+++ b/test/functional/vimscript/json_functions_spec.lua
@@ -343,7 +343,7 @@ describe('json_decode() function', function()
exc_exec('call json_decode("\\t\\"abc\\\\u0000")'))
end)
- it('fails to parse unknown escape sequnces', function()
+ it('fails to parse unknown escape sequences', function()
eq('Vim(call):E474: Unknown escape sequence: \\a"',
exc_exec('call json_decode("\\t\\"\\\\a\\"")'))
end)
diff --git a/test/functional/vimscript/map_functions_spec.lua b/test/functional/vimscript/map_functions_spec.lua
index aa64006de0..ba1b4d7a76 100644
--- a/test/functional/vimscript/map_functions_spec.lua
+++ b/test/functional/vimscript/map_functions_spec.lua
@@ -3,6 +3,8 @@ local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
local eq = helpers.eq
local eval = helpers.eval
+local exec = helpers.exec
+local exec_lua = helpers.exec_lua
local expect = helpers.expect
local feed = helpers.feed
local funcs = helpers.funcs
@@ -10,6 +12,8 @@ local meths = helpers.meths
local nvim = helpers.nvim
local source = helpers.source
local command = helpers.command
+local exec_capture = helpers.exec_capture
+local pcall_err = helpers.pcall_err
describe('maparg()', function()
before_each(clear)
@@ -172,14 +176,12 @@ describe('mapset()', function()
it('can restore mapping description from the dict returned by maparg()', function()
meths.set_keymap('n', 'lhs', 'rhs', {desc = 'map description'})
- eq('\nn lhs rhs\n map description',
- helpers.exec_capture("nmap lhs"))
+ eq('\nn lhs rhs\n map description', exec_capture("nmap lhs"))
local mapargs = funcs.maparg('lhs', 'n', false, true)
- meths.del_keymap('n', 'lhs')
- eq('\nNo mapping found', helpers.exec_capture("nmap lhs"))
+ meths.set_keymap('n', 'lhs', 'rhs', {desc = 'MAP DESCRIPTION'})
+ eq('\nn lhs rhs\n MAP DESCRIPTION', exec_capture("nmap lhs"))
funcs.mapset('n', false, mapargs)
- eq('\nn lhs rhs\n map description',
- helpers.exec_capture("nmap lhs"))
+ eq('\nn lhs rhs\n map description', exec_capture("nmap lhs"))
end)
it('can restore "replace_keycodes" from the dict returned by maparg()', function()
@@ -194,4 +196,59 @@ describe('mapset()', function()
feed('foo')
expect('<<lt><')
end)
+
+ it('replaces an abbreviation of the same lhs #20320', function()
+ command('inoreabbr foo bar')
+ eq('\ni foo * bar', exec_capture('iabbr foo'))
+ feed('ifoo ')
+ expect('bar ')
+ local mapargs = funcs.maparg('foo', 'i', true, true)
+ command('inoreabbr foo BAR')
+ eq('\ni foo * BAR', exec_capture('iabbr foo'))
+ feed('foo ')
+ expect('bar BAR ')
+ funcs.mapset('i', true, mapargs)
+ eq('\ni foo * bar', exec_capture('iabbr foo'))
+ feed('foo<Esc>')
+ expect('bar BAR bar')
+ end)
+
+ it('can restore Lua callback from the dict returned by maparg()', function()
+ eq(0, exec_lua([[
+ GlobalCount = 0
+ vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ return GlobalCount
+ ]]))
+ feed('asdf')
+ eq(1, exec_lua([[return GlobalCount]]))
+
+ exec_lua([[
+ _G.saved_asdf_map = vim.fn.maparg('asdf', 'n', false, true)
+ vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 10 end })
+ ]])
+ feed('asdf')
+ eq(11, exec_lua([[return GlobalCount]]))
+
+ exec_lua([[vim.fn.mapset('n', false, _G.saved_asdf_map)]])
+ feed('asdf')
+ eq(12, exec_lua([[return GlobalCount]]))
+
+ exec([[
+ let g:saved_asdf_map = maparg('asdf', 'n', v:false, v:true)
+ lua vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 10 end })
+ ]])
+ feed('asdf')
+ eq(22, exec_lua([[return GlobalCount]]))
+
+ command([[call mapset('n', v:false, g:saved_asdf_map)]])
+ feed('asdf')
+ eq(23, exec_lua([[return GlobalCount]]))
+ end)
+
+ it('does not leak memory if lhs is missing', function()
+ eq('Vim:E460: entries missing in mapset() dict argument',
+ pcall_err(exec_lua, [[vim.fn.mapset('n', false, {rhs = 'foo'})]]))
+ eq('Vim:E460: entries missing in mapset() dict argument',
+ pcall_err(exec_lua, [[vim.fn.mapset('n', false, {callback = function() end})]]))
+ end)
end)
diff --git a/test/functional/vimscript/msgpack_functions_spec.lua b/test/functional/vimscript/msgpack_functions_spec.lua
index cab67d77e4..de5a721efe 100644
--- a/test/functional/vimscript/msgpack_functions_spec.lua
+++ b/test/functional/vimscript/msgpack_functions_spec.lua
@@ -5,7 +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
+local is_os = helpers.is_os
describe('msgpack*() functions', function()
before_each(clear)
@@ -467,7 +467,7 @@ 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
+ if is_os('win') then
helpers.assert_alive()
pending('msgpackparse() has a bug on windows')
return
diff --git a/test/functional/vimscript/null_spec.lua b/test/functional/vimscript/null_spec.lua
index 2451da983e..1153baac46 100644
--- a/test/functional/vimscript/null_spec.lua
+++ b/test/functional/vimscript/null_spec.lua
@@ -69,7 +69,7 @@ describe('NULL', function()
null_expr_test('can be splice-indexed', 'L[:]', 0, {})
null_expr_test('is not locked', 'islocked("v:_null_list")', 0, 0)
null_test('is accepted by :for', 'for x in L|throw x|endfor', 0)
- null_expr_test('does not crash append()', 'append(1, L)', 0, 0, function()
+ null_expr_test('does not crash append()', 'append(0, L)', 0, 0, function()
eq({''}, curbufmeths.get_lines(0, -1, false))
end)
null_expr_test('does not crash setline()', 'setline(1, L)', 0, 0, function()
diff --git a/test/functional/vimscript/server_spec.lua b/test/functional/vimscript/server_spec.lua
index 6e95459630..c89a0c4e93 100644
--- a/test/functional/vimscript/server_spec.lua
+++ b/test/functional/vimscript/server_spec.lua
@@ -1,11 +1,14 @@
local helpers = require('test.functional.helpers')(after_each)
+local assert_log = helpers.assert_log
local eq, neq, eval = helpers.eq, helpers.neq, helpers.eval
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 is_os = helpers.is_os
+
+local testlog = 'Xtest-server-log'
local function clear_serverlist()
for _, server in pairs(funcs.serverlist()) do
@@ -14,12 +17,16 @@ local function clear_serverlist()
end
describe('server', function()
+ after_each(function()
+ os.remove(testlog)
+ end)
+
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
+ if not is_os('win') then
matches(dir, funcs.serverstart())
end
end)
@@ -65,7 +72,7 @@ describe('server', function()
eq('', meths.get_vvar('servername'))
-- v:servername and $NVIM take the next available server.
- local servername = (iswin() and [[\\.\pipe\Xtest-functional-server-pipe]]
+ local servername = (is_os('win') and [[\\.\pipe\Xtest-functional-server-pipe]]
or './Xtest-functional-server-socket')
funcs.serverstart(servername)
eq(servername, meths.get_vvar('servername'))
@@ -74,13 +81,20 @@ describe('server', function()
end)
it('serverstop() returns false for invalid input', function()
- clear()
+ clear{env={
+ NVIM_LOG_FILE=testlog,
+ NVIM_LISTEN_ADDRESS='.',
+ }}
eq(0, eval("serverstop('')"))
eq(0, eval("serverstop('bogus-socket-name')"))
+ assert_log('Not listening on bogus%-socket%-name', testlog, 10)
end)
it('parses endpoints', function()
- clear()
+ clear{env={
+ NVIM_LOG_FILE=testlog,
+ NVIM_LISTEN_ADDRESS='.',
+ }}
clear_serverlist()
eq({}, funcs.serverlist())
@@ -104,6 +118,7 @@ describe('server', function()
if status then
table.insert(expected, v4)
pcall(funcs.serverstart, v4) -- exists already; ignore
+ assert_log('Failed to start server: address already in use: 127%.0%.0%.1', testlog, 10)
end
local v6 = '::1:12345'
@@ -111,6 +126,7 @@ describe('server', function()
if status then
table.insert(expected, v6)
pcall(funcs.serverstart, v6) -- exists already; ignore
+ assert_log('Failed to start server: address already in use: ::1', testlog, 10)
end
eq(expected, funcs.serverlist())
clear_serverlist()
@@ -130,7 +146,7 @@ describe('server', function()
local n = eval('len(serverlist())')
-- Add some servers.
- local servs = (iswin()
+ local servs = (is_os('win')
and { [[\\.\pipe\Xtest-pipe0934]], [[\\.\pipe\Xtest-pipe4324]] }
or { [[./Xtest-pipe0934]], [[./Xtest-pipe4324]] })
for _, s in ipairs(servs) do
@@ -164,7 +180,7 @@ describe('startup --listen', function()
end)
it('sets v:servername, overrides $NVIM_LISTEN_ADDRESS', function()
- local addr = (iswin() and [[\\.\pipe\Xtest-listen-pipe]]
+ local addr = (is_os('win') and [[\\.\pipe\Xtest-listen-pipe]]
or './Xtest-listen-pipe')
clear({ env={ NVIM_LISTEN_ADDRESS='./Xtest-env-pipe' },
args={ '--listen', addr } })
diff --git a/test/functional/vimscript/system_spec.lua b/test/functional/vimscript/system_spec.lua
index a778e2f435..7ada1c4bea 100644
--- a/test/functional/vimscript/system_spec.lua
+++ b/test/functional/vimscript/system_spec.lua
@@ -1,3 +1,5 @@
+-- Tests for system() and :! shell.
+
local helpers = require('test.functional.helpers')(after_each)
local assert_alive = helpers.assert_alive
@@ -9,9 +11,9 @@ 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
local pcall_err = helpers.pcall_err
+local is_os = helpers.is_os
local Screen = require('test.functional.ui.screen')
@@ -85,7 +87,7 @@ describe('system()', function()
end)
it('does NOT run in shell', function()
- if iswin() then
+ if is_os('win') then
eq("%PATH%\n", eval("system(['powershell', '-NoProfile', '-NoLogo', '-ExecutionPolicy', 'RemoteSigned', '-Command', 'Write-Output', '%PATH%'])"))
else
eq("* $PATH %PATH%\n", eval("system(['echo', '*', '$PATH', '%PATH%'])"))
@@ -94,7 +96,7 @@ describe('system()', function()
end)
it('sets v:shell_error', function()
- if iswin() then
+ if is_os('win') then
eval([[system("cmd.exe /c exit")]])
eq(0, eval('v:shell_error'))
eval([[system("cmd.exe /c exit 1")]])
@@ -123,7 +125,7 @@ describe('system()', function()
screen:attach()
end)
- if iswin() then
+ if is_os('win') then
local function test_more()
eq('root = true', eval([[get(split(system('"more" ".editorconfig"'), "\n"), 0, '')]]))
end
@@ -184,7 +186,7 @@ describe('system()', function()
-- * on Windows, expected to default to Western European enc
-- * on Linux, expected to default to UTF8
command([[let &shellcmdflag = '-NoLogo -NoProfile -ExecutionPolicy RemoteSigned -Command ']])
- eq(iswin() and '??\n' or 'ああ\n', eval([[system('Write-Output "ああ"')]]))
+ eq(is_os('win') and '??\n' or 'ああ\n', eval([[system('Write-Output "ああ"')]]))
end)
it('`echo` and waits for its return', function()
@@ -213,7 +215,7 @@ describe('system()', function()
screen:try_resize(72, 14)
feed(':4verbose echo system("echo hi")<cr>')
- if iswin() then
+ if is_os('win') then
screen:expect{any=[[Executing command: "'fake_shell' 'cmdflag' '"echo hi"'"]]}
else
screen:expect{any=[[Executing command: "'fake_shell' 'cmdflag' 'echo hi'"]]}
@@ -243,7 +245,7 @@ describe('system()', function()
end)
it('`yes` interrupted with CTRL-C', function()
- feed(':call system("' .. (iswin()
+ feed(':call system("' .. (is_os('win')
and 'for /L %I in (1,0,2) do @echo y'
or 'yes') .. '")<cr>')
screen:expect([[
@@ -260,7 +262,7 @@ describe('system()', function()
~ |
~ |
~ |
-]] .. (iswin()
+]] .. (is_os('win')
and [[
:call system("for /L %I in (1,0,2) do @echo y") |]]
or [[
@@ -286,7 +288,7 @@ describe('system()', function()
it('`yes` interrupted with mapped CTRL-C', function()
command('nnoremap <C-C> i')
- feed(':call system("' .. (iswin()
+ feed(':call system("' .. (is_os('win')
and 'for /L %I in (1,0,2) do @echo y'
or 'yes') .. '")<cr>')
screen:expect([[
@@ -303,7 +305,7 @@ describe('system()', function()
~ |
~ |
~ |
-]] .. (iswin()
+]] .. (is_os('win')
and [[
:call system("for /L %I in (1,0,2) do @echo y") |]]
or [[
@@ -330,7 +332,7 @@ describe('system()', function()
describe('passing no input', function()
it('returns the program output', function()
- if iswin() then
+ if is_os('win') then
eq("echoed\n", eval('system("echo echoed")'))
else
eq("echoed", eval('system("echo -n echoed")'))
@@ -438,7 +440,7 @@ describe('systemlist()', function()
before_each(clear)
it('sets v:shell_error', function()
- if iswin() then
+ if is_os('win') then
eval([[systemlist("cmd.exe /c exit")]])
eq(0, eval('v:shell_error'))
eval([[systemlist("cmd.exe /c exit 1")]])
@@ -617,12 +619,12 @@ describe('systemlist()', function()
return
end
helpers.set_shell_powershell()
- eq({iswin() and 'あ\r' or 'あ'}, eval([[systemlist('Write-Output あ')]]))
+ eq({is_os('win') and 'あ\r' or 'あ'}, eval([[systemlist('Write-Output あ')]]))
-- Sanity test w/ default encoding
-- * on Windows, expected to default to Western European enc
-- * on Linux, expected to default to UTF8
command([[let &shellcmdflag = '-NoLogo -NoProfile -ExecutionPolicy RemoteSigned -Command ']])
- eq({iswin() and '?\r' or 'あ'}, eval([[systemlist('Write-Output あ')]]))
+ eq({is_os('win') and '?\r' or 'あ'}, eval([[systemlist('Write-Output あ')]]))
end)
end)
@@ -639,15 +641,15 @@ describe('shell :!', function()
1
4
2]])
- if iswin() then
+ if is_os('win') then
feed(':4verbose %!sort /R<cr>')
screen:expect{
- any=[[Executing command: .?Start%-Process sort %-ArgumentList "/R" %-RedirectStandardInput .* %-RedirectStandardOutput .* %-NoNewWindow %-Wait]]
+ any=[[Executing command: .?& { Get%-Content .* | & sort /R } 2>&1 | Out%-File %-Encoding UTF8 .*; exit $LastExitCode"]]
}
else
feed(':4verbose %!sort -r<cr>')
screen:expect{
- any=[[Executing command: .?Start%-Process sort %-ArgumentList "%-r" %-RedirectStandardInput .* %-RedirectStandardOutput .* %-NoNewWindow %-Wait]]
+ any=[[Executing command: .?& { Get%-Content .* | & sort %-r } 2>&1 | Out%-File %-Encoding UTF8 .*; exit $LastExitCode"]]
}
end
feed('<CR>')
@@ -660,4 +662,33 @@ describe('shell :!', function()
1]])
end
end)
+
+ it(':{range}! without redirecting to buffer', function()
+ local screen = Screen.new(500, 10)
+ screen:attach()
+ insert([[
+ 3
+ 1
+ 4
+ 2]])
+ feed(':4verbose %w !sort<cr>')
+ if is_os('win') then
+ screen:expect{
+ any=[[Executing command: .?sort %< .*]]
+ }
+ else
+ screen:expect{
+ any=[[Executing command: .?%(sort%) %< .*]]
+
+ }
+ end
+ feed('<CR>')
+ helpers.set_shell_powershell(true)
+ feed(':4verbose %w !sort<cr>')
+ screen:expect{
+ any=[[Executing command: .?& { Get%-Content .* | & sort }]]
+ }
+ feed('<CR>')
+ helpers.expect_exit(command, 'qall!')
+ end)
end)
diff --git a/test/functional/vimscript/timer_spec.lua b/test/functional/vimscript/timer_spec.lua
index 5463cfb234..1818a71ea2 100644
--- a/test/functional/vimscript/timer_spec.lua
+++ b/test/functional/vimscript/timer_spec.lua
@@ -131,34 +131,34 @@ describe('timers', function()
nvim_async("command", "call timer_start("..load_adjust(100)..", 'AddItem', {'repeat': -1})")
screen:expect([[
- ITEM 1 |
+ ^ITEM 1 |
ITEM 2 |
{1:~ }|
{1:~ }|
{1:~ }|
- ^ |
+ |
]])
nvim_async("command", "let g:cont = 1")
screen:expect([[
- ITEM 1 |
+ ^ITEM 1 |
ITEM 2 |
ITEM 3 |
{1:~ }|
{1:~ }|
- ^ |
+ |
]])
feed("3")
eq(51, eval("g:c2"))
- screen:expect([[
+ screen:expect{grid=[[
^ITEM 1 |
ITEM 2 |
ITEM 3 |
{1:~ }|
{1:~ }|
|
- ]])
+ ]], unchanged=true}
end)
it('can be stopped', function()
diff --git a/test/functional/vimscript/writefile_spec.lua b/test/functional/vimscript/writefile_spec.lua
index 5f693249a9..8c8da9dc88 100644
--- a/test/functional/vimscript/writefile_spec.lua
+++ b/test/functional/vimscript/writefile_spec.lua
@@ -111,6 +111,26 @@ describe('writefile()', function()
pcall_err(command, ('call writefile([42], %s)'):format(ddname_tail)))
end)
+ it('writefile(..., "p") creates missing parent directories', function()
+ os.remove(dname)
+ eq(nil, read_file(dfname))
+ eq(0, funcs.writefile({'abc', 'def', 'ghi'}, dfname, 'p'))
+ eq('abc\ndef\nghi\n', read_file(dfname))
+ os.remove(dfname)
+ os.remove(dname)
+ eq(nil, read_file(dfname))
+ eq(0, funcs.writefile({'\na\nb\n'}, dfname, 'pb'))
+ eq('\0a\0b\0', read_file(dfname))
+ os.remove(dfname)
+ os.remove(dname)
+ eq('Vim(call):E32: No file name',
+ pcall_err(command, ('call writefile([], "%s", "p")'):format(dfname .. '.d/')))
+ eq(('Vim(call):E482: Can\'t open file ./ for writing: illegal operation on a directory'),
+ pcall_err(command, 'call writefile([], "./", "p")'))
+ eq(('Vim(call):E482: Can\'t open file . for writing: illegal operation on a directory'),
+ pcall_err(command, 'call writefile([], ".", "p")'))
+ end)
+
it('errors out with invalid arguments', function()
write_file(fname, 'TEST')
eq('Vim(call):E119: Not enough arguments for function: writefile',
diff --git a/test/helpers.lua b/test/helpers.lua
index 7ec9beea92..82ff23bef8 100644
--- a/test/helpers.lua
+++ b/test/helpers.lua
@@ -1,6 +1,7 @@
-require('vim.compat')
+require('test.compat')
local shared = require('vim.shared')
local assert = require('luassert')
+local busted = require('busted')
local luv = require('luv')
local lfs = require('lfs')
local relpath = require('pl.path').relpath
@@ -45,6 +46,28 @@ function module.sleep(ms)
luv.sleep(ms)
end
+-- Calls fn() until it succeeds, up to `max` times or until `max_ms`
+-- milliseconds have passed.
+function module.retry(max, max_ms, fn)
+ assert(max == nil or max > 0)
+ assert(max_ms == nil or max_ms > 0)
+ local tries = 1
+ local timeout = (max_ms and max_ms or 10000)
+ local start_time = luv.now()
+ while true do
+ local status, result = pcall(fn)
+ if status then
+ return result
+ end
+ luv.update_time() -- Update cached value of luv.now() (libuv: uv_now()).
+ if (max and tries >= max) or (luv.now() - start_time > timeout) then
+ busted.fail(string.format("retry() attempts: %d\n%s", tries, tostring(result)), 2)
+ end
+ tries = tries + 1
+ luv.sleep(20) -- Avoid hot loop...
+ end
+end
+
local check_logs_useless_lines = {
['Warning: noted but unhandled ioctl']=1,
['could cause spurious value errors to appear']=2,
@@ -87,6 +110,8 @@ end
--- Asserts that `pat` matches (or *not* if inverse=true) any line in the tail of `logfile`.
---
+--- Retries for 1 second in case of filesystem delay.
+---
---@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
@@ -96,43 +121,34 @@ function module.assert_log(pat, logfile, nrlines, inverse)
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
- if inverse then error(msg) else return end
+
+ module.retry(nil, 1000, function()
+ 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
+ if inverse then error(msg) else return end
+ end
end
- end
- if not inverse then error(msg) end
+ if not inverse then error(msg) end
+ end)
end
---- Asserts that `pat` does NOT matche any line in the tail of `logfile`.
+--- Asserts that `pat` does NOT match 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
--- an error if `fn` succeeds.
---
--- Replaces line/column numbers with zero:
--- shared.lua:0: in function 'gsplit'
--- shared.lua:0: in function <shared.lua:0>'
---
--- Usage:
--- -- Match exact string.
--- eq('e', pcall_err(function(a, b) error('e') end, 'arg1', 'arg2'))
--- -- Match Lua pattern.
--- matches('e[or]+$', pcall_err(function(a, b) error('some error') end, 'arg1', 'arg2'))
---
-function module.pcall_err_withfile(fn, ...)
+function module.pcall(fn, ...)
assert(type(fn) == 'function')
local status, rv = pcall(fn, ...)
- if status == true then
- error('expected failure, but got success')
+ if status then
+ return status, rv
end
+
-- From:
-- C:/long/path/foo.lua:186: Expected string, got number
-- to:
@@ -150,13 +166,37 @@ function module.pcall_err_withfile(fn, ...)
-- We remove this so that the tests are not lua dependent.
errmsg = errmsg:gsub('%s*%(tail call%): %?', '')
- return errmsg
+ return status, errmsg
+end
+
+-- Invokes `fn` and returns the error string (with truncated paths), or raises
+-- an error if `fn` succeeds.
+--
+-- Replaces line/column numbers with zero:
+-- shared.lua:0: in function 'gsplit'
+-- shared.lua:0: in function <shared.lua:0>'
+--
+-- Usage:
+-- -- Match exact string.
+-- eq('e', pcall_err(function(a, b) error('e') end, 'arg1', 'arg2'))
+-- -- Match Lua pattern.
+-- matches('e[or]+$', pcall_err(function(a, b) error('some error') end, 'arg1', 'arg2'))
+--
+function module.pcall_err_withfile(fn, ...)
+ assert(type(fn) == 'function')
+ local status, rv = module.pcall(fn, ...)
+ if status == true then
+ error('expected failure, but got success')
+ end
+ return rv
end
function module.pcall_err_withtrace(fn, ...)
local errmsg = module.pcall_err_withfile(fn, ...)
- return errmsg:gsub('.../helpers.lua:0: ', '')
+ return errmsg:gsub('^%.%.%./helpers%.lua:0: ', '')
+ :gsub('^Error executing lua:- ' ,'')
+ :gsub('^%[string "<nvim>"%]:0: ' ,'')
end
function module.pcall_err(...)
@@ -257,43 +297,26 @@ function module.check_logs()
table.concat(runtime_errors, ', ')))
end
-function module.iswin()
- return package.config:sub(1,1) == '\\'
+function module.sysname()
+ local platform = luv.os_uname()
+ if platform and platform.sysname then
+ return platform.sysname:lower()
+ end
end
--- Gets (lowercase) OS name from CMake, uname, or "win" if iswin().
-module.uname = (function()
- local platform = nil
- return (function()
- if platform then
- return platform
- end
-
- if os.getenv("SYSTEM_NAME") then -- From CMAKE_HOST_SYSTEM_NAME.
- platform = string.lower(os.getenv("SYSTEM_NAME"))
- return platform
- end
-
- local status, f = pcall(module.popen_r, 'uname', '-s')
- if status then
- platform = string.lower(f:read("*l"))
- f:close()
- elseif module.iswin() then
- platform = 'windows'
- else
- error('unknown platform')
- end
- return platform
- end)
-end)()
-
function module.is_os(s)
- if not (s == 'win' or s == 'mac' or s == 'unix') then
+ if not (s == 'win'
+ or s == 'mac'
+ or s == 'freebsd'
+ or s == 'openbsd'
+ or s == 'bsd') then
error('unknown platform: '..tostring(s))
end
- return ((s == 'win' and module.iswin())
- or (s == 'mac' and module.uname() == 'darwin')
- or (s == 'unix'))
+ return ((s == 'win' and module.sysname():find('windows'))
+ or (s == 'mac' and module.sysname() == 'darwin')
+ or (s == 'freebsd' and module.sysname() == 'freebsd')
+ or (s == 'openbsd' and module.sysname() == 'openbsd')
+ or (s == 'bsd' and module.sysname():find('bsd')))
end
local function tmpdir_get()
@@ -319,11 +342,11 @@ module.tmpname = (function()
return fname
else
local fname = os.tmpname()
- if module.uname() == 'windows' and fname:sub(1, 2) == '\\s' then
+ if module.is_os('win') and fname:sub(1, 2) == '\\s' then
-- In Windows tmpname() returns a filename starting with
-- special sequence \s, prepend $TEMP path
return tmpdir..fname
- elseif fname:match('^/tmp') and module.uname() == 'darwin' then
+ elseif fname:match('^/tmp') and module.is_os('mac') then
-- In OS X /tmp links to /private/tmp
return '/private'..fname
else
@@ -366,14 +389,14 @@ function module.check_cores(app, force)
exc_re = { os.getenv('NVIM_TEST_CORE_EXC_RE'), local_tmpdir }
db_cmd = os.getenv('NVIM_TEST_CORE_DB_CMD') or gdb_db_cmd
random_skip = os.getenv('NVIM_TEST_CORE_RANDOM_SKIP')
- elseif 'darwin' == module.uname() then
+ elseif module.is_os('mac') then
initial_path = '/cores'
re = nil
exc_re = { local_tmpdir }
db_cmd = lldb_db_cmd
else
initial_path = '.'
- if 'freebsd' == module.uname() then
+ if module.is_os('freebsd') then
re = '/nvim.core$'
else
re = '/core[^/]*$'
@@ -788,20 +811,19 @@ function module.write_file(name, text, no_dedent, append)
file:close()
end
-function module.isCI(name)
+function module.is_ci(name)
local any = (name == nil)
- assert(any or name == 'sourcehut' or name == 'github')
- local sh = ((any or name == 'sourcehut') and nil ~= os.getenv('SOURCEHUT'))
+ assert(any or name == 'github' or name == 'cirrus')
local gh = ((any or name == 'github') and nil ~= os.getenv('GITHUB_ACTIONS'))
- return sh or gh
-
+ local cirrus = ((any or name == 'cirrus') and nil ~= os.getenv('CIRRUS_CI'))
+ return gh or cirrus
end
-- Gets the (tail) contents of `logfile`.
-- Also moves the file to "${NVIM_LOG_FILE}.displayed" on CI environments.
function module.read_nvim_log(logfile, ci_rename)
logfile = logfile or os.getenv('NVIM_LOG_FILE') or '.nvimlog'
- local is_ci = module.isCI()
+ local is_ci = module.is_ci()
local keep = is_ci and 100 or 10
local lines = module.read_file_list(logfile, -keep) or {}
local log = (('-'):rep(78)..'\n'
diff --git a/test/includes/CMakeLists.txt b/test/includes/CMakeLists.txt
deleted file mode 100644
index b4da4c0611..0000000000
--- a/test/includes/CMakeLists.txt
+++ /dev/null
@@ -1,32 +0,0 @@
-file(GLOB_RECURSE PRE_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} pre/*.h)
-
-# We need to add the SDK directories on OS X, and perhaps other operating
-# systems.
-set(gen_cflags)
-foreach(gen_include ${CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES})
- list(APPEND gen_cflags ${CMAKE_INCLUDE_FLAG_C}${gen_include})
-endforeach()
-
-get_directory_property(gen_cdefs COMPILE_DEFINITIONS)
-foreach(gen_cdef ${gen_cdefs})
- if(NOT ${gen_cdef} MATCHES "INCLUDE_GENERATED_DECLARATIONS")
- list(APPEND gen_cflags "-D${gen_cdef}")
- endif()
-endforeach()
-
-foreach(hfile ${PRE_HEADERS})
- string(REGEX REPLACE ^pre/ post/ post_hfile ${hfile})
- get_filename_component(hdir ${CMAKE_CURRENT_BINARY_DIR}/${post_hfile} DIRECTORY)
- file(MAKE_DIRECTORY ${hdir})
- add_custom_command(
- OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${post_hfile}
- COMMAND ${CMAKE_C_COMPILER} -std=c99 -E -P
- ${CMAKE_CURRENT_SOURCE_DIR}/${hfile}
- ${gen_cflags}
- -I${LIBUV_INCLUDE_DIRS}
- -o ${CMAKE_CURRENT_BINARY_DIR}/${post_hfile})
- list(APPEND POST_HEADERS ${post_hfile})
-endforeach()
-
-add_custom_target(unittest-headers DEPENDS ${POST_HEADERS})
-set_target_properties(unittest-headers PROPERTIES FOLDER test)
diff --git a/test/unit/buffer_spec.lua b/test/unit/buffer_spec.lua
index 5dccc2f5a2..a54ea8c656 100644
--- a/test/unit/buffer_spec.lua
+++ b/test/unit/buffer_spec.lua
@@ -28,9 +28,9 @@ describe('buffer functions', function()
setup(function()
-- create the files
- io.open(path1, 'w').close()
- io.open(path2, 'w').close()
- io.open(path3, 'w').close()
+ io.open(path1, 'w'):close()
+ io.open(path2, 'w'):close()
+ io.open(path3, 'w'):close()
end)
teardown(function()
@@ -233,10 +233,12 @@ describe('buffer functions', function()
output_buffer,
buffer_byte_size,
to_cstr(pat),
- false,
+ NULL,
+ 0,
fillchar,
maximum_cell_count,
NULL,
+ NULL,
NULL)
end
diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua
index 6387f89fe4..128625374e 100644
--- a/test/unit/eval/typval_spec.lua
+++ b/test/unit/eval/typval_spec.lua
@@ -2175,7 +2175,8 @@ describe('typval.c', function()
eq({a='TSET'}, dct2tbl(d1))
eq({a='TSET'}, dct2tbl(d2))
end)
- itp('disallows overriding builtin or user functions', function()
+ pending('disallows overriding builtin or user functions: here be the dragons', function()
+ -- pending: see TODO below
local d = dict()
d.dv_scope = lib.VAR_DEF_SCOPE
local f_lua = {
@@ -2196,6 +2197,7 @@ describe('typval.c', function()
local d5 = dict({Test=f_tv})
local d6 = dict({Test=p_tv})
eval0([[execute("function Test()\nendfunction")]])
+ -- TODO: test breaks at this point
tv_dict_extend(d, d2, 'force',
'E704: Funcref variable name must start with a capital: tr')
tv_dict_extend(d, d3, 'force',
@@ -2253,8 +2255,8 @@ describe('typval.c', function()
local d1 = dict()
alloc_log:check({a.dict(d1)})
eq(1, d1.dv_refcount)
- eq(false, tv_dict_equal(nil, d1))
- eq(false, tv_dict_equal(d1, nil))
+ eq(true, tv_dict_equal(nil, d1))
+ eq(true, tv_dict_equal(d1, nil))
eq(true, tv_dict_equal(d1, d1))
eq(1, d1.dv_refcount)
alloc_log:check({})
@@ -2623,7 +2625,7 @@ describe('typval.c', function()
describe('check_lock()', function()
local function tv_check_lock(lock, name, name_len, emsg)
return check_emsg(function()
- return lib.var_check_lock(lock, name, name_len)
+ return lib.value_check_lock(lock, name, name_len)
end, emsg)
end
itp('works', function()
@@ -2721,8 +2723,8 @@ describe('typval.c', function()
local d1 = lua2typvalt({})
alloc_log:check({a.dict(d1.vval.v_dict)})
eq(1, d1.vval.v_dict.dv_refcount)
- eq(false, tv_equal(nd, d1))
- eq(false, tv_equal(d1, nd))
+ eq(true, tv_equal(nd, d1))
+ eq(true, tv_equal(d1, nd))
eq(true, tv_equal(d1, d1))
eq(1, d1.vval.v_dict.dv_refcount)
alloc_log:check({})
diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua
index 29ea0235be..14d8eda357 100644
--- a/test/unit/helpers.lua
+++ b/test/unit/helpers.lua
@@ -7,9 +7,6 @@ local global_helpers = require('test.helpers')
local assert = require('luassert')
local say = require('say')
-local posix = nil
-local syscall = nil
-
local check_cores = global_helpers.check_cores
local dedent = global_helpers.dedent
local neq = global_helpers.neq
@@ -373,122 +370,91 @@ local function to_cstr(string)
return cstr(#string + 1, string)
end
-local sc
-
-if posix ~= nil then
- sc = {
- fork = posix.fork,
- pipe = posix.pipe,
- read = posix.read,
- write = posix.write,
- close = posix.close,
- wait = posix.wait,
- exit = posix._exit,
- }
-elseif syscall ~= nil then
- sc = {
- fork = syscall.fork,
- pipe = function()
- local ret = {syscall.pipe()}
- return ret[3], ret[4]
- end,
- read = function(rd, len)
- return rd:read(nil, len)
- end,
- write = function(wr, s)
- return wr:write(s)
- end,
- close = function(p)
- return p:close()
- end,
- wait = syscall.wait,
- exit = syscall.exit,
- }
-else
- cimport_immediate('./test/unit/fixtures/posix.h')
- sc = {
- fork = function()
- return tonumber(ffi.C.fork())
- end,
- pipe = function()
- local ret = ffi.new('int[2]', {-1, -1})
- ffi.errno(0)
- local res = ffi.C.pipe(ret)
- if (res ~= 0) then
+cimport_immediate('./test/unit/fixtures/posix.h')
+local sc = {
+ fork = function()
+ return tonumber(ffi.C.fork())
+ end,
+ pipe = function()
+ local ret = ffi.new('int[2]', {-1, -1})
+ ffi.errno(0)
+ local res = ffi.C.pipe(ret)
+ if (res ~= 0) then
+ local err = ffi.errno(0)
+ assert(res == 0, ("pipe() error: %u: %s"):format(
+ err, ffi.string(ffi.C.strerror(err))))
+ end
+ assert(ret[0] ~= -1 and ret[1] ~= -1)
+ return ret[0], ret[1]
+ end,
+ read = function(rd, len)
+ local ret = ffi.new('char[?]', len, {0})
+ local total_bytes_read = 0
+ ffi.errno(0)
+ while total_bytes_read < len do
+ local bytes_read = tonumber(ffi.C.read(
+ rd,
+ ffi.cast('void*', ret + total_bytes_read),
+ len - total_bytes_read))
+ if bytes_read == -1 then
local err = ffi.errno(0)
- assert(res == 0, ("pipe() error: %u: %s"):format(
- err, ffi.string(ffi.C.strerror(err))))
- end
- assert(ret[0] ~= -1 and ret[1] ~= -1)
- return ret[0], ret[1]
- end,
- read = function(rd, len)
- local ret = ffi.new('char[?]', len, {0})
- local total_bytes_read = 0
- ffi.errno(0)
- while total_bytes_read < len do
- local bytes_read = tonumber(ffi.C.read(
- rd,
- ffi.cast('void*', ret + total_bytes_read),
- len - total_bytes_read))
- if bytes_read == -1 then
- local err = ffi.errno(0)
- if err ~= ffi.C.kPOSIXErrnoEINTR then
- assert(false, ("read() error: %u: %s"):format(
- err, ffi.string(ffi.C.strerror(err))))
- end
- elseif bytes_read == 0 then
- break
- else
- total_bytes_read = total_bytes_read + bytes_read
+ if err ~= ffi.C.kPOSIXErrnoEINTR then
+ assert(false, ("read() error: %u: %s"):format(
+ err, ffi.string(ffi.C.strerror(err))))
end
+ elseif bytes_read == 0 then
+ break
+ else
+ total_bytes_read = total_bytes_read + bytes_read
end
- return ffi.string(ret, total_bytes_read)
- end,
- write = function(wr, s)
- local wbuf = to_cstr(s)
- local total_bytes_written = 0
- ffi.errno(0)
- while total_bytes_written < #s do
- local bytes_written = tonumber(ffi.C.write(
- wr,
- ffi.cast('void*', wbuf + total_bytes_written),
- #s - total_bytes_written))
- if bytes_written == -1 then
- local err = ffi.errno(0)
- if err ~= ffi.C.kPOSIXErrnoEINTR then
- assert(false, ("write() error: %u: %s ('%s')"):format(
- err, ffi.string(ffi.C.strerror(err)), s))
- end
- elseif bytes_written == 0 then
- break
- else
- total_bytes_written = total_bytes_written + bytes_written
+ end
+ return ffi.string(ret, total_bytes_read)
+ end,
+ write = function(wr, s)
+ local wbuf = to_cstr(s)
+ local total_bytes_written = 0
+ ffi.errno(0)
+ while total_bytes_written < #s do
+ local bytes_written = tonumber(ffi.C.write(
+ wr,
+ ffi.cast('void*', wbuf + total_bytes_written),
+ #s - total_bytes_written))
+ if bytes_written == -1 then
+ local err = ffi.errno(0)
+ if err ~= ffi.C.kPOSIXErrnoEINTR then
+ assert(false, ("write() error: %u: %s ('%s')"):format(
+ err, ffi.string(ffi.C.strerror(err)), s))
end
+ elseif bytes_written == 0 then
+ break
+ else
+ total_bytes_written = total_bytes_written + bytes_written
end
- return total_bytes_written
- end,
- close = ffi.C.close,
- wait = function(pid)
- ffi.errno(0)
- while true do
- local r = ffi.C.waitpid(pid, nil, ffi.C.kPOSIXWaitWUNTRACED)
- if r == -1 then
- local err = ffi.errno(0)
- if err == ffi.C.kPOSIXErrnoECHILD then
- break
- elseif err ~= ffi.C.kPOSIXErrnoEINTR then
- assert(false, ("waitpid() error: %u: %s"):format(
- err, ffi.string(ffi.C.strerror(err))))
- end
- else
- assert(r == pid)
+ end
+ return total_bytes_written
+ end,
+ close = ffi.C.close,
+ wait = function(pid)
+ ffi.errno(0)
+ local stat_loc = ffi.new('int[1]', {0})
+ while true do
+ local r = ffi.C.waitpid(pid, stat_loc, ffi.C.kPOSIXWaitWUNTRACED)
+ if r == -1 then
+ local err = ffi.errno(0)
+ if err == ffi.C.kPOSIXErrnoECHILD then
+ break
+ elseif err ~= ffi.C.kPOSIXErrnoEINTR then
+ assert(false, ("waitpid() error: %u: %s"):format(
+ err, ffi.string(ffi.C.strerror(err))))
end
+ else
+ assert(r == pid)
end
- end,
- exit = ffi.C._exit,
- }
-end
+ end
+ return stat_loc[0]
+ end,
+ exit = ffi.C._exit,
+}
local function format_list(lst)
local ret = ''
@@ -730,18 +696,22 @@ local function check_child_err(rd)
end
end
-local function itp_parent(rd, pid, allow_failure)
- local err, emsg = pcall(check_child_err, rd)
- sc.wait(pid)
+local function itp_parent(rd, pid, allow_failure, location)
+ local ok, emsg = pcall(check_child_err, rd)
+ local status = sc.wait(pid)
sc.close(rd)
- if not err then
+ if not ok then
if allow_failure then
- io.stderr:write('Errorred out:\n' .. tostring(emsg) .. '\n')
+ io.stderr:write('Errorred out ('..status..'):\n' .. tostring(emsg) .. '\n')
os.execute([[
sh -c "source ci/common/test.sh
check_core_dumps --delete \"]] .. Paths.test_luajit_prg .. [[\""]])
else
- error(emsg)
+ error(tostring(emsg)..'\nexit code: '..status)
+ end
+ elseif status ~= 0 then
+ if not allow_failure then
+ error("child process errored out with status "..status.."!\n\n"..location)
end
end
end
@@ -758,6 +728,11 @@ local function gen_itp(it)
-- FIXME Fix tests with this true
return
end
+
+ -- Pre-emptively calculating error location, wasteful, ugh!
+ -- But the way this code messes around with busted implies the real location is strictly
+ -- not available in the parent when an actual error occurs. so we have to do this here.
+ local location = debug.traceback()
it(name, function()
local rd, wr = sc.pipe()
child_pid = sc.fork()
@@ -768,7 +743,7 @@ local function gen_itp(it)
sc.close(wr)
local saved_child_pid = child_pid
child_pid = nil
- itp_parent(rd, saved_child_pid, allow_failure)
+ itp_parent(rd, saved_child_pid, allow_failure, location)
end
end)
end
diff --git a/test/unit/message_spec.lua b/test/unit/message_spec.lua
index 7e92b5c857..549eff6e03 100644
--- a/test/unit/message_spec.lua
+++ b/test/unit/message_spec.lua
@@ -22,7 +22,7 @@ describe('trunc_string', function()
local function test_copy(s, expected, room)
room = room and room or 20
local buf = cimp.xmalloc(ffi.sizeof('char_u') * buflen)
- local str = cimp.vim_strsave(to_cstr(s))
+ local str = cimp.xstrdup(to_cstr(s))
cimp.trunc_string(str, buf, room, buflen)
eq(expected, ffi.string(buf))
cimp.xfree(buf)
diff --git a/test/unit/os/fs_spec.lua b/test/unit/os/fs_spec.lua
index 0bb33772cd..c718244ea4 100644
--- a/test/unit/os/fs_spec.lua
+++ b/test/unit/os/fs_spec.lua
@@ -72,9 +72,9 @@ describe('fs.c', function()
before_each(function()
lfs.mkdir('unit-test-directory');
- io.open('unit-test-directory/test.file', 'w').close()
+ io.open('unit-test-directory/test.file', 'w'):close()
- io.open('unit-test-directory/test_2.file', 'w').close()
+ io.open('unit-test-directory/test_2.file', 'w'):close()
lfs.link('test.file', 'unit-test-directory/test_link.file', true)
lfs.link('non_existing_file.file', 'unit-test-directory/test_broken_link.file', true)
@@ -472,7 +472,7 @@ describe('fs.c', function()
describe('os_remove', function()
before_each(function()
- io.open('unit-test-directory/test_remove.file', 'w').close()
+ io.open('unit-test-directory/test_remove.file', 'w'):close()
end)
after_each(function()
diff --git a/test/unit/path_spec.lua b/test/unit/path_spec.lua
index fb476397e6..1fc4e2496e 100644
--- a/test/unit/path_spec.lua
+++ b/test/unit/path_spec.lua
@@ -82,8 +82,8 @@ describe('path.c', function()
local f2 = 'f2.o'
before_each(function()
-- create the three files that will be used in this spec
- io.open(f1, 'w').close()
- io.open(f2, 'w').close()
+ io.open(f1, 'w'):close()
+ io.open(f2, 'w'):close()
end)
after_each(function()
@@ -355,7 +355,7 @@ end)
describe('path.c', function()
setup(function()
lfs.mkdir('unit-test-directory');
- io.open('unit-test-directory/test.file', 'w').close()
+ io.open('unit-test-directory/test.file', 'w'):close()
-- Since the tests are executed, they are called by an executable. We use
-- that executable for several asserts.
@@ -504,6 +504,16 @@ describe('path.c', function()
eq(OK, result)
end)
+ itp('does not remove trailing slash from non-existing relative directory #20847', function()
+ local expected = lfs.currentdir() .. '/non_existing_dir/'
+ local filename = 'non_existing_dir/'
+ local buflen = get_buf_len(expected, filename)
+ local do_expand = 1
+ local buf, result = vim_FullName(filename, buflen, do_expand)
+ eq(expected, ffi.string(buf))
+ eq(OK, result)
+ end)
+
itp('expands "./" to the current directory #7117', function()
local expected = lfs.currentdir() .. '/unit-test-directory/test.file'
local filename = './unit-test-directory/test.file'
@@ -640,6 +650,10 @@ describe('path.c', function()
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]]))
+ eq(1, path_with_url([[test-C:/xyz/foo/b5]]))
+ eq(1, path_with_url([[test-custom:/xyz/foo/b5]]))
+ eq(0, path_with_url([[c:/xyz/foo/b5]]))
+ eq(0, path_with_url([[C:/xyz/foo/b5]]))
end)
end)
end)
diff --git a/test/unit/search_spec.lua b/test/unit/search_spec.lua
index ce37ebfc3a..be905bf5f0 100644
--- a/test/unit/search_spec.lua
+++ b/test/unit/search_spec.lua
@@ -37,7 +37,7 @@ 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)
+ local fail = search.search_regcomp(to_cstr(pat), nil, pat_save, pat_use, options, regmatch)
return fail, regmatch
end
diff --git a/test/unit/tui_spec.lua b/test/unit/tui_spec.lua
index 36ce4a1493..25b70a17c2 100644
--- a/test/unit/tui_spec.lua
+++ b/test/unit/tui_spec.lua
@@ -9,6 +9,7 @@ local cinput = cimport("./src/nvim/tui/input.h")
local rbuffer = cimport("./test/unit/fixtures/rbuffer.h")
local globals = cimport("./src/nvim/globals.h")
local multiqueue = cimport("./test/unit/fixtures/multiqueue.h")
+local ui_client = cimport("./src/nvim/ui_client.h")
itp('handle_background_color', function()
local handle_background_color = cinput.ut_handle_background_color
@@ -33,11 +34,9 @@ itp('handle_background_color', function()
term_input.waiting_for_bg_response = 1
eq(kComplete, handle_background_color(term_input))
eq(0, term_input.waiting_for_bg_response)
- eq(1, multiqueue.multiqueue_size(events))
-
- local event = multiqueue.multiqueue_get(events)
- local bg_event = ffi.cast("Event*", event.argv[1])
- eq(bg, ffi.string(bg_event.argv[0]))
+ eq(0, multiqueue.multiqueue_size(events))
+ eq(bg, ({[0]="light", [1] = "dark", [-1] = "none"})
+ [tonumber(ui_client.ui_client_bg_response)])
-- Buffer has been consumed.
eq(0, rbuf.size)
@@ -114,9 +113,7 @@ itp('handle_background_color', function()
eq(kComplete, handle_background_color(term_input))
eq(0, term_input.waiting_for_bg_response)
- local event = multiqueue.multiqueue_get(events)
- local bg_event = ffi.cast("Event*", event.argv[1])
- eq('light', ffi.string(bg_event.argv[0]))
+ eq(0, tonumber(ui_client.ui_client_bg_response))
eq(0, multiqueue.multiqueue_size(events))
eq(0, rbuf.size)
@@ -133,9 +130,7 @@ itp('handle_background_color', function()
eq(kComplete, handle_background_color(term_input))
eq(0, term_input.waiting_for_bg_response)
- event = multiqueue.multiqueue_get(events)
- bg_event = ffi.cast("Event*", event.argv[1])
- eq('light', ffi.string(bg_event.argv[0]))
+ eq(0, tonumber(ui_client.ui_client_bg_response))
eq(0, multiqueue.multiqueue_size(events))
eq(0, rbuf.size)
@@ -161,7 +156,7 @@ itp('handle_background_color', function()
eq(kComplete, handle_background_color(term_input))
eq(0, term_input.waiting_for_bg_response)
- eq(1, multiqueue.multiqueue_size(events))
+ eq(0, multiqueue.multiqueue_size(events))
eq(3, rbuf.size)
rbuffer.rbuffer_consumed(rbuf, rbuf.size)
end)